/include/crypto/internal/

option value='aarch64/hotplug-vcpu/v6.5'>aarch64/hotplug-vcpu/v6.5 Russell King's ARM Linux kernel treeRussell King
summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig98
-rw-r--r--sound/soc/Makefile61
-rw-r--r--sound/soc/adi/Kconfig11
-rw-r--r--sound/soc/adi/Makefile5
-rw-r--r--sound/soc/adi/axi-i2s.c83
-rw-r--r--sound/soc/adi/axi-spdif.c13
-rw-r--r--sound/soc/amd/Kconfig186
-rw-r--r--sound/soc/amd/Makefile22
-rw-r--r--sound/soc/amd/acp-config.c337
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c811
-rw-r--r--sound/soc/amd/acp-es8336.c320
-rw-r--r--sound/soc/amd/acp-pcm-dma.c1125
-rw-r--r--sound/soc/amd/acp-rt5645.c218
-rw-r--r--sound/soc/amd/acp.h141
-rw-r--r--sound/soc/amd/acp/Kconfig189
-rw-r--r--sound/soc/amd/acp/Makefile50
-rw-r--r--sound/soc/amd/acp/acp-i2s.c699
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c647
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c245
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c1803
-rw-r--r--sound/soc/amd/acp/acp-mach.h139
-rw-r--r--sound/soc/amd/acp/acp-pci.c301
-rw-r--r--sound/soc/amd/acp/acp-pdm.c187
-rw-r--r--sound/soc/amd/acp/acp-platform.c360
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c247
-rw-r--r--sound/soc/amd/acp/acp-renoir.c191
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c565
-rw-r--r--sound/soc/amd/acp/acp-sdw-mach-common.c98
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c461
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c174
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c456
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h12
-rw-r--r--sound/soc/amd/acp/acp63.c298
-rw-r--r--sound/soc/amd/acp/acp70.c228
-rw-r--r--sound/soc/amd/acp/acp_common.h20
-rw-r--r--sound/soc/amd/acp/amd-acp63-acpi-match.c144
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c329
-rw-r--r--sound/soc/amd/acp/amd-acpi-mach.c93
-rw-r--r--sound/soc/amd/acp/amd-sdw-acpi.c62
-rw-r--r--sound/soc/amd/acp/amd.h365
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h146
-rw-r--r--sound/soc/amd/acp/soc_amd_sdw_common.h62
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c556
-rw-r--r--sound/soc/amd/mach-config.h38
-rw-r--r--sound/soc/amd/ps/Makefile11
-rw-r--r--sound/soc/amd/ps/acp63.h448
-rw-r--r--sound/soc/amd/ps/pci-ps.c755
-rw-r--r--sound/soc/amd/ps/ps-common.c493
-rw-r--r--sound/soc/amd/ps/ps-mach.c80
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c463
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c804
-rw-r--r--sound/soc/amd/raven/Makefile8
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c331
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c526
-rw-r--r--sound/soc/amd/raven/acp3x.h162
-rw-r--r--sound/soc/amd/raven/chip_offset_byte.h639
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c347
-rw-r--r--sound/soc/amd/renoir/Makefile8
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c504
-rw-r--r--sound/soc/amd/renoir/acp3x-rn.c77
-rw-r--r--sound/soc/amd/renoir/rn-pci-acp3x.c434
-rw-r--r--sound/soc/amd/renoir/rn_acp3x.h93
-rw-r--r--sound/soc/amd/renoir/rn_chip_offset_byte.h349
-rw-r--r--sound/soc/amd/rpl/Makefile5
-rw-r--r--sound/soc/amd/rpl/rpl-pci-acp6x.c227
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x.h36
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h30
-rw-r--r--sound/soc/amd/vangogh/Makefile11
-rw-r--r--sound/soc/amd/vangogh/acp5x-i2s.c417
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c489
-rw-r--r--sound/soc/amd/vangogh/acp5x-pcm-dma.c513
-rw-r--r--sound/soc/amd/vangogh/acp5x.h224
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c342
-rw-r--r--sound/soc/amd/vangogh/vg_chip_offset_byte.h337
-rw-r--r--sound/soc/amd/yc/Makefile9
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c740
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c455
-rw-r--r--sound/soc/amd/yc/acp6x.h110
-rw-r--r--sound/soc/amd/yc/acp6x_chip_offset_byte.h444
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c350
-rw-r--r--sound/soc/apple/Kconfig11
-rw-r--r--sound/soc/apple/Makefile3
-rw-r--r--sound/soc/apple/mca.c1212
-rw-r--r--sound/soc/atmel/Kconfig144
-rw-r--r--sound/soc/atmel/Makefile41
-rw-r--r--sound/soc/atmel/atmel-classd.c273
-rw-r--r--sound/soc/atmel/atmel-classd.h1
-rw-r--r--sound/soc/atmel/atmel-i2s.c742
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c35
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c141
-rw-r--r--sound/soc/atmel/atmel-pcm.h29
-rw-r--r--sound/soc/atmel/atmel-pdmic.c210
-rw-r--r--sound/soc/atmel/atmel-pdmic.h1
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c507
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h17
-rw-r--r--sound/soc/atmel/atmel_wm8904.c33
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c1138
-rw-r--r--sound/soc/atmel/mchp-pdmc.c1156
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c1203
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c903
-rw-r--r--sound/soc/atmel/mikroe-proto.c176
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c140
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c63
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c435
-rw-r--r--sound/soc/au1x/Kconfig7
-rw-r--r--sound/soc/au1x/Makefile17
-rw-r--r--sound/soc/au1x/ac97c.c27
-rw-r--r--sound/soc/au1x/db1000.c11
-rw-r--r--sound/soc/au1x/db1200.c64
-rw-r--r--sound/soc/au1x/dbdma2.c100
-rw-r--r--sound/soc/au1x/dma.c98
-rw-r--r--sound/soc/au1x/i2sc.c35
-rw-r--r--sound/soc/au1x/psc-ac97.c40
-rw-r--r--sound/soc/au1x/psc-i2s.c50
-rw-r--r--sound/soc/au1x/psc.h6
-rw-r--r--sound/soc/bcm/Kconfig23
-rw-r--r--sound/soc/bcm/Makefile12
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c735
-rw-r--r--sound/soc/bcm/bcm63xx-i2s-whistler.c303
-rw-r--r--sound/soc/bcm/bcm63xx-i2s.h89
-rw-r--r--sound/soc/bcm/bcm63xx-pcm-whistler.c418
-rw-r--r--sound/soc/bcm/cygnus-pcm.c750
-rw-r--r--sound/soc/bcm/cygnus-ssp.c1405
-rw-r--r--sound/soc/bcm/cygnus-ssp.h127
-rw-r--r--sound/soc/blackfin/Kconfig205
-rw-r--r--sound/soc/blackfin/Makefile39
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c469
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c388
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h57
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c109
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c131
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c109
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c212
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c361
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h17
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c391
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c1102
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h174
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c126
-rw-r--r--sound/soc/blackfin/bf6xx-i2s.c239
-rw-r--r--sound/soc/blackfin/bf6xx-sport.c429
-rw-r--r--sound/soc/blackfin/bf6xx-sport.h82
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1373.c173
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1701.c113
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1x61.c142
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1x81.c129
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c145
-rw-r--r--sound/soc/cirrus/Kconfig53
-rw-r--r--sound/soc/cirrus/Makefile15
-rw-r--r--sound/soc/cirrus/edb93xx.c126
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c450
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c219
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c24
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.h13
-rw-r--r--sound/soc/cirrus/simone.c87
-rw-r--r--sound/soc/cirrus/snappercl15.c136
-rw-r--r--sound/soc/codecs/88pm860x-codec.c231
-rw-r--r--sound/soc/codecs/88pm860x-codec.h9
-rw-r--r--sound/soc/codecs/Kconfig2330
-rw-r--r--sound/soc/codecs/Makefile887
-rw-r--r--sound/soc/codecs/ab8500-codec.c386
-rw-r--r--sound/soc/codecs/ab8500-codec.h7
-rw-r--r--sound/soc/codecs/ac97.c63
-rw-r--r--sound/soc/codecs/ad1836.c78
-rw-r--r--sound/soc/codecs/ad1836.h3
-rw-r--r--sound/soc/codecs/ad193x-i2c.c17
-rw-r--r--sound/soc/codecs/ad193x-spi.c10
-rw-r--r--sound/soc/codecs/ad193x.c214
-rw-r--r--sound/soc/codecs/ad193x.h15
-rw-r--r--sound/soc/codecs/ad1980.c94
-rw-r--r--sound/soc/codecs/ad73311.c32
-rw-r--r--sound/soc/codecs/ad73311.h17
-rw-r--r--sound/soc/codecs/adau-utils.c60
-rw-r--r--sound/soc/codecs/adau-utils.h8
-rw-r--r--sound/soc/codecs/adau1372-i2c.c41
-rw-r--r--sound/soc/codecs/adau1372-spi.c59
-rw-r--r--sound/soc/codecs/adau1372.c1073
-rw-r--r--sound/soc/codecs/adau1372.h22
-rw-r--r--sound/soc/codecs/adau1373.c346
-rw-r--r--sound/soc/codecs/adau1373.h1
-rw-r--r--sound/soc/codecs/adau1701.c211
-rw-r--r--sound/soc/codecs/adau1701.h3
-rw-r--r--sound/soc/codecs/adau1761-i2c.c27
-rw-r--r--sound/soc/codecs/adau1761-spi.c22
-rw-r--r--sound/soc/codecs/adau1761.c319
-rw-r--r--sound/soc/codecs/adau1761.h3
-rw-r--r--sound/soc/codecs/adau1781-i2c.c23
-rw-r--r--sound/soc/codecs/adau1781-spi.c18
-rw-r--r--sound/soc/codecs/adau1781.c55
-rw-r--r--sound/soc/codecs/adau1781.h3
-rw-r--r--sound/soc/codecs/adau17x1.c496
-rw-r--r--sound/soc/codecs/adau17x1.h27
-rw-r--r--sound/soc/codecs/adau1977-i2c.c15
-rw-r--r--sound/soc/codecs/adau1977-spi.c20
-rw-r--r--sound/soc/codecs/adau1977.c118
-rw-r--r--sound/soc/codecs/adau1977.h3
-rw-r--r--sound/soc/codecs/adau7002.c131
-rw-r--r--sound/soc/codecs/adau7118-hw.c43
-rw-r--r--sound/soc/codecs/adau7118-i2c.c88
-rw-r--r--sound/soc/codecs/adau7118.c575
-rw-r--r--sound/soc/codecs/adau7118.h24
-rw-r--r--sound/soc/codecs/adav801.c10
-rw-r--r--sound/soc/codecs/adav803.c15
-rw-r--r--sound/soc/codecs/adav80x.c142
-rw-r--r--sound/soc/codecs/adav80x.h3
-rw-r--r--sound/soc/codecs/ads117x.c40
-rw-r--r--sound/soc/codecs/ak4104.c103
-rw-r--r--sound/soc/codecs/ak4118.c416
-rw-r--r--sound/soc/codecs/ak4375.c606
-rw-r--r--sound/soc/codecs/ak4458.c813
-rw-r--r--sound/soc/codecs/ak4458.h90
-rw-r--r--sound/soc/codecs/ak4535.c93
-rw-r--r--sound/soc/codecs/ak4535.h5
-rw-r--r--sound/soc/codecs/ak4554.c43
-rw-r--r--sound/soc/codecs/ak4613.c640
-rw-r--r--sound/soc/codecs/ak4619.c912
-rw-r--r--sound/soc/codecs/ak4641.c125
-rw-r--r--sound/soc/codecs/ak4641.h47
-rw-r--r--sound/soc/codecs/ak4642.c175
-rw-r--r--sound/soc/codecs/ak4671.c82
-rw-r--r--sound/soc/codecs/ak4671.h7
-rw-r--r--sound/soc/codecs/ak5386.c95
-rw-r--r--sound/soc/codecs/ak5558.c508
-rw-r--r--sound/soc/codecs/ak5558.h52
-rw-r--r--sound/soc/codecs/alc5623.c196
-rw-r--r--sound/soc/codecs/alc5623.h6
-rw-r--r--sound/soc/codecs/alc5632.c177
-rw-r--r--sound/soc/codecs/alc5632.h5
-rw-r--r--sound/soc/codecs/arizona-jack.c1668
-rw-r--r--sound/soc/codecs/arizona.c825
-rw-r--r--sound/soc/codecs/arizona.h192
-rw-r--r--sound/soc/codecs/audio-iio-aux.c314
-rw-r--r--sound/soc/codecs/aw8738.c104
-rw-r--r--sound/soc/codecs/aw87390.c461
-rw-r--r--sound/soc/codecs/aw87390.h85
-rw-r--r--sound/soc/codecs/aw88081.c1317
-rw-r--r--sound/soc/codecs/aw88081.h329
-rw-r--r--sound/soc/codecs/aw88166.c1815
-rw-r--r--sound/soc/codecs/aw88166.h529
-rw-r--r--sound/soc/codecs/aw88261.c1282
-rw-r--r--sound/soc/codecs/aw88261.h459
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c576
-rw-r--r--sound/soc/codecs/aw88395/aw88395.h58
-rw-r--r--sound/soc/codecs/aw88395/aw88395_data_type.h142
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c1721
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h218
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c1174
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.h92
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h383
-rw-r--r--sound/soc/codecs/aw88399.c2173
-rw-r--r--sound/soc/codecs/aw88399.h629
-rw-r--r--sound/soc/codecs/bd28623.c239
-rw-r--r--sound/soc/codecs/bt-sco.c91
-rw-r--r--sound/soc/codecs/chv3-codec.c42
-rw-r--r--sound/soc/codecs/cirrus_legacy.h21
-rw-r--r--sound/soc/codecs/cpcap.c1884
-rw-r--r--sound/soc/codecs/cq93vc.c64
-rw-r--r--sound/soc/codecs/cros_ec_codec.c1064
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c2451
-rw-r--r--sound/soc/codecs/cs-amp-lib.c737
-rw-r--r--sound/soc/codecs/cs35l32.c148
-rw-r--r--sound/soc/codecs/cs35l32.h6
-rw-r--r--sound/soc/codecs/cs35l33.c1287
-rw-r--r--sound/soc/codecs/cs35l33.h217
-rw-r--r--sound/soc/codecs/cs35l34.c1217
-rw-r--r--sound/soc/codecs/cs35l34.h265
-rw-r--r--sound/soc/codecs/cs35l35.c1662
-rw-r--r--sound/soc/codecs/cs35l35.h297
-rw-r--r--sound/soc/codecs/cs35l36.c1940
-rw-r--r--sound/soc/codecs/cs35l36.h446
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c96
-rw-r--r--sound/soc/codecs/cs35l41-lib.c1592
-rw-r--r--sound/soc/codecs/cs35l41-spi.c99
-rw-r--r--sound/soc/codecs/cs35l41.c1492
-rw-r--r--sound/soc/codecs/cs35l41.h41
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c76
-rw-r--r--sound/soc/codecs/cs35l45-spi.c78
-rw-r--r--sound/soc/codecs/cs35l45-tables.c332
-rw-r--r--sound/soc/codecs/cs35l45.c1513
-rw-r--r--sound/soc/codecs/cs35l45.h514
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c108
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c606
-rw-r--r--sound/soc/codecs/cs35l56-shared.c1740
-rw-r--r--sound/soc/codecs/cs35l56-spi.c95
-rw-r--r--sound/soc/codecs/cs35l56.c1814
-rw-r--r--sound/soc/codecs/cs35l56.h77
-rw-r--r--sound/soc/codecs/cs40l50-codec.c307
-rw-r--r--sound/soc/codecs/cs4234.c917
-rw-r--r--sound/soc/codecs/cs4234.h287
-rw-r--r--sound/soc/codecs/cs4265.c146
-rw-r--r--sound/soc/codecs/cs4265.h6
-rw-r--r--sound/soc/codecs/cs4270.c217
-rw-r--r--sound/soc/codecs/cs4271-i2c.c24
-rw-r--r--sound/soc/codecs/cs4271-spi.c19
-rw-r--r--sound/soc/codecs/cs4271.c243
-rw-r--r--sound/soc/codecs/cs4271.h1
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c104
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c625
-rw-r--r--sound/soc/codecs/cs42l42.c2495
-rw-r--r--sound/soc/codecs/cs42l42.h82
-rw-r--r--sound/soc/codecs/cs42l43-jack.c962
-rw-r--r--sound/soc/codecs/cs42l43-sdw.c71
-rw-r--r--sound/soc/codecs/cs42l43.c2476
-rw-r--r--sound/soc/codecs/cs42l43.h146
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c34
-rw-r--r--sound/soc/codecs/cs42l51.c415
-rw-r--r--sound/soc/codecs/cs42l51.h15
-rw-r--r--sound/soc/codecs/cs42l52.c312
-rw-r--r--sound/soc/codecs/cs42l52.h6
-rw-r--r--sound/soc/codecs/cs42l56.c335
-rw-r--r--sound/soc/codecs/cs42l56.h6
-rw-r--r--sound/soc/codecs/cs42l73.c260
-rw-r--r--sound/soc/codecs/cs42l73.h16
-rw-r--r--sound/soc/codecs/cs42l83-i2c.c240
-rw-r--r--sound/soc/codecs/cs42l84.c1111
-rw-r--r--sound/soc/codecs/cs42l84.h210
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c32
-rw-r--r--sound/soc/codecs/cs42xx8.c245
-rw-r--r--sound/soc/codecs/cs42xx8.h3
-rw-r--r--sound/soc/codecs/cs43130.c2781
-rw-r--r--sound/soc/codecs/cs43130.h541
-rw-r--r--sound/soc/codecs/cs4341.c352
-rw-r--r--sound/soc/codecs/cs4349.c66
-rw-r--r--sound/soc/codecs/cs4349.h11
-rw-r--r--sound/soc/codecs/cs47l15.c1505
-rw-r--r--sound/soc/codecs/cs47l24.c421
-rw-r--r--sound/soc/codecs/cs47l24.h5
-rw-r--r--sound/soc/codecs/cs47l35.c1782
-rw-r--r--sound/soc/codecs/cs47l85.c2733
-rw-r--r--sound/soc/codecs/cs47l90.c2657
-rw-r--r--sound/soc/codecs/cs47l92.c2103
-rw-r--r--sound/soc/codecs/cs48l32-tables.c538
-rw-r--r--sound/soc/codecs/cs48l32.c4076
-rw-r--r--sound/soc/codecs/cs48l32.h403
-rw-r--r--sound/soc/codecs/cs530x-i2c.c88
-rw-r--r--sound/soc/codecs/cs530x-spi.c92
-rw-r--r--sound/soc/codecs/cs530x.c1343
-rw-r--r--sound/soc/codecs/cs530x.h253
-rw-r--r--sound/soc/codecs/cs53l30.c1107
-rw-r--r--sound/soc/codecs/cs53l30.h455
-rw-r--r--sound/soc/codecs/cx20442.c173
-rw-r--r--sound/soc/codecs/cx20442.h7
-rw-r--r--sound/soc/codecs/cx2072x.c1718
-rw-r--r--sound/soc/codecs/cx2072x.h314
-rw-r--r--sound/soc/codecs/da7210.c224
-rw-r--r--sound/soc/codecs/da7213.c1034
-rw-r--r--sound/soc/codecs/da7213.h129
-rw-r--r--sound/soc/codecs/da7218.c603
-rw-r--r--sound/soc/codecs/da7218.h33
-rw-r--r--sound/soc/codecs/da7219-aad.c593
-rw-r--r--sound/soc/codecs/da7219-aad.h32
-rw-r--r--sound/soc/codecs/da7219.c1595
-rw-r--r--sound/soc/codecs/da7219.h60
-rw-r--r--sound/soc/codecs/da732x.c272
-rw-r--r--sound/soc/codecs/da732x.h17
-rw-r--r--sound/soc/codecs/da732x_reg.h5
-rw-r--r--sound/soc/codecs/da9055.c203
-rw-r--r--sound/soc/codecs/dmic.c192
-rw-r--r--sound/soc/codecs/es7134.c316
-rw-r--r--sound/soc/codecs/es7241.c311
-rw-r--r--sound/soc/codecs/es8311.c974
-rw-r--r--sound/soc/codecs/es8311.h162
-rw-r--r--sound/soc/codecs/es8316.c933
-rw-r--r--sound/soc/codecs/es8316.h135
-rw-r--r--sound/soc/codecs/es8323.c791
-rw-r--r--sound/soc/codecs/es8323.h78
-rw-r--r--sound/soc/codecs/es8326.c1374
-rw-r--r--sound/soc/codecs/es8326.h200
-rw-r--r--sound/soc/codecs/es8328-i2c.c21
-rw-r--r--sound/soc/codecs/es8328-spi.c12
-rw-r--r--sound/soc/codecs/es8328.c386
-rw-r--r--sound/soc/codecs/es8328.h61
-rw-r--r--sound/soc/codecs/es8375.c794
-rw-r--r--sound/soc/codecs/es8375.h123
-rw-r--r--sound/soc/codecs/es8389.c962
-rw-r--r--sound/soc/codecs/es8389.h140
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.c89
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.h393
-rw-r--r--sound/soc/codecs/framer-codec.c413
-rw-r--r--sound/soc/codecs/fs-amp-lib.c265
-rw-r--r--sound/soc/codecs/fs-amp-lib.h150
-rw-r--r--sound/soc/codecs/fs210x.c1586
-rw-r--r--sound/soc/codecs/fs210x.h75
-rw-r--r--sound/soc/codecs/gtm601.c60
-rw-r--r--sound/soc/codecs/hda-dai.c105
-rw-r--r--sound/soc/codecs/hda.c396
-rw-r--r--sound/soc/codecs/hda.h19
-rw-r--r--sound/soc/codecs/hdac_hda.c677
-rw-r--r--sound/soc/codecs/hdac_hda.h34
-rw-r--r--sound/soc/codecs/hdac_hdmi.c1983
-rw-r--r--sound/soc/codecs/hdmi-codec.c1212
-rw-r--r--sound/soc/codecs/ics43432.c22
-rw-r--r--sound/soc/codecs/idt821034.c1183
-rw-r--r--sound/soc/codecs/inno_rk3036.c82
-rw-r--r--sound/soc/codecs/inno_rk3036.h1
-rw-r--r--sound/soc/codecs/isabelle.c87
-rw-r--r--sound/soc/codecs/isabelle.h6
-rw-r--r--sound/soc/codecs/jz4725b.c667
-rw-r--r--sound/soc/codecs/jz4740.c97
-rw-r--r--sound/soc/codecs/jz4760.c872
-rw-r--r--sound/soc/codecs/jz4770.c919
-rw-r--r--sound/soc/codecs/l3.c91
-rw-r--r--sound/soc/codecs/lm4857.c14
-rw-r--r--sound/soc/codecs/lm49453.c118
-rw-r--r--sound/soc/codecs/lm49453.h6
-rw-r--r--sound/soc/codecs/lochnagar-sc.c266
-rw-r--r--sound/soc/codecs/lpass-macro-common.c93
-rw-r--r--sound/soc/codecs/lpass-macro-common.h77
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c4035
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c2543
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1781
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c2939
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.h17
-rw-r--r--sound/soc/codecs/madera.c4796
-rw-r--r--sound/soc/codecs/madera.h458
-rw-r--r--sound/soc/codecs/max9759.c198
-rw-r--r--sound/soc/codecs/max9768.c72
-rw-r--r--sound/soc/codecs/max98088.c562
-rw-r--r--sound/soc/codecs/max98088.h5
-rw-r--r--sound/soc/codecs/max98090.c594
-rw-r--r--sound/soc/codecs/max98090.h13
-rw-r--r--sound/soc/codecs/max98095.c525
-rw-r--r--sound/soc/codecs/max98095.h7
-rw-r--r--sound/soc/codecs/max98357a.c113
-rw-r--r--sound/soc/codecs/max98363.c465
-rw-r--r--sound/soc/codecs/max98363.h36
-rw-r--r--sound/soc/codecs/max98371.c430
-rw-r--r--sound/soc/codecs/max98371.h63
-rw-r--r--sound/soc/codecs/max98373-i2c.c616
-rw-r--r--sound/soc/codecs/max98373-sdw.c888
-rw-r--r--sound/soc/codecs/max98373-sdw.h72
-rw-r--r--sound/soc/codecs/max98373.c510
-rw-r--r--sound/soc/codecs/max98373.h240
-rw-r--r--sound/soc/codecs/max98388.c1013
-rw-r--r--sound/soc/codecs/max98388.h234
-rw-r--r--sound/soc/codecs/max98390.c1135
-rw-r--r--sound/soc/codecs/max98390.h667
-rw-r--r--sound/soc/codecs/max98396.c1914
-rw-r--r--sound/soc/codecs/max98396.h327
-rw-r--r--sound/soc/codecs/max9850.c117
-rw-r--r--sound/soc/codecs/max9850.h7
-rw-r--r--sound/soc/codecs/max98504.c382
-rw-r--r--sound/soc/codecs/max98504.h56
-rw-r--r--sound/soc/codecs/max98520.c766
-rw-r--r--sound/soc/codecs/max98520.h159
-rw-r--r--sound/soc/codecs/max9860.c738
-rw-r--r--sound/soc/codecs/max9860.h154
-rw-r--r--sound/soc/codecs/max9867.c718
-rw-r--r--sound/soc/codecs/max9867.h67
-rw-r--r--sound/soc/codecs/max9877.c12
-rw-r--r--sound/soc/codecs/max9877.h9
-rw-r--r--sound/soc/codecs/max98925.c119
-rw-r--r--sound/soc/codecs/max98925.h7
-rw-r--r--sound/soc/codecs/max98926.c593
-rw-r--r--sound/soc/codecs/max98926.h846
-rw-r--r--sound/soc/codecs/max98927.c914
-rw-r--r--sound/soc/codecs/max98927.h271
-rw-r--r--sound/soc/codecs/mc13783.c110
-rw-r--r--sound/soc/codecs/mc13783.h14
-rw-r--r--sound/soc/codecs/ml26124.c135
-rw-r--r--sound/soc/codecs/ml26124.h14
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c1280
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c1249
-rw-r--r--sound/soc/codecs/mt6351.c1496
-rw-r--r--sound/soc/codecs/mt6351.h105
-rw-r--r--sound/soc/codecs/mt6357.c1855
-rw-r--r--sound/soc/codecs/mt6357.h660
-rw-r--r--sound/soc/codecs/mt6358.c2430
-rw-r--r--sound/soc/codecs/mt6358.h2310
-rw-r--r--sound/soc/codecs/mt6359-accdet.c1062
-rw-r--r--sound/soc/codecs/mt6359-accdet.h137
-rw-r--r--sound/soc/codecs/mt6359.c2960
-rw-r--r--sound/soc/codecs/mt6359.h4289
-rw-r--r--sound/soc/codecs/mt6660.c580
-rw-r--r--sound/soc/codecs/mt6660.h77
-rw-r--r--sound/soc/codecs/nau8315.c167
-rw-r--r--sound/soc/codecs/nau8325.c899
-rw-r--r--sound/soc/codecs/nau8325.h391
-rw-r--r--sound/soc/codecs/nau8540.c993
-rw-r--r--sound/soc/codecs/nau8540.h265
-rw-r--r--sound/soc/codecs/nau8810.c929
-rw-r--r--sound/soc/codecs/nau8810.h286
-rw-r--r--sound/soc/codecs/nau8821.c1984
-rw-r--r--sound/soc/codecs/nau8821.h586
-rw-r--r--sound/soc/codecs/nau8822.c1221
-rw-r--r--sound/soc/codecs/nau8822.h224
-rw-r--r--sound/soc/codecs/nau8824.c2058
-rw-r--r--sound/soc/codecs/nau8824.h478
-rw-r--r--sound/soc/codecs/nau8825.c2144
-rw-r--r--sound/soc/codecs/nau8825.h223
-rw-r--r--sound/soc/codecs/ntp8835.c480
-rw-r--r--sound/soc/codecs/ntp8918.c396
-rw-r--r--sound/soc/codecs/ntpfw.c137
-rw-r--r--sound/soc/codecs/ntpfw.h23
-rw-r--r--sound/soc/codecs/pcm1681.c79
-rw-r--r--sound/soc/codecs/pcm1754.c185
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c63
-rw-r--r--sound/soc/codecs/pcm1789.c271
-rw-r--r--sound/soc/codecs/pcm1789.h17
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c59
-rw-r--r--sound/soc/codecs/pcm179x-spi.c58
-rw-r--r--sound/soc/codecs/pcm179x.c96
-rw-r--r--sound/soc/codecs/pcm179x.h19
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c60
-rw-r--r--sound/soc/codecs/pcm186x-spi.c61
-rw-r--r--sound/soc/codecs/pcm186x.c706
-rw-r--r--sound/soc/codecs/pcm186x.h219
-rw-r--r--sound/soc/codecs/pcm3008.c94
-rw-r--r--sound/soc/codecs/pcm3008.h22
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c59
-rw-r--r--sound/soc/codecs/pcm3060-spi.c59
-rw-r--r--sound/soc/codecs/pcm3060.c347
-rw-r--r--sound/soc/codecs/pcm3060.h96
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c23
-rw-r--r--sound/soc/codecs/pcm3168a-spi.c11
-rw-r--r--sound/soc/codecs/pcm3168a.c495
-rw-r--r--sound/soc/codecs/pcm3168a.h5
-rw-r--r--sound/soc/codecs/pcm5102a.c57
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c47
-rw-r--r--sound/soc/codecs/pcm512x-spi.c23
-rw-r--r--sound/soc/codecs/pcm512x.c528
-rw-r--r--sound/soc/codecs/pcm512x.h14
-rw-r--r--sound/soc/codecs/pcm6240.c2167
-rw-r--r--sound/soc/codecs/pcm6240.h247
-rw-r--r--sound/soc/codecs/peb2466.c2064
-rw-r--r--sound/soc/codecs/pm4125-sdw.c495
-rw-r--r--sound/soc/codecs/pm4125.c1761
-rw-r--r--sound/soc/codecs/pm4125.h293
-rw-r--r--sound/soc/codecs/rk3308_codec.c975
-rw-r--r--sound/soc/codecs/rk3308_codec.h579
-rw-r--r--sound/soc/codecs/rk3328_codec.c534
-rw-r--r--sound/soc/codecs/rk3328_codec.h210
-rw-r--r--sound/soc/codecs/rk817_codec.c540
-rw-r--r--sound/soc/codecs/rl6231.c130
-rw-r--r--sound/soc/codecs/rl6231.h8
-rw-r--r--sound/soc/codecs/rl6347a.c11
-rw-r--r--sound/soc/codecs/rl6347a.h5
-rw-r--r--sound/soc/codecs/rt-sdw-common.c238
-rw-r--r--sound/soc/codecs/rt-sdw-common.h66
-rw-r--r--sound/soc/codecs/rt1011.c2486
-rw-r--r--sound/soc/codecs/rt1011.h704
-rw-r--r--sound/soc/codecs/rt1015.c1190
-rw-r--r--sound/soc/codecs/rt1015.h449
-rw-r--r--sound/soc/codecs/rt1015p.c154
-rw-r--r--sound/soc/codecs/rt1016.c693
-rw-r--r--sound/soc/codecs/rt1016.h232
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c822
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.h183
-rw-r--r--sound/soc/codecs/rt1019.c609
-rw-r--r--sound/soc/codecs/rt1019.h164
-rw-r--r--sound/soc/codecs/rt1305.c1179
-rw-r--r--sound/soc/codecs/rt1305.h273
-rw-r--r--sound/soc/codecs/rt1308-sdw.c817
-rw-r--r--sound/soc/codecs/rt1308-sdw.h172
-rw-r--r--sound/soc/codecs/rt1308.c871
-rw-r--r--sound/soc/codecs/rt1308.h294
-rw-r--r--sound/soc/codecs/rt1316-sdw.c794
-rw-r--r--sound/soc/codecs/rt1316-sdw.h52
-rw-r--r--sound/soc/codecs/rt1318-sdw.c868
-rw-r--r--sound/soc/codecs/rt1318-sdw.h96
-rw-r--r--sound/soc/codecs/rt1318.c1353
-rw-r--r--sound/soc/codecs/rt1318.h342
-rw-r--r--sound/soc/codecs/rt1320-sdw.c1823
-rw-r--r--sound/soc/codecs/rt1320-sdw.h113
-rw-r--r--sound/soc/codecs/rt274.c1234
-rw-r--r--sound/soc/codecs/rt274.h214
-rw-r--r--sound/soc/codecs/rt286.c338
-rw-r--r--sound/soc/codecs/rt286.h7
-rw-r--r--sound/soc/codecs/rt298.c390
-rw-r--r--sound/soc/codecs/rt298.h17
-rw-r--r--sound/soc/codecs/rt5514-spi.c514
-rw-r--r--sound/soc/codecs/rt5514-spi.h37
-rw-r--r--sound/soc/codecs/rt5514.c1335
-rw-r--r--sound/soc/codecs/rt5514.h286
-rw-r--r--sound/soc/codecs/rt5616.c659
-rw-r--r--sound/soc/codecs/rt5616.h5
-rw-r--r--sound/soc/codecs/rt5631.c376
-rw-r--r--sound/soc/codecs/rt5631.h1
-rw-r--r--sound/soc/codecs/rt5640.c1229
-rw-r--r--sound/soc/codecs/rt5640.h139
-rw-r--r--sound/soc/codecs/rt5645.c1201
-rw-r--r--sound/soc/codecs/rt5645.h29
-rw-r--r--sound/soc/codecs/rt5651.c879
-rw-r--r--sound/soc/codecs/rt5651.h38
-rw-r--r--sound/soc/codecs/rt5659.c708
-rw-r--r--sound/soc/codecs/rt5659.h34
-rw-r--r--sound/soc/codecs/rt5660.c1350
-rw-r--r--sound/soc/codecs/rt5660.h847
-rw-r--r--sound/soc/codecs/rt5663.c3756
-rw-r--r--sound/soc/codecs/rt5663.h1128
-rw-r--r--sound/soc/codecs/rt5665.c4868
-rw-r--r--sound/soc/codecs/rt5665.h2002
-rw-r--r--sound/soc/codecs/rt5668.c2584
-rw-r--r--sound/soc/codecs/rt5668.h1312
-rw-r--r--sound/soc/codecs/rt5670-dsp.h5
-rw-r--r--sound/soc/codecs/rt5670.c906
-rw-r--r--sound/soc/codecs/rt5670.h49
-rw-r--r--sound/soc/codecs/rt5677-spi.c456
-rw-r--r--sound/soc/codecs/rt5677-spi.h22
-rw-r--r--sound/soc/codecs/rt5677.c1207
-rw-r--r--sound/soc/codecs/rt5677.h188
-rw-r--r--sound/soc/codecs/rt5682-i2c.c348
-rw-r--r--sound/soc/codecs/rt5682-sdw.c811
-rw-r--r--sound/soc/codecs/rt5682.c3191
-rw-r--r--sound/soc/codecs/rt5682.h1499
-rw-r--r--sound/soc/codecs/rt5682s.c3349
-rw-r--r--sound/soc/codecs/rt5682s.h1492
-rw-r--r--sound/soc/codecs/rt700-sdw.c572
-rw-r--r--sound/soc/codecs/rt700-sdw.h335
-rw-r--r--sound/soc/codecs/rt700.c1235
-rw-r--r--sound/soc/codecs/rt700.h171
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c496
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.h99
-rw-r--r--sound/soc/codecs/rt711-sdca.c1669
-rw-r--r--sound/soc/codecs/rt711-sdca.h244
-rw-r--r--sound/soc/codecs/rt711-sdw.c585
-rw-r--r--sound/soc/codecs/rt711-sdw.h283
-rw-r--r--sound/soc/codecs/rt711.c1335
-rw-r--r--sound/soc/codecs/rt711.h251
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c984
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h107
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c510
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h75
-rw-r--r--sound/soc/codecs/rt712-sdca.c1926
-rw-r--r--sound/soc/codecs/rt712-sdca.h261
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c284
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.h171
-rw-r--r--sound/soc/codecs/rt715-sdca.c1072
-rw-r--r--sound/soc/codecs/rt715-sdca.h132
-rw-r--r--sound/soc/codecs/rt715-sdw.c551
-rw-r--r--sound/soc/codecs/rt715-sdw.h337
-rw-r--r--sound/soc/codecs/rt715.c1133
-rw-r--r--sound/soc/codecs/rt715.h224
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c548
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.h150
-rw-r--r--sound/soc/codecs/rt721-sdca.c1561
-rw-r--r--sound/soc/codecs/rt721-sdca.h274
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c557
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h122
-rw-r--r--sound/soc/codecs/rt722-sdca.c1583
-rw-r--r--sound/soc/codecs/rt722-sdca.h250
-rw-r--r--sound/soc/codecs/rt9120.c643
-rw-r--r--sound/soc/codecs/rt9123.c500
-rw-r--r--sound/soc/codecs/rt9123p.c171
-rw-r--r--sound/soc/codecs/rtq9124.c543
-rw-r--r--sound/soc/codecs/rtq9128.c789
-rw-r--r--sound/soc/codecs/sdw-mockup.c275
-rw-r--r--sound/soc/codecs/sgtl5000.c1156
-rw-r--r--sound/soc/codecs/sgtl5000.h18
-rw-r--r--sound/soc/codecs/si476x.c59
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c10
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c5
-rw-r--r--sound/soc/codecs/sigmadsp.c61
-rw-r--r--sound/soc/codecs/sigmadsp.h6
-rw-r--r--sound/soc/codecs/simple-amplifier.c110
-rw-r--r--sound/soc/codecs/simple-mux.c192
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c581
-rw-r--r--sound/soc/codecs/sirf-audio-codec.h125
-rw-r--r--sound/soc/codecs/sma1303.c1811
-rw-r--r--sound/soc/codecs/sma1303.h609
-rw-r--r--sound/soc/codecs/sma1307.c2044
-rw-r--r--sound/soc/codecs/sma1307.h444
-rw-r--r--sound/soc/codecs/sn95031.c934
-rw-r--r--sound/soc/codecs/sn95031.h133
-rw-r--r--sound/soc/codecs/spdif_receiver.c34
-rw-r--r--sound/soc/codecs/spdif_transmitter.c34
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c46
-rw-r--r--sound/soc/codecs/src4xxx.c518
-rw-r--r--sound/soc/codecs/src4xxx.h113
-rw-r--r--sound/soc/codecs/ssm2305.c99
-rw-r--r--sound/soc/codecs/ssm2518.c106
-rw-r--r--sound/soc/codecs/ssm2518.h3
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c15
-rw-r--r--sound/soc/codecs/ssm2602-spi.c10
-rw-r--r--sound/soc/codecs/ssm2602.c209
-rw-r--r--sound/soc/codecs/ssm2602.h16
-rw-r--r--sound/soc/codecs/ssm3515.c448
-rw-r--r--sound/soc/codecs/ssm4567.c73
-rw-r--r--sound/soc/codecs/sta32x.c245
-rw-r--r--sound/soc/codecs/sta32x.h6
-rw-r--r--sound/soc/codecs/sta350.c216
-rw-r--r--sound/soc/codecs/sta350.h8
-rw-r--r--sound/soc/codecs/sta529.c86
-rw-r--r--sound/soc/codecs/stac9766.c229
-rw-r--r--sound/soc/codecs/stac9766.h17
-rw-r--r--sound/soc/codecs/sti-sas.c289
-rw-r--r--sound/soc/codecs/tas2552.c259
-rw-r--r--sound/soc/codecs/tas2552.h12
-rw-r--r--sound/soc/codecs/tas2562.c781
-rw-r--r--sound/soc/codecs/tas2562.h90
-rw-r--r--sound/soc/codecs/tas2764-quirks.h180
-rw-r--r--sound/soc/codecs/tas2764.c950
-rw-r--r--sound/soc/codecs/tas2764.h129
-rw-r--r--sound/soc/codecs/tas2770.c890
-rw-r--r--sound/soc/codecs/tas2770.h151
-rw-r--r--sound/soc/codecs/tas2780.c652
-rw-r--r--sound/soc/codecs/tas2780.h101
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c371
-rw-r--r--sound/soc/codecs/tas2781-comlib.c221
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c2618
-rw-r--r--sound/soc/codecs/tas2781-i2c.c2132
-rw-r--r--sound/soc/codecs/tas2783-sdw.c1347
-rw-r--r--sound/soc/codecs/tas2783.h110
-rw-r--r--sound/soc/codecs/tas5086.c139
-rw-r--r--sound/soc/codecs/tas571x.c715
-rw-r--r--sound/soc/codecs/tas571x.h118
-rw-r--r--sound/soc/codecs/tas5720.c827
-rw-r--r--sound/soc/codecs/tas5720.h127
-rw-r--r--sound/soc/codecs/tas5805m.c611
-rw-r--r--sound/soc/codecs/tas6424.c816
-rw-r--r--sound/soc/codecs/tas6424.h158
-rw-r--r--sound/soc/codecs/tda7419.c641
-rw-r--r--sound/soc/codecs/tfa9879.c112
-rw-r--r--sound/soc/codecs/tfa9879.h7
-rw-r--r--sound/soc/codecs/tfa989x.c425
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c1522
-rw-r--r--sound/soc/codecs/tlv320adcx140.c1215
-rw-r--r--sound/soc/codecs/tlv320adcx140.h159
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c19
-rw-r--r--sound/soc/codecs/tlv320aic23-spi.c12
-rw-r--r--sound/soc/codecs/tlv320aic23.c144
-rw-r--r--sound/soc/codecs/tlv320aic23.h5
-rw-r--r--sound/soc/codecs/tlv320aic26.c117
-rw-r--r--sound/soc/codecs/tlv320aic26.h7
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c1282
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h387
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c499
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c70
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c70
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c1394
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h348
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c72
-rw-r--r--sound/soc/codecs/tlv320aic3x-spi.c77
-rw-r--r--sound/soc/codecs/tlv320aic3x.c634
-rw-r--r--sound/soc/codecs/tlv320aic3x.h69
-rw-r--r--sound/soc/codecs/tlv320dac33.c556
-rw-r--r--sound/soc/codecs/tlv320dac33.h16
-rw-r--r--sound/soc/codecs/tpa6130a2.c470
-rw-r--r--sound/soc/codecs/tpa6130a2.h30
-rw-r--r--sound/soc/codecs/ts3a227e.c112
-rw-r--r--sound/soc/codecs/ts3a227e.h5
-rw-r--r--sound/soc/codecs/tscs42xx.c1512
-rw-r--r--sound/soc/codecs/tscs42xx.h2701
-rw-r--r--sound/soc/codecs/tscs454.c3481
-rw-r--r--sound/soc/codecs/tscs454.h2323
-rw-r--r--sound/soc/codecs/twl4030.c559
-rw-r--r--sound/soc/codecs/twl6040.c320
-rw-r--r--sound/soc/codecs/twl6040.h26
-rw-r--r--sound/soc/codecs/uda1334.c294
-rw-r--r--sound/soc/codecs/uda1342.c347
-rw-r--r--sound/soc/codecs/uda1342.h78
-rw-r--r--sound/soc/codecs/uda134x.c587
-rw-r--r--sound/soc/codecs/uda134x.h32
-rw-r--r--sound/soc/codecs/uda1380.c294
-rw-r--r--sound/soc/codecs/uda1380.h9
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c905
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.h66
-rw-r--r--sound/soc/codecs/wcd-common.c144
-rw-r--r--sound/soc/codecs/wcd-common.h46
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1647
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h343
-rw-r--r--sound/soc/codecs/wcd9335.c5168
-rw-r--r--sound/soc/codecs/wcd9335.h641
-rw-r--r--sound/soc/codecs/wcd934x.c5919
-rw-r--r--sound/soc/codecs/wcd937x-sdw.c1115
-rw-r--r--sound/soc/codecs/wcd937x.c2987
-rw-r--r--sound/soc/codecs/wcd937x.h618
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c1279
-rw-r--r--sound/soc/codecs/wcd938x.c3559
-rw-r--r--sound/soc/codecs/wcd938x.h686
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c1477
-rw-r--r--sound/soc/codecs/wcd939x.c3623
-rw-r--r--sound/soc/codecs/wcd939x.h947
-rw-r--r--sound/soc/codecs/wl1273.c522
-rw-r--r--sound/soc/codecs/wl1273.h30
-rw-r--r--sound/soc/codecs/wm0010.c271
-rw-r--r--sound/soc/codecs/wm1250-ev1.c158
-rw-r--r--sound/soc/codecs/wm2000.c148
-rw-r--r--sound/soc/codecs/wm2000.h5
-rw-r--r--sound/soc/codecs/wm2200.c328
-rw-r--r--sound/soc/codecs/wm2200.h6
-rw-r--r--sound/soc/codecs/wm5100-tables.c18
-rw-r--r--sound/soc/codecs/wm5100.c427
-rw-r--r--sound/soc/codecs/wm5100.h8
-rw-r--r--sound/soc/codecs/wm5102.c410
-rw-r--r--sound/soc/codecs/wm5102.h5
-rw-r--r--sound/soc/codecs/wm5110.c571
-rw-r--r--sound/soc/codecs/wm5110.h5
-rw-r--r--sound/soc/codecs/wm8350.c234
-rw-r--r--sound/soc/codecs/wm8350.h10
-rw-r--r--sound/soc/codecs/wm8400.c253
-rw-r--r--sound/soc/codecs/wm8400.h7
-rw-r--r--sound/soc/codecs/wm8510.c174
-rw-r--r--sound/soc/codecs/wm8510.h5
-rw-r--r--sound/soc/codecs/wm8523.c147
-rw-r--r--sound/soc/codecs/wm8523.h7
-rw-r--r--sound/soc/codecs/wm8524.c282
-rw-r--r--sound/soc/codecs/wm8580.c280
-rw-r--r--sound/soc/codecs/wm8580.h7
-rw-r--r--sound/soc/codecs/wm8711.c139
-rw-r--r--sound/soc/codecs/wm8711.h5
-rw-r--r--sound/soc/codecs/wm8727.c32
-rw-r--r--sound/soc/codecs/wm8728.c103
-rw-r--r--sound/soc/codecs/wm8728.h5
-rw-r--r--sound/soc/codecs/wm8731-i2c.c68
-rw-r--r--sound/soc/codecs/wm8731-spi.c59
-rw-r--r--sound/soc/codecs/wm8731.c362
-rw-r--r--sound/soc/codecs/wm8731.h34
-rw-r--r--sound/soc/codecs/wm8737.c130
-rw-r--r--sound/soc/codecs/wm8737.h5
-rw-r--r--sound/soc/codecs/wm8741.c207
-rw-r--r--sound/soc/codecs/wm8741.h5
-rw-r--r--sound/soc/codecs/wm8750.c140
-rw-r--r--sound/soc/codecs/wm8750.h6
-rw-r--r--sound/soc/codecs/wm8753.c337
-rw-r--r--sound/soc/codecs/wm8753.h10
-rw-r--r--sound/soc/codecs/wm8770.c167
-rw-r--r--sound/soc/codecs/wm8770.h5
-rw-r--r--sound/soc/codecs/wm8776.c126
-rw-r--r--sound/soc/codecs/wm8776.h5
-rw-r--r--sound/soc/codecs/wm8782.c141
-rw-r--r--sound/soc/codecs/wm8804-i2c.c30
-rw-r--r--sound/soc/codecs/wm8804-spi.c10
-rw-r--r--sound/soc/codecs/wm8804.c121
-rw-r--r--sound/soc/codecs/wm8804.h5
-rw-r--r--sound/soc/codecs/wm8900.c295
-rw-r--r--sound/soc/codecs/wm8900.h5
-rw-r--r--sound/soc/codecs/wm8903.c324
-rw-r--r--sound/soc/codecs/wm8903.h8
-rw-r--r--sound/soc/codecs/wm8904.c876
-rw-r--r--sound/soc/codecs/wm8904.h6
-rw-r--r--sound/soc/codecs/wm8940.c306
-rw-r--r--sound/soc/codecs/wm8940.h8
-rw-r--r--sound/soc/codecs/wm8955.c214
-rw-r--r--sound/soc/codecs/wm8955.h5
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c365
-rw-r--r--sound/soc/codecs/wm8960.c593
-rw-r--r--sound/soc/codecs/wm8960.h9
-rw-r--r--sound/soc/codecs/wm8961.c270
-rw-r--r--sound/soc/codecs/wm8961.h5
-rw-r--r--sound/soc/codecs/wm8962.c681
-rw-r--r--sound/soc/codecs/wm8962.h13
-rw-r--r--sound/soc/codecs/wm8971.c133
-rw-r--r--sound/soc/codecs/wm8971.h7
-rw-r--r--sound/soc/codecs/wm8974.c270
-rw-r--r--sound/soc/codecs/wm8974.h5
-rw-r--r--sound/soc/codecs/wm8978.c221
-rw-r--r--sound/soc/codecs/wm8978.h7
-rw-r--r--sound/soc/codecs/wm8983.c207
-rw-r--r--sound/soc/codecs/wm8983.h5
-rw-r--r--sound/soc/codecs/wm8985.c356
-rw-r--r--sound/soc/codecs/wm8985.h43
-rw-r--r--sound/soc/codecs/wm8988.c156
-rw-r--r--sound/soc/codecs/wm8988.h6
-rw-r--r--sound/soc/codecs/wm8990.c299
-rw-r--r--sound/soc/codecs/wm8990.h7
-rw-r--r--sound/soc/codecs/wm8991.c225
-rw-r--r--sound/soc/codecs/wm8991.h6
-rw-r--r--sound/soc/codecs/wm8993.c286
-rw-r--r--sound/soc/codecs/wm8993.h1
-rw-r--r--sound/soc/codecs/wm8994.c1155
-rw-r--r--sound/soc/codecs/wm8994.h37
-rw-r--r--sound/soc/codecs/wm8995.c332
-rw-r--r--sound/soc/codecs/wm8995.h5
-rw-r--r--sound/soc/codecs/wm8996.c548
-rw-r--r--sound/soc/codecs/wm8996.h10
-rw-r--r--sound/soc/codecs/wm8997.c210
-rw-r--r--sound/soc/codecs/wm8997.h5
-rw-r--r--sound/soc/codecs/wm8998.c357
-rw-r--r--sound/soc/codecs/wm8998.h5
-rw-r--r--sound/soc/codecs/wm9081.c269
-rw-r--r--sound/soc/codecs/wm9081.h5
-rw-r--r--sound/soc/codecs/wm9090.c132
-rw-r--r--sound/soc/codecs/wm9090.h15
-rw-r--r--sound/soc/codecs/wm9705.c236
-rw-r--r--sound/soc/codecs/wm9705.h11
-rw-r--r--sound/soc/codecs/wm9712.c299
-rw-r--r--sound/soc/codecs/wm9712.h11
-rw-r--r--sound/soc/codecs/wm9713.c268
-rw-r--r--sound/soc/codecs/wm9713.h5
-rw-r--r--sound/soc/codecs/wm_adsp.c3177
-rw-r--r--sound/soc/codecs/wm_adsp.h176
-rw-r--r--sound/soc/codecs/wm_hubs.c266
-rw-r--r--sound/soc/codecs/wm_hubs.h27
-rw-r--r--sound/soc/codecs/wmfw.h167
-rw-r--r--sound/soc/codecs/wsa881x.c1229
-rw-r--r--sound/soc/codecs/wsa883x.c1721
-rw-r--r--sound/soc/codecs/wsa884x.c2181
-rw-r--r--sound/soc/codecs/zl38060.c639
-rw-r--r--sound/soc/davinci/Kconfig101
-rw-r--r--sound/soc/davinci/Makefile15
-rw-r--r--sound/soc/davinci/davinci-evm.c504
-rw-r--r--sound/soc/davinci/davinci-vcif.c262
-rw-r--r--sound/soc/davinci/edma-pcm.h32
-rw-r--r--sound/soc/dwc/Kconfig19
-rw-r--r--sound/soc/dwc/Makefile3
-rw-r--r--sound/soc/dwc/designware_i2s.c749
-rw-r--r--sound/soc/dwc/dwc-i2s.c1104
-rw-r--r--sound/soc/dwc/dwc-pcm.c266
-rw-r--r--sound/soc/dwc/local.h167
-rw-r--r--sound/soc/fsl/Kconfig248
-rw-r--r--sound/soc/fsl/Makefile74
-rw-r--r--sound/soc/fsl/efika-audio-fabric.c28
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c78
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c866
-rw-r--r--sound/soc/fsl/fsl_asrc.c1112
-rw-r--r--sound/soc/fsl/fsl_asrc.h151
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h178
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c360
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c729
-rw-r--r--sound/soc/fsl/fsl_aud2htx.c309
-rw-r--r--sound/soc/fsl/fsl_aud2htx.h68
-rw-r--r--sound/soc/fsl/fsl_audmix.c569
-rw-r--r--sound/soc/fsl/fsl_audmix.h103
-rw-r--r--sound/soc/fsl/fsl_dma.c164
-rw-r--r--sound/soc/fsl/fsl_dma.h5
-rw-r--r--sound/soc/fsl/fsl_easrc.c2363
-rw-r--r--sound/soc/fsl/fsl_easrc.h655
-rw-r--r--sound/soc/fsl/fsl_esai.c629
-rw-r--r--sound/soc/fsl/fsl_esai.h5
-rw-r--r--sound/soc/fsl/fsl_micfil.c1618
-rw-r--r--sound/soc/fsl/fsl_micfil.h214
-rw-r--r--sound/soc/fsl/fsl_mqs.c471
-rw-r--r--sound/soc/fsl/fsl_qmc_audio.c974
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c353
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h47
-rw-r--r--sound/soc/fsl/fsl_sai.c1624
-rw-r--r--sound/soc/fsl/fsl_sai.h242
-rw-r--r--sound/soc/fsl/fsl_spdif.c750
-rw-r--r--sound/soc/fsl/fsl_spdif.h37
-rw-r--r--sound/soc/fsl/fsl_ssi.c1935
-rw-r--r--sound/soc/fsl/fsl_ssi.h436
-rw-r--r--sound/soc/fsl/fsl_ssi_dbg.c105
-rw-r--r--sound/soc/fsl/fsl_utils.c139
-rw-r--r--sound/soc/fsl/fsl_utils.h19
-rw-r--r--sound/soc/fsl/fsl_xcvr.c1843
-rw-r--r--sound/soc/fsl/fsl_xcvr.h403
-rw-r--r--sound/soc/fsl/imx-audio-rpmsg.c145
-rw-r--r--sound/soc/fsl/imx-audmix.c339
-rw-r--r--sound/soc/fsl/imx-audmux.c186
-rw-r--r--sound/soc/fsl/imx-audmux.h1
-rw-r--r--sound/soc/fsl/imx-card.c943
-rw-r--r--sound/soc/fsl/imx-es8328.c114
-rw-r--r--sound/soc/fsl/imx-hdmi.c224
-rw-r--r--sound/soc/fsl/imx-mc13783.c168
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c37
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c159
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c852
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.h512
-rw-r--r--sound/soc/fsl/imx-pcm.h24
-rw-r--r--sound/soc/fsl/imx-rpmsg.c254
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c68
-rw-r--r--sound/soc/fsl/imx-spdif.c103
-rw-r--r--sound/soc/fsl/imx-ssi.c657
-rw-r--r--sound/soc/fsl/imx-ssi.h8
-rw-r--r--sound/soc/fsl/imx-wm8962.c328
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.c372
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.h80
-rw-r--r--sound/soc/fsl/lpc3xxx-pcm.c72
-rw-r--r--sound/soc/fsl/mpc5200_dma.c146
-rw-r--r--sound/soc/fsl/mpc5200_dma.h1
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c32
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.h13
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c29
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c432
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c234
-rw-r--r--sound/soc/fsl/p1022_ds.c86
-rw-r--r--sound/soc/fsl/p1022_rdk.c116
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c61
-rw-r--r--sound/soc/fsl/phycore-ac97.c125
-rw-r--r--sound/soc/fsl/wm1133-ev1.c292
-rw-r--r--sound/soc/generic/Kconfig39
-rw-r--r--sound/soc/generic/Makefile15
-rw-r--r--sound/soc/generic/audio-graph-card.c661
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.c187
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample1.dtsi396
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample2.dtsi382
-rw-r--r--sound/soc/generic/audio-graph-card2.c1412
-rw-r--r--sound/soc/generic/simple-card-utils.c1262
-rw-r--r--sound/soc/generic/simple-card.c1148
-rw-r--r--sound/soc/generic/test-component.c657
-rw-r--r--sound/soc/google/Kconfig9
-rw-r--r--sound/soc/google/Makefile2
-rw-r--r--sound/soc/google/chv3-i2s.c339
-rw-r--r--sound/soc/hisilicon/Kconfig10
-rw-r--r--sound/soc/hisilicon/Makefile2
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c611
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.h265
-rw-r--r--sound/soc/img/Kconfig15
-rw-r--r--sound/soc/img/Makefile1
-rw-r--r--sound/soc/img/img-i2s-in.c171
-rw-r--r--sound/soc/img/img-i2s-out.c180
-rw-r--r--sound/soc/img/img-parallel-out.c68
-rw-r--r--sound/soc/img/img-spdif-in.c140
-rw-r--r--sound/soc/img/img-spdif-out.c136
-rw-r--r--sound/soc/img/pistachio-internal-dac.c57
-rw-r--r--sound/soc/intel/Kconfig285
-rw-r--r--sound/soc/intel/Makefile11
-rw-r--r--sound/soc/intel/atom/Makefile10
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c207
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h23
-rw-r--r--sound/soc/intel/atom/sst-mfld-dsp.h18
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c77
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c214
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h22
-rw-r--r--sound/soc/intel/atom/sst/Makefile13
-rw-r--r--sound/soc/intel/atom/sst/sst.c168
-rw-r--r--sound/soc/intel/atom/sst/sst.h115
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c217
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c115
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c45
-rw-r--r--sound/soc/intel/atom/sst/sst_loader.c52
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c74
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c75
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c196
-rw-r--r--sound/soc/intel/avs/Makefile20
-rw-r--r--sound/soc/intel/avs/apl.c270
-rw-r--r--sound/soc/intel/avs/avs.h367
-rw-r--r--sound/soc/intel/avs/board_selection.c672
-rw-r--r--sound/soc/intel/avs/boards/Kconfig199
-rw-r--r--sound/soc/intel/avs/boards/Makefile41
-rw-r--r--sound/soc/intel/avs/boards/da7219.c282
-rw-r--r--sound/soc/intel/avs/boards/dmic.c126
-rw-r--r--sound/soc/intel/avs/boards/es8336.c331
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c254
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c129
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c158
-rw-r--r--sound/soc/intel/avs/boards/max98373.c213
-rw-r--r--sound/soc/intel/avs/boards/max98927.c210
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c315
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c155
-rw-r--r--sound/soc/intel/avs/boards/probe.c83
-rw-r--r--sound/soc/intel/avs/boards/rt274.c280
-rw-r--r--sound/soc/intel/avs/boards/rt286.c250
-rw-r--r--sound/soc/intel/avs/boards/rt298.c269
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c197
-rw-r--r--sound/soc/intel/avs/boards/rt5640.c271
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c268
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c345
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c199
-rw-r--r--sound/soc/intel/avs/cldma.c290
-rw-r--r--sound/soc/intel/avs/cldma.h32
-rw-r--r--sound/soc/intel/avs/cnl.c92
-rw-r--r--sound/soc/intel/avs/control.c214
-rw-r--r--sound/soc/intel/avs/control.h27
-rw-r--r--sound/soc/intel/avs/core.c959
-rw-r--r--sound/soc/intel/avs/debug.h91
-rw-r--r--sound/soc/intel/avs/debugfs.c438
-rw-r--r--sound/soc/intel/avs/dsp.c329
-rw-r--r--sound/soc/intel/avs/icl.c203
-rw-r--r--sound/soc/intel/avs/ipc.c583
-rw-r--r--sound/soc/intel/avs/lnl.c28
-rw-r--r--sound/soc/intel/avs/loader.c750
-rw-r--r--sound/soc/intel/avs/messages.c904
-rw-r--r--sound/soc/intel/avs/messages.h1035
-rw-r--r--sound/soc/intel/avs/mtl.c201
-rw-r--r--sound/soc/intel/avs/path.c1608
-rw-r--r--sound/soc/intel/avs/path.h95
-rw-r--r--sound/soc/intel/avs/pcm.c1773
-rw-r--r--sound/soc/intel/avs/pcm.h16
-rw-r--r--sound/soc/intel/avs/probes.c314
-rw-r--r--sound/soc/intel/avs/ptl.c99
-rw-r--r--sound/soc/intel/avs/registers.h184
-rw-r--r--sound/soc/intel/avs/skl.c180
-rw-r--r--sound/soc/intel/avs/sysfs.c35
-rw-r--r--sound/soc/intel/avs/tgl.c87
-rw-r--r--sound/soc/intel/avs/topology.c2247
-rw-r--r--sound/soc/intel/avs/topology.h238
-rw-r--r--sound/soc/intel/avs/trace.c33
-rw-r--r--sound/soc/intel/avs/trace.h156
-rw-r--r--sound/soc/intel/avs/utils.c302
-rw-r--r--sound/soc/intel/avs/utils.h77
-rw-r--r--sound/soc/intel/baytrail/Makefile4
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-dsp.c366
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.c789
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.h73
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-pcm.c507
-rw-r--r--sound/soc/intel/boards/Kconfig542
-rw-r--r--sound/soc/intel/boards/Makefile88
-rw-r--r--sound/soc/intel/boards/bdw-rt5650.c335
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c460
-rw-r--r--sound/soc/intel/boards/bdw_rt286.c259
-rw-r--r--sound/soc/intel/boards/broadwell.c285
-rw-r--r--sound/soc/intel/boards/byt-max98090.c188
-rw-r--r--sound/soc/intel/boards/byt-rt5640.c230
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c296
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c307
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c730
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c193
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c1905
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c1069
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c670
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c487
-rw-r--r--sound/soc/intel/boards/cht_bsw_nau8824.c308
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c573
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c370
-rw-r--r--sound/soc/intel/boards/ehl_rt5660.c316
-rw-r--r--sound/soc/intel/boards/haswell.c209
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c100
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.h29
-rw-r--r--sound/soc/intel/boards/hsw_rt5640.c173
-rw-r--r--sound/soc/intel/boards/mfld_machine.c430
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c178
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c485
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c536
-rw-r--r--sound/soc/intel/boards/skl_rt286.c379
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.c785
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.h173
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c207
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.h26
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c307
-rw-r--r--sound/soc/intel/boards/sof_da7219.c490
-rw-r--r--sound/soc/intel/boards/sof_es8336.c858
-rw-r--r--sound/soc/intel/boards/sof_hdmi_common.h24
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c612
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.h48
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c345
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.c74
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.h22
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c444
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c689
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.h63
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c936
-rw-r--r--sound/soc/intel/boards/sof_sdw.c1500
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h66
-rw-r--r--sound/soc/intel/boards/sof_sdw_hdmi.c43
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c245
-rw-r--r--sound/soc/intel/boards/sof_wm8804.c302
-rw-r--r--sound/soc/intel/catpt/Makefile6
-rw-r--r--sound/soc/intel/catpt/core.h175
-rw-r--r--sound/soc/intel/catpt/device.c399
-rw-r--r--sound/soc/intel/catpt/dsp.c545
-rw-r--r--sound/soc/intel/catpt/ipc.c298
-rw-r--r--sound/soc/intel/catpt/loader.c671
-rw-r--r--sound/soc/intel/catpt/messages.c313
-rw-r--r--sound/soc/intel/catpt/messages.h399
-rw-r--r--sound/soc/intel/catpt/pcm.c1201
-rw-r--r--sound/soc/intel/catpt/registers.h178
-rw-r--r--sound/soc/intel/catpt/sysfs.c56
-rw-r--r--sound/soc/intel/catpt/trace.h83
-rw-r--r--sound/soc/intel/common/Makefile30
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c783
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c534
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-bxt-match.c95
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-byt-match.c220
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cfl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cht-match.c235
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cml-match.c314
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cnl-match.c84
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ehl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c68
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hda-match.c24
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c35
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-icl-match.c189
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c117
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-kbl-match.c119
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c801
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c1246
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-nvl-match.c90
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c773
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c568
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c42
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c208
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h18
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-skl-match.c39
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ssp-common.c168
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c834
-rw-r--r--sound/soc/intel/common/soc-intel-quirks.h98
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.c140
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.h15
-rw-r--r--sound/soc/intel/common/sst-acpi.c255
-rw-r--r--sound/soc/intel/common/sst-acpi.h33
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h391
-rw-r--r--sound/soc/intel/common/sst-dsp.c493
-rw-r--r--sound/soc/intel/common/sst-dsp.h293
-rw-r--r--sound/soc/intel/common/sst-firmware.c1213
-rw-r--r--sound/soc/intel/common/sst-ipc.c320
-rw-r--r--sound/soc/intel/common/sst-ipc.h94
-rw-r--r--sound/soc/intel/common/sst-match-acpi.c43
-rw-r--r--sound/soc/intel/haswell/Makefile4
-rw-r--r--sound/soc/intel/haswell/sst-haswell-dsp.c709
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c2232
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.h534
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c1419
-rw-r--r--sound/soc/intel/keembay/Makefile4
-rw-r--r--sound/soc/intel/keembay/kmb_platform.c928
-rw-r--r--sound/soc/intel/keembay/kmb_platform.h156
-rw-r--r--sound/soc/intel/skylake/Makefile10
-rw-r--r--sound/soc/intel/skylake/skl-messages.c944
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c147
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.h106
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c1058
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c368
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.h251
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c347
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h160
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c904
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h140
-rw-r--r--sound/soc/intel/skylake/skl-sst.c461
-rw-r--r--sound/soc/intel/skylake/skl-topology.c1522
-rw-r--r--sound/soc/intel/skylake/skl-topology.h349
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h230
-rw-r--r--sound/soc/intel/skylake/skl.c689
-rw-r--r--sound/soc/intel/skylake/skl.h103
-rw-r--r--sound/soc/jz4740/Kconfig27
-rw-r--r--sound/soc/jz4740/Makefile8
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c649
-rw-r--r--sound/soc/jz4740/jz4740-i2s.h16
-rw-r--r--sound/soc/jz4740/qi_lb60.c112
-rw-r--r--sound/soc/kirkwood/Kconfig4
-rw-r--r--sound/soc/kirkwood/Makefile5
-rw-r--r--sound/soc/kirkwood/armada-370-db.c62
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c142
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c183
-rw-r--r--sound/soc/kirkwood/kirkwood.h12
-rw-r--r--sound/soc/loongson/Kconfig49
-rw-r--r--sound/soc/loongson/Makefile15
-rw-r--r--sound/soc/loongson/loongson1_ac97.c398
-rw-r--r--sound/soc/loongson/loongson_card.c220
-rw-r--r--sound/soc/loongson/loongson_dma.c345
-rw-r--r--sound/soc/loongson/loongson_dma.h16
-rw-r--r--sound/soc/loongson/loongson_i2s.c276
-rw-r--r--sound/soc/loongson/loongson_i2s.h71
-rw-r--r--sound/soc/loongson/loongson_i2s_pci.c162
-rw-r--r--sound/soc/loongson/loongson_i2s_plat.c185
-rw-r--r--sound/soc/mediatek/Kconfig334
-rw-r--r--sound/soc/mediatek/Makefile18
-rw-r--r--sound/soc/mediatek/common/Makefile8
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c592
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h51
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c160
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h28
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h173
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c1410
-rw-r--r--sound/soc/mediatek/common/mtk-dai-adda-common.c70
-rw-r--r--sound/soc/mediatek/common/mtk-dai-adda-common.h45
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c275
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.h43
-rw-r--r--sound/soc/mediatek/common/mtk-soc-card.h23
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c349
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.h56
-rw-r--r--sound/soc/mediatek/mt2701/Makefile8
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c298
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h34
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-common.h98
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c1483
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c420
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-reg.h141
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c182
-rw-r--r--sound/soc/mediatek/mt6797/Makefile14
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.c123
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.h17
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-common.h59
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c900
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-adda.c323
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-pcm.c319
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-interconnection.h33
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-mt6351.c255
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-reg.h1015
-rw-r--r--sound/soc/mediatek/mt7986/Makefile9
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-common.h49
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-pcm.c609
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-dai-etdm.c426
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-reg.h196
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-wm8960.c175
-rw-r--r--sound/soc/mediatek/mt8173/Makefile8
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-common.h65
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c1241
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c (renamed from sound/soc/mediatek/mt8173-max98090.c)99
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c257
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c (renamed from sound/soc/mediatek/mt8173-rt5650-rt5676.c)205
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c362
-rw-r--r--sound/soc/mediatek/mt8183/Makefile15
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.c614
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.h38
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-common.h111
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c979
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c887
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-adda.c426
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-i2s.c1058
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-pcm.c319
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-tdm.c748
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-interconnection.h33
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c899
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-reg.h1668
-rw-r--r--sound/soc/mediatek/mt8186/Makefile21
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.c589
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.h103
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-common.h198
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-control.c254
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.c242
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c3005
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.c152
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h45
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-adda.c776
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-hostless.c298
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-hw-gain.c236
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-i2s.c1231
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-pcm.c419
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-src.c695
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-tdm.c643
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-interconnection.h69
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-misc-control.c252
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.c57
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.h17
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366.c1377
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-reg.h2913
-rw-r--r--sound/soc/mediatek/mt8188/Makefile16
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c755
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.h137
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-common.h153
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c3399
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.c210
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h87
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c512
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-dmic.c683
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c2712
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-pcm.c368
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c1478
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-reg.h3195
-rw-r--r--sound/soc/mediatek/mt8189/Makefile18
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.c750
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.h76
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-common.h240
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-pcm.c2615
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-adda.c1228
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-i2s.c1463
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-pcm.c332
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-tdm.c672
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-interconnection.h97
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-nau8825.c1178
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-reg.h10773
-rw-r--r--sound/soc/mediatek/mt8192/Makefile16
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.c665
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.h244
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-common.h173
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-control.c161
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.c307
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c2341
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c1365
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-i2s.c2101
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-pcm.c411
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-tdm.c778
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-interconnection.h65
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c1239
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-reg.h3133
-rw-r--r--sound/soc/mediatek/mt8195/Makefile15
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.c716
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.h119
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-common.h158
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c3206
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.c215
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h93
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-adda.c753
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c2778
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c369
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c1581
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-reg.h2797
-rw-r--r--sound/soc/mediatek/mt8365/Makefile15
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-clk.c420
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-clk.h32
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-common.h448
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-pcm.c2271
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-adda.c311
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-dmic.c310
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-i2s.c845
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-pcm.c293
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-mt6357.c346
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-reg.h993
-rw-r--r--sound/soc/mediatek/mtk-afe-common.h100
-rw-r--r--sound/soc/mediatek/mtk-afe-pcm.c1304
-rw-r--r--sound/soc/meson/Kconfig138
-rw-r--r--sound/soc/meson/Makefile46
-rw-r--r--sound/soc/meson/aiu-acodec-ctrl.c203
-rw-r--r--sound/soc/meson/aiu-codec-ctrl.c151
-rw-r--r--sound/soc/meson/aiu-encoder-i2s.c334
-rw-r--r--sound/soc/meson/aiu-encoder-spdif.c209
-rw-r--r--sound/soc/meson/aiu-fifo-i2s.c173
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c188
-rw-r--r--sound/soc/meson/aiu-fifo.c215
-rw-r--r--sound/soc/meson/aiu-fifo.h48
-rw-r--r--sound/soc/meson/aiu.c358
-rw-r--r--sound/soc/meson/aiu.h88
-rw-r--r--sound/soc/meson/axg-card.c373
-rw-r--r--sound/soc/meson/axg-fifo.c398
-rw-r--r--sound/soc/meson/axg-fifo.h95
-rw-r--r--sound/soc/meson/axg-frddr.c400
-rw-r--r--sound/soc/meson/axg-pdm.c641
-rw-r--r--sound/soc/meson/axg-spdifin.c496
-rw-r--r--sound/soc/meson/axg-spdifout.c447
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c437
-rw-r--r--sound/soc/meson/axg-tdm-formatter.h45
-rw-r--r--sound/soc/meson/axg-tdm-interface.c584
-rw-r--r--sound/soc/meson/axg-tdm.h81
-rw-r--r--sound/soc/meson/axg-tdmin.c260
-rw-r--r--sound/soc/meson/axg-tdmout.c339
-rw-r--r--sound/soc/meson/axg-toddr.c353
-rw-r--r--sound/soc/meson/g12a-toacodec.c356
-rw-r--r--sound/soc/meson/g12a-tohdmitx.c283
-rw-r--r--sound/soc/meson/gx-card.c142
-rw-r--r--sound/soc/meson/meson-card-utils.c334
-rw-r--r--sound/soc/meson/meson-card.h54
-rw-r--r--sound/soc/meson/meson-codec-glue.c147
-rw-r--r--sound/soc/meson/meson-codec-glue.h32
-rw-r--r--sound/soc/meson/t9015.c314
-rw-r--r--sound/soc/mxs/Kconfig3
-rw-r--r--sound/soc/mxs/Makefile7
-rw-r--r--sound/soc/mxs/mxs-pcm.c16
-rw-r--r--sound/soc/mxs/mxs-pcm.h15
-rw-r--r--sound/soc/mxs/mxs-saif.c254
-rw-r--r--sound/soc/mxs/mxs-saif.h15
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c88
-rw-r--r--sound/soc/nuc900/Kconfig28
-rw-r--r--sound/soc/nuc900/Makefile11
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c398
-rw-r--r--sound/soc/nuc900/nuc900-audio.c75
-rw-r--r--sound/soc/nuc900/nuc900-audio.h112
-rw-r--r--sound/soc/nuc900/nuc900-pcm.c326
-rw-r--r--sound/soc/omap/Kconfig124
-rw-r--r--sound/soc/omap/Makefile31
-rw-r--r--sound/soc/omap/am3517evm.c141
-rw-r--r--sound/soc/omap/mcbsp.c1090
-rw-r--r--sound/soc/omap/omap-mcbsp.c857
-rw-r--r--sound/soc/omap/omap-mcbsp.h44
-rw-r--r--sound/soc/omap/omap-pcm.c263
-rw-r--r--sound/soc/pxa/Kconfig201
-rw-r--r--sound/soc/pxa/Makefile50
-rw-r--r--sound/soc/pxa/brownstone.c137
-rw-r--r--sound/soc/pxa/corgi.c319
-rw-r--r--sound/soc/pxa/e740_wm9705.c171
-rw-r--r--sound/soc/pxa/e750_wm9705.c153
-rw-r--r--sound/soc/pxa/e800_wm9712.c152
-rw-r--r--sound/soc/pxa/em-x270.c96
-rw-r--r--sound/soc/pxa/hx4700.c227
-rw-r--r--sound/soc/pxa/imote2.c95
-rw-r--r--sound/soc/pxa/magician.c534
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c209
-rw-r--r--sound/soc/pxa/mmp-pcm.c250
-rw-r--r--sound/soc/pxa/mmp-sspa.c485
-rw-r--r--sound/soc/pxa/mmp-sspa.h48
-rw-r--r--sound/soc/pxa/palm27x.c163
-rw-r--r--sound/soc/pxa/poodle.c291
-rw-r--r--sound/soc/pxa/pxa-ssp.c358
-rw-r--r--sound/soc/pxa/pxa-ssp.h11
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c149
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h17
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c165
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h8
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c94
-rw-r--r--sound/soc/pxa/raumfeld.c320
-rw-r--r--sound/soc/pxa/spitz.c124
-rw-r--r--sound/soc/pxa/tosa.c266
-rw-r--r--sound/soc/pxa/ttc-dkb.c153
-rw-r--r--sound/soc/pxa/z2.c220
-rw-r--r--sound/soc/pxa/zylonite.c275
-rw-r--r--sound/soc/qcom/Kconfig235
-rw-r--r--sound/soc/qcom/Makefile44
-rw-r--r--sound/soc/qcom/apq8016_sbc.c337
-rw-r--r--sound/soc/qcom/apq8096.c145
-rw-r--r--sound/soc/qcom/common.c278
-rw-r--r--sound/soc/qcom/common.h16
-rw-r--r--sound/soc/qcom/lpass-apq8016.c161
-rw-r--r--sound/soc/qcom/lpass-cdc-dma.c303
-rw-r--r--sound/soc/qcom/lpass-cpu.c1208
-rw-r--r--sound/soc/qcom/lpass-hdmi.c254
-rw-r--r--sound/soc/qcom/lpass-hdmi.h102
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c105
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h349
-rw-r--r--sound/soc/qcom/lpass-platform.c1344
-rw-r--r--sound/soc/qcom/lpass-sc7180.c325
-rw-r--r--sound/soc/qcom/lpass-sc7280.c455
-rw-r--r--sound/soc/qcom/lpass.h362
-rw-r--r--sound/soc/qcom/qdsp6/Makefile20
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c1339
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h821
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c607
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.h27
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-clocks.c120
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c1147
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c1941
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.h274
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c884
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c324
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c828
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h156
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c1343
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c1676
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h152
-rw-r--r--sound/soc/qcom/qdsp6/q6core.c375
-rw-r--r--sound/soc/qcom/qdsp6/q6core.h15
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.c103
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.h25
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-errno.h51
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c185
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h30
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c658
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h23
-rw-r--r--sound/soc/qcom/qdsp6/q6prm-clocks.c94
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c240
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.h97
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.c1174
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.h9
-rw-r--r--sound/soc/qcom/qdsp6/q6usb.c422
-rw-r--r--sound/soc/qcom/qdsp6/topology.c1340
-rw-r--r--sound/soc/qcom/sc7180.c582
-rw-r--r--sound/soc/qcom/sc7280.c400
-rw-r--r--sound/soc/qcom/sc8280xp.c194
-rw-r--r--sound/soc/qcom/sdm845.c591
-rw-r--r--sound/soc/qcom/sdw.c216
-rw-r--r--sound/soc/qcom/sdw.h16
-rw-r--r--sound/soc/qcom/sm8250.c205
-rw-r--r--sound/soc/qcom/storm.c59
-rw-r--r--sound/soc/qcom/usb_offload_utils.c56
-rw-r--r--sound/soc/qcom/usb_offload_utils.h30
-rw-r--r--sound/soc/qcom/x1e80100.c210
-rw-r--r--sound/soc/renesas/Kconfig (renamed from sound/soc/sh/Kconfig)30
-rw-r--r--sound/soc/renesas/Makefile (renamed from sound/soc/sh/Makefile)19
-rw-r--r--sound/soc/renesas/dma-sh7760.c (renamed from sound/soc/sh/dma-sh7760.c)123
-rw-r--r--sound/soc/renesas/fsi.c (renamed from sound/soc/sh/fsi.c)215
-rw-r--r--sound/soc/renesas/hac.c (renamed from sound/soc/sh/hac.c)32
-rw-r--r--sound/soc/renesas/migor.c (renamed from sound/soc/sh/migor.c)38
-rw-r--r--sound/soc/renesas/rcar/Makefile6
-rw-r--r--sound/soc/renesas/rcar/adg.c819
-rw-r--r--sound/soc/renesas/rcar/cmd.c (renamed from sound/soc/sh/rcar/cmd.c)124
-rw-r--r--sound/soc/renesas/rcar/core.c2080
-rw-r--r--sound/soc/renesas/rcar/ctu.c385
-rw-r--r--sound/soc/renesas/rcar/debugfs.c96
-rw-r--r--sound/soc/renesas/rcar/dma.c (renamed from sound/soc/sh/rcar/dma.c)642
-rw-r--r--sound/soc/renesas/rcar/dvc.c (renamed from sound/soc/sh/rcar/dvc.c)169
-rw-r--r--sound/soc/renesas/rcar/gen.c495
-rw-r--r--sound/soc/renesas/rcar/mix.c352
-rw-r--r--sound/soc/renesas/rcar/msiof.c629
-rw-r--r--sound/soc/renesas/rcar/rsnd.h895
-rw-r--r--sound/soc/renesas/rcar/src.c793
-rw-r--r--sound/soc/renesas/rcar/ssi.c1252
-rw-r--r--sound/soc/renesas/rcar/ssiu.c605
-rw-r--r--sound/soc/renesas/rz-ssi.c1287
-rw-r--r--sound/soc/renesas/sh7760-ac97.c (renamed from sound/soc/sh/sh7760-ac97.c)25
-rw-r--r--sound/soc/renesas/siu.h (renamed from sound/soc/sh/siu.h)30
-rw-r--r--sound/soc/renesas/siu_dai.c (renamed from sound/soc/sh/siu_dai.c)47
-rw-r--r--sound/soc/renesas/siu_pcm.c (renamed from sound/soc/sh/siu_pcm.c)163
-rw-r--r--sound/soc/renesas/ssi.c (renamed from sound/soc/sh/ssi.c)36
-rw-r--r--sound/soc/rockchip/Kconfig76
-rw-r--r--sound/soc/rockchip/Makefile19
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c263
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c627
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c598
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h29
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c1435
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.h400
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c421
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c716
-rw-r--r--sound/soc/rockchip/rockchip_pdm.h92
-rw-r--r--sound/soc/rockchip/rockchip_rt5645.c98
-rw-r--r--sound/soc/rockchip/rockchip_sai.c1529
-rw-r--r--sound/soc/rockchip/rockchip_sai.h251
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c153
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h5
-rw-r--r--sound/soc/samsung/Kconfig208
-rw-r--r--sound/soc/samsung/Makefile79
-rw-r--r--sound/soc/samsung/ac97.c436
-rw-r--r--sound/soc/samsung/aries_wm8994.c702
-rw-r--r--sound/soc/samsung/arndale.c217
-rw-r--r--sound/soc/samsung/arndale_rt5631.c140
-rw-r--r--sound/soc/samsung/bells.c223
-rw-r--r--sound/soc/samsung/dma.h38
-rw-r--r--sound/soc/samsung/dmaengine.c74
-rw-r--r--sound/soc/samsung/h1940_uda1380.c261
-rw-r--r--sound/soc/samsung/i2s-regs.h22
-rw-r--r--sound/soc/samsung/i2s.c1051
-rw-r--r--sound/soc/samsung/i2s.h22
-rw-r--r--sound/soc/samsung/idma.c90
-rw-r--r--sound/soc/samsung/idma.h9
-rw-r--r--sound/soc/samsung/jive_wm8750.c146
-rw-r--r--sound/soc/samsung/littlemill.c120
-rw-r--r--sound/soc/samsung/ln2440sbc_alc650.c72
-rw-r--r--sound/soc/samsung/lowland.c92
-rw-r--r--sound/soc/samsung/midas_wm1811.c775
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c395
-rw-r--r--sound/soc/samsung/odroid.c349
-rw-r--r--sound/soc/samsung/odroidx2_max98090.c185
-rw-r--r--sound/soc/samsung/pcm.c166
-rw-r--r--sound/soc/samsung/pcm.h8
-rw-r--r--sound/soc/samsung/regs-ac97.h66
-rw-r--r--sound/soc/samsung/regs-i2s-v2.h115
-rw-r--r--sound/soc/samsung/regs-iis.h69
-rw-r--r--sound/soc/samsung/rx1950_uda1380.c283
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c735
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.h107
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c200
-rw-r--r--sound/soc/samsung/s3c2412-i2s.h27
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c505
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.h35
-rw-r--r--sound/soc/samsung/s3c24xx_simtec.c372
-rw-r--r--sound/soc/samsung/s3c24xx_simtec.h22
-rw-r--r--sound/soc/samsung/s3c24xx_simtec_hermes.c114
-rw-r--r--sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c102
-rw-r--r--sound/soc/samsung/s3c24xx_uda134x.c342
-rw-r--r--sound/soc/samsung/smartq_wm8987.c238
-rw-r--r--sound/soc/samsung/smdk2443_wm9710.c68
-rw-r--r--sound/soc/samsung/smdk_spdif.c32
-rw-r--r--sound/soc/samsung/smdk_wm8580.c241
-rw-r--r--sound/soc/samsung/smdk_wm8580pcm.c176
-rw-r--r--sound/soc/samsung/smdk_wm8994.c108
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c39
-rw-r--r--sound/soc/samsung/smdk_wm9713.c108
-rw-r--r--sound/soc/samsung/snow.c265
-rw-r--r--sound/soc/samsung/spdif.c109
-rw-r--r--sound/soc/samsung/spdif.h8
-rw-r--r--sound/soc/samsung/speyside.c173
-rw-r--r--sound/soc/samsung/tm2_wm5110.c677
-rw-r--r--sound/soc/samsung/tobermory.c71
-rw-r--r--sound/soc/sdca/Kconfig59
-rw-r--r--sound/soc/sdca/Makefile15
-rw-r--r--sound/soc/sdca/sdca_asoc.c1559
-rw-r--r--sound/soc/sdca/sdca_class.c304
-rw-r--r--sound/soc/sdca/sdca_class.h37
-rw-r--r--sound/soc/sdca/sdca_class_function.c460
-rw-r--r--sound/soc/sdca/sdca_device.c108
-rw-r--r--sound/soc/sdca/sdca_fdl.c504
-rw-r--r--sound/soc/sdca/sdca_function_device.c117
-rw-r--r--sound/soc/sdca/sdca_function_device.h15
-rw-r--r--sound/soc/sdca/sdca_functions.c2282
-rw-r--r--sound/soc/sdca/sdca_hid.c168
-rw-r--r--sound/soc/sdca/sdca_interrupts.c612
-rw-r--r--sound/soc/sdca/sdca_regmap.c373
-rw-r--r--sound/soc/sdca/sdca_ump.c262
-rw-r--r--sound/soc/sdw_utils/Kconfig6
-rw-r--r--sound/soc/sdw_utils/Makefile12
-rw-r--r--sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c156
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l42.c92
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l43.c174
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l45.c80
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs_amp.c125
-rw-r--r--sound/soc/sdw_utils/soc_sdw_dmic.c46
-rw-r--r--sound/soc/sdw_utils/soc_sdw_maxim.c139
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt5682.c92
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt700.c89
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt711.c161
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp.c307
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp_coeff_tables.h300
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_dmic.c45
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c84
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c223
-rw-r--r--sound/soc/sdw_utils/soc_sdw_ti_amp.c93
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c1612
-rw-r--r--sound/soc/sh/rcar/Makefile5
-rw-r--r--sound/soc/sh/rcar/adg.c546
-rw-r--r--sound/soc/sh/rcar/core.c1185
-rw-r--r--sound/soc/sh/rcar/ctu.c154
-rw-r--r--sound/soc/sh/rcar/gen.c397
-rw-r--r--sound/soc/sh/rcar/mix.c196
-rw-r--r--sound/soc/sh/rcar/rsnd.h644
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c520
-rw-r--r--sound/soc/sh/rcar/src.c648
-rw-r--r--sound/soc/sh/rcar/ssi.c946
-rw-r--r--sound/soc/sh/rcar/ssiu.c225
-rw-r--r--sound/soc/sirf/Kconfig20
-rw-r--r--sound/soc/sirf/Makefile7
-rw-r--r--sound/soc/sirf/sirf-audio-port.c86
-rw-r--r--sound/soc/sirf/sirf-audio.c155
-rw-r--r--sound/soc/sirf/sirf-usp.c436
-rw-r--r--sound/soc/sirf/sirf-usp.h293
-rw-r--r--sound/soc/soc-ac97.c233
-rw-r--r--sound/soc/soc-acpi.c204
-rw-r--r--sound/soc/soc-cache.c53
-rw-r--r--sound/soc/soc-card-test.c129
-rw-r--r--sound/soc/soc-card.c248
-rw-r--r--sound/soc/soc-component.c1300
-rw-r--r--sound/soc/soc-compress.c751
-rw-r--r--sound/soc/soc-core.c4933
-rw-r--r--sound/soc/soc-dai.c816
-rw-r--r--sound/soc/soc-dapm.c3090
-rw-r--r--sound/soc/soc-devres.c65
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c371
-rw-r--r--sound/soc/soc-io.c273
-rw-r--r--sound/soc/soc-jack.c173
-rw-r--r--sound/soc/soc-link.c213
-rw-r--r--sound/soc/soc-ops-test.c548
-rw-r--r--sound/soc/soc-ops.c809
-rw-r--r--sound/soc/soc-pcm.c3656
-rw-r--r--sound/soc/soc-topology-test.c821
-rw-r--r--sound/soc/soc-topology.c2373
-rw-r--r--sound/soc/soc-usb.c322
-rw-r--r--sound/soc/soc-utils-test.c232
-rw-r--r--sound/soc/soc-utils.c251
-rw-r--r--sound/soc/sof/Kconfig306
-rw-r--r--sound/soc/sof/Makefile61
-rw-r--r--sound/soc/sof/amd/Kconfig107
-rw-r--r--sound/soc/sof/amd/Makefile20
-rw-r--r--sound/soc/sof/amd/acp-common.c267
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h143
-rw-r--r--sound/soc/sof/amd/acp-ipc.c310
-rw-r--r--sound/soc/sof/amd/acp-loader.c320
-rw-r--r--sound/soc/sof/amd/acp-pcm.c120
-rw-r--r--sound/soc/sof/amd/acp-probes.c147
-rw-r--r--sound/soc/sof/amd/acp-stream.c187
-rw-r--r--sound/soc/sof/amd/acp-trace.c64
-rw-r--r--sound/soc/sof/amd/acp.c968
-rw-r--r--sound/soc/sof/amd/acp.h373
-rw-r--r--sound/soc/sof/amd/acp63.c142
-rw-r--r--sound/soc/sof/amd/acp70.c142
-rw-r--r--sound/soc/sof/amd/pci-acp63.c115
-rw-r--r--sound/soc/sof/amd/pci-acp70.c121
-rw-r--r--sound/soc/sof/amd/pci-rmb.c104
-rw-r--r--sound/soc/sof/amd/pci-rn.c108
-rw-r--r--sound/soc/sof/amd/pci-vangogh.c101
-rw-r--r--sound/soc/sof/amd/rembrandt.c142
-rw-r--r--sound/soc/sof/amd/renoir.c117
-rw-r--r--sound/soc/sof/amd/vangogh.c177
-rw-r--r--sound/soc/sof/compress.c391
-rw-r--r--sound/soc/sof/control.c220
-rw-r--r--sound/soc/sof/core.c843
-rw-r--r--sound/soc/sof/debug.c469
-rw-r--r--sound/soc/sof/fw-file-profile.c345
-rw-r--r--sound/soc/sof/imx/Kconfig45
-rw-r--r--sound/soc/sof/imx/Makefile9
-rw-r--r--sound/soc/sof/imx/imx-common.c484
-rw-r--r--sound/soc/sof/imx/imx-common.h169
-rw-r--r--sound/soc/sof/imx/imx8.c473
-rw-r--r--sound/soc/sof/imx/imx9.c117
-rw-r--r--sound/soc/sof/intel/Kconfig415
-rw-r--r--sound/soc/sof/intel/Makefile53
-rw-r--r--sound/soc/sof/intel/apl.c122
-rw-r--r--sound/soc/sof/intel/atom.c421
-rw-r--r--sound/soc/sof/intel/atom.h74
-rw-r--r--sound/soc/sof/intel/bdw.c698
-rw-r--r--sound/soc/sof/intel/byt.c480
-rw-r--r--sound/soc/sof/intel/cnl.c521
-rw-r--r--sound/soc/sof/intel/ext_manifest.h35
-rw-r--r--sound/soc/sof/intel/hda-bus.c112
-rw-r--r--sound/soc/sof/intel/hda-codec.c458
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c108
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c338
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c660
-rw-r--r--sound/soc/sof/intel/hda-dai.c942
-rw-r--r--sound/soc/sof/intel/hda-dsp.c1635
-rw-r--r--sound/soc/sof/intel/hda-ipc.c556
-rw-r--r--sound/soc/sof/intel/hda-ipc.h56
-rw-r--r--sound/soc/sof/intel/hda-loader-skl.c578
-rw-r--r--sound/soc/sof/intel/hda-loader.c728
-rw-r--r--sound/soc/sof/intel/hda-mlink.c1120
-rw-r--r--sound/soc/sof/intel/hda-pcm.c353
-rw-r--r--sound/soc/sof/intel/hda-probes.c150
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c445
-rw-r--r--sound/soc/sof/intel/hda-stream.c1213
-rw-r--r--sound/soc/sof/intel/hda-trace.c98
-rw-r--r--sound/soc/sof/intel/hda.c1757
-rw-r--r--sound/soc/sof/intel/hda.h1056
-rw-r--r--sound/soc/sof/intel/icl.c197
-rw-r--r--sound/soc/sof/intel/lnl.c190
-rw-r--r--sound/soc/sof/intel/lnl.h21
-rw-r--r--sound/soc/sof/intel/mtl.c819
-rw-r--r--sound/soc/sof/intel/mtl.h134
-rw-r--r--sound/soc/sof/intel/nvl.c55
-rw-r--r--sound/soc/sof/intel/nvl.h14
-rw-r--r--sound/soc/sof/intel/pci-apl.c111
-rw-r--r--sound/soc/sof/intel/pci-cnl.c149
-rw-r--r--sound/soc/sof/intel/pci-icl.c115
-rw-r--r--sound/soc/sof/intel/pci-lnl.c84
-rw-r--r--sound/soc/sof/intel/pci-mtl.c147
-rw-r--r--sound/soc/sof/intel/pci-nvl.c82
-rw-r--r--sound/soc/sof/intel/pci-ptl.c113
-rw-r--r--sound/soc/sof/intel/pci-skl.c95
-rw-r--r--sound/soc/sof/intel/pci-tgl.c324
-rw-r--r--sound/soc/sof/intel/pci-tng.c250
-rw-r--r--sound/soc/sof/intel/ptl.c158
-rw-r--r--sound/soc/sof/intel/ptl.h19
-rw-r--r--sound/soc/sof/intel/shim.h220
-rw-r--r--sound/soc/sof/intel/skl.c118
-rw-r--r--sound/soc/sof/intel/telemetry.c96
-rw-r--r--sound/soc/sof/intel/telemetry.h35
-rw-r--r--sound/soc/sof/intel/tgl.c256
-rw-r--r--sound/soc/sof/intel/tracepoints.c5
-rw-r--r--sound/soc/sof/iomem-utils.c127
-rw-r--r--sound/soc/sof/ipc.c235
-rw-r--r--sound/soc/sof/ipc3-control.c731
-rw-r--r--sound/soc/sof/ipc3-dtrace.c664
-rw-r--r--sound/soc/sof/ipc3-loader.c419
-rw-r--r--sound/soc/sof/ipc3-pcm.c439
-rw-r--r--sound/soc/sof/ipc3-priv.h67
-rw-r--r--sound/soc/sof/ipc3-topology.c2713
-rw-r--r--sound/soc/sof/ipc3.c1157
-rw-r--r--sound/soc/sof/ipc4-control.c867
-rw-r--r--sound/soc/sof/ipc4-fw-reg.h155
-rw-r--r--sound/soc/sof/ipc4-loader.c633
-rw-r--r--sound/soc/sof/ipc4-mtrace.c672
-rw-r--r--sound/soc/sof/ipc4-pcm.c1324
-rw-r--r--sound/soc/sof/ipc4-priv.h132
-rw-r--r--sound/soc/sof/ipc4-telemetry.c95
-rw-r--r--sound/soc/sof/ipc4-telemetry.h73
-rw-r--r--sound/soc/sof/ipc4-topology.c3809
-rw-r--r--sound/soc/sof/ipc4-topology.h528
-rw-r--r--sound/soc/sof/ipc4.c906
-rw-r--r--sound/soc/sof/loader.c190
-rw-r--r--sound/soc/sof/mediatek/Kconfig45
-rw-r--r--sound/soc/sof/mediatek/Makefile4
-rw-r--r--sound/soc/sof/mediatek/adsp_helper.h50
-rw-r--r--sound/soc/sof/mediatek/mt8186/Makefile4
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.c100
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.h24
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-loader.c58
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c570
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h93
-rw-r--r--sound/soc/sof/mediatek/mt8195/Makefile3
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.c165
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.h28
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-loader.c61
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c526
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h161
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c215
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.h20
-rw-r--r--sound/soc/sof/nocodec.c115
-rw-r--r--sound/soc/sof/ops.c187
-rw-r--r--sound/soc/sof/ops.h663
-rw-r--r--sound/soc/sof/pcm.c811
-rw-r--r--sound/soc/sof/pm.c389
-rw-r--r--sound/soc/sof/sof-acpi-dev.c102
-rw-r--r--sound/soc/sof/sof-acpi-dev.h16
-rw-r--r--sound/soc/sof/sof-audio.c979
-rw-r--r--sound/soc/sof/sof-audio.h685
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c383
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c161
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c339
-rw-r--r--sound/soc/sof/sof-client-probes-ipc3.c243
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c409
-rw-r--r--sound/soc/sof/sof-client-probes.c587
-rw-r--r--sound/soc/sof/sof-client-probes.h72
-rw-r--r--sound/soc/sof/sof-client.c669
-rw-r--r--sound/soc/sof/sof-client.h76
-rw-r--r--sound/soc/sof/sof-of-dev.c104
-rw-r--r--sound/soc/sof/sof-of-dev.h25
-rw-r--r--sound/soc/sof/sof-pci-dev.c296
-rw-r--r--sound/soc/sof/sof-pci-dev.h17
-rw-r--r--sound/soc/sof/sof-priv.h904
-rw-r--r--sound/soc/sof/sof-utils.c76
-rw-r--r--sound/soc/sof/sof-utils.h19
-rw-r--r--sound/soc/sof/stream-ipc.c131
-rw-r--r--sound/soc/sof/topology.c2581
-rw-r--r--sound/soc/sof/trace.c53
-rw-r--r--sound/soc/sof/xtensa/Kconfig3
-rw-r--r--sound/soc/sof/xtensa/Makefile5
-rw-r--r--sound/soc/sof/xtensa/core.c155
-rw-r--r--sound/soc/spacemit/Kconfig15
-rw-r--r--sound/soc/spacemit/Makefile5
-rw-r--r--sound/soc/spacemit/k1_i2s.c461
-rw-r--r--sound/soc/spear/Kconfig5
-rw-r--r--sound/soc/spear/Makefile7
-rw-r--r--sound/soc/spear/spdif_in.c20
-rw-r--r--sound/soc/spear/spdif_in_regs.h15
-rw-r--r--sound/soc/spear/spdif_out.c21
-rw-r--r--sound/soc/spear/spdif_out_regs.h15
-rw-r--r--sound/soc/spear/spear_pcm.h13
-rw-r--r--sound/soc/sprd/Kconfig20
-rw-r--r--sound/soc/sprd/Makefile8
-rw-r--r--sound/soc/sprd/sprd-mcdt.c1006
-rw-r--r--sound/soc/sprd/sprd-mcdt.h107
-rw-r--r--sound/soc/sprd/sprd-pcm-compress.c671
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.c497
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.h58
-rw-r--r--sound/soc/starfive/Kconfig20
-rw-r--r--sound/soc/starfive/Makefile3
-rw-r--r--sound/soc/starfive/jh7110_pwmdac.c528
-rw-r--r--sound/soc/starfive/jh7110_tdm.c662
-rw-r--r--sound/soc/sti/Kconfig3
-rw-r--r--sound/soc/sti/Makefile3
-rw-r--r--sound/soc/sti/sti_uniperif.c356
-rw-r--r--sound/soc/sti/uniperif.h234
-rw-r--r--sound/soc/sti/uniperif_player.c379
-rw-r--r--sound/soc/sti/uniperif_reader.c310
-rw-r--r--sound/soc/stm/Kconfig47
-rw-r--r--sound/soc/stm/Makefile18
-rw-r--r--sound/soc/stm/stm32_adfsdm.c409
-rw-r--r--sound/soc/stm/stm32_i2s.c1394
-rw-r--r--sound/soc/stm/stm32_sai.c316
-rw-r--r--sound/soc/stm/stm32_sai.h308
-rw-r--r--sound/soc/stm/stm32_sai_sub.c1799
-rw-r--r--sound/soc/stm/stm32_spdifrx.c1081
-rw-r--r--sound/soc/sunxi/Kconfig61
-rw-r--r--sound/soc/sunxi/Makefile9
-rw-r--r--sound/soc/sunxi/sun4i-codec.c1935
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c1699
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c772
-rw-r--r--sound/soc/sunxi/sun50i-codec-analog.c596
-rw-r--r--sound/soc/sunxi/sun50i-dmic.c437
-rw-r--r--sound/soc/sunxi/sun8i-adda-pr-regmap.c102
-rw-r--r--sound/soc/sunxi/sun8i-adda-pr-regmap.h7
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c853
-rw-r--r--sound/soc/sunxi/sun8i-codec.c1720
-rw-r--r--sound/soc/tegra/Kconfig229
-rw-r--r--sound/soc/tegra/Makefile62
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1052
-rw-r--r--sound/soc/tegra/tegra186_asrc.h116
-rw-r--r--sound/soc/tegra/tegra186_dspk.c554
-rw-r--r--sound/soc/tegra/tegra186_dspk.h70
-rw-r--r--sound/soc/tegra/tegra20_ac97.c115
-rw-r--r--sound/soc/tegra/tegra20_ac97.h16
-rw-r--r--sound/soc/tegra/tegra20_das.c221
-rw-r--r--sound/soc/tegra/tegra20_das.h134
-rw-r--r--sound/soc/tegra/tegra20_i2s.c154
-rw-r--r--sound/soc/tegra/tegra20_i2s.h17
-rw-r--r--sound/soc/tegra/tegra20_spdif.c241
-rw-r--r--sound/soc/tegra/tegra20_spdif.h17
-rw-r--r--sound/soc/tegra/tegra210_admaif.c1045
-rw-r--r--sound/soc/tegra/tegra210_admaif.h241
-rw-r--r--sound/soc/tegra/tegra210_adx.c752
-rw-r--r--sound/soc/tegra/tegra210_adx.h96
-rw-r--r--sound/soc/tegra/tegra210_ahub.c2272
-rw-r--r--sound/soc/tegra/tegra210_ahub.h171
-rw-r--r--sound/soc/tegra/tegra210_amx.c801
-rw-r--r--sound/soc/tegra/tegra210_amx.h113
-rw-r--r--sound/soc/tegra/tegra210_dmic.c570
-rw-r--r--sound/soc/tegra/tegra210_dmic.h82
-rw-r--r--sound/soc/tegra/tegra210_i2s.c1172
-rw-r--r--sound/soc/tegra/tegra210_i2s.h178
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c1014
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.h215
-rw-r--r--sound/soc/tegra/tegra210_mixer.c687
-rw-r--r--sound/soc/tegra/tegra210_mixer.h100
-rw-r--r--sound/soc/tegra/tegra210_mvc.c779
-rw-r--r--sound/soc/tegra/tegra210_mvc.h122
-rw-r--r--sound/soc/tegra/tegra210_ope.c420
-rw-r--r--sound/soc/tegra/tegra210_ope.h90
-rw-r--r--sound/soc/tegra/tegra210_peq.c433
-rw-r--r--sound/soc/tegra/tegra210_peq.h56
-rw-r--r--sound/soc/tegra/tegra210_sfc.c3644
-rw-r--r--sound/soc/tegra/tegra210_sfc.h78
-rw-r--r--sound/soc/tegra/tegra30_ahub.c253
-rw-r--r--sound/soc/tegra/tegra30_ahub.h21
-rw-r--r--sound/soc/tegra/tegra30_i2s.c149
-rw-r--r--sound/soc/tegra/tegra30_i2s.h15
-rw-r--r--sound/soc/tegra/tegra_alc5632.c265
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c1026
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.h51
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c240
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h53
-rw-r--r--sound/soc/tegra/tegra_audio_graph_card.c267
-rw-r--r--sound/soc/tegra/tegra_cif.h87
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.c130
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.h31
-rw-r--r--sound/soc/tegra/tegra_max98090.c325
-rw-r--r--sound/soc/tegra/tegra_pcm.c171
-rw-r--r--sound/soc/tegra/tegra_pcm.h31
-rw-r--r--sound/soc/tegra/tegra_rt5640.c266
-rw-r--r--sound/soc/tegra/tegra_rt5677.c341
-rw-r--r--sound/soc/tegra/tegra_wm8753.c219
-rw-r--r--sound/soc/tegra/tegra_wm8903.c394
-rw-r--r--sound/soc/tegra/tegra_wm9712.c178
-rw-r--r--sound/soc/tegra/trimslice.c206
-rw-r--r--sound/soc/ti/Kconfig195
-rw-r--r--sound/soc/ti/Makefile46
-rw-r--r--sound/soc/ti/ams-delta.c (renamed from sound/soc/omap/ams-delta.c)180
-rw-r--r--sound/soc/ti/davinci-evm.c262
-rw-r--r--sound/soc/ti/davinci-i2s.c (renamed from sound/soc/davinci/davinci-i2s.c)500
-rw-r--r--sound/soc/ti/davinci-i2s.h (renamed from sound/soc/davinci/davinci-i2s.h)5
-rw-r--r--sound/soc/ti/davinci-mcasp.c (renamed from sound/soc/davinci/davinci-mcasp.c)1469
-rw-r--r--sound/soc/ti/davinci-mcasp.h (renamed from sound/soc/davinci/davinci-mcasp.h)48
-rw-r--r--sound/soc/ti/edma-pcm.c (renamed from sound/soc/davinci/edma-pcm.c)30
-rw-r--r--sound/soc/ti/edma-pcm.h24
-rw-r--r--sound/soc/ti/j721e-evm.c938
-rw-r--r--sound/soc/ti/n810.c (renamed from sound/soc/omap/n810.c)114
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c (renamed from sound/soc/omap/omap-abe-twl6040.c)211
-rw-r--r--sound/soc/ti/omap-dmic.c (renamed from sound/soc/omap/omap-dmic.c)83
-rw-r--r--sound/soc/ti/omap-dmic.h (renamed from sound/soc/omap/omap-dmic.h)5
-rw-r--r--sound/soc/ti/omap-hdmi.c (renamed from sound/soc/omap/omap-hdmi-audio.c)66
-rw-r--r--sound/soc/ti/omap-mcbsp-priv.h (renamed from sound/soc/omap/mcbsp.h)128
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c502
-rw-r--r--sound/soc/ti/omap-mcbsp.c1441
-rw-r--r--sound/soc/ti/omap-mcbsp.h32
-rw-r--r--sound/soc/ti/omap-mcpdm.c (renamed from sound/soc/omap/omap-mcpdm.c)163
-rw-r--r--sound/soc/ti/omap-mcpdm.h (renamed from sound/soc/omap/omap-mcpdm.h)16
-rw-r--r--sound/soc/ti/omap-twl4030.c (renamed from sound/soc/omap/omap-twl4030.c)107
-rw-r--r--sound/soc/ti/omap3pandora.c (renamed from sound/soc/omap/omap3pandora.c)147
-rw-r--r--sound/soc/ti/osk5912.c (renamed from sound/soc/omap/osk5912.c)40
-rw-r--r--sound/soc/ti/rx51.c (renamed from sound/soc/omap/rx51.c)190
-rw-r--r--sound/soc/ti/sdma-pcm.c71
-rw-r--r--sound/soc/ti/sdma-pcm.h21
-rw-r--r--sound/soc/ti/udma-pcm.c43
-rw-r--r--sound/soc/ti/udma-pcm.h18
-rw-r--r--sound/soc/txx9/Kconfig29
-rw-r--r--sound/soc/txx9/Makefile11
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c235
-rw-r--r--sound/soc/txx9/txx9aclc-generic.c89
-rw-r--r--sound/soc/txx9/txx9aclc.c430
-rw-r--r--sound/soc/txx9/txx9aclc.h74
-rw-r--r--sound/soc/uniphier/Kconfig42
-rw-r--r--sound/soc/uniphier/Makefile11
-rw-r--r--sound/soc/uniphier/aio-compress.c433
-rw-r--r--sound/soc/uniphier/aio-core.c1266
-rw-r--r--sound/soc/uniphier/aio-cpu.c832
-rw-r--r--sound/soc/uniphier/aio-dma.c279
-rw-r--r--sound/soc/uniphier/aio-ld11.c356
-rw-r--r--sound/soc/uniphier/aio-pxs2.c265
-rw-r--r--sound/soc/uniphier/aio-reg.h476
-rw-r--r--sound/soc/uniphier/aio.h354
-rw-r--r--sound/soc/uniphier/evea.c569
-rw-r--r--sound/soc/ux500/Kconfig3
-rw-r--r--sound/soc/ux500/Makefile7
-rw-r--r--sound/soc/ux500/mop500.c75
-rw-r--r--sound/soc/ux500/mop500_ab8500.c38
-rw-r--r--sound/soc/ux500/mop500_ab8500.h11
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c108
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h7
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c85
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h21
-rw-r--r--sound/soc/ux500/ux500_pcm.c97
-rw-r--r--sound/soc/ux500/ux500_pcm.h7
-rw-r--r--sound/soc/xilinx/Kconfig27
-rw-r--r--sound/soc/xilinx/Makefile7
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c727
-rw-r--r--sound/soc/xilinx/xlnx_i2s.c259
-rw-r--r--sound/soc/xilinx/xlnx_spdif.c318
-rw-r--r--sound/soc/xtensa/Kconfig5
-rw-r--r--sound/soc/xtensa/Makefile3
-rw-r--r--sound/soc/xtensa/xtfpga-i2s.c97
-rw-r--r--sound/soc/zte/Kconfig17
-rw-r--r--sound/soc/zte/Makefile2
-rw-r--r--sound/soc/zte/zx296702-i2s.c436
-rw-r--r--sound/soc/zte/zx296702-spdif.c365
2027 files changed, 760772 insertions, 110073 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7ea66ee3653f..36e0d443ba0e 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SoC audio configuration
#
@@ -6,14 +7,14 @@ menuconfig SND_SOC
tristate "ALSA for SoC audio support"
select SND_PCM
select AC97_BUS if SND_SOC_AC97_BUS
- select SND_JACK if INPUT=y || INPUT=SND
+ select SND_JACK
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
- ---help---
+ help
If you want ASoC support, you should say Y here and also to the
specific driver for your SoC platform below.
-
+
ASoC provides power efficient ALSA support for embedded battery powered
SoC based systems like PDA's, Phones and Personal Media Players.
@@ -35,44 +36,119 @@ config SND_SOC_COMPRESS
config SND_SOC_TOPOLOGY
bool
+ select SND_DYNAMIC_MINORS
+
+config SND_SOC_TOPOLOGY_BUILD
+ bool "Build topology core"
+ select SND_SOC_TOPOLOGY
+ depends on KUNIT
+ help
+ This option exists to facilitate running the KUnit tests for
+ the topology core, KUnit is frequently tested in virtual
+ environments with minimal drivers enabled but the topology
+ core is usually selected by drivers. There is little reason
+ to enable it if not doing a KUnit build.
+
+config SND_SOC_TOPOLOGY_KUNIT_TEST
+ tristate "KUnit tests for SoC topology"
+ depends on KUNIT
+ depends on SND_SOC_TOPOLOGY
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC topology support say Y here.
+
+ This builds a module which can be later manually loaded to run KUNIT
+ test cases against soc-topology.c API. This should be primarily used
+ by developers to test their changes to ASoC.
+
+ Do note that it creates fake playback devices which do not interact
+ well with userspace. When running tests one may want to disable
+ userspace applications such as pulseaudio, to prevent unnecessary
+ problems.
+
+config SND_SOC_CARD_KUNIT_TEST
+ tristate "KUnit tests for SoC card"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC card functions say Y here.
+ If unsure, say N.
+
+config SND_SOC_UTILS_KUNIT_TEST
+ tristate "KUnit tests for SoC utils"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC utils library say Y here.
+
+config SND_SOC_OPS_KUNIT_TEST
+ tristate "KUnit tests for SoC ops"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC ops library say Y here.
+
+config SND_SOC_ACPI
+ tristate
+
+config SND_SOC_USB
+ tristate "SoC based USB audio offloading"
+ depends on SND_USB_AUDIO
+ help
+ Enable this option if an ASoC platform card has support to handle
+ USB audio offloading. This enables the SoC USB layer, which will
+ notify the ASoC USB DPCM backend DAI link about available USB audio
+ devices. Based on the notifications, sequences to enable the audio
+ stream can be taken based on the design.
# All the supported SoCs
source "sound/soc/adi/Kconfig"
source "sound/soc/amd/Kconfig"
+source "sound/soc/apple/Kconfig"
source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
source "sound/soc/bcm/Kconfig"
-source "sound/soc/blackfin/Kconfig"
source "sound/soc/cirrus/Kconfig"
-source "sound/soc/davinci/Kconfig"
source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig"
+source "sound/soc/google/Kconfig"
+source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig"
-source "sound/soc/nuc900/Kconfig"
-source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
+source "sound/soc/loongson/Kconfig"
source "sound/soc/img/Kconfig"
source "sound/soc/intel/Kconfig"
source "sound/soc/mediatek/Kconfig"
+source "sound/soc/meson/Kconfig"
source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/qcom/Kconfig"
+source "sound/soc/renesas/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
-source "sound/soc/sh/Kconfig"
-source "sound/soc/sirf/Kconfig"
+source "sound/soc/sdca/Kconfig"
+source "sound/soc/spacemit/Kconfig"
source "sound/soc/spear/Kconfig"
+source "sound/soc/sprd/Kconfig"
+source "sound/soc/starfive/Kconfig"
source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
-source "sound/soc/txx9/Kconfig"
+source "sound/soc/ti/Kconfig"
+source "sound/soc/uniphier/Kconfig"
source "sound/soc/ux500/Kconfig"
+source "sound/soc/xilinx/Kconfig"
source "sound/soc/xtensa/Kconfig"
-source "sound/soc/zte/Kconfig"
+
+# SOF
+source "sound/soc/sof/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
+source "sound/soc/sdw_utils/Kconfig"
+
# generic frame-work
source "sound/soc/generic/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21d16ee..8c0480e6484e 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,51 +1,86 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-core-y := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
+snd-soc-core-y += soc-pcm.o soc-devres.o soc-ops.o soc-link.o soc-card.o
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
-snd-soc-core-objs += soc-topology.o
+snd-soc-core-y += soc-topology.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),)
+# snd-soc-test-y := soc-topology-test.o
+obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) += soc-topology-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_CARD_KUNIT_TEST),)
+obj-$(CONFIG_SND_SOC_CARD_KUNIT_TEST) += soc-card-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),)
+# snd-soc-test-y := soc-utils-test.o
+obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) += soc-utils-test.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_OPS_KUNIT_TEST),)
+obj-$(CONFIG_SND_SOC_OPS_KUNIT_TEST) += soc-ops-test.o
endif
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
-snd-soc-core-objs += soc-generic-dmaengine-pcm.o
+snd-soc-core-y += soc-generic-dmaengine-pcm.o
endif
ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
-snd-soc-core-objs += soc-ac97.o
+snd-soc-core-y += soc-ac97.o
+endif
+
+ifneq ($(CONFIG_SND_SOC_ACPI),)
+snd-soc-acpi-y := soc-acpi.o
endif
+obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o
+
+obj-$(CONFIG_SND_SOC_USB) += soc-usb.o
+
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
+obj-$(CONFIG_SND_SOC) += apple/
obj-$(CONFIG_SND_SOC) += adi/
obj-$(CONFIG_SND_SOC) += amd/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += bcm/
-obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += cirrus/
-obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
+obj-$(CONFIG_SND_SOC) += google/
+obj-$(CONFIG_SND_SOC) += hisilicon/
obj-$(CONFIG_SND_SOC) += jz4740/
+obj-$(CONFIG_SND_SOC) += loongson/
obj-$(CONFIG_SND_SOC) += img/
obj-$(CONFIG_SND_SOC) += intel/
obj-$(CONFIG_SND_SOC) += mediatek/
+obj-$(CONFIG_SND_SOC) += meson/
obj-$(CONFIG_SND_SOC) += mxs/
-obj-$(CONFIG_SND_SOC) += nuc900/
-obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/
+obj-$(CONFIG_SND_SOC) += renesas/
obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
-obj-$(CONFIG_SND_SOC) += sh/
-obj-$(CONFIG_SND_SOC) += sirf/
+obj-$(CONFIG_SND_SOC) += sdca/
+obj-$(CONFIG_SND_SOC) += sof/
+obj-$(CONFIG_SND_SOC) += spacemit/
obj-$(CONFIG_SND_SOC) += spear/
+obj-$(CONFIG_SND_SOC) += sprd/
+obj-$(CONFIG_SND_SOC) += starfive/
obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
-obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += ti/
+obj-$(CONFIG_SND_SOC) += uniphier/
obj-$(CONFIG_SND_SOC) += ux500/
+obj-$(CONFIG_SND_SOC) += xilinx/
obj-$(CONFIG_SND_SOC) += xtensa/
-obj-$(CONFIG_SND_SOC) += zte/
+obj-$(CONFIG_SND_SOC) += sdw_utils/
diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig
index dd763f55edac..d47dffbf40d0 100644
--- a/sound/soc/adi/Kconfig
+++ b/sound/soc/adi/Kconfig
@@ -1,12 +1,8 @@
-config SND_SOC_ADI
- tristate "Audio support for Analog Devices reference designs"
- depends on MICROBLAZE || ARCH_ZYNQ || COMPILE_TEST
- help
- Audio support for various reference designs by Analog Devices.
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Analog Devices"
config SND_SOC_ADI_AXI_I2S
tristate "AXI-I2S support"
- depends on SND_SOC_ADI
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
@@ -14,8 +10,9 @@ config SND_SOC_ADI_AXI_I2S
config SND_SOC_ADI_AXI_SPDIF
tristate "AXI-SPDIF support"
- depends on SND_SOC_ADI
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral.
+
+endmenu
diff --git a/sound/soc/adi/Makefile b/sound/soc/adi/Makefile
index 64456c1e5347..0d2db8d05806 100644
--- a/sound/soc/adi/Makefile
+++ b/sound/soc/adi/Makefile
@@ -1,5 +1,6 @@
-snd-soc-adi-axi-i2s-objs := axi-i2s.o
-snd-soc-adi-axi-spdif-objs := axi-spdif.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-adi-axi-i2s-y := axi-i2s.o
+snd-soc-adi-axi-spdif-y := axi-spdif.o
obj-$(CONFIG_SND_SOC_ADI_AXI_I2S) += snd-soc-adi-axi-i2s.o
obj-$(CONFIG_SND_SOC_ADI_AXI_SPDIF) += snd-soc-adi-axi-spdif.o
diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c
index 4c23381727a1..41f89384f8fd 100644
--- a/sound/soc/adi/axi-i2s.c
+++ b/sound/soc/adi/axi-i2s.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012-2013, Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/clk.h>
@@ -43,6 +42,9 @@ struct axi_i2s {
struct clk *clk;
struct clk *clk_ref;
+ bool has_capture;
+ bool has_playback;
+
struct snd_soc_dai_driver dai_driver;
struct snd_dmaengine_dai_dma_data capture_dma_data;
@@ -136,13 +138,16 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
- &i2s->capture_dma_data);
+ snd_soc_dai_init_dma_data(
+ dai,
+ i2s->has_playback ? &i2s->playback_dma_data : NULL,
+ i2s->has_capture ? &i2s->capture_dma_data : NULL);
return 0;
}
static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
+ .probe = axi_i2s_dai_probe,
.startup = axi_i2s_startup,
.shutdown = axi_i2s_shutdown,
.trigger = axi_i2s_trigger,
@@ -150,25 +155,13 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
};
static struct snd_soc_dai_driver axi_i2s_dai = {
- .probe = axi_i2s_dai_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
- },
.ops = &axi_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static const struct snd_soc_component_driver axi_i2s_component = {
.name = "axi-i2s",
+ .legacy_dai_naming = 1,
};
static const struct regmap_config axi_i2s_regmap_config = {
@@ -178,6 +171,19 @@ static const struct regmap_config axi_i2s_regmap_config = {
.max_register = AXI_I2S_REG_STATUS,
};
+static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
+{
+ struct property *dma_names;
+ const char *dma_name;
+
+ of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
+ if (strcmp(dma_name, "rx") == 0)
+ i2s->has_capture = true;
+ if (strcmp(dma_name, "tx") == 0)
+ i2s->has_playback = true;
+ }
+}
+
static int axi_i2s_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -191,8 +197,9 @@ static int axi_i2s_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2s);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ axi_i2s_parse_of(i2s, pdev->dev.of_node);
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -213,13 +220,29 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
return ret;
- i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
- i2s->playback_dma_data.addr_width = 4;
- i2s->playback_dma_data.maxburst = 1;
+ if (i2s->has_playback) {
+ axi_i2s_dai.playback.channels_min = 2;
+ axi_i2s_dai.playback.channels_max = 2;
+ axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
+ axi_i2s_dai.playback.formats =
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
+
+ i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
+ i2s->playback_dma_data.addr_width = 4;
+ i2s->playback_dma_data.maxburst = 1;
+ }
+
+ if (i2s->has_capture) {
+ axi_i2s_dai.capture.channels_min = 2;
+ axi_i2s_dai.capture.channels_max = 2;
+ axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
+ axi_i2s_dai.capture.formats =
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
- i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
- i2s->capture_dma_data.addr_width = 4;
- i2s->capture_dma_data.maxburst = 1;
+ i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
+ i2s->capture_dma_data.addr_width = 4;
+ i2s->capture_dma_data.maxburst = 1;
+ }
i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
i2s->ratnum.den_step = 1;
@@ -240,6 +263,10 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
goto err_clk_disable;
+ dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
+ str_enabled_disabled(i2s->has_capture),
+ str_enabled_disabled(i2s->has_playback));
+
return 0;
err_clk_disable:
@@ -247,13 +274,11 @@ err_clk_disable:
return ret;
}
-static int axi_i2s_dev_remove(struct platform_device *pdev)
+static void axi_i2s_dev_remove(struct platform_device *pdev)
{
struct axi_i2s *i2s = platform_get_drvdata(pdev);
clk_disable_unprepare(i2s->clk);
-
- return 0;
}
static const struct of_device_id axi_i2s_of_match[] = {
diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c
index d7259d412892..5581134201a3 100644
--- a/sound/soc/adi/axi-spdif.c
+++ b/sound/soc/adi/axi-spdif.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012-2013, Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/init.h>
@@ -149,6 +148,7 @@ static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
}
static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
+ .probe = axi_spdif_dai_probe,
.startup = axi_spdif_startup,
.shutdown = axi_spdif_shutdown,
.trigger = axi_spdif_trigger,
@@ -156,7 +156,6 @@ static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
};
static struct snd_soc_dai_driver axi_spdif_dai = {
- .probe = axi_spdif_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -168,6 +167,7 @@ static struct snd_soc_dai_driver axi_spdif_dai = {
static const struct snd_soc_component_driver axi_spdif_component = {
.name = "axi-spdif",
+ .legacy_dai_naming = 1,
};
static const struct regmap_config axi_spdif_regmap_config = {
@@ -190,8 +190,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, spdif);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -240,13 +239,11 @@ err_clk_disable:
return ret;
}
-static int axi_spdif_dev_remove(struct platform_device *pdev)
+static void axi_spdif_dev_remove(struct platform_device *pdev)
{
struct axi_spdif *spdif = platform_get_drvdata(pdev);
clk_disable_unprepare(spdif->clk);
-
- return 0;
}
static const struct of_device_id axi_spdif_of_match[] = {
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 78187eb24f56..fd35a03aadcb 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -1,4 +1,190 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "AMD"
+
config SND_SOC_AMD_ACP
tristate "AMD Audio Coprocessor support"
help
This option enables ACP DMA support on AMD platform.
+
+config SND_SOC_AMD_CZ_DA7219MX98357_MACH
+ tristate "AMD CZ support for DA7219, RT5682 and MAX9835"
+ select CLK_FIXED_FCH
+ select SND_SOC_DA7219
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_MAX98357A
+ select SND_SOC_ADAU7002
+ select REGULATOR
+ depends on SND_SOC_AMD_ACP && I2C && GPIOLIB && ACPI
+ help
+ This option enables machine driver for DA7219, RT5682 and MAX9835.
+
+config SND_SOC_AMD_CZ_RT5645_MACH
+ tristate "AMD CZ support for RT5645"
+ select SND_SOC_RT5645
+ depends on SND_SOC_AMD_ACP && I2C
+ help
+ This option enables machine driver for rt5645.
+
+config SND_SOC_AMD_ST_ES8336_MACH
+ tristate "AMD ST support for ES8336"
+ select SND_SOC_ACPI if ACPI
+ select SND_SOC_ES8316
+ depends on SND_SOC_AMD_ACP && ACPI
+ depends on I2C
+ help
+ This option enables machine driver for Jadeite platform
+ using es8336 codec.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP3x
+ tristate "AMD Audio Coprocessor-v3.x support"
+ depends on X86 && PCI
+ help
+ This option enables ACP v3.x I2S support on AMD platform
+
+config SND_SOC_AMD_RV_RT5682_MACH
+ tristate "AMD RV support for RT5682"
+ select CLK_FIXED_FCH
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_MAX98357A
+ select SND_SOC_CROS_EC_CODEC
+ select I2C_CROS_EC_TUNNEL
+ select SND_SOC_RT1015
+ select SND_SOC_RT1015P
+ depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC && GPIOLIB
+ help
+ This option enables machine driver for RT5682 and MAX9835.
+
+config SND_SOC_AMD_RENOIR
+ tristate "AMD Audio Coprocessor - Renoir support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables ACP support for Renoir platform
+
+config SND_SOC_AMD_RENOIR_MACH
+ tristate "AMD Renoir support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_RENOIR && GPIOLIB
+ help
+ This option enables machine driver for DMIC
+
+config SND_SOC_AMD_ACP5x
+ tristate "AMD Audio Coprocessor-v5.x I2S support"
+ depends on X86 && PCI
+ select SND_AMD_ACP_CONFIG
+ help
+ This option enables ACP v5.x support on AMD platform
+
+ By enabling this flag build will trigger for ACP PCI driver,
+ ACP DMA driver, CPU DAI driver.
+
+config SND_SOC_AMD_VANGOGH_MACH
+ tristate "AMD Vangogh support for NAU8821/CS35L41/MAX98388"
+ select SND_SOC_NAU8821
+ select SND_SOC_CS35L41_SPI
+ select SND_SOC_MAX98388
+ select SND_AMD_ACP_CONFIG
+ depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
+ help
+ This option enables machine driver for Vangogh platform
+ using NAU8821 and either CS35L41 or MAX98388 codecs.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP6x
+ tristate "AMD Audio Coprocessor-v6.x Yellow Carp support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e ACP v6.x support on
+ AMD Yellow Carp platform. By enabling this flag build will be
+ triggered for ACP PCI driver, ACP PDM DMA driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_YC_MACH
+ tristate "AMD YC support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_ACP6x && ACPI
+ help
+ This option enables machine driver for Yellow Carp platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_AMD_ACP_CONFIG
+ tristate "AMD ACP configuration selection"
+ select SND_SOC_ACPI if ACPI
+ help
+ This option adds an auto detection to determine which ACP
+ driver modules to use
+
+source "sound/soc/amd/acp/Kconfig"
+
+config SND_SOC_AMD_RPL_ACP6x
+ tristate "AMD Audio Coprocessor-v6.2 RPL support"
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e. ACP v6.2 support on
+ AMD RPL platform. By enabling this flag build will be
+ triggered for ACP PCI driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP63_TOPLEVEL
+ tristate "support for AMD platforms with ACP version >= 6.3"
+ default SND_AMD_ACP_CONFIG
+ depends on SND_AMD_ACP_CONFIG
+ depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
+ depends on X86 || COMPILE_TEST
+ help
+ This adds support for AMD platforms with ACP version >= 6.3.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+if SND_SOC_AMD_ACP63_TOPLEVEL
+
+config SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ tristate
+ select SND_AMD_SOUNDWIRE_ACPI if ACPI
+
+config SND_SOC_AMD_SOUNDWIRE
+ tristate "Support for SoundWire based AMD platforms"
+ default SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on ACPI
+ depends on SOUNDWIRE_AMD
+ help
+ This adds support for SoundWire for AMD platforms.
+ Say Y if you want to enable SoundWire links with SOF.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS
+ tristate "AMD Audio Coprocessor-v6.3/v7.0/v7.1 support"
+ select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_ACPI_AMD_MATCH
+ depends on X86 && PCI && ACPI
+ help
+ This option enables Audio Coprocessor i.e ACP6.3/ACP7.0/ACP7.1
+ variants support. By enabling this flag build will be triggered
+ for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire DMA
+ driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS_MACH
+ tristate "AMD PINK SARDINE support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_PS
+ help
+ This option enables machine driver for Pink Sardine platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
+
+endif
+endmenu
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index 1a66ec0366b2..4f89d962cce2 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -1,3 +1,21 @@
-snd-soc-acp-pcm-objs := acp-pcm-dma.o
+# SPDX-License-Identifier: GPL-2.0-only
+acp_audio_dma-y := acp-pcm-dma.o
+snd-soc-acp-da7219mx98357-mach-y := acp-da7219-max98357a.o
+snd-soc-acp-rt5645-mach-y := acp-rt5645.o
+snd-soc-acp-es8336-mach-y := acp-es8336.o
+snd-soc-acp-rt5682-mach-y := acp3x-rt5682-max9836.o
+snd-acp-config-y := acp-config.o
-obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
+obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o
+obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o
+obj-$(CONFIG_SND_SOC_AMD_ST_ES8336_MACH) += snd-soc-acp-es8336-mach.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
+obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
+obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/
+obj-$(CONFIG_SND_SOC_AMD_PS) += ps/
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
new file mode 100644
index 000000000000..365209ea53f3
--- /dev/null
+++ b/sound/soc/amd/acp-config.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/* ACP machine configuration module */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../sof/amd/acp.h"
+#include "mach-config.h"
+
+#define ACP_7_0_REV 0x70
+
+static int acp_quirk_data;
+
+static const struct config_entry config_table[] = {
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+ },
+ },
+ {}
+ },
+ },
+};
+
+static int snd_amd_acp_acpi_find_config(struct pci_dev *pci)
+{
+ const union acpi_object *obj;
+ int acp_flag = FLAG_AMD_LEGACY_ONLY_DMIC;
+
+ if (!acpi_dev_get_property(ACPI_COMPANION(&pci->dev), "acp-audio-config-flag",
+ ACPI_TYPE_INTEGER, &obj))
+ acp_flag = obj->integer.value;
+
+ return acp_flag;
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci)
+{
+ const struct config_entry *table = config_table;
+ u16 device = pci->device;
+ int i;
+
+ /* Do not enable FLAGS on older platforms with Rev Id zero
+ * For platforms which has ACP 7.0 or higher, read the acp
+ * config flag from BIOS ACPI table and for older platforms
+ * read it from DMI tables.
+ */
+ if (!pci->revision)
+ return 0;
+ else if (pci->revision >= ACP_7_0_REV)
+ return snd_amd_acp_acpi_find_config(pci);
+
+ for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) {
+ if (table->device != device)
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ acp_quirk_data = table->flags;
+ return table->flags;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_amd_acp_find_config);
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+static struct snd_soc_acpi_codecs amp_max98388 = {
+ .num_codecs = 1,
+ .codecs = {"ADS8388"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-dsp",
+ .pdata = (void *)&acp_quirk_data,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-acp.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[] = {
+ {
+ .id = "NVTN2020",
+ .drv_name = "nau8821-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max98388,
+ .fw_filename = "sof-vangogh.ri",
+ .sof_tplg_filename = "sof-vangogh-nau8821-max.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_vangogh_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[] = {
+ {
+ .id = "AMDI1019",
+ .drv_name = "rmb-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-acp-rmb.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "nau8825-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-rmb-nau8825-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-hs-rt1019",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-rmb-rt5682s-rt1019.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_rmb_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[] = {
+ {
+ .id = "AMDI1019",
+ .drv_name = "acp63-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-acp_6_3.ri",
+ .sof_tplg_filename = "sof-acp_6_3.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = {
+ {
+ .id = "AMDI1010",
+ .drv_name = "acp70-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-acp_7_0.ri",
+ .sof_tplg_filename = "sof-acp_7_0.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines);
+
+MODULE_DESCRIPTION("AMD ACP Machine Configuration Module");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
new file mode 100644
index 000000000000..42aa009c4e13
--- /dev/null
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: MIT
+//
+// Machine driver for AMD ACP Audio engine using DA7219, RT5682 & MAX98357 codec
+//
+//Copyright 2017-2021 Advanced Micro Devices, Inc.
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/acpi.h>
+
+#include "acp.h"
+#include "../codecs/da7219.h"
+#include "../codecs/rt5682.h"
+
+#define CZ_PLAT_CLK 48000000
+#define DUAL_CHANNEL 2
+#define RT5682_PLL_FREQ (48000 * 512)
+
+static struct snd_soc_jack cz_jack;
+static struct snd_soc_jack_pin cz_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static struct clk *da7219_dai_wclk;
+static struct clk *da7219_dai_bclk;
+static struct clk *rt5682_dai_wclk;
+static struct clk *rt5682_dai_bclk;
+
+void *acp_soc_is_rltk_max(struct device *dev);
+
+static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK,
+ CZ_PLAT_CLK, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL,
+ CZ_PLAT_CLK, DA7219_PLL_FREQ_OUT_98304);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ da7219_dai_wclk = devm_clk_get(component->dev, "da7219-dai-wclk");
+ if (IS_ERR(da7219_dai_wclk))
+ return PTR_ERR(da7219_dai_wclk);
+
+ da7219_dai_bclk = devm_clk_get(component->dev, "da7219-dai-bclk");
+ if (IS_ERR(da7219_dai_bclk))
+ return PTR_ERR(da7219_dai_bclk);
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ snd_soc_component_set_jack(component, &cz_jack, NULL);
+
+ return 0;
+}
+
+static int da7219_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ /*
+ * Set wclk to 48000 because the rate constraint of this driver is
+ * 48000. ADAU7002 spec: "The ADAU7002 requires a BCLK rate that is
+ * minimum of 64x the LRCLK sample rate." DA7219 is the only clk
+ * source so for all codecs we have to limit bclk to 64X lrclk.
+ */
+ clk_set_rate(da7219_dai_wclk, 48000);
+ clk_set_rate(da7219_dai_bclk, 48000 * 64);
+ ret = clk_prepare_enable(da7219_dai_bclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable master clock %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void da7219_clk_disable(void)
+{
+ clk_disable_unprepare(da7219_dai_bclk);
+}
+
+static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(codec_dai->dev, "codec dai name = %s\n", codec_dai->name);
+
+ /* Set codec sysclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev,
+ "Failed to set rt5682 SYSCLK: %d\n", ret);
+ return ret;
+ }
+ /* set codec PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ CZ_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set rt5682 PLL: %d\n", ret);
+ return ret;
+ }
+
+ rt5682_dai_wclk = devm_clk_get(component->dev, "rt5682-dai-wclk");
+ if (IS_ERR(rt5682_dai_wclk))
+ return PTR_ERR(rt5682_dai_wclk);
+
+ rt5682_dai_bclk = devm_clk_get(component->dev, "rt5682-dai-bclk");
+ if (IS_ERR(rt5682_dai_bclk))
+ return PTR_ERR(rt5682_dai_bclk);
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ ret = snd_soc_component_set_jack(component, &cz_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int rt5682_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ /*
+ * Set wclk to 48000 because the rate constraint of this driver is
+ * 48000. ADAU7002 spec: "The ADAU7002 requires a BCLK rate that is
+ * minimum of 64x the LRCLK sample rate." RT5682 is the only clk
+ * source so for all codecs we have to limit bclk to 64X lrclk.
+ */
+ ret = clk_set_rate(rt5682_dai_wclk, 48000);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting wclk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(rt5682_dai_bclk, 48000 * 64);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting bclk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(rt5682_dai_wclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable wclk %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static void rt5682_clk_disable(void)
+{
+ clk_disable_unprepare(rt5682_dai_wclk);
+}
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL1;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ return da7219_clk_enable(substream);
+}
+
+static int cz_dmic1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return da7219_clk_enable(substream);
+}
+
+static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
+{
+ da7219_clk_disable();
+}
+
+static int cz_rt5682_play_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_cap_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL1;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_dmic1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return rt5682_clk_enable(substream);
+}
+
+static void cz_rt5682_shutdown(struct snd_pcm_substream *substream)
+{
+ rt5682_clk_disable();
+}
+
+static const struct snd_soc_ops cz_da7219_play_ops = {
+ .startup = cz_da7219_play_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_da7219_cap_ops = {
+ .startup = cz_da7219_cap_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_max_play_ops = {
+ .startup = cz_max_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_dmic0_cap_ops = {
+ .startup = cz_dmic0_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_dmic1_cap_ops = {
+ .startup = cz_dmic1_startup,
+ .shutdown = cz_da7219_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_play_ops = {
+ .startup = cz_rt5682_play_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_cap_ops = {
+ .startup = cz_rt5682_cap_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_max_play_ops = {
+ .startup = cz_rt5682_max_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_dmic0_cap_ops = {
+ .startup = cz_rt5682_dmic0_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_dmic1_cap_ops = {
+ .startup = cz_rt5682_dmic1_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
+SND_SOC_DAILINK_DEF(designware2,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
+SND_SOC_DAILINK_DEF(designware3,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3")));
+
+SND_SOC_DAILINK_DEF(dlgs,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", "da7219-hifi")));
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+SND_SOC_DAILINK_DEF(mx,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
+SND_SOC_DAILINK_DEF(adau,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ADAU7002:00", "adau7002-hifi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
+
+static struct snd_soc_dai_link cz_dai_7219_98357[] = {
+ {
+ .name = "amd-da7219-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_da7219_init,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_da7219_play_ops,
+ SND_SOC_DAILINK_REG(designware1, dlgs, platform),
+ },
+ {
+ .name = "amd-da7219-cap",
+ .stream_name = "Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_da7219_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, dlgs, platform),
+ },
+ {
+ .name = "amd-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_max_play_ops,
+ SND_SOC_DAILINK_REG(designware3, mx, platform),
+ },
+ {
+ /* C panel DMIC */
+ .name = "dmic0",
+ .stream_name = "DMIC0 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_dmic0_cap_ops,
+ SND_SOC_DAILINK_REG(designware3, adau, platform),
+ },
+ {
+ /* A/B panel DMIC */
+ .name = "dmic1",
+ .stream_name = "DMIC1 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_dmic1_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, adau, platform),
+ },
+};
+
+static struct snd_soc_dai_link cz_dai_5682_98357[] = {
+ {
+ .name = "amd-rt5682-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_rt5682_init,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_play_ops,
+ SND_SOC_DAILINK_REG(designware1, rt5682, platform),
+ },
+ {
+ .name = "amd-rt5682-cap",
+ .stream_name = "Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, rt5682, platform),
+ },
+ {
+ .name = "amd-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_max_play_ops,
+ SND_SOC_DAILINK_REG(designware3, mx, platform),
+ },
+ {
+ /* C panel DMIC */
+ .name = "dmic0",
+ .stream_name = "DMIC0 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_dmic0_cap_ops,
+ SND_SOC_DAILINK_REG(designware3, adau, platform),
+ },
+ {
+ /* A/B panel DMIC */
+ .name = "dmic1",
+ .stream_name = "DMIC1 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_dmic1_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, adau, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget cz_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route cz_audio_route[] = {
+ {"Headphones", NULL, "HPL"},
+ {"Headphones", NULL, "HPR"},
+ {"MIC", NULL, "Headset Mic"},
+ {"Speakers", NULL, "Speaker"},
+ {"PDM_DAT", NULL, "Int Mic"},
+};
+
+static const struct snd_soc_dapm_route cz_rt5682_audio_route[] = {
+ {"Headphones", NULL, "HPOL"},
+ {"Headphones", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Speakers", NULL, "Speaker"},
+ {"PDM_DAT", NULL, "Int Mic"},
+};
+
+static const struct snd_kcontrol_new cz_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static struct snd_soc_card cz_card = {
+ .name = "acpd7219m98357",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_7219_98357,
+ .num_links = ARRAY_SIZE(cz_dai_7219_98357),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(cz_audio_route),
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+static struct snd_soc_card cz_rt5682_card = {
+ .name = "acpr5682m98357",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_5682_98357,
+ .num_links = ARRAY_SIZE(cz_dai_5682_98357),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_rt5682_audio_route,
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+void *acp_soc_is_rltk_max(struct device *dev)
+{
+ const struct acpi_device_id *match;
+
+ match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!match)
+ return NULL;
+ return (void *)match->driver_data;
+}
+
+static struct regulator_consumer_supply acp_da7219_supplies[] = {
+ REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"),
+ REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"),
+ REGULATOR_SUPPLY("VDDIO", "i2c-DLGS7219:00"),
+ REGULATOR_SUPPLY("IOVDD", "ADAU7002:00"),
+};
+
+static struct regulator_init_data acp_da7219_data = {
+ .constraints = {
+ .always_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(acp_da7219_supplies),
+ .consumer_supplies = acp_da7219_supplies,
+};
+
+static struct regulator_config acp_da7219_cfg = {
+ .init_data = &acp_da7219_data,
+};
+
+static const struct regulator_ops acp_da7219_ops = {
+};
+
+static const struct regulator_desc acp_da7219_desc = {
+ .name = "reg-fixed-1.8V",
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &acp_da7219_ops,
+ .fixed_uV = 1800000, /* 1.8V */
+ .n_voltages = 1,
+};
+
+static int cz_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+ struct regulator_dev *rdev;
+ struct device *dev = &pdev->dev;
+
+ card = (struct snd_soc_card *)acp_soc_is_rltk_max(dev);
+ if (!card)
+ return -ENODEV;
+ if (!strcmp(card->name, "acpd7219m98357")) {
+ acp_da7219_cfg.dev = &pdev->dev;
+ rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc,
+ &acp_da7219_cfg);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator: %d\n",
+ (int)PTR_ERR(rdev));
+ return -EINVAL;
+ }
+ }
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info),
+ GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ acp_bt_uart_enable = !device_property_read_bool(&pdev->dev,
+ "bt-pad-enable");
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cz_audio_acpi_match[] = {
+ { "AMD7219", (unsigned long)&cz_card },
+ { "AMDI5682", (unsigned long)&cz_rt5682_card},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
+#endif
+
+static struct platform_driver cz_pcm_driver = {
+ .driver = {
+ .name = "cz-da7219-max98357a",
+ .acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = cz_probe,
+};
+
+module_platform_driver(cz_pcm_driver);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("DA7219, RT5682 & MAX98357A audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
new file mode 100644
index 000000000000..b16dde0e2987
--- /dev/null
+++ b/sound/soc/amd/acp-es8336.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Stoney platform using ES8336 Codec
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include "acp.h"
+
+#define DUAL_CHANNEL 2
+#define DRV_NAME "acp2x_mach"
+#define ST_JADEITE 1
+#define ES8336_PLL_FREQ (48000 * 256)
+
+static unsigned long acp2x_machine_id;
+static struct snd_soc_jack st_jack;
+static struct device *codec_dev;
+static struct gpio_desc *gpio_pa;
+
+static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpiod_set_value_cansleep(gpio_pa, true);
+ else
+ gpiod_set_value_cansleep(gpio_pa, false);
+
+ return 0;
+}
+
+static struct snd_soc_jack_pin st_es8316_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int st_es8336_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct snd_soc_component *codec;
+
+ codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+ card = rtd->card;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &st_jack, st_es8316_jack_pins,
+ ARRAY_SIZE(st_es8316_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+ snd_jack_set_key(st_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ ret = snd_soc_component_set_jack(codec, &st_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const unsigned int st_channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int st_rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list st_constraints_rates = {
+ .count = ARRAY_SIZE(st_rates),
+ .list = st_rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list st_constraints_channels = {
+ .count = ARRAY_SIZE(st_channels),
+ .list = st_channels,
+ .mask = 0,
+};
+
+static int st_es8336_codec_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ card = rtd->card;
+ machine = snd_soc_card_get_drvdata(card);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, ES8336_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &st_constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &st_constraints_rates);
+
+ machine->play_i2s_instance = I2S_MICSP_INSTANCE;
+ machine->cap_i2s_instance = I2S_MICSP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return 0;
+}
+
+static const struct snd_soc_ops st_es8336_ops = {
+ .startup = st_es8336_codec_startup,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
+
+static struct snd_soc_dai_link st_dai_es8336[] = {
+ {
+ .name = "amdes8336",
+ .stream_name = "ES8336 HiFi Play",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .init = st_es8336_init,
+ .ops = &st_es8336_ops,
+ SND_SOC_DAILINK_REG(designware1, codec, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget st_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ sof_es8316_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route st_audio_route[] = {
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"MIC1", NULL, "Headset Mic"},
+ {"MIC2", NULL, "Internal Mic"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_kcontrol_new st_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static const struct acpi_gpio_params pa_enable_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
+ { "pa-enable-gpios", &pa_enable_gpio, 1 },
+ { }
+};
+
+static int st_es8336_late_probe(struct snd_soc_card *card)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev("ESSX8336", NULL, -1);
+ if (!adev)
+ return -ENODEV;
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev) {
+ dev_err(card->dev, "can not find codec dev\n");
+ return -ENODEV;
+ }
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios);
+ if (ret)
+ dev_warn(card->dev, "Failed to add driver gpios\n");
+
+ gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio_pa)) {
+ ret = dev_err_probe(card->dev, PTR_ERR(gpio_pa),
+ "could not get pa-enable GPIO\n");
+ put_device(codec_dev);
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_card st_card = {
+ .name = "acpes8336",
+ .owner = THIS_MODULE,
+ .dai_link = st_dai_es8336,
+ .num_links = ARRAY_SIZE(st_dai_es8336),
+ .dapm_widgets = st_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(st_widgets),
+ .dapm_routes = st_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(st_audio_route),
+ .controls = st_mc_controls,
+ .num_controls = ARRAY_SIZE(st_mc_controls),
+ .late_probe = st_es8336_late_probe,
+};
+
+static int st_es8336_quirk_cb(const struct dmi_system_id *id)
+{
+ acp2x_machine_id = ST_JADEITE;
+ return 1;
+}
+
+static const struct dmi_system_id st_es8336_quirk_table[] = {
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMD"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jadeite"),
+ },
+ },
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "IP3 Technology CO.,Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN1D"),
+ },
+ },
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Standard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN10"),
+ },
+ },
+ {}
+};
+
+static int st_es8336_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ dmi_check_system(st_es8336_quirk_table);
+ switch (acp2x_machine_id) {
+ case ST_JADEITE:
+ card = &st_card;
+ st_card.dev = &pdev->dev;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, &st_card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id st_audio_acpi_match[] = {
+ {"AMDI8336", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, st_audio_acpi_match);
+#endif
+
+static struct platform_driver st_mach_driver = {
+ .driver = {
+ .name = "st-es8316",
+ .acpi_match_table = ACPI_PTR(st_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = st_es8336_probe,
+};
+
+module_platform_driver(st_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("st-es8316 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index 3191e0a7d273..897dde630022 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -1,28 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD ALSA SoC PCM Driver for ACP 2.x
*
* Copyright 2014-2015 Advanced Micro Devices, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/delay.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/sizes.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
-
+#include <drm/amd_asic_type.h>
#include "acp.h"
+#define DRV_NAME "acp_audio_dma"
+
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 2
#define PLAYBACK_MAX_PERIOD_SIZE 16384
@@ -35,6 +30,15 @@
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
#define MIN_BUFFER MAX_BUFFER
+#define ST_PLAYBACK_MAX_PERIOD_SIZE 4096
+#define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE
+#define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define ST_MIN_BUFFER ST_MAX_BUFFER
+
+#define DRV_NAME "acp_audio_dma"
+bool acp_bt_uart_enable = true;
+EXPORT_SYMBOL(acp_bt_uart_enable);
+
static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
@@ -73,10 +77,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
.periods_max = CAPTURE_MAX_NUM_PERIODS,
};
-struct audio_drv_data {
- struct snd_pcm_substream *play_stream;
- struct snd_pcm_substream *capture_stream;
- void __iomem *acp_mmio;
+static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = ST_MAX_BUFFER,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = ST_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_st_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = ST_MAX_BUFFER,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = ST_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
};
static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg)
@@ -89,7 +125,8 @@ static void acp_reg_write(u32 val, void __iomem *acp_mmio, u32 reg)
writel(val, acp_mmio + (reg * 4));
}
-/* Configure a given dma channel parameters - enable/disble,
+/*
+ * Configure a given dma channel parameters - enable/disable,
* number of descriptors, priority
*/
static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num,
@@ -108,17 +145,18 @@ static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num,
& dscr_strt_idx),
acp_mmio, mmACP_DMA_DSCR_STRT_IDX_0 + ch_num);
- /* program a DMA channel with the number of descriptors to be
+ /*
+ * program a DMA channel with the number of descriptors to be
* processed in the transfer
- */
+ */
acp_reg_write(ACP_DMA_DSCR_CNT_0__DMAChDscrCnt_MASK & num_dscrs,
- acp_mmio, mmACP_DMA_DSCR_CNT_0 + ch_num);
+ acp_mmio, mmACP_DMA_DSCR_CNT_0 + ch_num);
/* set DMA channel priority */
acp_reg_write(priority_level, acp_mmio, mmACP_DMA_PRIO_0 + ch_num);
}
-/* Initialize a dma descriptor in SRAM based on descritor information passed */
+/* Initialize a dma descriptor in SRAM based on descriptor information passed */
static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
u16 descr_idx,
acp_dma_dscr_transfer_t *descr_info)
@@ -139,107 +177,128 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
}
-/* Initialize the DMA descriptor information for transfer between
+static void pre_config_reset(void __iomem *acp_mmio, u16 ch_num)
+{
+ u32 dma_ctrl;
+ int ret;
+
+ /* clear the reset bit */
+ dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
+ acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+ /* check the reset bit before programming configuration registers */
+ ret = readl_poll_timeout(acp_mmio + ((mmACP_DMA_CNTL_0 + ch_num) * 4),
+ dma_ctrl,
+ !(dma_ctrl & ACP_DMA_CNTL_0__DMAChRst_MASK),
+ 100, ACP_DMA_RESET_TIME);
+ if (ret < 0)
+ pr_err("Failed to clear reset of channel : %d\n", ch_num);
+}
+
+/*
+ * Initialize the DMA descriptor information for transfer between
* system memory <-> ACP SRAM
*/
static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
u32 size, int direction,
- u32 pte_offset)
+ u32 pte_offset, u16 ch,
+ u32 sram_bank, u16 dma_dscr_idx,
+ u32 asic_type)
{
u16 i;
- u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL];
for (i = 0; i < NUM_DSCRS_PER_CHANNEL; i++) {
dmadscr[i].xfer_val = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i;
- dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS +
- (size / 2) - (i * (size/2));
+ dma_dscr_idx = dma_dscr_idx + i;
+ dmadscr[i].dest = sram_bank + (i * (size / 2));
dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
- + (pte_offset * SZ_4K) + (i * (size/2));
- dmadscr[i].xfer_val |=
- (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
- (size / 2);
+ + (pte_offset * SZ_4K) + (i * (size / 2));
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM << 16) |
+ (size / 2);
+ break;
+ default:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM << 16) |
+ (size / 2);
+ }
} else {
- dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i;
- dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS +
- (i * (size/2));
- dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
- + (pte_offset * SZ_4K) +
- (i * (size/2));
- dmadscr[i].xfer_val |=
- BIT(22) |
- (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) |
- (size / 2);
+ dma_dscr_idx = dma_dscr_idx + i;
+ dmadscr[i].src = sram_bank + (i * (size / 2));
+ dmadscr[i].dest =
+ ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
+ (pte_offset * SZ_4K) + (i * (size / 2));
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC << 16) |
+ (size / 2);
+ break;
+ default:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION << 16) |
+ (size / 2);
+ }
}
config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
- &dmadscr[i]);
+ &dmadscr[i]);
}
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH12,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
- else
- config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH14,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
+ pre_config_reset(acp_mmio, ch);
+ config_acp_dma_channel(acp_mmio, ch,
+ dma_dscr_idx - 1,
+ NUM_DSCRS_PER_CHANNEL,
+ ACP_DMA_PRIORITY_LEVEL_NORMAL);
}
-/* Initialize the DMA descriptor information for transfer between
+/*
+ * Initialize the DMA descriptor information for transfer between
* ACP SRAM <-> I2S
*/
-static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio,
- u32 size, int direction)
+static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size,
+ int direction, u32 sram_bank,
+ u16 destination, u16 ch,
+ u16 dma_dscr_idx, u32 asic_type)
{
-
u16 i;
- u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH13;
acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL];
for (i = 0; i < NUM_DSCRS_PER_CHANNEL; i++) {
dmadscr[i].xfer_val = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH13 + i;
- dmadscr[i].src = ACP_SHARED_RAM_BANK_1_ADDRESS +
- (i * (size/2));
+ dma_dscr_idx = dma_dscr_idx + i;
+ dmadscr[i].src = sram_bank + (i * (size / 2));
/* dmadscr[i].dest is unused by hardware. */
dmadscr[i].dest = 0;
- dmadscr[i].xfer_val |= BIT(22) | (TO_ACP_I2S_1 << 16) |
+ dmadscr[i].xfer_val |= BIT(22) | (destination << 16) |
(size / 2);
} else {
- dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i;
+ dma_dscr_idx = dma_dscr_idx + i;
/* dmadscr[i].src is unused by hardware. */
dmadscr[i].src = 0;
- dmadscr[i].dest = ACP_SHARED_RAM_BANK_5_ADDRESS +
- (i * (size / 2));
+ dmadscr[i].dest =
+ sram_bank + (i * (size / 2));
dmadscr[i].xfer_val |= BIT(22) |
- (FROM_ACP_I2S_1 << 16) | (size / 2);
+ (destination << 16) | (size / 2);
}
config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
- &dmadscr[i]);
+ &dmadscr[i]);
}
- /* Configure the DMA channel with the above descriptore */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- config_acp_dma_channel(acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH13,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
- else
- config_acp_dma_channel(acp_mmio, I2S_TO_ACP_DMA_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH15,
- NUM_DSCRS_PER_CHANNEL,
- ACP_DMA_PRIORITY_LEVEL_NORMAL);
+ pre_config_reset(acp_mmio, ch);
+ /* Configure the DMA channel with the above descriptor */
+ config_acp_dma_channel(acp_mmio, ch, dma_dscr_idx - 1,
+ NUM_DSCRS_PER_CHANNEL,
+ ACP_DMA_PRIORITY_LEVEL_NORMAL);
}
/* Create page table entries in ACP SRAM for the allocated memory */
-static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
+static void acp_pte_config(void __iomem *acp_mmio, dma_addr_t addr,
u16 num_of_pages, u32 pte_offset)
{
u16 page_idx;
- u64 addr;
u32 low;
u32 high;
u32 offset;
@@ -248,8 +307,7 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
for (page_idx = 0; page_idx < (num_of_pages); page_idx++) {
/* Load the low address of page int ACP SRAM through SRBM */
acp_reg_write((offset + (page_idx * 8)),
- acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
- addr = page_to_phys(pg);
+ acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
low = lower_32_bits(addr);
high = upper_32_bits(addr);
@@ -258,42 +316,102 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
/* Load the High address of page int ACP SRAM through SRBM */
acp_reg_write((offset + (page_idx * 8) + 4),
- acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+ acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
/* page enable in ACP */
high |= BIT(31);
acp_reg_write(high, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
- /* Move to next physically contiguos page */
- pg++;
+ /* Move to next physically contiguous page */
+ addr += PAGE_SIZE;
}
}
static void config_acp_dma(void __iomem *acp_mmio,
- struct audio_substream_data *audio_config)
+ struct audio_substream_data *rtd,
+ u32 asic_type)
{
- u32 pte_offset;
+ u16 ch_acp_sysmem, ch_acp_i2s;
- if (audio_config->direction == SNDRV_PCM_STREAM_PLAYBACK)
- pte_offset = ACP_PLAYBACK_PTE_OFFSET;
- else
- pte_offset = ACP_CAPTURE_PTE_OFFSET;
-
- acp_pte_config(acp_mmio, audio_config->pg, audio_config->num_of_pages,
- pte_offset);
+ acp_pte_config(acp_mmio, rtd->dma_addr, rtd->num_of_pages,
+ rtd->pte_offset);
+ if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_acp_sysmem = rtd->ch1;
+ ch_acp_i2s = rtd->ch2;
+ } else {
+ ch_acp_i2s = rtd->ch1;
+ ch_acp_sysmem = rtd->ch2;
+ }
/* Configure System memory <-> ACP SRAM DMA descriptors */
- set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size,
- audio_config->direction, pte_offset);
-
+ set_acp_sysmem_dma_descriptors(acp_mmio, rtd->size,
+ rtd->direction, rtd->pte_offset,
+ ch_acp_sysmem, rtd->sram_bank,
+ rtd->dma_dscr_idx_1, asic_type);
/* Configure ACP SRAM <-> I2S DMA descriptors */
- set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size,
- audio_config->direction);
+ set_acp_to_i2s_dma_descriptors(acp_mmio, rtd->size,
+ rtd->direction, rtd->sram_bank,
+ rtd->destination, ch_acp_i2s,
+ rtd->dma_dscr_idx_2, asic_type);
+}
+
+static void acp_dma_cap_channel_enable(void __iomem *acp_mmio,
+ u16 cap_channel)
+{
+ u32 val, ch_reg, imr_reg, res_reg;
+
+ switch (cap_channel) {
+ case CAP_CHANNEL1:
+ ch_reg = mmACP_I2SMICSP_RER1;
+ res_reg = mmACP_I2SMICSP_RCR1;
+ imr_reg = mmACP_I2SMICSP_IMR1;
+ break;
+ case CAP_CHANNEL0:
+ default:
+ ch_reg = mmACP_I2SMICSP_RER0;
+ res_reg = mmACP_I2SMICSP_RCR0;
+ imr_reg = mmACP_I2SMICSP_IMR0;
+ break;
+ }
+ val = acp_reg_read(acp_mmio,
+ mmACP_I2S_16BIT_RESOLUTION_EN);
+ if (val & ACP_I2S_MIC_16BIT_RESOLUTION_EN) {
+ acp_reg_write(0x0, acp_mmio, ch_reg);
+ /* Set 16bit resolution on capture */
+ acp_reg_write(0x2, acp_mmio, res_reg);
+ }
+ val = acp_reg_read(acp_mmio, imr_reg);
+ val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK;
+ val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK;
+ acp_reg_write(val, acp_mmio, imr_reg);
+ acp_reg_write(0x1, acp_mmio, ch_reg);
+}
+
+static void acp_dma_cap_channel_disable(void __iomem *acp_mmio,
+ u16 cap_channel)
+{
+ u32 val, ch_reg, imr_reg;
+
+ switch (cap_channel) {
+ case CAP_CHANNEL1:
+ imr_reg = mmACP_I2SMICSP_IMR1;
+ ch_reg = mmACP_I2SMICSP_RER1;
+ break;
+ case CAP_CHANNEL0:
+ default:
+ imr_reg = mmACP_I2SMICSP_IMR0;
+ ch_reg = mmACP_I2SMICSP_RER0;
+ break;
+ }
+ val = acp_reg_read(acp_mmio, imr_reg);
+ val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK;
+ val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK;
+ acp_reg_write(val, acp_mmio, imr_reg);
+ acp_reg_write(0x0, acp_mmio, ch_reg);
}
/* Start a given DMA channel transfer */
-static void acp_dma_start(void __iomem *acp_mmio,
- u16 ch_num, bool is_circular)
+static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num, bool is_circular)
{
u32 dma_ctrl;
@@ -303,7 +421,8 @@ static void acp_dma_start(void __iomem *acp_mmio,
/* Invalidating the DAGB cache */
acp_reg_write(1, acp_mmio, mmACP_DAGB_ATU_CTRL);
- /* configure the DMA channel and start the DMA transfer
+ /*
+ * configure the DMA channel and start the DMA transfer
* set dmachrun bit to start the transfer and enable the
* interrupt on completion of the dma transfer
*/
@@ -311,8 +430,10 @@ static void acp_dma_start(void __iomem *acp_mmio,
switch (ch_num) {
case ACP_TO_I2S_DMA_CH_NUM:
- case ACP_TO_SYSRAM_CH_NUM:
case I2S_TO_ACP_DMA_CH_NUM:
+ case ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM:
+ case I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM:
+ case ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM:
dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
break;
default:
@@ -320,7 +441,7 @@ static void acp_dma_start(void __iomem *acp_mmio,
break;
}
- /* enable for ACP SRAM to/from I2S DMA channel */
+ /* enable for ACP to SRAM DMA channel */
if (is_circular == true)
dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK;
else
@@ -338,9 +459,10 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
- /* clear the dma control register fields before writing zero
+ /*
+ * clear the dma control register fields before writing zero
* in reset bit
- */
+ */
dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRun_MASK;
dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
@@ -348,9 +470,10 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS);
if (dma_ch_sts & BIT(ch_num)) {
- /* set the reset bit for this channel to stop the dma
- * transfer
- */
+ /*
+ * set the reset bit for this channel to stop the dma
+ * transfer
+ */
dma_ctrl |= ACP_DMA_CNTL_0__DMAChRst_MASK;
acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
}
@@ -359,13 +482,14 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
while (true) {
dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS);
if (!(dma_ch_sts & BIT(ch_num))) {
- /* clear the reset flag after successfully stopping
- * the dma transfer and break from the loop
- */
+ /*
+ * clear the reset flag after successfully stopping
+ * the dma transfer and break from the loop
+ */
dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0
- + ch_num);
+ + ch_num);
break;
}
if (--count == 0) {
@@ -378,7 +502,7 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
}
static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank,
- bool power_on)
+ bool power_on)
{
u32 val, req_reg, sts_reg, sts_reg_mask;
u32 loops = 1000;
@@ -425,7 +549,7 @@ static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank,
}
/* Initialize and bring ACP hardware to default state. */
-static int acp_init(void __iomem *acp_mmio)
+static int acp_init(void __iomem *acp_mmio, u32 asic_type)
{
u16 bank;
u32 val, count, sram_pte_offset;
@@ -458,7 +582,7 @@ static int acp_init(void __iomem *acp_mmio)
while (true) {
val = acp_reg_read(acp_mmio, mmACP_STATUS);
- if (val & (u32) 0x1)
+ if (val & (u32)0x1)
break;
if (--count == 0) {
pr_err("Failed to reset ACP\n");
@@ -472,13 +596,20 @@ static int acp_init(void __iomem *acp_mmio)
val &= ~ACP_SOFT_RESET__SoftResetAud_MASK;
acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
- /* initiailize Onion control DAGB register */
+ /* For BT instance change pins from UART to BT */
+ if (!acp_bt_uart_enable) {
+ val = acp_reg_read(acp_mmio, mmACP_BT_UART_PAD_SEL);
+ val |= ACP_BT_UART_PAD_SELECT_MASK;
+ acp_reg_write(val, acp_mmio, mmACP_BT_UART_PAD_SEL);
+ }
+
+ /* initialize Onion control DAGB register */
acp_reg_write(ACP_ONION_CNTL_DEFAULT, acp_mmio,
- mmACP_AXI2DAGB_ONION_CNTL);
+ mmACP_AXI2DAGB_ONION_CNTL);
- /* initiailize Garlic control DAGB registers */
+ /* initialize Garlic control DAGB registers */
acp_reg_write(ACP_GARLIC_CNTL_DEFAULT, acp_mmio,
- mmACP_AXI2DAGB_GARLIC_CNTL);
+ mmACP_AXI2DAGB_GARLIC_CNTL);
sram_pte_offset = ACP_DAGB_GRP_SRAM_BASE_ADDRESS |
ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel_MASK |
@@ -486,27 +617,32 @@ static int acp_init(void __iomem *acp_mmio)
ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBGrpEnable_MASK;
acp_reg_write(sram_pte_offset, acp_mmio, mmACP_DAGB_BASE_ADDR_GRP_1);
acp_reg_write(ACP_PAGE_SIZE_4K_ENABLE, acp_mmio,
- mmACP_DAGB_PAGE_SIZE_GRP_1);
+ mmACP_DAGB_PAGE_SIZE_GRP_1);
acp_reg_write(ACP_SRAM_BASE_ADDRESS, acp_mmio,
- mmACP_DMA_DESC_BASE_ADDR);
+ mmACP_DMA_DESC_BASE_ADDR);
- /* Num of descriptiors in SRAM 0x4, means 256 descriptors;(64 * 4) */
+ /* Num of descriptors in SRAM 0x4, means 256 descriptors;(64 * 4) */
acp_reg_write(0x4, acp_mmio, mmACP_DMA_DESC_MAX_NUM_DSCR);
acp_reg_write(ACP_EXTERNAL_INTR_CNTL__DMAIOCMask_MASK,
- acp_mmio, mmACP_EXTERNAL_INTR_CNTL);
+ acp_mmio, mmACP_EXTERNAL_INTR_CNTL);
- /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on.
+ /*
+ * When ACP_TILE_P1 is turned on, all SRAM banks get turned on.
* Now, turn off all of them. This can't be done in 'poweron' of
* ACP pm domain, as this requires ACP to be initialized.
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
*/
- for (bank = 1; bank < 48; bank++)
- acp_set_sram_bank_state(acp_mmio, bank, false);
-
+ if (asic_type != CHIP_STONEY) {
+ for (bank = 1; bank < 48; bank++)
+ acp_set_sram_bank_state(acp_mmio, bank, false);
+ }
return 0;
}
-/* Deintialize ACP */
+/* Deinitialize ACP */
static int acp_deinit(void __iomem *acp_mmio)
{
u32 val;
@@ -530,7 +666,7 @@ static int acp_deinit(void __iomem *acp_mmio)
}
udelay(100);
}
- /** Disable ACP clock */
+ /* Disable ACP clock */
val = acp_reg_read(acp_mmio, mmACP_CONTROL);
val &= ~ACP_CONTROL__ClkEn_MASK;
acp_reg_write(val, acp_mmio, mmACP_CONTROL);
@@ -539,7 +675,7 @@ static int acp_deinit(void __iomem *acp_mmio)
while (true) {
val = acp_reg_read(acp_mmio, mmACP_STATUS);
- if (!(val & (u32) 0x1))
+ if (!(val & (u32)0x1))
break;
if (--count == 0) {
pr_err("Failed to reset ACP\n");
@@ -570,24 +706,29 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
if ((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) != 0) {
valid_irq = true;
- if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) ==
- PLAYBACK_START_DMA_DESCR_CH13)
- dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
- else
- dscr_idx = PLAYBACK_END_DMA_DESCR_CH12;
- config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx,
- 1, 0);
- acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
+ snd_pcm_period_elapsed(irq_data->play_i2ssp_stream);
+ acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
- snd_pcm_period_elapsed(irq_data->play_stream);
+ if ((intr_flag & BIT(ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM)) != 0) {
+ valid_irq = true;
+ snd_pcm_period_elapsed(irq_data->play_i2s_micsp_stream);
+ acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
- acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) << 16,
- acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ if ((intr_flag & BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) != 0) {
+ valid_irq = true;
+ snd_pcm_period_elapsed(irq_data->play_i2sbt_stream);
+ acp_reg_write((intr_flag &
+ BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
}
if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) {
valid_irq = true;
- if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_15) ==
+ if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_14) ==
CAPTURE_START_DMA_DESCR_CH15)
dscr_idx = CAPTURE_END_DMA_DESCR_CH14;
else
@@ -596,15 +737,28 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
1, 0);
acp_dma_start(acp_mmio, ACP_TO_SYSRAM_CH_NUM, false);
+ snd_pcm_period_elapsed(irq_data->capture_i2ssp_stream);
acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) << 16,
- acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
}
- if ((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) != 0) {
+ if ((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) != 0) {
valid_irq = true;
- snd_pcm_period_elapsed(irq_data->capture_stream);
- acp_reg_write((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) << 16,
- acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_10) ==
+ CAPTURE_START_DMA_DESCR_CH11)
+ dscr_idx = CAPTURE_END_DMA_DESCR_CH10;
+ else
+ dscr_idx = CAPTURE_START_DMA_DESCR_CH10;
+ config_acp_dma_channel(acp_mmio,
+ ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM,
+ dscr_idx, 1, 0);
+ acp_dma_start(acp_mmio, ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM,
+ false);
+
+ snd_pcm_period_elapsed(irq_data->capture_i2sbt_stream);
+ acp_reg_write((intr_flag &
+ BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
}
if (valid_irq)
@@ -613,68 +767,92 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
return IRQ_NONE;
}
-static int acp_dma_open(struct snd_pcm_substream *substream)
+static int acp_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u16 bank;
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct audio_drv_data *intr_data = dev_get_drvdata(prtd->platform->dev);
-
+ struct audio_drv_data *intr_data = dev_get_drvdata(component->dev);
struct audio_substream_data *adata =
kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL);
- if (adata == NULL)
+ if (!adata)
return -ENOMEM;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- runtime->hw = acp_pcm_hardware_playback;
- else
- runtime->hw = acp_pcm_hardware_capture;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (intr_data->asic_type) {
+ case CHIP_STONEY:
+ runtime->hw = acp_st_pcm_hardware_playback;
+ break;
+ default:
+ runtime->hw = acp_pcm_hardware_playback;
+ }
+ } else {
+ switch (intr_data->asic_type) {
+ case CHIP_STONEY:
+ runtime->hw = acp_st_pcm_hardware_capture;
+ break;
+ default:
+ runtime->hw = acp_pcm_hardware_capture;
+ }
+ }
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
- dev_err(prtd->platform->dev, "set integer constraint failed\n");
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(adata);
return ret;
}
adata->acp_mmio = intr_data->acp_mmio;
runtime->private_data = adata;
- /* Enable ACP irq, when neither playback or capture streams are
+ /*
+ * Enable ACP irq, when neither playback or capture streams are
* active by the time when a new stream is being opened.
* This enablement is not required for another stream, if current
* stream is not closed
- */
- if (!intr_data->play_stream && !intr_data->capture_stream)
+ */
+ if (!intr_data->play_i2ssp_stream && !intr_data->capture_i2ssp_stream &&
+ !intr_data->play_i2sbt_stream && !intr_data->capture_i2sbt_stream &&
+ !intr_data->play_i2s_micsp_stream)
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- intr_data->play_stream = substream;
- for (bank = 1; bank <= 4; bank++)
- acp_set_sram_bank_state(intr_data->acp_mmio, bank,
- true);
+ /*
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (intr_data->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(intr_data->acp_mmio,
+ bank, true);
+ }
} else {
- intr_data->capture_stream = substream;
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(intr_data->acp_mmio, bank,
- true);
+ if (intr_data->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(intr_data->acp_mmio,
+ bank, true);
+ }
}
return 0;
}
-static int acp_dma_hw_params(struct snd_pcm_substream *substream,
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- int status;
uint64_t size;
- struct snd_dma_buffer *dma_buffer;
- struct page *pg;
+ u32 val = 0;
struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd;
-
- dma_buffer = &substream->dma_buffer;
+ struct snd_soc_pcm_runtime *prtd = snd_soc_substream_to_rtd(substream);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ struct snd_soc_card *card = prtd->card;
+ struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card);
runtime = substream->runtime;
rtd = runtime->private_data;
@@ -682,129 +860,262 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
if (WARN_ON(!rtd))
return -EINVAL;
- size = params_buffer_bytes(params);
- status = snd_pcm_lib_malloc_pages(substream, size);
- if (status < 0)
- return status;
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ rtd->capture_channel = pinfo->capture_channel;
+ }
+ }
+ if (adata->asic_type == CHIP_STONEY) {
+ val = acp_reg_read(adata->acp_mmio,
+ mmACP_I2S_16BIT_RESOLUTION_EN);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val |= ACP_I2S_BT_16BIT_RESOLUTION_EN;
+ break;
+ case I2S_MICSP_INSTANCE:
+ val |= ACP_I2S_MICSP_16BIT_RESOLUTION_EN;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val |= ACP_I2S_SP_16BIT_RESOLUTION_EN;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val |= ACP_I2S_BT_16BIT_RESOLUTION_EN;
+ break;
+ case I2S_MICSP_INSTANCE:
+ case I2S_SP_INSTANCE:
+ default:
+ val |= ACP_I2S_MIC_16BIT_RESOLUTION_EN;
+ }
+ }
+ acp_reg_write(val, adata->acp_mmio,
+ mmACP_I2S_16BIT_RESOLUTION_EN);
+ }
- memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
- pg = virt_to_page(substream->dma_buffer.area);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ rtd->pte_offset = ACP_ST_BT_PLAYBACK_PTE_OFFSET;
+ rtd->ch1 = SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_3_ADDRESS;
+ rtd->destination = TO_BLUETOOTH;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH8;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH9;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW;
+ adata->play_i2sbt_stream = substream;
+ break;
+ case I2S_MICSP_INSTANCE:
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_PLAYBACK_PTE_OFFSET;
+ break;
+ default:
+ rtd->pte_offset = ACP_PLAYBACK_PTE_OFFSET;
+ }
+ rtd->ch1 = SYSRAM_TO_ACP_MICSP_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_1_ADDRESS;
+ rtd->destination = TO_ACP_I2S_2;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH4;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH5;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW;
+
+ adata->play_i2s_micsp_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_PLAYBACK_PTE_OFFSET;
+ break;
+ default:
+ rtd->pte_offset = ACP_PLAYBACK_PTE_OFFSET;
+ }
+ rtd->ch1 = SYSRAM_TO_ACP_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_1_ADDRESS;
+ rtd->destination = TO_ACP_I2S_1;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH12;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH13;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_TRANSMIT_BYTE_CNT_LOW;
+ adata->play_i2ssp_stream = substream;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ rtd->pte_offset = ACP_ST_BT_CAPTURE_PTE_OFFSET;
+ rtd->ch1 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_4_ADDRESS;
+ rtd->destination = FROM_BLUETOOTH;
+ rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH10;
+ rtd->dma_dscr_idx_2 = CAPTURE_START_DMA_DESCR_CH11;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW;
+ rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_11;
+ adata->capture_i2sbt_stream = substream;
+ break;
+ case I2S_MICSP_INSTANCE:
+ case I2S_SP_INSTANCE:
+ default:
+ rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET;
+ rtd->ch1 = I2S_TO_ACP_DMA_CH_NUM;
+ rtd->ch2 = ACP_TO_SYSRAM_CH_NUM;
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_CAPTURE_PTE_OFFSET;
+ rtd->sram_bank = ACP_SRAM_BANK_2_ADDRESS;
+ break;
+ default:
+ rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET;
+ rtd->sram_bank = ACP_SRAM_BANK_5_ADDRESS;
+ }
+ rtd->destination = FROM_ACP_I2S_1;
+ rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH14;
+ rtd->dma_dscr_idx_2 = CAPTURE_START_DMA_DESCR_CH15;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_RECEIVED_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_RECEIVED_BYTE_CNT_LOW;
+ rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_15;
+ adata->capture_i2ssp_stream = substream;
+ }
+ }
- if (pg != NULL) {
- acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
- /* Save for runtime private data */
- rtd->pg = pg;
- rtd->order = get_order(size);
+ size = params_buffer_bytes(params);
- /* Fill the page table entries in ACP SRAM */
- rtd->pg = pg;
- rtd->size = size;
- rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
- rtd->direction = substream->stream;
+ acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
+ /* Save for runtime private data */
+ rtd->dma_addr = runtime->dma_addr;
+ rtd->order = get_order(size);
- config_acp_dma(rtd->acp_mmio, rtd);
- status = 0;
- } else {
- status = -ENOMEM;
- }
- return status;
+ /* Fill the page table entries in ACP SRAM */
+ rtd->size = size;
+ rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ rtd->direction = substream->stream;
+
+ config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
+ return 0;
}
-static int acp_dma_hw_free(struct snd_pcm_substream *substream)
+static u64 acp_get_byte_count(struct audio_substream_data *rtd)
{
- return snd_pcm_lib_free_pages(substream);
+ union acp_dma_count byte_count;
+
+ byte_count.bcount.high = acp_reg_read(rtd->acp_mmio,
+ rtd->byte_cnt_high_reg_offset);
+ byte_count.bcount.low = acp_reg_read(rtd->acp_mmio,
+ rtd->byte_cnt_low_reg_offset);
+ return byte_count.bytescount;
}
-static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- u16 dscr;
- u32 mul, dma_config, period_bytes;
+ u32 buffersize;
u32 pos = 0;
+ u64 bytescount = 0;
+ u16 dscr;
+ u32 period_bytes, delay;
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
- period_bytes = frames_to_bytes(runtime, runtime->period_size);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13);
+ if (!rtd)
+ return -EINVAL;
- if (dscr == PLAYBACK_START_DMA_DESCR_CH13)
- mul = 0;
- else
- mul = 1;
- pos = (mul * period_bytes);
- } else {
- dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14);
- if (dma_config != 0) {
- dscr = acp_reg_read(rtd->acp_mmio,
- mmACP_DMA_CUR_DSCR_14);
- if (dscr == CAPTURE_START_DMA_DESCR_CH14)
- mul = 1;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ bytescount = acp_get_byte_count(rtd);
+ if (bytescount >= rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ if (bytescount < period_bytes) {
+ pos = 0;
+ } else {
+ dscr = acp_reg_read(rtd->acp_mmio, rtd->dma_curr_dscr);
+ if (dscr == rtd->dma_dscr_idx_1)
+ pos = period_bytes;
else
- mul = 2;
- pos = (mul * period_bytes);
+ pos = 0;
}
-
- if (pos >= (2 * period_bytes))
- pos = 0;
-
+ if (bytescount > 0) {
+ delay = do_div(bytescount, period_bytes);
+ adata->delay += bytes_to_frames(runtime, delay);
+ }
+ } else {
+ buffersize = frames_to_bytes(runtime, runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
}
return bytes_to_frames(runtime, pos);
}
-static int acp_dma_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- return snd_pcm_lib_default_mmap(substream, vma);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ snd_pcm_sframes_t delay = adata->delay;
+
+ adata->delay = 0;
+
+ return delay;
}
-static int acp_dma_prepare(struct snd_pcm_substream *substream)
+static int acp_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
+ u16 ch_acp_sysmem, ch_acp_i2s;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH12,
- NUM_DSCRS_PER_CHANNEL, 0);
- config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH13,
- NUM_DSCRS_PER_CHANNEL, 0);
- /* Fill ACP SRAM (2 periods) with zeros from System RAM
- * which is zero-ed in hw_params
- */
- acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
-
- /* ACP SRAM (2 periods of buffer size) is intially filled with
- * zeros. Before rendering starts, 2nd half of SRAM will be
- * filled with valid audio data DMA'ed from first half of system
- * RAM and 1st half of SRAM will be filled with Zeros. This is
- * the initial scenario when redering starts from SRAM. Later
- * on, 2nd half of system memory will be DMA'ed to 1st half of
- * SRAM, 1st half of system memory will be DMA'ed to 2nd half of
- * SRAM in ping-pong way till rendering stops.
- */
- config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH12,
- 1, 0);
+ if (!rtd)
+ return -EINVAL;
+
+ if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_acp_sysmem = rtd->ch1;
+ ch_acp_i2s = rtd->ch2;
} else {
- config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH14,
- NUM_DSCRS_PER_CHANNEL, 0);
- config_acp_dma_channel(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM,
- CAPTURE_START_DMA_DESCR_CH15,
- NUM_DSCRS_PER_CHANNEL, 0);
+ ch_acp_i2s = rtd->ch1;
+ ch_acp_sysmem = rtd->ch2;
}
+ config_acp_dma_channel(rtd->acp_mmio,
+ ch_acp_sysmem,
+ rtd->dma_dscr_idx_1,
+ NUM_DSCRS_PER_CHANNEL, 0);
+ config_acp_dma_channel(rtd->acp_mmio,
+ ch_acp_i2s,
+ rtd->dma_dscr_idx_2,
+ NUM_DSCRS_PER_CHANNEL, 0);
return 0;
}
-static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int acp_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret;
- u32 loops = 1000;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct audio_substream_data *rtd = runtime->private_data;
if (!rtd)
@@ -813,136 +1124,177 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- acp_dma_start(rtd->acp_mmio,
- SYSRAM_TO_ACP_CH_NUM, false);
- while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) &
- BIT(SYSRAM_TO_ACP_CH_NUM)) {
- if (!loops--) {
- dev_err(prtd->platform->dev,
- "acp dma start timeout\n");
- return -ETIMEDOUT;
- }
- cpu_relax();
+ rtd->bytescount = acp_get_byte_count(rtd);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (rtd->capture_channel == CAP_CHANNEL0) {
+ acp_dma_cap_channel_disable(rtd->acp_mmio,
+ CAP_CHANNEL1);
+ acp_dma_cap_channel_enable(rtd->acp_mmio,
+ CAP_CHANNEL0);
}
-
- acp_dma_start(rtd->acp_mmio,
- ACP_TO_I2S_DMA_CH_NUM, true);
-
+ if (rtd->capture_channel == CAP_CHANNEL1) {
+ acp_dma_cap_channel_disable(rtd->acp_mmio,
+ CAP_CHANNEL0);
+ acp_dma_cap_channel_enable(rtd->acp_mmio,
+ CAP_CHANNEL1);
+ }
+ acp_dma_start(rtd->acp_mmio, rtd->ch1, true);
} else {
- acp_dma_start(rtd->acp_mmio,
- I2S_TO_ACP_DMA_CH_NUM, true);
+ acp_dma_start(rtd->acp_mmio, rtd->ch1, true);
+ acp_dma_start(rtd->acp_mmio, rtd->ch2, true);
}
ret = 0;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
- /* Need to stop only circular DMA channels :
- * ACP_TO_I2S_DMA_CH_NUM / I2S_TO_ACP_DMA_CH_NUM. Non-circular
- * channels will stopped automatically after its transfer
- * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ret = acp_dma_stop(rtd->acp_mmio,
- ACP_TO_I2S_DMA_CH_NUM);
- else
- ret = acp_dma_stop(rtd->acp_mmio,
- I2S_TO_ACP_DMA_CH_NUM);
+ acp_dma_stop(rtd->acp_mmio, rtd->ch2);
+ ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1);
break;
default:
ret = -EINVAL;
-
}
return ret;
}
-static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
- SNDRV_DMA_TYPE_DEV,
- NULL, MIN_BUFFER,
- MAX_BUFFER);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ struct device *parent = component->dev->parent;
+
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ parent,
+ ST_MIN_BUFFER,
+ ST_MAX_BUFFER);
+ break;
+ default:
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ parent,
+ MIN_BUFFER,
+ MAX_BUFFER);
+ break;
+ }
+ return 0;
}
-static int acp_dma_close(struct snd_pcm_substream *substream)
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
u16 bank;
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
- struct snd_soc_pcm_runtime *prtd = substream->private_data;
- struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev);
-
- kfree(rtd);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- adata->play_stream = NULL;
- for (bank = 1; bank <= 4; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- false);
- } else {
- adata->capture_stream = NULL;
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- false);
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->play_i2sbt_stream = NULL;
+ break;
+ case I2S_MICSP_INSTANCE:
+ adata->play_i2s_micsp_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->play_i2ssp_stream = NULL;
+ /*
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks
+ * is ON.Setting SRAM bank state code skipped for STONEY
+ * platform. Added condition checks for Carrizo platform
+ * only.
+ */
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio,
+ bank, false);
+ }
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->capture_i2sbt_stream = NULL;
+ break;
+ case I2S_MICSP_INSTANCE:
+ case I2S_SP_INSTANCE:
+ default:
+ adata->capture_i2ssp_stream = NULL;
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio,
+ bank, false);
+ }
+ }
}
- /* Disable ACP irq, when the current stream is being closed and
+ /*
+ * Disable ACP irq, when the current stream is being closed and
* another stream is also not active.
- */
- if (!adata->play_stream && !adata->capture_stream)
+ */
+ if (!adata->play_i2ssp_stream && !adata->capture_i2ssp_stream &&
+ !adata->play_i2sbt_stream && !adata->capture_i2sbt_stream &&
+ !adata->play_i2s_micsp_stream)
acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
-
+ kfree(rtd);
return 0;
}
-static struct snd_pcm_ops acp_dma_ops = {
- .open = acp_dma_open,
- .close = acp_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = acp_dma_hw_params,
- .hw_free = acp_dma_hw_free,
- .trigger = acp_dma_trigger,
- .pointer = acp_dma_pointer,
- .mmap = acp_dma_mmap,
- .prepare = acp_dma_prepare,
-};
-
-static struct snd_soc_platform_driver acp_asoc_platform = {
- .ops = &acp_dma_ops,
- .pcm_new = acp_dma_new,
+static const struct snd_soc_component_driver acp_asoc_platform = {
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .hw_params = acp_dma_hw_params,
+ .trigger = acp_dma_trigger,
+ .pointer = acp_dma_pointer,
+ .delay = acp_dma_delay,
+ .prepare = acp_dma_prepare,
+ .pcm_construct = acp_dma_new,
};
static int acp_audio_probe(struct platform_device *pdev)
{
- int status;
+ int status, irq;
struct audio_drv_data *audio_drv_data;
- struct resource *res;
+ const u32 *pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Missing platform data\n");
+ return -ENODEV;
+ }
audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
- GFP_KERNEL);
- if (audio_drv_data == NULL)
+ GFP_KERNEL);
+ if (!audio_drv_data)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res);
+ audio_drv_data->acp_mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(audio_drv_data->acp_mmio))
+ return PTR_ERR(audio_drv_data->acp_mmio);
- /* The following members gets populated in device 'open'
+ /*
+ * The following members gets populated in device 'open'
* function. Till then interrupts are disabled in 'acp_init'
* and device doesn't generate any interrupts.
*/
- audio_drv_data->play_stream = NULL;
- audio_drv_data->capture_stream = NULL;
+ audio_drv_data->play_i2ssp_stream = NULL;
+ audio_drv_data->capture_i2ssp_stream = NULL;
+ audio_drv_data->play_i2sbt_stream = NULL;
+ audio_drv_data->capture_i2sbt_stream = NULL;
+ audio_drv_data->play_i2s_micsp_stream = NULL;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+ audio_drv_data->asic_type = *pdata;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
return -ENODEV;
- }
- status = devm_request_irq(&pdev->dev, res->start, dma_irq_handler,
- 0, "ACP_IRQ", &pdev->dev);
+ status = devm_request_irq(&pdev->dev, irq, dma_irq_handler,
+ 0, "ACP_IRQ", &pdev->dev);
if (status) {
dev_err(&pdev->dev, "ACP IRQ request failed\n");
return status;
@@ -951,9 +1303,14 @@ static int acp_audio_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, audio_drv_data);
/* Initialize the ACP */
- acp_init(audio_drv_data->acp_mmio);
+ status = acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type);
+ if (status) {
+ dev_err(&pdev->dev, "ACP Init failed status:%d\n", status);
+ return status;
+ }
- status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp_asoc_platform, NULL, 0);
if (status != 0) {
dev_err(&pdev->dev, "Fail to register ALSA platform device\n");
return status;
@@ -966,37 +1323,70 @@ static int acp_audio_probe(struct platform_device *pdev)
return status;
}
-static int acp_audio_remove(struct platform_device *pdev)
+static void acp_audio_remove(struct platform_device *pdev)
{
+ int status;
struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev);
- acp_deinit(adata->acp_mmio);
- snd_soc_unregister_platform(&pdev->dev);
+ status = acp_deinit(adata->acp_mmio);
+ if (status)
+ dev_err(&pdev->dev, "ACP Deinit failed status:%d\n", status);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static int acp_pcm_resume(struct device *dev)
{
u16 bank;
+ int status;
+ struct audio_substream_data *rtd;
struct audio_drv_data *adata = dev_get_drvdata(dev);
- acp_init(adata->acp_mmio);
+ status = acp_init(adata->acp_mmio, adata->asic_type);
+ if (status) {
+ dev_err(dev, "ACP Init failed status:%d\n", status);
+ return status;
+ }
- if (adata->play_stream && adata->play_stream->runtime) {
- for (bank = 1; bank <= 4; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- true);
- config_acp_dma(adata->acp_mmio,
- adata->play_stream->runtime->private_data);
+ if (adata->play_i2ssp_stream && adata->play_i2ssp_stream->runtime) {
+ /*
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
+ true);
+ }
+ rtd = adata->play_i2ssp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
}
- if (adata->capture_stream && adata->capture_stream->runtime) {
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- true);
- config_acp_dma(adata->acp_mmio,
- adata->capture_stream->runtime->private_data);
+ if (adata->capture_i2ssp_stream &&
+ adata->capture_i2ssp_stream->runtime) {
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
+ true);
+ }
+ rtd = adata->capture_i2ssp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->asic_type != CHIP_CARRIZO) {
+ if (adata->play_i2s_micsp_stream &&
+ adata->play_i2s_micsp_stream->runtime) {
+ rtd = adata->play_i2s_micsp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->play_i2sbt_stream &&
+ adata->play_i2sbt_stream->runtime) {
+ rtd = adata->play_i2sbt_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
+ if (adata->capture_i2sbt_stream &&
+ adata->capture_i2sbt_stream->runtime) {
+ rtd = adata->capture_i2sbt_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
}
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0;
@@ -1004,18 +1394,26 @@ static int acp_pcm_resume(struct device *dev)
static int acp_pcm_runtime_suspend(struct device *dev)
{
+ int status;
struct audio_drv_data *adata = dev_get_drvdata(dev);
- acp_deinit(adata->acp_mmio);
+ status = acp_deinit(adata->acp_mmio);
+ if (status)
+ dev_err(dev, "ACP Deinit failed status:%d\n", status);
acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0;
}
static int acp_pcm_runtime_resume(struct device *dev)
{
+ int status;
struct audio_drv_data *adata = dev_get_drvdata(dev);
- acp_init(adata->acp_mmio);
+ status = acp_init(adata->acp_mmio, adata->asic_type);
+ if (status) {
+ dev_err(dev, "ACP Init failed status:%d\n", status);
+ return status;
+ }
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0;
}
@@ -1030,14 +1428,15 @@ static struct platform_driver acp_dma_driver = {
.probe = acp_audio_probe,
.remove = acp_audio_remove,
.driver = {
- .name = "acp_audio_dma",
+ .name = DRV_NAME,
.pm = &acp_pm_ops,
},
};
module_platform_driver(acp_dma_driver);
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
MODULE_DESCRIPTION("AMD ACP PCM Driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:acp-dma-audio");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
new file mode 100644
index 000000000000..11d373169380
--- /dev/null
+++ b/sound/soc/amd/acp-rt5645.c
@@ -0,0 +1,218 @@
+/*
+ * Machine driver for AMD ACP Audio engine using Realtek RT5645 codec
+ *
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * This file is modified from rt288 machine driver
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ */
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+
+#include "../codecs/rt5645.h"
+
+#define CZ_PLAT_CLK 24000000
+
+static struct snd_soc_jack cz_jack;
+static struct snd_soc_jack_pin cz_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int cz_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
+ CZ_PLAT_CLK, params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
+ params_rate(params) * 512, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cz_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct snd_soc_component *codec;
+
+ codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+ card = rtd->card;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ rt5645_set_jack_detect(codec, &cz_jack, &cz_jack, &cz_jack);
+
+ return 0;
+}
+
+static const struct snd_soc_ops cz_aif1_ops = {
+ .hw_params = cz_aif1_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
+SND_SOC_DAILINK_DEF(designware2,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
+
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
+
+static struct snd_soc_dai_link cz_dai_rt5650[] = {
+ {
+ .name = "amd-rt5645-play",
+ .stream_name = "RT5645_AIF1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_init,
+ .ops = &cz_aif1_ops,
+ SND_SOC_DAILINK_REG(designware1, codec, platform),
+ },
+ {
+ .name = "amd-rt5645-cap",
+ .stream_name = "RT5645_AIF1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &cz_aif1_ops,
+ SND_SOC_DAILINK_REG(designware2, codec, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget cz_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route cz_audio_route[] = {
+ {"Headphones", NULL, "HPOL"},
+ {"Headphones", NULL, "HPOR"},
+ {"RECMIXL", NULL, "Headset Mic"},
+ {"RECMIXR", NULL, "Headset Mic"},
+ {"Speakers", NULL, "SPOL"},
+ {"Speakers", NULL, "SPOR"},
+ {"DMIC L2", NULL, "Int Mic"},
+ {"DMIC R2", NULL, "Int Mic"},
+};
+
+static const struct snd_kcontrol_new cz_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static struct snd_soc_card cz_card = {
+ .name = "acprt5650",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_rt5650,
+ .num_links = ARRAY_SIZE(cz_dai_rt5650),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(cz_audio_route),
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+static int cz_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+
+ card = &cz_card;
+ cz_card.dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ cz_card.name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cz_audio_acpi_match[] = {
+ { "AMDI1002", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
+#endif
+
+static struct platform_driver cz_pcm_driver = {
+ .driver = {
+ .name = "cz-rt5645",
+ .acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = cz_probe,
+};
+
+module_platform_driver(cz_pcm_driver);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_DESCRIPTION("cz-rt5645 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
index 330832ef4e5e..b29bef90f886 100644
--- a/sound/soc/amd/acp.h
+++ b/sound/soc/amd/acp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ACP_HW_H
#define __ACP_HW_H
@@ -9,16 +10,30 @@
#define ACP_PLAYBACK_PTE_OFFSET 10
#define ACP_CAPTURE_PTE_OFFSET 0
+/* Playback and Capture Offset for Stoney */
+#define ACP_ST_PLAYBACK_PTE_OFFSET 0x04
+#define ACP_ST_CAPTURE_PTE_OFFSET 0x00
+#define ACP_ST_BT_PLAYBACK_PTE_OFFSET 0x08
+#define ACP_ST_BT_CAPTURE_PTE_OFFSET 0x0c
+
#define ACP_GARLIC_CNTL_DEFAULT 0x00000FB4
#define ACP_ONION_CNTL_DEFAULT 0x00000FB4
#define ACP_PHYSICAL_BASE 0x14000
-/* Playback SRAM address (as a destination in dma descriptor) */
-#define ACP_SHARED_RAM_BANK_1_ADDRESS 0x4002000
-
-/* Capture SRAM address (as a source in dma descriptor) */
-#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000
+/*
+ * In case of I2S SP controller instance, Stoney uses SRAM bank 1 for
+ * playback and SRAM Bank 2 for capture where as in case of BT I2S
+ * Instance, Stoney uses SRAM Bank 3 for playback & SRAM Bank 4 will
+ * be used for capture. Carrizo uses I2S SP controller instance. SRAM Banks
+ * 1, 2, 3, 4 will be used for playback & SRAM Banks 5, 6, 7, 8 will be used
+ * for capture scenario.
+ */
+#define ACP_SRAM_BANK_1_ADDRESS 0x4002000
+#define ACP_SRAM_BANK_2_ADDRESS 0x4004000
+#define ACP_SRAM_BANK_3_ADDRESS 0x4006000
+#define ACP_SRAM_BANK_4_ADDRESS 0x4008000
+#define ACP_SRAM_BANK_5_ADDRESS 0x400A000
#define ACP_DMA_RESET_TIME 10000
#define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF
@@ -33,8 +48,16 @@
#define TO_ACP_I2S_1 0x2
#define TO_ACP_I2S_2 0x4
+#define TO_BLUETOOTH 0x3
#define FROM_ACP_I2S_1 0xa
#define FROM_ACP_I2S_2 0xb
+#define FROM_BLUETOOTH 0xb
+
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+#define I2S_MICSP_INSTANCE 0x03
+#define CAP_CHANNEL0 0x00
+#define CAP_CHANNEL1 0x01
#define ACP_TILE_ON_MASK 0x03
#define ACP_TILE_OFF_MASK 0x02
@@ -52,8 +75,20 @@
#define ACP_TO_I2S_DMA_CH_NUM 13
/* Capture DMA channels */
-#define ACP_TO_SYSRAM_CH_NUM 14
-#define I2S_TO_ACP_DMA_CH_NUM 15
+#define I2S_TO_ACP_DMA_CH_NUM 14
+#define ACP_TO_SYSRAM_CH_NUM 15
+
+/* Playback DMA Channels for I2S BT instance */
+#define SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM 8
+#define ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM 9
+
+/* Capture DMA Channels for I2S BT Instance */
+#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 10
+#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 11
+
+/* Playback DMA channels for I2S MICSP instance */
+#define SYSRAM_TO_ACP_MICSP_INSTANCE_CH_NUM 4
+#define ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM 5
#define NUM_DSCRS_PER_CHANNEL 2
@@ -67,6 +102,30 @@
#define CAPTURE_START_DMA_DESCR_CH15 6
#define CAPTURE_END_DMA_DESCR_CH15 7
+/* I2S BT Instance DMA Descriptors */
+#define PLAYBACK_START_DMA_DESCR_CH8 8
+#define PLAYBACK_END_DMA_DESCR_CH8 9
+#define PLAYBACK_START_DMA_DESCR_CH9 10
+#define PLAYBACK_END_DMA_DESCR_CH9 11
+
+#define CAPTURE_START_DMA_DESCR_CH10 12
+#define CAPTURE_END_DMA_DESCR_CH10 13
+#define CAPTURE_START_DMA_DESCR_CH11 14
+#define CAPTURE_END_DMA_DESCR_CH11 15
+
+/* I2S MICSP Instance DMA Descriptors */
+#define PLAYBACK_START_DMA_DESCR_CH4 0
+#define PLAYBACK_END_DMA_DESCR_CH4 1
+#define PLAYBACK_START_DMA_DESCR_CH5 2
+#define PLAYBACK_END_DMA_DESCR_CH5 3
+
+#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209
+#define ACP_I2S_MIC_16BIT_RESOLUTION_EN 0x01
+#define ACP_I2S_MICSP_16BIT_RESOLUTION_EN 0x01
+#define ACP_I2S_SP_16BIT_RESOLUTION_EN 0x02
+#define ACP_I2S_BT_16BIT_RESOLUTION_EN 0x04
+#define ACP_BT_UART_PAD_SELECT_MASK 0x1
+
enum acp_dma_priority_level {
/* 0x0 Specifies the DMA channel is given normal priority */
ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
@@ -76,14 +135,56 @@ enum acp_dma_priority_level {
};
struct audio_substream_data {
- struct page *pg;
+ dma_addr_t dma_addr;
unsigned int order;
u16 num_of_pages;
+ u16 i2s_instance;
+ u16 capture_channel;
u16 direction;
+ u16 ch1;
+ u16 ch2;
+ u16 destination;
+ u16 dma_dscr_idx_1;
+ u16 dma_dscr_idx_2;
+ u32 pte_offset;
+ u32 sram_bank;
+ u32 byte_cnt_high_reg_offset;
+ u32 byte_cnt_low_reg_offset;
+ u32 dma_curr_dscr;
uint64_t size;
+ u64 bytescount;
void __iomem *acp_mmio;
};
+struct audio_drv_data {
+ struct snd_pcm_substream *play_i2ssp_stream;
+ struct snd_pcm_substream *capture_i2ssp_stream;
+ struct snd_pcm_substream *play_i2sbt_stream;
+ struct snd_pcm_substream *capture_i2sbt_stream;
+ struct snd_pcm_substream *play_i2s_micsp_stream;
+ void __iomem *acp_mmio;
+ u32 asic_type;
+ snd_pcm_sframes_t delay;
+};
+
+/*
+ * this structure used for platform data transfer between machine driver
+ * and dma driver
+ */
+struct acp_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+ u16 capture_channel;
+};
+
+union acp_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
enum {
ACP_TILE_P1 = 0,
ACP_TILE_P2,
@@ -93,26 +194,30 @@ enum {
};
enum {
- ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION = 0x0,
- ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC = 0x1,
- ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM = 0x8,
- ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM = 0x9,
- ACP_DMA_ATTRIBUTES_FORCE_SIZE = 0xF
+ ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION = 0x0,
+ ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC = 0x1,
+ ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM = 0x8,
+ ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM = 0x9,
+ ACP_DMA_ATTR_FORCE_SIZE = 0xF
};
typedef struct acp_dma_dscr_transfer {
/* Specifies the source memory location for the DMA data transfer. */
u32 src;
- /* Specifies the destination memory location to where the data will
+ /*
+ * Specifies the destination memory location to where the data will
* be transferred.
- */
+ */
u32 dest;
- /* Specifies the number of bytes need to be transferred
- * from source to destination memory.Transfer direction & IOC enable
- */
+ /*
+ * Specifies the number of bytes need to be transferred
+ * from source to destination memory.Transfer direction & IOC enable
+ */
u32 xfer_val;
/* Reserved for future use */
u32 reserved;
} acp_dma_dscr_transfer_t;
+extern bool acp_bt_uart_enable;
+
#endif /*__ACP_HW_H */
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
new file mode 100644
index 000000000000..c2a60bc80ee6
--- /dev/null
+++ b/sound/soc/amd/acp/Kconfig
@@ -0,0 +1,189 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+#
+
+config SND_SOC_AMD_ACP_COMMON
+ tristate "AMD Audio ACP Common support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables common modules for Audio-Coprocessor i.e. ACP
+ IP block on AMD platforms.
+
+config SND_SOC_ACPI_AMD_MATCH
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+if SND_SOC_AMD_ACP_COMMON
+
+config SND_SOC_AMD_ACP_PDM
+ tristate
+
+config SND_SOC_AMD_ACP_LEGACY_COMMON
+ tristate
+
+config SND_SOC_AMD_ACP_I2S
+ tristate
+
+config SND_SOC_AMD_ACPI_MACH
+ tristate
+
+config SND_SOC_AMD_ACP_PCM
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+config SND_SOC_AMD_ACP_PCI
+ tristate "AMD ACP PCI Driver Support"
+ depends on X86 && PCI
+ depends on ACPI
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This options enables generic PCI driver for ACP device.
+
+config SND_AMD_ASOC_RENOIR
+ tristate "AMD ACP ASOC Renoir Support"
+ depends on ACPI
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ depends on X86 && PCI
+ help
+ This option enables Renoir I2S support on AMD platform.
+
+config SND_AMD_ASOC_REMBRANDT
+ tristate "AMD ACP ASOC Rembrandt Support"
+ depends on ACPI
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ depends on AMD_NODE
+ depends on X86 && PCI
+ help
+ This option enables Rembrandt I2S support on AMD platform.
+ Say Y if you want to enable AUDIO on Rembrandt
+ If unsure select "N".
+
+config SND_AMD_ASOC_ACP63
+ tristate "AMD ACP ASOC ACP6.3 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ depends on AMD_NODE
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This option enables Acp6.3 I2S support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP6.3
+ If unsure select "N".
+
+config SND_AMD_ASOC_ACP70
+ tristate "AMD ACP ASOC Acp7.0 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ depends on AMD_NODE
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This option enables Acp7.0 PDM support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP7.0
+ If unsure select "N".
+
+config SND_SOC_AMD_MACH_COMMON
+ tristate
+ depends on X86 && PCI && I2C
+ select CLK_FIXED_FCH
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_DMIC
+ select SND_SOC_RT1019
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5682S
+ select SND_SOC_NAU8825
+ select SND_SOC_NAU8821
+ select SND_SOC_MAX98388
+ help
+ This option enables common Machine driver module for ACP.
+
+config SND_SOC_AMD_LEGACY_MACH
+ tristate "AMD Legacy Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ help
+ This option enables legacy sound card support for ACP audio.
+
+config SND_SOC_AMD_SOF_MACH
+ tristate "AMD SOF Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ help
+ This option enables SOF sound card support for ACP audio.
+
+config SND_SOC_AMD_SDW_MACH_COMMON
+ tristate
+ help
+ This option enables common SoundWire Machine driver module for
+ AMD platforms.
+
+config SND_SOC_AMD_SOF_SDW_MACH
+ tristate "AMD SOF Soundwire Machine Driver Support"
+ depends on X86 && PCI && ACPI
+ depends on SOUNDWIRE
+ select SND_SOC_AMD_SDW_MACH_COMMON
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_DMIC
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
+ help
+ This option enables SOF sound card support for SoundWire enabled
+ AMD platforms along with ACP PDM controller.
+ Say Y if you want to enable SoundWire based machine driver support
+ on AMD platform.
+ If unsure select "N".
+
+config SND_SOC_AMD_LEGACY_SDW_MACH
+ tristate "AMD Legacy(No DSP) Soundwire Machine Driver Support"
+ depends on X86 && PCI && ACPI
+ depends on SOUNDWIRE
+ select SND_SOC_AMD_SDW_MACH_COMMON
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_DMIC
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT712_SDCA_SDW
+ select SND_SOC_RT712_SDCA_DMIC_SDW
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT1320_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
+ help
+ This option enables Legacy(No DSP) sound card support for SoundWire
+ enabled AMD platforms along with ACP PDM controller.
+ Say Y if you want to enable SoundWire based machine driver support
+ on AMD platform.
+ If unsure select "N".
+
+endif # SND_SOC_AMD_ACP_COMMON
+
+config SND_AMD_SOUNDWIRE_ACPI
+ tristate
+ depends on ACPI
+ help
+ This options enables ACPI helper functions for SoundWire
+ interface for AMD platforms.
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
new file mode 100644
index 000000000000..08220b9a3802
--- /dev/null
+++ b/sound/soc/amd/acp/Makefile
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#common acp driver
+snd-acp-pcm-y := acp-platform.o
+snd-acp-i2s-y := acp-i2s.o
+snd-acp-pdm-y := acp-pdm.o
+snd-acp-legacy-common-y := acp-legacy-common.o
+snd-acp-pci-y := acp-pci.o
+snd-amd-sdw-acpi-y := amd-sdw-acpi.o
+snd-amd-acpi-mach-y := amd-acpi-mach.o
+
+#platform specific driver
+snd-acp-renoir-y := acp-renoir.o
+snd-acp-rembrandt-y := acp-rembrandt.o
+snd-acp63-y := acp63.o
+snd-acp70-y := acp70.o
+
+#machine specific driver
+snd-acp-mach-y := acp-mach-common.o
+snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
+snd-acp-sof-mach-y := acp-sof-mach.o
+snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
+snd-acp-sdw-mach-y := acp-sdw-mach-common.o
+snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
+snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_LEGACY_COMMON) += snd-acp-legacy-common.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
+obj-$(CONFIG_SND_SOC_AMD_ACPI_MACH) += snd-amd-acpi-mach.o
+
+obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
+
+obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o
+obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o
+obj-$(CONFIG_SND_SOC_ACPI_AMD_MATCH) += snd-soc-acpi-amd-match.o
+obj-$(CONFIG_SND_SOC_AMD_SDW_MACH_COMMON) += snd-acp-sdw-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_SDW_MACH) += snd-acp-sdw-sof-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_SDW_MACH) += snd-acp-sdw-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
new file mode 100644
index 000000000000..4ba0a66981ea
--- /dev/null
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -0,0 +1,699 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio I2S controller
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitfield.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_playcap"
+#define I2S_MASTER_MODE_ENABLE 1
+#define LRCLK_DIV_FIELD GENMASK(10, 2)
+#define BCLK_DIV_FIELD GENMASK(23, 11)
+#define ACP63_LRCLK_DIV_FIELD GENMASK(12, 2)
+#define ACP63_BCLK_DIV_FIELD GENMASK(23, 13)
+
+static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id)
+{
+ u32 i2s_clk_reg, val;
+
+ switch (dai_id) {
+ case I2S_SP_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ case I2S_BT_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM1_MSTRCLKGEN;
+ break;
+ case I2S_HS_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ default:
+ i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ val = I2S_MASTER_MODE_ENABLE;
+ if (chip->tdm_mode)
+ val |= BIT(1);
+
+ switch (chip->acp_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div);
+ val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div);
+ break;
+ default:
+ val |= FIELD_PREP(LRCLK_DIV_FIELD, chip->lrclk_div);
+ val |= FIELD_PREP(BCLK_DIV_FIELD, chip->bclk_div);
+ }
+ writel(val, chip->base + i2s_clk_reg);
+}
+
+static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct device *dev = cpu_dai->component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ int mode;
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ chip->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ chip->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip;
+ struct acp_stream *stream;
+ int slot_len, no_of_slots;
+
+ chip = dev_get_drvdata(dev->parent);
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported bitdepth %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ case ACP_RMB_PCI_ID:
+ switch (slots) {
+ case 1 ... 7:
+ no_of_slots = slots;
+ break;
+ case 8:
+ no_of_slots = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported slots %d\n", slots);
+ return -EINVAL;
+ }
+ break;
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ switch (slots) {
+ case 1 ... 31:
+ no_of_slots = slots;
+ break;
+ case 32:
+ no_of_slots = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported slots %d\n", slots);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev);
+ return -EINVAL;
+ }
+
+ slots = no_of_slots;
+
+ spin_lock_irq(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ case ACP_RMB_PCI_ID:
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ chip->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ chip->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ break;
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ chip->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 13) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ chip->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 13) | (slot_len << 18);
+ break;
+ default:
+ dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev);
+ spin_unlock_irq(&chip->acp_lock);
+ return -EINVAL;
+ }
+ }
+ spin_unlock_irq(&chip->acp_lock);
+ return 0;
+}
+
+static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip;
+ struct acp_resource *rsrc;
+ u32 val;
+ u32 xfer_resolution;
+ u32 reg_val, fmt_reg, tdm_fmt;
+ u32 lrclk_div_val, bclk_div_val;
+
+ chip = dev_get_drvdata(dev->parent);
+ rsrc = chip->rsrc;
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ chip->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution;
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ chip->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution;
+ }
+
+ val = readl(chip->base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (xfer_resolution << 3);
+ writel(val, chip->base + reg_val);
+
+ if (chip->tdm_mode) {
+ val = readl(chip->base + reg_val);
+ writel(val | BIT(1), chip->base + reg_val);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tdm_fmt = chip->tdm_tx_fmt[dai->driver->id - 1];
+ else
+ tdm_fmt = chip->tdm_rx_fmt[dai->driver->id - 1];
+ writel(tdm_fmt, chip->base + fmt_reg);
+ }
+
+ if (rsrc->soc_mclk) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 768;
+ break;
+ case 16000:
+ bclk_div_val = 384;
+ break;
+ case 24000:
+ bclk_div_val = 256;
+ break;
+ case 32000:
+ bclk_div_val = 192;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 128;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 64;
+ break;
+ case 192000:
+ bclk_div_val = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 384;
+ break;
+ case 16000:
+ bclk_div_val = 192;
+ break;
+ case 24000:
+ bclk_div_val = 128;
+ break;
+ case 32000:
+ bclk_div_val = 96;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 64;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 32;
+ break;
+ case 192000:
+ bclk_div_val = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 64;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 48000:
+ case 96000:
+ case 192000:
+ switch (params_channels(params)) {
+ case 2:
+ break;
+ case 4:
+ bclk_div_val = bclk_div_val >> 1;
+ lrclk_div_val = lrclk_div_val << 1;
+ break;
+ case 8:
+ bclk_div_val = bclk_div_val >> 2;
+ lrclk_div_val = lrclk_div_val << 2;
+ break;
+ case 16:
+ bclk_div_val = bclk_div_val >> 3;
+ lrclk_div_val = lrclk_div_val << 3;
+ break;
+ case 32:
+ bclk_div_val = bclk_div_val >> 4;
+ lrclk_div_val = lrclk_div_val << 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported channels %#x\n",
+ params_channels(params));
+ }
+ break;
+ default:
+ break;
+ }
+ chip->lrclk_div = lrclk_div_val;
+ chip->bclk_div = bclk_div_val;
+ }
+ return 0;
+}
+
+static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_resource *rsrc = chip->rsrc;
+ u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
+
+ period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stream->bytescount = acp_get_byte_count(chip, stream->dai_id, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_TX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_BTTDM_ITER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_TX_RINGBUFSIZE(chip);
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE(chip);
+ break;
+ case I2S_HS_INSTANCE:
+ water_val = ACP_HS_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_ITER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_TX_RINGBUFSIZE;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_RX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_BTTDM_IRER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_RX_RINGBUFSIZE(chip);
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE(chip);
+ break;
+ case I2S_HS_INSTANCE:
+ water_val = ACP_HS_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_IRER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_RX_RINGBUFSIZE;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+
+ writel(period_bytes, chip->base + water_val);
+ writel(buf_size, chip->base + buf_reg);
+ if (rsrc->soc_mclk)
+ acp_set_i2s_clk(chip, dai->driver->id);
+ val = readl(chip->base + reg_val);
+ val = val | BIT(0);
+ writel(val, chip->base + reg_val);
+ writel(1, chip->base + ier_val);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ }
+ val = readl(chip->base + reg_val);
+ val = val & ~BIT(0);
+ writel(val, chip->base + reg_val);
+
+ if (!(readl(chip->base + ACP_BTTDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_BTTDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_BTTDM_IER);
+ if (!(readl(chip->base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_I2STDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_I2STDM_IER);
+ if (!(readl(chip->base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_HSTDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_HSTDM_IER);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
+ u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_SP_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_SP_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_BT_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_BT_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_HS_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_HS_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_HS_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, chip->base + reg_dma_size);
+ writel(acp_fifo_addr, chip->base + reg_fifo_addr);
+ writel(FIFO_SIZE, chip->base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_RX_THRESHOLD(rsrc->offset)) |
+ BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_TX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
+
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+
+ return 0;
+}
+
+static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_resource *rsrc = chip->rsrc;
+ unsigned int dir = substream->stream;
+ unsigned int irq_bit = 0;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(I2S_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
+ stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(I2S_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
+ stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(BT_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
+ stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(BT_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
+ stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(HS_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_HS_PB_PTE_OFFSET;
+ stream->fifo_offset = HS_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(HS_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_HS_CP_PTE_OFFSET;
+ stream->fifo_offset = HS_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ /* Save runtime dai configuration in stream */
+ stream->id = dai->driver->id + dir;
+ stream->dai_id = dai->driver->id;
+ stream->irq_bit = irq_bit;
+ stream->dir = substream->stream;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
+ .startup = acp_i2s_startup,
+ .hw_params = acp_i2s_hwparams,
+ .prepare = acp_i2s_prepare,
+ .trigger = acp_i2s_trigger,
+ .set_fmt = acp_i2s_set_fmt,
+ .set_tdm_slot = acp_i2s_set_tdm_slot,
+};
+EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP Audio I2S controller");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c
new file mode 100644
index 000000000000..3078f459e005
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-common.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba Kareem <Syed.SabaKareem@amd.com>
+//
+
+/*
+ * Common file to be used by amd platforms
+ */
+
+#include "amd.h"
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/export.h>
+
+#include "../mach-config.h"
+
+#define ACP_RENOIR_PDM_ADDR 0x02
+#define ACP_REMBRANDT_PDM_ADDR 0x03
+#define ACP63_PDM_ADDR 0x02
+#define ACP70_PDM_ADDR 0x02
+
+struct acp_resource rn_rsrc = {
+ .offset = 20,
+ .no_of_ctrls = 1,
+ .irqp_used = 0,
+ .irq_reg_offset = 0x1800,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x02052800,
+};
+EXPORT_SYMBOL_NS_GPL(rn_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource rmb_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+EXPORT_SYMBOL_NS_GPL(rmb_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource acp63_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+EXPORT_SYMBOL_NS_GPL(acp63_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource acp70_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x10000,
+ .sram_pte_offset = 0x03800000,
+};
+EXPORT_SYMBOL_NS_GPL(acp70_rsrc, "SND_SOC_ACP_COMMON");
+
+static const struct snd_acp_hw_ops acp_common_hw_ops = {
+ /* ACP hardware initilizations */
+ .acp_init = acp_init,
+ .acp_deinit = acp_deinit,
+
+ /* ACP Interrupts*/
+ .irq = acp_irq_handler,
+ .en_interrupts = acp_enable_interrupts,
+ .dis_interrupts = acp_disable_interrupts,
+};
+
+irqreturn_t acp_irq_handler(int irq, void *data)
+{
+ struct acp_chip_info *chip = data;
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream;
+ u16 i2s_flag = 0;
+ u32 ext_intr_stat, ext_intr_stat1;
+
+ if (rsrc->no_of_ctrls == 2)
+ ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(chip, (rsrc->irqp_used - 1)));
+
+ ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ if (ext_intr_stat & stream->irq_bit) {
+ writel(stream->irq_bit,
+ ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ if (chip->rsrc->no_of_ctrls == 2) {
+ if (ext_intr_stat1 & stream->irq_bit) {
+ writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(chip,
+ (rsrc->irqp_used - 1)));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ if (i2s_flag)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+int acp_enable_interrupts(struct acp_chip_info *chip)
+{
+ struct acp_resource *rsrc;
+ u32 ext_intr_ctrl;
+
+ rsrc = chip->rsrc;
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(chip));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, "SND_SOC_ACP_COMMON");
+
+int acp_disable_interrupts(struct acp_chip_info *chip)
+{
+ struct acp_resource *rsrc;
+
+ rsrc = chip->rsrc;
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(chip));
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, "SND_SOC_ACP_COMMON");
+
+static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct acp_stream *stream = runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+
+ u32 physical_addr, pdm_size, period_bytes;
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ pdm_size = frames_to_bytes(runtime, runtime->buffer_size);
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init ACP PDM Ring buffer */
+ writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
+ writel(pdm_size, chip->base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void set_acp_pdm_clk(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int pdm_ctrl;
+
+ /* Enable default ACP PDM clk */
+ writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(pdm_ctrl, chip->base + ACP_WOV_MISC_CTRL);
+ set_acp_pdm_ring_buffer(substream, dai);
+}
+
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 ext_int_ctrl;
+
+ soc_runtime = snd_soc_substream_to_rtd(substream);
+ dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+
+ /* Programming channel mask and sampling rate */
+ writel(chip->ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ /* Enabling ACP Pdm interuppts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ set_acp_pdm_clk(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, "SND_SOC_ACP_COMMON");
+
+static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size, reg_fifo_size, reg_fifo_addr;
+ u32 phy_addr, acp_fifo_addr, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip);
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip);
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip);
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip);
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_HS_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, chip->base + reg_dma_size);
+ writel(acp_fifo_addr, chip->base + reg_fifo_addr);
+ writel(FIFO_SIZE, chip->base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_RX_THRESHOLD(rsrc->offset)) |
+ BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_TX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
+
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ return 0;
+}
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip,
+ struct acp_stream *stream)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 tdm_fmt, reg_val, fmt_reg, val;
+
+ soc_runtime = snd_soc_substream_to_rtd(substream);
+ dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tdm_fmt = chip->tdm_tx_fmt[stream->dai_id - 1];
+ switch (stream->dai_id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ default:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = chip->xfer_tx_resolution[stream->dai_id - 1] << 3;
+ } else {
+ tdm_fmt = chip->tdm_rx_fmt[stream->dai_id - 1];
+ switch (stream->dai_id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ default:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = chip->xfer_rx_resolution[stream->dai_id - 1] << 3;
+ }
+ writel(val, chip->base + reg_val);
+ if (chip->tdm_mode == TDM_ENABLE) {
+ writel(tdm_fmt, chip->base + fmt_reg);
+ val = readl(chip->base + reg_val);
+ writel(val | 0x2, chip->base + reg_val);
+ }
+ return set_acp_i2s_dma_fifo(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, "SND_SOC_ACP_COMMON");
+
+static int acp_power_on(struct acp_chip_info *chip)
+{
+ u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg;
+ void __iomem *base;
+
+ base = chip->base;
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ acp_pgfsm_stat_reg = ACP_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL;
+ break;
+ case ACP_RMB_PCI_ID:
+ acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL;
+ break;
+ case ACP63_PCI_ID:
+ acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL;
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(base + acp_pgfsm_stat_reg);
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ writel(ACP_PGFSM_CNTL_POWER_ON_MASK, base + acp_pgfsm_ctrl_reg);
+
+ return readl_poll_timeout(base + acp_pgfsm_stat_reg, val,
+ !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp_reset(void __iomem *base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, base + ACP_SOFT_RESET);
+ ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,
+ DELAY_US, ACP_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, base + ACP_SOFT_RESET);
+ return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
+}
+
+int acp_init(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* power on */
+ ret = acp_power_on(chip);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, chip->base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ writel(0, chip->base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_init, "SND_SOC_ACP_COMMON");
+
+int acp_deinit(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret)
+ return ret;
+
+ if (chip->acp_rev < ACP70_PCI_ID)
+ writel(0, chip->base + ACP_CONTROL);
+ else
+ writel(0x01, chip->base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_deinit, "SND_SOC_ACP_COMMON");
+int acp_machine_select(struct acp_chip_info *chip)
+{
+ struct snd_soc_acpi_mach *mach;
+ int size, platform;
+
+ if (chip->flag == FLAG_AMD_LEGACY_ONLY_DMIC && chip->is_pdm_dev) {
+ platform = chip->acp_rev;
+ chip->mach_dev = platform_device_register_data(chip->dev, "acp-pdm-mach",
+ PLATFORM_DEVID_NONE, &platform,
+ sizeof(platform));
+ } else {
+ size = sizeof(*chip->machines);
+ mach = snd_soc_acpi_find_machine(chip->machines);
+ if (!mach) {
+ dev_err(chip->dev, "warning: No matching ASoC machine driver found\n");
+ return -EINVAL;
+ }
+ mach->mach_params.subsystem_rev = chip->acp_rev;
+ chip->mach_dev = platform_device_register_data(chip->dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ }
+ if (IS_ERR(chip->mach_dev))
+ dev_warn(chip->dev, "Unable to register Machine device\n");
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_machine_select, "SND_SOC_ACP_COMMON");
+
+static void check_acp3x_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP3X_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ chip->is_pdm_config = true;
+ break;
+ }
+}
+
+static void check_acp6x_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void check_acp70_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_19:
+ case ACP_CONFIG_20:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ u32 pdm_addr, ret;
+
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ pdm_addr = ACP_RENOIR_PDM_ADDR;
+ check_acp3x_config(chip);
+ break;
+ case ACP_RMB_PCI_ID:
+ pdm_addr = ACP_REMBRANDT_PDM_ADDR;
+ check_acp6x_config(chip);
+ break;
+ case ACP63_PCI_ID:
+ pdm_addr = ACP63_PDM_ADDR;
+ check_acp6x_config(chip);
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ pdm_addr = ACP70_PDM_ADDR;
+ check_acp70_config(chip);
+ break;
+ default:
+ break;
+ }
+
+ if (chip->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0);
+ if (pdm_dev) {
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == pdm_addr)
+ chip->is_pdm_dev = true;
+ }
+
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret))
+ chip->is_pdm_dev = dmic_status;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(check_acp_config, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp31_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp31_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp31_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp31_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp31_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp31_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp6x_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp6x_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp6x_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp6x_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp6x_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp6x_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp63_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp63_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp63_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp63_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp63_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp63_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp70_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp70_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp70_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp70_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp70_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp70_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP legacy common features");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
new file mode 100644
index 000000000000..a7a551366a40
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Machine Driver Legacy Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+#include "acp3x-es83xx/acp3x-es83xx.h"
+
+static struct acp_card_drvdata rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata es83xx_rn_data = {
+ .hs_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = ES83XX,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata max_nau8825_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = NAU8825,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata acp_dmic_data = {
+ .dmic_cpu_id = DMIC,
+ .dmic_codec_id = DMIC,
+};
+
+static bool acp_asoc_init_ops(struct acp_card_drvdata *priv)
+{
+ bool has_ops = false;
+
+ if (priv->hs_codec_id == ES83XX) {
+ has_ops = true;
+ acp3x_es83xx_init_ops(&priv->ops);
+ }
+ return has_ops;
+}
+
+static int acp_asoc_suspend_pre(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = acp_ops_suspend_pre(card);
+ if (ret == 1)
+ return 0;
+ else
+ return ret;
+}
+
+static int acp_asoc_resume_post(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = acp_ops_resume_post(card);
+ if (ret == 1)
+ return 0;
+ else
+ return ret;
+}
+
+static int acp_asoc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ acp_card_drvdata = card->drvdata;
+ acp_card_drvdata->acpi_mach = (struct snd_soc_acpi_mach *)pdev->dev.platform_data;
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+
+ acp_asoc_init_ops(card->drvdata);
+
+ /* If widgets and controls are not set in specific callback,
+ * they will be added per-codec in acp-mach-common.c
+ */
+ ret = acp_ops_configure_widgets(card);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Cannot configure widgets for card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+ card->suspend_pre = acp_asoc_suspend_pre;
+ card->resume_post = acp_asoc_resume_post;
+
+ ret = acp_ops_probe(card);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Cannot probe card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+ if (!strcmp(pdev->name, "acp-pdm-mach"))
+ acp_card_drvdata->acp_rev = *((int *)dev->platform_data);
+ else
+ acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
+
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ ret = acp_legacy_dai_links_create(card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Cannot create dai links for card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ card->name, ret);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "acp3xalc56821019",
+ .driver_data = (kernel_ulong_t)&rt5682_rt1019_data,
+ },
+ {
+ .name = "acp3xalc5682sm98360",
+ .driver_data = (kernel_ulong_t)&rt5682s_max_data,
+ },
+ {
+ .name = "acp3xalc5682s1019",
+ .driver_data = (kernel_ulong_t)&rt5682s_rt1019_data,
+ },
+ {
+ .name = "acp3x-es83xx",
+ .driver_data = (kernel_ulong_t)&es83xx_rn_data,
+ },
+ {
+ .name = "rmb-nau8825-max",
+ .driver_data = (kernel_ulong_t)&max_nau8825_data,
+ },
+ {
+ .name = "rmb-rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&rt5682s_rt1019_rmb_data,
+ },
+ {
+ .name = "acp-pdm-mach",
+ .driver_data = (kernel_ulong_t)&acp_dmic_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .pm = &snd_soc_pm_ops,
+ .name = "acp_mach",
+ },
+ .probe = acp_asoc_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS("SND_SOC_AMD_MACH");
+MODULE_DESCRIPTION("ACP chrome audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
new file mode 100644
index 000000000000..4d99472c75ba
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -0,0 +1,1803 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Machine Driver Interface for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "../../codecs/rt5682.h"
+#include "../../codecs/rt1019.h"
+#include "../../codecs/rt5682s.h"
+#include "../../codecs/nau8825.h"
+#include "../../codecs/nau8821.h"
+#include "acp-mach.h"
+
+#define PCO_PLAT_CLK 48000000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define DUAL_CHANNEL 2
+#define FOUR_CHANNEL 4
+#define NAU8821_CODEC_DAI "nau8821-hifi"
+#define NAU8821_BCLK 1536000
+#define NAU8821_FREQ_OUT 12288000
+#define MAX98388_CODEC_DAI "max98388-aif1"
+
+#define TDM_MODE_ENABLE 1
+
+const struct dmi_system_id acp_quirk_table[] = {
+ {
+ /* Google skyrim proto-0 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "Google_Skyrim"),
+ },
+ .driver_data = (void *)TDM_MODE_ENABLE,
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(acp_quirk_table);
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp_clk_enable(struct acp_card_drvdata *drvdata,
+ unsigned int srate, unsigned int bclk_ratio)
+{
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * bclk_ratio);
+
+ return clk_prepare_enable(drvdata->wclk);
+}
+
+/* Declare RT5682 codec components */
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+
+static struct snd_soc_jack rt5682_jack;
+static struct snd_soc_jack_pin rt5682_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new rt5682_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget rt5682_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rt5682_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+/* Define card ops for RT5682 CODEC */
+static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682)
+ return -EINVAL;
+
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_dapm_new_controls(dapm, rt5682_widgets,
+ ARRAY_SIZE(rt5682_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5682_controls,
+ ARRAY_SIZE(rt5682_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rt5682_jack,
+ rt5682_jack_pins,
+ ARRAY_SIZE(rt5682_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &rt5682_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(dapm, rt5682_map, ARRAY_SIZE(rt5682_map));
+}
+
+static int acp_card_hs_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return ret;
+}
+
+static void acp_card_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (!drvdata->soc_mclk)
+ clk_disable_unprepare(drvdata->wclk);
+}
+
+static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1,
+ 49152000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682_ops = {
+ .startup = acp_card_hs_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt5682_hw_params,
+};
+
+/* Define RT5682S CODEC component*/
+SND_SOC_DAILINK_DEF(rt5682s,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1")));
+
+static struct snd_soc_jack rt5682s_jack;
+static struct snd_soc_jack_pin rt5682s_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new rt5682s_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget rt5682s_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rt5682s_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682S)
+ return -EINVAL;
+
+ if (!drvdata->soc_mclk) {
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+ }
+
+ ret = snd_soc_dapm_new_controls(dapm, rt5682s_widgets,
+ ARRAY_SIZE(rt5682s_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5682s_controls,
+ ARRAY_SIZE(rt5682s_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rt5682s_jack,
+ rt5682s_jack_pins,
+ ARRAY_SIZE(rt5682s_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &rt5682s_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(dapm, rt5682s_map, ARRAY_SIZE(rt5682s_map));
+}
+
+static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1,
+ 49152000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * ch * format);
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682s_ops = {
+ .startup = acp_card_hs_startup,
+ .hw_params = acp_card_rt5682s_hw_params,
+};
+
+static const unsigned int dmic_channels[] = {
+ DUAL_CHANNEL, FOUR_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list dmic_constraints_channels = {
+ .count = ARRAY_SIZE(dmic_channels),
+ .list = dmic_channels,
+ .mask = 0,
+};
+
+static int acp_card_dmic_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &dmic_constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_dmic_ops = {
+ .startup = acp_card_dmic_startup,
+};
+
+/* Declare RT1019 codec components */
+SND_SOC_DAILINK_DEF(rt1019,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"),
+ COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
+
+static const struct snd_kcontrol_new rt1019_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget rt1019_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route rt1019_map_lr[] = {
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
+
+static struct snd_soc_codec_conf rt1019_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"),
+ .name_prefix = "Right",
+ },
+};
+
+static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(dapm, rt1019_widgets,
+ ARRAY_SIZE(rt1019_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt1019_controls,
+ ARRAY_SIZE(rt1019_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(dapm, rt1019_map_lr,
+ ARRAY_SIZE(rt1019_map_lr));
+}
+
+static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int i, ret = 0;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strcmp(codec_dai->name, "rt1019-aif"))
+ continue;
+
+ if (drvdata->tdm_mode)
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ TDM_CHANNELS * format * srate, 256 * srate);
+ else
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ ch * format * srate, 256 * srate);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1019_SCLK_S_PLL,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A
+ | SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ /**
+ * As codec supports slot 2 for left channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:00")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x4, 8, 16);
+ if (ret < 0)
+ break;
+ }
+
+ /**
+ * As codec supports slot 3 for right channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:01")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x8, 8, 16);
+ if (ret < 0)
+ break;
+ }
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int acp_card_amp_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt1019_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt1019_hw_params,
+};
+
+/* Declare Maxim codec components */
+SND_SOC_DAILINK_DEF(max98360a,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi")));
+
+static const struct snd_kcontrol_new max98360a_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget max98360a_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max98360a_map[] = {
+ {"Spk", NULL, "Speaker"},
+};
+
+static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98360A)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(dapm, max98360a_widgets,
+ ARRAY_SIZE(max98360a_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max98360a_controls,
+ ARRAY_SIZE(max98360a_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(dapm, max98360a_map,
+ ARRAY_SIZE(max98360a_map));
+}
+
+static int acp_card_maxim_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ unsigned int fmt, srate, ch, format;
+ int ret;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_maxim_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_maxim_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(max98388,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", MAX98388_CODEC_DAI),
+ COMP_CODEC("i2c-ADS8388:01", MAX98388_CODEC_DAI)));
+
+static const struct snd_kcontrol_new max98388_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget max98388_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max98388_map[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static const unsigned int max98388_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits_max = {
+ .list = max98388_format,
+ .count = ARRAY_SIZE(max98388_format),
+};
+
+static int acp_card_max98388_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits_max);
+
+ return 0;
+}
+
+static int acp_card_max98388_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98388)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(dapm, max98388_widgets,
+ ARRAY_SIZE(max98388_widgets));
+
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max98388_controls,
+ ARRAY_SIZE(max98388_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(dapm, max98388_map,
+ ARRAY_SIZE(max98388_map));
+}
+
+static int acp_max98388_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai =
+ snd_soc_card_get_codec_dai(card,
+ MAX98388_CODEC_DAI);
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_max98388_ops = {
+ .startup = acp_card_max98388_startup,
+ .hw_params = acp_max98388_hw_params,
+};
+
+/* Declare nau8825 codec components */
+SND_SOC_DAILINK_DEF(nau8825,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi")));
+
+static struct snd_soc_jack nau8825_jack;
+static struct snd_soc_jack_pin nau8825_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget nau8825_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route nau8825_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+};
+
+static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != NAU8825)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(dapm, nau8825_widgets,
+ ARRAY_SIZE(nau8825_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8825_controls,
+ ARRAY_SIZE(nau8825_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &nau8825_jack,
+ nau8825_jack_pins,
+ ARRAY_SIZE(nau8825_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &nau8825_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(dapm, nau8825_map, ARRAY_SIZE(nau8825_map));
+}
+
+static int acp_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS,
+ (48000 * 256), SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params),
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set FLL: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 4 and slot 5 for playback and slot 6 for capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x30, 0xC0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x40, 0x30, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int acp_nau8825_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_nau8825_ops = {
+ .startup = acp_nau8825_startup,
+ .hw_params = acp_nau8825_hw_params,
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, NAU8821_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "set sysclk err = %d\n", ret);
+ return -EIO;
+ }
+ } else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, NAU8821_BCLK,
+ NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ }
+ return ret;
+}
+
+static struct snd_soc_jack nau8821_jack;
+static struct snd_soc_jack_pin nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new nau8821_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget nau8821_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8821_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static const unsigned int nau8821_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = nau8821_format,
+ .count = ARRAY_SIZE(nau8821_format),
+};
+
+static int acp_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ ret = snd_soc_dapm_new_controls(dapm, nau8821_widgets,
+ ARRAY_SIZE(nau8821_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ // Don't need to add routes if widget addition failed
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8821_controls,
+ ARRAY_SIZE(nau8821_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &nau8821_jack,
+ nau8821_jack_pins,
+ ARRAY_SIZE(nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ nau8821_enable_jack_detect(component, &nau8821_jack);
+
+ return snd_soc_dapm_add_routes(dapm, nau8821_audio_route,
+ ARRAY_SIZE(nau8821_audio_route));
+}
+
+static int acp_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+ return 0;
+}
+
+static int acp_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->soc_mclk)
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params),
+ params_rate(params) * 256);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_8821_ops = {
+ .startup = acp_8821_startup,
+ .hw_params = acp_nau8821_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(nau8821,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
+ NAU8821_CODEC_DAI)));
+
+/* Declare DMIC codec components */
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+
+/* Declare ACP CPU components */
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ .name = "acp_asoc_renoir.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_rmb_component[] = {
+ {
+ .name = "acp_asoc_rembrandt.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_acp63_component[] = {
+ {
+ .name = "acp_asoc_acp63.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_acp70_component[] = {
+ {
+ .name = "acp_asoc_acp70.0",
+ }
+};
+
+static struct snd_soc_dai_link_component sof_component[] = {
+ {
+ .name = "0000:04:00.5",
+ }
+};
+
+SND_SOC_DAILINK_DEF(i2s_sp,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-sp")));
+SND_SOC_DAILINK_DEF(i2s_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs")));
+SND_SOC_DAILINK_DEF(sof_sp,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
+SND_SOC_DAILINK_DEF(sof_sp_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp-virtual")));
+SND_SOC_DAILINK_DEF(sof_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs")));
+SND_SOC_DAILINK_DEF(sof_hs_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual")));
+SND_SOC_DAILINK_DEF(sof_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-bt")));
+SND_SOC_DAILINK_DEF(sof_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
+SND_SOC_DAILINK_DEF(pdm_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-pdm-dmic")));
+
+static int acp_rtk_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret = 0;
+
+ if (!component)
+ return 0;
+
+ if (strncmp(component->name, "i2c-RTL5682", 11) &&
+ strncmp(component->name, "i2c-10EC1019", 12))
+ return 0;
+
+ /*
+ * For Realtek's codec and amplifier components,
+ * the lrck and bclk must be enabled brfore their all dapms be powered on,
+ * and must be disabled after their all dapms be powered down
+ * to avoid any pop.
+ */
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+
+ /* Increase bclk's enable_count */
+ ret = clk_prepare_enable(drvdata->bclk);
+ if (ret < 0)
+ dev_err(component->dev, "Failed to enable bclk %d\n", ret);
+ } else {
+ /*
+ * Decrease bclk's enable_count.
+ * While the enable_count is 0, the bclk would be closed.
+ */
+ clk_disable_unprepare(drvdata->bclk);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->bt_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ if (drv_data->hs_codec_id == NAU8821) {
+ links[i].codecs = nau8821;
+ links[i].num_codecs = ARRAY_SIZE(nau8821);
+ links[i].init = acp_8821_init;
+ links[i].ops = &acp_8821_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->hs_cpu_id == I2S_HS) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_hs;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->hs_codec_id == NAU8825) {
+ links[i].codecs = nau8825;
+ links[i].num_codecs = ARRAY_SIZE(nau8825);
+ links[i].init = acp_card_nau8825_init;
+ links[i].ops = &acp_card_nau8825_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_SP) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ if (drv_data->acp_rev == ACP_RN_PCI_ID) {
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ } else {
+ links[i].cpus = sof_sp_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
+ }
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].playback_only = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_HS) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = sof_hs_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs_virtual);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].playback_only = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ if (drv_data->amp_codec_id == MAX98388) {
+ links[i].playback_only = 0;
+ links[i].codecs = max98388;
+ links[i].num_codecs = ARRAY_SIZE(max98388);
+ links[i].ops = &acp_max98388_ops;
+ links[i].init = acp_card_max98388_init;
+ card->codec_conf = max98388_conf;
+ card->num_configs = ARRAY_SIZE(max98388_conf);
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ i++;
+ }
+
+ if (drv_data->bt_cpu_id == I2S_BT) {
+ links[i].name = "acp-bt-codec";
+ links[i].id = BT_BE_ID;
+ links[i].cpus = sof_bt;
+ links[i].num_cpus = ARRAY_SIZE(sof_bt);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->bt_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ links[i].id = DMIC_BE_ID;
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ links[i].cpus = sof_dmic;
+ links[i].num_cpus = ARRAY_SIZE(sof_dmic);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].capture_only = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+ card->set_bias_level = acp_rtk_set_bias_level;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_sofdsp_dai_links_create, "SND_SOC_AMD_MACH");
+
+int acp_legacy_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+ int rc;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ if (drv_data->hs_codec_id == ES83XX) {
+ rc = acp_ops_configure_link(card, &links[i]);
+ if (rc != 0) {
+ dev_err(dev, "Failed to configure link for ES83XX: %d\n", rc);
+ return rc;
+ }
+ }
+ i++;
+ }
+
+ if (drv_data->hs_cpu_id == I2S_HS) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_hs;
+ links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->hs_codec_id == NAU8825) {
+ links[i].codecs = nau8825;
+ links[i].num_codecs = ARRAY_SIZE(nau8825);
+ links[i].init = acp_card_nau8825_init;
+ links[i].ops = &acp_card_nau8825_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_SP) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].playback_only = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_HS) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = i2s_hs;
+ links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+
+ links[i].playback_only = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ links[i].stream_name = "DMIC capture";
+ links[i].id = DMIC_BE_ID;
+ if (drv_data->dmic_codec_id == DMIC) {
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ } else {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ links[i].cpus = pdm_dmic;
+ links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ links[i].platforms = platform_acp70_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp70_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+ links[i].ops = &acp_card_dmic_ops;
+ links[i].capture_only = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+ card->set_bias_level = acp_rtk_set_bias_level;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_legacy_dai_links_create, "SND_SOC_AMD_MACH");
+
+MODULE_DESCRIPTION("AMD ACP Common Machine driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
new file mode 100644
index 000000000000..f94c30c20f20
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __ACP_MACH_H
+#define __ACP_MACH_H
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "acp_common.h"
+
+#define TDM_CHANNELS 8
+
+#define ACP_OPS(priv, cb) ((priv)->ops.cb)
+
+#define acp_get_drvdata(card) ((struct acp_card_drvdata *)(card)->drvdata)
+
+enum be_id {
+ HEADSET_BE_ID = 0,
+ AMP_BE_ID,
+ DMIC_BE_ID,
+ BT_BE_ID,
+};
+
+enum cpu_endpoints {
+ NONE = 0,
+ I2S_HS,
+ I2S_SP,
+ I2S_BT,
+ DMIC,
+};
+
+enum codec_endpoints {
+ DUMMY = 0,
+ RT5682,
+ RT1019,
+ MAX98360A,
+ RT5682S,
+ NAU8825,
+ NAU8821,
+ MAX98388,
+ ES83XX,
+};
+
+struct acp_mach_ops {
+ int (*probe)(struct snd_soc_card *card);
+ int (*configure_link)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+ int (*configure_widgets)(struct snd_soc_card *card);
+ int (*suspend_pre)(struct snd_soc_card *card);
+ int (*resume_post)(struct snd_soc_card *card);
+};
+
+struct acp_card_drvdata {
+ unsigned int hs_cpu_id;
+ unsigned int amp_cpu_id;
+ unsigned int bt_cpu_id;
+ unsigned int dmic_cpu_id;
+ unsigned int hs_codec_id;
+ unsigned int amp_codec_id;
+ unsigned int bt_codec_id;
+ unsigned int dmic_codec_id;
+ unsigned int dai_fmt;
+ unsigned int acp_rev;
+ struct clk *wclk;
+ struct clk *bclk;
+ struct acp_mach_ops ops;
+ struct snd_soc_acpi_mach *acpi_mach;
+ void *mach_priv;
+ bool soc_mclk;
+ bool tdm_mode;
+};
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
+int acp_legacy_dai_links_create(struct snd_soc_card *card);
+extern const struct dmi_system_id acp_quirk_table[];
+
+static inline int acp_ops_probe(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, probe))
+ ret = ACP_OPS(priv, probe)(card);
+ return ret;
+}
+
+static inline int acp_ops_configure_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, configure_link))
+ ret = ACP_OPS(priv, configure_link)(card, dai_link);
+ return ret;
+}
+
+static inline int acp_ops_configure_widgets(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, configure_widgets))
+ ret = ACP_OPS(priv, configure_widgets)(card);
+ return ret;
+}
+
+static inline int acp_ops_suspend_pre(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, suspend_pre))
+ ret = ACP_OPS(priv, suspend_pre)(card);
+ return ret;
+}
+
+static inline int acp_ops_resume_post(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, resume_post))
+ ret = ACP_OPS(priv, resume_post)(card);
+ return ret;
+}
+
+#endif
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
new file mode 100644
index 000000000000..f83708755ed1
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic PCI interface for ACP device
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_pci"
+
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x125C000
+
+static irqreturn_t irq_handler(int irq, void *data)
+{
+ struct acp_chip_info *chip = data;
+
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->irq)
+ return chip->acp_hw_ops->irq(irq, chip);
+
+ return IRQ_NONE;
+}
+static void acp_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp_platform_devs(struct pci_dev *pci, struct acp_chip_info *chip, u32 addr)
+{
+ struct platform_device_info pdevinfo;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (chip->is_i2s_config || chip->is_pdm_dev) {
+ chip->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!chip->res) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ chip->res->flags = IORESOURCE_MEM;
+ chip->res->start = addr;
+ chip->res->end = addr + (ACP3x_REG_END - ACP3x_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp_fill_platform_dev_info(&pdevinfo, parent, NULL, chip->name,
+ 0, chip->res, 1, chip, sizeof(*chip));
+
+ chip->acp_plat_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(chip->acp_plat_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(chip->acp_plat_dev);
+ goto err;
+ }
+ if (chip->is_pdm_dev && chip->is_pdm_config) {
+ chip->dmic_codec_dev = platform_device_register_data(&pci->dev,
+ "dmic-codec",
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+ if (IS_ERR(chip->dmic_codec_dev)) {
+ dev_err(&pci->dev, "failed to create DMIC device\n");
+ ret = PTR_ERR(chip->dmic_codec_dev);
+ goto unregister_acp_plat_dev;
+ }
+ }
+ return 0;
+unregister_acp_plat_dev:
+ platform_device_unregister(chip->acp_plat_dev);
+err:
+ return ret;
+}
+
+static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ struct device *dev = &pci->dev;
+ struct acp_chip_info *chip;
+ unsigned int flag, addr;
+ int ret;
+
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY && flag != FLAG_AMD_LEGACY_ONLY_DMIC)
+ return -ENODEV;
+
+ chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ if (pci_enable_device(pci))
+ return dev_err_probe(&pci->dev, -ENODEV,
+ "pci_enable_device failed\n");
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ ret = -ENOMEM;
+ goto disable_pci;
+ }
+
+ pci_set_master(pci);
+
+ chip->acp_rev = pci->revision;
+ switch (pci->revision) {
+ case 0x01:
+ chip->name = "acp_asoc_renoir";
+ chip->rsrc = &rn_rsrc;
+ chip->acp_hw_ops_init = acp31_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_acp_machines;
+ break;
+ case 0x6f:
+ chip->name = "acp_asoc_rembrandt";
+ chip->rsrc = &rmb_rsrc;
+ chip->acp_hw_ops_init = acp6x_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_rmb_acp_machines;
+ break;
+ case 0x63:
+ chip->name = "acp_asoc_acp63";
+ chip->rsrc = &acp63_rsrc;
+ chip->acp_hw_ops_init = acp63_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_acp63_acp_machines;
+ break;
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ chip->name = "acp_asoc_acp70";
+ chip->rsrc = &acp70_rsrc;
+ chip->acp_hw_ops_init = acp70_hw_ops_init;
+ chip->machines = snd_soc_acpi_amd_acp70_acp_machines;
+ break;
+ default:
+ dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
+ ret = -EINVAL;
+ goto release_regions;
+ }
+ chip->flag = flag;
+
+ addr = pci_resource_start(pci, 0);
+ chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
+ if (!chip->base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ chip->addr = addr;
+
+ chip->acp_hw_ops_init(chip);
+ ret = acp_hw_init(chip);
+ if (ret)
+ goto release_regions;
+
+ ret = devm_request_irq(dev, pci->irq, irq_handler,
+ IRQF_SHARED, "ACP_I2S_IRQ", chip);
+ if (ret) {
+ dev_err(&pci->dev, "ACP I2S IRQ request failed %d\n", ret);
+ goto de_init;
+ }
+
+ check_acp_config(pci, chip);
+ if (!chip->is_pdm_dev && !chip->is_i2s_config)
+ goto skip_pdev_creation;
+
+ ret = create_acp_platform_devs(pci, chip, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+
+ chip->chip_pdev = chip->acp_plat_dev;
+ chip->dev = &chip->acp_plat_dev->dev;
+
+ acp_machine_select(chip);
+
+ INIT_LIST_HEAD(&chip->stream_list);
+ spin_lock_init(&chip->acp_lock);
+skip_pdev_creation:
+ dev_set_drvdata(&pci->dev, chip);
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return ret;
+
+de_init:
+ acp_hw_deinit(chip);
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+};
+
+static int snd_acp_suspend(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_hw_deinit(chip);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_acp_resume(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_hw_init(chip);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ ret = acp_hw_en_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP en-interrupts failed\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops acp_pm_ops = {
+ RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
+static void acp_pci_remove(struct pci_dev *pci)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = pci_get_drvdata(pci);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ if (chip->dmic_codec_dev)
+ platform_device_unregister(chip->dmic_codec_dev);
+ if (chip->acp_plat_dev)
+ platform_device_unregister(chip->acp_plat_dev);
+ if (chip->mach_dev)
+ platform_device_unregister(chip->mach_dev);
+
+ ret = acp_hw_deinit(chip);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+}
+
+/* PCI IDs */
+static const struct pci_device_id acp_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, acp_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_amd_acp_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = acp_pci_ids,
+ .probe = acp_pci_probe,
+ .remove = acp_pci_remove,
+ .driver = {
+ .pm = pm_ptr(&acp_pm_ops),
+ },
+};
+module_pci_driver(snd_amd_acp_pci_driver);
+
+MODULE_DESCRIPTION("AMD ACP common PCI support");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c
new file mode 100644
index 000000000000..1bfc34c2aa53
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio PDM controller
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp-pdm"
+
+static int acp_dmic_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip;
+ u32 physical_addr, size_dmic, period_bytes;
+ unsigned int dmic_ctrl;
+
+ chip = dev_get_platdata(dev);
+ /* Enable default DMIC clk */
+ writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
+ dmic_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
+ dmic_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(dmic_ctrl, chip->base + ACP_WOV_MISC_CTRL);
+
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ size_dmic = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ physical_addr = ACP7x_DMIC_MEM_WINDOW_START;
+ else
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init DMIC Ring buffer */
+ writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
+ writel(size_dmic, chip->base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+
+ return 0;
+}
+
+static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int dma_enable;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ if (!(dma_enable & DMA_EN_MASK)) {
+ writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ }
+
+ ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE,
+ dma_enable, (dma_enable & DMA_EN_MASK),
+ DELAY_US, PDM_TIMEOUT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((dma_enable & DMA_EN_MASK)) {
+ writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
+
+ }
+
+ ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE,
+ dma_enable, !(dma_enable & DMA_EN_MASK),
+ DELAY_US, PDM_TIMEOUT);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int acp_dmic_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int channels, ch_mask;
+
+ channels = params_channels(hwparams);
+ switch (channels) {
+ case 2:
+ ch_mask = 0;
+ break;
+ case 4:
+ ch_mask = 1;
+ break;
+ case 6:
+ ch_mask = 2;
+ break;
+ default:
+ dev_err(dev, "Invalid channels %d\n", channels);
+ return -EINVAL;
+ }
+
+ chip->ch_mask = ch_mask;
+ if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) {
+ dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams));
+ return -EINVAL;
+ }
+
+ writel(ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ return 0;
+}
+
+static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ u32 ext_int_ctrl;
+
+ stream->dai_id = DMIC_INSTANCE;
+ stream->irq_bit = BIT(PDM_DMA_STAT);
+ stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET;
+ stream->reg_offset = ACP_REGION2_OFFSET;
+
+ /* Enable DMIC Interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+
+ return 0;
+}
+
+static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ u32 ext_int_ctrl;
+
+ /* Disable DMIC interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+}
+
+const struct snd_soc_dai_ops acp_dmic_dai_ops = {
+ .prepare = acp_dmic_prepare,
+ .hw_params = acp_dmic_hwparams,
+ .trigger = acp_dmic_dai_trigger,
+ .startup = acp_dmic_dai_startup,
+ .shutdown = acp_dmic_dai_shutdown,
+};
+EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP Audio PDM controller");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
new file mode 100644
index 000000000000..b25ac5612808
--- /dev/null
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic interface for ACP audio blck PCM component
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_i2s_dma"
+
+static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp6x_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp6x_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream)
+{
+ struct acp_resource *rsrc = chip->rsrc;
+ u32 reg_val;
+
+ reg_val = rsrc->sram_pte_offset;
+ stream->reg_offset = 0x02000000;
+
+ writel((reg_val + GRP1_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ writel((reg_val + GRP2_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+
+ writel(reg_val | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_5);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5);
+
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+}
+EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, "SND_SOC_ACP_COMMON");
+
+void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size)
+{
+ struct snd_pcm_substream *substream = stream->substream;
+ struct acp_resource *rsrc = chip->rsrc;
+ dma_addr_t addr = substream->dma_buffer.addr;
+ int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ u32 low, high, val;
+ u16 page_idx;
+
+ switch (chip->acp_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ switch (stream->dai_id) {
+ case I2S_SP_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x0;
+ else
+ val = 0x1000;
+ break;
+ case I2S_BT_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x2000;
+ else
+ val = 0x3000;
+ break;
+ case I2S_HS_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x4000;
+ else
+ val = 0x5000;
+ break;
+ case DMIC_INSTANCE:
+ val = 0x6000;
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", stream->dai_id);
+ return;
+ }
+ break;
+ default:
+ val = stream->pte_offset;
+ break;
+ }
+
+ for (page_idx = 0; page_idx < num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+ writel(low, chip->base + rsrc->scratch_reg_offset + val);
+ high |= BIT(31);
+ writel(high, chip->base + rsrc->scratch_reg_offset + val + 4);
+
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(config_acp_dma, "SND_SOC_ACP_COMMON");
+
+static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip;
+ struct acp_stream *stream;
+ int ret;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->substream = substream;
+ chip = dev_get_drvdata(dev->parent);
+ switch (chip->acp_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp6x_pcm_hardware_playback;
+ else
+ runtime->hw = acp6x_pcm_hardware_capture;
+ break;
+ default:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp_pcm_hardware_playback;
+ else
+ runtime->hw = acp_pcm_hardware_capture;
+ break;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, DMA_SIZE);
+ if (ret) {
+ dev_err(component->dev, "set hw constraint HW_PARAM_PERIOD_BYTES failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, DMA_SIZE);
+ if (ret) {
+ dev_err(component->dev, "set hw constraint HW_PARAM_BUFFER_BYTES failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(stream);
+ return ret;
+ }
+ runtime->private_data = stream;
+
+ writel(1, ACP_EXTERNAL_INTR_ENB(chip));
+
+ spin_lock_irq(&chip->acp_lock);
+ list_add_tail(&stream->list, &chip->stream_list);
+ spin_unlock_irq(&chip->acp_lock);
+
+ return ret;
+}
+
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u64 size = params_buffer_bytes(params);
+
+ /* Configure ACP DMA block with params */
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, size);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ bytescount = acp_get_byte_count(chip, stream->dai_id, substream->stream);
+
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+
+ pos = do_div(bytescount, buffersize);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+
+ /* Remove entry from list */
+ spin_lock_irq(&chip->acp_lock);
+ list_del(&stream->list);
+ spin_unlock_irq(&chip->acp_lock);
+ kfree(stream);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp_pcm_component = {
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .hw_params = acp_dma_hw_params,
+ .pointer = acp_dma_pointer,
+ .pcm_construct = acp_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+int acp_platform_register(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ struct snd_soc_dai_driver;
+ unsigned int status;
+
+ chip = dev_get_platdata(dev);
+ if (!chip || !chip->base) {
+ dev_err(dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ status = devm_snd_soc_register_component(dev, &acp_pcm_component,
+ chip->dai_driver,
+ chip->num_dai);
+ if (status) {
+ dev_err(dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_register, "SND_SOC_ACP_COMMON");
+
+int acp_platform_unregister(struct device *dev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP PCM Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
new file mode 100644
index 000000000000..7e9c07488dcc
--- /dev/null
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/amd/node.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_asoc_rembrandt"
+
+#define MP1_C2PMSG_69 0x3B10A14
+#define MP1_C2PMSG_85 0x3B10A54
+#define MP1_C2PMSG_93 0x3B10A74
+
+static struct snd_soc_dai_driver acp_rmb_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp6x_master_clock_generate(struct device *dev)
+{
+ int data, rc;
+
+ rc = amd_smn_write(0, MP1_C2PMSG_93, 0);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, MP1_C2PMSG_85, 0xC4);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, MP1_C2PMSG_69, 0x4);
+ if (rc)
+ return rc;
+
+ return read_poll_timeout(smn_read_register, data, data > 0, DELAY_US,
+ ACP_TIMEOUT, false, MP1_C2PMSG_93);
+}
+
+static int rembrandt_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ if (chip->acp_rev != ACP_RMB_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp_rmb_dai;
+ chip->num_dai = ARRAY_SIZE(acp_rmb_dai);
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
+ ret = acp6x_master_clock_generate(dev);
+ if (ret)
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void rembrandt_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int rmb_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk)
+ acp6x_master_clock_generate(dev);
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rmb_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume)
+};
+
+static struct platform_driver rembrandt_driver = {
+ .probe = rembrandt_audio_probe,
+ .remove = rembrandt_audio_remove,
+ .driver = {
+ .name = "acp_asoc_rembrandt",
+ .pm = pm_ptr(&rmb_dma_pm_ops),
+ },
+};
+
+module_platform_driver(rembrandt_driver);
+
+MODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
new file mode 100644
index 000000000000..04f6d70b6a92
--- /dev/null
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_asoc_renoir"
+
+static struct snd_soc_dai_driver acp_renoir_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+
+static int renoir_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ if (chip->acp_rev != ACP_RN_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp_renoir_dai;
+ chip->num_dai = ARRAY_SIZE(acp_renoir_dai);
+
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+
+ acp_platform_register(dev);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void renoir_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+}
+
+static int rn_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rn_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume)
+};
+
+static struct platform_driver renoir_driver = {
+ .probe = renoir_audio_probe,
+ .remove = renoir_audio_remove,
+ .driver = {
+ .name = "acp_asoc_renoir",
+ .pm = pm_ptr(&rn_dma_pm_ops),
+ },
+};
+
+module_platform_driver(renoir_driver);
+
+MODULE_DESCRIPTION("AMD ACP Renoir Driver");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
new file mode 100644
index 000000000000..fae94b9edd5a
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-legacy-mach - ASoC legacy Machine driver for AMD SoundWire platforms
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "soc_amd_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long soc_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_JACK_JDSRC(soc_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_JACK_JDSRC(soc_sdw_quirk));
+ if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+ if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR)
+ dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n");
+}
+
+static int soc_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ soc_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id soc_sdw_quirk_table[] = {
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD3"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD4"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {}
+};
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *soc_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf,
+ struct snd_soc_dai_link_component *sdw_platform_component)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *soc_end;
+ int cpu_pin_id;
+ int stream;
+ int ret;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (soc_end->name_prefix) {
+ (*codec_conf)->dlc.name = soc_end->codec_name;
+ (*codec_conf)->name_prefix = soc_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (soc_end->include_sidecar) {
+ ret = soc_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-PIN%d-PLAYBACK",
+ "SDW%d-PIN%d-CAPTURE",
+ "SDW%d-PIN%d-PLAYBACK-%s",
+ "SDW%d-PIN%d-CAPTURE-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ int num_cpus = hweight32(soc_dai->link_mask[stream]);
+ int num_codecs = soc_dai->num_devs[stream];
+ int playback, capture;
+ int j = 0;
+ char *name;
+
+ if (!soc_dai->num_devs[stream])
+ continue;
+
+ soc_end = list_first_entry(&soc_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = soc_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(soc_end->link_mask) - 1,
+ cpu_pin_id,
+ type_strings[soc_end->dai_info->dai_type]);
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(soc_end->link_mask) - 1,
+ cpu_pin_id);
+ }
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (!soc_end->dai_info->direction[stream])
+ continue;
+
+ int link_num = ffs(soc_end->link_mask) - 1;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, cpu_pin_id);
+ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ codec_maps[j].cpu = 0;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = soc_end->codec_name;
+ codecs[j].dai_name = soc_end->dai_info->dai_name;
+ j++;
+ }
+
+ WARN_ON(j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, sdw_platform_component,
+ 1, codecs, num_codecs,
+ 0, asoc_sdw_rtd_init, &sdw_ops);
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (soc_end->dai_info->init)
+ soc_end->dai_info->init(card, *dai_links,
+ soc_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *soc_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct snd_soc_dai_link_component *sdw_platform_component;
+ int ret;
+
+ sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!sdw_platform_component)
+ return -ENOMEM;
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ sdw_platform_component->name = "amd_ps_sdw_dma.0";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* generate DAI links by each sdw link */
+ while (soc_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, soc_dais, dai_links,
+ &current_be_id, codec_conf, sdw_platform_component);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ soc_dais++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct snd_soc_dai_link_component *pdm_cpu;
+ struct snd_soc_dai_link_component *pdm_platform;
+ int ret;
+
+ pdm_cpu = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!pdm_cpu)
+ return -ENOMEM;
+
+ pdm_platform = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!pdm_platform)
+ return -ENOMEM;
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ pdm_cpu->name = "acp_ps_pdm_dma.0";
+ pdm_platform->name = "acp_ps_pdm_dma.0";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *be_id = ACP_DMIC_BE_ID;
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
+ 0, 1, // DMIC only supports capture
+ pdm_cpu->name, pdm_platform->name,
+ "dmic-codec.0", "dmic-hifi", no_pcm,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int soc_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, dmic_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;
+ struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;
+ struct snd_soc_aux_dev *soc_aux;
+ struct snd_soc_codec_conf *codec_conf;
+ struct snd_soc_dai_link *dai_links;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_aux = 0;
+ int num_confs;
+ int num_links;
+ int be_id = 0;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ num_confs = num_ends;
+
+ /* One per DAI link, worst case is a DAI link for every endpoint */
+ soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
+ if (!soc_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
+ if (!soc_ends)
+ return -ENOMEM;
+
+ soc_aux = devm_kcalloc(dev, num_aux, sizeof(*soc_aux), GFP_KERNEL);
+ if (!soc_aux)
+ return -ENOMEM;
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs);
+ if (ret < 0)
+ return ret;
+
+ sdw_be_num = ret;
+
+ /* enable dmic */
+ if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
+ dmic_num = 1;
+
+ dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
+
+ codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf)
+ return -ENOMEM;
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + dmic_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links)
+ return -ENOMEM;
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_confs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+ card->aux_dev = soc_aux;
+ card->num_aux_devs = num_aux;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ soc_dais, &codec_conf);
+ if (ret)
+ return ret;
+ }
+
+ /* dmic */
+ if (dmic_num > 0) {
+ if (ctx->ignore_internal_dmic) {
+ dev_warn(dev, "Ignoring ACP DMIC\n");
+ } else {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id, 0);
+ if (ret)
+ return ret;
+ }
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+ return ret;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct amd_mc_ctx *amd_ctx;
+ struct asoc_sdw_mc_private *ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
+ if (!amd_ctx)
+ return -ENOMEM;
+
+ amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
+ amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ ctx->private = amd_ctx;
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "amd-soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = asoc_sdw_card_late_probe;
+
+ snd_soc_card_set_drvdata(card, ctx);
+ if (mach->mach_params.subsystem_id_set)
+ snd_soc_card_set_pci_ssid(card,
+ mach->mach_params.subsystem_vendor,
+ mach->mach_params.subsystem_device);
+
+ dmi_check_system(soc_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ soc_sdw_quirk, quirk_override);
+ soc_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = soc_sdw_quirk;
+ dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk);
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = soc_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+ if (mach->mach_params.dmic_num) {
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:dmic cfg-mics:%d",
+ card->components,
+ mach->mach_params.dmic_num);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "amd_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver soc_sdw_driver = {
+ .driver = {
+ .name = "amd_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(soc_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
+MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
diff --git a/sound/soc/amd/acp/acp-sdw-mach-common.c b/sound/soc/amd/acp/acp-sdw-mach-common.c
new file mode 100644
index 000000000000..e5f394dc2f4c
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-mach-common.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-mach-common - Common machine driver helper functions for
+ * legacy(No DSP) stack and SOF stack.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include "soc_amd_sdw_common.h"
+
+int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW1_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ case SOC_SDW_AMP_IN_DAI_ID:
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP63_SW1_AUDIO0_RX;
+ break;
+ default:
+ dev_err(dev, "invalid be_id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp63_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(dev, "sdw_link_id:%d, be_id:%d, cpu_pin_id:%d\n", sdw_link_id, be_id, *cpu_pin_id);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp70_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
+MODULE_DESCRIPTION("AMD SoundWire Common Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
new file mode 100644
index 000000000000..5677ae63fca9
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "soc_amd_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long sof_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_JACK_JDSRC(sof_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_JACK_JDSRC(sof_sdw_quirk));
+ if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+}
+
+static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_sdw_quirk_table[] = {
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {}
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:04:00.5",
+ }
+};
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *sof_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *sof_end;
+ int cpu_pin_id;
+ int stream;
+ int ret;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->name_prefix) {
+ (*codec_conf)->dlc.name = sof_end->codec_name;
+ (*codec_conf)->name_prefix = sof_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (sof_end->include_sidecar) {
+ ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-PIN%d-PLAYBACK",
+ "SDW%d-PIN%d-CAPTURE",
+ "SDW%d-PIN%d-PLAYBACK-%s",
+ "SDW%d-PIN%d-CAPTURE-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ int num_cpus = hweight32(sof_dai->link_mask[stream]);
+ int num_codecs = sof_dai->num_devs[stream];
+ int playback, capture;
+ int j = 0;
+ char *name;
+
+ if (!sof_dai->num_devs[stream])
+ continue;
+
+ sof_end = list_first_entry(&sof_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = sof_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(sof_end->link_mask) - 1,
+ cpu_pin_id,
+ type_strings[sof_end->dai_info->dai_type]);
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(sof_end->link_mask) - 1,
+ cpu_pin_id);
+ }
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (!sof_end->dai_info->direction[stream])
+ continue;
+
+ int link_num = ffs(sof_end->link_mask) - 1;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, cpu_pin_id);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
+
+ codec_maps[j].cpu = 0;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = sof_end->codec_name;
+ codecs[j].dai_name = sof_end->dai_info->dai_name;
+ j++;
+ }
+
+ WARN_ON(j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, platform_component,
+ ARRAY_SIZE(platform_component), codecs, num_codecs,
+ 1, asoc_sdw_rtd_init, &sdw_ops);
+
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->dai_info->init)
+ sof_end->dai_info->init(card, *dai_links,
+ sof_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *sof_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ int ret;
+
+ /* generate DAI links by each sdw link */
+ while (sof_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, sof_dais, dai_links,
+ &current_be_id, codec_conf);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ sof_dais++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
+{
+ struct device *dev = card->dev;
+ int ret;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
+ 0, 1, // DMIC only supports capture
+ "acp-sof-dmic", platform_component->name,
+ "dmic-codec", "dmic-hifi", no_pcm,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int sof_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, dmic_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
+ struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
+ struct snd_soc_aux_dev *sof_aux;
+ struct snd_soc_codec_conf *codec_conf;
+ struct snd_soc_dai_link *dai_links;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_aux = 0;
+ int num_links;
+ int be_id = 0;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ /* One per DAI link, worst case is a DAI link for every endpoint */
+ sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
+ if (!sof_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
+ if (!sof_ends)
+ return -ENOMEM;
+
+ sof_aux = devm_kcalloc(dev, num_aux, sizeof(*sof_aux), GFP_KERNEL);
+ if (!sof_aux)
+ return -ENOMEM;
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs);
+ if (ret < 0)
+ return ret;
+
+ sdw_be_num = ret;
+
+ /* enable dmic */
+ if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
+ dmic_num = 1;
+
+ dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
+
+ codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf)
+ return -ENOMEM;
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + dmic_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links)
+ return -ENOMEM;
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_devs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+ card->aux_dev = sof_aux;
+ card->num_aux_devs = num_aux;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ sof_dais, &codec_conf);
+ if (ret)
+ return ret;
+ }
+
+ /* dmic */
+ if (dmic_num > 0) {
+ if (ctx->ignore_internal_dmic) {
+ dev_warn(dev, "Ignoring ACP DMIC\n");
+ } else {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id, 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+ return ret;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct amd_mc_ctx *amd_ctx;
+ struct asoc_sdw_mc_private *ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
+ if (!amd_ctx)
+ return -ENOMEM;
+
+ amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
+ amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ ctx->private = amd_ctx;
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "amd-soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = asoc_sdw_card_late_probe;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ dmi_check_system(sof_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ sof_sdw_quirk, quirk_override);
+ sof_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = sof_sdw_quirk;
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = sof_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "amd_sof_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver sof_sdw_driver = {
+ .driver = {
+ .name = "amd_sof_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(sof_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
+MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
new file mode 100644
index 000000000000..6215e31ecedd
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * SOF Machine Driver Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+
+static struct acp_card_drvdata sof_rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_nau8825_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = NAU8825,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+};
+
+static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+};
+
+static struct acp_card_drvdata sof_nau8821_max98388_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_HS,
+ .bt_cpu_id = I2S_BT,
+ .hs_codec_id = NAU8821,
+ .amp_codec_id = MAX98388,
+ .soc_mclk = true,
+};
+
+static int acp_sof_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry)
+ return -EINVAL;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ /* Widgets and controls added per-codec in acp-mach-common.c */
+
+ acp_card_drvdata = card->drvdata;
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
+ ret = acp_sofdsp_dai_links_create(card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n");
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register card(%s)\n", card->name);
+ return 0;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "rt5682-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data
+ },
+ {
+ .name = "rt5682-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data
+ },
+ {
+ .name = "rt5682s-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data
+ },
+ {
+ .name = "rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data
+ },
+ {
+ .name = "nau8825-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8825_data
+ },
+ {
+ .name = "rt5682s-hs-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data
+ },
+ {
+ .name = "nau8821-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .name = "sof_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp_sof_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS("SND_SOC_AMD_MACH");
+MODULE_DESCRIPTION("ACP SOF Machine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
new file mode 100644
index 000000000000..141ea4787d99
--- /dev/null
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD ACP Audio engine using ES8336 codec.
+//
+// Copyright 2023 Marian Postevca <posteuca@mutex.one>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc-acpi.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/string_choices.h>
+#include "../acp-mach.h"
+#include "acp3x-es83xx.h"
+
+#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv))
+
+#define DUAL_CHANNEL 2
+
+#define ES83XX_ENABLE_DMIC BIT(4)
+#define ES83XX_48_MHZ_MCLK BIT(5)
+
+struct acp3x_es83xx_private {
+ bool speaker_on;
+ bool headphone_on;
+ unsigned long quirk;
+ struct snd_soc_component *codec;
+ struct device *codec_dev;
+ struct gpio_desc *gpio_speakers, *gpio_headphone;
+ struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio;
+ struct acpi_gpio_mapping gpio_mapping[3];
+ struct snd_soc_dapm_route mic_map[2];
+};
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256)
+#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000)
+
+static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct acp3x_es83xx_private *priv;
+ unsigned int freq;
+ int ret;
+
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ priv = get_mach_priv(rtd->card);
+
+ if (priv->quirk & ES83XX_48_MHZ_MCLK) {
+ dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n");
+ freq = ES83xx_48_MHZ_MCLK_FREQ;
+ } else {
+ dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n");
+ freq = ES83xx_12288_KHZ_MCLK_FREQ;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ return 0;
+}
+
+static struct snd_soc_jack es83xx_jack;
+
+static struct snd_soc_jack_pin es83xx_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Headphone Power", SND_SOC_NOPM, 0, 0,
+ acp3x_es83xx_headphone_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ acp3x_es83xx_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Headphone", NULL, "Headphone Power"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled Speaker and/or headphone switch.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+
+static const struct snd_kcontrol_new acp3x_es83xx_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card)
+{
+ card->dapm_widgets = acp3x_es83xx_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets);
+ card->controls = acp3x_es83xx_controls;
+ card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls);
+ card->dapm_routes = acp3x_es83xx_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map);
+
+ return 0;
+}
+
+static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ dev_dbg(priv->codec_dev, "headphone power event = %d\n", event);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->headphone_on = true;
+ else
+ priv->headphone_on = false;
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);
+
+ return 0;
+}
+
+static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ dev_dbg(priv->codec_dev, "speaker power event: %d\n", event);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->speaker_on = true;
+ else
+ priv->speaker_on = false;
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);
+
+ return 0;
+}
+
+static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ /* We need to disable the jack in the machine driver suspend
+ * callback so that the CODEC suspend callback actually gets
+ * called. Without doing it, the CODEC suspend/resume
+ * callbacks do not get called if headphones are plugged in.
+ * This is because plugging in headphones keeps some supplies
+ * active, this in turn means that the lowest bias level
+ * that the CODEC can go to is SND_SOC_BIAS_STANDBY.
+ * If components do not set idle_bias_on to true then
+ * their suspend/resume callbacks do not get called.
+ */
+ dev_dbg(priv->codec_dev, "card suspend\n");
+ snd_soc_component_set_jack(priv->codec, NULL, NULL);
+ return 0;
+}
+
+static int acp3x_es83xx_resume_post(struct snd_soc_card *card)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ /* We disabled jack detection in suspend callback,
+ * enable it back.
+ */
+ dev_dbg(priv->codec_dev, "card resume\n");
+ snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL);
+ return 0;
+}
+
+static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
+{
+
+ priv->enable_spk_gpio.crs_entry_index = 0;
+ priv->enable_hp_gpio.crs_entry_index = 1;
+
+ priv->enable_spk_gpio.active_low = false;
+ priv->enable_hp_gpio.active_low = false;
+
+ priv->gpio_mapping[0].name = "speakers-enable-gpios";
+ priv->gpio_mapping[0].data = &priv->enable_spk_gpio;
+ priv->gpio_mapping[0].size = 1;
+ priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
+
+ priv->gpio_mapping[1].name = "headphone-enable-gpios";
+ priv->gpio_mapping[1].data = &priv->enable_hp_gpio;
+ priv->gpio_mapping[1].size = 1;
+ priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
+
+ dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n",
+ priv->enable_spk_gpio.crs_entry_index,
+ str_low_high(priv->enable_spk_gpio.active_low),
+ priv->enable_hp_gpio.crs_entry_index,
+ str_low_high(priv->enable_hp_gpio.active_low));
+ return 0;
+}
+
+static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv)
+{
+ int num_routes = 0;
+ int i;
+
+ if (!(priv->quirk & ES83XX_ENABLE_DMIC)) {
+ priv->mic_map[num_routes].sink = "MIC1";
+ priv->mic_map[num_routes].source = "Internal Mic";
+ num_routes++;
+ }
+
+ priv->mic_map[num_routes].sink = "MIC2";
+ priv->mic_map[num_routes].source = "Headset Mic";
+ num_routes++;
+
+ for (i = 0; i < num_routes; i++)
+ dev_info(priv->codec_dev, "%s is %s\n",
+ priv->mic_map[i].source, priv->mic_map[i].sink);
+
+ return num_routes;
+}
+
+static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+ int ret = 0;
+ int num_routes;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &es83xx_jack, es83xx_jack_pins,
+ ARRAY_SIZE(es83xx_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+
+ snd_soc_component_set_jack(codec, &es83xx_jack, NULL);
+
+ priv->codec = codec;
+ acp3x_es83xx_configure_gpios(priv);
+
+ ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping);
+ if (ret)
+ dev_warn(priv->codec_dev, "failed to add speaker gpio\n");
+
+ priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable",
+ priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_speakers)) {
+ dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n");
+ return PTR_ERR(priv->gpio_speakers);
+ }
+
+ priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable",
+ priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_headphone)) {
+ dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n");
+ return PTR_ERR(priv->gpio_headphone);
+ }
+
+ num_routes = acp3x_es83xx_configure_mics(priv);
+ if (num_routes > 0) {
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+
+ ret = snd_soc_dapm_add_routes(dapm, priv->mic_map, num_routes);
+ if (ret != 0)
+ device_remove_software_node(priv->codec_dev);
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp3x_es83xx_ops = {
+ .startup = acp3x_es83xx_codec_startup,
+};
+
+
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+
+static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {}
+};
+
+static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)
+{
+ link->codecs = codec;
+ link->num_codecs = ARRAY_SIZE(codec);
+ link->init = acp3x_es83xx_init;
+ link->ops = &acp3x_es83xx_ops;
+ link->dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ return 0;
+}
+
+static int acp3x_es83xx_probe(struct snd_soc_card *card)
+{
+ int ret = 0;
+ struct device *dev = card->dev;
+ const struct dmi_system_id *dmi_id;
+
+ dmi_id = dmi_first_match(acp3x_es83xx_dmi_table);
+ if (dmi_id && dmi_id->driver_data) {
+ struct acp3x_es83xx_private *priv;
+ struct acp_card_drvdata *acp_drvdata;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+
+ acp_drvdata = (struct acp_card_drvdata *)card->drvdata;
+
+ dev_info(dev, "matched DMI table with this system, trying to register sound card\n");
+
+ adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1);
+ if (!adev) {
+ dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id);
+ return -ENXIO;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev) {
+ dev_warn(dev, "Error cannot find codec device, will defer probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ put_device(codec_dev);
+ return -ENOMEM;
+ }
+
+ priv->codec_dev = codec_dev;
+ priv->quirk = (unsigned long)dmi_id->driver_data;
+ acp_drvdata->mach_priv = priv;
+ dev_info(dev, "successfully probed the sound card\n");
+ } else {
+ ret = -ENODEV;
+ dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n");
+ }
+ return ret;
+}
+
+
+void acp3x_es83xx_init_ops(struct acp_mach_ops *ops)
+{
+ ops->probe = acp3x_es83xx_probe;
+ ops->configure_widgets = acp3x_es83xx_configure_widgets;
+ ops->configure_link = acp3x_es83xx_configure_link;
+ ops->suspend_pre = acp3x_es83xx_suspend_pre;
+ ops->resume_post = acp3x_es83xx_resume_post;
+}
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h
new file mode 100644
index 000000000000..03551ffdd9da
--- /dev/null
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2023 Marian Postevca <posteuca@mutex.one>
+ */
+
+#ifndef __ACP3X_ES83XX_H
+#define __ACP3X_ES83XX_H
+
+void acp3x_es83xx_init_ops(struct acp_mach_ops *ops);
+
+#endif
+
diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c
new file mode 100644
index 000000000000..10fb416b959d
--- /dev/null
+++ b/sound/soc/amd/acp/acp63.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
+/*
+ * Hardware interface for ACP6.3 block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/pci.h>
+
+#include <asm/amd/node.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_asoc_acp63"
+
+#define CLK_PLL_PWR_REQ_N0 0X0006C2C0
+#define CLK_SPLL_FIELD_2_N0 0X0006C114
+#define CLK_PLL_REQ_N0 0X0006C0DC
+#define CLK_DFSBYPASS_CONTR 0X0006C2C8
+#define CLK_DFS_CNTL_N0 0X0006C1A4
+
+#define PLL_AUTO_STOP_REQ BIT(4)
+#define PLL_AUTO_START_REQ BIT(0)
+#define PLL_FRANCE_EN BIT(4)
+#define EXIT_DPF_BYPASS_0 BIT(16)
+#define EXIT_DPF_BYPASS_1 BIT(17)
+#define CLK0_DIVIDER 0X30
+
+union clk_pll_req_no {
+ struct {
+ u32 fb_mult_int : 9;
+ u32 reserved : 3;
+ u32 pll_spine_div : 4;
+ u32 gb_mult_frac : 16;
+ } bitfields, bits;
+ u32 clk_pll_req_no_reg;
+};
+
+static struct snd_soc_dai_driver acp63_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp63_i2s_master_clock_generate(struct acp_chip_info *chip)
+{
+ int rc;
+ u32 data;
+ union clk_pll_req_no clk_pll;
+
+ /* Clk5 pll register values to get mclk as 196.6MHz*/
+ clk_pll.bits.fb_mult_int = 0x31;
+ clk_pll.bits.pll_spine_div = 0;
+ clk_pll.bits.gb_mult_frac = 0x26E9;
+
+ rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_SPLL_FIELD_2_N0, &data);
+ if (rc)
+ return rc;
+ if (data & PLL_FRANCE_EN) {
+ rc = amd_smn_write(0, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN);
+ if (rc)
+ return rc;
+ }
+
+ rc = amd_smn_write(0, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_DFSBYPASS_CONTR, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1);
+ if (rc)
+ return rc;
+
+ return amd_smn_write(0, CLK_DFS_CNTL_N0, CLK0_DIVIDER);
+}
+
+static int acp63_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ if (chip->acp_rev != ACP63_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp63_dai;
+ chip->num_dai = ARRAY_SIZE(acp63_dai);
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
+ ret = acp63_i2s_master_clock_generate(chip);
+ if (ret)
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk)
+ acp63_i2s_master_clock_generate(chip);
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops acp63_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume)
+};
+
+static struct platform_driver acp63_driver = {
+ .probe = acp63_audio_probe,
+ .remove = acp63_audio_remove,
+ .driver = {
+ .name = "acp_asoc_acp63",
+ .pm = pm_ptr(&acp63_dma_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_driver);
+
+MODULE_DESCRIPTION("AMD ACP acp63 Driver");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c
new file mode 100644
index 000000000000..bca311c88139
--- /dev/null
+++ b/sound/soc/amd/acp/acp70.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
+/*
+ * Hardware interface for ACP7.0 block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/pci.h>
+#include "amd.h"
+#include "acp-mach.h"
+
+#include <asm/amd/node.h>
+
+#define DRV_NAME "acp_asoc_acp70"
+
+#define CLK7_CLK0_DFS_CNTL_N1 0X0006C1A4
+#define CLK0_DIVIDER 0X19
+
+static struct snd_soc_dai_driver acp70_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp_acp70_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (chip->acp_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp70_dai;
+ chip->num_dai = ARRAY_SIZE(acp70_dai);
+
+ /* Set clk7 DFS clock divider register value to get mclk as 196.608MHz*/
+ ret = amd_smn_write(0, CLK7_CLK0_DFS_CNTL_N1, CLK0_DIVIDER);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set I2S master clock as 196.608MHz\n");
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp_acp70_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp70_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops acp70_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume)
+};
+
+static struct platform_driver acp70_driver = {
+ .probe = acp_acp70_audio_probe,
+ .remove = acp_acp70_audio_remove,
+ .driver = {
+ .name = "acp_asoc_acp70",
+ .pm = pm_ptr(&acp70_dma_pm_ops),
+ },
+};
+
+module_platform_driver(acp70_driver);
+
+MODULE_DESCRIPTION("AMD ACP ACP70 Driver");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp_common.h b/sound/soc/amd/acp/acp_common.h
new file mode 100644
index 000000000000..984685602e3d
--- /dev/null
+++ b/sound/soc/amd/acp/acp_common.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
+ */
+
+/*
+ * acp_common.h - acp common header file
+ */
+
+#ifndef __ACP_COMMON_H
+#define __ACP_COMMON_H
+
+#define ACP_RN_PCI_ID 0x01
+#define ACP_VANGOGH_PCI_ID 0x50
+#define ACP_RMB_PCI_ID 0x6F
+#define ACP63_PCI_ID 0x63
+#define ACP70_PCI_ID 0x70
+#define ACP71_PCI_ID 0x71
+#define ACP72_PCI_ID 0x72
+
+#endif
diff --git a/sound/soc/amd/acp/amd-acp63-acpi-match.c b/sound/soc/amd/acp/amd-acp63-acpi-match.c
new file mode 100644
index 000000000000..9b6a49c051cd
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp63-acpi-match.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp63-acpi-match.c - tables and support for ACP 6.3 platform
+ * ACPI enumeration.
+ *
+ * Copyright 2024 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp63_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp63_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp63_4_in_1_sdca,
+ .drv_name = "amd_sof_sdw",
+ .sof_tplg_filename = "sof-acp_6_3-rt711-l0-rt1316-l0-rt714-l1.tplg",
+ .fw_filename = "sof-acp_6_3.ri",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_sdw_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = acp63_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp63_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP6.3 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
new file mode 100644
index 000000000000..871b4f054a84
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp70-acpi-match.c - tables and support for ACP 7.0 & ACP7.1
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_single_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+ {
+ .adr = 0x00003001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = {
+ {
+ .adr = 0x00013001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ },
+ {
+ .adr = 0x00013001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00013201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00013301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56x4_1_adr[] = {
+ {
+ .adr = 0x00013301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00013101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00013001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1_cs35l56x4_l1[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l43_1_cs35l56x4_1_adr),
+ .adr_d = cs42l43_1_cs35l56x4_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0_cs35l56x4_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56x4_1_adr),
+ .adr_d = cs35l56x4_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56x4_1_adr),
+ .adr_d = cs35l56x4_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_single_adr),
+ .adr_d = rt1320_1_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_rt722_l0_rt1320_l1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l43_l0_cs35l56x4_l1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(1),
+ .links = acp70_cs42l43_l1_cs35l56x4_l1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(1),
+ .links = acp70_cs35l56x4_l1,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sof_sdw",
+ .sof_tplg_filename = "sof-acp_7_0-rt722-l0.tplg",
+ .fw_filename = "sof-acp_7_0.ri",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c
new file mode 100644
index 000000000000..27da2a862f1c
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acpi-mach.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acpi-match.c - tables and support for ACP platforms
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "acp3xalc56821019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "acp3xalc5682sm98360",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "acp3xalc5682s1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-acp",
+ },
+ {
+ .id = "ESSX8336",
+ .drv_name = "acp3x-es83xx",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
+ {
+ .id = "10508825",
+ .drv_name = "rmb-nau8825-max",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ },
+ {
+ .id = "AMDI0007",
+ .drv_name = "rembrandt-acp",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rmb-rt5682s-rt1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_rmb_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[] = {
+ {
+ .id = "AMDI0052",
+ .drv_name = "acp63-acp",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp63_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[] = {
+ {
+ .id = "AMDI0029",
+ .drv_name = "acp70-acp",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp70_acp_machines, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Venkataprasad.potturu@amd.com");
diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c
new file mode 100644
index 000000000000..0160b0df26a0
--- /dev/null
+++ b/sound/soc/amd/acp/amd-sdw-acpi.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+
+/*
+ * SDW AMD ACPI scan helper function
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw_amd.h>
+#include <linux/string.h>
+
+int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+ u32 sdw_bitmap = 0;
+ u8 count = 0;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ /* Found controller, find links supported */
+ ret = fwnode_property_read_u32_array(acpi_fwnode_handle(adev),
+ "mipi-sdw-manager-list", &sdw_bitmap, 1);
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-manager-list: %d\n", ret);
+ return -EINVAL;
+ }
+ count = hweight32(sdw_bitmap);
+ /* Check count is within bounds */
+ if (count > info->count) {
+ dev_err(&adev->dev, "Manager count %d exceeds max %d\n",
+ count, info->count);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ dev_dbg(&adev->dev, "No SoundWire Managers detected\n");
+ return -EINVAL;
+ }
+ dev_dbg(&adev->dev, "ACPI reports %d SoundWire Manager devices\n", count);
+ info->link_mask = sdw_bitmap;
+ return 0;
+}
+EXPORT_SYMBOL_NS(amd_sdw_scan_controller, "SND_AMD_SOUNDWIRE_ACPI");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("AMD SoundWire ACPI helpers");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
new file mode 100644
index 000000000000..73a028e67246
--- /dev/null
+++ b/sound/soc/amd/acp/amd.h
@@ -0,0 +1,365 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef __AMD_ACP_H
+#define __AMD_ACP_H
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+
+#include "acp_common.h"
+#include "chip_offset_byte.h"
+
+#define DMIC_INSTANCE 0x00
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+#define I2S_HS_INSTANCE 0x03
+
+#define MEM_WINDOW_START 0x4080000
+
+#define ACP_I2S_REG_START 0x1242400
+#define ACP_I2S_REG_END 0x1242810
+#define ACP3x_I2STDM_REG_START 0x1242400
+#define ACP3x_I2STDM_REG_END 0x1242410
+#define ACP3x_BT_TDM_REG_START 0x1242800
+#define ACP3x_BT_TDM_REG_END 0x1242810
+
+#define THRESHOLD(bit, base) ((bit) + (base))
+#define I2S_RX_THRESHOLD(base) THRESHOLD(7, base)
+#define I2S_TX_THRESHOLD(base) THRESHOLD(8, base)
+#define BT_TX_THRESHOLD(base) THRESHOLD(6, base)
+#define BT_RX_THRESHOLD(base) THRESHOLD(5, base)
+#define HS_TX_THRESHOLD(base) THRESHOLD(4, base)
+#define HS_RX_THRESHOLD(base) THRESHOLD(3, base)
+
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
+#define ACP_SRAM_PDM_PTE_OFFSET 0x400
+#define ACP_SRAM_HS_PB_PTE_OFFSET 0x500
+#define ACP_SRAM_HS_CP_PTE_OFFSET 0x600
+#define PAGE_SIZE_4K_ENABLE 0x2
+
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
+#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
+#define I2S_HS_TX_MEM_WINDOW_START 0x40A0000
+#define I2S_HS_RX_MEM_WINDOW_START 0x40C0000
+
+#define ACP7x_I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define ACP7x_I2S_SP_RX_MEM_WINDOW_START 0x4200000
+#define ACP7x_I2S_BT_TX_MEM_WINDOW_START 0x4400000
+#define ACP7x_I2S_BT_RX_MEM_WINDOW_START 0x4600000
+#define ACP7x_I2S_HS_TX_MEM_WINDOW_START 0x4800000
+#define ACP7x_I2S_HS_RX_MEM_WINDOW_START 0x4A00000
+#define ACP7x_DMIC_MEM_WINDOW_START 0x4C00000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define BT_PB_FIFO_ADDR_OFFSET 0x900
+#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define HS_PB_FIFO_ADDR_OFFSET 0xD00
+#define HS_CAPT_FIFO_ADDR_OFFSET 0xF00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER 65536
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+#define ACP_MAX_STREAM 8
+
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+
+#define SLOT_WIDTH_8 0x8
+#define SLOT_WIDTH_16 0x10
+#define SLOT_WIDTH_24 0x18
+#define SLOT_WIDTH_32 0x20
+
+#define ACP6X_PGFSM_CONTROL 0x1024
+#define ACP6X_PGFSM_STATUS 0x1028
+
+#define ACP63_PGFSM_CONTROL ACP6X_PGFSM_CONTROL
+#define ACP63_PGFSM_STATUS ACP6X_PGFSM_STATUS
+
+#define ACP70_PGFSM_CONTROL ACP6X_PGFSM_CONTROL
+#define ACP70_PGFSM_STATUS ACP6X_PGFSM_STATUS
+
+#define ACP_ZSC_DSP_CTRL 0x0001014
+#define ACP_ZSC_STS 0x0001018
+#define ACP_SOFT_RST_DONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0xffffffff
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xffffffff
+
+#define ACP_TIMEOUT 500
+#define DELAY_US 5
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK 0x10000
+#define PDM_DEC_64 0x2
+#define PDM_CLK_FREQ_MASK 0x07
+#define PDM_MISC_CTRL_MASK 0x18
+#define PDM_ENABLE 0x01
+#define PDM_DISABLE 0x00
+#define DMA_EN_MASK 0x02
+#define DELAY_US 5
+#define PDM_TIMEOUT 1000
+#define ACP_REGION2_OFFSET 0x02000000
+
+struct acp_chip_info {
+ char *name; /* Platform name */
+ struct resource *res;
+ struct device *dev;
+ struct snd_soc_dai_driver *dai_driver;
+
+ unsigned int acp_rev; /* ACP Revision id */
+ void __iomem *base; /* ACP memory PCI base */
+ struct snd_acp_hw_ops *acp_hw_ops;
+ int (*acp_hw_ops_init)(struct acp_chip_info *chip);
+ struct platform_device *chip_pdev;
+ struct acp_resource *rsrc; /* Platform specific resources*/
+ struct list_head stream_list;
+ spinlock_t acp_lock; /* Used to protect stream_list */
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *acp_plat_dev;
+ struct platform_device *mach_dev;
+ struct snd_soc_acpi_mach *machines;
+ int num_dai;
+ u32 addr;
+ u32 bclk_div;
+ u32 lrclk_div;
+ u32 ch_mask;
+ u32 tdm_tx_fmt[3];
+ u32 tdm_rx_fmt[3];
+ u32 xfer_tx_resolution[3];
+ u32 xfer_rx_resolution[3];
+ unsigned int flag; /* Distinguish b/w Legacy or Only PDM */
+ bool is_pdm_dev; /* flag set to true when ACP PDM controller exists */
+ bool is_pdm_config; /* flag set to true when PDM configuration is selected from BIOS */
+ bool is_i2s_config; /* flag set to true when I2S configuration is selected from BIOS */
+ bool tdm_mode;
+};
+
+struct acp_stream {
+ struct list_head list;
+ struct snd_pcm_substream *substream;
+ int irq_bit;
+ int dai_id;
+ int id;
+ int dir;
+ u64 bytescount;
+ u32 reg_offset;
+ u32 pte_offset;
+ u32 fifo_offset;
+};
+
+struct acp_resource {
+ int offset;
+ int no_of_ctrls;
+ int irqp_used;
+ bool soc_mclk;
+ u32 irq_reg_offset;
+ u64 scratch_reg_offset;
+ u64 sram_pte_offset;
+};
+
+/**
+ * struct snd_acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @irq: ACP irq handler
+ * @en_interrupts: ACP enable interrupts
+ * @dis_interrupts: ACP disable interrupts
+ */
+struct snd_acp_hw_ops {
+ /* ACP hardware initilizations */
+ int (*acp_init)(struct acp_chip_info *chip);
+ int (*acp_deinit)(struct acp_chip_info *chip);
+
+ /* ACP Interrupts*/
+ irqreturn_t (*irq)(int irq, void *data);
+ int (*en_interrupts)(struct acp_chip_info *chip);
+ int (*dis_interrupts)(struct acp_chip_info *chip);
+};
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+extern struct acp_resource rn_rsrc;
+extern struct acp_resource rmb_rsrc;
+extern struct acp_resource acp63_rsrc;
+extern struct acp_resource acp70_rsrc;
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[];
+
+extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
+extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
+
+int acp_platform_register(struct device *dev);
+int acp_platform_unregister(struct device *dev);
+
+int acp_machine_select(struct acp_chip_info *chip);
+
+int acp_init(struct acp_chip_info *chip);
+int acp_deinit(struct acp_chip_info *chip);
+int acp_enable_interrupts(struct acp_chip_info *chip);
+int acp_disable_interrupts(struct acp_chip_info *chip);
+irqreturn_t acp_irq_handler(int irq, void *data);
+
+extern struct snd_acp_hw_ops acp31_common_hw_ops;
+extern struct snd_acp_hw_ops acp6x_common_hw_ops;
+extern struct snd_acp_hw_ops acp63_common_hw_ops;
+extern struct snd_acp_hw_ops acp70_common_hw_ops;
+extern int acp31_hw_ops_init(struct acp_chip_info *chip);
+extern int acp6x_hw_ops_init(struct acp_chip_info *chip);
+extern int acp63_hw_ops_init(struct acp_chip_info *chip);
+extern int acp70_hw_ops_init(struct acp_chip_info *chip);
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream);
+void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size);
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip);
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip, struct acp_stream *stream);
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip);
+
+static inline int acp_hw_init(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_init)
+ return chip->acp_hw_ops->acp_init(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_deinit)
+ return chip->acp_hw_ops->acp_deinit(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_en_interrupts(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->en_interrupts)
+ return chip->acp_hw_ops->en_interrupts(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_dis_interrupts(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->dis_interrupts)
+ chip->acp_hw_ops->dis_interrupts(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline u64 acp_get_byte_count(struct acp_chip_info *chip, int dai_id, int direction)
+{
+ u64 byte_count = 0, low = 0, high = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", dai_id);
+ goto POINTER_RETURN_BYTES;
+ }
+ } else {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case DMIC_INSTANCE:
+ high = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", dai_id);
+ goto POINTER_RETURN_BYTES;
+ }
+ }
+ /* Get 64 bit value from two 32 bit registers */
+ byte_count = (high << 32) | low;
+
+POINTER_RETURN_BYTES:
+ return byte_count;
+}
+#endif
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
new file mode 100644
index 000000000000..82275c9de53a
--- /dev/null
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef _ACP_IP_OFFSET_HEADER
+#define _ACP_IP_OFFSET_HEADER
+
+#define ACPAXI2AXI_ATU_CTRL 0xC40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+
+#define GRP1_OFFSET 0x0
+#define GRP2_OFFSET 0x4000
+
+#define ACP_PGFSM_CONTROL 0x141C
+#define ACP_PGFSM_STATUS 0x1420
+#define ACP_SOFT_RESET 0x1000
+#define ACP_CONTROL 0x1004
+#define ACP_PIN_CONFIG 0x1440
+#define ACP3X_PIN_CONFIG 0x1400
+
+#define ACP_EXTERNAL_INTR_REG_ADDR(chip, offset, ctrl) \
+ (chip->base + chip->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
+
+#define ACP_EXTERNAL_INTR_ENB(chip) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x0, 0x0)
+#define ACP_EXTERNAL_INTR_CNTL(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x4, ctrl)
+#define ACP_EXTERNAL_INTR_STAT(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, \
+ (0x4 + (chip->rsrc->no_of_ctrls * 0x04)), ctrl)
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+
+#define ACP_I2S_REG_ADDR(acp_adata, addr) \
+ ((addr) + (acp_adata->rsrc->irqp_used * \
+ acp_adata->rsrc->irq_reg_offset))
+
+#define ACP_I2S_RX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2000)
+#define ACP_I2S_RX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2004)
+#define ACP_I2S_RX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2008)
+#define ACP_I2S_RX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x200C)
+#define ACP_I2S_RX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2010)
+#define ACP_I2S_RX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2014)
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2018)
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x201C)
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2020)
+#define ACP_I2S_TX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2024)
+#define ACP_I2S_TX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2028)
+#define ACP_I2S_TX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x202C)
+#define ACP_I2S_TX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2030)
+#define ACP_I2S_TX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2034)
+#define ACP_I2S_TX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2038)
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x203C)
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2040)
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2044)
+#define ACP_BT_RX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2048)
+#define ACP_BT_RX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x204C)
+#define ACP_BT_RX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2050)
+#define ACP_BT_RX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2054)
+#define ACP_BT_RX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2058)
+#define ACP_BT_RX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x205C)
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2060)
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2064)
+#define ACP_BT_RX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2068)
+#define ACP_BT_TX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x206C)
+#define ACP_BT_TX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2070)
+#define ACP_BT_TX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2074)
+#define ACP_BT_TX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2078)
+#define ACP_BT_TX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x207C)
+#define ACP_BT_TX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2080)
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2084)
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2088)
+#define ACP_BT_TX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x208C)
+
+#define ACP_HS_RX_RINGBUFADDR 0x3A90
+#define ACP_HS_RX_RINGBUFSIZE 0x3A94
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x3A98
+#define ACP_HS_RX_FIFOADDR 0x3A9C
+#define ACP_HS_RX_FIFOSIZE 0x3AA0
+#define ACP_HS_RX_DMA_SIZE 0x3AA4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x3AA8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x3AAC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x3AB0
+#define ACP_HS_TX_RINGBUFADDR 0x3AB4
+#define ACP_HS_TX_RINGBUFSIZE 0x3AB8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x3ABC
+#define ACP_HS_TX_FIFOADDR 0x3AC0
+#define ACP_HS_TX_FIFOSIZE 0x3AC4
+#define ACP_HS_TX_DMA_SIZE 0x3AC8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x3ACC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x3AD0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x3AD4
+
+#define ACP_I2STDM_IER 0x2400
+#define ACP_I2STDM_IRER 0x2404
+#define ACP_I2STDM_RXFRMT 0x2408
+#define ACP_I2STDM_ITER 0x240C
+#define ACP_I2STDM_TXFRMT 0x2410
+
+/* Registers from ACP_BT_TDM block */
+
+#define ACP_BTTDM_IER 0x2800
+#define ACP_BTTDM_IRER 0x2804
+#define ACP_BTTDM_RXFRMT 0x2808
+#define ACP_BTTDM_ITER 0x280C
+#define ACP_BTTDM_TXFRMT 0x2810
+
+/* Registers from ACP_HS_TDM block */
+#define ACP_HSTDM_IER 0x2814
+#define ACP_HSTDM_IRER 0x2818
+#define ACP_HSTDM_RXFRMT 0x281C
+#define ACP_HSTDM_ITER 0x2820
+#define ACP_HSTDM_TXFRMT 0x2824
+
+/* Registers from ACP_WOV_PDM block */
+
+#define ACP_WOV_PDM_ENABLE 0x2C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x2C08
+#define ACP_WOV_RX_RINGBUFADDR 0x2C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x2C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x2C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x2C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x2C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x2C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x2C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x2C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x2C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x2C30
+#define ACP_WOV_BUFFER_STATUS 0x2C58
+#define ACP_WOV_MISC_CTRL 0x2C5C
+#define ACP_WOV_CLK_CTRL 0x2C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x2C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x2C68
+
+#define ACP_I2STDM0_MSTRCLKGEN 0x2414
+#define ACP_I2STDM1_MSTRCLKGEN 0x2418
+#define ACP_I2STDM2_MSTRCLKGEN 0x241C
+#endif
diff --git a/sound/soc/amd/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h
new file mode 100644
index 000000000000..3930cc46fa58
--- /dev/null
+++ b/sound/soc/amd/acp/soc_amd_sdw_common.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
+ */
+
+/*
+ * soc_amd_sdw_common.h - prototypes for common helpers
+ */
+
+#ifndef SOC_AMD_SDW_COMMON_H
+#define SOC_AMD_SDW_COMMON_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+#include <sound/soc_sdw_utils.h>
+
+#define ACP63_SDW_MAX_CPU_DAIS 8
+#define ACP63_SDW_MAX_LINKS 2
+
+#define AMD_SDW_MAX_GROUPS 9
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
+#define ACP72_PCI_REV 0x72
+
+#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
+#define ASOC_SDW_FOUR_SPK BIT(4)
+#define ASOC_SDW_ACP_DMIC BIT(5)
+#define ASOC_SDW_CODEC_SPKR BIT(15)
+
+#define AMD_SDW0 0
+#define AMD_SDW1 1
+#define ACP63_SW0_AUDIO0_TX 0
+#define ACP63_SW0_AUDIO1_TX 1
+#define ACP63_SW0_AUDIO2_TX 2
+
+#define ACP63_SW0_AUDIO0_RX 3
+#define ACP63_SW0_AUDIO1_RX 4
+#define ACP63_SW0_AUDIO2_RX 5
+
+#define ACP63_SW1_AUDIO0_TX 0
+#define ACP63_SW1_AUDIO0_RX 1
+
+#define ACP_DMIC_BE_ID 4
+
+#define ACP70_SW_AUDIO0_TX 0
+#define ACP70_SW_AUDIO1_TX 1
+#define ACP70_SW_AUDIO2_TX 2
+
+#define ACP70_SW_AUDIO0_RX 3
+#define ACP70_SW_AUDIO1_RX 4
+#define ACP70_SW_AUDIO2_RX 5
+
+struct amd_mc_ctx {
+ unsigned int acp_rev;
+ unsigned int max_sdw_links;
+};
+
+int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+
+#endif
diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c
new file mode 100644
index 000000000000..4ca1978020a9
--- /dev/null
+++ b/sound/soc/amd/acp3x-rt5682-max9836.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec.
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+
+#include "raven/acp3x.h"
+#include "../codecs/rt5682.h"
+#include "../codecs/rt1015.h"
+
+#define PCO_PLAT_CLK 48000000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define DUAL_CHANNEL 2
+
+static struct snd_soc_jack pco_jack;
+static struct snd_soc_jack_pin pco_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct clk *rt5682_dai_wclk;
+static struct clk *rt5682_dai_bclk;
+static struct gpio_desc *dmic_sel;
+void *soc_is_rltk_max(struct device *dev);
+
+enum {
+ RT5682 = 0,
+ MAX,
+ EC,
+};
+
+static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ /* set rt5682 dai fmt */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "Failed to set rt5682 dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ /* set codec PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set rt5682 PLL: %d\n", ret);
+ return ret;
+ }
+
+ /* Set codec sysclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "Failed to set rt5682 SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack,
+ pco_jack_pins,
+ ARRAY_SIZE(pco_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int rt5682_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ /* RT5682 will support only 48K output with 48M mclk */
+ clk_set_rate(rt5682_dai_wclk, 48000);
+ clk_set_rate(rt5682_dai_bclk, 48000 * 64);
+ ret = clk_prepare_enable(rt5682_dai_wclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable wclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int acp3x_1015_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int srate, i, ret;
+
+ ret = 0;
+ srate = params_rate(params);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strcmp(codec_dai->name, "rt1015-aif"))
+ continue;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
+ 64 * srate, 256 * srate);
+ if (ret < 0)
+ return ret;
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+
+static void rt5682_clk_disable(void)
+{
+ clk_disable_unprepare(rt5682_dai_wclk);
+}
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp3x_5682_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return rt5682_clk_enable(substream);
+}
+
+static int acp3x_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return rt5682_clk_enable(substream);
+}
+
+static int acp3x_ec_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+
+ return rt5682_clk_enable(substream);
+}
+
+static int dmic_switch;
+
+static int dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = dmic_switch;
+ return 0;
+}
+
+static int dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (dmic_sel) {
+ dmic_switch = ucontrol->value.integer.value[0];
+ gpiod_set_value(dmic_sel, dmic_switch);
+ }
+ return 0;
+}
+
+static void rt5682_shutdown(struct snd_pcm_substream *substream)
+{
+ rt5682_clk_disable();
+}
+
+static const struct snd_soc_ops acp3x_5682_ops = {
+ .startup = acp3x_5682_startup,
+ .shutdown = rt5682_shutdown,
+};
+
+static const struct snd_soc_ops acp3x_max_play_ops = {
+ .startup = acp3x_max_startup,
+ .shutdown = rt5682_shutdown,
+ .hw_params = acp3x_1015_hw_params,
+};
+
+static const struct snd_soc_ops acp3x_ec_cap0_ops = {
+ .startup = acp3x_ec_dmic0_startup,
+ .shutdown = rt5682_shutdown,
+};
+
+SND_SOC_DAILINK_DEF(acp3x_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(acp3x_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.2")));
+
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+SND_SOC_DAILINK_DEF(max,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
+SND_SOC_DAILINK_DEF(rt1015p,
+ DAILINK_COMP_ARRAY(COMP_CODEC("RTL1015:00", "HiFi")));
+SND_SOC_DAILINK_DEF(rt1015,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1015:00", "rt1015-aif"),
+ COMP_CODEC("i2c-10EC1015:01", "rt1015-aif")));
+SND_SOC_DAILINK_DEF(cros_ec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("GOOG0013:00", "EC Codec I2S RX")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp3x_rv_i2s_dma.0")));
+
+static struct snd_soc_codec_conf rt1015_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1015:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1015:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_dai_link acp3x_dai[] = {
+ [RT5682] = {
+ .name = "acp3x-5682-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = acp3x_5682_init,
+ .ops = &acp3x_5682_ops,
+ SND_SOC_DAILINK_REG(acp3x_i2s, rt5682, platform),
+ },
+ [MAX] = {
+ .name = "acp3x-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .ops = &acp3x_max_play_ops,
+ .cpus = acp3x_bt,
+ .num_cpus = ARRAY_SIZE(acp3x_bt),
+ .platforms = platform,
+ .num_platforms = ARRAY_SIZE(platform),
+ },
+ [EC] = {
+ .name = "acp3x-ec-dmic0-capture",
+ .stream_name = "Capture DMIC0",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .capture_only = 1,
+ .ops = &acp3x_ec_cap0_ops,
+ SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
+ },
+};
+
+static const char * const dmic_mux_text[] = {
+ "Front Mic",
+ "Rear Mic",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ acp3x_dmic_enum, SND_SOC_NOPM, 0, dmic_mux_text);
+
+static const struct snd_kcontrol_new acp3x_dmic_mux_control =
+ SOC_DAPM_ENUM_EXT("DMIC Select Mux", acp3x_dmic_enum,
+ dmic_get, dmic_set);
+
+static const struct snd_soc_dapm_widget acp3x_5682_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+};
+
+static const struct snd_soc_dapm_route acp3x_5682_audio_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Spk", NULL, "Speaker"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+};
+
+static const struct snd_kcontrol_new acp3x_5682_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_card acp3x_5682 = {
+ .name = "acp3xalc5682m98357",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_5682_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_5682_widgets),
+ .dapm_routes = acp3x_5682_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_5682_audio_route),
+ .controls = acp3x_5682_mc_controls,
+ .num_controls = ARRAY_SIZE(acp3x_5682_mc_controls),
+};
+
+static const struct snd_soc_dapm_widget acp3x_1015_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route acp3x_1015_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+ {"Left Spk", NULL, "Left SPO"},
+ {"Right Spk", NULL, "Right SPO"},
+};
+
+static const struct snd_kcontrol_new acp3x_mc_1015_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static struct snd_soc_card acp3x_1015 = {
+ .name = "acp3xalc56821015",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_1015_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_1015_widgets),
+ .dapm_routes = acp3x_1015_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_1015_route),
+ .codec_conf = rt1015_conf,
+ .num_configs = ARRAY_SIZE(rt1015_conf),
+ .controls = acp3x_mc_1015_controls,
+ .num_controls = ARRAY_SIZE(acp3x_mc_1015_controls),
+};
+
+static const struct snd_soc_dapm_widget acp3x_1015p_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
+static const struct snd_soc_dapm_route acp3x_1015p_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+ /* speaker */
+ { "Speakers", NULL, "Speaker" },
+};
+
+static const struct snd_kcontrol_new acp3x_mc_1015p_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_card acp3x_1015p = {
+ .name = "acp3xalc56821015p",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_1015p_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_1015p_widgets),
+ .dapm_routes = acp3x_1015p_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_1015p_route),
+ .controls = acp3x_mc_1015p_controls,
+ .num_controls = ARRAY_SIZE(acp3x_mc_1015p_controls),
+};
+
+void *soc_is_rltk_max(struct device *dev)
+{
+ const struct acpi_device_id *match;
+
+ match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!match)
+ return NULL;
+ return (void *)match->driver_data;
+}
+
+static void card_spk_dai_link_present(struct snd_soc_dai_link *links,
+ const char *card_name)
+{
+ if (!strcmp(card_name, "acp3xalc56821015")) {
+ links[1].codecs = rt1015;
+ links[1].num_codecs = ARRAY_SIZE(rt1015);
+ } else if (!strcmp(card_name, "acp3xalc56821015p")) {
+ links[1].codecs = rt1015p;
+ links[1].num_codecs = ARRAY_SIZE(rt1015p);
+ } else {
+ links[1].codecs = max;
+ links[1].num_codecs = ARRAY_SIZE(max);
+ }
+}
+
+static int acp3x_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp3x_platform_info *machine;
+ struct device *dev = &pdev->dev;
+
+ card = (struct snd_soc_card *)soc_is_rltk_max(dev);
+ if (!card)
+ return -ENODEV;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card_spk_dai_link_present(card->dai_link, card->name);
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
+ if (IS_ERR(dmic_sel)) {
+ dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n",
+ PTR_ERR(dmic_sel));
+ return PTR_ERR(dmic_sel);
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static const struct acpi_device_id acp3x_audio_acpi_match[] = {
+ { "AMDI5682", (unsigned long)&acp3x_5682},
+ { "AMDI1015", (unsigned long)&acp3x_1015},
+ { "10021015", (unsigned long)&acp3x_1015p},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, acp3x_audio_acpi_match);
+
+static struct platform_driver acp3x_audio = {
+ .driver = {
+ .name = "acp3x-alc5682-max98357",
+ .acpi_match_table = ACPI_PTR(acp3x_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp3x_probe,
+};
+
+module_platform_driver(acp3x_audio);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("ALC5682 ALC1015, ALC1015P & MAX98357 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
new file mode 100644
index 000000000000..5b6362103ca0
--- /dev/null
+++ b/sound/soc/amd/mach-config.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __AMD_MACH_CONFIG_H
+#define __AMD_MACH_CONFIG_H
+
+#include <sound/soc-acpi.h>
+
+#define FLAG_AMD_SOF BIT(1)
+#define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
+#define FLAG_AMD_LEGACY BIT(3)
+#define FLAG_AMD_LEGACY_ONLY_DMIC BIT(4)
+
+#define ACP_PCI_DEV_ID 0x15E2
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[];
+
+struct config_entry {
+ u32 flags;
+ u16 device;
+ const struct dmi_system_id *dmi_table;
+};
+
+#endif
diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
new file mode 100644
index 000000000000..778ee4726389
--- /dev/null
+++ b/sound/soc/amd/ps/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Pink Sardine platform Support
+snd-pci-ps-y := pci-ps.o ps-common.o
+snd-ps-pdm-dma-y := ps-pdm-dma.o
+snd-soc-ps-mach-y := ps-mach.o
+snd-ps-sdw-dma-y := ps-sdw-dma.o
+
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-sdw-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS_MACH) += snd-soc-ps-mach.o
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
new file mode 100644
index 000000000000..62cb6bef17ab
--- /dev/null
+++ b/sound/soc/amd/ps/acp63.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD Common ACP header file for ACP6.3, ACP7.0 & ACP7.1 platforms
+ *
+ * Copyright (C) 2022, 2023, 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <linux/soundwire/sdw_amd.h>
+#include <sound/acp63_chip_offset_byte.h>
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP63_REG_START 0x1240000
+#define ACP63_REG_END 0x125C000
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
+#define ACP72_PCI_REV 0x72
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP63_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP63_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP63_PGFSM_STATUS_MASK 3
+#define ACP63_POWERED_ON 0
+#define ACP63_POWER_ON_IN_PROGRESS 1
+#define ACP63_POWERED_OFF 2
+#define ACP63_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define ACP_DMIC_DEV 2
+
+#define ACP63_DMIC_ADDR 2
+#define ACP63_SDW_ADDR 5
+#define AMD_SDW_MAX_MANAGERS 2
+
+/* time in ms for acp timeout */
+#define ACP63_TIMEOUT 500
+
+#define ACP_SDW0_STAT BIT(21)
+#define ACP_SDW1_STAT BIT(2)
+#define ACP_ERROR_IRQ BIT(29)
+
+#define ACP_AUDIO0_TX_THRESHOLD 0x1c
+#define ACP_AUDIO1_TX_THRESHOLD 0x1a
+#define ACP_AUDIO2_TX_THRESHOLD 0x18
+#define ACP_AUDIO0_RX_THRESHOLD 0x1b
+#define ACP_AUDIO1_RX_THRESHOLD 0x19
+#define ACP_AUDIO2_RX_THRESHOLD 0x17
+#define ACP63_P1_AUDIO1_TX_THRESHOLD BIT(6)
+#define ACP63_P1_AUDIO1_RX_THRESHOLD BIT(5)
+#define ACP63_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP63_P1_SDW_DMA_IRQ_MASK 0x60
+#define ACP63_SDW0_DMA_MAX_STREAMS 6
+#define ACP63_SDW1_DMA_MAX_STREAMS 2
+#define ACP63_P1_AUDIO_TX_THRESHOLD 6
+
+/*
+ * Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL register.
+ * Stream id IRQ Bit
+ * 0 (SDW0_AUDIO0_TX) 28
+ * 1 (SDW0_AUDIO1_TX) 26
+ * 2 (SDW0_AUDIO2_TX) 24
+ * 3 (SDW0_AUDIO0_RX) 27
+ * 4 (SDW0_AUDIO1_RX) 25
+ * 5 (SDW0_AUDIO2_RX) 23
+ */
+#define ACP63_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP63_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register.
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO1_TX) 6
+ * 1 (SDW1_AUDIO1_RX) 5
+ */
+#define ACP63_SDW1_DMA_IRQ_MASK(i) (ACP63_P1_AUDIO_TX_THRESHOLD - (i))
+
+#define ACP_DELAY_US 5
+#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
+#define SDW0_MEM_WINDOW_START 0x4800000
+#define ACP_SDW_SRAM_PTE_OFFSET 0x03800400
+#define SDW0_PTE_OFFSET 0x400
+#define SDW_FIFO_SIZE 0x100
+#define SDW_DMA_SIZE 0x40
+#define ACP_SDW0_FIFO_OFFSET 0x100
+#define ACP_SDW_PTE_OFFSET 0x100
+#define SDW_FIFO_OFFSET 0x100
+#define SDW_PTE_OFFSET(i) (SDW0_PTE_OFFSET + ((i) * 0x600))
+#define ACP_SDW_FIFO_OFFSET(i) (ACP_SDW0_FIFO_OFFSET + ((i) * 0x500))
+#define SDW_MEM_WINDOW_START(i) (SDW0_MEM_WINDOW_START + ((i) * 0xC0000))
+
+#define SDW_PLAYBACK_MIN_NUM_PERIODS 2
+#define SDW_PLAYBACK_MAX_NUM_PERIODS 8
+#define SDW_PLAYBACK_MAX_PERIOD_SIZE 8192
+#define SDW_PLAYBACK_MIN_PERIOD_SIZE 1024
+#define SDW_CAPTURE_MIN_NUM_PERIODS 2
+#define SDW_CAPTURE_MAX_NUM_PERIODS 8
+#define SDW_CAPTURE_MAX_PERIOD_SIZE 8192
+#define SDW_CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
+#define SDW_MIN_BUFFER SDW_MAX_BUFFER
+
+#define ACP_HW_OPS(acp_data, cb) ((acp_data)->hw_ops->cb)
+
+#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F
+#define ACP70_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP70_PGFSM_STATUS_MASK 0xFF
+#define ACP70_TIMEOUT 2000
+#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
+#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
+#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
+#define ACP70_SDW0_PME_STAT BIT(26)
+#define ACP70_SDW1_PME_STAT BIT(27)
+
+#define ACP70_SDW0_DMA_MAX_STREAMS 6
+#define ACP70_SDW1_DMA_MAX_STREAMS ACP70_SDW0_DMA_MAX_STREAMS
+#define ACP70_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP70_P1_SDW_DMA_IRQ_MASK 0x1F8
+
+#define ACP70_P1_AUDIO0_TX_THRESHOLD 0x8
+#define ACP70_P1_AUDIO1_TX_THRESHOLD 0x6
+#define ACP70_P1_AUDIO2_TX_THRESHOLD 0x4
+#define ACP70_P1_AUDIO0_RX_THRESHOLD 0x7
+#define ACP70_P1_AUDIO1_RX_THRESHOLD 0x5
+#define ACP70_P1_AUDIO2_RX_THRESHOLD 0x3
+
+#define ACP70_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register for ACP70/ACP71 platforms
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO0_TX) 8
+ * 1 (SDW1_AUDIO1_TX) 6
+ * 2 (SDW1_AUDIO2_TX) 4
+ * 3 (SDW1_AUDIO0_RX) 7
+ * 4 (SDW1_AUDIO1_RX) 5
+ * 5 (SDW1_AUDIO2_RX) 3
+ */
+#define ACP70_SDW1_DMA_TX_IRQ_MASK(i) (ACP70_P1_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW1_DMA_RX_IRQ_MASK(i) (ACP70_P1_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+#define ACP70_SW0_AUDIO0_TX_EN ACP_SW0_AUDIO0_TX_EN
+#define ACP70_SW0_AUDIO1_TX_EN ACP_SW0_AUDIO1_TX_EN
+#define ACP70_SW0_AUDIO2_TX_EN ACP_SW0_AUDIO2_TX_EN
+#define ACP70_SW0_AUDIO0_RX_EN ACP_SW0_AUDIO0_RX_EN
+#define ACP70_SW0_AUDIO1_RX_EN ACP_SW0_AUDIO1_RX_EN
+#define ACP70_SW0_AUDIO2_RX_EN ACP_SW0_AUDIO2_RX_EN
+
+#define ACP70_SW1_AUDIO0_TX_EN 0x0003C10
+#define ACP70_SW1_AUDIO1_TX_EN 0x0003C50
+#define ACP70_SW1_AUDIO2_TX_EN 0x0003C6C
+#define ACP70_SW1_AUDIO0_RX_EN 0x0003C88
+#define ACP70_SW1_AUDIO1_RX_EN 0x0003D28
+#define ACP70_SW1_AUDIO2_RX_EN 0x0003D44
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+enum amd_acp63_sdw0_channel {
+ ACP63_SDW0_AUDIO0_TX = 0,
+ ACP63_SDW0_AUDIO1_TX,
+ ACP63_SDW0_AUDIO2_TX,
+ ACP63_SDW0_AUDIO0_RX,
+ ACP63_SDW0_AUDIO1_RX,
+ ACP63_SDW0_AUDIO2_RX,
+};
+
+enum amd_acp63_sdw1_channel {
+ ACP63_SDW1_AUDIO1_TX,
+ ACP63_SDW1_AUDIO1_RX,
+};
+
+enum amd_acp70_sdw_channel {
+ ACP70_SDW_AUDIO0_TX = 0,
+ ACP70_SDW_AUDIO1_TX,
+ ACP70_SDW_AUDIO2_TX,
+ ACP70_SDW_AUDIO0_RX,
+ ACP70_SDW_AUDIO1_RX,
+ ACP70_SDW_AUDIO2_RX,
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp63_base;
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp63_base;
+ struct mutex *acp_lock;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct sdw_dma_dev_data {
+ void __iomem *acp_base;
+ struct mutex *acp_lock; /* used to protect acp common register access */
+ u32 acp_rev;
+ struct snd_pcm_substream *acp63_sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp63_sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw0_dma_stream[ACP70_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw1_dma_stream[ACP70_SDW1_DMA_MAX_STREAMS];
+};
+
+struct acp_sdw_dma_stream {
+ u16 num_pages;
+ u16 channels;
+ u32 stream_id;
+ u32 instance;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+};
+
+union acp_sdw_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+struct sdw_dma_ring_buf_reg {
+ u32 reg_dma_size;
+ u32 reg_fifo_addr;
+ u32 reg_fifo_size;
+ u32 reg_ring_buf_size;
+ u32 reg_ring_buf_addr;
+ u32 water_mark_size_reg;
+ u32 pos_low_reg;
+ u32 pos_high_reg;
+};
+
+struct acp63_dev_data;
+
+/**
+ * struct acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @acp_get_config: function to read the acp pin configuration
+ * @acp_sdw_dma_irq_thread: ACP SoundWire DMA interrupt thread
+ * acp_suspend: ACP system level suspend callback
+ * acp_resume: ACP system level resume callback
+ * acp_suspend_runtime: ACP runtime suspend callback
+ * acp_resume_runtime: ACP runtime resume callback
+ */
+struct acp_hw_ops {
+ int (*acp_init)(void __iomem *acp_base, struct device *dev);
+ int (*acp_deinit)(void __iomem *acp_base, struct device *dev);
+ void (*acp_get_config)(struct pci_dev *pci, struct acp63_dev_data *acp_data);
+ void (*acp_sdw_dma_irq_thread)(struct acp63_dev_data *acp_data);
+ int (*acp_suspend)(struct device *dev);
+ int (*acp_resume)(struct device *dev);
+ int (*acp_suspend_runtime)(struct device *dev);
+ int (*acp_resume_runtime)(struct device *dev);
+};
+
+/**
+ * struct acp63_dev_data - acp pci driver context
+ * @acp63_base: acp mmio base
+ * @res: resource
+ * @hw_ops: ACP pci driver platform-specific ops
+ * @pdm_dev: ACP PDM controller platform device
+ * @dmic_codec: platform device for DMIC Codec
+ * sdw_dma_dev: platform device for SoundWire DMA controller
+ * @mach_dev: platform device for machine driver to support ACP PDM/SoundWire configuration
+ * @acp_lock: used to protect acp common registers
+ * @info: SoundWire AMD information found in ACPI tables
+ * @sdw: SoundWire context for all SoundWire manager instances
+ * @machine: ACPI machines for SoundWire interface
+ * @is_sdw_dev: flag set to true when any SoundWire manager instances are available
+ * @is_pdm_dev: flag set to true when ACP PDM controller exists
+ * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
+ * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
+ * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled
+ * @acp70_sdw0_wake_event: flag set to true when wake irq asserted for SW0 instance
+ * @acp70_sdw1_wake_event: flag set to true when wake irq asserted for SW1 instance
+ * @addr: pci ioremap address
+ * @reg_range: ACP reigister range
+ * @acp_rev: ACP PCI revision id
+ * @acp_sw_pad_keeper_en: store acp SoundWire pad keeper enable register value
+ * @acp_pad_pulldown_ctrl: store acp pad pulldown control register value
+ * @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW0 instance
+ * @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW1 instance
+ * @acp70_sdw0-dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW0 instance
+ * @acp70_sdw_dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW1 instance
+ */
+
+struct acp63_dev_data {
+ void __iomem *acp63_base;
+ struct resource *res;
+ struct acp_hw_ops *hw_ops;
+ struct platform_device *pdm_dev;
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *sdw_dma_dev;
+ struct platform_device *mach_dev;
+ struct mutex acp_lock; /* protect shared registers */
+ struct sdw_amd_acpi_info info;
+ /* sdw context allocated by SoundWire driver */
+ struct sdw_amd_ctx *sdw;
+ struct snd_soc_acpi_mach *machines;
+ bool is_sdw_dev;
+ bool is_pdm_dev;
+ bool is_pdm_config;
+ bool is_sdw_config;
+ bool sdw_en_stat;
+ bool acp70_sdw0_wake_event;
+ bool acp70_sdw1_wake_event;
+ u32 addr;
+ u32 reg_range;
+ u32 acp_rev;
+ u32 subsystem_vendor;
+ u32 subsystem_device;
+ u32 acp_sw_pad_keeper_en;
+ u32 acp_pad_pulldown_ctrl;
+ u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
+ u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
+ u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS];
+ u16 acp70_sdw1_dma_intr_stat[ACP70_SDW1_DMA_MAX_STREAMS];
+};
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops);
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops);
+
+static inline int acp_hw_init(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_init)
+ return ACP_HW_OPS(adata, acp_init)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_deinit)
+ return ACP_HW_OPS(adata, acp_deinit)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_hw_get_config(struct pci_dev *pci, struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_get_config)
+ ACP_HW_OPS(adata, acp_get_config)(pci, adata);
+}
+
+static inline void acp_hw_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_sdw_dma_irq_thread)
+ ACP_HW_OPS(adata, acp_sdw_dma_irq_thread)(adata);
+}
+
+static inline int acp_hw_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend)
+ return ACP_HW_OPS(adata, acp_suspend)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume)
+ return ACP_HW_OPS(adata, acp_resume)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_suspend_runtime(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime)
+ return ACP_HW_OPS(adata, acp_suspend_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime)
+ return ACP_HW_OPS(adata, acp_resume_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
new file mode 100644
index 000000000000..3a20cc10d61f
--- /dev/null
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
+ *
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <linux/soundwire/sdw_amd.h>
+#include "../mach-config.h"
+
+#include "acp63.h"
+
+static void handle_acp70_sdw_wake_event(struct acp63_dev_data *adata)
+{
+ struct amd_sdw_manager *amd_manager;
+
+ if (adata->acp70_sdw0_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw0_wake_event = 0;
+ }
+
+ if (adata->acp70_sdw1_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw1_wake_event = 0;
+ }
+}
+
+static short int check_and_handle_acp70_sdw_wake_irq(struct acp63_dev_data *adata)
+{
+ u32 ext_intr_stat1;
+ int irq_flag = 0;
+ bool sdw_wake_irq = false;
+
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
+ writel(ACP70_SDW0_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
+ writel(ACP70_SDW1_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW0_WAKE_EN);
+ writel(ACP70_SDW0_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW1_WAKE_EN);
+ writel(ACP70_SDW1_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (sdw_wake_irq) {
+ handle_acp70_sdw_wake_event(adata);
+ irq_flag = 1;
+ }
+ return irq_flag;
+}
+
+static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 ext_intr_stat,
+ u32 ext_intr_stat1)
+{
+ u32 stream_id = 0;
+ u16 sdw_dma_irq_flag = 0;
+ u16 index;
+
+ if (ext_intr_stat & ACP63_SDW_DMA_IRQ_MASK) {
+ for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat & BIT(index)) {
+ writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ switch (index) {
+ case ACP_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_TX;
+ break;
+ case ACP_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_TX;
+ break;
+ case ACP_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_TX;
+ break;
+ case ACP_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_RX;
+ break;
+ case ACP_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_RX;
+ break;
+ case ACP_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_RX;
+ break;
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ }
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_RX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_RX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_RX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_TX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_TX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_TX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) {
+ for (index = ACP70_P1_AUDIO2_RX_THRESHOLD;
+ index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat1 & BIT(index)) {
+ writel(BIT(index),
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ switch (index) {
+ case ACP70_P1_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_TX;
+ break;
+ case ACP70_P1_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_TX;
+ break;
+ case ACP70_P1_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_TX;
+ break;
+ case ACP70_P1_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_RX;
+ break;
+ case ACP70_P1_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_RX;
+ break;
+ case ACP70_P1_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_RX;
+ break;
+ }
+
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ break;
+ }
+ return sdw_dma_irq_flag;
+}
+
+static irqreturn_t acp63_irq_thread(int irq, void *context)
+{
+ struct acp63_dev_data *adata = context;
+
+ acp_hw_sdw_dma_irq_thread(adata);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
+{
+ struct acp63_dev_data *adata;
+ struct pdm_dev_data *ps_pdm_data;
+ struct amd_sdw_manager *amd_manager;
+ u32 ext_intr_stat, ext_intr_stat1;
+ u16 irq_flag = 0;
+ u16 wake_irq_flag = 0;
+ u16 sdw_dma_irq_flag = 0;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+ /* ACP interrupts will be cleared by reading particular bit and writing
+ * same value to the status register. writing zero's doesn't have any
+ * effect.
+ * Bit by bit checking of IRQ field is implemented.
+ */
+ ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (ext_intr_stat & ACP_SDW0_STAT) {
+ writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP_SDW1_STAT) {
+ writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ if (ext_intr_stat & ACP_ERROR_IRQ) {
+ writel(ACP_ERROR_IRQ, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ /* TODO: Report SoundWire Manager instance errors */
+ writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
+ writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
+ writel(0, adata->acp63_base + ACP_ERROR_STATUS);
+ irq_flag = 1;
+ }
+
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ wake_irq_flag = check_and_handle_acp70_sdw_wake_irq(adata);
+
+ if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
+ ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev);
+ writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (ps_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
+ irq_flag = 1;
+ }
+
+ sdw_dma_irq_flag = check_and_handle_sdw_dma_irq(adata, ext_intr_stat, ext_intr_stat1);
+ if (sdw_dma_irq_flag)
+ return IRQ_WAKE_THREAD;
+
+ if (irq_flag | wake_irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE)
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ struct acpi_device *sdw_dev;
+ struct acp63_dev_data *acp_data;
+
+ acp_data = dev_get_drvdata(dev);
+ if (!addr)
+ return -ENODEV;
+
+ sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0);
+ if (!sdw_dev)
+ return -ENODEV;
+
+ acp_data->info.handle = sdw_dev->handle;
+ acp_data->info.count = AMD_SDW_MAX_MANAGERS;
+ return amd_sdw_scan_controller(&acp_data->info);
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ struct acp63_dev_data *acp_data;
+ struct sdw_amd_res sdw_res;
+ int ret;
+
+ acp_data = dev_get_drvdata(dev);
+ memset(&sdw_res, 0, sizeof(sdw_res));
+ sdw_res.addr = acp_data->addr;
+ sdw_res.reg_range = acp_data->reg_range;
+ sdw_res.handle = acp_data->info.handle;
+ sdw_res.parent = dev;
+ sdw_res.dev = dev;
+ sdw_res.acp_lock = &acp_data->acp_lock;
+ sdw_res.count = acp_data->info.count;
+ sdw_res.mmio_base = acp_data->acp63_base;
+ sdw_res.acp_rev = acp_data->acp_rev;
+ sdw_res.link_mask = acp_data->info.link_mask;
+ ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+ if (ret)
+ dev_err(dev, "error: SoundWire probe failed\n");
+ return ret;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+ if (acp_data->sdw)
+ sdw_amd_exit(acp_data->sdw);
+ acp_data->sdw = NULL;
+
+ return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+ struct snd_soc_acpi_mach *mach;
+ const struct snd_soc_acpi_link_adr *link;
+ struct acp63_dev_data *acp_data = dev_get_drvdata(dev);
+ int ret, i;
+
+ if (acp_data->info.count) {
+ ret = sdw_amd_get_slave_info(acp_data->sdw);
+ if (ret) {
+ dev_dbg(dev, "failed to read slave information\n");
+ return NULL;
+ }
+ for (mach = acp_data->machines; mach; mach++) {
+ if (!mach->links)
+ break;
+ link = mach->links;
+ for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
+ if (!snd_soc_acpi_sdw_link_slaves_found(dev, link,
+ acp_data->sdw->peripherals))
+ break;
+ }
+ if (i == acp_data->info.count || !link->num_adr)
+ break;
+ }
+ if (mach && mach->link_mask) {
+ mach->mach_params.links = mach->links;
+ mach->mach_params.link_mask = mach->link_mask;
+ mach->mach_params.subsystem_rev = acp_data->acp_rev;
+ mach->mach_params.subsystem_vendor = acp_data->subsystem_vendor;
+ mach->mach_params.subsystem_device = acp_data->subsystem_device;
+ mach->mach_params.subsystem_id_set = true;
+
+ dev_dbg(dev, "SSID %x%x\n", mach->mach_params.subsystem_vendor,
+ mach->mach_params.subsystem_device);
+ return mach;
+ }
+ }
+ dev_dbg(dev, "No SoundWire machine driver found\n");
+ return NULL;
+}
+#else
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ return 0;
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+ return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static int acp63_machine_register(struct device *dev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+ int size;
+
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ size = sizeof(*adata->machines);
+ mach = acp63_sdw_machine_select(dev);
+ if (mach) {
+ adata->mach_dev = platform_device_register_data(dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach,
+ size);
+ if (IS_ERR(adata->mach_dev)) {
+ dev_err(dev,
+ "cannot register Machine device for SoundWire Interface\n");
+ return PTR_ERR(adata->mach_dev);
+ }
+ }
+
+ } else if (adata->is_pdm_dev && !adata->is_sdw_dev && adata->is_pdm_config) {
+ adata->mach_dev = platform_device_register_data(dev, "acp_ps_mach",
+ PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(adata->mach_dev)) {
+ dev_err(dev, "cannot register amd_ps_mach device\n");
+ return PTR_ERR(adata->mach_dev);
+ }
+ }
+ return 0;
+}
+
+static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ bool is_dmic_dev = false;
+ bool is_sdw_dev = false;
+ bool wov_en, dmic_en;
+ int ret;
+
+ /* IF WOV entry not found, enable dmic based on acp-audio-device-type entry*/
+ wov_en = true;
+ dmic_en = false;
+
+ acp_hw_get_config(pci, acp_data);
+
+ if (acp_data->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+ if (pdm_dev) {
+ /* is_dmic_dev flag will be set when ACP PDM controller device exists */
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ dmic_en = true;
+ }
+
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret))
+ wov_en = dmic_status;
+ }
+
+ if (dmic_en && wov_en)
+ is_dmic_dev = true;
+
+ if (acp_data->is_sdw_config) {
+ ret = acp_scan_sdw_devices(&pci->dev, ACP63_SDW_ADDR);
+ if (!ret && acp_data->info.link_mask)
+ is_sdw_dev = true;
+ }
+
+ acp_data->is_pdm_dev = is_dmic_dev;
+ acp_data->is_sdw_dev = is_sdw_dev;
+ if (!is_dmic_dev && !is_sdw_dev) {
+ dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
+{
+ struct platform_device_info pdevinfo;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (adata->is_sdw_dev || adata->is_pdm_dev) {
+ adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ if (adata->is_pdm_dev && adata->is_pdm_config) {
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->pdm_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->pdm_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->pdm_dev);
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ adata->dmic_codec_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->dmic_codec_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->dmic_codec_dev);
+ goto unregister_pdm_dev;
+ }
+ }
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ ret = amd_sdw_probe(&pci->dev);
+ if (ret) {
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->sdw_dma_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->sdw_dma_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->sdw_dma_dev);
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ }
+
+ return 0;
+unregister_dmic_codec_dev:
+ platform_device_unregister(adata->dmic_codec_dev);
+unregister_pdm_dev:
+ platform_device_unregister(adata->pdm_dev);
+de_init:
+ if (acp_hw_deinit(adata, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci)
+{
+ adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops),
+ GFP_KERNEL);
+ if (!adata->hw_ops)
+ return -ENOMEM;
+
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ acp63_hw_init_ops(adata->hw_ops);
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ acp70_hw_init_ops(adata->hw_ops);
+ break;
+ default:
+ dev_err(&pci->dev, "ACP device not found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int snd_acp63_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp63_dev_data *adata;
+ u32 addr;
+ u32 irqflags, flag;
+ int ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* ACP PCI revision id check for ACP6.3, ACP7.0 & ACP7.1 platforms */
+ switch (pci->revision) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp63/acp70/acp71 pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP6.2 audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp63_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp63_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp63_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ adata->addr = addr;
+ adata->reg_range = ACP63_REG_END - ACP63_REG_START;
+ adata->acp_rev = pci->revision;
+ adata->subsystem_vendor = pci->subsystem_vendor;
+ adata->subsystem_device = pci->subsystem_device;
+
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ mutex_init(&adata->acp_lock);
+ ret = acp_hw_init_ops(adata, pci);
+ if (ret) {
+ dev_err(&pci->dev, "ACP hw ops init failed\n");
+ goto release_regions;
+ }
+ ret = acp_hw_init(adata, &pci->dev);
+ if (ret)
+ goto release_regions;
+ ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
+ acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto de_init;
+ }
+ ret = get_acp63_device_config(pci, adata);
+ /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
+ if (ret) {
+ dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
+ goto skip_pdev_creation;
+ }
+ ret = create_acp63_platform_devs(pci, adata, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ adata->machines = snd_soc_acpi_amd_acp70_sdw_machines;
+ else
+ adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
+
+ ret = acp63_machine_register(&pci->dev);
+ if (ret) {
+ dev_err(&pci->dev, "ACP machine register failed\n");
+ goto de_init;
+ }
+skip_pdev_creation:
+ device_set_wakeup_enable(&pci->dev, true);
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+de_init:
+ if (acp_hw_deinit(adata, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp_suspend(struct device *dev)
+{
+ return acp_hw_suspend(dev);
+}
+
+static int snd_acp_runtime_resume(struct device *dev)
+{
+ return acp_hw_runtime_resume(dev);
+}
+
+static int snd_acp_resume(struct device *dev)
+{
+ return acp_hw_resume(dev);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
+static void snd_acp63_remove(struct pci_dev *pci)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->sdw) {
+ amd_sdw_exit(adata);
+ platform_device_unregister(adata->sdw_dma_dev);
+ }
+ if (adata->is_pdm_dev) {
+ platform_device_unregister(adata->pdm_dev);
+ platform_device_unregister(adata->dmic_codec_dev);
+ }
+ if (adata->mach_dev)
+ platform_device_unregister(adata->mach_dev);
+ ret = acp_hw_deinit(adata, &pci->dev);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp63_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp63_ids);
+
+static struct pci_driver ps_acp63_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp63_ids,
+ .probe = snd_acp63_probe,
+ .remove = snd_acp63_remove,
+ .driver = {
+ .pm = pm_ptr(&acp63_pm_ops),
+ }
+};
+
+module_pci_driver(ps_acp63_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
+MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
+MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c
new file mode 100644
index 000000000000..7b4966b75dc6
--- /dev/null
+++ b/sound/soc/amd/ps/ps-common.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ACP PCI driver callback routines for ACP6.3, ACP7.0 & ACP7.1
+ * platforms.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ * Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+
+#include "acp63.h"
+
+static int acp63_power_on(void __iomem *acp_base)
+{
+ u32 val;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP63_PGFSM_STATUS_MASK) != ACP63_POWER_ON_IN_PROGRESS)
+ writel(ACP63_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static int acp63_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP63_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static void acp63_enable_interrupts(void __iomem *acp_base)
+{
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp63_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp63_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp63_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ acp63_enable_interrupts(acp_base);
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static int acp63_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp63_disable_interrupts(acp_base);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_CONTROL);
+ writel(1, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp63_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
+{
+ u32 sdw0_en, sdw1_en;
+
+ sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
+ sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
+ return (sdw0_en || sdw1_en);
+}
+
+static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
+{
+ u32 val;
+
+ val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
+ if (val && adata->sdw->pdev[0])
+ pm_request_resume(&adata->sdw->pdev[0]->dev);
+
+ val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
+ if (val && adata->sdw->pdev[1])
+ pm_request_resume(&adata->sdw->pdev[1]->dev);
+}
+
+static int __maybe_unused snd_acp63_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->is_sdw_dev) {
+ adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+ if (adata->sdw_en_stat) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+
+ if (!adata->sdw_en_stat)
+ handle_acp63_sdw_pme_event(adata);
+ return 0;
+}
+
+static int __maybe_unused snd_acp63_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ u32 acp_sw_pad_keeper_en;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en);
+ if (!acp_sw_pad_keeper_en) {
+ writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ }
+ return ret;
+}
+
+static void acp63_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP63_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw0_dma_stream[stream_id]);
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP63_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw1_dma_stream[stream_id]);
+ adata->acp63_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp63_init;
+ hw_ops->acp_deinit = acp63_deinit;
+ hw_ops->acp_get_config = acp63_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp63_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp63_suspend;
+ hw_ops->acp_resume = snd_acp63_resume;
+ hw_ops->acp_suspend_runtime = snd_acp63_suspend;
+ hw_ops->acp_resume_runtime = snd_acp63_runtime_resume;
+}
+
+static int acp70_power_on(void __iomem *acp_base)
+{
+ u32 val = 0;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return 0;
+ if (val & ACP70_PGFSM_STATUS_MASK)
+ writel(ACP70_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static int acp70_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP70_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static void acp70_enable_sdw_host_wake_interrupts(void __iomem *acp_base)
+{
+ u32 ext_intr_cntl1;
+
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= ACP70_SDW_HOST_WAKE_MASK;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+}
+
+static void acp70_enable_interrupts(void __iomem *acp_base)
+{
+ u32 sdw0_wake_en, sdw1_wake_en;
+
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ sdw0_wake_en = readl(acp_base + ACP_SW0_WAKE_EN);
+ sdw1_wake_en = readl(acp_base + ACP_SW1_WAKE_EN);
+ if (sdw0_wake_en || sdw1_wake_en)
+ acp70_enable_sdw_host_wake_interrupts(acp_base);
+}
+
+static void acp70_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp70_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp70_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ acp70_enable_interrupts(acp_base);
+ writel(0x1, acp_base + ACP_PME_EN);
+ return 0;
+}
+
+static int acp70_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp70_disable_interrupts(acp_base);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp70_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_20:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_16:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ case ACP_CONFIG_19:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void acp70_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP70_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw0_dma_stream[stream_id]);
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP70_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw1_dma_stream[stream_id]);
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+static int __maybe_unused snd_acp70_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->is_sdw_dev) {
+ adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+ if (adata->sdw_en_stat) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp70_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int __maybe_unused snd_acp70_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ u32 acp_sw_pad_keeper_en;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en);
+ if (!acp_sw_pad_keeper_en) {
+ writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ }
+ return ret;
+}
+
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp70_init;
+ hw_ops->acp_deinit = acp70_deinit;
+ hw_ops->acp_get_config = acp70_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp70_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp70_suspend;
+ hw_ops->acp_resume = snd_acp70_resume;
+ hw_ops->acp_suspend_runtime = snd_acp70_suspend;
+ hw_ops->acp_resume_runtime = snd_acp70_runtime_resume;
+}
diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c
new file mode 100644
index 000000000000..ff8ad036b077
--- /dev/null
+++ b/sound/soc/amd/ps/ps-mach.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Machine driver for AMD Pink Sardine platform using DMIC
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+
+#include "acp63.h"
+
+#define DRV_NAME "acp_ps_mach"
+
+SND_SOC_DAILINK_DEF(acp63_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_ps_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_ps_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp63_dai_pdm[] = {
+ {
+ .name = "acp63-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp63_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp63_card = {
+ .name = "acp63",
+ .owner = THIS_MODULE,
+ .dai_link = acp63_dai_pdm,
+ .num_links = 1,
+};
+
+static int acp63_probe(struct platform_device *pdev)
+{
+ struct acp63_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ int ret;
+
+ platform_set_drvdata(pdev, &acp63_card);
+ card = platform_get_drvdata(pdev);
+ acp63_card.dev = &pdev->dev;
+
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+
+ return 0;
+}
+
+static struct platform_driver acp63_mach_driver = {
+ .driver = {
+ .name = "acp_ps_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp63_probe,
+};
+
+module_platform_driver(acp63_mach_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD Pink Sardine support for DMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c
new file mode 100644
index 000000000000..9cfbe05ad996
--- /dev/null
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
+ *
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+
+#include "acp63.h"
+
+#define DRV_NAME "acp_ps_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp63_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp63_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
+{
+ u32 ext_int_ctrl;
+
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
+}
+
+static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata)
+{
+ u32 ext_int_ctrl;
+
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
+}
+
+static bool acp63_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp63_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp63_enable_pdm_clock(acp_base);
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp63_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp63_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp63_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp63_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp63_enable_pdm_interrupts(adata);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp63_base = adata->acp63_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp63_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp63_config_dma(rtd, substream->stream);
+ acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp63_base);
+ return 0;
+}
+
+static u64 acp63_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ u32 high, low;
+ u64 byte_count;
+
+ high = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count = high;
+ low = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ byte_count = (byte_count << 32) | low;
+ return byte_count;
+}
+
+static snd_pcm_uframes_t acp63_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp63_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp63_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp63_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ acp63_disable_pdm_interrupts(adata);
+ adata->capture_stream = NULL;
+ kfree(runtime->private_data);
+ return 0;
+}
+
+static int acp63_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DECIMATION_FACTOR, rtd->acp63_base + ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp63_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
+ if (!pdm_status)
+ ret = acp63_start_pdm_dma(rtd->acp63_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
+ if (pdm_status)
+ ret = acp63_stop_pdm_dma(rtd->acp63_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp63_pdm_dai_ops = {
+ .trigger = acp63_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp63_pdm_dai_driver = {
+ .name = "acp_ps_pdm_dma.0",
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp63_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp63_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp63_pdm_dma_open,
+ .close = acp63_pdm_dma_close,
+ .hw_params = acp63_pdm_dma_hw_params,
+ .pointer = acp63_pdm_dma_pointer,
+ .pcm_construct = acp63_pdm_dma_new,
+};
+
+static int acp63_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ struct acp63_dev_data *acp_data;
+ struct device *parent;
+ int status;
+
+ parent = pdev->dev.parent;
+ acp_data = dev_get_drvdata(parent);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp63_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp63_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+ adata->acp_lock = &acp_data->acp_lock;
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp63_pdm_component,
+ &acp63_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp63_base);
+ }
+ acp63_enable_pdm_interrupts(adata);
+ return 0;
+}
+
+static int acp63_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp63_disable_pdm_interrupts(adata);
+ return 0;
+}
+
+static int acp63_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp63_enable_pdm_interrupts(adata);
+ return 0;
+}
+
+static const struct dev_pm_ops acp63_pdm_pm_ops = {
+ RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume)
+};
+
+static struct platform_driver acp63_pdm_dma_driver = {
+ .probe = acp63_pdm_audio_probe,
+ .remove = acp63_pdm_audio_remove,
+ .driver = {
+ .name = "acp_ps_pdm_dma",
+ .pm = pm_ptr(&acp63_pdm_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_pdm_dma_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
new file mode 100644
index 000000000000..5449323e2728
--- /dev/null
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC common SoundWire DMA Driver for ACP6.3, ACP7.0 and ACP7.1
+ * platforms.
+ *
+ * Copyright 2023, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_amd.h>
+#include "acp63.h"
+
+#define DRV_NAME "amd_ps_sdw_dma"
+
+static struct sdw_dma_ring_buf_reg acp63_sdw0_dma_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register
+ * set as per hardware register documentation
+ */
+static struct sdw_dma_ring_buf_reg acp63_sdw1_dma_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+};
+
+static u32 acp63_sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+ ACP_SW0_AUDIO0_TX_EN,
+ ACP_SW0_AUDIO1_TX_EN,
+ ACP_SW0_AUDIO2_TX_EN,
+ ACP_SW0_AUDIO0_RX_EN,
+ ACP_SW0_AUDIO1_RX_EN,
+ ACP_SW0_AUDIO2_RX_EN,
+};
+
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA enable register programming for SDW1 instance,
+ * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers
+ * as per hardware register documentation.
+ */
+static u32 acp63_sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ ACP_SW1_AUDIO1_TX_EN,
+ ACP_SW1_AUDIO1_RX_EN,
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw0_dma_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw1_dma_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO0_TX_DMA_SIZE, ACP_P1_AUDIO0_TX_FIFOADDR, ACP_P1_AUDIO0_TX_FIFOSIZE,
+ ACP_P1_AUDIO0_TX_RINGBUFSIZE, ACP_P1_AUDIO0_TX_RINGBUFADDR,
+ ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_TX_DMA_SIZE, ACP_P1_AUDIO2_TX_FIFOADDR, ACP_P1_AUDIO2_TX_FIFOSIZE,
+ ACP_P1_AUDIO2_TX_RINGBUFSIZE, ACP_P1_AUDIO2_TX_RINGBUFADDR,
+ ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO0_RX_DMA_SIZE, ACP_P1_AUDIO0_RX_FIFOADDR, ACP_P1_AUDIO0_RX_FIFOSIZE,
+ ACP_P1_AUDIO0_RX_RINGBUFSIZE, ACP_P1_AUDIO0_RX_RINGBUFADDR,
+ ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_RX_DMA_SIZE, ACP_P1_AUDIO2_RX_FIFOADDR, ACP_P1_AUDIO2_RX_FIFOSIZE,
+ ACP_P1_AUDIO2_RX_RINGBUFSIZE, ACP_P1_AUDIO2_RX_RINGBUFADDR,
+ ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static u32 acp70_sdw0_dma_enable_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ ACP70_SW0_AUDIO0_TX_EN,
+ ACP70_SW0_AUDIO1_TX_EN,
+ ACP70_SW0_AUDIO2_TX_EN,
+ ACP70_SW0_AUDIO0_RX_EN,
+ ACP70_SW0_AUDIO1_RX_EN,
+ ACP70_SW0_AUDIO2_RX_EN,
+};
+
+static u32 acp70_sdw1_dma_enable_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ ACP70_SW1_AUDIO0_TX_EN,
+ ACP70_SW1_AUDIO1_TX_EN,
+ ACP70_SW1_AUDIO2_TX_EN,
+ ACP70_SW1_AUDIO0_RX_EN,
+ ACP70_SW1_AUDIO1_RX_EN,
+ ACP70_SW1_AUDIO2_RX_EN,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, u32 irq_mask,
+ u32 irq_mask1, bool enable)
+{
+ u32 ext_intr_cntl, ext_intr_cntl1;
+
+ if (enable) {
+ ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl |= irq_mask;
+ writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= irq_mask1;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ } else {
+ ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl &= ~irq_mask;
+ writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 &= ~irq_mask1;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ }
+}
+
+static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 stream_id)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ u32 sdw_dma_pte_offset;
+ dma_addr_t addr;
+
+ addr = stream->dma_addr;
+ sdw_dma_pte_offset = SDW_PTE_OFFSET(stream->instance);
+ val = sdw_dma_pte_offset + (stream_id * ACP_SDW_PTE_OFFSET);
+
+ /* Group Enable */
+ writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+ writel(PAGE_SIZE_4K_ENABLE, acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+ for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ writel(low, acp_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ writel(high, acp_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+ writel(0x1, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size,
+ u32 manager_instance, u32 acp_rev)
+{
+ u32 reg_dma_size;
+ u32 reg_fifo_addr;
+ u32 reg_fifo_size;
+ u32 reg_ring_buf_size;
+ u32 reg_ring_buf_addr;
+ u32 sdw_fifo_addr;
+ u32 sdw_fifo_offset;
+ u32 sdw_ring_buf_addr;
+ u32 sdw_ring_buf_size;
+ u32 sdw_mem_window_offset;
+
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp63_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp63_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp70_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance);
+ sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance);
+ sdw_fifo_addr = sdw_fifo_offset + (stream_id * SDW_FIFO_OFFSET);
+ sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET);
+ sdw_ring_buf_size = size;
+ writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size);
+ writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr);
+ writel(sdw_fifo_addr, acp_base + reg_fifo_addr);
+ writel(SDW_DMA_SIZE, acp_base + reg_dma_size);
+ writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size);
+ return 0;
+}
+
+static int acp63_sdw_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct acp_sdw_dma_stream *stream;
+ struct snd_soc_dai *cpu_dai;
+ struct amd_sdw_manager *amd_manager;
+ struct snd_soc_pcm_runtime *prtd = snd_soc_substream_to_rtd(substream);
+ int ret;
+
+ runtime = substream->runtime;
+ cpu_dai = snd_soc_rtd_to_cpu(prtd, 0);
+ amd_manager = snd_soc_dai_get_drvdata(cpu_dai);
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp63_sdw_hardware_playback;
+ else
+ runtime->hw = acp63_sdw_hardware_capture;
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ stream->stream_id = cpu_dai->id;
+ stream->instance = amd_manager->instance;
+ runtime->private_data = stream;
+ return ret;
+}
+
+static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct sdw_dma_dev_data *sdw_data;
+ u32 period_bytes;
+ u32 water_mark_size_reg;
+ u32 irq_mask, ext_intr_ctrl;
+ u64 size;
+ u32 stream_id;
+ u32 acp_ext_intr_cntl_reg;
+ int ret;
+
+ sdw_data = dev_get_drvdata(component->dev);
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return -EINVAL;
+ stream_id = stream->stream_id;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp63_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP63_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP63_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp63_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ irq_mask = BIT(ACP63_SDW1_DMA_IRQ_MASK(stream_id));
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp70_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp70_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW1_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW1_DMA_RX_IRQ_MASK(stream_id));
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ stream->dma_addr = substream->runtime->dma_addr;
+ stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp63_config_dma(stream, sdw_data->acp_base, stream_id);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size,
+ stream->instance, sdw_data->acp_rev);
+ if (ret) {
+ dev_err(component->dev, "Invalid DMA channel\n");
+ return -EINVAL;
+ }
+ ext_intr_ctrl = readl(sdw_data->acp_base + acp_ext_intr_cntl_reg);
+ ext_intr_ctrl |= irq_mask;
+ writel(ext_intr_ctrl, sdw_data->acp_base + acp_ext_intr_cntl_reg);
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ return 0;
+}
+
+static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 acp_rev)
+{
+ union acp_sdw_dma_count byte_count;
+ u32 pos_low_reg, pos_high_reg;
+
+ byte_count.bytescount = 0;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ if (pos_low_reg) {
+ byte_count.bcount.high = readl(acp_base + pos_high_reg);
+ byte_count.bcount.low = readl(acp_base + pos_low_reg);
+ }
+POINTER_RETURN_BYTES:
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *substream)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp_sdw_dma_stream *stream;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ sdw_data = dev_get_drvdata(comp->dev);
+ stream = substream->runtime->private_data;
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base, sdw_data->acp_rev);
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp63_sdw_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER);
+ return 0;
+}
+
+static int acp63_sdw_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp_sdw_dma_stream *stream;
+
+ sdw_data = dev_get_drvdata(component->dev);
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return -EINVAL;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ kfree(stream);
+ return 0;
+}
+
+static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
+ void __iomem *acp_base, u32 acp_rev, bool sdw_dma_enable)
+{
+ struct acp_sdw_dma_stream *stream;
+ u32 stream_id;
+ u32 sdw_dma_en_reg;
+ u32 sdw_dma_en_stat_reg;
+ u32 sdw_dma_stat;
+ u32 dma_enable;
+
+ stream = substream->runtime->private_data;
+ stream_id = stream->stream_id;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp63_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp63_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ case ACP72_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp70_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ sdw_dma_en_stat_reg = sdw_dma_en_reg + 4;
+ dma_enable = sdw_dma_enable;
+ writel(dma_enable, acp_base + sdw_dma_en_reg);
+ return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat,
+ (sdw_dma_stat == dma_enable), ACP_DELAY_US, ACP_COUNTER);
+}
+
+static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
+ struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ int ret;
+
+ sdw_data = dev_get_drvdata(comp->dev);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, true);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, false);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ dev_err(comp->dev, "trigger %d failed: %d", cmd, ret);
+ return ret;
+}
+
+static const struct snd_soc_component_driver acp63_sdw_component = {
+ .name = DRV_NAME,
+ .open = acp63_sdw_dma_open,
+ .close = acp63_sdw_dma_close,
+ .hw_params = acp63_sdw_dma_hw_params,
+ .trigger = acp63_sdw_dma_trigger,
+ .pointer = acp63_sdw_dma_pointer,
+ .pcm_construct = acp63_sdw_dma_new,
+ .use_dai_pcm_id = true,
+
+};
+
+static int acp63_sdw_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp63_dev_data *acp_data;
+ struct device *parent;
+ int status;
+
+ parent = pdev->dev.parent;
+ acp_data = dev_get_drvdata(parent);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
+ if (!sdw_data)
+ return -ENOMEM;
+
+ sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!sdw_data->acp_base)
+ return -ENOMEM;
+
+ sdw_data->acp_lock = &acp_data->acp_lock;
+ sdw_data->acp_rev = acp_data->acp_rev;
+ dev_set_drvdata(&pdev->dev, sdw_data);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp63_sdw_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register sdw dma component\n");
+ return status;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_sdw_platform_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP63_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP63_P1_SDW_DMA_IRQ_MASK;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ if (instance == ACP_SDW0)
+ stream_count = ACP63_SDW0_DMA_MAX_STREAMS;
+ else
+ stream_count = ACP63_SDW1_DMA_MAX_STREAMS;
+
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp63_sdw0_dma_stream[index];
+ water_mark_size_reg = acp63_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp63_sdw1_dma_stream[index];
+ water_mark_size_reg = acp63_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ ACP63_PCI_REV);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp70_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP70_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP70_P1_SDW_DMA_IRQ_MASK;
+ stream_count = ACP70_SDW0_DMA_MAX_STREAMS;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp70_sdw0_dma_stream[index];
+ water_mark_size_reg = acp70_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp70_sdw1_dma_stream[index];
+ water_mark_size_reg = acp70_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ sdw_data->acp_rev);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp63_sdw_pcm_resume(struct device *dev)
+{
+ struct sdw_dma_dev_data *sdw_data;
+
+ sdw_data = dev_get_drvdata(dev);
+ if (sdw_data->acp_rev == ACP63_PCI_REV)
+ return acp63_restore_sdw_dma_config(sdw_data);
+ else
+ return acp70_restore_sdw_dma_config(sdw_data);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume)
+};
+
+static struct platform_driver acp63_sdw_dma_driver = {
+ .probe = acp63_sdw_platform_probe,
+ .remove = acp63_sdw_platform_remove,
+ .driver = {
+ .name = "amd_ps_sdw_dma",
+ .pm = pm_ptr(&acp63_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_sdw_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD common SDW DMA Driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile
new file mode 100644
index 000000000000..b2ea030cbf25
--- /dev/null
+++ b/sound/soc/amd/raven/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Raven Ridge platform Support
+snd-pci-acp3x-y := pci-acp3x.o
+snd-acp3x-pcm-dma-y := acp3x-pcm-dma.o
+snd-acp3x-i2s-y := acp3x-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-i2s.o
diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c
new file mode 100644
index 000000000000..352485dd98b1
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "acp3x.h"
+
+#define DRV_NAME "acp3x_i2s_playcap"
+
+static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct i2s_dev_data *adata;
+ int mode;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ u32 tx_mask, u32 rx_mask, int slots, int slot_width)
+{
+ struct i2s_dev_data *adata;
+ u32 frm_len;
+ u16 slot_len;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* These values are as per Hardware Spec */
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
+ adata->tdm_fmt = frm_len;
+ return 0;
+}
+
+static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp3x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u32 val;
+ u32 reg_val, frmt_reg;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ rtd = substream->runtime->private_data;
+ card = prtd->card;
+ adata = snd_soc_dai_get_drvdata(dai);
+ pinfo = snd_soc_card_get_drvdata(card);
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ else
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ }
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ rtd->xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rtd->xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ rtd->xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rtd->xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_ITER;
+ frmt_reg = mmACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_ITER;
+ frmt_reg = mmACP_I2STDM_TXFRMT;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_IRER;
+ frmt_reg = mmACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_IRER;
+ frmt_reg = mmACP_I2STDM_RXFRMT;
+ }
+ }
+ if (adata->tdm_mode) {
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ rv_writel(val | 0x2, rtd->acp3x_base + reg_val);
+ rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg);
+ }
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (rtd->xfer_resolution << 3);
+ rv_writel(val, rtd->acp3x_base + reg_val);
+ return 0;
+}
+
+static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ u32 val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+ int ret;
+
+ rtd = substream->runtime->private_data;
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rtd->bytescount = acp_get_byte_count(rtd,
+ substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ water_val =
+ mmACP_BT_TX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_BTTDM_ITER;
+ ier_val = mmACP_BTTDM_IER;
+ buf_reg = mmACP_BT_TX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ mmACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_I2STDM_ITER;
+ ier_val = mmACP_I2STDM_IER;
+ buf_reg = mmACP_I2S_TX_RINGBUFSIZE;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ water_val =
+ mmACP_BT_RX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_BTTDM_IRER;
+ ier_val = mmACP_BTTDM_IER;
+ buf_reg = mmACP_BT_RX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ mmACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = mmACP_I2STDM_IRER;
+ ier_val = mmACP_I2STDM_IER;
+ buf_reg = mmACP_I2S_RX_RINGBUFSIZE;
+ }
+ }
+ rv_writel(period_bytes, rtd->acp3x_base + water_val);
+ rv_writel(buf_size, rtd->acp3x_base + buf_reg);
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ val = val | BIT(0);
+ rv_writel(val, rtd->acp3x_base + reg_val);
+ rv_writel(1, rtd->acp3x_base + ier_val);
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_ITER;
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_IRER;
+ }
+ }
+ val = rv_readl(rtd->acp3x_base + reg_val);
+ val = val & ~BIT(0);
+ rv_writel(val, rtd->acp3x_base + reg_val);
+
+ if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&
+ !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))
+ rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
+ if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&
+ !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))
+ rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
+ .hw_params = acp3x_i2s_hwparams,
+ .trigger = acp3x_i2s_trigger,
+ .set_fmt = acp3x_i2s_set_fmt,
+ .set_tdm_slot = acp3x_i2s_set_tdm_slot,
+};
+
+static const struct snd_soc_component_driver acp3x_dai_component = {
+ .name = DRV_NAME,
+ .legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver acp3x_i2s_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp3x_i2s_dai_ops,
+};
+
+static int acp3x_dai_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ int ret;
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
+ GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENOMEM;
+ }
+ adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp3x_base)
+ return -ENOMEM;
+
+ adata->i2s_irq = res->start;
+ dev_set_drvdata(&pdev->dev, adata);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &acp3x_dai_component, &acp3x_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct platform_driver acp3x_dai_driver = {
+ .probe = acp3x_dai_probe,
+ .driver = {
+ .name = "acp3x_i2s_playcap",
+ },
+};
+
+module_platform_driver(acp3x_dai_driver);
+
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c
new file mode 100644
index 000000000000..bb9ed52d744d
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "acp3x.h"
+
+#define DRV_NAME "acp3x_rv_i2s_dma"
+
+static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+ struct i2s_dev_data *rv_i2s_data;
+ u16 play_flag, cap_flag;
+ u32 val;
+
+ rv_i2s_data = dev_id;
+ if (!rv_i2s_data)
+ return IRQ_NONE;
+
+ play_flag = 0;
+ cap_flag = 0;
+ val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) {
+ rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->play_stream);
+ play_flag = 1;
+ }
+ if ((val & BIT(I2S_TX_THRESHOLD)) &&
+ rv_i2s_data->i2ssp_play_stream) {
+ rv_writel(BIT(I2S_TX_THRESHOLD),
+ rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->i2ssp_play_stream);
+ play_flag = 1;
+ }
+
+ if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) {
+ rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->capture_stream);
+ cap_flag = 1;
+ }
+ if ((val & BIT(I2S_RX_THRESHOLD)) &&
+ rv_i2s_data->i2ssp_capture_stream) {
+ rv_writel(BIT(I2S_RX_THRESHOLD),
+ rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rv_i2s_data->i2ssp_capture_stream);
+ cap_flag = 1;
+ }
+
+ if (play_flag | cap_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
+ u32 reg_dma_size, reg_fifo_size;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val = ACP_SRAM_BT_PB_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_PB_PTE_OFFSET;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ val = ACP_SRAM_BT_CP_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_CP_PTE_OFFSET;
+ }
+ }
+ /* Group Enable */
+ rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base +
+ mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base +
+ mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val
+ + 4);
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_dma_size = mmACP_BT_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_BT_TX_FIFOADDR;
+ reg_fifo_size = mmACP_BT_TX_FIFOSIZE;
+ rv_writel(I2S_BT_TX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_BT_TX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = mmACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = mmACP_I2S_TX_FIFOSIZE;
+ rv_writel(I2S_SP_TX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_I2S_TX_RINGBUFADDR);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_dma_size = mmACP_BT_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_BT_RX_FIFOADDR;
+ reg_fifo_size = mmACP_BT_RX_FIFOSIZE;
+ rv_writel(I2S_BT_RX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_BT_RX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = mmACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = mmACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = mmACP_I2S_RX_FIFOSIZE;
+ rv_writel(I2S_SP_RX_MEM_WINDOW_START,
+ rtd->acp3x_base + mmACP_I2S_RX_RINGBUFADDR);
+ }
+ }
+ rv_writel(DMA_SIZE, rtd->acp3x_base + reg_dma_size);
+ rv_writel(acp_fifo_addr, rtd->acp3x_base + reg_fifo_addr);
+ rv_writel(FIFO_SIZE, rtd->acp3x_base + reg_fifo_size);
+ rv_writel(BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
+ | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD),
+ rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);
+}
+
+static int acp3x_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *i2s_data;
+ int ret;
+
+ runtime = substream->runtime;
+ prtd = snd_soc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
+ if (!i2s_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp3x_pcm_hardware_playback;
+ else
+ runtime->hw = acp3x_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(i2s_data);
+ return ret;
+ }
+
+ i2s_data->acp3x_base = adata->acp3x_base;
+ runtime->private_data = i2s_data;
+ return ret;
+}
+
+
+static int acp3x_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp3x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u64 size;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ card = prtd->card;
+ pinfo = snd_soc_card_get_drvdata(card);
+ adata = dev_get_drvdata(component->dev);
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->play_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = substream;
+ }
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->capture_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = substream;
+ }
+ }
+ } else {
+ pr_err("pinfo failed\n");
+ }
+ size = params_buffer_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp3x_dma(rtd, substream->stream);
+ return 0;
+}
+
+static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_stream_instance *rtd;
+ u32 pos;
+ u32 buffersize;
+ u64 bytescount;
+
+ rtd = substream->runtime->private_data;
+
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd, substream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp3x_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp3x_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *ins;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ ins = substream->runtime->private_data;
+ if (!ins)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (ins->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->play_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = NULL;
+ }
+ } else {
+ switch (ins->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ adata->capture_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp3x_i2s_component = {
+ .name = DRV_NAME,
+ .open = acp3x_dma_open,
+ .close = acp3x_dma_close,
+ .hw_params = acp3x_dma_hw_params,
+ .pointer = acp3x_dma_pointer,
+ .pcm_construct = acp3x_dma_new,
+};
+
+static int acp3x_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp3x_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->i2s_irq = status;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp3x_i2s_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp i2s component\n");
+ return -ENODEV;
+ }
+ status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
+ irqflags, "ACP3x_I2S_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n");
+ return -ENODEV;
+ }
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp3x_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp3x_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+ u32 val, reg_val, frmt_val;
+
+ reg_val = 0;
+ frmt_val = 0;
+ adata = dev_get_drvdata(dev);
+
+ if (adata->play_stream && adata->play_stream->runtime) {
+ struct i2s_stream_instance *rtd =
+ adata->play_stream->runtime->private_data;
+ config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_ITER;
+ frmt_val = mmACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_ITER;
+ frmt_val = mmACP_I2STDM_TXFRMT;
+ }
+ rv_writel((rtd->xfer_resolution << 3),
+ rtd->acp3x_base + reg_val);
+ }
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ struct i2s_stream_instance *rtd =
+ adata->capture_stream->runtime->private_data;
+ config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ reg_val = mmACP_BTTDM_IRER;
+ frmt_val = mmACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = mmACP_I2STDM_IRER;
+ frmt_val = mmACP_I2STDM_RXFRMT;
+ }
+ rv_writel((rtd->xfer_resolution << 3),
+ rtd->acp3x_base + reg_val);
+ }
+ if (adata->tdm_mode == TDM_ENABLE) {
+ rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val);
+ val = rv_readl(adata->acp3x_base + reg_val);
+ rv_writel(val | 0x2, adata->acp3x_base + reg_val);
+ }
+ rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+
+static int acp3x_pcm_runtime_suspend(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+
+ rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
+
+ return 0;
+}
+
+static int acp3x_pcm_runtime_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+
+ rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static const struct dev_pm_ops acp3x_pm_ops = {
+ .runtime_suspend = acp3x_pcm_runtime_suspend,
+ .runtime_resume = acp3x_pcm_runtime_resume,
+ .resume = acp3x_resume,
+};
+
+static struct platform_driver acp3x_dma_driver = {
+ .probe = acp3x_audio_probe,
+ .remove = acp3x_audio_remove,
+ .driver = {
+ .name = "acp3x_rv_i2s_dma",
+ .pm = &acp3x_pm_ops,
+ },
+};
+
+module_platform_driver(acp3x_dma_driver);
+
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h
new file mode 100644
index 000000000000..7702f628ecd6
--- /dev/null
+++ b/sound/soc/amd/raven/acp3x.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PCM Driver
+ *
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ */
+
+#include "chip_offset_byte.h"
+#include <sound/pcm.h>
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+
+#define ACP3x_DEVS 4
+#define ACP3x_PHY_BASE_ADDRESS 0x1240000
+#define ACP3x_I2S_MODE 0
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x1250200
+#define ACP3x_I2STDM_REG_START 0x1242400
+#define ACP3x_I2STDM_REG_END 0x1242410
+#define ACP3x_BT_TDM_REG_START 0x1242800
+#define ACP3x_BT_TDM_REG_END 0x1242810
+#define I2S_MODE 0x04
+#define I2S_RX_THRESHOLD 27
+#define I2S_TX_THRESHOLD 28
+#define BT_TX_THRESHOLD 26
+#define BT_RX_THRESHOLD 25
+#define ACP_ERR_INTR_MASK 29
+#define ACP3x_POWER_ON 0x00
+#define ACP3x_POWER_ON_IN_PROGRESS 0x01
+#define ACP3x_POWER_OFF 0x02
+#define ACP3x_POWER_OFF_IN_PROGRESS 0x03
+#define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
+#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define BT_PB_FIFO_ADDR_OFFSET 0x900
+#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define SLOT_WIDTH_8 0x08
+#define SLOT_WIDTH_16 0x10
+#define SLOT_WIDTH_24 0x18
+#define SLOT_WIDTH_32 0x20
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+struct acp3x_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+ u16 capture_channel;
+};
+
+struct i2s_dev_data {
+ bool tdm_mode;
+ int i2s_irq;
+ u16 i2s_instance;
+ u32 tdm_fmt;
+ u32 substream_type;
+ void __iomem *acp3x_base;
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+ struct snd_pcm_substream *i2ssp_play_stream;
+ struct snd_pcm_substream *i2ssp_capture_stream;
+};
+
+struct i2s_stream_instance {
+ u16 num_pages;
+ u16 i2s_instance;
+ u16 capture_channel;
+ u16 direction;
+ u16 channels;
+ u32 xfer_resolution;
+ u32 val;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp3x_base;
+};
+
+static inline u32 rv_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP3x_PHY_BASE_ADDRESS);
+}
+
+static inline void rv_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP3x_PHY_BASE_ADDRESS);
+}
+
+static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
+ int direction)
+{
+ u64 byte_count;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_BT_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_BT_INSTANCE:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_BT_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count = rv_readl(rtd->acp3x_base +
+ mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count |= rv_readl(rtd->acp3x_base +
+ mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
+ }
+ }
+ return byte_count;
+}
diff --git a/sound/soc/amd/raven/chip_offset_byte.h b/sound/soc/amd/raven/chip_offset_byte.h
new file mode 100644
index 000000000000..9c1fac58fb2a
--- /dev/null
+++ b/sound/soc/amd/raven/chip_offset_byte.h
@@ -0,0 +1,639 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 3.0 Register Documentation
+ *
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp_ip_OFFSET_HEADER
+#define _acp_ip_OFFSET_HEADER
+// Registers from ACP_DMA block
+
+#define mmACP_DMA_CNTL_0 0x1240000
+#define mmACP_DMA_CNTL_1 0x1240004
+#define mmACP_DMA_CNTL_2 0x1240008
+#define mmACP_DMA_CNTL_3 0x124000C
+#define mmACP_DMA_CNTL_4 0x1240010
+#define mmACP_DMA_CNTL_5 0x1240014
+#define mmACP_DMA_CNTL_6 0x1240018
+#define mmACP_DMA_CNTL_7 0x124001C
+#define mmACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define mmACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define mmACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define mmACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define mmACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define mmACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define mmACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define mmACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define mmACP_DMA_DSCR_CNT_0 0x1240040
+#define mmACP_DMA_DSCR_CNT_1 0x1240044
+#define mmACP_DMA_DSCR_CNT_2 0x1240048
+#define mmACP_DMA_DSCR_CNT_3 0x124004C
+#define mmACP_DMA_DSCR_CNT_4 0x1240050
+#define mmACP_DMA_DSCR_CNT_5 0x1240054
+#define mmACP_DMA_DSCR_CNT_6 0x1240058
+#define mmACP_DMA_DSCR_CNT_7 0x124005C
+#define mmACP_DMA_PRIO_0 0x1240060
+#define mmACP_DMA_PRIO_1 0x1240064
+#define mmACP_DMA_PRIO_2 0x1240068
+#define mmACP_DMA_PRIO_3 0x124006C
+#define mmACP_DMA_PRIO_4 0x1240070
+#define mmACP_DMA_PRIO_5 0x1240074
+#define mmACP_DMA_PRIO_6 0x1240078
+#define mmACP_DMA_PRIO_7 0x124007C
+#define mmACP_DMA_CUR_DSCR_0 0x1240080
+#define mmACP_DMA_CUR_DSCR_1 0x1240084
+#define mmACP_DMA_CUR_DSCR_2 0x1240088
+#define mmACP_DMA_CUR_DSCR_3 0x124008C
+#define mmACP_DMA_CUR_DSCR_4 0x1240090
+#define mmACP_DMA_CUR_DSCR_5 0x1240094
+#define mmACP_DMA_CUR_DSCR_6 0x1240098
+#define mmACP_DMA_CUR_DSCR_7 0x124009C
+#define mmACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define mmACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define mmACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define mmACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define mmACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define mmACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define mmACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define mmACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define mmACP_DMA_ERR_STS_0 0x12400C0
+#define mmACP_DMA_ERR_STS_1 0x12400C4
+#define mmACP_DMA_ERR_STS_2 0x12400C8
+#define mmACP_DMA_ERR_STS_3 0x12400CC
+#define mmACP_DMA_ERR_STS_4 0x12400D0
+#define mmACP_DMA_ERR_STS_5 0x12400D4
+#define mmACP_DMA_ERR_STS_6 0x12400D8
+#define mmACP_DMA_ERR_STS_7 0x12400DC
+#define mmACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define mmACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define mmACP_DMA_CH_STS 0x12400E8
+#define mmACP_DMA_CH_GROUP 0x12400EC
+#define mmACP_DMA_CH_RST_STS 0x12400F0
+
+
+// Registers from ACP_AXI2AXIATU block
+
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define mmACPAXI2AXI_ATU_CTRL 0x1240C40
+
+
+// Registers from ACP_CLKRST block
+
+#define mmACP_SOFT_RESET 0x1241000
+#define mmACP_CONTROL 0x1241004
+#define mmACP_STATUS 0x1241008
+#define mmACP_DSP0_OCD_HALT_ON_RST 0x124100C
+#define mmACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+
+// Registers from ACP_MISC block
+
+#define mmACP_EXTERNAL_INTR_ENB 0x1241800
+#define mmACP_EXTERNAL_INTR_CNTL 0x1241804
+#define mmACP_EXTERNAL_INTR_STAT 0x1241808
+#define mmACP_DSP0_INTR_CNTL 0x124180C
+#define mmACP_DSP0_INTR_STAT 0x1241810
+#define mmACP_DSP_SW_INTR_CNTL 0x1241814
+#define mmACP_DSP_SW_INTR_STAT 0x1241818
+#define mmACP_SW_INTR_TRIG 0x124181C
+#define mmACP_SMU_MAILBOX 0x1241820
+#define mmDSP_INTERRUPT_ROUTING_CTRL 0x1241824
+#define mmACP_DSP0_WATCHDOG_TIMER_CNTL 0x1241828
+#define mmACP_DSP0_EXT_TIMER1_CNTL 0x124182C
+#define mmACP_DSP0_EXT_TIMER2_CNTL 0x1241830
+#define mmACP_DSP0_EXT_TIMER3_CNTL 0x1241834
+#define mmACP_DSP0_EXT_TIMER4_CNTL 0x1241838
+#define mmACP_DSP0_EXT_TIMER5_CNTL 0x124183C
+#define mmACP_DSP0_EXT_TIMER6_CNTL 0x1241840
+#define mmACP_DSP0_EXT_TIMER1_CURR_VALUE 0x1241844
+#define mmACP_DSP0_EXT_TIMER2_CURR_VALUE 0x1241848
+#define mmACP_DSP0_EXT_TIMER3_CURR_VALUE 0x124184C
+#define mmACP_DSP0_EXT_TIMER4_CURR_VALUE 0x1241850
+#define mmACP_DSP0_EXT_TIMER5_CURR_VALUE 0x1241854
+#define mmACP_DSP0_EXT_TIMER6_CURR_VALUE 0x1241858
+#define mmACP_FW_STATUS 0x124185C
+#define mmACP_TIMER 0x1241874
+#define mmACP_TIMER_CNTL 0x1241878
+#define mmACP_PGMEM_CTRL 0x12418C0
+#define mmACP_ERROR_STATUS 0x12418C4
+#define mmACP_SW_I2S_ERROR_REASON 0x12418C8
+#define mmACP_MEM_PG_STS 0x12418CC
+
+
+// Registers from ACP_PGFSM block
+
+#define mmACP_I2S_PIN_CONFIG 0x1241400
+#define mmACP_PAD_PULLUP_PULLDOWN_CTRL 0x1241404
+#define mmACP_PAD_DRIVE_STRENGTH_CTRL 0x1241408
+#define mmACP_SW_PAD_KEEPER_EN 0x124140C
+#define mmACP_SW_WAKE_EN 0x1241410
+#define mmACP_I2S_WAKE_EN 0x1241414
+#define mmACP_PME_EN 0x1241418
+#define mmACP_PGFSM_CONTROL 0x124141C
+#define mmACP_PGFSM_STATUS 0x1241420
+
+
+// Registers from ACP_SCRATCH block
+
+#define mmACP_SCRATCH_REG_0 0x1250000
+#define mmACP_SCRATCH_REG_1 0x1250004
+#define mmACP_SCRATCH_REG_2 0x1250008
+#define mmACP_SCRATCH_REG_3 0x125000C
+#define mmACP_SCRATCH_REG_4 0x1250010
+#define mmACP_SCRATCH_REG_5 0x1250014
+#define mmACP_SCRATCH_REG_6 0x1250018
+#define mmACP_SCRATCH_REG_7 0x125001C
+#define mmACP_SCRATCH_REG_8 0x1250020
+#define mmACP_SCRATCH_REG_9 0x1250024
+#define mmACP_SCRATCH_REG_10 0x1250028
+#define mmACP_SCRATCH_REG_11 0x125002C
+#define mmACP_SCRATCH_REG_12 0x1250030
+#define mmACP_SCRATCH_REG_13 0x1250034
+#define mmACP_SCRATCH_REG_14 0x1250038
+#define mmACP_SCRATCH_REG_15 0x125003C
+#define mmACP_SCRATCH_REG_16 0x1250040
+#define mmACP_SCRATCH_REG_17 0x1250044
+#define mmACP_SCRATCH_REG_18 0x1250048
+#define mmACP_SCRATCH_REG_19 0x125004C
+#define mmACP_SCRATCH_REG_20 0x1250050
+#define mmACP_SCRATCH_REG_21 0x1250054
+#define mmACP_SCRATCH_REG_22 0x1250058
+#define mmACP_SCRATCH_REG_23 0x125005C
+#define mmACP_SCRATCH_REG_24 0x1250060
+#define mmACP_SCRATCH_REG_25 0x1250064
+#define mmACP_SCRATCH_REG_26 0x1250068
+#define mmACP_SCRATCH_REG_27 0x125006C
+#define mmACP_SCRATCH_REG_28 0x1250070
+#define mmACP_SCRATCH_REG_29 0x1250074
+#define mmACP_SCRATCH_REG_30 0x1250078
+#define mmACP_SCRATCH_REG_31 0x125007C
+#define mmACP_SCRATCH_REG_32 0x1250080
+#define mmACP_SCRATCH_REG_33 0x1250084
+#define mmACP_SCRATCH_REG_34 0x1250088
+#define mmACP_SCRATCH_REG_35 0x125008C
+#define mmACP_SCRATCH_REG_36 0x1250090
+#define mmACP_SCRATCH_REG_37 0x1250094
+#define mmACP_SCRATCH_REG_38 0x1250098
+#define mmACP_SCRATCH_REG_39 0x125009C
+#define mmACP_SCRATCH_REG_40 0x12500A0
+#define mmACP_SCRATCH_REG_41 0x12500A4
+#define mmACP_SCRATCH_REG_42 0x12500A8
+#define mmACP_SCRATCH_REG_43 0x12500AC
+#define mmACP_SCRATCH_REG_44 0x12500B0
+#define mmACP_SCRATCH_REG_45 0x12500B4
+#define mmACP_SCRATCH_REG_46 0x12500B8
+#define mmACP_SCRATCH_REG_47 0x12500BC
+#define mmACP_SCRATCH_REG_48 0x12500C0
+#define mmACP_SCRATCH_REG_49 0x12500C4
+#define mmACP_SCRATCH_REG_50 0x12500C8
+#define mmACP_SCRATCH_REG_51 0x12500CC
+#define mmACP_SCRATCH_REG_52 0x12500D0
+#define mmACP_SCRATCH_REG_53 0x12500D4
+#define mmACP_SCRATCH_REG_54 0x12500D8
+#define mmACP_SCRATCH_REG_55 0x12500DC
+#define mmACP_SCRATCH_REG_56 0x12500E0
+#define mmACP_SCRATCH_REG_57 0x12500E4
+#define mmACP_SCRATCH_REG_58 0x12500E8
+#define mmACP_SCRATCH_REG_59 0x12500EC
+#define mmACP_SCRATCH_REG_60 0x12500F0
+#define mmACP_SCRATCH_REG_61 0x12500F4
+#define mmACP_SCRATCH_REG_62 0x12500F8
+#define mmACP_SCRATCH_REG_63 0x12500FC
+#define mmACP_SCRATCH_REG_64 0x1250100
+#define mmACP_SCRATCH_REG_65 0x1250104
+#define mmACP_SCRATCH_REG_66 0x1250108
+#define mmACP_SCRATCH_REG_67 0x125010C
+#define mmACP_SCRATCH_REG_68 0x1250110
+#define mmACP_SCRATCH_REG_69 0x1250114
+#define mmACP_SCRATCH_REG_70 0x1250118
+#define mmACP_SCRATCH_REG_71 0x125011C
+#define mmACP_SCRATCH_REG_72 0x1250120
+#define mmACP_SCRATCH_REG_73 0x1250124
+#define mmACP_SCRATCH_REG_74 0x1250128
+#define mmACP_SCRATCH_REG_75 0x125012C
+#define mmACP_SCRATCH_REG_76 0x1250130
+#define mmACP_SCRATCH_REG_77 0x1250134
+#define mmACP_SCRATCH_REG_78 0x1250138
+#define mmACP_SCRATCH_REG_79 0x125013C
+#define mmACP_SCRATCH_REG_80 0x1250140
+#define mmACP_SCRATCH_REG_81 0x1250144
+#define mmACP_SCRATCH_REG_82 0x1250148
+#define mmACP_SCRATCH_REG_83 0x125014C
+#define mmACP_SCRATCH_REG_84 0x1250150
+#define mmACP_SCRATCH_REG_85 0x1250154
+#define mmACP_SCRATCH_REG_86 0x1250158
+#define mmACP_SCRATCH_REG_87 0x125015C
+#define mmACP_SCRATCH_REG_88 0x1250160
+#define mmACP_SCRATCH_REG_89 0x1250164
+#define mmACP_SCRATCH_REG_90 0x1250168
+#define mmACP_SCRATCH_REG_91 0x125016C
+#define mmACP_SCRATCH_REG_92 0x1250170
+#define mmACP_SCRATCH_REG_93 0x1250174
+#define mmACP_SCRATCH_REG_94 0x1250178
+#define mmACP_SCRATCH_REG_95 0x125017C
+#define mmACP_SCRATCH_REG_96 0x1250180
+#define mmACP_SCRATCH_REG_97 0x1250184
+#define mmACP_SCRATCH_REG_98 0x1250188
+#define mmACP_SCRATCH_REG_99 0x125018C
+#define mmACP_SCRATCH_REG_100 0x1250190
+#define mmACP_SCRATCH_REG_101 0x1250194
+#define mmACP_SCRATCH_REG_102 0x1250198
+#define mmACP_SCRATCH_REG_103 0x125019C
+#define mmACP_SCRATCH_REG_104 0x12501A0
+#define mmACP_SCRATCH_REG_105 0x12501A4
+#define mmACP_SCRATCH_REG_106 0x12501A8
+#define mmACP_SCRATCH_REG_107 0x12501AC
+#define mmACP_SCRATCH_REG_108 0x12501B0
+#define mmACP_SCRATCH_REG_109 0x12501B4
+#define mmACP_SCRATCH_REG_110 0x12501B8
+#define mmACP_SCRATCH_REG_111 0x12501BC
+#define mmACP_SCRATCH_REG_112 0x12501C0
+#define mmACP_SCRATCH_REG_113 0x12501C4
+#define mmACP_SCRATCH_REG_114 0x12501C8
+#define mmACP_SCRATCH_REG_115 0x12501CC
+#define mmACP_SCRATCH_REG_116 0x12501D0
+#define mmACP_SCRATCH_REG_117 0x12501D4
+#define mmACP_SCRATCH_REG_118 0x12501D8
+#define mmACP_SCRATCH_REG_119 0x12501DC
+#define mmACP_SCRATCH_REG_120 0x12501E0
+#define mmACP_SCRATCH_REG_121 0x12501E4
+#define mmACP_SCRATCH_REG_122 0x12501E8
+#define mmACP_SCRATCH_REG_123 0x12501EC
+#define mmACP_SCRATCH_REG_124 0x12501F0
+#define mmACP_SCRATCH_REG_125 0x12501F4
+#define mmACP_SCRATCH_REG_126 0x12501F8
+#define mmACP_SCRATCH_REG_127 0x12501FC
+#define mmACP_SCRATCH_REG_128 0x1250200
+
+
+// Registers from ACP_SW_ACLK block
+
+#define mmSW_CORB_Base_Address 0x1243200
+#define mmSW_CORB_Write_Pointer 0x1243204
+#define mmSW_CORB_Read_Pointer 0x1243208
+#define mmSW_CORB_Control 0x124320C
+#define mmSW_CORB_Size 0x1243214
+#define mmSW_RIRB_Base_Address 0x1243218
+#define mmSW_RIRB_Write_Pointer 0x124321C
+#define mmSW_RIRB_Response_Interrupt_Count 0x1243220
+#define mmSW_RIRB_Control 0x1243224
+#define mmSW_RIRB_Size 0x1243228
+#define mmSW_RIRB_FIFO_MIN_THDL 0x124322C
+#define mmSW_imm_cmd_UPPER_WORD 0x1243230
+#define mmSW_imm_cmd_LOWER_QWORD 0x1243234
+#define mmSW_imm_resp_UPPER_WORD 0x1243238
+#define mmSW_imm_resp_LOWER_QWORD 0x124323C
+#define mmSW_imm_cmd_sts 0x1243240
+#define mmSW_BRA_BASE_ADDRESS 0x1243244
+#define mmSW_BRA_TRANSFER_SIZE 0x1243248
+#define mmSW_BRA_DMA_BUSY 0x124324C
+#define mmSW_BRA_RESP 0x1243250
+#define mmSW_BRA_RESP_FRAME_ADDR 0x1243254
+#define mmSW_BRA_CURRENT_TRANSFER_SIZE 0x1243258
+#define mmSW_STATE_CHANGE_STATUS_0TO7 0x124325C
+#define mmSW_STATE_CHANGE_STATUS_8TO11 0x1243260
+#define mmSW_STATE_CHANGE_STATUS_MASK_0to7 0x1243264
+#define mmSW_STATE_CHANGE_STATUS_MASK_8to11 0x1243268
+#define mmSW_CLK_FREQUENCY_CTRL 0x124326C
+#define mmSW_ERROR_INTR_MASK 0x1243270
+#define mmSW_PHY_TEST_MODE_DATA_OFF 0x1243274
+
+
+// Registers from ACP_SW_SWCLK block
+
+#define mmACP_SW_EN 0x1243000
+#define mmACP_SW_EN_STATUS 0x1243004
+#define mmACP_SW_FRAMESIZE 0x1243008
+#define mmACP_SW_SSP_Counter 0x124300C
+#define mmACP_SW_Audio_TX_EN 0x1243010
+#define mmACP_SW_Audio_TX_EN_STATUS 0x1243014
+#define mmACP_SW_Audio_TX_Frame_Format 0x1243018
+#define mmACP_SW_Audio_TX_SampleInterval 0x124301C
+#define mmACP_SW_Audio_TX_Hctrl_DP0 0x1243020
+#define mmACP_SW_Audio_TX_Hctrl_DP1 0x1243024
+#define mmACP_SW_Audio_TX_Hctrl_DP2 0x1243028
+#define mmACP_SW_Audio_TX_Hctrl_DP3 0x124302C
+#define mmACP_SW_Audio_TX_offset_DP0 0x1243030
+#define mmACP_SW_Audio_TX_offset_DP1 0x1243034
+#define mmACP_SW_Audio_TX_offset_DP2 0x1243038
+#define mmACP_SW_Audio_TX_offset_DP3 0x124303C
+#define mmACP_SW_Audio_TX_Channel_Enable_DP0 0x1243040
+#define mmACP_SW_Audio_TX_Channel_Enable_DP1 0x1243044
+#define mmACP_SW_Audio_TX_Channel_Enable_DP2 0x1243048
+#define mmACP_SW_Audio_TX_Channel_Enable_DP3 0x124304C
+#define mmACP_SW_BT_TX_EN 0x1243050
+#define mmACP_SW_BT_TX_EN_STATUS 0x1243054
+#define mmACP_SW_BT_TX_Frame_Format 0x1243058
+#define mmACP_SW_BT_TX_SampleInterval 0x124305C
+#define mmACP_SW_BT_TX_Hctrl 0x1243060
+#define mmACP_SW_BT_TX_offset 0x1243064
+#define mmACP_SW_BT_TX_Channel_Enable_DP0 0x1243068
+#define mmACP_SW_Headset_TX_EN 0x124306C
+#define mmACP_SW_Headset_TX_EN_STATUS 0x1243070
+#define mmACP_SW_Headset_TX_Frame_Format 0x1243074
+#define mmACP_SW_Headset_TX_SampleInterval 0x1243078
+#define mmACP_SW_Headset_TX_Hctrl 0x124307C
+#define mmACP_SW_Headset_TX_offset 0x1243080
+#define mmACP_SW_Headset_TX_Channel_Enable_DP0 0x1243084
+#define mmACP_SW_Audio_RX_EN 0x1243088
+#define mmACP_SW_Audio_RX_EN_STATUS 0x124308C
+#define mmACP_SW_Audio_RX_Frame_Format 0x1243090
+#define mmACP_SW_Audio_RX_SampleInterval 0x1243094
+#define mmACP_SW_Audio_RX_Hctrl_DP0 0x1243098
+#define mmACP_SW_Audio_RX_Hctrl_DP1 0x124309C
+#define mmACP_SW_Audio_RX_Hctrl_DP2 0x1243100
+#define mmACP_SW_Audio_RX_Hctrl_DP3 0x1243104
+#define mmACP_SW_Audio_RX_offset_DP0 0x1243108
+#define mmACP_SW_Audio_RX_offset_DP1 0x124310C
+#define mmACP_SW_Audio_RX_offset_DP2 0x1243110
+#define mmACP_SW_Audio_RX_offset_DP3 0x1243114
+#define mmACP_SW_Audio_RX_Channel_Enable_DP0 0x1243118
+#define mmACP_SW_Audio_RX_Channel_Enable_DP1 0x124311C
+#define mmACP_SW_Audio_RX_Channel_Enable_DP2 0x1243120
+#define mmACP_SW_Audio_RX_Channel_Enable_DP3 0x1243124
+#define mmACP_SW_BT_RX_EN 0x1243128
+#define mmACP_SW_BT_RX_EN_STATUS 0x124312C
+#define mmACP_SW_BT_RX_Frame_Format 0x1243130
+#define mmACP_SW_BT_RX_SampleInterval 0x1243134
+#define mmACP_SW_BT_RX_Hctrl 0x1243138
+#define mmACP_SW_BT_RX_offset 0x124313C
+#define mmACP_SW_BT_RX_Channel_Enable_DP0 0x1243140
+#define mmACP_SW_Headset_RX_EN 0x1243144
+#define mmACP_SW_Headset_RX_EN_STATUS 0x1243148
+#define mmACP_SW_Headset_RX_Frame_Format 0x124314C
+#define mmACP_SW_Headset_RX_SampleInterval 0x1243150
+#define mmACP_SW_Headset_RX_Hctrl 0x1243154
+#define mmACP_SW_Headset_RX_offset 0x1243158
+#define mmACP_SW_Headset_RX_Channel_Enable_DP0 0x124315C
+#define mmACP_SW_BPT_PORT_EN 0x1243160
+#define mmACP_SW_BPT_PORT_EN_STATUS 0x1243164
+#define mmACP_SW_BPT_PORT_Frame_Format 0x1243168
+#define mmACP_SW_BPT_PORT_SampleInterval 0x124316C
+#define mmACP_SW_BPT_PORT_Hctrl 0x1243170
+#define mmACP_SW_BPT_PORT_offset 0x1243174
+#define mmACP_SW_BPT_PORT_Channel_Enable 0x1243178
+#define mmACP_SW_BPT_PORT_First_byte_addr 0x124317C
+#define mmACP_SW_CLK_RESUME_CTRL 0x1243180
+#define mmACP_SW_CLK_RESUME_Delay_Cntr 0x1243184
+#define mmACP_SW_BUS_RESET_CTRL 0x1243188
+#define mmACP_SW_PRBS_ERR_STATUS 0x124318C
+
+
+// Registers from ACP_AUDIO_BUFFERS block
+
+#define mmACP_I2S_RX_RINGBUFADDR 0x1242000
+#define mmACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define mmACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define mmACP_I2S_RX_FIFOADDR 0x124200C
+#define mmACP_I2S_RX_FIFOSIZE 0x1242010
+#define mmACP_I2S_RX_DMA_SIZE 0x1242014
+#define mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define mmACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define mmACP_I2S_TX_RINGBUFADDR 0x1242024
+#define mmACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define mmACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define mmACP_I2S_TX_FIFOADDR 0x1242030
+#define mmACP_I2S_TX_FIFOSIZE 0x1242034
+#define mmACP_I2S_TX_DMA_SIZE 0x1242038
+#define mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define mmACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define mmACP_BT_RX_RINGBUFADDR 0x1242048
+#define mmACP_BT_RX_RINGBUFSIZE 0x124204C
+#define mmACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define mmACP_BT_RX_FIFOADDR 0x1242054
+#define mmACP_BT_RX_FIFOSIZE 0x1242058
+#define mmACP_BT_RX_DMA_SIZE 0x124205C
+#define mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define mmACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define mmACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define mmACP_BT_TX_RINGBUFADDR 0x124206C
+#define mmACP_BT_TX_RINGBUFSIZE 0x1242070
+#define mmACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define mmACP_BT_TX_FIFOADDR 0x1242078
+#define mmACP_BT_TX_FIFOSIZE 0x124207C
+#define mmACP_BT_TX_DMA_SIZE 0x1242080
+#define mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define mmACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define mmACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define mmACP_HS_RX_RINGBUFADDR 0x1242090
+#define mmACP_HS_RX_RINGBUFSIZE 0x1242094
+#define mmACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define mmACP_HS_RX_FIFOADDR 0x124209C
+#define mmACP_HS_RX_FIFOSIZE 0x12420A0
+#define mmACP_HS_RX_DMA_SIZE 0x12420A4
+#define mmACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define mmACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define mmACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define mmACP_HS_TX_RINGBUFADDR 0x12420B4
+#define mmACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define mmACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define mmACP_HS_TX_FIFOADDR 0x12420C0
+#define mmACP_HS_TX_FIFOSIZE 0x12420C4
+#define mmACP_HS_TX_DMA_SIZE 0x12420C8
+#define mmACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define mmACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define mmACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+
+// Registers from ACP_I2S_TDM block
+
+#define mmACP_I2STDM_IER 0x1242400
+#define mmACP_I2STDM_IRER 0x1242404
+#define mmACP_I2STDM_RXFRMT 0x1242408
+#define mmACP_I2STDM_ITER 0x124240C
+#define mmACP_I2STDM_TXFRMT 0x1242410
+
+
+// Registers from ACP_BT_TDM block
+
+#define mmACP_BTTDM_IER 0x1242800
+#define mmACP_BTTDM_IRER 0x1242804
+#define mmACP_BTTDM_RXFRMT 0x1242808
+#define mmACP_BTTDM_ITER 0x124280C
+#define mmACP_BTTDM_TXFRMT 0x1242810
+
+
+// Registers from AZALIA_IP block
+
+#define mmAudio_Az_Global_Capabilities 0x1200000
+#define mmAudio_Az_Minor_Version 0x1200002
+#define mmAudio_Az_Major_Version 0x1200003
+#define mmAudio_Az_Output_Payload_Capability 0x1200004
+#define mmAudio_Az_Input_Payload_Capability 0x1200006
+#define mmAudio_Az_Global_Control 0x1200008
+#define mmAudio_Az_Wake_Enable 0x120000C
+#define mmAudio_Az_State_Change_Status 0x120000E
+#define mmAudio_Az_Global_Status 0x1200010
+#define mmAudio_Az_Linked_List_Capability_Header 0x1200014
+#define mmAudio_Az_Output_Stream_Payload_Capability 0x1200018
+#define mmAudio_Az_Input_Stream_Payload_Capability 0x120001A
+#define mmAudio_Az_Interrupt_Control 0x1200020
+#define mmAudio_Az_Interrupt_Status 0x1200024
+#define mmAudio_Az_Wall_Clock_Counter 0x1200030
+#define mmAudio_Az_Stream_Synchronization 0x1200038
+#define mmAudio_Az_CORB_Lower_Base_Address 0x1200040
+#define mmAudio_Az_CORB_Upper_Base_Address 0x1200044
+#define mmAudio_Az_CORB_Write_Pointer 0x1200048
+#define mmAudio_Az_CORB_Read_Pointer 0x120004A
+#define mmAudio_Az_CORB_Control 0x120004C
+#define mmAudio_Az_CORB_Status 0x120004D
+#define mmAudio_Az_CORB_Size 0x120004E
+#define mmAudio_Az_RIRB_Lower_Base_Address 0x1200050
+#define mmAudio_Az_RIRB_Upper_Base_Address 0x1200054
+#define mmAudio_Az_RIRB_Write_Pointer 0x1200058
+#define mmAudio_Az_RIRB_Response_Interrupt_Count 0x120005A
+#define mmAudio_Az_RIRB_Control 0x120005C
+#define mmAudio_Az_RIRB_Status 0x120005D
+#define mmAudio_Az_RIRB_Size 0x120005E
+#define mmAudio_Az_Immediate_Command_Output_Interface 0x1200060
+#define mmAudio_Az_Immediate_Response_Input_Interface 0x1200064
+#define mmAudio_Az_Immediate_Command_Status 0x1200068
+#define mmAudio_Az_DPLBASE 0x1200070
+#define mmAudio_Az_DPUBASE 0x1200074
+#define mmAudio_Az_Input_SD0CTL_and_STS 0x1200080
+#define mmAudio_Az_Input_SD0LPIB 0x1200084
+#define mmAudio_Az_Input_SD0CBL 0x1200088
+#define mmAudio_Az_Input_SD0LVI 0x120008C
+#define mmAudio_Az_Input_SD0FIFOS 0x1200090
+#define mmAudio_Az_Input_SD0FMT 0x1200092
+#define mmAudio_Az_Input_SD0BDPL 0x1200098
+#define mmAudio_Az_Input_SD0BDPU 0x120009C
+#define mmAudio_Az_Input_SD1CTL_and_STS 0x12000A0
+#define mmAudio_Az_Input_SD1LPIB 0x12000A4
+#define mmAudio_Az_Input_SD1CBL 0x12000A8
+#define mmAudio_Az_Input_SD1LVI 0x12000AC
+#define mmAudio_Az_Input_SD1FIFOS 0x12000B0
+#define mmAudio_Az_Input_SD1FMT 0x12000B2
+#define mmAudio_Az_Input_SD1BDPL 0x12000B8
+#define mmAudio_Az_Input_SD1BDPU 0x12000BC
+#define mmAudio_Az_Input_SD2CTL_and_STS 0x12000C0
+#define mmAudio_Az_Input_SD2LPIB 0x12000C4
+#define mmAudio_Az_Input_SD2CBL 0x12000C8
+#define mmAudio_Az_Input_SD2LVI 0x12000CC
+#define mmAudio_Az_Input_SD2FIFOS 0x12000D0
+#define mmAudio_Az_Input_SD2FMT 0x12000D2
+#define mmAudio_Az_Input_SD2BDPL 0x12000D8
+#define mmAudio_Az_Input_SD2BDPU 0x12000DC
+#define mmAudio_Az_Input_SD3CTL_and_STS 0x12000E0
+#define mmAudio_Az_Input_SD3LPIB 0x12000E4
+#define mmAudio_Az_Input_SD3CBL 0x12000E8
+#define mmAudio_Az_Input_SD3LVI 0x12000EC
+#define mmAudio_Az_Input_SD3FIFOS 0x12000F0
+#define mmAudio_Az_Input_SD3FMT 0x12000F2
+#define mmAudio_Az_Input_SD3BDPL 0x12000F8
+#define mmAudio_Az_Input_SD3BDPU 0x12000FC
+#define mmAudio_Az_Output_SD0CTL_and_STS 0x1200100
+#define mmAudio_Az_Output_SD0LPIB 0x1200104
+#define mmAudio_Az_Output_SD0CBL 0x1200108
+#define mmAudio_Az_Output_SD0LVI 0x120010C
+#define mmAudio_Az_Output_SD0FIFOS 0x1200110
+#define mmAudio_Az_Output_SD0FMT 0x1200112
+#define mmAudio_Az_Output_SD0BDPL 0x1200118
+#define mmAudio_Az_Output_SD0BDPU 0x120011C
+#define mmAudio_Az_Output_SD1CTL_and_STS 0x1200120
+#define mmAudio_Az_Output_SD1LPIB 0x1200124
+#define mmAudio_Az_Output_SD1CBL 0x1200128
+#define mmAudio_Az_Output_SD1LVI 0x120012C
+#define mmAudio_Az_Output_SD1FIFOS 0x1200130
+#define mmAudio_Az_Output_SD1FMT 0x1200132
+#define mmAudio_Az_Output_SD1BDPL 0x1200138
+#define mmAudio_Az_Output_SD1BDPU 0x120013C
+#define mmAudio_Az_Output_SD2CTL_and_STS 0x1200140
+#define mmAudio_Az_Output_SD2LPIB 0x1200144
+#define mmAudio_Az_Output_SD2CBL 0x1200148
+#define mmAudio_Az_Output_SD2LVI 0x120014C
+#define mmAudio_Az_Output_SD2FIFOS 0x1200150
+#define mmAudio_Az_Output_SD2FMT 0x1200152
+#define mmAudio_Az_Output_SD2BDPL 0x1200158
+#define mmAudio_Az_Output_SD2BDPU 0x120015C
+#define mmAudio_Az_Output_SD3CTL_and_STS 0x1200160
+#define mmAudio_Az_Output_SD3LPIB 0x1200164
+#define mmAudio_Az_Output_SD3CBL 0x1200168
+#define mmAudio_Az_Output_SD3LVI 0x120016C
+#define mmAudio_Az_Output_SD3FIFOS 0x1200170
+#define mmAudio_Az_Output_SD3FMT 0x1200172
+#define mmAudio_Az_Output_SD3BDPL 0x1200178
+#define mmAudio_Az_Output_SD3BDPU 0x120017C
+#define mmAudioAZ_Misc_Control_Register_1 0x1200180
+#define mmAudioAZ_Misc_Control_Register_2 0x1200182
+#define mmAudioAZ_Misc_Control_Register_3 0x1200183
+#define mmAudio_AZ_Multiple_Links_Capability_Header 0x1200200
+#define mmAudio_AZ_Multiple_Links_Capability_Declaration 0x1200204
+#define mmAudio_AZ_Link0_Capabilities 0x1200240
+#define mmAudio_AZ_Link0_Control 0x1200244
+#define mmAudio_AZ_Link0_Output_Stream_ID 0x1200248
+#define mmAudio_AZ_Link0_SDI_Identifier 0x120024C
+#define mmAudio_AZ_Link0_Per_Stream_Overhead 0x1200250
+#define mmAudio_AZ_Link0_Wall_Frame_Counter 0x1200258
+#define mmAudio_AZ_Link0_Output_Payload_Capability_L 0x1200260
+#define mmAudio_AZ_Link0_Output_Payload_Capability_U 0x1200264
+#define mmAudio_AZ_Link0_Input_Payload_Capability_L 0x1200270
+#define mmAudio_AZ_Link0_Input_Payload_Capability_U 0x1200274
+#define mmAudio_Az_Input_SD0LICBA 0x1202084
+#define mmAudio_Az_Input_SD1LICBA 0x12020A4
+#define mmAudio_Az_Input_SD2LICBA 0x12020C4
+#define mmAudio_Az_Input_SD3LICBA 0x12020E4
+#define mmAudio_Az_Output_SD0LICBA 0x1202104
+#define mmAudio_Az_Output_SD1LICBA 0x1202124
+#define mmAudio_Az_Output_SD2LICBA 0x1202144
+#define mmAudio_Az_Output_SD3LICBA 0x1202164
+#define mmAUDIO_AZ_POWER_MANAGEMENT_CONTROL 0x1204000
+#define mmAUDIO_AZ_IOC_SOFTRST_CONTROL 0x1204004
+#define mmAUDIO_AZ_IOC_CLKGATE_CONTROL 0x1204008
+
+
+// Registers from ACP_AZALIA block
+
+#define mmACP_AZ_PAGE0_LBASE_ADDR 0x1243800
+#define mmACP_AZ_PAGE0_UBASE_ADDR 0x1243804
+#define mmACP_AZ_PAGE0_PGEN_SIZE 0x1243808
+#define mmACP_AZ_PAGE0_OFFSET 0x124380C
+#define mmACP_AZ_PAGE1_LBASE_ADDR 0x1243810
+#define mmACP_AZ_PAGE1_UBASE_ADDR 0x1243814
+#define mmACP_AZ_PAGE1_PGEN_SIZE 0x1243818
+#define mmACP_AZ_PAGE1_OFFSET 0x124381C
+#define mmACP_AZ_PAGE2_LBASE_ADDR 0x1243820
+#define mmACP_AZ_PAGE2_UBASE_ADDR 0x1243824
+#define mmACP_AZ_PAGE2_PGEN_SIZE 0x1243828
+#define mmACP_AZ_PAGE2_OFFSET 0x124382C
+#define mmACP_AZ_PAGE3_LBASE_ADDR 0x1243830
+#define mmACP_AZ_PAGE3_UBASE_ADDR 0x1243834
+#define mmACP_AZ_PAGE3_PGEN_SIZE 0x1243838
+#define mmACP_AZ_PAGE3_OFFSET 0x124383C
+#define mmACP_AZ_PAGE4_LBASE_ADDR 0x1243840
+#define mmACP_AZ_PAGE4_UBASE_ADDR 0x1243844
+#define mmACP_AZ_PAGE4_PGEN_SIZE 0x1243848
+#define mmACP_AZ_PAGE4_OFFSET 0x124384C
+#define mmACP_AZ_PAGE5_LBASE_ADDR 0x1243850
+#define mmACP_AZ_PAGE5_UBASE_ADDR 0x1243854
+#define mmACP_AZ_PAGE5_PGEN_SIZE 0x1243858
+#define mmACP_AZ_PAGE5_OFFSET 0x124385C
+#define mmACP_AZ_PAGE6_LBASE_ADDR 0x1243860
+#define mmACP_AZ_PAGE6_UBASE_ADDR 0x1243864
+#define mmACP_AZ_PAGE6_PGEN_SIZE 0x1243868
+#define mmACP_AZ_PAGE6_OFFSET 0x124386C
+#define mmACP_AZ_PAGE7_LBASE_ADDR 0x1243870
+#define mmACP_AZ_PAGE7_UBASE_ADDR 0x1243874
+#define mmACP_AZ_PAGE7_PGEN_SIZE 0x1243878
+#define mmACP_AZ_PAGE7_OFFSET 0x124387C
+
+
+#endif
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
new file mode 100644
index 000000000000..a013a607b3d4
--- /dev/null
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ACP PCI Driver
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+
+#include "acp3x.h"
+
+struct acp3x_dev_data {
+ void __iomem *acp3x_base;
+ bool acp3x_audio_mode;
+ struct resource *res;
+ struct platform_device *pdev[ACP3x_DEVS];
+ u32 pme_en;
+};
+
+static int acp3x_power_on(struct acp3x_dev_data *adata)
+{
+ void __iomem *acp3x_base = adata->acp3x_base;
+ u32 val;
+ int timeout;
+
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if (!((val & ACP_PGFSM_STATUS_MASK) ==
+ ACP_POWER_ON_IN_PROGRESS))
+ rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp3x_base + mmACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+ if (!val) {
+ /* ACP power On clears PME_EN.
+ * Restore the value to its prior state
+ */
+ rv_writel(adata->pme_en, acp3x_base + mmACP_PME_EN);
+ return 0;
+ }
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp3x_reset(void __iomem *acp3x_base)
+{
+ u32 val;
+ int timeout;
+
+ rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
+ break;
+ cpu_relax();
+ }
+ rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp3x_enable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp3x_disable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp3x_init(struct acp3x_dev_data *adata)
+{
+ void __iomem *acp3x_base = adata->acp3x_base;
+ int ret;
+
+ /* power on */
+ ret = acp3x_power_on(adata);
+ if (ret) {
+ pr_err("ACP3x power on failed\n");
+ return ret;
+ }
+ /* Reset */
+ ret = acp3x_reset(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x reset failed\n");
+ return ret;
+ }
+ acp3x_enable_interrupts(acp3x_base);
+ return 0;
+}
+
+static int acp3x_deinit(void __iomem *acp3x_base)
+{
+ int ret;
+
+ acp3x_disable_interrupts(acp3x_base);
+ /* Reset */
+ ret = acp3x_reset(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x reset failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int snd_acp3x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp3x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP3x_DEVS];
+ unsigned int irqflags;
+ int ret, i;
+ u32 addr, val;
+
+ /* Raven device detection */
+ if (pci->revision != 0x00)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ irqflags = IRQF_SHARED;
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp3x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp3x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ /* Save ACP_PME_EN state */
+ adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
+ ret = acp3x_init(adata);
+ if (ret)
+ goto release_regions;
+
+ val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
+ switch (val) {
+ case I2S_MODE:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * 4,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp3x_i2s_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
+
+ adata->res[1].name = "acp3x_i2s_sp";
+ adata->res[1].flags = IORESOURCE_MEM;
+ adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
+ adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
+
+ adata->res[2].name = "acp3x_i2s_bt";
+ adata->res[2].flags = IORESOURCE_MEM;
+ adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
+ adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
+
+ adata->res[3].name = "acp3x_i2s_irq";
+ adata->res[3].flags = IORESOURCE_IRQ;
+ adata->res[3].start = pci->irq;
+ adata->res[3].end = adata->res[3].start;
+
+ adata->acp3x_audio_mode = ACP3x_I2S_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp3x_rv_i2s_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 4;
+ pdevinfo[0].res = &adata->res[0];
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "acp3x_i2s_playcap";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[1].num_res = 1;
+ pdevinfo[1].res = &adata->res[1];
+
+ pdevinfo[2].name = "acp3x_i2s_playcap";
+ pdevinfo[2].id = 1;
+ pdevinfo[2].parent = &pci->dev;
+ pdevinfo[2].num_res = 1;
+ pdevinfo[2].res = &adata->res[1];
+
+ pdevinfo[3].name = "acp3x_i2s_playcap";
+ pdevinfo[3].id = 2;
+ pdevinfo[3].parent = &pci->dev;
+ pdevinfo[3].num_res = 1;
+ pdevinfo[3].res = &adata->res[2];
+ for (i = 0; i < ACP3x_DEVS; i++) {
+ adata->pdev[i] =
+ platform_device_register_full(&pdevinfo[i]);
+ if (IS_ERR(adata->pdev[i])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[i].name);
+ ret = PTR_ERR(adata->pdev[i]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ default:
+ dev_info(&pci->dev, "ACP audio mode : %d\n", val);
+ break;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ if (val == I2S_MODE)
+ for (i = 0; i < ACP3x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+de_init:
+ if (acp3x_deinit(adata->acp3x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp3x_suspend(struct device *dev)
+{
+ int ret;
+ struct acp3x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return 0;
+}
+
+static int snd_acp3x_resume(struct device *dev)
+{
+ int ret;
+ struct acp3x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp3x_init(adata);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops acp3x_pm = {
+ .runtime_suspend = snd_acp3x_suspend,
+ .runtime_resume = snd_acp3x_resume,
+ .resume = snd_acp3x_resume,
+};
+
+static void snd_acp3x_remove(struct pci_dev *pci)
+{
+ struct acp3x_dev_data *adata;
+ int i, ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
+ for (i = 0; i < ACP3x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+ }
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp3x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
+
+static struct pci_driver acp3x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp3x_ids,
+ .probe = snd_acp3x_probe,
+ .remove = snd_acp3x_remove,
+ .driver = {
+ .pm = &acp3x_pm,
+ }
+};
+
+module_pci_driver(acp3x_driver);
+
+MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
+MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
+MODULE_DESCRIPTION("AMD ACP3x PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/renoir/Makefile b/sound/soc/amd/renoir/Makefile
new file mode 100644
index 000000000000..76b4a9c3e24f
--- /dev/null
+++ b/sound/soc/amd/renoir/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Renoir platform Support
+snd-rn-pci-acp3x-y := rn-pci-acp3x.o
+snd-acp3x-pdm-dma-y := acp3x-pdm-dma.o
+snd-acp3x-rn-y := acp3x-rn.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-rn-pci-acp3x.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-acp3x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_RENOIR_MACH) += snd-acp3x-rn.o
diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
new file mode 100644
index 000000000000..95ac8c680037
--- /dev/null
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PDM Driver
+//
+//Copyright 2020 Advanced Micro Devices, Inc.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "rn_acp3x.h"
+
+#define DRV_NAME "acp_rn_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static irqreturn_t pdm_irq_handler(int irq, void *dev_id)
+{
+ struct pdm_dev_data *rn_pdm_data;
+ u16 cap_flag;
+ u32 val;
+
+ rn_pdm_data = dev_id;
+ if (!rn_pdm_data)
+ return IRQ_NONE;
+
+ cap_flag = 0;
+ val = rn_readl(rn_pdm_data->acp_base + ACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(PDM_DMA_STAT)) && rn_pdm_data->capture_stream) {
+ rn_writel(BIT(PDM_DMA_STAT), rn_pdm_data->acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(rn_pdm_data->capture_stream);
+ cap_flag = 1;
+ }
+
+ if (cap_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void init_pdm_ring_buffer(u32 physical_addr,
+ u32 buffer_size,
+ u32 watermark_size,
+ void __iomem *acp_base)
+{
+ rn_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ rn_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ rn_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ rn_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+
+ rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void enable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void disable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
+ rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static bool check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable &
+ ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+ return pdm_dma_status;
+}
+
+static int start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ enable_pdm_clock(acp_base);
+ rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = rn_readl(acp_base +
+ ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void config_acp_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = 0;
+
+ /* Group Enable */
+ rn_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ rn_writel(PAGE_SIZE_4K_ENABLE, rtd->acp_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ rn_writel(low, rtd->acp_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ rn_writel(high, rtd->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ enable_pdm_interrupts(adata->acp_base);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp_base = adata->acp_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp_dma(rtd, substream->stream);
+ init_pdm_ring_buffer(MEM_WINDOW_START, size, period_bytes,
+ rtd->acp_base);
+ return 0;
+}
+
+static u64 acp_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ union acp_pdm_dma_count byte_count;
+
+ byte_count.bcount.high =
+ rn_readl(rtd->acp_base +
+ ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count.bcount.low =
+ rn_readl(rtd->acp_base +
+ ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+ disable_pdm_interrupts(adata->acp_base);
+ adata->capture_stream = NULL;
+ return 0;
+}
+
+static int acp_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rn_writel(ch_mask, rtd->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ rn_writel(PDM_DECIMATION_FACTOR, rtd->acp_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp_pdm_get_byte_count(rtd,
+ substream->stream);
+ pdm_status = check_pdm_dma_status(rtd->acp_base);
+ if (!pdm_status)
+ ret = start_pdm_dma(rtd->acp_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = check_pdm_dma_status(rtd->acp_base);
+ if (pdm_status)
+ ret = stop_pdm_dma(rtd->acp_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp_pdm_dai_ops = {
+ .trigger = acp_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp_pdm_dai_driver = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp_pdm_dma_open,
+ .close = acp_pdm_dma_close,
+ .hw_params = acp_pdm_dma_hw_params,
+ .pointer = acp_pdm_dma_pointer,
+ .pcm_construct = acp_pdm_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+static int acp_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->pdm_irq = status;
+
+ adata->capture_stream = NULL;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp_pdm_component,
+ &acp_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ status = devm_request_irq(&pdev->dev, adata->pdm_irq, pdm_irq_handler,
+ irqflags, "ACP_PDM_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP PDM IRQ request failed\n");
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ config_acp_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ init_pdm_ring_buffer(MEM_WINDOW_START, buffer_len, period_bytes,
+ adata->acp_base);
+ }
+ enable_pdm_interrupts(adata->acp_base);
+ return 0;
+}
+
+static int acp_pdm_runtime_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ disable_pdm_interrupts(adata->acp_base);
+
+ return 0;
+}
+
+static int acp_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ enable_pdm_interrupts(adata->acp_base);
+ return 0;
+}
+
+static const struct dev_pm_ops acp_pdm_pm_ops = {
+ .runtime_suspend = acp_pdm_runtime_suspend,
+ .runtime_resume = acp_pdm_runtime_resume,
+ .resume = acp_pdm_resume,
+};
+
+static struct platform_driver acp_pdm_dma_driver = {
+ .probe = acp_pdm_audio_probe,
+ .remove = acp_pdm_audio_remove,
+ .driver = {
+ .name = "acp_rn_pdm_dma",
+ .pm = &acp_pdm_pm_ops,
+ },
+};
+
+module_platform_driver(acp_pdm_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP3x Renior PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/renoir/acp3x-rn.c b/sound/soc/amd/renoir/acp3x-rn.c
new file mode 100644
index 000000000000..3249f74a0197
--- /dev/null
+++ b/sound/soc/amd/renoir/acp3x-rn.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD Renoir platform using DMIC
+//
+//Copyright 2020 Advanced Micro Devices, Inc.
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+
+#include "rn_acp3x.h"
+
+#define DRV_NAME "acp_pdm_mach"
+
+SND_SOC_DAILINK_DEF(acp_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_rn_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_rn_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp_dai_pdm[] = {
+ {
+ .name = "acp3x-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp_pdm, dmic_codec, platform),
+ },
+};
+
+static struct snd_soc_card acp_card = {
+ .name = "acp",
+ .owner = THIS_MODULE,
+ .dai_link = acp_dai_pdm,
+ .num_links = 1,
+};
+
+static int acp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct acp_pdm *machine = NULL;
+ struct snd_soc_card *card;
+
+ card = &acp_card;
+ acp_card.dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static struct platform_driver acp_mach_driver = {
+ .driver = {
+ .name = "acp_pdm_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp_probe,
+};
+
+module_platform_driver(acp_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Renoir support for DMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/renoir/rn-pci-acp3x.c b/sound/soc/amd/renoir/rn-pci-acp3x.c
new file mode 100644
index 000000000000..b3812b70f5f9
--- /dev/null
+++ b/sound/soc/amd/renoir/rn-pci-acp3x.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD Renoir ACP PCI Driver
+//
+//Copyright 2020 Advanced Micro Devices, Inc.
+
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "rn_acp3x.h"
+
+static int acp_power_gating;
+module_param(acp_power_gating, int, 0644);
+MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
+
+/*
+ * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
+ * = 0 - Skip the DMIC device creation and return probe failure
+ * = 1 - Force DMIC support
+ */
+static int dmic_acpi_check = ACP_DMIC_AUTO;
+module_param(dmic_acpi_check, bint, 0644);
+MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
+
+struct acp_dev_data {
+ void __iomem *acp_base;
+ struct resource *res;
+ struct platform_device *pdev[ACP_DEVS];
+};
+
+static int rn_acp_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = rn_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rn_acp_power_off(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+ acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rn_acp_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rn_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ rn_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rn_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void rn_acp_enable_interrupts(void __iomem *acp_base)
+{
+ u32 ext_intr_ctrl;
+
+ rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
+ ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void rn_acp_disable_interrupts(void __iomem *acp_base)
+{
+ rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int rn_acp_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = rn_acp_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ rn_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = rn_acp_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ rn_acp_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int rn_acp_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ rn_acp_disable_interrupts(acp_base);
+ /* Reset */
+ ret = rn_acp_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ rn_writel(0x00, acp_base + ACP_CONTROL);
+ /* power off */
+ if (acp_power_gating) {
+ ret = rn_acp_power_off(acp_base);
+ if (ret) {
+ pr_err("ACP power off failed\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct dmi_system_id rn_acp_quirk_table[] = {
+ {
+ /* Lenovo IdeaPad S340-14API */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
+ }
+ },
+ {
+ /* Lenovo IdeaPad Flex 5 14ARE05 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
+ }
+ },
+ {
+ /* Lenovo IdeaPad 5 15ARE05 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
+ }
+ },
+ {
+ /* Lenovo ThinkPad E14 Gen 2 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
+ }
+ },
+ {
+ /* Lenovo ThinkPad X395 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
+ }
+ },
+ {}
+};
+
+static int snd_rn_acp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP_DEVS];
+#if defined(CONFIG_ACPI)
+ acpi_handle handle;
+ acpi_integer dmic_status;
+#endif
+ const struct dmi_system_id *dmi_id;
+ unsigned int irqflags, flag;
+ int ret, index;
+ u32 addr;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Renoir device check */
+ if (pci->revision != 0x01)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ /* check for msi interrupt support */
+ ret = pci_enable_msi(pci);
+ if (ret)
+ /* msi is not enabled */
+ irqflags = IRQF_SHARED;
+ else
+ /* msi is enabled */
+ irqflags = 0;
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp_base) {
+ ret = -ENOMEM;
+ goto disable_msi;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = rn_acp_init(adata->acp_base);
+ if (ret)
+ goto disable_msi;
+
+ if (!dmic_acpi_check) {
+ ret = -ENODEV;
+ goto de_init;
+ } else if (dmic_acpi_check == ACP_DMIC_AUTO) {
+#if defined(CONFIG_ACPI)
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (ACPI_FAILURE(ret)) {
+ ret = -ENODEV;
+ goto de_init;
+ }
+ if (!dmic_status) {
+ ret = -ENODEV;
+ goto de_init;
+ }
+#endif
+ dmi_id = dmi_first_match(rn_acp_quirk_table);
+ if (dmi_id && !dmi_id->driver_data) {
+ dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
+ ret = -ENODEV;
+ goto de_init;
+ }
+ }
+
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * 2,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp_pdm_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
+ adata->res[1].name = "acp_pdm_irq";
+ adata->res[1].flags = IORESOURCE_IRQ;
+ adata->res[1].start = pci->irq;
+ adata->res[1].end = pci->irq;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp_rn_pdm_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 2;
+ pdevinfo[0].res = adata->res;
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "dmic-codec";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[2].name = "acp_pdm_mach";
+ pdevinfo[2].id = 0;
+ pdevinfo[2].parent = &pci->dev;
+ for (index = 0; index < ACP_DEVS; index++) {
+ adata->pdev[index] =
+ platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ for (index = 0; index < ACP_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (rn_acp_deinit(adata->acp_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+disable_msi:
+ pci_disable_msi(pci);
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_rn_acp_suspend(struct device *dev)
+{
+ int ret;
+ struct acp_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = rn_acp_deinit(adata->acp_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return ret;
+}
+
+static int snd_rn_acp_resume(struct device *dev)
+{
+ int ret;
+ struct acp_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = rn_acp_init(adata->acp_base);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops rn_acp_pm = {
+ .runtime_suspend = snd_rn_acp_suspend,
+ .runtime_resume = snd_rn_acp_resume,
+ .suspend = snd_rn_acp_suspend,
+ .resume = snd_rn_acp_resume,
+ .restore = snd_rn_acp_resume,
+ .poweroff = snd_rn_acp_suspend,
+};
+
+static void snd_rn_acp_remove(struct pci_dev *pci)
+{
+ struct acp_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ for (index = 0; index < ACP_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+ ret = rn_acp_deinit(adata->acp_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_disable_msi(pci);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_rn_acp_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
+
+static struct pci_driver rn_acp_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_rn_acp_ids,
+ .probe = snd_rn_acp_probe,
+ .remove = snd_rn_acp_remove,
+ .driver = {
+ .pm = &rn_acp_pm,
+ }
+};
+
+module_pci_driver(rn_acp_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h
new file mode 100644
index 000000000000..7d0f4e6a2834
--- /dev/null
+++ b/sound/soc/amd/renoir/rn_acp3x.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ */
+
+#include "rn_chip_offset_byte.h"
+
+#define ACP_DEVS 3
+#define ACP_PHY_BASE_ADDRESS 0x1240000
+#define ACP_REG_START 0x1240000
+#define ACP_REG_END 0x1250200
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP_POWER_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWER_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 0x2
+#define ACP_PDM_CLK_FREQ_MASK 0x07
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 0x01
+#define ACP_PDM_DISABLE 0x00
+#define ACP_PDM_DMA_EN_STATUS 0x02
+#define TWO_CH 0x02
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define ACP_DMIC_AUTO -1
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp_base;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp_base;
+};
+
+union acp_pdm_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+static inline u32 rn_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP_PHY_BASE_ADDRESS);
+}
+
+static inline void rn_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP_PHY_BASE_ADDRESS);
+}
+
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/renoir/rn_chip_offset_byte.h b/sound/soc/amd/renoir/rn_chip_offset_byte.h
new file mode 100644
index 000000000000..d20d967b5ff9
--- /dev/null
+++ b/sound/soc/amd/renoir/rn_chip_offset_byte.h
@@ -0,0 +1,349 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 3.1 Register Documentation
+ *
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _rn_OFFSET_HEADER
+#define _rn_OFFSET_HEADER
+// Registers from ACP_DMA block
+
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+// Registers from ACP_AXI2AXIATU block
+
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+
+// Registers from ACP_CLKRST block
+
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+// Registers from ACP_MISC block
+
+#define ACP_EXTERNAL_INTR_ENB 0x1241800
+#define ACP_EXTERNAL_INTR_CNTL 0x1241804
+#define ACP_EXTERNAL_INTR_STAT 0x1241808
+#define ACP_PGMEM_CTRL 0x12418C0
+#define ACP_ERROR_STATUS 0x12418C4
+#define ACP_SW_I2S_ERROR_REASON 0x12418C8
+#define ACP_MEM_PG_STS 0x12418CC
+
+// Registers from ACP_PGFSM block
+
+#define ACP_I2S_PIN_CONFIG 0x1241400
+#define ACP_PAD_PULLUP_PULLDOWN_CTRL 0x1241404
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x1241408
+#define ACP_SW_PAD_KEEPER_EN 0x124140C
+#define ACP_PGFSM_CONTROL 0x124141C
+#define ACP_PGFSM_STATUS 0x1241420
+#define ACP_CLKMUX_SEL 0x1241424
+#define ACP_DEVICE_STATE 0x1241428
+#define AZ_DEVICE_STATE 0x124142C
+#define ACP_INTR_URGENCY_TIMER 0x1241430
+#define AZ_INTR_URGENCY_TIMER 0x1241434
+
+// Registers from ACP_SCRATCH block
+
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+
+// Registers from ACP_AUDIO_BUFFERS block
+
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+// Registers from ACP_I2S_TDM block
+
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+
+// Registers from ACP_BT_TDM block
+
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+
+// Registers from ACP_WOV block
+
+#define ACP_WOV_PDM_ENABLE 0x1242C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08
+#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x1242C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x1242C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x1242C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x1242C30
+#define ACP_WOV_BUFFER_STATUS 0x1242C58
+#define ACP_WOV_MISC_CTRL 0x1242C5C
+#define ACP_WOV_CLK_CTRL 0x1242C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x1242C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x1242C68
+#endif
diff --git a/sound/soc/amd/rpl/Makefile b/sound/soc/amd/rpl/Makefile
new file mode 100644
index 000000000000..a3825c5be4e7
--- /dev/null
+++ b/sound/soc/amd/rpl/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+# RPL platform Support
+snd-rpl-pci-acp6x-y := rpl-pci-acp6x.o
+
+obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += snd-rpl-pci-acp6x.o
diff --git a/sound/soc/amd/rpl/rpl-pci-acp6x.c b/sound/soc/amd/rpl/rpl-pci-acp6x.c
new file mode 100644
index 000000000000..e3afe9172bdf
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl-pci-acp6x.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD RPL ACP PCI Driver
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rpl_acp6x.h"
+
+struct rpl_dev_data {
+ void __iomem *acp6x_base;
+};
+
+static int rpl_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rpl_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rpl_acp_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ rpl_acp_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static int rpl_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = rpl_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = rpl_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ return 0;
+}
+
+static int rpl_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ /* Reset */
+ ret = rpl_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ rpl_acp_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static int snd_rpl_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct rpl_dev_data *adata;
+ u32 addr;
+ int ret;
+
+ /* RPL device check */
+ switch (pci->revision) {
+ case 0x62:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp6x pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP6x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = rpl_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_rpl_suspend(struct device *dev)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = rpl_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_rpl_resume(struct device *dev)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = rpl_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops rpl_pm = {
+ RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume)
+};
+
+static void snd_rpl_remove(struct pci_dev *pci)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = pci_get_drvdata(pci);
+ ret = rpl_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_rpl_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_rpl_ids);
+
+static struct pci_driver rpl_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_rpl_ids,
+ .probe = snd_rpl_probe,
+ .remove = snd_rpl_remove,
+ .driver = {
+ .pm = pm_ptr(&rpl_pm),
+ }
+};
+
+module_pci_driver(rpl_acp6x_driver);
+
+MODULE_DESCRIPTION("AMD ACP RPL PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/rpl/rpl_acp6x.h b/sound/soc/amd/rpl/rpl_acp6x.h
new file mode 100644
index 000000000000..f5816a33632e
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl_acp6x.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "rpl_acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+static inline u32 rpl_acp_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void rpl_acp_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
diff --git a/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..456498f5396d
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.2 Register Documentation
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _rpl_acp6x_OFFSET_HEADER
+#define _rpl_acp6x_OFFSET_HEADER
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_PGFSM_CONTROL 0x124101C
+#define ACP_PGFSM_STATUS 0x1241020
+#define ACP_CLKMUX_SEL 0x1241024
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+
+#endif
diff --git a/sound/soc/amd/vangogh/Makefile b/sound/soc/amd/vangogh/Makefile
new file mode 100644
index 000000000000..7eae82faa392
--- /dev/null
+++ b/sound/soc/amd/vangogh/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Vangogh platform Support
+snd-pci-acp5x-y := pci-acp5x.o
+snd-acp5x-i2s-y := acp5x-i2s.o
+snd-acp5x-pcm-dma-y := acp5x-pcm-dma.o
+snd-soc-acp5x-mach-y := acp5x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-pci-acp5x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-pcm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_VANGOGH_MACH) += snd-soc-acp5x-mach.o
diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c
new file mode 100644
index 000000000000..bf719f628617
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_i2s_playcap"
+
+static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct i2s_dev_data *adata;
+ int mode;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_BP_FP:
+ adata->master_mode = I2S_MASTER_MODE_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ adata->master_mode = I2S_MASTER_MODE_DISABLE;
+ break;
+ }
+ return 0;
+}
+
+static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct i2s_dev_data *adata;
+ u32 frm_len;
+ u16 slot_len;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* These values are as per Hardware Spec */
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
+ adata->tdm_fmt = frm_len;
+ return 0;
+}
+
+static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp5x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+
+ u32 val;
+ u32 reg_val, frmt_reg;
+ u32 lrclk_div_val, bclk_div_val;
+
+ lrclk_div_val = 0;
+ bclk_div_val = 0;
+ prtd = snd_soc_substream_to_rtd(substream);
+ rtd = substream->runtime->private_data;
+ card = prtd->card;
+ adata = snd_soc_dai_get_drvdata(dai);
+ pinfo = snd_soc_card_get_drvdata(card);
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ else
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ }
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ rtd->xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rtd->xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ rtd->xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rtd->xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ frmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_ITER;
+ frmt_reg = ACP_I2STDM_TXFRMT;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ frmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_IRER;
+ frmt_reg = ACP_I2STDM_RXFRMT;
+ }
+ }
+ if (adata->tdm_mode) {
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
+ acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
+ }
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (rtd->xfer_resolution << 3);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+
+ if (adata->master_mode) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 768;
+ break;
+ case 16000:
+ bclk_div_val = 384;
+ break;
+ case 24000:
+ bclk_div_val = 256;
+ break;
+ case 32000:
+ bclk_div_val = 192;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 128;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 64;
+ break;
+ case 192000:
+ bclk_div_val = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 384;
+ break;
+ case 16000:
+ bclk_div_val = 192;
+ break;
+ case 24000:
+ bclk_div_val = 128;
+ break;
+ case 32000:
+ bclk_div_val = 96;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 64;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 32;
+ break;
+ case 192000:
+ bclk_div_val = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ rtd->lrclk_div = lrclk_div_val;
+ rtd->bclk_div = bclk_div_val;
+ }
+ return 0;
+}
+
+static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct i2s_dev_data *adata;
+ u32 val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+ int ret;
+
+ adata = snd_soc_dai_get_drvdata(dai);
+ rtd = substream->runtime->private_data;
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rtd->bytescount = acp_get_byte_count(rtd,
+ substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ water_val =
+ ACP_HS_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_ITER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_TX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ ACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ water_val =
+ ACP_HS_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_IRER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_RX_RINGBUFSIZE;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ ACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE;
+ }
+ }
+ acp_writel(period_bytes, rtd->acp5x_base + water_val);
+ acp_writel(buf_size, rtd->acp5x_base + buf_reg);
+ if (adata->master_mode)
+ acp5x_set_i2s_clk(adata, rtd);
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val = val | BIT(0);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+ acp_writel(1, rtd->acp5x_base + ier_val);
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_ITER;
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_IRER;
+ }
+ }
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val = val & ~BIT(0);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+
+ if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
+ acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
+ if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
+ acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
+ .hw_params = acp5x_i2s_hwparams,
+ .trigger = acp5x_i2s_trigger,
+ .set_fmt = acp5x_i2s_set_fmt,
+ .set_tdm_slot = acp5x_i2s_set_tdm_slot,
+};
+
+static const struct snd_soc_component_driver acp5x_dai_component = {
+ .name = "acp5x-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver acp5x_i2s_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .ops = &acp5x_i2s_dai_ops,
+};
+
+static int acp5x_dai_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ int ret;
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
+ GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENOMEM;
+ }
+ adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp5x_base)
+ return -ENOMEM;
+
+ adata->master_mode = I2S_MASTER_MODE_ENABLE;
+ dev_set_drvdata(&pdev->dev, adata);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &acp5x_dai_component,
+ &acp5x_i2s_dai, 1);
+ if (ret)
+ dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
+ return ret;
+}
+
+static struct platform_driver acp5x_dai_driver = {
+ .probe = acp5x_dai_probe,
+ .driver = {
+ .name = "acp5x_i2s_playcap",
+ },
+};
+
+module_platform_driver(acp5x_dai_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
new file mode 100644
index 000000000000..5454de24fbcc
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Vangogh platform using either
+ * NAU8821 & CS35L41 or NAU8821 & MAX98388 codecs.
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input-event-codes.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../../codecs/nau8821.h"
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_mach"
+#define DUAL_CHANNEL 2
+#define ACP5X_NAU8821_BCLK 3072000
+#define ACP5X_NAU8821_FREQ_OUT 12288000
+#define ACP5X_NAU8821_COMP_NAME "i2c-NVTN2020:00"
+#define ACP5X_NAU8821_DAI_NAME "nau8821-hifi"
+#define ACP5X_CS35L41_COMP_LNAME "spi-VLV1776:00"
+#define ACP5X_CS35L41_COMP_RNAME "spi-VLV1776:01"
+#define ACP5X_CS35L41_DAI_NAME "cs35l41-pcm"
+#define ACP5X_MAX98388_COMP_LNAME "i2c-ADS8388:00"
+#define ACP5X_MAX98388_COMP_RNAME "i2c-ADS8388:01"
+#define ACP5X_MAX98388_DAI_NAME "max98388-aif1"
+
+static struct snd_soc_jack vg_headset;
+
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
+SND_SOC_DAILINK_DEF(acp5x_i2s, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(acp5x_bt, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));
+SND_SOC_DAILINK_DEF(nau8821, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_NAU8821_COMP_NAME,
+ ACP5X_NAU8821_DAI_NAME)));
+
+static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new acp5x_8821_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+ if (!dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "set sysclk err = %d\n", ret);
+ return -EIO;
+ }
+ } else {
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(dai->dev, "can't set BLK clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(dai, 0, 0, ACP5X_NAU8821_BCLK, ACP5X_NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(dai->dev, "can't set FLL: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &vg_headset, acp5x_nau8821_jack_pins,
+ ARRAY_SIZE(acp5x_nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
+ nau8821_enable_jack_detect(component, &vg_headset);
+
+ return ret;
+}
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const unsigned int channels[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static const unsigned int acp5x_nau8821_format[] = {32};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = acp5x_nau8821_format,
+ .count = ARRAY_SIZE(acp5x_nau8821_format),
+};
+
+static int acp5x_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+
+ return 0;
+}
+
+static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+ int ret, bclk;
+
+ if (!dai)
+ return -EINVAL;
+
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FS clock %d\n", ret);
+
+ bclk = snd_soc_params_to_bclk(params);
+ if (bclk < 0) {
+ dev_err(dai->dev, "Fail to get BCLK rate: %d\n", bclk);
+ return bclk;
+ }
+
+ ret = snd_soc_dai_set_pll(dai, 0, 0, bclk, params_rate(params) * 256);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp5x_8821_ops = {
+ .startup = acp5x_8821_startup,
+ .hw_params = acp5x_nau8821_hw_params,
+};
+
+static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int bclk, rate = params_rate(params);
+ struct snd_soc_component *comp;
+ int ret, i;
+
+ switch (rate) {
+ case 48000:
+ bclk = 1536000;
+ break;
+ default:
+ bclk = 0;
+ break;
+ }
+
+ for_each_rtd_components(rtd, i, comp) {
+ if (!(strcmp(comp->name, ACP5X_CS35L41_COMP_LNAME)) ||
+ !(strcmp(comp->name, ACP5X_CS35L41_COMP_RNAME))) {
+ if (!bclk) {
+ dev_err(comp->dev, "Invalid sample rate: 0x%x\n", rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_set_sysclk(comp, 0, 0, bclk, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(comp->dev, "failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp5x_cs35l41_play_ops = {
+ .startup = acp5x_cs35l41_startup,
+ .hw_params = acp5x_cs35l41_hw_params,
+};
+
+static struct snd_soc_codec_conf acp5x_cs35l41_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(cs35l41, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_CS35L41_COMP_LNAME,
+ ACP5X_CS35L41_DAI_NAME),
+ COMP_CODEC(ACP5X_CS35L41_COMP_RNAME,
+ ACP5X_CS35L41_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = {
+ {
+ .name = "acp5x-8821-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-CS35L41-Stereo",
+ .stream_name = "CS35L41 Stereo Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .ops = &acp5x_cs35l41_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_35l41_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static struct snd_soc_card acp5x_8821_35l41_card = {
+ .name = "acp5x",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_35l41_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_35l41_dai),
+ .dapm_widgets = acp5x_8821_35l41_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_35l41_widgets),
+ .dapm_routes = acp5x_8821_35l41_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_35l41_audio_route),
+ .codec_conf = acp5x_cs35l41_conf,
+ .num_configs = ARRAY_SIZE(acp5x_cs35l41_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+static int acp5x_max98388_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return 0;
+}
+
+static const struct snd_soc_ops acp5x_max98388_play_ops = {
+ .startup = acp5x_max98388_startup,
+};
+
+static struct snd_soc_codec_conf acp5x_max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_MAX98388_COMP_LNAME,
+ ACP5X_MAX98388_DAI_NAME),
+ COMP_CODEC(ACP5X_MAX98388_COMP_RNAME,
+ ACP5X_MAX98388_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_98388_dai[] = {
+ {
+ .name = "acp5x-8821-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-max98388-play",
+ .stream_name = "MAX98388 Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .ops = &acp5x_max98388_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, max98388, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget acp5x_8821_98388_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SPK("SPK", NULL),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_98388_route[] = {
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+
+ { "SPK", NULL, "Left BE_OUT" },
+ { "SPK", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_card acp5x_8821_98388_card = {
+ .name = "acp5x-max98388",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_98388_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_98388_dai),
+ .dapm_widgets = acp5x_8821_98388_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_98388_widgets),
+ .dapm_routes = acp5x_8821_98388_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_98388_route),
+ .codec_conf = acp5x_max98388_conf,
+ .num_configs = ARRAY_SIZE(acp5x_max98388_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+static const struct dmi_system_id acp5x_vg_quirk_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ .driver_data = (void *)&acp5x_8821_35l41_card,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ .driver_data = (void *)&acp5x_8821_98388_card,
+ },
+ {}
+};
+
+static int acp5x_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ struct acp5x_platform_info *machine;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ dmi_id = dmi_first_match(acp5x_vg_quirk_table);
+ if (!dmi_id || !dmi_id->driver_data)
+ return -ENODEV;
+
+ machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card = dmi_id->driver_data;
+ card->dev = dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ return dev_err_probe(dev, ret, "Register card (%s) failed\n", card->name);
+
+ return 0;
+}
+
+static struct platform_driver acp5x_mach_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp5x_probe,
+};
+
+module_platform_driver(acp5x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 audio support");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
new file mode 100644
index 000000000000..aa4726899434
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_i2s_dma"
+
+static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+ struct i2s_dev_data *vg_i2s_data;
+ u16 irq_flag;
+ u32 val;
+
+ vg_i2s_data = dev_id;
+ if (!vg_i2s_data)
+ return IRQ_NONE;
+
+ irq_flag = 0;
+ val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) {
+ acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->play_stream);
+ irq_flag = 1;
+ }
+ if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) {
+ acp_writel(BIT(I2S_TX_THRESHOLD),
+ vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream);
+ irq_flag = 1;
+ }
+
+ if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) {
+ acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->capture_stream);
+ irq_flag = 1;
+ }
+ if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) {
+ acp_writel(BIT(I2S_RX_THRESHOLD),
+ vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream);
+ irq_flag = 1;
+ }
+
+ if (irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
+ u32 reg_dma_size, reg_fifo_size;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ val = ACP_SRAM_HS_PB_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_PB_PTE_OFFSET;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ val = ACP_SRAM_HS_CP_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_CP_PTE_OFFSET;
+ }
+ }
+ /* Group Enable */
+ acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+ acp_writel(I2S_HS_TX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
+ acp_writel(I2S_SP_TX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+ acp_writel(I2S_HS_RX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
+ acp_writel(I2S_SP_RX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR);
+ }
+ }
+ acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size);
+ acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr);
+ acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size);
+ acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD)
+ | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD),
+ rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static int acp5x_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *i2s_data;
+ int ret;
+
+ runtime = substream->runtime;
+ prtd = snd_soc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+
+ i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
+ if (!i2s_data)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp5x_pcm_hardware_playback;
+ else
+ runtime->hw = acp5x_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(i2s_data);
+ return ret;
+ }
+ i2s_data->acp5x_base = adata->acp5x_base;
+ runtime->private_data = i2s_data;
+ return ret;
+}
+
+static int acp5x_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp5x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u64 size;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ card = prtd->card;
+ pinfo = snd_soc_card_get_drvdata(card);
+ adata = dev_get_drvdata(component->dev);
+ rtd = substream->runtime->private_data;
+
+ if (!rtd)
+ return -EINVAL;
+
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->play_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = substream;
+ }
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->capture_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = substream;
+ }
+ }
+ } else {
+ dev_err(component->dev, "pinfo failed\n");
+ return -EINVAL;
+ }
+ size = params_buffer_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp5x_dma(rtd, substream->stream);
+ return 0;
+}
+
+static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_stream_instance *rtd;
+ u32 pos;
+ u32 buffersize;
+ u64 bytescount;
+
+ rtd = substream->runtime->private_data;
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd, substream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp5x_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp5x_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *ins;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ ins = substream->runtime->private_data;
+ if (!ins)
+ return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (ins->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->play_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = NULL;
+ }
+ } else {
+ switch (ins->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->capture_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = NULL;
+ }
+ }
+ kfree(ins);
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp5x_i2s_component = {
+ .name = DRV_NAME,
+ .open = acp5x_dma_open,
+ .close = acp5x_dma_close,
+ .hw_params = acp5x_dma_hw_params,
+ .pointer = acp5x_dma_pointer,
+ .pcm_construct = acp5x_dma_new,
+};
+
+static int acp5x_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp5x_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->i2s_irq = status;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp5x_i2s_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+ status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
+ irqflags, "ACP5x_I2S_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");
+ return status;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp5x_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp5x_pcm_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *rtd;
+ u32 val;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->play_stream && adata->play_stream->runtime) {
+ rtd = adata->play_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER);
+ }
+ }
+ if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) {
+ rtd = adata->i2ssp_play_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER);
+ }
+ }
+
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ rtd = adata->capture_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER);
+ }
+ }
+ if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) {
+ rtd = adata->i2ssp_capture_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER);
+ }
+ }
+ acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int acp5x_pcm_suspend(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int acp5x_pcm_runtime_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static const struct dev_pm_ops acp5x_pm_ops = {
+ RUNTIME_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
+};
+
+static struct platform_driver acp5x_dma_driver = {
+ .probe = acp5x_audio_probe,
+ .remove = acp5x_audio_remove,
+ .driver = {
+ .name = "acp5x_i2s_dma",
+ .pm = &acp5x_pm_ops,
+ },
+};
+
+module_platform_driver(acp5x_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h
new file mode 100644
index 000000000000..ac1936a8c43f
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PCM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "vg_chip_offset_byte.h"
+#include <sound/pcm.h>
+
+#define ACP5x_PHY_BASE_ADDRESS 0x1240000
+#define ACP_DEVICE_ID 0x15E2
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERR_INTR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+#define ACP5x_DEVS 4
+#define ACP5x_REG_START 0x1240000
+#define ACP5x_REG_END 0x1250200
+#define ACP5x_I2STDM_REG_START 0x1242400
+#define ACP5x_I2STDM_REG_END 0x1242410
+#define ACP5x_HS_TDM_REG_START 0x1242814
+#define ACP5x_HS_TDM_REG_END 0x1242824
+#define I2S_MODE 0
+#define ACP5x_I2S_MODE 1
+#define ACP5x_RES 4
+#define I2S_RX_THRESHOLD 27
+#define I2S_TX_THRESHOLD 28
+#define HS_TX_THRESHOLD 24
+#define HS_RX_THRESHOLD 23
+
+#define I2S_SP_INSTANCE 1
+#define I2S_HS_INSTANCE 2
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_HS_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_HS_CP_PTE_OFFSET 0x300
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_HS_TX_MEM_WINDOW_START 0x4040000
+#define I2S_HS_RX_MEM_WINDOW_START 0x4060000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define HS_PB_FIFO_ADDR_OFFSET 0x900
+#define HS_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define I2S_MASTER_MODE_ENABLE 1
+#define I2S_MASTER_MODE_DISABLE 0
+
+#define SLOT_WIDTH_8 8
+#define SLOT_WIDTH_16 16
+#define SLOT_WIDTH_24 24
+#define SLOT_WIDTH_32 32
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+#define ACP5x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+struct i2s_dev_data {
+ bool tdm_mode;
+ bool master_mode;
+ int i2s_irq;
+ u16 i2s_instance;
+ u32 tdm_fmt;
+ void __iomem *acp5x_base;
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+ struct snd_pcm_substream *i2ssp_play_stream;
+ struct snd_pcm_substream *i2ssp_capture_stream;
+};
+
+struct i2s_stream_instance {
+ u16 num_pages;
+ u16 i2s_instance;
+ u16 direction;
+ u16 channels;
+ u32 xfer_resolution;
+ u32 val;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp5x_base;
+ u32 lrclk_div;
+ u32 bclk_div;
+};
+
+union acp_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+struct acp5x_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+};
+
+union acp_i2stdm_mstrclkgen {
+ struct {
+ u32 i2stdm_master_mode : 1;
+ u32 i2stdm_format_mode : 1;
+ u32 i2stdm_lrclk_div_val : 9;
+ u32 i2stdm_bclk_div_val : 11;
+ u32:10;
+ } bitfields, bits;
+ u32 u32_all;
+};
+
+/* common header file uses exact offset rather than relative
+ * offset which requires subtraction logic from base_addr
+ * for accessing ACP5x MMIO space registers
+ */
+static inline u32 acp_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP5x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP5x_PHY_BASE_ADDRESS);
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
+ int direction)
+{
+ union acp_dma_count byte_count;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_TX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_TX_LINEARPOSCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_TX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_TX_LINEARPOSCNTR_LOW);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_RX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_RX_LINEARPOSCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_RX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_RX_LINEARPOSCNTR_LOW);
+ }
+ }
+ return byte_count.bytescount;
+}
+
+static inline void acp5x_set_i2s_clk(struct i2s_dev_data *adata,
+ struct i2s_stream_instance *rtd)
+{
+ union acp_i2stdm_mstrclkgen mclkgen;
+ u32 master_reg;
+
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ master_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ mclkgen.bits.i2stdm_master_mode = 0x1;
+ if (adata->tdm_mode)
+ mclkgen.bits.i2stdm_format_mode = 0x01;
+ else
+ mclkgen.bits.i2stdm_format_mode = 0x00;
+
+ mclkgen.bits.i2stdm_bclk_div_val = rtd->bclk_div;
+ mclkgen.bits.i2stdm_lrclk_div_val = rtd->lrclk_div;
+ acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg);
+}
diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
new file mode 100644
index 000000000000..af56ff09f02a
--- /dev/null
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD Vangogh ACP PCI Driver
+//
+// Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "acp5x.h"
+#include "../mach-config.h"
+
+struct acp5x_dev_data {
+ void __iomem *acp5x_base;
+ bool acp5x_audio_mode;
+ struct resource *res;
+ struct platform_device *pdev[ACP5x_DEVS];
+};
+
+static int acp5x_power_on(void __iomem *acp5x_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp5x_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp5x_reset(void __iomem *acp5x_base)
+{
+ u32 val;
+ int timeout;
+
+ acp_writel(1, acp5x_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp_writel(0, acp5x_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp5x_enable_interrupts(void __iomem *acp5x_base)
+{
+ acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp5x_disable_interrupts(void __iomem *acp5x_base)
+{
+ acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);
+ acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp5x_init(void __iomem *acp5x_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp5x_power_on(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x power on failed\n");
+ return ret;
+ }
+ acp_writel(0x01, acp5x_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp5x_reset(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x reset failed\n");
+ return ret;
+ }
+ acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
+ acp5x_enable_interrupts(acp5x_base);
+ return 0;
+}
+
+static int acp5x_deinit(void __iomem *acp5x_base)
+{
+ int ret;
+
+ acp5x_disable_interrupts(acp5x_base);
+ /* Reset */
+ ret = acp5x_reset(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x reset failed\n");
+ return ret;
+ }
+ acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
+ acp_writel(0x00, acp5x_base + ACP_CONTROL);
+ return 0;
+}
+
+static int snd_acp5x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp5x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP5x_DEVS];
+ unsigned int irqflags, flag;
+ int ret, i;
+ u32 addr, val;
+
+ /*
+ * Return if ACP config flag is defined, except when board
+ * supports SOF while it is not being enabled in kernel config.
+ */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY &&
+ (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH)))
+ return -ENODEV;
+
+ irqflags = IRQF_SHARED;
+ if (pci->revision != 0x50)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP5x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ addr = pci_resource_start(pci, 0);
+ adata->acp5x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp5x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp5x_init(adata->acp5x_base);
+ if (ret)
+ goto release_regions;
+
+ val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case I2S_MODE:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * ACP5x_RES,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp5x_i2s_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);
+
+ adata->res[1].name = "acp5x_i2s_sp";
+ adata->res[1].flags = IORESOURCE_MEM;
+ adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
+ adata->res[1].end = addr + ACP5x_I2STDM_REG_END;
+
+ adata->res[2].name = "acp5x_i2s_hs";
+ adata->res[2].flags = IORESOURCE_MEM;
+ adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
+ adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;
+
+ adata->res[3].name = "acp5x_i2s_irq";
+ adata->res[3].flags = IORESOURCE_IRQ;
+ adata->res[3].start = pci->irq;
+ adata->res[3].end = adata->res[3].start;
+
+ adata->acp5x_audio_mode = ACP5x_I2S_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp5x_i2s_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 4;
+ pdevinfo[0].res = &adata->res[0];
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "acp5x_i2s_playcap";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[1].num_res = 1;
+ pdevinfo[1].res = &adata->res[1];
+
+ pdevinfo[2].name = "acp5x_i2s_playcap";
+ pdevinfo[2].id = 1;
+ pdevinfo[2].parent = &pci->dev;
+ pdevinfo[2].num_res = 1;
+ pdevinfo[2].res = &adata->res[2];
+
+ pdevinfo[3].name = "acp5x_mach";
+ pdevinfo[3].id = 0;
+ pdevinfo[3].parent = &pci->dev;
+ for (i = 0; i < ACP5x_DEVS; i++) {
+ adata->pdev[i] =
+ platform_device_register_full(&pdevinfo[i]);
+ if (IS_ERR(adata->pdev[i])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[i].name);
+ ret = PTR_ERR(adata->pdev[i]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ default:
+ dev_info(&pci->dev, "ACP audio mode : %d\n", val);
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ for (--i; i >= 0; i--)
+ platform_device_unregister(adata->pdev[i]);
+de_init:
+ if (acp5x_deinit(adata->acp5x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp5x_suspend(struct device *dev)
+{
+ int ret;
+ struct acp5x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp5x_deinit(adata->acp5x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return ret;
+}
+
+static int snd_acp5x_resume(struct device *dev)
+{
+ int ret;
+ struct acp5x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp5x_init(adata->acp5x_base);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops acp5x_pm = {
+ RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
+};
+
+static void snd_acp5x_remove(struct pci_dev *pci)
+{
+ struct acp5x_dev_data *adata;
+ int i, ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
+ for (i = 0; i < ACP5x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+ }
+ ret = acp5x_deinit(adata->acp5x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp5x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);
+
+static struct pci_driver acp5x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp5x_ids,
+ .probe = snd_acp5x_probe,
+ .remove = snd_acp5x_remove,
+ .driver = {
+ .pm = pm_ptr(&acp5x_pm),
+ }
+};
+
+module_pci_driver(acp5x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/vangogh/vg_chip_offset_byte.h b/sound/soc/amd/vangogh/vg_chip_offset_byte.h
new file mode 100644
index 000000000000..b1165ae142b7
--- /dev/null
+++ b/sound/soc/amd/vangogh/vg_chip_offset_byte.h
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 5.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp_ip_OFFSET_HEADER
+#define _acp_ip_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+/* Registers from ACP_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241800
+#define ACP_EXTERNAL_INTR_CNTL 0x1241804
+#define ACP_EXTERNAL_INTR_STAT 0x1241808
+#define ACP_ERROR_STATUS 0x12418C4
+#define ACP_SW_I2S_ERROR_REASON 0x12418C8
+#define ACP_MEM_PG_STS 0x12418CC
+#define ACP_PGMEM_DEEP_SLEEP_CTRL 0x12418D0
+#define ACP_PGMEM_SHUT_DOWN_CTRL 0x12418D4
+
+/* Registers from ACP_PGFSM block */
+#define ACP_PIN_CONFIG 0x1241400
+#define ACP_PAD_PULLUP_CTRL 0x1241404
+#define ACP_PAD_PULLDOWN_CTRL 0x1241408
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124140C
+#define ACP_PAD_SCHMEN_CTRL 0x1241410
+#define ACP_SW_PAD_KEEPER_EN 0x1241414
+#define ACP_SW_WAKE_EN 0x1241418
+#define ACP_I2S_WAKE_EN 0x124141C
+#define ACP_PME_EN 0x1241420
+#define ACP_PGFSM_CONTROL 0x1241424
+#define ACP_PGFSM_STATUS 0x1241428
+#define ACP_CLKMUX_SEL 0x124142C
+#define ACP_DEVICE_STATE 0x1241430
+#define AZ_DEVICE_STATE 0x1241434
+#define ACP_INTR_URGENCY_TIMER 0x1241438
+#define AZ_INTR_URGENCY_TIMER 0x124143C
+#define ACP_AON_SW_INTR_TRIG 0x1241440
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+#endif
diff --git a/sound/soc/amd/yc/Makefile b/sound/soc/amd/yc/Makefile
new file mode 100644
index 000000000000..7a0a3a410b2d
--- /dev/null
+++ b/sound/soc/amd/yc/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Yellow Carp platform Support
+snd-pci-acp6x-y := pci-acp6x.o
+snd-acp6x-pdm-dma-y := acp6x-pdm-dma.o
+snd-soc-acp6x-mach-y := acp6x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-pci-acp6x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-acp6x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_YC_MACH) += snd-soc-acp6x-mach.o
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
new file mode 100644
index 000000000000..f210a253da9f
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Yellow Carp platform using DMIC
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_mach"
+
+SND_SOC_DAILINK_DEF(acp6x_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_yc_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_yc_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp6x_dai_pdm[] = {
+ {
+ .name = "acp6x-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp6x_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp6x_card = {
+ .name = "acp6x",
+ .owner = THIS_MODULE,
+ .dai_link = acp6x_dai_pdm,
+ .num_links = 1,
+};
+
+static const struct dmi_system_id yc_acp_quirk_table[] = {
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5525"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D1"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21HY"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M1"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M4"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21ME"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82TL"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82V2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82YM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83AS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83BS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83L3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83N6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83J2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83J3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5602RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6400RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M3402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6500RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6501RM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1404FA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1504FA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M7600RE"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M3502RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VEK"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 14 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 15 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi G 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Razer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Blade 14 (2022) - RZ09-0427"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Swift SFA16-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "IRBIS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "15NBC1011"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16z-n000"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb1xxx"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb2xxx"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A42"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A43"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A44"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A22"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A3E"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A7F"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A81"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8B27"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8B2F"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8BD6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MECHREVO"),
+ DMI_MATCH(DMI_BOARD_NAME, "MRID6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MDC"),
+ DMI_MATCH(DMI_BOARD_NAME, "Herbag_MDU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "System76"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "pang12"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "System76"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "pang13"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7UCX"),
+ }
+ },
+ {}
+};
+
+static int acp6x_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ struct acp6x_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ int ret;
+ bool is_dmic_enable, wov_en;
+
+ /* IF WOV entry not found, enable dmic based on AcpDmicConnected entry*/
+ is_dmic_enable = false;
+ wov_en = true;
+ /* check the parent device's firmware node has _DSD or not */
+ adev = ACPI_COMPANION(pdev->dev.parent);
+ if (adev) {
+ const union acpi_object *obj;
+
+ if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == 1)
+ is_dmic_enable = true;
+ }
+
+ handle = ACPI_HANDLE(pdev->dev.parent);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret)) {
+ wov_en = dmic_status;
+ if (!wov_en)
+ return -ENODEV;
+ } else {
+ /* Incase of ACPI method read failure then jump to check_dmi_entry */
+ goto check_dmi_entry;
+ }
+
+ if (is_dmic_enable)
+ platform_set_drvdata(pdev, &acp6x_card);
+
+check_dmi_entry:
+ /* check for any DMI overrides */
+ dmi_id = dmi_first_match(yc_acp_quirk_table);
+ if (dmi_id)
+ platform_set_drvdata(pdev, dmi_id->driver_data);
+
+ card = platform_get_drvdata(pdev);
+ if (!card)
+ return -ENODEV;
+ dev_info(&pdev->dev, "Enabling ACP DMIC support via %s", dmi_id ? "DMI" : "ACPI");
+ acp6x_card.dev = &pdev->dev;
+
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static struct platform_driver acp6x_mach_driver = {
+ .driver = {
+ .name = "acp_yc_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp6x_probe,
+};
+
+module_platform_driver(acp6x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Yellow Carp support for DMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
new file mode 100644
index 000000000000..ac758b90f441
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Yellow Carp PDM Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp6x_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ acp6x_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ acp6x_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ acp6x_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ acp6x_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp6x_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp6x_enable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp6x_disable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static bool acp6x_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp6x_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp6x_enable_pdm_clock(acp_base);
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ acp6x_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp6x_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ acp6x_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp6x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp6x_writel(PAGE_SIZE_4K_ENABLE, rtd->acp6x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp6x_writel(low, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp6x_writel(high, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp6x_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp6x_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp6x_base = adata->acp6x_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp6x_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp6x_config_dma(rtd, substream->stream);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp6x_base);
+ return 0;
+}
+
+static u64 acp6x_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ union acp_pdm_dma_count byte_count;
+
+ byte_count.bcount.high =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count.bcount.low =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp6x_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp6x_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp6x_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp6x_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ adata->capture_stream = NULL;
+ return 0;
+}
+
+static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ acp6x_writel(ch_mask, rtd->acp6x_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ acp6x_writel(PDM_DECIMATION_FACTOR, rtd->acp6x_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp6x_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (!pdm_status)
+ ret = acp6x_start_pdm_dma(rtd->acp6x_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (pdm_status)
+ ret = acp6x_stop_pdm_dma(rtd->acp6x_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
+ .trigger = acp6x_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp6x_pdm_dai_driver = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp6x_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp6x_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp6x_pdm_dma_open,
+ .close = acp6x_pdm_dma_close,
+ .hw_params = acp6x_pdm_dma_hw_params,
+ .pointer = acp6x_pdm_dma_pointer,
+ .pcm_construct = acp6x_pdm_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+static int acp6x_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ int status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp6x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp6x_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp6x_pdm_component,
+ &acp6x_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp6x_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp6x_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp6x_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp6x_base);
+ }
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int acp6x_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int acp6x_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static const struct dev_pm_ops acp6x_pdm_pm_ops = {
+ RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
+};
+
+static struct platform_driver acp6x_pdm_dma_driver = {
+ .probe = acp6x_pdm_audio_probe,
+ .remove = acp6x_pdm_audio_remove,
+ .driver = {
+ .name = "acp_yc_pdm_dma",
+ .pm = pm_ptr(&acp6x_pdm_pm_ops),
+ },
+};
+
+module_platform_driver(acp6x_pdm_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6x YC PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x.h b/sound/soc/amd/yc/acp6x.h
new file mode 100644
index 000000000000..2de7d1edf00b
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+#define ACP6x_REG_START 0x1240000
+#define ACP6x_REG_END 0x1250200
+#define ACP6x_DEVS 3
+#define ACP6x_PDM_MODE 1
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp6x_base;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp6x_base;
+};
+
+union acp_pdm_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+static inline u32 acp6x_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp6x_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
diff --git a/sound/soc/amd/yc/acp6x_chip_offset_byte.h b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..f05fb2dfb5da
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp6x_OFFSET_HEADER
+#define _acp6x_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_9 0x1240C44
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_9 0x1240C48
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_10 0x1240C4C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_10 0x1240C50
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_11 0x1240C54
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_11 0x1240C58
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_12 0x1240C5C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_12 0x1240C60
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_13 0x1240C64
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_13 0x1240C68
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_14 0x1240C6C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_14 0x1240C70
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_15 0x1240C74
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_15 0x1240C78
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_16 0x1240C7C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_16 0x1240C80
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_ZSC_DSP_CTRL 0x1241014
+#define ACP_ZSC_STS 0x1241018
+#define ACP_PGFSM_CONTROL 0x1241024
+#define ACP_PGFSM_STATUS 0x1241028
+#define ACP_CLKMUX_SEL 0x124102C
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+#define ACP_SW_PAD_KEEPER_EN 0x1241454
+#define ACP_SW_WAKE_EN 0x1241458
+#define ACP_I2S_WAKE_EN 0x124145C
+#define ACP_SW1_WAKE_EN 0x1241460
+
+/* Registers from ACP_P1_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241A00
+#define ACP_EXTERNAL_INTR_CNTL 0x1241A04
+#define ACP_EXTERNAL_INTR_CNTL1 0x1241A08
+#define ACP_EXTERNAL_INTR_STAT 0x1241A0C
+#define ACP_EXTERNAL_INTR_STAT1 0x1241A10
+#define ACP_ERROR_STATUS 0x1241A4C
+#define ACP_P1_SW_I2S_ERROR_REASON 0x1241A50
+#define ACP_P1_SW_POS_TRACK_I2S_TX_CTRL 0x1241A6C
+#define ACP_P1_SW_I2S_TX_DMA_POS 0x1241A70
+#define ACP_P1_SW_POS_TRACK_I2S_RX_CTRL 0x1241A74
+#define ACP_P1_SW_I2S_RX_DMA_POS 0x1241A78
+#define ACP_P1_DMIC_I2S_GPIO_INTR_CTRL 0x1241A7C
+#define ACP_P1_DMIC_I2S_GPIO_INTR_STATUS 0x1241A80
+#define ACP_SCRATCH_REG_BASE_ADDR 0x1241A84
+#define ACP_P1_SW_POS_TRACK_BT_TX_CTRL 0x1241A88
+#define ACP_P1_SW_BT_TX_DMA_POS 0x1241A8C
+#define ACP_P1_SW_POS_TRACK_HS_TX_CTRL 0x1241A90
+#define ACP_P1_SW_HS_TX_DMA_POS 0x1241A94
+#define ACP_P1_SW_POS_TRACK_BT_RX_CTRL 0x1241A98
+#define ACP_P1_SW_BT_RX_DMA_POS 0x1241A9C
+#define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x1241AA0
+#define ACP_P1_SW_HS_RX_DMA_POS 0x1241AA4
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+
+/* Registers from ACP_WOV block */
+#define ACP_WOV_PDM_ENABLE 0x1242C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08
+#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x1242C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x1242C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x1242C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x1242C30
+#define ACP_WOV_WAKE 0x1242C54
+#define ACP_WOV_BUFFER_STATUS 0x1242C58
+#define ACP_WOV_MISC_CTRL 0x1242C5C
+#define ACP_WOV_CLK_CTRL 0x1242C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x1242C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x1242C68
+#define ACP_PDM_CLKDIV 0x1242C6C
+
+/* Registers from ACP_P1_AUDIO_BUFFERS block */
+#define ACP_P1_I2S_RX_RINGBUFADDR 0x1243A00
+#define ACP_P1_I2S_RX_RINGBUFSIZE 0x1243A04
+#define ACP_P1_I2S_RX_LINKPOSITIONCNTR 0x1243A08
+#define ACP_P1_I2S_RX_FIFOADDR 0x1243A0C
+#define ACP_P1_I2S_RX_FIFOSIZE 0x1243A10
+#define ACP_P1_I2S_RX_DMA_SIZE 0x1243A14
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1243A18
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x1243A1C
+#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x1243A20
+#define ACP_P1_I2S_TX_RINGBUFADDR 0x1243A24
+#define ACP_P1_I2S_TX_RINGBUFSIZE 0x1243A28
+#define ACP_P1_I2S_TX_LINKPOSITIONCNTR 0x1243A2C
+#define ACP_P1_I2S_TX_FIFOADDR 0x1243A30
+#define ACP_P1_I2S_TX_FIFOSIZE 0x1243A34
+#define ACP_P1_I2S_TX_DMA_SIZE 0x1243A38
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x1243A3C
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1243A40
+#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x1243A44
+#define ACP_P1_BT_RX_RINGBUFADDR 0x1243A48
+#define ACP_P1_BT_RX_RINGBUFSIZE 0x1243A4C
+#define ACP_P1_BT_RX_LINKPOSITIONCNTR 0x1243A50
+#define ACP_P1_BT_RX_FIFOADDR 0x1243A54
+#define ACP_P1_BT_RX_FIFOSIZE 0x1243A58
+#define ACP_P1_BT_RX_DMA_SIZE 0x1243A5C
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1243A60
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x1243A64
+#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x1243A68
+#define ACP_P1_BT_TX_RINGBUFADDR 0x1243A6C
+#define ACP_P1_BT_TX_RINGBUFSIZE 0x1243A70
+#define ACP_P1_BT_TX_LINKPOSITIONCNTR 0x1243A74
+#define ACP_P1_BT_TX_FIFOADDR 0x1243A78
+#define ACP_P1_BT_TX_FIFOSIZE 0x1243A7C
+#define ACP_P1_BT_TX_DMA_SIZE 0x1243A80
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1243A84
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x1243A88
+#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x1243A8C
+#define ACP_P1_HS_RX_RINGBUFADDR 0x1243A90
+#define ACP_P1_HS_RX_RINGBUFSIZE 0x1243A94
+#define ACP_P1_HS_RX_LINKPOSITIONCNTR 0x1243A98
+#define ACP_P1_HS_RX_FIFOADDR 0x1243A9C
+#define ACP_P1_HS_RX_FIFOSIZE 0x1243AA0
+#define ACP_P1_HS_RX_DMA_SIZE 0x1243AA4
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x1243AA8
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x1243AAC
+#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x1243AB0
+#define ACP_P1_HS_TX_RINGBUFADDR 0x1243AB4
+#define ACP_P1_HS_TX_RINGBUFSIZE 0x1243AB8
+#define ACP_P1_HS_TX_LINKPOSITIONCNTR 0x1243ABC
+#define ACP_P1_HS_TX_FIFOADDR 0x1243AC0
+#define ACP_P1_HS_TX_FIFOSIZE 0x1243AC4
+#define ACP_P1_HS_TX_DMA_SIZE 0x1243AC8
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x1243ACC
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x1243AD0
+#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x1243AD4
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+#endif
diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c
new file mode 100644
index 000000000000..1140ed1cbb3d
--- /dev/null
+++ b/sound/soc/amd/yc/pci-acp6x.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD Yellow Carp ACP PCI Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+struct acp6x_dev_data {
+ void __iomem *acp6x_base;
+ struct resource *res;
+ bool acp6x_audio_mode;
+ struct platform_device *pdev[ACP6x_DEVS];
+};
+
+static int acp6x_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ acp6x_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp6x_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp6x_disable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp6x_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp6x_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ acp6x_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ acp6x_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int acp6x_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ acp6x_disable_interrupts(acp_base);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ acp6x_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)
+{
+ struct acp6x_dev_data *adata;
+ struct pdm_dev_data *yc_pdm_data;
+ u32 val;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+
+ val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (val & BIT(PDM_DMA_STAT)) {
+ yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
+ acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (yc_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(yc_pdm_data->capture_stream);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int snd_acp6x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp6x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP6x_DEVS];
+ int index = 0;
+ int val = 0x00;
+ u32 addr;
+ unsigned int irqflags, flag;
+ int ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Yellow Carp device check */
+ switch (pci->revision) {
+ case 0x60:
+ case 0x6f:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp6x pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_info(&pci->dev, "Audio Mode %d\n", val);
+ break;
+ default:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource),
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res->name = "acp_iomem";
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);
+
+ adata->acp6x_audio_mode = ACP6x_PDM_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp_yc_pdm_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 1;
+ pdevinfo[0].res = adata->res;
+
+ pdevinfo[1].name = "dmic-codec";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+
+ pdevinfo[2].name = "acp_yc_mach";
+ pdevinfo[2].id = 0;
+ pdevinfo[2].parent = &pci->dev;
+
+ for (index = 0; index < ACP6x_DEVS; index++) {
+ adata->pdev[index] =
+ platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ }
+ ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,
+ irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto unregister_devs;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+unregister_devs:
+ for (--index; index >= 0; index--)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (acp6x_deinit(adata->acp6x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp6x_suspend(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_acp6x_resume(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops acp6x_pm = {
+ RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
+};
+
+static void snd_acp6x_remove(struct pci_dev *pci)
+{
+ struct acp6x_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {
+ for (index = 0; index < ACP6x_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+ }
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp6x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);
+
+static struct pci_driver yc_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp6x_ids,
+ .probe = snd_acp6x_probe,
+ .remove = snd_acp6x_remove,
+ .driver = {
+ .pm = pm_ptr(&acp6x_pm),
+ }
+};
+
+module_pci_driver(yc_acp6x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig
new file mode 100644
index 000000000000..d8dc2f1ccc83
--- /dev/null
+++ b/sound/soc/apple/Kconfig
@@ -0,0 +1,11 @@
+menu "Apple"
+
+config SND_SOC_APPLE_MCA
+ tristate "Apple Silicon MCA driver"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select SND_DMAENGINE_PCM
+ help
+ This option enables an ASoC platform driver for MCA peripherals found
+ on Apple Silicon SoCs.
+
+endmenu
diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile
new file mode 100644
index 000000000000..1eb8fbef60c6
--- /dev/null
+++ b/sound/soc/apple/Makefile
@@ -0,0 +1,3 @@
+snd-soc-apple-mca-y := mca.o
+
+obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o
diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
new file mode 100644
index 000000000000..c4dcb2b54591
--- /dev/null
+++ b/sound/soc/apple/mca.c
@@ -0,0 +1,1212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Apple SoCs MCA driver
+//
+// Copyright (C) The Asahi Linux Contributors
+//
+// The MCA peripheral is made up of a number of identical units called clusters.
+// Each cluster has its separate clock parent, SYNC signal generator, carries
+// four SERDES units and has a dedicated I2S port on the SoC's periphery.
+//
+// The clusters can operate independently, or can be combined together in a
+// configurable manner. We mostly treat them as self-contained independent
+// units and don't configure any cross-cluster connections except for the I2S
+// ports. The I2S ports can be routed to any of the clusters (irrespective
+// of their native cluster). We map this onto ASoC's (DPCM) notion of backend
+// and frontend DAIs. The 'cluster guts' are frontends which are dynamically
+// routed to backend I2S ports.
+//
+// DAI references in devicetree are resolved to backends. The routing between
+// frontends and backends is determined by the machine driver in the DAPM paths
+// it supplies.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define USE_RXB_FOR_CAPTURE
+
+/* Relative to cluster base */
+#define REG_STATUS 0x0
+#define STATUS_MCLK_EN BIT(0)
+#define REG_MCLK_CONF 0x4
+#define MCLK_CONF_DIV GENMASK(11, 8)
+
+#define REG_SYNCGEN_STATUS 0x100
+#define SYNCGEN_STATUS_EN BIT(0)
+#define REG_SYNCGEN_MCLK_SEL 0x104
+#define SYNCGEN_MCLK_SEL GENMASK(3, 0)
+#define REG_SYNCGEN_HI_PERIOD 0x108
+#define REG_SYNCGEN_LO_PERIOD 0x10c
+
+#define REG_PORT_ENABLES 0x600
+#define PORT_ENABLES_CLOCKS GENMASK(2, 1)
+#define PORT_ENABLES_TX_DATA BIT(3)
+#define REG_PORT_CLOCK_SEL 0x604
+#define PORT_CLOCK_SEL GENMASK(11, 8)
+#define REG_PORT_DATA_SEL 0x608
+#define PORT_DATA_SEL_TXA(cl) (1 << ((cl)*2))
+#define PORT_DATA_SEL_TXB(cl) (2 << ((cl)*2))
+
+#define REG_INTSTATE 0x700
+#define REG_INTMASK 0x704
+
+/* Bases of serdes units (relative to cluster) */
+#define CLUSTER_RXA_OFF 0x200
+#define CLUSTER_TXA_OFF 0x300
+#define CLUSTER_RXB_OFF 0x400
+#define CLUSTER_TXB_OFF 0x500
+
+#define CLUSTER_TX_OFF CLUSTER_TXA_OFF
+
+#ifndef USE_RXB_FOR_CAPTURE
+#define CLUSTER_RX_OFF CLUSTER_RXA_OFF
+#else
+#define CLUSTER_RX_OFF CLUSTER_RXB_OFF
+#endif
+
+/* Relative to serdes unit base */
+#define REG_SERDES_STATUS 0x00
+#define SERDES_STATUS_EN BIT(0)
+#define SERDES_STATUS_RST BIT(1)
+#define REG_TX_SERDES_CONF 0x04
+#define REG_RX_SERDES_CONF 0x08
+#define SERDES_CONF_NCHANS GENMASK(3, 0)
+#define SERDES_CONF_WIDTH_MASK GENMASK(8, 4)
+#define SERDES_CONF_WIDTH_16BIT 0x40
+#define SERDES_CONF_WIDTH_20BIT 0x80
+#define SERDES_CONF_WIDTH_24BIT 0xc0
+#define SERDES_CONF_WIDTH_32BIT 0x100
+#define SERDES_CONF_BCLK_POL 0x400
+#define SERDES_CONF_LSB_FIRST 0x800
+#define SERDES_CONF_UNK1 BIT(12)
+#define SERDES_CONF_UNK2 BIT(13)
+#define SERDES_CONF_UNK3 BIT(14)
+#define SERDES_CONF_NO_DATA_FEEDBACK BIT(15)
+#define SERDES_CONF_SYNC_SEL GENMASK(18, 16)
+#define REG_TX_SERDES_BITSTART 0x08
+#define REG_RX_SERDES_BITSTART 0x0c
+#define REG_TX_SERDES_SLOTMASK 0x0c
+#define REG_RX_SERDES_SLOTMASK 0x10
+#define REG_RX_SERDES_PORT 0x04
+
+/* Relative to switch base */
+#define REG_DMA_ADAPTER_A(cl) (0x8000 * (cl))
+#define REG_DMA_ADAPTER_B(cl) (0x8000 * (cl) + 0x4000)
+#define DMA_ADAPTER_TX_LSB_PAD GENMASK(4, 0)
+#define DMA_ADAPTER_TX_NCHANS GENMASK(6, 5)
+#define DMA_ADAPTER_RX_MSB_PAD GENMASK(12, 8)
+#define DMA_ADAPTER_RX_NCHANS GENMASK(14, 13)
+#define DMA_ADAPTER_NCHANS GENMASK(22, 20)
+
+#define SWITCH_STRIDE 0x8000
+#define CLUSTER_STRIDE 0x4000
+
+#define MAX_NCLUSTERS 6
+
+#define APPLE_MCA_FMTBITS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct mca_cluster {
+ int no;
+ __iomem void *base;
+ struct mca_data *host;
+ struct device *pd_dev;
+ struct clk *clk_parent;
+ struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1];
+
+ bool port_started[SNDRV_PCM_STREAM_LAST + 1];
+ int port_driver; /* The cluster driving this cluster's port */
+
+ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1];
+ struct device_link *pd_link;
+
+ unsigned int bclk_ratio;
+
+ /* Masks etc. picked up via the set_tdm_slot method */
+ int tdm_slots;
+ int tdm_slot_width;
+ unsigned int tdm_tx_mask;
+ unsigned int tdm_rx_mask;
+};
+
+struct mca_data {
+ struct device *dev;
+
+ __iomem void *switch_base;
+
+ struct device *pd_dev;
+ struct reset_control *rstc;
+ struct device_link *pd_link;
+
+ /* Mutex for accessing port_driver of foreign clusters */
+ struct mutex port_mutex;
+
+ int nclusters;
+ struct mca_cluster clusters[] __counted_by(nclusters);
+};
+
+static void mca_modify(struct mca_cluster *cl, int regoffset, u32 mask, u32 val)
+{
+ __iomem void *ptr = cl->base + regoffset;
+ u32 newval;
+
+ newval = (val & mask) | (readl_relaxed(ptr) & ~mask);
+ writel_relaxed(newval, ptr);
+}
+
+/*
+ * Get the cluster of FE or BE DAI
+ */
+static struct mca_cluster *mca_dai_to_cluster(struct snd_soc_dai *dai)
+{
+ struct mca_data *mca = snd_soc_dai_get_drvdata(dai);
+ /*
+ * FE DAIs are 0 ... nclusters - 1
+ * BE DAIs are nclusters ... 2*nclusters - 1
+ */
+ int cluster_no = dai->id % mca->nclusters;
+
+ return &mca->clusters[cluster_no];
+}
+
+/* called before PCM trigger */
+static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
+ int serdes_conf =
+ serdes_unit + (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, 7));
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN | SERDES_STATUS_RST,
+ SERDES_STATUS_RST);
+ /*
+ * Experiments suggest that it takes at most ~1 us
+ * for the bit to clear, so wait 2 us for good measure.
+ */
+ udelay(2);
+ WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) &
+ SERDES_STATUS_RST);
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
+ mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+ FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1));
+ break;
+ default:
+ break;
+ }
+}
+
+static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN | SERDES_STATUS_RST,
+ SERDES_STATUS_EN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mca_fe_enable_clocks(struct mca_cluster *cl)
+{
+ struct mca_data *mca = cl->host;
+ int ret;
+
+ ret = clk_prepare_enable(cl->clk_parent);
+ if (ret) {
+ dev_err(mca->dev,
+ "cluster %d: unable to enable clock parent: %d\n",
+ cl->no, ret);
+ return ret;
+ }
+
+ /*
+ * We can't power up the device earlier than this because
+ * the power state driver would error out on seeing the device
+ * as clock-gated.
+ */
+ cl->pd_link = device_link_add(mca->dev, cl->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!cl->pd_link) {
+ dev_err(mca->dev,
+ "cluster %d: unable to prop-up power domain\n", cl->no);
+ clk_disable_unprepare(cl->clk_parent);
+ return -EINVAL;
+ }
+
+ writel_relaxed(cl->no + 1, cl->base + REG_SYNCGEN_MCLK_SEL);
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN,
+ SYNCGEN_STATUS_EN);
+ mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, STATUS_MCLK_EN);
+
+ return 0;
+}
+
+static void mca_fe_disable_clocks(struct mca_cluster *cl)
+{
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0);
+ mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, 0);
+
+ device_link_del(cl->pd_link);
+ clk_disable_unprepare(cl->clk_parent);
+}
+
+static bool mca_fe_clocks_in_use(struct mca_cluster *cl)
+{
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *be_cl;
+ int stream, i;
+
+ mutex_lock(&mca->port_mutex);
+ for (i = 0; i < mca->nclusters; i++) {
+ be_cl = &mca->clusters[i];
+
+ if (be_cl->port_driver != cl->no)
+ continue;
+
+ for_each_pcm_streams(stream) {
+ if (be_cl->clocks_in_use[stream]) {
+ mutex_unlock(&mca->port_mutex);
+ return true;
+ }
+ }
+ }
+ mutex_unlock(&mca->port_mutex);
+ return false;
+}
+
+static int mca_be_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl;
+ int ret;
+
+ if (cl->port_driver < 0)
+ return -EINVAL;
+
+ fe_cl = &mca->clusters[cl->port_driver];
+
+ /*
+ * Typically the CODECs we are paired with will require clocks
+ * to be present at time of unmute with the 'mute_stream' op
+ * or at time of DAPM widget power-up. We need to enable clocks
+ * here at the latest (frontend prepare would be too late).
+ */
+ if (!mca_fe_clocks_in_use(fe_cl)) {
+ ret = mca_fe_enable_clocks(fe_cl);
+ if (ret < 0)
+ return ret;
+ }
+
+ cl->clocks_in_use[substream->stream] = true;
+
+ return 0;
+}
+
+static int mca_be_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl;
+
+ if (cl->port_driver < 0)
+ return -EINVAL;
+
+ /*
+ * We are operating on a foreign cluster here, but since we
+ * belong to the same PCM, accesses should have been
+ * synchronized at ASoC level.
+ */
+ fe_cl = &mca->clusters[cl->port_driver];
+ if (!mca_fe_clocks_in_use(fe_cl))
+ return 0; /* Nothing to do */
+
+ cl->clocks_in_use[substream->stream] = false;
+
+ if (!mca_fe_clocks_in_use(fe_cl))
+ mca_fe_disable_clocks(fe_cl);
+
+ return 0;
+}
+
+static unsigned int mca_crop_mask(unsigned int mask, int nchans)
+{
+ while (hweight32(mask) > nchans)
+ mask &= ~(1 << __fls(mask));
+
+ return mask;
+}
+
+static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit,
+ unsigned int mask, int slots, int nchans,
+ int slot_width, bool is_tx, int port)
+{
+ __iomem void *serdes_base = cl->base + serdes_unit;
+ u32 serdes_conf, serdes_conf_mask;
+
+ serdes_conf_mask = SERDES_CONF_WIDTH_MASK | SERDES_CONF_NCHANS;
+ serdes_conf = FIELD_PREP(SERDES_CONF_NCHANS, max(slots, 1) - 1);
+ switch (slot_width) {
+ case 16:
+ serdes_conf |= SERDES_CONF_WIDTH_16BIT;
+ break;
+ case 20:
+ serdes_conf |= SERDES_CONF_WIDTH_20BIT;
+ break;
+ case 24:
+ serdes_conf |= SERDES_CONF_WIDTH_24BIT;
+ break;
+ case 32:
+ serdes_conf |= SERDES_CONF_WIDTH_32BIT;
+ break;
+ default:
+ goto err;
+ }
+
+ serdes_conf_mask |= SERDES_CONF_SYNC_SEL;
+ serdes_conf |= FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1);
+
+ if (is_tx) {
+ serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3;
+ serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3;
+ } else {
+ serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3 |
+ SERDES_CONF_NO_DATA_FEEDBACK;
+ serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_NO_DATA_FEEDBACK;
+ }
+
+ mca_modify(cl,
+ serdes_unit +
+ (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF),
+ serdes_conf_mask, serdes_conf);
+
+ if (is_tx) {
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_TX_SERDES_SLOTMASK);
+ writel_relaxed(~((u32)mca_crop_mask(mask, nchans)),
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0x4);
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0x8);
+ writel_relaxed(~((u32)mask),
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0xc);
+ } else {
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_RX_SERDES_SLOTMASK);
+ writel_relaxed(~((u32)mca_crop_mask(mask, nchans)),
+ serdes_base + REG_RX_SERDES_SLOTMASK + 0x4);
+ writel_relaxed(1 << port,
+ serdes_base + REG_RX_SERDES_PORT);
+ }
+
+ return 0;
+
+err:
+ dev_err(cl->host->dev,
+ "unsupported SERDES configuration requested (mask=0x%x slots=%d slot_width=%d)\n",
+ mask, slots, slot_width);
+ return -EINVAL;
+}
+
+static int mca_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ unsigned int mask, nchannels;
+
+ if (cl->tdm_slots) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mask = cl->tdm_tx_mask;
+ else
+ mask = cl->tdm_rx_mask;
+
+ nchannels = hweight32(mask);
+ } else {
+ nchannels = 2;
+ }
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, nchannels);
+}
+
+static int mca_fe_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ cl->tdm_slots = slots;
+ cl->tdm_slot_width = slot_width;
+ cl->tdm_tx_mask = tx_mask;
+ cl->tdm_rx_mask = rx_mask;
+
+ return 0;
+}
+
+static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ bool fpol_inv = false;
+ u32 serdes_conf = 0;
+ u32 bitstart;
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) !=
+ SND_SOC_DAIFMT_BP_FP)
+ goto err;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fpol_inv = 0;
+ bitstart = 1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fpol_inv = 1;
+ bitstart = 0;
+ break;
+ default:
+ goto err;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ fpol_inv ^= 1;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ serdes_conf |= SERDES_CONF_BCLK_POL;
+ break;
+ }
+
+ if (!fpol_inv)
+ goto err;
+
+ mca_modify(cl, CLUSTER_TX_OFF + REG_TX_SERDES_CONF,
+ SERDES_CONF_BCLK_POL, serdes_conf);
+ mca_modify(cl, CLUSTER_RX_OFF + REG_RX_SERDES_CONF,
+ SERDES_CONF_BCLK_POL, serdes_conf);
+ writel_relaxed(bitstart,
+ cl->base + CLUSTER_TX_OFF + REG_TX_SERDES_BITSTART);
+ writel_relaxed(bitstart,
+ cl->base + CLUSTER_RX_OFF + REG_RX_SERDES_BITSTART);
+
+ return 0;
+
+err:
+ dev_err(mca->dev, "unsupported DAI format (0x%x) requested\n", fmt);
+ return -EINVAL;
+}
+
+static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ cl->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static int mca_fe_get_port(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = NULL;
+ for_each_dpcm_be(fe, substream->stream, dpcm) {
+ be = dpcm->be;
+ break;
+ }
+
+ if (!be)
+ return -EINVAL;
+
+ return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no;
+}
+
+static int mca_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct device *dev = mca->dev;
+ unsigned int samp_rate = params_rate(params);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool refine_tdm = false;
+ unsigned long bclk_ratio;
+ unsigned int tdm_slots, tdm_slot_width, tdm_mask;
+ u32 regval, pad;
+ int ret, port, nchans_ceiled;
+
+ if (!cl->tdm_slot_width) {
+ /*
+ * We were not given TDM settings from above, set initial
+ * guesses which will later be refined.
+ */
+ tdm_slot_width = params_width(params);
+ tdm_slots = params_channels(params);
+ refine_tdm = true;
+ } else {
+ tdm_slot_width = cl->tdm_slot_width;
+ tdm_slots = cl->tdm_slots;
+ tdm_mask = is_tx ? cl->tdm_tx_mask : cl->tdm_rx_mask;
+ }
+
+ if (cl->bclk_ratio)
+ bclk_ratio = cl->bclk_ratio;
+ else
+ bclk_ratio = tdm_slot_width * tdm_slots;
+
+ if (refine_tdm) {
+ int nchannels = params_channels(params);
+
+ if (nchannels > 2) {
+ dev_err(dev, "missing TDM for stream with two or more channels\n");
+ return -EINVAL;
+ }
+
+ if ((bclk_ratio % nchannels) != 0) {
+ dev_err(dev, "BCLK ratio (%ld) not divisible by no. of channels (%d)\n",
+ bclk_ratio, nchannels);
+ return -EINVAL;
+ }
+
+ tdm_slot_width = bclk_ratio / nchannels;
+
+ if (tdm_slot_width > 32 && nchannels == 1)
+ tdm_slot_width = 32;
+
+ if (tdm_slot_width < params_width(params)) {
+ dev_err(dev, "TDM slots too narrow (tdm=%u params=%d)\n",
+ tdm_slot_width, params_width(params));
+ return -EINVAL;
+ }
+
+ tdm_mask = (1 << tdm_slots) - 1;
+ }
+
+ port = mca_fe_get_port(substream);
+ if (port < 0)
+ return port;
+
+ ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF,
+ tdm_mask, tdm_slots, params_channels(params),
+ tdm_slot_width, is_tx, port);
+ if (ret)
+ return ret;
+
+ pad = 32 - params_width(params);
+
+ /*
+ * TODO: Here the register semantics aren't clear.
+ */
+ nchans_ceiled = min_t(int, params_channels(params), 4);
+ regval = FIELD_PREP(DMA_ADAPTER_NCHANS, nchans_ceiled) |
+ FIELD_PREP(DMA_ADAPTER_TX_NCHANS, 0x2) |
+ FIELD_PREP(DMA_ADAPTER_RX_NCHANS, 0x2) |
+ FIELD_PREP(DMA_ADAPTER_TX_LSB_PAD, pad) |
+ FIELD_PREP(DMA_ADAPTER_RX_MSB_PAD, pad);
+
+#ifndef USE_RXB_FOR_CAPTURE
+ writel_relaxed(regval, mca->switch_base + REG_DMA_ADAPTER_A(cl->no));
+#else
+ if (is_tx)
+ writel_relaxed(regval,
+ mca->switch_base + REG_DMA_ADAPTER_A(cl->no));
+ else
+ writel_relaxed(regval,
+ mca->switch_base + REG_DMA_ADAPTER_B(cl->no));
+#endif
+
+ if (!mca_fe_clocks_in_use(cl)) {
+ /*
+ * Set up FSYNC duty cycle as even as possible.
+ */
+ writel_relaxed((bclk_ratio / 2) - 1,
+ cl->base + REG_SYNCGEN_HI_PERIOD);
+ writel_relaxed(((bclk_ratio + 1) / 2) - 1,
+ cl->base + REG_SYNCGEN_LO_PERIOD);
+ writel_relaxed(FIELD_PREP(MCLK_CONF_DIV, 0x1),
+ cl->base + REG_MCLK_CONF);
+
+ ret = clk_set_rate(cl->clk_parent, bclk_ratio * samp_rate);
+ if (ret) {
+ dev_err(mca->dev, "cluster %d: unable to set clock parent: %d\n",
+ cl->no, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mca_fe_ops = {
+ .startup = mca_fe_startup,
+ .set_fmt = mca_fe_set_fmt,
+ .set_bclk_ratio = mca_set_bclk_ratio,
+ .set_tdm_slot = mca_fe_set_tdm_slot,
+ .hw_params = mca_fe_hw_params,
+ .trigger = mca_fe_trigger,
+};
+
+static bool mca_be_started(struct mca_cluster *cl)
+{
+ int stream;
+
+ for_each_pcm_streams(stream)
+ if (cl->port_started[stream])
+ return true;
+ return false;
+}
+
+static int mca_be_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *fe;
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_cluster *fe_cl;
+ struct mca_data *mca = cl->host;
+ struct snd_soc_dpcm *dpcm;
+
+ fe = NULL;
+
+ for_each_dpcm_fe(be, substream->stream, dpcm) {
+ if (fe && dpcm->fe != fe) {
+ dev_err(mca->dev, "many FE per one BE unsupported\n");
+ return -EINVAL;
+ }
+
+ fe = dpcm->fe;
+ }
+
+ if (!fe)
+ return -EINVAL;
+
+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0));
+
+ if (mca_be_started(cl)) {
+ /*
+ * Port is already started in the other direction.
+ * Make sure there isn't a conflict with another cluster
+ * driving the port.
+ */
+ if (cl->port_driver != fe_cl->no)
+ return -EINVAL;
+
+ cl->port_started[substream->stream] = true;
+ return 0;
+ }
+
+ writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA,
+ cl->base + REG_PORT_ENABLES);
+ writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1),
+ cl->base + REG_PORT_CLOCK_SEL);
+ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no),
+ cl->base + REG_PORT_DATA_SEL);
+ mutex_lock(&mca->port_mutex);
+ cl->port_driver = fe_cl->no;
+ mutex_unlock(&mca->port_mutex);
+ cl->port_started[substream->stream] = true;
+
+ return 0;
+}
+
+static void mca_be_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+
+ cl->port_started[substream->stream] = false;
+
+ if (!mca_be_started(cl)) {
+ /*
+ * Were we the last direction to shutdown?
+ * Turn off the lights.
+ */
+ writel_relaxed(0, cl->base + REG_PORT_ENABLES);
+ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL);
+ mutex_lock(&mca->port_mutex);
+ cl->port_driver = -1;
+ mutex_unlock(&mca->port_mutex);
+ }
+}
+
+static const struct snd_soc_dai_ops mca_be_ops = {
+ .prepare = mca_be_prepare,
+ .hw_free = mca_be_hw_free,
+ .startup = mca_be_startup,
+ .shutdown = mca_be_shutdown,
+};
+
+static int mca_set_runtime_hwparams(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct dma_chan *chan)
+{
+ struct device *dma_dev = chan->device->dev;
+ struct snd_dmaengine_dai_dma_data dma_data = {};
+ int ret;
+
+ struct snd_pcm_hardware hw;
+
+ memset(&hw, 0, sizeof(hw));
+
+ hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED;
+ hw.periods_min = 2;
+ hw.periods_max = UINT_MAX;
+ hw.period_bytes_min = 256;
+ hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
+ hw.buffer_bytes_max = SIZE_MAX;
+ hw.fifo_size = 16;
+
+ ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, &dma_data,
+ &hw, chan);
+
+ if (ret)
+ return ret;
+
+ return snd_soc_set_runtime_hwparams(substream, &hw);
+}
+
+static int mca_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mca_cluster *cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(rtd, 0));
+ struct dma_chan *chan = cl->dma_chans[substream->stream];
+ int ret;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ ret = mca_set_runtime_hwparams(component, substream, chan);
+ if (ret)
+ return ret;
+
+ return snd_dmaengine_pcm_open(substream, chan);
+}
+
+static int mca_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct dma_slave_config slave_config;
+ int ret;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ memset(&slave_config, 0, sizeof(slave_config));
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ &slave_config);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ slave_config.dst_port_window_size =
+ min_t(u32, params_channels(params), 4);
+ else
+ slave_config.src_port_window_size =
+ min_t(u32, params_channels(params), 4);
+
+ return dmaengine_slave_config(chan, &slave_config);
+}
+
+static int mca_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ return snd_dmaengine_pcm_close(substream);
+}
+
+static int mca_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ /*
+ * Before we do the PCM trigger proper, insert an opportunity
+ * to reset the frontend's SERDES.
+ */
+ mca_fe_early_trigger(substream, cmd, snd_soc_rtd_to_cpu(rtd, 0));
+
+ return snd_dmaengine_pcm_trigger(substream, cmd);
+}
+
+static snd_pcm_uframes_t mca_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return -ENOTSUPP;
+
+ return snd_dmaengine_pcm_pointer(substream);
+}
+
+static struct dma_chan *mca_request_dma_channel(struct mca_cluster *cl, unsigned int stream)
+{
+ bool is_tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+#ifndef USE_RXB_FOR_CAPTURE
+ char *name = devm_kasprintf(cl->host->dev, GFP_KERNEL,
+ is_tx ? "tx%da" : "rx%da", cl->no);
+#else
+ char *name = devm_kasprintf(cl->host->dev, GFP_KERNEL,
+ is_tx ? "tx%da" : "rx%db", cl->no);
+#endif
+ return of_dma_request_slave_channel(cl->host->dev->of_node, name);
+
+}
+
+static void mca_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_chip(pcm);
+ struct mca_cluster *cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(rtd, 0));
+ unsigned int i;
+
+ if (rtd->dai_link->no_pcm)
+ return;
+
+ for_each_pcm_streams(i) {
+ struct snd_pcm_substream *substream =
+ rtd->pcm->streams[i].substream;
+
+ if (!substream || !cl->dma_chans[i])
+ continue;
+
+ dma_release_channel(cl->dma_chans[i]);
+ cl->dma_chans[i] = NULL;
+ }
+}
+
+
+static int mca_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(rtd, 0));
+ unsigned int i;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ for_each_pcm_streams(i) {
+ struct snd_pcm_substream *substream =
+ rtd->pcm->streams[i].substream;
+ struct dma_chan *chan;
+
+ if (!substream)
+ continue;
+
+ chan = mca_request_dma_channel(cl, i);
+
+ if (IS_ERR_OR_NULL(chan)) {
+ mca_pcm_free(component, rtd->pcm);
+
+ if (chan && PTR_ERR(chan) == -EPROBE_DEFER)
+ return PTR_ERR(chan);
+
+ dev_err(component->dev, "unable to obtain DMA channel (stream %d cluster %d): %pe\n",
+ i, cl->no, chan);
+
+ if (!chan)
+ return -EINVAL;
+ return PTR_ERR(chan);
+ }
+
+ cl->dma_chans[i] = chan;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_IRAM,
+ chan->device->dev, 512 * 1024 * 6,
+ SIZE_MAX);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mca_component = {
+ .name = "apple-mca",
+ .open = mca_pcm_open,
+ .close = mca_close,
+ .hw_params = mca_hw_params,
+ .trigger = mca_trigger,
+ .pointer = mca_pointer,
+ .pcm_construct = mca_pcm_new,
+ .pcm_destruct = mca_pcm_free,
+};
+
+static void apple_mca_release(struct mca_data *mca)
+{
+ int i;
+
+ for (i = 0; i < mca->nclusters; i++) {
+ struct mca_cluster *cl = &mca->clusters[i];
+
+ if (!IS_ERR_OR_NULL(cl->clk_parent))
+ clk_put(cl->clk_parent);
+
+ if (!IS_ERR_OR_NULL(cl->pd_dev))
+ dev_pm_domain_detach(cl->pd_dev, true);
+ }
+
+ if (mca->pd_link)
+ device_link_del(mca->pd_link);
+
+ if (!IS_ERR_OR_NULL(mca->pd_dev))
+ dev_pm_domain_detach(mca->pd_dev, true);
+
+ reset_control_rearm(mca->rstc);
+}
+
+static int apple_mca_probe(struct platform_device *pdev)
+{
+ struct mca_data *mca;
+ struct mca_cluster *clusters;
+ struct snd_soc_dai_driver *dai_drivers;
+ struct resource *res;
+ void __iomem *base;
+ int nclusters;
+ int ret, i;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (resource_size(res) < CLUSTER_STRIDE)
+ return -EINVAL;
+ nclusters = (resource_size(res) - CLUSTER_STRIDE) / CLUSTER_STRIDE + 1;
+
+ mca = devm_kzalloc(&pdev->dev, struct_size(mca, clusters, nclusters),
+ GFP_KERNEL);
+ if (!mca)
+ return -ENOMEM;
+ mca->dev = &pdev->dev;
+ mca->nclusters = nclusters;
+ mutex_init(&mca->port_mutex);
+ platform_set_drvdata(pdev, mca);
+ clusters = mca->clusters;
+
+ mca->switch_base =
+ devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(mca->switch_base))
+ return PTR_ERR(mca->switch_base);
+
+ mca->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(mca->rstc))
+ return PTR_ERR(mca->rstc);
+
+ dai_drivers = devm_kzalloc(
+ &pdev->dev, sizeof(*dai_drivers) * 2 * nclusters, GFP_KERNEL);
+ if (!dai_drivers)
+ return -ENOMEM;
+
+ mca->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, 0);
+ if (IS_ERR(mca->pd_dev))
+ return -EINVAL;
+
+ mca->pd_link = device_link_add(&pdev->dev, mca->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!mca->pd_link) {
+ ret = -EINVAL;
+ /* Prevent an unbalanced reset rearm */
+ mca->rstc = NULL;
+ goto err_release;
+ }
+
+ reset_control_reset(mca->rstc);
+
+ for (i = 0; i < nclusters; i++) {
+ struct mca_cluster *cl = &clusters[i];
+ struct snd_soc_dai_driver *fe =
+ &dai_drivers[mca->nclusters + i];
+ struct snd_soc_dai_driver *be = &dai_drivers[i];
+
+ cl->host = mca;
+ cl->no = i;
+ cl->base = base + CLUSTER_STRIDE * i;
+ cl->port_driver = -1;
+ cl->clk_parent = of_clk_get(pdev->dev.of_node, i);
+ if (IS_ERR(cl->clk_parent)) {
+ dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n",
+ i, PTR_ERR(cl->clk_parent));
+ ret = PTR_ERR(cl->clk_parent);
+ goto err_release;
+ }
+ cl->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i + 1);
+ if (IS_ERR(cl->pd_dev)) {
+ dev_err(&pdev->dev,
+ "unable to obtain cluster %d PD: %ld\n", i,
+ PTR_ERR(cl->pd_dev));
+ ret = PTR_ERR(cl->pd_dev);
+ goto err_release;
+ }
+
+ fe->id = i;
+ fe->name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-pcm-%d", i);
+ if (!fe->name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ fe->ops = &mca_fe_ops;
+ fe->playback.channels_min = 1;
+ fe->playback.channels_max = 32;
+ fe->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ fe->playback.formats = APPLE_MCA_FMTBITS;
+ fe->capture.channels_min = 1;
+ fe->capture.channels_max = 32;
+ fe->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ fe->capture.formats = APPLE_MCA_FMTBITS;
+ fe->symmetric_rate = 1;
+
+ fe->playback.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d TX", i);
+ fe->capture.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d RX", i);
+
+ if (!fe->playback.stream_name || !fe->capture.stream_name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ be->id = i + nclusters;
+ be->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-i2s-%d", i);
+ if (!be->name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ be->ops = &mca_be_ops;
+ be->playback.channels_min = 1;
+ be->playback.channels_max = 32;
+ be->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ be->playback.formats = APPLE_MCA_FMTBITS;
+ be->capture.channels_min = 1;
+ be->capture.channels_max = 32;
+ be->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ be->capture.formats = APPLE_MCA_FMTBITS;
+
+ be->playback.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d TX", i);
+ be->capture.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d RX", i);
+ if (!be->playback.stream_name || !be->capture.stream_name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ }
+
+ ret = snd_soc_register_component(&pdev->dev, &mca_component,
+ dai_drivers, nclusters * 2);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register ASoC component: %d\n",
+ ret);
+ goto err_release;
+ }
+
+ return 0;
+
+err_release:
+ apple_mca_release(mca);
+ return ret;
+}
+
+static void apple_mca_remove(struct platform_device *pdev)
+{
+ struct mca_data *mca = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_component(&pdev->dev);
+ apple_mca_release(mca);
+}
+
+static const struct of_device_id apple_mca_of_match[] = {
+ { .compatible = "apple,t8103-mca", },
+ { .compatible = "apple,mca", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, apple_mca_of_match);
+
+static struct platform_driver apple_mca_driver = {
+ .driver = {
+ .name = "apple-mca",
+ .of_match_table = apple_mca_of_match,
+ },
+ .probe = apple_mca_probe,
+ .remove = apple_mca_remove,
+};
+module_platform_driver(apple_mca_driver);
+
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_DESCRIPTION("ASoC Apple MCA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 06e099e802df..4f51612f3dd2 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -1,41 +1,43 @@
-config SND_ATMEL_SOC
- tristate "SoC Audio for the Atmel System-on-Chip"
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Atmel"
depends on HAS_IOMEM
- help
- Say Y or M if you want to add support for codecs attached to
- the ATMEL SSC interface. You will also need
- to select the audio interfaces to support below.
-
-if SND_ATMEL_SOC
config SND_ATMEL_SOC_PDC
- tristate
- default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
- default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
-
-config SND_ATMEL_SOC_SSC_PDC
- tristate
+ bool
config SND_ATMEL_SOC_DMA
- tristate
+ bool
select SND_SOC_GENERIC_DMAENGINE_PCM
- default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m
- default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y)
-
-config SND_ATMEL_SOC_SSC_DMA
- tristate
config SND_ATMEL_SOC_SSC
tristate
- default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y
- default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m
+ select SND_ATMEL_SOC_DMA
+ select SND_ATMEL_SOC_PDC
+
+config SND_ATMEL_SOC_SSC_PDC
+ tristate "SoC PCM DAI support for AT91 SSC controller using PDC"
+ depends on ATMEL_SSC
+ select SND_ATMEL_SOC_PDC
+ select SND_ATMEL_SOC_SSC
+ help
+ Say Y or M if you want to add support for Atmel SSC interface
+ in PDC mode configured using audio-graph-card in device-tree.
+
+config SND_ATMEL_SOC_SSC_DMA
+ tristate "SoC PCM DAI support for AT91 SSC controller using DMA"
+ depends on ATMEL_SSC
+ select SND_ATMEL_SOC_DMA
+ select SND_ATMEL_SOC_SSC
+ help
+ Say Y or M if you want to add support for Atmel SSC interface
+ in DMA mode configured using audio-graph-card in device-tree.
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
depends on ARCH_AT91 || COMPILE_TEST
- depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+ depends on ATMEL_SSC && I2C
select SND_ATMEL_SOC_SSC_PDC
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Say Y if you want to add support for SoC audio on WM8731-based
AT91sam9g20 evaluation board.
@@ -63,7 +65,7 @@ config SND_AT91_SOC_SAM9X5_WM8731
config SND_ATMEL_SOC_CLASSD
tristate "Atmel ASoC driver for boards using CLASSD"
depends on ARCH_AT91 || COMPILE_TEST
- select SND_ATMEL_SOC_DMA
+ select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y if you want to add support for Atmel ASoC driver for boards using
@@ -77,4 +79,94 @@ config SND_ATMEL_SOC_PDMIC
help
Say Y if you want to add support for Atmel ASoC driver for boards using
PDMIC.
-endif
+
+config SND_ATMEL_SOC_TSE850_PCM5142
+ tristate "ASoC driver for the Axentia TSE-850"
+ depends on ARCH_AT91 && OF
+ depends on ATMEL_SSC && I2C
+ select SND_ATMEL_SOC_SSC_DMA
+ select SND_SOC_PCM512x_I2C
+ help
+ Say Y if you want to add support for the ASoC driver for the
+ Axentia TSE-850 with a PCM5142 codec.
+
+config SND_ATMEL_SOC_I2S
+ tristate "Atmel ASoC driver for boards using I2S"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Atmel ASoc driver for boards
+ using I2S.
+
+config SND_SOC_MIKROE_PROTO
+ tristate "Support for Mikroe-PROTO board"
+ depends on OF
+ depends on SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8731
+ help
+ Say Y or M if you want to add support for MikroElektronika PROTO Audio
+ Board. This board contains the WM8731 codec, which can be configured
+ using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins.
+ Both playback and capture are supported.
+
+config SND_MCHP_SOC_I2S_MCC
+ tristate "Microchip ASoC driver for boards using I2S MCC"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for I2S Multi-Channel ASoC
+ driver on the following Microchip platforms:
+ - sam9x60
+ - sama7g5
+
+ The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
+ and supports a Time Division Multiplexed (TDM) interface with
+ external multi-channel audio codecs.
+ Starting with sama7g5, I2S and Left-Justified multi-channel is
+ supported by using multiple data pins, output and input, without TDM.
+
+config SND_MCHP_SOC_SPDIFTX
+ tristate "Microchip ASoC driver for boards using S/PDIF TX"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Microchip S/PDIF TX ASoc
+ driver on the following Microchip platforms:
+ - sama7g5
+
+ This S/PDIF TX driver is compliant with IEC-60958 standard and
+ includes programmable User Data and Channel Status fields.
+
+config SND_MCHP_SOC_SPDIFRX
+ tristate "Microchip ASoC driver for boards using S/PDIF RX"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ depends on COMMON_CLK
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Microchip S/PDIF RX ASoc
+ driver on the following Microchip platforms:
+ - sama7g5
+
+ This S/PDIF RX driver is compliant with IEC-60958 standard and
+ includes programmable User Data and Channel Status fields.
+
+config SND_MCHP_SOC_PDMC
+ tristate "Microchip ASoC driver for boards using PDMC"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for Microchip ASoC PDMC driver on the
+ following Microchip platforms:
+ - sama7g5
+
+ The Pulse Density Microphone Controller (PDMC) interfaces up to 4 digital
+ microphones PDM outputs. It generates a single clock line and samples 1 or
+ 2 data lines. The signal path includes an audio grade programmable
+ decimation filter and outputs 24-bit audio words.
+
+endmenu
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index a2b127bd9c87..03d9c419c93f 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -1,21 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0
# AT91 Platform Support
-snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
-snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
-snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+snd-soc-atmel-pcm-pdc-y := atmel-pcm-pdc.o
+snd-soc-atmel-pcm-dma-y := atmel-pcm-dma.o
+snd-soc-atmel_ssc_dai-y := atmel_ssc_dai.o
+snd-soc-atmel-i2s-y := atmel-i2s.o
+snd-soc-mchp-i2s-mcc-y := mchp-i2s-mcc.o
+snd-soc-mchp-spdiftx-y := mchp-spdiftx.o
+snd-soc-mchp-spdifrx-y := mchp-spdifrx.o
+snd-soc-mchp-pdmc-y := mchp-pdmc.o
-obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
-obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
+# pdc and dma need to both be built-in if any user of
+# ssc is built-in.
+ifdef CONFIG_SND_ATMEL_SOC_PDC
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel-pcm-pdc.o
+endif
+ifdef CONFIG_SND_ATMEL_SOC_DMA
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel-pcm-dma.o
+endif
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
+obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
+obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o
+obj-$(CONFIG_SND_MCHP_SOC_SPDIFRX) += snd-soc-mchp-spdifrx.o
+obj-$(CONFIG_SND_MCHP_SOC_PDMC) += snd-soc-mchp-pdmc.o
# AT91 Machine Support
-snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
-snd-atmel-soc-wm8904-objs := atmel_wm8904.o
-snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
-snd-atmel-soc-classd-objs := atmel-classd.o
-snd-atmel-soc-pdmic-objs := atmel-pdmic.o
+snd-soc-sam9g20-wm8731-y := sam9g20_wm8731.o
+snd-atmel-soc-wm8904-y := atmel_wm8904.o
+snd-soc-sam9x5-wm8731-y := sam9x5_wm8731.o
+snd-atmel-soc-classd-y := atmel-classd.o
+snd-atmel-soc-pdmic-y := atmel-pdmic.o
+snd-atmel-soc-tse850-pcm5142-y := tse850-pcm5142.o
+snd-soc-mikroe-proto-y := mikroe-proto.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
+obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o
+obj-$(CONFIG_SND_SOC_MIKROE_PROTO) += snd-soc-mikroe-proto.o
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 6107de9c538b..1f8c60d2de82 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
*
* Copyright (C) 2015 Atmel
*
* Author: Songjun Wu <songjun.wu@atmel.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 or later
- * as published by the Free Software Foundation.
*/
#include <linux/of.h>
@@ -14,6 +11,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -32,7 +30,7 @@ struct atmel_classd {
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
- struct clk *aclk;
+ struct device *dev;
int irq;
const struct atmel_classd_pdata *pdata;
};
@@ -51,7 +49,7 @@ static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
{
struct device_node *np = dev->of_node;
struct atmel_classd_pdata *pdata;
- const char *pwm_type;
+ const char *pwm_type_s;
int ret;
if (!np) {
@@ -63,8 +61,8 @@ static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
if (!pdata)
return ERR_PTR(-ENOMEM);
- ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
- if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
+ ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type_s);
+ if ((ret == 0) && (strcmp(pwm_type_s, "diff") == 0))
pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
else
pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
@@ -121,52 +119,34 @@ static const struct snd_pcm_hardware atmel_classd_hw = {
static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ int err;
regmap_write(dd->regmap, CLASSD_THR, 0x0);
- return clk_prepare_enable(dd->pclk);
-}
-
-static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
-
- clk_disable_unprepare(dd->pclk);
+ err = clk_prepare_enable(dd->pclk);
+ if (err)
+ return err;
+ err = clk_prepare_enable(dd->gclk);
+ if (err) {
+ clk_disable_unprepare(dd->pclk);
+ return err;
+ }
+ return 0;
}
-static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
- .startup = atmel_classd_cpu_dai_startup,
- .shutdown = atmel_classd_cpu_dai_shutdown,
-};
-
-static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_CLASSD_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &atmel_classd_cpu_dai_ops,
-};
-
-static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
- .name = "atmel-classd",
-};
-
/* platform */
static int
atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct dma_slave_config *slave_config)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
if (params_physical_width(params) != 16) {
- dev_err(rtd->platform->dev,
+ dev_err(dd->dev,
"only supports 16-bit audio data\n");
return -EINVAL;
}
@@ -248,9 +228,9 @@ static const char * const pwm_type[] = {
"Single ended", "Differential"
};
-static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
+static int atmel_classd_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
const struct atmel_classd_pdata *pdata = dd->pdata;
u32 mask, val;
@@ -284,54 +264,35 @@ static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
default:
val |= (CLASSD_MR_NOVR_VAL_10NS
<< CLASSD_MR_NOVR_VAL_SHIFT);
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"non-overlapping value %d is invalid, the default value 10 is specified\n",
pdata->non_overlap_time);
break;
}
}
- snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
- dev_info(codec->dev,
+ dev_info(component->dev,
"PWM modulation type is %s, non-overlapping is %s\n",
pwm_type[pdata->pwm_type],
- pdata->non_overlap_enable?"enabled":"disabled");
+ str_enabled_disabled(pdata->non_overlap_enable));
return 0;
}
-static struct regmap *atmel_classd_codec_get_remap(struct device *dev)
+static int atmel_classd_component_resume(struct snd_soc_component *component)
{
- return dev_get_regmap(dev, NULL);
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_classd = {
- .probe = atmel_classd_codec_probe,
- .controls = atmel_classd_snd_controls,
- .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
- .get_regmap = atmel_classd_codec_get_remap,
-};
-
-/* codec dai component */
-static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
- int ret;
-
- ret = clk_prepare_enable(dd->aclk);
- if (ret)
- return ret;
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
- return clk_prepare_enable(dd->gclk);
+ return regcache_sync(dd->regmap);
}
-static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
- int mute)
+static int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai,
+ int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
u32 mask, val;
mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
@@ -341,46 +302,46 @@ static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
else
val = 0;
- snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
return 0;
}
-#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
-#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
+#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_GCLK_RATE_12M288_MPY_8 (12288 * 1000 * 8)
static struct {
int rate;
int sample_rate;
int dsp_clk;
- unsigned long aclk_rate;
+ unsigned long gclk_rate;
} const sample_rates[] = {
{ 8000, CLASSD_INTPMR_FRAME_8K,
- CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
{ 16000, CLASSD_INTPMR_FRAME_16K,
- CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
{ 32000, CLASSD_INTPMR_FRAME_32K,
- CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
{ 48000, CLASSD_INTPMR_FRAME_48K,
- CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
{ 96000, CLASSD_INTPMR_FRAME_96K,
- CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
{ 22050, CLASSD_INTPMR_FRAME_22K,
- CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
{ 44100, CLASSD_INTPMR_FRAME_44K,
- CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
{ 88200, CLASSD_INTPMR_FRAME_88K,
- CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
};
static int
-atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *codec_dai)
+atmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
int fs;
int i, best, best_val, cur_val, ret;
u32 mask, val;
@@ -398,14 +359,13 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
}
}
- dev_dbg(codec->dev,
- "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
- sample_rates[best].rate, sample_rates[best].aclk_rate);
+ dev_dbg(component->dev,
+ "Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
+ sample_rates[best].rate, sample_rates[best].gclk_rate);
clk_disable_unprepare(dd->gclk);
- clk_disable_unprepare(dd->aclk);
- ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+ ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
if (ret)
return ret;
@@ -413,32 +373,27 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
| (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
- snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
-
- ret = clk_prepare_enable(dd->aclk);
- if (ret)
- return ret;
+ snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val);
return clk_prepare_enable(dd->gclk);
}
static void
-atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
+atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
clk_disable_unprepare(dd->gclk);
- clk_disable_unprepare(dd->aclk);
}
-static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
+static int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
- snd_soc_update_bits(codec, CLASSD_MR,
+ snd_soc_component_update_bits(component, CLASSD_MR,
CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
(CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
|(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
@@ -446,10 +401,10 @@ static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
return 0;
}
-static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *codec_dai)
+static int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
u32 mask, val;
mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
@@ -470,24 +425,22 @@ static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+ snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
return 0;
}
-static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = {
- .digital_mute = atmel_classd_codec_dai_digital_mute,
- .startup = atmel_classd_codec_dai_startup,
- .shutdown = atmel_classd_codec_dai_shutdown,
- .hw_params = atmel_classd_codec_dai_hw_params,
- .prepare = atmel_classd_codec_dai_prepare,
- .trigger = atmel_classd_codec_dai_trigger,
+static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
+ .startup = atmel_classd_cpu_dai_startup,
+ .shutdown = atmel_classd_cpu_dai_shutdown,
+ .mute_stream = atmel_classd_cpu_dai_mute_stream,
+ .hw_params = atmel_classd_cpu_dai_hw_params,
+ .prepare = atmel_classd_cpu_dai_prepare,
+ .trigger = atmel_classd_cpu_dai_trigger,
+ .no_capture_mute = 1,
};
-#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
-
-static struct snd_soc_dai_driver atmel_classd_codec_dai = {
- .name = ATMEL_CLASSD_CODEC_DAI_NAME,
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -495,7 +448,18 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = {
.rates = ATMEL_CLASSD_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &atmel_classd_codec_dai_ops,
+ .ops = &atmel_classd_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
+ .name = "atmel-classd",
+ .probe = atmel_classd_component_probe,
+ .resume = atmel_classd_component_resume,
+ .controls = atmel_classd_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .legacy_dai_naming = 1,
};
/* ASoC sound card */
@@ -504,17 +468,28 @@ static int atmel_classd_asoc_card_init(struct device *dev,
{
struct snd_soc_dai_link *dai_link;
struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link_component *comp;
dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
if (!dai_link)
return -ENOMEM;
+ comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai_link->cpus = &comp[0];
+ dai_link->codecs = &snd_soc_dummy_dlc;
+ dai_link->platforms = &comp[1];
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+ dai_link->num_platforms = 1;
+
dai_link->name = "CLASSD";
dai_link->stream_name = "CLASSD PCM";
- dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME;
- dai_link->cpu_dai_name = dev_name(dev);
- dai_link->codec_name = dev_name(dev);
- dai_link->platform_name = dev_name(dev);
+ dai_link->cpus->dai_name = dev_name(dev);
+ dai_link->platforms->name = dev_name(dev);
card->dai_link = dai_link;
card->num_links = 1;
@@ -565,11 +540,8 @@ static int atmel_classd_probe(struct platform_device *pdev)
dd->pdata = pdata;
dd->irq = platform_get_irq(pdev, 0);
- if (dd->irq < 0) {
- ret = dd->irq;
- dev_err(dev, "failed to could not get irq: %d\n", ret);
- return ret;
- }
+ if (dd->irq < 0)
+ return dd->irq;
dd->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dd->pclk)) {
@@ -585,27 +557,12 @@ static int atmel_classd_probe(struct platform_device *pdev)
return ret;
}
- dd->aclk = devm_clk_get(dev, "aclk");
- if (IS_ERR(dd->aclk)) {
- ret = PTR_ERR(dd->aclk);
- dev_err(dev, "failed to get audio clock: %d\n", ret);
- return ret;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no memory resource\n");
- return -ENXIO;
- }
-
- io_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(io_base)) {
- ret = PTR_ERR(io_base);
- dev_err(dev, "failed to remap register memory: %d\n", ret);
- return ret;
- }
+ io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
dd->phy_base = res->start;
+ dd->dev = dev;
dd->regmap = devm_regmap_init_mmio(dev, io_base,
&atmel_classd_regmap_config);
@@ -631,13 +588,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
return ret;
}
- ret = snd_soc_register_codec(dev, &soc_codec_dev_classd,
- &atmel_classd_codec_dai, 1);
- if (ret) {
- dev_err(dev, "could not register codec: %d\n", ret);
- return ret;
- }
-
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card) {
@@ -646,7 +596,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
}
snd_soc_card_set_drvdata(card, dd);
- platform_set_drvdata(pdev, card);
ret = atmel_classd_asoc_card_init(dev, card);
if (ret) {
@@ -663,16 +612,9 @@ static int atmel_classd_probe(struct platform_device *pdev)
return 0;
unregister_codec:
- snd_soc_unregister_codec(dev);
return ret;
}
-static int atmel_classd_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver atmel_classd_driver = {
.driver = {
.name = "atmel-classd",
@@ -680,7 +622,6 @@ static struct platform_driver atmel_classd_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = atmel_classd_probe,
- .remove = atmel_classd_remove,
};
module_platform_driver(atmel_classd_driver);
diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h
index 73f8fdd1ca83..0f2e25aeb458 100644
--- a/sound/soc/atmel/atmel-classd.h
+++ b/sound/soc/atmel/atmel-classd.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ATMEL_CLASSD_H_
#define __ATMEL_CLASSD_H_
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
new file mode 100644
index 000000000000..762199faf872
--- /dev/null
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Atmel I2S controller
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define ATMEL_I2SC_MAX_TDM_CHANNELS 8
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define ATMEL_I2SC_CR 0x0000 /* Control Register */
+#define ATMEL_I2SC_MR 0x0004 /* Mode Register */
+#define ATMEL_I2SC_SR 0x0008 /* Status Register */
+#define ATMEL_I2SC_SCR 0x000c /* Status Clear Register */
+#define ATMEL_I2SC_SSR 0x0010 /* Status Set Register */
+#define ATMEL_I2SC_IER 0x0014 /* Interrupt Enable Register */
+#define ATMEL_I2SC_IDR 0x0018 /* Interrupt Disable Register */
+#define ATMEL_I2SC_IMR 0x001c /* Interrupt Mask Register */
+#define ATMEL_I2SC_RHR 0x0020 /* Receiver Holding Register */
+#define ATMEL_I2SC_THR 0x0024 /* Transmitter Holding Register */
+#define ATMEL_I2SC_VERSION 0x0028 /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define ATMEL_I2SC_CR_RXEN BIT(0) /* Receiver Enable */
+#define ATMEL_I2SC_CR_RXDIS BIT(1) /* Receiver Disable */
+#define ATMEL_I2SC_CR_CKEN BIT(2) /* Clock Enable */
+#define ATMEL_I2SC_CR_CKDIS BIT(3) /* Clock Disable */
+#define ATMEL_I2SC_CR_TXEN BIT(4) /* Transmitter Enable */
+#define ATMEL_I2SC_CR_TXDIS BIT(5) /* Transmitter Disable */
+#define ATMEL_I2SC_CR_SWRST BIT(7) /* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+#define ATMEL_I2SC_MR_MODE_MASK GENMASK(0, 0)
+#define ATMEL_I2SC_MR_MODE_SLAVE (0 << 0)
+#define ATMEL_I2SC_MR_MODE_MASTER (1 << 0)
+
+#define ATMEL_I2SC_MR_DATALENGTH_MASK GENMASK(4, 2)
+#define ATMEL_I2SC_MR_DATALENGTH_32_BITS (0 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_24_BITS (1 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_20_BITS (2 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_18_BITS (3 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS (4 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS_COMPACT (5 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS (6 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS_COMPACT (7 << 2)
+
+#define ATMEL_I2SC_MR_FORMAT_MASK GENMASK(7, 6)
+#define ATMEL_I2SC_MR_FORMAT_I2S (0 << 6)
+#define ATMEL_I2SC_MR_FORMAT_LJ (1 << 6) /* Left Justified */
+#define ATMEL_I2SC_MR_FORMAT_TDM (2 << 6)
+#define ATMEL_I2SC_MR_FORMAT_TDMLJ (3 << 6)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_RXMONO BIT(8)
+
+/* Receiver uses one DMA channel ... */
+#define ATMEL_I2SC_MR_RXDMA_MASK GENMASK(9, 9)
+#define ATMEL_I2SC_MR_RXDMA_SINGLE (0 << 9) /* for all audio channels */
+#define ATMEL_I2SC_MR_RXDMA_MULTIPLE (1 << 9) /* per audio channel */
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define ATMEL_I2SC_MR_RXLOOP BIT(10)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_TXMONO BIT(12)
+
+/* Transmitter uses one DMA channel ... */
+#define ATMEL_I2SC_MR_TXDMA_MASK GENMASK(13, 13)
+#define ATMEL_I2SC_MR_TXDMA_SINGLE (0 << 13) /* for all audio channels */
+#define ATMEL_I2SC_MR_TXDME_MULTIPLE (1 << 13) /* per audio channel */
+
+/* x sample transmitted when underrun */
+#define ATMEL_I2SC_MR_TXSAME_MASK GENMASK(14, 14)
+#define ATMEL_I2SC_MR_TXSAME_ZERO (0 << 14) /* Zero sample */
+#define ATMEL_I2SC_MR_TXSAME_PREVIOUS (1 << 14) /* Previous sample */
+
+/* Audio Clock to I2SC Master Clock ratio */
+#define ATMEL_I2SC_MR_IMCKDIV_MASK GENMASK(21, 16)
+#define ATMEL_I2SC_MR_IMCKDIV(div) \
+ (((div) << 16) & ATMEL_I2SC_MR_IMCKDIV_MASK)
+
+/* Master Clock to fs ratio */
+#define ATMEL_I2SC_MR_IMCKFS_MASK GENMASK(29, 24)
+#define ATMEL_I2SC_MR_IMCKFS(fs) \
+ (((fs) << 24) & ATMEL_I2SC_MR_IMCKFS_MASK)
+
+/* Master Clock mode */
+#define ATMEL_I2SC_MR_IMCKMODE_MASK GENMASK(30, 30)
+/* 0: No master clock generated (selected clock drives I2SCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SCK (0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SMCK (1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define ATMEL_I2SC_MR_IWS BIT(31)
+
+/*
+ * ---- Status Registers ----
+ */
+#define ATMEL_I2SC_SR_RXEN BIT(0) /* Receiver Enabled */
+#define ATMEL_I2SC_SR_RXRDY BIT(1) /* Receive Ready */
+#define ATMEL_I2SC_SR_RXOR BIT(2) /* Receive Overrun */
+
+#define ATMEL_I2SC_SR_TXEN BIT(4) /* Transmitter Enabled */
+#define ATMEL_I2SC_SR_TXRDY BIT(5) /* Transmit Ready */
+#define ATMEL_I2SC_SR_TXUR BIT(6) /* Transmit Underrun */
+
+/* Receive Overrun Channel */
+#define ATMEL_I2SC_SR_RXORCH_MASK GENMASK(15, 8)
+#define ATMEL_I2SC_SR_RXORCH(ch) (1 << (((ch) & 0x7) + 8))
+
+/* Transmit Underrun Channel */
+#define ATMEL_I2SC_SR_TXURCH_MASK GENMASK(27, 20)
+#define ATMEL_I2SC_SR_TXURCH(ch) (1 << (((ch) & 0x7) + 20))
+
+/*
+ * ---- Interrupt Enable/Disable/Mask Registers ----
+ */
+#define ATMEL_I2SC_INT_RXRDY ATMEL_I2SC_SR_RXRDY
+#define ATMEL_I2SC_INT_RXOR ATMEL_I2SC_SR_RXOR
+#define ATMEL_I2SC_INT_TXRDY ATMEL_I2SC_SR_TXRDY
+#define ATMEL_I2SC_INT_TXUR ATMEL_I2SC_SR_TXUR
+
+static const struct regmap_config atmel_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_I2SC_VERSION,
+};
+
+struct atmel_i2s_gck_param {
+ int fs;
+ unsigned long mck;
+ int imckdiv;
+ int imckfs;
+};
+
+#define I2S_MCK_12M288 12288000UL
+#define I2S_MCK_11M2896 11289600UL
+#define I2S_MCK_6M144 6144000UL
+
+/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */
+static const struct atmel_i2s_gck_param gck_params[] = {
+ /* mck = 6.144Mhz */
+ { 8000, I2S_MCK_6M144, 1, 47}, /* mck = 768 fs */
+
+ /* mck = 12.288MHz */
+ { 16000, I2S_MCK_12M288, 1, 47}, /* mck = 768 fs */
+ { 24000, I2S_MCK_12M288, 3, 63}, /* mck = 512 fs */
+ { 32000, I2S_MCK_12M288, 3, 47}, /* mck = 384 fs */
+ { 48000, I2S_MCK_12M288, 7, 63}, /* mck = 256 fs */
+ { 64000, I2S_MCK_12M288, 7, 47}, /* mck = 192 fs */
+ { 96000, I2S_MCK_12M288, 7, 31}, /* mck = 128 fs */
+ {192000, I2S_MCK_12M288, 7, 15}, /* mck = 64 fs */
+
+ /* mck = 11.2896MHz */
+ { 11025, I2S_MCK_11M2896, 1, 63}, /* mck = 1024 fs */
+ { 22050, I2S_MCK_11M2896, 3, 63}, /* mck = 512 fs */
+ { 44100, I2S_MCK_11M2896, 7, 63}, /* mck = 256 fs */
+ { 88200, I2S_MCK_11M2896, 7, 31}, /* mck = 128 fs */
+ {176400, I2S_MCK_11M2896, 7, 15}, /* mck = 64 fs */
+};
+
+struct atmel_i2s_dev;
+
+struct atmel_i2s_caps {
+ int (*mck_init)(struct atmel_i2s_dev *, struct device_node *np);
+};
+
+struct atmel_i2s_dev {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct snd_dmaengine_dai_dma_data capture;
+ unsigned int fmt;
+ const struct atmel_i2s_gck_param *gck_param;
+ const struct atmel_i2s_caps *caps;
+ int clk_use_no;
+};
+
+static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id)
+{
+ struct atmel_i2s_dev *dev = dev_id;
+ unsigned int sr, imr, pending, ch, mask;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+ regmap_read(dev->regmap, ATMEL_I2SC_IMR, &imr);
+ pending = sr & imr;
+
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & ATMEL_I2SC_INT_RXOR) {
+ mask = ATMEL_I2SC_SR_RXOR;
+
+ for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+ if (sr & ATMEL_I2SC_SR_RXORCH(ch)) {
+ mask |= ATMEL_I2SC_SR_RXORCH(ch);
+ dev_err(dev->dev,
+ "RX overrun on channel %d\n", ch);
+ }
+ }
+ regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+ ret = IRQ_HANDLED;
+ }
+
+ if (pending & ATMEL_I2SC_INT_TXUR) {
+ mask = ATMEL_I2SC_SR_TXUR;
+
+ for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+ if (sr & ATMEL_I2SC_SR_TXURCH(ch)) {
+ mask |= ATMEL_I2SC_SR_TXURCH(ch);
+ dev_err(dev->dev,
+ "TX underrun on channel %d\n", ch);
+ }
+ }
+ regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+#define ATMEL_I2S_RATES SNDRV_PCM_RATE_8000_192000
+
+#define ATMEL_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int atmel_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev->fmt = fmt;
+ return 0;
+}
+
+static int atmel_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ unsigned int rhr, sr = 0;
+
+ if (is_playback) {
+ regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+ if (sr & ATMEL_I2SC_SR_RXRDY) {
+ /*
+ * The RX Ready flag should not be set. However if here,
+ * we flush (read) the Receive Holding Register to start
+ * from a clean state.
+ */
+ dev_dbg(dev->dev, "RXRDY is set\n");
+ regmap_read(dev->regmap, ATMEL_I2SC_RHR, &rhr);
+ }
+ }
+
+ return 0;
+}
+
+static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs)
+{
+ int i, best;
+
+ if (!dev->gclk) {
+ dev_err(dev->dev, "cannot generate the I2S Master Clock\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Find the best possible settings to generate the I2S Master Clock
+ * from the PLL Audio.
+ */
+ dev->gck_param = NULL;
+ best = INT_MAX;
+ for (i = 0; i < ARRAY_SIZE(gck_params); ++i) {
+ const struct atmel_i2s_gck_param *gck_param = &gck_params[i];
+ int val = abs(fs - gck_param->fs);
+
+ if (val < best) {
+ best = val;
+ dev->gck_param = gck_param;
+ }
+ }
+
+ return 0;
+}
+
+static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ unsigned int mr = 0, mr_mask;
+ int ret;
+
+ mr_mask = ATMEL_I2SC_MR_FORMAT_MASK | ATMEL_I2SC_MR_MODE_MASK |
+ ATMEL_I2SC_MR_DATALENGTH_MASK;
+ if (is_playback)
+ mr_mask |= ATMEL_I2SC_MR_TXMONO;
+ else
+ mr_mask |= ATMEL_I2SC_MR_RXMONO;
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mr |= ATMEL_I2SC_MR_FORMAT_I2S;
+ break;
+
+ default:
+ dev_err(dev->dev, "unsupported bus format\n");
+ return -EINVAL;
+ }
+
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* codec is slave, so cpu is master */
+ mr |= ATMEL_I2SC_MR_MODE_MASTER;
+ ret = atmel_i2s_get_gck_param(dev, params_rate(params));
+ if (ret)
+ return ret;
+ break;
+
+ case SND_SOC_DAIFMT_BC_FC:
+ /* codec is master, so cpu is slave */
+ mr |= ATMEL_I2SC_MR_MODE_SLAVE;
+ dev->gck_param = NULL;
+ break;
+
+ default:
+ dev_err(dev->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ switch (params_channels(params)) {
+ case 1:
+ if (is_playback)
+ mr |= ATMEL_I2SC_MR_TXMONO;
+ else
+ mr |= ATMEL_I2SC_MR_RXMONO;
+ break;
+ case 2:
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of audio channels\n");
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_8_BITS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_16_BITS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_18_BITS | ATMEL_I2SC_MR_IWS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_20_BITS | ATMEL_I2SC_MR_IWS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS | ATMEL_I2SC_MR_IWS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mr |= ATMEL_I2SC_MR_DATALENGTH_32_BITS;
+ break;
+
+ default:
+ dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
+}
+
+static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev,
+ bool enabled)
+{
+ unsigned int mr, mr_mask;
+ unsigned long gclk_rate;
+ int ret;
+
+ mr = 0;
+ mr_mask = (ATMEL_I2SC_MR_IMCKDIV_MASK |
+ ATMEL_I2SC_MR_IMCKFS_MASK |
+ ATMEL_I2SC_MR_IMCKMODE_MASK);
+
+ if (!enabled) {
+ /* Disable the I2S Master Clock generator. */
+ ret = regmap_write(dev->regmap, ATMEL_I2SC_CR,
+ ATMEL_I2SC_CR_CKDIS);
+ if (ret)
+ return ret;
+
+ /* Reset the I2S Master Clock generator settings. */
+ ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR,
+ mr_mask, mr);
+ if (ret)
+ return ret;
+
+ /* Disable/unprepare the PMC generated clock. */
+ clk_disable_unprepare(dev->gclk);
+
+ return 0;
+ }
+
+ if (!dev->gck_param)
+ return -EINVAL;
+
+ gclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1);
+
+ ret = clk_set_rate(dev->gclk, gclk_rate);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dev->gclk);
+ if (ret)
+ return ret;
+
+ /* Update the Mode Register to generate the I2S Master Clock. */
+ mr |= ATMEL_I2SC_MR_IMCKDIV(dev->gck_param->imckdiv);
+ mr |= ATMEL_I2SC_MR_IMCKFS(dev->gck_param->imckfs);
+ mr |= ATMEL_I2SC_MR_IMCKMODE_I2SMCK;
+ ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
+ if (ret)
+ return ret;
+
+ /* Finally enable the I2S Master Clock generator. */
+ return regmap_write(dev->regmap, ATMEL_I2SC_CR,
+ ATMEL_I2SC_CR_CKEN);
+}
+
+static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ bool is_master, mck_enabled;
+ unsigned int cr, mr;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cr = is_playback ? ATMEL_I2SC_CR_TXEN : ATMEL_I2SC_CR_RXEN;
+ mck_enabled = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cr = is_playback ? ATMEL_I2SC_CR_TXDIS : ATMEL_I2SC_CR_RXDIS;
+ mck_enabled = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Read the Mode Register to retrieve the master/slave state. */
+ err = regmap_read(dev->regmap, ATMEL_I2SC_MR, &mr);
+ if (err)
+ return err;
+ is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER;
+
+ /* If master starts, enable the audio clock. */
+ if (is_master && mck_enabled) {
+ if (!dev->clk_use_no) {
+ err = atmel_i2s_switch_mck_generator(dev, true);
+ if (err)
+ return err;
+ }
+ dev->clk_use_no++;
+ }
+
+ err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr);
+ if (err)
+ return err;
+
+ /* If master stops, disable the audio clock. */
+ if (is_master && !mck_enabled) {
+ if (dev->clk_use_no == 1) {
+ err = atmel_i2s_switch_mck_generator(dev, false);
+ if (err)
+ return err;
+ }
+ dev->clk_use_no--;
+ }
+
+ return err;
+}
+
+static int atmel_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
+ .probe = atmel_i2s_dai_probe,
+ .prepare = atmel_i2s_prepare,
+ .trigger = atmel_i2s_trigger,
+ .hw_params = atmel_i2s_hw_params,
+ .set_fmt = atmel_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver atmel_i2s_dai = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_I2S_RATES,
+ .formats = ATMEL_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_I2S_RATES,
+ .formats = ATMEL_I2S_FORMATS,
+ },
+ .ops = &atmel_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct snd_soc_component_driver atmel_i2s_component = {
+ .name = "atmel-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev,
+ struct device_node *np)
+{
+ struct clk *muxclk;
+ int err;
+
+ if (!dev->gclk)
+ return 0;
+
+ /* muxclk is optional, so we return error for probe defer only */
+ muxclk = devm_clk_get(dev->dev, "muxclk");
+ if (IS_ERR(muxclk)) {
+ err = PTR_ERR(muxclk);
+ if (err == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_dbg(dev->dev,
+ "failed to get the I2S clock control: %d\n", err);
+ return 0;
+ }
+
+ return clk_set_parent(muxclk, dev->gclk);
+}
+
+static const struct atmel_i2s_caps atmel_i2s_sama5d2_caps = {
+ .mck_init = atmel_i2s_sama5d2_mck_init,
+};
+
+static const struct of_device_id atmel_i2s_dt_ids[] = {
+ {
+ .compatible = "atmel,sama5d2-i2s",
+ .data = (void *)&atmel_i2s_sama5d2_caps,
+ },
+
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_i2s_dt_ids);
+
+static int atmel_i2s_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ struct atmel_i2s_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ int irq;
+ int err;
+ unsigned int pcm_flags = 0;
+ unsigned int version;
+
+ /* Get memory for driver data. */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* Get hardware capabilities. */
+ match = of_match_node(atmel_i2s_dt_ids, np);
+ if (match)
+ dev->caps = match->data;
+
+ /* Map I/O registers. */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &atmel_i2s_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Request IRQ. */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, atmel_i2s_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ /* Get the peripheral clock. */
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev,
+ "failed to get the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ /* Get audio clock to generate the I2S Master Clock (I2S_MCK) */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ /* Master Mode not supported */
+ dev->gclk = NULL;
+ }
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ /* Do hardware specific settings to initialize I2S_MCK generator */
+ if (dev->caps && dev->caps->mck_init) {
+ err = dev->caps->mck_init(dev, np);
+ if (err)
+ return err;
+ }
+
+ /* Enable the peripheral clock. */
+ err = clk_prepare_enable(dev->pclk);
+ if (err)
+ return err;
+
+ /* Get IP version. */
+ regmap_read(dev->regmap, ATMEL_I2SC_VERSION, &version);
+ dev_info(&pdev->dev, "hw version: %#x\n", version);
+
+ /* Enable error interrupts. */
+ regmap_write(dev->regmap, ATMEL_I2SC_IER,
+ ATMEL_I2SC_INT_RXOR | ATMEL_I2SC_INT_TXUR);
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &atmel_i2s_component,
+ &atmel_i2s_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ /* Prepare DMA config. */
+ dev->playback.addr = (dma_addr_t)mem->start + ATMEL_I2SC_THR;
+ dev->playback.maxburst = 1;
+ dev->capture.addr = (dma_addr_t)mem->start + ATMEL_I2SC_RHR;
+ dev->capture.maxburst = 1;
+
+ if (of_property_match_string(np, "dma-names", "rx-tx") == 0)
+ pcm_flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, pcm_flags);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ return 0;
+}
+
+static void atmel_i2s_remove(struct platform_device *pdev)
+{
+ struct atmel_i2s_dev *dev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(dev->pclk);
+}
+
+static struct platform_driver atmel_i2s_driver = {
+ .driver = {
+ .name = "atmel_i2s",
+ .of_match_table = atmel_i2s_dt_ids,
+ },
+ .probe = atmel_i2s_probe,
+ .remove = atmel_i2s_remove,
+};
+module_platform_driver(atmel_i2s_driver);
+
+MODULE_DESCRIPTION("Atmel I2S Controller driver");
+MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index dd57a9eac171..7306e04da513 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC.
*
@@ -8,20 +9,6 @@
* Based on atmel-pcm by:
* Sedji Gaouaou <sedji.gaouaou@atmel.com>
* Copyright 2008 Atmel
- *
- * 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>
@@ -31,7 +18,6 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/atmel-ssc.h>
-#include <linux/platform_data/dma-atmel.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -57,7 +43,7 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
.buffer_bytes_max = 512 * 1024,
};
-/**
+/*
* atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
*
* We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
@@ -66,10 +52,10 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
static void atmel_pcm_dma_irq(u32 ssc_sr,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pcm_dma_params *prtd;
- prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (ssc_sr & prtd->mask->ssc_error) {
if (snd_pcm_running(substream))
@@ -91,12 +77,12 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pcm_dma_params *prtd;
struct ssc_device *ssc;
int ret;
- prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
ssc = prtd->ssc;
ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
@@ -124,16 +110,11 @@ static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
int atmel_pcm_dma_platform_register(struct device *dev)
{
- return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, 0);
+ return devm_snd_dmaengine_pcm_register(dev,
+ &atmel_dmaengine_pcm_config, 0);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
-void atmel_pcm_dma_platform_unregister(struct device *dev)
-{
- snd_dmaengine_pcm_unregister(dev);
-}
-EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
-
MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
MODULE_DESCRIPTION("Atmel DMA based PCM module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
index da861b44413f..7db8df85c54f 100644
--- a/sound/soc/atmel/atmel-pcm-pdc.c
+++ b/sound/soc/atmel/atmel-pcm-pdc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
*
@@ -15,20 +16,6 @@
* Author: Nicolas Pitre
* Created: Nov 30, 2004
* Copyright: (C) 2004 MontaVista Software, 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; 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>
@@ -47,83 +34,21 @@
#include "atmel-pcm.h"
-static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
- int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = ATMEL_SSC_DMABUF_SIZE;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
- (void *)buf->area, (void *)(long)buf->addr, size);
-
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- return 0;
-}
-
-static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return remap_pfn_range(vma, vma->vm_start,
- substream->dma_buffer.addr >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int atmel_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
- ret = atmel_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n");
- ret = atmel_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, ATMEL_SSC_DMABUF_SIZE,
+ ATMEL_SSC_DMABUF_SIZE);
-static void atmel_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_coherent(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
+ return 0;
}
/*--------------------------------------------------------------------------*\
@@ -209,20 +134,18 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
/*--------------------------------------------------------------------------*\
* PCM operations
\*--------------------------------------------------------------------------*/
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int atmel_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
/* this may get called several times by oss emulation
* with different params */
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd->params = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
prtd->dma_buffer = runtime->dma_addr;
@@ -238,7 +161,8 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+static int atmel_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
struct atmel_pcm_dma_params *params = prtd->params;
@@ -252,7 +176,8 @@ static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+static int atmel_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
struct atmel_pcm_dma_params *params = prtd->params;
@@ -264,8 +189,8 @@ static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int atmel_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *rtd = substream->runtime;
struct atmel_runtime_data *prtd = rtd->private_data;
@@ -330,8 +255,8 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static snd_pcm_uframes_t atmel_pcm_pointer(
- struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd = runtime->private_data;
@@ -348,7 +273,8 @@ static snd_pcm_uframes_t atmel_pcm_pointer(
return x;
}
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
+static int atmel_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct atmel_runtime_data *prtd;
@@ -373,7 +299,8 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int atmel_pcm_close(struct snd_pcm_substream *substream)
+static int atmel_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct atmel_runtime_data *prtd = substream->runtime->private_data;
@@ -381,36 +308,24 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops atmel_pcm_ops = {
+static const struct snd_soc_component_driver atmel_soc_platform = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_pcm_hw_params,
.hw_free = atmel_pcm_hw_free,
.prepare = atmel_pcm_prepare,
.trigger = atmel_pcm_trigger,
.pointer = atmel_pcm_pointer,
- .mmap = atmel_pcm_mmap,
-};
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
- .ops = &atmel_pcm_ops,
- .pcm_new = atmel_pcm_new,
- .pcm_free = atmel_pcm_free,
+ .pcm_construct = atmel_pcm_new,
};
int atmel_pcm_pdc_platform_register(struct device *dev)
{
- return snd_soc_register_platform(dev, &atmel_soc_platform);
+ return devm_snd_soc_register_component(dev, &atmel_soc_platform,
+ NULL, 0);
}
EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
-void atmel_pcm_pdc_platform_unregister(struct device *dev)
-{
- snd_soc_unregister_platform(dev);
-}
-EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
-
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("Atmel PCM module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index 6eaf081cad50..2e648748e5cb 100644
--- a/sound/soc/atmel/atmel-pcm.h
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC.
*
@@ -15,20 +16,6 @@
* Author: Nicolas Pitre
* Created: Nov 30, 2004
* Copyright: (C) 2004 MontaVista Software, 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; 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
*/
#ifndef _ATMEL_PCM_H
@@ -83,32 +70,22 @@ struct atmel_pcm_dma_params {
#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
-#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \
- defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE)
+#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_PDC)
int atmel_pcm_pdc_platform_register(struct device *dev);
-void atmel_pcm_pdc_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_pdc_platform_register(struct device *dev)
{
return 0;
}
-static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
-{
-}
#endif
-#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \
- defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE)
+#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_DMA)
int atmel_pcm_dma_platform_register(struct device *dev);
-void atmel_pcm_dma_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_dma_platform_register(struct device *dev)
{
return 0;
}
-static inline void atmel_pcm_dma_platform_unregister(struct device *dev)
-{
-}
#endif
#endif /* _ATMEL_PCM_H */
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index aee4787a0b89..4dfc7e5ca8ff 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Atmel PDMIC driver
*
* Copyright (C) 2015 Atmel
*
* Author: Songjun Wu <songjun.wu@atmel.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 or later
- * as published by the Free Software Foundation.
*/
#include <linux/of.h>
@@ -32,6 +29,7 @@ struct atmel_pdmic {
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
+ struct device *dev;
int irq;
struct snd_pcm_substream *substream;
const struct atmel_pdmic_pdata *pdata;
@@ -80,7 +78,7 @@ static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
if (pdata->mic_max_freq < pdata->mic_min_freq) {
dev_err(dev,
- "mic-max-freq should not less than mic-min-freq\n");
+ "mic-max-freq should not be less than mic-min-freq\n");
return ERR_PTR(-EINVAL);
}
@@ -106,7 +104,7 @@ static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
int ret;
@@ -115,8 +113,10 @@ static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
return ret;
ret = clk_prepare_enable(dd->pclk);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(dd->gclk);
return ret;
+ }
/* Clear all bits in the Control Register(PDMIC_CR) */
regmap_write(dd->regmap, PDMIC_CR, 0);
@@ -132,7 +132,7 @@ static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
/* Disable the overrun error interrupt */
@@ -145,34 +145,28 @@ static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = cpu_dai->component;
u32 val;
+ int ret;
/* Clean the PDMIC Converted Data Register */
- return regmap_read(dd->regmap, PDMIC_CDR, &val);
-}
-
-static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
- .startup = atmel_pdmic_cpu_dai_startup,
- .shutdown = atmel_pdmic_cpu_dai_shutdown,
- .prepare = atmel_pdmic_cpu_dai_prepare,
-};
+ ret = regmap_read(dd->regmap, PDMIC_CDR, &val);
+ if (ret < 0)
+ return 0;
-#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+ ret = snd_soc_component_update_bits(component, PDMIC_CR,
+ PDMIC_CR_ENPDM_MASK,
+ PDMIC_CR_ENPDM_DIS <<
+ PDMIC_CR_ENPDM_SHIFT);
+ if (ret < 0)
+ return ret;
-static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
- .capture = {
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = ATMEL_PDMIC_FORMATS,},
- .ops = &atmel_pdmic_cpu_dai_ops,
-};
+ return 0;
+}
-static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
- .name = "atmel-pdmic",
-};
+#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
/* platform */
#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
@@ -197,14 +191,14 @@ atmel_pdmic_platform_configure_dma(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct dma_slave_config *slave_config)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
int ret;
ret = snd_hwparams_to_dma_slave_config(substream, params,
slave_config);
if (ret) {
- dev_err(rtd->platform->dev,
+ dev_err(dd->dev,
"hw params to dma slave configure failed\n");
return ret;
}
@@ -283,17 +277,17 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0),
);
-int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
+static int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int dgain_val, scale_val;
int i;
- dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
+ dgain_val = (snd_soc_component_read(component, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
>> PDMIC_DSPR1_DGAIN_SHIFT;
- scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
+ scale_val = (snd_soc_component_read(component, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
>> PDMIC_DSPR0_SCALE_SHIFT;
for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) {
@@ -310,7 +304,7 @@ static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int max = mc->max;
unsigned int val;
int ret;
@@ -320,12 +314,12 @@ static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
if (val > max)
return -EINVAL;
- ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
+ ret = snd_soc_component_update_bits(component, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT);
if (ret < 0)
return ret;
- ret = snd_soc_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
+ ret = snd_soc_component_update_bits(component, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT);
if (ret < 0)
return ret;
@@ -344,34 +338,27 @@ SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
};
-static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
+static int atmel_pdmic_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
- snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
+ snd_soc_component_update_bits(component, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
(u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_pdmic = {
- .probe = atmel_pdmic_codec_probe,
- .controls = atmel_pdmic_snd_controls,
- .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
-};
-
-/* codec dai component */
#define PDMIC_MR_PRESCAL_MAX_VAL 127
static int
-atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *codec_dai)
+atmel_pdmic_cpu_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
unsigned int rate_min = substream->runtime->hw.rate_min;
unsigned int rate_max = substream->runtime->hw.rate_max;
int fs = params_rate(params);
@@ -381,13 +368,13 @@ atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
if (params_channels(params) != 1) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"only supports one channel\n");
return -EINVAL;
}
if ((fs < rate_min) || (fs > rate_max)) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
fs, rate_min, rate_max);
@@ -432,30 +419,19 @@ atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
}
- snd_soc_update_bits(codec, PDMIC_MR,
+ snd_soc_component_update_bits(component, PDMIC_MR,
PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
- snd_soc_update_bits(codec, PDMIC_DSPR0,
+ snd_soc_component_update_bits(component, PDMIC_DSPR0,
PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
return 0;
}
-static int atmel_pdmic_codec_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *codec_dai)
+static int atmel_pdmic_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_codec *codec = codec_dai->codec;
-
- snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
- PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
-
- return 0;
-}
-
-static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *codec_dai)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = cpu_dai->component;
u32 val;
switch (cmd) {
@@ -473,21 +449,21 @@ static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
+ snd_soc_component_update_bits(component, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
return 0;
}
-static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = {
- .hw_params = atmel_pdmic_codec_dai_hw_params,
- .prepare = atmel_pdmic_codec_dai_prepare,
- .trigger = atmel_pdmic_codec_dai_trigger,
+static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
+ .startup = atmel_pdmic_cpu_dai_startup,
+ .shutdown = atmel_pdmic_cpu_dai_shutdown,
+ .prepare = atmel_pdmic_cpu_dai_prepare,
+ .hw_params = atmel_pdmic_cpu_dai_hw_params,
+ .trigger = atmel_pdmic_cpu_dai_trigger,
};
-#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi"
-static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
- .name = ATMEL_PDMIC_CODEC_DAI_NAME,
+static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -495,7 +471,17 @@ static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
.rates = SNDRV_PCM_RATE_KNOT,
.formats = ATMEL_PDMIC_FORMATS,
},
- .ops = &atmel_pdmic_codec_dai_ops,
+ .ops = &atmel_pdmic_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
+ .name = "atmel-pdmic",
+ .probe = atmel_pdmic_component_probe,
+ .controls = atmel_pdmic_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .legacy_dai_naming = 1,
};
/* ASoC sound card */
@@ -504,17 +490,25 @@ static int atmel_pdmic_asoc_card_init(struct device *dev,
{
struct snd_soc_dai_link *dai_link;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link_component *comp;
dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
if (!dai_link)
return -ENOMEM;
+ comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai_link->cpus = comp;
+ dai_link->codecs = &snd_soc_dummy_dlc;
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+
dai_link->name = "PDMIC";
dai_link->stream_name = "PDMIC PCM";
- dai_link->codec_dai_name = ATMEL_PDMIC_CODEC_DAI_NAME;
- dai_link->cpu_dai_name = dev_name(dev);
- dai_link->codec_name = dev_name(dev);
- dai_link->platform_name = dev_name(dev);
+ dai_link->cpus->dai_name = dev_name(dev);
card->dai_link = dai_link;
card->num_links = 1;
@@ -592,13 +586,11 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return -ENOMEM;
dd->pdata = pdata;
+ dd->dev = dev;
dd->irq = platform_get_irq(pdev, 0);
- if (dd->irq < 0) {
- ret = dd->irq;
- dev_err(dev, "failed to could not get irq: %d\n", ret);
- return ret;
- }
+ if (dd->irq < 0)
+ return dd->irq;
dd->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dd->pclk)) {
@@ -614,7 +606,7 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return ret;
}
- /* The gclk clock frequency must always be tree times
+ /* The gclk clock frequency must always be three times
* lower than the pclk clock frequency
*/
ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3);
@@ -623,18 +615,9 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return ret;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no memory resource\n");
- return -ENXIO;
- }
-
- io_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(io_base)) {
- ret = PTR_ERR(io_base);
- dev_err(dev, "failed to remap register memory: %d\n", ret);
- return ret;
- }
+ io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
dd->phy_base = res->start;
@@ -654,7 +637,7 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return ret;
}
- /* Get the minimal and maximal sample rate that micphone supports */
+ /* Get the minimal and maximal sample rate that the microphone supports */
atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max);
/* register cpu dai */
@@ -677,16 +660,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return ret;
}
- /* register codec and codec dai */
- atmel_pdmic_codec_dai.capture.rate_min = rate_min;
- atmel_pdmic_codec_dai.capture.rate_max = rate_max;
- ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic,
- &atmel_pdmic_codec_dai, 1);
- if (ret) {
- dev_err(dev, "could not register codec: %d\n", ret);
- return ret;
- }
-
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card) {
@@ -695,7 +668,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
}
snd_soc_card_set_drvdata(card, dd);
- platform_set_drvdata(pdev, card);
ret = atmel_pdmic_asoc_card_init(dev, card);
if (ret) {
@@ -712,24 +684,16 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
return 0;
unregister_codec:
- snd_soc_unregister_codec(dev);
return ret;
}
-static int atmel_pdmic_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver atmel_pdmic_driver = {
.driver = {
.name = "atmel-pdmic",
- .of_match_table = of_match_ptr(atmel_pdmic_of_match),
+ .of_match_table = atmel_pdmic_of_match,
.pm = &snd_soc_pm_ops,
},
.probe = atmel_pdmic_probe,
- .remove = atmel_pdmic_remove,
};
module_platform_driver(atmel_pdmic_driver);
diff --git a/sound/soc/atmel/atmel-pdmic.h b/sound/soc/atmel/atmel-pdmic.h
index 4527ac741919..1dd35187102c 100644
--- a/sound/soc/atmel/atmel-pdmic.h
+++ b/sound/soc/atmel/atmel-pdmic.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ATMEL_PDMIC_H_
#define __ATMEL_PDMIC_H_
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index ba8def5665c4..89098f41679c 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel_ssc_dai.c -- ALSA SoC ATMEL SSC Audio Layer Platform driver
*
@@ -11,20 +12,6 @@
* Frank Mandarino <fmandarino@endrelia.com>
* Based on pxa2xx Platform drivers 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 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/init.h>
@@ -129,19 +116,16 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
{
.name = "ssc0",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
{
.name = "ssc1",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
{
.name = "ssc2",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
@@ -225,8 +209,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
if (frame_size < 0)
return frame_size;
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFS:
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
&& ssc->clk_from_rk_pin)
/* Receiver Frame Synchro (i.e. capture)
@@ -236,7 +220,7 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
mck_div = 3;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_BC_FC:
if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
&& !ssc->clk_from_rk_pin)
/* Transmit Frame Synchro (i.e. playback)
@@ -248,8 +232,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
break;
}
- switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
r.num = ssc_p->mck_rate / mck_div / frame_size;
ret = snd_interval_ratnum(i, 1, &r, &num, &den);
@@ -259,8 +243,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
}
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BC_FC:
t.min = 8000;
t.max = ssc_p->mck_rate / mck_div / frame_size;
t.openmin = t.openmax = 0;
@@ -285,7 +269,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
int ret;
@@ -295,11 +280,15 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
/* Enable PMC peripheral clock for this SSC */
pr_debug("atmel_ssc_dai: Starting clock\n");
- clk_enable(ssc_p->ssc->clk);
+ ret = clk_enable(ssc_p->ssc->clk);
+ if (ret)
+ return ret;
+
ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
- /* Reset the SSC to keep it at a clean status */
- ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+ /* Reset the SSC unless initialized to keep it in a clean state */
+ if (!ssc_p->initialized)
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dir = 0;
@@ -320,7 +309,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
return ret;
}
- dma_params = &ssc_dma_params[dai->id][dir];
+ dma_params = &ssc_dma_params[pdev->id][dir];
dma_params->ssc = ssc_p->ssc;
dma_params->substream = substream;
@@ -328,13 +317,10 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
snd_soc_dai_set_dma_data(dai, substream, dma_params);
- spin_lock_irq(&ssc_p->lock);
- if (ssc_p->dir_mask & dir_mask) {
- spin_unlock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask)
return -EBUSY;
- }
+
ssc_p->dir_mask |= dir_mask;
- spin_unlock_irq(&ssc_p->lock);
return 0;
}
@@ -346,7 +332,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
@@ -365,7 +352,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
dir_mask = 1 << dir;
- spin_lock_irq(&ssc_p->lock);
ssc_p->dir_mask &= ~dir_mask;
if (!ssc_p->dir_mask) {
if (ssc_p->initialized) {
@@ -377,8 +363,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
/* Clear the SSC dividers */
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+ ssc_p->forced_divider = 0;
}
- spin_unlock_irq(&ssc_p->lock);
/* Shutdown the SSC clock. */
pr_debug("atmel_ssc_dai: Stopping clock\n");
@@ -392,7 +378,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
ssc_p->daifmt = fmt;
return 0;
@@ -404,7 +391,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
switch (div_id) {
case ATMEL_SSC_CMR_DIV:
@@ -421,14 +409,17 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
else
if (div != ssc_p->cmr_div)
return -EBUSY;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV);
break;
case ATMEL_SSC_TCMR_PERIOD:
ssc_p->tcmr_period = div;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD);
break;
case ATMEL_SSC_RCMR_PERIOD:
ssc_p->rcmr_period = div;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD);
break;
default:
@@ -438,6 +429,28 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
return 0;
}
+/* Is the cpu-dai master of the frame clock? */
+static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
+{
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BP_FP:
+ return 1;
+ }
+ return 0;
+}
+
+/* Is the cpu-dai master of the bit clock? */
+static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p)
+{
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FC:
+ case SND_SOC_DAIFMT_BP_FP:
+ return 1;
+ }
+ return 0;
+}
+
/*
* Configure the SSC.
*/
@@ -445,14 +458,18 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- int id = dai->id;
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ int id = pdev->id;
struct atmel_ssc_info *ssc_p = &ssc_info[id];
struct ssc_device *ssc = ssc_p->ssc;
struct atmel_pcm_dma_params *dma_params;
int dir, channels, bits;
u32 tfmr, rfmr, tcmr, rcmr;
int ret;
- int fslen, fslen_ext;
+ int fslen, fslen_ext, fs_osync, fs_edge;
+ u32 cmr_div;
+ u32 tcmr_period;
+ u32 rcmr_period;
/*
* Currently, there is only one set of dma params for
@@ -464,6 +481,46 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
else
dir = 1;
+ /*
+ * If the cpu dai should provide BCLK, but noone has provided the
+ * divider needed for that to work, fall back to something sensible.
+ */
+ cmr_div = ssc_p->cmr_div;
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) &&
+ atmel_ssc_cbs(ssc_p)) {
+ int bclk_rate = snd_soc_params_to_bclk(params);
+
+ if (bclk_rate < 0) {
+ dev_err(dai->dev, "unable to calculate cmr_div: %d\n",
+ bclk_rate);
+ return bclk_rate;
+ }
+
+ cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate);
+ }
+
+ /*
+ * If the cpu dai should provide LRCLK, but noone has provided the
+ * dividers needed for that to work, fall back to something sensible.
+ */
+ tcmr_period = ssc_p->tcmr_period;
+ rcmr_period = ssc_p->rcmr_period;
+ if (atmel_ssc_cfs(ssc_p)) {
+ int frame_size = snd_soc_params_to_frame_size(params);
+
+ if (frame_size < 0) {
+ dev_err(dai->dev,
+ "unable to calculate tx/rx cmr_period: %d\n",
+ frame_size);
+ return frame_size;
+ }
+
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD)))
+ tcmr_period = frame_size / 2 - 1;
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD)))
+ rcmr_period = frame_size / 2 - 1;
+ }
+
dma_params = ssc_p->dma_params[dir];
channels = params_channels(params);
@@ -496,226 +553,45 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
/*
* Compute SSC register settings.
*/
- switch (ssc_p->daifmt
- & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * I2S format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated
- * from the MCK divider, and the BCLK signal
- * is output on the SSC TK line.
- */
+ fslen_ext = (bits - 1) / 16;
+ fslen = (bits - 1) % 16;
- if (bits > 16 && !ssc->pdata->has_fslen_ext) {
- dev_err(dai->dev,
- "sample size %d is too large for SSC device\n",
- bits);
- return -EINVAL;
- }
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- fslen_ext = (bits - 1) / 16;
- fslen = (bits - 1) % 16;
-
- rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
-
- rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(RFMR_FSLEN, fslen)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
- | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
-
- tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(TFMR_FSLEN, fslen)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
- break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fs_osync = SSC_FSOS_POSITIVE;
+ fs_edge = SSC_START_RISING_RF;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 0);
+ tcmr = SSC_BF(TCMR_STTDLY, 0);
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
- /* I2S format, CODEC supplies BCLK and LRC clocks. */
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, 0)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
break;
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS:
- /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */
- if (bits > 16 && !ssc->pdata->has_fslen_ext) {
- dev_err(dai->dev,
- "sample size %d is too large for SSC device\n",
- bits);
- return -EINVAL;
- }
+ case SND_SOC_DAIFMT_I2S:
+ fs_osync = SSC_FSOS_NEGATIVE;
+ fs_edge = SSC_START_FALLING_RF;
- fslen_ext = (bits - 1) / 16;
- fslen = (bits - 1) % 16;
-
- rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(RFMR_FSLEN, fslen)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
- | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(TFMR_FSLEN, fslen)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
- break;
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output
- * on the SSC TK line.
- */
- rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
- | SSC_BF(RCMR_STTDLY, 1)
- | SSC_BF(RCMR_START, SSC_START_RISING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
-
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
- | SSC_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
- | SSC_BF(TCMR_STTDLY, 1)
- | SSC_BF(TCMR_START, SSC_START_RISING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
- | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
-
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
break;
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_A:
/*
- * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
+ * DSP/PCM Mode A format
*
* Data is transferred on first BCLK after LRC pulse rising
* edge.If stereo, the right channel data is contiguous with
* the left channel data.
*/
- rcmr = SSC_BF(RCMR_PERIOD, 0)
- | SSC_BF(RCMR_STTDLY, START_DELAY)
- | SSC_BF(RCMR_START, SSC_START_RISING_RF)
- | SSC_BF(RCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_PIN : SSC_CKS_CLOCK);
-
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(RFMR_FSLEN, 0)
- | SSC_BF(RFMR_DATNB, (channels - 1))
- | SSC_BIT(RFMR_MSBF)
- | SSC_BF(RFMR_LOOP, 0)
- | SSC_BF(RFMR_DATLEN, (bits - 1));
-
- tcmr = SSC_BF(TCMR_PERIOD, 0)
- | SSC_BF(TCMR_STTDLY, START_DELAY)
- | SSC_BF(TCMR_START, SSC_START_RISING_RF)
- | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
- | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
- | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
- SSC_CKS_CLOCK : SSC_CKS_PIN);
-
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
- | SSC_BF(TFMR_FSDEN, 0)
- | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
- | SSC_BF(TFMR_FSLEN, 0)
- | SSC_BF(TFMR_DATNB, (channels - 1))
- | SSC_BIT(TFMR_MSBF)
- | SSC_BF(TFMR_DATDEF, 0)
- | SSC_BF(TFMR_DATLEN, (bits - 1));
+ fs_osync = SSC_FSOS_POSITIVE;
+ fs_edge = SSC_START_RISING_RF;
+ fslen = fslen_ext = 0;
+
+ rcmr = SSC_BF(RCMR_STTDLY, 1);
+ tcmr = SSC_BF(TCMR_STTDLY, 1);
+
break;
default:
@@ -723,6 +599,70 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
ssc_p->daifmt);
return -EINVAL;
}
+
+ if (!atmel_ssc_cfs(ssc_p)) {
+ fslen = fslen_ext = 0;
+ rcmr_period = tcmr_period = 0;
+ fs_osync = SSC_FSOS_NONE;
+ }
+
+ rcmr |= SSC_BF(RCMR_START, fs_edge);
+ tcmr |= SSC_BF(TCMR_START, fs_edge);
+
+ if (atmel_ssc_cbs(ssc_p)) {
+ /*
+ * SSC provides BCLK
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output
+ * on the SSC TK line.
+ */
+ rcmr |= SSC_BF(RCMR_CKS, SSC_CKS_DIV)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+ tcmr |= SSC_BF(TCMR_CKS, SSC_CKS_DIV)
+ | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
+ } else {
+ rcmr |= SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+ SSC_CKS_PIN : SSC_CKS_CLOCK)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE);
+
+ tcmr |= SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
+ SSC_CKS_CLOCK : SSC_CKS_PIN)
+ | SSC_BF(TCMR_CKO, SSC_CKO_NONE);
+ }
+
+ rcmr |= SSC_BF(RCMR_PERIOD, rcmr_period)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING);
+
+ tcmr |= SSC_BF(TCMR_PERIOD, tcmr_period)
+ | SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
+
+ rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, fs_osync)
+ | SSC_BF(RFMR_FSLEN, fslen)
+ | SSC_BF(RFMR_DATNB, (channels - 1))
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, fs_osync)
+ | SSC_BF(TFMR_FSLEN, fslen)
+ | SSC_BF(TFMR_DATNB, (channels - 1))
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+
+ if (fslen_ext && !ssc->pdata->has_fslen_ext) {
+ dev_err(dai->dev, "sample size %d is too large for SSC device\n",
+ bits);
+ return -EINVAL;
+ }
+
pr_debug("atmel_ssc_hw_params: "
"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
rcmr, rfmr, tcmr, tfmr);
@@ -745,7 +685,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
if (ret < 0) {
printk(KERN_WARNING
"atmel_ssc_dai: request_irq failure\n");
- pr_debug("Atmel_ssc_dai: Stoping clock\n");
+ pr_debug("Atmel_ssc_dai: Stopping clock\n");
clk_disable(ssc_p->ssc->clk);
return ret;
}
@@ -754,7 +694,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
}
/* set SSC clock mode register */
- ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+ ssc_writel(ssc_p->ssc->regs, CMR, cmr_div);
/* set receive clock mode and format */
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
@@ -772,7 +712,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir;
@@ -795,7 +736,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir;
@@ -820,15 +762,15 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
return 0;
}
-#ifdef CONFIG_PM
-static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
+static int atmel_ssc_suspend(struct snd_soc_component *component)
{
struct atmel_ssc_info *ssc_p;
+ struct platform_device *pdev = to_platform_device(component->dev);
- if (!cpu_dai->active)
+ if (!snd_soc_component_active(component))
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
+ ssc_p = &ssc_info[pdev->id];
/* Save the status register before disabling transmit and receive */
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
@@ -847,17 +789,16 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
return 0;
}
-
-
-static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
+static int atmel_ssc_resume(struct snd_soc_component *component)
{
struct atmel_ssc_info *ssc_p;
+ struct platform_device *pdev = to_platform_device(component->dev);
u32 cr;
- if (!cpu_dai->active)
+ if (!snd_soc_component_active(component))
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
+ ssc_p = &ssc_info[pdev->id];
/* restore SSC register settings */
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
@@ -879,13 +820,10 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
return 0;
}
-#else /* CONFIG_PM */
-# define atmel_ssc_suspend NULL
-# define atmel_ssc_resume NULL
-#endif /* CONFIG_PM */
+/* S24_LE is not supported if more than 2 channels (of TDM slots) are used. */
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.startup = atmel_ssc_startup,
@@ -898,9 +836,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
};
static struct snd_soc_dai_driver atmel_ssc_dai = {
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
.playback = {
+ .stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
@@ -908,6 +845,7 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
.rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
+ .stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
@@ -918,20 +856,22 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
};
static const struct snd_soc_component_driver atmel_ssc_component = {
- .name = "atmel-ssc",
+ .name = "atmel-ssc",
+ .suspend = pm_ptr(atmel_ssc_suspend),
+ .resume = pm_ptr(atmel_ssc_resume),
+ .legacy_dai_naming = 1,
};
static int asoc_ssc_init(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ssc_device *ssc = platform_get_drvdata(pdev);
+ struct ssc_device *ssc = dev_get_drvdata(dev);
int ret;
- ret = snd_soc_register_component(dev, &atmel_ssc_component,
+ ret = devm_snd_soc_register_component(dev, &atmel_ssc_component,
&atmel_ssc_dai, 1);
if (ret) {
dev_err(dev, "Could not register DAI: %d\n", ret);
- goto err;
+ return ret;
}
if (ssc->pdata->use_dma)
@@ -941,37 +881,19 @@ static int asoc_ssc_init(struct device *dev)
if (ret) {
dev_err(dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_dai;
+ return ret;
}
return 0;
-
-err_unregister_dai:
- snd_soc_unregister_component(dev);
-err:
- return ret;
-}
-
-static void asoc_ssc_exit(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct ssc_device *ssc = platform_get_drvdata(pdev);
-
- if (ssc->pdata->use_dma)
- atmel_pcm_dma_platform_unregister(dev);
- else
- atmel_pcm_pdc_platform_unregister(dev);
-
- snd_soc_unregister_component(dev);
}
/**
* atmel_ssc_set_audio - Allocate the specified SSC for audio use.
+ * @ssc_id: SSD ID in [0, NUM_SSC_DEVICES[
*/
int atmel_ssc_set_audio(int ssc_id)
{
struct ssc_device *ssc;
- int ret;
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
@@ -983,9 +905,7 @@ int atmel_ssc_set_audio(int ssc_id)
ssc_info[ssc_id].ssc = ssc;
}
- ret = asoc_ssc_init(&ssc->pdev->dev);
-
- return ret;
+ return asoc_ssc_init(&ssc->pdev->dev);
}
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
@@ -993,7 +913,6 @@ void atmel_ssc_put_audio(int ssc_id)
{
struct ssc_device *ssc = ssc_info[ssc_id].ssc;
- asoc_ssc_exit(&ssc->pdev->dev);
ssc_free(ssc);
}
EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index 80b153857a88..3470b966e449 100644
--- a/sound/soc/atmel/atmel_ssc_dai.h
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* atmel_ssc_dai.h - ALSA SSC interface for the Atmel SoC
*
@@ -11,20 +12,6 @@
* Frank Mandarino <fmandarino@endrelia.com>
* Based on pxa2xx Platform drivers 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 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
*/
#ifndef _ATMEL_SSC_DAI_H
@@ -106,13 +93,13 @@ struct atmel_ssc_state {
struct atmel_ssc_info {
char *name;
struct ssc_device *ssc;
- spinlock_t lock; /* lock for dir_mask */
unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
unsigned short initialized; /* true if SSC has been initialized */
unsigned short daifmt;
unsigned short cmr_div;
unsigned short tcmr_period;
unsigned short rcmr_period;
+ unsigned int forced_divider;
struct atmel_pcm_dma_params *dma_params[2];
struct atmel_ssc_state ssc_state;
unsigned long mck_rate;
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index fdd28ed3e0b9..0f4021c6c588 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec.
*
* Copyright (C) 2012 Atmel
*
* Author: Bo Shen <voice.shen@atmel.com>
- *
- * GPLv2 or later
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <sound/soc.h>
@@ -27,8 +25,8 @@ static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
@@ -53,18 +51,23 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops atmel_asoc_wm8904_ops = {
+static const struct snd_soc_ops atmel_asoc_wm8904_ops = {
.hw_params = atmel_asoc_wm8904_hw_params,
};
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8904-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
.name = "WM8904",
.stream_name = "WM8904 PCM",
- .codec_dai_name = "wm8904-hifi",
.dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &atmel_asoc_wm8904_ops,
+ SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card atmel_asoc_wm8904_card = {
@@ -108,8 +111,8 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
ret = -EINVAL;
return ret;
}
- dailink->cpu_of_node = cpu_np;
- dailink->platform_of_node = cpu_np;
+ dailink->cpus->of_node = cpu_np;
+ dailink->platforms->of_node = cpu_np;
of_node_put(cpu_np);
codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
@@ -118,7 +121,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
ret = -EINVAL;
return ret;
}
- dailink->codec_of_node = codec_np;
+ dailink->codecs->of_node = codec_np;
of_node_put(codec_np);
return 0;
@@ -137,7 +140,7 @@ static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
return ret;
}
- id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+ id = of_alias_get_id((struct device_node *)dailink->cpus->of_node, "ssc");
ret = atmel_ssc_set_audio(id);
if (ret != 0) {
dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id);
@@ -157,18 +160,16 @@ err_set_audio:
return ret;
}
-static int atmel_asoc_wm8904_remove(struct platform_device *pdev)
+static void atmel_asoc_wm8904_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
int id;
- id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+ id = of_alias_get_id((struct device_node *)dailink->cpus->of_node, "ssc");
snd_soc_unregister_card(card);
atmel_ssc_put_audio(id);
-
- return 0;
}
#ifdef CONFIG_OF
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
new file mode 100644
index 000000000000..17d138bb9064
--- /dev/null
+++ b/sound/soc/atmel/mchp-i2s-mcc.c
@@ -0,0 +1,1138 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip I2S Multi-channel controller
+//
+// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/lcm.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/dmaengine_pcm.h>
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define MCHP_I2SMCC_CR 0x0000 /* Control Register */
+#define MCHP_I2SMCC_MRA 0x0004 /* Mode Register A */
+#define MCHP_I2SMCC_MRB 0x0008 /* Mode Register B */
+#define MCHP_I2SMCC_SR 0x000C /* Status Register */
+#define MCHP_I2SMCC_IERA 0x0010 /* Interrupt Enable Register A */
+#define MCHP_I2SMCC_IDRA 0x0014 /* Interrupt Disable Register A */
+#define MCHP_I2SMCC_IMRA 0x0018 /* Interrupt Mask Register A */
+#define MCHP_I2SMCC_ISRA 0X001C /* Interrupt Status Register A */
+
+#define MCHP_I2SMCC_IERB 0x0020 /* Interrupt Enable Register B */
+#define MCHP_I2SMCC_IDRB 0x0024 /* Interrupt Disable Register B */
+#define MCHP_I2SMCC_IMRB 0x0028 /* Interrupt Mask Register B */
+#define MCHP_I2SMCC_ISRB 0X002C /* Interrupt Status Register B */
+
+#define MCHP_I2SMCC_RHR 0x0030 /* Receiver Holding Register */
+#define MCHP_I2SMCC_THR 0x0034 /* Transmitter Holding Register */
+
+#define MCHP_I2SMCC_RHL0R 0x0040 /* Receiver Holding Left 0 Register */
+#define MCHP_I2SMCC_RHR0R 0x0044 /* Receiver Holding Right 0 Register */
+
+#define MCHP_I2SMCC_RHL1R 0x0048 /* Receiver Holding Left 1 Register */
+#define MCHP_I2SMCC_RHR1R 0x004C /* Receiver Holding Right 1 Register */
+
+#define MCHP_I2SMCC_RHL2R 0x0050 /* Receiver Holding Left 2 Register */
+#define MCHP_I2SMCC_RHR2R 0x0054 /* Receiver Holding Right 2 Register */
+
+#define MCHP_I2SMCC_RHL3R 0x0058 /* Receiver Holding Left 3 Register */
+#define MCHP_I2SMCC_RHR3R 0x005C /* Receiver Holding Right 3 Register */
+
+#define MCHP_I2SMCC_THL0R 0x0060 /* Transmitter Holding Left 0 Register */
+#define MCHP_I2SMCC_THR0R 0x0064 /* Transmitter Holding Right 0 Register */
+
+#define MCHP_I2SMCC_THL1R 0x0068 /* Transmitter Holding Left 1 Register */
+#define MCHP_I2SMCC_THR1R 0x006C /* Transmitter Holding Right 1 Register */
+
+#define MCHP_I2SMCC_THL2R 0x0070 /* Transmitter Holding Left 2 Register */
+#define MCHP_I2SMCC_THR2R 0x0074 /* Transmitter Holding Right 2 Register */
+
+#define MCHP_I2SMCC_THL3R 0x0078 /* Transmitter Holding Left 3 Register */
+#define MCHP_I2SMCC_THR3R 0x007C /* Transmitter Holding Right 3 Register */
+
+#define MCHP_I2SMCC_VERSION 0x00FC /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define MCHP_I2SMCC_CR_RXEN BIT(0) /* Receiver Enable */
+#define MCHP_I2SMCC_CR_RXDIS BIT(1) /* Receiver Disable */
+#define MCHP_I2SMCC_CR_CKEN BIT(2) /* Clock Enable */
+#define MCHP_I2SMCC_CR_CKDIS BIT(3) /* Clock Disable */
+#define MCHP_I2SMCC_CR_TXEN BIT(4) /* Transmitter Enable */
+#define MCHP_I2SMCC_CR_TXDIS BIT(5) /* Transmitter Disable */
+#define MCHP_I2SMCC_CR_SWRST BIT(7) /* Software Reset */
+
+/*
+ * ---- Mode Register A (Read/Write) ----
+ */
+#define MCHP_I2SMCC_MRA_MODE_MASK GENMASK(0, 0)
+#define MCHP_I2SMCC_MRA_MODE_SLAVE (0 << 0)
+#define MCHP_I2SMCC_MRA_MODE_MASTER (1 << 0)
+
+#define MCHP_I2SMCC_MRA_DATALENGTH_MASK GENMASK(3, 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_32_BITS (0 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_24_BITS (1 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_20_BITS (2 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_18_BITS (3 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS (4 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS_COMPACT (5 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS (6 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT (7 << 1)
+
+#define MCHP_I2SMCC_MRA_WIRECFG_MASK GENMASK(5, 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_TDM(pin) (((pin) << 4) & \
+ MCHP_I2SMCC_MRA_WIRECFG_MASK)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0 (0 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1 (1 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2 (2 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_TDM_3 (3 << 4)
+
+#define MCHP_I2SMCC_MRA_FORMAT_MASK GENMASK(7, 6)
+#define MCHP_I2SMCC_MRA_FORMAT_I2S (0 << 6)
+#define MCHP_I2SMCC_MRA_FORMAT_LJ (1 << 6) /* Left Justified */
+#define MCHP_I2SMCC_MRA_FORMAT_TDM (2 << 6)
+#define MCHP_I2SMCC_MRA_FORMAT_TDMLJ (3 << 6)
+
+/* Transmitter uses one DMA channel ... */
+/* Left audio samples duplicated to right audio channel */
+#define MCHP_I2SMCC_MRA_RXMONO BIT(8)
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define MCHP_I2SMCC_MRA_RXLOOP BIT(9)
+
+/* Receiver uses one DMA channel ... */
+/* Left audio samples duplicated to right audio channel */
+#define MCHP_I2SMCC_MRA_TXMONO BIT(10)
+
+/* x sample transmitted when underrun */
+#define MCHP_I2SMCC_MRA_TXSAME_ZERO (0 << 11) /* Zero sample */
+#define MCHP_I2SMCC_MRA_TXSAME_PREVIOUS (1 << 11) /* Previous sample */
+
+/* select between peripheral clock and generated clock */
+#define MCHP_I2SMCC_MRA_SRCCLK_PCLK (0 << 12)
+#define MCHP_I2SMCC_MRA_SRCCLK_GCLK (1 << 12)
+
+/* Number of TDM Channels - 1 */
+#define MCHP_I2SMCC_MRA_NBCHAN_MASK GENMASK(15, 13)
+#define MCHP_I2SMCC_MRA_NBCHAN(ch) \
+ ((((ch) - 1) << 13) & MCHP_I2SMCC_MRA_NBCHAN_MASK)
+
+/* Selected Clock to I2SMCC Master Clock ratio */
+#define MCHP_I2SMCC_MRA_IMCKDIV_MASK GENMASK(21, 16)
+#define MCHP_I2SMCC_MRA_IMCKDIV(div) \
+ (((div) << 16) & MCHP_I2SMCC_MRA_IMCKDIV_MASK)
+
+/* TDM Frame Synchronization */
+#define MCHP_I2SMCC_MRA_TDMFS_MASK GENMASK(23, 22)
+#define MCHP_I2SMCC_MRA_TDMFS_SLOT (0 << 22)
+#define MCHP_I2SMCC_MRA_TDMFS_HALF (1 << 22)
+#define MCHP_I2SMCC_MRA_TDMFS_BIT (2 << 22)
+
+/* Selected Clock to I2SMC Serial Clock ratio */
+#define MCHP_I2SMCC_MRA_ISCKDIV_MASK GENMASK(29, 24)
+#define MCHP_I2SMCC_MRA_ISCKDIV(div) \
+ (((div) << 24) & MCHP_I2SMCC_MRA_ISCKDIV_MASK)
+
+/* Master Clock mode */
+#define MCHP_I2SMCC_MRA_IMCKMODE_MASK GENMASK(30, 30)
+/* 0: No master clock generated*/
+#define MCHP_I2SMCC_MRA_IMCKMODE_NONE (0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define MCHP_I2SMCC_MRA_IMCKMODE_GEN (1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define MCHP_I2SMCC_MRA_IWS BIT(31)
+
+/*
+ * ---- Mode Register B (Read/Write) ----
+ */
+/* all enabled I2S left channels are filled first, then I2S right channels */
+#define MCHP_I2SMCC_MRB_CRAMODE_LEFT_FIRST (0 << 0)
+/*
+ * an enabled I2S left channel is filled, then the corresponding right
+ * channel, until all channels are filled
+ */
+#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR (1 << 0)
+
+#define MCHP_I2SMCC_MRB_FIFOEN BIT(4)
+
+#define MCHP_I2SMCC_MRB_DMACHUNK_MASK GENMASK(9, 8)
+#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
+ (((fls(no_words) - 1) << 8) & MCHP_I2SMCC_MRB_DMACHUNK_MASK)
+
+#define MCHP_I2SMCC_MRB_CLKSEL_MASK GENMASK(16, 16)
+#define MCHP_I2SMCC_MRB_CLKSEL_EXT (0 << 16)
+#define MCHP_I2SMCC_MRB_CLKSEL_INT (1 << 16)
+
+/*
+ * ---- Status Registers (Read-only) ----
+ */
+#define MCHP_I2SMCC_SR_RXEN BIT(0) /* Receiver Enabled */
+#define MCHP_I2SMCC_SR_TXEN BIT(4) /* Transmitter Enabled */
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers A ----
+ */
+#define MCHP_I2SMCC_INT_TXRDY_MASK(ch) GENMASK((ch) - 1, 0)
+#define MCHP_I2SMCC_INT_TXRDYCH(ch) BIT(ch)
+#define MCHP_I2SMCC_INT_TXUNF_MASK(ch) GENMASK((ch) + 7, 8)
+#define MCHP_I2SMCC_INT_TXUNFCH(ch) BIT((ch) + 8)
+#define MCHP_I2SMCC_INT_RXRDY_MASK(ch) GENMASK((ch) + 15, 16)
+#define MCHP_I2SMCC_INT_RXRDYCH(ch) BIT((ch) + 16)
+#define MCHP_I2SMCC_INT_RXOVF_MASK(ch) GENMASK((ch) + 23, 24)
+#define MCHP_I2SMCC_INT_RXOVFCH(ch) BIT((ch) + 24)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers B ----
+ */
+#define MCHP_I2SMCC_INT_WERR BIT(0)
+#define MCHP_I2SMCC_INT_TXFFRDY BIT(8)
+#define MCHP_I2SMCC_INT_TXFFEMP BIT(9)
+#define MCHP_I2SMCC_INT_RXFFRDY BIT(12)
+#define MCHP_I2SMCC_INT_RXFFFUL BIT(13)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define MCHP_I2SMCC_VERSION_MASK GENMASK(11, 0)
+
+#define MCHP_I2SMCC_MAX_CHANNELS 8
+#define MCHP_I2MCC_TDM_SLOT_WIDTH 32
+
+/*
+ * ---- DMA chunk size allowed ----
+ */
+#define MCHP_I2SMCC_DMA_8_WORD_CHUNK 8
+#define MCHP_I2SMCC_DMA_4_WORD_CHUNK 4
+#define MCHP_I2SMCC_DMA_2_WORD_CHUNK 2
+#define MCHP_I2SMCC_DMA_1_WORD_CHUNK 1
+#define DMA_BURST_ALIGNED(_p, _s, _w) !(_p % (_s * _w))
+
+static const struct regmap_config mchp_i2s_mcc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = MCHP_I2SMCC_VERSION,
+};
+
+struct mchp_i2s_mcc_soc_data {
+ unsigned int data_pin_pair_num;
+ bool has_fifo;
+};
+
+struct mchp_i2s_mcc_dev {
+ struct wait_queue_head wq_txrdy;
+ struct wait_queue_head wq_rxrdy;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ const struct mchp_i2s_mcc_soc_data *soc;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct snd_dmaengine_dai_dma_data capture;
+ unsigned int fmt;
+ unsigned int sysclk;
+ unsigned int frame_length;
+ int tdm_slots;
+ int channels;
+ u8 tdm_data_pair;
+ unsigned int gclk_use:1;
+ unsigned int gclk_running:1;
+ unsigned int tx_rdy:1;
+ unsigned int rx_rdy:1;
+};
+
+static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
+{
+ struct mchp_i2s_mcc_dev *dev = dev_id;
+ u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0, idrb = 0;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
+ regmap_read(dev->regmap, MCHP_I2SMCC_ISRA, &sra);
+ pendinga = imra & sra;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_IMRB, &imrb);
+ regmap_read(dev->regmap, MCHP_I2SMCC_ISRB, &srb);
+ pendingb = imrb & srb;
+
+ if (!pendinga && !pendingb)
+ return IRQ_NONE;
+
+ /*
+ * Tx/Rx ready interrupts are enabled when stopping only, to assure
+ * availability and to disable clocks if necessary
+ */
+ if (dev->soc->has_fifo) {
+ idrb |= pendingb & (MCHP_I2SMCC_INT_TXFFRDY |
+ MCHP_I2SMCC_INT_RXFFRDY);
+ } else {
+ idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
+ MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
+ }
+ if (idra || idrb)
+ ret = IRQ_HANDLED;
+
+ if ((!dev->soc->has_fifo &&
+ (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
+ (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
+ (idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) ||
+ (dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_TXFFRDY)) {
+ dev->tx_rdy = 1;
+ wake_up_interruptible(&dev->wq_txrdy);
+ }
+ if ((!dev->soc->has_fifo &&
+ (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
+ (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
+ (idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) ||
+ (dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_RXFFRDY)) {
+ dev->rx_rdy = 1;
+ wake_up_interruptible(&dev->wq_rxrdy);
+ }
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRB, idrb);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
+
+ return ret;
+}
+
+static int mchp_i2s_mcc_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ __func__, clk_id, freq, dir);
+
+ /* We do not need SYSCLK */
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ dev->sysclk = freq;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() ratio=%u\n", __func__, ratio);
+
+ dev->frame_length = ratio;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() fmt=%#x\n", __func__, fmt);
+
+ /* We don't support any kind of clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+ return -EINVAL;
+
+ /* We can't generate only FSYNC */
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_BC_FP)
+ return -EINVAL;
+
+ /* We can only reconfigure the IP when it's stopped */
+ if (fmt & SND_SOC_DAIFMT_CONT)
+ return -EINVAL;
+
+ dev->fmt = fmt;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev,
+ "%s() tx_mask=0x%08x rx_mask=0x%08x slots=%d width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (slots < 0 || slots > MCHP_I2SMCC_MAX_CHANNELS ||
+ slot_width != MCHP_I2MCC_TDM_SLOT_WIDTH)
+ return -EINVAL;
+
+ if (slots) {
+ /* We do not support daisy chain */
+ if (rx_mask != GENMASK(slots - 1, 0) ||
+ rx_mask != tx_mask)
+ return -EINVAL;
+ }
+
+ dev->tdm_slots = slots;
+ dev->frame_length = slots * MCHP_I2MCC_TDM_SLOT_WIDTH;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
+ unsigned long rate,
+ struct clk **best_clk,
+ unsigned long *best_rate,
+ unsigned long *best_diff_rate)
+{
+ long round_rate;
+ unsigned int diff_rate;
+
+ round_rate = clk_round_rate(clk, rate);
+ if (round_rate < 0)
+ return (int)round_rate;
+
+ diff_rate = abs(rate - round_rate);
+ if (diff_rate < *best_diff_rate) {
+ *best_clk = clk;
+ *best_diff_rate = diff_rate;
+ *best_rate = rate;
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
+ unsigned int bclk, unsigned int *mra,
+ unsigned long *best_rate)
+{
+ unsigned long clk_rate;
+ unsigned long lcm_rate;
+ unsigned long best_diff_rate = ~0;
+ unsigned int sysclk;
+ struct clk *best_clk = NULL;
+ int ret;
+
+ /* For code simplification */
+ if (!dev->sysclk)
+ sysclk = bclk;
+ else
+ sysclk = dev->sysclk;
+
+ /*
+ * MCLK is Selected CLK / (2 * IMCKDIV),
+ * BCLK is Selected CLK / (2 * ISCKDIV);
+ * if IMCKDIV or ISCKDIV are 0, MCLK or BCLK = Selected CLK
+ */
+ lcm_rate = lcm(sysclk, bclk);
+ if ((lcm_rate / sysclk % 2 == 1 && lcm_rate / sysclk > 2) ||
+ (lcm_rate / bclk % 2 == 1 && lcm_rate / bclk > 2))
+ lcm_rate *= 2;
+
+ for (clk_rate = lcm_rate;
+ (clk_rate == sysclk || clk_rate / (sysclk * 2) <= GENMASK(5, 0)) &&
+ (clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
+ clk_rate += lcm_rate) {
+ ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
+ &best_clk, best_rate,
+ &best_diff_rate);
+ if (ret) {
+ dev_err(dev->dev, "gclk error for rate %lu: %d",
+ clk_rate, ret);
+ } else {
+ if (!best_diff_rate) {
+ dev_dbg(dev->dev, "found perfect rate on gclk: %lu\n",
+ clk_rate);
+ break;
+ }
+ }
+
+ ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
+ &best_clk, best_rate,
+ &best_diff_rate);
+ if (ret) {
+ dev_err(dev->dev, "pclk error for rate %lu: %d",
+ clk_rate, ret);
+ } else {
+ if (!best_diff_rate) {
+ dev_dbg(dev->dev, "found perfect rate on pclk: %lu\n",
+ clk_rate);
+ break;
+ }
+ }
+ }
+
+ /* check if clocks returned only errors */
+ if (!best_clk) {
+ dev_err(dev->dev, "unable to change rate to clocks\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
+ best_clk == dev->pclk ? "pclk" : "gclk",
+ *best_rate, best_diff_rate);
+
+ /* Configure divisors */
+ if (dev->sysclk)
+ *mra |= MCHP_I2SMCC_MRA_IMCKDIV(*best_rate / (2 * sysclk));
+ *mra |= MCHP_I2SMCC_MRA_ISCKDIV(*best_rate / (2 * bclk));
+
+ if (best_clk == dev->gclk)
+ *mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
+ else
+ *mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_is_running(struct mchp_i2s_mcc_dev *dev)
+{
+ u32 sr;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
+ return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN));
+}
+
+static inline int mchp_i2s_mcc_period_to_maxburst(int period_size, int sample_size)
+{
+ int p_size = period_size;
+ int s_size = sample_size;
+
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_I2SMCC_DMA_8_WORD_CHUNK))
+ return MCHP_I2SMCC_DMA_8_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_I2SMCC_DMA_4_WORD_CHUNK))
+ return MCHP_I2SMCC_DMA_4_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_I2SMCC_DMA_2_WORD_CHUNK))
+ return MCHP_I2SMCC_DMA_2_WORD_CHUNK;
+ return MCHP_I2SMCC_DMA_1_WORD_CHUNK;
+}
+
+static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned long rate = 0;
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int sample_bytes = params_physical_width(params) / 8;
+ int period_bytes = params_period_size(params) *
+ params_channels(params) * sample_bytes;
+ int maxburst;
+ u32 mra = 0;
+ u32 mrb = 0;
+ unsigned int channels = params_channels(params);
+ unsigned int frame_length = dev->frame_length;
+ unsigned int bclk_rate;
+ int set_divs = 0;
+ int ret;
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u period_bytes=%d\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params), period_bytes);
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ if (dev->tdm_slots) {
+ dev_err(dev->dev, "I2S with TDM is not supported\n");
+ return -EINVAL;
+ }
+ mra |= MCHP_I2SMCC_MRA_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if (dev->tdm_slots) {
+ dev_err(dev->dev, "Left-Justified with TDM is not supported\n");
+ return -EINVAL;
+ }
+ mra |= MCHP_I2SMCC_MRA_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mra |= MCHP_I2SMCC_MRA_FORMAT_TDM;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported bus format\n");
+ return -EINVAL;
+ }
+
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* cpu is BCLK and LRC master */
+ mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
+ if (dev->sysclk)
+ mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
+ set_divs = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ /* cpu is BCLK master */
+ mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
+ set_divs = 1;
+ fallthrough;
+ case SND_SOC_DAIFMT_BC_FC:
+ /* cpu is slave */
+ mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
+ if (dev->sysclk)
+ dev_warn(dev->dev, "Unable to generate MCLK in Slave mode\n");
+ break;
+ default:
+ dev_err(dev->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+ /* for I2S and LEFT_J one pin is needed for every 2 channels */
+ if (channels > dev->soc->data_pin_pair_num * 2) {
+ dev_err(dev->dev,
+ "unsupported number of audio channels: %d\n",
+ channels);
+ return -EINVAL;
+ }
+
+ /* enable for interleaved format */
+ mrb |= MCHP_I2SMCC_MRB_CRAMODE_REGULAR;
+
+ switch (channels) {
+ case 1:
+ if (is_playback)
+ mra |= MCHP_I2SMCC_MRA_TXMONO;
+ else
+ mra |= MCHP_I2SMCC_MRA_RXMONO;
+ break;
+ case 2:
+ break;
+ case 4:
+ mra |= MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1;
+ break;
+ case 8:
+ mra |= MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of audio channels\n");
+ return -EINVAL;
+ }
+
+ if (!frame_length)
+ frame_length = 2 * params_physical_width(params);
+ } else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
+ mra |= MCHP_I2SMCC_MRA_WIRECFG_TDM(dev->tdm_data_pair);
+
+ if (dev->tdm_slots) {
+ if (channels % 2 && channels * 2 <= dev->tdm_slots) {
+ /*
+ * Duplicate data for even-numbered channels
+ * to odd-numbered channels
+ */
+ if (is_playback)
+ mra |= MCHP_I2SMCC_MRA_TXMONO;
+ else
+ mra |= MCHP_I2SMCC_MRA_RXMONO;
+ }
+ channels = dev->tdm_slots;
+ }
+
+ mra |= MCHP_I2SMCC_MRA_NBCHAN(channels);
+ if (!frame_length)
+ frame_length = channels * MCHP_I2MCC_TDM_SLOT_WIDTH;
+ }
+
+ /*
+ * We must have the same burst size configured
+ * in the DMA transfer and in out IP
+ */
+ maxburst = mchp_i2s_mcc_period_to_maxburst(period_bytes, sample_bytes);
+ mrb |= MCHP_I2SMCC_MRB_DMACHUNK(maxburst);
+ if (is_playback)
+ dev->playback.maxburst = maxburst;
+ else
+ dev->capture.maxburst = maxburst;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_8_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_16_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_18_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_20_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_32_BITS;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+ return -EINVAL;
+ }
+
+ if (set_divs) {
+ bclk_rate = frame_length * params_rate(params);
+ ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra,
+ &rate);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to configure the divisors: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* enable FIFO if available */
+ if (dev->soc->has_fifo)
+ mrb |= MCHP_I2SMCC_MRB_FIFOEN;
+
+ /*
+ * If we are already running, the wanted setup must be
+ * the same with the one that's currently ongoing
+ */
+ if (mchp_i2s_mcc_is_running(dev)) {
+ u32 mra_cur;
+ u32 mrb_cur;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_MRA, &mra_cur);
+ regmap_read(dev->regmap, MCHP_I2SMCC_MRB, &mrb_cur);
+ if (mra != mra_cur || mrb != mrb_cur)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ if (mra & MCHP_I2SMCC_MRA_SRCCLK_GCLK && !dev->gclk_use) {
+ /* set the rate */
+ ret = clk_set_rate(dev->gclk, rate);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to set rate %lu to GCLK: %d\n",
+ rate, ret);
+ return ret;
+ }
+
+ ret = clk_prepare(dev->gclk);
+ if (ret < 0) {
+ dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
+ return ret;
+ }
+ dev->gclk_use = 1;
+ }
+
+ /* Save the number of channels to know what interrupts to enable */
+ dev->channels = channels;
+
+ ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
+ if (ret < 0) {
+ if (dev->gclk_use) {
+ clk_unprepare(dev->gclk);
+ dev->gclk_use = 0;
+ }
+ return ret;
+ }
+ return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
+}
+
+static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ long err;
+
+ if (is_playback) {
+ err = wait_event_interruptible_timeout(dev->wq_txrdy,
+ dev->tx_rdy,
+ msecs_to_jiffies(500));
+ if (err == 0) {
+ dev_warn_once(dev->dev,
+ "Timeout waiting for Tx ready\n");
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRB,
+ MCHP_I2SMCC_INT_TXFFRDY);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
+ MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels));
+
+ dev->tx_rdy = 1;
+ }
+ } else {
+ err = wait_event_interruptible_timeout(dev->wq_rxrdy,
+ dev->rx_rdy,
+ msecs_to_jiffies(500));
+ if (err == 0) {
+ dev_warn_once(dev->dev,
+ "Timeout waiting for Rx ready\n");
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRB,
+ MCHP_I2SMCC_INT_RXFFRDY);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
+ MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
+ dev->rx_rdy = 1;
+ }
+ }
+
+ if (!mchp_i2s_mcc_is_running(dev)) {
+ regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
+
+ if (dev->gclk_running) {
+ clk_disable(dev->gclk);
+ dev->gclk_running = 0;
+ }
+ if (dev->gclk_use) {
+ clk_unprepare(dev->gclk);
+ dev->gclk_use = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 cr = 0;
+ u32 iera = 0, ierb = 0;
+ u32 sr;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (is_playback)
+ cr = MCHP_I2SMCC_CR_TXEN | MCHP_I2SMCC_CR_CKEN;
+ else
+ cr = MCHP_I2SMCC_CR_RXEN | MCHP_I2SMCC_CR_CKEN;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
+ if (is_playback && (sr & MCHP_I2SMCC_SR_TXEN)) {
+ cr = MCHP_I2SMCC_CR_TXDIS;
+ dev->tx_rdy = 0;
+ /*
+ * Enable Tx Ready interrupts on all channels
+ * to assure all data is sent
+ */
+ if (dev->soc->has_fifo)
+ ierb = MCHP_I2SMCC_INT_TXFFRDY;
+ else
+ iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
+ } else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
+ cr = MCHP_I2SMCC_CR_RXDIS;
+ dev->rx_rdy = 0;
+ /*
+ * Enable Rx Ready interrupts on all channels
+ * to assure all data is received
+ */
+ if (dev->soc->has_fifo)
+ ierb = MCHP_I2SMCC_INT_RXFFRDY;
+ else
+ iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((cr & MCHP_I2SMCC_CR_CKEN) && dev->gclk_use &&
+ !dev->gclk_running) {
+ err = clk_enable(dev->gclk);
+ if (err) {
+ dev_err_once(dev->dev, "failed to enable GCLK: %d\n",
+ err);
+ } else {
+ dev->gclk_running = 1;
+ }
+ }
+
+ if (dev->soc->has_fifo)
+ regmap_write(dev->regmap, MCHP_I2SMCC_IERB, ierb);
+ else
+ regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
+ regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Software reset the IP if it's not running */
+ if (!mchp_i2s_mcc_is_running(dev)) {
+ return regmap_write(dev->regmap, MCHP_I2SMCC_CR,
+ MCHP_I2SMCC_CR_SWRST);
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ init_waitqueue_head(&dev->wq_txrdy);
+ init_waitqueue_head(&dev->wq_rxrdy);
+ dev->tx_rdy = 1;
+ dev->rx_rdy = 1;
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
+ .probe = mchp_i2s_mcc_dai_probe,
+ .set_sysclk = mchp_i2s_mcc_set_sysclk,
+ .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio,
+ .startup = mchp_i2s_mcc_startup,
+ .trigger = mchp_i2s_mcc_trigger,
+ .hw_params = mchp_i2s_mcc_hw_params,
+ .hw_free = mchp_i2s_mcc_hw_free,
+ .set_fmt = mchp_i2s_mcc_set_dai_fmt,
+ .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot,
+};
+
+#define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mchp_i2s_mcc_dai = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .ops = &mchp_i2s_mcc_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .symmetric_channels = 1,
+};
+
+static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
+ .name = "mchp-i2s-mcc",
+ .legacy_dai_naming = 1,
+};
+
+#ifdef CONFIG_OF
+static struct mchp_i2s_mcc_soc_data mchp_i2s_mcc_sam9x60 = {
+ .data_pin_pair_num = 1,
+};
+
+static struct mchp_i2s_mcc_soc_data mchp_i2s_mcc_sama7g5 = {
+ .data_pin_pair_num = 4,
+ .has_fifo = true,
+};
+
+static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
+ {
+ .compatible = "microchip,sam9x60-i2smcc",
+ .data = &mchp_i2s_mcc_sam9x60,
+ },
+ {
+ .compatible = "microchip,sama7g5-i2smcc",
+ .data = &mchp_i2s_mcc_sama7g5,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
+#endif
+
+static int mchp_i2s_mcc_soc_data_parse(struct platform_device *pdev,
+ struct mchp_i2s_mcc_dev *dev)
+{
+ int err;
+
+ if (!dev->soc) {
+ dev_err(&pdev->dev, "failed to get soc data\n");
+ return -ENODEV;
+ }
+
+ if (dev->soc->data_pin_pair_num == 1)
+ return 0;
+
+ err = of_property_read_u8(pdev->dev.of_node, "microchip,tdm-data-pair",
+ &dev->tdm_data_pair);
+ if (err < 0 && err != -EINVAL) {
+ dev_err(&pdev->dev,
+ "bad property data for 'microchip,tdm-data-pair': %d",
+ err);
+ return err;
+ }
+ if (err == -EINVAL) {
+ dev_info(&pdev->dev,
+ "'microchip,tdm-data-pair' not found; assuming DIN/DOUT 0 for TDM\n");
+ dev->tdm_data_pair = 0;
+ } else {
+ if (dev->tdm_data_pair > dev->soc->data_pin_pair_num - 1) {
+ dev_err(&pdev->dev,
+ "invalid value for 'microchip,tdm-data-pair': %d\n",
+ dev->tdm_data_pair);
+ return -EINVAL;
+ }
+ dev_dbg(&pdev->dev, "TMD format on DIN/DOUT %d pins\n",
+ dev->tdm_data_pair);
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_probe(struct platform_device *pdev)
+{
+ struct mchp_i2s_mcc_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ u32 version;
+ int irq;
+ int err;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_i2s_mcc_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_i2s_mcc_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev,
+ "failed to get the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ /* Get the optional generated clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_warn(&pdev->dev,
+ "generated clock not found: %d\n", err);
+ dev->gclk = NULL;
+ }
+
+ dev->soc = of_device_get_match_data(&pdev->dev);
+ err = mchp_i2s_mcc_soc_data_parse(pdev, dev);
+ if (err < 0)
+ return err;
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ err = clk_prepare_enable(dev->pclk);
+ if (err) {
+ dev_err(&pdev->dev,
+ "failed to enable the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_i2s_mcc_component,
+ &mchp_i2s_mcc_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ dev->playback.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_THR;
+ dev->capture.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_RHR;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ /* Get IP version. */
+ regmap_read(dev->regmap, MCHP_I2SMCC_VERSION, &version);
+ dev_info(&pdev->dev, "hw version: %#lx\n",
+ version & MCHP_I2SMCC_VERSION_MASK);
+
+ return 0;
+}
+
+static void mchp_i2s_mcc_remove(struct platform_device *pdev)
+{
+ struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(dev->pclk);
+}
+
+static struct platform_driver mchp_i2s_mcc_driver = {
+ .driver = {
+ .name = "mchp_i2s_mcc",
+ .of_match_table = mchp_i2s_mcc_dt_ids,
+ },
+ .probe = mchp_i2s_mcc_probe,
+ .remove = mchp_i2s_mcc_remove,
+};
+module_platform_driver(mchp_i2s_mcc_driver);
+
+MODULE_DESCRIPTION("Microchip I2S Multi-Channel Controller driver");
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c
new file mode 100644
index 000000000000..06dc3c48e7e8
--- /dev/null
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip Pulse Density Microphone Controller (PDMC) interfaces
+//
+// Copyright (C) 2019-2022 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <dt-bindings/sound/microchip,pdmc.h>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+/*
+ * ---- PDMC Register map ----
+ */
+#define MCHP_PDMC_CR 0x00 /* Control Register */
+#define MCHP_PDMC_MR 0x04 /* Mode Register */
+#define MCHP_PDMC_CFGR 0x08 /* Configuration Register */
+#define MCHP_PDMC_RHR 0x0C /* Receive Holding Register */
+#define MCHP_PDMC_IER 0x14 /* Interrupt Enable Register */
+#define MCHP_PDMC_IDR 0x18 /* Interrupt Disable Register */
+#define MCHP_PDMC_IMR 0x1C /* Interrupt Mask Register */
+#define MCHP_PDMC_ISR 0x20 /* Interrupt Status Register */
+#define MCHP_PDMC_VER 0x50 /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define MCHP_PDMC_CR_SWRST BIT(0) /* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+#define MCHP_PDMC_MR_PDMCEN_MASK GENMASK(3, 0)
+#define MCHP_PDMC_MR_PDMCEN(ch) (BIT(ch) & MCHP_PDMC_MR_PDMCEN_MASK)
+
+#define MCHP_PDMC_MR_OSR_MASK GENMASK(17, 16)
+#define MCHP_PDMC_MR_OSR64 (1 << 16)
+#define MCHP_PDMC_MR_OSR128 (2 << 16)
+#define MCHP_PDMC_MR_OSR256 (3 << 16)
+
+#define MCHP_PDMC_MR_SINCORDER_MASK GENMASK(23, 20)
+
+#define MCHP_PDMC_MR_SINC_OSR_MASK GENMASK(27, 24)
+#define MCHP_PDMC_MR_SINC_OSR_DIS (0 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_8 (1 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_16 (2 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_32 (3 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_64 (4 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_128 (5 << 24)
+#define MCHP_PDMC_MR_SINC_OSR_256 (6 << 24)
+
+#define MCHP_PDMC_MR_CHUNK_MASK GENMASK(31, 28)
+
+/*
+ * ---- Configuration Register (Read/Write) ----
+ */
+#define MCHP_PDMC_CFGR_BSSEL_MASK (BIT(0) | BIT(2) | BIT(4) | BIT(6))
+#define MCHP_PDMC_CFGR_BSSEL(ch) BIT((ch) * 2)
+
+#define MCHP_PDMC_CFGR_PDMSEL_MASK (BIT(16) | BIT(18) | BIT(20) | BIT(22))
+#define MCHP_PDMC_CFGR_PDMSEL(ch) BIT((ch) * 2 + 16)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers ----
+ */
+#define MCHP_PDMC_IR_RXRDY BIT(0)
+#define MCHP_PDMC_IR_RXEMPTY BIT(1)
+#define MCHP_PDMC_IR_RXFULL BIT(2)
+#define MCHP_PDMC_IR_RXCHUNK BIT(3)
+#define MCHP_PDMC_IR_RXUDR BIT(4)
+#define MCHP_PDMC_IR_RXOVR BIT(5)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define MCHP_PDMC_VER_VERSION GENMASK(11, 0)
+
+#define MCHP_PDMC_MAX_CHANNELS 4
+#define MCHP_PDMC_DS_NO 2
+#define MCHP_PDMC_EDGE_NO 2
+
+/*
+ * ---- DMA chunk size allowed ----
+ */
+#define MCHP_PDMC_DMA_8_WORD_CHUNK 8
+#define MCHP_PDMC_DMA_4_WORD_CHUNK 4
+#define MCHP_PDMC_DMA_2_WORD_CHUNK 2
+#define MCHP_PDMC_DMA_1_WORD_CHUNK 1
+#define DMA_BURST_ALIGNED(_p, _s, _w) !(_p % (_s * _w))
+
+struct mic_map {
+ int ds_pos;
+ int clk_edge;
+};
+
+struct mchp_pdmc_chmap {
+ struct snd_pcm_chmap_elem *chmap;
+ struct mchp_pdmc *dd;
+ struct snd_pcm *pcm;
+ struct snd_kcontrol *kctl;
+};
+
+struct mchp_pdmc {
+ struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS];
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data addr;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ u32 pdmcen;
+ u32 suspend_irq;
+ u32 startup_delay_us;
+ int mic_no;
+ int sinc_order;
+ bool audio_filter_en;
+ atomic_t busy_stream;
+};
+
+static const char *const mchp_pdmc_sinc_filter_order_text[] = {
+ "1", "2", "3", "4", "5"
+};
+
+static const unsigned int mchp_pdmc_sinc_filter_order_values[] = {
+ 1, 2, 3, 4, 5,
+};
+
+static const struct soc_enum mchp_pdmc_sinc_filter_order_enum = {
+ .items = ARRAY_SIZE(mchp_pdmc_sinc_filter_order_text),
+ .texts = mchp_pdmc_sinc_filter_order_text,
+ .values = mchp_pdmc_sinc_filter_order_values,
+};
+
+static int mchp_pdmc_sinc_order_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int item;
+
+ item = snd_soc_enum_val_to_item(e, dd->sinc_order);
+ uvalue->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int mchp_pdmc_sinc_order_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = uvalue->value.enumerated.item;
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ if (atomic_read(&dd->busy_stream))
+ return -EBUSY;
+
+ if (val == dd->sinc_order)
+ return 0;
+
+ dd->sinc_order = val;
+
+ return 1;
+}
+
+static int mchp_pdmc_af_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+
+ uvalue->value.integer.value[0] = !!dd->audio_filter_en;
+
+ return 0;
+}
+
+static int mchp_pdmc_af_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
+ bool af = uvalue->value.integer.value[0] ? true : false;
+
+ if (atomic_read(&dd->busy_stream))
+ return -EBUSY;
+
+ if (dd->audio_filter_en == af)
+ return 0;
+
+ dd->audio_filter_en = af;
+
+ return 1;
+}
+
+static int mchp_pdmc_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = info->dd->mic_no;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_RR; /* maxmimum 4 channels */
+ return 0;
+}
+
+static inline struct snd_pcm_substream *
+mchp_pdmc_chmap_substream(struct mchp_pdmc_chmap *info, unsigned int idx)
+{
+ struct snd_pcm_substream *s;
+
+ for (s = info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; s; s = s->next)
+ if (s->number == idx)
+ return s;
+ return NULL;
+}
+
+static struct snd_pcm_chmap_elem *mchp_pdmc_chmap_get(struct snd_pcm_substream *substream,
+ struct mchp_pdmc_chmap *ch_info)
+{
+ struct snd_pcm_chmap_elem *map;
+
+ for (map = ch_info->chmap; map->channels; map++) {
+ if (map->channels == substream->runtime->channels)
+ return map;
+ }
+ return NULL;
+}
+
+static int mchp_pdmc_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = info->dd;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map;
+ int i;
+ u32 cfgr_val = 0;
+
+ if (!info->chmap)
+ return -EINVAL;
+ substream = mchp_pdmc_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0, sizeof(long) * info->dd->mic_no);
+ if (!substream->runtime)
+ return 0; /* no channels set */
+
+ map = mchp_pdmc_chmap_get(substream, info);
+ if (!map)
+ return -EINVAL;
+
+ for (i = 0; i < map->channels; i++) {
+ int map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
+ map->map[i] - SNDRV_CHMAP_FL;
+
+ /* make sure the reported channel map is the real one, so write the map */
+ if (dd->channel_mic_map[map_idx].ds_pos)
+ cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
+ if (dd->channel_mic_map[map_idx].clk_edge)
+ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
+
+ ucontrol->value.integer.value[i] = map->map[i];
+ }
+
+ regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val);
+
+ return 0;
+}
+
+static int mchp_pdmc_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct mchp_pdmc *dd = info->dd;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_chmap_elem *map;
+ u32 cfgr_val = 0;
+ int i;
+
+ if (!info->chmap)
+ return -EINVAL;
+ substream = mchp_pdmc_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+
+ if (!substream->runtime)
+ return 0; /* just for avoiding error from alsactl restore */
+
+ map = mchp_pdmc_chmap_get(substream, info);
+ if (!map)
+ return -EINVAL;
+
+ for (i = 0; i < map->channels; i++) {
+ int map_idx;
+
+ map->map[i] = ucontrol->value.integer.value[i];
+ map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
+ map->map[i] - SNDRV_CHMAP_FL;
+
+ /* configure IP for the desired channel map */
+ if (dd->channel_mic_map[map_idx].ds_pos)
+ cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
+ if (dd->channel_mic_map[map_idx].clk_edge)
+ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
+ }
+
+ regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val);
+
+ return 0;
+}
+
+static void mchp_pdmc_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = NULL;
+ kfree(info);
+}
+
+static int mchp_pdmc_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
+ const struct snd_pcm_chmap_elem *map;
+ unsigned int __user *dst;
+ int c, count = 0;
+
+ if (!info->chmap)
+ return -EINVAL;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (map = info->chmap; map->channels; map++) {
+ int chs_bytes = map->channels * 4;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 0; c < map->channels; c++) {
+ if (put_user(map->map[c], dst))
+ return -EFAULT;
+ dst++;
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mchp_pdmc_snd_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Audio Filter", 0, &mchp_pdmc_af_get, &mchp_pdmc_af_put),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SINC Filter Order",
+ .info = snd_soc_info_enum_double,
+ .get = mchp_pdmc_sinc_order_get,
+ .put = mchp_pdmc_sinc_order_put,
+ .private_value = (unsigned long)&mchp_pdmc_sinc_filter_order_enum,
+ },
+};
+
+static const struct snd_soc_component_driver mchp_pdmc_dai_component = {
+ .name = "mchp-pdmc",
+ .controls = mchp_pdmc_snd_controls,
+ .num_controls = ARRAY_SIZE(mchp_pdmc_snd_controls),
+};
+
+static const unsigned int mchp_pdmc_1mic[] = {1};
+static const unsigned int mchp_pdmc_2mic[] = {1, 2};
+static const unsigned int mchp_pdmc_3mic[] = {1, 2, 3};
+static const unsigned int mchp_pdmc_4mic[] = {1, 2, 3, 4};
+
+static const struct snd_pcm_hw_constraint_list mchp_pdmc_chan_constr[] = {
+ {
+ .list = mchp_pdmc_1mic,
+ .count = ARRAY_SIZE(mchp_pdmc_1mic),
+ },
+ {
+ .list = mchp_pdmc_2mic,
+ .count = ARRAY_SIZE(mchp_pdmc_2mic),
+ },
+ {
+ .list = mchp_pdmc_3mic,
+ .count = ARRAY_SIZE(mchp_pdmc_3mic),
+ },
+ {
+ .list = mchp_pdmc_4mic,
+ .count = ARRAY_SIZE(mchp_pdmc_4mic),
+ },
+};
+
+static int mchp_pdmc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+
+ regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST);
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mchp_pdmc_chan_constr[dd->mic_no - 1]);
+
+ return 0;
+}
+
+static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, NULL, &dd->addr);
+
+ return 0;
+}
+
+static int mchp_pdmc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int fmt_master = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ unsigned int fmt_format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ /* IP needs to be bitclock master */
+ if (fmt_master != SND_SOC_DAIFMT_BP_FP &&
+ fmt_master != SND_SOC_DAIFMT_BP_FC)
+ return -EINVAL;
+
+ /* IP supports only PDM interface */
+ if (fmt_format != SND_SOC_DAIFMT_PDM)
+ return -EINVAL;
+
+ return 0;
+}
+
+static u32 mchp_pdmc_mr_set_osr(int audio_filter_en, unsigned int osr)
+{
+ if (audio_filter_en) {
+ switch (osr) {
+ case 64:
+ return MCHP_PDMC_MR_OSR64;
+ case 128:
+ return MCHP_PDMC_MR_OSR128;
+ case 256:
+ return MCHP_PDMC_MR_OSR256;
+ }
+ } else {
+ switch (osr) {
+ case 8:
+ return MCHP_PDMC_MR_SINC_OSR_8;
+ case 16:
+ return MCHP_PDMC_MR_SINC_OSR_16;
+ case 32:
+ return MCHP_PDMC_MR_SINC_OSR_32;
+ case 64:
+ return MCHP_PDMC_MR_SINC_OSR_64;
+ case 128:
+ return MCHP_PDMC_MR_SINC_OSR_128;
+ case 256:
+ return MCHP_PDMC_MR_SINC_OSR_256;
+ }
+ }
+ return 0;
+}
+
+static inline int mchp_pdmc_period_to_maxburst(int period_size, int sample_size)
+{
+ int p_size = period_size;
+ int s_size = sample_size;
+
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_8_WORD_CHUNK))
+ return MCHP_PDMC_DMA_8_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_4_WORD_CHUNK))
+ return MCHP_PDMC_DMA_4_WORD_CHUNK;
+ if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_2_WORD_CHUNK))
+ return MCHP_PDMC_DMA_2_WORD_CHUNK;
+ return MCHP_PDMC_DMA_1_WORD_CHUNK;
+}
+
+static struct snd_pcm_chmap_elem mchp_pdmc_std_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 3,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned long gclk_rate = 0;
+ unsigned long best_diff_rate = ~0UL;
+ unsigned int channels = params_channels(params);
+ unsigned int osr = 0, osr_start;
+ unsigned int fs = params_rate(params);
+ int sample_bytes = params_physical_width(params) / 8;
+ int period_bytes = params_period_size(params) *
+ params_channels(params) * sample_bytes;
+ int maxburst;
+ u32 mr_val = 0;
+ u32 cfgr_val = 0;
+ int i;
+ int ret;
+
+ dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u period_bytes=%d\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params), period_bytes);
+
+ if (channels > dd->mic_no) {
+ dev_err(comp->dev, "more channels %u than microphones %d\n",
+ channels, dd->mic_no);
+ return -EINVAL;
+ }
+
+ dd->pdmcen = 0;
+ for (i = 0; i < channels; i++) {
+ dd->pdmcen |= MCHP_PDMC_MR_PDMCEN(i);
+ if (dd->channel_mic_map[i].ds_pos)
+ cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
+ if (dd->channel_mic_map[i].clk_edge)
+ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
+ }
+
+ /*
+ * from these point forward, we consider the controller busy, so the
+ * audio filter and SINC order can't be changed
+ */
+ atomic_set(&dd->busy_stream, 1);
+ for (osr_start = dd->audio_filter_en ? 64 : 8;
+ osr_start <= 256 && best_diff_rate; osr_start *= 2) {
+ long round_rate;
+ unsigned long diff_rate;
+
+ round_rate = clk_round_rate(dd->gclk,
+ (unsigned long)fs * 16 * osr_start);
+ if (round_rate < 0)
+ continue;
+ diff_rate = abs((fs * 16 * osr_start) - round_rate);
+ if (diff_rate < best_diff_rate) {
+ best_diff_rate = diff_rate;
+ osr = osr_start;
+ gclk_rate = fs * 16 * osr;
+ }
+ }
+ if (!gclk_rate) {
+ dev_err(comp->dev, "invalid sampling rate: %u\n", fs);
+ return -EINVAL;
+ }
+
+ /* CLK is enabled by runtime PM. */
+ clk_disable_unprepare(dd->gclk);
+
+ /* set the rate */
+ ret = clk_set_rate(dd->gclk, gclk_rate);
+ clk_prepare_enable(dd->gclk);
+ if (ret) {
+ dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n",
+ gclk_rate, ret);
+ return ret;
+ }
+
+ mr_val |= mchp_pdmc_mr_set_osr(dd->audio_filter_en, osr);
+
+ mr_val |= FIELD_PREP(MCHP_PDMC_MR_SINCORDER_MASK, dd->sinc_order);
+
+ maxburst = mchp_pdmc_period_to_maxburst(period_bytes, sample_bytes);
+ dd->addr.maxburst = maxburst;
+ mr_val |= FIELD_PREP(MCHP_PDMC_MR_CHUNK_MASK, dd->addr.maxburst);
+ dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst);
+
+ snd_soc_component_update_bits(comp, MCHP_PDMC_MR,
+ MCHP_PDMC_MR_OSR_MASK |
+ MCHP_PDMC_MR_SINCORDER_MASK |
+ MCHP_PDMC_MR_SINC_OSR_MASK |
+ MCHP_PDMC_MR_CHUNK_MASK, mr_val);
+
+ snd_soc_component_write(comp, MCHP_PDMC_CFGR, cfgr_val);
+
+ return 0;
+}
+
+static void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd)
+{
+ u32 tmp, steps = 16;
+
+ /*
+ * PDMC doesn't wait for microphones' startup time thus the acquisition
+ * may start before the microphones are ready leading to poc noises at
+ * the beginning of capture. To avoid this, we need to wait 50ms (in
+ * normal startup procedure) or 150 ms (worst case after resume from sleep
+ * states) after microphones are enabled and then clear the FIFOs (by
+ * reading the RHR 16 times) and possible interrupts before continuing.
+ * Also, for this to work the DMA needs to be started after interrupts
+ * are enabled.
+ */
+ usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5);
+
+ while (steps--)
+ regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp);
+
+ /* Clear interrupts. */
+ regmap_read(dd->regmap, MCHP_PDMC_ISR, &tmp);
+}
+
+static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *cpu = dai->component;
+#ifdef DEBUG
+ u32 val;
+#endif
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
+ MCHP_PDMC_MR_PDMCEN_MASK,
+ dd->pdmcen);
+
+ mchp_pdmc_noise_filter_workaround(dd);
+
+ /* Enable interrupts. */
+ regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq |
+ MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
+ dd->suspend_irq = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* Disable overrun and underrun error interrupts */
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq |
+ MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
+ MCHP_PDMC_MR_PDMCEN_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+#ifdef DEBUG
+ regmap_read(dd->regmap, MCHP_PDMC_MR, &val);
+ dev_dbg(dd->dev, "MR (0x%02x): 0x%08x\n", MCHP_PDMC_MR, val);
+ regmap_read(dd->regmap, MCHP_PDMC_CFGR, &val);
+ dev_dbg(dd->dev, "CFGR (0x%02x): 0x%08x\n", MCHP_PDMC_CFGR, val);
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &val);
+ dev_dbg(dd->dev, "IMR (0x%02x): 0x%08x\n", MCHP_PDMC_IMR, val);
+#endif
+
+ return 0;
+}
+
+static int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd)
+{
+ struct mchp_pdmc_chmap *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = mchp_pdmc_chmap_ctl_info,
+ .get = mchp_pdmc_chmap_ctl_get,
+ .put = mchp_pdmc_chmap_ctl_put,
+ .tlv.c = mchp_pdmc_chmap_ctl_tlv,
+ };
+ int err;
+
+ if (WARN_ON(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl))
+ return -EBUSY;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->dd = dd;
+ info->chmap = mchp_pdmc_std_chmaps;
+ knew.name = "Capture Channel Map";
+ knew.device = pcm->device;
+ knew.count = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = mchp_pdmc_chmap_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0)
+ return err;
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = info->kctl;
+ return 0;
+}
+
+static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = mchp_pdmc_add_chmap_ctls(rtd->pcm, dd);
+ if (ret < 0)
+ dev_err(dd->dev, "failed to add channel map controls: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = {
+ .probe = mchp_pdmc_dai_probe,
+ .set_fmt = mchp_pdmc_set_fmt,
+ .startup = mchp_pdmc_startup,
+ .hw_params = mchp_pdmc_hw_params,
+ .trigger = mchp_pdmc_trigger,
+ .pcm_new = &mchp_pdmc_pcm_new,
+};
+
+static struct snd_soc_dai_driver mchp_pdmc_dai = {
+ .name = "mchp-pdmc",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &mchp_pdmc_dai_ops,
+};
+
+/* PDMC interrupt handler */
+static irqreturn_t mchp_pdmc_interrupt(int irq, void *dev_id)
+{
+ struct mchp_pdmc *dd = dev_id;
+ u32 isr, msr, pending;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dd->regmap, MCHP_PDMC_ISR, &isr);
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &msr);
+
+ pending = isr & msr;
+ dev_dbg(dd->dev, "ISR (0x%02x): 0x%08x, IMR (0x%02x): 0x%08x, pending: 0x%08x\n",
+ MCHP_PDMC_ISR, isr, MCHP_PDMC_IMR, msr, pending);
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & MCHP_PDMC_IR_RXUDR) {
+ dev_warn(dd->dev, "underrun detected\n");
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXUDR);
+ ret = IRQ_HANDLED;
+ }
+ if (pending & MCHP_PDMC_IR_RXOVR) {
+ dev_warn(dd->dev, "overrun detected\n");
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXOVR);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+/* regmap configuration */
+static bool mchp_pdmc_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_MR:
+ case MCHP_PDMC_CFGR:
+ case MCHP_PDMC_IMR:
+ case MCHP_PDMC_ISR:
+ case MCHP_PDMC_RHR:
+ case MCHP_PDMC_VER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_CR:
+ case MCHP_PDMC_MR:
+ case MCHP_PDMC_CFGR:
+ case MCHP_PDMC_IER:
+ case MCHP_PDMC_IDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_pdmc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_ISR:
+ case MCHP_PDMC_RHR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MCHP_PDMC_RHR:
+ case MCHP_PDMC_ISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mchp_pdmc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = MCHP_PDMC_VER,
+ .readable_reg = mchp_pdmc_readable_reg,
+ .writeable_reg = mchp_pdmc_writeable_reg,
+ .precious_reg = mchp_pdmc_precious_reg,
+ .volatile_reg = mchp_pdmc_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int mchp_pdmc_dt_init(struct mchp_pdmc *dd)
+{
+ struct device_node *np = dd->dev->of_node;
+ bool mic_ch[MCHP_PDMC_DS_NO][MCHP_PDMC_EDGE_NO] = {0};
+ int i;
+ int ret;
+
+ if (!np) {
+ dev_err(dd->dev, "device node not found\n");
+ return -EINVAL;
+ }
+
+ dd->mic_no = of_property_count_u32_elems(np, "microchip,mic-pos");
+ if (dd->mic_no < 0) {
+ dev_err(dd->dev, "failed to get microchip,mic-pos: %d",
+ dd->mic_no);
+ return dd->mic_no;
+ }
+ if (!dd->mic_no || dd->mic_no % 2 ||
+ dd->mic_no / 2 > MCHP_PDMC_MAX_CHANNELS) {
+ dev_err(dd->dev, "invalid array length for microchip,mic-pos: %d",
+ dd->mic_no);
+ return -EINVAL;
+ }
+
+ dd->mic_no /= 2;
+
+ dev_info(dd->dev, "%d PDM microphones declared\n", dd->mic_no);
+
+ /*
+ * by default, we consider the order of microphones in
+ * microchip,mic-pos to be the same with the channel mapping;
+ * 1st microphone channel 0, 2nd microphone channel 1, etc.
+ */
+ for (i = 0; i < dd->mic_no; i++) {
+ int ds;
+ int edge;
+
+ ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2,
+ &ds);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to get value no %d value from microchip,mic-pos: %d",
+ i * 2, ret);
+ return ret;
+ }
+ if (ds >= MCHP_PDMC_DS_NO) {
+ dev_err(dd->dev,
+ "invalid DS index in microchip,mic-pos array: %d",
+ ds);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2 + 1,
+ &edge);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to get value no %d value from microchip,mic-pos: %d",
+ i * 2 + 1, ret);
+ return ret;
+ }
+
+ if (edge != MCHP_PDMC_CLK_POSITIVE &&
+ edge != MCHP_PDMC_CLK_NEGATIVE) {
+ dev_err(dd->dev,
+ "invalid edge in microchip,mic-pos array: %d", edge);
+ return -EINVAL;
+ }
+ if (mic_ch[ds][edge]) {
+ dev_err(dd->dev,
+ "duplicated mic (DS %d, edge %d) in microchip,mic-pos array",
+ ds, edge);
+ return -EINVAL;
+ }
+ mic_ch[ds][edge] = true;
+ dd->channel_mic_map[i].ds_pos = ds;
+ dd->channel_mic_map[i].clk_edge = edge;
+ }
+
+ dd->startup_delay_us = 150000;
+ of_property_read_u32(np, "microchip,startup-delay-us", &dd->startup_delay_us);
+
+ return 0;
+}
+
+/* used to clean the channel index found on RHR's MSB */
+static int mchp_pdmc_process(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ unsigned long bytes)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u8 *dma_ptr = runtime->dma_area + hwoff +
+ channel * (runtime->dma_bytes / runtime->channels);
+ u8 *dma_ptr_end = dma_ptr + bytes;
+ unsigned int sample_size = samples_to_bytes(runtime, 1);
+
+ for (; dma_ptr < dma_ptr_end; dma_ptr += sample_size)
+ *dma_ptr = 0;
+
+ return 0;
+}
+
+static struct snd_dmaengine_pcm_config mchp_pdmc_config = {
+ .process = mchp_pdmc_process,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static int mchp_pdmc_runtime_suspend(struct device *dev)
+{
+ struct mchp_pdmc *dd = dev_get_drvdata(dev);
+
+ regcache_cache_only(dd->regmap, true);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->pclk);
+
+ return 0;
+}
+
+static int mchp_pdmc_runtime_resume(struct device *dev)
+{
+ struct mchp_pdmc *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(dd->pclk);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to enable the peripheral clock: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(dd->gclk);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to enable generic clock: %d\n", ret);
+ goto disable_pclk;
+ }
+
+ regcache_cache_only(dd->regmap, false);
+ regcache_mark_dirty(dd->regmap);
+ ret = regcache_sync(dd->regmap);
+ if (ret) {
+ regcache_cache_only(dd->regmap, true);
+ clk_disable_unprepare(dd->gclk);
+disable_pclk:
+ clk_disable_unprepare(dd->pclk);
+ }
+
+ return ret;
+}
+
+static int mchp_pdmc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mchp_pdmc *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ u32 version;
+ int irq;
+ int ret;
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->dev = &pdev->dev;
+ ret = mchp_pdmc_dt_init(dd);
+ if (ret < 0)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK: %d\n", ret);
+ return ret;
+ }
+
+ io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(io_base)) {
+ ret = PTR_ERR(io_base);
+ dev_err(dev, "failed to remap register memory: %d\n", ret);
+ return ret;
+ }
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &mchp_pdmc_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0,
+ dev_name(&pdev->dev), dd);
+ if (ret < 0) {
+ dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
+ irq, ret);
+ return ret;
+ }
+
+ /* by default audio filter is enabled and the SINC Filter order
+ * will be set to the recommended value, 3
+ */
+ dd->audio_filter_en = true;
+ dd->sinc_order = 3;
+
+ dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR;
+ platform_set_drvdata(pdev, dd);
+
+ pm_runtime_enable(dd->dev);
+ if (!pm_runtime_enabled(dd->dev)) {
+ ret = mchp_pdmc_runtime_resume(dd->dev);
+ if (ret)
+ return ret;
+ }
+
+ /* register platform */
+ ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ goto pm_runtime_suspend;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component,
+ &mchp_pdmc_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ goto pm_runtime_suspend;
+ }
+
+ /* print IP version */
+ regmap_read(dd->regmap, MCHP_PDMC_VER, &version);
+ dev_info(dd->dev, "hw version: %#lx\n",
+ version & MCHP_PDMC_VER_VERSION);
+
+ return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dd->dev))
+ mchp_pdmc_runtime_suspend(dd->dev);
+ pm_runtime_disable(dd->dev);
+
+ return ret;
+}
+
+static void mchp_pdmc_remove(struct platform_device *pdev)
+{
+ struct mchp_pdmc *dd = platform_get_drvdata(pdev);
+
+ atomic_set(&dd->busy_stream, 0);
+
+ if (!pm_runtime_status_suspended(dd->dev))
+ mchp_pdmc_runtime_suspend(dd->dev);
+
+ pm_runtime_disable(dd->dev);
+}
+
+static const struct of_device_id mchp_pdmc_of_match[] = {
+ {
+ .compatible = "microchip,sama7g5-pdmc",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match);
+
+static const struct dev_pm_ops mchp_pdmc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver mchp_pdmc_driver = {
+ .driver = {
+ .name = "mchp-pdmc",
+ .of_match_table = of_match_ptr(mchp_pdmc_of_match),
+ .pm = pm_ptr(&mchp_pdmc_pm_ops),
+ },
+ .probe = mchp_pdmc_probe,
+ .remove = mchp_pdmc_remove,
+};
+module_platform_driver(mchp_pdmc_driver);
+
+MODULE_DESCRIPTION("Microchip PDMC driver under ALSA SoC architecture");
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
new file mode 100644
index 000000000000..521bee4998f8
--- /dev/null
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -0,0 +1,1203 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip S/PDIF RX Controller
+//
+// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/*
+ * ---- S/PDIF Receiver Controller Register map ----
+ */
+#define SPDIFRX_CR 0x00 /* Control Register */
+#define SPDIFRX_MR 0x04 /* Mode Register */
+
+#define SPDIFRX_IER 0x10 /* Interrupt Enable Register */
+#define SPDIFRX_IDR 0x14 /* Interrupt Disable Register */
+#define SPDIFRX_IMR 0x18 /* Interrupt Mask Register */
+#define SPDIFRX_ISR 0x1c /* Interrupt Status Register */
+#define SPDIFRX_RSR 0x20 /* Status Register */
+#define SPDIFRX_RHR 0x24 /* Holding Register */
+
+#define SPDIFRX_CHSR(channel, reg) \
+ (0x30 + (channel) * 0x30 + (reg) * 4) /* Channel x Status Registers */
+
+#define SPDIFRX_CHUD(channel, reg) \
+ (0x48 + (channel) * 0x30 + (reg) * 4) /* Channel x User Data Registers */
+
+#define SPDIFRX_WPMR 0xE4 /* Write Protection Mode Register */
+#define SPDIFRX_WPSR 0xE8 /* Write Protection Status Register */
+
+#define SPDIFRX_VERSION 0xFC /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define SPDIFRX_CR_SWRST BIT(0) /* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+/* Receive Enable */
+#define SPDIFRX_MR_RXEN_MASK GENMASK(0, 0)
+#define SPDIFRX_MR_RXEN_DISABLE (0 << 0) /* SPDIF Receiver Disabled */
+#define SPDIFRX_MR_RXEN_ENABLE (1 << 0) /* SPDIF Receiver Enabled */
+
+/* Validity Bit Mode */
+#define SPDIFRX_MR_VBMODE_MASK GENAMSK(1, 1)
+#define SPDIFRX_MR_VBMODE_ALWAYS_LOAD \
+ (0 << 1) /* Load sample regardless of validity bit value */
+#define SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 \
+ (1 << 1) /* Load sample only if validity bit is 0 */
+
+/* Data Word Endian Mode */
+#define SPDIFRX_MR_ENDIAN_MASK GENMASK(2, 2)
+#define SPDIFRX_MR_ENDIAN_LITTLE (0 << 2) /* Little Endian Mode */
+#define SPDIFRX_MR_ENDIAN_BIG (1 << 2) /* Big Endian Mode */
+
+/* Parity Bit Mode */
+#define SPDIFRX_MR_PBMODE_MASK GENMASK(3, 3)
+#define SPDIFRX_MR_PBMODE_PARCHECK (0 << 3) /* Parity Check Enabled */
+#define SPDIFRX_MR_PBMODE_NOPARCHECK (1 << 3) /* Parity Check Disabled */
+
+/* Sample Data Width */
+#define SPDIFRX_MR_DATAWIDTH_MASK GENMASK(5, 4)
+#define SPDIFRX_MR_DATAWIDTH(width) \
+ (((6 - (width) / 4) << 4) & SPDIFRX_MR_DATAWIDTH_MASK)
+
+/* Packed Data Mode in Receive Holding Register */
+#define SPDIFRX_MR_PACK_MASK GENMASK(7, 7)
+#define SPDIFRX_MR_PACK_DISABLED (0 << 7)
+#define SPDIFRX_MR_PACK_ENABLED (1 << 7)
+
+/* Start of Block Bit Mode */
+#define SPDIFRX_MR_SBMODE_MASK GENMASK(8, 8)
+#define SPDIFRX_MR_SBMODE_ALWAYS_LOAD (0 << 8)
+#define SPDIFRX_MR_SBMODE_DISCARD (1 << 8)
+
+/* Consecutive Preamble Error Threshold Automatic Restart */
+#define SPDIFRX_MR_AUTORST_MASK GENMASK(24, 24)
+#define SPDIFRX_MR_AUTORST_NOACTION (0 << 24)
+#define SPDIFRX_MR_AUTORST_UNLOCK_ON_PRE_ERR (1 << 24)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
+ */
+#define SPDIFRX_IR_RXRDY BIT(0)
+#define SPDIFRX_IR_LOCKED BIT(1)
+#define SPDIFRX_IR_LOSS BIT(2)
+#define SPDIFRX_IR_BLOCKEND BIT(3)
+#define SPDIFRX_IR_SFE BIT(4)
+#define SPDIFRX_IR_PAR_ERR BIT(5)
+#define SPDIFRX_IR_OVERRUN BIT(6)
+#define SPDIFRX_IR_RXFULL BIT(7)
+#define SPDIFRX_IR_CSC(ch) BIT((ch) + 8)
+#define SPDIFRX_IR_SECE BIT(10)
+#define SPDIFRX_IR_BLOCKST BIT(11)
+#define SPDIFRX_IR_NRZ_ERR BIT(12)
+#define SPDIFRX_IR_PRE_ERR BIT(13)
+#define SPDIFRX_IR_CP_ERR BIT(14)
+
+/*
+ * ---- Receiver Status Register (Read/Write) ----
+ */
+/* Enable Status */
+#define SPDIFRX_RSR_ULOCK BIT(0)
+#define SPDIFRX_RSR_BADF BIT(1)
+#define SPDIFRX_RSR_LOWF BIT(2)
+#define SPDIFRX_RSR_NOSIGNAL BIT(3)
+#define SPDIFRX_RSR_IFS_MASK GENMASK(27, 16)
+#define SPDIFRX_RSR_IFS(reg) \
+ (((reg) & SPDIFRX_RSR_IFS_MASK) >> 16)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define SPDIFRX_VERSION_MASK GENMASK(11, 0)
+#define SPDIFRX_VERSION_MFN_MASK GENMASK(18, 16)
+#define SPDIFRX_VERSION_MFN(reg) (((reg) & SPDIFRX_VERSION_MFN_MASK) >> 16)
+
+static bool mchp_spdifrx_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_MR:
+ case SPDIFRX_IMR:
+ case SPDIFRX_ISR:
+ case SPDIFRX_RSR:
+ case SPDIFRX_CHSR(0, 0):
+ case SPDIFRX_CHSR(0, 1):
+ case SPDIFRX_CHSR(0, 2):
+ case SPDIFRX_CHSR(0, 3):
+ case SPDIFRX_CHSR(0, 4):
+ case SPDIFRX_CHSR(0, 5):
+ case SPDIFRX_CHUD(0, 0):
+ case SPDIFRX_CHUD(0, 1):
+ case SPDIFRX_CHUD(0, 2):
+ case SPDIFRX_CHUD(0, 3):
+ case SPDIFRX_CHUD(0, 4):
+ case SPDIFRX_CHUD(0, 5):
+ case SPDIFRX_CHSR(1, 0):
+ case SPDIFRX_CHSR(1, 1):
+ case SPDIFRX_CHSR(1, 2):
+ case SPDIFRX_CHSR(1, 3):
+ case SPDIFRX_CHSR(1, 4):
+ case SPDIFRX_CHSR(1, 5):
+ case SPDIFRX_CHUD(1, 0):
+ case SPDIFRX_CHUD(1, 1):
+ case SPDIFRX_CHUD(1, 2):
+ case SPDIFRX_CHUD(1, 3):
+ case SPDIFRX_CHUD(1, 4):
+ case SPDIFRX_CHUD(1, 5):
+ case SPDIFRX_WPMR:
+ case SPDIFRX_WPSR:
+ case SPDIFRX_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdifrx_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_CR:
+ case SPDIFRX_MR:
+ case SPDIFRX_IER:
+ case SPDIFRX_IDR:
+ case SPDIFRX_WPMR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_ISR:
+ case SPDIFRX_RHR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdifrx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_IMR:
+ case SPDIFRX_ISR:
+ case SPDIFRX_RSR:
+ case SPDIFRX_CHSR(0, 0):
+ case SPDIFRX_CHSR(0, 1):
+ case SPDIFRX_CHSR(0, 2):
+ case SPDIFRX_CHSR(0, 3):
+ case SPDIFRX_CHSR(0, 4):
+ case SPDIFRX_CHSR(0, 5):
+ case SPDIFRX_CHUD(0, 0):
+ case SPDIFRX_CHUD(0, 1):
+ case SPDIFRX_CHUD(0, 2):
+ case SPDIFRX_CHUD(0, 3):
+ case SPDIFRX_CHUD(0, 4):
+ case SPDIFRX_CHUD(0, 5):
+ case SPDIFRX_CHSR(1, 0):
+ case SPDIFRX_CHSR(1, 1):
+ case SPDIFRX_CHSR(1, 2):
+ case SPDIFRX_CHSR(1, 3):
+ case SPDIFRX_CHSR(1, 4):
+ case SPDIFRX_CHSR(1, 5):
+ case SPDIFRX_CHUD(1, 0):
+ case SPDIFRX_CHUD(1, 1):
+ case SPDIFRX_CHUD(1, 2):
+ case SPDIFRX_CHUD(1, 3):
+ case SPDIFRX_CHUD(1, 4):
+ case SPDIFRX_CHUD(1, 5):
+ case SPDIFRX_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mchp_spdifrx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIFRX_VERSION,
+ .readable_reg = mchp_spdifrx_readable_reg,
+ .writeable_reg = mchp_spdifrx_writeable_reg,
+ .precious_reg = mchp_spdifrx_precious_reg,
+ .volatile_reg = mchp_spdifrx_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define SPDIFRX_GCLK_RATIO_MIN (12 * 64)
+
+#define SPDIFRX_CS_BITS 192
+#define SPDIFRX_UD_BITS 192
+
+#define SPDIFRX_CHANNELS 2
+
+/**
+ * struct mchp_spdifrx_ch_stat: MCHP SPDIFRX channel status
+ * @data: channel status bits
+ * @done: completion to signal channel status bits acquisition done
+ */
+struct mchp_spdifrx_ch_stat {
+ unsigned char data[SPDIFRX_CS_BITS / 8];
+ struct completion done;
+};
+
+/**
+ * struct mchp_spdifrx_user_data: MCHP SPDIFRX user data
+ * @data: user data bits
+ * @done: completion to signal user data bits acquisition done
+ */
+struct mchp_spdifrx_user_data {
+ unsigned char data[SPDIFRX_UD_BITS / 8];
+ struct completion done;
+};
+
+/**
+ * struct mchp_spdifrx_mixer_control: MCHP SPDIFRX mixer control data structure
+ * @ch_stat: array of channel statuses
+ * @user_data: array of user data
+ * @ulock: ulock bit status
+ * @badf: badf bit status
+ * @signal: signal bit status
+ */
+struct mchp_spdifrx_mixer_control {
+ struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS];
+ struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS];
+ bool ulock;
+ bool badf;
+ bool signal;
+};
+
+/**
+ * struct mchp_spdifrx_dev: MCHP SPDIFRX device data structure
+ * @capture: DAI DMA configuration data
+ * @control: mixer controls
+ * @mlock: mutex to protect concurency b/w configuration and control APIs
+ * @dev: struct device
+ * @regmap: regmap for this device
+ * @pclk: peripheral clock
+ * @gclk: generic clock
+ * @trigger_enabled: true if enabled though trigger() ops
+ */
+struct mchp_spdifrx_dev {
+ struct snd_dmaengine_dai_dma_data capture;
+ struct mchp_spdifrx_mixer_control control;
+ struct mutex mlock;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ unsigned int trigger_enabled;
+};
+
+static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev,
+ int channel)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u8 *ch_stat = &ctrl->ch_stat[channel].data[0];
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat[channel].data) / 4; i++) {
+ regmap_read(dev->regmap, SPDIFRX_CHSR(channel, i), &val);
+ *ch_stat++ = val & 0xFF;
+ *ch_stat++ = (val >> 8) & 0xFF;
+ *ch_stat++ = (val >> 16) & 0xFF;
+ *ch_stat++ = (val >> 24) & 0xFF;
+ }
+}
+
+static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev,
+ int channel)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u8 *user_data = &ctrl->user_data[channel].data[0];
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->user_data[channel].data) / 4; i++) {
+ regmap_read(dev->regmap, SPDIFRX_CHUD(channel, i), &val);
+ *user_data++ = val & 0xFF;
+ *user_data++ = (val >> 8) & 0xFF;
+ *user_data++ = (val >> 16) & 0xFF;
+ *user_data++ = (val >> 24) & 0xFF;
+ }
+}
+
+static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
+{
+ struct mchp_spdifrx_dev *dev = dev_id;
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 sr, imr, pending;
+ irqreturn_t ret = IRQ_NONE;
+ int ch;
+
+ regmap_read(dev->regmap, SPDIFRX_ISR, &sr);
+ regmap_read(dev->regmap, SPDIFRX_IMR, &imr);
+ pending = sr & imr;
+ dev_dbg(dev->dev, "ISR: %#x, IMR: %#x, pending: %#x\n", sr, imr,
+ pending);
+
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & SPDIFRX_IR_BLOCKEND) {
+ for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
+ mchp_spdifrx_channel_user_data_read(dev, ch);
+ complete(&ctrl->user_data[ch].done);
+ }
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
+ ret = IRQ_HANDLED;
+ }
+
+ for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
+ if (pending & SPDIFRX_IR_CSC(ch)) {
+ mchp_spdifrx_channel_status_read(dev, ch);
+ complete(&ctrl->ch_stat[ch].done);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch));
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if (pending & SPDIFRX_IR_OVERRUN) {
+ dev_warn(dev->dev, "Overrun detected\n");
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mutex_lock(&dev->mlock);
+ /* Enable overrun interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_OVERRUN);
+
+ /* Enable receiver. */
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_ENABLE);
+ dev->trigger_enabled = true;
+ mutex_unlock(&dev->mlock);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ mutex_lock(&dev->mlock);
+ /* Disable overrun interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_OVERRUN);
+
+ /* Disable receiver. */
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_DISABLE);
+ dev->trigger_enabled = false;
+ mutex_unlock(&dev->mlock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ u32 mr = 0;
+ int ret;
+
+ dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_err(dev->dev, "Playback is not supported\n");
+ return -EINVAL;
+ }
+
+ if (params_channels(params) != SPDIFRX_CHANNELS) {
+ dev_err(dev->dev, "unsupported number of channels: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ mr |= SPDIFRX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mr |= SPDIFRX_MR_DATAWIDTH(params_width(params));
+ break;
+ default:
+ dev_err(dev->dev, "unsupported PCM format: %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mlock);
+ if (dev->trigger_enabled) {
+ dev_err(dev->dev, "PCM already running\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ /* GCLK is enabled by runtime PM. */
+ clk_disable_unprepare(dev->gclk);
+
+ ret = clk_set_min_rate(dev->gclk, params_rate(params) *
+ SPDIFRX_GCLK_RATIO_MIN + 1);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to set gclk min rate: rate %u * ratio %u + 1\n",
+ params_rate(params), SPDIFRX_GCLK_RATIO_MIN);
+ /* Restore runtime PM state. */
+ clk_prepare_enable(dev->gclk);
+ goto unlock;
+ }
+ ret = clk_prepare_enable(dev->gclk);
+ if (ret) {
+ dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
+ goto unlock;
+ }
+
+ dev_dbg(dev->dev, "GCLK range min set to %d\n",
+ params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1);
+
+ ret = regmap_write(dev->regmap, SPDIFRX_MR, mr);
+
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ return ret;
+}
+
+#define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_BE | \
+ 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 \
+ )
+
+static int mchp_spdifrx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev,
+ int channel,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel];
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion and return with
+ * timeout check the dev->trigger_enabled.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled CSC IRQ will update the data in software
+ * caches (ch_stat->data)
+ * - otherwise we just update it here the software caches with latest
+ * available information and return it; in this case we don't need
+ * spin locking as the IRQ is disabled and will not be raised from
+ * anywhere else.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&ch_stat->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
+ /* Check for new data available */
+ ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "channel status for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel));
+ ret = ret ? : -ETIMEDOUT;
+ goto pm_runtime_put;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with latest channel status. */
+ mchp_spdifrx_channel_status_read(dev, channel);
+ }
+
+ memcpy(uvalue->value.iec958.status, ch_stat->data,
+ sizeof(ch_stat->data));
+
+pm_runtime_put:
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_cs_get(dev, 0, uvalue);
+}
+
+static int mchp_spdifrx_cs2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_cs_get(dev, 1, uvalue);
+}
+
+static int mchp_spdifrx_cs_mask(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ memset(uvalue->value.iec958.status, 0xff,
+ sizeof(uvalue->value.iec958.status));
+
+ return 0;
+}
+
+static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev,
+ int channel,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel];
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion to just timeout we
+ * check here the dev->trigger_enabled flag.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled we need to wait for blockend IRQ to read
+ * data to and update it for us in software caches
+ * - otherwise reading the SPDIFRX_CHUD() registers is enough.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&user_data->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
+ ret = wait_for_completion_interruptible_timeout(&user_data->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present. */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "user data for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
+ ret = ret ? : -ETIMEDOUT;
+ goto pm_runtime_put;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with last available data. */
+ mchp_spdifrx_channel_user_data_read(dev, channel);
+ }
+
+ memcpy(uvalue->value.iec958.subcode, user_data->data,
+ sizeof(user_data->data));
+
+pm_runtime_put:
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_subcode_ch_get(dev, 0, uvalue);
+}
+
+static int mchp_spdifrx_subcode_ch2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return mchp_spdifrx_subcode_ch_get(dev, 1, uvalue);
+}
+
+static int mchp_spdifrx_boolean_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 mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int ret;
+ bool ulock_old = ctrl->ulock;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK);
+ } else {
+ ctrl->ulock = 0;
+ }
+
+ uvalue->value.integer.value[0] = ctrl->ulock;
+
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ return ulock_old != ctrl->ulock;
+}
+
+static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int ret;
+ bool badf_old = ctrl->badf;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ ctrl->badf = !!(val & SPDIFRX_RSR_BADF);
+ } else {
+ ctrl->badf = 0;
+ }
+
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ uvalue->value.integer.value[0] = ctrl->badf;
+
+ return badf_old != ctrl->badf;
+}
+
+static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ u32 val = ~0U, loops = 10;
+ int ret;
+ bool signal_old = ctrl->signal;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * To get the signal we need to have receiver enabled. This
+ * could be enabled also from trigger() function thus we need to
+ * take care of not disabling the receiver when it runs.
+ */
+ if (!dev->trigger_enabled) {
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_ENABLE);
+
+ /* Wait for RSR.ULOCK bit. */
+ while (--loops) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ if (!(val & SPDIFRX_RSR_ULOCK))
+ break;
+ usleep_range(100, 150);
+ }
+
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_DISABLE);
+ } else {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ }
+
+ pm_runtime_put_autosuspend(dev->dev);
+
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ if (!(val & SPDIFRX_RSR_ULOCK))
+ ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL);
+ else
+ ctrl->signal = 0;
+ uvalue->value.integer.value[0] = ctrl->signal;
+
+ return signal_old != ctrl->signal;
+}
+
+static int mchp_spdifrx_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+
+ return 0;
+}
+
+static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ unsigned long rate;
+ u32 val;
+ int ret;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ /* If the receiver is not locked, ISF data is invalid. */
+ if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) {
+ ucontrol->value.integer.value[0] = 0;
+ goto pm_runtime_put;
+ }
+ } else {
+ /* Reveicer is not locked, IFS data is invalid. */
+ ucontrol->value.integer.value[0] = 0;
+ goto pm_runtime_put;
+ }
+
+ rate = clk_get_rate(dev->gclk);
+
+ ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val));
+
+pm_runtime_put:
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = {
+ /* Channel status controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT)
+ " Channel 1",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_cs1_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT)
+ " Channel 2",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_cs2_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_cs_mask,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default Channel 1",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_subcode_ch1_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default Channel 2",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_info,
+ .get = mchp_spdifrx_subcode_ch2_get,
+ },
+ /* Lock status */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Unlocked",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_boolean_info,
+ .get = mchp_spdifrx_ulock_get,
+ },
+ /* Bad format */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE)"Bad Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_boolean_info,
+ .get = mchp_spdifrx_badf_get,
+ },
+ /* Signal */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Signal",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_boolean_info,
+ .get = mchp_spdifrx_signal_get,
+ },
+ /* Sampling rate */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdifrx_rate_info,
+ .get = mchp_spdifrx_rate_get,
+ },
+};
+
+static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
+ int ch;
+
+ snd_soc_dai_init_dma_data(dai, NULL, &dev->capture);
+
+ /* Software reset the IP */
+ regmap_write(dev->regmap, SPDIFRX_CR, SPDIFRX_CR_SWRST);
+
+ /* Default configuration */
+ regmap_write(dev->regmap, SPDIFRX_MR,
+ SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 |
+ SPDIFRX_MR_SBMODE_DISCARD |
+ SPDIFRX_MR_AUTORST_NOACTION |
+ SPDIFRX_MR_PACK_DISABLED);
+
+ for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
+ init_completion(&ctrl->ch_stat[ch].done);
+ init_completion(&ctrl->user_data[ch].done);
+ }
+
+ /* Add controls */
+ snd_soc_add_dai_controls(dai, mchp_spdifrx_ctrls,
+ ARRAY_SIZE(mchp_spdifrx_ctrls));
+
+ return 0;
+}
+
+static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai)
+{
+ struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Disable interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IDR, GENMASK(14, 0));
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = {
+ .probe = mchp_spdifrx_dai_probe,
+ .remove = mchp_spdifrx_dai_remove,
+ .trigger = mchp_spdifrx_trigger,
+ .hw_params = mchp_spdifrx_hw_params,
+};
+
+static struct snd_soc_dai_driver mchp_spdifrx_dai = {
+ .name = "mchp-spdifrx",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = SPDIFRX_CHANNELS,
+ .channels_max = SPDIFRX_CHANNELS,
+ .rates = MCHP_SPDIF_RATES,
+ .formats = MCHP_SPDIF_FORMATS,
+ },
+ .ops = &mchp_spdifrx_dai_ops,
+};
+
+static const struct snd_soc_component_driver mchp_spdifrx_component = {
+ .name = "mchp-spdifrx",
+ .legacy_dai_naming = 1,
+};
+
+static const struct of_device_id mchp_spdifrx_dt_ids[] = {
+ {
+ .compatible = "microchip,sama7g5-spdifrx",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids);
+
+static int mchp_spdifrx_runtime_suspend(struct device *dev)
+{
+ struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdifrx->regmap, true);
+ clk_disable_unprepare(spdifrx->gclk);
+ clk_disable_unprepare(spdifrx->pclk);
+
+ return 0;
+}
+
+static int mchp_spdifrx_runtime_resume(struct device *dev)
+{
+ struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdifrx->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(spdifrx->gclk);
+ if (ret)
+ goto disable_pclk;
+
+ regcache_cache_only(spdifrx->regmap, false);
+ regcache_mark_dirty(spdifrx->regmap);
+ ret = regcache_sync(spdifrx->regmap);
+ if (ret) {
+ regcache_cache_only(spdifrx->regmap, true);
+ clk_disable_unprepare(spdifrx->gclk);
+disable_pclk:
+ clk_disable_unprepare(spdifrx->pclk);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops mchp_spdifrx_pm_ops = {
+ RUNTIME_PM_OPS(mchp_spdifrx_runtime_suspend, mchp_spdifrx_runtime_resume,
+ NULL)
+};
+
+static int mchp_spdifrx_probe(struct platform_device *pdev)
+{
+ struct mchp_spdifrx_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ int irq;
+ int err;
+ u32 vers;
+
+ /* Get memory for driver data. */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* Map I/O registers. */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_spdifrx_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Request IRQ. */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_spdif_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ /* Get the peripheral clock */
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev, "failed to get the peripheral clock: %d\n",
+ err);
+ return err;
+ }
+
+ /* Get the generated clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ err = PTR_ERR(dev->gclk);
+ dev_err(&pdev->dev,
+ "failed to get the PMC generated clock: %d\n", err);
+ return err;
+ }
+
+ /*
+ * Signal control need a valid rate on gclk. hw_params() configures
+ * it propertly but requesting signal before any hw_params() has been
+ * called lead to invalid value returned for signal. Thus, configure
+ * gclk at a valid rate, here, in initialization, to simplify the
+ * control path.
+ */
+ clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1);
+
+ mutex_init(&dev->mlock);
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(dev->dev);
+ if (!pm_runtime_enabled(dev->dev)) {
+ err = mchp_spdifrx_runtime_resume(dev->dev);
+ if (err)
+ goto pm_runtime_disable;
+ }
+
+ dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR;
+ dev->capture.maxburst = 1;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ goto pm_runtime_suspend;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_spdifrx_component,
+ &mchp_spdifrx_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "fail to register dai\n");
+ goto pm_runtime_suspend;
+ }
+
+ regmap_read(regmap, SPDIFRX_VERSION, &vers);
+ dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK);
+
+ return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdifrx_runtime_suspend(dev->dev);
+pm_runtime_disable:
+ pm_runtime_disable(dev->dev);
+ return err;
+}
+
+static void mchp_spdifrx_remove(struct platform_device *pdev)
+{
+ struct mchp_spdifrx_dev *dev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(dev->dev);
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdifrx_runtime_suspend(dev->dev);
+}
+
+static struct platform_driver mchp_spdifrx_driver = {
+ .probe = mchp_spdifrx_probe,
+ .remove = mchp_spdifrx_remove,
+ .driver = {
+ .name = "mchp_spdifrx",
+ .of_match_table = mchp_spdifrx_dt_ids,
+ .pm = pm_ptr(&mchp_spdifrx_pm_ops),
+ },
+};
+
+module_platform_driver(mchp_spdifrx_driver);
+
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_DESCRIPTION("Microchip S/PDIF RX Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c
new file mode 100644
index 000000000000..245c0352c141
--- /dev/null
+++ b/sound/soc/atmel/mchp-spdiftx.c
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip S/PDIF TX Controller
+//
+// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+
+#include <sound/asoundef.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/*
+ * ---- S/PDIF Transmitter Controller Register map ----
+ */
+#define SPDIFTX_CR 0x00 /* Control Register */
+#define SPDIFTX_MR 0x04 /* Mode Register */
+#define SPDIFTX_CDR 0x0C /* Common Data Register */
+
+#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */
+#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */
+#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */
+#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */
+
+#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */
+#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */
+
+#define SPDIFTX_VERSION 0xF0
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */
+#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+/* Transmit Enable */
+#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0)
+#define SPDIFTX_MR_TXEN_DISABLE (0 << 0)
+#define SPDIFTX_MR_TXEN_ENABLE (1 << 0)
+
+/* Multichannel Transfer */
+#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1)
+#define SPDIFTX_MR_MULTICH_MONO (0 << 1)
+#define SPDIFTX_MR_MULTICH_DUAL (1 << 1)
+
+/* Data Word Endian Mode */
+#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2)
+#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2)
+#define SPDIFTX_MR_ENDIAN_BIG (1 << 2)
+
+/* Data Justification */
+#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3)
+#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3)
+#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3)
+
+/* Common Audio Register Transfer Mode */
+#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4)
+#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4)
+#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4)
+#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4)
+
+/* Valid Bits per Sample */
+#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8)
+
+/* Chunk Size */
+#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16)
+
+/* Validity Bits for Channels 1 and 2 */
+#define SPDIFTX_MR_VALID1 BIT(24)
+#define SPDIFTX_MR_VALID2 BIT(25)
+
+/* Disable Null Frame on underrun */
+#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27)
+#define SPDIFTX_MR_DNFR_INVALID (0 << 27)
+#define SPDIFTX_MR_DNFR_VALID (1 << 27)
+
+/* Bytes per Sample */
+#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
+ */
+#define SPDIFTX_IR_TXRDY BIT(0)
+#define SPDIFTX_IR_TXEMPTY BIT(1)
+#define SPDIFTX_IR_TXFULL BIT(2)
+#define SPDIFTX_IR_TXCHUNK BIT(3)
+#define SPDIFTX_IR_TXUDR BIT(4)
+#define SPDIFTX_IR_TXOVR BIT(5)
+#define SPDIFTX_IR_CSRDY BIT(6)
+#define SPDIFTX_IR_UDRDY BIT(7)
+#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8)
+#define SPDIFTX_IR_SECE BIT(10)
+#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11)
+#define SPDIFTX_IR_BEND BIT(13)
+
+static bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFTX_MR:
+ case SPDIFTX_IMR:
+ case SPDIFTX_ISR:
+ case SPDIFTX_CH1UD(0):
+ case SPDIFTX_CH1UD(1):
+ case SPDIFTX_CH1UD(2):
+ case SPDIFTX_CH1UD(3):
+ case SPDIFTX_CH1UD(4):
+ case SPDIFTX_CH1UD(5):
+ case SPDIFTX_CH1S(0):
+ case SPDIFTX_CH1S(1):
+ case SPDIFTX_CH1S(2):
+ case SPDIFTX_CH1S(3):
+ case SPDIFTX_CH1S(4):
+ case SPDIFTX_CH1S(5):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFTX_CR:
+ case SPDIFTX_MR:
+ case SPDIFTX_CDR:
+ case SPDIFTX_IER:
+ case SPDIFTX_IDR:
+ case SPDIFTX_CH1UD(0):
+ case SPDIFTX_CH1UD(1):
+ case SPDIFTX_CH1UD(2):
+ case SPDIFTX_CH1UD(3):
+ case SPDIFTX_CH1UD(4):
+ case SPDIFTX_CH1UD(5):
+ case SPDIFTX_CH1S(0):
+ case SPDIFTX_CH1S(1):
+ case SPDIFTX_CH1S(2):
+ case SPDIFTX_CH1S(3):
+ case SPDIFTX_CH1S(4):
+ case SPDIFTX_CH1S(5):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFTX_CDR:
+ case SPDIFTX_ISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mchp_spdiftx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIFTX_VERSION,
+ .readable_reg = mchp_spdiftx_readable_reg,
+ .writeable_reg = mchp_spdiftx_writeable_reg,
+ .precious_reg = mchp_spdiftx_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define SPDIFTX_GCLK_RATIO 128
+
+#define SPDIFTX_CS_BITS 192
+#define SPDIFTX_UD_BITS 192
+
+struct mchp_spdiftx_mixer_control {
+ unsigned char ch_stat[SPDIFTX_CS_BITS / 8];
+ unsigned char user_data[SPDIFTX_UD_BITS / 8];
+ spinlock_t lock; /* exclusive access to control data */
+};
+
+struct mchp_spdiftx_dev {
+ struct mchp_spdiftx_mixer_control control;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ unsigned int fmt;
+ unsigned int suspend_irq;
+};
+
+static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev)
+{
+ u32 mr;
+
+ regmap_read(dev->regmap, SPDIFTX_MR, &mr);
+ return !!(mr & SPDIFTX_MR_TXEN_ENABLE);
+}
+
+static void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev)
+{
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) {
+ val = (ctrl->ch_stat[(i * 4) + 0] << 0) |
+ (ctrl->ch_stat[(i * 4) + 1] << 8) |
+ (ctrl->ch_stat[(i * 4) + 2] << 16) |
+ (ctrl->ch_stat[(i * 4) + 3] << 24);
+
+ regmap_write(dev->regmap, SPDIFTX_CH1S(i), val);
+ }
+}
+
+static void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev)
+{
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) {
+ val = (ctrl->user_data[(i * 4) + 0] << 0) |
+ (ctrl->user_data[(i * 4) + 1] << 8) |
+ (ctrl->user_data[(i * 4) + 2] << 16) |
+ (ctrl->user_data[(i * 4) + 3] << 24);
+
+ regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val);
+ }
+}
+
+static irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id)
+{
+ struct mchp_spdiftx_dev *dev = dev_id;
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 sr, imr, pending, idr = 0;
+
+ regmap_read(dev->regmap, SPDIFTX_ISR, &sr);
+ regmap_read(dev->regmap, SPDIFTX_IMR, &imr);
+ pending = sr & imr;
+
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & SPDIFTX_IR_TXUDR) {
+ dev_warn(dev->dev, "underflow detected\n");
+ idr |= SPDIFTX_IR_TXUDR;
+ }
+
+ if (pending & SPDIFTX_IR_TXOVR) {
+ dev_warn(dev->dev, "overflow detected\n");
+ idr |= SPDIFTX_IR_TXOVR;
+ }
+
+ if (pending & SPDIFTX_IR_UDRDY) {
+ spin_lock(&ctrl->lock);
+ mchp_spdiftx_user_data_write(dev);
+ spin_unlock(&ctrl->lock);
+ idr |= SPDIFTX_IR_UDRDY;
+ }
+
+ if (pending & SPDIFTX_IR_CSRDY) {
+ spin_lock(&ctrl->lock);
+ mchp_spdiftx_channel_status_write(dev);
+ spin_unlock(&ctrl->lock);
+ idr |= SPDIFTX_IR_CSRDY;
+ }
+
+ regmap_write(dev->regmap, SPDIFTX_IDR, idr);
+
+ return IRQ_HANDLED;
+}
+
+static int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Software reset the IP */
+ regmap_write(dev->regmap, SPDIFTX_CR,
+ SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
+
+ return 0;
+}
+
+static void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Disable interrupts */
+ regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff);
+}
+
+static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ int ret;
+
+ /* do not start/stop while channel status or user data is updated */
+ spin_lock(&ctrl->lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_write(dev->regmap, SPDIFTX_IER, dev->suspend_irq |
+ SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
+ dev->suspend_irq = 0;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
+ SPDIFTX_MR_TXEN_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_read(dev->regmap, SPDIFTX_IMR, &dev->suspend_irq);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_STOP:
+ regmap_write(dev->regmap, SPDIFTX_IDR, dev->suspend_irq |
+ SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = regmap_update_bits(dev->regmap, SPDIFTX_MR, SPDIFTX_MR_TXEN_MASK,
+ SPDIFTX_MR_TXEN_DISABLE);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock(&ctrl->lock);
+ if (ret)
+ dev_err(dev->dev, "unable to start/stop TX: %d\n", ret);
+
+ return ret;
+}
+
+static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned long flags;
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ u32 mr;
+ unsigned int bps = params_physical_width(params) / 8;
+ unsigned char aes3;
+ int ret;
+
+ dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ dev_err(dev->dev, "Capture is not supported\n");
+ return -EINVAL;
+ }
+
+ regmap_read(dev->regmap, SPDIFTX_MR, &mr);
+
+ if (mr & SPDIFTX_MR_TXEN_ENABLE) {
+ dev_err(dev->dev, "PCM already running\n");
+ return -EBUSY;
+ }
+
+ /* Defaults: Toggle mode, justify to LSB, chunksize 1 */
+ mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB;
+ dev->playback.maxburst = 1;
+ switch (params_channels(params)) {
+ case 1:
+ mr |= SPDIFTX_MR_MULTICH_MONO;
+ break;
+ case 2:
+ mr |= SPDIFTX_MR_MULTICH_DUAL;
+ if (bps > 2)
+ dev->playback.maxburst = 2;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of channels: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+ mr |= FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, dev->playback.maxburst);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 16);
+ break;
+ case SNDRV_PCM_FORMAT_S18_3BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 18);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
+ break;
+ case SNDRV_PCM_FORMAT_S24_BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ mr |= SPDIFTX_MR_ENDIAN_BIG;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mr |= FIELD_PREP(SPDIFTX_MR_VBPS_MASK, 32);
+ break;
+ default:
+ dev_err(dev->dev, "unsupported PCM format: %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ mr |= FIELD_PREP(SPDIFTX_MR_BPS_MASK, bps - 1);
+
+ switch (params_rate(params)) {
+ case 22050:
+ aes3 = IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ aes3 = IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ aes3 = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ aes3 = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ aes3 = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ aes3 = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ aes3 = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ aes3 = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ aes3 = IEC958_AES3_CON_FS_192000;
+ break;
+ case 8000:
+ case 11025:
+ case 16000:
+ case 64000:
+ aes3 = IEC958_AES3_CON_FS_NOTID;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported sample frequency: %u\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&ctrl->lock, flags);
+ ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS;
+ ctrl->ch_stat[3] |= aes3;
+ mchp_spdiftx_channel_status_write(dev);
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ /* GCLK is enabled by runtime PM. */
+ clk_disable_unprepare(dev->gclk);
+
+ ret = clk_set_rate(dev->gclk, params_rate(params) *
+ SPDIFTX_GCLK_RATIO);
+ if (ret) {
+ dev_err(dev->dev,
+ "unable to change gclk rate to: rate %u * ratio %u\n",
+ params_rate(params), SPDIFTX_GCLK_RATIO);
+ return ret;
+ }
+ ret = clk_prepare_enable(dev->gclk);
+ if (ret) {
+ dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__,
+ params_rate(params) * SPDIFTX_GCLK_RATIO);
+
+ regmap_write(dev->regmap, SPDIFTX_MR, mr);
+
+ return 0;
+}
+
+static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ return regmap_write(dev->regmap, SPDIFTX_CR,
+ SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
+}
+
+#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_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 \
+ )
+
+static int mchp_spdiftx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned long flags;
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ memcpy(uvalue->value.iec958.status, ctrl->ch_stat,
+ sizeof(ctrl->ch_stat));
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+}
+
+static int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned long flags;
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ int changed = 0;
+ int i;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) {
+ if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i])
+ changed = 1;
+ ctrl->ch_stat[i] = uvalue->value.iec958.status[i];
+ }
+
+ if (changed) {
+ /* don't enable IP while we copy the channel status */
+ if (mchp_spdiftx_is_running(dev)) {
+ /*
+ * if SPDIF is running, wait for interrupt to write
+ * channel status
+ */
+ regmap_write(dev->regmap, SPDIFTX_IER,
+ SPDIFTX_IR_CSRDY);
+ } else {
+ mchp_spdiftx_channel_status_write(dev);
+ }
+ }
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return changed;
+}
+
+static int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ memset(uvalue->value.iec958.status, 0xff,
+ sizeof(uvalue->value.iec958.status));
+
+ return 0;
+}
+
+static int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ memcpy(uvalue->value.iec958.subcode, ctrl->user_data,
+ sizeof(ctrl->user_data));
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+}
+
+static int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned long flags;
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
+ int changed = 0;
+ int i;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) {
+ if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i])
+ changed = 1;
+
+ ctrl->user_data[i] = uvalue->value.iec958.subcode[i];
+ }
+ if (changed) {
+ if (mchp_spdiftx_is_running(dev)) {
+ /*
+ * if SPDIF is running, wait for interrupt to write
+ * user data
+ */
+ regmap_write(dev->regmap, SPDIFTX_IER,
+ SPDIFTX_IR_UDRDY);
+ } else {
+ mchp_spdiftx_user_data_write(dev);
+ }
+ }
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return changed;
+}
+
+static struct snd_kcontrol_new mchp_spdiftx_ctrls[] = {
+ /* Channel status controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdiftx_info,
+ .get = mchp_spdiftx_cs_get,
+ .put = mchp_spdiftx_cs_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mchp_spdiftx_info,
+ .get = mchp_spdiftx_cs_mask,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Playback Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = mchp_spdiftx_info,
+ .get = mchp_spdiftx_subcode_get,
+ .put = mchp_spdiftx_subcode_put,
+ },
+};
+
+static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, NULL);
+
+ /* Add controls */
+ snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls,
+ ARRAY_SIZE(mchp_spdiftx_ctrls));
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = {
+ .probe = mchp_spdiftx_dai_probe,
+ .startup = mchp_spdiftx_dai_startup,
+ .shutdown = mchp_spdiftx_dai_shutdown,
+ .trigger = mchp_spdiftx_trigger,
+ .hw_params = mchp_spdiftx_hw_params,
+ .hw_free = mchp_spdiftx_hw_free,
+};
+
+static struct snd_soc_dai_driver mchp_spdiftx_dai = {
+ .name = "mchp-spdiftx",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MCHP_SPDIFTX_RATES,
+ .formats = MCHP_SPDIFTX_FORMATS,
+ },
+ .ops = &mchp_spdiftx_dai_ops,
+};
+
+static const struct snd_soc_component_driver mchp_spdiftx_component = {
+ .name = "mchp-spdiftx",
+ .legacy_dai_naming = 1,
+};
+
+static const struct of_device_id mchp_spdiftx_dt_ids[] = {
+ {
+ .compatible = "microchip,sama7g5-spdiftx",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids);
+
+static int mchp_spdiftx_runtime_suspend(struct device *dev)
+{
+ struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdiftx->regmap, true);
+
+ clk_disable_unprepare(spdiftx->gclk);
+ clk_disable_unprepare(spdiftx->pclk);
+
+ return 0;
+}
+
+static int mchp_spdiftx_runtime_resume(struct device *dev)
+{
+ struct mchp_spdiftx_dev *spdiftx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdiftx->pclk);
+ if (ret) {
+ dev_err(spdiftx->dev,
+ "failed to enable the peripheral clock: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(spdiftx->gclk);
+ if (ret) {
+ dev_err(spdiftx->dev,
+ "failed to enable generic clock: %d\n", ret);
+ goto disable_pclk;
+ }
+
+ regcache_cache_only(spdiftx->regmap, false);
+ regcache_mark_dirty(spdiftx->regmap);
+ ret = regcache_sync(spdiftx->regmap);
+ if (ret) {
+ regcache_cache_only(spdiftx->regmap, true);
+ clk_disable_unprepare(spdiftx->gclk);
+disable_pclk:
+ clk_disable_unprepare(spdiftx->pclk);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops mchp_spdiftx_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(mchp_spdiftx_runtime_suspend, mchp_spdiftx_runtime_resume,
+ NULL)
+};
+
+static int mchp_spdiftx_probe(struct platform_device *pdev)
+{
+ struct mchp_spdiftx_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct mchp_spdiftx_mixer_control *ctrl;
+ int irq;
+ int err;
+
+ /* Get memory for driver data. */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* Map I/O registers. */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_spdiftx_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Request IRQ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ /* Get the peripheral clock */
+ dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(dev->pclk)) {
+ err = PTR_ERR(dev->pclk);
+ dev_err(&pdev->dev,
+ "failed to get the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ /* Get the generic clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ err = PTR_ERR(dev->gclk);
+ dev_err(&pdev->dev,
+ "failed to get the PMC generic clock: %d\n", err);
+ return err;
+ }
+
+ ctrl = &dev->control;
+ spin_lock_init(&ctrl->lock);
+
+ /* Init channel status */
+ ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT |
+ IEC958_AES0_CON_EMPHASIS_NONE;
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(dev->dev);
+ if (!pm_runtime_enabled(dev->dev)) {
+ err = mchp_spdiftx_runtime_resume(dev->dev);
+ if (err)
+ return err;
+ }
+
+ dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR;
+ dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PMC: %d\n", err);
+ goto pm_runtime_suspend;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_spdiftx_component,
+ &mchp_spdiftx_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register component: %d\n", err);
+ goto pm_runtime_suspend;
+ }
+
+ return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdiftx_runtime_suspend(dev->dev);
+ pm_runtime_disable(dev->dev);
+
+ return err;
+}
+
+static void mchp_spdiftx_remove(struct platform_device *pdev)
+{
+ struct mchp_spdiftx_dev *dev = platform_get_drvdata(pdev);
+
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdiftx_runtime_suspend(dev->dev);
+
+ pm_runtime_disable(dev->dev);
+}
+
+static struct platform_driver mchp_spdiftx_driver = {
+ .probe = mchp_spdiftx_probe,
+ .remove = mchp_spdiftx_remove,
+ .driver = {
+ .name = "mchp_spdiftx",
+ .of_match_table = mchp_spdiftx_dt_ids,
+ .pm = pm_ptr(&mchp_spdiftx_pm_ops)
+ },
+};
+
+module_platform_driver(mchp_spdiftx_driver);
+
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c
new file mode 100644
index 000000000000..8341a6e06493
--- /dev/null
+++ b/sound/soc/atmel/mikroe-proto.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASoC driver for PROTO AudioCODEC (with a WM8731)
+ *
+ * Author: Florian Meier, <koalo@koalo.de>
+ * Copyright 2013
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/wm8731.h"
+
+#define XTAL_RATE 12288000 /* This is fixed on this board */
+
+static int snd_proto_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ /* Set proto sysclk */
+ int ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+ XTAL_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Failed to set WM8731 SYSCLK: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget snd_proto_widget[] = {
+ SND_SOC_DAPM_MIC("Microphone Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route snd_proto_route[] = {
+ /* speaker connected to LHPOUT/RHPOUT */
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
+ {"MICIN", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Microphone Jack"},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_proto = {
+ .name = "snd_mikroe_proto",
+ .owner = THIS_MODULE,
+ .dapm_widgets = snd_proto_widget,
+ .num_dapm_widgets = ARRAY_SIZE(snd_proto_widget),
+ .dapm_routes = snd_proto_route,
+ .num_dapm_routes = ARRAY_SIZE(snd_proto_route),
+};
+
+static int snd_proto_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai;
+ struct snd_soc_dai_link_component *comp;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct device_node *bitclkmaster = NULL;
+ struct device_node *framemaster = NULL;
+ unsigned int dai_fmt;
+ int ret = 0;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No device node supplied\n");
+ return -EINVAL;
+ }
+
+ snd_proto.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&snd_proto, "model");
+ if (ret)
+ return ret;
+
+ dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ /* for cpus/codecs/platforms */
+ comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ snd_proto.dai_link = dai;
+ snd_proto.num_links = 1;
+
+ dai->cpus = &comp[0];
+ dai->num_cpus = 1;
+ dai->codecs = &comp[1];
+ dai->num_codecs = 1;
+ dai->platforms = &comp[2];
+ dai->num_platforms = 1;
+
+ dai->name = "WM8731";
+ dai->stream_name = "WM8731 HiFi";
+ dai->codecs->dai_name = "wm8731-hifi";
+ dai->init = &snd_proto_init;
+
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "audio-codec node missing\n");
+ return -EINVAL;
+ }
+ dai->codecs->of_node = codec_np;
+
+ cpu_np = of_parse_phandle(np, "i2s-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "i2s-controller missing\n");
+ ret = -EINVAL;
+ goto put_codec_node;
+ }
+ dai->cpus->of_node = cpu_np;
+ dai->platforms->of_node = cpu_np;
+
+ dai_fmt = snd_soc_daifmt_parse_format(np, NULL);
+ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL,
+ &bitclkmaster, &framemaster);
+ if (bitclkmaster != framemaster) {
+ dev_err(&pdev->dev, "Must be the same bitclock and frame master\n");
+ ret = -EINVAL;
+ goto put_cpu_node;
+ }
+ if (bitclkmaster) {
+ if (codec_np == bitclkmaster)
+ dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ else
+ dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ } else {
+ dai_fmt |= snd_soc_daifmt_parse_clock_provider_as_flag(np, NULL);
+ }
+
+
+ dai->dai_fmt = dai_fmt;
+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_proto);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
+
+
+put_cpu_node:
+ of_node_put(bitclkmaster);
+ of_node_put(framemaster);
+ of_node_put(cpu_np);
+put_codec_node:
+ of_node_put(codec_np);
+ return ret;
+}
+
+static const struct of_device_id snd_proto_of_match[] = {
+ { .compatible = "mikroe,mikroe-proto", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, snd_proto_of_match);
+
+static struct platform_driver snd_proto_driver = {
+ .driver = {
+ .name = "snd-mikroe-proto",
+ .of_match_table = snd_proto_of_match,
+ },
+ .probe = snd_proto_probe,
+};
+
+module_platform_driver(snd_proto_driver);
+
+MODULE_AUTHOR("Florian Meier");
+MODULE_DESCRIPTION("ASoC Driver for PROTO board (WM8731)");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index d7469cdd90dc..be54a63f43d5 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based
* ATMEL AT91SAM9G20ek board.
@@ -13,20 +14,6 @@
* Based on corgi.c by:
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand 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.
- *
- * 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>
@@ -36,7 +23,6 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/atmel-ssc.h>
@@ -59,35 +45,6 @@
*/
#undef ENABLE_MIC_INPUT
-static struct clk *mclk;
-
-static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
- struct snd_soc_dapm_context *dapm,
- enum snd_soc_bias_level level)
-{
- static int mclk_on;
- int ret = 0;
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
- if (!mclk_on)
- ret = clk_enable(mclk);
- if (ret == 0)
- mclk_on = 1;
- break;
-
- case SND_SOC_BIAS_OFF:
- case SND_SOC_BIAS_STANDBY:
- if (mclk_on)
- clk_disable(mclk);
- mclk_on = 0;
- break;
- }
-
- return ret;
-}
-
static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
@@ -109,37 +66,41 @@ static const struct snd_soc_dapm_route intercon[] = {
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct device *dev = rtd->dev;
int ret;
- printk(KERN_DEBUG
- "at91sam9g20ek_wm8731 "
- ": at91sam9g20ek_wm8731_init() called\n");
+ dev_dbg(dev, "%s called\n", __func__);
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
- MCLK_RATE, SND_SOC_CLOCK_IN);
+ MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
- printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+ dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
return ret;
}
#ifndef ENABLE_MIC_INPUT
- snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
+ snd_soc_dapm_disable_pin(snd_soc_card_to_dapm(rtd->card), "Int Mic");
#endif
return 0;
}
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_CPU("at91rm9200_ssc.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("at91rm9200_ssc.0")));
+
static struct snd_soc_dai_link at91sam9g20ek_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .cpu_dai_name = "at91rm9200_ssc.0",
- .codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
- .platform_name = "at91rm9200_ssc.0",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
+#ifndef ENABLE_MIC_INPUT
+ .playback_only = true,
+#endif
+ SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card snd_soc_at91sam9g20ek = {
@@ -147,7 +108,6 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.owner = THIS_MODULE,
.dai_link = &at91sam9g20ek_dai,
.num_links = 1,
- .set_bias_level = at91sam9g20ek_set_bias_level,
.dapm_widgets = at91sam9g20ek_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
@@ -160,7 +120,6 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *codec_np, *cpu_np;
- struct clk *pllb;
struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
int ret;
@@ -170,35 +129,10 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
ret = atmel_ssc_set_audio(0);
if (ret) {
- dev_err(&pdev->dev, "ssc channel is not valid\n");
- return -EINVAL;
- }
-
- /*
- * Codec MCLK is supplied by PCK0 - set it up.
- */
- mclk = clk_get(NULL, "pck0");
- if (IS_ERR(mclk)) {
- printk(KERN_ERR "ASoC: Failed to get MCLK\n");
- ret = PTR_ERR(mclk);
- goto err;
- }
-
- pllb = clk_get(NULL, "pllb");
- if (IS_ERR(pllb)) {
- printk(KERN_ERR "ASoC: Failed to get PLLB\n");
- ret = PTR_ERR(pllb);
- goto err_mclk;
- }
- ret = clk_set_parent(mclk, pllb);
- clk_put(pllb);
- if (ret != 0) {
- printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
- goto err_mclk;
+ dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
+ return ret;
}
- clk_set_rate(mclk, MCLK_RATE);
-
card->dev = &pdev->dev;
/* Parse device node info */
@@ -212,53 +146,51 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
goto err;
/* Parse codec info */
- at91sam9g20ek_dai.codec_name = NULL;
+ at91sam9g20ek_dai.codecs->name = NULL;
codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
if (!codec_np) {
dev_err(&pdev->dev, "codec info missing\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
- at91sam9g20ek_dai.codec_of_node = codec_np;
+ at91sam9g20ek_dai.codecs->of_node = codec_np;
/* Parse dai and platform info */
- at91sam9g20ek_dai.cpu_dai_name = NULL;
- at91sam9g20ek_dai.platform_name = NULL;
+ at91sam9g20ek_dai.cpus->dai_name = NULL;
+ at91sam9g20ek_dai.platforms->name = NULL;
cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "dai and pcm info missing\n");
- return -EINVAL;
+ of_node_put(codec_np);
+ ret = -EINVAL;
+ goto err;
}
- at91sam9g20ek_dai.cpu_of_node = cpu_np;
- at91sam9g20ek_dai.platform_of_node = cpu_np;
+ at91sam9g20ek_dai.cpus->of_node = cpu_np;
+ at91sam9g20ek_dai.platforms->of_node = cpu_np;
of_node_put(codec_np);
of_node_put(cpu_np);
ret = snd_soc_register_card(card);
if (ret) {
- printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n");
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
+ goto err;
}
- return ret;
+ return 0;
-err_mclk:
- clk_put(mclk);
- mclk = NULL;
err:
atmel_ssc_put_audio(0);
return ret;
}
-static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
+static void at91sam9g20ek_audio_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- clk_disable(mclk);
- mclk = NULL;
snd_soc_unregister_card(card);
atmel_ssc_put_audio(0);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -275,7 +207,7 @@ static struct platform_driver at91sam9g20ek_audio_driver = {
.of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
},
.probe = at91sam9g20ek_audio_probe,
- .remove = at91sam9g20ek_audio_remove,
+ .remove = at91sam9g20ek_audio_remove,
};
module_platform_driver(at91sam9g20ek_audio_driver);
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
index ccdf547f4d8c..1b5ef4e9d2b8 100644
--- a/sound/soc/atmel/sam9x5_wm8731.c
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards
* that are using WM8731 as codec.
@@ -10,12 +11,6 @@
*
* Based on sam9g20_wm8731.c by:
* Sedji Gaouaou <sedji.gaouaou@atmel.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/of.h>
#include <linux/export.h>
@@ -45,17 +40,17 @@ struct sam9x5_drvdata {
*/
static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct device *dev = rtd->dev;
int ret;
- dev_dbg(dev, "ASoC: %s called\n", __func__);
+ dev_dbg(dev, "%s called\n", __func__);
/* set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
- dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret);
+ dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
return ret;
}
@@ -82,6 +77,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_dai_link *dai;
struct sam9x5_drvdata *priv;
+ struct snd_soc_dai_link_component *comp;
int ret;
if (!np) {
@@ -92,7 +88,8 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
- if (!dai || !card || !priv) {
+ comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+ if (!dai || !card || !priv || !comp) {
ret = -ENOMEM;
goto out;
}
@@ -105,12 +102,20 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
card->num_links = 1;
card->dapm_widgets = sam9x5_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets);
+
+ dai->cpus = &comp[0];
+ dai->num_cpus = 1;
+ dai->codecs = &comp[1];
+ dai->num_codecs = 1;
+ dai->platforms = &comp[2];
+ dai->num_platforms = 1;
+
dai->name = "WM8731";
dai->stream_name = "WM8731 PCM";
- dai->codec_dai_name = "wm8731-hifi";
+ dai->codecs->dai_name = "wm8731-hifi";
dai->init = sam9x5_wm8731_init;
dai->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM;
+ | SND_SOC_DAIFMT_CBP_CFP;
ret = snd_soc_of_parse_card_name(card, "atmel,model");
if (ret) {
@@ -131,56 +136,52 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
goto out;
}
- dai->codec_of_node = codec_np;
+ dai->codecs->of_node = codec_np;
cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "atmel,ssc-controller node missing\n");
ret = -EINVAL;
- goto out;
+ goto out_put_codec_np;
}
- dai->cpu_of_node = cpu_np;
- dai->platform_of_node = cpu_np;
+ dai->cpus->of_node = cpu_np;
+ dai->platforms->of_node = cpu_np;
priv->ssc_id = of_alias_get_id(cpu_np, "ssc");
ret = atmel_ssc_set_audio(priv->ssc_id);
if (ret != 0) {
- dev_err(&pdev->dev,
- "ASoC: Failed to set SSC %d for audio: %d\n",
+ dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n",
ret, priv->ssc_id);
- goto out;
+ goto out_put_cpu_np;
}
- of_node_put(codec_np);
- of_node_put(cpu_np);
-
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
- dev_err(&pdev->dev,
- "ASoC: Platform device allocation failed\n");
+ dev_err(&pdev->dev, "Platform device allocation failed\n");
goto out_put_audio;
}
- dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__);
+ dev_dbg(&pdev->dev, "%s ok\n", __func__);
- return ret;
+ goto out_put_cpu_np;
out_put_audio:
atmel_ssc_put_audio(priv->ssc_id);
+out_put_cpu_np:
+ of_node_put(cpu_np);
+out_put_codec_np:
+ of_node_put(codec_np);
out:
return ret;
}
-static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+static void sam9x5_wm8731_driver_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct sam9x5_drvdata *priv = card->drvdata;
- snd_soc_unregister_card(card);
atmel_ssc_put_audio(priv->ssc_id);
-
- return 0;
}
static const struct of_device_id sam9x5_wm8731_of_match[] = {
diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c
new file mode 100644
index 000000000000..0b4927d07f25
--- /dev/null
+++ b/sound/soc/atmel/tse850-pcm5142.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec
+//
+// Copyright (C) 2016 Axentia Technologies AB
+//
+// Author: Peter Rosin <peda@axentia.se>
+//
+// loop1 relays
+// IN1 +---o +------------+ o---+ OUT1
+// \ /
+// + +
+// | / |
+// +--o +--. |
+// | add | |
+// | V |
+// | .---. |
+// DAC +----------->|Sum|---+
+// | '---' |
+// | |
+// + +
+//
+// IN2 +---o--+------------+--o---+ OUT2
+// loop2 relays
+//
+// The 'loop1' gpio pin controls two relays, which are either in loop
+// position, meaning that input and output are directly connected, or
+// they are in mixer position, meaning that the signal is passed through
+// the 'Sum' mixer. Similarly for 'loop2'.
+//
+// In the above, the 'loop1' relays are inactive, thus feeding IN1 to the
+// mixer (if 'add' is active) and feeding the mixer output to OUT1. The
+// 'loop2' relays are active, short-cutting the TSE-850 from channel 2.
+// IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name
+// of the (filtered) output from the PCM5142 codec.
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+struct tse850_priv {
+ struct gpio_desc *add;
+ struct gpio_desc *loop1;
+ struct gpio_desc *loop2;
+
+ struct regulator *ana;
+
+ int add_cache;
+ int loop1_cache;
+ int loop2_cache;
+};
+
+static int tse850_get_mux1(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->loop1_cache;
+
+ return 0;
+}
+
+static int tse850_put_mux1(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= e->items)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(tse850->loop1, val);
+ tse850->loop1_cache = val;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static int tse850_get_mux2(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->loop2_cache;
+
+ return 0;
+}
+
+static int tse850_put_mux2(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= e->items)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(tse850->loop2, val);
+ tse850->loop2_cache = val;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static int tse850_get_mix(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->add_cache;
+
+ return 0;
+}
+
+static int tse850_put_mix(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ int connect = !!ucontrol->value.integer.value[0];
+
+ if (tse850->add_cache == connect)
+ return 0;
+
+ /*
+ * Hmmm, this gpiod_set_value_cansleep call should probably happen
+ * inside snd_soc_dapm_mixer_update_power in the loop.
+ */
+ gpiod_set_value_cansleep(tse850->add, connect);
+ tse850->add_cache = connect;
+
+ snd_soc_dapm_mixer_update_power(dapm, kctrl, connect, NULL);
+ return 1;
+}
+
+static int tse850_get_ana(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = regulator_get_voltage(tse850->ana);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Map regulator output values like so:
+ * -11.5V to "Low" (enum 0)
+ * 11.5V-12.5V to "12V" (enum 1)
+ * 12.5V-13.5V to "13V" (enum 2)
+ * ...
+ * 18.5V-19.5V to "19V" (enum 8)
+ * 19.5V- to "20V" (enum 9)
+ */
+ if (ret < 11000000)
+ ret = 11000000;
+ else if (ret > 20000000)
+ ret = 20000000;
+ ret -= 11000000;
+ ret = (ret + 500000) / 1000000;
+
+ ucontrol->value.enumerated.item[0] = ret;
+
+ return 0;
+}
+
+static int tse850_put_ana(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kctrl);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int uV = ucontrol->value.enumerated.item[0];
+ int ret;
+
+ if (uV >= e->items)
+ return -EINVAL;
+
+ /*
+ * Map enum zero (Low) to 2 volts on the regulator, do this since
+ * the ana regulator is supplied by the system 12V voltage and
+ * requesting anything below the system voltage causes the system
+ * voltage to be passed through the regulator. Also, the ana
+ * regulator induces noise when requesting voltages near the
+ * system voltage. So, by mapping Low to 2V, that noise is
+ * eliminated when all that is needed is 12V (the system voltage).
+ */
+ if (uV)
+ uV = 11000000 + (1000000 * uV);
+ else
+ uV = 2000000;
+
+ ret = regulator_set_voltage(tse850->ana, uV, uV);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static const char * const mux_text[] = { "Mixer", "Loop" };
+
+static const struct soc_enum mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(mux_text), mux_text);
+
+static const struct snd_kcontrol_new mux1 =
+ SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1);
+
+static const struct snd_kcontrol_new mux2 =
+ SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2);
+
+static const struct snd_kcontrol_new mix[] = {
+ SOC_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0,
+ tse850_get_mix, tse850_put_mix),
+};
+
+static const char * const ana_text[] = {
+ "Low", "12V", "13V", "14V", "15V", "16V", "17V", "18V", "19V", "20V"
+};
+
+static const struct soc_enum ana_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ana_text), ana_text);
+
+static const struct snd_kcontrol_new out =
+ SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana);
+
+static const struct snd_soc_dapm_widget tse850_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("OUT1", NULL),
+ SND_SOC_DAPM_LINE("OUT2", NULL),
+ SND_SOC_DAPM_LINE("IN1", NULL),
+ SND_SOC_DAPM_LINE("IN2", NULL),
+ SND_SOC_DAPM_INPUT("DAC"),
+ 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),
+ SOC_MIXER_ARRAY("MIX", SND_SOC_NOPM, 0, 0, mix),
+ SND_SOC_DAPM_MUX("MUX1", SND_SOC_NOPM, 0, 0, &mux1),
+ SND_SOC_DAPM_MUX("MUX2", SND_SOC_NOPM, 0, 0, &mux2),
+ SND_SOC_DAPM_OUT_DRV("OUT", SND_SOC_NOPM, 0, 0, &out, 1),
+};
+
+/*
+ * These connections are not entirely correct, since both IN1 and IN2
+ * are always fed to MIX (if the "IN switch" is set so), i.e. without
+ * regard to the loop1 and loop2 relays that according to this only
+ * control MUX1 and MUX2 but in fact also control how the input signals
+ * are routed.
+ * But, 1) I don't know how to do it right, and 2) it doesn't seem to
+ * matter in practice since nothing is powered in those sections anyway.
+ */
+static const struct snd_soc_dapm_route tse850_intercon[] = {
+ { "OUT1", NULL, "MUX1" },
+ { "OUT2", NULL, "MUX2" },
+
+ { "MUX1", "Loop", "IN1" },
+ { "MUX1", "Mixer", "OUT" },
+
+ { "MUX2", "Loop", "IN2" },
+ { "MUX2", "Mixer", "OUT" },
+
+ { "OUT", NULL, "MIX" },
+
+ { "MIX", NULL, "DAC" },
+ { "MIX", "IN Switch", "IN1" },
+ { "MIX", "IN Switch", "IN2" },
+
+ /* connect board input to the codec left channel output pin */
+ { "DAC", NULL, "OUTL" },
+};
+
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "pcm512x-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link tse850_dailink = {
+ .name = "TSE-850",
+ .stream_name = "TSE-850-PCM",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFC,
+ SND_SOC_DAILINK_REG(pcm),
+};
+
+static struct snd_soc_card tse850_card = {
+ .name = "TSE-850-ASoC",
+ .owner = THIS_MODULE,
+ .dai_link = &tse850_dailink,
+ .num_links = 1,
+ .dapm_widgets = tse850_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tse850_dapm_widgets),
+ .dapm_routes = tse850_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tse850_intercon),
+ .fully_routed = true,
+};
+
+static int tse850_dt_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_dai_link *dailink = &tse850_dailink;
+
+ if (!np) {
+ dev_err(&pdev->dev, "only device tree supported\n");
+ return -EINVAL;
+ }
+
+ cpu_np = of_parse_phandle(np, "axentia,cpu-dai", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "failed to get cpu dai\n");
+ return -EINVAL;
+ }
+ dailink->cpus->of_node = cpu_np;
+ dailink->platforms->of_node = cpu_np;
+ of_node_put(cpu_np);
+
+ codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "failed to get codec info\n");
+ return -EINVAL;
+ }
+ dailink->codecs->of_node = codec_np;
+ of_node_put(codec_np);
+
+ return 0;
+}
+
+static int tse850_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &tse850_card;
+ struct device *dev = card->dev = &pdev->dev;
+ struct tse850_priv *tse850;
+ int ret;
+
+ tse850 = devm_kzalloc(dev, sizeof(*tse850), GFP_KERNEL);
+ if (!tse850)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, tse850);
+
+ ret = tse850_dt_init(pdev);
+ if (ret) {
+ dev_err(dev, "failed to init dt info\n");
+ return ret;
+ }
+
+ tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->add))
+ return dev_err_probe(dev, PTR_ERR(tse850->add),
+ "failed to get 'add' gpio\n");
+ tse850->add_cache = 1;
+
+ tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->loop1))
+ return dev_err_probe(dev, PTR_ERR(tse850->loop1),
+ "failed to get 'loop1' gpio\n");
+ tse850->loop1_cache = 1;
+
+ tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->loop2))
+ return dev_err_probe(dev, PTR_ERR(tse850->loop2),
+ "failed to get 'loop2' gpio\n");
+ tse850->loop2_cache = 1;
+
+ tse850->ana = devm_regulator_get(dev, "axentia,ana");
+ if (IS_ERR(tse850->ana))
+ return dev_err_probe(dev, PTR_ERR(tse850->ana),
+ "failed to get 'ana' regulator\n");
+
+ ret = regulator_enable(tse850->ana);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable the 'ana' regulator\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(dev, "snd_soc_register_card failed\n");
+ goto err_disable_ana;
+ }
+
+ return 0;
+
+err_disable_ana:
+ regulator_disable(tse850->ana);
+ return ret;
+}
+
+static void tse850_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+ regulator_disable(tse850->ana);
+}
+
+static const struct of_device_id tse850_dt_ids[] = {
+ { .compatible = "axentia,tse850-pcm5142", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tse850_dt_ids);
+
+static struct platform_driver tse850_driver = {
+ .driver = {
+ .name = "axentia-tse850-pcm5142",
+ .of_match_table = tse850_dt_ids,
+ },
+ .probe = tse850_probe,
+ .remove = tse850_remove,
+};
+
+module_platform_driver(tse850_driver);
+
+/* Module information */
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
index a56104040e83..a7630897bc0b 100644
--- a/sound/soc/au1x/Kconfig
+++ b/sound/soc/au1x/Kconfig
@@ -1,6 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
##
## Au1200/Au1550/Au1300 PSC + DBDMA
##
+menu "Au1x"
+
config SND_SOC_AU1XPSC
tristate "SoC Audio for Au12xx/Au13xx/Au1550"
depends on MIPS_ALCHEMY
@@ -57,8 +60,10 @@ config SND_SOC_DB1200
select SND_SOC_AC97_CODEC
select SND_SOC_WM9712
select SND_SOC_AU1XPSC_I2S
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Select this option to enable audio (AC97 and I2S) on the
Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards.
If you need Db1300 touchscreen support, you definitely want to say Y.
+
+endmenu
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
index 920710514ea0..9c6f5c38f92d 100644
--- a/sound/soc/au1x/Makefile
+++ b/sound/soc/au1x/Makefile
@@ -1,12 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
# Au1200/Au1550 PSC audio
-snd-soc-au1xpsc-dbdma-objs := dbdma2.o
-snd-soc-au1xpsc-i2s-objs := psc-i2s.o
-snd-soc-au1xpsc-ac97-objs := psc-ac97.o
+snd-soc-au1xpsc-dbdma-y := dbdma2.o
+snd-soc-au1xpsc-i2s-y := psc-i2s.o
+snd-soc-au1xpsc-ac97-y := psc-ac97.o
# Au1000/1500/1100 Audio units
-snd-soc-au1x-dma-objs := dma.o
-snd-soc-au1x-ac97c-objs := ac97c.o
-snd-soc-au1x-i2sc-objs := i2sc.o
+snd-soc-au1x-dma-y := dma.o
+snd-soc-au1x-ac97c-y := ac97c.o
+snd-soc-au1x-i2sc-y := i2sc.o
obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
@@ -16,8 +17,8 @@ obj-$(CONFIG_SND_SOC_AU1XAC97C) += snd-soc-au1x-ac97c.o
obj-$(CONFIG_SND_SOC_AU1XI2SC) += snd-soc-au1x-i2sc.o
# Boards
-snd-soc-db1000-objs := db1000.o
-snd-soc-db1200-objs := db1200.o
+snd-soc-db1000-y := db1000.o
+snd-soc-db1200-y := db1200.o
obj-$(CONFIG_SND_SOC_DB1000) += snd-soc-db1000.o
obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index 29a97d52e8ad..f8ab936250dc 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au1000/Au1500/Au1100 AC97C controller driver for ASoC
*
@@ -91,8 +92,8 @@ static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
do {
mutex_lock(&ctx->lock);
- tmo = 5;
- while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+ tmo = 6;
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
udelay(21); /* wait an ac97 frame time */
if (!tmo) {
pr_debug("ac97rd timeout #1\n");
@@ -105,7 +106,7 @@ static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
* poll, Forrest, poll...
*/
tmo = 0x10000;
- while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
asm volatile ("nop");
data = RD(ctx, AC97_CMDRESP);
@@ -194,19 +195,18 @@ static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_soc_dai_ops alchemy_ac97c_ops = {
- .startup = alchemy_ac97c_startup,
-};
-
static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
{
return ac97c_workdata ? 0 : -ENODEV;
}
+static const struct snd_soc_dai_ops alchemy_ac97c_ops = {
+ .probe = au1xac97c_dai_probe,
+ .startup = alchemy_ac97c_startup,
+};
+
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
.name = "alchemy-ac97c",
- .bus_control = true,
- .probe = au1xac97c_dai_probe,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -223,7 +223,8 @@ static struct snd_soc_dai_driver au1xac97c_dai_driver = {
};
static const struct snd_soc_component_driver au1xac97c_component = {
- .name = "au1xac97c",
+ .name = "au1xac97c",
+ .legacy_dai_naming = 1,
};
static int au1xac97c_drvprobe(struct platform_device *pdev)
@@ -247,7 +248,7 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
pdev->name))
return -EBUSY;
- ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start,
+ ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
resource_size(iores));
if (!ctx->mmio)
return -EBUSY;
@@ -284,7 +285,7 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
return 0;
}
-static int au1xac97c_drvremove(struct platform_device *pdev)
+static void au1xac97c_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
@@ -293,8 +294,6 @@ static int au1xac97c_drvremove(struct platform_device *pdev)
WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
ac97c_workdata = NULL; /* MDEV */
-
- return 0;
}
#ifdef CONFIG_PM
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
index e97c32798e98..c0e105a56cc5 100644
--- a/sound/soc/au1x/db1000.c
+++ b/sound/soc/au1x/db1000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DB1000/DB1500/DB1100 ASoC audio fabric support code.
*
@@ -18,13 +19,15 @@
#include "psc.h"
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_CPU("alchemy-ac97c")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec", "ac97-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("alchemy-pcm-dma.0")));
+
static struct snd_soc_dai_link db1000_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
- .codec_dai_name = "ac97-hifi",
- .cpu_dai_name = "alchemy-ac97c",
- .platform_name = "alchemy-pcm-dma.0",
- .codec_name = "ac97-codec",
+ SND_SOC_DAILINK_REG(hifi),
};
static struct snd_soc_card db1000_ac97 = {
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index 5c73061d912a..81abe2e18402 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* DB1200/DB1300/DB1550 ASoC audio fabric support code.
*
@@ -43,16 +44,19 @@ static const struct platform_device_id db1200_pids[] = {
},
{},
};
+MODULE_DEVICE_TABLE(platform, db1200_pids);
/*------------------------- AC97 PART ---------------------------*/
+SND_SOC_DAILINK_DEFS(db1200_ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_ac97.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec.1", "ac97-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.1")));
+
static struct snd_soc_dai_link db1200_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
- .codec_dai_name = "ac97-hifi",
- .cpu_dai_name = "au1xpsc_ac97.1",
- .platform_name = "au1xpsc-pcm.1",
- .codec_name = "ac97-codec.1",
+ SND_SOC_DAILINK_REG(db1200_ac97),
};
static struct snd_soc_card db1200_ac97_machine = {
@@ -62,13 +66,15 @@ static struct snd_soc_card db1200_ac97_machine = {
.num_links = 1,
};
+SND_SOC_DAILINK_DEFS(db1300_ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_ac97.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec.1", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.1")));
+
static struct snd_soc_dai_link db1300_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
- .codec_dai_name = "wm9712-hifi",
- .cpu_dai_name = "au1xpsc_ac97.1",
- .platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm9712-codec.1",
+ SND_SOC_DAILINK_REG(db1300_ac97),
};
static struct snd_soc_card db1300_ac97_machine = {
@@ -89,8 +95,8 @@ static struct snd_soc_card db1550_ac97_machine = {
static int db1200_i2s_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
/* WM8731 has its own 12MHz crystal */
snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
@@ -99,20 +105,22 @@ static int db1200_i2s_startup(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_soc_ops db1200_i2s_wm8731_ops = {
+static const struct snd_soc_ops db1200_i2s_wm8731_ops = {
.startup = db1200_i2s_startup,
};
+SND_SOC_DAILINK_DEFS(db1200_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_i2s.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.1")));
+
static struct snd_soc_dai_link db1200_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .codec_dai_name = "wm8731-hifi",
- .cpu_dai_name = "au1xpsc_i2s.1",
- .platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1200_i2s),
};
static struct snd_soc_card db1200_i2s_machine = {
@@ -122,16 +130,18 @@ static struct snd_soc_card db1200_i2s_machine = {
.num_links = 1,
};
+SND_SOC_DAILINK_DEFS(db1300_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_i2s.2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.2")));
+
static struct snd_soc_dai_link db1300_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .codec_dai_name = "wm8731-hifi",
- .cpu_dai_name = "au1xpsc_i2s.2",
- .platform_name = "au1xpsc-pcm.2",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1300_i2s),
};
static struct snd_soc_card db1300_i2s_machine = {
@@ -141,16 +151,18 @@ static struct snd_soc_card db1300_i2s_machine = {
.num_links = 1,
};
+SND_SOC_DAILINK_DEFS(db1550_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("au1xpsc_i2s.3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("au1xpsc-pcm.3")));
+
static struct snd_soc_dai_link db1550_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .codec_dai_name = "wm8731-hifi",
- .cpu_dai_name = "au1xpsc_i2s.3",
- .platform_name = "au1xpsc-pcm.3",
- .codec_name = "wm8731.0-001b",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &db1200_i2s_wm8731_ops,
+ SND_SOC_DAILINK_REG(db1550_i2s),
};
static struct snd_soc_card db1550_i2s_machine = {
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 5741c0aa6c03..3392693faeb9 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@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.
- *
* DMA glue for Au1x-PSC audio.
- *
*/
@@ -32,6 +28,8 @@
/*#define PCM_DEBUG*/
+#define DRV_NAME "dbdma2"
+
#define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x)
#ifdef PCM_DEBUG
#define DBG MSG
@@ -184,30 +182,26 @@ out:
return 0;
}
-static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
+static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- struct au1xpsc_audio_dmadata *pcd =
- snd_soc_platform_get_drvdata(rtd->platform);
+ struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component);
return &pcd[ss->stream];
}
-static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+static int au1xpsc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct au1xpsc_audio_dmadata *pcd;
int stype, ret;
- ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (ret < 0)
- goto out;
-
stype = substream->stream;
- pcd = to_dmadata(substream);
+ pcd = to_dmadata(substream, component);
- DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
- "runtime->min_align %d\n",
+ DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
+ "runtime->min_align %lu\n",
(unsigned long)runtime->dma_area,
(unsigned long)runtime->dma_addr, runtime->dma_bytes,
runtime->min_align);
@@ -234,15 +228,10 @@ out:
return ret;
}
-static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
au1xxx_dbdma_reset(pcd->ddma_chan);
@@ -257,9 +246,10 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int au1xpsc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- u32 c = to_dmadata(substream)->ddma_chan;
+ u32 c = to_dmadata(substream, component)->ddma_chan;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -277,18 +267,21 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
-au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+au1xpsc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
+ return bytes_to_frames(substream->runtime,
+ to_dmadata(substream, component)->pos);
}
-static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
int stype = substream->stream, *dmaids;
- dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dmaids = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!dmaids)
return -ENODEV; /* whoa, has ordering changed? */
@@ -298,54 +291,51 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+static int au1xpsc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- au1x_pcm_dbdma_free(to_dmadata(substream));
+ au1x_pcm_dbdma_free(to_dmadata(substream, component));
return 0;
}
-static struct snd_pcm_ops au1xpsc_pcm_ops = {
- .open = au1xpsc_pcm_open,
- .close = au1xpsc_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = au1xpsc_pcm_hw_params,
- .hw_free = au1xpsc_pcm_hw_free,
- .prepare = au1xpsc_pcm_prepare,
- .trigger = au1xpsc_pcm_trigger,
- .pointer = au1xpsc_pcm_pointer,
-};
-
-static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int au1xpsc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
return 0;
}
/* au1xpsc audio platform */
-static struct snd_soc_platform_driver au1xpsc_soc_platform = {
- .ops = &au1xpsc_pcm_ops,
- .pcm_new = au1xpsc_pcm_new,
+static const struct snd_soc_component_driver au1xpsc_soc_component = {
+ .name = DRV_NAME,
+ .open = au1xpsc_pcm_open,
+ .close = au1xpsc_pcm_close,
+ .hw_params = au1xpsc_pcm_hw_params,
+ .prepare = au1xpsc_pcm_prepare,
+ .trigger = au1xpsc_pcm_trigger,
+ .pointer = au1xpsc_pcm_pointer,
+ .pcm_construct = au1xpsc_pcm_new,
};
static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
{
struct au1xpsc_audio_dmadata *dmadata;
- dmadata = devm_kzalloc(&pdev->dev,
- 2 * sizeof(struct au1xpsc_audio_dmadata),
+ dmadata = devm_kcalloc(&pdev->dev,
+ 2, sizeof(struct au1xpsc_audio_dmadata),
GFP_KERNEL);
if (!dmadata)
return -ENOMEM;
platform_set_drvdata(pdev, dmadata);
- return devm_snd_soc_register_platform(&pdev->dev,
- &au1xpsc_soc_platform);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &au1xpsc_soc_component, NULL, 0);
}
static struct platform_driver au1xpsc_pcm_driver = {
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
index fcf5a9adde81..c9c2b1e71d55 100644
--- a/sound/soc/au1x/dma.c
+++ b/sound/soc/au1x/dma.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au1000/Au1500/Au1100 Audio DMA support.
*
@@ -21,6 +22,8 @@
#include "psc.h"
+#define DRV_NAME "au1x_dma"
+
struct pcm_period {
u32 start;
u32 relative_end; /* relative to start of buffer */
@@ -171,26 +174,28 @@ static const struct snd_pcm_hardware alchemy_pcm_hardware = {
.fifo_size = 16,
};
-static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
+static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- return snd_soc_platform_get_drvdata(rtd->platform);
+ return snd_soc_component_get_drvdata(component);
}
-static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
+static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss,
+ struct snd_soc_component *component)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss, component);
return &(ctx->stream[ss->stream]);
}
-static int alchemy_pcm_open(struct snd_pcm_substream *substream)
+static int alchemy_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
int *dmaids, s = substream->stream;
char *name;
- dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dmaids = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
if (!dmaids)
return -ENODEV; /* whoa, has ordering changed? */
@@ -209,9 +214,10 @@ static int alchemy_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int alchemy_pcm_close(struct snd_pcm_substream *substream)
+static int alchemy_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
+ struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
int stype = substream->stream;
ctx->stream[stype].substream = NULL;
@@ -220,35 +226,29 @@ static int alchemy_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
+static int alchemy_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct audio_stream *stream = ss_to_as(substream);
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- err = au1000_setup_dma_link(stream,
- params_period_bytes(hw_params),
- params_periods(hw_params));
- if (err)
- snd_pcm_lib_free_pages(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
- return err;
+ return au1000_setup_dma_link(stream,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
}
-static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
+static int alchemy_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct audio_stream *stream = ss_to_as(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
au1000_release_dma_link(stream);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
-static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int alchemy_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- struct audio_stream *stream = ss_to_as(substream);
+ struct audio_stream *stream = ss_to_as(substream, component);
int err = 0;
switch (cmd) {
@@ -265,9 +265,10 @@ static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return err;
}
-static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
+static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
- struct audio_stream *stream = ss_to_as(ss);
+ struct audio_stream *stream = ss_to_as(ss, component);
long location;
location = get_dma_residue(stream->dma);
@@ -277,29 +278,26 @@ static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, location);
}
-static struct snd_pcm_ops alchemy_pcm_ops = {
- .open = alchemy_pcm_open,
- .close = alchemy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = alchemy_pcm_hw_params,
- .hw_free = alchemy_pcm_hw_free,
- .trigger = alchemy_pcm_trigger,
- .pointer = alchemy_pcm_pointer,
-};
-
-static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int alchemy_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 65536, (4096 * 1024) - 1);
return 0;
}
-static struct snd_soc_platform_driver alchemy_pcm_soc_platform = {
- .ops = &alchemy_pcm_ops,
- .pcm_new = alchemy_pcm_new,
+static const struct snd_soc_component_driver alchemy_pcm_soc_component = {
+ .name = DRV_NAME,
+ .open = alchemy_pcm_open,
+ .close = alchemy_pcm_close,
+ .hw_params = alchemy_pcm_hw_params,
+ .hw_free = alchemy_pcm_hw_free,
+ .trigger = alchemy_pcm_trigger,
+ .pointer = alchemy_pcm_pointer,
+ .pcm_construct = alchemy_pcm_new,
};
static int alchemy_pcm_drvprobe(struct platform_device *pdev)
@@ -312,8 +310,8 @@ static int alchemy_pcm_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- return devm_snd_soc_register_platform(&pdev->dev,
- &alchemy_pcm_soc_platform);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &alchemy_pcm_soc_component, NULL, 0);
}
static struct platform_driver alchemy_pcmdma_driver = {
diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c
index 450c842c776c..57735004f416 100644
--- a/sound/soc/au1x/i2sc.c
+++ b/sound/soc/au1x/i2sc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au1000/Au1500/Au1100 I2S controller driver for ASoC
*
@@ -118,9 +119,9 @@ static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
goto out;
}
- /* I2S controller only supports master */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
+ /* I2S controller only supports provider */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP: /* CODEC consumer */
break;
default:
goto out;
@@ -209,7 +210,7 @@ static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
};
static struct snd_soc_dai_driver au1xi2s_dai_driver = {
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.playback = {
.rates = AU1XI2SC_RATES,
.formats = AU1XI2SC_FMTS,
@@ -226,7 +227,8 @@ static struct snd_soc_dai_driver au1xi2s_dai_driver = {
};
static const struct snd_soc_component_driver au1xi2s_component = {
- .name = "au1xi2s",
+ .name = "au1xi2s",
+ .legacy_dai_naming = 1,
};
static int au1xi2s_drvprobe(struct platform_device *pdev)
@@ -247,7 +249,7 @@ static int au1xi2s_drvprobe(struct platform_device *pdev)
pdev->name))
return -EBUSY;
- ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start,
+ ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
resource_size(iores));
if (!ctx->mmio)
return -EBUSY;
@@ -268,18 +270,15 @@ static int au1xi2s_drvprobe(struct platform_device *pdev)
&au1xi2s_dai_driver, 1);
}
-static int au1xi2s_drvremove(struct platform_device *pdev)
+static void au1xi2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
snd_soc_unregister_component(&pdev->dev);
WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
-
- return 0;
}
-#ifdef CONFIG_PM
static int au1xi2s_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
@@ -294,23 +293,13 @@ static int au1xi2s_drvresume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops au1xi2sc_pmops = {
- .suspend = au1xi2s_drvsuspend,
- .resume = au1xi2s_drvresume,
-};
-
-#define AU1XI2SC_PMOPS (&au1xi2sc_pmops)
-
-#else
-
-#define AU1XI2SC_PMOPS NULL
-
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(au1xi2sc_pmops, au1xi2s_drvsuspend,
+ au1xi2s_drvresume);
static struct platform_driver au1xi2s_driver = {
.driver = {
.name = "alchemy-i2sc",
- .pm = AU1XI2SC_PMOPS,
+ .pm = pm_ptr(&au1xi2sc_pmops),
},
.probe = au1xi2s_drvprobe,
.remove = au1xi2s_drvremove,
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index bb53c7059005..94698e08a513 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2009 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@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.
- *
* Au1xxx-PSC AC97 glue.
- *
*/
#include <linux/init.h>
@@ -62,7 +58,7 @@ static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
static inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x)
{
struct snd_soc_card *c = x->bus->card->private_data;
- return snd_soc_dai_get_drvdata(c->rtd->cpu_dai);
+ return snd_soc_dai_get_drvdata(c->snd_soc_rtd_to_cpu(rtd, 0));
}
#else
@@ -337,14 +333,13 @@ static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
}
static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+ .probe = au1xpsc_ac97_probe,
.startup = au1xpsc_ac97_startup,
.trigger = au1xpsc_ac97_trigger,
.hw_params = au1xpsc_ac97_hw_params,
};
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
- .bus_control = true,
- .probe = au1xpsc_ac97_probe,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -361,13 +356,14 @@ static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
};
static const struct snd_soc_component_driver au1xpsc_ac97_component = {
- .name = "au1xpsc-ac97",
+ .name = "au1xpsc-ac97",
+ .legacy_dai_naming = 1,
};
static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
int ret;
- struct resource *iores, *dmares;
+ struct resource *dmares;
unsigned long sel;
struct au1xpsc_audio_data *wd;
@@ -378,8 +374,7 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
mutex_init(&wd->lock);
- iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+ wd->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wd->mmio))
return PTR_ERR(wd->mmio);
@@ -426,7 +421,7 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
return 0;
}
-static int au1xpsc_ac97_drvremove(struct platform_device *pdev)
+static void au1xpsc_ac97_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
@@ -439,11 +434,8 @@ static int au1xpsc_ac97_drvremove(struct platform_device *pdev)
wmb(); /* drain writebuffer */
au1xpsc_ac97_workdata = NULL; /* MDEV */
-
- return 0;
}
-#ifdef CONFIG_PM
static int au1xpsc_ac97_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
@@ -474,23 +466,13 @@ static int au1xpsc_ac97_drvresume(struct device *dev)
return 0;
}
-static struct dev_pm_ops au1xpscac97_pmops = {
- .suspend = au1xpsc_ac97_drvsuspend,
- .resume = au1xpsc_ac97_drvresume,
-};
-
-#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
-
-#else
-
-#define AU1XPSCAC97_PMOPS NULL
-
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(au1xpscac97_pmops, au1xpsc_ac97_drvsuspend,
+ au1xpsc_ac97_drvresume);
static struct platform_driver au1xpsc_ac97_driver = {
.driver = {
.name = "au1xpsc_ac97",
- .pm = AU1XPSCAC97_PMOPS,
+ .pm = pm_ptr(&au1xpscac97_pmops),
},
.probe = au1xpsc_ac97_drvprobe,
.remove = au1xpsc_ac97_drvremove,
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index 0bf9d62b91a0..bf59105fcb7a 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@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.
- *
* Au1xxx-PSC I2S glue.
*
* NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
@@ -93,12 +90,12 @@ static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
goto out;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* CODEC master */
- ct |= PSC_I2SCFG_MS; /* PSC I2S slave mode */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC: /* CODEC provider */
+ ct |= PSC_I2SCFG_MS; /* PSC I2S consumer mode */
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
- ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */
+ case SND_SOC_DAIFMT_BP_FP: /* CODEC consumer */
+ ct &= ~PSC_I2SCFG_MS; /* PSC I2S provider mode */
break;
default:
goto out;
@@ -289,12 +286,13 @@ static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = {
};
static const struct snd_soc_component_driver au1xpsc_i2s_component = {
- .name = "au1xpsc-i2s",
+ .name = "au1xpsc-i2s",
+ .legacy_dai_naming = 1,
};
static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
- struct resource *iores, *dmares;
+ struct resource *dmares;
unsigned long sel;
struct au1xpsc_audio_data *wd;
@@ -303,8 +301,7 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
if (!wd)
return -ENOMEM;
- iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+ wd->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wd->mmio))
return PTR_ERR(wd->mmio);
@@ -343,25 +340,20 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, wd);
- return snd_soc_register_component(&pdev->dev, &au1xpsc_i2s_component,
- &wd->dai_drv, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &au1xpsc_i2s_component, &wd->dai_drv, 1);
}
-static int au1xpsc_i2s_drvremove(struct platform_device *pdev)
+static void au1xpsc_i2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
- snd_soc_unregister_component(&pdev->dev);
-
__raw_writel(0, I2S_CFG(wd));
wmb(); /* drain writebuffer */
__raw_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
wmb(); /* drain writebuffer */
-
- return 0;
}
-#ifdef CONFIG_PM
static int au1xpsc_i2s_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
@@ -392,23 +384,13 @@ static int au1xpsc_i2s_drvresume(struct device *dev)
return 0;
}
-static struct dev_pm_ops au1xpsci2s_pmops = {
- .suspend = au1xpsc_i2s_drvsuspend,
- .resume = au1xpsc_i2s_drvresume,
-};
-
-#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
-
-#else
-
-#define AU1XPSCI2S_PMOPS NULL
-
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(au1xpsci2s_pmops, au1xpsc_i2s_drvsuspend,
+ au1xpsc_i2s_drvresume);
static struct platform_driver au1xpsc_i2s_driver = {
.driver = {
.name = "au1xpsc_i2s",
- .pm = AU1XPSCI2S_PMOPS,
+ .pm = pm_ptr(&au1xpsci2s_pmops),
},
.probe = au1xpsc_i2s_drvprobe,
.remove = au1xpsc_i2s_drvremove,
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index 74dffeb641fa..216596e4348a 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Alchemy ALSA ASoC audio support.
*
* (c) 2007-2011 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@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.
- *
*/
#ifndef _AU1X_PCM_H
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index 6a834e109f1d..de4e8a0daf1c 100644
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Broadcom"
+
config SND_BCM2835_SOC_I2S
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
depends on ARCH_BCM2835 || COMPILE_TEST
@@ -7,3 +10,23 @@ config SND_BCM2835_SOC_I2S
Say Y or M if you want to add support for codecs attached to
the BCM2835 I2S interface. You will also need
to select the audio interfaces to support below.
+
+config SND_SOC_CYGNUS
+ tristate "SoC platform audio for Broadcom Cygnus chips"
+ depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+ help
+ Say Y if you want to add support for ASoC audio on Broadcom
+ Cygnus chips (bcm958300, bcm958305, bcm911360)
+
+ If you don't know what to do here, say N.
+
+config SND_BCM63XX_I2S_WHISTLER
+ tristate "SoC Audio support for the Broadcom BCM63XX I2S module"
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for ASoC audio on Broadcom
+ DSL/PON chips (bcm63158, bcm63178)
+
+ If you don't know what to do here, say N
+
+endmenu
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
index bc816b71e5a4..0c1325a97b70 100644
--- a/sound/soc/bcm/Makefile
+++ b/sound/soc/bcm/Makefile
@@ -1,5 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
# BCM2835 Platform Support
-snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
+snd-soc-bcm2835-i2s-y := bcm2835-i2s.o
obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
+# CYGNUS Platform Support
+snd-soc-cygnus-y := cygnus-pcm.o cygnus-ssp.o
+
+obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
+
+# BCM63XX Platform Support
+snd-soc-63xx-y := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o
+
+obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o \ No newline at end of file
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
index 3303d5f58082..87d2f06c2f53 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC I2S Audio Layer for Broadcom BCM2835 SoC
*
@@ -20,23 +21,16 @@
* Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
* Author: Timur Tabi <timur@freescale.com>
* Copyright 2007-2010 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.
- *
- * 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/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -46,55 +40,6 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
-/* Clock registers */
-#define BCM2835_CLK_PCMCTL_REG 0x00
-#define BCM2835_CLK_PCMDIV_REG 0x04
-
-/* Clock register settings */
-#define BCM2835_CLK_PASSWD (0x5a000000)
-#define BCM2835_CLK_PASSWD_MASK (0xff000000)
-#define BCM2835_CLK_MASH(v) ((v) << 9)
-#define BCM2835_CLK_FLIP BIT(8)
-#define BCM2835_CLK_BUSY BIT(7)
-#define BCM2835_CLK_KILL BIT(5)
-#define BCM2835_CLK_ENAB BIT(4)
-#define BCM2835_CLK_SRC(v) (v)
-
-#define BCM2835_CLK_SHIFT (12)
-#define BCM2835_CLK_DIVI(v) ((v) << BCM2835_CLK_SHIFT)
-#define BCM2835_CLK_DIVF(v) (v)
-#define BCM2835_CLK_DIVF_MASK (0xFFF)
-
-enum {
- BCM2835_CLK_MASH_0 = 0,
- BCM2835_CLK_MASH_1,
- BCM2835_CLK_MASH_2,
- BCM2835_CLK_MASH_3,
-};
-
-enum {
- BCM2835_CLK_SRC_GND = 0,
- BCM2835_CLK_SRC_OSC,
- BCM2835_CLK_SRC_DBG0,
- BCM2835_CLK_SRC_DBG1,
- BCM2835_CLK_SRC_PLLA,
- BCM2835_CLK_SRC_PLLC,
- BCM2835_CLK_SRC_PLLD,
- BCM2835_CLK_SRC_HDMI,
-};
-
-/* Most clocks are not useable (freq = 0) */
-static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
- [BCM2835_CLK_SRC_GND] = 0,
- [BCM2835_CLK_SRC_OSC] = 19200000,
- [BCM2835_CLK_SRC_DBG0] = 0,
- [BCM2835_CLK_SRC_DBG1] = 0,
- [BCM2835_CLK_SRC_PLLA] = 0,
- [BCM2835_CLK_SRC_PLLC] = 0,
- [BCM2835_CLK_SRC_PLLD] = 500000000,
- [BCM2835_CLK_SRC_HDMI] = 0,
-};
-
/* I2S registers */
#define BCM2835_I2S_CS_A_REG 0x00
#define BCM2835_I2S_FIFO_A_REG 0x04
@@ -147,6 +92,8 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
#define BCM2835_I2S_CHWID(v) (v)
#define BCM2835_I2S_CH1(v) ((v) << 16)
#define BCM2835_I2S_CH2(v) (v)
+#define BCM2835_I2S_CH1_POS(v) BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(v))
+#define BCM2835_I2S_CH2_POS(v) BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(v))
#define BCM2835_I2S_TX_PANIC(v) ((v) << 24)
#define BCM2835_I2S_RX_PANIC(v) ((v) << 16)
@@ -158,32 +105,38 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
#define BCM2835_I2S_INT_RXR BIT(1)
#define BCM2835_I2S_INT_TXW BIT(0)
-/* I2S DMA interface */
-/* FIXME: Needs IOMMU support */
-#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000)
+/* Frame length register is 10 bit, maximum length 1024 */
+#define BCM2835_I2S_MAX_FRAME_LENGTH 1024
/* General device struct */
struct bcm2835_i2s_dev {
struct device *dev;
struct snd_dmaengine_dai_dma_data dma_data[2];
unsigned int fmt;
- unsigned int bclk_ratio;
-
- struct regmap *i2s_regmap;
- struct regmap *clk_regmap;
+ unsigned int tdm_slots;
+ unsigned int rx_mask;
+ unsigned int tx_mask;
+ unsigned int slot_width;
+ unsigned int frame_length;
+
+ struct regmap *i2s_regmap;
+ struct clk *clk;
+ bool clk_prepared;
+ int clk_rate;
};
static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
{
- /* Start the clock if in master mode */
- unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
-
- switch (master) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
- BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
+ unsigned int provider = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+
+ if (dev->clk_prepared)
+ return;
+
+ switch (provider) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ clk_prepare_enable(dev->clk);
+ dev->clk_prepared = true;
break;
default:
break;
@@ -192,28 +145,9 @@ static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
{
- uint32_t clkreg;
- int timeout = 1000;
-
- /* Stop clock */
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
- BCM2835_CLK_PASSWD);
-
- /* Wait for the BUSY flag going down */
- while (--timeout) {
- regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
- if (!(clkreg & BCM2835_CLK_BUSY))
- break;
- }
-
- if (!timeout) {
- /* KILL the clock */
- dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n");
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK,
- BCM2835_CLK_KILL | BCM2835_CLK_PASSWD);
- }
+ if (dev->clk_prepared)
+ clk_disable_unprepare(dev->clk);
+ dev->clk_prepared = false;
}
static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
@@ -223,8 +157,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
uint32_t syncval;
uint32_t csreg;
uint32_t i2s_active_state;
- uint32_t clkreg;
- uint32_t clk_active_state;
+ bool clk_was_prepared;
uint32_t off;
uint32_t clr;
@@ -238,15 +171,10 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON);
- regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
- clk_active_state = clkreg & BCM2835_CLK_ENAB;
-
/* Start clock if not running */
- if (!clk_active_state) {
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
- BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
- }
+ clk_was_prepared = dev->clk_prepared;
+ if (!clk_was_prepared)
+ bcm2835_i2s_start_clock(dev);
/* Stop I2S module */
regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0);
@@ -280,7 +208,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
dev_err(dev->dev, "I2S SYNC error!\n");
/* Stop clock if it was not running before */
- if (!clk_active_state)
+ if (!clk_was_prepared)
bcm2835_i2s_stop_clock(dev);
/* Restore I2S state */
@@ -300,29 +228,120 @@ static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai,
unsigned int ratio)
{
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- dev->bclk_ratio = ratio;
+
+ if (!ratio) {
+ dev->tdm_slots = 0;
+ return 0;
+ }
+
+ if (ratio > BCM2835_I2S_MAX_FRAME_LENGTH)
+ return -EINVAL;
+
+ dev->tdm_slots = 2;
+ dev->rx_mask = 0x03;
+ dev->tx_mask = 0x03;
+ dev->slot_width = ratio / 2;
+ dev->frame_length = ratio;
+
+ return 0;
+}
+
+static int bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int width)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ if (slots) {
+ if (slots < 0 || width < 0)
+ return -EINVAL;
+
+ /* Limit masks to available slots */
+ rx_mask &= GENMASK(slots - 1, 0);
+ tx_mask &= GENMASK(slots - 1, 0);
+
+ /*
+ * The driver is limited to 2-channel setups.
+ * Check that exactly 2 bits are set in the masks.
+ */
+ if (hweight_long((unsigned long) rx_mask) != 2
+ || hweight_long((unsigned long) tx_mask) != 2)
+ return -EINVAL;
+
+ if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH)
+ return -EINVAL;
+ }
+
+ dev->tdm_slots = slots;
+
+ dev->rx_mask = rx_mask;
+ dev->tx_mask = tx_mask;
+ dev->slot_width = width;
+ dev->frame_length = slots * width;
+
return 0;
}
+/*
+ * Convert logical slot number into physical slot number.
+ *
+ * If odd_offset is 0 sequential number is identical to logical number.
+ * This is used for DSP modes with slot numbering 0 1 2 3 ...
+ *
+ * Otherwise odd_offset defines the physical offset for odd numbered
+ * slots. This is used for I2S and left/right justified modes to
+ * translate from logical slot numbers 0 1 2 3 ... into physical slot
+ * numbers 0 2 ... 3 4 ...
+ */
+static int bcm2835_i2s_convert_slot(unsigned int slot, unsigned int odd_offset)
+{
+ if (!odd_offset)
+ return slot;
+
+ if (slot & 1)
+ return (slot >> 1) + odd_offset;
+
+ return slot >> 1;
+}
+
+/*
+ * Calculate channel position from mask and slot width.
+ *
+ * Mask must contain exactly 2 set bits.
+ * Lowest set bit is channel 1 position, highest set bit channel 2.
+ * The constant offset is added to both channel positions.
+ *
+ * If odd_offset is > 0 slot positions are translated to
+ * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd
+ * logical slot numbers starting at physical slot odd_offset.
+ */
+static void bcm2835_i2s_calc_channel_pos(
+ unsigned int *ch1_pos, unsigned int *ch2_pos,
+ unsigned int mask, unsigned int width,
+ unsigned int bit_offset, unsigned int odd_offset)
+{
+ *ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset)
+ * width + bit_offset;
+ *ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset)
+ * width + bit_offset;
+}
+
static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- unsigned int sampling_rate = params_rate(params);
- unsigned int data_length, data_delay, bclk_ratio;
- unsigned int ch1pos, ch2pos, mode, format;
- unsigned int mash = BCM2835_CLK_MASH_1;
- unsigned int divi, divf, target_frequency;
- int clk_src = -1;
- unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
- bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS
- || master == SND_SOC_DAIFMT_CBS_CFM);
-
- bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS
- || master == SND_SOC_DAIFMT_CBM_CFS);
+ unsigned int data_length, data_delay, framesync_length;
+ unsigned int slots, slot_width, odd_slot_offset;
+ int frame_length, bclk_rate;
+ unsigned int rx_mask, tx_mask;
+ unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos;
+ unsigned int mode, format;
+ bool bit_clock_provider = false;
+ bool frame_sync_provider = false;
+ bool frame_start_falling_edge = false;
uint32_t csreg;
+ int ret = 0;
/*
* If a stream is already enabled,
@@ -333,138 +352,177 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON))
return 0;
- /*
- * Adjust the data length according to the format.
- * We prefill the half frame length with an integer
- * divider of 2400 as explained at the clock settings.
- * Maybe it is overwritten there, if the Integer mode
- * does not apply.
- */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- data_length = 16;
- bclk_ratio = 40;
+ data_length = params_width(params);
+ data_delay = 0;
+ odd_slot_offset = 0;
+ mode = 0;
+
+ if (dev->tdm_slots) {
+ slots = dev->tdm_slots;
+ slot_width = dev->slot_width;
+ frame_length = dev->frame_length;
+ rx_mask = dev->rx_mask;
+ tx_mask = dev->tx_mask;
+ bclk_rate = dev->frame_length * params_rate(params);
+ } else {
+ slots = 2;
+ slot_width = params_width(params);
+ rx_mask = 0x03;
+ tx_mask = 0x03;
+
+ frame_length = snd_soc_params_to_frame_size(params);
+ if (frame_length < 0)
+ return frame_length;
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0)
+ return bclk_rate;
+ }
+
+ /* Check if data fits into slots */
+ if (data_length > slot_width)
+ return -EINVAL;
+
+ /* Check if CPU is bit clock provider */
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ bit_clock_provider = true;
break;
- case SNDRV_PCM_FORMAT_S32_LE:
- data_length = 32;
- bclk_ratio = 80;
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BC_FC:
+ bit_clock_provider = false;
break;
default:
return -EINVAL;
}
- /* If bclk_ratio already set, use that one. */
- if (dev->bclk_ratio)
- bclk_ratio = dev->bclk_ratio;
+ /* Check if CPU is frame sync provider */
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ case SND_SOC_DAIFMT_BC_FP:
+ frame_sync_provider = true;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ case SND_SOC_DAIFMT_BC_FC:
+ frame_sync_provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
- /*
- * Clock Settings
- *
- * The target frequency of the bit clock is
- * sampling rate * frame length
- *
- * Integer mode:
- * Sampling rates that are multiples of 8000 kHz
- * can be driven by the oscillator of 19.2 MHz
- * with an integer divider as long as the frame length
- * is an integer divider of 19200000/8000=2400 as set up above.
- * This is no longer possible if the sampling rate
- * is too high (e.g. 192 kHz), because the oscillator is too slow.
- *
- * MASH mode:
- * For all other sampling rates, it is not possible to
- * have an integer divider. Approximate the clock
- * with the MASH module that induces a slight frequency
- * variance. To minimize that it is best to have the fastest
- * clock here. That is PLLD with 500 MHz.
- */
- target_frequency = sampling_rate * bclk_ratio;
- clk_src = BCM2835_CLK_SRC_OSC;
- mash = BCM2835_CLK_MASH_0;
-
- if (bcm2835_clk_freq[clk_src] % target_frequency == 0
- && bit_master && frame_master) {
- divi = bcm2835_clk_freq[clk_src] / target_frequency;
- divf = 0;
- } else {
- uint64_t dividend;
-
- if (!dev->bclk_ratio) {
- /*
- * Overwrite bclk_ratio, because the
- * above trick is not needed or can
- * not be used.
- */
- bclk_ratio = 2 * data_length;
+ /* Clock should only be set up here if CPU is clock master */
+ if (bit_clock_provider &&
+ (!dev->clk_prepared || dev->clk_rate != bclk_rate)) {
+ if (dev->clk_prepared)
+ bcm2835_i2s_stop_clock(dev);
+
+ if (dev->clk_rate != bclk_rate) {
+ ret = clk_set_rate(dev->clk, bclk_rate);
+ if (ret)
+ return ret;
+ dev->clk_rate = bclk_rate;
}
- target_frequency = sampling_rate * bclk_ratio;
-
- clk_src = BCM2835_CLK_SRC_PLLD;
- mash = BCM2835_CLK_MASH_1;
-
- dividend = bcm2835_clk_freq[clk_src];
- dividend <<= BCM2835_CLK_SHIFT;
- do_div(dividend, target_frequency);
- divi = dividend >> BCM2835_CLK_SHIFT;
- divf = dividend & BCM2835_CLK_DIVF_MASK;
+ bcm2835_i2s_start_clock(dev);
}
- /* Set clock divider */
- regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
- | BCM2835_CLK_DIVI(divi)
- | BCM2835_CLK_DIVF(divf));
-
- /* Setup clock, but don't start it yet */
- regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
- | BCM2835_CLK_MASH(mash)
- | BCM2835_CLK_SRC(clk_src));
-
/* Setup the frame format */
format = BCM2835_I2S_CHEN;
- if (data_length > 24)
+ if (data_length >= 24)
format |= BCM2835_I2S_CHWEX;
format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
+ /* CH2 format is the same as for CH1 */
+ format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format);
+
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- data_delay = 1;
- break;
- default:
+ /* I2S mode needs an even number of slots */
+ if (slots & 1)
+ return -EINVAL;
+
/*
- * TODO
- * Others are possible but are not implemented at the moment.
+ * Use I2S-style logical slot numbering: even slots
+ * are in first half of frame, odd slots in second half.
*/
- dev_err(dev->dev, "%s:bad format\n", __func__);
- return -EINVAL;
- }
+ odd_slot_offset = slots >> 1;
- ch1pos = data_delay;
- ch2pos = bclk_ratio / 2 + data_delay;
+ /* MSB starts one cycle after frame start */
+ data_delay = 1;
- switch (params_channels(params)) {
- case 2:
- format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format);
- format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos));
- format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos));
+ /* Setup frame sync signal for 50% duty cycle */
+ framesync_length = frame_length / 2;
+ frame_start_falling_edge = true;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if (slots & 1)
+ return -EINVAL;
+
+ odd_slot_offset = slots >> 1;
+ data_delay = 0;
+ framesync_length = frame_length / 2;
+ frame_start_falling_edge = false;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (slots & 1)
+ return -EINVAL;
+
+ /* Odd frame lengths aren't supported */
+ if (frame_length & 1)
+ return -EINVAL;
+
+ odd_slot_offset = slots >> 1;
+ data_delay = slot_width - data_length;
+ framesync_length = frame_length / 2;
+ frame_start_falling_edge = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ data_delay = 1;
+ framesync_length = 1;
+ frame_start_falling_edge = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ data_delay = 0;
+ framesync_length = 1;
+ frame_start_falling_edge = false;
break;
default:
return -EINVAL;
}
+ bcm2835_i2s_calc_channel_pos(&rx_ch1_pos, &rx_ch2_pos,
+ rx_mask, slot_width, data_delay, odd_slot_offset);
+ bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos,
+ tx_mask, slot_width, data_delay, odd_slot_offset);
+
+ /*
+ * Transmitting data immediately after frame start, eg
+ * in left-justified or DSP mode A, only works stable
+ * if bcm2835 is the frame clock provider.
+ */
+ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider)
+ dev_warn(dev->dev,
+ "Unstable consumer config detected, L/R may be swapped");
+
/*
* Set format for both streams.
* We cannot set another frame length
* (and therefore word length) anyway,
* so the format will be the same.
*/
- regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format);
- regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format);
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG,
+ format
+ | BCM2835_I2S_CH1_POS(rx_ch1_pos)
+ | BCM2835_I2S_CH2_POS(rx_ch2_pos));
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG,
+ format
+ | BCM2835_I2S_CH1_POS(tx_ch1_pos)
+ | BCM2835_I2S_CH2_POS(tx_ch2_pos));
/* Setup the I2S mode */
- mode = 0;
if (data_length <= 16) {
/*
@@ -476,65 +534,41 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP;
}
- mode |= BCM2835_I2S_FLEN(bclk_ratio - 1);
- mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2);
+ mode |= BCM2835_I2S_FLEN(frame_length - 1);
+ mode |= BCM2835_I2S_FSLEN(framesync_length);
- /* Master or slave? */
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* CPU is master */
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- /*
- * CODEC is bit clock master
- * CPU is frame master
- */
+ /* CLKM selects bcm2835 clock slave mode */
+ if (!bit_clock_provider)
mode |= BCM2835_I2S_CLKM;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- /*
- * CODEC is frame master
- * CPU is bit clock master
- */
+
+ /* FSM selects bcm2835 frame sync slave mode */
+ if (!frame_sync_provider)
mode |= BCM2835_I2S_FSM;
+
+ /* CLKI selects normal clocking mode, sampling on rising edge */
+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ mode |= BCM2835_I2S_CLKI;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* CODEC is master */
- mode |= BCM2835_I2S_CLKM;
- mode |= BCM2835_I2S_FSM;
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
break;
default:
- dev_err(dev->dev, "%s:bad master\n", __func__);
return -EINVAL;
}
- /*
- * Invert clocks?
- *
- * The BCM approach seems to be inverted to the classical I2S approach.
- */
+ /* FSI selects frame start on falling edge */
switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
- /* None. Therefore, both for BCM */
- mode |= BCM2835_I2S_CLKI;
- mode |= BCM2835_I2S_FSI;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- /* Both. Therefore, none for BCM */
+ case SND_SOC_DAIFMT_IB_NF:
+ if (frame_start_falling_edge)
+ mode |= BCM2835_I2S_FSI;
break;
case SND_SOC_DAIFMT_NB_IF:
- /*
- * Invert only frame sync. Therefore,
- * invert only bit clock for BCM
- */
- mode |= BCM2835_I2S_CLKI;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- /*
- * Invert only bit clock. Therefore,
- * invert only frame sync for BCM
- */
- mode |= BCM2835_I2S_FSI;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (!frame_start_falling_edge)
+ mode |= BCM2835_I2S_FSI;
break;
default:
return -EINVAL;
@@ -557,7 +591,27 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
/* Clear FIFOs */
bcm2835_i2s_clear_fifos(dev, true, true);
- return 0;
+ dev_dbg(dev->dev,
+ "slots: %d width: %d rx mask: 0x%02x tx_mask: 0x%02x\n",
+ slots, slot_width, rx_mask, tx_mask);
+
+ dev_dbg(dev->dev, "frame len: %d sync len: %d data len: %d\n",
+ frame_length, framesync_length, data_length);
+
+ dev_dbg(dev->dev, "rx pos: %d,%d tx pos: %d,%d\n",
+ rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos);
+
+ dev_dbg(dev->dev, "sampling rate: %d bclk rate: %d\n",
+ params_rate(params), bclk_rate);
+
+ dev_dbg(dev->dev, "CLKM: %d CLKI: %d FSM: %d FSI: %d frame start: %s edge\n",
+ !!(mode & BCM2835_I2S_CLKM),
+ !!(mode & BCM2835_I2S_CLKI),
+ !!(mode & BCM2835_I2S_FSM),
+ !!(mode & BCM2835_I2S_FSI),
+ (mode & BCM2835_I2S_FSI) ? "falling" : "rising");
+
+ return ret;
}
static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream,
@@ -566,8 +620,6 @@ static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream,
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
uint32_t cs_reg;
- bcm2835_i2s_start_clock(dev);
-
/*
* Clear both FIFOs if the one that should be started
* is not empty at the moment. This should only happen
@@ -601,7 +653,7 @@ static void bcm2835_i2s_stop(struct bcm2835_i2s_dev *dev,
BCM2835_I2S_CS_A_REG, mask, 0);
/* Stop also the clock when not SND_SOC_DAIFMT_CONT */
- if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT))
+ if (!snd_soc_dai_active(dai) && !(dev->fmt & SND_SOC_DAIFMT_CONT))
bcm2835_i2s_stop_clock(dev);
}
@@ -643,7 +695,7 @@ static int bcm2835_i2s_startup(struct snd_pcm_substream *substream,
{
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- if (dai->active)
+ if (snd_soc_dai_active(dai))
return 0;
/* Should this still be running stop it */
@@ -671,7 +723,7 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
bcm2835_i2s_stop(dev, substream, dai);
/* If both streams are stopped, disable module and clock */
- if (dai->active)
+ if (snd_soc_dai_active(dai))
return;
/* Disable the module */
@@ -685,46 +737,54 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
bcm2835_i2s_stop_clock(dev);
}
-static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
- .startup = bcm2835_i2s_startup,
- .shutdown = bcm2835_i2s_shutdown,
- .prepare = bcm2835_i2s_prepare,
- .trigger = bcm2835_i2s_trigger,
- .hw_params = bcm2835_i2s_hw_params,
- .set_fmt = bcm2835_i2s_set_dai_fmt,
- .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio
-};
-
static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai,
- &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
- &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+ &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
return 0;
}
+static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
+ .probe = bcm2835_i2s_dai_probe,
+ .startup = bcm2835_i2s_startup,
+ .shutdown = bcm2835_i2s_shutdown,
+ .prepare = bcm2835_i2s_prepare,
+ .trigger = bcm2835_i2s_trigger,
+ .hw_params = bcm2835_i2s_hw_params,
+ .set_fmt = bcm2835_i2s_set_dai_fmt,
+ .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio,
+ .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot,
+};
+
static struct snd_soc_dai_driver bcm2835_i2s_dai = {
.name = "bcm2835-i2s",
- .probe = bcm2835_i2s_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .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
},
.capture = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .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 = &bcm2835_i2s_dai_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg)
@@ -737,7 +797,7 @@ static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg)
return true;
default:
return false;
- };
+ }
}
static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
@@ -747,82 +807,67 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
return true;
default:
return false;
- };
-}
-
-static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case BCM2835_CLK_PCMCTL_REG:
- return true;
- default:
- return false;
- };
+ }
}
-static const struct regmap_config bcm2835_regmap_config[] = {
- {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = BCM2835_I2S_GRAY_REG,
- .precious_reg = bcm2835_i2s_precious_reg,
- .volatile_reg = bcm2835_i2s_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
- },
- {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = BCM2835_CLK_PCMDIV_REG,
- .volatile_reg = bcm2835_clk_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
- },
+static const struct regmap_config bcm2835_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = BCM2835_I2S_GRAY_REG,
+ .precious_reg = bcm2835_i2s_precious_reg,
+ .volatile_reg = bcm2835_i2s_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct snd_soc_component_driver bcm2835_i2s_component = {
- .name = "bcm2835-i2s-comp",
+ .name = "bcm2835-i2s-comp",
+ .legacy_dai_naming = 1,
};
static int bcm2835_i2s_probe(struct platform_device *pdev)
{
struct bcm2835_i2s_dev *dev;
- int i;
int ret;
- struct regmap *regmap[2];
- struct resource *mem[2];
-
- /* Request both ioareas */
- for (i = 0; i <= 1; i++) {
- void __iomem *base;
-
- mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
- base = devm_ioremap_resource(&pdev->dev, mem[i]);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- regmap[i] = devm_regmap_init_mmio(&pdev->dev, base,
- &bcm2835_regmap_config[i]);
- if (IS_ERR(regmap[i]))
- return PTR_ERR(regmap[i]);
- }
+ void __iomem *base;
+ const __be32 *addr;
+ dma_addr_t dma_base;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
GFP_KERNEL);
if (!dev)
return -ENOMEM;
- dev->i2s_regmap = regmap[0];
- dev->clk_regmap = regmap[1];
+ /* get the clock */
+ dev->clk_prepared = false;
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk),
+ "could not get clk\n");
+
+ /* Request ioarea */
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &bcm2835_regmap_config);
+ if (IS_ERR(dev->i2s_regmap))
+ return PTR_ERR(dev->i2s_regmap);
+
+ /* Set the DMA address - we have to parse DT ourselves */
+ addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(&pdev->dev, "could not get DMA-register address\n");
+ return -EINVAL;
+ }
+ dma_base = be32_to_cpup(addr);
- /* Set the DMA address */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
- (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
- + BCM2835_VCMMU_SHIFT;
+ dma_base + BCM2835_I2S_FIFO_A_REG;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
- (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
- + BCM2835_VCMMU_SHIFT;
+ dma_base + BCM2835_I2S_FIFO_A_REG;
/* Set the bus width */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
@@ -834,8 +879,14 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
- /* BCLK ratio - use default */
- dev->bclk_ratio = 0;
+ /*
+ * Set the PACK flag to enable S16_LE support (2 S16_LE values
+ * packed into 32-bit transfers).
+ */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags =
+ SND_DMAENGINE_PCM_DAI_FLAG_PACK;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags =
+ SND_DMAENGINE_PCM_DAI_FLAG_PACK;
/* Store the pdev */
dev->dev = &pdev->dev;
diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c
new file mode 100644
index 000000000000..c47ed1e6ea2b
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// linux/sound/bcm/bcm63xx-i2s-whistler.c
+// BCM63xx whistler i2s driver
+// Copyright (c) 2020 Broadcom Corporation
+// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "bcm63xx-i2s.h"
+
+#define DRV_NAME "brcm-i2s"
+
+static bool brcm_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TX_CFG ... I2S_TX_DESC_IFF_LEN:
+ case I2S_TX_CFG_2 ... I2S_RX_DESC_IFF_LEN:
+ case I2S_RX_CFG_2 ... I2S_REG_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool brcm_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TX_CFG ... I2S_REG_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool brcm_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TX_CFG:
+ case I2S_TX_IRQ_CTL:
+ case I2S_TX_DESC_IFF_ADDR:
+ case I2S_TX_DESC_IFF_LEN:
+ case I2S_TX_DESC_OFF_ADDR:
+ case I2S_TX_DESC_OFF_LEN:
+ case I2S_TX_CFG_2:
+ case I2S_RX_CFG:
+ case I2S_RX_IRQ_CTL:
+ case I2S_RX_DESC_OFF_ADDR:
+ case I2S_RX_DESC_OFF_LEN:
+ case I2S_RX_DESC_IFF_LEN:
+ case I2S_RX_DESC_IFF_ADDR:
+ case I2S_RX_CFG_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config brcm_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = I2S_REG_MAX,
+ .writeable_reg = brcm_i2s_wr_reg,
+ .readable_reg = brcm_i2s_rd_reg,
+ .volatile_reg = brcm_i2s_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int bcm63xx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
+
+ ret = clk_set_rate(i2s_priv->i2s_clk, params_rate(params));
+ if (ret < 0)
+ dev_err(i2s_priv->dev,
+ "Can't set sample rate, err: %d\n", ret);
+
+ return ret;
+}
+
+static int bcm63xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ unsigned int slavemode;
+ struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
+ struct regmap *regmap_i2s = i2s_priv->regmap_i2s;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG,
+ I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
+ I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE,
+ I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
+ I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 0);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 0);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 1);
+
+ /* TX and RX block each have an independent bit to indicate
+ * if it is generating the clock for the I2S bus. The bus
+ * clocks need to be generated from either the TX or RX block,
+ * but not both
+ */
+ regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode);
+ if (slavemode & I2S_RX_SLAVE_MODE_MASK)
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_MASTER_MODE);
+ else
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_SLAVE_MODE);
+ } else {
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG,
+ I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
+ I2S_RX_CLOCK_ENABLE,
+ I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
+ I2S_RX_CLOCK_ENABLE);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 0);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 0);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 1);
+
+ regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode);
+ if (slavemode & I2S_TX_SLAVE_MODE_MASK)
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK, 0);
+ else
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK,
+ I2S_RX_SLAVE_MODE);
+ }
+ return 0;
+}
+
+static void bcm63xx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ unsigned int enabled, slavemode;
+ struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
+ struct regmap *regmap_i2s = i2s_priv->regmap_i2s;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG,
+ I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
+ I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE, 0);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 1);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 4);
+ regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 4);
+
+ regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode);
+ slavemode = slavemode & I2S_TX_SLAVE_MODE_MASK;
+ if (!slavemode) {
+ regmap_read(regmap_i2s, I2S_RX_CFG, &enabled);
+ enabled = enabled & I2S_RX_ENABLE_MASK;
+ if (enabled)
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK,
+ I2S_RX_MASTER_MODE);
+ }
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_SLAVE_MODE);
+ } else {
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG,
+ I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
+ I2S_RX_CLOCK_ENABLE, 0);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 1);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 4);
+ regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 4);
+
+ regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode);
+ slavemode = slavemode & I2S_RX_SLAVE_MODE_MASK;
+ if (!slavemode) {
+ regmap_read(regmap_i2s, I2S_TX_CFG, &enabled);
+ enabled = enabled & I2S_TX_ENABLE_MASK;
+ if (enabled)
+ regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
+ I2S_TX_SLAVE_MODE_MASK,
+ I2S_TX_MASTER_MODE);
+ }
+
+ regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
+ I2S_RX_SLAVE_MODE_MASK, I2S_RX_SLAVE_MODE);
+ }
+}
+
+static const struct snd_soc_dai_ops bcm63xx_i2s_dai_ops = {
+ .startup = bcm63xx_i2s_startup,
+ .shutdown = bcm63xx_i2s_shutdown,
+ .hw_params = bcm63xx_i2s_hw_params,
+};
+
+static struct snd_soc_dai_driver bcm63xx_i2s_dai = {
+ .name = DRV_NAME,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &bcm63xx_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+};
+
+static const struct snd_soc_component_driver bcm63xx_i2s_component = {
+ .name = "bcm63xx",
+ .legacy_dai_naming = 1,
+};
+
+static int bcm63xx_i2s_dev_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ void __iomem *regs;
+ struct bcm_i2s_priv *i2s_priv;
+ struct regmap *regmap_i2s;
+ struct clk *i2s_clk;
+
+ i2s_priv = devm_kzalloc(&pdev->dev, sizeof(*i2s_priv), GFP_KERNEL);
+ if (!i2s_priv)
+ return -ENOMEM;
+
+ i2s_clk = devm_clk_get(&pdev->dev, "i2sclk");
+ if (IS_ERR(i2s_clk)) {
+ dev_err(&pdev->dev, "%s: cannot get a brcm clock: %ld\n",
+ __func__, PTR_ERR(i2s_clk));
+ return PTR_ERR(i2s_clk);
+ }
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ return ret;
+ }
+
+ regmap_i2s = devm_regmap_init_mmio(&pdev->dev,
+ regs, &brcm_i2s_regmap_config);
+ if (IS_ERR(regmap_i2s))
+ return PTR_ERR(regmap_i2s);
+
+ regmap_update_bits(regmap_i2s, I2S_MISC_CFG,
+ I2S_PAD_LVL_LOOP_DIS_MASK,
+ I2S_PAD_LVL_LOOP_DIS_ENABLE);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &bcm63xx_i2s_component,
+ &bcm63xx_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register the dai\n");
+ return ret;
+ }
+
+ i2s_priv->dev = &pdev->dev;
+ i2s_priv->i2s_clk = i2s_clk;
+ i2s_priv->regmap_i2s = regmap_i2s;
+ dev_set_drvdata(&pdev->dev, i2s_priv);
+
+ ret = bcm63xx_soc_platform_probe(pdev, i2s_priv);
+ if (ret)
+ dev_err(&pdev->dev, "failed to register the pcm\n");
+
+ return ret;
+}
+
+static void bcm63xx_i2s_dev_remove(struct platform_device *pdev)
+{
+ bcm63xx_soc_platform_remove(pdev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id snd_soc_bcm_audio_match[] = {
+ {.compatible = "brcm,bcm63xx-i2s"},
+ { }
+};
+#endif
+
+static struct platform_driver bcm63xx_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(snd_soc_bcm_audio_match),
+ },
+ .probe = bcm63xx_i2s_dev_probe,
+ .remove = bcm63xx_i2s_dev_remove,
+};
+
+module_platform_driver(bcm63xx_i2s_driver);
+
+MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h
new file mode 100644
index 000000000000..f30556bec89e
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-i2s.h
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// linux/sound/soc/bcm/bcm63xx-i2s.h
+// Copyright (c) 2020 Broadcom Corporation
+// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
+
+#ifndef __BCM63XX_I2S_H
+#define __BCM63XX_I2S_H
+
+#define I2S_DESC_FIFO_DEPTH 8
+#define I2S_MISC_CFG (0x003C)
+#define I2S_PAD_LVL_LOOP_DIS_MASK (1 << 2)
+#define I2S_PAD_LVL_LOOP_DIS_ENABLE I2S_PAD_LVL_LOOP_DIS_MASK
+
+#define I2S_TX_ENABLE_MASK (1 << 31)
+#define I2S_TX_ENABLE I2S_TX_ENABLE_MASK
+#define I2S_TX_OUT_R (1 << 19)
+#define I2S_TX_DATA_ALIGNMENT (1 << 2)
+#define I2S_TX_DATA_ENABLE (1 << 1)
+#define I2S_TX_CLOCK_ENABLE (1 << 0)
+
+#define I2S_TX_DESC_OFF_LEVEL_SHIFT 12
+#define I2S_TX_DESC_OFF_LEVEL_MASK (0x0F << I2S_TX_DESC_OFF_LEVEL_SHIFT)
+#define I2S_TX_DESC_IFF_LEVEL_SHIFT 8
+#define I2S_TX_DESC_IFF_LEVEL_MASK (0x0F << I2S_TX_DESC_IFF_LEVEL_SHIFT)
+#define I2S_TX_DESC_OFF_INTR_EN_MSK (1 << 1)
+#define I2S_TX_DESC_OFF_INTR_EN I2S_TX_DESC_OFF_INTR_EN_MSK
+
+#define I2S_TX_CFG (0x0000)
+#define I2S_TX_IRQ_CTL (0x0004)
+#define I2S_TX_IRQ_EN (0x0008)
+#define I2S_TX_IRQ_IFF_THLD (0x000c)
+#define I2S_TX_IRQ_OFF_THLD (0x0010)
+#define I2S_TX_DESC_IFF_ADDR (0x0014)
+#define I2S_TX_DESC_IFF_LEN (0x0018)
+#define I2S_TX_DESC_OFF_ADDR (0x001C)
+#define I2S_TX_DESC_OFF_LEN (0x0020)
+#define I2S_TX_CFG_2 (0x0024)
+#define I2S_TX_SLAVE_MODE_SHIFT 13
+#define I2S_TX_SLAVE_MODE_MASK (1 << I2S_TX_SLAVE_MODE_SHIFT)
+#define I2S_TX_SLAVE_MODE I2S_TX_SLAVE_MODE_MASK
+#define I2S_TX_MASTER_MODE 0
+#define I2S_TX_INTR_MASK 0x0F
+
+#define I2S_RX_ENABLE_MASK (1 << 31)
+#define I2S_RX_ENABLE I2S_RX_ENABLE_MASK
+#define I2S_RX_IN_R (1 << 19)
+#define I2S_RX_DATA_ALIGNMENT (1 << 2)
+#define I2S_RX_CLOCK_ENABLE (1 << 0)
+
+#define I2S_RX_DESC_OFF_LEVEL_SHIFT 12
+#define I2S_RX_DESC_OFF_LEVEL_MASK (0x0F << I2S_RX_DESC_OFF_LEVEL_SHIFT)
+#define I2S_RX_DESC_IFF_LEVEL_SHIFT 8
+#define I2S_RX_DESC_IFF_LEVEL_MASK (0x0F << I2S_RX_DESC_IFF_LEVEL_SHIFT)
+#define I2S_RX_DESC_OFF_INTR_EN_MSK (1 << 1)
+#define I2S_RX_DESC_OFF_INTR_EN I2S_RX_DESC_OFF_INTR_EN_MSK
+
+#define I2S_RX_CFG (0x0040) /* 20c0 */
+#define I2S_RX_IRQ_CTL (0x0044)
+#define I2S_RX_IRQ_EN (0x0048)
+#define I2S_RX_IRQ_IFF_THLD (0x004C)
+#define I2S_RX_IRQ_OFF_THLD (0x0050)
+#define I2S_RX_DESC_IFF_ADDR (0x0054)
+#define I2S_RX_DESC_IFF_LEN (0x0058)
+#define I2S_RX_DESC_OFF_ADDR (0x005C)
+#define I2S_RX_DESC_OFF_LEN (0x0060)
+#define I2S_RX_CFG_2 (0x0064)
+#define I2S_RX_SLAVE_MODE_SHIFT 13
+#define I2S_RX_SLAVE_MODE_MASK (1 << I2S_RX_SLAVE_MODE_SHIFT)
+#define I2S_RX_SLAVE_MODE I2S_RX_SLAVE_MODE_MASK
+#define I2S_RX_MASTER_MODE 0
+#define I2S_RX_INTR_MASK 0x0F
+
+#define I2S_REG_MAX 0x007C
+
+struct bcm_i2s_priv {
+ struct device *dev;
+ struct regmap *regmap_i2s;
+ struct clk *i2s_clk;
+ struct snd_pcm_substream *play_substream;
+ struct snd_pcm_substream *capture_substream;
+ struct i2s_dma_desc *play_dma_desc;
+ struct i2s_dma_desc *capture_dma_desc;
+};
+
+extern int bcm63xx_soc_platform_probe(struct platform_device *pdev,
+ struct bcm_i2s_priv *i2s_priv);
+extern int bcm63xx_soc_platform_remove(struct platform_device *pdev);
+
+#endif
diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c
new file mode 100644
index 000000000000..e3a4fcc63a56
--- /dev/null
+++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// linux/sound/bcm/bcm63xx-pcm-whistler.c
+// BCM63xx whistler pcm interface
+// Copyright (c) 2020 Broadcom Corporation
+// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
+
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <sound/soc.h>
+#include "bcm63xx-i2s.h"
+
+
+struct i2s_dma_desc {
+ unsigned char *dma_area;
+ dma_addr_t dma_addr;
+ unsigned int dma_len;
+};
+
+struct bcm63xx_runtime_data {
+ int dma_len;
+ dma_addr_t dma_addr;
+ dma_addr_t dma_addr_next;
+};
+
+static const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
+ .period_bytes_max = 8192 - 32,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 32,
+};
+
+static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_dma_desc *dma_desc;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
+ if (!dma_desc)
+ return -ENOMEM;
+
+ snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream, dma_desc);
+
+ return 0;
+}
+
+static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_dma_desc *dma_desc;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ kfree(dma_desc);
+
+ return 0;
+}
+
+static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd;
+ struct bcm_i2s_priv *i2s_priv;
+ struct regmap *regmap_i2s;
+
+ rtd = snd_soc_substream_to_rtd(substream);
+ i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
+ regmap_i2s = i2s_priv->regmap_i2s;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap_i2s,
+ I2S_TX_IRQ_EN,
+ I2S_TX_DESC_OFF_INTR_EN,
+ I2S_TX_DESC_OFF_INTR_EN);
+ regmap_update_bits(regmap_i2s,
+ I2S_TX_CFG,
+ I2S_TX_ENABLE_MASK,
+ I2S_TX_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_write(regmap_i2s,
+ I2S_TX_IRQ_EN,
+ 0);
+ regmap_update_bits(regmap_i2s,
+ I2S_TX_CFG,
+ I2S_TX_ENABLE_MASK,
+ 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ } else {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_IRQ_EN,
+ I2S_RX_DESC_OFF_INTR_EN_MSK,
+ I2S_RX_DESC_OFF_INTR_EN);
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_CFG,
+ I2S_RX_ENABLE_MASK,
+ I2S_RX_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_IRQ_EN,
+ I2S_RX_DESC_OFF_INTR_EN_MSK,
+ 0);
+ regmap_update_bits(regmap_i2s,
+ I2S_RX_CFG,
+ I2S_RX_ENABLE_MASK,
+ 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_dma_desc *dma_desc;
+ struct regmap *regmap_i2s;
+ struct bcm_i2s_priv *i2s_priv;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ uint32_t regaddr_desclen, regaddr_descaddr;
+
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ dma_desc->dma_len = snd_pcm_lib_period_bytes(substream);
+ dma_desc->dma_addr = runtime->dma_addr;
+ dma_desc->dma_area = runtime->dma_area;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regaddr_desclen = I2S_TX_DESC_IFF_LEN;
+ regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
+ } else {
+ regaddr_desclen = I2S_RX_DESC_IFF_LEN;
+ regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
+ }
+
+ i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
+ regmap_i2s = i2s_priv->regmap_i2s;
+
+ regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
+ regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+bcm63xx_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t x;
+ struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
+
+ if (!prtd->dma_addr_next)
+ prtd->dma_addr_next = substream->runtime->dma_addr;
+
+ x = bytes_to_frames(substream->runtime,
+ prtd->dma_addr_next - substream->runtime->dma_addr);
+
+ return x == substream->runtime->buffer_size ? 0 : x;
+}
+
+static int bcm63xx_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct bcm63xx_runtime_data *prtd;
+
+ runtime->hw = bcm63xx_pcm_hardware;
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ ret = -ENOMEM;
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ goto out;
+
+ runtime->private_data = prtd;
+ return 0;
+out:
+ return ret;
+}
+
+static int bcm63xx_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct bcm63xx_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
+{
+ unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
+ struct bcm63xx_runtime_data *prtd;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ struct regmap *regmap_i2s;
+ struct i2s_dma_desc *dma_desc;
+ struct snd_soc_pcm_runtime *rtd;
+ struct bcm_i2s_priv *i2s_priv;
+
+ i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
+ regmap_i2s = i2s_priv->regmap_i2s;
+
+ /* rx */
+ regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
+
+ if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
+ substream = i2s_priv->capture_substream;
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ prtd = runtime->private_data;
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+
+ offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
+ I2S_RX_DESC_OFF_LEVEL_SHIFT;
+ bool val_read = false;
+ while (offlevel) {
+ regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
+ regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
+ val_read = true;
+ offlevel--;
+ }
+ if (val_read)
+ prtd->dma_addr_next = val_1 + val_2;
+
+ ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
+ I2S_RX_DESC_IFF_LEVEL_SHIFT;
+
+ availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
+ while (availdepth) {
+ dma_desc->dma_addr +=
+ snd_pcm_lib_period_bytes(substream);
+ dma_desc->dma_area +=
+ snd_pcm_lib_period_bytes(substream);
+ if (dma_desc->dma_addr - runtime->dma_addr >=
+ runtime->dma_bytes) {
+ dma_desc->dma_addr = runtime->dma_addr;
+ dma_desc->dma_area = runtime->dma_area;
+ }
+
+ prtd->dma_addr = dma_desc->dma_addr;
+ regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
+ snd_pcm_lib_period_bytes(substream));
+ regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
+ dma_desc->dma_addr);
+ availdepth--;
+ }
+
+ snd_pcm_period_elapsed(substream);
+
+ /* Clear interrupt by writing 0 */
+ regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
+ I2S_RX_INTR_MASK, 0);
+ }
+
+ /* tx */
+ regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
+
+ if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
+ substream = i2s_priv->play_substream;
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ prtd = runtime->private_data;
+ dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+
+ offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
+ I2S_TX_DESC_OFF_LEVEL_SHIFT;
+ while (offlevel) {
+ regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
+ regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2);
+ prtd->dma_addr_next = val_1 + val_2;
+ offlevel--;
+ }
+
+ ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
+ I2S_TX_DESC_IFF_LEVEL_SHIFT;
+ availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
+
+ while (availdepth) {
+ dma_desc->dma_addr +=
+ snd_pcm_lib_period_bytes(substream);
+ dma_desc->dma_area +=
+ snd_pcm_lib_period_bytes(substream);
+
+ if (dma_desc->dma_addr - runtime->dma_addr >=
+ runtime->dma_bytes) {
+ dma_desc->dma_addr = runtime->dma_addr;
+ dma_desc->dma_area = runtime->dma_area;
+ }
+
+ prtd->dma_addr = dma_desc->dma_addr;
+ regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
+ snd_pcm_lib_period_bytes(substream));
+ regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
+ dma_desc->dma_addr);
+ availdepth--;
+ }
+
+ snd_pcm_period_elapsed(substream);
+
+ /* Clear interrupt by writing 0 */
+ regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
+ I2S_TX_INTR_MASK, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct bcm_i2s_priv *i2s_priv;
+ int ret;
+
+ i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
+
+ of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
+
+ ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+ i2s_priv->play_substream =
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+ i2s_priv->capture_substream =
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
+ pcm->card->dev,
+ bcm63xx_pcm_hardware.buffer_bytes_max);
+}
+
+static const struct snd_soc_component_driver bcm63xx_soc_platform = {
+ .open = bcm63xx_pcm_open,
+ .close = bcm63xx_pcm_close,
+ .hw_params = bcm63xx_pcm_hw_params,
+ .hw_free = bcm63xx_pcm_hw_free,
+ .prepare = bcm63xx_pcm_prepare,
+ .trigger = bcm63xx_pcm_trigger,
+ .pointer = bcm63xx_pcm_pointer,
+ .pcm_construct = bcm63xx_soc_pcm_new,
+};
+
+int bcm63xx_soc_platform_probe(struct platform_device *pdev,
+ struct bcm_i2s_priv *i2s_priv)
+{
+ int ret;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
+ irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "i2s_init: failed to request interrupt.ret=%d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &bcm63xx_soc_platform, NULL, 0);
+}
+
+int bcm63xx_soc_platform_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c
new file mode 100644
index 000000000000..4cb2fe10bcdc
--- /dev/null
+++ b/sound/soc/bcm/cygnus-pcm.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014-2015 Broadcom Corporation
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+/* Register offset needed for ASoC PCM module */
+
+#define INTH_R5F_STATUS_OFFSET 0x040
+#define INTH_R5F_CLEAR_OFFSET 0x048
+#define INTH_R5F_MASK_SET_OFFSET 0x050
+#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
+
+#define BF_REARM_FREE_MARK_OFFSET 0x344
+#define BF_REARM_FULL_MARK_OFFSET 0x348
+
+/* Ring Buffer Ctrl Regs --- Start */
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
+#define SRC_RBUF_0_RDADDR_OFFSET 0x500
+#define SRC_RBUF_1_RDADDR_OFFSET 0x518
+#define SRC_RBUF_2_RDADDR_OFFSET 0x530
+#define SRC_RBUF_3_RDADDR_OFFSET 0x548
+#define SRC_RBUF_4_RDADDR_OFFSET 0x560
+#define SRC_RBUF_5_RDADDR_OFFSET 0x578
+#define SRC_RBUF_6_RDADDR_OFFSET 0x590
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
+#define SRC_RBUF_0_WRADDR_OFFSET 0x504
+#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
+#define SRC_RBUF_2_WRADDR_OFFSET 0x534
+#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
+#define SRC_RBUF_4_WRADDR_OFFSET 0x564
+#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
+#define SRC_RBUF_6_WRADDR_OFFSET 0x594
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
+#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
+#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
+#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
+#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
+#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
+#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
+#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
+#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
+#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
+#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
+#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
+#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
+#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
+#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
+#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
+#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
+#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
+#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
+#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
+#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
+#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
+#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
+#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
+#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
+#define DST_RBUF_3_RDADDR_OFFSET 0x608
+#define DST_RBUF_4_RDADDR_OFFSET 0x620
+#define DST_RBUF_5_RDADDR_OFFSET 0x638
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
+#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
+#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
+#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
+#define DST_RBUF_3_WRADDR_OFFSET 0x60c
+#define DST_RBUF_4_WRADDR_OFFSET 0x624
+#define DST_RBUF_5_WRADDR_OFFSET 0x63c
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
+#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
+#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
+#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
+#define DST_RBUF_3_BASEADDR_OFFSET 0x610
+#define DST_RBUF_4_BASEADDR_OFFSET 0x628
+#define DST_RBUF_5_BASEADDR_OFFSET 0x640
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
+#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
+#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
+#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
+#define DST_RBUF_3_ENDADDR_OFFSET 0x614
+#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
+#define DST_RBUF_5_ENDADDR_OFFSET 0x644
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
+#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
+#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
+#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
+#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
+#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
+#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
+/* Ring Buffer Ctrl Regs --- End */
+
+/* Error Status Regs --- Start */
+/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
+#define ESR0_STATUS_OFFSET 0x900
+#define ESR1_STATUS_OFFSET 0x918
+#define ESR2_STATUS_OFFSET 0x930
+#define ESR3_STATUS_OFFSET 0x948
+#define ESR4_STATUS_OFFSET 0x960
+
+/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
+#define ESR0_STATUS_CLR_OFFSET 0x908
+#define ESR1_STATUS_CLR_OFFSET 0x920
+#define ESR2_STATUS_CLR_OFFSET 0x938
+#define ESR3_STATUS_CLR_OFFSET 0x950
+#define ESR4_STATUS_CLR_OFFSET 0x968
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
+#define ESR0_MASK_STATUS_OFFSET 0x90c
+#define ESR1_MASK_STATUS_OFFSET 0x924
+#define ESR2_MASK_STATUS_OFFSET 0x93c
+#define ESR3_MASK_STATUS_OFFSET 0x954
+#define ESR4_MASK_STATUS_OFFSET 0x96c
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
+#define ESR0_MASK_SET_OFFSET 0x910
+#define ESR1_MASK_SET_OFFSET 0x928
+#define ESR2_MASK_SET_OFFSET 0x940
+#define ESR3_MASK_SET_OFFSET 0x958
+#define ESR4_MASK_SET_OFFSET 0x970
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
+#define ESR0_MASK_CLR_OFFSET 0x914
+#define ESR1_MASK_CLR_OFFSET 0x92c
+#define ESR2_MASK_CLR_OFFSET 0x944
+#define ESR3_MASK_CLR_OFFSET 0x95c
+#define ESR4_MASK_CLR_OFFSET 0x974
+/* Error Status Regs --- End */
+
+#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */
+#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */
+#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */
+#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */
+#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */
+
+
+/* Mask for R5F register. Set all relevant interrupt for playback handler */
+#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
+ BIT(R5F_ESR1_SHIFT) | \
+ BIT(R5F_ESR3_SHIFT))
+
+/* Mask for R5F register. Set all relevant interrupt for capture handler */
+#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
+
+/*
+ * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
+ * This number should be a multiple of 256. Minimum value is 256
+ */
+#define PERIOD_BYTES_MIN 0x100
+
+static const struct snd_pcm_hardware cygnus_pcm_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+
+ /* A period is basically an interrupt */
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = 0x10000,
+
+ /* period_min/max gives range of approx interrupts per buffer */
+ .periods_min = 2,
+ .periods_max = 8,
+
+ /*
+ * maximum buffer size in bytes = period_bytes_max * periods_max
+ * We allocate this amount of data for each enabled channel
+ */
+ .buffer_bytes_max = 4 * 0x8000,
+};
+
+static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
+
+static struct cygnus_aio_port *cygnus_dai_get_dma_data(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+
+ return snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(soc_runtime, 0), substream);
+}
+
+static void ringbuf_set_initial(void __iomem *audio_io,
+ struct ringbuf_regs *p_rbuf,
+ bool is_playback,
+ u32 start,
+ u32 periodsize,
+ u32 bufsize)
+{
+ u32 initial_rd;
+ u32 initial_wr;
+ u32 end;
+ u32 fmark_val; /* free or full mark */
+
+ p_rbuf->period_bytes = periodsize;
+ p_rbuf->buf_size = bufsize;
+
+ if (is_playback) {
+ /* Set the pointers to indicate full (flip uppermost bit) */
+ initial_rd = start;
+ initial_wr = initial_rd ^ BIT(31);
+ } else {
+ /* Set the pointers to indicate empty */
+ initial_wr = start;
+ initial_rd = initial_wr;
+ }
+
+ end = start + bufsize - 1;
+
+ /*
+ * The interrupt will fire when free/full mark is *exceeded*
+ * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
+ * to be PERIOD_BYTES_MIN less than the period size.
+ */
+ fmark_val = periodsize - PERIOD_BYTES_MIN;
+
+ writel(start, audio_io + p_rbuf->baseaddr);
+ writel(end, audio_io + p_rbuf->endaddr);
+ writel(fmark_val, audio_io + p_rbuf->fmark);
+ writel(initial_rd, audio_io + p_rbuf->rdaddr);
+ writel(initial_wr, audio_io + p_rbuf->wraddr);
+}
+
+static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ struct ringbuf_regs *p_rbuf;
+ int status = 0;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ /* Map the ssp portnum to a set of ring buffers. */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ p_rbuf = &aio->play_rb_regs;
+
+ switch (aio->portnum) {
+ case 0:
+ *p_rbuf = RINGBUF_REG_PLAYBACK(0);
+ break;
+ case 1:
+ *p_rbuf = RINGBUF_REG_PLAYBACK(2);
+ break;
+ case 2:
+ *p_rbuf = RINGBUF_REG_PLAYBACK(4);
+ break;
+ case 3: /* SPDIF */
+ *p_rbuf = RINGBUF_REG_PLAYBACK(6);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ } else {
+ p_rbuf = &aio->capture_rb_regs;
+
+ switch (aio->portnum) {
+ case 0:
+ *p_rbuf = RINGBUF_REG_CAPTURE(0);
+ break;
+ case 1:
+ *p_rbuf = RINGBUF_REG_CAPTURE(2);
+ break;
+ case 2:
+ *p_rbuf = RINGBUF_REG_CAPTURE(4);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ }
+
+ return status;
+}
+
+static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ p_rbuf = &aio->play_rb_regs;
+ else
+ p_rbuf = &aio->capture_rb_regs;
+
+ return p_rbuf;
+}
+
+static void enable_intr(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ u32 clear_mask;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ /* The port number maps to the bit position to be cleared */
+ clear_mask = BIT(aio->portnum);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Clear interrupt status before enabling them */
+ writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
+ /* Unmask the interrupts of the given port*/
+ writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
+
+ writel(ANY_PLAYBACK_IRQ,
+ aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+ } else {
+ writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
+ writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
+
+ writel(ANY_CAPTURE_IRQ,
+ aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+ }
+
+}
+
+static void disable_intr(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct cygnus_aio_port *aio;
+ u32 set_mask;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
+
+ /* The port number maps to the bit position to be set */
+ set_mask = BIT(aio->portnum);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Mask the interrupts of the given port*/
+ writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
+ writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
+ writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
+ } else {
+ writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
+ writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
+ }
+
+}
+
+static int cygnus_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ enable_intr(substream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ disable_intr(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ struct ringbuf_regs *p_rbuf = NULL;
+ u32 regval;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ p_rbuf = get_ringbuf(substream);
+
+ /*
+ * If free/full mark interrupt occurs, provide timestamp
+ * to ALSA and update appropriate idx by period_bytes
+ */
+ snd_pcm_period_elapsed(substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Set the ring buffer to full */
+ regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+ regval = regval ^ BIT(31);
+ writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
+ } else {
+ /* Set the ring buffer to empty */
+ regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
+ writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
+ }
+}
+
+/*
+ * ESR0/1/3 status Description
+ * 0x1 I2S0_out port caused interrupt
+ * 0x2 I2S1_out port caused interrupt
+ * 0x4 I2S2_out port caused interrupt
+ * 0x8 SPDIF_out port caused interrupt
+ */
+static void handle_playback_irq(struct cygnus_audio *cygaud)
+{
+ void __iomem *audio_io;
+ u32 port;
+ u32 esr_status0, esr_status1, esr_status3;
+
+ audio_io = cygaud->audio;
+
+ /*
+ * ESR status gets updates with/without interrupts enabled.
+ * So, check the ESR mask, which provides interrupt enable/
+ * disable status and use it to determine which ESR status
+ * should be serviced.
+ */
+ esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
+ esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
+ esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
+ esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
+ esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
+ esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
+
+ for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
+ u32 esrmask = BIT(port);
+
+ /*
+ * Ringbuffer or FIFO underflow
+ * If we get this interrupt then, it is also true that we have
+ * not yet responded to the freemark interrupt.
+ * Log a debug message. The freemark handler below will
+ * handle getting everything going again.
+ */
+ if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
+ dev_dbg(cygaud->dev,
+ "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
+ esr_status0, esr_status1, esr_status3);
+ }
+
+ /*
+ * Freemark is hit. This is the normal interrupt.
+ * In typical operation the read and write regs will be equal
+ */
+ if (esrmask & esr_status3) {
+ struct snd_pcm_substream *playstr;
+
+ playstr = cygaud->portinfo[port].play_stream;
+ cygnus_pcm_period_elapsed(playstr);
+ }
+ }
+
+ /* Clear ESR interrupt */
+ writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
+ writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
+ writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
+ /* Rearm freemark logic by writing 1 to the correct bit */
+ writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
+}
+
+/*
+ * ESR2/4 status Description
+ * 0x1 I2S0_in port caused interrupt
+ * 0x2 I2S1_in port caused interrupt
+ * 0x4 I2S2_in port caused interrupt
+ */
+static void handle_capture_irq(struct cygnus_audio *cygaud)
+{
+ void __iomem *audio_io;
+ u32 port;
+ u32 esr_status2, esr_status4;
+
+ audio_io = cygaud->audio;
+
+ /*
+ * ESR status gets updates with/without interrupts enabled.
+ * So, check the ESR mask, which provides interrupt enable/
+ * disable status and use it to determine which ESR status
+ * should be serviced.
+ */
+ esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
+ esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
+ esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
+ esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
+
+ for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
+ u32 esrmask = BIT(port);
+
+ /*
+ * Ringbuffer or FIFO overflow
+ * If we get this interrupt then, it is also true that we have
+ * not yet responded to the fullmark interrupt.
+ * Log a debug message. The fullmark handler below will
+ * handle getting everything going again.
+ */
+ if (esrmask & esr_status2)
+ dev_dbg(cygaud->dev,
+ "Overflow: esr2=0x%x\n", esr_status2);
+
+ if (esrmask & esr_status4) {
+ struct snd_pcm_substream *capstr;
+
+ capstr = cygaud->portinfo[port].capture_stream;
+ cygnus_pcm_period_elapsed(capstr);
+ }
+ }
+
+ writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
+ writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
+ /* Rearm fullmark logic by writing 1 to the correct bit */
+ writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
+}
+
+static irqreturn_t cygnus_dma_irq(int irq, void *data)
+{
+ u32 r5_status;
+ struct cygnus_audio *cygaud = data;
+
+ /*
+ * R5 status bits Description
+ * 0 ESR0 (playback FIFO interrupt)
+ * 1 ESR1 (playback rbuf interrupt)
+ * 2 ESR2 (capture rbuf interrupt)
+ * 3 ESR3 (Freemark play. interrupt)
+ * 4 ESR4 (Fullmark capt. interrupt)
+ */
+ r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
+
+ if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
+ return IRQ_NONE;
+
+ /* If playback interrupt happened */
+ if (ANY_PLAYBACK_IRQ & r5_status) {
+ handle_playback_irq(cygaud);
+ writel(ANY_PLAYBACK_IRQ & r5_status,
+ cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+ }
+
+ /* If capture interrupt happened */
+ if (ANY_CAPTURE_IRQ & r5_status) {
+ handle_capture_irq(cygaud);
+ writel(ANY_CAPTURE_IRQ & r5_status,
+ cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cygnus_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cygnus_aio_port *aio;
+ int ret;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ if (!aio)
+ return -ENODEV;
+
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
+
+ snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
+ if (ret < 0)
+ return ret;
+ /*
+ * Keep track of which substream belongs to which port.
+ * This info is needed by snd_pcm_period_elapsed() in irq_handler
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->play_stream = substream;
+ else
+ aio->capture_stream = substream;
+
+ return 0;
+}
+
+static int cygnus_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct cygnus_aio_port *aio;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->play_stream = NULL;
+ else
+ aio->capture_stream = NULL;
+
+ if (!aio->play_stream && !aio->capture_stream)
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum);
+
+ return 0;
+}
+
+static int cygnus_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cygnus_aio_port *aio;
+ unsigned long bufsize, periodsize;
+ bool is_play;
+ u32 start;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ periodsize = snd_pcm_lib_period_bytes(substream);
+
+ dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
+ __func__, bufsize, periodsize);
+
+ configure_ringbuf_regs(substream);
+
+ p_rbuf = get_ringbuf(substream);
+
+ start = runtime->dma_addr;
+
+ is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+
+ ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
+ periodsize, bufsize);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct cygnus_aio_port *aio;
+ unsigned int res = 0, cur = 0, base = 0;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ /*
+ * Get the offset of the current read (for playack) or write
+ * index (for capture). Report this value back to the asoc framework.
+ */
+ p_rbuf = get_ringbuf(substream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+ else
+ cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
+
+ base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
+
+ /*
+ * Mask off the MSB of the rdaddr,wraddr and baseaddr
+ * since MSB is not part of the address
+ */
+ res = (cur & 0x7fffffff) - (base & 0x7fffffff);
+
+ return bytes_to_frames(substream->runtime, res);
+}
+
+static int cygnus_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size = cygnus_pcm_hw.buffer_bytes_max;
+ struct snd_card *card = rtd->card->snd_card;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &cygnus_dma_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver cygnus_soc_platform = {
+ .open = cygnus_pcm_open,
+ .close = cygnus_pcm_close,
+ .prepare = cygnus_pcm_prepare,
+ .trigger = cygnus_pcm_trigger,
+ .pointer = cygnus_pcm_pointer,
+ .pcm_construct = cygnus_dma_new,
+};
+
+int cygnus_soc_platform_register(struct device *dev,
+ struct cygnus_audio *cygaud)
+{
+ int rc;
+
+ dev_dbg(dev, "%s Enter\n", __func__);
+
+ rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
+ IRQF_SHARED, "cygnus-audio", cygaud);
+ if (rc) {
+ dev_err(dev, "%s request_irq error %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
+ NULL, 0);
+ if (rc) {
+ dev_err(dev, "%s failed\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+int cygnus_soc_platform_unregister(struct device *dev)
+{
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC PCM module");
diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c
new file mode 100644
index 000000000000..e0ce0232eb1e
--- /dev/null
+++ b/sound/soc/bcm/cygnus-ssp.c
@@ -0,0 +1,1405 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014-2015 Broadcom Corporation
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.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-dai.h>
+
+#include "cygnus-ssp.h"
+
+#define DEFAULT_VCO 1354750204
+
+#define CAPTURE_FCI_ID_BASE 0x180
+#define CYGNUS_SSP_TRISTATE_MASK 0x001fff
+#define CYGNUS_PLLCLKSEL_MASK 0xf
+
+/* Used with stream_on field to indicate which streams are active */
+#define PLAYBACK_STREAM_MASK BIT(0)
+#define CAPTURE_STREAM_MASK BIT(1)
+
+#define I2S_STREAM_CFG_MASK 0xff003ff
+#define I2S_CAP_STREAM_CFG_MASK 0xf0
+#define SPDIF_STREAM_CFG_MASK 0x3ff
+#define CH_GRP_STEREO 0x1
+
+/* Begin register offset defines */
+#define AUD_MISC_SEROUT_OE_REG_BASE 0x01c
+#define AUD_MISC_SEROUT_SPDIF_OE 12
+#define AUD_MISC_SEROUT_MCLK_OE 3
+#define AUD_MISC_SEROUT_LRCK_OE 2
+#define AUD_MISC_SEROUT_SCLK_OE 1
+#define AUD_MISC_SEROUT_SDAT_OE 0
+
+/* AUD_FMM_BF_CTRL_xxx regs */
+#define BF_DST_CFG0_OFFSET 0x100
+#define BF_DST_CFG1_OFFSET 0x104
+#define BF_DST_CFG2_OFFSET 0x108
+
+#define BF_DST_CTRL0_OFFSET 0x130
+#define BF_DST_CTRL1_OFFSET 0x134
+#define BF_DST_CTRL2_OFFSET 0x138
+
+#define BF_SRC_CFG0_OFFSET 0x148
+#define BF_SRC_CFG1_OFFSET 0x14c
+#define BF_SRC_CFG2_OFFSET 0x150
+#define BF_SRC_CFG3_OFFSET 0x154
+
+#define BF_SRC_CTRL0_OFFSET 0x1c0
+#define BF_SRC_CTRL1_OFFSET 0x1c4
+#define BF_SRC_CTRL2_OFFSET 0x1c8
+#define BF_SRC_CTRL3_OFFSET 0x1cc
+
+#define BF_SRC_GRP0_OFFSET 0x1fc
+#define BF_SRC_GRP1_OFFSET 0x200
+#define BF_SRC_GRP2_OFFSET 0x204
+#define BF_SRC_GRP3_OFFSET 0x208
+
+#define BF_SRC_GRP_EN_OFFSET 0x320
+#define BF_SRC_GRP_FLOWON_OFFSET 0x324
+#define BF_SRC_GRP_SYNC_DIS_OFFSET 0x328
+
+/* AUD_FMM_IOP_OUT_I2S_xxx regs */
+#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00
+#define OUT_I2S_0_CFG_OFFSET 0xa04
+#define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c
+
+#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40
+#define OUT_I2S_1_CFG_OFFSET 0xa44
+#define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c
+
+#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80
+#define OUT_I2S_2_CFG_OFFSET 0xa84
+#define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */
+#define SPDIF_STREAM_CFG_OFFSET 0xac0
+#define SPDIF_CTRL_OFFSET 0xac4
+#define SPDIF_FORMAT_CFG_OFFSET 0xad8
+#define SPDIF_MCLK_CFG_OFFSET 0xadc
+
+/* AUD_FMM_IOP_PLL_0_xxx regs */
+#define IOP_PLL_0_MACRO_OFFSET 0xb00
+#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14
+#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18
+#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c
+
+#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30
+#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34
+#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38
+
+/* AUD_FMM_IOP_xxx regs */
+#define IOP_PLL_0_CONTROL_OFFSET 0xb04
+#define IOP_PLL_0_USER_NDIV_OFFSET 0xb08
+#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20
+#define IOP_PLL_0_RESET_OFFSET 0xb5c
+
+/* AUD_FMM_IOP_IN_I2S_xxx regs */
+#define IN_I2S_0_STREAM_CFG_OFFSET 0x00
+#define IN_I2S_0_CFG_OFFSET 0x04
+#define IN_I2S_1_STREAM_CFG_OFFSET 0x40
+#define IN_I2S_1_CFG_OFFSET 0x44
+#define IN_I2S_2_STREAM_CFG_OFFSET 0x80
+#define IN_I2S_2_CFG_OFFSET 0x84
+
+/* AUD_FMM_IOP_MISC_xxx regs */
+#define IOP_SW_INIT_LOGIC 0x1c0
+
+/* End register offset defines */
+
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */
+#define I2S_OUT_MCLKRATE_SHIFT 16
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */
+#define I2S_OUT_PLLCLKSEL_SHIFT 0
+
+/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */
+#define I2S_OUT_STREAM_ENA 31
+#define I2S_OUT_STREAM_CFG_GROUP_ID 20
+#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24
+
+/* AUD_FMM_IOP_IN_I2S_x_CAP */
+#define I2S_IN_STREAM_CFG_CAP_ENA 31
+#define I2S_IN_STREAM_CFG_0_GROUP_ID 4
+
+/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */
+#define I2S_OUT_CFGX_CLK_ENA 0
+#define I2S_OUT_CFGX_DATA_ENABLE 1
+#define I2S_OUT_CFGX_DATA_ALIGNMENT 6
+#define I2S_OUT_CFGX_BITS_PER_SLOT 13
+#define I2S_OUT_CFGX_VALID_SLOT 14
+#define I2S_OUT_CFGX_FSYNC_WIDTH 18
+#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26
+#define I2S_OUT_CFGX_SLAVE_MODE 30
+#define I2S_OUT_CFGX_TDM_MODE 31
+
+/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */
+#define BF_SRC_CFGX_SFIFO_ENA 0
+#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1
+#define BF_SRC_CFGX_SAMPLE_CH_MODE 2
+#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5
+#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10
+#define BF_SRC_CFGX_BIT_RES 20
+#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31
+
+/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */
+#define BF_DST_CFGX_CAP_ENA 0
+#define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1
+#define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2
+#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11
+#define BF_DST_CFGX_FCI_ID 12
+#define BF_DST_CFGX_CAP_MODE 24
+#define BF_DST_CFGX_PROC_SEQ_ID_VALID 31
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx */
+#define SPDIF_0_OUT_DITHER_ENA 3
+#define SPDIF_0_OUT_STREAM_ENA 31
+
+/* AUD_FMM_IOP_PLL_0_USER */
+#define IOP_PLL_0_USER_NDIV_FRAC 10
+
+/* AUD_FMM_IOP_PLL_0_ACTIVE */
+#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10
+
+
+#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \
+ .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \
+ .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \
+ .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \
+ .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \
+ .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \
+ .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \
+ .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \
+ .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \
+ .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \
+ .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \
+}
+
+struct pll_macro_entry {
+ u32 mclk;
+ u32 pll_ch_num;
+};
+
+/*
+ * PLL has 3 output channels (1x, 2x, and 4x). Below are
+ * the common MCLK frequencies used by audio driver
+ */
+static const struct pll_macro_entry pll_predef_mclk[] = {
+ { 4096000, 0},
+ { 8192000, 1},
+ {16384000, 2},
+
+ { 5644800, 0},
+ {11289600, 1},
+ {22579200, 2},
+
+ { 6144000, 0},
+ {12288000, 1},
+ {24576000, 2},
+
+ {12288000, 0},
+ {24576000, 1},
+ {49152000, 2},
+
+ {22579200, 0},
+ {45158400, 1},
+ {90316800, 2},
+
+ {24576000, 0},
+ {49152000, 1},
+ {98304000, 2},
+};
+
+#define CYGNUS_RATE_MIN 8000
+#define CYGNUS_RATE_MAX 384000
+
+/* List of valid frame sizes for tdm mode */
+static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
+
+static const unsigned int cygnus_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+ 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cygnus_rate_constraint = {
+ .count = ARRAY_SIZE(cygnus_rates),
+ .list = cygnus_rates,
+};
+
+static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)
+{
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+ return &cygaud->portinfo[dai->id];
+}
+
+static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)
+{
+ u32 value, fci_id;
+ int status = 0;
+
+ switch (aio->port_type) {
+ case PORT_TDM:
+ value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+ value &= ~I2S_STREAM_CFG_MASK;
+
+ /* Set Group ID */
+ writel(aio->portnum,
+ aio->cygaud->audio + aio->regs.bf_sourcech_grp);
+
+ /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */
+ value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;
+ value |= aio->portnum; /* FCI ID is the port num */
+ value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;
+ writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+ /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+ value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+ value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */
+ value = readl(aio->cygaud->i2s_in +
+ aio->regs.i2s_cap_stream_cfg);
+ value &= ~I2S_CAP_STREAM_CFG_MASK;
+ value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;
+ writel(value, aio->cygaud->i2s_in +
+ aio->regs.i2s_cap_stream_cfg);
+
+ /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */
+ fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+ value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);
+ value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);
+ value |= (fci_id << BF_DST_CFGX_FCI_ID);
+ value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);
+ writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+ /* Enable the transmit pin for this port */
+ value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);
+ writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ break;
+ case PORT_SPDIF:
+ writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);
+
+ value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+ value |= BIT(SPDIF_0_OUT_DITHER_ENA);
+ writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+
+ /* Enable and set the FCI ID for the SPDIF channel */
+ value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+ value &= ~SPDIF_STREAM_CFG_MASK;
+ value |= aio->portnum; /* FCI ID is the port num */
+ value |= BIT(SPDIF_0_OUT_STREAM_ENA);
+ writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+ value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+ value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ /* Enable the spdif output pin */
+ value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);
+ writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+ break;
+ default:
+ dev_err(aio->cygaud->dev, "Port not supported\n");
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static void audio_ssp_in_enable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+ value |= BIT(BF_DST_CFGX_CAP_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+ writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+ value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+ value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+ writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+ aio->streams_on |= CAPTURE_STREAM_MASK;
+}
+
+static void audio_ssp_in_disable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+
+ value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+ value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+ writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+ aio->streams_on &= ~CAPTURE_STREAM_MASK;
+
+ /* If both playback and capture are off */
+ if (!aio->streams_on) {
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+ value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+ }
+
+ writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+ value &= ~BIT(BF_DST_CFGX_CAP_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+}
+
+static int audio_ssp_out_enable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+ int status = 0;
+
+ switch (aio->port_type) {
+ case PORT_TDM:
+ value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+ value |= BIT(I2S_OUT_STREAM_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+ writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+ value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ aio->streams_on |= PLAYBACK_STREAM_MASK;
+ break;
+ case PORT_SPDIF:
+ value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+ value |= 0x3;
+ writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+
+ writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ break;
+ default:
+ dev_err(aio->cygaud->dev,
+ "Port not supported %d\n", aio->portnum);
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static int audio_ssp_out_disable(struct cygnus_aio_port *aio)
+{
+ u32 value;
+ int status = 0;
+
+ switch (aio->port_type) {
+ case PORT_TDM:
+ aio->streams_on &= ~PLAYBACK_STREAM_MASK;
+
+ /* If both playback and capture are off */
+ if (!aio->streams_on) {
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+ value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+ }
+
+ /* set group_sync_dis = 1 */
+ value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+ value |= BIT(aio->portnum);
+ writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+ writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ /* set group_sync_dis = 0 */
+ value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+ value &= ~BIT(aio->portnum);
+ writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+ value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+ value &= ~BIT(I2S_OUT_STREAM_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+ /* IOP SW INIT on OUT_I2S_x */
+ value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+ value |= BIT(aio->portnum);
+ writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+ value &= ~BIT(aio->portnum);
+ writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+ break;
+ case PORT_SPDIF:
+ value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+ value &= ~0x3;
+ writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+ writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ break;
+ default:
+ dev_err(aio->cygaud->dev,
+ "Port not supported %d\n", aio->portnum);
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,
+ struct cygnus_aio_port *aio)
+{
+ int i = 0, error;
+ bool found = false;
+ const struct pll_macro_entry *p_entry;
+ struct clk *ch_clk;
+
+ for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {
+ p_entry = &pll_predef_mclk[i];
+ if (p_entry->mclk == mclk) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dev_err(cygaud->dev,
+ "%s No valid mclk freq (%u) found!\n", __func__, mclk);
+ return -EINVAL;
+ }
+
+ ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];
+
+ if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {
+ error = clk_prepare_enable(ch_clk);
+ if (error) {
+ dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+ aio->clk_trace.cap_clk_en = true;
+ }
+
+ if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {
+ error = clk_prepare_enable(ch_clk);
+ if (error) {
+ dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+ aio->clk_trace.play_clk_en = true;
+ }
+
+ error = clk_set_rate(ch_clk, mclk);
+ if (error) {
+ dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",
+ __func__, error);
+ return error;
+ }
+
+ return p_entry->pll_ch_num;
+}
+
+static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio)
+{
+ u32 value;
+ u32 mask = 0xf;
+ u32 sclk;
+ u32 mclk_rate;
+ unsigned int bit_rate;
+ unsigned int ratio;
+
+ bit_rate = aio->bit_per_frame * aio->lrclk;
+
+ /*
+ * Check if the bit clock can be generated from the given MCLK.
+ * MCLK must be a perfect multiple of bit clock and must be one of the
+ * following values... (2,4,6,8,10,12,14)
+ */
+ if ((aio->mclk % bit_rate) != 0)
+ return -EINVAL;
+
+ ratio = aio->mclk / bit_rate;
+ switch (ratio) {
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ case 12:
+ case 14:
+ mclk_rate = ratio / 2;
+ break;
+
+ default:
+ dev_err(aio->cygaud->dev,
+ "Invalid combination of MCLK and BCLK\n");
+ dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
+ aio->lrclk, aio->bit_per_frame, aio->mclk);
+ return -EINVAL;
+ }
+
+ /* Set sclk rate */
+ switch (aio->port_type) {
+ case PORT_TDM:
+ sclk = aio->bit_per_frame;
+ if (sclk == 512)
+ sclk = 0;
+
+ /* sclks_per_1fs_div = sclk cycles/32 */
+ sclk /= 32;
+
+ /* Set number of bitclks per frame */
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);
+ value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+ dev_dbg(aio->cygaud->dev,
+ "SCLKS_PER_1FS_DIV32 = 0x%x\n", value);
+ break;
+ case PORT_SPDIF:
+ break;
+ default:
+ dev_err(aio->cygaud->dev, "Unknown port type\n");
+ return -EINVAL;
+ }
+
+ /* Set MCLK_RATE ssp port (spdif and ssp are the same) */
+ value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);
+ value |= (mclk_rate << I2S_OUT_MCLKRATE_SHIFT);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+ dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);
+ dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",
+ aio->bit_per_frame, aio->mclk, aio->lrclk);
+ return 0;
+}
+
+static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+ int rate, bitres;
+ u32 value;
+ u32 mask = 0x1f;
+ int ret = 0;
+
+ dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);
+ dev_dbg(aio->cygaud->dev, "params_channels %d\n",
+ params_channels(params));
+ dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));
+ dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));
+
+ rate = params_rate(params);
+
+ switch (aio->mode) {
+ case CYGNUS_SSPMODE_TDM:
+ if ((rate == 192000) && (params_channels(params) > 4)) {
+ dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",
+ params_channels(params), rate);
+ return -EINVAL;
+ }
+ break;
+ case CYGNUS_SSPMODE_I2S:
+ aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */
+ break;
+ default:
+ dev_err(aio->cygaud->dev,
+ "%s port running in unknown mode\n", __func__);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);
+ value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bitres = 16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /* 32 bit mode is coded as 0 */
+ bitres = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+ value &= ~(mask << BF_SRC_CFGX_BIT_RES);
+ value |= (bitres << BF_SRC_CFGX_BIT_RES);
+ writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+ } else {
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = readl(aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ value |= BIT(BF_DST_CFGX_CAP_MODE);
+ writel(value, aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = readl(aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ value &= ~BIT(BF_DST_CFGX_CAP_MODE);
+ writel(value, aio->cygaud->audio +
+ aio->regs.bf_destch_cfg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ aio->lrclk = rate;
+
+ if (!aio->is_slave)
+ ret = cygnus_ssp_set_clocks(aio);
+
+ return ret;
+}
+
+/*
+ * This function sets the mclk frequency for pll clock
+ */
+static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ int sel;
+ u32 value;
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(aio->cygaud->dev,
+ "%s Enter port = %d\n", __func__, aio->portnum);
+ sel = pll_configure_mclk(cygaud, freq, aio);
+ if (sel < 0) {
+ dev_err(aio->cygaud->dev,
+ "%s Setting mclk failed.\n", __func__);
+ return -EINVAL;
+ }
+
+ aio->mclk = freq;
+
+ dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);
+ value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);
+ value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+ return 0;
+}
+
+static int cygnus_ssp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+ snd_soc_dai_set_dma_data(dai, substream, aio);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->clk_trace.play_en = true;
+ else
+ aio->clk_trace.cap_en = true;
+
+ substream->runtime->hw.rate_min = CYGNUS_RATE_MIN;
+ substream->runtime->hw.rate_max = CYGNUS_RATE_MAX;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cygnus_rate_constraint);
+ return 0;
+}
+
+static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aio->clk_trace.play_en = false;
+ else
+ aio->clk_trace.cap_en = false;
+
+ if (!aio->is_slave) {
+ u32 val;
+
+ val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ val &= CYGNUS_PLLCLKSEL_MASK;
+ if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+ dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+ val);
+ return;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (aio->clk_trace.play_clk_en) {
+ clk_disable_unprepare(aio->cygaud->
+ audio_clk[val]);
+ aio->clk_trace.play_clk_en = false;
+ }
+ } else {
+ if (aio->clk_trace.cap_clk_en) {
+ clk_disable_unprepare(aio->cygaud->
+ audio_clk[val]);
+ aio->clk_trace.cap_clk_en = false;
+ }
+ }
+ }
+}
+
+/*
+ * Bit Update Notes
+ * 31 Yes TDM Mode (1 = TDM, 0 = i2s)
+ * 30 Yes Slave Mode (1 = Slave, 0 = Master)
+ * 29:26 No Sclks per frame
+ * 25:18 Yes FS Width
+ * 17:14 No Valid Slots
+ * 13 No Bits (1 = 16 bits, 0 = 32 bits)
+ * 12:08 No Bits per samp
+ * 07 Yes Justifcation (1 = LSB, 0 = MSB)
+ * 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay
+ * 05 Yes SCLK polarity (1 = Rising, 0 = Falling)
+ * 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left)
+ * 03:02 Yes Reserved - write as zero
+ * 01 No Data Enable
+ * 00 No CLK Enable
+ */
+#define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03
+
+/* Input cfg is same as output, but the FS width is not a valid field */
+#define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)
+
+int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+ if ((len > 0) && (len < 256)) {
+ aio->fsync_width = len;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width);
+
+static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ u32 ssp_curcfg;
+ u32 ssp_newcfg;
+ u32 ssp_outcfg;
+ u32 ssp_incfg;
+ u32 val;
+ u32 mask;
+
+ dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt);
+
+ if (aio->port_type == PORT_SPDIF)
+ return -EINVAL;
+
+ ssp_newcfg = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
+ aio->is_slave = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
+ aio->is_slave = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+ aio->mode = CYGNUS_SSPMODE_I2S;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);
+
+ /* DSP_A = data after FS, DSP_B = data during FS */
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+
+ if ((aio->fsync_width > 0) && (aio->fsync_width < 256))
+ ssp_newcfg |=
+ (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);
+ else
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+
+ aio->mode = CYGNUS_SSPMODE_TDM;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * SSP out cfg.
+ * Retain bits we do not want to update, then OR in new bits
+ */
+ ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+ writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ /*
+ * SSP in cfg.
+ * Retain bits we do not want to update, then OR in new bits
+ */
+ ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+ ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+ writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+ val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+ /*
+ * Configure the word clk and bit clk as output or tristate
+ * Each port has 4 bits for controlling its pins.
+ * Shift the mask based upon port number.
+ */
+ mask = BIT(AUD_MISC_SEROUT_LRCK_OE)
+ | BIT(AUD_MISC_SEROUT_SCLK_OE)
+ | BIT(AUD_MISC_SEROUT_MCLK_OE);
+ mask = mask << (aio->portnum * 4);
+ if (aio->is_slave)
+ /* Set bit for tri-state */
+ val |= mask;
+ else
+ /* Clear bit for drive */
+ val &= ~mask;
+
+ dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val);
+ writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+ return 0;
+}
+
+static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(aio->cygaud->dev,
+ "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ssp_out_enable(aio);
+ else
+ audio_ssp_in_enable(aio);
+ cygaud->active_ports++;
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_ssp_out_disable(aio);
+ else
+ audio_ssp_in_disable(aio);
+ cygaud->active_ports--;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ u32 value;
+ int bits_per_slot = 0; /* default to 32-bits per slot */
+ int frame_bits;
+ unsigned int active_slots;
+ bool found = false;
+ int i;
+
+ if (tx_mask != rx_mask) {
+ dev_err(aio->cygaud->dev,
+ "%s tx_mask must equal rx_mask\n", __func__);
+ return -EINVAL;
+ }
+
+ active_slots = hweight32(tx_mask);
+
+ if (active_slots > 16)
+ return -EINVAL;
+
+ /* Slot value must be even */
+ if (active_slots % 2)
+ return -EINVAL;
+
+ /* We encode 16 slots as 0 in the reg */
+ if (active_slots == 16)
+ active_slots = 0;
+
+ /* Slot Width is either 16 or 32 */
+ switch (slot_width) {
+ case 16:
+ bits_per_slot = 1;
+ break;
+ case 32:
+ bits_per_slot = 0;
+ break;
+ default:
+ bits_per_slot = 0;
+ dev_warn(aio->cygaud->dev,
+ "%s Defaulting Slot Width to 32\n", __func__);
+ }
+
+ frame_bits = slots * slot_width;
+
+ for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {
+ if (ssp_valid_tdm_framesize[i] == frame_bits) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_err(aio->cygaud->dev,
+ "%s In TDM mode, frame bits INVALID (%d)\n",
+ __func__, frame_bits);
+ return -EINVAL;
+ }
+
+ aio->bit_per_frame = frame_bits;
+
+ dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",
+ __func__, active_slots, frame_bits);
+
+ /* Set capture side of ssp port */
+ value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+ value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+ value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+ value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+ value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+ writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+ /* Set playback side of ssp port */
+ value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+ value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+ value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+ value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+ value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+ writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+ if (!snd_soc_dai_active(cpu_dai))
+ return 0;
+
+ if (!aio->is_slave) {
+ u32 val;
+
+ val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+ val &= CYGNUS_PLLCLKSEL_MASK;
+ if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+ dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+ val);
+ return -EINVAL;
+ }
+
+ if (aio->clk_trace.cap_clk_en)
+ clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+ if (aio->clk_trace.play_clk_en)
+ clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+
+ aio->pll_clk_num = val;
+ }
+
+ return 0;
+}
+
+static int cygnus_ssp_suspend(struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ for_each_component_dais(component, dai)
+ ret |= __cygnus_ssp_suspend(dai);
+
+ return ret;
+}
+
+static int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ int error;
+
+ if (!snd_soc_dai_active(cpu_dai))
+ return 0;
+
+ if (!aio->is_slave) {
+ if (aio->clk_trace.cap_clk_en) {
+ error = clk_prepare_enable(aio->cygaud->
+ audio_clk[aio->pll_clk_num]);
+ if (error) {
+ dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ if (aio->clk_trace.play_clk_en) {
+ error = clk_prepare_enable(aio->cygaud->
+ audio_clk[aio->pll_clk_num]);
+ if (error) {
+ if (aio->clk_trace.cap_clk_en)
+ clk_disable_unprepare(aio->cygaud->
+ audio_clk[aio->pll_clk_num]);
+ dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int cygnus_ssp_resume(struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ for_each_component_dais(component, dai)
+ ret |= __cygnus_ssp_resume(dai);
+
+ return ret;
+}
+
+#else
+#define cygnus_ssp_suspend NULL
+#define cygnus_ssp_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {
+ .startup = cygnus_ssp_startup,
+ .shutdown = cygnus_ssp_shutdown,
+ .trigger = cygnus_ssp_trigger,
+ .hw_params = cygnus_ssp_hw_params,
+ .set_fmt = cygnus_ssp_set_fmt,
+ .set_sysclk = cygnus_ssp_set_sysclk,
+ .set_tdm_slot = cygnus_set_dai_tdm_slot,
+};
+
+static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = {
+ .startup = cygnus_ssp_startup,
+ .shutdown = cygnus_ssp_shutdown,
+ .trigger = cygnus_ssp_trigger,
+ .hw_params = cygnus_ssp_hw_params,
+ .set_sysclk = cygnus_ssp_set_sysclk,
+};
+
+#define INIT_CPU_DAI(num) { \
+ .name = "cygnus-ssp" #num, \
+ .playback = { \
+ .channels_min = 2, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_KNOT, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .channels_min = 2, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_KNOT, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &cygnus_ssp_dai_ops, \
+}
+
+static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
+ INIT_CPU_DAI(0),
+ INIT_CPU_DAI(1),
+ INIT_CPU_DAI(2),
+};
+
+static const struct snd_soc_dai_driver cygnus_spdif_dai_info = {
+ .name = "cygnus-spdif",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &cygnus_spdif_dai_ops,
+};
+
+static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
+
+static const struct snd_soc_component_driver cygnus_ssp_component = {
+ .name = "cygnus-audio",
+ .suspend = cygnus_ssp_suspend,
+ .resume = cygnus_ssp_resume,
+ .legacy_dai_naming = 1,
+};
+
+/*
+ * Return < 0 if error
+ * Return 0 if disabled
+ * Return 1 if enabled and node is parsed successfully
+ */
+static int parse_ssp_child_node(struct platform_device *pdev,
+ struct device_node *dn,
+ struct cygnus_audio *cygaud,
+ struct snd_soc_dai_driver *p_dai)
+{
+ struct cygnus_aio_port *aio;
+ struct cygnus_ssp_regs ssp_regs[3];
+ u32 rawval;
+ int portnum = -1;
+ enum cygnus_audio_port_type port_type;
+
+ if (of_property_read_u32(dn, "reg", &rawval)) {
+ dev_err(&pdev->dev, "Missing reg property\n");
+ return -EINVAL;
+ }
+
+ portnum = rawval;
+ switch (rawval) {
+ case 0:
+ ssp_regs[0] = INIT_SSP_REGS(0);
+ port_type = PORT_TDM;
+ break;
+ case 1:
+ ssp_regs[1] = INIT_SSP_REGS(1);
+ port_type = PORT_TDM;
+ break;
+ case 2:
+ ssp_regs[2] = INIT_SSP_REGS(2);
+ port_type = PORT_TDM;
+ break;
+ case 3:
+ port_type = PORT_SPDIF;
+ break;
+ default:
+ dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);
+ return -EINVAL;
+ }
+
+ aio = &cygaud->portinfo[portnum];
+ aio->cygaud = cygaud;
+ aio->portnum = portnum;
+ aio->port_type = port_type;
+ aio->fsync_width = -1;
+
+ switch (port_type) {
+ case PORT_TDM:
+ aio->regs = ssp_regs[portnum];
+ *p_dai = cygnus_ssp_dai_info[portnum];
+ aio->mode = CYGNUS_SSPMODE_UNKNOWN;
+ break;
+
+ case PORT_SPDIF:
+ aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;
+ aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;
+ aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;
+ aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;
+ *p_dai = cygnus_spdif_dai_info;
+
+ /* For the purposes of this code SPDIF can be I2S mode */
+ aio->mode = CYGNUS_SSPMODE_I2S;
+ break;
+ default:
+ dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);
+ return -EINVAL;
+ }
+
+ dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);
+ aio->streams_on = 0;
+ aio->cygaud->dev = &pdev->dev;
+ aio->clk_trace.play_en = false;
+ aio->clk_trace.cap_en = false;
+
+ audio_ssp_init_portregs(aio);
+ return 0;
+}
+
+static int audio_clk_init(struct platform_device *pdev,
+ struct cygnus_audio *cygaud)
+{
+ int i;
+ char clk_name[PROP_LEN_MAX];
+
+ for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {
+ snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);
+
+ cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(cygaud->audio_clk[i]))
+ return PTR_ERR(cygaud->audio_clk[i]);
+ }
+
+ return 0;
+}
+
+static int cygnus_ssp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child_node;
+ struct cygnus_audio *cygaud;
+ int err;
+ int node_count;
+ int active_port_count;
+
+ cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);
+ if (!cygaud)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, cygaud);
+
+ cygaud->audio = devm_platform_ioremap_resource_byname(pdev, "aud");
+ if (IS_ERR(cygaud->audio))
+ return PTR_ERR(cygaud->audio);
+
+ cygaud->i2s_in = devm_platform_ioremap_resource_byname(pdev, "i2s_in");
+ if (IS_ERR(cygaud->i2s_in))
+ return PTR_ERR(cygaud->i2s_in);
+
+ /* Tri-state all controlable pins until we know that we need them */
+ writel(CYGNUS_SSP_TRISTATE_MASK,
+ cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+ node_count = of_get_child_count(pdev->dev.of_node);
+ if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {
+ dev_err(dev, "child nodes is %d. Must be between 1 and %d\n",
+ node_count, CYGNUS_MAX_PORTS);
+ return -EINVAL;
+ }
+
+ active_port_count = 0;
+
+ for_each_available_child_of_node(pdev->dev.of_node, child_node) {
+ err = parse_ssp_child_node(pdev, child_node, cygaud,
+ &cygnus_ssp_dai[active_port_count]);
+
+ /* negative is err, 0 is active and good, 1 is disabled */
+ if (err < 0) {
+ of_node_put(child_node);
+ return err;
+ }
+ else if (!err) {
+ dev_dbg(dev, "Activating DAI: %s\n",
+ cygnus_ssp_dai[active_port_count].name);
+ active_port_count++;
+ }
+ }
+
+ cygaud->dev = dev;
+ cygaud->active_ports = 0;
+
+ dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
+ err = devm_snd_soc_register_component(dev, &cygnus_ssp_component,
+ cygnus_ssp_dai, active_port_count);
+ if (err) {
+ dev_err(dev, "snd_soc_register_dai failed\n");
+ return err;
+ }
+
+ cygaud->irq_num = platform_get_irq(pdev, 0);
+ if (cygaud->irq_num <= 0)
+ return cygaud->irq_num;
+
+ err = audio_clk_init(pdev, cygaud);
+ if (err) {
+ dev_err(dev, "audio clock initialization failed\n");
+ return err;
+ }
+
+ err = cygnus_soc_platform_register(dev, cygaud);
+ if (err) {
+ dev_err(dev, "platform reg error %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void cygnus_ssp_remove(struct platform_device *pdev)
+{
+ cygnus_soc_platform_unregister(&pdev->dev);
+}
+
+static const struct of_device_id cygnus_ssp_of_match[] = {
+ { .compatible = "brcm,cygnus-audio" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
+
+static struct platform_driver cygnus_ssp_driver = {
+ .probe = cygnus_ssp_probe,
+ .remove = cygnus_ssp_remove,
+ .driver = {
+ .name = "cygnus-ssp",
+ .of_match_table = cygnus_ssp_of_match,
+ },
+};
+
+module_platform_driver(cygnus_ssp_driver);
+
+MODULE_ALIAS("platform:cygnus-ssp");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");
diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h
new file mode 100644
index 000000000000..4925e03c3c30
--- /dev/null
+++ b/sound/soc/bcm/cygnus-ssp.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2014-2015 Broadcom Corporation */
+#ifndef __CYGNUS_SSP_H__
+#define __CYGNUS_SSP_H__
+
+#define CYGNUS_TDM_DAI_MAX_SLOTS 16
+
+#define CYGNUS_MAX_PLAYBACK_PORTS 4
+#define CYGNUS_MAX_CAPTURE_PORTS 3
+#define CYGNUS_MAX_I2S_PORTS 3
+#define CYGNUS_MAX_PORTS CYGNUS_MAX_PLAYBACK_PORTS
+#define CYGNUS_AUIDO_MAX_NUM_CLKS 3
+
+#define CYGNUS_SSP_FRAMEBITS_DIV 1
+
+#define CYGNUS_SSPMODE_I2S 0
+#define CYGNUS_SSPMODE_TDM 1
+#define CYGNUS_SSPMODE_UNKNOWN -1
+
+#define CYGNUS_SSP_CLKSRC_PLL 0
+
+/* Max string length of our dt property names */
+#define PROP_LEN_MAX 40
+
+struct ringbuf_regs {
+ unsigned rdaddr;
+ unsigned wraddr;
+ unsigned baseaddr;
+ unsigned endaddr;
+ unsigned fmark; /* freemark for play, fullmark for caputure */
+ unsigned period_bytes;
+ unsigned buf_size;
+};
+
+#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
+ .rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
+ .wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
+ .baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
+ .endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
+ .fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
+ .period_bytes = 0, \
+ .buf_size = 0, \
+})
+
+#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs) { \
+ .rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
+ .wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
+ .baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
+ .endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
+ .fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
+ .period_bytes = 0, \
+ .buf_size = 0, \
+})
+
+enum cygnus_audio_port_type {
+ PORT_TDM,
+ PORT_SPDIF,
+};
+
+struct cygnus_ssp_regs {
+ u32 i2s_stream_cfg;
+ u32 i2s_cfg;
+ u32 i2s_cap_stream_cfg;
+ u32 i2s_cap_cfg;
+ u32 i2s_mclk_cfg;
+
+ u32 bf_destch_ctrl;
+ u32 bf_destch_cfg;
+ u32 bf_sourcech_ctrl;
+ u32 bf_sourcech_cfg;
+ u32 bf_sourcech_grp;
+};
+
+struct cygnus_track_clk {
+ bool cap_en;
+ bool play_en;
+ bool cap_clk_en;
+ bool play_clk_en;
+};
+
+struct cygnus_aio_port {
+ int portnum;
+ int mode;
+ bool is_slave;
+ int streams_on; /* will be 0 if both capture and play are off */
+ int fsync_width;
+ int port_type;
+
+ u32 mclk;
+ u32 lrclk;
+ u32 bit_per_frame;
+ u32 pll_clk_num;
+
+ struct cygnus_audio *cygaud;
+ struct cygnus_ssp_regs regs;
+
+ struct ringbuf_regs play_rb_regs;
+ struct ringbuf_regs capture_rb_regs;
+
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+
+ struct cygnus_track_clk clk_trace;
+};
+
+
+struct cygnus_audio {
+ struct cygnus_aio_port portinfo[CYGNUS_MAX_PORTS];
+
+ int irq_num;
+ void __iomem *audio;
+ struct device *dev;
+ void __iomem *i2s_in;
+
+ struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
+ int active_ports;
+ unsigned long vco_rate;
+};
+
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+ int len);
+extern int cygnus_soc_platform_register(struct device *dev,
+ struct cygnus_audio *cygaud);
+extern int cygnus_soc_platform_unregister(struct device *dev);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+ int len);
+#endif
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
deleted file mode 100644
index 6410aa2cc2cf..000000000000
--- a/sound/soc/blackfin/Kconfig
+++ /dev/null
@@ -1,205 +0,0 @@
-config SND_BF5XX_I2S
- tristate "SoC I2S Audio for the ADI Blackfin chip"
- depends on BLACKFIN
- select SND_BF5XX_SOC_SPORT if !BF60x
- select SND_BF6XX_SOC_SPORT if BF60x
- help
- Say Y or M if you want to add support for codecs attached to
- the Blackfin SPORT (synchronous serial ports) interface in I2S
- mode (supports single stereo In/Out).
- You will also need to select the audio interfaces to support below.
-
-config SND_BF5XX_SOC_SSM2602
- tristate "SoC SSM2602 Audio Codec Add-On Card support"
- depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
- select SND_BF5XX_SOC_I2S if !BF60x
- select SND_BF6XX_SOC_I2S if BF60x
- select SND_SOC_SSM2602_SPI if SPI_MASTER
- select SND_SOC_SSM2602_I2C if I2C
- help
- Say Y if you want to add support for the Analog Devices
- SSM2602 Audio Codec Add-On Card.
-
-config SND_SOC_BFIN_EVAL_ADAU1701
- tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1701
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
-config SND_SOC_BFIN_EVAL_ADAU1373
- tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1373
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that first ADAU1373 DAI is connected to the
- first SPORT port on the BF5XX board.
-
-config SND_SOC_BFIN_EVAL_ADAU1X61
- tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1761_I2C
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that the ADAU1X61 is connected to the
- first SPORT port on the BF5XX board.
-
-config SND_SOC_BFIN_EVAL_ADAU1X81
- tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards"
- depends on SND_BF5XX_I2S && I2C
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAU1781_I2C
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81
- board connected to one of the Blackfin evaluation boards like the
- BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that the ADAU1X81 is connected to the
- first SPORT port on the BF5XX board.
-
-config SND_SOC_BFIN_EVAL_ADAV80X
- tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
- depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
- select SND_BF5XX_SOC_I2S
- select SND_SOC_ADAV801 if SPI_MASTER
- select SND_SOC_ADAV803 if I2C
- help
- Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or
- EVAL-ADAV803 board connected to one of the Blackfin evaluation boards
- like the BF5XX-STAMP or BF5XX-EZKIT.
-
- Note: This driver assumes that the ADAV80X digital record and playback
- interfaces are connected to the first SPORT port on the BF5XX board.
-
-config SND_BF5XX_SOC_AD1836
- tristate "SoC AD1836 Audio support for BF5xx"
- depends on SND_BF5XX_I2S && SPI_MASTER
- select SND_BF5XX_SOC_I2S
- select SND_SOC_AD1836
- help
- Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
-
-config SND_BF5XX_SOC_AD193X
- tristate "SoC AD193X Audio support for Blackfin"
- depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
- select SND_BF5XX_SOC_I2S
- select SND_SOC_AD193X_I2C if I2C
- select SND_SOC_AD193X_SPI if SPI_MASTER
- help
- Say Y if you want to add support for AD193X codec on Blackfin.
- This driver supports AD1936, AD1937, AD1938 and AD1939.
-
-config SND_BF5XX_SOC_AD73311
- tristate "SoC AD73311 Audio support for Blackfin"
- depends on SND_BF5XX_I2S
- select SND_BF5XX_SOC_I2S
- select SND_SOC_AD73311
- help
- Say Y if you want to add support for AD73311 codec on Blackfin.
-
-config SND_BFIN_AD73311_SE
- int "PF pin for AD73311L Chip Select"
- depends on SND_BF5XX_SOC_AD73311
- default 4
- help
- Enter the GPIO used to control AD73311's SE pin. Acceptable
- values are 0 to 7
-
-config SND_BF5XX_AC97
- tristate "SoC AC97 Audio for the ADI BF5xx chip"
- depends on BLACKFIN
- select AC97_BUS
- select SND_SOC_AC97_BUS
- select SND_BF5XX_SOC_SPORT
- select SND_BF5XX_SOC_AC97
- help
- Say Y or M if you want to add support for codecs attached to
- the Blackfin SPORT (synchronous serial ports) interface in slot 16
- mode (pseudo AC97 interface).
- You will also need to select the audio interfaces to support below.
-
- Note:
- AC97 codecs which do not implement the slot-16 mode will not function
- properly with this driver. This driver is known to work with the
- Analog Devices line of AC97 codecs.
-
-config SND_BF5XX_MMAP_SUPPORT
- bool "Enable MMAP Support"
- depends on SND_BF5XX_AC97
- default y
- help
- Say y if you want AC97 driver to support mmap mode.
- We introduce an intermediate buffer to simulate mmap.
-
-config SND_BF5XX_MULTICHAN_SUPPORT
- bool "Enable Multichannel Support"
- depends on SND_BF5XX_AC97
- default n
- help
- Say y if you want AC97 driver to support up to 5.1 channel audio.
- this mode will consume much more memory for DMA.
-
-config SND_BF5XX_HAVE_COLD_RESET
- bool "BOARD has COLD Reset GPIO"
- depends on SND_BF5XX_AC97
- default y if BFIN548_EZKIT
- default n if !BFIN548_EZKIT
-
-config SND_BF5XX_RESET_GPIO_NUM
- int "Set a GPIO for cold reset"
- depends on SND_BF5XX_HAVE_COLD_RESET
- range 0 159
- default 19 if BFIN548_EZKIT
- default 5 if BFIN537_STAMP
- default 0
- help
- Set the correct GPIO for RESET the sound chip.
-
-config SND_BF5XX_SOC_AD1980
- tristate "SoC AD1980/1 Audio support for BF5xx (Obsolete)"
- depends on SND_BF5XX_AC97
- select SND_BF5XX_SOC_AC97
- select SND_SOC_AD1980
- help
- Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
-
- 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.
-
-config SND_BF5XX_SOC_SPORT
- tristate
-
-config SND_BF6XX_SOC_SPORT
- tristate
-
-config SND_BF5XX_SOC_I2S
- tristate
-
-config SND_BF6XX_SOC_I2S
- tristate
-
-config SND_BF5XX_SOC_AC97
- tristate
-
-config SND_BF5XX_SPORT_NUM
- int "Set a SPORT for Sound chip"
- depends on (SND_BF5XX_SOC_SPORT || SND_BF6XX_SOC_SPORT)
- range 0 3 if BF54x
- range 0 1 if !BF54x
- default 0
- help
- Set the correct SPORT for sound chip.
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
deleted file mode 100644
index f21e948b2e9b..000000000000
--- a/sound/soc/blackfin/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-# Blackfin Platform Support
-snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
-snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
-snd-soc-bf5xx-sport-objs := bf5xx-sport.o
-snd-soc-bf6xx-sport-objs := bf6xx-sport.o
-snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
-snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
-snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o
-
-obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
-obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
-obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
-obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o
-obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
-obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
-obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o
-
-# Blackfin Machine Support
-snd-ad1836-objs := bf5xx-ad1836.o
-snd-ad1980-objs := bf5xx-ad1980.o
-snd-ssm2602-objs := bf5xx-ssm2602.o
-snd-ad73311-objs := bf5xx-ad73311.o
-snd-ad193x-objs := bf5xx-ad193x.o
-snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
-snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
-snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o
-snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
-snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
-
-obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
-obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
-obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
-obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
-obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
-obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
deleted file mode 100644
index 02ad2606fa19..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ac97-pcm.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: DMA Driver for AC97 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/init.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/gfp.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-
-#include "bf5xx-ac97.h"
-#include "bf5xx-sport.h"
-
-static unsigned int ac97_chan_mask[] = {
- SP_FL, /* Mono */
- SP_STEREO, /* Stereo */
- SP_2DOT1, /* 2.1*/
- SP_QUAD,/*Quadraquic*/
- SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
- SP_5DOT1, /* 5.1 */
-};
-
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
-static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
- sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
- runtime->channels, count, chan_mask);
- sport->tx_pos += runtime->period_size;
- if (sport->tx_pos >= runtime->buffer_size)
- sport->tx_pos %= runtime->buffer_size;
- sport->tx_delay_pos = sport->tx_pos;
- } else {
- bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf +
- sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos *
- runtime->channels, count);
- sport->rx_pos += runtime->period_size;
- if (sport->rx_pos >= runtime->buffer_size)
- sport->rx_pos %= runtime->buffer_size;
- }
-}
-#endif
-
-static void bf5xx_dma_irq(void *data)
-{
- struct snd_pcm_substream *pcm = data;
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- struct snd_pcm_runtime *runtime = pcm->runtime;
- struct sport_device *sport = runtime->private_data;
- bf5xx_mmap_copy(pcm, runtime->period_size);
- if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (sport->once == 0) {
- snd_pcm_period_elapsed(pcm);
- bf5xx_mmap_copy(pcm, runtime->period_size);
- sport->once = 1;
- }
- }
-#endif
- snd_pcm_period_elapsed(pcm);
-}
-
-/* The memory size for pure pcm data is 128*1024 = 0x20000 bytes.
- * The total rx/tx buffer is for ac97 frame to hold all pcm data
- * is 0x20000 * sizeof(struct ac97_frame) / 4.
- */
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
-#endif
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
-
- .period_bytes_min = 32,
- .period_bytes_max = 0x10000,
- .periods_min = 1,
- .periods_max = PAGE_SIZE/32,
- .buffer_bytes_max = 0x20000, /* 128 kbytes */
- .fifo_size = 16,
-};
-
-static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max
- * sizeof(struct ac97_frame) / 4;
-
- snd_pcm_lib_malloc_pages(substream, size);
-
- return 0;
-}
-
-static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport->once = 0;
- if (runtime->dma_area)
- memset(runtime->dma_area, 0, runtime->buffer_size);
- memset(sport->tx_dma_buf, 0, runtime->buffer_size *
- sizeof(struct ac97_frame));
- } else
- memset(sport->rx_dma_buf, 0, runtime->buffer_size *
- sizeof(struct ac97_frame));
-#endif
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
-
- /* An intermediate buffer is introduced for implementing mmap for
- * SPORT working in TMD mode(include AC97).
- */
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
- runtime->period_size * sizeof(struct ac97_frame));
- } else {
- sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
- runtime->period_size * sizeof(struct ac97_frame));
- }
-#else
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_tx_dma(sport, runtime->dma_area, runtime->periods,
- runtime->period_size * sizeof(struct ac97_frame));
- } else {
- sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_rx_dma(sport, runtime->dma_area, runtime->periods,
- runtime->period_size * sizeof(struct ac97_frame));
- }
-#endif
- return 0;
-}
-
-static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- int ret = 0;
-
- pr_debug("%s enter\n", __func__);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- bf5xx_mmap_copy(substream, runtime->period_size);
- sport->tx_delay_pos = 0;
-#endif
- sport_tx_start(sport);
- } else
- sport_rx_start(sport);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- sport->tx_pos = 0;
-#endif
- sport_tx_stop(sport);
- } else {
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- sport->rx_pos = 0;
-#endif
- sport_rx_stop(sport);
- }
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- unsigned int curr;
-
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- curr = sport->tx_delay_pos;
- else
- curr = sport->rx_pos;
-#else
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame);
- else
- curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame);
-
-#endif
- return curr;
-}
-
-static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- if (sport_handle != NULL)
- runtime->private_data = sport_handle;
- else {
- pr_err("sport_handle is NULL\n");
- return -1;
- }
- return 0;
-
- out:
- return ret;
-}
-
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
-static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- size_t size = vma->vm_end - vma->vm_start;
- vma->vm_start = (unsigned long)runtime->dma_area;
- vma->vm_end = vma->vm_start + size;
- vma->vm_flags |= VM_SHARED;
- return 0 ;
-}
-#else
-static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos,
- void __user *buf, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
- pr_debug("%s copy pos:0x%lx count:0x%lx\n",
- substream->stream ? "Capture" : "Playback", pos, count);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos,
- (__u16 *)buf, count, chan_mask);
- else
- bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
- (__u16 *)buf, count);
- return 0;
-}
-#endif
-
-static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
- .open = bf5xx_pcm_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = bf5xx_pcm_hw_params,
- .hw_free = bf5xx_pcm_hw_free,
- .prepare = bf5xx_pcm_prepare,
- .trigger = bf5xx_pcm_trigger,
- .pointer = bf5xx_pcm_pointer,
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- .mmap = bf5xx_pcm_mmap,
-#else
- .copy = bf5xx_pcm_copy,
-#endif
-};
-
-static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max
- * sizeof(struct ac97_frame) / 4;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area) {
- pr_err("Failed to allocate dma memory\n");
- pr_err("Please increase uncached DMA memory region\n");
- return -ENOMEM;
- }
- buf->bytes = size;
-
- pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
- buf->area, buf->bytes);
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
-/*
- * Need to allocate local buffer when enable
- * MMAP for SPORT working in TMD mode (include AC97).
- */
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (!sport_handle->tx_dma_buf) {
- sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
- size, &sport_handle->tx_dma_phy, GFP_KERNEL);
- if (!sport_handle->tx_dma_buf) {
- pr_err("Failed to allocate memory for tx dma buf - Please increase uncached DMA memory region\n");
- return -ENOMEM;
- } else
- memset(sport_handle->tx_dma_buf, 0, size);
- } else
- memset(sport_handle->tx_dma_buf, 0, size);
- } else {
- if (!sport_handle->rx_dma_buf) {
- sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
- size, &sport_handle->rx_dma_phy, GFP_KERNEL);
- if (!sport_handle->rx_dma_buf) {
- pr_err("Failed to allocate memory for rx dma buf - Please increase uncached DMA memory region\n");
- return -ENOMEM;
- } else
- memset(sport_handle->rx_dma_buf, 0, size);
- } else
- memset(sport_handle->rx_dma_buf, 0, size);
- }
-#endif
- return 0;
-}
-
-static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
- sizeof(struct ac97_frame) / 4;
-#endif
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_coherent(NULL, buf->bytes, buf->area, 0);
- buf->area = NULL;
-#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (sport_handle->tx_dma_buf)
- dma_free_coherent(NULL, size, \
- sport_handle->tx_dma_buf, 0);
- sport_handle->tx_dma_buf = NULL;
- } else {
-
- if (sport_handle->rx_dma_buf)
- dma_free_coherent(NULL, size, \
- sport_handle->rx_dma_buf, 0);
- sport_handle->rx_dma_buf = NULL;
- }
-#endif
- }
-}
-
-static int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- pr_debug("%s enter\n", __func__);
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-static struct snd_soc_platform_driver bf5xx_ac97_soc_platform = {
- .ops = &bf5xx_pcm_ac97_ops,
- .pcm_new = bf5xx_pcm_ac97_new,
- .pcm_free = bf5xx_pcm_free_dma_buffers,
-};
-
-static int bf5xx_soc_platform_probe(struct platform_device *pdev)
-{
- return devm_snd_soc_register_platform(&pdev->dev,
- &bf5xx_ac97_soc_platform);
-}
-
-static struct platform_driver bf5xx_pcm_driver = {
- .driver = {
- .name = "bfin-ac97-pcm-audio",
- },
-
- .probe = bf5xx_soc_platform_probe,
-};
-
-module_platform_driver(bf5xx_pcm_driver);
-
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
deleted file mode 100644
index a040cfe29fc0..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip.
- *
- * Author: Roy Huang
- * Created: 11th. June 2007
- * Copyright: Analog Device 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/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include <asm/irq.h>
-#include <asm/portmux.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-
-#include "bf5xx-sport.h"
-#include "bf5xx-ac97.h"
-
-/* Anomaly notes:
- * 05000250 - AD1980 is running in TDM mode and RFS/TFS are generated by SPORT
- * contrtoller. But, RFSDIV and TFSDIV are always set to 16*16-1,
- * while the max AC97 data size is 13*16. The DIV is always larger
- * than data size. AD73311 and ad2602 are not running in TDM mode.
- * AD1836 and AD73322 depend on external RFS/TFS only. So, this
- * anomaly does not affect blackfin sound drivers.
-*/
-
-static struct sport_device *ac97_sport_handle;
-
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
- size_t count, unsigned int chan_mask)
-{
- while (count--) {
- dst->ac97_tag = TAG_VALID;
- if (chan_mask & SP_FL) {
- dst->ac97_pcm_r = *src++;
- dst->ac97_tag |= TAG_PCM_RIGHT;
- }
- if (chan_mask & SP_FR) {
- dst->ac97_pcm_l = *src++;
- dst->ac97_tag |= TAG_PCM_LEFT;
-
- }
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- if (chan_mask & SP_SR) {
- dst->ac97_sl = *src++;
- dst->ac97_tag |= TAG_PCM_SL;
- }
- if (chan_mask & SP_SL) {
- dst->ac97_sr = *src++;
- dst->ac97_tag |= TAG_PCM_SR;
- }
- if (chan_mask & SP_LFE) {
- dst->ac97_lfe = *src++;
- dst->ac97_tag |= TAG_PCM_LFE;
- }
- if (chan_mask & SP_FC) {
- dst->ac97_center = *src++;
- dst->ac97_tag |= TAG_PCM_CENTER;
- }
-#endif
- dst++;
- }
-}
-EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
-
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
- size_t count)
-{
- while (count--) {
- *(dst++) = src->ac97_pcm_l;
- *(dst++) = src->ac97_pcm_r;
- src++;
- }
-}
-EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
-
-static unsigned int sport_tx_curr_frag(struct sport_device *sport)
-{
- return sport->tx_curr_frag = sport_curr_offset_tx(sport) /
- sport->tx_fragsize;
-}
-
-static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
-{
- struct sport_device *sport = ac97_sport_handle;
- int *cmd_count = sport->private_data;
- int nextfrag = sport_tx_curr_frag(sport);
- struct ac97_frame *nextwrite;
-
- sport_incfrag(sport, &nextfrag, 1);
-
- nextwrite = (struct ac97_frame *)(sport->tx_buf +
- nextfrag * sport->tx_fragsize);
- pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
- sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
- nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
- nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
- nextwrite[cmd_count[nextfrag]].ac97_data = data;
- ++cmd_count[nextfrag];
- pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
- addr >> 8, data, nextfrag);
-}
-
-static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct sport_device *sport_handle = ac97_sport_handle;
- struct ac97_frame out_frame[2], in_frame[2];
-
- pr_debug("%s enter 0x%x\n", __func__, reg);
-
- /* When dma descriptor is enabled, the register should not be read */
- if (sport_handle->tx_run || sport_handle->rx_run) {
- pr_err("Could you send a mail to cliff.cai@analog.com "
- "to report this?\n");
- return -EFAULT;
- }
-
- memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
- memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
- out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
- out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
- sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
- (unsigned char *)&in_frame,
- 2 * sizeof(struct ac97_frame));
- return in_frame[1].ac97_data;
-}
-
-void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- struct sport_device *sport_handle = ac97_sport_handle;
-
- pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
-
- if (sport_handle->tx_run) {
- enqueue_cmd(ac97, (reg << 8), val); /* write */
- enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
- } else {
- struct ac97_frame frame;
- memset(&frame, 0, sizeof(struct ac97_frame));
- frame.ac97_tag = TAG_VALID | TAG_CMD;
- frame.ac97_addr = (reg << 8);
- frame.ac97_data = val;
- sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
- NULL, sizeof(struct ac97_frame));
- }
-}
-
-static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct sport_device *sport_handle = ac97_sport_handle;
- u16 gpio = P_IDENT(sport_handle->pin_req[3]);
-
- pr_debug("%s enter\n", __func__);
-
- peripheral_free_list(sport_handle->pin_req);
- gpio_request(gpio, "bf5xx-ac97");
- gpio_direction_output(gpio, 1);
- udelay(2);
- gpio_set_value(gpio, 0);
- udelay(1);
- gpio_free(gpio);
- peripheral_request_list(sport_handle->pin_req, "soc-audio");
-}
-
-static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
-{
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- pr_debug("%s enter\n", __func__);
-
- /* It is specified for bf548-ezkit */
- gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
- /* Keep reset pin low for 1 ms */
- mdelay(1);
- gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
- /* Wait for bit clock recover */
- mdelay(1);
-#else
- pr_info("%s: Not implemented\n", __func__);
-#endif
-}
-
-static struct snd_ac97_bus_ops bf5xx_ac97_ops = {
- .read = bf5xx_ac97_read,
- .write = bf5xx_ac97_write,
- .warm_reset = bf5xx_ac97_warm_reset,
- .reset = bf5xx_ac97_cold_reset,
-};
-
-#ifdef CONFIG_PM
-static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- pr_debug("%s : sport %d\n", __func__, dai->id);
- if (!dai->active)
- return 0;
- if (dai->capture_active)
- sport_rx_stop(sport);
- if (dai->playback_active)
- sport_tx_stop(sport);
- return 0;
-}
-
-static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
-{
- int ret;
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- pr_debug("%s : sport %d\n", __func__, dai->id);
- if (!dai->active)
- return 0;
-
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- ret = sport_set_multichannel(sport, 16, 0x3FF, 0x3FF, 1);
-#else
- ret = sport_set_multichannel(sport, 16, 0x1F, 0x1F, 1);
-#endif
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- return 0;
-}
-
-#else
-#define bf5xx_ac97_suspend NULL
-#define bf5xx_ac97_resume NULL
-#endif
-
-static struct snd_soc_dai_driver bfin_ac97_dai = {
- .bus_control = true,
- .suspend = bf5xx_ac97_suspend,
- .resume = bf5xx_ac97_resume,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- .channels_max = 6,
-#else
- .channels_max = 2,
-#endif
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
-};
-
-static const struct snd_soc_component_driver bfin_ac97_component = {
- .name = "bfin-ac97",
-};
-
-static int asoc_bfin_ac97_probe(struct platform_device *pdev)
-{
- struct sport_device *sport_handle;
- int ret;
-
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- /* Request PB3 as reset pin */
- ret = devm_gpio_request_one(&pdev->dev,
- CONFIG_SND_BF5XX_RESET_GPIO_NUM,
- GPIOF_OUT_INIT_HIGH, "SND_AD198x RESET");
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to request GPIO_%d for reset: %d\n",
- CONFIG_SND_BF5XX_RESET_GPIO_NUM, ret);
- return ret;
- }
-#endif
-
- sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
- PAGE_SIZE);
- if (!sport_handle) {
- ret = -ENODEV;
- goto sport_err;
- }
-
- /*SPORT works in TDM mode to simulate AC97 transfers*/
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 0x3FF, 1);
-#else
- ret = sport_set_multichannel(sport_handle, 16, 0x1F, 0x1F, 1);
-#endif
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = snd_soc_set_ac97_ops(&bf5xx_ac97_ops);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
- goto sport_config_err;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &bfin_ac97_component,
- &bfin_ac97_dai, 1);
- if (ret) {
- pr_err("Failed to register DAI: %d\n", ret);
- goto sport_config_err;
- }
-
- ac97_sport_handle = sport_handle;
-
- return 0;
-
-sport_config_err:
- sport_done(sport_handle);
-sport_err:
- snd_soc_set_ac97_ops(NULL);
-
- return ret;
-}
-
-static int asoc_bfin_ac97_remove(struct platform_device *pdev)
-{
- struct sport_device *sport_handle = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_done(sport_handle);
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver asoc_bfin_ac97_driver = {
- .driver = {
- .name = "bfin-ac97",
- },
-
- .probe = asoc_bfin_ac97_probe,
- .remove = asoc_bfin_ac97_remove,
-};
-
-module_platform_driver(asoc_bfin_ac97_driver);
-
-MODULE_AUTHOR("Roy Huang");
-MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h
deleted file mode 100644
index a680fdc9bb42..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * sound/soc/blackfin/bf5xx-ac97.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 _BF5XX_AC97_H
-#define _BF5XX_AC97_H
-
-/* Frame format in memory, only support stereo currently */
-struct ac97_frame {
- u16 ac97_tag; /* slot 0 */
- u16 ac97_addr; /* slot 1 */
- u16 ac97_data; /* slot 2 */
- u16 ac97_pcm_l; /*slot 3:front left*/
- u16 ac97_pcm_r; /*slot 4:front left*/
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- u16 ac97_mdm_l1;
- u16 ac97_center; /*slot 6:center*/
- u16 ac97_sl; /*slot 7:surround left*/
- u16 ac97_sr; /*slot 8:surround right*/
- u16 ac97_lfe; /*slot 9:lfe*/
-#endif
-} __attribute__ ((packed));
-
-/* Speaker location */
-#define SP_FL 0x0001
-#define SP_FR 0x0010
-#define SP_FC 0x0002
-#define SP_LFE 0x0020
-#define SP_SL 0x0004
-#define SP_SR 0x0040
-
-#define SP_STEREO (SP_FL | SP_FR)
-#define SP_2DOT1 (SP_FL | SP_FR | SP_LFE)
-#define SP_QUAD (SP_FL | SP_FR | SP_SL | SP_SR)
-#define SP_5DOT1 (SP_FL | SP_FR | SP_FC | SP_LFE | SP_SL | SP_SR)
-
-#define TAG_VALID 0x8000
-#define TAG_CMD 0x6000
-#define TAG_PCM_LEFT 0x1000
-#define TAG_PCM_RIGHT 0x0800
-#define TAG_PCM_MDM_L1 0x0400
-#define TAG_PCM_CENTER 0x0200
-#define TAG_PCM_SL 0x0100
-#define TAG_PCM_SR 0x0080
-#define TAG_PCM_LFE 0x0040
-
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
- size_t count, unsigned int chan_mask);
-
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, \
- size_t count);
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
deleted file mode 100644
index 864df2616e10..000000000000
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ad1836.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Aug 4 2009
- * Description: Board driver for ad1836 sound chip
- *
- * 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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <asm/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "../codecs/ad1836.h"
-
-static struct snd_soc_card bf5xx_ad1836;
-
-static int bf5xx_ad1836_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
- int ret = 0;
-
- /* set cpu DAI channel mapping */
- ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
- channel_map, ARRAY_SIZE(channel_map), channel_map);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-#define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ad1836_dai = {
- .name = "ad1836",
- .stream_name = "AD1836",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .dai_fmt = BF5XX_AD1836_DAIFMT,
- .init = bf5xx_ad1836_init,
-};
-
-static struct snd_soc_card bf5xx_ad1836 = {
- .name = "bfin-ad1836",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_ad1836_dai,
- .num_links = 1,
-};
-
-static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bf5xx_ad1836;
- const char **link_name;
- int ret;
-
- link_name = pdev->dev.platform_data;
- if (!link_name) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
- bf5xx_ad1836_dai.cpu_dai_name = link_name[0];
- bf5xx_ad1836_dai.codec_name = link_name[1];
-
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "Failed to register card\n");
- return ret;
-}
-
-static struct platform_driver bf5xx_ad1836_driver = {
- .driver = {
- .name = "bfin-snd-ad1836",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bf5xx_ad1836_driver_probe,
-};
-module_platform_driver(bf5xx_ad1836_driver);
-
-/* Module information */
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("ALSA SoC AD1836 board driver");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c
deleted file mode 100644
index 603ad1f2b9b9..000000000000
--- a/sound/soc/blackfin/bf5xx-ad193x.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ad193x.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Thur June 4 2009
- * Description: Board driver for ad193x sound chip
- *
- * 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/moduleparam.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <asm/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "../codecs/ad193x.h"
-
-static struct snd_soc_card bf5xx_ad193x;
-
-static int bf5xx_ad193x_link_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set codec DAI slots, 8 channels, all channels are enabled */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-#define BF5XX_AD193X_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
- {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "spi0.5",
- .dai_fmt = BF5XX_AD193X_DAIFMT,
- .init = bf5xx_ad193x_link_init,
- },
- {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "spi0.5",
- .dai_fmt = BF5XX_AD193X_DAIFMT,
- .init = bf5xx_ad193x_link_init,
- },
-};
-
-static struct snd_soc_card bf5xx_ad193x = {
- .name = "bfin-ad193x",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .num_links = 1,
-};
-
-static struct platform_device *bfxx_ad193x_snd_device;
-
-static int __init bf5xx_ad193x_init(void)
-{
- int ret;
-
- bfxx_ad193x_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bfxx_ad193x_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bfxx_ad193x_snd_device, &bf5xx_ad193x);
- ret = platform_device_add(bfxx_ad193x_snd_device);
-
- if (ret)
- platform_device_put(bfxx_ad193x_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ad193x_exit(void)
-{
- platform_device_unregister(bfxx_ad193x_snd_device);
-}
-
-module_init(bf5xx_ad193x_init);
-module_exit(bf5xx_ad193x_exit);
-
-/* Module information */
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("ALSA SoC AD193X board driver");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
deleted file mode 100644
index 0fa81a523b8a..000000000000
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ad1980.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: Board driver for AD1980/1 audio codec
- *
- * 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
- */
-
-/*
- * 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/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <asm/dma.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <linux/gpio.h>
-#include <asm/portmux.h>
-
-#include "bf5xx-ac97.h"
-
-static struct snd_soc_card bf5xx_board;
-
-static struct snd_soc_dai_link bf5xx_board_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97.0",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-ac97-pcm-audio",
- .codec_name = "ad1980",
- },
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97.1",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-ac97-pcm-audio",
- .codec_name = "ad1980",
- },
-};
-
-static struct snd_soc_card bf5xx_board = {
- .name = "bfin-ad1980",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .num_links = 1,
-};
-
-static struct platform_device *bf5xx_board_snd_device;
-
-static int __init bf5xx_board_init(void)
-{
- int ret;
-
- bf5xx_board_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf5xx_board_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board);
- ret = platform_device_add(bf5xx_board_snd_device);
-
- if (ret)
- platform_device_put(bf5xx_board_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_board_exit(void)
-{
- platform_device_unregister(bf5xx_board_snd_device);
-}
-
-module_init(bf5xx_board_init);
-module_exit(bf5xx_board_exit);
-
-/* Module information */
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board (Obsolete)");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
deleted file mode 100644
index 786bbdd96e7c..000000000000
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ad73311.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Thur Sep 25 2008
- * Description: Board driver for ad73311 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/moduleparam.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <asm/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "../codecs/ad73311.h"
-#include "bf5xx-sport.h"
-
-#if CONFIG_SND_BF5XX_SPORT_NUM == 0
-#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1
-#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1
-#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2
-#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16
-#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT
-#else
-#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1
-#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1
-#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2
-#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16
-#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT
-#endif
-
-#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
-
-static struct snd_soc_card bf5xx_ad73311;
-
-static int snd_ad73311_startup(void)
-{
- pr_debug("%s enter\n", __func__);
-
- /* Pull up SE pin on AD73311L */
- gpio_set_value(GPIO_SE, 1);
- return 0;
-}
-
-static int snd_ad73311_configure(void)
-{
- unsigned short ctrl_regs[6];
- unsigned short status = 0;
- int count = 0;
-
- /* DMCLK = MCLK = 16.384 MHz
- * SCLK = DMCLK/8 = 2.048 MHz
- * Sample Rate = DMCLK/2048 = 8 KHz
- */
- ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \
- REGB_SCDIV(0) | REGB_DIRATE(0);
- ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \
- REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ;
- ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \
- REGD_IGS(2);
- ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f);
- ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ;
- ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA;
-
- local_irq_disable();
- snd_ad73311_startup();
- udelay(1);
-
- bfin_write_SPORT_TCR1(TFSR);
- bfin_write_SPORT_TCR2(0xF);
- SSYNC();
-
- /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to
- * FIFO before enable SPORT to transfer the data
- */
- for (count = 0; count < 6; count++)
- bfin_write_SPORT_TX16(ctrl_regs[count]);
- SSYNC();
- bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN);
- SSYNC();
-
- /* When TUVF is set, the data is already send out */
- while (!(status & TUVF) && ++count < 10000) {
- udelay(1);
- status = bfin_read_SPORT_STAT();
- SSYNC();
- }
- bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN);
- SSYNC();
- local_irq_enable();
-
- if (count >= 10000) {
- printk(KERN_ERR "ad73311: failed to configure codec\n");
- return -1;
- }
- return 0;
-}
-
-static int bf5xx_probe(struct snd_soc_card *card)
-{
- int err;
- if (gpio_request(GPIO_SE, "AD73311_SE")) {
- printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE);
- return -EBUSY;
- }
-
- gpio_direction_output(GPIO_SE, 0);
-
- err = snd_ad73311_configure();
- if (err < 0)
- return -EFAULT;
-
- return 0;
-}
-
-#define BF5XX_AD7311_DAI_FMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
- {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ad73311",
- .dai_fmt = BF5XX_AD7311_DAI_FMT,
- },
- {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ad73311",
- .dai_fmt = BF5XX_AD7311_DAI_FMT,
- },
-};
-
-static struct snd_soc_card bf5xx_ad73311 = {
- .name = "bfin-ad73311",
- .owner = THIS_MODULE,
- .probe = bf5xx_probe,
- .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .num_links = 1,
-};
-
-static struct platform_device *bf5xx_ad73311_snd_device;
-
-static int __init bf5xx_ad73311_init(void)
-{
- int ret;
-
- pr_debug("%s enter\n", __func__);
- bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf5xx_ad73311_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311);
- ret = platform_device_add(bf5xx_ad73311_snd_device);
-
- if (ret)
- platform_device_put(bf5xx_ad73311_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ad73311_exit(void)
-{
- pr_debug("%s enter\n", __func__);
- platform_device_unregister(bf5xx_ad73311_snd_device);
-}
-
-module_init(bf5xx_ad73311_init);
-module_exit(bf5xx_ad73311_exit);
-
-/* Module information */
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
deleted file mode 100644
index 6cba211da32e..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-i2s-pcm.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: DMA driver for i2s codec
- *
- * 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/init.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/gfp.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-
-#include "bf5xx-sport.h"
-#include "bf5xx-i2s-pcm.h"
-
-static void bf5xx_dma_irq(void *data)
-{
- struct snd_pcm_substream *pcm = data;
- snd_pcm_period_elapsed(pcm);
-}
-
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .period_bytes_min = 32,
- .period_bytes_max = 0x10000,
- .periods_min = 1,
- .periods_max = PAGE_SIZE/32,
- .buffer_bytes_max = 0x20000, /* 128 kbytes */
- .fifo_size = 16,
-};
-
-static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- unsigned int buffer_size = params_buffer_bytes(params);
- struct bf5xx_i2s_pcm_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode)
- buffer_size = buffer_size / params_channels(params) * 8;
-
- return snd_pcm_lib_malloc_pages(substream, buffer_size);
-}
-
-static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
-
- return 0;
-}
-
-static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- int period_bytes = frames_to_bytes(runtime, runtime->period_size);
- struct bf5xx_i2s_pcm_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode)
- period_bytes = period_bytes / runtime->channels * 8;
-
- pr_debug("%s enter\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_tx_dma(sport, runtime->dma_area,
- runtime->periods, period_bytes);
- } else {
- sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_rx_dma(sport, runtime->dma_area,
- runtime->periods, period_bytes);
- }
-
- return 0;
-}
-
-static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- int ret = 0;
-
- pr_debug("%s enter\n", __func__);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_tx_start(sport);
- else
- sport_rx_start(sport);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_tx_stop(sport);
- else
- sport_rx_stop(sport);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- unsigned int diff;
- snd_pcm_uframes_t frames;
- struct bf5xx_i2s_pcm_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- pr_debug("%s enter\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- diff = sport_curr_offset_tx(sport);
- } else {
- diff = sport_curr_offset_rx(sport);
- }
-
- /*
- * TX at least can report one frame beyond the end of the
- * buffer if we hit the wraparound case - clamp to within the
- * buffer as the ALSA APIs require.
- */
- if (diff == snd_pcm_lib_buffer_bytes(substream))
- diff = 0;
-
- frames = bytes_to_frames(substream->runtime, diff);
- if (dma_data->tdm_mode)
- frames = frames * runtime->channels / 8;
-
- return frames;
-}
-
-static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- struct bf5xx_i2s_pcm_data *dma_data;
- int ret;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- pr_debug("%s enter\n", __func__);
-
- snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
- if (dma_data->tdm_mode)
- runtime->hw.buffer_bytes_max /= 4;
- else
- runtime->hw.info |= SNDRV_PCM_INFO_MMAP;
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- if (sport_handle != NULL) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
- runtime->private_data = sport_handle;
- } else {
- pr_err("sport_handle is NULL\n");
- return -1;
- }
- return 0;
-
- out:
- return ret;
-}
-
-static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- size_t size = vma->vm_end - vma->vm_start;
- vma->vm_start = (unsigned long)runtime->dma_area;
- vma->vm_end = vma->vm_start + size;
- vma->vm_flags |= VM_SHARED;
-
- return 0 ;
-}
-
-static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int sample_size = runtime->sample_bits / 8;
- struct bf5xx_i2s_pcm_data *dma_data;
- unsigned int i;
- void *src, *dst;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = buf;
- dst = runtime->dma_area;
- dst += pos * sample_size * 8;
-
- while (count--) {
- for (i = 0; i < runtime->channels; i++) {
- memcpy(dst + dma_data->map[i] *
- sample_size, src, sample_size);
- src += sample_size;
- }
- dst += 8 * sample_size;
- }
- } else {
- src = runtime->dma_area;
- src += pos * sample_size * 8;
- dst = buf;
-
- while (count--) {
- for (i = 0; i < runtime->channels; i++) {
- memcpy(dst, src + dma_data->map[i] *
- sample_size, sample_size);
- dst += sample_size;
- }
- src += 8 * sample_size;
- }
- }
- } else {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = buf;
- dst = runtime->dma_area;
- dst += frames_to_bytes(runtime, pos);
- } else {
- src = runtime->dma_area;
- src += frames_to_bytes(runtime, pos);
- dst = buf;
- }
-
- memcpy(dst, src, frames_to_bytes(runtime, count));
- }
-
- return 0;
-}
-
-static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int sample_size = runtime->sample_bits / 8;
- void *buf = runtime->dma_area;
- struct bf5xx_i2s_pcm_data *dma_data;
- unsigned int offset, samples;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (dma_data->tdm_mode) {
- offset = pos * 8 * sample_size;
- samples = count * 8;
- } else {
- offset = frames_to_bytes(runtime, pos);
- samples = count * runtime->channels;
- }
-
- snd_pcm_format_set_silence(runtime->format, buf + offset, samples);
-
- return 0;
-}
-
-static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
- .open = bf5xx_pcm_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = bf5xx_pcm_hw_params,
- .hw_free = bf5xx_pcm_hw_free,
- .prepare = bf5xx_pcm_prepare,
- .trigger = bf5xx_pcm_trigger,
- .pointer = bf5xx_pcm_pointer,
- .mmap = bf5xx_pcm_mmap,
- .copy = bf5xx_pcm_copy,
- .silence = bf5xx_pcm_silence,
-};
-
-static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
- int ret;
-
- pr_debug("%s enter\n", __func__);
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
- SNDRV_DMA_TYPE_DEV, card->dev, size, size);
-}
-
-static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
- .ops = &bf5xx_pcm_i2s_ops,
- .pcm_new = bf5xx_pcm_i2s_new,
-};
-
-static int bfin_i2s_soc_platform_probe(struct platform_device *pdev)
-{
- return devm_snd_soc_register_platform(&pdev->dev,
- &bf5xx_i2s_soc_platform);
-}
-
-static struct platform_driver bfin_i2s_pcm_driver = {
- .driver = {
- .name = "bfin-i2s-pcm-audio",
- },
-
- .probe = bfin_i2s_soc_platform_probe,
-};
-
-module_platform_driver(bfin_i2s_pcm_driver);
-
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h
deleted file mode 100644
index 1f0435249f88..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * This program is free software; you can 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 _BF5XX_TDM_PCM_H
-#define _BF5XX_TDM_PCM_H
-
-#define BFIN_TDM_DAI_MAX_SLOTS 8
-
-struct bf5xx_i2s_pcm_data {
- unsigned int map[BFIN_TDM_DAI_MAX_SLOTS];
- bool tdm_mode;
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
deleted file mode 100644
index b69aeef6418e..000000000000
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-i2s.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: Blackfin I2S CPU DAI driver
- *
- * 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/init.h>
-#include <linux/module.h>
-#include <linux/device.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 <asm/irq.h>
-#include <asm/portmux.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-
-#include "bf5xx-sport.h"
-#include "bf5xx-i2s-pcm.h"
-
-struct bf5xx_i2s_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- int configured;
-
- unsigned int slots;
- unsigned int tx_mask;
- unsigned int rx_mask;
-
- struct bf5xx_i2s_pcm_data tx_dma_data;
- struct bf5xx_i2s_pcm_data rx_dma_data;
-};
-
-static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- int ret = 0;
-
- /* interface format:support I2S,slave mode */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- bf5xx_i2s->tcr1 |= TFSR | TCKFE;
- bf5xx_i2s->rcr1 |= RFSR | RCKFE;
- bf5xx_i2s->tcr2 |= TSFSE;
- bf5xx_i2s->rcr2 |= RSFSE;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- bf5xx_i2s->tcr1 |= TFSR;
- bf5xx_i2s->rcr1 |= RFSR;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- ret = -EINVAL;
- break;
- default:
- dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n",
- __func__);
- ret = -EINVAL;
- break;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- ret = -EINVAL;
- break;
- default:
- dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n",
- __func__);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- int ret = 0;
-
- bf5xx_i2s->tcr2 &= ~0x1f;
- bf5xx_i2s->rcr2 &= ~0x1f;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- bf5xx_i2s->tcr2 |= 7;
- bf5xx_i2s->rcr2 |= 7;
- sport_handle->wdsize = 1;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- bf5xx_i2s->tcr2 |= 15;
- bf5xx_i2s->rcr2 |= 15;
- sport_handle->wdsize = 2;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- bf5xx_i2s->tcr2 |= 23;
- bf5xx_i2s->rcr2 |= 23;
- sport_handle->wdsize = 3;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_i2s->tcr2 |= 31;
- bf5xx_i2s->rcr2 |= 31;
- sport_handle->wdsize = 4;
- break;
- }
-
- if (!bf5xx_i2s->configured) {
- /*
- * TX and RX are not independent,they are enabled at the
- * same time, even if only one side is running. So, we
- * need to configure both of them at the time when the first
- * stream is opened.
- *
- * CPU DAI:slave mode.
- */
- bf5xx_i2s->configured = 1;
- ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
- bf5xx_i2s->rcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
- bf5xx_i2s->tcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
- }
-
- return 0;
-}
-
-static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
-
- dev_dbg(dai->dev, "%s enter\n", __func__);
- /* No active stream, SPORT is allowed to be configured again. */
- if (!dai->active)
- bf5xx_i2s->configured = 0;
-}
-
-static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_slot,
- unsigned int rx_num, unsigned int *rx_slot)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- unsigned int tx_mapped = 0, rx_mapped = 0;
- unsigned int slot;
- int i;
-
- if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
- (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
- return -EINVAL;
-
- for (i = 0; i < tx_num; i++) {
- slot = tx_slot[i];
- if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
- (!(tx_mapped & (1 << slot)))) {
- bf5xx_i2s->tx_dma_data.map[i] = slot;
- tx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
- for (i = 0; i < rx_num; i++) {
- slot = rx_slot[i];
- if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
- (!(rx_mapped & (1 << slot)))) {
- bf5xx_i2s->rx_dma_data.map[i] = slot;
- rx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int rx_mask, int slots, int width)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
-
- if (slots % 8 != 0 || slots > 8)
- return -EINVAL;
-
- if (width != 32)
- return -EINVAL;
-
- bf5xx_i2s->slots = slots;
- bf5xx_i2s->tx_mask = tx_mask;
- bf5xx_i2s->rx_mask = rx_mask;
-
- bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
- bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
-
- return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
-}
-
-#ifdef CONFIG_PM
-static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
-
- dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
-
- if (dai->capture_active)
- sport_rx_stop(sport_handle);
- if (dai->playback_active)
- sport_tx_stop(sport_handle);
- return 0;
-}
-
-static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- int ret;
-
- dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
-
- ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
- bf5xx_i2s->rcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
- bf5xx_i2s->tcr2, 0, 0);
- if (ret) {
- dev_err(dai->dev, "SPORT is busy!\n");
- return -EBUSY;
- }
-
- return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
- bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
-}
-
-#else
-#define bf5xx_i2s_suspend NULL
-#define bf5xx_i2s_resume NULL
-#endif
-
-static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- unsigned int i;
-
- for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
- bf5xx_i2s->tx_dma_data.map[i] = i;
- bf5xx_i2s->rx_dma_data.map[i] = i;
- }
-
- dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
- dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
-
- return 0;
-}
-
-#define BF5XX_I2S_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_96000)
-
-#define BF5XX_I2S_FORMATS \
- (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
-
-static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
- .shutdown = bf5xx_i2s_shutdown,
- .hw_params = bf5xx_i2s_hw_params,
- .set_fmt = bf5xx_i2s_set_dai_fmt,
- .set_tdm_slot = bf5xx_i2s_set_tdm_slot,
- .set_channel_map = bf5xx_i2s_set_channel_map,
-};
-
-static struct snd_soc_dai_driver bf5xx_i2s_dai = {
- .probe = bf5xx_i2s_dai_probe,
- .suspend = bf5xx_i2s_suspend,
- .resume = bf5xx_i2s_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = BF5XX_I2S_RATES,
- .formats = BF5XX_I2S_FORMATS,},
- .capture = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = BF5XX_I2S_RATES,
- .formats = BF5XX_I2S_FORMATS,},
- .ops = &bf5xx_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver bf5xx_i2s_component = {
- .name = "bf5xx-i2s",
-};
-
-static int bf5xx_i2s_probe(struct platform_device *pdev)
-{
- struct sport_device *sport_handle;
- int ret;
-
- /* configure SPORT for I2S */
- sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
- sizeof(struct bf5xx_i2s_port));
- if (!sport_handle)
- return -ENODEV;
-
- /* register with the ASoC layers */
- ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
- &bf5xx_i2s_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
- sport_done(sport_handle);
- return ret;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_remove(struct platform_device *pdev)
-{
- struct sport_device *sport_handle = platform_get_drvdata(pdev);
-
- dev_dbg(&pdev->dev, "%s enter\n", __func__);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_done(sport_handle);
-
- return 0;
-}
-
-static struct platform_driver bfin_i2s_driver = {
- .probe = bf5xx_i2s_probe,
- .remove = bf5xx_i2s_remove,
- .driver = {
- .name = "bfin-i2s",
- },
-};
-
-module_platform_driver(bfin_i2s_driver);
-
-/* Module information */
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
deleted file mode 100644
index 9dfa1241ea66..000000000000
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * File: bf5xx_sport.c
- * Based on:
- * Author: Roy Huang <roy.huang@analog.com>
- *
- * Created: Tue Sep 21 10:52:42 CEST 2004
- * Description:
- * Blackfin SPORT Driver
- *
- * Copyright 2004-2007 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/kernel.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/gpio.h>
-#include <linux/bug.h>
-#include <linux/module.h>
-#include <asm/portmux.h>
-#include <asm/dma.h>
-#include <asm/blackfin.h>
-#include <asm/cacheflush.h>
-
-#include "bf5xx-sport.h"
-/* delay between frame sync pulse and first data bit in multichannel mode */
-#define FRAME_DELAY (1<<12)
-
-/* note: multichannel is in units of 8 channels,
- * tdm_count is # channels NOT / 8 ! */
-int sport_set_multichannel(struct sport_device *sport,
- int tdm_count, u32 tx_mask, u32 rx_mask, int packed)
-{
- pr_debug("%s tdm_count=%d tx_mask:0x%08x rx_mask:0x%08x packed=%d\n",
- __func__, tdm_count, tx_mask, rx_mask, packed);
-
- if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
- return -EBUSY;
-
- if (tdm_count & 0x7)
- return -EINVAL;
-
- if (tdm_count > 32)
- return -EINVAL; /* Only support less than 32 channels now */
-
- if (tdm_count) {
- sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12;
- sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
- (packed ? (MCDTXPE|MCDRXPE) : 0);
-
- sport->regs->mtcs0 = tx_mask;
- sport->regs->mrcs0 = rx_mask;
- sport->regs->mtcs1 = 0;
- sport->regs->mrcs1 = 0;
- sport->regs->mtcs2 = 0;
- sport->regs->mrcs2 = 0;
- sport->regs->mtcs3 = 0;
- sport->regs->mrcs3 = 0;
- } else {
- sport->regs->mcmc1 = 0;
- sport->regs->mcmc2 = 0;
-
- sport->regs->mtcs0 = 0;
- sport->regs->mrcs0 = 0;
- }
-
- sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0;
- sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0;
-
- SSYNC();
-
- return 0;
-}
-EXPORT_SYMBOL(sport_set_multichannel);
-
-int sport_config_rx(struct sport_device *sport, unsigned int rcr1,
- unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv)
-{
- if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
- return -EBUSY;
-
- sport->regs->rcr1 = rcr1;
- sport->regs->rcr2 = rcr2;
- sport->regs->rclkdiv = clkdiv;
- sport->regs->rfsdiv = fsdiv;
-
- SSYNC();
-
- return 0;
-}
-EXPORT_SYMBOL(sport_config_rx);
-
-int sport_config_tx(struct sport_device *sport, unsigned int tcr1,
- unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv)
-{
- if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
- return -EBUSY;
-
- sport->regs->tcr1 = tcr1;
- sport->regs->tcr2 = tcr2;
- sport->regs->tclkdiv = clkdiv;
- sport->regs->tfsdiv = fsdiv;
-
- SSYNC();
-
- return 0;
-}
-EXPORT_SYMBOL(sport_config_tx);
-
-static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
- size_t fragsize, unsigned int cfg,
- unsigned int x_count, unsigned int ycount, size_t wdsize)
-{
-
- int i;
-
- for (i = 0; i < fragcount; ++i) {
- desc[i].next_desc_addr = &(desc[i + 1]);
- desc[i].start_addr = (unsigned long)buf + i*fragsize;
- desc[i].cfg = cfg;
- desc[i].x_count = x_count;
- desc[i].x_modify = wdsize;
- desc[i].y_count = ycount;
- desc[i].y_modify = wdsize;
- }
-
- /* make circular */
- desc[fragcount-1].next_desc_addr = desc;
-
- pr_debug("setup desc: desc0=%p, next0=%p, desc1=%p,"
- "next1=%p\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
- desc, desc[0].next_desc_addr,
- desc+1, desc[1].next_desc_addr,
- desc[0].x_count, desc[0].y_count,
- desc[0].start_addr, desc[0].cfg);
-}
-
-static int sport_start(struct sport_device *sport)
-{
- enable_dma(sport->dma_rx_chan);
- enable_dma(sport->dma_tx_chan);
- sport->regs->rcr1 |= RSPEN;
- sport->regs->tcr1 |= TSPEN;
- SSYNC();
-
- return 0;
-}
-
-static int sport_stop(struct sport_device *sport)
-{
- sport->regs->tcr1 &= ~TSPEN;
- sport->regs->rcr1 &= ~RSPEN;
- SSYNC();
-
- disable_dma(sport->dma_rx_chan);
- disable_dma(sport->dma_tx_chan);
- return 0;
-}
-
-static inline int sport_hook_rx_dummy(struct sport_device *sport)
-{
- struct dmasg *desc, temp_desc;
- unsigned long flags;
-
- if (WARN_ON(!sport->dummy_rx_desc) ||
- WARN_ON(sport->curr_rx_desc == sport->dummy_rx_desc))
- return -EINVAL;
-
- /* Maybe the dummy buffer descriptor ring is damaged */
- sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc + 1;
-
- local_irq_save(flags);
- desc = get_dma_next_desc_ptr(sport->dma_rx_chan);
- /* Copy the descriptor which will be damaged to backup */
- temp_desc = *desc;
- desc->x_count = sport->dummy_count / 2;
- desc->y_count = 0;
- desc->next_desc_addr = sport->dummy_rx_desc;
- local_irq_restore(flags);
- /* Waiting for dummy buffer descriptor is already hooked*/
- while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
- sizeof(struct dmasg)) != sport->dummy_rx_desc)
- continue;
- sport->curr_rx_desc = sport->dummy_rx_desc;
- /* Restore the damaged descriptor */
- *desc = temp_desc;
-
- return 0;
-}
-
-static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
-{
- if (dummy) {
- sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc;
- sport->curr_rx_desc = sport->dummy_rx_desc;
- } else
- sport->curr_rx_desc = sport->dma_rx_desc;
-
- set_dma_next_desc_addr(sport->dma_rx_chan, sport->curr_rx_desc);
- set_dma_x_count(sport->dma_rx_chan, 0);
- set_dma_x_modify(sport->dma_rx_chan, 0);
- set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \
- WDSIZE_32 | WNR));
- set_dma_curr_addr(sport->dma_rx_chan, sport->curr_rx_desc->start_addr);
- SSYNC();
-
- return 0;
-}
-
-static inline int sport_tx_dma_start(struct sport_device *sport, int dummy)
-{
- if (dummy) {
- sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc;
- sport->curr_tx_desc = sport->dummy_tx_desc;
- } else
- sport->curr_tx_desc = sport->dma_tx_desc;
-
- set_dma_next_desc_addr(sport->dma_tx_chan, sport->curr_tx_desc);
- set_dma_x_count(sport->dma_tx_chan, 0);
- set_dma_x_modify(sport->dma_tx_chan, 0);
- set_dma_config(sport->dma_tx_chan,
- (DMAFLOW_LARGE | NDSIZE_9 | WDSIZE_32));
- set_dma_curr_addr(sport->dma_tx_chan, sport->curr_tx_desc->start_addr);
- SSYNC();
-
- return 0;
-}
-
-int sport_rx_start(struct sport_device *sport)
-{
- unsigned long flags;
- pr_debug("%s enter\n", __func__);
- if (sport->rx_run)
- return -EBUSY;
- if (sport->tx_run) {
- /* tx is running, rx is not running */
- if (WARN_ON(!sport->dma_rx_desc) ||
- WARN_ON(sport->curr_rx_desc != sport->dummy_rx_desc))
- return -EINVAL;
- local_irq_save(flags);
- while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
- sizeof(struct dmasg)) != sport->dummy_rx_desc)
- continue;
- sport->dummy_rx_desc->next_desc_addr = sport->dma_rx_desc;
- local_irq_restore(flags);
- sport->curr_rx_desc = sport->dma_rx_desc;
- } else {
- sport_tx_dma_start(sport, 1);
- sport_rx_dma_start(sport, 0);
- sport_start(sport);
- }
-
- sport->rx_run = 1;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_rx_start);
-
-int sport_rx_stop(struct sport_device *sport)
-{
- pr_debug("%s enter\n", __func__);
-
- if (!sport->rx_run)
- return 0;
- if (sport->tx_run) {
- /* TX dma is still running, hook the dummy buffer */
- sport_hook_rx_dummy(sport);
- } else {
- /* Both rx and tx dma will be stopped */
- sport_stop(sport);
- sport->curr_rx_desc = NULL;
- sport->curr_tx_desc = NULL;
- }
-
- sport->rx_run = 0;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_rx_stop);
-
-static inline int sport_hook_tx_dummy(struct sport_device *sport)
-{
- struct dmasg *desc, temp_desc;
- unsigned long flags;
-
- if (WARN_ON(!sport->dummy_tx_desc) ||
- WARN_ON(sport->curr_tx_desc == sport->dummy_tx_desc))
- return -EINVAL;
-
- sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc + 1;
-
- /* Shorten the time on last normal descriptor */
- local_irq_save(flags);
- desc = get_dma_next_desc_ptr(sport->dma_tx_chan);
- /* Store the descriptor which will be damaged */
- temp_desc = *desc;
- desc->x_count = sport->dummy_count / 2;
- desc->y_count = 0;
- desc->next_desc_addr = sport->dummy_tx_desc;
- local_irq_restore(flags);
- /* Waiting for dummy buffer descriptor is already hooked*/
- while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \
- sizeof(struct dmasg)) != sport->dummy_tx_desc)
- continue;
- sport->curr_tx_desc = sport->dummy_tx_desc;
- /* Restore the damaged descriptor */
- *desc = temp_desc;
-
- return 0;
-}
-
-int sport_tx_start(struct sport_device *sport)
-{
- unsigned long flags;
- pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__,
- sport->tx_run, sport->rx_run);
- if (sport->tx_run)
- return -EBUSY;
- if (sport->rx_run) {
- if (WARN_ON(!sport->dma_tx_desc) ||
- WARN_ON(sport->curr_tx_desc != sport->dummy_tx_desc))
- return -EINVAL;
- /* Hook the normal buffer descriptor */
- local_irq_save(flags);
- while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) -
- sizeof(struct dmasg)) != sport->dummy_tx_desc)
- continue;
- sport->dummy_tx_desc->next_desc_addr = sport->dma_tx_desc;
- local_irq_restore(flags);
- sport->curr_tx_desc = sport->dma_tx_desc;
- } else {
-
- sport_tx_dma_start(sport, 0);
- /* Let rx dma run the dummy buffer */
- sport_rx_dma_start(sport, 1);
- sport_start(sport);
- }
- sport->tx_run = 1;
- return 0;
-}
-EXPORT_SYMBOL(sport_tx_start);
-
-int sport_tx_stop(struct sport_device *sport)
-{
- if (!sport->tx_run)
- return 0;
- if (sport->rx_run) {
- /* RX is still running, hook the dummy buffer */
- sport_hook_tx_dummy(sport);
- } else {
- /* Both rx and tx dma stopped */
- sport_stop(sport);
- sport->curr_rx_desc = NULL;
- sport->curr_tx_desc = NULL;
- }
-
- sport->tx_run = 0;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_tx_stop);
-
-static inline int compute_wdsize(size_t wdsize)
-{
- switch (wdsize) {
- case 1:
- return WDSIZE_8;
- case 2:
- return WDSIZE_16;
- case 4:
- default:
- return WDSIZE_32;
- }
-}
-
-int sport_config_rx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize)
-{
- unsigned int x_count;
- unsigned int y_count;
- unsigned int cfg;
- dma_addr_t addr;
-
- pr_debug("%s buf:%p, frag:%d, fragsize:0x%lx\n", __func__, \
- buf, fragcount, fragsize);
-
- x_count = fragsize / sport->wdsize;
- y_count = 0;
-
- /* for fragments larger than 64k words we use 2d dma,
- * denote fragecount as two numbers' mutliply and both of them
- * are less than 64k.*/
- if (x_count >= 0x10000) {
- int i, count = x_count;
-
- for (i = 16; i > 0; i--) {
- x_count = 1 << i;
- if ((count & (x_count - 1)) == 0) {
- y_count = count >> i;
- if (y_count < 0x10000)
- break;
- }
- }
- if (i == 0)
- return -EINVAL;
- }
- pr_debug("%s(x_count:0x%x, y_count:0x%x)\n", __func__,
- x_count, y_count);
-
- if (sport->dma_rx_desc)
- dma_free_coherent(NULL, sport->rx_desc_bytes,
- sport->dma_rx_desc, 0);
-
- /* Allocate a new descritor ring as current one. */
- sport->dma_rx_desc = dma_alloc_coherent(NULL, \
- fragcount * sizeof(struct dmasg), &addr, 0);
- sport->rx_desc_bytes = fragcount * sizeof(struct dmasg);
-
- if (!sport->dma_rx_desc) {
- pr_err("Failed to allocate memory for rx desc\n");
- return -ENOMEM;
- }
-
- sport->rx_buf = buf;
- sport->rx_fragsize = fragsize;
- sport->rx_frags = fragcount;
-
- cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | WNR | \
- (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
-
- if (y_count != 0)
- cfg |= DMA2D;
-
- setup_desc(sport->dma_rx_desc, buf, fragcount, fragsize,
- cfg|DMAEN, x_count, y_count, sport->wdsize);
-
- return 0;
-}
-EXPORT_SYMBOL(sport_config_rx_dma);
-
-int sport_config_tx_dma(struct sport_device *sport, void *buf, \
- int fragcount, size_t fragsize)
-{
- unsigned int x_count;
- unsigned int y_count;
- unsigned int cfg;
- dma_addr_t addr;
-
- pr_debug("%s buf:%p, fragcount:%d, fragsize:0x%lx\n",
- __func__, buf, fragcount, fragsize);
-
- x_count = fragsize/sport->wdsize;
- y_count = 0;
-
- /* for fragments larger than 64k words we use 2d dma,
- * denote fragecount as two numbers' mutliply and both of them
- * are less than 64k.*/
- if (x_count >= 0x10000) {
- int i, count = x_count;
-
- for (i = 16; i > 0; i--) {
- x_count = 1 << i;
- if ((count & (x_count - 1)) == 0) {
- y_count = count >> i;
- if (y_count < 0x10000)
- break;
- }
- }
- if (i == 0)
- return -EINVAL;
- }
- pr_debug("%s x_count:0x%x, y_count:0x%x\n", __func__,
- x_count, y_count);
-
-
- if (sport->dma_tx_desc) {
- dma_free_coherent(NULL, sport->tx_desc_bytes, \
- sport->dma_tx_desc, 0);
- }
-
- sport->dma_tx_desc = dma_alloc_coherent(NULL, \
- fragcount * sizeof(struct dmasg), &addr, 0);
- sport->tx_desc_bytes = fragcount * sizeof(struct dmasg);
- if (!sport->dma_tx_desc) {
- pr_err("Failed to allocate memory for tx desc\n");
- return -ENOMEM;
- }
-
- sport->tx_buf = buf;
- sport->tx_fragsize = fragsize;
- sport->tx_frags = fragcount;
- cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | \
- (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
-
- if (y_count != 0)
- cfg |= DMA2D;
-
- setup_desc(sport->dma_tx_desc, buf, fragcount, fragsize,
- cfg|DMAEN, x_count, y_count, sport->wdsize);
-
- return 0;
-}
-EXPORT_SYMBOL(sport_config_tx_dma);
-
-/* setup dummy dma descriptor ring, which don't generate interrupts,
- * the x_modify is set to 0 */
-static int sport_config_rx_dummy(struct sport_device *sport)
-{
- struct dmasg *desc;
- unsigned config;
-
- pr_debug("%s entered\n", __func__);
- if (L1_DATA_A_LENGTH)
- desc = l1_data_sram_zalloc(2 * sizeof(*desc));
- else {
- dma_addr_t addr;
- desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
- memset(desc, 0, 2 * sizeof(*desc));
- }
- if (desc == NULL) {
- pr_err("Failed to allocate memory for dummy rx desc\n");
- return -ENOMEM;
- }
- sport->dummy_rx_desc = desc;
- desc->start_addr = (unsigned long)sport->dummy_buf;
- config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize)
- | WNR | DMAEN;
- desc->cfg = config;
- desc->x_count = sport->dummy_count/sport->wdsize;
- desc->x_modify = sport->wdsize;
- desc->y_count = 0;
- desc->y_modify = 0;
- memcpy(desc+1, desc, sizeof(*desc));
- desc->next_desc_addr = desc + 1;
- desc[1].next_desc_addr = desc;
- return 0;
-}
-
-static int sport_config_tx_dummy(struct sport_device *sport)
-{
- struct dmasg *desc;
- unsigned int config;
-
- pr_debug("%s entered\n", __func__);
-
- if (L1_DATA_A_LENGTH)
- desc = l1_data_sram_zalloc(2 * sizeof(*desc));
- else {
- dma_addr_t addr;
- desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
- memset(desc, 0, 2 * sizeof(*desc));
- }
- if (!desc) {
- pr_err("Failed to allocate memory for dummy tx desc\n");
- return -ENOMEM;
- }
- sport->dummy_tx_desc = desc;
- desc->start_addr = (unsigned long)sport->dummy_buf + \
- sport->dummy_count;
- config = DMAFLOW_LARGE | NDSIZE_9 |
- compute_wdsize(sport->wdsize) | DMAEN;
- desc->cfg = config;
- desc->x_count = sport->dummy_count/sport->wdsize;
- desc->x_modify = sport->wdsize;
- desc->y_count = 0;
- desc->y_modify = 0;
- memcpy(desc+1, desc, sizeof(*desc));
- desc->next_desc_addr = desc + 1;
- desc[1].next_desc_addr = desc;
- return 0;
-}
-
-unsigned long sport_curr_offset_rx(struct sport_device *sport)
-{
- unsigned long curr = get_dma_curr_addr(sport->dma_rx_chan);
-
- return (unsigned char *)curr - sport->rx_buf;
-}
-EXPORT_SYMBOL(sport_curr_offset_rx);
-
-unsigned long sport_curr_offset_tx(struct sport_device *sport)
-{
- unsigned long curr = get_dma_curr_addr(sport->dma_tx_chan);
-
- return (unsigned char *)curr - sport->tx_buf;
-}
-EXPORT_SYMBOL(sport_curr_offset_tx);
-
-void sport_incfrag(struct sport_device *sport, int *frag, int tx)
-{
- ++(*frag);
- if (tx == 1 && *frag == sport->tx_frags)
- *frag = 0;
-
- if (tx == 0 && *frag == sport->rx_frags)
- *frag = 0;
-}
-EXPORT_SYMBOL(sport_incfrag);
-
-void sport_decfrag(struct sport_device *sport, int *frag, int tx)
-{
- --(*frag);
- if (tx == 1 && *frag == 0)
- *frag = sport->tx_frags;
-
- if (tx == 0 && *frag == 0)
- *frag = sport->rx_frags;
-}
-EXPORT_SYMBOL(sport_decfrag);
-
-static int sport_check_status(struct sport_device *sport,
- unsigned int *sport_stat,
- unsigned int *rx_stat,
- unsigned int *tx_stat)
-{
- int status = 0;
-
- if (sport_stat) {
- SSYNC();
- status = sport->regs->stat;
- if (status & (TOVF|TUVF|ROVF|RUVF))
- sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
- SSYNC();
- *sport_stat = status;
- }
-
- if (rx_stat) {
- SSYNC();
- status = get_dma_curr_irqstat(sport->dma_rx_chan);
- if (status & (DMA_DONE|DMA_ERR))
- clear_dma_irqstat(sport->dma_rx_chan);
- SSYNC();
- *rx_stat = status;
- }
-
- if (tx_stat) {
- SSYNC();
- status = get_dma_curr_irqstat(sport->dma_tx_chan);
- if (status & (DMA_DONE|DMA_ERR))
- clear_dma_irqstat(sport->dma_tx_chan);
- SSYNC();
- *tx_stat = status;
- }
-
- return 0;
-}
-
-int sport_dump_stat(struct sport_device *sport, char *buf, size_t len)
-{
- int ret;
-
- ret = snprintf(buf, len,
- "sts: 0x%04x\n"
- "rx dma %d sts: 0x%04x tx dma %d sts: 0x%04x\n",
- sport->regs->stat,
- sport->dma_rx_chan,
- get_dma_curr_irqstat(sport->dma_rx_chan),
- sport->dma_tx_chan,
- get_dma_curr_irqstat(sport->dma_tx_chan));
- buf += ret;
- len -= ret;
-
- ret += snprintf(buf, len,
- "curr_rx_desc:0x%p, curr_tx_desc:0x%p\n"
- "dma_rx_desc:0x%p, dma_tx_desc:0x%p\n"
- "dummy_rx_desc:0x%p, dummy_tx_desc:0x%p\n",
- sport->curr_rx_desc, sport->curr_tx_desc,
- sport->dma_rx_desc, sport->dma_tx_desc,
- sport->dummy_rx_desc, sport->dummy_tx_desc);
-
- return ret;
-}
-
-static irqreturn_t rx_handler(int irq, void *dev_id)
-{
- unsigned int rx_stat;
- struct sport_device *sport = dev_id;
-
- pr_debug("%s enter\n", __func__);
- sport_check_status(sport, NULL, &rx_stat, NULL);
- if (!(rx_stat & DMA_DONE))
- pr_err("rx dma is already stopped\n");
-
- if (sport->rx_callback) {
- sport->rx_callback(sport->rx_data);
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
-}
-
-static irqreturn_t tx_handler(int irq, void *dev_id)
-{
- unsigned int tx_stat;
- struct sport_device *sport = dev_id;
- pr_debug("%s enter\n", __func__);
- sport_check_status(sport, NULL, NULL, &tx_stat);
- if (!(tx_stat & DMA_DONE)) {
- pr_err("tx dma is already stopped\n");
- return IRQ_HANDLED;
- }
- if (sport->tx_callback) {
- sport->tx_callback(sport->tx_data);
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
-}
-
-static irqreturn_t err_handler(int irq, void *dev_id)
-{
- unsigned int status = 0;
- struct sport_device *sport = dev_id;
-
- pr_debug("%s\n", __func__);
- if (sport_check_status(sport, &status, NULL, NULL)) {
- pr_err("error checking status ??");
- return IRQ_NONE;
- }
-
- if (status & (TOVF|TUVF|ROVF|RUVF)) {
- pr_info("sport status error:%s%s%s%s\n",
- status & TOVF ? " TOVF" : "",
- status & TUVF ? " TUVF" : "",
- status & ROVF ? " ROVF" : "",
- status & RUVF ? " RUVF" : "");
- if (status & TOVF || status & TUVF) {
- disable_dma(sport->dma_tx_chan);
- if (sport->tx_run)
- sport_tx_dma_start(sport, 0);
- else
- sport_tx_dma_start(sport, 1);
- enable_dma(sport->dma_tx_chan);
- } else {
- disable_dma(sport->dma_rx_chan);
- if (sport->rx_run)
- sport_rx_dma_start(sport, 0);
- else
- sport_rx_dma_start(sport, 1);
- enable_dma(sport->dma_rx_chan);
- }
- }
- status = sport->regs->stat;
- if (status & (TOVF|TUVF|ROVF|RUVF))
- sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
- SSYNC();
-
- if (sport->err_callback)
- sport->err_callback(sport->err_data);
-
- return IRQ_HANDLED;
-}
-
-int sport_set_rx_callback(struct sport_device *sport,
- void (*rx_callback)(void *), void *rx_data)
-{
- if (WARN_ON(!rx_callback))
- return -EINVAL;
- sport->rx_callback = rx_callback;
- sport->rx_data = rx_data;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_set_rx_callback);
-
-int sport_set_tx_callback(struct sport_device *sport,
- void (*tx_callback)(void *), void *tx_data)
-{
- if (WARN_ON(!tx_callback))
- return -EINVAL;
- sport->tx_callback = tx_callback;
- sport->tx_data = tx_data;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_set_tx_callback);
-
-int sport_set_err_callback(struct sport_device *sport,
- void (*err_callback)(void *), void *err_data)
-{
- if (WARN_ON(!err_callback))
- return -EINVAL;
- sport->err_callback = err_callback;
- sport->err_data = err_data;
-
- return 0;
-}
-EXPORT_SYMBOL(sport_set_err_callback);
-
-static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
-{
- /* Extract settings from platform data */
- struct device *dev = &pdev->dev;
- struct bfin_snd_platform_data *pdata = dev->platform_data;
- struct resource *res;
-
- param->num = pdev->id;
-
- if (!pdata) {
- dev_err(dev, "no platform_data\n");
- return -ENODEV;
- }
- param->pin_req = pdata->pin_req;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no MEM resource\n");
- return -ENODEV;
- }
- param->regs = (struct sport_register *)res->start;
-
- /* first RX, then TX */
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(dev, "no rx DMA resource\n");
- return -ENODEV;
- }
- param->dma_rx_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(dev, "no tx DMA resource\n");
- return -ENODEV;
- }
- param->dma_tx_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "no irq resource\n");
- return -ENODEV;
- }
- param->err_irq = res->start;
-
- return 0;
-}
-
-struct sport_device *sport_init(struct platform_device *pdev,
- unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
-{
- struct device *dev = &pdev->dev;
- struct sport_param param;
- struct sport_device *sport;
- int ret;
-
- dev_dbg(dev, "%s enter\n", __func__);
-
- param.wdsize = wdsize;
- param.dummy_count = dummy_count;
- if (WARN_ON(param.wdsize == 0 || param.dummy_count == 0))
- return NULL;
-
- ret = sport_config_pdev(pdev, &param);
- if (ret)
- return NULL;
-
- if (peripheral_request_list(param.pin_req, "soc-audio")) {
- dev_err(dev, "requesting Peripherals failed\n");
- return NULL;
- }
-
- sport = kzalloc(sizeof(*sport), GFP_KERNEL);
- if (!sport) {
- dev_err(dev, "failed to allocate for sport device\n");
- goto __init_err0;
- }
-
- sport->num = param.num;
- sport->dma_rx_chan = param.dma_rx_chan;
- sport->dma_tx_chan = param.dma_tx_chan;
- sport->err_irq = param.err_irq;
- sport->regs = param.regs;
- sport->pin_req = param.pin_req;
-
- if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
- dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
- goto __init_err1;
- }
- if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
- dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
- goto __init_err2;
- }
-
- if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
- dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
- goto __init_err2;
- }
-
- if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
- dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
- goto __init_err3;
- }
-
- if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
- sport) < 0) {
- dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
- goto __init_err3;
- }
-
- dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
- sport->dma_rx_chan, sport->dma_tx_chan,
- sport->err_irq, sport->regs);
-
- sport->wdsize = param.wdsize;
- sport->dummy_count = param.dummy_count;
-
- sport->private_data = kzalloc(priv_size, GFP_KERNEL);
- if (!sport->private_data) {
- dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
- goto __init_err4;
- }
-
- if (L1_DATA_A_LENGTH)
- sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
- else
- sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
- if (sport->dummy_buf == NULL) {
- dev_err(dev, "failed to allocate dummy buffer\n");
- goto __error1;
- }
-
- ret = sport_config_rx_dummy(sport);
- if (ret) {
- dev_err(dev, "failed to config rx dummy ring\n");
- goto __error2;
- }
- ret = sport_config_tx_dummy(sport);
- if (ret) {
- dev_err(dev, "failed to config tx dummy ring\n");
- goto __error3;
- }
-
- platform_set_drvdata(pdev, sport);
-
- return sport;
-__error3:
- if (L1_DATA_A_LENGTH)
- l1_data_sram_free(sport->dummy_rx_desc);
- else
- dma_free_coherent(NULL, 2*sizeof(struct dmasg),
- sport->dummy_rx_desc, 0);
-__error2:
- if (L1_DATA_A_LENGTH)
- l1_data_sram_free(sport->dummy_buf);
- else
- kfree(sport->dummy_buf);
-__error1:
- kfree(sport->private_data);
-__init_err4:
- free_irq(sport->err_irq, sport);
-__init_err3:
- free_dma(sport->dma_tx_chan);
-__init_err2:
- free_dma(sport->dma_rx_chan);
-__init_err1:
- kfree(sport);
-__init_err0:
- peripheral_free_list(param.pin_req);
- return NULL;
-}
-EXPORT_SYMBOL(sport_init);
-
-void sport_done(struct sport_device *sport)
-{
- if (sport == NULL)
- return;
-
- sport_stop(sport);
- if (sport->dma_rx_desc)
- dma_free_coherent(NULL, sport->rx_desc_bytes,
- sport->dma_rx_desc, 0);
- if (sport->dma_tx_desc)
- dma_free_coherent(NULL, sport->tx_desc_bytes,
- sport->dma_tx_desc, 0);
-
-#if L1_DATA_A_LENGTH != 0
- l1_data_sram_free(sport->dummy_rx_desc);
- l1_data_sram_free(sport->dummy_tx_desc);
- l1_data_sram_free(sport->dummy_buf);
-#else
- dma_free_coherent(NULL, 2*sizeof(struct dmasg),
- sport->dummy_rx_desc, 0);
- dma_free_coherent(NULL, 2*sizeof(struct dmasg),
- sport->dummy_tx_desc, 0);
- kfree(sport->dummy_buf);
-#endif
- free_dma(sport->dma_rx_chan);
- free_dma(sport->dma_tx_chan);
- free_irq(sport->err_irq, sport);
-
- kfree(sport->private_data);
- peripheral_free_list(sport->pin_req);
- kfree(sport);
-}
-EXPORT_SYMBOL(sport_done);
-
-/*
-* It is only used to send several bytes when dma is not enabled
- * sport controller is configured but not enabled.
- * Multichannel cannot works with pio mode */
-/* Used by ac97 to write and read codec register */
-int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
- u8 *in_data, int len)
-{
- unsigned short dma_config;
- unsigned short status;
- unsigned long flags;
- unsigned long wait = 0;
-
- pr_debug("%s enter, out_data:%p, in_data:%p len:%d\n", \
- __func__, out_data, in_data, len);
- pr_debug("tcr1:0x%04x, tcr2:0x%04x, tclkdiv:0x%04x, tfsdiv:0x%04x\n"
- "mcmc1:0x%04x, mcmc2:0x%04x\n",
- sport->regs->tcr1, sport->regs->tcr2,
- sport->regs->tclkdiv, sport->regs->tfsdiv,
- sport->regs->mcmc1, sport->regs->mcmc2);
- flush_dcache_range((unsigned)out_data, (unsigned)(out_data + len));
-
- /* Enable tx dma */
- dma_config = (RESTART | WDSIZE_16 | DI_EN);
- set_dma_start_addr(sport->dma_tx_chan, (unsigned long)out_data);
- set_dma_x_count(sport->dma_tx_chan, len/2);
- set_dma_x_modify(sport->dma_tx_chan, 2);
- set_dma_config(sport->dma_tx_chan, dma_config);
- enable_dma(sport->dma_tx_chan);
-
- if (in_data != NULL) {
- invalidate_dcache_range((unsigned)in_data, \
- (unsigned)(in_data + len));
- /* Enable rx dma */
- dma_config = (RESTART | WDSIZE_16 | WNR | DI_EN);
- set_dma_start_addr(sport->dma_rx_chan, (unsigned long)in_data);
- set_dma_x_count(sport->dma_rx_chan, len/2);
- set_dma_x_modify(sport->dma_rx_chan, 2);
- set_dma_config(sport->dma_rx_chan, dma_config);
- enable_dma(sport->dma_rx_chan);
- }
-
- local_irq_save(flags);
- sport->regs->tcr1 |= TSPEN;
- sport->regs->rcr1 |= RSPEN;
- SSYNC();
-
- status = get_dma_curr_irqstat(sport->dma_tx_chan);
- while (status & DMA_RUN) {
- udelay(1);
- status = get_dma_curr_irqstat(sport->dma_tx_chan);
- pr_debug("DMA status:0x%04x\n", status);
- if (wait++ > 100)
- goto __over;
- }
- status = sport->regs->stat;
- wait = 0;
-
- while (!(status & TXHRE)) {
- pr_debug("sport status:0x%04x\n", status);
- udelay(1);
- status = *(unsigned short *)&sport->regs->stat;
- if (wait++ > 1000)
- goto __over;
- }
- /* Wait for the last byte sent out */
- udelay(20);
- pr_debug("sport status:0x%04x\n", status);
-
-__over:
- sport->regs->tcr1 &= ~TSPEN;
- sport->regs->rcr1 &= ~RSPEN;
- SSYNC();
- disable_dma(sport->dma_tx_chan);
- /* Clear the status */
- clear_dma_irqstat(sport->dma_tx_chan);
- if (in_data != NULL) {
- disable_dma(sport->dma_rx_chan);
- clear_dma_irqstat(sport->dma_rx_chan);
- }
- SSYNC();
- local_irq_restore(flags);
-
- return 0;
-}
-EXPORT_SYMBOL(sport_send_and_recv);
-
-MODULE_AUTHOR("Roy Huang");
-MODULE_DESCRIPTION("SPORT driver for ADI Blackfin");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
deleted file mode 100644
index 9fc2192feb3b..000000000000
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * File: bf5xx_sport.h
- * Based on:
- * Author: Roy Huang <roy.huang@analog.com>
- *
- * Created:
- * Description:
- *
- * Copyright 2004-2007 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 __BF5XX_SPORT_H__
-#define __BF5XX_SPORT_H__
-
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <asm/dma.h>
-#include <asm/bfin_sport.h>
-
-#define DESC_ELEMENT_COUNT 9
-
-struct sport_device {
- int num;
- int dma_rx_chan;
- int dma_tx_chan;
- int err_irq;
- const unsigned short *pin_req;
- struct sport_register *regs;
-
- unsigned char *rx_buf;
- unsigned char *tx_buf;
- unsigned int rx_fragsize;
- unsigned int tx_fragsize;
- unsigned int rx_frags;
- unsigned int tx_frags;
- unsigned int wdsize;
-
- /* for dummy dma transfer */
- void *dummy_buf;
- unsigned int dummy_count;
-
- /* DMA descriptor ring head of current audio stream*/
- struct dmasg *dma_rx_desc;
- struct dmasg *dma_tx_desc;
- unsigned int rx_desc_bytes;
- unsigned int tx_desc_bytes;
-
- unsigned int rx_run:1; /* rx is running */
- unsigned int tx_run:1; /* tx is running */
-
- struct dmasg *dummy_rx_desc;
- struct dmasg *dummy_tx_desc;
-
- struct dmasg *curr_rx_desc;
- struct dmasg *curr_tx_desc;
-
- int rx_curr_frag;
- int tx_curr_frag;
-
- unsigned int rcr1;
- unsigned int rcr2;
- int rx_tdm_count;
-
- unsigned int tcr1;
- unsigned int tcr2;
- int tx_tdm_count;
-
- void (*rx_callback)(void *data);
- void *rx_data;
- void (*tx_callback)(void *data);
- void *tx_data;
- void (*err_callback)(void *data);
- void *err_data;
- unsigned char *tx_dma_buf;
- unsigned char *rx_dma_buf;
-#ifdef CONFIG_SND_BF5XX_MMAP_SUPPORT
- dma_addr_t tx_dma_phy;
- dma_addr_t rx_dma_phy;
- int tx_pos;/*pcm sample count*/
- int rx_pos;
- unsigned int tx_buffer_size;
- unsigned int rx_buffer_size;
- int tx_delay_pos;
- int once;
-#endif
- void *private_data;
-};
-
-struct sport_param {
- int num;
- int dma_rx_chan;
- int dma_tx_chan;
- int err_irq;
- const unsigned short *pin_req;
- struct sport_register *regs;
- unsigned int wdsize;
- unsigned int dummy_count;
- void *private_data;
-};
-
-struct sport_device *sport_init(struct platform_device *pdev,
- unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
-
-void sport_done(struct sport_device *sport);
-
-/* first use these ...*/
-
-/* note: multichannel is in units of 8 channels, tdm_count is number of channels
- * NOT / 8 ! all channels are enabled by default */
-int sport_set_multichannel(struct sport_device *sport, int tdm_count,
- u32 tx_mask, u32 rx_mask, int packed);
-
-int sport_config_rx(struct sport_device *sport,
- unsigned int rcr1, unsigned int rcr2,
- unsigned int clkdiv, unsigned int fsdiv);
-
-int sport_config_tx(struct sport_device *sport,
- unsigned int tcr1, unsigned int tcr2,
- unsigned int clkdiv, unsigned int fsdiv);
-
-/* ... then these: */
-
-/* buffer size (in bytes) == fragcount * fragsize_bytes */
-
-/* this is not a very general api, it sets the dma to 2d autobuffer mode */
-
-int sport_config_rx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize_bytes);
-
-int sport_config_tx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize_bytes);
-
-int sport_tx_start(struct sport_device *sport);
-int sport_tx_stop(struct sport_device *sport);
-int sport_rx_start(struct sport_device *sport);
-int sport_rx_stop(struct sport_device *sport);
-
-/* for use in interrupt handler */
-unsigned long sport_curr_offset_rx(struct sport_device *sport);
-unsigned long sport_curr_offset_tx(struct sport_device *sport);
-
-void sport_incfrag(struct sport_device *sport, int *frag, int tx);
-void sport_decfrag(struct sport_device *sport, int *frag, int tx);
-
-int sport_set_rx_callback(struct sport_device *sport,
- void (*rx_callback)(void *), void *rx_data);
-int sport_set_tx_callback(struct sport_device *sport,
- void (*tx_callback)(void *), void *tx_data);
-int sport_set_err_callback(struct sport_device *sport,
- void (*err_callback)(void *), void *err_data);
-
-int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
- u8 *in_data, int len);
-#endif /* BF53X_SPORT_H */
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
deleted file mode 100644
index 9c19ccc936e2..000000000000
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-ssm2602.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: board 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/moduleparam.h>
-#include <linux/device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <linux/gpio.h>
-#include "../codecs/ssm2602.h"
-#include "bf5xx-sport.h"
-
-static struct snd_soc_card bf5xx_ssm2602;
-
-static int bf5xx_ssm2602_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- /*
- * If you are using a crystal source which frequency is not 12MHz
- * then modify the below case statement with frequency of the crystal.
- *
- * If you are using the SPORT to generate clocking then this is
- * where to do it.
- */
- return snd_soc_dai_set_sysclk(rtd->codec_dai, SSM2602_SYSCLK, 12000000,
- SND_SOC_CLOCK_IN);
-}
-
-/* CODEC is master for BCLK and LRC in this configuration. */
-#define BF5XX_SSM2602_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
- {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ssm2602.0-001b",
- .init = bf5xx_ssm2602_dai_init,
- .dai_fmt = BF5XX_SSM2602_DAIFMT,
- },
- {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "ssm2602.0-001b",
- .init = bf5xx_ssm2602_dai_init,
- .dai_fmt = BF5XX_SSM2602_DAIFMT,
- },
-};
-
-static struct snd_soc_card bf5xx_ssm2602 = {
- .name = "bfin-ssm2602",
- .owner = THIS_MODULE,
- .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .num_links = 1,
-};
-
-static struct platform_device *bf5xx_ssm2602_snd_device;
-
-static int __init bf5xx_ssm2602_init(void)
-{
- int ret;
-
- pr_debug("%s enter\n", __func__);
- bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf5xx_ssm2602_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(bf5xx_ssm2602_snd_device, &bf5xx_ssm2602);
- ret = platform_device_add(bf5xx_ssm2602_snd_device);
-
- if (ret)
- platform_device_put(bf5xx_ssm2602_snd_device);
-
- return ret;
-}
-
-static void __exit bf5xx_ssm2602_exit(void)
-{
- pr_debug("%s enter\n", __func__);
- platform_device_unregister(bf5xx_ssm2602_snd_device);
-}
-
-module_init(bf5xx_ssm2602_init);
-module_exit(bf5xx_ssm2602_exit);
-
-/* Module information */
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION("ALSA SoC SSM2602 BF527-EZKIT");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf6xx-i2s.c b/sound/soc/blackfin/bf6xx-i2s.c
deleted file mode 100644
index bd3b4d464145..000000000000
--- a/sound/soc/blackfin/bf6xx-i2s.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * bf6xx-i2s.c - Analog Devices BF6XX i2s interface driver
- *
- * Copyright (c) 2012 Analog Devices 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.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dai.h>
-
-#include "bf6xx-sport.h"
-
-struct sport_params param;
-
-static int bfin_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(cpu_dai);
- struct device *dev = &sport->pdev->dev;
- int ret = 0;
-
- param.spctl &= ~(SPORT_CTL_OPMODE | SPORT_CTL_CKRE | SPORT_CTL_FSR
- | SPORT_CTL_LFS | SPORT_CTL_LAFS);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_CKRE
- | SPORT_CTL_LFS;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- param.spctl |= SPORT_CTL_FSR;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_LFS
- | SPORT_CTL_LAFS;
- break;
- default:
- dev_err(dev, "%s: Unknown DAI format type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- param.spctl &= ~(SPORT_CTL_ICLK | SPORT_CTL_IFS);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- ret = -EINVAL;
- break;
- default:
- dev_err(dev, "%s: Unknown DAI master type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int bfin_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- struct device *dev = &sport->pdev->dev;
- int ret = 0;
-
- param.spctl &= ~SPORT_CTL_SLEN;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- param.spctl |= 0x70;
- sport->wdsize = 1;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- param.spctl |= 0xf0;
- sport->wdsize = 2;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- param.spctl |= 0x170;
- sport->wdsize = 3;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- param.spctl |= 0x1f0;
- sport->wdsize = 4;
- break;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = sport_set_tx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT tx is busy!\n");
- return ret;
- }
- } else {
- ret = sport_set_rx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT rx is busy!\n");
- return ret;
- }
- }
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- if (dai->capture_active)
- sport_rx_stop(sport);
- if (dai->playback_active)
- sport_tx_stop(sport);
- return 0;
-}
-
-static int bfin_i2s_resume(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- struct device *dev = &sport->pdev->dev;
- int ret;
-
- ret = sport_set_tx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT tx is busy!\n");
- return ret;
- }
- ret = sport_set_rx_params(sport, &param);
- if (ret) {
- dev_err(dev, "SPORT rx is busy!\n");
- return ret;
- }
-
- return 0;
-}
-
-#else
-#define bfin_i2s_suspend NULL
-#define bfin_i2s_resume NULL
-#endif
-
-#define BFIN_I2S_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_96000)
-
-#define BFIN_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-
-static struct snd_soc_dai_ops bfin_i2s_dai_ops = {
- .hw_params = bfin_i2s_hw_params,
- .set_fmt = bfin_i2s_set_dai_fmt,
-};
-
-static struct snd_soc_dai_driver bfin_i2s_dai = {
- .suspend = bfin_i2s_suspend,
- .resume = bfin_i2s_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = BFIN_I2S_RATES,
- .formats = BFIN_I2S_FORMATS,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = BFIN_I2S_RATES,
- .formats = BFIN_I2S_FORMATS,
- },
- .ops = &bfin_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver bfin_i2s_component = {
- .name = "bfin-i2s",
-};
-
-static int bfin_i2s_probe(struct platform_device *pdev)
-{
- struct sport_device *sport;
- struct device *dev = &pdev->dev;
- int ret;
-
- sport = sport_create(pdev);
- if (!sport)
- return -ENODEV;
-
- /* register with the ASoC layers */
- ret = snd_soc_register_component(dev, &bfin_i2s_component,
- &bfin_i2s_dai, 1);
- if (ret) {
- dev_err(dev, "Failed to register DAI: %d\n", ret);
- sport_delete(sport);
- return ret;
- }
- platform_set_drvdata(pdev, sport);
-
- return 0;
-}
-
-static int bfin_i2s_remove(struct platform_device *pdev)
-{
- struct sport_device *sport = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_delete(sport);
-
- return 0;
-}
-
-static struct platform_driver bfin_i2s_driver = {
- .probe = bfin_i2s_probe,
- .remove = bfin_i2s_remove,
- .driver = {
- .name = "bfin-i2s",
- },
-};
-
-module_platform_driver(bfin_i2s_driver);
-
-MODULE_DESCRIPTION("Analog Devices BF6XX i2s interface driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf6xx-sport.c b/sound/soc/blackfin/bf6xx-sport.c
deleted file mode 100644
index dfb744381c42..000000000000
--- a/sound/soc/blackfin/bf6xx-sport.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * bf6xx_sport.c Analog Devices BF6XX SPORT driver
- *
- * Copyright (c) 2012 Analog Devices 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.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <asm/blackfin.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include "bf6xx-sport.h"
-
-int sport_set_tx_params(struct sport_device *sport,
- struct sport_params *params)
-{
- if (sport->tx_regs->spctl & SPORT_CTL_SPENPRI)
- return -EBUSY;
- sport->tx_regs->spctl = params->spctl | SPORT_CTL_SPTRAN;
- sport->tx_regs->div = params->div;
- SSYNC();
- return 0;
-}
-EXPORT_SYMBOL(sport_set_tx_params);
-
-int sport_set_rx_params(struct sport_device *sport,
- struct sport_params *params)
-{
- if (sport->rx_regs->spctl & SPORT_CTL_SPENPRI)
- return -EBUSY;
- sport->rx_regs->spctl = params->spctl & ~SPORT_CTL_SPTRAN;
- sport->rx_regs->div = params->div;
- SSYNC();
- return 0;
-}
-EXPORT_SYMBOL(sport_set_rx_params);
-
-static int compute_wdsize(size_t wdsize)
-{
- switch (wdsize) {
- case 1:
- return WDSIZE_8 | PSIZE_8;
- case 2:
- return WDSIZE_16 | PSIZE_16;
- default:
- return WDSIZE_32 | PSIZE_32;
- }
-}
-
-void sport_tx_start(struct sport_device *sport)
-{
- set_dma_next_desc_addr(sport->tx_dma_chan, sport->tx_desc);
- set_dma_config(sport->tx_dma_chan, DMAFLOW_LIST | DI_EN
- | compute_wdsize(sport->wdsize) | NDSIZE_6);
- enable_dma(sport->tx_dma_chan);
- sport->tx_regs->spctl |= SPORT_CTL_SPENPRI;
- SSYNC();
-}
-EXPORT_SYMBOL(sport_tx_start);
-
-void sport_rx_start(struct sport_device *sport)
-{
- set_dma_next_desc_addr(sport->rx_dma_chan, sport->rx_desc);
- set_dma_config(sport->rx_dma_chan, DMAFLOW_LIST | DI_EN | WNR
- | compute_wdsize(sport->wdsize) | NDSIZE_6);
- enable_dma(sport->rx_dma_chan);
- sport->rx_regs->spctl |= SPORT_CTL_SPENPRI;
- SSYNC();
-}
-EXPORT_SYMBOL(sport_rx_start);
-
-void sport_tx_stop(struct sport_device *sport)
-{
- sport->tx_regs->spctl &= ~SPORT_CTL_SPENPRI;
- SSYNC();
- disable_dma(sport->tx_dma_chan);
-}
-EXPORT_SYMBOL(sport_tx_stop);
-
-void sport_rx_stop(struct sport_device *sport)
-{
- sport->rx_regs->spctl &= ~SPORT_CTL_SPENPRI;
- SSYNC();
- disable_dma(sport->rx_dma_chan);
-}
-EXPORT_SYMBOL(sport_rx_stop);
-
-void sport_set_tx_callback(struct sport_device *sport,
- void (*tx_callback)(void *), void *tx_data)
-{
- sport->tx_callback = tx_callback;
- sport->tx_data = tx_data;
-}
-EXPORT_SYMBOL(sport_set_tx_callback);
-
-void sport_set_rx_callback(struct sport_device *sport,
- void (*rx_callback)(void *), void *rx_data)
-{
- sport->rx_callback = rx_callback;
- sport->rx_data = rx_data;
-}
-EXPORT_SYMBOL(sport_set_rx_callback);
-
-static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
- size_t fragsize, unsigned int cfg,
- unsigned int count, size_t wdsize)
-{
-
- int i;
-
- for (i = 0; i < fragcount; ++i) {
- desc[i].next_desc_addr = &(desc[i + 1]);
- desc[i].start_addr = (unsigned long)buf + i*fragsize;
- desc[i].cfg = cfg;
- desc[i].x_count = count;
- desc[i].x_modify = wdsize;
- desc[i].y_count = 0;
- desc[i].y_modify = 0;
- }
-
- /* make circular */
- desc[fragcount-1].next_desc_addr = desc;
-}
-
-int sport_config_tx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize)
-{
- unsigned int count;
- unsigned int cfg;
- dma_addr_t addr;
-
- count = fragsize/sport->wdsize;
-
- if (sport->tx_desc)
- dma_free_coherent(NULL, sport->tx_desc_size,
- sport->tx_desc, 0);
-
- sport->tx_desc = dma_alloc_coherent(NULL,
- fragcount * sizeof(struct dmasg), &addr, 0);
- sport->tx_desc_size = fragcount * sizeof(struct dmasg);
- if (!sport->tx_desc)
- return -ENOMEM;
-
- sport->tx_buf = buf;
- sport->tx_fragsize = fragsize;
- sport->tx_frags = fragcount;
- cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize) | NDSIZE_6;
-
- setup_desc(sport->tx_desc, buf, fragcount, fragsize,
- cfg|DMAEN, count, sport->wdsize);
-
- return 0;
-}
-EXPORT_SYMBOL(sport_config_tx_dma);
-
-int sport_config_rx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize)
-{
- unsigned int count;
- unsigned int cfg;
- dma_addr_t addr;
-
- count = fragsize/sport->wdsize;
-
- if (sport->rx_desc)
- dma_free_coherent(NULL, sport->rx_desc_size,
- sport->rx_desc, 0);
-
- sport->rx_desc = dma_alloc_coherent(NULL,
- fragcount * sizeof(struct dmasg), &addr, 0);
- sport->rx_desc_size = fragcount * sizeof(struct dmasg);
- if (!sport->rx_desc)
- return -ENOMEM;
-
- sport->rx_buf = buf;
- sport->rx_fragsize = fragsize;
- sport->rx_frags = fragcount;
- cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize)
- | WNR | NDSIZE_6;
-
- setup_desc(sport->rx_desc, buf, fragcount, fragsize,
- cfg|DMAEN, count, sport->wdsize);
-
- return 0;
-}
-EXPORT_SYMBOL(sport_config_rx_dma);
-
-unsigned long sport_curr_offset_tx(struct sport_device *sport)
-{
- unsigned long curr = get_dma_curr_addr(sport->tx_dma_chan);
-
- return (unsigned char *)curr - sport->tx_buf;
-}
-EXPORT_SYMBOL(sport_curr_offset_tx);
-
-unsigned long sport_curr_offset_rx(struct sport_device *sport)
-{
- unsigned long curr = get_dma_curr_addr(sport->rx_dma_chan);
-
- return (unsigned char *)curr - sport->rx_buf;
-}
-EXPORT_SYMBOL(sport_curr_offset_rx);
-
-static irqreturn_t sport_tx_irq(int irq, void *dev_id)
-{
- struct sport_device *sport = dev_id;
- static unsigned long status;
-
- status = get_dma_curr_irqstat(sport->tx_dma_chan);
- if (status & (DMA_DONE|DMA_ERR)) {
- clear_dma_irqstat(sport->tx_dma_chan);
- SSYNC();
- }
- if (sport->tx_callback)
- sport->tx_callback(sport->tx_data);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sport_rx_irq(int irq, void *dev_id)
-{
- struct sport_device *sport = dev_id;
- unsigned long status;
-
- status = get_dma_curr_irqstat(sport->rx_dma_chan);
- if (status & (DMA_DONE|DMA_ERR)) {
- clear_dma_irqstat(sport->rx_dma_chan);
- SSYNC();
- }
- if (sport->rx_callback)
- sport->rx_callback(sport->rx_data);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sport_err_irq(int irq, void *dev_id)
-{
- struct sport_device *sport = dev_id;
- struct device *dev = &sport->pdev->dev;
-
- if (sport->tx_regs->spctl & SPORT_CTL_DERRPRI)
- dev_err(dev, "sport error: TUVF\n");
- if (sport->rx_regs->spctl & SPORT_CTL_DERRPRI)
- dev_err(dev, "sport error: ROVF\n");
-
- return IRQ_HANDLED;
-}
-
-static int sport_get_resource(struct sport_device *sport)
-{
- struct platform_device *pdev = sport->pdev;
- struct device *dev = &pdev->dev;
- struct bfin_snd_platform_data *pdata = dev->platform_data;
- struct resource *res;
-
- if (!pdata) {
- dev_err(dev, "No platform data\n");
- return -ENODEV;
- }
- sport->pin_req = pdata->pin_req;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "No tx MEM resource\n");
- return -ENODEV;
- }
- sport->tx_regs = (struct sport_register *)res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- dev_err(dev, "No rx MEM resource\n");
- return -ENODEV;
- }
- sport->rx_regs = (struct sport_register *)res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(dev, "No tx DMA resource\n");
- return -ENODEV;
- }
- sport->tx_dma_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(dev, "No rx DMA resource\n");
- return -ENODEV;
- }
- sport->rx_dma_chan = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "No tx error irq resource\n");
- return -ENODEV;
- }
- sport->tx_err_irq = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!res) {
- dev_err(dev, "No rx error irq resource\n");
- return -ENODEV;
- }
- sport->rx_err_irq = res->start;
-
- return 0;
-}
-
-static int sport_request_resource(struct sport_device *sport)
-{
- struct device *dev = &sport->pdev->dev;
- int ret;
-
- ret = peripheral_request_list(sport->pin_req, "soc-audio");
- if (ret) {
- dev_err(dev, "Unable to request sport pin\n");
- return ret;
- }
-
- ret = request_dma(sport->tx_dma_chan, "SPORT TX Data");
- if (ret) {
- dev_err(dev, "Unable to allocate DMA channel for sport tx\n");
- goto err_tx_dma;
- }
- set_dma_callback(sport->tx_dma_chan, sport_tx_irq, sport);
-
- ret = request_dma(sport->rx_dma_chan, "SPORT RX Data");
- if (ret) {
- dev_err(dev, "Unable to allocate DMA channel for sport rx\n");
- goto err_rx_dma;
- }
- set_dma_callback(sport->rx_dma_chan, sport_rx_irq, sport);
-
- ret = request_irq(sport->tx_err_irq, sport_err_irq,
- 0, "SPORT TX ERROR", sport);
- if (ret) {
- dev_err(dev, "Unable to allocate tx error IRQ for sport\n");
- goto err_tx_irq;
- }
-
- ret = request_irq(sport->rx_err_irq, sport_err_irq,
- 0, "SPORT RX ERROR", sport);
- if (ret) {
- dev_err(dev, "Unable to allocate rx error IRQ for sport\n");
- goto err_rx_irq;
- }
-
- return 0;
-err_rx_irq:
- free_irq(sport->tx_err_irq, sport);
-err_tx_irq:
- free_dma(sport->rx_dma_chan);
-err_rx_dma:
- free_dma(sport->tx_dma_chan);
-err_tx_dma:
- peripheral_free_list(sport->pin_req);
- return ret;
-}
-
-static void sport_free_resource(struct sport_device *sport)
-{
- free_irq(sport->rx_err_irq, sport);
- free_irq(sport->tx_err_irq, sport);
- free_dma(sport->rx_dma_chan);
- free_dma(sport->tx_dma_chan);
- peripheral_free_list(sport->pin_req);
-}
-
-struct sport_device *sport_create(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct sport_device *sport;
- int ret;
-
- sport = kzalloc(sizeof(*sport), GFP_KERNEL);
- if (!sport) {
- dev_err(dev, "Unable to allocate memory for sport device\n");
- return NULL;
- }
- sport->pdev = pdev;
-
- ret = sport_get_resource(sport);
- if (ret) {
- kfree(sport);
- return NULL;
- }
-
- ret = sport_request_resource(sport);
- if (ret) {
- kfree(sport);
- return NULL;
- }
-
- dev_dbg(dev, "SPORT create success\n");
- return sport;
-}
-EXPORT_SYMBOL(sport_create);
-
-void sport_delete(struct sport_device *sport)
-{
- if (sport->tx_desc)
- dma_free_coherent(NULL, sport->tx_desc_size,
- sport->tx_desc, 0);
- if (sport->rx_desc)
- dma_free_coherent(NULL, sport->rx_desc_size,
- sport->rx_desc, 0);
- sport_free_resource(sport);
- kfree(sport);
-}
-EXPORT_SYMBOL(sport_delete);
-
-MODULE_DESCRIPTION("Analog Devices BF6XX SPORT driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf6xx-sport.h b/sound/soc/blackfin/bf6xx-sport.h
deleted file mode 100644
index 307d193cfcef..000000000000
--- a/sound/soc/blackfin/bf6xx-sport.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * bf6xx_sport - Analog Devices BF6XX SPORT driver
- *
- * Copyright (c) 2012 Analog Devices 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.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _BF6XX_SPORT_H_
-#define _BF6XX_SPORT_H_
-
-#include <linux/platform_device.h>
-#include <asm/bfin_sport3.h>
-
-struct sport_device {
- struct platform_device *pdev;
- const unsigned short *pin_req;
- struct sport_register *tx_regs;
- struct sport_register *rx_regs;
- int tx_dma_chan;
- int rx_dma_chan;
- int tx_err_irq;
- int rx_err_irq;
-
- void (*tx_callback)(void *data);
- void *tx_data;
- void (*rx_callback)(void *data);
- void *rx_data;
-
- struct dmasg *tx_desc;
- struct dmasg *rx_desc;
- unsigned int tx_desc_size;
- unsigned int rx_desc_size;
- unsigned char *tx_buf;
- unsigned char *rx_buf;
- unsigned int tx_fragsize;
- unsigned int rx_fragsize;
- unsigned int tx_frags;
- unsigned int rx_frags;
- unsigned int wdsize;
-};
-
-struct sport_params {
- u32 spctl;
- u32 div;
-};
-
-struct sport_device *sport_create(struct platform_device *pdev);
-void sport_delete(struct sport_device *sport);
-int sport_set_tx_params(struct sport_device *sport,
- struct sport_params *params);
-int sport_set_rx_params(struct sport_device *sport,
- struct sport_params *params);
-void sport_tx_start(struct sport_device *sport);
-void sport_rx_start(struct sport_device *sport);
-void sport_tx_stop(struct sport_device *sport);
-void sport_rx_stop(struct sport_device *sport);
-void sport_set_tx_callback(struct sport_device *sport,
- void (*tx_callback)(void *), void *tx_data);
-void sport_set_rx_callback(struct sport_device *sport,
- void (*rx_callback)(void *), void *rx_data);
-int sport_config_tx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize);
-int sport_config_rx_dma(struct sport_device *sport, void *buf,
- int fragcount, size_t fragsize);
-unsigned long sport_curr_offset_tx(struct sport_device *sport);
-unsigned long sport_curr_offset_rx(struct sport_device *sport);
-
-
-
-#endif
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
deleted file mode 100644
index 72ac78988426..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1373.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1373 on Analog Devices bfin
- * evaluation boards.
- *
- * 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/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau1373.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Line In1", NULL),
- SND_SOC_DAPM_LINE("Line In2", NULL),
- SND_SOC_DAPM_LINE("Line In3", NULL),
- SND_SOC_DAPM_LINE("Line In4", NULL),
-
- SND_SOC_DAPM_LINE("Line Out1", NULL),
- SND_SOC_DAPM_LINE("Line Out2", NULL),
- SND_SOC_DAPM_LINE("Stereo Out", NULL),
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_HP("Earpiece", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = {
- { "AIN1L", NULL, "Line In1" },
- { "AIN1R", NULL, "Line In1" },
- { "AIN2L", NULL, "Line In2" },
- { "AIN2R", NULL, "Line In2" },
- { "AIN3L", NULL, "Line In3" },
- { "AIN3R", NULL, "Line In3" },
- { "AIN4L", NULL, "Line In4" },
- { "AIN4R", NULL, "Line In4" },
-
- /* MICBIAS can be connected via a jumper to the line-in jack, since w
- don't know which one is going to be used, just power both. */
- { "Line In1", NULL, "MICBIAS1" },
- { "Line In2", NULL, "MICBIAS1" },
- { "Line In3", NULL, "MICBIAS1" },
- { "Line In4", NULL, "MICBIAS1" },
- { "Line In1", NULL, "MICBIAS2" },
- { "Line In2", NULL, "MICBIAS2" },
- { "Line In3", NULL, "MICBIAS2" },
- { "Line In4", NULL, "MICBIAS2" },
-
- { "Line Out1", NULL, "LOUT1L" },
- { "Line Out1", NULL, "LOUT1R" },
- { "Line Out2", NULL, "LOUT2L" },
- { "Line Out2", NULL, "LOUT2R" },
- { "Headphone", NULL, "HPL" },
- { "Headphone", NULL, "HPR" },
- { "Earpiece", NULL, "EP" },
- { "Speaker", NULL, "SPKL" },
- { "Stereo Out", NULL, "SPKR" },
-};
-
-static int bfin_eval_adau1373_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
- int pll_rate;
-
- switch (params_rate(params)) {
- case 48000:
- case 8000:
- case 12000:
- case 16000:
- case 24000:
- case 32000:
- pll_rate = 48000 * 1024;
- break;
- case 44100:
- case 7350:
- case 11025:
- case 14700:
- case 22050:
- case 29400:
- pll_rate = 44100 * 1024;
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
- ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int pll_rate = 48000 * 1024;
- int ret;
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
- ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-static struct snd_soc_ops bfin_eval_adau1373_ops = {
- .hw_params = bfin_eval_adau1373_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adau1373_dai = {
- .name = "adau1373",
- .stream_name = "adau1373",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau1373-aif1",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1373.0-001a",
- .ops = &bfin_eval_adau1373_ops,
- .init = bfin_eval_adau1373_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card bfin_eval_adau1373 = {
- .name = "bfin-eval-adau1373",
- .owner = THIS_MODULE,
- .dai_link = &bfin_eval_adau1373_dai,
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1373_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets),
- .dapm_routes = bfin_eval_adau1373_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1373_dapm_routes),
-};
-
-static int bfin_eval_adau1373_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bfin_eval_adau1373;
-
- card->dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
-}
-
-static struct platform_driver bfin_eval_adau1373_driver = {
- .driver = {
- .name = "bfin-eval-adau1373",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1373_probe,
-};
-
-module_platform_driver(bfin_eval_adau1373_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1373");
diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c
deleted file mode 100644
index 5c67f72cf9a9..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1701.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1701MINIZ on Analog Devices bfin
- * evaluation boards.
- *
- * 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/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau1701.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = {
- { "Speaker", NULL, "OUT0" },
- { "Speaker", NULL, "OUT1" },
- { "Line Out", NULL, "OUT2" },
- { "Line Out", NULL, "OUT3" },
-
- { "IN0", NULL, "Line In" },
- { "IN1", NULL, "Line In" },
-};
-
-static int bfin_eval_adau1701_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static struct snd_soc_ops bfin_eval_adau1701_ops = {
- .hw_params = bfin_eval_adau1701_hw_params,
-};
-
-#define BFIN_EVAL_ADAU1701_DAI_FMT (SND_SOC_DAIFMT_I2S | \
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = {
- {
- .name = "adau1701",
- .stream_name = "adau1701",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau1701",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1701.0-0034",
- .ops = &bfin_eval_adau1701_ops,
- .dai_fmt = BFIN_EVAL_ADAU1701_DAI_FMT,
- },
- {
- .name = "adau1701",
- .stream_name = "adau1701",
- .cpu_dai_name = "bfin-i2s.1",
- .codec_dai_name = "adau1701",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1701.0-0034",
- .ops = &bfin_eval_adau1701_ops,
- .dai_fmt = BFIN_EVAL_ADAU1701_DAI_FMT,
- },
-};
-
-static struct snd_soc_card bfin_eval_adau1701 = {
- .name = "bfin-eval-adau1701",
- .owner = THIS_MODULE,
- .dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM],
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1701_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets),
- .dapm_routes = bfin_eval_adau1701_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes),
-};
-
-static int bfin_eval_adau1701_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bfin_eval_adau1701;
-
- card->dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
-}
-
-static struct platform_driver bfin_eval_adau1701_driver = {
- .driver = {
- .name = "bfin-eval-adau1701",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1701_probe,
-};
-
-module_platform_driver(bfin_eval_adau1701_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1701");
diff --git a/sound/soc/blackfin/bfin-eval-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c
deleted file mode 100644
index fddfe00c9d69..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1x61.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin
- * evaluation boards.
- *
- * 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/device.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau17x1.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("In 1", NULL),
- SND_SOC_DAPM_LINE("In 2", NULL),
- SND_SOC_DAPM_LINE("In 3-4", NULL),
-
- SND_SOC_DAPM_LINE("Diff Out L", NULL),
- SND_SOC_DAPM_LINE("Diff Out R", NULL),
- SND_SOC_DAPM_LINE("Stereo Out", NULL),
- SND_SOC_DAPM_HP("Capless HP Out", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = {
- { "LAUX", NULL, "In 3-4" },
- { "RAUX", NULL, "In 3-4" },
- { "LINP", NULL, "In 1" },
- { "LINN", NULL, "In 1"},
- { "RINP", NULL, "In 2" },
- { "RINN", NULL, "In 2" },
-
- { "In 1", NULL, "MICBIAS" },
- { "In 2", NULL, "MICBIAS" },
-
- { "Capless HP Out", NULL, "LHP" },
- { "Capless HP Out", NULL, "RHP" },
- { "Diff Out L", NULL, "LOUT" },
- { "Diff Out R", NULL, "ROUT" },
- { "Stereo Out", NULL, "LOUT" },
- { "Stereo Out", NULL, "ROUT" },
-};
-
-static int bfin_eval_adau1x61_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int pll_rate;
- int ret;
-
- switch (params_rate(params)) {
- case 48000:
- case 8000:
- case 12000:
- case 16000:
- case 24000:
- case 32000:
- case 96000:
- pll_rate = 48000 * 1024;
- break;
- case 44100:
- case 7350:
- case 11025:
- case 14700:
- case 22050:
- case 29400:
- case 88200:
- pll_rate = 44100 * 1024;
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
- ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static const struct snd_soc_ops bfin_eval_adau1x61_ops = {
- .hw_params = bfin_eval_adau1x61_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
- .name = "adau1x61",
- .stream_name = "adau1x61",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1761.0-0038",
- .ops = &bfin_eval_adau1x61_ops,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card bfin_eval_adau1x61 = {
- .name = "bfin-eval-adau1x61",
- .owner = THIS_MODULE,
- .driver_name = "eval-adau1x61",
- .dai_link = &bfin_eval_adau1x61_dai,
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1x61_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets),
- .dapm_routes = bfin_eval_adau1x61_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes),
- .fully_routed = true,
-};
-
-static int bfin_eval_adau1x61_probe(struct platform_device *pdev)
-{
- bfin_eval_adau1x61.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x61);
-}
-
-static struct platform_driver bfin_eval_adau1x61_driver = {
- .driver = {
- .name = "bfin-eval-adau1x61",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1x61_probe,
-};
-module_platform_driver(bfin_eval_adau1x61_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1x61");
diff --git a/sound/soc/blackfin/bfin-eval-adau1x81.c b/sound/soc/blackfin/bfin-eval-adau1x81.c
deleted file mode 100644
index 3e01cbe53fc7..000000000000
--- a/sound/soc/blackfin/bfin-eval-adau1x81.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Machine driver for EVAL-ADAU1x81 on Analog Devices bfin
- * evaluation boards.
- *
- * 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/device.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../codecs/adau17x1.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adau1x81_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Stereo In", NULL),
- SND_SOC_DAPM_LINE("Beep", NULL),
-
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_HP("Headphone", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adau1x81_dapm_routes[] = {
- { "BEEP", NULL, "Beep" },
- { "LMIC", NULL, "Stereo In" },
- { "LMIC", NULL, "Stereo In" },
-
- { "Headphone", NULL, "AOUTL" },
- { "Headphone", NULL, "AOUTR" },
- { "Speaker", NULL, "SP" },
-};
-
-static int bfin_eval_adau1x81_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int pll_rate;
- int ret;
-
- switch (params_rate(params)) {
- case 48000:
- case 8000:
- case 12000:
- case 16000:
- case 24000:
- case 32000:
- case 96000:
- pll_rate = 48000 * 1024;
- break;
- case 44100:
- case 7350:
- case 11025:
- case 14700:
- case 22050:
- case 29400:
- case 88200:
- pll_rate = 44100 * 1024;
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
- ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
- SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static const struct snd_soc_ops bfin_eval_adau1x81_ops = {
- .hw_params = bfin_eval_adau1x81_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adau1x81_dai = {
- .name = "adau1x81",
- .stream_name = "adau1x81",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adau-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .codec_name = "adau1781.0-0038",
- .ops = &bfin_eval_adau1x81_ops,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card bfin_eval_adau1x81 = {
- .name = "bfin-eval-adau1x81",
- .driver_name = "eval-adau1x81",
- .dai_link = &bfin_eval_adau1x81_dai,
- .num_links = 1,
-
- .dapm_widgets = bfin_eval_adau1x81_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x81_dapm_widgets),
- .dapm_routes = bfin_eval_adau1x81_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x81_dapm_routes),
- .fully_routed = true,
-};
-
-static int bfin_eval_adau1x81_probe(struct platform_device *pdev)
-{
- bfin_eval_adau1x81.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x81);
-}
-
-static struct platform_driver bfin_eval_adau1x81_driver = {
- .driver = {
- .name = "bfin-eval-adau1x81",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adau1x81_probe,
-};
-module_platform_driver(bfin_eval_adau1x81_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adau1x81 driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-eval-adau1x81");
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
deleted file mode 100644
index 1037477d10b2..000000000000
--- a/sound/soc/blackfin/bfin-eval-adav80x.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Machine driver for EVAL-ADAV801 and EVAL-ADAV803 on Analog Devices bfin
- * evaluation boards.
- *
- * Copyright 2011 Analog Devices Inc.
- * Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include "../codecs/adav80x.h"
-
-static const struct snd_soc_dapm_widget bfin_eval_adav80x_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static const struct snd_soc_dapm_route bfin_eval_adav80x_dapm_routes[] = {
- { "Line Out", NULL, "VOUTL" },
- { "Line Out", NULL, "VOUTR" },
-
- { "VINL", NULL, "Line In" },
- { "VINR", NULL, "Line In" },
-};
-
-static int bfin_eval_adav80x_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_pll(codec_dai, ADAV80X_PLL1, ADAV80X_PLL_SRC_XTAL,
- 27000000, params_rate(params) * 256);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_PLL1,
- params_rate(params) * 256, SND_SOC_CLOCK_IN);
-
- return ret;
-}
-
-static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK1, 0,
- SND_SOC_CLOCK_OUT);
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK2, 0,
- SND_SOC_CLOCK_OUT);
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK3, 0,
- SND_SOC_CLOCK_OUT);
-
- snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_XTAL, 2700000, 0);
-
- return 0;
-}
-
-static struct snd_soc_ops bfin_eval_adav80x_ops = {
- .hw_params = bfin_eval_adav80x_hw_params,
-};
-
-static struct snd_soc_dai_link bfin_eval_adav80x_dais[] = {
- {
- .name = "adav80x",
- .stream_name = "ADAV80x HiFi",
- .cpu_dai_name = "bfin-i2s.0",
- .codec_dai_name = "adav80x-hifi",
- .platform_name = "bfin-i2s-pcm-audio",
- .init = bfin_eval_adav80x_codec_init,
- .ops = &bfin_eval_adav80x_ops,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- },
-};
-
-static struct snd_soc_card bfin_eval_adav80x = {
- .name = "bfin-eval-adav80x",
- .owner = THIS_MODULE,
- .dai_link = bfin_eval_adav80x_dais,
- .num_links = ARRAY_SIZE(bfin_eval_adav80x_dais),
-
- .dapm_widgets = bfin_eval_adav80x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adav80x_dapm_widgets),
- .dapm_routes = bfin_eval_adav80x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(bfin_eval_adav80x_dapm_routes),
-};
-
-enum bfin_eval_adav80x_type {
- BFIN_EVAL_ADAV801,
- BFIN_EVAL_ADAV803,
-};
-
-static int bfin_eval_adav80x_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &bfin_eval_adav80x;
- const char *codec_name;
-
- switch (platform_get_device_id(pdev)->driver_data) {
- case BFIN_EVAL_ADAV801:
- codec_name = "spi0.1";
- break;
- case BFIN_EVAL_ADAV803:
- codec_name = "adav803.0-0034";
- break;
- default:
- return -EINVAL;
- }
-
- bfin_eval_adav80x_dais[0].codec_name = codec_name;
-
- card->dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
-}
-
-static const struct platform_device_id bfin_eval_adav80x_ids[] = {
- { "bfin-eval-adav801", BFIN_EVAL_ADAV801 },
- { "bfin-eval-adav803", BFIN_EVAL_ADAV803 },
- { },
-};
-MODULE_DEVICE_TABLE(platform, bfin_eval_adav80x_ids);
-
-static struct platform_driver bfin_eval_adav80x_driver = {
- .driver = {
- .name = "bfin-eval-adav80x",
- .pm = &snd_soc_pm_ops,
- },
- .probe = bfin_eval_adav80x_probe,
- .id_table = bfin_eval_adav80x_ids,
-};
-
-module_platform_driver(bfin_eval_adav80x_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC bfin adav80x driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index c7cd60f009e9..31475e64e7dd 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Cirrus Logic"
+
config SND_EP93XX_SOC
tristate "SoC Audio support for the Cirrus Logic EP93xx series"
depends on ARCH_EP93XX || COMPILE_TEST
@@ -7,37 +10,27 @@ config SND_EP93XX_SOC
the EP93xx I2S or AC97 interfaces.
config SND_EP93XX_SOC_I2S
- tristate
-
-config SND_EP93XX_SOC_AC97
- tristate
- select AC97_BUS
- select SND_SOC_AC97_BUS
+ tristate "I2S controller support for the Cirrus Logic EP93xx series"
+ depends on SND_EP93XX_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the EP93xx I2S interface.
-config SND_EP93XX_SOC_SNAPPERCL15
- tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
- depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 && I2C
- select SND_EP93XX_SOC_I2S
- select SND_SOC_TLV320AIC23_I2C
- help
- Say Y or M here if you want to add support for I2S audio on the
- Bluewater Systems Snapper CL15 module.
+if SND_EP93XX_SOC_I2S
-config SND_EP93XX_SOC_SIMONE
- tristate "SoC Audio support for Simplemachines Sim.One board"
- depends on SND_EP93XX_SOC && MACH_SIM_ONE
- select SND_EP93XX_SOC_AC97
- select SND_SOC_AC97_CODEC
+config SND_EP93XX_SOC_I2S_WATCHDOG
+ bool "IRQ based underflow watchdog workaround"
+ default y
help
- Say Y or M here if you want to add support for AC97 audio on the
- Simplemachines Sim.One board.
+ I2S controller on EP93xx seems to have undocumented HW issue.
+ Underflow of internal I2S controller FIFO could confuse the
+ state machine and the whole stream can be shifted by one byte
+ until I2S is disabled. This option enables IRQ based watchdog
+ which disables and re-enables I2S in case of underflow and
+ fills FIFO with zeroes.
-config SND_EP93XX_SOC_EDB93XX
- tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
- depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
- select SND_EP93XX_SOC_I2S
- select SND_SOC_CS4271_I2C if I2C
- select SND_SOC_CS4271_SPI if SPI_MASTER
- help
- Say Y or M here if you want to add support for I2S audio on the
- Cirrus Logic EDB93xx boards.
+ If you are unsure how to answer this question, answer Y.
+
+endif # if SND_EP93XX_SOC_I2S
+
+endmenu
diff --git a/sound/soc/cirrus/Makefile b/sound/soc/cirrus/Makefile
index 5514146cbdf0..61d8cf64e859 100644
--- a/sound/soc/cirrus/Makefile
+++ b/sound/soc/cirrus/Makefile
@@ -1,17 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
# EP93xx Platform Support
-snd-soc-ep93xx-objs := ep93xx-pcm.o
-snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
-snd-soc-ep93xx-ac97-objs := ep93xx-ac97.o
+snd-soc-ep93xx-y := ep93xx-pcm.o
+snd-soc-ep93xx-i2s-y := ep93xx-i2s.o
obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
-obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o
-# EP93XX Machine Support
-snd-soc-snappercl15-objs := snappercl15.o
-snd-soc-simone-objs := simone.o
-snd-soc-edb93xx-objs := edb93xx.o
-
-obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o
-obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o
-obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o
diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
deleted file mode 100644
index 85962657aabe..000000000000
--- a/sound/soc/cirrus/edb93xx.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * SoC audio for EDB93xx
- *
- * 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/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-static int edb93xx_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int err;
- unsigned int mclk_rate;
- unsigned int rate = params_rate(params);
-
- /*
- * According to CS4271 datasheet we use MCLK/LRCK=256 for
- * rates below 50kHz and 128 for higher sample rates
- */
- if (rate < 50000)
- mclk_rate = rate * 64 * 4;
- else
- mclk_rate = rate * 64 * 2;
-
- err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
- SND_SOC_CLOCK_IN);
- if (err)
- return err;
-
- return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
- SND_SOC_CLOCK_OUT);
-}
-
-static struct snd_soc_ops edb93xx_ops = {
- .hw_params = edb93xx_hw_params,
-};
-
-static struct snd_soc_dai_link edb93xx_dai = {
- .name = "CS4271",
- .stream_name = "CS4271 HiFi",
- .platform_name = "ep93xx-i2s",
- .cpu_dai_name = "ep93xx-i2s",
- .codec_name = "spi0.0",
- .codec_dai_name = "cs4271-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &edb93xx_ops,
-};
-
-static struct snd_soc_card snd_soc_edb93xx = {
- .name = "EDB93XX",
- .owner = THIS_MODULE,
- .dai_link = &edb93xx_dai,
- .num_links = 1,
-};
-
-static int edb93xx_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_edb93xx;
- int ret;
-
- ret = ep93xx_i2s_acquire();
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- ep93xx_i2s_release();
- }
-
- return ret;
-}
-
-static int edb93xx_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- ep93xx_i2s_release();
-
- return 0;
-}
-
-static struct platform_driver edb93xx_driver = {
- .driver = {
- .name = "edb93xx-audio",
- },
- .probe = edb93xx_probe,
- .remove = edb93xx_remove,
-};
-
-module_platform_driver(edb93xx_driver);
-
-MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
-MODULE_DESCRIPTION("ALSA SoC EDB93xx");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:edb93xx-audio");
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
deleted file mode 100644
index bbf7a9266a99..000000000000
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * ASoC driver for Cirrus Logic EP93xx AC97 controller.
- *
- * Copyright (c) 2010 Mika Westerberg
- *
- * Based on s3c-ac97 ASoC driver by Jaswinder Singh.
- *
- * This program is free software; you can 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/err.h>
-#include <linux/io.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <sound/core.h>
-#include <sound/dmaengine_pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/soc.h>
-
-#include <linux/platform_data/dma-ep93xx.h>
-
-#include "ep93xx-pcm.h"
-
-/*
- * Per channel (1-4) registers.
- */
-#define AC97CH(n) (((n) - 1) * 0x20)
-
-#define AC97DR(n) (AC97CH(n) + 0x0000)
-
-#define AC97RXCR(n) (AC97CH(n) + 0x0004)
-#define AC97RXCR_REN BIT(0)
-#define AC97RXCR_RX3 BIT(3)
-#define AC97RXCR_RX4 BIT(4)
-#define AC97RXCR_CM BIT(15)
-
-#define AC97TXCR(n) (AC97CH(n) + 0x0008)
-#define AC97TXCR_TEN BIT(0)
-#define AC97TXCR_TX3 BIT(3)
-#define AC97TXCR_TX4 BIT(4)
-#define AC97TXCR_CM BIT(15)
-
-#define AC97SR(n) (AC97CH(n) + 0x000c)
-#define AC97SR_TXFE BIT(1)
-#define AC97SR_TXUE BIT(6)
-
-#define AC97RISR(n) (AC97CH(n) + 0x0010)
-#define AC97ISR(n) (AC97CH(n) + 0x0014)
-#define AC97IE(n) (AC97CH(n) + 0x0018)
-
-/*
- * Global AC97 controller registers.
- */
-#define AC97S1DATA 0x0080
-#define AC97S2DATA 0x0084
-#define AC97S12DATA 0x0088
-
-#define AC97RGIS 0x008c
-#define AC97GIS 0x0090
-#define AC97IM 0x0094
-/*
- * Common bits for RGIS, GIS and IM registers.
- */
-#define AC97_SLOT2RXVALID BIT(1)
-#define AC97_CODECREADY BIT(5)
-#define AC97_SLOT2TXCOMPLETE BIT(6)
-
-#define AC97EOI 0x0098
-#define AC97EOI_WINT BIT(0)
-#define AC97EOI_CODECREADY BIT(1)
-
-#define AC97GCR 0x009c
-#define AC97GCR_AC97IFE BIT(0)
-
-#define AC97RESET 0x00a0
-#define AC97RESET_TIMEDRESET BIT(0)
-
-#define AC97SYNC 0x00a4
-#define AC97SYNC_TIMEDSYNC BIT(0)
-
-#define AC97_TIMEOUT msecs_to_jiffies(5)
-
-/**
- * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
- * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
- * @dev: pointer to the platform device dev structure
- * @regs: mapped AC97 controller registers
- * @done: bus ops wait here for an interrupt
- */
-struct ep93xx_ac97_info {
- struct mutex lock;
- struct device *dev;
- void __iomem *regs;
- struct completion done;
- struct snd_dmaengine_dai_dma_data dma_params_rx;
- struct snd_dmaengine_dai_dma_data dma_params_tx;
-};
-
-/* currently ALSA only supports a single AC97 device */
-static struct ep93xx_ac97_info *ep93xx_ac97_info;
-
-static struct ep93xx_dma_data ep93xx_ac97_pcm_out = {
- .name = "ac97-pcm-out",
- .port = EP93XX_DMA_AAC1,
- .direction = DMA_MEM_TO_DEV,
-};
-
-static struct ep93xx_dma_data ep93xx_ac97_pcm_in = {
- .name = "ac97-pcm-in",
- .port = EP93XX_DMA_AAC1,
- .direction = DMA_DEV_TO_MEM,
-};
-
-static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
- unsigned reg)
-{
- return __raw_readl(info->regs + reg);
-}
-
-static inline void ep93xx_ac97_write_reg(struct ep93xx_ac97_info *info,
- unsigned reg, unsigned val)
-{
- __raw_writel(val, info->regs + reg);
-}
-
-static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
- unsigned short val;
-
- mutex_lock(&info->lock);
-
- ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2RXVALID);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT)) {
- dev_warn(info->dev, "timeout reading register %x\n", reg);
- mutex_unlock(&info->lock);
- return -ETIMEDOUT;
- }
- val = (unsigned short)ep93xx_ac97_read_reg(info, AC97S2DATA);
-
- mutex_unlock(&info->lock);
- return val;
-}
-
-static void ep93xx_ac97_write(struct snd_ac97 *ac97,
- unsigned short reg,
- unsigned short val)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * Writes to the codec need to be done so that slot 2 is filled in
- * before slot 1.
- */
- ep93xx_ac97_write_reg(info, AC97S2DATA, val);
- ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
-
- ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2TXCOMPLETE);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "timeout writing register %x\n", reg);
-
- mutex_unlock(&info->lock);
-}
-
-static void ep93xx_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * We are assuming that before this functions gets called, the codec
- * BIT_CLK is stopped by forcing the codec into powerdown mode. We can
- * control the SYNC signal directly via AC97SYNC register. Using
- * TIMEDSYNC the controller will keep the SYNC high > 1us.
- */
- ep93xx_ac97_write_reg(info, AC97SYNC, AC97SYNC_TIMEDSYNC);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "codec warm reset timeout\n");
-
- mutex_unlock(&info->lock);
-}
-
-static void ep93xx_ac97_cold_reset(struct snd_ac97 *ac97)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * For doing cold reset, we disable the AC97 controller interface, clear
- * WINT and CODECREADY bits, and finally enable the interface again.
- */
- ep93xx_ac97_write_reg(info, AC97GCR, 0);
- ep93xx_ac97_write_reg(info, AC97EOI, AC97EOI_CODECREADY | AC97EOI_WINT);
- ep93xx_ac97_write_reg(info, AC97GCR, AC97GCR_AC97IFE);
-
- /*
- * Now, assert the reset and wait for the codec to become ready.
- */
- ep93xx_ac97_write_reg(info, AC97RESET, AC97RESET_TIMEDRESET);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "codec cold reset timeout\n");
-
- /*
- * Give the codec some time to come fully out from the reset. This way
- * we ensure that the subsequent reads/writes will work.
- */
- usleep_range(15000, 20000);
-
- mutex_unlock(&info->lock);
-}
-
-static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
-{
- struct ep93xx_ac97_info *info = dev_id;
- unsigned status, mask;
-
- /*
- * Just mask out the interrupt and wake up the waiting thread.
- * Interrupts are cleared via reading/writing to slot 1 & 2 registers by
- * the waiting thread.
- */
- status = ep93xx_ac97_read_reg(info, AC97GIS);
- mask = ep93xx_ac97_read_reg(info, AC97IM);
- mask &= ~status;
- ep93xx_ac97_write_reg(info, AC97IM, mask);
-
- complete(&info->done);
- return IRQ_HANDLED;
-}
-
-static struct snd_ac97_bus_ops ep93xx_ac97_ops = {
- .read = ep93xx_ac97_read,
- .write = ep93xx_ac97_write,
- .reset = ep93xx_ac97_cold_reset,
- .warm_reset = ep93xx_ac97_warm_reset,
-};
-
-static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
- unsigned v = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /*
- * Enable compact mode, TX slots 3 & 4, and the TX FIFO
- * itself.
- */
- v |= AC97TXCR_CM;
- v |= AC97TXCR_TX3 | AC97TXCR_TX4;
- v |= AC97TXCR_TEN;
- ep93xx_ac97_write_reg(info, AC97TXCR(1), v);
- } else {
- /*
- * Enable compact mode, RX slots 3 & 4, and the RX FIFO
- * itself.
- */
- v |= AC97RXCR_CM;
- v |= AC97RXCR_RX3 | AC97RXCR_RX4;
- v |= AC97RXCR_REN;
- ep93xx_ac97_write_reg(info, AC97RXCR(1), v);
- }
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /*
- * As per Cirrus EP93xx errata described below:
- *
- * http://www.cirrus.com/en/pubs/errata/ER667E2B.pdf
- *
- * we will wait for the TX FIFO to be empty before
- * clearing the TEN bit.
- */
- unsigned long timeout = jiffies + AC97_TIMEOUT;
-
- do {
- v = ep93xx_ac97_read_reg(info, AC97SR(1));
- if (time_after(jiffies, timeout)) {
- dev_warn(info->dev, "TX timeout\n");
- break;
- }
- } while (!(v & (AC97SR_TXFE | AC97SR_TXUE)));
-
- /* disable the TX FIFO */
- ep93xx_ac97_write_reg(info, AC97TXCR(1), 0);
- } else {
- /* disable the RX FIFO */
- ep93xx_ac97_write_reg(info, AC97RXCR(1), 0);
- }
- break;
-
- default:
- dev_warn(info->dev, "unknown command %d\n", cmd);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
-{
- struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
-
- info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out;
- info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in;
-
- dai->playback_dma_data = &info->dma_params_tx;
- dai->capture_dma_data = &info->dma_params_rx;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
- .trigger = ep93xx_ac97_trigger,
-};
-
-static struct snd_soc_dai_driver ep93xx_ac97_dai = {
- .name = "ep93xx-ac97",
- .id = 0,
- .bus_control = true,
- .probe = ep93xx_ac97_dai_probe,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &ep93xx_ac97_dai_ops,
-};
-
-static const struct snd_soc_component_driver ep93xx_ac97_component = {
- .name = "ep93xx-ac97",
-};
-
-static int ep93xx_ac97_probe(struct platform_device *pdev)
-{
- struct ep93xx_ac97_info *info;
- struct resource *res;
- unsigned int irq;
- int ret;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(info->regs))
- return PTR_ERR(info->regs);
-
- irq = platform_get_irq(pdev, 0);
- if (!irq)
- return -ENODEV;
-
- ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
- IRQF_TRIGGER_HIGH, pdev->name, info);
- if (ret)
- goto fail;
-
- dev_set_drvdata(&pdev->dev, info);
-
- mutex_init(&info->lock);
- init_completion(&info->done);
- info->dev = &pdev->dev;
-
- ep93xx_ac97_info = info;
- platform_set_drvdata(pdev, info);
-
- ret = snd_soc_set_ac97_ops(&ep93xx_ac97_ops);
- if (ret)
- goto fail;
-
- ret = snd_soc_register_component(&pdev->dev, &ep93xx_ac97_component,
- &ep93xx_ac97_dai, 1);
- if (ret)
- goto fail;
-
- ret = devm_ep93xx_pcm_platform_register(&pdev->dev);
- if (ret)
- goto fail_unregister;
-
- return 0;
-
-fail_unregister:
- snd_soc_unregister_component(&pdev->dev);
-fail:
- ep93xx_ac97_info = NULL;
- snd_soc_set_ac97_ops(NULL);
- return ret;
-}
-
-static int ep93xx_ac97_remove(struct platform_device *pdev)
-{
- struct ep93xx_ac97_info *info = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
-
- /* disable the AC97 controller */
- ep93xx_ac97_write_reg(info, AC97GCR, 0);
-
- ep93xx_ac97_info = NULL;
-
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver ep93xx_ac97_driver = {
- .probe = ep93xx_ac97_probe,
- .remove = ep93xx_ac97_remove,
- .driver = {
- .name = "ep93xx-ac97",
- },
-};
-
-module_platform_driver(ep93xx_ac97_driver);
-
-MODULE_DESCRIPTION("EP93xx AC97 ASoC Driver");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ep93xx-ac97");
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 934f8aefdd90..cca01c03f048 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/soc/ep93xx-i2s.c
* EP93xx I2S driver
@@ -7,11 +8,6 @@
* Based on the original driver by:
* Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.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.
- *
*/
#include <linux/module.h>
@@ -19,6 +15,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
@@ -27,16 +24,18 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/hardware.h>
-#include <mach/ep93xx-regs.h>
-#include <linux/platform_data/dma-ep93xx.h>
+#include <linux/soc/cirrus/ep93xx.h>
#include "ep93xx-pcm.h"
#define EP93XX_I2S_TXCLKCFG 0x00
#define EP93XX_I2S_RXCLKCFG 0x04
+#define EP93XX_I2S_GLSTS 0x08
#define EP93XX_I2S_GLCTRL 0x0C
+#define EP93XX_I2S_I2STX0LFT 0x10
+#define EP93XX_I2S_I2STX0RT 0x14
+
#define EP93XX_I2S_TXLINCTRLDATA 0x28
#define EP93XX_I2S_TXCTRL 0x2C
#define EP93XX_I2S_TXWRDLEN 0x30
@@ -51,7 +50,17 @@
#define EP93XX_I2S_WRDLEN_24 (1 << 0)
#define EP93XX_I2S_WRDLEN_32 (2 << 0)
-#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
+#define EP93XX_I2S_RXLINCTRLDATA_R_JUST BIT(1) /* Right justify */
+
+#define EP93XX_I2S_TXLINCTRLDATA_R_JUST BIT(2) /* Right justify */
+
+/*
+ * Transmit empty interrupt level select:
+ * 0 - Generate interrupt when FIFO is half empty
+ * 1 - Generate interrupt when FIFO is empty
+ */
+#define EP93XX_I2S_TXCTRL_TXEMPTY_LVL BIT(0)
+#define EP93XX_I2S_TXCTRL_TXUFIE BIT(1) /* Transmit interrupt enable */
#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
@@ -59,6 +68,8 @@
#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
+#define EP93XX_I2S_GLSTS_TX0_FIFO_FULL BIT(12)
+
struct ep93xx_i2s_info {
struct clk *mclk;
struct clk *sclk;
@@ -68,19 +79,6 @@ struct ep93xx_i2s_info {
struct snd_dmaengine_dai_dma_data dma_params_tx;
};
-static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = {
- .name = "i2s-pcm-out",
- .port = EP93XX_DMA_I2S1,
- .direction = DMA_MEM_TO_DEV,
- },
- [SNDRV_PCM_STREAM_CAPTURE] = {
- .name = "i2s-pcm-in",
- .port = EP93XX_DMA_I2S1,
- .direction = DMA_DEV_TO_MEM,
- },
-};
-
static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
unsigned reg, unsigned val)
{
@@ -96,40 +94,48 @@ static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
- int i;
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
/* Enable clocks */
- clk_enable(info->mclk);
- clk_enable(info->sclk);
- clk_enable(info->lrclk);
+ clk_prepare_enable(info->mclk);
+ clk_prepare_enable(info->sclk);
+ clk_prepare_enable(info->lrclk);
/* Enable i2s */
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
}
- /* Enable fifos */
+ /* Enable fifo */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
base_reg = EP93XX_I2S_TX0EN;
else
base_reg = EP93XX_I2S_RX0EN;
- for (i = 0; i < 3; i++)
- ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
+ ep93xx_i2s_write_reg(info, base_reg, 1);
+
+ /* Enable TX IRQs (FIFO empty or underflow) */
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) &&
+ stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL,
+ EP93XX_I2S_TXCTRL_TXEMPTY_LVL |
+ EP93XX_I2S_TXCTRL_TXUFIE);
}
static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
- int i;
- /* Disable fifos */
+ /* Disable IRQs */
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) &&
+ stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, 0);
+
+ /* Disable fifo */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
base_reg = EP93XX_I2S_TX0EN;
else
base_reg = EP93XX_I2S_RX0EN;
- for (i = 0; i < 3; i++)
- ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
+ ep93xx_i2s_write_reg(info, base_reg, 0);
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
@@ -137,23 +143,59 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
/* Disable clocks */
- clk_disable(info->lrclk);
- clk_disable(info->sclk);
- clk_disable(info->mclk);
+ clk_disable_unprepare(info->lrclk);
+ clk_disable_unprepare(info->sclk);
+ clk_disable_unprepare(info->mclk);
}
}
+/*
+ * According to documentation I2S controller can handle underflow conditions
+ * just fine, but in reality the state machine is sometimes confused so that
+ * the whole stream is shifted by one byte. The watchdog below disables the TX
+ * FIFO, fills the buffer with zeroes and re-enables the FIFO. State machine
+ * is being reset and by filling the buffer we get some time before next
+ * underflow happens.
+ */
+static irqreturn_t ep93xx_i2s_interrupt(int irq, void *dev_id)
+{
+ struct ep93xx_i2s_info *info = dev_id;
+
+ /* Disable FIFO */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 0);
+ /*
+ * Fill TX FIFO with zeroes, this way we can defer next IRQs as much as
+ * possible and get more time for DMA to catch up. Actually there are
+ * only 8 samples in this FIFO, so even on 8kHz maximum deferral here is
+ * 1ms.
+ */
+ while (!(ep93xx_i2s_read_reg(info, EP93XX_I2S_GLSTS) &
+ EP93XX_I2S_GLSTS_TX0_FIFO_FULL)) {
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0LFT, 0);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0RT, 0);
+ }
+ /* Re-enable FIFO */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 1);
+
+ return IRQ_HANDLED;
+}
+
static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
- info->dma_params_tx.filter_data =
- &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- info->dma_params_rx.filter_data =
- &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ snd_soc_dai_init_dma_data(dai, &info->dma_params_tx,
+ &info->dma_params_rx);
- dai->playback_dma_data = &info->dma_params_tx;
- dai->capture_dma_data = &info->dma_params_rx;
+ return 0;
+}
+
+static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ ep93xx_i2s_enable(info, substream->stream);
return 0;
}
@@ -170,39 +212,39 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
- unsigned int clk_cfg, lin_ctrl;
+ unsigned int clk_cfg;
+ unsigned int txlin_ctrl = 0;
+ unsigned int rxlin_ctrl = 0;
clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
- lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
clk_cfg |= EP93XX_I2S_CLKCFG_REL;
- lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
case SND_SOC_DAIFMT_RIGHT_J:
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
+ rxlin_ctrl |= EP93XX_I2S_RXLINCTRLDATA_R_JUST;
+ txlin_ctrl |= EP93XX_I2S_TXLINCTRLDATA_R_JUST;
break;
default:
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* CPU is master */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* CPU is provider */
clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Codec is master */
+ case SND_SOC_DAIFMT_BC_FC:
+ /* Codec is provider */
clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
break;
@@ -213,32 +255,32 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
/* Negative bit clock, lrclk low on left word */
- clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
+ clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS);
break;
case SND_SOC_DAIFMT_NB_IF:
/* Negative bit clock, lrclk low on right word */
clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
- clk_cfg |= EP93XX_I2S_CLKCFG_REL;
+ clk_cfg |= EP93XX_I2S_CLKCFG_LRS;
break;
case SND_SOC_DAIFMT_IB_NF:
/* Positive bit clock, lrclk low on left word */
clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
- clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_LRS;
break;
case SND_SOC_DAIFMT_IB_IF:
/* Positive bit clock, lrclk low on right word */
- clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
+ clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS;
break;
}
/* Write new register values */
ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, rxlin_ctrl);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, txlin_ctrl);
return 0;
}
@@ -298,7 +340,6 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
if (err)
return err;
- ep93xx_i2s_enable(info, substream->stream);
return 0;
}
@@ -309,16 +350,18 @@ static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
return -EINVAL;
+ if (!freq)
+ return 0;
return clk_set_rate(info->mclk, freq);
}
#ifdef CONFIG_PM
-static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
+static int ep93xx_i2s_suspend(struct snd_soc_component *component)
{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+ struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
- if (!dai->active)
+ if (!snd_soc_component_active(component))
return 0;
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
@@ -327,11 +370,11 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
return 0;
}
-static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
+static int ep93xx_i2s_resume(struct snd_soc_component *component)
{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+ struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
- if (!dai->active)
+ if (!snd_soc_component_active(component))
return 0;
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
@@ -345,6 +388,8 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
#endif
static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
+ .probe = ep93xx_i2s_dai_probe,
+ .startup = ep93xx_i2s_startup,
.shutdown = ep93xx_i2s_shutdown,
.hw_params = ep93xx_i2s_hw_params,
.set_sysclk = ep93xx_i2s_set_sysclk,
@@ -354,10 +399,7 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver ep93xx_i2s_dai = {
- .symmetric_rates= 1,
- .probe = ep93xx_i2s_dai_probe,
- .suspend = ep93xx_i2s_suspend,
- .resume = ep93xx_i2s_resume,
+ .symmetric_rate = 1,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -374,24 +416,36 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = {
};
static const struct snd_soc_component_driver ep93xx_i2s_component = {
- .name = "ep93xx-i2s",
+ .name = "ep93xx-i2s",
+ .suspend = ep93xx_i2s_suspend,
+ .resume = ep93xx_i2s_resume,
+ .legacy_dai_naming = 1,
};
static int ep93xx_i2s_probe(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info;
- struct resource *res;
int err;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_ioremap_resource(&pdev->dev, res);
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
+ if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG)) {
+ int irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq < 0 ? irq : -ENODEV;
+
+ err = devm_request_irq(&pdev->dev, irq, ep93xx_i2s_interrupt, 0,
+ pdev->name, info);
+ if (err)
+ return err;
+ }
+
info->mclk = clk_get(&pdev->dev, "mclk");
if (IS_ERR(info->mclk)) {
err = PTR_ERR(info->mclk);
@@ -412,19 +466,17 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, info);
- err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
+ err = devm_snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
&ep93xx_i2s_dai, 1);
if (err)
goto fail_put_lrclk;
err = devm_ep93xx_pcm_platform_register(&pdev->dev);
if (err)
- goto fail_unregister;
+ goto fail_put_lrclk;
return 0;
-fail_unregister:
- snd_soc_unregister_component(&pdev->dev);
fail_put_lrclk:
clk_put(info->lrclk);
fail_put_sclk:
@@ -435,22 +487,27 @@ fail:
return err;
}
-static int ep93xx_i2s_remove(struct platform_device *pdev)
+static void ep93xx_i2s_remove(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
clk_put(info->lrclk);
clk_put(info->sclk);
clk_put(info->mclk);
- return 0;
}
+static const struct of_device_id ep93xx_i2s_of_ids[] = {
+ { .compatible = "cirrus,ep9301-i2s" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ep93xx_i2s_of_ids);
+
static struct platform_driver ep93xx_i2s_driver = {
.probe = ep93xx_i2s_probe,
- .remove = ep93xx_i2s_remove,
+ .remove = ep93xx_i2s_remove,
.driver = {
.name = "ep93xx-i2s",
+ .of_match_table = ep93xx_i2s_of_ids,
},
};
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index 67a73330db5e..5ecb4671cbba 100644
--- a/sound/soc/cirrus/ep93xx-pcm.c
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
*
@@ -6,10 +7,6 @@
*
* Rewritten for the SoC audio subsystem (Based on PXA2xx code):
* Copyright (c) 2008 Ryan Mallon
- *
- * This program is free software; you can 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>
@@ -21,8 +18,6 @@
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
-#include <linux/platform_data/dma-ep93xx.h>
-
#include "ep93xx-pcm.h"
static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
@@ -38,30 +33,15 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
.fifo_size = 32,
};
-static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
-{
- struct ep93xx_dma_data *data = filter_param;
-
- if (data->direction == ep93xx_dma_chan_direction(chan)) {
- chan->private = data;
- return true;
- }
-
- return false;
-}
-
static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = {
.pcm_hardware = &ep93xx_pcm_hardware,
- .compat_filter_fn = ep93xx_pcm_dma_filter,
.prealloc_buffer_size = 131072,
};
int devm_ep93xx_pcm_platform_register(struct device *dev)
{
return devm_snd_dmaengine_pcm_register(dev,
- &ep93xx_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_NO_DT |
- SND_DMAENGINE_PCM_FLAG_COMPAT);
+ &ep93xx_dmaengine_pcm_config, 0);
}
EXPORT_SYMBOL_GPL(devm_ep93xx_pcm_platform_register);
diff --git a/sound/soc/cirrus/ep93xx-pcm.h b/sound/soc/cirrus/ep93xx-pcm.h
index b7a12a2fae9c..8e1c722bffe0 100644
--- a/sound/soc/cirrus/ep93xx-pcm.h
+++ b/sound/soc/cirrus/ep93xx-pcm.h
@@ -1,17 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __EP93XX_PCM_H__
diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c
deleted file mode 100644
index 1ec661834e5a..000000000000
--- a/sound/soc/cirrus/simone.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * simone.c -- ASoC audio for Simplemachines Sim.One board
- *
- * Copyright (c) 2010 Mika Westerberg
- *
- * Based on snappercl15 machine driver by Ryan Mallon.
- *
- * This program is free software; you can 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/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-static struct snd_soc_dai_link simone_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "ep93xx-ac97",
- .codec_dai_name = "ac97-hifi",
- .codec_name = "ac97-codec",
- .platform_name = "ep93xx-ac97",
-};
-
-static struct snd_soc_card snd_soc_simone = {
- .name = "Sim.One",
- .owner = THIS_MODULE,
- .dai_link = &simone_dai,
- .num_links = 1,
-};
-
-static struct platform_device *simone_snd_ac97_device;
-
-static int simone_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_simone;
- int ret;
-
- simone_snd_ac97_device = platform_device_register_simple("ac97-codec",
- -1, NULL, 0);
- if (IS_ERR(simone_snd_ac97_device))
- return PTR_ERR(simone_snd_ac97_device);
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- platform_device_unregister(simone_snd_ac97_device);
- }
-
- return ret;
-}
-
-static int simone_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- platform_device_unregister(simone_snd_ac97_device);
-
- return 0;
-}
-
-static struct platform_driver simone_driver = {
- .driver = {
- .name = "simone-audio",
- },
- .probe = simone_probe,
- .remove = simone_remove,
-};
-
-module_platform_driver(simone_driver);
-
-MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:simone-audio");
diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c
deleted file mode 100644
index 98089df08df6..000000000000
--- a/sound/soc/cirrus/snappercl15.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
- *
- * Copyright (C) 2008 Bluewater Systems Ltd
- * Author: Ryan Mallon
- *
- * 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/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-#include "../codecs/tlv320aic23.h"
-
-#define CODEC_CLOCK 5644800
-
-static int snappercl15_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int err;
-
- err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK,
- SND_SOC_CLOCK_IN);
- if (err)
- return err;
-
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK,
- SND_SOC_CLOCK_OUT);
- if (err)
- return err;
-
- return 0;
-}
-
-static struct snd_soc_ops snappercl15_ops = {
- .hw_params = snappercl15_hw_params,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Jack", NULL, "LHPOUT"},
- {"Headphone Jack", NULL, "RHPOUT"},
-
- {"LLINEIN", NULL, "Line In"},
- {"RLINEIN", NULL, "Line In"},
-
- {"MICIN", NULL, "Mic Jack"},
-};
-
-static struct snd_soc_dai_link snappercl15_dai = {
- .name = "tlv320aic23",
- .stream_name = "AIC23",
- .cpu_dai_name = "ep93xx-i2s",
- .codec_dai_name = "tlv320aic23-hifi",
- .codec_name = "tlv320aic23-codec.0-001a",
- .platform_name = "ep93xx-i2s",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &snappercl15_ops,
-};
-
-static struct snd_soc_card snd_soc_snappercl15 = {
- .name = "Snapper CL15",
- .owner = THIS_MODULE,
- .dai_link = &snappercl15_dai,
- .num_links = 1,
-
- .dapm_widgets = tlv320aic23_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static int snappercl15_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_snappercl15;
- int ret;
-
- ret = ep93xx_i2s_acquire();
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- ep93xx_i2s_release();
- }
-
- return ret;
-}
-
-static int snappercl15_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- ep93xx_i2s_release();
-
- return 0;
-}
-
-static struct platform_driver snappercl15_driver = {
- .driver = {
- .name = "snappercl15-audio",
- },
- .probe = snappercl15_probe,
- .remove = snappercl15_remove,
-};
-
-module_platform_driver(snappercl15_driver);
-
-MODULE_AUTHOR("Ryan Mallon");
-MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:snappercl15-audio");
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index e8bed6b0c9db..b4f5b24cde45 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Author: Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -139,14 +136,14 @@ struct pm860x_priv {
unsigned int pcmclk;
unsigned int dir;
unsigned int filter;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct i2c_client *i2c;
struct regmap *regmap;
struct pm860x_chip *chip;
struct pm860x_det det;
int irq[4];
- unsigned char name[4][MAX_NAME_LEN+1];
+ unsigned char name[4][MAX_NAME_LEN];
};
/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
@@ -272,15 +269,15 @@ static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int val[2], val2[2], i;
- val[0] = snd_soc_read(codec, reg) & 0x3f;
- val[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT) >> 4) & 0xf;
- val2[0] = snd_soc_read(codec, reg2) & 0x3f;
- val2[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT)) & 0xf;
+ val[0] = snd_soc_component_read(component, reg) & 0x3f;
+ val[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT) >> 4) & 0xf;
+ val2[0] = snd_soc_component_read(component, reg2) & 0x3f;
+ val2[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT)) & 0xf;
for (i = 0; i < ARRAY_SIZE(st_table); i++) {
if ((st_table[i].m == val[0]) && (st_table[i].n == val[1]))
@@ -296,7 +293,7 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int err;
@@ -308,18 +305,18 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
return -EINVAL;
- err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
+ err = snd_soc_component_update_bits(component, reg, 0x3f, st_table[val].m);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0xf0,
+ err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0xf0,
st_table[val].n << 4);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, 0x3f, st_table[val2].m);
+ err = snd_soc_component_update_bits(component, reg2, 0x3f, st_table[val2].m);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0x0f,
+ err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0x0f,
st_table[val2].n);
return err;
}
@@ -329,15 +326,15 @@ static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
int max = mc->max, val, val2;
unsigned int mask = (1 << fls(max)) - 1;
- val = snd_soc_read(codec, reg) >> shift;
- val2 = snd_soc_read(codec, reg2) >> shift;
+ val = snd_soc_component_read(component, reg) >> shift;
+ val2 = snd_soc_component_read(component, reg2) >> shift;
ucontrol->value.integer.value[0] = (max - val) & mask;
ucontrol->value.integer.value[1] = (max - val2) & mask;
@@ -349,7 +346,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
@@ -365,11 +362,11 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- err = snd_soc_update_bits(codec, reg, val_mask, val);
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ err = snd_soc_component_update_bits(component, reg2, val_mask, val2);
return err;
}
@@ -382,7 +379,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
/*
* In order to avoid current on the load, mute power-on and power-off
@@ -390,8 +387,8 @@ static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
* Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is
* finished.
*/
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET, DAC_MUTE, 0);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, DAC_MUTE, 0);
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
return 0;
}
@@ -399,41 +396,41 @@ static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
static int pm860x_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int dac = 0;
int data;
- if (!strcmp(w->name, "Left DAC"))
+ if (!snd_soc_dapm_widget_name_cmp(w, "Left DAC"))
dac = DAC_LEFT;
- if (!strcmp(w->name, "Right DAC"))
+ if (!snd_soc_dapm_widget_name_cmp(w, "Right DAC"))
dac = DAC_RIGHT;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (dac) {
/* Auto mute in power-on sequence. */
dac |= MODULATOR;
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET,
DAC_MUTE, DAC_MUTE);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
/* update dac */
- snd_soc_update_bits(codec, PM860X_DAC_EN_2,
+ snd_soc_component_update_bits(component, PM860X_DAC_EN_2,
dac, dac);
}
break;
case SND_SOC_DAPM_PRE_PMD:
if (dac) {
/* Auto mute in power-off sequence. */
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET,
DAC_MUTE, DAC_MUTE);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
/* update dac */
- data = snd_soc_read(codec, PM860X_DAC_EN_2);
+ data = snd_soc_component_read(component, PM860X_DAC_EN_2);
data &= ~dac;
if (!(data & (DAC_LEFT | DAC_RIGHT)))
data &= ~MODULATOR;
- snd_soc_write(codec, PM860X_DAC_EN_2, data);
+ snd_soc_component_write(component, PM860X_DAC_EN_2, data);
}
break;
}
@@ -532,10 +529,6 @@ static const struct snd_kcontrol_new pm860x_snd_controls[] = {
* DAPM Controls
*/
-/* PCM Switch / PCM Interface */
-static const struct snd_kcontrol_new pcm_switch_controls =
- SOC_DAPM_SINGLE("Switch", PM860X_ADC_EN_2, 0, 1, 0);
-
/* AUX1 Switch */
static const struct snd_kcontrol_new aux1_switch_controls =
SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 4, 1, 0);
@@ -552,17 +545,6 @@ static const struct snd_kcontrol_new lepa_switch_controls =
static const struct snd_kcontrol_new repa_switch_controls =
SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0);
-/* PCM Mux / Mux7 */
-static const char *aif1_text[] = {
- "PCM L", "PCM R",
-};
-
-static SOC_ENUM_SINGLE_DECL(aif1_enum,
- PM860X_PCM_IFACE_3, 6, aif1_text);
-
-static const struct snd_kcontrol_new aif1_mux =
- SOC_DAPM_ENUM("PCM Mux", aif1_enum);
-
/* I2S Mux / Mux9 */
static const char *i2s_din_text[] = {
"DIN", "DIN1",
@@ -920,15 +902,15 @@ static const struct snd_soc_dapm_route pm860x_dapm_routes[] = {
* Use MUTE_LEFT & MUTE_RIGHT to implement digital mute.
* These bits can also be used to mute.
*/
-static int pm860x_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int pm860x_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int data = 0, mask = MUTE_LEFT | MUTE_RIGHT;
if (mute)
data = mask;
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET, mask, data);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, mask, data);
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
return 0;
}
@@ -937,7 +919,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned char inf = 0, mask = 0;
/* bit size */
@@ -952,7 +934,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
mask |= PCM_INF2_18WL;
- snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf);
/* sample rate */
switch (params_rate(params)) {
@@ -971,7 +953,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_PCM_RATE, 0x0f, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_RATE, 0x0f, inf);
return 0;
}
@@ -979,23 +961,23 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
unsigned char inf = 0, mask = 0;
int ret = -EINVAL;
mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
if (pm860x->dir == PM860X_CLK_DIR_OUT) {
inf |= PCM_INF2_MASTER;
ret = 0;
}
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN) {
inf &= ~PCM_INF2_MASTER;
ret = 0;
@@ -1012,15 +994,15 @@ static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
mask |= PCM_MODE_MASK;
if (ret)
return ret;
- snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf);
return 0;
}
static int pm860x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
if (dir == PM860X_CLK_DIR_OUT)
pm860x->dir = PM860X_CLK_DIR_OUT;
@@ -1034,7 +1016,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned char inf;
/* bit size */
@@ -1048,7 +1030,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
/* sample rate */
switch (params_rate(params)) {
@@ -1076,7 +1058,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_4, 0xf, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_4, 0xf, inf);
return 0;
}
@@ -1084,21 +1066,21 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
unsigned char inf = 0, mask = 0;
mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
if (pm860x->dir == PM860X_CLK_DIR_OUT)
inf |= PCM_INF2_MASTER;
else
return -EINVAL;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN)
inf &= ~PCM_INF2_MASTER;
else
@@ -1116,14 +1098,15 @@ static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
mask |= PCM_MODE_MASK;
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, mask, inf);
return 0;
}
-static int pm860x_set_bias_level(struct snd_soc_codec *codec,
+static int pm860x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int data;
switch (level) {
@@ -1134,7 +1117,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Enable Audio PLL & Audio section */
data = AUDIO_PLL | AUDIO_SECTION_ON;
pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
@@ -1154,17 +1137,19 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
}
static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+ .mute_stream = pm860x_mute_stream,
.hw_params = pm860x_pcm_hw_params,
.set_fmt = pm860x_pcm_set_dai_fmt,
.set_sysclk = pm860x_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+ .mute_stream = pm860x_mute_stream,
.hw_params = pm860x_i2s_hw_params,
.set_fmt = pm860x_i2s_set_dai_fmt,
.set_sysclk = pm860x_set_dai_sysclk,
+ .no_capture_mute = 1,
};
#define PM860X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
@@ -1216,7 +1201,7 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
},
};
-static irqreturn_t pm860x_codec_handler(int irq, void *data)
+static irqreturn_t pm860x_component_handler(int irq, void *data)
{
struct pm860x_priv *pm860x = data;
int status, shrt, report = 0, mic_report = 0;
@@ -1230,7 +1215,7 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
#ifndef CONFIG_SND_SOC_88PM860X_MODULE
if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 |
SHORT_LO1 | SHORT_LO2))
- trace_snd_soc_jack_irq(dev_name(pm860x->codec->dev));
+ trace_snd_soc_jack_irq(dev_name(pm860x->component->dev));
#endif
if ((pm860x->det.hp_det & SND_JACK_HEADPHONE)
@@ -1256,17 +1241,17 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE,
SND_JACK_MICROPHONE);
- dev_dbg(pm860x->codec->dev, "headphone report:0x%x, mask:%x\n",
+ dev_dbg(pm860x->component->dev, "headphone report:0x%x, mask:%x\n",
report, mask);
- dev_dbg(pm860x->codec->dev, "microphone report:0x%x\n", mic_report);
+ dev_dbg(pm860x->component->dev, "microphone report:0x%x\n", mic_report);
return IRQ_HANDLED;
}
-int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
+int pm860x_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack,
int det, int hook, int hs_shrt, int lo_shrt)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int data;
pm860x->det.hp_jack = jack;
@@ -1290,15 +1275,15 @@ int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
}
/* sync status */
- pm860x_codec_handler(0, pm860x);
+ pm860x_component_handler(0, pm860x);
return 0;
}
EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect);
-int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
+int pm860x_mic_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int det)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
pm860x->det.mic_jack = jack;
pm860x->det.mic_det = det;
@@ -1308,24 +1293,25 @@ int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
MICDET_MASK, MICDET_MASK);
/* sync status */
- pm860x_codec_handler(0, pm860x);
+ pm860x_component_handler(0, pm860x);
return 0;
}
EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect);
-static int pm860x_probe(struct snd_soc_codec *codec)
+static int pm860x_probe(struct snd_soc_component *component)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int i, ret;
- pm860x->codec = codec;
+ pm860x->component = component;
+ snd_soc_component_init_regmap(component, pm860x->regmap);
for (i = 0; i < 4; i++) {
ret = request_threaded_irq(pm860x->irq[i], NULL,
- pm860x_codec_handler, IRQF_ONESHOT,
+ pm860x_component_handler, IRQF_ONESHOT,
pm860x->name[i], pm860x);
if (ret < 0) {
- dev_err(codec->dev, "Failed to request IRQ!\n");
+ dev_err(component->dev, "Failed to request IRQ!\n");
goto out;
}
}
@@ -1338,35 +1324,28 @@ out:
return ret;
}
-static int pm860x_remove(struct snd_soc_codec *codec)
+static void pm860x_remove(struct snd_soc_component *component)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int i;
for (i = 3; i >= 0; i--)
free_irq(pm860x->irq[i], pm860x);
- return 0;
}
-static struct regmap *pm860x_get_regmap(struct device *dev)
-{
- struct pm860x_priv *pm860x = dev_get_drvdata(dev);
-
- return pm860x->regmap;
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_pm860x = {
- .probe = pm860x_probe,
- .remove = pm860x_remove,
- .set_bias_level = pm860x_set_bias_level,
- .get_regmap = pm860x_get_regmap,
-
- .controls = pm860x_snd_controls,
- .num_controls = ARRAY_SIZE(pm860x_snd_controls),
- .dapm_widgets = pm860x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets),
- .dapm_routes = pm860x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_pm860x = {
+ .probe = pm860x_probe,
+ .remove = pm860x_remove,
+ .set_bias_level = pm860x_set_bias_level,
+ .controls = pm860x_snd_controls,
+ .num_controls = ARRAY_SIZE(pm860x_snd_controls),
+ .dapm_widgets = pm860x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets),
+ .dapm_routes = pm860x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int pm860x_codec_probe(struct platform_device *pdev)
@@ -1395,30 +1374,24 @@ static int pm860x_codec_probe(struct platform_device *pdev)
return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
- strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
+ strscpy(pm860x->name[i], res->name, MAX_NAME_LEN);
}
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pm860x,
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_pm860x,
pm860x_dai, ARRAY_SIZE(pm860x_dai));
if (ret) {
- dev_err(&pdev->dev, "Failed to register codec\n");
+ dev_err(&pdev->dev, "Failed to register component\n");
return -EINVAL;
}
return ret;
}
-static int pm860x_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver pm860x_codec_driver = {
.driver = {
.name = "88pm860x-codec",
},
.probe = pm860x_codec_probe,
- .remove = pm860x_codec_remove,
};
module_platform_driver(pm860x_codec_driver);
diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h
index f7282f4f4a79..f025146e506c 100644
--- a/sound/soc/codecs/88pm860x-codec.h
+++ b/sound/soc/codecs/88pm860x-codec.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 88pm860x-codec.h -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __88PM860X_H
@@ -88,9 +85,9 @@
#define PM860X_SHORT_LINEOUT (1 << 4)
#define PM860X_DET_MASK 0x1F
-extern int pm860x_hs_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+extern int pm860x_hs_jack_detect(struct snd_soc_component *, struct snd_soc_jack *,
int, int, int, int);
-extern int pm860x_mic_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+extern int pm860x_mic_jack_detect(struct snd_soc_component *, struct snd_soc_jack *,
int);
#endif /* __88PM860X_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 50693c867e71..6087ebde9523 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Helper to resolve issues with configs that have SPI enabled but I2C
# modular, meaning we can't build the codec driver in with I2C support.
# We use an ordered list of conditional defaults to pick the appropriate
@@ -13,193 +14,370 @@ 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
- 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_AK4613 if I2C
- 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_CS4349 if I2C
- select SND_SOC_CS47L24 if MFD_CS47L24
- select SND_SOC_CX20442 if TTY
- select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
- select SND_SOC_DA7213 if I2C
- select SND_SOC_DA7218 if I2C
- select SND_SOC_DA7219 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_GTM601
- select SND_SOC_HDAC_HDMI
- select SND_SOC_ICS43432
- select SND_SOC_INNO_RK3036
- 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_NAU8825 if I2C
- select SND_SOC_PCM1681 if I2C
- select SND_SOC_PCM179X if SPI_MASTER
- select SND_SOC_PCM3008
- select SND_SOC_PCM3168A_I2C if I2C
- select SND_SOC_PCM3168A_SPI if SPI_MASTER
- select SND_SOC_PCM512x_I2C if I2C
- select SND_SOC_PCM512x_SPI if SPI_MASTER
- select SND_SOC_RT286 if I2C
- select SND_SOC_RT298 if I2C
- select SND_SOC_RT5616 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_RT5659 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_STI_SAS
- select SND_SOC_TAS2552 if I2C
- select SND_SOC_TAS5086 if I2C
- select SND_SOC_TAS571X 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_WM8998 if MFD_WM8998
- select SND_SOC_WM9081 if I2C
- select SND_SOC_WM9090 if I2C
- select SND_SOC_WM9705 if SND_SOC_AC97_BUS
- select SND_SOC_WM9712 if SND_SOC_AC97_BUS
- select SND_SOC_WM9713 if SND_SOC_AC97_BUS
- help
- Normally ASoC codec drivers are only built if a machine driver which
- uses them is also built since they are only usable with a machine
- driver. Selecting this option will allow these drivers to be built
- without an explicit machine driver for test and development purposes.
+ imply SND_SOC_88PM860X
+ imply SND_SOC_AB8500_CODEC
+ imply SND_SOC_AC97_CODEC
+ imply SND_SOC_AD1836
+ imply SND_SOC_AD193X_SPI
+ imply SND_SOC_AD193X_I2C
+ imply SND_SOC_AD1980
+ imply SND_SOC_AD73311
+ imply SND_SOC_ADAU1372_I2C
+ imply SND_SOC_ADAU1372_SPI
+ imply SND_SOC_ADAU1373
+ imply SND_SOC_ADAU1761_I2C
+ imply SND_SOC_ADAU1761_SPI
+ imply SND_SOC_ADAU1781_I2C
+ imply SND_SOC_ADAU1781_SPI
+ imply SND_SOC_ADAV801
+ imply SND_SOC_ADAV803
+ imply SND_SOC_ADAU1977_SPI
+ imply SND_SOC_ADAU1977_I2C
+ imply SND_SOC_ADAU1701
+ imply SND_SOC_ADAU7002
+ imply SND_SOC_ADAU7118_I2C
+ imply SND_SOC_ADAU7118_HW
+ imply SND_SOC_ADS117X
+ imply SND_SOC_AK4104
+ imply SND_SOC_AK4118
+ imply SND_SOC_AK4375
+ imply SND_SOC_AK4458
+ imply SND_SOC_AK4535
+ imply SND_SOC_AK4554
+ imply SND_SOC_AK4613
+ imply SND_SOC_AK4619
+ imply SND_SOC_AK4641
+ imply SND_SOC_AK4642
+ imply SND_SOC_AK4671
+ imply SND_SOC_AK5386
+ imply SND_SOC_AK5558
+ imply SND_SOC_ALC5623
+ imply SND_SOC_ALC5632
+ imply SND_SOC_AUDIO_IIO_AUX
+ imply SND_SOC_AW8738
+ imply SND_SOC_AW87390
+ imply SND_SOC_AW88395
+ imply SND_SOC_AW88081
+ imply SND_SOC_AW88166
+ imply SND_SOC_AW88261
+ imply SND_SOC_AW88399
+ imply SND_SOC_BT_SCO
+ imply SND_SOC_BD28623
+ imply SND_SOC_CHV3_CODEC
+ imply SND_SOC_CQ0093VC
+ imply SND_SOC_CROS_EC_CODEC
+ imply SND_SOC_CS35L32
+ imply SND_SOC_CS35L33
+ imply SND_SOC_CS35L34
+ imply SND_SOC_CS35L35
+ imply SND_SOC_CS35L36
+ imply SND_SOC_CS35L41_SPI
+ imply SND_SOC_CS35L41_I2C
+ imply SND_SOC_CS35L45_I2C
+ imply SND_SOC_CS35L45_SPI
+ imply SND_SOC_CS35L56_I2C
+ imply SND_SOC_CS35L56_SPI
+ imply SND_SOC_CS35L56_SDW
+ imply SND_SOC_CS40L50
+ imply SND_SOC_CS42L42
+ imply SND_SOC_CS42L42_SDW
+ imply SND_SOC_CS42L43
+ imply SND_SOC_CS42L43_SDW
+ imply SND_SOC_CS42L51_I2C
+ imply SND_SOC_CS42L52
+ imply SND_SOC_CS42L56
+ imply SND_SOC_CS42L73
+ imply SND_SOC_CS42L84
+ imply SND_SOC_CS4234
+ imply SND_SOC_CS4265
+ imply SND_SOC_CS4270
+ imply SND_SOC_CS4271_I2C
+ imply SND_SOC_CS4271_SPI
+ imply SND_SOC_CS42XX8_I2C
+ imply SND_SOC_CS43130
+ imply SND_SOC_CS4341
+ imply SND_SOC_CS4349
+ imply SND_SOC_CS47L15
+ imply SND_SOC_CS47L24
+ imply SND_SOC_CS47L35
+ imply SND_SOC_CS47L85
+ imply SND_SOC_CS47L90
+ imply SND_SOC_CS47L92
+ imply SND_SOC_CS48L32
+ imply SND_SOC_CS53L30
+ imply SND_SOC_CS530X_I2C
+ imply SND_SOC_CS530X_SPI
+ imply SND_SOC_CX20442
+ imply SND_SOC_CX2072X
+ imply SND_SOC_DA7210
+ imply SND_SOC_DA7213
+ imply SND_SOC_DA7218
+ imply SND_SOC_DA7219
+ imply SND_SOC_DA732X
+ imply SND_SOC_DA9055
+ imply SND_SOC_DMIC
+ imply SND_SOC_ES8316
+ imply SND_SOC_ES8323
+ imply SND_SOC_ES8326
+ imply SND_SOC_ES8328_SPI
+ imply SND_SOC_ES8328_I2C
+ imply SND_SOC_ES8375
+ imply SND_SOC_ES8389
+ imply SND_SOC_ES7134
+ imply SND_SOC_ES7241
+ imply SND_SOC_FRAMER
+ imply SND_SOC_FS210X
+ imply SND_SOC_GTM601
+ imply SND_SOC_HDAC_HDMI
+ imply SND_SOC_HDAC_HDA
+ imply SND_SOC_ICS43432
+ imply SND_SOC_IDT821034
+ imply SND_SOC_INNO_RK3036
+ imply SND_SOC_ISABELLE
+ imply SND_SOC_JZ4740_CODEC
+ imply SND_SOC_JZ4725B_CODEC
+ imply SND_SOC_JZ4760_CODEC
+ imply SND_SOC_JZ4770_CODEC
+ imply SND_SOC_LM4857
+ imply SND_SOC_LM49453
+ imply SND_SOC_LOCHNAGAR_SC
+ imply SND_SOC_MAX98088
+ imply SND_SOC_MAX98090
+ imply SND_SOC_MAX98095
+ imply SND_SOC_MAX98357A
+ imply SND_SOC_MAX98371
+ imply SND_SOC_MAX98504
+ imply SND_SOC_MAX98520
+ imply SND_SOC_MAX9867
+ imply SND_SOC_MAX98925
+ imply SND_SOC_MAX98926
+ imply SND_SOC_MAX98927
+ imply SND_SOC_MAX98363
+ imply SND_SOC_MAX98373_I2C
+ imply SND_SOC_MAX98373_SDW
+ imply SND_SOC_MAX98388
+ imply SND_SOC_MAX98390
+ imply SND_SOC_MAX98396
+ imply SND_SOC_MAX9850
+ imply SND_SOC_MAX9860
+ imply SND_SOC_MAX9759
+ imply SND_SOC_MAX9768
+ imply SND_SOC_MAX9877
+ imply SND_SOC_MC13783
+ imply SND_SOC_ML26124
+ imply SND_SOC_MT6351
+ imply SND_SOC_MT6357
+ imply SND_SOC_MT6358
+ imply SND_SOC_MT6359
+ imply SND_SOC_MT6660
+ imply SND_SOC_NAU8315
+ imply SND_SOC_NAU8325
+ imply SND_SOC_NAU8540
+ imply SND_SOC_NAU8810
+ imply SND_SOC_NAU8821
+ imply SND_SOC_NAU8822
+ imply SND_SOC_NAU8824
+ imply SND_SOC_NAU8825
+ imply SND_SOC_HDMI_CODEC
+ imply SND_SOC_PCM1681
+ imply SND_SOC_PCM1754
+ imply SND_SOC_PCM1789_I2C
+ imply SND_SOC_PCM179X_I2C
+ imply SND_SOC_PCM179X_SPI
+ imply SND_SOC_PCM186X_I2C
+ imply SND_SOC_PCM186X_SPI
+ imply SND_SOC_PCM3008
+ imply SND_SOC_PCM3060_I2C
+ imply SND_SOC_PCM3060_SPI
+ imply SND_SOC_PCM3168A_I2C
+ imply SND_SOC_PCM3168A_SPI
+ imply SND_SOC_PCM5102A
+ imply SND_SOC_PCM512x_I2C
+ imply SND_SOC_PCM512x_SPI
+ imply SND_SOC_PCM6240
+ imply SND_SOC_PEB2466
+ imply SND_SOC_PM4125_SDW
+ imply SND_SOC_RK3308
+ imply SND_SOC_RK3328
+ imply SND_SOC_RK817
+ imply SND_SOC_RT274
+ imply SND_SOC_RT286
+ imply SND_SOC_RT298
+ imply SND_SOC_RT1011
+ imply SND_SOC_RT1015
+ imply SND_SOC_RT1015P
+ imply SND_SOC_RT1016
+ imply SND_SOC_RT1017_SDCA_SDW
+ imply SND_SOC_RT1019
+ imply SND_SOC_RT1305
+ imply SND_SOC_RT1308
+ imply SND_SOC_RT5514
+ imply SND_SOC_RT5616
+ imply SND_SOC_RT5631
+ imply SND_SOC_RT5640
+ imply SND_SOC_RT5645
+ imply SND_SOC_RT5651
+ imply SND_SOC_RT5659
+ imply SND_SOC_RT5660
+ imply SND_SOC_RT5663
+ imply SND_SOC_RT5665
+ imply SND_SOC_RT5668
+ imply SND_SOC_RT5670
+ imply SND_SOC_RT5677
+ imply SND_SOC_RT5682_I2C
+ imply SND_SOC_RT5682_SDW
+ imply SND_SOC_RT5682S
+ imply SND_SOC_RT700_SDW
+ imply SND_SOC_RT711_SDW
+ imply SND_SOC_RT711_SDCA_SDW
+ imply SND_SOC_RT712_SDCA_SDW
+ imply SND_SOC_RT712_SDCA_DMIC_SDW
+ imply SND_SOC_RT715_SDW
+ imply SND_SOC_RT715_SDCA_SDW
+ imply SND_SOC_RT721_SDCA_SDW
+ imply SND_SOC_RT722_SDCA_SDW
+ imply SND_SOC_RT1308_SDW
+ imply SND_SOC_RT1316_SDW
+ imply SND_SOC_RT1318
+ imply SND_SOC_RT1318_SDW
+ imply SND_SOC_RT1320_SDW
+ imply SND_SOC_RT9120
+ imply SND_SOC_RT9123
+ imply SND_SOC_RTQ9124
+ imply SND_SOC_RTQ9128
+ imply SND_SOC_SDW_MOCKUP
+ imply SND_SOC_SGTL5000
+ imply SND_SOC_SI476X
+ imply SND_SOC_SIMPLE_AMPLIFIER
+ imply SND_SOC_SIMPLE_MUX
+ imply SND_SOC_SMA1303
+ imply SND_SOC_SMA1307
+ imply SND_SOC_SPDIF
+ imply SND_SOC_SRC4XXX_I2C
+ imply SND_SOC_SSM2305
+ imply SND_SOC_SSM2518
+ imply SND_SOC_SSM2602_SPI
+ imply SND_SOC_SSM2602_I2C
+ imply SND_SOC_SSM4567
+ imply SND_SOC_STA32X
+ imply SND_SOC_STA350
+ imply SND_SOC_STA529
+ imply SND_SOC_STAC9766
+ imply SND_SOC_STI_SAS
+ imply SND_SOC_TAS2552
+ imply SND_SOC_TAS2562
+ imply SND_SOC_TAS2764
+ imply SND_SOC_TAS2770
+ imply SND_SOC_TAS2780
+ imply SND_SOC_TAS2781_I2C
+ imply SND_SOC_TAS2783_SDW
+ imply SND_SOC_TAS5086
+ imply SND_SOC_TAS571X
+ imply SND_SOC_TAS5720
+ imply SND_SOC_TAS6424
+ imply SND_SOC_TDA7419
+ imply SND_SOC_TFA9879
+ imply SND_SOC_TFA989X
+ imply SND_SOC_TLV320ADC3XXX
+ imply SND_SOC_TLV320ADCX140
+ imply SND_SOC_TLV320AIC23_I2C
+ imply SND_SOC_TLV320AIC23_SPI
+ imply SND_SOC_TLV320AIC26
+ imply SND_SOC_TLV320AIC31XX
+ imply SND_SOC_TLV320AIC32X4_I2C
+ imply SND_SOC_TLV320AIC32X4_SPI
+ imply SND_SOC_TLV320AIC3X_I2C
+ imply SND_SOC_TLV320AIC3X_SPI
+ imply SND_SOC_TPA6130A2
+ imply SND_SOC_TLV320DAC33
+ imply SND_SOC_TSCS42XX
+ imply SND_SOC_TSCS454
+ imply SND_SOC_TS3A227E
+ imply SND_SOC_TWL4030
+ imply SND_SOC_TWL6040
+ imply SND_SOC_UDA1334
+ imply SND_SOC_UDA1342
+ imply SND_SOC_UDA1380
+ imply SND_SOC_WCD9335
+ imply SND_SOC_WCD934X
+ imply SND_SOC_WCD937X_SDW
+ imply SND_SOC_WCD938X_SDW
+ imply SND_SOC_WCD939X_SDW
+ imply SND_SOC_LPASS_MACRO_COMMON
+ imply SND_SOC_LPASS_RX_MACRO
+ imply SND_SOC_LPASS_TX_MACRO
+ imply SND_SOC_WM0010
+ imply SND_SOC_WM1250_EV1
+ imply SND_SOC_WM2000
+ imply SND_SOC_WM2200
+ imply SND_SOC_WM5100
+ imply SND_SOC_WM5102
+ imply SND_SOC_WM5110
+ imply SND_SOC_WM8350
+ imply SND_SOC_WM8400
+ imply SND_SOC_WM8510
+ imply SND_SOC_WM8523
+ imply SND_SOC_WM8524
+ imply SND_SOC_WM8580
+ imply SND_SOC_WM8711
+ imply SND_SOC_WM8727
+ imply SND_SOC_WM8728
+ imply SND_SOC_WM8731_I2C
+ imply SND_SOC_WM8731_SPI
+ imply SND_SOC_WM8737
+ imply SND_SOC_WM8741
+ imply SND_SOC_WM8750
+ imply SND_SOC_WM8753
+ imply SND_SOC_WM8770
+ imply SND_SOC_WM8776
+ imply SND_SOC_WM8782
+ imply SND_SOC_WM8804_I2C
+ imply SND_SOC_WM8804_SPI
+ imply SND_SOC_WM8900
+ imply SND_SOC_WM8903
+ imply SND_SOC_WM8904
+ imply SND_SOC_WM8940
+ imply SND_SOC_WM8955
+ imply SND_SOC_WM8960
+ imply SND_SOC_WM8961
+ imply SND_SOC_WM8962
+ imply SND_SOC_WM8971
+ imply SND_SOC_WM8974
+ imply SND_SOC_WM8978
+ imply SND_SOC_WM8983
+ imply SND_SOC_WM8985
+ imply SND_SOC_WM8988
+ imply SND_SOC_WM8990
+ imply SND_SOC_WM8991
+ imply SND_SOC_WM8993
+ imply SND_SOC_WM8994
+ imply SND_SOC_WM8995
+ imply SND_SOC_WM8996
+ imply SND_SOC_WM8997
+ imply SND_SOC_WM8998
+ imply SND_SOC_WM9081
+ imply SND_SOC_WM9090
+ imply SND_SOC_WM9705
+ imply SND_SOC_WM9712
+ imply SND_SOC_WM9713
+ imply SND_SOC_WSA881X
+ imply SND_SOC_WSA883X
+ imply SND_SOC_WSA884X
+ imply SND_SOC_ZL38060
+ help
+ Normally ASoC codec drivers are only built if a machine driver which
+ uses them is also built since they are only usable with a machine
+ driver. Selecting this option will allow these drivers to be built
+ without an explicit machine driver for test and development purposes.
Support for the bus types used to access the codecs to be built must
be selected separately.
- If unsure select "N".
+ If unsure select "N".
config SND_SOC_88PM860X
tristate
+ depends on MFD_88PM860X
config SND_SOC_ARIZONA
tristate
@@ -221,18 +399,34 @@ config SND_SOC_WM_HUBS
config SND_SOC_WM_ADSP
tristate
+ select FW_CS_DSP
select SND_SOC_COMPRESS
+ default y if SND_SOC_MADERA=y
default y if SND_SOC_CS47L24=y
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM2200=y
+ default y if SND_SOC_CS35L41_SPI=y
+ default y if SND_SOC_CS35L41_I2C=y
+ default y if SND_SOC_CS35L45_SPI=y
+ default y if SND_SOC_CS35L45_I2C=y
+ default y if SND_SOC_CS35L56=y
+ default y if SND_SOC_CS48L32=y
+ default m if SND_SOC_MADERA=m
default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM2200=m
+ default m if SND_SOC_CS35L41_SPI=m
+ default m if SND_SOC_CS35L41_I2C=m
+ default m if SND_SOC_CS35L45_SPI=m
+ default m if SND_SOC_CS35L45_I2C=m
+ default m if SND_SOC_CS35L56=m
+ default m if SND_SOC_CS48L32=m
config SND_SOC_AB8500_CODEC
tristate
+ depends on ABX500_CORE
config SND_SOC_AC97_CODEC
tristate "Build generic ASoC AC97 CODEC driver"
@@ -241,27 +435,52 @@ config SND_SOC_AC97_CODEC
config SND_SOC_AD1836
tristate
+ depends on SPI_MASTER
config SND_SOC_AD193X
tristate
config SND_SOC_AD193X_SPI
tristate
+ depends on SPI_MASTER
select SND_SOC_AD193X
config SND_SOC_AD193X_I2C
tristate
+ depends on I2C
select SND_SOC_AD193X
config SND_SOC_AD1980
- select REGMAP_AC97
tristate
+ depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_AD73311
tristate
-config SND_SOC_ADAU1373
+config SND_SOC_ADAU_UTILS
+ tristate
+
+config SND_SOC_ADAU1372
tristate
+ select SND_SOC_ADAU_UTILS
+
+config SND_SOC_ADAU1372_I2C
+ tristate "Analog Devices ADAU1372 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_ADAU1372
+ select REGMAP_I2C
+
+config SND_SOC_ADAU1372_SPI
+ tristate "Analog Devices ADAU1372 CODEC (SPI)"
+ depends on SPI
+ select SND_SOC_ADAU1372
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1373
+ tristate "Analog Devices ADAU1373 CODEC"
+ depends on I2C
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
@@ -271,18 +490,21 @@ config SND_SOC_ADAU1701
config SND_SOC_ADAU17X1
tristate
select SND_SOC_SIGMADSP_REGMAP
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1761
tristate
select SND_SOC_ADAU17X1
config SND_SOC_ADAU1761_I2C
- tristate
+ tristate "Analog Devices AU1761 CODEC - I2C"
+ depends on I2C
select SND_SOC_ADAU1761
select REGMAP_I2C
config SND_SOC_ADAU1761_SPI
- tristate
+ tristate "Analog Devices AU1761 CODEC - SPI"
+ depends on SPI
select SND_SOC_ADAU1761
select REGMAP_SPI
@@ -292,11 +514,13 @@ config SND_SOC_ADAU1781
config SND_SOC_ADAU1781_I2C
tristate
+ depends on I2C
select SND_SOC_ADAU1781
select REGMAP_I2C
config SND_SOC_ADAU1781_SPI
tristate
+ depends on SPI_MASTER
select SND_SOC_ADAU1781
select REGMAP_SPI
@@ -305,23 +529,57 @@ config SND_SOC_ADAU1977
config SND_SOC_ADAU1977_SPI
tristate
+ depends on SPI_MASTER
select SND_SOC_ADAU1977
select REGMAP_SPI
config SND_SOC_ADAU1977_I2C
tristate
+ depends on I2C
select SND_SOC_ADAU1977
select REGMAP_I2C
+config SND_SOC_ADAU7002
+ tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
+
+config SND_SOC_ADAU7118
+ tristate
+
+config SND_SOC_ADAU7118_HW
+ tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - HW Mode"
+ select SND_SOC_ADAU7118
+ help
+ Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
+ Converter. In this mode, the device works in standalone mode which
+ means that there is no bus to communicate with it. Stereo mode is not
+ supported in this mode.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-adau7118-hw.
+
+config SND_SOC_ADAU7118_I2C
+ tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - I2C"
+ depends on I2C
+ select SND_SOC_ADAU7118
+ select REGMAP_I2C
+ help
+ Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
+ Converter over I2C. This gives full support over the device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-adau7118-i2c.
+
config SND_SOC_ADAV80X
tristate
config SND_SOC_ADAV801
tristate
+ depends on SPI_MASTER
select SND_SOC_ADAV80X
config SND_SOC_ADAV803
tristate
+ depends on I2C
select SND_SOC_ADAV80X
config SND_SOC_ADS117X
@@ -331,8 +589,29 @@ config SND_SOC_AK4104
tristate "AKM AK4104 CODEC"
depends on SPI_MASTER
+config SND_SOC_AK4118
+ tristate "AKM AK4118 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_AK4375
+ tristate "AKM AK4375 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the Asahi-Kasei AK4375 codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-ak4375.
+
+config SND_SOC_AK4458
+ tristate "AKM AK4458 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
config SND_SOC_AK4535
tristate
+ depends on I2C
config SND_SOC_AK4554
tristate "AKM AK4554 CODEC"
@@ -341,8 +620,14 @@ config SND_SOC_AK4613
tristate "AKM AK4613 CODEC"
depends on I2C
+config SND_SOC_AK4619
+ tristate "AKM AK4619 CODEC"
+ depends on I2C
+
config SND_SOC_AK4641
tristate
+ depends on I2C
+ depends on GPIOLIB_LEGACY
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
@@ -350,24 +635,342 @@ config SND_SOC_AK4642
config SND_SOC_AK4671
tristate
+ depends on I2C
config SND_SOC_AK5386
tristate "AKM AK5638 CODEC"
+config SND_SOC_AK5558
+ tristate "AKM AK5558 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
config SND_SOC_ALC5623
- tristate "Realtek ALC5623 CODEC"
+ tristate "Realtek ALC5623 CODEC"
depends on I2C
config SND_SOC_ALC5632
tristate
+ depends on I2C
+
+config SND_SOC_AUDIO_IIO_AUX
+ tristate "Audio IIO Auxiliary device"
+ depends on IIO
+ help
+ Enable support for Industrial I/O devices as audio auxiliary devices.
+ This allows to have an IIO device present in the audio path and
+ controlled using mixer controls.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-audio-iio-aux.
+
+config SND_SOC_AW8738
+ tristate "Awinic AW8738 Audio Amplifier"
+ select GPIOLIB
+ help
+ Enable support for the Awinic AW8738 audio amplifier (or similar).
+ The driver supports simple audio amplifiers similar to
+ SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the
+ operation mode using the Awinic-specific one-wire pulse control.
+
+config SND_SOC_AW88395_LIB
+ select CRC8
+ tristate
+
+config SND_SOC_AW88395
+ tristate "Soc Audio for awinic aw88395"
+ depends on I2C
+ select CRC32
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ this option enables support for aw88395 Smart PA.
+ The Awinic AW88395 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with an integrated 10V
+ smart boost convert.
+
+config SND_SOC_AW88166
+ tristate "Soc Audio for awinic aw88166"
+ depends on I2C
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88166 Smart PA.
+ The awinic AW88166 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with sound quality
+ enhancement algorithms and speaker protection.
+
+config SND_SOC_AW88261
+ tristate "Soc Audio for awinic aw88261"
+ depends on I2C
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88261 Smart PA.
+ The awinic AW88261 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier. The output voltage of
+ boost converter can be adjusted smartly according to
+ the input amplitude.
+
+config SND_SOC_AW88081
+ tristate "Soc Audio for awinic aw88081/aw88083"
+ depends on I2C
+ select REGMAP_I2C
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88081 Smart PA.
+ The awinic AW88081 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier. Due to its 9uV noise
+ floor and ultra-low distortion, clean listening is guaranteed.
+
+config SND_SOC_AW87390
+ tristate "Soc Audio for awinic aw87390"
+ depends on I2C
+ select REGMAP_I2C
+ select SND_SOC_AW88395_LIB
+ help
+ The awinic aw87390 is specifically designed to improve
+ the musical output dynamic range, enhance the overall
+ sound quality, which is a new high efficiency, low
+ noise, constant large volume, 6th Smart K audio amplifier.
+
+config SND_SOC_AW88399
+ tristate "Soc Audio for awinic aw88399"
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88399 Smart PA.
+ The awinic AW88399 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier and SKTune speaker
+ protection algorithms.
+
+config SND_SOC_BD28623
+ tristate "ROHM BD28623 CODEC"
+ help
+ Enable support for ROHM BD28623MUV Class D speaker amplifier.
+ This codec does not have any control buses such as I2C, it
+ detect format of I2S automatically.
+
+config SND_SOC_BT_SCO
+ tristate "Dummy BT SCO codec driver"
+
+config SND_SOC_CHV3_CODEC
+ tristate "Google Chameleon v3 codec driver"
+ help
+ Enable support for the Google Chameleon v3 audio codec.
+ This codec does not have a control interface, it always outputs
+ 8 channel S32_LE audio.
+
+config SND_SOC_CPCAP
+ tristate "Motorola CPCAP codec"
+ depends on MFD_CPCAP || COMPILE_TEST
config SND_SOC_CQ0093VC
tristate
+config SND_SOC_CROS_EC_CODEC
+ tristate "codec driver for ChromeOS EC"
+ depends on CROS_EC
+ select CRYPTO
+ select CRYPTO_LIB_SHA256
+ help
+ If you say yes here you will get support for the
+ ChromeOS Embedded Controller's Audio Codec.
+
+config SND_SOC_CS_AMP_LIB
+ tristate
+
+config SND_SOC_CS_AMP_LIB_TEST
+ tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS_AMP_LIB && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the Cirrus Logic common
+ amplifier library.
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
config SND_SOC_CS35L32
tristate "Cirrus Logic CS35L32 CODEC"
depends on I2C
+config SND_SOC_CS35L33
+ tristate "Cirrus Logic CS35L33 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L34
+ tristate "Cirrus Logic CS35L34 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L35
+ tristate "Cirrus Logic CS35L35 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L36
+ tristate "Cirrus Logic CS35L36 CODEC"
+ depends on I2C
+
+config SND_SOC_CS35L41_LIB
+ tristate
+
+config SND_SOC_CS35L41
+ tristate
+
+config SND_SOC_CS35L41_SPI
+ tristate "Cirrus Logic CS35L41 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_CS35L41_LIB
+ select SND_SOC_CS35L41
+ select REGMAP_SPI
+
+config SND_SOC_CS35L41_I2C
+ tristate "Cirrus Logic CS35L41 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS35L41_LIB
+ select SND_SOC_CS35L41
+ select REGMAP_I2C
+
+config SND_SOC_CS35L45
+ tristate
+ select REGMAP_IRQ
+
+config SND_SOC_CS35L45_SPI
+ tristate "Cirrus Logic CS35L45 CODEC (SPI)"
+ depends on SPI_MASTER
+ select REGMAP
+ select REGMAP_SPI
+ select SND_SOC_CS35L45
+ help
+ Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+ with SPI control.
+
+config SND_SOC_CS35L45_I2C
+ tristate "Cirrus Logic CS35L45 CODEC (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS35L45
+ help
+ Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+ with I2C control.
+
+config SND_SOC_CS35L56
+ tristate
+
+config SND_SOC_CS35L56_SHARED
+ select SND_SOC_CS_AMP_LIB
+ tristate
+
+config SND_SOC_CS35L56_I2C
+ tristate "Cirrus Logic CS35L56 CODEC (I2C)"
+ depends on I2C
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_I2C
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with I2C control
+
+config SND_SOC_CS35L56_SPI
+ tristate "Cirrus Logic CS35L56 CODEC (SPI)"
+ depends on SPI_MASTER
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_SPI
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with SPI control
+
+config SND_SOC_CS35L56_SDW
+ tristate "Cirrus Logic CS35L56 CODEC (SDW)"
+ depends on SOUNDWIRE
+ select REGMAP
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with SoundWire control
+
+config SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
+ bool
+
+menu "CS35L56 driver options"
+ depends on SND_SOC_CS35L56
+
+config SND_SOC_CS35L56_CAL_DEBUGFS
+ bool "CS35L56 create debugfs for factory calibration"
+ default N
+ depends on DEBUG_FS
+ select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
+ help
+ Create debugfs entries used during factory-line manufacture
+ for factory calibration.
+
+ If unsure select "N".
+
+config SND_SOC_CS35L56_CAL_SET_CTRL
+ bool "CS35L56 ALSA control to restore factory calibration"
+ default N
+ select SND_SOC_CS35L56_CAL_SYSFS_COMMON
+ help
+ Allow restoring factory calibration data through an ALSA
+ control. This is only needed on platforms without UEFI or
+ some other method of non-volatile storage that the driver
+ can access directly.
+
+ On most platforms this is not needed.
+
+ If unsure select "N".
+endmenu
+
+config SND_SOC_CS40L50
+ tristate "Cirrus Logic CS40L50 CODEC"
+ depends on MFD_CS40L50_CORE
+ help
+ This option enables support for I2S streaming to Cirrus Logic CS40L50.
+
+ CS40L50 is a haptic driver with waveform memory, an integrated
+ DSP, and closed-loop algorithms. If built as a module, it will be
+ called snd-soc-cs40l50.
+
+config SND_SOC_CS42L42_CORE
+ tristate
+
+config SND_SOC_CS42L42
+ tristate "Cirrus Logic CS42L42 CODEC (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS42L42_CORE
+
+config SND_SOC_CS42L42_SDW
+ tristate "Cirrus Logic CS42L42 CODEC on Soundwire"
+ depends on SOUNDWIRE
+ select SND_SOC_CS42L42_CORE
+ help
+ Enable support for Cirrus Logic CS42L42 codec with Soundwire control
+
+config SND_SOC_CS42L43
+ tristate "Cirrus Logic CS42L43 CODEC"
+ depends on MFD_CS42L43
+ help
+ Select this to support the audio functions of the Cirrus Logic
+ CS42L43 PC CODEC.
+
+config SND_SOC_CS42L43_SDW
+ tristate "Cirrus Logic CS42L43 CODEC (SoundWire)"
+ depends on SND_SOC_CS42L43 && MFD_CS42L43_SDW
+ help
+ Select this to support the audio functions of the Cirrus Logic
+ CS42L43 PC CODEC over SoundWire.
+
config SND_SOC_CS42L51
tristate
@@ -388,6 +991,24 @@ config SND_SOC_CS42L73
tristate "Cirrus Logic CS42L73 CODEC"
depends on I2C
+config SND_SOC_CS42L83
+ tristate "Cirrus Logic CS42L83 CODEC"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS42L42_CORE
+
+config SND_SOC_CS42L84
+ tristate "Cirrus Logic CS42L84 CODEC"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+
+config SND_SOC_CS4234
+ tristate "Cirrus Logic CS4234 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
config SND_SOC_CS4265
tristate "Cirrus Logic CS4265 CODEC"
depends on I2C
@@ -430,108 +1051,509 @@ config SND_SOC_CS42XX8_I2C
select SND_SOC_CS42XX8
select REGMAP_I2C
+# Cirrus Logic CS43130 HiFi DAC
+config SND_SOC_CS43130
+ tristate "Cirrus Logic CS43130 CODEC"
+ depends on I2C
+
+config SND_SOC_CS4341
+ tristate "Cirrus Logic CS4341 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+
# Cirrus Logic CS4349 HiFi DAC
config SND_SOC_CS4349
tristate "Cirrus Logic CS4349 CODEC"
depends on I2C
+config SND_SOC_CS47L15
+ tristate
+ depends on MFD_CS47L15
+
config SND_SOC_CS47L24
tristate
+ depends on MFD_CS47L24 && MFD_ARIZONA
+
+config SND_SOC_CS47L35
+ tristate
+ depends on MFD_CS47L35
+
+config SND_SOC_CS47L85
+ tristate
+ depends on MFD_CS47L85
+
+config SND_SOC_CS47L90
+ tristate
+ depends on MFD_CS47L90
+
+config SND_SOC_CS47L92
+ tristate
+ depends on MFD_CS47L92
+
+config SND_SOC_CS48L32
+ tristate "Cirrus Logic CS48L32 audio DSP"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ help
+ Build the codec driver for the Cirrus Logic CS48L32 audio DSP.
+
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+ tristate "Cirrus Logic CS53L30 CODEC"
+ depends on I2C
+
+config SND_SOC_CS530X
+ tristate
+
+config SND_SOC_CS530X_I2C
+ tristate "Cirrus Logic CS530x ADCs (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS530X
+ help
+ Enable support for Cirrus Logic CS530X ADCs
+ with I2C control.
+
+config SND_SOC_CS530X_SPI
+ tristate "Cirrus Logic CS530x ADCs (SPI)"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ select SND_SOC_CS530X
+ help
+ Enable support for Cirrus Logic CS530X ADCs
+ with SPI control.
config SND_SOC_CX20442
tristate
depends on TTY
+config SND_SOC_CX2072X
+ tristate "Conexant CX2072X CODEC"
+ depends on I2C
+ help
+ Enable support for Conexant CX20721 and CX20723 codec chips.
+
config SND_SOC_JZ4740_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
select REGMAP_MMIO
- tristate
+ tristate "Ingenic JZ4740 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4740 SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4740-codec.
+
+config SND_SOC_JZ4725B_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4725B internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4725B SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4725b-codec.
+
+config SND_SOC_JZ4760_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4760 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4760 SoC
+ from Ingenic.
-config SND_SOC_L3
- tristate
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4760-codec.
+
+config SND_SOC_JZ4770_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4770 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4770 SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4770-codec.
config SND_SOC_DA7210
- tristate
+ tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_DA7213
- tristate
+ tristate "Dialog DA7213 CODEC"
+ depends on I2C
config SND_SOC_DA7218
tristate
+ depends on I2C
config SND_SOC_DA7219
- tristate
+ tristate
+ depends on I2C
config SND_SOC_DA732X
- tristate
+ tristate
+ depends on I2C
config SND_SOC_DA9055
tristate
+ depends on I2C
-config SND_SOC_BT_SCO
+config SND_SOC_DMIC
+ tristate "Generic Digital Microphone CODEC"
+ help
+ Enable support for the Generic Digital Microphone CODEC.
+ Select this if your sound card has DMICs.
+
+config SND_SOC_HDMI_CODEC
tristate
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ select HDMI
-config SND_SOC_DMIC
+config SND_SOC_ES7134
+ tristate "Everest Semi ES7134 CODEC"
+
+config SND_SOC_ES7241
+ tristate "Everest Semi ES7241 CODEC"
+
+config SND_SOC_ES83XX_DSM_COMMON
+ depends on ACPI
tristate
+config SND_SOC_ES8311
+ tristate "Everest Semi ES8311 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8316
+ tristate "Everest Semi ES8316 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8323
+ tristate "Everest Semi ES8323 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8326
+ tristate "Everest Semi ES8326 CODEC"
+ depends on I2C
+
config SND_SOC_ES8328
- tristate "Everest Semi ES8328 CODEC"
+ tristate
config SND_SOC_ES8328_I2C
- tristate
+ tristate "Everest Semi ES8328 CODEC (I2C)"
+ depends on I2C
select SND_SOC_ES8328
config SND_SOC_ES8328_SPI
- tristate
+ tristate "Everest Semi ES8328 CODEC (SPI)"
+ depends on SPI_MASTER
select SND_SOC_ES8328
+config SND_SOC_ES8375
+ tristate "Everest Semi ES8375 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8389
+ tristate "Everest Semi ES8389 CODEC"
+ depends on I2C
+
+config SND_SOC_FRAMER
+ tristate "Framer codec"
+ depends on GENERIC_FRAMER
+ help
+ Enable support for the framer codec.
+ The framer codec uses the generic framer infrastructure to transport
+ some audio data over an analog E1/T1/J1 line.
+ This codec allows to use some of the time slots available on the TDM
+ bus on which the framer is connected to transport the audio data.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-framer.
+
+config SND_SOC_FS_AMP_LIB
+ select CRC16
+ tristate
+
+config SND_SOC_FS210X
+ tristate 'FourSemi FS2104/5S digital audio amplifier'
+ depends on I2C
+ select GPIOLIB
+ select REGMAP_I2C
+ select SND_SOC_FS_AMP_LIB
+ help
+ Enable support for FourSemi FS2104/5S digital audio amplifier.
+ The FS2104/5S are Inductor-Less, Stereo, Closed-Loop,
+ Digital Input Class-D Power Amplifiers with Enhanced Signal Processing.
+ The amplifiers support I2C and I2S/TDM.
+
config SND_SOC_GTM601
tristate 'GTM601 UMTS modem audio codec'
config SND_SOC_HDAC_HDMI
tristate
select SND_HDA_EXT_CORE
+ select SND_PCM_ELD
select HDMI
-config SND_SOC_ICS43432
+config SND_SOC_HDAC_HDA
tristate
+ select SND_HDA
+
+config SND_SOC_HDA
+ tristate "HD-Audio codec driver"
+ select SND_HDA_EXT_CORE
+ select SND_HDA
+ help
+ This enables HD-Audio codec support in ASoC subsystem. Compared
+ to SND_SOC_HDAC_HDA, driver's behavior is identical to HD-Audio
+ legacy solution - including the dynamic resource allocation
+ based on actual codec capabilities.
+
+config SND_SOC_ICS43432
+ tristate "ICS43423 and compatible i2s microphones"
+
+config SND_SOC_IDT821034
+ tristate "Renesas IDT821034 quad PCM codec"
+ depends on SPI
+ help
+ Enable support for the Renesas IDT821034 quad PCM with
+ programmable gain codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-idt821034.
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
config SND_SOC_ISABELLE
- tristate
+ tristate
+ depends on I2C
config SND_SOC_LM49453
tristate
+ depends on I2C
+
+config SND_SOC_LOCHNAGAR_SC
+ tristate "Lochnagar Sound Card"
+ depends on MFD_LOCHNAGAR || COMPILE_TEST
+ help
+ This driver support the sound card functionality of the Cirrus
+ Logic Lochnagar audio development board.
+
+config SND_SOC_MADERA
+ tristate
+ default y if SND_SOC_CS47L15=y
+ default y if SND_SOC_CS47L35=y
+ default y if SND_SOC_CS47L85=y
+ default y if SND_SOC_CS47L90=y
+ default y if SND_SOC_CS47L92=y
+ default m if SND_SOC_CS47L15=m
+ default m if SND_SOC_CS47L35=m
+ default m if SND_SOC_CS47L85=m
+ default m if SND_SOC_CS47L90=m
+ default m if SND_SOC_CS47L92=m
config SND_SOC_MAX98088
- tristate
+ tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
+ depends on I2C
config SND_SOC_MAX98090
- tristate
+ tristate "Maxim MAX98090 CODEC"
+ depends on I2C
config SND_SOC_MAX98095
- tristate
+ tristate
+ depends on I2C
config SND_SOC_MAX98357A
- tristate
+ tristate "Maxim MAX98357A CODEC"
+
+config SND_SOC_MAX98371
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98504
+ tristate "Maxim MAX98504 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_MAX9867
+ tristate "Maxim MAX9867 CODEC"
+ depends on I2C
config SND_SOC_MAX98925
- tristate
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98926
+ tristate
+ depends on I2C
+
+config SND_SOC_MAX98927
+ tristate "Maxim Integrated MAX98927 Speaker Amplifier"
+ depends on I2C
+
+config SND_SOC_MAX98520
+ tristate "Maxim Integrated MAX98520 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Maxim Integrated MAX98520 audio
+ amplifier, which implements a tripler charge pump
+ based boost converter and supports sample rates of
+ 8KHz to 192KHz.
+
+ To compile this driver as a module, choose M here.
+
+config SND_SOC_MAX98363
+ tristate "Analog Devices MAX98363 Soundwire Speaker Amplifier"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ Enable support for Analog Devices MAX98363 Soundwire
+ amplifier. MAX98363 supports the MIPI SoundWire v1.2
+ compatible interface for audio and control data.
+ This amplifier does not support I2C and I2S.
+
+config SND_SOC_MAX98373
+ tristate
+
+config SND_SOC_MAX98373_I2C
+ tristate "Maxim Integrated MAX98373 Speaker Amplifier"
+ depends on I2C
+ select SND_SOC_MAX98373
+
+config SND_SOC_MAX98373_SDW
+ tristate "Maxim Integrated MAX98373 Speaker Amplifier - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_MAX98373
+ select REGMAP_SOUNDWIRE
+ help
+ Enable support for Maxim Integrated MAX98373 Soundwire
+ amplifier. MAX98373 supports either the MIPI SoundWire
+ compatible interface for audio and control data, or
+ the PCM interface for audio data and a standard I2C
+ interface for control data. Select this if MAX98373 is
+ connected via soundwire.
+
+config SND_SOC_MAX98388
+ tristate "Analog Devices MAX98388 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Analog Devices MAX98388 audio
+ amplifier. The device provides a PCM interface for
+ audio data and a standard I2C interface for control
+ data communication.
+
+config SND_SOC_MAX98390
+ tristate "Maxim Integrated MAX98390 Speaker Amplifier"
+ depends on I2C
+
+config SND_SOC_MAX98396
+ tristate "Analog Devices MAX98396 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Analog Devices MAX98396 audio
+ amplifier. The device provides a PCM interface for
+ audio data and a standard I2C interface for control
+ data communication.
config SND_SOC_MAX9850
tristate
+ depends on I2C
+
+config SND_SOC_MAX9860
+ tristate "Maxim MAX9860 Mono Audio Voice Codec"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_MSM8916_WCD_ANALOG
+ tristate "Qualcomm MSM8916 WCD Analog Codec"
+ depends on SPMI || COMPILE_TEST
+
+config SND_SOC_MSM8916_WCD_DIGITAL
+ tristate "Qualcomm MSM8916 WCD DIGITAL Codec"
+ select REGMAP_MMIO
config SND_SOC_PCM1681
tristate "Texas Instruments PCM1681 CODEC"
depends on I2C
+config SND_SOC_PCM1754
+ tristate "Texas Instruments PCM1754 CODEC"
+ depends on GPIOLIB
+
+config SND_SOC_PCM1789
+ tristate
+
+config SND_SOC_PCM1789_I2C
+ tristate "Texas Instruments PCM1789 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_PCM1789
+ help
+ Enable support for Texas Instruments PCM1789 CODEC.
+ Select this if your PCM1789 is connected via an I2C bus.
+
config SND_SOC_PCM179X
- tristate "Texas Instruments PCM179X CODEC"
+ tristate
+
+config SND_SOC_PCM179X_I2C
+ tristate "Texas Instruments PCM179X CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_PCM179X
+ help
+ Enable support for Texas Instruments PCM179x CODEC.
+ Select this if your PCM179x is connected via an I2C bus.
+
+config SND_SOC_PCM179X_SPI
+ tristate "Texas Instruments PCM179X CODEC (SPI)"
depends on SPI_MASTER
+ select SND_SOC_PCM179X
+ help
+ Enable support for Texas Instruments PCM179x CODEC.
+ Select this if your PCM179x is connected via an SPI bus.
+
+config SND_SOC_PCM186X
+ tristate
+
+config SND_SOC_PCM186X_I2C
+ tristate "Texas Instruments PCM186x CODECs - I2C"
+ depends on I2C
+ select SND_SOC_PCM186X
+ select REGMAP_I2C
+
+config SND_SOC_PCM186X_SPI
+ tristate "Texas Instruments PCM186x CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM186X
+ select REGMAP_SPI
config SND_SOC_PCM3008
- tristate
+ tristate
+
+config SND_SOC_PCM3060
+ tristate
+
+config SND_SOC_PCM3060_I2C
+ tristate "Texas Instruments PCM3060 CODEC - I2C"
+ depends on I2C
+ select SND_SOC_PCM3060
+ select REGMAP_I2C
+
+config SND_SOC_PCM3060_SPI
+ tristate "Texas Instruments PCM3060 CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM3060
+ select REGMAP_SPI
config SND_SOC_PCM3168A
tristate
@@ -548,6 +1570,9 @@ config SND_SOC_PCM3168A_SPI
select SND_SOC_PCM3168A
select REGMAP_SPI
+config SND_SOC_PCM5102A
+ tristate "Texas Instruments PCM5102A CODEC"
+
config SND_SOC_PCM512x
tristate
@@ -563,30 +1588,125 @@ config SND_SOC_PCM512x_SPI
select SND_SOC_PCM512x
select REGMAP_SPI
+config SND_SOC_PCM6240
+ tristate "Texas Instruments PCM6240 Family Audio chips based on I2C"
+ depends on I2C
+ help
+ Enable support for Texas Instruments PCM6240 Family Audio chips.
+ Note the PCM6240 driver implements a flexible and configurable
+ setting for register and filter coefficients, to one, two or
+ even multiple PCM6240 Family Audio chips.
+
+config SND_SOC_PEB2466
+ tristate "Infineon PEB2466 quad PCM codec"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Enable support for the Infineon PEB2466 quad PCM codec,
+ also named SICOFI 4-uC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-peb2466.
+
+config SND_SOC_PM4125
+ depends on SND_SOC_PM4125_SDW
+ select SND_SOC_WCD_COMMON
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+
+config SND_SOC_PM4125_SDW
+ tristate "PM4125 audio codec - SDW"
+ select SND_SOC_PM4125
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ select SND_SOC_WCD_COMMON
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The PMIC PM4125 has an in-built audio codec IC used with SoCs
+ like QCM2290, and it is connected via soundwire and SPMI.
+ To compile this codec driver say Y or m.
+
+config SND_SOC_RK3308
+ tristate "Rockchip RK3308 audio CODEC"
+ depends on ARM64 || COMPILE_TEST
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This is a device driver for the audio codec embedded in the
+ Rockchip RK3308 SoC.
+
+ It has 8 24-bit ADCs and 2 24-bit DACs. The maximum supported
+ sampling rate is 192 kHz.
+
+config SND_SOC_RK3328
+ tristate "Rockchip RK3328 audio CODEC"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
+
+config SND_SOC_RK817
+ tristate "Rockchip RK817 audio CODEC"
+ depends on MFD_RK8XX || COMPILE_TEST
+
config SND_SOC_RL6231
tristate
+ default y if SND_SOC_RT5514=y
default y if SND_SOC_RT5616=y
default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y
default y if SND_SOC_RT5651=y
default y if SND_SOC_RT5659=y
+ default y if SND_SOC_RT5660=y
+ default y if SND_SOC_RT5663=y
+ default y if SND_SOC_RT5665=y
+ default y if SND_SOC_RT5668=y
default y if SND_SOC_RT5670=y
default y if SND_SOC_RT5677=y
+ default y if SND_SOC_RT5682=y
+ default y if SND_SOC_RT1011=y
+ default y if SND_SOC_RT1015=y
+ default y if SND_SOC_RT1015P=y
+ default y if SND_SOC_RT1019=y
+ default y if SND_SOC_RT1305=y
+ default y if SND_SOC_RT1308=y
+ default m if SND_SOC_RT5514=m
default m if SND_SOC_RT5616=m
default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
default m if SND_SOC_RT5659=m
+ default m if SND_SOC_RT5660=m
+ default m if SND_SOC_RT5663=m
+ default m if SND_SOC_RT5665=m
+ default m if SND_SOC_RT5668=m
default m if SND_SOC_RT5670=m
default m if SND_SOC_RT5677=m
+ default m if SND_SOC_RT5682=m
+ default m if SND_SOC_RT1011=m
+ default m if SND_SOC_RT1015=m
+ default m if SND_SOC_RT1015P=m
+ default m if SND_SOC_RT1019=m
+ default m if SND_SOC_RT1305=m
+ default m if SND_SOC_RT1308=m
+
+config SND_SOC_RT_SDW_COMMON
+ tristate
+ default y if SND_SOC_RT721_SDCA_SDW=y
+ default m if SND_SOC_RT721_SDCA_SDW=m
config SND_SOC_RL6347A
tristate
+ default y if SND_SOC_RT274=y
default y if SND_SOC_RT286=y
default y if SND_SOC_RT298=y
+ default m if SND_SOC_RT274=m
default m if SND_SOC_RT286=m
default m if SND_SOC_RT298=m
+config SND_SOC_RT274
+ tristate
+ depends on I2C
+
config SND_SOC_RT286
tristate
depends on I2C
@@ -595,30 +1715,122 @@ config SND_SOC_RT298
tristate
depends on I2C
-config SND_SOC_RT5616
+config SND_SOC_RT1011
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1015
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1015P
tristate
+config SND_SOC_RT1016
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1017_SDCA_SDW
+ tristate "Realtek RT1017 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1019
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1305
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1308
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1308_SDW
+ tristate "Realtek RT1308 Codec - SDW"
+ depends on I2C && SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1316_SDW
+ tristate "Realtek RT1316 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1318
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1318_SDW
+ tristate "Realtek RT1318 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1320_SDW
+ tristate "Realtek RT1320 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT5514
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5514_SPI
+ tristate
+ depends on SPI_MASTER
+
+config SND_SOC_RT5514_SPI_BUILTIN
+ bool # force RT5514_SPI to be built-in to avoid link errors
+ default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m
+
+config SND_SOC_RT5616
+ tristate "Realtek RT5616 CODEC"
+ depends on I2C
+
config SND_SOC_RT5631
tristate "Realtek ALC5631/RT5631 CODEC"
depends on I2C
config SND_SOC_RT5640
- tristate
+ tristate "Realtek RT5640/RT5639 Codec"
+ depends on I2C
config SND_SOC_RT5645
- tristate
+ tristate
+ depends on I2C
config SND_SOC_RT5651
tristate
+ depends on I2C
config SND_SOC_RT5659
+ tristate "Realtek RT5658/RT5659 Codec"
+ depends on I2C
+
+config SND_SOC_RT5660
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5663
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5665
tristate
+ depends on I2C
+
+config SND_SOC_RT5668
+ tristate
+ depends on I2C
config SND_SOC_RT5670
tristate
+ depends on I2C
config SND_SOC_RT5677
tristate
+ depends on I2C
select REGMAP_I2C
select REGMAP_IRQ
@@ -626,6 +1838,148 @@ config SND_SOC_RT5677_SPI
tristate
default SND_SOC_RT5677 && SPI
+config SND_SOC_RT5682
+ tristate
+
+config SND_SOC_RT5682_I2C
+ tristate
+ depends on I2C
+ select SND_SOC_RT5682
+
+config SND_SOC_RT5682_SDW
+ tristate "Realtek RT5682 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT5682
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT5682S
+ tristate
+ depends on I2C
+
+config SND_SOC_RT700
+ tristate
+
+config SND_SOC_RT700_SDW
+ tristate "Realtek RT700 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT700
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT711
+ tristate
+
+config SND_SOC_RT711_SDW
+ tristate "Realtek RT711 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT711
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT711_SDCA_SDW
+ tristate "Realtek RT711 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT712_SDCA_SDW
+ tristate "Realtek RT712 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT712_SDCA_DMIC_SDW
+ tristate "Realtek RT712 SDCA DMIC Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT721_SDCA_SDW
+ tristate "Realtek RT721 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT722_SDCA_SDW
+ tristate "Realtek RT722 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT715
+ tristate
+
+config SND_SOC_RT715_SDW
+ tristate "Realtek RT715 Codec - SDW"
+ depends on SOUNDWIRE
+ select SND_SOC_RT715
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT715_SDCA_SDW
+ tristate "Realtek RT715 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+config SND_SOC_RT9120
+ tristate "Richtek RT9120 Stereo Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for Richtek RT9120 20W, stereo, inductor-less,
+ high-efficiency Class-D audio amplifier.
+
+config SND_SOC_RT9123
+ tristate "Richtek RT9123 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the I2C control mode of Richtek RT9123 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RT9123P
+ tristate "Richtek RT9123P Mono Class-D Amplifier"
+ help
+ Enable support for the HW control mode of Richtek RT9123P 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RTQ9124
+ tristate "Richtek RTQ9124 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9124 1x30W digital input automotive
+ audio amplifier with current sense and real-time load diagnostics.
+
+config SND_SOC_RTQ9128
+ tristate "Richtek RTQ9128 45W Digital Input Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9128 digital input 4-channel
+ automotive audio amplifier. It is a ultra-low output noise,
+ high-efficiency, four-channel class-D audio power amplifier
+ that can deliver over 87% power efficienty at 4x75W into 4Ohm,
+ 25V supply in automotive applications.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-rtq9128.
+
+config SND_SOC_SDW_MOCKUP
+ tristate "SoundWire mockup codec"
+ depends on EXPERT
+ depends on SOUNDWIRE
+ help
+ This option enables a SoundWire mockup codec that does not drive the
+ bus, take part in the command/command protocol or generate data on a
+ Source port.
+ This option is only intended to be used for tests on a device
+ with a connector, in combination with a bus analyzer, or to test new
+ topologies that differ from the actual hardware layout.
+ This mockup device could be totally virtual but could also be a
+ real physical one with one key restriction: it is not allowed by the
+ SoundWire specification to be configured via a sideband mechanism and
+ generate audio data for capture. However, nothing prevents such a
+ peripheral device from snooping the bus.
+
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate "Freescale SGTL5000 CODEC"
@@ -633,6 +1987,7 @@ config SND_SOC_SGTL5000
config SND_SOC_SI476X
tristate
+ depends on MFD_SI476X_CORE
config SND_SOC_SIGMADSP
tristate
@@ -646,19 +2001,53 @@ config SND_SOC_SIGMADSP_REGMAP
tristate
select SND_SOC_SIGMADSP
-config SND_SOC_SIRF_AUDIO_CODEC
- tristate "SiRF SoC internal audio codec"
- select REGMAP_MMIO
+config SND_SOC_SIMPLE_AMPLIFIER
+ tristate "Simple Audio Amplifier"
-config SND_SOC_SN95031
- tristate
+config SND_SOC_SIMPLE_MUX
+ tristate "Simple Audio Mux"
+ depends on GPIOLIB
+
+config SND_SOC_SMA1303
+ tristate "Iron Device SMA1303 Audio Amplifier"
+ depends on I2C
+ help
+ Enable support for Iron Device SMA1303 Boosted Class-D amplifier
+
+config SND_SOC_SMA1307
+ tristate "Iron Device SMA1307 Audio Amplifier"
+ depends on I2C
+ help
+ Enable support for Iron Device SMA1307 boosted digital speaker
+ amplifier with feedback-loop.
+ If you are using a system with an SMA1307 amplifier connected
+ via I2C, enable this option.
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
-config SND_SOC_SSM2518
+config SND_SOC_SRC4XXX_I2C
+ tristate "Texas Instruments SRC4XXX DIR/DIT and SRC codecs"
+ depends on I2C
+ select SND_SOC_SRC4XXX
+ help
+ Enable support for the TI SRC4XXX family of codecs. These include the
+ scr4392 which has digital receivers, transmitters, and
+ a sample rate converter, including numerous ports.
+
+config SND_SOC_SRC4XXX
tristate
+config SND_SOC_SSM2305
+ tristate "Analog Devices SSM2305 Class-D Amplifier"
+ help
+ Enable support for Analog Devices SSM2305 filterless
+ high-efficiency mono Class-D audio power amplifiers.
+
+config SND_SOC_SSM2518
+ tristate "Analog Devices SSM2518 Class-D Amplifier"
+ depends on I2C
+
config SND_SOC_SSM2602
tristate
@@ -674,6 +2063,12 @@ config SND_SOC_SSM2602_I2C
select SND_SOC_SSM2602
select REGMAP_I2C
+config SND_SOC_SSM3515
+ tristate "Analog Devices SSM3515 amplifier driver"
+ select REGMAP_I2C
+ depends on I2C
+ depends on OF
+
config SND_SOC_SSM4567
tristate "Analog Devices ssm4567 amplifier driver support"
depends on I2C
@@ -689,9 +2084,12 @@ config SND_SOC_STA350
config SND_SOC_STA529
tristate
+ depends on I2C
config SND_SOC_STAC9766
tristate
+ depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_STI_SAS
tristate "codec Audio support for STI SAS codec"
@@ -700,18 +2098,126 @@ config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
+config SND_SOC_TAS2562
+ tristate "Texas Instruments TAS2562 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2764
+ tristate "Texas Instruments TAS2764 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2770
+ tristate "Texas Instruments TAS2770 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_TAS2780
+ tristate "Texas Instruments TAS2780 Mono Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS2780 high-efficiency
+ digital input mono Class-D audio power amplifiers.
+
+config SND_SOC_TAS2781_COMLIB
+ tristate
+
+config SND_SOC_TAS2781_COMLIB_I2C
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ tristate
+
+config SND_SOC_TAS2781_FMWLIB
+ select SND_SOC_TAS2781_COMLIB
+ select CRC8
+ tristate
+ default n
+
+config SND_SOC_TAS2781_I2C
+ tristate "Texas Instruments TAS2781 speaker amplifier based on I2C"
+ depends on I2C
+ select SND_SOC_TAS2781_COMLIB_I2C
+ select SND_SOC_TAS2781_FMWLIB
+ help
+ Enable support for Texas Instruments TAS2781 Smart Amplifier
+ Digital input mono Class-D and DSP-inside audio power amplifiers.
+ Note the TAS2781 driver implements a flexible and configurable
+ algo coefficient setting, for one, two or even multiple TAS2781
+ chips.
+
+config SND_SOC_TAS2783_SDW
+ tristate "Texas Instruments TAS2783 speaker amplifier (sdw)"
+ depends on SOUNDWIRE
+ depends on EFI
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+ select CRC32
+ help
+ Enable support for Texas Instruments TAS2783A Digital input
+ mono Class-D and DSP-inside audio power amplifiers. TAS2783
+ driver implements a flexible and configurable algorithm
+ cofficient setting, for one, two or multiple TAS2783 chips.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
config SND_SOC_TAS571X
- tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
+ tristate "Texas Instruments TAS571x power amplifiers"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS5707, TAS5711, TAS5717,
+ TAS5719 and TAS5721 power amplifiers
+
+config SND_SOC_TAS5720
+ tristate "Texas Instruments TAS5720 Mono Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS5720L/M high-efficiency mono
+ Class-D audio power amplifiers.
+
+config SND_SOC_TAS5805M
+ tristate "Texas Instruments TAS5805M speaker amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS5805M Class-D
+ amplifiers. This is a speaker amplifier with an integrated
+ DSP. DSP configuration for each instance needs to be supplied
+ via a device-tree attribute.
+
+config SND_SOC_TAS6424
+ tristate "Texas Instruments TAS6424 Quad-Channel Audio amplifier"
depends on I2C
+ help
+ Enable support for Texas Instruments TAS6424 high-efficiency
+ digital input quad-channel Class-D audio power amplifiers.
+
+config SND_SOC_TDA7419
+ tristate "ST TDA7419 audio processor"
+ depends on I2C
+ select REGMAP_I2C
config SND_SOC_TFA9879
tristate "NXP Semiconductors TFA9879 amplifier"
depends on I2C
+config SND_SOC_TFA989X
+ tristate "NXP/Goodix TFA989X (TFA1) amplifiers"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for NXP (now Goodix) TFA989X (TFA1 family) speaker
+ amplifiers, e.g. TFA9895.
+ Note that the driver currently bypasses the built-in "CoolFlux DSP"
+ and does not support (hardware) volume control.
+
+config SND_SOC_TLV320ADC3XXX
+ tristate "Texas Instruments TLV320ADC3001/3101 audio ADC"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101
+ ADCs.
+
config SND_SOC_TLV320AIC23
tristate
@@ -736,60 +2242,225 @@ config SND_SOC_TLV320AIC31XX
config SND_SOC_TLV320AIC32X4
tristate
+ depends on COMMON_CLK
+
+config SND_SOC_TLV320AIC32X4_I2C
+ tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
+ depends on I2C
+ depends on COMMON_CLK
+ select SND_SOC_TLV320AIC32X4
+
+config SND_SOC_TLV320AIC32X4_SPI
+ tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
+ depends on SPI_MASTER
+ depends on COMMON_CLK
+ select SND_SOC_TLV320AIC32X4
config SND_SOC_TLV320AIC3X
- tristate "Texas Instruments TLV320AIC3x CODECs"
+ tristate
+
+config SND_SOC_TLV320AIC3X_I2C
+ tristate "Texas Instruments TLV320AIC3x audio CODECs - I2C"
depends on I2C
+ select SND_SOC_TLV320AIC3X
+ select REGMAP_I2C
+
+config SND_SOC_TLV320AIC3X_SPI
+ tristate "Texas Instruments TLV320AIC3x audio CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_TLV320AIC3X
+ select REGMAP_SPI
config SND_SOC_TLV320DAC33
tristate
+ depends on I2C
+ depends on GPIOLIB_LEGACY
+
+config SND_SOC_TLV320ADCX140
+ tristate "Texas Instruments TLV320ADCX140 CODEC family"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Add support for Texas Instruments tlv320adc3140, tlv320adc5140 and
+ tlv320adc6140 quad channel ADCs.
config SND_SOC_TS3A227E
tristate "TI Headset/Mic detect and keypress chip"
depends on I2C
+config SND_SOC_TSCS42XX
+ tristate "Tempo Semiconductor TSCS42xx CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Add support for Tempo Semiconductor's TSCS42xx audio CODEC.
+
+config SND_SOC_TSCS454
+ tristate "Tempo Semiconductor TSCS454 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Add support for Tempo Semiconductor's TSCS454 audio CODEC.
+
config SND_SOC_TWL4030
- select MFD_TWL4030_AUDIO
tristate
+ depends on TWL4030_CORE
+ select MFD_TWL4030_AUDIO
config SND_SOC_TWL6040
tristate
+ depends on TWL6040_CORE
-config SND_SOC_UDA134X
- tristate
+config SND_SOC_UDA1334
+ tristate "NXP UDA1334 DAC"
+ depends on GPIOLIB
+ help
+ The UDA1334 is an NXP audio codec, supports the I2S-bus data format
+ and has basic features such as de-emphasis (at 44.1 kHz sampling
+ rate) and mute.
+
+config SND_SOC_UDA1342
+ tristate "NXP UDA1342 CODEC"
+ depends on I2C
+ help
+ The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
+ mic inputs), stereo audio DAC, with basic audio processing.
config SND_SOC_UDA1380
- tristate
+ tristate
+ depends on I2C
+ depends on GPIOLIB_LEGACY
-config SND_SOC_WL1273
+config SND_SOC_WCD_CLASSH
tristate
+config SND_SOC_WCD_COMMON
+ tristate
+
+config SND_SOC_WCD9335
+ tristate "WCD9335 Codec"
+ depends on SLIMBUS
+ select REGMAP_SLIMBUS
+ select REGMAP_IRQ
+ select SND_SOC_WCD_CLASSH
+ help
+ The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports
+ Qualcomm Technologies, Inc. (QTI) multimedia solutions,
+ including the MSM8996, MSM8976, and MSM8956 chipsets.
+
+config SND_SOC_WCD_MBHC
+ tristate
+
+config SND_SOC_WCD934X
+ tristate "WCD9340/WCD9341 Codec"
+ depends on COMMON_CLK
+ depends on SLIMBUS
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_IRQ
+ select REGMAP_SLIMBUS
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+ select SND_SOC_WCD_MBHC
+ depends on MFD_WCD934X || COMPILE_TEST
+ help
+ The WCD9340/9341 is a audio codec IC Integrated in
+ Qualcomm SoCs like SDM845.
+
+config SND_SOC_WCD937X
+ depends on SND_SOC_WCD937X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+
+config SND_SOC_WCD937X_SDW
+ tristate "WCD9370/WCD9375 Codec - SDW"
+ select SND_SOC_WCD937X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9370/9375 is an audio codec IC used with SoCs
+ like SC7280 or QCM6490 chipsets, and it connected
+ via soundwire.
+ To compile this codec driver say Y or m.
+
+config SND_SOC_WCD938X
+ depends on SND_SOC_WCD938X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+ select MULTIPLEXER
+
+config SND_SOC_WCD938X_SDW
+ tristate "WCD9380/WCD9385 Codec - SDW"
+ select SND_SOC_WCD938X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9380/9385 is a audio codec IC Integrated in
+ Qualcomm SoCs like SM8250.
+
+config SND_SOC_WCD939X
+ depends on SND_SOC_WCD939X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ depends on TYPEC || !TYPEC
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+
+config SND_SOC_WCD939X_SDW
+ tristate "WCD9390/WCD9395 Codec - SDW"
+ depends on TYPEC || !TYPEC
+ select SND_SOC_WCD939X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9390/9395 is a audio codec IC Integrated in
+ Qualcomm SoCs like SM8650.
+
config SND_SOC_WM0010
tristate
+ depends on SPI_MASTER
config SND_SOC_WM1250_EV1
tristate
+ depends on I2C
config SND_SOC_WM2000
tristate
+ depends on I2C
config SND_SOC_WM2200
tristate
+ depends on I2C
config SND_SOC_WM5100
tristate
+ depends on I2C
config SND_SOC_WM5102
tristate
+ depends on MFD_WM5102 && MFD_ARIZONA
config SND_SOC_WM5110
tristate
+ depends on MFD_WM5110 && MFD_ARIZONA
config SND_SOC_WM8350
tristate
+ depends on MFD_WM8350
config SND_SOC_WM8400
tristate
+ # FIXME nothing selects SND_SOC_WM8400??
+ depends on MFD_WM8400
config SND_SOC_WM8510
tristate "Wolfson Microelectronics WM8510 CODEC"
@@ -799,8 +2470,12 @@ config SND_SOC_WM8523
tristate "Wolfson Microelectronics WM8523 DAC"
depends on I2C
+config SND_SOC_WM8524
+ tristate "Wolfson Microelectronics WM8524 DAC"
+ depends on GPIOLIB
+
config SND_SOC_WM8580
- tristate "Wolfson Microelectronics WM8523 CODEC"
+ tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs"
depends on I2C
config SND_SOC_WM8711
@@ -815,15 +2490,26 @@ config SND_SOC_WM8728
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
- tristate "Wolfson Microelectronics WM8731 CODEC"
- depends on SND_SOC_I2C_AND_SPI
+ tristate
+
+config SND_SOC_WM8731_I2C
+ tristate "Wolfson Microelectronics WM8731 CODEC with I2C"
+ depends on I2C
+ select REGMAP
+ select SND_SOC_WM8731
+
+config SND_SOC_WM8731_SPI
+ tristate "Wolfson Microelectronics WM8731 CODEC with SPI"
+ depends on SPI
+ select REGMAP
+ select SND_SOC_WM8731
config SND_SOC_WM8737
tristate "Wolfson Microelectronics WM8737 ADC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8741
- tristate "Wolfson Microelectronics WM8737 DAC"
+ tristate "Wolfson Microelectronics WM8741 DAC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8750
@@ -843,7 +2529,7 @@ config SND_SOC_WM8776
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8782
- tristate
+ tristate "Wolfson Microelectronics WM8782 ADC"
config SND_SOC_WM8804
tristate
@@ -862,25 +2548,31 @@ config SND_SOC_WM8804_SPI
config SND_SOC_WM8900
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8903
tristate "Wolfson Microelectronics WM8903 CODEC"
depends on I2C
config SND_SOC_WM8904
- tristate
+ tristate "Wolfson Microelectronics WM8904 CODEC"
+ depends on I2C
config SND_SOC_WM8940
- tristate
+ tristate "Wolfson Microelectronics WM8940 codec"
+ depends on I2C
config SND_SOC_WM8955
tristate
+ depends on I2C
config SND_SOC_WM8960
- tristate
+ tristate "Wolfson Microelectronics WM8960 CODEC"
+ depends on I2C
config SND_SOC_WM8961
- tristate
+ tristate "Wolfson Microelectronics WM8961 CODEC"
+ depends on I2C
config SND_SOC_WM8962
tristate "Wolfson Microelectronics WM8962 CODEC"
@@ -888,6 +2580,7 @@ config SND_SOC_WM8962
config SND_SOC_WM8971
tristate
+ depends on I2C
config SND_SOC_WM8974
tristate "Wolfson Microelectronics WM8974 codec"
@@ -899,74 +2592,245 @@ config SND_SOC_WM8978
config SND_SOC_WM8983
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8985
- tristate
+ tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8988
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8990
tristate
+ depends on I2C
config SND_SOC_WM8991
tristate
+ depends on I2C
config SND_SOC_WM8993
tristate
+ depends on I2C
config SND_SOC_WM8994
tristate
+ depends on MFD_WM8994
config SND_SOC_WM8995
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8996
tristate
+ depends on I2C
config SND_SOC_WM8997
tristate
+ depends on MFD_WM8997 && MFD_ARIZONA
config SND_SOC_WM8998
- tristate
+ tristate "Wolfson Microelectronics WM8998 codec driver"
+ depends on MFD_WM8998 && MFD_ARIZONA
config SND_SOC_WM9081
tristate
+ depends on I2C
config SND_SOC_WM9090
tristate
+ depends on I2C
config SND_SOC_WM9705
tristate
+ depends on SND_SOC_AC97_BUS || AC97_BUS_NEW
+ select REGMAP_AC97
+ select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9712
tristate
+ depends on SND_SOC_AC97_BUS || AC97_BUS_NEW
+ select REGMAP_AC97
+ select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9713
tristate
+ depends on SND_SOC_AC97_BUS || AC97_BUS_NEW
select REGMAP_AC97
+ select AC97_BUS_COMPAT if AC97_BUS_NEW
+
+config SND_SOC_WSA881X
+ tristate "WSA881X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ This enables support for Qualcomm WSA8810/WSA8815 Class-D
+ Smart Speaker Amplifier.
+
+config SND_SOC_WSA883X
+ tristate "WSA883X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ This enables support for Qualcomm WSA8830/WSA8835 Class-D
+ Smart Speaker Amplifier.
+
+config SND_SOC_WSA884X
+ tristate "WSA884X Codec"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D
+ Smart Speaker Amplifier.
+
+config SND_SOC_ZL38060
+ tristate "Microsemi ZL38060 Connected Home Audio Processor"
+ depends on SPI_MASTER
+ depends on GPIOLIB
+ select REGMAP
+ help
+ Support for ZL38060 Connected Home Audio Processor from Microsemi,
+ which consists of a Digital Signal Processor (DSP), several Digital
+ Audio Interfaces (DAIs), analog outputs, and a block of 14 GPIOs.
# Amp
config SND_SOC_LM4857
tristate
+ depends on I2C
+
+config SND_SOC_MAX9759
+ tristate "Maxim MAX9759 speaker Amplifier"
+ depends on GPIOLIB
config SND_SOC_MAX9768
tristate
+ depends on I2C
config SND_SOC_MAX9877
tristate
+ depends on I2C
config SND_SOC_MC13783
tristate
+ depends on MFD_MC13XXX
config SND_SOC_ML26124
tristate
+ depends on I2C
+
+config SND_SOC_MT6351
+ tristate "MediaTek MT6351 Codec"
+
+config SND_SOC_MT6357
+ tristate "MediaTek MT6357 Codec"
+ help
+ Enable support for the platform which uses MT6357 as
+ external codec device.
+
+config SND_SOC_MT6358
+ tristate "MediaTek MT6358 Codec"
+ help
+ Enable support for the platform which uses MT6358 as
+ external codec device.
+
+config SND_SOC_MT6359
+ tristate "MediaTek MT6359 Codec"
+ depends on MTK_PMIC_WRAP
+ help
+ Enable support for the platform which uses MT6359 as
+ external codec device.
+
+config SND_SOC_MT6359_ACCDET
+ tristate "MediaTek MT6359 ACCDET driver"
+ depends on MTK_PMIC_WRAP
+ help
+ ACCDET means Accessory Detection technology, MediaTek develop it
+ for ASoC codec soc-jack detection mechanism.
+ Select N if you don't have jack on board.
+
+config SND_SOC_MT6660
+ tristate "Mediatek MT6660 Speaker Amplifier"
+ depends on I2C
+ help
+ MediaTek MT6660 is a smart power amplifier which contain
+ speaker protection, multi-band DRC, equalizer functions.
+ Select N if you don't have MT6660 on board.
+ Select M to build this as module.
+
+config SND_SOC_NAU8315
+ tristate "Nuvoton Technology Corporation NAU8315 CODEC"
+
+config SND_SOC_NAU8325
+ tristate "Nuvoton Technology Corporation NAU8325 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8540
+ tristate "Nuvoton Technology Corporation NAU85L40 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8810
+ tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8821
+ tristate "Nuvoton Technology Corporation NAU88L21 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8822
+ tristate "Nuvoton Technology Corporation NAU88C22 CODEC"
+ depends on I2C
+
+config SND_SOC_NAU8824
+ tristate "Nuvoton Technology Corporation NAU88L24 CODEC"
+ depends on I2C
config SND_SOC_NAU8825
tristate
+ depends on I2C
+
+config SND_SOC_NTPFW
+ tristate
+
+config SND_SOC_NTP8918
+ select SND_SOC_NTPFW
+ tristate "NeoFidelity NTP8918 amplifier"
+ depends on I2C
+
+config SND_SOC_NTP8835
+ select SND_SOC_NTPFW
+ tristate "NeoFidelity NTP8835 and NTP8835C amplifiers"
+ depends on I2C
config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
+config SND_SOC_LPASS_MACRO_COMMON
+ tristate
+
+config SND_SOC_LPASS_WSA_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_VA_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_RX_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_TX_MACRO
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ select SND_SOC_LPASS_MACRO_COMMON
+ tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)"
+
endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d44f7d347183..d687d4f74363 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,204 +1,435 @@
-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-ak4613-objs := ak4613.o
-snd-soc-ak4641-objs := ak4641.o
-snd-soc-ak4642-objs := ak4642.o
-snd-soc-ak4671-objs := ak4671.o
-snd-soc-ak5386-objs := ak5386.o
-snd-soc-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-cs4349-objs := cs4349.o
-snd-soc-cs47l24-objs := cs47l24.o
-snd-soc-cx20442-objs := cx20442.o
-snd-soc-da7210-objs := da7210.o
-snd-soc-da7213-objs := da7213.o
-snd-soc-da7218-objs := da7218.o
-snd-soc-da7219-objs := da7219.o da7219-aad.o
-snd-soc-da732x-objs := da732x.o
-snd-soc-da9055-objs := da9055.o
-snd-soc-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-gtm601-objs := gtm601.o
-snd-soc-hdac-hdmi-objs := hdac_hdmi.o
-snd-soc-ics43432-objs := ics43432.o
-snd-soc-inno-rk3036-objs := inno_rk3036.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-nau8825-objs := nau8825.o
-snd-soc-pcm1681-objs := pcm1681.o
-snd-soc-pcm179x-codec-objs := pcm179x.o
-snd-soc-pcm3008-objs := pcm3008.o
-snd-soc-pcm3168a-objs := pcm3168a.o
-snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
-snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
-snd-soc-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-rl6347a-objs := rl6347a.o
-snd-soc-rt286-objs := rt286.o
-snd-soc-rt298-objs := rt298.o
-snd-soc-rt5616-objs := rt5616.o
-snd-soc-rt5631-objs := rt5631.o
-snd-soc-rt5640-objs := rt5640.o
-snd-soc-rt5645-objs := rt5645.o
-snd-soc-rt5651-objs := rt5651.o
-snd-soc-rt5659-objs := rt5659.o
-snd-soc-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-sti-sas-objs := sti-sas.o
-snd-soc-tas5086-objs := tas5086.o
-snd-soc-tas571x-objs := tas571x.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-wm8998-objs := wm8998.o
-snd-soc-wm9081-objs := wm9081.o
-snd-soc-wm9090-objs := wm9090.o
-snd-soc-wm9705-objs := wm9705.o
-snd-soc-wm9712-objs := wm9712.o
-snd-soc-wm9713-objs := wm9713.o
-snd-soc-wm-hubs-objs := wm_hubs.o
-
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-88pm860x-y := 88pm860x-codec.o
+snd-soc-ab8500-codec-y := ab8500-codec.o
+snd-soc-ac97-y := ac97.o
+snd-soc-ad1836-y := ad1836.o
+snd-soc-ad193x-y := ad193x.o
+snd-soc-ad193x-spi-y := ad193x-spi.o
+snd-soc-ad193x-i2c-y := ad193x-i2c.o
+snd-soc-ad1980-y := ad1980.o
+snd-soc-ad73311-y := ad73311.o
+snd-soc-adau-utils-y := adau-utils.o
+snd-soc-adau1372-y := adau1372.o
+snd-soc-adau1372-i2c-y := adau1372-i2c.o
+snd-soc-adau1372-spi-y := adau1372-spi.o
+snd-soc-adau1373-y := adau1373.o
+snd-soc-adau1701-y := adau1701.o
+snd-soc-adau17x1-y := adau17x1.o
+snd-soc-adau1761-y := adau1761.o
+snd-soc-adau1761-i2c-y := adau1761-i2c.o
+snd-soc-adau1761-spi-y := adau1761-spi.o
+snd-soc-adau1781-y := adau1781.o
+snd-soc-adau1781-i2c-y := adau1781-i2c.o
+snd-soc-adau1781-spi-y := adau1781-spi.o
+snd-soc-adau1977-y := adau1977.o
+snd-soc-adau1977-spi-y := adau1977-spi.o
+snd-soc-adau1977-i2c-y := adau1977-i2c.o
+snd-soc-adau7002-y := adau7002.o
+snd-soc-adau7118-y := adau7118.o
+snd-soc-adau7118-i2c-y := adau7118-i2c.o
+snd-soc-adau7118-hw-y := adau7118-hw.o
+snd-soc-adav80x-y := adav80x.o
+snd-soc-adav801-y := adav801.o
+snd-soc-adav803-y := adav803.o
+snd-soc-ads117x-y := ads117x.o
+snd-soc-ak4104-y := ak4104.o
+snd-soc-ak4118-y := ak4118.o
+snd-soc-ak4375-y := ak4375.o
+snd-soc-ak4458-y := ak4458.o
+snd-soc-ak4535-y := ak4535.o
+snd-soc-ak4554-y := ak4554.o
+snd-soc-ak4613-y := ak4613.o
+snd-soc-ak4619-y := ak4619.o
+snd-soc-ak4641-y := ak4641.o
+snd-soc-ak4642-y := ak4642.o
+snd-soc-ak4671-y := ak4671.o
+snd-soc-ak5386-y := ak5386.o
+snd-soc-ak5558-y := ak5558.o
+snd-soc-arizona-y := arizona.o arizona-jack.o
+snd-soc-audio-iio-aux-y := audio-iio-aux.o
+snd-soc-aw8738-y := aw8738.o
+snd-soc-aw87390-y := aw87390.o
+snd-soc-aw88081-y := aw88081.o
+snd-soc-aw88395-lib-y := aw88395/aw88395_lib.o \
+ aw88395/aw88395_device.o
+snd-soc-aw88395-y := aw88395/aw88395.o
+snd-soc-aw88166-y := aw88166.o
+snd-soc-aw88261-y := aw88261.o
+snd-soc-aw88399-y := aw88399.o
+snd-soc-bd28623-y := bd28623.o
+snd-soc-bt-sco-y := bt-sco.o
+snd-soc-chv3-codec-y := chv3-codec.o
+snd-soc-cpcap-y := cpcap.o
+snd-soc-cq93vc-y := cq93vc.o
+snd-soc-cros-ec-codec-y := cros_ec_codec.o
+snd-soc-cs-amp-lib-y := cs-amp-lib.o
+snd-soc-cs-amp-lib-test-y := cs-amp-lib-test.o
+snd-soc-cs35l32-y := cs35l32.o
+snd-soc-cs35l33-y := cs35l33.o
+snd-soc-cs35l34-y := cs35l34.o
+snd-soc-cs35l35-y := cs35l35.o
+snd-soc-cs35l36-y := cs35l36.o
+snd-soc-cs35l41-lib-y := cs35l41-lib.o
+snd-soc-cs35l41-y := cs35l41.o
+snd-soc-cs35l41-spi-y := cs35l41-spi.o
+snd-soc-cs35l41-i2c-y := cs35l41-i2c.o
+snd-soc-cs35l45-y := cs35l45.o cs35l45-tables.o
+snd-soc-cs35l45-spi-y := cs35l45-spi.o
+snd-soc-cs35l45-i2c-y := cs35l45-i2c.o
+snd-soc-cs35l56-y := cs35l56.o
+snd-soc-cs35l56-shared-y := cs35l56-shared.o
+snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
+snd-soc-cs35l56-spi-y := cs35l56-spi.o
+snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
+snd-soc-cs40l50-y := cs40l50-codec.o
+snd-soc-cs42l42-y := cs42l42.o
+snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
+snd-soc-cs42l42-sdw-y := cs42l42-sdw.o
+snd-soc-cs42l43-y := cs42l43.o cs42l43-jack.o
+snd-soc-cs42l43-sdw-y := cs42l43-sdw.o
+snd-soc-cs42l51-y := cs42l51.o
+snd-soc-cs42l51-i2c-y := cs42l51-i2c.o
+snd-soc-cs42l52-y := cs42l52.o
+snd-soc-cs42l56-y := cs42l56.o
+snd-soc-cs42l73-y := cs42l73.o
+snd-soc-cs42l83-i2c-y := cs42l83-i2c.o
+snd-soc-cs42l84-y := cs42l84.o
+snd-soc-cs4234-y := cs4234.o
+snd-soc-cs4265-y := cs4265.o
+snd-soc-cs4270-y := cs4270.o
+snd-soc-cs4271-y := cs4271.o
+snd-soc-cs4271-i2c-y := cs4271-i2c.o
+snd-soc-cs4271-spi-y := cs4271-spi.o
+snd-soc-cs42xx8-y := cs42xx8.o
+snd-soc-cs42xx8-i2c-y := cs42xx8-i2c.o
+snd-soc-cs43130-y := cs43130.o
+snd-soc-cs4341-y := cs4341.o
+snd-soc-cs4349-y := cs4349.o
+snd-soc-cs47l15-y := cs47l15.o
+snd-soc-cs47l24-y := cs47l24.o
+snd-soc-cs47l35-y := cs47l35.o
+snd-soc-cs47l85-y := cs47l85.o
+snd-soc-cs47l90-y := cs47l90.o
+snd-soc-cs47l92-y := cs47l92.o
+snd-soc-cs48l32-y := cs48l32.o cs48l32-tables.o
+snd-soc-cs53l30-y := cs53l30.o
+snd-soc-cs530x-y := cs530x.o
+snd-soc-cs530x-i2c-y := cs530x-i2c.o
+snd-soc-cs530x-spi-y := cs530x-spi.o
+snd-soc-cx20442-y := cx20442.o
+snd-soc-cx2072x-y := cx2072x.o
+snd-soc-da7210-y := da7210.o
+snd-soc-da7213-y := da7213.o
+snd-soc-da7218-y := da7218.o
+snd-soc-da7219-y := da7219.o da7219-aad.o
+snd-soc-da732x-y := da732x.o
+snd-soc-da9055-y := da9055.o
+snd-soc-dmic-y := dmic.o
+snd-soc-es7134-y := es7134.o
+snd-soc-es7241-y := es7241.o
+snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o
+snd-soc-es8311-y := es8311.o
+snd-soc-es8316-y := es8316.o
+snd-soc-es8323-y := es8323.o
+snd-soc-es8326-y := es8326.o
+snd-soc-es8328-y := es8328.o
+snd-soc-es8328-i2c-y := es8328-i2c.o
+snd-soc-es8328-spi-y := es8328-spi.o
+snd-soc-es8375-y := es8375.o
+snd-soc-es8389-y := es8389.o
+snd-soc-framer-y := framer-codec.o
+snd-soc-fs-amp-lib-y := fs-amp-lib.o
+snd-soc-fs210x-y := fs210x.o
+snd-soc-gtm601-y := gtm601.o
+snd-soc-hdac-hdmi-y := hdac_hdmi.o
+snd-soc-hdac-hda-y := hdac_hda.o
+snd-soc-hda-codec-y := hda.o hda-dai.o
+snd-soc-ics43432-y := ics43432.o
+snd-soc-idt821034-y := idt821034.o
+snd-soc-inno-rk3036-y := inno_rk3036.o
+snd-soc-isabelle-y := isabelle.o
+snd-soc-jz4740-codec-y := jz4740.o
+snd-soc-jz4725b-codec-y := jz4725b.o
+snd-soc-jz4760-codec-y := jz4760.o
+snd-soc-jz4770-codec-y := jz4770.o
+snd-soc-lm4857-y := lm4857.o
+snd-soc-lm49453-y := lm49453.o
+snd-soc-lochnagar-sc-y := lochnagar-sc.o
+snd-soc-lpass-macro-common-y := lpass-macro-common.o
+snd-soc-lpass-rx-macro-y := lpass-rx-macro.o
+snd-soc-lpass-tx-macro-y := lpass-tx-macro.o
+snd-soc-lpass-wsa-macro-y := lpass-wsa-macro.o
+snd-soc-lpass-va-macro-y := lpass-va-macro.o
+snd-soc-madera-y := madera.o
+snd-soc-max9759-y := max9759.o
+snd-soc-max9768-y := max9768.o
+snd-soc-max98088-y := max98088.o
+snd-soc-max98090-y := max98090.o
+snd-soc-max98095-y := max98095.o
+snd-soc-max98357a-y := max98357a.o
+snd-soc-max98371-y := max98371.o
+snd-soc-max9867-y := max9867.o
+snd-soc-max98925-y := max98925.o
+snd-soc-max98926-y := max98926.o
+snd-soc-max98927-y := max98927.o
+snd-soc-max98520-y := max98520.o
+snd-soc-max98363-y := max98363.o
+snd-soc-max98373-y := max98373.o
+snd-soc-max98373-i2c-y := max98373-i2c.o
+snd-soc-max98373-sdw-y := max98373-sdw.o
+snd-soc-max98388-y := max98388.o
+snd-soc-max98390-y := max98390.o
+snd-soc-max98396-y := max98396.o
+snd-soc-max9850-y := max9850.o
+snd-soc-max9860-y := max9860.o
+snd-soc-mc13783-y := mc13783.o
+snd-soc-ml26124-y := ml26124.o
+snd-soc-msm8916-analog-y := msm8916-wcd-analog.o
+snd-soc-msm8916-digital-y := msm8916-wcd-digital.o
+snd-soc-mt6351-y := mt6351.o
+snd-soc-mt6357-y := mt6357.o
+snd-soc-mt6358-y := mt6358.o
+snd-soc-mt6359-y := mt6359.o
+snd-soc-mt6359-accdet-y := mt6359-accdet.o
+snd-soc-mt6660-y := mt6660.o
+snd-soc-nau8315-y := nau8315.o
+snd-soc-nau8325-y := nau8325.o
+snd-soc-nau8540-y := nau8540.o
+snd-soc-nau8810-y := nau8810.o
+snd-soc-nau8821-y := nau8821.o
+snd-soc-nau8822-y := nau8822.o
+snd-soc-nau8824-y := nau8824.o
+snd-soc-nau8825-y := nau8825.o
+snd-soc-ntp8835-y := ntp8835.o
+snd-soc-ntp8918-y := ntp8918.o
+snd-soc-ntpfw-y := ntpfw.o
+snd-soc-hdmi-codec-y := hdmi-codec.o
+snd-soc-pcm1681-y := pcm1681.o
+snd-soc-pcm1754-y := pcm1754.o
+snd-soc-pcm1789-codec-y := pcm1789.o
+snd-soc-pcm1789-i2c-y := pcm1789-i2c.o
+snd-soc-pcm179x-codec-y := pcm179x.o
+snd-soc-pcm179x-i2c-y := pcm179x-i2c.o
+snd-soc-pcm179x-spi-y := pcm179x-spi.o
+snd-soc-pcm186x-y := pcm186x.o
+snd-soc-pcm186x-i2c-y := pcm186x-i2c.o
+snd-soc-pcm186x-spi-y := pcm186x-spi.o
+snd-soc-pcm3008-y := pcm3008.o
+snd-soc-pcm3060-y := pcm3060.o
+snd-soc-pcm3060-i2c-y := pcm3060-i2c.o
+snd-soc-pcm3060-spi-y := pcm3060-spi.o
+snd-soc-pcm3168a-y := pcm3168a.o
+snd-soc-pcm3168a-i2c-y := pcm3168a-i2c.o
+snd-soc-pcm3168a-spi-y := pcm3168a-spi.o
+snd-soc-pcm5102a-y := pcm5102a.o
+snd-soc-pcm512x-y := pcm512x.o
+snd-soc-pcm512x-i2c-y := pcm512x-i2c.o
+snd-soc-pcm512x-spi-y := pcm512x-spi.o
+snd-soc-pcm6240-y := pcm6240.o
+snd-soc-peb2466-y := peb2466.o
+snd-soc-pm4125-y := pm4125.o
+snd-soc-pm4125-sdw-y := pm4125-sdw.o
+snd-soc-rk3308-y := rk3308_codec.o
+snd-soc-rk3328-y := rk3328_codec.o
+snd-soc-rk817-y := rk817_codec.o
+snd-soc-rl6231-y := rl6231.o
+snd-soc-rt-sdw-common-y := rt-sdw-common.o
+snd-soc-rl6347a-y := rl6347a.o
+snd-soc-rt1011-y := rt1011.o
+snd-soc-rt1015-y := rt1015.o
+snd-soc-rt1015p-y := rt1015p.o
+snd-soc-rt1016-y := rt1016.o
+snd-soc-rt1017-sdca-y := rt1017-sdca-sdw.o
+snd-soc-rt1019-y := rt1019.o
+snd-soc-rt1305-y := rt1305.o
+snd-soc-rt1308-y := rt1308.o
+snd-soc-rt1308-sdw-y := rt1308-sdw.o
+snd-soc-rt1316-sdw-y := rt1316-sdw.o
+snd-soc-rt1318-y := rt1318.o
+snd-soc-rt1318-sdw-y := rt1318-sdw.o
+snd-soc-rt1320-sdw-y := rt1320-sdw.o
+snd-soc-rt274-y := rt274.o
+snd-soc-rt286-y := rt286.o
+snd-soc-rt298-y := rt298.o
+snd-soc-rt5514-y := rt5514.o
+snd-soc-rt5514-spi-y := rt5514-spi.o
+snd-soc-rt5616-y := rt5616.o
+snd-soc-rt5631-y := rt5631.o
+snd-soc-rt5640-y := rt5640.o
+snd-soc-rt5645-y := rt5645.o
+snd-soc-rt5651-y := rt5651.o
+snd-soc-rt5659-y := rt5659.o
+snd-soc-rt5660-y := rt5660.o
+snd-soc-rt5663-y := rt5663.o
+snd-soc-rt5665-y := rt5665.o
+snd-soc-rt5668-y := rt5668.o
+snd-soc-rt5670-y := rt5670.o
+snd-soc-rt5677-y := rt5677.o
+snd-soc-rt5677-spi-y := rt5677-spi.o
+snd-soc-rt5682-y := rt5682.o
+snd-soc-rt5682-sdw-y := rt5682-sdw.o
+snd-soc-rt5682-i2c-y := rt5682-i2c.o
+snd-soc-rt5682s-y := rt5682s.o
+snd-soc-rt700-y := rt700.o rt700-sdw.o
+snd-soc-rt711-y := rt711.o rt711-sdw.o
+snd-soc-rt711-sdca-y := rt711-sdca.o rt711-sdca-sdw.o
+snd-soc-rt712-sdca-y := rt712-sdca.o rt712-sdca-sdw.o
+snd-soc-rt712-sdca-dmic-y := rt712-sdca-dmic.o
+snd-soc-rt715-y := rt715.o rt715-sdw.o
+snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o
+snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o
+snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o
+snd-soc-rt9120-y := rt9120.o
+snd-soc-rt9123-y := rt9123.o
+snd-soc-rt9123p-y := rt9123p.o
+snd-soc-rtq9124-y := rtq9124.o
+snd-soc-rtq9128-y := rtq9128.o
+snd-soc-sdw-mockup-y := sdw-mockup.o
+snd-soc-sgtl5000-y := sgtl5000.o
+snd-soc-alc5623-y := alc5623.o
+snd-soc-alc5632-y := alc5632.o
+snd-soc-sigmadsp-y := sigmadsp.o
+snd-soc-sigmadsp-i2c-y := sigmadsp-i2c.o
+snd-soc-sigmadsp-regmap-y := sigmadsp-regmap.o
+snd-soc-si476x-y := si476x.o
+snd-soc-sma1303-y := sma1303.o
+snd-soc-sma1307-y := sma1307.o
+snd-soc-spdif-tx-y := spdif_transmitter.o
+snd-soc-spdif-rx-y := spdif_receiver.o
+snd-soc-src4xxx-y := src4xxx.o
+snd-soc-src4xxx-i2c-y := src4xxx-i2c.o
+snd-soc-ssm2305-y := ssm2305.o
+snd-soc-ssm2518-y := ssm2518.o
+snd-soc-ssm2602-y := ssm2602.o
+snd-soc-ssm2602-spi-y := ssm2602-spi.o
+snd-soc-ssm2602-i2c-y := ssm2602-i2c.o
+snd-soc-ssm3515-y := ssm3515.o
+snd-soc-ssm4567-y := ssm4567.o
+snd-soc-sta32x-y := sta32x.o
+snd-soc-sta350-y := sta350.o
+snd-soc-sta529-y := sta529.o
+snd-soc-stac9766-y := stac9766.o
+snd-soc-sti-sas-y := sti-sas.o
+snd-soc-tas5086-y := tas5086.o
+snd-soc-tas571x-y := tas571x.o
+snd-soc-tas5720-y := tas5720.o
+snd-soc-tas5805m-y := tas5805m.o
+snd-soc-tas6424-y := tas6424.o
+snd-soc-tda7419-y := tda7419.o
+snd-soc-tas2770-y := tas2770.o
+snd-soc-tas2781-comlib-y := tas2781-comlib.o
+snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o
+snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
+snd-soc-tas2781-i2c-y := tas2781-i2c.o
+snd-soc-tas2783-sdw-y := tas2783-sdw.o
+snd-soc-tfa9879-y := tfa9879.o
+snd-soc-tfa989x-y := tfa989x.o
+snd-soc-tlv320adc3xxx-y := tlv320adc3xxx.o
+snd-soc-tlv320aic23-y := tlv320aic23.o
+snd-soc-tlv320aic23-i2c-y := tlv320aic23-i2c.o
+snd-soc-tlv320aic23-spi-y := tlv320aic23-spi.o
+snd-soc-tlv320aic26-y := tlv320aic26.o
+snd-soc-tlv320aic31xx-y := tlv320aic31xx.o
+snd-soc-tlv320aic32x4-y := tlv320aic32x4.o tlv320aic32x4-clk.o
+snd-soc-tlv320aic32x4-i2c-y := tlv320aic32x4-i2c.o
+snd-soc-tlv320aic32x4-spi-y := tlv320aic32x4-spi.o
+snd-soc-tlv320aic3x-y := tlv320aic3x.o
+snd-soc-tlv320aic3x-i2c-y := tlv320aic3x-i2c.o
+snd-soc-tlv320aic3x-spi-y := tlv320aic3x-spi.o
+snd-soc-tlv320dac33-y := tlv320dac33.o
+snd-soc-tlv320adcx140-y := tlv320adcx140.o
+snd-soc-tscs42xx-y := tscs42xx.o
+snd-soc-tscs454-y := tscs454.o
+snd-soc-ts3a227e-y := ts3a227e.o
+snd-soc-twl4030-y := twl4030.o
+snd-soc-twl6040-y := twl6040.o
+snd-soc-uda1334-y := uda1334.o
+snd-soc-uda1342-y := uda1342.o
+snd-soc-uda1380-y := uda1380.o
+snd-soc-wcd-classh-y := wcd-clsh-v2.o
+snd-soc-wcd-common-y := wcd-common.o
+snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
+snd-soc-wcd9335-y := wcd9335.o
+snd-soc-wcd934x-y := wcd934x.o
+snd-soc-wcd937x-y := wcd937x.o
+snd-soc-wcd937x-sdw-y := wcd937x-sdw.o
+snd-soc-wcd938x-y := wcd938x.o
+snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
+snd-soc-wcd939x-y := wcd939x.o
+snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
+snd-soc-wm-adsp-y := wm_adsp.o
+snd-soc-wm0010-y := wm0010.o
+snd-soc-wm1250-ev1-y := wm1250-ev1.o
+snd-soc-wm2000-y := wm2000.o
+snd-soc-wm2200-y := wm2200.o
+snd-soc-wm5100-y := wm5100.o wm5100-tables.o
+snd-soc-wm5102-y := wm5102.o
+snd-soc-wm5110-y := wm5110.o
+snd-soc-wm8350-y := wm8350.o
+snd-soc-wm8400-y := wm8400.o
+snd-soc-wm8510-y := wm8510.o
+snd-soc-wm8523-y := wm8523.o
+snd-soc-wm8524-y := wm8524.o
+snd-soc-wm8580-y := wm8580.o
+snd-soc-wm8711-y := wm8711.o
+snd-soc-wm8727-y := wm8727.o
+snd-soc-wm8728-y := wm8728.o
+snd-soc-wm8731-y := wm8731.o
+snd-soc-wm8731-i2c-y := wm8731-i2c.o
+snd-soc-wm8731-spi-y := wm8731-spi.o
+snd-soc-wm8737-y := wm8737.o
+snd-soc-wm8741-y := wm8741.o
+snd-soc-wm8750-y := wm8750.o
+snd-soc-wm8753-y := wm8753.o
+snd-soc-wm8770-y := wm8770.o
+snd-soc-wm8776-y := wm8776.o
+snd-soc-wm8782-y := wm8782.o
+snd-soc-wm8804-y := wm8804.o
+snd-soc-wm8804-i2c-y := wm8804-i2c.o
+snd-soc-wm8804-spi-y := wm8804-spi.o
+snd-soc-wm8900-y := wm8900.o
+snd-soc-wm8903-y := wm8903.o
+snd-soc-wm8904-y := wm8904.o
+snd-soc-wm8996-y := wm8996.o
+snd-soc-wm8940-y := wm8940.o
+snd-soc-wm8955-y := wm8955.o
+snd-soc-wm8960-y := wm8960.o
+snd-soc-wm8961-y := wm8961.o
+snd-soc-wm8962-y := wm8962.o
+snd-soc-wm8971-y := wm8971.o
+snd-soc-wm8974-y := wm8974.o
+snd-soc-wm8978-y := wm8978.o
+snd-soc-wm8983-y := wm8983.o
+snd-soc-wm8985-y := wm8985.o
+snd-soc-wm8988-y := wm8988.o
+snd-soc-wm8990-y := wm8990.o
+snd-soc-wm8991-y := wm8991.o
+snd-soc-wm8993-y := wm8993.o
+snd-soc-wm8994-y := wm8994.o wm8958-dsp2.o
+snd-soc-wm8995-y := wm8995.o
+snd-soc-wm8997-y := wm8997.o
+snd-soc-wm8998-y := wm8998.o
+snd-soc-wm9081-y := wm9081.o
+snd-soc-wm9090-y := wm9090.o
+snd-soc-wm9705-y := wm9705.o
+snd-soc-wm9712-y := wm9712.o
+snd-soc-wm9713-y := wm9713.o
+snd-soc-wm-hubs-y := wm_hubs.o
+snd-soc-wsa881x-y := wsa881x.o
+snd-soc-wsa883x-y := wsa883x.o
+snd-soc-wsa884x-y := wsa884x.o
+snd-soc-zl38060-y := zl38060.o
# Amp
-snd-soc-max9877-objs := max9877.o
-snd-soc-tpa6130a2-objs := tpa6130a2.o
-snd-soc-tas2552-objs := tas2552.o
+snd-soc-max9877-y := max9877.o
+snd-soc-max98504-y := max98504.o
+snd-soc-simple-amplifier-y := simple-amplifier.o
+snd-soc-tpa6130a2-y := tpa6130a2.o
+snd-soc-tas2552-y := tas2552.o
+snd-soc-tas2562-y := tas2562.o
+snd-soc-tas2764-y := tas2764.o
+snd-soc-tas2780-y := tas2780.o
+# Mux
+snd-soc-simple-mux-y := simple-mux.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@@ -209,6 +440,10 @@ obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o
+obj-$(CONFIG_SND_SOC_ADAU1372) += snd-soc-adau1372.o
+obj-$(CONFIG_SND_SOC_ADAU1372_I2C) += snd-soc-adau1372-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1372_SPI) += snd-soc-adau1372-spi.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
@@ -221,28 +456,78 @@ obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
+obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o
+obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7118_HW) += snd-soc-adau7118-hw.o
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
+obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o
+obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o
+obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o
+obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
+obj-$(CONFIG_SND_SOC_AK5558) += snd-soc-ak5558.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
+obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o
+obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o
+obj-$(CONFIG_SND_SOC_AW87390) += snd-soc-aw87390.o
+obj-$(CONFIG_SND_SOC_AW88081) += snd-soc-aw88081.o
+obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
+obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o
+obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o
+obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o
+obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o
+obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o
+obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
+obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
+obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o
+obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o
+obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o
+obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST) += snd-soc-cs-amp-lib-test.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
+obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
+obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o
+obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o
+obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o
+obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o
+obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o
+obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o
+obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
+obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o
+obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
+obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o
+obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o
+obj-$(CONFIG_SND_SOC_CS40L50) += snd-soc-cs40l50.o
+obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o
+obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
+obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o
+obj-$(CONFIG_SND_SOC_CS42L43) += snd-soc-cs42l43.o
+obj-$(CONFIG_SND_SOC_CS42L43_SDW) += snd-soc-cs42l43-sdw.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o
obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
+obj-$(CONFIG_SND_SOC_CS42L83) += snd-soc-cs42l83-i2c.o
+obj-$(CONFIG_SND_SOC_CS42L84) += snd-soc-cs42l84.o
+obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o
obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
@@ -250,72 +535,205 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
+obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o
+obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
+obj-$(CONFIG_SND_SOC_CS47L15) += snd-soc-cs47l15.o
+obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o
+obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o
+obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o
+obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o
+obj-$(CONFIG_SND_SOC_CS48L32) += snd-soc-cs48l32.o
+obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
+obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o
+obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.o
+obj-$(CONFIG_SND_SOC_CS530X_SPI) += snd-soc-cs530x-spi.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
+obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
-obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o
+obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o
+obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o
+obj-$(CONFIG_SND_SOC_ES8311) += snd-soc-es8311.o
+obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
+obj-$(CONFIG_SND_SOC_ES8323) += snd-soc-es8323.o
+obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_ES8375) += snd-soc-es8375.o
+obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o
+obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o
+obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o
+obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.o
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
+obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
+obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
+obj-$(CONFIG_SND_SOC_IDT821034) += snd-soc-idt821034.o
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
-obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
+obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o
+obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
+obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
+obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o
+obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
+obj-$(CONFIG_SND_SOC_MAX98371) += snd-soc-max98371.o
+obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
+obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
+obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
+obj-$(CONFIG_SND_SOC_MAX98520) += snd-soc-max98520.o
+obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o
+obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o
+obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o
+obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o
+obj-$(CONFIG_SND_SOC_MAX98388) += snd-soc-max98388.o
+obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o
+obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
+obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
+obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
+obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
+obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o
+obj-$(CONFIG_SND_SOC_MT6357) += snd-soc-mt6357.o
+obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o
+obj-$(CONFIG_SND_SOC_MT6359) += snd-soc-mt6359.o
+obj-$(CONFIG_SND_SOC_MT6359_ACCDET) += mt6359-accdet.o
+obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
+obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o
+obj-$(CONFIG_SND_SOC_NAU8325) += snd-soc-nau8325.o
+obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
+obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
+obj-$(CONFIG_SND_SOC_NAU8821) += snd-soc-nau8821.o
+obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o
+obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
+obj-$(CONFIG_SND_SOC_NTP8835) += snd-soc-ntp8835.o
+obj-$(CONFIG_SND_SOC_NTP8918) += snd-soc-ntp8918.o
+obj-$(CONFIG_SND_SOC_NTPFW) += snd-soc-ntpfw.o
+obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1754) += snd-soc-pcm1754.o
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
+obj-$(CONFIG_SND_SOC_PCM1789_I2C) += snd-soc-pcm1789-i2c.o
+obj-$(CONFIG_SND_SOC_PCM1789) += snd-soc-pcm1789-codec.o
+obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o
+obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o
+obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_PCM3060) += snd-soc-pcm3060.o
+obj-$(CONFIG_SND_SOC_PCM3060_I2C) += snd-soc-pcm3060-i2c.o
+obj-$(CONFIG_SND_SOC_PCM3060_SPI) += snd-soc-pcm3060-spi.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
+obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
+obj-$(CONFIG_SND_SOC_PCM6240) += snd-soc-pcm6240.o
+obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o
+obj-$(CONFIG_SND_SOC_PM4125_SDW) += snd-soc-pm4125-sdw.o
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125.o
+ifdef CONFIG_SND_SOC_PM4125_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125-sdw.o
+endif
+obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.o
+obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
+obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
+obj-$(CONFIG_SND_SOC_RT_SDW_COMMON) += snd-soc-rt-sdw-common.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
+obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
+obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o
+obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o
+obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o
+obj-$(CONFIG_SND_SOC_RT1017_SDCA_SDW) += snd-soc-rt1017-sdca.o
+obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o
+obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
+obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
+obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o
+obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o
+obj-$(CONFIG_SND_SOC_RT1318) += snd-soc-rt1318.o
+obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o
+obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-sdw.o
+obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
+obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
+obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o
+obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o
+obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o
+obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
+obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o
+obj-$(CONFIG_SND_SOC_RT5682_I2C) += snd-soc-rt5682-i2c.o
+obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o
+obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o
+obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o
+obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
+obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o
+obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o
+obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o
+obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o
+obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o
+obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o
+obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o
+obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o
+obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o
+obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o
+obj-$(CONFIG_SND_SOC_RTQ9124) += snd-soc-rtq9124.o
+obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.o
+obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
-obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
+obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
+obj-$(CONFIG_SND_SOC_SMA1307) += snd-soc-sma1307.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
+obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o
+obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o
+obj-$(CONFIG_SND_SOC_SSM2305) += snd-soc-ssm2305.o
obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o
obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o
+obj-$(CONFIG_SND_SOC_SSM3515) += snd-soc-ssm3515.o
obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
@@ -323,23 +741,65 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
+obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
+obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
+obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
+obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o
+obj-$(CONFIG_SND_SOC_TAS2781_COMLIB_I2C) += snd-soc-tas2781-comlib-i2c.o
+obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o
+obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o
+obj-$(CONFIG_SND_SOC_TAS2783_SDW) += snd-soc-tas2783-sdw.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
+obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
+obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o
+obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
+obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
+obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
+obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o
+obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X_I2C) += snd-soc-tlv320aic3x-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X_SPI) += snd-soc-tlv320aic3x-spi.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
+obj-$(CONFIG_SND_SOC_TLV320ADCX140) += snd-soc-tlv320adcx140.o
+obj-$(CONFIG_SND_SOC_TSCS42XX) += snd-soc-tscs42xx.o
+obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o
obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
-obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
+obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
+obj-$(CONFIG_SND_SOC_UDA1342) += snd-soc-uda1342.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
-obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o
+obj-$(CONFIG_SND_SOC_WCD_COMMON) += snd-soc-wcd-common.o
+obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
+obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
+obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
+obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x.o
+ifdef CONFIG_SND_SOC_WCD937X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x-sdw.o
+endif
+obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x.o
+ifdef CONFIG_SND_SOC_WCD938X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o
+endif
+obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x.o
+ifdef CONFIG_SND_SOC_WCD939X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
+endif
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
@@ -351,11 +811,14 @@ obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
+obj-$(CONFIG_SND_SOC_WM8524) += snd-soc-wm8524.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8731_I2C) += snd-soc-wm8731-i2c.o
+obj-$(CONFIG_SND_SOC_WM8731_SPI) += snd-soc-wm8731-spi.o
obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o
obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
@@ -395,7 +858,21 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
+obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o
+obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o
+obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
+obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
+obj-$(CONFIG_SND_SOC_LPASS_MACRO_COMMON) += snd-soc-lpass-macro-common.o
+obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_VA_MACRO) += snd-soc-lpass-va-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o
+
+# Mux
+obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index affb192238a4..fdda1b747bf7 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) ST-Ericsson SA 2012
*
@@ -11,12 +12,6 @@
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* Jarmo K. Kuronen <jarmo.kuronen@symbio.com>,
* for ST-Ericsson.
- *
- * License terms:
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -116,13 +111,6 @@ enum amic_idx {
AMIC_IDX_2
};
-struct ab8500_codec_drvdata_dbg {
- struct regulator *vaud;
- struct regulator *vamic1;
- struct regulator *vamic2;
- struct regulator *vdmic;
-};
-
/* Private data for AB8500 device-driver */
struct ab8500_codec_drvdata {
struct regmap *regmap;
@@ -1037,86 +1025,86 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
};
/* ANC FIR-coefficients configuration sequence */
-static void anc_fir(struct snd_soc_codec *codec,
+static void anc_fir(struct snd_soc_component *component,
unsigned int bnk, unsigned int par, unsigned int val)
{
if (par == 0 && bnk == 0)
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCFIRUPDATE),
BIT(AB8500_ANCCONF1_ANCFIRUPDATE));
- snd_soc_write(codec, AB8500_ANCCONF5, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_ANCCONF6, val & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF5, val >> 8 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF6, val & 0xff);
if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1)
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0);
}
/* ANC IIR-coefficients configuration sequence */
-static void anc_iir(struct snd_soc_codec *codec, unsigned int bnk,
+static void anc_iir(struct snd_soc_component *component, unsigned int bnk,
unsigned int par, unsigned int val)
{
if (par == 0) {
if (bnk == 0) {
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRINIT),
BIT(AB8500_ANCCONF1_ANCIIRINIT));
- usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY);
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRINIT), 0);
- usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY);
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
} else {
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRUPDATE),
BIT(AB8500_ANCCONF1_ANCIIRUPDATE));
}
} else if (par > 3) {
- snd_soc_write(codec, AB8500_ANCCONF7, 0);
- snd_soc_write(codec, AB8500_ANCCONF8, val >> 16 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF7, 0);
+ snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff);
}
- snd_soc_write(codec, AB8500_ANCCONF7, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_ANCCONF8, val & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF7, val >> 8 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF8, val & 0xff);
if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1)
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0);
}
/* ANC IIR-/FIR-coefficients configuration sequence */
-static void anc_configure(struct snd_soc_codec *codec,
+static void anc_configure(struct snd_soc_component *component,
bool apply_fir, bool apply_iir)
{
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
unsigned int bnk, par, val;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
if (apply_fir)
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ENANC), 0);
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC));
if (apply_fir)
for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) {
- val = snd_soc_read(codec,
+ val = snd_soc_component_read(component,
drvdata->anc_fir_values[par]);
- anc_fir(codec, bnk, par, val);
+ anc_fir(component, bnk, par, val);
}
if (apply_iir)
for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) {
- val = snd_soc_read(codec,
+ val = snd_soc_component_read(component,
drvdata->anc_iir_values[par]);
- anc_iir(codec, bnk, par, val);
+ anc_iir(component, bnk, par, val);
}
- dev_dbg(codec->dev, "%s: Exit.\n", __func__);
+ dev_dbg(component->dev, "%s: Exit.\n", __func__);
}
/*
@@ -1126,11 +1114,11 @@ static void anc_configure(struct snd_soc_codec *codec,
static int sid_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
mutex_lock(&drvdata->ctrl_lock);
- ucontrol->value.integer.value[0] = drvdata->sid_status;
+ ucontrol->value.enumerated.item[0] = drvdata->sid_status;
mutex_unlock(&drvdata->ctrl_lock);
return 0;
@@ -1140,15 +1128,15 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
static int sid_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
unsigned int param, sidconf, val;
int status = 1;
- dev_dbg(codec->dev, "%s: Enter\n", __func__);
+ dev_dbg(component->dev, "%s: Enter\n", __func__);
- if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) {
- dev_err(codec->dev,
+ if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) {
+ dev_err(component->dev,
"%s: ERROR: This control supports '%s' only!\n",
__func__, enum_sid_state[SID_APPLY_FIR]);
return -EIO;
@@ -1156,10 +1144,10 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
mutex_lock(&drvdata->ctrl_lock);
- sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
+ sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF);
if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) {
- dev_err(codec->dev, "%s: Sidetone busy while off!\n",
+ dev_err(component->dev, "%s: Sidetone busy while off!\n",
__func__);
status = -EPERM;
} else {
@@ -1168,18 +1156,18 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
goto out;
}
- snd_soc_write(codec, AB8500_SIDFIRADR, 0);
+ snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
- val = snd_soc_read(codec, drvdata->sid_fir_values[param]);
- snd_soc_write(codec, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_SIDFIRCOEF2, val & 0xff);
+ val = snd_soc_component_read(component, drvdata->sid_fir_values[param]);
+ snd_soc_component_write(component, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
+ snd_soc_component_write(component, AB8500_SIDFIRCOEF2, val & 0xff);
}
- snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
BIT(AB8500_SIDFIRADR_FIRSIDSET),
BIT(AB8500_SIDFIRADR_FIRSIDSET));
- snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
drvdata->sid_status = SID_FIR_CONFIGURED;
@@ -1187,7 +1175,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
out:
mutex_unlock(&drvdata->ctrl_lock);
- dev_dbg(codec->dev, "%s: Exit\n", __func__);
+ dev_dbg(component->dev, "%s: Exit\n", __func__);
return status;
}
@@ -1195,11 +1183,11 @@ out:
static int anc_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
mutex_lock(&drvdata->ctrl_lock);
- ucontrol->value.integer.value[0] = drvdata->anc_status;
+ ucontrol->value.enumerated.item[0] = drvdata->anc_status;
mutex_unlock(&drvdata->ctrl_lock);
return 0;
@@ -1208,10 +1196,10 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
static int anc_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
- struct device *dev = codec->dev;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
+ struct device *dev = component->dev;
bool apply_fir, apply_iir;
unsigned int req;
int status;
@@ -1220,7 +1208,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
mutex_lock(&drvdata->ctrl_lock);
- req = ucontrol->value.integer.value[0];
+ req = ucontrol->value.enumerated.item[0];
if (req >= ARRAY_SIZE(enum_anc_state)) {
status = -EINVAL;
goto cleanup;
@@ -1244,7 +1232,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
}
snd_soc_dapm_sync(dapm);
- anc_configure(codec, apply_fir, apply_iir);
+ anc_configure(component, apply_fir, apply_iir);
if (apply_fir) {
if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1291,8 +1279,8 @@ static int filter_control_info(struct snd_kcontrol *kcontrol,
static int filter_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component);
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
unsigned int i;
@@ -1308,8 +1296,8 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
static int filter_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component);
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
unsigned int i;
@@ -1926,11 +1914,11 @@ enum ab8500_filter {
* Extended interface for codec-driver
*/
-static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
+static int ab8500_audio_init_audioblock(struct snd_soc_component *component)
{
int status;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
/* Reset audio-registers and disable 32kHz-clock output 2 */
status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
@@ -1943,26 +1931,26 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
return 0;
}
-static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
+static int ab8500_audio_setup_mics(struct snd_soc_component *component,
struct amic_settings *amics)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u8 value8;
unsigned int value;
int status;
const struct snd_soc_dapm_route *route;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
/* Set DMic-clocks to outputs */
- status = abx500_get_register_interruptible(codec->dev, AB8500_MISC,
+ status = abx500_get_register_interruptible(component->dev, AB8500_MISC,
AB8500_GPIO_DIR4_REG,
&value8);
if (status < 0)
return status;
value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
GPIO31_DIR_OUTPUT;
- status = abx500_set_register_interruptible(codec->dev,
+ status = abx500_set_register_interruptible(component->dev,
AB8500_MISC,
AB8500_GPIO_DIR4_REG,
value);
@@ -1970,41 +1958,41 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
return status;
/* Attach regulators to AMic DAPM-paths */
- dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 1a regulator: %s\n", __func__,
amic_micbias_str(amics->mic1a_micbias));
route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias];
status = snd_soc_dapm_add_routes(dapm, route, 1);
- dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 1b regulator: %s\n", __func__,
amic_micbias_str(amics->mic1b_micbias));
route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias];
status |= snd_soc_dapm_add_routes(dapm, route, 1);
- dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 2 regulator: %s\n", __func__,
amic_micbias_str(amics->mic2_micbias));
route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias];
status |= snd_soc_dapm_add_routes(dapm, route, 1);
if (status < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
__func__, status);
return status;
}
/* Set AMic-configuration */
- dev_dbg(codec->dev, "%s: Mic 1 mic-type: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 1 mic-type: %s\n", __func__,
amic_type_str(amics->mic1_type));
- snd_soc_update_bits(codec, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ?
0 : AB8500_ANAGAINX_ENSEMICX);
- dev_dbg(codec->dev, "%s: Mic 2 mic-type: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 2 mic-type: %s\n", __func__,
amic_type_str(amics->mic2_type));
- snd_soc_update_bits(codec, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ?
0 : AB8500_ANAGAINX_ENSEMICX);
return 0;
}
-static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
+static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,
enum ear_cm_voltage ear_cmv)
{
char *cmv_str;
@@ -2023,14 +2011,14 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
cmv_str = "1.58V";
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: Unknown earpiece CM-voltage (%d)!\n",
__func__, (int)ear_cmv);
return -EINVAL;
}
- dev_dbg(codec->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
cmv_str);
- snd_soc_update_bits(codec, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
+ snd_soc_component_update_bits(component, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
ear_cmv);
return 0;
@@ -2040,7 +2028,7 @@ static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
unsigned int delay)
{
unsigned int mask, val;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
val = 0;
@@ -2052,21 +2040,21 @@ static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported bit-delay (0x%x)!\n",
__func__, delay);
return -EINVAL;
}
- dev_dbg(dai->codec->dev, "%s: IF0 Bit-delay: %d bits.\n",
+ dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n",
__func__, delay);
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
return 0;
}
/* Gates clocking according format mask */
-static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec,
+static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component,
unsigned int fmt)
{
unsigned int mask;
@@ -2079,22 +2067,22 @@ static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec,
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
case SND_SOC_DAIFMT_CONT: /* continuous clock */
- dev_dbg(codec->dev, "%s: IF0 Clock is continuous.\n",
+ dev_dbg(component->dev, "%s: IF0 Clock is continuous.\n",
__func__);
val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
break;
case SND_SOC_DAIFMT_GATED: /* clock is gated */
- dev_dbg(codec->dev, "%s: IF0 Clock is gated.\n",
+ dev_dbg(component->dev, "%s: IF0 Clock is gated.\n",
__func__);
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: ERROR: Unsupported clock mask (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
return 0;
}
@@ -2103,10 +2091,10 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
unsigned int mask;
unsigned int val;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int status;
- dev_dbg(codec->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
+ dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
@@ -2114,35 +2102,35 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
BIT(AB8500_DIGIFCONF3_IF0MASTER);
val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */
- dev_dbg(dai->codec->dev,
- "%s: IF0 Master-mode: AB8500 master.\n", __func__);
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Master-mode: AB8500 provider.\n", __func__);
val |= BIT(AB8500_DIGIFCONF3_IF0MASTER);
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & FRM slave */
- dev_dbg(dai->codec->dev,
- "%s: IF0 Master-mode: AB8500 slave.\n", __func__);
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Master-mode: AB8500 consumer.\n", __func__);
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
- dev_err(dai->codec->dev,
- "%s: ERROR: The device is either a master or a slave.\n",
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ dev_err(dai->component->dev,
+ "%s: ERROR: The device is either a provider or a consumer.\n",
__func__);
+ fallthrough;
default:
- dev_err(dai->codec->dev,
- "%s: ERROR: Unsupporter master mask 0x%x\n",
- __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupporter clocking mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
return -EINVAL;
- break;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val);
/* Set clock gating */
- status = ab8500_codec_set_dai_clock_gate(codec, fmt);
+ status = ab8500_codec_set_dai_clock_gate(component, fmt);
if (status) {
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Failed to set clock gate (%d).\n",
__func__, status);
return status;
@@ -2158,27 +2146,27 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: /* I2S mode */
- dev_dbg(dai->codec->dev, "%s: IF0 Protocol: I2S\n", __func__);
+ dev_dbg(dai->component->dev, "%s: IF0 Protocol: I2S\n", __func__);
val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1);
ab8500_audio_set_bit_delay(dai, 0);
break;
case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: IF0 Protocol: DSP A (TDM)\n", __func__);
val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
ab8500_audio_set_bit_delay(dai, 1);
break;
case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: IF0 Protocol: DSP B (TDM)\n", __func__);
val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
ab8500_audio_set_bit_delay(dai, 0);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported format (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
@@ -2186,37 +2174,37 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: IF0: Normal bit clock, normal frame\n",
__func__);
break;
case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: IF0: Normal bit clock, inverted frame\n",
__func__);
val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
break;
case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: IF0: Inverted bit clock, normal frame\n",
__func__);
val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
break;
case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: IF0: Inverted bit clock, inverted frame\n",
__func__);
val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported INV mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
return 0;
}
@@ -2225,7 +2213,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val, mask, slot, slots_active;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
@@ -2246,17 +2234,17 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
BIT(AB8500_DIGIFCONF2_IF0WL0);
break;
default:
- dev_err(dai->codec->dev, "%s: Unsupported slot-width 0x%x\n",
+ dev_err(dai->component->dev, "%s: Unsupported slot-width 0x%x\n",
__func__, slot_width);
return -EINVAL;
}
- dev_dbg(dai->codec->dev, "%s: IF0 slot-width: %d bits.\n",
+ dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n",
__func__, slot_width);
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
/* Setup TDM clocking according to slot count */
- dev_dbg(dai->codec->dev, "%s: Slots, total: %d\n", __func__, slots);
+ dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots);
mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
switch (slots) {
@@ -2274,12 +2262,12 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported number of slots (%d)!\n",
__func__, slots);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
/* Setup TDM DA according to active tx slots */
@@ -2290,7 +2278,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
slots_active = hweight32(tx_mask);
- dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
+ dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__,
slots_active);
switch (slots_active) {
@@ -2298,26 +2286,26 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
case 1:
slot = ffs(tx_mask);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot);
break;
case 2:
slot = ffs(tx_mask);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot);
slot = fls(tx_mask);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot);
break;
case 8:
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: In 8-channel mode DA-from-slot mapping is set manually.",
__func__);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: Unsupported number of active TX-slots (%d)!\n",
__func__, slots_active);
return -EINVAL;
@@ -2331,7 +2319,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
slots_active = hweight32(rx_mask);
- dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
+ dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__,
slots_active);
switch (slots_active) {
@@ -2339,29 +2327,29 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
case 1:
slot = ffs(rx_mask);
- snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot),
+ snd_soc_component_update_bits(component, AB8500_ADSLOTSEL(slot),
AB8500_MASK_SLOT(slot),
AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
break;
case 2:
slot = ffs(rx_mask);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
AB8500_ADSLOTSEL(slot),
AB8500_MASK_SLOT(slot),
AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
slot = fls(rx_mask);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
AB8500_ADSLOTSEL(slot),
AB8500_MASK_SLOT(slot),
AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot));
break;
case 8:
- dev_dbg(dai->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: In 8-channel mode AD-to-slot mapping is set manually.",
__func__);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: Unsupported number of active RX-slots (%d)!\n",
__func__, slots_active);
return -EINVAL;
@@ -2387,7 +2375,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.formats = AB8500_SUPPORTED_FMT,
},
.ops = &ab8500_codec_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
},
{
.name = "ab8500-codec-dai.1",
@@ -2400,7 +2388,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.formats = AB8500_SUPPORTED_FMT,
},
.ops = &ab8500_codec_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
}
};
@@ -2409,28 +2397,28 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
{
u32 value;
- if (of_get_property(np, "stericsson,amic1-type-single-ended", NULL))
+ if (of_property_read_bool(np, "stericsson,amic1-type-single-ended"))
codec->amics.mic1_type = AMIC_TYPE_SINGLE_ENDED;
else
codec->amics.mic1_type = AMIC_TYPE_DIFFERENTIAL;
- if (of_get_property(np, "stericsson,amic2-type-single-ended", NULL))
+ if (of_property_read_bool(np, "stericsson,amic2-type-single-ended"))
codec->amics.mic2_type = AMIC_TYPE_SINGLE_ENDED;
else
codec->amics.mic2_type = AMIC_TYPE_DIFFERENTIAL;
/* Has a non-standard Vamic been requested? */
- if (of_get_property(np, "stericsson,amic1a-bias-vamic2", NULL))
+ if (of_property_read_bool(np, "stericsson,amic1a-bias-vamic2"))
codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC2;
else
codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC1;
- if (of_get_property(np, "stericsson,amic1b-bias-vamic2", NULL))
+ if (of_property_read_bool(np, "stericsson,amic1b-bias-vamic2"))
codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC2;
else
codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC1;
- if (of_get_property(np, "stericsson,amic2-bias-vamic1", NULL))
+ if (of_property_read_bool(np, "stericsson,amic2-bias-vamic1"))
codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1;
else
codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2;
@@ -2459,58 +2447,33 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
}
}
-static int ab8500_codec_probe(struct snd_soc_codec *codec)
+static int ab8500_codec_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct device *dev = codec->dev;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct device *dev = component->dev;
struct device_node *np = dev->of_node;
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
- struct ab8500_platform_data *pdata;
+ struct ab8500_codec_platform_data codec_pdata;
struct filter_control *fc;
int status;
dev_dbg(dev, "%s: Enter.\n", __func__);
- /* Setup AB8500 according to board-settings */
- pdata = dev_get_platdata(dev->parent);
+ ab8500_codec_of_probe(dev, np, &codec_pdata);
- 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);
+ status = ab8500_audio_setup_mics(component, &codec_pdata.amics);
if (status < 0) {
pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
return status;
}
- status = ab8500_audio_set_ear_cmv(codec, pdata->codec->ear_cmv);
+ status = ab8500_audio_set_ear_cmv(component, codec_pdata.ear_cmv);
if (status < 0) {
pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n",
__func__, status);
return status;
}
- status = ab8500_audio_init_audioblock(codec);
+ status = ab8500_audio_init_audioblock(component);
if (status < 0) {
dev_err(dev, "%s: failed to init audio-block (%d)!\n",
__func__, status);
@@ -2518,13 +2481,13 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
}
/* Override HW-defaults */
- snd_soc_write(codec, AB8500_ANACONF5,
+ snd_soc_component_write(component, AB8500_ANACONF5,
BIT(AB8500_ANACONF5_HSAUTOEN));
- snd_soc_write(codec, AB8500_SHORTCIRCONF,
+ snd_soc_component_write(component, AB8500_SHORTCIRCONF,
BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
/* Add filter controls */
- status = snd_soc_add_codec_controls(codec, ab8500_filter_controls,
+ status = snd_soc_add_component_controls(component, ab8500_filter_controls,
ARRAY_SIZE(ab8500_filter_controls));
if (status < 0) {
dev_err(dev,
@@ -2549,14 +2512,17 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
return status;
}
-static struct snd_soc_codec_driver ab8500_codec_driver = {
- .probe = ab8500_codec_probe,
- .controls = ab8500_ctrls,
- .num_controls = ARRAY_SIZE(ab8500_ctrls),
- .dapm_widgets = ab8500_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets),
- .dapm_routes = ab8500_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes),
+static const struct snd_soc_component_driver ab8500_component_driver = {
+ .probe = ab8500_codec_probe,
+ .controls = ab8500_ctrls,
+ .num_controls = ARRAY_SIZE(ab8500_ctrls),
+ .dapm_widgets = ab8500_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets),
+ .dapm_routes = ab8500_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ab8500_codec_driver_probe(struct platform_device *pdev)
@@ -2585,7 +2551,8 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
- status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &ab8500_component_driver,
ab8500_codec_dai,
ARRAY_SIZE(ab8500_codec_dai));
if (status < 0)
@@ -2596,24 +2563,13 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
return status;
}
-static int ab8500_codec_driver_remove(struct platform_device *pdev)
-{
- dev_dbg(&pdev->dev, "%s Enter.\n", __func__);
-
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver ab8500_codec_platform_driver = {
.driver = {
.name = "ab8500-codec",
},
.probe = ab8500_codec_driver_probe,
- .remove = ab8500_codec_driver_remove,
- .suspend = NULL,
- .resume = NULL,
};
module_platform_driver(ab8500_codec_platform_driver);
+MODULE_DESCRIPTION("ASoC AB8500 codec driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h
index e2e54425d25e..2a6f6409f1f8 100644
--- a/sound/soc/codecs/ab8500-codec.h
+++ b/sound/soc/codecs/ab8500-codec.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) ST-Ericsson SA 2012
*
@@ -10,12 +11,6 @@
* Mikko J. Lehto <mikko.lehto@symbio.com>,
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* for ST-Ericsson.
- *
- * License terms:
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#ifndef AB8500_CODEC_REGISTERS_H
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 5b3224c63943..d8444a083af2 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ac97.c -- ALSA Soc AC97 codec support
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Generic AC97 support.
*/
@@ -17,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -36,8 +33,8 @@ static const struct snd_soc_dapm_route ac97_routes[] = {
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
@@ -65,7 +62,7 @@ static struct snd_soc_dai_driver ac97_dai = {
.ops = &ac97_dai_ops,
};
-static int ac97_soc_probe(struct snd_soc_codec *codec)
+static int ac97_soc_probe(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
struct snd_ac97_bus *ac97_bus;
@@ -73,7 +70,7 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
+ ret = snd_ac97_bus(component->card->snd_card, 0, soc_ac97_ops,
NULL, &ac97_bus);
if (ret < 0)
return ret;
@@ -83,25 +80,25 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_set_drvdata(component, ac97);
return 0;
}
#ifdef CONFIG_PM
-static int ac97_soc_suspend(struct snd_soc_codec *codec)
+static int ac97_soc_suspend(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
snd_ac97_suspend(ac97);
return 0;
}
-static int ac97_soc_resume(struct snd_soc_codec *codec)
+static int ac97_soc_resume(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
snd_ac97_resume(ac97);
@@ -112,36 +109,40 @@ static int ac97_soc_resume(struct snd_soc_codec *codec)
#define ac97_soc_resume NULL
#endif
-static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
- .probe = ac97_soc_probe,
- .suspend = ac97_soc_suspend,
- .resume = ac97_soc_resume,
-
- .dapm_widgets = ac97_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
- .dapm_routes = ac97_routes,
- .num_dapm_routes = ARRAY_SIZE(ac97_routes),
+static const struct snd_soc_component_driver soc_component_dev_ac97 = {
+ .probe = ac97_soc_probe,
+ .suspend = ac97_soc_suspend,
+ .resume = ac97_soc_resume,
+ .dapm_widgets = ac97_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
+ .dapm_routes = ac97_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac97_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ac97_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ac97, &ac97_dai, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ac97, &ac97_dai, 1);
}
-static int ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
+#ifdef CONFIG_OF
+static const struct of_device_id ac97_codec_of_match[] = {
+ { .compatible = "realtek,alc203", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac97_codec_of_match);
+#endif
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
+ .of_match_table = of_match_ptr(ac97_codec_of_match),
},
.probe = ac97_probe,
- .remove = ac97_remove,
};
module_platform_driver(ac97_codec_driver);
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index e2ce6c4d7ece..8afeadcaf8b0 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
@@ -149,9 +148,9 @@ static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- /* ALCLK,ABCLK are both output, AD1836 can only be master */
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ /* ALCLK,ABCLK are both output, AD1836 can only be provider */
+ case SND_SOC_DAIFMT_CBP_CFP:
break;
default:
return -EINVAL;
@@ -164,7 +163,7 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(dai->component);
int word_len = 0;
/* bit size */
@@ -228,17 +227,17 @@ static struct snd_soc_dai_driver ad183x_dais[] = {
};
#ifdef CONFIG_PM
-static int ad1836_suspend(struct snd_soc_codec *codec)
+static int ad1836_suspend(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* reset clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
-static int ad1836_resume(struct snd_soc_codec *codec)
+static int ad1836_resume(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* restore clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
@@ -248,10 +247,10 @@ static int ad1836_resume(struct snd_soc_codec *codec)
#define ad1836_resume NULL
#endif
-static int ad1836_probe(struct snd_soc_codec *codec)
+static int ad1836_probe(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int num_dacs, num_adcs;
int ret = 0;
int i;
@@ -277,7 +276,7 @@ static int ad1836_probe(struct snd_soc_codec *codec)
if (ad1836->type == AD1836) {
/* left/right diff:PGA/MUX */
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
- ret = snd_soc_add_codec_controls(codec, ad1836_controls,
+ ret = snd_soc_add_component_controls(component, ad1836_controls,
ARRAY_SIZE(ad1836_controls));
if (ret)
return ret;
@@ -285,11 +284,11 @@ static int ad1836_probe(struct snd_soc_codec *codec)
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
}
- ret = snd_soc_add_codec_controls(codec, ad183x_dac_controls, num_dacs * 2);
+ ret = snd_soc_add_component_controls(component, ad183x_dac_controls, num_dacs * 2);
if (ret)
return ret;
- ret = snd_soc_add_codec_controls(codec, ad183x_adc_controls, num_adcs);
+ ret = snd_soc_add_component_controls(component, ad183x_adc_controls, num_adcs);
if (ret)
return ret;
@@ -306,33 +305,33 @@ static int ad1836_probe(struct snd_soc_codec *codec)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
- if (ret)
- return ret;
return ret;
}
/* power down chip */
-static int ad1836_remove(struct snd_soc_codec *codec)
+static void ad1836_remove(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* reset clock control mode */
- return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
-static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
- .probe = ad1836_probe,
- .remove = ad1836_remove,
- .suspend = ad1836_suspend,
- .resume = ad1836_resume,
-
- .controls = ad183x_controls,
- .num_controls = ARRAY_SIZE(ad183x_controls),
- .dapm_widgets = ad183x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
- .dapm_routes = ad183x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_ad1836 = {
+ .probe = ad1836_probe,
+ .remove = ad1836_remove,
+ .suspend = ad1836_suspend,
+ .resume = ad1836_resume,
+ .controls = ad183x_controls,
+ .num_controls = ARRAY_SIZE(ad183x_controls),
+ .dapm_widgets = ad183x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
+ .dapm_routes = ad183x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct reg_default ad1836_reg_defaults[] = {
@@ -359,7 +358,7 @@ static const struct regmap_config ad1836_regmap_config = {
.max_register = AD1836_ADC_CTRL3,
.reg_defaults = ad1836_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int ad1836_spi_probe(struct spi_device *spi)
@@ -380,17 +379,11 @@ static int ad1836_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ad1836);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_ad1836, &ad183x_dais[ad1836->type], 1);
return ret;
}
-static int ad1836_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id ad1836_ids[] = {
{ "ad1835", AD1835 },
{ "ad1836", AD1836 },
@@ -406,7 +399,6 @@ static struct spi_driver ad1836_spi_driver = {
.name = "ad1836",
},
.probe = ad1836_spi_probe,
- .remove = ad1836_spi_remove,
.id_table = ad1836_ids,
};
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index dd7be0dbbc58..05711fab687a 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __AD1836_H__
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index 171313664bc8..6aa168e01fbb 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD1936/AD1937 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -21,8 +20,7 @@ static const struct i2c_device_id ad193x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
-static int ad193x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ad193x_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -32,21 +30,14 @@ static int ad193x_i2c_probe(struct i2c_client *client,
return ad193x_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- (enum ad193x_type)id->driver_data);
-}
-
-static int ad193x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ (uintptr_t)i2c_get_match_data(client));
}
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
- .probe = ad193x_i2c_probe,
- .remove = ad193x_i2c_remove,
+ .probe = ad193x_i2c_probe,
.id_table = ad193x_id,
};
module_i2c_driver(ad193x_i2c_driver);
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
index 23c28573bdb7..bce96a3d81c9 100644
--- a/sound/soc/codecs/ad193x-spi.c
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD1938/AD1939 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -29,12 +28,6 @@ static int ad193x_spi_probe(struct spi_device *spi)
(enum ad193x_type)id->driver_data);
}
-static int ad193x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id ad193x_spi_id[] = {
{ "ad193x", AD193X },
{ "ad1933", AD1933 },
@@ -51,7 +44,6 @@ static struct spi_driver ad193x_spi_driver = {
.name = "ad193x",
},
.probe = ad193x_spi_probe,
- .remove = ad193x_spi_remove,
.id_table = ad193x_spi_id,
};
module_spi_driver(ad193x_spi_driver);
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 3a3f3f2343d7..b93531c3a9a4 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD193X Audio Codec driver supporting AD1936/7/8/9
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -37,6 +36,13 @@ static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
+static const unsigned int ad193x_sb[] = {32};
+
+static struct snd_pcm_hw_constraint_list constr = {
+ .list = ad193x_sb,
+ .count = ARRAY_SIZE(ad193x_sb),
+};
+
static const struct snd_kcontrol_new ad193x_snd_controls[] = {
/* DAC volume control */
SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL,
@@ -93,6 +99,15 @@ static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
SND_SOC_DAPM_INPUT("ADC2IN"),
};
+static int ad193x_check_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+
+ return !!ad193x->sysclk;
+}
+
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
{ "DAC Output", NULL, "DAC" },
@@ -101,7 +116,7 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC2OUT", NULL, "DAC Output" },
{ "DAC3OUT", NULL, "DAC Output" },
{ "DAC4OUT", NULL, "DAC Output" },
- { "SYSCLK", NULL, "PLL_PWR" },
+ { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll },
};
static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
@@ -128,9 +143,9 @@ static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
* DAI ops entries
*/
-static int ad193x_mute(struct snd_soc_dai *dai, int mute)
+static int ad193x_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
if (mute)
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
@@ -146,7 +161,7 @@ static int ad193x_mute(struct snd_soc_dai *dai, int mute)
static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
unsigned int channels;
switch (slots) {
@@ -179,25 +194,28 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec_dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component);
unsigned int adc_serfmt = 0;
+ unsigned int dac_serfmt = 0;
unsigned int adc_fmt = 0;
unsigned int dac_fmt = 0;
/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
- * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+ * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode
+ * (SND_SOC_DAIFMT_I2S)
*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
adc_serfmt |= AD193X_ADC_SERFMT_TDM;
+ dac_serfmt |= AD193X_DAC_SERFMT_STEREO;
break;
case SND_SOC_DAIFMT_DSP_A:
adc_serfmt |= AD193X_ADC_SERFMT_AUX;
+ dac_serfmt |= AD193X_DAC_SERFMT_TDM;
break;
default:
if (ad193x_has_adc(ad193x))
return -EINVAL;
- break;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -221,22 +239,26 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ /* For DSP_*, LRCLK's polarity must be inverted */
+ if (fmt & SND_SOC_DAIFMT_DSP_A)
+ dac_fmt ^= AD193X_DAC_LEFT_HIGH;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
adc_fmt |= AD193X_ADC_LCR_MASTER;
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
+ case SND_SOC_DAIFMT_CBC_CFP:
adc_fmt |= AD193X_ADC_LCR_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ case SND_SOC_DAIFMT_CBP_CFC:
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -248,6 +270,8 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
AD193X_ADC_FMT_MASK, adc_fmt);
}
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0,
+ AD193X_DAC_SERFMT_MASK, dac_serfmt);
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_FMT_MASK, dac_fmt);
@@ -257,8 +281,23 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+
+ if (clk_id == AD193X_SYSCLK_MCLK) {
+ /* MCLK must be 512 x fs */
+ if (dir == SND_SOC_CLOCK_OUT || freq != 24576000)
+ return -EINVAL;
+
+ regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1,
+ AD193X_PLL_SRC_MASK,
+ AD193X_PLL_DAC_SRC_MCLK |
+ AD193X_PLL_CLK_SRC_MCLK);
+
+ snd_soc_dapm_sync(dapm);
+ return 0;
+ }
switch (freq) {
case 12288000:
case 18432000:
@@ -275,8 +314,15 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int word_len = 0, master_rate = 0;
- struct snd_soc_codec *codec = dai->codec;
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+ bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u8 dacc0;
+
+ dev_dbg(dai->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
+ __func__, params_rate(params), params_format(params),
+ params_width(params), params_channels(params));
+
/* bit size */
switch (params_width(params)) {
@@ -307,6 +353,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
break;
}
+ if (is_playback) {
+ switch (params_rate(params)) {
+ case 48000:
+ dacc0 = AD193X_DAC_SR_48;
+ break;
+ case 96000:
+ dacc0 = AD193X_DAC_SR_96;
+ break;
+ case 192000:
+ dacc0 = AD193X_DAC_SR_192;
+ break;
+ default:
+ dev_err(dai->dev, "invalid sampling rate: %d\n", params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, AD193X_DAC_SR_MASK, dacc0);
+ }
+
regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0,
AD193X_PLL_INPUT_MASK, master_rate);
@@ -321,12 +386,22 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int ad193x_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constr);
+}
+
static const struct snd_soc_dai_ops ad193x_dai_ops = {
+ .startup = ad193x_startup,
.hw_params = ad193x_hw_params,
- .digital_mute = ad193x_mute,
+ .mute_stream = ad193x_mute,
.set_tdm_slot = ad193x_set_tdm_slot,
.set_sysclk = ad193x_set_dai_sysclk,
.set_fmt = ad193x_set_dai_fmt,
+ .no_capture_mute = 1,
};
/* codec DAI instance */
@@ -336,7 +411,7 @@ static struct snd_soc_dai_driver ad193x_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
@@ -351,38 +426,67 @@ static struct snd_soc_dai_driver ad193x_dai = {
.ops = &ad193x_dai_ops,
};
-static int ad193x_codec_probe(struct snd_soc_codec *codec)
-{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- int num, ret;
-
- /* default setting for ad193x */
+/* codec DAI instance for DAC only */
+static struct snd_soc_dai_driver ad193x_no_adc_dai = {
+ .name = "ad193x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ad193x_dai_ops,
+};
- /* 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);
+/* codec register values to set after reset */
+static void ad193x_reg_default_init(struct ad193x_priv *ad193x)
+{
+ static const struct reg_sequence reg_init[] = {
+ { 0, 0x99 }, /* PLL_CLK_CTRL0: pll input: mclki/xi 12.288Mhz */
+ { 1, 0x04 }, /* PLL_CLK_CTRL1: no on-chip Vref */
+ { 2, 0x40 }, /* DAC_CTRL0: TDM mode */
+ { 3, 0x00 }, /* DAC_CTRL1: reset */
+ { 4, 0x1A }, /* DAC_CTRL2: 48kHz de-emphasis, unmute dac */
+ { 5, 0x00 }, /* DAC_CHNL_MUTE: unmute DAC channels */
+ { 6, 0x00 }, /* DAC_L1_VOL: no attenuation */
+ { 7, 0x00 }, /* DAC_R1_VOL: no attenuation */
+ { 8, 0x00 }, /* DAC_L2_VOL: no attenuation */
+ { 9, 0x00 }, /* DAC_R2_VOL: no attenuation */
+ { 10, 0x00 }, /* DAC_L3_VOL: no attenuation */
+ { 11, 0x00 }, /* DAC_R3_VOL: no attenuation */
+ { 12, 0x00 }, /* DAC_L4_VOL: no attenuation */
+ { 13, 0x00 }, /* DAC_R4_VOL: no attenuation */
+ };
+ static const struct reg_sequence reg_adc_init[] = {
+ { 14, 0x03 }, /* ADC_CTRL0: high-pass filter enable */
+ { 15, 0x43 }, /* ADC_CTRL1: sata delay=1, adc aux mode */
+ { 16, 0x00 }, /* ADC_CTRL2: reset */
+ };
+
+ regmap_multi_reg_write(ad193x->regmap, reg_init, ARRAY_SIZE(reg_init));
- /* adc only */
if (ad193x_has_adc(ad193x)) {
- /* 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);
+ regmap_multi_reg_write(ad193x->regmap, reg_adc_init,
+ ARRAY_SIZE(reg_adc_init));
}
+}
+
+static int ad193x_component_probe(struct snd_soc_component *component)
+{
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int num, ret;
- /* pll input: mclki/xi */
- regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
- regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
+ /* default setting for ad193x */
+ ad193x_reg_default_init(ad193x);
/* adc only */
if (ad193x_has_adc(ad193x)) {
/* add adc controls */
num = ARRAY_SIZE(ad193x_adc_snd_controls);
- ret = snd_soc_add_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
ad193x_adc_snd_controls,
num);
if (ret)
@@ -408,14 +512,17 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
- .probe = ad193x_codec_probe,
- .controls = ad193x_snd_controls,
- .num_controls = ARRAY_SIZE(ad193x_snd_controls),
- .dapm_widgets = ad193x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets),
- .dapm_routes = audio_paths,
- .num_dapm_routes = ARRAY_SIZE(audio_paths),
+static const struct snd_soc_component_driver soc_component_dev_ad193x = {
+ .probe = ad193x_component_probe,
+ .controls = ad193x_snd_controls,
+ .num_controls = ARRAY_SIZE(ad193x_snd_controls),
+ .dapm_widgets = ad193x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
const struct regmap_config ad193x_regmap_config = {
@@ -440,8 +547,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, ad193x);
- return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
- &ad193x_dai, 1);
+ if (ad193x_has_adc(ad193x))
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x,
+ &ad193x_dai, 1);
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x,
+ &ad193x_no_adc_dai, 1);
}
EXPORT_SYMBOL_GPL(ad193x_probe);
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index 8b1e65f928d2..61f4648861d5 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AD193X Audio Codec driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __AD193X_H__
@@ -31,8 +30,17 @@ int ad193x_probe(struct device *dev, struct regmap *regmap,
#define AD193X_PLL_INPUT_512 (2 << 1)
#define AD193X_PLL_INPUT_768 (3 << 1)
#define AD193X_PLL_CLK_CTRL1 0x01
+#define AD193X_PLL_SRC_MASK 0x03
+#define AD193X_PLL_DAC_SRC_PLL 0
+#define AD193X_PLL_DAC_SRC_MCLK 1
+#define AD193X_PLL_CLK_SRC_PLL (0 << 1)
+#define AD193X_PLL_CLK_SRC_MCLK (1 << 1)
#define AD193X_DAC_CTRL0 0x02
#define AD193X_DAC_POWERDOWN 0x01
+#define AD193X_DAC_SR_MASK 0x06
+#define AD193X_DAC_SR_48 (0 << 1)
+#define AD193X_DAC_SR_96 (1 << 1)
+#define AD193X_DAC_SR_192 (2 << 1)
#define AD193X_DAC_SERFMT_MASK 0xC0
#define AD193X_DAC_SERFMT_STEREO (0 << 6)
#define AD193X_DAC_SERFMT_TDM (1 << 6)
@@ -96,4 +104,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap,
#define AD193X_NUM_REGS 17
+#define AD193X_SYSCLK_PLL 0
+#define AD193X_SYSCLK_MCLK 1
+
#endif
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 9ef20dbccbe3..3c1ae13c1aae 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ad1980.c -- ALSA Soc AD1980 codec support
*
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices Inc.
* Author: Roy Huang <roy.huang@analog.com>
* Cliff Cai <cliff.cai@analog.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
/*
@@ -96,7 +92,7 @@ static const struct regmap_config ad1980_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = ad1980_readable_reg,
@@ -205,9 +201,9 @@ static struct snd_soc_dai_driver ad1980_dai = {
#define AD1980_VENDOR_ID 0x41445300
#define AD1980_VENDOR_MASK 0xffffff00
-static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+static int ad1980_reset(struct snd_soc_component *component, int try_warm)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
unsigned int retry_cnt = 0;
int ret;
@@ -223,16 +219,16 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
* case the first nibble of data is eaten by the addr. (Tag is
* always 16 bit)
*/
- snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+ snd_soc_component_write(component, AC97_AD_SERIAL_CFG, 0x9900);
} while (retry_cnt++ < 10);
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+ dev_err(component->dev, "Failed to reset: AC97 link error\n");
return -EIO;
}
-static int ad1980_soc_probe(struct snd_soc_codec *codec)
+static int ad1980_soc_probe(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
struct regmap *regmap;
@@ -240,10 +236,10 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
u16 vendor_id2;
u16 ext_status;
- ac97 = snd_soc_new_ac97_codec(codec, 0, 0);
+ ac97 = snd_soc_new_ac97_component(component, 0, 0);
if (IS_ERR(ac97)) {
ret = PTR_ERR(ac97);
- dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
+ dev_err(component->dev, "Failed to register AC97 component: %d\n", ret);
return ret;
}
@@ -253,70 +249,65 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
goto err_free_ac97;
}
- snd_soc_codec_init_regmap(codec, regmap);
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_init_regmap(component, regmap);
+ snd_soc_component_set_drvdata(component, ac97);
- ret = ad1980_reset(codec, 0);
+ ret = ad1980_reset(component, 0);
if (ret < 0)
goto reset_err;
- vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
+ vendor_id2 = snd_soc_component_read(component, AC97_VENDOR_ID2);
if (vendor_id2 == 0x5374) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Found AD1981 - only 2/2 IN/OUT Channels supported\n");
}
/* unmute captures and playbacks volume */
- snd_soc_write(codec, AC97_MASTER, 0x0000);
- snd_soc_write(codec, AC97_PCM, 0x0000);
- snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
- snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
- snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ snd_soc_component_write(component, AC97_MASTER, 0x0000);
+ snd_soc_component_write(component, AC97_PCM, 0x0000);
+ snd_soc_component_write(component, AC97_REC_GAIN, 0x0000);
+ snd_soc_component_write(component, AC97_CENTER_LFE_MASTER, 0x0000);
+ snd_soc_component_write(component, AC97_SURROUND_MASTER, 0x0000);
/*power on LFE/CENTER/Surround DACs*/
- ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
- snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
+ ext_status = snd_soc_component_read(component, AC97_EXTENDED_STATUS);
+ snd_soc_component_write(component, AC97_EXTENDED_STATUS, ext_status&~0x3800);
return 0;
reset_err:
- snd_soc_codec_exit_regmap(codec);
+ snd_soc_component_exit_regmap(component);
err_free_ac97:
- snd_soc_free_ac97_codec(ac97);
+ snd_soc_free_ac97_component(ac97);
return ret;
}
-static int ad1980_soc_remove(struct snd_soc_codec *codec)
+static void ad1980_soc_remove(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
- snd_soc_codec_exit_regmap(codec);
- snd_soc_free_ac97_codec(ac97);
- return 0;
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(ac97);
}
-static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
- .probe = ad1980_soc_probe,
- .remove = ad1980_soc_remove,
-
- .controls = ad1980_snd_ac97_controls,
- .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
- .dapm_widgets = ad1980_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
- .dapm_routes = ad1980_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_ad1980 = {
+ .probe = ad1980_soc_probe,
+ .remove = ad1980_soc_remove,
+ .controls = ad1980_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
+ .dapm_widgets = ad1980_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
+ .dapm_routes = ad1980_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ad1980_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad1980, &ad1980_dai, 1);
-}
-
-static int ad1980_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ad1980, &ad1980_dai, 1);
}
static struct platform_driver ad1980_codec_driver = {
@@ -325,7 +316,6 @@ static struct platform_driver ad1980_codec_driver = {
},
.probe = ad1980_probe,
- .remove = ad1980_remove,
};
module_platform_driver(ad1980_codec_driver);
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index a9400aef60b5..f6090ac57e93 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ad73311.c -- ALSA Soc AD73311 codec support
*
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices Inc.
* Author: Cliff Cai <cliff.cai@analog.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/init.h>
@@ -54,23 +50,20 @@ static struct snd_soc_dai_driver ad73311_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
-static struct snd_soc_codec_driver soc_codec_dev_ad73311 = {
- .dapm_widgets = ad73311_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
- .dapm_routes = ad73311_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_ad73311 = {
+ .dapm_widgets = ad73311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
+ .dapm_routes = ad73311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ad73311_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad73311, &ad73311_dai, 1);
-}
-
-static int ad73311_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ad73311, &ad73311_dai, 1);
}
static struct platform_driver ad73311_codec_driver = {
@@ -79,7 +72,6 @@ static struct platform_driver ad73311_codec_driver = {
},
.probe = ad73311_probe,
- .remove = ad73311_remove,
};
module_platform_driver(ad73311_codec_driver);
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h
index 4b353eefc0bf..774c62d561ba 100644
--- a/sound/soc/codecs/ad73311.h
+++ b/sound/soc/codecs/ad73311.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* File: sound/soc/codec/ad73311.h
* Based on:
@@ -6,26 +7,10 @@
* Created: Thur Sep 25, 2008
* Description: definitions for AD73311 registers
*
- *
* Modified:
* Copyright 2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AD73311_H__
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644
index 000000000000..836940f2ab92
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Shared helper functions for devices from the ADAU family
+ *
+ * Copyright 2011-2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "adau-utils.h"
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+ uint8_t regs[5])
+{
+ unsigned int r, n, m, i, j;
+ unsigned int div;
+
+ if (!freq_out) {
+ r = 0;
+ n = 0;
+ m = 0;
+ div = 0;
+ } else {
+ if (freq_out % freq_in != 0) {
+ div = DIV_ROUND_UP(freq_in, 13500000);
+ freq_in /= div;
+ r = freq_out / freq_in;
+ i = freq_out % freq_in;
+ j = gcd(i, freq_in);
+ n = i / j;
+ m = freq_in / j;
+ div--;
+ } else {
+ r = freq_out / freq_in;
+ n = 0;
+ m = 0;
+ div = 0;
+ }
+ if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+ return -EINVAL;
+ }
+
+ regs[0] = m >> 8;
+ regs[1] = m & 0xff;
+ regs[2] = n >> 8;
+ regs[3] = n & 0xff;
+ regs[4] = (r << 3) | (div << 1);
+ if (m != 0)
+ regs[4] |= 1; /* Fractional mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau_calc_pll_cfg);
+
+MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h
new file mode 100644
index 000000000000..bf5947b35390
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_SOC_CODECS_ADAU_PLL_H
+#define SOUND_SOC_CODECS_ADAU_PLL_H
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+ uint8_t regs[5]);
+
+#endif
diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c
new file mode 100644
index 000000000000..73f83be38f74
--- /dev/null
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1372 codec
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1372.h"
+
+static int adau1372_i2c_probe(struct i2c_client *client)
+{
+ return adau1372_probe(&client->dev,
+ devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL);
+}
+
+static const struct i2c_device_id adau1372_i2c_ids[] = {
+ { "adau1372" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids);
+
+static struct i2c_driver adau1372_i2c_driver = {
+ .driver = {
+ .name = "adau1372",
+ .of_match_table = adau1372_of_match,
+ },
+ .probe = adau1372_i2c_probe,
+ .id_table = adau1372_i2c_ids,
+};
+module_i2c_driver(adau1372_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372-spi.c b/sound/soc/codecs/adau1372-spi.c
new file mode 100644
index 000000000000..656bd1fabeb3
--- /dev/null
+++ b/sound/soc/codecs/adau1372-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1372 codec
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1372.h"
+
+static void adau1372_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1372_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = adau1372_regmap_config;
+ config.read_flag_mask = 0x1;
+
+ return adau1372_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config), adau1372_spi_switch_mode);
+}
+
+static const struct spi_device_id adau1372_spi_id[] = {
+ { "adau1372", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1372_spi_id);
+
+static struct spi_driver adau1372_spi_driver = {
+ .driver = {
+ .name = "adau1372",
+ .of_match_table = adau1372_of_match,
+ },
+ .probe = adau1372_spi_probe,
+ .id_table = adau1372_spi_id,
+};
+module_spi_driver(adau1372_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
new file mode 100644
index 000000000000..fdee689cae53
--- /dev/null
+++ b/sound/soc/codecs/adau1372.c
@@ -0,0 +1,1073 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADAU1372 Audio Codec driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "adau1372.h"
+#include "adau-utils.h"
+
+struct adau1372 {
+ struct regmap *regmap;
+ void (*switch_mode)(struct device *dev);
+ bool use_pll;
+ bool enabled;
+ bool clock_provider;
+
+ struct snd_pcm_hw_constraint_list rate_constraints;
+ unsigned int slot_width;
+
+ struct clk *mclk;
+ struct gpio_desc *pd_gpio;
+ struct device *dev;
+};
+
+#define ADAU1372_REG_CLK_CTRL 0x00
+#define ADAU1372_REG_PLL(x) (0x01 + (x))
+#define ADAU1372_REG_DAC_SOURCE 0x11
+#define ADAU1372_REG_SOUT_SOURCE_0_1 0x13
+#define ADAU1372_REG_SOUT_SOURCE_2_3 0x14
+#define ADAU1372_REG_SOUT_SOURCE_4_5 0x15
+#define ADAU1372_REG_SOUT_SOURCE_6_7 0x16
+#define ADAU1372_REG_ADC_SDATA_CH 0x17
+#define ADAU1372_REG_ASRCO_SOURCE_0_1 0x18
+#define ADAU1372_REG_ASRCO_SOURCE_2_3 0x19
+#define ADAU1372_REG_ASRC_MODE 0x1a
+#define ADAU1372_REG_ADC_CTRL0 0x1b
+#define ADAU1372_REG_ADC_CTRL1 0x1c
+#define ADAU1372_REG_ADC_CTRL2 0x1d
+#define ADAU1372_REG_ADC_CTRL3 0x1e
+#define ADAU1372_REG_ADC_VOL(x) (0x1f + (x))
+#define ADAU1372_REG_PGA_CTRL(x) (0x23 + (x))
+#define ADAU1372_REG_PGA_BOOST 0x28
+#define ADAU1372_REG_MICBIAS 0x2d
+#define ADAU1372_REG_DAC_CTRL 0x2e
+#define ADAU1372_REG_DAC_VOL(x) (0x2f + (x))
+#define ADAU1372_REG_OP_STAGE_MUTE 0x31
+#define ADAU1372_REG_SAI0 0x32
+#define ADAU1372_REG_SAI1 0x33
+#define ADAU1372_REG_SOUT_CTRL 0x34
+#define ADAU1372_REG_MODE_MP(x) (0x38 + (x))
+#define ADAU1372_REG_OP_STAGE_CTRL 0x43
+#define ADAU1372_REG_DECIM_PWR 0x44
+#define ADAU1372_REG_INTERP_PWR 0x45
+#define ADAU1372_REG_BIAS_CTRL0 0x46
+#define ADAU1372_REG_BIAS_CTRL1 0x47
+
+#define ADAU1372_CLK_CTRL_PLL_EN BIT(7)
+#define ADAU1372_CLK_CTRL_XTAL_DIS BIT(4)
+#define ADAU1372_CLK_CTRL_CLKSRC BIT(3)
+#define ADAU1372_CLK_CTRL_CC_MDIV BIT(1)
+#define ADAU1372_CLK_CTRL_MCLK_EN BIT(0)
+
+#define ADAU1372_SAI0_DELAY1 (0x0 << 6)
+#define ADAU1372_SAI0_DELAY0 (0x1 << 6)
+#define ADAU1372_SAI0_DELAY_MASK (0x3 << 6)
+#define ADAU1372_SAI0_SAI_I2S (0x0 << 4)
+#define ADAU1372_SAI0_SAI_TDM2 (0x1 << 4)
+#define ADAU1372_SAI0_SAI_TDM4 (0x2 << 4)
+#define ADAU1372_SAI0_SAI_TDM8 (0x3 << 4)
+#define ADAU1372_SAI0_SAI_MASK (0x3 << 4)
+#define ADAU1372_SAI0_FS_48 0x0
+#define ADAU1372_SAI0_FS_8 0x1
+#define ADAU1372_SAI0_FS_12 0x2
+#define ADAU1372_SAI0_FS_16 0x3
+#define ADAU1372_SAI0_FS_24 0x4
+#define ADAU1372_SAI0_FS_32 0x5
+#define ADAU1372_SAI0_FS_96 0x6
+#define ADAU1372_SAI0_FS_192 0x7
+#define ADAU1372_SAI0_FS_MASK 0xf
+
+#define ADAU1372_SAI1_TDM_TS BIT(7)
+#define ADAU1372_SAI1_BCLK_TDMC BIT(6)
+#define ADAU1372_SAI1_LR_MODE BIT(5)
+#define ADAU1372_SAI1_LR_POL BIT(4)
+#define ADAU1372_SAI1_BCLKRATE BIT(2)
+#define ADAU1372_SAI1_BCLKEDGE BIT(1)
+#define ADAU1372_SAI1_MS BIT(0)
+
+static const unsigned int adau1372_rates[] = {
+ [ADAU1372_SAI0_FS_8] = 8000,
+ [ADAU1372_SAI0_FS_12] = 12000,
+ [ADAU1372_SAI0_FS_16] = 16000,
+ [ADAU1372_SAI0_FS_24] = 24000,
+ [ADAU1372_SAI0_FS_32] = 32000,
+ [ADAU1372_SAI0_FS_48] = 48000,
+ [ADAU1372_SAI0_FS_96] = 96000,
+ [ADAU1372_SAI0_FS_192] = 192000,
+};
+
+/* 8k, 12k, 24k, 48k */
+#define ADAU1372_RATE_MASK_TDM8 0x17
+/* + 16k, 96k */
+#define ADAU1372_RATE_MASK_TDM4_MASTER (ADAU1372_RATE_MASK_TDM8 | 0x48 | 0x20)
+/* +32k */
+#define ADAU1372_RATE_MASK_TDM4 (ADAU1372_RATE_MASK_TDM4_MASTER | 0x20)
+/* + 192k */
+#define ADAU1372_RATE_MASK_TDM2 (ADAU1372_RATE_MASK_TDM4 | 0x80)
+
+static const DECLARE_TLV_DB_MINMAX(adau1372_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1372_pga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adau1372_pga_boost_tlv, 0, 1000, 0);
+
+static const char * const adau1372_bias_text[] = {
+ "Normal operation", "Extreme power saving", "Enhanced performance",
+ "Power saving",
+};
+
+static const unsigned int adau1372_bias_adc_values[] = {
+ 0, 2, 3,
+};
+
+static const char * const adau1372_bias_adc_text[] = {
+ "Normal operation", "Enhanced performance", "Power saving",
+};
+
+static const char * const adau1372_bias_dac_text[] = {
+ "Normal operation", "Power saving", "Superior performance",
+ "Enhanced performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_hp_enum,
+ ADAU1372_REG_BIAS_CTRL0, 6, adau1372_bias_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe0_1_enum,
+ ADAU1372_REG_BIAS_CTRL0, 4, adau1372_bias_text);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc2_3_enum,
+ ADAU1372_REG_BIAS_CTRL0, 2, 0x3, adau1372_bias_adc_text,
+ adau1372_bias_adc_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc0_1_enum,
+ ADAU1372_REG_BIAS_CTRL0, 0, 0x3, adau1372_bias_adc_text,
+ adau1372_bias_adc_values);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe2_3_enum,
+ ADAU1372_REG_BIAS_CTRL1, 4, adau1372_bias_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_mic_enum,
+ ADAU1372_REG_BIAS_CTRL1, 2, adau1372_bias_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_bias_dac_enum,
+ ADAU1372_REG_BIAS_CTRL1, 0, adau1372_bias_dac_text);
+
+static const char * const adau1372_hpf_text[] = {
+ "Off",
+ "1 Hz",
+ "4 Hz",
+ "8 Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_hpf0_1_enum, ADAU1372_REG_ADC_CTRL2, 5,
+ adau1372_hpf_text);
+static SOC_ENUM_SINGLE_DECL(adau1372_hpf2_3_enum, ADAU1372_REG_ADC_CTRL3, 5,
+ adau1372_hpf_text);
+static const struct snd_kcontrol_new adau1372_controls[] = {
+ SOC_SINGLE_TLV("ADC 0 Capture Volume", ADAU1372_REG_ADC_VOL(0),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("ADC 1 Capture Volume", ADAU1372_REG_ADC_VOL(1),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("ADC 2 Capture Volume", ADAU1372_REG_ADC_VOL(2),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("ADC 3 Capture Volume", ADAU1372_REG_ADC_VOL(3),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE("ADC 0 Capture Switch", ADAU1372_REG_ADC_CTRL0, 3, 1, 1),
+ SOC_SINGLE("ADC 1 Capture Switch", ADAU1372_REG_ADC_CTRL0, 4, 1, 1),
+ SOC_SINGLE("ADC 2 Capture Switch", ADAU1372_REG_ADC_CTRL1, 3, 1, 1),
+ SOC_SINGLE("ADC 3 Capture Switch", ADAU1372_REG_ADC_CTRL1, 4, 1, 1),
+
+ SOC_ENUM("ADC 0+1 High-Pass-Filter", adau1372_hpf0_1_enum),
+ SOC_ENUM("ADC 2+3 High-Pass-Filter", adau1372_hpf2_3_enum),
+
+ SOC_SINGLE_TLV("PGA 0 Capture Volume", ADAU1372_REG_PGA_CTRL(0),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 1 Capture Volume", ADAU1372_REG_PGA_CTRL(1),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 2 Capture Volume", ADAU1372_REG_PGA_CTRL(2),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 3 Capture Volume", ADAU1372_REG_PGA_CTRL(3),
+ 0, 0x3f, 0, adau1372_pga_tlv),
+ SOC_SINGLE_TLV("PGA 0 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 0, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE_TLV("PGA 1 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 1, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE_TLV("PGA 2 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 2, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE_TLV("PGA 3 Boost Capture Volume", ADAU1372_REG_PGA_BOOST,
+ 3, 1, 0, adau1372_pga_boost_tlv),
+ SOC_SINGLE("PGA 0 Capture Switch", ADAU1372_REG_PGA_CTRL(0), 7, 1, 0),
+ SOC_SINGLE("PGA 1 Capture Switch", ADAU1372_REG_PGA_CTRL(1), 7, 1, 0),
+ SOC_SINGLE("PGA 2 Capture Switch", ADAU1372_REG_PGA_CTRL(2), 7, 1, 0),
+ SOC_SINGLE("PGA 3 Capture Switch", ADAU1372_REG_PGA_CTRL(3), 7, 1, 0),
+
+ SOC_SINGLE_TLV("DAC 0 Playback Volume", ADAU1372_REG_DAC_VOL(0),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE_TLV("DAC 1 Playback Volume", ADAU1372_REG_DAC_VOL(1),
+ 0, 0xff, 1, adau1372_digital_tlv),
+ SOC_SINGLE("DAC 0 Playback Switch", ADAU1372_REG_DAC_CTRL, 3, 1, 1),
+ SOC_SINGLE("DAC 1 Playback Switch", ADAU1372_REG_DAC_CTRL, 4, 1, 1),
+
+ SOC_ENUM("Headphone Bias", adau1372_bias_hp_enum),
+ SOC_ENUM("Microphone Bias", adau1372_bias_mic_enum),
+ SOC_ENUM("AFE 0+1 Bias", adau1372_bias_afe0_1_enum),
+ SOC_ENUM("AFE 2+3 Bias", adau1372_bias_afe2_3_enum),
+ SOC_ENUM("ADC 0+1 Bias", adau1372_bias_adc0_1_enum),
+ SOC_ENUM("ADC 2+3 Bias", adau1372_bias_adc2_3_enum),
+ SOC_ENUM("DAC 0+1 Bias", adau1372_bias_dac_enum),
+};
+
+static const char * const adau1372_decimator_mux_text[] = {
+ "ADC",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_decimator0_1_mux_enum, ADAU1372_REG_ADC_CTRL2,
+ 2, adau1372_decimator_mux_text);
+
+static const struct snd_kcontrol_new adau1372_decimator0_1_mux_control =
+ SOC_DAPM_ENUM("Decimator 0+1 Capture Mux", adau1372_decimator0_1_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(adau1372_decimator2_3_mux_enum, ADAU1372_REG_ADC_CTRL3,
+ 2, adau1372_decimator_mux_text);
+
+static const struct snd_kcontrol_new adau1372_decimator2_3_mux_control =
+ SOC_DAPM_ENUM("Decimator 2+3 Capture Mux", adau1372_decimator2_3_mux_enum);
+
+static const unsigned int adau1372_asrco_mux_values[] = {
+ 4, 5, 6, 7,
+};
+
+static const char * const adau1372_asrco_mux_text[] = {
+ "Decimator0",
+ "Decimator1",
+ "Decimator2",
+ "Decimator3",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco0_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1,
+ 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco1_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1,
+ 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco2_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3,
+ 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco3_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3,
+ 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values);
+
+static const struct snd_kcontrol_new adau1372_asrco0_mux_control =
+ SOC_DAPM_ENUM("Output ASRC0 Capture Mux", adau1372_asrco0_mux_enum);
+static const struct snd_kcontrol_new adau1372_asrco1_mux_control =
+ SOC_DAPM_ENUM("Output ASRC1 Capture Mux", adau1372_asrco1_mux_enum);
+static const struct snd_kcontrol_new adau1372_asrco2_mux_control =
+ SOC_DAPM_ENUM("Output ASRC2 Capture Mux", adau1372_asrco2_mux_enum);
+static const struct snd_kcontrol_new adau1372_asrco3_mux_control =
+ SOC_DAPM_ENUM("Output ASRC3 Capture Mux", adau1372_asrco3_mux_enum);
+
+static const unsigned int adau1372_sout_mux_values[] = {
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+static const char * const adau1372_sout_mux_text[] = {
+ "Output ASRC0",
+ "Output ASRC1",
+ "Output ASRC2",
+ "Output ASRC3",
+ "Serial Input 0",
+ "Serial Input 1",
+ "Serial Input 2",
+ "Serial Input 3",
+ "Serial Input 4",
+ "Serial Input 5",
+ "Serial Input 6",
+ "Serial Input 7",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout0_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout1_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout2_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout3_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout4_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout5_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout6_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7,
+ 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout7_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7,
+ 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values);
+
+static const struct snd_kcontrol_new adau1372_sout0_mux_control =
+ SOC_DAPM_ENUM("Serial Output 0 Capture Mux", adau1372_sout0_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout1_mux_control =
+ SOC_DAPM_ENUM("Serial Output 1 Capture Mux", adau1372_sout1_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout2_mux_control =
+ SOC_DAPM_ENUM("Serial Output 2 Capture Mux", adau1372_sout2_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout3_mux_control =
+ SOC_DAPM_ENUM("Serial Output 3 Capture Mux", adau1372_sout3_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout4_mux_control =
+ SOC_DAPM_ENUM("Serial Output 4 Capture Mux", adau1372_sout4_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout5_mux_control =
+ SOC_DAPM_ENUM("Serial Output 5 Capture Mux", adau1372_sout5_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout6_mux_control =
+ SOC_DAPM_ENUM("Serial Output 6 Capture Mux", adau1372_sout6_mux_enum);
+static const struct snd_kcontrol_new adau1372_sout7_mux_control =
+ SOC_DAPM_ENUM("Serial Output 7 Capture Mux", adau1372_sout7_mux_enum);
+
+static const char * const adau1372_asrci_mux_text[] = {
+ "Serial Input 0+1",
+ "Serial Input 2+3",
+ "Serial Input 4+5",
+ "Serial Input 6+7",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1372_asrci_mux_enum,
+ ADAU1372_REG_ASRC_MODE, 2, adau1372_asrci_mux_text);
+
+static const struct snd_kcontrol_new adau1372_asrci_mux_control =
+ SOC_DAPM_ENUM("Input ASRC Playback Mux", adau1372_asrci_mux_enum);
+
+static const unsigned int adau1372_dac_mux_values[] = {
+ 12, 13
+};
+
+static const char * const adau1372_dac_mux_text[] = {
+ "Input ASRC0",
+ "Input ASRC1",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac0_mux_enum, ADAU1372_REG_DAC_SOURCE,
+ 0, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac1_mux_enum, ADAU1372_REG_DAC_SOURCE,
+ 4, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values);
+
+static const struct snd_kcontrol_new adau1372_dac0_mux_control =
+ SOC_DAPM_ENUM("DAC 0 Playback Mux", adau1372_dac0_mux_enum);
+static const struct snd_kcontrol_new adau1372_dac1_mux_control =
+ SOC_DAPM_ENUM("DAC 1 Playback Mux", adau1372_dac1_mux_enum);
+
+static const struct snd_soc_dapm_widget adau1372_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("DMIC0_1"),
+ SND_SOC_DAPM_INPUT("DMIC2_3"),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS0", ADAU1372_REG_MICBIAS, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1372_REG_MICBIAS, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("PGA0", ADAU1372_REG_PGA_CTRL(0), 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA1", ADAU1372_REG_PGA_CTRL(1), 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA2", ADAU1372_REG_PGA_CTRL(2), 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA3", ADAU1372_REG_PGA_CTRL(3), 6, 1, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC0", NULL, ADAU1372_REG_ADC_CTRL2, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1", NULL, ADAU1372_REG_ADC_CTRL2, 1, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, ADAU1372_REG_ADC_CTRL3, 0, 0),
+ SND_SOC_DAPM_ADC("ADC3", NULL, ADAU1372_REG_ADC_CTRL3, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC0 Filter", ADAU1372_REG_DECIM_PWR, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 Filter", ADAU1372_REG_DECIM_PWR, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2 Filter", ADAU1372_REG_DECIM_PWR, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3 Filter", ADAU1372_REG_DECIM_PWR, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC0 Decimator", ADAU1372_REG_DECIM_PWR, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC1 Decimator", ADAU1372_REG_DECIM_PWR, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC2 Decimator", ADAU1372_REG_DECIM_PWR, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Output ASRC3 Decimator", ADAU1372_REG_DECIM_PWR, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Decimator0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control),
+ SND_SOC_DAPM_MUX("Decimator1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control),
+ SND_SOC_DAPM_MUX("Decimator2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control),
+ SND_SOC_DAPM_MUX("Decimator3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control),
+
+ SND_SOC_DAPM_MUX("Output ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco0_mux_control),
+ SND_SOC_DAPM_MUX("Output ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco1_mux_control),
+ SND_SOC_DAPM_MUX("Output ASRC2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco2_mux_control),
+ SND_SOC_DAPM_MUX("Output ASRC3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco3_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 0 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout0_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 1 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout1_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 2 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout2_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 3 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout3_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 4 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout4_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 5 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout5_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 6 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout6_mux_control),
+ SND_SOC_DAPM_MUX("Serial Output 7 Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau1372_sout7_mux_control),
+
+ SND_SOC_DAPM_AIF_IN("Serial Input 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 1", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 2", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 3", NULL, 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 4", NULL, 4, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 5", NULL, 5, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 6", NULL, 6, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Serial Input 7", NULL, 7, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("Serial Output 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 1", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 2", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 3", NULL, 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 4", NULL, 4, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 5", NULL, 5, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 6", NULL, 6, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Serial Output 7", NULL, 7, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("Output ASRC Supply", ADAU1372_REG_ASRC_MODE, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Input ASRC Supply", ADAU1372_REG_ASRC_MODE, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC1 Modulator", ADAU1372_REG_INTERP_PWR, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC0 Modulator", ADAU1372_REG_INTERP_PWR, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Input ASRC1 Interpolator", ADAU1372_REG_INTERP_PWR, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Input ASRC0 Interpolator", ADAU1372_REG_INTERP_PWR, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Input ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control),
+ SND_SOC_DAPM_MUX("Input ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control),
+
+ SND_SOC_DAPM_MUX("DAC 0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac0_mux_control),
+ SND_SOC_DAPM_MUX("DAC 1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac1_mux_control),
+
+ SND_SOC_DAPM_DAC("DAC0", NULL, ADAU1372_REG_DAC_CTRL, 0, 0),
+ SND_SOC_DAPM_DAC("DAC1", NULL, ADAU1372_REG_DAC_CTRL, 1, 0),
+
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_LP", ADAU1372_REG_OP_STAGE_CTRL, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_LN", ADAU1372_REG_OP_STAGE_CTRL, 1, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_RP", ADAU1372_REG_OP_STAGE_CTRL, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("OP_STAGE_RN", ADAU1372_REG_OP_STAGE_CTRL, 3, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+};
+
+#define ADAU1372_SOUT_ROUTES(x) \
+ { "Serial Output " #x " Capture Mux", "Output ASRC0", "Output ASRC0 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Output ASRC1", "Output ASRC1 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Output ASRC2", "Output ASRC2 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Output ASRC3", "Output ASRC3 Mux" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 0", "Serial Input 0" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 1", "Serial Input 1" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 2", "Serial Input 2" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 3", "Serial Input 3" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 4", "Serial Input 4" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 5", "Serial Input 5" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 6", "Serial Input 6" }, \
+ { "Serial Output " #x " Capture Mux", "Serial Input 7", "Serial Input 7" }, \
+ { "Serial Output " #x, NULL, "Serial Output " #x " Capture Mux" }, \
+ { "Capture", NULL, "Serial Output " #x }
+
+#define ADAU1372_ASRCO_ROUTES(x) \
+ { "Output ASRC" #x " Mux", "Decimator0", "Decimator0 Mux" }, \
+ { "Output ASRC" #x " Mux", "Decimator1", "Decimator1 Mux" }, \
+ { "Output ASRC" #x " Mux", "Decimator2", "Decimator2 Mux" }, \
+ { "Output ASRC" #x " Mux", "Decimator3", "Decimator3 Mux" }
+
+static const struct snd_soc_dapm_route adau1372_dapm_routes[] = {
+ { "PGA0", NULL, "AIN0" },
+ { "PGA1", NULL, "AIN1" },
+ { "PGA2", NULL, "AIN2" },
+ { "PGA3", NULL, "AIN3" },
+
+ { "ADC0", NULL, "PGA0" },
+ { "ADC1", NULL, "PGA1" },
+ { "ADC2", NULL, "PGA2" },
+ { "ADC3", NULL, "PGA3" },
+
+ { "Decimator0 Mux", "ADC", "ADC0" },
+ { "Decimator1 Mux", "ADC", "ADC1" },
+ { "Decimator2 Mux", "ADC", "ADC2" },
+ { "Decimator3 Mux", "ADC", "ADC3" },
+
+ { "Decimator0 Mux", "DMIC", "DMIC0_1" },
+ { "Decimator1 Mux", "DMIC", "DMIC0_1" },
+ { "Decimator2 Mux", "DMIC", "DMIC2_3" },
+ { "Decimator3 Mux", "DMIC", "DMIC2_3" },
+
+ { "Decimator0 Mux", NULL, "ADC0 Filter" },
+ { "Decimator1 Mux", NULL, "ADC1 Filter" },
+ { "Decimator2 Mux", NULL, "ADC2 Filter" },
+ { "Decimator3 Mux", NULL, "ADC3 Filter" },
+
+ { "Output ASRC0 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC1 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC2 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC3 Mux", NULL, "Output ASRC Supply" },
+ { "Output ASRC0 Mux", NULL, "Output ASRC0 Decimator" },
+ { "Output ASRC1 Mux", NULL, "Output ASRC1 Decimator" },
+ { "Output ASRC2 Mux", NULL, "Output ASRC2 Decimator" },
+ { "Output ASRC3 Mux", NULL, "Output ASRC3 Decimator" },
+
+ ADAU1372_ASRCO_ROUTES(0),
+ ADAU1372_ASRCO_ROUTES(1),
+ ADAU1372_ASRCO_ROUTES(2),
+ ADAU1372_ASRCO_ROUTES(3),
+
+ ADAU1372_SOUT_ROUTES(0),
+ ADAU1372_SOUT_ROUTES(1),
+ ADAU1372_SOUT_ROUTES(2),
+ ADAU1372_SOUT_ROUTES(3),
+ ADAU1372_SOUT_ROUTES(4),
+ ADAU1372_SOUT_ROUTES(5),
+ ADAU1372_SOUT_ROUTES(6),
+ ADAU1372_SOUT_ROUTES(7),
+
+ { "Serial Input 0", NULL, "Playback" },
+ { "Serial Input 1", NULL, "Playback" },
+ { "Serial Input 2", NULL, "Playback" },
+ { "Serial Input 3", NULL, "Playback" },
+ { "Serial Input 4", NULL, "Playback" },
+ { "Serial Input 5", NULL, "Playback" },
+ { "Serial Input 6", NULL, "Playback" },
+ { "Serial Input 7", NULL, "Playback" },
+
+ { "Input ASRC0 Mux", "Serial Input 0+1", "Serial Input 0" },
+ { "Input ASRC1 Mux", "Serial Input 0+1", "Serial Input 1" },
+ { "Input ASRC0 Mux", "Serial Input 2+3", "Serial Input 2" },
+ { "Input ASRC1 Mux", "Serial Input 2+3", "Serial Input 3" },
+ { "Input ASRC0 Mux", "Serial Input 4+5", "Serial Input 4" },
+ { "Input ASRC1 Mux", "Serial Input 4+5", "Serial Input 5" },
+ { "Input ASRC0 Mux", "Serial Input 6+7", "Serial Input 6" },
+ { "Input ASRC1 Mux", "Serial Input 6+7", "Serial Input 7" },
+ { "Input ASRC0 Mux", NULL, "Input ASRC Supply" },
+ { "Input ASRC1 Mux", NULL, "Input ASRC Supply" },
+ { "Input ASRC0 Mux", NULL, "Input ASRC0 Interpolator" },
+ { "Input ASRC1 Mux", NULL, "Input ASRC1 Interpolator" },
+
+ { "DAC 0 Mux", "Input ASRC0", "Input ASRC0 Mux" },
+ { "DAC 0 Mux", "Input ASRC1", "Input ASRC1 Mux" },
+ { "DAC 1 Mux", "Input ASRC0", "Input ASRC0 Mux" },
+ { "DAC 1 Mux", "Input ASRC1", "Input ASRC1 Mux" },
+
+ { "DAC0", NULL, "DAC 0 Mux" },
+ { "DAC1", NULL, "DAC 1 Mux" },
+ { "DAC0", NULL, "DAC0 Modulator" },
+ { "DAC1", NULL, "DAC1 Modulator" },
+
+ { "OP_STAGE_LP", NULL, "DAC0" },
+ { "OP_STAGE_LN", NULL, "DAC0" },
+ { "OP_STAGE_RP", NULL, "DAC1" },
+ { "OP_STAGE_RN", NULL, "DAC1" },
+
+ { "HPOUTL", NULL, "OP_STAGE_LP" },
+ { "HPOUTL", NULL, "OP_STAGE_LN" },
+ { "HPOUTR", NULL, "OP_STAGE_RP" },
+ { "HPOUTR", NULL, "OP_STAGE_RN" },
+};
+
+static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int sai0 = 0, sai1 = 0;
+ bool invert_lrclk = false;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adau1372->clock_provider = true;
+ sai1 |= ADAU1372_SAI1_MS;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1372->clock_provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert_lrclk = false;
+ sai1 |= ADAU1372_SAI1_BCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_lrclk = true;
+ sai1 |= ADAU1372_SAI1_BCLKEDGE;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sai0 |= ADAU1372_SAI0_DELAY1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ sai0 |= ADAU1372_SAI0_DELAY0;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ sai0 |= ADAU1372_SAI0_DELAY1;
+ sai1 |= ADAU1372_SAI1_LR_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ sai0 |= ADAU1372_SAI0_DELAY0;
+ sai1 |= ADAU1372_SAI1_LR_MODE;
+ break;
+ }
+
+ if (invert_lrclk)
+ sai1 |= ADAU1372_SAI1_LR_POL;
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_DELAY_MASK, sai0);
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1,
+ ADAU1372_SAI1_MS | ADAU1372_SAI1_BCLKEDGE |
+ ADAU1372_SAI1_LR_MODE | ADAU1372_SAI1_LR_POL, sai1);
+
+ return 0;
+}
+
+static int adau1372_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int slot_width;
+ unsigned int sai0, sai1;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(adau1372_rates); i++) {
+ if (rate == adau1372_rates[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(adau1372_rates))
+ return -EINVAL;
+
+ sai0 = i;
+
+ slot_width = adau1372->slot_width;
+ if (slot_width == 0)
+ slot_width = params_width(params);
+
+ switch (slot_width) {
+ case 16:
+ sai1 = ADAU1372_SAI1_BCLKRATE;
+ break;
+ case 24:
+ case 32:
+ sai1 = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_FS_MASK, sai0);
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLKRATE, sai1);
+
+ return 0;
+}
+
+static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int sai0, sai1;
+
+ /* I2S mode */
+ if (slots == 0) {
+ /* The other settings dont matter in I2S mode */
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0,
+ ADAU1372_SAI0_SAI_MASK, ADAU1372_SAI0_SAI_I2S);
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
+ adau1372->slot_width = 0;
+ return 0;
+ }
+
+ /* We have 8 channels anything outside that is not supported */
+ if ((tx_mask & ~0xff) != 0 || (rx_mask & ~0xff) != 0)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ sai1 = ADAU1372_SAI1_BCLK_TDMC;
+ break;
+ case 24:
+ case 32:
+ sai1 = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 2:
+ sai0 = ADAU1372_SAI0_SAI_TDM2;
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
+ break;
+ case 4:
+ sai0 = ADAU1372_SAI0_SAI_TDM4;
+ if (adau1372->clock_provider)
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER;
+ else
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4;
+ break;
+ case 8:
+ sai0 = ADAU1372_SAI0_SAI_TDM8;
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1372->slot_width = width;
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_SAI_MASK, sai0);
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLK_TDMC, sai1);
+
+ /* Mask is inverted in hardware */
+ regmap_write(adau1372->regmap, ADAU1372_REG_SOUT_CTRL, ~tx_mask);
+
+ return 0;
+}
+
+static int adau1372_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+ unsigned int sai1;
+
+ if (tristate)
+ sai1 = ADAU1372_SAI1_TDM_TS;
+ else
+ sai1 = 0;
+
+ return regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_TDM_TS, sai1);
+}
+
+static int adau1372_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai);
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &adau1372->rate_constraints);
+
+ return 0;
+}
+
+static void adau1372_enable_pll(struct adau1372 *adau1372)
+{
+ unsigned int val, timeout = 0;
+ int ret;
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_PLL_EN, ADAU1372_CLK_CTRL_PLL_EN);
+ do {
+ /* Takes about 1ms to lock */
+ usleep_range(1000, 2000);
+ ret = regmap_read(adau1372->regmap, ADAU1372_REG_PLL(5), &val);
+ if (ret)
+ break;
+ timeout++;
+ } while (!(val & 1) && timeout < 3);
+
+ if (ret < 0 || !(val & 1))
+ dev_err(adau1372->dev, "Failed to lock PLL\n");
+}
+
+static void adau1372_set_power(struct adau1372 *adau1372, bool enable)
+{
+ if (adau1372->enabled == enable)
+ return;
+
+ if (enable) {
+ unsigned int clk_ctrl = ADAU1372_CLK_CTRL_MCLK_EN;
+
+ clk_prepare_enable(adau1372->mclk);
+ if (adau1372->pd_gpio)
+ gpiod_set_value(adau1372->pd_gpio, 0);
+
+ if (adau1372->switch_mode)
+ adau1372->switch_mode(adau1372->dev);
+
+ regcache_cache_only(adau1372->regmap, false);
+
+ /*
+ * Clocks needs to be enabled before any other register can be
+ * accessed.
+ */
+ if (adau1372->use_pll) {
+ adau1372_enable_pll(adau1372);
+ clk_ctrl |= ADAU1372_CLK_CTRL_CLKSRC;
+ }
+
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_CLKSRC, clk_ctrl);
+ regcache_sync(adau1372->regmap);
+ } else {
+ if (adau1372->pd_gpio) {
+ /*
+ * This will turn everything off and reset the register
+ * map. No need to do any register writes to manually
+ * turn things off.
+ */
+ gpiod_set_value(adau1372->pd_gpio, 1);
+ regcache_mark_dirty(adau1372->regmap);
+ } else {
+ regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_PLL_EN, 0);
+ }
+ clk_disable_unprepare(adau1372->mclk);
+ regcache_cache_only(adau1372->regmap, true);
+ }
+
+ adau1372->enabled = enable;
+}
+
+static int adau1372_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau1372 *adau1372 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ adau1372_set_power(adau1372, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ adau1372_set_power(adau1372, false);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver adau1372_driver = {
+ .set_bias_level = adau1372_set_bias_level,
+ .controls = adau1372_controls,
+ .num_controls = ARRAY_SIZE(adau1372_controls),
+ .dapm_widgets = adau1372_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets),
+ .dapm_routes = adau1372_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops adau1372_dai_ops = {
+ .set_fmt = adau1372_set_dai_fmt,
+ .set_tdm_slot = adau1372_set_tdm_slot,
+ .set_tristate = adau1372_set_tristate,
+ .hw_params = adau1372_hw_params,
+ .startup = adau1372_startup,
+};
+
+#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1372_dai_driver = {
+ .name = "adau1372",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ADAU1372_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ADAU1372_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &adau1372_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int adau1372_setup_pll(struct adau1372 *adau1372, unsigned int rate)
+{
+ u8 regs[5];
+ unsigned int i;
+ int ret;
+
+ ret = adau_calc_pll_cfg(rate, 49152000, regs);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ regmap_write(adau1372->regmap, ADAU1372_REG_PLL(i), regs[i]);
+
+ return 0;
+}
+
+int adau1372_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev))
+{
+ struct adau1372 *adau1372;
+ unsigned int clk_ctrl;
+ unsigned long rate;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adau1372 = devm_kzalloc(dev, sizeof(*adau1372), GFP_KERNEL);
+ if (!adau1372)
+ return -ENOMEM;
+
+ adau1372->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(adau1372->mclk))
+ return PTR_ERR(adau1372->mclk);
+
+ adau1372->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(adau1372->pd_gpio))
+ return PTR_ERR(adau1372->pd_gpio);
+
+ adau1372->regmap = regmap;
+ adau1372->switch_mode = switch_mode;
+ adau1372->dev = dev;
+ adau1372->rate_constraints.list = adau1372_rates;
+ adau1372->rate_constraints.count = ARRAY_SIZE(adau1372_rates);
+ adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2;
+
+ dev_set_drvdata(dev, adau1372);
+
+ /*
+ * The datasheet says that the internal MCLK always needs to run at
+ * 12.288MHz. Automatically choose a valid configuration from the
+ * external clock.
+ */
+ rate = clk_get_rate(adau1372->mclk);
+
+ switch (rate) {
+ case 12288000:
+ clk_ctrl = ADAU1372_CLK_CTRL_CC_MDIV;
+ break;
+ case 24576000:
+ clk_ctrl = 0;
+ break;
+ default:
+ clk_ctrl = 0;
+ ret = adau1372_setup_pll(adau1372, rate);
+ if (ret < 0)
+ return ret;
+ adau1372->use_pll = true;
+ break;
+ }
+
+ /*
+ * Most of the registers are inaccessible unless the internal clock is
+ * enabled.
+ */
+ regcache_cache_only(regmap, true);
+
+ regmap_update_bits(regmap, ADAU1372_REG_CLK_CTRL, ADAU1372_CLK_CTRL_CC_MDIV, clk_ctrl);
+
+ /*
+ * No pinctrl support yet, put the multi-purpose pins in the most
+ * sensible mode for general purpose CODEC operation.
+ */
+ regmap_write(regmap, ADAU1372_REG_MODE_MP(1), 0x00); /* SDATA OUT */
+ regmap_write(regmap, ADAU1372_REG_MODE_MP(6), 0x12); /* CLOCKOUT */
+
+ regmap_write(regmap, ADAU1372_REG_OP_STAGE_MUTE, 0x0);
+
+ regmap_write(regmap, 0x7, 0x01); /* CLOCK OUT */
+
+ return devm_snd_soc_register_component(dev, &adau1372_driver, &adau1372_dai_driver, 1);
+}
+EXPORT_SYMBOL(adau1372_probe);
+
+static const struct reg_default adau1372_reg_defaults[] = {
+ { ADAU1372_REG_CLK_CTRL, 0x00 },
+ { ADAU1372_REG_PLL(0), 0x00 },
+ { ADAU1372_REG_PLL(1), 0x00 },
+ { ADAU1372_REG_PLL(2), 0x00 },
+ { ADAU1372_REG_PLL(3), 0x00 },
+ { ADAU1372_REG_PLL(4), 0x00 },
+ { ADAU1372_REG_PLL(5), 0x00 },
+ { ADAU1372_REG_DAC_SOURCE, 0x10 },
+ { ADAU1372_REG_SOUT_SOURCE_0_1, 0x54 },
+ { ADAU1372_REG_SOUT_SOURCE_2_3, 0x76 },
+ { ADAU1372_REG_SOUT_SOURCE_4_5, 0x54 },
+ { ADAU1372_REG_SOUT_SOURCE_6_7, 0x76 },
+ { ADAU1372_REG_ADC_SDATA_CH, 0x04 },
+ { ADAU1372_REG_ASRCO_SOURCE_0_1, 0x10 },
+ { ADAU1372_REG_ASRCO_SOURCE_2_3, 0x32 },
+ { ADAU1372_REG_ASRC_MODE, 0x00 },
+ { ADAU1372_REG_ADC_CTRL0, 0x19 },
+ { ADAU1372_REG_ADC_CTRL1, 0x19 },
+ { ADAU1372_REG_ADC_CTRL2, 0x00 },
+ { ADAU1372_REG_ADC_CTRL3, 0x00 },
+ { ADAU1372_REG_ADC_VOL(0), 0x00 },
+ { ADAU1372_REG_ADC_VOL(1), 0x00 },
+ { ADAU1372_REG_ADC_VOL(2), 0x00 },
+ { ADAU1372_REG_ADC_VOL(3), 0x00 },
+ { ADAU1372_REG_PGA_CTRL(0), 0x40 },
+ { ADAU1372_REG_PGA_CTRL(1), 0x40 },
+ { ADAU1372_REG_PGA_CTRL(2), 0x40 },
+ { ADAU1372_REG_PGA_CTRL(3), 0x40 },
+ { ADAU1372_REG_PGA_BOOST, 0x00 },
+ { ADAU1372_REG_MICBIAS, 0x00 },
+ { ADAU1372_REG_DAC_CTRL, 0x18 },
+ { ADAU1372_REG_DAC_VOL(0), 0x00 },
+ { ADAU1372_REG_DAC_VOL(1), 0x00 },
+ { ADAU1372_REG_OP_STAGE_MUTE, 0x0f },
+ { ADAU1372_REG_SAI0, 0x00 },
+ { ADAU1372_REG_SAI1, 0x00 },
+ { ADAU1372_REG_SOUT_CTRL, 0x00 },
+ { ADAU1372_REG_MODE_MP(0), 0x00 },
+ { ADAU1372_REG_MODE_MP(1), 0x10 },
+ { ADAU1372_REG_MODE_MP(4), 0x00 },
+ { ADAU1372_REG_MODE_MP(5), 0x00 },
+ { ADAU1372_REG_MODE_MP(6), 0x11 },
+ { ADAU1372_REG_OP_STAGE_CTRL, 0x0f },
+ { ADAU1372_REG_DECIM_PWR, 0x00 },
+ { ADAU1372_REG_INTERP_PWR, 0x00 },
+ { ADAU1372_REG_BIAS_CTRL0, 0x00 },
+ { ADAU1372_REG_BIAS_CTRL1, 0x00 },
+};
+
+static bool adau1372_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == ADAU1372_REG_PLL(5))
+ return true;
+
+ return false;
+}
+
+const struct regmap_config adau1372_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 16,
+ .max_register = 0x4d,
+
+ .reg_defaults = adau1372_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults),
+ .volatile_reg = adau1372_volatile_register,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_GPL(adau1372_regmap_config);
+
+const struct of_device_id adau1372_of_match[] = {
+ { .compatible = "adi,adau1372" },
+ { }
+};
+EXPORT_SYMBOL_GPL(adau1372_of_match);
+MODULE_DEVICE_TABLE(of, adau1372_of_match);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372.h b/sound/soc/codecs/adau1372.h
new file mode 100644
index 000000000000..c55b1c24fe39
--- /dev/null
+++ b/sound/soc/codecs/adau1372.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ADAU1372 driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef SOUND_SOC_CODECS_ADAU1372_H
+#define SOUND_SOC_CODECS_ADAU1372_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct of_device_id adau1372_of_match[];
+int adau1372_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1372_regmap_config;
+
+#endif
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index fe1353a797b9..16b9b2658341 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1,39 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Analog Devices ADAU1373 Audio Codec drive
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/gcd.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
-#include <sound/adau1373.h>
#include "adau1373.h"
+#include "adau-utils.h"
struct adau1373_dai {
unsigned int clk_src;
unsigned int sysclk;
bool enable_src;
- bool master;
+ bool clock_provider;
+};
+
+enum adau1373_micbias_voltage {
+ ADAU1373_MICBIAS_2_9V,
+ ADAU1373_MICBIAS_2_2V,
+ ADAU1373_MICBIAS_2_6V,
+ ADAU1373_MICBIAS_1_8V,
};
+#define ADAU1373_DRC_SIZE 13
+
struct adau1373 {
struct regmap *regmap;
struct adau1373_dai dais[3];
+
+ bool input_differential[4];
+ bool lineout_differential;
+ bool lineout_ground_sense;
+
+ unsigned int num_drc;
+ u8 drc_setting[3][ADAU1373_DRC_SIZE];
+
+ enum adau1373_micbias_voltage micbias1;
+ enum adau1373_micbias_voltage micbias2;
};
#define ADAU1373_INPUT_MODE 0x00
@@ -548,8 +567,8 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int pll_id = w->name[3] - '1';
unsigned int val;
@@ -820,14 +839,14 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int dai;
const char *clk;
dai = sink->name[3] - '1';
- if (!adau1373->dais[dai].master)
+ if (!adau1373->dais[dai].clock_provider)
return 0;
if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
@@ -835,14 +854,14 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
else
clk = "SYSCLK2";
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int dai;
dai = sink->name[3] - '1';
@@ -1030,8 +1049,8 @@ static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
static int adau1373_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
unsigned int div;
unsigned int freq;
@@ -1097,19 +1116,19 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream,
static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
unsigned int ctrl;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl = ADAU1373_DAI_MASTER;
- adau1373_dai->master = true;
+ adau1373_dai->clock_provider = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl = 0;
- adau1373_dai->master = false;
+ adau1373_dai->clock_provider = false;
break;
default:
return -EINVAL;
@@ -1157,7 +1176,7 @@ static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(dai->component);
struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
switch (clk_id) {
@@ -1205,7 +1224,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.id = 1,
@@ -1225,7 +1244,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.id = 2,
@@ -1245,16 +1264,17 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+static int adau1373_set_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int dpll_div = 0;
- unsigned int x, r, n, m, i, j, mode;
+ uint8_t pll_regs[5];
+ int ret;
switch (pll_id) {
case ADAU1373_PLL1:
@@ -1295,27 +1315,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
dpll_div++;
}
- if (freq_out % freq_in != 0) {
- /* fout = fin * (r + (n/m)) / x */
- x = DIV_ROUND_UP(freq_in, 13500000);
- freq_in /= x;
- r = freq_out / freq_in;
- i = freq_out % freq_in;
- j = gcd(i, freq_in);
- n = i / j;
- m = freq_in / j;
- x--;
- mode = 1;
- } else {
- /* fout = fin / r */
- r = freq_out / freq_in;
- n = 0;
- m = 0;
- x = 0;
- mode = 0;
- }
-
- if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+ ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
+ if (ret)
return -EINVAL;
if (dpll_div) {
@@ -1330,12 +1331,11 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
(source << 4) | dpll_div);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id),
- (r << 3) | (x << 1) | mode);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]);
/* Set sysclk to pll_rate / 4 */
regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
@@ -1352,67 +1352,62 @@ static void adau1373_load_drc_settings(struct adau1373 *adau1373,
regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]);
}
-static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+static int adau1373_get_micbias(unsigned int val,
+ enum adau1373_micbias_voltage *micbias)
{
- switch (micbias) {
- case ADAU1373_MICBIAS_2_9V:
- case ADAU1373_MICBIAS_2_2V:
- case ADAU1373_MICBIAS_2_6V:
- case ADAU1373_MICBIAS_1_8V:
- return true;
+ switch (val) {
+ case 2900000:
+ *micbias = ADAU1373_MICBIAS_2_9V;
+ return 0;
+ case 2200000:
+ *micbias = ADAU1373_MICBIAS_2_2V;
+ return 0;
+ case 2600000:
+ *micbias = ADAU1373_MICBIAS_2_6V;
+ return 0;
+ case 1800000:
+ *micbias = ADAU1373_MICBIAS_1_8V;
+ return 0;
default:
- break;
+ return -EINVAL;
}
- return false;
}
-static int adau1373_probe(struct snd_soc_codec *codec)
+static int adau1373_probe(struct snd_soc_component *component)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
- struct adau1373_platform_data *pdata = codec->dev->platform_data;
- bool lineout_differential = false;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int val;
int i;
- if (pdata) {
- if (pdata->num_drc > ARRAY_SIZE(pdata->drc_setting))
- return -EINVAL;
-
- if (!adau1373_valid_micbias(pdata->micbias1) ||
- !adau1373_valid_micbias(pdata->micbias2))
- return -EINVAL;
-
- for (i = 0; i < pdata->num_drc; ++i) {
- adau1373_load_drc_settings(adau1373, i,
- pdata->drc_setting[i]);
- }
+ for (i = 0; i < adau1373->num_drc; ++i) {
+ adau1373_load_drc_settings(adau1373, i,
+ adau1373->drc_setting[i]);
+ }
- snd_soc_add_codec_controls(codec, adau1373_drc_controls,
- pdata->num_drc);
+ snd_soc_add_component_controls(component, adau1373_drc_controls,
+ adau1373->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;
+ for (i = 0; i < ARRAY_SIZE(adau1373->input_differential); ++i) {
+ if (adau1373->input_differential[i])
+ val |= BIT(i);
+ }
+ regmap_write(adau1373->regmap, ADAU1373_INPUT_MODE, val);
- val = 0;
- 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);
+ val = 0;
+ if (adau1373->lineout_differential)
+ val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+ if (adau1373->lineout_ground_sense)
+ val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
- lineout_differential = pdata->lineout_differential;
+ regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val);
- regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
- (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
- (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
- }
+ regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
+ (adau1373->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+ (adau1373->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
- if (!lineout_differential) {
- snd_soc_add_codec_controls(codec, adau1373_lineout2_controls,
+ if (!adau1373->lineout_differential) {
+ snd_soc_add_component_controls(component, adau1373_lineout2_controls,
ARRAY_SIZE(adau1373_lineout2_controls));
}
@@ -1422,10 +1417,10 @@ static int adau1373_probe(struct snd_soc_codec *codec)
return 0;
}
-static int adau1373_set_bias_level(struct snd_soc_codec *codec,
+static int adau1373_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1444,9 +1439,9 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int adau1373_resume(struct snd_soc_codec *codec)
+static int adau1373_resume(struct snd_soc_component *component)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
regcache_sync(adau1373->regmap);
@@ -1471,31 +1466,94 @@ static const struct regmap_config adau1373_regmap_config = {
.volatile_reg = adau1373_register_volatile,
.max_register = ADAU1373_SOFT_RESET,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1373_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults),
};
-static struct snd_soc_codec_driver adau1373_codec_driver = {
- .probe = adau1373_probe,
- .resume = adau1373_resume,
- .set_bias_level = adau1373_set_bias_level,
- .idle_bias_off = true,
+static const struct snd_soc_component_driver adau1373_component_driver = {
+ .probe = adau1373_probe,
+ .resume = adau1373_resume,
+ .set_bias_level = adau1373_set_bias_level,
+ .set_pll = adau1373_set_pll,
+ .controls = adau1373_controls,
+ .num_controls = ARRAY_SIZE(adau1373_controls),
+ .dapm_widgets = adau1373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+ .dapm_routes = adau1373_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
- .set_pll = adau1373_set_pll,
+static void adau1373_reset(void *reset_gpio)
+{
+ gpiod_set_value_cansleep(reset_gpio, 1);
+}
- .controls = adau1373_controls,
- .num_controls = ARRAY_SIZE(adau1373_controls),
- .dapm_widgets = adau1373_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
- .dapm_routes = adau1373_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
-};
+static int adau1373_parse_fw(struct device *dev, struct adau1373 *adau1373)
+{
+ int ret, drc_count;
+ unsigned int val;
-static int adau1373_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ if (device_property_present(dev, "adi,input1-differential"))
+ adau1373->input_differential[0] = true;
+ if (device_property_present(dev, "adi,input2-differential"))
+ adau1373->input_differential[1] = true;
+ if (device_property_present(dev, "adi,input3-differential"))
+ adau1373->input_differential[2] = true;
+ if (device_property_present(dev, "adi,input4-differential"))
+ adau1373->input_differential[3] = true;
+
+ if (device_property_present(dev, "adi,lineout-differential"))
+ adau1373->lineout_differential = true;
+ if (device_property_present(dev, "adi,lineout-gnd-sense"))
+ adau1373->lineout_ground_sense = true;
+
+ ret = device_property_read_u32(dev, "adi,micbias1-microvolt", &val);
+ if (!ret) {
+ ret = adau1373_get_micbias(val, &adau1373->micbias1);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get micbias1(%u)\n", val);
+ }
+
+ ret = device_property_read_u32(dev, "adi,micbias2-microvolt", &val);
+ if (!ret) {
+ ret = adau1373_get_micbias(val, &adau1373->micbias2);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get micbias2(%u)\n", val);
+ }
+
+ drc_count = device_property_count_u8(dev, "adi,drc-settings");
+ if (drc_count < 0)
+ return 0;
+ if (drc_count % ADAU1373_DRC_SIZE != 0)
+ return dev_err_probe(dev, -EINVAL,
+ "DRC count(%u) not multiple of %u\n",
+ drc_count, ADAU1373_DRC_SIZE);
+
+ adau1373->num_drc = drc_count / ADAU1373_DRC_SIZE;
+ if (adau1373->num_drc > ARRAY_SIZE(adau1373->drc_setting))
+ return dev_err_probe(dev, -EINVAL,
+ "Too many DRC settings(%u)\n",
+ adau1373->num_drc);
+
+ ret = device_property_read_u8_array(dev, "adi,drc-settings",
+ (u8 *)&adau1373->drc_setting[0],
+ drc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read DRC settings\n");
+
+ return 0;
+}
+
+static int adau1373_i2c_probe(struct i2c_client *client)
{
struct adau1373 *adau1373;
+ struct gpio_desc *gpiod;
int ret;
adau1373 = devm_kzalloc(&client->dev, sizeof(*adau1373), GFP_KERNEL);
@@ -1507,33 +1565,57 @@ static int adau1373_i2c_probe(struct i2c_client *client,
if (IS_ERR(adau1373->regmap))
return PTR_ERR(adau1373->regmap);
- regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+ /*
+ * If the powerdown GPIO is specified, we use it for reset. Otherwise
+ * a software reset is done.
+ */
+ gpiod = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+
+ if (gpiod) {
+ gpiod_set_value_cansleep(gpiod, 0);
+ fsleep(10);
+
+ ret = devm_add_action_or_reset(&client->dev, adau1373_reset,
+ gpiod);
+ if (ret)
+ return ret;
+ } else {
+ regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+ }
dev_set_drvdata(&client->dev, adau1373);
- ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+ ret = adau1373_parse_fw(&client->dev, adau1373);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1373_component_driver,
adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
return ret;
}
-static int adau1373_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id adau1373_i2c_id[] = {
- { "adau1373", 0 },
+ { "adau1373" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+static const struct of_device_id adau1373_of_match[] = {
+ { .compatible = "adi,adau1373", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1373_of_match);
+
static struct i2c_driver adau1373_i2c_driver = {
.driver = {
.name = "adau1373",
+ .of_match_table = adau1373_of_match,
},
.probe = adau1373_i2c_probe,
- .remove = adau1373_i2c_remove,
.id_table = adau1373_i2c_id,
};
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
index c6ab5530760c..56320d5e32d8 100644
--- a/sound/soc/codecs/adau1373.h
+++ b/sound/soc/codecs/adau1373.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ADAU1373_H__
#define __ADAU1373_H__
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index de53c0d7bf10..6876462d8bdb 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
* based on an inital version by Cliff Cai <cliff.cai@analog.com>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -14,8 +13,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -23,7 +21,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
#include "adau1701.h"
@@ -107,8 +105,8 @@ static const char * const supply_names[] = {
};
struct adau1701 {
- int gpio_nreset;
- int gpio_pll_mode[2];
+ struct gpio_desc *gpio_nreset;
+ struct gpio_descs *gpio_pll_mode;
unsigned int dai_fmt;
unsigned int pll_clkdiv;
unsigned int sysclk;
@@ -298,45 +296,45 @@ static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
.safeload = adau1701_safeload,
};
-static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
+static int adau1701_reset(struct snd_soc_component *component, unsigned int clkdiv,
unsigned int rate)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
int ret;
+ DECLARE_BITMAP(values, 2);
sigmadsp_reset(adau1701->sigmadsp);
- if (clkdiv != ADAU1707_CLKDIV_UNSET &&
- gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
- gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) {
switch (clkdiv) {
case 64:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 0);
break;
case 256:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 1);
break;
case 384:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 0);
break;
- case 0: /* fallback */
+ case 0: /* fallback */
case 512:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 1);
break;
}
+ gpiod_multi_set_value_cansleep(adau1701->gpio_pll_mode, values);
}
adau1701->pll_clkdiv = clkdiv;
- if (gpio_is_valid(adau1701->gpio_nreset)) {
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset) {
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
- gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
@@ -348,7 +346,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
if (clkdiv != ADAU1707_CLKDIV_UNSET) {
ret = sigmadsp_setup(adau1701->sigmadsp, rate);
if (ret) {
- dev_warn(codec->dev, "Failed to load firmware\n");
+ dev_warn(component->dev, "Failed to load firmware\n");
return ret;
}
}
@@ -362,10 +360,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
return 0;
}
-static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
+static int adau1701_set_capture_pcm_format(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
unsigned int val;
@@ -403,10 +401,10 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
return 0;
}
-static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
+static int adau1701_set_playback_pcm_format(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
@@ -435,8 +433,8 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adau1701_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int clkdiv = adau1701->sysclk / params_rate(params);
unsigned int val;
int ret;
@@ -447,7 +445,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
* firmware upload.
*/
if (clkdiv != adau1701->pll_clkdiv) {
- ret = adau1701_reset(codec, clkdiv, params_rate(params));
+ ret = adau1701_reset(component, clkdiv, params_rate(params));
if (ret < 0)
return ret;
}
@@ -470,26 +468,26 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
ADAU1701_DSPCTRL_SR_MASK, val);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return adau1701_set_playback_pcm_format(codec, params);
+ return adau1701_set_playback_pcm_format(component, params);
else
- return adau1701_set_capture_pcm_format(codec, params);
+ return adau1701_set_capture_pcm_format(component, params);
}
static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int serictl = 0x00, seroctl = 0x00;
bool invert_lrclk;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
/* master, 64-bits per sample, 1 frame per sample */
seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16
| ADAU1701_SEROCTL_OLF1024;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -548,11 +546,11 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int adau1701_set_bias_level(struct snd_soc_codec *codec,
+static int adau1701_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -574,11 +572,11 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
+static int adau1701_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (mute)
@@ -591,11 +589,11 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
return 0;
}
-static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int adau1701_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
unsigned int val;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -618,7 +616,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
static int adau1701_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(dai->component);
return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
}
@@ -632,8 +630,9 @@ static int adau1701_startup(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops adau1701_dai_ops = {
.set_fmt = adau1701_set_dai_fmt,
.hw_params = adau1701_hw_params,
- .digital_mute = adau1701_digital_mute,
+ .mute_stream = adau1701_mute_stream,
.startup = adau1701_startup,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver adau1701_dai = {
@@ -653,7 +652,7 @@ static struct snd_soc_dai_driver adau1701_dai = {
.formats = ADAU1701_FORMATS,
},
.ops = &adau1701_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
#ifdef CONFIG_OF
@@ -664,20 +663,20 @@ static const struct of_device_id adau1701_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
#endif
-static int adau1701_probe(struct snd_soc_codec *codec)
+static int adau1701_probe(struct snd_soc_component *component)
{
int i, ret;
unsigned int val;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
- ret = sigmadsp_attach(adau1701->sigmadsp, &codec->component);
+ ret = sigmadsp_attach(adau1701->sigmadsp, component);
if (ret)
return ret;
ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
adau1701->supplies);
if (ret < 0) {
- dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
@@ -690,7 +689,7 @@ static int adau1701_probe(struct snd_soc_codec *codec)
adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
/* initalize with pre-configured pll mode settings */
- ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+ ret = adau1701_reset(component, adau1701->pll_clkdiv, 0);
if (ret < 0)
goto exit_regulators_disable;
@@ -715,22 +714,20 @@ exit_regulators_disable:
return ret;
}
-static int adau1701_remove(struct snd_soc_codec *codec)
+static void adau1701_remove(struct snd_soc_component *component)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(adau1701->gpio_nreset))
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset)
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
-
- return 0;
}
#ifdef CONFIG_PM
-static int adau1701_suspend(struct snd_soc_codec *codec)
+static int adau1701_suspend(struct snd_soc_component *component)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies),
adau1701->supplies);
@@ -738,60 +735,56 @@ static int adau1701_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int adau1701_resume(struct snd_soc_codec *codec)
+static int adau1701_resume(struct snd_soc_component *component)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
adau1701->supplies);
if (ret < 0) {
- dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
- return adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+ return adau1701_reset(component, adau1701->pll_clkdiv, 0);
}
#else
#define adau1701_resume NULL
#define adau1701_suspend NULL
#endif /* CONFIG_PM */
-static struct snd_soc_codec_driver adau1701_codec_drv = {
+static const struct snd_soc_component_driver adau1701_component_drv = {
.probe = adau1701_probe,
.remove = adau1701_remove,
.resume = adau1701_resume,
.suspend = adau1701_suspend,
.set_bias_level = adau1701_set_bias_level,
- .idle_bias_off = true,
-
.controls = adau1701_controls,
.num_controls = ARRAY_SIZE(adau1701_controls),
.dapm_widgets = adau1701_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets),
.dapm_routes = adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
-
.set_sysclk = adau1701_set_sysclk,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config adau1701_regmap = {
.reg_bits = 16,
.val_bits = 32,
.max_register = ADAU1701_MAX_REGISTER,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = adau1701_volatile_reg,
.reg_write = adau1701_reg_write,
.reg_read = adau1701_reg_read,
};
-static int adau1701_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1701_i2c_probe(struct i2c_client *client)
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
- int gpio_nreset = -EINVAL;
- int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret, i;
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
@@ -825,26 +818,6 @@ static int adau1701_i2c_probe(struct i2c_client *client,
if (dev->of_node) {
- gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
- if (gpio_nreset < 0 && gpio_nreset != -ENOENT) {
- ret = gpio_nreset;
- goto exit_regulators_disable;
- }
-
- 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) {
- ret = gpio_pll_mode[0];
- goto exit_regulators_disable;
- }
-
- gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 1);
- if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) {
- ret = gpio_pll_mode[1];
- goto exit_regulators_disable;
- }
-
of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
&adau1701->pll_clkdiv);
@@ -853,32 +826,20 @@ static int adau1701_i2c_probe(struct i2c_client *client,
ARRAY_SIZE(adau1701->pin_config));
}
- if (gpio_is_valid(gpio_nreset)) {
- ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
- "ADAU1701 Reset");
- if (ret < 0)
- goto exit_regulators_disable;
+ adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN);
+
+ if (IS_ERR(adau1701->gpio_nreset)) {
+ ret = PTR_ERR(adau1701->gpio_nreset);
+ goto exit_regulators_disable;
}
- if (gpio_is_valid(gpio_pll_mode[0]) &&
- gpio_is_valid(gpio_pll_mode[1])) {
- ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 0");
- if (ret < 0)
- goto exit_regulators_disable;
+ adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW);
- ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 1");
- if (ret < 0)
- goto exit_regulators_disable;
+ if (IS_ERR(adau1701->gpio_pll_mode)) {
+ ret = PTR_ERR(adau1701->gpio_pll_mode);
+ goto exit_regulators_disable;
}
- adau1701->gpio_nreset = gpio_nreset;
- adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
- adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
-
i2c_set_clientdata(client, adau1701);
adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
@@ -888,7 +849,8 @@ static int adau1701_i2c_probe(struct i2c_client *client,
goto exit_regulators_disable;
}
- ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1701_component_drv,
&adau1701_dai, 1);
exit_regulators_disable:
@@ -897,17 +859,11 @@ exit_regulators_disable:
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 },
+ { "adau1401" },
+ { "adau1401a" },
+ { "adau1701" },
+ { "adau1702" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
@@ -918,7 +874,6 @@ static struct i2c_driver adau1701_i2c_driver = {
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
- .remove = adau1701_i2c_remove,
.id_table = adau1701_i2c_id,
};
diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h
index 8d0949a2aec9..19b41e453a36 100644
--- a/sound/soc/codecs/adau1701.h
+++ b/sound/soc/codecs/adau1701.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* header file for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADAU1701_H
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 348ccb17d3cc..eba7e4f42c78 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1761.h"
-static int adau1761_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1761_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,12 @@ static int adau1761_i2c_probe(struct i2c_client *client,
return adau1761_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
-static int adau1761_i2c_remove(struct i2c_client *client)
+static void adau1761_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ adau17x1_remove(&client->dev);
}
static const struct i2c_device_id adau1761_i2c_ids[] = {
@@ -44,9 +41,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_i2c_dt_ids[] = {
+ { .compatible = "adi,adau1361", },
+ { .compatible = "adi,adau1461", },
+ { .compatible = "adi,adau1761", },
+ { .compatible = "adi,adau1961", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids);
+#endif
+
static struct i2c_driver adau1761_i2c_driver = {
.driver = {
.name = "adau1761",
+ .of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
},
.probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index 8bc1fbd25fcc..7c9242c2ff94 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
@@ -46,10 +45,9 @@ static int adau1761_spi_probe(struct spi_device *spi)
id->driver_data, adau1761_spi_switch_mode);
}
-static int adau1761_spi_remove(struct spi_device *spi)
+static void adau1761_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
+ adau17x1_remove(&spi->dev);
}
static const struct spi_device_id adau1761_spi_id[] = {
@@ -61,9 +59,21 @@ static const struct spi_device_id adau1761_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_spi_dt_ids[] = {
+ { .compatible = "adi,adau1361", },
+ { .compatible = "adi,adau1461", },
+ { .compatible = "adi,adau1761", },
+ { .compatible = "adi,adau1961", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids);
+#endif
+
static struct spi_driver adau1761_spi_driver = {
.driver = {
.name = "adau1761",
+ .of_match_table = of_match_ptr(adau1761_spi_dt_ids),
},
.probe = adau1761_spi_probe,
.remove = adau1761_spi_remove,
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 2f12477e539e..a70c46dd5d76 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -29,6 +28,10 @@
#define ADAU1761_REC_MIXER_RIGHT1 0x400d
#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
+#define ADAU1761_ALC_CTRL0 0x4011
+#define ADAU1761_ALC_CTRL1 0x4012
+#define ADAU1761_ALC_CTRL2 0x4013
+#define ADAU1761_ALC_CTRL3 0x4014
#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
@@ -72,6 +75,10 @@ static const struct reg_default adau1761_reg_defaults[] = {
{ ADAU1761_REC_MIXER_RIGHT0, 0x00 },
{ ADAU1761_REC_MIXER_RIGHT1, 0x00 },
{ ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
+ { ADAU1761_ALC_CTRL0, 0x00 },
+ { ADAU1761_ALC_CTRL1, 0x00 },
+ { ADAU1761_ALC_CTRL2, 0x00 },
+ { ADAU1761_ALC_CTRL3, 0x00 },
{ ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
{ ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
{ ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
@@ -122,6 +129,10 @@ static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_max_gain_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_target_tlv, -2850, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_alc_ng_threshold_tlv, -7650, 150, 0);
+
static const unsigned int adau1761_bias_select_values[] = {
0, 2, 3,
};
@@ -148,6 +159,103 @@ static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
adau1761_bias_select_values);
+static const unsigned int adau1761_pga_slew_time_values[] = {
+ 3, 0, 1, 2,
+};
+
+static const char * const adau1761_pga_slew_time_text[] = {
+ "Off",
+ "24 ms",
+ "48 ms",
+ "96 ms",
+};
+
+static const char * const adau1761_alc_function_text[] = {
+ "Off",
+ "Right",
+ "Left",
+ "Stereo",
+ "DSP control",
+};
+
+static const char * const adau1761_alc_hold_time_text[] = {
+ "2.67 ms",
+ "5.34 ms",
+ "10.68 ms",
+ "21.36 ms",
+ "42.72 ms",
+ "85.44 ms",
+ "170.88 ms",
+ "341.76 ms",
+ "683.52 ms",
+ "1367 ms",
+ "2734.1 ms",
+ "5468.2 ms",
+ "10936 ms",
+ "21873 ms",
+ "43745 ms",
+ "87491 ms",
+};
+
+static const char * const adau1761_alc_attack_time_text[] = {
+ "6 ms",
+ "12 ms",
+ "24 ms",
+ "48 ms",
+ "96 ms",
+ "192 ms",
+ "384 ms",
+ "768 ms",
+ "1540 ms",
+ "3070 ms",
+ "6140 ms",
+ "12290 ms",
+ "24580 ms",
+ "49150 ms",
+ "98300 ms",
+ "196610 ms",
+};
+
+static const char * const adau1761_alc_decay_time_text[] = {
+ "24 ms",
+ "48 ms",
+ "96 ms",
+ "192 ms",
+ "384 ms",
+ "768 ms",
+ "15400 ms",
+ "30700 ms",
+ "61400 ms",
+ "12290 ms",
+ "24580 ms",
+ "49150 ms",
+ "98300 ms",
+ "196610 ms",
+ "393220 ms",
+ "786430 ms",
+};
+
+static const char * const adau1761_alc_ng_type_text[] = {
+ "Hold",
+ "Mute",
+ "Fade",
+ "Fade + Mute",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_pga_slew_time_enum,
+ ADAU1761_ALC_CTRL0, 6, 0x3, adau1761_pga_slew_time_text,
+ adau1761_pga_slew_time_values);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_function_enum,
+ ADAU1761_ALC_CTRL0, 0, adau1761_alc_function_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_hold_time_enum,
+ ADAU1761_ALC_CTRL1, 4, adau1761_alc_hold_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_attack_time_enum,
+ ADAU1761_ALC_CTRL2, 4, adau1761_alc_attack_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_decay_time_enum,
+ ADAU1761_ALC_CTRL2, 0, adau1761_alc_decay_time_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_alc_ng_type_enum,
+ ADAU1761_ALC_CTRL3, 6, adau1761_alc_ng_type_text);
+
static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
4, 1, 0),
@@ -162,6 +270,22 @@ static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
+
+ SOC_ENUM("PGA Capture Slew Time", adau1761_pga_slew_time_enum),
+
+ SOC_SINGLE_TLV("ALC Capture Max Gain Volume", ADAU1761_ALC_CTRL0,
+ 3, 7, 0, adau1761_alc_max_gain_tlv),
+ SOC_ENUM("ALC Capture Function", adau1761_alc_function_enum),
+ SOC_ENUM("ALC Capture Hold Time", adau1761_alc_hold_time_enum),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", ADAU1761_ALC_CTRL1,
+ 0, 15, 0, adau1761_alc_target_tlv),
+ SOC_ENUM("ALC Capture Attack Time", adau1761_alc_decay_time_enum),
+ SOC_ENUM("ALC Capture Decay Time", adau1761_alc_attack_time_enum),
+ SOC_ENUM("ALC Capture Noise Gate Type", adau1761_alc_ng_type_enum),
+ SOC_SINGLE("ALC Capture Noise Gate Switch",
+ ADAU1761_ALC_CTRL3, 5, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Noise Gate Threshold Volume",
+ ADAU1761_ALC_CTRL3, 0, 31, 0, adau1761_alc_ng_threshold_tlv),
};
static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
@@ -255,8 +379,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
@@ -432,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" },
- { "DSP", NULL, "Digital Clock 0" },
-
{ "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
@@ -445,10 +567,61 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Digital Clock 1", NULL, "SYSCLK" },
};
-static int adau1761_set_bias_level(struct snd_soc_codec *codec,
+static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = {
+ { "DSP", NULL, "Digital Clock 0" },
+};
+
+static int adau1761_compatibility_probe(struct device *dev)
+{
+ struct adau *adau = dev_get_drvdata(dev);
+ struct regmap *regmap = adau->regmap;
+ int val, ret = 0;
+
+ /* Only consider compatibility mode when ADAU1361 was specified. */
+ if (adau->type != ADAU1361)
+ return 0;
+
+ regcache_cache_bypass(regmap, true);
+
+ /*
+ * This will enable the core clock and bypass the PLL,
+ * so that we can access the registers for probing purposes
+ * (without having to set up the PLL).
+ */
+ regmap_write(regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+ /*
+ * ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips;
+ * reading it results in zero at all times, and write is a no-op.
+ * Use this register to probe for ADAU1761.
+ */
+ regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1);
+ ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+ if (ret)
+ goto exit;
+ if (val != 1)
+ goto exit;
+ regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0);
+ ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+ if (ret)
+ goto exit;
+ if (val != 0)
+ goto exit;
+
+ adau->type = ADAU1761_AS_1361;
+exit:
+ /* Disable core clock after probing. */
+ regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0);
+ regcache_cache_bypass(regmap, false);
+ return ret;
+}
+
+static int adau1761_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -456,13 +629,17 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
+ regcache_cache_only(adau->regmap, false);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ regcache_sync(adau->regmap);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+ regcache_cache_only(adau->regmap, true);
break;
}
@@ -470,9 +647,9 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
}
static enum adau1761_output_mode adau1761_get_lineout_mode(
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
if (pdata)
return pdata->lineout_mode;
@@ -480,11 +657,11 @@ static enum adau1761_output_mode adau1761_get_lineout_mode(
return ADAU1761_OUTPUT_MODE_LINE;
}
-static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
enum adau1761_digmic_jackdet_pin_mode mode;
unsigned int val = 0;
int ret;
@@ -509,12 +686,13 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
if (pdata->jackdetect_active_low)
val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
- ret = snd_soc_add_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
adau1761_jack_detect_controls,
ARRAY_SIZE(adau1761_jack_detect_controls));
if (ret)
return ret;
- case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
+ fallthrough;
+ case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE:
ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes,
ARRAY_SIZE(adau1761_no_dmic_routes));
if (ret)
@@ -542,11 +720,11 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
return 0;
}
-static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
+static int adau1761_setup_headphone_mode(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
enum adau1761_output_mode mode;
int ret;
@@ -564,7 +742,7 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
- /* fallthrough */
+ fallthrough;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
@@ -584,7 +762,7 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
adau1761_capless_dapm_routes,
ARRAY_SIZE(adau1761_capless_dapm_routes));
} else {
- ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
+ ret = snd_soc_add_component_controls(component, adau1761_mono_controls,
ARRAY_SIZE(adau1761_mono_controls));
if (ret)
return ret;
@@ -628,6 +806,10 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
case ADAU1761_DEJITTER:
case ADAU1761_CLK_ENABLE0:
case ADAU1761_CLK_ENABLE1:
+ case ADAU1761_ALC_CTRL0:
+ case ADAU1761_ALC_CTRL1:
+ case ADAU1761_ALC_CTRL2:
+ case ADAU1761_ALC_CTRL3:
return true;
default:
break;
@@ -636,14 +818,14 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
return adau17x1_readable_register(dev, reg);
}
-static int adau1761_codec_probe(struct snd_soc_codec *codec)
+static int adau1761_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
int ret;
- ret = adau17x1_add_widgets(codec);
+ ret = adau17x1_add_widgets(component);
if (ret < 0)
return ret;
@@ -654,20 +836,20 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
- ret = snd_soc_add_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
adau1761_differential_mode_controls,
ARRAY_SIZE(adau1761_differential_mode_controls));
if (ret)
return ret;
} else {
- ret = snd_soc_add_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
adau1761_single_mode_controls,
ARRAY_SIZE(adau1761_single_mode_controls));
if (ret)
return ret;
}
- switch (adau1761_get_lineout_mode(codec)) {
+ switch (adau1761_get_lineout_mode(component)) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
@@ -682,15 +864,19 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
return -EINVAL;
}
- ret = adau1761_setup_headphone_mode(codec);
+ ret = adau1761_setup_headphone_mode(component);
if (ret)
return ret;
- ret = adau1761_setup_digmic_jackdetect(codec);
+ ret = adau1761_setup_digmic_jackdetect(component);
if (ret)
return ret;
- if (adau->type == ADAU1761) {
+ /*
+ * If we've got an ADAU1761, or an ADAU1761 operating as an
+ * ADAU1361, we need these non-DSP related DAPM widgets and routes.
+ */
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) {
ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
ARRAY_SIZE(adau1761_dapm_widgets));
if (ret)
@@ -701,26 +887,50 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
if (ret)
return ret;
}
-
- ret = adau17x1_add_routes(codec);
+ /*
+ * These routes are DSP related and only used when we have a
+ * bona fide ADAU1761.
+ */
+ if (adau->type == ADAU1761) {
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes,
+ ARRAY_SIZE(adau1761_dapm_dsp_routes));
+ if (ret)
+ return ret;
+ }
+ /*
+ * In the ADAU1761, by default, the AIF is routed to the DSP, whereas
+ * for the ADAU1361, the AIF is permanently routed to the ADC and DAC.
+ * Thus, if we have an ADAU1761 masquerading as an ADAU1361,
+ * we need to explicitly route the AIF to the ADC and DAC.
+ * For the ADAU1761, this is normally done by set_tdm_slot, but this
+ * function is not necessarily called during stream setup, so set up
+ * the compatible AIF routings here from the start.
+ */
+ if (adau->type == ADAU1761_AS_1361) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01);
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01);
+ }
+ ret = adau17x1_add_routes(component);
if (ret < 0)
return ret;
return 0;
}
-static const struct snd_soc_codec_driver adau1761_codec_driver = {
- .probe = adau1761_codec_probe,
- .resume = adau17x1_resume,
- .set_bias_level = adau1761_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = adau1761_controls,
- .num_controls = ARRAY_SIZE(adau1761_controls),
- .dapm_widgets = adau1x61_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
- .dapm_routes = adau1x61_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
+static const struct snd_soc_component_driver adau1761_component_driver = {
+ .probe = adau1761_component_probe,
+ .resume = adau17x1_resume,
+ .set_bias_level = adau1761_set_bias_level,
+ .controls = adau1761_controls,
+ .num_controls = ARRAY_SIZE(adau1761_controls),
+ .dapm_widgets = adau1x61_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
+ .dapm_routes = adau1x61_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
@@ -783,7 +993,16 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
+ ret = adau1761_compatibility_probe(dev);
+ if (ret)
+ return ret;
+
+ /* Enable cache only mode as we could miss writes before bias level
+ * reaches standby and the core clock is enabled */
+ regcache_cache_only(regmap, true);
+
+ return devm_snd_soc_register_component(dev, &adau1761_component_driver,
+ dai_drv, 1);
}
EXPORT_SYMBOL_GPL(adau1761_probe);
@@ -796,7 +1015,7 @@ const struct regmap_config adau1761_regmap_config = {
.readable_reg = adau1761_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1761_regmap_config);
diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h
index a9e0d288301e..7beabf448ad1 100644
--- a/sound/soc/codecs/adau1761.h
+++ b/sound/soc/codecs/adau1761.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 0e32bba92339..cb67fde8d9a8 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1781.h"
-static int adau1781_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1781_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,12 @@ static int adau1781_i2c_probe(struct i2c_client *client,
return adau1781_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
-static int adau1781_i2c_remove(struct i2c_client *client)
+static void adau1781_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ adau17x1_remove(&client->dev);
}
static const struct i2c_device_id adau1781_i2c_ids[] = {
@@ -42,9 +39,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_i2c_dt_ids[] = {
+ { .compatible = "adi,adau1381", },
+ { .compatible = "adi,adau1781", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids);
+#endif
+
static struct i2c_driver adau1781_i2c_driver = {
.driver = {
.name = "adau1781",
+ .of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
},
.probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 33a73ff78de4..1a09633d5a88 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
@@ -46,10 +45,9 @@ static int adau1781_spi_probe(struct spi_device *spi)
id->driver_data, adau1781_spi_switch_mode);
}
-static int adau1781_spi_remove(struct spi_device *spi)
+static void adau1781_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
+ adau17x1_remove(&spi->dev);
}
static const struct spi_device_id adau1781_spi_id[] = {
@@ -59,9 +57,19 @@ static const struct spi_device_id adau1781_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_spi_dt_ids[] = {
+ { .compatible = "adi,adau1381", },
+ { .compatible = "adi,adau1781", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids);
+#endif
+
static struct spi_driver adau1781_spi_driver = {
.driver = {
.name = "adau1781",
+ .of_match_table = of_match_ptr(adau1781_spi_dt_ids),
},
.probe = adau1781_spi_probe,
.remove = adau1781_spi_remove,
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index fde9068550a6..38cc0b6e4122 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Driver for ADAU1781/ADAU1781 codec
+ * Driver for ADAU1381/ADAU1781 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -174,8 +173,8 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
@@ -314,10 +313,10 @@ static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
{ "Right Decimator", NULL, "DMIC Select" },
};
-static int adau1781_set_bias_level(struct snd_soc_codec *codec,
+static int adau1781_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -380,14 +379,14 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
ADAU1781_INPUT_DIFFERNTIAL, val);
}
-static int adau1781_codec_probe(struct snd_soc_codec *codec)
+static int adau1781_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau1781_platform_data *pdata = dev_get_platdata(component->dev);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
int ret;
- ret = adau17x1_add_widgets(codec);
+ ret = adau17x1_add_widgets(component);
if (ret)
return ret;
@@ -419,25 +418,27 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
return ret;
}
- ret = adau17x1_add_routes(codec);
+ ret = adau17x1_add_routes(component);
if (ret < 0)
return ret;
return 0;
}
-static const struct snd_soc_codec_driver adau1781_codec_driver = {
- .probe = adau1781_codec_probe,
- .resume = adau17x1_resume,
- .set_bias_level = adau1781_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = adau1781_controls,
- .num_controls = ARRAY_SIZE(adau1781_controls),
- .dapm_widgets = adau1781_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
- .dapm_routes = adau1781_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
+static const struct snd_soc_component_driver adau1781_component_driver = {
+ .probe = adau1781_component_probe,
+ .resume = adau17x1_resume,
+ .set_bias_level = adau1781_set_bias_level,
+ .controls = adau1781_controls,
+ .num_controls = ARRAY_SIZE(adau1781_controls),
+ .dapm_widgets = adau1781_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
+ .dapm_routes = adau1781_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
@@ -471,7 +472,7 @@ const struct regmap_config adau1781_regmap_config = {
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
@@ -496,7 +497,7 @@ int adau1781_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1781_codec_driver,
+ return devm_snd_soc_register_component(dev, &adau1781_component_driver,
&adau1781_dai_driver, 1);
}
EXPORT_SYMBOL_GPL(adau1781_probe);
diff --git a/sound/soc/codecs/adau1781.h b/sound/soc/codecs/adau1781.h
index 2b96e0a9ff2e..ac8b8acbdbbd 100644
--- a/sound/soc/codecs/adau1781.h
+++ b/sound/soc/codecs/adau1781.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1381/ADAU1781 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fcf05b254ecd..26d7eb437ad1 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -1,14 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Common code for ADAU1X61 and ADAU1X81 codecs
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -16,13 +16,20 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
#include "adau17x1.h"
+#include "adau-utils.h"
+
+#define ADAU17X1_SAFELOAD_TARGET_ADDRESS 0x0006
+#define ADAU17X1_SAFELOAD_TRIGGER 0x0007
+#define ADAU17X1_SAFELOAD_DATA 0x0001
+#define ADAU17X1_SAFELOAD_DATA_SIZE 20
+#define ADAU17X1_WORD_SIZE 4
static const char * const adau17x1_capture_mixer_boost_text[] = {
"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
@@ -58,12 +65,14 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
};
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+ unsigned int rate);
+
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- int ret;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
if (SND_SOC_DAPM_EVENT_ON(event)) {
adau->pll_regs[5] = 1;
@@ -76,7 +85,7 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
}
/* The PLL register is 6 bytes long and can only be written at once. */
- ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (SND_SOC_DAPM_EVENT_ON(event)) {
@@ -89,6 +98,27 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int adau17x1_adc_fixup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+
+ /*
+ * If we are capturing, toggle the ADOSR bit in Converter Control 0 to
+ * avoid losing SNR (workaround from ADI). This must be done after
+ * the ADC(s) have been enabled. According to the data sheet, it is
+ * normally illegal to set this bit when the sampling rate is 96 kHz,
+ * but according to ADI it is acceptable for this workaround.
+ */
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_ADOSR, ADAU17X1_CONVERTER0_ADOSR);
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_ADOSR, 0);
+
+ return 0;
+}
+
static const char * const adau17x1_mono_stereo_text[] = {
"Stereo",
"Mono Left Channel (L+R)",
@@ -120,7 +150,8 @@ static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mode_mux),
- SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
+ SND_SOC_DAPM_ADC_E("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0,
+ adau17x1_adc_fixup, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
@@ -154,11 +185,11 @@ static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = {};
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
@@ -182,7 +213,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
- change = snd_soc_test_bits(codec, reg, 0xff, val);
+ change = snd_soc_component_test_bits(component, reg, 0xff, val);
if (change) {
update.kcontrol = kcontrol;
update.mask = 0xff;
@@ -199,8 +230,8 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int stream = e->shift_l;
unsigned int reg, val;
@@ -224,7 +255,7 @@ static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \
const struct snd_kcontrol_new _name = \
- SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\
+ SOC_ENUM_EXT(_label, (const struct soc_enum)\
SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \
ARRAY_SIZE(_text), _text), \
adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
@@ -276,6 +307,7 @@ static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
{ "DSP", NULL, "Left Decimator" },
{ "DSP", NULL, "Right Decimator" },
+ { "DSP", NULL, "Playback" },
};
static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
@@ -289,7 +321,7 @@ static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
{ "Capture", NULL, "Right Decimator" },
};
-bool adau17x1_has_dsp(struct adau *adau)
+static bool adau17x1_has_dsp(struct adau *adau)
{
switch (adau->type) {
case ADAU1761:
@@ -300,21 +332,161 @@ bool adau17x1_has_dsp(struct adau *adau)
return false;
}
}
-EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+/* Chip has a DSP but we're pretending it doesn't. */
+static bool adau17x1_has_disused_dsp(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761_AS_1361:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool adau17x1_has_safeload(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761:
+ case ADAU1781:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (freq_in < 8000000 || freq_in > 27000000)
+ return -EINVAL;
+
+ ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs);
+ if (ret < 0)
+ return ret;
+
+ /* The PLL register is 6 bytes long and can only be written at once. */
+ ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+ if (ret)
+ return ret;
+
+ adau->pll_freq = freq_out;
+
+ return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(dai->component);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
+ bool is_pll;
+ bool was_pll;
+
+ switch (clk_id) {
+ case ADAU17X1_CLK_SRC_MCLK:
+ is_pll = false;
+ break;
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ if (!adau->mclk)
+ return -EINVAL;
+ fallthrough;
+ case ADAU17X1_CLK_SRC_PLL:
+ is_pll = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (adau->clk_src) {
+ case ADAU17X1_CLK_SRC_MCLK:
+ was_pll = false;
+ break;
+ case ADAU17X1_CLK_SRC_PLL:
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ was_pll = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau->sysclk = freq;
+
+ if (is_pll != was_pll) {
+ if (is_pll) {
+ snd_soc_dapm_add_routes(dapm,
+ &adau17x1_dapm_pll_route, 1);
+ } else {
+ snd_soc_dapm_del_routes(dapm,
+ &adau17x1_dapm_pll_route, 1);
+ }
+ }
+
+ adau->clk_src = clk_id;
+
+ return 0;
+}
+
+static int adau17x1_auto_pll(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau *adau = snd_soc_dai_get_drvdata(dai);
+ unsigned int pll_rate;
+
+ switch (params_rate(params)) {
+ case 48000:
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 96000:
+ pll_rate = 48000 * 1024;
+ break;
+ case 44100:
+ case 7350:
+ case 11025:
+ case 14700:
+ case 22050:
+ case 29400:
+ case 88200:
+ pll_rate = 44100 * 1024;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK,
+ clk_get_rate(adau->mclk), pll_rate);
+}
static int adau17x1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
unsigned int val, div, dsp_div;
unsigned int freq;
int ret;
- if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+ switch (adau->clk_src) {
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ ret = adau17x1_auto_pll(dai, params);
+ if (ret)
+ return ret;
+ fallthrough;
+ case ADAU17X1_CLK_SRC_PLL:
freq = adau->pll_freq;
- else
+ break;
+ default:
freq = adau->sysclk;
+ break;
+ }
if (freq % params_rate(params) != 0)
return -EINVAL;
@@ -354,13 +526,14 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div);
- if (adau17x1_has_dsp(adau)) {
+
+ if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau))
regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+ if (adau17x1_has_dsp(adau))
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
- }
if (adau->sigmadsp) {
- ret = adau17x1_setup_firmware(adau, params_rate(params));
+ ret = adau17x1_setup_firmware(component, params_rate(params));
if (ret < 0)
return ret;
}
@@ -386,106 +559,20 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
}
-static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
- int source, unsigned int freq_in, unsigned int freq_out)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- unsigned int r, n, m, i, j;
- unsigned int div;
- int ret;
-
- if (freq_in < 8000000 || freq_in > 27000000)
- return -EINVAL;
-
- if (!freq_out) {
- r = 0;
- n = 0;
- m = 0;
- div = 0;
- } else {
- if (freq_out % freq_in != 0) {
- div = DIV_ROUND_UP(freq_in, 13500000);
- freq_in /= div;
- r = freq_out / freq_in;
- i = freq_out % freq_in;
- j = gcd(i, freq_in);
- n = i / j;
- m = freq_in / j;
- div--;
- } else {
- r = freq_out / freq_in;
- n = 0;
- m = 0;
- div = 0;
- }
- if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
- return -EINVAL;
- }
-
- adau->pll_regs[0] = m >> 8;
- adau->pll_regs[1] = m & 0xff;
- adau->pll_regs[2] = n >> 8;
- adau->pll_regs[3] = n & 0xff;
- adau->pll_regs[4] = (r << 3) | (div << 1);
- if (m != 0)
- adau->pll_regs[4] |= 1; /* Fractional mode */
-
- /* The PLL register is 6 bytes long and can only be written at once. */
- ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
- adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
- if (ret)
- return ret;
-
- adau->pll_freq = freq_out;
-
- return 0;
-}
-
-static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
-
- switch (clk_id) {
- case ADAU17X1_CLK_SRC_MCLK:
- case ADAU17X1_CLK_SRC_PLL:
- break;
- default:
- return -EINVAL;
- }
-
- adau->sysclk = freq;
-
- if (adau->clk_src != clk_id) {
- if (clk_id == ADAU17X1_CLK_SRC_PLL) {
- snd_soc_dapm_add_routes(dapm,
- &adau17x1_dapm_pll_route, 1);
- } else {
- snd_soc_dapm_del_routes(dapm,
- &adau17x1_dapm_pll_route, 1);
- }
- }
-
- adau->clk_src = clk_id;
-
- return 0;
-}
-
static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl0, ctrl1;
+ unsigned int ctrl0_mask;
int lrclk_pol;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
adau->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl0 = 0;
adau->master = false;
break;
@@ -537,8 +624,16 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
if (lrclk_pol)
ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
- regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
- regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
+ /* Set the mask to update all relevant bits in ADAU17X1_SERIAL_PORT0 */
+ ctrl0_mask = ADAU17X1_SERIAL_PORT0_MASTER |
+ ADAU17X1_SERIAL_PORT0_LRCLK_POL |
+ ADAU17X1_SERIAL_PORT0_BCLK_POL |
+ ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0_mask,
+ ctrl0);
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+ ADAU17X1_SERIAL_PORT1_DELAY_MASK, ctrl1);
adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
@@ -548,7 +643,7 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
unsigned int ser_ctrl0, ser_ctrl1;
unsigned int conv_ctrl0, conv_ctrl1;
@@ -579,7 +674,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
switch (slot_width * slots) {
case 32:
- if (adau->type == ADAU1761)
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
@@ -654,7 +749,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
- if (!adau17x1_has_dsp(adau))
+ if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
@@ -673,7 +768,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
static int adau17x1_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
if (adau->sigmadsp)
return sigmadsp_restrict_params(adau->sigmadsp, substream);
@@ -691,10 +786,10 @@ const struct snd_soc_dai_ops adau17x1_dai_ops = {
};
EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
-int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+int adau17x1_set_micbias_voltage(struct snd_soc_component *component,
enum adau17x1_micbias_voltage micbias)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (micbias) {
case ADAU17X1_MICBIAS_0_90_AVDD:
@@ -780,36 +875,58 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
-int adau17x1_setup_firmware(struct adau *adau, unsigned int rate)
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+ unsigned int rate)
{
int ret;
- int dspsr;
+ int dspsr, dsp_run;
+ struct adau *adau = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ /* Check if sample rate is the same as before. If it is there is no
+ * point in performing the below steps as the call to
+ * sigmadsp_setup(...) will return directly when it finds the sample
+ * rate to be the same as before. By checking this we can prevent an
+ * audiable popping noise which occours when toggling DSP_RUN.
+ */
+ if (adau->sigmadsp->current_samplerate == rate)
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
if (ret)
- return ret;
+ goto err;
+
+ ret = regmap_read(adau->regmap, ADAU17X1_DSP_RUN, &dsp_run);
+ if (ret)
+ goto err;
regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
+ regmap_write(adau->regmap, ADAU17X1_DSP_RUN, 0);
ret = sigmadsp_setup(adau->sigmadsp, rate);
if (ret) {
regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
- return ret;
+ goto err;
}
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+ regmap_write(adau->regmap, ADAU17X1_DSP_RUN, dsp_run);
- return 0;
+err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
-int adau17x1_add_widgets(struct snd_soc_codec *codec)
+int adau17x1_add_widgets(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
int ret;
- ret = snd_soc_add_codec_controls(codec, adau17x1_controls,
+ ret = snd_soc_add_component_controls(component, adau17x1_controls,
ARRAY_SIZE(adau17x1_controls));
if (ret)
return ret;
@@ -827,9 +944,9 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
if (!adau->sigmadsp)
return 0;
- ret = sigmadsp_attach(adau->sigmadsp, &codec->component);
+ ret = sigmadsp_attach(adau->sigmadsp, component);
if (ret) {
- dev_err(codec->dev, "Failed to attach firmware: %d\n",
+ dev_err(component->dev, "Failed to attach firmware: %d\n",
ret);
return ret;
}
@@ -839,10 +956,10 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
-int adau17x1_add_routes(struct snd_soc_codec *codec)
+int adau17x1_add_routes(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
int ret;
ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes,
@@ -857,16 +974,20 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
}
+
+ if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK)
+ snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1);
+
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
-int adau17x1_resume(struct snd_soc_codec *codec)
+int adau17x1_resume(struct snd_soc_component *component)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
if (adau->switch_mode)
- adau->switch_mode(codec->dev);
+ adau->switch_mode(component->dev);
regcache_sync(adau->regmap);
@@ -874,11 +995,62 @@ int adau17x1_resume(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(adau17x1_resume);
+static int adau17x1_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+ const uint8_t bytes[], size_t len)
+{
+ uint8_t buf[ADAU17X1_WORD_SIZE];
+ uint8_t data[ADAU17X1_SAFELOAD_DATA_SIZE];
+ unsigned int addr_offset;
+ unsigned int nbr_words;
+ int ret;
+
+ /* write data to safeload addresses. Check if len is not a multiple of
+ * 4 bytes, if so we need to zero pad.
+ */
+ nbr_words = len / ADAU17X1_WORD_SIZE;
+ if ((len - nbr_words * ADAU17X1_WORD_SIZE) == 0) {
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_DATA, bytes, len);
+ } else {
+ nbr_words++;
+ memset(data, 0, ADAU17X1_SAFELOAD_DATA_SIZE);
+ memcpy(data, bytes, len);
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_DATA, data,
+ nbr_words * ADAU17X1_WORD_SIZE);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Write target address, target address is offset by 1 */
+ addr_offset = addr - 1;
+ put_unaligned_be32(addr_offset, buf);
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_TARGET_ADDRESS, buf, ADAU17X1_WORD_SIZE);
+ if (ret < 0)
+ return ret;
+
+ /* write nbr of words to trigger address */
+ put_unaligned_be32(nbr_words, buf);
+ ret = regmap_raw_write(sigmadsp->control_data,
+ ADAU17X1_SAFELOAD_TRIGGER, buf, ADAU17X1_WORD_SIZE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct sigmadsp_ops adau17x1_sigmadsp_ops = {
+ .safeload = adau17x1_safeload,
+};
+
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev),
const char *firmware_name)
{
struct adau *adau;
+ int ret;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -887,6 +1059,29 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
if (!adau)
return -ENOMEM;
+ /* Clock is optional (for the driver) */
+ adau->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(adau->mclk))
+ return PTR_ERR(adau->mclk);
+
+ if (adau->mclk) {
+ adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
+
+ /*
+ * Any valid PLL output rate will work at this point, use one
+ * that is likely to be chosen later as well. The register will
+ * be written when the PLL is powered up for the first time.
+ */
+ ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024,
+ adau->pll_regs);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(adau->mclk);
+ if (ret)
+ return ret;
+ }
+
adau->regmap = regmap;
adau->switch_mode = switch_mode;
adau->type = type;
@@ -894,8 +1089,13 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, adau);
if (firmware_name) {
- adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
- firmware_name);
+ if (adau17x1_has_safeload(adau)) {
+ adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap,
+ &adau17x1_sigmadsp_ops, firmware_name);
+ } else {
+ adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap,
+ NULL, firmware_name);
+ }
if (IS_ERR(adau->sigmadsp)) {
dev_warn(dev, "Could not find firmware file: %ld\n",
PTR_ERR(adau->sigmadsp));
@@ -910,6 +1110,14 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
}
EXPORT_SYMBOL_GPL(adau17x1_probe);
+void adau17x1_remove(struct device *dev)
+{
+ struct adau *adau = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(adau->mclk);
+}
+EXPORT_SYMBOL_GPL(adau17x1_remove);
+
MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index e13583e6ff56..5e58abfffc3d 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ADAU17X1_H__
#define __ADAU17X1_H__
@@ -9,6 +10,7 @@
enum adau17x1_type {
ADAU1361,
ADAU1761,
+ ADAU1761_AS_1361,
ADAU1381,
ADAU1781,
};
@@ -22,13 +24,18 @@ enum adau17x1_pll_src {
};
enum adau17x1_clk_src {
+ /* Automatically configure PLL based on the sample rate */
+ ADAU17X1_CLK_SRC_PLL_AUTO,
ADAU17X1_CLK_SRC_MCLK,
ADAU17X1_CLK_SRC_PLL,
};
+struct clk;
+
struct adau {
unsigned int sysclk;
unsigned int pll_freq;
+ struct clk *mclk;
enum adau17x1_clk_src clk_src;
enum adau17x1_type type;
@@ -47,23 +54,21 @@ struct adau {
struct sigmadsp *sigmadsp;
};
-int adau17x1_add_widgets(struct snd_soc_codec *codec);
-int adau17x1_add_routes(struct snd_soc_codec *codec);
+int adau17x1_add_widgets(struct snd_soc_component *component);
+int adau17x1_add_routes(struct snd_soc_component *component);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev),
const char *firmware_name);
-int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+void adau17x1_remove(struct device *dev);
+int adau17x1_set_micbias_voltage(struct snd_soc_component *component,
enum adau17x1_micbias_voltage micbias);
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
bool adau17x1_precious_register(struct device *dev, unsigned int reg);
-int adau17x1_resume(struct snd_soc_codec *codec);
+int adau17x1_resume(struct snd_soc_component *component);
extern const struct snd_soc_dai_ops adau17x1_dai_ops;
-int adau17x1_setup_firmware(struct adau *adau, unsigned int rate);
-bool adau17x1_has_dsp(struct adau *adau);
-
#define ADAU17X1_CLOCK_CONTROL 0x4000
#define ADAU17X1_PLL_CONTROL 0x4002
#define ADAU17X1_REC_POWER_MGMT 0x4009
@@ -103,9 +108,9 @@ bool adau17x1_has_dsp(struct adau *adau);
#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0)
-#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x0 << 5)
-#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x1 << 5)
-#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x2 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x0 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x1 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x2 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5)
@@ -123,5 +128,7 @@ bool adau17x1_has_dsp(struct adau *adau);
#define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7
+#define ADAU17X1_CONVERTER0_ADOSR BIT(3)
+
#endif
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
index 21e7394a972a..441c8079246a 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1977.h"
-static int adau1977_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1977_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,7 @@ static int adau1977_i2c_probe(struct i2c_client *client,
return adau1977_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
-}
-
-static int adau1977_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
static const struct i2c_device_id adau1977_i2c_ids[] = {
@@ -48,7 +40,6 @@ static struct i2c_driver adau1977_i2c_driver = {
.name = "adau1977",
},
.probe = adau1977_i2c_probe,
- .remove = adau1977_i2c_remove,
.id_table = adau1977_i2c_ids,
};
module_i2c_driver(adau1977_i2c_driver);
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
index 0b46d88b481c..e7e95e5d1911 100644
--- a/sound/soc/codecs/adau1977-spi.c
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -1,15 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/of.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
@@ -46,12 +46,6 @@ static int adau1977_spi_probe(struct spi_device *spi)
id->driver_data, adau1977_spi_switch_mode);
}
-static int adau1977_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id adau1977_spi_ids[] = {
{ "adau1977", ADAU1977 },
{ "adau1978", ADAU1978 },
@@ -60,12 +54,20 @@ static const struct spi_device_id adau1977_spi_ids[] = {
};
MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
+static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = {
+ { .compatible = "adi,adau1977" },
+ { .compatible = "adi,adau1978" },
+ { .compatible = "adi,adau1979" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1977_spi_of_match);
+
static struct spi_driver adau1977_spi_driver = {
.driver = {
.name = "adau1977",
+ .of_match_table = of_match_ptr(adau1977_spi_of_match),
},
.probe = adau1977_spi_probe,
- .remove = adau1977_spi_remove,
.id_table = adau1977_spi_ids,
};
module_spi_driver(adau1977_spi_driver);
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index 9bdd15f408c1..b42f35b1a603 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/delay.h>
@@ -13,7 +12,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/platform_data/adau1977.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -25,6 +23,8 @@
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <dt-bindings/sound/adi,adau1977.h>
+
#include "adau1977.h"
#define ADAU1977_REG_POWER 0x00
@@ -124,10 +124,10 @@ struct adau1977 {
struct device *dev;
void (*switch_mode)(struct device *dev);
- unsigned int max_master_fs;
+ unsigned int max_clock_provider_fs;
unsigned int slot_width;
bool enabled;
- bool master;
+ bool clock_provider;
};
static const struct reg_default adau1977_reg_defaults[] = {
@@ -236,8 +236,6 @@ static int adau1977_reset(struct adau1977 *adau1977)
ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER,
ADAU1977_POWER_RESET);
regcache_cache_bypass(adau1977->regmap, false);
- if (ret)
- return ret;
return ret;
}
@@ -294,8 +292,8 @@ static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate,
static int adau1977_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
unsigned int slot_width;
unsigned int ctrl0, ctrl0_mask;
@@ -332,7 +330,7 @@ static int adau1977_hw_params(struct snd_pcm_substream *substream,
ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
}
- if (adau1977->master) {
+ if (adau1977->clock_provider) {
switch (params_width(params)) {
case 16:
ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
@@ -388,8 +386,7 @@ static int adau1977_power_disable(struct adau1977 *adau1977)
regcache_mark_dirty(adau1977->regmap);
- if (adau1977->reset_gpio)
- gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
regcache_cache_only(adau1977->regmap, true);
@@ -420,8 +417,7 @@ static int adau1977_power_enable(struct adau1977 *adau1977)
goto err_disable_avdd;
}
- if (adau1977->reset_gpio)
- gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
regcache_cache_only(adau1977->regmap, false);
@@ -473,10 +469,11 @@ err_disable_avdd:
return ret;
}
-static int adau1977_set_bias_level(struct snd_soc_codec *codec,
+static int adau1977_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret = 0;
switch (level) {
@@ -485,7 +482,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
ret = adau1977_power_enable(adau1977);
break;
case SND_SOC_BIAS_OFF:
@@ -499,7 +496,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl0, ctrl1, drv;
unsigned int slot[4];
unsigned int i;
@@ -508,7 +505,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slots == 0) {
/* 0 = No fixed slot width */
adau1977->slot_width = 0;
- adau1977->max_master_fs = 192000;
+ adau1977->max_clock_provider_fs = 192000;
return regmap_update_bits(adau1977->regmap,
ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK,
ADAU1977_SAI_CTRL0_SAI_I2S);
@@ -537,7 +534,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 24:
/* We can only generate 16 bit or 32 bit wide slots */
- if (adau1977->master)
+ if (adau1977->clock_provider)
return -EINVAL;
ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
break;
@@ -597,15 +594,15 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
adau1977->slot_width = width;
- /* In master mode the maximum bitclock is 24.576 MHz */
- adau1977->max_master_fs = min(192000, 24576000 / width / slots);
+ /* In clock provider mode the maximum bitclock is 24.576 MHz */
+ adau1977->max_clock_provider_fs = min(192000, 24576000 / width / slots);
return 0;
}
static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
@@ -619,18 +616,18 @@ static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream)
static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl0 = 0, ctrl1 = 0, block_power = 0;
bool invert_lrclk;
int ret;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- adau1977->master = false;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1977->clock_provider = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
- adau1977->master = true;
+ adau1977->clock_provider = true;
break;
default:
return -EINVAL;
@@ -706,7 +703,7 @@ static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adau1977_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
u64 formats = 0;
if (adau1977->slot_width == 16)
@@ -718,9 +715,10 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
- if (adau1977->master)
+ if (adau1977->clock_provider)
snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs);
+ SNDRV_PCM_HW_PARAM_RATE, 8000,
+ adau1977->max_clock_provider_fs);
if (formats != 0)
snd_pcm_hw_constraint_mask64(substream->runtime,
@@ -731,7 +729,7 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (tristate)
@@ -792,13 +790,13 @@ static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq)
return true;
}
-static int adau1977_set_sysclk(struct snd_soc_codec *codec,
+static int adau1977_set_sysclk(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
unsigned int mask = 0;
unsigned int clk_src;
- unsigned int ret;
+ int ret;
if (dir != SND_SOC_CLOCK_IN)
return -EINVAL;
@@ -846,10 +844,10 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec,
return 0;
}
-static int adau1977_codec_probe(struct snd_soc_codec *codec)
+static int adau1977_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
int ret;
switch (adau1977->type) {
@@ -867,32 +865,30 @@ static int adau1977_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver adau1977_codec_driver = {
- .probe = adau1977_codec_probe,
- .set_bias_level = adau1977_set_bias_level,
- .set_sysclk = adau1977_set_sysclk,
- .idle_bias_off = true,
-
- .controls = adau1977_snd_controls,
- .num_controls = ARRAY_SIZE(adau1977_snd_controls),
- .dapm_widgets = adau1977_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets),
- .dapm_routes = adau1977_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes),
+static const struct snd_soc_component_driver adau1977_component_driver = {
+ .probe = adau1977_component_probe,
+ .set_bias_level = adau1977_set_bias_level,
+ .set_sysclk = adau1977_set_sysclk,
+ .controls = adau1977_snd_controls,
+ .num_controls = ARRAY_SIZE(adau1977_snd_controls),
+ .dapm_widgets = adau1977_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets),
+ .dapm_routes = adau1977_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int adau1977_setup_micbias(struct adau1977 *adau1977)
{
- struct adau1977_platform_data *pdata = adau1977->dev->platform_data;
unsigned int micbias;
- if (pdata) {
- micbias = pdata->micbias;
- if (micbias > ADAU1977_MICBIAS_9V0)
- return -EINVAL;
-
- } else {
+ if (device_property_read_u32(adau1977->dev, "adi,micbias", &micbias))
micbias = ADAU1977_MICBIAS_8V5;
+
+ if (micbias > ADAU1977_MICBIAS_9V0) {
+ dev_err(adau1977->dev, "Invalid value for 'adi,micbias'\n");
+ return -EINVAL;
}
return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS,
@@ -918,7 +914,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
adau1977->type = type;
adau1977->regmap = regmap;
adau1977->switch_mode = switch_mode;
- adau1977->max_master_fs = 192000;
+ adau1977->max_clock_provider_fs = 192000;
adau1977->constraints.list = adau1977_rates;
adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
@@ -968,7 +964,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1977_codec_driver,
+ return devm_snd_soc_register_component(dev, &adau1977_component_driver,
&adau1977_dai, 1);
err_poweroff:
@@ -996,7 +992,7 @@ const struct regmap_config adau1977_regmap_config = {
.max_register = ADAU1977_REG_DC_HPF_CAL,
.volatile_reg = adau1977_register_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1977_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
};
diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h
index 95e714345a86..80baeb4f39b9 100644
--- a/sound/soc/codecs/adau1977.h
+++ b/sound/soc/codecs/adau1977.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1977_H__
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644
index 000000000000..c9134e1de0b2
--- /dev/null
+++ b/sound/soc/codecs/adau7002.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADAU7002 Stereo PDM-to-I2S/TDM converter driver
+ *
+ * Copyright 2014-2016 Analog Devices
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+struct adau7002_priv {
+ int wakeup_delay;
+};
+
+static int adau7002_aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct adau7002_priv *adau7002 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (adau7002->wakeup_delay)
+ msleep(adau7002->wakeup_delay);
+ break;
+ }
+
+ return 0;
+}
+
+static int adau7002_component_probe(struct snd_soc_component *component)
+{
+ struct adau7002_priv *adau7002;
+
+ adau7002 = devm_kzalloc(component->dev, sizeof(*adau7002),
+ GFP_KERNEL);
+ if (!adau7002)
+ return -ENOMEM;
+
+ device_property_read_u32(component->dev, "wakeup-delay-ms",
+ &adau7002->wakeup_delay);
+
+ snd_soc_component_set_drvdata(component, adau7002);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget adau7002_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT_E("ADAU AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0, adau7002_aif_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_INPUT("PDM_DAT"),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7002_routes[] = {
+ { "ADAU AIF", NULL, "PDM_DAT"},
+ { "Capture", NULL, "PDM_DAT" },
+ { "Capture", NULL, "IOVDD" },
+};
+
+static struct snd_soc_dai_driver adau7002_dai = {
+ .name = "adau7002-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 20,
+ },
+};
+
+static const struct snd_soc_component_driver adau7002_component_driver = {
+ .probe = adau7002_component_probe,
+ .dapm_widgets = adau7002_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
+ .dapm_routes = adau7002_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau7002_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int adau7002_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &adau7002_component_driver,
+ &adau7002_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau7002_dt_ids[] = {
+ { .compatible = "adi,adau7002", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id adau7002_acpi_match[] = {
+ { "ADAU7002", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, adau7002_acpi_match);
+#endif
+
+static struct platform_driver adau7002_driver = {
+ .driver = {
+ .name = "adau7002",
+ .of_match_table = of_match_ptr(adau7002_dt_ids),
+ .acpi_match_table = ACPI_PTR(adau7002_acpi_match),
+ },
+ .probe = adau7002_probe,
+};
+module_platform_driver(adau7002_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c
new file mode 100644
index 000000000000..45a5d2dcc0f2
--- /dev/null
+++ b/sound/soc/codecs/adau7118-hw.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter Standalone Hw
+// driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "adau7118.h"
+
+static int adau7118_probe_hw(struct platform_device *pdev)
+{
+ return adau7118_probe(&pdev->dev, NULL, true);
+}
+
+static const struct of_device_id adau7118_of_match[] = {
+ { .compatible = "adi,adau7118" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adau7118_of_match);
+
+static const struct platform_device_id adau7118_id[] = {
+ { .name = "adau7118" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, adau7118_id);
+
+static struct platform_driver adau7118_driver_hw = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe = adau7118_probe_hw,
+ .id_table = adau7118_id,
+};
+module_platform_driver(adau7118_driver_hw);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver for standalone hw mode");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c
new file mode 100644
index 000000000000..f9dc8f4ef9a4
--- /dev/null
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "adau7118.h"
+
+static const struct reg_default adau7118_reg_defaults[] = {
+ { ADAU7118_REG_VENDOR_ID, 0x41 },
+ { ADAU7118_REG_DEVICE_ID1, 0x71 },
+ { ADAU7118_REG_DEVICE_ID2, 0x18 },
+ { ADAU7118_REG_REVISION_ID, 0x00 },
+ { ADAU7118_REG_ENABLES, 0x3F },
+ { ADAU7118_REG_DEC_RATIO_CLK_MAP, 0xC0 },
+ { ADAU7118_REG_HPF_CONTROL, 0xD0 },
+ { ADAU7118_REG_SPT_CTRL1, 0x41 },
+ { ADAU7118_REG_SPT_CTRL2, 0x00 },
+ { ADAU7118_REG_SPT_CX(0), 0x01 },
+ { ADAU7118_REG_SPT_CX(1), 0x11 },
+ { ADAU7118_REG_SPT_CX(2), 0x21 },
+ { ADAU7118_REG_SPT_CX(3), 0x31 },
+ { ADAU7118_REG_SPT_CX(4), 0x41 },
+ { ADAU7118_REG_SPT_CX(5), 0x51 },
+ { ADAU7118_REG_SPT_CX(6), 0x61 },
+ { ADAU7118_REG_SPT_CX(7), 0x71 },
+ { ADAU7118_REG_DRIVE_STRENGTH, 0x2a },
+ { ADAU7118_REG_RESET, 0x00 },
+};
+
+static bool adau7118_volatile(struct device *dev, unsigned int reg)
+{
+ return (reg == ADAU7118_REG_RESET);
+}
+
+
+static const struct regmap_config adau7118_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_defaults = adau7118_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .max_register = ADAU7118_REG_RESET,
+ .volatile_reg = adau7118_volatile,
+};
+
+static int adau7118_probe_i2c(struct i2c_client *i2c)
+{
+ struct regmap *map;
+
+ map = devm_regmap_init_i2c(i2c, &adau7118_regmap_config);
+ if (IS_ERR(map)) {
+ dev_err(&i2c->dev, "Failed to init regmap %ld\n", PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ return adau7118_probe(&i2c->dev, map, false);
+}
+
+static const struct of_device_id adau7118_of_match[] = {
+ { .compatible = "adi,adau7118" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adau7118_of_match);
+
+static const struct i2c_device_id adau7118_id[] = {
+ {"adau7118"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adau7118_id);
+
+static struct i2c_driver adau7118_driver = {
+ .driver = {
+ .name = "adau7118",
+ .of_match_table = adau7118_of_match,
+ },
+ .probe = adau7118_probe_i2c,
+ .id_table = adau7118_id,
+};
+module_i2c_driver(adau7118_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c
new file mode 100644
index 000000000000..5ddea63a6612
--- /dev/null
+++ b/sound/soc/codecs/adau7118.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "adau7118.h"
+
+#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0)
+#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
+#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4)
+#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4)
+#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
+#define ADAU7118_TRISTATE_MASK BIT(6)
+#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
+#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1)
+#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
+#define ADAU7118_SAI_MODE_MASK BIT(0)
+#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
+#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0)
+#define ADAU7118_LRCLK_BCLK_POL(x) \
+ FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
+#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4)
+#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
+#define ADAU7118_FULL_SOFT_R_MASK BIT(1)
+#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
+
+struct adau7118_data {
+ struct regmap *map;
+ struct device *dev;
+ struct regulator *iovdd;
+ struct regulator *dvdd;
+ u32 slot_width;
+ u32 slots;
+ bool hw_mode;
+ bool right_j;
+};
+
+/* Input Enable */
+static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
+ SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
+ /* Input Enable Switches */
+ SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[0]),
+ SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[1]),
+ SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[2]),
+ SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
+ &adau7118_dapm_pdm_control[3]),
+
+ /* PDM Clocks */
+ SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
+
+ /* Output channels */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
+ 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
+ { "PDM0", "Capture Switch", "PDM_DAT0" },
+ { "PDM1", "Capture Switch", "PDM_DAT1" },
+ { "PDM2", "Capture Switch", "PDM_DAT2" },
+ { "PDM3", "Capture Switch", "PDM_DAT3" },
+ { "AIF1TX1", NULL, "PDM0" },
+ { "AIF1TX2", NULL, "PDM0" },
+ { "AIF1TX3", NULL, "PDM1" },
+ { "AIF1TX4", NULL, "PDM1" },
+ { "AIF1TX5", NULL, "PDM2" },
+ { "AIF1TX6", NULL, "PDM2" },
+ { "AIF1TX7", NULL, "PDM3" },
+ { "AIF1TX8", NULL, "PDM3" },
+ { "Capture", NULL, "PDM_CLK0" },
+ { "Capture", NULL, "PDM_CLK1" },
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
+ { "AIF1TX", NULL, "PDM_DAT0" },
+ { "AIF1TX", NULL, "PDM_DAT1" },
+ { "AIF1TX", NULL, "PDM_DAT2" },
+ { "AIF1TX", NULL, "PDM_DAT3" },
+};
+
+static const struct snd_soc_dapm_widget adau7118_widgets[] = {
+ SND_SOC_DAPM_INPUT("PDM_DAT0"),
+ SND_SOC_DAPM_INPUT("PDM_DAT1"),
+ SND_SOC_DAPM_INPUT("PDM_DAT2"),
+ SND_SOC_DAPM_INPUT("PDM_DAT3"),
+};
+
+static int adau7118_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int chan, ret;
+
+ dev_dbg(st->dev, "Set channel map, %d", tx_num);
+
+ for (chan = 0; chan < tx_num; chan++) {
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CX(chan),
+ ADAU7118_SPT_SLOT_MASK,
+ ADAU7118_SPT_SLOT(tx_slot[chan]));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret = 0;
+ u32 regval;
+
+ dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(0));
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ st->right_j = true;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ break;
+ default:
+ dev_err(st->dev, "Invalid format %d",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ regval = ADAU7118_LRCLK_BCLK_POL(0);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval = ADAU7118_LRCLK_BCLK_POL(2);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval = ADAU7118_LRCLK_BCLK_POL(1);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval = ADAU7118_LRCLK_BCLK_POL(3);
+ break;
+ default:
+ dev_err(st->dev, "Invalid Inv mask %d",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL2,
+ ADAU7118_LRCLK_BCLK_POL_MASK,
+ regval);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ dev_dbg(st->dev, "Set tristate, %d\n", tristate);
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_TRISTATE_MASK,
+ ADAU7118_TRISTATE(tristate));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret = 0;
+ u32 regval;
+
+ dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
+
+ switch (slot_width) {
+ case 32:
+ regval = ADAU7118_SLOT_WIDTH(0);
+ break;
+ case 24:
+ regval = ADAU7118_SLOT_WIDTH(2);
+ break;
+ case 16:
+ regval = ADAU7118_SLOT_WIDTH(1);
+ break;
+ default:
+ dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_SLOT_WIDTH_MASK, regval);
+ if (ret < 0)
+ return ret;
+
+ st->slot_width = slot_width;
+ st->slots = slots;
+
+ return 0;
+}
+
+static int adau7118_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct adau7118_data *st =
+ snd_soc_component_get_drvdata(dai->component);
+ u32 data_width = params_width(params), slots_width;
+ int ret;
+ u32 regval;
+
+ if (!st->slots) {
+ /* set stereo mode */
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_SAI_MODE_MASK,
+ ADAU7118_SAI_MODE(0));
+ if (ret < 0)
+ return ret;
+
+ slots_width = 32;
+ } else {
+ slots_width = st->slot_width;
+ }
+
+ if (data_width > slots_width) {
+ dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
+ data_width, slots_width);
+ return -EINVAL;
+ }
+
+ if (st->right_j) {
+ switch (slots_width - data_width) {
+ case 8:
+ /* delay bclck by 8 */
+ regval = ADAU7118_DATA_FMT(2);
+ break;
+ case 12:
+ /* delay bclck by 12 */
+ regval = ADAU7118_DATA_FMT(3);
+ break;
+ case 16:
+ /* delay bclck by 16 */
+ regval = ADAU7118_DATA_FMT(4);
+ break;
+ default:
+ dev_err(st->dev,
+ "Cannot set right_j setting, slot_w:%d, data_w:%d\n",
+ slots_width, data_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ regval);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adau7118_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adau7118_data *st = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret = 0;
+
+ dev_dbg(st->dev, "Set bias level %d\n", level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ /* power on */
+ ret = regulator_enable(st->iovdd);
+ if (ret)
+ return ret;
+
+ /* there's no timing constraints before enabling dvdd */
+ ret = regulator_enable(st->dvdd);
+ if (ret) {
+ regulator_disable(st->iovdd);
+ return ret;
+ }
+
+ if (st->hw_mode)
+ return 0;
+
+ regcache_cache_only(st->map, false);
+ /* sync cache */
+ ret = snd_soc_component_cache_sync(component);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* power off */
+ ret = regulator_disable(st->dvdd);
+ if (ret)
+ return ret;
+
+ ret = regulator_disable(st->iovdd);
+ if (ret)
+ return ret;
+
+ if (st->hw_mode)
+ return 0;
+
+ /* cache only */
+ regcache_mark_dirty(st->map);
+ regcache_cache_only(st->map, true);
+
+ break;
+ }
+
+ return ret;
+}
+
+static int adau7118_component_probe(struct snd_soc_component *component)
+{
+ struct adau7118_data *st = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret = 0;
+
+ if (st->hw_mode) {
+ ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
+ ARRAY_SIZE(adau7118_widgets_hw));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
+ ARRAY_SIZE(adau7118_routes_hw));
+ } else {
+ snd_soc_component_init_regmap(component, st->map);
+ ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
+ ARRAY_SIZE(adau7118_widgets_sw));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
+ ARRAY_SIZE(adau7118_routes_sw));
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops adau7118_ops = {
+ .hw_params = adau7118_hw_params,
+ .set_channel_map = adau7118_set_channel_map,
+ .set_fmt = adau7118_set_fmt,
+ .set_tdm_slot = adau7118_set_tdm_slot,
+ .set_tristate = adau7118_set_tristate,
+};
+
+static struct snd_soc_dai_driver adau7118_dai = {
+ .name = "adau7118-hifi-capture",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 4000,
+ .rate_max = 192000,
+ .sig_bits = 24,
+ },
+};
+
+static const struct snd_soc_component_driver adau7118_component_driver = {
+ .probe = adau7118_component_probe,
+ .set_bias_level = adau7118_set_bias_level,
+ .dapm_widgets = adau7118_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int adau7118_regulator_setup(struct adau7118_data *st)
+{
+ st->iovdd = devm_regulator_get(st->dev, "iovdd");
+ if (IS_ERR(st->iovdd)) {
+ dev_err(st->dev, "Could not get iovdd: %ld\n",
+ PTR_ERR(st->iovdd));
+ return PTR_ERR(st->iovdd);
+ }
+
+ st->dvdd = devm_regulator_get(st->dev, "dvdd");
+ if (IS_ERR(st->dvdd)) {
+ dev_err(st->dev, "Could not get dvdd: %ld\n",
+ PTR_ERR(st->dvdd));
+ return PTR_ERR(st->dvdd);
+ }
+ /* just assume the device is in reset */
+ if (!st->hw_mode) {
+ regcache_mark_dirty(st->map);
+ regcache_cache_only(st->map, true);
+ }
+
+ return 0;
+}
+
+static int adau7118_parset_dt(const struct adau7118_data *st)
+{
+ int ret;
+ u32 dec_ratio = 0;
+ /* 4 inputs */
+ u32 clk_map[4], regval;
+
+ if (st->hw_mode)
+ return 0;
+
+ ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
+ &dec_ratio);
+ if (!ret) {
+ switch (dec_ratio) {
+ case 64:
+ regval = ADAU7118_DEC_RATIO(0);
+ break;
+ case 32:
+ regval = ADAU7118_DEC_RATIO(1);
+ break;
+ case 16:
+ regval = ADAU7118_DEC_RATIO(2);
+ break;
+ default:
+ dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(st->map,
+ ADAU7118_REG_DEC_RATIO_CLK_MAP,
+ ADAU7118_DEC_RATIO_MASK, regval);
+ if (ret)
+ return ret;
+ }
+
+ ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
+ clk_map, ARRAY_SIZE(clk_map));
+ if (!ret) {
+ int pdm;
+ u32 _clk_map = 0;
+
+ for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
+ _clk_map |= (clk_map[pdm] << (pdm + 4));
+
+ ret = regmap_update_bits(st->map,
+ ADAU7118_REG_DEC_RATIO_CLK_MAP,
+ ADAU7118_CLK_MAP_MASK, _clk_map);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
+{
+ struct adau7118_data *st;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->dev = dev;
+ st->hw_mode = hw_mode;
+ dev_set_drvdata(dev, st);
+
+ if (!hw_mode) {
+ st->map = map;
+ adau7118_dai.ops = &adau7118_ops;
+ /*
+ * Perform a full soft reset. This will set all register's
+ * with their reset values.
+ */
+ ret = regmap_update_bits(map, ADAU7118_REG_RESET,
+ ADAU7118_FULL_SOFT_R_MASK,
+ ADAU7118_FULL_SOFT_R(1));
+ if (ret)
+ return ret;
+ }
+
+ ret = adau7118_parset_dt(st);
+ if (ret)
+ return ret;
+
+ ret = adau7118_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev,
+ &adau7118_component_driver,
+ &adau7118_dai, 1);
+}
+EXPORT_SYMBOL_GPL(adau7118_probe);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau7118.h b/sound/soc/codecs/adau7118.h
new file mode 100644
index 000000000000..c65679a4dff1
--- /dev/null
+++ b/sound/soc/codecs/adau7118.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_ADAU7118_H
+#define _LINUX_ADAU7118_H
+
+struct regmap;
+struct device;
+
+/* register map */
+#define ADAU7118_REG_VENDOR_ID 0x00
+#define ADAU7118_REG_DEVICE_ID1 0x01
+#define ADAU7118_REG_DEVICE_ID2 0x02
+#define ADAU7118_REG_REVISION_ID 0x03
+#define ADAU7118_REG_ENABLES 0x04
+#define ADAU7118_REG_DEC_RATIO_CLK_MAP 0x05
+#define ADAU7118_REG_HPF_CONTROL 0x06
+#define ADAU7118_REG_SPT_CTRL1 0x07
+#define ADAU7118_REG_SPT_CTRL2 0x08
+#define ADAU7118_REG_SPT_CX(num) (0x09 + (num))
+#define ADAU7118_REG_DRIVE_STRENGTH 0x11
+#define ADAU7118_REG_RESET 0x12
+
+int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode);
+
+#endif
diff --git a/sound/soc/codecs/adav801.c b/sound/soc/codecs/adav801.c
index 055f1228c2b4..f734c71211da 100644
--- a/sound/soc/codecs/adav801.c
+++ b/sound/soc/codecs/adav801.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAV801 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -30,18 +29,11 @@ static int adav80x_spi_probe(struct spi_device *spi)
return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
-static int adav80x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver adav80x_spi_driver = {
.driver = {
.name = "adav801",
},
.probe = adav80x_spi_probe,
- .remove = adav80x_spi_remove,
.id_table = adav80x_spi_id,
};
module_spi_driver(adav80x_spi_driver);
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
index 52881faedcf6..8b96c41f0354 100644
--- a/sound/soc/codecs/adav803.c
+++ b/sound/soc/codecs/adav803.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAV803 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -15,30 +14,22 @@
#include "adav80x.h"
static const struct i2c_device_id adav803_id[] = {
- { "adav803", 0 },
+ { "adav803" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adav803_id);
-static int adav803_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adav803_probe(struct i2c_client *client)
{
return adav80x_bus_probe(&client->dev,
devm_regmap_init_i2c(client, &adav80x_regmap_config));
}
-static int adav803_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
},
.probe = adav803_probe,
- .remove = adav803_remove,
.id_table = adav803_id,
};
module_i2c_driver(adav803_driver);
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index acff8d62059c..8a89187f9c78 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADAV80X Audio Codec driver supporting ADAV801, ADAV803
*
* Copyright 2011 Analog Devices Inc.
* Author: Yi Li <yi.li@analog.com>
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -212,8 +211,8 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
const char *clk;
switch (adav80x->clk_src) {
@@ -230,14 +229,14 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
return 0;
}
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
}
@@ -281,9 +280,9 @@ static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
{ "AIFAUXIN", NULL, "SYSCLK" },
};
-static int adav80x_set_deemph(struct snd_soc_codec *codec)
+static int adav80x_set_deemph(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adav80x->deemph) {
@@ -315,8 +314,8 @@ static int adav80x_set_deemph(struct snd_soc_codec *codec)
static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
@@ -324,14 +323,14 @@ static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
adav80x->deemph = deemph;
- return adav80x_set_deemph(codec);
+ return adav80x_set_deemph(component);
}
static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = adav80x->deemph;
return 0;
@@ -365,16 +364,17 @@ static unsigned int adav80x_port_ctrl_regs[2][2] = {
static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int capture = 0x00;
unsigned int playback = 0x00;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
capture |= ADAV80X_CAPTURE_MODE_MASTER;
playback |= ADAV80X_PLAYBACK_MODE_MASTER;
- case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -415,10 +415,10 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
+static int adav80x_set_adc_clock(struct snd_soc_component *component,
unsigned int sample_rate)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (sample_rate <= 48000)
@@ -432,10 +432,10 @@ static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
+static int adav80x_set_dac_clock(struct snd_soc_component *component,
unsigned int sample_rate)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (sample_rate <= 48000)
@@ -450,10 +450,10 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
+static int adav80x_set_capture_pcm_format(struct snd_soc_component *component,
struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
switch (params_width(params)) {
@@ -479,10 +479,10 @@ static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
+static int adav80x_set_playback_pcm_format(struct snd_soc_component *component,
struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
@@ -514,32 +514,32 @@ static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adav80x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
if (rate * 256 != adav80x->sysclk)
return -EINVAL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- adav80x_set_playback_pcm_format(codec, dai, params);
- adav80x_set_dac_clock(codec, rate);
+ adav80x_set_playback_pcm_format(component, dai, params);
+ adav80x_set_dac_clock(component, rate);
} else {
- adav80x_set_capture_pcm_format(codec, dai, params);
- adav80x_set_adc_clock(codec, rate);
+ adav80x_set_capture_pcm_format(component, dai, params);
+ adav80x_set_adc_clock(component, rate);
}
adav80x->rate = rate;
- adav80x_set_deemph(codec);
+ adav80x_set_deemph(component);
return 0;
}
-static int adav80x_set_sysclk(struct snd_soc_codec *codec,
+static int adav80x_set_sysclk(struct snd_soc_component *component,
int clk_id, int source,
unsigned int freq, int dir)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
if (dir == SND_SOC_CLOCK_IN) {
switch (clk_id) {
@@ -619,11 +619,11 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
+static int adav80x_set_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int pll_ctrl1 = 0;
unsigned int pll_ctrl2 = 0;
unsigned int pll_src;
@@ -648,6 +648,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
break;
}
+ fallthrough;
default:
return -EINVAL;
}
@@ -694,10 +695,10 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
return 0;
}
-static int adav80x_set_bias_level(struct snd_soc_codec *codec,
+static int adav80x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int mask = ADAV80X_DAC_CTRL1_PD;
switch (level) {
@@ -722,10 +723,10 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec,
static int adav80x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
+ if (!snd_soc_component_active(component) || !adav80x->rate)
return 0;
return snd_pcm_hw_constraint_single(substream->runtime,
@@ -735,10 +736,10 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- if (!snd_soc_codec_is_active(codec))
+ if (!snd_soc_component_active(component))
adav80x->rate = 0;
}
@@ -799,10 +800,10 @@ static struct snd_soc_dai_driver adav80x_dais[] = {
},
};
-static int adav80x_probe(struct snd_soc_codec *codec)
+static int adav80x_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
/* Force PLLs on for SYSCLK output */
snd_soc_dapm_force_enable_pin(dapm, "PLL1");
@@ -816,30 +817,31 @@ static int adav80x_probe(struct snd_soc_codec *codec)
return 0;
}
-static int adav80x_resume(struct snd_soc_codec *codec)
+static int adav80x_resume(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
regcache_sync(adav80x->regmap);
return 0;
}
-static struct snd_soc_codec_driver adav80x_codec_driver = {
- .probe = adav80x_probe,
- .resume = adav80x_resume,
- .set_bias_level = adav80x_set_bias_level,
- .suspend_bias_off = true,
-
- .set_pll = adav80x_set_pll,
- .set_sysclk = adav80x_set_sysclk,
-
- .controls = adav80x_controls,
- .num_controls = ARRAY_SIZE(adav80x_controls),
- .dapm_widgets = adav80x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
- .dapm_routes = adav80x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
+static const struct snd_soc_component_driver adav80x_component_driver = {
+ .probe = adav80x_probe,
+ .resume = adav80x_resume,
+ .set_bias_level = adav80x_set_bias_level,
+ .set_pll = adav80x_set_pll,
+ .set_sysclk = adav80x_set_sysclk,
+ .controls = adav80x_controls,
+ .num_controls = ARRAY_SIZE(adav80x_controls),
+ .dapm_widgets = adav80x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
+ .dapm_routes = adav80x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
@@ -856,7 +858,7 @@ int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, adav80x);
adav80x->regmap = regmap;
- return snd_soc_register_codec(dev, &adav80x_codec_driver,
+ return devm_snd_soc_register_component(dev, &adav80x_component_driver,
adav80x_dais, ARRAY_SIZE(adav80x_dais));
}
EXPORT_SYMBOL_GPL(adav80x_bus_probe);
@@ -868,7 +870,7 @@ const struct regmap_config adav80x_regmap_config = {
.max_register = ADAV80X_PLL_OUTE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adav80x_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
};
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
index 8a1d7c09dca5..fb50ff57e317 100644
--- a/sound/soc/codecs/adav80x.h
+++ b/sound/soc/codecs/adav80x.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* header file for ADAV80X parts
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADAV80X_H
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index 1222282e93c3..44aa06e03486 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ads117x.c -- Driver for ads1174/8 ADC chips
*
* Copyright 2009 ShotSpotter Inc.
* Author: Graeme Gregory <gg@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/kernel.h>
@@ -20,6 +16,8 @@
#include <sound/initval.h>
#include <sound/soc.h>
+#include <linux/of.h>
+
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
@@ -56,32 +54,38 @@ static struct snd_soc_dai_driver ads117x_dai = {
.formats = ADS117X_FORMATS,},
};
-static struct snd_soc_codec_driver soc_codec_dev_ads117x = {
- .dapm_widgets = ads117x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
- .dapm_routes = ads117x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_ads117x = {
+ .dapm_widgets = ads117x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
+ .dapm_routes = ads117x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ads117x_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ads117x, &ads117x_dai, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ads117x, &ads117x_dai, 1);
}
-static int ads117x_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
+#if defined(CONFIG_OF)
+static const struct of_device_id ads117x_dt_ids[] = {
+ { .compatible = "ti,ads1174" },
+ { .compatible = "ti,ads1178" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads117x_dt_ids);
+#endif
static struct platform_driver ads117x_codec_driver = {
.driver = {
.name = "ads117x-codec",
+ .of_match_table = of_match_ptr(ads117x_dt_ids),
},
.probe = ads117x_probe,
- .remove = ads117x_remove,
};
module_platform_driver(ads117x_codec_driver);
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 595d02d7602c..a33cb329865c 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AK4104 ALSA SoC (ASoC) driver
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <sound/asoundef.h>
#include <sound/core.h>
@@ -65,8 +61,8 @@ static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int val = 0;
int ret;
@@ -81,12 +77,12 @@ static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
break;
default:
- dev_err(codec->dev, "invalid dai format\n");
+ dev_err(component->dev, "invalid dai format\n");
return -EINVAL;
}
- /* This device can only be slave */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ /* This device can only be consumer */
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
@@ -102,8 +98,8 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int ret, val = 0;
/* set the IEC958 bits: consumer mode, no copyright bit */
@@ -141,7 +137,7 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
val |= IEC958_AES3_CON_FS_192000;
break;
default:
- dev_err(codec->dev, "unsupported sampling rate\n");
+ dev_err(component->dev, "unsupported sampling rate\n");
return -EINVAL;
}
@@ -163,7 +159,10 @@ static struct snd_soc_dai_driver ak4104_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE
@@ -171,14 +170,14 @@ static struct snd_soc_dai_driver ak4104_dai = {
.ops = &ak4101_dai_ops,
};
-static int ak4104_probe(struct snd_soc_codec *codec)
+static int ak4104_probe(struct snd_soc_component *component)
{
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_enable(ak4104->regulator);
if (ret < 0) {
- dev_err(codec->dev, "Unable to enable regulator: %d\n", ret);
+ dev_err(component->dev, "Unable to enable regulator: %d\n", ret);
return ret;
}
@@ -202,30 +201,28 @@ exit_disable_regulator:
return ret;
}
-static int ak4104_remove(struct snd_soc_codec *codec)
+static void ak4104_remove(struct snd_soc_component *component)
{
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0);
regulator_disable(ak4104->regulator);
-
- return 0;
}
#ifdef CONFIG_PM
-static int ak4104_soc_suspend(struct snd_soc_codec *codec)
+static int ak4104_soc_suspend(struct snd_soc_component *component)
{
- struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
regulator_disable(priv->regulator);
return 0;
}
-static int ak4104_soc_resume(struct snd_soc_codec *codec)
+static int ak4104_soc_resume(struct snd_soc_component *component)
{
- struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_enable(priv->regulator);
@@ -239,16 +236,18 @@ static int ak4104_soc_resume(struct snd_soc_codec *codec)
#define ak4104_soc_resume NULL
#endif /* CONFIG_PM */
-static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
- .probe = ak4104_probe,
- .remove = ak4104_remove,
- .suspend = ak4104_soc_suspend,
- .resume = ak4104_soc_resume,
-
- .dapm_widgets = ak4104_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
- .dapm_routes = ak4104_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
+static const struct snd_soc_component_driver soc_component_device_ak4104 = {
+ .probe = ak4104_probe,
+ .remove = ak4104_remove,
+ .suspend = ak4104_soc_suspend,
+ .resume = ak4104_soc_resume,
+ .dapm_widgets = ak4104_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
+ .dapm_routes = ak4104_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ak4104_regmap = {
@@ -264,8 +263,8 @@ static const struct regmap_config ak4104_regmap = {
static int ak4104_spi_probe(struct spi_device *spi)
{
- struct device_node *np = spi->dev.of_node;
struct ak4104_private *ak4104;
+ struct gpio_desc *reset_gpiod;
unsigned int val;
int ret;
@@ -293,19 +292,10 @@ static int ak4104_spi_probe(struct spi_device *spi)
return ret;
}
- if (np) {
- enum of_gpio_flags flags;
- int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
-
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(&spi->dev, gpio,
- flags & OF_GPIO_ACTIVE_LOW ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- "ak4104 reset");
- if (ret < 0)
- return ret;
- }
- }
+ reset_gpiod = devm_gpiod_get_optional(&spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (PTR_ERR(reset_gpiod) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
/* read the 'reserved' register - according to the datasheet, it
* should contain 0x5b. Not a good way to verify the presence of
@@ -318,17 +308,11 @@ static int ak4104_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ak4104);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_device_ak4104, &ak4104_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_device_ak4104, &ak4104_dai, 1);
return ret;
}
-static int ak4104_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct of_device_id ak4104_of_match[] = {
{ .compatible = "asahi-kasei,ak4104", },
{ }
@@ -348,7 +332,6 @@ static struct spi_driver ak4104_spi_driver = {
},
.id_table = ak4104_id_table,
.probe = ak4104_spi_probe,
- .remove = ak4104_spi_remove,
};
module_spi_driver(ak4104_spi_driver);
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
new file mode 100644
index 000000000000..23e868e4e3fb
--- /dev/null
+++ b/sound/soc/codecs/ak4118.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ak4118.c -- Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright 2018 DEVIALET
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#define AK4118_REG_CLK_PWR_CTL 0x00
+#define AK4118_REG_FORMAT_CTL 0x01
+#define AK4118_REG_IO_CTL0 0x02
+#define AK4118_REG_IO_CTL1 0x03
+#define AK4118_REG_INT0_MASK 0x04
+#define AK4118_REG_INT1_MASK 0x05
+#define AK4118_REG_RCV_STATUS0 0x06
+#define AK4118_REG_RCV_STATUS1 0x07
+#define AK4118_REG_RXCHAN_STATUS0 0x08
+#define AK4118_REG_RXCHAN_STATUS1 0x09
+#define AK4118_REG_RXCHAN_STATUS2 0x0a
+#define AK4118_REG_RXCHAN_STATUS3 0x0b
+#define AK4118_REG_RXCHAN_STATUS4 0x0c
+#define AK4118_REG_TXCHAN_STATUS0 0x0d
+#define AK4118_REG_TXCHAN_STATUS1 0x0e
+#define AK4118_REG_TXCHAN_STATUS2 0x0f
+#define AK4118_REG_TXCHAN_STATUS3 0x10
+#define AK4118_REG_TXCHAN_STATUS4 0x11
+#define AK4118_REG_BURST_PREAMB_PC0 0x12
+#define AK4118_REG_BURST_PREAMB_PC1 0x13
+#define AK4118_REG_BURST_PREAMB_PD0 0x14
+#define AK4118_REG_BURST_PREAMB_PD1 0x15
+#define AK4118_REG_QSUB_CTL 0x16
+#define AK4118_REG_QSUB_TRACK 0x17
+#define AK4118_REG_QSUB_INDEX 0x18
+#define AK4118_REG_QSUB_MIN 0x19
+#define AK4118_REG_QSUB_SEC 0x1a
+#define AK4118_REG_QSUB_FRAME 0x1b
+#define AK4118_REG_QSUB_ZERO 0x1c
+#define AK4118_REG_QSUB_ABS_MIN 0x1d
+#define AK4118_REG_QSUB_ABS_SEC 0x1e
+#define AK4118_REG_QSUB_ABS_FRAME 0x1f
+#define AK4118_REG_GPE 0x20
+#define AK4118_REG_GPDR 0x21
+#define AK4118_REG_GPSCR 0x22
+#define AK4118_REG_GPLR 0x23
+#define AK4118_REG_DAT_MASK_DTS 0x24
+#define AK4118_REG_RX_DETECT 0x25
+#define AK4118_REG_STC_DAT_DETECT 0x26
+#define AK4118_REG_RXCHAN_STATUS5 0x27
+#define AK4118_REG_TXCHAN_STATUS5 0x28
+#define AK4118_REG_MAX 0x29
+
+#define AK4118_REG_FORMAT_CTL_DIF0 (1 << 4)
+#define AK4118_REG_FORMAT_CTL_DIF1 (1 << 5)
+#define AK4118_REG_FORMAT_CTL_DIF2 (1 << 6)
+
+struct ak4118_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset;
+ struct gpio_desc *irq;
+ struct snd_soc_component *component;
+};
+
+static const struct reg_default ak4118_reg_defaults[] = {
+ {AK4118_REG_CLK_PWR_CTL, 0x43},
+ {AK4118_REG_FORMAT_CTL, 0x6a},
+ {AK4118_REG_IO_CTL0, 0x88},
+ {AK4118_REG_IO_CTL1, 0x48},
+ {AK4118_REG_INT0_MASK, 0xee},
+ {AK4118_REG_INT1_MASK, 0xb5},
+ {AK4118_REG_RCV_STATUS0, 0x00},
+ {AK4118_REG_RCV_STATUS1, 0x10},
+ {AK4118_REG_TXCHAN_STATUS0, 0x00},
+ {AK4118_REG_TXCHAN_STATUS1, 0x00},
+ {AK4118_REG_TXCHAN_STATUS2, 0x00},
+ {AK4118_REG_TXCHAN_STATUS3, 0x00},
+ {AK4118_REG_TXCHAN_STATUS4, 0x00},
+ {AK4118_REG_GPE, 0x77},
+ {AK4118_REG_GPDR, 0x00},
+ {AK4118_REG_GPSCR, 0x00},
+ {AK4118_REG_GPLR, 0x00},
+ {AK4118_REG_DAT_MASK_DTS, 0x3f},
+ {AK4118_REG_RX_DETECT, 0x00},
+ {AK4118_REG_STC_DAT_DETECT, 0x00},
+ {AK4118_REG_TXCHAN_STATUS5, 0x00},
+};
+
+static const char * const ak4118_input_select_txt[] = {
+ "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
+};
+static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0,
+ ak4118_input_select_txt);
+
+static const struct snd_kcontrol_new ak4118_input_mux_controls =
+ SOC_DAPM_ENUM("Input Select", ak4118_insel_enum);
+
+static const char * const ak4118_iec958_fs_txt[] = {
+ "44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200",
+ "8000", "96000", "64000", "176400", "192000",
+};
+
+static const int ak4118_iec958_fs_val[] = {
+ 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1,
+ 0x4, 0x4, ak4118_iec958_fs_txt,
+ ak4118_iec958_fs_val);
+
+static struct snd_kcontrol_new ak4118_iec958_controls[] = {
+ SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0),
+ SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0),
+ SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1),
+ SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0),
+ SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum),
+};
+
+static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INRX0"),
+ SND_SOC_DAPM_INPUT("INRX1"),
+ SND_SOC_DAPM_INPUT("INRX2"),
+ SND_SOC_DAPM_INPUT("INRX3"),
+ SND_SOC_DAPM_INPUT("INRX4"),
+ SND_SOC_DAPM_INPUT("INRX5"),
+ SND_SOC_DAPM_INPUT("INRX6"),
+ SND_SOC_DAPM_INPUT("INRX7"),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4118_input_mux_controls),
+};
+
+static const struct snd_soc_dapm_route ak4118_dapm_routes[] = {
+ {"Input Mux", "RX0", "INRX0"},
+ {"Input Mux", "RX1", "INRX1"},
+ {"Input Mux", "RX2", "INRX2"},
+ {"Input Mux", "RX3", "INRX3"},
+ {"Input Mux", "RX4", "INRX4"},
+ {"Input Mux", "RX5", "INRX5"},
+ {"Input Mux", "RX6", "INRX6"},
+ {"Input Mux", "RX7", "INRX7"},
+};
+
+
+static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118,
+ unsigned int format)
+{
+ int dif;
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dif = AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return dif;
+}
+
+static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118,
+ unsigned int format)
+{
+ int dif;
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 |
+ AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return dif;
+}
+
+static int ak4118_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
+ int dif;
+ int ret = 0;
+
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dif = ak4118_set_dai_fmt_provider(ak4118, format);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dif = ak4118_set_dai_fmt_consumer(ak4118, format);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ goto exit;
+ }
+
+ /* format not supported */
+ if (dif < 0) {
+ ret = dif;
+ goto exit;
+ }
+
+ ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL,
+ AK4118_REG_FORMAT_CTL_DIF0 |
+ AK4118_REG_FORMAT_CTL_DIF1 |
+ AK4118_REG_FORMAT_CTL_DIF2, dif);
+ if (ret < 0)
+ goto exit;
+
+exit:
+ return ret;
+}
+
+static int ak4118_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4118_dai_ops = {
+ .hw_params = ak4118_hw_params,
+ .set_fmt = ak4118_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver ak4118_dai = {
+ .name = "ak4118-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &ak4118_dai_ops,
+};
+
+static irqreturn_t ak4118_irq_handler(int irq, void *data)
+{
+ struct ak4118_priv *ak4118 = data;
+ struct snd_soc_component *component = ak4118->component;
+ struct snd_kcontrol_new *kctl_new;
+ unsigned int i;
+
+ if (!component)
+ return IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) {
+ kctl_new = &ak4118_iec958_controls[i];
+
+ snd_soc_component_notify_control(component, kctl_new->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ak4118_probe(struct snd_soc_component *component)
+{
+ struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ak4118->component = component;
+
+ /* release reset */
+ gpiod_set_value(ak4118->reset, 0);
+
+ /* unmask all int1 sources */
+ ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "failed to write regmap 0x%x 0x%x: %d\n",
+ AK4118_REG_INT1_MASK, 0x00, ret);
+ return ret;
+ }
+
+ /* rx detect enable on all channels */
+ ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "failed to write regmap 0x%x 0x%x: %d\n",
+ AK4118_REG_RX_DETECT, 0xff, ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_component_controls(component, ak4118_iec958_controls,
+ ARRAY_SIZE(ak4118_iec958_controls));
+ if (ret) {
+ dev_err(component->dev,
+ "failed to add component kcontrols: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ak4118_remove(struct snd_soc_component *component)
+{
+ struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
+
+ /* hold reset */
+ gpiod_set_value(ak4118->reset, 1);
+}
+
+static const struct snd_soc_component_driver soc_component_drv_ak4118 = {
+ .probe = ak4118_probe,
+ .remove = ak4118_remove,
+ .dapm_widgets = ak4118_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4118_dapm_widgets),
+ .dapm_routes = ak4118_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4118_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4118_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = ak4118_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults),
+
+ .cache_type = REGCACHE_NONE,
+ .max_register = AK4118_REG_MAX - 1,
+};
+
+static int ak4118_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4118_priv *ak4118;
+ int ret;
+
+ ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv),
+ GFP_KERNEL);
+ if (ak4118 == NULL)
+ return -ENOMEM;
+
+ ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap);
+ if (IS_ERR(ak4118->regmap))
+ return PTR_ERR(ak4118->regmap);
+
+ i2c_set_clientdata(i2c, ak4118);
+
+ ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ak4118->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset),
+ "Failed to get reset\n");
+
+ ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN);
+ if (IS_ERR(ak4118->irq))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq),
+ "Failed to get IRQ\n");
+
+ ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq),
+ NULL, ak4118_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "ak4118-irq", ak4118);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_drv_ak4118, &ak4118_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ak4118_of_match[] = {
+ { .compatible = "asahi-kasei,ak4118", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ak4118_of_match);
+#endif
+
+static const struct i2c_device_id ak4118_id_table[] = {
+ { "ak4118" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ak4118_id_table);
+
+static struct i2c_driver ak4118_i2c_driver = {
+ .driver = {
+ .name = "ak4118",
+ .of_match_table = of_match_ptr(ak4118_of_match),
+ },
+ .id_table = ak4118_id_table,
+ .probe = ak4118_i2c_probe,
+};
+
+module_i2c_driver(ak4118_i2c_driver);
+
+MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver");
+MODULE_AUTHOR("Adrien Charruel <adrien.charruel@devialet.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c
new file mode 100644
index 000000000000..452559d8c97b
--- /dev/null
+++ b/sound/soc/codecs/ak4375.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Based on code by Hu Jin
+ * Copyright (C) 2014 Asahi Kasei Microdevices Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Registers and fields */
+#define AK4375_00_POWER_MANAGEMENT1 0x00
+#define PMPLL BIT(0) /* 0: PLL off, 1: PLL on */
+#define AK4375_01_POWER_MANAGEMENT2 0x01
+#define PMCP1 BIT(0) /* Charge Pump 1: LDO1 and DAC */
+#define PMCP2 BIT(1) /* Charge Pump 2: Class-G HP Amp */
+#define PMLDO1P BIT(4)
+#define PMLDO1N BIT(5)
+#define PMLDO (PMLDO1P | PMLDO1N)
+#define AK4375_02_POWER_MANAGEMENT3 0x02
+#define AK4375_03_POWER_MANAGEMENT4 0x03
+#define AK4375_04_OUTPUT_MODE_SETTING 0x04
+#define AK4375_05_CLOCK_MODE_SELECT 0x05
+#define FS_MASK GENMASK(4, 0)
+#define FS_8KHZ 0x00
+#define FS_11_025KHZ 0x01
+#define FS_16KHZ 0x04
+#define FS_22_05KHZ 0x05
+#define FS_32KHZ 0x08
+#define FS_44_1KHZ 0x09
+#define FS_48KHZ 0x0a
+#define FS_88_2KHZ 0x0d
+#define FS_96KHZ 0x0e
+#define FS_176_4KHZ 0x11
+#define FS_192KHZ 0x12
+#define CM_MASK GENMASK(6, 5) /* For SRC Bypass mode */
+#define CM_0 (0x0 << 5)
+#define CM_1 (0x1 << 5)
+#define CM_2 (0x2 << 5)
+#define CM_3 (0x3 << 5)
+#define AK4375_06_DIGITAL_FILTER_SELECT 0x06
+#define DADFSEL BIT(5) /* 0: in SRC Bypass mode, 1: in SRC mode */
+#define DASL BIT(6)
+#define DASD BIT(7)
+#define AK4375_07_DAC_MONO_MIXING 0x07
+#define DACMUTE_MASK (GENMASK(5, 4) | GENMASK(1, 0)) /* Clear to mute */
+#define AK4375_08_JITTER_CLEANER_SETTING1 0x08
+#define AK4375_09_JITTER_CLEANER_SETTING2 0x09
+#define AK4375_0A_JITTER_CLEANER_SETTING3 0x0a
+#define SELDAIN BIT(1) /* 0: SRC Bypass mode, 1: SRC mode */
+#define XCKSEL BIT(6) /* 0: PLL0, 1: MCKI */
+#define XCKCPSEL BIT(7) /* Should be equal to SELDAIN and XCKSEL */
+#define AK4375_0B_LCH_OUTPUT_VOLUME 0x0b
+#define AK4375_0C_RCH_OUTPUT_VOLUME 0x0c
+#define AK4375_0D_HP_VOLUME_CONTROL 0x0d
+#define AK4375_0E_PLL_CLK_SOURCE_SELECT 0x0e
+#define PLS BIT(0) /* 0: MCKI, 1: BCLK */
+#define AK4375_0F_PLL_REF_CLK_DIVIDER1 0x0f /* Reference clock divider [15:8] bits */
+#define AK4375_10_PLL_REF_CLK_DIVIDER2 0x10 /* Reference clock divider [7:0] bis */
+#define AK4375_11_PLL_FB_CLK_DIVIDER1 0x11 /* Feedback clock divider [15:8] bits */
+#define AK4375_12_PLL_FB_CLK_DIVIDER2 0x12 /* Feedback clock divider [7:0] bits */
+#define AK4375_13_SRC_CLK_SOURCE 0x13 /* SRC Bypass: SRCCKS=XCKSEL=SELDAIN=0 */
+#define SRCCKS BIT(0) /* SRC Clock source 0: MCKI, 1: PLL0 */
+#define DIV BIT(4)
+#define AK4375_14_DAC_CLK_DIVIDER 0x14
+#define AK4375_15_AUDIO_IF_FORMAT 0x15
+#define DEVICEID_MASK GENMASK(7, 5)
+#define AK4375_24_MODE_CONTROL 0x24
+
+#define AK4375_PLL_FREQ_OUT_112896000 112896000 /* 44.1 kHz base rate */
+#define AK4375_PLL_FREQ_OUT_122880000 122880000 /* 32 and 48 kHz base rates */
+
+#define DEVICEID_AK4375 0x00
+#define DEVICEID_AK4375A 0x01
+#define DEVICEID_AK4376A 0x02
+#define DEVICEID_AK4377 0x03
+#define DEVICEID_AK4331 0x07
+
+static const char * const supply_names[] = {
+ "avdd", "tvdd"
+};
+
+struct ak4375_drvdata {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct snd_soc_component_driver *comp_drv;
+};
+
+struct ak4375_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *pdn_gpiod;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ unsigned int rate;
+ unsigned int pld;
+ u8 mute_save;
+};
+
+static const struct reg_default ak4375_reg_defaults[] = {
+ { 0x00, 0x00 }, { 0x01, 0x00 }, { 0x02, 0x00 },
+ { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 },
+ { 0x06, 0x00 }, { 0x07, 0x00 }, { 0x08, 0x00 },
+ { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x0b, 0x19 },
+ { 0x0c, 0x19 }, { 0x0d, 0x75 }, { 0x0e, 0x01 },
+ { 0x0f, 0x00 }, { 0x10, 0x00 }, { 0x11, 0x00 },
+ { 0x12, 0x00 }, { 0x13, 0x00 }, { 0x14, 0x00 },
+ { 0x15, 0x00 }, { 0x24, 0x00 },
+};
+
+/*
+ * Output Digital volume control:
+ * from -12.5 to 3 dB in 0.5 dB steps (mute instead of -12.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -1250, 50, 0);
+
+/*
+ * HP-Amp Analog volume control:
+ * from -4.2 to 6 dB in 2 dB steps (mute instead of -4.2 dB)
+ */
+static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0);
+
+static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" };
+static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" };
+static const char * const ak4375_cpmode_select_texts[] = {
+ "Automatic Switching",
+ "+-VDD Operation",
+ "+-1/2VDD Operation"
+};
+
+/*
+ * DASD, DASL bits Digital Filter Setting
+ * 0, 0 : Sharp Roll-Off Filter
+ * 0, 1 : Slow Roll-Off Filter
+ * 1, 0 : Short delay Sharp Roll-Off Filter
+ * 1, 1 : Short delay Slow Roll-Off Filter
+ */
+static const char * const ak4375_digfil_select_texts[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short delay Sharp Roll-Off Filter",
+ "Short delay Slow Roll-Off Filter",
+};
+
+static const struct soc_enum ak4375_ovolcn_enum =
+ SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7,
+ ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts);
+static const struct soc_enum ak4375_mdacl_enum =
+ SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 2,
+ ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts);
+static const struct soc_enum ak4375_mdacr_enum =
+ SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6,
+ ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts);
+static const struct soc_enum ak4375_cpmode_enum =
+ SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2,
+ ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts);
+static const struct soc_enum ak4375_digfil_enum =
+ SOC_ENUM_SINGLE(AK4375_06_DIGITAL_FILTER_SELECT, 6,
+ ARRAY_SIZE(ak4375_digfil_select_texts), ak4375_digfil_select_texts);
+
+static const struct snd_kcontrol_new ak4375_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Output Volume", AK4375_0B_LCH_OUTPUT_VOLUME,
+ AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, dac_tlv),
+ SOC_SINGLE_TLV("HP-Amp Analog Volume",
+ AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv),
+
+ SOC_DOUBLE("DAC Signal Invert Switch", AK4375_07_DAC_MONO_MIXING, 3, 7, 1, 0),
+
+ SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum),
+ SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum),
+ SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum),
+ SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum),
+ SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum),
+};
+
+static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("LDACL Switch", AK4375_07_DAC_MONO_MIXING, 0, 1, 0),
+ SOC_DAPM_SINGLE("RDACL Switch", AK4375_07_DAC_MONO_MIXING, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4375_hpr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("LDACR Switch", AK4375_07_DAC_MONO_MIXING, 4, 1, 0),
+ SOC_DAPM_SINGLE("RDACR Switch", AK4375_07_DAC_MONO_MIXING, 5, 1, 0),
+};
+
+static int ak4375_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, PMPLL);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, PMCP1);
+ usleep_range(6500, 7000);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, PMLDO);
+ usleep_range(1000, 2000);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, PMCP2);
+ usleep_range(4500, 5000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, 0x0);
+ snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, 0x0);
+ snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, 0x0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ak4375_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC", NULL, AK4375_02_POWER_MANAGEMENT3, 0, 0, ak4375_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_IN("SDTI", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+
+ SND_SOC_DAPM_MIXER("HPR Mixer", AK4375_03_POWER_MANAGEMENT4, 1, 0,
+ &ak4375_hpr_mixer_controls[0], ARRAY_SIZE(ak4375_hpr_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HPL Mixer", AK4375_03_POWER_MANAGEMENT4, 0, 0,
+ &ak4375_hpl_mixer_controls[0], ARRAY_SIZE(ak4375_hpl_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route ak4375_intercon[] = {
+ { "DAC", NULL, "SDTI" },
+
+ { "HPL Mixer", "LDACL Switch", "DAC" },
+ { "HPL Mixer", "RDACL Switch", "DAC" },
+ { "HPR Mixer", "LDACR Switch", "DAC" },
+ { "HPR Mixer", "RDACR Switch", "DAC" },
+
+ { "HPL", NULL, "HPL Mixer" },
+ { "HPR", NULL, "HPR Mixer" },
+};
+
+static int ak4375_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ unsigned int freq_in, freq_out;
+
+ ak4375->rate = params_rate(params);
+
+ if (ak4375->rate <= 96000)
+ ak4375->pld = 0;
+ else
+ ak4375->pld = 1;
+
+ freq_in = 32 * ak4375->rate / (ak4375->pld + 1);
+
+ if ((ak4375->rate % 8000) == 0)
+ freq_out = AK4375_PLL_FREQ_OUT_122880000;
+ else
+ freq_out = AK4375_PLL_FREQ_OUT_112896000;
+
+ return snd_soc_dai_set_pll(dai, 0, 0, freq_in, freq_out);
+}
+
+static int ak4375_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ unsigned int mclk, plm, mdiv, div;
+ u8 cms, fs, cm;
+
+ cms = snd_soc_component_read(component, AK4375_05_CLOCK_MODE_SELECT);
+ fs = cms & ~FS_MASK;
+ cm = cms & ~CM_MASK;
+
+ switch (ak4375->rate) {
+ case 8000:
+ fs |= FS_8KHZ;
+ break;
+ case 11025:
+ fs |= FS_11_025KHZ;
+ break;
+ case 16000:
+ fs |= FS_16KHZ;
+ break;
+ case 22050:
+ fs |= FS_22_05KHZ;
+ break;
+ case 32000:
+ fs |= FS_32KHZ;
+ break;
+ case 44100:
+ fs |= FS_44_1KHZ;
+ break;
+ case 48000:
+ fs |= FS_48KHZ;
+ break;
+ case 88200:
+ fs |= FS_88_2KHZ;
+ break;
+ case 96000:
+ fs |= FS_96KHZ;
+ break;
+ case 176400:
+ fs |= FS_176_4KHZ;
+ break;
+ case 192000:
+ fs |= FS_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ak4375->rate <= 24000) {
+ cm |= CM_1;
+ mclk = 512 * ak4375->rate;
+ mdiv = freq_out / mclk - 1;
+ div = 0;
+ } else if (ak4375->rate <= 96000) {
+ cm |= CM_0;
+ mclk = 256 * ak4375->rate;
+ mdiv = freq_out / mclk - 1;
+ div = 0;
+ } else {
+ cm |= CM_3;
+ mclk = 128 * ak4375->rate;
+ mdiv = 4;
+ div = 1;
+ }
+
+ /* Writing both fields in one go seems to make playback choppy on start */
+ snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, FS_MASK, fs);
+ snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, CM_MASK, cm);
+
+ snd_soc_component_write(component, AK4375_0F_PLL_REF_CLK_DIVIDER1,
+ (ak4375->pld & 0xff00) >> 8);
+ snd_soc_component_write(component, AK4375_10_PLL_REF_CLK_DIVIDER2,
+ ak4375->pld & 0x00ff);
+
+ plm = freq_out / freq_in - 1;
+ snd_soc_component_write(component, AK4375_11_PLL_FB_CLK_DIVIDER1, (plm & 0xff00) >> 8);
+ snd_soc_component_write(component, AK4375_12_PLL_FB_CLK_DIVIDER2, plm & 0x00ff);
+
+ snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, DIV, div);
+
+ /* SRCCKS bit: force to 1 for SRC PLL source clock */
+ snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, SRCCKS, SRCCKS);
+
+ snd_soc_component_write(component, AK4375_14_DAC_CLK_DIVIDER, mdiv);
+
+ dev_dbg(ak4375->dev, "rate=%d mclk=%d f_in=%d f_out=%d PLD=%d PLM=%d MDIV=%d DIV=%d\n",
+ ak4375->rate, mclk, freq_in, freq_out, ak4375->pld, plm, mdiv, div);
+
+ return 0;
+}
+
+static int ak4375_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
+ u8 val = snd_soc_component_read(component, AK4375_07_DAC_MONO_MIXING);
+
+ dev_dbg(ak4375->dev, "mute=%d val=%d\n", mute, val);
+
+ if (mute) {
+ ak4375->mute_save = val & DACMUTE_MASK;
+ val &= ~DACMUTE_MASK;
+ } else {
+ val |= ak4375->mute_save;
+ }
+
+ snd_soc_component_write(component, AK4375_07_DAC_MONO_MIXING, val);
+
+ return 0;
+}
+
+#define AK4375_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define AK4375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops ak4375_dai_ops = {
+ .hw_params = ak4375_hw_params,
+ .mute_stream = ak4375_mute,
+ .set_pll = ak4375_dai_set_pll,
+};
+
+static struct snd_soc_dai_driver ak4375_dai = {
+ .name = "ak4375-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4375_RATES,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .formats = AK4375_FORMATS,
+ },
+ .ops = &ak4375_dai_ops,
+};
+
+static void ak4375_power_off(struct ak4375_priv *ak4375)
+{
+ gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0);
+ usleep_range(1000, 2000);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+}
+
+static int ak4375_power_on(struct ak4375_priv *ak4375)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(3000, 4000);
+
+ gpiod_set_value_cansleep(ak4375->pdn_gpiod, 1);
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static int ak4375_runtime_suspend(struct device *dev)
+{
+ struct ak4375_priv *ak4375 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak4375->regmap, true);
+ ak4375_power_off(ak4375);
+
+ return 0;
+}
+
+static int ak4375_runtime_resume(struct device *dev)
+{
+ struct ak4375_priv *ak4375 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ak4375_power_on(ak4375);
+ if (ret < 0)
+ return ret;
+
+ regcache_cache_only(ak4375->regmap, false);
+ regcache_mark_dirty(ak4375->regmap);
+
+ return regcache_sync(ak4375->regmap);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4375 = {
+ .controls = ak4375_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4375_snd_controls),
+ .dapm_widgets = ak4375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4375_dapm_widgets),
+ .dapm_routes = ak4375_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4375_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4375_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AK4375_24_MODE_CONTROL,
+ .reg_defaults = ak4375_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4375_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct ak4375_drvdata ak4375_drvdata = {
+ .dai_drv = &ak4375_dai,
+ .comp_drv = &soc_codec_dev_ak4375,
+};
+
+static const struct dev_pm_ops ak4375_pm = {
+ RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static int ak4375_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4375_priv *ak4375;
+ const struct ak4375_drvdata *drvdata;
+ unsigned int deviceid;
+ int ret, i;
+
+ ak4375 = devm_kzalloc(&i2c->dev, sizeof(*ak4375), GFP_KERNEL);
+ if (!ak4375)
+ return -ENOMEM;
+
+ ak4375->regmap = devm_regmap_init_i2c(i2c, &ak4375_regmap);
+ if (IS_ERR(ak4375->regmap))
+ return PTR_ERR(ak4375->regmap);
+
+ i2c_set_clientdata(i2c, ak4375);
+ ak4375->dev = &i2c->dev;
+
+ drvdata = of_device_get_match_data(&i2c->dev);
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ ak4375->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(ak4375->dev, ARRAY_SIZE(ak4375->supplies), ak4375->supplies);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW);
+ if (IS_ERR(ak4375->pdn_gpiod))
+ return dev_err_probe(ak4375->dev, PTR_ERR(ak4375->pdn_gpiod),
+ "failed to get pdn\n");
+
+ ret = ak4375_power_on(ak4375);
+ if (ret < 0)
+ return ret;
+
+ /* Don't read deviceid from cache */
+ regcache_cache_bypass(ak4375->regmap, true);
+
+ ret = regmap_read(ak4375->regmap, AK4375_15_AUDIO_IF_FORMAT, &deviceid);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "unable to read DEVICEID!\n");
+ return ret;
+ }
+
+ regcache_cache_bypass(ak4375->regmap, false);
+
+ deviceid = (deviceid & DEVICEID_MASK) >> 5;
+
+ switch (deviceid) {
+ case DEVICEID_AK4331:
+ dev_err(ak4375->dev, "found untested AK4331\n");
+ return -EINVAL;
+ case DEVICEID_AK4375:
+ dev_dbg(ak4375->dev, "found AK4375\n");
+ break;
+ case DEVICEID_AK4375A:
+ dev_dbg(ak4375->dev, "found AK4375A\n");
+ break;
+ case DEVICEID_AK4376A:
+ dev_err(ak4375->dev, "found unsupported AK4376/A!\n");
+ return -EINVAL;
+ case DEVICEID_AK4377:
+ dev_err(ak4375->dev, "found unsupported AK4377!\n");
+ return -EINVAL;
+ default:
+ dev_err(ak4375->dev, "unrecognized DEVICEID!\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_set_active(ak4375->dev);
+ pm_runtime_enable(ak4375->dev);
+
+ ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv,
+ drvdata->dai_drv, 1);
+ if (ret < 0) {
+ dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ak4375_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id ak4375_of_match[] = {
+ { .compatible = "asahi-kasei,ak4375", .data = &ak4375_drvdata },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ak4375_of_match);
+
+static struct i2c_driver ak4375_i2c_driver = {
+ .driver = {
+ .name = "ak4375",
+ .pm = pm_ptr(&ak4375_pm),
+ .of_match_table = ak4375_of_match,
+ },
+ .probe = ak4375_i2c_probe,
+ .remove = ak4375_i2c_remove,
+};
+module_i2c_driver(ak4375_i2c_driver);
+
+MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>");
+MODULE_DESCRIPTION("ASoC AK4375 DAC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
new file mode 100644
index 000000000000..f0b465f9ded5
--- /dev/null
+++ b/sound/soc/codecs/ak4458.c
@@ -0,0 +1,813 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Audio driver for AK4458 DAC
+//
+// Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+// Copyright 2018 NXP
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ak4458.h"
+
+#define AK4458_NUM_SUPPLIES 2
+static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = {
+ "DVDD",
+ "AVDD",
+};
+
+enum ak4458_type {
+ AK4458 = 0,
+ AK4497 = 1,
+};
+
+struct ak4458_drvdata {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct snd_soc_component_driver *comp_drv;
+ enum ak4458_type type;
+};
+
+/* AK4458 Codec Private Data */
+struct ak4458_priv {
+ struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES];
+ const struct ak4458_drvdata *drvdata;
+ struct device *dev;
+ struct regmap *regmap;
+ struct reset_control *reset;
+ struct gpio_desc *mute_gpiod;
+ int digfil; /* SSLOW, SD, SLOW bits */
+ int fs; /* sampling rate */
+ int fmt;
+ int slots;
+ int slot_width;
+ u32 dsd_path; /* For ak4497 */
+};
+
+static const struct reg_default ak4458_reg_defaults[] = {
+ { 0x00, 0x0C }, /* 0x00 AK4458_00_CONTROL1 */
+ { 0x01, 0x22 }, /* 0x01 AK4458_01_CONTROL2 */
+ { 0x02, 0x00 }, /* 0x02 AK4458_02_CONTROL3 */
+ { 0x03, 0xFF }, /* 0x03 AK4458_03_LCHATT */
+ { 0x04, 0xFF }, /* 0x04 AK4458_04_RCHATT */
+ { 0x05, 0x00 }, /* 0x05 AK4458_05_CONTROL4 */
+ { 0x06, 0x00 }, /* 0x06 AK4458_06_DSD1 */
+ { 0x07, 0x03 }, /* 0x07 AK4458_07_CONTROL5 */
+ { 0x08, 0x00 }, /* 0x08 AK4458_08_SOUND_CONTROL */
+ { 0x09, 0x00 }, /* 0x09 AK4458_09_DSD2 */
+ { 0x0A, 0x0D }, /* 0x0A AK4458_0A_CONTROL6 */
+ { 0x0B, 0x0C }, /* 0x0B AK4458_0B_CONTROL7 */
+ { 0x0C, 0x00 }, /* 0x0C AK4458_0C_CONTROL8 */
+ { 0x0D, 0x00 }, /* 0x0D AK4458_0D_CONTROL9 */
+ { 0x0E, 0x50 }, /* 0x0E AK4458_0E_CONTROL10 */
+ { 0x0F, 0xFF }, /* 0x0F AK4458_0F_L2CHATT */
+ { 0x10, 0xFF }, /* 0x10 AK4458_10_R2CHATT */
+ { 0x11, 0xFF }, /* 0x11 AK4458_11_L3CHATT */
+ { 0x12, 0xFF }, /* 0x12 AK4458_12_R3CHATT */
+ { 0x13, 0xFF }, /* 0x13 AK4458_13_L4CHATT */
+ { 0x14, 0xFF }, /* 0x14 AK4458_14_R4CHATT */
+};
+
+/*
+ * Volume control:
+ * from -127 to 0 dB in 0.5 dB steps (mute instead of -127.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+
+/*
+ * DEM1 bit DEM0 bit Mode
+ * 0 0 44.1kHz
+ * 0 1 OFF (default)
+ * 1 0 48kHz
+ * 1 1 32kHz
+ */
+static const char * const ak4458_dem_select_texts[] = {
+ "44.1kHz", "OFF", "48kHz", "32kHz"
+};
+
+/*
+ * SSLOW, SD, SLOW bits Digital Filter Setting
+ * 0, 0, 0 : Sharp Roll-Off Filter
+ * 0, 0, 1 : Slow Roll-Off Filter
+ * 0, 1, 0 : Short delay Sharp Roll-Off Filter
+ * 0, 1, 1 : Short delay Slow Roll-Off Filter
+ * 1, *, * : Super Slow Roll-Off Filter
+ */
+static const char * const ak4458_digfil_select_texts[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short delay Sharp Roll-Off Filter",
+ "Short delay Slow Roll-Off Filter",
+ "Super Slow Roll-Off Filter"
+};
+
+/*
+ * DZFB: Inverting Enable of DZF
+ * 0: DZF goes H at Zero Detection
+ * 1: DZF goes L at Zero Detection
+ */
+static const char * const ak4458_dzfb_select_texts[] = {"H", "L"};
+
+/*
+ * SC1-0 bits: Sound Mode Setting
+ * 0 0 : Sound Mode 0
+ * 0 1 : Sound Mode 1
+ * 1 0 : Sound Mode 2
+ * 1 1 : Reserved
+ */
+static const char * const ak4458_sc_select_texts[] = {
+ "Sound Mode 0", "Sound Mode 1", "Sound Mode 2"
+};
+
+/* FIR2-0 bits: FIR Filter Mode Setting */
+static const char * const ak4458_fir_select_texts[] = {
+ "Mode 0", "Mode 1", "Mode 2", "Mode 3",
+ "Mode 4", "Mode 5", "Mode 6", "Mode 7",
+};
+
+/* ATS1-0 bits Attenuation Speed */
+static const char * const ak4458_ats_select_texts[] = {
+ "4080/fs", "2040/fs", "510/fs", "255/fs",
+};
+
+/* DIF2 bit Audio Interface Format Setting(BICK fs) */
+static const char * const ak4458_dif_select_texts[] = {"32fs,48fs", "64fs",};
+
+static const struct soc_enum ak4458_dac1_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_01_CONTROL2, 1,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_dac2_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_0A_CONTROL6, 0,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_dac3_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 4,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_dac4_dem_enum =
+ SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 6,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts);
+static const struct soc_enum ak4458_digfil_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4458_digfil_select_texts),
+ ak4458_digfil_select_texts);
+static const struct soc_enum ak4458_dzfb_enum =
+ SOC_ENUM_SINGLE(AK4458_02_CONTROL3, 2,
+ ARRAY_SIZE(ak4458_dzfb_select_texts),
+ ak4458_dzfb_select_texts);
+static const struct soc_enum ak4458_sm_enum =
+ SOC_ENUM_SINGLE(AK4458_08_SOUND_CONTROL, 0,
+ ARRAY_SIZE(ak4458_sc_select_texts),
+ ak4458_sc_select_texts);
+static const struct soc_enum ak4458_fir_enum =
+ SOC_ENUM_SINGLE(AK4458_0C_CONTROL8, 0,
+ ARRAY_SIZE(ak4458_fir_select_texts),
+ ak4458_fir_select_texts);
+static const struct soc_enum ak4458_ats_enum =
+ SOC_ENUM_SINGLE(AK4458_0B_CONTROL7, 6,
+ ARRAY_SIZE(ak4458_ats_select_texts),
+ ak4458_ats_select_texts);
+static const struct soc_enum ak4458_dif_enum =
+ SOC_ENUM_SINGLE(AK4458_00_CONTROL1, 3,
+ ARRAY_SIZE(ak4458_dif_select_texts),
+ ak4458_dif_select_texts);
+
+static int get_digfil(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = ak4458->digfil;
+
+ return 0;
+}
+
+static int set_digfil(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int num;
+
+ num = ucontrol->value.enumerated.item[0];
+ if (num > 4)
+ return -EINVAL;
+
+ ak4458->digfil = num;
+
+ /* write SD bit */
+ snd_soc_component_update_bits(component, AK4458_01_CONTROL2,
+ AK4458_SD_MASK,
+ ((ak4458->digfil & 0x02) << 4));
+
+ /* write SLOW bit */
+ snd_soc_component_update_bits(component, AK4458_02_CONTROL3,
+ AK4458_SLOW_MASK,
+ (ak4458->digfil & 0x01));
+
+ /* write SSLOW bit */
+ snd_soc_component_update_bits(component, AK4458_05_CONTROL4,
+ AK4458_SSLOW_MASK,
+ ((ak4458->digfil & 0x04) >> 2));
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new ak4458_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", AK4458_03_LCHATT,
+ AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", AK4458_0F_L2CHATT,
+ AK4458_10_R2CHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC3 Playback Volume", AK4458_11_L3CHATT,
+ AK4458_12_R3CHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC4 Playback Volume", AK4458_13_L4CHATT,
+ AK4458_14_R4CHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_ENUM("AK4458 De-emphasis Response DAC1", ak4458_dac1_dem_enum),
+ SOC_ENUM("AK4458 De-emphasis Response DAC2", ak4458_dac2_dem_enum),
+ SOC_ENUM("AK4458 De-emphasis Response DAC3", ak4458_dac3_dem_enum),
+ SOC_ENUM("AK4458 De-emphasis Response DAC4", ak4458_dac4_dem_enum),
+ SOC_ENUM_EXT("AK4458 Digital Filter Setting", ak4458_digfil_enum,
+ get_digfil, set_digfil),
+ SOC_ENUM("AK4458 Inverting Enable of DZFB", ak4458_dzfb_enum),
+ SOC_ENUM("AK4458 Sound Mode", ak4458_sm_enum),
+ SOC_ENUM("AK4458 FIR Filter Mode Setting", ak4458_fir_enum),
+ SOC_ENUM("AK4458 Attenuation transition Time Setting",
+ ak4458_ats_enum),
+ SOC_ENUM("AK4458 BICK fs Setting", ak4458_dif_enum),
+};
+
+/* ak4458 dapm widgets */
+static const struct snd_soc_dapm_widget ak4458_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AK4458 DAC1", NULL, AK4458_0A_CONTROL6, 2, 0),/*pw*/
+ SND_SOC_DAPM_AIF_IN("AK4458 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTA"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC2", NULL, AK4458_0A_CONTROL6, 3, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTB"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC3", NULL, AK4458_0B_CONTROL7, 2, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTC"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC4", NULL, AK4458_0B_CONTROL7, 3, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTD"),
+};
+
+static const struct snd_soc_dapm_route ak4458_intercon[] = {
+ {"AK4458 DAC1", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTA", NULL, "AK4458 DAC1"},
+
+ {"AK4458 DAC2", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTB", NULL, "AK4458 DAC2"},
+
+ {"AK4458 DAC3", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTC", NULL, "AK4458 DAC3"},
+
+ {"AK4458 DAC4", NULL, "AK4458 SDTI"},
+ {"AK4458 AOUTD", NULL, "AK4458 DAC4"},
+};
+
+/* ak4497 controls */
+static const struct snd_kcontrol_new ak4497_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC Playback Volume", AK4458_03_LCHATT,
+ AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv),
+ SOC_ENUM("AK4497 De-emphasis Response DAC", ak4458_dac1_dem_enum),
+ SOC_ENUM_EXT("AK4497 Digital Filter Setting", ak4458_digfil_enum,
+ get_digfil, set_digfil),
+ SOC_ENUM("AK4497 Inverting Enable of DZFB", ak4458_dzfb_enum),
+ SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum),
+ SOC_ENUM("AK4497 Attenuation transition Time Setting",
+ ak4458_ats_enum),
+};
+
+/* ak4497 dapm widgets */
+static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4458_0A_CONTROL6, 2, 0),
+ SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("AK4497 AOUT"),
+};
+
+/* ak4497 dapm routes */
+static const struct snd_soc_dapm_route ak4497_intercon[] = {
+ {"AK4497 DAC", NULL, "AK4497 SDTI"},
+ {"AK4497 AOUT", NULL, "AK4497 DAC"},
+
+};
+
+static int ak4458_get_tdm_mode(struct ak4458_priv *ak4458)
+{
+ switch (ak4458->slots * ak4458->slot_width) {
+ case 128:
+ return 1;
+ case 256:
+ return 2;
+ case 512:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+static int ak4458_rstn_control(struct snd_soc_component *component, int bit)
+{
+ int ret;
+
+ if (bit)
+ ret = snd_soc_component_update_bits(component,
+ AK4458_00_CONTROL1,
+ AK4458_RSTN_MASK,
+ 0x1);
+ else
+ ret = snd_soc_component_update_bits(component,
+ AK4458_00_CONTROL1,
+ AK4458_RSTN_MASK,
+ 0x0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int pcm_width = max(params_physical_width(params), ak4458->slot_width);
+ u8 format, dsdsel0, dsdsel1, dchn;
+ int nfs1, dsd_bclk, ret, channels, channels_max;
+
+ nfs1 = params_rate(params);
+ ak4458->fs = nfs1;
+
+ /* calculate bit clock */
+ channels = params_channels(params);
+ channels_max = dai->driver->playback.channels_max;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ dsd_bclk = nfs1 * params_physical_width(params);
+ switch (dsd_bclk) {
+ case 2822400:
+ dsdsel0 = 0;
+ dsdsel1 = 0;
+ break;
+ case 5644800:
+ dsdsel0 = 1;
+ dsdsel1 = 0;
+ break;
+ case 11289600:
+ dsdsel0 = 0;
+ dsdsel1 = 1;
+ break;
+ case 22579200:
+ if (ak4458->drvdata->type == AK4497) {
+ dsdsel0 = 1;
+ dsdsel1 = 1;
+ } else {
+ dev_err(dai->dev, "DSD512 not supported.\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported dsd bclk.\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK4458_06_DSD1,
+ AK4458_DSDSEL_MASK, dsdsel0);
+ snd_soc_component_update_bits(component, AK4458_09_DSD2,
+ AK4458_DSDSEL_MASK, dsdsel1);
+ break;
+ }
+
+ /* Master Clock Frequency Auto Setting Mode Enable */
+ snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80);
+
+ switch (pcm_width) {
+ case 16:
+ if (ak4458->fmt == SND_SOC_DAIFMT_I2S)
+ format = AK4458_DIF_24BIT_I2S;
+ else
+ format = AK4458_DIF_16BIT_LSB;
+ break;
+ case 32:
+ switch (ak4458->fmt) {
+ case SND_SOC_DAIFMT_I2S:
+ format = AK4458_DIF_32BIT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = AK4458_DIF_32BIT_MSB;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = AK4458_DIF_32BIT_LSB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = AK4458_DIF_32BIT_MSB;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ format = AK4458_DIF_32BIT_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK4458_00_CONTROL1,
+ AK4458_DIF_MASK, format);
+
+ /*
+ * Enable/disable Daisy Chain if in TDM mode and the number of played
+ * channels is bigger than the maximum supported number of channels
+ */
+ dchn = ak4458_get_tdm_mode(ak4458) &&
+ (ak4458->fmt == SND_SOC_DAIFMT_DSP_B) &&
+ (channels > channels_max) ? AK4458_DCHAIN_MASK : 0;
+
+ snd_soc_component_update_bits(component, AK4458_0B_CONTROL7,
+ AK4458_DCHAIN_MASK, dchn);
+
+ if (ak4458->drvdata->type == AK4497) {
+ ret = snd_soc_component_update_bits(component, AK4458_09_DSD2,
+ 0x4, (ak4458->dsd_path << 2));
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ak4458_rstn_control(component, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(component, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC: /* Consumer Mode */
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP: /* Provider Mode is not supported */
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ default:
+ dev_err(component->dev, "Clock provider mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_PDM:
+ ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Audio format 0x%02X unsupported\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ /* DSD mode */
+ snd_soc_component_update_bits(component, AK4458_02_CONTROL3,
+ AK4458_DP_MASK,
+ ak4458->fmt == SND_SOC_DAIFMT_PDM ?
+ AK4458_DP_MASK : 0);
+
+ ret = ak4458_rstn_control(component, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(component, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const int att_speed[] = { 4080, 2040, 510, 255 };
+
+static int ak4458_set_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int nfs, ndt, reg;
+ int ats;
+
+ nfs = ak4458->fs;
+
+ reg = snd_soc_component_read(component, AK4458_0B_CONTROL7);
+ ats = (reg & AK4458_ATS_MASK) >> AK4458_ATS_SHIFT;
+
+ ndt = att_speed[ats] / (nfs / 1000);
+
+ if (mute) {
+ snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 1);
+ mdelay(ndt);
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
+ } else {
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 0);
+ snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 0);
+ mdelay(ndt);
+ }
+
+ return 0;
+}
+
+static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
+ int mode;
+
+ ak4458->slots = slots;
+ ak4458->slot_width = slot_width;
+
+ mode = ak4458_get_tdm_mode(ak4458) << AK4458_MODE_SHIFT;
+
+ snd_soc_component_update_bits(component, AK4458_0A_CONTROL6,
+ AK4458_MODE_MASK,
+ mode);
+
+ return 0;
+}
+
+#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_DSD_U8 |\
+ SNDRV_PCM_FMTBIT_DSD_U16_LE |\
+ SNDRV_PCM_FMTBIT_DSD_U32_LE)
+
+static const unsigned int ak4458_rates[] = {
+ 8000, 11025, 16000, 22050,
+ 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800,
+ 384000, 705600, 768000, 1411200,
+ 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = {
+ .count = ARRAY_SIZE(ak4458_rates),
+ .list = ak4458_rates,
+};
+
+static int ak4458_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak4458_rate_constraints);
+}
+
+static const struct snd_soc_dai_ops ak4458_dai_ops = {
+ .startup = ak4458_startup,
+ .hw_params = ak4458_hw_params,
+ .set_fmt = ak4458_set_dai_fmt,
+ .mute_stream = ak4458_set_dai_mute,
+ .set_tdm_slot = ak4458_set_tdm_slot,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver ak4458_dai = {
+ .name = "ak4458-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK4458_FORMATS,
+ },
+ .ops = &ak4458_dai_ops,
+};
+
+static struct snd_soc_dai_driver ak4497_dai = {
+ .name = "ak4497-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK4458_FORMATS,
+ },
+ .ops = &ak4458_dai_ops,
+};
+
+static void ak4458_reset(struct ak4458_priv *ak4458, bool active)
+{
+ if (!IS_ERR_OR_NULL(ak4458->reset)) {
+ if (active)
+ reset_control_assert(ak4458->reset);
+ else
+ reset_control_deassert(ak4458->reset);
+ usleep_range(1000, 2000);
+ }
+}
+
+static int ak4458_runtime_suspend(struct device *dev)
+{
+ struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak4458->regmap, true);
+
+ ak4458_reset(ak4458, true);
+
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ return 0;
+}
+
+static int ak4458_runtime_resume(struct device *dev)
+{
+ struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ if (ret != 0) {
+ dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (ak4458->mute_gpiod)
+ gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
+
+ ak4458_reset(ak4458, false);
+
+ regcache_cache_only(ak4458->regmap, false);
+ regcache_mark_dirty(ak4458->regmap);
+
+ return regcache_sync(ak4458->regmap);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4458 = {
+ .controls = ak4458_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4458_snd_controls),
+ .dapm_widgets = ak4458_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4458_dapm_widgets),
+ .dapm_routes = ak4458_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4458_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_ak4497 = {
+ .controls = ak4497_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4497_snd_controls),
+ .dapm_widgets = ak4497_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets),
+ .dapm_routes = ak4497_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4497_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4458_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4458_14_R4CHATT,
+ .reg_defaults = ak4458_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4458_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct ak4458_drvdata ak4458_drvdata = {
+ .dai_drv = &ak4458_dai,
+ .comp_drv = &soc_codec_dev_ak4458,
+ .type = AK4458,
+};
+
+static const struct ak4458_drvdata ak4497_drvdata = {
+ .dai_drv = &ak4497_dai,
+ .comp_drv = &soc_codec_dev_ak4497,
+ .type = AK4497,
+};
+
+static const struct dev_pm_ops ak4458_pm = {
+ RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static int ak4458_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak4458_priv *ak4458;
+ int ret, i;
+
+ ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL);
+ if (!ak4458)
+ return -ENOMEM;
+
+ ak4458->regmap = devm_regmap_init_i2c(i2c, &ak4458_regmap);
+ if (IS_ERR(ak4458->regmap))
+ return PTR_ERR(ak4458->regmap);
+
+ i2c_set_clientdata(i2c, ak4458);
+ ak4458->dev = &i2c->dev;
+
+ ak4458->drvdata = of_device_get_match_data(&i2c->dev);
+
+ ak4458->reset = devm_reset_control_get_optional_shared(ak4458->dev, NULL);
+ if (IS_ERR(ak4458->reset))
+ return PTR_ERR(ak4458->reset);
+
+ ak4458->mute_gpiod = devm_gpiod_get_optional(ak4458->dev, "mute",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ak4458->mute_gpiod))
+ return PTR_ERR(ak4458->mute_gpiod);
+
+ /* Optional property for ak4497 */
+ of_property_read_u32(i2c->dev.of_node, "dsd-path", &ak4458->dsd_path);
+
+ for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++)
+ ak4458->supplies[i].supply = ak4458_supply_names[i];
+
+ ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ if (ret != 0) {
+ dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(ak4458->dev,
+ ak4458->drvdata->comp_drv,
+ ak4458->drvdata->dai_drv, 1);
+ if (ret < 0) {
+ dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&i2c->dev);
+ regcache_cache_only(ak4458->regmap, true);
+ ak4458_reset(ak4458, false);
+
+ return 0;
+}
+
+static void ak4458_i2c_remove(struct i2c_client *i2c)
+{
+ struct ak4458_priv *ak4458 = i2c_get_clientdata(i2c);
+
+ ak4458_reset(ak4458, true);
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id ak4458_of_match[] = {
+ { .compatible = "asahi-kasei,ak4458", .data = &ak4458_drvdata},
+ { .compatible = "asahi-kasei,ak4497", .data = &ak4497_drvdata},
+ { },
+};
+MODULE_DEVICE_TABLE(of, ak4458_of_match);
+
+static struct i2c_driver ak4458_i2c_driver = {
+ .driver = {
+ .name = "ak4458",
+ .pm = pm_ptr(&ak4458_pm),
+ .of_match_table = ak4458_of_match,
+ },
+ .probe = ak4458_i2c_probe,
+ .remove = ak4458_i2c_remove,
+};
+
+module_i2c_driver(ak4458_i2c_driver);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+MODULE_DESCRIPTION("ASoC AK4458 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4458.h b/sound/soc/codecs/ak4458.h
new file mode 100644
index 000000000000..9ad869575f8d
--- /dev/null
+++ b/sound/soc/codecs/ak4458.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Audio driver for AK4458
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright 2018 NXP
+ */
+
+#ifndef _AK4458_H
+#define _AK4458_H
+
+#include <linux/regmap.h>
+
+/* Settings */
+
+#define AK4458_00_CONTROL1 0x00
+#define AK4458_01_CONTROL2 0x01
+#define AK4458_02_CONTROL3 0x02
+#define AK4458_03_LCHATT 0x03
+#define AK4458_04_RCHATT 0x04
+#define AK4458_05_CONTROL4 0x05
+#define AK4458_06_DSD1 0x06
+#define AK4458_07_CONTROL5 0x07
+#define AK4458_08_SOUND_CONTROL 0x08
+#define AK4458_09_DSD2 0x09
+#define AK4458_0A_CONTROL6 0x0A
+#define AK4458_0B_CONTROL7 0x0B
+#define AK4458_0C_CONTROL8 0x0C
+#define AK4458_0D_CONTROL9 0x0D
+#define AK4458_0E_CONTROL10 0x0E
+#define AK4458_0F_L2CHATT 0x0F
+#define AK4458_10_R2CHATT 0x10
+#define AK4458_11_L3CHATT 0x11
+#define AK4458_12_R3CHATT 0x12
+#define AK4458_13_L4CHATT 0x13
+#define AK4458_14_R4CHATT 0x14
+
+/* Bitfield Definitions */
+
+/* AK4458_00_CONTROL1 (0x00) Fields
+ * Addr Register Name D7 D6 D5 D4 D3 D2 D1 D0
+ * 00H Control 1 ACKS 0 0 0 DIF2 DIF1 DIF0 RSTN
+ */
+
+/* Digital Filter (SD, SLOW, SSLOW) */
+#define AK4458_SD_MASK GENMASK(5, 5)
+#define AK4458_SLOW_MASK GENMASK(0, 0)
+#define AK4458_SSLOW_MASK GENMASK(0, 0)
+
+/* DIF2 1 0
+ * x 1 0 MSB justified Figure 3 (default)
+ * x 1 1 I2S Compliment Figure 4
+ */
+#define AK4458_DIF_SHIFT 1
+#define AK4458_DIF_MASK GENMASK(3, 1)
+
+#define AK4458_DIF_16BIT_LSB (0 << 1)
+#define AK4458_DIF_24BIT_I2S (3 << 1)
+#define AK4458_DIF_32BIT_LSB (5 << 1)
+#define AK4458_DIF_32BIT_MSB (6 << 1)
+#define AK4458_DIF_32BIT_I2S (7 << 1)
+
+/* AK4458_00_CONTROL1 (0x00) D0 bit */
+#define AK4458_RSTN_MASK GENMASK(0, 0)
+#define AK4458_RSTN (0x1 << 0)
+
+/* AK4458_0A_CONTROL6 Mode bits */
+#define AK4458_MODE_SHIFT 6
+#define AK4458_MODE_MASK GENMASK(7, 6)
+#define AK4458_MODE_NORMAL (0 << AK4458_MODE_SHIFT)
+#define AK4458_MODE_TDM128 (1 << AK4458_MODE_SHIFT)
+#define AK4458_MODE_TDM256 (2 << AK4458_MODE_SHIFT)
+#define AK4458_MODE_TDM512 (3 << AK4458_MODE_SHIFT)
+
+/* DAC Digital attenuator transition time setting
+ * Table 19
+ * Mode ATS1 ATS2 ATT speed
+ * 0 0 0 4080/fs
+ * 1 0 1 2040/fs
+ * 2 1 0 510/fs
+ * 3 1 1 255/fs
+ * */
+#define AK4458_ATS_SHIFT 6
+#define AK4458_ATS_MASK GENMASK(7, 6)
+#define AK4458_DCHAIN_MASK (0x1 << 1)
+
+#define AK4458_DSDSEL_MASK (0x1 << 0)
+#define AK4458_DP_MASK (0x1 << 7)
+
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 54428c64387b..aadc46a47280 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ak4535.c -- AK4535 ALSA Soc Audio driver
*
@@ -6,10 +7,6 @@
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.c by Liam Girdwood
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -251,8 +248,8 @@ static const struct snd_soc_dapm_route ak4535_audio_map[] = {
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component);
ak4535->sysclk = freq;
return 0;
@@ -262,9 +259,9 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
- u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5);
+ struct snd_soc_component *component = dai->component;
+ struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component);
+ u8 mode2 = snd_soc_component_read(component, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
if (rate)
@@ -283,14 +280,14 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
}
/* set rate */
- snd_soc_write(codec, AK4535_MODE2, mode2);
+ snd_soc_component_write(component, AK4535_MODE2, mode2);
return 0;
}
static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode1 = 0;
/* interface format */
@@ -308,37 +305,38 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* use 32 fs for BCLK to save power */
mode1 |= 0x4;
- snd_soc_write(codec, AK4535_MODE1, mode1);
+ snd_soc_component_write(component, AK4535_MODE1, mode1);
return 0;
}
-static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+static int ak4535_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, AK4535_DAC);
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, AK4535_DAC);
+
if (!mute)
- snd_soc_write(codec, AK4535_DAC, mute_reg & ~0x20);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg & ~0x20);
else
- snd_soc_write(codec, AK4535_DAC, mute_reg | 0x20);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg | 0x20);
return 0;
}
-static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+static int ak4535_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0x20);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0x80);
- snd_soc_update_bits(codec, AK4535_PM2, 0x80, 0);
+ snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0x80);
+ snd_soc_component_update_bits(component, AK4535_PM2, 0x80, 0);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0);
+ snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0);
break;
}
return 0;
@@ -351,8 +349,9 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ak4535_dai_ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
- .digital_mute = ak4535_mute,
+ .mute_stream = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ak4535_dai = {
@@ -372,9 +371,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
.ops = &ak4535_dai_ops,
};
-static int ak4535_resume(struct snd_soc_codec *codec)
+static int ak4535_resume(struct snd_soc_component *component)
{
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -390,21 +389,22 @@ static const struct regmap_config ak4535_regmap = {
.num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults),
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
- .resume = ak4535_resume,
- .set_bias_level = ak4535_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = ak4535_snd_controls,
- .num_controls = ARRAY_SIZE(ak4535_snd_controls),
- .dapm_widgets = ak4535_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
- .dapm_routes = ak4535_audio_map,
- .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
+static const struct snd_soc_component_driver soc_component_dev_ak4535 = {
+ .resume = ak4535_resume,
+ .set_bias_level = ak4535_set_bias_level,
+ .controls = ak4535_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4535_snd_controls),
+ .dapm_widgets = ak4535_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
+ .dapm_routes = ak4535_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int ak4535_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4535_i2c_probe(struct i2c_client *i2c)
{
struct ak4535_priv *ak4535;
int ret;
@@ -423,20 +423,14 @@ static int ak4535_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4535);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ak4535, &ak4535_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ak4535, &ak4535_dai, 1);
return ret;
}
-static int ak4535_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ak4535_i2c_id[] = {
- { "ak4535", 0 },
+ { "ak4535" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
@@ -445,8 +439,7 @@ static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
},
- .probe = ak4535_i2c_probe,
- .remove = ak4535_i2c_remove,
+ .probe = ak4535_i2c_probe,
.id_table = ak4535_i2c_id,
};
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
index 402de1d274bf..978caf52144f 100644
--- a/sound/soc/codecs/ak4535.h
+++ b/sound/soc/codecs/ak4535.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ak4535.h -- AK4535 Soc Audio driver
*
@@ -6,10 +7,6 @@
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _AK4535_H
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
index 298dedc05140..b9607de5a191 100644
--- a/sound/soc/codecs/ak4554.c
+++ b/sound/soc/codecs/ak4554.c
@@ -1,13 +1,8 @@
-/*
- * ak4554.c
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+// ak4554.c
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
#include <linux/module.h>
#include <sound/soc.h>
@@ -61,29 +56,26 @@ static struct snd_soc_dai_driver ak4554_dai = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4554 = {
- .dapm_widgets = ak4554_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
- .dapm_routes = ak4554_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_ak4554 = {
+ .dapm_widgets = ak4554_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
+ .dapm_routes = ak4554_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ak4554_soc_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ak4554,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ak4554,
&ak4554_dai, 1);
}
-static int ak4554_soc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static const struct of_device_id ak4554_of_match[] = {
{ .compatible = "asahi-kasei,ak4554" },
{},
@@ -96,10 +88,9 @@ static struct platform_driver ak4554_driver = {
.of_match_table = ak4554_of_match,
},
.probe = ak4554_soc_probe,
- .remove = ak4554_soc_remove,
};
module_platform_driver(ak4554_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SoC AK4554 driver");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 647f69de6baa..de9e43185555 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -1,23 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ak4613.c -- Asahi Kasei ALSA Soc Audio driver
+//
+// Copyright (C) 2015 Renesas Electronics Corporation
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// Based on ak4642.c by Kuninori Morimoto
+// Based on wm8731.c by Richard Purdie
+// Based on ak4535.c by Richard Purdie
+// Based on wm8753.c by Liam Girdwood
+
/*
- * ak4613.c -- Asahi Kasei ALSA Soc Audio driver
+ * +-------+
+ * |AK4613 |
+ * SDTO1 <-| |
+ * | |
+ * SDTI1 ->| |
+ * SDTI2 ->| |
+ * SDTI3 ->| |
+ * +-------+
+ *
+ * +---+
+ * clk | |___________________________________________...
+ *
+ * [TDM512]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4][L5][R5][L6][R6]
+ *
+ * [TDM256]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4]
+ * SDTI2 [L5][R5][L6][R6]
+ *
+ * [TDM128]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2]
+ * SDTI2 [L3][R3][L4][R4]
+ * SDTI3 [L5][R5][L6][R6]
+ *
+ * [STEREO]
+ * Playback 2ch : SDTI1
+ * Capture 2ch : SDTO1
+ *
+ * [TDM512]
+ * Playback 12ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ * [TDM256]
+ * Playback 12ch : SDTI1 + SDTI2
+ * Playback 8ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ * [TDM128]
+ * Playback 12ch : SDTI1 + SDTI2 + SDTI3
+ * Playback 8ch : SDTI1 + SDTI2
+ * Playback 4ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
*
- * Copyright (C) 2015 Renesas Electronics Corporation
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * !!! NOTE !!!
*
- * Based on ak4642.c by Kuninori Morimoto
- * Based on wm8731.c by Richard Purdie
- * Based on ak4535.c by Richard Purdie
- * Based on wm8753.c by Liam Girdwood
+ * Renesas is the only user of ak4613 on upstream so far,
+ * but the chip connection is like below.
+ * Thus, Renesas can't test all connection case.
+ * Tested TDM is very limited.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * +-----+ +-----------+
+ * | SoC | | AK4613 |
+ * | |<-----|SDTO1 IN1|<-- Mic
+ * | | | IN2|
+ * | | | |
+ * | |----->|SDTI1 OUT1|--> Headphone
+ * +-----+ |SDTI2 OUT2|
+ * |SDTI3 OUT3|
+ * | OUT4|
+ * | OUT5|
+ * | OUT6|
+ * +-----------+
+ *
+ * Renesas SoC can handle [2, 6,8] channels.
+ * Ak4613 can handle [2,4, 8,12] channels.
+ *
+ * Because of above HW connection and available channels number,
+ * Renesas could test are ...
+ *
+ * [STEREO] Playback 2ch : SDTI1
+ * Capture 2ch : SDTO1
+ * [TDM256] Playback 8ch : SDTI1 (*)
+ *
+ * (*) it used 8ch data between SoC <-> AK4613 on TDM256 mode,
+ * but could confirm is only first 2ch because only 1
+ * Headphone is connected.
+ *
+ * see
+ * AK4613_ENABLE_TDM_TEST
*/
-
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -75,24 +158,80 @@
#define DFS_DOUBLE_SPEED (1 << 2)
#define DFS_QUAD_SPEED (2 << 2)
-struct ak4613_formats {
- unsigned int width;
- unsigned int fmt;
-};
+/* ICTRL */
+#define ICTRL_MASK (0x3)
+
+/* OCTRL */
+#define OCTRL_MASK (0x3F)
+
+/*
+ * configs
+ *
+ * 0x000000BA
+ *
+ * B : AK4613_CONFIG_SDTI_x
+ * A : AK4613_CONFIG_MODE_x
+ */
+#define AK4613_CONFIG_SET(priv, x) priv->configs |= AK4613_CONFIG_##x
+#define AK4613_CONFIG_GET(priv, x) (priv->configs & AK4613_CONFIG_##x##_MASK)
+
+/*
+ * AK4613_CONFIG_SDTI_x
+ *
+ * It indicates how many SDTIx is connected.
+ */
+#define AK4613_CONFIG_SDTI_MASK (0xF << 4)
+#define AK4613_CONFIG_SDTI(x) (((x) & 0xF) << 4)
+#define AK4613_CONFIG_SDTI_set(priv, x) AK4613_CONFIG_SET(priv, SDTI(x))
+#define AK4613_CONFIG_SDTI_get(priv) ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF)
+
+/*
+ * AK4613_CONFIG_MODE_x
+ *
+ * Same as Ctrl1 :: TDM1/TDM0
+ * No shift is requested
+ * see
+ * AK4613_CTRL1_TO_MODE()
+ * Table 11/12/13/14
+ */
+#define AK4613_CONFIG_MODE_MASK (0xF)
+#define AK4613_CONFIG_MODE_STEREO (0x0)
+#define AK4613_CONFIG_MODE_TDM512 (0x1)
+#define AK4613_CONFIG_MODE_TDM256 (0x2)
+#define AK4613_CONFIG_MODE_TDM128 (0x3)
+
+/*
+ * !!!! FIXME !!!!
+ *
+ * Because of testable HW limitation, TDM256 8ch TDM was only tested.
+ * This driver uses AK4613_ENABLE_TDM_TEST instead of new DT property so far.
+ * Don't hesitate to update driver, you don't need to care compatible
+ * with Renesas.
+ *
+ * #define AK4613_ENABLE_TDM_TEST
+ */
struct ak4613_interface {
- struct ak4613_formats capture;
- struct ak4613_formats playback;
+ unsigned int width;
+ unsigned int fmt;
+ u8 dif;
};
struct ak4613_priv {
struct mutex lock;
- const struct ak4613_interface *iface;
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ struct snd_pcm_hw_constraint_list constraint_channels;
+ struct work_struct dummy_write_work;
+ struct snd_soc_component *component;
+ unsigned int rate;
+ unsigned int sysclk;
unsigned int fmt;
+ unsigned int configs;
+ int cnt;
+ u8 ctrl1;
u8 oc;
u8 ic;
- int cnt;
};
/*
@@ -129,16 +268,24 @@ static const struct reg_default ak4613_reg[] = {
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
};
-#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
-#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+/*
+ * CTRL1 register
+ * see
+ * Table 11/12/13/14
+ */
+#define AUDIO_IFACE(_dif, _width, _fmt) \
+ { \
+ .dif = _dif, \
+ .width = _width, \
+ .fmt = SND_SOC_DAIFMT_##_fmt,\
+ }
static const struct ak4613_interface ak4613_iface[] = {
- /* capture */ /* playback */
- [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) },
- [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) },
- [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) },
- [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) },
- [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) },
+ /* It doesn't support asymmetric format */
+
+ AUDIO_IFACE(0x03, 24, LEFT_J),
+ AUDIO_IFACE(0x04, 24, I2S),
};
+#define AK4613_CTRL1_TO_MODE(priv) ((priv)->ctrl1 >> 6) /* AK4613_CONFIG_MODE_x */
static const struct regmap_config ak4613_regmap_cfg = {
.reg_bits = 8,
@@ -146,6 +293,7 @@ static const struct regmap_config ak4613_regmap_cfg = {
.max_register = 0x16,
.reg_defaults = ak4613_reg,
.num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct of_device_id ak4613_of_match[] = {
@@ -232,9 +380,9 @@ static const struct snd_soc_dapm_route ak4613_intercon[] = {
static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct device *dev = codec->dev;
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
mutex_lock(&priv->lock);
priv->cnt--;
@@ -243,67 +391,185 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
priv->cnt = 0;
}
if (!priv->cnt)
- priv->iface = NULL;
+ priv->ctrl1 = 0;
mutex_unlock(&priv->lock);
}
-static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static void ak4613_hw_constraints(struct ak4613_priv *priv,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ static const unsigned int ak4613_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ };
+#define AK4613_CHANNEL_2 0
+#define AK4613_CHANNEL_4 1
+#define AK4613_CHANNEL_8 2
+#define AK4613_CHANNEL_12 3
+#define AK4613_CHANNEL_NONE -1
+ static const unsigned int ak4613_channels[] = {
+ [AK4613_CHANNEL_2] = 2,
+ [AK4613_CHANNEL_4] = 4,
+ [AK4613_CHANNEL_8] = 8,
+ [AK4613_CHANNEL_12] = 12,
+ };
+#define MODE_MAX 4
+#define SDTx_MAX 4
+#define MASK(x) (1 << AK4613_CHANNEL_##x)
+ static const int mask_list[MODE_MAX][SDTx_MAX] = {
+ /* SDTO SDTIx1 SDTIx2 SDTIx3 */
+ [AK4613_CONFIG_MODE_STEREO] = { MASK(2), MASK(2), MASK(2), MASK(2)},
+ [AK4613_CONFIG_MODE_TDM512] = { MASK(4), MASK(12), MASK(12), MASK(12)},
+ [AK4613_CONFIG_MODE_TDM256] = { MASK(4), MASK(8), MASK(8)|MASK(12), MASK(8)|MASK(12)},
+ [AK4613_CONFIG_MODE_TDM128] = { MASK(4), MASK(4), MASK(4)|MASK(8), MASK(4)|MASK(8)|MASK(12)},
+ };
+ struct snd_pcm_hw_constraint_list *constraint;
+ unsigned int mask;
+ unsigned int mode;
+ unsigned int fs;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int sdti_num;
+ int i;
- fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+ constraint = &priv->constraint_rates;
+ constraint->list = ak4613_rates;
+ constraint->mask = 0;
+ constraint->count = 0;
- switch (fmt) {
- case SND_SOC_DAIFMT_RIGHT_J:
- case SND_SOC_DAIFMT_LEFT_J:
- case SND_SOC_DAIFMT_I2S:
- priv->fmt = fmt;
+ /*
+ * Slave Mode
+ * Normal: [32kHz, 48kHz] : 256fs,384fs or 512fs
+ * Double: [64kHz, 96kHz] : 256fs
+ * Quad : [128kHz,192kHz]: 128fs
+ *
+ * Master mode
+ * Normal: [32kHz, 48kHz] : 256fs or 512fs
+ * Double: [64kHz, 96kHz] : 256fs
+ * Quad : [128kHz,192kHz]: 128fs
+ */
+ for (i = 0; i < ARRAY_SIZE(ak4613_rates); i++) {
+ /* minimum fs on each range */
+ fs = (ak4613_rates[i] <= 96000) ? 256 : 128;
+
+ if (priv->sysclk >= ak4613_rates[i] * fs)
+ constraint->count = i + 1;
+ }
- break;
- default:
- return -EINVAL;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, constraint);
+
+
+ sdti_num = AK4613_CONFIG_SDTI_get(priv);
+ if (WARN_ON(sdti_num >= SDTx_MAX))
+ return;
+
+ if (priv->cnt) {
+ /*
+ * If it was already working,
+ * the constraint is same as working mode.
+ */
+ mode = AK4613_CTRL1_TO_MODE(priv);
+ mask = 0; /* no default */
+ } else {
+ /*
+ * It is not yet working,
+ * the constraint is based on board configs.
+ * STEREO mask is default
+ */
+ mode = AK4613_CONFIG_GET(priv, MODE);
+ mask = mask_list[AK4613_CONFIG_MODE_STEREO][is_play * sdti_num];
}
+ if (WARN_ON(mode >= MODE_MAX))
+ return;
+
+ /* add each mode mask */
+ mask |= mask_list[mode][is_play * sdti_num];
+
+ constraint = &priv->constraint_channels;
+ constraint->list = ak4613_channels;
+ constraint->mask = mask;
+ constraint->count = sizeof(ak4613_channels);
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
+}
+
+static int ak4613_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->lock);
+ ak4613_hw_constraints(priv, substream);
+ priv->cnt++;
+ mutex_unlock(&priv->lock);
+
return 0;
}
-static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
- int is_play,
- unsigned int fmt, unsigned int width)
+static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
{
- const struct ak4613_formats *fmts;
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
- fmts = (is_play) ? &iface->playback : &iface->capture;
+ priv->sysclk = freq;
- if (fmts->fmt != fmt)
- return false;
+ return 0;
+}
- if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
- if (fmts->width != width)
- return false;
- } else {
- if (fmts->width < width)
- return false;
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int fmt;
+
+ fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (fmt) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ priv->fmt = fmt;
+ break;
+ default:
+ return -EINVAL;
}
- return true;
+ fmt = format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ switch (fmt) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ /*
+ * SUPPORTME
+ *
+ * "clock provider" is not yet supperted
+ */
+ return -EINVAL;
+ }
+
+ return 0;
}
static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
- const struct ak4613_interface *iface;
- struct device *dev = codec->dev;
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
unsigned int width = params_width(params);
unsigned int fmt = priv->fmt;
unsigned int rate;
- int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int i, ret;
- u8 fmt_ctrl, ctrl2;
+ u8 ctrl2;
rate = params_rate(params);
switch (rate) {
@@ -312,6 +578,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
case 48000:
ctrl2 = DFS_NORMAL_SPEED;
break;
+ case 64000:
case 88200:
case 96000:
ctrl2 = DFS_DOUBLE_SPEED;
@@ -323,49 +590,60 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
+ priv->rate = rate;
/*
* FIXME
*
- * It doesn't support TDM at this point
+ * It doesn't have full TDM suppert yet
*/
- fmt_ctrl = NO_FMT;
ret = -EINVAL;
- iface = NULL;
mutex_lock(&priv->lock);
- if (priv->iface) {
- if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
- iface = priv->iface;
+ if (priv->cnt > 1) {
+ /*
+ * If it was already working, use current priv->ctrl1
+ */
+ ret = 0;
} else {
- for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) {
- if (!ak4613_dai_fmt_matching(ak4613_iface + i,
- is_play,
- fmt, width))
- continue;
- iface = ak4613_iface + i;
- break;
+ /*
+ * It is not yet working,
+ */
+ unsigned int channel = params_channels(params);
+ u8 tdm;
+
+ /* STEREO or TDM */
+ if (channel == 2)
+ tdm = AK4613_CONFIG_MODE_STEREO;
+ else
+ tdm = AK4613_CONFIG_GET(priv, MODE);
+
+ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) {
+ const struct ak4613_interface *iface = ak4613_iface + i;
+
+ if ((iface->fmt == fmt) && (iface->width == width)) {
+ /*
+ * Ctrl1
+ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
+ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE|
+ * < tdm > < iface->dif >
+ */
+ priv->ctrl1 = (tdm << 6) | (iface->dif << 3);
+ ret = 0;
+ break;
+ }
}
}
-
- if ((priv->iface == NULL) ||
- (priv->iface == iface)) {
- priv->iface = iface;
- priv->cnt++;
- ret = 0;
- }
mutex_unlock(&priv->lock);
if (ret < 0)
goto hw_params_end;
- fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
+ snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1);
+ snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2);
- snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
- snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
-
- snd_soc_write(codec, ICTRL, priv->ic);
- snd_soc_write(codec, OCTRL, priv->oc);
+ snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic);
+ snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc);
hw_params_end:
if (ret < 0)
@@ -374,7 +652,7 @@ hw_params_end:
return ret;
}
-static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+static int ak4613_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
u8 mgmt1 = 0;
@@ -382,27 +660,112 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
mgmt1 |= RSTN;
- /* fall through */
+ fallthrough;
case SND_SOC_BIAS_PREPARE:
mgmt1 |= PMADC | PMDAC;
- /* fall through */
+ fallthrough;
case SND_SOC_BIAS_STANDBY:
mgmt1 |= PMVR;
- /* fall through */
+ fallthrough;
case SND_SOC_BIAS_OFF:
default:
break;
}
- snd_soc_write(codec, PW_MGMT1, mgmt1);
+ snd_soc_component_write(component, PW_MGMT1, mgmt1);
return 0;
}
+static void ak4613_dummy_write(struct work_struct *work)
+{
+ struct ak4613_priv *priv = container_of(work,
+ struct ak4613_priv,
+ dummy_write_work);
+ struct snd_soc_component *component = priv->component;
+ unsigned int mgmt1;
+ unsigned int mgmt3;
+
+ /*
+ * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
+ *
+ * Note
+ *
+ * To avoid extra delay, we want to avoid preemption here,
+ * but we can't. Because it uses I2C access which is using IRQ
+ * and sleep. Thus, delay might be more than 5 LR clocks
+ * see also
+ * ak4613_dai_trigger()
+ */
+ udelay(5000000 / priv->rate);
+
+ mgmt1 = snd_soc_component_read(component, PW_MGMT1);
+ mgmt3 = snd_soc_component_read(component, PW_MGMT3);
+
+ snd_soc_component_write(component, PW_MGMT1, mgmt1);
+ snd_soc_component_write(component, PW_MGMT3, mgmt3);
+}
+
+static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+
+ /*
+ * FIXME
+ *
+ * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
+ * from Power Down Release. Otherwise, Playback volume will be 0dB.
+ * To avoid complex multiple delay/dummy_write method from
+ * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...),
+ * call it once here.
+ *
+ * But, unfortunately, we can't "write" here because here is atomic
+ * context (It uses I2C access for writing).
+ * Thus, use schedule_work() to switching to normal context
+ * immediately.
+ *
+ * Note
+ *
+ * Calling ak4613_dummy_write() function might be delayed.
+ * In such case, ak4613 volume might be temporarily 0dB when
+ * beggining of playback.
+ * see also
+ * ak4613_dummy_write()
+ */
+
+ if ((cmd != SNDRV_PCM_TRIGGER_START) &&
+ (cmd != SNDRV_PCM_TRIGGER_RESUME))
+ return 0;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ priv->component = component;
+ schedule_work(&priv->dummy_write_work);
+
+ return 0;
+}
+
+/*
+ * Select below from Sound Card, not Auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 ak4613_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak4613_dai_ops = {
+ .startup = ak4613_dai_startup,
.shutdown = ak4613_dai_shutdown,
+ .set_sysclk = ak4613_dai_set_sysclk,
.set_fmt = ak4613_dai_set_fmt,
+ .trigger = ak4613_dai_trigger,
.hw_params = ak4613_dai_hw_params,
+ .auto_selectable_formats = &ak4613_dai_formats,
+ .num_auto_selectable_formats = 1,
};
#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\
@@ -413,38 +776,47 @@ static const struct snd_soc_dai_ops ak4613_dai_ops = {
SNDRV_PCM_RATE_96000 |\
SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000)
-#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S24_LE)
+#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_driver ak4613_dai = {
.name = "ak4613-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 12,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 4,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
.ops = &ak4613_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ak4613_resume(struct snd_soc_codec *codec)
+static int ak4613_suspend(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+ regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
+ return 0;
+}
+
+static int ak4613_resume(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
-static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+static const struct snd_soc_component_driver soc_component_dev_ak4613 = {
+ .suspend = ak4613_suspend,
.resume = ak4613_resume,
.set_bias_level = ak4613_set_bias_level,
.controls = ak4613_snd_controls,
@@ -453,6 +825,8 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
.num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
.dapm_routes = ak4613_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
};
static void ak4613_parse_of(struct ak4613_priv *priv,
@@ -460,43 +834,57 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
{
struct device_node *np = dev->of_node;
char prop[32];
+ int sdti_num;
int i;
/* Input 1 - 2 */
for (i = 0; i < 2; i++) {
snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
- if (!of_get_property(np, prop, NULL))
+ if (!of_property_read_bool(np, prop))
priv->ic |= 1 << i;
}
/* Output 1 - 6 */
for (i = 0; i < 6; i++) {
snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
- if (!of_get_property(np, prop, NULL))
+ if (!of_property_read_bool(np, prop))
priv->oc |= 1 << i;
}
+
+ /*
+ * enable TDM256 test
+ *
+ * !!! FIXME !!!
+ *
+ * It should be configured by DT or other way
+ * if it was full supported.
+ * But it is using ifdef style for now for test
+ * purpose.
+ */
+#if defined(AK4613_ENABLE_TDM_TEST)
+ AK4613_CONFIG_SET(priv, MODE_TDM256);
+#endif
+
+ /*
+ * connected STDI
+ * TDM support is assuming it is probed via Audio-Graph-Card style here.
+ * Default is SDTIx1 if it was probed via Simple-Audio-Card for now.
+ */
+ sdti_num = of_graph_get_endpoint_count(np);
+ if ((sdti_num >= SDTx_MAX) || (sdti_num < 1))
+ sdti_num = 1;
+
+ AK4613_CONFIG_SDTI_set(priv, sdti_num);
}
-static int ak4613_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4613_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
- struct device_node *np = dev->of_node;
const struct regmap_config *regmap_cfg;
struct regmap *regmap;
struct ak4613_priv *priv;
- regmap_cfg = NULL;
- if (np) {
- const struct of_device_id *of_id;
-
- of_id = of_match_device(ak4613_of_match, dev);
- if (of_id)
- regmap_cfg = of_id->data;
- } else {
- regmap_cfg = (const struct regmap_config *)id->driver_data;
- }
-
+ regmap_cfg = i2c_get_match_data(i2c);
if (!regmap_cfg)
return -EINVAL;
@@ -506,8 +894,10 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
ak4613_parse_of(priv, dev);
- priv->iface = NULL;
+ priv->ctrl1 = 0;
priv->cnt = 0;
+ priv->sysclk = 0;
+ INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
mutex_init(&priv->lock);
@@ -517,24 +907,16 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ak4613,
&ak4613_dai, 1);
}
-static int ak4613_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver ak4613_i2c_driver = {
.driver = {
.name = "ak4613-codec",
- .owner = THIS_MODULE,
.of_match_table = ak4613_of_match,
},
.probe = ak4613_i2c_probe,
- .remove = ak4613_i2c_remove,
.id_table = ak4613_i2c_id,
};
diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c
new file mode 100644
index 000000000000..daf6e15b7077
--- /dev/null
+++ b/sound/soc/codecs/ak4619.c
@@ -0,0 +1,912 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ak4619.c -- Asahi Kasei ALSA SoC Audio driver
+ *
+ * Copyright (C) 2023 Renesas Electronics Corporation
+ * Khanh Le <khanh.le.xr@renesas.com>
+ *
+ * Based on ak4613.c by Kuninori Morimoto
+ * Based on da7213.c by Adam Thomson
+ * Based on ak4641.c by Harald Welte
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+/*
+ * Registers
+ */
+
+#define PWR_MGMT 0x00 /* Power Management */
+#define AU_IFF1 0x01 /* Audio I/F Format */
+#define AU_IFF2 0x02 /* Audio I/F Format (Extended) */
+#define SYS_CLK 0x03 /* System Clock Setting */
+#define MIC_AMP1 0x04 /* MIC AMP Gain 1 */
+#define MIC_AMP2 0x05 /* MIC AMP Gain 2 */
+#define LADC1 0x06 /* ADC1 Lch Digital Volume */
+#define RADC1 0x07 /* ADC1 Rch Digital Volume */
+#define LADC2 0x08 /* ADC2 Lch Digital Volume */
+#define RADC2 0x09 /* ADC2 Rch Digital Volume */
+#define ADC_DF 0x0a /* ADC Digital Filter Setting */
+#define ADC_AI 0x0b /* ADC Analog Input Setting */
+#define ADC_MHPF 0x0D /* ADC Mute & HPF Control */
+#define LDAC1 0x0E /* DAC1 Lch Digital Volume */
+#define RDAC1 0x0F /* DAC1 Rch Digital Volume */
+#define LDAC2 0x10 /* DAC2 Lch Digital Volume */
+#define RDAC2 0x11 /* DAC2 Rch Digital Volume */
+#define DAC_IS 0x12 /* DAC Input Select Setting */
+#define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */
+#define DAC_MF 0x14 /* DAC Mute & Filter Setting */
+
+/*
+ * Bit fields
+ */
+
+/* Power Management */
+#define PMAD2 BIT(5)
+#define PMAD1 BIT(4)
+#define PMDA2 BIT(2)
+#define PMDA1 BIT(1)
+#define RSTN BIT(0)
+
+/* Audio_I/F Format */
+#define DCF_STEREO_I2S (0x0 << 4)
+#define DCF_STEREO_MSB (0x5 << 4)
+#define DCF_PCM_SF (0x6 << 4)
+#define DCF_PCM_LF (0x7 << 4)
+#define DSL_32 (0x3 << 2)
+#define DCF_MASK (0x7 << 4)
+#define DSL_MASK (0x3 << 2)
+#define BCKP BIT(1)
+
+/* Audio_I/F Format (Extended) */
+#define DIDL_24 (0x0 << 2)
+#define DIDL_20 (0x1 << 2)
+#define DIDL_16 (0x2 << 2)
+#define DIDL_32 (0x3 << 2)
+#define DODL_24 (0x0 << 0)
+#define DODL_20 (0x1 << 0)
+#define DODL_16 (0x2 << 0)
+#define DIDL_MASK (0x3 << 2)
+#define DODL_MASK (0x3 << 0)
+#define SLOT BIT(4)
+
+/* System Clock Setting */
+#define FS_MASK 0x7
+
+/* MIC AMP Gain */
+#define MGNL_SHIFT 4
+#define MGNR_SHIFT 0
+#define MGN_MAX 0xB
+
+/* ADC Digital Volume */
+#define VOLAD_SHIFT 0
+#define VOLAD_MAX 0xFF
+
+/* ADC Digital Filter Setting */
+#define AD1SL_SHIFT 0
+#define AD2SL_SHIFT 4
+
+/* Analog Input Select */
+#define AD1LSEL_SHIFT 6
+#define AD1RSEL_SHIFT 4
+#define AD2LSEL_SHIFT 2
+#define AD2RSEL_SHIFT 0
+
+/* ADC Mute & HPF Control */
+#define ATSPAD_SHIFT 7
+#define AD1MUTE_SHIFT 5
+#define AD2MUTE_SHIFT 6
+#define AD1MUTE_MAX 1
+#define AD2MUTE_MAX 1
+#define AD1MUTE_EN BIT(5)
+#define AD2MUTE_EN BIT(6)
+#define AD1HPFN_SHIFT 1
+#define AD1HPFN_MAX 1
+#define AD2HPFN_SHIFT 2
+#define AD2HPFN_MAX 1
+
+/* DAC Digital Volume */
+#define VOLDA_SHIFT 0
+#define VOLDA_MAX 0xFF
+
+/* DAC Input Select Setting */
+#define DAC1SEL_SHIFT 0
+#define DAC2SEL_SHIFT 2
+
+/* DAC De-Emphasis Setting */
+#define DEM1_32000 (0x3 << 0)
+#define DEM1_44100 (0x0 << 0)
+#define DEM1_48000 (0x2 << 0)
+#define DEM1_OFF (0x1 << 0)
+#define DEM2_32000 (0x3 << 2)
+#define DEM2_44100 (0x0 << 2)
+#define DEM2_48000 (0x2 << 2)
+#define DEM2_OFF (0x1 << 2)
+#define DEM1_MASK (0x3 << 0)
+#define DEM2_MASK (0x3 << 2)
+#define DEM1_SHIFT 0
+#define DEM2_SHIFT 2
+
+/* DAC Mute & Filter Setting */
+#define DA1MUTE_SHIFT 4
+#define DA1MUTE_MAX 1
+#define DA2MUTE_SHIFT 5
+#define DA2MUTE_MAX 1
+#define DA1MUTE_EN BIT(4)
+#define DA2MUTE_EN BIT(5)
+#define ATSPDA_SHIFT 7
+#define DA1SL_SHIFT 0
+#define DA2SL_SHIFT 2
+
+/* Codec private data */
+struct ak4619_priv {
+ struct regmap *regmap;
+ struct snd_pcm_hw_constraint_list constraint;
+ int deemph_en;
+ unsigned int playback_rate;
+ unsigned int sysclk;
+};
+
+/*
+ * DAC Volume
+ *
+ * max : 0x00 : +12.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -115.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1);
+
+/*
+ * MIC Volume
+ *
+ * max : 0x0B : +27.0 dB
+ * ( 3 dB step )
+ * min: 0x00 : -6.0 dB
+ */
+static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0);
+
+/*
+ * ADC Volume
+ *
+ * max : 0x00 : +24.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -103.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
+
+/* ADC & DAC Volume Level Transition Time select */
+static const char * const ak4619_vol_trans_txt[] = {
+ "4/fs", "16/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt);
+
+/* ADC Digital Filter select */
+static const char * const ak4619_adc_digi_fil_txt[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short Delay Sharp Roll-Off Filter",
+ "Short Delay Slow Roll-Off Filter",
+ "Voice Filter"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt);
+
+/* DAC De-Emphasis Filter select */
+static const char * const ak4619_dac_de_emp_txt[] = {
+ "44.1kHz", "OFF", "48kHz", "32kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt);
+
+/* DAC Digital Filter select */
+static const char * const ak4619_dac_digi_fil_txt[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short Delay Sharp Roll-Off Filter",
+ "Short Delay Slow Roll-Off Filter"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt);
+
+/*
+ * Control functions
+ */
+
+static void ak4619_set_deemph(struct snd_soc_component *component)
+{
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ u8 dem = 0;
+
+ if (!ak4619->deemph_en)
+ return;
+
+ switch (ak4619->playback_rate) {
+ case 32000:
+ dem |= DEM1_32000 | DEM2_32000;
+ break;
+ case 44100:
+ dem |= DEM1_44100 | DEM2_44100;
+ break;
+ case 48000:
+ dem |= DEM1_48000 | DEM2_48000;
+ break;
+ default:
+ dem |= DEM1_OFF | DEM2_OFF;
+ break;
+ }
+ snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem);
+}
+
+static int ak4619_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ int deemph_en = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ switch (deemph_en) {
+ case 0:
+ case 1:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ak4619->deemph_en != deemph_en)
+ ret = 1; /* The value changed */
+
+ ak4619->deemph_en = deemph_en;
+ ak4619_set_deemph(component);
+
+ return ret;
+}
+
+static int ak4619_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = ak4619->deemph_en;
+
+ return 0;
+};
+
+/*
+ * KControls
+ */
+static const struct snd_kcontrol_new ak4619_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv),
+ SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv),
+
+ SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv),
+ SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv),
+
+ /* Volume Level Transition Time controls */
+ SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans),
+ SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans),
+
+ /* Mute controls */
+ SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1),
+ SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1),
+
+ SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1),
+ SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1),
+
+ /* Filter controls */
+ SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil),
+ SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil),
+
+ SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1),
+ SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1),
+
+ SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp),
+ SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp),
+
+ SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil),
+ SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil),
+
+ SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph),
+};
+
+/*
+ * DAPM
+ */
+
+/* Analog input mode */
+static const char * const ak4619_analog_in_txt[] = {
+ "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt);
+
+static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux =
+ SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in);
+static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux =
+ SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in);
+static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux =
+ SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in);
+static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux =
+ SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in);
+
+/* DAC source mux */
+static const char * const ak4619_dac_in_txt[] = {
+ "SDIN1", "SDIN2", "SDOUT1", "SDOUT2"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt);
+
+static const struct snd_kcontrol_new ak4619_dac_1_in_mux =
+ SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in);
+static const struct snd_kcontrol_new ak4619_dac_2_in_mux =
+ SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in);
+
+static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = {
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN5L"),
+
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+ SND_SOC_DAPM_INPUT("AIN5R"),
+
+ SND_SOC_DAPM_INPUT("MIC1L"),
+ SND_SOC_DAPM_INPUT("MIC1R"),
+ SND_SOC_DAPM_INPUT("MIC2L"),
+ SND_SOC_DAPM_INPUT("MIC2R"),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux),
+
+ /* MUXs for DAC source selection */
+ SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux),
+ SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux),
+};
+
+static const struct snd_soc_dapm_route ak4619_intercon[] = {
+ /* Dest Connecting Widget Source */
+
+ /* Output path */
+ {"AOUT1L", NULL, "DAC1"},
+ {"AOUT2L", NULL, "DAC2"},
+
+ {"AOUT1R", NULL, "DAC1"},
+ {"AOUT2R", NULL, "DAC2"},
+
+ {"DAC1", NULL, "DAC 1 Source MUX"},
+ {"DAC2", NULL, "DAC 2 Source MUX"},
+
+ {"DAC 1 Source MUX", "SDIN1", "SDIN1"},
+ {"DAC 1 Source MUX", "SDIN2", "SDIN2"},
+ {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"},
+ {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"},
+
+ {"DAC 2 Source MUX", "SDIN1", "SDIN1"},
+ {"DAC 2 Source MUX", "SDIN2", "SDIN2"},
+ {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"},
+ {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"},
+
+ /* Input path */
+ {"SDOUT1", NULL, "ADC1"},
+ {"SDOUT2", NULL, "ADC2"},
+
+ {"ADC1", NULL, "Analog Input 1 Left MUX"},
+ {"ADC1", NULL, "Analog Input 1 Right MUX"},
+
+ {"ADC2", NULL, "Analog Input 2 Left MUX"},
+ {"ADC2", NULL, "Analog Input 2 Right MUX"},
+
+ {"Analog Input 1 Left MUX", "Differential", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"},
+
+ {"Analog Input 1 Right MUX", "Differential", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"},
+
+ {"Analog Input 2 Left MUX", "Differential", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"},
+
+ {"Analog Input 2 Right MUX", "Differential", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"},
+
+ {"MIC1L", NULL, "AIN1L"},
+ {"MIC1L", NULL, "AIN2L"},
+
+ {"MIC1R", NULL, "AIN1R"},
+ {"MIC1R", NULL, "AIN2R"},
+
+ {"MIC2L", NULL, "AIN4L"},
+ {"MIC2L", NULL, "AIN5L"},
+
+ {"MIC2R", NULL, "AIN4R"},
+ {"MIC2R", NULL, "AIN5R"},
+};
+
+static const struct reg_default ak4619_reg_defaults[] = {
+ { PWR_MGMT, 0x00 },
+ { AU_IFF1, 0x0C },
+ { AU_IFF2, 0x0C },
+ { SYS_CLK, 0x00 },
+ { MIC_AMP1, 0x22 },
+ { MIC_AMP2, 0x22 },
+ { LADC1, 0x30 },
+ { RADC1, 0x30 },
+ { LADC2, 0x30 },
+ { RADC2, 0x30 },
+ { ADC_DF, 0x00 },
+ { ADC_AI, 0x00 },
+ { ADC_MHPF, 0x00 },
+ { LDAC1, 0x18 },
+ { RDAC1, 0x18 },
+ { LDAC2, 0x18 },
+ { RDAC2, 0x18 },
+ { DAC_IS, 0x04 },
+ { DAC_DEMP, 0x05 },
+ { DAC_MF, 0x0A },
+};
+
+static int ak4619_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ u8 pwr_ctrl = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ pwr_ctrl |= RSTN;
+ fallthrough;
+ case SND_SOC_BIAS_PREPARE:
+ pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2;
+ fallthrough;
+ case SND_SOC_BIAS_STANDBY:
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_component_write(component, PWR_MGMT, pwr_ctrl);
+
+ return 0;
+}
+
+static int ak4619_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ unsigned int width;
+ unsigned int rate;
+ unsigned int fs;
+ bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u8 dai_ctrl = 0;
+ u8 clk_mode = 0;
+
+ width = params_width(params);
+ switch (width) {
+ case 16:
+ dai_ctrl |= is_play ? DIDL_16 : DODL_16;
+ break;
+ case 20:
+ dai_ctrl |= is_play ? DIDL_20 : DODL_20;
+ break;
+ case 24:
+ dai_ctrl |= is_play ? DIDL_24 : DODL_24;
+ break;
+ case 32:
+ if (is_play)
+ dai_ctrl |= DIDL_32;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rate = params_rate(params);
+ if (rate)
+ fs = ak4619->sysclk / rate;
+ else
+ return -EINVAL;
+
+ switch (rate) {
+ case 8000:
+ case 11025:
+ case 12000:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ switch (fs) {
+ case 256:
+ clk_mode |= (0x0 << 0);
+ break;
+ case 384:
+ clk_mode |= (0x2 << 0);
+ break;
+ case 512:
+ clk_mode |= (0x3 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 64000:
+ case 88200:
+ case 96000:
+ if (fs == 256)
+ clk_mode |= (0x1 << 0);
+ else
+ return -EINVAL;
+ break;
+ case 176400:
+ case 192000:
+ if (fs == 128)
+ clk_mode |= (0x4 << 0);
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode);
+ snd_soc_component_update_bits(component, AU_IFF2,
+ is_play ? DIDL_MASK : DODL_MASK, dai_ctrl);
+
+ if (is_play) {
+ ak4619->playback_rate = rate;
+ ak4619_set_deemph(component);
+ }
+
+ return 0;
+}
+
+static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 dai_fmt1 = 0;
+ u8 dai_fmt2 = 0;
+
+ /* Set clock normal/inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_fmt1 |= BCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ default:
+ return -EINVAL;
+ }
+
+ /* Only Stereo modes are supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_fmt1 |= DCF_STEREO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_fmt1 |= DCF_STEREO_MSB;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
+ dai_fmt1 |= DCF_PCM_SF;
+ dai_fmt2 |= SLOT;
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
+ dai_fmt1 |= DCF_PCM_LF;
+ dai_fmt2 |= SLOT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Only slave mode is support */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 64 BICK per LRCLK is supported */
+ dai_fmt1 |= DSL_32;
+
+ snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK |
+ DSL_MASK | BCKP, dai_fmt1);
+ snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2);
+
+ return 0;
+}
+
+static int ak4619_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ak4619->sysclk = freq;
+
+ return 0;
+}
+
+static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0);
+ snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0);
+
+ return 0;
+}
+
+static void ak4619_hw_constraints(struct ak4619_priv *ak4619,
+ struct snd_pcm_runtime *runtime)
+{
+ struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint;
+ int ak4619_rate_mask = 0;
+ unsigned int fs;
+ int i;
+ static const unsigned int ak4619_sr[] = {
+ 8000,
+ 11025,
+ 12000,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ };
+
+ /*
+ * [8kHz - 48kHz] : 256fs, 384fs or 512fs
+ * [64kHz - 96kHz] : 256fs
+ * [176.4kHz, 192kHz] : 128fs
+ */
+
+ for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) {
+ fs = ak4619->sysclk / ak4619_sr[i];
+
+ switch (fs) {
+ case 512:
+ case 384:
+ case 256:
+ ak4619_rate_mask |= (1 << i);
+ break;
+ case 128:
+ switch (i) {
+ case (ARRAY_SIZE(ak4619_sr) - 1):
+ case (ARRAY_SIZE(ak4619_sr) - 2):
+ ak4619_rate_mask |= (1 << i);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ constraint->list = ak4619_sr;
+ constraint->mask = ak4619_rate_mask;
+ constraint->count = ARRAY_SIZE(ak4619_sr);
+
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint);
+};
+
+#define PLAYBACK_MODE 0
+#define CAPTURE_MODE 1
+
+static int ak4619_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ak4619_hw_constraints(ak4619, substream->runtime);
+
+ return 0;
+}
+
+static u64 ak4619_dai_formats[] = {
+ /*
+ * Select below from Sound Card, not here
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+
+ /* First Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J,
+
+ /* Second Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops ak4619_dai_ops = {
+ .startup = ak4619_dai_startup,
+ .set_sysclk = ak4619_dai_set_sysclk,
+ .set_fmt = ak4619_dai_set_fmt,
+ .hw_params = ak4619_dai_hw_params,
+ .mute_stream = ak4619_dai_mute,
+ .auto_selectable_formats = ak4619_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats),
+};
+
+static const struct snd_soc_component_driver soc_component_dev_ak4619 = {
+ .set_bias_level = ak4619_set_bias_level,
+ .controls = ak4619_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4619_snd_controls),
+ .dapm_widgets = ak4619_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets),
+ .dapm_routes = ak4619_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4619_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4619_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x14,
+ .reg_defaults = ak4619_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct of_device_id ak4619_of_match[] = {
+ { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4619_of_match);
+
+static const struct i2c_device_id ak4619_i2c_id[] = {
+ { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id);
+
+#define AK4619_RATES SNDRV_PCM_RATE_8000_192000
+
+#define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4619_dai = {
+ .name = "ak4619-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4619_RATES,
+ .formats = AK4619_DAC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4619_RATES,
+ .formats = AK4619_ADC_FORMATS,
+ },
+ .ops = &ak4619_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int ak4619_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct ak4619_priv *ak4619;
+ int ret;
+
+ ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL);
+ if (!ak4619)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ak4619);
+
+ ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg);
+ if (IS_ERR(ak4619->regmap)) {
+ ret = PTR_ERR(ak4619->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619,
+ &ak4619_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register ak4619 component: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct i2c_driver ak4619_i2c_driver = {
+ .driver = {
+ .name = "ak4619-codec",
+ .of_match_table = ak4619_of_match,
+ },
+ .probe = ak4619_i2c_probe,
+ .id_table = ak4619_i2c_id,
+};
+module_i2c_driver(ak4619_i2c_driver);
+
+MODULE_DESCRIPTION("SoC AK4619 driver");
+MODULE_AUTHOR("Khanh Le <khanh.le.xr@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index b14176f8d884..9db8cdb26d33 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ak4641.c -- AK4641 ALSA Soc Audio driver
*
@@ -5,10 +6,6 @@
* Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
*
* Based on ak4535.c by Richard Purdie
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -27,7 +24,27 @@
#include <sound/tlv.h>
#include <sound/ak4641.h>
-#include "ak4641.h"
+/* AK4641 register space */
+#define AK4641_PM1 0x00
+#define AK4641_PM2 0x01
+#define AK4641_SIG1 0x02
+#define AK4641_SIG2 0x03
+#define AK4641_MODE1 0x04
+#define AK4641_MODE2 0x05
+#define AK4641_DAC 0x06
+#define AK4641_MIC 0x07
+#define AK4641_TIMER 0x08
+#define AK4641_ALC1 0x09
+#define AK4641_ALC2 0x0a
+#define AK4641_PGA 0x0b
+#define AK4641_LATT 0x0c
+#define AK4641_RATT 0x0d
+#define AK4641_VOL 0x0e
+#define AK4641_STATUS 0x0f
+#define AK4641_EQLO 0x10
+#define AK4641_EQMID 0x11
+#define AK4641_EQHI 0x12
+#define AK4641_BTIF 0x13
/* codec private data */
struct ak4641_priv {
@@ -50,9 +67,9 @@ static const struct reg_default ak4641_reg_defaults[] = {
static const int deemph_settings[] = {44100, 0, 48000, 32000};
-static int ak4641_set_deemph(struct snd_soc_codec *codec)
+static int ak4641_set_deemph(struct snd_soc_component *component)
{
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
int i, best = 0;
for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
@@ -66,16 +83,16 @@ static int ak4641_set_deemph(struct snd_soc_codec *codec)
best = i;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+ dev_dbg(component->dev, "Set deemphasis %d\n", best);
- return snd_soc_update_bits(codec, AK4641_DAC, 0x3, best);
+ return snd_soc_component_update_bits(component, AK4641_DAC, 0x3, best);
}
static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
@@ -83,14 +100,14 @@ static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
ak4641->deemph = deemph;
- return ak4641_set_deemph(codec);
+ return ak4641_set_deemph(component);
}
static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = ak4641->deemph;
return 0;
@@ -287,8 +304,8 @@ static const struct snd_soc_dapm_route ak4641_audio_map[] = {
static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
ak4641->sysclk = freq;
return 0;
@@ -298,8 +315,8 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
int rate = params_rate(params), fs = 256;
u8 mode2;
@@ -320,16 +337,16 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
mode2 = (0x0 << 5);
break;
default:
- dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ dev_err(component->dev, "Error: unsupported fs=%d\n", fs);
return -EINVAL;
}
- snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
+ snd_soc_component_update_bits(component, AK4641_MODE2, (0x3 << 5), mode2);
/* Update de-emphasis filter for the new rate */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ak4641->playback_fs = rate;
- ak4641_set_deemph(codec);
+ ak4641_set_deemph(component);
}
return 0;
@@ -338,7 +355,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 btif;
int ret;
@@ -360,7 +377,7 @@ static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- ret = snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+ ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif);
if (ret < 0)
return ret;
@@ -370,7 +387,7 @@ static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode1 = 0;
/* interface format */
@@ -385,34 +402,35 @@ static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, AK4641_MODE1, mode1);
+ return snd_soc_component_write(component, AK4641_MODE1, mode1);
}
-static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+ return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0);
}
-static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+static int ak4641_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
- struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
+ struct ak4641_platform_data *pdata = component->dev->platform_data;
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
/* unmute */
- snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* mute */
- snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
@@ -422,16 +440,16 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
ret = regcache_sync(ak4641->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
}
- snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
- snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
+ snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0x80);
+ snd_soc_component_update_bits(component, AK4641_PM2, 0x80, 0);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
+ snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0);
if (pdata && gpio_is_valid(pdata->gpio_npdn))
gpio_set_value(pdata->gpio_npdn, 0);
if (pdata && gpio_is_valid(pdata->gpio_power))
@@ -450,15 +468,17 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
.hw_params = ak4641_i2s_hw_params,
.set_fmt = ak4641_i2s_set_dai_fmt,
- .digital_mute = ak4641_mute,
+ .mute_stream = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
.hw_params = NULL, /* rates are controlled by BT chip */
.set_fmt = ak4641_pcm_set_dai_fmt,
- .digital_mute = ak4641_mute,
+ .mute_stream = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ak4641_dai[] = {
@@ -480,7 +500,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
.formats = AK4641_FORMATS,
},
.ops = &ak4641_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "ak4641-voice",
@@ -500,11 +520,11 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
.formats = AK4641_FORMATS,
},
.ops = &ak4641_pcm_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
+static const struct snd_soc_component_driver soc_component_dev_ak4641 = {
.controls = ak4641_snd_controls,
.num_controls = ARRAY_SIZE(ak4641_snd_controls),
.dapm_widgets = ak4641_dapm_widgets,
@@ -512,7 +532,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
.dapm_routes = ak4641_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
.set_bias_level = ak4641_set_bias_level,
- .suspend_bias_off = true,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ak4641_regmap = {
@@ -525,8 +548,7 @@ static const struct regmap_config ak4641_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4641_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4641_i2c_probe(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
struct ak4641_priv *ak4641;
@@ -561,7 +583,8 @@ static int ak4641_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4641);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ak4641,
ak4641_dai, ARRAY_SIZE(ak4641_dai));
if (ret != 0)
goto err_gpio2;
@@ -582,12 +605,10 @@ err_out:
return ret;
}
-static int ak4641_i2c_remove(struct i2c_client *i2c)
+static void ak4641_i2c_remove(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
- snd_soc_unregister_codec(&i2c->dev);
-
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 0);
@@ -596,12 +617,10 @@ static int ak4641_i2c_remove(struct i2c_client *i2c)
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
-
- return 0;
}
static const struct i2c_device_id ak4641_i2c_id[] = {
- { "ak4641", 0 },
+ { "ak4641" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
diff --git a/sound/soc/codecs/ak4641.h b/sound/soc/codecs/ak4641.h
deleted file mode 100644
index 4a263248efea..000000000000
--- a/sound/soc/codecs/ak4641.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * ak4641.h -- AK4641 SoC Audio driver
- *
- * Copyright 2008 Harald Welte <laforge@gnufiish.org>
- *
- * Based on ak4535.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _AK4641_H
-#define _AK4641_H
-
-/* AK4641 register space */
-
-#define AK4641_PM1 0x00
-#define AK4641_PM2 0x01
-#define AK4641_SIG1 0x02
-#define AK4641_SIG2 0x03
-#define AK4641_MODE1 0x04
-#define AK4641_MODE2 0x05
-#define AK4641_DAC 0x06
-#define AK4641_MIC 0x07
-#define AK4641_TIMER 0x08
-#define AK4641_ALC1 0x09
-#define AK4641_ALC2 0x0a
-#define AK4641_PGA 0x0b
-#define AK4641_LATT 0x0c
-#define AK4641_RATT 0x0d
-#define AK4641_VOL 0x0e
-#define AK4641_STATUS 0x0f
-#define AK4641_EQLO 0x10
-#define AK4641_EQMID 0x11
-#define AK4641_EQHI 0x12
-#define AK4641_BTIF 0x13
-
-#define AK4641_CACHEREGNUM 0x14
-
-
-
-#define AK4641_DAI_HIFI 0
-#define AK4641_DAI_VOICE 1
-
-
-#endif
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index cda27c22812a..fe035d2fc913 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -1,17 +1,13 @@
-/*
- * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on wm8731.c by Richard Purdie
- * Based on ak4535.c by Richard Purdie
- * Based on wm8753.c by Liam Girdwood
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Based on wm8731.c by Richard Purdie
+// Based on ak4535.c by Richard Purdie
+// Based on wm8753.c by Liam Girdwood
/* ** CAUTION **
*
@@ -28,7 +24,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -178,19 +174,19 @@ static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = {
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
case SND_SOC_DAPM_PRE_PMU:
/* Power save mode ON */
- snd_soc_update_bits(codec, SG_SL2, LOPS, LOPS);
+ snd_soc_component_update_bits(component, SG_SL2, LOPS, LOPS);
break;
case SND_SOC_DAPM_POST_PMU:
case SND_SOC_DAPM_POST_PMD:
/* Power save mode OFF */
- mdelay(300);
- snd_soc_update_bits(codec, SG_SL2, LOPS, 0);
+ msleep(300);
+ snd_soc_component_update_bits(component, SG_SL2, LOPS, 0);
break;
}
@@ -282,7 +278,7 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (is_play) {
/*
@@ -295,8 +291,8 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
*/
- snd_soc_write(codec, L_IVC, 0x91); /* volume */
- snd_soc_write(codec, R_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, L_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, R_IVC, 0x91); /* volume */
} else {
/*
* start stereo input
@@ -311,11 +307,11 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
- snd_soc_update_bits(codec, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0);
- snd_soc_write(codec, TIMER, ZTM(0x3) | WTM(0x3));
- snd_soc_write(codec, ALC_CTL1, ALC | LMTH0);
- snd_soc_update_bits(codec, PW_MGMT1, PMADL, PMADL);
- snd_soc_update_bits(codec, PW_MGMT3, PMADR, PMADR);
+ snd_soc_component_update_bits(component, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0);
+ snd_soc_component_write(component, TIMER, ZTM(0x3) | WTM(0x3));
+ snd_soc_component_write(component, ALC_CTL1, ALC | LMTH0);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMADL, PMADL);
+ snd_soc_component_update_bits(component, PW_MGMT3, PMADR, PMADR);
}
return 0;
@@ -325,22 +321,22 @@ static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (is_play) {
} else {
/* stop stereo input */
- snd_soc_update_bits(codec, PW_MGMT1, PMADL, 0);
- snd_soc_update_bits(codec, PW_MGMT3, PMADR, 0);
- snd_soc_update_bits(codec, ALC_CTL1, ALC, 0);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMADL, 0);
+ snd_soc_component_update_bits(component, PW_MGMT3, PMADR, 0);
+ snd_soc_component_update_bits(component, ALC_CTL1, ALC, 0);
}
}
static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
u8 pll;
int extended_freq = 0;
@@ -382,33 +378,33 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
if (extended_freq && !priv->drvdata->extended_frequencies)
return -EINVAL;
- snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
+ snd_soc_component_update_bits(component, MD_CTL1, PLL_MASK, pll);
return 0;
}
static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 data;
u8 bcko;
data = MCKO | PMPLL; /* use MCKO */
bcko = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set clocking for audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
data |= MS;
bcko = BCKO_64;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PW_MGMT2, MS | MCKO | PMPLL, data);
- snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
+ snd_soc_component_update_bits(component, PW_MGMT2, MS | MCKO | PMPLL, data);
+ snd_soc_component_update_bits(component, MD_CTL1, BCKO_MASK, bcko);
/* format type */
data = 0;
@@ -425,15 +421,15 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+ snd_soc_component_update_bits(component, MD_CTL1, DIF_MASK, data);
return 0;
}
-static int ak4642_set_mcko(struct snd_soc_codec *codec,
+static int ak4642_set_mcko(struct snd_soc_component *component,
u32 frequency)
{
- u32 fs_list[] = {
+ static const u32 fs_list[] = {
[0] = 8000,
[1] = 12000,
[2] = 16000,
@@ -447,7 +443,7 @@ static int ak4642_set_mcko(struct snd_soc_codec *codec,
[14] = 29400,
[15] = 44100,
};
- u32 ps_list[] = {
+ static const u32 ps_list[] = {
[0] = 256,
[1] = 128,
[2] = 64,
@@ -458,7 +454,7 @@ static int ak4642_set_mcko(struct snd_soc_codec *codec,
for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
if (frequency == ps_list[ps] * fs_list[fs]) {
- snd_soc_write(codec, MD_CTL2,
+ snd_soc_component_write(component, MD_CTL2,
PSs(ps) | FSs(fs));
return 0;
}
@@ -472,25 +468,25 @@ 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;
- struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
u32 rate = clk_get_rate(priv->mcko);
if (!rate)
rate = params_rate(params) * 256;
- return ak4642_set_mcko(codec, rate);
+ return ak4642_set_mcko(component, rate);
}
-static int ak4642_set_bias_level(struct snd_soc_codec *codec,
+static int ak4642_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, PW_MGMT1, 0x00);
+ snd_soc_component_write(component, PW_MGMT1, 0x00);
break;
default:
- snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMVCM, PMVCM);
break;
}
@@ -520,30 +516,39 @@ static struct snd_soc_dai_driver ak4642_dai = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.ops = &ak4642_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+ regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
- regcache_sync(regmap);
return 0;
}
-static int ak4642_probe(struct snd_soc_codec *codec)
+static int ak4642_resume(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+ return 0;
+}
+static int ak4642_probe(struct snd_soc_component *component)
{
- struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
if (priv->mcko)
- ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+ ak4642_set_mcko(component, clk_get_rate(priv->mcko));
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+static const struct snd_soc_component_driver soc_component_dev_ak4642 = {
.probe = ak4642_probe,
+ .suspend = ak4642_suspend,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
@@ -552,6 +557,8 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
.dapm_routes = ak4642_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
};
static const struct regmap_config ak4642_regmap = {
@@ -560,6 +567,7 @@ static const struct regmap_config ak4642_regmap = {
.max_register = FIL1_3,
.reg_defaults = ak4642_reg,
.num_reg_defaults = NUM_AK4642_REG_DEFAULTS,
+ .cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config ak4643_regmap = {
@@ -568,6 +576,7 @@ static const struct regmap_config ak4643_regmap = {
.max_register = SPK_MS,
.reg_defaults = ak4643_reg,
.num_reg_defaults = ARRAY_SIZE(ak4643_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config ak4648_regmap = {
@@ -576,6 +585,7 @@ static const struct regmap_config ak4648_regmap = {
.max_register = EQ_FBEQE,
.reg_defaults = ak4648_reg,
.num_reg_defaults = ARRAY_SIZE(ak4648_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct ak4642_drvdata ak4642_drvdata = {
@@ -608,9 +618,7 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
of_property_read_string(np, "clock-output-names", &clk_name);
- clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
- (parent_clk_name) ? 0 : CLK_IS_ROOT,
- rate);
+ clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate);
if (!IS_ERR(clk))
of_clk_add_provider(np, of_clk_src_simple_get, clk);
@@ -620,35 +628,23 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
#define ak4642_of_parse_mcko(d) 0
#endif
-static const struct of_device_id ak4642_of_match[];
-static int ak4642_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4642_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
- struct device_node *np = dev->of_node;
- const struct ak4642_drvdata *drvdata = NULL;
+ const struct ak4642_drvdata *drvdata;
struct regmap *regmap;
struct ak4642_priv *priv;
struct clk *mcko = NULL;
- if (np) {
- const struct of_device_id *of_id;
-
+ if (dev_fwnode(dev)) {
mcko = ak4642_of_parse_mcko(dev);
if (IS_ERR(mcko))
mcko = NULL;
-
- of_id = of_match_device(ak4642_of_match, dev);
- if (of_id)
- drvdata = of_id->data;
- } else {
- drvdata = (const struct ak4642_drvdata *)id->driver_data;
}
- if (!drvdata) {
- dev_err(dev, "Unknown device type\n");
- return -EINVAL;
- }
+ drvdata = i2c_get_match_data(i2c);
+ if (!drvdata)
+ return dev_err_probe(dev, -EINVAL, "Unknown device type\n");
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -663,21 +659,15 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(dev,
- &soc_codec_dev_ak4642, &ak4642_dai, 1);
-}
-
-static int ak4642_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_ak4642, &ak4642_dai, 1);
}
static const struct of_device_id ak4642_of_match[] = {
{ .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata},
{ .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata},
{ .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata},
- {},
+ {}
};
MODULE_DEVICE_TABLE(of, ak4642_of_match);
@@ -685,7 +675,7 @@ 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);
@@ -695,7 +685,6 @@ static struct i2c_driver ak4642_i2c_driver = {
.of_match_table = ak4642_of_match,
},
.probe = ak4642_i2c_probe,
- .remove = ak4642_i2c_remove,
.id_table = ak4642_i2c_id,
};
@@ -703,4 +692,4 @@ module_i2c_driver(ak4642_i2c_driver);
MODULE_DESCRIPTION("Soc AK4642 driver");
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index c73a9f6914b6..d545aa2e0a39 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ak4671.c -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
@@ -163,15 +158,15 @@ static const struct snd_kcontrol_new ak4671_snd_controls[] = {
static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, AK4671_MUTEN);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, 0);
break;
}
@@ -427,10 +422,10 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 fs;
- fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ fs = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0);
fs &= ~AK4671_FS;
switch (params_rate(params)) {
@@ -465,7 +460,7 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, fs);
return 0;
}
@@ -473,10 +468,10 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 pll;
- pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ pll = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0);
pll &= ~AK4671_PLL;
switch (freq) {
@@ -511,25 +506,25 @@ static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
return -EINVAL;
}
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, pll);
return 0;
}
static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 mode;
u8 format;
/* set master/slave audio interface */
- mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+ mode = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT1);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
mode |= AK4671_M_S;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
mode &= ~(AK4671_M_S);
break;
default:
@@ -537,7 +532,7 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* interface format */
- format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+ format = snd_soc_component_read(component, AK4671_FORMAT_SELECT);
format &= ~AK4671_DIF;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -557,24 +552,24 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* set mode and format */
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
- snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT1, mode);
+ snd_soc_component_write(component, AK4671_FORMAT_SELECT, format);
return 0;
}
-static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+static int ak4671_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+ snd_soc_component_update_bits(component, AK4671_AD_DA_POWER_MANAGEMENT,
AK4671_PMVCM, AK4671_PMVCM);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+ snd_soc_component_write(component, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
break;
}
return 0;
@@ -610,14 +605,17 @@ static struct snd_soc_dai_driver ak4671_dai = {
.ops = &ak4671_dai_ops,
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
- .set_bias_level = ak4671_set_bias_level,
- .controls = ak4671_snd_controls,
- .num_controls = ARRAY_SIZE(ak4671_snd_controls),
- .dapm_widgets = ak4671_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
- .dapm_routes = ak4671_intercon,
- .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
+static const struct snd_soc_component_driver soc_component_dev_ak4671 = {
+ .set_bias_level = ak4671_set_bias_level,
+ .controls = ak4671_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4671_snd_controls),
+ .dapm_widgets = ak4671_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
+ .dapm_routes = ak4671_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ak4671_regmap = {
@@ -630,8 +628,7 @@ static const struct regmap_config ak4671_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4671_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ak4671_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -643,19 +640,13 @@ static int ak4671_i2c_probe(struct i2c_client *client,
return ret;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_ak4671, &ak4671_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_ak4671, &ak4671_dai, 1);
return ret;
}
-static int ak4671_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ak4671_i2c_id[] = {
- { "ak4671", 0 },
+ { "ak4671" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
@@ -665,7 +656,6 @@ static struct i2c_driver ak4671_i2c_driver = {
.name = "ak4671-codec",
},
.probe = ak4671_i2c_probe,
- .remove = ak4671_i2c_remove,
.id_table = ak4671_i2c_id,
};
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
index 394a34d3f50a..3dac0a1ae772 100644
--- a/sound/soc/codecs/ak4671.h
+++ b/sound/soc/codecs/ak4671.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ak4671.h -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _AK4671_H
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index afa95360826d..6525d50b7ab2 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -1,20 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC driver for
* Asahi Kasei AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC
*
* (c) 2013 Daniel Mack <zonque@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -24,7 +22,7 @@ static const char * const supply_names[] = {
};
struct ak5386_priv {
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
@@ -38,30 +36,29 @@ static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
{ "Capture", NULL, "AINR" },
};
-static int ak5386_soc_probe(struct snd_soc_codec *codec)
+static int ak5386_soc_probe(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
-static int ak5386_soc_remove(struct snd_soc_codec *codec)
+static void ak5386_soc_remove(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
- return 0;
}
#ifdef CONFIG_PM
-static int ak5386_soc_suspend(struct snd_soc_codec *codec)
+static int ak5386_soc_suspend(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
return 0;
}
-static int ak5386_soc_resume(struct snd_soc_codec *codec)
+static int ak5386_soc_resume(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
#else
@@ -69,26 +66,29 @@ static int ak5386_soc_resume(struct snd_soc_codec *codec)
#define ak5386_soc_resume NULL
#endif /* CONFIG_PM */
-static struct snd_soc_codec_driver soc_codec_ak5386 = {
- .probe = ak5386_soc_probe,
- .remove = ak5386_soc_remove,
- .suspend = ak5386_soc_suspend,
- .resume = ak5386_soc_resume,
- .dapm_widgets = ak5386_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
- .dapm_routes = ak5386_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+static const struct snd_soc_component_driver soc_component_ak5386 = {
+ .probe = ak5386_soc_probe,
+ .remove = ak5386_soc_remove,
+ .suspend = ak5386_soc_suspend,
+ .resume = ak5386_soc_resume,
+ .dapm_widgets = ak5386_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
+ .dapm_routes = ak5386_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
format &= SND_SOC_DAIFMT_FORMAT_MASK;
if (format != SND_SOC_DAIFMT_LEFT_J &&
format != SND_SOC_DAIFMT_I2S) {
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -99,8 +99,8 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
/*
* From the datasheet:
@@ -112,8 +112,7 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
* the AK5386 in power-down mode (PDN pin = “L”).
*/
- if (gpio_is_valid(priv->reset_gpio))
- gpio_set_value(priv->reset_gpio, 1);
+ gpiod_set_value(priv->reset_gpio, 1);
return 0;
}
@@ -121,11 +120,10 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
static int ak5386_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(priv->reset_gpio))
- gpio_set_value(priv->reset_gpio, 0);
+ gpiod_set_value(priv->reset_gpio, 0);
return 0;
}
@@ -169,7 +167,6 @@ static int ak5386_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->reset_gpio = -EINVAL;
dev_set_drvdata(dev, priv);
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
@@ -180,29 +177,19 @@ static int ak5386_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- if (of_match_device(of_match_ptr(ak5386_dt_ids), dev))
- priv->reset_gpio = of_get_named_gpio(dev->of_node,
- "reset-gpio", 0);
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get AK5386 reset GPIO\n");
- if (gpio_is_valid(priv->reset_gpio))
- if (devm_gpio_request_one(dev, priv->reset_gpio,
- GPIOF_OUT_INIT_LOW,
- "AK5386 Reset"))
- priv->reset_gpio = -EINVAL;
+ gpiod_set_consumer_name(priv->reset_gpio, "AK5386 Reset");
- return snd_soc_register_codec(dev, &soc_codec_ak5386,
+ return devm_snd_soc_register_component(dev, &soc_component_ak5386,
&ak5386_dai, 1);
}
-static int ak5386_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver ak5386_driver = {
.probe = ak5386_probe,
- .remove = ak5386_remove,
.driver = {
.name = "ak5386",
.of_match_table = of_match_ptr(ak5386_dt_ids),
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
new file mode 100644
index 000000000000..683f3e472f50
--- /dev/null
+++ b/sound/soc/codecs/ak5558.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Audio driver for AK5558 ADC
+//
+// Copyright (C) 2015 Asahi Kasei Microdevices Corporation
+// Copyright 2018 NXP
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ak5558.h"
+
+enum ak555x_type {
+ AK5558,
+ AK5552,
+};
+
+#define AK5558_NUM_SUPPLIES 2
+static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = {
+ "DVDD",
+ "AVDD",
+};
+
+/* AK5558 Codec Private Data */
+struct ak5558_priv {
+ struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES];
+ struct snd_soc_component component;
+ struct regmap *regmap;
+ struct i2c_client *i2c;
+ struct gpio_desc *reset_gpiod; /* Reset & Power down GPIO */
+ int slots;
+ int slot_width;
+};
+
+/* ak5558 register cache & default register settings */
+static const struct reg_default ak5558_reg[] = {
+ { 0x0, 0xFF }, /* 0x00 AK5558_00_POWER_MANAGEMENT1 */
+ { 0x1, 0x01 }, /* 0x01 AK5558_01_POWER_MANAGEMENT2 */
+ { 0x2, 0x01 }, /* 0x02 AK5558_02_CONTROL1 */
+ { 0x3, 0x00 }, /* 0x03 AK5558_03_CONTROL2 */
+ { 0x4, 0x00 }, /* 0x04 AK5558_04_CONTROL3 */
+ { 0x5, 0x00 } /* 0x05 AK5558_05_DSD */
+};
+
+static const char * const mono_texts[] = {
+ "8 Slot", "2 Slot", "4 Slot", "1 Slot",
+};
+
+static const struct soc_enum ak5558_mono_enum[] = {
+ SOC_ENUM_SINGLE(AK5558_01_POWER_MANAGEMENT2, 1,
+ ARRAY_SIZE(mono_texts), mono_texts),
+};
+
+static const char * const mono_5552_texts[] = {
+ "2 Slot", "1 Slot (Fixed)", "2 Slot", "1 Slot (Optimal)",
+};
+
+static const struct soc_enum ak5552_mono_enum[] = {
+ SOC_ENUM_SINGLE(AK5558_01_POWER_MANAGEMENT2, 1,
+ ARRAY_SIZE(mono_5552_texts), mono_5552_texts),
+};
+
+static const char * const digfil_texts[] = {
+ "Sharp Roll-Off", "Slow Roll-Off",
+ "Short Delay Sharp Roll-Off", "Short Delay Slow Roll-Off",
+};
+
+static const struct soc_enum ak5558_adcset_enum[] = {
+ SOC_ENUM_SINGLE(AK5558_04_CONTROL3, 0,
+ ARRAY_SIZE(digfil_texts), digfil_texts),
+};
+
+static const struct snd_kcontrol_new ak5558_snd_controls[] = {
+ SOC_ENUM("Monaural Mode", ak5558_mono_enum[0]),
+ SOC_ENUM("Digital Filter", ak5558_adcset_enum[0]),
+};
+
+static const struct snd_kcontrol_new ak5552_snd_controls[] = {
+ SOC_ENUM("Monaural Mode", ak5552_mono_enum[0]),
+ SOC_ENUM("Digital Filter", ak5558_adcset_enum[0]),
+};
+
+static const struct snd_soc_dapm_widget ak5558_dapm_widgets[] = {
+ /* Analog Input */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+ SND_SOC_DAPM_INPUT("AIN5"),
+ SND_SOC_DAPM_INPUT("AIN6"),
+ SND_SOC_DAPM_INPUT("AIN7"),
+ SND_SOC_DAPM_INPUT("AIN8"),
+
+ SND_SOC_DAPM_ADC("ADC Ch1", NULL, AK5558_00_POWER_MANAGEMENT1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Ch2", NULL, AK5558_00_POWER_MANAGEMENT1, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Ch3", NULL, AK5558_00_POWER_MANAGEMENT1, 2, 0),
+ SND_SOC_DAPM_ADC("ADC Ch4", NULL, AK5558_00_POWER_MANAGEMENT1, 3, 0),
+ SND_SOC_DAPM_ADC("ADC Ch5", NULL, AK5558_00_POWER_MANAGEMENT1, 4, 0),
+ SND_SOC_DAPM_ADC("ADC Ch6", NULL, AK5558_00_POWER_MANAGEMENT1, 5, 0),
+ SND_SOC_DAPM_ADC("ADC Ch7", NULL, AK5558_00_POWER_MANAGEMENT1, 6, 0),
+ SND_SOC_DAPM_ADC("ADC Ch8", NULL, AK5558_00_POWER_MANAGEMENT1, 7, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget ak5552_dapm_widgets[] = {
+ /* Analog Input */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+
+ SND_SOC_DAPM_ADC("ADC Ch1", NULL, AK5558_00_POWER_MANAGEMENT1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Ch2", NULL, AK5558_00_POWER_MANAGEMENT1, 1, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route ak5558_intercon[] = {
+ {"ADC Ch1", NULL, "AIN1"},
+ {"SDTO", NULL, "ADC Ch1"},
+
+ {"ADC Ch2", NULL, "AIN2"},
+ {"SDTO", NULL, "ADC Ch2"},
+
+ {"ADC Ch3", NULL, "AIN3"},
+ {"SDTO", NULL, "ADC Ch3"},
+
+ {"ADC Ch4", NULL, "AIN4"},
+ {"SDTO", NULL, "ADC Ch4"},
+
+ {"ADC Ch5", NULL, "AIN5"},
+ {"SDTO", NULL, "ADC Ch5"},
+
+ {"ADC Ch6", NULL, "AIN6"},
+ {"SDTO", NULL, "ADC Ch6"},
+
+ {"ADC Ch7", NULL, "AIN7"},
+ {"SDTO", NULL, "ADC Ch7"},
+
+ {"ADC Ch8", NULL, "AIN8"},
+ {"SDTO", NULL, "ADC Ch8"},
+};
+
+static const struct snd_soc_dapm_route ak5552_intercon[] = {
+ {"ADC Ch1", NULL, "AIN1"},
+ {"SDTO", NULL, "ADC Ch1"},
+
+ {"ADC Ch2", NULL, "AIN2"},
+ {"SDTO", NULL, "ADC Ch2"},
+};
+
+static int ak5558_set_mcki(struct snd_soc_component *component)
+{
+ return snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_CKS,
+ AK5558_CKS_AUTO);
+}
+
+static int ak5558_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+ u8 bits;
+ int pcm_width = max(params_physical_width(params), ak5558->slot_width);
+
+ switch (pcm_width) {
+ case 16:
+ bits = AK5558_DIF_24BIT_MODE;
+ break;
+ case 32:
+ bits = AK5558_DIF_32BIT_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_BITS, bits);
+
+ return 0;
+}
+
+static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 format;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
+ default:
+ dev_err(dai->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = AK5558_DIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = AK5558_DIF_MSB_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = AK5558_DIF_MSB_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_DIF, format);
+
+ return 0;
+}
+
+static int ak5558_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+ int tdm_mode;
+
+ ak5558->slots = slots;
+ ak5558->slot_width = slot_width;
+
+ switch (slots * slot_width) {
+ case 128:
+ tdm_mode = AK5558_MODE_TDM128;
+ break;
+ case 256:
+ tdm_mode = AK5558_MODE_TDM256;
+ break;
+ case 512:
+ tdm_mode = AK5558_MODE_TDM512;
+ break;
+ default:
+ tdm_mode = AK5558_MODE_NORMAL;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, AK5558_03_CONTROL2, AK5558_MODE_BITS,
+ tdm_mode);
+ return 0;
+}
+
+#define AK5558_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const unsigned int ak5558_rates[] = {
+ 8000, 11025, 16000, 22050,
+ 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800,
+ 384000, 705600, 768000, 1411200,
+ 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list ak5558_rate_constraints = {
+ .count = ARRAY_SIZE(ak5558_rates),
+ .list = ak5558_rates,
+};
+
+static int ak5558_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak5558_rate_constraints);
+}
+
+static const struct snd_soc_dai_ops ak5558_dai_ops = {
+ .startup = ak5558_startup,
+ .hw_params = ak5558_hw_params,
+
+ .set_fmt = ak5558_set_dai_fmt,
+ .set_tdm_slot = ak5558_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ak5558_dai = {
+ .name = "ak5558-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK5558_FORMATS,
+ },
+ .ops = &ak5558_dai_ops,
+};
+
+static struct snd_soc_dai_driver ak5552_dai = {
+ .name = "ak5552-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK5558_FORMATS,
+ },
+ .ops = &ak5558_dai_ops,
+};
+
+static void ak5558_reset(struct ak5558_priv *ak5558, bool active)
+{
+ if (!ak5558->reset_gpiod)
+ return;
+
+ gpiod_set_value_cansleep(ak5558->reset_gpiod, active);
+ usleep_range(1000, 2000);
+}
+
+static int ak5558_probe(struct snd_soc_component *component)
+{
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+
+ ak5558_reset(ak5558, false);
+ return ak5558_set_mcki(component);
+}
+
+static void ak5558_remove(struct snd_soc_component *component)
+{
+ struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component);
+
+ ak5558_reset(ak5558, true);
+}
+
+static int ak5558_runtime_suspend(struct device *dev)
+{
+ struct ak5558_priv *ak5558 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak5558->regmap, true);
+ ak5558_reset(ak5558, true);
+
+ regulator_bulk_disable(ARRAY_SIZE(ak5558->supplies),
+ ak5558->supplies);
+ return 0;
+}
+
+static int ak5558_runtime_resume(struct device *dev)
+{
+ struct ak5558_priv *ak5558 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak5558->supplies),
+ ak5558->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ak5558_reset(ak5558, true);
+ ak5558_reset(ak5558, false);
+
+ regcache_cache_only(ak5558->regmap, false);
+ regcache_mark_dirty(ak5558->regmap);
+
+ return regcache_sync(ak5558->regmap);
+}
+
+static const struct dev_pm_ops ak5558_pm = {
+ RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_ak5558 = {
+ .probe = ak5558_probe,
+ .remove = ak5558_remove,
+ .controls = ak5558_snd_controls,
+ .num_controls = ARRAY_SIZE(ak5558_snd_controls),
+ .dapm_widgets = ak5558_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5558_dapm_widgets),
+ .dapm_routes = ak5558_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak5558_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_ak5552 = {
+ .probe = ak5558_probe,
+ .remove = ak5558_remove,
+ .controls = ak5552_snd_controls,
+ .num_controls = ARRAY_SIZE(ak5552_snd_controls),
+ .dapm_widgets = ak5552_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5552_dapm_widgets),
+ .dapm_routes = ak5552_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak5552_intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak5558_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK5558_05_DSD,
+ .reg_defaults = ak5558_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak5558_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak5558_i2c_probe(struct i2c_client *i2c)
+{
+ struct ak5558_priv *ak5558;
+ int ret = 0;
+ int dev_id;
+ int i;
+
+ ak5558 = devm_kzalloc(&i2c->dev, sizeof(*ak5558), GFP_KERNEL);
+ if (!ak5558)
+ return -ENOMEM;
+
+ ak5558->regmap = devm_regmap_init_i2c(i2c, &ak5558_regmap);
+ if (IS_ERR(ak5558->regmap))
+ return PTR_ERR(ak5558->regmap);
+
+ i2c_set_clientdata(i2c, ak5558);
+ ak5558->i2c = i2c;
+
+ ak5558->reset_gpiod = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ak5558->reset_gpiod))
+ return PTR_ERR(ak5558->reset_gpiod);
+
+ for (i = 0; i < ARRAY_SIZE(ak5558->supplies); i++)
+ ak5558->supplies[i].supply = ak5558_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(ak5558->supplies),
+ ak5558->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ dev_id = (uintptr_t)of_device_get_match_data(&i2c->dev);
+ switch (dev_id) {
+ case AK5552:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_ak5552,
+ &ak5552_dai, 1);
+ break;
+ case AK5558:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_ak5558,
+ &ak5558_dai, 1);
+ break;
+ default:
+ dev_err(&i2c->dev, "unexpected device type\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&i2c->dev);
+ regcache_cache_only(ak5558->regmap, true);
+
+ return 0;
+}
+
+static void ak5558_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct of_device_id ak5558_i2c_dt_ids[] __maybe_unused = {
+ { .compatible = "asahi-kasei,ak5558", .data = (void *) AK5558 },
+ { .compatible = "asahi-kasei,ak5552", .data = (void *) AK5552 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak5558_i2c_dt_ids);
+
+static struct i2c_driver ak5558_i2c_driver = {
+ .driver = {
+ .name = "ak5558",
+ .of_match_table = of_match_ptr(ak5558_i2c_dt_ids),
+ .pm = pm_ptr(&ak5558_pm),
+ },
+ .probe = ak5558_i2c_probe,
+ .remove = ak5558_i2c_remove,
+};
+
+module_i2c_driver(ak5558_i2c_driver);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+MODULE_DESCRIPTION("ASoC AK5558 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak5558.h b/sound/soc/codecs/ak5558.h
new file mode 100644
index 000000000000..61059086f161
--- /dev/null
+++ b/sound/soc/codecs/ak5558.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Audio driver header for AK5558
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright 2018 NXP
+ */
+
+#ifndef _AK5558_H
+#define _AK5558_H
+
+#define AK5558_00_POWER_MANAGEMENT1 0x00
+#define AK5558_01_POWER_MANAGEMENT2 0x01
+#define AK5558_02_CONTROL1 0x02
+#define AK5558_03_CONTROL2 0x03
+#define AK5558_04_CONTROL3 0x04
+#define AK5558_05_DSD 0x05
+
+/* AK5558_02_CONTROL1 fields */
+#define AK5558_DIF GENMASK(1, 1)
+#define AK5558_DIF_MSB_MODE (0 << 1)
+#define AK5558_DIF_I2S_MODE (1 << 1)
+
+#define AK5558_BITS GENMASK(2, 2)
+#define AK5558_DIF_24BIT_MODE (0 << 2)
+#define AK5558_DIF_32BIT_MODE (1 << 2)
+
+#define AK5558_CKS GENMASK(6, 3)
+#define AK5558_CKS_128FS_192KHZ (0 << 3)
+#define AK5558_CKS_192FS_192KHZ (1 << 3)
+#define AK5558_CKS_256FS_48KHZ (2 << 3)
+#define AK5558_CKS_256FS_96KHZ (3 << 3)
+#define AK5558_CKS_384FS_96KHZ (4 << 3)
+#define AK5558_CKS_384FS_48KHZ (5 << 3)
+#define AK5558_CKS_512FS_48KHZ (6 << 3)
+#define AK5558_CKS_768FS_48KHZ (7 << 3)
+#define AK5558_CKS_64FS_384KHZ (8 << 3)
+#define AK5558_CKS_32FS_768KHZ (9 << 3)
+#define AK5558_CKS_96FS_384KHZ (10 << 3)
+#define AK5558_CKS_48FS_768KHZ (11 << 3)
+#define AK5558_CKS_64FS_768KHZ (12 << 3)
+#define AK5558_CKS_1024FS_16KHZ (13 << 3)
+#define AK5558_CKS_AUTO (15 << 3)
+
+/* AK5558_03_CONTROL2 fields */
+#define AK5558_MODE_BITS GENMASK(6, 5)
+#define AK5558_MODE_NORMAL (0 << 5)
+#define AK5558_MODE_TDM128 (1 << 5)
+#define AK5558_MODE_TDM256 (2 << 5)
+#define AK5558_MODE_TDM512 (3 << 5)
+
+#endif
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index d2e3a3ef7499..a9946dcdc9f6 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* alc5623.c -- alc562[123] ALSA Soc Audio driver
*
@@ -6,13 +7,7 @@
*
* Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
- *
* Based on WM8753.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
@@ -47,28 +42,28 @@ struct alc5623_priv {
unsigned int jack_det_ctrl;
};
-static inline int alc5623_reset(struct snd_soc_codec *codec)
+static inline int alc5623_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, ALC5623_RESET, 0);
+ return snd_soc_component_write(component, ALC5623_RESET, 0);
}
static int amp_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
/* to power-on/off class-d amp generators/speaker */
/* need to write to 'index-46h' register : */
/* so write index num (here 0x46) to reg 0x6a */
/* and then 0xffff/0 to reg 0x6c */
- snd_soc_write(codec, ALC5623_HID_CTRL_INDEX, 0x46);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0);
break;
}
@@ -526,7 +521,7 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
int i;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int gbl_clk = 0, pll_div = 0;
u16 reg;
@@ -534,12 +529,12 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -ENODEV;
/* Disable PLL power */
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2,
ALC5623_PWR_ADD2_PLL,
0);
/* pll is not used in slave mode */
- reg = snd_soc_read(codec, ALC5623_DAI_CONTROL);
+ reg = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
if (reg & ALC5623_DAI_SDP_SLAVE_MODE)
return 0;
@@ -575,13 +570,13 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (!pll_div)
return -EINVAL;
- snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
- snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div);
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+ snd_soc_component_write(component, ALC5623_PLL_CTRL, pll_div);
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2,
ALC5623_PWR_ADD2_PLL,
ALC5623_PWR_ADD2_PLL);
gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL;
- snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+ snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
return 0;
}
@@ -604,9 +599,9 @@ static const struct _coeff_div coeff_div[] = {
{384*1, 0x0c6b},
};
-static int get_coeff(struct snd_soc_codec *codec, int rate)
+static int get_coeff(struct snd_soc_component *component, int rate)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -622,8 +617,8 @@ static int get_coeff(struct snd_soc_codec *codec, int rate)
static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 8192000:
@@ -643,15 +638,15 @@ static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = ALC5623_DAI_SDP_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5623_DAI_SDP_SLAVE_MODE;
break;
default:
@@ -695,18 +690,18 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ return snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface);
}
static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
int coeff, rate;
u16 iface;
- iface = snd_soc_read(codec, ALC5623_DAI_CONTROL);
+ iface = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
iface &= ~ALC5623_DAI_I2S_DL_MASK;
/* bit size */
@@ -728,30 +723,30 @@ static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface);
rate = params_rate(params);
- coeff = get_coeff(codec, rate);
+ coeff = get_coeff(component, rate);
if (coeff < 0)
return -EINVAL;
coeff = coeff_div[coeff].regvalue;
- dev_dbg(codec->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
+ dev_dbg(component->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
__func__, alc5623->sysclk, rate, coeff);
- snd_soc_write(codec, ALC5623_STEREO_AD_DA_CLK_CTRL, coeff);
+ snd_soc_component_write(component, ALC5623_STEREO_AD_DA_CLK_CTRL, coeff);
return 0;
}
-static int alc5623_mute(struct snd_soc_dai *dai, int mute)
+static int alc5623_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 hp_mute = ALC5623_MISC_M_DAC_L_INPUT | ALC5623_MISC_M_DAC_R_INPUT;
- u16 mute_reg = snd_soc_read(codec, ALC5623_MISC_CTRL) & ~hp_mute;
+ u16 mute_reg = snd_soc_component_read(component, ALC5623_MISC_CTRL) & ~hp_mute;
if (mute)
mute_reg |= hp_mute;
- return snd_soc_write(codec, ALC5623_MISC_CTRL, mute_reg);
+ return snd_soc_component_write(component, ALC5623_MISC_CTRL, mute_reg);
}
#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
@@ -769,60 +764,60 @@ static int alc5623_mute(struct snd_soc_dai *dai, int mute)
(ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
| ALC5623_PWR_ADD1_HP_OUT_AMP)
-static void enable_power_depop(struct snd_soc_codec *codec)
+static void enable_power_depop(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_PWR_ADD1_SOFTGEN_EN,
ALC5623_PWR_ADD1_SOFTGEN_EN);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
- snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ snd_soc_component_update_bits(component, ALC5623_MISC_CTRL,
ALC5623_MISC_HP_DEPOP_MODE2_EN,
ALC5623_MISC_HP_DEPOP_MODE2_EN);
msleep(500);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN);
/* avoid writing '1' into 5622 reserved bits */
if (alc5623->id == 0x22)
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_ADD1_POWER_EN_5622);
else
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_ADD1_POWER_EN);
/* disable HP Depop2 */
- snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ snd_soc_component_update_bits(component, ALC5623_MISC_CTRL,
ALC5623_MISC_HP_DEPOP_MODE2_EN,
0);
}
-static int alc5623_set_bias_level(struct snd_soc_codec *codec,
+static int alc5623_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
- enable_power_depop(codec);
+ enable_power_depop(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2,
ALC5623_PWR_ADD2_VREF);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3,
ALC5623_PWR_ADD3_MAIN_BIAS);
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, 0);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, 0);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, 0);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, 0);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, 0);
break;
}
return 0;
@@ -834,10 +829,11 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops alc5623_dai_ops = {
.hw_params = alc5623_pcm_hw_params,
- .digital_mute = alc5623_mute,
+ .mute_stream = alc5623_mute,
.set_fmt = alc5623_set_dai_fmt,
.set_sysclk = alc5623_set_dai_sysclk,
.set_pll = alc5623_set_dai_pll,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver alc5623_dai = {
@@ -862,25 +858,25 @@ static struct snd_soc_dai_driver alc5623_dai = {
.ops = &alc5623_dai_ops,
};
-static int alc5623_suspend(struct snd_soc_codec *codec)
+static int alc5623_suspend(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
regcache_cache_only(alc5623->regmap, true);
return 0;
}
-static int alc5623_resume(struct snd_soc_codec *codec)
+static int alc5623_resume(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
int ret;
/* Sync reg_cache with the hardware */
regcache_cache_only(alc5623->regmap, false);
ret = regcache_sync(alc5623->regmap);
if (ret != 0) {
- dev_err(codec->dev, "Failed to sync register cache: %d\n",
+ dev_err(component->dev, "Failed to sync register cache: %d\n",
ret);
regcache_cache_only(alc5623->regmap, true);
return ret;
@@ -889,41 +885,41 @@ static int alc5623_resume(struct snd_soc_codec *codec)
return 0;
}
-static int alc5623_probe(struct snd_soc_codec *codec)
+static int alc5623_probe(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- alc5623_reset(codec);
+ alc5623_reset(component);
if (alc5623->add_ctrl) {
- snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
+ snd_soc_component_write(component, ALC5623_ADD_CTRL_REG,
alc5623->add_ctrl);
}
if (alc5623->jack_det_ctrl) {
- snd_soc_write(codec, ALC5623_JACK_DET_CTRL,
+ snd_soc_component_write(component, ALC5623_JACK_DET_CTRL,
alc5623->jack_det_ctrl);
}
switch (alc5623->id) {
case 0x21:
- snd_soc_add_codec_controls(codec, alc5621_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5621_vol_snd_controls,
ARRAY_SIZE(alc5621_vol_snd_controls));
break;
case 0x22:
- snd_soc_add_codec_controls(codec, alc5622_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5622_vol_snd_controls,
ARRAY_SIZE(alc5622_vol_snd_controls));
break;
case 0x23:
- snd_soc_add_codec_controls(codec, alc5623_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5623_vol_snd_controls,
ARRAY_SIZE(alc5623_vol_snd_controls));
break;
default:
return -EINVAL;
}
- snd_soc_add_codec_controls(codec, alc5623_snd_controls,
+ snd_soc_add_component_controls(component, alc5623_snd_controls,
ARRAY_SIZE(alc5623_snd_controls));
snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets,
@@ -951,12 +947,15 @@ static int alc5623_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
- .probe = alc5623_probe,
- .suspend = alc5623_suspend,
- .resume = alc5623_resume,
- .set_bias_level = alc5623_set_bias_level,
- .suspend_bias_off = true,
+static const struct snd_soc_component_driver soc_component_device_alc5623 = {
+ .probe = alc5623_probe,
+ .suspend = alc5623_suspend,
+ .resume = alc5623_resume,
+ .set_bias_level = alc5623_set_bias_level,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config alc5623_regmap = {
@@ -968,19 +967,27 @@ static const struct regmap_config alc5623_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct i2c_device_id alc5623_i2c_table[] = {
+ {"alc5621", 0x21},
+ {"alc5622", 0x22},
+ {"alc5623", 0x23},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
+
/*
* ALC5623 2 wire address is determined by A1 pin
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
-static int alc5623_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int alc5623_i2c_probe(struct i2c_client *client)
{
struct alc5623_platform_data *pdata;
struct alc5623_priv *alc5623;
struct device_node *np;
unsigned int vid1, vid2;
+ unsigned int matched_id;
int ret;
u32 val32;
@@ -1009,10 +1016,12 @@ static int alc5623_i2c_probe(struct i2c_client *client,
}
vid2 >>= 8;
- if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
+ matched_id = (uintptr_t)i2c_get_match_data(client);
+
+ if ((vid1 != 0x10ec) || (vid2 != matched_id)) {
dev_err(&client->dev, "unknown or wrong codec\n");
- dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
- 0x10ec, id->driver_data,
+ dev_err(&client->dev, "Expected %x:%x, got %x:%x\n",
+ 0x10ec, matched_id,
vid1, vid2);
return -ENODEV;
}
@@ -1052,33 +1061,21 @@ static int alc5623_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, alc5623);
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_device_alc5623, &alc5623_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_device_alc5623, &alc5623_dai, 1);
if (ret != 0)
dev_err(&client->dev, "Failed to register codec: %d\n", ret);
return ret;
}
-static int alc5623_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id alc5623_i2c_table[] = {
- {"alc5621", 0x21},
- {"alc5622", 0x22},
- {"alc5623", 0x23},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
-
+#ifdef CONFIG_OF
static const struct of_device_id alc5623_of_match[] = {
{ .compatible = "realtek,alc5623", },
{ }
};
MODULE_DEVICE_TABLE(of, alc5623_of_match);
+#endif
/* i2c codec control layer */
static struct i2c_driver alc5623_i2c_driver = {
@@ -1087,7 +1084,6 @@ static struct i2c_driver alc5623_i2c_driver = {
.of_match_table = of_match_ptr(alc5623_of_match),
},
.probe = alc5623_i2c_probe,
- .remove = alc5623_i2c_remove,
.id_table = alc5623_i2c_table,
};
diff --git a/sound/soc/codecs/alc5623.h b/sound/soc/codecs/alc5623.h
index f3d68260d425..1dd88c772509 100644
--- a/sound/soc/codecs/alc5623.h
+++ b/sound/soc/codecs/alc5623.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* alc5623.h -- alc562[123] ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
*
* Author: flove <flove@realtek.com>
* Arnaud Patard <arnaud.patard@rtp-net.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef _ALC5623_H
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 4d3ba33eb6f9..72f4622204ff 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* alc5632.c -- ALC5632 ALSA SoC Audio Codec
*
@@ -9,10 +10,6 @@
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.c by Arnaud Patard
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License version 2 as
-* published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -116,20 +113,20 @@ static inline int alc5632_reset(struct regmap *map)
static int amp_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
/* to power-on/off class-d amp generators/speaker */
/* need to write to 'index-46h' register : */
/* so write index num (here 0x46) to reg 0x6a */
/* and then 0xffff/0 to reg 0x6c */
- snd_soc_write(codec, ALC5632_HID_CTRL_INDEX, 0x46);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0xFFFF);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0);
break;
}
@@ -681,7 +678,7 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
int i;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int gbl_clk = 0, pll_div = 0;
u16 reg;
@@ -689,15 +686,15 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -EINVAL;
/* Disable PLL power */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL1,
0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL2,
0);
/* pll is not used in slave mode */
- reg = snd_soc_read(codec, ALC5632_DAI_CONTROL);
+ reg = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
if (reg & ALC5632_DAI_SDP_SLAVE_MODE)
return 0;
@@ -745,19 +742,19 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -EINVAL;
/* choose MCLK/BCLK/VBCLK */
- snd_soc_write(codec, ALC5632_GPCR2, gbl_clk);
+ snd_soc_component_write(component, ALC5632_GPCR2, gbl_clk);
/* choose PLL1 clock rate */
- snd_soc_write(codec, ALC5632_PLL1_CTRL, pll_div);
+ snd_soc_component_write(component, ALC5632_PLL1_CTRL, pll_div);
/* enable PLL1 */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL1,
ALC5632_PWR_ADD2_PLL1);
/* enable PLL2 */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL2,
ALC5632_PWR_ADD2_PLL2);
/* use PLL1 as main SYSCLK */
- snd_soc_update_bits(codec, ALC5632_GPCR1,
+ snd_soc_component_update_bits(component, ALC5632_GPCR1,
ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1,
ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1);
@@ -775,9 +772,9 @@ static const struct _coeff_div coeff_div[] = {
{512*1, 0x3075},
};
-static int get_coeff(struct snd_soc_codec *codec, int rate)
+static int get_coeff(struct snd_soc_component *component, int rate)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -793,8 +790,8 @@ static int get_coeff(struct snd_soc_codec *codec, int rate)
static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 4096000:
@@ -815,15 +812,15 @@ static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set audio interface clocking */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = ALC5632_DAI_SDP_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5632_DAI_SDP_SLAVE_MODE;
break;
default:
@@ -864,17 +861,17 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ return snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface);
}
static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int coeff, rate;
u16 iface;
- iface = snd_soc_read(codec, ALC5632_DAI_CONTROL);
+ iface = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
iface &= ~ALC5632_DAI_I2S_DL_MASK;
/* bit size */
@@ -893,29 +890,29 @@ static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface);
rate = params_rate(params);
- coeff = get_coeff(codec, rate);
+ coeff = get_coeff(component, rate);
if (coeff < 0)
return -EINVAL;
coeff = coeff_div[coeff].regvalue;
- snd_soc_write(codec, ALC5632_DAC_CLK_CTRL1, coeff);
+ snd_soc_component_write(component, ALC5632_DAC_CLK_CTRL1, coeff);
return 0;
}
-static int alc5632_mute(struct snd_soc_dai *dai, int mute)
+static int alc5632_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 hp_mute = ALC5632_MISC_HP_DEPOP_MUTE_L
|ALC5632_MISC_HP_DEPOP_MUTE_R;
- u16 mute_reg = snd_soc_read(codec, ALC5632_MISC_CTRL) & ~hp_mute;
+ u16 mute_reg = snd_soc_component_read(component, ALC5632_MISC_CTRL) & ~hp_mute;
if (mute)
mute_reg |= hp_mute;
- return snd_soc_write(codec, ALC5632_MISC_CTRL, mute_reg);
+ return snd_soc_component_write(component, ALC5632_MISC_CTRL, mute_reg);
}
#define ALC5632_ADD2_POWER_EN (ALC5632_PWR_ADD2_VREF)
@@ -929,73 +926,73 @@ static int alc5632_mute(struct snd_soc_dai *dai, int mute)
| ALC5632_PWR_ADD1_HP_OUT_ENH_AMP \
| ALC5632_PWR_ADD1_MAIN_BIAS)
-static void enable_power_depop(struct snd_soc_codec *codec)
+static void enable_power_depop(struct snd_soc_component *component)
{
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_ADD1_SOFTGEN_EN,
ALC5632_PWR_ADD1_SOFTGEN_EN);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
ALC5632_ADD3_POWER_EN,
ALC5632_ADD3_POWER_EN);
- snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ snd_soc_component_update_bits(component, ALC5632_MISC_CTRL,
ALC5632_MISC_HP_DEPOP_MODE2_EN,
ALC5632_MISC_HP_DEPOP_MODE2_EN);
/* "normal" mode: 0 @ 26 */
/* set all PR0-7 mixers to 0 */
- snd_soc_update_bits(codec, ALC5632_PWR_DOWN_CTRL_STATUS,
+ snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS,
ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
0);
msleep(500);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_ADD2_POWER_EN,
ALC5632_ADD2_POWER_EN);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_ADD1_POWER_EN,
ALC5632_ADD1_POWER_EN);
/* disable HP Depop2 */
- snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ snd_soc_component_update_bits(component, ALC5632_MISC_CTRL,
ALC5632_MISC_HP_DEPOP_MODE2_EN,
0);
}
-static int alc5632_set_bias_level(struct snd_soc_codec *codec,
+static int alc5632_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
- enable_power_depop(codec);
+ enable_power_depop(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_MANAG_ADD1_MASK,
ALC5632_PWR_ADD1_MAIN_BIAS);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_MANAG_ADD2_MASK,
ALC5632_PWR_ADD2_VREF);
/* "normal" mode: 0 @ 26 */
- snd_soc_update_bits(codec, ALC5632_PWR_DOWN_CTRL_STATUS,
+ snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS,
ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
0xffff ^ (ALC5632_PWR_VREF_PR3
| ALC5632_PWR_VREF_PR2));
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_MANAG_ADD2_MASK, 0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
ALC5632_PWR_MANAG_ADD3_MASK, 0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_MANAG_ADD1_MASK, 0);
break;
}
@@ -1008,10 +1005,11 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops alc5632_dai_ops = {
.hw_params = alc5632_pcm_hw_params,
- .digital_mute = alc5632_mute,
+ .mute_stream = alc5632_mute,
.set_fmt = alc5632_set_dai_fmt,
.set_sysclk = alc5632_set_dai_sysclk,
.set_pll = alc5632_set_dai_pll,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver alc5632_dai = {
@@ -1034,13 +1032,13 @@ static struct snd_soc_dai_driver alc5632_dai = {
.formats = ALC5632_FORMATS,},
.ops = &alc5632_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
#ifdef CONFIG_PM
-static int alc5632_resume(struct snd_soc_codec *codec)
+static int alc5632_resume(struct snd_soc_component *component)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
regcache_sync(alc5632->regmap);
@@ -1050,13 +1048,13 @@ static int alc5632_resume(struct snd_soc_codec *codec)
#define alc5632_resume NULL
#endif
-static int alc5632_probe(struct snd_soc_codec *codec)
+static int alc5632_probe(struct snd_soc_component *component)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
switch (alc5632->id) {
case 0x5c:
- snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5632_vol_snd_controls,
ARRAY_SIZE(alc5632_vol_snd_controls));
break;
default:
@@ -1066,18 +1064,20 @@ static int alc5632_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_device_alc5632 = {
- .probe = alc5632_probe,
- .resume = alc5632_resume,
- .set_bias_level = alc5632_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = alc5632_snd_controls,
- .num_controls = ARRAY_SIZE(alc5632_snd_controls),
- .dapm_widgets = alc5632_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets),
- .dapm_routes = alc5632_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes),
+static const struct snd_soc_component_driver soc_component_device_alc5632 = {
+ .probe = alc5632_probe,
+ .resume = alc5632_resume,
+ .set_bias_level = alc5632_set_bias_level,
+ .controls = alc5632_snd_controls,
+ .num_controls = ARRAY_SIZE(alc5632_snd_controls),
+ .dapm_widgets = alc5632_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets),
+ .dapm_routes = alc5632_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config alc5632_regmap = {
@@ -1091,18 +1091,24 @@ static const struct regmap_config alc5632_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct i2c_device_id alc5632_i2c_table[] = {
+ {"alc5632", 0x5c},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
+
/*
* alc5632 2 wire address is determined by A1 pin
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
-static int alc5632_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int alc5632_i2c_probe(struct i2c_client *client)
{
struct alc5632_priv *alc5632;
int ret, ret1, ret2;
unsigned int vid1, vid2;
+ unsigned int matched_id;
alc5632 = devm_kzalloc(&client->dev,
sizeof(struct alc5632_priv), GFP_KERNEL);
@@ -1128,7 +1134,9 @@ static int alc5632_i2c_probe(struct i2c_client *client,
vid2 >>= 8;
- if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
+ matched_id = (uintptr_t)i2c_get_match_data(client);
+
+ if ((vid1 != 0x10EC) || (vid2 != matched_id)) {
dev_err(&client->dev,
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
return -EINVAL;
@@ -1149,34 +1157,24 @@ static int alc5632_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_device_alc5632, &alc5632_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_device_alc5632, &alc5632_dai, 1);
if (ret < 0) {
- dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+ dev_err(&client->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int alc5632_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id alc5632_i2c_table[] = {
- {"alc5632", 0x5c},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
-
+#ifdef CONFIG_OF
static const struct of_device_id alc5632_of_match[] = {
{ .compatible = "realtek,alc5632", },
{ }
};
MODULE_DEVICE_TABLE(of, alc5632_of_match);
+#endif
/* i2c codec control layer */
static struct i2c_driver alc5632_i2c_driver = {
@@ -1185,7 +1183,6 @@ static struct i2c_driver alc5632_i2c_driver = {
.of_match_table = of_match_ptr(alc5632_of_match),
},
.probe = alc5632_i2c_probe,
- .remove = alc5632_i2c_remove,
.id_table = alc5632_i2c_table,
};
diff --git a/sound/soc/codecs/alc5632.h b/sound/soc/codecs/alc5632.h
index 1b5bda594ea3..a2bb5f9c7109 100644
--- a/sound/soc/codecs/alc5632.h
+++ b/sound/soc/codecs/alc5632.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* alc5632.h -- ALC5632 ALSA SoC Audio Codec
*
@@ -9,10 +10,6 @@
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.h by Arnaud Patard
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License version 2 as
-* published by the Free Software Foundation.
*/
#ifndef _ALC5632_H
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
new file mode 100644
index 000000000000..303c1d44ebd8
--- /dev/null
+++ b/sound/soc/codecs/arizona-jack.c
@@ -0,0 +1,1668 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * extcon-arizona.c - Extcon driver Wolfson Arizona devices
+ *
+ * Copyright (C) 2012-2014 Wolfson Microelectronics plc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+#include <dt-bindings/mfd/arizona.h>
+
+#include "arizona.h"
+
+#define ARIZONA_MAX_MICD_RANGE 8
+
+/*
+ * The hardware supports 8 ranges / buttons, but the snd-jack interface
+ * only supports 6 buttons (button 0-5).
+ */
+#define ARIZONA_MAX_MICD_BUTTONS 6
+
+#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4
+#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5
+#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
+#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb
+
+#define ARIZONA_TST_CAP_DEFAULT 0x3
+#define ARIZONA_TST_CAP_CLAMP 0x1
+
+#define ARIZONA_HPDET_MAX 10000
+
+#define HPDET_DEBOUNCE 500
+#define DEFAULT_MICD_TIMEOUT 2000
+
+#define ARIZONA_HPDET_WAIT_COUNT 15
+#define ARIZONA_HPDET_WAIT_DELAY_MS 20
+
+#define QUICK_HEADPHONE_MAX_OHM 3
+#define MICROPHONE_MIN_OHM 1257
+#define MICROPHONE_MAX_OHM 30000
+
+#define MICD_DBTIME_TWO_READINGS 2
+#define MICD_DBTIME_FOUR_READINGS 4
+
+#define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \
+ ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \
+ ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \
+ ARIZONA_MICD_LVL_7)
+
+#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7)
+
+#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8)
+
+static const struct arizona_micd_config micd_default_modes[] = {
+ { ARIZONA_ACCDET_SRC, 1, 0 },
+ { 0, 2, 1 },
+};
+
+static const struct arizona_micd_range micd_default_ranges[] = {
+ { .max = 11, .key = BTN_0 },
+ { .max = 28, .key = BTN_1 },
+ { .max = 54, .key = BTN_2 },
+ { .max = 100, .key = BTN_3 },
+ { .max = 186, .key = BTN_4 },
+ { .max = 430, .key = BTN_5 },
+};
+
+/* The number of levels in arizona_micd_levels valid for button thresholds */
+#define ARIZONA_NUM_MICD_BUTTON_LEVELS 64
+
+static const int arizona_micd_levels[] = {
+ 3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
+ 49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
+ 105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
+ 270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
+ 1257, 30000,
+};
+
+static void arizona_start_hpdet_acc_id(struct arizona_priv *info);
+
+static void arizona_extcon_hp_clamp(struct arizona_priv *info,
+ bool clamp)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int mask = 0, val = 0;
+ unsigned int cap_sel = 0;
+ int ret;
+
+ switch (arizona->type) {
+ case WM8998:
+ case WM1814:
+ mask = 0;
+ break;
+ case WM5110:
+ case WM8280:
+ mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
+ ARIZONA_HP1L_SHRTI;
+ if (clamp) {
+ val = ARIZONA_HP1L_SHRTO;
+ cap_sel = ARIZONA_TST_CAP_CLAMP;
+ } else {
+ val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
+ cap_sel = ARIZONA_TST_CAP_DEFAULT;
+ }
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HP_TEST_CTRL_1,
+ ARIZONA_HP1_TST_CAP_SEL_MASK,
+ cap_sel);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to set TST_CAP_SEL: %d\n", ret);
+ break;
+ default:
+ mask = ARIZONA_RMV_SHRT_HP1L;
+ if (clamp)
+ val = ARIZONA_RMV_SHRT_HP1L;
+ break;
+ }
+
+ snd_soc_dapm_mutex_lock(arizona->dapm);
+
+ arizona->hpdet_clamp = clamp;
+
+ /* Keep the HP output stages disabled while doing the clamp */
+ if (clamp) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, 0);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to disable headphone outputs: %d\n", ret);
+ }
+
+ if (mask) {
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
+ mask, val);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
+ mask, val);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
+ }
+
+ /* Restore the desired state while not doing the clamp */
+ if (!clamp) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, arizona->hp_ena);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to restore headphone outputs: %d\n", ret);
+ }
+
+ snd_soc_dapm_mutex_unlock(arizona->dapm);
+}
+
+static void arizona_extcon_set_mode(struct arizona_priv *info, int mode)
+{
+ struct arizona *arizona = info->arizona;
+
+ mode %= info->micd_num_modes;
+
+ gpiod_set_value_cansleep(info->micd_pol_gpio,
+ info->micd_modes[mode].gpio);
+
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_BIAS_SRC_MASK,
+ info->micd_modes[mode].bias <<
+ ARIZONA_MICD_BIAS_SRC_SHIFT);
+ regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
+
+ info->micd_mode = mode;
+
+ dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
+}
+
+static const char *arizona_extcon_get_micbias(struct arizona_priv *info)
+{
+ switch (info->micd_modes[0].bias) {
+ case 1:
+ return "MICBIAS1";
+ case 2:
+ return "MICBIAS2";
+ case 3:
+ return "MICBIAS3";
+ default:
+ return "MICVDD";
+ }
+}
+
+static void arizona_extcon_pulse_micbias(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ const char *widget = arizona_extcon_get_micbias(info);
+ struct snd_soc_dapm_context *dapm = arizona->dapm;
+ int ret;
+
+ ret = snd_soc_dapm_force_enable_pin(dapm, widget);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to enable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+
+ if (!arizona->pdata.micd_force_micbias) {
+ ret = snd_soc_dapm_disable_pin(dapm, widget);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+ }
+}
+
+static void arizona_start_mic(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ bool change;
+ int ret;
+ unsigned int mode;
+
+ /* Microphone detection can't use idle mode */
+ pm_runtime_get_sync(arizona->dev);
+
+ if (info->detecting) {
+ ret = regulator_allow_bypass(info->micvdd, false);
+ if (ret)
+ dev_err(arizona->dev, "Failed to regulate MICVDD: %d\n", ret);
+ }
+
+ ret = regulator_enable(info->micvdd);
+ if (ret)
+ dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", ret);
+
+ if (info->micd_reva) {
+ const struct reg_sequence reva[] = {
+ { 0x80, 0x3 },
+ { 0x294, 0x0 },
+ { 0x80, 0x0 },
+ };
+
+ regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
+ }
+
+ if (info->detecting && arizona->pdata.micd_software_compare)
+ mode = ARIZONA_ACCDET_MODE_ADC;
+ else
+ mode = ARIZONA_ACCDET_MODE_MIC;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_MODE_MASK, mode);
+
+ arizona_extcon_pulse_micbias(info);
+
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, ARIZONA_MICD_ENA,
+ &change);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to enable micd: %d\n", ret);
+ } else if (!change) {
+ regulator_disable(info->micvdd);
+ pm_runtime_put_autosuspend(arizona->dev);
+ }
+}
+
+static void arizona_stop_mic(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ const char *widget = arizona_extcon_get_micbias(info);
+ struct snd_soc_dapm_context *dapm = arizona->dapm;
+ bool change = false;
+ int ret;
+
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, 0,
+ &change);
+ if (ret < 0)
+ dev_err(arizona->dev, "Failed to disable micd: %d\n", ret);
+
+ ret = snd_soc_dapm_disable_pin(dapm, widget);
+ if (ret)
+ dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+
+ if (info->micd_reva) {
+ const struct reg_sequence reva[] = {
+ { 0x80, 0x3 },
+ { 0x294, 0x2 },
+ { 0x80, 0x0 },
+ };
+
+ regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
+ }
+
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret)
+ dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
+
+ if (change) {
+ regulator_disable(info->micvdd);
+ pm_runtime_put_autosuspend(arizona->dev);
+ }
+}
+
+static struct {
+ unsigned int threshold;
+ unsigned int factor_a;
+ unsigned int factor_b;
+} arizona_hpdet_b_ranges[] = {
+ { 100, 5528, 362464 },
+ { 169, 11084, 6186851 },
+ { 169, 11065, 65460395 },
+};
+
+#define ARIZONA_HPDET_B_RANGE_MAX 0x3fb
+
+static struct {
+ int min;
+ int max;
+} arizona_hpdet_c_ranges[] = {
+ { 0, 30 },
+ { 8, 100 },
+ { 100, 1000 },
+ { 1000, 10000 },
+};
+
+static int arizona_hpdet_read(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val, range;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read HPDET status: %d\n", ret);
+ return ret;
+ }
+
+ switch (info->hpdet_ip_version) {
+ case 0:
+ if (!(val & ARIZONA_HP_DONE)) {
+ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ val &= ARIZONA_HP_LVL_MASK;
+ break;
+
+ case 1:
+ if (!(val & ARIZONA_HP_DONE_B)) {
+ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read HP value: %d\n", ret);
+ return -EAGAIN;
+ }
+
+ regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ &range);
+ range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
+ >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
+
+ if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
+ (val < arizona_hpdet_b_ranges[range].threshold ||
+ val >= ARIZONA_HPDET_B_RANGE_MAX)) {
+ range++;
+ dev_dbg(arizona->dev, "Moving to HPDET range %d\n", range);
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK,
+ range <<
+ ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
+ return -EAGAIN;
+ }
+
+ /* If we go out of range report top of range */
+ if (val < arizona_hpdet_b_ranges[range].threshold ||
+ val >= ARIZONA_HPDET_B_RANGE_MAX) {
+ dev_dbg(arizona->dev, "Measurement out of range\n");
+ return ARIZONA_HPDET_MAX;
+ }
+
+ dev_dbg(arizona->dev, "HPDET read %d in range %d\n", val, range);
+
+ val = arizona_hpdet_b_ranges[range].factor_b
+ / ((val * 100) -
+ arizona_hpdet_b_ranges[range].factor_a);
+ break;
+
+ case 2:
+ if (!(val & ARIZONA_HP_DONE_B)) {
+ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ val &= ARIZONA_HP_LVL_B_MASK;
+ /* Convert to ohms, the value is in 0.5 ohm increments */
+ val /= 2;
+
+ regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ &range);
+ range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
+ >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
+
+ /* Skip up a range, or report? */
+ if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&
+ (val >= arizona_hpdet_c_ranges[range].max)) {
+ range++;
+ dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n",
+ arizona_hpdet_c_ranges[range].min,
+ arizona_hpdet_c_ranges[range].max);
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK,
+ range <<
+ ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
+ return -EAGAIN;
+ }
+
+ if (range && (val < arizona_hpdet_c_ranges[range].min)) {
+ dev_dbg(arizona->dev, "Reporting range boundary %d\n",
+ arizona_hpdet_c_ranges[range].min);
+ val = arizona_hpdet_c_ranges[range].min;
+ }
+ break;
+
+ default:
+ dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n", info->hpdet_ip_version);
+ return -EINVAL;
+ }
+
+ dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
+ return val;
+}
+
+static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading,
+ bool *mic)
+{
+ struct arizona *arizona = info->arizona;
+#ifdef CONFIG_GPIOLIB_LEGACY
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+#else
+ int id_gpio = 0;
+#endif
+
+ if (!arizona->pdata.hpdet_acc_id)
+ return 0;
+
+ /*
+ * If we're using HPDET for accessory identification we need
+ * to take multiple measurements, step through them in sequence.
+ */
+ info->hpdet_res[info->num_hpdet_res++] = *reading;
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ /* Only check the mic directly if we didn't already ID it */
+ if (id_gpio && info->num_hpdet_res == 1) {
+ dev_dbg(arizona->dev, "Measuring mic\n");
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_MODE_MASK |
+ ARIZONA_ACCDET_SRC,
+ ARIZONA_ACCDET_MODE_HPR |
+ info->micd_modes[0].src);
+
+ gpio_set_value_cansleep(id_gpio, 1);
+
+ regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+ return -EAGAIN;
+ }
+#endif
+
+ /* OK, got both. Now, compare... */
+ dev_dbg(arizona->dev, "HPDET measured %d %d\n",
+ info->hpdet_res[0], info->hpdet_res[1]);
+
+ /* Take the headphone impedance for the main report */
+ *reading = info->hpdet_res[0];
+
+ /* Sometimes we get false readings due to slow insert */
+ if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
+ dev_dbg(arizona->dev, "Retrying high impedance\n");
+ info->num_hpdet_res = 0;
+ info->hpdet_retried = true;
+ arizona_start_hpdet_acc_id(info);
+ pm_runtime_put(arizona->dev);
+ return -EAGAIN;
+ }
+
+ /*
+ * If we measure the mic as high impedance
+ */
+ if (!id_gpio || info->hpdet_res[1] > 50) {
+ dev_dbg(arizona->dev, "Detected mic\n");
+ *mic = true;
+ info->detecting = true;
+ } else {
+ dev_dbg(arizona->dev, "Detected headphone\n");
+ }
+
+ /* Make sure everything is reset back to the real polarity */
+ regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_SRC, info->micd_modes[0].src);
+
+ return 0;
+}
+
+static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+{
+ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+#ifdef CONFIG_GPIOLIB_LEGACY
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+#endif
+ int ret, reading, state, report;
+ bool mic = false;
+
+ mutex_lock(&info->lock);
+
+ /* If we got a spurious IRQ for some reason then ignore it */
+ if (!info->hpdet_active) {
+ dev_warn(arizona->dev, "Spurious HPDET IRQ\n");
+ mutex_unlock(&info->lock);
+ return IRQ_NONE;
+ }
+
+ /* If the cable was removed while measuring ignore the result */
+ state = info->jack->status & SND_JACK_MECHANICAL;
+ if (!state) {
+ dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n");
+ goto done;
+ }
+
+ ret = arizona_hpdet_read(info);
+ if (ret == -EAGAIN)
+ goto out;
+ else if (ret < 0)
+ goto done;
+ reading = ret;
+
+ /* Reset back to starting range */
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
+ 0);
+
+ ret = arizona_hpdet_do_id(info, &reading, &mic);
+ if (ret == -EAGAIN)
+ goto out;
+ else if (ret < 0)
+ goto done;
+
+ /* Report high impedence cables as line outputs */
+ if (reading >= 5000)
+ report = SND_JACK_LINEOUT;
+ else
+ report = SND_JACK_HEADPHONE;
+
+ snd_soc_jack_report(info->jack, report, SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+done:
+ /* Reset back to starting range */
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
+ 0);
+
+ arizona_extcon_hp_clamp(info, false);
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ if (id_gpio)
+ gpio_set_value_cansleep(id_gpio, 0);
+#endif
+
+ /* If we have a mic then reenable MICDET */
+ if (state && (mic || info->mic))
+ arizona_start_mic(info);
+
+ if (info->hpdet_active) {
+ pm_runtime_put_autosuspend(arizona->dev);
+ info->hpdet_active = false;
+ }
+
+ /* Do not set hp_det done when the cable has been unplugged */
+ if (state)
+ info->hpdet_done = true;
+
+out:
+ mutex_unlock(&info->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void arizona_identify_headphone(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ int ret;
+
+ if (info->hpdet_done)
+ return;
+
+ dev_dbg(arizona->dev, "Starting HPDET\n");
+
+ /* Make sure we keep the device enabled during the measurement */
+ pm_runtime_get_sync(arizona->dev);
+
+ info->hpdet_active = true;
+
+ arizona_stop_mic(info);
+
+ arizona_extcon_hp_clamp(info, true);
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_MODE_MASK,
+ arizona->pdata.hpdet_channel);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+ if (ret) {
+ dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret);
+ goto err;
+ }
+
+ return;
+
+err:
+ arizona_extcon_hp_clamp(info, false);
+ pm_runtime_put_autosuspend(arizona->dev);
+
+ /* Just report headphone */
+ snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
+ SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ if (info->mic)
+ arizona_start_mic(info);
+
+ info->hpdet_active = false;
+}
+
+static void arizona_start_hpdet_acc_id(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ int hp_reading = 32;
+ bool mic;
+ int ret;
+
+ dev_dbg(arizona->dev, "Starting identification via HPDET\n");
+
+ /* Make sure we keep the device enabled during the measurement */
+ pm_runtime_get_sync(arizona->dev);
+
+ info->hpdet_active = true;
+
+ arizona_extcon_hp_clamp(info, true);
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ACCESSORY_DETECT_MODE_1,
+ ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK,
+ info->micd_modes[0].src |
+ arizona->pdata.hpdet_channel);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
+ goto err;
+ }
+
+ if (arizona->pdata.hpdet_acc_id_line) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+ if (ret) {
+ dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret);
+ goto err;
+ }
+ } else {
+ arizona_hpdet_do_id(info, &hp_reading, &mic);
+ }
+
+ return;
+
+err:
+ /* Just report headphone */
+ snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
+ SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ info->hpdet_active = false;
+}
+
+static void arizona_micd_timeout_work(struct work_struct *work)
+{
+ struct arizona_priv *info = container_of(work,
+ struct arizona_priv,
+ micd_timeout_work.work);
+
+ mutex_lock(&info->lock);
+
+ dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
+
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+
+ mutex_unlock(&info->lock);
+}
+
+static int arizona_micd_adc_read(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val;
+ int ret;
+
+ /* Must disable MICD before we read the ADCVAL */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, 0);
+
+ ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read MICDET_ADCVAL: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
+
+ val &= ARIZONA_MICDET_ADCVAL_MASK;
+ if (val < ARRAY_SIZE(arizona_micd_levels))
+ val = arizona_micd_levels[val];
+ else
+ val = INT_MAX;
+
+ if (val <= QUICK_HEADPHONE_MAX_OHM)
+ val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
+ else if (val <= MICROPHONE_MIN_OHM)
+ val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
+ else if (val <= MICROPHONE_MAX_OHM)
+ val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
+ else
+ val = ARIZONA_MICD_LVL_8;
+
+ return val;
+}
+
+static int arizona_micd_read(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val = 0;
+ int ret, i;
+
+ for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
+ ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(arizona->dev, "MICDET: %x\n", val);
+
+ if (!(val & ARIZONA_MICD_VALID)) {
+ dev_warn(arizona->dev, "Microphone detection state invalid\n");
+ return -EINVAL;
+ }
+ }
+
+ if (i == 10 && !(val & MICD_LVL_0_TO_8)) {
+ dev_err(arizona->dev, "Failed to get valid MICDET value\n");
+ return -EINVAL;
+ }
+
+ return val;
+}
+
+static int arizona_micdet_reading(void *priv)
+{
+ struct arizona_priv *info = priv;
+ struct arizona *arizona = info->arizona;
+ int ret, val;
+
+ if (info->detecting && arizona->pdata.micd_software_compare)
+ ret = arizona_micd_adc_read(info);
+ else
+ ret = arizona_micd_read(info);
+ if (ret < 0)
+ return ret;
+
+ val = ret;
+
+ /* Due to jack detect this should never happen */
+ if (!(val & ARIZONA_MICD_STS)) {
+ dev_warn(arizona->dev, "Detected open circuit\n");
+ info->mic = false;
+ info->detecting = false;
+ arizona_identify_headphone(info);
+ return 0;
+ }
+
+ /* If we got a high impedence we should have a headset, report it. */
+ if (val & ARIZONA_MICD_LVL_8) {
+ info->mic = true;
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+
+ snd_soc_jack_report(info->jack, SND_JACK_MICROPHONE, SND_JACK_MICROPHONE);
+
+ /* Don't need to regulate for button detection */
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret)
+ dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
+
+ return 0;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones. If both polarities report a low
+ * impedence then give up and report headphones.
+ */
+ if (val & MICD_LVL_1_TO_7) {
+ if (info->jack_flips >= info->micd_num_modes * 10) {
+ dev_dbg(arizona->dev, "Detected HP/line\n");
+
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+ } else {
+ info->micd_mode++;
+ if (info->micd_mode == info->micd_num_modes)
+ info->micd_mode = 0;
+ arizona_extcon_set_mode(info, info->micd_mode);
+
+ info->jack_flips++;
+
+ if (arizona->pdata.micd_software_compare)
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA,
+ ARIZONA_MICD_ENA);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_timeout_work,
+ msecs_to_jiffies(arizona->pdata.micd_timeout));
+ }
+
+ return 0;
+ }
+
+ /*
+ * If we're still detecting and we detect a short then we've
+ * got a headphone.
+ */
+ dev_dbg(arizona->dev, "Headphone detected\n");
+ info->detecting = false;
+
+ arizona_identify_headphone(info);
+
+ return 0;
+}
+
+static int arizona_button_reading(void *priv)
+{
+ struct arizona_priv *info = priv;
+ struct arizona *arizona = info->arizona;
+ int val, key, lvl;
+
+ val = arizona_micd_read(info);
+ if (val < 0)
+ return val;
+
+ /*
+ * If we're still detecting and we detect a short then we've
+ * got a headphone. Otherwise it's a button press.
+ */
+ if (val & MICD_LVL_0_TO_7) {
+ if (info->mic) {
+ dev_dbg(arizona->dev, "Mic button detected\n");
+
+ lvl = val & ARIZONA_MICD_LVL_MASK;
+ lvl >>= ARIZONA_MICD_LVL_SHIFT;
+
+ if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
+ key = ffs(lvl) - 1;
+ snd_soc_jack_report(info->jack,
+ SND_JACK_BTN_0 >> key,
+ info->micd_button_mask);
+ } else {
+ dev_err(arizona->dev, "Button out of range\n");
+ }
+ } else {
+ dev_warn(arizona->dev, "Button with no mic: %x\n", val);
+ }
+ } else {
+ dev_dbg(arizona->dev, "Mic button released\n");
+ snd_soc_jack_report(info->jack, 0, info->micd_button_mask);
+ arizona_extcon_pulse_micbias(info);
+ }
+
+ return 0;
+}
+
+static void arizona_micd_detect(struct work_struct *work)
+{
+ struct arizona_priv *info = container_of(work,
+ struct arizona_priv,
+ micd_detect_work.work);
+ struct arizona *arizona = info->arizona;
+
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ mutex_lock(&info->lock);
+
+ /* If the cable was removed while measuring ignore the result */
+ if (!(info->jack->status & SND_JACK_MECHANICAL)) {
+ dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
+ mutex_unlock(&info->lock);
+ return;
+ }
+
+ if (info->detecting)
+ arizona_micdet_reading(info);
+ else
+ arizona_button_reading(info);
+
+ pm_runtime_mark_last_busy(arizona->dev);
+ mutex_unlock(&info->lock);
+}
+
+static irqreturn_t arizona_micdet(int irq, void *data)
+{
+ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ int debounce = arizona->pdata.micd_detect_debounce;
+
+ cancel_delayed_work_sync(&info->micd_detect_work);
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ mutex_lock(&info->lock);
+ if (!info->detecting)
+ debounce = 0;
+ mutex_unlock(&info->lock);
+
+ if (debounce)
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_detect_work,
+ msecs_to_jiffies(debounce));
+ else
+ arizona_micd_detect(&info->micd_detect_work.work);
+
+ return IRQ_HANDLED;
+}
+
+static void arizona_hpdet_work(struct work_struct *work)
+{
+ struct arizona_priv *info = container_of(work,
+ struct arizona_priv,
+ hpdet_work.work);
+
+ mutex_lock(&info->lock);
+ arizona_start_hpdet_acc_id(info);
+ mutex_unlock(&info->lock);
+}
+
+static int arizona_hpdet_wait(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ unsigned int val;
+ int i, ret;
+
+ for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) {
+ ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2,
+ &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read HPDET state: %d\n", ret);
+ return ret;
+ }
+
+ switch (info->hpdet_ip_version) {
+ case 0:
+ if (val & ARIZONA_HP_DONE)
+ return 0;
+ break;
+ default:
+ if (val & ARIZONA_HP_DONE_B)
+ return 0;
+ break;
+ }
+
+ msleep(ARIZONA_HPDET_WAIT_DELAY_MS);
+ }
+
+ dev_warn(arizona->dev, "HPDET did not appear to complete\n");
+
+ return -ETIMEDOUT;
+}
+
+static irqreturn_t arizona_jackdet(int irq, void *data)
+{
+ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ unsigned int val, present, mask;
+ bool cancelled_hp, cancelled_mic;
+ int ret, i;
+
+ cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
+ cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ pm_runtime_get_sync(arizona->dev);
+
+ mutex_lock(&info->lock);
+
+ if (info->micd_clamp) {
+ mask = ARIZONA_MICD_CLAMP_STS;
+ present = 0;
+ } else {
+ mask = ARIZONA_JD1_STS;
+ if (arizona->pdata.jd_invert)
+ present = 0;
+ else
+ present = ARIZONA_JD1_STS;
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret);
+ mutex_unlock(&info->lock);
+ pm_runtime_put_autosuspend(arizona->dev);
+ return IRQ_NONE;
+ }
+
+ val &= mask;
+ if (val == info->last_jackdet) {
+ dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
+ if (cancelled_hp)
+ queue_delayed_work(system_power_efficient_wq,
+ &info->hpdet_work,
+ msecs_to_jiffies(HPDET_DEBOUNCE));
+
+ if (cancelled_mic) {
+ int micd_timeout = arizona->pdata.micd_timeout;
+
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_timeout_work,
+ msecs_to_jiffies(micd_timeout));
+ }
+
+ goto out;
+ }
+ info->last_jackdet = val;
+
+ if (info->last_jackdet == present) {
+ dev_dbg(arizona->dev, "Detected jack\n");
+ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+
+ info->detecting = true;
+ info->mic = false;
+ info->jack_flips = 0;
+
+ if (!arizona->pdata.hpdet_acc_id) {
+ arizona_start_mic(info);
+ } else {
+ queue_delayed_work(system_power_efficient_wq,
+ &info->hpdet_work,
+ msecs_to_jiffies(HPDET_DEBOUNCE));
+ }
+
+ if (info->micd_clamp || !arizona->pdata.jd_invert)
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB |
+ ARIZONA_JD1_DB, 0);
+ } else {
+ dev_dbg(arizona->dev, "Detected jack removal\n");
+
+ arizona_stop_mic(info);
+
+ info->num_hpdet_res = 0;
+ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++)
+ info->hpdet_res[i] = 0;
+ info->mic = false;
+ info->hpdet_done = false;
+ info->hpdet_retried = false;
+
+ snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask);
+
+ /*
+ * If the jack was removed during a headphone detection we
+ * need to wait for the headphone detection to finish, as
+ * it can not be aborted. We don't want to be able to start
+ * a new headphone detection from a fresh insert until this
+ * one is finished.
+ */
+ arizona_hpdet_wait(info);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,
+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
+ }
+
+out:
+ /* Clear trig_sts to make sure DCVDD is not forced up */
+ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
+ ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
+ ARIZONA_MICD_CLAMP_RISE_TRIG_STS |
+ ARIZONA_JD1_FALL_TRIG_STS |
+ ARIZONA_JD1_RISE_TRIG_STS);
+
+ mutex_unlock(&info->lock);
+
+ pm_runtime_put_autosuspend(arizona->dev);
+
+ return IRQ_HANDLED;
+}
+
+/* Map a level onto a slot in the register bank */
+static void arizona_micd_set_level(struct arizona *arizona, int index,
+ unsigned int level)
+{
+ int reg;
+ unsigned int mask;
+
+ reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2);
+
+ if (!(index % 2)) {
+ mask = 0x3f00;
+ level <<= 8;
+ } else {
+ mask = 0x3f;
+ }
+
+ /* Program the level itself */
+ regmap_update_bits(arizona->regmap, reg, mask, level);
+}
+
+static int arizona_extcon_get_micd_configs(struct device *dev,
+ struct arizona *arizona)
+{
+ const char * const prop = "wlf,micd-configs";
+ const int entries_per_config = 3;
+ struct arizona_micd_config *micd_configs;
+ int nconfs, ret;
+ int i, j;
+ u32 *vals;
+
+ nconfs = device_property_count_u32(arizona->dev, prop);
+ if (nconfs <= 0)
+ return 0;
+
+ vals = kcalloc(nconfs, sizeof(u32), GFP_KERNEL);
+ if (!vals)
+ return -ENOMEM;
+
+ ret = device_property_read_u32_array(arizona->dev, prop, vals, nconfs);
+ if (ret < 0)
+ goto out;
+
+ nconfs /= entries_per_config;
+ micd_configs = devm_kcalloc(dev, nconfs, sizeof(*micd_configs),
+ GFP_KERNEL);
+ if (!micd_configs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0, j = 0; i < nconfs; ++i) {
+ micd_configs[i].src = vals[j++] ? ARIZONA_ACCDET_SRC : 0;
+ micd_configs[i].bias = vals[j++];
+ micd_configs[i].gpio = vals[j++];
+ }
+
+ arizona->pdata.micd_configs = micd_configs;
+ arizona->pdata.num_micd_configs = nconfs;
+
+out:
+ kfree(vals);
+ return ret;
+}
+
+static int arizona_extcon_device_get_pdata(struct device *dev,
+ struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val = ARIZONA_ACCDET_MODE_HPL;
+ int ret;
+
+ device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val);
+ switch (val) {
+ case ARIZONA_ACCDET_MODE_HPL:
+ case ARIZONA_ACCDET_MODE_HPR:
+ pdata->hpdet_channel = val;
+ break;
+ default:
+ dev_err(arizona->dev, "Wrong wlf,hpdet-channel DT value %d\n", val);
+ pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
+ }
+
+ device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce",
+ &pdata->micd_detect_debounce);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time",
+ &pdata->micd_bias_start_time);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-rate",
+ &pdata->micd_rate);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-dbtime",
+ &pdata->micd_dbtime);
+
+ device_property_read_u32(arizona->dev, "wlf,micd-timeout-ms",
+ &pdata->micd_timeout);
+
+ pdata->micd_force_micbias = device_property_read_bool(arizona->dev,
+ "wlf,micd-force-micbias");
+
+ pdata->micd_software_compare = device_property_read_bool(arizona->dev,
+ "wlf,micd-software-compare");
+
+ pdata->jd_invert = device_property_read_bool(arizona->dev,
+ "wlf,jd-invert");
+
+ device_property_read_u32(arizona->dev, "wlf,gpsw", &pdata->gpsw);
+
+ pdata->jd_gpio5 = device_property_read_bool(arizona->dev,
+ "wlf,use-jd2");
+ pdata->jd_gpio5_nopull = device_property_read_bool(arizona->dev,
+ "wlf,use-jd2-nopull");
+
+ ret = arizona_extcon_get_micd_configs(dev, arizona);
+ if (ret < 0)
+ dev_err(arizona->dev, "Failed to read micd configs: %d\n", ret);
+
+ return 0;
+}
+
+int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev)
+{
+ struct arizona *arizona = info->arizona;
+ struct arizona_pdata *pdata = &arizona->pdata;
+ int ret, mode;
+
+ if (!dev_get_platdata(arizona->dev))
+ arizona_extcon_device_get_pdata(dev, arizona);
+
+ info->micvdd = devm_regulator_get(dev, "MICVDD");
+ if (IS_ERR(info->micvdd))
+ return dev_err_probe(arizona->dev, PTR_ERR(info->micvdd), "getting MICVDD\n");
+
+ mutex_init(&info->lock);
+ info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
+ INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
+ INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
+ INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
+
+ switch (arizona->type) {
+ case WM5102:
+ switch (arizona->rev) {
+ case 0:
+ info->micd_reva = true;
+ break;
+ default:
+ info->micd_clamp = true;
+ info->hpdet_ip_version = 1;
+ break;
+ }
+ break;
+ case WM5110:
+ case WM8280:
+ switch (arizona->rev) {
+ case 0 ... 2:
+ break;
+ default:
+ info->micd_clamp = true;
+ info->hpdet_ip_version = 2;
+ break;
+ }
+ break;
+ case WM8998:
+ case WM1814:
+ info->micd_clamp = true;
+ info->hpdet_ip_version = 2;
+ break;
+ default:
+ break;
+ }
+
+ if (!pdata->micd_timeout)
+ pdata->micd_timeout = DEFAULT_MICD_TIMEOUT;
+
+ if (pdata->num_micd_configs) {
+ info->micd_modes = pdata->micd_configs;
+ info->micd_num_modes = pdata->num_micd_configs;
+ } else {
+ info->micd_modes = micd_default_modes;
+ info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
+ }
+
+ if (arizona->pdata.gpsw > 0)
+ regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1,
+ ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw);
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ if (pdata->micd_pol_gpio > 0) {
+ if (info->micd_modes[0].gpio)
+ mode = GPIOF_OUT_INIT_HIGH;
+ else
+ mode = GPIOF_OUT_INIT_LOW;
+
+ ret = devm_gpio_request_one(dev, pdata->micd_pol_gpio,
+ mode, "MICD polarity");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+ pdata->micd_pol_gpio, ret);
+ return ret;
+ }
+
+ info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio);
+ } else
+#endif
+ {
+ if (info->micd_modes[0].gpio)
+ mode = GPIOD_OUT_HIGH;
+ else
+ mode = GPIOD_OUT_LOW;
+
+ /* We can't use devm here because we need to do the get
+ * against the MFD device, as that is where the of_node
+ * will reside, but if we devm against that the GPIO
+ * will not be freed if the extcon driver is unloaded.
+ */
+ info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
+ "wlf,micd-pol",
+ mode);
+ if (IS_ERR(info->micd_pol_gpio)) {
+ ret = PTR_ERR(info->micd_pol_gpio);
+ dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n");
+ return ret;
+ }
+ }
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ if (arizona->pdata.hpdet_id_gpio > 0) {
+ ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "HPDET");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+ arizona->pdata.hpdet_id_gpio, ret);
+ gpiod_put(info->micd_pol_gpio);
+ return ret;
+ }
+ }
+#endif
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_probe);
+
+int arizona_jack_codec_dev_remove(struct arizona_priv *info)
+{
+ gpiod_put(info->micd_pol_gpio);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_remove);
+
+static int arizona_jack_enable_jack_detect(struct arizona_priv *info,
+ struct snd_soc_jack *jack)
+{
+ struct arizona *arizona = info->arizona;
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val;
+ unsigned int clamp_mode;
+ int jack_irq_fall, jack_irq_rise;
+ int ret, i, j;
+
+ if (arizona->pdata.micd_bias_start_time)
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_BIAS_STARTTIME_MASK,
+ arizona->pdata.micd_bias_start_time
+ << ARIZONA_MICD_BIAS_STARTTIME_SHIFT);
+
+ if (arizona->pdata.micd_rate)
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_RATE_MASK,
+ arizona->pdata.micd_rate
+ << ARIZONA_MICD_RATE_SHIFT);
+
+ switch (arizona->pdata.micd_dbtime) {
+ case MICD_DBTIME_FOUR_READINGS:
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_DBTIME_MASK,
+ ARIZONA_MICD_DBTIME);
+ break;
+ case MICD_DBTIME_TWO_READINGS:
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_DBTIME_MASK, 0);
+ break;
+ default:
+ break;
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) <
+ ARIZONA_NUM_MICD_BUTTON_LEVELS);
+
+ if (arizona->pdata.num_micd_ranges) {
+ info->micd_ranges = pdata->micd_ranges;
+ info->num_micd_ranges = pdata->num_micd_ranges;
+ } else {
+ info->micd_ranges = micd_default_ranges;
+ info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges);
+ }
+
+ if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_BUTTONS) {
+ dev_err(arizona->dev, "Too many MICD ranges: %d > %d\n",
+ arizona->pdata.num_micd_ranges, ARIZONA_MAX_MICD_BUTTONS);
+ return -EINVAL;
+ }
+
+ if (info->num_micd_ranges > 1) {
+ for (i = 1; i < info->num_micd_ranges; i++) {
+ if (info->micd_ranges[i - 1].max >
+ info->micd_ranges[i].max) {
+ dev_err(arizona->dev, "MICD ranges must be sorted\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Disable all buttons by default */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
+ ARIZONA_MICD_LVL_SEL_MASK, 0x81);
+
+ /* Set up all the buttons the user specified */
+ for (i = 0; i < info->num_micd_ranges; i++) {
+ for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++)
+ if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
+ break;
+
+ if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) {
+ dev_err(arizona->dev, "Unsupported MICD level %d\n",
+ info->micd_ranges[i].max);
+ return -EINVAL;
+ }
+
+ dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
+ arizona_micd_levels[j], i);
+
+ arizona_micd_set_level(arizona, i, j);
+
+ /* SND_JACK_BTN_# masks start with the most significant bit */
+ info->micd_button_mask |= SND_JACK_BTN_0 >> i;
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0 >> i,
+ info->micd_ranges[i].key);
+
+ /* Enable reporting of that range */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
+ 1 << i, 1 << i);
+ }
+
+ /* Set all the remaining keys to a maximum */
+ for (; i < ARIZONA_MAX_MICD_RANGE; i++)
+ arizona_micd_set_level(arizona, i, 0x3f);
+
+ /*
+ * If we have a clamp use it, activating in conjunction with
+ * GPIO5 if that is connected for jack detect operation.
+ */
+ if (info->micd_clamp) {
+ if (arizona->pdata.jd_gpio5) {
+ /* Put the GPIO into input mode with optional pull */
+ val = 0xc101;
+ if (arizona->pdata.jd_gpio5_nopull)
+ val &= ~ARIZONA_GPN_PU;
+
+ regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL,
+ val);
+
+ if (arizona->pdata.jd_invert)
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H;
+ else
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H;
+ } else {
+ if (arizona->pdata.jd_invert)
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH;
+ else
+ clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL;
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MICD_CLAMP_CONTROL,
+ ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB,
+ ARIZONA_MICD_CLAMP_DB);
+ }
+
+ arizona_extcon_set_mode(info, 0);
+
+ info->jack = jack;
+
+ pm_runtime_get_sync(arizona->dev);
+
+ if (info->micd_clamp) {
+ jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
+ jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
+ } else {
+ jack_irq_rise = ARIZONA_IRQ_JD_RISE;
+ jack_irq_fall = ARIZONA_IRQ_JD_FALL;
+ }
+
+ ret = arizona_request_irq(arizona, jack_irq_rise,
+ "JACKDET rise", arizona_jackdet, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get JACKDET rise IRQ: %d\n", ret);
+ goto err_pm;
+ }
+
+ ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set JD rise IRQ wake: %d\n", ret);
+ goto err_rise;
+ }
+
+ ret = arizona_request_irq(arizona, jack_irq_fall,
+ "JACKDET fall", arizona_jackdet, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get JD fall IRQ: %d\n", ret);
+ goto err_rise_wake;
+ }
+
+ ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set JD fall IRQ wake: %d\n", ret);
+ goto err_fall;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
+ "MICDET", arizona_micdet, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get MICDET IRQ: %d\n", ret);
+ goto err_fall_wake;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET,
+ "HPDET", arizona_hpdet_irq, info);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to get HPDET IRQ: %d\n", ret);
+ goto err_micdet;
+ }
+
+ arizona_clk32k_enable(arizona);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_JD1_DB, ARIZONA_JD1_DB);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+ ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
+
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret != 0)
+ dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n", ret);
+
+ pm_runtime_put(arizona->dev);
+
+ return 0;
+
+err_micdet:
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+err_fall_wake:
+ arizona_set_irq_wake(arizona, jack_irq_fall, 0);
+err_fall:
+ arizona_free_irq(arizona, jack_irq_fall, info);
+err_rise_wake:
+ arizona_set_irq_wake(arizona, jack_irq_rise, 0);
+err_rise:
+ arizona_free_irq(arizona, jack_irq_rise, info);
+err_pm:
+ pm_runtime_put(arizona->dev);
+ info->jack = NULL;
+ return ret;
+}
+
+static int arizona_jack_disable_jack_detect(struct arizona_priv *info)
+{
+ struct arizona *arizona = info->arizona;
+ int jack_irq_rise, jack_irq_fall;
+ bool change;
+ int ret;
+
+ if (!info->jack)
+ return 0;
+
+ if (info->micd_clamp) {
+ jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
+ jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
+ } else {
+ jack_irq_rise = ARIZONA_IRQ_JD_RISE;
+ jack_irq_fall = ARIZONA_IRQ_JD_FALL;
+ }
+
+ arizona_set_irq_wake(arizona, jack_irq_rise, 0);
+ arizona_set_irq_wake(arizona, jack_irq_fall, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info);
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+ arizona_free_irq(arizona, jack_irq_rise, info);
+ arizona_free_irq(arizona, jack_irq_fall, info);
+ cancel_delayed_work_sync(&info->hpdet_work);
+ cancel_delayed_work_sync(&info->micd_detect_work);
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_ENA, 0,
+ &change);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to disable micd on remove: %d\n", ret);
+ } else if (change) {
+ regulator_disable(info->micvdd);
+ pm_runtime_put(arizona->dev);
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MICD_CLAMP_CONTROL,
+ ARIZONA_MICD_CLAMP_MODE_MASK, 0);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+ ARIZONA_JD1_ENA, 0);
+ arizona_clk32k_disable(arizona);
+ info->jack = NULL;
+
+ return 0;
+}
+
+int arizona_jack_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct arizona_priv *info = snd_soc_component_get_drvdata(component);
+
+ if (jack)
+ return arizona_jack_enable_jack_detect(info, jack);
+ else
+ return arizona_jack_disable_jack_detect(info);
+}
+EXPORT_SYMBOL_GPL(arizona_jack_set_jack);
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 33143fe1de0b..8c683b0bb74c 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1,18 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* arizona.c - Wolfson Arizona class device shared support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -83,34 +81,14 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- bool manual_ena = false;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
int val;
- switch (arizona->type) {
- case WM5102:
- switch (arizona->rev) {
- case 0:
- break;
- default:
- manual_ena = true;
- break;
- }
- default:
- break;
- }
-
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- if (!priv->spk_ena && manual_ena) {
- regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
- priv->spk_ena_pending = true;
- }
- break;
case SND_SOC_DAPM_POST_PMU:
- val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
+ val = snd_soc_component_read(component,
+ ARIZONA_INTERRUPT_RAW_STATUS_3);
if (val & ARIZONA_SPK_OVERHEAT_STS) {
dev_crit(arizona->dev,
"Speaker not enabled due to temperature\n");
@@ -120,38 +98,17 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
regmap_update_bits_async(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
1 << w->shift, 1 << w->shift);
-
- if (priv->spk_ena_pending) {
- msleep(75);
- regmap_write_async(arizona->regmap, 0x4f5, 0xda);
- priv->spk_ena_pending = false;
- priv->spk_ena++;
- }
break;
case SND_SOC_DAPM_PRE_PMD:
- if (manual_ena) {
- priv->spk_ena--;
- if (!priv->spk_ena)
- regmap_write_async(arizona->regmap,
- 0x4f5, 0x25a);
- }
-
regmap_update_bits_async(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
1 << w->shift, 0);
break;
- case SND_SOC_DAPM_POST_PMD:
- if (manual_ena) {
- if (!priv->spk_ena)
- regmap_write_async(arizona->regmap,
- 0x4f5, 0x0da);
- }
- break;
default:
break;
}
- return 0;
+ return arizona_out_ev(w, kcontrol, event);
}
static irqreturn_t arizona_thermal_warn(int irq, void *data)
@@ -201,17 +158,19 @@ static irqreturn_t arizona_thermal_shutdown(int irq, void *data)
static const struct snd_soc_dapm_widget arizona_spkl =
SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU);
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD);
static const struct snd_soc_dapm_widget arizona_spkr =
SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM,
ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU);
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD);
-int arizona_init_spk(struct snd_soc_codec *codec)
+int arizona_init_spk(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int ret;
@@ -221,6 +180,8 @@ int arizona_init_spk(struct snd_soc_codec *codec)
switch (arizona->type) {
case WM8997:
+ case CS47L24:
+ case WM1831:
break;
default:
ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
@@ -229,6 +190,14 @@ int arizona_init_spk(struct snd_soc_codec *codec)
break;
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk);
+
+int arizona_init_spk_irqs(struct arizona *arizona)
+{
+ int ret;
+
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
"Thermal warning", arizona_thermal_warn,
arizona);
@@ -247,7 +216,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_init_spk);
+EXPORT_SYMBOL_GPL(arizona_init_spk_irqs);
+
+int arizona_free_spk_irqs(struct arizona *arizona)
+{
+ arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona);
+ arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_free_spk_irqs);
static const struct snd_soc_dapm_route arizona_mono_routes[] = {
{ "OUT1R", NULL, "OUT1L" },
@@ -258,10 +236,10 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
{ "OUT6R", NULL, "OUT6L" },
};
-int arizona_init_mono(struct snd_soc_codec *codec)
+int arizona_init_mono(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int i;
@@ -275,10 +253,10 @@ int arizona_init_mono(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_mono);
-int arizona_init_gpio(struct snd_soc_codec *codec)
+int arizona_init_gpio(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int i;
@@ -310,6 +288,100 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
+int arizona_init_common(struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val, mask;
+ int i;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+
+ for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
+ /* Default is 0 so noop with defaults */
+ if (pdata->out_mono[i])
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+ ARIZONA_OUT1_MONO, val);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
+ if (pdata->spk_mute[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
+ ARIZONA_SPK1_MUTE_ENDIAN_MASK |
+ ARIZONA_SPK1_MUTE_SEQ1_MASK,
+ pdata->spk_mute[i]);
+
+ if (pdata->spk_fmt[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
+ ARIZONA_SPK1_FMT_MASK,
+ pdata->spk_fmt[i]);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
+ /* Default for both is 0 so noop with defaults */
+ val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT;
+ if (pdata->inmode[i] & ARIZONA_INMODE_DMIC)
+ val |= 1 << ARIZONA_IN1_MODE_SHIFT;
+
+ switch (arizona->type) {
+ case WM8998:
+ case WM1814:
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8),
+ ARIZONA_IN1L_SRC_SE_MASK,
+ (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ << ARIZONA_IN1L_SRC_SE_SHIFT);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8),
+ ARIZONA_IN1R_SRC_SE_MASK,
+ (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ << ARIZONA_IN1R_SRC_SE_SHIFT);
+
+ mask = ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK;
+ break;
+ default:
+ if (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
+
+ mask = ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK |
+ ARIZONA_IN1_SINGLE_ENDED_MASK;
+ break;
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_IN1L_CONTROL + (i * 8),
+ mask, val);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_common);
+
+int arizona_init_vol_limit(struct arizona *arizona)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) {
+ if (arizona->pdata.out_vol_limit[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4,
+ ARIZONA_OUT1L_VOL_LIM_MASK,
+ arizona->pdata.out_vol_limit[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_vol_limit);
+
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
@@ -565,7 +637,6 @@ const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
};
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,
@@ -605,7 +676,7 @@ const struct soc_enum arizona_asrc_rate1 =
arizona_rate_text, arizona_rate_val);
EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
-static const char *arizona_vol_ramp_text[] = {
+static const char * const arizona_vol_ramp_text[] = {
"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
"15ms/6dB", "30ms/6dB",
};
@@ -634,7 +705,7 @@ SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
-static const char *arizona_lhpf_mode_text[] = {
+static const char * const arizona_lhpf_mode_text[] = {
"Low-pass", "High-pass"
};
@@ -662,7 +733,7 @@ SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
-static const char *arizona_ng_hold_text[] = {
+static const char * const arizona_ng_hold_text[] = {
"30ms", "120ms", "250ms", "500ms",
};
@@ -716,7 +787,7 @@ const struct soc_enum arizona_anc_input_src[] = {
ARRAY_SIZE(arizona_anc_input_src_text),
arizona_anc_input_src_text),
SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL,
- ARIZONA_FCL_MIC_MODE_SEL,
+ ARIZONA_FCL_MIC_MODE_SEL_SHIFT,
ARRAY_SIZE(arizona_anc_channel_src_text),
arizona_anc_channel_src_text),
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
@@ -724,7 +795,7 @@ const struct soc_enum arizona_anc_input_src[] = {
ARRAY_SIZE(arizona_anc_input_src_text),
arizona_anc_input_src_text),
SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL,
- ARIZONA_FCR_MIC_MODE_SEL,
+ ARIZONA_FCR_MIC_MODE_SEL_SHIFT,
ARRAY_SIZE(arizona_anc_channel_src_text),
arizona_anc_channel_src_text),
};
@@ -796,9 +867,17 @@ const struct soc_enum arizona_output_anc_src[] = {
};
EXPORT_SYMBOL_GPL(arizona_output_anc_src);
-static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
+const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0),
+};
+EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
+
+static void arizona_in_set_vu(struct snd_soc_component *component, int ena)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int val;
int i;
@@ -808,15 +887,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
val = 0;
for (i = 0; i < priv->num_inputs; i++)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 4),
ARIZONA_IN_VU, val);
}
-bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+bool arizona_input_analog(struct snd_soc_component *component, int shift)
{
unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
- unsigned int val = snd_soc_read(codec, reg);
+ unsigned int val = snd_soc_component_read(component, reg);
return !(val & ARIZONA_IN1_MODE_MASK);
}
@@ -825,8 +904,8 @@ EXPORT_SYMBOL_GPL(arizona_input_analog);
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int reg;
if (w->shift % 2)
@@ -839,25 +918,26 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
priv->in_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, reg, ARIZONA_IN1L_MUTE, 0);
+ snd_soc_component_update_bits(component, reg,
+ ARIZONA_IN1L_MUTE, 0);
/* If this is the last input pending then allow VU */
priv->in_pending--;
if (priv->in_pending == 0) {
msleep(1);
- arizona_in_set_vu(codec, 1);
+ arizona_in_set_vu(component, 1);
}
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
ARIZONA_IN1L_MUTE | ARIZONA_IN_VU,
ARIZONA_IN1L_MUTE | ARIZONA_IN_VU);
break;
case SND_SOC_DAPM_POST_PMD:
/* Disable volume updates if no inputs are enabled */
- reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
+ reg = snd_soc_component_read(component, ARIZONA_INPUT_ENABLES);
if (reg == 0)
- arizona_in_set_vu(codec, 0);
+ arizona_in_set_vu(component, 0);
break;
default:
break;
@@ -871,8 +951,9 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -884,7 +965,19 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_up_pending++;
- priv->out_up_delay += 17;
+ priv->out_up_delay += 17000;
+ break;
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_up_pending++;
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ break;
+ default:
+ priv->out_up_delay += 10000;
+ break;
+ }
break;
default:
break;
@@ -898,9 +991,13 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT2R_ENA_SHIFT:
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_up_pending--;
- if (!priv->out_up_pending) {
- msleep(priv->out_up_delay);
+ if (!priv->out_up_pending && priv->out_up_delay) {
+ dev_dbg(component->dev, "Power up delay: %d\n",
+ priv->out_up_delay);
+ fsleep(priv->out_up_delay);
priv->out_up_delay = 0;
}
break;
@@ -918,7 +1015,23 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_down_pending++;
- priv->out_down_delay++;
+ priv->out_down_delay += 1000;
+ break;
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_down_pending++;
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ break;
+ case WM8998:
+ case WM1814:
+ priv->out_down_delay += 5000;
+ break;
+ default:
+ priv->out_down_delay += 1000;
+ break;
+ }
break;
default:
break;
@@ -932,9 +1045,13 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT2R_ENA_SHIFT:
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
priv->out_down_pending--;
- if (!priv->out_down_pending) {
- msleep(priv->out_down_delay);
+ if (!priv->out_down_pending && priv->out_down_delay) {
+ dev_dbg(component->dev, "Power down delay: %d\n",
+ priv->out_down_delay);
+ fsleep(priv->out_down_delay);
priv->out_down_delay = 0;
}
break;
@@ -950,12 +1067,11 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_out_ev);
-int arizona_hp_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event)
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
unsigned int mask = 1 << w->shift;
unsigned int val;
@@ -989,15 +1105,15 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_hp_ev);
-static int arizona_dvfs_enable(struct snd_soc_codec *codec)
+static int arizona_dvfs_enable(struct snd_soc_component *component)
{
- const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ const struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int ret;
ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000);
if (ret) {
- dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret);
+ dev_err(component->dev, "Failed to boost DCVDD: %d\n", ret);
return ret;
}
@@ -1006,7 +1122,7 @@ static int arizona_dvfs_enable(struct snd_soc_codec *codec)
ARIZONA_SUBSYS_MAX_FREQ,
ARIZONA_SUBSYS_MAX_FREQ);
if (ret) {
- dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret);
+ dev_err(component->dev, "Failed to enable subsys max: %d\n", ret);
regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
return ret;
}
@@ -1014,9 +1130,9 @@ static int arizona_dvfs_enable(struct snd_soc_codec *codec)
return 0;
}
-static int arizona_dvfs_disable(struct snd_soc_codec *codec)
+static int arizona_dvfs_disable(struct snd_soc_component *component)
{
- const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ const struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int ret;
@@ -1024,28 +1140,28 @@ static int arizona_dvfs_disable(struct snd_soc_codec *codec)
ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
ARIZONA_SUBSYS_MAX_FREQ, 0);
if (ret) {
- dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret);
+ dev_err(component->dev, "Failed to disable subsys max: %d\n", ret);
return ret;
}
ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
if (ret) {
- dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret);
+ dev_err(component->dev, "Failed to unboost DCVDD: %d\n", ret);
return ret;
}
return 0;
}
-int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags)
+int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
mutex_lock(&priv->dvfs_lock);
if (!priv->dvfs_cached && !priv->dvfs_reqs) {
- ret = arizona_dvfs_enable(codec);
+ ret = arizona_dvfs_enable(component);
if (ret)
goto err;
}
@@ -1057,9 +1173,9 @@ err:
}
EXPORT_SYMBOL_GPL(arizona_dvfs_up);
-int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
+int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int old_reqs;
int ret = 0;
@@ -1069,7 +1185,7 @@ int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
priv->dvfs_reqs &= ~flags;
if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
- ret = arizona_dvfs_disable(codec);
+ ret = arizona_dvfs_disable(component);
mutex_unlock(&priv->dvfs_lock);
return ret;
@@ -1079,8 +1195,8 @@ EXPORT_SYMBOL_GPL(arizona_dvfs_down);
int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
mutex_lock(&priv->dvfs_lock);
@@ -1088,7 +1204,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (priv->dvfs_reqs)
- ret = arizona_dvfs_enable(codec);
+ ret = arizona_dvfs_enable(component);
priv->dvfs_cached = false;
break;
@@ -1100,7 +1216,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
priv->dvfs_cached = true;
if (priv->dvfs_reqs)
- ret = arizona_dvfs_disable(codec);
+ ret = arizona_dvfs_disable(component);
break;
default:
break;
@@ -1121,8 +1237,7 @@ int arizona_anc_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 = 0x3 << w->shift;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int val;
switch (event) {
@@ -1136,7 +1251,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
return 0;
}
- snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
+ snd_soc_component_write(component, ARIZONA_CLOCK_CONTROL, val);
return 0;
}
@@ -1156,10 +1271,10 @@ static unsigned int arizona_opclk_ref_44k1_rates[] = {
45158400,
};
-static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
- unsigned int freq)
+static int arizona_set_opclk(struct snd_soc_component *component,
+ unsigned int clk, unsigned int freq)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int reg;
unsigned int *rates;
int ref, div, refclk;
@@ -1183,13 +1298,13 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
rates = arizona_opclk_ref_48k_rates;
for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
- rates[ref] <= refclk; ref++) {
+ rates[ref] <= refclk; ref++) {
div = 1;
while (rates[ref] / div >= freq && div < 32) {
if (rates[ref] / div == freq) {
- dev_dbg(codec->dev, "Configured %dHz OPCLK\n",
+ dev_dbg(component->dev, "Configured %dHz OPCLK\n",
freq);
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
ARIZONA_OPCLK_DIV_MASK |
ARIZONA_OPCLK_SEL_MASK,
(div <<
@@ -1201,14 +1316,54 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
}
- dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
+ dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq);
return -EINVAL;
}
-int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+int arizona_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(component->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ clk_idx = ARIZONA_MCLK1;
+ break;
+ case ARIZONA_CLK_SRC_MCLK2:
+ clk_idx = ARIZONA_MCLK2;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(arizona->mclk[clk_idx]);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(arizona->mclk[clk_idx]);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(arizona_clk_ev);
+
+int arizona_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
char *name;
unsigned int reg;
@@ -1230,7 +1385,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
break;
case ARIZONA_CLK_OPCLK:
case ARIZONA_CLK_ASYNC_OPCLK:
- return arizona_set_opclk(codec, clk_id, freq);
+ return arizona_set_opclk(component, clk_id, freq);
default:
return -EINVAL;
}
@@ -1284,8 +1439,8 @@ EXPORT_SYMBOL_GPL(arizona_set_sysclk);
static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int lrclk, bclk, mode, base;
@@ -1300,7 +1455,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_DSP_B:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
- != SND_SOC_DAIFMT_CBM_CFM) {
+ != SND_SOC_DAIFMT_CBP_CFP) {
arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
return -EINVAL;
}
@@ -1311,7 +1466,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_LEFT_J:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
- != SND_SOC_DAIFMT_CBM_CFM) {
+ != SND_SOC_DAIFMT_CBP_CFP) {
arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
return -EINVAL;
}
@@ -1324,15 +1479,15 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
bclk |= ARIZONA_AIF1_BCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
bclk |= ARIZONA_AIF1_BCLK_MSTR;
lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
break;
@@ -1398,29 +1553,6 @@ static const int arizona_48k_bclk_rates[] = {
24576000,
};
-static const unsigned int arizona_48k_rates[] = {
- 12000,
- 24000,
- 48000,
- 96000,
- 192000,
- 384000,
- 768000,
- 4000,
- 8000,
- 16000,
- 32000,
- 64000,
- 128000,
- 256000,
- 512000,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
- .count = ARRAY_SIZE(arizona_48k_rates),
- .list = arizona_48k_rates,
-};
-
static const int arizona_44k1_bclk_rates[] = {
-1,
44100,
@@ -1443,22 +1575,7 @@ static const int arizona_44k1_bclk_rates[] = {
22579200,
};
-static const unsigned int arizona_44k1_rates[] = {
- 11025,
- 22050,
- 44100,
- 88200,
- 176400,
- 352800,
- 705600,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
- .count = ARRAY_SIZE(arizona_44k1_rates),
- .list = arizona_44k1_rates,
-};
-
-static int arizona_sr_vals[] = {
+static const unsigned int arizona_sr_vals[] = {
0,
12000,
24000,
@@ -1485,13 +1602,21 @@ static int arizona_sr_vals[] = {
512000,
};
+#define ARIZONA_48K_RATE_MASK 0x0F003E
+#define ARIZONA_44K1_RATE_MASK 0x003E00
+#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
+
+static const struct snd_pcm_hw_constraint_list arizona_constraint = {
+ .count = ARRAY_SIZE(arizona_sr_vals),
+ .list = arizona_sr_vals,
+};
+
static int arizona_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
- const struct snd_pcm_hw_constraint_list *constraint;
unsigned int base_rate;
if (!substream->runtime)
@@ -1509,22 +1634,21 @@ static int arizona_startup(struct snd_pcm_substream *substream,
}
if (base_rate == 0)
- return 0;
-
- if (base_rate % 8000)
- constraint = &arizona_44k1_constraint;
+ dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+ else if (base_rate % 8000)
+ dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
else
- constraint = &arizona_48k_constraint;
+ dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- constraint);
+ &dai_priv->constraint);
}
-static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
+static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component,
unsigned int rate)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
struct reg_sequence dac_comp[] = {
{ 0x80, 0x3 },
@@ -1550,8 +1674,8 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
int base = dai->driver->base;
int i, sr_val, ret;
@@ -1574,9 +1698,9 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
case WM5102:
case WM8997:
if (arizona_sr_vals[sr_val] >= 88200)
- ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ);
+ ret = arizona_dvfs_up(component, ARIZONA_DVFS_SR1_RQ);
else
- ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ);
+ ret = arizona_dvfs_down(component, ARIZONA_DVFS_SR1_RQ);
if (ret) {
arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
@@ -1591,26 +1715,31 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
case ARIZONA_CLK_SYSCLK:
switch (priv->arizona->type) {
case WM5102:
- arizona_wm5102_set_dac_comp(codec,
+ arizona_wm5102_set_dac_comp(component,
params_rate(params));
break;
default:
break;
}
- snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
- ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+ snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1,
+ ARIZONA_SAMPLE_RATE_1_MASK,
+ sr_val);
if (base)
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK, 0);
+ snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK, 0);
break;
case ARIZONA_CLK_ASYNCCLK:
- snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
- ARIZONA_ASYNC_SAMPLE_RATE_1_MASK, sr_val);
+ snd_soc_component_update_bits(component,
+ ARIZONA_ASYNC_SAMPLE_RATE_1,
+ ARIZONA_ASYNC_SAMPLE_RATE_1_MASK,
+ sr_val);
if (base)
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK,
- 8 << ARIZONA_AIF1_RATE_SHIFT);
+ snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK,
+ 8 << ARIZONA_AIF1_RATE_SHIFT);
break;
default:
arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
@@ -1620,20 +1749,20 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
return 0;
}
-static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec,
+static bool arizona_aif_cfg_changed(struct snd_soc_component *component,
int base, int bclk, int lrclk, int frame)
{
int val;
- val = snd_soc_read(codec, base + ARIZONA_AIF_BCLK_CTRL);
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_BCLK_CTRL);
if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
return true;
- val = snd_soc_read(codec, base + ARIZONA_AIF_TX_BCLK_RATE);
- if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK))
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK))
return true;
- val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1);
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_FRAME_CTRL_1);
if (frame != (val & (ARIZONA_AIF1TX_WL_MASK |
ARIZONA_AIF1TX_SLOT_LEN_MASK)))
return true;
@@ -1645,8 +1774,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int base = dai->driver->base;
const int *rates;
@@ -1683,7 +1812,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
}
/* Force multiple of 2 channels for I2S mode */
- val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_FORMAT);
val &= ARIZONA_AIF1_FMT_MASK;
if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
arizona_aif_dbg(dai, "Forcing stereo mode\n");
@@ -1711,19 +1840,20 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width;
- reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame);
+ reconfig = arizona_aif_cfg_changed(component, base, bclk, lrclk, frame);
if (reconfig) {
/* Save AIF TX/RX state */
- aif_tx_state = snd_soc_read(codec,
+ aif_tx_state = snd_soc_component_read(component,
base + ARIZONA_AIF_TX_ENABLES);
- aif_rx_state = snd_soc_read(codec,
+ aif_rx_state = snd_soc_component_read(component,
base + ARIZONA_AIF_RX_ENABLES);
/* Disable AIF TX/RX before reconfiguring it */
regmap_update_bits_async(arizona->regmap,
- base + ARIZONA_AIF_TX_ENABLES, 0xff, 0x0);
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, 0x0);
regmap_update_bits(arizona->regmap,
- base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+ base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
}
ret = arizona_hw_params_rate(substream, params, dai);
@@ -1778,9 +1908,9 @@ static const char *arizona_dai_clk_str(int clk_id)
static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
struct snd_soc_dapm_route routes[2];
@@ -1795,13 +1925,13 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
if (clk_id == dai_priv->clk)
return 0;
- if (dai->active) {
- dev_err(codec->dev, "Can't change clock on active DAI %d\n",
+ if (snd_soc_dai_active(dai)) {
+ dev_err(component->dev, "Can't change clock on active DAI %d\n",
dai->id);
return -EBUSY;
}
- dev_dbg(codec->dev, "Setting AIF%d to %s\n", dai->id + 1,
+ dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id + 1,
arizona_dai_clk_str(clk_id));
memset(&routes, 0, sizeof(routes));
@@ -1823,7 +1953,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int base = dai->driver->base;
unsigned int reg;
@@ -1832,16 +1962,17 @@ static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
else
reg = 0;
- return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_TRI, reg);
+ return snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_TRI, reg);
}
static void arizona_set_channels_to_mask(struct snd_soc_dai *dai,
unsigned int base,
int channels, unsigned int mask)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int slot, i;
@@ -1862,8 +1993,8 @@ static void arizona_set_channels_to_mask(struct snd_soc_dai *dai,
static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int base = dai->driver->base;
int rx_max_chan = dai->driver->playback.channels_max;
@@ -1911,6 +2042,7 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
struct arizona_dai_priv *dai_priv = &priv->dai[id];
dai_priv->clk = ARIZONA_CLK_SYSCLK;
+ dai_priv->constraint = arizona_constraint;
return 0;
}
@@ -1929,6 +2061,25 @@ static struct {
{ 1000000, 13500000, 0, 1 },
};
+static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = {
+ 13500000,
+ 6144000,
+ 6144000,
+ 3072000,
+ 3072000,
+ 2822400,
+ 2822400,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 768000,
+};
+
static struct {
unsigned int min;
unsigned int max;
@@ -1941,8 +2092,8 @@ static struct {
struct arizona_fll_cfg {
int n;
- int theta;
- int lambda;
+ unsigned int theta;
+ unsigned int lambda;
int refdiv;
int outdiv;
int fratio;
@@ -2042,24 +2193,46 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
/* Adjust FRATIO/refdiv to avoid integer mode if possible */
refdiv = cfg->refdiv;
- while (div <= ARIZONA_FLL_MAX_REFDIV) {
- for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
- ratio++) {
- if ((ARIZONA_FLL_VCO_CORNER / 2) /
- (fll->vco_mult * ratio) < Fref)
- break;
+ arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n",
+ init_ratio, Fref, refdiv);
+ while (div <= ARIZONA_FLL_MAX_REFDIV) {
+ /* start from init_ratio because this may already give a
+ * fractional N.K
+ */
+ for (ratio = init_ratio; ratio > 0; ratio--) {
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
+ arizona_fll_dbg(fll,
+ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+ Fref, refdiv, div, ratio);
return ratio;
}
}
- for (ratio = init_ratio - 1; ratio > 0; ratio--) {
+ for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO;
+ ratio++) {
+ if ((ARIZONA_FLL_VCO_CORNER / 2) /
+ (fll->vco_mult * ratio) < Fref) {
+ arizona_fll_dbg(fll, "pseudo: hit VCO corner\n");
+ break;
+ }
+
+ if (Fref > pseudo_fref_max[ratio - 1]) {
+ arizona_fll_dbg(fll,
+ "pseudo: exceeded max fref(%u) for ratio=%u\n",
+ pseudo_fref_max[ratio - 1],
+ ratio);
+ break;
+ }
+
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
+ arizona_fll_dbg(fll,
+ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+ Fref, refdiv, div, ratio);
return ratio;
}
}
@@ -2068,6 +2241,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
Fref /= 2;
refdiv++;
init_ratio = arizona_find_fratio(Fref, NULL);
+ arizona_fll_dbg(fll,
+ "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n",
+ Fref, refdiv, div, init_ratio);
}
arizona_fll_warn(fll, "Falling back to integer mode operation\n");
@@ -2138,14 +2314,14 @@ static int arizona_calc_fll(struct arizona_fll *fll,
return -EINVAL;
}
- arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+ arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
cfg->n, cfg->theta, cfg->lambda);
- arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
- cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
- arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
+ arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
+ cfg->fratio, ratio, cfg->outdiv,
+ cfg->refdiv, 1 << cfg->refdiv);
+ arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
return 0;
-
}
static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
@@ -2183,13 +2359,13 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
ARIZONA_FLL1_CTRL_UPD | cfg->n);
}
-static int arizona_is_enabled_fll(struct arizona_fll *fll)
+static int arizona_is_enabled_fll(struct arizona_fll *fll, int base)
{
struct arizona *arizona = fll->arizona;
unsigned int reg;
int ret;
- ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
+ ret = regmap_read(arizona->regmap, base + 1, &reg);
if (ret != 0) {
arizona_fll_err(fll, "Failed to read current state: %d\n",
ret);
@@ -2199,25 +2375,68 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll)
return reg & ARIZONA_FLL1_ENA;
}
+static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned int val;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, base + 6, &val);
+ if (ret != 0) {
+ arizona_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ val &= ARIZONA_FLL1_CLK_REF_SRC_MASK;
+ val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_FLL_SRC_MCLK1:
+ clk = arizona->mclk[ARIZONA_MCLK1];
+ break;
+ case ARIZONA_FLL_SRC_MCLK2:
+ clk = arizona->mclk[ARIZONA_MCLK2];
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
static int arizona_enable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
bool use_sync = false;
- int already_enabled = arizona_is_enabled_fll(fll);
+ int already_enabled = arizona_is_enabled_fll(fll, fll->base);
+ int sync_enabled = arizona_is_enabled_fll(fll, fll->base + 0x10);
struct arizona_fll_cfg cfg;
int i;
unsigned int val;
if (already_enabled < 0)
return already_enabled;
+ if (sync_enabled < 0)
+ return sync_enabled;
if (already_enabled) {
/* Facilitate smooth refclk across the transition */
- regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
- ARIZONA_FLL1_GAIN_MASK, 0);
regmap_update_bits(fll->arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
udelay(32);
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
+ ARIZONA_FLL1_GAIN_MASK, 0);
+
+ if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+ arizona_set_fll_clks(fll, fll->base, false);
}
/*
@@ -2228,6 +2447,10 @@ static int arizona_enable_fll(struct arizona_fll *fll)
fll->ref_src != fll->sync_src) {
arizona_calc_fll(fll, &cfg, fll->ref_freq, false);
+ /* Ref path hardcodes lambda to 65536 when sync is on */
+ if (fll->sync_src >= 0 && cfg.lambda)
+ cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda;
+
arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src,
false);
if (fll->sync_src >= 0) {
@@ -2250,6 +2473,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
return -EINVAL;
}
+ if (already_enabled && !!sync_enabled != use_sync)
+ arizona_fll_warn(fll, "Synchroniser changed on active FLL\n");
+
/*
* Increase the bandwidth if we're not using a low frequency
* sync source.
@@ -2263,14 +2489,17 @@ static int arizona_enable_fll(struct arizona_fll *fll)
ARIZONA_FLL1_SYNC_BW);
if (!already_enabled)
- pm_runtime_get(arizona->dev);
+ pm_runtime_get_sync(arizona->dev);
- regmap_update_bits_async(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
- if (use_sync)
+ if (use_sync) {
+ arizona_set_fll_clks(fll, fll->base + 0x10, true);
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA,
ARIZONA_FLL1_SYNC_ENA);
+ }
+ arizona_set_fll_clks(fll, fll->base, true);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
if (already_enabled)
regmap_update_bits_async(arizona->regmap, fll->base + 1,
@@ -2301,19 +2530,24 @@ static int arizona_enable_fll(struct arizona_fll *fll)
static void arizona_disable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
- bool change;
+ bool ref_change, sync_change;
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
regmap_update_bits_check(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, 0, &change);
- regmap_update_bits(arizona->regmap, fll->base + 0x11,
- ARIZONA_FLL1_SYNC_ENA, 0);
+ ARIZONA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0, &sync_change);
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, 0);
- if (change)
+ if (sync_change)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+
+ if (ref_change) {
+ arizona_set_fll_clks(fll, fll->base, false);
pm_runtime_put_autosuspend(arizona->dev);
+ }
}
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
@@ -2333,9 +2567,8 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
fll->ref_src = source;
fll->ref_freq = Fref;
- if (fll->fout && Fref > 0) {
+ if (fll->fout && Fref > 0)
ret = arizona_enable_fll(fll);
- }
return ret;
}
@@ -2411,7 +2644,7 @@ EXPORT_SYMBOL_GPL(arizona_init_fll);
/**
* arizona_set_output_mode - Set the mode of the specified output
*
- * @codec: Device to configure
+ * @component: Device to configure
* @output: Output number
* @diff: True to set the output to differential mode
*
@@ -2424,7 +2657,8 @@ EXPORT_SYMBOL_GPL(arizona_init_fll);
* Most systems have a single static configuration and should use
* platform data instead.
*/
-int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
+ bool diff)
{
unsigned int reg, val;
@@ -2438,7 +2672,8 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
else
val = 0;
- return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+ return snd_soc_component_update_bits(component, reg,
+ ARIZONA_OUT1_MONO, val);
}
EXPORT_SYMBOL_GPL(arizona_set_output_mode);
@@ -2487,8 +2722,8 @@ static bool arizona_eq_filter_unstable(bool mode, __be16 _a, __be16 _b)
int arizona_eq_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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
struct soc_bytes *params = (void *)kcontrol->private_value;
unsigned int val;
__be16 *data;
@@ -2531,8 +2766,8 @@ EXPORT_SYMBOL_GPL(arizona_eq_coeff_put);
int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
__be16 *data = (__be16 *)ucontrol->value.bytes.data;
s16 val = be16_to_cpu(*data);
@@ -2545,6 +2780,78 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
+int arizona_of_get_audio_pdata(struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ struct device_node *np = arizona->dev->of_node;
+ u32 val;
+ u32 pdm_val[ARIZONA_MAX_PDM_SPK];
+ int ret;
+ int count = 0;
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,inmode", val) {
+ if (count == ARRAY_SIZE(pdata->inmode))
+ break;
+
+ pdata->inmode[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,dmic-ref", val) {
+ if (count == ARRAY_SIZE(pdata->dmic_ref))
+ break;
+
+ pdata->dmic_ref[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,out-mono", val) {
+ if (count == ARRAY_SIZE(pdata->out_mono))
+ break;
+
+ pdata->out_mono[count] = !!val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,max-channels-clocked", val) {
+ if (count == ARRAY_SIZE(pdata->max_channels_clocked))
+ break;
+
+ pdata->max_channels_clocked[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,out-volume-limit", val) {
+ if (count == ARRAY_SIZE(pdata->out_vol_limit))
+ break;
+
+ pdata->out_vol_limit[count] = val;
+ count++;
+ }
+
+ ret = of_property_read_u32_array(np, "wlf,spk-fmt",
+ pdm_val, ARRAY_SIZE(pdm_val));
+
+ if (ret >= 0)
+ for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count)
+ pdata->spk_fmt[count] = pdm_val[count];
+
+ ret = of_property_read_u32_array(np, "wlf,spk-mute",
+ pdm_val, ARRAY_SIZE(pdm_val));
+
+ if (ret >= 0)
+ for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count)
+ pdata->spk_mute[count] = pdm_val[count];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata);
+
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 8b6adb5419bb..ecd8890eefc1 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -1,19 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* arizona.h - Wolfson Arizona class device shared support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _ASOC_ARIZONA_H
#define _ASOC_ARIZONA_H
#include <linux/completion.h>
+#include <linux/notifier.h>
+#include <linux/mfd/arizona/core.h>
#include <sound/soc.h>
@@ -57,17 +56,21 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
-#define ARIZONA_MAX_DAI 8
+#define ARIZONA_MAX_DAI 10
#define ARIZONA_MAX_ADSP 4
#define ARIZONA_DVFS_SR1_RQ 0x001
#define ARIZONA_DVFS_ADSP1_RQ 0x100
-struct arizona;
+/* Notifier events */
+#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
+
struct wm_adsp;
struct arizona_dai_priv {
int clk;
+
+ struct snd_pcm_hw_constraint_list constraint;
};
struct arizona_priv {
@@ -85,12 +88,48 @@ struct arizona_priv {
unsigned int out_down_pending;
unsigned int out_down_delay;
- unsigned int spk_ena:2;
- unsigned int spk_ena_pending:1;
-
unsigned int dvfs_reqs;
struct mutex dvfs_lock;
bool dvfs_cached;
+
+ /* Variables used by arizona-jack.c code */
+ struct mutex lock;
+ struct delayed_work hpdet_work;
+ struct delayed_work micd_detect_work;
+ struct delayed_work micd_timeout_work;
+ struct snd_soc_jack *jack;
+ struct regulator *micvdd;
+ struct gpio_desc *micd_pol_gpio;
+
+ u16 last_jackdet;
+
+ int micd_mode;
+ const struct arizona_micd_config *micd_modes;
+ int micd_num_modes;
+
+ int micd_button_mask;
+ const struct arizona_micd_range *micd_ranges;
+ int num_micd_ranges;
+
+ bool micd_reva;
+ bool micd_clamp;
+
+ bool hpdet_active;
+ bool hpdet_done;
+ bool hpdet_retried;
+
+ bool mic;
+ bool detecting;
+
+ int num_hpdet_res;
+ unsigned int hpdet_res[3];
+
+ int jack_flips;
+ int hpdet_ip_version;
+};
+
+struct arizona_voice_trigger_info {
+ int core;
};
#define ARIZONA_NUM_MIXER_INPUTS 104
@@ -184,20 +223,22 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#define ARIZONA_DSP_ROUTES(name) \
{ name, NULL, name " Preloader"}, \
- { name " Preloader", NULL, name " Aux 1" }, \
- { name " Preloader", NULL, name " Aux 2" }, \
- { name " Preloader", NULL, name " Aux 3" }, \
- { name " Preloader", NULL, name " Aux 4" }, \
- { name " Preloader", NULL, name " Aux 5" }, \
- { name " Preloader", NULL, name " Aux 6" }, \
+ { name " Preloader", NULL, "SYSCLK" }, \
+ { name " Preload", NULL, name " Preloader"}, \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
- ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \
- ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
+ ARIZONA_MIXER_ROUTES(name, name "L"), \
+ ARIZONA_MIXER_ROUTES(name, name "R")
#define ARIZONA_EQ_CONTROL(xname, xbase) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -216,6 +257,9 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#define ARIZONA_RATE_ENUM_SIZE 4
#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+/* SND_JACK_* mask for supported cable/switch types */
+#define ARIZONA_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_MECHANICAL)
+
extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
@@ -246,26 +290,26 @@ extern const struct soc_enum arizona_anc_input_src[];
extern const struct soc_enum arizona_anc_ng_enum;
extern const struct soc_enum arizona_output_anc_src[];
-extern 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_anc_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-
-extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
- int source, unsigned int freq, int dir);
+extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
+
+int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+
+int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
+ unsigned int freq, int dir);
extern const struct snd_soc_dai_ops arizona_dai_ops;
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
@@ -288,29 +332,67 @@ struct arizona_fll {
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
};
-extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
-extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
-extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event);
-extern void arizona_init_dvfs(struct arizona_priv *priv);
-
-extern int arizona_init_fll(struct arizona *arizona, int id, int base,
- int lock_irq, int ok_irq, struct arizona_fll *fll);
-extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
- unsigned int Fref, unsigned int Fout);
-extern int arizona_set_fll(struct arizona_fll *fll, int source,
+int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags);
+int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags);
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+void arizona_init_dvfs(struct arizona_priv *priv);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base,
+ int lock_irq, int ok_irq, struct arizona_fll *fll);
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout);
-extern int arizona_init_spk(struct snd_soc_codec *codec);
-extern int arizona_init_gpio(struct snd_soc_codec *codec);
-extern int arizona_init_mono(struct snd_soc_codec *codec);
+int arizona_init_spk(struct snd_soc_component *component);
+int arizona_init_gpio(struct snd_soc_component *component);
+int arizona_init_mono(struct snd_soc_component *component);
-extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+int arizona_init_common(struct arizona *arizona);
+int arizona_init_vol_limit(struct arizona *arizona);
-int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+int arizona_init_spk_irqs(struct arizona *arizona);
+int arizona_free_spk_irqs(struct arizona *arizona);
+
+int arizona_init_dai(struct arizona_priv *priv, int id);
+
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
bool diff);
-extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+bool arizona_input_analog(struct snd_soc_component *component, int shift);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+static inline int arizona_register_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb,
+ int (*notify)
+ (struct notifier_block *nb,
+ unsigned long action, void *data))
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+
+ nb->notifier_call = notify;
+
+ return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
+
+static inline int arizona_unregister_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb)
+{
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->arizona;
+
+ return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
+
+int arizona_of_get_audio_pdata(struct arizona *arizona);
+
+int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev);
+int arizona_jack_codec_dev_remove(struct arizona_priv *info);
+
+int arizona_jack_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data);
-extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
#endif
diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c
new file mode 100644
index 000000000000..066e401912b0
--- /dev/null
+++ b/sound/soc/codecs/audio-iio-aux.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC glue to use IIO devices as audio components
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/cleanup.h>
+#include <linux/iio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct audio_iio_aux_chan {
+ struct iio_channel *iio_chan;
+ const char *name;
+ int max;
+ int min;
+ bool is_invert_range;
+};
+
+struct audio_iio_aux {
+ struct device *dev;
+ unsigned int num_chans;
+ struct audio_iio_aux_chan chans[] __counted_by(num_chans);
+};
+
+static int audio_iio_aux_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chan->max - chan->min;
+ uinfo->type = (uinfo->value.integer.max == 1) ?
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ return 0;
+}
+
+static int audio_iio_aux_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+ int max = chan->max;
+ int min = chan->min;
+ bool invert_range = chan->is_invert_range;
+ int ret;
+ int val;
+
+ ret = iio_read_channel_raw(chan->iio_chan, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.integer.value[0] = val - min;
+ if (invert_range)
+ ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int audio_iio_aux_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+ int max = chan->max;
+ int min = chan->min;
+ bool invert_range = chan->is_invert_range;
+ int val;
+ int ret;
+ int tmp;
+
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ return -EINVAL;
+ if (val > max - min)
+ return -EINVAL;
+
+ val = val + min;
+ if (invert_range)
+ val = max - val;
+
+ ret = iio_read_channel_raw(chan->iio_chan, &tmp);
+ if (ret < 0)
+ return ret;
+
+ if (tmp == val)
+ return 0;
+
+ ret = iio_write_channel_raw(chan->iio_chan, val);
+ if (ret)
+ return ret;
+
+ return 1; /* The value changed */
+}
+
+static int audio_iio_aux_add_controls(struct snd_soc_component *component,
+ struct audio_iio_aux_chan *chan)
+{
+ struct snd_kcontrol_new control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = chan->name,
+ .info = audio_iio_aux_info_volsw,
+ .get = audio_iio_aux_get_volsw,
+ .put = audio_iio_aux_put_volsw,
+ .private_value = (unsigned long)chan,
+ };
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+/*
+ * These data could be on stack but they are pretty big.
+ * As ASoC internally copy them and protect them against concurrent accesses
+ * (snd_soc_bind_card() protects using client_mutex), keep them in the global
+ * data area.
+ */
+static struct snd_soc_dapm_widget widgets[3];
+static struct snd_soc_dapm_route routes[2];
+
+/* Be sure sizes are correct (need 3 widgets and 2 routes) */
+static_assert(ARRAY_SIZE(widgets) >= 3, "3 widgets are needed");
+static_assert(ARRAY_SIZE(routes) >= 2, "2 routes are needed");
+
+static int audio_iio_aux_add_dapms(struct snd_soc_component *component,
+ struct audio_iio_aux_chan *chan)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ /* Allocated names are not needed afterwards (duplicated in ASoC internals) */
+ char *input_name __free(kfree) = kasprintf(GFP_KERNEL, "%s IN", chan->name);
+ if (!input_name)
+ return -ENOMEM;
+
+ char *output_name __free(kfree) = kasprintf(GFP_KERNEL, "%s OUT", chan->name);
+ if (!output_name)
+ return -ENOMEM;
+
+ char *pga_name __free(kfree) = kasprintf(GFP_KERNEL, "%s PGA", chan->name);
+ if (!pga_name)
+ return -ENOMEM;
+
+ widgets[0] = SND_SOC_DAPM_INPUT(input_name);
+ widgets[1] = SND_SOC_DAPM_OUTPUT(output_name);
+ widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0);
+ ret = snd_soc_dapm_new_controls(dapm, widgets, 3);
+ if (ret)
+ return ret;
+
+ routes[0].sink = pga_name;
+ routes[0].control = NULL;
+ routes[0].source = input_name;
+ routes[1].sink = output_name;
+ routes[1].control = NULL;
+ routes[1].source = pga_name;
+
+ return snd_soc_dapm_add_routes(dapm, routes, 2);
+}
+
+static int audio_iio_aux_component_probe(struct snd_soc_component *component)
+{
+ struct audio_iio_aux *iio_aux = snd_soc_component_get_drvdata(component);
+ struct audio_iio_aux_chan *chan;
+ int ret;
+ int i;
+
+ for (i = 0; i < iio_aux->num_chans; i++) {
+ chan = iio_aux->chans + i;
+
+ ret = iio_read_max_channel_raw(chan->iio_chan, &chan->max);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot get max raw value\n",
+ i, chan->name);
+
+ ret = iio_read_min_channel_raw(chan->iio_chan, &chan->min);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot get min raw value\n",
+ i, chan->name);
+
+ if (chan->min > chan->max) {
+ /*
+ * This should never happen but to avoid any check
+ * later, just swap values here to ensure that the
+ * minimum value is lower than the maximum value.
+ */
+ dev_dbg(component->dev, "chan[%d] %s: Swap min and max\n",
+ i, chan->name);
+ swap(chan->min, chan->max);
+ }
+
+ /* Set initial value */
+ ret = iio_write_channel_raw(chan->iio_chan,
+ chan->is_invert_range ? chan->max : chan->min);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot set initial value\n",
+ i, chan->name);
+
+ ret = audio_iio_aux_add_controls(component, chan);
+ if (ret)
+ return ret;
+
+ ret = audio_iio_aux_add_dapms(component, chan);
+ if (ret)
+ return ret;
+
+ dev_dbg(component->dev, "chan[%d]: Added %s (min=%d, max=%d, invert=%s)\n",
+ i, chan->name, chan->min, chan->max,
+ str_on_off(chan->is_invert_range));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver audio_iio_aux_component_driver = {
+ .probe = audio_iio_aux_component_probe,
+};
+
+static int audio_iio_aux_probe(struct platform_device *pdev)
+{
+ struct audio_iio_aux_chan *iio_aux_chan;
+ struct device *dev = &pdev->dev;
+ struct audio_iio_aux *iio_aux;
+ int count;
+ int ret;
+ int i;
+
+ count = device_property_string_array_count(dev, "io-channel-names");
+ if (count < 0)
+ return dev_err_probe(dev, count, "failed to count io-channel-names\n");
+
+ iio_aux = devm_kzalloc(dev, struct_size(iio_aux, chans, count), GFP_KERNEL);
+ if (!iio_aux)
+ return -ENOMEM;
+
+ iio_aux->dev = dev;
+
+ iio_aux->num_chans = count;
+
+ const char **names __free(kfree) = kcalloc(iio_aux->num_chans,
+ sizeof(*names),
+ GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
+
+ u32 *invert_ranges __free(kfree) = kcalloc(iio_aux->num_chans,
+ sizeof(*invert_ranges),
+ GFP_KERNEL);
+ if (!invert_ranges)
+ return -ENOMEM;
+
+ ret = device_property_read_string_array(dev, "io-channel-names",
+ names, iio_aux->num_chans);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read io-channel-names\n");
+
+ /*
+ * snd-control-invert-range is optional and can contain fewer items
+ * than the number of channels. Unset values default to 0.
+ */
+ count = device_property_count_u32(dev, "snd-control-invert-range");
+ if (count > 0) {
+ count = min_t(unsigned int, count, iio_aux->num_chans);
+ ret = device_property_read_u32_array(dev, "snd-control-invert-range",
+ invert_ranges, count);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n");
+ }
+
+ for (i = 0; i < iio_aux->num_chans; i++) {
+ iio_aux_chan = iio_aux->chans + i;
+ iio_aux_chan->name = names[i];
+ iio_aux_chan->is_invert_range = invert_ranges[i];
+
+ iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name);
+ if (IS_ERR(iio_aux_chan->iio_chan))
+ return dev_err_probe(dev, PTR_ERR(iio_aux_chan->iio_chan),
+ "get IIO channel '%s' failed\n",
+ iio_aux_chan->name);
+ }
+
+ platform_set_drvdata(pdev, iio_aux);
+
+ return devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver,
+ NULL, 0);
+}
+
+static const struct of_device_id audio_iio_aux_ids[] = {
+ { .compatible = "audio-iio-aux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, audio_iio_aux_ids);
+
+static struct platform_driver audio_iio_aux_driver = {
+ .driver = {
+ .name = "audio-iio-aux",
+ .of_match_table = audio_iio_aux_ids,
+ },
+ .probe = audio_iio_aux_probe,
+};
+module_platform_driver(audio_iio_aux_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("IIO ALSA SoC aux driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/aw8738.c b/sound/soc/codecs/aw8738.c
new file mode 100644
index 000000000000..0fe8af160319
--- /dev/null
+++ b/sound/soc/codecs/aw8738.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+struct aw8738_priv {
+ struct gpio_desc *gpiod_mode;
+ unsigned int mode;
+};
+
+static int aw8738_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct aw8738_priv *aw = snd_soc_component_get_drvdata(c);
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ for (i = 0; i < aw->mode; i++) {
+ gpiod_set_value_cansleep(aw->gpiod_mode, 0);
+ udelay(2);
+ gpiod_set_value_cansleep(aw->gpiod_mode, 1);
+ udelay(2);
+ }
+ msleep(40);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ gpiod_set_value_cansleep(aw->gpiod_mode, 0);
+ usleep_range(1000, 2000);
+ break;
+ default:
+ WARN(1, "Unexpected event");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw8738_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, aw8738_drv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route aw8738_dapm_routes[] = {
+ { "DRV", NULL, "IN" },
+ { "OUT", NULL, "DRV" },
+};
+
+static const struct snd_soc_component_driver aw8738_component_driver = {
+ .dapm_widgets = aw8738_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw8738_dapm_widgets),
+ .dapm_routes = aw8738_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw8738_dapm_routes),
+};
+
+static int aw8738_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aw8738_priv *aw;
+ int ret;
+
+ aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL);
+ if (!aw)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, aw);
+
+ aw->gpiod_mode = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW);
+ if (IS_ERR(aw->gpiod_mode))
+ return dev_err_probe(dev, PTR_ERR(aw->gpiod_mode),
+ "Failed to get 'mode' gpio");
+
+ ret = device_property_read_u32(dev, "awinic,mode", &aw->mode);
+ if (ret)
+ return -EINVAL;
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &aw8738_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aw8738_of_match[] = {
+ { .compatible = "awinic,aw8738" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw8738_of_match);
+#endif
+
+static struct platform_driver aw8738_driver = {
+ .probe = aw8738_probe,
+ .driver = {
+ .name = "aw8738",
+ .of_match_table = of_match_ptr(aw8738_of_match),
+ },
+};
+module_platform_driver(aw8738_driver);
+
+MODULE_DESCRIPTION("Awinic AW8738 Amplifier Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c
new file mode 100644
index 000000000000..d7fd865c349f
--- /dev/null
+++ b/sound/soc/codecs/aw87390.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw87390.c -- AW87390 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw87390.h"
+#include "aw88395/aw88395_data_type.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw87390_remap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = AW87390_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int aw87390_dev_reg_update(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int i, ret;
+
+ if (!data) {
+ dev_err(aw_dev->dev, "data is NULL\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len-1; i += 2) {
+ if (data[i] == AW87390_DELAY_REG_ADDR) {
+ usleep_range(data[i + 1] * AW87390_REG_DELAY_TIME,
+ data[i + 1] * AW87390_REG_DELAY_TIME + 10);
+ continue;
+ }
+ ret = regmap_write(aw_dev->regmap, data[i], data[i + 1]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw87390_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]\n",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw87390_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+
+static int aw87390_dev_fw_update(struct aw_device *aw_dev)
+{
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw87390_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw87390_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw87390_dev_get_prof_data failed\n");
+ return ret;
+ }
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw87390_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed\n");
+ return ret;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static int aw87390_power_off(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW87390_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off\n");
+ return 0;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE);
+ if (ret)
+ return ret;
+ aw_dev->status = AW87390_DEV_PW_OFF;
+
+ return 0;
+}
+
+static int aw87390_power_on(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW87390_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on\n");
+ return 0;
+ }
+
+ if (!aw_dev->fw_status) {
+ dev_err(aw_dev->dev, "fw not load\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw87390_dev_fw_update(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s load profile failed\n", __func__);
+ return ret;
+ }
+ aw_dev->status = AW87390_DEV_PW_ON;
+
+ return 0;
+}
+
+static int aw87390_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+
+ if (aw_dev->prof_index == index)
+ return -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw87390_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw87390->aw_pa->prof_info.count;
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw87390_dev_get_prof_name(aw87390->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw87390_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw87390->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw87390_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw87390->lock);
+ ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change\n");
+ mutex_unlock(&aw87390->lock);
+ return 0;
+ }
+
+ if (aw87390->aw_pa->status == AW87390_DEV_PW_ON) {
+ aw87390_power_off(aw87390->aw_pa);
+ aw87390_power_on(aw87390->aw_pa);
+ }
+
+ mutex_unlock(&aw87390->lock);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new aw87390_controls[] = {
+ AW87390_PROFILE_EXT("AW87390 Profile Set", aw87390_profile_info,
+ aw87390_profile_get, aw87390_profile_set),
+};
+
+static int aw87390_request_firmware_file(struct aw87390 *aw87390)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW87390_ACF_FILE, aw87390->aw_pa->dev);
+ if (ret)
+ return dev_err_probe(aw87390->aw_pa->dev, ret,
+ "load [%s] failed!\n", AW87390_ACF_FILE);
+
+ dev_dbg(aw87390->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW87390_ACF_FILE, cont ? cont->size : 0);
+
+ aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev,
+ struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw87390->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+
+ aw87390->aw_cfg->len = cont->size;
+ memcpy(aw87390->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg);
+ if (ret) {
+ dev_err(aw87390->aw_pa->dev, "load [%s] failed!\n", AW87390_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw87390->lock);
+
+ ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg);
+ if (ret)
+ dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n");
+
+ mutex_unlock(&aw87390->lock);
+
+ return ret;
+}
+
+static int aw87390_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw87390->aw_pa;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = aw87390_power_on(aw_dev);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = aw87390_power_off(aw_dev);
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route aw87390_dapm_routes[] = {
+ { "SPK PA", NULL, "IN" },
+ { "OUT", NULL, "SPK PA" },
+};
+
+static int aw87390_codec_probe(struct snd_soc_component *component)
+{
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = aw87390_request_firmware_file(aw87390);
+ if (ret)
+ return dev_err_probe(aw87390->aw_pa->dev, ret,
+ "aw87390_request_firmware_file failed\n");
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
+ .probe = aw87390_codec_probe,
+ .dapm_widgets = aw87390_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw87390_dapm_widgets),
+ .dapm_routes = aw87390_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes),
+ .controls = aw87390_controls,
+ .num_controls = ARRAY_SIZE(aw87390_controls),
+};
+
+static void aw87390_parse_channel_dt(struct aw87390 *aw87390)
+{
+ struct aw_device *aw_dev = aw87390->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW87390_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW87390_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (chip_id != AW87390_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ aw87390->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = AW87390_CHIP_ID;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = AW87390_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW87390_DEV_FW_FAILED;
+ aw_dev->prof_index = AW87390_INIT_PROFILE;
+ aw_dev->status = AW87390_DEV_PW_OFF;
+
+ aw87390_parse_channel_dt(aw87390);
+
+ return 0;
+}
+
+static int aw87390_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw87390 *aw87390;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n");
+
+ aw87390 = devm_kzalloc(&i2c->dev, sizeof(*aw87390), GFP_KERNEL);
+ if (!aw87390)
+ return -ENOMEM;
+
+ mutex_init(&aw87390->lock);
+
+ i2c_set_clientdata(i2c, aw87390);
+
+ aw87390->regmap = devm_regmap_init_i2c(i2c, &aw87390_remap_config);
+ if (IS_ERR(aw87390->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw87390->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw87390_init(aw87390, i2c, aw87390->regmap);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(aw87390->regmap, AW87390_ID_REG, AW87390_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw87390, NULL, 0);
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw87390_i2c_id[] = {
+ { AW87390_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
+
+static struct i2c_driver aw87390_i2c_driver = {
+ .driver = {
+ .name = AW87390_I2C_NAME,
+ },
+ .probe = aw87390_i2c_probe,
+ .id_table = aw87390_i2c_id,
+};
+module_i2c_driver(aw87390_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW87390 PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h
new file mode 100644
index 000000000000..d0d049e65991
--- /dev/null
+++ b/sound/soc/codecs/aw87390.h
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw87390.h -- aw87390 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW87390_H__
+#define __AW87390_H__
+
+#define AW87390_ID_REG (0x00)
+#define AW87390_SYSCTRL_REG (0x01)
+#define AW87390_MDCTRL_REG (0x02)
+#define AW87390_CPOVP_REG (0x03)
+#define AW87390_CPP_REG (0x04)
+#define AW87390_PAG_REG (0x05)
+#define AW87390_AGC3P_REG (0x06)
+#define AW87390_AGC3PA_REG (0x07)
+#define AW87390_AGC2P_REG (0x08)
+#define AW87390_AGC2PA_REG (0x09)
+#define AW87390_AGC1PA_REG (0x0A)
+#define AW87390_SYSST_REG (0x59)
+#define AW87390_SYSINT_REG (0x60)
+#define AW87390_DFT_SYSCTRL_REG (0x61)
+#define AW87390_DFT_MDCTRL_REG (0x62)
+#define AW87390_DFT_CPADP_REG (0x63)
+#define AW87390_DFT_AGCPA_REG (0x64)
+#define AW87390_DFT_POFR_REG (0x65)
+#define AW87390_DFT_OC_REG (0x66)
+#define AW87390_DFT_ADP1_REG (0x67)
+#define AW87390_DFT_REF_REG (0x68)
+#define AW87390_DFT_LDO_REG (0x69)
+#define AW87390_ADP1_REG (0x70)
+#define AW87390_ADP2_REG (0x71)
+#define AW87390_NG1_REG (0x72)
+#define AW87390_NG2_REG (0x73)
+#define AW87390_NG3_REG (0x74)
+#define AW87390_CP_REG (0x75)
+#define AW87390_AB_REG (0x76)
+#define AW87390_TEST_REG (0x77)
+#define AW87390_ENCR_REG (0x78)
+#define AW87390_DELAY_REG_ADDR (0xFE)
+
+#define AW87390_SOFT_RESET_VALUE (0xAA)
+#define AW87390_POWER_DOWN_VALUE (0x00)
+#define AW87390_REG_MAX (0xFF)
+#define AW87390_DEV_DEFAULT_CH (0)
+#define AW87390_INIT_PROFILE (0)
+#define AW87390_REG_DELAY_TIME (1000)
+#define AW87390_I2C_NAME "aw87390"
+#define AW87390_ACF_FILE "aw87390_acf.bin"
+
+#define AW87390_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum aw87390_id {
+ AW87390_CHIP_ID = 0x76,
+};
+
+enum {
+ AW87390_DEV_FW_FAILED = 0,
+ AW87390_DEV_FW_OK,
+};
+
+enum {
+ AW87390_DEV_PW_OFF = 0,
+ AW87390_DEV_PW_ON,
+};
+
+struct aw87390 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c
new file mode 100644
index 000000000000..fbd1fd12381a
--- /dev/null
+++ b/sound/soc/codecs/aw88081.c
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88081.c -- AW88081 ALSA SoC Audio driver
+//
+// Copyright (c) 2024 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88081.h"
+#include "aw88395/aw88395_device.h"
+
+enum aw8808x_type {
+ AW88081,
+ AW88083,
+};
+
+struct aw88081 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+ enum aw8808x_type devtype;
+ bool phase_sync;
+};
+
+static const struct regmap_config aw88081_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88081_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config aw88083_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88083_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int aw88081_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88081_BIT_PLL_CHECK) != AW88081_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw88081_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88081_CCO_MUX_MASK);
+ if (reg_val == AW88081_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88081_PLLCTRL1_REG,
+ ~AW88081_CCO_MUX_MASK, AW88081_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88081_PLLCTRL1_REG,
+ ~AW88081_CCO_MUX_MASK, AW88081_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw88081_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw88081_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ unsigned int value;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_PWMCTRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88081_NOISE_GATE_EN_MASK))
+ check_val = AW88081_NO_SWS_SYSST_CHECK;
+ else
+ check_val = AW88081_SWS_SYSST_CHECK;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88081_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ value = reg_val & (~AW88081_BIT_SYSST_CHECK_MASK) & check_val;
+ if (value != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ reg_val, check_val);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw88081_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88081_I2SCTRL3_REG,
+ ~AW88081_I2STXEN_MASK, AW88081_I2STXEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_I2SCTRL3_REG,
+ ~AW88081_I2STXEN_MASK, AW88081_I2STXEN_DISABLE_VALUE);
+}
+
+static void aw88081_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ if (pwd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_PWDN_MASK, AW88081_PWDN_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_PWDN_MASK, AW88081_PWDN_WORKING_VALUE);
+}
+
+static void aw88081_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_EN_PA_MASK, AW88081_EN_PA_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_EN_PA_MASK, AW88081_EN_PA_WORKING_VALUE);
+}
+
+static void aw88083_i2c_wen(struct aw88081 *aw88081, bool flag)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw88081->devtype != AW88083)
+ return;
+
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_DISABLE_VALUE);
+}
+
+static void aw88083_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_AMPPD_MASK, AW88083_AMPPD_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_AMPPD_MASK, AW88083_AMPPD_WORKING_VALUE);
+}
+
+static void aw88083_dev_pllpd(struct aw_device *aw_dev, bool pllpd)
+{
+ if (pllpd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_WORKING_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_POWER_DOWN_VALUE);
+}
+
+static void aw88081_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ unsigned int int_status;
+
+ /* read int status and clear */
+ regmap_read(aw_dev->regmap, AW88081_SYSINT_REG, &int_status);
+ /* make sure int status is clear */
+ regmap_read(aw_dev->regmap, AW88081_SYSINT_REG, &int_status);
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
+}
+
+static void aw88081_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int volume;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88081_MUTE_VOL);
+
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL2_REG, ~AW88081_VOL_MASK, volume);
+}
+
+static void aw88081_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw88081_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88081_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw88081_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw88081_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw88081_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw88081_dev_set_volume(aw_dev, AW88081_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88081_MUTE_VOL; i += fade_step) {
+ aw88081_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88081_MUTE_VOL)
+ aw88081_dev_set_volume(aw_dev, AW88081_MUTE_VOL);
+}
+
+static void aw88081_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw88081_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_HMUTE_MASK, AW88081_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_HMUTE_MASK, AW88081_HMUTE_DISABLE_VALUE);
+ aw88081_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88081_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+ if (uls_hmute)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_ULS_HMUTE_MASK,
+ AW88081_ULS_HMUTE_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_ULS_HMUTE_MASK,
+ AW88081_ULS_HMUTE_DISABLE_VALUE);
+}
+
+static int aw88081_dev_reg_value_check(struct aw_device *aw_dev,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ unsigned int read_vol;
+
+ if (reg_addr == AW88081_SYSCTRL_REG) {
+ *reg_val &= ~(~AW88081_EN_PA_MASK |
+ ~AW88081_PWDN_MASK |
+ ~AW88081_HMUTE_MASK |
+ ~AW88081_ULS_HMUTE_MASK);
+
+ *reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE |
+ AW88081_PWDN_POWER_DOWN_VALUE |
+ AW88081_HMUTE_ENABLE_VALUE |
+ AW88081_ULS_HMUTE_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88081_SYSCTRL2_REG) {
+ read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ /* i2stxen */
+ if (reg_addr == AW88081_I2SCTRL3_REG) {
+ /* close tx */
+ *reg_val &= AW88081_I2STXEN_MASK;
+ *reg_val |= AW88081_I2STXEN_DISABLE_VALUE;
+ }
+
+ return 0;
+}
+
+static int aw88083_dev_reg_value_check(struct aw_device *aw_dev,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ unsigned int read_vol;
+
+ if (reg_addr == AW88081_SYSCTRL_REG) {
+ *reg_val &= ~(~AW88083_AMPPD_MASK |
+ ~AW88081_PWDN_MASK |
+ ~AW88081_HMUTE_MASK |
+ ~AW88083_I2C_WEN_MASK);
+
+ *reg_val |= AW88083_AMPPD_POWER_DOWN_VALUE |
+ AW88081_PWDN_POWER_DOWN_VALUE |
+ AW88081_HMUTE_ENABLE_VALUE |
+ AW88083_I2C_WEN_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88081_SYSCTRL2_REG) {
+ read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ return 0;
+}
+
+static int aw88081_reg_value_check(struct aw88081 *aw88081,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_reg_value_check(aw_dev, reg_addr, reg_val);
+ break;
+ case AW88083:
+ ret = aw88083_dev_reg_value_check(aw_dev, reg_addr, reg_val);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported device\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_reg_update(struct aw88081 *aw88081,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u16 reg_val;
+ u8 reg_addr;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ ret = aw88081_reg_value_check(aw88081, reg_addr, &reg_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+
+ /* keep min volume */
+ aw88081_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+ return 0;
+}
+
+static int aw88081_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88081_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+
+static int aw88081_dev_fw_update(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw88081_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88081_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw88081_dev_reg_update(aw88081, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static int aw88081_dev_start(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88081_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ /* power on */
+ aw88081_dev_pwd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+
+ ret = aw88081_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw88081_dev_amppd(aw_dev, false);
+ usleep_range(AW88081_1000_US, AW88081_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw88081_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ /* enable tx feedback */
+ aw88081_dev_i2s_tx_enable(aw_dev, true);
+
+ /* close uls mute */
+ aw88081_dev_uls_hmute(aw_dev, false);
+
+ /* close mute */
+ aw88081_dev_mute(aw_dev, false);
+
+ /* clear inturrupt */
+ aw88081_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88081_DEV_PW_ON;
+
+ return 0;
+
+sysst_check_fail:
+ aw88081_dev_i2s_tx_enable(aw_dev, false);
+ aw88081_dev_clear_int_status(aw_dev);
+ aw88081_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw88081_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw88083_dev_start(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88083_i2c_wen(aw88081, true);
+
+ /* power on */
+ aw88081_dev_pwd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+
+ aw88083_dev_pllpd(aw_dev, true);
+ /* amppd on */
+ aw88083_dev_amppd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 50);
+
+ /* close mute */
+ aw88081_dev_mute(aw_dev, false);
+
+ aw88083_i2c_wen(aw88081, false);
+
+ aw_dev->status = AW88081_DEV_PW_ON;
+
+ return 0;
+}
+
+static int aw88081_device_start(struct aw88081 *aw88081)
+{
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_start(aw88081);
+ break;
+ case AW88083:
+ ret = aw88083_dev_start(aw88081);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(aw88081->aw_pa->dev, "unsupported device\n");
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_stop(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ /* clear inturrupt */
+ aw88081_dev_clear_int_status(aw_dev);
+
+ aw88081_dev_uls_hmute(aw_dev, true);
+ /* set mute */
+ aw88081_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw88081_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88081_1000_US, AW88081_1000_US + 100);
+
+ /* enable amppd */
+ aw88081_dev_amppd(aw_dev, true);
+
+ /* set power down */
+ aw88081_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88083_dev_stop(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ aw88083_i2c_wen(aw88081, true);
+ /* set mute */
+ aw88081_dev_mute(aw_dev, true);
+
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 100);
+
+ /* enable amppd */
+ aw88083_dev_amppd(aw_dev, true);
+
+ aw88083_dev_pllpd(aw_dev, false);
+
+ /* set power down */
+ aw88081_dev_pwd(aw_dev, true);
+
+ aw88083_i2c_wen(aw88081, false);
+
+ return 0;
+}
+
+static int aw88081_stop(struct aw88081 *aw88081)
+{
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_stop(aw88081);
+ break;
+ case AW88083:
+ ret = aw88083_dev_stop(aw88081);
+ break;
+ default:
+ dev_err(aw88081->aw_pa->dev, "unsupported device\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_reg_update(struct aw88081 *aw88081, bool force)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ if (force) {
+ ret = regmap_write(aw_dev->regmap,
+ AW88081_ID_REG, AW88081_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret)
+ return ret;
+ } else {
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret)
+ return ret;
+ }
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static void aw88081_start_pa(struct aw88081 *aw88081)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88081_START_RETRIES; i++) {
+ ret = aw88081_reg_update(aw88081, aw88081->phase_sync);
+ if (ret) {
+ dev_err(aw88081->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ continue;
+ }
+ ret = aw88081_device_start(aw88081);
+ if (ret) {
+ dev_err(aw88081->aw_pa->dev, "aw88081 device start failed. retry = %d", i);
+ continue;
+ } else {
+ dev_dbg(aw88081->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88081_startup_work(struct work_struct *work)
+{
+ struct aw88081 *aw88081 =
+ container_of(work, struct aw88081, start_work.work);
+
+ mutex_lock(&aw88081->lock);
+ aw88081_start_pa(aw88081);
+ mutex_unlock(&aw88081->lock);
+}
+
+static void aw88081_start(struct aw88081 *aw88081, bool sync_start)
+{
+ if (aw88081->aw_pa->fw_status != AW88081_DEV_FW_OK)
+ return;
+
+ if (aw88081->aw_pa->status == AW88081_DEV_PW_ON)
+ return;
+
+ if (sync_start == AW88081_SYNC_START)
+ aw88081_start_pa(aw88081);
+ else
+ queue_delayed_work(system_dfl_wq,
+ &aw88081->start_work,
+ AW88081_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88081_dai[] = {
+ {
+ .name = "aw88081-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88081_RATES,
+ .formats = AW88081_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88081_RATES,
+ .formats = AW88081_FORMATS,
+ },
+ },
+};
+
+static int aw88081_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88081_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88081_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88081_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw88081_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88081->aw_pa->prof_info.count;
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88081_dev_get_prof_name(aw88081->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88081_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88081->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88081_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88081->lock);
+ ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88081->lock);
+ return 0;
+ }
+
+ if (aw88081->aw_pa->status) {
+ aw88081_stop(aw88081);
+ aw88081_start(aw88081, AW88081_SYNC_START);
+ }
+
+ mutex_unlock(&aw88081->lock);
+
+ return 1;
+}
+
+static int aw88081_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88081->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88081_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88081->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ aw88083_i2c_wen(aw88081, true);
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88081_dev_set_volume(aw88081->aw_pa, vol_desc->ctl_volume);
+ return 1;
+ }
+
+ aw88083_i2c_wen(aw88081, false);
+
+ return 0;
+}
+
+static int aw88081_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88081->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88081_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88081->aw_pa->fade_step != value) {
+ aw88081->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88081_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88081_SYSCTRL2_REG,
+ 0, AW88081_MUTE_VOL, 0, aw88081_volume_get,
+ aw88081_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88081_MUTE_VOL, 0,
+ aw88081_get_fade_step, aw88081_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, 0,
+ aw88081_get_fade_in_time, aw88081_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, 0,
+ aw88081_get_fade_out_time, aw88081_set_fade_out_time),
+ AW88081_PROFILE_EXT("Profile Set", aw88081_profile_info,
+ aw88081_profile_get, aw88081_profile_set),
+};
+
+static void aw88081_parse_channel_dt(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW88081_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw88081->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw88081_init(struct aw88081 *aw88081, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW88081_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case AW88081_CHIP_ID:
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+ break;
+ case AW88083_CHIP_ID:
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+ break;
+ default:
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ aw88081->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = chip_id;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->fade_step = AW88081_VOLUME_STEP_DB;
+ aw_dev->volume_desc.mute_volume = AW88081_MUTE_VOL;
+ aw88081_parse_channel_dt(aw88081);
+
+ return 0;
+}
+
+static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88081_ID_REG, AW88081_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ aw_dev->fade_in_time = AW88081_500_US;
+ aw_dev->fade_out_time = AW88081_500_US;
+ aw_dev->prof_cur = AW88081_INIT_PROFILE;
+ aw_dev->prof_index = AW88081_INIT_PROFILE;
+
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_ON;
+ aw88081_stop(aw88081);
+
+ return 0;
+}
+
+static int aw88081_request_firmware_file(struct aw88081 *aw88081)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88081->aw_pa->fw_status = AW88081_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88081_ACF_FILE, aw88081->aw_pa->dev);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw88081->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88081_ACF_FILE, cont ? cont->size : 0);
+
+ aw88081->aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88081->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88081->aw_cfg->len = (int)cont->size;
+ memcpy(aw88081->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg);
+ if (ret)
+ return ret;
+
+ mutex_lock(&aw88081->lock);
+ ret = aw88081_dev_init(aw88081, aw88081->aw_cfg);
+ mutex_unlock(&aw88081->lock);
+
+ return ret;
+}
+
+static int aw88081_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88081->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88081_start(aw88081, AW88081_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88081_stop(aw88081);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88081->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88081_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0,
+ aw88081_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88081_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88081_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88081->start_work, aw88081_startup_work);
+
+ ret = aw88081_request_firmware_file(aw88081);
+ if (ret)
+ dev_err(aw88081->aw_pa->dev, "%s: request firmware failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88081_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88081->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88081 = {
+ .probe = aw88081_codec_probe,
+ .remove = aw88081_codec_remove,
+ .dapm_widgets = aw88081_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88081_dapm_widgets),
+ .dapm_routes = aw88081_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88081_audio_map),
+ .controls = aw88081_controls,
+ .num_controls = ARRAY_SIZE(aw88081_controls),
+};
+
+static const struct i2c_device_id aw88081_i2c_id[] = {
+ { AW88081_I2C_NAME, AW88081},
+ { AW88083_I2C_NAME, AW88083},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id);
+
+static int aw88081_i2c_probe(struct i2c_client *i2c)
+{
+ const struct regmap_config *regmap_config;
+ const struct i2c_device_id *id;
+ struct aw88081 *aw88081;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88081 = devm_kzalloc(&i2c->dev, sizeof(*aw88081), GFP_KERNEL);
+ if (!aw88081)
+ return -ENOMEM;
+
+ id = i2c_match_id(aw88081_i2c_id, i2c);
+ aw88081->devtype = id->driver_data;
+
+ mutex_init(&aw88081->lock);
+
+ i2c_set_clientdata(i2c, aw88081);
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ regmap_config = &aw88081_regmap_config;
+ break;
+ case AW88083:
+ regmap_config = &aw88083_regmap_config;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aw88081->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(aw88081->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88081->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88081_init(aw88081, i2c, aw88081->regmap);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88081,
+ aw88081_dai, ARRAY_SIZE(aw88081_dai));
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id aw88081_of_match[] = {
+ { .compatible = "awinic,aw88081" },
+ { .compatible = "awinic,aw88083" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw88081_of_match);
+#endif
+
+static struct i2c_driver aw88081_i2c_driver = {
+ .driver = {
+ .name = AW88081_I2C_NAME,
+ .of_match_table = of_match_ptr(aw88081_of_match),
+ },
+ .probe = aw88081_i2c_probe,
+ .id_table = aw88081_i2c_id,
+};
+module_i2c_driver(aw88081_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88081 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88081.h b/sound/soc/codecs/aw88081.h
new file mode 100644
index 000000000000..7a4564270ab3
--- /dev/null
+++ b/sound/soc/codecs/aw88081.h
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88081.h -- AW88081 ALSA SoC Audio driver
+//
+// Copyright (c) 2024 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88081_H__
+#define __AW88081_H__
+
+#define AW88081_ID_REG (0x00)
+#define AW88081_SYSST_REG (0x01)
+#define AW88081_SYSINT_REG (0x02)
+#define AW88081_SYSINTM_REG (0x03)
+#define AW88081_SYSCTRL_REG (0x04)
+#define AW88081_SYSCTRL2_REG (0x05)
+#define AW88081_I2SCTRL1_REG (0x06)
+#define AW88081_I2SCTRL2_REG (0x07)
+#define AW88081_I2SCTRL3_REG (0x08)
+#define AW88081_DACCFG1_REG (0x09)
+#define AW88081_DACCFG2_REG (0x0A)
+#define AW88081_DACCFG3_REG (0x0B)
+#define AW88081_DACCFG4_REG (0x0C)
+#define AW88081_DACCFG5_REG (0x0D)
+#define AW88081_DACCFG6_REG (0x0E)
+#define AW88081_DACCFG7_REG (0x11)
+#define AW88081_PWMCTRL1_REG (0x13)
+#define AW88081_PWMCTRL2_REG (0x14)
+#define AW88081_PWMCTRL3_REG (0x15)
+#define AW88081_PWMCTRL4_REG (0x16)
+#define AW88081_I2SCFG1_REG (0x17)
+#define AW88081_DBGCTRL_REG (0x18)
+#define AW88081_PDMCTRL_REG (0x19)
+#define AW88081_DACST_REG (0x20)
+#define AW88081_PATTERNST_REG (0x21)
+#define AW88081_I2SINT_REG (0x26)
+#define AW88081_I2SCAPCNT_REG (0x27)
+#define AW88081_ANASTA1_REG (0x28)
+#define AW88081_ANASTA2_REG (0x29)
+#define AW88081_ANASTA3_REG (0x2A)
+#define AW88081_VBAT_REG (0x21)
+#define AW88081_TEMP_REG (0x22)
+#define AW88081_PVDD_REG (0x23)
+#define AW88081_ISNDAT_REG (0x24)
+#define AW88081_VSNDAT_REG (0x25)
+#define AW88081_DSMCFG1_REG (0x30)
+#define AW88081_DSMCFG2_REG (0x31)
+#define AW88081_DSMCFG3_REG (0x32)
+#define AW88081_DSMCFG4_REG (0x33)
+#define AW88081_DSMCFG5_REG (0x34)
+#define AW88081_DSMCFG6_REG (0x35)
+#define AW88081_DSMCFG7_REG (0x36)
+#define AW88081_DSMCFG8_REG (0x37)
+#define AW88081_TESTIN_REG (0x38)
+#define AW88081_TESTOUT_REG (0x39)
+#define AW88081_BOPCTRL1_REG (0x40)
+#define AW88081_BOPCTRL2_REG (0x41)
+#define AW88081_BOPCTRL3_REG (0x42)
+#define AW88081_BOPSTA_REG (0x43)
+#define AW88081_PLLCTRL1_REG (0x54)
+#define AW88081_PLLCTRL2_REG (0x55)
+#define AW88081_PLLCTRL3_REG (0x56)
+#define AW88081_CDACTRL1_REG (0x57)
+#define AW88081_CDACTRL2_REG (0x58)
+#define AW88081_CDACTRL3_REG (0x59)
+#define AW88081_DITHERCFG1_REG (0x5A)
+#define AW88081_DITHERCFG2_REG (0x5B)
+#define AW88081_DITHERCFG3_REG (0x5C)
+#define AW88081_TM_REG (0x6E)
+#define AW88081_TM2_REG (0x6F)
+#define AW88081_TESTCTRL1_REG (0x70)
+#define AW88081_TESTCTRL2_REG (0x71)
+
+#define AW88081_REG_MAX (0x72)
+
+#define AW88081_UVLS_START_BIT (14)
+#define AW88081_UVLS_UVLO (1)
+#define AW88081_UVLS_UVLO_VALUE \
+ (AW88081_UVLS_UVLO << AW88081_UVLS_START_BIT)
+
+#define AW88081_SWS_START_BIT (8)
+#define AW88081_SWS_SWITCHING (1)
+#define AW88081_SWS_SWITCHING_VALUE \
+ (AW88081_SWS_SWITCHING << AW88081_SWS_START_BIT)
+
+#define AW88081_NOCLKS_START_BIT (5)
+#define AW88081_NOCLKS_NO_CLOCK (1)
+#define AW88081_NOCLKS_NO_CLOCK_VALUE \
+ (AW88081_NOCLKS_NO_CLOCK << AW88081_NOCLKS_START_BIT)
+
+#define AW88081_CLKS_START_BIT (4)
+#define AW88081_CLKS_STABLE (1)
+#define AW88081_CLKS_STABLE_VALUE \
+ (AW88081_CLKS_STABLE << AW88081_CLKS_START_BIT)
+
+#define AW88081_OCDS_START_BIT (3)
+#define AW88081_OCDS_OC (1)
+#define AW88081_OCDS_OC_VALUE \
+ (AW88081_OCDS_OC << AW88081_OCDS_START_BIT)
+
+#define AW88081_OTHS_START_BIT (1)
+#define AW88081_OTHS_OT (1)
+#define AW88081_OTHS_OT_VALUE \
+ (AW88081_OTHS_OT << AW88081_OTHS_START_BIT)
+
+#define AW88081_PLLS_START_BIT (0)
+#define AW88081_PLLS_LOCKED (1)
+#define AW88081_PLLS_LOCKED_VALUE \
+ (AW88081_PLLS_LOCKED << AW88081_PLLS_START_BIT)
+
+#define AW88081_BIT_PLL_CHECK \
+ (AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_BIT_SYSST_CHECK_MASK \
+ (~(AW88081_UVLS_UVLO_VALUE | \
+ AW88081_SWS_SWITCHING_VALUE | \
+ AW88081_NOCLKS_NO_CLOCK_VALUE | \
+ AW88081_CLKS_STABLE_VALUE | \
+ AW88081_OCDS_OC_VALUE | \
+ AW88081_OTHS_OT_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE))
+
+#define AW88081_NO_SWS_SYSST_CHECK \
+ (AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_SWS_SYSST_CHECK \
+ (AW88081_SWS_SWITCHING_VALUE | \
+ AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_ULS_HMUTE_START_BIT (14)
+#define AW88081_ULS_HMUTE_BITS_LEN (1)
+#define AW88081_ULS_HMUTE_MASK \
+ (~(((1<<AW88081_ULS_HMUTE_BITS_LEN)-1) << AW88081_ULS_HMUTE_START_BIT))
+
+#define AW88081_ULS_HMUTE_DISABLE (0)
+#define AW88081_ULS_HMUTE_DISABLE_VALUE \
+ (AW88081_ULS_HMUTE_DISABLE << AW88081_ULS_HMUTE_START_BIT)
+
+#define AW88081_ULS_HMUTE_ENABLE (1)
+#define AW88081_ULS_HMUTE_ENABLE_VALUE \
+ (AW88081_ULS_HMUTE_ENABLE << AW88081_ULS_HMUTE_START_BIT)
+
+#define AW88081_HMUTE_START_BIT (8)
+#define AW88081_HMUTE_BITS_LEN (1)
+#define AW88081_HMUTE_MASK \
+ (~(((1<<AW88081_HMUTE_BITS_LEN)-1) << AW88081_HMUTE_START_BIT))
+
+#define AW88081_HMUTE_DISABLE (0)
+#define AW88081_HMUTE_DISABLE_VALUE \
+ (AW88081_HMUTE_DISABLE << AW88081_HMUTE_START_BIT)
+
+#define AW88081_HMUTE_ENABLE (1)
+#define AW88081_HMUTE_ENABLE_VALUE \
+ (AW88081_HMUTE_ENABLE << AW88081_HMUTE_START_BIT)
+
+#define AW88081_EN_PA_START_BIT (1)
+#define AW88081_EN_PA_BITS_LEN (1)
+#define AW88081_EN_PA_MASK \
+ (~(((1<<AW88081_EN_PA_BITS_LEN)-1) << AW88081_EN_PA_START_BIT))
+
+#define AW88081_EN_PA_WORKING (1)
+#define AW88081_EN_PA_WORKING_VALUE \
+ (AW88081_EN_PA_WORKING << AW88081_EN_PA_START_BIT)
+
+#define AW88081_EN_PA_POWER_DOWN (0)
+#define AW88081_EN_PA_POWER_DOWN_VALUE \
+ (AW88081_EN_PA_POWER_DOWN << AW88081_EN_PA_START_BIT)
+
+#define AW88081_PWDN_START_BIT (0)
+#define AW88081_PWDN_BITS_LEN (1)
+#define AW88081_PWDN_MASK \
+ (~(((1<<AW88081_PWDN_BITS_LEN)-1) << AW88081_PWDN_START_BIT))
+
+#define AW88081_PWDN_WORKING (0)
+#define AW88081_PWDN_WORKING_VALUE \
+ (AW88081_PWDN_WORKING << AW88081_PWDN_START_BIT)
+
+#define AW88081_PWDN_POWER_DOWN (1)
+#define AW88081_PWDN_POWER_DOWN_VALUE \
+ (AW88081_PWDN_POWER_DOWN << AW88081_PWDN_START_BIT)
+
+#define AW88081_VOL_START_BIT (0)
+#define AW88081_VOL_BITS_LEN (10)
+#define AW88081_VOL_MASK \
+ (~(((1<<AW88081_VOL_BITS_LEN)-1) << AW88081_VOL_START_BIT))
+
+#define AW88081_VOLUME_STEP_DB (64)
+#define AW88081_MUTE_VOL (1023)
+
+#define AW88081_I2STXEN_START_BIT (6)
+#define AW88081_I2STXEN_BITS_LEN (1)
+#define AW88081_I2STXEN_MASK \
+ (~(((1<<AW88081_I2STXEN_BITS_LEN)-1) << AW88081_I2STXEN_START_BIT))
+
+#define AW88081_I2STXEN_DISABLE (0)
+#define AW88081_I2STXEN_DISABLE_VALUE \
+ (AW88081_I2STXEN_DISABLE << AW88081_I2STXEN_START_BIT)
+
+#define AW88081_I2STXEN_ENABLE (1)
+#define AW88081_I2STXEN_ENABLE_VALUE \
+ (AW88081_I2STXEN_ENABLE << AW88081_I2STXEN_START_BIT)
+
+#define AW88081_NOISE_GATE_EN_START_BIT (13)
+#define AW88081_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88081_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88081_NOISE_GATE_EN_BITS_LEN)-1) << AW88081_NOISE_GATE_EN_START_BIT))
+
+#define AW88081_NOISE_GATE_EN_DISABLE (0)
+#define AW88081_NOISE_GATE_EN_DISABLE_VALUE \
+ (AW88081_NOISE_GATE_EN_DISABLE << AW88081_NOISE_GATE_EN_START_BIT)
+
+#define AW88081_NOISE_GATE_EN_ENABLE (1)
+#define AW88081_NOISE_GATE_EN_ENABLE_VALUE \
+ (AW88081_NOISE_GATE_EN_ENABLE << AW88081_NOISE_GATE_EN_START_BIT)
+
+#define AW88081_CCO_MUX_START_BIT (13)
+#define AW88081_CCO_MUX_BITS_LEN (1)
+#define AW88081_CCO_MUX_MASK \
+ (~(((1<<AW88081_CCO_MUX_BITS_LEN)-1) << AW88081_CCO_MUX_START_BIT))
+
+#define AW88081_CCO_MUX_DIVIDED (0)
+#define AW88081_CCO_MUX_DIVIDED_VALUE \
+ (AW88081_CCO_MUX_DIVIDED << AW88081_CCO_MUX_START_BIT)
+
+#define AW88081_CCO_MUX_BYPASS (1)
+#define AW88081_CCO_MUX_BYPASS_VALUE \
+ (AW88081_CCO_MUX_BYPASS << AW88081_CCO_MUX_START_BIT)
+
+#define AW88083_I2C_WEN_START_BIT (14)
+#define AW88083_I2C_WEN_BITS_LEN (2)
+#define AW88083_I2C_WEN_MASK \
+ (~(((1<<AW88083_I2C_WEN_BITS_LEN)-1) << AW88083_I2C_WEN_START_BIT))
+
+#define AW88083_I2C_WEN_DISABLE (0)
+#define AW88083_I2C_WEN_DISABLE_VALUE \
+ (AW88083_I2C_WEN_DISABLE << AW88083_I2C_WEN_START_BIT)
+
+#define AW88083_I2C_WEN_ENABLE (2)
+#define AW88083_I2C_WEN_ENABLE_VALUE \
+ (AW88083_I2C_WEN_ENABLE << AW88083_I2C_WEN_START_BIT)
+
+#define AW88083_PLL_PD_START_BIT (2)
+#define AW88083_PLL_PD_BITS_LEN (1)
+#define AW88083_PLL_PD_MASK \
+ (~(((1<<AW88083_PLL_PD_BITS_LEN)-1) << AW88083_PLL_PD_START_BIT))
+
+#define AW88083_PLL_PD_POWER_DOWN (1)
+#define AW88083_PLL_PD_POWER_DOWN_VALUE \
+ (AW88083_PLL_PD_POWER_DOWN << AW88083_PLL_PD_START_BIT)
+
+#define AW88083_PLL_PD_WORKING (0)
+#define AW88083_PLL_PD_WORKING_VALUE \
+ (AW88083_PLL_PD_WORKING << AW88083_PLL_PD_START_BIT)
+
+#define AW88083_AMPPD_START_BIT (1)
+#define AW88083_AMPPD_BITS_LEN (1)
+#define AW88083_AMPPD_MASK \
+ (~(((1<<AW88083_AMPPD_BITS_LEN)-1) << AW88083_AMPPD_START_BIT))
+
+#define AW88083_AMPPD_WORKING (0)
+#define AW88083_AMPPD_WORKING_VALUE \
+ (AW88083_AMPPD_WORKING << AW88083_AMPPD_START_BIT)
+
+#define AW88083_AMPPD_POWER_DOWN (1)
+#define AW88083_AMPPD_POWER_DOWN_VALUE \
+ (AW88083_AMPPD_POWER_DOWN << AW88083_AMPPD_START_BIT)
+
+#define AW88083_REG_MAX (0x7D)
+#define AW88083_I2C_NAME "aw88083"
+#define AW88083_CHIP_ID 0x2407
+
+#define AW88081_START_RETRIES (5)
+#define AW88081_START_WORK_DELAY_MS (0)
+
+#define AW88081_I2C_NAME "aw88081"
+#define AW88081_CHIP_ID 0x2116
+
+#define AW88081_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88081_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+
+#define AW88081_DEV_DEFAULT_CH (0)
+#define AW88081_ACF_FILE "aw88081_acf.bin"
+#define AW88081_DEV_SYSST_CHECK_MAX (10)
+#define AW88081_SOFT_RESET_VALUE (0x55aa)
+
+#define AW88081_INIT_PROFILE (0)
+
+#define AW88081_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW88081_SYNC_START = 0,
+ AW88081_ASYNC_START,
+};
+
+enum {
+ AW88081_500_US = 500,
+ AW88081_1000_US = 1000,
+ AW88081_2000_US = 2000,
+ AW88081_5000_US = 5000,
+};
+
+enum {
+ AW88081_DEV_PW_OFF = 0,
+ AW88081_DEV_PW_ON,
+};
+
+enum {
+ AW88081_DEV_FW_FAILED = 0,
+ AW88081_DEV_FW_OK,
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c
new file mode 100644
index 000000000000..daee4de9e3b0
--- /dev/null
+++ b/sound/soc/codecs/aw88166.c
@@ -0,0 +1,1815 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88166.c -- ALSA SoC AW88166 codec support
+//
+// Copyright (c) 2025 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88166.h"
+#include "aw88395/aw88395_device.h"
+
+struct aw88166 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+
+ unsigned int check_val;
+ unsigned int crc_init_val;
+ unsigned int vcalb_init_val;
+ unsigned int re_init_val;
+ unsigned int dither_st;
+ bool phase_sync;
+};
+
+static const struct regmap_config aw88166_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88166_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ int ret;
+
+ if (pwd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_PWDN_MASK, AW88166_PWDN_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_PWDN_MASK, AW88166_PWDN_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSINT_REG, &reg_val);
+ if (ret)
+ dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret);
+ else
+ *int_status = reg_val;
+
+ dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status);
+}
+
+static void aw_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ u16 int_status;
+
+ /* read int status and clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ /* make sure int status is clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ if (int_status)
+ dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status);
+}
+
+static int aw_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88166_BIT_PLL_CHECK) != AW88166_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_PLLCTRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88166_CCO_MUX_MASK);
+ if (reg_val == AW88166_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_PLLCTRL2_REG,
+ ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ regmap_update_bits(aw_dev->regmap, AW88166_PLLCTRL2_REG,
+ ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_PWMCTRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88166_NOISE_GATE_EN_MASK))
+ check_val = AW88166_BIT_SYSST_NOSWS_CHECK;
+ else
+ check_val = AW88166_BIT_SYSST_SWS_CHECK;
+
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if ((reg_val & (~AW88166_BIT_SYSST_CHECK_MASK) & check_val) != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88166_BIT_SYSST_NOSWS_CHECK);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_AMPPD_MASK, AW88166_AMPPD_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_AMPPD_MASK, AW88166_AMPPD_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable)
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_DSPBY_MASK, AW88166_DSPBY_WORKING_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_DSPBY_MASK, AW88166_DSPBY_BYPASS_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw88166_dev_get_icalk(struct aw88166 *aw88166, int16_t *icalk)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int efrm_reg_val, efrl_reg_val;
+ uint16_t ef_isn_geslp, ef_isn_h5bits;
+ uint16_t icalk_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val);
+ if (ret)
+ return ret;
+
+ ef_isn_geslp = (efrm_reg_val & (~AW88166_EF_ISN_GESLP_MASK)) >>
+ AW88166_EF_ISN_GESLP_SHIFT;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val);
+ if (ret)
+ return ret;
+
+ ef_isn_h5bits = (efrl_reg_val & (~AW88166_EF_ISN_H5BITS_MASK)) >>
+ AW88166_EF_ISN_H5BITS_SHIFT;
+
+ if (aw88166->check_val == AW_EF_AND_CHECK)
+ icalk_val = ef_isn_geslp & (ef_isn_h5bits | AW88166_EF_ISN_H5BITS_SIGN_MASK);
+ else
+ icalk_val = ef_isn_geslp | (ef_isn_h5bits & (~AW88166_EF_ISN_H5BITS_SIGN_MASK));
+
+ if (icalk_val & (~AW88166_ICALK_SIGN_MASK))
+ icalk_val = icalk_val | AW88166_ICALK_NEG_MASK;
+ *icalk = (int16_t)icalk_val;
+
+ return 0;
+}
+
+static int aw88166_dev_get_vcalk(struct aw88166 *aw88166, int16_t *vcalk)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int efrm_reg_val, efrl_reg_val;
+ uint16_t ef_vsn_geslp, ef_vsn_h3bits;
+ uint16_t vcalk_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val);
+ if (ret)
+ return ret;
+
+ ef_vsn_geslp = (efrm_reg_val & (~AW88166_EF_VSN_GESLP_MASK)) >>
+ AW88166_EF_VSN_GESLP_SHIFT;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val);
+ if (ret)
+ return ret;
+
+ ef_vsn_h3bits = (efrl_reg_val & (~AW88166_EF_VSN_H3BITS_MASK)) >>
+ AW88166_EF_VSN_H3BITS_SHIFT;
+
+ if (aw88166->check_val == AW_EF_AND_CHECK)
+ vcalk_val = ef_vsn_geslp & (ef_vsn_h3bits | AW88166_EF_VSN_H3BITS_SIGN_MASK);
+ else
+ vcalk_val = ef_vsn_geslp | (ef_vsn_h3bits & (~AW88166_EF_VSN_H3BITS_SIGN_MASK));
+
+ if (vcalk_val & (~AW88166_VCALK_SIGN_MASK))
+ vcalk_val = vcalk_val | AW88166_VCALK_NEG_MASK;
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw88166_dev_set_vcalb(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int32_t ical_k, vcal_k, vcalb;
+ int16_t icalk, vcalk;
+ unsigned int reg_val;
+ int ret;
+
+ ret = aw88166_dev_get_icalk(aw88166, &icalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get icalk failed\n");
+ return ret;
+ }
+ ical_k = icalk * AW88166_ICABLK_FACTOR + AW88166_CABL_BASE_VALUE;
+
+ ret = aw88166_dev_get_vcalk(aw88166, &vcalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get vbcalk failed\n");
+ return ret;
+ }
+ vcal_k = vcalk * AW88166_VCABLK_FACTOR + AW88166_CABL_BASE_VALUE;
+
+ vcalb = AW88166_VCALB_ACCURACY * AW88166_VSCAL_FACTOR /
+ AW88166_ISCAL_FACTOR * ical_k / vcal_k * aw88166->vcalb_init_val;
+
+ vcalb = vcalb >> AW88166_VCALB_ADJ_FACTOR;
+ reg_val = (uint32_t)vcalb;
+
+ regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, reg_val);
+
+ return 0;
+}
+
+static int aw_dev_init_vcalb_update(struct aw88166 *aw88166, int flag)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ switch (flag) {
+ case AW88166_RECOVERY_SEC_DATA:
+ ret = regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, aw88166->vcalb_init_val);
+ break;
+ case AW88166_RECORD_SEC_DATA:
+ ret = regmap_read(aw_dev->regmap, AW88166_DSPVCALB_REG, &aw88166->vcalb_init_val);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported type:%d\n", flag);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_init_re_update(struct aw88166 *aw88166, int flag)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int re_temp_h, re_temp_l;
+ int ret;
+
+ switch (flag) {
+ case AW88166_RECOVERY_SEC_DATA:
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, aw88166->re_init_val >> 16);
+ if (ret)
+ return ret;
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG,
+ (uint16_t)aw88166->re_init_val);
+ if (ret)
+ return ret;
+ break;
+ case AW88166_RECORD_SEC_DATA:
+ ret = regmap_read(aw_dev->regmap, AW88166_ACR1_REG, &re_temp_h);
+ if (ret)
+ return ret;
+ ret = regmap_read(aw_dev->regmap, AW88166_ACR2_REG, &re_temp_l);
+ if (ret)
+ return ret;
+ aw88166->re_init_val = (re_temp_h << 16) + re_temp_l;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported type:%d\n", flag);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void aw_dev_backup_sec_record(struct aw88166 *aw88166)
+{
+ aw_dev_init_vcalb_update(aw88166, AW88166_RECORD_SEC_DATA);
+ aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+}
+
+static void aw_dev_backup_sec_recovery(struct aw88166 *aw88166)
+{
+ aw_dev_init_vcalb_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+ aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+}
+
+static int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ uint16_t re_lbits, re_hbits;
+ u32 cali_re;
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re >= AW88166_CALI_RE_MAX) ||
+ (aw_dev->cali_desc.cali_re <= AW88166_CALI_RE_MIN))
+ return -EINVAL;
+
+ cali_re = AW88166_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88166_DSP_RE_SHIFT);
+
+ re_hbits = (cali_re & (~AW88166_CALI_RE_HBITS_MASK)) >> AW88166_CALI_RE_HBITS_SHIFT;
+ re_lbits = (cali_re & (~AW88166_CALI_RE_LBITS_MASK)) >> AW88166_CALI_RE_LBITS_SHIFT;
+
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, re_hbits);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG, re_lbits);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re error");
+
+ return ret;
+}
+
+static int aw_dev_fw_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, fw_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate fw_end_addr */
+ fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_FW_BASE_ADDR;
+
+ /* write fw_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_END_ADDR_MASK, fw_len_val);
+ if (ret)
+ return ret;
+ /* enable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_ENABLE_VALUE);
+
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+
+ /* read crc check result */
+ regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT;
+
+ /* disable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88166_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x\n",
+ __func__, check_val, AW88166_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_cfg_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, cfg_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate cfg end addr */
+ cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_CFG_BASE_ADDR;
+
+ /* write cfg_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_END_ADDR_MASK, cfg_len_val);
+ if (ret)
+ return ret;
+
+ /* enable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_ENABLE_VALUE);
+ if (ret)
+ return ret;
+
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+
+ /* read crc check result */
+ ret = regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT;
+
+ /* disable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88166_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x\n",
+ check_val, AW88166_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_hw_crc_check(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_BYPASS_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_fw_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = aw_dev_cfg_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "cfg_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88166_CRCCTRL_REG, aw88166->crc_init_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE);
+
+ return ret;
+
+crc_check_failed:
+ regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE);
+ return ret;
+}
+
+static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag)
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCTRL3_REG,
+ ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_ENABLE_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCTRL3_REG,
+ ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_DISABLE_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88166_WDT_CNT_MASK)))
+ return -EPERM;
+
+ return 0;
+}
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88166_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88166_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ for (i = 0; i < AW88166_DEV_DSP_CHECK_MAX; i++) {
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp wdt status error=%d", ret);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int reg_value;
+ u16 real_value;
+ int ret;
+
+ real_value = min((value + vol_desc->init_volume), (unsigned int)AW88166_MUTE_VOL);
+
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ real_value = (real_value << AW88166_VOL_START_BIT) | (reg_value & AW88166_VOL_MASK);
+
+ ret = regmap_write(aw_dev->regmap, AW88166_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+static void aw_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ u16 fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88166_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88166_MUTE_VOL; i += fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88166_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88166_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_HMUTE_MASK, AW88166_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_HMUTE_MASK, AW88166_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88166_dev_set_dither(struct aw88166 *aw88166, bool dither)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ if (dither)
+ regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_DISABLE_VALUE);
+}
+
+static int aw88166_dev_start(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88166_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88166_dev_set_dither(aw88166, false);
+
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed\n");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK) {
+ aw_dev_backup_sec_recovery(aw88166);
+ ret = aw_dev_hw_crc_check(aw88166);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed\n");
+ goto crc_check_fail;
+ }
+ aw_dev_dsp_enable(aw_dev, false);
+ aw88166_dev_set_vcalb(aw88166);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed\n");
+ goto dsp_check_fail;
+ }
+ } else {
+ dev_dbg(aw_dev->dev, "start pa with dsp bypass");
+ }
+
+ /* enable tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, true);
+
+ if (aw88166->dither_st == AW88166_DITHER_EN_ENABLE_VALUE)
+ aw88166_dev_set_dither(aw88166, true);
+
+ /* close mute */
+ aw88166_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88166_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+sysst_check_fail:
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88166_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ u32 tmp_len;
+ int i, ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, base);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < len; i += AW88166_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88166_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88166_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_get_ra(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ u32 dsp_ra;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88166_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error\n");
+ return ret;
+ }
+
+ cali_desc->ra = AW88166_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88166_DSP_RE_SHIFT);
+
+ return 0;
+}
+
+static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp config len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp config data is null or len is 0\n");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_CFG_ADDR);
+ if (ret)
+ return ret;
+
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp firmware len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp firmware data is null or len is 0\n");
+ return -EINVAL;
+ }
+
+ aw_dev->dsp_fw_len = len;
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_FW_ADDR);
+
+ return ret;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ /* read dsp_rom_check_reg */
+ aw_dev_dsp_read(aw_dev, AW88166_DSP_ROM_CHECK_ADDR, &reg_val, AW_DSP_16_DATA);
+ if (reg_val != AW88166_DSP_ROM_CHECK_DATA) {
+ dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]\n",
+ reg_val, AW88166_DSP_ROM_CHECK_DATA);
+ return -EPERM;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write(aw_dev, AW88166_DSP_CFG_ADDR,
+ AW88166_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA);
+ aw_dev_dsp_read(aw_dev, AW88166_DSP_CFG_ADDR, &reg_val, AW_DSP_16_DATA);
+ if (reg_val != AW88166_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]\n",
+ reg_val, AW88166_DSP_ODD_NUM_BIT_TEST);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88166_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_MEM_CLKSEL_MASK,
+ AW88166_MEM_CLKSEL_DAPHCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed\n");
+ break;
+ case AW88166_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_MEM_CLKSEL_MASK,
+ AW88166_MEM_CLKSEL_OSCCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed\n");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x\n", flag);
+ break;
+ }
+}
+
+static int aw_dev_update_reg_container(struct aw88166 *aw88166,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ u16 read_vol, reg_val;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u8 reg_addr;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported\n", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88166_DSPVCALB_REG) {
+ aw88166->vcalb_init_val = reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88166_SYSCTRL_REG) {
+ if (reg_val & (~AW88166_DSPBY_MASK))
+ aw_dev->dsp_cfg = AW88166_DEV_DSP_BYPASS;
+ else
+ aw_dev->dsp_cfg = AW88166_DEV_DSP_WORK;
+
+ reg_val &= (AW88166_HMUTE_MASK | AW88166_PWDN_MASK |
+ AW88166_DSPBY_MASK);
+ reg_val |= (AW88166_HMUTE_ENABLE_VALUE | AW88166_PWDN_POWER_DOWN_VALUE |
+ AW88166_DSPBY_BYPASS_VALUE);
+ }
+
+ if (reg_addr == AW88166_I2SCTRL3_REG) {
+ reg_val &= AW88166_I2STXEN_MASK;
+ reg_val |= AW88166_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88166_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88166_VOL_MASK)) >>
+ AW88166_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ if (reg_addr == AW88166_DBGCTRL_REG) {
+ if ((reg_val & (~AW88166_EF_DBMD_MASK)) == AW88166_EF_DBMD_OR_VALUE)
+ aw88166->check_val = AW_EF_OR_CHECK;
+ else
+ aw88166->check_val = AW_EF_AND_CHECK;
+
+ aw88166->dither_st = reg_val & (~AW88166_DITHER_EN_MASK);
+ }
+
+ if (reg_addr == AW88166_ACR1_REG) {
+ aw88166->re_init_val |= (uint32_t)reg_val << 16;
+ continue;
+ }
+
+ if (reg_addr == AW88166_ACR2_REG) {
+ aw88166->re_init_val |= (uint32_t)reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88166_CRCCTRL_REG)
+ aw88166->crc_init_val = reg_val;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+ else
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+
+ return 0;
+}
+
+static int aw_dev_reg_update(struct aw88166 *aw88166,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw88166->aw_pa->dev, "reg data is null or len is 0\n");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw88166, data, len);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "reg update failed\n");
+
+ return ret;
+}
+
+static int aw88166_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]\n",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88166_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+
+static int aw88166_dev_fw_update(struct aw88166 *aw88166, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ if ((aw_dev->prof_cur == aw_dev->prof_index) &&
+ (force_up_en == AW88166_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88166_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error\n", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88166_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88166_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw_dev_reg_update(aw88166, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed\n");
+ return ret;
+ }
+
+ aw88166_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC);
+
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed\n");
+ goto error;
+ }
+
+ aw_dev_backup_sec_recovery(aw88166);
+
+ if (up_dsp_fw_en) {
+ dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver);
+ ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_FW].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp fw failed\n");
+ goto error;
+ }
+ }
+
+ /* update dsp config */
+ ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_CFG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp cfg failed\n");
+ goto error;
+ }
+
+ aw_dev_backup_sec_record(aw88166);
+
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+ return ret;
+}
+
+static void aw88166_start_pa(struct aw88166 *aw88166)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88166_START_RETRIES; i++) {
+ ret = aw88166_dev_start(aw88166);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "aw88166 device start failed. retry = %d", i);
+ ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_ON, true);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_dbg(aw88166->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88166_startup_work(struct work_struct *work)
+{
+ struct aw88166 *aw88166 =
+ container_of(work, struct aw88166, start_work.work);
+
+ mutex_lock(&aw88166->lock);
+ aw88166_start_pa(aw88166);
+ mutex_unlock(&aw88166->lock);
+}
+
+static void aw88166_start(struct aw88166 *aw88166, bool sync_start)
+{
+ int ret;
+
+ if (aw88166->aw_pa->fw_status != AW88166_DEV_FW_OK)
+ return;
+
+ if (aw88166->aw_pa->status == AW88166_DEV_PW_ON)
+ return;
+
+ ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_OFF, aw88166->phase_sync);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "fw update failed\n");
+ return;
+ }
+
+ if (sync_start == AW88166_SYNC_START)
+ aw88166_start_pa(aw88166);
+ else
+ queue_delayed_work(system_dfl_wq,
+ &aw88166->start_work,
+ AW88166_START_WORK_DELAY_MS);
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+ if (reg_val & AW88166_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x\n", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88166_stop(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_cfg =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG];
+ struct aw_sec_data_desc *dsp_fw =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW];
+ int int_st;
+
+ if (aw_dev->status == AW88166_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88166_DEV_PW_OFF;
+
+ aw88166_dev_mute(aw_dev, true);
+ usleep_range(AW88166_4000_US, AW88166_4000_US + 100);
+
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 100);
+
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st) {
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC);
+ aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+ }
+
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver aw88166_dai[] = {
+ {
+ .name = "aw88166-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88166_RATES,
+ .formats = AW88166_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88166_RATES,
+ .formats = AW88166_FORMATS,
+ },
+ },
+};
+
+static int aw88166_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88166_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88166_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EINVAL;
+
+ aw_dev->prof_index = index;
+ dev_dbg(aw_dev->dev, "set prof[%s]",
+ aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]);
+
+ return 0;
+}
+
+static int aw88166_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88166->aw_pa->prof_info.count;
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88166_dev_get_prof_name(aw88166->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88166_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88166->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88166_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw88166->lock);
+ ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88166->lock);
+ return 0;
+ }
+
+ if (aw88166->aw_pa->status) {
+ aw88166_stop(aw88166->aw_pa);
+ aw88166_start(aw88166, AW88166_SYNC_START);
+ }
+
+ mutex_unlock(&aw88166->lock);
+
+ return 1;
+}
+
+static int aw88166_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88166->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88166_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88166->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw_dev_set_volume(aw88166->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88166->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88166_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88166->aw_pa->fade_step != value) {
+ aw88166->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88166_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw_dev->cali_desc.cali_re != value) {
+ aw_dev->cali_desc.cali_re = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed\n");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88166_1000_US / 10;
+ aw_dev->fade_out_time = AW88166_1000_US >> 1;
+ aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id;
+ aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id;
+
+ ret = aw88166_dev_fw_update(aw88166, AW88166_FORCE_UPDATE_ON, AW88166_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88166_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 100);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88166_request_firmware_file(struct aw88166 *aw88166)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88166->aw_pa->fw_status = AW88166_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88166_ACF_FILE, aw88166->aw_pa->dev);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "request [%s] failed!\n", AW88166_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88166->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88166_ACF_FILE, cont ? cont->size : 0);
+
+ aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev,
+ struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw88166->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88166->aw_cfg->len = (int)cont->size;
+ memcpy(aw88166->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "load [%s] failed!\n", AW88166_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88166->lock);
+ /* aw device init */
+ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "dev init failed\n");
+ mutex_unlock(&aw88166->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aw88166_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88166_SYSCTRL2_REG,
+ 6, AW88166_MUTE_VOL, 0, aw88166_volume_get,
+ aw88166_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88166_MUTE_VOL, 0,
+ aw88166_get_fade_step, aw88166_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88166_get_fade_in_time, aw88166_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88166_get_fade_out_time, aw88166_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88166_CALI_RE_MAX, 0,
+ aw88166_re_get, aw88166_re_set),
+ AW88166_PROFILE_EXT("AW88166 Profile Set", aw88166_profile_info,
+ aw88166_profile_get, aw88166_profile_set),
+};
+
+static int aw88166_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88166->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88166_start(aw88166, AW88166_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88166_stop(aw88166->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88166->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88166_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0,
+ aw88166_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88166_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88166_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88166->start_work, aw88166_startup_work);
+
+ ret = aw88166_request_firmware_file(aw88166);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88166_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88166->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88166 = {
+ .probe = aw88166_codec_probe,
+ .remove = aw88166_codec_remove,
+ .dapm_widgets = aw88166_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88166_dapm_widgets),
+ .dapm_routes = aw88166_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88166_audio_map),
+ .controls = aw88166_controls,
+ .num_controls = ARRAY_SIZE(aw88166_controls),
+};
+
+static void aw88166_hw_reset(struct aw88166 *aw88166)
+{
+ if (aw88166->reset_gpio) {
+ gpiod_set_value_cansleep(aw88166->reset_gpio, 1);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ gpiod_set_value_cansleep(aw88166->reset_gpio, 0);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ }
+}
+
+static void aw88166_parse_channel_dt(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw_dev->channel = channel_value;
+ aw88166->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+}
+
+static int aw88166_init(struct aw88166 *aw88166, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(regmap, AW88166_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+ aw88166->aw_pa = aw_dev;
+
+ aw_dev->i2c = i2c;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->regmap = regmap;
+ mutex_init(&aw_dev->dsp_lock);
+
+ aw_dev->chip_id = chip_id;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = AW88166_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW88166_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88166_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88166_VOL_DEFAULT_VALUE;
+
+ aw88166_parse_channel_dt(aw88166);
+
+ return 0;
+}
+
+static int aw88166_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88166 *aw88166;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n");
+
+ aw88166 = devm_kzalloc(&i2c->dev, sizeof(*aw88166), GFP_KERNEL);
+ if (!aw88166)
+ return -ENOMEM;
+
+ mutex_init(&aw88166->lock);
+
+ i2c_set_clientdata(i2c, aw88166);
+
+ aw88166->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88166->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->reset_gpio),
+ "reset gpio not defined\n");
+ aw88166_hw_reset(aw88166);
+
+ aw88166->regmap = devm_regmap_init_i2c(i2c, &aw88166_remap_config);
+ if (IS_ERR(aw88166->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88166_init(aw88166, i2c, aw88166->regmap);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88166,
+ aw88166_dai, ARRAY_SIZE(aw88166_dai));
+}
+
+static const struct i2c_device_id aw88166_i2c_id[] = {
+ { AW88166_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88166_i2c_id);
+
+static struct i2c_driver aw88166_i2c_driver = {
+ .driver = {
+ .name = AW88166_I2C_NAME,
+ },
+ .probe = aw88166_i2c_probe,
+ .id_table = aw88166_i2c_id,
+};
+module_i2c_driver(aw88166_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88166 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88166.h b/sound/soc/codecs/aw88166.h
new file mode 100644
index 000000000000..9f3f47a7003e
--- /dev/null
+++ b/sound/soc/codecs/aw88166.h
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88166.h -- ALSA SoC AW88166 codec support
+//
+// Copyright (c) 2025 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88166_H__
+#define __AW88166_H__
+
+/* registers list */
+#define AW88166_ID_REG (0x00)
+#define AW88166_SYSST_REG (0x01)
+#define AW88166_SYSINT_REG (0x02)
+#define AW88166_SYSINTM_REG (0x03)
+#define AW88166_SYSCTRL_REG (0x04)
+#define AW88166_SYSCTRL2_REG (0x05)
+#define AW88166_I2SCTRL1_REG (0x06)
+#define AW88166_I2SCTRL2_REG (0x07)
+#define AW88166_I2SCTRL3_REG (0x08)
+#define AW88166_DACCFG1_REG (0x09)
+#define AW88166_DACCFG2_REG (0x0A)
+#define AW88166_DACCFG3_REG (0x0B)
+#define AW88166_DACCFG4_REG (0x0C)
+#define AW88166_DACCFG5_REG (0x0D)
+#define AW88166_DACCFG6_REG (0x0E)
+#define AW88166_DACCFG7_REG (0x0F)
+#define AW88166_MPDCFG1_REG (0x10)
+#define AW88166_MPDCFG2_REG (0x11)
+#define AW88166_MPDCFG3_REG (0x12)
+#define AW88166_MPDCFG4_REG (0x13)
+#define AW88166_PWMCTRL1_REG (0x14)
+#define AW88166_PWMCTRL2_REG (0x15)
+#define AW88166_PWMCTRL3_REG (0x16)
+#define AW88166_I2SCFG1_REG (0x17)
+#define AW88166_DBGCTRL_REG (0x18)
+#define AW88166_HAGCST_REG (0x20)
+#define AW88166_VBAT_REG (0x21)
+#define AW88166_TEMP_REG (0x22)
+#define AW88166_PVDD_REG (0x23)
+#define AW88166_ISNDAT_REG (0x24)
+#define AW88166_I2SINT_REG (0x25)
+#define AW88166_I2SCAPCNT_REG (0x26)
+#define AW88166_ANASTA1_REG (0x27)
+#define AW88166_ANASTA2_REG (0x28)
+#define AW88166_ANASTA3_REG (0x29)
+#define AW88166_TESTDET_REG (0x2A)
+#define AW88166_TESTIN_REG (0x38)
+#define AW88166_TESTOUT_REG (0x39)
+#define AW88166_MEMTEST_REG (0x3A)
+#define AW88166_DSPMADD_REG (0x40)
+#define AW88166_DSPMDAT_REG (0x41)
+#define AW88166_WDT_REG (0x42)
+#define AW88166_ACR1_REG (0x43)
+#define AW88166_ACR2_REG (0x44)
+#define AW88166_ASR1_REG (0x45)
+#define AW88166_ASR2_REG (0x46)
+#define AW88166_DSPCFG_REG (0x47)
+#define AW88166_ASR3_REG (0x48)
+#define AW88166_ASR4_REG (0x49)
+#define AW88166_DSPVCALB_REG (0x4A)
+#define AW88166_CRCCTRL_REG (0x4B)
+#define AW88166_DSPDBG1_REG (0x4C)
+#define AW88166_DSPDBG2_REG (0x4D)
+#define AW88166_DSPDBG3_REG (0x4E)
+#define AW88166_ISNCTRL1_REG (0x50)
+#define AW88166_PLLCTRL1_REG (0x51)
+#define AW88166_PLLCTRL2_REG (0x52)
+#define AW88166_PLLCTRL3_REG (0x53)
+#define AW88166_CDACTRL1_REG (0x54)
+#define AW88166_CDACTRL2_REG (0x55)
+#define AW88166_CDACTRL3_REG (0x56)
+#define AW88166_SADCCTRL1_REG (0x57)
+#define AW88166_SADCCTRL2_REG (0x58)
+#define AW88166_BOPCTRL1_REG (0x59)
+#define AW88166_BOPCTRL2_REG (0x5A)
+#define AW88166_BOPCTRL3_REG (0x5B)
+#define AW88166_BOPCTRL4_REG (0x5C)
+#define AW88166_BOPCTRL5_REG (0x5D)
+#define AW88166_BOPCTRL6_REG (0x5E)
+#define AW88166_BOPCTRL7_REG (0x5F)
+#define AW88166_BSTCTRL1_REG (0x60)
+#define AW88166_BSTCTRL2_REG (0x61)
+#define AW88166_BSTCTRL3_REG (0x62)
+#define AW88166_BSTCTRL4_REG (0x63)
+#define AW88166_BSTCTRL5_REG (0x64)
+#define AW88166_BSTCTRL6_REG (0x65)
+#define AW88166_DSMCFG1_REG (0x66)
+#define AW88166_DSMCFG2_REG (0x67)
+#define AW88166_DSMCFG3_REG (0x68)
+#define AW88166_DSMCFG4_REG (0x69)
+#define AW88166_DSMCFG5_REG (0x6A)
+#define AW88166_DSMCFG6_REG (0x6B)
+#define AW88166_DSMCFG7_REG (0x6C)
+#define AW88166_DSMCFG8_REG (0x6D)
+#define AW88166_TESTCTRL1_REG (0x70)
+#define AW88166_TESTCTRL2_REG (0x71)
+#define AW88166_EFCTRL1_REG (0x72)
+#define AW88166_EFCTRL2_REG (0x73)
+#define AW88166_EFWH_REG (0x74)
+#define AW88166_EFWM2_REG (0x75)
+#define AW88166_EFWM1_REG (0x76)
+#define AW88166_EFRH_REG (0x77)
+#define AW88166_EFRM2_REG (0x78)
+#define AW88166_EFRM1_REG (0x79)
+#define AW88166_EFRL_REG (0x7A)
+#define AW88166_TM_REG (0x7C)
+#define AW88166_TM2_REG (0x7D)
+
+#define AW88166_REG_MAX (0x7E)
+#define AW88166_MUTE_VOL (1023)
+
+#define AW88166_DSP_CFG_ADDR (0x9B00)
+#define AW88166_DSP_REG_CFG_ADPZ_RA (0x9B68)
+#define AW88166_DSP_FW_ADDR (0x8980)
+#define AW88166_DSP_ROM_CHECK_ADDR (0x1F40)
+
+#define AW88166_CALI_RE_HBITS_MASK (~(0xFFFF0000))
+#define AW88166_CALI_RE_HBITS_SHIFT (16)
+
+#define AW88166_CALI_RE_LBITS_MASK (~(0xFFFF))
+#define AW88166_CALI_RE_LBITS_SHIFT (0)
+
+#define AW88166_I2STXEN_START_BIT (9)
+#define AW88166_I2STXEN_BITS_LEN (1)
+#define AW88166_I2STXEN_MASK \
+ (~(((1<<AW88166_I2STXEN_BITS_LEN)-1) << AW88166_I2STXEN_START_BIT))
+
+#define AW88166_I2STXEN_DISABLE (0)
+#define AW88166_I2STXEN_DISABLE_VALUE \
+ (AW88166_I2STXEN_DISABLE << AW88166_I2STXEN_START_BIT)
+
+#define AW88166_I2STXEN_ENABLE (1)
+#define AW88166_I2STXEN_ENABLE_VALUE \
+ (AW88166_I2STXEN_ENABLE << AW88166_I2STXEN_START_BIT)
+
+#define AW88166_VOL_START_BIT (0)
+#define AW88166_VOL_BITS_LEN (10)
+#define AW88166_VOL_MASK \
+ (~(((1<<AW88166_VOL_BITS_LEN)-1) << AW88166_VOL_START_BIT))
+
+#define AW88166_PWDN_START_BIT (0)
+#define AW88166_PWDN_BITS_LEN (1)
+#define AW88166_PWDN_MASK \
+ (~(((1<<AW88166_PWDN_BITS_LEN)-1) << AW88166_PWDN_START_BIT))
+
+#define AW88166_PWDN_POWER_DOWN (1)
+#define AW88166_PWDN_POWER_DOWN_VALUE \
+ (AW88166_PWDN_POWER_DOWN << AW88166_PWDN_START_BIT)
+
+#define AW88166_PWDN_WORKING (0)
+#define AW88166_PWDN_WORKING_VALUE \
+ (AW88166_PWDN_WORKING << AW88166_PWDN_START_BIT)
+
+#define AW88166_DSPBY_START_BIT (2)
+#define AW88166_DSPBY_BITS_LEN (1)
+#define AW88166_DSPBY_MASK \
+ (~(((1<<AW88166_DSPBY_BITS_LEN)-1) << AW88166_DSPBY_START_BIT))
+
+#define AW88166_DSPBY_WORKING (0)
+#define AW88166_DSPBY_WORKING_VALUE \
+ (AW88166_DSPBY_WORKING << AW88166_DSPBY_START_BIT)
+
+#define AW88166_DSPBY_BYPASS (1)
+#define AW88166_DSPBY_BYPASS_VALUE \
+ (AW88166_DSPBY_BYPASS << AW88166_DSPBY_START_BIT)
+
+#define AW88166_MEM_CLKSEL_START_BIT (3)
+#define AW88166_MEM_CLKSEL_BITS_LEN (1)
+#define AW88166_MEM_CLKSEL_MASK \
+ (~(((1<<AW88166_MEM_CLKSEL_BITS_LEN)-1) << AW88166_MEM_CLKSEL_START_BIT))
+
+#define AW88166_MEM_CLKSEL_OSCCLK (0)
+#define AW88166_MEM_CLKSEL_OSCCLK_VALUE \
+ (AW88166_MEM_CLKSEL_OSCCLK << AW88166_MEM_CLKSEL_START_BIT)
+
+#define AW88166_MEM_CLKSEL_DAPHCLK (1)
+#define AW88166_MEM_CLKSEL_DAPHCLK_VALUE \
+ (AW88166_MEM_CLKSEL_DAPHCLK << AW88166_MEM_CLKSEL_START_BIT)
+
+#define AW88166_DITHER_EN_START_BIT (15)
+#define AW88166_DITHER_EN_BITS_LEN (1)
+#define AW88166_DITHER_EN_MASK \
+ (~(((1<<AW88166_DITHER_EN_BITS_LEN)-1) << AW88166_DITHER_EN_START_BIT))
+
+#define AW88166_DITHER_EN_DISABLE (0)
+#define AW88166_DITHER_EN_DISABLE_VALUE \
+ (AW88166_DITHER_EN_DISABLE << AW88166_DITHER_EN_START_BIT)
+
+#define AW88166_DITHER_EN_ENABLE (1)
+#define AW88166_DITHER_EN_ENABLE_VALUE \
+ (AW88166_DITHER_EN_ENABLE << AW88166_DITHER_EN_START_BIT)
+
+#define AW88166_HMUTE_START_BIT (8)
+#define AW88166_HMUTE_BITS_LEN (1)
+#define AW88166_HMUTE_MASK \
+ (~(((1<<AW88166_HMUTE_BITS_LEN)-1) << AW88166_HMUTE_START_BIT))
+
+#define AW88166_HMUTE_DISABLE (0)
+#define AW88166_HMUTE_DISABLE_VALUE \
+ (AW88166_HMUTE_DISABLE << AW88166_HMUTE_START_BIT)
+
+#define AW88166_HMUTE_ENABLE (1)
+#define AW88166_HMUTE_ENABLE_VALUE \
+ (AW88166_HMUTE_ENABLE << AW88166_HMUTE_START_BIT)
+
+#define AW88166_EF_DBMD_START_BIT (2)
+#define AW88166_EF_DBMD_BITS_LEN (1)
+#define AW88166_EF_DBMD_MASK \
+ (~(((1<<AW88166_EF_DBMD_BITS_LEN)-1) << AW88166_EF_DBMD_START_BIT))
+
+#define AW88166_EF_DBMD_OR (1)
+#define AW88166_EF_DBMD_OR_VALUE \
+ (AW88166_EF_DBMD_OR << AW88166_EF_DBMD_START_BIT)
+
+#define AW88166_CLKI_START_BIT (4)
+#define AW88166_NOCLKI_START_BIT (5)
+#define AW88166_PLLI_START_BIT (0)
+#define AW88166_PLLI_INT_VALUE (1)
+#define AW88166_PLLI_INT_INTERRUPT \
+ (AW88166_PLLI_INT_VALUE << AW88166_PLLI_START_BIT)
+
+#define AW88166_CLKI_INT_VALUE (1)
+#define AW88166_CLKI_INT_INTERRUPT \
+ (AW88166_CLKI_INT_VALUE << AW88166_CLKI_START_BIT)
+
+#define AW88166_NOCLKI_INT_VALUE (1)
+#define AW88166_NOCLKI_INT_INTERRUPT \
+ (AW88166_NOCLKI_INT_VALUE << AW88166_NOCLKI_START_BIT)
+
+#define AW88166_BIT_SYSINT_CHECK \
+ (AW88166_PLLI_INT_INTERRUPT | \
+ AW88166_CLKI_INT_INTERRUPT | \
+ AW88166_NOCLKI_INT_INTERRUPT)
+
+#define AW88166_CRC_CHECK_START_BIT (12)
+#define AW88166_CRC_CHECK_BITS_LEN (3)
+#define AW88166_CRC_CHECK_BITS_MASK \
+ (~(((1<<AW88166_CRC_CHECK_BITS_LEN)-1) << AW88166_CRC_CHECK_START_BIT))
+
+#define AW88166_RCV_MODE_RECEIVER (1)
+#define AW88166_RCV_MODE_RECEIVER_VALUE \
+ (AW88166_RCV_MODE_RECEIVER << AW88166_RCV_MODE_START_BIT)
+
+#define AW88166_AMPPD_START_BIT (1)
+#define AW88166_AMPPD_BITS_LEN (1)
+#define AW88166_AMPPD_MASK \
+ (~(((1<<AW88166_AMPPD_BITS_LEN)-1) << AW88166_AMPPD_START_BIT))
+
+#define AW88166_AMPPD_WORKING (0)
+#define AW88166_AMPPD_WORKING_VALUE \
+ (AW88166_AMPPD_WORKING << AW88166_AMPPD_START_BIT)
+
+#define AW88166_AMPPD_POWER_DOWN (1)
+#define AW88166_AMPPD_POWER_DOWN_VALUE \
+ (AW88166_AMPPD_POWER_DOWN << AW88166_AMPPD_START_BIT)
+
+#define AW88166_RAM_CG_BYP_START_BIT (0)
+#define AW88166_RAM_CG_BYP_BITS_LEN (1)
+#define AW88166_RAM_CG_BYP_MASK \
+ (~(((1<<AW88166_RAM_CG_BYP_BITS_LEN)-1) << AW88166_RAM_CG_BYP_START_BIT))
+
+#define AW88166_RAM_CG_BYP_WORK (0)
+#define AW88166_RAM_CG_BYP_WORK_VALUE \
+ (AW88166_RAM_CG_BYP_WORK << AW88166_RAM_CG_BYP_START_BIT)
+
+#define AW88166_RAM_CG_BYP_BYPASS (1)
+#define AW88166_RAM_CG_BYP_BYPASS_VALUE \
+ (AW88166_RAM_CG_BYP_BYPASS << AW88166_RAM_CG_BYP_START_BIT)
+
+#define AW88166_CRC_END_ADDR_START_BIT (0)
+#define AW88166_CRC_END_ADDR_BITS_LEN (12)
+#define AW88166_CRC_END_ADDR_MASK \
+ (~(((1<<AW88166_CRC_END_ADDR_BITS_LEN)-1) << AW88166_CRC_END_ADDR_START_BIT))
+
+#define AW88166_CRC_CODE_EN_START_BIT (13)
+#define AW88166_CRC_CODE_EN_BITS_LEN (1)
+#define AW88166_CRC_CODE_EN_MASK \
+ (~(((1<<AW88166_CRC_CODE_EN_BITS_LEN)-1) << AW88166_CRC_CODE_EN_START_BIT))
+
+#define AW88166_CRC_CODE_EN_DISABLE (0)
+#define AW88166_CRC_CODE_EN_DISABLE_VALUE \
+ (AW88166_CRC_CODE_EN_DISABLE << AW88166_CRC_CODE_EN_START_BIT)
+
+#define AW88166_CRC_CODE_EN_ENABLE (1)
+#define AW88166_CRC_CODE_EN_ENABLE_VALUE \
+ (AW88166_CRC_CODE_EN_ENABLE << AW88166_CRC_CODE_EN_START_BIT)
+
+#define AW88166_CRC_CFG_EN_START_BIT (12)
+#define AW88166_CRC_CFG_EN_BITS_LEN (1)
+#define AW88166_CRC_CFG_EN_MASK \
+ (~(((1<<AW88166_CRC_CFG_EN_BITS_LEN)-1) << AW88166_CRC_CFG_EN_START_BIT))
+
+#define AW88166_CRC_CFG_EN_DISABLE (0)
+#define AW88166_CRC_CFG_EN_DISABLE_VALUE \
+ (AW88166_CRC_CFG_EN_DISABLE << AW88166_CRC_CFG_EN_START_BIT)
+
+#define AW88166_CRC_CFG_EN_ENABLE (1)
+#define AW88166_CRC_CFG_EN_ENABLE_VALUE \
+ (AW88166_CRC_CFG_EN_ENABLE << AW88166_CRC_CFG_EN_START_BIT)
+
+#define AW88166_OCDS_START_BIT (3)
+#define AW88166_OCDS_OC (1)
+#define AW88166_OCDS_OC_VALUE \
+ (AW88166_OCDS_OC << AW88166_OCDS_START_BIT)
+
+#define AW88166_NOCLKS_START_BIT (5)
+#define AW88166_NOCLKS_NO_CLOCK (1)
+#define AW88166_NOCLKS_NO_CLOCK_VALUE \
+ (AW88166_NOCLKS_NO_CLOCK << AW88166_NOCLKS_START_BIT)
+
+#define AW88166_SWS_START_BIT (8)
+#define AW88166_SWS_SWITCHING (1)
+#define AW88166_SWS_SWITCHING_VALUE \
+ (AW88166_SWS_SWITCHING << AW88166_SWS_START_BIT)
+
+#define AW88166_BSTS_START_BIT (9)
+#define AW88166_BSTS_FINISHED (1)
+#define AW88166_BSTS_FINISHED_VALUE \
+ (AW88166_BSTS_FINISHED << AW88166_BSTS_START_BIT)
+
+#define AW88166_UVLS_START_BIT (14)
+#define AW88166_UVLS_NORMAL (0)
+#define AW88166_UVLS_NORMAL_VALUE \
+ (AW88166_UVLS_NORMAL << AW88166_UVLS_START_BIT)
+
+#define AW88166_BSTOCS_START_BIT (11)
+#define AW88166_BSTOCS_OVER_CURRENT (1)
+#define AW88166_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88166_BSTOCS_OVER_CURRENT << AW88166_BSTOCS_START_BIT)
+
+#define AW88166_OTHS_START_BIT (1)
+#define AW88166_OTHS_OT (1)
+#define AW88166_OTHS_OT_VALUE \
+ (AW88166_OTHS_OT << AW88166_OTHS_START_BIT)
+
+#define AW88166_PLLS_START_BIT (0)
+#define AW88166_PLLS_LOCKED (1)
+#define AW88166_PLLS_LOCKED_VALUE \
+ (AW88166_PLLS_LOCKED << AW88166_PLLS_START_BIT)
+
+#define AW88166_CLKS_START_BIT (4)
+#define AW88166_CLKS_STABLE (1)
+#define AW88166_CLKS_STABLE_VALUE \
+ (AW88166_CLKS_STABLE << AW88166_CLKS_START_BIT)
+
+#define AW88166_BIT_PLL_CHECK \
+ (AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE)
+
+#define AW88166_BIT_SYSST_CHECK_MASK \
+ (~(AW88166_UVLS_NORMAL_VALUE | \
+ AW88166_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_SWS_SWITCHING_VALUE | \
+ AW88166_NOCLKS_NO_CLOCK_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_OCDS_OC_VALUE | \
+ AW88166_OTHS_OT_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE))
+
+#define AW88166_BIT_SYSST_NOSWS_CHECK \
+ (AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE)
+
+#define AW88166_BIT_SYSST_SWS_CHECK \
+ (AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE | \
+ AW88166_SWS_SWITCHING_VALUE)
+
+#define AW88166_CCO_MUX_START_BIT (14)
+#define AW88166_CCO_MUX_BITS_LEN (1)
+#define AW88166_CCO_MUX_MASK \
+ (~(((1<<AW88166_CCO_MUX_BITS_LEN)-1) << AW88166_CCO_MUX_START_BIT))
+
+#define AW88166_CCO_MUX_DIVIDED (0)
+#define AW88166_CCO_MUX_DIVIDED_VALUE \
+ (AW88166_CCO_MUX_DIVIDED << AW88166_CCO_MUX_START_BIT)
+
+#define AW88166_CCO_MUX_BYPASS (1)
+#define AW88166_CCO_MUX_BYPASS_VALUE \
+ (AW88166_CCO_MUX_BYPASS << AW88166_CCO_MUX_START_BIT)
+
+#define AW88166_NOISE_GATE_EN_START_BIT (13)
+#define AW88166_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88166_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88166_NOISE_GATE_EN_BITS_LEN)-1) << AW88166_NOISE_GATE_EN_START_BIT))
+
+#define AW88166_WDT_CNT_START_BIT (0)
+#define AW88166_WDT_CNT_BITS_LEN (8)
+#define AW88166_WDT_CNT_MASK \
+ (~(((1<<AW88166_WDT_CNT_BITS_LEN)-1) << AW88166_WDT_CNT_START_BIT))
+
+#define AW88166_EF_ISN_GESLP_START_BIT (0)
+#define AW88166_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88166_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88166_EF_ISN_GESLP_BITS_LEN)-1) << AW88166_EF_ISN_GESLP_START_BIT))
+#define AW88166_EF_ISN_GESLP_SHIFT (0)
+
+#define AW88166_EF_VSN_GESLP_START_BIT (10)
+#define AW88166_EF_VSN_GESLP_BITS_LEN (6)
+#define AW88166_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88166_EF_VSN_GESLP_BITS_LEN)-1) << AW88166_EF_VSN_GESLP_START_BIT))
+#define AW88166_EF_VSN_GESLP_SHIFT (10)
+
+#define AW88166_EF_VSN_H3BITS_START_BIT (13)
+#define AW88166_EF_VSN_H3BITS_BITS_LEN (3)
+#define AW88166_EF_VSN_H3BITS_MASK \
+ (~(((1<<AW88166_EF_VSN_H3BITS_BITS_LEN)-1) << AW88166_EF_VSN_H3BITS_START_BIT))
+#define AW88166_EF_VSN_H3BITS_SHIFT (10)
+#define AW88166_EF_VSN_H3BITS_SIGN_MASK (0x7)
+
+#define AW88166_EF_ISN_H5BITS_START_BIT (8)
+#define AW88166_EF_ISN_H5BITS_BITS_LEN (5)
+#define AW88166_EF_ISN_H5BITS_MASK \
+ (~(((1<<AW88166_EF_ISN_H5BITS_BITS_LEN)-1) << AW88166_EF_ISN_H5BITS_START_BIT))
+#define AW88166_EF_ISN_H5BITS_SIGN_MASK (0x1F)
+#define AW88166_EF_ISN_H5BITS_SHIFT (3)
+
+#define AW88166_VSCAL_FACTOR (65300)
+#define AW88166_ISCAL_FACTOR (34667)
+#define AW88166_CABL_BASE_VALUE (1000)
+#define AW88166_VCALK_SIGN_MASK (~(1 << 5))
+#define AW88166_VCALK_NEG_MASK (0xFFE0)
+#define AW88166_ICALK_SIGN_MASK (~(1 << 9))
+#define AW88166_ICALK_NEG_MASK (0xFE00)
+#define AW88166_ICABLK_FACTOR (1)
+#define AW88166_VCABLK_FACTOR (2)
+#define AW88166_VCALB_ADJ_FACTOR (12)
+#define AW88166_VCALB_ACCURACY (1 << 12)
+#define AW88166_DSP_RE_SHIFT (12)
+#define AW88166_CALI_RE_MAX (15000)
+#define AW88166_CALI_RE_MIN (4000)
+#define AW88166_VOLUME_STEP_DB (64)
+#define AW88166_VOL_DEFAULT_VALUE (0)
+#define AW88166_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88166_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+
+#define AW88166_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88166_DSP_ROM_CHECK_DATA (0xFF99)
+
+#define AW88166_DEV_DEFAULT_CH (0)
+#define AW88166_DEV_DSP_CHECK_MAX (5)
+#define AW88166_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW_FW_ADDR_LEN (4)
+#define AW88166_CRC_CHECK_PASS_VAL (0x4)
+#define AW88166_CRC_CFG_BASE_ADDR (0xD80)
+#define AW88166_CRC_FW_BASE_ADDR (0x4C0)
+#define AW88166_DEV_SYSST_CHECK_MAX (10)
+#define AW88166_START_RETRIES (5)
+#define AW88166_START_WORK_DELAY_MS (0)
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+#define AW88166_CHIP_ID (0x2066)
+#define AW88166_I2C_NAME "aw88166"
+#define AW88166_ACF_FILE "aw88166_acf.bin"
+
+#define AW88166_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88166_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AW88166_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW_EF_AND_CHECK = 0,
+ AW_EF_OR_CHECK,
+};
+
+enum {
+ AW88166_DSP_FW_UPDATE_OFF = 0,
+ AW88166_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88166_FORCE_UPDATE_OFF = 0,
+ AW88166_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88166_1000_US = 1000,
+ AW88166_2000_US = 2000,
+ AW88166_3000_US = 3000,
+ AW88166_4000_US = 4000,
+};
+
+enum AW88166_DEV_STATUS {
+ AW88166_DEV_PW_OFF = 0,
+ AW88166_DEV_PW_ON,
+};
+
+enum AW88166_DEV_FW_STATUS {
+ AW88166_DEV_FW_FAILED = 0,
+ AW88166_DEV_FW_OK,
+};
+
+enum AW88166_DEV_MEMCLK {
+ AW88166_DEV_MEMCLK_OSC = 0,
+ AW88166_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88166_DEV_DSP_CFG {
+ AW88166_DEV_DSP_WORK = 0,
+ AW88166_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88166_SYNC_START = 0,
+ AW88166_ASYNC_START,
+};
+
+enum {
+ AW88166_RECORD_SEC_DATA = 0,
+ AW88166_RECOVERY_SEC_DATA = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
new file mode 100644
index 000000000000..8f37bfb974ae
--- /dev/null
+++ b/sound/soc/codecs/aw88261.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.c -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88261.h"
+#include "aw88395/aw88395_data_type.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw88261_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88261_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int real_value, volume;
+ unsigned int reg_value;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
+ real_value = DB_TO_REG_VAL(volume);
+
+ regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
+
+ real_value = (real_value | (reg_value & AW88261_VOL_START_MASK));
+
+ dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
+
+ regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
+}
+
+static void aw88261_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw88261_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw88261_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time,
+ aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw88261_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw88261_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
+ aw88261_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88261_MUTE_VOL) {
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+ ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+ ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
+}
+
+static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ if (pwd)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE);
+}
+
+static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE);
+}
+
+static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw88261_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
+ aw88261_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88261_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ unsigned int int_status;
+
+ /* read int status and clear */
+ regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
+ /* make sure int status is clear */
+ regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
+}
+
+static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ return ret;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88261_CCO_MUX_MASK);
+ if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+ ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+ ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw88261_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw88261_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
+ & AW88261_BIT_SYSST_CHECK;
+ if (check_val != AW88261_BIT_SYSST_CHECK) {
+ dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ reg_val, AW88261_BIT_SYSST_CHECK);
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+ if (uls_hmute)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_ULS_HMUTE_MASK,
+ AW88261_ULS_HMUTE_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_ULS_HMUTE_MASK,
+ AW88261_ULS_HMUTE_DISABLE_VALUE);
+}
+
+static void aw88261_reg_force_set(struct aw88261 *aw88261)
+{
+ if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
+ /* set FORCE_PWM */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
+ AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+ /* set BOOST_OS_WIDTH */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
+ AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+ /* set BURST_LOOPR */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
+ AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+ /* set RSQN_DLY */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
+ AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+ /* set BURST_SSMODE */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
+ AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+ /* set BST_BURST */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
+ AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+ } else {
+ dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
+ }
+}
+
+static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+ u16 reg_icalk, reg_icalkl;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK);
+
+ reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT);
+
+ if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK))
+ reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG;
+
+ *icalk = (int16_t)reg_icalk;
+
+ return ret;
+}
+
+static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ u16 reg_vcalk, reg_vcalkl;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK);
+
+ reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT);
+
+ if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK)
+ reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG);
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw88261_dev_set_vcalb(struct aw_device *aw_dev)
+{
+ int16_t icalk_val, vcalk_val;
+ int icalk, vcalk, vcalb;
+ u32 reg_val;
+ int ret;
+
+ ret = aw88261_dev_get_icalk(aw_dev, &icalk_val);
+ if (ret)
+ return ret;
+
+ ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+
+ icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val;
+ vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val;
+ if (!vcalk)
+ return -EINVAL;
+
+ vcalb = AW88261_VCAL_FACTOR * icalk / vcalk;
+ reg_val = (unsigned int)vcalb;
+
+ dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x",
+ icalk, vcalk, vcalb, reg_val);
+ ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val);
+
+ return ret;
+}
+
+static int aw88261_dev_reg_update(struct aw88261 *aw88261,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int read_val, efcheck_val, read_vol;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u16 reg_val;
+ u8 reg_addr;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88261_SYSCTRL_REG) {
+ aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK);
+ ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+ if (ret)
+ break;
+
+ read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) |
+ (~AW88261_HMUTE_MASK);
+ reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK);
+ reg_val |= read_val;
+
+ /* enable uls hmute */
+ reg_val &= AW88261_ULS_HMUTE_MASK;
+ reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88261_DBGCTRL_REG) {
+ efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK);
+ if (efcheck_val == AW88261_OR_VALUE)
+ aw88261->efuse_check = AW88261_EF_OR_CHECK;
+ else
+ aw88261->efuse_check = AW88261_EF_AND_CHECK;
+ }
+
+ /* i2stxen */
+ if (reg_addr == AW88261_I2SCTRL3_REG) {
+ /* close tx */
+ reg_val &= AW88261_I2STXEN_MASK;
+ reg_val |= AW88261_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88261_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88261_VOL_MASK)) >>
+ AW88261_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume =
+ REG_VAL_TO_DB(read_vol);
+ }
+
+ if (reg_addr == AW88261_VSNTM1_REG)
+ continue;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ break;
+ }
+
+ ret = aw88261_dev_set_vcalb(aw_dev);
+ if (ret)
+ return ret;
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+
+ /* keep min volume */
+ aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+ return ret;
+}
+
+static int aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+
+static int aw88261_dev_fw_update(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw88261_dev_reg_update(aw88261, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return ret;
+}
+
+static int aw88261_dev_start(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88261_DEV_PW_ON) {
+ dev_info(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ /* power on */
+ aw88261_dev_pwd(aw_dev, false);
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+
+ ret = aw88261_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw88261_dev_amppd(aw_dev, false);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw88261_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ /* enable tx feedback */
+ aw88261_dev_i2s_tx_enable(aw_dev, true);
+
+ if (aw88261->amppd_st)
+ aw88261_dev_amppd(aw_dev, true);
+
+ aw88261_reg_force_set(aw88261);
+
+ /* close uls mute */
+ aw88261_dev_uls_hmute(aw_dev, false);
+
+ /* close mute */
+ if (!aw88261->mute_st)
+ aw88261_dev_mute(aw_dev, false);
+
+ /* clear inturrupt */
+ aw88261_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88261_DEV_PW_ON;
+
+ return 0;
+
+sysst_check_fail:
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+ aw88261_dev_clear_int_status(aw_dev);
+ aw88261_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw88261_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88261_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw88261_dev_stop(struct aw_device *aw_dev)
+{
+ if (aw_dev->status == AW88261_DEV_PW_OFF) {
+ dev_info(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88261_DEV_PW_OFF;
+
+ /* clear inturrupt */
+ aw88261_dev_clear_int_status(aw_dev);
+
+ aw88261_dev_uls_hmute(aw_dev, true);
+ /* set mute */
+ aw88261_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+ /* enable amppd */
+ aw88261_dev_amppd(aw_dev, true);
+
+ /* set power down */
+ aw88261_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88261_reg_update(struct aw88261 *aw88261, bool force)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ if (force) {
+ ret = regmap_write(aw_dev->regmap,
+ AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret)
+ return ret;
+ } else {
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret)
+ return ret;
+ } else {
+ ret = 0;
+ }
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return ret;
+}
+
+static void aw88261_start_pa(struct aw88261 *aw88261)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88261_START_RETRIES; i++) {
+ ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ continue;
+ }
+ ret = aw88261_dev_start(aw88261);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
+ continue;
+ } else {
+ dev_info(aw88261->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88261_startup_work(struct work_struct *work)
+{
+ struct aw88261 *aw88261 =
+ container_of(work, struct aw88261, start_work.work);
+
+ mutex_lock(&aw88261->lock);
+ aw88261_start_pa(aw88261);
+ mutex_unlock(&aw88261->lock);
+}
+
+static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
+{
+ if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
+ return;
+
+ if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
+ return;
+
+ if (sync_start == AW88261_SYNC_START)
+ aw88261_start_pa(aw88261);
+ else
+ queue_delayed_work(system_dfl_wq,
+ &aw88261->start_work,
+ AW88261_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88261_dai[] = {
+ {
+ .name = "aw88261-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ },
+};
+
+static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88261->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88261->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw88261_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88261->aw_pa->prof_info.count;
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88261_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88261->lock);
+ ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88261->lock);
+ return 0;
+ }
+
+ if (aw88261->aw_pa->status) {
+ aw88261_dev_stop(aw88261->aw_pa);
+ aw88261_start(aw88261, AW88261_SYNC_START);
+ }
+
+ mutex_unlock(&aw88261->lock);
+
+ return 1;
+}
+
+static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88261->aw_pa->fade_step != value) {
+ aw88261->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88261_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
+ 6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
+ aw88261_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
+ aw88261_get_fade_step, aw88261_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88261_get_fade_in_time, aw88261_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88261_get_fade_out_time, aw88261_set_fade_out_time),
+ AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
+ aw88261_profile_get, aw88261_profile_set),
+};
+
+static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88261->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88261_start(aw88261, AW88261_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88261_dev_stop(aw88261->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88261->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88261_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88261_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88261_frcset_check(struct aw88261 *aw88261)
+{
+ unsigned int reg_val;
+ u16 temh, teml, tem;
+ int ret;
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+ temh = ((u16)reg_val & (~AW88261_TEMH_MASK));
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+ teml = ((u16)reg_val & (~AW88261_TEML_MASK));
+
+ if (aw88261->efuse_check == AW88261_EF_OR_CHECK)
+ tem = (temh | teml);
+ else
+ tem = (temh & teml);
+
+ if (tem == AW88261_DEFAULT_CFG)
+ aw88261->frcset_en = AW88261_FRCSET_ENABLE;
+ else
+ aw88261->frcset_en = AW88261_FRCSET_DISABLE;
+
+ dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d",
+ tem, aw88261->frcset_en);
+
+ return ret;
+}
+
+static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ aw_dev->fade_in_time = AW88261_500_US;
+ aw_dev->fade_out_time = AW88261_500_US;
+ aw_dev->prof_cur = AW88261_INIT_PROFILE;
+ aw_dev->prof_index = AW88261_INIT_PROFILE;
+
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = aw88261_frcset_check(aw88261);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88261_dev_clear_int_status(aw_dev);
+
+ aw88261_dev_uls_hmute(aw_dev, true);
+
+ aw88261_dev_mute(aw_dev, true);
+
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+ aw88261_dev_amppd(aw_dev, true);
+
+ aw88261_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88261_request_firmware_file(struct aw88261 *aw88261)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev);
+ if (ret)
+ return dev_err_probe(aw88261->aw_pa->dev, ret,
+ "load [%s] failed!", AW88261_ACF_FILE);
+
+ dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88261_ACF_FILE, cont ? cont->size : 0);
+
+ aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88261->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88261->aw_cfg->len = (int)cont->size;
+ memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88261->lock);
+ /* aw device init */
+ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg);
+ if (ret)
+ dev_err(aw88261->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88261->lock);
+
+ return ret;
+}
+
+static int aw88261_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
+
+ ret = aw88261_request_firmware_file(aw88261);
+ if (ret)
+ return dev_err_probe(aw88261->aw_pa->dev, ret,
+ "aw88261_request_firmware_file failed\n");
+
+ /* add widgets */
+ ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
+ ARRAY_SIZE(aw88261_dapm_widgets));
+ if (ret)
+ return ret;
+
+ /* add route */
+ ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
+ ARRAY_SIZE(aw88261_audio_map));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, aw88261_controls,
+ ARRAY_SIZE(aw88261_controls));
+
+ return ret;
+}
+
+static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88261->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
+ .probe = aw88261_codec_probe,
+ .remove = aw88261_codec_remove,
+};
+
+static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW88261_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw88261->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW88261_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+ if (chip_id != AW88261_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+
+ dev_info(&i2c->dev, "chip id = %x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ aw88261->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = AW88261_CHIP_ID;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = 0;
+ aw_dev->fw_status = AW88261_DEV_FW_FAILED;
+ aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
+ aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
+ aw88261_parse_channel_dt(aw88261);
+
+ return ret;
+}
+
+static int aw88261_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88261 *aw88261;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL);
+ if (!aw88261)
+ return -ENOMEM;
+
+ mutex_init(&aw88261->lock);
+
+ i2c_set_clientdata(i2c, aw88261);
+
+ aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
+ if (IS_ERR(aw88261->regmap)) {
+ ret = PTR_ERR(aw88261->regmap);
+ return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret);
+ }
+
+ /* aw pa init */
+ ret = aw88261_init(aw88261, i2c, aw88261->regmap);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88261,
+ aw88261_dai, ARRAY_SIZE(aw88261_dai));
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw88261_i2c_id[] = {
+ { AW88261_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
+
+static struct i2c_driver aw88261_i2c_driver = {
+ .driver = {
+ .name = AW88261_I2C_NAME,
+ },
+ .probe = aw88261_i2c_probe,
+ .id_table = aw88261_i2c_id,
+};
+module_i2c_driver(aw88261_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
new file mode 100644
index 000000000000..734d0f93ced9
--- /dev/null
+++ b/sound/soc/codecs/aw88261.h
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.h -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_H__
+#define __AW88261_H__
+
+#define AW88261_ID_REG (0x00)
+#define AW88261_SYSST_REG (0x01)
+#define AW88261_SYSINT_REG (0x02)
+#define AW88261_SYSINTM_REG (0x03)
+#define AW88261_SYSCTRL_REG (0x04)
+#define AW88261_SYSCTRL2_REG (0x05)
+#define AW88261_I2SCTRL1_REG (0x06)
+#define AW88261_I2SCTRL2_REG (0x07)
+#define AW88261_I2SCTRL3_REG (0x08)
+#define AW88261_DACCFG1_REG (0x09)
+#define AW88261_DACCFG2_REG (0x0A)
+#define AW88261_DACCFG3_REG (0x0B)
+#define AW88261_DACCFG4_REG (0x0C)
+#define AW88261_DACCFG5_REG (0x0D)
+#define AW88261_DACCFG6_REG (0x0E)
+#define AW88261_DACCFG7_REG (0x0F)
+#define AW88261_DACCFG8_REG (0x10)
+#define AW88261_PWMCTRL1_REG (0x11)
+#define AW88261_PWMCTRL2_REG (0x12)
+#define AW88261_I2SCFG1_REG (0x13)
+#define AW88261_DBGCTRL_REG (0x14)
+#define AW88261_DACCFG9_REG (0x15)
+#define AW88261_DACCFG10_REG (0x16)
+#define AW88261_DACST_REG (0x20)
+#define AW88261_VBAT_REG (0x21)
+#define AW88261_TEMP_REG (0x22)
+#define AW88261_PVDD_REG (0x23)
+#define AW88261_ISNDAT_REG (0x24)
+#define AW88261_VSNDAT_REG (0x25)
+#define AW88261_I2SINT_REG (0x26)
+#define AW88261_I2SCAPCNT_REG (0x27)
+#define AW88261_ANASTA1_REG (0x28)
+#define AW88261_ANASTA2_REG (0x29)
+#define AW88261_ANASTA3_REG (0x2A)
+#define AW88261_TESTDET_REG (0x2B)
+#define AW88261_DSMCFG1_REG (0x30)
+#define AW88261_DSMCFG2_REG (0x31)
+#define AW88261_DSMCFG3_REG (0x32)
+#define AW88261_DSMCFG4_REG (0x33)
+#define AW88261_DSMCFG5_REG (0x34)
+#define AW88261_DSMCFG6_REG (0x35)
+#define AW88261_DSMCFG7_REG (0x36)
+#define AW88261_DSMCFG8_REG (0x37)
+#define AW88261_TESTIN_REG (0x38)
+#define AW88261_TESTOUT_REG (0x39)
+#define AW88261_SADCCTRL1_REG (0x3A)
+#define AW88261_SADCCTRL2_REG (0x3B)
+#define AW88261_SADCCTRL3_REG (0x3C)
+#define AW88261_SADCCTRL4_REG (0x3D)
+#define AW88261_SADCCTRL5_REG (0x3E)
+#define AW88261_SADCCTRL6_REG (0x3F)
+#define AW88261_SADCCTRL7_REG (0x40)
+#define AW88261_VSNTM1_REG (0x50)
+#define AW88261_VSNTM2_REG (0x51)
+#define AW88261_ISNCTRL1_REG (0x52)
+#define AW88261_ISNCTRL2_REG (0x53)
+#define AW88261_PLLCTRL1_REG (0x54)
+#define AW88261_PLLCTRL2_REG (0x55)
+#define AW88261_PLLCTRL3_REG (0x56)
+#define AW88261_CDACTRL1_REG (0x57)
+#define AW88261_CDACTRL2_REG (0x58)
+#define AW88261_DITHERCFG1_REG (0x59)
+#define AW88261_DITHERCFG2_REG (0x5A)
+#define AW88261_DITHERCFG3_REG (0x5B)
+#define AW88261_CPCTRL_REG (0x5C)
+#define AW88261_BSTCTRL1_REG (0x60)
+#define AW88261_BSTCTRL2_REG (0x61)
+#define AW88261_BSTCTRL3_REG (0x62)
+#define AW88261_BSTCTRL4_REG (0x63)
+#define AW88261_BSTCTRL5_REG (0x64)
+#define AW88261_BSTCTRL6_REG (0x65)
+#define AW88261_BSTCTRL7_REG (0x66)
+#define AW88261_BSTCTRL8_REG (0x67)
+#define AW88261_BSTCTRL9_REG (0x68)
+#define AW88261_TM_REG (0x6F)
+#define AW88261_TESTCTRL1_REG (0x70)
+#define AW88261_TESTCTRL2_REG (0x71)
+#define AW88261_EFCTRL1_REG (0x72)
+#define AW88261_EFCTRL2_REG (0x73)
+#define AW88261_EFWH_REG (0x74)
+#define AW88261_EFWM2_REG (0x75)
+#define AW88261_EFWM1_REG (0x76)
+#define AW88261_EFWL_REG (0x77)
+#define AW88261_EFRH4_REG (0x78)
+#define AW88261_EFRH3_REG (0x79)
+#define AW88261_EFRH2_REG (0x7A)
+#define AW88261_EFRH1_REG (0x7B)
+#define AW88261_EFRL4_REG (0x7C)
+#define AW88261_EFRL3_REG (0x7D)
+#define AW88261_EFRL2_REG (0x7E)
+#define AW88261_EFRL1_REG (0x7F)
+
+#define AW88261_REG_MAX (0x80)
+#define AW88261_EF_DBMD_MASK (0xfff7)
+#define AW88261_OR_VALUE (0x0008)
+
+#define AW88261_TEMH_MASK (0x83ff)
+#define AW88261_TEML_MASK (0x83ff)
+#define AW88261_DEFAULT_CFG (0x0000)
+
+#define AW88261_ICALK_SHIFT (0)
+#define AW88261_ICALKL_SHIFT (0)
+#define AW88261_VCALK_SHIFT (0)
+#define AW88261_VCALKL_SHIFT (0)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_UVLS_START_BIT (14)
+#define AW88261_UVLS_NORMAL (0)
+#define AW88261_UVLS_NORMAL_VALUE \
+ (AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT)
+
+#define AW88261_BSTOCS_START_BIT (11)
+#define AW88261_BSTOCS_OVER_CURRENT (1)
+#define AW88261_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT)
+
+#define AW88261_BSTS_START_BIT (9)
+#define AW88261_BSTS_FINISHED (1)
+#define AW88261_BSTS_FINISHED_VALUE \
+ (AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT)
+
+#define AW88261_SWS_START_BIT (8)
+#define AW88261_SWS_SWITCHING (1)
+#define AW88261_SWS_SWITCHING_VALUE \
+ (AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT)
+
+#define AW88261_NOCLKS_START_BIT (5)
+#define AW88261_NOCLKS_NO_CLOCK (1)
+#define AW88261_NOCLKS_NO_CLOCK_VALUE \
+ (AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT)
+
+#define AW88261_CLKS_START_BIT (4)
+#define AW88261_CLKS_STABLE (1)
+#define AW88261_CLKS_STABLE_VALUE \
+ (AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT)
+
+#define AW88261_OCDS_START_BIT (3)
+#define AW88261_OCDS_OC (1)
+#define AW88261_OCDS_OC_VALUE \
+ (AW88261_OCDS_OC << AW88261_OCDS_START_BIT)
+
+#define AW88261_OTHS_START_BIT (1)
+#define AW88261_OTHS_OT (1)
+#define AW88261_OTHS_OT_VALUE \
+ (AW88261_OTHS_OT << AW88261_OTHS_START_BIT)
+
+#define AW88261_PLLS_START_BIT (0)
+#define AW88261_PLLS_LOCKED (1)
+#define AW88261_PLLS_LOCKED_VALUE \
+ (AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT)
+
+#define AW88261_BIT_PLL_CHECK \
+ (AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_BIT_SYSST_CHECK_MASK \
+ (~(AW88261_UVLS_NORMAL_VALUE | \
+ AW88261_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_NOCLKS_NO_CLOCK_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_OCDS_OC_VALUE | \
+ AW88261_OTHS_OT_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE))
+
+#define AW88261_BIT_SYSST_CHECK \
+ (AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_ULS_HMUTE_START_BIT (14)
+#define AW88261_ULS_HMUTE_BITS_LEN (1)
+#define AW88261_ULS_HMUTE_MASK \
+ (~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT))
+
+#define AW88261_ULS_HMUTE_DISABLE (0)
+#define AW88261_ULS_HMUTE_DISABLE_VALUE \
+ (AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_ULS_HMUTE_ENABLE (1)
+#define AW88261_ULS_HMUTE_ENABLE_VALUE \
+ (AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_START_BIT (8)
+#define AW88261_HMUTE_BITS_LEN (1)
+#define AW88261_HMUTE_MASK \
+ (~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT))
+
+#define AW88261_HMUTE_DISABLE (0)
+#define AW88261_HMUTE_DISABLE_VALUE \
+ (AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_ENABLE (1)
+#define AW88261_HMUTE_ENABLE_VALUE \
+ (AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_AMPPD_WORKING (0)
+#define AW88261_AMPPD_WORKING_VALUE \
+ (AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT)
+
+#define AW88261_AMPPD_POWER_DOWN (1)
+#define AW88261_AMPPD_POWER_DOWN_VALUE \
+ (AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT)
+
+#define AW88261_PWDN_START_BIT (0)
+#define AW88261_PWDN_BITS_LEN (1)
+#define AW88261_PWDN_MASK \
+ (~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT))
+
+#define AW88261_PWDN_WORKING (0)
+#define AW88261_PWDN_WORKING_VALUE \
+ (AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT)
+
+#define AW88261_PWDN_POWER_DOWN (1)
+#define AW88261_PWDN_POWER_DOWN_VALUE \
+ (AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT)
+
+#define AW88261_MUTE_VOL (90 * 8)
+#define AW88261_VOLUME_STEP_DB (6 * 8)
+
+#define AW88261_VOL_6DB_START (6)
+
+#define AW88261_VOL_START_BIT (0)
+#define AW88261_VOL_BITS_LEN (10)
+#define AW88261_VOL_MASK \
+ (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
+
+#define AW88261_VOL_DEFAULT_VALUE (0)
+
+#define AW88261_I2STXEN_START_BIT (6)
+#define AW88261_I2STXEN_BITS_LEN (1)
+#define AW88261_I2STXEN_MASK \
+ (~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT))
+
+#define AW88261_I2STXEN_DISABLE (0)
+#define AW88261_I2STXEN_DISABLE_VALUE \
+ (AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_I2STXEN_ENABLE (1)
+#define AW88261_I2STXEN_ENABLE_VALUE \
+ (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_CCO_MUX_START_BIT (14)
+#define AW88261_CCO_MUX_BITS_LEN (1)
+#define AW88261_CCO_MUX_MASK \
+ (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
+
+#define AW88261_CCO_MUX_DIVIDED (0)
+#define AW88261_CCO_MUX_DIVIDED_VALUE \
+ (AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_CCO_MUX_BYPASS (1)
+#define AW88261_CCO_MUX_BYPASS_VALUE \
+ (AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88261_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88261_FORCE_PWM_START_BIT (12)
+#define AW88261_FORCE_PWM_BITS_LEN (1)
+#define AW88261_FORCE_PWM_MASK \
+ (~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT))
+
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM (1)
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE \
+ (AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT)
+
+#define AW88261_BST_OS_WIDTH_START_BIT (0)
+#define AW88261_BST_OS_WIDTH_BITS_LEN (3)
+#define AW88261_BST_OS_WIDTH_MASK \
+ (~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT))
+
+#define AW88261_BST_OS_WIDTH_50NS (4)
+#define AW88261_BST_OS_WIDTH_50NS_VALUE \
+ (AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT)
+
+/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */
+#define AW88261_BST_LOOPR_START_BIT (0)
+#define AW88261_BST_LOOPR_BITS_LEN (2)
+#define AW88261_BST_LOOPR_MASK \
+ (~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT))
+
+#define AW88261_BST_LOOPR_340K (2)
+#define AW88261_BST_LOOPR_340K_VALUE \
+ (AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT)
+
+/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */
+#define AW88261_RSQN_DLY_START_BIT (14)
+#define AW88261_RSQN_DLY_BITS_LEN (2)
+#define AW88261_RSQN_DLY_MASK \
+ (~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT))
+
+#define AW88261_RSQN_DLY_35NS (2)
+#define AW88261_RSQN_DLY_35NS_VALUE \
+ (AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT)
+
+/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */
+#define AW88261_BURST_SSMODE_START_BIT (3)
+#define AW88261_BURST_SSMODE_BITS_LEN (1)
+#define AW88261_BURST_SSMODE_MASK \
+ (~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT))
+
+#define AW88261_BURST_SSMODE_FAST (0)
+#define AW88261_BURST_SSMODE_FAST_VALUE \
+ (AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT)
+
+/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */
+#define AW88261_BST_BURST_START_BIT (7)
+#define AW88261_BST_BURST_BITS_LEN (3)
+#define AW88261_BST_BURST_MASK \
+ (~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT))
+
+#define AW88261_BST_BURST_30MA (2)
+#define AW88261_BST_BURST_30MA_VALUE \
+ (AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_VSN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_ISN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT))
+
+#define AW88261_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT))
+
+#define AW88261_CABL_BASE_VALUE (1000)
+#define AW88261_ICABLK_FACTOR (1)
+#define AW88261_VCABLK_FACTOR (1)
+
+#define AW88261_VCAL_FACTOR (1<<13)
+
+#define AW88261_START_RETRIES (5)
+#define AW88261_START_WORK_DELAY_MS (0)
+
+#define AW88261_I2C_NAME "aw88261"
+
+#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+
+#define AW88261_DEV_DEFAULT_CH (0)
+#define AW88261_ACF_FILE "aw88261_acf.bin"
+#define AW88261_DEV_SYSST_CHECK_MAX (10)
+#define AW88261_SOFT_RESET_VALUE (0x55aa)
+#define AW88261_REG_TO_DB (0x3f)
+#define AW88261_VOL_START_MASK (0xfc00)
+#define AW88261_INIT_PROFILE (0)
+
+#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \
+ AW88261_VOLUME_STEP_DB) + \
+ ((value) & AW88261_REG_TO_DB))
+#define DB_TO_REG_VAL(value) ((((value) / AW88261_VOLUME_STEP_DB) << \
+ AW88261_VOL_6DB_START) + \
+ ((value) % AW88261_VOLUME_STEP_DB))
+
+#define AW88261_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW88261_SYNC_START = 0,
+ AW88261_ASYNC_START,
+};
+
+enum aw88261_id {
+ AW88261_CHIP_ID = 0x2113,
+};
+
+enum {
+ AW88261_500_US = 500,
+ AW88261_1000_US = 1000,
+ AW88261_2000_US = 2000,
+};
+
+enum {
+ AW88261_DEV_PW_OFF = 0,
+ AW88261_DEV_PW_ON,
+};
+
+enum {
+ AW88261_DEV_FW_FAILED = 0,
+ AW88261_DEV_FW_OK,
+};
+
+enum {
+ AW88261_EF_AND_CHECK = 0,
+ AW88261_EF_OR_CHECK,
+};
+
+enum {
+ AW88261_FRCSET_DISABLE = 0,
+ AW88261_FRCSET_ENABLE,
+};
+
+struct aw88261 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+
+ int efuse_check;
+ int frcset_en;
+ unsigned int mute_st;
+ unsigned int amppd_st;
+
+ bool phase_sync;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
new file mode 100644
index 000000000000..3602b5b9f7d7
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395.c -- ALSA SoC AW88395 codec support
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88395.h"
+#include "aw88395_device.h"
+#include "aw88395_lib.h"
+#include "aw88395_reg.h"
+
+static const struct regmap_config aw88395_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88395_REG_MAX - 1,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88395_start_pa(struct aw88395 *aw88395)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88395_START_RETRIES; i++) {
+ ret = aw88395_dev_start(aw88395->aw_pa);
+ if (ret) {
+ dev_err(aw88395->aw_pa->dev, "aw88395 device start failed. retry = %d", i);
+ ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_ON, true);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_info(aw88395->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88395_startup_work(struct work_struct *work)
+{
+ struct aw88395 *aw88395 =
+ container_of(work, struct aw88395, start_work.work);
+
+ mutex_lock(&aw88395->lock);
+ aw88395_start_pa(aw88395);
+ mutex_unlock(&aw88395->lock);
+}
+
+static void aw88395_start(struct aw88395 *aw88395, bool sync_start)
+{
+ int ret;
+
+ if (aw88395->aw_pa->fw_status != AW88395_DEV_FW_OK)
+ return;
+
+ if (aw88395->aw_pa->status == AW88395_DEV_PW_ON)
+ return;
+
+ ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_OFF, true);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "fw update failed.");
+ return;
+ }
+
+ if (sync_start == AW88395_SYNC_START)
+ aw88395_start_pa(aw88395);
+ else
+ queue_delayed_work(system_dfl_wq,
+ &aw88395->start_work,
+ AW88395_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88395_dai[] = {
+ {
+ .name = "aw88395-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88395_RATES,
+ .formats = AW88395_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88395_RATES,
+ .formats = AW88395_FORMATS,
+ },
+ },
+};
+
+static int aw88395_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88395_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88395_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88395_dev_get_profile_count(aw88395->aw_pa);
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88395_dev_get_prof_name(aw88395->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88395_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88395_dev_get_profile_index(aw88395->aw_pa);
+
+ return 0;
+}
+
+static int aw88395_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88395->lock);
+ ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret < 0) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88395->lock);
+ return 0;
+ }
+
+ if (aw88395->aw_pa->status) {
+ aw88395_dev_stop(aw88395->aw_pa);
+ aw88395_start(aw88395, AW88395_SYNC_START);
+ }
+
+ mutex_unlock(&aw88395->lock);
+
+ return 1;
+}
+
+static int aw88395_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88395_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88395_dev_set_volume(aw88395->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88395->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88395_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88395->aw_pa->fade_step != value) {
+ aw88395->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88395_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw_dev->cali_desc.cali_re != value) {
+ aw_dev->cali_desc.cali_re = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88395_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88395_SYSCTRL2_REG,
+ 6, AW88395_MUTE_VOL, 0, aw88395_volume_get,
+ aw88395_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88395_MUTE_VOL, 0,
+ aw88395_get_fade_step, aw88395_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88395_get_fade_in_time, aw88395_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88395_get_fade_out_time, aw88395_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88395_CALI_RE_MAX, 0,
+ aw88395_re_get, aw88395_re_set),
+ AW88395_PROFILE_EXT("Profile Set", aw88395_profile_info,
+ aw88395_profile_get, aw88395_profile_set),
+};
+
+static int aw88395_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88395->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88395_start(aw88395, AW88395_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88395_dev_stop(aw88395->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88395->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88395_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88395_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88395_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88395_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88395->start_work, aw88395_startup_work);
+
+ /* add widgets */
+ ret = snd_soc_dapm_new_controls(dapm, aw88395_dapm_widgets,
+ ARRAY_SIZE(aw88395_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ /* add route */
+ ret = snd_soc_dapm_add_routes(dapm, aw88395_audio_map,
+ ARRAY_SIZE(aw88395_audio_map));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, aw88395_controls,
+ ARRAY_SIZE(aw88395_controls));
+
+ return ret;
+}
+
+static void aw88395_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88395->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88395 = {
+ .probe = aw88395_codec_probe,
+ .remove = aw88395_codec_remove,
+};
+
+static struct aw88395 *aw88395_malloc_init(struct i2c_client *i2c)
+{
+ struct aw88395 *aw88395 = devm_kzalloc(&i2c->dev,
+ sizeof(struct aw88395), GFP_KERNEL);
+ if (!aw88395)
+ return NULL;
+
+ mutex_init(&aw88395->lock);
+
+ return aw88395;
+}
+
+static void aw88395_hw_reset(struct aw88395 *aw88395)
+{
+ if (aw88395->reset_gpio) {
+ gpiod_set_value_cansleep(aw88395->reset_gpio, 0);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ gpiod_set_value_cansleep(aw88395->reset_gpio, 1);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ } else {
+ dev_err(aw88395->aw_pa->dev, "%s failed", __func__);
+ }
+}
+
+static int aw88395_request_firmware_file(struct aw88395 *aw88395)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88395_ACF_FILE, aw88395->aw_pa->dev);
+ if ((ret < 0) || (!cont)) {
+ dev_err(aw88395->aw_pa->dev, "load [%s] failed!", AW88395_ACF_FILE);
+ return ret;
+ }
+
+ dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88395_ACF_FILE, cont ? cont->size : 0);
+
+ aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88395->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88395->aw_cfg->len = (int)cont->size;
+ memcpy(aw88395->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__);
+
+ mutex_lock(&aw88395->lock);
+ /* aw device init */
+ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg);
+ if (ret < 0)
+ dev_err(aw88395->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88395->lock);
+
+ return ret;
+}
+
+static int aw88395_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88395 *aw88395;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+ dev_err(&i2c->dev, "check_functionality failed");
+ return -EIO;
+ }
+
+ aw88395 = aw88395_malloc_init(i2c);
+ if (!aw88395) {
+ dev_err(&i2c->dev, "malloc aw88395 failed");
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, aw88395);
+
+ aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88395->reset_gpio))
+ dev_info(&i2c->dev, "reset gpio not defined\n");
+
+ /* hardware reset */
+ aw88395_hw_reset(aw88395);
+
+ aw88395->regmap = devm_regmap_init_i2c(i2c, &aw88395_remap_config);
+ if (IS_ERR(aw88395->regmap)) {
+ ret = PTR_ERR(aw88395->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* aw pa init */
+ ret = aw88395_init(&aw88395->aw_pa, i2c, aw88395->regmap);
+ if (ret < 0)
+ return ret;
+
+ ret = aw88395_request_firmware_file(aw88395);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s failed\n", __func__);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88395,
+ aw88395_dai, ARRAY_SIZE(aw88395_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to register aw88395: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id aw88395_i2c_id[] = {
+ { AW88395_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id);
+
+static struct i2c_driver aw88395_i2c_driver = {
+ .driver = {
+ .name = AW88395_I2C_NAME,
+ },
+ .probe = aw88395_i2c_probe,
+ .id_table = aw88395_i2c_id,
+};
+module_i2c_driver(aw88395_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88395 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h
new file mode 100644
index 000000000000..c2a4f0cb8cd5
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395.h -- ALSA SoC AW88395 codec support
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_H__
+#define __AW88395_H__
+
+#define AW88395_CHIP_ID_REG (0x00)
+#define AW88395_START_RETRIES (5)
+#define AW88395_START_WORK_DELAY_MS (0)
+
+#define AW88395_DSP_16_DATA_MASK (0x0000ffff)
+
+#define AW88395_I2C_NAME "aw88395"
+
+#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88395_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+
+#define AW88395_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW88395_SYNC_START = 0,
+ AW88395_ASYNC_START,
+};
+
+enum {
+ AW88395_STREAM_CLOSE = 0,
+ AW88395_STREAM_OPEN,
+};
+
+struct aw88395 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_data_type.h b/sound/soc/codecs/aw88395/aw88395_data_type.h
new file mode 100644
index 000000000000..e7aa56178b36
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_data_type.h
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw883_data_type.h -- The data type of the AW88395 chip
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_DATA_TYPE_H__
+#define __AW88395_DATA_TYPE_H__
+
+#define PROJECT_NAME_MAX (24)
+#define CUSTOMER_NAME_MAX (16)
+#define CFG_VERSION_MAX (4)
+#define DEV_NAME_MAX (16)
+#define PROFILE_STR_MAX (32)
+
+#define ACF_FILE_ID (0xa15f908)
+
+enum aw_cfg_hdr_version {
+ AW88395_CFG_HDR_VER = 0x00000001,
+ AW88395_CFG_HDR_VER_V1 = 0x01000000,
+};
+
+enum aw_cfg_dde_type {
+ AW88395_DEV_NONE_TYPE_ID = 0xFFFFFFFF,
+ AW88395_DEV_TYPE_ID = 0x00000000,
+ AW88395_SKT_TYPE_ID = 0x00000001,
+ AW88395_DEV_DEFAULT_TYPE_ID = 0x00000002,
+};
+
+enum aw_sec_type {
+ ACF_SEC_TYPE_REG = 0,
+ ACF_SEC_TYPE_DSP,
+ ACF_SEC_TYPE_DSP_CFG,
+ ACF_SEC_TYPE_DSP_FW,
+ ACF_SEC_TYPE_HDR_REG,
+ ACF_SEC_TYPE_HDR_DSP_CFG,
+ ACF_SEC_TYPE_HDR_DSP_FW,
+ ACF_SEC_TYPE_MULTIPLE_BIN,
+ ACF_SEC_TYPE_SKT_PROJECT,
+ ACF_SEC_TYPE_DSP_PROJECT,
+ ACF_SEC_TYPE_MONITOR,
+ ACF_SEC_TYPE_MAX,
+};
+
+enum profile_data_type {
+ AW88395_DATA_TYPE_REG = 0,
+ AW88395_DATA_TYPE_DSP_CFG,
+ AW88395_DATA_TYPE_DSP_FW,
+ AW88395_DATA_TYPE_MAX,
+};
+
+enum aw_prof_type {
+ AW88395_PROFILE_MUSIC = 0,
+ AW88395_PROFILE_VOICE,
+ AW88395_PROFILE_VOIP,
+ AW88395_PROFILE_RINGTONE,
+ AW88395_PROFILE_RINGTONE_HS,
+ AW88395_PROFILE_LOWPOWER,
+ AW88395_PROFILE_BYPASS,
+ AW88395_PROFILE_MMI,
+ AW88395_PROFILE_FM,
+ AW88395_PROFILE_NOTIFICATION,
+ AW88395_PROFILE_RECEIVER,
+ AW88395_PROFILE_MAX,
+};
+
+enum aw_profile_status {
+ AW88395_PROFILE_WAIT = 0,
+ AW88395_PROFILE_OK,
+};
+
+struct aw_cfg_hdr {
+ u32 id;
+ char project[PROJECT_NAME_MAX];
+ char custom[CUSTOMER_NAME_MAX];
+ char version[CFG_VERSION_MAX];
+ u32 author_id;
+ u32 ddt_size;
+ u32 ddt_num;
+ u32 hdr_offset;
+ u32 hdr_version;
+ u32 reserved[3];
+};
+
+struct aw_cfg_dde {
+ u32 type;
+ char dev_name[DEV_NAME_MAX];
+ u16 dev_index;
+ u16 dev_bus;
+ u16 dev_addr;
+ u16 dev_profile;
+ u32 data_type;
+ u32 data_size;
+ u32 data_offset;
+ u32 data_crc;
+ u32 reserved[5];
+};
+
+struct aw_cfg_dde_v1 {
+ u32 type;
+ char dev_name[DEV_NAME_MAX];
+ u16 dev_index;
+ u16 dev_bus;
+ u16 dev_addr;
+ u16 dev_profile;
+ u32 data_type;
+ u32 data_size;
+ u32 data_offset;
+ u32 data_crc;
+ char dev_profile_str[PROFILE_STR_MAX];
+ u32 chip_id;
+ u32 reserved[4];
+};
+
+struct aw_sec_data_desc {
+ u32 len;
+ u8 *data;
+};
+
+struct aw_prof_desc {
+ u32 id;
+ u32 prof_st;
+ char *prf_str;
+ u32 fw_ver;
+ struct aw_sec_data_desc sec_desc[AW88395_DATA_TYPE_MAX];
+};
+
+struct aw_all_prof_info {
+ struct aw_prof_desc prof_desc[AW88395_PROFILE_MAX];
+};
+
+struct aw_prof_info {
+ int count;
+ int prof_type;
+ char **prof_name_list;
+ struct aw_prof_desc *prof_desc;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c
new file mode 100644
index 000000000000..79c3135a4110
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.c
@@ -0,0 +1,1721 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_device.c -- AW88395 function for ALSA Audio Driver
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+// Author: Ben Yi <yijiangtao@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include "aw88395_device.h"
+#include "aw88395_reg.h"
+
+static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)dsp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ u16 temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data & AW88395_DSP_16_DATA_MASK;
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data >> 16;
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type)
+{
+ u32 reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW_DSP_16_DATA:
+ ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, dsp_data);
+ break;
+ case AW_DSP_32_DATA:
+ ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state*/
+ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw_dev_dsp_write);
+
+static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data = temp_data;
+
+ return 0;
+}
+
+static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data = temp_data;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data |= (temp_data << 16);
+
+ return 0;
+}
+
+int aw_dev_dsp_read(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type)
+{
+ u32 reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW_DSP_16_DATA:
+ ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, *dsp_data);
+ break;
+ case AW_DSP_32_DATA:
+ ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, *dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state*/
+ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw_dev_dsp_read);
+
+static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id)
+{
+ int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_CHIP_ID_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+
+ dev_info(aw_dev->dev, "chip id = %x\n", reg_val);
+ *chip_id = reg_val;
+
+ return 0;
+}
+
+static unsigned int reg_val_to_db(unsigned int value)
+{
+ return (((value >> AW88395_VOL_6DB_START) * AW88395_VOLUME_STEP_DB) +
+ ((value & 0x3f) % AW88395_VOLUME_STEP_DB));
+}
+
+static unsigned short db_to_reg_val(unsigned short value)
+{
+ return (((value / AW88395_VOLUME_STEP_DB) << AW88395_VOL_6DB_START) +
+ (value % AW88395_VOLUME_STEP_DB));
+}
+
+static int aw_dev_dsp_fw_check(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_fw_desc;
+ struct aw_prof_desc *set_prof_desc;
+ u16 base_addr = AW88395_DSP_FW_ADDR;
+ u16 addr = base_addr;
+ u32 dsp_val;
+ u16 bin_val;
+ int ret, i;
+
+ ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ dsp_fw_desc = &set_prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW];
+
+ for (i = 0; i < AW88395_FW_CHECK_PART; i++) {
+ ret = aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp read failed");
+ return ret;
+ }
+
+ bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]);
+
+ if (dsp_val != bin_val) {
+ dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] != bindata[0x%x]",
+ addr, dsp_val, bin_val);
+ return -EINVAL;
+ }
+
+ addr += (dsp_fw_desc->len / 2) / AW88395_FW_CHECK_PART;
+ if ((addr - base_addr) > dsp_fw_desc->len) {
+ dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int reg_value;
+ u16 real_value, volume;
+ int ret;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88395_MUTE_VOL);
+ real_value = db_to_reg_val(volume);
+
+ /* cal real value */
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ /* [15 : 6] volume */
+ real_value = (real_value << AW88395_VOL_START_BIT) | (reg_value & AW88395_VOL_MASK);
+
+ /* write value */
+ ret = regmap_write(aw_dev->regmap, AW88395_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol)
+{
+ int ret;
+
+ ret = aw_dev_set_volume(aw_dev, set_vol);
+ if (ret)
+ dev_dbg(aw_dev->dev, "set volume failed");
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_set_volume);
+
+static void aw_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ u16 fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88395_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88395_MUTE_VOL; i += fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88395_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev,
+ unsigned int addr, unsigned int dsp_data, unsigned char data_type)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+ unsigned int addr_offset;
+ __le16 data1;
+ __le32 data2;
+
+ dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data);
+
+ addr_offset = (addr - AW88395_DSP_CFG_ADDR) * 2;
+ if (addr_offset > crc_dsp_cfg->len) {
+ dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]",
+ addr_offset, crc_dsp_cfg->len);
+ return -EINVAL;
+ }
+ switch (data_type) {
+ case AW_DSP_16_DATA:
+ data1 = cpu_to_le16((u16)dsp_data);
+ memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2);
+ break;
+ case AW_DSP_32_DATA:
+ data2 = cpu_to_le32(dsp_data);
+ memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev)
+{
+ u32 cali_re;
+ int ret;
+
+ cali_re = AW88395_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88395_DSP_RE_SHIFT);
+
+ /* set cali re to device */
+ ret = aw_dev_dsp_write(aw_dev,
+ AW88395_DSP_REG_CFG_ADPZ_RE, cali_re, AW_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RE,
+ cali_re, AW_DSP_32_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "modify dsp cfg failed");
+
+ return ret;
+}
+
+static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG,
+ ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG,
+ ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_DISABLE_VALUE);
+ }
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+ u32 crc_value, crc_data_len;
+
+ /* get crc data len */
+ crc_data_len = (AW88395_DSP_REG_CRC_ADDR - AW88395_DSP_CFG_ADDR) * 2;
+ if (crc_data_len > crc_dsp_cfg->len) {
+ dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d",
+ crc_data_len, crc_dsp_cfg->len);
+ return -EINVAL;
+ }
+
+ if (crc_data_len & 0x11) {
+ dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len);
+ return -EINVAL;
+ }
+
+ crc_value = crc32c(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF;
+
+ return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value,
+ AW_DSP_32_DATA);
+}
+
+static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG,
+ ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG,
+ ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_DISABLE_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_dsp_check_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+ int i;
+
+ for (i = 0; i < AW88395_DSP_ST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg0x%x failed", AW88395_SYSST_REG);
+ continue;
+ }
+
+ if ((reg_val & (~AW88395_DSPS_MASK)) != AW88395_DSPS_NORMAL_VALUE) {
+ dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val);
+ ret = -EPERM;
+ continue;
+ } else {
+ dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val);
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_DSPBY_MASK, AW88395_DSPBY_WORKING_VALUE);
+ if (ret)
+ dev_dbg(aw_dev->dev, "enable dsp failed");
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_DSPBY_MASK, AW88395_DSPBY_BYPASS_VALUE);
+ if (ret)
+ dev_dbg(aw_dev->dev, "disable dsp failed");
+ }
+}
+
+static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_BYPASS) {
+ dev_info(aw_dev->dev, "dsp bypass");
+ return 0;
+ }
+
+ ret = aw_dev_dsp_set_crc32(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "set dsp crc32 failed");
+ return ret;
+ }
+
+ aw_dev_dsp_check_crc_enable(aw_dev, true);
+
+ /* dsp enable */
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88395_5000_US, AW88395_5000_US + 100);
+
+ ret = aw_dev_dsp_check_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check crc32 fail");
+ } else {
+ aw_dev_dsp_check_crc_enable(aw_dev, false);
+ aw_dev->dsp_crc_st = AW88395_DSP_CRC_OK;
+ }
+
+ return ret;
+}
+
+static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ int ret;
+
+ if (pwd) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_PWDN_MASK, AW88395_PWDN_POWER_DOWN_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_PWDN_MASK, AW88395_PWDN_WORKING_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_AMPPD_MASK, AW88395_AMPPD_POWER_DOWN_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_AMPPD_MASK, AW88395_AMPPD_WORKING_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ int ret;
+
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_HMUTE_MASK, AW88395_HMUTE_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_HMUTE_MASK, AW88395_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_mute);
+
+static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+ unsigned int reg_val;
+ u16 reg_icalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalk = reg_val & (~AW88395_EF_ISN_GESLP_MASK);
+
+ if (reg_icalk & (~AW88395_EF_ISN_GESLP_SIGN_MASK))
+ reg_icalk = reg_icalk | AW88395_EF_ISN_GESLP_SIGN_NEG;
+
+ *icalk = (int16_t)reg_icalk;
+
+ return ret;
+}
+
+static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ unsigned int reg_val;
+ u16 reg_vcalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRH_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val = reg_val >> AW88395_EF_VSENSE_GAIN_SHIFT;
+
+ reg_vcalk = (u16)reg_val & (~AW88395_EF_VSN_GESLP_MASK);
+
+ if (reg_vcalk & (~AW88395_EF_VSN_GESLP_SIGN_MASK))
+ reg_vcalk = reg_vcalk | AW88395_EF_VSN_GESLP_SIGN_NEG;
+
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ unsigned int reg_val;
+ u16 reg_vcalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalk = reg_val >> AW88395_EF_DAC_GESLP_SHIFT;
+
+ if (reg_vcalk & AW88395_EF_DAC_GESLP_SIGN_MASK)
+ reg_vcalk = reg_vcalk | AW88395_EF_DAC_GESLP_SIGN_NEG;
+
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select)
+{
+ unsigned int vsense_reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_I2SCFG3_REG, &vsense_reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read vsense_reg_val failed");
+ return ret;
+ }
+ dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val);
+
+ if (vsense_reg_val & (~AW88395_VDSEL_MASK)) {
+ *vsense_select = AW88395_DEV_VDSEL_VSENSE;
+ dev_dbg(aw_dev->dev, "vsense outside");
+ } else {
+ *vsense_select = AW88395_DEV_VDSEL_DAC;
+ dev_dbg(aw_dev->dev, "vsense inside");
+ }
+
+ return 0;
+}
+
+static int aw_dev_set_vcalb(struct aw_device *aw_dev)
+{
+ int16_t icalk_val, vcalk_val;
+ int icalk, vsense_select;
+ u32 vcalb_adj, reg_val;
+ int vcalb, vcalk;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VCALB, &vcalb_adj, AW_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read vcalb_adj failed");
+ return ret;
+ }
+
+ ret = aw_dev_vsense_select(aw_dev, &vsense_select);
+ if (ret)
+ return ret;
+ dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select);
+
+ ret = aw_dev_get_icalk(aw_dev, &icalk_val);
+ if (ret)
+ return ret;
+ icalk = AW88395_CABL_BASE_VALUE + AW88395_ICABLK_FACTOR * icalk_val;
+
+ switch (vsense_select) {
+ case AW88395_DEV_VDSEL_VSENSE:
+ ret = aw_dev_get_vcalk(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+ vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR * vcalk_val;
+ vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR /
+ AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj;
+
+ dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d",
+ AW88395_VCABLK_FACTOR, AW88395_VSCAL_FACTOR, icalk, vcalk);
+ break;
+ case AW88395_DEV_VDSEL_DAC:
+ ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+ vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR_DAC * vcalk_val;
+ vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR_DAC /
+ AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj;
+
+ dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d",
+ AW88395_VCABLK_FACTOR_DAC,
+ AW88395_VSCAL_FACTOR_DAC, icalk, vcalk);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported vsense status");
+ return -EINVAL;
+ }
+
+ if ((vcalk == 0) || (AW88395_ISCAL_FACTOR == 0)) {
+ dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported",
+ vcalk, AW88395_ISCAL_FACTOR);
+ return -EINVAL;
+ }
+
+ vcalb = vcalb >> AW88395_VCALB_ADJ_FACTOR;
+ reg_val = (u32)vcalb;
+
+ dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x",
+ vcalb, reg_val, vcalb_adj);
+
+ ret = aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_VCALB, reg_val, AW_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "write vcalb failed");
+ return ret;
+ }
+
+ ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_VCALB,
+ (u32)reg_val, AW_DSP_16_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "modify dsp cfg failed");
+
+ return ret;
+}
+
+static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev)
+{
+ struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc;
+ u32 cali_delay;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev,
+ AW88395_DSP_CALI_F0_DELAY, &cali_delay, AW_DSP_16_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret);
+ else
+ desc->delay = AW88395_CALI_DELAY_CACL(cali_delay);
+
+ dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay);
+
+ return ret;
+}
+
+static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSINT_REG, &reg_val);
+ if (ret)
+ dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret);
+ else
+ *int_status = reg_val;
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status);
+}
+
+static void aw_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ u16 int_status;
+
+ /* read int status and clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ /* make sure int status is clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ if (int_status)
+ dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status);
+}
+
+static int aw_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret)
+ return -EIO;
+ if ((reg_val & AW88395_BIT_PLL_CHECK) != AW88395_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88395_CCO_MUX_MASK);
+ if (reg_val == AW88395_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG,
+ ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG,
+ ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = reg_val & (~AW88395_BIT_SYSST_CHECK_MASK)
+ & AW88395_BIT_SYSST_CHECK;
+ if (check_val != AW88395_BIT_SYSST_CHECK) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88395_BIT_SYSST_CHECK);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+
+ if (reg_val & AW88395_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev)
+{
+ struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if ((reg_val & (~AW88395_RCV_MODE_MASK)) == AW88395_RCV_MODE_RECEIVER_VALUE)
+ profctrl_desc->cur_mode = AW88395_RCV_MODE;
+ else
+ profctrl_desc->cur_mode = AW88395_NOT_RCV_MODE;
+}
+
+static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg)
+{
+ unsigned int reg_val = 0;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if (reg_val & (~AW88395_DSPBY_MASK))
+ *dsp_cfg = AW88395_DEV_DSP_BYPASS;
+ else
+ *dsp_cfg = AW88395_DEV_DSP_WORK;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88395_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG,
+ ~AW88395_MEM_CLKSEL_MASK,
+ AW88395_MEM_CLKSEL_DAP_HCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed");
+ break;
+ case AW88395_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG,
+ ~AW88395_MEM_CLKSEL_MASK,
+ AW88395_MEM_CLKSEL_OSC_CLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag);
+ break;
+ }
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88395_WDT_CNT_MASK)))
+ ret = -EPERM;
+
+ return ret;
+}
+
+static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax)
+{
+ return aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VMAX, vmax, AW_DSP_16_DATA);
+}
+
+static int aw_dev_update_reg_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int read_val;
+ int16_t *reg_data;
+ int data_len;
+ u16 read_vol;
+ u16 reg_val;
+ u8 reg_addr;
+ int i, ret;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88395_SYSCTRL_REG) {
+ ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+ if (ret)
+ break;
+ read_val &= (~AW88395_HMUTE_MASK);
+ reg_val &= AW88395_HMUTE_MASK;
+ reg_val |= read_val;
+ }
+ if (reg_addr == AW88395_HAGCCFG7_REG)
+ reg_val &= AW88395_AGC_DSP_CTL_MASK;
+
+ if (reg_addr == AW88395_I2SCFG1_REG) {
+ /* close tx */
+ reg_val &= AW88395_I2STXEN_MASK;
+ reg_val |= AW88395_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88395_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88395_VOL_MASK)) >>
+ AW88395_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume =
+ reg_val_to_db(read_vol);
+ }
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ break;
+
+ }
+
+ aw_dev_get_cur_mode_st(aw_dev);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ /* clear control volume when PA change profile */
+ vol_desc->ctl_volume = 0;
+ } else {
+ /* keep control volume when PA start with sync mode */
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+ }
+
+ aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg);
+
+ return ret;
+}
+
+static int aw_dev_reg_update(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw_dev, data, len);
+ if (ret) {
+ dev_err(aw_dev->dev, "reg update failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_get_ra(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ u32 dsp_ra;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error");
+ return ret;
+ }
+
+ cali_desc->ra = AW88395_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88395_DSP_RE_SHIFT);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ int i, ret;
+
+#ifdef AW88395_DSP_I2C_WRITES
+ u32 tmp_len;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+#else
+ __be16 reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* i2c write */
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+ for (i = 0; i < len; i += 2) {
+ reg_val = cpu_to_be16p((u16 *)(data + i));
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
+ (u16)reg_val);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+#endif
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+
+ dev_dbg(aw_dev->dev, "dsp firmware len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp firmware data is null or len is 0");
+ return -EINVAL;
+ }
+ aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_FW_ADDR);
+ aw_dev->dsp_fw_len = len;
+
+ return 0;
+}
+
+static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int size)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+
+ if (!crc_dsp_cfg->data) {
+ crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL);
+ if (!crc_dsp_cfg->data)
+ return -ENOMEM;
+ crc_dsp_cfg->len = size;
+ } else if (crc_dsp_cfg->len < size) {
+ devm_kfree(aw_dev->dev, crc_dsp_cfg->data);
+ crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL);
+ if (!crc_dsp_cfg->data)
+ return -ENOMEM;
+ crc_dsp_cfg->len = size;
+ }
+ memcpy(crc_dsp_cfg->data, data, size);
+ swab16_array((u16 *)crc_dsp_cfg->data, size >> 1);
+
+ return 0;
+}
+
+static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp config len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp config data is null or len is 0");
+ return -EINVAL;
+ }
+
+ aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_CFG_ADDR);
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_set_vcalb(aw_dev);
+ if (ret)
+ return ret;
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+ if (ret)
+ return ret;
+ ret = aw_dev_get_cali_f0_delay(aw_dev);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax);
+ if (ret) {
+ dev_err(aw_dev->dev, "get vmax failed");
+ return ret;
+ }
+ dev_dbg(aw_dev->dev, "get init vmax:0x%x", aw_dev->vmax_desc.init_vmax);
+ aw_dev->dsp_crc_st = AW88395_DSP_CRC_NA;
+
+ return 0;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* check the odd bits of reg 0x40 */
+ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST);
+ regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, &reg_val);
+ if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check the even bits of reg 0x40 */
+ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, &reg_val);
+ if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check dsp_fw_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_FW_ADDR, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_FW_ADDR, &reg_val);
+ if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_CFG_ADDR, AW88395_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ if ((aw_dev->prof_cur == aw_dev->prof_index) &&
+ (force_up_en == AW88395_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88395_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw88395_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC);
+
+ if (up_dsp_fw_en) {
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed");
+ goto error;
+ }
+
+ /* update dsp firmware */
+ dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver);
+ ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_FW].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ goto error;
+ }
+ }
+
+ /* update dsp config */
+ ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_CFG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ goto error;
+ }
+
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_fw_update);
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88395_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88395_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ for (i = 0; i < AW88395_DEV_DSP_CHECK_MAX; i++) {
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp wdt status error=%d", ret);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re < AW88395_CALI_RE_MAX) &&
+ (aw_dev->cali_desc.cali_re > AW88395_CALI_RE_MIN)) {
+
+ ret = aw_dev_dsp_set_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re failed");
+ }
+}
+
+int aw88395_dev_start(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW88395_DEV_PW_ON) {
+ dev_info(aw_dev->dev, "already power on");
+ return 0;
+ }
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) {
+ /* dsp bypass */
+ aw_dev_dsp_enable(aw_dev, false);
+ ret = aw_dev_dsp_fw_check(aw_dev);
+ if (ret)
+ goto dev_dsp_fw_check_fail;
+
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+
+ if (aw_dev->dsp_crc_st != AW88395_DSP_CRC_OK) {
+ ret = aw_dev_dsp_check_crc32(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed");
+ goto crc_check_fail;
+ }
+ }
+
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed");
+ goto dsp_check_fail;
+ }
+ } else {
+ dev_dbg(aw_dev->dev, "start pa with dsp bypass");
+ }
+
+ /* enable tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, true);
+
+ /* close mute */
+ aw88395_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88395_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+dev_dsp_fw_check_fail:
+sysst_check_fail:
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88395_DEV_PW_OFF;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_start);
+
+int aw88395_dev_stop(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_cfg =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG];
+ struct aw_sec_data_desc *dsp_fw =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW];
+ int int_st = 0;
+ int ret;
+
+ if (aw_dev->status == AW88395_DEV_PW_OFF) {
+ dev_info(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88395_DEV_PW_OFF;
+
+ /* set mute */
+ aw88395_dev_mute(aw_dev, true);
+ usleep_range(AW88395_4000_US, AW88395_4000_US + 100);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 100);
+
+ /* check sysint state */
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st < 0) {
+ /* system status anomaly */
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC);
+ ret = aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ if (ret)
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ ret = aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ if (ret)
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+ }
+
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_stop);
+
+int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ int ret;
+
+ if ((!aw_dev) || (!aw_cfg)) {
+ pr_err("aw_dev is NULL or aw_cfg is NULL");
+ return -ENOMEM;
+ }
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88395_1000_US / 10;
+ aw_dev->fade_out_time = AW88395_1000_US >> 1;
+ aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id;
+ aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id;
+
+ ret = aw88395_dev_fw_update(aw_dev, AW88395_FORCE_UPDATE_ON, AW88395_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ /* set mute */
+ aw88395_dev_mute(aw_dev, true);
+ usleep_range(AW88395_4000_US, AW88395_4000_US + 100);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 100);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_init);
+
+static void aw88395_parse_channel_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+ int ret;
+
+ ret = of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read audio-channel failed,use default 0");
+ aw_dev->channel = AW88395_DEV_DEFAULT_CH;
+ return;
+ }
+
+ dev_dbg(aw_dev->dev, "read audio-channel value is: %d",
+ channel_value);
+ aw_dev->channel = channel_value;
+}
+
+static int aw_dev_init(struct aw_device *aw_dev)
+{
+ aw_dev->chip_id = AW88395_CHIP_ID;
+ /* call aw device init func */
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = 0;
+ aw_dev->fw_status = AW88395_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88395_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE;
+ aw88395_parse_channel_dt(aw_dev);
+
+ return 0;
+}
+
+int aw88395_dev_get_profile_count(struct aw_device *aw_dev)
+{
+ return aw_dev->prof_info.count;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_count);
+
+int aw88395_dev_get_profile_index(struct aw_device *aw_dev)
+{
+ return aw_dev->prof_index;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_index);
+
+int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EINVAL;
+
+ aw_dev->prof_index = index;
+ dev_dbg(aw_dev->dev, "set prof[%s]",
+ aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index);
+
+int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name);
+
+int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_data);
+
+int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap)
+{
+ u16 chip_id;
+ int ret;
+
+ if (*aw_dev) {
+ dev_info(&i2c->dev, "it should be initialized here.\n");
+ } else {
+ *aw_dev = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL);
+ if (!(*aw_dev))
+ return -ENOMEM;
+ }
+
+ (*aw_dev)->i2c = i2c;
+ (*aw_dev)->dev = &i2c->dev;
+ (*aw_dev)->regmap = regmap;
+ mutex_init(&(*aw_dev)->dsp_lock);
+
+ /* read chip id */
+ ret = aw_dev_read_chipid((*aw_dev), &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case AW88395_CHIP_ID:
+ ret = aw_dev_init((*aw_dev));
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err((*aw_dev)->dev, "unsupported device");
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_init);
+
+MODULE_DESCRIPTION("AW88395 device lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h
new file mode 100644
index 000000000000..3626f222899d
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_device.h -- AW88395 function for ALSA Audio Driver
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_DEVICE_FILE_H__
+#define __AW88395_DEVICE_FILE_H__
+
+#include "aw88395.h"
+#include "aw88395_data_type.h"
+#include "aw88395_lib.h"
+
+#define AW88395_DEV_DEFAULT_CH (0)
+#define AW88395_DEV_DSP_CHECK_MAX (5)
+#define AW88395_DSP_I2C_WRITES
+#define AW88395_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW88395_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88395_DSP_EVEN_NUM_BIT_TEST (0xAAAA)
+#define AW88395_DSP_ST_CHECK_MAX (2)
+#define AW88395_FADE_IN_OUT_DEFAULT (0)
+#define AW88395_CALI_RE_MAX (15000)
+#define AW88395_CALI_RE_MIN (4000)
+#define AW88395_CALI_DELAY_CACL(value) ((value * 32) / 48)
+
+#define AW88395_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88395_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+
+#define AW88395_ACF_FILE "aw88395_acf.bin"
+#define AW88395_DEV_SYSST_CHECK_MAX (10)
+
+enum {
+ AW88395_DEV_VDSEL_DAC = 0,
+ AW88395_DEV_VDSEL_VSENSE = 1,
+};
+
+enum {
+ AW88395_DSP_CRC_NA = 0,
+ AW88395_DSP_CRC_OK = 1,
+};
+
+enum {
+ AW88395_DSP_FW_UPDATE_OFF = 0,
+ AW88395_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88395_FORCE_UPDATE_OFF = 0,
+ AW88395_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88395_1000_US = 1000,
+ AW88395_2000_US = 2000,
+ AW88395_3000_US = 3000,
+ AW88395_4000_US = 4000,
+ AW88395_5000_US = 5000,
+ AW88395_10000_US = 10000,
+ AW88395_100000_US = 100000,
+};
+
+enum {
+ AW88395_DEV_TYPE_OK = 0,
+ AW88395_DEV_TYPE_NONE = 1,
+};
+
+
+enum AW88395_DEV_STATUS {
+ AW88395_DEV_PW_OFF = 0,
+ AW88395_DEV_PW_ON,
+};
+
+enum AW88395_DEV_FW_STATUS {
+ AW88395_DEV_FW_FAILED = 0,
+ AW88395_DEV_FW_OK,
+};
+
+enum AW88395_DEV_MEMCLK {
+ AW88395_DEV_MEMCLK_OSC = 0,
+ AW88395_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88395_DEV_DSP_CFG {
+ AW88395_DEV_DSP_WORK = 0,
+ AW88395_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW_DSP_16_DATA = 0,
+ AW_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88395_NOT_RCV_MODE = 0,
+ AW88395_RCV_MODE = 1,
+};
+
+struct aw_profctrl_desc {
+ unsigned int cur_mode;
+};
+
+enum {
+ CALI_RESULT_NORMAL,
+ CALI_RESULT_ERROR,
+};
+
+struct aw_volume_desc {
+ unsigned int init_volume;
+ unsigned int mute_volume;
+ unsigned int ctl_volume;
+ unsigned int max_volume;
+};
+
+struct aw_dsp_mem_desc {
+ unsigned int dsp_madd_reg;
+ unsigned int dsp_mdat_reg;
+ unsigned int dsp_fw_base_addr;
+ unsigned int dsp_cfg_base_addr;
+};
+
+struct aw_vmax_desc {
+ unsigned int init_vmax;
+};
+
+struct aw_cali_delay_desc {
+ unsigned int delay;
+};
+
+#define AW_CALI_CFG_NUM (4)
+struct cali_cfg {
+ uint32_t data[AW_CALI_CFG_NUM];
+};
+
+struct aw_cali_backup_desc {
+ unsigned int dsp_ng_cfg;
+ unsigned int dsp_lp_cfg;
+};
+
+struct aw_cali_desc {
+ u32 cali_re;
+ u32 ra;
+ bool cali_switch;
+ bool cali_running;
+ uint16_t cali_result;
+ uint16_t store_vol;
+ struct cali_cfg cali_cfg;
+ struct aw_cali_backup_desc backup_info;
+};
+
+struct aw_container {
+ int len;
+ u8 data[];
+};
+
+struct aw_device {
+ int status;
+ struct mutex dsp_lock;
+
+ unsigned char prof_cur;
+ unsigned char prof_index;
+ unsigned char dsp_crc_st;
+ unsigned char dsp_cfg;
+ u16 chip_id;
+
+ unsigned int channel;
+ unsigned int fade_step;
+ unsigned int prof_data_type;
+
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ char *acf;
+
+ u32 dsp_fw_len;
+ u32 dsp_cfg_len;
+ u8 platform;
+ u8 fw_status;
+
+ unsigned int fade_in_time;
+ unsigned int fade_out_time;
+
+ struct aw_prof_info prof_info;
+ struct aw_sec_data_desc crc_dsp_cfg;
+ struct aw_profctrl_desc profctrl_desc;
+ struct aw_volume_desc volume_desc;
+ struct aw_dsp_mem_desc dsp_mem_desc;
+ struct aw_vmax_desc vmax_desc;
+
+ struct aw_cali_delay_desc cali_delay_desc;
+ struct aw_cali_desc cali_desc;
+
+};
+
+int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap);
+int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88395_dev_start(struct aw_device *aw_dev);
+int aw88395_dev_stop(struct aw_device *aw_dev);
+int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en);
+
+void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol);
+int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc);
+int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name);
+int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index);
+int aw88395_dev_get_profile_index(struct aw_device *aw_dev);
+int aw88395_dev_get_profile_count(struct aw_device *aw_dev);
+int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute);
+int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type);
+int aw_dev_dsp_read(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type);
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
new file mode 100644
index 000000000000..ceb7fc43d018
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -0,0 +1,1174 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_lib.c -- ACF bin parsing and check library file for aw88395
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include "aw88395_lib.h"
+#include "aw88395_device.h"
+
+#define AW88395_CRC8_POLYNOMIAL 0x8C
+DECLARE_CRC8_TABLE(aw_crc8_table);
+
+static char *profile_name[AW88395_PROFILE_MAX] = {
+ "Music", "Voice", "Voip", "Ringtone",
+ "Ringtone_hs", "Lowpower", "Bypass",
+ "Mmi", "Fm", "Notification", "Receiver"
+};
+
+static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin);
+
+static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ unsigned char *p_check_sum;
+ unsigned int sum_data = 0;
+ unsigned int check_sum;
+ unsigned int i, len;
+
+ p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr -
+ bin->header_info[bin_num].header_len)]);
+ len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len;
+ check_sum = le32_to_cpup((void *)p_check_sum);
+
+ for (i = 4; i < len; i++)
+ sum_data += *(p_check_sum + i);
+
+ dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x",
+ __func__, p_check_sum, check_sum, sum_data);
+ if (sum_data != check_sum) {
+ dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x",
+ __func__, bin_num, check_sum, sum_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 ||
+ bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) {
+ dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_register_num, parse_register_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ parse_register_num = le32_to_cpup((void *)p_check_sum);
+ check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) /
+ (bin->header_info[bin_num].reg_byte_len +
+ bin->header_info[bin_num].data_byte_len);
+ dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n",
+ __func__, parse_register_num, check_register_num);
+ if (parse_register_num != check_register_num) {
+ dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n",
+ __func__, parse_register_num, check_register_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].reg_num = parse_register_num;
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_dsp_reg_num, parse_dsp_reg_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM));
+ bin->header_info[bin_num].reg_data_byte_len =
+ le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN));
+ check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) /
+ bin->header_info[bin_num].reg_data_byte_len;
+ dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+ __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num);
+ if (parse_dsp_reg_num != check_dsp_reg_num) {
+ dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n");
+ dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+ __func__, check_dsp_reg_num, check_dsp_reg_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum);
+ bin->header_info[bin_num].reg_num = parse_dsp_reg_num;
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr +
+ DSP_VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_soc_app_num, parse_soc_app_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum);
+ parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM));
+ check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM;
+ dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+ __func__, bin_num, parse_soc_app_num, check_soc_app_num);
+ if (parse_soc_app_num != check_soc_app_num) {
+ dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+ __func__, parse_soc_app_num, check_soc_app_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].reg_num = parse_soc_app_num;
+ bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum +
+ APP_DOWNLOAD_ADDR));
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr +
+ APP_VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static void aw_get_single_bin_header(struct aw_bin *bin)
+{
+ memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN);
+
+ bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN;
+ bin->all_bin_parse_num += 1;
+}
+
+static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num,
+ int bin_serial_num, struct aw_bin *bin)
+{
+ struct bin_header_info aw_bin_header_info;
+ unsigned int bin_start_addr;
+ unsigned int valid_data_len;
+
+ if (bin->info.len < sizeof(struct bin_header_info)) {
+ dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n",
+ (int)sizeof(struct bin_header_info), bin->info.len);
+ return -EINVAL;
+ }
+
+ aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1];
+ if (!bin_serial_num) {
+ bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET));
+ bin->p_addr += (HEADER_LEN + bin_start_addr);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num +
+ VALID_DATA_ADDR_OFFSET;
+ } else {
+ valid_data_len = aw_bin_header_info.bin_data_len;
+ bin->p_addr += (HDADER_LEN + valid_data_len);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len +
+ VALID_DATA_ADDR_OFFSET;
+ }
+
+ return aw_parse_bin_header(aw_dev, bin);
+}
+
+static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int bin_num, i;
+ int ret;
+
+ bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET));
+ if (bin->multi_bin_parse_num == 1)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ VALID_DATA_ADDR_OFFSET;
+
+ aw_get_single_bin_header(bin);
+
+ for (i = 0; i < bin_num; i++) {
+ dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i);
+ ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int bin_data_type;
+
+ if (bin->info.len < sizeof(struct bin_header_info)) {
+ dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n",
+ (int)sizeof(struct bin_header_info), bin->info.len);
+ return -EINVAL;
+ }
+
+ bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET));
+ dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type);
+ switch (bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ case DATA_TYPE_DSP_REG:
+ case DATA_TYPE_SOC_APP:
+ bin->single_bin_parse_num += 1;
+ dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__,
+ bin->single_bin_parse_num);
+ if (!bin->multi_bin_parse_num)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ VALID_DATA_ADDR_OFFSET;
+ aw_get_single_bin_header(bin);
+ return 0;
+ case DATA_TYPE_MULTI_BINS:
+ bin->multi_bin_parse_num += 1;
+ dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__,
+ bin->multi_bin_parse_num);
+ return aw_get_multi_bin_header(aw_dev, bin);
+ default:
+ dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__);
+ return 0;
+ }
+}
+
+static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int header_version;
+
+ header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET));
+ dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version);
+
+ switch (header_version) {
+ case HEADER_VERSION_V1:
+ return aw_parse_bin_header(aw_dev, bin);
+ default:
+ dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n");
+ return -EINVAL;
+ }
+}
+
+static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ int ret = -EINVAL;
+ int i;
+
+ if (!bin) {
+ dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n");
+ return ret;
+ }
+ bin->p_addr = bin->info.data;
+ bin->all_bin_parse_num = 0;
+ bin->multi_bin_parse_num = 0;
+ bin->single_bin_parse_num = 0;
+
+ ret = aw_check_bin_header_version(aw_dev, bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n");
+ return ret;
+ }
+
+ for (i = 0; i < bin->all_bin_parse_num; i++) {
+ ret = aw_check_sum(aw_dev, bin, i);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n");
+ return ret;
+ }
+ ret = aw_check_data_version(aw_dev, bin, i);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check data version error\n");
+ return ret;
+ }
+ if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) {
+ switch (bin->header_info[i].bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ ret = aw_check_register_num(aw_dev, bin, i);
+ break;
+ case DATA_TYPE_DSP_REG:
+ ret = aw_check_dsp_reg_num(aw_dev, bin, i);
+ break;
+ case DATA_TYPE_SOC_APP:
+ ret = aw_check_soc_app_num(aw_dev, bin, i);
+ break;
+ default:
+ bin->header_info[i].valid_data_len =
+ bin->header_info[i].bin_data_len;
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ if (data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)data, data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ if (data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)data, data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data,
+ unsigned int data_len, struct aw_prof_desc *prof_desc)
+{
+ int ret;
+ int i;
+
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(struct aw_bin),
+ GFP_KERNEL);
+ if (!aw_bin)
+ return -ENOMEM;
+
+ aw_bin->info.len = data_len;
+ memcpy(aw_bin->info.data, data, data_len);
+
+ ret = aw_parsing_bin_file(aw_dev, aw_bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse bin failed");
+ return ret;
+ }
+
+ for (i = 0; i < aw_bin->all_bin_parse_num; i++) {
+ switch (aw_bin->header_info[i].bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ case DATA_TYPE_DSP_REG:
+ if (aw_bin->header_info[i].valid_data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
+ aw_bin->header_info[i].valid_data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ case DATA_TYPE_DSP_FW:
+ case DATA_TYPE_SOC_APP:
+ if (aw_bin->header_info[i].valid_data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
+ aw_bin->header_info[i].valid_data_len >> 1);
+
+ prof_desc->fw_ver = aw_bin->header_info[i].app_version;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ default:
+ dev_dbg(aw_dev->dev, "bin_data_type not found");
+ break;
+ }
+ }
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
+ uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc)
+{
+ int ret;
+
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(*aw_bin),
+ GFP_KERNEL);
+ if (!aw_bin)
+ return -ENOMEM;
+
+ aw_bin->info.len = data_len;
+ memcpy(aw_bin->info.data, data, data_len);
+
+ ret = aw_parsing_bin_file(aw_dev, aw_bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse bin failed");
+ return ret;
+ }
+
+ if ((aw_bin->all_bin_parse_num != 1) ||
+ (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) {
+ dev_err(aw_dev->dev, "bin num or type error");
+ return -EINVAL;
+ }
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
+ data + aw_bin->header_info[0].valid_data_addr;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
+ aw_bin->header_info[0].valid_data_len;
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr,
+ struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc)
+{
+ switch (cfg_dde->data_type) {
+ case ACF_SEC_TYPE_REG:
+ return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_DSP_CFG:
+ return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_DSP_FW:
+ return aw_dev_parse_raw_dsp_fw(
+ (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ return aw_dev_prof_parse_multi_bin(
+ aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_HDR_REG:
+ return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ default:
+ dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_dev_type(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_cfg_dde *cfg_dde =
+ (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int sec_num = 0;
+ int ret, i;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+ (cfg_dde[i].type == AW88395_DEV_TYPE_ID) &&
+ (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+ if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) {
+ dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+ cfg_dde[i].dev_profile);
+ return -EINVAL;
+ }
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
+ ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+ &all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ sec_num++;
+ }
+ }
+
+ if (sec_num == 0) {
+ dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num);
+ return AW88395_DEV_TYPE_NONE;
+ }
+
+ return AW88395_DEV_TYPE_OK;
+}
+
+static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_cfg_dde *cfg_dde =
+ (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int sec_num = 0;
+ int ret, i;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+ (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) &&
+ (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+ if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) {
+ dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+ cfg_dde[i].dev_profile);
+ return -EINVAL;
+ }
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
+ ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+ &all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ sec_num++;
+ }
+ }
+
+ if (sec_num == 0) {
+ dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_cfg_get_reg_valid_prof(struct aw_device *aw_dev,
+ struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int num = 0;
+ int i;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK)
+ prof_info->count++;
+ }
+
+ dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count);
+
+ if (!prof_info->count) {
+ dev_err(aw_dev->dev, "no profile data");
+ return -EPERM;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ if (num >= prof_info->count) {
+ dev_err(aw_dev->dev, "overflow count[%d]",
+ prof_info->count);
+ return -EINVAL;
+ }
+ prof_info->prof_desc[num] = prof_desc[i];
+ prof_info->prof_desc[num].id = i;
+ num++;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_cfg_get_multiple_valid_prof(struct aw_device *aw_dev,
+ struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_sec_data_desc *sec_desc;
+ int num = 0;
+ int i;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ sec_desc = prof_desc[i].sec_desc;
+ if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_REG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0))
+ prof_info->count++;
+ }
+ }
+
+ dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count);
+
+ if (!prof_info->count) {
+ dev_err(aw_dev->dev, "no profile data");
+ return -EPERM;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ sec_desc = prof_desc[i].sec_desc;
+ if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_REG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) {
+ if (num >= prof_info->count) {
+ dev_err(aw_dev->dev, "overflow count[%d]",
+ prof_info->count);
+ return -EINVAL;
+ }
+ prof_info->prof_desc[num] = prof_desc[i];
+ prof_info->prof_desc[num].id = i;
+ num++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ int ret;
+
+ struct aw_all_prof_info *all_prof_info __free(kfree) = kzalloc(sizeof(*all_prof_info),
+ GFP_KERNEL);
+ if (!all_prof_info)
+ return -ENOMEM;
+
+ ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info);
+ if (ret < 0) {
+ return ret;
+ } else if (ret == AW88395_DEV_TYPE_NONE) {
+ dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev");
+ ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (aw_dev->prof_data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_cfg_get_multiple_valid_prof(aw_dev, all_prof_info);
+ break;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_cfg_get_reg_valid_prof(aw_dev, all_prof_info);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported data type\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret)
+ aw_dev->prof_info.prof_name_list = profile_name;
+
+ return ret;
+}
+
+static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc = prof_info->prof_desc;
+ int i;
+
+ if (!prof_desc) {
+ dev_err(aw_dev->dev, "prof_desc is NULL");
+ return -EINVAL;
+ }
+
+ prof_info->prof_name_list = devm_kzalloc(aw_dev->dev,
+ prof_info->count * PROFILE_STR_MAX,
+ GFP_KERNEL);
+ if (!prof_info->prof_name_list)
+ return -ENOMEM;
+
+ for (i = 0; i < prof_info->count; i++) {
+ prof_desc[i].id = i;
+ prof_info->prof_name_list[i] = prof_desc[i].prf_str;
+ dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]);
+ }
+
+ return 0;
+}
+
+static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ int default_num = 0;
+ int dev_num = 0;
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ if (cfg_dde[i].type == AW88395_DEV_TYPE_ID)
+ dev_num++;
+
+ if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID)
+ default_num++;
+ }
+
+ if (dev_num != 0) {
+ aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID;
+ } else if (default_num != 0) {
+ aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID;
+ } else {
+ dev_err(aw_dev->dev, "can't find scene");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg,
+ unsigned int *scene_num)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+ (*scene_num)++;
+ }
+
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg,
+ unsigned int *scene_num)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ unsigned int i;
+
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
+ }
+
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg,
+ unsigned int *count)
+{
+ int ret;
+
+ ret = aw_get_dde_type_info(aw_dev, aw_cfg);
+ if (ret < 0)
+ return ret;
+
+ switch (aw_dev->prof_info.prof_type) {
+ case AW88395_DEV_TYPE_ID:
+ ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count);
+ break;
+ case AW88395_DEV_DEFAULT_TYPE_ID:
+ ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr,
+ struct aw_cfg_dde_v1 *cfg_dde,
+ int *cur_scene_id)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int ret;
+
+ switch (cfg_dde->data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse multi bin failed");
+ return ret;
+ }
+ prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+ prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+ (*cur_scene_id)++;
+ break;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_parse_reg_bin_with_hdr(aw_dev,
+ (uint8_t *)prof_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse reg bin with hdr failed");
+ return ret;
+ }
+ prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+ prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+ (*cur_scene_id)++;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int cur_scene_id = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+ ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr,
+ &cfg_dde[i], &cur_scene_id);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ }
+ }
+
+ if (cur_scene_id == 0) {
+ dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int cur_scene_id = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+ ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr,
+ &cfg_dde[i], &cur_scene_id);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ }
+ }
+
+ if (cur_scene_id == 0) {
+ dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *cfg_hdr)
+{
+ int ret;
+
+ switch (aw_dev->prof_info.prof_type) {
+ case AW88395_DEV_TYPE_ID:
+ ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr);
+ break;
+ case AW88395_DEV_DEFAULT_TYPE_ID:
+ ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr);
+ break;
+ default:
+ dev_err(aw_dev->dev, "prof type matched failed, get num[%d]",
+ aw_dev->prof_info.prof_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int ret;
+
+ ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "get scene count failed");
+ return ret;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse hdr failed");
+ return ret;
+ }
+
+ ret = aw_dev_create_prof_name_list_v1(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "create prof name list failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr;
+ int ret;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+ switch (cfg_hdr->hdr_version) {
+ case AW88395_CFG_HDR_VER:
+ ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+ cfg_hdr->hdr_version);
+ return ret;
+ }
+ break;
+ case AW88395_CFG_HDR_VER_V1:
+ ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+ cfg_hdr->hdr_version);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version);
+ return -EINVAL;
+ }
+ aw_dev->fw_status = AW88395_DEV_FW_OK;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load);
+
+static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ unsigned int end_data_offset;
+ struct aw_cfg_hdr *cfg_hdr;
+ struct aw_cfg_dde *cfg_dde;
+ unsigned int act_data = 0;
+ unsigned int hdr_ddt_len;
+ unsigned int i;
+ u8 act_crc8;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ /* check file type id is awinic acf file */
+ if (cfg_hdr->id != ACF_FILE_ID) {
+ dev_err(aw_dev->dev, "not acf type file");
+ return -EINVAL;
+ }
+
+ hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+ if (hdr_ddt_len > aw_cfg->len) {
+ dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]",
+ cfg_hdr->hdr_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* check data size */
+ cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+ act_data += hdr_ddt_len;
+ for (i = 0; i < cfg_hdr->ddt_num; i++)
+ act_data += cfg_dde[i].data_size;
+
+ if (act_data != aw_cfg->len) {
+ dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!",
+ act_data, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ /* data check */
+ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+ if (end_data_offset > aw_cfg->len) {
+ dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]",
+ i, end_data_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* crc check */
+ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+ cfg_dde[i].data_size, 0);
+ if (act_crc8 != cfg_dde[i].data_crc) {
+ dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x",
+ i, (u32)act_crc8, cfg_dde[i].data_crc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_dde_v1 *cfg_dde;
+ unsigned int end_data_offset;
+ struct aw_cfg_hdr *cfg_hdr;
+ unsigned int act_data = 0;
+ unsigned int hdr_ddt_len;
+ u8 act_crc8;
+ int i;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+ /* check file type id is awinic acf file */
+ if (cfg_hdr->id != ACF_FILE_ID) {
+ dev_err(aw_dev->dev, "not acf type file");
+ return -EINVAL;
+ }
+
+ hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+ if (hdr_ddt_len > aw_cfg->len) {
+ dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]",
+ cfg_hdr->hdr_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* check data size */
+ cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+ act_data += hdr_ddt_len;
+ for (i = 0; i < cfg_hdr->ddt_num; i++)
+ act_data += cfg_dde[i].data_size;
+
+ if (act_data != aw_cfg->len) {
+ dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!",
+ act_data, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ /* data check */
+ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+ if (end_data_offset > aw_cfg->len) {
+ dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]",
+ i, end_data_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* crc check */
+ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+ cfg_dde[i].data_size, 0);
+ if (act_crc8 != cfg_dde[i].data_crc) {
+ dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x",
+ i, (u32)act_crc8, cfg_dde[i].data_crc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr;
+
+ if (!aw_cfg) {
+ dev_err(aw_dev->dev, "aw_prof is NULL");
+ return -EINVAL;
+ }
+
+ if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) {
+ dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]",
+ aw_cfg->len, (int)sizeof(struct aw_cfg_hdr));
+ return -EINVAL;
+ }
+
+ crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL);
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ switch (cfg_hdr->hdr_version) {
+ case AW88395_CFG_HDR_VER:
+ return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg);
+ case AW88395_CFG_HDR_VER_V1:
+ return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg);
+ default:
+ dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check);
+
+MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.h b/sound/soc/codecs/aw88395/aw88395_lib.h
new file mode 100644
index 000000000000..8a620920d8bd
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.h
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_lib.h -- ACF bin parsing and check library file for aw88395
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_LIB_H__
+#define __AW88395_LIB_H__
+
+#define CHECK_REGISTER_NUM_OFFSET (4)
+#define VALID_DATA_LEN (4)
+#define VALID_DATA_ADDR (4)
+#define PARSE_DSP_REG_NUM (4)
+#define REG_DATA_BYTP_LEN (8)
+#define CHECK_DSP_REG_NUM (12)
+#define DSP_VALID_DATA_LEN (12)
+#define DSP_VALID_DATA_ADDR (12)
+#define PARSE_SOC_APP_NUM (8)
+#define CHECK_SOC_APP_NUM (12)
+#define APP_DOWNLOAD_ADDR (4)
+#define APP_VALID_DATA_LEN (12)
+#define APP_VALID_DATA_ADDR (12)
+#define BIN_NUM_MAX (100)
+#define HEADER_LEN (60)
+#define BIN_DATA_TYPE_OFFSET (8)
+#define DATA_LEN (44)
+#define VALID_DATA_ADDR_OFFSET (60)
+#define START_ADDR_OFFSET (64)
+
+#define AW88395_FW_CHECK_PART (10)
+#define HDADER_LEN (60)
+
+#define HEADER_VERSION_OFFSET (4)
+
+enum bin_header_version_enum {
+ HEADER_VERSION_V1 = 0x01000000,
+};
+
+enum data_type_enum {
+ DATA_TYPE_REGISTER = 0x00000000,
+ DATA_TYPE_DSP_REG = 0x00000010,
+ DATA_TYPE_DSP_CFG = 0x00000011,
+ DATA_TYPE_SOC_REG = 0x00000020,
+ DATA_TYPE_SOC_APP = 0x00000021,
+ DATA_TYPE_DSP_FW = 0x00000022,
+ DATA_TYPE_MULTI_BINS = 0x00002000,
+};
+
+enum data_version_enum {
+ DATA_VERSION_V1 = 0x00000001,
+ DATA_VERSION_MAX,
+};
+
+struct bin_header_info {
+ unsigned int check_sum;
+ unsigned int header_ver;
+ unsigned int bin_data_type;
+ unsigned int bin_data_ver;
+ unsigned int bin_data_len;
+ unsigned int ui_ver;
+ unsigned char chip_type[8];
+ unsigned int reg_byte_len;
+ unsigned int data_byte_len;
+ unsigned int device_addr;
+ unsigned int valid_data_len;
+ unsigned int valid_data_addr;
+
+ unsigned int reg_num;
+ unsigned int reg_data_byte_len;
+ unsigned int download_addr;
+ unsigned int app_version;
+ unsigned int header_len;
+};
+
+struct bin_container {
+ unsigned int len;
+ unsigned char data[];
+};
+
+struct aw_bin {
+ unsigned char *p_addr;
+ unsigned int all_bin_parse_num;
+ unsigned int multi_bin_parse_num;
+ unsigned int single_bin_parse_num;
+ struct bin_header_info header_info[BIN_NUM_MAX];
+ struct bin_container info;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h
new file mode 100644
index 000000000000..e64f24e97150
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_reg.h
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_reg.h -- AW88395 chip register file
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_REG_H__
+#define __AW88395_REG_H__
+
+#define AW88395_ID_REG (0x00)
+#define AW88395_SYSST_REG (0x01)
+#define AW88395_SYSINT_REG (0x02)
+#define AW88395_SYSINTM_REG (0x03)
+#define AW88395_SYSCTRL_REG (0x04)
+#define AW88395_SYSCTRL2_REG (0x05)
+#define AW88395_I2SCTRL_REG (0x06)
+#define AW88395_I2SCFG1_REG (0x07)
+#define AW88395_I2SCFG2_REG (0x08)
+#define AW88395_HAGCCFG1_REG (0x09)
+#define AW88395_HAGCCFG2_REG (0x0A)
+#define AW88395_HAGCCFG3_REG (0x0B)
+#define AW88395_HAGCCFG4_REG (0x0C)
+#define AW88395_HAGCCFG5_REG (0x0D)
+#define AW88395_HAGCCFG6_REG (0x0E)
+#define AW88395_HAGCCFG7_REG (0x0F)
+#define AW88395_MPDCFG_REG (0x10)
+#define AW88395_PWMCTRL_REG (0x11)
+#define AW88395_I2SCFG3_REG (0x12)
+#define AW88395_DBGCTRL_REG (0x13)
+#define AW88395_HAGCST_REG (0x20)
+#define AW88395_VBAT_REG (0x21)
+#define AW88395_TEMP_REG (0x22)
+#define AW88395_PVDD_REG (0x23)
+#define AW88395_ISNDAT_REG (0x24)
+#define AW88395_VSNDAT_REG (0x25)
+#define AW88395_I2SINT_REG (0x26)
+#define AW88395_I2SCAPCNT_REG (0x27)
+#define AW88395_ANASTA1_REG (0x28)
+#define AW88395_ANASTA2_REG (0x29)
+#define AW88395_ANASTA3_REG (0x2A)
+#define AW88395_ANASTA4_REG (0x2B)
+#define AW88395_TESTDET_REG (0x2C)
+#define AW88395_TESTIN_REG (0x38)
+#define AW88395_TESTOUT_REG (0x39)
+#define AW88395_DSPMADD_REG (0x40)
+#define AW88395_DSPMDAT_REG (0x41)
+#define AW88395_WDT_REG (0x42)
+#define AW88395_ACR1_REG (0x43)
+#define AW88395_ACR2_REG (0x44)
+#define AW88395_ASR1_REG (0x45)
+#define AW88395_ASR2_REG (0x46)
+#define AW88395_DSPCFG_REG (0x47)
+#define AW88395_ASR3_REG (0x48)
+#define AW88395_ASR4_REG (0x49)
+#define AW88395_VSNCTRL1_REG (0x50)
+#define AW88395_ISNCTRL1_REG (0x51)
+#define AW88395_PLLCTRL1_REG (0x52)
+#define AW88395_PLLCTRL2_REG (0x53)
+#define AW88395_PLLCTRL3_REG (0x54)
+#define AW88395_CDACTRL1_REG (0x55)
+#define AW88395_CDACTRL2_REG (0x56)
+#define AW88395_SADCCTRL1_REG (0x57)
+#define AW88395_SADCCTRL2_REG (0x58)
+#define AW88395_CPCTRL1_REG (0x59)
+#define AW88395_BSTCTRL1_REG (0x60)
+#define AW88395_BSTCTRL2_REG (0x61)
+#define AW88395_BSTCTRL3_REG (0x62)
+#define AW88395_BSTCTRL4_REG (0x63)
+#define AW88395_BSTCTRL5_REG (0x64)
+#define AW88395_BSTCTRL6_REG (0x65)
+#define AW88395_BSTCTRL7_REG (0x66)
+#define AW88395_DSMCFG1_REG (0x67)
+#define AW88395_DSMCFG2_REG (0x68)
+#define AW88395_DSMCFG3_REG (0x69)
+#define AW88395_DSMCFG4_REG (0x6A)
+#define AW88395_DSMCFG5_REG (0x6B)
+#define AW88395_DSMCFG6_REG (0x6C)
+#define AW88395_DSMCFG7_REG (0x6D)
+#define AW88395_DSMCFG8_REG (0x6E)
+#define AW88395_TESTCTRL1_REG (0x70)
+#define AW88395_TESTCTRL2_REG (0x71)
+#define AW88395_EFCTRL1_REG (0x72)
+#define AW88395_EFCTRL2_REG (0x73)
+#define AW88395_EFWH_REG (0x74)
+#define AW88395_EFWM2_REG (0x75)
+#define AW88395_EFWM1_REG (0x76)
+#define AW88395_EFWL_REG (0x77)
+#define AW88395_EFRH_REG (0x78)
+#define AW88395_EFRM2_REG (0x79)
+#define AW88395_EFRM1_REG (0x7A)
+#define AW88395_EFRL_REG (0x7B)
+#define AW88395_TM_REG (0x7C)
+
+enum aw88395_id {
+ AW88395_CHIP_ID = 0x2049,
+};
+
+#define AW88395_REG_MAX (0x7D)
+
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_UVLS_START_BIT (14)
+#define AW88395_UVLS_NORMAL (0)
+#define AW88395_UVLS_NORMAL_VALUE \
+ (AW88395_UVLS_NORMAL << AW88395_UVLS_START_BIT)
+
+#define AW88395_DSPS_START_BIT (12)
+#define AW88395_DSPS_BITS_LEN (1)
+#define AW88395_DSPS_MASK \
+ (~(((1<<AW88395_DSPS_BITS_LEN)-1) << AW88395_DSPS_START_BIT))
+
+#define AW88395_DSPS_NORMAL (0)
+#define AW88395_DSPS_NORMAL_VALUE \
+ (AW88395_DSPS_NORMAL << AW88395_DSPS_START_BIT)
+
+#define AW88395_BSTOCS_START_BIT (11)
+#define AW88395_BSTOCS_OVER_CURRENT (1)
+#define AW88395_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88395_BSTOCS_OVER_CURRENT << AW88395_BSTOCS_START_BIT)
+
+#define AW88395_BSTS_START_BIT (9)
+#define AW88395_BSTS_FINISHED (1)
+#define AW88395_BSTS_FINISHED_VALUE \
+ (AW88395_BSTS_FINISHED << AW88395_BSTS_START_BIT)
+
+#define AW88395_SWS_START_BIT (8)
+#define AW88395_SWS_SWITCHING (1)
+#define AW88395_SWS_SWITCHING_VALUE \
+ (AW88395_SWS_SWITCHING << AW88395_SWS_START_BIT)
+
+#define AW88395_NOCLKS_START_BIT (5)
+#define AW88395_NOCLKS_NO_CLOCK (1)
+#define AW88395_NOCLKS_NO_CLOCK_VALUE \
+ (AW88395_NOCLKS_NO_CLOCK << AW88395_NOCLKS_START_BIT)
+
+#define AW88395_CLKS_START_BIT (4)
+#define AW88395_CLKS_STABLE (1)
+#define AW88395_CLKS_STABLE_VALUE \
+ (AW88395_CLKS_STABLE << AW88395_CLKS_START_BIT)
+
+#define AW88395_OCDS_START_BIT (3)
+#define AW88395_OCDS_OC (1)
+#define AW88395_OCDS_OC_VALUE \
+ (AW88395_OCDS_OC << AW88395_OCDS_START_BIT)
+
+#define AW88395_OTHS_START_BIT (1)
+#define AW88395_OTHS_OT (1)
+#define AW88395_OTHS_OT_VALUE \
+ (AW88395_OTHS_OT << AW88395_OTHS_START_BIT)
+
+#define AW88395_PLLS_START_BIT (0)
+#define AW88395_PLLS_LOCKED (1)
+#define AW88395_PLLS_LOCKED_VALUE \
+ (AW88395_PLLS_LOCKED << AW88395_PLLS_START_BIT)
+
+#define AW88395_BIT_PLL_CHECK \
+ (AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_BIT_SYSST_CHECK_MASK \
+ (~(AW88395_UVLS_NORMAL_VALUE | \
+ AW88395_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_NOCLKS_NO_CLOCK_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_OCDS_OC_VALUE | \
+ AW88395_OTHS_OT_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE))
+
+#define AW88395_BIT_SYSST_CHECK \
+ (AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_WDI_START_BIT (6)
+#define AW88395_WDI_INT_VALUE (1)
+#define AW88395_WDI_INTERRUPT \
+ (AW88395_WDI_INT_VALUE << AW88395_WDI_START_BIT)
+
+#define AW88395_NOCLKI_START_BIT (5)
+#define AW88395_NOCLKI_INT_VALUE (1)
+#define AW88395_NOCLKI_INTERRUPT \
+ (AW88395_NOCLKI_INT_VALUE << AW88395_NOCLKI_START_BIT)
+
+#define AW88395_CLKI_START_BIT (4)
+#define AW88395_CLKI_INT_VALUE (1)
+#define AW88395_CLKI_INTERRUPT \
+ (AW88395_CLKI_INT_VALUE << AW88395_CLKI_START_BIT)
+
+#define AW88395_PLLI_START_BIT (0)
+#define AW88395_PLLI_INT_VALUE (1)
+#define AW88395_PLLI_INTERRUPT \
+ (AW88395_PLLI_INT_VALUE << AW88395_PLLI_START_BIT)
+
+#define AW88395_BIT_SYSINT_CHECK \
+ (AW88395_WDI_INTERRUPT | \
+ AW88395_CLKI_INTERRUPT | \
+ AW88395_NOCLKI_INTERRUPT | \
+ AW88395_PLLI_INTERRUPT)
+
+#define AW88395_HMUTE_START_BIT (8)
+#define AW88395_HMUTE_BITS_LEN (1)
+#define AW88395_HMUTE_MASK \
+ (~(((1<<AW88395_HMUTE_BITS_LEN)-1) << AW88395_HMUTE_START_BIT))
+
+#define AW88395_HMUTE_DISABLE (0)
+#define AW88395_HMUTE_DISABLE_VALUE \
+ (AW88395_HMUTE_DISABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_HMUTE_ENABLE (1)
+#define AW88395_HMUTE_ENABLE_VALUE \
+ (AW88395_HMUTE_ENABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_RCV_MODE_START_BIT (7)
+#define AW88395_RCV_MODE_BITS_LEN (1)
+#define AW88395_RCV_MODE_MASK \
+ (~(((1<<AW88395_RCV_MODE_BITS_LEN)-1) << AW88395_RCV_MODE_START_BIT))
+
+#define AW88395_RCV_MODE_RECEIVER (1)
+#define AW88395_RCV_MODE_RECEIVER_VALUE \
+ (AW88395_RCV_MODE_RECEIVER << AW88395_RCV_MODE_START_BIT)
+
+#define AW88395_DSPBY_START_BIT (2)
+#define AW88395_DSPBY_BITS_LEN (1)
+#define AW88395_DSPBY_MASK \
+ (~(((1<<AW88395_DSPBY_BITS_LEN)-1) << AW88395_DSPBY_START_BIT))
+
+#define AW88395_DSPBY_WORKING (0)
+#define AW88395_DSPBY_WORKING_VALUE \
+ (AW88395_DSPBY_WORKING << AW88395_DSPBY_START_BIT)
+
+#define AW88395_DSPBY_BYPASS (1)
+#define AW88395_DSPBY_BYPASS_VALUE \
+ (AW88395_DSPBY_BYPASS << AW88395_DSPBY_START_BIT)
+
+#define AW88395_AMPPD_START_BIT (1)
+#define AW88395_AMPPD_BITS_LEN (1)
+#define AW88395_AMPPD_MASK \
+ (~(((1<<AW88395_AMPPD_BITS_LEN)-1) << AW88395_AMPPD_START_BIT))
+
+#define AW88395_AMPPD_WORKING (0)
+#define AW88395_AMPPD_WORKING_VALUE \
+ (AW88395_AMPPD_WORKING << AW88395_AMPPD_START_BIT)
+
+#define AW88395_AMPPD_POWER_DOWN (1)
+#define AW88395_AMPPD_POWER_DOWN_VALUE \
+ (AW88395_AMPPD_POWER_DOWN << AW88395_AMPPD_START_BIT)
+
+#define AW88395_PWDN_START_BIT (0)
+#define AW88395_PWDN_BITS_LEN (1)
+#define AW88395_PWDN_MASK \
+ (~(((1<<AW88395_PWDN_BITS_LEN)-1) << AW88395_PWDN_START_BIT))
+
+#define AW88395_PWDN_WORKING (0)
+#define AW88395_PWDN_WORKING_VALUE \
+ (AW88395_PWDN_WORKING << AW88395_PWDN_START_BIT)
+
+#define AW88395_PWDN_POWER_DOWN (1)
+#define AW88395_PWDN_POWER_DOWN_VALUE \
+ (AW88395_PWDN_POWER_DOWN << AW88395_PWDN_START_BIT)
+
+#define AW88395_MUTE_VOL (90 * 8)
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_VOL_6DB_START (6)
+#define AW88395_VOL_START_BIT (6)
+#define AW88395_VOL_BITS_LEN (10)
+#define AW88395_VOL_MASK \
+ (~(((1<<AW88395_VOL_BITS_LEN)-1) << AW88395_VOL_START_BIT))
+
+#define AW88395_VOL_DEFAULT_VALUE (0)
+
+#define AW88395_I2STXEN_START_BIT (0)
+#define AW88395_I2STXEN_BITS_LEN (1)
+#define AW88395_I2STXEN_MASK \
+ (~(((1<<AW88395_I2STXEN_BITS_LEN)-1) << AW88395_I2STXEN_START_BIT))
+
+#define AW88395_I2STXEN_DISABLE (0)
+#define AW88395_I2STXEN_DISABLE_VALUE \
+ (AW88395_I2STXEN_DISABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_I2STXEN_ENABLE (1)
+#define AW88395_I2STXEN_ENABLE_VALUE \
+ (AW88395_I2STXEN_ENABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_START_BIT (15)
+#define AW88395_AGC_DSP_CTL_BITS_LEN (1)
+#define AW88395_AGC_DSP_CTL_MASK \
+ (~(((1<<AW88395_AGC_DSP_CTL_BITS_LEN)-1) << AW88395_AGC_DSP_CTL_START_BIT))
+
+#define AW88395_AGC_DSP_CTL_DISABLE (0)
+#define AW88395_AGC_DSP_CTL_DISABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_DISABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_ENABLE (1)
+#define AW88395_AGC_DSP_CTL_ENABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_ENABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_VDSEL_START_BIT (0)
+#define AW88395_VDSEL_BITS_LEN (1)
+#define AW88395_VDSEL_MASK \
+ (~(((1<<AW88395_VDSEL_BITS_LEN)-1) << AW88395_VDSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_START_BIT (3)
+#define AW88395_MEM_CLKSEL_BITS_LEN (1)
+#define AW88395_MEM_CLKSEL_MASK \
+ (~(((1<<AW88395_MEM_CLKSEL_BITS_LEN)-1) << AW88395_MEM_CLKSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_OSC_CLK (0)
+#define AW88395_MEM_CLKSEL_OSC_CLK_VALUE \
+ (AW88395_MEM_CLKSEL_OSC_CLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_MEM_CLKSEL_DAP_HCLK (1)
+#define AW88395_MEM_CLKSEL_DAP_HCLK_VALUE \
+ (AW88395_MEM_CLKSEL_DAP_HCLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_CCO_MUX_START_BIT (14)
+#define AW88395_CCO_MUX_BITS_LEN (1)
+#define AW88395_CCO_MUX_MASK \
+ (~(((1<<AW88395_CCO_MUX_BITS_LEN)-1) << AW88395_CCO_MUX_START_BIT))
+
+#define AW88395_CCO_MUX_DIVIDED (0)
+#define AW88395_CCO_MUX_DIVIDED_VALUE \
+ (AW88395_CCO_MUX_DIVIDED << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_CCO_MUX_BYPASS (1)
+#define AW88395_CCO_MUX_BYPASS_VALUE \
+ (AW88395_CCO_MUX_BYPASS << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_EF_VSN_GESLP_START_BIT (0)
+#define AW88395_EF_VSN_GESLP_BITS_LEN (10)
+#define AW88395_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88395_EF_VSN_GESLP_BITS_LEN)-1) << AW88395_EF_VSN_GESLP_START_BIT))
+
+#define AW88395_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_EF_ISN_GESLP_START_BIT (0)
+#define AW88395_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88395_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88395_EF_ISN_GESLP_BITS_LEN)-1) << AW88395_EF_ISN_GESLP_START_BIT))
+
+#define AW88395_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_CABL_BASE_VALUE (1000)
+#define AW88395_ICABLK_FACTOR (1)
+#define AW88395_VCABLK_FACTOR (1)
+#define AW88395_VCAL_FACTOR (1 << 12)
+#define AW88395_VSCAL_FACTOR (16500)
+#define AW88395_ISCAL_FACTOR (3667)
+#define AW88395_EF_VSENSE_GAIN_SHIFT (0)
+
+#define AW88395_VCABLK_FACTOR_DAC (2)
+#define AW88395_VSCAL_FACTOR_DAC (11790)
+#define AW88395_EF_DAC_GESLP_SHIFT (10)
+#define AW88395_EF_DAC_GESLP_SIGN_MASK (1 << 5)
+#define AW88395_EF_DAC_GESLP_SIGN_NEG (0xffc0)
+
+#define AW88395_VCALB_ADJ_FACTOR (12)
+
+#define AW88395_WDT_CNT_START_BIT (0)
+#define AW88395_WDT_CNT_BITS_LEN (8)
+#define AW88395_WDT_CNT_MASK \
+ (~(((1<<AW88395_WDT_CNT_BITS_LEN)-1) << AW88395_WDT_CNT_START_BIT))
+
+#define AW88395_DSP_CFG_ADDR (0x9C80)
+#define AW88395_DSP_FW_ADDR (0x8C00)
+#define AW88395_DSP_REG_VMAX (0x9C94)
+#define AW88395_DSP_REG_CFG_ADPZ_RE (0x9D00)
+#define AW88395_DSP_REG_VCALB (0x9CF7)
+#define AW88395_DSP_RE_SHIFT (12)
+
+#define AW88395_DSP_REG_CFG_ADPZ_RA (0x9D02)
+#define AW88395_DSP_REG_CRC_ADDR (0x9F42)
+#define AW88395_DSP_CALI_F0_DELAY (0x9CFD)
+
+#endif
diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c
new file mode 100644
index 000000000000..b588c27909b5
--- /dev/null
+++ b/sound/soc/codecs/aw88399.c
@@ -0,0 +1,2173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88399.c -- ALSA SoC AW88399 codec support
+//
+// Copyright (c) 2023 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include <linux/sort.h>
+#include <sound/soc.h>
+#include "aw88399.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw88399_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88399_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ int ret;
+
+ if (pwd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, &reg_val);
+ if (ret)
+ dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret);
+ else
+ *int_status = reg_val;
+
+ dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status);
+}
+
+static void aw_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ u16 int_status;
+
+ /* read int status and clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ /* make sure int status is clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ if (int_status)
+ dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status);
+}
+
+static int aw_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88399_CCO_MUX_MASK);
+ if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG,
+ ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG,
+ ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88399_NOISE_GATE_EN_MASK))
+ check_val = AW88399_BIT_SYSST_NOSWS_CHECK;
+ else
+ check_val = AW88399_BIT_SYSST_SWS_CHECK;
+
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK);
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable)
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk)
+{
+ uint16_t icalkh_val, icalkl_val, icalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, &reg_val);
+ if (ret)
+ return ret;
+ icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+ icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ icalk_val = icalkh_val & icalkl_val;
+ else
+ icalk_val = icalkh_val | icalkl_val;
+
+ if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK))
+ icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG;
+ *icalk = (int16_t)icalk_val;
+
+ return 0;
+}
+
+static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk)
+{
+ uint16_t vcalkh_val, vcalkl_val, vcalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ vcalk_val = vcalkh_val & vcalkl_val;
+ else
+ vcalk_val = vcalkh_val | vcalkl_val;
+
+ if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK)
+ vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG;
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk)
+{
+ uint16_t vcalkh_val, vcalkl_val, vcalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, &reg_val);
+ if (ret)
+ return ret;
+ vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+ vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) &
+ (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT);
+ else
+ vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) |
+ (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT);
+
+ if (vcalk_val & (~AW88399_TEM4_SIGN_MASK))
+ vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG;
+
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw_dev_set_vcalb(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int vsense_select, vsense_value;
+ int32_t ical_k, vcal_k, vcalb;
+ int16_t icalk, vcalk;
+ uint16_t reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value);
+ if (ret)
+ return ret;
+
+ vsense_select = vsense_value & (~AW88399_VDSEL_MASK);
+
+ ret = aw88399_dev_get_icalk(aw88399, &icalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get icalk failed\n");
+ return ret;
+ }
+
+ ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE;
+
+ switch (vsense_select) {
+ case AW88399_DEV_VDSEL_VSENSE:
+ ret = aw88399_dev_get_vcalk(aw88399, &vcalk);
+ vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE;
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR *
+ ical_k / vcal_k * aw88399->vcalb_init_val;
+ break;
+ case AW88399_DEV_VDSEL_DAC:
+ ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk);
+ vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE;
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR /
+ AW88399_ISCAL_DAC_FACTOR * ical_k /
+ vcal_k * aw88399->vcalb_init_val;
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ return ret;
+
+ vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR;
+ reg_val = (uint32_t)vcalb;
+
+ regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val);
+
+ return 0;
+}
+
+static int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ uint16_t re_lbits, re_hbits;
+ u32 cali_re;
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) ||
+ (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN))
+ return -EINVAL;
+
+ cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT);
+
+ re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT;
+ re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re error");
+
+ return ret;
+}
+
+static int aw_dev_fw_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, fw_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate fw_end_addr */
+ fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR;
+
+ /* write fw_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_END_ADDR_MASK, fw_len_val);
+ if (ret)
+ return ret;
+ /* enable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE);
+
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+
+ /* read crc check result */
+ regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT;
+
+ /* disable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88399_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x",
+ __func__, check_val, AW88399_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_cfg_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, cfg_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate cfg end addr */
+ cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR;
+
+ /* write cfg_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_END_ADDR_MASK, cfg_len_val);
+ if (ret)
+ return ret;
+
+ /* enable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE);
+ if (ret)
+ return ret;
+
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+
+ /* read crc check result */
+ ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT;
+
+ /* disable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88399_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x",
+ check_val, AW88399_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_hw_crc_check(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_fw_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = aw_dev_cfg_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "cfg_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE);
+
+ return ret;
+
+crc_check_failed:
+ regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE);
+ return ret;
+}
+
+static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag)
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG,
+ ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88399_WDT_CNT_MASK)))
+ return -EPERM;
+
+ return 0;
+}
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88399_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88399_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) {
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp wdt status error=%d", ret);
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int reg_value;
+ u16 real_value;
+ int ret;
+
+ real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK);
+
+ ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+static void aw_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ u16 fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88399_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (dither)
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE);
+}
+
+static int aw88399_dev_start(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88399_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88399_dev_set_dither(aw88399, false);
+
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) {
+ ret = aw_dev_hw_crc_check(aw88399);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed");
+ goto crc_check_fail;
+ }
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_set_vcalb(aw88399);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed");
+ goto dsp_check_fail;
+ }
+ } else {
+ dev_dbg(aw_dev->dev, "start pa with dsp bypass");
+ }
+
+ /* enable tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, true);
+
+ if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE)
+ aw88399_dev_set_dither(aw88399, true);
+
+ /* close mute */
+ aw88399_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88399_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+sysst_check_fail:
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88399_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ u32 tmp_len;
+ int i, ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_get_ra(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ u32 dsp_ra;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error");
+ return ret;
+ }
+
+ cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88399_DSP_RE_SHIFT);
+
+ return 0;
+}
+
+static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp config len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp config data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR);
+ if (ret)
+ return ret;
+
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp firmware len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp firmware data is null or len is 0");
+ return -EINVAL;
+ }
+
+ aw_dev->dsp_fw_len = len;
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR);
+
+ return ret;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ /* read dsp_rom_check_reg */
+ aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, &reg_val, AW_DSP_16_DATA);
+ if (reg_val != AW88399_DSP_ROM_CHECK_DATA) {
+ dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]",
+ reg_val, AW88399_DSP_ROM_CHECK_DATA);
+ return -EPERM;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR,
+ AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA);
+ aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, &reg_val, AW_DSP_16_DATA);
+ if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88399_DSP_ODD_NUM_BIT_TEST);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88399_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_MEM_CLKSEL_MASK,
+ AW88399_MEM_CLKSEL_DAPHCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed");
+ break;
+ case AW88399_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_MEM_CLKSEL_MASK,
+ AW88399_MEM_CLKSEL_OSCCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag);
+ break;
+ }
+}
+
+static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev)
+{
+ struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE)
+ profctrl_desc->cur_mode = AW88399_RCV_MODE;
+ else
+ profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE;
+}
+
+static int aw_dev_update_reg_container(struct aw88399 *aw88399,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ u16 read_vol, reg_val;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u8 reg_addr;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88399_DSPVCALB_REG) {
+ aw88399->vcalb_init_val = reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88399_SYSCTRL_REG) {
+ if (reg_val & (~AW88399_DSPBY_MASK))
+ aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS;
+ else
+ aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK;
+
+ reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK |
+ AW88399_DSPBY_MASK);
+ reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE |
+ AW88399_DSPBY_BYPASS_VALUE);
+ }
+
+ if (reg_addr == AW88399_I2SCTRL3_REG) {
+ reg_val &= AW88399_I2STXEN_MASK;
+ reg_val |= AW88399_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88399_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88399_VOL_MASK)) >>
+ AW88399_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ if (reg_addr == AW88399_DBGCTRL_REG) {
+ if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE)
+ aw88399->check_val = AW_EF_OR_CHECK;
+ else
+ aw88399->check_val = AW_EF_AND_CHECK;
+
+ aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK);
+ }
+
+ if (reg_addr == AW88399_CRCCTRL_REG)
+ aw88399->crc_init_val = reg_val;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+
+ aw_dev_get_cur_mode_st(aw_dev);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+ else
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+
+ return 0;
+}
+
+static int aw_dev_reg_update(struct aw88399 *aw88399,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw88399, data, len);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "reg update failed");
+
+ return ret;
+}
+
+static int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+
+static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ if ((aw_dev->prof_cur == aw_dev->prof_index) &&
+ (force_up_en == AW88399_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw_dev_reg_update(aw88399, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw88399_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC);
+
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed");
+ goto error;
+ }
+
+ if (up_dsp_fw_en) {
+ dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver);
+ ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_FW].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ goto error;
+ }
+ }
+
+ /* update dsp config */
+ ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_CFG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ goto error;
+ }
+
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+ return ret;
+}
+
+static void aw88399_start_pa(struct aw88399 *aw88399)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88399_START_RETRIES; i++) {
+ ret = aw88399_dev_start(aw88399);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i);
+ ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_dbg(aw88399->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88399_startup_work(struct work_struct *work)
+{
+ struct aw88399 *aw88399 =
+ container_of(work, struct aw88399, start_work.work);
+
+ mutex_lock(&aw88399->lock);
+ aw88399_start_pa(aw88399);
+ mutex_unlock(&aw88399->lock);
+}
+
+static void aw88399_start(struct aw88399 *aw88399, bool sync_start)
+{
+ int ret;
+
+ if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK)
+ return;
+
+ if (aw88399->aw_pa->status == AW88399_DEV_PW_ON)
+ return;
+
+ ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "fw update failed.");
+ return;
+ }
+
+ if (sync_start == AW88399_SYNC_START)
+ aw88399_start_pa(aw88399);
+ else
+ queue_delayed_work(system_dfl_wq,
+ &aw88399->start_work,
+ AW88399_START_WORK_DELAY_MS);
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+ if (reg_val & AW88399_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88399_stop(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_cfg =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG];
+ struct aw_sec_data_desc *dsp_fw =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW];
+ int int_st;
+
+ if (aw_dev->status == AW88399_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88399_DEV_PW_OFF;
+
+ aw88399_dev_mute(aw_dev, true);
+ usleep_range(AW88399_4000_US, AW88399_4000_US + 100);
+
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 100);
+
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st) {
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC);
+ aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+ }
+
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver aw88399_dai[] = {
+ {
+ .name = "aw88399-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88399_RATES,
+ .formats = AW88399_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88399_RATES,
+ .formats = AW88399_FORMATS,
+ },
+ },
+};
+
+static void aw_cali_svc_run_mute(struct aw_device *aw_dev, uint16_t cali_result)
+{
+ if (cali_result == CALI_RESULT_ERROR)
+ aw88399_dev_mute(aw_dev, true);
+ else if (cali_result == CALI_RESULT_NORMAL)
+ aw88399_dev_mute(aw_dev, false);
+}
+
+static int aw_cali_svc_get_cali_cfg(struct aw_device *aw_dev)
+{
+ struct cali_cfg *cali_cfg = &aw_dev->cali_desc.cali_cfg;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ &cali_cfg->data[0], AW_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ &cali_cfg->data[1], AW_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ &cali_cfg->data[2], AW_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ &cali_cfg->data[3], AW_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_set_cali_cfg(struct aw_device *aw_dev,
+ struct cali_cfg cali_cfg)
+{
+ int ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ cali_cfg.data[0], AW_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ cali_cfg.data[1], AW_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ cali_cfg.data[2], AW_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ cali_cfg.data[3], AW_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_cali_en(struct aw_device *aw_dev, bool cali_en)
+{
+ struct cali_cfg set_cfg;
+ int ret;
+
+ aw_dev_dsp_enable(aw_dev, false);
+ if (cali_en) {
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, AW883XX_DSP_NG_EN_DISABLE_VALUE);
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ AW88399_DSP_LOW_POWER_SWITCH_DISABLE, AW_DSP_16_DATA);
+
+ ret = aw_cali_svc_get_cali_cfg(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get cali cfg failed\n");
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ set_cfg.data[0] = 0;
+ set_cfg.data[1] = 0;
+ set_cfg.data[2] = -1;
+ set_cfg.data[3] = 1;
+
+ ret = aw_cali_svc_set_cali_cfg(aw_dev, set_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali cfg failed\n");
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ } else {
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ }
+
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+}
+
+static int aw_cali_svc_cali_run_dsp_vol(struct aw_device *aw_dev, bool enable)
+{
+ unsigned int reg_val;
+ int ret;
+
+ if (enable) {
+ ret = regmap_read(aw_dev->regmap, AW88399_DSPCFG_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg 0x%x failed\n", AW88399_DSPCFG_REG);
+ return ret;
+ }
+
+ aw_dev->cali_desc.store_vol = reg_val & (~AW88399_DSP_VOL_MASK);
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, AW88399_DSP_VOL_MUTE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, aw_dev->cali_desc.store_vol);
+ }
+
+ return ret;
+}
+
+static void aw_cali_svc_backup_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+ unsigned int reg_val, dsp_val;
+
+ regmap_read(aw_dev->regmap, AW88399_DBGCTRL_REG, &reg_val);
+ backup_desc->dsp_ng_cfg = reg_val & (~AW883XX_DSP_NG_EN_MASK);
+
+ aw_dev_dsp_read(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, &dsp_val, AW_DSP_16_DATA);
+
+ backup_desc->dsp_lp_cfg = dsp_val;
+}
+
+static void aw_cali_svc_recover_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, backup_desc->dsp_ng_cfg);
+
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ backup_desc->dsp_lp_cfg, AW_DSP_16_DATA);
+}
+
+static int aw_cali_svc_cali_re_mode_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ return ret;
+ }
+
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status error\n");
+ return ret;
+ }
+
+ aw_cali_svc_backup_info(aw_dev);
+ ret = aw_cali_svc_cali_en(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_cali_svc_cali_en failed\n");
+ return ret;
+ }
+
+ ret = aw_cali_svc_cali_run_dsp_vol(aw_dev, true);
+ if (ret) {
+ aw_cali_svc_cali_en(aw_dev, false);
+ return ret;
+ }
+
+ } else {
+ aw_cali_svc_cali_run_dsp_vol(aw_dev, false);
+ aw_cali_svc_recover_info(aw_dev);
+ aw_cali_svc_cali_en(aw_dev, false);
+ }
+
+ return 0;
+}
+
+static int aw_cali_svc_get_dev_re(struct aw_device *aw_dev, uint32_t *re)
+{
+ uint32_t dsp_re, show_re;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CALRE, &dsp_re, AW_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ show_re = AW88399_DSP_RE_TO_SHOW_RE(dsp_re, AW88399_DSP_REG_CALRE_SHIFT);
+
+ *re = (uint32_t)(show_re - aw_dev->cali_desc.ra);
+
+ return 0;
+}
+
+static void aw_cali_svc_del_max_min_ave_algo(uint32_t *data, int data_size, uint32_t *dsp_re)
+{
+ int sum = 0, i;
+
+ for (i = 1; i < data_size - 1; i++)
+ sum += data[i];
+
+ *dsp_re = sum / (data_size - AW_CALI_DATA_SUM_RM);
+}
+
+static int aw_cali_svc_get_iv_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_data;
+ int ret, i;
+
+ for (i = 0; i < AW_GET_IV_CNT_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_ASR1_REG, &reg_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "read 0x%x failed\n", AW88399_ASR1_REG);
+ return ret;
+ }
+
+ reg_data &= (~AW88399_REABS_MASK);
+ if (!reg_data)
+ return 0;
+ msleep(30);
+ }
+
+ dev_err(aw_dev->dev, "IV data abnormal, please check\n");
+
+ return -EINVAL;
+}
+
+static int compare_ints(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static int aw_cali_svc_get_smooth_cali_re(struct aw_device *aw_dev)
+{
+ uint32_t re_temp[AW_CALI_READ_CNT_MAX];
+ uint32_t dsp_re;
+ int ret, i;
+
+ for (i = 0; i < AW_CALI_READ_CNT_MAX; i++) {
+ ret = aw_cali_svc_get_dev_re(aw_dev, &re_temp[i]);
+ if (ret)
+ goto cali_re_fail;
+ msleep(30);
+ }
+
+ sort(re_temp, AW_CALI_READ_CNT_MAX, sizeof(uint32_t), compare_ints, NULL);
+
+ aw_cali_svc_del_max_min_ave_algo(re_temp, AW_CALI_READ_CNT_MAX, &dsp_re);
+
+ ret = aw_cali_svc_get_iv_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get iv data failed");
+ goto cali_re_fail;
+ }
+
+ if (dsp_re < AW88399_CALI_RE_MIN || dsp_re > AW88399_CALI_RE_MAX) {
+ dev_err(aw_dev->dev, "out range re value: [%d]mohm\n", dsp_re);
+ aw_dev->cali_desc.cali_re = dsp_re;
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+
+ return 0;
+ }
+
+ aw_dev->cali_desc.cali_result = CALI_RESULT_NORMAL;
+
+ aw_dev->cali_desc.cali_re = dsp_re;
+ dev_dbg(aw_dev->dev, "re[%d]mohm\n", aw_dev->cali_desc.cali_re);
+
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+
+cali_re_fail:
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+ return -EINVAL;
+}
+
+static int aw_cali_svc_dev_cali_re(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_cali_desc *cali_desc = &aw_dev->cali_desc;
+ int ret;
+
+ if (cali_desc->cali_running) {
+ dev_err(aw_dev->dev, "calibration in progress\n");
+ return -EINVAL;
+ }
+
+ cali_desc->cali_running = true;
+ aw_cali_svc_run_mute(aw_dev, CALI_RESULT_NORMAL);
+
+ ret = aw_cali_svc_cali_re_mode_enable(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "start cali re failed\n");
+ goto re_mode_err;
+ }
+
+ msleep(1000);
+
+ ret = aw_cali_svc_get_smooth_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "get cali re failed\n");
+
+ aw_cali_svc_cali_re_mode_enable(aw_dev, false);
+
+re_mode_err:
+ cali_desc->cali_running = false;
+
+ return ret;
+}
+
+static int aw88399_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88399_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88399_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EINVAL;
+
+ aw_dev->prof_index = index;
+ dev_dbg(aw_dev->dev, "set prof[%s]",
+ aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]);
+
+ return 0;
+}
+
+static int aw88399_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88399->aw_pa->prof_info.count;
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88399_dev_get_prof_name(aw88399->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88399_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88399->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88399_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw88399->lock);
+ ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88399->lock);
+ return 0;
+ }
+
+ if (aw88399->aw_pa->status) {
+ aw88399_stop(aw88399->aw_pa);
+ aw88399_start(aw88399, AW88399_SYNC_START);
+ }
+
+ mutex_unlock(&aw88399->lock);
+
+ return 1;
+}
+
+static int aw88399_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88399->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88399_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88399->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw_dev_set_volume(aw88399->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88399->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88399_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88399->aw_pa->fade_step != value) {
+ aw88399->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88399_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw_dev->cali_desc.cali_re != value) {
+ aw_dev->cali_desc.cali_re = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_calib_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_switch;
+
+ return 0;
+}
+
+static int aw88399_calib_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->cali_desc.cali_switch == ucontrol->value.integer.value[0])
+ return 0;
+
+ aw_dev->cali_desc.cali_switch = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int aw88399_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* do nothing */
+ return 0;
+}
+
+static int aw88399_calib_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->status && aw_dev->cali_desc.cali_switch)
+ aw_cali_svc_dev_cali_re(aw88399);
+
+ return 0;
+}
+
+static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88399_1000_US / 10;
+ aw_dev->fade_out_time = AW88399_1000_US >> 1;
+ aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id;
+ aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id;
+
+ ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88399_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 100);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88399_request_firmware_file(struct aw88399 *aw88399)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88399_ACF_FILE, cont ? cont->size : 0);
+
+ aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev,
+ struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw88399->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88399->aw_cfg->len = (int)cont->size;
+ memcpy(aw88399->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88399->lock);
+ /* aw device init */
+ ret = aw88399_dev_init(aw88399, aw88399->aw_cfg);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88399->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aw88399_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG,
+ 6, AW88399_MUTE_VOL, 0, aw88399_volume_get,
+ aw88399_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88399_MUTE_VOL, 0,
+ aw88399_get_fade_step, aw88399_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88399_get_fade_in_time, aw88399_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88399_get_fade_out_time, aw88399_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88399_CALI_RE_MAX, 0,
+ aw88399_re_get, aw88399_re_set),
+ SOC_SINGLE_BOOL_EXT("Calib Switch", 0,
+ aw88399_calib_switch_get, aw88399_calib_switch_set),
+ SOC_SINGLE_EXT("Trigger Calib", SND_SOC_NOPM, 0, 1, 0,
+ aw88399_calib_get, aw88399_calib_set),
+ AW88399_PROFILE_EXT("AW88399 Profile Set", aw88399_profile_info,
+ aw88399_profile_get, aw88399_profile_set),
+};
+
+static int aw88399_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88399->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88399_start(aw88399, AW88399_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88399_stop(aw88399->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88399->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88399_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88399_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88399_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88399_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88399->start_work, aw88399_startup_work);
+
+ ret = aw88399_request_firmware_file(aw88399);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88399_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88399->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88399 = {
+ .probe = aw88399_codec_probe,
+ .remove = aw88399_codec_remove,
+ .dapm_widgets = aw88399_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88399_dapm_widgets),
+ .dapm_routes = aw88399_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88399_audio_map),
+ .controls = aw88399_controls,
+ .num_controls = ARRAY_SIZE(aw88399_controls),
+};
+
+static void aw88399_hw_reset(struct aw88399 *aw88399)
+{
+ if (aw88399->reset_gpio) {
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 1);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 0);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 1);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ }
+}
+
+static void aw88399_parse_channel_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw_dev->channel = channel_value;
+}
+
+static int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(regmap, AW88399_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+ if (chip_id != AW88399_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+ dev_dbg(&i2c->dev, "chip id = %x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+ aw88399->aw_pa = aw_dev;
+
+ aw_dev->i2c = i2c;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->regmap = regmap;
+ mutex_init(&aw_dev->dsp_lock);
+
+ aw_dev->chip_id = chip_id;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = AW88399_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW88399_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88399_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE;
+
+ aw88399_parse_channel_dt(aw_dev);
+
+ return 0;
+}
+
+static int aw88399_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88399 *aw88399;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88399 = devm_kzalloc(&i2c->dev, sizeof(*aw88399), GFP_KERNEL);
+ if (!aw88399)
+ return -ENOMEM;
+
+ mutex_init(&aw88399->lock);
+
+ i2c_set_clientdata(i2c, aw88399);
+
+ aw88399->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88399->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88399->reset_gpio),
+ "reset gpio not defined\n");
+ aw88399_hw_reset(aw88399);
+
+ aw88399->regmap = devm_regmap_init_i2c(i2c, &aw88399_remap_config);
+ if (IS_ERR(aw88399->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88399->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88399_init(aw88399, i2c, aw88399->regmap);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88399,
+ aw88399_dai, ARRAY_SIZE(aw88399_dai));
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw88399: %d", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw88399_i2c_id[] = {
+ { AW88399_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id aw88399_acpi_match[] = {
+ { "AWDZ8399", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, aw88399_acpi_match);
+#endif
+
+static struct i2c_driver aw88399_i2c_driver = {
+ .driver = {
+ .name = AW88399_I2C_NAME,
+ .acpi_match_table = ACPI_PTR(aw88399_acpi_match),
+ },
+ .probe = aw88399_i2c_probe,
+ .id_table = aw88399_i2c_id,
+};
+module_i2c_driver(aw88399_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88399 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h
new file mode 100644
index 000000000000..b386f4836748
--- /dev/null
+++ b/sound/soc/codecs/aw88399.h
@@ -0,0 +1,629 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88399.h -- ALSA SoC AW88399 codec support
+//
+// Copyright (c) 2023 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88399_H__
+#define __AW88399_H__
+
+/* registers list */
+#define AW88399_ID_REG (0x00)
+#define AW88399_SYSST_REG (0x01)
+#define AW88399_SYSINT_REG (0x02)
+#define AW88399_SYSINTM_REG (0x03)
+#define AW88399_SYSCTRL_REG (0x04)
+#define AW88399_SYSCTRL2_REG (0x05)
+#define AW88399_I2SCTRL1_REG (0x06)
+#define AW88399_I2SCTRL2_REG (0x07)
+#define AW88399_I2SCTRL3_REG (0x08)
+#define AW88399_DACCFG1_REG (0x09)
+#define AW88399_DACCFG2_REG (0x0A)
+#define AW88399_DACCFG3_REG (0x0B)
+#define AW88399_DACCFG4_REG (0x0C)
+#define AW88399_DACCFG5_REG (0x0D)
+#define AW88399_DACCFG6_REG (0x0E)
+#define AW88399_DACCFG7_REG (0x0F)
+#define AW88399_MPDCFG1_REG (0x10)
+#define AW88399_MPDCFG2_REG (0x11)
+#define AW88399_MPDCFG3_REG (0x12)
+#define AW88399_MPDCFG4_REG (0x13)
+#define AW88399_PWMCTRL1_REG (0x14)
+#define AW88399_PWMCTRL2_REG (0x15)
+#define AW88399_PWMCTRL3_REG (0x16)
+#define AW88399_I2SCFG1_REG (0x17)
+#define AW88399_DBGCTRL_REG (0x18)
+#define AW88399_HAGCST_REG (0x20)
+#define AW88399_VBAT_REG (0x21)
+#define AW88399_TEMP_REG (0x22)
+#define AW88399_PVDD_REG (0x23)
+#define AW88399_ISNDAT_REG (0x24)
+#define AW88399_VSNDAT_REG (0x25)
+#define AW88399_I2SINT_REG (0x26)
+#define AW88399_I2SCAPCNT_REG (0x27)
+#define AW88399_ANASTA1_REG (0x28)
+#define AW88399_ANASTA2_REG (0x29)
+#define AW88399_ANASTA3_REG (0x2A)
+#define AW88399_TESTDET_REG (0x2B)
+#define AW88399_DSMCFG1_REG (0x30)
+#define AW88399_DSMCFG2_REG (0x31)
+#define AW88399_DSMCFG3_REG (0x32)
+#define AW88399_DSMCFG4_REG (0x33)
+#define AW88399_DSMCFG5_REG (0x34)
+#define AW88399_DSMCFG6_REG (0x35)
+#define AW88399_DSMCFG7_REG (0x36)
+#define AW88399_DSMCFG8_REG (0x37)
+#define AW88399_TESTIN_REG (0x38)
+#define AW88399_TESTOUT_REG (0x39)
+#define AW88399_MEMTEST_REG (0x3A)
+#define AW88399_VSNCTRL1_REG (0x3B)
+#define AW88399_ISNCTRL1_REG (0x3C)
+#define AW88399_ISNCTRL2_REG (0x3D)
+#define AW88399_DSPMADD_REG (0x40)
+#define AW88399_DSPMDAT_REG (0x41)
+#define AW88399_WDT_REG (0x42)
+#define AW88399_ACR1_REG (0x43)
+#define AW88399_ACR2_REG (0x44)
+#define AW88399_ASR1_REG (0x45)
+#define AW88399_ASR2_REG (0x46)
+#define AW88399_DSPCFG_REG (0x47)
+#define AW88399_ASR3_REG (0x48)
+#define AW88399_ASR4_REG (0x49)
+#define AW88399_DSPVCALB_REG (0x4A)
+#define AW88399_CRCCTRL_REG (0x4B)
+#define AW88399_DSPDBG1_REG (0x4C)
+#define AW88399_DSPDBG2_REG (0x4D)
+#define AW88399_DSPDBG3_REG (0x4E)
+#define AW88399_PLLCTRL1_REG (0x50)
+#define AW88399_PLLCTRL2_REG (0x51)
+#define AW88399_PLLCTRL3_REG (0x52)
+#define AW88399_CDACTRL1_REG (0x53)
+#define AW88399_CDACTRL2_REG (0x54)
+#define AW88399_CDACTRL3_REG (0x55)
+#define AW88399_SADCCTRL1_REG (0x56)
+#define AW88399_SADCCTRL2_REG (0x57)
+#define AW88399_BOPCTRL1_REG (0x58)
+#define AW88399_BOPCTRL2_REG (0x5A)
+#define AW88399_BOPCTRL3_REG (0x5B)
+#define AW88399_BOPCTRL4_REG (0x5C)
+#define AW88399_BOPCTRL5_REG (0x5D)
+#define AW88399_BOPCTRL6_REG (0x5E)
+#define AW88399_BOPCTRL7_REG (0x5F)
+#define AW88399_BSTCTRL1_REG (0x60)
+#define AW88399_BSTCTRL2_REG (0x61)
+#define AW88399_BSTCTRL3_REG (0x62)
+#define AW88399_BSTCTRL4_REG (0x63)
+#define AW88399_BSTCTRL5_REG (0x64)
+#define AW88399_BSTCTRL6_REG (0x65)
+#define AW88399_BSTCTRL7_REG (0x66)
+#define AW88399_BSTCTRL8_REG (0x67)
+#define AW88399_BSTCTRL9_REG (0x68)
+#define AW88399_BSTCTRL10_REG (0x69)
+#define AW88399_CPCTRL_REG (0x6A)
+#define AW88399_EFWH_REG (0x6C)
+#define AW88399_EFWM2_REG (0x6D)
+#define AW88399_EFWM1_REG (0x6E)
+#define AW88399_EFWL_REG (0x6F)
+#define AW88399_TESTCTRL1_REG (0x70)
+#define AW88399_TESTCTRL2_REG (0x71)
+#define AW88399_EFCTRL1_REG (0x72)
+#define AW88399_EFCTRL2_REG (0x73)
+#define AW88399_EFRH4_REG (0x74)
+#define AW88399_EFRH3_REG (0x75)
+#define AW88399_EFRH2_REG (0x76)
+#define AW88399_EFRH1_REG (0x77)
+#define AW88399_EFRL4_REG (0x78)
+#define AW88399_EFRL3_REG (0x79)
+#define AW88399_EFRL2_REG (0x7A)
+#define AW88399_EFRL1_REG (0x7B)
+#define AW88399_TM_REG (0x7C)
+#define AW88399_TM2_REG (0x7D)
+
+#define AW88399_REG_MAX (0x7E)
+#define AW88399_MUTE_VOL (1023)
+
+#define AW88399_DSP_CFG_ADDR (0x9B00)
+#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68)
+#define AW88399_DSP_FW_ADDR (0x8980)
+#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40)
+#define AW88399_DSP_ROM_CHECK_DATA (0x4638)
+
+#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000))
+#define AW88399_CALI_RE_HBITS_SHIFT (16)
+
+#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF))
+#define AW88399_CALI_RE_LBITS_SHIFT (0)
+
+#define AW88399_I2STXEN_START_BIT (9)
+#define AW88399_I2STXEN_BITS_LEN (1)
+#define AW88399_I2STXEN_MASK \
+ (~(((1<<AW88399_I2STXEN_BITS_LEN)-1) << AW88399_I2STXEN_START_BIT))
+
+#define AW88399_I2STXEN_DISABLE (0)
+#define AW88399_I2STXEN_DISABLE_VALUE \
+ (AW88399_I2STXEN_DISABLE << AW88399_I2STXEN_START_BIT)
+
+#define AW88399_I2STXEN_ENABLE (1)
+#define AW88399_I2STXEN_ENABLE_VALUE \
+ (AW88399_I2STXEN_ENABLE << AW88399_I2STXEN_START_BIT)
+
+#define AW88399_VOL_START_BIT (0)
+#define AW88399_VOL_BITS_LEN (10)
+#define AW88399_VOL_MASK \
+ (~(((1<<AW88399_VOL_BITS_LEN)-1) << AW88399_VOL_START_BIT))
+
+#define AW88399_PWDN_START_BIT (0)
+#define AW88399_PWDN_BITS_LEN (1)
+#define AW88399_PWDN_MASK \
+ (~(((1<<AW88399_PWDN_BITS_LEN)-1) << AW88399_PWDN_START_BIT))
+
+#define AW88399_PWDN_POWER_DOWN (1)
+#define AW88399_PWDN_POWER_DOWN_VALUE \
+ (AW88399_PWDN_POWER_DOWN << AW88399_PWDN_START_BIT)
+
+#define AW88399_PWDN_WORKING (0)
+#define AW88399_PWDN_WORKING_VALUE \
+ (AW88399_PWDN_WORKING << AW88399_PWDN_START_BIT)
+
+#define AW88399_DSPBY_START_BIT (2)
+#define AW88399_DSPBY_BITS_LEN (1)
+#define AW88399_DSPBY_MASK \
+ (~(((1<<AW88399_DSPBY_BITS_LEN)-1) << AW88399_DSPBY_START_BIT))
+
+#define AW88399_DSPBY_WORKING (0)
+#define AW88399_DSPBY_WORKING_VALUE \
+ (AW88399_DSPBY_WORKING << AW88399_DSPBY_START_BIT)
+
+#define AW88399_DSPBY_BYPASS (1)
+#define AW88399_DSPBY_BYPASS_VALUE \
+ (AW88399_DSPBY_BYPASS << AW88399_DSPBY_START_BIT)
+
+#define AW88399_MEM_CLKSEL_START_BIT (3)
+#define AW88399_MEM_CLKSEL_BITS_LEN (1)
+#define AW88399_MEM_CLKSEL_MASK \
+ (~(((1<<AW88399_MEM_CLKSEL_BITS_LEN)-1) << AW88399_MEM_CLKSEL_START_BIT))
+
+#define AW88399_MEM_CLKSEL_OSCCLK (0)
+#define AW88399_MEM_CLKSEL_OSCCLK_VALUE \
+ (AW88399_MEM_CLKSEL_OSCCLK << AW88399_MEM_CLKSEL_START_BIT)
+
+#define AW88399_MEM_CLKSEL_DAPHCLK (1)
+#define AW88399_MEM_CLKSEL_DAPHCLK_VALUE \
+ (AW88399_MEM_CLKSEL_DAPHCLK << AW88399_MEM_CLKSEL_START_BIT)
+
+#define AW88399_DITHER_EN_START_BIT (15)
+#define AW88399_DITHER_EN_BITS_LEN (1)
+#define AW88399_DITHER_EN_MASK \
+ (~(((1<<AW88399_DITHER_EN_BITS_LEN)-1) << AW88399_DITHER_EN_START_BIT))
+
+#define AW88399_DITHER_EN_DISABLE (0)
+#define AW88399_DITHER_EN_DISABLE_VALUE \
+ (AW88399_DITHER_EN_DISABLE << AW88399_DITHER_EN_START_BIT)
+
+#define AW88399_DITHER_EN_ENABLE (1)
+#define AW88399_DITHER_EN_ENABLE_VALUE \
+ (AW88399_DITHER_EN_ENABLE << AW88399_DITHER_EN_START_BIT)
+
+#define AW88399_HMUTE_START_BIT (8)
+#define AW88399_HMUTE_BITS_LEN (1)
+#define AW88399_HMUTE_MASK \
+ (~(((1<<AW88399_HMUTE_BITS_LEN)-1) << AW88399_HMUTE_START_BIT))
+
+#define AW88399_HMUTE_DISABLE (0)
+#define AW88399_HMUTE_DISABLE_VALUE \
+ (AW88399_HMUTE_DISABLE << AW88399_HMUTE_START_BIT)
+
+#define AW88399_HMUTE_ENABLE (1)
+#define AW88399_HMUTE_ENABLE_VALUE \
+ (AW88399_HMUTE_ENABLE << AW88399_HMUTE_START_BIT)
+
+#define AW88399_EF_DBMD_START_BIT (2)
+#define AW88399_EF_DBMD_BITS_LEN (1)
+#define AW88399_EF_DBMD_MASK \
+ (~(((1<<AW88399_EF_DBMD_BITS_LEN)-1) << AW88399_EF_DBMD_START_BIT))
+
+#define AW88399_EF_DBMD_OR (1)
+#define AW88399_EF_DBMD_OR_VALUE \
+ (AW88399_EF_DBMD_OR << AW88399_EF_DBMD_START_BIT)
+
+#define AW88399_VDSEL_START_BIT (5)
+#define AW88399_VDSEL_BITS_LEN (1)
+#define AW88399_VDSEL_MASK \
+ (~(((1<<AW88399_VDSEL_BITS_LEN)-1) << AW88399_VDSEL_START_BIT))
+
+#define AW88399_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88399_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88399_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88399_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88399_EF_ISN_GESLP_H_START_BIT))
+
+/* EF_VSN_GESLP_H bit 9:0 (EFRH3 0x75) */
+#define AW88399_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88399_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88399_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88399_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88399_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88399_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88399_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88399_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88399_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88399_EF_ISN_GESLP_L_START_BIT))
+
+/* EF_VSN_GESLP_L bit 9:0 (EFRL3 0x79) */
+#define AW88399_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88399_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88399_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88399_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88399_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88399_INTERNAL_VSN_TRIM_H_START_BIT (9)
+#define AW88399_INTERNAL_VSN_TRIM_H_BITS_LEN (6)
+#define AW88399_INTERNAL_VSN_TRIM_H_MASK \
+ (~(((1<<AW88399_INTERNAL_VSN_TRIM_H_BITS_LEN)-1) << AW88399_INTERNAL_VSN_TRIM_H_START_BIT))
+
+#define AW88399_INTERNAL_VSN_TRIM_L_START_BIT (9)
+#define AW88399_INTERNAL_VSN_TRIM_L_BITS_LEN (6)
+#define AW88399_INTERNAL_VSN_TRIM_L_MASK \
+ (~(((1<<AW88399_INTERNAL_VSN_TRIM_L_BITS_LEN)-1) << AW88399_INTERNAL_VSN_TRIM_L_START_BIT))
+
+#define AW88399_RCV_MODE_START_BIT (7)
+#define AW88399_RCV_MODE_BITS_LEN (1)
+#define AW88399_RCV_MODE_MASK \
+ (~(((1<<AW88399_RCV_MODE_BITS_LEN)-1) << AW88399_RCV_MODE_START_BIT))
+
+#define AW88399_CLKI_START_BIT (4)
+#define AW88399_NOCLKI_START_BIT (5)
+#define AW88399_PLLI_START_BIT (0)
+#define AW88399_PLLI_INT_VALUE (1)
+#define AW88399_PLLI_INT_INTERRUPT \
+ (AW88399_PLLI_INT_VALUE << AW88399_PLLI_START_BIT)
+
+#define AW88399_CLKI_INT_VALUE (1)
+#define AW88399_CLKI_INT_INTERRUPT \
+ (AW88399_CLKI_INT_VALUE << AW88399_CLKI_START_BIT)
+
+#define AW88399_NOCLKI_INT_VALUE (1)
+#define AW88399_NOCLKI_INT_INTERRUPT \
+ (AW88399_NOCLKI_INT_VALUE << AW88399_NOCLKI_START_BIT)
+
+#define AW88399_BIT_SYSINT_CHECK \
+ (AW88399_PLLI_INT_INTERRUPT | \
+ AW88399_CLKI_INT_INTERRUPT | \
+ AW88399_NOCLKI_INT_INTERRUPT)
+
+#define AW88399_CRC_CHECK_START_BIT (12)
+#define AW88399_CRC_CHECK_BITS_LEN (3)
+#define AW88399_CRC_CHECK_BITS_MASK \
+ (~(((1<<AW88399_CRC_CHECK_BITS_LEN)-1) << AW88399_CRC_CHECK_START_BIT))
+
+#define AW88399_RCV_MODE_RECEIVER (1)
+#define AW88399_RCV_MODE_RECEIVER_VALUE \
+ (AW88399_RCV_MODE_RECEIVER << AW88399_RCV_MODE_START_BIT)
+
+#define AW88399_AMPPD_START_BIT (1)
+#define AW88399_AMPPD_BITS_LEN (1)
+#define AW88399_AMPPD_MASK \
+ (~(((1<<AW88399_AMPPD_BITS_LEN)-1) << AW88399_AMPPD_START_BIT))
+
+#define AW88399_AMPPD_WORKING (0)
+#define AW88399_AMPPD_WORKING_VALUE \
+ (AW88399_AMPPD_WORKING << AW88399_AMPPD_START_BIT)
+
+#define AW88399_AMPPD_POWER_DOWN (1)
+#define AW88399_AMPPD_POWER_DOWN_VALUE \
+ (AW88399_AMPPD_POWER_DOWN << AW88399_AMPPD_START_BIT)
+
+#define AW88399_RAM_CG_BYP_START_BIT (0)
+#define AW88399_RAM_CG_BYP_BITS_LEN (1)
+#define AW88399_RAM_CG_BYP_MASK \
+ (~(((1<<AW88399_RAM_CG_BYP_BITS_LEN)-1) << AW88399_RAM_CG_BYP_START_BIT))
+
+#define AW88399_RAM_CG_BYP_WORK (0)
+#define AW88399_RAM_CG_BYP_WORK_VALUE \
+ (AW88399_RAM_CG_BYP_WORK << AW88399_RAM_CG_BYP_START_BIT)
+
+#define AW88399_RAM_CG_BYP_BYPASS (1)
+#define AW88399_RAM_CG_BYP_BYPASS_VALUE \
+ (AW88399_RAM_CG_BYP_BYPASS << AW88399_RAM_CG_BYP_START_BIT)
+
+#define AW88399_CRC_END_ADDR_START_BIT (0)
+#define AW88399_CRC_END_ADDR_BITS_LEN (12)
+#define AW88399_CRC_END_ADDR_MASK \
+ (~(((1<<AW88399_CRC_END_ADDR_BITS_LEN)-1) << AW88399_CRC_END_ADDR_START_BIT))
+
+#define AW88399_CRC_CODE_EN_START_BIT (13)
+#define AW88399_CRC_CODE_EN_BITS_LEN (1)
+#define AW88399_CRC_CODE_EN_MASK \
+ (~(((1<<AW88399_CRC_CODE_EN_BITS_LEN)-1) << AW88399_CRC_CODE_EN_START_BIT))
+
+#define AW88399_CRC_CODE_EN_DISABLE (0)
+#define AW88399_CRC_CODE_EN_DISABLE_VALUE \
+ (AW88399_CRC_CODE_EN_DISABLE << AW88399_CRC_CODE_EN_START_BIT)
+
+#define AW88399_CRC_CODE_EN_ENABLE (1)
+#define AW88399_CRC_CODE_EN_ENABLE_VALUE \
+ (AW88399_CRC_CODE_EN_ENABLE << AW88399_CRC_CODE_EN_START_BIT)
+
+#define AW88399_CRC_CFG_EN_START_BIT (12)
+#define AW88399_CRC_CFG_EN_BITS_LEN (1)
+#define AW88399_CRC_CFG_EN_MASK \
+ (~(((1<<AW88399_CRC_CFG_EN_BITS_LEN)-1) << AW88399_CRC_CFG_EN_START_BIT))
+
+#define AW88399_CRC_CFG_EN_DISABLE (0)
+#define AW88399_CRC_CFG_EN_DISABLE_VALUE \
+ (AW88399_CRC_CFG_EN_DISABLE << AW88399_CRC_CFG_EN_START_BIT)
+
+#define AW88399_CRC_CFG_EN_ENABLE (1)
+#define AW88399_CRC_CFG_EN_ENABLE_VALUE \
+ (AW88399_CRC_CFG_EN_ENABLE << AW88399_CRC_CFG_EN_START_BIT)
+
+#define AW88399_OCDS_START_BIT (3)
+#define AW88399_OCDS_OC (1)
+#define AW88399_OCDS_OC_VALUE \
+ (AW88399_OCDS_OC << AW88399_OCDS_START_BIT)
+
+#define AW88399_NOCLKS_START_BIT (5)
+#define AW88399_NOCLKS_NO_CLOCK (1)
+#define AW88399_NOCLKS_NO_CLOCK_VALUE \
+ (AW88399_NOCLKS_NO_CLOCK << AW88399_NOCLKS_START_BIT)
+
+#define AW88399_SWS_START_BIT (8)
+#define AW88399_SWS_SWITCHING (1)
+#define AW88399_SWS_SWITCHING_VALUE \
+ (AW88399_SWS_SWITCHING << AW88399_SWS_START_BIT)
+
+#define AW88399_BSTS_START_BIT (9)
+#define AW88399_BSTS_FINISHED (1)
+#define AW88399_BSTS_FINISHED_VALUE \
+ (AW88399_BSTS_FINISHED << AW88399_BSTS_START_BIT)
+
+#define AW88399_UVLS_START_BIT (14)
+#define AW88399_UVLS_NORMAL (0)
+#define AW88399_UVLS_NORMAL_VALUE \
+ (AW88399_UVLS_NORMAL << AW88399_UVLS_START_BIT)
+
+#define AW88399_BSTOCS_START_BIT (11)
+#define AW88399_BSTOCS_OVER_CURRENT (1)
+#define AW88399_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88399_BSTOCS_OVER_CURRENT << AW88399_BSTOCS_START_BIT)
+
+#define AW88399_OTHS_START_BIT (1)
+#define AW88399_OTHS_OT (1)
+#define AW88399_OTHS_OT_VALUE \
+ (AW88399_OTHS_OT << AW88399_OTHS_START_BIT)
+
+#define AW88399_PLLS_START_BIT (0)
+#define AW88399_PLLS_LOCKED (1)
+#define AW88399_PLLS_LOCKED_VALUE \
+ (AW88399_PLLS_LOCKED << AW88399_PLLS_START_BIT)
+
+#define AW88399_CLKS_START_BIT (4)
+#define AW88399_CLKS_STABLE (1)
+#define AW88399_CLKS_STABLE_VALUE \
+ (AW88399_CLKS_STABLE << AW88399_CLKS_START_BIT)
+
+#define AW88399_BIT_PLL_CHECK \
+ (AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE)
+
+#define AW88399_BIT_SYSST_CHECK_MASK \
+ (~(AW88399_UVLS_NORMAL_VALUE | \
+ AW88399_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_SWS_SWITCHING_VALUE | \
+ AW88399_NOCLKS_NO_CLOCK_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_OCDS_OC_VALUE | \
+ AW88399_OTHS_OT_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE))
+
+#define AW88399_BIT_SYSST_NOSWS_CHECK \
+ (AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE)
+
+#define AW88399_BIT_SYSST_SWS_CHECK \
+ (AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE | \
+ AW88399_SWS_SWITCHING_VALUE)
+
+#define AW88399_CCO_MUX_START_BIT (14)
+#define AW88399_CCO_MUX_BITS_LEN (1)
+#define AW88399_CCO_MUX_MASK \
+ (~(((1<<AW88399_CCO_MUX_BITS_LEN)-1) << AW88399_CCO_MUX_START_BIT))
+
+#define AW88399_CCO_MUX_DIVIDED (0)
+#define AW88399_CCO_MUX_DIVIDED_VALUE \
+ (AW88399_CCO_MUX_DIVIDED << AW88399_CCO_MUX_START_BIT)
+
+#define AW88399_CCO_MUX_BYPASS (1)
+#define AW88399_CCO_MUX_BYPASS_VALUE \
+ (AW88399_CCO_MUX_BYPASS << AW88399_CCO_MUX_START_BIT)
+
+#define AW88399_NOISE_GATE_EN_START_BIT (13)
+#define AW88399_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88399_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88399_NOISE_GATE_EN_BITS_LEN)-1) << AW88399_NOISE_GATE_EN_START_BIT))
+
+#define AW88399_WDT_CNT_START_BIT (0)
+#define AW88399_WDT_CNT_BITS_LEN (8)
+#define AW88399_WDT_CNT_MASK \
+ (~(((1<<AW88399_WDT_CNT_BITS_LEN)-1) << AW88399_WDT_CNT_START_BIT))
+
+#define AW88399_REABS_START_BIT (3)
+#define AW88399_REABS_BITS_LEN (1)
+#define AW88399_REABS_MASK \
+ (~(((1<<AW88399_REABS_BITS_LEN)-1) << AW88399_REABS_START_BIT))
+
+#define AW88399_DSP_VOL_START_BIT (8)
+#define AW88399_DSP_VOL_BITS_LEN (8)
+#define AW88399_DSP_VOL_MASK \
+ (~(((1<<AW88399_DSP_VOL_BITS_LEN)-1) << AW88399_DSP_VOL_START_BIT))
+
+#define AW883XX_DSP_NG_EN_START (13)
+#define AW883XX_DSP_NG_EN_LEN (1)
+#define AW883XX_DSP_NG_EN_MASK \
+ (~(((1 << AW883XX_DSP_NG_EN_LEN) - 1) << AW883XX_DSP_NG_EN_START))
+#define AW883XX_DSP_NG_EN_DISABLE (0)
+#define AW883XX_DSP_NG_EN_DISABLE_VALUE \
+ (AW883XX_DSP_NG_EN_DISABLE << AW883XX_DSP_NG_EN_START)
+
+#define AW88399_VOLUME_STEP_DB (64)
+#define AW88399_VOL_DEFAULT_VALUE (0)
+#define AW88399_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88399_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88399_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88399_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88399_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88399_TEM4_SIGN_MASK (~(1 << 5))
+#define AW88399_TEM4_SIGN_NEG (0xffc0)
+
+#define AW88399_ICABLK_FACTOR (1)
+#define AW88399_VCABLK_FACTOR (1)
+#define AW88399_VCABLK_DAC_FACTOR (2)
+
+#define AW88399_VCALB_ADJ_FACTOR (12)
+#define AW88399_VCALB_ACCURACY (1 << 12)
+
+#define AW88399_ISCAL_FACTOR (3125)
+#define AW88399_VSCAL_FACTOR (18875)
+#define AW88399_ISCAL_DAC_FACTOR (3125)
+#define AW88399_VSCAL_DAC_FACTOR (12600)
+#define AW88399_CABL_BASE_VALUE (1000)
+
+#define AW88399_DEV_DEFAULT_CH (0)
+#define AW88399_DEV_DSP_CHECK_MAX (5)
+#define AW88399_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW88399_DSP_RE_SHIFT (12)
+#define AW88399_CALI_RE_MAX (15000)
+#define AW88399_CALI_RE_MIN (4000)
+#define AW_FW_ADDR_LEN (4)
+#define AW88399_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+#define AW88399_CRC_CHECK_PASS_VAL (0x4)
+
+#define AW88399_CRC_CFG_BASE_ADDR (0xD80)
+#define AW88399_CRC_FW_BASE_ADDR (0x4C0)
+#define AW88399_ACF_FILE "aw88399_acf.bin"
+#define AW88399_DEV_SYSST_CHECK_MAX (10)
+#define AW88399_CHIP_ID 0x2183
+
+#define AW88399_I2C_NAME "aw88399"
+
+#define AW88399_START_RETRIES (5)
+#define AW88399_START_WORK_DELAY_MS (0)
+
+#define AW88399_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88399_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+
+#define AW_CALI_READ_CNT_MAX (8)
+#define AW88399_DSP_REG_CALRE (0x8141)
+#define AW88399_DSP_REG_CALRE_SHIFT (10)
+#define AW_CALI_DATA_SUM_RM (2)
+
+#define AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH (0x9B4C)
+#define AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH (0x9B4E)
+#define AW88399_DSP_REG_CFG_ADPZ_USTEPN (0x9B6E)
+#define AW88399_DSP_REG_CFG_RE_ALPHA (0x9BD4)
+#define AW_GET_IV_CNT_MAX (6)
+
+#define AW88399_DSP_VOL_MUTE (0XFF00)
+
+#define AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR (0x9BEC)
+#define AW88399_DSP_LOW_POWER_SWITCH_DISABLE (0x110b)
+
+#define AW88399_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW_EF_AND_CHECK = 0,
+ AW_EF_OR_CHECK,
+};
+
+enum {
+ AW88399_DEV_VDSEL_DAC = 0,
+ AW88399_DEV_VDSEL_VSENSE = 32,
+};
+
+enum {
+ AW88399_DSP_CRC_NA = 0,
+ AW88399_DSP_CRC_OK = 1,
+};
+
+enum {
+ AW88399_DSP_FW_UPDATE_OFF = 0,
+ AW88399_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88399_FORCE_UPDATE_OFF = 0,
+ AW88399_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88399_1000_US = 1000,
+ AW88399_2000_US = 2000,
+ AW88399_3000_US = 3000,
+ AW88399_4000_US = 4000,
+};
+
+enum AW88399_DEV_STATUS {
+ AW88399_DEV_PW_OFF = 0,
+ AW88399_DEV_PW_ON,
+};
+
+enum AW88399_DEV_FW_STATUS {
+ AW88399_DEV_FW_FAILED = 0,
+ AW88399_DEV_FW_OK,
+};
+
+enum AW88399_DEV_MEMCLK {
+ AW88399_DEV_MEMCLK_OSC = 0,
+ AW88399_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88399_DEV_DSP_CFG {
+ AW88399_DEV_DSP_WORK = 0,
+ AW88399_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88399_NOT_RCV_MODE = 0,
+ AW88399_RCV_MODE = 1,
+};
+
+enum {
+ AW88399_SYNC_START = 0,
+ AW88399_ASYNC_START,
+};
+
+struct aw88399 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+
+ unsigned int check_val;
+ unsigned int crc_init_val;
+ unsigned int vcalb_init_val;
+ unsigned int dither_st;
+};
+
+#endif
diff --git a/sound/soc/codecs/bd28623.c b/sound/soc/codecs/bd28623.c
new file mode 100644
index 000000000000..07e7bd79c8b0
--- /dev/null
+++ b/sound/soc/codecs/bd28623.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ROHM BD28623MUV class D speaker amplifier codec driver.
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define BD28623_NUM_SUPPLIES 3
+
+static const char *const bd28623_supply_names[BD28623_NUM_SUPPLIES] = {
+ "VCCA",
+ "VCCP1",
+ "VCCP2",
+};
+
+struct bd28623_priv {
+ struct device *dev;
+ struct regulator_bulk_data supplies[BD28623_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *mute_gpio;
+
+ int switch_spk;
+};
+
+static const struct snd_soc_dapm_widget bd28623_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT1P"),
+ SND_SOC_DAPM_OUTPUT("OUT1N"),
+ SND_SOC_DAPM_OUTPUT("OUT2P"),
+ SND_SOC_DAPM_OUTPUT("OUT2N"),
+};
+
+static const struct snd_soc_dapm_route bd28623_routes[] = {
+ { "OUT1P", NULL, "DAC" },
+ { "OUT1N", NULL, "DAC" },
+ { "OUT2P", NULL, "DAC" },
+ { "OUT2N", NULL, "DAC" },
+};
+
+static int bd28623_power_on(struct bd28623_priv *bd)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(bd->supplies), bd->supplies);
+ if (ret) {
+ dev_err(bd->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(bd->reset_gpio, 0);
+ usleep_range(300000, 400000);
+
+ return 0;
+}
+
+static void bd28623_power_off(struct bd28623_priv *bd)
+{
+ gpiod_set_value_cansleep(bd->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(bd->supplies), bd->supplies);
+}
+
+static int bd28623_get_switch_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = bd->switch_spk;
+
+ return 0;
+}
+
+static int bd28623_set_switch_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ if (bd->switch_spk == ucontrol->value.integer.value[0])
+ return 0;
+
+ bd->switch_spk = ucontrol->value.integer.value[0];
+
+ gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new bd28623_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Speaker Switch", 0,
+ bd28623_get_switch_spk, bd28623_set_switch_spk),
+};
+
+static int bd28623_codec_probe(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ bd->switch_spk = 1;
+
+ ret = bd28623_power_on(bd);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
+
+ return 0;
+}
+
+static void bd28623_codec_remove(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ bd28623_power_off(bd);
+}
+
+static int bd28623_codec_suspend(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+
+ bd28623_power_off(bd);
+
+ return 0;
+}
+
+static int bd28623_codec_resume(struct snd_soc_component *component)
+{
+ struct bd28623_priv *bd = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = bd28623_power_on(bd);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_bd = {
+ .probe = bd28623_codec_probe,
+ .remove = bd28623_codec_remove,
+ .suspend = bd28623_codec_suspend,
+ .resume = bd28623_codec_resume,
+ .dapm_widgets = bd28623_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bd28623_widgets),
+ .dapm_routes = bd28623_routes,
+ .num_dapm_routes = ARRAY_SIZE(bd28623_routes),
+ .controls = bd28623_controls,
+ .num_controls = ARRAY_SIZE(bd28623_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver soc_dai_bd = {
+ .name = "bd28623-speaker",
+ .playback = {
+ .stream_name = "Playback",
+ .formats = SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_32000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+};
+
+static int bd28623_probe(struct platform_device *pdev)
+{
+ struct bd28623_priv *bd;
+ struct device *dev = &pdev->dev;
+ int i, ret;
+
+ bd = devm_kzalloc(&pdev->dev, sizeof(struct bd28623_priv), GFP_KERNEL);
+ if (!bd)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(bd->supplies); i++)
+ bd->supplies[i].supply = bd28623_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(bd->supplies),
+ bd->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ bd->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(bd->reset_gpio)) {
+ dev_err(dev, "Failed to request reset_gpio: %ld\n",
+ PTR_ERR(bd->reset_gpio));
+ return PTR_ERR(bd->reset_gpio);
+ }
+
+ bd->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(bd->mute_gpio)) {
+ dev_err(dev, "Failed to request mute_gpio: %ld\n",
+ PTR_ERR(bd->mute_gpio));
+ return PTR_ERR(bd->mute_gpio);
+ }
+
+ platform_set_drvdata(pdev, bd);
+ bd->dev = dev;
+
+ return devm_snd_soc_register_component(dev, &soc_codec_bd,
+ &soc_dai_bd, 1);
+}
+
+static const struct of_device_id bd28623_of_match[] __maybe_unused = {
+ { .compatible = "rohm,bd28623", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bd28623_of_match);
+
+static struct platform_driver bd28623_codec_driver = {
+ .driver = {
+ .name = "bd28623",
+ .of_match_table = of_match_ptr(bd28623_of_match),
+ },
+ .probe = bd28623_probe,
+};
+module_platform_driver(bd28623_codec_driver);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("ROHM BD28623 speaker amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad113e96..3afcef2dfa35 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -1,12 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic Bluetooth SCO link
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/init.h>
@@ -18,49 +13,69 @@
static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
SND_SOC_DAPM_INPUT("RX"),
SND_SOC_DAPM_OUTPUT("TX"),
+ SND_SOC_DAPM_AIF_IN("BT_SCO_RX", "Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0,
+ SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_dapm_route bt_sco_routes[] = {
- { "Capture", NULL, "RX" },
- { "TX", NULL, "Playback" },
+ { "BT_SCO_TX", NULL, "RX" },
+ { "TX", NULL, "BT_SCO_RX" },
};
-static struct snd_soc_dai_driver bt_sco_dai = {
- .name = "bt-sco-pcm",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+static struct snd_soc_dai_driver bt_sco_dai[] = {
+ {
+ .name = "bt-sco-pcm",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
},
+ {
+ .name = "bt-sco-pcm-wb",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ }
};
-static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
- .dapm_widgets = bt_sco_widgets,
- .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
- .dapm_routes = bt_sco_routes,
- .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+static const struct snd_soc_component_driver soc_component_dev_bt_sco = {
+ .dapm_widgets = bt_sco_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
+ .dapm_routes = bt_sco_routes,
+ .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int bt_sco_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
- &bt_sco_dai, 1);
-}
-
-static int bt_sco_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_bt_sco,
+ bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
}
static const struct platform_device_id bt_sco_driver_ids[] = {
@@ -77,6 +92,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
#if defined(CONFIG_OF)
static const struct of_device_id bt_sco_codec_of_match[] = {
{ .compatible = "delta,dfbmcs320", },
+ { .compatible = "linux,bt-sco", },
{},
};
MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
@@ -88,7 +104,6 @@ static struct platform_driver bt_sco_driver = {
.of_match_table = of_match_ptr(bt_sco_codec_of_match),
},
.probe = bt_sco_probe,
- .remove = bt_sco_remove,
.id_table = bt_sco_driver_ids,
};
diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c
new file mode 100644
index 000000000000..40020500b1fe
--- /dev/null
+++ b/sound/soc/codecs/chv3-codec.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver chv3_codec_dai = {
+ .name = "chv3-codec-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_chv3_codec = {
+};
+
+static int chv3_codec_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_chv3_codec, &chv3_codec_dai, 1);
+}
+
+static const struct of_device_id chv3_codec_of_match[] = {
+ { .compatible = "google,chv3-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, chv3_codec_of_match);
+
+static struct platform_driver chv3_codec_platform_driver = {
+ .driver = {
+ .name = "chv3-codec",
+ .of_match_table = chv3_codec_of_match,
+ },
+ .probe = chv3_codec_probe,
+};
+module_platform_driver(chv3_codec_platform_driver);
+
+MODULE_DESCRIPTION("ASoC Chameleon v3 codec driver");
+MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cirrus_legacy.h b/sound/soc/codecs/cirrus_legacy.h
new file mode 100644
index 000000000000..87c6fd79290d
--- /dev/null
+++ b/sound/soc/codecs/cirrus_legacy.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Some small helpers for older Cirrus Logic parts.
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+static inline int cirrus_read_device_id(struct regmap *regmap, unsigned int reg)
+{
+ u8 devid[3];
+ int ret;
+
+ ret = regmap_bulk_read(regmap, reg, devid, ARRAY_SIZE(devid));
+ if (ret < 0)
+ return ret;
+
+ return ((devid[0] & 0xFF) << 12) |
+ ((devid[1] & 0xFF) << 4) |
+ ((devid[2] & 0xF0) >> 4);
+}
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c
new file mode 100644
index 000000000000..6b80c455b074
--- /dev/null
+++ b/sound/soc/codecs/cpcap.c
@@ -0,0 +1,1884 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC CPCAP codec driver
+ *
+ * Copyright (C) 2017 - 2018 Sebastian Reichel <sre@kernel.org>
+ *
+ * Very loosely based on original driver from Motorola:
+ * Copyright (C) 2007 - 2009 Motorola, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <sound/core.h>
+#include <linux/input.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Register 8 - CPCAP_REG_INTS1 --- Interrupt Sense 1 */
+#define CPCAP_BIT_HS_S 9 /* Headset */
+#define CPCAP_BIT_MB2_S 10 /* Mic Bias2 */
+
+/* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */
+#define CPCAP_BIT_PTT_S 11 /* Push To Talk */
+
+/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */
+#define CPCAP_BIT_AUDIO_LOW_PWR 6
+#define CPCAP_BIT_AUD_LOWPWR_SPEED 5
+#define CPCAP_BIT_VAUDIOPRISTBY 4
+#define CPCAP_BIT_VAUDIO_MODE1 2
+#define CPCAP_BIT_VAUDIO_MODE0 1
+#define CPCAP_BIT_V_AUDIO_EN 0
+
+/* Register 513 CPCAP_REG_CC --- CODEC */
+#define CPCAP_BIT_CDC_CLK2 15
+#define CPCAP_BIT_CDC_CLK1 14
+#define CPCAP_BIT_CDC_CLK0 13
+#define CPCAP_BIT_CDC_SR3 12
+#define CPCAP_BIT_CDC_SR2 11
+#define CPCAP_BIT_CDC_SR1 10
+#define CPCAP_BIT_CDC_SR0 9
+#define CPCAP_BIT_CDC_CLOCK_TREE_RESET 8
+#define CPCAP_BIT_MIC2_CDC_EN 7
+#define CPCAP_BIT_CDC_EN_RX 6
+#define CPCAP_BIT_DF_RESET 5
+#define CPCAP_BIT_MIC1_CDC_EN 4
+#define CPCAP_BIT_AUDOHPF_1 3
+#define CPCAP_BIT_AUDOHPF_0 2
+#define CPCAP_BIT_AUDIHPF_1 1
+#define CPCAP_BIT_AUDIHPF_0 0
+
+/* Register 514 CPCAP_REG_CDI --- CODEC Digital Audio Interface */
+#define CPCAP_BIT_CDC_PLL_SEL 15
+#define CPCAP_BIT_CLK_IN_SEL 13
+#define CPCAP_BIT_DIG_AUD_IN 12
+#define CPCAP_BIT_CDC_CLK_EN 11
+#define CPCAP_BIT_CDC_DIG_AUD_FS1 10
+#define CPCAP_BIT_CDC_DIG_AUD_FS0 9
+#define CPCAP_BIT_MIC2_TIMESLOT2 8
+#define CPCAP_BIT_MIC2_TIMESLOT1 7
+#define CPCAP_BIT_MIC2_TIMESLOT0 6
+#define CPCAP_BIT_MIC1_RX_TIMESLOT2 5
+#define CPCAP_BIT_MIC1_RX_TIMESLOT1 4
+#define CPCAP_BIT_MIC1_RX_TIMESLOT0 3
+#define CPCAP_BIT_FS_INV 2
+#define CPCAP_BIT_CLK_INV 1
+#define CPCAP_BIT_SMB_CDC 0
+
+/* Register 515 CPCAP_REG_SDAC --- Stereo DAC */
+#define CPCAP_BIT_FSYNC_CLK_IN_COMMON 11
+#define CPCAP_BIT_SLAVE_PLL_CLK_INPUT 10
+#define CPCAP_BIT_ST_CLOCK_TREE_RESET 9
+#define CPCAP_BIT_DF_RESET_ST_DAC 8
+#define CPCAP_BIT_ST_SR3 7
+#define CPCAP_BIT_ST_SR2 6
+#define CPCAP_BIT_ST_SR1 5
+#define CPCAP_BIT_ST_SR0 4
+#define CPCAP_BIT_ST_DAC_CLK2 3
+#define CPCAP_BIT_ST_DAC_CLK1 2
+#define CPCAP_BIT_ST_DAC_CLK0 1
+#define CPCAP_BIT_ST_DAC_EN 0
+
+/* Register 516 CPCAP_REG_SDACDI --- Stereo DAC Digital Audio Interface */
+#define CPCAP_BIT_ST_L_TIMESLOT2 13
+#define CPCAP_BIT_ST_L_TIMESLOT1 12
+#define CPCAP_BIT_ST_L_TIMESLOT0 11
+#define CPCAP_BIT_ST_R_TIMESLOT2 10
+#define CPCAP_BIT_ST_R_TIMESLOT1 9
+#define CPCAP_BIT_ST_R_TIMESLOT0 8
+#define CPCAP_BIT_ST_DAC_CLK_IN_SEL 7
+#define CPCAP_BIT_ST_FS_INV 6
+#define CPCAP_BIT_ST_CLK_INV 5
+#define CPCAP_BIT_ST_DIG_AUD_FS1 4
+#define CPCAP_BIT_ST_DIG_AUD_FS0 3
+#define CPCAP_BIT_DIG_AUD_IN_ST_DAC 2
+#define CPCAP_BIT_ST_CLK_EN 1
+#define CPCAP_BIT_SMB_ST_DAC 0
+
+/* Register 517 CPCAP_REG_TXI --- TX Interface */
+#define CPCAP_BIT_PTT_TH 15
+#define CPCAP_BIT_PTT_CMP_EN 14
+#define CPCAP_BIT_HS_ID_TX 13
+#define CPCAP_BIT_MB_ON2 12
+#define CPCAP_BIT_MB_ON1L 11
+#define CPCAP_BIT_MB_ON1R 10
+#define CPCAP_BIT_RX_L_ENCODE 9
+#define CPCAP_BIT_RX_R_ENCODE 8
+#define CPCAP_BIT_MIC2_MUX 7
+#define CPCAP_BIT_MIC2_PGA_EN 6
+#define CPCAP_BIT_CDET_DIS 5
+#define CPCAP_BIT_EMU_MIC_MUX 4
+#define CPCAP_BIT_HS_MIC_MUX 3
+#define CPCAP_BIT_MIC1_MUX 2
+#define CPCAP_BIT_MIC1_PGA_EN 1
+#define CPCAP_BIT_DLM 0
+
+/* Register 518 CPCAP_REG_TXMP --- Mic Gain */
+#define CPCAP_BIT_MB_BIAS_R1 11
+#define CPCAP_BIT_MB_BIAS_R0 10
+#define CPCAP_BIT_MIC2_GAIN_4 9
+#define CPCAP_BIT_MIC2_GAIN_3 8
+#define CPCAP_BIT_MIC2_GAIN_2 7
+#define CPCAP_BIT_MIC2_GAIN_1 6
+#define CPCAP_BIT_MIC2_GAIN_0 5
+#define CPCAP_BIT_MIC1_GAIN_4 4
+#define CPCAP_BIT_MIC1_GAIN_3 3
+#define CPCAP_BIT_MIC1_GAIN_2 2
+#define CPCAP_BIT_MIC1_GAIN_1 1
+#define CPCAP_BIT_MIC1_GAIN_0 0
+
+/* Register 519 CPCAP_REG_RXOA --- RX Output Amplifier */
+#define CPCAP_BIT_UNUSED_519_15 15
+#define CPCAP_BIT_UNUSED_519_14 14
+#define CPCAP_BIT_UNUSED_519_13 13
+#define CPCAP_BIT_STDAC_LOW_PWR_DISABLE 12
+#define CPCAP_BIT_HS_LOW_PWR 11
+#define CPCAP_BIT_HS_ID_RX 10
+#define CPCAP_BIT_ST_HS_CP_EN 9
+#define CPCAP_BIT_EMU_SPKR_R_EN 8
+#define CPCAP_BIT_EMU_SPKR_L_EN 7
+#define CPCAP_BIT_HS_L_EN 6
+#define CPCAP_BIT_HS_R_EN 5
+#define CPCAP_BIT_A4_LINEOUT_L_EN 4
+#define CPCAP_BIT_A4_LINEOUT_R_EN 3
+#define CPCAP_BIT_A2_LDSP_L_EN 2
+#define CPCAP_BIT_A2_LDSP_R_EN 1
+#define CPCAP_BIT_A1_EAR_EN 0
+
+/* Register 520 CPCAP_REG_RXVC --- RX Volume Control */
+#define CPCAP_BIT_VOL_EXT3 15
+#define CPCAP_BIT_VOL_EXT2 14
+#define CPCAP_BIT_VOL_EXT1 13
+#define CPCAP_BIT_VOL_EXT0 12
+#define CPCAP_BIT_VOL_DAC3 11
+#define CPCAP_BIT_VOL_DAC2 10
+#define CPCAP_BIT_VOL_DAC1 9
+#define CPCAP_BIT_VOL_DAC0 8
+#define CPCAP_BIT_VOL_DAC_LSB_1dB1 7
+#define CPCAP_BIT_VOL_DAC_LSB_1dB0 6
+#define CPCAP_BIT_VOL_CDC3 5
+#define CPCAP_BIT_VOL_CDC2 4
+#define CPCAP_BIT_VOL_CDC1 3
+#define CPCAP_BIT_VOL_CDC0 2
+#define CPCAP_BIT_VOL_CDC_LSB_1dB1 1
+#define CPCAP_BIT_VOL_CDC_LSB_1dB0 0
+
+/* Register 521 CPCAP_REG_RXCOA --- Codec to Output Amp Switches */
+#define CPCAP_BIT_PGA_CDC_EN 10
+#define CPCAP_BIT_CDC_SW 9
+#define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW 8
+#define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW 7
+#define CPCAP_BIT_ALEFT_HS_CDC_SW 6
+#define CPCAP_BIT_ARIGHT_HS_CDC_SW 5
+#define CPCAP_BIT_A4_LINEOUT_L_CDC_SW 4
+#define CPCAP_BIT_A4_LINEOUT_R_CDC_SW 3
+#define CPCAP_BIT_A2_LDSP_L_CDC_SW 2
+#define CPCAP_BIT_A2_LDSP_R_CDC_SW 1
+#define CPCAP_BIT_A1_EAR_CDC_SW 0
+
+/* Register 522 CPCAP_REG_RXSDOA --- RX Stereo DAC to Output Amp Switches */
+#define CPCAP_BIT_PGA_DAC_EN 12
+#define CPCAP_BIT_ST_DAC_SW 11
+#define CPCAP_BIT_MONO_DAC1 10
+#define CPCAP_BIT_MONO_DAC0 9
+#define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW 8
+#define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW 7
+#define CPCAP_BIT_ALEFT_HS_DAC_SW 6
+#define CPCAP_BIT_ARIGHT_HS_DAC_SW 5
+#define CPCAP_BIT_A4_LINEOUT_L_DAC_SW 4
+#define CPCAP_BIT_A4_LINEOUT_R_DAC_SW 3
+#define CPCAP_BIT_A2_LDSP_L_DAC_SW 2
+#define CPCAP_BIT_A2_LDSP_R_DAC_SW 1
+#define CPCAP_BIT_A1_EAR_DAC_SW 0
+
+/* Register 523 CPCAP_REG_RXEPOA --- RX External PGA to Output Amp Switches */
+#define CPCAP_BIT_PGA_EXT_L_EN 14
+#define CPCAP_BIT_PGA_EXT_R_EN 13
+#define CPCAP_BIT_PGA_IN_L_SW 12
+#define CPCAP_BIT_PGA_IN_R_SW 11
+#define CPCAP_BIT_MONO_EXT1 10
+#define CPCAP_BIT_MONO_EXT0 9
+#define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW 8
+#define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW 7
+#define CPCAP_BIT_ALEFT_HS_EXT_SW 6
+#define CPCAP_BIT_ARIGHT_HS_EXT_SW 5
+#define CPCAP_BIT_A4_LINEOUT_L_EXT_SW 4
+#define CPCAP_BIT_A4_LINEOUT_R_EXT_SW 3
+#define CPCAP_BIT_A2_LDSP_L_EXT_SW 2
+#define CPCAP_BIT_A2_LDSP_R_EXT_SW 1
+#define CPCAP_BIT_A1_EAR_EXT_SW 0
+
+/* Register 525 CPCAP_REG_A2LA --- SPK Amplifier and Clock Config for Headset */
+#define CPCAP_BIT_NCP_CLK_SYNC 7
+#define CPCAP_BIT_A2_CLK_SYNC 6
+#define CPCAP_BIT_A2_FREE_RUN 5
+#define CPCAP_BIT_A2_CLK2 4
+#define CPCAP_BIT_A2_CLK1 3
+#define CPCAP_BIT_A2_CLK0 2
+#define CPCAP_BIT_A2_CLK_IN 1
+#define CPCAP_BIT_A2_CONFIG 0
+
+#define SLEEP_ACTIVATE_POWER 2
+#define CLOCK_TREE_RESET_TIME 1
+
+/* constants for ST delay workaround */
+#define STM_STDAC_ACTIVATE_RAMP_TIME 1
+#define STM_STDAC_EN_TEST_PRE 0x090C
+#define STM_STDAC_EN_TEST_POST 0x0000
+#define STM_STDAC_EN_ST_TEST1_PRE 0x2400
+#define STM_STDAC_EN_ST_TEST1_POST 0x0400
+
+struct cpcap_reg_info {
+ u16 reg;
+ u16 mask;
+ u16 val;
+};
+
+static const struct cpcap_reg_info cpcap_default_regs[] = {
+ { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 },
+ { CPCAP_REG_CC, 0xFFFF, 0x0000 },
+ { CPCAP_REG_CC, 0xFFFF, 0x0000 },
+ { CPCAP_REG_CDI, 0xBFFF, 0x0000 },
+ { CPCAP_REG_SDAC, 0x0FFF, 0x0000 },
+ { CPCAP_REG_SDACDI, 0x3FFF, 0x0000 },
+ { CPCAP_REG_TXI, 0x0FDF, 0x0000 },
+ { CPCAP_REG_TXMP, 0x0FFF, 0x0400 },
+ { CPCAP_REG_RXOA, 0x01FF, 0x0000 },
+ { CPCAP_REG_RXVC, 0xFF3C, 0x0000 },
+ { CPCAP_REG_RXCOA, 0x07FF, 0x0000 },
+ { CPCAP_REG_RXSDOA, 0x1FFF, 0x0000 },
+ { CPCAP_REG_RXEPOA, 0x7FFF, 0x0000 },
+ { CPCAP_REG_A2LA, BIT(CPCAP_BIT_A2_FREE_RUN),
+ BIT(CPCAP_BIT_A2_FREE_RUN) },
+};
+
+enum cpcap_dai {
+ CPCAP_DAI_HIFI,
+ CPCAP_DAI_VOICE,
+};
+
+struct cpcap_audio {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ u16 vendor;
+
+ int codec_clk_id;
+ int codec_freq;
+ int codec_format;
+ struct regulator *vaudio;
+ int hsirq;
+ int mb2irq;
+ struct snd_soc_jack jack;
+};
+
+static int cpcap_st_workaround(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int err = 0;
+
+ /* Only CPCAP from ST requires workaround */
+ if (cpcap->vendor != CPCAP_VENDOR_ST)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ err = regmap_write(cpcap->regmap, CPCAP_REG_TEST,
+ STM_STDAC_EN_TEST_PRE);
+ if (err)
+ return err;
+ err = regmap_write(cpcap->regmap, CPCAP_REG_ST_TEST1,
+ STM_STDAC_EN_ST_TEST1_PRE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(STM_STDAC_ACTIVATE_RAMP_TIME);
+
+ err = regmap_write(cpcap->regmap, CPCAP_REG_ST_TEST1,
+ STM_STDAC_EN_ST_TEST1_POST);
+ if (err)
+ return err;
+ err = regmap_write(cpcap->regmap, CPCAP_REG_TEST,
+ STM_STDAC_EN_TEST_POST);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+/* Capture Gain Control: 0dB to 31dB in 1dB steps */
+static const DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
+
+/* Playback Gain Control: -33dB to 12dB in 3dB steps */
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3300, 300, 0);
+
+static const struct snd_kcontrol_new cpcap_snd_controls[] = {
+ /* Playback Gain */
+ SOC_SINGLE_TLV("HiFi Playback Volume",
+ CPCAP_REG_RXVC, CPCAP_BIT_VOL_DAC0, 0xF, 0, vol_tlv),
+ SOC_SINGLE_TLV("Voice Playback Volume",
+ CPCAP_REG_RXVC, CPCAP_BIT_VOL_CDC0, 0xF, 0, vol_tlv),
+ SOC_SINGLE_TLV("Ext Playback Volume",
+ CPCAP_REG_RXVC, CPCAP_BIT_VOL_EXT0, 0xF, 0, vol_tlv),
+
+ /* Capture Gain */
+ SOC_SINGLE_TLV("Mic1 Capture Volume",
+ CPCAP_REG_TXMP, CPCAP_BIT_MIC1_GAIN_0, 0x1F, 0, mic_gain_tlv),
+ SOC_SINGLE_TLV("Mic2 Capture Volume",
+ CPCAP_REG_TXMP, CPCAP_BIT_MIC2_GAIN_0, 0x1F, 0, mic_gain_tlv),
+
+ /* Phase Invert */
+ SOC_SINGLE("Hifi Left Phase Invert Switch",
+ CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC0, 1, 0),
+ SOC_SINGLE("Ext Left Phase Invert Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0),
+};
+
+static const char * const cpcap_out_mux_texts[] = {
+ "Off", "Voice", "HiFi", "Ext"
+};
+
+static const char * const cpcap_in_right_mux_texts[] = {
+ "Off", "Mic 1", "Headset Mic", "EMU Mic", "Ext Right"
+};
+
+static const char * const cpcap_in_left_mux_texts[] = {
+ "Off", "Mic 2", "Ext Left"
+};
+
+/*
+ * input muxes use unusual register layout, so that we need to use custom
+ * getter/setter methods
+ */
+static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_left_mux_enum,
+ cpcap_in_left_mux_texts);
+static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_right_mux_enum,
+ cpcap_in_right_mux_texts);
+
+/*
+ * mux uses same bit in CPCAP_REG_RXCOA, CPCAP_REG_RXSDOA & CPCAP_REG_RXEPOA;
+ * even though the register layout makes it look like a mixer, this is a mux.
+ * Enabling multiple inputs will result in no audio being forwarded.
+ */
+static SOC_ENUM_SINGLE_DECL(cpcap_earpiece_mux_enum, 0, 0, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_spkr_r_mux_enum, 0, 1, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_spkr_l_mux_enum, 0, 2, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_line_r_mux_enum, 0, 3, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_line_l_mux_enum, 0, 4, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_hs_r_mux_enum, 0, 5, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_hs_l_mux_enum, 0, 6, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_emu_l_mux_enum, 0, 7, cpcap_out_mux_texts);
+static SOC_ENUM_SINGLE_DECL(cpcap_emu_r_mux_enum, 0, 8, cpcap_out_mux_texts);
+
+static int cpcap_output_mux_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int shift = e->shift_l;
+ int reg_voice, reg_hifi, reg_ext, status;
+ int err;
+
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXCOA, &reg_voice);
+ if (err)
+ return err;
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXSDOA, &reg_hifi);
+ if (err)
+ return err;
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXEPOA, &reg_ext);
+ if (err)
+ return err;
+
+ reg_voice = (reg_voice >> shift) & 1;
+ reg_hifi = (reg_hifi >> shift) & 1;
+ reg_ext = (reg_ext >> shift) & 1;
+ status = reg_ext << 2 | reg_hifi << 1 | reg_voice;
+
+ switch (status) {
+ case 0x04:
+ ucontrol->value.enumerated.item[0] = 3;
+ break;
+ case 0x02:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case 0x01:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int cpcap_output_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int muxval = ucontrol->value.enumerated.item[0];
+ unsigned int mask = BIT(e->shift_l);
+ u16 reg_voice = 0x00, reg_hifi = 0x00, reg_ext = 0x00;
+ int err;
+
+ switch (muxval) {
+ case 1:
+ reg_voice = mask;
+ break;
+ case 2:
+ reg_hifi = mask;
+ break;
+ case 3:
+ reg_ext = mask;
+ break;
+ default:
+ break;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA,
+ mask, reg_voice);
+ if (err)
+ return err;
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXSDOA,
+ mask, reg_hifi);
+ if (err)
+ return err;
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXEPOA,
+ mask, reg_ext);
+ if (err)
+ return err;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL);
+
+ return 0;
+}
+
+static int cpcap_input_right_mux_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int regval, mask;
+ int err;
+
+ err = regmap_read(cpcap->regmap, CPCAP_REG_TXI, &regval);
+ if (err)
+ return err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC1_MUX);
+ mask |= BIT(CPCAP_BIT_HS_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_EMU_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_RX_R_ENCODE);
+
+ switch (regval & mask) {
+ case BIT(CPCAP_BIT_RX_R_ENCODE):
+ ucontrol->value.enumerated.item[0] = 4;
+ break;
+ case BIT(CPCAP_BIT_EMU_MIC_MUX):
+ ucontrol->value.enumerated.item[0] = 3;
+ break;
+ case BIT(CPCAP_BIT_HS_MIC_MUX):
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case BIT(CPCAP_BIT_MIC1_MUX):
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int cpcap_input_right_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int muxval = ucontrol->value.enumerated.item[0];
+ int regval = 0, mask;
+ int err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC1_MUX);
+ mask |= BIT(CPCAP_BIT_HS_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_EMU_MIC_MUX);
+ mask |= BIT(CPCAP_BIT_RX_R_ENCODE);
+
+ switch (muxval) {
+ case 1:
+ regval = BIT(CPCAP_BIT_MIC1_MUX);
+ break;
+ case 2:
+ regval = BIT(CPCAP_BIT_HS_MIC_MUX);
+ break;
+ case 3:
+ regval = BIT(CPCAP_BIT_EMU_MIC_MUX);
+ break;
+ case 4:
+ regval = BIT(CPCAP_BIT_RX_R_ENCODE);
+ break;
+ default:
+ break;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, regval);
+ if (err)
+ return err;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL);
+
+ return 0;
+}
+
+static int cpcap_input_left_mux_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int regval, mask;
+ int err;
+
+ err = regmap_read(cpcap->regmap, CPCAP_REG_TXI, &regval);
+ if (err)
+ return err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC2_MUX);
+ mask |= BIT(CPCAP_BIT_RX_L_ENCODE);
+
+ switch (regval & mask) {
+ case BIT(CPCAP_BIT_RX_L_ENCODE):
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case BIT(CPCAP_BIT_MIC2_MUX):
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int cpcap_input_left_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int muxval = ucontrol->value.enumerated.item[0];
+ int regval = 0, mask;
+ int err;
+
+ mask = 0;
+ mask |= BIT(CPCAP_BIT_MIC2_MUX);
+ mask |= BIT(CPCAP_BIT_RX_L_ENCODE);
+
+ switch (muxval) {
+ case 1:
+ regval = BIT(CPCAP_BIT_MIC2_MUX);
+ break;
+ case 2:
+ regval = BIT(CPCAP_BIT_RX_L_ENCODE);
+ break;
+ default:
+ break;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, regval);
+ if (err)
+ return err;
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cpcap_input_left_mux =
+ SOC_DAPM_ENUM_EXT("Input Left", cpcap_input_left_mux_enum,
+ cpcap_input_left_mux_get_enum,
+ cpcap_input_left_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_input_right_mux =
+ SOC_DAPM_ENUM_EXT("Input Right", cpcap_input_right_mux_enum,
+ cpcap_input_right_mux_get_enum,
+ cpcap_input_right_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_emu_left_mux =
+ SOC_DAPM_ENUM_EXT("EMU Left", cpcap_emu_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_emu_right_mux =
+ SOC_DAPM_ENUM_EXT("EMU Right", cpcap_emu_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_hs_left_mux =
+ SOC_DAPM_ENUM_EXT("Headset Left", cpcap_hs_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_hs_right_mux =
+ SOC_DAPM_ENUM_EXT("Headset Right", cpcap_hs_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_line_left_mux =
+ SOC_DAPM_ENUM_EXT("Line Left", cpcap_line_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_line_right_mux =
+ SOC_DAPM_ENUM_EXT("Line Right", cpcap_line_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_speaker_left_mux =
+ SOC_DAPM_ENUM_EXT("Speaker Left", cpcap_spkr_l_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_speaker_right_mux =
+ SOC_DAPM_ENUM_EXT("Speaker Right", cpcap_spkr_r_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+static const struct snd_kcontrol_new cpcap_earpiece_mux =
+ SOC_DAPM_ENUM_EXT("Earpiece", cpcap_earpiece_mux_enum,
+ cpcap_output_mux_get_enum, cpcap_output_mux_put_enum);
+
+static const struct snd_kcontrol_new cpcap_hifi_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("HiFi Mono Playback Switch",
+ CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC1, 1, 0),
+};
+static const struct snd_kcontrol_new cpcap_ext_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Ext Mono Playback Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0),
+};
+
+static const struct snd_kcontrol_new cpcap_extr_mute_control =
+ SOC_DAPM_SINGLE("Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_R_SW, 1, 0);
+static const struct snd_kcontrol_new cpcap_extl_mute_control =
+ SOC_DAPM_SINGLE("Switch",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_L_SW, 1, 0);
+
+static const struct snd_kcontrol_new cpcap_voice_loopback =
+ SOC_DAPM_SINGLE("Switch",
+ CPCAP_REG_TXI, CPCAP_BIT_DLM, 1, 0);
+
+static const struct snd_soc_dapm_widget cpcap_dapm_widgets[] = {
+ /* DAIs */
+ SND_SOC_DAPM_AIF_IN("HiFi RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Voice RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Voice TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Power Supply */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VAUDIO", SLEEP_ACTIVATE_POWER, 0),
+
+ /* Highpass Filters */
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter RX",
+ CPCAP_REG_CC, CPCAP_BIT_AUDIHPF_0, 0x3, 0x3, 0x0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter TX",
+ CPCAP_REG_CC, CPCAP_BIT_AUDOHPF_0, 0x3, 0x3, 0x0),
+
+ /* Clocks */
+ SND_SOC_DAPM_SUPPLY("HiFi DAI Clock",
+ CPCAP_REG_SDACDI, CPCAP_BIT_ST_CLK_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Voice DAI Clock",
+ CPCAP_REG_CDI, CPCAP_BIT_CDC_CLK_EN, 0, NULL, 0),
+
+ /* Microphone Bias */
+ SND_SOC_DAPM_SUPPLY("MIC1R Bias",
+ CPCAP_REG_TXI, CPCAP_BIT_MB_ON1R, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC1L Bias",
+ CPCAP_REG_TXI, CPCAP_BIT_MB_ON1L, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias",
+ CPCAP_REG_TXI, CPCAP_BIT_MB_ON2, 0, NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("EMUMIC"),
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("EXTR"),
+ SND_SOC_DAPM_INPUT("EXTL"),
+
+ /* Capture Route */
+ SND_SOC_DAPM_MUX("Right Capture Route",
+ SND_SOC_NOPM, 0, 0, &cpcap_input_right_mux),
+ SND_SOC_DAPM_MUX("Left Capture Route",
+ SND_SOC_NOPM, 0, 0, &cpcap_input_left_mux),
+
+ /* Capture PGAs */
+ SND_SOC_DAPM_PGA("Microphone 1 PGA",
+ CPCAP_REG_TXI, CPCAP_BIT_MIC1_PGA_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Microphone 2 PGA",
+ CPCAP_REG_TXI, CPCAP_BIT_MIC2_PGA_EN, 0, NULL, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC Right", NULL,
+ CPCAP_REG_CC, CPCAP_BIT_MIC1_CDC_EN, 0),
+ SND_SOC_DAPM_ADC("ADC Left", NULL,
+ CPCAP_REG_CC, CPCAP_BIT_MIC2_CDC_EN, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC_E("DAC HiFi", NULL,
+ CPCAP_REG_SDAC, CPCAP_BIT_ST_DAC_EN, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_DAC_E("DAC Voice", NULL,
+ CPCAP_REG_CC, CPCAP_BIT_CDC_EN_RX, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* Playback PGA */
+ SND_SOC_DAPM_PGA("HiFi PGA",
+ CPCAP_REG_RXSDOA, CPCAP_BIT_PGA_DAC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Voice PGA",
+ CPCAP_REG_RXCOA, CPCAP_BIT_PGA_CDC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Ext Right PGA",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_R_EN, 0,
+ NULL, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("Ext Left PGA",
+ CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_L_EN, 0,
+ NULL, 0,
+ cpcap_st_workaround,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* Playback Switch */
+ SND_SOC_DAPM_SWITCH("Ext Right Enable", SND_SOC_NOPM, 0, 0,
+ &cpcap_extr_mute_control),
+ SND_SOC_DAPM_SWITCH("Ext Left Enable", SND_SOC_NOPM, 0, 0,
+ &cpcap_extl_mute_control),
+
+ /* Loopback Switch */
+ SND_SOC_DAPM_SWITCH("Voice Loopback", SND_SOC_NOPM, 0, 0,
+ &cpcap_voice_loopback),
+
+ /* Mono Mixer */
+ SOC_MIXER_ARRAY("HiFi Mono Left Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_hifi_mono_mixer_controls),
+ SOC_MIXER_ARRAY("HiFi Mono Right Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_hifi_mono_mixer_controls),
+ SOC_MIXER_ARRAY("Ext Mono Left Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_ext_mono_mixer_controls),
+ SOC_MIXER_ARRAY("Ext Mono Right Mixer", SND_SOC_NOPM, 0, 0,
+ cpcap_ext_mono_mixer_controls),
+
+ /* Output Routes */
+ SND_SOC_DAPM_MUX("Earpiece Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_earpiece_mux),
+ SND_SOC_DAPM_MUX("Speaker Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_speaker_right_mux),
+ SND_SOC_DAPM_MUX("Speaker Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_speaker_left_mux),
+ SND_SOC_DAPM_MUX("Lineout Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_line_right_mux),
+ SND_SOC_DAPM_MUX("Lineout Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_line_left_mux),
+ SND_SOC_DAPM_MUX("Headset Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_hs_right_mux),
+ SND_SOC_DAPM_MUX("Headset Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_hs_left_mux),
+ SND_SOC_DAPM_MUX("EMU Right Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_emu_right_mux),
+ SND_SOC_DAPM_MUX("EMU Left Playback Route", SND_SOC_NOPM, 0, 0,
+ &cpcap_emu_left_mux),
+
+ /* Output Amplifier */
+ SND_SOC_DAPM_PGA("Earpiece PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A1_EAR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_L_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_L_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_HS_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_HS_L_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("EMU Right PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_R_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("EMU Left PGA",
+ CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_L_EN, 0, NULL, 0),
+
+ /* Headet Charge Pump */
+ SND_SOC_DAPM_SUPPLY("Headset Charge Pump",
+ CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, 0, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("EP"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("LINER"),
+ SND_SOC_DAPM_OUTPUT("LINEL"),
+ SND_SOC_DAPM_OUTPUT("HSR"),
+ SND_SOC_DAPM_OUTPUT("HSL"),
+ SND_SOC_DAPM_OUTPUT("EMUR"),
+ SND_SOC_DAPM_OUTPUT("EMUL"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Power Supply */
+ {"HiFi PGA", NULL, "VAUDIO"},
+ {"Voice PGA", NULL, "VAUDIO"},
+ {"Ext Right PGA", NULL, "VAUDIO"},
+ {"Ext Left PGA", NULL, "VAUDIO"},
+ {"Microphone 1 PGA", NULL, "VAUDIO"},
+ {"Microphone 2 PGA", NULL, "VAUDIO"},
+
+ /* Stream -> AIF */
+ {"HiFi RX", NULL, "HiFi Playback"},
+ {"Voice RX", NULL, "Voice Playback"},
+ {"Voice Capture", NULL, "Voice TX"},
+
+ /* AIF clocks */
+ {"HiFi RX", NULL, "HiFi DAI Clock"},
+ {"Voice RX", NULL, "Voice DAI Clock"},
+ {"Voice TX", NULL, "Voice DAI Clock"},
+
+ /* Digital Loopback */
+ {"Voice Loopback", "Switch", "Voice TX"},
+ {"Voice RX", NULL, "Voice Loopback"},
+
+ /* Highpass Filters */
+ {"Highpass Filter RX", NULL, "Voice RX"},
+ {"Voice TX", NULL, "Highpass Filter TX"},
+
+ /* AIF -> DAC mapping */
+ {"DAC HiFi", NULL, "HiFi RX"},
+ {"DAC Voice", NULL, "Highpass Filter RX"},
+
+ /* DAC -> PGA */
+ {"HiFi PGA", NULL, "DAC HiFi"},
+ {"Voice PGA", NULL, "DAC Voice"},
+
+ /* Ext Input -> PGA */
+ {"Ext Right PGA", NULL, "EXTR"},
+ {"Ext Left PGA", NULL, "EXTL"},
+
+ /* Ext PGA -> Ext Playback Switch */
+ {"Ext Right Enable", "Switch", "Ext Right PGA"},
+ {"Ext Left Enable", "Switch", "Ext Left PGA"},
+
+ /* HiFi PGA -> Mono Mixer */
+ {"HiFi Mono Left Mixer", NULL, "HiFi PGA"},
+ {"HiFi Mono Left Mixer", "HiFi Mono Playback Switch", "HiFi PGA"},
+ {"HiFi Mono Right Mixer", NULL, "HiFi PGA"},
+ {"HiFi Mono Right Mixer", "HiFi Mono Playback Switch", "HiFi PGA"},
+
+ /* Ext Playback Switch -> Ext Mono Mixer */
+ {"Ext Mono Right Mixer", NULL, "Ext Right Enable"},
+ {"Ext Mono Right Mixer", "Ext Mono Playback Switch", "Ext Left Enable"},
+ {"Ext Mono Left Mixer", NULL, "Ext Left Enable"},
+ {"Ext Mono Left Mixer", "Ext Mono Playback Switch", "Ext Right Enable"},
+
+ /* HiFi Mono Mixer -> Output Route */
+ {"Earpiece Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Speaker Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Speaker Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+ {"Lineout Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Lineout Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+ {"Headset Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"Headset Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+ {"EMU Right Playback Route", "HiFi", "HiFi Mono Right Mixer"},
+ {"EMU Left Playback Route", "HiFi", "HiFi Mono Left Mixer"},
+
+ /* Voice PGA -> Output Route */
+ {"Earpiece Playback Route", "Voice", "Voice PGA"},
+ {"Speaker Right Playback Route", "Voice", "Voice PGA"},
+ {"Speaker Left Playback Route", "Voice", "Voice PGA"},
+ {"Lineout Right Playback Route", "Voice", "Voice PGA"},
+ {"Lineout Left Playback Route", "Voice", "Voice PGA"},
+ {"Headset Right Playback Route", "Voice", "Voice PGA"},
+ {"Headset Left Playback Route", "Voice", "Voice PGA"},
+ {"EMU Right Playback Route", "Voice", "Voice PGA"},
+ {"EMU Left Playback Route", "Voice", "Voice PGA"},
+
+ /* Ext Mono Mixer -> Output Route */
+ {"Earpiece Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Speaker Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Speaker Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+ {"Lineout Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Lineout Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+ {"Headset Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"Headset Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+ {"EMU Right Playback Route", "Ext", "Ext Mono Right Mixer"},
+ {"EMU Left Playback Route", "Ext", "Ext Mono Left Mixer"},
+
+ /* Output Route -> Output Amplifier */
+ {"Earpiece PGA", NULL, "Earpiece Playback Route"},
+ {"Speaker Right PGA", NULL, "Speaker Right Playback Route"},
+ {"Speaker Left PGA", NULL, "Speaker Left Playback Route"},
+ {"Lineout Right PGA", NULL, "Lineout Right Playback Route"},
+ {"Lineout Left PGA", NULL, "Lineout Left Playback Route"},
+ {"Headset Right PGA", NULL, "Headset Right Playback Route"},
+ {"Headset Left PGA", NULL, "Headset Left Playback Route"},
+ {"EMU Right PGA", NULL, "EMU Right Playback Route"},
+ {"EMU Left PGA", NULL, "EMU Left Playback Route"},
+
+ /* Output Amplifier -> Output */
+ {"EP", NULL, "Earpiece PGA"},
+ {"SPKR", NULL, "Speaker Right PGA"},
+ {"SPKL", NULL, "Speaker Left PGA"},
+ {"LINER", NULL, "Lineout Right PGA"},
+ {"LINEL", NULL, "Lineout Left PGA"},
+ {"HSR", NULL, "Headset Right PGA"},
+ {"HSL", NULL, "Headset Left PGA"},
+ {"EMUR", NULL, "EMU Right PGA"},
+ {"EMUL", NULL, "EMU Left PGA"},
+
+ /* Headset Charge Pump -> Headset */
+ {"HSR", NULL, "Headset Charge Pump"},
+ {"HSL", NULL, "Headset Charge Pump"},
+
+ /* Mic -> Mic Route */
+ {"Right Capture Route", "Mic 1", "MICR"},
+ {"Right Capture Route", "Headset Mic", "HSMIC"},
+ {"Right Capture Route", "EMU Mic", "EMUMIC"},
+ {"Right Capture Route", "Ext Right", "EXTR"},
+ {"Left Capture Route", "Mic 2", "MICL"},
+ {"Left Capture Route", "Ext Left", "EXTL"},
+
+ /* Input Route -> Microphone PGA */
+ {"Microphone 1 PGA", NULL, "Right Capture Route"},
+ {"Microphone 2 PGA", NULL, "Left Capture Route"},
+
+ /* Microphone PGA -> ADC */
+ {"ADC Right", NULL, "Microphone 1 PGA"},
+ {"ADC Left", NULL, "Microphone 2 PGA"},
+
+ /* ADC -> Stream */
+ {"Highpass Filter TX", NULL, "ADC Right"},
+ {"Highpass Filter TX", NULL, "ADC Left"},
+
+ /* Mic Bias */
+ {"MICL", NULL, "MIC1L Bias"},
+ {"MICR", NULL, "MIC1R Bias"},
+};
+
+static int cpcap_set_sysclk(struct cpcap_audio *cpcap, enum cpcap_dai dai,
+ int clk_id, int freq)
+{
+ u16 clkfreqreg, clkfreqshift;
+ u16 clkfreqmask, clkfreqval;
+ u16 clkidreg, clkidshift;
+ u16 mask, val;
+ int err;
+
+ switch (dai) {
+ case CPCAP_DAI_HIFI:
+ clkfreqreg = CPCAP_REG_SDAC;
+ clkfreqshift = CPCAP_BIT_ST_DAC_CLK0;
+ clkidreg = CPCAP_REG_SDACDI;
+ clkidshift = CPCAP_BIT_ST_DAC_CLK_IN_SEL;
+ break;
+ case CPCAP_DAI_VOICE:
+ clkfreqreg = CPCAP_REG_CC;
+ clkfreqshift = CPCAP_BIT_CDC_CLK0;
+ clkidreg = CPCAP_REG_CDI;
+ clkidshift = CPCAP_BIT_CLK_IN_SEL;
+ break;
+ default:
+ dev_err(cpcap->component->dev, "invalid DAI: %d", dai);
+ return -EINVAL;
+ }
+
+ /* setup clk id */
+ if (clk_id < 0 || clk_id > 1) {
+ dev_err(cpcap->component->dev, "invalid clk id %d", clk_id);
+ return -EINVAL;
+ }
+ err = regmap_update_bits(cpcap->regmap, clkidreg, BIT(clkidshift),
+ clk_id ? BIT(clkidshift) : 0);
+ if (err)
+ return err;
+
+ /* enable PLL for Voice DAI */
+ if (dai == CPCAP_DAI_VOICE) {
+ mask = BIT(CPCAP_BIT_CDC_PLL_SEL);
+ val = BIT(CPCAP_BIT_CDC_PLL_SEL);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ mask, val);
+ if (err)
+ return err;
+ }
+
+ /* setup frequency */
+ clkfreqmask = 0x7 << clkfreqshift;
+ switch (freq) {
+ case 15360000:
+ clkfreqval = 0x01 << clkfreqshift;
+ break;
+ case 16800000:
+ clkfreqval = 0x02 << clkfreqshift;
+ break;
+ case 19200000:
+ clkfreqval = 0x03 << clkfreqshift;
+ break;
+ case 26000000:
+ clkfreqval = 0x04 << clkfreqshift;
+ break;
+ case 33600000:
+ clkfreqval = 0x05 << clkfreqshift;
+ break;
+ case 38400000:
+ clkfreqval = 0x06 << clkfreqshift;
+ break;
+ default:
+ dev_err(cpcap->component->dev, "unsupported freq %u", freq);
+ return -EINVAL;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, clkfreqreg,
+ clkfreqmask, clkfreqval);
+ if (err)
+ return err;
+
+ if (dai == CPCAP_DAI_VOICE) {
+ cpcap->codec_clk_id = clk_id;
+ cpcap->codec_freq = freq;
+ }
+
+ return 0;
+}
+
+static int cpcap_set_samprate(struct cpcap_audio *cpcap, enum cpcap_dai dai,
+ int samplerate)
+{
+ struct snd_soc_component *component = cpcap->component;
+ u16 sampreg, sampmask, sampshift, sampval, sampreset;
+ int err, sampreadval;
+
+ switch (dai) {
+ case CPCAP_DAI_HIFI:
+ sampreg = CPCAP_REG_SDAC;
+ sampshift = CPCAP_BIT_ST_SR0;
+ sampreset = BIT(CPCAP_BIT_DF_RESET_ST_DAC) |
+ BIT(CPCAP_BIT_ST_CLOCK_TREE_RESET);
+ break;
+ case CPCAP_DAI_VOICE:
+ sampreg = CPCAP_REG_CC;
+ sampshift = CPCAP_BIT_CDC_SR0;
+ sampreset = BIT(CPCAP_BIT_DF_RESET) |
+ BIT(CPCAP_BIT_CDC_CLOCK_TREE_RESET);
+ break;
+ default:
+ dev_err(component->dev, "invalid DAI: %d", dai);
+ return -EINVAL;
+ }
+
+ sampmask = 0xF << sampshift | sampreset;
+ switch (samplerate) {
+ case 48000:
+ sampval = 0x8 << sampshift;
+ break;
+ case 44100:
+ sampval = 0x7 << sampshift;
+ break;
+ case 32000:
+ sampval = 0x6 << sampshift;
+ break;
+ case 24000:
+ sampval = 0x5 << sampshift;
+ break;
+ case 22050:
+ sampval = 0x4 << sampshift;
+ break;
+ case 16000:
+ sampval = 0x3 << sampshift;
+ break;
+ case 12000:
+ sampval = 0x2 << sampshift;
+ break;
+ case 11025:
+ sampval = 0x1 << sampshift;
+ break;
+ case 8000:
+ sampval = 0x0 << sampshift;
+ break;
+ default:
+ dev_err(component->dev, "unsupported samplerate %d", samplerate);
+ return -EINVAL;
+ }
+ err = regmap_update_bits(cpcap->regmap, sampreg,
+ sampmask, sampval | sampreset);
+ if (err)
+ return err;
+
+ /* Wait for clock tree reset to complete */
+ mdelay(CLOCK_TREE_RESET_TIME);
+
+ err = regmap_read(cpcap->regmap, sampreg, &sampreadval);
+ if (err)
+ return err;
+
+ if (sampreadval & sampreset) {
+ dev_err(component->dev, "reset self-clear failed: %04x",
+ sampreadval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cpcap_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int rate = params_rate(params);
+
+ dev_dbg(component->dev, "HiFi setup HW params: rate=%d", rate);
+ return cpcap_set_samprate(cpcap, CPCAP_DAI_HIFI, rate);
+}
+
+static int cpcap_hifi_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ dev_dbg(dev, "HiFi setup sysclk: clk_id=%u, freq=%u", clk_id, freq);
+ return cpcap_set_sysclk(cpcap, CPCAP_DAI_HIFI, clk_id, freq);
+}
+
+static int cpcap_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ static const u16 reg = CPCAP_REG_SDACDI;
+ static const u16 mask =
+ BIT(CPCAP_BIT_SMB_ST_DAC) |
+ BIT(CPCAP_BIT_ST_CLK_INV) |
+ BIT(CPCAP_BIT_ST_FS_INV) |
+ BIT(CPCAP_BIT_ST_DIG_AUD_FS0) |
+ BIT(CPCAP_BIT_ST_DIG_AUD_FS1) |
+ BIT(CPCAP_BIT_ST_L_TIMESLOT0) |
+ BIT(CPCAP_BIT_ST_L_TIMESLOT1) |
+ BIT(CPCAP_BIT_ST_L_TIMESLOT2) |
+ BIT(CPCAP_BIT_ST_R_TIMESLOT0) |
+ BIT(CPCAP_BIT_ST_R_TIMESLOT1) |
+ BIT(CPCAP_BIT_ST_R_TIMESLOT2);
+ u16 val = 0x0000;
+
+ dev_dbg(dev, "HiFi setup dai format (%08x)", fmt);
+
+ /*
+ * "HiFi Playback" should always be configured as
+ * SND_SOC_DAIFMT_CBP_CFP - codec clk & frm provider
+ * SND_SOC_DAIFMT_I2S - I2S mode
+ */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val &= ~BIT(CPCAP_BIT_SMB_ST_DAC);
+ break;
+ default:
+ dev_err(dev, "HiFi dai fmt failed: CPCAP should be provider");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= BIT(CPCAP_BIT_ST_FS_INV);
+ val |= BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val &= ~BIT(CPCAP_BIT_ST_FS_INV);
+ val |= BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= BIT(CPCAP_BIT_ST_FS_INV);
+ val &= ~BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ val &= ~BIT(CPCAP_BIT_ST_FS_INV);
+ val &= ~BIT(CPCAP_BIT_ST_CLK_INV);
+ break;
+ default:
+ dev_err(dev, "HiFi dai fmt failed: unsupported clock invert mode");
+ return -EINVAL;
+ }
+
+ if (val & BIT(CPCAP_BIT_ST_CLK_INV))
+ val &= ~BIT(CPCAP_BIT_ST_CLK_INV);
+ else
+ val |= BIT(CPCAP_BIT_ST_CLK_INV);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0);
+ val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS1);
+ break;
+ default:
+ /* 01 - 4 slots network mode */
+ val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0);
+ val &= ~BIT(CPCAP_BIT_ST_DIG_AUD_FS1);
+ /* L on slot 1 */
+ val |= BIT(CPCAP_BIT_ST_L_TIMESLOT0);
+ break;
+ }
+
+ dev_dbg(dev, "HiFi dai format: val=%04x", val);
+ return regmap_update_bits(cpcap->regmap, reg, mask, val);
+}
+
+static int cpcap_hifi_set_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 reg = CPCAP_REG_RXSDOA;
+ static const u16 mask = BIT(CPCAP_BIT_ST_DAC_SW);
+ u16 val;
+
+ if (mute)
+ val = 0;
+ else
+ val = BIT(CPCAP_BIT_ST_DAC_SW);
+
+ dev_dbg(component->dev, "HiFi mute: %d", mute);
+ return regmap_update_bits(cpcap->regmap, reg, mask, val);
+}
+
+static const struct snd_soc_dai_ops cpcap_dai_hifi_ops = {
+ .hw_params = cpcap_hifi_hw_params,
+ .set_sysclk = cpcap_hifi_set_dai_sysclk,
+ .set_fmt = cpcap_hifi_set_dai_fmt,
+ .mute_stream = cpcap_hifi_set_mute,
+ .no_capture_mute = 1,
+};
+
+static int cpcap_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct device *dev = component->dev;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 reg_cdi = CPCAP_REG_CDI;
+ int rate = params_rate(params);
+ int channels = params_channels(params);
+ int direction = substream->stream;
+ u16 val, mask;
+ int err;
+
+ dev_dbg(dev, "Voice setup HW params: rate=%d, direction=%d, chan=%d",
+ rate, direction, channels);
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, rate);
+ if (err)
+ return err;
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ mask = 0x0000;
+ mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0);
+ mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT1);
+ mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT2);
+ mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT0);
+ mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT1);
+ mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT2);
+ val = 0x0000;
+ if (channels >= 2)
+ val = BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0);
+ err = regmap_update_bits(cpcap->regmap, reg_cdi, mask, val);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Voice setup sysclk: clk_id=%u, freq=%u",
+ clk_id, freq);
+ return cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, clk_id, freq);
+}
+
+static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 mask = BIT(CPCAP_BIT_SMB_CDC) |
+ BIT(CPCAP_BIT_CLK_INV) |
+ BIT(CPCAP_BIT_FS_INV) |
+ BIT(CPCAP_BIT_CDC_DIG_AUD_FS0) |
+ BIT(CPCAP_BIT_CDC_DIG_AUD_FS1);
+ u16 val = 0x0000;
+ int err;
+
+ dev_dbg(component->dev, "Voice setup dai format (%08x)", fmt);
+
+ /*
+ * "Voice Playback" and "Voice Capture" should always be
+ * configured as SND_SOC_DAIFMT_CBP_CFP - codec clk & frm
+ * provider
+ */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val &= ~BIT(CPCAP_BIT_SMB_CDC);
+ break;
+ default:
+ dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the provider");
+ val &= ~BIT(CPCAP_BIT_SMB_CDC);
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= BIT(CPCAP_BIT_CLK_INV);
+ val |= BIT(CPCAP_BIT_FS_INV);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= BIT(CPCAP_BIT_CLK_INV);
+ val &= ~BIT(CPCAP_BIT_FS_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val &= ~BIT(CPCAP_BIT_CLK_INV);
+ val |= BIT(CPCAP_BIT_FS_INV);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ val &= ~BIT(CPCAP_BIT_CLK_INV);
+ val &= ~BIT(CPCAP_BIT_FS_INV);
+ break;
+ default:
+ dev_err(component->dev, "Voice dai fmt failed: unsupported clock invert mode");
+ break;
+ }
+
+ if (val & BIT(CPCAP_BIT_CLK_INV))
+ val &= ~BIT(CPCAP_BIT_CLK_INV);
+ else
+ val |= BIT(CPCAP_BIT_CLK_INV);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* 11 - true I2S mode */
+ val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0);
+ val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS1);
+ break;
+ default:
+ /* 4 timeslots network mode */
+ val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0);
+ val &= ~BIT(CPCAP_BIT_CDC_DIG_AUD_FS1);
+ break;
+ }
+
+ dev_dbg(component->dev, "Voice dai format: val=%04x", val);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, mask, val);
+ if (err)
+ return err;
+
+ cpcap->codec_format = val;
+ return 0;
+}
+
+
+/*
+ * Configure codec for voice call if requested.
+ *
+ * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt()
+ * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the
+ * cpcap related hardware as CPU is not involved in the voice call.
+ */
+static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai,
+ bool voice_call)
+{
+ int mask, err;
+
+ /* Modem to codec VAUDIO_MODE1 */
+ mask = BIT(CPCAP_BIT_VAUDIO_MODE1);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Clear MIC1_MUX for call */
+ mask = BIT(CPCAP_BIT_MIC1_MUX);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, voice_call ? 0 : mask);
+ if (err)
+ return err;
+
+ /* Set MIC2_MUX for call */
+ mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) |
+ BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Enable LDSP for call */
+ mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Enable CPCAP_BIT_PGA_CDC_EN for call */
+ mask = BIT(CPCAP_BIT_PGA_CDC_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Unmute voice for call */
+ if (dai) {
+ err = snd_soc_dai_digital_mute(dai, !voice_call,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (err)
+ return err;
+ }
+
+ /* Set modem to codec mic CDC and HPF for call */
+ mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) |
+ BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) |
+ BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC,
+ mask, voice_call ? mask : 0);
+ if (err)
+ return err;
+
+ /* Enable modem to codec CDC for call*/
+ mask = BIT(CPCAP_BIT_CDC_CLK_EN);
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ mask, voice_call ? mask : 0);
+
+ return err;
+}
+
+static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int err, ts_mask, mask;
+ bool voice_call;
+
+ /*
+ * Primitive test for voice call, probably needs more checks
+ * later on for 16-bit calls detected, Bluetooth headset etc.
+ */
+ if (tx_mask == 0 && rx_mask == 1 && slot_width == 8)
+ voice_call = true;
+ else
+ voice_call = false;
+
+ ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0;
+ ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0;
+
+ mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0;
+ mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0;
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ ts_mask, mask);
+ if (err)
+ return err;
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000);
+ if (err)
+ return err;
+
+ err = cpcap_voice_call(cpcap, dai, voice_call);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ static const u16 reg = CPCAP_REG_RXCOA;
+ static const u16 mask = BIT(CPCAP_BIT_CDC_SW);
+ u16 val;
+
+ if (mute)
+ val = 0;
+ else
+ val = BIT(CPCAP_BIT_CDC_SW);
+
+ dev_dbg(component->dev, "Voice mute: %d", mute);
+ return regmap_update_bits(cpcap->regmap, reg, mask, val);
+};
+
+static const struct snd_soc_dai_ops cpcap_dai_voice_ops = {
+ .hw_params = cpcap_voice_hw_params,
+ .set_sysclk = cpcap_voice_set_dai_sysclk,
+ .set_fmt = cpcap_voice_set_dai_fmt,
+ .set_tdm_slot = cpcap_voice_set_tdm_slot,
+ .mute_stream = cpcap_voice_set_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cpcap_dai[] = {
+{
+ .id = 0,
+ .name = "cpcap-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE,
+ },
+ .ops = &cpcap_dai_hifi_ops,
+},
+{
+ .id = 1,
+ .name = "cpcap-voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &cpcap_dai_voice_ops,
+},
+};
+
+static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration)
+{
+ u16 hifi_val, voice_val;
+ u16 hifi_mask = BIT(CPCAP_BIT_DIG_AUD_IN_ST_DAC);
+ u16 voice_mask = BIT(CPCAP_BIT_DIG_AUD_IN);
+ int err;
+
+
+
+ if (!swap_dai_configuration) {
+ /* Codec on DAI0, HiFi on DAI1 */
+ voice_val = 0;
+ hifi_val = hifi_mask;
+ } else {
+ /* Codec on DAI1, HiFi on DAI0 */
+ voice_val = voice_mask;
+ hifi_val = 0;
+ }
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI,
+ voice_mask, voice_val);
+ if (err)
+ return err;
+
+ err = regmap_update_bits(cpcap->regmap, CPCAP_REG_SDACDI,
+ hifi_mask, hifi_val);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int cpcap_audio_reset(struct snd_soc_component *component,
+ bool swap_dai_configuration)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ int i, err = 0;
+
+ dev_dbg(component->dev, "init audio codec");
+
+ for (i = 0; i < ARRAY_SIZE(cpcap_default_regs); i++) {
+ err = regmap_update_bits(cpcap->regmap,
+ cpcap_default_regs[i].reg,
+ cpcap_default_regs[i].mask,
+ cpcap_default_regs[i].val);
+ if (err)
+ return err;
+ }
+
+ /* setup default settings */
+ err = cpcap_dai_mux(cpcap, swap_dai_configuration);
+ if (err)
+ return err;
+
+ err = cpcap_set_sysclk(cpcap, CPCAP_DAI_HIFI, 0, 26000000);
+ if (err)
+ return err;
+ err = cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, 0, 26000000);
+ if (err)
+ return err;
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_HIFI, 48000);
+ if (err)
+ return err;
+
+ err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, 48000);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static irqreturn_t cpcap_hs_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mask = SND_JACK_HEADSET;
+ int val;
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S))) {
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_TXI, val, val);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_RXOA, val, val);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+
+ /* Give PTTS time to settle */
+ msleep(20);
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS2,
+ BIT(CPCAP_BIT_PTT_S))) {
+ /* Headphones detected. (May also be a headset with the
+ * MFB pressed.)
+ */
+ status = SND_JACK_HEADPHONE;
+ dev_info(component->dev, "HP plugged in\n");
+ } else if (regmap_test_bits(regmap, CPCAP_REG_INTS1,
+ BIT(CPCAP_BIT_MB2_S)) == 1) {
+ status = SND_JACK_HEADSET;
+ dev_info(component->dev, "HS plugged in\n");
+ } else
+ dev_info(component->dev, "Unsupported HS plugged in\n");
+ } else {
+ bool mic = cpcap->jack.status & SND_JACK_MICROPHONE;
+
+ dev_info(component->dev, "H%s disconnect\n", mic ? "S" : "P");
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, val, 0);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, val, 0);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+
+ mask |= SND_JACK_BTN_0;
+ }
+
+ snd_soc_jack_report(&cpcap->jack, status, mask);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_mb2_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mb2;
+ int ptt;
+
+ if (regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S)) == 1)
+ return IRQ_HANDLED;
+
+ mb2 = regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_MB2_S));
+ ptt = regmap_test_bits(regmap, CPCAP_REG_INTS2, BIT(CPCAP_BIT_PTT_S));
+
+ /* Initial detection might have been with MFB pressed */
+ if (!(cpcap->jack.status & SND_JACK_MICROPHONE)) {
+ if (ptt == 1 && mb2 == 1) {
+ dev_info(component->dev, "MIC plugged in\n");
+ snd_soc_jack_report(&cpcap->jack, SND_JACK_MICROPHONE,
+ SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ if (!mb2 || !ptt)
+ status = SND_JACK_BTN_0;
+
+ snd_soc_jack_report(&cpcap->jack, status, SND_JACK_BTN_0);
+
+ return IRQ_HANDLED;
+}
+
+static int cpcap_soc_probe(struct snd_soc_component *component)
+{
+ struct platform_device *pdev = to_platform_device(component->dev);
+ struct snd_soc_card *card = component->card;
+ struct cpcap_audio *cpcap;
+ int err;
+
+ cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL);
+ if (!cpcap)
+ return -ENOMEM;
+
+ snd_soc_component_set_drvdata(component, cpcap);
+ cpcap->component = component;
+
+ cpcap->vaudio = devm_regulator_get(component->dev, "VAUDIO");
+ if (IS_ERR(cpcap->vaudio))
+ return dev_err_probe(component->dev, PTR_ERR(cpcap->vaudio),
+ "Cannot get VAUDIO regulator\n");
+
+ err = snd_soc_card_jack_new(card, "Headphones",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &cpcap->jack);
+ if (err < 0) {
+ dev_err(component->dev, "Cannot create HS jack: %i\n", err);
+ return err;
+ }
+
+ snd_jack_set_key(cpcap->jack.jack, SND_JACK_BTN_0, KEY_MEDIA);
+
+ cpcap->regmap = dev_get_regmap(component->dev->parent, NULL);
+ if (!cpcap->regmap)
+ return -ENODEV;
+ snd_soc_component_init_regmap(component, cpcap->regmap);
+
+ err = cpcap_get_vendor(component->dev, cpcap->regmap, &cpcap->vendor);
+ if (err)
+ return err;
+
+ cpcap->hsirq = platform_get_irq_byname(pdev, "hs");
+ if (cpcap->hsirq < 0)
+ return cpcap->hsirq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->hsirq, NULL,
+ cpcap_hs_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-hs",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no HS irq%i: %i\n",
+ cpcap->hsirq, err);
+ return err;
+ }
+
+ cpcap->mb2irq = platform_get_irq_byname(pdev, "mb2");
+ if (cpcap->mb2irq < 0)
+ return cpcap->mb2irq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->mb2irq, NULL,
+ cpcap_mb2_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-mb2",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no MB2 irq%i: %i\n",
+ cpcap->mb2irq, err);
+ return err;
+ }
+
+ err = cpcap_audio_reset(component, false);
+ if (err)
+ return err;
+
+ cpcap_hs_irq_thread(cpcap->hsirq, component);
+
+ enable_irq_wake(cpcap->hsirq);
+ enable_irq_wake(cpcap->mb2irq);
+
+ return 0;
+}
+
+static void cpcap_soc_remove(struct snd_soc_component *component)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ disable_irq_wake(cpcap->hsirq);
+ disable_irq_wake(cpcap->mb2irq);
+}
+
+static int cpcap_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ /* VAIDIO should be kept in normal mode in order MIC/PTT to work */
+ if (cpcap->jack.status & SND_JACK_MICROPHONE)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_cpcap = {
+ .probe = cpcap_soc_probe,
+ .remove = cpcap_soc_remove,
+ .controls = cpcap_snd_controls,
+ .num_controls = ARRAY_SIZE(cpcap_snd_controls),
+ .dapm_widgets = cpcap_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .set_bias_level = cpcap_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cpcap_codec_probe(struct platform_device *pdev)
+{
+ struct device_node *codec_node =
+ of_get_child_by_name(pdev->dev.parent->of_node, "audio-codec");
+ if (!codec_node)
+ return -ENODEV;
+
+ pdev->dev.of_node = codec_node;
+
+ return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cpcap,
+ cpcap_dai, ARRAY_SIZE(cpcap_dai));
+}
+
+static struct platform_driver cpcap_codec_driver = {
+ .probe = cpcap_codec_probe,
+ .driver = {
+ .name = "cpcap-codec",
+ },
+};
+module_platform_driver(cpcap_codec_driver);
+
+MODULE_ALIAS("platform:cpcap-codec");
+MODULE_DESCRIPTION("ASoC CPCAP codec driver");
+MODULE_AUTHOR("Sebastian Reichel");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index 1c895a53001d..32b6a417d0e8 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA SoC CQ0093 Voice Codec Driver for DaVinci platforms
*
* Copyright (C) 2010 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -43,9 +30,9 @@ static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0),
};
-static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
+static int cq93vc_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 reg;
if (mute)
@@ -53,7 +40,7 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
+ snd_soc_component_update_bits(component, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
reg);
return 0;
@@ -72,23 +59,23 @@ static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
-static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
+static int cq93vc_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_ON);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
case SND_SOC_BIAS_OFF:
/* force all power off */
- snd_soc_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
}
@@ -100,8 +87,9 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
static const struct snd_soc_dai_ops cq93vc_dai_ops = {
- .digital_mute = cq93vc_mute,
+ .mute_stream = cq93vc_mute,
.set_sysclk = cq93vc_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cq93vc_dai = {
@@ -121,30 +109,29 @@ static struct snd_soc_dai_driver cq93vc_dai = {
.ops = &cq93vc_dai_ops,
};
-static struct regmap *cq93vc_get_regmap(struct device *dev)
+static int cq93vc_probe(struct snd_soc_component *component)
{
- struct davinci_vc *davinci_vc = dev->platform_data;
+ struct davinci_vc *davinci_vc = component->dev->platform_data;
- return davinci_vc->regmap;
+ snd_soc_component_init_regmap(component, davinci_vc->regmap);
+
+ return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
- .set_bias_level = cq93vc_set_bias_level,
- .get_regmap = cq93vc_get_regmap,
- .controls = cq93vc_snd_controls,
- .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
+static const struct snd_soc_component_driver soc_component_dev_cq93vc = {
+ .set_bias_level = cq93vc_set_bias_level,
+ .probe = cq93vc_probe,
+ .controls = cq93vc_snd_controls,
+ .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int cq93vc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_cq93vc, &cq93vc_dai, 1);
-}
-
-static int cq93vc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cq93vc, &cq93vc_dai, 1);
}
static struct platform_driver cq93vc_codec_driver = {
@@ -153,7 +140,6 @@ static struct platform_driver cq93vc_codec_driver = {
},
.probe = cq93vc_platform_probe,
- .remove = cq93vc_platform_remove,
};
module_platform_driver(cq93vc_codec_driver);
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
new file mode 100644
index 000000000000..f78a85b89d95
--- /dev/null
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 Google, Inc.
+ *
+ * ChromeOS Embedded Controller codec driver.
+ *
+ * This driver uses the cros-ec interface to communicate with the ChromeOS
+ * EC for audio function.
+ */
+
+#include <crypto/sha2.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/string_choices.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct cros_ec_codec_priv {
+ struct device *dev;
+ struct cros_ec_device *ec_device;
+
+ /* common */
+ uint32_t ec_capabilities;
+
+ uint64_t ec_shm_addr;
+ uint32_t ec_shm_len;
+
+ uint64_t ap_shm_phys_addr;
+ uint32_t ap_shm_len;
+ uint64_t ap_shm_addr;
+ uint64_t ap_shm_last_alloc;
+
+ /* DMIC */
+ atomic_t dmic_probed;
+
+ /* I2S_RX */
+ uint32_t i2s_rx_bclk_ratio;
+
+ /* WoV */
+ bool wov_enabled;
+ uint8_t *wov_audio_shm_p;
+ uint32_t wov_audio_shm_len;
+ uint8_t wov_audio_shm_type;
+ uint8_t *wov_lang_shm_p;
+ uint32_t wov_lang_shm_len;
+ uint8_t wov_lang_shm_type;
+
+ struct mutex wov_dma_lock;
+ uint8_t wov_buf[64000];
+ uint32_t wov_rp, wov_wp;
+ size_t wov_dma_offset;
+ bool wov_burst_read;
+ struct snd_pcm_substream *wov_substream;
+ struct delayed_work wov_copy_work;
+ struct notifier_block wov_notifier;
+};
+
+static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap)
+{
+ return priv->ec_capabilities & BIT(cap);
+}
+
+static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd,
+ uint8_t *out, size_t outsize,
+ uint8_t *in, size_t insize)
+{
+ int ret;
+ struct cros_ec_command *msg;
+
+ msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = cmd;
+ msg->outsize = outsize;
+ msg->insize = insize;
+
+ if (outsize)
+ memcpy(msg->data, out, outsize);
+
+ ret = cros_ec_cmd_xfer_status(ec_dev, msg);
+ if (ret < 0)
+ goto error;
+
+ if (in && insize)
+ memcpy(in, msg->data, insize);
+
+ ret = 0;
+error:
+ kfree(msg);
+ return ret;
+}
+
+static int dmic_get_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_dmic p;
+ struct ec_response_ec_codec_dmic_get_gain_idx r;
+ int ret;
+
+ p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
+ p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0)
+ return ret;
+ ucontrol->value.integer.value[0] = r.gain;
+
+ p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
+ p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0)
+ return ret;
+ ucontrol->value.integer.value[1] = r.gain;
+
+ return 0;
+}
+
+static int dmic_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *control =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max_dmic_gain = control->max;
+ int left = ucontrol->value.integer.value[0];
+ int right = ucontrol->value.integer.value[1];
+ struct ec_param_ec_codec_dmic p;
+ int ret;
+
+ if (left > max_dmic_gain || right > max_dmic_gain)
+ return -EINVAL;
+
+ dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right);
+
+ p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
+ p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;
+ p.set_gain_idx_param.gain = left;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX;
+ p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1;
+ p.set_gain_idx_param.gain = right;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0);
+
+enum {
+ DMIC_CTL_GAIN = 0,
+};
+
+static struct snd_kcontrol_new dmic_controls[] = {
+ [DMIC_CTL_GAIN] =
+ SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM,
+ 0, 0, 0, dmic_get_gain, dmic_put_gain,
+ dmic_gain_tlv),
+};
+
+static int dmic_probe(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct device *dev = priv->dev;
+ struct soc_mixer_control *control;
+ struct ec_param_ec_codec_dmic p;
+ struct ec_response_ec_codec_dmic_get_max_gain r;
+ int ret;
+
+ if (!atomic_add_unless(&priv->dmic_probed, 1, 1))
+ return 0;
+
+ p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN;
+
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret < 0) {
+ dev_warn(dev, "get_max_gain() unsupported\n");
+ return 0;
+ }
+
+ dev_dbg(dev, "max gain = %d\n", r.max_gain);
+
+ control = (struct soc_mixer_control *)
+ dmic_controls[DMIC_CTL_GAIN].private_value;
+ control->max = r.max_gain;
+ control->platform_max = r.max_gain;
+
+ return snd_soc_add_component_controls(component,
+ &dmic_controls[DMIC_CTL_GAIN], 1);
+}
+
+static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+ enum ec_codec_i2s_rx_sample_depth depth;
+ uint32_t bclk;
+ int ret;
+
+ if (params_rate(params) != 48000)
+ return -EINVAL;
+
+ switch (params_width(params)) {
+ case 16:
+ depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
+ break;
+ case 24:
+ depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "set depth to %u\n", depth);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH;
+ p.set_sample_depth_param.depth = depth;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ if (priv->i2s_rx_bclk_ratio)
+ bclk = params_rate(params) * priv->i2s_rx_bclk_ratio;
+ else
+ bclk = snd_soc_params_to_bclk(params);
+
+ dev_dbg(component->dev, "set bclk to %u\n", bclk);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_BCLK;
+ p.set_bclk_param.bclk = bclk;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static int i2s_rx_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ priv->i2s_rx_bclk_ratio = ratio;
+ return 0;
+}
+
+static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p;
+ enum ec_codec_i2s_rx_daifmt daifmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "set format to %u\n", daifmt);
+
+ p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT;
+ p.set_daifmt_param.daifmt = daifmt;
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static const struct snd_soc_dai_ops i2s_rx_dai_ops = {
+ .hw_params = i2s_rx_hw_params,
+ .set_fmt = i2s_rx_set_fmt,
+ .set_bclk_ratio = i2s_rx_set_bclk_ratio,
+};
+
+static int i2s_rx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_i2s_rx p = {};
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(component->dev, "enable I2S RX\n");
+ p.cmd = EC_CODEC_I2S_RX_ENABLE;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(component->dev, "disable I2S RX\n");
+ p.cmd = EC_CODEC_I2S_RX_DISABLE;
+ break;
+ default:
+ return 0;
+ }
+
+ return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+}
+
+static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = {
+ {"I2S RX", NULL, "DMIC"},
+ {"I2S RX", NULL, "I2S RX Enable"},
+};
+
+static struct snd_soc_dai_driver i2s_rx_dai_driver = {
+ .name = "EC Codec I2S RX",
+ .capture = {
+ .stream_name = "I2S Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &i2s_rx_dai_ops,
+};
+
+static int i2s_rx_probe(struct snd_soc_component *component)
+{
+ return dmic_probe(component);
+}
+
+static const struct snd_soc_component_driver i2s_rx_component_driver = {
+ .probe = i2s_rx_probe,
+ .dapm_widgets = i2s_rx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
+ .dapm_routes = i2s_rx_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
+ .endianness = 1,
+};
+
+static void *wov_map_shm(struct cros_ec_codec_priv *priv,
+ uint8_t shm_id, uint32_t *len, uint8_t *type)
+{
+ struct ec_param_ec_codec p;
+ struct ec_response_ec_codec_get_shm_addr r;
+ uint32_t req, offset;
+
+ p.cmd = EC_CODEC_GET_SHM_ADDR;
+ p.get_shm_addr_param.shm_id = shm_id;
+ if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r)) < 0) {
+ dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n");
+ return NULL;
+ }
+
+ dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len);
+
+ *len = r.len;
+ *type = r.type;
+
+ switch (r.type) {
+ case EC_CODEC_SHM_TYPE_EC_RAM:
+ return (void __force *)devm_ioremap_wc(priv->dev,
+ r.phys_addr + priv->ec_shm_addr, r.len);
+ case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
+ if (r.phys_addr) {
+ dev_err(priv->dev, "unknown status\n");
+ return NULL;
+ }
+
+ req = round_up(r.len, PAGE_SIZE);
+ dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req);
+
+ if (priv->ap_shm_last_alloc + req >
+ priv->ap_shm_phys_addr + priv->ap_shm_len) {
+ dev_err(priv->dev, "insufficient space for AP SHM\n");
+ return NULL;
+ }
+
+ dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n",
+ priv->ap_shm_last_alloc, req);
+
+ p.cmd = EC_CODEC_SET_SHM_ADDR;
+ p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc;
+ p.set_shm_addr_param.len = req;
+ p.set_shm_addr_param.shm_id = shm_id;
+ if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ NULL, 0) < 0) {
+ dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n");
+ return NULL;
+ }
+
+ /*
+ * Note: EC codec only requests for `r.len' but we allocate
+ * round up PAGE_SIZE `req'.
+ */
+ offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr;
+ priv->ap_shm_last_alloc += req;
+
+ return (void *)(uintptr_t)(priv->ap_shm_addr + offset);
+ default:
+ return NULL;
+ }
+}
+
+static bool wov_queue_full(struct cros_ec_codec_priv *priv)
+{
+ return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp;
+}
+
+static size_t wov_queue_size(struct cros_ec_codec_priv *priv)
+{
+ if (priv->wov_wp >= priv->wov_rp)
+ return priv->wov_wp - priv->wov_rp;
+ else
+ return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp;
+}
+
+static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len)
+{
+ struct snd_pcm_runtime *runtime = priv->wov_substream->runtime;
+ size_t req;
+
+ while (len) {
+ req = min(len, runtime->dma_bytes - priv->wov_dma_offset);
+ if (priv->wov_wp >= priv->wov_rp)
+ req = min(req, (size_t)priv->wov_wp - priv->wov_rp);
+ else
+ req = min(req, sizeof(priv->wov_buf) - priv->wov_rp);
+
+ memcpy(runtime->dma_area + priv->wov_dma_offset,
+ priv->wov_buf + priv->wov_rp, req);
+
+ priv->wov_dma_offset += req;
+ if (priv->wov_dma_offset == runtime->dma_bytes)
+ priv->wov_dma_offset = 0;
+
+ priv->wov_rp += req;
+ if (priv->wov_rp == sizeof(priv->wov_buf))
+ priv->wov_rp = 0;
+
+ len -= req;
+ }
+
+ snd_pcm_period_elapsed(priv->wov_substream);
+}
+
+static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv)
+{
+ size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
+
+ while (period_bytes && wov_queue_size(priv) >= period_bytes) {
+ wov_queue_dequeue(priv, period_bytes);
+ period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream);
+ }
+}
+
+static void wov_queue_enqueue(struct cros_ec_codec_priv *priv,
+ uint8_t *addr, size_t len, bool iomem)
+{
+ size_t req;
+
+ while (len) {
+ if (wov_queue_full(priv)) {
+ wov_queue_try_dequeue(priv);
+
+ if (wov_queue_full(priv)) {
+ dev_err(priv->dev, "overrun detected\n");
+ return;
+ }
+ }
+
+ if (priv->wov_wp >= priv->wov_rp)
+ req = sizeof(priv->wov_buf) - priv->wov_wp;
+ else
+ /* Note: waste 1-byte to differentiate full and empty */
+ req = priv->wov_rp - priv->wov_wp - 1;
+ req = min(req, len);
+
+ if (iomem)
+ memcpy_fromio(priv->wov_buf + priv->wov_wp,
+ (void __force __iomem *)addr, req);
+ else
+ memcpy(priv->wov_buf + priv->wov_wp, addr, req);
+
+ priv->wov_wp += req;
+ if (priv->wov_wp == sizeof(priv->wov_buf))
+ priv->wov_wp = 0;
+
+ addr += req;
+ len -= req;
+ }
+
+ wov_queue_try_dequeue(priv);
+}
+
+static int wov_read_audio_shm(struct cros_ec_codec_priv *priv)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_read_audio_shm r;
+ int ret;
+
+ p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n");
+ return ret;
+ }
+
+ if (!r.len)
+ dev_dbg(priv->dev, "no data, sleep\n");
+ else
+ wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len,
+ priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM);
+ return -EAGAIN;
+}
+
+static int wov_read_audio(struct cros_ec_codec_priv *priv)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_read_audio r;
+ int remain = priv->wov_burst_read ? 16000 : 320;
+ int ret;
+
+ while (remain >= 0) {
+ p.cmd = EC_CODEC_WOV_READ_AUDIO;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to EC_CODEC_WOV_READ_AUDIO\n");
+ return ret;
+ }
+
+ if (!r.len) {
+ dev_dbg(priv->dev, "no data, sleep\n");
+ priv->wov_burst_read = false;
+ break;
+ }
+
+ wov_queue_enqueue(priv, r.buf, r.len, false);
+ remain -= r.len;
+ }
+
+ return -EAGAIN;
+}
+
+static void wov_copy_work(struct work_struct *w)
+{
+ struct cros_ec_codec_priv *priv =
+ container_of(w, struct cros_ec_codec_priv, wov_copy_work.work);
+ int ret;
+
+ mutex_lock(&priv->wov_dma_lock);
+ if (!priv->wov_substream) {
+ dev_warn(priv->dev, "no pcm substream\n");
+ goto leave;
+ }
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM))
+ ret = wov_read_audio_shm(priv);
+ else
+ ret = wov_read_audio(priv);
+
+ if (ret == -EAGAIN)
+ schedule_delayed_work(&priv->wov_copy_work,
+ msecs_to_jiffies(10));
+ else if (ret)
+ dev_err(priv->dev, "failed to read audio data\n");
+leave:
+ mutex_unlock(&priv->wov_dma_lock);
+}
+
+static int wov_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->wov_enabled;
+ return 0;
+}
+
+static int wov_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+ struct ec_param_ec_codec_wov p;
+ int ret;
+
+ if (priv->wov_enabled != enabled) {
+ if (enabled)
+ p.cmd = EC_CODEC_WOV_ENABLE;
+ else
+ p.cmd = EC_CODEC_WOV_DISABLE;
+
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to %s wov\n",
+ str_enable_disable(enabled));
+ return ret;
+ }
+
+ priv->wov_enabled = enabled;
+ }
+
+ return 0;
+}
+
+static int wov_set_lang_shm(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, size_t size, uint8_t *digest)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param;
+ int ret;
+
+ if (size > priv->wov_lang_shm_len) {
+ dev_err(priv->dev, "no enough SHM size: %d\n",
+ priv->wov_lang_shm_len);
+ return -EIO;
+ }
+
+ switch (priv->wov_lang_shm_type) {
+ case EC_CODEC_SHM_TYPE_EC_RAM:
+ memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p,
+ buf, size);
+ memset_io((void __force __iomem *)priv->wov_lang_shm_p + size,
+ 0, priv->wov_lang_shm_len - size);
+ break;
+ case EC_CODEC_SHM_TYPE_SYSTEM_RAM:
+ memcpy(priv->wov_lang_shm_p, buf, size);
+ memset(priv->wov_lang_shm_p + size, 0,
+ priv->wov_lang_shm_len - size);
+
+ /* make sure write to memory before calling host command */
+ wmb();
+ break;
+ }
+
+ p.cmd = EC_CODEC_WOV_SET_LANG_SHM;
+ memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
+ pp->total_len = size;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wov_set_lang(struct cros_ec_codec_priv *priv,
+ uint8_t *buf, size_t size, uint8_t *digest)
+{
+ struct ec_param_ec_codec_wov p;
+ struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param;
+ size_t i, req;
+ int ret;
+
+ for (i = 0; i < size; i += req) {
+ req = min(size - i, ARRAY_SIZE(pp->buf));
+
+ p.cmd = EC_CODEC_WOV_SET_LANG;
+ memcpy(pp->hash, digest, SHA256_DIGEST_SIZE);
+ pp->total_len = size;
+ pp->offset = i;
+ memcpy(pp->buf, buf + i, req);
+ pp->len = req;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int wov_hotword_model_put(struct snd_kcontrol *kcontrol,
+ const unsigned int __user *bytes,
+ unsigned int size)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ struct ec_param_ec_codec_wov p;
+ struct ec_response_ec_codec_wov_get_lang r;
+ uint8_t digest[SHA256_DIGEST_SIZE];
+ uint8_t *buf;
+ int ret;
+
+ /* Skips the TLV header. */
+ bytes += 2;
+ size -= 8;
+
+ dev_dbg(priv->dev, "%s: size=%d\n", __func__, size);
+
+ buf = memdup_user(bytes, size);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ sha256(buf, size, digest);
+ dev_dbg(priv->dev, "hash=%*phN\n", SHA256_DIGEST_SIZE, digest);
+
+ p.cmd = EC_CODEC_WOV_GET_LANG;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret)
+ goto leave;
+
+ if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) {
+ dev_dbg(priv->dev, "not updated");
+ goto leave;
+ }
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM))
+ ret = wov_set_lang_shm(priv, buf, size, digest);
+ else
+ ret = wov_set_lang(priv, buf, size, digest);
+
+leave:
+ kfree(buf);
+ return ret;
+}
+
+static struct snd_kcontrol_new wov_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0,
+ wov_enable_get, wov_enable_put),
+ SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL,
+ wov_hotword_model_put),
+};
+
+static struct snd_soc_dai_driver wov_dai_driver = {
+ .name = "Wake on Voice",
+ .capture = {
+ .stream_name = "WoV Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static int wov_host_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend, void *notify)
+{
+ struct cros_ec_codec_priv *priv =
+ container_of(nb, struct cros_ec_codec_priv, wov_notifier);
+ u32 host_event;
+
+ dev_dbg(priv->dev, "%s\n", __func__);
+
+ host_event = cros_ec_get_host_event(priv->ec_device);
+ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) {
+ schedule_delayed_work(&priv->wov_copy_work, 0);
+ return NOTIFY_OK;
+ } else {
+ return NOTIFY_DONE;
+ }
+}
+
+static int wov_probe(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_init(&priv->wov_dma_lock);
+ INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work);
+
+ priv->wov_notifier.notifier_call = wov_host_event;
+ ret = blocking_notifier_chain_register(
+ &priv->ec_device->event_notifier, &priv->wov_notifier);
+ if (ret)
+ return ret;
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) {
+ priv->wov_lang_shm_p = wov_map_shm(priv,
+ EC_CODEC_SHM_ID_WOV_LANG,
+ &priv->wov_lang_shm_len,
+ &priv->wov_lang_shm_type);
+ if (!priv->wov_lang_shm_p)
+ return -EFAULT;
+ }
+
+ if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) {
+ priv->wov_audio_shm_p = wov_map_shm(priv,
+ EC_CODEC_SHM_ID_WOV_AUDIO,
+ &priv->wov_audio_shm_len,
+ &priv->wov_audio_shm_type);
+ if (!priv->wov_audio_shm_p)
+ return -EFAULT;
+ }
+
+ return dmic_probe(component);
+}
+
+static void wov_remove(struct snd_soc_component *component)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ blocking_notifier_chain_unregister(
+ &priv->ec_device->event_notifier, &priv->wov_notifier);
+}
+
+static int wov_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hw_param = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .buffer_bytes_max = 0x20000,
+ };
+
+ return snd_soc_set_runtime_hwparams(substream, &hw_param);
+}
+
+static int wov_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->wov_dma_lock);
+ priv->wov_substream = substream;
+ priv->wov_rp = priv->wov_wp = 0;
+ priv->wov_dma_offset = 0;
+ priv->wov_burst_read = true;
+ mutex_unlock(&priv->wov_dma_lock);
+
+ return 0;
+}
+
+static int wov_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->wov_dma_lock);
+ wov_queue_dequeue(priv, wov_queue_size(priv));
+ priv->wov_substream = NULL;
+ mutex_unlock(&priv->wov_dma_lock);
+
+ cancel_delayed_work_sync(&priv->wov_copy_work);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cros_ec_codec_priv *priv =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, priv->wov_dma_offset);
+}
+
+static int wov_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ return 0;
+}
+
+static const struct snd_soc_component_driver wov_component_driver = {
+ .probe = wov_probe,
+ .remove = wov_remove,
+ .controls = wov_controls,
+ .num_controls = ARRAY_SIZE(wov_controls),
+ .open = wov_pcm_open,
+ .hw_params = wov_pcm_hw_params,
+ .hw_free = wov_pcm_hw_free,
+ .pointer = wov_pcm_pointer,
+ .pcm_construct = wov_pcm_new,
+};
+
+static int cros_ec_codec_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_codec_priv *priv;
+ struct ec_param_ec_codec p;
+ struct ec_response_ec_codec_get_capabilities r;
+ int ret;
+#ifdef CONFIG_OF
+ struct resource res;
+ u64 ec_shm_size;
+ const __be32 *regaddr_p;
+#endif
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+#ifdef CONFIG_OF
+ regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL);
+ if (regaddr_p) {
+ priv->ec_shm_addr = of_read_number(regaddr_p, 2);
+ priv->ec_shm_len = ec_shm_size;
+
+ dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n",
+ priv->ec_shm_addr, priv->ec_shm_len);
+ }
+
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (!ret) {
+ priv->ap_shm_phys_addr = res.start;
+ priv->ap_shm_len = resource_size(&res);
+ priv->ap_shm_addr =
+ (uint64_t)(uintptr_t)devm_ioremap_wc(
+ dev, priv->ap_shm_phys_addr,
+ priv->ap_shm_len);
+ priv->ap_shm_last_alloc = priv->ap_shm_phys_addr;
+
+ dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n",
+ priv->ap_shm_phys_addr, priv->ap_shm_len);
+ }
+#endif
+
+ priv->dev = dev;
+ priv->ec_device = ec_device;
+ atomic_set(&priv->dmic_probed, 0);
+
+ p.cmd = EC_CODEC_GET_CAPABILITIES;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC,
+ (uint8_t *)&p, sizeof(p),
+ (uint8_t *)&r, sizeof(r));
+ if (ret) {
+ dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n");
+ return ret;
+ }
+ priv->ec_capabilities = r.capabilities;
+
+ /* Reset EC codec i2s rx. */
+ p.cmd = EC_CODEC_I2S_RX_RESET;
+ ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX,
+ (uint8_t *)&p, sizeof(p), NULL, 0);
+ if (ret == -ENOPROTOOPT) {
+ dev_info(dev,
+ "Missing reset command. Please update EC firmware.\n");
+ } else if (ret) {
+ dev_err(dev, "failed to EC_CODEC_I2S_RESET: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver,
+ &i2s_rx_dai_driver, 1);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(dev, &wov_component_driver,
+ &wov_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cros_ec_codec_of_match[] = {
+ { .compatible = "google,cros-ec-codec" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cros_ec_codec_acpi_id[] = {
+ { "GOOG0013", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_ec_codec_acpi_id);
+#endif
+
+static struct platform_driver cros_ec_codec_platform_driver = {
+ .driver = {
+ .name = "cros-ec-codec",
+ .of_match_table = of_match_ptr(cros_ec_codec_of_match),
+ .acpi_match_table = ACPI_PTR(cros_ec_codec_acpi_id),
+ },
+ .probe = cros_ec_codec_platform_probe,
+};
+
+module_platform_driver(cros_ec_codec_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ChromeOS EC codec driver");
+MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>");
+MODULE_ALIAS("platform:cros-ec-codec");
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
new file mode 100644
index 000000000000..923f1857e45b
--- /dev/null
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -0,0 +1,2451 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus common amplifier library.
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+#include <kunit/static_stub.h>
+#include <linux/device/faux.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/gpio/driver.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <sound/cs-amp-lib.h>
+
+#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
+#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
+
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
+#define HP_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+struct cs_amp_lib_test_priv {
+ struct faux_device *amp_dev;
+
+ struct cirrus_amp_efi_data *cal_blob;
+ struct list_head ctl_write_list;
+ u32 efi_attr;
+};
+
+struct cs_amp_lib_test_ctl_write_entry {
+ struct list_head list;
+ unsigned int value;
+ char name[16];
+};
+
+struct cs_amp_lib_test_param {
+ int num_amps;
+ int amp_index;
+};
+
+static struct cirrus_amp_efi_data *cs_amp_lib_test_cal_blob_dup(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_efi_data *temp;
+
+ KUNIT_ASSERT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ temp = kunit_kmalloc(test, priv->cal_blob->size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, temp);
+ memcpy(temp, priv->cal_blob, priv->cal_blob->size);
+
+ return temp;
+}
+
+static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ unsigned int blob_size;
+ int i;
+
+ blob_size = struct_size(priv->cal_blob, data, num_amps);
+
+ priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+
+ priv->cal_blob->size = blob_size;
+ priv->cal_blob->count = num_amps;
+
+ get_random_bytes(priv->cal_blob->data, flex_array_size(priv->cal_blob, data, num_amps));
+
+ /* Ensure all timestamps are non-zero to mark the entry valid. */
+ for (i = 0; i < num_amps; i++)
+ priv->cal_blob->data[i].calTime[0] |= 1;
+
+ /*
+ * Ensure that all UIDs are non-zero and unique.
+ * Make both words non-zero and not equal values, so that
+ * tests can verify that both words were checked or changed.
+ */
+ for (i = 0; i < num_amps; i++) {
+ *(u8 *)&priv->cal_blob->data[i].calTarget[0] = i + 1;
+ *(u8 *)&priv->cal_blob->data[i].calTarget[1] = i;
+ }
+}
+
+static u64 cs_amp_lib_test_get_target_uid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ u64 uid;
+
+ uid = priv->cal_blob->data[param->amp_index].calTarget[1];
+ uid <<= 32;
+ uid |= priv->cal_blob->data[param->amp_index].calTarget[0];
+
+ return uid;
+}
+
+/* Redirected get_efi_variable to simulate that the file is too short */
+static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ if (!buf) {
+ *size = offsetof(struct cirrus_amp_efi_data, data) - 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/* Should return -EOVERFLOW if the header is larger than the EFI data */
+static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_nohead);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+}
+
+/* Redirected get_efi_variable to simulate that the count is larger than the file */
+static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ if (!buf) {
+ /*
+ * Return a size that is shorter than required for the
+ * declared number of entries.
+ */
+ *size = priv->cal_blob->size - 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1);
+
+ return EFI_SUCCESS;
+}
+
+/* Should return -EOVERFLOW if the entry count is larger than the EFI data */
+static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_bad_count);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+}
+
+/* Redirected get_efi_variable to simulate that the variable not found */
+static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_NOT_FOUND;
+}
+
+/* If EFI doesn't contain a cal data variable the result should be -ENOENT */
+static void cs_amp_lib_test_no_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/* Redirected get_efi_variable to simulate reading a cal data blob */
+static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
+ static const efi_guid_t expected_guid = CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ if (returned_attr) {
+ if (priv->efi_attr)
+ *returned_attr = priv->efi_attr;
+ else
+ *returned_attr = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+#define CS_AMP_LIB_ZERO_FILLED_BLOB_SIZE \
+ struct_size_t(struct cirrus_amp_efi_data, data, 8)
+
+/* Redirected get_efi_variable to simulate reading a prealloced zero-filled blob */
+static efi_status_t cs_amp_lib_test_get_efi_variable_all_zeros(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
+ static const efi_guid_t expected_guid = CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = CS_AMP_LIB_ZERO_FILLED_BLOB_SIZE;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_EQ(test, *size, struct_size(priv->cal_blob, data, 8));
+ priv->cal_blob = kunit_kzalloc(test, CS_AMP_LIB_ZERO_FILLED_BLOB_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+ memset(buf, 0, CS_AMP_LIB_ZERO_FILLED_BLOB_SIZE);
+
+ if (returned_attr) {
+ if (priv->efi_attr)
+ *returned_attr = priv->efi_attr;
+ else
+ *returned_attr = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_hp_cal_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = L"SmartAmpCalibrationData";
+ static const efi_guid_t expected_guid =
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93);
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ if (returned_attr) {
+ *returned_attr = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* Get cal data block from HP variable. */
+static void cs_amp_lib_test_get_hp_efi_cal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_hp_cal_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_MEMEQ(test, &result_data, &priv->cal_blob->data[0], sizeof(result_data));
+}
+
+/* Get cal data block for a given amp, matched by target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ target_uid = cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL);
+ KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index without checking target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index with checked target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ target_uid = cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * Get cal data block for a given amp index with checked target UID.
+ * The UID does not match so the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ /* Get a target UID that won't match the entry */
+ target_uid = ~cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/*
+ * Get cal data block for a given amp, where the cal data does not
+ * specify calTarget so the lookup falls back to using the index
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Make all the target values zero so they are ignored */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] = 0;
+ priv->cal_blob->data[i].calTarget[1] = 0;
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and there isn't an
+ * index to fall back do, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values != bad_target_uid */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+ priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, -1,
+ &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and the index is
+ * out of range, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values != bad_target_uid */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+ priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, 99,
+ &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/*
+ * If the target UID isn't given, and the index is out of range, the
+ * result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 99, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/* If neither the target UID or the index is given the result should be -ENOENT. */
+static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/*
+ * If the UID is passed as 0 this must not match an entry with an
+ * unpopulated calTarget
+ */
+static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values zero so they are ignored */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] = 0;
+ priv->cal_blob->data[i].calTarget[1] = 0;
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+/*
+ * If an entry has a timestamp of 0 it should be ignored even if it has
+ * a matching target UID.
+ */
+static void cs_amp_lib_test_get_efi_cal_empty_entry_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ u64 uid;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Mark the 3rd entry invalid by zeroing calTime */
+ priv->cal_blob->data[2].calTime[0] = 0;
+ priv->cal_blob->data[2].calTime[1] = 0;
+
+ /* Get the UID value of the 3rd entry */
+ uid = priv->cal_blob->data[2].calTarget[1];
+ uid <<= 32;
+ uid |= priv->cal_blob->data[2].calTarget[0];
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ /* Lookup by UID should not find it */
+ KUNIT_EXPECT_EQ(test,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
+ uid, -1,
+ &result_data),
+ -ENOENT);
+
+ /* Get by index should ignore it */
+ KUNIT_EXPECT_EQ(test,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
+ 0, 2,
+ &result_data),
+ -ENOENT);
+}
+
+static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = {
+ .alg_id = 0x9f210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+
+static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name);
+ KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls);
+
+ entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry);
+
+ INIT_LIST_HEAD(&entry->list);
+ strscpy(entry->name, ctl_name, sizeof(entry->name));
+ entry->value = val;
+
+ list_add_tail(&entry->list, &priv->ctl_write_list);
+
+ return 0;
+}
+
+static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+ struct cirrus_amp_cal_data data;
+ struct cs_dsp *dsp;
+ int ret;
+
+ dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
+ dsp->dev = &priv->amp_dev->dev;
+
+ get_random_bytes(&data, sizeof(data));
+
+ /* Redirect calls to write firmware controls */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->write_cal_coeff,
+ cs_amp_lib_test_write_cal_coeff);
+
+ ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4);
+
+ /* Checksum control must be written last */
+ entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calR);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
+}
+
+static int cs_amp_lib_test_read_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 *val)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name);
+ KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls);
+
+ if (strcmp(ctl_name, controls->ambient) == 0)
+ *val = 19;
+ else if (strcmp(ctl_name, controls->calr) == 0)
+ *val = 1077;
+ else if (strcmp(ctl_name, controls->status) == 0)
+ *val = 2;
+ else
+ kunit_fail_current_test("Bad control '%s'\n", ctl_name);
+
+ return 0;
+}
+
+static void cs_amp_lib_test_read_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data data = { 0 };
+ struct cs_dsp *dsp;
+ int ret;
+
+ dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
+ dsp->dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->read_cal_coeff,
+ cs_amp_lib_test_read_cal_coeff);
+
+ ret = cs_amp_read_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, 19, data.calAmbient);
+ KUNIT_EXPECT_EQ(test, 1077, data.calR);
+ KUNIT_EXPECT_EQ(test, 2, data.calStatus);
+ KUNIT_EXPECT_NE(test, 0, data.calTime[0] | data.calTime[1]);
+}
+
+static void cs_amp_lib_test_write_ambient_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+ struct cs_dsp *dsp;
+ int ret;
+
+ dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
+ dsp->dev = &priv->amp_dev->dev;
+
+ /* Redirect calls to write firmware controls */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->write_cal_coeff,
+ cs_amp_lib_test_write_cal_coeff);
+
+ ret = cs_amp_write_ambient_temp(dsp, &cs_amp_lib_test_calibration_controls, 18);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 1);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient);
+ KUNIT_EXPECT_EQ(test, entry->value, 18);
+}
+
+static efi_status_t cs_amp_lib_test_set_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 attr,
+ unsigned long size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
+ static const efi_guid_t expected_guid = CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_ASSERT_NOT_NULL(test, name);
+ KUNIT_ASSERT_NOT_NULL(test, guid);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ KUNIT_ASSERT_NE(test, 0, size);
+
+ kunit_kfree(test, priv->cal_blob);
+ priv->cal_blob = kunit_kmalloc(test, size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+ memcpy(priv->cal_blob, buf, size);
+ priv->efi_attr = attr;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_set_efi_variable_denied(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 attr,
+ unsigned long size,
+ void *buf)
+{
+ return EFI_WRITE_PROTECTED;
+}
+
+#define CS_AMP_CAL_DEFAULT_EFI_ATTR \
+ (EFI_VARIABLE_NON_VOLATILE | \
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+ EFI_VARIABLE_RUNTIME_ACCESS)
+
+static void cs_amp_lib_test_create_new_cal_efi(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* For unspecified number of amps */
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, CS_AMP_CAL_DEFAULT_EFI_ATTR, priv->efi_attr);
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 1);
+ KUNIT_EXPECT_LE(test, priv->cal_blob->count, 8);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ for (i = 1; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* For 2 amps */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 2, &data));
+ KUNIT_EXPECT_EQ(test, CS_AMP_CAL_DEFAULT_EFI_ATTR, priv->efi_attr);
+ KUNIT_EXPECT_EQ(test, 2, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 2), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+
+ /* For 4 amps */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 4, &data));
+ KUNIT_EXPECT_EQ(test, 4, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 4), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+
+ /* For 6 amps */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+}
+
+static void cs_amp_lib_test_create_new_cal_efi_indexed(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* In slot 0 */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 0, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* In slot 1 */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[0], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* In slot 5 */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 5, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[5], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[0], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+}
+
+static void cs_amp_lib_test_create_new_cal_efi_indexed_no_max(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* In slot 0 with unspecified number of amps */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 0, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 1);
+ KUNIT_EXPECT_LE(test, priv->cal_blob->count, 8);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ for (i = 1; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* In slot 1 with unspecified number of amps */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 2);
+ KUNIT_EXPECT_LE(test, priv->cal_blob->count, 8);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[0], sizeof(data)));
+ for (i = 2; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* In slot 5 with unspecified number of amps */
+ priv->cal_blob = NULL;
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 5, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 6);
+ KUNIT_EXPECT_LE(test, priv->cal_blob->count, 8);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ for (i = 0; (i < 5) && (i < priv->cal_blob->count); i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[5], sizeof(data));
+ for (i = 6; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+}
+
+static void cs_amp_lib_test_grow_append_cal_efi(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* Initially 1 used entry grown to 2 entries */
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 2, &data));
+ KUNIT_EXPECT_EQ(test, CS_AMP_CAL_DEFAULT_EFI_ATTR, priv->efi_attr);
+ KUNIT_EXPECT_EQ(test, 2, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 2), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+
+ /* Initially 1 entry grown to 4 entries */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 4, &data));
+ KUNIT_EXPECT_EQ(test, 4, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 4), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+
+ /* Initially 2 entries grown to 4 entries */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+ KUNIT_ASSERT_EQ(test, 2, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 4, &data));
+ KUNIT_EXPECT_EQ(test, 4, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 4), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+
+ /* Initially 1 entry grown to 6 entries */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 4 entries grown to 6 entries */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+}
+
+static void cs_amp_lib_test_grow_append_cal_efi_indexed(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* Initially 1 entry grown to 2 entries using slot 1 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, 2, &data));
+ KUNIT_EXPECT_EQ(test, 2, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 2), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+
+ /* Initially 1 entry grown to 6 entries using slot 1 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 2 entries grown to 6 entries using slot 2 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+ KUNIT_ASSERT_EQ(test, 2, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 2, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 2 entries grown to 6 entries using slot 4 */
+ kunit_kfree(test, original_blob);
+ kunit_kfree(test, priv->cal_blob);
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+ KUNIT_ASSERT_EQ(test, 2, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 4, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+}
+
+static void cs_amp_lib_test_cal_efi_all_zeros_add_first(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ /* Simulate a BIOS reserving EFI space that is entirely zero-filled. */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_all_zeros);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /*
+ * Add an entry. The header should be filled in to match the
+ * original EFI variable size.
+ */
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 8), priv->cal_blob->size);
+ KUNIT_EXPECT_EQ(test, 8, priv->cal_blob->count);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ for (i = 1; i < priv->cal_blob->count; i++) {
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[1]);
+ }
+}
+
+static void cs_amp_lib_test_cal_efi_all_zeros_add_first_no_shrink(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ /* Simulate a BIOS reserving EFI space that is entirely zero-filled. */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_all_zeros);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /*
+ * Add an entry. The header should be filled in to match the
+ * original EFI variable size. A number of amps less than the
+ * available preallocated space does not shrink the EFI variable.
+ */
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 4, &data));
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 8), priv->cal_blob->size);
+ KUNIT_EXPECT_EQ(test, 8, priv->cal_blob->count);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ for (i = 1; i < priv->cal_blob->count; i++) {
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[1]);
+ }
+}
+
+static void cs_amp_lib_test_cal_efi_all_zeros_add_first_indexed(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ /* Simulate a BIOS reserving EFI space that is entirely zero-filled. */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_all_zeros);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /*
+ * Write entry to slot 2. The header should be filled in to match
+ * the original EFI variable size.
+ */
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 2, -1, &data));
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 8), priv->cal_blob->size);
+ KUNIT_EXPECT_EQ(test, 8, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[1]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[1]);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[2], sizeof(data));
+ for (i = 3; i < priv->cal_blob->count; i++) {
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[1]);
+ }
+}
+
+static void cs_amp_lib_test_cal_efi_all_zeros_add_first_indexed_no_shrink(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ /* Simulate a BIOS reserving EFI space that is entirely zero-filled. */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_all_zeros);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /*
+ * Write entry to slot 2. The header should be filled in to match
+ * the original EFI variable size. A number of amps less than the
+ * available preallocated space does not shrink the EFI variable.
+ */
+ get_random_bytes(&data, sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 2, 4, &data));
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 8), priv->cal_blob->size);
+ KUNIT_EXPECT_EQ(test, 8, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[1]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[1]);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[2], sizeof(data));
+ for (i = 3; i < priv->cal_blob->count; i++) {
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[i].calTime[1]);
+ }
+}
+
+static void cs_amp_lib_test_grow_append_cal_efi_indexed_no_max(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* Initially 1 entry adding slot 1 */
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 2);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ for (i = 2; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* Initially 1 entry adding slot 3 */
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 3, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 4);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ for (i = 4; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* Initially 2 entries adding slot 3 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+ KUNIT_ASSERT_EQ(test, 2, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 3, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 1);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ for (i = 4; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* Initially 4 entries adding slot 4 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 4, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 1);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ for (i = 5; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+
+ /* Initially 4 entries adding slot 6 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 6, -1, &data));
+ KUNIT_EXPECT_GE(test, priv->cal_blob->count, 1);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, priv->cal_blob->count),
+ priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[6], sizeof(data));
+ for (i = 7; i < priv->cal_blob->count; i++)
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[i], sizeof(data)));
+}
+
+static void cs_amp_lib_test_grow_cal_efi_replace_indexed(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* Initially 1 entry grown to 2 entries overwriting slot 0 */
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 0, 2, &data));
+ KUNIT_EXPECT_EQ(test, 2, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 2), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+
+ /* Initially 2 entries grown to 4 entries overwriting slot 1 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+ KUNIT_ASSERT_EQ(test, 2, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, 4, &data));
+ KUNIT_EXPECT_EQ(test, 4, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 4), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+
+ /* Initially 4 entries grown to 6 entries overwriting slot 1 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 4 entries grown to 6 entries overwriting slot 3 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 3, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 6 entries grown to 8 entries overwriting slot 4 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa; /* won't match */
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 4, 8, &data));
+ KUNIT_EXPECT_EQ(test, 8, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 8), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[6], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[7], sizeof(data)));
+}
+
+static void cs_amp_lib_test_grow_cal_efi_replace_by_uid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /* Initially 1 entry grown to 2 entries overwriting slot 0 */
+ cs_amp_lib_test_init_dummy_cal_blob(test, 1);
+ KUNIT_ASSERT_EQ(test, 1, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[0].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 2, &data));
+ KUNIT_EXPECT_EQ(test, 2, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 2), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[1], sizeof(data)));
+
+ /* Initially 2 entries grown to 4 entries overwriting slot 1 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+ KUNIT_ASSERT_EQ(test, 2, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[1].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 4, &data));
+ KUNIT_EXPECT_EQ(test, 4, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 4), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[2], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[3], sizeof(data)));
+
+ /* Initially 4 entries grown to 6 entries overwriting slot 1 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[1].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 4 entries grown to 6 entries overwriting slot 3 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[3].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 6, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[4], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[5], sizeof(data)));
+
+ /* Initially 6 entries grown to 8 entries overwriting slot 4 */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[4].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, 8, &data));
+ KUNIT_EXPECT_EQ(test, 8, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 8), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[6], sizeof(data)));
+ KUNIT_EXPECT_TRUE(test, mem_is_zero(&priv->cal_blob->data[7], sizeof(data)));
+}
+
+static void cs_amp_lib_test_cal_efi_replace_by_uid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+
+ /* Replace entry matching slot 0 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[0].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 4 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[4].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 3 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[3].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 5 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[5].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[5], sizeof(data));
+}
+
+static void cs_amp_lib_test_cal_efi_replace_by_index(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+
+ /*
+ * Replace entry matching slot 0.
+ * data.calTarget is deliberately set different to current calTarget
+ * of the slot to check that the index forces that slot to be used.
+ */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = ~priv->cal_blob->data[0].calTarget[0];
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 0, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 4 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = ~priv->cal_blob->data[4].calTarget[0];
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 4, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 3 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = ~priv->cal_blob->data[3].calTarget[0];
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 3, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 5 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = ~priv->cal_blob->data[5].calTarget[0];
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 5, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[5], sizeof(data));
+}
+
+static void cs_amp_lib_test_cal_efi_deduplicate(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+ int i;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ /*
+ * Replace entry matching slot 0.
+ * An active entry in slot 1 for the same UID should be marked empty.
+ * Other entries are unaltered.
+ */
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[1].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 0, -1, &data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[1]);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+
+ /*
+ * Replace entry matching slot 1.
+ * An active entry in slot 0 for the same UID should be marked empty.
+ * Other entries are unaltered.
+ */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[0].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[1]);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+
+ /*
+ * Replace entry matching slot 1.
+ * An active entry in slot 3 for the same UID should be marked empty.
+ * Other entries are unaltered.
+ */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ memcpy(data.calTarget, priv->cal_blob->data[3].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 1, -1, &data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[3].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[3].calTime[1]);
+
+ /*
+ * Worst case, all entries have the same UID
+ */
+ priv->cal_blob = NULL;
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ for (i = 0; i < priv->cal_blob->count; i++) {
+ priv->cal_blob->data[i].calTarget[0] = 0xe5e5e5e5;
+ priv->cal_blob->data[i].calTarget[1] = 0xa7a7a7a7;
+ }
+ memcpy(data.calTarget, priv->cal_blob->data[2].calTarget, sizeof(data.calTarget));
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 2, -1, &data));
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[0].calTime[1]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[1].calTime[1]);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[3].calTime[0]);
+ KUNIT_EXPECT_EQ(test, 0, priv->cal_blob->data[3].calTime[1]);
+}
+
+static void cs_amp_lib_test_cal_efi_find_free(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+
+ /*
+ * Slot 0 is empty.
+ * data.calTarget is set to a value that won't match any existing entry.
+ */
+ memset(&priv->cal_blob->data[0].calTime, 0, sizeof(priv->cal_blob->data[0].calTime));
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa;
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Slot 4 is empty */
+ memset(&priv->cal_blob->data[4].calTime, 0, sizeof(priv->cal_blob->data[4].calTime));
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa;
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Slot 3 is empty */
+ memset(&priv->cal_blob->data[3].calTime, 0, sizeof(priv->cal_blob->data[3].calTime));
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa;
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+
+ /* Replace entry matching slot 5 */
+ memset(&priv->cal_blob->data[5].calTime, 0, sizeof(priv->cal_blob->data[5].calTime));
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = 0xaaaaaaaa;
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[4], &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[5], sizeof(data));
+}
+
+static void cs_amp_lib_test_cal_efi_bad_cal_target(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+
+ /* Zero calTarget is illegal */
+ get_random_bytes(&data, sizeof(data));
+ memset(data.calTarget, 0, sizeof(data.calTarget));
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, -1, -1, &data), 0);
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, 0, -1, &data), 0);
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, 0, 2, &data), 0);
+}
+
+static void cs_amp_lib_test_cal_efi_write_denied(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable_denied);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 4);
+ KUNIT_ASSERT_EQ(test, 4, priv->cal_blob->count);
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+
+ /* Unspecified slot */
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, -1, -1, &data), 0);
+ KUNIT_EXPECT_MEMEQ(test, original_blob, priv->cal_blob, original_blob->size);
+
+ /* Unspecified slot with size */
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, -1, 6, &data), 0);
+ KUNIT_EXPECT_MEMEQ(test, original_blob, priv->cal_blob, original_blob->size);
+
+ /* Specified slot */
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, 1, -1, &data), 0);
+ KUNIT_EXPECT_MEMEQ(test, original_blob, priv->cal_blob, original_blob->size);
+
+ /* Specified slot with size */
+ KUNIT_EXPECT_LT(test, cs_amp_set_efi_calibration_data(dev, 1, 6, &data), 0);
+ KUNIT_EXPECT_MEMEQ(test, original_blob, priv->cal_blob, original_blob->size);
+}
+
+static void cs_amp_lib_test_cal_efi_attr_preserved(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_efi_variable);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+ memset(&priv->cal_blob->data[0], 0, sizeof(priv->cal_blob->data[0]));
+ get_random_bytes(&data, sizeof(data));
+
+ /* Set a non-standard attr to return from get_efi_variable() */
+ priv->efi_attr = EFI_VARIABLE_HARDWARE_ERROR_RECORD;
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, -1, -1, &data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_EQ(test, priv->efi_attr, EFI_VARIABLE_HARDWARE_ERROR_RECORD);
+}
+
+static efi_status_t cs_amp_lib_test_set_hp_efi_cal_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 attr,
+ unsigned long size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = HP_CALIBRATION_EFI_NAME;
+ static const efi_guid_t expected_guid = HP_CALIBRATION_EFI_GUID;
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_ASSERT_NOT_NULL(test, name);
+ KUNIT_ASSERT_NOT_NULL(test, guid);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_ACCESS_DENIED;
+
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ KUNIT_ASSERT_NE(test, 0, size);
+
+ kunit_kfree(test, priv->cal_blob);
+ priv->cal_blob = kunit_kmalloc(test, size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+ memcpy(priv->cal_blob, buf, size);
+ priv->efi_attr = attr;
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * If the HP EFI exists it should be the one that is updated.
+ */
+static void cs_amp_lib_test_cal_efi_update_hp(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const struct cirrus_amp_efi_data *original_blob;
+ struct cirrus_amp_cal_data data;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_hp_cal_efi_variable);
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->set_efi_variable,
+ cs_amp_lib_test_set_hp_efi_cal_variable);
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 6);
+ KUNIT_ASSERT_EQ(test, 6, priv->cal_blob->count);
+
+ /* Replace entry matching slot 4 */
+ original_blob = cs_amp_lib_test_cal_blob_dup(test);
+ get_random_bytes(&data, sizeof(data));
+ data.calTarget[0] = ~priv->cal_blob->data[4].calTarget[0];
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_set_efi_calibration_data(dev, 4, -1, &data));
+ KUNIT_EXPECT_EQ(test, 6, priv->cal_blob->count);
+ KUNIT_EXPECT_EQ(test, struct_size(priv->cal_blob, data, 6), priv->cal_blob->size);
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[0], &priv->cal_blob->data[0], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[1], &priv->cal_blob->data[1], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[2], &priv->cal_blob->data[2], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[3], &priv->cal_blob->data[3], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &data, &priv->cal_blob->data[4], sizeof(data));
+ KUNIT_EXPECT_MEMEQ(test, &original_blob->data[5], &priv->cal_blob->data[5], sizeof(data));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_not_present(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ KUNIT_EXPECT_EQ(test, -ENOENT, cs_amp_get_vendor_spkid(dev));
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d0(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd0;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d1(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd1;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_00(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ KUNIT_ASSERT_EQ(test, 0, efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID));
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d0(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d0);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d1(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d1);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_illegal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_00);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_buf_too_small(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_BUFFER_TOO_SMALL;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_oversize(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_buf_too_small);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_30(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x30;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_31(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x31;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_hp_30(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_30);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_31);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static int cs_amp_lib_test_case_init(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+ INIT_LIST_HEAD(&priv->ctl_write_list);
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs_amp_lib_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ return 0;
+}
+
+static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = {
+ { .num_amps = 2, .amp_index = 0 },
+ { .num_amps = 2, .amp_index = 1 },
+
+ { .num_amps = 3, .amp_index = 0 },
+ { .num_amps = 3, .amp_index = 1 },
+ { .num_amps = 3, .amp_index = 2 },
+
+ { .num_amps = 4, .amp_index = 0 },
+ { .num_amps = 4, .amp_index = 1 },
+ { .num_amps = 4, .amp_index = 2 },
+ { .num_amps = 4, .amp_index = 3 },
+
+ { .num_amps = 5, .amp_index = 0 },
+ { .num_amps = 5, .amp_index = 1 },
+ { .num_amps = 5, .amp_index = 2 },
+ { .num_amps = 5, .amp_index = 3 },
+ { .num_amps = 5, .amp_index = 4 },
+
+ { .num_amps = 6, .amp_index = 0 },
+ { .num_amps = 6, .amp_index = 1 },
+ { .num_amps = 6, .amp_index = 2 },
+ { .num_amps = 6, .amp_index = 3 },
+ { .num_amps = 6, .amp_index = 4 },
+ { .num_amps = 6, .amp_index = 5 },
+
+ { .num_amps = 8, .amp_index = 0 },
+ { .num_amps = 8, .amp_index = 1 },
+ { .num_amps = 8, .amp_index = 2 },
+ { .num_amps = 8, .amp_index = 3 },
+ { .num_amps = 8, .amp_index = 4 },
+ { .num_amps = 8, .amp_index = 5 },
+ { .num_amps = 8, .amp_index = 6 },
+ { .num_amps = 8, .amp_index = 7 },
+};
+
+static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d",
+ param->num_amps, param->amp_index);
+}
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
+ cs_amp_lib_test_get_cal_param_desc);
+
+static struct kunit_case cs_amp_lib_test_cases[] = {
+ /* Tests for getting calibration data from EFI */
+ KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
+ KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test),
+ KUNIT_CASE(cs_amp_lib_test_no_cal_data_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
+ KUNIT_CASE(cs_amp_lib_test_get_hp_efi_cal),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_empty_entry_test),
+
+ /* Tests for writing and reading calibration data */
+ KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
+ KUNIT_CASE(cs_amp_lib_test_read_cal_data_test),
+ KUNIT_CASE(cs_amp_lib_test_write_ambient_test),
+
+ /* Test cases for writing cal data to UEFI */
+ KUNIT_CASE(cs_amp_lib_test_create_new_cal_efi),
+ KUNIT_CASE(cs_amp_lib_test_create_new_cal_efi_indexed),
+ KUNIT_CASE(cs_amp_lib_test_create_new_cal_efi_indexed_no_max),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_all_zeros_add_first),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_all_zeros_add_first_no_shrink),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_all_zeros_add_first_indexed),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_all_zeros_add_first_indexed_no_shrink),
+ KUNIT_CASE(cs_amp_lib_test_grow_append_cal_efi),
+ KUNIT_CASE(cs_amp_lib_test_grow_append_cal_efi_indexed),
+ KUNIT_CASE(cs_amp_lib_test_grow_append_cal_efi_indexed_no_max),
+ KUNIT_CASE(cs_amp_lib_test_grow_cal_efi_replace_indexed),
+ KUNIT_CASE(cs_amp_lib_test_grow_cal_efi_replace_by_uid),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_replace_by_uid),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_replace_by_index),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_deduplicate),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_find_free),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_bad_cal_target),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_write_denied),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_attr_preserved),
+ KUNIT_CASE(cs_amp_lib_test_cal_efi_update_hp),
+
+ /* Test cases for speaker ID */
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_not_present),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d0),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d1),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_illegal),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+
+ { } /* terminator */
+};
+
+static struct kunit_suite cs_amp_lib_test_suite = {
+ .name = "snd-soc-cs-amp-lib-test",
+ .init = cs_amp_lib_test_case_init,
+ .test_cases = cs_amp_lib_test_cases,
+};
+
+kunit_test_suite(cs_amp_lib_test_suite);
+
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
new file mode 100644
index 000000000000..8c9fd9980a7d
--- /dev/null
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -0,0 +1,737 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus Logic Smart Amplifiers
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <asm/byteorder.h>
+#include <kunit/static_stub.h>
+#include <linux/cleanup.h>
+#include <linux/debugfs.h>
+#include <linux/dev_printk.h>
+#include <linux/efi.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
+#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
+
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
+#define HP_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+
+static const struct cs_amp_lib_cal_efivar {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+} cs_amp_lib_cal_efivars[] = {
+ {
+ .name = HP_CALIBRATION_EFI_NAME,
+ .guid = &HP_CALIBRATION_EFI_GUID,
+ },
+ {
+ .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
+ .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
+ },
+};
+
+#define CS_AMP_CAL_DEFAULT_EFI_ATTR \
+ (EFI_VARIABLE_NON_VOLATILE | \
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+ EFI_VARIABLE_RUNTIME_ACCESS)
+
+/* Offset from Unix time to Windows time (100ns since 1 Jan 1601) */
+#define UNIX_TIME_TO_WINDOWS_TIME_OFFSET 116444736000000000ULL
+
+static DEFINE_MUTEX(cs_amp_efi_cal_write_lock);
+
+static u64 cs_amp_time_now_in_windows_time(void)
+{
+ u64 time_in_100ns = div_u64(ktime_get_real_ns(), 100);
+
+ return time_in_100ns + UNIX_TIME_TO_WINDOWS_TIME_OFFSET;
+}
+
+static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ __be32 beval = cpu_to_be32(val);
+ int ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
+
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
+ mutex_lock(&dsp->pwr_lock);
+ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
+ mutex_unlock(&dsp->pwr_lock);
+
+ if (ret < 0) {
+ dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int cs_amp_read_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 *val)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ __be32 beval;
+ int ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_read_cal_coeff, dsp, controls, ctl_name, val);
+
+ if (!IS_REACHABLE(CONFIG_FW_CS_DSP))
+ return -ENODEV;
+
+ scoped_guard(mutex, &dsp->pwr_lock) {
+ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &beval, sizeof(beval));
+ }
+
+ if (ret < 0) {
+ dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
+ return ret;
+ }
+
+ *val = be32_to_cpu(beval);
+
+ return 0;
+}
+
+static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const struct cirrus_amp_cal_data *data)
+{
+ int ret;
+
+ dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
+ data->calAmbient, data->calStatus, data->calR);
+
+ if (list_empty(&dsp->ctl_list)) {
+ dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
+ return -ENOENT;
+ }
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int _cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ struct cirrus_amp_cal_data *data)
+{
+ u64 time;
+ u32 val;
+ int ret;
+
+ if (list_empty(&dsp->ctl_list)) {
+ dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
+ return -ENOENT;
+ }
+
+ ret = cs_amp_read_cal_coeff(dsp, controls, controls->ambient, &val);
+ if (ret)
+ return ret;
+
+ data->calAmbient = (s8)val;
+
+ ret = cs_amp_read_cal_coeff(dsp, controls, controls->calr, &val);
+ if (ret)
+ return ret;
+
+ data->calR = (u16)val;
+
+ ret = cs_amp_read_cal_coeff(dsp, controls, controls->status, &val);
+ if (ret)
+ return ret;
+
+ data->calStatus = (u8)val;
+
+ /* Fill in timestamp */
+ time = cs_amp_time_now_in_windows_time();
+ data->calTime[0] = (u32)time;
+ data->calTime[1] = (u32)(time >> 32);
+
+ return 0;
+}
+
+/**
+ * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
+ * @dsp: Pointer to struct cs_dsp.
+ * @controls: Pointer to definition of firmware controls to be written.
+ * @data: Pointer to calibration data.
+ *
+ * Returns: 0 on success, else negative error value.
+ */
+int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const struct cirrus_amp_cal_data *data)
+{
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_write_cal_coeffs(dsp, controls, data);
+ else
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB");
+
+/**
+ * cs_amp_read_cal_coeffs - Read calibration data from firmware controls.
+ * @dsp: Pointer to struct cs_dsp.
+ * @controls: Pointer to definition of firmware controls to be read.
+ * @data: Pointer to calibration data where results will be written.
+ *
+ * Returns: 0 on success, else negative error value.
+ */
+int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ struct cirrus_amp_cal_data *data)
+{
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_read_cal_coeffs(dsp, controls, data);
+ else
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_read_cal_coeffs, "SND_SOC_CS_AMP_LIB");
+
+/**
+ * cs_amp_write_ambient_temp - write value to calibration ambient temperature
+ * @dsp: Pointer to struct cs_dsp.
+ * @controls: Pointer to definition of firmware controls to be read.
+ * @temp: Temperature in degrees celcius.
+ *
+ * Returns: 0 on success, else negative error value.
+ */
+int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ u32 temp)
+{
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
+ else
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
+
+static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ u32 attr;
+
+ if (!returned_attr)
+ returned_attr = &attr;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid,
+ returned_attr, size, buf);
+
+ if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
+ return efi.get_variable(name, guid, returned_attr, size, buf);
+
+ return EFI_NOT_FOUND;
+}
+
+static efi_status_t cs_amp_set_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 attr,
+ unsigned long size,
+ void *buf)
+{
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_set_efi_variable, name, guid, attr, size, buf);
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
+ return EFI_NOT_FOUND;
+
+ return efi.set_variable(name, guid, attr, size, buf);
+}
+
+static int cs_amp_convert_efi_status(efi_status_t status)
+{
+ switch (status) {
+ case EFI_SUCCESS:
+ return 0;
+ case EFI_NOT_FOUND:
+ return -ENOENT;
+ case EFI_BUFFER_TOO_SMALL:
+ return -EFBIG;
+ case EFI_WRITE_PROTECTED:
+ case EFI_UNSUPPORTED:
+ case EFI_ACCESS_DENIED:
+ case EFI_SECURITY_VIOLATION:
+ return -EACCES;
+ default:
+ return -EIO;
+ }
+}
+
+static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
+ efi_char16_t **name,
+ efi_guid_t **guid,
+ u32 *attr)
+{
+ struct cirrus_amp_efi_data *efi_data __free(kfree) = NULL;
+ unsigned long data_size = 0;
+ efi_status_t status;
+ int i, ret;
+
+ /* Find EFI variable and get size */
+ for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ attr, &data_size, NULL);
+ if (status == EFI_BUFFER_TOO_SMALL)
+ break;
+ }
+
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return ERR_PTR(-ENOENT);
+
+ if (name)
+ *name = cs_amp_lib_cal_efivars[i].name;
+
+ if (guid)
+ *guid = cs_amp_lib_cal_efivars[i].guid;
+
+ if (data_size < sizeof(*efi_data)) {
+ dev_err(dev, "EFI cal variable truncated\n");
+ return ERR_PTR(-EOVERFLOW);
+ }
+
+ /* Get variable contents into buffer */
+ efi_data = kmalloc(data_size, GFP_KERNEL);
+ if (!efi_data)
+ return ERR_PTR(-ENOMEM);
+
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ attr, &data_size, efi_data);
+ if (status != EFI_SUCCESS) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
+
+ if ((efi_data->count > 128) ||
+ struct_size(efi_data, data, efi_data->count) > data_size) {
+ dev_err(dev, "EFI cal variable truncated\n");
+ ret = -EOVERFLOW;
+ goto err;
+ }
+
+ /* This could be zero-filled space pre-allocated by the BIOS */
+ if (efi_data->size == 0)
+ efi_data->size = data_size;
+
+ return_ptr(efi_data);
+
+err:
+ dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
+
+ return ERR_PTR(ret);
+}
+
+static int cs_amp_set_cal_efi_buffer(struct device *dev,
+ efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 attr,
+ struct cirrus_amp_efi_data *data)
+{
+ efi_status_t status;
+
+ status = cs_amp_set_efi_variable(name, guid, attr,
+ struct_size(data, data, data->count), data);
+
+ return cs_amp_convert_efi_status(status);
+}
+
+static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+ struct cirrus_amp_cal_data *out_data)
+{
+ struct cirrus_amp_efi_data *efi_data __free(kfree) = NULL;
+ struct cirrus_amp_cal_data *cal = NULL;
+ int i;
+
+ efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL);
+ if (IS_ERR(efi_data))
+ return PTR_ERR(efi_data);
+
+ if (target_uid) {
+ for (i = 0; i < efi_data->count; ++i) {
+ u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
+
+ /* Skip empty entries */
+ if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
+ continue;
+
+ /* Skip entries with unpopulated silicon ID */
+ if (cal_target == 0)
+ continue;
+
+ if (cal_target == target_uid) {
+ cal = &efi_data->data[i];
+ break;
+ }
+ }
+ }
+
+ if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
+ (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
+ u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
+
+ /*
+ * Treat unpopulated cal_target as a wildcard.
+ * If target_uid != 0 we can only get here if cal_target == 0
+ * or it didn't match any cal_target value.
+ * If target_uid == 0 it is a wildcard.
+ */
+ if ((cal_target == 0) || (target_uid == 0))
+ cal = &efi_data->data[amp_index];
+ else
+ dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
+ }
+
+ if (!cal) {
+ dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
+ return -ENOENT;
+ }
+
+ memcpy(out_data, cal, sizeof(*out_data));
+
+ return 0;
+}
+
+static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
+ const struct cirrus_amp_cal_data *in_data)
+{
+ u64 cal_target = cs_amp_cal_target_u64(in_data);
+ unsigned long num_entries;
+ struct cirrus_amp_efi_data *data __free(kfree) = NULL;
+ efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
+ efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
+ u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
+ int i, ret;
+
+ if (cal_target == 0)
+ return -EINVAL;
+
+ data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr);
+ ret = PTR_ERR_OR_ZERO(data);
+ if (ret == -ENOENT) {
+ data = NULL;
+ goto alloc_new;
+ } else if (ret) {
+ return ret;
+ }
+
+ /*
+ * If the EFI variable is just zero-filled reserved space the count
+ * must be set.
+ */
+ if (data->count == 0)
+ data->count = (data->size - sizeof(data)) / sizeof(data->data[0]);
+
+ if (amp_index < 0) {
+ /* Is there already a slot for this target? */
+ for (amp_index = 0; amp_index < data->count; amp_index++) {
+ if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target)
+ break;
+ }
+
+ /* Else find an empty slot */
+ if (amp_index >= data->count) {
+ for (amp_index = 0; amp_index < data->count; amp_index++) {
+ if ((data->data[amp_index].calTime[0] == 0) &&
+ (data->data[amp_index].calTime[1] == 0))
+ break;
+ }
+ }
+ } else {
+ /*
+ * If the index is forced there could be another active
+ * slot with the same calTarget. So deduplicate.
+ */
+ for (i = 0; i < data->count; i++) {
+ if (i == amp_index)
+ continue;
+
+ if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0))
+ continue;
+
+ if (cs_amp_cal_target_u64(&data->data[i]) == cal_target)
+ memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime));
+ }
+ }
+
+alloc_new:
+ if (amp_index < 0)
+ amp_index = 0;
+
+ num_entries = max(num_amps, amp_index + 1);
+ if (!data || (data->count < num_entries)) {
+ struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data);
+ unsigned int new_data_size = struct_size(data, data, num_entries);
+
+ data = kzalloc(new_data_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (old_data)
+ memcpy(data, old_data, struct_size(old_data, data, old_data->count));
+
+ data->count = num_entries;
+ data->size = new_data_size;
+ }
+
+ data->data[amp_index] = *in_data;
+ ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
+ if (ret) {
+ dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
+ * @dev: struct device of the caller.
+ * @target_uid: UID to match, or zero to ignore UID matching.
+ * @amp_index: Entry index to use, or -1 to prevent lookup by index.
+ * @out_data: struct cirrus_amp_cal_data where the entry will be copied.
+ *
+ * This function can perform 3 types of lookup:
+ *
+ * (target_uid > 0, amp_index >= 0)
+ * UID search with fallback to using the array index.
+ * Search the calibration data for a non-zero calTarget that matches
+ * target_uid, and if found return that entry. Else, if the entry at
+ * [amp_index] has calTarget == 0, return that entry. Else fail.
+ *
+ * (target_uid > 0, amp_index < 0)
+ * UID search only.
+ * Search the calibration data for a non-zero calTarget that matches
+ * target_uid, and if found return that entry. Else fail.
+ *
+ * (target_uid == 0, amp_index >= 0)
+ * Array index fetch only.
+ * Return the entry at [amp_index].
+ *
+ * An array lookup will be skipped if amp_index exceeds the number of
+ * entries in the calibration array, and in this case the return will
+ * be -ENOENT. An out-of-range amp_index does not prevent matching by
+ * target_uid - it has the same effect as passing amp_index < 0.
+ *
+ * If the EFI data is too short to be a valid entry, or the entry count
+ * in the EFI data overflows the actual length of the data, this function
+ * returns -EOVERFLOW.
+ *
+ * Return: 0 if the entry was found, -ENOENT if no entry was found,
+ * -EOVERFLOW if the EFI file is corrupt, else other error value.
+ */
+int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+ struct cirrus_amp_cal_data *out_data)
+{
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
+ else
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
+
+/**
+ * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI.
+ * @dev: struct device of the caller.
+ * @amp_index: Entry index to use, or -1 to use any available slot.
+ * @num_amps: Maximum number of amps to reserve slots for, or -1 to ignore.
+ * @in_data: struct cirrus_amp_cal_data entry to be written to EFI.
+ *
+ * If a Vendor-specific variable exists it will be updated,
+ * else if the Cirrus variable exists it will be updated
+ * else the Cirrus variable will be created.
+ *
+ * If amp_index >= 0 the data will be placed in this entry of the calibration
+ * data array, overwriting what was in that entry. Any other entries with the
+ * same calTarget will be marked empty.
+ *
+ * If amp_index < 0 and in_data->calTarget matches any existing entry, that
+ * entry will be overwritten. Else the first available free entry will be used,
+ * extending the size of the EFI variable if there are no free entries.
+ *
+ * If num_amps > 0 the EFI variable will be sized to contain at least this
+ * many calibration entries, with any new entries marked empty.
+ *
+ * Return: 0 if the write was successful, -EFBIG if space could not be made in
+ * the EFI file to add the entry, -EACCES if it was not possible to
+ * read or write the EFI variable.
+ */
+int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
+ const struct cirrus_amp_cal_data *in_data)
+{
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) {
+ scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
+ return _cs_amp_set_efi_calibration_data(dev, amp_index,
+ num_amps, in_data);
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
+
+struct cs_amp_spkid_efi {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+ u8 values[2];
+};
+
+static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
+{
+ efi_status_t status;
+ unsigned long size;
+ u8 spkid;
+ int i, ret;
+
+ size = sizeof(spkid);
+ status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid);
+ ret = cs_amp_convert_efi_status(status);
+ if (ret < 0)
+ return ret;
+
+ if (size == 0)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(info->values); i++) {
+ if (info->values[i] == spkid)
+ return i;
+ }
+
+ dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
+
+ return -EINVAL;
+}
+
+static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
+ {
+ .name = LENOVO_SPEAKER_ID_EFI_NAME,
+ .guid = &LENOVO_SPEAKER_ID_EFI_GUID,
+ .values = { 0xd0, 0xd1 },
+ },
+ {
+ .name = HP_SPEAKER_ID_EFI_NAME,
+ .guid = &HP_SPEAKER_ID_EFI_GUID,
+ .values = { 0x30, 0x31 },
+ },
+};
+
+/**
+ * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
+ * @dev: pointer to struct device
+ *
+ * Known vendor-specific methods of speaker ID are checked and if one is
+ * found its speaker ID value is returned.
+ *
+ * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
+ * was not found. -EACCES if the vendor-specific storage could not
+ * be read. Other error values indicate that the data from the
+ * vendor-specific storage was found but could not be understood.
+ */
+int cs_amp_get_vendor_spkid(struct device *dev)
+{
+ int i, ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
+ ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
+ if (ret != -ENOENT)
+ return ret;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+
+/**
+ * cs_amp_create_debugfs - create a debugfs directory for a device
+ *
+ * @dev: pointer to struct device
+ *
+ * Creates a node under "cirrus_logic" in the root of the debugfs filesystem.
+ * This is for Cirrus-specific debugfs functionality to be grouped in a
+ * defined way, independently of the debugfs provided by ALSA/ASoC.
+ * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily
+ * have a stable layout or naming convention.
+ *
+ * Return: Pointer to the dentry for the created directory, or -ENODEV.
+ */
+struct dentry *cs_amp_create_debugfs(struct device *dev)
+{
+ struct dentry *dir;
+
+ dir = debugfs_lookup("cirrus_logic", NULL);
+ if (!dir)
+ dir = debugfs_create_dir("cirrus_logic", NULL);
+
+ return debugfs_create_dir(dev_name(dev), dir);
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB");
+
+static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
+ .get_efi_variable = cs_amp_get_efi_variable,
+ .set_efi_variable = cs_amp_set_efi_variable,
+ .write_cal_coeff = cs_amp_write_cal_coeff,
+ .read_cal_coeff = cs_amp_read_cal_coeff,
+};
+
+const struct cs_amp_test_hooks * const cs_amp_test_hooks =
+ PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
+EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
+
+MODULE_DESCRIPTION("Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 44c30fe3e315..0bb4bdb3deec 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs35l32.c -- CS35L32 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
@@ -17,13 +13,12 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -34,6 +29,7 @@
#include <dt-bindings/sound/cs35l32.h>
#include "cs35l32.h"
+#include "cirrus_legacy.h"
#define CS35L32_NUM_SUPPLIES 2
static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
@@ -43,7 +39,7 @@ static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
struct cs35l32_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
struct cs35l32_platform_data pdata;
struct gpio_desc *reset_gpio;
@@ -154,16 +150,16 @@ static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
CS35L32_ADSP_MASTER_MASK,
CS35L32_ADSP_MASTER_MASK);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
CS35L32_ADSP_MASTER_MASK, 0);
break;
default:
@@ -175,9 +171,9 @@ static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
+ return snd_soc_component_update_bits(component, CS35L32_PWRCTL2,
CS35L32_SDOUT_3ST, tristate << 3);
}
@@ -198,11 +194,11 @@ static struct snd_soc_dai_driver cs35l32_dai[] = {
.formats = CS35L32_FORMATS,
},
.ops = &cs35l32_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
+static int cs35l32_component_set_sysclk(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir)
{
unsigned int val;
@@ -224,20 +220,21 @@ static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
return -EINVAL;
}
- return snd_soc_update_bits(codec, CS35L32_CLK_CTL,
+ return snd_soc_component_update_bits(component, CS35L32_CLK_CTL,
CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
- .set_sysclk = cs35l32_codec_set_sysclk,
-
- .dapm_widgets = cs35l32_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
- .dapm_routes = cs35l32_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
-
- .controls = cs35l32_snd_controls,
- .num_controls = ARRAY_SIZE(cs35l32_snd_controls),
+static const struct snd_soc_component_driver soc_component_dev_cs35l32 = {
+ .set_sysclk = cs35l32_component_set_sysclk,
+ .controls = cs35l32_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l32_snd_controls),
+ .dapm_widgets = cs35l32_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
+ .dapm_routes = cs35l32_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
/* Current and threshold powerup sequence Pg37 in datasheet */
@@ -262,7 +259,10 @@ static const struct regmap_config cs35l32_regmap = {
.volatile_reg = cs35l32_volatile_register,
.readable_reg = cs35l32_readable_register,
.precious_reg = cs35l32_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
@@ -274,7 +274,9 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
pdata->sdout_share = val;
- of_property_read_u32(np, "cirrus,boost-manager", &val);
+ if (of_property_read_u32(np, "cirrus,boost-manager", &val))
+ val = -1u;
+
switch (val) {
case CS35L32_BOOST_MGR_AUTO:
case CS35L32_BOOST_MGR_AUTO_AUDIO:
@@ -282,13 +284,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
case CS35L32_BOOST_MGR_FIXED:
pdata->boost_mng = val;
break;
+ case -1u:
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,boost-manager DT value %d\n", val);
pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
}
- of_property_read_u32(np, "cirrus,sdout-datacfg", &val);
+ if (of_property_read_u32(np, "cirrus,sdout-datacfg", &val))
+ val = -1u;
switch (val) {
case CS35L32_DATA_CFG_LR_VP:
case CS35L32_DATA_CFG_LR_STAT:
@@ -296,13 +300,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
case CS35L32_DATA_CFG_LR_VPSTAT:
pdata->sdout_datacfg = val;
break;
+ case -1u:
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,sdout-datacfg DT value %d\n", val);
pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
}
- of_property_read_u32(np, "cirrus,battery-threshold", &val);
+ if (of_property_read_u32(np, "cirrus,battery-threshold", &val))
+ val = -1u;
switch (val) {
case CS35L32_BATT_THRESH_3_1V:
case CS35L32_BATT_THRESH_3_2V:
@@ -310,13 +316,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
case CS35L32_BATT_THRESH_3_4V:
pdata->batt_thresh = val;
break;
+ case -1u:
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,battery-threshold DT value %d\n", val);
pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
}
- of_property_read_u32(np, "cirrus,battery-recovery", &val);
+ if (of_property_read_u32(np, "cirrus,battery-recovery", &val))
+ val = -1u;
switch (val) {
case CS35L32_BATT_RECOV_3_1V:
case CS35L32_BATT_RECOV_3_2V:
@@ -326,6 +334,7 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
case CS35L32_BATT_RECOV_3_6V:
pdata->batt_recov = val;
break;
+ case -1u:
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,battery-recovery DT value %d\n", val);
@@ -335,23 +344,17 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l32_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l32_private *cs35l32;
struct cs35l32_platform_data *pdata =
dev_get_platdata(&i2c_client->dev);
- int ret, i;
- unsigned int devid = 0;
+ int ret, i, devid;
unsigned int reg;
-
- cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private),
- GFP_KERNEL);
- if (!cs35l32) {
- dev_err(&i2c_client->dev, "could not allocate codec\n");
+ cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
+ if (!cs35l32)
return -ENOMEM;
- }
i2c_set_clientdata(i2c_client, cs35l32);
@@ -365,13 +368,11 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
if (pdata) {
cs35l32->pdata = *pdata;
} else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs35l32_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev, "could not allocate pdata\n");
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata)
return -ENOMEM;
- }
+
if (i2c_client->dev.of_node) {
ret = cs35l32_handle_of_data(i2c_client,
&cs35l32->pdata);
@@ -403,40 +404,40 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
/* 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 (IS_ERR(cs35l32->reset_gpio)) {
+ ret = PTR_ERR(cs35l32->reset_gpio);
+ goto err_supplies;
+ }
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;
+ devid = cirrus_read_device_id(cs35l32->regmap, CS35L32_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
if (devid != CS35L32_CHIP_ID) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
"CS35L32 Device ID (%X). Expected %X\n",
devid, CS35L32_CHIP_ID);
- return ret;
+ goto err_disable;
}
ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;
+ goto err_disable;
}
ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
ARRAY_SIZE(cs35l32_monitor_patch));
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
- return ret;
+ goto err_disable;
}
dev_info(&i2c_client->dev,
@@ -477,10 +478,10 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
CS35L32_PDN_AMP);
/* Clear MCLK Error Bit since we don't have the clock yet */
- ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+ regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs35l32, cs35l32_dai,
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs35l32, cs35l32_dai,
ARRAY_SIZE(cs35l32_dai));
if (ret < 0)
goto err_disable;
@@ -488,24 +489,21 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
return 0;
err_disable:
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+err_supplies:
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
return ret;
}
-static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
+static void cs35l32_i2c_remove(struct i2c_client *i2c_client)
{
struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
- snd_soc_unregister_codec(&i2c_client->dev);
-
/* Hold down reset */
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);
@@ -544,11 +542,9 @@ static int cs35l32_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops cs35l32_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, NULL)
};
static const struct of_device_id cs35l32_of_match[] = {
@@ -559,7 +555,7 @@ MODULE_DEVICE_TABLE(of, cs35l32_of_match);
static const struct i2c_device_id cs35l32_id[] = {
- {"cs35l32", 0},
+ {"cs35l32"},
{}
};
@@ -568,7 +564,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l32_id);
static struct i2c_driver cs35l32_i2c_driver = {
.driver = {
.name = "cs35l32",
- .pm = &cs35l32_runtime_pm,
+ .pm = pm_ptr(&cs35l32_runtime_pm),
.of_match_table = cs35l32_of_match,
},
.id_table = cs35l32_id,
diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h
index 1d6c2508cd41..9471a30e9105 100644
--- a/sound/soc/codecs/cs35l32.h
+++ b/sound/soc/codecs/cs35l32.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs35l32.h -- CS35L32 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef __CS35L32_H__
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644
index 000000000000..98b4d371d931
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/gpio/consumer.h>
+#include <sound/cs35l33.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/of.h>
+
+#include "cs35l33.h"
+#include "cirrus_legacy.h"
+
+#define CS35L33_BOOT_DELAY 50
+
+struct cs35l33_private {
+ struct snd_soc_component *component;
+ struct cs35l33_pdata pdata;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ bool amp_cal;
+ int mclk_int;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ bool is_tdm_mode;
+ bool enable_soft_ramp;
+};
+
+static const struct reg_default cs35l33_reg[] = {
+ {CS35L33_PWRCTL1, 0x85},
+ {CS35L33_PWRCTL2, 0xFE},
+ {CS35L33_CLK_CTL, 0x0C},
+ {CS35L33_BST_PEAK_CTL, 0x90},
+ {CS35L33_PROTECT_CTL, 0x55},
+ {CS35L33_BST_CTL1, 0x00},
+ {CS35L33_BST_CTL2, 0x01},
+ {CS35L33_ADSP_CTL, 0x00},
+ {CS35L33_ADC_CTL, 0xC8},
+ {CS35L33_DAC_CTL, 0x14},
+ {CS35L33_DIG_VOL_CTL, 0x00},
+ {CS35L33_CLASSD_CTL, 0x04},
+ {CS35L33_AMP_CTL, 0x90},
+ {CS35L33_INT_MASK_1, 0xFF},
+ {CS35L33_INT_MASK_2, 0xFF},
+ {CS35L33_DIAG_LOCK, 0x00},
+ {CS35L33_DIAG_CTRL_1, 0x40},
+ {CS35L33_DIAG_CTRL_2, 0x00},
+ {CS35L33_HG_MEMLDO_CTL, 0x62},
+ {CS35L33_HG_REL_RATE, 0x03},
+ {CS35L33_LDO_DEL, 0x12},
+ {CS35L33_HG_HEAD, 0x0A},
+ {CS35L33_HG_EN, 0x05},
+ {CS35L33_TX_VMON, 0x00},
+ {CS35L33_TX_IMON, 0x03},
+ {CS35L33_TX_VPMON, 0x02},
+ {CS35L33_TX_VBSTMON, 0x05},
+ {CS35L33_TX_FLAG, 0x06},
+ {CS35L33_TX_EN1, 0x00},
+ {CS35L33_TX_EN2, 0x00},
+ {CS35L33_TX_EN3, 0x00},
+ {CS35L33_TX_EN4, 0x00},
+ {CS35L33_RX_AUD, 0x40},
+ {CS35L33_RX_SPLY, 0x03},
+ {CS35L33_RX_ALIVE, 0x04},
+ {CS35L33_BST_CTL4, 0x63},
+};
+
+static const struct reg_sequence cs35l33_patch[] = {
+ { 0x00, 0x99, 0 },
+ { 0x59, 0x02, 0 },
+ { 0x52, 0x30, 0 },
+ { 0x39, 0x45, 0 },
+ { 0x57, 0x30, 0 },
+ { 0x2C, 0x68, 0 },
+ { 0x00, 0x00, 0 },
+};
+
+static bool cs35l33_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_HG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l33_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ /* these are read only registers */
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_HG_STATUS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool cs35l33_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_PWRCTL1:
+ case CS35L33_PWRCTL2:
+ case CS35L33_CLK_CTL:
+ case CS35L33_BST_PEAK_CTL:
+ case CS35L33_PROTECT_CTL:
+ case CS35L33_BST_CTL1:
+ case CS35L33_BST_CTL2:
+ case CS35L33_ADSP_CTL:
+ case CS35L33_ADC_CTL:
+ case CS35L33_DAC_CTL:
+ case CS35L33_DIG_VOL_CTL:
+ case CS35L33_CLASSD_CTL:
+ case CS35L33_AMP_CTL:
+ case CS35L33_INT_MASK_1:
+ case CS35L33_INT_MASK_2:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_DIAG_LOCK:
+ case CS35L33_DIAG_CTRL_1:
+ case CS35L33_DIAG_CTRL_2:
+ case CS35L33_HG_MEMLDO_CTL:
+ case CS35L33_HG_REL_RATE:
+ case CS35L33_LDO_DEL:
+ case CS35L33_HG_HEAD:
+ case CS35L33_HG_EN:
+ case CS35L33_TX_VMON:
+ case CS35L33_TX_IMON:
+ case CS35L33_TX_VPMON:
+ case CS35L33_TX_VBSTMON:
+ case CS35L33_TX_FLAG:
+ case CS35L33_TX_EN1:
+ case CS35L33_TX_EN2:
+ case CS35L33_TX_EN3:
+ case CS35L33_TX_EN4:
+ case CS35L33_RX_AUD:
+ case CS35L33_RX_SPLY:
+ case CS35L33_RX_ALIVE:
+ case CS35L33_BST_CTL4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 100, 0);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l33_snd_controls[] = {
+
+ SOC_SINGLE_TLV("SPK Amp Volume", CS35L33_AMP_CTL,
+ 4, 0x09, 0, classd_ctl_tlv),
+ SOC_SINGLE_SX_TLV("DAC Volume", CS35L33_DIG_VOL_CTL,
+ 0, 0x34, 0xE4, dac_tlv),
+};
+
+static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!priv->amp_cal) {
+ usleep_range(8000, 9000);
+ priv->amp_cal = true;
+ regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_CAL, 0);
+ dev_dbg(component->dev, "Amp calibration done\n");
+ }
+ dev_dbg(component->dev, "Amp turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev, "Amp turned off\n");
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_BST, 0);
+ val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM;
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_PDN_TDM, val);
+ dev_dbg(component->dev, "BST turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(component->dev, "SDIN turned on\n");
+ if (!priv->amp_cal) {
+ regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_CAL, CS35L33_AMP_CAL);
+ dev_dbg(component->dev, "Amp calibration started\n");
+ usleep_range(10000, 11000);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_PDN_TDM, CS35L33_PDN_TDM);
+ usleep_range(4000, 4100);
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_BST, CS35L33_PDN_BST);
+ dev_dbg(component->dev, "BST and SDIN turned off\n");
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+
+ }
+
+ return 0;
+}
+
+static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int mask = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+ unsigned int mask2 = CS35L33_SDOUT_3ST_TDM;
+ unsigned int val, val2;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->is_tdm_mode) {
+ /* set sdout_3st_i2s and reset pdn_tdm */
+ val = CS35L33_SDOUT_3ST_I2S;
+ /* reset sdout_3st_tdm */
+ val2 = 0;
+ } else {
+ /* reset sdout_3st_i2s and set pdn_tdm */
+ val = CS35L33_PDN_TDM;
+ /* set sdout_3st_tdm */
+ val2 = CS35L33_SDOUT_3ST_TDM;
+ }
+ dev_dbg(component->dev, "SDOUT turned on\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+ val2 = CS35L33_SDOUT_3ST_TDM;
+ dev_dbg(component->dev, "SDOUT turned off\n");
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ return 0;
+ }
+
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ mask, val);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ mask2, val2);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l33_dapm_widgets[] = {
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("SPKDRV", CS35L33_PWRCTL1, 7, 1, NULL, 0,
+ cs35l33_spkrdrv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L33_PWRCTL2,
+ 2, 1, cs35l33_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("MON"),
+
+ SND_SOC_DAPM_ADC("VMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("IMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_IMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("VPMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VPMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("VBSTMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VBSTMON_SHIFT, 1),
+
+ SND_SOC_DAPM_AIF_OUT_E("SDOUT", NULL, 0, SND_SOC_NOPM, 0, 0,
+ cs35l33_sdout_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l33_audio_map[] = {
+ {"SDIN", NULL, "CS35L33 Playback"},
+ {"SPKDRV", NULL, "SDIN"},
+ {"SPK", NULL, "SPKDRV"},
+
+ {"VMON", NULL, "MON"},
+ {"IMON", NULL, "MON"},
+
+ {"SDOUT", NULL, "VMON"},
+ {"SDOUT", NULL, "IMON"},
+ {"CS35L33 Capture", NULL, "SDOUT"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vphg_auto_route[] = {
+ {"SPKDRV", NULL, "VPMON"},
+ {"VPMON", NULL, "CS35L33 Playback"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = {
+ {"SDOUT", NULL, "VPMON"},
+ {"VPMON", NULL, "MON"},
+ {"SDOUT", NULL, "VBSTMON"},
+ {"VBSTMON", NULL, "MON"},
+};
+
+static int cs35l33_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ unsigned int val;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_ALL, 0);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_ALL, CS35L33_PDN_ALL);
+ regmap_read(priv->regmap, CS35L33_INT_STATUS_2, &val);
+ usleep_range(1000, 1100);
+ if (val & CS35L33_PDN_DONE)
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS, CS35L33_MCLKDIS);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l33_mclk_div {
+ int mclk;
+ int srate;
+ u8 adsp_rate;
+ u8 int_fs_ratio;
+};
+
+static const struct cs35l33_mclk_div cs35l33_mclk_coeffs[] = {
+ /* MCLK, Sample Rate, adsp_rate, int_fs_ratio */
+ {5644800, 11025, 0x4, CS35L33_INT_FS_RATE},
+ {5644800, 22050, 0x8, CS35L33_INT_FS_RATE},
+ {5644800, 44100, 0xC, CS35L33_INT_FS_RATE},
+
+ {6000000, 8000, 0x1, 0},
+ {6000000, 11025, 0x2, 0},
+ {6000000, 11029, 0x3, 0},
+ {6000000, 12000, 0x4, 0},
+ {6000000, 16000, 0x5, 0},
+ {6000000, 22050, 0x6, 0},
+ {6000000, 22059, 0x7, 0},
+ {6000000, 24000, 0x8, 0},
+ {6000000, 32000, 0x9, 0},
+ {6000000, 44100, 0xA, 0},
+ {6000000, 44118, 0xB, 0},
+ {6000000, 48000, 0xC, 0},
+
+ {6144000, 8000, 0x1, CS35L33_INT_FS_RATE},
+ {6144000, 12000, 0x4, CS35L33_INT_FS_RATE},
+ {6144000, 16000, 0x5, CS35L33_INT_FS_RATE},
+ {6144000, 24000, 0x8, CS35L33_INT_FS_RATE},
+ {6144000, 32000, 0x9, CS35L33_INT_FS_RATE},
+ {6144000, 48000, 0xC, CS35L33_INT_FS_RATE},
+};
+
+static int cs35l33_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l33_mclk_coeffs); i++) {
+ if (cs35l33_mclk_coeffs[i].mclk == mclk &&
+ cs35l33_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, CS35L33_MS_MASK);
+ dev_dbg(component->dev, "Audio port in master mode\n");
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, 0);
+ dev_dbg(component->dev, "Audio port in slave mode\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * tdm mode in cs35l33 resembles dsp-a mode very
+ * closely, it is dsp-a with fsync shifted left by half bclk
+ */
+ priv->is_tdm_mode = true;
+ dev_dbg(component->dev, "Audio port in TDM mode\n");
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ priv->is_tdm_mode = false;
+ dev_dbg(component->dev, "Audio port in I2S mode\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l33_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ int sample_size = params_width(params);
+ int coeff = cs35l33_get_mclk_coeff(priv->mclk_int, params_rate(params));
+
+ if (coeff < 0)
+ return coeff;
+
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_ADSP_FS | CS35L33_INT_FS_RATE,
+ cs35l33_mclk_coeffs[coeff].int_fs_ratio
+ | cs35l33_mclk_coeffs[coeff].adsp_rate);
+
+ if (priv->is_tdm_mode) {
+ sample_size = (sample_size / 8) - 1;
+ if (sample_size > 2)
+ sample_size = 2;
+ regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+ CS35L33_AUDIN_RX_DEPTH,
+ sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT);
+ }
+
+ dev_dbg(component->dev, "sample rate=%d, bits per sample=%d\n",
+ params_rate(params), params_width(params));
+
+ return 0;
+}
+
+static const unsigned int cs35l33_src_rates[] = {
+ 8000, 11025, 11029, 12000, 16000, 22050,
+ 22059, 24000, 32000, 44100, 44118, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l33_constraints = {
+ .count = ARRAY_SIZE(cs35l33_src_rates),
+ .list = cs35l33_src_rates,
+};
+
+static int cs35l33_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l33_constraints);
+ return 0;
+}
+
+static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ if (tristate) {
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_SDOUT_3ST_I2S, CS35L33_SDOUT_3ST_I2S);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_SDOUT_3ST_TDM, CS35L33_SDOUT_3ST_TDM);
+ } else {
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_SDOUT_3ST_I2S, 0);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_SDOUT_3ST_TDM, 0);
+ }
+
+ return 0;
+}
+
+static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg, bit_pos, i;
+ int slot, slot_num;
+
+ if (slot_width != 8)
+ return -EINVAL;
+
+ /* scan rx_mask for aud slot */
+ slot = ffs(rx_mask) - 1;
+ if (slot >= 0) {
+ regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+ CS35L33_X_LOC, slot);
+ dev_dbg(component->dev, "Audio starts from slots %d", slot);
+ }
+
+ /*
+ * scan tx_mask: vmon(2 slots); imon (2 slots);
+ * vpmon (1 slot) vbstmon (1 slot)
+ */
+ slot = ffs(tx_mask) - 1;
+ slot_num = 0;
+
+ for (i = 0; i < 2 ; i++) {
+ /* disable vpmon/vbstmon: enable later if set in tx_mask */
+ regmap_update_bits(priv->regmap, CS35L33_TX_VPMON + i,
+ CS35L33_X_STATE | CS35L33_X_LOC, CS35L33_X_STATE
+ | CS35L33_X_LOC);
+ }
+
+ /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route,
+ ARRAY_SIZE(cs35l33_vp_vbst_mon_route));
+
+ while (slot >= 0) {
+ /* configure VMON_TX_LOC */
+ if (slot_num == 0) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ dev_dbg(component->dev, "VMON enabled in slots %d-%d",
+ slot, slot + 1);
+ }
+
+ /* configure IMON_TX_LOC */
+ if (slot_num == 3) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_IMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ dev_dbg(component->dev, "IMON enabled in slots %d-%d",
+ slot, slot + 1);
+ }
+
+ /* configure VPMON_TX_LOC */
+ if (slot_num == 4) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VPMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ snd_soc_dapm_add_routes(dapm,
+ &cs35l33_vp_vbst_mon_route[0], 2);
+ dev_dbg(component->dev, "VPMON enabled in slots %d", slot);
+ }
+
+ /* configure VBSTMON_TX_LOC */
+ if (slot_num == 5) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VBSTMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ snd_soc_dapm_add_routes(dapm,
+ &cs35l33_vp_vbst_mon_route[2], 2);
+ dev_dbg(component->dev,
+ "VBSTMON enabled in slots %d", slot);
+ }
+
+ /* Enable the relevant tx slot */
+ reg = CS35L33_TX_EN4 - (slot/8);
+ bit_pos = slot - ((slot / 8) * (8));
+ regmap_update_bits(priv->regmap, reg,
+ 1 << bit_pos, 1 << bit_pos);
+
+ tx_mask &= ~(1 << slot);
+ slot = ffs(tx_mask) - 1;
+ slot_num++;
+ }
+
+ return 0;
+}
+
+static int cs35l33_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case CS35L33_MCLK_5644:
+ case CS35L33_MCLK_6:
+ case CS35L33_MCLK_6144:
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIV2, 0);
+ cs35l33->mclk_int = freq;
+ break;
+ case CS35L33_MCLK_11289:
+ case CS35L33_MCLK_12:
+ case CS35L33_MCLK_12288:
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIV2, CS35L33_MCLKDIV2);
+ cs35l33->mclk_int = freq/2;
+ break;
+ default:
+ cs35l33->mclk_int = 0;
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "external mclk freq=%d, internal mclk freq=%d\n",
+ freq, cs35l33->mclk_int);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l33_ops = {
+ .startup = cs35l33_pcm_startup,
+ .set_tristate = cs35l33_set_tristate,
+ .set_fmt = cs35l33_set_dai_fmt,
+ .hw_params = cs35l33_pcm_hw_params,
+ .set_tdm_slot = cs35l33_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l33_dai = {
+ .name = "cs35l33-dai",
+ .id = 0,
+ .playback = {
+ .stream_name = "CS35L33 Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS35L33_RATES,
+ .formats = CS35L33_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CS35L33 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS35L33_RATES,
+ .formats = CS35L33_FORMATS,
+ },
+ .ops = &cs35l33_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs35l33_set_hg_data(struct snd_soc_component *component,
+ struct cs35l33_pdata *pdata)
+{
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs35l33_private *priv = snd_soc_component_get_drvdata(component);
+
+ if (hg_config->enable_hg_algo) {
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_MEM_DEPTH_MASK,
+ hg_config->mem_depth << CS35L33_MEM_DEPTH_SHIFT);
+ regmap_write(priv->regmap, CS35L33_HG_REL_RATE,
+ hg_config->release_rate);
+ regmap_update_bits(priv->regmap, CS35L33_HG_HEAD,
+ CS35L33_HD_RM_MASK,
+ hg_config->hd_rm << CS35L33_HD_RM_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_LDO_THLD_MASK,
+ hg_config->ldo_thld << CS35L33_LDO_THLD_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_LDO_DISABLE_MASK,
+ hg_config->ldo_path_disable <<
+ CS35L33_LDO_DISABLE_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_LDO_ENTRY_DELAY_MASK,
+ hg_config->ldo_entry_delay <<
+ CS35L33_LDO_ENTRY_DELAY_SHIFT);
+ if (hg_config->vp_hg_auto) {
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_VP_HG_AUTO_MASK,
+ CS35L33_VP_HG_AUTO_MASK);
+ snd_soc_dapm_add_routes(dapm, cs35l33_vphg_auto_route,
+ ARRAY_SIZE(cs35l33_vphg_auto_route));
+ }
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_VP_HG_MASK,
+ hg_config->vp_hg << CS35L33_VP_HG_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_VP_HG_RATE_MASK,
+ hg_config->vp_hg_rate << CS35L33_VP_HG_RATE_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_VP_HG_VA_MASK,
+ hg_config->vp_hg_va << CS35L33_VP_HG_VA_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_CLASS_HG_EN_MASK, CS35L33_CLASS_HG_EN_MASK);
+ }
+ return 0;
+}
+
+static int cs35l33_set_bst_ipk(struct snd_soc_component *component, unsigned int bst)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
+ int ret = 0, steps = 0;
+
+ /* Boost current in uA */
+ if (bst > 3600000 || bst < 1850000) {
+ dev_err(component->dev, "Invalid boost current %d\n", bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (bst % 15625) {
+ dev_err(component->dev, "Current not a multiple of 15625uA (%d)\n",
+ bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ while (bst > 1850000) {
+ bst -= 15625;
+ steps++;
+ }
+
+ regmap_write(cs35l33->regmap, CS35L33_BST_PEAK_CTL,
+ steps+0x70);
+
+err:
+ return ret;
+}
+
+static int cs35l33_probe(struct snd_soc_component *component)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component);
+
+ cs35l33->component = component;
+ pm_runtime_get_sync(component->dev);
+
+ regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL,
+ CS35L33_ALIVE_WD_DIS, 0x8);
+ regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL2,
+ CS35L33_ALIVE_WD_DIS2,
+ CS35L33_ALIVE_WD_DIS2);
+
+ /* Set Platform Data */
+ regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL1,
+ CS35L33_BST_CTL_MASK, cs35l33->pdata.boost_ctl);
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_DRV_SEL_MASK,
+ cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT);
+
+ if (cs35l33->pdata.boost_ipk)
+ cs35l33_set_bst_ipk(component, cs35l33->pdata.boost_ipk);
+
+ if (cs35l33->enable_soft_ramp) {
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, CS35L33_DIGSFT);
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
+ CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
+ } else {
+ snd_soc_component_update_bits(component, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, 0);
+ }
+
+ /* update IMON scaling rate if different from default of 0x8 */
+ if (cs35l33->pdata.imon_adc_scale != 0x8)
+ snd_soc_component_update_bits(component, CS35L33_ADC_CTL,
+ CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
+
+ cs35l33_set_hg_data(component, &(cs35l33->pdata));
+
+ /*
+ * unmask important interrupts that causes the chip to enter
+ * speaker safe mode and hence deserves user attention
+ */
+ regmap_update_bits(cs35l33->regmap, CS35L33_INT_MASK_1,
+ CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT |
+ CS35L33_M_CAL_ERR, 0);
+
+ pm_runtime_put_sync(component->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l33 = {
+ .probe = cs35l33_probe,
+ .set_bias_level = cs35l33_set_bias_level,
+ .set_sysclk = cs35l33_component_set_sysclk,
+ .controls = cs35l33_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l33_snd_controls),
+ .dapm_widgets = cs35l33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets),
+ .dapm_routes = cs35l33_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs35l33_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L33_MAX_REGISTER,
+ .reg_defaults = cs35l33_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l33_reg),
+ .volatile_reg = cs35l33_volatile_register,
+ .readable_reg = cs35l33_readable_register,
+ .writeable_reg = cs35l33_writeable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs35l33_runtime_resume(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l33->regmap, false);
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+
+ ret = regcache_sync(cs35l33->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs35l33->regmap, true);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static int cs35l33_runtime_suspend(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ regcache_cache_only(cs35l33->regmap, true);
+ regcache_mark_dirty(cs35l33->regmap);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l33_pm_ops = {
+ RUNTIME_PM_OPS(cs35l33_runtime_suspend, cs35l33_runtime_resume, NULL)
+};
+
+static int cs35l33_get_hg_data(const struct device_node *np,
+ struct cs35l33_pdata *pdata)
+{
+ struct device_node *hg;
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ u32 val32;
+
+ hg = of_get_child_by_name(np, "cirrus,hg-algo");
+ hg_config->enable_hg_algo = hg ? true : false;
+
+ if (hg_config->enable_hg_algo) {
+ if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0)
+ hg_config->mem_depth = val32;
+ if (of_property_read_u32(hg, "cirrus,release-rate",
+ &val32) >= 0)
+ hg_config->release_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0)
+ hg_config->ldo_thld = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-path-disable",
+ &val32) >= 0)
+ hg_config->ldo_path_disable = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-entry-delay",
+ &val32) >= 0)
+ hg_config->ldo_entry_delay = val32;
+
+ hg_config->vp_hg_auto = of_property_read_bool(hg,
+ "cirrus,vp-hg-auto");
+
+ if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0)
+ hg_config->vp_hg = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0)
+ hg_config->vp_hg_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0)
+ hg_config->vp_hg_va = val32;
+ }
+
+ of_node_put(hg);
+
+ return 0;
+}
+
+static irqreturn_t cs35l33_irq_thread(int irq, void *data)
+{
+ struct cs35l33_private *cs35l33 = data;
+ struct snd_soc_component *component = cs35l33->component;
+ unsigned int sticky_val1, sticky_val2, current_val, mask1, mask2;
+
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_2,
+ &sticky_val2);
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+ &sticky_val1);
+ regmap_read(cs35l33->regmap, CS35L33_INT_MASK_2, &mask2);
+ regmap_read(cs35l33->regmap, CS35L33_INT_MASK_1, &mask1);
+
+ /* Check to see if the unmasked bits are active,
+ * if not then exit.
+ */
+ if (!(sticky_val1 & ~mask1) && !(sticky_val2 & ~mask2))
+ return IRQ_NONE;
+
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+ &current_val);
+
+ /* handle the interrupts */
+
+ if (sticky_val1 & CS35L33_AMP_SHORT) {
+ dev_crit(component->dev, "Amp short error\n");
+ if (!(current_val & CS35L33_AMP_SHORT)) {
+ dev_dbg(component->dev,
+ "Amp short error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL,
+ CS35L33_AMP_SHORT_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL,
+ CS35L33_AMP_SHORT_RLS,
+ CS35L33_AMP_SHORT_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_AMP_SHORT_RLS,
+ 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_CAL_ERR) {
+ dev_err(component->dev, "Cal error\n");
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ if (!(current_val & CS35L33_CAL_ERR)) {
+ dev_dbg(component->dev, "Cal error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ CS35L33_CAL_ERR_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_OTE) {
+ dev_crit(component->dev, "Over temperature error\n");
+ if (!(current_val & CS35L33_OTE)) {
+ dev_dbg(component->dev,
+ "Over temperature error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS,
+ CS35L33_OTE_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_OTW) {
+ dev_err(component->dev, "Over temperature warning\n");
+ if (!(current_val & CS35L33_OTW)) {
+ dev_dbg(component->dev,
+ "Over temperature warning release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS,
+ CS35L33_OTW_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+ }
+ }
+ if (CS35L33_ALIVE_ERR & sticky_val1)
+ dev_err(component->dev, "ERROR: ADSPCLK Interrupt\n");
+
+ if (CS35L33_MCLK_ERR & sticky_val1)
+ dev_err(component->dev, "ERROR: MCLK Interrupt\n");
+
+ if (CS35L33_VMON_OVFL & sticky_val2)
+ dev_err(component->dev,
+ "ERROR: VMON Overflow Interrupt\n");
+
+ if (CS35L33_IMON_OVFL & sticky_val2)
+ dev_err(component->dev,
+ "ERROR: IMON Overflow Interrupt\n");
+
+ if (CS35L33_VPMON_OVFL & sticky_val2)
+ dev_err(component->dev,
+ "ERROR: VPMON Overflow Interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static const char * const cs35l33_core_supplies[] = {
+ "VA",
+ "VP",
+};
+
+static int cs35l33_of_get_pdata(struct device *dev,
+ struct cs35l33_private *cs35l33)
+{
+ struct device_node *np = dev->of_node;
+ struct cs35l33_pdata *pdata = &cs35l33->pdata;
+ u32 val32;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "cirrus,boost-ctl", &val32) >= 0) {
+ pdata->boost_ctl = val32;
+ pdata->amp_drv_sel = 1;
+ }
+
+ if (of_property_read_u32(np, "cirrus,ramp-rate", &val32) >= 0) {
+ pdata->ramp_rate = val32;
+ cs35l33->enable_soft_ramp = true;
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-ipk", &val32) >= 0)
+ pdata->boost_ipk = val32;
+
+ if (of_property_read_u32(np, "cirrus,imon-adc-scale", &val32) >= 0) {
+ if ((val32 == 0x0) || (val32 == 0x7) || (val32 == 0x6))
+ pdata->imon_adc_scale = val32;
+ else
+ /* use default value */
+ pdata->imon_adc_scale = 0x8;
+ } else {
+ /* use default value */
+ pdata->imon_adc_scale = 0x8;
+ }
+
+ cs35l33_get_hg_data(np, pdata);
+
+ return 0;
+}
+
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l33_private *cs35l33;
+ struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret, devid, i;
+ unsigned int reg;
+
+ cs35l33 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l33_private),
+ GFP_KERNEL);
+ if (!cs35l33)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs35l33);
+ cs35l33->regmap = devm_regmap_init_i2c(i2c_client, &cs35l33_regmap);
+ if (IS_ERR(cs35l33->regmap)) {
+ ret = PTR_ERR(cs35l33->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l33->regmap, true);
+
+ for (i = 0; i < ARRAY_SIZE(cs35l33_core_supplies); i++)
+ cs35l33->core_supplies[i].supply
+ = cs35l33_core_supplies[i];
+ cs35l33->num_core_supplies = ARRAY_SIZE(cs35l33_core_supplies);
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l33->pdata = *pdata;
+ } else {
+ cs35l33_of_get_pdata(&i2c_client->dev, cs35l33);
+ pdata = &cs35l33->pdata;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l33", cs35l33);
+ if (ret != 0)
+ dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+ /* We could issue !RST or skip it based on AMP topology */
+ cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(cs35l33->reset_gpio)) {
+ dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
+ __func__);
+ return PTR_ERR(cs35l33->reset_gpio);
+ }
+
+ ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+ regcache_cache_only(cs35l33->regmap, false);
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs35l33->regmap, CS35L33_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_enable;
+ }
+
+ if (devid != CS35L33_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35L33 Device ID (%X). Expected ID %X\n",
+ devid, CS35L33_CHIP_ID);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_read(cs35l33->regmap, CS35L33_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_enable;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L33, Revision: %02X\n", reg & 0xFF);
+
+ ret = regmap_register_patch(cs35l33->regmap,
+ cs35l33_patch, ARRAY_SIZE(cs35l33_patch));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "Error in applying regmap patch: %d\n", ret);
+ goto err_enable;
+ }
+
+ /* disable mclk and tdm */
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM,
+ CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM);
+
+ pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+ pm_runtime_use_autosuspend(&i2c_client->dev);
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs35l33, &cs35l33_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "%s: Register component failed\n",
+ __func__);
+ goto err_enable;
+ }
+
+ return 0;
+
+err_enable:
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static void cs35l33_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
+
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+}
+
+static const struct of_device_id cs35l33_of_match[] = {
+ { .compatible = "cirrus,cs35l33", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l33_of_match);
+
+static const struct i2c_device_id cs35l33_id[] = {
+ {"cs35l33"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+ .driver = {
+ .name = "cs35l33",
+ .pm = pm_ptr(&cs35l33_pm_ops),
+ .of_match_table = cs35l33_of_match,
+
+ },
+ .id_table = cs35l33_id,
+ .probe = cs35l33_i2c_probe,
+ .remove = cs35l33_i2c_remove,
+
+};
+module_i2c_driver(cs35l33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L33 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
new file mode 100644
index 000000000000..fcb5e1723be6
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l33.h -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+
+#ifndef __CS35L33_H__
+#define __CS35L33_H__
+
+#define CS35L33_CHIP_ID 0x00035A33
+#define CS35L33_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L33_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L33_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L33_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L33_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L33_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L33_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L33_CLK_CTL 0x08 /* Clock Ctl */
+#define CS35L33_BST_PEAK_CTL 0x09 /* Max Current for Boost */
+#define CS35L33_PROTECT_CTL 0x0A /* Amp Protection Parameters */
+#define CS35L33_BST_CTL1 0x0B /* Boost Converter CTL1 */
+#define CS35L33_BST_CTL2 0x0C /* Boost Converter CTL2 */
+#define CS35L33_ADSP_CTL 0x0D /* Serial Port Control */
+#define CS35L33_ADC_CTL 0x0E /* ADC Control */
+#define CS35L33_DAC_CTL 0x0F /* DAC Control */
+#define CS35L33_DIG_VOL_CTL 0x10 /* Digital Volume CTL */
+#define CS35L33_CLASSD_CTL 0x11 /* Class D Amp CTL */
+#define CS35L33_AMP_CTL 0x12 /* Amp Gain/Protecton Release CTL */
+#define CS35L33_INT_MASK_1 0x13 /* Interrupt Mask 1 */
+#define CS35L33_INT_MASK_2 0x14 /* Interrupt Mask 2 */
+#define CS35L33_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
+#define CS35L33_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
+#define CS35L33_DIAG_LOCK 0x17 /* Diagnostic Mode Register Lock */
+#define CS35L33_DIAG_CTRL_1 0x18 /* Diagnostic Mode Register Control */
+#define CS35L33_DIAG_CTRL_2 0x19 /* Diagnostic Mode Register Control 2 */
+#define CS35L33_HG_MEMLDO_CTL 0x23 /* H/G Memory/LDO CTL */
+#define CS35L33_HG_REL_RATE 0x24 /* H/G Release Rate */
+#define CS35L33_LDO_DEL 0x25 /* LDO Entry Delay/VPhg Control 1 */
+#define CS35L33_HG_HEAD 0x29 /* H/G Headroom */
+#define CS35L33_HG_EN 0x2A /* H/G Enable/VPhg CNT2 */
+#define CS35L33_TX_VMON 0x2D /* TDM TX Control 1 (VMON) */
+#define CS35L33_TX_IMON 0x2E /* TDM TX Control 2 (IMON) */
+#define CS35L33_TX_VPMON 0x2F /* TDM TX Control 3 (VPMON) */
+#define CS35L33_TX_VBSTMON 0x30 /* TDM TX Control 4 (VBSTMON) */
+#define CS35L33_TX_FLAG 0x31 /* TDM TX Control 5 (FLAG) */
+#define CS35L33_TX_EN1 0x32 /* TDM TX Enable 1 */
+#define CS35L33_TX_EN2 0x33 /* TDM TX Enable 2 */
+#define CS35L33_TX_EN3 0x34 /* TDM TX Enable 3 */
+#define CS35L33_TX_EN4 0x35 /* TDM TX Enable 4 */
+#define CS35L33_RX_AUD 0x36 /* TDM RX Control 1 */
+#define CS35L33_RX_SPLY 0x37 /* TDM RX Control 2 */
+#define CS35L33_RX_ALIVE 0x38 /* TDM RX Control 3 */
+#define CS35L33_BST_CTL4 0x39 /* Boost Converter Control 4 */
+#define CS35L33_HG_STATUS 0x3F /* H/G Status */
+#define CS35L33_MAX_REGISTER 0x59
+
+#define CS35L33_MCLK_5644 5644800
+#define CS35L33_MCLK_6144 6144000
+#define CS35L33_MCLK_6 6000000
+#define CS35L33_MCLK_11289 11289600
+#define CS35L33_MCLK_12 12000000
+#define CS35L33_MCLK_12288 12288000
+
+/* CS35L33_PWRCTL1 */
+#define CS35L33_PDN_AMP (1 << 7)
+#define CS35L33_PDN_BST (1 << 2)
+#define CS35L33_PDN_ALL 1
+
+/* CS35L33_PWRCTL2 */
+#define CS35L33_PDN_VMON_SHIFT 7
+#define CS35L33_PDN_VMON (1 << CS35L33_PDN_VMON_SHIFT)
+#define CS35L33_PDN_IMON_SHIFT 6
+#define CS35L33_PDN_IMON (1 << CS35L33_PDN_IMON_SHIFT)
+#define CS35L33_PDN_VPMON_SHIFT 5
+#define CS35L33_PDN_VPMON (1 << CS35L33_PDN_VPMON_SHIFT)
+#define CS35L33_PDN_VBSTMON_SHIFT 4
+#define CS35L33_PDN_VBSTMON (1 << CS35L33_PDN_VBSTMON_SHIFT)
+#define CS35L33_SDOUT_3ST_I2S_SHIFT 3
+#define CS35L33_SDOUT_3ST_I2S (1 << CS35L33_SDOUT_3ST_I2S_SHIFT)
+#define CS35L33_PDN_SDIN_SHIFT 2
+#define CS35L33_PDN_SDIN (1 << CS35L33_PDN_SDIN_SHIFT)
+#define CS35L33_PDN_TDM_SHIFT 1
+#define CS35L33_PDN_TDM (1 << CS35L33_PDN_TDM_SHIFT)
+
+/* CS35L33_CLK_CTL */
+#define CS35L33_MCLKDIS (1 << 7)
+#define CS35L33_MCLKDIV2 (1 << 6)
+#define CS35L33_SDOUT_3ST_TDM (1 << 5)
+#define CS35L33_INT_FS_RATE (1 << 4)
+#define CS35L33_ADSP_FS 0xF
+
+/* CS35L33_PROTECT_CTL */
+#define CS35L33_ALIVE_WD_DIS (3 << 2)
+
+/* CS35L33_BST_CTL1 */
+#define CS35L33_BST_CTL_SRC (1 << 6)
+#define CS35L33_BST_CTL_SHIFT (1 << 5)
+#define CS35L33_BST_CTL_MASK 0x3F
+
+/* CS35L33_BST_CTL2 */
+#define CS35L33_TDM_WD_SEL (1 << 4)
+#define CS35L33_ALIVE_WD_DIS2 (1 << 3)
+#define CS35L33_VBST_SR_STEP 0x3
+
+/* CS35L33_ADSP_CTL */
+#define CS35L33_ADSP_DRIVE (1 << 7)
+#define CS35L33_MS_MASK (1 << 6)
+#define CS35L33_SDIN_LOC (3 << 4)
+#define CS35L33_ALIVE_RATE 0x3
+
+/* CS35L33_ADC_CTL */
+#define CS35L33_INV_VMON (1 << 7)
+#define CS35L33_INV_IMON (1 << 6)
+#define CS35L33_ADC_NOTCH_DIS (1 << 5)
+#define CS35L33_IMON_SCALE 0xF
+
+/* CS35L33_DAC_CTL */
+#define CS35L33_INV_DAC (1 << 7)
+#define CS35L33_DAC_NOTCH_DIS (1 << 5)
+#define CS35L33_DIGSFT (1 << 4)
+#define CS35L33_DSR_RATE 0xF
+
+/* CS35L33_CLASSD_CTL */
+#define CS35L33_AMP_SD (1 << 6)
+#define CS35L33_AMP_DRV_SEL_SRC (1 << 5)
+#define CS35L33_AMP_DRV_SEL_MASK 0x10
+#define CS35L33_AMP_DRV_SEL_SHIFT 4
+#define CS35L33_AMP_CAL (1 << 3)
+#define CS35L33_GAIN_CHG_ZC_MASK 0x04
+#define CS35L33_GAIN_CHG_ZC_SHIFT 2
+#define CS35L33_CLASS_D_CTL_MASK 0x3F
+
+/* CS35L33_AMP_CTL */
+#define CS35L33_AMP_GAIN 0xF0
+#define CS35L33_CAL_ERR_RLS (1 << 3)
+#define CS35L33_AMP_SHORT_RLS (1 << 2)
+#define CS35L33_OTW_RLS (1 << 1)
+#define CS35L33_OTE_RLS 1
+
+/* CS35L33_INT_MASK_1 */
+#define CS35L33_M_CAL_ERR_SHIFT 6
+#define CS35L33_M_CAL_ERR (1 << CS35L33_M_CAL_ERR_SHIFT)
+#define CS35L33_M_ALIVE_ERR_SHIFT 5
+#define CS35L33_M_ALIVE_ERR (1 << CS35L33_M_ALIVE_ERR_SHIFT)
+#define CS35L33_M_AMP_SHORT_SHIFT 2
+#define CS35L33_M_AMP_SHORT (1 << CS35L33_M_AMP_SHORT_SHIFT)
+#define CS35L33_M_OTW_SHIFT 1
+#define CS35L33_M_OTW (1 << CS35L33_M_OTW_SHIFT)
+#define CS35L33_M_OTE_SHIFT 0
+#define CS35L33_M_OTE (1 << CS35L33_M_OTE_SHIFT)
+
+/* CS35L33_INT_STATUS_1 */
+#define CS35L33_CAL_ERR (1 << 6)
+#define CS35L33_ALIVE_ERR (1 << 5)
+#define CS35L33_ADSPCLK_ERR (1 << 4)
+#define CS35L33_MCLK_ERR (1 << 3)
+#define CS35L33_AMP_SHORT (1 << 2)
+#define CS35L33_OTW (1 << 1)
+#define CS35L33_OTE (1 << 0)
+
+/* CS35L33_INT_STATUS_2 */
+#define CS35L33_VMON_OVFL (1 << 7)
+#define CS35L33_IMON_OVFL (1 << 6)
+#define CS35L33_VPMON_OVFL (1 << 5)
+#define CS35L33_VBSTMON_OVFL (1 << 4)
+#define CS35L33_PDN_DONE 1
+
+/* CS35L33_BST_CTL4 */
+#define CS35L33_BST_RGS 0x70
+#define CS35L33_BST_COEFF3 0xF
+
+/* CS35L33_HG_MEMLDO_CTL */
+#define CS35L33_MEM_DEPTH_SHIFT 5
+#define CS35L33_MEM_DEPTH_MASK (0x3 << CS35L33_MEM_DEPTH_SHIFT)
+#define CS35L33_LDO_THLD_SHIFT 1
+#define CS35L33_LDO_THLD_MASK (0xF << CS35L33_LDO_THLD_SHIFT)
+#define CS35L33_LDO_DISABLE_SHIFT 0
+#define CS35L33_LDO_DISABLE_MASK (0x1 << CS35L33_LDO_DISABLE_SHIFT)
+
+/* CS35L33_LDO_DEL */
+#define CS35L33_VP_HG_VA_SHIFT 5
+#define CS35L33_VP_HG_VA_MASK (0x7 << CS35L33_VP_HG_VA_SHIFT)
+#define CS35L33_LDO_ENTRY_DELAY_SHIFT 2
+#define CS35L33_LDO_ENTRY_DELAY_MASK (0x7 << CS35L33_LDO_ENTRY_DELAY_SHIFT)
+#define CS35L33_VP_HG_RATE_SHIFT 0
+#define CS35L33_VP_HG_RATE_MASK (0x3 << CS35L33_VP_HG_RATE_SHIFT)
+
+/* CS35L33_HG_HEAD */
+#define CS35L33_HD_RM_SHIFT 0
+#define CS35L33_HD_RM_MASK (0x7F << CS35L33_HD_RM_SHIFT)
+
+/* CS35L33_HG_EN */
+#define CS35L33_CLASS_HG_ENA_SHIFT 7
+#define CS35L33_CLASS_HG_EN_MASK (0x1 << CS35L33_CLASS_HG_ENA_SHIFT)
+#define CS35L33_VP_HG_AUTO_SHIFT 6
+#define CS35L33_VP_HG_AUTO_MASK (0x1 << 6)
+#define CS35L33_VP_HG_SHIFT 0
+#define CS35L33_VP_HG_MASK (0x1F << CS35L33_VP_HG_SHIFT)
+
+#define CS35L33_RATES (SNDRV_PCM_RATE_8000_48000)
+#define CS35L33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* CS35L33_{RX,TX}_X */
+#define CS35L33_X_STATE_SHIFT 7
+#define CS35L33_X_STATE (1 << CS35L33_X_STATE_SHIFT)
+#define CS35L33_X_LOC_SHIFT 0
+#define CS35L33_X_LOC (0x1F << CS35L33_X_LOC_SHIFT)
+
+/* CS35L33_RX_AUD */
+#define CS35L33_AUDIN_RX_DEPTH_SHIFT 5
+#define CS35L33_AUDIN_RX_DEPTH (0x7 << CS35L33_AUDIN_RX_DEPTH_SHIFT)
+
+#endif
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
new file mode 100644
index 000000000000..a5a8075598ff
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.c
@@ -0,0 +1,1217 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l34.c -- CS35l34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio/consumer.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l34.h>
+
+#include "cs35l34.h"
+#include "cirrus_legacy.h"
+
+#define PDN_DONE_ATTEMPTS 10
+#define CS35L34_START_DELAY 50
+
+struct cs35l34_private {
+ struct snd_soc_component *component;
+ struct cs35l34_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ int mclk_int;
+ bool tdm_mode;
+ struct gpio_desc *reset_gpio; /* Active-low reset GPIO */
+};
+
+static const struct reg_default cs35l34_reg[] = {
+ {CS35L34_PWRCTL1, 0x01},
+ {CS35L34_PWRCTL2, 0x19},
+ {CS35L34_PWRCTL3, 0x01},
+ {CS35L34_ADSP_CLK_CTL, 0x08},
+ {CS35L34_MCLK_CTL, 0x11},
+ {CS35L34_AMP_INP_DRV_CTL, 0x01},
+ {CS35L34_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L34_AMP_DIG_VOL, 0x00},
+ {CS35L34_AMP_ANLG_GAIN_CTL, 0x0F},
+ {CS35L34_PROTECT_CTL, 0x06},
+ {CS35L34_AMP_KEEP_ALIVE_CTL, 0x04},
+ {CS35L34_BST_CVTR_V_CTL, 0x00},
+ {CS35L34_BST_PEAK_I, 0x10},
+ {CS35L34_BST_RAMP_CTL, 0x87},
+ {CS35L34_BST_CONV_COEF_1, 0x24},
+ {CS35L34_BST_CONV_COEF_2, 0x24},
+ {CS35L34_BST_CONV_SLOPE_COMP, 0x4E},
+ {CS35L34_BST_CONV_SW_FREQ, 0x08},
+ {CS35L34_CLASS_H_CTL, 0x0D},
+ {CS35L34_CLASS_H_HEADRM_CTL, 0x0D},
+ {CS35L34_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L34_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L34_CLASS_H_STATUS, 0x05},
+ {CS35L34_VPBR_CTL, 0x0A},
+ {CS35L34_VPBR_VOL_CTL, 0x90},
+ {CS35L34_VPBR_TIMING_CTL, 0x6A},
+ {CS35L34_PRED_MAX_ATTEN_SPK_LOAD, 0x95},
+ {CS35L34_PRED_BROWNOUT_THRESH, 0x1C},
+ {CS35L34_PRED_BROWNOUT_VOL_CTL, 0x00},
+ {CS35L34_PRED_BROWNOUT_RATE_CTL, 0x10},
+ {CS35L34_PRED_WAIT_CTL, 0x10},
+ {CS35L34_PRED_ZVP_INIT_IMP_CTL, 0x08},
+ {CS35L34_PRED_MAN_SAFE_VPI_CTL, 0x80},
+ {CS35L34_VPBR_ATTEN_STATUS, 0x00},
+ {CS35L34_PRED_BRWNOUT_ATT_STATUS, 0x00},
+ {CS35L34_SPKR_MON_CTL, 0xC6},
+ {CS35L34_ADSP_I2S_CTL, 0x00},
+ {CS35L34_ADSP_TDM_CTL, 0x00},
+ {CS35L34_TDM_TX_CTL_1_VMON, 0x00},
+ {CS35L34_TDM_TX_CTL_2_IMON, 0x04},
+ {CS35L34_TDM_TX_CTL_3_VPMON, 0x03},
+ {CS35L34_TDM_TX_CTL_4_VBSTMON, 0x07},
+ {CS35L34_TDM_TX_CTL_5_FLAG1, 0x08},
+ {CS35L34_TDM_TX_CTL_6_FLAG2, 0x09},
+ {CS35L34_TDM_TX_SLOT_EN_1, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_2, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_3, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_4, 0x00},
+ {CS35L34_TDM_RX_CTL_1_AUDIN, 0x40},
+ {CS35L34_TDM_RX_CTL_3_ALIVE, 0x04},
+ {CS35L34_MULT_DEV_SYNCH1, 0x00},
+ {CS35L34_MULT_DEV_SYNCH2, 0x80},
+ {CS35L34_PROT_RELEASE_CTL, 0x00},
+ {CS35L34_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L34_DIAG_MODE_CTL_1, 0x00},
+ {CS35L34_DIAG_MODE_CTL_2, 0x00},
+ {CS35L34_INT_MASK_1, 0xFF},
+ {CS35L34_INT_MASK_2, 0xFF},
+ {CS35L34_INT_MASK_3, 0xFF},
+ {CS35L34_INT_MASK_4, 0xFF},
+ {CS35L34_INT_STATUS_1, 0x30},
+ {CS35L34_INT_STATUS_2, 0x05},
+ {CS35L34_INT_STATUS_3, 0x00},
+ {CS35L34_INT_STATUS_4, 0x00},
+ {CS35L34_OTP_TRIM_STATUS, 0x00},
+};
+
+static bool cs35l34_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_DEVID_AB:
+ case CS35L34_DEVID_CD:
+ case CS35L34_DEVID_E:
+ case CS35L34_FAB_ID:
+ case CS35L34_REV_ID:
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ case CS35L34_CLASS_H_STATUS:
+ case CS35L34_VPBR_ATTEN_STATUS:
+ case CS35L34_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l34_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_DEVID_AB:
+ case CS35L34_DEVID_CD:
+ case CS35L34_DEVID_E:
+ case CS35L34_FAB_ID:
+ case CS35L34_REV_ID:
+ case CS35L34_PWRCTL1:
+ case CS35L34_PWRCTL2:
+ case CS35L34_PWRCTL3:
+ case CS35L34_ADSP_CLK_CTL:
+ case CS35L34_MCLK_CTL:
+ case CS35L34_AMP_INP_DRV_CTL:
+ case CS35L34_AMP_DIG_VOL_CTL:
+ case CS35L34_AMP_DIG_VOL:
+ case CS35L34_AMP_ANLG_GAIN_CTL:
+ case CS35L34_PROTECT_CTL:
+ case CS35L34_AMP_KEEP_ALIVE_CTL:
+ case CS35L34_BST_CVTR_V_CTL:
+ case CS35L34_BST_PEAK_I:
+ case CS35L34_BST_RAMP_CTL:
+ case CS35L34_BST_CONV_COEF_1:
+ case CS35L34_BST_CONV_COEF_2:
+ case CS35L34_BST_CONV_SLOPE_COMP:
+ case CS35L34_BST_CONV_SW_FREQ:
+ case CS35L34_CLASS_H_CTL:
+ case CS35L34_CLASS_H_HEADRM_CTL:
+ case CS35L34_CLASS_H_RELEASE_RATE:
+ case CS35L34_CLASS_H_FET_DRIVE_CTL:
+ case CS35L34_CLASS_H_STATUS:
+ case CS35L34_VPBR_CTL:
+ case CS35L34_VPBR_VOL_CTL:
+ case CS35L34_VPBR_TIMING_CTL:
+ case CS35L34_PRED_MAX_ATTEN_SPK_LOAD:
+ case CS35L34_PRED_BROWNOUT_THRESH:
+ case CS35L34_PRED_BROWNOUT_VOL_CTL:
+ case CS35L34_PRED_BROWNOUT_RATE_CTL:
+ case CS35L34_PRED_WAIT_CTL:
+ case CS35L34_PRED_ZVP_INIT_IMP_CTL:
+ case CS35L34_PRED_MAN_SAFE_VPI_CTL:
+ case CS35L34_VPBR_ATTEN_STATUS:
+ case CS35L34_PRED_BRWNOUT_ATT_STATUS:
+ case CS35L34_SPKR_MON_CTL:
+ case CS35L34_ADSP_I2S_CTL:
+ case CS35L34_ADSP_TDM_CTL:
+ case CS35L34_TDM_TX_CTL_1_VMON:
+ case CS35L34_TDM_TX_CTL_2_IMON:
+ case CS35L34_TDM_TX_CTL_3_VPMON:
+ case CS35L34_TDM_TX_CTL_4_VBSTMON:
+ case CS35L34_TDM_TX_CTL_5_FLAG1:
+ case CS35L34_TDM_TX_CTL_6_FLAG2:
+ case CS35L34_TDM_TX_SLOT_EN_1:
+ case CS35L34_TDM_TX_SLOT_EN_2:
+ case CS35L34_TDM_TX_SLOT_EN_3:
+ case CS35L34_TDM_TX_SLOT_EN_4:
+ case CS35L34_TDM_RX_CTL_1_AUDIN:
+ case CS35L34_TDM_RX_CTL_3_ALIVE:
+ case CS35L34_MULT_DEV_SYNCH1:
+ case CS35L34_MULT_DEV_SYNCH2:
+ case CS35L34_PROT_RELEASE_CTL:
+ case CS35L34_DIAG_MODE_REG_LOCK:
+ case CS35L34_DIAG_MODE_CTL_1:
+ case CS35L34_DIAG_MODE_CTL_2:
+ case CS35L34_INT_MASK_1:
+ case CS35L34_INT_MASK_2:
+ case CS35L34_INT_MASK_3:
+ case CS35L34_INT_MASK_4:
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ case CS35L34_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l34_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->tdm_mode)
+ regmap_update_bits(priv->regmap, CS35L34_PWRCTL3,
+ CS35L34_PDN_TDM, 0x00);
+
+ ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "Cannot set Power bits %d\n", ret);
+ return ret;
+ }
+ usleep_range(5000, 5100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->tdm_mode) {
+ regmap_update_bits(priv->regmap, CS35L34_PWRCTL3,
+ CS35L34_PDN_TDM, CS35L34_PDN_TDM);
+ }
+ ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg, bit_pos;
+ int slot, slot_num;
+
+ if (slot_width != 8)
+ return -EINVAL;
+
+ priv->tdm_mode = true;
+ /* scan rx_mask for aud slot */
+ slot = ffs(rx_mask) - 1;
+ if (slot >= 0)
+ snd_soc_component_update_bits(component, CS35L34_TDM_RX_CTL_1_AUDIN,
+ CS35L34_X_LOC, slot);
+
+ /* scan tx_mask: vmon(2 slots); imon (2 slots); vpmon (1 slot)
+ * vbstmon (1 slot)
+ */
+ slot = ffs(tx_mask) - 1;
+ slot_num = 0;
+
+ /* disable vpmon/vbstmon: enable later if set in tx_mask */
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_3_VPMON,
+ CS35L34_X_STATE | CS35L34_X_LOC,
+ CS35L34_X_STATE | CS35L34_X_LOC);
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_4_VBSTMON,
+ CS35L34_X_STATE | CS35L34_X_LOC,
+ CS35L34_X_STATE | CS35L34_X_LOC);
+
+ /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ while (slot >= 0) {
+ /* configure VMON_TX_LOC */
+ if (slot_num == 0)
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_1_VMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+
+ /* configure IMON_TX_LOC */
+ if (slot_num == 4) {
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_2_IMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+ /* configure VPMON_TX_LOC */
+ if (slot_num == 3) {
+ snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_3_VPMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+ /* configure VBSTMON_TX_LOC */
+ if (slot_num == 7) {
+ snd_soc_component_update_bits(component,
+ CS35L34_TDM_TX_CTL_4_VBSTMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+
+ /* Enable the relevant tx slot */
+ reg = CS35L34_TDM_TX_SLOT_EN_4 - (slot/8);
+ bit_pos = slot - ((slot / 8) * (8));
+ snd_soc_component_update_bits(component, reg,
+ 1 << bit_pos, 1 << bit_pos);
+
+ tx_mask &= ~(1 << slot);
+ slot = ffs(tx_mask) - 1;
+ slot_num++;
+ }
+
+ return 0;
+}
+
+static int cs35l34_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL,
+ CS35L34_BST_CVTL_MASK, priv->pdata.boost_vtge);
+ usleep_range(5000, 5100);
+ regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL,
+ CS35L34_BST_CVTL_MASK, 0);
+ regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, CS35L34_MUTE);
+ usleep_range(5000, 5100);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 300, 100, 0);
+
+
+static const struct snd_kcontrol_new cs35l34_snd_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Volume", CS35L34_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Amp Gain Volume", CS35L34_AMP_ANLG_GAIN_CTL,
+ 0, 0xF, 0, amp_gain_tlv),
+};
+
+
+static int cs35l34_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ int ret, i;
+ unsigned int reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = regmap_read(priv->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ &reg);
+ if (ret != 0) {
+ pr_err("%s regmap read failure %d\n", __func__, ret);
+ return ret;
+ }
+ if (reg & CS35L34_AMP_DIGSFT)
+ msleep(40);
+ else
+ usleep_range(2000, 2100);
+
+ for (i = 0; i < PDN_DONE_ATTEMPTS; i++) {
+ ret = regmap_read(priv->regmap, CS35L34_INT_STATUS_2,
+ &reg);
+ if (ret != 0) {
+ pr_err("%s regmap read failure %d\n",
+ __func__, ret);
+ return ret;
+ }
+ if (reg & CS35L34_PDN_DONE)
+ break;
+
+ usleep_range(5000, 5100);
+ }
+ if (i == PDN_DONE_ATTEMPTS)
+ pr_err("%s Device did not power down properly\n",
+ __func__);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l34_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L34_PWRCTL3,
+ 1, 1, cs35l34_sdin_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L34_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_SUPPLY("EXTCLK", CS35L34_PWRCTL3, 7, 1,
+ cs35l34_mclk_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VPST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L34_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L34_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L34_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L34_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L34_PWRCTL2, 5, 1),
+ SND_SOC_DAPM_ADC("BOOST", NULL, CS35L34_PWRCTL2, 2, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L34_PWRCTL2, 0, 1, NULL, 0,
+ cs35l34_main_amp_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l34_audio_map[] = {
+ {"SDIN", NULL, "AMP Playback"},
+ {"BOOST", NULL, "SDIN"},
+ {"CLASS H", NULL, "BOOST"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+
+ {"VPMON ADC", NULL, "CLASS H"},
+ {"VBSTMON ADC", NULL, "CLASS H"},
+ {"SPK", NULL, "VPMON ADC"},
+ {"SPK", NULL, "VBSTMON ADC"},
+
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "EXTCLK"},
+ {"SDOUT", NULL, "EXTCLK"},
+};
+
+struct cs35l34_mclk_div {
+ int mclk;
+ int srate;
+ u8 adsp_rate;
+};
+
+static struct cs35l34_mclk_div cs35l34_mclk_coeffs[] = {
+
+ /* MCLK, Sample Rate, adsp_rate */
+
+ {5644800, 11025, 0x1},
+ {5644800, 22050, 0x4},
+ {5644800, 44100, 0x7},
+
+ {6000000, 8000, 0x0},
+ {6000000, 11025, 0x1},
+ {6000000, 12000, 0x2},
+ {6000000, 16000, 0x3},
+ {6000000, 22050, 0x4},
+ {6000000, 24000, 0x5},
+ {6000000, 32000, 0x6},
+ {6000000, 44100, 0x7},
+ {6000000, 48000, 0x8},
+
+ {6144000, 8000, 0x0},
+ {6144000, 11025, 0x1},
+ {6144000, 12000, 0x2},
+ {6144000, 16000, 0x3},
+ {6144000, 22050, 0x4},
+ {6144000, 24000, 0x5},
+ {6144000, 32000, 0x6},
+ {6144000, 44100, 0x7},
+ {6144000, 48000, 0x8},
+};
+
+static int cs35l34_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l34_mclk_coeffs); i++) {
+ if (cs35l34_mclk_coeffs[i].mclk == mclk &&
+ cs35l34_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l34_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *priv = snd_soc_component_get_drvdata(component);
+ int srate = params_rate(params);
+ int ret;
+
+ int coeff = cs35l34_get_mclk_coeff(priv->mclk_int, srate);
+
+ if (coeff < 0) {
+ dev_err(component->dev, "ERROR: Invalid mclk %d and/or srate %d\n",
+ priv->mclk_int, srate);
+ return coeff;
+ }
+
+ ret = regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ CS35L34_ADSP_RATE, cs35l34_mclk_coeffs[coeff].adsp_rate);
+ if (ret != 0)
+ dev_err(component->dev, "Failed to set clock state %d\n", ret);
+
+ return ret;
+}
+
+static int cs35l34_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+
+ struct snd_soc_component *component = dai->component;
+
+ if (tristate)
+ snd_soc_component_update_bits(component, CS35L34_PWRCTL3,
+ CS35L34_PDN_SDOUT, CS35L34_PDN_SDOUT);
+ else
+ snd_soc_component_update_bits(component, CS35L34_PWRCTL3,
+ CS35L34_PDN_SDOUT, 0);
+ return 0;
+}
+
+static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+
+ switch (freq) {
+ case CS35L34_MCLK_5644:
+ value = CS35L34_MCLK_RATE_5P6448;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_6:
+ value = CS35L34_MCLK_RATE_6P0000;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_6144:
+ value = CS35L34_MCLK_RATE_6P1440;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_11289:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_5P6448;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ case CS35L34_MCLK_12:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P0000;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ case CS35L34_MCLK_12288:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P1440;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ default:
+ dev_err(component->dev, "ERROR: Invalid Frequency %d\n", freq);
+ cs35l34->mclk_int = 0;
+ return -EINVAL;
+ }
+ regmap_update_bits(cs35l34->regmap, CS35L34_MCLK_CTL,
+ CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_MASK, value);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l34_ops = {
+ .set_tristate = cs35l34_set_tristate,
+ .set_fmt = cs35l34_set_dai_fmt,
+ .hw_params = cs35l34_pcm_hw_params,
+ .set_sysclk = cs35l34_dai_set_sysclk,
+ .set_tdm_slot = cs35l34_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l34_dai = {
+ .name = "cs35l34",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L34_RATES,
+ .formats = CS35L34_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L34_RATES,
+ .formats = CS35L34_FORMATS,
+ },
+ .ops = &cs35l34_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34,
+ unsigned int inductor)
+{
+ struct snd_soc_component *component = cs35l34->component;
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x24);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x24);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x4E);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 0);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x47);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 1);
+ break;
+ case 1500: /* 1.5uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x3C);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 2);
+ break;
+ case 2200: /* 2.2uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x19);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x25);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x23);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 3);
+ break;
+ default:
+ dev_err(component->dev, "%s Invalid Inductor Value %d uH\n",
+ __func__, inductor);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l34_probe(struct snd_soc_component *component)
+{
+ int ret = 0;
+ struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component);
+
+ pm_runtime_get_sync(component->dev);
+
+ /* Set over temperature warning attenuation to 6 dB */
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_OTW_ATTN_MASK, 0x8);
+
+ /* Set Power control registers 2 and 3 to have everything
+ * powered down at initialization
+ */
+ regmap_write(cs35l34->regmap, CS35L34_PWRCTL2, 0xFD);
+ regmap_write(cs35l34->regmap, CS35L34_PWRCTL3, 0x1F);
+
+ /* Set mute bit at startup */
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, CS35L34_MUTE);
+
+ /* Set Platform Data */
+ if (cs35l34->pdata.boost_peak)
+ regmap_update_bits(cs35l34->regmap, CS35L34_BST_PEAK_I,
+ CS35L34_BST_PEAK_MASK,
+ cs35l34->pdata.boost_peak);
+
+ if (cs35l34->pdata.gain_zc_disable)
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_GAIN_ZC_MASK, 0);
+ else
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_GAIN_ZC_MASK, CS35L34_GAIN_ZC_MASK);
+
+ if (cs35l34->pdata.aif_half_drv)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_CLK_CTL,
+ CS35L34_ADSP_DRIVE, 0);
+
+ if (cs35l34->pdata.digsft_disable)
+ regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ CS35L34_AMP_DIGSFT, 0);
+
+ if (cs35l34->pdata.amp_inv)
+ regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ CS35L34_INV, CS35L34_INV);
+
+ if (cs35l34->pdata.boost_ind)
+ ret = cs35l34_boost_inductor(cs35l34, cs35l34->pdata.boost_ind);
+
+ if (cs35l34->pdata.i2s_sdinloc)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_I2S_CTL,
+ CS35L34_I2S_LOC_MASK,
+ cs35l34->pdata.i2s_sdinloc << CS35L34_I2S_LOC_SHIFT);
+
+ if (cs35l34->pdata.tdm_rising_edge)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_TDM_CTL,
+ 1, 1);
+
+ pm_runtime_put_sync(component->dev);
+
+ return ret;
+}
+
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l34 = {
+ .probe = cs35l34_probe,
+ .dapm_widgets = cs35l34_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l34_dapm_widgets),
+ .dapm_routes = cs35l34_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l34_audio_map),
+ .controls = cs35l34_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l34_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs35l34_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L34_MAX_REGISTER,
+ .reg_defaults = cs35l34_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l34_reg),
+ .volatile_reg = cs35l34_volatile_register,
+ .readable_reg = cs35l34_readable_register,
+ .precious_reg = cs35l34_precious_register,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs35l34_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l34_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+
+ if (of_property_read_u32(np, "cirrus,boost-vtge-millivolt",
+ &val) >= 0) {
+ /* Boost Voltage has a maximum of 8V */
+ if (val > 8000 || (val < 3300 && val > 0)) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val);
+ return -EINVAL;
+ }
+ if (val == 0)
+ pdata->boost_vtge = 0; /* Use VP */
+ else
+ pdata->boost_vtge = ((val - 3300)/100) + 1;
+ } else {
+ dev_warn(&i2c_client->dev,
+ "Boost Voltage not specified. Using VP\n");
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) {
+ pdata->boost_ind = val;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val) >= 0) {
+ if (val > 3840 || val < 1200) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %d mA\n", val);
+ return -EINVAL;
+ }
+ pdata->boost_peak = ((val - 1200)/80) + 1;
+ }
+
+ pdata->aif_half_drv = of_property_read_bool(np,
+ "cirrus,aif-half-drv");
+ pdata->digsft_disable = of_property_read_bool(np,
+ "cirrus,digsft-disable");
+
+ pdata->gain_zc_disable = of_property_read_bool(np,
+ "cirrus,gain-zc-disable");
+ pdata->amp_inv = of_property_read_bool(np, "cirrus,amp-inv");
+
+ if (of_property_read_u32(np, "cirrus,i2s-sdinloc", &val) >= 0)
+ pdata->i2s_sdinloc = val;
+ if (of_property_read_u32(np, "cirrus,tdm-rising-edge", &val) >= 0)
+ pdata->tdm_rising_edge = val;
+
+ return 0;
+}
+
+static irqreturn_t cs35l34_irq_thread(int irq, void *data)
+{
+ struct cs35l34_private *cs35l34 = data;
+ struct snd_soc_component *component = cs35l34->component;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_4, &mask4);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_3, &mask3);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_2, &mask2);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_1, &mask1);
+
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &current1);
+
+ if (sticky1 & CS35L34_CAL_ERR) {
+ dev_err(component->dev, "Cal error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_CAL_ERR)) {
+ dev_dbg(component->dev, "Cal error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS,
+ CS35L34_CAL_ERR_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS, 0);
+ /* note: amp will re-calibrate on next resume */
+ }
+ }
+
+ if (sticky1 & CS35L34_ALIVE_ERR)
+ dev_err(component->dev, "Alive error\n");
+
+ if (sticky1 & CS35L34_AMP_SHORT) {
+ dev_crit(component->dev, "Amp short error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_AMP_SHORT)) {
+ dev_dbg(component->dev,
+ "Amp short error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS,
+ CS35L34_SHORT_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L34_OTW) {
+ dev_crit(component->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_OTW)) {
+ dev_dbg(component->dev,
+ "Over temperature warning release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS,
+ CS35L34_OTW_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L34_OTE) {
+ dev_crit(component->dev, "Over temperature error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_OTE)) {
+ dev_dbg(component->dev,
+ "Over temperature error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS,
+ CS35L34_OTE_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L34_BST_HIGH) {
+ dev_crit(component->dev, "VBST too high error; powering off!\n");
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2,
+ CS35L34_PDN_AMP, CS35L34_PDN_AMP);
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L34_LBST_SHORT) {
+ dev_crit(component->dev, "LBST short error; powering off!\n");
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2,
+ CS35L34_PDN_AMP, CS35L34_PDN_AMP);
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const char * const cs35l34_core_supplies[] = {
+ "VA",
+ "VP",
+};
+
+static int cs35l34_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l34_private *cs35l34;
+ struct cs35l34_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int i, devid;
+ int ret;
+ unsigned int reg;
+
+ cs35l34 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l34), GFP_KERNEL);
+ if (!cs35l34)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs35l34);
+ cs35l34->regmap = devm_regmap_init_i2c(i2c_client, &cs35l34_regmap);
+ if (IS_ERR(cs35l34->regmap)) {
+ ret = PTR_ERR(cs35l34->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs35l34->num_core_supplies = ARRAY_SIZE(cs35l34_core_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs35l34_core_supplies); i++)
+ cs35l34->core_supplies[i].supply = cs35l34_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l34->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto err_regulator;
+ }
+
+ if (i2c_client->dev.of_node) {
+ ret = cs35l34_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ goto err_regulator;
+
+ }
+ cs35l34->pdata = *pdata;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l34", cs35l34);
+ if (ret != 0)
+ dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+ cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l34->reset_gpio)) {
+ ret = PTR_ERR(cs35l34->reset_gpio);
+ goto err_regulator;
+ }
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
+
+ msleep(CS35L34_START_DELAY);
+
+ devid = cirrus_read_device_id(cs35l34->regmap, CS35L34_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reset;
+ }
+
+ if (devid != CS35L34_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35l34 Device ID (%X). Expected ID %X\n",
+ devid, CS35L34_CHIP_ID);
+ ret = -ENODEV;
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs35l34->regmap, CS35L34_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_reset;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35l34 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ /* Unmask critical interrupts */
+ regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_1,
+ CS35L34_M_CAL_ERR | CS35L34_M_ALIVE_ERR |
+ CS35L34_M_AMP_SHORT | CS35L34_M_OTW |
+ CS35L34_M_OTE, 0);
+ regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_3,
+ CS35L34_M_BST_HIGH | CS35L34_M_LBST_SHORT, 0);
+
+ pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+ pm_runtime_use_autosuspend(&i2c_client->dev);
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs35l34, &cs35l34_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Register component failed\n", __func__);
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+err_regulator:
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return ret;
+}
+
+static void cs35l34_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l34_private *cs35l34 = i2c_get_clientdata(client);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+}
+
+static int cs35l34_runtime_resume(struct device *dev)
+{
+ struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l34->regmap, false);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
+ msleep(CS35L34_START_DELAY);
+
+ ret = regcache_sync(cs35l34->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+ return 0;
+err:
+ regcache_cache_only(cs35l34->regmap, true);
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return ret;
+}
+
+static int cs35l34_runtime_suspend(struct device *dev)
+{
+ struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs35l34->regmap, true);
+ regcache_mark_dirty(cs35l34->regmap);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l34_pm_ops = {
+ RUNTIME_PM_OPS(cs35l34_runtime_suspend, cs35l34_runtime_resume, NULL)
+};
+
+static const struct of_device_id cs35l34_of_match[] = {
+ {.compatible = "cirrus,cs35l34"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l34_of_match);
+
+static const struct i2c_device_id cs35l34_id[] = {
+ {"cs35l34"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l34_id);
+
+static struct i2c_driver cs35l34_i2c_driver = {
+ .driver = {
+ .name = "cs35l34",
+ .pm = pm_ptr(&cs35l34_pm_ops),
+ .of_match_table = cs35l34_of_match,
+
+ },
+ .id_table = cs35l34_id,
+ .probe = cs35l34_i2c_probe,
+ .remove = cs35l34_i2c_remove,
+
+};
+
+static int __init cs35l34_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&cs35l34_i2c_driver);
+ if (ret != 0) {
+ pr_err("Failed to register CS35l34 I2C driver: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+module_init(cs35l34_modinit);
+
+static void __exit cs35l34_exit(void)
+{
+ i2c_del_driver(&cs35l34_i2c_driver);
+}
+module_exit(cs35l34_exit);
+
+MODULE_DESCRIPTION("ASoC CS35l34 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h
new file mode 100644
index 000000000000..97959e334f9b
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l34.h -- CS35L34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ */
+
+#ifndef __CS35L34_H__
+#define __CS35L34_H__
+
+#define CS35L34_CHIP_ID 0x00035A34
+#define CS35L34_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L34_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L34_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L34_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L34_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L34_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L34_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L34_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L34_ADSP_CLK_CTL 0x0A /* (ADSP) Clock Ctl */
+#define CS35L34_MCLK_CTL 0x0B /* Master Clocking Ctl */
+#define CS35L34_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L34_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L34_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L34_AMP_ANLG_GAIN_CTL 0x17 /* Amplifier Analog Gain Ctl */
+#define CS35L34_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L34_AMP_KEEP_ALIVE_CTL 0x1A /* Amplifier Keep Alive Ctl */
+#define CS35L34_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L34_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L34_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L34_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L34_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L34_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L34_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L34_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L34_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L34_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L34_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L34_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L34_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L34_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L34_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L34_PRED_MAX_ATTEN_SPK_LOAD 0x40 /* PRD Max Atten / Spkr Load */
+#define CS35L34_PRED_BROWNOUT_THRESH 0x41 /* PRD Brownout Threshold */
+#define CS35L34_PRED_BROWNOUT_VOL_CTL 0x42 /* PRD Brownout Volume Ctl */
+#define CS35L34_PRED_BROWNOUT_RATE_CTL 0x43 /* PRD Brownout Rate Ctl */
+#define CS35L34_PRED_WAIT_CTL 0x44 /* PRD Wait Ctl */
+#define CS35L34_PRED_ZVP_INIT_IMP_CTL 0x46 /* PRD ZVP Initial Imp Ctl */
+#define CS35L34_PRED_MAN_SAFE_VPI_CTL 0x47 /* PRD Manual Safe VPI Ctl */
+#define CS35L34_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L34_PRED_BRWNOUT_ATT_STATUS 0x4C /* PRD Brownout Atten Status */
+#define CS35L34_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L34_ADSP_I2S_CTL 0x50 /* ADSP I2S Ctl */
+#define CS35L34_ADSP_TDM_CTL 0x51 /* ADSP TDM Ctl */
+#define CS35L34_TDM_TX_CTL_1_VMON 0x52 /* TDM TX Ctl 1 (VMON) */
+#define CS35L34_TDM_TX_CTL_2_IMON 0x53 /* TDM TX Ctl 2 (IMON) */
+#define CS35L34_TDM_TX_CTL_3_VPMON 0x54 /* TDM TX Ctl 3 (VPMON) */
+#define CS35L34_TDM_TX_CTL_4_VBSTMON 0x55 /* TDM TX Ctl 4 (VBSTMON) */
+#define CS35L34_TDM_TX_CTL_5_FLAG1 0x56 /* TDM TX Ctl 5 (FLAG1) */
+#define CS35L34_TDM_TX_CTL_6_FLAG2 0x57 /* TDM TX Ctl 6 (FLAG2) */
+#define CS35L34_TDM_TX_SLOT_EN_1 0x5A /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_2 0x5B /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_3 0x5C /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_4 0x5D /* TDM TX Slot Enable */
+#define CS35L34_TDM_RX_CTL_1_AUDIN 0x5E /* TDM RX Ctl 1 */
+#define CS35L34_TDM_RX_CTL_3_ALIVE 0x60 /* TDM RX Ctl 3 (ALIVE) */
+#define CS35L34_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L34_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L34_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L34_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L34_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L34_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L34_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L34_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L34_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L34_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L34_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L34_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L34_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L34_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L34_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L34_MAX_REGISTER 0x7F
+#define CS35L34_REGISTER_COUNT 0x4E
+
+#define CS35L34_MCLK_5644 5644800
+#define CS35L34_MCLK_6144 6144000
+#define CS35L34_MCLK_6 6000000
+#define CS35L34_MCLK_11289 11289600
+#define CS35L34_MCLK_12 12000000
+#define CS35L34_MCLK_12288 12288000
+
+/* CS35L34_PWRCTL1 */
+#define CS35L34_SFT_RST (1 << 7)
+#define CS35L34_DISCHG_FLT (1 << 1)
+#define CS35L34_PDN_ALL 1
+
+/* CS35L34_PWRCTL2 */
+#define CS35L34_PDN_VMON (1 << 7)
+#define CS35L34_PDN_IMON (1 << 6)
+#define CS35L34_PDN_CLASSH (1 << 5)
+#define CS35L34_PDN_VPBR (1 << 4)
+#define CS35L34_PDN_PRED (1 << 3)
+#define CS35L34_PDN_BST (1 << 2)
+#define CS35L34_PDN_AMP 1
+
+/* CS35L34_PWRCTL3 */
+#define CS35L34_MCLK_DIS (1 << 7)
+#define CS35L34_PDN_VBSTMON_OUT (1 << 4)
+#define CS35L34_PDN_VMON_OUT (1 << 3)
+/* Tristate the ADSP SDOUT when in I2C mode */
+#define CS35L34_PDN_SDOUT (1 << 2)
+#define CS35L34_PDN_SDIN (1 << 1)
+#define CS35L34_PDN_TDM 1
+
+/* CS35L34_ADSP_CLK_CTL */
+#define CS35L34_ADSP_RATE 0xF
+#define CS35L34_ADSP_DRIVE (1 << 4)
+#define CS35L34_ADSP_M_S (1 << 7)
+
+/* CS35L34_MCLK_CTL */
+#define CS35L34_MCLK_DIV (1 << 4)
+#define CS35L34_MCLK_RATE_MASK 0x7
+#define CS35L34_MCLK_RATE_6P1440 0x2
+#define CS35L34_MCLK_RATE_6P0000 0x1
+#define CS35L34_MCLK_RATE_5P6448 0x0
+#define CS35L34_MCLKDIS (1 << 7)
+#define CS35L34_MCLKDIV2 (1 << 6)
+#define CS35L34_SDOUT_3ST_TDM (1 << 5)
+#define CS35L34_INT_FS_RATE (1 << 4)
+#define CS35L34_ADSP_FS 0xF
+
+/* CS35L34_AMP_INP_DRV_CTL */
+#define CS35L34_DRV_STR_SRC (1 << 1)
+#define CS35L34_DRV_STR 1
+
+/* CS35L34_AMP_DIG_VOL_CTL */
+#define CS35L34_AMP_DSR_RATE_MASK 0xF0
+#define CS35L34_AMP_DSR_RATE_SHIFT (1 << 4)
+#define CS35L34_NOTCH_DIS (1 << 3)
+#define CS35L34_AMP_DIGSFT (1 << 1)
+#define CS35L34_INV 1
+
+/* CS35L34_PROTECT_CTL */
+#define CS35L34_OTW_ATTN_MASK 0xC
+#define CS35L34_OTW_THRD_MASK 0x3
+#define CS35L34_MUTE (1 << 5)
+#define CS35L34_GAIN_ZC (1 << 4)
+#define CS35L34_GAIN_ZC_MASK 0x10
+#define CS35L34_GAIN_ZC_SHIFT 4
+
+/* CS35L34_AMP_KEEP_ALIVE_CTL */
+#define CS35L34_ALIVE_WD_DIS (1 << 2)
+
+/* CS35L34_BST_CVTR_V_CTL */
+#define CS35L34_BST_CVTL_MASK 0x3F
+
+/* CS35L34_BST_PEAK_I */
+#define CS35L34_BST_PEAK_MASK 0x3F
+
+/* CS35L34_ADSP_I2S_CTL */
+#define CS35L34_I2S_LOC_MASK 0xC
+#define CS35L34_I2S_LOC_SHIFT 2
+
+/* CS35L34_MULT_DEV_SYNCH2 */
+#define CS35L34_SYNC2_MASK 0xF
+
+/* CS35L34_PROT_RELEASE_CTL */
+#define CS35L34_CAL_ERR_RLS (1 << 7)
+#define CS35L34_SHORT_RLS (1 << 2)
+#define CS35L34_OTW_RLS (1 << 1)
+#define CS35L34_OTE_RLS 1
+
+/* CS35L34_INT_MASK_1 */
+#define CS35L34_M_CAL_ERR_SHIFT 7
+#define CS35L34_M_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT)
+#define CS35L34_M_ALIVE_ERR_SHIFT 5
+#define CS35L34_M_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT)
+#define CS35L34_M_ADSP_CLK_SHIFT 4
+#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT)
+#define CS35L34_M_MCLK_SHIFT 3
+#define CS35L34_M_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT)
+#define CS35L34_M_AMP_SHORT_SHIFT 2
+#define CS35L34_M_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT)
+#define CS35L34_M_OTW_SHIFT 1
+#define CS35L34_M_OTW (1 << CS35L34_M_OTW_SHIFT)
+#define CS35L34_M_OTE_SHIFT 0
+#define CS35L34_M_OTE (1 << CS35L34_M_OTE_SHIFT)
+
+/* CS35L34_INT_MASK_2 */
+#define CS35L34_M_PDN_DONE_SHIFT 4
+#define CS35L34_M_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT)
+#define CS35L34_M_PRED_SHIFT 3
+#define CS35L34_M_PRED_ERR (1 << CS35L34_M_PRED_SHIFT)
+#define CS35L34_M_PRED_CLR_SHIFT 2
+#define CS35L34_M_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT)
+#define CS35L34_M_VPBR_SHIFT 1
+#define CS35L34_M_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT)
+#define CS35L34_M_VPBR_CLR_SHIFT 0
+#define CS35L34_M_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT)
+
+/* CS35L34_INT_MASK_3 */
+#define CS35L34_M_BST_HIGH_SHIFT 4
+#define CS35L34_M_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT)
+#define CS35L34_M_BST_HIGH_FLAG_SHIFT 3
+#define CS35L34_M_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT)
+#define CS35L34_M_BST_IPK_FLAG_SHIFT 2
+#define CS35L34_M_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT)
+#define CS35L34_M_LBST_SHORT_SHIFT 0
+#define CS35L34_M_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT)
+
+/* CS35L34_INT_MASK_4 */
+#define CS35L34_M_VMON_OVFL_SHIFT 3
+#define CS35L34_M_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT)
+#define CS35L34_M_IMON_OVFL_SHIFT 2
+#define CS35L34_M_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT)
+#define CS35L34_M_VPMON_OVFL_SHIFT 1
+#define CS35L34_M_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT)
+#define CS35L34_M_VBSTMON_OVFL_SHIFT 1
+#define CS35L34_M_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT)
+
+/* CS35L34_INT_1 */
+#define CS35L34_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT)
+#define CS35L34_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT)
+#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT)
+#define CS35L34_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT)
+#define CS35L34_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT)
+#define CS35L34_OTW (1 << CS35L34_M_OTW_SHIFT)
+#define CS35L34_OTE (1 << CS35L34_M_OTE_SHIFT)
+
+/* CS35L34_INT_2 */
+#define CS35L34_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT)
+#define CS35L34_PRED_ERR (1 << CS35L34_M_PRED_SHIFT)
+#define CS35L34_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT)
+#define CS35L34_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT)
+#define CS35L34_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT)
+
+/* CS35L34_INT_3 */
+#define CS35L34_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT)
+#define CS35L34_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT)
+#define CS35L34_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT)
+#define CS35L34_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT)
+
+/* CS35L34_INT_4 */
+#define CS35L34_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT)
+#define CS35L34_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT)
+#define CS35L34_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT)
+#define CS35L34_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT)
+
+/* CS35L34_{RX,TX}_X */
+#define CS35L34_X_STATE_SHIFT 7
+#define CS35L34_X_STATE (1 << CS35L34_X_STATE_SHIFT)
+#define CS35L34_X_LOC_SHIFT 0
+#define CS35L34_X_LOC (0x1F << CS35L34_X_LOC_SHIFT)
+
+#define CS35L34_RATES (SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_32000)
+#define CS35L34_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#endif
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 000000000000..7a01b1d9fc9d
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1662 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+#include "cirrus_legacy.h"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L35_VALID_PDATA 0x80000000
+
+static const struct reg_default cs35l35_reg[] = {
+ {CS35L35_PWRCTL1, 0x01},
+ {CS35L35_PWRCTL2, 0x11},
+ {CS35L35_PWRCTL3, 0x00},
+ {CS35L35_CLK_CTL1, 0x04},
+ {CS35L35_CLK_CTL2, 0x12},
+ {CS35L35_CLK_CTL3, 0xCF},
+ {CS35L35_SP_FMT_CTL1, 0x20},
+ {CS35L35_SP_FMT_CTL2, 0x00},
+ {CS35L35_SP_FMT_CTL3, 0x02},
+ {CS35L35_MAG_COMP_CTL, 0x00},
+ {CS35L35_AMP_INP_DRV_CTL, 0x01},
+ {CS35L35_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L35_AMP_DIG_VOL, 0x00},
+ {CS35L35_ADV_DIG_VOL, 0x00},
+ {CS35L35_PROTECT_CTL, 0x06},
+ {CS35L35_AMP_GAIN_AUD_CTL, 0x13},
+ {CS35L35_AMP_GAIN_PDM_CTL, 0x00},
+ {CS35L35_AMP_GAIN_ADV_CTL, 0x00},
+ {CS35L35_GPI_CTL, 0x00},
+ {CS35L35_BST_CVTR_V_CTL, 0x00},
+ {CS35L35_BST_PEAK_I, 0x07},
+ {CS35L35_BST_RAMP_CTL, 0x85},
+ {CS35L35_BST_CONV_COEF_1, 0x24},
+ {CS35L35_BST_CONV_COEF_2, 0x24},
+ {CS35L35_BST_CONV_SLOPE_COMP, 0x4E},
+ {CS35L35_BST_CONV_SW_FREQ, 0x04},
+ {CS35L35_CLASS_H_CTL, 0x0B},
+ {CS35L35_CLASS_H_HEADRM_CTL, 0x0B},
+ {CS35L35_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L35_CLASS_H_VP_CTL, 0xC5},
+ {CS35L35_VPBR_CTL, 0x0A},
+ {CS35L35_VPBR_VOL_CTL, 0x90},
+ {CS35L35_VPBR_TIMING_CTL, 0x6A},
+ {CS35L35_VPBR_MODE_VOL_CTL, 0x00},
+ {CS35L35_SPKR_MON_CTL, 0xC0},
+ {CS35L35_IMON_SCALE_CTL, 0x30},
+ {CS35L35_AUDIN_RXLOC_CTL, 0x00},
+ {CS35L35_ADVIN_RXLOC_CTL, 0x80},
+ {CS35L35_VMON_TXLOC_CTL, 0x00},
+ {CS35L35_IMON_TXLOC_CTL, 0x80},
+ {CS35L35_VPMON_TXLOC_CTL, 0x04},
+ {CS35L35_VBSTMON_TXLOC_CTL, 0x84},
+ {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04},
+ {CS35L35_ZERO_FILL_LOC_CTL, 0x00},
+ {CS35L35_AUDIN_DEPTH_CTL, 0x0F},
+ {CS35L35_SPKMON_DEPTH_CTL, 0x0F},
+ {CS35L35_SUPMON_DEPTH_CTL, 0x0F},
+ {CS35L35_ZEROFILL_DEPTH_CTL, 0x00},
+ {CS35L35_MULT_DEV_SYNCH1, 0x02},
+ {CS35L35_MULT_DEV_SYNCH2, 0x80},
+ {CS35L35_PROT_RELEASE_CTL, 0x00},
+ {CS35L35_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L35_DIAG_MODE_CTL_1, 0x40},
+ {CS35L35_DIAG_MODE_CTL_2, 0x00},
+ {CS35L35_INT_MASK_1, 0xFF},
+ {CS35L35_INT_MASK_2, 0xFF},
+ {CS35L35_INT_MASK_3, 0xFF},
+ {CS35L35_INT_MASK_4, 0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+ case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+ case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+ case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+ case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+ case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+ case CS35L35_CLASS_H_STATUS:
+ case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+ case CS35L35_VPBR_ATTEN_STATUS:
+ case CS35L35_SPKR_MON_CTL:
+ case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+ case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+ case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+ case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void cs35l35_reset(struct cs35l35_private *cs35l35)
+{
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+ usleep_range(1000, 1100);
+}
+
+static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35)
+{
+ int ret;
+
+ if (cs35l35->pdata.ext_bst) {
+ usleep_range(5000, 5500);
+ return 0;
+ }
+
+ reinit_completion(&cs35l35->pdn_done);
+
+ ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+ msecs_to_jiffies(100));
+ if (ret == 0) {
+ dev_err(cs35l35->dev, "PDN_DONE did not complete\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 0 << CS35L35_MCLK_DIS_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK,
+ 0 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK,
+ 1 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 1);
+
+ /* Already muted, so disable volume ramp for faster shutdown */
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK, 0);
+
+ ret = cs35l35_wait_for_pdn(cs35l35);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 1 << CS35L35_MCLK_DIS_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK,
+ 1 << CS35L35_AMP_DIGSFT_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ unsigned int reg[4];
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 0 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* If in PDM mode we must use VP for Voltage control */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ 0 << CS35L35_BST_CTL_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 0);
+
+ for (i = 0; i < 2; i++)
+ regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+ &reg, ARRAY_SIZE(reg));
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK,
+ 1 << CS35L35_AMP_MUTE_SHIFT);
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ /*
+ * If PDM mode we should switch back to pdata value
+ * for Voltage control when we go down
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl
+ << CS35L35_BST_CTL_SHIFT);
+
+ break;
+ default:
+ dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+ amp_gain_tlv),
+ SOC_SINGLE_TLV("PDM Volume", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog Advisory Volume", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+ cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+ cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"SDOUT", NULL, "VBSTMON ADC"},
+ {"SDOUT", NULL, "VPMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "AMP Playback"},
+ {"CLASS H", NULL, "SDIN"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+ cs35l35->clock_consumer = false;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+ cs35l35->clock_consumer = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs35l35->i2s_mode = true;
+ cs35l35->pdm_mode = false;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ cs35l35->pdm_mode = true;
+ cs35l35->i2s_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l35_sysclk_config {
+ int sysclk;
+ int srate;
+ u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+ /* SYSCLK, Sample Rate, Serial Port Cfg */
+ {5644800, 44100, 0x00},
+ {5644800, 88200, 0x40},
+ {6144000, 48000, 0x10},
+ {6144000, 96000, 0x50},
+ {11289600, 44100, 0x01},
+ {11289600, 88200, 0x41},
+ {11289600, 176400, 0x81},
+ {12000000, 44100, 0x03},
+ {12000000, 48000, 0x13},
+ {12000000, 88200, 0x43},
+ {12000000, 96000, 0x53},
+ {12000000, 176400, 0x83},
+ {12000000, 192000, 0x93},
+ {12288000, 48000, 0x11},
+ {12288000, 96000, 0x51},
+ {12288000, 192000, 0x91},
+ {13000000, 44100, 0x07},
+ {13000000, 48000, 0x17},
+ {13000000, 88200, 0x47},
+ {13000000, 96000, 0x57},
+ {13000000, 176400, 0x87},
+ {13000000, 192000, 0x97},
+ {22579200, 44100, 0x02},
+ {22579200, 88200, 0x42},
+ {22579200, 176400, 0x82},
+ {24000000, 44100, 0x0B},
+ {24000000, 48000, 0x1B},
+ {24000000, 88200, 0x4B},
+ {24000000, 96000, 0x5B},
+ {24000000, 176400, 0x8B},
+ {24000000, 192000, 0x9B},
+ {24576000, 48000, 0x12},
+ {24576000, 96000, 0x52},
+ {24576000, 192000, 0x92},
+ {26000000, 44100, 0x0F},
+ {26000000, 48000, 0x1F},
+ {26000000, 88200, 0x4F},
+ {26000000, 96000, 0x5F},
+ {26000000, 176400, 0x8F},
+ {26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+ if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+ cs35l35_clk_ctl[i].srate == srate)
+ return cs35l35_clk_ctl[i].clk_cfg;
+ }
+ return -EINVAL;
+}
+
+static int cs35l35_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ int srate = params_rate(params);
+ int ret = 0;
+ u8 sp_sclks;
+ int audin_format;
+ int errata_chk;
+
+ int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+ if (clk_ctl < 0) {
+ dev_err(component->dev, "Invalid CLK:Rate %d:%d\n",
+ cs35l35->sysclk, srate);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+ CS35L35_CLK_CTL2_MASK, clk_ctl);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set port config %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Rev A0 Errata
+ * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+ * the Class H algorithm does not enable weak-drive operation for
+ * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+ */
+ errata_chk = (clk_ctl & CS35L35_SP_RATE_MASK) >> CS35L35_SP_RATE_SHIFT;
+
+ if (classh->classh_wk_fet_disable == 0x00 &&
+ (errata_chk == 0x01 || errata_chk == 0x02)) {
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DEL_MASK,
+ 0 << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set fet config %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (params_width(params)) {
+ case 8:
+ audin_format = CS35L35_SDIN_DEPTH_8;
+ break;
+ case 16:
+ audin_format = CS35L35_SDIN_DEPTH_16;
+ break;
+ case 24:
+ audin_format = CS35L35_SDIN_DEPTH_24;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported Width %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL,
+ CS35L35_AUDIN_DEPTH_MASK,
+ audin_format <<
+ CS35L35_AUDIN_DEPTH_SHIFT);
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL,
+ CS35L35_ADVIN_DEPTH_MASK,
+ audin_format <<
+ CS35L35_ADVIN_DEPTH_SHIFT);
+ }
+ }
+
+ if (cs35l35->i2s_mode) {
+ /* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+ if ((cs35l35->sclk / srate) % 4) {
+ dev_err(component->dev, "Unsupported sclk/fs ratio %d:%d\n",
+ cs35l35->sclk, srate);
+ return -EINVAL;
+ }
+ sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+ /* Only certain ratios supported when device is a clock consumer */
+ if (cs35l35->clock_consumer) {
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_48FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(component->dev, "ratio not supported\n");
+ return -EINVAL;
+ }
+ } else {
+ /* Only certain ratios supported when device is a clock provider */
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(component->dev, "ratio not supported\n");
+ return -EINVAL;
+ }
+ }
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLK_CTL3,
+ CS35L35_SP_SCLKS_MASK, sp_sclks <<
+ CS35L35_SP_SCLKS_SHIFT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set fsclk %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+ .count = ARRAY_SIZE(cs35l35_src_rates),
+ .list = cs35l35_src_rates,
+};
+
+static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 0 << CS35L35_PDM_MODE_SHIFT);
+
+ return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+ 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+ .count = ARRAY_SIZE(cs35l35_pdm_rates),
+ .list = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l35_pdm_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 1 << CS35L35_PDM_MODE_SHIFT);
+
+ return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+
+ /* Need the SCLK Frequency regardless of sysclk source for I2S */
+ cs35l35->sclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+ .startup = cs35l35_pcm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+ .startup = cs35l35_pdm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_hw_params,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+ {
+ .name = "cs35l35-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs35l35-pdm",
+ .id = 1,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_pdm_ops,
+ },
+};
+
+static int cs35l35_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ int clksrc;
+ int ret = 0;
+
+ switch (clk_id) {
+ case 0:
+ clksrc = CS35L35_CLK_SOURCE_MCLK;
+ break;
+ case 1:
+ clksrc = CS35L35_CLK_SOURCE_SCLK;
+ break;
+ case 2:
+ clksrc = CS35L35_CLK_SOURCE_PDM;
+ break;
+ default:
+ dev_err(component->dev, "Invalid CLK Source\n");
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs35l35->sysclk = freq;
+ break;
+ default:
+ dev_err(component->dev, "Invalid CLK Frequency Input : %d\n", freq);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_CLK_SOURCE_MASK,
+ clksrc << CS35L35_CLK_SOURCE_SHIFT);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set sysclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cs35l35_boost_inductor(struct cs35l35_private *cs35l35,
+ int inductor)
+{
+ struct regmap *regmap = cs35l35->regmap;
+ unsigned int bst_ipk = 0;
+
+ /*
+ * Digital Boost Converter Configuration for feedback,
+ * ramping, switching frequency, and estimation block seeding.
+ */
+
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_SWFREQ_MASK, 0x00);
+
+ regmap_read(regmap, CS35L35_BST_PEAK_I, &bst_ipk);
+ bst_ipk &= CS35L35_BST_IPK_MASK;
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x24);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x24);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x00);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x4E);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x01);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x47);
+ break;
+ case 1500: /* 1.5uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x02);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x3C);
+ break;
+ case 2200: /* 2.2uH */
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x19);
+ regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x25);
+ regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ,
+ CS35L35_BST_CONV_LBST_MASK, 0x03);
+
+ if (bst_ipk < 0x04)
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B);
+ else
+ regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x23);
+ break;
+ default:
+ dev_err(cs35l35->dev, "Invalid Inductor Value %d uH\n",
+ inductor);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l35_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+ int ret;
+
+ /* Set Platform Data */
+ if (cs35l35->pdata.bst_vctl)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl);
+
+ if (cs35l35->pdata.bst_ipk)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+ CS35L35_BST_IPK_MASK,
+ cs35l35->pdata.bst_ipk <<
+ CS35L35_BST_IPK_SHIFT);
+
+ ret = cs35l35_boost_inductor(cs35l35, cs35l35->pdata.boost_ind);
+ if (ret)
+ return ret;
+
+ if (cs35l35->pdata.gain_zc)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_GAIN_ZC_MASK,
+ cs35l35->pdata.gain_zc <<
+ CS35L35_AMP_GAIN_ZC_SHIFT);
+
+ if (cs35l35->pdata.aud_channel)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_RXLOC_CTL,
+ CS35L35_AUD_IN_LR_MASK,
+ cs35l35->pdata.aud_channel <<
+ CS35L35_AUD_IN_LR_SHIFT);
+
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ADVIN_RXLOC_CTL,
+ CS35L35_ADV_IN_LR_MASK,
+ cs35l35->pdata.adv_channel <<
+ CS35L35_ADV_IN_LR_SHIFT);
+ if (cs35l35->pdata.shared_bst)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+ CS35L35_CH_STEREO_MASK,
+ 1 << CS35L35_CH_STEREO_SHIFT);
+ ret = snd_soc_add_component_controls(component, cs35l35_adv_controls,
+ ARRAY_SIZE(cs35l35_adv_controls));
+ if (ret)
+ return ret;
+ }
+
+ if (cs35l35->pdata.sp_drv_str)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_SP_DRV_MASK,
+ cs35l35->pdata.sp_drv_str <<
+ CS35L35_SP_DRV_SHIFT);
+ if (cs35l35->pdata.sp_drv_unused)
+ regmap_update_bits(cs35l35->regmap, CS35L35_SP_FMT_CTL3,
+ CS35L35_SP_I2S_DRV_MASK,
+ cs35l35->pdata.sp_drv_unused <<
+ CS35L35_SP_I2S_DRV_SHIFT);
+
+ if (classh->classh_algo_enable) {
+ if (classh->classh_bst_override)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_BST_OVR_MASK,
+ classh->classh_bst_override <<
+ CS35L35_CH_BST_OVR_SHIFT);
+ if (classh->classh_bst_max_limit)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_BST_LIM_MASK,
+ classh->classh_bst_max_limit <<
+ CS35L35_CH_BST_LIM_SHIFT);
+ if (classh->classh_mem_depth)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_MEM_DEPTH_MASK,
+ classh->classh_mem_depth <<
+ CS35L35_CH_MEM_DEPTH_SHIFT);
+ if (classh->classh_headroom)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_HEADRM_CTL,
+ CS35L35_CH_HDRM_CTL_MASK,
+ classh->classh_headroom <<
+ CS35L35_CH_HDRM_CTL_SHIFT);
+ if (classh->classh_release_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_RELEASE_RATE,
+ CS35L35_CH_REL_RATE_MASK,
+ classh->classh_release_rate <<
+ CS35L35_CH_REL_RATE_SHIFT);
+ if (classh->classh_wk_fet_disable)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DIS_MASK,
+ classh->classh_wk_fet_disable <<
+ CS35L35_CH_WKFET_DIS_SHIFT);
+ if (classh->classh_wk_fet_delay)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DEL_MASK,
+ classh->classh_wk_fet_delay <<
+ CS35L35_CH_WKFET_DEL_SHIFT);
+ if (classh->classh_wk_fet_thld)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_THLD_MASK,
+ classh->classh_wk_fet_thld <<
+ CS35L35_CH_WKFET_THLD_SHIFT);
+ if (classh->classh_vpch_auto)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_AUTO_MASK,
+ classh->classh_vpch_auto <<
+ CS35L35_CH_VP_AUTO_SHIFT);
+ if (classh->classh_vpch_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_RATE_MASK,
+ classh->classh_vpch_rate <<
+ CS35L35_CH_VP_RATE_SHIFT);
+ if (classh->classh_vpch_man)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_MAN_MASK,
+ classh->classh_vpch_man <<
+ CS35L35_CH_VP_MAN_SHIFT);
+ }
+
+ if (monitor_config->is_present) {
+ if (monitor_config->vmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL,
+ CS35L35_VMON_DEPTH_MASK,
+ monitor_config->vmon_dpth <<
+ CS35L35_VMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->imon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL,
+ CS35L35_IMON_DEPTH_MASK,
+ monitor_config->imon_dpth <<
+ CS35L35_IMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->imon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->imon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_SCALE_CTL,
+ CS35L35_IMON_SCALE_MASK,
+ monitor_config->imon_scale <<
+ CS35L35_IMON_SCALE_SHIFT);
+ }
+ if (monitor_config->vpmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VPMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth <<
+ CS35L35_VPMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vpmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vbstmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VBSTMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth <<
+ CS35L35_VBSTMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vbstmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vbstmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpbrstat_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VPBRSTAT_DEPTH_MASK,
+ monitor_config->vpbrstat_dpth <<
+ CS35L35_VPBRSTAT_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpbrstat_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vpbrstat_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->zerofill_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_ZEROFILL_DEPTH_MASK,
+ monitor_config->zerofill_dpth <<
+ CS35L35_ZEROFILL_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->zerofill_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->zerofill_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l35 = {
+ .probe = cs35l35_component_probe,
+ .set_sysclk = cs35l35_component_set_sysclk,
+ .dapm_widgets = cs35l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+ .dapm_routes = cs35l35_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+ .controls = cs35l35_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs35l35_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L35_MAX_REGISTER,
+ .reg_defaults = cs35l35_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+ .volatile_reg = cs35l35_volatile_register,
+ .readable_reg = cs35l35_readable_register,
+ .precious_reg = cs35l35_precious_register,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+ struct cs35l35_private *cs35l35 = data;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+ /* Check to see if unmasked bits are active */
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ if (sticky2 & CS35L35_PDN_DONE)
+ complete(&cs35l35->pdn_done);
+
+ /* read the current values */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &current1);
+
+ /* handle the interrupts */
+ if (sticky1 & CS35L35_CAL_ERR) {
+ dev_crit(cs35l35->dev, "Calibration Error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_CAL_ERR)) {
+ pr_debug("%s : Cal error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS,
+ CS35L35_CAL_ERR_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_AMP_SHORT) {
+ dev_crit(cs35l35->dev, "AMP Short Error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_AMP_SHORT)) {
+ dev_dbg(cs35l35->dev, "Amp short error release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS,
+ CS35L35_SHORT_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTW) {
+ dev_warn(cs35l35->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTW)) {
+ dev_dbg(cs35l35->dev, "Over temperature warn release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS,
+ CS35L35_OTW_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTE) {
+ dev_crit(cs35l35->dev, "Over temperature error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTE)) {
+ dev_dbg(cs35l35->dev, "Over temperature error release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS,
+ CS35L35_OTE_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L35_BST_HIGH) {
+ dev_crit(cs35l35->dev, "VBST error: powering off!\n");
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L35_LBST_SHORT) {
+ dev_crit(cs35l35->dev, "LBST error: powering off!\n");
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky2 & CS35L35_VPBR_ERR)
+ dev_dbg(cs35l35->dev, "Error: Reactive Brownout\n");
+
+ if (sticky4 & CS35L35_VMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: VMON overflow\n");
+
+ if (sticky4 & CS35L35_IMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: IMON overflow\n");
+
+ return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l35_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct device_node *classh, *signal_format;
+ struct classh_cfg *classh_config = &pdata->classh_algo;
+ struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+ unsigned int val32 = 0;
+ u8 monitor_array[4];
+ const int imon_array_size = ARRAY_SIZE(monitor_array);
+ const int mon_array_size = imon_array_size - 1;
+ int ret = 0;
+
+ if (!np)
+ return 0;
+
+ pdata->bst_pdn_fet_on = of_property_read_bool(np,
+ "cirrus,boost-pdn-fet-on");
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32);
+ if (ret >= 0) {
+ if (val32 < 2600 || val32 > 9000) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val32);
+ return -EINVAL;
+ }
+ pdata->bst_vctl = ((val32 - 2600) / 100) + 1;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32);
+ if (ret >= 0) {
+ if (val32 < 1680 || val32 > 4480) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %u mA\n", val32);
+ return -EINVAL;
+ }
+
+ pdata->bst_ipk = ((val32 - 1680) / 110) | CS35L35_VALID_PDATA;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val32);
+ if (ret >= 0) {
+ pdata->boost_ind = val32;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+ pdata->sp_drv_str = val32;
+ if (of_property_read_u32(np, "cirrus,sp-drv-unused", &val32) >= 0)
+ pdata->sp_drv_unused = val32 | CS35L35_VALID_PDATA;
+
+ pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+ if (pdata->stereo) {
+ ret = of_property_read_u32(np, "cirrus,audio-channel", &val32);
+ if (ret >= 0)
+ pdata->aud_channel = val32;
+
+ ret = of_property_read_u32(np, "cirrus,advisory-channel",
+ &val32);
+ if (ret >= 0)
+ pdata->adv_channel = val32;
+
+ pdata->shared_bst = of_property_read_bool(np,
+ "cirrus,shared-boost");
+ }
+
+ pdata->ext_bst = of_property_read_bool(np, "cirrus,external-boost");
+
+ pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+ classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+ classh_config->classh_algo_enable = (classh != NULL);
+
+ if (classh_config->classh_algo_enable) {
+ classh_config->classh_bst_override =
+ of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-mem-depth",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_mem_depth = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-release-rate",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_release_rate = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-headroom",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_headroom = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-wk-fet-disable",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_disable = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_wk_fet_delay = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_thld = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_auto = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_rate = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-man",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_vpch_man = val32;
+ }
+ of_node_put(classh);
+
+ /* frame depth location */
+ signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+ monitor_config->is_present = signal_format ? true : false;
+ if (monitor_config->is_present) {
+ ret = of_property_read_u8_array(signal_format, "cirrus,imon",
+ monitor_array, imon_array_size);
+ if (!ret) {
+ monitor_config->imon_specs = true;
+ monitor_config->imon_dpth = monitor_array[0];
+ monitor_config->imon_loc = monitor_array[1];
+ monitor_config->imon_frm = monitor_array[2];
+ monitor_config->imon_scale = monitor_array[3];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vmon_specs = true;
+ monitor_config->vmon_dpth = monitor_array[0];
+ monitor_config->vmon_loc = monitor_array[1];
+ monitor_config->vmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vpmon_specs = true;
+ monitor_config->vpmon_dpth = monitor_array[0];
+ monitor_config->vpmon_loc = monitor_array[1];
+ monitor_config->vpmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vbstmon_specs = true;
+ monitor_config->vbstmon_dpth = monitor_array[0];
+ monitor_config->vbstmon_loc = monitor_array[1];
+ monitor_config->vbstmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vpbrstat_specs = true;
+ monitor_config->vpbrstat_dpth = monitor_array[0];
+ monitor_config->vpbrstat_loc = monitor_array[1];
+ monitor_config->vpbrstat_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->zerofill_specs = true;
+ monitor_config->zerofill_dpth = monitor_array[0];
+ monitor_config->zerofill_loc = monitor_array[1];
+ monitor_config->zerofill_frm = monitor_array[2];
+ }
+ }
+ of_node_put(signal_format);
+
+ return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+ { 0x7F, 0x99 },
+ { 0x00, 0x99 },
+ { 0x52, 0x22 },
+ { 0x04, 0x14 },
+ { 0x6D, 0x44 },
+ { 0x24, 0x10 },
+ { 0x58, 0xC4 },
+ { 0x00, 0x98 },
+ { 0x18, 0x08 },
+ { 0x00, 0x00 },
+ { 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l35_private *cs35l35;
+ struct device *dev = &i2c_client->dev;
+ struct cs35l35_platform_data *pdata = dev_get_platdata(dev);
+ int i, devid;
+ int ret;
+ unsigned int reg;
+
+ cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
+ if (!cs35l35)
+ return -ENOMEM;
+
+ cs35l35->dev = dev;
+
+ i2c_set_clientdata(i2c_client, cs35l35);
+ cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+ if (IS_ERR(cs35l35->regmap)) {
+ ret = PTR_ERR(cs35l35->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+ cs35l35->supplies[i].supply = cs35l35_supplies[i];
+
+ cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+ ret = devm_regulator_bulk_get(dev, cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l35->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(dev, sizeof(struct cs35l35_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ if (i2c_client->dev.of_node) {
+ ret = cs35l35_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+ cs35l35->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be valid if in stereo mode */
+ cs35l35->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l35->reset_gpio)) {
+ ret = PTR_ERR(cs35l35->reset_gpio);
+ cs35l35->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+
+ cs35l35_reset(cs35l35);
+
+ init_completion(&cs35l35->pdn_done);
+
+ ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l35_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW |
+ IRQF_SHARED, "cs35l35", cs35l35);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs35l35->regmap, CS35L35_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
+
+ if (devid != CS35L35_CHIP_ID) {
+ dev_err(dev, "CS35L35 Device ID (%X). Expected ID %X\n",
+ devid, CS35L35_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+ ARRAY_SIZE(cs35l35_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply errata patch: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(dev, "Cirrus Logic CS35L35 (%x), Revision: %02X\n",
+ devid, reg & 0xFF);
+
+ /* Set the INT Masks for critical errors */
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1,
+ CS35L35_INT1_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2,
+ CS35L35_INT2_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3,
+ CS35L35_INT3_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4,
+ CS35L35_INT4_CRIT_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PWR2_PDN_MASK,
+ CS35L35_PWR2_PDN_MASK);
+
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+ CS35L35_PWR3_PDN_MASK,
+ CS35L35_PWR3_PDN_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l35,
+ cs35l35_dai, ARRAY_SIZE(cs35l35_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regulator_bulk_disable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+
+ return ret;
+}
+
+static void cs35l35_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs35l35_private *cs35l35 = i2c_get_clientdata(i2c_client);
+
+ regulator_bulk_disable(cs35l35->num_supplies, cs35l35->supplies);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+ {.compatible = "cirrus,cs35l35"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+ {"cs35l35"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+ .driver = {
+ .name = "cs35l35",
+ .of_match_table = cs35l35_of_match,
+ },
+ .id_table = cs35l35_id,
+ .probe = cs35l35_i2c_probe,
+ .remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 000000000000..5e4509f41b32
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#ifndef __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG 0x01
+#define CS35L35_LASTREG 0x7E
+#define CS35L35_CHIP_ID 0x00035A35
+#define CS35L35_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L35_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L35_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L35_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L35_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L35_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L35_CLK_CTL1 0x0A /* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2 0x0B /* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3 0x0C /* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1 0x0D /* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2 0x0E /* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3 0x0F /* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL 0x13 /* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL 0x17 /* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL 0x19 /* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL 0x1A /* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL 0x1B /* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL 0x1C /* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL 0x34 /* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L35_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL 0x3D /* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL 0x51 /* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL 0x52 /* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL 0x53 /* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL 0x54 /* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL 0x55 /* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL 0x56 /* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL 0x57 /* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL 0x58 /* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL 0x59 /* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL 0x5A /* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL 0x5B /* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL 0x5C /* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL 0x5D /* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS 0x78 /* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER 0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST 0x80
+#define CS35L35_DISCHG_FLT 0x02
+#define CS35L35_PDN_ALL 0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON 0x80
+#define CS35L35_PDN_IMON 0x40
+#define CS35L35_PDN_CLASSH 0x20
+#define CS35L35_PDN_VPBR 0x10
+#define CS35L35_PDN_BST 0x04
+#define CS35L35_PDN_AMP 0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT 0x10
+#define CS35L35_PDN_VMON_OUT 0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK 0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT 0
+#define CS35L35_ADVIN_DEPTH_MASK 0x0C
+#define CS35L35_ADVIN_DEPTH_SHIFT 2
+#define CS35L35_SDIN_DEPTH_8 0x01
+#define CS35L35_SDIN_DEPTH_16 0x02
+#define CS35L35_SDIN_DEPTH_24 0x03
+
+#define CS35L35_SDOUT_DEPTH_8 0x01
+#define CS35L35_SDOUT_DEPTH_12 0x02
+#define CS35L35_SDOUT_DEPTH_16 0x03
+
+#define CS35L35_AUD_IN_LR_MASK 0x80
+#define CS35L35_AUD_IN_LR_SHIFT 7
+#define CS35L35_ADV_IN_LR_MASK 0x80
+#define CS35L35_ADV_IN_LR_SHIFT 7
+#define CS35L35_AUD_IN_LOC_MASK 0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT 0
+#define CS35L35_ADV_IN_LOC_MASK 0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT 0
+
+#define CS35L35_IMON_DEPTH_MASK 0x03
+#define CS35L35_IMON_DEPTH_SHIFT 0
+#define CS35L35_VMON_DEPTH_MASK 0x0C
+#define CS35L35_VMON_DEPTH_SHIFT 2
+#define CS35L35_VBSTMON_DEPTH_MASK 0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT 0
+#define CS35L35_VPMON_DEPTH_MASK 0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT 2
+#define CS35L35_VPBRSTAT_DEPTH_MASK 0x30
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT 4
+#define CS35L35_ZEROFILL_DEPTH_MASK 0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT 0x00
+
+#define CS35L35_MON_TXLOC_MASK 0x3F
+#define CS35L35_MON_TXLOC_SHIFT 0
+#define CS35L35_MON_FRM_MASK 0x80
+#define CS35L35_MON_FRM_SHIFT 7
+
+#define CS35L35_IMON_SCALE_MASK 0xF8
+#define CS35L35_IMON_SCALE_SHIFT 3
+
+#define CS35L35_MS_MASK 0x80
+#define CS35L35_MS_SHIFT 7
+#define CS35L35_SPMODE_MASK 0x40
+#define CS35L35_SP_DRV_MASK 0x10
+#define CS35L35_SP_DRV_SHIFT 4
+#define CS35L35_CLK_CTL2_MASK 0xFF
+#define CS35L35_PDM_MODE_MASK 0x40
+#define CS35L35_PDM_MODE_SHIFT 6
+#define CS35L35_CLK_SOURCE_MASK 0x03
+#define CS35L35_CLK_SOURCE_SHIFT 0
+#define CS35L35_CLK_SOURCE_MCLK 0
+#define CS35L35_CLK_SOURCE_SCLK 1
+#define CS35L35_CLK_SOURCE_PDM 2
+
+#define CS35L35_SP_SCLKS_MASK 0x0F
+#define CS35L35_SP_SCLKS_SHIFT 0x00
+#define CS35L35_SP_SCLKS_16FS 0x03
+#define CS35L35_SP_SCLKS_32FS 0x07
+#define CS35L35_SP_SCLKS_48FS 0x0B
+#define CS35L35_SP_SCLKS_64FS 0x0F
+#define CS35L35_SP_RATE_MASK 0xC0
+#define CS35L35_SP_RATE_SHIFT 6
+
+#define CS35L35_PDN_BST_MASK 0x06
+#define CS35L35_PDN_BST_FETON_SHIFT 1
+#define CS35L35_PDN_BST_FETOFF_SHIFT 2
+#define CS35L35_PWR2_PDN_MASK 0xE0
+#define CS35L35_PWR3_PDN_MASK 0x1E
+#define CS35L35_PDN_ALL_MASK 0x01
+#define CS35L35_DISCHG_FILT_MASK 0x02
+#define CS35L35_DISCHG_FILT_SHIFT 1
+#define CS35L35_MCLK_DIS_MASK 0x04
+#define CS35L35_MCLK_DIS_SHIFT 2
+
+#define CS35L35_BST_CTL_MASK 0x7F
+#define CS35L35_BST_CTL_SHIFT 0
+#define CS35L35_BST_IPK_MASK 0x1F
+#define CS35L35_BST_IPK_SHIFT 0
+#define CS35L35_AMP_MUTE_MASK 0x20
+#define CS35L35_AMP_MUTE_SHIFT 5
+#define CS35L35_AMP_GAIN_ZC_MASK 0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT 4
+
+#define CS35L35_AMP_DIGSFT_MASK 0x02
+#define CS35L35_AMP_DIGSFT_SHIFT 1
+
+/* CS35L35_SP_FMT_CTL3 */
+#define CS35L35_SP_I2S_DRV_MASK 0x03
+#define CS35L35_SP_I2S_DRV_SHIFT 0
+
+/* Boost Converter Config */
+#define CS35L35_BST_CONV_COEFF_MASK 0xFF
+#define CS35L35_BST_CONV_SLOPE_MASK 0xFF
+#define CS35L35_BST_CONV_LBST_MASK 0x03
+#define CS35L35_BST_CONV_SWFREQ_MASK 0xF0
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK 0x40
+#define CS35L35_CH_STEREO_SHIFT 6
+#define CS35L35_CH_BST_OVR_MASK 0x04
+#define CS35L35_CH_BST_OVR_SHIFT 2
+#define CS35L35_CH_BST_LIM_MASK 0x08
+#define CS35L35_CH_BST_LIM_SHIFT 3
+#define CS35L35_CH_MEM_DEPTH_MASK 0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT 0
+#define CS35L35_CH_HDRM_CTL_MASK 0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT 0
+#define CS35L35_CH_REL_RATE_MASK 0xFF
+#define CS35L35_CH_REL_RATE_SHIFT 0
+#define CS35L35_CH_WKFET_DIS_MASK 0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT 7
+#define CS35L35_CH_WKFET_DEL_MASK 0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT 4
+#define CS35L35_CH_WKFET_THLD_MASK 0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT 0
+#define CS35L35_CH_VP_AUTO_MASK 0x80
+#define CS35L35_CH_VP_AUTO_SHIFT 7
+#define CS35L35_CH_VP_RATE_MASK 0x60
+#define CS35L35_CH_VP_RATE_SHIFT 5
+#define CS35L35_CH_VP_MAN_MASK 0x1F
+#define CS35L35_CH_VP_MAN_SHIFT 0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS 0x80
+#define CS35L35_SHORT_RLS 0x04
+#define CS35L35_OTW_RLS 0x02
+#define CS35L35_OTE_RLS 0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK 0x38
+#define CS35L35_INT2_CRIT_MASK 0xEF
+#define CS35L35_INT3_CRIT_MASK 0xEE
+#define CS35L35_INT4_CRIT_MASK 0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT 4
+#define CS35L35_M_PDN_DONE_MASK 0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR 0x80
+#define CS35L35_OTP_ERR 0x40
+#define CS35L35_LRCLK_ERR 0x20
+#define CS35L35_SPCLK_ERR 0x10
+#define CS35L35_MCLK_ERR 0x08
+#define CS35L35_AMP_SHORT 0x04
+#define CS35L35_OTW 0x02
+#define CS35L35_OTE 0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE 0x10
+#define CS35L35_VPBR_ERR 0x02
+#define CS35L35_VPBR_CLR 0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH 0x10
+#define CS35L35_BST_HIGH_FLAG 0x08
+#define CS35L35_BST_IPK_FLAG 0x04
+#define CS35L35_LBST_SHORT 0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL 0x08
+#define CS35L35_IMON_OVFL 0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct cs35l35_private {
+ struct device *dev;
+ struct cs35l35_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int sysclk;
+ int sclk;
+ bool pdm_mode;
+ bool i2s_mode;
+ bool clock_consumer;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+ "VA",
+ "VP",
+};
+
+#endif
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
new file mode 100644
index 000000000000..93818d7ec1a7
--- /dev/null
+++ b/sound/soc/codecs/cs35l36.c
@@ -0,0 +1,1940 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l36.c -- CS35L36 ALSA SoC audio driver
+//
+// Copyright 2018 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l36.h>
+#include <linux/completion.h>
+
+#include "cs35l36.h"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L36_VALID_PDATA 0x80000000
+
+static const char * const cs35l36_supplies[] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l36_private {
+ struct device *dev;
+ struct cs35l36_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int clksrc;
+ int chip_version;
+ int rev_id;
+ int ldm_mode_sel;
+ struct gpio_desc *reset_gpio;
+};
+
+struct cs35l36_pll_config {
+ int freq;
+ int clk_cfg;
+ int fll_igain;
+};
+
+static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = {
+ {32768, 0x00, 0x05},
+ {8000, 0x01, 0x03},
+ {11025, 0x02, 0x03},
+ {12000, 0x03, 0x03},
+ {16000, 0x04, 0x04},
+ {22050, 0x05, 0x04},
+ {24000, 0x06, 0x04},
+ {32000, 0x07, 0x05},
+ {44100, 0x08, 0x05},
+ {48000, 0x09, 0x05},
+ {88200, 0x0A, 0x06},
+ {96000, 0x0B, 0x06},
+ {128000, 0x0C, 0x07},
+ {176400, 0x0D, 0x07},
+ {192000, 0x0E, 0x07},
+ {256000, 0x0F, 0x08},
+ {352800, 0x10, 0x08},
+ {384000, 0x11, 0x08},
+ {512000, 0x12, 0x09},
+ {705600, 0x13, 0x09},
+ {750000, 0x14, 0x09},
+ {768000, 0x15, 0x09},
+ {1000000, 0x16, 0x0A},
+ {1024000, 0x17, 0x0A},
+ {1200000, 0x18, 0x0A},
+ {1411200, 0x19, 0x0A},
+ {1500000, 0x1A, 0x0A},
+ {1536000, 0x1B, 0x0A},
+ {2000000, 0x1C, 0x0A},
+ {2048000, 0x1D, 0x0A},
+ {2400000, 0x1E, 0x0A},
+ {2822400, 0x1F, 0x0A},
+ {3000000, 0x20, 0x0A},
+ {3072000, 0x21, 0x0A},
+ {3200000, 0x22, 0x0A},
+ {4000000, 0x23, 0x0A},
+ {4096000, 0x24, 0x0A},
+ {4800000, 0x25, 0x0A},
+ {5644800, 0x26, 0x0A},
+ {6000000, 0x27, 0x0A},
+ {6144000, 0x28, 0x0A},
+ {6250000, 0x29, 0x08},
+ {6400000, 0x2A, 0x0A},
+ {6500000, 0x2B, 0x08},
+ {6750000, 0x2C, 0x09},
+ {7526400, 0x2D, 0x0A},
+ {8000000, 0x2E, 0x0A},
+ {8192000, 0x2F, 0x0A},
+ {9600000, 0x30, 0x0A},
+ {11289600, 0x31, 0x0A},
+ {12000000, 0x32, 0x0A},
+ {12288000, 0x33, 0x0A},
+ {12500000, 0x34, 0x08},
+ {12800000, 0x35, 0x0A},
+ {13000000, 0x36, 0x0A},
+ {13500000, 0x37, 0x0A},
+ {19200000, 0x38, 0x0A},
+ {22579200, 0x39, 0x0A},
+ {24000000, 0x3A, 0x0A},
+ {24576000, 0x3B, 0x0A},
+ {25000000, 0x3C, 0x0A},
+ {25600000, 0x3D, 0x0A},
+ {26000000, 0x3E, 0x0A},
+ {27000000, 0x3F, 0x0A},
+};
+
+static const struct reg_default cs35l36_reg[] = {
+ {CS35L36_TESTKEY_CTRL, 0x00000000},
+ {CS35L36_USERKEY_CTL, 0x00000000},
+ {CS35L36_OTP_CTRL1, 0x00002460},
+ {CS35L36_OTP_CTRL2, 0x00000000},
+ {CS35L36_OTP_CTRL3, 0x00000000},
+ {CS35L36_OTP_CTRL4, 0x00000000},
+ {CS35L36_OTP_CTRL5, 0x00000000},
+ {CS35L36_PAC_CTL1, 0x00000004},
+ {CS35L36_PAC_CTL2, 0x00000000},
+ {CS35L36_PAC_CTL3, 0x00000000},
+ {CS35L36_PWR_CTRL1, 0x00000000},
+ {CS35L36_PWR_CTRL2, 0x00003321},
+ {CS35L36_PWR_CTRL3, 0x01000010},
+ {CS35L36_CTRL_OVRRIDE, 0x00000002},
+ {CS35L36_AMP_OUT_MUTE, 0x00000000},
+ {CS35L36_OTP_TRIM_STATUS, 0x00000000},
+ {CS35L36_DISCH_FILT, 0x00000000},
+ {CS35L36_PROTECT_REL_ERR, 0x00000000},
+ {CS35L36_PAD_INTERFACE, 0x00000038},
+ {CS35L36_PLL_CLK_CTRL, 0x00000010},
+ {CS35L36_GLOBAL_CLK_CTRL, 0x00000003},
+ {CS35L36_ADC_CLK_CTRL, 0x00000000},
+ {CS35L36_SWIRE_CLK_CTRL, 0x00000000},
+ {CS35L36_SP_SCLK_CLK_CTRL, 0x00000000},
+ {CS35L36_MDSYNC_EN, 0x00000000},
+ {CS35L36_MDSYNC_TX_ID, 0x00000000},
+ {CS35L36_MDSYNC_PWR_CTRL, 0x00000000},
+ {CS35L36_MDSYNC_DATA_TX, 0x00000000},
+ {CS35L36_MDSYNC_TX_STATUS, 0x00000002},
+ {CS35L36_MDSYNC_RX_STATUS, 0x00000000},
+ {CS35L36_MDSYNC_ERR_STATUS, 0x00000000},
+ {CS35L36_BSTCVRT_VCTRL1, 0x00000000},
+ {CS35L36_BSTCVRT_VCTRL2, 0x00000001},
+ {CS35L36_BSTCVRT_PEAK_CUR, 0x0000004A},
+ {CS35L36_BSTCVRT_SFT_RAMP, 0x00000003},
+ {CS35L36_BSTCVRT_COEFF, 0x00002424},
+ {CS35L36_BSTCVRT_SLOPE_LBST, 0x00005800},
+ {CS35L36_BSTCVRT_SW_FREQ, 0x00010000},
+ {CS35L36_BSTCVRT_DCM_CTRL, 0x00002001},
+ {CS35L36_BSTCVRT_DCM_MODE_FORCE, 0x00000000},
+ {CS35L36_BSTCVRT_OVERVOLT_CTRL, 0x00000130},
+ {CS35L36_VPI_LIMIT_MODE, 0x00000000},
+ {CS35L36_VPI_LIMIT_MINMAX, 0x00003000},
+ {CS35L36_VPI_VP_THLD, 0x00101010},
+ {CS35L36_VPI_TRACK_CTRL, 0x00000000},
+ {CS35L36_VPI_TRIG_MODE_CTRL, 0x00000000},
+ {CS35L36_VPI_TRIG_STEPS, 0x00000000},
+ {CS35L36_VI_SPKMON_FILT, 0x00000003},
+ {CS35L36_VI_SPKMON_GAIN, 0x00000909},
+ {CS35L36_VI_SPKMON_IP_SEL, 0x00000000},
+ {CS35L36_DTEMP_WARN_THLD, 0x00000002},
+ {CS35L36_DTEMP_STATUS, 0x00000000},
+ {CS35L36_VPVBST_FS_SEL, 0x00000001},
+ {CS35L36_VPVBST_VP_CTRL, 0x000001C0},
+ {CS35L36_VPVBST_VBST_CTRL, 0x000001C0},
+ {CS35L36_ASP_TX_PIN_CTRL, 0x00000028},
+ {CS35L36_ASP_RATE_CTRL, 0x00090000},
+ {CS35L36_ASP_FORMAT, 0x00000002},
+ {CS35L36_ASP_FRAME_CTRL, 0x00180018},
+ {CS35L36_ASP_TX1_TX2_SLOT, 0x00010000},
+ {CS35L36_ASP_TX3_TX4_SLOT, 0x00030002},
+ {CS35L36_ASP_TX5_TX6_SLOT, 0x00050004},
+ {CS35L36_ASP_TX7_TX8_SLOT, 0x00070006},
+ {CS35L36_ASP_RX1_SLOT, 0x00000000},
+ {CS35L36_ASP_RX_TX_EN, 0x00000000},
+ {CS35L36_ASP_RX1_SEL, 0x00000008},
+ {CS35L36_ASP_TX1_SEL, 0x00000018},
+ {CS35L36_ASP_TX2_SEL, 0x00000019},
+ {CS35L36_ASP_TX3_SEL, 0x00000028},
+ {CS35L36_ASP_TX4_SEL, 0x00000029},
+ {CS35L36_ASP_TX5_SEL, 0x00000020},
+ {CS35L36_ASP_TX6_SEL, 0x00000000},
+ {CS35L36_SWIRE_P1_TX1_SEL, 0x00000018},
+ {CS35L36_SWIRE_P1_TX2_SEL, 0x00000019},
+ {CS35L36_SWIRE_P2_TX1_SEL, 0x00000028},
+ {CS35L36_SWIRE_P2_TX2_SEL, 0x00000029},
+ {CS35L36_SWIRE_P2_TX3_SEL, 0x00000020},
+ {CS35L36_SWIRE_DP1_FIFO_CFG, 0x0000001B},
+ {CS35L36_SWIRE_DP2_FIFO_CFG, 0x0000001B},
+ {CS35L36_SWIRE_DP3_FIFO_CFG, 0x0000001B},
+ {CS35L36_SWIRE_PCM_RX_DATA, 0x00000000},
+ {CS35L36_SWIRE_FS_SEL, 0x00000001},
+ {CS35L36_AMP_DIG_VOL_CTRL, 0x00008000},
+ {CS35L36_VPBR_CFG, 0x02AA1905},
+ {CS35L36_VBBR_CFG, 0x02AA1905},
+ {CS35L36_VPBR_STATUS, 0x00000000},
+ {CS35L36_VBBR_STATUS, 0x00000000},
+ {CS35L36_OVERTEMP_CFG, 0x00000001},
+ {CS35L36_AMP_ERR_VOL, 0x00000000},
+ {CS35L36_CLASSH_CFG, 0x000B0405},
+ {CS35L36_CLASSH_FET_DRV_CFG, 0x00000111},
+ {CS35L36_NG_CFG, 0x00000033},
+ {CS35L36_AMP_GAIN_CTRL, 0x00000273},
+ {CS35L36_PWM_MOD_IO_CTRL, 0x00000000},
+ {CS35L36_PWM_MOD_STATUS, 0x00000000},
+ {CS35L36_DAC_MSM_CFG, 0x00000000},
+ {CS35L36_AMP_SLOPE_CTRL, 0x00000B00},
+ {CS35L36_AMP_PDM_VOLUME, 0x00000000},
+ {CS35L36_AMP_PDM_RATE_CTRL, 0x00000000},
+ {CS35L36_PDM_CH_SEL, 0x00000000},
+ {CS35L36_AMP_NG_CTRL, 0x0000212F},
+ {CS35L36_PDM_HIGHFILT_CTRL, 0x00000000},
+ {CS35L36_PAC_INT0_CTRL, 0x00000001},
+ {CS35L36_PAC_INT1_CTRL, 0x00000001},
+ {CS35L36_PAC_INT2_CTRL, 0x00000001},
+ {CS35L36_PAC_INT3_CTRL, 0x00000001},
+ {CS35L36_PAC_INT4_CTRL, 0x00000001},
+ {CS35L36_PAC_INT5_CTRL, 0x00000001},
+ {CS35L36_PAC_INT6_CTRL, 0x00000001},
+ {CS35L36_PAC_INT7_CTRL, 0x00000001},
+};
+
+static bool cs35l36_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L36_SW_RESET:
+ case CS35L36_SW_REV:
+ case CS35L36_HW_REV:
+ case CS35L36_TESTKEY_CTRL:
+ case CS35L36_USERKEY_CTL:
+ case CS35L36_OTP_MEM30:
+ case CS35L36_OTP_CTRL1:
+ case CS35L36_OTP_CTRL2:
+ case CS35L36_OTP_CTRL3:
+ case CS35L36_OTP_CTRL4:
+ case CS35L36_OTP_CTRL5:
+ case CS35L36_PAC_CTL1:
+ case CS35L36_PAC_CTL2:
+ case CS35L36_PAC_CTL3:
+ case CS35L36_DEVICE_ID:
+ case CS35L36_FAB_ID:
+ case CS35L36_REV_ID:
+ case CS35L36_PWR_CTRL1:
+ case CS35L36_PWR_CTRL2:
+ case CS35L36_PWR_CTRL3:
+ case CS35L36_CTRL_OVRRIDE:
+ case CS35L36_AMP_OUT_MUTE:
+ case CS35L36_OTP_TRIM_STATUS:
+ case CS35L36_DISCH_FILT:
+ case CS35L36_PROTECT_REL_ERR:
+ case CS35L36_PAD_INTERFACE:
+ case CS35L36_PLL_CLK_CTRL:
+ case CS35L36_GLOBAL_CLK_CTRL:
+ case CS35L36_ADC_CLK_CTRL:
+ case CS35L36_SWIRE_CLK_CTRL:
+ case CS35L36_SP_SCLK_CLK_CTRL:
+ case CS35L36_TST_FS_MON0:
+ case CS35L36_MDSYNC_EN:
+ case CS35L36_MDSYNC_TX_ID:
+ case CS35L36_MDSYNC_PWR_CTRL:
+ case CS35L36_MDSYNC_DATA_TX:
+ case CS35L36_MDSYNC_TX_STATUS:
+ case CS35L36_MDSYNC_RX_STATUS:
+ case CS35L36_MDSYNC_ERR_STATUS:
+ case CS35L36_BSTCVRT_VCTRL1:
+ case CS35L36_BSTCVRT_VCTRL2:
+ case CS35L36_BSTCVRT_PEAK_CUR:
+ case CS35L36_BSTCVRT_SFT_RAMP:
+ case CS35L36_BSTCVRT_COEFF:
+ case CS35L36_BSTCVRT_SLOPE_LBST:
+ case CS35L36_BSTCVRT_SW_FREQ:
+ case CS35L36_BSTCVRT_DCM_CTRL:
+ case CS35L36_BSTCVRT_DCM_MODE_FORCE:
+ case CS35L36_BSTCVRT_OVERVOLT_CTRL:
+ case CS35L36_BST_TST_MANUAL:
+ case CS35L36_BST_ANA2_TEST:
+ case CS35L36_VPI_LIMIT_MODE:
+ case CS35L36_VPI_LIMIT_MINMAX:
+ case CS35L36_VPI_VP_THLD:
+ case CS35L36_VPI_TRACK_CTRL:
+ case CS35L36_VPI_TRIG_MODE_CTRL:
+ case CS35L36_VPI_TRIG_STEPS:
+ case CS35L36_VI_SPKMON_FILT:
+ case CS35L36_VI_SPKMON_GAIN:
+ case CS35L36_VI_SPKMON_IP_SEL:
+ case CS35L36_DTEMP_WARN_THLD:
+ case CS35L36_DTEMP_STATUS:
+ case CS35L36_VPVBST_FS_SEL:
+ case CS35L36_VPVBST_VP_CTRL:
+ case CS35L36_VPVBST_VBST_CTRL:
+ case CS35L36_ASP_TX_PIN_CTRL:
+ case CS35L36_ASP_RATE_CTRL:
+ case CS35L36_ASP_FORMAT:
+ case CS35L36_ASP_FRAME_CTRL:
+ case CS35L36_ASP_TX1_TX2_SLOT:
+ case CS35L36_ASP_TX3_TX4_SLOT:
+ case CS35L36_ASP_TX5_TX6_SLOT:
+ case CS35L36_ASP_TX7_TX8_SLOT:
+ case CS35L36_ASP_RX1_SLOT:
+ case CS35L36_ASP_RX_TX_EN:
+ case CS35L36_ASP_RX1_SEL:
+ case CS35L36_ASP_TX1_SEL:
+ case CS35L36_ASP_TX2_SEL:
+ case CS35L36_ASP_TX3_SEL:
+ case CS35L36_ASP_TX4_SEL:
+ case CS35L36_ASP_TX5_SEL:
+ case CS35L36_ASP_TX6_SEL:
+ case CS35L36_SWIRE_P1_TX1_SEL:
+ case CS35L36_SWIRE_P1_TX2_SEL:
+ case CS35L36_SWIRE_P2_TX1_SEL:
+ case CS35L36_SWIRE_P2_TX2_SEL:
+ case CS35L36_SWIRE_P2_TX3_SEL:
+ case CS35L36_SWIRE_DP1_FIFO_CFG:
+ case CS35L36_SWIRE_DP2_FIFO_CFG:
+ case CS35L36_SWIRE_DP3_FIFO_CFG:
+ case CS35L36_SWIRE_PCM_RX_DATA:
+ case CS35L36_SWIRE_FS_SEL:
+ case CS35L36_AMP_DIG_VOL_CTRL:
+ case CS35L36_VPBR_CFG:
+ case CS35L36_VBBR_CFG:
+ case CS35L36_VPBR_STATUS:
+ case CS35L36_VBBR_STATUS:
+ case CS35L36_OVERTEMP_CFG:
+ case CS35L36_AMP_ERR_VOL:
+ case CS35L36_CLASSH_CFG:
+ case CS35L36_CLASSH_FET_DRV_CFG:
+ case CS35L36_NG_CFG:
+ case CS35L36_AMP_GAIN_CTRL:
+ case CS35L36_PWM_MOD_IO_CTRL:
+ case CS35L36_PWM_MOD_STATUS:
+ case CS35L36_DAC_MSM_CFG:
+ case CS35L36_AMP_SLOPE_CTRL:
+ case CS35L36_AMP_PDM_VOLUME:
+ case CS35L36_AMP_PDM_RATE_CTRL:
+ case CS35L36_PDM_CH_SEL:
+ case CS35L36_AMP_NG_CTRL:
+ case CS35L36_PDM_HIGHFILT_CTRL:
+ case CS35L36_INT1_STATUS:
+ case CS35L36_INT2_STATUS:
+ case CS35L36_INT3_STATUS:
+ case CS35L36_INT4_STATUS:
+ case CS35L36_INT1_RAW_STATUS:
+ case CS35L36_INT2_RAW_STATUS:
+ case CS35L36_INT3_RAW_STATUS:
+ case CS35L36_INT4_RAW_STATUS:
+ case CS35L36_INT1_MASK:
+ case CS35L36_INT2_MASK:
+ case CS35L36_INT3_MASK:
+ case CS35L36_INT4_MASK:
+ case CS35L36_INT1_EDGE_LVL_CTRL:
+ case CS35L36_INT3_EDGE_LVL_CTRL:
+ case CS35L36_PAC_INT_STATUS:
+ case CS35L36_PAC_INT_RAW_STATUS:
+ case CS35L36_PAC_INT_FLUSH_CTRL:
+ case CS35L36_PAC_INT0_CTRL:
+ case CS35L36_PAC_INT1_CTRL:
+ case CS35L36_PAC_INT2_CTRL:
+ case CS35L36_PAC_INT3_CTRL:
+ case CS35L36_PAC_INT4_CTRL:
+ case CS35L36_PAC_INT5_CTRL:
+ case CS35L36_PAC_INT6_CTRL:
+ case CS35L36_PAC_INT7_CTRL:
+ return true;
+ default:
+ if (reg >= CS35L36_PAC_PMEM_WORD0 &&
+ reg <= CS35L36_PAC_PMEM_WORD1023)
+ return true;
+ else
+ return false;
+ }
+}
+
+static bool cs35l36_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L36_TESTKEY_CTRL:
+ case CS35L36_USERKEY_CTL:
+ case CS35L36_TST_FS_MON0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l36_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L36_SW_RESET:
+ case CS35L36_SW_REV:
+ case CS35L36_HW_REV:
+ case CS35L36_TESTKEY_CTRL:
+ case CS35L36_USERKEY_CTL:
+ case CS35L36_DEVICE_ID:
+ case CS35L36_FAB_ID:
+ case CS35L36_REV_ID:
+ case CS35L36_INT1_STATUS:
+ case CS35L36_INT2_STATUS:
+ case CS35L36_INT3_STATUS:
+ case CS35L36_INT4_STATUS:
+ case CS35L36_INT1_RAW_STATUS:
+ case CS35L36_INT2_RAW_STATUS:
+ case CS35L36_INT3_RAW_STATUS:
+ case CS35L36_INT4_RAW_STATUS:
+ case CS35L36_INT1_MASK:
+ case CS35L36_INT2_MASK:
+ case CS35L36_INT3_MASK:
+ case CS35L36_INT4_MASK:
+ case CS35L36_INT1_EDGE_LVL_CTRL:
+ case CS35L36_INT3_EDGE_LVL_CTRL:
+ case CS35L36_PAC_INT_STATUS:
+ case CS35L36_PAC_INT_RAW_STATUS:
+ case CS35L36_PAC_INT_FLUSH_CTRL:
+ return true;
+ default:
+ if (reg >= CS35L36_PAC_PMEM_WORD0 &&
+ reg <= CS35L36_PAC_PMEM_WORD1023)
+ return true;
+ else
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_RANGE(dig_vol_tlv, 0, 912,
+ TLV_DB_MINMAX_ITEM(-10200, 1200));
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+
+static const char * const cs35l36_pcm_sftramp_text[] = {
+ "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"};
+
+static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L36_AMP_DIG_VOL_CTRL, 0,
+ cs35l36_pcm_sftramp_text);
+
+static int cs35l36_ldm_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs35l36->ldm_mode_sel;
+
+ return 0;
+}
+
+static int cs35l36_ldm_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ int val = (ucontrol->value.integer.value[0]) ? CS35L36_NG_AMP_EN_MASK :
+ 0;
+
+ cs35l36->ldm_mode_sel = val;
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_AMP_EN_MASK, val);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs35l36_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L36_AMP_DIG_VOL_CTRL,
+ 3, 0x4D0, 0x390, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L36_AMP_GAIN_CTRL, 5, 0x13, 0,
+ amp_gain_tlv),
+ SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp),
+ SOC_SINGLE("Amp Gain Zero-Cross Switch", CS35L36_AMP_GAIN_CTRL,
+ CS35L36_AMP_ZC_SHIFT, 1, 0),
+ SOC_SINGLE("PDM LDM Enter Ramp Switch", CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_LDM_ENTER_SHIFT, 1, 0),
+ SOC_SINGLE("PDM LDM Exit Ramp Switch", CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_LDM_EXIT_SHIFT, 1, 0),
+ SOC_SINGLE_BOOL_EXT("LDM Select Switch", 0, cs35l36_ldm_sel_get,
+ cs35l36_ldm_sel_put),
+};
+
+static int cs35l36_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ u32 reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1,
+ CS35L36_GLOBAL_EN_MASK,
+ 1 << CS35L36_GLOBAL_EN_SHIFT);
+
+ usleep_range(2000, 2100);
+
+ regmap_read(cs35l36->regmap, CS35L36_INT4_RAW_STATUS, &reg);
+
+ if (WARN_ON_ONCE(reg & CS35L36_PLL_UNLOCK_MASK))
+ dev_crit(cs35l36->dev, "PLL Unlocked\n");
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL,
+ CS35L36_PCM_RX_SEL_MASK,
+ CS35L36_PCM_RX_SEL_PCM);
+ regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE,
+ CS35L36_AMP_MUTE_MASK,
+ 0 << CS35L36_AMP_MUTE_SHIFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL,
+ CS35L36_PCM_RX_SEL_MASK,
+ CS35L36_PCM_RX_SEL_ZERO);
+ regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE,
+ CS35L36_AMP_MUTE_MASK,
+ 1 << CS35L36_AMP_MUTE_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1,
+ CS35L36_GLOBAL_EN_MASK,
+ 0 << CS35L36_GLOBAL_EN_SHIFT);
+
+ usleep_range(2000, 2100);
+ break;
+ default:
+ dev_dbg(component->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l36_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!cs35l36->pdata.extern_boost)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2,
+ CS35L36_BST_EN_MASK,
+ CS35L36_BST_EN <<
+ CS35L36_BST_EN_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!cs35l36->pdata.extern_boost)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2,
+ CS35L36_BST_EN_MASK,
+ CS35L36_BST_DIS_VP <<
+ CS35L36_BST_EN_SHIFT);
+ break;
+ default:
+ dev_dbg(component->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char * const cs35l36_chan_text[] = {
+ "RX1",
+ "RX2",
+};
+
+static SOC_ENUM_SINGLE_DECL(chansel_enum, CS35L36_ASP_RX1_SLOT, 0,
+ cs35l36_chan_text);
+
+static const struct snd_kcontrol_new cs35l36_chan_mux =
+ SOC_DAPM_ENUM("Input Mux", chansel_enum);
+
+static const struct snd_kcontrol_new amp_enable_ctrl =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", CS35L36_AMP_OUT_MUTE,
+ CS35L36_AMP_MUTE_SHIFT, 1, 1);
+
+static const struct snd_kcontrol_new boost_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const char * const asp_tx_src_text[] = {
+ "Zero Fill", "ASPRX1", "VMON", "IMON", "ERRVOL", "VPMON", "VBSTMON"
+};
+
+static const unsigned int asp_tx_src_values[] = {
+ 0x00, 0x08, 0x18, 0x19, 0x20, 0x28, 0x29
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx1_src_enum, CS35L36_ASP_TX1_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx1_src =
+ SOC_DAPM_ENUM("ASPTX1SRC", asp_tx1_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx2_src_enum, CS35L36_ASP_TX2_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx2_src =
+ SOC_DAPM_ENUM("ASPTX2SRC", asp_tx2_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx3_src_enum, CS35L36_ASP_TX3_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx3_src =
+ SOC_DAPM_ENUM("ASPTX3SRC", asp_tx3_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx4_src_enum, CS35L36_ASP_TX4_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx4_src =
+ SOC_DAPM_ENUM("ASPTX4SRC", asp_tx4_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx5_src_enum, CS35L36_ASP_TX5_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx5_src =
+ SOC_DAPM_ENUM("ASPTX5SRC", asp_tx5_src_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx6_src_enum, CS35L36_ASP_TX6_SEL, 0,
+ CS35L36_APS_TX_SEL_MASK, asp_tx_src_text,
+ asp_tx_src_values);
+
+static const struct snd_kcontrol_new asp_tx6_src =
+ SOC_DAPM_ENUM("ASPTX6SRC", asp_tx6_src_enum);
+
+static const struct snd_soc_dapm_widget cs35l36_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Channel Mux", SND_SOC_NOPM, 0, 0, &cs35l36_chan_mux),
+ SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS35L36_ASP_RX_TX_EN, 16, 0),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L36_PWR_CTRL2, 0, 0, NULL, 0,
+ cs35l36_main_amp_event, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &amp_enable_ctrl),
+ SND_SOC_DAPM_MIXER("CLASS H", CS35L36_PWR_CTRL3, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH_E("BOOST Enable", SND_SOC_NOPM, 0, 0, &boost_ctrl,
+ cs35l36_boost_event, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L36_ASP_RX_TX_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, CS35L36_ASP_RX_TX_EN, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, CS35L36_ASP_RX_TX_EN, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, CS35L36_ASP_RX_TX_EN, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, CS35L36_ASP_RX_TX_EN, 4, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, CS35L36_ASP_RX_TX_EN, 5, 0),
+
+ SND_SOC_DAPM_MUX("ASPTX1SRC", SND_SOC_NOPM, 0, 0, &asp_tx1_src),
+ SND_SOC_DAPM_MUX("ASPTX2SRC", SND_SOC_NOPM, 0, 0, &asp_tx2_src),
+ SND_SOC_DAPM_MUX("ASPTX3SRC", SND_SOC_NOPM, 0, 0, &asp_tx3_src),
+ SND_SOC_DAPM_MUX("ASPTX4SRC", SND_SOC_NOPM, 0, 0, &asp_tx4_src),
+ SND_SOC_DAPM_MUX("ASPTX5SRC", SND_SOC_NOPM, 0, 0, &asp_tx5_src),
+ SND_SOC_DAPM_MUX("ASPTX6SRC", SND_SOC_NOPM, 0, 0, &asp_tx6_src),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L36_PWR_CTRL2, 12, 0),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L36_PWR_CTRL2, 13, 0),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L36_PWR_CTRL2, 8, 0),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L36_PWR_CTRL2, 9, 0),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+};
+
+static const struct snd_soc_dapm_route cs35l36_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "VSENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+
+ {"ASPTX1SRC", "IMON", "IMON ADC"},
+ {"ASPTX1SRC", "VMON", "VMON ADC"},
+ {"ASPTX1SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX1SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX2SRC", "IMON", "IMON ADC"},
+ {"ASPTX2SRC", "VMON", "VMON ADC"},
+ {"ASPTX2SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX2SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX3SRC", "IMON", "IMON ADC"},
+ {"ASPTX3SRC", "VMON", "VMON ADC"},
+ {"ASPTX3SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX3SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX4SRC", "IMON", "IMON ADC"},
+ {"ASPTX4SRC", "VMON", "VMON ADC"},
+ {"ASPTX4SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX4SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX5SRC", "IMON", "IMON ADC"},
+ {"ASPTX5SRC", "VMON", "VMON ADC"},
+ {"ASPTX5SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX5SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX6SRC", "IMON", "IMON ADC"},
+ {"ASPTX6SRC", "VMON", "VMON ADC"},
+ {"ASPTX6SRC", "VBSTMON", "VBSTMON ADC"},
+ {"ASPTX6SRC", "VPMON", "VPMON ADC"},
+
+ {"ASPTX1", NULL, "ASPTX1SRC"},
+ {"ASPTX2", NULL, "ASPTX2SRC"},
+ {"ASPTX3", NULL, "ASPTX3SRC"},
+ {"ASPTX4", NULL, "ASPTX4SRC"},
+ {"ASPTX5", NULL, "ASPTX5SRC"},
+ {"ASPTX6", NULL, "ASPTX6SRC"},
+
+ {"AMP Capture", NULL, "ASPTX1"},
+ {"AMP Capture", NULL, "ASPTX2"},
+ {"AMP Capture", NULL, "ASPTX3"},
+ {"AMP Capture", NULL, "ASPTX4"},
+ {"AMP Capture", NULL, "ASPTX5"},
+ {"AMP Capture", NULL, "ASPTX6"},
+
+ {"AMP Enable", "Switch", "AMP Playback"},
+ {"SDIN", NULL, "AMP Enable"},
+ {"Channel Mux", "RX1", "SDIN"},
+ {"Channel Mux", "RX2", "SDIN"},
+ {"BOOST Enable", "Switch", "Channel Mux"},
+ {"CLASS H", NULL, "BOOST Enable"},
+ {"Main AMP", NULL, "Channel Mux"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai,
+ unsigned int fmt)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component_dai->component);
+ unsigned int asp_fmt, lrclk_fmt, sclk_fmt, clock_provider, clk_frc;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ clock_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ clock_provider = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_SCLK_MSTR_MASK,
+ clock_provider << CS35L36_SCLK_MSTR_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
+ CS35L36_LRCLK_MSTR_MASK,
+ clock_provider << CS35L36_LRCLK_MSTR_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT:
+ clk_frc = 1;
+ break;
+ case SND_SOC_DAIFMT_GATED:
+ clk_frc = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_SCLK_FRC_MASK, clk_frc <<
+ CS35L36_SCLK_FRC_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
+ CS35L36_LRCLK_FRC_MASK, clk_frc <<
+ CS35L36_LRCLK_FRC_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_fmt = 1;
+ sclk_fmt = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ lrclk_fmt = 0;
+ sclk_fmt = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ lrclk_fmt = 1;
+ sclk_fmt = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ lrclk_fmt = 0;
+ sclk_fmt = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
+ CS35L36_LRCLK_INV_MASK,
+ lrclk_fmt << CS35L36_LRCLK_INV_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_SCLK_INV_MASK,
+ sclk_fmt << CS35L36_SCLK_INV_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FORMAT,
+ CS35L36_ASP_FMT_MASK, asp_fmt);
+
+ return 0;
+}
+
+struct cs35l36_global_fs_config {
+ int rate;
+ int fs_cfg;
+};
+
+static const struct cs35l36_global_fs_config cs35l36_fs_rates[] = {
+ {12000, 0x01},
+ {24000, 0x02},
+ {48000, 0x03},
+ {96000, 0x04},
+ {192000, 0x05},
+ {384000, 0x06},
+ {11025, 0x09},
+ {22050, 0x0A},
+ {44100, 0x0B},
+ {88200, 0x0C},
+ {176400, 0x0D},
+ {8000, 0x11},
+ {16000, 0x12},
+ {32000, 0x13},
+};
+
+static int cs35l36_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_width, global_fs = params_rate(params);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l36_fs_rates); i++) {
+ if (global_fs == cs35l36_fs_rates[i].rate)
+ regmap_update_bits(cs35l36->regmap,
+ CS35L36_GLOBAL_CLK_CTRL,
+ CS35L36_GLOBAL_FS_MASK,
+ cs35l36_fs_rates[i].fs_cfg <<
+ CS35L36_GLOBAL_FS_SHIFT);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ asp_width = CS35L36_ASP_WIDTH_16;
+ break;
+ case 24:
+ asp_width = CS35L36_ASP_WIDTH_24;
+ break;
+ case 32:
+ asp_width = CS35L36_ASP_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL,
+ CS35L36_ASP_RX_WIDTH_MASK,
+ asp_width << CS35L36_ASP_RX_WIDTH_SHIFT);
+ } else {
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL,
+ CS35L36_ASP_TX_WIDTH_MASK,
+ asp_width << CS35L36_ASP_TX_WIDTH_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ int fs1, fs2;
+
+ if (freq > CS35L36_FS_NOM_6MHZ) {
+ fs1 = CS35L36_FS1_DEFAULT_VAL;
+ fs2 = CS35L36_FS2_DEFAULT_VAL;
+ } else {
+ fs1 = 3 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4;
+ fs2 = 5 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4;
+ }
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_TST_FS_MON0,
+ CS35L36_FS1_WINDOW_MASK | CS35L36_FS2_WINDOW_MASK,
+ fs1 | (fs2 << CS35L36_FS2_WINDOW_SHIFT));
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ return 0;
+}
+
+static const struct cs35l36_pll_config *cs35l36_get_clk_config(
+ struct cs35l36_private *cs35l36, int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l36_pll_sysclk); i++) {
+ if (cs35l36_pll_sysclk[i].freq == freq)
+ return &cs35l36_pll_sysclk[i];
+ }
+
+ return NULL;
+}
+
+static const struct snd_soc_dai_ops cs35l36_ops = {
+ .set_fmt = cs35l36_set_dai_fmt,
+ .hw_params = cs35l36_pcm_hw_params,
+ .set_sysclk = cs35l36_dai_set_sysclk,
+};
+
+#define CS35L36_RATES ( \
+ SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_384000)
+
+static struct snd_soc_dai_driver cs35l36_dai[] = {
+ {
+ .name = "cs35l36-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L36_RATES,
+ .formats = CS35L36_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L36_RATES,
+ .formats = CS35L36_TX_FORMATS,
+ },
+ .ops = &cs35l36_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int cs35l36_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ const struct cs35l36_pll_config *clk_cfg;
+ int prev_clksrc;
+ bool pdm_switch;
+
+ prev_clksrc = cs35l36->clksrc;
+
+ switch (clk_id) {
+ case 0:
+ cs35l36->clksrc = CS35L36_PLLSRC_SCLK;
+ break;
+ case 1:
+ cs35l36->clksrc = CS35L36_PLLSRC_LRCLK;
+ break;
+ case 2:
+ cs35l36->clksrc = CS35L36_PLLSRC_PDMCLK;
+ break;
+ case 3:
+ cs35l36->clksrc = CS35L36_PLLSRC_SELF;
+ break;
+ case 4:
+ cs35l36->clksrc = CS35L36_PLLSRC_MCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ clk_cfg = cs35l36_get_clk_config(cs35l36, freq);
+ if (clk_cfg == NULL) {
+ dev_err(component->dev, "Invalid CLK Config Freq: %d\n", freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_OPENLOOP_MASK,
+ 1 << CS35L36_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_REFCLK_FREQ_MASK,
+ clk_cfg->clk_cfg << CS35L36_REFCLK_FREQ_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_REFCLK_EN_MASK,
+ 0 << CS35L36_PLL_REFCLK_EN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_CLK_SEL_MASK,
+ cs35l36->clksrc);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_OPENLOOP_MASK,
+ 0 << CS35L36_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL,
+ CS35L36_PLL_REFCLK_EN_MASK,
+ 1 << CS35L36_PLL_REFCLK_EN_SHIFT);
+
+ if (cs35l36->rev_id == CS35L36_REV_A0) {
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_write(cs35l36->regmap, CS35L36_DCO_CTRL, 0x00036DA8);
+ regmap_write(cs35l36->regmap, CS35L36_MISC_CTRL, 0x0100EE0E);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS,
+ CS35L36_PLL_IGAIN_MASK,
+ CS35L36_PLL_IGAIN <<
+ CS35L36_PLL_IGAIN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS,
+ CS35L36_PLL_FFL_IGAIN_MASK,
+ clk_cfg->fll_igain);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ }
+
+ if (cs35l36->clksrc == CS35L36_PLLSRC_PDMCLK) {
+ pdm_switch = cs35l36->ldm_mode_sel &&
+ (prev_clksrc != CS35L36_PLLSRC_PDMCLK);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 0 << CS35L36_NG_DELAY_SHIFT);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_MODE_MASK,
+ 1 << CS35L36_PDM_MODE_SHIFT);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 3 << CS35L36_NG_DELAY_SHIFT);
+ } else {
+ pdm_switch = cs35l36->ldm_mode_sel &&
+ (prev_clksrc == CS35L36_PLLSRC_PDMCLK);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 0 << CS35L36_NG_DELAY_SHIFT);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG,
+ CS35L36_PDM_MODE_MASK,
+ 0 << CS35L36_PDM_MODE_SHIFT);
+
+ if (pdm_switch)
+ regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG,
+ CS35L36_NG_DELAY_MASK,
+ 3 << CS35L36_NG_DELAY_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l36_boost_inductor(struct cs35l36_private *cs35l36, int inductor)
+{
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF,
+ CS35L36_BSTCVRT_K1_MASK, 0x3C);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF,
+ CS35L36_BSTCVRT_K2_MASK,
+ 0x3C << CS35L36_BSTCVRT_K2_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SW_FREQ,
+ CS35L36_BSTCVRT_CCMFREQ_MASK, 0x00);
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_SLOPE_MASK,
+ 0x75 << CS35L36_BSTCVRT_SLOPE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_LBSTVAL_MASK, 0x00);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_SLOPE_MASK,
+ 0x6B << CS35L36_BSTCVRT_SLOPE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST,
+ CS35L36_BSTCVRT_LBSTVAL_MASK, 0x01);
+ break;
+ default:
+ dev_err(cs35l36->dev, "%s Invalid Inductor Value %d uH\n",
+ __func__, inductor);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l36_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) {
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL,
+ CS35L36_DCM_AUTO_MASK,
+ CS35L36_DCM_AUTO_MASK);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL,
+ CS35L36_BST_MAN_IPKCOMP_MASK,
+ 0 << CS35L36_BST_MAN_IPKCOMP_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL,
+ CS35L36_BST_MAN_IPKCOMP_EN_MASK,
+ CS35L36_BST_MAN_IPKCOMP_EN_MASK);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ }
+
+ if (cs35l36->pdata.amp_pcm_inv)
+ regmap_update_bits(cs35l36->regmap, CS35L36_AMP_DIG_VOL_CTRL,
+ CS35L36_AMP_PCM_INV_MASK,
+ CS35L36_AMP_PCM_INV_MASK);
+
+ if (cs35l36->pdata.multi_amp_mode)
+ regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
+ CS35L36_ASP_TX_HIZ_MASK,
+ CS35L36_ASP_TX_HIZ_MASK);
+
+ if (cs35l36->pdata.imon_pol_inv)
+ regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT,
+ CS35L36_IMON_POL_MASK, 0);
+
+ if (cs35l36->pdata.vmon_pol_inv)
+ regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT,
+ CS35L36_VMON_POL_MASK, 0);
+
+ if (cs35l36->pdata.bst_vctl)
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1,
+ CS35L35_BSTCVRT_CTL_MASK,
+ cs35l36->pdata.bst_vctl);
+
+ if (cs35l36->pdata.bst_vctl_sel)
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2,
+ CS35L35_BSTCVRT_CTL_SEL_MASK,
+ cs35l36->pdata.bst_vctl_sel);
+
+ if (cs35l36->pdata.bst_ipk)
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_PEAK_CUR,
+ CS35L36_BST_IPK_MASK,
+ cs35l36->pdata.bst_ipk);
+
+ if (cs35l36->pdata.boost_ind) {
+ ret = cs35l36_boost_inductor(cs35l36, cs35l36->pdata.boost_ind);
+ if (ret < 0) {
+ dev_err(cs35l36->dev,
+ "Boost inductor config failed(%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (cs35l36->pdata.temp_warn_thld)
+ regmap_update_bits(cs35l36->regmap, CS35L36_DTEMP_WARN_THLD,
+ CS35L36_TEMP_THLD_MASK,
+ cs35l36->pdata.temp_warn_thld);
+
+ if (cs35l36->pdata.irq_drv_sel)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_DRV_SEL_MASK,
+ cs35l36->pdata.irq_drv_sel <<
+ CS35L36_INT_DRV_SEL_SHIFT);
+
+ if (cs35l36->pdata.irq_gpio_sel)
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_GPIO_SEL_MASK,
+ cs35l36->pdata.irq_gpio_sel <<
+ CS35L36_INT_GPIO_SEL_SHIFT);
+
+ /*
+ * Rev B0 has 2 versions
+ * L36 is 10V
+ * L37 is 12V
+ * If L36 we need to clamp some values for safety
+ * after probe has setup dt values. We want to make
+ * sure we dont miss any values set in probe
+ */
+ if (cs35l36->chip_version == CS35L36_10V_L36) {
+ regmap_update_bits(cs35l36->regmap,
+ CS35L36_BSTCVRT_OVERVOLT_CTRL,
+ CS35L36_BST_OVP_THLD_MASK,
+ CS35L36_BST_OVP_THLD_11V);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_BST_ANA2_TEST,
+ CS35L36_BST_OVP_TRIM_MASK,
+ CS35L36_BST_OVP_TRIM_11V <<
+ CS35L36_BST_OVP_TRIM_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2,
+ CS35L36_BST_CTRL_LIM_MASK,
+ 1 << CS35L36_BST_CTRL_LIM_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1,
+ CS35L35_BSTCVRT_CTL_MASK,
+ CS35L36_BST_CTRL_10V_CLAMP);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+ }
+
+ /*
+ * RevA and B require the disabling of
+ * SYNC_GLOBAL_OVR when GLOBAL_EN = 0.
+ * Just turn it off from default
+ */
+ regmap_update_bits(cs35l36->regmap, CS35L36_CTRL_OVRRIDE,
+ CS35L36_SYNC_GLOBAL_OVR_MASK,
+ 0 << CS35L36_SYNC_GLOBAL_OVR_SHIFT);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l36 = {
+ .probe = &cs35l36_component_probe,
+ .set_sysclk = cs35l36_component_set_sysclk,
+ .dapm_widgets = cs35l36_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l36_dapm_widgets),
+ .dapm_routes = cs35l36_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l36_audio_map),
+ .controls = cs35l36_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l36_aud_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs35l36_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = CS35L36_PAC_PMEM_WORD1023,
+ .reg_defaults = cs35l36_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l36_reg),
+ .precious_reg = cs35l36_precious_reg,
+ .volatile_reg = cs35l36_volatile_reg,
+ .readable_reg = cs35l36_readable_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static irqreturn_t cs35l36_irq(int irq, void *data)
+{
+ struct cs35l36_private *cs35l36 = data;
+ unsigned int status[4];
+ unsigned int masks[4];
+ int ret = IRQ_NONE;
+
+ /* ack the irq by reading all status registers */
+ regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_STATUS, status,
+ ARRAY_SIZE(status));
+
+ regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_MASK, masks,
+ ARRAY_SIZE(masks));
+
+ /* Check to see if unmasked bits are active */
+ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
+ !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) {
+ return IRQ_NONE;
+ }
+
+ /*
+ * The following interrupts require a
+ * protection release cycle to get the
+ * speaker out of Safe-Mode.
+ */
+ if (status[2] & CS35L36_AMP_SHORT_ERR) {
+ dev_crit(cs35l36->dev, "Amp short error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_AMP_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_AMP_SHORT_ERR_RLS,
+ CS35L36_AMP_SHORT_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_AMP_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT3_STATUS,
+ CS35L36_AMP_SHORT_ERR,
+ CS35L36_AMP_SHORT_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_TEMP_WARN) {
+ dev_crit(cs35l36->dev, "Over temperature warning\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_WARN_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_WARN_ERR_RLS,
+ CS35L36_TEMP_WARN_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_WARN_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_TEMP_WARN, CS35L36_TEMP_WARN);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_TEMP_ERR) {
+ dev_crit(cs35l36->dev, "Over temperature error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_TEMP_ERR, CS35L36_TEMP_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_BST_OVP_ERR) {
+ dev_crit(cs35l36->dev, "VBST Over Voltage error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_TEMP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_BST_OVP_ERR, CS35L36_BST_OVP_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_BST_DCM_UVP_ERR) {
+ dev_crit(cs35l36->dev, "DCM VBST Under Voltage Error\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_UVP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_UVP_ERR_RLS,
+ CS35L36_BST_UVP_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_UVP_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_BST_DCM_UVP_ERR,
+ CS35L36_BST_DCM_UVP_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L36_BST_SHORT_ERR) {
+ dev_crit(cs35l36->dev, "LBST SHORT error!\n");
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_SHORT_ERR_RLS,
+ CS35L36_BST_SHORT_ERR_RLS);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR,
+ CS35L36_BST_SHORT_ERR_RLS, 0);
+ regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS,
+ CS35L36_BST_SHORT_ERR,
+ CS35L36_BST_SHORT_ERR);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int cs35l36_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l36_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config;
+ struct device_node *vpbr_node;
+ unsigned int val;
+ int ret;
+
+ if (!np)
+ return 0;
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val);
+ if (!ret) {
+ if (val < 2550 || val > 12000) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val);
+ return -EINVAL;
+ }
+ pdata->bst_vctl = (((val - 2550) / 100) + 1) << 1;
+ } else {
+ dev_err(&i2c_client->dev,
+ "Unable to find required parameter 'cirrus,boost-ctl-millivolt'");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-select", &val);
+ if (!ret)
+ pdata->bst_vctl_sel = val | CS35L36_VALID_PDATA;
+
+ ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val);
+ if (!ret) {
+ if (val < 1600 || val > 4500) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %u mA\n", val);
+ return -EINVAL;
+ }
+
+ pdata->bst_ipk = (val - 1600) / 50;
+ } else {
+ dev_err(&i2c_client->dev,
+ "Unable to find required parameter 'cirrus,boost-peak-milliamp'");
+ return -EINVAL;
+ }
+
+ pdata->multi_amp_mode = of_property_read_bool(np,
+ "cirrus,multi-amp-mode");
+
+ pdata->dcm_mode = of_property_read_bool(np,
+ "cirrus,dcm-mode-enable");
+
+ pdata->amp_pcm_inv = of_property_read_bool(np,
+ "cirrus,amp-pcm-inv");
+
+ pdata->imon_pol_inv = of_property_read_bool(np,
+ "cirrus,imon-pol-inv");
+
+ pdata->vmon_pol_inv = of_property_read_bool(np,
+ "cirrus,vmon-pol-inv");
+
+ if (of_property_read_u32(np, "cirrus,temp-warn-threshold", &val) >= 0)
+ pdata->temp_warn_thld = val | CS35L36_VALID_PDATA;
+
+ if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) {
+ pdata->boost_ind = val;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,irq-drive-select", &val) >= 0)
+ pdata->irq_drv_sel = val | CS35L36_VALID_PDATA;
+
+ if (of_property_read_u32(np, "cirrus,irq-gpio-select", &val) >= 0)
+ pdata->irq_gpio_sel = val | CS35L36_VALID_PDATA;
+
+ /* VPBR Config */
+ vpbr_node = of_get_child_by_name(np, "cirrus,vpbr-config");
+ vpbr_config->is_present = vpbr_node ? true : false;
+ if (vpbr_config->is_present) {
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-en",
+ &val) >= 0)
+ vpbr_config->vpbr_en = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-thld",
+ &val) >= 0)
+ vpbr_config->vpbr_thld = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-rate",
+ &val) >= 0)
+ vpbr_config->vpbr_atk_rate = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-vol",
+ &val) >= 0)
+ vpbr_config->vpbr_atk_vol = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-max-attn",
+ &val) >= 0)
+ vpbr_config->vpbr_max_attn = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-wait",
+ &val) >= 0)
+ vpbr_config->vpbr_wait = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-rel-rate",
+ &val) >= 0)
+ vpbr_config->vpbr_rel_rate = val;
+ if (of_property_read_u32(vpbr_node, "cirrus,vpbr-mute-en",
+ &val) >= 0)
+ vpbr_config->vpbr_mute_en = val;
+ }
+ of_node_put(vpbr_node);
+
+ return 0;
+}
+
+static int cs35l36_pac(struct cs35l36_private *cs35l36)
+{
+ int ret, count;
+ unsigned int val;
+
+ if (cs35l36->rev_id != CS35L36_REV_B0)
+ return 0;
+
+ /*
+ * Magic code for internal PAC
+ */
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_UNLOCK2);
+
+ usleep_range(9500, 10500);
+
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1,
+ CS35L36_PAC_RESET);
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3,
+ CS35L36_PAC_MEM_ACCESS);
+ regmap_write(cs35l36->regmap, CS35L36_PAC_PMEM_WORD0,
+ CS35L36_B0_PAC_PATCH);
+
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3,
+ CS35L36_PAC_MEM_ACCESS_CLR);
+ regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1,
+ CS35L36_PAC_ENABLE_MASK);
+
+ usleep_range(9500, 10500);
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, &val);
+ if (ret < 0) {
+ dev_err(cs35l36->dev, "Failed to read int4_status %d\n", ret);
+ return ret;
+ }
+
+ count = 0;
+ while (!(val & CS35L36_MCU_CONFIG_CLR)) {
+ usleep_range(100, 200);
+ count++;
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS,
+ &val);
+ if (ret < 0) {
+ dev_err(cs35l36->dev, "Failed to read int4_status %d\n",
+ ret);
+ return ret;
+ }
+
+ if (count >= 100)
+ return -EINVAL;
+ }
+
+ regmap_write(cs35l36->regmap, CS35L36_INT4_STATUS,
+ CS35L36_MCU_CONFIG_CLR);
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAC_CTL1,
+ CS35L36_PAC_ENABLE_MASK, 0);
+
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK1);
+ regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL,
+ CS35L36_TEST_LOCK2);
+
+ return 0;
+}
+
+static void cs35l36_apply_vpbr_config(struct cs35l36_private *cs35l36)
+{
+ struct cs35l36_platform_data *pdata = &cs35l36->pdata;
+ struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config;
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL3,
+ CS35L36_VPBR_EN_MASK,
+ vpbr_config->vpbr_en <<
+ CS35L36_VPBR_EN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_THLD_MASK,
+ vpbr_config->vpbr_thld <<
+ CS35L36_VPBR_THLD_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_MAX_ATTN_MASK,
+ vpbr_config->vpbr_max_attn <<
+ CS35L36_VPBR_MAX_ATTN_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_ATK_VOL_MASK,
+ vpbr_config->vpbr_atk_vol <<
+ CS35L36_VPBR_ATK_VOL_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_ATK_RATE_MASK,
+ vpbr_config->vpbr_atk_rate <<
+ CS35L36_VPBR_ATK_RATE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_WAIT_MASK,
+ vpbr_config->vpbr_wait <<
+ CS35L36_VPBR_WAIT_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_REL_RATE_MASK,
+ vpbr_config->vpbr_rel_rate <<
+ CS35L36_VPBR_REL_RATE_SHIFT);
+ regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG,
+ CS35L36_VPBR_MUTE_EN_MASK,
+ vpbr_config->vpbr_mute_en <<
+ CS35L36_VPBR_MUTE_EN_SHIFT);
+}
+
+static const struct reg_sequence cs35l36_reva0_errata_patch[] = {
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 },
+ /* Errata Writes */
+ { CS35L36_OTP_CTRL1, 0x00002060 },
+ { CS35L36_OTP_CTRL2, 0x00000001 },
+ { CS35L36_OTP_CTRL1, 0x00002460 },
+ { CS35L36_OTP_CTRL2, 0x00000001 },
+ { 0x00002088, 0x012A1838 },
+ { 0x00003014, 0x0100EE0E },
+ { 0x00003008, 0x0008184A },
+ { 0x00007418, 0x509001C8 },
+ { 0x00007064, 0x0929A800 },
+ { 0x00002D10, 0x0002C01C },
+ { 0x0000410C, 0x00000A11 },
+ { 0x00006E08, 0x8B19140C },
+ { 0x00006454, 0x0300000A },
+ { CS35L36_AMP_NG_CTRL, 0x000020EF },
+ { 0x00007E34, 0x0000000E },
+ { 0x0000410C, 0x00000A11 },
+ { 0x00007410, 0x20514B00 },
+ /* PAC Config */
+ { CS35L36_CTRL_OVRRIDE, 0x00000000 },
+ { CS35L36_PAC_INT0_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT1_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT2_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT3_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT4_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT5_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT6_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT7_CTRL, 0x00860001 },
+ { CS35L36_PAC_INT_FLUSH_CTRL, 0x000000FF },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 },
+};
+
+static const struct reg_sequence cs35l36_revb0_errata_patch[] = {
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 },
+ { 0x00007064, 0x0929A800 },
+ { 0x00007850, 0x00002FA9 },
+ { 0x00007854, 0x0003F1D5 },
+ { 0x00007858, 0x0003F5E3 },
+ { 0x0000785C, 0x00001137 },
+ { 0x00007860, 0x0001A7A5 },
+ { 0x00007864, 0x0002F16A },
+ { 0x00007868, 0x00003E21 },
+ { 0x00007848, 0x00000001 },
+ { 0x00003854, 0x05180240 },
+ { 0x00007418, 0x509001C8 },
+ { 0x0000394C, 0x028764BD },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 },
+ { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 },
+};
+
+static int cs35l36_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs35l36_private *cs35l36;
+ struct device *dev = &i2c_client->dev;
+ struct cs35l36_platform_data *pdata = dev_get_platdata(dev);
+ struct irq_data *irq_d;
+ int ret, irq_pol, chip_irq_pol, i;
+ u32 reg_id, reg_revid, l37_id_reg;
+
+ cs35l36 = devm_kzalloc(dev, sizeof(struct cs35l36_private), GFP_KERNEL);
+ if (!cs35l36)
+ return -ENOMEM;
+
+ cs35l36->dev = dev;
+
+ i2c_set_clientdata(i2c_client, cs35l36);
+ cs35l36->regmap = devm_regmap_init_i2c(i2c_client, &cs35l36_regmap);
+ if (IS_ERR(cs35l36->regmap)) {
+ ret = PTR_ERR(cs35l36->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs35l36->num_supplies = ARRAY_SIZE(cs35l36_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs35l36_supplies); i++)
+ cs35l36->supplies[i].supply = cs35l36_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, cs35l36->num_supplies,
+ cs35l36->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l36->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(dev, sizeof(struct cs35l36_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ ret = cs35l36_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+
+ cs35l36->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l36->num_supplies, cs35l36->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l36->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l36->reset_gpio)) {
+ ret = PTR_ERR(cs35l36->reset_gpio);
+ cs35l36->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err_disable_regs;
+ }
+ }
+
+ if (cs35l36->reset_gpio)
+ gpiod_set_value_cansleep(cs35l36->reset_gpio, 1);
+
+ usleep_range(2000, 2100);
+
+ /* initialize amplifier */
+ ret = regmap_read(cs35l36->regmap, CS35L36_SW_RESET, &reg_id);
+ if (ret < 0) {
+ dev_err(dev, "Get Device ID failed %d\n", ret);
+ goto err;
+ }
+
+ if (reg_id != CS35L36_CHIP_ID) {
+ dev_err(dev, "Device ID (%X). Expected ID %X\n", reg_id,
+ CS35L36_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_REV_ID, &reg_revid);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed %d\n", ret);
+ goto err;
+ }
+
+ cs35l36->rev_id = reg_revid >> 8;
+
+ ret = regmap_read(cs35l36->regmap, CS35L36_OTP_MEM30, &l37_id_reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n",
+ ret);
+ goto err;
+ }
+
+ if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37)
+ cs35l36->chip_version = CS35L36_12V_L37;
+ else
+ cs35l36->chip_version = CS35L36_10V_L36;
+
+ switch (cs35l36->rev_id) {
+ case CS35L36_REV_A0:
+ ret = regmap_register_patch(cs35l36->regmap,
+ cs35l36_reva0_errata_patch,
+ ARRAY_SIZE(cs35l36_reva0_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply A0 errata patch %d\n",
+ ret);
+ goto err;
+ }
+ break;
+ case CS35L36_REV_B0:
+ ret = cs35l36_pac(cs35l36);
+ if (ret < 0) {
+ dev_err(dev, "Failed to Trim OTP %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_register_patch(cs35l36->regmap,
+ cs35l36_revb0_errata_patch,
+ ARRAY_SIZE(cs35l36_revb0_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply B0 errata patch %d\n",
+ ret);
+ goto err;
+ }
+ break;
+ }
+
+ if (pdata->vpbr_config.is_present)
+ cs35l36_apply_vpbr_config(cs35l36);
+
+ irq_d = irq_get_irq_data(i2c_client->irq);
+ if (!irq_d) {
+ dev_err(&i2c_client->dev, "Invalid IRQ: %d\n", i2c_client->irq);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ irq_pol = irqd_get_trigger_type(irq_d);
+
+ switch (irq_pol) {
+ case IRQF_TRIGGER_FALLING:
+ case IRQF_TRIGGER_LOW:
+ chip_irq_pol = 0;
+ break;
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_HIGH:
+ chip_irq_pol = 1;
+ break;
+ default:
+ dev_err(cs35l36->dev, "Invalid IRQ polarity: %d\n", irq_pol);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_POL_SEL_MASK,
+ chip_irq_pol << CS35L36_INT_POL_SEL_SHIFT);
+
+ ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l36_irq,
+ IRQF_ONESHOT | irq_pol, "cs35l36",
+ cs35l36);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+
+ regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE,
+ CS35L36_INT_OUTPUT_EN_MASK, 1);
+
+ /* Set interrupt masks for critical errors */
+ regmap_write(cs35l36->regmap, CS35L36_INT1_MASK,
+ CS35L36_INT1_MASK_DEFAULT);
+ regmap_write(cs35l36->regmap, CS35L36_INT3_MASK,
+ CS35L36_INT3_MASK_DEFAULT);
+
+ dev_info(&i2c_client->dev, "Cirrus Logic CS35L%d, Revision: %02X\n",
+ cs35l36->chip_version, reg_revid >> 8);
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l36,
+ cs35l36_dai,
+ ARRAY_SIZE(cs35l36_dai));
+ if (ret < 0) {
+ dev_err(dev, "%s: Register component failed %d\n", __func__,
+ ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs35l36->reset_gpio, 0);
+
+err_disable_regs:
+ regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies);
+ return ret;
+}
+
+static void cs35l36_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l36_private *cs35l36 = i2c_get_clientdata(client);
+
+ /* Reset interrupt masks for device removal */
+ regmap_write(cs35l36->regmap, CS35L36_INT1_MASK,
+ CS35L36_INT1_MASK_RESET);
+ regmap_write(cs35l36->regmap, CS35L36_INT3_MASK,
+ CS35L36_INT3_MASK_RESET);
+
+ if (cs35l36->reset_gpio)
+ gpiod_set_value_cansleep(cs35l36->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies);
+}
+static const struct of_device_id cs35l36_of_match[] = {
+ {.compatible = "cirrus,cs35l36"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l36_of_match);
+
+static const struct i2c_device_id cs35l36_id[] = {
+ {"cs35l36"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l36_id);
+
+static struct i2c_driver cs35l36_i2c_driver = {
+ .driver = {
+ .name = "cs35l36",
+ .of_match_table = cs35l36_of_match,
+ },
+ .id_table = cs35l36_id,
+ .probe = cs35l36_i2c_probe,
+ .remove = cs35l36_i2c_remove,
+};
+module_i2c_driver(cs35l36_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L36 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h
new file mode 100644
index 000000000000..f6e38c633b93
--- /dev/null
+++ b/sound/soc/codecs/cs35l36.h
@@ -0,0 +1,446 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cs35l36.h -- CS35L36 ALSA SoC audio driver
+ *
+ * Copyright 2018 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef __CS35L36_H__
+#define __CS35L36_H__
+
+#include <linux/regmap.h>
+
+#define CS35L36_FIRSTREG 0x00000000
+#define CS35L36_LASTREG 0x00E037FC
+#define CS35L36_SW_RESET 0x00000000
+#define CS35L36_SW_REV 0x00000004
+#define CS35L36_HW_REV 0x00000008
+#define CS35L36_TESTKEY_CTRL 0x00000020
+#define CS35L36_USERKEY_CTL 0x00000024
+#define CS35L36_OTP_MEM30 0x00000478
+#define CS35L36_OTP_CTRL1 0x00000500
+#define CS35L36_OTP_CTRL2 0x00000504
+#define CS35L36_OTP_CTRL3 0x00000508
+#define CS35L36_OTP_CTRL4 0x0000050C
+#define CS35L36_OTP_CTRL5 0x00000510
+#define CS35L36_PAC_CTL1 0x00000C00
+#define CS35L36_PAC_CTL2 0x00000C04
+#define CS35L36_PAC_CTL3 0x00000C08
+#define CS35L36_DEVICE_ID 0x00002004
+#define CS35L36_FAB_ID 0x00002008
+#define CS35L36_REV_ID 0x0000200C
+#define CS35L36_PWR_CTRL1 0x00002014
+#define CS35L36_PWR_CTRL2 0x00002018
+#define CS35L36_PWR_CTRL3 0x0000201C
+#define CS35L36_CTRL_OVRRIDE 0x00002020
+#define CS35L36_AMP_OUT_MUTE 0x00002024
+#define CS35L36_OTP_TRIM_STATUS 0x00002028
+#define CS35L36_DISCH_FILT 0x0000202C
+#define CS35L36_OSC_TRIM 0x00002030
+#define CS35L36_PROTECT_REL_ERR 0x00002034
+#define CS35L36_PAD_INTERFACE 0x00002400
+#define CS35L36_PLL_CLK_CTRL 0x00002C04
+#define CS35L36_GLOBAL_CLK_CTRL 0x00002C0C
+#define CS35L36_ADC_CLK_CTRL 0x00002C10
+#define CS35L36_SWIRE_CLK_CTRL 0x00002C14
+#define CS35L36_SP_SCLK_CLK_CTRL 0x00002D00
+#define CS35L36_TST_FS_MON0 0x00002D10
+#define CS35L36_PLL_LOOP_PARAMS 0x00003008
+#define CS35L36_DCO_CTRL 0x00003010
+#define CS35L36_MISC_CTRL 0x00003014
+#define CS35L36_MDSYNC_EN 0x00003404
+#define CS35L36_MDSYNC_TX_ID 0x00003408
+#define CS35L36_MDSYNC_PWR_CTRL 0x0000340C
+#define CS35L36_MDSYNC_DATA_TX 0x00003410
+#define CS35L36_MDSYNC_TX_STATUS 0x0000341C
+#define CS35L36_MDSYNC_RX_STATUS 0x00003420
+#define CS35L36_MDSYNC_ERR_STATUS 0x00003424
+#define CS35L36_BSTCVRT_VCTRL1 0x00003800
+#define CS35L36_BSTCVRT_VCTRL2 0x00003804
+#define CS35L36_BSTCVRT_PEAK_CUR 0x00003808
+#define CS35L36_BSTCVRT_SFT_RAMP 0x0000380C
+#define CS35L36_BSTCVRT_COEFF 0x00003810
+#define CS35L36_BSTCVRT_SLOPE_LBST 0x00003814
+#define CS35L36_BSTCVRT_SW_FREQ 0x00003818
+#define CS35L36_BSTCVRT_DCM_CTRL 0x0000381C
+#define CS35L36_BSTCVRT_DCM_MODE_FORCE 0x00003820
+#define CS35L36_BSTCVRT_OVERVOLT_CTRL 0x00003830
+#define CS35L36_BST_TST_MANUAL 0x0000393C
+#define CS35L36_BST_ANA2_TEST 0x0000394C
+#define CS35L36_VPI_LIMIT_MODE 0x00003C04
+#define CS35L36_VPI_LIMIT_MINMAX 0x00003C08
+#define CS35L36_VPI_VP_THLD 0x00003C0C
+#define CS35L36_VPI_TRACK_CTRL 0x00003C10
+#define CS35L36_VPI_TRIG_MODE_CTRL 0x00003C14
+#define CS35L36_VPI_TRIG_STEPS 0x00003C18
+#define CS35L36_VI_SPKMON_FILT 0x00004004
+#define CS35L36_VI_SPKMON_GAIN 0x00004008
+#define CS35L36_VI_SPKMON_IP_SEL 0x00004100
+#define CS35L36_DTEMP_WARN_THLD 0x00004220
+#define CS35L36_DTEMP_STATUS 0x00004300
+#define CS35L36_VPVBST_FS_SEL 0x00004400
+#define CS35L36_VPVBST_VP_CTRL 0x00004440
+#define CS35L36_VPVBST_VBST_CTRL 0x00004444
+#define CS35L36_ASP_TX_PIN_CTRL 0x00004800
+#define CS35L36_ASP_RATE_CTRL 0x00004804
+#define CS35L36_ASP_FORMAT 0x00004808
+#define CS35L36_ASP_FRAME_CTRL 0x00004818
+#define CS35L36_ASP_TX1_TX2_SLOT 0x0000481C
+#define CS35L36_ASP_TX3_TX4_SLOT 0x00004820
+#define CS35L36_ASP_TX5_TX6_SLOT 0x00004824
+#define CS35L36_ASP_TX7_TX8_SLOT 0x00004828
+#define CS35L36_ASP_RX1_SLOT 0x0000482C
+#define CS35L36_ASP_RX_TX_EN 0x0000483C
+#define CS35L36_ASP_RX1_SEL 0x00004C00
+#define CS35L36_ASP_TX1_SEL 0x00004C20
+#define CS35L36_ASP_TX2_SEL 0x00004C24
+#define CS35L36_ASP_TX3_SEL 0x00004C28
+#define CS35L36_ASP_TX4_SEL 0x00004C2C
+#define CS35L36_ASP_TX5_SEL 0x00004C30
+#define CS35L36_ASP_TX6_SEL 0x00004C34
+#define CS35L36_SWIRE_P1_TX1_SEL 0x00004C40
+#define CS35L36_SWIRE_P1_TX2_SEL 0x00004C44
+#define CS35L36_SWIRE_P2_TX1_SEL 0x00004C60
+#define CS35L36_SWIRE_P2_TX2_SEL 0x00004C64
+#define CS35L36_SWIRE_P2_TX3_SEL 0x00004C68
+#define CS35L36_SWIRE_DP1_FIFO_CFG 0x00005000
+#define CS35L36_SWIRE_DP2_FIFO_CFG 0x00005004
+#define CS35L36_SWIRE_DP3_FIFO_CFG 0x00005008
+#define CS35L36_SWIRE_PCM_RX_DATA 0x0000500C
+#define CS35L36_SWIRE_FS_SEL 0x00005010
+#define CS35L36_SPARE_CP_BITS 0x00005C00
+#define CS35L36_AMP_DIG_VOL_CTRL 0x00006000
+#define CS35L36_VPBR_CFG 0x00006404
+#define CS35L36_VBBR_CFG 0x00006408
+#define CS35L36_VPBR_STATUS 0x0000640C
+#define CS35L36_VBBR_STATUS 0x00006410
+#define CS35L36_OVERTEMP_CFG 0x00006414
+#define CS35L36_AMP_ERR_VOL 0x00006418
+#define CS35L36_CLASSH_CFG 0x00006800
+#define CS35L36_CLASSH_FET_DRV_CFG 0x00006804
+#define CS35L36_NG_CFG 0x00006808
+#define CS35L36_AMP_GAIN_CTRL 0x00006C04
+#define CS35L36_PWM_MOD_IO_CTRL 0x0000706C
+#define CS35L36_PWM_MOD_STATUS 0x00007070
+#define CS35L36_DAC_MSM_CFG 0x00007400
+#define CS35L36_AMP_SLOPE_CTRL 0x00007410
+#define CS35L36_AMP_PDM_VOLUME 0x00007E04
+#define CS35L36_AMP_PDM_RATE_CTRL 0x00007E08
+#define CS35L36_PDM_CH_SEL 0x00007E10
+#define CS35L36_AMP_NG_CTRL 0x00007E14
+#define CS35L36_PDM_HIGHFILT_CTRL 0x00007E3C
+#define CS35L36_INT1_STATUS 0x00D00000
+#define CS35L36_INT2_STATUS 0x00D00004
+#define CS35L36_INT3_STATUS 0x00D00008
+#define CS35L36_INT4_STATUS 0x00D0000C
+#define CS35L36_INT1_RAW_STATUS 0x00D00020
+#define CS35L36_INT2_RAW_STATUS 0x00D00024
+#define CS35L36_INT3_RAW_STATUS 0x00D00028
+#define CS35L36_INT4_RAW_STATUS 0x00D0002C
+#define CS35L36_INT1_MASK 0x00D00040
+#define CS35L36_INT2_MASK 0x00D00044
+#define CS35L36_INT3_MASK 0x00D00048
+#define CS35L36_INT4_MASK 0x00D0004C
+#define CS35L36_INT1_EDGE_LVL_CTRL 0x00D00060
+#define CS35L36_INT3_EDGE_LVL_CTRL 0x00D00068
+#define CS35L36_PAC_INT_STATUS 0x00D00200
+#define CS35L36_PAC_INT_RAW_STATUS 0x00D00210
+#define CS35L36_PAC_INT_FLUSH_CTRL 0x00D00218
+#define CS35L36_PAC_INT0_CTRL 0x00D00220
+#define CS35L36_PAC_INT1_CTRL 0x00D00224
+#define CS35L36_PAC_INT2_CTRL 0x00D00228
+#define CS35L36_PAC_INT3_CTRL 0x00D0022C
+#define CS35L36_PAC_INT4_CTRL 0x00D00230
+#define CS35L36_PAC_INT5_CTRL 0x00D00234
+#define CS35L36_PAC_INT6_CTRL 0x00D00238
+#define CS35L36_PAC_INT7_CTRL 0x00D0023C
+#define CS35L36_PAC_PMEM_WORD0 0x00E02800
+#define CS35L36_PAC_PMEM_WORD1 0x00E02804
+#define CS35L36_PAC_PMEM_WORD1023 0x00E037FC
+
+#define CS35L36_INTPAC_REG_COUNT 25
+#define CS35L36_CHIP_ID 0x00035A36
+
+#define CS35L36_INT_OUTPUT_EN_MASK 0x01
+#define CS35L36_INT_GPIO_SEL_MASK 0x02
+#define CS35L36_INT_GPIO_SEL_SHIFT 1
+#define CS35L36_INT_POL_SEL_MASK 0x04
+#define CS35L36_INT_POL_SEL_SHIFT 2
+#define CS35L36_INT_DRV_SEL_MASK 0x20
+#define CS35L36_INT_DRV_SEL_SHIFT 5
+#define CS35L36_IRQ_SRC_MASK 0x08
+#define CS35L36_IRQ_SRC_SHIFT 3
+
+#define CS35L36_SCLK_MSTR_MASK 0x40
+#define CS35L36_SCLK_MSTR_SHIFT 6
+#define CS35L36_LRCLK_MSTR_MASK 0x01
+#define CS35L36_LRCLK_MSTR_SHIFT 0
+#define CS35L36_SCLK_INV_MASK 0x100
+#define CS35L36_SCLK_INV_SHIFT 8
+#define CS35L36_LRCLK_INV_MASK 0x04
+#define CS35L36_LRCLK_INV_SHIFT 2
+#define CS35L36_SCLK_FRC_MASK 0x80
+#define CS35L36_SCLK_FRC_SHIFT 7
+#define CS35L36_LRCLK_FRC_MASK 0x02
+#define CS35L36_LRCLK_FRC_SHIFT 1
+
+#define CS35L36_PDM_MODE_MASK 0x01
+#define CS35L36_PDM_MODE_SHIFT 0
+
+#define CS35L36_ASP_FMT_MASK 0x07
+#define CS35L36_ASP_FMT_SHIFT 0
+
+#define CS35L36_ASP_RX_WIDTH_MASK 0xFF0000
+#define CS35L36_ASP_RX_WIDTH_SHIFT 16
+#define CS35L36_ASP_TX_WIDTH_MASK 0xFF
+#define CS35L36_ASP_TX_WIDTH_SHIFT 0
+#define CS35L36_ASP_WIDTH_16 0x10
+#define CS35L36_ASP_WIDTH_24 0x18
+#define CS35L36_ASP_WIDTH_32 0x20
+
+#define CS35L36_ASP_RX1_SLOT_MASK 0x3F
+#define CS35L36_ASP_RX1_EN_MASK 0x00010000
+#define CS35L36_ASP_RX1_EN_SHIFT 16
+
+#define CS35L36_ASP_TX1_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX2_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX2_SLOT_SHIFT 16
+#define CS35L36_ASP_TX3_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX4_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX4_SLOT_SHIFT 16
+#define CS35L36_ASP_TX5_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX6_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX6_SLOT_SHIFT 16
+#define CS35L36_ASP_TX7_SLOT_MASK 0x3F
+#define CS35L36_ASP_TX8_SLOT_MASK 0x3F0000
+#define CS35L36_ASP_TX8_SLOT_SHIFT 16
+#define CS35L36_ASP_TX_HIZ_MASK 0x200000
+
+#define CS35L36_APS_TX_SEL_MASK 0x7F
+
+#define CS35L36_ASP_TX1_EN_MASK 0x01
+#define CS35L36_ASP_TX2_EN_MASK 0x02
+#define CS35L36_ASP_TX2_EN_SHIFT 1
+#define CS35L36_ASP_TX3_EN_MASK 0x04
+#define CS35L36_ASP_TX3_EN_SHIFT 2
+#define CS35L36_ASP_TX4_EN_MASK 0x08
+#define CS35L36_ASP_TX4_EN_SHIFT 3
+#define CS35L36_ASP_TX5_EN_MASK 0x10
+#define CS35L36_ASP_TX5_EN_SHIFT 4
+#define CS35L36_ASP_TX6_EN_MASK 0x20
+#define CS35L36_ASP_TX6_EN_SHIFT 5
+#define CS35L36_ASP_TX7_EN_MASK 0x40
+#define CS35L36_ASP_TX7_EN_SHIFT 6
+#define CS35L36_ASP_TX8_EN_MASK 0x80
+#define CS35L36_ASP_TX8_EN_SHIFT 7
+
+
+#define CS35L36_PLL_CLK_SEL_MASK 0x07
+#define CS35L36_PLL_CLK_SEL_SHIFT 0
+#define CS35L36_PLLSRC_SCLK 0
+#define CS35L36_PLLSRC_LRCLK 1
+#define CS35L36_PLLSRC_SELF 3
+#define CS35L36_PLLSRC_PDMCLK 4
+#define CS35L36_PLLSRC_MCLK 5
+#define CS35L36_PLLSRC_SWIRE 7
+#define CS35L36_REFCLK_FREQ_MASK 0x7E0
+#define CS35L36_REFCLK_FREQ_SHIFT 5
+#define CS35L36_PLL_OPENLOOP_MASK 0x800
+#define CS35L36_PLL_OPENLOOP_SHIFT 11
+#define CS35L36_PLL_REFCLK_EN_MASK 0x10
+#define CS35L36_PLL_REFCLK_EN_SHIFT 4
+
+
+#define CS35L36_GLOBAL_FS_MASK 0x1F
+#define CS35L36_GLOBAL_FS_SHIFT 0
+
+#define CS35L36_HPF_PCM_EN_MASK 0x800
+#define CS35L36_HPF_PCM_EN_SHIFT 15
+#define CS35L36_PCM_RX_SEL_MASK 0x7F
+#define CS35L36_PCM_RX_SEL_SHIFT 0
+
+#define CS35L36_PCM_RX_SEL_ZERO 0x00
+#define CS35L36_PCM_RX_SEL_PCM 0x08
+#define CS35L36_PCM_RX_SEL_SWIRE 0x10
+#define CS35L36_PCM_RX_SEL_DIAG 0x04
+
+#define CS35L36_GLOBAL_EN_MASK 0x01
+#define CS35L36_GLOBAL_EN_SHIFT 0x00
+
+#define CS35L36_AMP_PCM_INV_MASK 0x4000
+#define CS35L36_AMP_PCM_INV_SHIFT 14
+
+#define CS35L36_AMP_VOL_PCM_MASK 0x3FF8
+#define CS35L36_AMP_VOL_PCM_SHIFT 3
+#define CS35L36_DIGITAL_MUTE 0x04CF
+
+#define CS35L36_AMP_RAMP_MASK 0x0007
+#define CS35L36_AMP_RAMP_SHIFT 0
+
+#define CS35L36_AMP_MUTE_MASK 0x0010
+#define CS35L36_AMP_MUTE_SHIFT 4
+
+#define CS35L36_GLOBAL_RESYNC_FS1_MASK 0x00000200
+#define CS35L36_GLOBAL_RESYNC_FS2_MASK 0x00000400
+#define CS35L36_SYNC_GLOBAL_OVR_MASK 0x00000002
+#define CS35L36_SYNC_GLOBAL_OVR_SHIFT 1
+
+#define CS35L36_REFCLK_IN_MASK 0x00100000
+#define CS35L36_PLL_UNLOCK_MASK 0x00002000
+
+#define CS35L36_ASP_RX_UDF_MASK 0x00000040
+#define CS35L36_ASP_RX_OVF_MASK 0x00000080
+
+#define CS35L36_IMON_POL_MASK 0x02
+#define CS35L36_IMON_POL_SHIFT 1
+
+#define CS35L36_VMON_POL_MASK 0x01
+#define CS35L36_VMON_POL_SHIFT 0
+
+#define CS35L36_PDN_DONE 0x40
+#define CS35L36_PDN_DONE_SHIFT 6
+#define CS35L36_PUP_DONE 0x80
+#define CS35L36_PUP_DONE_SHIFT 7
+#define CS35L36_GLOBAL_EN_ASSRT 0x20
+#define CS35L36_PUP_DONE_IRQ_UNMASK 0x7F
+#define CS35L36_PUP_DONE_IRQ_MASK 0xBF
+
+#define CS35L36_FS1_WINDOW_MASK 0x000007FF
+#define CS35L36_FS2_WINDOW_MASK 0x00FFF800
+#define CS35L36_FS2_WINDOW_SHIFT 12
+
+#define CS35L36_PLL_FFL_IGAIN_MASK 0x0F
+#define CS35L36_PLL_IGAIN_MASK 0x3F0
+#define CS35L36_PLL_IGAIN_SHIFT 4
+#define CS35L36_PLL_IGAIN 0x04
+
+#define CS35L36_BST_EN_MASK 0x30
+#define CS35L36_BST_EN 0x02
+#define CS35L36_BST_DIS_VP 0x01
+#define CS35L36_BST_DIS_EXTN 0x00
+#define CS35L36_BST_EN_SHIFT 4
+#define CS35L36_BST_MAN_IPKCOMP_MASK 0x200
+#define CS35L36_BST_MAN_IPKCOMP_SHIFT 9
+
+#define CS35L36_BST_MAN_IPKCOMP_EN_MASK 0x100
+#define CS35L36_BST_MAN_IPKCOMP_EN_SHIFT 8
+
+#define CS35L36_BST_IPK_MASK 0x7F
+#define CS35L36_BST_OVP_THLD_MASK 0x3F
+#define CS35L36_BST_OVP_THLD_11V 0x10
+#define CS35L36_BST_OVP_TRIM_MASK 0x00078000
+#define CS35L36_BST_OVP_TRIM_SHIFT 15
+#define CS35L36_BST_OVP_TRIM_11V 0x0C
+#define CS35L36_BST_CTRL_LIM_MASK 0x04
+#define CS35L36_BST_CTRL_LIM_SHIFT 2
+#define CS35L36_BST_CTRL_10V_CLAMP 0x96
+
+#define CS35L36_NG_AMP_EN_MASK 0x3F00
+#define CS35L36_NG_DELAY_MASK 0x70
+#define CS35L36_NG_DELAY_SHIFT 4
+#define CS35L36_AMP_ZC_SHIFT 10
+#define CS35L36_PDM_LDM_ENTER_SHIFT 3
+#define CS35L36_PDM_LDM_EXIT_SHIFT 4
+
+#define CS35L36_BSTCVRT_K1_MASK 0xFF
+#define CS35L36_BSTCVRT_K2_MASK 0xFF00
+#define CS35L36_BSTCVRT_K2_SHIFT 8
+#define CS35L36_BSTCVRT_SLOPE_MASK 0xFF00
+#define CS35L36_BSTCVRT_SLOPE_SHIFT 8
+#define CS35L36_BSTCVRT_CCMFREQ_MASK 0x0F
+#define CS35L36_BSTCVRT_LBSTVAL_MASK 0x03
+#define CS35L35_BSTCVRT_CTL_MASK 0xFF
+#define CS35L35_BSTCVRT_CTL_SEL_MASK 0x03
+#define CS35L36_DCM_AUTO_MASK 0x01
+
+#define CS35L36_INT1_MASK_DEFAULT 0xF9BA7FFF
+#define CS35L36_INT1_MASK_RESET 0xFFFFFFFF
+#define CS35L36_INT3_MASK_DEFAULT 0xFFFFEFFF
+#define CS35L36_INT3_MASK_RESET 0xFFFFFFFF
+
+
+#define CS35L36_AMP_SHORT_ERR 0x1000
+#define CS35L36_BST_SHORT_ERR 0x40000
+#define CS35L36_TEMP_WARN 0x2000000
+#define CS35L36_TEMP_ERR 0x4000000
+#define CS35L36_BST_OVP_ERR 0x10000
+#define CS35L36_BST_DCM_UVP_ERR 0x20000
+
+#define CS35L36_AMP_SHORT_ERR_RLS 0x02
+#define CS35L36_BST_SHORT_ERR_RLS 0x04
+#define CS35L36_BST_OVP_ERR_RLS 0x08
+#define CS35L36_BST_UVP_ERR_RLS 0x10
+#define CS35L36_TEMP_WARN_ERR_RLS 0x20
+#define CS35L36_TEMP_ERR_RLS 0x40
+#define CS35L36_TEMP_THLD_MASK 0x03
+
+#define CS35L36_REV_B0 0xb0
+#define CS35L36_REV_A0 0xa0
+#define CS35L36_B0_PAC_PATCH 0x00DD0102
+
+#define CS35L36_OTP_ECC_EN_MASK 0x400
+#define CS35L36_OTP_ECC_EN_SHIFT 10
+#define CS35L36_OTP_RUN_BOOT_MASK 0x01
+#define CS35L36_OTP_BOOT_DONE 0x2000000
+#define CS35L36_PAC_RESET_MASK 0x04
+#define CS35L36_PAC_RESET_SHIFT 2
+#define CS35L36_PAC_STALL_MASK 0x02
+#define CS35L36_PAC_STALL_SHIFT 1
+#define CS35L36_PAC_ENABLE_MASK 0x00000001
+#define CS35L36_PAC_MEM_ACCESS 0x01
+#define CS35L36_PAC_MEM_ACCESS_CLR 0
+#define CS35L36_SOFT_RESET 0x5AAA
+#define CS35L36_MCU_BOOT_COMPLETE 0x02
+#define CS35L36_MCU_CONFIG_UNMASK 0x00FEFFFF
+#define CS35L36_MCU_CONFIG_CLR 0x00010000
+#define CS35L36_MCU_CONFIG_MASK 0x00FFFFFF
+#define CS35L36_GPIO_INT_SEL_MASK 0x0000003B
+#define CS35L36_GPIO_INT_SEL_UNMASK 0x0000003A
+#define CS35L36_PAC_RESET 0x00000000
+#define CS35L36_OTP_REV_MASK 0x00FF0000
+#define CS35L36_OTP_REV_L37 0x00CC0000
+#define CS35L36_12V_L37 37
+#define CS35L36_10V_L36 36
+
+#define CS35L36_VPBR_EN_MASK 0x00001000
+#define CS35L36_VPBR_EN_SHIFT 12
+
+#define CS35L36_VPBR_THLD_MASK 0x0000001F
+#define CS35L36_VPBR_THLD_SHIFT 0
+#define CS35L36_VPBR_MAX_ATTN_MASK 0x00000F00
+#define CS35L36_VPBR_MAX_ATTN_SHIFT 8
+#define CS35L36_VPBR_ATK_VOL_MASK 0x0000F000
+#define CS35L36_VPBR_ATK_VOL_SHIFT 12
+#define CS35L36_VPBR_ATK_RATE_MASK 0x00070000
+#define CS35L36_VPBR_ATK_RATE_SHIFT 16
+#define CS35L36_VPBR_WAIT_MASK 0x00180000
+#define CS35L36_VPBR_WAIT_SHIFT 19
+#define CS35L36_VPBR_REL_RATE_MASK 0x00E00000
+#define CS35L36_VPBR_REL_RATE_SHIFT 21
+#define CS35L36_VPBR_MUTE_EN_MASK 0x01000000
+#define CS35L36_VPBR_MUTE_EN_SHIFT 24
+
+#define CS35L36_OSC_FREQ_TRIM_MASK 0x070
+#define CS35L36_OSC_TRIM_DONE 0x08
+
+#define CS35L36_FS1_DEFAULT_VAL 16
+#define CS35L36_FS2_DEFAULT_VAL 36
+#define CS35L36_FS_NOM_6MHZ 6000000
+
+#define CS35L36_TEST_UNLOCK1 0x00005555
+#define CS35L36_TEST_UNLOCK2 0x0000AAAA
+#define CS35L36_TEST_LOCK1 0x0000CCCC
+#define CS35L36_TEST_LOCK2 0x00003333
+
+#define CS35L36_PAC_PROG_MEM 512
+
+#define CS35L36_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L36_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+extern const int cs35l36_a0_pac_patch[CS35L36_PAC_PROG_MEM];
+
+#endif
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
new file mode 100644
index 000000000000..34097996b784
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-i2c.c -- CS35l41 I2C driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "cs35l41.h"
+
+static const struct i2c_device_id cs35l41_id_i2c[] = {
+ { "cs35l40" },
+ { "cs35l41" },
+ { "cs35l51" },
+ { "cs35l53" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
+
+static int cs35l41_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l41_private *cs35l41;
+ struct device *dev = &client->dev;
+ struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(dev);
+ const struct regmap_config *regmap_config = &cs35l41_regmap_i2c;
+
+ cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+
+ if (!cs35l41)
+ return -ENOMEM;
+
+ cs35l41->dev = dev;
+ cs35l41->irq = client->irq;
+
+ i2c_set_clientdata(client, cs35l41);
+ cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l41->regmap))
+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
+ "Failed to allocate register map\n");
+
+ return cs35l41_probe(cs35l41, hw_cfg);
+}
+
+static void cs35l41_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l41_private *cs35l41 = i2c_get_clientdata(client);
+
+ cs35l41_remove(cs35l41);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cs35l41_of_match[] = {
+ { .compatible = "cirrus,cs35l40" },
+ { .compatible = "cirrus,cs35l41" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_match[] = {
+ { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
+#endif
+
+static struct i2c_driver cs35l41_i2c_driver = {
+ .driver = {
+ .name = "cs35l41",
+ .pm = pm_ptr(&cs35l41_pm_ops),
+ .of_match_table = of_match_ptr(cs35l41_of_match),
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
+ },
+ .id_table = cs35l41_id_i2c,
+ .probe = cs35l41_i2c_probe,
+ .remove = cs35l41_i2c_remove,
+};
+
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
new file mode 100644
index 000000000000..1702f26049d3
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -0,0 +1,1592 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-lib.c -- CS35L41 Common functions for HDA and ASoC Audio drivers
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+// Author: Lucas Tanure <lucas.tanure@cirrus.com>
+
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+#include <sound/cs35l41.h>
+
+#define CS35L41_FIRMWARE_OLD_VERSION 0x001C00 /* v0.28.0 */
+
+static const struct reg_default cs35l41_reg[] = {
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_PWR_CTRL3, 0x01000010 },
+ { CS35L41_GPIO_PAD_CONTROL, 0x00000000 },
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 },
+ { CS35L41_TST_FS_MON0, 0x00020016 },
+ { CS35L41_BSTCVRT_COEFF, 0x00002424 },
+ { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 },
+ { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A },
+ { CS35L41_SP_ENABLES, 0x00000000 },
+ { CS35L41_SP_RATE_CTRL, 0x00000028 },
+ { CS35L41_SP_FORMAT, 0x18180200 },
+ { CS35L41_SP_HIZ_CTRL, 0x00000002 },
+ { CS35L41_SP_FRAME_TX_SLOT, 0x03020100 },
+ { CS35L41_SP_FRAME_RX_SLOT, 0x00000100 },
+ { CS35L41_SP_TX_WL, 0x00000018 },
+ { CS35L41_SP_RX_WL, 0x00000018 },
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 },
+ { CS35L41_ASP_TX1_SRC, 0x00000018 },
+ { CS35L41_ASP_TX2_SRC, 0x00000019 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+ { CS35L41_DSP1_RX1_SRC, 0x00000008 },
+ { CS35L41_DSP1_RX2_SRC, 0x00000009 },
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 },
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 },
+ { CS35L41_DSP1_RX5_SRC, 0x00000020 },
+ { CS35L41_DSP1_RX6_SRC, 0x00000021 },
+ { CS35L41_DSP1_RX7_SRC, 0x0000003A },
+ { CS35L41_DSP1_RX8_SRC, 0x0000003B },
+ { CS35L41_NGATE1_SRC, 0x00000008 },
+ { CS35L41_NGATE2_SRC, 0x00000009 },
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 },
+ { CS35L41_CLASSH_CFG, 0x000B0405 },
+ { CS35L41_WKFET_CFG, 0x00000111 },
+ { CS35L41_NG_CFG, 0x00000033 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_IRQ1_MASK1, 0xFFFFFFFF },
+ { CS35L41_IRQ1_MASK2, 0xFFFFFFFF },
+ { CS35L41_IRQ1_MASK3, 0xFFFF87FF },
+ { CS35L41_IRQ1_MASK4, 0xFEFFFFFF },
+ { CS35L41_GPIO1_CTRL1, 0x81000001 },
+ { CS35L41_GPIO2_CTRL1, 0x81000001 },
+ { CS35L41_MIXER_NGATE_CFG, 0x00000000 },
+ { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 },
+ { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 },
+ { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 },
+};
+
+static bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_DEVID:
+ case CS35L41_REVID:
+ case CS35L41_FABID:
+ case CS35L41_RELID:
+ case CS35L41_OTPID:
+ case CS35L41_SFT_RESET:
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_OTP_CTRL0:
+ case CS35L41_OTP_CTRL3:
+ case CS35L41_OTP_CTRL4:
+ case CS35L41_OTP_CTRL5:
+ case CS35L41_OTP_CTRL6:
+ case CS35L41_OTP_CTRL7:
+ case CS35L41_OTP_CTRL8:
+ case CS35L41_PWR_CTRL1:
+ case CS35L41_PWR_CTRL2:
+ case CS35L41_PWR_CTRL3:
+ case CS35L41_CTRL_OVRRIDE:
+ case CS35L41_AMP_OUT_MUTE:
+ case CS35L41_PROTECT_REL_ERR_IGN:
+ case CS35L41_GPIO_PAD_CONTROL:
+ case CS35L41_JTAG_CONTROL:
+ case CS35L41_PWRMGT_CTL:
+ case CS35L41_WAKESRC_CTL:
+ case CS35L41_PWRMGT_STS:
+ case CS35L41_PLL_CLK_CTRL:
+ case CS35L41_DSP_CLK_CTRL:
+ case CS35L41_GLOBAL_CLK_CTRL:
+ case CS35L41_DATA_FS_SEL:
+ case CS35L41_TST_FS_MON0:
+ case CS35L41_MDSYNC_EN:
+ case CS35L41_MDSYNC_TX_ID:
+ case CS35L41_MDSYNC_PWR_CTRL:
+ case CS35L41_MDSYNC_DATA_TX:
+ case CS35L41_MDSYNC_TX_STATUS:
+ case CS35L41_MDSYNC_DATA_RX:
+ case CS35L41_MDSYNC_RX_STATUS:
+ case CS35L41_MDSYNC_ERR_STATUS:
+ case CS35L41_MDSYNC_SYNC_PTE2:
+ case CS35L41_MDSYNC_SYNC_PTE3:
+ case CS35L41_MDSYNC_SYNC_MSM_STATUS:
+ case CS35L41_BSTCVRT_VCTRL1:
+ case CS35L41_BSTCVRT_VCTRL2:
+ case CS35L41_BSTCVRT_PEAK_CUR:
+ case CS35L41_BSTCVRT_SFT_RAMP:
+ case CS35L41_BSTCVRT_COEFF:
+ case CS35L41_BSTCVRT_SLOPE_LBST:
+ case CS35L41_BSTCVRT_SW_FREQ:
+ case CS35L41_BSTCVRT_DCM_CTRL:
+ case CS35L41_BSTCVRT_DCM_MODE_FORCE:
+ case CS35L41_BSTCVRT_OVERVOLT_CTRL:
+ case CS35L41_VI_VOL_POL:
+ case CS35L41_DTEMP_WARN_THLD:
+ case CS35L41_DTEMP_CFG:
+ case CS35L41_DTEMP_EN:
+ case CS35L41_VPVBST_FS_SEL:
+ case CS35L41_SP_ENABLES:
+ case CS35L41_SP_RATE_CTRL:
+ case CS35L41_SP_FORMAT:
+ case CS35L41_SP_HIZ_CTRL:
+ case CS35L41_SP_FRAME_TX_SLOT:
+ case CS35L41_SP_FRAME_RX_SLOT:
+ case CS35L41_SP_TX_WL:
+ case CS35L41_SP_RX_WL:
+ case CS35L41_DAC_PCM1_SRC:
+ case CS35L41_ASP_TX1_SRC:
+ case CS35L41_ASP_TX2_SRC:
+ case CS35L41_ASP_TX3_SRC:
+ case CS35L41_ASP_TX4_SRC:
+ case CS35L41_DSP1_RX1_SRC:
+ case CS35L41_DSP1_RX2_SRC:
+ case CS35L41_DSP1_RX3_SRC:
+ case CS35L41_DSP1_RX4_SRC:
+ case CS35L41_DSP1_RX5_SRC:
+ case CS35L41_DSP1_RX6_SRC:
+ case CS35L41_DSP1_RX7_SRC:
+ case CS35L41_DSP1_RX8_SRC:
+ case CS35L41_NGATE1_SRC:
+ case CS35L41_NGATE2_SRC:
+ case CS35L41_AMP_DIG_VOL_CTRL:
+ case CS35L41_VPBR_CFG:
+ case CS35L41_VBBR_CFG:
+ case CS35L41_VPBR_STATUS:
+ case CS35L41_VBBR_STATUS:
+ case CS35L41_OVERTEMP_CFG:
+ case CS35L41_AMP_ERR_VOL:
+ case CS35L41_VOL_STATUS_TO_DSP:
+ case CS35L41_CLASSH_CFG:
+ case CS35L41_WKFET_CFG:
+ case CS35L41_NG_CFG:
+ case CS35L41_AMP_GAIN_CTRL:
+ case CS35L41_DAC_MSM_CFG:
+ case CS35L41_IRQ1_CFG:
+ case CS35L41_IRQ1_STATUS:
+ case CS35L41_IRQ1_STATUS1:
+ case CS35L41_IRQ1_STATUS2:
+ case CS35L41_IRQ1_STATUS3:
+ case CS35L41_IRQ1_STATUS4:
+ case CS35L41_IRQ1_RAW_STATUS1:
+ case CS35L41_IRQ1_RAW_STATUS2:
+ case CS35L41_IRQ1_RAW_STATUS3:
+ case CS35L41_IRQ1_RAW_STATUS4:
+ case CS35L41_IRQ1_MASK1:
+ case CS35L41_IRQ1_MASK2:
+ case CS35L41_IRQ1_MASK3:
+ case CS35L41_IRQ1_MASK4:
+ case CS35L41_IRQ1_FRC1:
+ case CS35L41_IRQ1_FRC2:
+ case CS35L41_IRQ1_FRC3:
+ case CS35L41_IRQ1_FRC4:
+ case CS35L41_IRQ1_EDGE1:
+ case CS35L41_IRQ1_EDGE4:
+ case CS35L41_IRQ1_POL1:
+ case CS35L41_IRQ1_POL2:
+ case CS35L41_IRQ1_POL3:
+ case CS35L41_IRQ1_POL4:
+ case CS35L41_IRQ1_DB3:
+ case CS35L41_IRQ2_CFG:
+ case CS35L41_IRQ2_STATUS:
+ case CS35L41_IRQ2_STATUS1:
+ case CS35L41_IRQ2_STATUS2:
+ case CS35L41_IRQ2_STATUS3:
+ case CS35L41_IRQ2_STATUS4:
+ case CS35L41_IRQ2_RAW_STATUS1:
+ case CS35L41_IRQ2_RAW_STATUS2:
+ case CS35L41_IRQ2_RAW_STATUS3:
+ case CS35L41_IRQ2_RAW_STATUS4:
+ case CS35L41_IRQ2_MASK1:
+ case CS35L41_IRQ2_MASK2:
+ case CS35L41_IRQ2_MASK3:
+ case CS35L41_IRQ2_MASK4:
+ case CS35L41_IRQ2_FRC1:
+ case CS35L41_IRQ2_FRC2:
+ case CS35L41_IRQ2_FRC3:
+ case CS35L41_IRQ2_FRC4:
+ case CS35L41_IRQ2_EDGE1:
+ case CS35L41_IRQ2_EDGE4:
+ case CS35L41_IRQ2_POL1:
+ case CS35L41_IRQ2_POL2:
+ case CS35L41_IRQ2_POL3:
+ case CS35L41_IRQ2_POL4:
+ case CS35L41_IRQ2_DB3:
+ case CS35L41_GPIO_STATUS1:
+ case CS35L41_GPIO1_CTRL1:
+ case CS35L41_GPIO2_CTRL1:
+ case CS35L41_MIXER_NGATE_CFG:
+ case CS35L41_MIXER_NGATE_CH1_CFG:
+ case CS35L41_MIXER_NGATE_CH2_CFG:
+ case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8:
+ case CS35L41_CLOCK_DETECT_1:
+ case CS35L41_DIE_STS1:
+ case CS35L41_DIE_STS2:
+ case CS35L41_TEMP_CAL1:
+ case CS35L41_TEMP_CAL2:
+ case CS35L41_DSP1_TIMESTAMP_COUNT:
+ case CS35L41_DSP1_SYS_ID:
+ case CS35L41_DSP1_SYS_VERSION:
+ case CS35L41_DSP1_SYS_CORE_ID:
+ case CS35L41_DSP1_SYS_AHB_ADDR:
+ case CS35L41_DSP1_SYS_XSRAM_SIZE:
+ case CS35L41_DSP1_SYS_YSRAM_SIZE:
+ case CS35L41_DSP1_SYS_PSRAM_SIZE:
+ case CS35L41_DSP1_SYS_PM_BOOT_SIZE:
+ case CS35L41_DSP1_SYS_FEATURES:
+ case CS35L41_DSP1_SYS_FIR_FILTERS:
+ case CS35L41_DSP1_SYS_LMS_FILTERS:
+ case CS35L41_DSP1_SYS_XM_BANK_SIZE:
+ case CS35L41_DSP1_SYS_YM_BANK_SIZE:
+ case CS35L41_DSP1_SYS_PM_BANK_SIZE:
+ case CS35L41_DSP1_RX1_RATE:
+ case CS35L41_DSP1_RX2_RATE:
+ case CS35L41_DSP1_RX3_RATE:
+ case CS35L41_DSP1_RX4_RATE:
+ case CS35L41_DSP1_RX5_RATE:
+ case CS35L41_DSP1_RX6_RATE:
+ case CS35L41_DSP1_RX7_RATE:
+ case CS35L41_DSP1_RX8_RATE:
+ case CS35L41_DSP1_TX1_RATE:
+ case CS35L41_DSP1_TX2_RATE:
+ case CS35L41_DSP1_TX3_RATE:
+ case CS35L41_DSP1_TX4_RATE:
+ case CS35L41_DSP1_TX5_RATE:
+ case CS35L41_DSP1_TX6_RATE:
+ case CS35L41_DSP1_TX7_RATE:
+ case CS35L41_DSP1_TX8_RATE:
+ case CS35L41_DSP1_SCRATCH1:
+ case CS35L41_DSP1_SCRATCH2:
+ case CS35L41_DSP1_SCRATCH3:
+ case CS35L41_DSP1_SCRATCH4:
+ case CS35L41_DSP1_CCM_CORE_CTRL:
+ case CS35L41_DSP1_CCM_CLK_OVERRIDE:
+ case CS35L41_DSP1_XM_MSTR_EN:
+ case CS35L41_DSP1_XM_CORE_PRI:
+ case CS35L41_DSP1_XM_AHB_PACK_PL_PRI:
+ case CS35L41_DSP1_XM_AHB_UP_PL_PRI:
+ case CS35L41_DSP1_XM_ACCEL_PL0_PRI:
+ case CS35L41_DSP1_XM_NPL0_PRI:
+ case CS35L41_DSP1_YM_MSTR_EN:
+ case CS35L41_DSP1_YM_CORE_PRI:
+ case CS35L41_DSP1_YM_AHB_PACK_PL_PRI:
+ case CS35L41_DSP1_YM_AHB_UP_PL_PRI:
+ case CS35L41_DSP1_YM_ACCEL_PL0_PRI:
+ case CS35L41_DSP1_YM_NPL0_PRI:
+ case CS35L41_DSP1_MPU_XM_ACCESS0:
+ case CS35L41_DSP1_MPU_YM_ACCESS0:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS0:
+ case CS35L41_DSP1_MPU_XREG_ACCESS0:
+ case CS35L41_DSP1_MPU_YREG_ACCESS0:
+ case CS35L41_DSP1_MPU_XM_ACCESS1:
+ case CS35L41_DSP1_MPU_YM_ACCESS1:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS1:
+ case CS35L41_DSP1_MPU_XREG_ACCESS1:
+ case CS35L41_DSP1_MPU_YREG_ACCESS1:
+ case CS35L41_DSP1_MPU_XM_ACCESS2:
+ case CS35L41_DSP1_MPU_YM_ACCESS2:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS2:
+ case CS35L41_DSP1_MPU_XREG_ACCESS2:
+ case CS35L41_DSP1_MPU_YREG_ACCESS2:
+ case CS35L41_DSP1_MPU_XM_ACCESS3:
+ case CS35L41_DSP1_MPU_YM_ACCESS3:
+ case CS35L41_DSP1_MPU_WNDW_ACCESS3:
+ case CS35L41_DSP1_MPU_XREG_ACCESS3:
+ case CS35L41_DSP1_MPU_YREG_ACCESS3:
+ case CS35L41_DSP1_MPU_XM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_XM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_YM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_YM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_PM_VIO_ADDR:
+ case CS35L41_DSP1_MPU_PM_VIO_STATUS:
+ case CS35L41_DSP1_MPU_LOCK_CONFIG:
+ case CS35L41_DSP1_MPU_WDT_RST_CTRL:
+ case CS35L41_OTP_TRIM_1:
+ case CS35L41_OTP_TRIM_2:
+ case CS35L41_OTP_TRIM_3:
+ case CS35L41_OTP_TRIM_4:
+ case CS35L41_OTP_TRIM_5:
+ case CS35L41_OTP_TRIM_6:
+ case CS35L41_OTP_TRIM_7:
+ case CS35L41_OTP_TRIM_8:
+ case CS35L41_OTP_TRIM_9:
+ case CS35L41_OTP_TRIM_10:
+ case CS35L41_OTP_TRIM_11:
+ case CS35L41_OTP_TRIM_12:
+ case CS35L41_OTP_TRIM_13:
+ case CS35L41_OTP_TRIM_14:
+ case CS35L41_OTP_TRIM_15:
+ case CS35L41_OTP_TRIM_16:
+ case CS35L41_OTP_TRIM_17:
+ case CS35L41_OTP_TRIM_18:
+ case CS35L41_OTP_TRIM_19:
+ case CS35L41_OTP_TRIM_20:
+ case CS35L41_OTP_TRIM_21:
+ case CS35L41_OTP_TRIM_22:
+ case CS35L41_OTP_TRIM_23:
+ case CS35L41_OTP_TRIM_24:
+ case CS35L41_OTP_TRIM_25:
+ case CS35L41_OTP_TRIM_26:
+ case CS35L41_OTP_TRIM_27:
+ case CS35L41_OTP_TRIM_28:
+ case CS35L41_OTP_TRIM_29:
+ case CS35L41_OTP_TRIM_30:
+ case CS35L41_OTP_TRIM_31:
+ case CS35L41_OTP_TRIM_32:
+ case CS35L41_OTP_TRIM_33:
+ case CS35L41_OTP_TRIM_34:
+ case CS35L41_OTP_TRIM_35:
+ case CS35L41_OTP_TRIM_36:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+ case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+ case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ /*test regs*/
+ case CS35L41_PLL_OVR:
+ case CS35L41_BST_TEST_DUTY:
+ case CS35L41_DIGPWM_IOCTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l41_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ case CS35L41_TST_FS_MON0:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L41_DEVID:
+ case CS35L41_SFT_RESET:
+ case CS35L41_FABID:
+ case CS35L41_REVID:
+ case CS35L41_OTPID:
+ case CS35L41_TEST_KEY_CTL:
+ case CS35L41_USER_KEY_CTL:
+ case CS35L41_PWRMGT_CTL:
+ case CS35L41_WAKESRC_CTL:
+ case CS35L41_PWRMGT_STS:
+ case CS35L41_DTEMP_EN:
+ case CS35L41_IRQ1_STATUS:
+ case CS35L41_IRQ1_STATUS1:
+ case CS35L41_IRQ1_STATUS2:
+ case CS35L41_IRQ1_STATUS3:
+ case CS35L41_IRQ1_STATUS4:
+ case CS35L41_IRQ1_RAW_STATUS1:
+ case CS35L41_IRQ1_RAW_STATUS2:
+ case CS35L41_IRQ1_RAW_STATUS3:
+ case CS35L41_IRQ1_RAW_STATUS4:
+ case CS35L41_IRQ2_STATUS:
+ case CS35L41_IRQ2_STATUS1:
+ case CS35L41_IRQ2_STATUS2:
+ case CS35L41_IRQ2_STATUS3:
+ case CS35L41_IRQ2_STATUS4:
+ case CS35L41_IRQ2_RAW_STATUS1:
+ case CS35L41_IRQ2_RAW_STATUS2:
+ case CS35L41_IRQ2_RAW_STATUS3:
+ case CS35L41_IRQ2_RAW_STATUS4:
+ case CS35L41_GPIO_STATUS1:
+ case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8:
+ case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068:
+ case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046:
+ case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093:
+ case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532:
+ case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022:
+ case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045:
+ case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114:
+ case CS35L41_DSP1_SCRATCH1:
+ case CS35L41_DSP1_SCRATCH2:
+ case CS35L41_DSP1_SCRATCH3:
+ case CS35L41_DSP1_SCRATCH4:
+ case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS:
+ case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct cs35l41_otp_packed_element_t otp_map_1[] = {
+ /* addr shift size */
+ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
+ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
+ { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/
+ { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/
+ { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/
+ { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/
+ { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/
+ { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/
+ { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/
+ { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+ { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/
+ { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/
+ { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/
+ { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+ { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/
+ { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/
+ { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/
+ { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+ { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/
+ { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/
+ { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/
+ { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+ { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/
+ { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/
+ { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/
+ { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/
+ { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+ { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/
+ { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+ { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+ { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+ { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+ { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/
+ { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/
+ { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/
+ { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/
+ { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/
+ { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/
+ { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/
+ { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/
+ { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/
+ { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/
+ { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/
+ { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/
+ { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/
+ { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/
+ { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/
+ { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/
+ { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/
+ { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/
+ { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/
+ { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/
+ { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/
+ { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/
+ { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/
+ { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/
+ { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/
+ { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/
+ { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/
+ { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/
+ { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/
+ { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/
+ { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/
+ { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/
+ { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/
+ { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/
+ { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/
+ { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/
+ { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/
+ { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/
+ { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/
+ { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/
+ { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/
+ { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/
+ { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/
+ { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/
+ { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/
+ { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/
+ { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/
+ { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/
+ { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/
+ { 0x00006E64, 0, 10 }, /*VOFF_INT1*/
+ { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/
+ { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/
+ { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/
+ { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/
+ { 0x00007434, 17, 1 }, /*FORCE_CAL*/
+ { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/
+ { 0x00007068, 0, 9 }, /*MODIX*/
+ { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/
+ { 0x0000400C, 0, 7 }, /*VIMON_DLY*/
+ { 0x00000000, 0, 1 }, /*extra bit*/
+ { 0x00017040, 0, 8 }, /*X_COORDINATE*/
+ { 0x00017040, 8, 8 }, /*Y_COORDINATE*/
+ { 0x00017040, 16, 8 }, /*WAFER_ID*/
+ { 0x00017040, 24, 8 }, /*DVS*/
+ { 0x00017044, 0, 24 }, /*LOT_NUMBER*/
+};
+
+static const struct cs35l41_otp_packed_element_t otp_map_2[] = {
+ /* addr shift size */
+ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
+ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
+ { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/
+ { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/
+ { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/
+ { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/
+ { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/
+ { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/
+ { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/
+ { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/
+ { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/
+ { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/
+ { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/
+ { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/
+ { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/
+ { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/
+ { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/
+ { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/
+ { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/
+ { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/
+ { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/
+ { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/
+ { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/
+ { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/
+ { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/
+ { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/
+ { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/
+ { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/
+ { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/
+ { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/
+ { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/
+ { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/
+ { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/
+ { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/
+ { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/
+ { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/
+ { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/
+ { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/
+ { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/
+ { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/
+ { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/
+ { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/
+ { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/
+ { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/
+ { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/
+ { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/
+ { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/
+ { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/
+ { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/
+ { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/
+ { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/
+ { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/
+ { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/
+ { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/
+ { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/
+ { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/
+ { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/
+ { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/
+ { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/
+ { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/
+ { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/
+ { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/
+ { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/
+ { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/
+ { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/
+ { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/
+ { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/
+ { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/
+ { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/
+ { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/
+ { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/
+ { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/
+ { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/
+ { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/
+ { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/
+ { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/
+ { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/
+ { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/
+ { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/
+ { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/
+ { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/
+ { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/
+ { 0x00006E64, 0, 10 }, /*VOFF_INT1*/
+ { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/
+ { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/
+ { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/
+ { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/
+ { 0x00007434, 17, 1 }, /*FORCE_CAL*/
+ { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/
+ { 0x00007068, 0, 9 }, /*MODIX*/
+ { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/
+ { 0x0000400C, 0, 7 }, /*VIMON_DLY*/
+ { 0x00004000, 11, 1 }, /*VMON_POL*/
+ { 0x00017040, 0, 8 }, /*X_COORDINATE*/
+ { 0x00017040, 8, 8 }, /*Y_COORDINATE*/
+ { 0x00017040, 16, 8 }, /*WAFER_ID*/
+ { 0x00017040, 24, 8 }, /*DVS*/
+ { 0x00017044, 0, 24 }, /*LOT_NUMBER*/
+};
+
+static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
+ { 0x00003854, 0x05180240 },
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_OTP_TRIM_30, 0x9091A1C8 },
+ { 0x00003014, 0x0200EE0E },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { 0x00000054, 0x00000004 },
+ { CS35L41_IRQ1_DB3, 0x00000000 },
+ { CS35L41_IRQ2_DB3, 0x00000000 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_revb0_errata_patch[] = {
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
+ { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 },
+ { 0x00004310, 0x00000000 },
+ { CS35L41_VPVBST_FS_SEL, 0x00000000 },
+ { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 },
+ { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
+ { CS35L41_PWR_CTRL2, 0x00000000 },
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 },
+ { CS35L41_ASP_TX3_SRC, 0x00000000 },
+ { CS35L41_ASP_TX4_SRC, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l41_fs_errata_patch[] = {
+ { CS35L41_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX8_RATE, 0x00000001 },
+};
+
+static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = {
+ {
+ .id = 0x01,
+ .map = otp_map_1,
+ .num_elements = ARRAY_SIZE(otp_map_1),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x02,
+ .map = otp_map_2,
+ .num_elements = ARRAY_SIZE(otp_map_2),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x03,
+ .map = otp_map_2,
+ .num_elements = ARRAY_SIZE(otp_map_2),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x06,
+ .map = otp_map_2,
+ .num_elements = ARRAY_SIZE(otp_map_2),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+ {
+ .id = 0x08,
+ .map = otp_map_1,
+ .num_elements = ARRAY_SIZE(otp_map_1),
+ .bit_offset = 16,
+ .word_offset = 2,
+ },
+};
+
+struct regmap_config cs35l41_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = CS35L41_REGSTRIDE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L41_LASTREG,
+ .reg_defaults = cs35l41_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+ .volatile_reg = cs35l41_volatile_reg,
+ .readable_reg = cs35l41_readable_reg,
+ .precious_reg = cs35l41_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c);
+
+struct regmap_config cs35l41_regmap_spi = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = CS35L41_REGSTRIDE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L41_LASTREG,
+ .reg_defaults = cs35l41_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l41_reg),
+ .volatile_reg = cs35l41_volatile_reg,
+ .readable_reg = cs35l41_readable_reg,
+ .precious_reg = cs35l41_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_GPL(cs35l41_regmap_spi);
+
+static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) {
+ if (cs35l41_otp_map_map[i].id == otp_id)
+ return &cs35l41_otp_map_map[i];
+ }
+
+ return NULL;
+}
+
+int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap)
+{
+ static const struct reg_sequence unlock[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+ if (ret)
+ dev_err(dev, "Failed to unlock test key: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock);
+
+int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap)
+{
+ static const struct reg_sequence unlock[] = {
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+ if (ret)
+ dev_err(dev, "Failed to lock test key: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_lock);
+
+/* Must be called with the TEST_KEY unlocked */
+int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
+{
+ const struct cs35l41_otp_map_element_t *otp_map_match;
+ const struct cs35l41_otp_packed_element_t *otp_map;
+ int bit_offset, word_offset, ret, i;
+ unsigned int bit_sum = 8;
+ u32 otp_val, otp_id_reg;
+ u32 *otp_mem;
+
+ otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL);
+ if (!otp_mem)
+ return -ENOMEM;
+
+ ret = regmap_read(regmap, CS35L41_OTPID, &otp_id_reg);
+ if (ret) {
+ dev_err(dev, "Read OTP ID failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map_match = cs35l41_find_otp_map(otp_id_reg);
+
+ if (!otp_map_match) {
+ dev_err(dev, "OTP Map matching ID %d not found\n", otp_id_reg);
+ ret = -EINVAL;
+ goto err_otp_unpack;
+ }
+
+ ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS);
+ if (ret) {
+ dev_err(dev, "Read OTP Mem failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map = otp_map_match->map;
+
+ bit_offset = otp_map_match->bit_offset;
+ word_offset = otp_map_match->word_offset;
+
+ for (i = 0; i < otp_map_match->num_elements; i++) {
+ dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d, otp_map[i].size = %u\n",
+ bit_offset, word_offset, bit_sum % 32, otp_map[i].size);
+ if (bit_offset + otp_map[i].size - 1 >= 32) {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(31, bit_offset)) >> bit_offset;
+ otp_val |= (otp_mem[++word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 33, 0)) <<
+ (32 - bit_offset);
+ bit_offset += otp_map[i].size - 32;
+ } else if (bit_offset + otp_map[i].size - 1 >= 0) {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 1, bit_offset)
+ ) >> bit_offset;
+ bit_offset += otp_map[i].size;
+ } else /* both bit_offset and otp_map[i].size are 0 */
+ otp_val = 0;
+
+ bit_sum += otp_map[i].size;
+
+ if (bit_offset == 32) {
+ bit_offset = 0;
+ word_offset++;
+ }
+
+ if (otp_map[i].reg != 0) {
+ ret = regmap_update_bits(regmap, otp_map[i].reg,
+ GENMASK(otp_map[i].shift + otp_map[i].size - 1,
+ otp_map[i].shift),
+ otp_val << otp_map[i].shift);
+ if (ret < 0) {
+ dev_err(dev, "Write OTP val failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ }
+ }
+
+ ret = 0;
+
+err_otp_unpack:
+ kfree(otp_mem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_otp_unpack);
+
+/* Must be called with the TEST_KEY unlocked */
+int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid)
+{
+ char *rev;
+ int ret;
+
+ switch (reg_revid) {
+ case CS35L41_REVID_A0:
+ ret = regmap_register_patch(reg, cs35l41_reva0_errata_patch,
+ ARRAY_SIZE(cs35l41_reva0_errata_patch));
+ rev = "A0";
+ break;
+ case CS35L41_REVID_B0:
+ ret = regmap_register_patch(reg, cs35l41_revb0_errata_patch,
+ ARRAY_SIZE(cs35l41_revb0_errata_patch));
+ rev = "B0";
+ break;
+ case CS35L41_REVID_B2:
+ ret = regmap_register_patch(reg, cs35l41_revb2_errata_patch,
+ ARRAY_SIZE(cs35l41_revb2_errata_patch));
+ rev = "B2";
+ break;
+ default:
+ ret = -EINVAL;
+ rev = "XX";
+ break;
+ }
+
+ if (ret)
+ dev_err(dev, "Failed to apply %s errata patch: %d\n", rev, ret);
+
+ ret = regmap_write(reg, CS35L41_DSP1_CCM_CORE_CTRL, 0);
+ if (ret < 0)
+ dev_err(dev, "Write CCM_CORE_CTRL failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch);
+
+int cs35l41_set_channels(struct device *dev, struct regmap *reg,
+ unsigned int tx_num, const unsigned int *tx_slot,
+ unsigned int rx_num, const unsigned int *rx_slot)
+{
+ unsigned int val, mask;
+ int i;
+
+ if (tx_num > 4 || rx_num > 2)
+ return -EINVAL;
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < rx_num; i++) {
+ dev_dbg(dev, "rx slot %d position = %d\n", i, rx_slot[i]);
+ val |= rx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(reg, CS35L41_SP_FRAME_RX_SLOT, mask, val);
+
+ val = 0;
+ mask = 0;
+ for (i = 0; i < tx_num; i++) {
+ dev_dbg(dev, "tx slot %d position = %d\n", i, tx_slot[i]);
+ val |= tx_slot[i] << (i * 8);
+ mask |= 0x3F << (i * 8);
+ }
+ regmap_update_bits(reg, CS35L41_SP_FRAME_TX_SLOT, mask, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_set_channels);
+
+static const unsigned char cs35l41_bst_k1_table[4][5] = {
+ { 0x24, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x24, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x40, 0x32, 0x32, 0x4F, 0x57 },
+ { 0x40, 0x32, 0x32, 0x4F, 0x57 }
+};
+
+static const unsigned char cs35l41_bst_k2_table[4][5] = {
+ { 0x24, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x24, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x48, 0x49, 0x66, 0xA3, 0xEA },
+ { 0x48, 0x49, 0x66, 0xA3, 0xEA }
+};
+
+static const unsigned char cs35l41_bst_slope_table[4] = {
+ 0x75, 0x6B, 0x3B, 0x28
+};
+
+static int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind,
+ int boost_cap, int boost_ipk)
+{
+ unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled;
+ int ret;
+
+ switch (boost_ind) {
+ case 1000: /* 1.0 uH */
+ bst_lbst_val = 0;
+ break;
+ case 1200: /* 1.2 uH */
+ bst_lbst_val = 1;
+ break;
+ case 1500: /* 1.5 uH */
+ bst_lbst_val = 2;
+ break;
+ case 2200: /* 2.2 uH */
+ bst_lbst_val = 3;
+ break;
+ default:
+ dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind);
+ return -EINVAL;
+ }
+
+ switch (boost_cap) {
+ case 0 ... 19:
+ bst_cbst_range = 0;
+ break;
+ case 20 ... 50:
+ bst_cbst_range = 1;
+ break;
+ case 51 ... 100:
+ bst_cbst_range = 2;
+ break;
+ case 101 ... 200:
+ bst_cbst_range = 3;
+ break;
+ default:
+ if (boost_cap < 0) {
+ dev_err(dev, "Invalid boost capacitor value: %d nH\n", boost_cap);
+ return -EINVAL;
+ }
+ /* 201 uF and greater */
+ bst_cbst_range = 4;
+ }
+
+ if (boost_ipk < 1600 || boost_ipk > 4500) {
+ dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF,
+ CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK,
+ cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range]
+ << CS35L41_BST_K1_SHIFT |
+ cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range]
+ << CS35L41_BST_K2_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost coefficients: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST,
+ CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK,
+ cs35l41_bst_slope_table[bst_lbst_val]
+ << CS35L41_BST_SLOPE_SHIFT |
+ bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret);
+ return ret;
+ }
+
+ bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10;
+
+ ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, CS35L41_BST_IPK_MASK,
+ bst_ipk_scaled << CS35L41_BST_IPK_SHIFT);
+ if (ret) {
+ dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+
+ return 0;
+}
+
+static const struct reg_sequence cs35l41_safe_to_reset[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000393C, 0x000000C0, 6000},
+ { 0x0000393C, 0x00000000 },
+ { 0x00007414, 0x00C82222 },
+ { 0x0000742C, 0x00000000 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe_start[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { 0x0000742C, 0x00000009 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe_end[] = {
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_safe_to_active_start[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000742C, 0x0000000F },
+ { 0x0000742C, 0x00000079 },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000001 }, // GLOBAL_EN = 1
+};
+
+static const struct reg_sequence cs35l41_safe_to_active_en_spk[] = {
+ { 0x0000742C, 0x000000F9 },
+ { 0x00007438, 0x00580941 },
+};
+
+static const struct reg_sequence cs35l41_reset_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { 0x00007414, 0x08C82222 },
+ { 0x0000742C, 0x00000009 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_actv_seq[] = {
+ /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */
+ {CS35L41_MDSYNC_EN, 0x00003000},
+ /* BST_CTL_SEL = MDSYNC */
+ {CS35L41_BSTCVRT_VCTRL2, 0x00000002},
+};
+
+static const struct reg_sequence cs35l41_pass_seq[] = {
+ /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */
+ {CS35L41_MDSYNC_EN, 0x00001000},
+ /* BST_EN = 0 */
+ {CS35L41_PWR_CTRL2, 0x00003300},
+ /* BST_CTL_SEL = MDSYNC */
+ {CS35L41_BSTCVRT_VCTRL2, 0x00000002},
+};
+
+int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
+ struct cs35l41_hw_cfg *hw_cfg)
+{
+ int ret;
+
+ switch (hw_cfg->bst_type) {
+ case CS35L41_SHD_BOOST_ACTV:
+ regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq));
+ fallthrough;
+ case CS35L41_INT_BOOST:
+ ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind,
+ hw_cfg->bst_cap, hw_cfg->bst_ipk);
+ if (ret)
+ dev_err(dev, "Error in Boost DT config: %d\n", ret);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ /* Only CLSA0100 doesn't use GPIO as VSPK switch, but even on that laptop we can
+ * toggle GPIO1 as is not connected to anything.
+ * There will be no other device without VSPK switch.
+ */
+ regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_multi_reg_write(regmap, cs35l41_reset_to_safe,
+ ARRAY_SIZE(cs35l41_reset_to_safe));
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT);
+ break;
+ case CS35L41_SHD_BOOST_PASS:
+ ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq,
+ ARRAY_SIZE(cs35l41_pass_seq));
+ break;
+ default:
+ dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_init_boost);
+
+bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
+{
+ switch (b_type) {
+ /* There is only one laptop that doesn't have VSPK switch. */
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ return false;
+ case CS35L41_EXT_BOOST:
+ regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_multi_reg_write(regmap, cs35l41_safe_to_reset,
+ ARRAY_SIZE(cs35l41_safe_to_reset));
+ return true;
+ default:
+ return true;
+ }
+}
+EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
+
+/*
+ * Enabling the CS35L41_SHD_BOOST_ACTV and CS35L41_SHD_BOOST_PASS shared boosts
+ * does also require a call to cs35l41_mdsync_up(), but not before getting the
+ * PLL Lock signal.
+ *
+ * PLL Lock seems to be triggered soon after snd_pcm_start() is executed and
+ * SNDRV_PCM_TRIGGER_START command is processed, which happens (long) after the
+ * SND_SOC_DAPM_PRE_PMU event handler is invoked as part of snd_pcm_prepare().
+ *
+ * This event handler is where cs35l41_global_enable() is normally called from,
+ * but waiting for PLL Lock here will time out. Increasing the wait duration
+ * will not help, as the only consequence of it would be to add an unnecessary
+ * delay in the invocation of snd_pcm_start().
+ *
+ * Trying to move the wait in the SNDRV_PCM_TRIGGER_START callback is not a
+ * solution either, as the trigger is executed in an IRQ-off atomic context.
+ *
+ * The current approach is to invoke cs35l41_mdsync_up() right after receiving
+ * the PLL Lock interrupt, in the IRQ handler.
+ */
+int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
+ int enable, struct cs_dsp *dsp)
+{
+ int ret;
+ unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status, pup_pdn_mask;
+ unsigned int pwr_ctl1_val;
+ struct reg_sequence cs35l41_mdsync_down_seq[] = {
+ {CS35L41_PWR_CTRL3, 0},
+ {CS35L41_GPIO_PAD_CONTROL, 0},
+ {CS35L41_PWR_CTRL1, 0, 3000},
+ };
+
+ pup_pdn_mask = enable ? CS35L41_PUP_DONE_MASK : CS35L41_PDN_DONE_MASK;
+
+ ret = regmap_read(regmap, CS35L41_PWR_CTRL1, &pwr_ctl1_val);
+ if (ret)
+ return ret;
+
+ if ((pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && enable) {
+ dev_dbg(dev, "Cannot set Global Enable - already set.\n");
+ return 0;
+ } else if (!(pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && !enable) {
+ dev_dbg(dev, "Cannot unset Global Enable - not set.\n");
+ return 0;
+ }
+
+ switch (b_type) {
+ case CS35L41_SHD_BOOST_ACTV:
+ case CS35L41_SHD_BOOST_PASS:
+ regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+ regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control);
+
+ pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK;
+ pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT;
+
+ gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ;
+ gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT;
+
+ pad_control &= ~CS35L41_GPIO1_CTRL_MASK;
+ pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK;
+
+ cs35l41_mdsync_down_seq[0].def = pwr_ctrl3;
+ cs35l41_mdsync_down_seq[1].def = pad_control;
+ cs35l41_mdsync_down_seq[2].def = pwr_ctrl1;
+
+ ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq,
+ ARRAY_SIZE(cs35l41_mdsync_down_seq));
+ /* Activation to be completed later via cs35l41_mdsync_up() */
+ if (ret || enable)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
+ int_status, int_status & pup_pdn_mask,
+ 1000, 100000);
+ if (ret)
+ dev_err(dev, "Enable(%d) failed: %d\n", enable, ret);
+
+ /* Clear PUP/PDN status */
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask);
+ break;
+ case CS35L41_INT_BOOST:
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
+ enable << CS35L41_GLOBAL_EN_SHIFT);
+ if (ret) {
+ dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
+ int_status, int_status & pup_pdn_mask,
+ 1000, 100000);
+ if (ret)
+ dev_err(dev, "Enable(%d) failed: %d\n", enable, ret);
+
+ /* Clear PUP/PDN status */
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ if (enable) {
+ /* Test Key is unlocked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start,
+ ARRAY_SIZE(cs35l41_safe_to_active_start));
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status,
+ int_status & CS35L41_PUP_DONE_MASK, 1000, 100000);
+ if (ret) {
+ dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK: %d\n", ret);
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK);
+
+ if (dsp->running && dsp->fw_id_version > CS35L41_FIRMWARE_OLD_VERSION)
+ ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
+ CSPL_MBOX_CMD_SPK_OUT_ENABLE);
+ else
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk,
+ ARRAY_SIZE(cs35l41_safe_to_active_en_spk));
+
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ } else {
+ /* Test Key is unlocked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_start,
+ ARRAY_SIZE(cs35l41_active_to_safe_start));
+ if (ret) {
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status,
+ int_status & CS35L41_PDN_DONE_MASK, 1000, 100000);
+ if (ret) {
+ dev_err(dev, "Failed waiting for CS35L41_PDN_DONE_MASK: %d\n", ret);
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PDN_DONE_MASK);
+
+ /* Test Key is locked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end,
+ ARRAY_SIZE(cs35l41_active_to_safe_end));
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_global_enable);
+
+/*
+ * To be called after receiving the IRQ Lock interrupt, in order to complete
+ * any shared boost activation initiated by cs35l41_global_enable().
+ */
+int cs35l41_mdsync_up(struct regmap *regmap)
+{
+ return regmap_update_bits(regmap, CS35L41_PWR_CTRL3,
+ CS35L41_SYNC_EN_MASK, CS35L41_SYNC_EN_MASK);
+}
+EXPORT_SYMBOL_GPL(cs35l41_mdsync_up);
+
+int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg)
+{
+ struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1;
+ struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2;
+ int irq_pol = IRQF_TRIGGER_NONE;
+
+ regmap_update_bits(regmap, CS35L41_GPIO1_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ gpio1->pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !gpio1->out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ regmap_update_bits(regmap, CS35L41_GPIO2_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ gpio2->pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !gpio2->out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ if (gpio1->valid)
+ regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO1_CTRL_MASK,
+ gpio1->func << CS35L41_GPIO1_CTRL_SHIFT);
+
+ if (gpio2->valid) {
+ regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO2_CTRL_MASK,
+ gpio2->func << CS35L41_GPIO2_CTRL_SHIFT);
+
+ switch (gpio2->func) {
+ case CS35L41_GPIO2_INT_PUSH_PULL_LOW:
+ case CS35L41_GPIO2_INT_OPEN_DRAIN:
+ irq_pol = IRQF_TRIGGER_LOW;
+ break;
+ case CS35L41_GPIO2_INT_PUSH_PULL_HIGH:
+ irq_pol = IRQF_TRIGGER_HIGH;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return irq_pol;
+}
+EXPORT_SYMBOL_GPL(cs35l41_gpio_config);
+
+static const struct cs_dsp_region cs35l41_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0},
+};
+
+void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_dsp *dsp)
+{
+ dsp->num = 1;
+ dsp->type = WMFW_HALO;
+ dsp->rev = 0;
+ dsp->dev = dev;
+ dsp->regmap = reg;
+ dsp->base = CS35L41_DSP1_CTRL_BASE;
+ dsp->base_sysinfo = CS35L41_DSP1_SYS_ID;
+ dsp->mem = cs35l41_dsp1_regions;
+ dsp->num_mems = ARRAY_SIZE(cs35l41_dsp1_regions);
+ dsp->lock_regions = 0xFFFFFFFF;
+}
+EXPORT_SYMBOL_GPL(cs35l41_configure_cs_dsp);
+
+static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
+ enum cs35l41_cspl_mbox_status sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ case CSPL_MBOX_CMD_SPK_OUT_ENABLE:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ default:
+ return false;
+ }
+}
+
+int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap,
+ enum cs35l41_cspl_mbox_cmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ // Set mailbox cmd
+ ret = regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(regmap, CS35L41_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (sts == CSPL_MBOX_STS_ERROR || sts == CSPL_MBOX_STS_ERROR2) {
+ dev_err(dev, "CSPL Error Detected\n");
+ return -EINVAL;
+ }
+
+ if (!cs35l41_check_cspl_mbox_sts(cmd, sts))
+ dev_dbg(dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+ else
+ return 0;
+ }
+
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+ return -ENOMSG;
+}
+EXPORT_SYMBOL_GPL(cs35l41_set_cspl_mbox_cmd);
+
+int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, cs35l41_fs_errata_patch,
+ ARRAY_SIZE(cs35l41_fs_errata_patch));
+ if (ret < 0)
+ dev_err(dev, "Failed to write fs errata: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_write_fs_errata);
+
+int cs35l41_enter_hibernate(struct device *dev, struct regmap *regmap,
+ enum cs35l41_boost_type b_type)
+{
+ if (!cs35l41_safe_reset(regmap, b_type)) {
+ dev_dbg(dev, "System does not support Suspend\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Enter hibernate\n");
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088);
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_enter_hibernate);
+
+static void cs35l41_wait_for_pwrmgt_sts(struct device *dev, struct regmap *regmap)
+{
+ const int pwrmgt_retries = 10;
+ unsigned int sts;
+ int i, ret;
+
+ for (i = 0; i < pwrmgt_retries; i++) {
+ ret = regmap_read(regmap, CS35L41_PWRMGT_STS, &sts);
+ if (ret)
+ dev_err(dev, "Failed to read PWRMGT_STS: %d\n", ret);
+ else if (!(sts & CS35L41_WR_PEND_STS_MASK))
+ return;
+
+ udelay(20);
+ }
+
+ dev_err(dev, "Timed out reading PWRMGT_STS\n");
+}
+
+int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret)
+ break;
+
+ usleep_range(100, 200);
+ }
+
+ if (j < wake_retries) {
+ dev_dbg(dev, "Wake success at cycle: %d\n", j);
+ return 0;
+ }
+
+ dev_err(dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l41_wait_for_pwrmgt_sts(dev, regmap);
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088);
+
+ cs35l41_wait_for_pwrmgt_sts(dev, regmap);
+ regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+ cs35l41_wait_for_pwrmgt_sts(dev, regmap);
+ regmap_write(regmap, CS35L41_PWRMGT_CTL, 0x3);
+ }
+
+ dev_err(dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(cs35l41_exit_hibernate);
+
+MODULE_DESCRIPTION("CS35L41 library");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
new file mode 100644
index 000000000000..f9b6bf7bea9c
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-spi.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-spi.c -- CS35l41 SPI driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l41.h"
+
+static const struct spi_device_id cs35l41_id_spi[] = {
+ { "cs35l40", 0 },
+ { "cs35l41", 0 },
+ { "cs35l51", 0 },
+ { "cs35l53", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, cs35l41_id_spi);
+
+static int cs35l41_spi_probe(struct spi_device *spi)
+{
+ const struct regmap_config *regmap_config = &cs35l41_regmap_spi;
+ struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(&spi->dev);
+ struct cs35l41_private *cs35l41;
+ int ret;
+
+ cs35l41 = devm_kzalloc(&spi->dev, sizeof(struct cs35l41_private), GFP_KERNEL);
+ if (!cs35l41)
+ return -ENOMEM;
+
+ spi->max_speed_hz = CS35L41_SPI_MAX_FREQ;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ spi_set_drvdata(spi, cs35l41);
+ cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l41->regmap))
+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
+ "Failed to allocate register map\n");
+
+ cs35l41->dev = &spi->dev;
+ cs35l41->irq = spi->irq;
+
+ return cs35l41_probe(cs35l41, hw_cfg);
+}
+
+static void cs35l41_spi_remove(struct spi_device *spi)
+{
+ struct cs35l41_private *cs35l41 = spi_get_drvdata(spi);
+
+ cs35l41_remove(cs35l41);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cs35l41_of_match[] = {
+ { .compatible = "cirrus,cs35l40" },
+ { .compatible = "cirrus,cs35l41" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l41_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l41_acpi_match[] = {
+ { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ { "CLSA3541", 0 }, /* Cirrus Logic PnP ID + part ID */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
+#endif
+
+static struct spi_driver cs35l41_spi_driver = {
+ .driver = {
+ .name = "cs35l41",
+ .pm = pm_ptr(&cs35l41_pm_ops),
+ .of_match_table = of_match_ptr(cs35l41_of_match),
+ .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
+ },
+ .id_table = cs35l41_id_spi,
+ .probe = cs35l41_spi_probe,
+ .remove = cs35l41_spi_remove,
+};
+
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
new file mode 100644
index 000000000000..3a8a8dd065b7
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.c
@@ -0,0 +1,1492 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 ALSA SoC audio driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs35l41.h"
+
+static const char * const cs35l41_supplies[CS35L41_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l41_pll_sysclk_config {
+ int freq;
+ int clk_cfg;
+};
+
+static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = {
+ { 32768, 0x00 },
+ { 8000, 0x01 },
+ { 11025, 0x02 },
+ { 12000, 0x03 },
+ { 16000, 0x04 },
+ { 22050, 0x05 },
+ { 24000, 0x06 },
+ { 32000, 0x07 },
+ { 44100, 0x08 },
+ { 48000, 0x09 },
+ { 88200, 0x0A },
+ { 96000, 0x0B },
+ { 128000, 0x0C },
+ { 176400, 0x0D },
+ { 192000, 0x0E },
+ { 256000, 0x0F },
+ { 352800, 0x10 },
+ { 384000, 0x11 },
+ { 512000, 0x12 },
+ { 705600, 0x13 },
+ { 750000, 0x14 },
+ { 768000, 0x15 },
+ { 1000000, 0x16 },
+ { 1024000, 0x17 },
+ { 1200000, 0x18 },
+ { 1411200, 0x19 },
+ { 1500000, 0x1A },
+ { 1536000, 0x1B },
+ { 2000000, 0x1C },
+ { 2048000, 0x1D },
+ { 2400000, 0x1E },
+ { 2822400, 0x1F },
+ { 3000000, 0x20 },
+ { 3072000, 0x21 },
+ { 3200000, 0x22 },
+ { 4000000, 0x23 },
+ { 4096000, 0x24 },
+ { 4800000, 0x25 },
+ { 5644800, 0x26 },
+ { 6000000, 0x27 },
+ { 6144000, 0x28 },
+ { 6250000, 0x29 },
+ { 6400000, 0x2A },
+ { 6500000, 0x2B },
+ { 6750000, 0x2C },
+ { 7526400, 0x2D },
+ { 8000000, 0x2E },
+ { 8192000, 0x2F },
+ { 9600000, 0x30 },
+ { 11289600, 0x31 },
+ { 12000000, 0x32 },
+ { 12288000, 0x33 },
+ { 12500000, 0x34 },
+ { 12800000, 0x35 },
+ { 13000000, 0x36 },
+ { 13500000, 0x37 },
+ { 19200000, 0x38 },
+ { 22579200, 0x39 },
+ { 24000000, 0x3A },
+ { 24576000, 0x3B },
+ { 25000000, 0x3C },
+ { 25600000, 0x3D },
+ { 26000000, 0x3E },
+ { 27000000, 0x3F },
+};
+
+struct cs35l41_fs_mon_config {
+ int freq;
+ unsigned int fs1;
+ unsigned int fs2;
+};
+
+static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
+ { 32768, 2254, 3754 },
+ { 8000, 9220, 15364 },
+ { 11025, 6148, 10244 },
+ { 12000, 6148, 10244 },
+ { 16000, 4612, 7684 },
+ { 22050, 3076, 5124 },
+ { 24000, 3076, 5124 },
+ { 32000, 2308, 3844 },
+ { 44100, 1540, 2564 },
+ { 48000, 1540, 2564 },
+ { 88200, 772, 1284 },
+ { 96000, 772, 1284 },
+ { 128000, 580, 964 },
+ { 176400, 388, 644 },
+ { 192000, 388, 644 },
+ { 256000, 292, 484 },
+ { 352800, 196, 324 },
+ { 384000, 196, 324 },
+ { 512000, 148, 244 },
+ { 705600, 100, 164 },
+ { 750000, 100, 164 },
+ { 768000, 100, 164 },
+ { 1000000, 76, 124 },
+ { 1024000, 76, 124 },
+ { 1200000, 64, 104 },
+ { 1411200, 52, 84 },
+ { 1500000, 52, 84 },
+ { 1536000, 52, 84 },
+ { 2000000, 40, 64 },
+ { 2048000, 40, 64 },
+ { 2400000, 34, 54 },
+ { 2822400, 28, 44 },
+ { 3000000, 28, 44 },
+ { 3072000, 28, 44 },
+ { 3200000, 27, 42 },
+ { 4000000, 22, 34 },
+ { 4096000, 22, 34 },
+ { 4800000, 19, 29 },
+ { 5644800, 16, 24 },
+ { 6000000, 16, 24 },
+ { 6144000, 16, 24 },
+ { 12288000, 0, 0 },
+};
+
+static int cs35l41_get_fs_mon_config_index(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_fs_mon); i++) {
+ if (cs35l41_fs_mon[i].freq == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static const DECLARE_TLV_DB_RANGE(dig_vol_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ 1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200));
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 50, 100, 0);
+
+static const struct snd_kcontrol_new dre_ctrl =
+ SOC_DAPM_SINGLE("Switch", CS35L41_PWR_CTRL3, 20, 1, 0);
+
+static const char * const cs35l41_pcm_sftramp_text[] = {
+ "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp,
+ CS35L41_AMP_DIG_VOL_CTRL, 0,
+ cs35l41_pcm_sftramp_text);
+
+static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l41->dsp.cs_dsp.booted)
+ return 0;
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ case SND_SOC_DAPM_PRE_PMD:
+ if (cs35l41->dsp.preloaded)
+ return 0;
+
+ if (cs35l41->dsp.cs_dsp.running) {
+ ret = wm_adsp_event(w, kcontrol, event);
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ default:
+ return 0;
+ }
+}
+
+static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ unsigned int fw_status;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!cs35l41->dsp.cs_dsp.running)
+ return wm_adsp_event(w, kcontrol, event);
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status);
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to read firmware status: %d\n", ret);
+ return ret;
+ }
+
+ switch (fw_status) {
+ case CSPL_MBOX_STS_RUNNING:
+ case CSPL_MBOX_STS_PAUSED:
+ break;
+ default:
+ dev_err(cs35l41->dev, "Firmware status is invalid: %u\n",
+ fw_status);
+ return -EINVAL;
+ }
+
+ return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_RESUME);
+ case SND_SOC_DAPM_PRE_PMD:
+ return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_PAUSE);
+ default:
+ return 0;
+ }
+}
+
+static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"};
+static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32};
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum,
+ CS35L41_DAC_PCM1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_pcm_source_texts,
+ cs35l41_pcm_source_values);
+
+static const struct snd_kcontrol_new pcm_source_mux =
+ SOC_DAPM_ENUM("PCM Source", cs35l41_pcm_source_enum);
+
+static const char * const cs35l41_tx_input_texts[] = {
+ "Zero", "ASPRX1", "ASPRX2", "VMON", "IMON",
+ "VPMON", "VBSTMON", "DSPTX1", "DSPTX2"
+};
+
+static const unsigned int cs35l41_tx_input_values[] = {
+ 0x00, CS35L41_INPUT_SRC_ASPRX1, CS35L41_INPUT_SRC_ASPRX2,
+ CS35L41_INPUT_SRC_VMON, CS35L41_INPUT_SRC_IMON, CS35L41_INPUT_SRC_VPMON,
+ CS35L41_INPUT_SRC_VBSTMON, CS35L41_INPUT_DSP_TX1, CS35L41_INPUT_DSP_TX2
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum,
+ CS35L41_ASP_TX1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx1_mux =
+ SOC_DAPM_ENUM("ASPTX1 SRC", cs35l41_asptx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx2_enum,
+ CS35L41_ASP_TX2_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx2_mux =
+ SOC_DAPM_ENUM("ASPTX2 SRC", cs35l41_asptx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx3_enum,
+ CS35L41_ASP_TX3_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx3_mux =
+ SOC_DAPM_ENUM("ASPTX3 SRC", cs35l41_asptx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum,
+ CS35L41_ASP_TX4_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new asp_tx4_mux =
+ SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum,
+ CS35L41_DSP1_RX1_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new dsp_rx1_mux =
+ SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum,
+ CS35L41_DSP1_RX2_SRC,
+ 0, CS35L41_ASP_SOURCE_MASK,
+ cs35l41_tx_input_texts,
+ cs35l41_tx_input_values);
+
+static const struct snd_kcontrol_new dsp_rx2_mux =
+ SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum);
+
+static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL,
+ 3, 0x4CF, 0x391, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0,
+ amp_gain_tlv),
+ SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp),
+ SOC_SINGLE("HW Noise Gate Enable", CS35L41_NG_CFG, 8, 63, 0),
+ SOC_SINGLE("HW Noise Gate Delay", CS35L41_NG_CFG, 4, 7, 0),
+ SOC_SINGLE("HW Noise Gate Threshold", CS35L41_NG_CFG, 0, 7, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Switch",
+ CS35L41_MIXER_NGATE_CH1_CFG, 16, 1, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Entry Delay",
+ CS35L41_MIXER_NGATE_CH1_CFG, 8, 15, 0),
+ SOC_SINGLE("Aux Noise Gate CH1 Threshold",
+ CS35L41_MIXER_NGATE_CH1_CFG, 0, 7, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Entry Delay",
+ CS35L41_MIXER_NGATE_CH2_CFG, 8, 15, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Switch",
+ CS35L41_MIXER_NGATE_CH2_CFG, 16, 1, 0),
+ SOC_SINGLE("Aux Noise Gate CH2 Threshold",
+ CS35L41_MIXER_NGATE_CH2_CFG, 0, 7, 0),
+ SOC_SINGLE("SCLK Force Switch", CS35L41_SP_FORMAT, CS35L41_SCLK_FRC_SHIFT, 1, 0),
+ SOC_SINGLE("LRCLK Force Switch", CS35L41_SP_FORMAT, CS35L41_LRCLK_FRC_SHIFT, 1, 0),
+ SOC_SINGLE("Invert Class D Switch", CS35L41_AMP_DIG_VOL_CTRL,
+ CS35L41_AMP_INV_PCM_SHIFT, 1, 0),
+ SOC_SINGLE("Amp Gain ZC Switch", CS35L41_AMP_GAIN_CTRL,
+ CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0),
+ WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+ WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable)
+{
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF;
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ enable << CS35L41_BST_EN_SHIFT);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit,
+ unsigned int rel_err_bit)
+{
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, irq_err_bit);
+ regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, rel_err_bit);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, 0);
+}
+
+static irqreturn_t cs35l41_irq(int irq, void *data)
+{
+ struct cs35l41_private *cs35l41 = data;
+ unsigned int status[4] = { 0, 0, 0, 0 };
+ unsigned int masks[4] = { 0, 0, 0, 0 };
+ unsigned int i;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(cs35l41->dev);
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "pm_runtime_resume_and_get failed in %s: %d\n",
+ __func__, ret);
+ return IRQ_NONE;
+ }
+
+ ret = IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(status); i++) {
+ regmap_read(cs35l41->regmap,
+ CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE),
+ &status[i]);
+ regmap_read(cs35l41->regmap,
+ CS35L41_IRQ1_MASK1 + (i * CS35L41_REGSTRIDE),
+ &masks[i]);
+ }
+
+ /* Check to see if unmasked bits are active */
+ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
+ !(status[2] & ~masks[2]) && !(status[3] & ~masks[3]))
+ goto done;
+
+ if (status[3] & CS35L41_OTP_BOOT_DONE) {
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4,
+ CS35L41_OTP_BOOT_DONE, CS35L41_OTP_BOOT_DONE);
+ }
+
+ /*
+ * The following interrupts require a
+ * protection release cycle to get the
+ * speaker out of Safe-Mode.
+ */
+ if (status[0] & CS35L41_AMP_SHORT_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
+ cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_TEMP_WARN) {
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_TEMP_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_BST_OVP_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_BST_DCM_UVP_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[0] & CS35L41_BST_SHORT_ERR) {
+ dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n");
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[2] & CS35L41_PLL_LOCK) {
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK);
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS) {
+ ret = cs35l41_mdsync_up(cs35l41->regmap);
+ if (ret)
+ dev_err(cs35l41->dev, "MDSYNC-up failed: %d\n", ret);
+ else
+ dev_dbg(cs35l41->dev, "MDSYNC-up done\n");
+
+ dev_dbg(cs35l41->dev, "PUP-done status: %d\n",
+ !!(status[0] & CS35L41_PUP_DONE_MASK));
+ }
+
+ ret = IRQ_HANDLED;
+ }
+
+done:
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
+ return ret;
+}
+
+static const struct reg_sequence cs35l41_pup_patch[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ { 0x00002084, 0x002F1AA0 },
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_pdn_patch[] = {
+ { CS35L41_TEST_KEY_CTL, 0x00000055 },
+ { CS35L41_TEST_KEY_CTL, 0x000000AA },
+ { 0x00002084, 0x002F1AA3 },
+ { CS35L41_TEST_KEY_CTL, 0x000000CC },
+ { CS35L41_TEST_KEY_CTL, 0x00000033 },
+};
+
+static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_multi_reg_write_bypassed(cs35l41->regmap,
+ cs35l41_pup_patch,
+ ARRAY_SIZE(cs35l41_pup_patch));
+
+ ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
+ 1, &cs35l41->dsp.cs_dsp);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
+ 0, &cs35l41->dsp.cs_dsp);
+
+ regmap_multi_reg_write_bypassed(cs35l41->regmap,
+ cs35l41_pdn_patch,
+ ARRAY_SIZE(cs35l41_pdn_patch));
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+ SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+ cs35l41_dsp_preload_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ cs35l41_dsp_audio_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, CS35L41_SP_ENABLES, 17, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L41_SP_ENABLES, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 0, CS35L41_SP_ENABLES, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0),
+
+ SND_SOC_DAPM_SIGGEN("VSENSE"),
+ SND_SOC_DAPM_SIGGEN("ISENSE"),
+ SND_SOC_DAPM_SIGGEN("VP"),
+ SND_SOC_DAPM_SIGGEN("VBST"),
+ SND_SOC_DAPM_SIGGEN("TEMP"),
+
+ SND_SOC_DAPM_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0,
+ cs35l41_main_amp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux),
+ SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux),
+ SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux),
+ SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux),
+ SND_SOC_DAPM_MUX("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux),
+ SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux),
+ SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux),
+ SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl),
+};
+
+static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
+ {"DSP RX1 Source", "ASPRX1", "ASPRX1"},
+ {"DSP RX1 Source", "ASPRX2", "ASPRX2"},
+ {"DSP RX2 Source", "ASPRX1", "ASPRX1"},
+ {"DSP RX2 Source", "ASPRX2", "ASPRX2"},
+
+ {"DSP1", NULL, "DSP RX1 Source"},
+ {"DSP1", NULL, "DSP RX2 Source"},
+
+ {"ASP TX1 Source", "VMON", "VMON ADC"},
+ {"ASP TX1 Source", "IMON", "IMON ADC"},
+ {"ASP TX1 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX1 Source", "DSPTX1", "DSP1"},
+ {"ASP TX1 Source", "DSPTX2", "DSP1"},
+ {"ASP TX1 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX1 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX2 Source", "VMON", "VMON ADC"},
+ {"ASP TX2 Source", "IMON", "IMON ADC"},
+ {"ASP TX2 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX2 Source", "DSPTX1", "DSP1"},
+ {"ASP TX2 Source", "DSPTX2", "DSP1"},
+ {"ASP TX2 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX2 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX3 Source", "VMON", "VMON ADC"},
+ {"ASP TX3 Source", "IMON", "IMON ADC"},
+ {"ASP TX3 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX3 Source", "DSPTX1", "DSP1"},
+ {"ASP TX3 Source", "DSPTX2", "DSP1"},
+ {"ASP TX3 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX3 Source", "ASPRX2", "ASPRX2" },
+ {"ASP TX4 Source", "VMON", "VMON ADC"},
+ {"ASP TX4 Source", "IMON", "IMON ADC"},
+ {"ASP TX4 Source", "VPMON", "VPMON ADC"},
+ {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"},
+ {"ASP TX4 Source", "DSPTX1", "DSP1"},
+ {"ASP TX4 Source", "DSPTX2", "DSP1"},
+ {"ASP TX4 Source", "ASPRX1", "ASPRX1" },
+ {"ASP TX4 Source", "ASPRX2", "ASPRX2" },
+ {"ASPTX1", NULL, "ASP TX1 Source"},
+ {"ASPTX2", NULL, "ASP TX2 Source"},
+ {"ASPTX3", NULL, "ASP TX3 Source"},
+ {"ASPTX4", NULL, "ASP TX4 Source"},
+ {"AMP Capture", NULL, "ASPTX1"},
+ {"AMP Capture", NULL, "ASPTX2"},
+ {"AMP Capture", NULL, "ASPTX3"},
+ {"AMP Capture", NULL, "ASPTX4"},
+
+ {"DSP1", NULL, "VMON"},
+ {"DSP1", NULL, "IMON"},
+ {"DSP1", NULL, "VPMON"},
+ {"DSP1", NULL, "VBSTMON"},
+ {"DSP1", NULL, "TEMPMON"},
+
+ {"VMON ADC", NULL, "VMON"},
+ {"IMON ADC", NULL, "IMON"},
+ {"VPMON ADC", NULL, "VPMON"},
+ {"VBSTMON ADC", NULL, "VBSTMON"},
+ {"TEMPMON ADC", NULL, "TEMPMON"},
+
+ {"VMON ADC", NULL, "VSENSE"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"TEMPMON ADC", NULL, "TEMP"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
+ {"ASPRX1", NULL, "AMP Playback"},
+ {"ASPRX2", NULL, "AMP Playback"},
+ {"DRE", "Switch", "CLASS H"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"Main AMP", NULL, "DRE"},
+ {"SPK", NULL, "Main AMP"},
+
+ {"PCM Source", "ASP", "ASPRX1"},
+ {"PCM Source", "DSP", "DSP1"},
+ {"CLASS H", NULL, "PCM Source"},
+};
+
+static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n,
+ const unsigned int *tx_slot,
+ unsigned int rx_n, const unsigned int *rx_slot)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_n, tx_slot, rx_n, rx_slot);
+}
+
+static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int daifmt = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ daifmt |= CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Mixed provider/consumer mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ daifmt |= 2 << CS35L41_ASP_FMT_SHIFT;
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Invalid or unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ daifmt |= CS35L41_LRCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ daifmt |= CS35L41_SCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ daifmt |= CS35L41_LRCLK_INV_MASK | CS35L41_SCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_warn(cs35l41->dev, "Invalid DAI clock INV\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK |
+ CS35L41_ASP_FMT_MASK | CS35L41_LRCLK_INV_MASK |
+ CS35L41_SCLK_INV_MASK, daifmt);
+}
+
+struct cs35l41_global_fs_config {
+ int rate;
+ int fs_cfg;
+};
+
+static const struct cs35l41_global_fs_config cs35l41_fs_rates[] = {
+ { 12000, 0x01 },
+ { 24000, 0x02 },
+ { 48000, 0x03 },
+ { 96000, 0x04 },
+ { 192000, 0x05 },
+ { 11025, 0x09 },
+ { 22050, 0x0A },
+ { 44100, 0x0B },
+ { 88200, 0x0C },
+ { 176400, 0x0D },
+ { 8000, 0x11 },
+ { 16000, 0x12 },
+ { 32000, 0x13 },
+};
+
+static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate = params_rate(params);
+ u8 asp_wl;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) {
+ if (rate == cs35l41_fs_rates[i].rate)
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(cs35l41_fs_rates)) {
+ dev_err(cs35l41->dev, "Unsupported rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ asp_wl = params_width(params);
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL,
+ CS35L41_GLOBAL_FS_MASK,
+ cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_ASP_WIDTH_RX_MASK,
+ asp_wl << CS35L41_ASP_WIDTH_RX_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_RX_WL,
+ CS35L41_ASP_RX_WL_MASK,
+ asp_wl << CS35L41_ASP_RX_WL_SHIFT);
+ } else {
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT,
+ CS35L41_ASP_WIDTH_TX_MASK,
+ asp_wl << CS35L41_ASP_WIDTH_TX_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_TX_WL,
+ CS35L41_ASP_TX_WL_MASK,
+ asp_wl << CS35L41_ASP_TX_WL_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l41_get_clk_config(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_pll_sysclk); i++) {
+ if (cs35l41_pll_sysclk[i].freq == freq)
+ return cs35l41_pll_sysclk[i].clk_cfg;
+ }
+
+ return -EINVAL;
+}
+
+static int cs35l41_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ int extclk_cfg, clksrc;
+
+ switch (clk_id) {
+ case CS35L41_CLKID_SCLK:
+ clksrc = CS35L41_PLLSRC_SCLK;
+ break;
+ case CS35L41_CLKID_LRCLK:
+ clksrc = CS35L41_PLLSRC_LRCLK;
+ break;
+ case CS35L41_CLKID_MCLK:
+ clksrc = CS35L41_PLLSRC_MCLK;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid CLK Config\n");
+ return -EINVAL;
+ }
+
+ extclk_cfg = cs35l41_get_clk_config(freq);
+
+ if (extclk_cfg < 0) {
+ dev_err(cs35l41->dev, "Invalid CLK Config: %d, freq: %u\n",
+ extclk_cfg, freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_OPENLOOP_MASK,
+ 1 << CS35L41_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_REFCLK_FREQ_MASK,
+ extclk_cfg << CS35L41_REFCLK_FREQ_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_EN_MASK,
+ 0 << CS35L41_PLL_CLK_EN_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_SEL_MASK, clksrc);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_OPENLOOP_MASK,
+ 0 << CS35L41_PLL_OPENLOOP_SHIFT);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL,
+ CS35L41_PLL_CLK_EN_MASK,
+ 1 << CS35L41_PLL_CLK_EN_SHIFT);
+
+ return 0;
+}
+
+static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int fs1_val;
+ unsigned int fs2_val;
+ unsigned int val;
+ int fsindex;
+
+ fsindex = cs35l41_get_fs_mon_config_index(freq);
+ if (fsindex < 0) {
+ dev_err(cs35l41->dev, "Invalid CLK Config freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(cs35l41->dev, "Set DAI sysclk %d\n", freq);
+
+ if (freq <= 6144000) {
+ /* Use the lookup table */
+ fs1_val = cs35l41_fs_mon[fsindex].fs1;
+ fs2_val = cs35l41_fs_mon[fsindex].fs2;
+ } else {
+ /* Use hard-coded values */
+ fs1_val = 0x10;
+ fs2_val = 0x24;
+ }
+
+ val = fs1_val;
+ val |= (fs2_val << CS35L41_FS2_WINDOW_SHIFT) & CS35L41_FS2_WINDOW_MASK;
+ regmap_write(cs35l41->regmap, CS35L41_TST_FS_MON0, val);
+
+ return 0;
+}
+
+static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ int ret;
+
+ if (!hw_cfg->valid)
+ return -EINVAL;
+
+ if (hw_cfg->bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
+ return -EINVAL;
+
+ /* Required */
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
+
+ /* Optional */
+ if (hw_cfg->dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK && hw_cfg->dout_hiz >= 0)
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL, CS35L41_ASP_DOUT_HIZ_MASK,
+ hw_cfg->dout_hiz);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route cs35l41_ext_bst_routes[] = {
+ {"Main AMP", NULL, "VSPK"},
+};
+
+static const struct snd_soc_dapm_widget cs35l41_ext_bst_widget[] = {
+ SND_SOC_DAPM_SUPPLY("VSPK", CS35L41_GPIO1_CTRL1, CS35L41_GPIO_LVL_SHIFT, 0, NULL, 0),
+};
+
+static int cs35l41_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) {
+ ret = snd_soc_dapm_new_controls(dapm, cs35l41_ext_bst_widget,
+ ARRAY_SIZE(cs35l41_ext_bst_widget));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, cs35l41_ext_bst_routes,
+ ARRAY_SIZE(cs35l41_ext_bst_routes));
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp2_component_probe(&cs35l41->dsp, component);
+}
+
+static void cs35l41_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&cs35l41->dsp, component);
+}
+
+static const struct snd_soc_dai_ops cs35l41_ops = {
+ .set_fmt = cs35l41_set_dai_fmt,
+ .hw_params = cs35l41_pcm_hw_params,
+ .set_sysclk = cs35l41_dai_set_sysclk,
+ .set_channel_map = cs35l41_set_channel_map,
+};
+
+#define CS35L41_RATES ( \
+ SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver cs35l41_dai[] = {
+ {
+ .name = "cs35l41-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L41_RATES,
+ .formats = CS35L41_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L41_RATES,
+ .formats = CS35L41_TX_FORMATS,
+ },
+ .ops = &cs35l41_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l41 = {
+ .name = "cs35l41-codec",
+ .probe = cs35l41_component_probe,
+ .remove = cs35l41_component_remove,
+
+ .dapm_widgets = cs35l41_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets),
+ .dapm_routes = cs35l41_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l41_audio_map),
+
+ .controls = cs35l41_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l41_aud_controls),
+ .set_sysclk = cs35l41_component_set_sysclk,
+
+ .endianness = 1,
+};
+
+static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg)
+{
+ struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1;
+ struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2;
+ unsigned int val;
+ int ret;
+
+ /* Some ACPI systems received the Shared Boost feature before the upstream driver,
+ * leaving those systems with deprecated _DSD properties.
+ * To correctly configure those systems add shared-boost-active and shared-boost-passive
+ * properties mapped to the correct value in boost-type.
+ * These two are not DT properties and should not be used in new systems designs.
+ */
+ if (device_property_read_bool(dev, "cirrus,shared-boost-active")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV;
+ } else if (device_property_read_bool(dev, "cirrus,shared-boost-passive")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS;
+ } else {
+ ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
+ if (ret >= 0)
+ hw_cfg->bst_type = val;
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
+ if (ret >= 0)
+ hw_cfg->bst_ipk = val;
+ else
+ hw_cfg->bst_ipk = -1;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-ind-nanohenry", &val);
+ if (ret >= 0)
+ hw_cfg->bst_ind = val;
+ else
+ hw_cfg->bst_ind = -1;
+
+ ret = device_property_read_u32(dev, "cirrus,boost-cap-microfarad", &val);
+ if (ret >= 0)
+ hw_cfg->bst_cap = val;
+ else
+ hw_cfg->bst_cap = -1;
+
+ ret = device_property_read_u32(dev, "cirrus,asp-sdout-hiz", &val);
+ if (ret >= 0)
+ hw_cfg->dout_hiz = val;
+ else
+ hw_cfg->dout_hiz = -1;
+
+ /* GPIO1 Pin Config */
+ gpio1->pol_inv = device_property_read_bool(dev, "cirrus,gpio1-polarity-invert");
+ gpio1->out_en = device_property_read_bool(dev, "cirrus,gpio1-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio1-src-select", &val);
+ if (ret >= 0) {
+ gpio1->func = val;
+ gpio1->valid = true;
+ }
+
+ /* GPIO2 Pin Config */
+ gpio2->pol_inv = device_property_read_bool(dev, "cirrus,gpio2-polarity-invert");
+ gpio2->out_en = device_property_read_bool(dev, "cirrus,gpio2-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio2-src-select", &val);
+ if (ret >= 0) {
+ gpio2->func = val;
+ gpio2->valid = true;
+ }
+
+ hw_cfg->valid = true;
+
+ return 0;
+}
+
+static int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
+{
+ struct wm_adsp *dsp;
+ uint32_t dsp1rx5_src;
+ int ret;
+
+ dsp = &cs35l41->dsp;
+ dsp->part = "cs35l41";
+ dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+ dsp->toggle_preload = true;
+
+ cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, &dsp->cs_dsp);
+
+ ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap);
+ if (ret < 0)
+ return ret;
+
+ ret = wm_halo_init(dsp);
+ if (ret) {
+ dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret);
+ return ret;
+ }
+
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ dsp1rx5_src = CS35L41_INPUT_SRC_VPMON;
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_SHD_BOOST_PASS:
+ dsp1rx5_src = CS35L41_INPUT_SRC_VBSTMON;
+ break;
+ default:
+ dev_err(cs35l41->dev, "wm_halo_init failed - Invalid Boost Type: %d\n",
+ cs35l41->hw_cfg.bst_type);
+ goto err_dsp;
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, dsp1rx5_src);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write DSP1RX5_SRC: %d failed: %d\n", dsp1rx5_src, ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, CS35L41_INPUT_SRC_VBSTMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write CS35L41_INPUT_SRC_VBSTMON failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC,
+ CS35L41_INPUT_SRC_TEMPMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC,
+ CS35L41_INPUT_SRC_RSVD);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret);
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(dsp);
+
+ return ret;
+}
+
+static int cs35l41_get_system_name(struct cs35l41_private *cs35l41)
+{
+ struct acpi_device *adev = ACPI_COMPANION(cs35l41->dev);
+ const char *sub = NULL;
+ const char *tmp;
+ int ret = 0;
+
+ /* If there is no acpi_device, there is no ACPI for this system, skip checking ACPI */
+ if (adev) {
+ acpi_handle handle = acpi_device_handle(adev);
+
+ sub = acpi_get_subsystem_id(handle);
+ ret = PTR_ERR_OR_ZERO(sub);
+ if (ret) {
+ sub = NULL;
+ /* If no _SUB, fallback to _HID, otherwise fail */
+ if (ret == -ENODATA) {
+ tmp = acpi_device_hid(adev);
+ /* If dummy hid, return 0 and fallback to legacy firmware path */
+ if (!strcmp(tmp, "device")) {
+ ret = 0;
+ goto err;
+ }
+ sub = kstrdup(tmp, GFP_KERNEL);
+ if (!sub) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ }
+ } else {
+ if (!device_property_read_string(cs35l41->dev, "cirrus,subsystem-id", &tmp)) {
+ sub = kstrdup(tmp, GFP_KERNEL);
+ if (!sub) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ }
+
+err:
+ if (sub) {
+ cs35l41->dsp.system_name = sub;
+ dev_info(cs35l41->dev, "Subsystem ID: %s\n", cs35l41->dsp.system_name);
+ } else
+ dev_warn(cs35l41->dev, "Subsystem ID not found\n");
+
+ return ret;
+}
+
+int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg)
+{
+ u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match;
+ int irq_pol = 0;
+ int ret;
+
+ if (hw_cfg) {
+ cs35l41->hw_cfg = *hw_cfg;
+ } else {
+ ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->hw_cfg);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < CS35L41_NUM_SUPPLIES; i++)
+ cs35l41->supplies[i].supply = cs35l41_supplies[i];
+
+ ret = devm_regulator_bulk_get(cs35l41->dev, CS35L41_NUM_SUPPLIES,
+ cs35l41->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l41->dev, ret,
+ "Failed to request core supplies\n");
+
+ ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l41->dev, ret,
+ "Failed to enable core supplies\n");
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l41->reset_gpio)) {
+ ret = PTR_ERR(cs35l41->reset_gpio);
+ cs35l41->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(cs35l41->dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err_probe(cs35l41->dev, ret,
+ "Failed to get reset GPIO\n");
+ goto err;
+ }
+ }
+ if (cs35l41->reset_gpio) {
+ /* satisfy minimum reset pulse width spec */
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4,
+ int_status, int_status & CS35L41_OTP_BOOT_DONE,
+ 1000, 100000);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret,
+ "Failed waiting for OTP_BOOT_DONE\n");
+ goto err;
+ }
+
+ regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
+ if (int_status & CS35L41_OTP_BOOT_ERR) {
+ dev_err(cs35l41->dev, "OTP Boot error\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n");
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
+
+ /* CS35L41 will have even MTLREVID
+ * CS35L41R will have odd MTLREVID
+ */
+ chipid_match = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+ if (regid != chipid_match) {
+ dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n",
+ regid, chipid_match);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+
+ ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
+ goto err;
+ }
+
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, &cs35l41->hw_cfg);
+
+ /* Set interrupt masks for critical errors */
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
+ CS35L41_INT1_MASK_DEFAULT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 0 << CS35L41_INT3_PLL_LOCK_SHIFT);
+
+ ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ "cs35l41", cs35l41);
+ if (ret != 0) {
+ dev_err_probe(cs35l41->dev, ret, "Failed to request IRQ\n");
+ goto err;
+ }
+
+ ret = cs35l41_set_pdata(cs35l41);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Set pdata failed\n");
+ goto err;
+ }
+
+ ret = cs35l41_get_system_name(cs35l41);
+ if (ret < 0)
+ goto err;
+
+ ret = cs35l41_dsp_init(cs35l41);
+ if (ret < 0)
+ goto err;
+
+ pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l41->dev);
+ pm_runtime_set_active(cs35l41->dev);
+ pm_runtime_get_noresume(cs35l41->dev);
+ pm_runtime_enable(cs35l41->dev);
+
+ ret = devm_snd_soc_register_component(cs35l41->dev,
+ &soc_component_dev_cs35l41,
+ cs35l41_dai, ARRAY_SIZE(cs35l41_dai));
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Register codec failed\n");
+ goto err_pm;
+ }
+
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n",
+ regid, reg_revid);
+
+ return 0;
+
+err_pm:
+ pm_runtime_dont_use_autosuspend(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ wm_adsp2_remove(&cs35l41->dsp);
+err:
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+ regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_probe);
+
+void cs35l41_remove(struct cs35l41_private *cs35l41)
+{
+ pm_runtime_get_sync(cs35l41->dev);
+ pm_runtime_dont_use_autosuspend(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 1 << CS35L41_INT3_PLL_LOCK_SHIFT);
+ kfree(cs35l41->dsp.system_name);
+ wm_adsp2_remove(&cs35l41->dsp);
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+}
+EXPORT_SYMBOL_GPL(cs35l41_remove);
+
+static int cs35l41_runtime_suspend(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Runtime suspend\n");
+
+ if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+ return 0;
+
+ cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static int cs35l41_runtime_resume(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "Runtime resume\n");
+
+ if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+ return 0;
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ return ret;
+
+ /* Test key needs to be unlocked to allow the OTP settings to re-apply */
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ ret = regcache_sync(cs35l41->regmap);
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+ return ret;
+ }
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
+
+ return 0;
+}
+
+static int cs35l41_sys_suspend(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n");
+ disable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int cs35l41_sys_suspend_noirq(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n");
+ enable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int cs35l41_sys_resume_noirq(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n");
+ disable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+static int cs35l41_sys_resume(struct device *dev)
+{
+ struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n");
+ enable_irq(cs35l41->irq);
+
+ return 0;
+}
+
+EXPORT_GPL_DEV_PM_OPS(cs35l41_pm_ops) = {
+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+
+ SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
+};
+
+MODULE_DESCRIPTION("ASoC CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
new file mode 100644
index 000000000000..c85cbc1dd333
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * cs35l41.h -- CS35L41 ALSA SoC audio driver
+ *
+ * Copyright 2017-2021 Cirrus Logic, Inc.
+ *
+ * Author: David Rhodes <david.rhodes@cirrus.com>
+ */
+
+#ifndef __CS35L41_H__
+#define __CS35L41_H__
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/cs35l41.h>
+
+#include "wm_adsp.h"
+
+#define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+extern const struct dev_pm_ops cs35l41_pm_ops;
+
+struct cs35l41_private {
+ struct wm_adsp dsp; /* needs to be first member */
+ struct snd_soc_codec *codec;
+ struct cs35l41_hw_cfg hw_cfg;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS35L41_NUM_SUPPLIES];
+ int irq;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+};
+
+int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);
+void cs35l41_remove(struct cs35l41_private *cs35l41);
+
+#endif /*__CS35L41_H__*/
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
new file mode 100644
index 000000000000..a09aa3b92ae1
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l45-i2c.c -- CS35L45 I2C driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l45_private *cs35l45;
+ struct device *dev = &client->dev;
+ int ret;
+
+ cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+ if (!cs35l45)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs35l45);
+ cs35l45->regmap = devm_regmap_init_i2c(client, &cs35l45_i2c_regmap);
+ if (IS_ERR(cs35l45->regmap)) {
+ ret = PTR_ERR(cs35l45->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l45->dev = dev;
+ cs35l45->irq = client->irq;
+ cs35l45->bus_type = CONTROL_BUS_I2C;
+ cs35l45->i2c_addr = client->addr;
+
+ return cs35l45_probe(cs35l45);
+}
+
+static void cs35l45_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l45_private *cs35l45 = i2c_get_clientdata(client);
+
+ cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+ { .compatible = "cirrus,cs35l45" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct i2c_device_id cs35l45_id_i2c[] = {
+ { "cs35l45" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c);
+
+static struct i2c_driver cs35l45_i2c_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = pm_ptr(&cs35l45_pm_ops),
+ },
+ .id_table = cs35l45_id_i2c,
+ .probe = cs35l45_i2c_probe,
+ .remove = cs35l45_i2c_remove,
+};
+module_i2c_driver(cs35l45_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS35L45");
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
new file mode 100644
index 000000000000..5f91472c3fd2
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l45-spi.c -- CS35L45 SPI driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_spi_probe(struct spi_device *spi)
+{
+ struct cs35l45_private *cs35l45;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+ if (cs35l45 == NULL)
+ return -ENOMEM;
+
+ spi->max_speed_hz = CS35L45_SPI_MAX_FREQ;
+ spi_setup(spi);
+
+ spi_set_drvdata(spi, cs35l45);
+ cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
+ if (IS_ERR(cs35l45->regmap)) {
+ ret = PTR_ERR(cs35l45->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l45->dev = dev;
+ cs35l45->irq = spi->irq;
+ cs35l45->bus_type = CONTROL_BUS_SPI;
+
+ return cs35l45_probe(cs35l45);
+}
+
+static void cs35l45_spi_remove(struct spi_device *spi)
+{
+ struct cs35l45_private *cs35l45 = spi_get_drvdata(spi);
+
+ cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+ { .compatible = "cirrus,cs35l45" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct spi_device_id cs35l45_id_spi[] = {
+ { "cs35l45", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l45_id_spi);
+
+static struct spi_driver cs35l45_spi_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = pm_ptr(&cs35l45_pm_ops),
+ },
+ .id_table = cs35l45_id_spi,
+ .probe = cs35l45_spi_probe,
+ .remove = cs35l45_spi_remove,
+};
+module_spi_driver(cs35l45_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS35L45");
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
new file mode 100644
index 000000000000..d2ecc7b3f619
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static const struct reg_sequence cs35l45_patch[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00000044, 0x00000055 },
+ { 0x00000044, 0x000000AA },
+ { 0x00006480, 0x0830500A },
+ { 0x00007C60, 0x1000850B },
+ { CS35L45_BOOST_OV_CFG, 0x007000D0 },
+ { CS35L45_LDPM_CONFIG, 0x0001B636 },
+ { 0x00002C08, 0x00000009 },
+ { 0x00006850, 0x0A30FFC4 },
+ { 0x00003820, 0x00040100 },
+ { 0x00003824, 0x00000000 },
+ { 0x00007CFC, 0x62870004 },
+ { 0x00007C60, 0x1001850B },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000000 },
+ { CS35L45_BOOST_CCM_CFG, 0xF0000003 },
+ { CS35L45_BOOST_DCM_CFG, 0x08710220 },
+ { CS35L45_ERROR_RELEASE, 0x00200000 },
+};
+
+int cs35l45_apply_patch(struct cs35l45_private *cs35l45)
+{
+ return regmap_register_patch(cs35l45->regmap, cs35l45_patch,
+ ARRAY_SIZE(cs35l45_patch));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, "SND_SOC_CS35L45");
+
+static const struct reg_default cs35l45_defaults[] = {
+ { CS35L45_BLOCK_ENABLES, 0x00003323 },
+ { CS35L45_BLOCK_ENABLES2, 0x00000010 },
+ { CS35L45_SYNC_GPIO1, 0x00000007 },
+ { CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 },
+ { CS35L45_GPIO3, 0x00000005 },
+ { CS35L45_PWRMGT_CTL, 0x00000000 },
+ { CS35L45_WAKESRC_CTL, 0x00000008 },
+ { CS35L45_WKI2C_CTL, 0x00000030 },
+ { CS35L45_REFCLK_INPUT, 0x00000510 },
+ { CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
+ { CS35L45_ASP_ENABLES1, 0x00000000 },
+ { CS35L45_ASP_CONTROL1, 0x00000028 },
+ { CS35L45_ASP_CONTROL2, 0x18180200 },
+ { CS35L45_ASP_CONTROL3, 0x00000002 },
+ { CS35L45_ASP_FRAME_CONTROL1, 0x03020100 },
+ { CS35L45_ASP_FRAME_CONTROL2, 0x00000004 },
+ { CS35L45_ASP_FRAME_CONTROL5, 0x00000100 },
+ { CS35L45_ASP_DATA_CONTROL1, 0x00000018 },
+ { CS35L45_ASP_DATA_CONTROL5, 0x00000018 },
+ { CS35L45_DACPCM1_INPUT, 0x00000008 },
+ { CS35L45_ASPTX1_INPUT, 0x00000018 },
+ { CS35L45_ASPTX2_INPUT, 0x00000019 },
+ { CS35L45_ASPTX3_INPUT, 0x00000020 },
+ { CS35L45_ASPTX4_INPUT, 0x00000028 },
+ { CS35L45_ASPTX5_INPUT, 0x00000048 },
+ { CS35L45_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX8_RATE, 0x00000001 },
+ { CS35L45_DSP1RX1_INPUT, 0x00000008 },
+ { CS35L45_DSP1RX2_INPUT, 0x00000009 },
+ { CS35L45_DSP1RX3_INPUT, 0x00000018 },
+ { CS35L45_DSP1RX4_INPUT, 0x00000019 },
+ { CS35L45_DSP1RX5_INPUT, 0x00000020 },
+ { CS35L45_DSP1RX6_INPUT, 0x00000028 },
+ { CS35L45_DSP1RX7_INPUT, 0x0000003A },
+ { CS35L45_DSP1RX8_INPUT, 0x00000028 },
+ { CS35L45_AMP_PCM_CONTROL, 0x00100000 },
+ { CS35L45_AMP_GAIN, 0x00002300 },
+ { CS35L45_IRQ1_CFG, 0x00000000 },
+ { CS35L45_IRQ1_MASK_1, 0xBFEFFFBF },
+ { CS35L45_IRQ1_MASK_2, 0xFFFFFFFF },
+ { CS35L45_IRQ1_MASK_3, 0xFFFF87FF },
+ { CS35L45_IRQ1_MASK_4, 0xF8FFFFFF },
+ { CS35L45_IRQ1_MASK_5, 0x0EF80000 },
+ { CS35L45_IRQ1_MASK_6, 0x00000000 },
+ { CS35L45_IRQ1_MASK_7, 0xFFFFFF78 },
+ { CS35L45_IRQ1_MASK_8, 0x00003FFF },
+ { CS35L45_IRQ1_MASK_9, 0x00000000 },
+ { CS35L45_IRQ1_MASK_10, 0x00000000 },
+ { CS35L45_IRQ1_MASK_11, 0x00000000 },
+ { CS35L45_IRQ1_MASK_12, 0x00000000 },
+ { CS35L45_IRQ1_MASK_13, 0x00000000 },
+ { CS35L45_IRQ1_MASK_14, 0x00000001 },
+ { CS35L45_IRQ1_MASK_15, 0x00000000 },
+ { CS35L45_IRQ1_MASK_16, 0x00000000 },
+ { CS35L45_IRQ1_MASK_17, 0x00000000 },
+ { CS35L45_IRQ1_MASK_18, 0x3FE5D0FF },
+ { CS35L45_GPIO1_CTRL1, 0x81000001 },
+ { CS35L45_GPIO2_CTRL1, 0x81000001 },
+ { CS35L45_GPIO3_CTRL1, 0x81000001 },
+};
+
+static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L45_DEVID ... CS35L45_OTPID:
+ case CS35L45_SFT_RESET:
+ case CS35L45_GLOBAL_ENABLES:
+ case CS35L45_BLOCK_ENABLES:
+ case CS35L45_BLOCK_ENABLES2:
+ case CS35L45_ERROR_RELEASE:
+ case CS35L45_SYNC_GPIO1:
+ case CS35L45_INTB_GPIO2_MCLK_REF:
+ case CS35L45_GPIO3:
+ case CS35L45_PWRMGT_CTL:
+ case CS35L45_WAKESRC_CTL:
+ case CS35L45_WKI2C_CTL:
+ case CS35L45_PWRMGT_STS:
+ case CS35L45_REFCLK_INPUT:
+ case CS35L45_GLOBAL_SAMPLE_RATE:
+ case CS35L45_ASP_ENABLES1:
+ case CS35L45_ASP_CONTROL1:
+ case CS35L45_ASP_CONTROL2:
+ case CS35L45_ASP_CONTROL3:
+ case CS35L45_ASP_FRAME_CONTROL1:
+ case CS35L45_ASP_FRAME_CONTROL2:
+ case CS35L45_ASP_FRAME_CONTROL5:
+ case CS35L45_ASP_DATA_CONTROL1:
+ case CS35L45_ASP_DATA_CONTROL5:
+ case CS35L45_DACPCM1_INPUT:
+ case CS35L45_ASPTX1_INPUT:
+ case CS35L45_ASPTX2_INPUT:
+ case CS35L45_ASPTX3_INPUT:
+ case CS35L45_ASPTX4_INPUT:
+ case CS35L45_ASPTX5_INPUT:
+ case CS35L45_DSP1RX1_INPUT:
+ case CS35L45_DSP1RX2_INPUT:
+ case CS35L45_DSP1RX3_INPUT:
+ case CS35L45_DSP1RX4_INPUT:
+ case CS35L45_DSP1RX5_INPUT:
+ case CS35L45_DSP1RX6_INPUT:
+ case CS35L45_DSP1RX7_INPUT:
+ case CS35L45_DSP1RX8_INPUT:
+ case CS35L45_HVLV_CONFIG:
+ case CS35L45_AMP_PCM_CONTROL:
+ case CS35L45_AMP_GAIN:
+ case CS35L45_AMP_PCM_HPF_TST:
+ case CS35L45_IRQ1_CFG:
+ case CS35L45_IRQ1_STATUS:
+ case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+ case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+ case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_GPIO1_CTRL1:
+ case CS35L45_GPIO2_CTRL1:
+ case CS35L45_GPIO3_CTRL1:
+ case CS35L45_DSP_MBOX_1:
+ case CS35L45_DSP_MBOX_2:
+ case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+ case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+ case CS35L45_DSP1_SYS_ID:
+ case CS35L45_DSP1_CLOCK_FREQ:
+ case CS35L45_DSP1_RX1_RATE:
+ case CS35L45_DSP1_RX2_RATE:
+ case CS35L45_DSP1_RX3_RATE:
+ case CS35L45_DSP1_RX4_RATE:
+ case CS35L45_DSP1_RX5_RATE:
+ case CS35L45_DSP1_RX6_RATE:
+ case CS35L45_DSP1_RX7_RATE:
+ case CS35L45_DSP1_RX8_RATE:
+ case CS35L45_DSP1_TX1_RATE:
+ case CS35L45_DSP1_TX2_RATE:
+ case CS35L45_DSP1_TX3_RATE:
+ case CS35L45_DSP1_TX4_RATE:
+ case CS35L45_DSP1_TX5_RATE:
+ case CS35L45_DSP1_TX6_RATE:
+ case CS35L45_DSP1_TX7_RATE:
+ case CS35L45_DSP1_TX8_RATE:
+ case CS35L45_DSP1_SCRATCH1:
+ case CS35L45_DSP1_SCRATCH2:
+ case CS35L45_DSP1_SCRATCH3:
+ case CS35L45_DSP1_SCRATCH4:
+ case CS35L45_DSP1_CCM_CORE_CONTROL:
+ case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+ case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+ case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+ case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+ case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+ case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+ case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L45_DEVID ... CS35L45_OTPID:
+ case CS35L45_SFT_RESET:
+ case CS35L45_GLOBAL_ENABLES:
+ case CS35L45_ERROR_RELEASE:
+ case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
+ case CS35L45_PWRMGT_STS:
+ case CS35L45_IRQ1_STATUS:
+ case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+ case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_DSP_MBOX_1:
+ case CS35L45_DSP_MBOX_2:
+ case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+ case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+ case CS35L45_DSP1_SYS_ID:
+ case CS35L45_DSP1_CLOCK_FREQ:
+ case CS35L45_DSP1_SCRATCH1:
+ case CS35L45_DSP1_SCRATCH2:
+ case CS35L45_DSP1_SCRATCH3:
+ case CS35L45_DSP1_SCRATCH4:
+ case CS35L45_DSP1_CCM_CORE_CONTROL:
+ case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+ case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+ case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+ case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+ case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+ case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+ case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config cs35l45_i2c_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L45_LASTREG,
+ .reg_defaults = cs35l45_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+ .volatile_reg = cs35l45_volatile_reg,
+ .readable_reg = cs35l45_readable_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, "SND_SOC_CS35L45");
+
+const struct regmap_config cs35l45_spi_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L45_LASTREG,
+ .reg_defaults = cs35l45_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+ .volatile_reg = cs35l45_volatile_reg,
+ .readable_reg = cs35l45_readable_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, "SND_SOC_CS35L45");
+
+static const struct {
+ u8 cfg_id;
+ u32 freq;
+} cs35l45_pll_refclk_freq[] = {
+ { 0x0C, 128000 },
+ { 0x0F, 256000 },
+ { 0x11, 384000 },
+ { 0x12, 512000 },
+ { 0x15, 768000 },
+ { 0x17, 1024000 },
+ { 0x19, 1411200 },
+ { 0x1B, 1536000 },
+ { 0x1C, 2116800 },
+ { 0x1D, 2048000 },
+ { 0x1E, 2304000 },
+ { 0x1F, 2822400 },
+ { 0x21, 3072000 },
+ { 0x23, 4233600 },
+ { 0x24, 4096000 },
+ { 0x25, 4608000 },
+ { 0x26, 5644800 },
+ { 0x27, 6000000 },
+ { 0x28, 6144000 },
+ { 0x29, 6350400 },
+ { 0x2A, 6912000 },
+ { 0x2D, 7526400 },
+ { 0x2E, 8467200 },
+ { 0x2F, 8192000 },
+ { 0x30, 9216000 },
+ { 0x31, 11289600 },
+ { 0x33, 12288000 },
+ { 0x37, 16934400 },
+ { 0x38, 18432000 },
+ { 0x39, 22579200 },
+ { 0x3B, 24576000 },
+};
+
+int cs35l45_get_clk_freq_id(unsigned int freq)
+{
+ int i;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_pll_refclk_freq); ++i) {
+ if (cs35l45_pll_refclk_freq[i].freq == freq)
+ return cs35l45_pll_refclk_freq[i].cfg_id;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, "SND_SOC_CS35L45");
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
new file mode 100644
index 000000000000..e33f11435980
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.c
@@ -0,0 +1,1513 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l45.c - CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/firmware.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs35l45.h"
+
+static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
+ enum cs35l45_cspl_mboxstate sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ case CSPL_MBOX_CMD_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_HIBERNATE);
+ default:
+ return false;
+ }
+}
+
+static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45,
+ struct regmap *regmap,
+ const enum cs35l45_cspl_mboxcmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ if (!cs35l45->dsp.cs_dsp.running) {
+ dev_err(cs35l45->dev, "DSP not running\n");
+ return -EPERM;
+ }
+
+ // Set mailbox cmd
+ ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (!cs35l45_check_cspl_mbox_sts(cmd, sts))
+ dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+ else
+ return 0;
+ }
+
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+ return -ENOMSG;
+}
+
+static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES,
+ CS35L45_GLOBAL_EN_MASK);
+
+ usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100);
+
+ regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l45_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l45->dsp.cs_dsp.booted)
+ return 0;
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ if (cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL,
+ CS35L45_MEM_RDY_MASK);
+
+ return wm_adsp_event(w, kcontrol, event);
+ case SND_SOC_DAPM_PRE_PMD:
+ if (cs35l45->dsp.preloaded)
+ return 0;
+
+ if (cs35l45->dsp.cs_dsp.running) {
+ ret = wm_adsp_event(w, kcontrol, event);
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ default:
+ return 0;
+ }
+}
+
+static int cs35l45_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_RESUME);
+ case SND_SOC_DAPM_PRE_PMD:
+ return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_PAUSE);
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int cs35l45_activate_ctl(struct snd_soc_component *component,
+ const char *ctl_name, bool active)
+{
+ struct snd_card *card = component->card->snd_card;
+ struct snd_kcontrol *kcontrol;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+
+ kcontrol = snd_soc_component_get_kcontrol(component, ctl_name);
+ if (!kcontrol) {
+ dev_err(component->dev, "Can't find kcontrol %s\n", ctl_name);
+ return -EINVAL;
+ }
+
+ index_offset = snd_ctl_get_ioff(kcontrol, &kcontrol->id);
+ vd = &kcontrol->vd[index_offset];
+ if (active)
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id);
+
+ return 0;
+}
+
+static int cs35l45_amplifier_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs35l45->amplifier_mode;
+
+ return 0;
+}
+
+static int cs35l45_amplifier_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_to_dapm(component);
+ unsigned int amp_state;
+ int ret;
+
+ if ((ucontrol->value.integer.value[0] == cs35l45->amplifier_mode) ||
+ (ucontrol->value.integer.value[0] > AMP_MODE_RCV))
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = regmap_read(cs35l45->regmap, CS35L45_BLOCK_ENABLES, &amp_state);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Failed to read AMP state: %d\n", ret);
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+ }
+
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_AMP_EN_MASK);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "SPK");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ if (ucontrol->value.integer.value[0] == AMP_MODE_SPK) {
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_RCV_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_BST_EN_MASK,
+ CS35L45_BST_ENABLE << CS35L45_BST_EN_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
+ CS35L45_HVLV_MODE_MASK,
+ CS35L45_HVLV_OPERATION <<
+ CS35L45_HVLV_MODE_SHIFT);
+
+ ret = cs35l45_activate_ctl(component, "Analog PCM Volume", true);
+ if (ret < 0)
+ dev_err(cs35l45->dev,
+ "Unable to deactivate ctl (%d)\n", ret);
+
+ } else /* AMP_MODE_RCV */ {
+ regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_RCV_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_BST_EN_MASK,
+ CS35L45_BST_DISABLE_FET_OFF <<
+ CS35L45_BST_EN_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
+ CS35L45_HVLV_MODE_MASK,
+ CS35L45_FORCE_LV_OPERATION <<
+ CS35L45_HVLV_MODE_SHIFT);
+
+ regmap_clear_bits(cs35l45->regmap,
+ CS35L45_BLOCK_ENABLES2,
+ CS35L45_AMP_DRE_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_AMP_GAIN,
+ CS35L45_AMP_GAIN_PCM_MASK,
+ CS35L45_AMP_GAIN_PCM_13DBV <<
+ CS35L45_AMP_GAIN_PCM_SHIFT);
+
+ ret = cs35l45_activate_ctl(component, "Analog PCM Volume", false);
+ if (ret < 0)
+ dev_err(cs35l45->dev,
+ "Unable to deactivate ctl (%d)\n", ret);
+ }
+
+ if (amp_state & CS35L45_AMP_EN_MASK)
+ regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_AMP_EN_MASK);
+
+ snd_soc_dapm_enable_pin_unlocked(dapm, "SPK");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ cs35l45->amplifier_mode = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static const char * const cs35l45_asp_tx_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2",
+ "VMON", "IMON", "ERR_VOL",
+ "VDD_BATTMON", "VDD_BSTMON",
+ "DSP_TX1", "DSP_TX2",
+ "Interpolator", "IL_TARGET",
+};
+
+static const unsigned int cs35l45_asp_tx_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+ CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+ CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2,
+ CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
+};
+
+static const struct soc_enum cs35l45_asp_tx_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+};
+
+static const char * const cs35l45_dsp_rx_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2",
+ "VMON", "IMON", "ERR_VOL",
+ "CLASSH_TGT", "VDD_BATTMON",
+ "VDD_BSTMON", "TEMPMON",
+};
+
+static const unsigned int cs35l45_dsp_rx_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+ CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON,
+ CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON,
+};
+
+static const struct soc_enum cs35l45_dsp_rx_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+};
+
+static const char * const cs35l45_dac_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2"
+};
+
+static const unsigned int cs35l45_dac_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2
+};
+
+static const struct soc_enum cs35l45_dacpcm_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt,
+ cs35l45_dac_val),
+};
+
+static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
+ SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]),
+ SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]),
+ SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]),
+ SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]),
+ SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
+};
+
+static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = {
+ SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]),
+ SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]),
+ SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]),
+ SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]),
+ SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]),
+ SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]),
+ SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]),
+ SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]),
+};
+
+static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
+ SOC_DAPM_ENUM("DACPCM Source", cs35l45_dacpcm_enums[0]),
+};
+static const struct snd_kcontrol_new amp_en_ctl =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+ SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+ cs35l45_dsp_preload_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ cs35l45_dsp_audio_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
+ cs35l45_global_en_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SIGGEN("VMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("IMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("TEMPMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("ERR_VOL"),
+ SND_SOC_DAPM_SIGGEN("AMP_INTP"),
+ SND_SOC_DAPM_SIGGEN("IL_TARGET"),
+
+ SND_SOC_DAPM_SUPPLY("VMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TEMPMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_TEMPMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VDD_BATTMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VDD_BATTMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VDD_BSTMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VDD_BSTMON_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("VMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("IMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("TEMPMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, SND_SOC_NOPM, 0, 0),
+
+
+ SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]),
+ SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]),
+ SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]),
+ SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
+ SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
+
+ SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]),
+ SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]),
+ SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]),
+ SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]),
+ SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]),
+ SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]),
+ SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]),
+ SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]),
+
+ SND_SOC_DAPM_MUX("DACPCM Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
+
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 0, &amp_en_ctl),
+
+ SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+};
+
+#define CS35L45_ASP_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "DSP_TX1", "DSP1" }, \
+ { name" Source", "DSP_TX2", "DSP1" }, \
+ { name" Source", "VMON", "VMON" }, \
+ { name" Source", "IMON", "IMON" }, \
+ { name" Source", "ERR_VOL", "ERR_VOL" }, \
+ { name" Source", "VDD_BATTMON", "VDD_BATTMON" }, \
+ { name" Source", "VDD_BSTMON", "VDD_BSTMON" }, \
+ { name" Source", "Interpolator", "AMP_INTP" }, \
+ { name" Source", "IL_TARGET", "IL_TARGET" }
+
+#define CS35L45_DSP_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }
+
+#define CS35L45_DAC_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "DSP_TX1", "DSP1" }, \
+ { name" Source", "DSP_TX2", "DSP1" }
+
+static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
+ /* Feedback */
+ { "VMON", NULL, "VMON_SRC" },
+ { "IMON", NULL, "IMON_SRC" },
+ { "TEMPMON", NULL, "TEMPMON_SRC" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" },
+
+ { "VMON", NULL, "VMON_EN" },
+ { "IMON", NULL, "IMON_EN" },
+ { "TEMPMON", NULL, "TEMPMON_EN" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_EN" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_EN" },
+
+ { "Capture", NULL, "ASP_TX1"},
+ { "Capture", NULL, "ASP_TX2"},
+ { "Capture", NULL, "ASP_TX3"},
+ { "Capture", NULL, "ASP_TX4"},
+ { "Capture", NULL, "ASP_TX5"},
+ { "ASP_TX1", NULL, "ASP_TX1 Source"},
+ { "ASP_TX2", NULL, "ASP_TX2 Source"},
+ { "ASP_TX3", NULL, "ASP_TX3 Source"},
+ { "ASP_TX4", NULL, "ASP_TX4 Source"},
+ { "ASP_TX5", NULL, "ASP_TX5 Source"},
+
+ { "ASP_TX1", NULL, "ASP_EN" },
+ { "ASP_TX2", NULL, "ASP_EN" },
+ { "ASP_TX3", NULL, "ASP_EN" },
+ { "ASP_TX4", NULL, "ASP_EN" },
+ { "ASP_TX1", NULL, "GLOBAL_EN" },
+ { "ASP_TX2", NULL, "GLOBAL_EN" },
+ { "ASP_TX3", NULL, "GLOBAL_EN" },
+ { "ASP_TX4", NULL, "GLOBAL_EN" },
+ { "ASP_TX5", NULL, "GLOBAL_EN" },
+
+ CS35L45_ASP_MUX_ROUTE("ASP_TX1"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX2"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX3"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX4"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX5"),
+
+ /* Playback */
+ { "ASP_RX1", NULL, "Playback" },
+ { "ASP_RX2", NULL, "Playback" },
+ { "ASP_RX1", NULL, "ASP_EN" },
+ { "ASP_RX2", NULL, "ASP_EN" },
+
+ { "AMP", NULL, "DACPCM Source"},
+ { "AMP", NULL, "GLOBAL_EN"},
+
+ CS35L45_DSP_MUX_ROUTE("DSP_RX1"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX2"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX3"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX4"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX5"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX6"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX7"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX8"),
+
+ {"DSP1", NULL, "DSP_RX1 Source"},
+ {"DSP1", NULL, "DSP_RX2 Source"},
+ {"DSP1", NULL, "DSP_RX3 Source"},
+ {"DSP1", NULL, "DSP_RX4 Source"},
+ {"DSP1", NULL, "DSP_RX5 Source"},
+ {"DSP1", NULL, "DSP_RX6 Source"},
+ {"DSP1", NULL, "DSP_RX7 Source"},
+ {"DSP1", NULL, "DSP_RX8 Source"},
+
+ {"DSP1", NULL, "VMON_EN"},
+ {"DSP1", NULL, "IMON_EN"},
+ {"DSP1", NULL, "VDD_BATTMON_EN"},
+ {"DSP1", NULL, "VDD_BSTMON_EN"},
+ {"DSP1", NULL, "TEMPMON_EN"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
+ CS35L45_DAC_MUX_ROUTE("DACPCM"),
+
+ { "AMP Enable", "Switch", "AMP" },
+ { "SPK", NULL, "AMP Enable"},
+};
+
+static const char * const amplifier_mode_texts[] = {"SPK", "RCV"};
+static SOC_ENUM_SINGLE_DECL(amplifier_mode_enum, SND_SOC_NOPM, 0,
+ amplifier_mode_texts);
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 1000, 300, 0);
+static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+
+static const struct snd_kcontrol_new cs35l45_controls[] = {
+ SOC_ENUM_EXT("Amplifier Mode", amplifier_mode_enum,
+ cs35l45_amplifier_mode_get, cs35l45_amplifier_mode_put),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L45_AMP_GAIN,
+ CS35L45_AMP_GAIN_PCM_SHIFT,
+ CS35L45_AMP_GAIN_PCM_MASK >> CS35L45_AMP_GAIN_PCM_SHIFT,
+ 0, amp_gain_tlv),
+ /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */
+ SOC_SINGLE_S_TLV("Digital PCM Volume",
+ CS35L45_AMP_PCM_CONTROL,
+ CS35L45_AMP_VOL_PCM_SHIFT + 1,
+ -409, 48,
+ (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
+ 0, cs35l45_dig_pcm_vol_tlv),
+ WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+ WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
+{
+ unsigned int val;
+ int freq_id;
+
+ freq_id = cs35l45_get_clk_freq_id(freq);
+ if (freq_id < 0) {
+ dev_err(cs35l45->dev, "Invalid freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val);
+ val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT;
+ if (val == freq_id)
+ return 0;
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+ regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT,
+ CS35L45_PLL_REFCLK_FREQ_MASK,
+ freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+ regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+
+ return 0;
+}
+
+static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int asp_fmt, fsync_inv, bclk_inv;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(cs35l45->dev, "Invalid DAI clocking\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = CS35l45_ASP_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = CS35L45_ASP_FMT_I2S;
+ break;
+ default:
+ dev_err(cs35l45->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ fsync_inv = 1;
+ bclk_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ fsync_inv = 0;
+ bclk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsync_inv = 1;
+ bclk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ fsync_inv = 0;
+ bclk_inv = 0;
+ break;
+ default:
+ dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_FMT_MASK |
+ CS35L45_ASP_FSYNC_INV_MASK |
+ CS35L45_ASP_BCLK_INV_MASK,
+ (asp_fmt << CS35L45_ASP_FMT_SHIFT) |
+ (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) |
+ (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT));
+
+ return 0;
+}
+
+static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt;
+ int bclk;
+
+ switch (params_rate(params)) {
+ case 44100:
+ global_fs = CS35L45_44P100_KHZ;
+ break;
+ case 48000:
+ global_fs = CS35L45_48P0_KHZ;
+ break;
+ case 88200:
+ global_fs = CS35L45_88P200_KHZ;
+ break;
+ case 96000:
+ global_fs = CS35L45_96P0_KHZ;
+ break;
+ default:
+ dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE,
+ CS35L45_GLOBAL_FS_MASK,
+ global_fs << CS35L45_GLOBAL_FS_SHIFT);
+
+ asp_wl = params_width(params);
+
+ if (cs35l45->slot_width)
+ asp_width = cs35l45->slot_width;
+ else
+ asp_width = params_width(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_WIDTH_RX_MASK,
+ asp_width << CS35L45_ASP_WIDTH_RX_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5,
+ CS35L45_ASP_WL_MASK,
+ asp_wl << CS35L45_ASP_WL_SHIFT);
+ } else {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_WIDTH_TX_MASK,
+ asp_width << CS35L45_ASP_WIDTH_TX_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1,
+ CS35L45_ASP_WL_MASK,
+ asp_wl << CS35L45_ASP_WL_SHIFT);
+ }
+
+ if (cs35l45->sysclk_set)
+ return 0;
+
+ /* I2S always has an even number of channels */
+ regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt);
+ asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT;
+ if (asp_fmt == CS35L45_ASP_FMT_I2S)
+ slot_multiple = 2;
+ else
+ slot_multiple = 1;
+
+ bclk = snd_soc_tdm_params_to_bclk(params, asp_width,
+ cs35l45->slot_count, slot_multiple);
+
+ return cs35l45_set_pll(cs35l45, bclk);
+}
+
+static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+
+ if (slot_width && ((slot_width < 16) || (slot_width > 128)))
+ return -EINVAL;
+
+ cs35l45->slot_width = slot_width;
+ cs35l45->slot_count = slots;
+
+ return 0;
+}
+
+static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ if (clk_id != 0) {
+ dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ cs35l45->sysclk_set = false;
+ if (freq == 0)
+ return 0;
+
+ ret = cs35l45_set_pll(cs35l45, freq);
+ if (ret < 0)
+ return -EINVAL;
+
+ cs35l45->sysclk_set = true;
+
+ return 0;
+}
+
+static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int global_fs, val, hpf_tune;
+
+ if (mute)
+ return 0;
+
+ regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs);
+ global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT;
+ switch (global_fs) {
+ case CS35L45_44P100_KHZ:
+ hpf_tune = CS35L45_HPF_44P1;
+ break;
+ case CS35L45_88P200_KHZ:
+ hpf_tune = CS35L45_HPF_88P2;
+ break;
+ default:
+ hpf_tune = CS35l45_HPF_DEFAULT;
+ break;
+ }
+
+ regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val);
+ if (val != hpf_tune) {
+ struct reg_sequence hpf_override_seq[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00000044, 0x00000055 },
+ { 0x00000044, 0x000000AA },
+ { CS35L45_AMP_PCM_HPF_TST, hpf_tune },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000000 },
+ };
+ regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq,
+ ARRAY_SIZE(hpf_override_seq));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = {
+ .set_fmt = cs35l45_asp_set_fmt,
+ .hw_params = cs35l45_asp_hw_params,
+ .set_tdm_slot = cs35l45_asp_set_tdm_slot,
+ .set_sysclk = cs35l45_asp_set_sysclk,
+ .mute_stream = cs35l45_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs35l45_dai[] = {
+ {
+ .name = "cs35l45",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L45_RATES,
+ .formats = CS35L45_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = CS35L45_RATES,
+ .formats = CS35L45_FORMATS,
+ },
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ .ops = &cs35l45_asp_dai_ops,
+ },
+};
+
+static int cs35l45_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ return wm_adsp2_component_probe(&cs35l45->dsp, component);
+}
+
+static void cs35l45_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&cs35l45->dsp, component);
+}
+
+static const struct snd_soc_component_driver cs35l45_component = {
+ .probe = cs35l45_component_probe,
+ .remove = cs35l45_component_remove,
+
+ .dapm_widgets = cs35l45_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
+
+ .dapm_routes = cs35l45_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes),
+
+ .controls = cs35l45_controls,
+ .num_controls = ARRAY_SIZE(cs35l45_controls),
+
+ .name = "cs35l45",
+
+ .endianness = 1,
+};
+
+static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
+{
+ unsigned int wksrc;
+
+ if (cs35l45->bus_type == CONTROL_BUS_I2C)
+ wksrc = CS35L45_WKSRC_I2C;
+ else
+ wksrc = CS35L45_WKSRC_SPI;
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_WKSRC_EN_MASK,
+ wksrc << CS35L45_WKSRC_EN_SHIFT);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_UPDT_WKCTL_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_UPDT_WKI2C_MASK);
+}
+
+static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
+{
+ dev_dbg(cs35l45->dev, "Enter hibernate\n");
+
+ cs35l45_setup_hibernate(cs35l45);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_IRQ1_MASK_2, CS35L45_DSP_VIRT2_MBOX_MASK);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+ return 0;
+}
+
+static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(cs35l45->dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret) {
+ dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_IRQ1_MASK_2,
+ CS35L45_DSP_VIRT2_MBOX_MASK);
+ return 0;
+ }
+ usleep_range(100, 200);
+ }
+
+ dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l45_setup_hibernate(cs35l45);
+ }
+
+ dev_err(cs35l45->dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+
+static int cs35l45_runtime_suspend(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ cs35l45_enter_hibernate(cs35l45);
+
+ regcache_cache_only(cs35l45->regmap, true);
+ regcache_mark_dirty(cs35l45->regmap);
+
+ dev_dbg(cs35l45->dev, "Runtime suspended\n");
+
+ return 0;
+}
+
+static int cs35l45_runtime_resume(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ dev_dbg(cs35l45->dev, "Runtime resume\n");
+
+ regcache_cache_only(cs35l45->regmap, false);
+
+ ret = cs35l45_exit_hibernate(cs35l45);
+ if (ret)
+ return ret;
+
+ ret = regcache_sync(cs35l45->regmap);
+ if (ret != 0)
+ dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
+
+ /* Clear global error status */
+ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ return ret;
+}
+
+static int cs35l45_sys_suspend(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "System suspend, disabling IRQ\n");
+ disable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_sys_suspend_noirq(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "Late system suspend, reenabling IRQ\n");
+ enable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_sys_resume_noirq(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "Early system resume, disabling IRQ\n");
+ disable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_sys_resume(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l45->dev, "System resume, reenabling IRQ\n");
+ enable_irq(cs35l45->irq);
+
+ return 0;
+}
+
+static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
+{
+ struct device_node *node = cs35l45->dev->of_node;
+ unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1,
+ CS35L45_GPIO3_CTRL1};
+ unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1,
+ CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3};
+ struct device_node *child;
+ unsigned int val;
+ char of_name[32];
+ int ret, i;
+
+ if (!node)
+ return 0;
+
+ for (i = 0; i < CS35L45_NUM_GPIOS; i++) {
+ sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1);
+ child = of_get_child_by_name(node, of_name);
+ if (!child)
+ continue;
+
+ ret = of_property_read_u32(child, "gpio-dir", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_DIR_MASK,
+ val << CS35L45_GPIO_DIR_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-lvl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_LVL_MASK,
+ val << CS35L45_GPIO_LVL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-op-cfg", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_OP_CFG_MASK,
+ val << CS35L45_GPIO_OP_CFG_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-pol", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_POL_MASK,
+ val << CS35L45_GPIO_POL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-ctrl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_CTRL_MASK,
+ val << CS35L45_GPIO_CTRL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-invert", &val);
+ if (!ret) {
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_INVERT_MASK,
+ val << CS35L45_GPIO_INVERT_SHIFT);
+ if (i == 1)
+ cs35l45->irq_invert = val;
+ }
+
+ of_node_put(child);
+ }
+
+ if (device_property_read_u32(cs35l45->dev,
+ "cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3,
+ CS35L45_ASP_DOUT_HIZ_CTRL_MASK,
+ val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45,
+ const unsigned int cmd,
+ unsigned int data)
+{
+ static char *speak_status = "Unknown";
+
+ switch (cmd) {
+ case EVENT_SPEAKER_STATUS:
+ switch (data) {
+ case 1:
+ speak_status = "All Clear";
+ break;
+ case 2:
+ speak_status = "Open Circuit";
+ break;
+ case 4:
+ speak_status = "Short Circuit";
+ break;
+ }
+
+ dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n",
+ speak_status);
+ break;
+ case EVENT_BOOT_DONE:
+ dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n");
+ break;
+ default:
+ dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+ unsigned int mbox_val;
+ int ret = 0;
+
+ ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val);
+ if (!ret && mbox_val)
+ cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK,
+ (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT);
+
+ /* Handle DSP trace log IRQ */
+ ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val);
+ if (!ret && mbox_val != 0) {
+ dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n");
+ }
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+
+ dev_dbg(cs35l45->dev, "PLL unlock detected!");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_pll_lock(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+
+ dev_dbg(cs35l45->dev, "PLL lock detected!");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data);
+
+static const struct cs35l45_irq cs35l45_irqs[] = {
+ CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
+ CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
+ CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+ CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb),
+};
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+ int i;
+
+ i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0);
+
+ if (i < 0 || i >= ARRAY_SIZE(cs35l45_irqs))
+ dev_err(cs35l45->dev, "Unspecified global error condition (%d) detected!\n", irq);
+ else
+ dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_irq cs35l45_reg_irqs[] = {
+ CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR),
+ CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+ CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX),
+};
+
+static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
+ .name = "cs35l45 IRQ1 Controller",
+ .main_status = CS35L45_IRQ1_STATUS,
+ .status_base = CS35L45_IRQ1_EINT_1,
+ .mask_base = CS35L45_IRQ1_MASK_1,
+ .ack_base = CS35L45_IRQ1_EINT_1,
+ .num_regs = 18,
+ .irqs = cs35l45_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l45_reg_irqs),
+ .runtime_pm = true,
+};
+
+static int cs35l45_initialize(struct cs35l45_private *cs35l45)
+{
+ struct device *dev = cs35l45->dev;
+ unsigned int dev_id[5];
+ unsigned int sts;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts,
+ (sts & CS35L45_OTP_BOOT_DONE_STS_MASK),
+ 1000, 5000);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n");
+ return ret;
+ }
+
+ ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id));
+ if (ret) {
+ dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret);
+ return ret;
+ }
+
+ switch (dev_id[0]) {
+ case 0x35A450:
+ case 0x35A460:
+ break;
+ default:
+ dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]);
+ return -ENODEV;
+ }
+
+ dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n",
+ dev_id[1], dev_id[4]);
+
+ regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4,
+ CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK);
+
+ ret = cs35l45_apply_patch(cs35l45);
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply init patch %d\n", ret);
+ return ret;
+ }
+
+ ret = cs35l45_apply_property_config(cs35l45);
+ if (ret < 0)
+ return ret;
+
+ cs35l45->amplifier_mode = AMP_MODE_SPK;
+
+ return 0;
+}
+
+static const struct reg_sequence cs35l45_fs_errata_patch[] = {
+ {0x02B80080, 0x00000001},
+ {0x02B80088, 0x00000001},
+ {0x02B80090, 0x00000001},
+ {0x02B80098, 0x00000001},
+ {0x02B800A0, 0x00000001},
+ {0x02B800A8, 0x00000001},
+ {0x02B800B0, 0x00000001},
+ {0x02B800B8, 0x00000001},
+ {0x02B80280, 0x00000001},
+ {0x02B80288, 0x00000001},
+ {0x02B80290, 0x00000001},
+ {0x02B80298, 0x00000001},
+ {0x02B802A0, 0x00000001},
+ {0x02B802A8, 0x00000001},
+ {0x02B802B0, 0x00000001},
+ {0x02B802B8, 0x00000001},
+};
+
+static const struct cs_dsp_region cs35l45_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L45_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L45_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L45_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L45_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L45_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l45_dsp_init(struct cs35l45_private *cs35l45)
+{
+ struct wm_adsp *dsp = &cs35l45->dsp;
+ int ret;
+
+ dsp->part = "cs35l45";
+ dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+ dsp->toggle_preload = true;
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs35l45->dev;
+ dsp->cs_dsp.regmap = cs35l45->regmap;
+ dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID;
+ dsp->cs_dsp.mem = cs35l45_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions);
+ dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
+
+ ret = wm_halo_init(dsp);
+
+ regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch,
+ ARRAY_SIZE(cs35l45_fs_errata_patch));
+
+ return ret;
+}
+
+int cs35l45_probe(struct cs35l45_private *cs35l45)
+{
+ struct device *dev = cs35l45->dev;
+ unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED;
+ int ret, i, irq;
+
+ cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
+ if (IS_ERR(cs35l45->vdd_batt))
+ return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt),
+ "Failed to request vdd-batt\n");
+
+ cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a");
+ if (IS_ERR(cs35l45->vdd_a))
+ return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a),
+ "Failed to request vdd-a\n");
+
+ /* VDD_BATT must always be enabled before other supplies */
+ ret = regulator_enable(cs35l45->vdd_batt);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n");
+
+ ret = regulator_enable(cs35l45->vdd_a);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable vdd-a\n");
+
+ /* If reset is shared only one instance can claim it */
+ cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l45->reset_gpio)) {
+ ret = PTR_ERR(cs35l45->reset_gpio);
+ cs35l45->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_dbg(dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err_probe(dev, ret, "Failed to get reset GPIO\n");
+ goto err;
+ }
+ }
+
+ if (cs35l45->reset_gpio) {
+ usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100);
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 1);
+ }
+
+ usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100);
+
+ ret = cs35l45_initialize(cs35l45);
+ if (ret < 0)
+ goto err_reset;
+
+ ret = cs35l45_dsp_init(cs35l45);
+ if (ret < 0)
+ goto err_reset;
+
+ pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l45->dev);
+ pm_runtime_set_active(cs35l45->dev);
+ pm_runtime_get_noresume(cs35l45->dev);
+ pm_runtime_enable(cs35l45->dev);
+
+ if (cs35l45->irq) {
+ if (cs35l45->irq_invert)
+ irq_pol |= IRQF_TRIGGER_HIGH;
+ else
+ irq_pol |= IRQF_TRIGGER_LOW;
+
+ ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0,
+ &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
+ goto err_dsp;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq);
+ if (irq < 0) {
+ dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
+ ret = irq;
+ goto err_dsp;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
+ irq_pol, cs35l45_irqs[i].name, cs35l45);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %s: %d\n",
+ cs35l45_irqs[i].name, ret);
+ goto err_dsp;
+ }
+ }
+ }
+
+ ret = devm_snd_soc_register_component(dev, &cs35l45_component,
+ cs35l45_dai,
+ ARRAY_SIZE(cs35l45_dai));
+ if (ret < 0)
+ goto err_dsp;
+
+ pm_runtime_put_autosuspend(cs35l45->dev);
+
+ return 0;
+
+err_dsp:
+ pm_runtime_disable(cs35l45->dev);
+ pm_runtime_put_noidle(cs35l45->dev);
+ wm_adsp2_remove(&cs35l45->dsp);
+
+err_reset:
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+err:
+ regulator_disable(cs35l45->vdd_a);
+ regulator_disable(cs35l45->vdd_batt);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_probe, "SND_SOC_CS35L45");
+
+void cs35l45_remove(struct cs35l45_private *cs35l45)
+{
+ pm_runtime_get_sync(cs35l45->dev);
+ pm_runtime_disable(cs35l45->dev);
+ wm_adsp2_remove(&cs35l45->dsp);
+
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+
+ pm_runtime_put_noidle(cs35l45->dev);
+ regulator_disable(cs35l45->vdd_a);
+ /* VDD_BATT must be the last to power-off */
+ regulator_disable(cs35l45->vdd_batt);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_remove, "SND_SOC_CS35L45");
+
+EXPORT_GPL_DEV_PM_OPS(cs35l45_pm_ops) = {
+ RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL)
+
+ SYSTEM_SLEEP_PM_OPS(cs35l45_sys_suspend, cs35l45_sys_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l45_sys_suspend_noirq, cs35l45_sys_resume_noirq)
+};
+
+MODULE_DESCRIPTION("ASoC CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
new file mode 100644
index 000000000000..7a790d2acac7
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.h
@@ -0,0 +1,514 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cs35l45.h - CS35L45 ALSA SoC audio driver
+ *
+ * Copyright 2019-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef CS35L45_H
+#define CS35L45_H
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
+#include "wm_adsp.h"
+
+#define CS35L45_DEVID 0x00000000
+#define CS35L45_REVID 0x00000004
+#define CS35L45_RELID 0x0000000C
+#define CS35L45_OTPID 0x00000010
+#define CS35L45_SFT_RESET 0x00000020
+#define CS35L45_GLOBAL_ENABLES 0x00002014
+#define CS35L45_BLOCK_ENABLES 0x00002018
+#define CS35L45_BLOCK_ENABLES2 0x0000201C
+#define CS35L45_ERROR_RELEASE 0x00002034
+#define CS35L45_SYNC_GPIO1 0x00002430
+#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434
+#define CS35L45_GPIO3 0x00002438
+#define CS35L45_PWRMGT_CTL 0x00002900
+#define CS35L45_WAKESRC_CTL 0x00002904
+#define CS35L45_WKI2C_CTL 0x00002908
+#define CS35L45_PWRMGT_STS 0x0000290C
+#define CS35L45_REFCLK_INPUT 0x00002C04
+#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
+#define CS35L45_BOOST_CCM_CFG 0x00003808
+#define CS35L45_BOOST_DCM_CFG 0x0000380C
+#define CS35L45_BOOST_OV_CFG 0x0000382C
+#define CS35L45_ASP_ENABLES1 0x00004800
+#define CS35L45_ASP_CONTROL1 0x00004804
+#define CS35L45_ASP_CONTROL2 0x00004808
+#define CS35L45_ASP_CONTROL3 0x0000480C
+#define CS35L45_ASP_FRAME_CONTROL1 0x00004810
+#define CS35L45_ASP_FRAME_CONTROL2 0x00004814
+#define CS35L45_ASP_FRAME_CONTROL5 0x00004820
+#define CS35L45_ASP_DATA_CONTROL1 0x00004830
+#define CS35L45_ASP_DATA_CONTROL5 0x00004840
+#define CS35L45_DACPCM1_INPUT 0x00004C00
+#define CS35L45_ASPTX1_INPUT 0x00004C20
+#define CS35L45_ASPTX2_INPUT 0x00004C24
+#define CS35L45_ASPTX3_INPUT 0x00004C28
+#define CS35L45_ASPTX4_INPUT 0x00004C2C
+#define CS35L45_ASPTX5_INPUT 0x00004C30
+#define CS35L45_DSP1RX1_INPUT 0x00004C40
+#define CS35L45_DSP1RX2_INPUT 0x00004C44
+#define CS35L45_DSP1RX3_INPUT 0x00004C48
+#define CS35L45_DSP1RX4_INPUT 0x00004C4C
+#define CS35L45_DSP1RX5_INPUT 0x00004C50
+#define CS35L45_DSP1RX6_INPUT 0x00004C54
+#define CS35L45_DSP1RX7_INPUT 0x00004C58
+#define CS35L45_DSP1RX8_INPUT 0x00004C5C
+#define CS35L45_HVLV_CONFIG 0x00006400
+#define CS35L45_LDPM_CONFIG 0x00006404
+#define CS35L45_AMP_PCM_CONTROL 0x00007000
+#define CS35L45_AMP_PCM_HPF_TST 0x00007004
+#define CS35L45_AMP_GAIN 0x00007800
+#define CS35L45_IRQ1_CFG 0x0000E000
+#define CS35L45_IRQ1_STATUS 0x0000E004
+#define CS35L45_IRQ1_EINT_1 0x0000E010
+#define CS35L45_IRQ1_EINT_2 0x0000E014
+#define CS35L45_IRQ1_EINT_3 0x0000E018
+#define CS35L45_IRQ1_EINT_4 0x0000E01C
+#define CS35L45_IRQ1_EINT_5 0x0000E020
+#define CS35L45_IRQ1_EINT_7 0x0000E028
+#define CS35L45_IRQ1_EINT_8 0x0000E02C
+#define CS35L45_IRQ1_EINT_18 0x0000E054
+#define CS35L45_IRQ1_STS_1 0x0000E090
+#define CS35L45_IRQ1_STS_2 0x0000E094
+#define CS35L45_IRQ1_STS_3 0x0000E098
+#define CS35L45_IRQ1_STS_4 0x0000E09C
+#define CS35L45_IRQ1_STS_5 0x0000E0A0
+#define CS35L45_IRQ1_STS_7 0x0000E0A8
+#define CS35L45_IRQ1_STS_8 0x0000E0AC
+#define CS35L45_IRQ1_STS_18 0x0000E0D4
+#define CS35L45_IRQ1_MASK_1 0x0000E110
+#define CS35L45_IRQ1_MASK_2 0x0000E114
+#define CS35L45_IRQ1_MASK_3 0x0000E118
+#define CS35L45_IRQ1_MASK_4 0x0000E11C
+#define CS35L45_IRQ1_MASK_5 0x0000E120
+#define CS35L45_IRQ1_MASK_6 0x0000E124
+#define CS35L45_IRQ1_MASK_7 0x0000E128
+#define CS35L45_IRQ1_MASK_8 0x0000E12C
+#define CS35L45_IRQ1_MASK_9 0x0000E130
+#define CS35L45_IRQ1_MASK_10 0x0000E134
+#define CS35L45_IRQ1_MASK_11 0x0000E138
+#define CS35L45_IRQ1_MASK_12 0x0000E13C
+#define CS35L45_IRQ1_MASK_13 0x0000E140
+#define CS35L45_IRQ1_MASK_14 0x0000E144
+#define CS35L45_IRQ1_MASK_15 0x0000E148
+#define CS35L45_IRQ1_MASK_16 0x0000E14C
+#define CS35L45_IRQ1_MASK_17 0x0000E150
+#define CS35L45_IRQ1_MASK_18 0x0000E154
+#define CS35L45_GPIO_STATUS1 0x0000F000
+#define CS35L45_GPIO1_CTRL1 0x0000F008
+#define CS35L45_GPIO2_CTRL1 0x0000F00C
+#define CS35L45_GPIO3_CTRL1 0x0000F010
+#define CS35L45_DSP_MBOX_1 0x00011000
+#define CS35L45_DSP_MBOX_2 0x00011004
+#define CS35L45_DSP_VIRT1_MBOX_1 0x00011020
+#define CS35L45_DSP_VIRT1_MBOX_2 0x00011024
+#define CS35L45_DSP_VIRT1_MBOX_3 0x00011028
+#define CS35L45_DSP_VIRT1_MBOX_4 0x0001102C
+#define CS35L45_DSP_VIRT2_MBOX_1 0x00011040
+#define CS35L45_DSP_VIRT2_MBOX_2 0x00011044
+#define CS35L45_DSP_VIRT2_MBOX_3 0x00011048
+#define CS35L45_DSP_VIRT2_MBOX_4 0x0001104C
+#define CS35L45_DSP1_XMEM_PACK_0 0x02000000
+#define CS35L45_DSP1_XMEM_PACK_4607 0x020047FC
+#define CS35L45_DSP1_XMEM_UNPACK32_0 0x02400000
+#define CS35L45_DSP1_XMEM_UNPACK32_3071 0x02402FFC
+#define CS35L45_DSP1_SYS_ID 0x025E0000
+#define CS35L45_DSP1_XMEM_UNPACK24_0 0x02800000
+#define CS35L45_DSP1_XMEM_UNPACK24_6143 0x02805FFC
+#define CS35L45_DSP1_CLOCK_FREQ 0x02B80000
+#define CS35L45_DSP1_RX1_RATE 0x02B80080
+#define CS35L45_DSP1_RX2_RATE 0x02B80088
+#define CS35L45_DSP1_RX3_RATE 0x02B80090
+#define CS35L45_DSP1_RX4_RATE 0x02B80098
+#define CS35L45_DSP1_RX5_RATE 0x02B800A0
+#define CS35L45_DSP1_RX6_RATE 0x02B800A8
+#define CS35L45_DSP1_RX7_RATE 0x02B800B0
+#define CS35L45_DSP1_RX8_RATE 0x02B800B8
+#define CS35L45_DSP1_TX1_RATE 0x02B80280
+#define CS35L45_DSP1_TX2_RATE 0x02B80288
+#define CS35L45_DSP1_TX3_RATE 0x02B80290
+#define CS35L45_DSP1_TX4_RATE 0x02B80298
+#define CS35L45_DSP1_TX5_RATE 0x02B802A0
+#define CS35L45_DSP1_TX6_RATE 0x02B802A8
+#define CS35L45_DSP1_TX7_RATE 0x02B802B0
+#define CS35L45_DSP1_TX8_RATE 0x02B802B8
+#define CS35L45_DSP1_SCRATCH1 0x02B805C0
+#define CS35L45_DSP1_SCRATCH2 0x02B805C8
+#define CS35L45_DSP1_SCRATCH3 0x02B805D0
+#define CS35L45_DSP1_SCRATCH4 0x02B805D8
+#define CS35L45_DSP1_CCM_CORE_CONTROL 0x02BC1000
+#define CS35L45_DSP1_YMEM_PACK_0 0x02C00000
+#define CS35L45_DSP1_YMEM_PACK_1532 0x02C017F0
+#define CS35L45_DSP1_YMEM_UNPACK32_0 0x03000000
+#define CS35L45_DSP1_YMEM_UNPACK32_1022 0x03000FF8
+#define CS35L45_DSP1_YMEM_UNPACK24_0 0x03400000
+#define CS35L45_DSP1_YMEM_UNPACK24_2043 0x03401FEC
+#define CS35L45_DSP1_PMEM_0 0x03800000
+#define CS35L45_DSP1_PMEM_3834 0x03803BE8
+#define CS35L45_LASTREG 0x03C6EFE8
+
+/* SFT_RESET */
+#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000
+
+/* GLOBAL_ENABLES */
+#define CS35L45_GLOBAL_EN_SHIFT 0
+#define CS35L45_GLOBAL_EN_MASK BIT(0)
+
+/* BLOCK_ENABLES */
+#define CS35L45_IMON_EN_SHIFT 13
+#define CS35L45_VMON_EN_SHIFT 12
+#define CS35L45_TEMPMON_EN_SHIFT 10
+#define CS35L45_VDD_BSTMON_EN_SHIFT 9
+#define CS35L45_VDD_BATTMON_EN_SHIFT 8
+#define CS35L45_BST_EN_SHIFT 4
+#define CS35L45_BST_EN_MASK GENMASK(5, 4)
+#define CS35L45_RCV_EN_SHIFT 2
+#define CS35L45_RCV_EN_MASK BIT(2)
+#define CS35L45_AMP_EN_SHIFT 0
+#define CS35L45_AMP_EN_MASK BIT(0)
+
+#define CS35L45_BST_DISABLE_FET_OFF 0x00
+#define CS35L45_BST_DISABLE_FET_ON 0x01
+#define CS35L45_BST_ENABLE 0x02
+
+/* BLOCK_ENABLES2 */
+#define CS35L45_ASP_EN_SHIFT 27
+#define CS35L45_AMP_DRE_EN_SHIFT 20
+#define CS35L45_AMP_DRE_EN_MASK BIT(20)
+#define CS35L45_MEM_RDY_SHIFT 1
+#define CS35L45_MEM_RDY_MASK BIT(1)
+
+/* ERROR_RELEASE */
+#define CS35L45_GLOBAL_ERR_RLS_MASK BIT(11)
+
+/* CCM_CORE */
+#define CS35L45_CCM_CORE_RESET_SHIFT 9
+#define CS35L45_CCM_CORE_RESET_MASK BIT(9)
+#define CS35L45_CCM_PM_REMAP_SHIFT 7
+#define CS35L45_CCM_PM_REMAP_MASK BIT(7)
+#define CS35L45_CCM_CORE_EN_SHIFT 0
+#define CS35L45_CCM_CORE_EN_MASK BIT(0)
+
+/* REFCLK_INPUT */
+#define CS35L45_PLL_FORCE_EN_SHIFT 16
+#define CS35L45_PLL_FORCE_EN_MASK BIT(16)
+#define CS35L45_PLL_OPEN_LOOP_SHIFT 11
+#define CS35L45_PLL_OPEN_LOOP_MASK BIT(11)
+#define CS35L45_PLL_REFCLK_FREQ_SHIFT 5
+#define CS35L45_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
+#define CS35L45_PLL_REFCLK_EN_SHIFT 4
+#define CS35L45_PLL_REFCLK_EN_MASK BIT(4)
+#define CS35L45_PLL_REFCLK_SEL_SHIFT 0
+#define CS35L45_PLL_REFCLK_SEL_MASK GENMASK(2, 0)
+
+#define CS35L45_PLL_REFCLK_SEL_BCLK 0x0
+
+/* GLOBAL_SAMPLE_RATE */
+#define CS35L45_GLOBAL_FS_SHIFT 0
+#define CS35L45_GLOBAL_FS_MASK GENMASK(4, 0)
+
+#define CS35L45_48P0_KHZ 0x03
+#define CS35L45_96P0_KHZ 0x04
+#define CS35L45_44P100_KHZ 0x0B
+#define CS35L45_88P200_KHZ 0x0C
+
+/* ASP_ENABLES_1 */
+#define CS35L45_ASP_RX2_EN_SHIFT 17
+#define CS35L45_ASP_RX1_EN_SHIFT 16
+#define CS35L45_ASP_TX5_EN_SHIFT 4
+#define CS35L45_ASP_TX4_EN_SHIFT 3
+#define CS35L45_ASP_TX3_EN_SHIFT 2
+#define CS35L45_ASP_TX2_EN_SHIFT 1
+#define CS35L45_ASP_TX1_EN_SHIFT 0
+
+/* ASP_CONTROL2 */
+#define CS35L45_ASP_WIDTH_RX_SHIFT 24
+#define CS35L45_ASP_WIDTH_RX_MASK GENMASK(31, 24)
+#define CS35L45_ASP_WIDTH_TX_SHIFT 16
+#define CS35L45_ASP_WIDTH_TX_MASK GENMASK(23, 16)
+#define CS35L45_ASP_FMT_SHIFT 8
+#define CS35L45_ASP_FMT_MASK GENMASK(10, 8)
+#define CS35L45_ASP_BCLK_INV_SHIFT 6
+#define CS35L45_ASP_BCLK_INV_MASK BIT(6)
+#define CS35L45_ASP_FSYNC_INV_SHIFT 2
+#define CS35L45_ASP_FSYNC_INV_MASK BIT(2)
+
+#define CS35l45_ASP_FMT_DSP_A 0
+#define CS35L45_ASP_FMT_I2S 2
+
+/* ASP_CONTROL3 */
+#define CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT 0
+#define CS35L45_ASP_DOUT_HIZ_CTRL_MASK GENMASK(1, 0)
+
+/* ASP_FRAME_CONTROL1 */
+#define CS35L45_ASP_TX4_SLOT_SHIFT 24
+#define CS35L45_ASP_TX4_SLOT_MASK GENMASK(29, 24)
+#define CS35L45_ASP_TX3_SLOT_SHIFT 16
+#define CS35L45_ASP_TX3_SLOT_MASK GENMASK(21, 16)
+#define CS35L45_ASP_TX2_SLOT_SHIFT 8
+#define CS35L45_ASP_TX2_SLOT_MASK GENMASK(13, 8)
+#define CS35L45_ASP_TX1_SLOT_SHIFT 0
+#define CS35L45_ASP_TX1_SLOT_MASK GENMASK(5, 0)
+
+#define CS35L45_ASP_TX_ALL_SLOTS (CS35L45_ASP_TX4_SLOT_MASK | \
+ CS35L45_ASP_TX3_SLOT_MASK | \
+ CS35L45_ASP_TX2_SLOT_MASK | \
+ CS35L45_ASP_TX1_SLOT_MASK)
+/* ASP_FRAME_CONTROL5 */
+#define CS35L45_ASP_RX2_SLOT_SHIFT 8
+#define CS35L45_ASP_RX2_SLOT_MASK GENMASK(13, 8)
+#define CS35L45_ASP_RX1_SLOT_SHIFT 0
+#define CS35L45_ASP_RX1_SLOT_MASK GENMASK(5, 0)
+
+#define CS35L45_ASP_RX_ALL_SLOTS (CS35L45_ASP_RX2_SLOT_MASK | \
+ CS35L45_ASP_RX1_SLOT_MASK)
+
+/* ASP_DATA_CONTROL1 */
+/* ASP_DATA_CONTROL5 */
+#define CS35L45_ASP_WL_SHIFT 0
+#define CS35L45_ASP_WL_MASK GENMASK(5, 0)
+
+/* HVLV_CONFIG */
+#define CS35L45_FORCE_LV_OPERATION 0x01
+#define CS35L45_FORCE_HV_OPERATION 0x02
+#define CS35L45_HVLV_OPERATION 0x03
+#define CS35L45_HVLV_MODE_SHIFT 0
+#define CS35L45_HVLV_MODE_MASK GENMASK(1, 0)
+
+/* AMP_PCM_CONTROL */
+#define CS35L45_AMP_VOL_PCM_SHIFT 0
+#define CS35L45_AMP_VOL_PCM_WIDTH 11
+
+/* AMP_PCM_HPF_TST */
+#define CS35l45_HPF_DEFAULT 0x00000000
+#define CS35L45_HPF_44P1 0x000108BD
+#define CS35L45_HPF_88P2 0x0001045F
+
+/* AMP_GAIN_PCM */
+#define CS35L45_AMP_GAIN_PCM_10DBV 0x00
+#define CS35L45_AMP_GAIN_PCM_13DBV 0x01
+#define CS35L45_AMP_GAIN_PCM_16DBV 0x02
+#define CS35L45_AMP_GAIN_PCM_19DBV 0x03
+
+#define CS35L45_AMP_GAIN_PCM_SHIFT 8
+#define CS35L45_AMP_GAIN_PCM_MASK GENMASK(9, 8)
+
+/* IRQ1_EINT_4 */
+#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1)
+#define CS35L45_OTP_BUSY_MASK BIT(0)
+
+/* GPIOX_CTRL1 */
+#define CS35L45_GPIO_DIR_SHIFT 31
+#define CS35L45_GPIO_DIR_MASK BIT(31)
+#define CS35L45_GPIO_LVL_SHIFT 15
+#define CS35L45_GPIO_LVL_MASK BIT(15)
+#define CS35L45_GPIO_OP_CFG_SHIFT 14
+#define CS35L45_GPIO_OP_CFG_MASK BIT(14)
+#define CS35L45_GPIO_POL_SHIFT 12
+#define CS35L45_GPIO_POL_MASK BIT(12)
+
+/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */
+#define CS35L45_GPIO_CTRL_SHIFT 20
+#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20)
+#define CS35L45_GPIO_INVERT_SHIFT 19
+#define CS35L45_GPIO_INVERT_MASK BIT(19)
+
+/* CS35L45_IRQ1_EINT_1 */
+#define CS35L45_BST_UVP_ERR_SHIFT 7
+#define CS35L45_BST_UVP_ERR_MASK BIT(7)
+#define CS35L45_BST_SHORT_ERR_SHIFT 8
+#define CS35L45_BST_SHORT_ERR_MASK BIT(8)
+#define CS35L45_TEMP_ERR_SHIFT 17
+#define CS35L45_TEMP_ERR_MASK BIT(17)
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT 22
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK BIT(22)
+#define CS35L45_UVLO_VDDBATT_ERR_SHIFT 29
+#define CS35L45_UVLO_VDDBATT_ERR_MASK BIT(29)
+#define CS35L45_AMP_SHORT_ERR_SHIFT 31
+#define CS35L45_AMP_SHORT_ERR_MASK BIT(31)
+
+/* CS35L45_IRQ1_EINT_2 */
+#define CS35L45_DSP_WDT_EXPIRE_SHIFT 4
+#define CS35L45_DSP_WDT_EXPIRE_MASK BIT(4)
+#define CS35L45_DSP_VIRT2_MBOX_SHIFT 21
+#define CS35L45_DSP_VIRT2_MBOX_MASK BIT(21)
+
+/* CS35L45_IRQ1_EINT_3 */
+#define CS35L45_PLL_LOCK_FLAG_SHIFT 1
+#define CS35L45_PLL_LOCK_FLAG_MASK BIT(1)
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT 4
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK BIT(4)
+#define CS35L45_AMP_CAL_ERR_SHIFT 25
+#define CS35L45_AMP_CAL_ERR_MASK BIT(25)
+
+/* CS35L45_IRQ1_EINT_18 */
+#define CS35L45_GLOBAL_ERROR_SHIFT 15
+#define CS35L45_GLOBAL_ERROR_MASK BIT(15)
+#define CS35L45_UVLO_VDDLV_ERR_SHIFT 16
+#define CS35L45_UVLO_VDDLV_ERR_MASK BIT(16)
+
+/* Mixer sources */
+#define CS35L45_PCM_SRC_MASK 0x7F
+#define CS35L45_PCM_SRC_ZERO 0x00
+#define CS35L45_PCM_SRC_ASP_RX1 0x08
+#define CS35L45_PCM_SRC_ASP_RX2 0x09
+#define CS35L45_PCM_SRC_VMON 0x18
+#define CS35L45_PCM_SRC_IMON 0x19
+#define CS35L45_PCM_SRC_ERR_VOL 0x20
+#define CS35L45_PCM_SRC_CLASSH_TGT 0x21
+#define CS35L45_PCM_SRC_VDD_BATTMON 0x28
+#define CS35L45_PCM_SRC_VDD_BSTMON 0x29
+#define CS35L45_PCM_SRC_DSP_TX1 0x32
+#define CS35L45_PCM_SRC_DSP_TX2 0x33
+#define CS35L45_PCM_SRC_TEMPMON 0x3A
+#define CS35L45_PCM_SRC_INTERPOLATOR 0x40
+#define CS35L45_PCM_SRC_IL_TARGET 0x48
+
+#define CS35L45_RESET_HOLD_US 2000
+#define CS35L45_RESET_US 2000
+#define CS35L45_POST_GLOBAL_EN_US 5000
+#define CS35L45_PRE_GLOBAL_DIS_US 3000
+
+/* WAKESRC_CTL */
+#define CS35L45_WKSRC_SYNC_GPIO1 BIT(0)
+#define CS35L45_WKSRC_INT_GPIO2 BIT(1)
+#define CS35L45_WKSRC_GPIO3 BIT(2)
+#define CS35L45_WKSRC_SPI BIT(3)
+#define CS35L45_WKSRC_I2C BIT(4)
+#define CS35L45_UPDT_WKCTL_SHIFT 15
+#define CS35L45_UPDT_WKCTL_MASK BIT(15)
+#define CS35L45_WKSRC_EN_SHIFT 8
+#define CS35L45_WKSRC_EN_MASK GENMASK(12, 8)
+#define CS35L45_WKSRC_POL_SHIFT 0
+#define CS35L45_WKSRC_POL_MASK GENMASK(3, 0)
+
+/* WAKEI2C_CTL */
+#define CS35L45_UPDT_WKI2C_SHIFT 15
+#define CS35L45_UPDT_WKI2C_MASK BIT(15)
+#define CS35L45_WKI2C_ADDR_SHIFT 0
+#define CS35L45_WKI2C_ADDR_MASK GENMASK(6, 0)
+
+#define CS35L45_SPI_MAX_FREQ 4000000
+
+enum cs35l45_cspl_mboxstate {
+ CSPL_MBOX_STS_RUNNING = 0,
+ CSPL_MBOX_STS_PAUSED = 1,
+ CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
+ CSPL_MBOX_STS_HIBERNATE = 3,
+};
+
+enum cs35l45_cspl_mboxcmd {
+ CSPL_MBOX_CMD_NONE = 0,
+ CSPL_MBOX_CMD_PAUSE = 1,
+ CSPL_MBOX_CMD_RESUME = 2,
+ CSPL_MBOX_CMD_REINIT = 3,
+ CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
+ CSPL_MBOX_CMD_HIBERNATE = 5,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
+ CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
+ CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
+};
+
+enum control_bus_type {
+ CONTROL_BUS_I2C = 0,
+ CONTROL_BUS_SPI = 1,
+};
+
+enum amp_mode {
+ AMP_MODE_SPK = 0,
+ AMP_MODE_RCV = 1,
+};
+
+#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE| \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define CS35L45_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+/*
+ * IRQs
+ */
+#define CS35L45_IRQ(_irq, _name, _hand) \
+ { \
+ .irq = CS35L45_ ## _irq ## _IRQ,\
+ .name = _name, \
+ .handler = _hand, \
+ }
+
+struct cs35l45_irq {
+ int irq;
+ const char *name;
+ irqreturn_t (*handler)(int irq, void *data);
+};
+
+#define CS35L45_REG_IRQ(_reg, _irq) \
+ [CS35L45_ ## _irq ## _IRQ] = { \
+ .reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1, \
+ .mask = CS35L45_ ## _irq ## _MASK \
+ }
+
+enum cs35l45_irq_list {
+ CS35L45_AMP_SHORT_ERR_IRQ,
+ CS35L45_UVLO_VDDBATT_ERR_IRQ,
+ CS35L45_BST_SHORT_ERR_IRQ,
+ CS35L45_BST_UVP_ERR_IRQ,
+ CS35L45_TEMP_ERR_IRQ,
+ CS35L45_AMP_CAL_ERR_IRQ,
+ CS35L45_UVLO_VDDLV_ERR_IRQ,
+ CS35L45_GLOBAL_ERROR_IRQ,
+ CS35L45_DSP_WDT_EXPIRE_IRQ,
+ CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
+ CS35L45_PLL_LOCK_FLAG_IRQ,
+ CS35L45_DSP_VIRT2_MBOX_IRQ,
+ CS35L45_NUM_IRQ
+};
+
+#define CS35L45_MBOX3_CMD_MASK 0xFF
+#define CS35L45_MBOX3_CMD_SHIFT 0
+#define CS35L45_MBOX3_DATA_MASK 0xFFFFFF00
+#define CS35L45_MBOX3_DATA_SHIFT 8
+
+enum mbox3_events {
+ EVENT_SPEAKER_STATUS = 0x66,
+ EVENT_BOOT_DONE = 0x67,
+};
+
+struct cs35l45_private {
+ struct wm_adsp dsp; /* needs to be first member */
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vdd_batt;
+ struct regulator *vdd_a;
+ bool initialized;
+ bool sysclk_set;
+ u8 slot_width;
+ u8 slot_count;
+ int amplifier_mode;
+ int irq_invert;
+ int irq;
+ unsigned int i2c_addr;
+ enum control_bus_type bus_type;
+ struct regmap_irq_chip_data *irq_data;
+};
+
+extern const struct dev_pm_ops cs35l45_pm_ops;
+extern const struct regmap_config cs35l45_i2c_regmap;
+extern const struct regmap_config cs35l45_spi_regmap;
+int cs35l45_apply_patch(struct cs35l45_private *cs35l45);
+int cs35l45_get_clk_freq_id(unsigned int freq);
+int cs35l45_probe(struct cs35l45_private *cs35l45);
+void cs35l45_remove(struct cs35l45_private *cs35l45);
+
+#endif /* CS35L45_H */
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
new file mode 100644
index 000000000000..0492ddc4102d
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_i2c_probe(struct i2c_client *client)
+{
+ unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client);
+ struct cs35l56_private *cs35l56;
+ struct device *dev = &client->dev;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = dev;
+ cs35l56->base.can_hibernate = true;
+
+ i2c_set_clientdata(client, cs35l56);
+
+ switch (id) {
+ case 0x3556:
+ regmap_config = &cs35l56_regmap_i2c;
+ cs35l56->base.type = 0x56;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_i2c;
+ cs35l56->base.type = 0x63;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n");
+ }
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(&cs35l56->base, client->irq);
+ if (ret < 0)
+ cs35l56_remove(cs35l56);
+
+ return ret;
+}
+
+static void cs35l56_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l56_private *cs35l56 = i2c_get_clientdata(client);
+
+ cs35l56_remove(cs35l56);
+}
+
+static const struct i2c_device_id cs35l56_id_i2c[] = {
+ { "cs35l56", 0x3556 },
+ { "cs35l63", 0x3563 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
+ { "CSC355C", 0x3556 },
+ { "CSC356C", 0x3563 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);
+#endif
+
+static struct i2c_driver cs35l56_i2c_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&cs35l56_pm_ops_i2c_spi),
+ .acpi_match_table = ACPI_PTR(cs35l56_asoc_acpi_match),
+ },
+ .id_table = cs35l56_id_i2c,
+ .probe = cs35l56_i2c_probe,
+ .remove = cs35l56_i2c_remove,
+};
+
+module_i2c_driver(cs35l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 I2C driver");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_CORE");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
new file mode 100644
index 000000000000..42d24ac2977f
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SoundWire binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/swab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "cs35l56.h"
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS35L56_SDW_ADDR_OFFSET 0x8000
+
+/* Cirrus bus bridge registers */
+#define CS35L56_SDW_MEM_ACCESS_STATUS 0xd0
+#define CS35L56_SDW_MEM_READ_DATA 0xd8
+
+#define CS35L56_SDW_LAST_LATE BIT(3)
+#define CS35L56_SDW_CMD_IN_PROGRESS BIT(2)
+#define CS35L56_SDW_RDATA_RDY BIT(0)
+
+#define CS35L56_LATE_READ_POLL_US 10
+#define CS35L56_LATE_READ_TIMEOUT_US 1000
+
+static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral,
+ unsigned int mask,
+ unsigned int match)
+{
+ int ret, val;
+
+ ret = read_poll_timeout(sdw_read_no_pm, val,
+ (val < 0) || ((val & mask) == match),
+ CS35L56_LATE_READ_POLL_US, CS35L56_LATE_READ_TIMEOUT_US,
+ false, peripheral, CS35L56_SDW_MEM_ACCESS_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg,
+ u8 *buf, size_t val_size)
+{
+ int ret, i;
+
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ for (i = 0; i < val_size; i += sizeof(u32)) {
+ /* Poll for bus bridge idle */
+ ret = cs35l56_sdw_poll_mem_status(peripheral,
+ CS35L56_SDW_CMD_IN_PROGRESS,
+ 0);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "!CMD_IN_PROGRESS fail: %d\n", ret);
+ return ret;
+ }
+
+ /* Reading LSByte triggers read of register to holding buffer */
+ sdw_read_no_pm(peripheral, reg + i);
+
+ /* Wait for data available */
+ ret = cs35l56_sdw_poll_mem_status(peripheral,
+ CS35L56_SDW_RDATA_RDY,
+ CS35L56_SDW_RDATA_RDY);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "RDATA_RDY fail: %d\n", ret);
+ return ret;
+ }
+
+ /* Read data from buffer */
+ ret = sdw_nread_no_pm(peripheral, CS35L56_SDW_MEM_READ_DATA,
+ sizeof(u32), &buf[i]);
+ if (ret) {
+ dev_err(&peripheral->dev, "Late read @%#x failed: %d\n", reg + i, ret);
+ return ret;
+ }
+
+ swab32s((u32 *)&buf[i]);
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf)
+{
+ int ret;
+
+ ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret);
+ return ret;
+ }
+
+ swab32s((u32 *)buf);
+
+ return 0;
+}
+
+static int cs35l56_sdw_read(void *context, const void *reg_buf,
+ const size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct sdw_slave *peripheral = context;
+ u8 *buf8 = val_buf;
+ unsigned int reg, bytes;
+ int ret;
+
+ reg = le32_to_cpu(*(const __le32 *)reg_buf);
+
+ if (cs35l56_is_otp_register(reg))
+ return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size);
+
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ if (val_size == 4)
+ return cs35l56_sdw_read_one(peripheral, reg, val_buf);
+
+ while (val_size) {
+ bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+ if (bytes > val_size)
+ bytes = val_size;
+
+ ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n",
+ reg, reg + bytes - 1, ret);
+ return ret;
+ }
+
+ swab32_array((u32 *)buf8, bytes / 4);
+ val_size -= bytes;
+ reg += bytes;
+ buf8 += bytes;
+ }
+
+ return 0;
+}
+
+static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes)
+{
+ u32 *dest32 = dest;
+ const u32 *src32 = src;
+
+ for (; nbytes > 0; nbytes -= 4)
+ *dest32++ = swab32(*src32++);
+}
+
+static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf)
+{
+ u32 val_le = swab32(*(u32 *)buf);
+ int ret;
+
+ ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_gather_write(void *context,
+ const void *reg_buf, size_t reg_size,
+ const void *val_buf, size_t val_size)
+{
+ struct sdw_slave *peripheral = context;
+ const u8 *src_be = val_buf;
+ u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */
+ unsigned int reg, bytes;
+ int ret;
+
+ reg = le32_to_cpu(*(const __le32 *)reg_buf);
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ if (val_size == 4)
+ return cs35l56_sdw_write_one(peripheral, reg, src_be);
+
+ while (val_size) {
+ bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+ if (bytes > val_size)
+ bytes = val_size;
+ if (bytes > sizeof(val_le_buf))
+ bytes = sizeof(val_le_buf);
+
+ cs35l56_swab_copy(val_le_buf, src_be, bytes);
+
+ ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n",
+ reg, reg + bytes - 1, ret);
+ return ret;
+ }
+
+ val_size -= bytes;
+ reg += bytes;
+ src_be += bytes;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size)
+{
+ const u8 *src_buf = val_buf;
+
+ /* First word of val_buf contains the destination address */
+ return cs35l56_sdw_gather_write(context, &src_buf[0], 4, &src_buf[4], val_size - 4);
+}
+
+/*
+ * Registers are big-endian on I2C and SPI but little-endian on SoundWire.
+ * Exported firmware controls are big-endian on I2C/SPI but little-endian on
+ * SoundWire. Firmware files are always big-endian and are opaque blobs.
+ * Present a big-endian regmap and hide the endianness swap, so that the ALSA
+ * byte controls always have the same byte order, and firmware file blobs
+ * can be written verbatim.
+ */
+static const struct regmap_bus cs35l56_regmap_bus_sdw = {
+ .read = cs35l56_sdw_read,
+ .write = cs35l56_sdw_write,
+ .gather_write = cs35l56_sdw_gather_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0);
+ if (ret < 0)
+ return ret;
+
+ cs35l56->sdw_unique_id = ret & 0xf;
+
+ return 0;
+}
+
+static void cs35l56_sdw_init(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ int ret;
+
+ pm_runtime_get_noresume(cs35l56->base.dev);
+
+ ret = cs35l56_sdw_get_unique_id(cs35l56);
+ if (ret)
+ goto out;
+
+ /* SoundWire UniqueId is used to index the calibration array */
+ if (cs35l56->base.cal_index < 0)
+ cs35l56->base.cal_index = cs35l56->sdw_unique_id;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret < 0) {
+ regcache_cache_only(cs35l56->base.regmap, true);
+ goto out;
+ }
+
+ /*
+ * cs35l56_init can return with !init_done if it triggered
+ * a soft reset.
+ */
+ if (cs35l56->base.init_done) {
+ /* Enable SoundWire interrupts */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ }
+
+out:
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
+ struct sdw_slave_intr_status *status)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ /* SoundWire core holds our pm_runtime when calling this function. */
+
+ dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port);
+
+ if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0)
+ return 0;
+
+ /*
+ * Prevent bus manager suspending and possibly issuing a
+ * bus-reset before the queued work has run.
+ */
+ pm_runtime_get_noresume(cs35l56->base.dev);
+
+ /*
+ * Mask and clear until it has been handled. The read of GEN_INT_STAT_1
+ * is required as per the SoundWire spec for interrupt status bits
+ * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1.
+ * None of the interrupts are time-critical so use the
+ * power-efficient queue.
+ */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work);
+
+ return 0;
+}
+
+static void cs35l56_sdw_irq_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ sdw_irq_work);
+
+ cs35l56_irq(-1, &cs35l56->base);
+
+ /* unmask interrupts */
+ if (!cs35l56->sdw_irq_no_unmask)
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ struct sdw_slave_prop *prop = &peripheral->prop;
+ struct sdw_dpn_prop *ports;
+
+ ports = devm_kcalloc(cs35l56->base.dev, 2, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT);
+ prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT);
+ prop->paging_support = true;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF;
+
+ /* DP1 - playback */
+ ports[0].num = CS35L56_SDW1_PLAYBACK_PORT;
+ ports[0].type = SDW_DPN_FULL;
+ ports[0].ch_prep_timeout = 10;
+ prop->sink_dpn_prop = &ports[0];
+
+ /* DP3 - capture */
+ ports[1].num = CS35L56_SDW1_CAPTURE_PORT;
+ ports[1].type = SDW_DPN_FULL;
+ ports[1].ch_prep_timeout = 10;
+ prop->src_dpn_prop = &ports[1];
+
+ return 0;
+}
+
+static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
+ enum sdw_slave_status status)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs35l56->base.dev, "%s: ATTACHED\n", __func__);
+ if (cs35l56->sdw_attached)
+ break;
+
+ if (!cs35l56->base.init_done || cs35l56->soft_resetting)
+ cs35l56_sdw_init(peripheral);
+
+ cs35l56->sdw_attached = true;
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs35l56->base.dev, "%s: UNATTACHED\n", __func__);
+ cs35l56->sdw_attached = false;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
+ enum sdw_clk_stop_mode mode,
+ enum sdw_clk_stop_type type)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ dev_dbg(cs35l56->base.dev, "%s: mode:%d type:%d\n", __func__, mode, type);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops cs35l56_sdw_ops = {
+ .read_prop = cs35l56_sdw_read_prop,
+ .interrupt_callback = cs35l56_sdw_interrupt,
+ .update_status = cs35l56_sdw_update_status,
+#ifdef DEBUG
+ .clk_stop = cs35l56_sdw_clk_stop,
+#endif
+};
+
+static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56)
+{
+ struct sdw_slave *peripheral = cs35l56->sdw_peripheral;
+
+ if (peripheral->unattach_request) {
+ /* Cannot access registers until bus is re-initialized. */
+ dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->base.dev, "initialization_complete timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ peripheral->unattach_request = 0;
+
+ /*
+ * Don't call regcache_mark_dirty(), we can't be sure that the
+ * Manager really did issue a Bus Reset.
+ */
+ }
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->base.init_done)
+ return 0;
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ if (!cs35l56->base.init_done)
+ return 0;
+
+ ret = cs35l56_sdw_handle_unattach(cs35l56);
+ if (ret < 0)
+ return ret;
+
+ ret = cs35l56_runtime_resume_common(&cs35l56->base, true);
+ if (ret)
+ return ret;
+
+ /* Re-enable SoundWire interrupts */
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->base.init_done)
+ return 0;
+
+ /*
+ * Disable SoundWire interrupts.
+ * Flush - don't cancel because that could leave an unbalanced pm_runtime_get.
+ */
+ cs35l56->sdw_irq_no_unmask = true;
+ flush_work(&cs35l56->sdw_irq_work);
+
+ /* Mask interrupts and flush in case sdw_irq_work was queued again */
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ flush_work(&cs35l56->sdw_irq_work);
+
+ return cs35l56_system_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ cs35l56->sdw_irq_no_unmask = false;
+ /* runtime_resume re-enables the interrupt */
+
+ return cs35l56_system_resume(dev);
+}
+
+static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+ struct device *dev = &peripheral->dev;
+ struct cs35l56_private *cs35l56;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = dev;
+ cs35l56->sdw_peripheral = peripheral;
+ cs35l56->sdw_link_num = peripheral->bus->link_id;
+ INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
+
+ dev_set_drvdata(dev, cs35l56);
+
+ switch ((unsigned int)id->driver_data) {
+ case 0x3556:
+ case 0x3557:
+ regmap_config = &cs35l56_regmap_sdw;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_sdw;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ cs35l56->base.type = ((unsigned int)id->driver_data) & 0xff;
+
+ cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+ peripheral, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ return dev_err_probe(dev, ret, "Failed to allocate register map\n");
+ }
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(cs35l56->base.regmap, true);
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs35l56_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ /* Disable SoundWire interrupts */
+ cs35l56->sdw_irq_no_unmask = true;
+ cancel_work_sync(&cs35l56->sdw_irq_work);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+
+ cs35l56_remove(cs35l56);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l56_sdw_pm = {
+ SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+ /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */
+};
+
+static const struct sdw_device_id cs35l56_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
+
+static struct sdw_driver cs35l56_sdw_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&cs35l56_sdw_pm),
+ },
+ .probe = cs35l56_sdw_probe,
+ .remove = cs35l56_sdw_remove,
+ .ops = &cs35l56_sdw_ops,
+ .id_table = cs35l56_sdw_id,
+};
+
+module_sdw_driver(cs35l56_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SoundWire driver");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_CORE");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
new file mode 100644
index 000000000000..60100c8f8c95
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -0,0 +1,1740 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Components shared between ASoC and HDA CS35L56 drivers
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/array_size.h>
+#include <linux/cleanup.h>
+#include <linux/debugfs.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kstrtox.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/string_choices.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#include "cs35l56.h"
+
+static const struct reg_sequence cs35l56_patch[] = {
+ /*
+ * Firmware can change these to non-defaults to satisfy SDCA.
+ * Ensure that they are at known defaults.
+ */
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1_CONTROL1, 0x00000028 },
+ { CS35L56_ASP1_CONTROL2, 0x18180200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000002 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+ { CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
+};
+
+static const struct reg_sequence cs35l56_patch_fw[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+static const struct reg_sequence cs35l63_patch_fw[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
+ ARRAY_SIZE(cs35l56_patch));
+ if (ret)
+ return ret;
+
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw,
+ ARRAY_SIZE(cs35l56_patch_fw));
+ break;
+ case 0x63:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw,
+ ARRAY_SIZE(cs35l63_patch_fw));
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED");
+
+static const struct reg_default cs35l56_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1_CONTROL1, 0x00000028 },
+ { CS35L56_ASP1_CONTROL2, 0x18180200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000002 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+ { CS35L56_IRQ1_MASK_1, 0x83ffffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0xfc000fff },
+ { CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+static const struct reg_default cs35l63_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1_CONTROL1, 0x00000028 },
+ { CS35L56_ASP1_CONTROL2, 0x18180200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000002 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+ { CS35L56_IRQ1_MASK_1, 0x8003ffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0x8c000fff },
+ { CS35L56_IRQ1_MASK_18, 0x0760f000 },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+static bool cs35l56_is_dsp_memory(unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+ case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095:
+ case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191:
+ case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+ case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070:
+ case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141:
+ case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DEVID:
+ case CS35L56_REVID:
+ case CS35L56_RELID:
+ case CS35L56_OTPID:
+ case CS35L56_SFT_RESET:
+ case CS35L56_GLOBAL_ENABLES:
+ case CS35L56_BLOCK_ENABLES:
+ case CS35L56_BLOCK_ENABLES2:
+ case CS35L56_REFCLK_INPUT:
+ case CS35L56_GLOBAL_SAMPLE_RATE:
+ case CS35L56_OTP_MEM_53:
+ case CS35L56_OTP_MEM_54:
+ case CS35L56_OTP_MEM_55:
+ case CS35L56_ASP1_ENABLES1:
+ case CS35L56_ASP1_CONTROL1:
+ case CS35L56_ASP1_CONTROL2:
+ case CS35L56_ASP1_CONTROL3:
+ case CS35L56_ASP1_FRAME_CONTROL1:
+ case CS35L56_ASP1_FRAME_CONTROL5:
+ case CS35L56_ASP1_DATA_CONTROL1:
+ case CS35L56_ASP1_DATA_CONTROL5:
+ case CS35L56_DACPCM1_INPUT:
+ case CS35L56_DACPCM2_INPUT:
+ case CS35L56_ASP1TX1_INPUT:
+ case CS35L56_ASP1TX2_INPUT:
+ case CS35L56_ASP1TX3_INPUT:
+ case CS35L56_ASP1TX4_INPUT:
+ case CS35L56_DSP1RX1_INPUT:
+ case CS35L56_DSP1RX2_INPUT:
+ case CS35L56_SWIRE_DP3_CH1_INPUT:
+ case CS35L56_SWIRE_DP3_CH2_INPUT:
+ case CS35L56_SWIRE_DP3_CH3_INPUT:
+ case CS35L56_SWIRE_DP3_CH4_INPUT:
+ case CS35L56_IRQ1_CFG:
+ case CS35L56_IRQ1_STATUS:
+ case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+ case CS35L56_IRQ1_EINT_18:
+ case CS35L56_IRQ1_EINT_20:
+ case CS35L56_IRQ1_MASK_1:
+ case CS35L56_IRQ1_MASK_2:
+ case CS35L56_IRQ1_MASK_4:
+ case CS35L56_IRQ1_MASK_8:
+ case CS35L56_IRQ1_MASK_18:
+ case CS35L56_IRQ1_MASK_20:
+ case CS35L56_MIXER_NGATE_CH1_CFG:
+ case CS35L56_MIXER_NGATE_CH2_CFG:
+ case CS35L56_DSP_VIRTUAL1_MBOX_1:
+ case CS35L56_DSP_VIRTUAL1_MBOX_2:
+ case CS35L56_DSP_VIRTUAL1_MBOX_3:
+ case CS35L56_DSP_VIRTUAL1_MBOX_4:
+ case CS35L56_DSP_VIRTUAL1_MBOX_5:
+ case CS35L56_DSP_VIRTUAL1_MBOX_6:
+ case CS35L56_DSP_VIRTUAL1_MBOX_7:
+ case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DIE_STS1:
+ case CS35L56_DIE_STS2:
+ case CS35L56_DSP_RESTRICT_STS1:
+ case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS35L56_DSP1_SCRATCH1:
+ case CS35L56_DSP1_SCRATCH2:
+ case CS35L56_DSP1_SCRATCH3:
+ case CS35L56_DSP1_SCRATCH4:
+ return true;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+ case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+ case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l56_common_volatile_reg(unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DEVID:
+ case CS35L56_REVID:
+ case CS35L56_RELID:
+ case CS35L56_OTPID:
+ case CS35L56_SFT_RESET:
+ case CS35L56_GLOBAL_ENABLES: /* owned by firmware */
+ case CS35L56_BLOCK_ENABLES: /* owned by firmware */
+ case CS35L56_BLOCK_ENABLES2: /* owned by firmware */
+ case CS35L56_REFCLK_INPUT: /* owned by firmware */
+ case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */
+ case CS35L56_DACPCM1_INPUT: /* owned by firmware */
+ case CS35L56_DACPCM2_INPUT: /* owned by firmware */
+ case CS35L56_DSP1RX1_INPUT: /* owned by firmware */
+ case CS35L56_DSP1RX2_INPUT: /* owned by firmware */
+ case CS35L56_IRQ1_STATUS:
+ case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+ case CS35L56_IRQ1_EINT_18:
+ case CS35L56_IRQ1_EINT_20:
+ case CS35L56_MIXER_NGATE_CH1_CFG:
+ case CS35L56_MIXER_NGATE_CH2_CFG:
+ case CS35L56_DSP_VIRTUAL1_MBOX_1:
+ case CS35L56_DSP_VIRTUAL1_MBOX_2:
+ case CS35L56_DSP_VIRTUAL1_MBOX_3:
+ case CS35L56_DSP_VIRTUAL1_MBOX_4:
+ case CS35L56_DSP_VIRTUAL1_MBOX_5:
+ case CS35L56_DSP_VIRTUAL1_MBOX_6:
+ case CS35L56_DSP_VIRTUAL1_MBOX_7:
+ case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DSP_RESTRICT_STS1:
+ case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS35L56_DSP1_SCRATCH1:
+ case CS35L56_DSP1_SCRATCH2:
+ case CS35L56_DSP1_SCRATCH3:
+ case CS35L56_DSP1_SCRATCH4:
+ return true;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_MAIN_RENDER_USER_MUTE:
+ case CS35L56_MAIN_RENDER_USER_VOLUME:
+ case CS35L56_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static bool cs35l63_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L63_MAIN_RENDER_USER_MUTE:
+ case CS35L63_MAIN_RENDER_USER_VOLUME:
+ case CS35L63_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static const struct cs35l56_fw_reg cs35l56_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+static const struct cs35l56_fw_reg cs35l56_b2_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_B2_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_B2_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+static const struct cs35l56_fw_reg cs35l63_fw_reg = {
+ .fw_ver = CS35L63_DSP1_FW_VER,
+ .halo_state = CS35L63_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L63_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L63_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L63_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L63_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L63_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L63_MAIN_POSTURE_NUMBER,
+};
+
+static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base)
+{
+ switch (cs35l56_base->type) {
+ default:
+ switch (cs35l56_base->rev) {
+ case 0xb0:
+ cs35l56_base->fw_reg = &cs35l56_fw_reg;
+ break;
+ default:
+ cs35l56_base->fw_reg = &cs35l56_b2_fw_reg;
+ break;
+ }
+ break;
+ case 0x63:
+ cs35l56_base->fw_reg = &cs35l63_fw_reg;
+ break;
+ }
+}
+
+int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command)
+{
+ unsigned int val;
+ int ret;
+
+ regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ val, (val == 0),
+ CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
+ if (ret) {
+ dev_warn(cs35l56_base->dev, "MBOX command %#x failed: %d\n", command, ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int val;
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->pm_cur_stat,
+ val, (val == CS35L56_HALO_STATE_SHUTDOWN),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(cs35l56_base->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
+ val, ret);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val = 0;
+ int read_ret, poll_ret;
+
+ /*
+ * The regmap must remain in cache-only until the chip has
+ * booted, so use a bypassed read of the status register.
+ */
+ poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret,
+ (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US,
+ false,
+ cs35l56_base->regmap,
+ cs35l56_base->fw_reg->halo_state,
+ &val);
+
+ if (poll_ret) {
+ dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n",
+ read_ret, val);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_for_firmware_boot, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_wait_control_port_ready(void)
+{
+ /* Wait for control port to be ready (datasheet tIRS). */
+ usleep_range(CS35L56_CONTROL_PORT_READY_US, 2 * CS35L56_CONTROL_PORT_READY_US);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_control_port_ready, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_wait_min_reset_pulse(void)
+{
+ /* Satisfy minimum reset pulse width spec */
+ usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, "SND_SOC_CS35L56_SHARED");
+
+static const struct {
+ u32 addr;
+ u32 value;
+} cs35l56_spi_system_reset_stages[] = {
+ { .addr = CS35L56_DSP_VIRTUAL1_MBOX_1, .value = CS35L56_MBOX_CMD_SYSTEM_RESET },
+ /* The next write is necessary to delimit the soft reset */
+ { .addr = CS35L56_DSP_MBOX_1_RAW, .value = CS35L56_MBOX_CMD_PING },
+};
+
+static void cs35l56_spi_issue_bus_locked_reset(struct cs35l56_base *cs35l56_base,
+ struct spi_device *spi)
+{
+ struct cs35l56_spi_payload *buf = cs35l56_base->spi_payload_buf;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = sizeof(*buf),
+ };
+ struct spi_message m;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_spi_system_reset_stages); i++) {
+ buf->addr = cpu_to_be32(cs35l56_spi_system_reset_stages[i].addr);
+ buf->value = cpu_to_be32(cs35l56_spi_system_reset_stages[i].value);
+ spi_message_init_with_transfers(&m, &t, 1);
+ ret = spi_sync_locked(spi, &m);
+ if (ret)
+ dev_warn(cs35l56_base->dev, "spi_sync failed: %d\n", ret);
+
+ usleep_range(CS35L56_SPI_RESET_TO_PORT_READY_US,
+ 2 * CS35L56_SPI_RESET_TO_PORT_READY_US);
+ }
+}
+
+static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
+{
+ struct spi_device *spi = to_spi_device(cs35l56_base->dev);
+ unsigned int val;
+ int read_ret, ret;
+
+ /*
+ * There must not be any other SPI bus activity while the amp is
+ * soft-resetting.
+ */
+ ret = spi_bus_lock(spi->controller);
+ if (ret) {
+ dev_warn(cs35l56_base->dev, "spi_bus_lock failed: %d\n", ret);
+ return;
+ }
+
+ cs35l56_spi_issue_bus_locked_reset(cs35l56_base, spi);
+ spi_bus_unlock(spi->controller);
+
+ /*
+ * Check firmware boot by testing for a response in MBOX_2.
+ * HALO_STATE cannot be trusted yet because the reset sequence
+ * can leave it with stale state. But MBOX is reset.
+ * The regmap must remain in cache-only until the chip has
+ * booted, so use a bypassed read.
+ */
+ ret = read_poll_timeout(regmap_read_bypassed, read_ret,
+ (val > 0) && (val < 0xffffffff),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US,
+ false,
+ cs35l56_base->regmap,
+ CS35L56_DSP_VIRTUAL1_MBOX_2,
+ &val);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "SPI reboot timed out(%d): MBOX2=%#x\n",
+ read_ret, val);
+ }
+}
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static const struct reg_sequence cs35l56_b2_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_B2_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static const struct reg_sequence cs35l63_system_reset_seq[] = {
+ REG_SEQ0(CS35L63_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
+{
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled system reset sequence below.
+ */
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ if (cs35l56_is_spi(cs35l56_base)) {
+ cs35l56_spi_system_reset(cs35l56_base);
+ return;
+ }
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ switch (cs35l56_base->rev) {
+ case 0xb0:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+ break;
+ default:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_b2_system_reset_seq,
+ ARRAY_SIZE(cs35l56_b2_system_reset_seq));
+ break;
+ }
+ break;
+ case 0x63:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l63_system_reset_seq,
+ ARRAY_SIZE(cs35l63_system_reset_seq));
+ break;
+ default:
+ break;
+ }
+
+ /* On SoundWire the registers won't be accessible until it re-enumerates. */
+ if (is_soundwire)
+ return;
+
+ cs35l56_wait_control_port_ready();
+
+ /* Leave in cache-only. This will be revoked when the chip has rebooted. */
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq)
+{
+ int ret;
+
+ if (irq < 1)
+ return 0;
+
+ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs35l56", cs35l56_base);
+ if (!ret)
+ cs35l56_base->irq = irq;
+ else
+ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED");
+
+irqreturn_t cs35l56_irq(int irq, void *data)
+{
+ struct cs35l56_base *cs35l56_base = data;
+ unsigned int status1 = 0, status8 = 0, status20 = 0;
+ unsigned int mask1, mask8, mask20;
+ unsigned int val;
+ int rv;
+
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!cs35l56_base->init_done)
+ return IRQ_NONE;
+
+ mutex_lock(&cs35l56_base->irq_lock);
+
+ rv = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (rv < 0) {
+ dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv);
+ goto err_unlock;
+ }
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+ if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
+ dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n");
+ goto err;
+ }
+
+ /* Ack interrupts */
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, &status1);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, &mask1);
+ status1 &= ~mask1;
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, status1);
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, &status8);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, &mask8);
+ status8 &= ~mask8;
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, status8);
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_20, &status20);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, &mask20);
+ status20 &= ~mask20;
+ /* We don't want EINT20 but they default to unmasked: force mask */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+
+ dev_dbg(cs35l56_base->dev, "%s: %#x %#x\n", __func__, status1, status8);
+
+ /* Check to see if unmasked bits are active */
+ if (!status1 && !status8 && !status20)
+ goto err;
+
+ if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
+ dev_crit(cs35l56_base->dev, "Amp short error\n");
+
+ if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
+ dev_crit(cs35l56_base->dev, "Overtemp error\n");
+
+ ret = IRQ_HANDLED;
+
+err:
+ pm_runtime_put(cs35l56_base->dev);
+err_unlock:
+ mutex_unlock(&cs35l56_base->irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+ int ret;
+
+ /*
+ * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
+ * can't be used here to test for memory retention.
+ * Assume that tuning must be re-loaded.
+ */
+ if (cs35l56_base->secured)
+ return true;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Failed to runtime_get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts,
+ &val);
+ if (ret)
+ dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ else
+ ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+ pm_runtime_put_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_is_fw_reload_needed, "SND_SOC_CS35L56_SHARED");
+
+static const struct reg_sequence cs35l56_hibernate_seq[] = {
+ /* This must be the last register access */
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE),
+};
+
+static void cs35l56_issue_wake_event(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+
+ /*
+ * Dummy transactions to trigger I2C/SPI auto-wake. Issue two
+ * transactions to meet the minimum required time from the rising edge
+ * to the last falling edge of wake.
+ *
+ * It uses bypassed read because we must wake the chip before
+ * disabling regmap cache-only.
+ */
+ regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+
+ usleep_range(CS35L56_WAKE_HOLD_TIME_US, 2 * CS35L56_WAKE_HOLD_TIME_US);
+
+ regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+
+ cs35l56_wait_control_port_ready();
+}
+
+static int cs35l56_wait_for_ps3(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->transducer_actual_ps,
+ val, (val >= CS35L56_PS3),
+ CS35L56_PS3_POLL_US,
+ CS35L56_PS3_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56_base->dev, "PS3 wait failed: %d\n", ret);
+
+ return ret;
+}
+
+int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base)
+{
+ if (!cs35l56_base->init_done)
+ return 0;
+
+ /* Firmware must have entered a power-save state */
+ cs35l56_wait_for_ps3(cs35l56_base);
+
+ /* Clear BOOT_DONE so it can be used to detect a reboot */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
+
+ if (!cs35l56_base->can_hibernate) {
+ regcache_cache_only(cs35l56_base->regmap, true);
+ dev_dbg(cs35l56_base->dev, "Suspended: no hibernate");
+
+ return 0;
+ }
+
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled hibernate sequence below.
+ */
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ dev_dbg(cs35l56_base->dev, "Suspended: hibernate");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend_common, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire)
+{
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56_base->init_done)
+ return 0;
+
+ if (!cs35l56_base->can_hibernate)
+ goto out_sync;
+
+ /* Must be done before releasing cache-only */
+ if (!is_soundwire)
+ cs35l56_issue_wake_event(cs35l56_base);
+
+out_sync:
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret);
+ goto err;
+ }
+
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ /* BOOT_DONE will be 1 if the amp reset */
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, &val);
+ if (val & CS35L56_OTP_BOOT_DONE_MASK) {
+ dev_dbg(cs35l56_base->dev, "Registers reset in suspend\n");
+ regcache_mark_dirty(cs35l56_base->regmap);
+ }
+
+ regcache_sync(cs35l56_base->regmap);
+
+ dev_dbg(cs35l56_base->dev, "Resumed");
+
+ return 0;
+
+err:
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, "SND_SOC_CS35L56_SHARED");
+
+static const struct cs_dsp_region cs35l56_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 },
+ { .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 },
+ { .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 },
+};
+
+void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ cs_dsp->num = 1;
+ cs_dsp->type = WMFW_HALO;
+ cs_dsp->rev = 0;
+ cs_dsp->dev = cs35l56_base->dev;
+ cs_dsp->regmap = cs35l56_base->regmap;
+ cs_dsp->base = CS35L56_DSP1_CORE_BASE;
+ cs_dsp->base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
+ cs_dsp->mem = cs35l56_dsp1_regions;
+ cs_dsp->num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
+ cs_dsp->no_core_startstop = true;
+
+ cs35l56_base->dsp = cs_dsp;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, "SND_SOC_CS35L56_SHARED");
+
+struct cs35l56_pte {
+ u8 x;
+ u8 wafer_id;
+ u8 pte[2];
+ u8 lot[3];
+ u8 y;
+ u8 unused[3];
+ u8 dvs;
+} __packed;
+static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0);
+
+static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base)
+{
+ struct cs35l56_pte pte;
+ u64 unique_id;
+ int ret;
+
+ ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret);
+ return ret;
+ }
+
+ unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16);
+ unique_id <<= 32;
+ unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) |
+ ((u32)pte.dvs << 24);
+
+ cs35l56_base->silicon_uid = unique_id;
+
+ return 0;
+}
+
+static int cs35l63_read_silicon_uid(struct cs35l56_base *cs35l56_base)
+{
+ u32 tmp[2];
+ u64 unique_id;
+ int ret;
+
+ ret = regmap_bulk_read(cs35l56_base->regmap, CS35L56_DIE_STS1, tmp, ARRAY_SIZE(tmp));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Cannot obtain CS35L56_DIE_STS: %d\n", ret);
+ return ret;
+ }
+
+ unique_id = tmp[1];
+ unique_id <<= 32;
+ unique_id |= tmp[0];
+
+ cs35l56_base->silicon_uid = unique_id;
+
+ return 0;
+}
+
+/* Firmware calibration controls */
+const struct cirrus_amp_cal_controls cs35l56_calibration_controls = {
+ .alg_id = 0x9f210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, "SND_SOC_CS35L56_SHARED");
+
+static const struct cirrus_amp_cal_controls cs35l63_calibration_controls = {
+ .alg_id = 0xbf210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+
+int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+
+ /* Driver can't apply calibration to a secured part, so skip */
+ if (cs35l56_base->secured)
+ return 0;
+
+ ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev,
+ cs35l56_base->silicon_uid,
+ cs35l56_base->cal_index,
+ &cs35l56_base->cal_data);
+
+ /* Only return an error status if probe should be aborted */
+ if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+ return 0;
+
+ if (ret < 0)
+ return ret;
+
+ cs35l56_base->cal_data_valid = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base,
+ const struct cirrus_amp_cal_data *data)
+{
+
+ /* Ignore if it is empty */
+ if (!data->calTime[0] && !data->calTime[1])
+ return -ENODATA;
+
+ if (cs_amp_cal_target_u64(data) != cs35l56_base->silicon_uid) {
+ dev_err(cs35l56_base->dev, "cal_data not for this silicon ID\n");
+ return -EINVAL;
+ }
+
+ cs35l56_base->cal_data = *data;
+ cs35l56_base->cal_data_valid = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_stash_calibration, "SND_SOC_CS35L56_SHARED");
+
+static int cs35l56_perform_calibration(struct cs35l56_base *cs35l56_base)
+{
+ const struct cirrus_amp_cal_controls *calibration_controls =
+ cs35l56_base->calibration_controls;
+ struct cs_dsp *dsp = cs35l56_base->dsp;
+ struct cirrus_amp_cal_data cal_data;
+ struct cs_dsp_coeff_ctl *ctl;
+ bool ngate_ch1_was_enabled = false;
+ bool ngate_ch2_was_enabled = false;
+ int cali_norm_en_alg_id, cali_norm_en_mem;
+ int ret;
+ __be32 val;
+
+ if (cs35l56_base->silicon_uid == 0) {
+ dev_err(cs35l56_base->dev, "Cannot calibrate: no silicon UID\n");
+ return -ENXIO;
+ }
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ if (cs35l56_base->rev < 0xb2) {
+ cali_norm_en_alg_id = 0x9f22f;
+ cali_norm_en_mem = WMFW_ADSP2_YM;
+ } else {
+ cali_norm_en_alg_id = 0x9f210;
+ cali_norm_en_mem = WMFW_ADSP2_XM;
+ }
+ break;
+ default:
+ cali_norm_en_alg_id = 0xbf210;
+ cali_norm_en_mem = WMFW_ADSP2_XM;
+ break;
+ }
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_wait_for_ps3(cs35l56_base);
+ if (ret) {
+ ret = -EBUSY;
+ goto err_pm_put;
+ }
+
+ regmap_update_bits_check(cs35l56_base->regmap, CS35L56_MIXER_NGATE_CH1_CFG,
+ CS35L56_AUX_NGATE_CHn_EN, 0, &ngate_ch1_was_enabled);
+ regmap_update_bits_check(cs35l56_base->regmap, CS35L56_MIXER_NGATE_CH2_CFG,
+ CS35L56_AUX_NGATE_CHn_EN, 0, &ngate_ch2_was_enabled);
+
+ scoped_guard(mutex, &dsp->pwr_lock) {
+ ctl = cs_dsp_get_ctl(dsp,
+ calibration_controls->status,
+ calibration_controls->mem_region,
+ calibration_controls->alg_id);
+ if (!ctl) {
+ dev_err(cs35l56_base->dev, "Could not get %s control\n",
+ calibration_controls->status);
+ ret = -EIO;
+ goto err;
+ }
+
+ val = cpu_to_be32(0);
+ ret = cs_dsp_coeff_write_ctrl(cs_dsp_get_ctl(dsp,
+ "CALI_NORM_EN",
+ cali_norm_en_mem,
+ cali_norm_en_alg_id),
+ 0, &val, sizeof(val));
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Could not write %s: %d\n", "CALI_NORM_EN", ret);
+ ret = -EIO;
+ goto err;
+ }
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_AUDIO_CALIBRATION);
+ if (ret) {
+ ret = -EIO;
+ goto err;
+ }
+
+ if (read_poll_timeout(cs_dsp_coeff_read_ctrl, ret,
+ (val == cpu_to_be32(1)),
+ CS35L56_CALIBRATION_POLL_US,
+ CS35L56_CALIBRATION_TIMEOUT_US,
+ true,
+ ctl, 0, &val, sizeof(val))) {
+ dev_err(cs35l56_base->dev, "Calibration timed out (CAL_STATUS: %u)\n",
+ be32_to_cpu(val));
+ switch (be32_to_cpu(val)) {
+ case CS35L56_CAL_STATUS_OUT_OF_RANGE:
+ ret = -ERANGE;
+ goto err;
+ default:
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+ }
+ }
+
+ cs35l56_base->cal_data_valid = false;
+ memset(&cal_data, 0, sizeof(cal_data));
+ ret = cs_amp_read_cal_coeffs(dsp, calibration_controls, &cal_data);
+ if (ret) {
+ ret = -EIO;
+ goto err;
+ }
+
+ dev_info(cs35l56_base->dev, "Cal status:%d calR:%d ambient:%d\n",
+ cal_data.calStatus, cal_data.calR, cal_data.calAmbient);
+
+ cal_data.calTarget[0] = (u32)cs35l56_base->silicon_uid;
+ cal_data.calTarget[1] = (u32)(cs35l56_base->silicon_uid >> 32);
+ cs35l56_base->cal_data = cal_data;
+ cs35l56_base->cal_data_valid = true;
+
+ ret = 0;
+
+err:
+ if (ngate_ch1_was_enabled) {
+ regmap_set_bits(cs35l56_base->regmap, CS35L56_MIXER_NGATE_CH1_CFG,
+ CS35L56_AUX_NGATE_CHn_EN);
+ }
+ if (ngate_ch2_was_enabled) {
+ regmap_set_bits(cs35l56_base->regmap, CS35L56_MIXER_NGATE_CH2_CFG,
+ CS35L56_AUX_NGATE_CHn_EN);
+ }
+err_pm_put:
+ pm_runtime_put(cs35l56_base->dev);
+
+ return ret;
+}
+
+ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base,
+ const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ static const char * const options[] = { "factory", "store_uefi" };
+ char buf[11] = { 0 };
+ int num_amps, ret;
+
+ if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS_COMMON))
+ return -ENXIO;
+
+ if (*ppos)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, from, count);
+ if (ret < 0)
+ return ret;
+
+ switch (sysfs_match_string(options, buf)) {
+ case 0:
+ ret = cs35l56_perform_calibration(cs35l56_base);
+ if (ret < 0)
+ return ret;
+ break;
+ case 1:
+ if (!cs35l56_base->cal_data_valid)
+ return -ENODATA;
+
+ num_amps = cs35l56_base->num_amps;
+ if (num_amps == 0)
+ num_amps = -1;
+
+ ret = cs_amp_set_efi_calibration_data(cs35l56_base->dev,
+ cs35l56_base->cal_index,
+ num_amps,
+ &cs35l56_base->cal_data);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return count;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_calibrate_debugfs_write, "SND_SOC_CS35L56_SHARED");
+
+ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base,
+ const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ unsigned long val;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS_COMMON))
+ return -ENXIO;
+
+ if (*ppos)
+ return -EINVAL;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = kstrtoul_from_user(from, count, 10, &val);
+ if (ret < 0)
+ goto out;
+
+ ret = cs_amp_write_ambient_temp(cs35l56_base->dsp, cs35l56_base->calibration_controls, val);
+ if (ret)
+ ret = -EIO;
+out:
+ pm_runtime_put(cs35l56_base->dev);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_cal_ambient_debugfs_write, "SND_SOC_CS35L56_SHARED");
+
+ssize_t cs35l56_cal_data_debugfs_read(struct cs35l56_base *cs35l56_base,
+ char __user *to, size_t count,
+ loff_t *ppos)
+{
+ if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS_COMMON))
+ return -ENXIO;
+
+ if (!cs35l56_base->cal_data_valid)
+ return 0;
+
+ return simple_read_from_buffer(to, count, ppos, &cs35l56_base->cal_data,
+ sizeof(cs35l56_base->cal_data));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_cal_data_debugfs_read, "SND_SOC_CS35L56_SHARED");
+
+ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
+ const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ struct cirrus_amp_cal_data cal_data;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS_COMMON))
+ return -ENXIO;
+
+ /* Only allow a full blob to be written */
+ if (*ppos || (count != sizeof(cal_data)))
+ return -EMSGSIZE;
+
+ ret = simple_write_to_buffer(&cal_data, sizeof(cal_data), ppos, from, count);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_stash_calibration(cs35l56_base, &cal_data);
+ if (ret)
+ return ret;
+
+ return count;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_cal_data_debugfs_write, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base,
+ const struct cs35l56_cal_debugfs_fops *fops)
+{
+ if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS_COMMON))
+ return;
+
+ cs35l56_base->debugfs = cs_amp_create_debugfs(cs35l56_base->dev);
+
+ debugfs_create_file("calibrate",
+ 0200, cs35l56_base->debugfs, cs35l56_base,
+ &fops->calibrate);
+ debugfs_create_file("cal_temperature",
+ 0200, cs35l56_base->debugfs, cs35l56_base,
+ &fops->cal_temperature);
+ debugfs_create_file("cal_data",
+ 0644, cs35l56_base->debugfs, cs35l56_base,
+ &fops->cal_data);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_create_cal_debugfs, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base)
+{
+ debugfs_remove_recursive(cs35l56_base->debugfs);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED");
+
+const char * const cs35l56_cal_set_status_text[] = {
+ "Unknown", "Default", "Set",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_cal_set_status_text, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct cs_dsp *dsp = cs35l56_base->dsp;
+ __be32 cal_set_status_be;
+ int alg_id;
+ int ret;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ alg_id = 0x9f210;
+ break;
+ default:
+ alg_id = 0xbf210;
+ break;
+ }
+
+ scoped_guard(mutex, &dsp->pwr_lock) {
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp,
+ "CAL_SET_STATUS",
+ WMFW_ADSP2_YM, alg_id),
+ 0, &cal_set_status_be,
+ sizeof(cal_set_status_be));
+ }
+ if (ret) {
+ uvalue->value.enumerated.item[0] = CS35L56_CAL_SET_STATUS_UNKNOWN;
+ return 0;
+ }
+
+ switch (be32_to_cpu(cal_set_status_be)) {
+ case CS35L56_CAL_SET_STATUS_DEFAULT:
+ case CS35L56_CAL_SET_STATUS_SET:
+ uvalue->value.enumerated.item[0] = be32_to_cpu(cal_set_status_be);
+ return 0;
+ default:
+ uvalue->value.enumerated.item[0] = CS35L56_CAL_SET_STATUS_UNKNOWN;
+ return 0;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_cal_set_status_get, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
+ bool *fw_missing, unsigned int *fw_version)
+{
+ unsigned int prot_status;
+ int ret;
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts, &prot_status);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
+ return ret;
+ }
+
+ *fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->fw_ver, fw_version);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int firmware_version;
+ bool firmware_missing;
+ int ret;
+
+ ret = cs35l56_read_prot_status(cs35l56_base, &firmware_missing, &firmware_version);
+ if (ret)
+ return;
+
+ if (!firmware_missing)
+ return;
+
+ dev_warn(cs35l56_base->dev, "FIRMWARE_MISSING\n");
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_warn_if_firmware_missing, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ __be32 pid, sid, tid;
+ unsigned int alg_id;
+ int ret;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ alg_id = 0x9f212;
+ break;
+ default:
+ alg_id = 0xbf212;
+ break;
+ }
+
+ scoped_guard(mutex, &cs_dsp->pwr_lock) {
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_PRJCT_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &pid, sizeof(pid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_CHNNL_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &sid, sizeof(sid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_SNPSHT_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &tid, sizeof(tid));
+ }
+
+ if (ret)
+ dev_warn(cs35l56_base->dev, "Can't read tuning IDs");
+ else
+ dev_info(cs35l56_base->dev, "Tuning PID: %#x, SID: %#x, TID: %#x\n",
+ be32_to_cpu(pid), be32_to_cpu(sid), be32_to_cpu(tid));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_log_tuning, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int devid, revid, otpid, secured, fw_ver;
+ bool fw_missing;
+
+ /*
+ * When the system is not using a reset_gpio ensure the device is
+ * awake, otherwise the device has just been released from reset and
+ * the driver must wait for the control port to become usable.
+ */
+ if (!cs35l56_base->reset_gpio)
+ cs35l56_issue_wake_event(cs35l56_base);
+ else
+ cs35l56_wait_control_port_ready();
+
+ ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+ cs35l56_base->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
+ cs35l56_set_fw_reg_table(cs35l56_base);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_DEVID, &devid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get Device ID failed\n");
+ return ret;
+ }
+ devid &= CS35L56_DEVID_MASK;
+
+ switch (devid) {
+ case 0x35A54:
+ case 0x35A56:
+ case 0x35A57:
+ cs35l56_base->calibration_controls = &cs35l56_calibration_controls;
+ break;
+ case 0x35A630:
+ cs35l56_base->calibration_controls = &cs35l63_calibration_controls;
+ devid = devid >> 4;
+ break;
+ default:
+ dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
+ return -ENODEV;
+ }
+
+ cs35l56_base->type = devid & 0xFF;
+
+ /* Silicon is now identified and booted so exit cache-only */
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get Secure status failed\n");
+ return ret;
+ }
+
+ /* When any bus is restricted treat the device as secured */
+ if (secured & CS35L56_RESTRICTED_MASK)
+ cs35l56_base->secured = true;
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_OTPID, &otpid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get OTP ID failed\n");
+ return ret;
+ }
+
+ ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver);
+ if (ret)
+ return ret;
+
+ dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
+ cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
+ fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
+
+ /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+ regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1,
+ CS35L56_AMP_SHORT_ERR_EINT1_MASK,
+ 0);
+ regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8,
+ CS35L56_TEMP_ERR_EINT1_MASK,
+ 0);
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = cs35l56_read_silicon_uid(cs35l56_base);
+ break;
+ default:
+ ret = cs35l63_read_silicon_uid(cs35l56_base);
+ break;
+ }
+ if (ret)
+ return ret;
+
+ dev_dbg(cs35l56_base->dev, "SiliconID = %#llx\n", cs35l56_base->silicon_uid);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
+{
+ struct gpio_descs *descs;
+ u32 speaker_id;
+ int i, ret;
+
+ /* Check for vendor-specific speaker ID method */
+ ret = cs_amp_get_vendor_spkid(cs35l56_base->dev);
+ if (ret >= 0) {
+ dev_dbg(cs35l56_base->dev, "Vendor Speaker ID = %d\n", ret);
+ return ret;
+ } else if (ret != -ENOENT) {
+ dev_err(cs35l56_base->dev, "Error getting vendor Speaker ID: %d\n", ret);
+ return ret;
+ }
+
+ /* Attempt to read the speaker type from a device property */
+ ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id);
+ if (!ret) {
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ return speaker_id;
+ }
+
+ /* Read the speaker type qualifier from the motherboard GPIOs */
+ descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN);
+ if (!descs) {
+ return -ENOENT;
+ } else if (IS_ERR(descs)) {
+ ret = PTR_ERR(descs);
+ return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n");
+ }
+
+ speaker_id = 0;
+ for (i = 0; i < descs->ndescs; i++) {
+ ret = gpiod_get_value_cansleep(descs->desc[i]);
+ if (ret < 0) {
+ dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i);
+ goto err;
+ }
+
+ speaker_id |= (ret << i);
+ }
+
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ ret = speaker_id;
+err:
+ gpiod_put_array(descs);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "SND_SOC_CS35L56_SHARED");
+
+static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
+ [0x0C] = 128000,
+ [0x0F] = 256000,
+ [0x11] = 384000,
+ [0x12] = 512000,
+ [0x15] = 768000,
+ [0x17] = 1024000,
+ [0x1A] = 1500000,
+ [0x1B] = 1536000,
+ [0x1C] = 2000000,
+ [0x1D] = 2048000,
+ [0x1E] = 2400000,
+ [0x20] = 3000000,
+ [0x21] = 3072000,
+ [0x23] = 4000000,
+ [0x24] = 4096000,
+ [0x25] = 4800000,
+ [0x27] = 6000000,
+ [0x28] = 6144000,
+ [0x29] = 6250000,
+ [0x2A] = 6400000,
+ [0x2E] = 8000000,
+ [0x2F] = 8192000,
+ [0x30] = 9600000,
+ [0x32] = 12000000,
+ [0x33] = 12288000,
+ [0x37] = 13500000,
+ [0x38] = 19200000,
+ [0x39] = 22579200,
+ [0x3B] = 24576000,
+};
+
+int cs35l56_get_bclk_freq_id(unsigned int freq)
+{
+ int i;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ /* The BCLK frequency must be a valid PLL REFCLK */
+ for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) {
+ if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_bclk_freq_id, "SND_SOC_CS35L56_SHARED");
+
+static const char * const cs35l56_supplies[/* auto-sized */] = {
+ "VDD_P",
+ "VDD_IO",
+ "VDD_A",
+};
+
+void cs35l56_fill_supply_names(struct regulator_bulk_data *data)
+{
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_supplies) != CS35L56_NUM_BULK_SUPPLIES);
+ for (i = 0; i < ARRAY_SIZE(cs35l56_supplies); i++)
+ data[i].supply = cs35l56_supplies[i];
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_fill_supply_names, "SND_SOC_CS35L56_SHARED");
+
+const char * const cs35l56_tx_input_texts[] = {
+ "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH",
+ "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4",
+ "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON",
+ "INTERPOLATOR", "SDW1RX1", "SDW1RX2",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, "SND_SOC_CS35L56_SHARED");
+
+const unsigned int cs35l56_tx_input_values[] = {
+ CS35L56_INPUT_SRC_NONE,
+ CS35L56_INPUT_SRC_ASP1RX1,
+ CS35L56_INPUT_SRC_ASP1RX2,
+ CS35L56_INPUT_SRC_VMON,
+ CS35L56_INPUT_SRC_IMON,
+ CS35L56_INPUT_SRC_ERR_VOL,
+ CS35L56_INPUT_SRC_CLASSH,
+ CS35L56_INPUT_SRC_VDDBMON,
+ CS35L56_INPUT_SRC_VBSTMON,
+ CS35L56_INPUT_SRC_DSP1TX1,
+ CS35L56_INPUT_SRC_DSP1TX2,
+ CS35L56_INPUT_SRC_DSP1TX3,
+ CS35L56_INPUT_SRC_DSP1TX4,
+ CS35L56_INPUT_SRC_DSP1TX5,
+ CS35L56_INPUT_SRC_DSP1TX6,
+ CS35L56_INPUT_SRC_DSP1TX7,
+ CS35L56_INPUT_SRC_DSP1TX8,
+ CS35L56_INPUT_SRC_TEMPMON,
+ CS35L56_INPUT_SRC_INTERPOLATOR,
+ CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1,
+ CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l56_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l56_regmap_spi = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l56_regmap_sdw = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l63_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_base = 0x8000,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_i2c, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l63_regmap_sdw = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_sdw, "SND_SOC_CS35L56_SHARED");
+
+MODULE_DESCRIPTION("ASoC CS35L56 Shared");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
new file mode 100644
index 000000000000..9bc9b7c98390
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_spi_probe(struct spi_device *spi)
+{
+ const struct regmap_config *regmap_config = &cs35l56_regmap_spi;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&spi->dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs35l56);
+
+ cs35l56->base.type = 0x56;
+
+ cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n");
+ }
+
+ cs35l56->base.dev = &spi->dev;
+ cs35l56->base.can_hibernate = true;
+ ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
+ if (ret < 0)
+ cs35l56_remove(cs35l56);
+
+ return ret;
+}
+
+static void cs35l56_spi_remove(struct spi_device *spi)
+{
+ struct cs35l56_private *cs35l56 = spi_get_drvdata(spi);
+
+ cs35l56_remove(cs35l56);
+}
+
+static const struct spi_device_id cs35l56_id_spi[] = {
+ { "cs35l56", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l56_id_spi);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
+ { "CSC355C", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);
+#endif
+
+static struct spi_driver cs35l56_spi_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&cs35l56_pm_ops_i2c_spi),
+ .acpi_match_table = ACPI_PTR(cs35l56_asoc_acpi_match),
+ },
+ .id_table = cs35l56_id_spi,
+ .probe = cs35l56_spi_probe,
+ .remove = cs35l56_spi_remove,
+};
+
+module_spi_driver(cs35l56_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SPI driver");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_CORE");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
new file mode 100644
index 000000000000..55b4d0d55712
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.c
@@ -0,0 +1,1814 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Driver for Cirrus Logic CS35L56 smart amp
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/cs-amp-lib.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "wm_adsp.h"
+#include "cs35l56.h"
+
+static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56)
+{
+ /* Wait for patching to complete */
+ flush_work(&cs35l56->dsp_work);
+}
+
+static int cs35l56_dspwait_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cs35l56_wait_dsp_ready(cs35l56);
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cs35l56_wait_dsp_ready(cs35l56);
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
+
+static SOC_ENUM_SINGLE_DECL(cs35l56_cal_set_status_enum, SND_SOC_NOPM, 0,
+ cs35l56_cal_set_status_text);
+
+static int cs35l56_cal_set_status_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ return cs35l56_cal_set_status_get(&cs35l56->base, ucontrol);
+}
+
+static const struct snd_kcontrol_new cs35l56_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L56_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER,
+ 0, 255, 0,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_ENUM_EXT_ACC("CAL_SET_STATUS", cs35l56_cal_set_status_enum,
+ cs35l56_cal_set_status_ctl_get, NULL,
+ SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
+};
+
+static const struct snd_kcontrol_new cs35l63_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L63_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L63_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L63_MAIN_POSTURE_NUMBER,
+ 0, 255, 0,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_ENUM_EXT_ACC("CAL_SET_STATUS", cs35l56_cal_set_status_enum,
+ cs35l56_cal_set_status_ctl_get, NULL,
+ SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
+ CS35L56_ASP1TX1_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx1_mux =
+ SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
+ CS35L56_ASP1TX2_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx2_mux =
+ SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
+ CS35L56_ASP1TX3_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx3_mux =
+ SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
+ CS35L56_ASP1TX4_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx4_mux =
+ SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
+ CS35L56_SWIRE_DP3_CH1_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx1_mux =
+ SOC_DAPM_ENUM("SDW1TX1 SRC", cs35l56_sdw1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx2_enum,
+ CS35L56_SWIRE_DP3_CH2_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx2_mux =
+ SOC_DAPM_ENUM("SDW1TX2 SRC", cs35l56_sdw1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx3_enum,
+ CS35L56_SWIRE_DP3_CH3_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx3_mux =
+ SOC_DAPM_ENUM("SDW1TX3 SRC", cs35l56_sdw1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum,
+ CS35L56_SWIRE_DP3_CH4_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx4_mux =
+ SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum);
+
+static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret;
+
+ dev_dbg(cs35l56->base.dev, "play: %d\n", event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Don't wait for ACK, we check in POST_PMU that it completed */
+ return regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_AUDIO_PLAY);
+ case SND_SOC_DAPM_POST_PMU:
+ /* Wait for firmware to enter PS0 power state */
+ ret = regmap_read_poll_timeout(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->transducer_actual_ps,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_err(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
+ return ret;
+ case SND_SOC_DAPM_POST_PMD:
+ return cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, cs35l56_dsp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_RX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_RX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX4_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX3 Source", SND_SOC_NOPM, 0, 0, &asp1_tx3_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX4 Source", SND_SOC_NOPM, 0, 0, &asp1_tx4_mux),
+
+ SND_SOC_DAPM_MUX("SDW1 TX1 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx1_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux),
+
+ SND_SOC_DAPM_SIGGEN("VMON ADC"),
+ SND_SOC_DAPM_SIGGEN("IMON ADC"),
+ SND_SOC_DAPM_SIGGEN("ERRVOL ADC"),
+ SND_SOC_DAPM_SIGGEN("CLASSH ADC"),
+ SND_SOC_DAPM_SIGGEN("VDDBMON ADC"),
+ SND_SOC_DAPM_SIGGEN("VBSTMON ADC"),
+ SND_SOC_DAPM_SIGGEN("TEMPMON ADC"),
+
+ SND_SOC_DAPM_INPUT("Calibrate"),
+};
+
+#define CS35L56_SRC_ROUTE(name) \
+ { name" Source", "ASP1RX1", "ASP1RX1" }, \
+ { name" Source", "ASP1RX2", "ASP1RX2" }, \
+ { name" Source", "VMON", "VMON ADC" }, \
+ { name" Source", "IMON", "IMON ADC" }, \
+ { name" Source", "ERRVOL", "ERRVOL ADC" }, \
+ { name" Source", "CLASSH", "CLASSH ADC" }, \
+ { name" Source", "VDDBMON", "VDDBMON ADC" }, \
+ { name" Source", "VBSTMON", "VBSTMON ADC" }, \
+ { name" Source", "DSP1TX1", "DSP1" }, \
+ { name" Source", "DSP1TX2", "DSP1" }, \
+ { name" Source", "DSP1TX3", "DSP1" }, \
+ { name" Source", "DSP1TX4", "DSP1" }, \
+ { name" Source", "DSP1TX5", "DSP1" }, \
+ { name" Source", "DSP1TX6", "DSP1" }, \
+ { name" Source", "DSP1TX7", "DSP1" }, \
+ { name" Source", "DSP1TX8", "DSP1" }, \
+ { name" Source", "TEMPMON", "TEMPMON ADC" }, \
+ { name" Source", "INTERPOLATOR", "AMP" }, \
+ { name" Source", "SDW1RX1", "SDW1 Playback" }, \
+ { name" Source", "SDW1RX2", "SDW1 Playback" },
+
+static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
+ { "AMP", NULL, "VDD_B" },
+ { "AMP", NULL, "VDD_AMP" },
+
+ { "ASP1 Playback", NULL, "PLAY" },
+ { "SDW1 Playback", NULL, "PLAY" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "DSP1", NULL, "ASP1RX1" },
+ { "DSP1", NULL, "ASP1RX2" },
+ { "DSP1", NULL, "SDW1 Playback" },
+ { "DSP1", NULL, "Calibrate" },
+ { "AMP", NULL, "DSP1" },
+ { "SPK", NULL, "AMP" },
+
+ CS35L56_SRC_ROUTE("ASP1 TX1")
+ CS35L56_SRC_ROUTE("ASP1 TX2")
+ CS35L56_SRC_ROUTE("ASP1 TX3")
+ CS35L56_SRC_ROUTE("ASP1 TX4")
+
+ { "ASP1TX1", NULL, "ASP1 TX1 Source" },
+ { "ASP1TX2", NULL, "ASP1 TX2 Source" },
+ { "ASP1TX3", NULL, "ASP1 TX3 Source" },
+ { "ASP1TX4", NULL, "ASP1 TX4 Source" },
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+
+ CS35L56_SRC_ROUTE("SDW1 TX1")
+ CS35L56_SRC_ROUTE("SDW1 TX2")
+ CS35L56_SRC_ROUTE("SDW1 TX3")
+ CS35L56_SRC_ROUTE("SDW1 TX4")
+ { "SDW1 Capture", NULL, "SDW1 TX1 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX2 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX3 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX4 Source" },
+};
+
+static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs35l56->base.dev, "%s: %d\n", __func__, event);
+
+ return wm_adsp_event(w, kcontrol, event);
+}
+
+static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int val;
+
+ dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(cs35l56->base.dev, "Unsupported clock source mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val = CS35L56_ASP_FMT_DSP_A << CS35L56_ASP_FMT_SHIFT;
+ cs35l56->tdm_mode = true;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = CS35L56_ASP_FMT_I2S << CS35L56_ASP_FMT_SHIFT;
+ cs35l56->tdm_mode = false;
+ break;
+ default:
+ dev_err(cs35l56->base.dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS35L56_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS35L56_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(cs35l56->base.dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->base.regmap,
+ CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_FMT_MASK |
+ CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK,
+ val);
+
+ /* Hi-Z DOUT in unused slots and when all TX are disabled */
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3,
+ CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
+ CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
+
+ return 0;
+}
+
+static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned long mask)
+{
+ unsigned int channel_shift;
+ int bit_num;
+
+ /* Enable consecutive TX1..TXn for each of the slots set in mask */
+ channel_shift = 0;
+ for_each_set_bit(bit_num, &mask, 32) {
+ reg_val &= ~(0x3f << channel_shift);
+ reg_val |= bit_num << channel_shift;
+ channel_shift += 8;
+ }
+
+ return reg_val;
+}
+
+static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+ if ((slots == 0) || (slot_width == 0)) {
+ dev_dbg(cs35l56->base.dev, "tdm config cleared\n");
+ cs35l56->asp_slot_width = 0;
+ cs35l56->asp_slot_count = 0;
+ return 0;
+ }
+
+ if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) {
+ dev_err(cs35l56->base.dev, "tdm invalid slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ /* More than 32 slots would give an unsupportable BCLK frequency */
+ if (slots > 32) {
+ dev_err(cs35l56->base.dev, "tdm invalid slot count %d\n", slots);
+ return -EINVAL;
+ }
+
+ cs35l56->asp_slot_width = (u8)slot_width;
+ cs35l56->asp_slot_count = (u8)slots;
+
+ // Note: rx/tx is from point of view of the CPU end
+ if (tx_mask == 0)
+ tx_mask = 0x3; // ASPRX1/RX2 in slots 0 and 1
+
+ if (rx_mask == 0)
+ rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3
+
+ /* Default unused slots to 63 */
+ regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL1,
+ cs35l56_make_tdm_config_word(0x3f3f3f3f, rx_mask));
+ regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL5,
+ cs35l56_make_tdm_config_word(0x3f3f3f, tx_mask));
+
+ dev_dbg(cs35l56->base.dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n",
+ cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask);
+
+ return 0;
+}
+
+static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate = params_rate(params);
+ u8 asp_width, asp_wl;
+
+ asp_wl = params_width(params);
+ if (cs35l56->asp_slot_width)
+ asp_width = cs35l56->asp_slot_width;
+ else
+ asp_width = asp_wl;
+
+ dev_dbg(cs35l56->base.dev, "%s: wl=%d, width=%d, rate=%d",
+ __func__, asp_wl, asp_width, rate);
+
+ if (!cs35l56->sysclk_set) {
+ unsigned int slots = cs35l56->asp_slot_count;
+ unsigned int bclk_freq;
+ int freq_id;
+
+ if (slots == 0) {
+ slots = params_channels(params);
+
+ /* I2S always has an even number of slots */
+ if (!cs35l56->tdm_mode)
+ slots = round_up(slots, 2);
+ }
+
+ bclk_freq = asp_width * slots * rate;
+ freq_id = cs35l56_get_bclk_freq_id(bclk_freq);
+ if (freq_id < 0) {
+ dev_err(cs35l56->base.dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1,
+ CS35L56_ASP_BCLK_FREQ_MASK,
+ freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_RX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_RX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL5,
+ CS35L56_ASP_RX_WL_MASK, asp_wl);
+ } else {
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_TX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_TX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL1,
+ CS35L56_ASP_TX_WL_MASK, asp_wl);
+ }
+
+ return 0;
+}
+
+static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ int freq_id;
+
+ if (freq == 0) {
+ cs35l56->sysclk_set = false;
+ return 0;
+ }
+
+ freq_id = cs35l56_get_bclk_freq_id(freq);
+ if (freq_id < 0)
+ return freq_id;
+
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1,
+ CS35L56_ASP_BCLK_FREQ_MASK,
+ freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+ cs35l56->sysclk_set = true;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_ops = {
+ .set_fmt = cs35l56_asp_dai_set_fmt,
+ .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot,
+ .hw_params = cs35l56_asp_dai_hw_params,
+ .set_sysclk = cs35l56_asp_dai_set_sysclk,
+};
+
+static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int cs35l56_sdw_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+ /* rx/tx are from point of view of the CPU end so opposite to our rx/tx */
+ cs35l56->rx_mask = tx_mask;
+ cs35l56->tx_mask = rx_mask;
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_config sconfig;
+ struct sdw_port_config pconfig;
+ int ret;
+
+ dev_dbg(cs35l56->base.dev, "%s: rate %d\n", __func__, params_rate(params));
+
+ if (!cs35l56->base.init_done)
+ return -ENODEV;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ memset(&sconfig, 0, sizeof(sconfig));
+ memset(&pconfig, 0, sizeof(pconfig));
+
+ sconfig.frame_rate = params_rate(params);
+ sconfig.bps = snd_pcm_format_width(params_format(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sconfig.direction = SDW_DATA_DIR_RX;
+ pconfig.num = CS35L56_SDW1_PLAYBACK_PORT;
+ pconfig.ch_mask = cs35l56->rx_mask;
+ } else {
+ sconfig.direction = SDW_DATA_DIR_TX;
+ pconfig.num = CS35L56_SDW1_CAPTURE_PORT;
+ pconfig.ch_mask = cs35l56->tx_mask;
+ }
+
+ if (pconfig.ch_mask == 0) {
+ sconfig.ch_count = params_channels(params);
+ pconfig.ch_mask = GENMASK(sconfig.ch_count - 1, 0);
+ } else {
+ sconfig.ch_count = hweight32(pconfig.ch_mask);
+ }
+
+ ret = sdw_stream_add_slave(cs35l56->sdw_peripheral, &sconfig, &pconfig,
+ 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!cs35l56->sdw_peripheral)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(cs35l56->sdw_peripheral, sdw_stream);
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_set_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = {
+ .set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot,
+ .shutdown = cs35l56_sdw_dai_shutdown,
+ .hw_params = cs35l56_sdw_dai_hw_params,
+ .hw_free = cs35l56_sdw_dai_hw_free,
+ .set_stream = cs35l56_sdw_dai_set_stream,
+};
+
+static struct snd_soc_dai_driver cs35l56_dai[] = {
+ {
+ .name = "cs35l56-asp1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_TX_FORMATS,
+ },
+ .ops = &cs35l56_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs35l56-sdw1",
+ .id = 1,
+ .playback = {
+ .stream_name = "SDW1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_RX_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs35l56_sdw_dai_ops,
+ },
+ {
+ .name = "cs35l56-sdw1c",
+ .id = 2,
+ .capture = {
+ .stream_name = "SDW1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_TX_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs35l56_sdw_dai_ops,
+ },
+};
+
+static int cs35l56_write_cal(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ if (cs35l56->base.secured || !cs35l56->base.cal_data_valid)
+ return -ENODATA;
+
+ ret = wm_adsp_run(&cs35l56->dsp);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
+ cs35l56->base.calibration_controls,
+ &cs35l56->base.cal_data);
+
+ wm_adsp_stop(&cs35l56->dsp);
+
+ if (ret == 0)
+ dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+ return ret;
+}
+
+static int cs35l56_dsp_download_and_power_up(struct cs35l56_private *cs35l56,
+ bool load_firmware)
+{
+ int ret;
+
+ /*
+ * Abort the first load if it didn't find the suffixed bins and
+ * we have an alternate fallback suffix.
+ */
+ cs35l56->dsp.bin_mandatory = (load_firmware && cs35l56->fallback_fw_suffix);
+
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ if ((ret == -ENOENT) && cs35l56->dsp.bin_mandatory) {
+ cs35l56->dsp.fwf_suffix = cs35l56->fallback_fw_suffix;
+ cs35l56->fallback_fw_suffix = NULL;
+ cs35l56->dsp.bin_mandatory = false;
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ }
+
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "wm_adsp_power_up ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, true);
+ if (ret)
+ return;
+
+ cs35l56_write_cal(cs35l56);
+
+ /* Always REINIT after applying patch or coefficients */
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+}
+
+static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
+{
+ int ret;
+
+ /*
+ * Disable SoundWire interrupts to prevent race with IRQ work.
+ * Setting sdw_irq_no_unmask prevents the handler re-enabling
+ * the SoundWire interrupt.
+ */
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->sdw_irq_no_unmask = true;
+ flush_work(&cs35l56->sdw_irq_work);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ flush_work(&cs35l56->sdw_irq_work);
+ }
+
+ ret = cs35l56_firmware_shutdown(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ /*
+ * Use wm_adsp to load and apply the firmware patch and coefficient files,
+ * but only if firmware is missing. If firmware is already patched just
+ * power-up wm_adsp without downloading firmware.
+ */
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, firmware_missing);
+ if (ret)
+ goto err;
+
+ mutex_lock(&cs35l56->base.irq_lock);
+
+ reinit_completion(&cs35l56->init_completion);
+
+ cs35l56->soft_resetting = true;
+ cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral);
+
+ if (cs35l56->sdw_peripheral) {
+ /*
+ * The system-reset causes the CS35L56 to detach from the bus.
+ * Wait for the manager to re-enumerate the CS35L56 and
+ * cs35l56_init() to run again.
+ */
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->base.dev, "%s: init_completion timed out (SDW)\n",
+ __func__);
+ goto err_unlock;
+ }
+ } else if (cs35l56_init(cs35l56)) {
+ goto err_unlock;
+ }
+
+ /* Check if the firmware is still reported missing */
+ cs35l56_warn_if_firmware_missing(&cs35l56->base);
+
+ regmap_clear_bits(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->prot_sts,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
+
+ if (cs35l56_write_cal(cs35l56) == 0)
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+
+err_unlock:
+ mutex_unlock(&cs35l56->base.irq_lock);
+err:
+ /* Re-enable SoundWire interrupts */
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->sdw_irq_no_unmask = false;
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ }
+}
+
+static void cs35l56_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ dsp_work);
+ unsigned int firmware_version;
+ bool firmware_missing;
+ int ret;
+
+ if (!cs35l56->base.init_done)
+ return;
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version);
+ if (ret)
+ goto err;
+
+ /* Populate fw file qualifier with the revision and security state */
+ kfree(cs35l56->dsp.fwf_name);
+ if (firmware_missing) {
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
+ } else {
+ /* Firmware files must match the running firmware version */
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL,
+ "%02x%s-%06x-dsp1",
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ firmware_version);
+ }
+
+ if (!cs35l56->dsp.fwf_name)
+ goto err;
+
+ dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
+ cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
+
+ /*
+ * The firmware cannot be patched if it is already running from
+ * patch RAM. In this case the firmware files are versioned to
+ * match the running firmware version and will only contain
+ * tunings. We do not need to shutdown the firmware to apply
+ * tunings so can use the lower cost reinit sequence instead.
+ */
+ if (!firmware_missing)
+ cs35l56_reinit_patch(cs35l56);
+ else
+ cs35l56_patch(cs35l56, firmware_missing);
+
+ cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp);
+err:
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static struct snd_soc_dapm_context *cs35l56_power_up_for_cal(struct cs35l56_private *cs35l56)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cs35l56->component);
+ int ret;
+
+ ret = snd_soc_dapm_enable_pin(dapm, "Calibrate");
+ if (ret)
+ return ERR_PTR(ret);
+
+ snd_soc_dapm_sync(dapm);
+
+ return dapm;
+}
+
+static void cs35l56_power_down_after_cal(struct cs35l56_private *cs35l56)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cs35l56->component);
+
+ snd_soc_dapm_disable_pin(dapm, "Calibrate");
+ snd_soc_dapm_sync(dapm);
+}
+
+static ssize_t cs35l56_debugfs_calibrate_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
+ struct snd_soc_dapm_context *dapm;
+ ssize_t ret;
+
+ dapm = cs35l56_power_up_for_cal(cs35l56);
+ if (IS_ERR(dapm))
+ return PTR_ERR(dapm);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = cs35l56_calibrate_debugfs_write(&cs35l56->base, from, count, ppos);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ cs35l56_power_down_after_cal(cs35l56);
+
+ return ret;
+}
+
+static ssize_t cs35l56_debugfs_cal_temperature_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
+ struct snd_soc_dapm_context *dapm;
+ ssize_t ret;
+
+ dapm = cs35l56_power_up_for_cal(cs35l56);
+ if (IS_ERR(dapm))
+ return PTR_ERR(dapm);
+
+ ret = cs35l56_cal_ambient_debugfs_write(&cs35l56->base, from, count, ppos);
+ cs35l56_power_down_after_cal(cs35l56);
+
+ return ret;
+}
+
+static ssize_t cs35l56_debugfs_cal_data_read(struct file *file,
+ char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
+ struct snd_soc_dapm_context *dapm;
+ ssize_t ret;
+
+ dapm = cs35l56_power_up_for_cal(cs35l56);
+ if (IS_ERR(dapm))
+ return PTR_ERR(dapm);
+
+ ret = cs35l56_cal_data_debugfs_read(&cs35l56->base, to, count, ppos);
+ cs35l56_power_down_after_cal(cs35l56);
+
+ return ret;
+}
+
+static int cs35l56_new_cal_data_apply(struct cs35l56_private *cs35l56)
+{
+ struct snd_soc_dapm_context *dapm;
+ int ret;
+
+ if (!cs35l56->base.cal_data_valid)
+ return -ENXIO;
+
+ if (cs35l56->base.secured)
+ return -EACCES;
+
+ dapm = cs35l56_power_up_for_cal(cs35l56);
+ if (IS_ERR(dapm))
+ return PTR_ERR(dapm);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
+ cs35l56->base.calibration_controls,
+ &cs35l56->base.cal_data);
+ if (ret == 0)
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ else
+ ret = -EIO;
+
+ snd_soc_dapm_mutex_unlock(dapm);
+ cs35l56_power_down_after_cal(cs35l56);
+
+ return ret;
+}
+
+static ssize_t cs35l56_debugfs_cal_data_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
+ int ret;
+
+ ret = cs35l56_cal_data_debugfs_write(&cs35l56->base, from, count, ppos);
+ if (ret == -ENODATA)
+ return count; /* Ignore writes of empty cal blobs */
+ else if (ret < 0)
+ return -EIO;
+
+ ret = cs35l56_new_cal_data_apply(cs35l56);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct cs35l56_cal_debugfs_fops cs35l56_cal_debugfs_fops = {
+ .calibrate = {
+ .write = cs35l56_debugfs_calibrate_write,
+ },
+ .cal_temperature = {
+ .write = cs35l56_debugfs_cal_temperature_write,
+ },
+ .cal_data = {
+ .read = cs35l56_debugfs_cal_data_read,
+ .write = cs35l56_debugfs_cal_data_write,
+ },
+};
+
+static int cs35l56_cal_data_rb_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ if (!cs35l56->base.cal_data_valid)
+ return -ENODATA;
+
+ memcpy(ucontrol->value.bytes.data, &cs35l56->base.cal_data,
+ sizeof(cs35l56->base.cal_data));
+
+ return 0;
+}
+
+static int cs35l56_cal_data_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * This control is write-only but mixer libraries often try to read
+ * a control before writing it. So we have to implement read.
+ * Return zeros so a write of valid data will always be a change
+ * from its "current value".
+ */
+ memset(ucontrol->value.bytes.data, 0, sizeof(cs35l56->base.cal_data));
+
+ return 0;
+}
+
+static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ const struct cirrus_amp_cal_data *cal_data = (const void *)ucontrol->value.bytes.data;
+ int ret;
+
+ if (cs35l56->base.cal_data_valid)
+ return -EACCES;
+
+ ret = cs35l56_stash_calibration(&cs35l56->base, cal_data);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_new_cal_data_apply(cs35l56);
+ if (ret < 0)
+ return ret;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
+ SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32),
+ cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set),
+ SND_SOC_BYTES_E_ACC("CAL_DATA_RB", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32),
+ cs35l56_cal_data_rb_ctl_get, NULL,
+ SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
+};
+
+static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
+{
+ if (cs35l56->dsp.fwf_suffix)
+ return 0;
+
+ if (!cs35l56->sdw_peripheral)
+ return 0;
+
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "l%uu%u",
+ cs35l56->sdw_link_num,
+ cs35l56->sdw_unique_id);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
+
+ /*
+ * There are published firmware files for L56 B0 silicon using
+ * the ALSA prefix as the filename suffix. Default to trying these
+ * first, with the new name as an alternate.
+ */
+ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+ }
+
+ return 0;
+}
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct dentry *debugfs_root = component->debugfs_root;
+ unsigned short vendor, device;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
+ if (!cs35l56->dsp.system_name &&
+ (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0) {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x-spkid%d",
+ vendor, device,
+ cs35l56->speaker_id);
+ } else {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x",
+ vendor, device);
+ }
+ if (!cs35l56->dsp.system_name)
+ return -ENOMEM;
+ }
+
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
+ return -ENODEV;
+ }
+
+ cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type);
+ if (!cs35l56->dsp.part)
+ return -ENOMEM;
+
+ cs35l56->component = component;
+ ret = cs35l56_set_fw_suffix(cs35l56);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs35l56->dsp, component);
+
+ debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done);
+ debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate);
+ debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched);
+
+
+ switch (cs35l56->base.type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = snd_soc_add_component_controls(component, cs35l56_controls,
+ ARRAY_SIZE(cs35l56_controls));
+ break;
+ case 0x63:
+ ret = snd_soc_add_component_controls(component, cs35l63_controls,
+ ARRAY_SIZE(cs35l63_controls));
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_SET_CTRL)) {
+ ret = snd_soc_add_component_controls(component,
+ cs35l56_cal_data_restore_controls,
+ ARRAY_SIZE(cs35l56_cal_data_restore_controls));
+ }
+
+ if (ret)
+ return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
+
+ ret = snd_soc_dapm_disable_pin(dapm, "Calibrate");
+ if (ret)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS))
+ cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_cal_debugfs_fops);
+
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ return 0;
+}
+
+static void cs35l56_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&cs35l56->dsp_work);
+
+ cs35l56_remove_cal_debugfs(&cs35l56->base);
+
+ if (cs35l56->dsp.cs_dsp.booted)
+ wm_adsp_power_down(&cs35l56->dsp);
+
+ wm_adsp2_component_remove(&cs35l56->dsp, component);
+
+ kfree(cs35l56->dsp.part);
+ cs35l56->dsp.part = NULL;
+
+ kfree(cs35l56->dsp.fwf_name);
+ cs35l56->dsp.fwf_name = NULL;
+
+ cs35l56->component = NULL;
+}
+
+static int cs35l56_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ /*
+ * Wait for patching to complete when transitioning from
+ * BIAS_OFF to BIAS_STANDBY
+ */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ cs35l56_wait_dsp_ready(cs35l56);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
+ .probe = cs35l56_component_probe,
+ .remove = cs35l56_component_remove,
+
+ .dapm_widgets = cs35l56_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets),
+ .dapm_routes = cs35l56_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map),
+
+ .set_bias_level = cs35l56_set_bias_level,
+
+ .suspend_bias_off = 1, /* see cs35l56_system_resume() */
+};
+
+static int __maybe_unused cs35l56_runtime_suspend_i2c_spi(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ return cs35l56_runtime_resume_common(&cs35l56->base, false);
+}
+
+int cs35l56_system_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend\n");
+
+ if (cs35l56->component)
+ flush_work(&cs35l56->dsp_work);
+
+ /*
+ * The interrupt line is normally shared, but after we start suspending
+ * we can't check if our device is the source of an interrupt, and can't
+ * clear it. Prevent this race by temporarily disabling the parent irq
+ * until we reach _no_irq.
+ */
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return pm_runtime_force_suspend(dev);
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
+
+int cs35l56_system_suspend_late(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend_late\n");
+
+ /*
+ * Assert RESET before removing supplies.
+ * RESET is usually shared by all amps so it must not be asserted until
+ * all driver instances have done their suspend() stage.
+ */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_late);
+
+int cs35l56_system_suspend_no_irq(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend_no_irq\n");
+
+ /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_no_irq);
+
+int cs35l56_system_resume_no_irq(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_resume_no_irq\n");
+
+ /*
+ * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
+ * spurious interrupts, and the interrupt line is normally shared.
+ * We can't check if our device is the source of an interrupt, and can't
+ * clear it, until it has fully resumed. Prevent this race by temporarily
+ * disabling the parent irq until we complete resume().
+ */
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_no_irq);
+
+int cs35l56_system_resume_early(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "system_resume_early\n");
+
+ /* Ensure a spec-compliant RESET pulse. */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ /* Enable supplies before releasing RESET. */
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret) {
+ dev_err(dev, "system_resume_early failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Release shared RESET before drivers start resume(). */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_early);
+
+int cs35l56_system_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "system_resume\n");
+
+ /*
+ * We might have done a hard reset or the CS35L56 was power-cycled
+ * so wait for control port to be ready.
+ */
+ cs35l56_wait_control_port_ready();
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ if (ret)
+ return ret;
+
+ /* Firmware won't have been loaded if the component hasn't probed */
+ if (!cs35l56->component)
+ return 0;
+
+ ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
+ dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
+ if (ret < 1)
+ return ret;
+
+ cs35l56->base.fw_patched = false;
+ wm_adsp_power_down(&cs35l56->dsp);
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ /*
+ * suspend_bias_off ensures we are now in BIAS_OFF so there will be
+ * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching.
+ */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume);
+
+static int cs35l56_control_add_nop(struct wm_adsp *dsp, struct cs_dsp_coeff_ctl *cs_ctl)
+{
+ return 0;
+}
+
+static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ cs35l56->dsp_wq = create_singlethread_workqueue("cs35l56-dsp");
+ if (!cs35l56->dsp_wq)
+ return -ENOMEM;
+
+ INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work);
+
+ dsp = &cs35l56->dsp;
+ cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp);
+
+ /*
+ * dsp->part is filled in later as it is based on the DEVID. In a
+ * SoundWire system that cannot be read until enumeration has occurred
+ * and the device has attached.
+ */
+ dsp->fw = 12;
+ dsp->wmfw_optional = true;
+
+ /*
+ * None of the firmware controls need to be exported so add a no-op
+ * callback that suppresses creating an ALSA control.
+ */
+ dsp->control_add = &cs35l56_control_add_nop;
+
+ dev_dbg(cs35l56->base.dev, "DSP system name: '%s'\n", dsp->system_name);
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0) {
+ dev_err(cs35l56->base.dev, "wm_halo_init failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
+{
+ struct device *dev = cs35l56->base.dev;
+ const char *prop;
+ int ret;
+
+ ret = device_property_read_string(dev, "cirrus,firmware-uid", &prop);
+ /* If bad sw node property, return 0 and fallback to legacy firmware path */
+ if (ret < 0)
+ return 0;
+
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0)
+ cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d",
+ prop, cs35l56->speaker_id);
+ else
+ cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
+
+ if (cs35l56->dsp.system_name == NULL)
+ return -ENOMEM;
+
+ dev_dbg(dev, "Firmware UID: %s\n", cs35l56->dsp.system_name);
+
+ return 0;
+}
+
+/*
+ * Some SoundWire laptops have a spk-id-gpios property but it points to
+ * the wrong ACPI Device node so can't be used to get the GPIO. Try to
+ * find the SDCA node containing the GpioIo resource and add a GPIO
+ * mapping to it.
+ */
+static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = {
+ { "spk-id-gpios", &cs35l56_af01_first_gpio, 1 },
+ { }
+};
+
+static void cs35l56_acpi_dev_release_driver_gpios(void *adev)
+{
+ acpi_dev_remove_driver_gpios(adev);
+}
+
+static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l56)
+{
+ struct fwnode_handle *af01_fwnode;
+ const union acpi_object *obj;
+ struct gpio_desc *desc;
+ int ret;
+
+ /* Find the SDCA node containing the GpioIo */
+ af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01");
+ if (!af01_fwnode) {
+ dev_dbg(cs35l56->base.dev, "No AF01 node\n");
+ return -ENOENT;
+ }
+
+ ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev),
+ "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret);
+ fwnode_handle_put(af01_fwnode);
+ return -ENOENT;
+ }
+
+ /* The broken properties we can handle are a 4-element package (one GPIO) */
+ if (obj->package.count != 4) {
+ dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n",
+ obj->package.count);
+ fwnode_handle_put(af01_fwnode);
+ return -ENOENT;
+ }
+
+ /* Add a GPIO mapping if it doesn't already have one */
+ if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) {
+ struct acpi_device *adev = to_acpi_device_node(af01_fwnode);
+
+ /*
+ * Can't use devm_acpi_dev_add_driver_gpios() because the
+ * mapping isn't being added to the node pointed to by
+ * ACPI_COMPANION().
+ */
+ ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping);
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
+ return dev_err_probe(cs35l56->base.dev, ret,
+ "Failed to add gpio mapping to AF01\n");
+ }
+
+ ret = devm_add_action_or_reset(cs35l56->base.dev,
+ cs35l56_acpi_dev_release_driver_gpios,
+ adev);
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n");
+ }
+
+ desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL);
+ if (IS_ERR(desc)) {
+ fwnode_handle_put(af01_fwnode);
+ ret = PTR_ERR(desc);
+ return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n");
+ }
+
+ ret = gpiod_get_value_cansleep(desc);
+ gpiod_put(desc);
+
+ if (ret < 0) {
+ fwnode_handle_put(af01_fwnode);
+ dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n");
+ return ret;
+ }
+
+ fwnode_handle_put(af01_fwnode);
+
+ dev_info(cs35l56->base.dev, "Got spk-id from AF01\n");
+
+ return ret;
+}
+
+int cs35l56_common_probe(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ init_completion(&cs35l56->init_completion);
+ mutex_init(&cs35l56->base.irq_lock);
+ cs35l56->base.cal_index = -1;
+ cs35l56->speaker_id = -ENOENT;
+
+ dev_set_drvdata(cs35l56->base.dev, cs35l56);
+
+ cs35l56_fill_supply_names(cs35l56->supplies);
+ ret = devm_regulator_bulk_get(cs35l56->base.dev, ARRAY_SIZE(cs35l56->supplies),
+ cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to request supplies\n");
+
+ /* Reset could be controlled by the BIOS or shared by multiple amps */
+ cs35l56->base.reset_gpio = devm_gpiod_get_optional(cs35l56->base.dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->base.reset_gpio)) {
+ ret = PTR_ERR(cs35l56->base.reset_gpio);
+ /*
+ * If RESET is shared the first amp to probe will grab the reset
+ * line and reset all the amps
+ */
+ if (ret != -EBUSY)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->base.reset_gpio = NULL;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n");
+
+ if (cs35l56->base.reset_gpio) {
+ /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ }
+
+ ret = cs35l56_get_speaker_id(&cs35l56->base);
+ if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT))
+ ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56);
+
+ if ((ret < 0) && (ret != -ENOENT))
+ goto err;
+
+ cs35l56->speaker_id = ret;
+
+ ret = cs35l56_get_firmware_uid(cs35l56);
+ if (ret != 0)
+ goto err;
+
+ ret = cs35l56_dsp_init(cs35l56);
+ if (ret < 0) {
+ dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
+ goto err;
+ }
+
+ ret = devm_snd_soc_register_component(cs35l56->base.dev,
+ &soc_component_dev_cs35l56,
+ cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
+ if (ret < 0) {
+ dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, "SND_SOC_CS35L56_CORE");
+
+int cs35l56_init(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ /*
+ * Check whether the actions associated with soft reset or one time
+ * init need to be performed.
+ */
+ if (cs35l56->soft_resetting)
+ goto post_soft_reset;
+
+ if (cs35l56->base.init_done)
+ return 0;
+
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100);
+ pm_runtime_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_set_active(cs35l56->base.dev);
+ pm_runtime_enable(cs35l56->base.dev);
+
+ ret = cs35l56_hw_init(&cs35l56->base);
+ if (ret < 0)
+ return ret;
+
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_get_calibration(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ if (!cs35l56->base.reset_gpio) {
+ dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
+ cs35l56->soft_resetting = true;
+ cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral);
+ if (cs35l56->sdw_peripheral) {
+ /* Keep alive while we wait for re-enumeration */
+ pm_runtime_get_noresume(cs35l56->base.dev);
+ return 0;
+ }
+ }
+
+post_soft_reset:
+ if (cs35l56->soft_resetting) {
+ cs35l56->soft_resetting = false;
+
+ /* Done re-enumerating after one-time init so release the keep-alive */
+ if (cs35l56->sdw_peripheral && !cs35l56->base.init_done)
+ pm_runtime_put_noidle(cs35l56->base.dev);
+
+ regcache_mark_dirty(cs35l56->base.regmap);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n");
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ return ret;
+
+ /* Registers could be dirty after soft reset or SoundWire enumeration */
+ regcache_sync(cs35l56->base.regmap);
+
+ /* Set ASP1 DOUT to high-impedance when it is not transmitting audio data. */
+ ret = regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3,
+ CS35L56_ASP1_DOUT_HIZ_CTRL_MASK);
+ if (ret)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n");
+
+ cs35l56->base.init_done = true;
+ complete(&cs35l56->init_completion);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init, "SND_SOC_CS35L56_CORE");
+
+void cs35l56_remove(struct cs35l56_private *cs35l56)
+{
+ cs35l56->base.init_done = false;
+
+ /*
+ * WAKE IRQs unmask if CS35L56 hibernates so free the handler to
+ * prevent it racing with remove().
+ */
+ if (cs35l56->base.irq)
+ devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base);
+
+ destroy_workqueue(cs35l56->dsp_wq);
+
+ pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_suspend(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+
+ regcache_cache_only(cs35l56->base.regmap, true);
+
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_remove, "SND_SOC_CS35L56_CORE");
+
+#if IS_ENABLED(CONFIG_SND_SOC_CS35L56_I2C) || IS_ENABLED(CONFIG_SND_SOC_CS35L56_SPI)
+EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = {
+ SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend_i2c_spi, cs35l56_runtime_resume_i2c_spi, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq)
+};
+#endif
+
+MODULE_DESCRIPTION("ASoC CS35L56 driver");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
new file mode 100644
index 000000000000..4c59f92f3206
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS35L56_H
+#define CS35L56_H
+
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+#include "wm_adsp.h"
+
+#define CS35L56_SDW_GEN_INT_STAT_1 0xc0
+#define CS35L56_SDW_GEN_INT_MASK_1 0xc1
+#define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0)
+
+#define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS35L56_RATES (SNDRV_PCM_RATE_48000)
+
+struct sdw_slave;
+
+struct cs35l56_private {
+ struct wm_adsp dsp; /* must be first member */
+ struct cs35l56_base base;
+ struct work_struct dsp_work;
+ struct workqueue_struct *dsp_wq;
+ struct snd_soc_component *component;
+ struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
+ struct sdw_slave *sdw_peripheral;
+ const char *fallback_fw_suffix;
+ struct work_struct sdw_irq_work;
+ bool sdw_irq_no_unmask;
+ bool soft_resetting;
+ bool sdw_attached;
+ struct completion init_completion;
+
+ int speaker_id;
+ u32 rx_mask;
+ u32 tx_mask;
+ u8 asp_slot_width;
+ u8 asp_slot_count;
+ bool tdm_mode;
+ bool sysclk_set;
+ u8 sdw_link_num;
+ u8 sdw_unique_id;
+};
+
+static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base)
+{
+ return container_of(cs35l56_base, struct cs35l56_private, base);
+}
+
+extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
+
+int cs35l56_system_suspend(struct device *dev);
+int cs35l56_system_suspend_late(struct device *dev);
+int cs35l56_system_suspend_no_irq(struct device *dev);
+int cs35l56_system_resume_no_irq(struct device *dev);
+int cs35l56_system_resume_early(struct device *dev);
+int cs35l56_system_resume(struct device *dev);
+irqreturn_t cs35l56_irq(int irq, void *data);
+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq);
+int cs35l56_common_probe(struct cs35l56_private *cs35l56);
+int cs35l56_init(struct cs35l56_private *cs35l56);
+void cs35l56_remove(struct cs35l56_private *cs35l56);
+
+#endif /* ifndef CS35L56_H */
diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c
new file mode 100644
index 000000000000..aa629ef53db4
--- /dev/null
+++ b/sound/soc/codecs/cs40l50-codec.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS40L50 Advanced Haptic Driver with waveform memory,
+// integrated DSP, and closed-loop algorithms
+//
+// Copyright 2024 Cirrus Logic, Inc.
+//
+// Author: James Ogletree <james.ogletree@cirrus.com>
+
+#include <linux/bitfield.h>
+#include <linux/mfd/cs40l50.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define CS40L50_REFCLK_INPUT 0x2C04
+#define CS40L50_ASP_CONTROL2 0x4808
+#define CS40L50_ASP_DATA_CONTROL5 0x4840
+
+/* PLL Config */
+#define CS40L50_PLL_REFCLK_BCLK 0x0
+#define CS40L50_PLL_REFCLK_MCLK 0x5
+#define CS40L50_PLL_REEFCLK_MCLK_CFG 0x00
+#define CS40L50_PLL_REFCLK_LOOP_MASK BIT(11)
+#define CS40L50_PLL_REFCLK_OPEN_LOOP 1
+#define CS40L50_PLL_REFCLK_CLOSED_LOOP 0
+#define CS40L50_PLL_REFCLK_LOOP_SHIFT 11
+#define CS40L50_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
+#define CS40L50_PLL_REFCLK_FREQ_SHIFT 5
+#define CS40L50_PLL_REFCLK_SEL_MASK GENMASK(2, 0)
+#define CS40L50_BCLK_RATIO_DEFAULT 32
+
+/* ASP Config */
+#define CS40L50_ASP_RX_WIDTH_SHIFT 24
+#define CS40L50_ASP_RX_WIDTH_MASK GENMASK(31, 24)
+#define CS40L50_ASP_RX_WL_MASK GENMASK(5, 0)
+#define CS40L50_ASP_FSYNC_INV_MASK BIT(2)
+#define CS40L50_ASP_BCLK_INV_MASK BIT(6)
+#define CS40L50_ASP_FMT_MASK GENMASK(10, 8)
+#define CS40L50_ASP_FMT_I2S 0x2
+
+struct cs40l50_pll_config {
+ unsigned int freq;
+ unsigned int cfg;
+};
+
+struct cs40l50_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int daifmt;
+ unsigned int bclk_ratio;
+ unsigned int rate;
+};
+
+static const struct cs40l50_pll_config cs40l50_pll_cfg[] = {
+ { 32768, 0x00 },
+ { 1536000, 0x1B },
+ { 3072000, 0x21 },
+ { 6144000, 0x28 },
+ { 9600000, 0x30 },
+ { 12288000, 0x33 },
+};
+
+static int cs40l50_get_clk_config(const unsigned int freq, unsigned int *cfg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs40l50_pll_cfg); i++) {
+ if (cs40l50_pll_cfg[i].freq == freq) {
+ *cfg = cs40l50_pll_cfg[i].cfg;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int cs40l50_swap_ext_clk(struct cs40l50_codec *codec, const unsigned int clk_src)
+{
+ unsigned int cfg;
+ int ret;
+
+ switch (clk_src) {
+ case CS40L50_PLL_REFCLK_BCLK:
+ ret = cs40l50_get_clk_config(codec->bclk_ratio * codec->rate, &cfg);
+ if (ret)
+ return ret;
+ break;
+ case CS40L50_PLL_REFCLK_MCLK:
+ cfg = CS40L50_PLL_REEFCLK_MCLK_CFG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_LOOP_MASK,
+ CS40L50_PLL_REFCLK_OPEN_LOOP <<
+ CS40L50_PLL_REFCLK_LOOP_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_FREQ_MASK |
+ CS40L50_PLL_REFCLK_SEL_MASK,
+ (cfg << CS40L50_PLL_REFCLK_FREQ_SHIFT) | clk_src);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_LOOP_MASK,
+ CS40L50_PLL_REFCLK_CLOSED_LOOP <<
+ CS40L50_PLL_REFCLK_LOOP_SHIFT);
+}
+
+static int cs40l50_clk_en(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = cs40l50_dsp_write(codec->dev, codec->regmap, CS40L50_STOP_PLAYBACK);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_dsp_write(codec->dev, codec->regmap, CS40L50_START_I2S);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_swap_ext_clk(codec, CS40L50_PLL_REFCLK_BCLK);
+ if (ret)
+ return ret;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = cs40l50_swap_ext_clk(codec, CS40L50_PLL_REFCLK_MCLK);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs40l50_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("ASP PLL", 0, SND_SOC_NOPM, 0, 0, cs40l50_clk_en,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route cs40l50_dapm_routes[] = {
+ { "ASP Playback", NULL, "ASP PLL" },
+ { "ASPRX1", NULL, "ASP Playback" },
+ { "ASPRX2", NULL, "ASP Playback" },
+
+ { "OUT", NULL, "ASPRX1" },
+ { "OUT", NULL, "ASPRX2" },
+};
+
+static int cs40l50_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(codec_dai->component);
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ codec->daifmt = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ codec->daifmt = CS40L50_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ codec->daifmt = CS40L50_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ codec->daifmt = CS40L50_ASP_FSYNC_INV_MASK | CS40L50_ASP_BCLK_INV_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codec->daifmt |= FIELD_PREP(CS40L50_ASP_FMT_MASK, CS40L50_ASP_FMT_I2S);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs40l50_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_rx_wl = params_width(params);
+ int ret;
+
+ codec->rate = params_rate(params);
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_ASP_DATA_CONTROL5,
+ CS40L50_ASP_RX_WL_MASK, asp_rx_wl);
+ if (ret)
+ return ret;
+
+ codec->daifmt |= (asp_rx_wl << CS40L50_ASP_RX_WIDTH_SHIFT);
+
+ return regmap_update_bits(codec->regmap, CS40L50_ASP_CONTROL2,
+ CS40L50_ASP_FSYNC_INV_MASK |
+ CS40L50_ASP_BCLK_INV_MASK |
+ CS40L50_ASP_FMT_MASK |
+ CS40L50_ASP_RX_WIDTH_MASK, codec->daifmt);
+}
+
+static int cs40l50_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(dai->component);
+
+ codec->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs40l50_dai_ops = {
+ .set_fmt = cs40l50_set_dai_fmt,
+ .set_bclk_ratio = cs40l50_set_dai_bclk_ratio,
+ .hw_params = cs40l50_hw_params,
+};
+
+static struct snd_soc_dai_driver cs40l50_dai[] = {
+ {
+ .name = "cs40l50-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &cs40l50_dai_ops,
+ },
+};
+
+static int cs40l50_codec_probe(struct snd_soc_component *component)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(component);
+
+ codec->bclk_ratio = CS40L50_BCLK_RATIO_DEFAULT;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_cs40l50 = {
+ .probe = cs40l50_codec_probe,
+ .dapm_widgets = cs40l50_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs40l50_dapm_widgets),
+ .dapm_routes = cs40l50_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs40l50_dapm_routes),
+};
+
+static int cs40l50_codec_driver_probe(struct platform_device *pdev)
+{
+ struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent);
+ struct cs40l50_codec *codec;
+
+ codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->regmap = cs40l50->regmap;
+ codec->dev = &pdev->dev;
+
+ return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cs40l50,
+ cs40l50_dai, ARRAY_SIZE(cs40l50_dai));
+}
+
+static const struct platform_device_id cs40l50_id[] = {
+ { "cs40l50-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs40l50_id);
+
+static struct platform_driver cs40l50_codec_driver = {
+ .probe = cs40l50_codec_driver_probe,
+ .id_table = cs40l50_id,
+ .driver = {
+ .name = "cs40l50-codec",
+ },
+};
+module_platform_driver(cs40l50_codec_driver);
+
+MODULE_DESCRIPTION("ASoC CS40L50 driver");
+MODULE_AUTHOR("James Ogletree <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
new file mode 100644
index 000000000000..89c424dd838b
--- /dev/null
+++ b/sound/soc/codecs/cs4234.c
@@ -0,0 +1,917 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// cs4234.c -- ALSA SoC CS4234 driver
+//
+// Copyright (C) 2020 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/workqueue.h>
+
+#include "cs4234.h"
+
+struct cs4234 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ struct completion vq_ramp_complete;
+ struct delayed_work vq_ramp_delay;
+ struct clk *mclk;
+ unsigned long mclk_rate;
+ unsigned long lrclk_rate;
+ unsigned int format;
+ struct snd_ratnum rate_dividers[2];
+ struct snd_pcm_hw_constraint_ratnums rate_constraint;
+};
+
+/* -89.92dB to +6.02dB with step of 0.38dB */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -8992, 38, 0);
+
+static const char * const cs4234_dac14_delay_text[] = {
+ "0us", "100us", "150us", "200us", "225us", "250us", "275us", "300us",
+ "325us", "350us", "375us", "400us", "425us", "450us", "475us", "500us",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_dac14_group_delay, CS4234_TPS_CTRL,
+ CS4234_GRP_DELAY_SHIFT, cs4234_dac14_delay_text);
+
+static const char * const cs4234_noise_gate_text[] = {
+ "72dB", "78dB", "84dB", "90dB", "96dB", "102dB", "138dB", "Disabled",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_ll_noise_gate, CS4234_LOW_LAT_CTRL1,
+ CS4234_LL_NG_SHIFT, cs4234_noise_gate_text);
+static SOC_ENUM_SINGLE_DECL(cs4234_dac14_noise_gate, CS4234_DAC_CTRL1,
+ CS4234_DAC14_NG_SHIFT, cs4234_noise_gate_text);
+static SOC_ENUM_SINGLE_DECL(cs4234_dac5_noise_gate, CS4234_DAC_CTRL2,
+ CS4234_DAC5_NG_SHIFT, cs4234_noise_gate_text);
+
+static const char * const cs4234_dac5_config_fltr_sel_text[] = {
+ "Interpolation Filter", "Sample and Hold"
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_dac5_config_fltr_sel, CS4234_DAC_CTRL1,
+ CS4234_DAC5_CFG_FLTR_SHIFT,
+ cs4234_dac5_config_fltr_sel_text);
+
+static const char * const cs4234_mute_delay_text[] = {
+ "1x", "4x", "16x", "64x",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_mute_delay, CS4234_VOLUME_MODE,
+ CS4234_MUTE_DELAY_SHIFT, cs4234_mute_delay_text);
+
+static const char * const cs4234_minmax_delay_text[] = {
+ "1x", "2x", "4x", "8x", "16x", "32x", "64x", "128x",
+};
+static SOC_ENUM_SINGLE_DECL(cs4234_min_delay, CS4234_VOLUME_MODE,
+ CS4234_MIN_DELAY_SHIFT, cs4234_minmax_delay_text);
+static SOC_ENUM_SINGLE_DECL(cs4234_max_delay, CS4234_VOLUME_MODE,
+ CS4234_MAX_DELAY_SHIFT, cs4234_minmax_delay_text);
+
+static int cs4234_dac14_grp_delay_put(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *uctrl)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kctrl);
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int val = 0;
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ regmap_read(cs4234->regmap, CS4234_ADC_CTRL2, &val);
+ if ((val & 0x0F) != 0x0F) { // are all the ADCs powerdown
+ ret = -EBUSY;
+ dev_err(component->dev, "Can't change group delay while ADC are ON\n");
+ goto exit;
+ }
+
+ regmap_read(cs4234->regmap, CS4234_DAC_CTRL4, &val);
+ if ((val & 0x1F) != 0x1F) { // are all the DACs powerdown
+ ret = -EBUSY;
+ dev_err(component->dev, "Can't change group delay while DAC are ON\n");
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kctrl, uctrl);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static void cs4234_vq_ramp_done(struct work_struct *work)
+{
+ struct delayed_work *dw = to_delayed_work(work);
+ struct cs4234 *cs4234 = container_of(dw, struct cs4234, vq_ramp_delay);
+
+ complete_all(&cs4234->vq_ramp_complete);
+}
+
+static int cs4234_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
+ case SND_SOC_BIAS_STANDBY:
+ wait_for_completion(&cs4234->vq_ramp_complete);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs4234_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("SDRX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX3", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX4", NULL, 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDRX5", NULL, 4, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC1", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC1_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC2", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC2_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC3", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC3_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC4", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC4_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC5", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC5_SHIFT, 1),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1"),
+ SND_SOC_DAPM_OUTPUT("AOUT2"),
+ SND_SOC_DAPM_OUTPUT("AOUT3"),
+ SND_SOC_DAPM_OUTPUT("AOUT4"),
+ SND_SOC_DAPM_OUTPUT("AOUT5"),
+
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+
+ SND_SOC_DAPM_ADC("ADC1", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC1_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC2_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC3", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC3_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC4", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC4_SHIFT, 1),
+
+ SND_SOC_DAPM_AIF_OUT("SDTX1", NULL, 0, SND_SOC_NOPM, 0, 1),
+ SND_SOC_DAPM_AIF_OUT("SDTX2", NULL, 1, SND_SOC_NOPM, 0, 1),
+ SND_SOC_DAPM_AIF_OUT("SDTX3", NULL, 2, SND_SOC_NOPM, 0, 1),
+ SND_SOC_DAPM_AIF_OUT("SDTX4", NULL, 3, SND_SOC_NOPM, 0, 1),
+};
+
+static const struct snd_soc_dapm_route cs4234_dapm_routes[] = {
+ /* Playback */
+ { "AOUT1", NULL, "DAC1" },
+ { "AOUT2", NULL, "DAC2" },
+ { "AOUT3", NULL, "DAC3" },
+ { "AOUT4", NULL, "DAC4" },
+ { "AOUT5", NULL, "DAC5" },
+
+ { "DAC1", NULL, "SDRX1" },
+ { "DAC2", NULL, "SDRX2" },
+ { "DAC3", NULL, "SDRX3" },
+ { "DAC4", NULL, "SDRX4" },
+ { "DAC5", NULL, "SDRX5" },
+
+ { "SDRX1", NULL, "Playback" },
+ { "SDRX2", NULL, "Playback" },
+ { "SDRX3", NULL, "Playback" },
+ { "SDRX4", NULL, "Playback" },
+ { "SDRX5", NULL, "Playback" },
+
+ /* Capture */
+ { "ADC1", NULL, "AIN1" },
+ { "ADC2", NULL, "AIN2" },
+ { "ADC3", NULL, "AIN3" },
+ { "ADC4", NULL, "AIN4" },
+
+ { "SDTX1", NULL, "ADC1" },
+ { "SDTX2", NULL, "ADC2" },
+ { "SDTX3", NULL, "ADC3" },
+ { "SDTX4", NULL, "ADC4" },
+
+ { "Capture", NULL, "SDTX1" },
+ { "Capture", NULL, "SDTX2" },
+ { "Capture", NULL, "SDTX3" },
+ { "Capture", NULL, "SDTX4" },
+};
+
+static const struct snd_kcontrol_new cs4234_snd_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", CS4234_MASTER_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC1 Volume", CS4234_DAC1_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC2 Volume", CS4234_DAC2_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC3 Volume", CS4234_DAC3_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC4 Volume", CS4234_DAC4_VOL, 0, 0xff, 1, dac_tlv),
+ SOC_SINGLE_TLV("DAC5 Volume", CS4234_DAC5_VOL, 0, 0xff, 1, dac_tlv),
+
+ SOC_SINGLE("DAC5 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC5_ATT_SHIFT, 1, 1),
+ SOC_SINGLE("DAC1-4 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC14_ATT_SHIFT, 1, 1),
+
+ SOC_SINGLE("ADC HPF Switch", CS4234_ADC_CTRL1, CS4234_ENA_HPF_SHIFT, 1, 0),
+
+ SOC_ENUM_EXT("DAC1-4 Group Delay", cs4234_dac14_group_delay,
+ snd_soc_get_enum_double, cs4234_dac14_grp_delay_put),
+
+ SOC_SINGLE("ADC1 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC1_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC2_SHIFT, 1, 0),
+ SOC_SINGLE("ADC3 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC3_SHIFT, 1, 0),
+ SOC_SINGLE("ADC4 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC4_SHIFT, 1, 0),
+
+ SOC_SINGLE("DAC1 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC1_SHIFT, 1, 0),
+ SOC_SINGLE("DAC2 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC2_SHIFT, 1, 0),
+ SOC_SINGLE("DAC3 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC3_SHIFT, 1, 0),
+ SOC_SINGLE("DAC4 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC4_SHIFT, 1, 0),
+ SOC_SINGLE("DAC5 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC5_SHIFT, 1, 0),
+
+ SOC_SINGLE("ADC1 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC1_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC2_SHIFT, 1, 1),
+ SOC_SINGLE("ADC3 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC3_SHIFT, 1, 1),
+ SOC_SINGLE("ADC4 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC4_SHIFT, 1, 1),
+
+ SOC_SINGLE("DAC1 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC1_SHIFT, 1, 1),
+ SOC_SINGLE("DAC2 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC2_SHIFT, 1, 1),
+ SOC_SINGLE("DAC3 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC3_SHIFT, 1, 1),
+ SOC_SINGLE("DAC4 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC4_SHIFT, 1, 1),
+ SOC_SINGLE("DAC5 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC5_SHIFT, 1, 1),
+ SOC_SINGLE("Low-latency Switch", CS4234_DAC_CTRL3, CS4234_MUTE_LL_SHIFT, 1, 1),
+
+ SOC_SINGLE("DAC1 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL1_SHIFT, 1, 0),
+ SOC_SINGLE("DAC2 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL2_SHIFT, 1, 0),
+ SOC_SINGLE("DAC3 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL3_SHIFT, 1, 0),
+ SOC_SINGLE("DAC4 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
+ CS4234_INV_LL4_SHIFT, 1, 0),
+
+ SOC_ENUM("Low-latency Noise Gate", cs4234_ll_noise_gate),
+ SOC_ENUM("DAC1-4 Noise Gate", cs4234_dac14_noise_gate),
+ SOC_ENUM("DAC5 Noise Gate", cs4234_dac5_noise_gate),
+
+ SOC_SINGLE("DAC1-4 De-emphasis Switch", CS4234_DAC_CTRL1,
+ CS4234_DAC14_DE_SHIFT, 1, 0),
+ SOC_SINGLE("DAC5 De-emphasis Switch", CS4234_DAC_CTRL1,
+ CS4234_DAC5_DE_SHIFT, 1, 0),
+
+ SOC_SINGLE("DAC5 Master Controlled Switch", CS4234_DAC_CTRL1,
+ CS4234_DAC5_MVC_SHIFT, 1, 0),
+
+ SOC_ENUM("DAC5 Filter", cs4234_dac5_config_fltr_sel),
+
+ SOC_ENUM("Mute Delay", cs4234_mute_delay),
+ SOC_ENUM("Ramp Minimum Delay", cs4234_min_delay),
+ SOC_ENUM("Ramp Maximum Delay", cs4234_max_delay),
+
+};
+
+static int cs4234_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ unsigned int sp_ctrl = 0;
+
+ cs4234->format = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (cs4234->format) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ sp_ctrl |= CS4234_LEFT_J << CS4234_SP_FORMAT_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ sp_ctrl |= CS4234_I2S << CS4234_SP_FORMAT_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* TDM mode in datasheet */
+ sp_ctrl |= CS4234_TDM << CS4234_SP_FORMAT_SHIFT;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (cs4234->format == SND_SOC_DAIFMT_DSP_A) {
+ dev_err(component->dev, "Unsupported DSP A format in master mode\n");
+ return -EINVAL;
+ }
+ sp_ctrl |= CS4234_MST_SLV_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sp_ctrl |= CS4234_INVT_SCLK_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported inverted clock setting\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs4234->regmap, CS4234_SP_CTRL,
+ CS4234_SP_FORMAT_MASK | CS4234_MST_SLV_MASK | CS4234_INVT_SCLK_MASK,
+ sp_ctrl);
+
+ return 0;
+}
+
+static int cs4234_dai_hw_params(struct snd_pcm_substream *sub,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ unsigned int mclk_mult, double_speed = 0;
+ int ret = 0, rate_ad, sample_width;
+
+ cs4234->lrclk_rate = params_rate(params);
+ mclk_mult = cs4234->mclk_rate / cs4234->lrclk_rate;
+
+ if (cs4234->lrclk_rate > 48000) {
+ double_speed = 1;
+ mclk_mult *= 2;
+ }
+
+ switch (mclk_mult) {
+ case 256:
+ case 384:
+ case 512:
+ regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP,
+ CS4234_SPEED_MODE_MASK,
+ double_speed << CS4234_SPEED_MODE_SHIFT);
+ regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP,
+ CS4234_MCLK_RATE_MASK,
+ ((mclk_mult / 128) - 2) << CS4234_MCLK_RATE_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported mclk/lrclk rate\n");
+ return -EINVAL;
+ }
+
+ switch (cs4234->lrclk_rate) {
+ case 48000:
+ case 96000:
+ rate_ad = CS4234_48K;
+ break;
+ case 44100:
+ case 88200:
+ rate_ad = CS4234_44K1;
+ break;
+ case 32000:
+ case 64000:
+ rate_ad = CS4234_32K;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported LR clock\n");
+ return -EINVAL;
+ }
+ regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, CS4234_BASE_RATE_MASK,
+ rate_ad << CS4234_BASE_RATE_SHIFT);
+
+ sample_width = params_width(params);
+ switch (sample_width) {
+ case 16:
+ sample_width = 0;
+ break;
+ case 18:
+ sample_width = 1;
+ break;
+ case 20:
+ sample_width = 2;
+ break;
+ case 24:
+ sample_width = 3;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample width\n");
+ return -EINVAL;
+ }
+ if (sub->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH,
+ CS4234_SDOUTX_SW_MASK,
+ sample_width << CS4234_SDOUTX_SW_SHIFT);
+ else
+ regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH,
+ CS4234_INPUT_SW_MASK | CS4234_LOW_LAT_SW_MASK | CS4234_DAC5_SW_MASK,
+ sample_width << CS4234_INPUT_SW_SHIFT |
+ sample_width << CS4234_LOW_LAT_SW_SHIFT |
+ sample_width << CS4234_DAC5_SW_SHIFT);
+
+ return ret;
+}
+
+/* Scale MCLK rate by 64 to avoid overflow in the ratnum calculation */
+#define CS4234_MCLK_SCALE 64
+
+static const struct snd_ratnum cs4234_dividers[] = {
+ {
+ .num = 0,
+ .den_min = 256 / CS4234_MCLK_SCALE,
+ .den_max = 512 / CS4234_MCLK_SCALE,
+ .den_step = 128 / CS4234_MCLK_SCALE,
+ },
+ {
+ .num = 0,
+ .den_min = 128 / CS4234_MCLK_SCALE,
+ .den_max = 192 / CS4234_MCLK_SCALE,
+ .den_step = 64 / CS4234_MCLK_SCALE,
+ },
+};
+
+static int cs4234_dai_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct cs4234 *cs4234 = rule->private;
+ int mclk = cs4234->mclk_rate;
+ struct snd_interval ranges[] = {
+ { /* Single Speed Mode */
+ .min = mclk / clamp(mclk / 30000, 256, 512),
+ .max = mclk / clamp(mclk / 50000, 256, 512),
+ },
+ { /* Double Speed Mode */
+ .min = mclk / clamp(mclk / 60000, 128, 256),
+ .max = mclk / clamp(mclk / 100000, 128, 256),
+ },
+ };
+
+ return snd_interval_ranges(hw_param_interval(params, rule->var),
+ ARRAY_SIZE(ranges), ranges, 0);
+}
+
+static int cs4234_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(comp);
+ int i, ret;
+
+ switch (cs4234->format) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ cs4234->rate_constraint.nrats = 2;
+
+ /*
+ * Playback only supports 24-bit samples in these modes.
+ * Note: SNDRV_PCM_HW_PARAM_SAMPLE_BITS constrains the physical
+ * width, which we don't care about, so constrain the format.
+ */
+ if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_mask64(
+ sub->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_minmax(sub->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, 4);
+ if (ret < 0)
+ return ret;
+ }
+
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cs4234->rate_constraint.nrats = 1;
+ break;
+ default:
+ dev_err(comp->dev, "Startup unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cs4234->rate_constraint.nrats; i++)
+ cs4234->rate_dividers[i].num = cs4234->mclk_rate / CS4234_MCLK_SCALE;
+
+ ret = snd_pcm_hw_constraint_ratnums(sub->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs4234->rate_constraint);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * MCLK/rate may be a valid ratio but out-of-spec (e.g. 24576000/64000)
+ * so this rule limits the range of sample rate for given MCLK.
+ */
+ return snd_pcm_hw_rule_add(sub->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ cs4234_dai_rule_rate, cs4234, -1);
+}
+
+static int cs4234_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
+ unsigned int slot_offset, dac5_slot, dac5_mask_group;
+ uint8_t dac5_masks[4];
+
+ if (slot_width != 32) {
+ dev_err(component->dev, "Unsupported slot width\n");
+ return -EINVAL;
+ }
+
+ /* Either 4 or 5 consecutive bits, DAC5 is optional */
+ slot_offset = ffs(tx_mask) - 1;
+ tx_mask >>= slot_offset;
+ if ((slot_offset % 4) || ((tx_mask != 0x0F) && (tx_mask != 0x1F))) {
+ dev_err(component->dev, "Unsupported tx slots allocation\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_DAC14_SRC_MASK,
+ (slot_offset / 4) << CS4234_DAC14_SRC_SHIFT);
+ regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_LL_SRC_MASK,
+ (slot_offset / 4) << CS4234_LL_SRC_SHIFT);
+
+ if (tx_mask == 0x1F) {
+ dac5_slot = slot_offset + 4;
+ memset(dac5_masks, 0xFF, sizeof(dac5_masks));
+ dac5_mask_group = dac5_slot / 8;
+ dac5_slot %= 8;
+ dac5_masks[dac5_mask_group] ^= BIT(7 - dac5_slot);
+ regmap_bulk_write(cs4234->regmap,
+ CS4234_SDIN1_MASK1,
+ dac5_masks,
+ ARRAY_SIZE(dac5_masks));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs4234_dai_ops = {
+ .set_fmt = cs4234_dai_set_fmt,
+ .hw_params = cs4234_dai_hw_params,
+ .startup = cs4234_dai_startup,
+ .set_tdm_slot = cs4234_dai_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs4234_dai[] = {
+ {
+ .name = "cs4234-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = CS4234_PCM_RATES,
+ .formats = CS4234_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS4234_PCM_RATES,
+ .formats = CS4234_FORMATS,
+ },
+ .ops = &cs4234_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct reg_default cs4234_default_reg[] = {
+ { CS4234_CLOCK_SP, 0x04},
+ { CS4234_SAMPLE_WIDTH, 0xFF},
+ { CS4234_SP_CTRL, 0x48},
+ { CS4234_SP_DATA_SEL, 0x01},
+ { CS4234_SDIN1_MASK1, 0xFF},
+ { CS4234_SDIN1_MASK2, 0xFF},
+ { CS4234_SDIN2_MASK1, 0xFF},
+ { CS4234_SDIN2_MASK2, 0xFF},
+ { CS4234_TPS_CTRL, 0x00},
+ { CS4234_ADC_CTRL1, 0xC0},
+ { CS4234_ADC_CTRL2, 0xFF},
+ { CS4234_LOW_LAT_CTRL1, 0xE0},
+ { CS4234_DAC_CTRL1, 0xE0},
+ { CS4234_DAC_CTRL2, 0xE0},
+ { CS4234_DAC_CTRL3, 0xBF},
+ { CS4234_DAC_CTRL4, 0x1F},
+ { CS4234_VOLUME_MODE, 0x87},
+ { CS4234_MASTER_VOL, 0x10},
+ { CS4234_DAC1_VOL, 0x10},
+ { CS4234_DAC2_VOL, 0x10},
+ { CS4234_DAC3_VOL, 0x10},
+ { CS4234_DAC4_VOL, 0x10},
+ { CS4234_DAC5_VOL, 0x10},
+ { CS4234_INT_CTRL, 0x40},
+ { CS4234_INT_MASK1, 0x10},
+ { CS4234_INT_MASK2, 0x20},
+};
+
+static bool cs4234_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4234_DEVID_AB ... CS4234_DEVID_EF:
+ case CS4234_REVID ... CS4234_DAC5_VOL:
+ case CS4234_INT_CTRL ... CS4234_MAX_REGISTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4234_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4234_INT_NOTIFY1:
+ case CS4234_INT_NOTIFY2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4234_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4234_DEVID_AB ... CS4234_REVID:
+ case CS4234_INT_NOTIFY1 ... CS4234_INT_NOTIFY2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct snd_soc_component_driver soc_component_cs4234 = {
+ .dapm_widgets = cs4234_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4234_dapm_widgets),
+ .dapm_routes = cs4234_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4234_dapm_routes),
+ .controls = cs4234_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4234_snd_controls),
+ .set_bias_level = cs4234_set_bias_level,
+ .idle_bias_on = 1,
+ .suspend_bias_off = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs4234_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4234_MAX_REGISTER,
+ .readable_reg = cs4234_readable_register,
+ .volatile_reg = cs4234_volatile_reg,
+ .writeable_reg = cs4234_writeable_register,
+ .reg_defaults = cs4234_default_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const char * const cs4234_core_supplies[] = {
+ "VA",
+ "VL",
+};
+
+static void cs4234_shutdown(struct cs4234 *cs4234)
+{
+ cancel_delayed_work_sync(&cs4234->vq_ramp_delay);
+ reinit_completion(&cs4234->vq_ramp_complete);
+
+ regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK,
+ CS4234_VQ_RAMP_MASK);
+ msleep(50);
+ regcache_cache_only(cs4234->regmap, true);
+ /* Clear VQ Ramp Bit in cache for the next PowerUp */
+ regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, 0);
+ gpiod_set_value_cansleep(cs4234->reset_gpio, 0);
+ regulator_bulk_disable(cs4234->num_core_supplies, cs4234->core_supplies);
+ clk_disable_unprepare(cs4234->mclk);
+}
+
+static int cs4234_powerup(struct cs4234 *cs4234)
+{
+ int ret;
+
+ ret = clk_prepare_enable(cs4234->mclk);
+ if (ret) {
+ dev_err(cs4234->dev, "Failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(cs4234->num_core_supplies, cs4234->core_supplies);
+ if (ret) {
+ dev_err(cs4234->dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(cs4234->mclk);
+ return ret;
+ }
+
+ usleep_range(CS4234_HOLD_RESET_TIME_US, 2 * CS4234_HOLD_RESET_TIME_US);
+ gpiod_set_value_cansleep(cs4234->reset_gpio, 1);
+
+ /* Make sure hardware reset done 2 ms + (3000/MCLK) */
+ usleep_range(CS4234_BOOT_TIME_US, CS4234_BOOT_TIME_US * 2);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &cs4234->vq_ramp_delay,
+ msecs_to_jiffies(CS4234_VQ_CHARGE_MS));
+
+ return 0;
+}
+
+static int cs4234_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs4234 *cs4234;
+ struct device *dev = &i2c_client->dev;
+ unsigned int revid;
+ uint32_t devid;
+ uint8_t ids[3];
+ int ret = 0, i;
+
+ cs4234 = devm_kzalloc(dev, sizeof(*cs4234), GFP_KERNEL);
+ if (!cs4234)
+ return -ENOMEM;
+ i2c_set_clientdata(i2c_client, cs4234);
+ cs4234->dev = dev;
+ init_completion(&cs4234->vq_ramp_complete);
+ INIT_DELAYED_WORK(&cs4234->vq_ramp_delay, cs4234_vq_ramp_done);
+
+ cs4234->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4234->reset_gpio))
+ return PTR_ERR(cs4234->reset_gpio);
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs4234->core_supplies) < ARRAY_SIZE(cs4234_core_supplies));
+
+ cs4234->num_core_supplies = ARRAY_SIZE(cs4234_core_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs4234_core_supplies); i++)
+ cs4234->core_supplies[i].supply = cs4234_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, cs4234->num_core_supplies, cs4234->core_supplies);
+ if (ret) {
+ dev_err(dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ cs4234->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(cs4234->mclk)) {
+ ret = PTR_ERR(cs4234->mclk);
+ dev_err(dev, "Failed to get the mclk: %d\n", ret);
+ return ret;
+ }
+ cs4234->mclk_rate = clk_get_rate(cs4234->mclk);
+
+ if (cs4234->mclk_rate < 7680000 || cs4234->mclk_rate > 25600000) {
+ dev_err(dev, "Invalid Master Clock rate\n");
+ return -EINVAL;
+ }
+
+ cs4234->regmap = devm_regmap_init_i2c(i2c_client, &cs4234_regmap);
+ if (IS_ERR(cs4234->regmap)) {
+ ret = PTR_ERR(cs4234->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = cs4234_powerup(cs4234);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(cs4234->regmap, CS4234_DEVID_AB, ids, ARRAY_SIZE(ids));
+ if (ret < 0) {
+ dev_err(dev, "Failed to read DEVID: %d\n", ret);
+ goto fail_shutdown;
+ }
+
+ devid = (ids[0] << 16) | (ids[1] << 8) | ids[2];
+ if (devid != CS4234_SUPPORTED_ID) {
+ dev_err(dev, "Unknown device ID: %x\n", devid);
+ ret = -EINVAL;
+ goto fail_shutdown;
+ }
+
+ ret = regmap_read(cs4234->regmap, CS4234_REVID, &revid);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read CS4234_REVID: %d\n", ret);
+ goto fail_shutdown;
+ }
+
+ dev_info(dev, "Cirrus Logic CS4234, Alpha Rev: %02X, Numeric Rev: %02X\n",
+ (revid & 0xF0) >> 4, revid & 0x0F);
+
+ ret = regulator_get_voltage(cs4234->core_supplies[CS4234_SUPPLY_VA].consumer);
+ switch (ret) {
+ case 3135000 ... 3650000:
+ regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1,
+ CS4234_VA_SEL_MASK,
+ CS4234_3V3 << CS4234_VA_SEL_SHIFT);
+ break;
+ case 4750000 ... 5250000:
+ regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1,
+ CS4234_VA_SEL_MASK,
+ CS4234_5V << CS4234_VA_SEL_SHIFT);
+ break;
+ default:
+ dev_err(dev, "Invalid VA voltage\n");
+ ret = -EINVAL;
+ goto fail_shutdown;
+ }
+
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ memcpy(&cs4234->rate_dividers, &cs4234_dividers, sizeof(cs4234_dividers));
+ cs4234->rate_constraint.rats = cs4234->rate_dividers;
+
+ ret = snd_soc_register_component(dev, &soc_component_cs4234, cs4234_dai,
+ ARRAY_SIZE(cs4234_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component:%d\n", ret);
+ pm_runtime_disable(&i2c_client->dev);
+ goto fail_shutdown;
+ }
+
+ return ret;
+
+fail_shutdown:
+ cs4234_shutdown(cs4234);
+
+ return ret;
+}
+
+static void cs4234_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs4234 *cs4234 = i2c_get_clientdata(i2c_client);
+ struct device *dev = &i2c_client->dev;
+
+ snd_soc_unregister_component(dev);
+ pm_runtime_disable(dev);
+ cs4234_shutdown(cs4234);
+}
+
+static int cs4234_runtime_resume(struct device *dev)
+{
+ struct cs4234 *cs4234 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cs4234_powerup(cs4234);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(cs4234->regmap);
+ regcache_cache_only(cs4234->regmap, false);
+ ret = regcache_sync(cs4234->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync regmap: %d\n", ret);
+ cs4234_shutdown(cs4234);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs4234_runtime_suspend(struct device *dev)
+{
+ struct cs4234 *cs4234 = dev_get_drvdata(dev);
+
+ cs4234_shutdown(cs4234);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs4234_pm = {
+ RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL)
+};
+
+static const struct of_device_id cs4234_of_match[] = {
+ { .compatible = "cirrus,cs4234", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4234_of_match);
+
+static struct i2c_driver cs4234_i2c_driver = {
+ .driver = {
+ .name = "cs4234",
+ .pm = pm_ptr(&cs4234_pm),
+ .of_match_table = cs4234_of_match,
+ },
+ .probe = cs4234_i2c_probe,
+ .remove = cs4234_i2c_remove,
+};
+module_i2c_driver(cs4234_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC Cirrus Logic CS4234 driver");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/cs4234.h b/sound/soc/codecs/cs4234.h
new file mode 100644
index 000000000000..76a75afc198d
--- /dev/null
+++ b/sound/soc/codecs/cs4234.h
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC Audio driver for CS4234 codec
+ *
+ * Copyright (C) 2020 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS4234_H
+#define CS4234_H
+
+#define CS4234_DEVID_AB 0x01
+#define CS4234_DEVID_CD 0x02
+#define CS4234_DEVID_EF 0x03
+#define CS4234_REVID 0x05
+
+#define CS4234_CLOCK_SP 0x06
+#define CS4234_BASE_RATE_MASK 0xC0
+#define CS4234_BASE_RATE_SHIFT 6
+#define CS4234_SPEED_MODE_MASK 0x30
+#define CS4234_SPEED_MODE_SHIFT 4
+#define CS4234_MCLK_RATE_MASK 0x0E
+#define CS4234_MCLK_RATE_SHIFT 1
+
+#define CS4234_SAMPLE_WIDTH 0x07
+#define CS4234_SDOUTX_SW_MASK 0xC0
+#define CS4234_SDOUTX_SW_SHIFT 6
+#define CS4234_INPUT_SW_MASK 0x30
+#define CS4234_INPUT_SW_SHIFT 4
+#define CS4234_LOW_LAT_SW_MASK 0x0C
+#define CS4234_LOW_LAT_SW_SHIFT 2
+#define CS4234_DAC5_SW_MASK 0x03
+#define CS4234_DAC5_SW_SHIFT 0
+
+#define CS4234_SP_CTRL 0x08
+#define CS4234_INVT_SCLK_MASK 0x80
+#define CS4234_INVT_SCLK_SHIFT 7
+#define CS4234_DAC5_SRC_MASK 0x70
+#define CS4234_DAC5_SRC_SHIFT 4
+#define CS4234_SP_FORMAT_MASK 0x0C
+#define CS4234_SP_FORMAT_SHIFT 2
+#define CS4234_SDO_CHAIN_MASK 0x02
+#define CS4234_SDO_CHAIN_SHIFT 1
+#define CS4234_MST_SLV_MASK 0x01
+#define CS4234_MST_SLV_SHIFT 0
+
+#define CS4234_SP_DATA_SEL 0x09
+#define CS4234_DAC14_SRC_MASK 0x38
+#define CS4234_DAC14_SRC_SHIFT 3
+#define CS4234_LL_SRC_MASK 0x07
+#define CS4234_LL_SRC_SHIFT 0
+
+#define CS4234_SDIN1_MASK1 0x0A
+#define CS4234_SDIN1_MASK2 0x0B
+#define CS4234_SDIN2_MASK1 0x0C
+#define CS4234_SDIN2_MASK2 0x0D
+
+#define CS4234_TPS_CTRL 0x0E
+#define CS4234_TPS_MODE_MASK 0x80
+#define CS4234_TPS_MODE_SHIFT 7
+#define CS4234_TPS_OFST_MASK 0x70
+#define CS4234_TPS_OFST_SHIFT 4
+#define CS4234_GRP_DELAY_MASK 0x0F
+#define CS4234_GRP_DELAY_SHIFT 0
+
+#define CS4234_ADC_CTRL1 0x0F
+#define CS4234_VA_SEL_MASK 0x20
+#define CS4234_VA_SEL_SHIFT 5
+#define CS4234_ENA_HPF_MASK 0x10
+#define CS4234_ENA_HPF_SHIFT 4
+#define CS4234_INV_ADC_MASK 0x0F
+#define CS4234_INV_ADC4_MASK 0x08
+#define CS4234_INV_ADC4_SHIFT 3
+#define CS4234_INV_ADC3_MASK 0x04
+#define CS4234_INV_ADC3_SHIFT 2
+#define CS4234_INV_ADC2_MASK 0x02
+#define CS4234_INV_ADC2_SHIFT 1
+#define CS4234_INV_ADC1_MASK 0x01
+#define CS4234_INV_ADC1_SHIFT 0
+
+#define CS4234_ADC_CTRL2 0x10
+#define CS4234_MUTE_ADC4_MASK 0x80
+#define CS4234_MUTE_ADC4_SHIFT 7
+#define CS4234_MUTE_ADC3_MASK 0x40
+#define CS4234_MUTE_ADC3_SHIFT 6
+#define CS4234_MUTE_ADC2_MASK 0x20
+#define CS4234_MUTE_ADC2_SHIFT 5
+#define CS4234_MUTE_ADC1_MASK 0x10
+#define CS4234_MUTE_ADC1_SHIFT 4
+#define CS4234_PDN_ADC4_MASK 0x08
+#define CS4234_PDN_ADC4_SHIFT 3
+#define CS4234_PDN_ADC3_MASK 0x04
+#define CS4234_PDN_ADC3_SHIFT 2
+#define CS4234_PDN_ADC2_MASK 0x02
+#define CS4234_PDN_ADC2_SHIFT 1
+#define CS4234_PDN_ADC1_MASK 0x01
+#define CS4234_PDN_ADC1_SHIFT 0
+
+#define CS4234_LOW_LAT_CTRL1 0x11
+#define CS4234_LL_NG_MASK 0xE0
+#define CS4234_LL_NG_SHIFT 5
+#define CS4234_INV_LL_MASK 0x0F
+#define CS4234_INV_LL4_MASK 0x08
+#define CS4234_INV_LL4_SHIFT 3
+#define CS4234_INV_LL3_MASK 0x04
+#define CS4234_INV_LL3_SHIFT 2
+#define CS4234_INV_LL2_MASK 0x02
+#define CS4234_INV_LL2_SHIFT 1
+#define CS4234_INV_LL1_MASK 0x01
+#define CS4234_INV_LL1_SHIFT 0
+
+#define CS4234_DAC_CTRL1 0x12
+#define CS4234_DAC14_NG_MASK 0xE0
+#define CS4234_DAC14_NG_SHIFT 5
+#define CS4234_DAC14_DE_MASK 0x10
+#define CS4234_DAC14_DE_SHIFT 4
+#define CS4234_DAC5_DE_MASK 0x08
+#define CS4234_DAC5_DE_SHIFT 3
+#define CS4234_DAC5_MVC_MASK 0x04
+#define CS4234_DAC5_MVC_SHIFT 2
+#define CS4234_DAC5_CFG_FLTR_MASK 0x03
+#define CS4234_DAC5_CFG_FLTR_SHIFT 0
+
+#define CS4234_DAC_CTRL2 0x13
+#define CS4234_DAC5_NG_MASK 0xE0
+#define CS4234_DAC5_NG_SHIFT 5
+#define CS4234_INV_DAC_MASK 0x1F
+#define CS4234_INV_DAC5_MASK 0x10
+#define CS4234_INV_DAC5_SHIFT 4
+#define CS4234_INV_DAC4_MASK 0x08
+#define CS4234_INV_DAC4_SHIFT 3
+#define CS4234_INV_DAC3_MASK 0x04
+#define CS4234_INV_DAC3_SHIFT 2
+#define CS4234_INV_DAC2_MASK 0x02
+#define CS4234_INV_DAC2_SHIFT 1
+#define CS4234_INV_DAC1_MASK 0x01
+#define CS4234_INV_DAC1_SHIFT 0
+
+#define CS4234_DAC_CTRL3 0x14
+#define CS4234_DAC5_ATT_MASK 0x80
+#define CS4234_DAC5_ATT_SHIFT 7
+#define CS4234_DAC14_ATT_MASK 0x40
+#define CS4234_DAC14_ATT_SHIFT 6
+#define CS4234_MUTE_LL_MASK 0x20
+#define CS4234_MUTE_LL_SHIFT 5
+#define CS4234_MUTE_DAC5_MASK 0x10
+#define CS4234_MUTE_DAC5_SHIFT 4
+#define CS4234_MUTE_DAC4_MASK 0x08
+#define CS4234_MUTE_DAC4_SHIFT 3
+#define CS4234_MUTE_DAC3_MASK 0x04
+#define CS4234_MUTE_DAC3_SHIFT 2
+#define CS4234_MUTE_DAC2_MASK 0x02
+#define CS4234_MUTE_DAC2_SHIFT 1
+#define CS4234_MUTE_DAC1_MASK 0x01
+#define CS4234_MUTE_DAC1_SHIFT 0
+
+#define CS4234_DAC_CTRL4 0x15
+#define CS4234_VQ_RAMP_MASK 0x80
+#define CS4234_VQ_RAMP_SHIFT 7
+#define CS4234_TPS_GAIN_MASK 0x40
+#define CS4234_TPS_GAIN_SHIFT 6
+#define CS4234_PDN_DAC5_MASK 0x10
+#define CS4234_PDN_DAC5_SHIFT 4
+#define CS4234_PDN_DAC4_MASK 0x08
+#define CS4234_PDN_DAC4_SHIFT 3
+#define CS4234_PDN_DAC3_MASK 0x04
+#define CS4234_PDN_DAC3_SHIFT 2
+#define CS4234_PDN_DAC2_MASK 0x02
+#define CS4234_PDN_DAC2_SHIFT 1
+#define CS4234_PDN_DAC1_MASK 0x01
+#define CS4234_PDN_DAC1_SHIFT 0
+
+#define CS4234_VOLUME_MODE 0x16
+#define CS4234_MUTE_DELAY_MASK 0xC0
+#define CS4234_MUTE_DELAY_SHIFT 6
+#define CS4234_MIN_DELAY_MASK 0x38
+#define CS4234_MIN_DELAY_SHIFT 3
+#define CS4234_MAX_DELAY_MASK 0x07
+#define CS4234_MAX_DELAY_SHIFT 0
+
+#define CS4234_MASTER_VOL 0x17
+#define CS4234_DAC1_VOL 0x18
+#define CS4234_DAC2_VOL 0x19
+#define CS4234_DAC3_VOL 0x1A
+#define CS4234_DAC4_VOL 0x1B
+#define CS4234_DAC5_VOL 0x1C
+
+#define CS4234_INT_CTRL 0x1E
+#define CS4234_INT_MODE_MASK 0x80
+#define CS4234_INT_MODE_SHIFT 7
+#define CS4234_INT_PIN_MASK 0x60
+#define CS4234_INT_PIN_SHIFT 5
+
+#define CS4234_INT_MASK1 0x1F
+#define CS4234_MSK_TST_MODE_MASK 0x80
+#define CS4234_MSK_TST_MODE_ERR_SHIFT 7
+#define CS4234_MSK_SP_ERR_MASK 0x40
+#define CS4234_MSK_SP_ERR_SHIFT 6
+#define CS4234_MSK_CLK_ERR_MASK 0x08
+#define CS4234_MSK_CLK_ERR_SHIFT 5
+#define CS4234_MSK_ADC4_OVFL_MASK 0x08
+#define CS4234_MSK_ADC4_OVFL_SHIFT 3
+#define CS4234_MSK_ADC3_OVFL_MASK 0x04
+#define CS4234_MSK_ADC3_OVFL_SHIFT 2
+#define CS4234_MSK_ADC2_OVFL_MASK 0x02
+#define CS4234_MSK_ADC2_OVFL_SHIFT 1
+#define CS4234_MSK_ADC1_OVFL_MASK 0x01
+#define CS4234_MSK_ADC1_OVFL_SHIFT 0
+
+#define CS4234_INT_MASK2 0x20
+#define CS4234_MSK_DAC5_CLIP_MASK 0x10
+#define CS4234_MSK_DAC5_CLIP_SHIFT 4
+#define CS4234_MSK_DAC4_CLIP_MASK 0x08
+#define CS4234_MSK_DAC4_CLIP_SHIFT 3
+#define CS4234_MSK_DAC3_CLIP_MASK 0x04
+#define CS4234_MSK_DAC3_CLIP_SHIFT 2
+#define CS4234_MSK_DAC2_CLIP_MASK 0x02
+#define CS4234_MSK_DAC2_CLIP_SHIFT 1
+#define CS4234_MSK_DAC1_CLIP_MASK 0x01
+#define CS4234_MSK_DAC1_CLIP_SHIFT 0
+
+#define CS4234_INT_NOTIFY1 0x21
+#define CS4234_TST_MODE_MASK 0x80
+#define CS4234_TST_MODE_SHIFT 7
+#define CS4234_SP_ERR_MASK 0x40
+#define CS4234_SP_ERR_SHIFT 6
+#define CS4234_CLK_MOD_ERR_MASK 0x08
+#define CS4234_CLK_MOD_ERR_SHIFT 5
+#define CS4234_ADC4_OVFL_MASK 0x08
+#define CS4234_ADC4_OVFL_SHIFT 3
+#define CS4234_ADC3_OVFL_MASK 0x04
+#define CS4234_ADC3_OVFL_SHIFT 2
+#define CS4234_ADC2_OVFL_MASK 0x02
+#define CS4234_ADC2_OVFL_SHIFT 1
+#define CS4234_ADC1_OVFL_MASK 0x01
+#define CS4234_ADC1_OVFL_SHIFT 0
+
+#define CS4234_INT_NOTIFY2 0x22
+#define CS4234_DAC5_CLIP_MASK 0x10
+#define CS4234_DAC5_CLIP_SHIFT 4
+#define CS4234_DAC4_CLIP_MASK 0x08
+#define CS4234_DAC4_CLIP_SHIFT 3
+#define CS4234_DAC3_CLIP_MASK 0x04
+#define CS4234_DAC3_CLIP_SHIFT 2
+#define CS4234_DAC2_CLIP_MASK 0x02
+#define CS4234_DAC2_CLIP_SHIFT 1
+#define CS4234_DAC1_CLIP_MASK 0x01
+#define CS4234_DAC1_CLIP_SHIFT 0
+
+#define CS4234_MAX_REGISTER CS4234_INT_NOTIFY2
+
+#define CS4234_SUPPORTED_ID 0x423400
+#define CS4234_BOOT_TIME_US 3000
+#define CS4234_HOLD_RESET_TIME_US 1000
+#define CS4234_VQ_CHARGE_MS 1000
+
+#define CS4234_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define CS4234_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+enum cs4234_supplies {
+ CS4234_SUPPLY_VA = 0,
+ CS4234_SUPPLY_VL,
+};
+
+enum cs4234_va_sel {
+ CS4234_3V3 = 0,
+ CS4234_5V,
+};
+
+enum cs4234_sp_format {
+ CS4234_LEFT_J = 0,
+ CS4234_I2S,
+ CS4234_TDM,
+};
+
+enum cs4234_base_rate_advisory {
+ CS4234_48K = 0,
+ CS4234_44K1,
+ CS4234_32K,
+};
+
+#endif
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 55db19ddc5ff..3f759c13d6d1 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs4265.c -- CS4265 ALSA SoC audio driver
*
* Copyright 2014 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <paul.handrigan@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
@@ -60,7 +56,7 @@ static const struct reg_default cs4265_reg_defaults[] = {
static bool cs4265_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS4265_CHIP_ID ... CS4265_SPDIF_CTL2:
+ case CS4265_CHIP_ID ... CS4265_MAX_REGISTER:
return true;
default:
return false;
@@ -157,8 +153,7 @@ static const struct snd_kcontrol_new cs4265_snd_controls[] = {
SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
3, 1, 0),
SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
- SOC_SINGLE("MMTLR Data Switch", 0,
- 1, 1, 0),
+ SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, 0, 1, 0),
SOC_ENUM("Mono Channel Select", spdif_mono_select_enum),
SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24),
};
@@ -190,7 +185,7 @@ static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
&loopback_ctl),
- SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SWITCH("SPDIF", CS4265_SPDIF_CTL2, 5, 1,
&spdif_switch),
SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
&dac_switch),
@@ -221,10 +216,11 @@ static const struct snd_soc_dapm_route cs4265_audio_map[] = {
{"LINEOUTR", NULL, "DAC"},
{"SPDIFOUT", NULL, "SPDIF"},
+ {"Pre-amp MIC", NULL, "MICL"},
+ {"Pre-amp MIC", NULL, "MICR"},
+ {"ADC Mux", "MIC", "Pre-amp MIC"},
{"ADC Mux", "LINEIN", "LINEINL"},
{"ADC Mux", "LINEIN", "LINEINR"},
- {"ADC Mux", "MIC", "MICL"},
- {"ADC Mux", "MIC", "MICR"},
{"ADC", NULL, "ADC Mux"},
{"DOUT", NULL, "ADC"},
{"DAI1 Capture", NULL, "DOUT"},
@@ -322,12 +318,12 @@ static int cs4265_get_clk_index(int mclk, int rate)
static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component);
int i;
if (clk_id != 0) {
- dev_err(codec->dev, "Invalid clk_id %d\n", clk_id);
+ dev_err(component->dev, "Invalid clk_id %d\n", clk_id);
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
@@ -337,24 +333,24 @@ static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
}
}
cs4265->sysclk = 0;
- dev_err(codec->dev, "Invalid freq parameter %d\n", freq);
+ dev_err(component->dev, "Invalid freq parameter %d\n", freq);
return -EINVAL;
}
static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component);
u8 iface = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
CS4265_ADC_MASTER);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
0);
break;
@@ -381,22 +377,22 @@ static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return 0;
}
-static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs4265_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
CS4265_DAC_CTL_MUTE);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
CS4265_SPDIF_CTL2_MUTE);
} else {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
0);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
0);
}
@@ -407,8 +403,8 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component);
int index;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
@@ -418,45 +414,45 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
if (index >= 0) {
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
- snd_soc_update_bits(codec, CS4265_MCLK_FREQ,
+ snd_soc_component_update_bits(component, CS4265_MCLK_FREQ,
CS4265_MCLK_FREQ_MASK,
clk_map_table[index].mclkdiv << 4);
} else {
- dev_err(codec->dev, "can't get correct mclk\n");
+ dev_err(component->dev, "can't get correct mclk\n");
return -EINVAL;
}
switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (1 << 4));
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_DIF, (1 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (1 << 6));
break;
case SND_SOC_DAIFMT_RIGHT_J:
if (params_width(params) == 16) {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (2 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (2 << 6));
} else {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (3 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (3 << 6));
}
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, 0);
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_DIF, 0);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, 0);
break;
@@ -466,23 +462,23 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cs4265_set_bias_level(struct snd_soc_codec *codec,
+static int cs4265_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN, 0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
@@ -496,13 +492,15 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
#define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
static const struct snd_soc_dai_ops cs4265_ops = {
.hw_params = cs4265_pcm_hw_params,
- .digital_mute = cs4265_digital_mute,
+ .mute_stream = cs4265_mute,
.set_fmt = cs4265_set_fmt,
.set_sysclk = cs4265_set_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs4265_dai[] = {
@@ -544,16 +542,17 @@ static struct snd_soc_dai_driver cs4265_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_cs4265 = {
- .set_bias_level = cs4265_set_bias_level,
-
- .dapm_widgets = cs4265_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets),
- .dapm_routes = cs4265_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map),
-
- .controls = cs4265_snd_controls,
- .num_controls = ARRAY_SIZE(cs4265_snd_controls),
+static const struct snd_soc_component_driver soc_component_cs4265 = {
+ .set_bias_level = cs4265_set_bias_level,
+ .controls = cs4265_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4265_snd_controls),
+ .dapm_widgets = cs4265_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets),
+ .dapm_routes = cs4265_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config cs4265_regmap = {
@@ -565,14 +564,13 @@ static const struct regmap_config cs4265_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
.readable_reg = cs4265_readable_register,
.volatile_reg = cs4265_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs4265_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4265_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4265_private *cs4265;
- int ret = 0;
+ int ret;
unsigned int devid = 0;
unsigned int reg;
@@ -601,12 +599,17 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
i2c_set_clientdata(i2c_client, cs4265);
ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
devid = reg & CS4265_CHIP_ID_MASK;
if (devid != CS4265_CHIP_ID_VAL) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
- "CS4265 Device ID (%X). Expected %X\n",
- devid, CS4265_CHIP_ID);
+ "CS4265 Part Number ID: 0x%x Expected: 0x%x\n",
+ devid >> 4, CS4265_CHIP_ID_VAL >> 4);
return ret;
}
dev_info(&i2c_client->dev,
@@ -615,16 +618,17 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_cs4265, cs4265_dai,
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_cs4265, cs4265_dai,
ARRAY_SIZE(cs4265_dai));
- return ret;
}
-static int cs4265_i2c_remove(struct i2c_client *client)
+static void cs4265_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ struct cs4265_private *cs4265 = i2c_get_clientdata(i2c);
+
+ if (cs4265->reset_gpio)
+ gpiod_set_value_cansleep(cs4265->reset_gpio, 0);
}
static const struct of_device_id cs4265_of_match[] = {
@@ -634,7 +638,7 @@ static const struct of_device_id cs4265_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4265_of_match);
static const struct i2c_device_id cs4265_id[] = {
- { "cs4265", 0 },
+ { "cs4265" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4265_id);
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h
index 0a80a8dcec67..8bc28c2bf99e 100644
--- a/sound/soc/codecs/cs4265.h
+++ b/sound/soc/codecs/cs4265.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs4265.h -- CS4265 ALSA SoC audio driver
*
* Copyright 2014 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <paul.handrigan@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef __CS4265_H__
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e07807d96b68..3139f03cd42b 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -21,6 +21,7 @@
* - Power management is supported
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -29,21 +30,11 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
-/*
- * The codec isn't really big-endian or little-endian, since the I2S
- * interface requires data to be sent serially with the MSbit first.
- * However, to support BE and LE I2S devices, we specify both here. That
- * way, ALSA will always match the bit patterns.
- */
-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
/* CS4270 registers addresses */
#define CS4270_CHIPID 0x01 /* Chip ID */
@@ -137,6 +128,9 @@ struct cs4270_private {
/* power domain regulators */
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+
+ /* reset gpio */
+ struct gpio_desc *reset_gpio;
};
static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
@@ -148,11 +142,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
- { "Capture", NULL, "AINA" },
- { "Capture", NULL, "AINB" },
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
- { "AOUTA", NULL, "Playback" },
- { "AOUTB", NULL, "Playback" },
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
@@ -219,7 +213,7 @@ static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
{
/* Unreadable registers are considered volatile */
if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
- return 1;
+ return true;
return reg == CS4270_CHIPID;
}
@@ -254,8 +248,8 @@ static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
cs4270->mclk = freq;
return 0;
@@ -277,8 +271,8 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -287,21 +281,21 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- dev_err(codec->dev, "invalid dai format\n");
+ dev_err(component->dev, "invalid dai format\n");
return -EINVAL;
}
/* set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs4270->slave_mode = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs4270->slave_mode = 0;
break;
default:
/* all other modes are unsupported by the hardware */
- dev_err(codec->dev, "Unknown master/slave configuration\n");
+ dev_err(component->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
@@ -326,8 +320,8 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int ret;
unsigned int i;
unsigned int rate;
@@ -346,13 +340,13 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
if (i == NUM_MCLK_RATIOS) {
/* We did not find a matching ratio */
- dev_err(codec->dev, "could not find matching ratio\n");
+ dev_err(component->dev, "could not find matching ratio\n");
return -EINVAL;
}
/* Set the sample rate */
- reg = snd_soc_read(codec, CS4270_MODE);
+ reg = snd_soc_component_read(component, CS4270_MODE);
reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
reg |= cs4270_mode_ratios[i].mclk;
@@ -361,15 +355,15 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
else
reg |= cs4270_mode_ratios[i].speed_mode;
- ret = snd_soc_write(codec, CS4270_MODE, reg);
+ ret = snd_soc_component_write(component, CS4270_MODE, reg);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
/* Set the DAI format */
- reg = snd_soc_read(codec, CS4270_FORMAT);
+ reg = snd_soc_component_read(component, CS4270_FORMAT);
reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
switch (cs4270->mode) {
@@ -380,13 +374,13 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
break;
default:
- dev_err(codec->dev, "unknown dai format\n");
+ dev_err(component->dev, "unknown dai format\n");
return -EINVAL;
}
- ret = snd_soc_write(codec, CS4270_FORMAT, reg);
+ ret = snd_soc_component_write(component, CS4270_FORMAT, reg);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -397,19 +391,20 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
* cs4270_dai_mute - enable/disable the CS4270 external mute
* @dai: the SOC DAI
* @mute: 0 = disable mute, 1 = enable mute
+ * @direction: (ignored)
*
* This function toggles the mute bits in the MUTE register. The CS4270's
* mute capability is intended for external muting circuitry, so if the
* board does not have the MUTEA or MUTEB pins connected to such circuitry,
* then this function will do nothing.
*/
-static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
+static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg6;
- reg6 = snd_soc_read(codec, CS4270_MUTE);
+ reg6 = snd_soc_component_read(component, CS4270_MUTE);
if (mute)
reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
@@ -418,7 +413,7 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
reg6 |= cs4270->manual_mute;
}
- return snd_soc_write(codec, CS4270_MUTE, reg6);
+ return snd_soc_component_write(component, CS4270_MUTE, reg6);
}
/**
@@ -438,8 +433,8 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int left = !ucontrol->value.integer.value[0];
int right = !ucontrol->value.integer.value[1];
@@ -468,7 +463,8 @@ static const struct snd_soc_dai_ops cs4270_dai_ops = {
.hw_params = cs4270_hw_params,
.set_sysclk = cs4270_set_dai_sysclk,
.set_fmt = cs4270_set_dai_fmt,
- .digital_mute = cs4270_dai_mute,
+ .mute_stream = cs4270_dai_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs4270_dai = {
@@ -496,14 +492,14 @@ static struct snd_soc_dai_driver cs4270_dai = {
/**
* cs4270_probe - ASoC probe function
- * @pdev: platform device
+ * @component: ASoC component
*
* This function is called when ASoC has all the pieces it needs to
* instantiate a sound driver.
*/
-static int cs4270_probe(struct snd_soc_codec *codec)
+static int cs4270_probe(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int ret;
/* Disable auto-mute. This feature appears to be buggy. In some
@@ -511,9 +507,9 @@ static int cs4270_probe(struct snd_soc_codec *codec)
* this feature disabled by default. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
- ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
+ ret = snd_soc_component_update_bits(component, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -522,10 +518,10 @@ static int cs4270_probe(struct snd_soc_codec *codec)
* playback has started. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
- ret = snd_soc_update_bits(codec, CS4270_TRANS,
+ ret = snd_soc_component_update_bits(component, CS4270_TRANS,
CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -537,17 +533,15 @@ static int cs4270_probe(struct snd_soc_codec *codec)
/**
* cs4270_remove - ASoC remove function
- * @pdev: platform device
+ * @component: ASoC component
*
* This function is the counterpart to cs4270_probe().
*/
-static int cs4270_remove(struct snd_soc_codec *codec)
+static void cs4270_remove(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
-
- return 0;
};
#ifdef CONFIG_PM
@@ -561,16 +555,16 @@ static int cs4270_remove(struct snd_soc_codec *codec)
* and all registers are written back to the hardware when resuming.
*/
-static int cs4270_soc_suspend(struct snd_soc_codec *codec)
+static int cs4270_soc_suspend(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg, ret;
- reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+ reg = snd_soc_component_read(component, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
if (reg < 0)
return reg;
- ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+ ret = snd_soc_component_write(component, CS4270_PWRCTL, reg);
if (ret < 0)
return ret;
@@ -580,9 +574,9 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int cs4270_soc_resume(struct snd_soc_codec *codec)
+static int cs4270_soc_resume(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg, ret;
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
@@ -598,10 +592,10 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
regcache_sync(cs4270->regmap);
/* ... then disable the power-down bits */
- reg = snd_soc_read(codec, CS4270_PWRCTL);
+ reg = snd_soc_component_read(component, CS4270_PWRCTL);
reg &= ~CS4270_PWRCTL_PDN_ALL;
- return snd_soc_write(codec, CS4270_PWRCTL, reg);
+ return snd_soc_component_write(component, CS4270_PWRCTL, reg);
}
#else
#define cs4270_soc_suspend NULL
@@ -611,18 +605,20 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
/*
* ASoC codec driver structure
*/
-static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
- .probe = cs4270_probe,
- .remove = cs4270_remove,
- .suspend = cs4270_soc_suspend,
- .resume = cs4270_soc_resume,
-
- .controls = cs4270_snd_controls,
- .num_controls = ARRAY_SIZE(cs4270_snd_controls),
- .dapm_widgets = cs4270_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets),
- .dapm_routes = cs4270_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes),
+static const struct snd_soc_component_driver soc_component_device_cs4270 = {
+ .probe = cs4270_probe,
+ .remove = cs4270_remove,
+ .suspend = cs4270_soc_suspend,
+ .resume = cs4270_soc_resume,
+ .controls = cs4270_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4270_snd_controls),
+ .dapm_widgets = cs4270_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets),
+ .dapm_routes = cs4270_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
/*
@@ -640,24 +636,36 @@ static const struct regmap_config cs4270_regmap = {
.max_register = CS4270_LASTREG,
.reg_defaults = cs4270_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .write_flag_mask = CS4270_I2C_INCR,
.readable_reg = cs4270_reg_is_readable,
.volatile_reg = cs4270_reg_is_volatile,
};
/**
+ * cs4270_i2c_remove - deinitialize the I2C interface of the CS4270
+ * @i2c_client: the I2C client object
+ *
+ * This function puts the chip into low power mode when the i2c device
+ * is removed.
+ */
+static void cs4270_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client);
+
+ gpiod_set_value_cansleep(cs4270->reset_gpio, 0);
+}
+
+/**
* cs4270_i2c_probe - initialize the I2C interface of the CS4270
* @i2c_client: the I2C client object
- * @id: the I2C device ID (ignored)
*
* This function is called whenever the I2C subsystem finds a device that
* matches the device ID given via a prior call to i2c_add_driver().
*/
-static int cs4270_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4270_i2c_probe(struct i2c_client *i2c_client)
{
- struct device_node *np = i2c_client->dev.of_node;
struct cs4270_private *cs4270;
unsigned int val;
int ret, i;
@@ -677,21 +685,22 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
if (ret < 0)
return ret;
- /* See if we have a way to bring the codec out of reset */
- if (np) {
- enum of_gpio_flags flags;
- int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
-
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(&i2c_client->dev, gpio,
- flags & OF_GPIO_ACTIVE_LOW ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- "cs4270 reset");
- if (ret < 0)
- return ret;
- }
+ /* reset the device */
+ cs4270->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs4270->reset_gpio)) {
+ dev_dbg(&i2c_client->dev, "Error getting CS4270 reset GPIO\n");
+ return PTR_ERR(cs4270->reset_gpio);
}
+ if (cs4270->reset_gpio) {
+ dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs4270->reset_gpio, 1);
+ }
+
+ /* Sleep 500ns before i2c communications */
+ ndelay(500);
+
cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap);
if (IS_ERR(cs4270->regmap))
return PTR_ERR(cs4270->regmap);
@@ -716,28 +725,16 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
i2c_set_clientdata(i2c_client, cs4270);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_device_cs4270, &cs4270_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_device_cs4270, &cs4270_dai, 1);
return ret;
}
-/**
- * cs4270_i2c_remove - remove an I2C device
- * @i2c_client: the I2C client object
- *
- * This function is the counterpart to cs4270_i2c_probe().
- */
-static int cs4270_i2c_remove(struct i2c_client *i2c_client)
-{
- snd_soc_unregister_codec(&i2c_client->dev);
- return 0;
-}
-
/*
* cs4270_id - I2C device IDs supported by this driver
*/
static const struct i2c_device_id cs4270_id[] = {
- {"cs4270", 0},
+ {"cs4270"},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index dcb3223d7d8f..1d210b969173 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 I2C audio driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -20,27 +11,19 @@
#include <sound/soc.h>
#include "cs4271.h"
-static int cs4271_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4271_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
config = cs4271_regmap_config;
config.reg_bits = 8;
- config.val_bits = 8;
return cs4271_probe(&client->dev,
devm_regmap_init_i2c(client, &config));
}
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id cs4271_i2c_id[] = {
- { "cs4271", 0 },
+ { "cs4271" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
@@ -51,7 +34,6 @@ static struct i2c_driver cs4271_i2c_driver = {
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_i2c_probe,
- .remove = cs4271_i2c_remove,
.id_table = cs4271_i2c_id,
};
module_i2c_driver(cs4271_i2c_driver);
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
index 1ff5f520196a..4feb80436bd9 100644
--- a/sound/soc/codecs/cs4271-spi.c
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 SPI audio driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -26,26 +17,18 @@ static int cs4271_spi_probe(struct spi_device *spi)
config = cs4271_regmap_config;
config.reg_bits = 16;
- config.val_bits = 8;
config.read_flag_mask = 0x21;
config.write_flag_mask = 0x20;
return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
-static int cs4271_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver cs4271_spi_driver = {
.driver = {
.name = "cs4271",
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_spi_probe,
- .remove = cs4271_spi_remove,
};
module_spi_driver(cs4271_spi_driver);
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index e770ee6f36da..77dfc83a3c01 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -1,31 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 ASoC codec driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* This driver support CS4271 codec being master or slave, working
* in control port mode, connected either via SPI or I2C.
* The data format accepted is I2S or left-justified.
* DAPM support not implemented.
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/tlv.h>
@@ -157,6 +148,10 @@ static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
return reg == CS4271_CHIPID;
}
+static const char * const supply_names[] = {
+ "vd", "vl", "va"
+};
+
struct cs4271_private {
unsigned int mclk;
bool master;
@@ -165,11 +160,11 @@ struct cs4271_private {
/* Current sample rate for de-emphasis control */
int rate;
/* GPIO driving Reset pin, if any */
- int gpio_nreset;
- /* GPIO that disable serial bus, if any */
- int gpio_disable;
+ struct gpio_desc *reset;
/* enable soft reset workaround */
bool enable_soft_reset;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ struct clk *clk;
};
static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
@@ -200,8 +195,8 @@ static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
cs4271->mclk = freq;
return 0;
@@ -210,21 +205,21 @@ static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
unsigned int val = 0;
int ret;
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- cs4271->master = 0;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs4271->master = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- cs4271->master = 1;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs4271->master = true;
val |= CS4271_MODE1_MASTER;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -244,7 +239,7 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
return ret;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -257,9 +252,9 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int cs4271_deemph[] = {0, 44100, 48000, 32000};
-static int cs4271_set_deemph(struct snd_soc_codec *codec)
+static int cs4271_set_deemph(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int i, ret;
int val = CS4271_DACCTL_DEM_DIS;
@@ -283,8 +278,8 @@ static int cs4271_set_deemph(struct snd_soc_codec *codec)
static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = cs4271->deemph;
return 0;
@@ -293,11 +288,11 @@ static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
cs4271->deemph = ucontrol->value.integer.value[0];
- return cs4271_set_deemph(codec);
+ return cs4271_set_deemph(component);
}
struct cs4271_clk_cfg {
@@ -337,14 +332,14 @@ static struct cs4271_clk_cfg cs4271_clk_tab[] = {
{0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
};
-#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+#define CS4271_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
static int cs4271_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int i, ret;
unsigned int ratio, val;
@@ -359,9 +354,9 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
*/
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- !dai->capture_active) ||
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) ||
(substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
- !dai->playback_active)) {
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK))) {
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN,
CS4271_MODE2_PDN);
@@ -386,14 +381,14 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
val = CS4271_MODE1_MODE_4X;
ratio = cs4271->mclk / cs4271->rate;
- for (i = 0; i < CS4171_NR_RATIOS; i++)
+ for (i = 0; i < CS4271_NR_RATIOS; i++)
if ((cs4271_clk_tab[i].master == cs4271->master) &&
(cs4271_clk_tab[i].speed_mode == val) &&
(cs4271_clk_tab[i].ratio == ratio))
break;
- if (i == CS4171_NR_RATIOS) {
- dev_err(codec->dev, "Invalid sample rate\n");
+ if (i == CS4271_NR_RATIOS) {
+ dev_err(component->dev, "Invalid sample rate\n");
return -EINVAL;
}
@@ -404,13 +399,13 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return cs4271_set_deemph(codec);
+ return cs4271_set_deemph(component);
}
static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int ret;
int val_a = 0;
int val_b = 0;
@@ -484,14 +479,26 @@ static struct snd_soc_dai_driver cs4271_dai = {
.formats = CS4271_PCM_FORMATS,
},
.ops = &cs4271_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
+static int cs4271_reset(struct snd_soc_component *component)
+{
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ gpiod_direction_output(cs4271->reset, 1);
+ mdelay(1);
+ gpiod_set_value(cs4271->reset, 0);
+ mdelay(1);
+
+ return 0;
+}
+
#ifdef CONFIG_PM
-static int cs4271_soc_suspend(struct snd_soc_codec *codec)
+static int cs4271_soc_suspend(struct snd_soc_component *component)
{
int ret;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
/* Set power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
@@ -499,13 +506,33 @@ static int cs4271_soc_suspend(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
+ regcache_mark_dirty(cs4271->regmap);
+ clk_disable_unprepare(cs4271->clk);
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+
return 0;
}
-static int cs4271_soc_resume(struct snd_soc_codec *codec)
+static int cs4271_soc_resume(struct snd_soc_component *component)
{
int ret;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(cs4271->clk);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable clk: %d\n", ret);
+ return ret;
+ }
+
+ /* Do a proper reset after power up */
+ cs4271_reset(component);
/* Restore codec state */
ret = regcache_sync(cs4271->regmap);
@@ -534,48 +561,54 @@ MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
EXPORT_SYMBOL_GPL(cs4271_dt_ids);
#endif
-static int cs4271_codec_probe(struct snd_soc_codec *codec)
+static int cs4271_component_probe(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
- struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+ struct cs4271_platform_data *cs4271plat = component->dev->platform_data;
int ret;
- bool amutec_eq_bmutec = false;
+ bool amutec_eq_bmutec;
-#ifdef CONFIG_OF
- if (of_match_device(cs4271_dt_ids, codec->dev)) {
- if (of_get_property(codec->dev->of_node,
- "cirrus,amutec-eq-bmutec", NULL))
- amutec_eq_bmutec = true;
-
- if (of_get_property(codec->dev->of_node,
- "cirrus,enable-soft-reset", NULL))
- cs4271->enable_soft_reset = true;
+ amutec_eq_bmutec = of_property_read_bool(component->dev->of_node,
+ "cirrus,amutec-eq-bmutec");
+ cs4271->enable_soft_reset = of_property_read_bool(component->dev->of_node,
+ "cirrus,enable-soft-reset");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
}
-#endif
if (cs4271plat) {
amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
}
- if (gpio_is_valid(cs4271->gpio_nreset)) {
- /* Reset codec */
- gpio_direction_output(cs4271->gpio_nreset, 0);
- mdelay(1);
- gpio_set_value(cs4271->gpio_nreset, 1);
- /* Give the codec time to wake up */
- mdelay(1);
+ ret = clk_prepare_enable(cs4271->clk);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable clk: %d\n", ret);
+ goto err_disable_regulators;
}
+ /* Reset codec */
+ cs4271_reset(component);
+
+ ret = regcache_sync(cs4271->regmap);
+ if (ret < 0)
+ goto err_disable_clk;
+
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;
+ goto err_disable_clk;
+
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, 0);
if (ret < 0)
- return ret;
+ goto err_disable_clk;
+
/* Power-up sequence requires 85 uS */
udelay(85);
@@ -585,57 +618,71 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec)
CS4271_MODE2_MUTECAEQUB);
return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(cs4271->clk);
+err_disable_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+ return ret;
}
-static int cs4271_codec_remove(struct snd_soc_codec *codec)
+static void cs4271_component_remove(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(cs4271->gpio_nreset))
- /* Set codec to the reset state */
- gpio_set_value(cs4271->gpio_nreset, 0);
+ /* Set codec to the reset state */
+ gpiod_set_value(cs4271->reset, 1);
- return 0;
+ regcache_mark_dirty(cs4271->regmap);
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+ clk_disable_unprepare(cs4271->clk);
};
-static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
- .probe = cs4271_codec_probe,
- .remove = cs4271_codec_remove,
+static const struct snd_soc_component_driver soc_component_dev_cs4271 = {
+ .probe = cs4271_component_probe,
+ .remove = cs4271_component_remove,
.suspend = cs4271_soc_suspend,
.resume = cs4271_soc_resume,
-
.controls = cs4271_snd_controls,
.num_controls = ARRAY_SIZE(cs4271_snd_controls),
.dapm_widgets = cs4271_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets),
.dapm_routes = cs4271_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int cs4271_common_probe(struct device *dev,
struct cs4271_private **c)
{
- struct cs4271_platform_data *cs4271plat = dev->platform_data;
struct cs4271_private *cs4271;
+ int i, ret;
cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
if (!cs4271)
return -ENOMEM;
- if (of_match_device(cs4271_dt_ids, dev))
- cs4271->gpio_nreset =
- of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+ cs4271->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(cs4271->reset))
+ return dev_err_probe(dev, PTR_ERR(cs4271->reset),
+ "error retrieving RESET GPIO\n");
+ gpiod_set_consumer_name(cs4271->reset, "CS4271 Reset");
- if (cs4271plat)
- cs4271->gpio_nreset = cs4271plat->gpio_nreset;
+ cs4271->clk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(cs4271->clk))
+ return dev_err_probe(dev, PTR_ERR(cs4271->clk), "Failed to get mclk\n");
- if (gpio_is_valid(cs4271->gpio_nreset)) {
- int ret;
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ cs4271->supplies[i].supply = supply_names[i];
- ret = devm_gpio_request(dev, cs4271->gpio_nreset,
- "CS4271 Reset");
- if (ret < 0)
- return ret;
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
}
*c = cs4271;
@@ -647,8 +694,8 @@ const struct regmap_config cs4271_regmap_config = {
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
+ .cache_type = REGCACHE_FLAT,
+ .val_bits = 8,
.volatile_reg = cs4271_volatile_reg,
};
EXPORT_SYMBOL_GPL(cs4271_regmap_config);
@@ -668,8 +715,8 @@ int cs4271_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, cs4271);
cs4271->regmap = regmap;
- return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
- 1);
+ return devm_snd_soc_register_component(dev, &soc_component_dev_cs4271,
+ &cs4271_dai, 1);
}
EXPORT_SYMBOL_GPL(cs4271_probe);
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
index 9adad8eefdc9..290283a9149e 100644
--- a/sound/soc/codecs/cs4271.h
+++ b/sound/soc/codecs/cs4271.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _CS4271_PRIV_H
#define _CS4271_PRIV_H
diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c
new file mode 100644
index 000000000000..98b6718ccabf
--- /dev/null
+++ b/sound/soc/codecs/cs42l42-i2c.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l42-i2c.c -- CS42L42 ALSA SoC audio driver for I2C
+ *
+ * Copyright 2016, 2022 Cirrus Logic, Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs42l42.h"
+
+static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct device *dev = &i2c_client->dev;
+ struct cs42l42_private *cs42l42;
+ struct regmap *regmap;
+ int ret;
+
+ cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL);
+ if (!cs42l42)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap),
+ "regmap_init() failed\n");
+
+ cs42l42->devid = CS42L42_CHIP_ID;
+ cs42l42->dev = dev;
+ cs42l42->regmap = regmap;
+ cs42l42->irq = i2c_client->irq;
+
+ ret = cs42l42_common_probe(cs42l42, &cs42l42_soc_component, &cs42l42_dai);
+ if (ret)
+ return ret;
+
+ return cs42l42_init(cs42l42);
+}
+
+static void cs42l42_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&i2c_client->dev);
+
+ cs42l42_common_remove(cs42l42);
+}
+
+static int cs42l42_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l42_i2c_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_i2c_resume)
+};
+
+static const struct of_device_id __maybe_unused cs42l42_of_match[] = {
+ { .compatible = "cirrus,cs42l42", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l42_of_match);
+
+static const struct acpi_device_id __maybe_unused cs42l42_acpi_match[] = {
+ {"10134242", 0,},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match);
+
+static const struct i2c_device_id cs42l42_id[] = {
+ {"cs42l42"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l42_id);
+
+static struct i2c_driver cs42l42_i2c_driver = {
+ .driver = {
+ .name = "cs42l42",
+ .pm = pm_ptr(&cs42l42_i2c_pm_ops),
+ .of_match_table = of_match_ptr(cs42l42_of_match),
+ .acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
+ },
+ .id_table = cs42l42_id,
+ .probe = cs42l42_i2c_probe,
+ .remove = cs42l42_i2c_remove,
+};
+
+module_i2c_driver(cs42l42_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L42 I2C driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS42L42_CORE");
diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c
new file mode 100644
index 000000000000..f837c7eff10b
--- /dev/null
+++ b/sound/soc/codecs/cs42l42-sdw.c
@@ -0,0 +1,625 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// cs42l42-sdw.c -- CS42L42 ALSA SoC audio driver SoundWire driver
+//
+// Copyright (C) 2022 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+#include "cs42l42.h"
+
+#define CS42L42_SDW_CAPTURE_PORT 1
+#define CS42L42_SDW_PLAYBACK_PORT 2
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS42L42_SDW_ADDR_OFFSET 0x8000
+
+#define CS42L42_SDW_MEM_ACCESS_STATUS 0xd0
+#define CS42L42_SDW_MEM_READ_DATA 0xd8
+
+#define CS42L42_SDW_LAST_LATE BIT(3)
+#define CS42L42_SDW_CMD_IN_PROGRESS BIT(2)
+#define CS42L42_SDW_RDATA_RDY BIT(0)
+
+#define CS42L42_DELAYED_READ_POLL_US 1
+#define CS42L42_DELAYED_READ_TIMEOUT_US 100
+
+static const struct snd_soc_dapm_route cs42l42_sdw_audio_map[] = {
+ /* Playback Path */
+ { "HP", NULL, "MIXER" },
+ { "MIXER", NULL, "DACSRC" },
+ { "DACSRC", NULL, "Playback" },
+
+ /* Capture Path */
+ { "ADCSRC", NULL, "HS" },
+ { "Capture", NULL, "ADCSRC" },
+};
+
+static int cs42l42_sdw_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+
+ if (!cs42l42->init_done)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ /* Needed for PLL configuration when we are notified of new bus config */
+ cs42l42->sample_rate = params_rate(params);
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = CS42L42_SDW_PLAYBACK_PORT;
+ else
+ port_config.num = CS42L42_SDW_CAPTURE_PORT;
+
+ ret = sdw_stream_add_slave(cs42l42->sdw_peripheral, &stream_config, &port_config, 1,
+ sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ cs42l42_src_config(dai->component, params_rate(params));
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+
+ dev_dbg(dai->dev, "dai_prepare: sclk=%u rate=%u\n", cs42l42->sclk, cs42l42->sample_rate);
+
+ if (!cs42l42->sclk || !cs42l42->sample_rate)
+ return -EINVAL;
+
+ /*
+ * At this point we know the sample rate from hw_params, and the SWIRE_CLK from bus_config()
+ * callback. This could only fail if the ACPI or machine driver are misconfigured to allow
+ * an unsupported SWIRE_CLK and sample_rate combination.
+ */
+
+ return cs42l42_pll_config(dai->component, cs42l42->sclk, cs42l42->sample_rate);
+}
+
+static int cs42l42_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ sdw_stream_remove_slave(cs42l42->sdw_peripheral, sdw_stream);
+ cs42l42->sample_rate = 0;
+
+ return 0;
+}
+
+static int cs42l42_sdw_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&slave->dev);
+ unsigned int pdn_mask;
+
+ if (prepare_ch->num == CS42L42_SDW_PLAYBACK_PORT)
+ pdn_mask = CS42L42_HP_PDN_MASK;
+ else
+ pdn_mask = CS42L42_ADC_PDN_MASK;
+
+ if (state == SDW_OPS_PORT_PRE_PREP) {
+ dev_dbg(cs42l42->dev, "Prep Port pdn_mask:%x\n", pdn_mask);
+ regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask);
+ usleep_range(CS42L42_HP_ADC_EN_TIME_US, CS42L42_HP_ADC_EN_TIME_US + 1000);
+ } else if (state == SDW_OPS_PORT_POST_DEPREP) {
+ dev_dbg(cs42l42->dev, "Deprep Port pdn_mask:%x\n", pdn_mask);
+ regmap_set_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask);
+ }
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void cs42l42_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static const struct snd_soc_dai_ops cs42l42_sdw_dai_ops = {
+ .startup = cs42l42_sdw_dai_startup,
+ .shutdown = cs42l42_sdw_dai_shutdown,
+ .hw_params = cs42l42_sdw_dai_hw_params,
+ .prepare = cs42l42_sdw_dai_prepare,
+ .hw_free = cs42l42_sdw_dai_hw_free,
+ .mute_stream = cs42l42_mute_stream,
+ .set_stream = cs42l42_sdw_dai_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver cs42l42_sdw_dai = {
+ .name = "cs42l42-sdw",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ /* Restrict which rates and formats are supported */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ /* Restrict which rates and formats are supported */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs42l42_sdw_dai_ops,
+};
+
+static int cs42l42_sdw_poll_status(struct sdw_slave *peripheral, u8 mask, u8 match)
+{
+ int ret, sdwret;
+
+ ret = read_poll_timeout(sdw_read_no_pm, sdwret,
+ (sdwret < 0) || ((sdwret & mask) == match),
+ CS42L42_DELAYED_READ_POLL_US, CS42L42_DELAYED_READ_TIMEOUT_US,
+ false, peripheral, CS42L42_SDW_MEM_ACCESS_STATUS);
+ if (ret == 0)
+ ret = sdwret;
+
+ if (ret < 0)
+ dev_err(&peripheral->dev, "MEM_ACCESS_STATUS & %#x for %#x fail: %d\n",
+ mask, match, ret);
+
+ return ret;
+}
+
+static int cs42l42_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct sdw_slave *peripheral = context;
+ u8 data;
+ int ret;
+
+ reg += CS42L42_SDW_ADDR_OFFSET;
+
+ ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = sdw_read_no_pm(peripheral, reg);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to issue read @0x%x: %d\n", reg, ret);
+ return ret;
+ }
+
+ data = (u8)ret; /* possible non-delayed read value */
+ ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_ACCESS_STATUS);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to read MEM_ACCESS_STATUS: %d\n", ret);
+ return ret;
+ }
+
+ /* If read was not delayed we already have the result */
+ if ((ret & CS42L42_SDW_LAST_LATE) == 0) {
+ *val = data;
+ return 0;
+ }
+
+ /* Poll for delayed read completion */
+ if ((ret & CS42L42_SDW_RDATA_RDY) == 0) {
+ ret = cs42l42_sdw_poll_status(peripheral,
+ CS42L42_SDW_RDATA_RDY, CS42L42_SDW_RDATA_RDY);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_READ_DATA);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to read READ_DATA: %d\n", ret);
+ return ret;
+ }
+
+ *val = (u8)ret;
+
+ return 0;
+}
+
+static int cs42l42_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct sdw_slave *peripheral = context;
+ int ret;
+
+ ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0);
+ if (ret < 0)
+ return ret;
+
+ return sdw_write_no_pm(peripheral, reg + CS42L42_SDW_ADDR_OFFSET, (u8)val);
+}
+
+/* Initialise cs42l42 using SoundWire - this is only called once, during initialisation */
+static void cs42l42_sdw_init(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ int ret;
+
+ regcache_cache_only(cs42l42->regmap, false);
+
+ ret = cs42l42_init(cs42l42);
+ if (ret < 0) {
+ regcache_cache_only(cs42l42->regmap, true);
+ goto err;
+ }
+
+ /* Write out any cached changes that happened between probe and attach */
+ ret = regcache_sync(cs42l42->regmap);
+ if (ret < 0)
+ dev_warn(cs42l42->dev, "Failed to sync cache: %d\n", ret);
+
+ /* Disable internal logic that makes clock-stop conditional */
+ regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL3, CS42L42_SW_CLK_STP_STAT_SEL_MASK);
+
+err:
+ /* This cancels the pm_runtime_get_noresume() call from cs42l42_sdw_probe(). */
+ pm_runtime_put_autosuspend(cs42l42->dev);
+}
+
+static int cs42l42_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ struct sdw_slave_prop *prop = &peripheral->prop;
+ struct sdw_dpn_prop *ports;
+
+ ports = devm_kcalloc(cs42l42->dev, 2, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ prop->source_ports = BIT(CS42L42_SDW_CAPTURE_PORT);
+ prop->sink_ports = BIT(CS42L42_SDW_PLAYBACK_PORT);
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* DP1 - capture */
+ ports[0].num = CS42L42_SDW_CAPTURE_PORT;
+ ports[0].type = SDW_DPN_FULL;
+ ports[0].ch_prep_timeout = 10;
+ prop->src_dpn_prop = &ports[0];
+
+ /* DP2 - playback */
+ ports[1].num = CS42L42_SDW_PLAYBACK_PORT;
+ ports[1].type = SDW_DPN_FULL;
+ ports[1].ch_prep_timeout = 10;
+ prop->sink_dpn_prop = &ports[1];
+
+ return 0;
+}
+
+static int cs42l42_sdw_update_status(struct sdw_slave *peripheral,
+ enum sdw_slave_status status)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs42l42->dev, "ATTACHED\n");
+
+ /*
+ * The SoundWire core can report stale ATTACH notifications
+ * if we hard-reset CS42L42 in probe() but it had already been
+ * enumerated. Reject the ATTACH if we haven't yet seen an
+ * UNATTACH report for the device being in reset.
+ */
+ if (cs42l42->sdw_waiting_first_unattach)
+ break;
+
+ /*
+ * Initialise codec, this only needs to be done once.
+ * When resuming from suspend, resume callback will handle re-init of codec,
+ * using regcache_sync().
+ */
+ if (!cs42l42->init_done)
+ cs42l42_sdw_init(peripheral);
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs42l42->dev, "UNATTACHED\n");
+
+ if (cs42l42->sdw_waiting_first_unattach) {
+ /*
+ * SoundWire core has seen that CS42L42 is not on
+ * the bus so release RESET and wait for ATTACH.
+ */
+ cs42l42->sdw_waiting_first_unattach = false;
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l42_sdw_bus_config(struct sdw_slave *peripheral,
+ struct sdw_bus_params *params)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ unsigned int new_sclk = params->curr_dr_freq / 2;
+
+ /* The cs42l42 cannot support a glitchless SWIRE_CLK change. */
+ if ((new_sclk != cs42l42->sclk) && cs42l42->stream_use) {
+ dev_warn(cs42l42->dev, "Rejected SCLK change while audio active\n");
+ return -EBUSY;
+ }
+
+ cs42l42->sclk = new_sclk;
+
+ dev_dbg(cs42l42->dev, "bus_config: sclk=%u c=%u r=%u\n",
+ cs42l42->sclk, params->col, params->row);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops cs42l42_sdw_ops = {
+/* No interrupt callback because only hardware INT is supported for Jack Detect in the CS42L42 */
+ .read_prop = cs42l42_sdw_read_prop,
+ .update_status = cs42l42_sdw_update_status,
+ .bus_config = cs42l42_sdw_bus_config,
+ .port_prep = cs42l42_sdw_port_prep,
+};
+
+static int cs42l42_sdw_runtime_suspend(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "Runtime suspend\n");
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /* The host controller could suspend, which would mean no register access */
+ regcache_cache_only(cs42l42->regmap, true);
+
+ return 0;
+}
+
+static const struct reg_sequence cs42l42_soft_reboot_seq[] = {
+ REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e),
+};
+
+static int cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42)
+{
+ struct sdw_slave *peripheral = cs42l42->sdw_peripheral;
+
+ if (!peripheral->unattach_request)
+ return 0;
+
+ /* Cannot access registers until master re-attaches. */
+ dev_dbg(&peripheral->dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(&peripheral->dev, "initialization_complete timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ peripheral->unattach_request = 0;
+
+ /*
+ * After a bus reset there must be a reconfiguration reset to
+ * reinitialize the internal state of CS42L42.
+ */
+ regmap_multi_reg_write_bypassed(cs42l42->regmap,
+ cs42l42_soft_reboot_seq,
+ ARRAY_SIZE(cs42l42_soft_reboot_seq));
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+ regcache_mark_dirty(cs42l42->regmap);
+
+ return 0;
+}
+
+static int cs42l42_sdw_runtime_resume(struct device *dev)
+{
+ static const unsigned int ts_dbnce_ms[] = { 0, 125, 250, 500, 750, 1000, 1250, 1500};
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ unsigned int dbnce;
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ ret = cs42l42_sdw_handle_unattach(cs42l42);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ dbnce = max(cs42l42->ts_dbnc_rise, cs42l42->ts_dbnc_fall);
+
+ if (dbnce > 0)
+ msleep(ts_dbnce_ms[dbnce]);
+ }
+
+ regcache_cache_only(cs42l42->regmap, false);
+
+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */
+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1);
+ regcache_sync(cs42l42->regmap);
+
+ return 0;
+}
+
+static int cs42l42_sdw_resume(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "System resume\n");
+
+ /* Power-up so it can re-enumerate */
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ /* Wait for re-attach */
+ ret = cs42l42_sdw_handle_unattach(cs42l42);
+ if (ret < 0)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static int cs42l42_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+ struct snd_soc_component_driver *component_drv;
+ struct device *dev = &peripheral->dev;
+ struct cs42l42_private *cs42l42;
+ struct regmap_config *regmap_conf;
+ struct regmap *regmap;
+ int irq, ret;
+
+ cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL);
+ if (!cs42l42)
+ return -ENOMEM;
+
+ if (has_acpi_companion(dev))
+ irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
+ else
+ irq = of_irq_get(dev->of_node, 0);
+
+ if (irq == -ENOENT)
+ irq = 0;
+ else if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get IRQ\n");
+
+ regmap_conf = devm_kmemdup(dev, &cs42l42_regmap, sizeof(cs42l42_regmap), GFP_KERNEL);
+ if (!regmap_conf)
+ return -ENOMEM;
+ regmap_conf->reg_bits = 16;
+ regmap_conf->num_ranges = 0;
+ regmap_conf->reg_read = cs42l42_sdw_read;
+ regmap_conf->reg_write = cs42l42_sdw_write;
+
+ regmap = devm_regmap_init(dev, NULL, peripheral, regmap_conf);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(regmap, true);
+
+ component_drv = devm_kmemdup(dev,
+ &cs42l42_soc_component,
+ sizeof(cs42l42_soc_component),
+ GFP_KERNEL);
+ if (!component_drv)
+ return -ENOMEM;
+
+ component_drv->dapm_routes = cs42l42_sdw_audio_map;
+ component_drv->num_dapm_routes = ARRAY_SIZE(cs42l42_sdw_audio_map);
+
+ cs42l42->dev = dev;
+ cs42l42->regmap = regmap;
+ cs42l42->sdw_peripheral = peripheral;
+ cs42l42->irq = irq;
+ cs42l42->devid = CS42L42_CHIP_ID;
+
+ /*
+ * pm_runtime is needed to control bus manager suspend, and to
+ * recover from an unattach_request when the manager suspends.
+ */
+ pm_runtime_set_autosuspend_delay(cs42l42->dev, 3000);
+ pm_runtime_use_autosuspend(cs42l42->dev);
+ pm_runtime_mark_last_busy(cs42l42->dev);
+ pm_runtime_set_active(cs42l42->dev);
+ pm_runtime_get_noresume(cs42l42->dev);
+ pm_runtime_enable(cs42l42->dev);
+
+ ret = cs42l42_common_probe(cs42l42, component_drv, &cs42l42_sdw_dai);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs42l42_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+
+ cs42l42_common_remove(cs42l42);
+ pm_runtime_disable(cs42l42->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l42_sdw_pm = {
+ SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume)
+ RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id cs42l42_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x4242, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs42l42_sdw_id);
+
+static struct sdw_driver cs42l42_sdw_driver = {
+ .driver = {
+ .name = "cs42l42-sdw",
+ .pm = pm_ptr(&cs42l42_sdw_pm),
+ },
+ .probe = cs42l42_sdw_probe,
+ .remove = cs42l42_sdw_remove,
+ .ops = &cs42l42_sdw_ops,
+ .id_table = cs42l42_sdw_id,
+};
+
+module_sdw_driver(cs42l42_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L42 SoundWire driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS42L42_CORE");
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
new file mode 100644
index 000000000000..2652a639a79a
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.c
@@ -0,0 +1,2495 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l42.c -- CS42L42 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <dt-bindings/sound/cs42l42.h>
+
+#include "cs42l42.h"
+#include "cirrus_legacy.h"
+
+static const char * const cs42l42_supply_names[] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD_FILT",
+ "VL",
+};
+
+static const struct reg_default cs42l42_reg_defaults[] = {
+ { CS42L42_FRZ_CTL, 0x00 },
+ { CS42L42_SRC_CTL, 0x10 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SFTRAMP_RATE, 0xA4 },
+ { CS42L42_SLOW_START_ENABLE, 0x70 },
+ { CS42L42_I2C_DEBOUNCE, 0x88 },
+ { CS42L42_I2C_STRETCH, 0x03 },
+ { CS42L42_I2C_TIMEOUT, 0xB7 },
+ { CS42L42_PWR_CTL1, 0xFF },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL1, 0x40 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_OSC_SWITCH, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x1B },
+ { CS42L42_TSENSE_CTL, 0x1B },
+ { CS42L42_TSRS_INT_DISABLE, 0x00 },
+ { CS42L42_HSDET_CTL1, 0x77 },
+ { CS42L42_HSDET_CTL2, 0x00 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_SPDIF_CLK_CFG, 0x00 },
+ { CS42L42_FSYNC_PW_LOWER, 0x00 },
+ { CS42L42_FSYNC_PW_UPPER, 0x00 },
+ { CS42L42_FSYNC_P_LOWER, 0xF9 },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x10 },
+ { CS42L42_FS_RATE_EN, 0x00 },
+ { CS42L42_IN_ASRC_CLK, 0x00 },
+ { CS42L42_OUT_ASRC_CLK, 0x00 },
+ { CS42L42_PLL_DIV_CFG1, 0x00 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0x01 },
+ { CS42L42_MIXER_INT_MASK, 0x0F },
+ { CS42L42_SRC_INT_MASK, 0x0F },
+ { CS42L42_ASP_RX_INT_MASK, 0x1F },
+ { CS42L42_ASP_TX_INT_MASK, 0x0F },
+ { CS42L42_CODEC_INT_MASK, 0x03 },
+ { CS42L42_SRCPL_INT_MASK, 0x7F },
+ { CS42L42_VPMON_INT_MASK, 0x01 },
+ { CS42L42_PLL_LOCK_INT_MASK, 0x01 },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
+ { CS42L42_PLL_CTL1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC0, 0x00 },
+ { CS42L42_PLL_DIV_FRAC1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC2, 0x00 },
+ { CS42L42_PLL_DIV_INT, 0x40 },
+ { CS42L42_PLL_CTL3, 0x10 },
+ { CS42L42_PLL_CAL_RATIO, 0x80 },
+ { CS42L42_PLL_CTL4, 0x03 },
+ { CS42L42_LOAD_DET_EN, 0x00 },
+ { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
+ { CS42L42_WAKE_CTL, 0xC0 },
+ { CS42L42_ADC_DISABLE_MUTE, 0x00 },
+ { CS42L42_TIPSENSE_CTL, 0x02 },
+ { CS42L42_MISC_DET_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0x1F },
+ { CS42L42_MIC_DET_CTL2, 0x2F },
+ { CS42L42_DET_INT1_MASK, 0xE0 },
+ { CS42L42_DET_INT2_MASK, 0xFF },
+ { CS42L42_HS_BIAS_CTL, 0xC2 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { CS42L42_ADC_VOLUME, 0x00 },
+ { CS42L42_ADC_WNF_HPF_CTL, 0x71 },
+ { CS42L42_DAC_CTL1, 0x00 },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_CLASSH_CTL, 0x07 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_EQ_COEF_IN0, 0x00 },
+ { CS42L42_EQ_COEF_IN1, 0x00 },
+ { CS42L42_EQ_COEF_IN2, 0x00 },
+ { CS42L42_EQ_COEF_IN3, 0x00 },
+ { CS42L42_EQ_COEF_RW, 0x00 },
+ { CS42L42_EQ_COEF_OUT0, 0x00 },
+ { CS42L42_EQ_COEF_OUT1, 0x00 },
+ { CS42L42_EQ_COEF_OUT2, 0x00 },
+ { CS42L42_EQ_COEF_OUT3, 0x00 },
+ { CS42L42_EQ_INIT_STAT, 0x00 },
+ { CS42L42_EQ_START_FILT, 0x00 },
+ { CS42L42_EQ_MUTE_CTL, 0x00 },
+ { CS42L42_SP_RX_CH_SEL, 0x04 },
+ { CS42L42_SP_RX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_RX_FS, 0x8C },
+ { CS42l42_SPDIF_CH_SEL, 0x0E },
+ { CS42L42_SP_TX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_TX_FS, 0xCC },
+ { CS42L42_SPDIF_SW_CTL1, 0x3F },
+ { CS42L42_SRC_SDIN_FS, 0x40 },
+ { CS42L42_SRC_SDOUT_FS, 0x40 },
+ { CS42L42_SPDIF_CTL1, 0x01 },
+ { CS42L42_SPDIF_CTL2, 0x00 },
+ { CS42L42_SPDIF_CTL3, 0x00 },
+ { CS42L42_SPDIF_CTL4, 0x42 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x0F },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
+};
+
+bool cs42l42_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L42_PAGE_REGISTER:
+ case CS42L42_DEVID_AB:
+ case CS42L42_DEVID_CD:
+ case CS42L42_DEVID_E:
+ case CS42L42_FABID:
+ case CS42L42_REVID:
+ case CS42L42_FRZ_CTL:
+ case CS42L42_SRC_CTL:
+ case CS42L42_MCLK_STATUS:
+ case CS42L42_MCLK_CTL:
+ case CS42L42_SFTRAMP_RATE:
+ case CS42L42_SLOW_START_ENABLE:
+ case CS42L42_I2C_DEBOUNCE:
+ case CS42L42_I2C_STRETCH:
+ case CS42L42_I2C_TIMEOUT:
+ case CS42L42_PWR_CTL1:
+ case CS42L42_PWR_CTL2:
+ case CS42L42_PWR_CTL3:
+ case CS42L42_RSENSE_CTL1:
+ case CS42L42_RSENSE_CTL2:
+ case CS42L42_OSC_SWITCH:
+ case CS42L42_OSC_SWITCH_STATUS:
+ case CS42L42_RSENSE_CTL3:
+ case CS42L42_TSENSE_CTL:
+ case CS42L42_TSRS_INT_DISABLE:
+ case CS42L42_TRSENSE_STATUS:
+ case CS42L42_HSDET_CTL1:
+ case CS42L42_HSDET_CTL2:
+ case CS42L42_HS_SWITCH_CTL:
+ case CS42L42_HS_DET_STATUS:
+ case CS42L42_HS_CLAMP_DISABLE:
+ case CS42L42_MCLK_SRC_SEL:
+ case CS42L42_SPDIF_CLK_CFG:
+ case CS42L42_FSYNC_PW_LOWER:
+ case CS42L42_FSYNC_PW_UPPER:
+ case CS42L42_FSYNC_P_LOWER:
+ case CS42L42_FSYNC_P_UPPER:
+ case CS42L42_ASP_CLK_CFG:
+ case CS42L42_ASP_FRM_CFG:
+ case CS42L42_FS_RATE_EN:
+ case CS42L42_IN_ASRC_CLK:
+ case CS42L42_OUT_ASRC_CLK:
+ case CS42L42_PLL_DIV_CFG1:
+ case CS42L42_ADC_OVFL_STATUS:
+ case CS42L42_MIXER_STATUS:
+ case CS42L42_SRC_STATUS:
+ case CS42L42_ASP_RX_STATUS:
+ case CS42L42_ASP_TX_STATUS:
+ case CS42L42_CODEC_STATUS:
+ case CS42L42_DET_INT_STATUS1:
+ case CS42L42_DET_INT_STATUS2:
+ case CS42L42_SRCPL_INT_STATUS:
+ case CS42L42_VPMON_STATUS:
+ case CS42L42_PLL_LOCK_STATUS:
+ case CS42L42_TSRS_PLUG_STATUS:
+ case CS42L42_ADC_OVFL_INT_MASK:
+ case CS42L42_MIXER_INT_MASK:
+ case CS42L42_SRC_INT_MASK:
+ case CS42L42_ASP_RX_INT_MASK:
+ case CS42L42_ASP_TX_INT_MASK:
+ case CS42L42_CODEC_INT_MASK:
+ case CS42L42_SRCPL_INT_MASK:
+ case CS42L42_VPMON_INT_MASK:
+ case CS42L42_PLL_LOCK_INT_MASK:
+ case CS42L42_TSRS_PLUG_INT_MASK:
+ case CS42L42_PLL_CTL1:
+ case CS42L42_PLL_DIV_FRAC0:
+ case CS42L42_PLL_DIV_FRAC1:
+ case CS42L42_PLL_DIV_FRAC2:
+ case CS42L42_PLL_DIV_INT:
+ case CS42L42_PLL_CTL3:
+ case CS42L42_PLL_CAL_RATIO:
+ case CS42L42_PLL_CTL4:
+ case CS42L42_LOAD_DET_RCSTAT:
+ case CS42L42_LOAD_DET_DONE:
+ case CS42L42_LOAD_DET_EN:
+ case CS42L42_HSBIAS_SC_AUTOCTL:
+ case CS42L42_WAKE_CTL:
+ case CS42L42_ADC_DISABLE_MUTE:
+ case CS42L42_TIPSENSE_CTL:
+ case CS42L42_MISC_DET_CTL:
+ case CS42L42_MIC_DET_CTL1:
+ case CS42L42_MIC_DET_CTL2:
+ case CS42L42_DET_STATUS1:
+ case CS42L42_DET_STATUS2:
+ case CS42L42_DET_INT1_MASK:
+ case CS42L42_DET_INT2_MASK:
+ case CS42L42_HS_BIAS_CTL:
+ case CS42L42_ADC_CTL:
+ case CS42L42_ADC_VOLUME:
+ case CS42L42_ADC_WNF_HPF_CTL:
+ case CS42L42_DAC_CTL1:
+ case CS42L42_DAC_CTL2:
+ case CS42L42_HP_CTL:
+ case CS42L42_CLASSH_CTL:
+ case CS42L42_MIXER_CHA_VOL:
+ case CS42L42_MIXER_ADC_VOL:
+ case CS42L42_MIXER_CHB_VOL:
+ case CS42L42_EQ_COEF_IN0:
+ case CS42L42_EQ_COEF_IN1:
+ case CS42L42_EQ_COEF_IN2:
+ case CS42L42_EQ_COEF_IN3:
+ case CS42L42_EQ_COEF_RW:
+ case CS42L42_EQ_COEF_OUT0:
+ case CS42L42_EQ_COEF_OUT1:
+ case CS42L42_EQ_COEF_OUT2:
+ case CS42L42_EQ_COEF_OUT3:
+ case CS42L42_EQ_INIT_STAT:
+ case CS42L42_EQ_START_FILT:
+ case CS42L42_EQ_MUTE_CTL:
+ case CS42L42_SP_RX_CH_SEL:
+ case CS42L42_SP_RX_ISOC_CTL:
+ case CS42L42_SP_RX_FS:
+ case CS42l42_SPDIF_CH_SEL:
+ case CS42L42_SP_TX_ISOC_CTL:
+ case CS42L42_SP_TX_FS:
+ case CS42L42_SPDIF_SW_CTL1:
+ case CS42L42_SRC_SDIN_FS:
+ case CS42L42_SRC_SDOUT_FS:
+ case CS42L42_SOFT_RESET_REBOOT:
+ case CS42L42_SPDIF_CTL1:
+ case CS42L42_SPDIF_CTL2:
+ case CS42L42_SPDIF_CTL3:
+ case CS42L42_SPDIF_CTL4:
+ case CS42L42_ASP_TX_SZ_EN:
+ case CS42L42_ASP_TX_CH_EN:
+ case CS42L42_ASP_TX_CH_AP_RES:
+ case CS42L42_ASP_TX_CH1_BIT_MSB:
+ case CS42L42_ASP_TX_CH1_BIT_LSB:
+ case CS42L42_ASP_TX_HIZ_DLY_CFG:
+ case CS42L42_ASP_TX_CH2_BIT_MSB:
+ case CS42L42_ASP_TX_CH2_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_EN:
+ case CS42L42_ASP_RX_DAI0_CH1_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH1_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH1_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH2_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH2_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH2_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH3_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH3_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH3_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH4_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH4_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH4_BIT_LSB:
+ case CS42L42_ASP_RX_DAI1_CH1_AP_RES:
+ case CS42L42_ASP_RX_DAI1_CH1_BIT_MSB:
+ case CS42L42_ASP_RX_DAI1_CH1_BIT_LSB:
+ case CS42L42_ASP_RX_DAI1_CH2_AP_RES:
+ case CS42L42_ASP_RX_DAI1_CH2_BIT_MSB:
+ case CS42L42_ASP_RX_DAI1_CH2_BIT_LSB:
+ case CS42L42_SUB_REVID:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_readable_register, "SND_SOC_CS42L42_CORE");
+
+bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L42_DEVID_AB:
+ case CS42L42_DEVID_CD:
+ case CS42L42_DEVID_E:
+ case CS42L42_MCLK_STATUS:
+ case CS42L42_OSC_SWITCH_STATUS:
+ case CS42L42_TRSENSE_STATUS:
+ case CS42L42_HS_DET_STATUS:
+ case CS42L42_ADC_OVFL_STATUS:
+ case CS42L42_MIXER_STATUS:
+ case CS42L42_SRC_STATUS:
+ case CS42L42_ASP_RX_STATUS:
+ case CS42L42_ASP_TX_STATUS:
+ case CS42L42_CODEC_STATUS:
+ case CS42L42_DET_INT_STATUS1:
+ case CS42L42_DET_INT_STATUS2:
+ case CS42L42_SRCPL_INT_STATUS:
+ case CS42L42_VPMON_STATUS:
+ case CS42L42_PLL_LOCK_STATUS:
+ case CS42L42_TSRS_PLUG_STATUS:
+ case CS42L42_LOAD_DET_RCSTAT:
+ case CS42L42_LOAD_DET_DONE:
+ case CS42L42_DET_STATUS1:
+ case CS42L42_DET_STATUS2:
+ case CS42L42_SOFT_RESET_REBOOT:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_volatile_register, "SND_SOC_CS42L42_CORE");
+
+const struct regmap_range_cfg cs42l42_page_range = {
+ .name = "Pages",
+ .range_min = 0,
+ .range_max = CS42L42_MAX_REGISTER,
+ .selector_reg = CS42L42_PAGE_REGISTER,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_page_range, "SND_SOC_CS42L42_CORE");
+
+const struct regmap_config cs42l42_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = cs42l42_readable_register,
+ .volatile_reg = cs42l42_volatile_register,
+
+ .ranges = &cs42l42_page_range,
+ .num_ranges = 1,
+
+ .max_register = CS42L42_MAX_REGISTER,
+ .reg_defaults = cs42l42_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_regmap, "SND_SOC_CS42L42_CORE");
+
+static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true);
+static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true);
+
+static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+
+ /* all bits of SLOW_START_EN must change together */
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ val = 0;
+ break;
+ case 1:
+ val = CS42L42_SLOW_START_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE,
+ CS42L42_SLOW_START_EN_MASK, val);
+}
+
+static const char * const cs42l42_hpf_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_hpf_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_HPF_CF_SHIFT,
+ cs42l42_hpf_freq_text);
+
+static const char * const cs42l42_wnf3_freq_text[] = {
+ "160Hz", "180Hz", "200Hz", "220Hz",
+ "240Hz", "260Hz", "280Hz", "300Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_wnf3_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_CF_SHIFT,
+ cs42l42_wnf3_freq_text);
+
+static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
+ /* ADC Volume and Filter Controls */
+ SOC_SINGLE("ADC Notch Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_NOTCH_DIS_SHIFT, true, true),
+ SOC_SINGLE("ADC Weak Force Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_FORCE_WEAK_VCM_SHIFT, true, false),
+ SOC_SINGLE("ADC Invert Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_INV_SHIFT, true, false),
+ SOC_SINGLE("ADC Boost Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_DIG_BOOST_SHIFT, true, false),
+ SOC_SINGLE_S8_TLV("ADC Volume", CS42L42_ADC_VOLUME, -97, 12, adc_tlv),
+ SOC_SINGLE("ADC WNF Switch", CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_EN_SHIFT, true, false),
+ SOC_SINGLE("ADC HPF Switch", CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_HPF_EN_SHIFT, true, false),
+ SOC_ENUM("HPF Corner Freq", cs42l42_hpf_freq_enum),
+ SOC_ENUM("WNF 3dB Freq", cs42l42_wnf3_freq_enum),
+
+ /* DAC Volume and Filter Controls */
+ SOC_SINGLE("DACA Invert Switch", CS42L42_DAC_CTL1,
+ CS42L42_DACA_INV_SHIFT, true, false),
+ SOC_SINGLE("DACB Invert Switch", CS42L42_DAC_CTL1,
+ CS42L42_DACB_INV_SHIFT, true, false),
+ SOC_SINGLE("DAC HPF Switch", CS42L42_DAC_CTL2,
+ CS42L42_DAC_HPF_EN_SHIFT, true, false),
+ SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL,
+ CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT,
+ 0x3f, 1, mixer_tlv),
+
+ SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE,
+ CS42L42_SLOW_START_EN_SHIFT, true, false,
+ snd_soc_get_volsw, cs42l42_slow_start_put),
+};
+
+static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs42l42->hp_adc_up_pending = true;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Only need one delay if HP and ADC are both powering-up */
+ if (cs42l42->hp_adc_up_pending) {
+ usleep_range(CS42L42_HP_ADC_EN_TIME_US,
+ CS42L42_HP_ADC_EN_TIME_US + 1000);
+ cs42l42->hp_adc_up_pending = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
+ /* Playback Path */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1,
+ cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ /* Playback Requirements */
+ SND_SOC_DAPM_SUPPLY("ASP DAI0", CS42L42_PWR_CTL1, CS42L42_ASP_DAI_PDN_SHIFT, 1, NULL, 0),
+
+ /* Capture Path */
+ SND_SOC_DAPM_INPUT("HS"),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1,
+ cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0),
+
+ /* Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("ASP DAO0", CS42L42_PWR_CTL1, CS42L42_ASP_DAO_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASP TX EN", CS42L42_ASP_TX_SZ_EN, CS42L42_ASP_TX_EN_SHIFT, 0, NULL, 0),
+
+ /* Playback/Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0),
+
+ /* Soundwire SRC power control */
+ SND_SOC_DAPM_PGA("DACSRC", CS42L42_PWR_CTL2, CS42L42_DAC_SRC_PDNB_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADCSRC", CS42L42_PWR_CTL2, CS42L42_ADC_SRC_PDNB_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
+ /* Playback Path */
+ {"HP", NULL, "DAC"},
+ {"DAC", NULL, "MIXER"},
+ {"MIXER", NULL, "SDIN1"},
+ {"MIXER", NULL, "SDIN2"},
+ {"SDIN1", NULL, "Playback"},
+ {"SDIN2", NULL, "Playback"},
+
+ /* Playback Requirements */
+ {"SDIN1", NULL, "ASP DAI0"},
+ {"SDIN2", NULL, "ASP DAI0"},
+ {"SDIN1", NULL, "SCLK"},
+ {"SDIN2", NULL, "SCLK"},
+
+ /* Capture Path */
+ {"ADC", NULL, "HS"},
+ { "SDOUT1", NULL, "ADC" },
+ { "SDOUT2", NULL, "ADC" },
+ { "Capture", NULL, "SDOUT1" },
+ { "Capture", NULL, "SDOUT2" },
+
+ /* Capture Requirements */
+ { "SDOUT1", NULL, "ASP DAO0" },
+ { "SDOUT2", NULL, "ASP DAO0" },
+ { "SDOUT1", NULL, "SCLK" },
+ { "SDOUT2", NULL, "SCLK" },
+ { "SDOUT1", NULL, "ASP TX EN" },
+ { "SDOUT2", NULL, "ASP TX EN" },
+};
+
+static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l42->irq_lock);
+ cs42l42->jack = jk;
+
+ if (jk) {
+ switch (cs42l42->hs_type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET);
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE);
+ break;
+ default:
+ break;
+ }
+ }
+ mutex_unlock(&cs42l42->irq_lock);
+
+ return 0;
+}
+
+const struct snd_soc_component_driver cs42l42_soc_component = {
+ .set_jack = cs42l42_set_jack,
+ .dapm_widgets = cs42l42_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets),
+ .dapm_routes = cs42l42_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map),
+ .controls = cs42l42_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l42_snd_controls),
+ .endianness = 1,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, "SND_SOC_CS42L42_CORE");
+
+/* Switch to SCLK. Atomic delay after the write to allow the switch to complete. */
+static const struct reg_sequence cs42l42_to_sclk_seq[] = {
+ {
+ .reg = CS42L42_OSC_SWITCH,
+ .def = CS42L42_SCLK_PRESENT_MASK,
+ .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US,
+ },
+};
+
+/* Switch to OSC. Atomic delay after the write to allow the switch to complete. */
+static const struct reg_sequence cs42l42_to_osc_seq[] = {
+ {
+ .reg = CS42L42_OSC_SWITCH,
+ .def = 0,
+ .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US,
+ },
+};
+
+struct cs42l42_pll_params {
+ u32 sclk;
+ u8 mclk_src_sel;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 mclk_int;
+ u8 pll_cal_ratio;
+ u8 n;
+};
+
+/*
+ * Common PLL Settings for given SCLK
+ * Table 4-5 from the Datasheet
+ */
+static const struct cs42l42_pll_params pll_ratio_table[] = {
+ { 1411200, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2},
+ { 1536000, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2},
+ { 2304000, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2},
+ { 2400000, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 2822400, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 3000000, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1},
+ { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1},
+ { 4800000, 1, 0x01, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 4800000, 1, 0x01, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2},
+ { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 6144000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1},
+ { 9600000, 1, 0x02, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 9600000, 1, 0x02, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2},
+ { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1},
+ { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1},
+ { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1},
+ { 19200000, 1, 0x03, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 19200000, 1, 0x03, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2},
+ { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}
+};
+
+int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk,
+ unsigned int sample_rate)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l42->stream_use) {
+ if (pll_ratio_table[cs42l42->pll_config].sclk == clk)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ /* MCLKint must be a multiple of the sample rate */
+ if (pll_ratio_table[i].mclk_int % sample_rate)
+ continue;
+
+ if (pll_ratio_table[i].sclk == clk) {
+ cs42l42->pll_config = i;
+
+ /* Configure the internal sample rate */
+ snd_soc_component_update_bits(component, CS42L42_MCLK_CTL,
+ CS42L42_INTERNAL_FS_MASK,
+ ((pll_ratio_table[i].mclk_int !=
+ 12000000) &&
+ (pll_ratio_table[i].mclk_int !=
+ 24000000)) <<
+ CS42L42_INTERNAL_FS_SHIFT);
+ if (pll_ratio_table[i].mclk_src_sel == 0) {
+ /* Pass the clock straight through */
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 0);
+ } else {
+ /* Configure PLL per table 4-5 */
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_CFG1,
+ CS42L42_SCLK_PREDIV_MASK,
+ pll_ratio_table[i].sclk_prediv
+ << CS42L42_SCLK_PREDIV_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_INT,
+ CS42L42_PLL_DIV_INT_MASK,
+ pll_ratio_table[i].pll_div_int
+ << CS42L42_PLL_DIV_INT_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_FRAC0,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC0_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_FRAC1,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC1_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_DIV_FRAC2,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC2_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CTL4,
+ CS42L42_PLL_MODE_MASK,
+ pll_ratio_table[i].pll_mode
+ << CS42L42_PLL_MODE_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CTL3,
+ CS42L42_PLL_DIVOUT_MASK,
+ (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n)
+ << CS42L42_PLL_DIVOUT_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_PLL_CAL_RATIO,
+ CS42L42_PLL_CAL_RATIO_MASK,
+ pll_ratio_table[i].pll_cal_ratio
+ << CS42L42_PLL_CAL_RATIO_SHIFT);
+ }
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_pll_config, "SND_SOC_CS42L42_CORE");
+
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int fs;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l42->stream_use)
+ return;
+
+ /* SRC MCLK must be as close as possible to 125 * sample rate */
+ if (sample_rate <= 48000)
+ fs = CS42L42_CLK_IASRC_SEL_6;
+ else
+ fs = CS42L42_CLK_IASRC_SEL_12;
+
+ /* Set the sample rates (96k or lower) */
+ snd_soc_component_update_bits(component,
+ CS42L42_FS_RATE_EN,
+ CS42L42_FS_EN_MASK,
+ (CS42L42_FS_EN_IASRC_96K |
+ CS42L42_FS_EN_OASRC_96K) <<
+ CS42L42_FS_EN_SHIFT);
+
+ snd_soc_component_update_bits(component,
+ CS42L42_IN_ASRC_CLK,
+ CS42L42_CLK_IASRC_SEL_MASK,
+ fs << CS42L42_CLK_IASRC_SEL_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_OUT_ASRC_CLK,
+ CS42L42_CLK_OASRC_SEL_MASK,
+ fs << CS42L42_CLK_OASRC_SEL_SHIFT);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_src_config, "SND_SOC_CS42L42_CORE");
+
+static int cs42l42_asp_config(struct snd_soc_component *component,
+ unsigned int sclk, unsigned int sample_rate)
+{
+ u32 fsync = sclk / sample_rate;
+
+ /* Set up the LRCLK */
+ if (((fsync * sample_rate) != sclk) || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported sclk %d/sample rate %d\n",
+ sclk,
+ sample_rate);
+ return -EINVAL;
+ }
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_LOWER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_UPPER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ /* Set the LRCLK to 50% duty cycle */
+ fsync = fsync / 2;
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_LOWER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_UPPER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+
+ return 0;
+}
+
+static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u32 asp_cfg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ asp_cfg_val |= CS42L42_ASP_MASTER_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ asp_cfg_val |= CS42L42_ASP_SLAVE_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /*
+ * 5050 mode, frame starts on falling edge of LRCLK,
+ * frame delayed by 1.0 SCLKs
+ */
+ snd_soc_component_update_bits(component,
+ CS42L42_ASP_FRM_CFG,
+ CS42L42_ASP_STP_MASK |
+ CS42L42_ASP_5050_MASK |
+ CS42L42_ASP_FSD_MASK,
+ CS42L42_ASP_5050_MASK |
+ (CS42L42_ASP_FSD_1_0 <<
+ CS42L42_ASP_FSD_SHIFT));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Bitclock/frame inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT;
+ asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, CS42L42_ASP_CLK_CFG, CS42L42_ASP_MODE_MASK |
+ CS42L42_ASP_SCPOL_MASK |
+ CS42L42_ASP_LCPOL_MASK,
+ asp_cfg_val);
+
+ return 0;
+}
+
+static int cs42l42_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * Sample rates < 44.1 kHz would produce an out-of-range SCLK with
+ * a standard I2S frame. If the machine driver sets SCLK it must be
+ * legal.
+ */
+ if (cs42l42->sclk)
+ return 0;
+
+ /* Machine driver has not set a SCLK, limit bottom end to 44.1 kHz */
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 44100, 96000);
+}
+
+static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int channels = params_channels(params);
+ unsigned int width = (params_width(params) / 8) - 1;
+ unsigned int sample_rate = params_rate(params);
+ unsigned int slot_width = 0;
+ unsigned int val = 0;
+ unsigned int bclk;
+ int ret;
+
+ if (cs42l42->bclk_ratio) {
+ /* machine driver has set the BCLK/samp-rate ratio */
+ bclk = cs42l42->bclk_ratio * params_rate(params);
+ } else if (cs42l42->sclk) {
+ /* machine driver has set the SCLK */
+ bclk = cs42l42->sclk;
+ } else {
+ /*
+ * Assume 24-bit samples are in 32-bit slots, to prevent SCLK being
+ * more than assumed (which would result in overclocking).
+ */
+ if (params_width(params) == 24)
+ slot_width = 32;
+
+ /* I2S frame always has multiple of 2 channels */
+ bclk = snd_soc_tdm_params_to_bclk(params, slot_width, 0, 2);
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ /* channel 2 on high LRCLK */
+ val = CS42L42_ASP_TX_CH2_AP_MASK |
+ (width << CS42L42_ASP_TX_CH2_RES_SHIFT) |
+ (width << CS42L42_ASP_TX_CH1_RES_SHIFT);
+
+ snd_soc_component_update_bits(component, CS42L42_ASP_TX_CH_AP_RES,
+ CS42L42_ASP_TX_CH1_AP_MASK | CS42L42_ASP_TX_CH2_AP_MASK |
+ CS42L42_ASP_TX_CH2_RES_MASK | CS42L42_ASP_TX_CH1_RES_MASK, val);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ val |= width << CS42L42_ASP_RX_CH_RES_SHIFT;
+ /* channel 1 on low LRCLK */
+ snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH1_AP_RES,
+ CS42L42_ASP_RX_CH_AP_MASK |
+ CS42L42_ASP_RX_CH_RES_MASK, val);
+ /* Channel 2 on high LRCLK */
+ val |= CS42L42_ASP_RX_CH_AP_HI << CS42L42_ASP_RX_CH_AP_SHIFT;
+ snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH2_AP_RES,
+ CS42L42_ASP_RX_CH_AP_MASK |
+ CS42L42_ASP_RX_CH_RES_MASK, val);
+
+ /* Channel B comes from the last active channel */
+ snd_soc_component_update_bits(component, CS42L42_SP_RX_CH_SEL,
+ CS42L42_SP_RX_CHB_SEL_MASK,
+ (channels - 1) << CS42L42_SP_RX_CHB_SEL_SHIFT);
+
+ /* Both LRCLK slots must be enabled */
+ snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN,
+ CS42L42_ASP_RX0_CH_EN_MASK,
+ BIT(CS42L42_ASP_RX0_CH1_SHIFT) |
+ BIT(CS42L42_ASP_RX0_CH2_SHIFT));
+ break;
+ default:
+ break;
+ }
+
+ ret = cs42l42_pll_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
+
+ ret = cs42l42_asp_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
+
+ cs42l42_src_config(component, sample_rate);
+
+ return 0;
+}
+
+static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (freq == 0) {
+ cs42l42->sclk = 0;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].sclk == freq) {
+ cs42l42->sclk = freq;
+ return 0;
+ }
+ }
+
+ dev_err(component->dev, "SCLK %u not supported\n", freq);
+
+ return -EINVAL;
+}
+
+static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int bclk_ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ cs42l42->bclk_ratio = bclk_ratio;
+
+ return 0;
+}
+
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+ unsigned int regval;
+ int ret;
+
+ if (mute) {
+ /* Mute the headphone */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_component_update_bits(component, CS42L42_HP_CTL,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK);
+
+ cs42l42->stream_use &= ~(1 << stream);
+ if (!cs42l42->stream_use) {
+ /*
+ * Switch to the internal oscillator.
+ * SCLK must remain running until after this clock switch.
+ * Without a source of clock the I2C bus doesn't work.
+ */
+ regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_osc_seq,
+ ARRAY_SIZE(cs42l42_to_osc_seq));
+
+ /* Must disconnect PLL before stopping it */
+ snd_soc_component_update_bits(component,
+ CS42L42_MCLK_SRC_SEL,
+ CS42L42_MCLK_SRC_SEL_MASK,
+ 0);
+ usleep_range(100, 200);
+
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 0);
+ }
+ } else {
+ if (!cs42l42->stream_use) {
+ /* SCLK must be running before codec unmute.
+ *
+ * PLL must not be started with ADC and HP both off
+ * otherwise the FILT+ supply will not charge properly.
+ * DAPM widgets power-up before stream unmute so at least
+ * one of the "DAC" or "ADC" widgets will already have
+ * powered-up.
+ */
+ if (pll_ratio_table[cs42l42->pll_config].mclk_src_sel) {
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 1);
+
+ if (pll_ratio_table[cs42l42->pll_config].n > 1) {
+ usleep_range(CS42L42_PLL_DIVOUT_TIME_US,
+ CS42L42_PLL_DIVOUT_TIME_US * 2);
+ regval = pll_ratio_table[cs42l42->pll_config].pll_divout;
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL3,
+ CS42L42_PLL_DIVOUT_MASK,
+ regval <<
+ CS42L42_PLL_DIVOUT_SHIFT);
+ }
+
+ ret = regmap_read_poll_timeout(cs42l42->regmap,
+ CS42L42_PLL_LOCK_STATUS,
+ regval,
+ (regval & 1),
+ CS42L42_PLL_LOCK_POLL_US,
+ CS42L42_PLL_LOCK_TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(component->dev, "PLL failed to lock: %d\n", ret);
+
+ /* PLL must be running to drive glitchless switch logic */
+ snd_soc_component_update_bits(component,
+ CS42L42_MCLK_SRC_SEL,
+ CS42L42_MCLK_SRC_SEL_MASK,
+ CS42L42_MCLK_SRC_SEL_MASK);
+ }
+
+ /* Mark SCLK as present, turn off internal oscillator */
+ regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq,
+ ARRAY_SIZE(cs42l42_to_sclk_seq));
+ }
+ cs42l42->stream_use |= 1 << stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Un-mute the headphone */
+ snd_soc_component_update_bits(component, CS42L42_HP_CTL,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK,
+ 0);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, "SND_SOC_CS42L42_CORE");
+
+#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops cs42l42_ops = {
+ .startup = cs42l42_dai_startup,
+ .hw_params = cs42l42_pcm_hw_params,
+ .set_fmt = cs42l42_set_dai_fmt,
+ .set_sysclk = cs42l42_set_sysclk,
+ .set_bclk_ratio = cs42l42_set_bclk_ratio,
+ .mute_stream = cs42l42_mute_stream,
+};
+
+struct snd_soc_dai_driver cs42l42_dai = {
+ .name = "cs42l42",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = CS42L42_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = CS42L42_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &cs42l42_ops,
+};
+EXPORT_SYMBOL_NS_GPL(cs42l42_dai, "SND_SOC_CS42L42_CORE");
+
+static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+
+ /* Set hs detect to manual, active mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL1,
+ CS42L42_HSDET_COMP1_LVL_MASK |
+ CS42L42_HSDET_COMP2_LVL_MASK,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ cs42l42->hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ cs42l42->hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ cs42l42->hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ cs42l42->hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ /* Detect Type 3 and Type 4 Headsets as Headphones */
+ default:
+ cs42l42->hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL1,
+ CS42L42_HSDET_COMP1_LVL_MASK |
+ CS42L42_HSDET_COMP2_LVL_MASK,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+}
+
+static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int int_status;
+
+ /* Read and save the hs detection result */
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ /* Mask the auto detect interrupt */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+
+ cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
+ CS42L42_HSDET_TYPE_SHIFT;
+
+ /* Set hs detect to automatic, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (2 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Run Manual detection if auto detect has not found a headset.
+ * We Re-Run with Manual Detection if the original detection was invalid or headphones,
+ * to ensure that a headset mic is detected in all cases.
+ */
+ if (cs42l42->hs_type == CS42L42_PLUG_INVALID ||
+ cs42l42->hs_type == CS42L42_PLUG_HEADPHONE) {
+ dev_dbg(cs42l42->dev, "Running Manual Detection Fallback\n");
+ cs42l42_manual_hs_type_detect(cs42l42);
+ }
+
+ /* Set up button detection */
+ if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) ||
+ (cs42l42->hs_type == CS42L42_PLUG_OMTP)) {
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set up hs detect level sensitivity */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (cs42l42->hs_bias_sense_en << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (1 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Turn on level detect circuitry */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (3 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ msleep(cs42l42->btn_det_init_dbnce);
+
+ /* Clear any button interrupts before unmasking them */
+ regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2,
+ &int_status);
+
+ /* Unmask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (0 << CS42L42_M_DETECT_TF_SHIFT) |
+ (0 << CS42L42_M_DETECT_FT_SHIFT) |
+ (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+ } else {
+ /* Make sure button detect and HS bias circuits are off */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+ }
+
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DAC_CTL2,
+ CS42L42_HPOUT_PULLDOWN_MASK |
+ CS42L42_HPOUT_LOAD_MASK |
+ CS42L42_HPOUT_CLAMP_MASK |
+ CS42L42_DAC_HPF_EN_MASK |
+ CS42L42_DAC_MON_EN_MASK,
+ (0 << CS42L42_HPOUT_PULLDOWN_SHIFT) |
+ (0 << CS42L42_HPOUT_LOAD_SHIFT) |
+ (0 << CS42L42_HPOUT_CLAMP_SHIFT) |
+ (1 << CS42L42_DAC_HPF_EN_SHIFT) |
+ (0 << CS42L42_DAC_MON_EN_SHIFT));
+
+ /* Unmask tip sense interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
+}
+
+static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ /* Mask tip sense interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_TS_PLUG_SHIFT) |
+ (1 << CS42L42_TS_UNPLUG_SHIFT));
+
+ /* Make sure button detect and HS bias circuits are off */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set hs detect to manual, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DAC_CTL2,
+ CS42L42_HPOUT_PULLDOWN_MASK |
+ CS42L42_HPOUT_LOAD_MASK |
+ CS42L42_HPOUT_CLAMP_MASK |
+ CS42L42_DAC_HPF_EN_MASK |
+ CS42L42_DAC_MON_EN_MASK,
+ (8 << CS42L42_HPOUT_PULLDOWN_SHIFT) |
+ (0 << CS42L42_HPOUT_LOAD_SHIFT) |
+ (1 << CS42L42_HPOUT_CLAMP_SHIFT) |
+ (1 << CS42L42_DAC_HPF_EN_SHIFT) |
+ (1 << CS42L42_DAC_MON_EN_SHIFT));
+
+ /* Power up HS bias to 2.7V */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (3 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Wait for HS bias to ramp up */
+ msleep(cs42l42->hs_bias_ramp_time);
+
+ /* Unmask auto detect interrupt */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ /* Set hs detect to automatic, enabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (3 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+}
+
+static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ /* Mask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ /* Ground HS bias */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set hs detect to manual, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+}
+
+static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
+{
+ int bias_level;
+ unsigned int detect_status;
+
+ /* Mask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ usleep_range(cs42l42->btn_det_event_dbnce * 1000,
+ cs42l42->btn_det_event_dbnce * 2000);
+
+ /* Test all 4 level detect biases */
+ bias_level = 1;
+ do {
+ /* Adjust button detect level sensitivity */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[bias_level] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ regmap_read(cs42l42->regmap, CS42L42_DET_STATUS2,
+ &detect_status);
+ } while ((detect_status & CS42L42_HS_TRUE_MASK) &&
+ (++bias_level < CS42L42_NUM_BIASES));
+
+ switch (bias_level) {
+ case 1: /* Function C button press */
+ bias_level = SND_JACK_BTN_2;
+ dev_dbg(cs42l42->dev, "Function C button press\n");
+ break;
+ case 2: /* Function B button press */
+ bias_level = SND_JACK_BTN_1;
+ dev_dbg(cs42l42->dev, "Function B button press\n");
+ break;
+ case 3: /* Function D button press */
+ bias_level = SND_JACK_BTN_3;
+ dev_dbg(cs42l42->dev, "Function D button press\n");
+ break;
+ case 4: /* Function A button press */
+ bias_level = SND_JACK_BTN_0;
+ dev_dbg(cs42l42->dev, "Function A button press\n");
+ break;
+ default:
+ bias_level = 0;
+ break;
+ }
+
+ /* Set button detect level sensitivity back to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] << CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Clear any button interrupts before unmasking them */
+ regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2,
+ &detect_status);
+
+ /* Unmask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (0 << CS42L42_M_DETECT_TF_SHIFT) |
+ (0 << CS42L42_M_DETECT_FT_SHIFT) |
+ (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ return bias_level;
+}
+
+struct cs42l42_irq_params {
+ u16 status_addr;
+ u16 mask_addr;
+ u8 mask;
+};
+
+static const struct cs42l42_irq_params irq_params_table[] = {
+ {CS42L42_ADC_OVFL_STATUS, CS42L42_ADC_OVFL_INT_MASK,
+ CS42L42_ADC_OVFL_VAL_MASK},
+ {CS42L42_MIXER_STATUS, CS42L42_MIXER_INT_MASK,
+ CS42L42_MIXER_VAL_MASK},
+ {CS42L42_SRC_STATUS, CS42L42_SRC_INT_MASK,
+ CS42L42_SRC_VAL_MASK},
+ {CS42L42_ASP_RX_STATUS, CS42L42_ASP_RX_INT_MASK,
+ CS42L42_ASP_RX_VAL_MASK},
+ {CS42L42_ASP_TX_STATUS, CS42L42_ASP_TX_INT_MASK,
+ CS42L42_ASP_TX_VAL_MASK},
+ {CS42L42_CODEC_STATUS, CS42L42_CODEC_INT_MASK,
+ CS42L42_CODEC_VAL_MASK},
+ {CS42L42_DET_INT_STATUS1, CS42L42_DET_INT1_MASK,
+ CS42L42_DET_INT_VAL1_MASK},
+ {CS42L42_DET_INT_STATUS2, CS42L42_DET_INT2_MASK,
+ CS42L42_DET_INT_VAL2_MASK},
+ {CS42L42_SRCPL_INT_STATUS, CS42L42_SRCPL_INT_MASK,
+ CS42L42_SRCPL_VAL_MASK},
+ {CS42L42_VPMON_STATUS, CS42L42_VPMON_INT_MASK,
+ CS42L42_VPMON_VAL_MASK},
+ {CS42L42_PLL_LOCK_STATUS, CS42L42_PLL_LOCK_INT_MASK,
+ CS42L42_PLL_LOCK_VAL_MASK},
+ {CS42L42_TSRS_PLUG_STATUS, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TSRS_PLUG_VAL_MASK}
+};
+
+irqreturn_t cs42l42_irq_thread(int irq, void *data)
+{
+ struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
+ unsigned int stickies[12];
+ unsigned int masks[12];
+ unsigned int current_plug_status;
+ unsigned int current_button_status;
+ unsigned int i;
+
+ pm_runtime_get_sync(cs42l42->dev);
+ mutex_lock(&cs42l42->irq_lock);
+ if (cs42l42->suspended || !cs42l42->init_done) {
+ mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_put_autosuspend(cs42l42->dev);
+ return IRQ_NONE;
+ }
+
+ /* Read sticky registers to clear interurpt */
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
+ &(stickies[i]));
+ regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr,
+ &(masks[i]));
+ stickies[i] = stickies[i] & (~masks[i]) &
+ irq_params_table[i].mask;
+ }
+
+ /* Read tip sense status before handling type detect */
+ current_plug_status = (stickies[11] &
+ (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >>
+ CS42L42_TS_PLUG_SHIFT;
+
+ /* Read button sense status */
+ current_button_status = stickies[7] &
+ (CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK);
+
+ /*
+ * Check auto-detect status. Don't assume a previous unplug event has
+ * cleared the flags. If the jack is unplugged and plugged during
+ * system suspend there won't have been an unplug event.
+ */
+ if ((~masks[5]) & irq_params_table[5].mask) {
+ if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
+ cs42l42_process_hs_type_detect(cs42l42);
+ switch (cs42l42->hs_type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ break;
+ default:
+ break;
+ }
+ dev_dbg(cs42l42->dev, "Auto detect done (%d)\n", cs42l42->hs_type);
+ }
+ }
+
+ /* Check tip sense status */
+ if ((~masks[11]) & irq_params_table[11].mask) {
+ switch (current_plug_status) {
+ case CS42L42_TS_PLUG:
+ if (cs42l42->plug_state != CS42L42_TS_PLUG) {
+ cs42l42->plug_state = CS42L42_TS_PLUG;
+ cs42l42_init_hs_type_detect(cs42l42);
+ }
+ break;
+
+ case CS42L42_TS_UNPLUG:
+ if (cs42l42->plug_state != CS42L42_TS_UNPLUG) {
+ cs42l42->plug_state = CS42L42_TS_UNPLUG;
+ cs42l42_cancel_hs_type_detect(cs42l42);
+
+ snd_soc_jack_report(cs42l42->jack, 0,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ dev_dbg(cs42l42->dev, "Unplug event\n");
+ }
+ break;
+
+ default:
+ cs42l42->plug_state = CS42L42_TS_TRANS;
+ }
+ }
+
+ /* Check button detect status */
+ if (cs42l42->plug_state == CS42L42_TS_PLUG && ((~masks[7]) & irq_params_table[7].mask)) {
+ if (!(current_button_status &
+ CS42L42_M_HSBIAS_HIZ_MASK)) {
+
+ if (current_button_status & CS42L42_M_DETECT_TF_MASK) {
+ dev_dbg(cs42l42->dev, "Button released\n");
+ snd_soc_jack_report(cs42l42->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) {
+ snd_soc_jack_report(cs42l42->jack,
+ cs42l42_handle_button_press(cs42l42),
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ }
+ }
+ }
+
+ mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_put_autosuspend(cs42l42->dev);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE");
+
+static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
+{
+ regmap_update_bits(cs42l42->regmap, CS42L42_ADC_OVFL_INT_MASK,
+ CS42L42_ADC_OVFL_MASK,
+ (1 << CS42L42_ADC_OVFL_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_MIXER_INT_MASK,
+ CS42L42_MIX_CHB_OVFL_MASK |
+ CS42L42_MIX_CHA_OVFL_MASK |
+ CS42L42_EQ_OVFL_MASK |
+ CS42L42_EQ_BIQUAD_OVFL_MASK,
+ (1 << CS42L42_MIX_CHB_OVFL_SHIFT) |
+ (1 << CS42L42_MIX_CHA_OVFL_SHIFT) |
+ (1 << CS42L42_EQ_OVFL_SHIFT) |
+ (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_SRC_INT_MASK,
+ CS42L42_SRC_ILK_MASK |
+ CS42L42_SRC_OLK_MASK |
+ CS42L42_SRC_IUNLK_MASK |
+ CS42L42_SRC_OUNLK_MASK,
+ (1 << CS42L42_SRC_ILK_SHIFT) |
+ (1 << CS42L42_SRC_OLK_SHIFT) |
+ (1 << CS42L42_SRC_IUNLK_SHIFT) |
+ (1 << CS42L42_SRC_OUNLK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_ASP_RX_INT_MASK,
+ CS42L42_ASPRX_NOLRCK_MASK |
+ CS42L42_ASPRX_EARLY_MASK |
+ CS42L42_ASPRX_LATE_MASK |
+ CS42L42_ASPRX_ERROR_MASK |
+ CS42L42_ASPRX_OVLD_MASK,
+ (1 << CS42L42_ASPRX_NOLRCK_SHIFT) |
+ (1 << CS42L42_ASPRX_EARLY_SHIFT) |
+ (1 << CS42L42_ASPRX_LATE_SHIFT) |
+ (1 << CS42L42_ASPRX_ERROR_SHIFT) |
+ (1 << CS42L42_ASPRX_OVLD_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_ASP_TX_INT_MASK,
+ CS42L42_ASPTX_NOLRCK_MASK |
+ CS42L42_ASPTX_EARLY_MASK |
+ CS42L42_ASPTX_LATE_MASK |
+ CS42L42_ASPTX_SMERROR_MASK,
+ (1 << CS42L42_ASPTX_NOLRCK_SHIFT) |
+ (1 << CS42L42_ASPTX_EARLY_SHIFT) |
+ (1 << CS42L42_ASPTX_LATE_SHIFT) |
+ (1 << CS42L42_ASPTX_SMERROR_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_SRCPL_INT_MASK,
+ CS42L42_SRCPL_ADC_LK_MASK |
+ CS42L42_SRCPL_DAC_LK_MASK |
+ CS42L42_SRCPL_ADC_UNLK_MASK |
+ CS42L42_SRCPL_DAC_UNLK_MASK,
+ (1 << CS42L42_SRCPL_ADC_LK_SHIFT) |
+ (1 << CS42L42_SRCPL_DAC_LK_SHIFT) |
+ (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT) |
+ (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT1_MASK,
+ CS42L42_TIP_SENSE_UNPLUG_MASK |
+ CS42L42_TIP_SENSE_PLUG_MASK |
+ CS42L42_HSBIAS_SENSE_MASK,
+ (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT) |
+ (1 << CS42L42_TIP_SENSE_PLUG_SHIFT) |
+ (1 << CS42L42_HSBIAS_SENSE_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_VPMON_INT_MASK,
+ CS42L42_VPMON_MASK,
+ (1 << CS42L42_VPMON_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_PLL_LOCK_INT_MASK,
+ CS42L42_PLL_LOCK_MASK,
+ (1 << CS42L42_PLL_LOCK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_RS_PLUG_MASK |
+ CS42L42_RS_UNPLUG_MASK |
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_RS_PLUG_SHIFT) |
+ (1 << CS42L42_RS_UNPLUG_SHIFT) |
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
+}
+
+static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+
+ cs42l42->hs_type = CS42L42_PLUG_INVALID;
+
+ /*
+ * DETECT_MODE must always be 0 with ADC and HP both off otherwise the
+ * FILT+ supply will not charge properly.
+ */
+ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK, 0);
+
+ /* Latch analog controls to VP power domain */
+ regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Remove ground noise-suppression clamps */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HS_CLAMP_DISABLE,
+ CS42L42_HS_CLAMP_DISABLE_MASK,
+ (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT));
+
+ /* Enable the tip sense circuit */
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_INV_MASK, CS42L42_TS_INV_MASK);
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TIPSENSE_CTL,
+ CS42L42_TIP_SENSE_CTRL_MASK |
+ CS42L42_TIP_SENSE_INV_MASK |
+ CS42L42_TIP_SENSE_DEBOUNCE_MASK,
+ (3 << CS42L42_TIP_SENSE_CTRL_SHIFT) |
+ (!cs42l42->ts_inv << CS42L42_TIP_SENSE_INV_SHIFT) |
+ (2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT));
+
+ /* Save the initial status of the tip sense */
+ regmap_read(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_STATUS,
+ &reg);
+ cs42l42->plug_state = (((char) reg) &
+ (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >>
+ CS42L42_TS_PLUG_SHIFT;
+}
+
+static const unsigned int threshold_defaults[] = {
+ CS42L42_HS_DET_LEVEL_15,
+ CS42L42_HS_DET_LEVEL_8,
+ CS42L42_HS_DET_LEVEL_4,
+ CS42L42_HS_DET_LEVEL_1
+};
+
+static int cs42l42_handle_device_data(struct device *dev,
+ struct cs42l42_private *cs42l42)
+{
+ unsigned int val;
+ u32 thresholds[CS42L42_NUM_BIASES];
+ int ret;
+ int i;
+
+ ret = device_property_read_u32(dev, "cirrus,ts-inv", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_INV_EN:
+ case CS42L42_TS_INV_DIS:
+ cs42l42->ts_inv = val;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,ts-inv DT value %d\n",
+ val);
+ cs42l42->ts_inv = CS42L42_TS_INV_DIS;
+ }
+ } else {
+ cs42l42->ts_inv = CS42L42_TS_INV_DIS;
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-rise", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_DBNCE_0:
+ case CS42L42_TS_DBNCE_125:
+ case CS42L42_TS_DBNCE_250:
+ case CS42L42_TS_DBNCE_500:
+ case CS42L42_TS_DBNCE_750:
+ case CS42L42_TS_DBNCE_1000:
+ case CS42L42_TS_DBNCE_1250:
+ case CS42L42_TS_DBNCE_1500:
+ cs42l42->ts_dbnc_rise = val;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,ts-dbnc-rise DT value %d\n",
+ val);
+ cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
+ }
+ } else {
+ cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_RISE_DBNCE_TIME_MASK,
+ (cs42l42->ts_dbnc_rise <<
+ CS42L42_TS_RISE_DBNCE_TIME_SHIFT));
+
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-fall", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_DBNCE_0:
+ case CS42L42_TS_DBNCE_125:
+ case CS42L42_TS_DBNCE_250:
+ case CS42L42_TS_DBNCE_500:
+ case CS42L42_TS_DBNCE_750:
+ case CS42L42_TS_DBNCE_1000:
+ case CS42L42_TS_DBNCE_1250:
+ case CS42L42_TS_DBNCE_1500:
+ cs42l42->ts_dbnc_fall = val;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,ts-dbnc-fall DT value %d\n",
+ val);
+ cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
+ }
+ } else {
+ cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_FALL_DBNCE_TIME_MASK,
+ (cs42l42->ts_dbnc_fall <<
+ CS42L42_TS_FALL_DBNCE_TIME_SHIFT));
+
+ ret = device_property_read_u32(dev, "cirrus,btn-det-init-dbnce", &val);
+ if (!ret) {
+ if (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX)
+ cs42l42->btn_det_init_dbnce = val;
+ else {
+ dev_err(dev,
+ "Wrong cirrus,btn-det-init-dbnce DT value %d\n",
+ val);
+ cs42l42->btn_det_init_dbnce =
+ CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
+ }
+ } else {
+ cs42l42->btn_det_init_dbnce =
+ CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,btn-det-event-dbnce", &val);
+ if (!ret) {
+ if (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX)
+ cs42l42->btn_det_event_dbnce = val;
+ else {
+ dev_err(dev,
+ "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val);
+ cs42l42->btn_det_event_dbnce =
+ CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
+ }
+ } else {
+ cs42l42->btn_det_event_dbnce =
+ CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
+ }
+
+ ret = device_property_read_u32_array(dev, "cirrus,bias-lvls",
+ thresholds, ARRAY_SIZE(thresholds));
+ if (!ret) {
+ for (i = 0; i < CS42L42_NUM_BIASES; i++) {
+ if (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX)
+ cs42l42->bias_thresholds[i] = thresholds[i];
+ else {
+ dev_err(dev,
+ "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
+ thresholds[i]);
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
+ }
+ }
+ } else {
+ for (i = 0; i < CS42L42_NUM_BIASES; i++)
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
+ }
+
+ ret = device_property_read_u32(dev, "cirrus,hs-bias-ramp-rate", &val);
+ if (!ret) {
+ switch (val) {
+ case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME0;
+ break;
+ case CS42L42_HSBIAS_RAMP_FAST:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME1;
+ break;
+ case CS42L42_HSBIAS_RAMP_SLOW:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ break;
+ case CS42L42_HSBIAS_RAMP_SLOWEST:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3;
+ break;
+ default:
+ dev_err(dev,
+ "Wrong cirrus,hs-bias-ramp-rate DT value %d\n",
+ val);
+ cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ }
+ } else {
+ cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_HS_BIAS_CTL,
+ CS42L42_HSBIAS_RAMP_MASK,
+ (cs42l42->hs_bias_ramp_rate <<
+ CS42L42_HSBIAS_RAMP_SHIFT));
+
+ if (device_property_read_bool(dev, "cirrus,hs-bias-sense-disable"))
+ cs42l42->hs_bias_sense_en = 0;
+ else
+ cs42l42->hs_bias_sense_en = 1;
+
+ return 0;
+}
+
+/* Datasheet suspend sequence */
+static const struct reg_sequence __maybe_unused cs42l42_shutdown_seq[] = {
+ REG_SEQ0(CS42L42_MIC_DET_CTL1, 0x9F),
+ REG_SEQ0(CS42L42_ADC_OVFL_INT_MASK, 0x01),
+ REG_SEQ0(CS42L42_MIXER_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_SRC_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_ASP_RX_INT_MASK, 0x1F),
+ REG_SEQ0(CS42L42_ASP_TX_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_CODEC_INT_MASK, 0x03),
+ REG_SEQ0(CS42L42_SRCPL_INT_MASK, 0x7F),
+ REG_SEQ0(CS42L42_VPMON_INT_MASK, 0x01),
+ REG_SEQ0(CS42L42_PLL_LOCK_INT_MASK, 0x01),
+ REG_SEQ0(CS42L42_TSRS_PLUG_INT_MASK, 0x0F),
+ REG_SEQ0(CS42L42_WAKE_CTL, 0xE1),
+ REG_SEQ0(CS42L42_DET_INT1_MASK, 0xE0),
+ REG_SEQ0(CS42L42_DET_INT2_MASK, 0xFF),
+ REG_SEQ0(CS42L42_MIXER_CHA_VOL, 0x3F),
+ REG_SEQ0(CS42L42_MIXER_ADC_VOL, 0x3F),
+ REG_SEQ0(CS42L42_MIXER_CHB_VOL, 0x3F),
+ REG_SEQ0(CS42L42_HP_CTL, 0x0F),
+ REG_SEQ0(CS42L42_ASP_RX_DAI0_EN, 0x00),
+ REG_SEQ0(CS42L42_ASP_CLK_CFG, 0x00),
+ REG_SEQ0(CS42L42_HSDET_CTL2, 0x00),
+ REG_SEQ0(CS42L42_PWR_CTL1, 0xFE),
+ REG_SEQ0(CS42L42_PWR_CTL2, 0x8C),
+ REG_SEQ0(CS42L42_DAC_CTL2, 0x02),
+ REG_SEQ0(CS42L42_HS_CLAMP_DISABLE, 0x00),
+ REG_SEQ0(CS42L42_MISC_DET_CTL, 0x03),
+ REG_SEQ0(CS42L42_TIPSENSE_CTL, 0x02),
+ REG_SEQ0(CS42L42_HSBIAS_SC_AUTOCTL, 0x03),
+ REG_SEQ0(CS42L42_PWR_CTL1, 0xFF)
+};
+
+int cs42l42_suspend(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ unsigned int reg;
+ u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)];
+ int i, ret;
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /*
+ * Wait for threaded irq handler to be idle and stop it processing
+ * future interrupts. This ensures a safe disable if the interrupt
+ * is shared.
+ */
+ mutex_lock(&cs42l42->irq_lock);
+ cs42l42->suspended = true;
+
+ /* Save register values that will be overwritten by shutdown sequence */
+ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) {
+ regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, &reg);
+ save_regs[i] = (u8)reg;
+ }
+
+ /* Shutdown codec */
+ regmap_multi_reg_write(cs42l42->regmap,
+ cs42l42_shutdown_seq,
+ ARRAY_SIZE(cs42l42_shutdown_seq));
+
+ /* All interrupt sources are now disabled */
+ mutex_unlock(&cs42l42->irq_lock);
+
+ /* Wait for power-down complete */
+ msleep(CS42L42_PDN_DONE_TIME_MS);
+ ret = regmap_read_poll_timeout(cs42l42->regmap,
+ CS42L42_CODEC_STATUS, reg,
+ (reg & CS42L42_PDN_DONE_MASK),
+ CS42L42_PDN_DONE_POLL_US,
+ CS42L42_PDN_DONE_TIMEOUT_US);
+ if (ret)
+ dev_warn(dev, "Failed to get PDN_DONE: %d\n", ret);
+
+ /* Discharge FILT+ */
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
+ CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK);
+
+ regcache_cache_only(cs42l42->regmap, true);
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+
+ /* Restore register values to the regmap cache */
+ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i)
+ regmap_write(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, save_regs[i]);
+
+ /* The cached address page register value is now stale */
+ regcache_drop_region(cs42l42->regmap, CS42L42_PAGE_REGISTER, CS42L42_PAGE_REGISTER);
+
+ dev_dbg(dev, "System suspended\n");
+
+ return 0;
+
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_suspend, "SND_SOC_CS42L42_CORE");
+
+int cs42l42_resume(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /*
+ * If jack was unplugged and re-plugged during suspend it could
+ * have changed type but the tip-sense state hasn't changed.
+ * Force a plugged state to be re-evaluated.
+ */
+ if (cs42l42->plug_state != CS42L42_TS_UNPLUG)
+ cs42l42->plug_state = CS42L42_TS_TRANS;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+
+ dev_dbg(dev, "System resume powered up\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_resume, "SND_SOC_CS42L42_CORE");
+
+void cs42l42_resume_restore(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l42->regmap, false);
+ regcache_mark_dirty(cs42l42->regmap);
+
+ mutex_lock(&cs42l42->irq_lock);
+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */
+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1);
+ regcache_sync(cs42l42->regmap);
+
+ cs42l42->suspended = false;
+ mutex_unlock(&cs42l42->irq_lock);
+
+ dev_dbg(dev, "System resumed\n");
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_resume_restore, "SND_SOC_CS42L42_CORE");
+
+static int __maybe_unused cs42l42_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+int cs42l42_common_probe(struct cs42l42_private *cs42l42,
+ const struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver *dai)
+{
+ int ret, i;
+
+ dev_set_drvdata(cs42l42->dev, cs42l42);
+ mutex_init(&cs42l42->irq_lock);
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs42l42_supply_names) != ARRAY_SIZE(cs42l42->supplies));
+ for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++)
+ cs42l42->supplies[i].supply = cs42l42_supply_names[i];
+
+ ret = devm_regulator_bulk_get(cs42l42->dev,
+ ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(cs42l42->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(cs42l42->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs42l42->reset_gpio = devm_gpiod_get_optional(cs42l42->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l42->reset_gpio)) {
+ ret = PTR_ERR(cs42l42->reset_gpio);
+ goto err_disable_noreset;
+ }
+
+ if (cs42l42->reset_gpio) {
+ dev_dbg(cs42l42->dev, "Found reset GPIO\n");
+
+ /*
+ * ACPI can override the default GPIO state we requested
+ * so ensure that we start with RESET low.
+ */
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+
+ /* Ensure minimum reset pulse width */
+ usleep_range(10, 500);
+
+ /*
+ * On SoundWire keep the chip in reset until we get an UNATTACH
+ * notification from the SoundWire core. This acts as a
+ * synchronization point to reject stale ATTACH notifications
+ * if the chip was already enumerated before we reset it.
+ */
+ if (cs42l42->sdw_peripheral)
+ cs42l42->sdw_waiting_first_unattach = true;
+ else
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ }
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+
+ /* Request IRQ if one was specified */
+ if (cs42l42->irq) {
+ ret = request_threaded_irq(cs42l42->irq,
+ NULL, cs42l42_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs42l42", cs42l42);
+ if (ret) {
+ dev_err_probe(cs42l42->dev, ret,
+ "Failed to request IRQ\n");
+ goto err_disable_noirq;
+ }
+ }
+
+ /* Register codec now so it can EPROBE_DEFER */
+ ret = devm_snd_soc_register_component(cs42l42->dev, component_drv, dai, 1);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+err_disable_noreset:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_probe, "SND_SOC_CS42L42_CORE");
+
+int cs42l42_init(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+ int devid, ret;
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(cs42l42->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
+
+ if (devid != cs42l42->devid) {
+ ret = -ENODEV;
+ dev_err(cs42l42->dev,
+ "CS42L%x Device ID (%X). Expected %X\n",
+ cs42l42->devid & 0xff, devid, cs42l42->devid);
+ goto err_disable;
+ }
+
+ ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
+ if (ret < 0) {
+ dev_err(cs42l42->dev, "Get Revision ID failed\n");
+ goto err_shutdown;
+ }
+
+ dev_info(cs42l42->dev,
+ "Cirrus Logic CS42L%x, Revision: %02X\n",
+ cs42l42->devid & 0xff, reg & 0xFF);
+
+ /* Power up the codec */
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL1,
+ CS42L42_ASP_DAO_PDN_MASK |
+ CS42L42_ASP_DAI_PDN_MASK |
+ CS42L42_MIXER_PDN_MASK |
+ CS42L42_EQ_PDN_MASK |
+ CS42L42_HP_PDN_MASK |
+ CS42L42_ADC_PDN_MASK |
+ CS42L42_PDN_ALL_MASK,
+ (1 << CS42L42_ASP_DAO_PDN_SHIFT) |
+ (1 << CS42L42_ASP_DAI_PDN_SHIFT) |
+ (1 << CS42L42_MIXER_PDN_SHIFT) |
+ (1 << CS42L42_EQ_PDN_SHIFT) |
+ (1 << CS42L42_HP_PDN_SHIFT) |
+ (1 << CS42L42_ADC_PDN_SHIFT) |
+ (0 << CS42L42_PDN_ALL_SHIFT));
+
+ ret = cs42l42_handle_device_data(cs42l42->dev, cs42l42);
+ if (ret != 0)
+ goto err_shutdown;
+
+ /*
+ * SRC power is linked to ASP power so doesn't work in Soundwire mode.
+ * Override it and use DAPM to control SRC power for Soundwire.
+ */
+ if (cs42l42->sdw_peripheral) {
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
+ CS42L42_SRC_PDN_OVERRIDE_MASK |
+ CS42L42_DAC_SRC_PDNB_MASK |
+ CS42L42_ADC_SRC_PDNB_MASK,
+ CS42L42_SRC_PDN_OVERRIDE_MASK);
+ }
+
+ /* Setup headset detection */
+ cs42l42_setup_hs_type_detect(cs42l42);
+
+ /*
+ * Set init_done before unmasking interrupts so any triggered
+ * immediately will be handled.
+ */
+ cs42l42->init_done = true;
+
+ /* Mask/Unmask Interrupts */
+ cs42l42_set_interrupt_masks(cs42l42);
+
+ return 0;
+
+err_shutdown:
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+
+err_disable:
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_init, "SND_SOC_CS42L42_CORE");
+
+void cs42l42_common_remove(struct cs42l42_private *cs42l42)
+{
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+ /*
+ * The driver might not have control of reset and power supplies,
+ * so ensure that the chip internals are powered down.
+ */
+ if (cs42l42->init_done) {
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+ }
+
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_remove, "SND_SOC_CS42L42_CORE");
+
+MODULE_DESCRIPTION("ASoC CS42L42 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_AUTHOR("Michael White, Cirrus Logic Inc, <michael.white@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Vitaly Rodionov <vitalyr@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
new file mode 100644
index 000000000000..3d85ebc59489
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cs42l42.h -- CS42L42 ALSA SoC audio driver header
+ *
+ * Copyright 2016-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ */
+
+#ifndef __CS42L42_H__
+#define __CS42L42_H__
+
+#include <dt-bindings/sound/cs42l42.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/jack.h>
+#include <sound/cs42l42.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+
+struct cs42l42_private {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+ struct snd_soc_jack *jack;
+ struct sdw_slave *sdw_peripheral;
+ struct mutex irq_lock;
+ int devid;
+ int irq;
+ int pll_config;
+ u32 sclk;
+ u32 sample_rate;
+ u32 bclk_ratio;
+ u8 plug_state;
+ u8 hs_type;
+ u8 ts_inv;
+ u8 ts_dbnc_rise;
+ u8 ts_dbnc_fall;
+ u8 btn_det_init_dbnce;
+ u8 btn_det_event_dbnce;
+ u8 bias_thresholds[CS42L42_NUM_BIASES];
+ u8 hs_bias_ramp_rate;
+ u8 hs_bias_ramp_time;
+ u8 hs_bias_sense_en;
+ u8 stream_use;
+ bool hp_adc_up_pending;
+ bool suspended;
+ bool sdw_waiting_first_unattach;
+ bool init_done;
+};
+
+extern const struct regmap_range_cfg cs42l42_page_range;
+extern const struct regmap_config cs42l42_regmap;
+extern const struct snd_soc_component_driver cs42l42_soc_component;
+extern struct snd_soc_dai_driver cs42l42_dai;
+
+bool cs42l42_readable_register(struct device *dev, unsigned int reg);
+bool cs42l42_volatile_register(struct device *dev, unsigned int reg);
+
+int cs42l42_pll_config(struct snd_soc_component *component,
+ unsigned int clk, unsigned int sample_rate);
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate);
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream);
+irqreturn_t cs42l42_irq_thread(int irq, void *data);
+int cs42l42_suspend(struct device *dev);
+int cs42l42_resume(struct device *dev);
+void cs42l42_resume_restore(struct device *dev);
+int cs42l42_common_probe(struct cs42l42_private *cs42l42,
+ const struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver *dai);
+int cs42l42_init(struct cs42l42_private *cs42l42);
+void cs42l42_common_remove(struct cs42l42_private *cs42l42);
+
+#endif /* __CS42L42_H__ */
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
new file mode 100644
index 000000000000..b719d62635a0
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver jack handling
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/build_bug.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-jack.h>
+#include <sound/soc.h>
+
+#include "cs42l43.h"
+
+static const unsigned int cs42l43_accdet_us[] = {
+ 20, 100, 1000, 10000, 50000, 75000, 100000, 200000,
+};
+
+static const unsigned int cs42l43_accdet_db_ms[] = {
+ 0, 125, 250, 500, 750, 1000, 1250, 1500,
+};
+
+static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 };
+
+static const unsigned int cs42l43_accdet_bias_sense[] = {
+ 14, 24, 43, 52, 61, 71, 90, 99, 0,
+};
+
+static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop,
+ unsigned int defval, unsigned int *val,
+ const unsigned int *values, const int nvalues)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ int i, ret;
+
+ ret = device_property_read_u32(cs42l43->dev, prop, &defval);
+ if (ret != -EINVAL && ret < 0) {
+ dev_err(priv->dev, "Property %s malformed: %d\n", prop, ret);
+ return ret;
+ }
+
+ if (val)
+ *val = defval;
+
+ for (i = 0; i < nvalues; i++)
+ if (defval == values[i])
+ return i;
+
+ dev_err(priv->dev, "Invalid value for property %s: %d\n", prop, defval);
+ return -EINVAL;
+}
+
+int cs42l43_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ /* This tip sense invert is always set, HW wants an inverted signal */
+ unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK;
+ unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT;
+ unsigned int autocontrol = 0, pdncntl = 0;
+ int ret;
+
+ dev_dbg(priv->dev, "Configure accessory detect\n");
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ priv->jack_hp = jack;
+
+ if (!jack)
+ goto done;
+
+ ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms");
+ if (ret != -EINVAL) {
+ if (ret < 0) {
+ dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n",
+ ret);
+ goto error;
+ }
+
+ if (ret > CS42L43_N_BUTTONS) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n");
+ goto error;
+ }
+
+ ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms",
+ priv->buttons, ret);
+ if (ret < 0) {
+ dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n",
+ ret);
+ goto error;
+ }
+ } else {
+ priv->buttons[0] = 70;
+ priv->buttons[1] = 185;
+ priv->buttons[2] = 355;
+ priv->buttons[3] = 735;
+ }
+
+ ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us,
+ cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us));
+ if (ret < 0)
+ goto error;
+
+ hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT;
+
+ priv->bias_low = device_property_read_bool(cs42l43->dev, "cirrus,bias-low");
+
+ ret = cs42l43_find_index(priv, "cirrus,bias-ramp-ms", 170,
+ &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms,
+ ARRAY_SIZE(cs42l43_accdet_ramp_ms));
+ if (ret < 0)
+ goto error;
+
+ hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,bias-sense-microamp", 14,
+ &priv->bias_sense_ua, cs42l43_accdet_bias_sense,
+ ARRAY_SIZE(cs42l43_accdet_bias_sense));
+ if (ret < 0)
+ goto error;
+
+ if (priv->bias_sense_ua)
+ autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT;
+
+ if (!device_property_read_bool(cs42l43->dev, "cirrus,button-automute"))
+ autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK;
+
+ ret = device_property_read_u32(cs42l43->dev, "cirrus,tip-debounce-ms",
+ &priv->tip_debounce_ms);
+ if (ret < 0 && ret != -EINVAL) {
+ dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret);
+ goto error;
+ }
+
+ /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */
+ if (device_property_read_bool(cs42l43->dev, "cirrus,tip-invert"))
+ autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT;
+
+ if (device_property_read_bool(cs42l43->dev, "cirrus,tip-disable-pullup"))
+ autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT;
+ else
+ autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500,
+ &priv->tip_fall_db_ms, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500,
+ &priv->tip_rise_db_ms, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT;
+
+ if (device_property_read_bool(cs42l43->dev, "cirrus,use-ring-sense")) {
+ unsigned int ring_deb = 0;
+
+ priv->use_ring_sense = true;
+
+ /* HW wants an inverted signal, so invert the invert */
+ if (!device_property_read_bool(cs42l43->dev, "cirrus,ring-invert"))
+ ring_deb |= CS42L43_RINGSENSE_INV_MASK;
+
+ if (!device_property_read_bool(cs42l43->dev,
+ "cirrus,ring-disable-pullup"))
+ ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK;
+
+ ret = cs42l43_find_index(priv, "cirrus,ring-fall-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,ring-rise-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT;
+ pdncntl |= CS42L43_RING_SENSE_EN_MASK;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL,
+ CS42L43_RINGSENSE_INV_MASK |
+ CS42L43_RINGSENSE_PULLUP_PDNB_MASK |
+ CS42L43_RINGSENSE_FALLING_DB_TIME_MASK |
+ CS42L43_RINGSENSE_RISING_DB_TIME_MASK,
+ ring_deb);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL,
+ CS42L43_TIPSENSE_INV_MASK |
+ CS42L43_TIPSENSE_FALLING_DB_TIME_MASK |
+ CS42L43_TIPSENSE_RISING_DB_TIME_MASK, tip_deb);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK |
+ CS42L43_AUTO_HSDET_TIME_MASK, hs2);
+
+done:
+ ret = 0;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK |
+ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL,
+ CS42L43_RING_SENSE_EN_MASK, pdncntl);
+
+ dev_dbg(priv->dev, "Successfully configured accessory detect\n");
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ dev_dbg(priv->dev, "Start headset bias\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
+
+ if (!type_detect) {
+ if (priv->bias_low)
+ val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ if (priv->bias_sense_ua)
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, val);
+
+ msleep(priv->bias_ramp_ms);
+}
+
+static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop headset bias\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, 0);
+
+ if (priv->bias_sense_ua) {
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0);
+ }
+}
+
+irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ queue_delayed_work(system_dfl_wq, &priv->bias_sense_timeout,
+ msecs_to_jiffies(1000));
+
+ return IRQ_HANDLED;
+}
+
+#define CS42L43_JACK_PRESENT 0x3
+#define CS42L43_JACK_ABSENT 0x0
+
+#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
+#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
+#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
+#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
+#define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN)
+#define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL)
+#define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5)
+
+static inline bool cs42l43_jack_present(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int sts = 0;
+
+ regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts);
+
+ sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+
+ return sts == CS42L43_JACK_PRESENT;
+}
+
+static void cs42l43_start_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT;
+
+ dev_dbg(priv->dev, "Start button detect\n");
+
+ priv->button_detect_running = true;
+
+ if (priv->bias_low)
+ val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_BUTTON_DETECT_MODE_MASK |
+ CS42L43_MIC_LVL_DET_DISABLE_MASK, val);
+}
+
+static void cs42l43_stop_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop button detect\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_BUTTON_DETECT_MODE_MASK |
+ CS42L43_MIC_LVL_DET_DISABLE_MASK,
+ CS42L43_MIC_LVL_DET_DISABLE_MASK);
+
+ priv->button_detect_running = false;
+}
+
+#define CS42L43_BUTTON_COMB_US 11000
+#define CS42L43_BUTTON_COMB_MAX 512
+#define CS42L43_BUTTON_ROUT 2210
+
+irqreturn_t cs42l43_button_press(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct cs42l43 *cs42l43 = priv->core;
+ irqreturn_t iret = IRQ_NONE;
+ unsigned int buttons = 0;
+ unsigned int val = 0;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for button press: %d\n", ret);
+ return iret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!priv->button_detect_running) {
+ dev_dbg(priv->dev, "Spurious button press IRQ\n");
+ goto error;
+ }
+
+ // Wait for 2 full cycles of comb filter to ensure good reading
+ usleep_range(2 * CS42L43_BUTTON_COMB_US, 2 * CS42L43_BUTTON_COMB_US + 50);
+
+ regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val);
+
+ /* Bail if jack removed, the button is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv)) {
+ dev_dbg(priv->dev, "Button ignored due to removal\n");
+ goto error;
+ }
+
+ if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) {
+ dev_dbg(priv->dev, "Button ignored due to bias sense\n");
+ goto error;
+ }
+
+ val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT;
+ val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20);
+ if (val)
+ val = (CS42L43_BUTTON_ROUT << 20) / val;
+ else
+ val = UINT_MAX;
+
+ for (i = 0; i < CS42L43_N_BUTTONS; i++) {
+ if (val < priv->buttons[i]) {
+ buttons = SND_JACK_BTN_0 >> i;
+ dev_dbg(priv->dev, "Detected button %d at %d Ohms\n", i, val);
+ break;
+ }
+ }
+
+ if (!buttons)
+ dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n", val);
+
+ snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS);
+
+ iret = IRQ_HANDLED;
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return iret;
+}
+
+irqreturn_t cs42l43_button_release(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ irqreturn_t iret = IRQ_NONE;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for button release: %d\n", ret);
+ return iret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (priv->button_detect_running) {
+ dev_dbg(priv->dev, "Button release IRQ\n");
+
+ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS);
+
+ iret = IRQ_HANDLED;
+ } else {
+ dev_dbg(priv->dev, "Spurious button release IRQ\n");
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return iret;
+}
+
+void cs42l43_bias_sense_timeout(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ bias_sense_timeout.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (cs42l43_jack_present(priv) && priv->button_detect_running) {
+ dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n");
+
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK);
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+static void cs42l43_start_load_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Start load detect\n");
+
+ snd_soc_dapm_mutex_lock(snd_soc_component_to_dapm(priv->component));
+
+ priv->load_detect_running = true;
+
+ if (priv->hp_ena && !priv->hp_ilimited) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_shutdown);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ time_left = wait_for_completion_timeout(&priv->hp_shutdown,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "Load detect HP power down timed out\n");
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL,
+ CS42L43_ADPTPWR_MODE_MASK, 0x4 << CS42L43_ADPTPWR_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL,
+ CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, 0x6);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, 0);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA,
+ CS42L43_HPLOAD_DET_EN_MASK,
+ CS42L43_HPLOAD_DET_EN_MASK);
+
+ snd_soc_dapm_mutex_unlock(snd_soc_component_to_dapm(priv->component));
+}
+
+static void cs42l43_stop_load_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop load detect\n");
+
+ snd_soc_dapm_mutex_lock(snd_soc_component_to_dapm(priv->component));
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA,
+ CS42L43_HPLOAD_DET_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL,
+ CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK,
+ 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL,
+ CS42L43_ADPTPWR_MODE_MASK, 0x7 << CS42L43_ADPTPWR_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2,
+ CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK,
+ priv->adc_ena);
+
+ if (priv->hp_ena && !priv->hp_ilimited) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_startup);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, priv->hp_ena);
+
+ time_left = wait_for_completion_timeout(&priv->hp_startup,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "Load detect HP restore timed out\n");
+ }
+
+ priv->load_detect_running = false;
+
+ snd_soc_dapm_mutex_unlock(snd_soc_component_to_dapm(priv->component));
+}
+
+static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0;
+ unsigned long time_left;
+
+ reinit_completion(&priv->load_detect);
+
+ cs42l43_start_load_detect(priv);
+ time_left = wait_for_completion_timeout(&priv->load_detect,
+ msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS));
+ cs42l43_stop_load_detect(priv);
+
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ regmap_read(cs42l43->regmap, CS42L43_LOADDETRESULTS, &val);
+
+ dev_dbg(priv->dev, "Headphone load detect: 0x%x\n", val);
+
+ /* Bail if jack removed, the load is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv))
+ return -ENODEV;
+
+ if (mic) {
+ cs42l43_start_hs_bias(priv, false);
+ cs42l43_start_button_detect(priv);
+
+ return CS42L43_JACK_HEADSET;
+ }
+
+ switch (val & CS42L43_AMP3_RES_DET_MASK) {
+ case 0x0: // low impedance
+ case 0x1: // high impedance
+ return CS42L43_JACK_HEADPHONE;
+ case 0x2: // lineout
+ case 0x3: // Open circuit
+ return CS42L43_JACK_LINEOUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_run_type_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200;
+ unsigned int type = 0xff;
+ unsigned long time_left;
+
+ reinit_completion(&priv->type_detect);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK);
+
+ cs42l43_start_hs_bias(priv, true);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT);
+
+ time_left = wait_for_completion_timeout(&priv->type_detect,
+ msecs_to_jiffies(timeout_ms));
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT);
+ cs42l43_stop_hs_bias(priv);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK, 0);
+
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ regmap_read(cs42l43->regmap, CS42L43_HS_STAT, &type);
+
+ dev_dbg(priv->dev, "Type detect: 0x%x\n", type);
+
+ /* Bail if jack removed, the type is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv))
+ return -ENODEV;
+
+ switch (type & CS42L43_HSDET_TYPE_STS_MASK) {
+ case 0x0: // CTIA
+ case 0x1: // OMTP
+ return cs42l43_run_load_detect(priv, true);
+ case 0x2: // 3-pole
+ return cs42l43_run_load_detect(priv, false);
+ case 0x3: // Open-circuit
+ return CS42L43_JACK_EXTENSION;
+ default:
+ return -EINVAL;
+ }
+}
+
+void cs42l43_clear_jack(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ cs42l43_stop_button_detect(priv);
+ cs42l43_stop_hs_bias(priv);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL,
+ CS42L43_JACK_STEREO_CONFIG_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK,
+ 0x2 << CS42L43_HSDET_MODE_SHIFT);
+}
+
+void cs42l43_tip_sense_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ tip_sense_work.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int sts = 0;
+ unsigned int tip, ring;
+ int ret, report;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for tip work: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts);
+
+ dev_dbg(priv->dev, "Tip sense: 0x%x\n", sts);
+
+ tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+ ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+
+ if (tip == CS42L43_JACK_PRESENT) {
+ if (cs42l43->sdw && !priv->jack_present) {
+ priv->jack_present = true;
+ pm_runtime_get(priv->dev);
+ }
+
+ if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) {
+ report = CS42L43_JACK_OPTICAL;
+ } else {
+ report = cs42l43_run_type_detect(priv);
+ if (report < 0) {
+ dev_err(priv->dev, "Jack detect failed: %d\n", report);
+ goto error;
+ }
+ }
+
+ snd_soc_jack_report(priv->jack_hp, report, report);
+ } else {
+ priv->jack_override = 0;
+
+ cs42l43_clear_jack(priv);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
+ if (cs42l43->sdw && priv->jack_present) {
+ pm_runtime_put(priv->dev);
+ priv->jack_present = false;
+ }
+ }
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ priv->suspend_jack_debounce = false;
+
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+irqreturn_t cs42l43_tip_sense(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ unsigned int db_delay = priv->tip_debounce_ms;
+
+ cancel_delayed_work(&priv->bias_sense_timeout);
+ cancel_delayed_work(&priv->tip_sense_work);
+
+ // Ensure delay after suspend is long enough to avoid false detection
+ if (priv->suspend_jack_debounce)
+ db_delay += priv->tip_fall_db_ms + priv->tip_rise_db_ms;
+
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work,
+ msecs_to_jiffies(db_delay));
+
+ return IRQ_HANDLED;
+}
+
+enum cs42l43_raw_jack {
+ CS42L43_JACK_RAW_CTIA = 0,
+ CS42L43_JACK_RAW_OMTP,
+ CS42L43_JACK_RAW_HEADPHONE,
+ CS42L43_JACK_RAW_LINE_OUT,
+ CS42L43_JACK_RAW_LINE_IN,
+ CS42L43_JACK_RAW_MICROPHONE,
+ CS42L43_JACK_RAW_OPTICAL,
+};
+
+#define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \
+ CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \
+ CS42L43_HSGND_HS3_SEL_MASK | \
+ CS42L43_HSGND_HS4_SEL_MASK)
+
+static const struct cs42l43_jack_override_mode {
+ unsigned int hsdet_mode;
+ unsigned int mic_ctrl;
+ unsigned int clamp_ctrl;
+ int report;
+} cs42l43_jack_override_modes[] = {
+ [CS42L43_JACK_RAW_CTIA] = {
+ .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
+ CS42L43_HSGND_HS3_SEL_MASK,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADSET,
+ },
+ [CS42L43_JACK_RAW_OMTP] = {
+ .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) |
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
+ CS42L43_HSGND_HS4_SEL_MASK,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADSET,
+ },
+ [CS42L43_JACK_RAW_HEADPHONE] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADPHONE,
+ },
+ [CS42L43_JACK_RAW_LINE_OUT] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_LINEOUT,
+ },
+ [CS42L43_JACK_RAW_LINE_IN] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT,
+ .report = CS42L43_JACK_LINEIN,
+ },
+ [CS42L43_JACK_RAW_MICROPHONE] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
+ CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
+ .report = CS42L43_JACK_LINEIN,
+ },
+ [CS42L43_JACK_RAW_OPTICAL] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_OPTICAL,
+ },
+};
+
+static const char * const cs42l43_jack_text[] = {
+ "None", "CTIA", "OMTP", "Headphone", "Line-Out",
+ "Line-In", "Microphone", "Optical",
+};
+
+static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) ==
+ ARRAY_SIZE(cs42l43_jack_text) - 1);
+
+SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text);
+
+int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->jack_lock);
+ ucontrol->value.integer.value[0] = priv->jack_override;
+ mutex_unlock(&priv->jack_lock);
+
+ return 0;
+}
+
+int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int override = ucontrol->value.integer.value[0];
+
+ if (override >= e->items)
+ return -EINVAL;
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!cs42l43_jack_present(priv)) {
+ mutex_unlock(&priv->jack_lock);
+ return -EBUSY;
+ }
+
+ if (override == priv->jack_override) {
+ mutex_unlock(&priv->jack_lock);
+ return 0;
+ }
+
+ priv->jack_override = override;
+
+ cs42l43_clear_jack(priv);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
+ if (!override) {
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0);
+ } else {
+ override--;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK |
+ CS42L43_HSDET_MANUAL_MODE_MASK |
+ CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
+ CS42L43_HSGND_HS3_SEL_MASK |
+ CS42L43_HSGND_HS4_SEL_MASK,
+ cs42l43_jack_override_modes[override].hsdet_mode);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL,
+ CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK |
+ CS42L43_JACK_STEREO_CONFIG_MASK,
+ cs42l43_jack_override_modes[override].mic_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ cs42l43_jack_override_modes[override].clamp_ctrl);
+
+ switch (override) {
+ case CS42L43_JACK_RAW_CTIA:
+ case CS42L43_JACK_RAW_OMTP:
+ cs42l43_start_hs_bias(priv, false);
+ cs42l43_start_button_detect(priv);
+ break;
+ case CS42L43_JACK_RAW_LINE_IN:
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK);
+ break;
+ case CS42L43_JACK_RAW_MICROPHONE:
+ cs42l43_start_hs_bias(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_jack_report(priv->jack_hp,
+ cs42l43_jack_override_modes[override].report,
+ cs42l43_jack_override_modes[override].report);
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ return 1;
+}
diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c
new file mode 100644
index 000000000000..336e88a7a987
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-sdw.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver SoundWire handling
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/errno.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/pcm.h>
+#include <sound/sdw.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+
+#include "cs42l43.h"
+
+int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent);
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &sconfig, &pconfig);
+ pconfig.num = dai->id;
+
+ ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream);
+ if (ret) {
+ dev_err(priv->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_add_peripheral, "SND_SOC_CS42L43");
+
+int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ return sdw_stream_remove_slave(sdw, sdw_stream);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_remove_peripheral, "SND_SOC_CS42L43");
+
+int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_set_stream, "SND_SOC_CS42L43");
+
+MODULE_DESCRIPTION("CS42L43 CODEC SoundWire Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
new file mode 100644
index 000000000000..fd02d8a57e0f
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.c
@@ -0,0 +1,2476 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/bitmap.h>
+#include <linux/gcd.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/cs42l43.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs42l43.h"
+
+#define CS42L43_DECL_MUX(name, reg) \
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \
+ 0, CS42L43_MIXER_SRC_MASK, \
+ cs42l43_mixer_texts, cs42l43_mixer_values); \
+static const struct snd_kcontrol_new cs42l43_##name##_mux = \
+ SOC_DAPM_ENUM("Route", cs42l43_##name##_enum)
+
+#define CS42L43_DECL_MIXER(name, reg) \
+ CS42L43_DECL_MUX(name##_in1, reg); \
+ CS42L43_DECL_MUX(name##_in2, reg + 0x4); \
+ CS42L43_DECL_MUX(name##_in3, reg + 0x8); \
+ CS42L43_DECL_MUX(name##_in4, reg + 0xC)
+
+#define CS42L43_DAPM_MUX(name_str, name) \
+ SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux)
+
+#define CS42L43_DAPM_MIXER(name_str, name) \
+ SND_SOC_DAPM_MUX(name_str " Input 1", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in1_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 2", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in2_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 3", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in3_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 4", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS42L43_BASE_ROUTES(name_str) \
+ { name_str, "Tone Generator 1", "Tone 1" }, \
+ { name_str, "Tone Generator 2", "Tone 2" }, \
+ { name_str, "Decimator 1", "Decimator 1" }, \
+ { name_str, "Decimator 2", "Decimator 2" }, \
+ { name_str, "Decimator 3", "Decimator 3" }, \
+ { name_str, "Decimator 4", "Decimator 4" }, \
+ { name_str, "ASPRX1", "ASPRX1" }, \
+ { name_str, "ASPRX2", "ASPRX2" }, \
+ { name_str, "ASPRX3", "ASPRX3" }, \
+ { name_str, "ASPRX4", "ASPRX4" }, \
+ { name_str, "ASPRX5", "ASPRX5" }, \
+ { name_str, "ASPRX6", "ASPRX6" }, \
+ { name_str, "DP5RX1", "DP5RX1" }, \
+ { name_str, "DP5RX2", "DP5RX2" }, \
+ { name_str, "DP6RX1", "DP6RX1" }, \
+ { name_str, "DP6RX2", "DP6RX2" }, \
+ { name_str, "DP7RX1", "DP7RX1" }, \
+ { name_str, "DP7RX2", "DP7RX2" }, \
+ { name_str, "ASRC INT1", "ASRC_INT1" }, \
+ { name_str, "ASRC INT2", "ASRC_INT2" }, \
+ { name_str, "ASRC INT3", "ASRC_INT3" }, \
+ { name_str, "ASRC INT4", "ASRC_INT4" }, \
+ { name_str, "ASRC DEC1", "ASRC_DEC1" }, \
+ { name_str, "ASRC DEC2", "ASRC_DEC2" }, \
+ { name_str, "ASRC DEC3", "ASRC_DEC3" }, \
+ { name_str, "ASRC DEC4", "ASRC_DEC4" }, \
+ { name_str, "ISRC1 INT1", "ISRC1INT1" }, \
+ { name_str, "ISRC1 INT2", "ISRC1INT2" }, \
+ { name_str, "ISRC1 DEC1", "ISRC1DEC1" }, \
+ { name_str, "ISRC1 DEC2", "ISRC1DEC2" }, \
+ { name_str, "ISRC2 INT1", "ISRC2INT1" }, \
+ { name_str, "ISRC2 INT2", "ISRC2INT2" }, \
+ { name_str, "ISRC2 DEC1", "ISRC2DEC1" }, \
+ { name_str, "ISRC2 DEC2", "ISRC2DEC2" }, \
+ { name_str, "EQ1", "EQ" }, \
+ { name_str, "EQ2", "EQ" }
+
+#define CS42L43_MUX_ROUTES(name_str, widget) \
+ { widget, NULL, name_str " Input" }, \
+ { name_str " Input", NULL, "Mixer Core" }, \
+ CS42L43_BASE_ROUTES(name_str " Input")
+
+#define CS42L43_MIXER_ROUTES(name_str, widget) \
+ { name_str " Mixer", NULL, name_str " Input 1" }, \
+ { name_str " Mixer", NULL, name_str " Input 2" }, \
+ { name_str " Mixer", NULL, name_str " Input 3" }, \
+ { name_str " Mixer", NULL, name_str " Input 4" }, \
+ { widget, NULL, name_str " Mixer" }, \
+ { name_str " Mixer", NULL, "Mixer Core" }, \
+ CS42L43_BASE_ROUTES(name_str " Input 1"), \
+ CS42L43_BASE_ROUTES(name_str " Input 2"), \
+ CS42L43_BASE_ROUTES(name_str " Input 3"), \
+ CS42L43_BASE_ROUTES(name_str " Input 4")
+
+#define CS42L43_MIXER_VOLUMES(name_str, base) \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 2 Volume", base + 4, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 3 Volume", base + 8, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 4 Volume", base + 12, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv)
+
+#define CS42L43_IRQ_ERROR(name) \
+static irqreturn_t cs42l43_##name(int irq, void *data) \
+{ \
+ struct cs42l43_codec *priv = data; \
+ dev_err(priv->dev, "Error " #name " IRQ\n"); \
+ return IRQ_HANDLED; \
+}
+
+CS42L43_IRQ_ERROR(pll_lost_lock)
+CS42L43_IRQ_ERROR(spkr_clock_stop)
+CS42L43_IRQ_ERROR(spkl_clock_stop)
+CS42L43_IRQ_ERROR(spkr_brown_out)
+CS42L43_IRQ_ERROR(spkl_brown_out)
+CS42L43_IRQ_ERROR(spkr_therm_shutdown)
+CS42L43_IRQ_ERROR(spkl_therm_shutdown)
+CS42L43_IRQ_ERROR(spkr_therm_warm)
+CS42L43_IRQ_ERROR(spkl_therm_warm)
+CS42L43_IRQ_ERROR(spkr_sc_detect)
+CS42L43_IRQ_ERROR(spkl_sc_detect)
+
+static void cs42l43_hp_ilimit_clear_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ hp_ilimit_clear_work.work);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(priv->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ priv->hp_ilimit_count--;
+
+ if (priv->hp_ilimit_count)
+ queue_delayed_work(system_dfl_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t cs42l43_hp_ilimit(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(priv->component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "headphone ilimit IRQ\n");
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) {
+ if (!priv->hp_ilimit_count)
+ queue_delayed_work(system_dfl_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ priv->hp_ilimit_count++;
+ snd_soc_dapm_mutex_unlock(dapm);
+ return IRQ_HANDLED;
+ }
+
+ dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n",
+ CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ priv->hp_ilimited = true;
+
+ // No need to wait for disable, as just disabling for a period of time
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ msleep(CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ena && !priv->load_detect_running) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_startup);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, priv->hp_ena);
+
+ time_left = wait_for_completion_timeout(&priv->hp_startup,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "ilimit HP restore timed out\n");
+ }
+
+ priv->hp_ilimited = false;
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return IRQ_HANDLED;
+}
+
+#define CS42L43_IRQ_COMPLETE(name) \
+static irqreturn_t cs42l43_##name(int irq, void *data) \
+{ \
+ struct cs42l43_codec *priv = data; \
+ dev_dbg(priv->dev, #name " completed\n"); \
+ complete(&priv->name); \
+ return IRQ_HANDLED; \
+}
+
+CS42L43_IRQ_COMPLETE(pll_ready)
+CS42L43_IRQ_COMPLETE(hp_startup)
+CS42L43_IRQ_COMPLETE(hp_shutdown)
+CS42L43_IRQ_COMPLETE(type_detect)
+CS42L43_IRQ_COMPLETE(spkr_shutdown)
+CS42L43_IRQ_COMPLETE(spkl_shutdown)
+CS42L43_IRQ_COMPLETE(spkr_startup)
+CS42L43_IRQ_COMPLETE(spkl_startup)
+CS42L43_IRQ_COMPLETE(load_detect)
+
+static irqreturn_t cs42l43_mic_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_component *component = priv->component;
+ int i;
+
+ dev_dbg(priv->dev, "Microphone shutter changed\n");
+
+ if (!component)
+ return IRQ_NONE;
+
+ for (i = 1; i < ARRAY_SIZE(priv->kctl); i++) {
+ if (!priv->kctl[i])
+ return IRQ_NONE;
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[i]->id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs42l43_spk_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_component *component = priv->component;
+
+ dev_dbg(priv->dev, "Speaker shutter changed\n");
+
+ if (!component)
+ return IRQ_NONE;
+
+ if (!priv->kctl[0])
+ return IRQ_NONE;
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[0]->id);
+
+ return IRQ_HANDLED;
+}
+
+static const unsigned int cs42l43_sample_rates[] = {
+ 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000,
+};
+
+#define CS42L43_CONSUMER_RATE_MASK 0xFF
+#define CS42L43_PROVIDER_RATE_MASK 0xEF // 44.1k only supported as consumer
+
+static const struct snd_pcm_hw_constraint_list cs42l43_constraint = {
+ .count = ARRAY_SIZE(cs42l43_sample_rates),
+ .list = cs42l43_sample_rates,
+};
+
+static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int provider = !dai->id || !!regmap_test_bits(cs42l43->regmap,
+ CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+
+ if (provider)
+ priv->constraint.mask = CS42L43_PROVIDER_RATE_MASK;
+ else
+ priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &priv->constraint);
+}
+
+static int cs42l43_convert_sample_rate(unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0x11;
+ case 16000:
+ return 0x12;
+ case 24000:
+ return 0x02;
+ case 32000:
+ return 0x13;
+ case 44100:
+ return 0x0B;
+ case 48000:
+ return 0x03;
+ case 96000:
+ return 0x04;
+ case 192000:
+ return 0x05;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_set_sample_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ ret = cs42l43_convert_sample_rate(params_rate(params));
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to convert sample rate: %d\n", ret);
+ return ret;
+ }
+
+ //FIXME: For now lets just set sample rate 1, this needs expanded in the future
+ regmap_update_bits(cs42l43->regmap, CS42L43_SAMPLE_RATE1,
+ CS42L43_SAMPLE_RATE_MASK, ret);
+
+ return 0;
+}
+
+static int cs42l43_asp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int dsp_mode = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CTRL,
+ CS42L43_ASP_FSYNC_MODE_MASK);
+ int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+ int n_chans = params_channels(params);
+ int data_width = params_width(params);
+ int n_slots = n_chans;
+ int slot_width = data_width;
+ int frame, bclk_target, i;
+ unsigned int reg;
+ int *slots;
+
+ if (priv->n_slots) {
+ n_slots = priv->n_slots;
+ slot_width = priv->slot_width;
+ }
+
+ if (!dsp_mode && (n_slots & 0x1)) {
+ dev_dbg(priv->dev, "Forcing balanced channels on ASP\n");
+ n_slots++;
+ }
+
+ frame = n_slots * slot_width;
+ bclk_target = params_rate(params) * frame;
+
+ if (provider) {
+ unsigned int gcd_nm = gcd(bclk_target, CS42L43_INTERNAL_SYSCLK);
+ int n = bclk_target / gcd_nm;
+ int m = CS42L43_INTERNAL_SYSCLK / gcd_nm;
+
+ if (n > (CS42L43_ASP_BCLK_N_MASK >> CS42L43_ASP_BCLK_N_SHIFT) ||
+ m > CS42L43_ASP_BCLK_M_MASK) {
+ dev_err(priv->dev, "Can't produce %dHz bclk\n", bclk_target);
+ return -EINVAL;
+ }
+
+ dev_dbg(priv->dev, "bclk %d/%d = %dHz, with %dx%d frame\n",
+ n, m, bclk_target, n_slots, slot_width);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG1,
+ CS42L43_ASP_BCLK_N_MASK | CS42L43_ASP_BCLK_M_MASK,
+ n << CS42L43_ASP_BCLK_N_SHIFT |
+ m << CS42L43_ASP_BCLK_M_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL1,
+ CS42L43_ASP_FSYNC_M_MASK, frame);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL4,
+ CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK,
+ frame << CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ reg = CS42L43_ASP_TX_CH1_CTRL;
+ slots = priv->tx_slots;
+ } else {
+ reg = CS42L43_ASP_RX_CH1_CTRL;
+ slots = priv->rx_slots;
+ }
+
+ for (i = 0; i < n_chans; i++, reg += 4) {
+ int slot_phase = dsp_mode | (i & CS42L43_ASP_CH_SLOT_PHASE_MASK);
+ int slot_pos;
+
+ if (dsp_mode)
+ slot_pos = slots[i] * slot_width;
+ else
+ slot_pos = (slots[i] / 2) * slot_width;
+
+ dev_dbg(priv->dev, "Configure channel %d at slot %d (%d,%d)\n",
+ i, slots[i], slot_pos, slot_phase);
+
+ regmap_update_bits(cs42l43->regmap, reg,
+ CS42L43_ASP_CH_WIDTH_MASK |
+ CS42L43_ASP_CH_SLOT_MASK |
+ CS42L43_ASP_CH_SLOT_PHASE_MASK,
+ ((data_width - 1) << CS42L43_ASP_CH_WIDTH_SHIFT) |
+ (slot_pos << CS42L43_ASP_CH_SLOT_SHIFT) |
+ slot_phase);
+ }
+
+ return cs42l43_set_sample_rate(substream, params, dai);
+}
+
+static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int provider = regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+ struct snd_soc_dapm_route routes[] = {
+ { "BCLK", NULL, "FSYNC" },
+ };
+ unsigned int asp_ctrl = 0;
+ unsigned int data_ctrl = 0;
+ unsigned int fsync_ctrl = 0;
+ unsigned int clk_config = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_B:
+ asp_ctrl |= CS42L43_ASP_FSYNC_MODE_MASK;
+ data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ if (provider)
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (!provider)
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+ clk_config |= CS42L43_ASP_MASTER_MODE_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported ASP mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ clk_config |= CS42L43_ASP_BCLK_INV_MASK; /* Yes BCLK_INV = NB */
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ clk_config |= CS42L43_ASP_BCLK_INV_MASK;
+ fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CTRL,
+ CS42L43_ASP_FSYNC_MODE_MASK,
+ asp_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_DATA_CTRL,
+ CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK |
+ CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK,
+ data_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK |
+ CS42L43_ASP_BCLK_INV_MASK,
+ clk_config);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL3,
+ CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK,
+ fsync_ctrl);
+
+ return 0;
+}
+
+static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned long mask,
+ int *slots, unsigned int nslots)
+{
+ int i = 0;
+ int slot;
+
+ for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) {
+ if (i == nslots) {
+ dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n",
+ mask);
+ return;
+ }
+
+ slots[i++] = slot;
+ }
+
+}
+
+static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ priv->n_slots = slots;
+ priv->slot_width = slot_width;
+
+ if (!slots) {
+ tx_mask = CS42L43_DEFAULT_SLOTS;
+ rx_mask = CS42L43_DEFAULT_SLOTS;
+ }
+
+ cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots,
+ ARRAY_SIZE(priv->tx_slots));
+ cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots,
+ ARRAY_SIZE(priv->rx_slots));
+
+ return 0;
+}
+
+static int cs42l43_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ static const char * const controls[] = {
+ "Speaker Digital Switch",
+ "Decimator 1 Switch",
+ "Decimator 2 Switch",
+ "Decimator 3 Switch",
+ "Decimator 4 Switch",
+ };
+ int i;
+
+ static_assert(ARRAY_SIZE(controls) == ARRAY_SIZE(priv->kctl));
+
+ for (i = 0; i < ARRAY_SIZE(controls); i++) {
+ if (priv->kctl[i])
+ continue;
+
+ priv->kctl[i] = snd_soc_component_get_kcontrol(component, controls[i]);
+ }
+
+ return 0;
+}
+
+static int cs42l43_dai_remove(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->kctl); i++)
+ priv->kctl[i] = NULL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l43_asp_ops = {
+ .probe = cs42l43_dai_probe,
+ .remove = cs42l43_dai_remove,
+ .startup = cs42l43_startup,
+ .hw_params = cs42l43_asp_hw_params,
+ .set_fmt = cs42l43_asp_set_fmt,
+ .set_tdm_slot = cs42l43_asp_set_tdm_slot,
+};
+
+static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = cs42l43_sdw_add_peripheral(substream, params, dai);
+ if (ret)
+ return ret;
+
+ return cs42l43_set_sample_rate(substream, params, dai);
+}
+
+static const struct snd_soc_dai_ops cs42l43_sdw_ops = {
+ .probe = cs42l43_dai_probe,
+ .remove = cs42l43_dai_remove,
+ .startup = cs42l43_startup,
+ .set_stream = cs42l43_sdw_set_stream,
+ .hw_params = cs42l43_sdw_hw_params,
+ .hw_free = cs42l43_sdw_remove_peripheral,
+};
+
+#define CS42L43_ASP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define CS42L43_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver cs42l43_dais[] = {
+ {
+ .name = "cs42l43-asp",
+ .ops = &cs42l43_asp_ops,
+ .symmetric_rate = 1,
+ .capture = {
+ .stream_name = "ASP Capture",
+ .channels_min = 1,
+ .channels_max = CS42L43_ASP_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_ASP_FORMATS,
+ },
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 1,
+ .channels_max = CS42L43_ASP_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_ASP_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp1",
+ .id = 1,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp2",
+ .id = 2,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp3",
+ .id = 3,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp4",
+ .id = 4,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp5",
+ .id = 5,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP5 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp6",
+ .id = 6,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP6 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp7",
+ .id = 7,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP7 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+};
+
+static const DECLARE_TLV_DB_SCALE(cs42l43_mixer_tlv, -3200, 100, 0);
+
+static const char * const cs42l43_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+static const char * const cs42l43_adc1_input_text[] = { "IN1", "IN2" };
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_adc1_input, CS42L43_ADC_B_CTRL1,
+ CS42L43_ADC_AIN_SEL_SHIFT,
+ cs42l43_adc1_input_text);
+
+static const struct snd_kcontrol_new cs42l43_adc1_input_ctl =
+ SOC_DAPM_ENUM("ADC1 Input", cs42l43_adc1_input);
+
+static const char * const cs42l43_dec_mode_text[] = { "ADC", "PDM" };
+
+static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec1_mode, cs42l43_dec_mode_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec2_mode, cs42l43_dec_mode_text);
+
+static const struct snd_kcontrol_new cs42l43_dec_mode_ctl[] = {
+ SOC_DAPM_ENUM("Decimator 1 Mode", cs42l43_dec1_mode),
+ SOC_DAPM_ENUM("Decimator 2 Mode", cs42l43_dec2_mode),
+};
+
+static const char * const cs42l43_pdm_clk_text[] = {
+ "3.072MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_pdm1_clk, CS42L43_PDM_CONTROL,
+ CS42L43_PDM1_CLK_DIV_SHIFT, cs42l43_pdm_clk_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_pdm2_clk, CS42L43_PDM_CONTROL,
+ CS42L43_PDM2_CLK_DIV_SHIFT, cs42l43_pdm_clk_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_adc_tlv, -600, 600, 0);
+static DECLARE_TLV_DB_SCALE(cs42l43_dec_tlv, -6400, 50, 0);
+
+static const char * const cs42l43_wnf_corner_text[] = {
+ "160Hz", "180Hz", "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+
+static const char * const cs42l43_hpf_corner_text[] = {
+ "3Hz", "12Hz", "48Hz", "96Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP,
+ CS42L43_AMP1_2_VI_RAMP_SHIFT, cs42l43_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_down, CS42L43_AMP1_2_VOL_RAMP,
+ CS42L43_AMP1_2_VD_RAMP_SHIFT, cs42l43_ramp_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_headphone_tlv, -11450, 50, 1);
+
+static const char * const cs42l43_headphone_ramp_text[] = {
+ "1", "2", "4", "6", "8", "11", "12", "16", "22", "24", "33", "36", "44",
+ "48", "66", "72",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_headphone_ramp, CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_RAMP_SHIFT,
+ cs42l43_headphone_ramp_text);
+
+static const char * const cs42l43_tone_freq_text[] = {
+ "1kHz", "2kHz", "4kHz", "6kHz", "8kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_tone1_freq, CS42L43_TONE_CH1_CTRL,
+ CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_tone2_freq, CS42L43_TONE_CH2_CTRL,
+ CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text);
+
+static const char * const cs42l43_mixer_texts[] = {
+ "None",
+ "Tone Generator 1", "Tone Generator 2",
+ "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4",
+ "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6",
+ "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2",
+ "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4",
+ "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4",
+ "ISRC1 INT1", "ISRC1 INT2",
+ "ISRC1 DEC1", "ISRC1 DEC2",
+ "ISRC2 INT1", "ISRC2 INT2",
+ "ISRC2 DEC1", "ISRC2 DEC2",
+ "EQ1", "EQ2",
+};
+
+static const unsigned int cs42l43_mixer_values[] = {
+ 0x00, // None
+ 0x04, 0x05, // Tone Generator 1, 2
+ 0x10, 0x11, 0x12, 0x13, // Decimator 1, 2, 3, 4
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2
+ 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4
+ 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4
+ 0x50, 0x51, // ISRC1 INT1, 2
+ 0x52, 0x53, // ISRC1 DEC1, 2
+ 0x54, 0x55, // ISRC2 INT1, 2
+ 0x56, 0x57, // ISRC2 DEC1, 2
+ 0x58, 0x59, // EQ1, 2
+};
+
+CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT);
+CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT);
+CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT);
+CS42L43_DECL_MUX(asptx4, CS42L43_ASPTX4_INPUT);
+CS42L43_DECL_MUX(asptx5, CS42L43_ASPTX5_INPUT);
+CS42L43_DECL_MUX(asptx6, CS42L43_ASPTX6_INPUT);
+
+CS42L43_DECL_MUX(dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT);
+CS42L43_DECL_MUX(dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT);
+CS42L43_DECL_MUX(dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT);
+CS42L43_DECL_MUX(dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT);
+CS42L43_DECL_MUX(dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT);
+CS42L43_DECL_MUX(dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT);
+CS42L43_DECL_MUX(dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT);
+CS42L43_DECL_MUX(dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT);
+CS42L43_DECL_MUX(dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT);
+CS42L43_DECL_MUX(dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT);
+
+CS42L43_DECL_MUX(asrcint1, CS42L43_ASRC_INT1_INPUT1);
+CS42L43_DECL_MUX(asrcint2, CS42L43_ASRC_INT2_INPUT1);
+CS42L43_DECL_MUX(asrcint3, CS42L43_ASRC_INT3_INPUT1);
+CS42L43_DECL_MUX(asrcint4, CS42L43_ASRC_INT4_INPUT1);
+CS42L43_DECL_MUX(asrcdec1, CS42L43_ASRC_DEC1_INPUT1);
+CS42L43_DECL_MUX(asrcdec2, CS42L43_ASRC_DEC2_INPUT1);
+CS42L43_DECL_MUX(asrcdec3, CS42L43_ASRC_DEC3_INPUT1);
+CS42L43_DECL_MUX(asrcdec4, CS42L43_ASRC_DEC4_INPUT1);
+
+CS42L43_DECL_MUX(isrc1int1, CS42L43_ISRC1INT1_INPUT1);
+CS42L43_DECL_MUX(isrc1int2, CS42L43_ISRC1INT2_INPUT1);
+CS42L43_DECL_MUX(isrc1dec1, CS42L43_ISRC1DEC1_INPUT1);
+CS42L43_DECL_MUX(isrc1dec2, CS42L43_ISRC1DEC2_INPUT1);
+CS42L43_DECL_MUX(isrc2int1, CS42L43_ISRC2INT1_INPUT1);
+CS42L43_DECL_MUX(isrc2int2, CS42L43_ISRC2INT2_INPUT1);
+CS42L43_DECL_MUX(isrc2dec1, CS42L43_ISRC2DEC1_INPUT1);
+CS42L43_DECL_MUX(isrc2dec2, CS42L43_ISRC2DEC2_INPUT1);
+
+CS42L43_DECL_MUX(spdif1, CS42L43_SPDIF1_INPUT1);
+CS42L43_DECL_MUX(spdif2, CS42L43_SPDIF2_INPUT1);
+
+CS42L43_DECL_MIXER(eq1, CS42L43_EQ1MIX_INPUT1);
+CS42L43_DECL_MIXER(eq2, CS42L43_EQ2MIX_INPUT1);
+
+CS42L43_DECL_MIXER(amp1, CS42L43_AMP1MIX_INPUT1);
+CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1);
+
+CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1);
+CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1);
+
+static int cs42l43_dapm_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.integer.value, priv->eq_coeffs, sizeof(priv->eq_coeffs));
+
+ return 0;
+}
+
+static int cs42l43_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ memcpy(priv->eq_coeffs, ucontrol->value.integer.value, sizeof(priv->eq_coeffs));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ mutex_lock(&priv->spk_vu_lock);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1,
+ CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1,
+ CS42L43_AMP1_2_VU_MASK, 0);
+
+ mutex_unlock(&priv->spk_vu_lock);
+}
+
+static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for shutters: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * SHUTTER_CONTROL is a mix of volatile and non-volatile bits, so must
+ * be cached for the non-volatiles, so drop it from the cache here so
+ * we force a read.
+ */
+ ret = regcache_drop_region(cs42l43->regmap, CS42L43_SHUTTER_CONTROL,
+ CS42L43_SHUTTER_CONTROL);
+ if (ret) {
+ dev_err(priv->dev, "Failed to drop shutter from cache: %d\n", ret);
+ goto error;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check shutter status: %d\n", ret);
+ goto error;
+ }
+
+ ret = !(val & BIT(shift));
+
+ dev_dbg(priv->dev, "%s shutter is %s\n",
+ BIT(shift) == CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK ? "Mic" : "Speaker",
+ ret ? "open" : "closed");
+
+error:
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static int cs42l43_decim_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT);
+ if (ret > 0)
+ ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol);
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+
+ return ret;
+}
+
+static int cs42l43_spk_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT);
+ if (ret > 0)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+
+ return ret;
+}
+
+static int cs42l43_spk_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret > 0)
+ cs42l43_spk_vu_sync(priv);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs42l43_controls[] = {
+ SOC_ENUM_EXT("Jack Override", cs42l43_jack_enum,
+ cs42l43_jack_get, cs42l43_jack_put),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2,
+ CS42L43_ADC_PGA_GAIN_SHIFT,
+ 0xF, 4, cs42l43_adc_tlv),
+
+ SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL,
+ CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0),
+ SOC_DOUBLE("PDM2 Invert Switch", CS42L43_DMIC_PDM_CTRL,
+ CS42L43_PDM2L_INV_SHIFT, CS42L43_PDM2R_INV_SHIFT, 1, 0),
+ SOC_ENUM("PDM1 Clock", cs42l43_pdm1_clk),
+ SOC_ENUM("PDM2 Clock", cs42l43_pdm2_clk),
+
+ SOC_SINGLE("Decimator 1 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 2 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 3 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 4 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+
+ SOC_ENUM("Decimator 1 WNF Corner Frequency", cs42l43_dec1_wnf_corner),
+ SOC_ENUM("Decimator 2 WNF Corner Frequency", cs42l43_dec2_wnf_corner),
+ SOC_ENUM("Decimator 3 WNF Corner Frequency", cs42l43_dec3_wnf_corner),
+ SOC_ENUM("Decimator 4 WNF Corner Frequency", cs42l43_dec4_wnf_corner),
+
+ SOC_SINGLE("Decimator 1 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 2 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 3 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 4 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+
+ SOC_ENUM("Decimator 1 HPF Corner Frequency", cs42l43_dec1_hpf_corner),
+ SOC_ENUM("Decimator 2 HPF Corner Frequency", cs42l43_dec2_hpf_corner),
+ SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner),
+ SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner),
+
+ SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+
+ SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+
+ SOC_DOUBLE_R_EXT("Speaker Digital Switch",
+ CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2,
+ CS42L43_AMP_MUTE_SHIFT, 1, 1,
+ cs42l43_spk_get, cs42l43_spk_put),
+
+ SOC_DOUBLE_R_EXT_TLV("Speaker Digital Volume",
+ CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2,
+ CS42L43_AMP_VOL_SHIFT,
+ 0xBF, 0, snd_soc_get_volsw, cs42l43_spk_put,
+ cs42l43_speaker_tlv),
+
+ SOC_ENUM("Speaker Ramp Up", cs42l43_speaker_ramp_up),
+ SOC_ENUM("Speaker Ramp Down", cs42l43_speaker_ramp_down),
+
+ CS42L43_MIXER_VOLUMES("Speaker L", CS42L43_AMP1MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("Speaker R", CS42L43_AMP2MIX_INPUT1),
+
+ SOC_DOUBLE_SX_TLV("Headphone Digital Volume", CS42L43_HPPATHVOL,
+ CS42L43_AMP3_PATH_VOL_SHIFT, CS42L43_AMP4_PATH_VOL_SHIFT,
+ 0x11B, 229, cs42l43_headphone_tlv),
+
+ SOC_DOUBLE("Headphone Invert Switch", CS42L43_DACCNFG1,
+ CS42L43_AMP3_INV_SHIFT, CS42L43_AMP4_INV_SHIFT, 1, 0),
+
+ SOC_SINGLE("Headphone Zero Cross Switch", CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_ZC_SHIFT, 1, 0),
+ SOC_SINGLE("Headphone Ramp Switch", CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_SFT_SHIFT, 1, 0),
+ SOC_ENUM("Headphone Ramp Rate", cs42l43_headphone_ramp),
+
+ CS42L43_MIXER_VOLUMES("Headphone L", CS42L43_AMP3MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("Headphone R", CS42L43_AMP4MIX_INPUT1),
+
+ SOC_ENUM("Tone 1 Frequency", cs42l43_tone1_freq),
+ SOC_ENUM("Tone 2 Frequency", cs42l43_tone2_freq),
+
+ SOC_DOUBLE_EXT("EQ Switch",
+ CS42L43_MUTE_EQ_IN0, CS42L43_MUTE_EQ_CH1_SHIFT,
+ CS42L43_MUTE_EQ_CH2_SHIFT, 1, 1,
+ cs42l43_dapm_get_volsw, cs42l43_dapm_put_volsw),
+
+ SND_SOC_BYTES_E("EQ Coefficients", 0, CS42L43_N_EQ_COEFFS,
+ cs42l43_eq_get, cs42l43_eq_put),
+
+ CS42L43_MIXER_VOLUMES("EQ1", CS42L43_EQ1MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("EQ2", CS42L43_EQ2MIX_INPUT1),
+};
+
+static int cs42l43_eq_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val;
+ int i, ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0,
+ CS42L43_WRITE_MODE_MASK, CS42L43_WRITE_MODE_MASK);
+
+ for (i = 0; i < CS42L43_N_EQ_COEFFS; i++)
+ regmap_write(cs42l43->regmap, CS42L43_COEFF_DATA_IN0,
+ priv->eq_coeffs[i]);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0,
+ CS42L43_WRITE_MODE_MASK, 0);
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_INIT_DONE0,
+ val, (val & CS42L43_INITIALIZE_DONE_MASK),
+ 2000, 10000);
+ if (ret)
+ dev_err(priv->dev, "Failed to start EQs: %d\n", ret);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, 0);
+ return ret;
+ default:
+ return 0;
+ }
+}
+
+struct cs42l43_pll_config {
+ unsigned int freq;
+
+ unsigned int div;
+ unsigned int mode;
+ unsigned int cal;
+};
+
+static const struct cs42l43_pll_config cs42l43_pll_configs[] = {
+ { 2400000, 0x50000000, 0x1, 0xA4 },
+ { 3000000, 0x40000000, 0x1, 0x83 },
+ { 3072000, 0x40000000, 0x3, 0x80 },
+};
+
+static int cs42l43_set_pll(struct cs42l43_codec *priv, unsigned int src,
+ unsigned int freq)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ if (priv->refclk_src == src && priv->refclk_freq == freq)
+ return 0;
+
+ if (regmap_test_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK)) {
+ dev_err(priv->dev, "PLL active, can't change configuration\n");
+ return -EBUSY;
+ }
+
+ switch (src) {
+ case CS42L43_SYSCLK_MCLK:
+ case CS42L43_SYSCLK_SDW:
+ dev_dbg(priv->dev, "Source PLL from %s at %uHz\n",
+ src ? "SoundWire" : "MCLK", freq);
+
+ priv->refclk_src = src;
+ priv->refclk_freq = freq;
+
+ return 0;
+ default:
+ dev_err(priv->dev, "Invalid PLL source: 0x%x\n", src);
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_enable_pll(struct cs42l43_codec *priv)
+{
+ static const struct reg_sequence enable_seq[] = {
+ { CS42L43_OSC_DIV_SEL, 0x0, },
+ { CS42L43_MCLK_SRC_SEL, CS42L43_OSC_PLL_MCLK_SEL_MASK, 5, },
+ };
+ struct cs42l43 *cs42l43 = priv->core;
+ const struct cs42l43_pll_config *config = NULL;
+ unsigned int div = 0;
+ unsigned int freq = priv->refclk_freq;
+ unsigned long time_left;
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ if (priv->refclk_src == CS42L43_SYSCLK_SDW) {
+ if (!freq)
+ freq = cs42l43->sdw_freq;
+ else if (!cs42l43->sdw_freq)
+ cs42l43->sdw_freq = freq;
+ }
+
+ dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq);
+
+ div = fls(freq) -
+ fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq);
+ freq >>= div;
+
+ if (div <= CS42L43_PLL_REFCLK_DIV_MASK) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_pll_configs); i++) {
+ if (freq == cs42l43_pll_configs[i].freq) {
+ config = &cs42l43_pll_configs[i];
+ break;
+ }
+ }
+ }
+
+ if (!config) {
+ dev_err(priv->dev, "No suitable PLL config: 0x%x, %uHz\n", div, freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_DIV_MASK | CS42L43_PLL_REFCLK_SRC_MASK,
+ div << CS42L43_PLL_REFCLK_DIV_SHIFT |
+ priv->refclk_src << CS42L43_PLL_REFCLK_SRC_SHIFT);
+ regmap_write(cs42l43->regmap, CS42L43_FDIV_FRAC, config->div);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_MODE_BYPASS_500_MASK |
+ CS42L43_PLL_MODE_BYPASS_1029_MASK,
+ config->mode << CS42L43_PLL_MODE_BYPASS_1029_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CAL_RATIO,
+ CS42L43_PLL_CAL_RATIO_MASK, config->cal);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, CS42L43_PLL_REFCLK_EN_MASK);
+
+ reinit_completion(&priv->pll_ready);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_EN_MASK, CS42L43_PLL_EN_MASK);
+
+ time_left = wait_for_completion_timeout(&priv->pll_ready,
+ msecs_to_jiffies(CS42L43_PLL_TIMEOUT_MS));
+ if (!time_left) {
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, 0);
+
+ dev_err(priv->dev, "Timeout out waiting for PLL\n");
+ return -ETIMEDOUT;
+ }
+
+ if (priv->refclk_src == CS42L43_SYSCLK_SDW)
+ cs42l43->sdw_pll_active = true;
+
+ dev_dbg(priv->dev, "PLL locked in %ums\n", 200 - jiffies_to_msecs(time_left));
+
+ /*
+ * Reads are not allowed over Soundwire without OSC_DIV2_EN or the PLL,
+ * but you can not change to PLL with OSC_DIV2_EN set. So ensure the whole
+ * change over happens under the regmap lock to prevent any reads.
+ */
+ regmap_multi_reg_write(cs42l43->regmap, enable_seq, ARRAY_SIZE(enable_seq));
+
+ return 0;
+}
+
+static int cs42l43_disable_pll(struct cs42l43_codec *priv)
+{
+ static const struct reg_sequence disable_seq[] = {
+ { CS42L43_MCLK_SRC_SEL, 0x0, 5, },
+ { CS42L43_OSC_DIV_SEL, CS42L43_OSC_DIV2_EN_MASK, },
+ };
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Disabling PLL\n");
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ regmap_multi_reg_write(cs42l43->regmap, disable_seq, ARRAY_SIZE(disable_seq));
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, 0);
+
+ cs42l43->sdw_pll_active = false;
+
+ return 0;
+}
+
+static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ mutex_lock(&cs42l43->pll_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->refclk_src == CS42L43_SYSCLK_MCLK) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(priv->dev, "Failed to enable MCLK: %d\n", ret);
+ break;
+ }
+ }
+
+ ret = cs42l43_enable_pll(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = cs42l43_disable_pll(priv);
+
+ if (priv->refclk_src == CS42L43_SYSCLK_MCLK)
+ clk_disable_unprepare(priv->mclk);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static int cs42l43_dapm_wait_completion(struct completion *pmu, struct completion *pmd,
+ int event, int timeout_ms)
+{
+ unsigned long time_left;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ reinit_completion(pmu);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ reinit_completion(pmd);
+ return 0;
+ case SND_SOC_DAPM_POST_PMU:
+ time_left = wait_for_completion_timeout(pmu, msecs_to_jiffies(timeout_ms));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ time_left = wait_for_completion_timeout(pmd, msecs_to_jiffies(timeout_ms));
+ break;
+ default:
+ return 0;
+ }
+
+ if (!time_left)
+ return -ETIMEDOUT;
+ else
+ return 0;
+}
+
+static int cs42l43_spkr_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ return cs42l43_dapm_wait_completion(&priv->spkr_startup,
+ &priv->spkr_shutdown, event,
+ CS42L43_SPK_TIMEOUT_MS);
+}
+
+static int cs42l43_spkl_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ return cs42l43_dapm_wait_completion(&priv->spkl_startup,
+ &priv->spkl_shutdown, event,
+ CS42L43_SPK_TIMEOUT_MS);
+}
+
+static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val = 0;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = mask;
+ fallthrough;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_ena &= ~mask;
+ priv->hp_ena |= val;
+
+ ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
+ &priv->hp_shutdown, event,
+ CS42L43_HP_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ if (!priv->load_detect_running && !priv->hp_ilimited)
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ mask, val);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->load_detect_running || priv->hp_ilimited)
+ break;
+
+ ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
+ &priv->hp_shutdown, event,
+ CS42L43_HP_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_mic_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int reg, ramp, mute;
+ unsigned int *val;
+ int ret;
+
+ switch (w->shift) {
+ case CS42L43_ADC1_EN_SHIFT:
+ case CS42L43_PDM1_DIN_L_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2;
+ ramp = CS42L43_DECIM1_VD_RAMP_MASK;
+ mute = CS42L43_DECIM1_MUTE_MASK;
+ val = &priv->decim_cache[0];
+ break;
+ case CS42L43_ADC2_EN_SHIFT:
+ case CS42L43_PDM1_DIN_R_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2;
+ ramp = CS42L43_DECIM2_VD_RAMP_MASK;
+ mute = CS42L43_DECIM2_MUTE_MASK;
+ val = &priv->decim_cache[1];
+ break;
+ case CS42L43_PDM2_DIN_L_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4;
+ ramp = CS42L43_DECIM3_VD_RAMP_MASK;
+ mute = CS42L43_DECIM3_MUTE_MASK;
+ val = &priv->decim_cache[2];
+ break;
+ case CS42L43_PDM2_DIN_R_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4;
+ ramp = CS42L43_DECIM4_VD_RAMP_MASK;
+ mute = CS42L43_DECIM4_MUTE_MASK;
+ val = &priv->decim_cache[3];
+ break;
+ default:
+ dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regmap_read(cs42l43->regmap, reg, val);
+ if (ret) {
+ dev_err(priv->dev,
+ "Failed to cache decimator settings: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_update_bits(cs42l43->regmap, reg, mute | ramp, mute);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs42l43->regmap, reg, mute | ramp, *val);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val = 0;
+ int ret;
+
+ ret = cs42l43_mic_ev(w, kcontrol, event);
+ if (ret)
+ return ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = mask;
+ fallthrough;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->adc_ena &= ~mask;
+ priv->adc_ena |= val;
+
+ if (!priv->load_detect_running)
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ mask, val);
+ fallthrough;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs42l43_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, cs42l43_pll_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("ADC1_IN1_P"),
+ SND_SOC_DAPM_INPUT("ADC1_IN1_N"),
+ SND_SOC_DAPM_INPUT("ADC1_IN2_P"),
+ SND_SOC_DAPM_INPUT("ADC1_IN2_N"),
+ SND_SOC_DAPM_INPUT("ADC2_IN_P"),
+ SND_SOC_DAPM_INPUT("ADC2_IN_N"),
+
+ SND_SOC_DAPM_INPUT("PDM1_DIN"),
+ SND_SOC_DAPM_INPUT("PDM2_DIN"),
+
+ SND_SOC_DAPM_MUX("ADC1 Input", SND_SOC_NOPM, 0, 0, &cs42l43_adc1_input_ctl),
+
+ SND_SOC_DAPM_PGA_E("ADC1", SND_SOC_NOPM, CS42L43_ADC1_EN_SHIFT, 0, NULL, 0,
+ cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("ADC2", SND_SOC_NOPM, CS42L43_ADC2_EN_SHIFT, 0, NULL, 0,
+ cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA_E("PDM1L", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_L_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM1R", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_R_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM2L", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_L_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM2R", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_R_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0,
+ &cs42l43_dec_mode_ctl[0]),
+ SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0,
+ &cs42l43_dec_mode_ctl[1]),
+
+ SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("FSYNC", 0, CS42L43_ASP_CTRL, CS42L43_ASP_FSYNC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("BCLK", 1, CS42L43_ASP_CTRL, CS42L43_ASP_BCLK_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH5_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH6_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 1,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX3", NULL, 2,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX4", NULL, 3,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX5", NULL, 4,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH5_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX6", NULL, 5,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH6_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP1TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX3", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX4", NULL, 3, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP2TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP3TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP3TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP4TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP5RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP5RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP6RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP6RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP7RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP7RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-amp", 0, 0),
+
+ SND_SOC_DAPM_PGA_E("AMP1", CS42L43_BLOCK_EN10, CS42L43_AMP1_EN_SHIFT, 0, NULL, 0,
+ cs42l43_spkl_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("AMP2", CS42L43_BLOCK_EN10, CS42L43_AMP2_EN_SHIFT, 0, NULL, 0,
+ cs42l43_spkr_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("AMP1_OUT_P"),
+ SND_SOC_DAPM_OUTPUT("AMP1_OUT_N"),
+ SND_SOC_DAPM_OUTPUT("AMP2_OUT_P"),
+ SND_SOC_DAPM_OUTPUT("AMP2_OUT_N"),
+
+ SND_SOC_DAPM_PGA("SPDIF", CS42L43_BLOCK_EN11, CS42L43_SPDIF_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPDIF_TX"),
+
+ SND_SOC_DAPM_PGA_E("HP", SND_SOC_NOPM, CS42L43_HP_EN_SHIFT, 0, NULL, 0,
+ cs42l43_hp_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("AMP3_OUT"),
+ SND_SOC_DAPM_OUTPUT("AMP4_OUT"),
+
+ SND_SOC_DAPM_SIGGEN("Tone"),
+ SND_SOC_DAPM_SUPPLY("Tone Generator", CS42L43_BLOCK_EN9, CS42L43_TONE_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 1", CS42L43_TONE_CH1_CTRL,
+ CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 2", CS42L43_TONE_CH2_CTRL,
+ CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0),
+
+ SND_SOC_DAPM_SUPPLY("ISRC1", CS42L43_BLOCK_EN5, CS42L43_ISRC1_BANK_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISRC2", CS42L43_BLOCK_EN5, CS42L43_ISRC2_BANK_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ISRC1INT2", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1INT1", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1DEC2", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1DEC1", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ISRC2INT2", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2INT1", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2DEC2", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2DEC1", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ASRC_INT", CS42L43_BLOCK_EN4,
+ CS42L43_ASRC_INT_BANK_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASRC_DEC", CS42L43_BLOCK_EN4,
+ CS42L43_ASRC_DEC_BANK_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ASRC_INT1", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT2", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT3", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT3_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT4", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT4_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC1", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC2", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC3", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC3_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC4", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC4_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("EQ Clock", CS42L43_BLOCK_EN7, CS42L43_EQ_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("EQ", CS42L43_START_EQZ0, CS42L43_START_FILTER_SHIFT,
+ 0, NULL, 0, cs42l43_eq_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT,
+ 0, NULL, 0),
+ CS42L43_DAPM_MUX("ASPTX1", asptx1),
+ CS42L43_DAPM_MUX("ASPTX2", asptx2),
+ CS42L43_DAPM_MUX("ASPTX3", asptx3),
+ CS42L43_DAPM_MUX("ASPTX4", asptx4),
+ CS42L43_DAPM_MUX("ASPTX5", asptx5),
+ CS42L43_DAPM_MUX("ASPTX6", asptx6),
+
+ CS42L43_DAPM_MUX("DP1TX1", dp1tx1),
+ CS42L43_DAPM_MUX("DP1TX2", dp1tx2),
+ CS42L43_DAPM_MUX("DP1TX3", dp1tx3),
+ CS42L43_DAPM_MUX("DP1TX4", dp1tx4),
+ CS42L43_DAPM_MUX("DP2TX1", dp2tx1),
+ CS42L43_DAPM_MUX("DP2TX2", dp2tx2),
+ CS42L43_DAPM_MUX("DP3TX1", dp3tx1),
+ CS42L43_DAPM_MUX("DP3TX2", dp3tx2),
+ CS42L43_DAPM_MUX("DP4TX1", dp4tx1),
+ CS42L43_DAPM_MUX("DP4TX2", dp4tx2),
+
+ CS42L43_DAPM_MUX("ASRC INT1", asrcint1),
+ CS42L43_DAPM_MUX("ASRC INT2", asrcint2),
+ CS42L43_DAPM_MUX("ASRC INT3", asrcint3),
+ CS42L43_DAPM_MUX("ASRC INT4", asrcint4),
+ CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1),
+ CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2),
+ CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3),
+ CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4),
+
+ CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1),
+ CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2),
+ CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1),
+ CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2),
+ CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1),
+ CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2),
+ CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1),
+ CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2),
+
+ CS42L43_DAPM_MUX("SPDIF1", spdif1),
+ CS42L43_DAPM_MUX("SPDIF2", spdif2),
+
+ CS42L43_DAPM_MIXER("EQ1", eq1),
+ CS42L43_DAPM_MIXER("EQ2", eq2),
+
+ CS42L43_DAPM_MIXER("Speaker L", amp1),
+ CS42L43_DAPM_MIXER("Speaker R", amp2),
+
+ CS42L43_DAPM_MIXER("Headphone L", amp3),
+ CS42L43_DAPM_MIXER("Headphone R", amp4),
+};
+
+static const struct snd_soc_dapm_route cs42l43_routes[] = {
+ { "ADC1_IN1_P", NULL, "PLL" },
+ { "ADC1_IN1_N", NULL, "PLL" },
+ { "ADC1_IN2_P", NULL, "PLL" },
+ { "ADC1_IN2_N", NULL, "PLL" },
+ { "ADC2_IN_P", NULL, "PLL" },
+ { "ADC2_IN_N", NULL, "PLL" },
+ { "PDM1_DIN", NULL, "PLL" },
+ { "PDM2_DIN", NULL, "PLL" },
+ { "AMP1_OUT_P", NULL, "PLL" },
+ { "AMP1_OUT_N", NULL, "PLL" },
+ { "AMP2_OUT_P", NULL, "PLL" },
+ { "AMP2_OUT_N", NULL, "PLL" },
+ { "SPDIF_TX", NULL, "PLL" },
+ { "HP", NULL, "PLL" },
+ { "AMP3_OUT", NULL, "PLL" },
+ { "AMP4_OUT", NULL, "PLL" },
+ { "Tone 1", NULL, "PLL" },
+ { "Tone 2", NULL, "PLL" },
+ { "ASP Playback", NULL, "PLL" },
+ { "ASP Capture", NULL, "PLL" },
+ { "DP1 Capture", NULL, "PLL" },
+ { "DP2 Capture", NULL, "PLL" },
+ { "DP3 Capture", NULL, "PLL" },
+ { "DP4 Capture", NULL, "PLL" },
+ { "DP5 Playback", NULL, "PLL" },
+ { "DP6 Playback", NULL, "PLL" },
+ { "DP7 Playback", NULL, "PLL" },
+
+ { "ADC1 Input", "IN1", "ADC1_IN1_P" },
+ { "ADC1 Input", "IN1", "ADC1_IN1_N" },
+ { "ADC1 Input", "IN2", "ADC1_IN2_P" },
+ { "ADC1 Input", "IN2", "ADC1_IN2_N" },
+
+ { "ADC1", NULL, "ADC1 Input" },
+ { "ADC2", NULL, "ADC2_IN_P" },
+ { "ADC2", NULL, "ADC2_IN_N" },
+
+ { "PDM1L", NULL, "PDM1_DIN" },
+ { "PDM1R", NULL, "PDM1_DIN" },
+ { "PDM2L", NULL, "PDM2_DIN" },
+ { "PDM2R", NULL, "PDM2_DIN" },
+
+ { "Decimator 1 Mode", "PDM", "PDM1L" },
+ { "Decimator 1 Mode", "ADC", "ADC1" },
+ { "Decimator 2 Mode", "PDM", "PDM1R" },
+ { "Decimator 2 Mode", "ADC", "ADC2" },
+
+ { "Decimator 1", NULL, "Decimator 1 Mode" },
+ { "Decimator 2", NULL, "Decimator 2 Mode" },
+ { "Decimator 3", NULL, "PDM2L" },
+ { "Decimator 4", NULL, "PDM2R" },
+
+ { "ASP Capture", NULL, "ASPTX1" },
+ { "ASP Capture", NULL, "ASPTX2" },
+ { "ASP Capture", NULL, "ASPTX3" },
+ { "ASP Capture", NULL, "ASPTX4" },
+ { "ASP Capture", NULL, "ASPTX5" },
+ { "ASP Capture", NULL, "ASPTX6" },
+ { "ASPTX1", NULL, "BCLK" },
+ { "ASPTX2", NULL, "BCLK" },
+ { "ASPTX3", NULL, "BCLK" },
+ { "ASPTX4", NULL, "BCLK" },
+ { "ASPTX5", NULL, "BCLK" },
+ { "ASPTX6", NULL, "BCLK" },
+
+ { "ASPRX1", NULL, "ASP Playback" },
+ { "ASPRX2", NULL, "ASP Playback" },
+ { "ASPRX3", NULL, "ASP Playback" },
+ { "ASPRX4", NULL, "ASP Playback" },
+ { "ASPRX5", NULL, "ASP Playback" },
+ { "ASPRX6", NULL, "ASP Playback" },
+ { "ASPRX1", NULL, "BCLK" },
+ { "ASPRX2", NULL, "BCLK" },
+ { "ASPRX3", NULL, "BCLK" },
+ { "ASPRX4", NULL, "BCLK" },
+ { "ASPRX5", NULL, "BCLK" },
+ { "ASPRX6", NULL, "BCLK" },
+
+ { "DP1 Capture", NULL, "DP1TX1" },
+ { "DP1 Capture", NULL, "DP1TX2" },
+ { "DP1 Capture", NULL, "DP1TX3" },
+ { "DP1 Capture", NULL, "DP1TX4" },
+
+ { "DP2 Capture", NULL, "DP2TX1" },
+ { "DP2 Capture", NULL, "DP2TX2" },
+
+ { "DP3 Capture", NULL, "DP3TX1" },
+ { "DP3 Capture", NULL, "DP3TX2" },
+
+ { "DP4 Capture", NULL, "DP4TX1" },
+ { "DP4 Capture", NULL, "DP4TX2" },
+
+ { "DP5RX1", NULL, "DP5 Playback" },
+ { "DP5RX2", NULL, "DP5 Playback" },
+
+ { "DP6RX1", NULL, "DP6 Playback" },
+ { "DP6RX2", NULL, "DP6 Playback" },
+
+ { "DP7RX1", NULL, "DP7 Playback" },
+ { "DP7RX2", NULL, "DP7 Playback" },
+
+ { "AMP1", NULL, "vdd-amp" },
+ { "AMP2", NULL, "vdd-amp" },
+
+ { "AMP1_OUT_P", NULL, "AMP1" },
+ { "AMP1_OUT_N", NULL, "AMP1" },
+ { "AMP2_OUT_P", NULL, "AMP2" },
+ { "AMP2_OUT_N", NULL, "AMP2" },
+
+ { "SPDIF_TX", NULL, "SPDIF" },
+
+ { "AMP3_OUT", NULL, "HP" },
+ { "AMP4_OUT", NULL, "HP" },
+
+ { "Tone 1", NULL, "Tone" },
+ { "Tone 1", NULL, "Tone Generator" },
+ { "Tone 2", NULL, "Tone" },
+ { "Tone 2", NULL, "Tone Generator" },
+
+ { "ISRC1INT2", NULL, "ISRC1" },
+ { "ISRC1INT1", NULL, "ISRC1" },
+ { "ISRC1DEC2", NULL, "ISRC1" },
+ { "ISRC1DEC1", NULL, "ISRC1" },
+
+ { "ISRC2INT2", NULL, "ISRC2" },
+ { "ISRC2INT1", NULL, "ISRC2" },
+ { "ISRC2DEC2", NULL, "ISRC2" },
+ { "ISRC2DEC1", NULL, "ISRC2" },
+
+ { "ASRC_INT1", NULL, "ASRC_INT" },
+ { "ASRC_INT2", NULL, "ASRC_INT" },
+ { "ASRC_INT3", NULL, "ASRC_INT" },
+ { "ASRC_INT4", NULL, "ASRC_INT" },
+ { "ASRC_DEC1", NULL, "ASRC_DEC" },
+ { "ASRC_DEC2", NULL, "ASRC_DEC" },
+ { "ASRC_DEC3", NULL, "ASRC_DEC" },
+ { "ASRC_DEC4", NULL, "ASRC_DEC" },
+
+ { "EQ", NULL, "EQ Clock" },
+
+ CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"),
+ CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"),
+ CS42L43_MUX_ROUTES("ASPTX3", "ASPTX3"),
+ CS42L43_MUX_ROUTES("ASPTX4", "ASPTX4"),
+ CS42L43_MUX_ROUTES("ASPTX5", "ASPTX5"),
+ CS42L43_MUX_ROUTES("ASPTX6", "ASPTX6"),
+
+ CS42L43_MUX_ROUTES("DP1TX1", "DP1TX1"),
+ CS42L43_MUX_ROUTES("DP1TX2", "DP1TX2"),
+ CS42L43_MUX_ROUTES("DP1TX3", "DP1TX3"),
+ CS42L43_MUX_ROUTES("DP1TX4", "DP1TX4"),
+ CS42L43_MUX_ROUTES("DP2TX1", "DP2TX1"),
+ CS42L43_MUX_ROUTES("DP2TX2", "DP2TX2"),
+ CS42L43_MUX_ROUTES("DP3TX1", "DP3TX1"),
+ CS42L43_MUX_ROUTES("DP3TX2", "DP3TX2"),
+ CS42L43_MUX_ROUTES("DP4TX1", "DP4TX1"),
+ CS42L43_MUX_ROUTES("DP4TX2", "DP4TX2"),
+
+ CS42L43_MUX_ROUTES("ASRC INT1", "ASRC_INT1"),
+ CS42L43_MUX_ROUTES("ASRC INT2", "ASRC_INT2"),
+ CS42L43_MUX_ROUTES("ASRC INT3", "ASRC_INT3"),
+ CS42L43_MUX_ROUTES("ASRC INT4", "ASRC_INT4"),
+ CS42L43_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"),
+ CS42L43_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"),
+ CS42L43_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"),
+ CS42L43_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"),
+
+ CS42L43_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS42L43_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS42L43_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS42L43_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS42L43_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS42L43_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ CS42L43_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS42L43_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS42L43_MUX_ROUTES("SPDIF1", "SPDIF"),
+ CS42L43_MUX_ROUTES("SPDIF2", "SPDIF"),
+
+ CS42L43_MIXER_ROUTES("EQ1", "EQ"),
+ CS42L43_MIXER_ROUTES("EQ2", "EQ"),
+
+ CS42L43_MIXER_ROUTES("Speaker L", "AMP1"),
+ CS42L43_MIXER_ROUTES("Speaker R", "AMP2"),
+
+ CS42L43_MIXER_ROUTES("Headphone L", "HP"),
+ CS42L43_MIXER_ROUTES("Headphone R", "HP"),
+};
+
+static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int src, unsigned int freq, int dir)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ mutex_lock(&cs42l43->pll_lock);
+ ret = cs42l43_set_pll(priv, src, freq);
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static int cs42l43_component_probe(struct snd_soc_component *component)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ snd_soc_component_init_regmap(component, cs42l43->regmap);
+
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots,
+ ARRAY_SIZE(priv->tx_slots));
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots,
+ ARRAY_SIZE(priv->rx_slots));
+
+ priv->component = component;
+ priv->constraint = cs42l43_constraint;
+
+ return 0;
+}
+
+static void cs42l43_component_remove(struct snd_soc_component *component)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ cs42l43_set_jack(priv->component, NULL, NULL);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ priv->component = NULL;
+}
+
+static const struct snd_soc_component_driver cs42l43_component_drv = {
+ .name = "cs42l43-codec",
+
+ .probe = cs42l43_component_probe,
+ .remove = cs42l43_component_remove,
+ .set_sysclk = cs42l43_set_sysclk,
+ .set_jack = cs42l43_set_jack,
+
+ .endianness = 1,
+
+ .controls = cs42l43_controls,
+ .num_controls = ARRAY_SIZE(cs42l43_controls),
+ .dapm_widgets = cs42l43_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l43_widgets),
+ .dapm_routes = cs42l43_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42l43_routes),
+};
+
+struct cs42l43_irq {
+ unsigned int irq;
+ const char *name;
+ irq_handler_t handler;
+};
+
+static const struct cs42l43_irq cs42l43_irqs[] = {
+ { CS42L43_PLL_LOST_LOCK, "pll lost lock", cs42l43_pll_lost_lock },
+ { CS42L43_PLL_READY, "pll ready", cs42l43_pll_ready },
+ { CS42L43_HP_STARTUP_DONE, "hp startup", cs42l43_hp_startup },
+ { CS42L43_HP_SHUTDOWN_DONE, "hp shutdown", cs42l43_hp_shutdown },
+ { CS42L43_HSDET_DONE, "type detect", cs42l43_type_detect },
+ { CS42L43_TIPSENSE_UNPLUG_PDET, "tip sense unplug", cs42l43_tip_sense },
+ { CS42L43_TIPSENSE_PLUG_PDET, "tip sense plug", cs42l43_tip_sense },
+ { CS42L43_DC_DETECT1_TRUE, "button press", cs42l43_button_press },
+ { CS42L43_DC_DETECT1_FALSE, "button release", cs42l43_button_release },
+ { CS42L43_HSBIAS_CLAMPED, "hsbias detect clamp", cs42l43_bias_detect_clamp },
+ { CS42L43_AMP2_CLK_STOP_FAULT, "spkr clock stop", cs42l43_spkr_clock_stop },
+ { CS42L43_AMP1_CLK_STOP_FAULT, "spkl clock stop", cs42l43_spkl_clock_stop },
+ { CS42L43_AMP2_VDDSPK_FAULT, "spkr brown out", cs42l43_spkr_brown_out },
+ { CS42L43_AMP1_VDDSPK_FAULT, "spkl brown out", cs42l43_spkl_brown_out },
+ { CS42L43_AMP2_SHUTDOWN_DONE, "spkr shutdown", cs42l43_spkr_shutdown },
+ { CS42L43_AMP1_SHUTDOWN_DONE, "spkl shutdown", cs42l43_spkl_shutdown },
+ { CS42L43_AMP2_STARTUP_DONE, "spkr startup", cs42l43_spkr_startup },
+ { CS42L43_AMP1_STARTUP_DONE, "spkl startup", cs42l43_spkl_startup },
+ { CS42L43_AMP2_THERM_SHDN, "spkr thermal shutdown", cs42l43_spkr_therm_shutdown },
+ { CS42L43_AMP1_THERM_SHDN, "spkl thermal shutdown", cs42l43_spkl_therm_shutdown },
+ { CS42L43_AMP2_THERM_WARN, "spkr thermal warning", cs42l43_spkr_therm_warm },
+ { CS42L43_AMP1_THERM_WARN, "spkl thermal warning", cs42l43_spkl_therm_warm },
+ { CS42L43_AMP2_SCDET, "spkr short circuit", cs42l43_spkr_sc_detect },
+ { CS42L43_AMP1_SCDET, "spkl short circuit", cs42l43_spkl_sc_detect },
+ { CS42L43_HP_ILIMIT, "hp ilimit", cs42l43_hp_ilimit },
+ { CS42L43_HP_LOADDET_DONE, "load detect done", cs42l43_load_detect },
+};
+
+static int cs42l43_request_irq(struct cs42l43_codec *priv,
+ const char * const name, unsigned int irq,
+ irq_handler_t handler, unsigned long flags)
+{
+ int ret;
+
+ ret = irq_create_mapping(priv->dom, irq);
+ if (ret < 0)
+ return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name);
+
+ dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name);
+
+ ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler,
+ IRQF_ONESHOT | flags, name, priv);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name);
+
+ return 0;
+}
+
+static void cs42l43_disable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ disable_irq(ret);
+}
+
+static void cs42l43_enable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ enable_irq(ret);
+}
+
+static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter,
+ const char * const open_name, unsigned int *open_irq,
+ const char * const close_name, unsigned int *close_irq,
+ irq_handler_t handler)
+{
+ int ret;
+
+ switch (shutter) {
+ case 0x1:
+ dev_warn(priv->dev, "Manual shutters, notifications not available\n");
+ return 0;
+ case 0x2:
+ *open_irq = CS42L43_GPIO1_RISE;
+ *close_irq = CS42L43_GPIO1_FALL;
+ break;
+ case 0x4:
+ *open_irq = CS42L43_GPIO2_RISE;
+ *close_irq = CS42L43_GPIO2_FALL;
+ break;
+ case 0x8:
+ *open_irq = CS42L43_GPIO3_RISE;
+ *close_irq = CS42L43_GPIO3_FALL;
+ break;
+ default:
+ return 0;
+ }
+
+ ret = cs42l43_request_irq(priv, close_name, *close_irq, handler, IRQF_SHARED);
+ if (ret)
+ return ret;
+
+ return cs42l43_request_irq(priv, open_name, *open_irq, handler, IRQF_SHARED);
+}
+
+static int cs42l43_codec_probe(struct platform_device *pdev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
+ struct cs42l43_codec *priv;
+ unsigned int val;
+ int i, ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ priv->core = cs42l43;
+
+ priv->dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
+ if (!priv->dom)
+ return -EPROBE_DEFER;
+
+ platform_set_drvdata(pdev, priv);
+
+ mutex_init(&priv->jack_lock);
+ mutex_init(&priv->spk_vu_lock);
+
+ init_completion(&priv->hp_startup);
+ init_completion(&priv->hp_shutdown);
+ init_completion(&priv->spkr_shutdown);
+ init_completion(&priv->spkl_shutdown);
+ init_completion(&priv->spkr_startup);
+ init_completion(&priv->spkl_startup);
+ init_completion(&priv->pll_ready);
+ init_completion(&priv->type_detect);
+ init_completion(&priv->load_detect);
+
+ INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work);
+ INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout);
+ INIT_DELAYED_WORK(&priv->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_work);
+
+ pm_runtime_set_autosuspend_delay(priv->dev, 100);
+ pm_runtime_use_autosuspend(priv->dev);
+ pm_runtime_set_active(priv->dev);
+ pm_runtime_get_noresume(priv->dev);
+
+ ret = devm_pm_runtime_enable(priv->dev);
+ if (ret)
+ goto err_pm;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
+ ret = cs42l43_request_irq(priv, cs42l43_irqs[i].name,
+ cs42l43_irqs[i].irq,
+ cs42l43_irqs[i].handler, 0);
+ if (ret)
+ goto err_pm;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check shutter source: %d\n", ret);
+ goto err_pm;
+ }
+
+ ret = cs42l43_shutter_irq(priv, val & CS42L43_MIC_SHUTTER_CFG_MASK,
+ "mic shutter open", &priv->shutter_irqs[0],
+ "mic shutter close", &priv->shutter_irqs[1],
+ cs42l43_mic_shutter);
+ if (ret)
+ goto err_pm;
+
+ ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
+ CS42L43_SPK_SHUTTER_CFG_SHIFT,
+ "spk shutter open", &priv->shutter_irqs[2],
+ "spk shutter close", &priv->shutter_irqs[3],
+ cs42l43_spk_shutter);
+ if (ret)
+ goto err_pm;
+
+ // Don't use devm as we need to get against the MFD device
+ priv->mclk = clk_get_optional(cs42l43->dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ ret = PTR_ERR(priv->mclk);
+ dev_err_probe(priv->dev, ret, "Failed to get mclk\n");
+ goto err_pm;
+ }
+
+ ret = devm_snd_soc_register_component(priv->dev, &cs42l43_component_drv,
+ cs42l43_dais, ARRAY_SIZE(cs42l43_dais));
+ if (ret) {
+ dev_err_probe(priv->dev, ret, "Failed to register component\n");
+ goto err_clk;
+ }
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return 0;
+
+err_clk:
+ clk_put(priv->mclk);
+err_pm:
+ pm_runtime_put_sync(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_codec_remove(struct platform_device *pdev)
+{
+ struct cs42l43_codec *priv = platform_get_drvdata(pdev);
+
+ clk_put(priv->mclk);
+}
+
+static int cs42l43_codec_runtime_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+
+ dev_dbg(priv->dev, "Runtime resume\n");
+
+ // Toggle the speaker volume update incase the speaker volume was synced
+ cs42l43_spk_vu_sync(priv);
+
+ return 0;
+}
+
+static int cs42l43_codec_suspend(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(priv->dev, "System suspend\n");
+
+ priv->suspend_jack_debounce = true;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_disable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_disable_irq(priv, priv->shutter_irqs[i]);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ cs42l43_clear_jack(priv);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs42l43_codec_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_enable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_enable_irq(priv, priv->shutter_irqs[i]);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l43_codec_pm_ops = {
+ RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
+};
+
+static const struct platform_device_id cs42l43_codec_id_table[] = {
+ { "cs42l43-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table);
+
+static struct platform_driver cs42l43_codec_driver = {
+ .driver = {
+ .name = "cs42l43-codec",
+ .pm = pm_ptr(&cs42l43_codec_pm_ops),
+ },
+
+ .probe = cs42l43_codec_probe,
+ .remove = cs42l43_codec_remove,
+ .id_table = cs42l43_codec_id_table,
+};
+module_platform_driver(cs42l43_codec_driver);
+
+MODULE_IMPORT_NS("SND_SOC_CS42L43");
+
+MODULE_DESCRIPTION("CS42L43 CODEC Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
new file mode 100644
index 000000000000..b2fa2cd1d99f
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS42L43 CODEC driver internal data
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS42L43_ASOC_INT_H
+#define CS42L43_ASOC_INT_H
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+
+#define CS42L43_INTERNAL_SYSCLK 24576000
+#define CS42L43_DEFAULT_SLOTS 0x3F
+
+#define CS42L43_PLL_TIMEOUT_MS 200
+#define CS42L43_SPK_TIMEOUT_MS 100
+#define CS42L43_HP_TIMEOUT_MS 2000
+#define CS42L43_LOAD_TIMEOUT_MS 1000
+
+#define CS42L43_HP_ILIMIT_BACKOFF_MS 1000
+#define CS42L43_HP_ILIMIT_DECAY_MS 300
+#define CS42L43_HP_ILIMIT_MAX_COUNT 4
+
+#define CS42L43_ASP_MAX_CHANNELS 6
+#define CS42L43_N_EQ_COEFFS 15
+
+#define CS42L43_N_BUTTONS 6
+
+struct clk;
+struct device;
+
+struct snd_soc_component;
+struct snd_soc_jack;
+
+struct cs42l43;
+
+struct cs42l43_codec {
+ struct device *dev;
+ struct cs42l43 *core;
+ struct snd_soc_component *component;
+ struct irq_domain *dom;
+ unsigned int shutter_irqs[4];
+
+ struct clk *mclk;
+
+ int n_slots;
+ int slot_width;
+ int tx_slots[CS42L43_ASP_MAX_CHANNELS];
+ int rx_slots[CS42L43_ASP_MAX_CHANNELS];
+ struct snd_pcm_hw_constraint_list constraint;
+
+ u32 eq_coeffs[CS42L43_N_EQ_COEFFS];
+
+ unsigned int refclk_src;
+ unsigned int refclk_freq;
+ struct completion pll_ready;
+
+ unsigned int decim_cache[4];
+ unsigned int adc_ena;
+ unsigned int hp_ena;
+
+ struct completion hp_startup;
+ struct completion hp_shutdown;
+ struct completion spkr_shutdown;
+ struct completion spkl_shutdown;
+ struct completion spkr_startup;
+ struct completion spkl_startup;
+ // Lock to ensure speaker VU updates don't clash
+ struct mutex spk_vu_lock;
+
+ // Lock for all jack detect operations
+ struct mutex jack_lock;
+ struct snd_soc_jack *jack_hp;
+
+ bool use_ring_sense;
+ unsigned int tip_debounce_ms;
+ unsigned int tip_fall_db_ms;
+ unsigned int tip_rise_db_ms;
+ unsigned int bias_low;
+ unsigned int bias_sense_ua;
+ unsigned int bias_ramp_ms;
+ unsigned int detect_us;
+ unsigned int buttons[CS42L43_N_BUTTONS];
+
+ struct delayed_work tip_sense_work;
+ struct delayed_work bias_sense_timeout;
+ struct completion type_detect;
+ struct completion load_detect;
+
+ bool load_detect_running;
+ bool button_detect_running;
+ bool jack_present;
+ int jack_override;
+ bool suspend_jack_debounce;
+
+ struct delayed_work hp_ilimit_clear_work;
+ bool hp_ilimited;
+ int hp_ilimit_count;
+
+ struct snd_kcontrol *kctl[5];
+};
+
+#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW)
+
+int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction);
+
+#else
+
+static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return -EINVAL;
+}
+
+#define cs42l43_sdw_remove_peripheral NULL
+#define cs42l43_sdw_set_stream NULL
+
+#endif
+
+int cs42l43_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d);
+void cs42l43_bias_sense_timeout(struct work_struct *work);
+void cs42l43_clear_jack(struct cs42l43_codec *priv);
+void cs42l43_tip_sense_work(struct work_struct *work);
+irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data);
+irqreturn_t cs42l43_button_press(int irq, void *data);
+irqreturn_t cs42l43_button_release(int irq, void *data);
+irqreturn_t cs42l43_tip_sense(int irq, void *data);
+int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+extern const struct soc_enum cs42l43_jack_enum;
+
+#endif /* CS42L43_ASOC_INT_H */
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index 9bad478474fa..ba7e237619f2 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l56.c -- CS42L51 ALSA SoC I2C audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/i2c.h>
@@ -17,35 +13,41 @@
#include "cs42l51.h"
-static struct i2c_device_id cs42l51_i2c_id[] = {
- {"cs42l51", 0},
- {}
+static const struct i2c_device_id cs42l51_i2c_id[] = {
+ { "cs42l51" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
-static int cs42l51_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct of_device_id cs42l51_of_match[] = {
+ { .compatible = "cirrus,cs42l51", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+
+static int cs42l51_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config config;
config = cs42l51_regmap;
- config.val_bits = 8;
- config.reg_bits = 8;
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
}
-static int cs42l51_i2c_remove(struct i2c_client *i2c)
+static void cs42l51_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
+ cs42l51_remove(&i2c->dev);
}
+static const struct dev_pm_ops cs42l51_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume)
+};
+
static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
.of_match_table = cs42l51_of_match,
+ .pm = &cs42l51_pm_ops,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index b3951524339f..0519affe0ed8 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l51.c
*
@@ -7,20 +8,12 @@
*
* Based on cs4270.c - Copyright (c) Freescale Semiconductor
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* For now:
* - Only I2C is support. Not SPI
* - master mode *NOT* supported
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -29,7 +22,9 @@
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include "cs42l51.h"
@@ -39,36 +34,44 @@ enum master_slave_mode {
MODE_MASTER,
};
+static const char * const cs42l51_supply_names[] = {
+ "VL",
+ "VD",
+ "VA",
+ "VAHP",
+};
+
struct cs42l51_private {
unsigned int mclk;
+ struct clk *mclk_handle;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
};
-#define CS42L51_FORMATS ( \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned long value = snd_soc_component_read(component, CS42L51_PCM_MIXER)&3;
switch (value) {
default:
case 0:
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.enumerated.item[0] = 0;
break;
/* same value : (L+R)/2 and (R+L)/2 */
case 1:
case 2:
- ucontrol->value.integer.value[0] = 1;
+ ucontrol->value.enumerated.item[0] = 1;
break;
case 3:
- ucontrol->value.integer.value[0] = 2;
+ ucontrol->value.enumerated.item[0] = 2;
break;
}
@@ -82,10 +85,10 @@ static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned char val;
- switch (ucontrol->value.integer.value[0]) {
+ switch (ucontrol->value.enumerated.item[0]) {
default:
case 0:
val = CHAN_MIX_NORMAL;
@@ -98,7 +101,7 @@ static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
break;
}
- snd_soc_write(codec, CS42L51_PCM_MIXER, val);
+ snd_soc_component_write(component, CS42L51_PCM_MIXER, val);
return 1;
}
@@ -109,12 +112,16 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
static const char *chan_mix[] = {
"L R",
"L+R",
"R L",
};
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0);
+
static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
@@ -131,12 +138,20 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
0, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("ADC Mixer Switch",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
+ SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume",
+ CS42L51_ADCA_ATT, CS42L51_ADCB_ATT,
+ 0, 0xA0, 96, adc_att_tlv),
+ SOC_DOUBLE_R_SX_TLV("PGA Volume",
+ CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL,
+ 0, 0x1A, 30, pga_tlv),
SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
SOC_DOUBLE_TLV("Mic Boost Volume",
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
+ SOC_DOUBLE_TLV("ADC Boost Volume",
+ CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
SOC_ENUM_EXT("PCM channel mixer",
@@ -153,17 +168,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
+ snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN,
CS42L51_POWER_CTL1_PDN);
break;
default:
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
+ snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN, 0);
break;
}
@@ -193,7 +208,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
- SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
@@ -204,12 +220,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
CS42L51_POWER_CTL1, 2, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
- SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
- CS42L51_POWER_CTL1, 5, 1,
- cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
- SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
- CS42L51_POWER_CTL1, 6, 1,
- cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("Left DAC", NULL, CS42L51_POWER_CTL1, 5, 1,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("Right DAC", NULL, CS42L51_POWER_CTL1, 6, 1,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
/* analog/mic */
SND_SOC_DAPM_INPUT("AIN1L"),
@@ -237,10 +251,40 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
&cs42l51_adcr_mux_controls),
};
+static int mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(cs42l51->mclk_handle);
+ case SND_SOC_DAPM_POST_PMD:
+ /* Delay mclk shutdown to fulfill power-down sequence requirements */
+ msleep(20);
+ clk_disable_unprepare(cs42l51->mclk_handle);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
static const struct snd_soc_dapm_route cs42l51_routes[] = {
{"HPL", NULL, "Left DAC"},
{"HPR", NULL, "Right DAC"},
+ {"Right DAC", NULL, "DAC Mux"},
+ {"Left DAC", NULL, "DAC Mux"},
+
+ {"DAC Mux", "Direct PCM", "Playback"},
+ {"DAC Mux", "DSP PCM", "Playback"},
+
{"Left ADC", NULL, "Left PGA"},
{"Right ADC", NULL, "Right PGA"},
@@ -263,8 +307,8 @@ static const struct snd_soc_dapm_route cs42l51_routes[] = {
static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -273,19 +317,19 @@ static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- dev_err(codec->dev, "invalid DAI format\n");
+ dev_err(component->dev, "invalid DAI format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42l51->func = MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42l51->func = MODE_SLAVE_AUTO;
break;
default:
- dev_err(codec->dev, "Unknown master/slave configuration\n");
+ dev_err(component->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
@@ -323,11 +367,24 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
};
+/*
+ * Master mode mclk/fs ratios.
+ * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
+ * The table below provides support of following ratios:
+ * 128: SSM (%128) with div2 disabled
+ * 256: SSM (%128) with div2 enabled
+ * In both cases, if sampling rate is above 50kHz, SSM is overridden
+ * with DSM (%128) configuration
+ */
+static struct cs42l51_ratios master_ratios[] = {
+ { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
+};
+
static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
cs42l51->mclk = freq;
return 0;
@@ -337,19 +394,21 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
int ret;
unsigned int i;
unsigned int rate;
unsigned int ratio;
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
- int intf_ctl, power_ctl, fmt;
+ int intf_ctl, power_ctl, fmt, mode;
switch (cs42l51->func) {
case MODE_MASTER:
- return -EINVAL;
+ ratios = master_ratios;
+ nr_ratios = ARRAY_SIZE(master_ratios);
+ break;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
@@ -370,12 +429,12 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
if (i == nr_ratios) {
/* We did not find a matching ratio */
- dev_err(codec->dev, "could not find matching ratio\n");
+ dev_err(component->dev, "could not find matching ratio\n");
return -EINVAL;
}
- intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
- power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
+ intf_ctl = snd_soc_component_read(component, CS42L51_INTF_CTL);
+ power_ctl = snd_soc_component_read(component, CS42L51_MIC_POWER_CTL);
intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
| CS42L51_INTF_CTL_DAC_FORMAT(7));
@@ -385,7 +444,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
- power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+ mode = ratios[i].speed_mode;
+ /* Force DSM mode if sampling rate is above 50kHz */
+ if (rate > 50000)
+ mode = CS42L51_DSM_MODE;
+ power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
+ /*
+ * Auto detect mode is not applicable for master mode and has to
+ * be disabled. Otherwise SPEED[1:0] bits will be ignored.
+ */
+ power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
break;
case MODE_SLAVE:
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
@@ -418,51 +486,59 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
fmt = CS42L51_DAC_DIF_RJ24;
break;
default:
- dev_err(codec->dev, "unknown format\n");
+ dev_err(component->dev, "unknown format\n");
return -EINVAL;
}
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
break;
default:
- dev_err(codec->dev, "unknown format\n");
+ dev_err(component->dev, "unknown format\n");
return -EINVAL;
}
if (ratios[i].mclk)
power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
- ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
+ ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl);
if (ret < 0)
return ret;
- ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
+ ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, power_ctl);
if (ret < 0)
return ret;
return 0;
}
-static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int reg;
int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
- reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
+ reg = snd_soc_component_read(component, CS42L51_DAC_OUT_CTL);
if (mute)
reg |= mask;
else
reg &= ~mask;
- return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
+ return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
+}
+
+static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ /* return dai id 0, whatever the endpoint index */
+ return 0;
}
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
.set_fmt = cs42l51_set_dai_fmt,
- .digital_mute = cs42l51_dai_mute,
+ .mute_stream = cs42l51_dai_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42l51_dai = {
@@ -484,9 +560,17 @@ static struct snd_soc_dai_driver cs42l51_dai = {
.ops = &cs42l51_dai_ops,
};
-static int cs42l51_codec_probe(struct snd_soc_codec *codec)
+static int cs42l51_component_probe(struct snd_soc_component *component)
{
int ret, reg;
+ struct snd_soc_dapm_context *dapm;
+ struct cs42l51_private *cs42l51;
+
+ cs42l51 = snd_soc_component_get_drvdata(component);
+ dapm = snd_soc_component_to_dapm(component);
+
+ if (cs42l51->mclk_handle)
+ snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1);
/*
* DAC configuration
@@ -497,27 +581,129 @@ static int cs42l51_codec_probe(struct snd_soc_codec *codec)
*/
reg = CS42L51_DAC_CTL_DATA_SEL(1)
| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
- ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
+ ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg);
if (ret < 0)
return ret;
return 0;
}
-static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
- .probe = cs42l51_codec_probe,
-
- .controls = cs42l51_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l51_snd_controls),
- .dapm_widgets = cs42l51_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
- .dapm_routes = cs42l51_routes,
- .num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
+static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
+ .probe = cs42l51_component_probe,
+ .controls = cs42l51_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l51_snd_controls),
+ .dapm_widgets = cs42l51_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
+ .dapm_routes = cs42l51_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
+ .of_xlate_dai_id = cs42l51_of_xlate_dai_id,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
+static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_POWER_CTL1:
+ case CS42L51_MIC_POWER_CTL:
+ case CS42L51_INTF_CTL:
+ case CS42L51_MIC_CTL:
+ case CS42L51_ADC_CTL:
+ case CS42L51_ADC_INPUT:
+ case CS42L51_DAC_OUT_CTL:
+ case CS42L51_DAC_CTL:
+ case CS42L51_ALC_PGA_CTL:
+ case CS42L51_ALC_PGB_CTL:
+ case CS42L51_ADCA_ATT:
+ case CS42L51_ADCB_ATT:
+ case CS42L51_ADCA_VOL:
+ case CS42L51_ADCB_VOL:
+ case CS42L51_PCMA_VOL:
+ case CS42L51_PCMB_VOL:
+ case CS42L51_BEEP_FREQ:
+ case CS42L51_BEEP_VOL:
+ case CS42L51_BEEP_CONF:
+ case CS42L51_TONE_CTL:
+ case CS42L51_AOUTA_VOL:
+ case CS42L51_AOUTB_VOL:
+ case CS42L51_PCM_MIXER:
+ case CS42L51_LIMIT_THRES_DIS:
+ case CS42L51_LIMIT_REL:
+ case CS42L51_LIMIT_ATT:
+ case CS42L51_ALC_EN:
+ case CS42L51_ALC_REL:
+ case CS42L51_ALC_THRES:
+ case CS42L51_NOISE_CONF:
+ case CS42L51_CHARGE_FREQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L51_CHIP_REV_ID:
+ case CS42L51_POWER_CTL1:
+ case CS42L51_MIC_POWER_CTL:
+ case CS42L51_INTF_CTL:
+ case CS42L51_MIC_CTL:
+ case CS42L51_ADC_CTL:
+ case CS42L51_ADC_INPUT:
+ case CS42L51_DAC_OUT_CTL:
+ case CS42L51_DAC_CTL:
+ case CS42L51_ALC_PGA_CTL:
+ case CS42L51_ALC_PGB_CTL:
+ case CS42L51_ADCA_ATT:
+ case CS42L51_ADCB_ATT:
+ case CS42L51_ADCA_VOL:
+ case CS42L51_ADCB_VOL:
+ case CS42L51_PCMA_VOL:
+ case CS42L51_PCMB_VOL:
+ case CS42L51_BEEP_FREQ:
+ case CS42L51_BEEP_VOL:
+ case CS42L51_BEEP_CONF:
+ case CS42L51_TONE_CTL:
+ case CS42L51_AOUTA_VOL:
+ case CS42L51_AOUTB_VOL:
+ case CS42L51_PCM_MIXER:
+ case CS42L51_LIMIT_THRES_DIS:
+ case CS42L51_LIMIT_REL:
+ case CS42L51_LIMIT_ATT:
+ case CS42L51_ALC_EN:
+ case CS42L51_ALC_REL:
+ case CS42L51_ALC_THRES:
+ case CS42L51_NOISE_CONF:
+ case CS42L51_STATUS:
+ case CS42L51_CHARGE_FREQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
const struct regmap_config cs42l51_regmap = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .use_single_write = true,
+ .readable_reg = cs42l51_readable_reg,
+ .volatile_reg = cs42l51_volatile_reg,
+ .writeable_reg = cs42l51_writeable_reg,
.max_register = CS42L51_CHARGE_FREQ,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42l51_regmap);
@@ -525,7 +711,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
unsigned int val;
- int ret;
+ int ret, i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -536,6 +722,41 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
dev_set_drvdata(dev, cs42l51);
+ cs42l51->regmap = regmap;
+
+ cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK");
+ if (IS_ERR(cs42l51->mclk_handle))
+ return PTR_ERR(cs42l51->mclk_handle);
+
+ for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
+ cs42l51->supplies[i].supply = cs42l51_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l51->reset_gpio)) {
+ ret = PTR_ERR(cs42l51->reset_gpio);
+ goto error;
+ }
+
+ if (cs42l51->reset_gpio) {
+ dev_dbg(dev, "Release reset gpio\n");
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
+ mdelay(2);
+ }
/* Verify that we have a CS42L51 */
ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
@@ -553,19 +774,57 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
val & CS42L51_CHIP_REV_MASK);
- ret = snd_soc_register_codec(dev,
- &soc_codec_device_cs42l51, &cs42l51_dai, 1);
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_device_cs42l51, &cs42l51_dai, 1);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
error:
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
return ret;
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
-const struct of_device_id cs42l51_of_match[] = {
- { .compatible = "cirrus,cs42l51", },
- { }
-};
-MODULE_DEVICE_TABLE(of, cs42l51_of_match);
-EXPORT_SYMBOL_GPL(cs42l51_of_match);
+void cs42l51_remove(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+ int ret;
+
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
+ if (ret)
+ dev_warn(dev, "Failed to disable all regulators (%pe)\n",
+ ERR_PTR(ret));
+
+}
+EXPORT_SYMBOL_GPL(cs42l51_remove);
+
+int cs42l51_suspend(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, true);
+ regcache_mark_dirty(cs42l51->regmap);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs42l51_suspend);
+
+int cs42l51_resume(struct device *dev)
+{
+ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l51->regmap, false);
+
+ return regcache_sync(cs42l51->regmap);
+}
+EXPORT_SYMBOL_GPL(cs42l51_resume);
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
index 0ca805492ac4..125703ede113 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -1,19 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cs42l51.h
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef _CS42L51_H
#define _CS42L51_H
@@ -22,7 +13,9 @@ struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
-extern const struct of_device_id cs42l51_of_match[];
+void cs42l51_remove(struct device *dev);
+int __maybe_unused cs42l51_suspend(struct device *dev);
+int __maybe_unused cs42l51_resume(struct device *dev);
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 47b97fcefb0b..662dc1a4835b 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l52.c -- CS42L52 ALSA SoC audio driver
*
@@ -5,34 +6,28 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/of_gpio.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l52.h>
#include "cs42l52.h"
struct sp_config {
@@ -40,9 +35,27 @@ struct sp_config {
u32 srate;
};
+struct cs42l52_platform_data {
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* MICA mode selection Differential or Single-ended */
+ bool mica_diff_cfg;
+
+ /* MICB mode selection Differential or Single-ended */
+ bool micb_diff_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg73 */
+ unsigned int chgfreq;
+
+ /* Reset GPIO */
+ struct gpio_desc *reset_gpio;
+};
+
struct cs42l52_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct device *dev;
struct sp_config config;
struct cs42l52_platform_data pdata;
@@ -141,7 +154,9 @@ static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0);
static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
-static DECLARE_TLV_DB_SCALE(mix_tlv, -50, 50, 0);
+static DECLARE_TLV_DB_SCALE(pass_tlv, -6000, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(mix_tlv, -5150, 50, 0);
static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0);
@@ -355,7 +370,7 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv),
SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
- CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pga_tlv),
+ CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pass_tlv),
SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
@@ -368,7 +383,7 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
CS42L52_ADCB_VOL, 0, 0xA0, 0x78, ipd_tlv),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL,
- 0, 0x19, 0x7F, ipd_tlv),
+ 0, 0x19, 0x7F, mix_tlv),
SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
@@ -473,17 +488,17 @@ static const struct snd_kcontrol_new cs42l52_micb_controls[] = {
SOC_ENUM("MICB Select", micb_enum),
};
-static int cs42l52_add_mic_controls(struct snd_soc_codec *codec)
+static int cs42l52_add_mic_controls(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
struct cs42l52_platform_data *pdata = &cs42l52->pdata;
if (!pdata->mica_diff_cfg)
- snd_soc_add_codec_controls(codec, cs42l52_mica_controls,
+ snd_soc_add_component_controls(component, cs42l52_mica_controls,
ARRAY_SIZE(cs42l52_mica_controls));
if (!pdata->micb_diff_cfg)
- snd_soc_add_codec_controls(codec, cs42l52_micb_controls,
+ snd_soc_add_component_controls(component, cs42l52_micb_controls,
ARRAY_SIZE(cs42l52_micb_controls));
return 0;
@@ -716,13 +731,13 @@ static int cs42l52_get_clk(int mclk, int rate)
static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) {
cs42l52->sysclk = freq;
} else {
- dev_err(codec->dev, "Invalid freq parameter\n");
+ dev_err(component->dev, "Invalid freq parameter\n");
return -EINVAL;
}
return 0;
@@ -730,15 +745,15 @@ static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
u8 iface = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = CS42L52_IFACE_CTL1_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = CS42L52_IFACE_CTL1_SLAVE;
break;
default:
@@ -783,21 +798,21 @@ static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
cs42l52->config.format = iface;
- snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format);
+ snd_soc_component_write(component, CS42L52_IFACE_CTL1, cs42l52->config.format);
return 0;
}
-static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l52_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute)
- snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
CS42L52_PB_CTL1_MUTE_MASK,
CS42L52_PB_CTL1_MUTE);
else
- snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
CS42L52_PB_CTL1_MUTE_MASK,
CS42L52_PB_CTL1_UNMUTE);
@@ -808,8 +823,8 @@ static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
u32 clk = 0;
int index;
@@ -823,36 +838,37 @@ static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
(clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
clk_map_table[index].mclkdiv2;
- snd_soc_write(codec, CS42L52_CLK_CTL, clk);
+ snd_soc_component_write(component, CS42L52_CLK_CTL, clk);
} else {
- dev_err(codec->dev, "can't get correct mclk\n");
+ dev_err(component->dev, "can't get correct mclk\n");
return -EINVAL;
}
return 0;
}
-static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l52_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, CS42L52_PWRCTL1,
+ snd_soc_component_update_bits(component, CS42L52_PWRCTL1,
CS42L52_PWRCTL1_PDN_CODEC, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l52->regmap, false);
regcache_sync(cs42l52->regmap);
}
- snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
regcache_cache_only(cs42l52->regmap, true);
break;
}
@@ -869,9 +885,10 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops cs42l52_ops = {
.hw_params = cs42l52_pcm_hw_params,
- .digital_mute = cs42l52_digital_mute,
+ .mute_stream = cs42l52_mute,
.set_fmt = cs42l52_set_fmt,
.set_sysclk = cs42l52_set_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42l52_dai = {
@@ -902,8 +919,8 @@ static void cs42l52_beep_work(struct work_struct *work)
{
struct cs42l52_private *cs42l52 =
container_of(work, struct cs42l52_private, beep_work);
- struct snd_soc_codec *codec = cs42l52->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = cs42l52->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int i;
int val = 0;
int best = 0;
@@ -915,18 +932,18 @@ static void cs42l52_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n",
beep_rates[best], cs42l52->beep_rate);
val = (best << CS42L52_BEEP_RATE_SHIFT);
snd_soc_dapm_enable_pin(dapm, "Beep");
} else {
- dev_dbg(codec->dev, "Disabling beep\n");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, CS42L52_BEEP_FREQ,
+ snd_soc_component_update_bits(component, CS42L52_BEEP_FREQ,
CS42L52_BEEP_RATE_MASK, val);
snd_soc_dapm_sync(dapm);
@@ -938,15 +955,16 @@ static void cs42l52_beep_work(struct work_struct *work)
static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
- struct snd_soc_codec *codec = input_get_drvdata(dev);
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ dev_dbg(component->dev, "Beep event %x %x\n", code, hz);
switch (code) {
case SND_BELL:
if (hz)
hz = 261;
+ break;
case SND_TONE:
break;
default:
@@ -959,9 +977,8 @@ static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l52_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct cs42l52_private *cs42l52 = dev_get_drvdata(dev);
long int time;
@@ -976,16 +993,16 @@ static ssize_t cs42l52_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void cs42l52_init_beep(struct snd_soc_codec *codec)
+static void cs42l52_init_beep(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l52->beep = devm_input_allocate_device(codec->dev);
+ cs42l52->beep = devm_input_allocate_device(component->dev);
if (!cs42l52->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -993,49 +1010,49 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
cs42l52->beep_rate = 0;
cs42l52->beep->name = "CS42L52 Beep Generator";
- cs42l52->beep->phys = dev_name(codec->dev);
+ cs42l52->beep->phys = dev_name(component->dev);
cs42l52->beep->id.bustype = BUS_I2C;
cs42l52->beep->evbit[0] = BIT_MASK(EV_SND);
cs42l52->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
cs42l52->beep->event = cs42l52_beep_event;
- cs42l52->beep->dev.parent = codec->dev;
- input_set_drvdata(cs42l52->beep, codec);
+ cs42l52->beep->dev.parent = component->dev;
+ input_set_drvdata(cs42l52->beep, component);
ret = input_register_device(cs42l52->beep);
if (ret != 0) {
cs42l52->beep = NULL;
- dev_err(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void cs42l52_free_beep(struct snd_soc_codec *codec)
+static void cs42l52_free_beep(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l52->beep_work);
cs42l52->beep = NULL;
- snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
+ snd_soc_component_update_bits(component, CS42L52_BEEP_TONE_CTL,
CS42L52_BEEP_EN_MASK, 0);
}
-static int cs42l52_probe(struct snd_soc_codec *codec)
+static int cs42l52_probe(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
regcache_cache_only(cs42l52->regmap, true);
- cs42l52_add_mic_controls(codec);
+ cs42l52_add_mic_controls(component);
- cs42l52_init_beep(codec);
+ cs42l52_init_beep(component);
cs42l52->sysclk = CS42L52_DEFAULT_CLK;
cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
@@ -1043,26 +1060,25 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
return 0;
}
-static int cs42l52_remove(struct snd_soc_codec *codec)
+static void cs42l52_remove(struct snd_soc_component *component)
{
- cs42l52_free_beep(codec);
-
- return 0;
+ cs42l52_free_beep(component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
- .probe = cs42l52_probe,
- .remove = cs42l52_remove,
- .set_bias_level = cs42l52_set_bias_level,
- .suspend_bias_off = true,
-
- .dapm_widgets = cs42l52_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
- .dapm_routes = cs42l52_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map),
-
- .controls = cs42l52_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l52_snd_controls),
+static const struct snd_soc_component_driver soc_component_dev_cs42l52 = {
+ .probe = cs42l52_probe,
+ .remove = cs42l52_remove,
+ .set_bias_level = cs42l52_set_bias_level,
+ .controls = cs42l52_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l52_snd_controls),
+ .dapm_widgets = cs42l52_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
+ .dapm_routes = cs42l52_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
/* Current and threshold powerup sequence Pg37 */
@@ -1086,21 +1102,19 @@ static const struct regmap_config cs42l52_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
.readable_reg = cs42l52_readable_register,
.volatile_reg = cs42l52_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l52_private *cs42l52;
- struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
+ struct cs42l52_platform_data *pdata;
int ret;
- unsigned int devid = 0;
+ unsigned int devid;
unsigned int reg;
u32 val32;
- cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private),
- GFP_KERNEL);
+ cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l52), GFP_KERNEL);
if (cs42l52 == NULL)
return -ENOMEM;
cs42l52->dev = &i2c_client->dev;
@@ -1111,52 +1125,43 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
return ret;
}
- if (pdata) {
- cs42l52->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l52_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev, "could not allocate pdata\n");
- return -ENOMEM;
- }
- if (i2c_client->dev.of_node) {
- if (of_property_read_bool(i2c_client->dev.of_node,
- "cirrus,mica-differential-cfg"))
- pdata->mica_diff_cfg = true;
-
- if (of_property_read_bool(i2c_client->dev.of_node,
- "cirrus,micb-differential-cfg"))
- pdata->micb_diff_cfg = true;
-
- if (of_property_read_u32(i2c_client->dev.of_node,
- "cirrus,micbias-lvl", &val32) >= 0)
- pdata->micbias_lvl = val32;
-
- if (of_property_read_u32(i2c_client->dev.of_node,
- "cirrus,chgfreq-divisor", &val32) >= 0)
- pdata->chgfreq = val32;
-
- pdata->reset_gpio =
- of_get_named_gpio(i2c_client->dev.of_node,
- "cirrus,reset-gpio", 0);
- }
- cs42l52->pdata = *pdata;
+
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,mica-differential-cfg"))
+ pdata->mica_diff_cfg = true;
+
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,micb-differential-cfg"))
+ pdata->micb_diff_cfg = true;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,micbias-lvl", &val32) >= 0)
+ pdata->micbias_lvl = val32;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,chgfreq-divisor", &val32) >= 0)
+ pdata->chgfreq = val32;
+
+ pdata->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "cirrus,reset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L52 /RST");
}
+ cs42l52->pdata = *pdata;
+
if (cs42l52->pdata.reset_gpio) {
- ret = devm_gpio_request_one(&i2c_client->dev,
- cs42l52->pdata.reset_gpio,
- GPIOF_OUT_INIT_HIGH,
- "CS42L52 /RST");
- if (ret < 0) {
- dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
- cs42l52->pdata.reset_gpio, ret);
- return ret;
- }
- gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
- gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
}
i2c_set_clientdata(i2c_client, cs42l52);
@@ -1168,6 +1173,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
ret);
ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
devid = reg & CS42L52_CHIP_ID_MASK;
if (devid != CS42L52_CHIP_ID) {
ret = -ENODEV;
@@ -1204,17 +1214,8 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
CS42L52_IFACE_CTL2_BIAS_LVL,
cs42l52->pdata.micbias_lvl);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l52, &cs42l52_dai, 1);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static int cs42l52_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l52, &cs42l52_dai, 1);
}
static const struct of_device_id cs42l52_of_match[] = {
@@ -1225,7 +1226,7 @@ MODULE_DEVICE_TABLE(of, cs42l52_of_match);
static const struct i2c_device_id cs42l52_id[] = {
- { "cs42l52", 0 },
+ { "cs42l52" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l52_id);
@@ -1236,8 +1237,7 @@ static struct i2c_driver cs42l52_i2c_driver = {
.of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
- .probe = cs42l52_i2c_probe,
- .remove = cs42l52_i2c_remove,
+ .probe = cs42l52_i2c_probe,
};
module_i2c_driver(cs42l52_i2c_driver);
diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h
index ac445993e6bf..e485670f9a6f 100644
--- a/sound/soc/codecs/cs42l52.h
+++ b/sound/soc/codecs/cs42l52.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs42l52.h -- CS42L52 ALSA SoC audio driver
*
@@ -5,11 +6,6 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef __CS42L52_H__
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index 7cd5f769bb61..aabb74f1f43c 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1,42 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l56.c -- CS42L56 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l56.h>
#include "cs42l56.h"
#define CS42L56_NUM_SUPPLIES 3
+
+struct cs42l56_platform_data {
+ /* GPIO for Reset */
+ struct gpio_desc *gpio_nreset;
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1a_ref_cfg;
+
+ /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2a_ref_cfg;
+
+ /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1b_ref_cfg;
+
+ /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2b_ref_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg62 */
+ unsigned int chgfreq;
+
+ /* HighPass Filter Right Channel Corner Frequency */
+ unsigned int hpfb_freq;
+
+ /* HighPass Filter Left Channel Corner Frequency */
+ unsigned int hpfa_freq;
+
+ /* Adaptive Power Control for LO/HP */
+ unsigned int adaptive_pwr;
+};
+
static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
"VA",
"VCP",
@@ -45,7 +73,7 @@ static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
struct cs42l56_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct device *dev;
struct cs42l56_platform_data pdata;
struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES];
@@ -56,7 +84,7 @@ struct cs42l56_private {
u8 iface;
u8 iface_fmt;
u8 iface_inv;
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
struct input_dev *beep;
struct work_struct beep_work;
int beep_rate;
@@ -64,8 +92,6 @@ struct cs42l56_private {
};
static const struct reg_default cs42l56_reg_defaults[] = {
- { 1, 0x56 }, /* r01 - ID 1 */
- { 2, 0x04 }, /* r02 - ID 2 */
{ 3, 0x7f }, /* r03 - Power Ctl 1 */
{ 4, 0xff }, /* r04 - Power Ctl 2 */
{ 5, 0x00 }, /* ro5 - Clocking Ctl 1 */
@@ -205,14 +231,6 @@ static const struct soc_enum beep_bass_enum =
SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1,
ARRAY_SIZE(beep_bass_text), beep_bass_text);
-static const char * const adc_swap_text[] = {
- "None", "A+B/2", "A-B/2", "Swap"
-};
-
-static const struct soc_enum adc_swap_enum =
- SOC_ENUM_SINGLE(CS42L56_MISC_ADC_CTL, 3,
- ARRAY_SIZE(adc_swap_text), adc_swap_text);
-
static const char * const pgaa_mux_text[] = {
"AIN1A", "AIN2A", "AIN3A"};
@@ -405,9 +423,9 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1),
SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME,
- CS42L56_HPB_VOLUME, 0, 0x84, 0x48, hl_tlv),
+ CS42L56_HPB_VOLUME, 0, 0x44, 0x48, hl_tlv),
SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
- CS42L56_LOB_VOLUME, 0, 0x84, 0x48, hl_tlv),
+ CS42L56_LOB_VOLUME, 0, 0x44, 0x48, hl_tlv),
SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
0, 0x00, 1, tone_tlv),
@@ -728,8 +746,8 @@ static int cs42l56_get_mclk_ratio(int mclk, int rate)
static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
switch (freq) {
case CS42L56_MCLK_5P6448MHZ:
@@ -755,10 +773,10 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
}
cs42l56->mclk = freq;
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_PREDIV_MASK,
cs42l56->mclk_prediv);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIV2_MASK,
cs42l56->mclk_div2);
@@ -767,14 +785,14 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42l56->iface = CS42L56_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42l56->iface = CS42L56_SLAVE_MODE;
break;
default:
@@ -805,22 +823,22 @@ static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MS_MODE_MASK, cs42l56->iface);
- snd_soc_update_bits(codec, CS42L56_SERIAL_FMT,
+ snd_soc_component_update_bits(component, CS42L56_SERIAL_FMT,
CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_SCLK_INV_MASK, cs42l56->iface_inv);
return 0;
}
-static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l56_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
/* Hit the DSP Mixer first */
- snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
@@ -829,21 +847,21 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
CS42L56_MSTA_MUTE_MASK,
CS42L56_MUTE_ALL);
/* Mute ADC's */
- snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL,
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_MUTE_ALL);
/* HP And LO */
- snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
} else {
- snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
@@ -852,18 +870,18 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
CS42L56_MSTA_MUTE_MASK,
CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL,
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
}
return 0;
@@ -873,39 +891,40 @@ static int cs42l56_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
int ratio;
ratio = cs42l56_get_mclk_ratio(cs42l56->mclk, params_rate(params));
if (ratio >= 0) {
- snd_soc_update_bits(codec, CS42L56_CLKCTL_2,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_2,
CS42L56_CLK_RATIO_MASK, ratio);
} else {
- dev_err(codec->dev, "unsupported mclk/sclk/lrclk ratio\n");
+ dev_err(component->dev, "unsupported mclk/sclk/lrclk ratio\n");
return -EINVAL;
}
return 0;
}
-static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l56_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIS_MASK, 0);
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l56->regmap, false);
regcache_sync(cs42l56->regmap);
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
@@ -917,13 +936,13 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 1);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 1);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIS_MASK, 1);
regcache_cache_only(cs42l56->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
@@ -943,9 +962,10 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops cs42l56_ops = {
.hw_params = cs42l56_pcm_hw_params,
- .digital_mute = cs42l56_digital_mute,
+ .mute_stream = cs42l56_mute,
.set_fmt = cs42l56_set_dai_fmt,
.set_sysclk = cs42l56_set_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42l56_dai = {
@@ -976,8 +996,8 @@ static void cs42l56_beep_work(struct work_struct *work)
{
struct cs42l56_private *cs42l56 =
container_of(work, struct cs42l56_private, beep_work);
- struct snd_soc_codec *codec = cs42l56->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = cs42l56->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int i;
int val = 0;
int best = 0;
@@ -989,18 +1009,18 @@ static void cs42l56_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n",
beep_freq[best], cs42l56->beep_rate);
val = (best << CS42L56_BEEP_RATE_SHIFT);
snd_soc_dapm_enable_pin(dapm, "Beep");
} else {
- dev_dbg(codec->dev, "Disabling beep\n");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, CS42L56_BEEP_FREQ_ONTIME,
+ snd_soc_component_update_bits(component, CS42L56_BEEP_FREQ_ONTIME,
CS42L56_BEEP_FREQ_MASK, val);
snd_soc_dapm_sync(dapm);
@@ -1012,15 +1032,16 @@ static void cs42l56_beep_work(struct work_struct *work)
static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
- struct snd_soc_codec *codec = input_get_drvdata(dev);
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ dev_dbg(component->dev, "Beep event %x %x\n", code, hz);
switch (code) {
case SND_BELL:
if (hz)
hz = 261;
+ break;
case SND_TONE:
break;
default:
@@ -1033,9 +1054,8 @@ static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l56_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct cs42l56_private *cs42l56 = dev_get_drvdata(dev);
long int time;
@@ -1050,16 +1070,16 @@ static ssize_t cs42l56_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void cs42l56_init_beep(struct snd_soc_codec *codec)
+static void cs42l56_init_beep(struct snd_soc_component *component)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l56->beep = devm_input_allocate_device(codec->dev);
+ cs42l56->beep = devm_input_allocate_device(component->dev);
if (!cs42l56->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -1067,67 +1087,66 @@ static void cs42l56_init_beep(struct snd_soc_codec *codec)
cs42l56->beep_rate = 0;
cs42l56->beep->name = "CS42L56 Beep Generator";
- cs42l56->beep->phys = dev_name(codec->dev);
+ cs42l56->beep->phys = dev_name(component->dev);
cs42l56->beep->id.bustype = BUS_I2C;
cs42l56->beep->evbit[0] = BIT_MASK(EV_SND);
cs42l56->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
cs42l56->beep->event = cs42l56_beep_event;
- cs42l56->beep->dev.parent = codec->dev;
- input_set_drvdata(cs42l56->beep, codec);
+ cs42l56->beep->dev.parent = component->dev;
+ input_set_drvdata(cs42l56->beep, component);
ret = input_register_device(cs42l56->beep);
if (ret != 0) {
cs42l56->beep = NULL;
- dev_err(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void cs42l56_free_beep(struct snd_soc_codec *codec)
+static void cs42l56_free_beep(struct snd_soc_component *component)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l56->beep_work);
cs42l56->beep = NULL;
- snd_soc_update_bits(codec, CS42L56_BEEP_TONE_CFG,
+ snd_soc_component_update_bits(component, CS42L56_BEEP_TONE_CFG,
CS42L56_BEEP_EN_MASK, 0);
}
-static int cs42l56_probe(struct snd_soc_codec *codec)
+static int cs42l56_probe(struct snd_soc_component *component)
{
- cs42l56_init_beep(codec);
+ cs42l56_init_beep(component);
return 0;
}
-static int cs42l56_remove(struct snd_soc_codec *codec)
+static void cs42l56_remove(struct snd_soc_component *component)
{
- cs42l56_free_beep(codec);
-
- return 0;
+ cs42l56_free_beep(component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
- .probe = cs42l56_probe,
- .remove = cs42l56_remove,
- .set_bias_level = cs42l56_set_bias_level,
- .suspend_bias_off = true,
-
- .dapm_widgets = cs42l56_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets),
- .dapm_routes = cs42l56_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map),
-
- .controls = cs42l56_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l56_snd_controls),
+static const struct snd_soc_component_driver soc_component_dev_cs42l56 = {
+ .probe = cs42l56_probe,
+ .remove = cs42l56_remove,
+ .set_bias_level = cs42l56_set_bias_level,
+ .controls = cs42l56_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l56_snd_controls),
+ .dapm_widgets = cs42l56_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets),
+ .dapm_routes = cs42l56_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config cs42l56_regmap = {
@@ -1139,7 +1158,7 @@ static const struct regmap_config cs42l56_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
.readable_reg = cs42l56_readable_register,
.volatile_reg = cs42l56_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
@@ -1175,25 +1194,26 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
pdata->hpfb_freq = val32;
- pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0);
+ pdata->gpio_nreset = devm_gpiod_get_optional(&i2c_client->dev, "cirrus,gpio-nreset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->gpio_nreset))
+ return PTR_ERR(pdata->gpio_nreset);
+
+ gpiod_set_consumer_name(pdata->gpio_nreset, "CS42L56 /RST");
return 0;
}
-static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l56_private *cs42l56;
- struct cs42l56_platform_data *pdata =
- dev_get_platdata(&i2c_client->dev);
int ret, i;
- unsigned int devid = 0;
+ unsigned int devid;
unsigned int alpha_rev, metal_rev;
unsigned int reg;
- cs42l56 = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l56_private),
- GFP_KERNEL);
+ cs42l56 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l56), GFP_KERNEL);
if (cs42l56 == NULL)
return -ENOMEM;
cs42l56->dev = &i2c_client->dev;
@@ -1205,40 +1225,17 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- if (pdata) {
- cs42l56->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l56_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev,
- "could not allocate pdata\n");
- return -ENOMEM;
- }
- if (i2c_client->dev.of_node) {
- ret = cs42l56_handle_of_data(i2c_client,
- &cs42l56->pdata);
- if (ret != 0)
- return ret;
- }
- cs42l56->pdata = *pdata;
+ if (i2c_client->dev.of_node) {
+ ret = cs42l56_handle_of_data(i2c_client, &cs42l56->pdata);
+ if (ret != 0)
+ return ret;
}
if (cs42l56->pdata.gpio_nreset) {
- ret = gpio_request_one(cs42l56->pdata.gpio_nreset,
- GPIOF_OUT_INIT_HIGH, "CS42L56 /RST");
- if (ret < 0) {
- dev_err(&i2c_client->dev,
- "Failed to request /RST %d: %d\n",
- cs42l56->pdata.gpio_nreset, ret);
- return ret;
- }
- gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
- gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
}
-
i2c_set_clientdata(i2c_client, cs42l56);
for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
@@ -1261,14 +1258,18 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- regcache_cache_bypass(cs42l56->regmap, true);
-
ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ goto err_enable;
+ }
+
devid = reg & CS42L56_CHIP_ID_MASK;
if (devid != CS42L56_DEVID) {
dev_err(&i2c_client->dev,
"CS42L56 Device ID (%X). Expected %X\n",
devid, CS42L56_DEVID);
+ ret = -EINVAL;
goto err_enable;
}
alpha_rev = reg & CS42L56_AREV_MASK;
@@ -1278,23 +1279,25 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n",
alpha_rev, metal_rev);
- regcache_cache_bypass(cs42l56->regmap, false);
-
if (cs42l56->pdata.ain1a_ref_cfg)
regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
- CS42L56_AIN1A_REF_MASK, 1);
+ CS42L56_AIN1A_REF_MASK,
+ CS42L56_AIN1A_REF_MASK);
if (cs42l56->pdata.ain1b_ref_cfg)
regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
- CS42L56_AIN1B_REF_MASK, 1);
+ CS42L56_AIN1B_REF_MASK,
+ CS42L56_AIN1B_REF_MASK);
if (cs42l56->pdata.ain2a_ref_cfg)
regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
- CS42L56_AIN2A_REF_MASK, 1);
+ CS42L56_AIN2A_REF_MASK,
+ CS42L56_AIN2A_REF_MASK);
if (cs42l56->pdata.ain2b_ref_cfg)
regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
- CS42L56_AIN2B_REF_MASK, 1);
+ CS42L56_AIN2B_REF_MASK,
+ CS42L56_AIN2B_REF_MASK);
if (cs42l56->pdata.micbias_lvl)
regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL,
@@ -1321,10 +1324,10 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
CS42L56_ADAPT_PWR_MASK,
cs42l56->pdata.adaptive_pwr);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l56, &cs42l56_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l56, &cs42l56_dai, 1);
if (ret < 0)
- return ret;
+ goto err_enable;
return 0;
@@ -1334,14 +1337,12 @@ err_enable:
return ret;
}
-static int cs42l56_i2c_remove(struct i2c_client *client)
+static void cs42l56_i2c_remove(struct i2c_client *client)
{
struct cs42l56_private *cs42l56 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
cs42l56->supplies);
- return 0;
}
static const struct of_device_id cs42l56_of_match[] = {
@@ -1352,7 +1353,7 @@ MODULE_DEVICE_TABLE(of, cs42l56_of_match);
static const struct i2c_device_id cs42l56_id[] = {
- { "cs42l56", 0 },
+ { "cs42l56" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l56_id);
diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h
index 5025ec9be9b2..62a8c3cb1a01 100644
--- a/sound/soc/codecs/cs42l56.h
+++ b/sound/soc/codecs/cs42l56.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs42l52.h -- CS42L56 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#ifndef __CS42L56_H__
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 42a8fd4e1f9b..bda8442c1d66 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l73.c -- CS42L73 ALSA Soc Audio driver
*
@@ -5,33 +6,36 @@
*
* Authors: Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>
* Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/of_gpio.h>
#include <linux/pm.h>
-#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l73.h>
+#include "cirrus_legacy.h"
#include "cs42l73.h"
+struct cs42l73_platform_data {
+ /* RST GPIO */
+ struct gpio_desc *reset_gpio;
+ unsigned int chgfreq;
+ int jack_detection;
+ unsigned int mclk_freq;
+};
+
struct sp_config {
u8 spc, mmcc, spfs;
u32 srate;
@@ -277,12 +281,6 @@ static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
CS42L73_MIXERCTL, 4,
cs42l73_spo_mixer_text);
-static const struct snd_kcontrol_new vsp_output_mux =
- SOC_DAPM_ENUM("Route", vsp_output_mux_enum);
-
-static const struct snd_kcontrol_new xsp_output_mux =
- SOC_DAPM_ENUM("Route", xsp_output_mux_enum);
-
static const struct snd_kcontrol_new hp_amp_ctl =
SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 0, 1, 1);
@@ -490,8 +488,8 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
/* 150 ms delay between setting PDN and MCLKDIS */
@@ -506,8 +504,8 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
/* 50 ms delay between setting PDN and MCLKDIS */
@@ -524,8 +522,8 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
/* 30 ms delay between setting PDN and MCLKDIS */
@@ -794,7 +792,7 @@ struct cs42l73_mclk_div {
u8 mmcc;
};
-static struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = {
+static const struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = {
/* MCLK, Sample Rate, xMMCC[5:0] */
{5644800, 11025, 0x30},
{5644800, 22050, 0x20},
@@ -844,7 +842,7 @@ struct cs42l73_mclkx_div {
u8 mclkdiv;
};
-static struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = {
+static const struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = {
{5644800, 1, 0}, /* 5644800 */
{6000000, 1, 0}, /* 6000000 */
{6144000, 1, 0}, /* 6144000 */
@@ -884,8 +882,8 @@ static int cs42l73_get_mclk_coeff(int mclk, int srate)
static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
int mclkx_coeff;
u32 mclk = 0;
@@ -899,14 +897,14 @@ static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx /
cs42l73_mclkx_coeffs[mclkx_coeff].ratio;
- dev_dbg(codec->dev, "MCLK%u %u <-> internal MCLK %u\n",
+ dev_dbg(component->dev, "MCLK%u %u <-> internal MCLK %u\n",
priv->mclksel + 1, cs42l73_mclkx_coeffs[mclkx_coeff].mclkx,
mclk);
dmmcc = (priv->mclksel << 4) |
(cs42l73_mclkx_coeffs[mclkx_coeff].mclkdiv << 1);
- snd_soc_write(codec, CS42L73_DMMCC, dmmcc);
+ snd_soc_component_write(component, CS42L73_DMMCC, dmmcc);
priv->sysclk = mclkx_coeff;
priv->mclk = mclk;
@@ -917,8 +915,8 @@ static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case CS42L73_CLKID_MCLK1:
@@ -930,7 +928,7 @@ static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
}
if ((cs42l73_set_mclk(dai, freq)) < 0) {
- dev_err(codec->dev, "Unable to set MCLK for dai %s\n",
+ dev_err(component->dev, "Unable to set MCLK for dai %s\n",
dai->name);
return -EINVAL;
}
@@ -942,21 +940,21 @@ static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
u8 id = codec_dai->id;
unsigned int inv, format;
u8 spc, mmcc;
- spc = snd_soc_read(codec, CS42L73_SPC(id));
- mmcc = snd_soc_read(codec, CS42L73_MMCC(id));
+ spc = snd_soc_component_read(component, CS42L73_SPC(id));
+ mmcc = snd_soc_component_read(component, CS42L73_MMCC(id));
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
mmcc |= CS42L73_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
mmcc &= ~CS42L73_MS_MASTER;
break;
@@ -974,12 +972,12 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
if (mmcc & CS42L73_MS_MASTER) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PCM format in slave mode only\n");
return -EINVAL;
}
if (id == CS42L73_ASP) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PCM format is not supported on ASP port\n");
return -EINVAL;
}
@@ -1029,7 +1027,7 @@ static unsigned int cs42l73_get_xspfs_coeff(u32 rate)
return 0; /* 0 = Don't know */
}
-static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
+static void cs42l73_update_asrc(struct snd_soc_component *component, int id, int srate)
{
u8 spfs = 0;
@@ -1038,13 +1036,13 @@ static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
switch (id) {
case CS42L73_XSP:
- snd_soc_update_bits(codec, CS42L73_VXSPFS, 0x0f, spfs);
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0x0f, spfs);
break;
case CS42L73_ASP:
- snd_soc_update_bits(codec, CS42L73_ASPC, 0x3c, spfs << 2);
+ snd_soc_component_update_bits(component, CS42L73_ASPC, 0x3c, spfs << 2);
break;
case CS42L73_VSP:
- snd_soc_update_bits(codec, CS42L73_VXSPFS, 0xf0, spfs << 4);
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0xf0, spfs << 4);
break;
default:
break;
@@ -1055,8 +1053,8 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
int id = dai->id;
int mclk_coeff;
int srate = params_rate(params);
@@ -1070,7 +1068,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
if (mclk_coeff < 0)
return -EINVAL;
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"DAI[%d]: MCLK %u, srate %u, MMCC[5:0] = %x\n",
id, priv->mclk, srate,
cs42l73_mclk_coeffs[mclk_coeff].mmcc);
@@ -1091,38 +1089,39 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
/* Update ASRCs */
priv->config[id].srate = srate;
- snd_soc_write(codec, CS42L73_SPC(id), priv->config[id].spc);
- snd_soc_write(codec, CS42L73_MMCC(id), priv->config[id].mmcc);
+ snd_soc_component_write(component, CS42L73_SPC(id), priv->config[id].spc);
+ snd_soc_component_write(component, CS42L73_MMCC(id), priv->config[id].mmcc);
- cs42l73_update_asrc(codec, id, srate);
+ cs42l73_update_asrc(component, id, srate);
return 0;
}
-static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l73_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 0);
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 0);
+ snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 0);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 0);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l73->regmap, false);
regcache_sync(cs42l73->regmap);
}
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
if (cs42l73->shutdwn_delay > 0) {
mdelay(cs42l73->shutdwn_delay);
cs42l73->shutdwn_delay = 0;
@@ -1131,7 +1130,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
* down.
*/
}
- snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
+ snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
break;
}
return 0;
@@ -1139,10 +1138,10 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int id = dai->id;
- return snd_soc_update_bits(codec, CS42L73_SPC(id), CS42L73_SP_3ST,
+ return snd_soc_component_update_bits(component, CS42L73_SPC(id), CS42L73_SP_3ST,
tristate << 7);
}
@@ -1191,7 +1190,7 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs42l73-asp",
@@ -1211,7 +1210,7 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs42l73-vsp",
@@ -1231,17 +1230,17 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static int cs42l73_probe(struct snd_soc_codec *codec)
+static int cs42l73_probe(struct snd_soc_component *component)
{
- struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
/* Set Charge Pump Frequency */
if (cs42l73->pdata.chgfreq)
- snd_soc_update_bits(codec, CS42L73_CPFCHC,
+ snd_soc_component_update_bits(component, CS42L73_CPFCHC,
CS42L73_CHARGEPUMP_MASK,
cs42l73->pdata.chgfreq << 4);
@@ -1252,18 +1251,19 @@ static int cs42l73_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l73 = {
- .probe = cs42l73_probe,
- .set_bias_level = cs42l73_set_bias_level,
- .suspend_bias_off = true,
-
- .dapm_widgets = cs42l73_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets),
- .dapm_routes = cs42l73_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map),
-
- .controls = cs42l73_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l73_snd_controls),
+static const struct snd_soc_component_driver soc_component_dev_cs42l73 = {
+ .probe = cs42l73_probe,
+ .set_bias_level = cs42l73_set_bias_level,
+ .controls = cs42l73_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l73_snd_controls),
+ .dapm_widgets = cs42l73_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets),
+ .dapm_routes = cs42l73_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config cs42l73_regmap = {
@@ -1275,21 +1275,21 @@ static const struct regmap_config cs42l73_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults),
.volatile_reg = cs42l73_volatile_register,
.readable_reg = cs42l73_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l73_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l73_private *cs42l73;
- struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
- int ret;
- unsigned int devid = 0;
+ struct cs42l73_platform_data *pdata;
+ int ret, devid;
unsigned int reg;
u32 val32;
- cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l73_private),
- GFP_KERNEL);
+ cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l73), GFP_KERNEL);
if (!cs42l73)
return -ENOMEM;
@@ -1300,85 +1300,66 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- if (pdata) {
- cs42l73->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l73_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev, "could not allocate pdata\n");
- return -ENOMEM;
- }
- if (i2c_client->dev.of_node) {
- if (of_property_read_u32(i2c_client->dev.of_node,
- "chgfreq", &val32) >= 0)
- pdata->chgfreq = val32;
- }
- pdata->reset_gpio = of_get_named_gpio(i2c_client->dev.of_node,
- "reset-gpio", 0);
- cs42l73->pdata = *pdata;
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_u32(i2c_client->dev.of_node, "chgfreq", &val32) >= 0)
+ pdata->chgfreq = val32;
}
+ pdata->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L73 /RST");
+ cs42l73->pdata = *pdata;
i2c_set_clientdata(i2c_client, cs42l73);
if (cs42l73->pdata.reset_gpio) {
- ret = devm_gpio_request_one(&i2c_client->dev,
- cs42l73->pdata.reset_gpio,
- GPIOF_OUT_INIT_HIGH,
- "CS42L73 /RST");
- if (ret < 0) {
- dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
- cs42l73->pdata.reset_gpio, ret);
- return ret;
- }
- gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
- gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
}
- regcache_cache_bypass(cs42l73->regmap, true);
-
/* initialize codec */
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs42l73->regmap, CS42L73_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reset;
+ }
if (devid != CS42L73_DEVID) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
"CS42L73 Device ID (%X). Expected %X\n",
devid, CS42L73_DEVID);
- return ret;
+ goto err_reset;
}
ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;;
+ goto err_reset;
}
dev_info(&i2c_client->dev,
"Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF);
- regcache_cache_bypass(cs42l73->regmap, false);
-
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l73, cs42l73_dai,
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l73, cs42l73_dai,
ARRAY_SIZE(cs42l73_dai));
if (ret < 0)
- return ret;
- return 0;
-}
+ goto err_reset;
-static int cs42l73_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+
+ return ret;
}
static const struct of_device_id cs42l73_of_match[] = {
@@ -1388,7 +1369,7 @@ static const struct of_device_id cs42l73_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42l73_of_match);
static const struct i2c_device_id cs42l73_id[] = {
- {"cs42l73", 0},
+ {"cs42l73"},
{}
};
@@ -1401,7 +1382,6 @@ static struct i2c_driver cs42l73_i2c_driver = {
},
.id_table = cs42l73_id,
.probe = cs42l73_i2c_probe,
- .remove = cs42l73_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h
index 45746186a678..e43a35576111 100644
--- a/sound/soc/codecs/cs42l73.h
+++ b/sound/soc/codecs/cs42l73.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS42L73 codec driver
*
@@ -5,21 +6,6 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Brian Austin <brian.austin@cirrus.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#ifndef __CS42L73_H__
diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c
new file mode 100644
index 000000000000..53a7fe1ab3dd
--- /dev/null
+++ b/sound/soc/codecs/cs42l83-i2c.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l83-i2c.c -- CS42L83 ALSA SoC audio driver for I2C
+ *
+ * Based on cs42l42-i2c.c:
+ * Copyright 2016, 2022 Cirrus Logic, Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs42l42.h"
+
+static const struct reg_default cs42l83_reg_defaults[] = {
+ { CS42L42_FRZ_CTL, 0x00 },
+ { CS42L42_SRC_CTL, 0x10 },
+ { CS42L42_MCLK_CTL, 0x00 }, /* <- only deviation from CS42L42 */
+ { CS42L42_SFTRAMP_RATE, 0xA4 },
+ { CS42L42_SLOW_START_ENABLE, 0x70 },
+ { CS42L42_I2C_DEBOUNCE, 0x88 },
+ { CS42L42_I2C_STRETCH, 0x03 },
+ { CS42L42_I2C_TIMEOUT, 0xB7 },
+ { CS42L42_PWR_CTL1, 0xFF },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL1, 0x40 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_OSC_SWITCH, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x1B },
+ { CS42L42_TSENSE_CTL, 0x1B },
+ { CS42L42_TSRS_INT_DISABLE, 0x00 },
+ { CS42L42_HSDET_CTL1, 0x77 },
+ { CS42L42_HSDET_CTL2, 0x00 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_SPDIF_CLK_CFG, 0x00 },
+ { CS42L42_FSYNC_PW_LOWER, 0x00 },
+ { CS42L42_FSYNC_PW_UPPER, 0x00 },
+ { CS42L42_FSYNC_P_LOWER, 0xF9 },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x10 },
+ { CS42L42_FS_RATE_EN, 0x00 },
+ { CS42L42_IN_ASRC_CLK, 0x00 },
+ { CS42L42_OUT_ASRC_CLK, 0x00 },
+ { CS42L42_PLL_DIV_CFG1, 0x00 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0x01 },
+ { CS42L42_MIXER_INT_MASK, 0x0F },
+ { CS42L42_SRC_INT_MASK, 0x0F },
+ { CS42L42_ASP_RX_INT_MASK, 0x1F },
+ { CS42L42_ASP_TX_INT_MASK, 0x0F },
+ { CS42L42_CODEC_INT_MASK, 0x03 },
+ { CS42L42_SRCPL_INT_MASK, 0x7F },
+ { CS42L42_VPMON_INT_MASK, 0x01 },
+ { CS42L42_PLL_LOCK_INT_MASK, 0x01 },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
+ { CS42L42_PLL_CTL1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC0, 0x00 },
+ { CS42L42_PLL_DIV_FRAC1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC2, 0x00 },
+ { CS42L42_PLL_DIV_INT, 0x40 },
+ { CS42L42_PLL_CTL3, 0x10 },
+ { CS42L42_PLL_CAL_RATIO, 0x80 },
+ { CS42L42_PLL_CTL4, 0x03 },
+ { CS42L42_LOAD_DET_EN, 0x00 },
+ { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
+ { CS42L42_WAKE_CTL, 0xC0 },
+ { CS42L42_ADC_DISABLE_MUTE, 0x00 },
+ { CS42L42_TIPSENSE_CTL, 0x02 },
+ { CS42L42_MISC_DET_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0x1F },
+ { CS42L42_MIC_DET_CTL2, 0x2F },
+ { CS42L42_DET_INT1_MASK, 0xE0 },
+ { CS42L42_DET_INT2_MASK, 0xFF },
+ { CS42L42_HS_BIAS_CTL, 0xC2 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { CS42L42_ADC_VOLUME, 0x00 },
+ { CS42L42_ADC_WNF_HPF_CTL, 0x71 },
+ { CS42L42_DAC_CTL1, 0x00 },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_CLASSH_CTL, 0x07 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_EQ_COEF_IN0, 0x00 },
+ { CS42L42_EQ_COEF_IN1, 0x00 },
+ { CS42L42_EQ_COEF_IN2, 0x00 },
+ { CS42L42_EQ_COEF_IN3, 0x00 },
+ { CS42L42_EQ_COEF_RW, 0x00 },
+ { CS42L42_EQ_COEF_OUT0, 0x00 },
+ { CS42L42_EQ_COEF_OUT1, 0x00 },
+ { CS42L42_EQ_COEF_OUT2, 0x00 },
+ { CS42L42_EQ_COEF_OUT3, 0x00 },
+ { CS42L42_EQ_INIT_STAT, 0x00 },
+ { CS42L42_EQ_START_FILT, 0x00 },
+ { CS42L42_EQ_MUTE_CTL, 0x00 },
+ { CS42L42_SP_RX_CH_SEL, 0x04 },
+ { CS42L42_SP_RX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_RX_FS, 0x8C },
+ { CS42l42_SPDIF_CH_SEL, 0x0E },
+ { CS42L42_SP_TX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_TX_FS, 0xCC },
+ { CS42L42_SPDIF_SW_CTL1, 0x3F },
+ { CS42L42_SRC_SDIN_FS, 0x40 },
+ { CS42L42_SRC_SDOUT_FS, 0x40 },
+ { CS42L42_SPDIF_CTL1, 0x01 },
+ { CS42L42_SPDIF_CTL2, 0x00 },
+ { CS42L42_SPDIF_CTL3, 0x00 },
+ { CS42L42_SPDIF_CTL4, 0x42 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x0F },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
+};
+
+/*
+ * This is all the same as for CS42L42 but we
+ * replace the on-reset register defaults.
+ */
+static const struct regmap_config cs42l83_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = cs42l42_readable_register,
+ .volatile_reg = cs42l42_volatile_register,
+
+ .ranges = &cs42l42_page_range,
+ .num_ranges = 1,
+
+ .max_register = CS42L42_MAX_REGISTER,
+ .reg_defaults = cs42l83_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs42l83_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct device *dev = &i2c_client->dev;
+ struct cs42l42_private *cs42l83;
+ struct regmap *regmap;
+ int ret;
+
+ cs42l83 = devm_kzalloc(dev, sizeof(*cs42l83), GFP_KERNEL);
+ if (!cs42l83)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(i2c_client, &cs42l83_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap),
+ "regmap_init() failed\n");
+
+ cs42l83->devid = CS42L83_CHIP_ID;
+ cs42l83->dev = dev;
+ cs42l83->regmap = regmap;
+ cs42l83->irq = i2c_client->irq;
+
+ ret = cs42l42_common_probe(cs42l83, &cs42l42_soc_component, &cs42l42_dai);
+ if (ret)
+ return ret;
+
+ return cs42l42_init(cs42l83);
+}
+
+static void cs42l83_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l42_private *cs42l83 = dev_get_drvdata(&i2c_client->dev);
+
+ cs42l42_common_remove(cs42l83);
+}
+
+static int cs42l83_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l83_i2c_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l83_i2c_resume)
+};
+
+static const struct of_device_id __maybe_unused cs42l83_of_match[] = {
+ { .compatible = "cirrus,cs42l83", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l83_of_match);
+
+static struct i2c_driver cs42l83_i2c_driver = {
+ .driver = {
+ .name = "cs42l83",
+ .pm = pm_ptr(&cs42l83_i2c_pm_ops),
+ .of_match_table = of_match_ptr(cs42l83_of_match),
+ },
+ .probe = cs42l83_i2c_probe,
+ .remove = cs42l83_i2c_remove,
+};
+
+module_i2c_driver(cs42l83_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L83 I2C driver");
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS42L42_CORE");
diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c
new file mode 100644
index 000000000000..1e1307a16f81
--- /dev/null
+++ b/sound/soc/codecs/cs42l84.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l84.c -- CS42L84 ALSA SoC audio driver
+ *
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on sound/soc/codecs/cs42l42{.c,.h}
+ * Copyright 2016 Cirrus Logic, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "cs42l84.h"
+#include "cirrus_legacy.h"
+
+struct cs42l84_private {
+ struct regmap *regmap;
+ struct device *dev;
+ struct gpio_desc *reset_gpio;
+ struct snd_soc_jack *jack;
+ struct mutex irq_lock;
+ u8 tip_state;
+ u8 ring_state;
+ int pll_config;
+ int bclk;
+ u8 pll_mclk_f;
+ u32 srate;
+ u8 stream_use;
+ int hs_type;
+};
+
+static bool cs42l84_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L84_DEVID ... CS42L84_DEVID+5:
+ case CS42L84_TSRS_PLUG_INT_STATUS:
+ case CS42L84_PLL_LOCK_STATUS:
+ case CS42L84_TSRS_PLUG_STATUS:
+ case CS42L84_HS_DET_STATUS2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs42l84_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .volatile_reg = cs42l84_volatile_register,
+
+ .max_register = 0x73fe,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kctl);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value;
+ int vola, volb;
+ int ret, ret2, updated = 0;
+
+ vola = val->value.integer.value[0] + mc->min;
+ volb = val->value.integer.value[1] + mc->min;
+
+ if (vola < mc->min || vola > mc->max || volb < mc->min || volb > mc->max)
+ return -EINVAL;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL,
+ CS42L84_FRZ_CTL_ENGAGE,
+ CS42L84_FRZ_CTL_ENGAGE);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_LSB,
+ 0xff, vola & 0xff);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_MSB,
+ 0xff, (vola >> 8) & 0x01);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_LSB,
+ 0xff, volb & 0xff);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_MSB,
+ 0xff, (volb >> 8) & 0x01);
+ if (ret < 0)
+ goto bail;
+ ret |= updated;
+
+bail:
+ ret2 = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL,
+ CS42L84_FRZ_CTL_ENGAGE, 0);
+ if (ret2 < 0 && ret >= 0)
+ ret = ret2;
+
+ return ret;
+}
+
+static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kctl);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value;
+ int vola, volb;
+ int ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_LSB);
+ if (ret < 0)
+ return ret;
+ vola = ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_MSB);
+ if (ret < 0)
+ return ret;
+ vola |= (ret & 1) << 8;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_LSB);
+ if (ret < 0)
+ return ret;
+ volb = ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_MSB);
+ if (ret < 0)
+ return ret;
+ volb |= (ret & 1) << 8;
+
+ if (vola & BIT(8))
+ vola |= ~((int)(BIT(8) - 1));
+ if (volb & BIT(8))
+ volb |= ~((int)(BIT(8) - 1));
+
+ val->value.integer.value[0] = vola - mc->min;
+ val->value.integer.value[1] = volb - mc->min;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -12800, 50, true);
+static const DECLARE_TLV_DB_SCALE(cs42l84_adc_tlv, -1200, 50, false);
+static const DECLARE_TLV_DB_SCALE(cs42l84_pre_tlv, 0, 1000, false);
+
+static const struct snd_kcontrol_new cs42l84_snd_controls[] = {
+ SOC_DOUBLE_R_S_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB,
+ CS42L84_DAC_CHB_VOL_LSB, 0, -256, 24, 8, 0,
+ cs42l84_get_dac_vol, cs42l84_put_dac_vol, cs42l84_dac_tlv),
+ SOC_SINGLE_TLV("ADC Preamp Capture Volume", CS42L84_ADC_CTL1,
+ CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0, cs42l84_pre_tlv),
+ SOC_SINGLE_TLV("ADC PGA Capture Volume", CS42L84_ADC_CTL1,
+ CS42L84_ADC_CTL1_PGA_GAIN_SHIFT, 24, 0, cs42l84_adc_tlv),
+ SOC_SINGLE("ADC WNF Switch", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("WNF Corner Frequency", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_WNF_CF_SHIFT, 3, 0),
+ SOC_SINGLE("ADC HPF Switch", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("HPF Corner Frequency", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_HPF_CF_SHIFT, 3, 0),
+};
+
+static const char * const cs42l84_mux_text[] = {
+ "Blank", "ADC", "ASP RX CH1", "ASP RX CH2",
+};
+
+static const unsigned int cs42l84_mux_values[] = {
+ 0b0000, 0b0111, 0b1101, 0b1110,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_daca_mux_enum,
+ CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACA_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_dacb_mux_enum,
+ CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACB_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_sdout1_mux_enum,
+ CS42L84_BUS_ASP_TX_SRC, CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static const struct snd_kcontrol_new cs42l84_daca_mux_ctrl =
+ SOC_DAPM_ENUM("DACA Select", cs42l84_daca_mux_enum);
+
+static const struct snd_kcontrol_new cs42l84_dacb_mux_ctrl =
+ SOC_DAPM_ENUM("DACB Select", cs42l84_dacb_mux_enum);
+
+static const struct snd_kcontrol_new cs42l84_sdout1_mux_ctrl =
+ SOC_DAPM_ENUM("SDOUT1 Select", cs42l84_sdout1_mux_enum);
+
+static const struct snd_soc_dapm_widget cs42l84_dapm_widgets[] = {
+ /* Playback Path */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_DAC("DAC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_DAC_SHIFT, 0),
+ SND_SOC_DAPM_MUX("DACA Select", SND_SOC_NOPM, 0, 0, &cs42l84_daca_mux_ctrl),
+ SND_SOC_DAPM_MUX("DACB Select", SND_SOC_NOPM, 0, 0, &cs42l84_dacb_mux_ctrl),
+ SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH1_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH2_SHIFT, 0),
+
+ /* Capture Path */
+ SND_SOC_DAPM_INPUT("HS"),
+ SND_SOC_DAPM_ADC("ADC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ADC_SHIFT, 0),
+ SND_SOC_DAPM_MUX("SDOUT1 Select", SND_SOC_NOPM, 0, 0, &cs42l84_sdout1_mux_ctrl),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L84_ASP_TX_EN, CS42L84_ASP_TX_EN_CH1_SHIFT, 0),
+
+ /* Playback/Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("BUS", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_BUS_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASP", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ASP_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BCLK", CS42L84_ASP_CTL, CS42L84_ASP_CTL_BCLK_EN_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route cs42l84_audio_map[] = {
+ /* Playback Path */
+ {"HP", NULL, "DAC"},
+ {"DAC", NULL, "DACA Select"},
+ {"DAC", NULL, "DACB Select"},
+ {"DACA Select", "ASP RX CH1", "SDIN1"},
+ {"DACA Select", "ASP RX CH2", "SDIN2"},
+ {"DACB Select", "ASP RX CH1", "SDIN1"},
+ {"DACB Select", "ASP RX CH2", "SDIN2"},
+ {"SDIN1", NULL, "Playback"},
+ {"SDIN2", NULL, "Playback"},
+
+ {"ADC", NULL, "HS"},
+ {"SDOUT1 Select", "ADC", "ADC"},
+ {"SDOUT1", NULL, "SDOUT1 Select"},
+ {"Capture", NULL, "SDOUT1"},
+
+ /* Playback Requirements */
+ {"DAC", NULL, "BUS"},
+ {"SDIN1", NULL, "ASP"},
+ {"SDIN2", NULL, "ASP"},
+ {"SDIN1", NULL, "BCLK"},
+ {"SDIN2", NULL, "BCLK"},
+
+ /* Capture Requirements */
+ {"SDOUT1", NULL, "BUS"},
+ {"SDOUT1", NULL, "ASP"},
+ {"SDOUT1", NULL, "BCLK"},
+};
+
+static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
+{
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l84->irq_lock);
+ cs42l84->jack = jk;
+ snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET);
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return 0;
+}
+
+static int cs42l84_component_probe(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, CS42L84_ASP_CTL,
+ CS42L84_ASP_CTL_TDM_MODE, 0);
+ snd_soc_component_update_bits(component, CS42L84_HP_VOL_CTL,
+ CS42L84_HP_VOL_CTL_SOFT | CS42L84_HP_VOL_CTL_ZERO_CROSS,
+ CS42L84_HP_VOL_CTL_ZERO_CROSS);
+
+ /* TDM settings */
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE |
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE |
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE | \
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE | \
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ /* Routing defaults */
+ snd_soc_component_write(component, CS42L84_BUS_DAC_SRC,
+ 0b1101 << CS42L84_BUS_DAC_SRC_DACA_SHIFT |
+ 0b1110 << CS42L84_BUS_DAC_SRC_DACB_SHIFT);
+ snd_soc_component_write(component, CS42L84_BUS_ASP_TX_SRC,
+ 0b0111 << CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs42l84 = {
+ .set_jack = cs42l84_set_jack,
+ .probe = cs42l84_component_probe,
+ .controls = cs42l84_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l84_snd_controls),
+ .dapm_widgets = cs42l84_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l84_dapm_widgets),
+ .dapm_routes = cs42l84_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l84_audio_map),
+ .endianness = 1,
+};
+
+struct cs42l84_pll_params {
+ u32 bclk;
+ u8 mclk_src_sel;
+ u8 bclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 mclk_int;
+};
+
+/*
+ * Common PLL Settings for given BCLK
+ */
+static const struct cs42l84_pll_params pll_ratio_table[] = {
+ { 3072000, 1, 0, 0x40, 0x000000, 0x03, 0x10, 12288000},
+ { 6144000, 1, 1, 0x40, 0x000000, 0x03, 0x10, 12288000},
+ { 12288000, 0, 0, 0, 0, 0, 0, 12288000},
+ { 24576000, 1, 3, 0x40, 0x000000, 0x03, 0x10, 12288000},
+};
+
+static int cs42l84_pll_config(struct snd_soc_component *component)
+{
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int i;
+ u32 clk;
+ u32 fsync;
+
+ clk = cs42l84->bclk;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l84->stream_use) {
+ if (pll_ratio_table[cs42l84->pll_config].bclk == clk)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].bclk == clk) {
+ cs42l84->pll_config = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(pll_ratio_table))
+ return -EINVAL;
+
+ /* Set up the LRCLK */
+ fsync = clk / cs42l84->srate;
+ if (((fsync * cs42l84->srate) != clk)
+ || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported bclk %d/sample rate %d\n",
+ clk, cs42l84->srate);
+ return -EINVAL;
+ }
+
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL2,
+ CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO,
+ FIELD_PREP(CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO, fsync & 0x7f));
+ snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL3,
+ CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI,
+ FIELD_PREP(CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, fsync >> 7));
+
+ /* Save what the MCLK will be */
+ switch (pll_ratio_table[i].mclk_int) {
+ case 12000000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12MHZ;
+ break;
+ case 12288000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12_288KHZ;
+ break;
+ case 24000000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24MHZ;
+ break;
+ case 24576000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24_576KHZ;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_EN, 0);
+
+ if (pll_ratio_table[i].mclk_src_sel) {
+ /* Configure PLL */
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL3, CS42L84_CCM_CTL3_REFCLK_DIV,
+ FIELD_PREP(CS42L84_CCM_CTL3_REFCLK_DIV, pll_ratio_table[i].bclk_prediv));
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_INT,
+ pll_ratio_table[i].pll_div_int);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC0,
+ pll_ratio_table[i].pll_div_frac);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC1,
+ pll_ratio_table[i].pll_div_frac >> 8);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC2,
+ pll_ratio_table[i].pll_div_frac >> 16);
+ snd_soc_component_update_bits(component,
+ CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_MODE,
+ FIELD_PREP(CS42L84_PLL_CTL1_MODE, pll_ratio_table[i].pll_mode));
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIVOUT,
+ pll_ratio_table[i].pll_divout);
+ }
+
+ return 0;
+}
+
+static int cs42l84_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Bitclock/frame inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l84_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u32 ccm_samp_rate;
+
+ cs42l84->srate = params_rate(params);
+
+ ret = cs42l84_pll_config(component);
+ if (ret)
+ return ret;
+
+ switch (params_rate(params)) {
+ case 44100:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_44K1HZ;
+ break;
+ case 48000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_48KHZ;
+ break;
+ case 88200:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_88K2HZ;
+ break;
+ case 96000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_96KHZ;
+ break;
+ case 176400:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_176K4HZ;
+ break;
+ case 192000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, CS42L84_CCM_SAMP_RATE, ccm_samp_rate);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_component_write(component, CS42L84_ASP_RX_CH1_WIDTH,
+ params_width(params) - 1);
+ snd_soc_component_write(component, CS42L84_ASP_RX_CH2_WIDTH,
+ params_width(params) - 1);
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_component_write(component, CS42L84_ASP_TX_CH1_WIDTH,
+ params_width(params) - 1);
+ snd_soc_component_write(component, CS42L84_ASP_TX_CH2_WIDTH,
+ params_width(params) - 1);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l84_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (freq == 0) {
+ cs42l84->bclk = 0;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].bclk == freq) {
+ cs42l84->bclk = freq;
+ return 0;
+ }
+ }
+
+ dev_err(component->dev, "BCLK %u not supported\n", freq);
+
+ return -EINVAL;
+}
+
+static int cs42l84_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ unsigned int regval;
+ int ret;
+
+ if (mute) {
+ /* Mute the headphone */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_component_update_bits(component, CS42L84_DAC_CTL1,
+ CS42L84_DAC_CTL1_UNMUTE, 0);
+ cs42l84->stream_use &= ~(1 << stream);
+ if (!cs42l84->stream_use) {
+ /* Must disconnect PLL before stopping it */
+ snd_soc_component_write(component, CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_RCO);
+
+ usleep_range(150, 300);
+
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1,
+ CS42L84_PLL_CTL1_EN, 0);
+
+ snd_soc_component_update_bits(component, CS42L84_CCM_CTL4,
+ CS42L84_CCM_CTL4_REFCLK_EN, 0);
+ }
+ } else {
+ if (!cs42l84->stream_use) {
+ /* SCLK must be running before codec unmute.
+ *
+ * Note carried over from CS42L42:
+ *
+ * PLL must not be started with ADC and HP both off
+ * otherwise the FILT+ supply will not charge properly.
+ * DAPM widgets power-up before stream unmute so at least
+ * one of the "DAC" or "ADC" widgets will already have
+ * powered-up.
+ */
+
+ snd_soc_component_update_bits(component, CS42L84_CCM_CTL4,
+ CS42L84_CCM_CTL4_REFCLK_EN,
+ CS42L84_CCM_CTL4_REFCLK_EN);
+
+ if (pll_ratio_table[cs42l84->pll_config].mclk_src_sel) {
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1,
+ CS42L84_PLL_CTL1_EN,
+ CS42L84_PLL_CTL1_EN);
+ /* TODO: should we be doing something with divout here? */
+
+ ret = regmap_read_poll_timeout(cs42l84->regmap,
+ CS42L84_PLL_LOCK_STATUS,
+ regval,
+ (regval & CS42L84_PLL_LOCK_STATUS_LOCKED),
+ CS42L84_PLL_LOCK_POLL_US,
+ CS42L84_PLL_LOCK_TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(component->dev, "PLL failed to lock: %d\n", ret);
+
+ if (regval & CS42L84_PLL_LOCK_STATUS_ERROR)
+ dev_warn(component->dev, "PLL lock error\n");
+
+ /* PLL must be running to drive glitchless switch logic */
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ,
+ FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_PLL)
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f));
+ usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2);
+ } else {
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ,
+ FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_BCLK)
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f));
+ usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2);
+ }
+ }
+ cs42l84->stream_use |= 1 << stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ /* Un-mute the headphone */
+ snd_soc_component_update_bits(component, CS42L84_DAC_CTL1,
+ CS42L84_DAC_CTL1_UNMUTE,
+ CS42L84_DAC_CTL1_UNMUTE);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l84_ops = {
+ .hw_params = cs42l84_pcm_hw_params,
+ .set_fmt = cs42l84_set_dai_fmt,
+ .set_sysclk = cs42l84_set_sysclk,
+ .mute_stream = cs42l84_mute_stream,
+};
+
+#define CS42L84_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver cs42l84_dai = {
+ .name = "cs42l84",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = CS42L84_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = CS42L84_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &cs42l84_ops,
+};
+
+struct cs42l84_irq_params {
+ u16 status_addr;
+ u16 mask_addr;
+ u8 mask;
+};
+
+static const struct cs42l84_irq_params irq_params_table[] = {
+ {CS42L84_TSRS_PLUG_INT_STATUS, CS42L84_TSRS_PLUG_INT_MASK,
+ CS42L84_TSRS_PLUG_VAL_MASK}
+};
+
+static void cs42l84_detect_hs(struct cs42l84_private *cs42l84)
+{
+ unsigned int reg;
+
+ /* Power up HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 3) | /* 2.7 V */
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0));
+
+ /* Power up level detection circuitry */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET, 0);
+
+ /* TODO: Optimize */
+ msleep(50);
+
+ /* Connect HSBIAS in CTIA wiring */
+ /* TODO: Should likely be subject of detection */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 0));
+
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 3));
+
+ /* TODO: Optimize */
+ msleep(50);
+
+ regmap_read(cs42l84->regmap, CS42L84_HS_DET_STATUS2, &reg);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET);
+
+ switch (reg & 0b11) {
+ case 0b11: /* shorted */
+ case 0b00: /* open */
+ /* Power down HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1)); /* 0.0 V */
+ break;
+ }
+
+ switch (reg & 0b11) {
+ case 0b10: /* load */
+ dev_dbg(cs42l84->dev, "Detected mic\n");
+ cs42l84->hs_type = SND_JACK_HEADSET;
+ snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ break;
+
+ case 0b00: /* open */
+ dev_dbg(cs42l84->dev, "Detected open circuit on HS4\n");
+ fallthrough;
+ case 0b11: /* shorted */
+ default:
+ snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ cs42l84->hs_type = SND_JACK_HEADPHONE;
+ dev_dbg(cs42l84->dev, "Detected bare headphone (no mic)\n");
+ break;
+ }
+}
+
+static void cs42l84_revert_hs(struct cs42l84_private *cs42l84)
+{
+ /* Power down HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1) | /* 0.0 V */
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0));
+
+ /* Disconnect HSBIAS */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_REF_HS4 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2));
+}
+
+static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84,
+ unsigned int val)
+{
+ regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK,
+ CS42L84_RS_PLUG | CS42L84_RS_UNPLUG |
+ CS42L84_TS_PLUG | CS42L84_TS_UNPLUG,
+ val);
+}
+
+static irqreturn_t cs42l84_irq_thread(int irq, void *data)
+{
+ struct cs42l84_private *cs42l84 = (struct cs42l84_private *)data;
+ unsigned int stickies[1];
+ unsigned int masks[1];
+ unsigned int reg;
+ u8 current_tip_state;
+ u8 current_ring_state;
+ int i;
+
+ mutex_lock(&cs42l84->irq_lock);
+ /* Read sticky registers to clear interrupt */
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs42l84->regmap, irq_params_table[i].status_addr,
+ &(stickies[i]));
+ regmap_read(cs42l84->regmap, irq_params_table[i].mask_addr,
+ &(masks[i]));
+ stickies[i] = stickies[i] & (~masks[i]) &
+ irq_params_table[i].mask;
+ }
+
+ /* When handling plug sene IRQs, we only care about EITHER tip OR ring.
+ * Ring is useless on remove, and is only useful on insert for
+ * detecting if the plug state has changed AFTER we have handled the
+ * tip sense IRQ, e.g. if the plug was not fully seated within the tip
+ * sense debounce time.
+ */
+
+ if ((~masks[0]) & irq_params_table[0].mask) {
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+
+ current_tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+
+ if (current_tip_state != cs42l84->tip_state) {
+ cs42l84->tip_state = current_tip_state;
+ switch (current_tip_state) {
+ case CS42L84_PLUG:
+ dev_dbg(cs42l84->dev, "Plug event\n");
+
+ cs42l84_detect_hs(cs42l84);
+
+ /*
+ * Check the tip sense status again, and possibly invalidate
+ * the detection result
+ *
+ * Thanks to debounce, this should reliably indicate if the tip
+ * was disconnected at any point during the detection procedure.
+ */
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+ current_tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+ if (current_tip_state != CS42L84_PLUG) {
+ dev_dbg(cs42l84->dev, "Wobbly connection, detection invalidated\n");
+ cs42l84->tip_state = CS42L84_UNPLUG;
+ cs42l84_revert_hs(cs42l84);
+ }
+
+ /* Unmask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84, 0);
+ break;
+ case CS42L84_UNPLUG:
+ cs42l84->ring_state = CS42L84_UNPLUG;
+ dev_dbg(cs42l84->dev, "Unplug event\n");
+
+ cs42l84_revert_hs(cs42l84);
+ cs42l84->hs_type = 0;
+ snd_soc_jack_report(cs42l84->jack, 0,
+ SND_JACK_HEADSET);
+
+ /* Mask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84,
+ CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
+ break;
+ default:
+ cs42l84->ring_state = CS42L84_TRANS;
+ break;
+ }
+
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return IRQ_HANDLED;
+ }
+
+ /* Tip state didn't change, we must've got a ring sense IRQ */
+ current_ring_state = (((char) reg) &
+ (CS42L84_RS_PLUG | CS42L84_RS_UNPLUG)) >>
+ CS42L84_RS_PLUG_SHIFT;
+
+ if (current_ring_state != cs42l84->ring_state) {
+ cs42l84->ring_state = current_ring_state;
+ if (current_ring_state == CS42L84_PLUG)
+ cs42l84_detect_hs(cs42l84);
+ }
+ }
+
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84)
+{
+ unsigned int reg;
+
+ /* Set up plug detection */
+ regmap_update_bits(cs42l84->regmap, CS42L84_MIC_DET_CTL4,
+ CS42L84_MIC_DET_CTL4_LATCH_TO_VP,
+ CS42L84_MIC_DET_CTL4_LATCH_TO_VP);
+ regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL2,
+ CS42L84_TIP_SENSE_CTL2_MODE,
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL2_MODE, CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET));
+ regmap_update_bits(cs42l84->regmap, CS42L84_RING_SENSE_CTL,
+ CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 |
+ CS42L84_RING_SENSE_CTL_RISETIME | CS42L84_RING_SENSE_CTL_FALLTIME,
+ CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 |
+ FIELD_PREP(CS42L84_RING_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_125MS) |
+ FIELD_PREP(CS42L84_RING_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS));
+ regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL,
+ CS42L84_TIP_SENSE_CTL_INV |
+ CS42L84_TIP_SENSE_CTL_RISETIME | CS42L84_TIP_SENSE_CTL_FALLTIME,
+ CS42L84_TIP_SENSE_CTL_INV |
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_500MS) |
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS));
+ regmap_update_bits(cs42l84->regmap, CS42L84_MSM_BLOCK_EN3,
+ CS42L84_MSM_BLOCK_EN3_TR_SENSE,
+ CS42L84_MSM_BLOCK_EN3_TR_SENSE);
+
+ /* Save the initial status of the tip sense */
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+ cs42l84->tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+
+ /* Set mic-detection threshold */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MIC_DET_CTL1, CS42L84_MIC_DET_CTL1_HS_DET_LEVEL,
+ FIELD_PREP(CS42L84_MIC_DET_CTL1_HS_DET_LEVEL, 0x2c)); /* ~1.9 V */
+
+ /* Disconnect HSBIAS (initially) */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_REF_HS4 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET | CS42L84_HS_DET_CTL2_CTL,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2) |
+ FIELD_PREP(CS42L84_HS_DET_CTL2_CTL, 0));
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_CLAMP_DISABLE, 1, 1);
+
+}
+
+static int cs42l84_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs42l84_private *cs42l84;
+ int ret, devid;
+ unsigned int reg;
+
+ cs42l84 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l84_private),
+ GFP_KERNEL);
+ if (!cs42l84)
+ return -ENOMEM;
+
+ cs42l84->dev = &i2c_client->dev;
+ i2c_set_clientdata(i2c_client, cs42l84);
+ mutex_init(&cs42l84->irq_lock);
+
+ cs42l84->regmap = devm_regmap_init_i2c(i2c_client, &cs42l84_regmap);
+ if (IS_ERR(cs42l84->regmap)) {
+ ret = PTR_ERR(cs42l84->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs42l84->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l84->reset_gpio)) {
+ ret = PTR_ERR(cs42l84->reset_gpio);
+ goto err_disable_noreset;
+ }
+
+ if (cs42l84->reset_gpio) {
+ dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 1);
+ }
+ usleep_range(CS42L84_BOOT_TIME_US, CS42L84_BOOT_TIME_US * 2);
+
+ /* Request IRQ if one was specified */
+ if (i2c_client->irq) {
+ ret = request_threaded_irq(i2c_client->irq,
+ NULL, cs42l84_irq_thread,
+ IRQF_ONESHOT,
+ "cs42l84", cs42l84);
+ if (ret == -EPROBE_DEFER) {
+ goto err_disable_noirq;
+ } else if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request IRQ: %d\n", ret);
+ goto err_disable_noirq;
+ }
+ }
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs42l84->regmap, CS42L84_DEVID);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
+
+ if (devid != CS42L84_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS42L84 Device ID (%X). Expected %X\n",
+ devid, CS42L84_CHIP_ID);
+ ret = -EINVAL;
+ goto err_disable;
+ }
+
+ ret = regmap_read(cs42l84->regmap, CS42L84_REVID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_shutdown;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS42L84, Revision: %02X\n", reg & 0xFF);
+
+ /* Setup plug detection */
+ cs42l84_setup_plug_detect(cs42l84);
+
+ /* Mask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
+
+ /* Register codec for machine driver */
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l84, &cs42l84_dai, 1);
+ if (ret < 0)
+ goto err_shutdown;
+
+ return 0;
+
+err_shutdown:
+ /* Nothing to do */
+
+err_disable:
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l84);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 0);
+err_disable_noreset:
+ return ret;
+}
+
+static void cs42l84_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l84_private *cs42l84 = i2c_get_clientdata(i2c_client);
+
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l84);
+
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 0);
+}
+
+static const struct of_device_id cs42l84_of_match[] = {
+ { .compatible = "cirrus,cs42l84", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l84_of_match);
+
+static const struct i2c_device_id cs42l84_id[] = {
+ { "cs42l84" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l84_id);
+
+static struct i2c_driver cs42l84_i2c_driver = {
+ .driver = {
+ .name = "cs42l84",
+ .of_match_table = cs42l84_of_match,
+ },
+ .id_table = cs42l84_id,
+ .probe = cs42l84_i2c_probe,
+ .remove = cs42l84_i2c_remove,
+};
+
+module_i2c_driver(cs42l84_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L84 driver");
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+MODULE_AUTHOR("James Calligeros <jcalligeros99@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l84.h b/sound/soc/codecs/cs42l84.h
new file mode 100644
index 000000000000..dbf778a902b9
--- /dev/null
+++ b/sound/soc/codecs/cs42l84.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on sound/soc/codecs/cs42l42.h
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ */
+
+
+#ifndef __CS42L84_H__
+#define __CS42L84_H__
+
+#include <linux/bits.h>
+
+#define CS42L84_CHIP_ID 0x42a84
+
+#define CS42L84_DEVID 0x0000
+#define CS42L84_REVID 0x73fe
+#define CS42L84_FRZ_CTL 0x0006
+#define CS42L84_FRZ_CTL_ENGAGE BIT(0)
+
+#define CS42L84_TSRS_PLUG_INT_STATUS 0x0400
+#define CS42L84_TSRS_PLUG_INT_MASK 0x0418
+#define CS42L84_RS_PLUG_SHIFT 0
+#define CS42L84_RS_PLUG BIT(0)
+#define CS42L84_RS_UNPLUG BIT(1)
+#define CS42L84_TS_PLUG_SHIFT 2
+#define CS42L84_TS_PLUG BIT(2)
+#define CS42L84_TS_UNPLUG BIT(3)
+#define CS42L84_TSRS_PLUG_VAL_MASK GENMASK(3, 0)
+#define CS42L84_PLL_LOCK_STATUS 0x040e // probably bit 0x10
+#define CS42L84_PLL_LOCK_STATUS_LOCKED BIT(4)
+#define CS42L84_PLL_LOCK_STATUS_ERROR BIT(5)
+
+#define CS42L84_PLUG 3
+#define CS42L84_UNPLUG 0
+#define CS42L84_TRANS 1
+
+#define CS42L84_CCM_CTL1 0x0600
+#define CS42L84_CCM_CTL1_MCLK_SRC GENMASK(1, 0)
+#define CS42L84_CCM_CTL1_MCLK_SRC_RCO 0
+#define CS42L84_CCM_CTL1_MCLK_SRC_MCLK 1
+#define CS42L84_CCM_CTL1_MCLK_SRC_BCLK 2
+#define CS42L84_CCM_CTL1_MCLK_SRC_PLL 3
+#define CS42L84_CCM_CTL1_MCLK_FREQ GENMASK(3, 2)
+#define CS42L84_CCM_CTL1_MCLK_F_12MHZ 0b00
+#define CS42L84_CCM_CTL1_MCLK_F_24MHZ 0b01
+#define CS42L84_CCM_CTL1_MCLK_F_12_288KHZ 0b10
+#define CS42L84_CCM_CTL1_MCLK_F_24_576KHZ 0b11
+#define CS42L84_CCM_CTL1_RCO \
+ (FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_RCO) \
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, CS42L84_CCM_CTL1_MCLK_F_12MHZ))
+
+#define CS42L84_CCM_SAMP_RATE 0x0601
+#define CS42L84_CCM_SAMP_RATE_RATE_48KHZ 4
+#define CS42L84_CCM_SAMP_RATE_RATE_96KHZ 5
+#define CS42L84_CCM_SAMP_RATE_RATE_192KHZ 6
+#define CS42L84_CCM_SAMP_RATE_RATE_44K1HZ 12
+#define CS42L84_CCM_SAMP_RATE_RATE_88K2HZ 13
+#define CS42L84_CCM_SAMP_RATE_RATE_176K4HZ 14
+#define CS42L84_CCM_CTL3 0x0602
+#define CS42L84_CCM_CTL3_REFCLK_DIV GENMASK(2, 1)
+#define CS42L84_CCM_CTL4 0x0603
+#define CS42L84_CCM_CTL4_REFCLK_EN BIT(0)
+
+#define CS42L84_CCM_ASP_CLK_CTRL 0x0608
+
+#define CS42L84_PLL_CTL1 0x0800
+#define CS42L84_PLL_CTL1_EN BIT(0)
+#define CS42L84_PLL_CTL1_MODE GENMASK(2, 1)
+#define CS42L84_PLL_DIV_FRAC0 0x0804
+#define CS42L84_PLL_DIV_FRAC1 0x0805
+#define CS42L84_PLL_DIV_FRAC2 0x0806
+#define CS42L84_PLL_DIV_INT 0x0807
+#define CS42L84_PLL_DIVOUT 0x0808
+
+#define CS42L84_RING_SENSE_CTL 0x1282
+#define CS42L84_RING_SENSE_CTL_INV BIT(7)
+#define CS42L84_RING_SENSE_CTL_UNK1 BIT(6)
+#define CS42L84_RING_SENSE_CTL_FALLTIME GENMASK(5, 3)
+#define CS42L84_RING_SENSE_CTL_RISETIME GENMASK(2, 0)
+#define CS42L84_TIP_SENSE_CTL 0x1283
+#define CS42L84_TIP_SENSE_CTL_INV BIT(7)
+#define CS42L84_TIP_SENSE_CTL_FALLTIME GENMASK(5, 3)
+#define CS42L84_TIP_SENSE_CTL_RISETIME GENMASK(2, 0)
+
+#define CS42L84_TSRS_PLUG_STATUS 0x1288
+
+#define CS42L84_TIP_SENSE_CTL2 0x1473
+#define CS42L84_TIP_SENSE_CTL2_MODE GENMASK(7, 6)
+#define CS42L84_TIP_SENSE_CTL2_MODE_DISABLED 0b00
+#define CS42L84_TIP_SENSE_CTL2_MODE_DIG_INPUT 0b01
+#define CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET 0b11
+#define CS42L84_TIP_SENSE_CTL2_INV BIT(5)
+
+#define CS42L84_MISC_DET_CTL 0x1474
+#define CS42L84_MISC_DET_CTL_DETECT_MODE GENMASK(4, 3)
+#define CS42L84_MISC_DET_CTL_HSBIAS_CTL GENMASK(2, 1)
+#define CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET BIT(0)
+
+#define CS42L84_MIC_DET_CTL1 0x1475
+#define CS42L84_MIC_DET_CTL1_HS_DET_LEVEL GENMASK(5, 0)
+
+#define CS42L84_MIC_DET_CTL4 0x1477
+#define CS42L84_MIC_DET_CTL4_LATCH_TO_VP BIT(1)
+
+#define CS42L84_HS_DET_STATUS2 0x147d
+
+#define CS42L84_MSM_BLOCK_EN1 0x1800
+#define CS42L84_MSM_BLOCK_EN2 0x1801
+#define CS42L84_MSM_BLOCK_EN2_ASP_SHIFT 6
+#define CS42L84_MSM_BLOCK_EN2_BUS_SHIFT 5
+#define CS42L84_MSM_BLOCK_EN2_DAC_SHIFT 4
+#define CS42L84_MSM_BLOCK_EN2_ADC_SHIFT 3
+#define CS42L84_MSM_BLOCK_EN3 0x1802
+#define CS42L84_MSM_BLOCK_EN3_TR_SENSE BIT(3)
+
+#define CS42L84_HS_DET_CTL2 0x1811
+#define CS42L84_HS_DET_CTL2_CTL GENMASK(7, 6)
+#define CS42L84_HS_DET_CTL2_SET GENMASK(5, 4)
+#define CS42L84_HS_DET_CTL2_REF BIT(3)
+#define CS42L84_HS_DET_CTL2_AUTO_TIME GENMASK(1, 0)
+
+#define CS42L84_HS_SWITCH_CTL 0x1812
+#define CS42L84_HS_SWITCH_CTL_REF_HS3 BIT(7)
+#define CS42L84_HS_SWITCH_CTL_REF_HS4 BIT(6)
+#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 BIT(5)
+#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 BIT(4)
+#define CS42L84_HS_SWITCH_CTL_HSB_HS3 BIT(3)
+#define CS42L84_HS_SWITCH_CTL_HSB_HS4 BIT(2)
+#define CS42L84_HS_SWITCH_CTL_GNDHS_HS3 BIT(1)
+#define CS42L84_HS_SWITCH_CTL_GNDHS_HS4 BIT(0)
+
+#define CS42L84_HS_CLAMP_DISABLE 0x1813
+
+#define CS42L84_ADC_CTL1 0x2000
+#define CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT 6
+#define CS42L84_ADC_CTL1_PGA_GAIN_SHIFT 0
+#define CS42L84_ADC_CTL4 0x2003
+#define CS42L84_ADC_CTL4_WNF_CF_SHIFT 4
+#define CS42L84_ADC_CTL4_WNF_EN_SHIFT 3
+#define CS42L84_ADC_CTL4_HPF_CF_SHIFT 1
+#define CS42L84_ADC_CTL4_HPF_EN_SHIFT 0
+
+#define CS42L84_DAC_CTL1 0x3000
+#define CS42L84_DAC_CTL1_UNMUTE BIT(0)
+//#define CS42L84_DAC_CTL1_DACB_INV_SHIFT 1
+//#define CS42L84_DAC_CTL1_DACA_INV_SHIFT 0
+#define CS42L84_DAC_CTL2 0x3001
+
+#define CS42L84_DAC_CHA_VOL_LSB 0x3004
+#define CS42L84_DAC_CHA_VOL_MSB 0x3005
+#define CS42L84_DAC_CHB_VOL_LSB 0x3006
+#define CS42L84_DAC_CHB_VOL_MSB 0x3007
+#define CS42L84_HP_VOL_CTL 0x3020
+#define CS42L84_HP_VOL_CTL_ZERO_CROSS BIT(1)
+#define CS42L84_HP_VOL_CTL_SOFT BIT(0)
+
+#define CS42L84_SRC_ASP_RX_CH1 0b1101
+#define CS42L84_SRC_ASP_RX_CH2 0b1110
+
+#define CS42L84_BUS_ASP_TX_SRC 0x4000
+#define CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT 0
+#define CS42L84_BUS_DAC_SRC 0x4001
+#define CS42L84_BUS_DAC_SRC_DACA_SHIFT 0
+#define CS42L84_BUS_DAC_SRC_DACB_SHIFT 4
+
+#define CS42L84_ASP_CTL 0x5000
+#define CS42L84_ASP_CTL_BCLK_EN_SHIFT 1
+#define CS42L84_ASP_CTL_TDM_MODE BIT(2)
+#define CS42L84_ASP_FSYNC_CTL2 0x5010
+#define CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO GENMASK(7, 1)
+#define CS42L84_ASP_FSYNC_CTL3 0x5011
+#define CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI GENMASK(4, 0)
+#define CS42L84_ASP_DATA_CTL 0x5018
+
+#define CS42L84_ASP_RX_EN 0x5020
+#define CS42L84_ASP_RX_EN_CH1_SHIFT 0
+#define CS42L84_ASP_RX_EN_CH2_SHIFT 1
+#define CS42L84_ASP_TX_EN 0x5024
+#define CS42L84_ASP_TX_EN_CH1_SHIFT 0
+
+#define CS42L84_ASP_RX_CH1_CTL1 0x5028
+#define CS42L84_ASP_RX_CH1_CTL2 0x5029
+#define CS42L84_ASP_RX_CH1_WIDTH 0x502a
+#define CS42L84_ASP_RX_CH2_CTL1 0x502c
+#define CS42L84_ASP_RX_CH2_CTL2 0x502d
+#define CS42L84_ASP_RX_CH2_WIDTH 0x502e
+
+#define CS42L84_ASP_RX_CHx_CTL1_EDGE BIT(0)
+#define CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB GENMASK(7, 1)
+#define CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB GENMASK(2, 0)
+
+#define CS42L84_ASP_TX_CH1_CTL1 0x5068
+#define CS42L84_ASP_TX_CH1_CTL2 0x5069
+#define CS42L84_ASP_TX_CH1_WIDTH 0x506a
+#define CS42L84_ASP_TX_CH2_CTL1 0x506c
+#define CS42L84_ASP_TX_CH2_CTL2 0x506d
+#define CS42L84_ASP_TX_CH2_WIDTH 0x506e
+
+#define CS42L84_DEBOUNCE_TIME_125MS 0b001
+#define CS42L84_DEBOUNCE_TIME_500MS 0b011
+
+#define CS42L84_BOOT_TIME_US 3000
+#define CS42L84_CLOCK_SWITCH_DELAY_US 150
+#define CS42L84_PLL_LOCK_POLL_US 250
+#define CS42L84_PLL_LOCK_TIMEOUT_US 1250
+
+#endif /* __CS42L84_H__ */
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
index 800c1d549347..039b3ecb3b9b 100644
--- a/sound/soc/codecs/cs42xx8-i2c.c
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -12,16 +12,24 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include "cs42xx8.h"
-static int cs42xx8_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cs42xx8_i2c_probe(struct i2c_client *i2c)
{
- int ret = cs42xx8_probe(&i2c->dev,
- devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
+ int ret;
+ struct cs42xx8_driver_data *drvdata;
+
+ drvdata = (struct cs42xx8_driver_data *)i2c_get_match_data(i2c);
+ if (!drvdata)
+ return dev_err_probe(&i2c->dev, -EINVAL,
+ "failed to find driver data\n");
+
+ ret = cs42xx8_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config), drvdata);
if (ret)
return ret;
@@ -31,15 +39,19 @@ static int cs42xx8_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static int cs42xx8_i2c_remove(struct i2c_client *i2c)
+static void cs42xx8_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
pm_runtime_disable(&i2c->dev);
-
- return 0;
}
-static struct i2c_device_id cs42xx8_i2c_id[] = {
+static const struct of_device_id cs42xx8_of_match[] = {
+ { .compatible = "cirrus,cs42448", .data = &cs42448_data, },
+ { .compatible = "cirrus,cs42888", .data = &cs42888_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
+
+static const struct i2c_device_id cs42xx8_i2c_id[] = {
{"cs42448", (kernel_ulong_t)&cs42448_data},
{"cs42888", (kernel_ulong_t)&cs42888_data},
{}
@@ -49,7 +61,7 @@ MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
static struct i2c_driver cs42xx8_i2c_driver = {
.driver = {
.name = "cs42xx8",
- .pm = &cs42xx8_pm,
+ .pm = pm_ptr(&cs42xx8_pm),
.of_match_table = cs42xx8_of_match,
},
.probe = cs42xx8_i2c_probe,
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index d562e1b9a5d1..d14538c49b97 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -13,7 +13,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm_params.h>
@@ -44,6 +44,9 @@ struct cs42xx8_priv {
bool slave_mode;
unsigned long sysclk;
+ u32 tx_channels;
+ struct gpio_desc *gpiod_reset;
+ u32 rate[2];
};
/* -127.5dB to 0dB with step of 0.5dB */
@@ -173,28 +176,34 @@ static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
};
struct cs42xx8_ratios {
- unsigned int ratio;
- unsigned char speed;
- unsigned char mclk;
+ unsigned int mfreq;
+ unsigned int min_mclk;
+ unsigned int max_mclk;
+ unsigned int ratio[3];
};
+/*
+ * According to reference mannual, define the cs42xx8_ratio struct
+ * MFreq2 | MFreq1 | MFreq0 | Description | SSM | DSM | QSM |
+ * 0 | 0 | 0 |1.029MHz to 12.8MHz | 256 | 128 | 64 |
+ * 0 | 0 | 1 |1.536MHz to 19.2MHz | 384 | 192 | 96 |
+ * 0 | 1 | 0 |2.048MHz to 25.6MHz | 512 | 256 | 128 |
+ * 0 | 1 | 1 |3.072MHz to 38.4MHz | 768 | 384 | 192 |
+ * 1 | x | x |4.096MHz to 51.2MHz |1024 | 512 | 256 |
+ */
static const struct cs42xx8_ratios cs42xx8_ratios[] = {
- { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) },
- { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) },
- { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) },
- { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) },
- { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) },
- { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) },
- { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) },
- { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) },
- { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) }
+ { 0, 1029000, 12800000, {256, 128, 64} },
+ { 2, 1536000, 19200000, {384, 192, 96} },
+ { 4, 2048000, 25600000, {512, 256, 128} },
+ { 6, 3072000, 38400000, {768, 384, 192} },
+ { 8, 4096000, 51200000, {1024, 512, 256} },
};
static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
cs42xx8->sysclk = freq;
@@ -204,8 +213,8 @@ static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
u32 val;
/* Set DAI format */
@@ -223,7 +232,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
break;
default:
- dev_err(codec->dev, "unsupported dai format\n");
+ dev_err(component->dev, "unsupported dai format\n");
return -EINVAL;
}
@@ -233,14 +242,14 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* Set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42xx8->slave_mode = true;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42xx8->slave_mode = false;
break;
default:
- dev_err(codec->dev, "unsupported master/slave mode\n");
+ dev_err(component->dev, "unsupported master/slave mode\n");
return -EINVAL;
}
@@ -251,41 +260,116 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u32 ratio = cs42xx8->sysclk / params_rate(params);
- u32 i, fm, val, mask;
+ u32 ratio[2];
+ u32 rate[2];
+ u32 fm[2];
+ u32 i, val, mask;
+ bool condition1, condition2;
+
+ if (tx)
+ cs42xx8->tx_channels = params_channels(params);
+
+ rate[tx] = params_rate(params);
+ rate[!tx] = cs42xx8->rate[!tx];
+
+ ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0;
+ ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0;
+
+ /* Get functional mode for tx and rx according to rate */
+ for (i = 0; i < 2; i++) {
+ if (cs42xx8->slave_mode) {
+ fm[i] = CS42XX8_FM_AUTO;
+ } else {
+ if (rate[i] < 50000) {
+ fm[i] = CS42XX8_FM_SINGLE;
+ } else if (rate[i] > 50000 && rate[i] < 100000) {
+ fm[i] = CS42XX8_FM_DOUBLE;
+ } else if (rate[i] > 100000 && rate[i] < 200000) {
+ fm[i] = CS42XX8_FM_QUAD;
+ } else {
+ dev_err(component->dev,
+ "unsupported sample rate\n");
+ return -EINVAL;
+ }
+ }
+ }
for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
- if (cs42xx8_ratios[i].ratio == ratio)
+ /* Is the ratio[tx] valid ? */
+ condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ?
+ (cs42xx8_ratios[i].ratio[0] == ratio[tx] ||
+ cs42xx8_ratios[i].ratio[1] == ratio[tx] ||
+ cs42xx8_ratios[i].ratio[2] == ratio[tx]) :
+ (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) &&
+ cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk &&
+ cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk;
+
+ if (!ratio[tx])
+ condition1 = true;
+
+ /* Is the ratio[!tx] valid ? */
+ condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ?
+ (cs42xx8_ratios[i].ratio[0] == ratio[!tx] ||
+ cs42xx8_ratios[i].ratio[1] == ratio[!tx] ||
+ cs42xx8_ratios[i].ratio[2] == ratio[!tx]) :
+ (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx]));
+
+ if (!ratio[!tx])
+ condition2 = true;
+
+ /*
+ * Both ratio[tx] and ratio[!tx] is valid, then we get
+ * a proper MFreq.
+ */
+ if (condition1 && condition2)
break;
}
if (i == ARRAY_SIZE(cs42xx8_ratios)) {
- dev_err(codec->dev, "unsupported sysclk ratio\n");
+ dev_err(component->dev, "unsupported sysclk ratio\n");
return -EINVAL;
}
- mask = CS42XX8_FUNCMOD_MFREQ_MASK;
- val = cs42xx8_ratios[i].mclk;
+ cs42xx8->rate[tx] = params_rate(params);
- fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
+ mask = CS42XX8_FUNCMOD_MFREQ_MASK;
+ val = cs42xx8_ratios[i].mfreq;
regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
- CS42XX8_FUNCMOD_xC_FM(tx, fm) | val);
+ CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val);
return 0;
}
-static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42xx8_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ /* Clear stored rate */
+ cs42xx8->rate[tx] = 0;
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
+ CS42XX8_FUNCMOD_xC_FM_MASK(tx),
+ CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO));
+ return 0;
+}
+
+static int cs42xx8_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ u8 dac_unmute = cs42xx8->tx_channels ?
+ ~((0x1 << cs42xx8->tx_channels) - 1) : 0;
- regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
- CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
+ regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
+ mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
return 0;
}
@@ -294,7 +378,9 @@ static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
.set_fmt = cs42xx8_set_dai_fmt,
.set_sysclk = cs42xx8_set_dai_sysclk,
.hw_params = cs42xx8_hw_params,
- .digital_mute = cs42xx8_digital_mute,
+ .hw_free = cs42xx8_hw_free,
+ .mute_stream = cs42xx8_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42xx8_dai = {
@@ -315,7 +401,6 @@ static struct snd_soc_dai_driver cs42xx8_dai = {
};
static const struct reg_default cs42xx8_reg[] = {
- { 0x01, 0x01 }, /* Chip I.D. and Revision Register */
{ 0x02, 0x00 }, /* Power Control */
{ 0x03, 0xF0 }, /* Functional Mode */
{ 0x04, 0x46 }, /* Interface Formats */
@@ -373,18 +458,18 @@ const struct regmap_config cs42xx8_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
.volatile_reg = cs42xx8_volatile_register,
.writeable_reg = cs42xx8_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
-static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
+static int cs42xx8_component_probe(struct snd_soc_component *component)
{
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (cs42xx8->drvdata->num_adcs) {
case 3:
- snd_soc_add_codec_controls(codec, cs42xx8_adc3_snd_controls,
+ snd_soc_add_component_controls(component, cs42xx8_adc3_snd_controls,
ARRAY_SIZE(cs42xx8_adc3_snd_controls));
snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets,
ARRAY_SIZE(cs42xx8_adc3_dapm_widgets));
@@ -401,16 +486,16 @@ static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver cs42xx8_driver = {
- .probe = cs42xx8_codec_probe,
- .idle_bias_off = true,
-
- .controls = cs42xx8_snd_controls,
- .num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
- .dapm_widgets = cs42xx8_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
- .dapm_routes = cs42xx8_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
+static const struct snd_soc_component_driver cs42xx8_driver = {
+ .probe = cs42xx8_component_probe,
+ .controls = cs42xx8_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
+ .dapm_widgets = cs42xx8_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
+ .dapm_routes = cs42xx8_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
const struct cs42xx8_driver_data cs42448_data = {
@@ -425,17 +510,8 @@ const struct cs42xx8_driver_data cs42888_data = {
};
EXPORT_SYMBOL_GPL(cs42888_data);
-const struct of_device_id cs42xx8_of_match[] = {
- { .compatible = "cirrus,cs42448", .data = &cs42448_data, },
- { .compatible = "cirrus,cs42888", .data = &cs42888_data, },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
-EXPORT_SYMBOL_GPL(cs42xx8_of_match);
-
-int cs42xx8_probe(struct device *dev, struct regmap *regmap)
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata)
{
- const struct of_device_id *of_id;
struct cs42xx8_priv *cs42xx8;
int ret, val, i;
@@ -449,17 +525,18 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
if (cs42xx8 == NULL)
return -ENOMEM;
- cs42xx8->regmap = regmap;
dev_set_drvdata(dev, cs42xx8);
- of_id = of_match_device(cs42xx8_of_match, dev);
- if (of_id)
- cs42xx8->drvdata = of_id->data;
+ cs42xx8->regmap = regmap;
- if (!cs42xx8->drvdata) {
- dev_err(dev, "failed to find driver data\n");
- return -EINVAL;
- }
+ cs42xx8->drvdata = drvdata;
+
+ cs42xx8->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs42xx8->gpiod_reset))
+ return PTR_ERR(cs42xx8->gpiod_reset);
+
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0);
cs42xx8->clk = devm_clk_get(dev, "mclk");
if (IS_ERR(cs42xx8->clk)) {
@@ -490,13 +567,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
/* Make sure hardware reset done */
msleep(5);
- /*
- * 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) {
@@ -515,16 +585,14 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
dev_info(dev, "found device, revision %X\n",
val & CS42XX8_CHIPID_REV_ID_MASK);
- regcache_cache_bypass(cs42xx8->regmap, false);
-
cs42xx8_dai.name = cs42xx8->drvdata->name;
/* Each adc supports stereo input */
cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
- ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
if (ret) {
- dev_err(dev, "failed to register codec:%d\n", ret);
+ dev_err(dev, "failed to register component:%d\n", ret);
goto err_enable;
}
@@ -538,7 +606,6 @@ err_enable:
}
EXPORT_SYMBOL_GPL(cs42xx8_probe);
-#ifdef CONFIG_PM
static int cs42xx8_runtime_resume(struct device *dev)
{
struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
@@ -550,6 +617,8 @@ static int cs42xx8_runtime_resume(struct device *dev)
return ret;
}
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0);
+
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
if (ret) {
@@ -561,6 +630,7 @@ static int cs42xx8_runtime_resume(struct device *dev)
msleep(5);
regcache_cache_only(cs42xx8->regmap, false);
+ regcache_mark_dirty(cs42xx8->regmap);
ret = regcache_sync(cs42xx8->regmap);
if (ret) {
@@ -588,16 +658,17 @@ static int cs42xx8_runtime_suspend(struct device *dev)
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 1);
+
clk_disable_unprepare(cs42xx8->clk);
return 0;
}
-#endif
-const struct dev_pm_ops cs42xx8_pm = {
- SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(cs42xx8_pm) = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
};
-EXPORT_SYMBOL_GPL(cs42xx8_pm);
MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h
index d36c61b6df74..342389e8b1a8 100644
--- a/sound/soc/codecs/cs42xx8.h
+++ b/sound/soc/codecs/cs42xx8.h
@@ -22,8 +22,7 @@ extern const struct dev_pm_ops cs42xx8_pm;
extern const struct cs42xx8_driver_data cs42448_data;
extern const struct cs42xx8_driver_data cs42888_data;
extern const struct regmap_config cs42xx8_regmap_config;
-extern const struct of_device_id cs42xx8_of_match[];
-int cs42xx8_probe(struct device *dev, struct regmap *regmap);
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata);
/* CS42888 register map */
#define CS42XX8_CHIPID 0x01 /* Chip ID */
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
new file mode 100644
index 000000000000..a3bdaac9c059
--- /dev/null
+++ b/sound/soc/codecs/cs43130.c
@@ -0,0 +1,2781 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs43130.c -- CS43130 ALSA Soc Audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Authors: Li Xu <li.xu@cirrus.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <sound/jack.h>
+
+#include "cs43130.h"
+#include "cirrus_legacy.h"
+
+static const struct reg_default cs43130_reg_defaults[] = {
+ {CS43130_SYS_CLK_CTL_1, 0x06},
+ {CS43130_SP_SRATE, 0x01},
+ {CS43130_SP_BITSIZE, 0x05},
+ {CS43130_PAD_INT_CFG, 0x03},
+ {CS43130_PWDN_CTL, 0xFE},
+ {CS43130_CRYSTAL_SET, 0x04},
+ {CS43130_PLL_SET_1, 0x00},
+ {CS43130_PLL_SET_2, 0x00},
+ {CS43130_PLL_SET_3, 0x00},
+ {CS43130_PLL_SET_4, 0x00},
+ {CS43130_PLL_SET_5, 0x40},
+ {CS43130_PLL_SET_6, 0x10},
+ {CS43130_PLL_SET_7, 0x80},
+ {CS43130_PLL_SET_8, 0x03},
+ {CS43130_PLL_SET_9, 0x02},
+ {CS43130_PLL_SET_10, 0x02},
+ {CS43130_CLKOUT_CTL, 0x00},
+ {CS43130_ASP_NUM_1, 0x01},
+ {CS43130_ASP_NUM_2, 0x00},
+ {CS43130_ASP_DEN_1, 0x08},
+ {CS43130_ASP_DEN_2, 0x00},
+ {CS43130_ASP_LRCK_HI_TIME_1, 0x1F},
+ {CS43130_ASP_LRCK_HI_TIME_2, 0x00},
+ {CS43130_ASP_LRCK_PERIOD_1, 0x3F},
+ {CS43130_ASP_LRCK_PERIOD_2, 0x00},
+ {CS43130_ASP_CLOCK_CONF, 0x0C},
+ {CS43130_ASP_FRAME_CONF, 0x0A},
+ {CS43130_XSP_NUM_1, 0x01},
+ {CS43130_XSP_NUM_2, 0x00},
+ {CS43130_XSP_DEN_1, 0x02},
+ {CS43130_XSP_DEN_2, 0x00},
+ {CS43130_XSP_LRCK_HI_TIME_1, 0x1F},
+ {CS43130_XSP_LRCK_HI_TIME_2, 0x00},
+ {CS43130_XSP_LRCK_PERIOD_1, 0x3F},
+ {CS43130_XSP_LRCK_PERIOD_2, 0x00},
+ {CS43130_XSP_CLOCK_CONF, 0x0C},
+ {CS43130_XSP_FRAME_CONF, 0x0A},
+ {CS43130_ASP_CH_1_LOC, 0x00},
+ {CS43130_ASP_CH_2_LOC, 0x00},
+ {CS43130_ASP_CH_1_SZ_EN, 0x06},
+ {CS43130_ASP_CH_2_SZ_EN, 0x0E},
+ {CS43130_XSP_CH_1_LOC, 0x00},
+ {CS43130_XSP_CH_2_LOC, 0x00},
+ {CS43130_XSP_CH_1_SZ_EN, 0x06},
+ {CS43130_XSP_CH_2_SZ_EN, 0x0E},
+ {CS43130_DSD_VOL_B, 0x78},
+ {CS43130_DSD_VOL_A, 0x78},
+ {CS43130_DSD_PATH_CTL_1, 0xA8},
+ {CS43130_DSD_INT_CFG, 0x00},
+ {CS43130_DSD_PATH_CTL_2, 0x02},
+ {CS43130_DSD_PCM_MIX_CTL, 0x00},
+ {CS43130_DSD_PATH_CTL_3, 0x40},
+ {CS43130_HP_OUT_CTL_1, 0x30},
+ {CS43130_PCM_FILT_OPT, 0x02},
+ {CS43130_PCM_VOL_B, 0x78},
+ {CS43130_PCM_VOL_A, 0x78},
+ {CS43130_PCM_PATH_CTL_1, 0xA8},
+ {CS43130_PCM_PATH_CTL_2, 0x00},
+ {CS43130_CLASS_H_CTL, 0x1E},
+ {CS43130_HP_DETECT, 0x04},
+ {CS43130_HP_LOAD_1, 0x00},
+ {CS43130_HP_MEAS_LOAD_1, 0x00},
+ {CS43130_HP_MEAS_LOAD_2, 0x00},
+ {CS43130_INT_MASK_1, 0xFF},
+ {CS43130_INT_MASK_2, 0xFF},
+ {CS43130_INT_MASK_3, 0xFF},
+ {CS43130_INT_MASK_4, 0xFF},
+ {CS43130_INT_MASK_5, 0xFF},
+};
+
+static bool cs43130_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_HP_DC_STAT_1 ... CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1 ... CS43130_HP_AC_STAT_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1:
+ case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG:
+ case CS43130_PWDN_CTL:
+ case CS43130_CRYSTAL_SET:
+ case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5:
+ case CS43130_PLL_SET_6:
+ case CS43130_PLL_SET_7:
+ case CS43130_PLL_SET_8:
+ case CS43130_PLL_SET_9:
+ case CS43130_PLL_SET_10:
+ case CS43130_CLKOUT_CTL:
+ case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF:
+ case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF:
+ case CS43130_ASP_CH_1_LOC:
+ case CS43130_ASP_CH_2_LOC:
+ case CS43130_ASP_CH_1_SZ_EN:
+ case CS43130_ASP_CH_2_SZ_EN:
+ case CS43130_XSP_CH_1_LOC:
+ case CS43130_XSP_CH_2_LOC:
+ case CS43130_XSP_CH_1_SZ_EN:
+ case CS43130_XSP_CH_2_SZ_EN:
+ case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3:
+ case CS43130_HP_OUT_CTL_1:
+ case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2:
+ case CS43130_CLASS_H_CTL:
+ case CS43130_HP_DETECT:
+ case CS43130_HP_STATUS:
+ case CS43130_HP_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_2:
+ case CS43130_HP_DC_STAT_1:
+ case CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1:
+ case CS43130_HP_AC_STAT_2:
+ case CS43130_HP_LOAD_STAT:
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct cs43130_pll_params {
+ unsigned int pll_in;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ unsigned int pll_out;
+ u8 pll_cal_ratio;
+};
+
+static const struct cs43130_pll_params pll_ratio_table[] = {
+ {9600000, 0x02, 0x49, 0x800000, 0x00, 0x08, 22579200, 151},
+ {9600000, 0x02, 0x50, 0x000000, 0x00, 0x08, 24576000, 164},
+
+ {11289600, 0x02, 0X40, 0, 0x01, 0x08, 22579200, 128},
+ {11289600, 0x02, 0x44, 0x06F700, 0x0, 0x08, 24576000, 139},
+
+ {12000000, 0x02, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120},
+ {12000000, 0x02, 0x40, 0x000000, 0x00, 0x08, 24576000, 131},
+
+ {12288000, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118},
+ {12288000, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128},
+
+ {13000000, 0x02, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111},
+ {13000000, 0x02, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121},
+
+ {19200000, 0x03, 0x49, 0x800000, 0x00, 0x08, 22579200, 151},
+ {19200000, 0x03, 0x50, 0x000000, 0x00, 0x08, 24576000, 164},
+
+ {22579200, 0, 0, 0, 0, 0, 22579200, 0},
+ {22579200, 0x03, 0x44, 0x06F700, 0x00, 0x08, 24576000, 139},
+
+ {24000000, 0x03, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120},
+ {24000000, 0x03, 0x40, 0x000000, 0x00, 0x08, 24576000, 131},
+
+ {24576000, 0x03, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118},
+ {24576000, 0, 0, 0, 0, 0, 24576000, 0},
+
+ {26000000, 0x03, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111},
+ {26000000, 0x03, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121},
+};
+
+static const struct cs43130_pll_params *cs43130_get_pll_table(
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].pll_in == freq_in &&
+ pll_ratio_table[i].pll_out == freq_out)
+ return &pll_ratio_table[i];
+ }
+
+ return NULL;
+}
+
+static int cs43130_pll_config(struct snd_soc_component *component)
+{
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ const struct cs43130_pll_params *pll_entry;
+
+ dev_dbg(cs43130->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n",
+ cs43130->mclk, cs43130->mclk_int);
+
+ pll_entry = cs43130_get_pll_table(cs43130->mclk, cs43130->mclk_int);
+ if (!pll_entry)
+ return -EINVAL;
+
+ if (pll_entry->pll_cal_ratio == 0) {
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK, 0);
+
+ cs43130->pll_bypass = true;
+ return 0;
+ }
+
+ cs43130->pll_bypass = false;
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_2,
+ CS43130_PLL_DIV_DATA_MASK,
+ pll_entry->pll_div_frac >>
+ CS43130_PLL_DIV_FRAC_0_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_3,
+ CS43130_PLL_DIV_DATA_MASK,
+ pll_entry->pll_div_frac >>
+ CS43130_PLL_DIV_FRAC_1_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_4,
+ CS43130_PLL_DIV_DATA_MASK,
+ pll_entry->pll_div_frac >>
+ CS43130_PLL_DIV_FRAC_2_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_5,
+ pll_entry->pll_div_int);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_6, pll_entry->pll_divout);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_7,
+ pll_entry->pll_cal_ratio);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_8,
+ CS43130_PLL_MODE_MASK,
+ pll_entry->pll_mode << CS43130_PLL_MODE_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_9,
+ pll_entry->sclk_prediv);
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK, 1);
+
+ return 0;
+}
+
+static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (freq_in) {
+ case 9600000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 19200000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs43130->mclk = freq_in;
+ break;
+ default:
+ dev_err(cs43130->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_out) {
+ case 22579200:
+ cs43130->mclk_int = freq_out;
+ break;
+ case 24576000:
+ cs43130->mclk_int = freq_out;
+ break;
+ default:
+ dev_err(cs43130->dev,
+ "unsupported pll output ref clock: %u\n", freq_out);
+ return -EINVAL;
+ }
+
+ ret = cs43130_pll_config(component);
+ dev_dbg(cs43130->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
+ return ret;
+}
+
+static int cs43130_wait_for_completion(struct cs43130_private *cs43130, struct completion *to_poll,
+ int time)
+{
+ int stickies, offset, flag, ret;
+
+ if (cs43130->has_irq_line) {
+ ret = wait_for_completion_timeout(to_poll, msecs_to_jiffies(time));
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else
+ return 0; // Discard number of jiffies left till timeout and return success
+ }
+
+ if (to_poll == &cs43130->xtal_rdy) {
+ offset = 0;
+ flag = CS43130_XTAL_RDY_INT;
+ } else if (to_poll == &cs43130->pll_rdy) {
+ offset = 0;
+ flag = CS43130_PLL_RDY_INT;
+ } else {
+ return -EINVAL;
+ }
+
+ return regmap_read_poll_timeout(cs43130->regmap, CS43130_INT_STATUS_1 + offset,
+ stickies, (stickies & flag),
+ 1000, time * 1000);
+}
+
+static int cs43130_change_clksrc(struct snd_soc_component *component,
+ enum cs43130_mclk_src_sel src)
+{
+ int ret;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ int mclk_int_decoded;
+
+ if (src == cs43130->mclk_int_src) {
+ /* clk source has not changed */
+ return 0;
+ }
+
+ switch (cs43130->mclk_int) {
+ case CS43130_MCLK_22M:
+ mclk_int_decoded = CS43130_MCLK_22P5;
+ break;
+ case CS43130_MCLK_24M:
+ mclk_int_decoded = CS43130_MCLK_24P5;
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int);
+ return -EINVAL;
+ }
+
+ switch (src) {
+ case CS43130_MCLK_SRC_EXT:
+ cs43130->pll_bypass = true;
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_EXT;
+ if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) {
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 1 << CS43130_PDN_XTAL_SHIFT);
+ } else {
+ reinit_completion(&cs43130->xtal_rdy);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 0);
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK,
+ 1 << CS43130_XTAL_RDY_INT_SHIFT);
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
+ }
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK,
+ src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK,
+ mclk_int_decoded << CS43130_MCLK_INT_SHIFT);
+ usleep_range(150, 200);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK,
+ 1 << CS43130_PDN_PLL_SHIFT);
+ break;
+ case CS43130_MCLK_SRC_PLL:
+ cs43130->pll_bypass = false;
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_PLL;
+ if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) {
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 1 << CS43130_PDN_XTAL_SHIFT);
+ } else {
+ reinit_completion(&cs43130->xtal_rdy);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 0);
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK,
+ 1 << CS43130_XTAL_RDY_INT_SHIFT);
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
+ }
+ }
+
+ reinit_completion(&cs43130->pll_rdy);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_PLL_RDY_INT_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK, 0);
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->pll_rdy, 100);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_PLL_RDY_INT_MASK,
+ 1 << CS43130_PLL_RDY_INT_SHIFT);
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for PLL_READY interrupt: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK,
+ src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK,
+ mclk_int_decoded << CS43130_MCLK_INT_SHIFT);
+ usleep_range(150, 200);
+ break;
+ case CS43130_MCLK_SRC_RCO:
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK,
+ src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK,
+ CS43130_MCLK_22P5 << CS43130_MCLK_INT_SHIFT);
+ usleep_range(150, 200);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 1 << CS43130_PDN_XTAL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK,
+ 1 << CS43130_PDN_PLL_SHIFT);
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid MCLK source value\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct cs43130_bitwidth_map cs43130_bitwidth_table[] = {
+ {8, CS43130_SP_BIT_SIZE_8, CS43130_CH_BIT_SIZE_8},
+ {16, CS43130_SP_BIT_SIZE_16, CS43130_CH_BIT_SIZE_16},
+ {24, CS43130_SP_BIT_SIZE_24, CS43130_CH_BIT_SIZE_24},
+ {32, CS43130_SP_BIT_SIZE_32, CS43130_CH_BIT_SIZE_32},
+};
+
+static const struct cs43130_bitwidth_map *cs43130_get_bitwidth_table(
+ unsigned int bitwidth)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs43130_bitwidth_table); i++) {
+ if (cs43130_bitwidth_table[i].bitwidth == bitwidth)
+ return &cs43130_bitwidth_table[i];
+ }
+
+ return NULL;
+}
+
+static int cs43130_set_bitwidth(int dai_id, unsigned int bitwidth_dai,
+ struct regmap *regmap)
+{
+ const struct cs43130_bitwidth_map *bw_map;
+
+ bw_map = cs43130_get_bitwidth_table(bitwidth_dai);
+ if (!bw_map)
+ return -EINVAL;
+
+ switch (dai_id) {
+ case CS43130_ASP_PCM_DAI:
+ case CS43130_ASP_DOP_DAI:
+ regmap_update_bits(regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_SP_BITSIZE,
+ CS43130_ASP_BITSIZE_MASK, bw_map->sp_bit);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_update_bits(regmap, CS43130_XSP_CH_1_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_XSP_CH_2_SZ_EN,
+ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+ regmap_update_bits(regmap, CS43130_SP_BITSIZE,
+ CS43130_XSP_BITSIZE_MASK, bw_map->sp_bit <<
+ CS43130_XSP_BITSIZE_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct cs43130_rate_map cs43130_rate_table[] = {
+ {32000, CS43130_ASP_SPRATE_32K},
+ {44100, CS43130_ASP_SPRATE_44_1K},
+ {48000, CS43130_ASP_SPRATE_48K},
+ {88200, CS43130_ASP_SPRATE_88_2K},
+ {96000, CS43130_ASP_SPRATE_96K},
+ {176400, CS43130_ASP_SPRATE_176_4K},
+ {192000, CS43130_ASP_SPRATE_192K},
+ {352800, CS43130_ASP_SPRATE_352_8K},
+ {384000, CS43130_ASP_SPRATE_384K},
+};
+
+static const struct cs43130_rate_map *cs43130_get_rate_table(int fs)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs43130_rate_table); i++) {
+ if (cs43130_rate_table[i].fs == fs)
+ return &cs43130_rate_table[i];
+ }
+
+ return NULL;
+}
+
+static const struct cs43130_clk_gen *cs43130_get_clk_gen(int mclk_int, int fs,
+ const struct cs43130_clk_gen *clk_gen_table, int len_clk_gen_table)
+{
+ int i;
+
+ for (i = 0; i < len_clk_gen_table; i++) {
+ if (clk_gen_table[i].mclk_int == mclk_int &&
+ clk_gen_table[i].fs == fs)
+ return &clk_gen_table[i];
+ }
+
+ return NULL;
+}
+
+static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
+ struct snd_pcm_hw_params *params,
+ struct cs43130_private *cs43130)
+{
+ u16 frm_size;
+ u16 hi_size;
+ u8 frm_delay;
+ u8 frm_phase;
+ u8 frm_data;
+ u8 sclk_edge;
+ u8 lrck_edge;
+ u8 clk_data;
+ u8 loc_ch1;
+ u8 loc_ch2;
+ u8 dai_mode_val;
+ const struct cs43130_clk_gen *clk_gen;
+
+ switch (cs43130->dais[dai_id].dai_format) {
+ case SND_SOC_DAIFMT_I2S:
+ hi_size = bitwidth_sclk;
+ frm_delay = 2;
+ frm_phase = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ hi_size = bitwidth_sclk;
+ frm_delay = 0;
+ frm_phase = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ hi_size = 1;
+ frm_delay = 2;
+ frm_phase = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ hi_size = 1;
+ frm_delay = 0;
+ frm_phase = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cs43130->dais[dai_id].dai_invert) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sclk_edge = 1;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sclk_edge = 0;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sclk_edge = 1;
+ lrck_edge = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ sclk_edge = 0;
+ lrck_edge = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cs43130->dais[dai_id].dai_mode) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dai_mode_val = 0;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dai_mode_val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ frm_size = bitwidth_sclk * params_channels(params);
+ loc_ch1 = 0;
+ loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
+
+ frm_data = frm_delay & CS43130_SP_FSD_MASK;
+ frm_data |= (frm_phase << CS43130_SP_STP_SHIFT) & CS43130_SP_STP_MASK;
+
+ clk_data = lrck_edge & CS43130_SP_LCPOL_IN_MASK;
+ clk_data |= (lrck_edge << CS43130_SP_LCPOL_OUT_SHIFT) &
+ CS43130_SP_LCPOL_OUT_MASK;
+ clk_data |= (sclk_edge << CS43130_SP_SCPOL_IN_SHIFT) &
+ CS43130_SP_SCPOL_IN_MASK;
+ clk_data |= (sclk_edge << CS43130_SP_SCPOL_OUT_SHIFT) &
+ CS43130_SP_SCPOL_OUT_MASK;
+ clk_data |= (dai_mode_val << CS43130_SP_MODE_SHIFT) &
+ CS43130_SP_MODE_MASK;
+
+ switch (dai_id) {
+ case CS43130_ASP_PCM_DAI:
+ case CS43130_ASP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_MSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_FRAME_CONF, frm_data);
+ regmap_write(cs43130->regmap, CS43130_ASP_CH_1_LOC, loc_ch1);
+ regmap_write(cs43130->regmap, CS43130_ASP_CH_2_LOC, loc_ch2);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_CLOCK_CONF, clk_data);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_1,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_2,
+ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+ CS43130_SP_LCPR_MSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_1,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_LSB_DATA_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_2,
+ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+ CS43130_SP_LCHI_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_FRAME_CONF, frm_data);
+ regmap_write(cs43130->regmap, CS43130_XSP_CH_1_LOC, loc_ch1);
+ regmap_write(cs43130->regmap, CS43130_XSP_CH_2_LOC, loc_ch2);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_1_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_2_SZ_EN,
+ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_CLOCK_CONF, clk_data);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (frm_size) {
+ case 16:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_16_clk_gen,
+ ARRAY_SIZE(cs43130_16_clk_gen));
+ break;
+ case 32:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_32_clk_gen,
+ ARRAY_SIZE(cs43130_32_clk_gen));
+ break;
+ case 48:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_48_clk_gen,
+ ARRAY_SIZE(cs43130_48_clk_gen));
+ break;
+ case 64:
+ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+ params_rate(params),
+ cs43130_64_clk_gen,
+ ARRAY_SIZE(cs43130_64_clk_gen));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!clk_gen)
+ return -EINVAL;
+
+ switch (dai_id) {
+ case CS43130_ASP_PCM_DAI:
+ case CS43130_ASP_DOP_DAI:
+ regmap_write(cs43130->regmap, CS43130_ASP_DEN_1,
+ (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
+ CS43130_SP_M_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_DEN_2,
+ (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
+ CS43130_SP_M_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_1,
+ (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
+ CS43130_SP_N_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_2,
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
+ CS43130_SP_N_MSB_DATA_SHIFT);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_write(cs43130->regmap, CS43130_XSP_DEN_1,
+ (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
+ CS43130_SP_M_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_DEN_2,
+ (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
+ CS43130_SP_M_MSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_NUM_1,
+ (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
+ CS43130_SP_N_LSB_DATA_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_XSP_NUM_2,
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
+ CS43130_SP_N_MSB_DATA_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs43130_pcm_dsd_mix(bool en, struct regmap *regmap)
+{
+ if (en) {
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_PREP_MASK,
+ 1 << CS43130_MIX_PCM_PREP_SHIFT);
+ usleep_range(6000, 6050);
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_DSD_MASK,
+ 1 << CS43130_MIX_PCM_DSD_SHIFT);
+ } else {
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_DSD_MASK,
+ 0 << CS43130_MIX_PCM_DSD_SHIFT);
+ usleep_range(1600, 1650);
+ regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL,
+ CS43130_MIX_PCM_PREP_MASK,
+ 0 << CS43130_MIX_PCM_PREP_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ unsigned int required_clk;
+ u8 dsd_speed;
+
+ mutex_lock(&cs43130->clk_mutex);
+ if (!cs43130->clk_req) {
+ /* no DAI is currently using clk */
+ if (!(CS43130_MCLK_22M % params_rate(params)))
+ required_clk = CS43130_MCLK_22M;
+ else
+ required_clk = CS43130_MCLK_24M;
+
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+ else
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+ }
+
+ cs43130->clk_req++;
+ if (cs43130->clk_req == 2)
+ cs43130_pcm_dsd_mix(true, cs43130->regmap);
+ mutex_unlock(&cs43130->clk_mutex);
+
+ switch (params_rate(params)) {
+ case 176400:
+ dsd_speed = 0;
+ break;
+ case 352800:
+ dsd_speed = 1;
+ break;
+ default:
+ dev_err(cs43130->dev, "Rate(%u) not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG,
+ CS43130_DSD_MASTER, CS43130_DSD_MASTER);
+ else
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG,
+ CS43130_DSD_MASTER, 0);
+
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SPEED_MASK,
+ dsd_speed << CS43130_DSD_SPEED_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_DSD <<
+ CS43130_DSD_SRC_SHIFT);
+
+ return 0;
+}
+
+static int cs43130_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ const struct cs43130_rate_map *rate_map;
+ unsigned int sclk = cs43130->dais[dai->id].sclk;
+ unsigned int bitwidth_sclk;
+ unsigned int bitwidth_dai = (unsigned int)(params_width(params));
+ unsigned int required_clk;
+ u8 dsd_speed;
+
+ mutex_lock(&cs43130->clk_mutex);
+ if (!cs43130->clk_req) {
+ /* no DAI is currently using clk */
+ if (!(CS43130_MCLK_22M % params_rate(params)))
+ required_clk = CS43130_MCLK_22M;
+ else
+ required_clk = CS43130_MCLK_24M;
+
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+ else
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+ }
+
+ cs43130->clk_req++;
+ if (cs43130->clk_req == 2)
+ cs43130_pcm_dsd_mix(true, cs43130->regmap);
+ mutex_unlock(&cs43130->clk_mutex);
+
+ switch (dai->id) {
+ case CS43130_ASP_DOP_DAI:
+ case CS43130_XSP_DOP_DAI:
+ /* DoP bitwidth is always 24-bit */
+ bitwidth_dai = 24;
+ sclk = params_rate(params) * bitwidth_dai *
+ params_channels(params);
+
+ switch (params_rate(params)) {
+ case 176400:
+ dsd_speed = 0;
+ break;
+ case 352800:
+ dsd_speed = 1;
+ break;
+ default:
+ dev_err(cs43130->dev, "Rate(%u) not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SPEED_MASK,
+ dsd_speed << CS43130_DSD_SPEED_SHIFT);
+ break;
+ case CS43130_ASP_PCM_DAI:
+ rate_map = cs43130_get_rate_table(params_rate(params));
+ if (!rate_map)
+ return -EINVAL;
+
+ regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val);
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid DAI (%d)\n", dai->id);
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case CS43130_ASP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_ASP <<
+ CS43130_DSD_SRC_SHIFT);
+ break;
+ case CS43130_XSP_DOP_DAI:
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP <<
+ CS43130_DSD_SRC_SHIFT);
+ break;
+ }
+
+ if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
+ /* Calculate SCLK in master mode if unassigned */
+ sclk = params_rate(params) * bitwidth_dai *
+ params_channels(params);
+
+ if (!sclk) {
+ /* at this point, SCLK must be set */
+ dev_err(cs43130->dev, "SCLK freq is not set\n");
+ return -EINVAL;
+ }
+
+ bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params);
+ if (bitwidth_sclk < bitwidth_dai) {
+ dev_err(cs43130->dev, "Format not supported: SCLK freq is too low\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(cs43130->dev,
+ "sclk = %u, fs = %d, bitwidth_dai = %u\n",
+ sclk, params_rate(params), bitwidth_dai);
+
+ dev_dbg(cs43130->dev,
+ "bitwidth_sclk = %u, num_ch = %u\n",
+ bitwidth_sclk, params_channels(params));
+
+ cs43130_set_bitwidth(dai->id, bitwidth_dai, cs43130->regmap);
+ cs43130_set_sp_fmt(dai->id, bitwidth_sclk, params, cs43130);
+
+ return 0;
+}
+
+static int cs43130_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&cs43130->clk_mutex);
+ cs43130->clk_req--;
+ if (!cs43130->clk_req) {
+ /* no DAI is currently using clk */
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO);
+ cs43130_pcm_dsd_mix(false, cs43130->regmap);
+ }
+ mutex_unlock(&cs43130->clk_mutex);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm_vol_tlv, -12750, 50, 1);
+
+static const char * const pcm_ch_text[] = {
+ "Left-Right Ch",
+ "Left-Left Ch",
+ "Right-Left Ch",
+ "Right-Right Ch",
+};
+
+static const struct reg_sequence pcm_ch_en_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {0x180005, 0x8C},
+ {0x180007, 0xAB},
+ {0x180015, 0x31},
+ {0x180017, 0xB2},
+ {0x180025, 0x30},
+ {0x180027, 0x84},
+ {0x180035, 0x9C},
+ {0x180037, 0xAE},
+ {0x18000D, 0x24},
+ {0x18000F, 0xA3},
+ {0x18001D, 0x05},
+ {0x18001F, 0xD4},
+ {0x18002D, 0x0B},
+ {0x18002F, 0xC7},
+ {0x18003D, 0x71},
+ {0x18003F, 0xE7},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence pcm_ch_dis_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {0x180005, 0x24},
+ {0x180007, 0xA3},
+ {0x180015, 0x05},
+ {0x180017, 0xD4},
+ {0x180025, 0x0B},
+ {0x180027, 0xC7},
+ {0x180035, 0x71},
+ {0x180037, 0xE7},
+ {0x18000D, 0x8C},
+ {0x18000F, 0xAB},
+ {0x18001D, 0x31},
+ {0x18001F, 0xB2},
+ {0x18002D, 0x30},
+ {0x18002F, 0x84},
+ {0x18003D, 0x9C},
+ {0x18003F, 0xAE},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_pcm_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_get_enum_double(kcontrol, ucontrol);
+}
+
+static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ switch (cs43130->dev_id) {
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ if (val >= 2)
+ regmap_multi_reg_write(cs43130->regmap, pcm_ch_en_seq,
+ ARRAY_SIZE(pcm_ch_en_seq));
+ else
+ regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq,
+ ARRAY_SIZE(pcm_ch_dis_seq));
+ break;
+ }
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static SOC_ENUM_SINGLE_DECL(pcm_ch_enum, CS43130_PCM_PATH_CTL_2, 0,
+ pcm_ch_text);
+
+static const char * const pcm_spd_texts[] = {
+ "Fast",
+ "Slow",
+};
+
+static SOC_ENUM_SINGLE_DECL(pcm_spd_enum, CS43130_PCM_FILT_OPT, 7,
+ pcm_spd_texts);
+
+static const char * const dsd_texts[] = {
+ "Off",
+ "BCKA Mode",
+ "BCKD Mode",
+};
+
+static const unsigned int dsd_values[] = {
+ CS43130_DSD_SRC_DSD,
+ CS43130_DSD_SRC_ASP,
+ CS43130_DSD_SRC_XSP,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dsd_enum, CS43130_DSD_INT_CFG, 0, 0x03,
+ dsd_texts, dsd_values);
+
+static const struct snd_kcontrol_new cs43130_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS43130_PCM_VOL_A, CS43130_PCM_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_DOUBLE_R_TLV("Master DSD Playback Volume",
+ CS43130_DSD_VOL_A, CS43130_DSD_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_ENUM_EXT("PCM Ch Select", pcm_ch_enum, cs43130_pcm_ch_get,
+ cs43130_pcm_ch_put),
+ SOC_ENUM("PCM Filter Speed", pcm_spd_enum),
+ SOC_SINGLE("PCM Phase Compensation", CS43130_PCM_FILT_OPT, 6, 1, 0),
+ SOC_SINGLE("PCM Nonoversample Emulate", CS43130_PCM_FILT_OPT, 5, 1, 0),
+ SOC_SINGLE("PCM High-pass Filter", CS43130_PCM_FILT_OPT, 1, 1, 0),
+ SOC_SINGLE("PCM De-emphasis Filter", CS43130_PCM_FILT_OPT, 0, 1, 0),
+ SOC_ENUM("DSD Phase Modulation", dsd_enum),
+};
+
+static const struct reg_sequence pcm_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD7, 0x01},
+ {CS43130_DXD8, 0},
+ {CS43130_DXD9, 0x01},
+ {CS43130_DXD3, 0x12},
+ {CS43130_DXD4, 0},
+ {CS43130_DXD10, 0x28},
+ {CS43130_DXD11, 0x28},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence dsd_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD7, 0x01},
+ {CS43130_DXD8, 0},
+ {CS43130_DXD9, 0x01},
+ {CS43130_DXD3, 0x12},
+ {CS43130_DXD4, 0},
+ {CS43130_DXD10, 0x1E},
+ {CS43130_DXD11, 0x20},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence pop_free_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD12, 0x0A},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence pop_free_seq2[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD13, 0x20},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence mute_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD3, 0x12},
+ {CS43130_DXD5, 0x02},
+ {CS43130_DXD4, 0x12},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence unmute_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD3, 0x10},
+ {CS43130_DXD5, 0},
+ {CS43130_DXD4, 0x16},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_dsd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, dsd_seq,
+ ARRAY_SIZE(dsd_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1,
+ CS43130_MUTE_MASK, 0);
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, unmute_seq,
+ ARRAY_SIZE(unmute_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, mute_seq,
+ ARRAY_SIZE(mute_seq));
+ regmap_update_bits(cs43130->regmap,
+ CS43130_DSD_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ /*
+ * DSD Power Down Sequence
+ * According to Design, 130ms is preferred.
+ */
+ msleep(130);
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ regmap_update_bits(cs43130->regmap,
+ CS43130_DSD_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ break;
+ }
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_pcm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, pcm_seq,
+ ARRAY_SIZE(pcm_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, 0);
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, unmute_seq,
+ ARRAY_SIZE(unmute_seq));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, mute_seq,
+ ARRAY_SIZE(mute_seq));
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ /*
+ * PCM Power Down Sequence
+ * According to Design, 130ms is preferred.
+ */
+ msleep(130);
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ break;
+ }
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct reg_sequence dac_postpmu_seq[] = {
+ {CS43130_DXD9, 0x0C},
+ {CS43130_DXD3, 0x10},
+ {CS43130_DXD4, 0x20},
+};
+
+static const struct reg_sequence dac_postpmd_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD6, 0x01},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, pop_free_seq,
+ ARRAY_SIZE(pop_free_seq));
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, pop_free_seq2,
+ ARRAY_SIZE(pop_free_seq2));
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(10000, 10050);
+
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, dac_postpmu_seq,
+ ARRAY_SIZE(dac_postpmu_seq));
+ /*
+ * Per datasheet, Sec. PCM Power-Up Sequence.
+ * According to Design, CS43130_DXD12 must be 0 to meet
+ * THDN and Dynamic Range spec.
+ */
+ msleep(1000);
+ regmap_write(cs43130->regmap, CS43130_DXD12, 0);
+ break;
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ usleep_range(12000, 12010);
+ regmap_write(cs43130->regmap, CS43130_DXD13, 0);
+ break;
+ }
+
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq,
+ ARRAY_SIZE(dac_postpmd_seq));
+ break;
+ }
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid DAC event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct reg_sequence hpin_prepmd_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD15, 0x64},
+ {CS43130_DXD14, 0},
+ {CS43130_DXD2, 0},
+ {CS43130_DXD1, 0},
+};
+
+static const struct reg_sequence hpin_postpmu_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD2, 1},
+ {CS43130_DXD14, 0xDC},
+ {CS43130_DXD15, 0xE4},
+ {CS43130_DXD1, 0},
+};
+
+static int cs43130_hpin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_multi_reg_write(cs43130->regmap, hpin_prepmd_seq,
+ ARRAY_SIZE(hpin_prepmd_seq));
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_multi_reg_write(cs43130->regmap, hpin_postpmu_seq,
+ ARRAY_SIZE(hpin_postpmu_seq));
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid HPIN event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const char * const bypass_mux_text[] = {
+ "Internal",
+ "Alternative",
+};
+static SOC_ENUM_SINGLE_DECL(bypass_enum, SND_SOC_NOPM, 0, bypass_mux_text);
+static const struct snd_kcontrol_new bypass_ctrl = SOC_DAPM_ENUM("Switch", bypass_enum);
+
+static const struct snd_soc_dapm_widget hp_widgets[] = {
+ SND_SOC_DAPM_MUX("Bypass Switch", SND_SOC_NOPM, 0, 0, &bypass_ctrl),
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN PCM", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_pcm_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN DoP", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_dsd_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_AIF_IN_E("XSPIN DoP", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_XSP_SHIFT, 1, cs43130_dsd_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_AIF_IN_E("XSPIN DSD", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_DSDIF_SHIFT, 1, cs43130_dsd_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)),
+
+ SND_SOC_DAPM_DAC("DSD", NULL, CS43130_DSD_PATH_CTL_2,
+ CS43130_DSD_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_DAC_E("HiFi DAC", NULL, CS43130_PWDN_CTL,
+ CS43130_PDN_HP_SHIFT, 1, cs43130_dac_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD)),
+
+/* Some devices have some extra analog widgets */
+#define NUM_ANALOG_WIDGETS 1
+
+ SND_SOC_DAPM_DAC_E("Analog Playback", NULL, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_SHIFT, 0, cs43130_hpin_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+};
+
+static const struct snd_soc_dapm_route hp_routes[] = {
+ {"ASPIN PCM", NULL, "ASP PCM Playback"},
+ {"ASPIN DoP", NULL, "ASP DoP Playback"},
+ {"XSPIN DoP", NULL, "XSP DoP Playback"},
+ {"XSPIN DSD", NULL, "XSP DSD Playback"},
+ {"DSD", NULL, "ASPIN DoP"},
+ {"DSD", NULL, "XSPIN DoP"},
+ {"DSD", NULL, "XSPIN DSD"},
+ {"HiFi DAC", NULL, "ASPIN PCM"},
+ {"HiFi DAC", NULL, "DSD"},
+ {"Bypass Switch", "Internal", "HiFi DAC"},
+ {"HPOUTA", NULL, "Bypass Switch"},
+ {"HPOUTB", NULL, "Bypass Switch"},
+
+/* Some devices have some extra analog routes */
+#define NUM_ANALOG_ROUTES 1
+ {"Bypass Switch", "Alternative", "Analog Playback"},
+};
+
+
+static const unsigned int cs43130_asp_src_rates[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_asp_constraints = {
+ .count = ARRAY_SIZE(cs43130_asp_src_rates),
+ .list = cs43130_asp_src_rates,
+};
+
+static int cs43130_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs43130_asp_constraints);
+}
+
+static const unsigned int cs43130_dop_src_rates[] = {
+ 176400, 352800,
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_dop_constraints = {
+ .count = ARRAY_SIZE(cs43130_dop_src_rates),
+ .list = cs43130_dop_src_rates,
+};
+
+static int cs43130_dop_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs43130_dop_constraints);
+}
+
+static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ default:
+ dev_err(cs43130->dev, "unsupported mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_NF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_NF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_IF;
+ break;
+ default:
+ dev_err(cs43130->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_B;
+ break;
+ default:
+ dev_err(cs43130->dev,
+ "unsupported audio format\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(cs43130->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n",
+ codec_dai->id,
+ cs43130->dais[codec_dai->id].dai_mode,
+ cs43130->dais[codec_dai->id].dai_format);
+
+ return 0;
+}
+
+static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ default:
+ dev_err(cs43130->dev, "Unsupported DAI format.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(cs43130->dev, "dai_mode = 0x%x\n",
+ cs43130->dais[codec_dai->id].dai_mode);
+
+ return 0;
+}
+
+static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ cs43130->dais[codec_dai->id].sclk = freq;
+ dev_dbg(cs43130->dev, "dai_id = %d, sclk = %u\n", codec_dai->id,
+ cs43130->dais[codec_dai->id].sclk);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs43130_pcm_ops = {
+ .startup = cs43130_pcm_startup,
+ .hw_params = cs43130_hw_params,
+ .hw_free = cs43130_hw_free,
+ .set_sysclk = cs43130_set_sysclk,
+ .set_fmt = cs43130_pcm_set_fmt,
+};
+
+static const struct snd_soc_dai_ops cs43130_dop_ops = {
+ .startup = cs43130_dop_startup,
+ .hw_params = cs43130_hw_params,
+ .hw_free = cs43130_hw_free,
+ .set_sysclk = cs43130_set_sysclk,
+ .set_fmt = cs43130_pcm_set_fmt,
+};
+
+static const struct snd_soc_dai_ops cs43130_dsd_ops = {
+ .startup = cs43130_dop_startup,
+ .hw_params = cs43130_dsd_hw_params,
+ .hw_free = cs43130_hw_free,
+ .set_fmt = cs43130_dsd_set_fmt,
+};
+
+static struct snd_soc_dai_driver cs43130_dai[] = {
+ {
+ .name = "cs43130-asp-pcm",
+ .id = CS43130_ASP_PCM_DAI,
+ .playback = {
+ .stream_name = "ASP PCM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_PCM_FORMATS,
+ },
+ .ops = &cs43130_pcm_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs43130-asp-dop",
+ .id = CS43130_ASP_DOP_DAI,
+ .playback = {
+ .stream_name = "ASP DoP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_DOP_FORMATS,
+ },
+ .ops = &cs43130_dop_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs43130-xsp-dop",
+ .id = CS43130_XSP_DOP_DAI,
+ .playback = {
+ .stream_name = "XSP DoP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_DOP_FORMATS,
+ },
+ .ops = &cs43130_dop_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "cs43130-xsp-dsd",
+ .id = CS43130_XSP_DSD_DAI,
+ .playback = {
+ .stream_name = "XSP DSD Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_DOP_FORMATS,
+ },
+ .ops = &cs43130_dsd_ops,
+ },
+
+};
+
+static int cs43130_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs43130->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ clk_id, source, freq, dir);
+
+ switch (freq) {
+ case CS43130_MCLK_22M:
+ case CS43130_MCLK_24M:
+ cs43130->mclk = freq;
+ break;
+ default:
+ dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ if (source == CS43130_MCLK_SRC_EXT) {
+ cs43130->pll_bypass = true;
+ } else {
+ dev_err(cs43130->dev, "Invalid MCLK source\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline u16 cs43130_get_ac_reg_val(u16 ac_freq)
+{
+ /* AC freq is counted in 5.94Hz step. */
+ return ac_freq / 6;
+}
+
+static int cs43130_show_dc(struct device *dev, char *buf, u8 ch)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ if (!cs43130->hpload_done)
+ return sysfs_emit(buf, "NO_HPLOAD\n");
+ else
+ return sysfs_emit(buf, "%u\n", cs43130->hpload_dc[ch]);
+}
+
+static ssize_t hpload_dc_l_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_dc(dev, buf, HP_LEFT);
+}
+
+static ssize_t hpload_dc_r_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_dc(dev, buf, HP_RIGHT);
+}
+
+static const u16 cs43130_ac_freq[CS43130_AC_FREQ] = {
+ 24,
+ 43,
+ 93,
+ 200,
+ 431,
+ 928,
+ 2000,
+ 4309,
+ 9283,
+ 20000,
+};
+
+static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
+{
+ int i, j = 0, tmp;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ if (cs43130->hpload_done && cs43130->ac_meas) {
+ for (i = 0; i < ARRAY_SIZE(cs43130_ac_freq); i++) {
+ tmp = sysfs_emit_at(buf, j, "%u\n",
+ cs43130->hpload_ac[i][ch]);
+ if (!tmp)
+ break;
+
+ j += tmp;
+ }
+
+ return j;
+ } else {
+ return sysfs_emit(buf, "NO_HPLOAD\n");
+ }
+}
+
+static ssize_t hpload_ac_l_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_ac(dev, buf, HP_LEFT);
+}
+
+static ssize_t hpload_ac_r_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cs43130_show_ac(dev, buf, HP_RIGHT);
+}
+
+static DEVICE_ATTR_RO(hpload_dc_l);
+static DEVICE_ATTR_RO(hpload_dc_r);
+static DEVICE_ATTR_RO(hpload_ac_l);
+static DEVICE_ATTR_RO(hpload_ac_r);
+
+static struct attribute *hpload_attrs[] = {
+ &dev_attr_hpload_dc_l.attr,
+ &dev_attr_hpload_dc_r.attr,
+ &dev_attr_hpload_ac_l.attr,
+ &dev_attr_hpload_ac_r.attr,
+};
+ATTRIBUTE_GROUPS(hpload);
+
+static const struct reg_sequence hp_en_cal_seq[] = {
+ {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
+ {CS43130_HP_MEAS_LOAD_1, 0},
+ {CS43130_HP_MEAS_LOAD_2, 0},
+ {CS43130_INT_MASK_4, 0},
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD16, 0xBB},
+ {CS43130_DXD12, 0x01},
+ {CS43130_DXD19, 0xCB},
+ {CS43130_DXD17, 0x95},
+ {CS43130_DXD18, 0x0B},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+};
+
+static const struct reg_sequence hp_en_cal_seq2[] = {
+ {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
+ {CS43130_HP_MEAS_LOAD_1, 0},
+ {CS43130_HP_MEAS_LOAD_2, 0},
+ {CS43130_INT_MASK_4, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+};
+
+static const struct reg_sequence hp_dis_cal_seq[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD12, 0},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0},
+};
+
+static const struct reg_sequence hp_dis_cal_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0},
+};
+
+static const struct reg_sequence hp_dc_ch_l_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x0A},
+ {CS43130_DXD17, 0x93},
+ {CS43130_DXD18, 0x0A},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x81},
+};
+
+static const struct reg_sequence hp_dc_ch_l_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x81},
+};
+
+static const struct reg_sequence hp_dc_ch_r_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x8A},
+ {CS43130_DXD17, 0x15},
+ {CS43130_DXD18, 0x06},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x91},
+};
+
+static const struct reg_sequence hp_dc_ch_r_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x91},
+};
+
+static const struct reg_sequence hp_ac_ch_l_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x0A},
+ {CS43130_DXD17, 0x93},
+ {CS43130_DXD18, 0x0A},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x82},
+};
+
+static const struct reg_sequence hp_ac_ch_l_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x82},
+};
+
+static const struct reg_sequence hp_ac_ch_r_seq[] = {
+ {CS43130_DXD1, 0x99},
+ {CS43130_DXD19, 0x8A},
+ {CS43130_DXD17, 0x15},
+ {CS43130_DXD18, 0x06},
+ {CS43130_DXD1, 0},
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x92},
+};
+
+static const struct reg_sequence hp_ac_ch_r_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x92},
+};
+
+static const struct reg_sequence hp_cln_seq[] = {
+ {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
+ {CS43130_HP_MEAS_LOAD_1, 0},
+ {CS43130_HP_MEAS_LOAD_2, 0},
+};
+
+struct reg_sequences {
+ const struct reg_sequence *seq;
+ int size;
+ unsigned int msk;
+};
+
+static const struct reg_sequences hpload_seq1[] = {
+ {
+ .seq = hp_en_cal_seq,
+ .size = ARRAY_SIZE(hp_en_cal_seq),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_l_seq,
+ .size = ARRAY_SIZE(hp_dc_ch_l_seq),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_l_seq,
+ .size = ARRAY_SIZE(hp_ac_ch_l_seq),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+ {
+ .seq = hp_dis_cal_seq,
+ .size = ARRAY_SIZE(hp_dis_cal_seq),
+ .msk = CS43130_HPLOAD_OFF_INT,
+ },
+ {
+ .seq = hp_en_cal_seq,
+ .size = ARRAY_SIZE(hp_en_cal_seq),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_r_seq,
+ .size = ARRAY_SIZE(hp_dc_ch_r_seq),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_r_seq,
+ .size = ARRAY_SIZE(hp_ac_ch_r_seq),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+};
+
+static const struct reg_sequences hpload_seq2[] = {
+ {
+ .seq = hp_en_cal_seq2,
+ .size = ARRAY_SIZE(hp_en_cal_seq2),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_l_seq2,
+ .size = ARRAY_SIZE(hp_dc_ch_l_seq2),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_l_seq2,
+ .size = ARRAY_SIZE(hp_ac_ch_l_seq2),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+ {
+ .seq = hp_dis_cal_seq2,
+ .size = ARRAY_SIZE(hp_dis_cal_seq2),
+ .msk = CS43130_HPLOAD_OFF_INT,
+ },
+ {
+ .seq = hp_en_cal_seq2,
+ .size = ARRAY_SIZE(hp_en_cal_seq2),
+ .msk = CS43130_HPLOAD_ON_INT,
+ },
+ {
+ .seq = hp_dc_ch_r_seq2,
+ .size = ARRAY_SIZE(hp_dc_ch_r_seq2),
+ .msk = CS43130_HPLOAD_DC_INT,
+ },
+ {
+ .seq = hp_ac_ch_r_seq2,
+ .size = ARRAY_SIZE(hp_ac_ch_r_seq2),
+ .msk = CS43130_HPLOAD_AC_INT,
+ },
+};
+
+static int cs43130_update_hpload(unsigned int msk, int ac_idx,
+ struct cs43130_private *cs43130)
+{
+ bool left_ch = true;
+ unsigned int reg;
+ u32 addr;
+ u16 impedance;
+
+ switch (msk) {
+ case CS43130_HPLOAD_DC_INT:
+ case CS43130_HPLOAD_AC_INT:
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_read(cs43130->regmap, CS43130_HP_LOAD_1, &reg);
+ if (reg & CS43130_HPLOAD_CHN_SEL)
+ left_ch = false;
+
+ if (msk == CS43130_HPLOAD_DC_INT)
+ addr = CS43130_HP_DC_STAT_1;
+ else
+ addr = CS43130_HP_AC_STAT_1;
+
+ regmap_read(cs43130->regmap, addr, &reg);
+ impedance = reg >> 3;
+ regmap_read(cs43130->regmap, addr + 1, &reg);
+ impedance |= reg << 5;
+
+ if (msk == CS43130_HPLOAD_DC_INT) {
+ if (left_ch)
+ cs43130->hpload_dc[HP_LEFT] = impedance;
+ else
+ cs43130->hpload_dc[HP_RIGHT] = impedance;
+
+ dev_dbg(cs43130->dev, "HP DC impedance (Ch %u): %u\n", !left_ch,
+ impedance);
+ } else {
+ if (left_ch)
+ cs43130->hpload_ac[ac_idx][HP_LEFT] = impedance;
+ else
+ cs43130->hpload_ac[ac_idx][HP_RIGHT] = impedance;
+
+ dev_dbg(cs43130->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n",
+ cs43130->ac_freq[ac_idx], !left_ch, impedance);
+ }
+
+ return 0;
+}
+
+static int cs43130_hpload_proc(struct cs43130_private *cs43130,
+ const struct reg_sequence *seq, int seq_size,
+ unsigned int rslt_msk, int ac_idx)
+{
+ int ret;
+ unsigned int msk;
+ u16 ac_reg_val;
+
+ reinit_completion(&cs43130->hpload_evt);
+
+ if (rslt_msk == CS43130_HPLOAD_AC_INT) {
+ ac_reg_val = cs43130_get_ac_reg_val(cs43130->ac_freq[ac_idx]);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_LOAD_1,
+ CS43130_HPLOAD_AC_START, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_MEAS_LOAD_1,
+ CS43130_HP_MEAS_LOAD_MASK,
+ ac_reg_val >> CS43130_HP_MEAS_LOAD_1_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_MEAS_LOAD_2,
+ CS43130_HP_MEAS_LOAD_MASK,
+ ac_reg_val >> CS43130_HP_MEAS_LOAD_2_SHIFT);
+ }
+
+ regmap_multi_reg_write(cs43130->regmap, seq,
+ seq_size);
+
+ ret = wait_for_completion_timeout(&cs43130->hpload_evt,
+ msecs_to_jiffies(1000));
+ regmap_read(cs43130->regmap, CS43130_INT_MASK_4, &msk);
+ if (!ret) {
+ dev_err(cs43130->dev, "Timeout waiting for HPLOAD interrupt\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(cs43130->dev, "HP load stat: %x, INT_MASK_4: %x\n",
+ cs43130->hpload_stat, msk);
+ if ((cs43130->hpload_stat & (CS43130_HPLOAD_NO_DC_INT |
+ CS43130_HPLOAD_UNPLUG_INT |
+ CS43130_HPLOAD_OOR_INT)) ||
+ !(cs43130->hpload_stat & rslt_msk)) {
+ dev_dbg(cs43130->dev, "HP load measure failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static const struct reg_sequence hv_seq[][2] = {
+ {
+ {CS43130_CLASS_H_CTL, 0x1C},
+ {CS43130_HP_OUT_CTL_1, 0x10},
+ },
+ {
+ {CS43130_CLASS_H_CTL, 0x1E},
+ {CS43130_HP_OUT_CTL_1, 0x20},
+ },
+ {
+ {CS43130_CLASS_H_CTL, 0x1E},
+ {CS43130_HP_OUT_CTL_1, 0x30},
+ },
+};
+
+static int cs43130_set_hv(struct regmap *regmap, u16 hpload_dc,
+ const u16 *dc_threshold)
+{
+ int i;
+
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++) {
+ if (hpload_dc <= dc_threshold[i])
+ break;
+ }
+
+ regmap_multi_reg_write(regmap, hv_seq[i], ARRAY_SIZE(hv_seq[i]));
+
+ return 0;
+}
+
+static void cs43130_imp_meas(struct work_struct *wk)
+{
+ unsigned int reg, seq_size;
+ int i, ret, ac_idx;
+ struct cs43130_private *cs43130;
+ struct snd_soc_component *component;
+ const struct reg_sequences *hpload_seq;
+
+ cs43130 = container_of(wk, struct cs43130_private, work);
+ component = cs43130->component;
+
+ if (!cs43130->mclk)
+ return;
+
+ cs43130->hpload_done = false;
+
+ mutex_lock(&cs43130->clk_mutex);
+ if (!cs43130->clk_req) {
+ /* clk not in use */
+ cs43130_set_pll(component, 0, 0, cs43130->mclk, CS43130_MCLK_22M);
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+ else
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+ }
+
+ cs43130->clk_req++;
+ mutex_unlock(&cs43130->clk_mutex);
+
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_4, &reg);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ hpload_seq = hpload_seq1;
+ seq_size = ARRAY_SIZE(hpload_seq1);
+ break;
+ case CS43131_CHIP_ID:
+ hpload_seq = hpload_seq2;
+ seq_size = ARRAY_SIZE(hpload_seq2);
+ break;
+ default:
+ WARN(1, "Invalid dev_id for meas: %d", cs43130->dev_id);
+ return;
+ }
+
+ i = 0;
+ ac_idx = 0;
+ while (i < seq_size) {
+ ret = cs43130_hpload_proc(cs43130, hpload_seq[i].seq,
+ hpload_seq[i].size,
+ hpload_seq[i].msk, ac_idx);
+ if (ret < 0)
+ goto exit;
+
+ cs43130_update_hpload(hpload_seq[i].msk, ac_idx, cs43130);
+
+ if (cs43130->ac_meas &&
+ hpload_seq[i].msk == CS43130_HPLOAD_AC_INT &&
+ ac_idx < CS43130_AC_FREQ - 1) {
+ ac_idx++;
+ } else {
+ ac_idx = 0;
+ i++;
+ }
+ }
+ cs43130->hpload_done = true;
+
+ if (cs43130->hpload_dc[HP_LEFT] >= CS43130_LINEOUT_LOAD)
+ snd_soc_jack_report(&cs43130->jack, CS43130_JACK_LINEOUT,
+ CS43130_JACK_MASK);
+ else
+ snd_soc_jack_report(&cs43130->jack, CS43130_JACK_HEADPHONE,
+ CS43130_JACK_MASK);
+
+ dev_dbg(cs43130->dev, "Set HP output control. DC threshold\n");
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++)
+ dev_dbg(cs43130->dev, "DC threshold[%d]: %u.\n", i,
+ cs43130->dc_threshold[i]);
+
+ cs43130_set_hv(cs43130->regmap, cs43130->hpload_dc[HP_LEFT],
+ cs43130->dc_threshold);
+
+exit:
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ cs43130_hpload_proc(cs43130, hp_dis_cal_seq,
+ ARRAY_SIZE(hp_dis_cal_seq),
+ CS43130_HPLOAD_OFF_INT, ac_idx);
+ break;
+ case CS43131_CHIP_ID:
+ cs43130_hpload_proc(cs43130, hp_dis_cal_seq2,
+ ARRAY_SIZE(hp_dis_cal_seq2),
+ CS43130_HPLOAD_OFF_INT, ac_idx);
+ break;
+ }
+
+ regmap_multi_reg_write(cs43130->regmap, hp_cln_seq,
+ ARRAY_SIZE(hp_cln_seq));
+
+ mutex_lock(&cs43130->clk_mutex);
+ cs43130->clk_req--;
+ /* clk not in use */
+ if (!cs43130->clk_req)
+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO);
+ mutex_unlock(&cs43130->clk_mutex);
+}
+
+static irqreturn_t cs43130_irq_thread(int irq, void *data)
+{
+ struct cs43130_private *cs43130 = (struct cs43130_private *)data;
+ unsigned int stickies[CS43130_NUM_INT];
+ unsigned int irq_occurrence = 0;
+ unsigned int masks[CS43130_NUM_INT];
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_1 + i,
+ &stickies[i]);
+ regmap_read(cs43130->regmap, CS43130_INT_MASK_1 + i,
+ &masks[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ stickies[i] = stickies[i] & (~masks[i]);
+ for (j = 0; j < 8; j++)
+ irq_occurrence += (stickies[i] >> j) & 1;
+ }
+
+ if (!irq_occurrence)
+ return IRQ_NONE;
+
+ if (stickies[0] & CS43130_XTAL_RDY_INT) {
+ complete(&cs43130->xtal_rdy);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_PLL_RDY_INT) {
+ complete(&cs43130->pll_rdy);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_NO_DC_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_err(cs43130->dev,
+ "DC load has not completed before AC load (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_UNPLUG_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_err(cs43130->dev, "HP unplugged during measurement (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_OOR_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_err(cs43130->dev, "HP load out of range (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_AC_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(cs43130->dev, "HP AC load measurement done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_DC_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(cs43130->dev, "HP DC load measurement done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_ON_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(cs43130->dev, "HP load state machine on done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[3] & CS43130_HPLOAD_OFF_INT) {
+ cs43130->hpload_stat = stickies[3];
+ dev_dbg(cs43130->dev, "HP load state machine off done (%x)\n",
+ cs43130->hpload_stat);
+ complete(&cs43130->hpload_evt);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_XTAL_ERR_INT) {
+ dev_err(cs43130->dev, "Crystal err: clock is not running\n");
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_HP_UNPLUG_INT) {
+ dev_dbg(cs43130->dev, "HP unplugged\n");
+ cs43130->hpload_done = false;
+ snd_soc_jack_report(&cs43130->jack, 0, CS43130_JACK_MASK);
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_HP_PLUG_INT) {
+ if (cs43130->dc_meas && !cs43130->hpload_done &&
+ !work_busy(&cs43130->work)) {
+ dev_dbg(cs43130->dev, "HP load queue work\n");
+ queue_work(cs43130->wq, &cs43130->work);
+ }
+
+ snd_soc_jack_report(&cs43130->jack, SND_JACK_MECHANICAL,
+ CS43130_JACK_MASK);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int cs43130_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_card *card = component->card;
+ unsigned int reg;
+
+ cs43130->component = component;
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) {
+ regmap_update_bits(cs43130->regmap, CS43130_CRYSTAL_SET,
+ CS43130_XTAL_IBIAS_MASK,
+ cs43130->xtal_ibias);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT, 0);
+ }
+
+ ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK,
+ &cs43130->jack);
+ if (ret < 0) {
+ dev_err(cs43130->dev, "Cannot create jack\n");
+ return ret;
+ }
+
+ cs43130->hpload_done = false;
+ if (cs43130->dc_meas) {
+ ret = sysfs_create_groups(&cs43130->dev->kobj, hpload_groups);
+ if (ret)
+ return ret;
+
+ cs43130->wq = create_singlethread_workqueue("cs43130_hp");
+ if (!cs43130->wq) {
+ sysfs_remove_groups(&cs43130->dev->kobj, hpload_groups);
+ return -ENOMEM;
+ }
+ INIT_WORK(&cs43130->work, cs43130_imp_meas);
+ }
+
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_1, &reg);
+ regmap_read(cs43130->regmap, CS43130_HP_STATUS, &reg);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT,
+ CS43130_HP_DETECT_CTRL_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT,
+ CS43130_HP_DETECT_CTRL_MASK,
+ CS43130_HP_DETECT_CTRL_MASK);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs43130_digital = {
+ .probe = cs43130_probe,
+ .controls = cs43130_snd_controls,
+ .num_controls = ARRAY_SIZE(cs43130_snd_controls),
+ .set_sysclk = cs43130_component_set_sysclk,
+ .set_pll = cs43130_set_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ /* Don't take into account the ending analog widgets and routes */
+ .dapm_widgets = hp_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hp_widgets) - NUM_ANALOG_WIDGETS,
+ .dapm_routes = hp_routes,
+ .num_dapm_routes = ARRAY_SIZE(hp_routes) - NUM_ANALOG_ROUTES,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs43130_analog = {
+ .probe = cs43130_probe,
+ .controls = cs43130_snd_controls,
+ .num_controls = ARRAY_SIZE(cs43130_snd_controls),
+ .set_sysclk = cs43130_component_set_sysclk,
+ .set_pll = cs43130_set_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .dapm_widgets = hp_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hp_widgets),
+ .dapm_routes = hp_routes,
+ .num_dapm_routes = ARRAY_SIZE(hp_routes),
+};
+
+static const struct regmap_config cs43130_regmap = {
+ .reg_bits = 24,
+ .pad_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS43130_LASTREG,
+ .reg_defaults = cs43130_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults),
+ .readable_reg = cs43130_readable_register,
+ .precious_reg = cs43130_precious_register,
+ .volatile_reg = cs43130_volatile_register,
+ .cache_type = REGCACHE_MAPLE,
+ /* needed for regcache_sync */
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const u16 cs43130_dc_threshold[CS43130_DC_THRESHOLD] = {
+ 50,
+ 120,
+};
+
+static int cs43130_handle_device_data(struct cs43130_private *cs43130)
+{
+ unsigned int val;
+ int i;
+
+ if (device_property_read_u32(cs43130->dev, "cirrus,xtal-ibias", &val) < 0) {
+ /* Crystal is unused. System clock is used for external MCLK */
+ cs43130->xtal_ibias = CS43130_XTAL_UNUSED;
+ return 0;
+ }
+
+ switch (val) {
+ case 1:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA;
+ break;
+ case 2:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA;
+ break;
+ case 3:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
+ break;
+ default:
+ dev_err(cs43130->dev,
+ "Invalid cirrus,xtal-ibias value: %d\n", val);
+ return -EINVAL;
+ }
+
+ cs43130->dc_meas = device_property_read_bool(cs43130->dev, "cirrus,dc-measure");
+ cs43130->ac_meas = device_property_read_bool(cs43130->dev, "cirrus,ac-measure");
+
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,ac-freq", cs43130->ac_freq,
+ CS43130_AC_FREQ)) {
+ for (i = 0; i < CS43130_AC_FREQ; i++)
+ cs43130->ac_freq[i] = cs43130_ac_freq[i];
+ }
+
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,dc-threshold",
+ cs43130->dc_threshold,
+ CS43130_DC_THRESHOLD)) {
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++)
+ cs43130->dc_threshold[i] = cs43130_dc_threshold[i];
+ }
+
+ return 0;
+}
+
+static int cs43130_i2c_probe(struct i2c_client *client)
+{
+ const struct snd_soc_component_driver *component_driver;
+ struct cs43130_private *cs43130;
+ int ret;
+ unsigned int reg;
+ int i, devid;
+
+ cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
+ if (!cs43130)
+ return -ENOMEM;
+
+ cs43130->dev = &client->dev;
+
+ i2c_set_clientdata(client, cs43130);
+
+ cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
+ if (IS_ERR(cs43130->regmap)) {
+ ret = PTR_ERR(cs43130->regmap);
+ return ret;
+ }
+
+ if (dev_fwnode(cs43130->dev)) {
+ ret = cs43130_handle_device_data(cs43130);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++)
+ cs43130->supplies[i].supply = cs43130_supply_names[i];
+
+ ret = devm_regulator_bulk_get(cs43130->dev,
+ ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(cs43130->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(cs43130->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs43130->reset_gpio = devm_gpiod_get_optional(cs43130->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs43130->reset_gpio)) {
+ ret = PTR_ERR(cs43130->reset_gpio);
+ goto err_supplies;
+ }
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ devid = cirrus_read_device_id(cs43130->regmap, CS43130_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(cs43130->dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
+
+ switch (devid) {
+ case CS43130_CHIP_ID:
+ case CS4399_CHIP_ID:
+ case CS43131_CHIP_ID:
+ case CS43198_CHIP_ID:
+ break;
+ default:
+ dev_err(cs43130->dev,
+ "CS43130 Device ID %X. Expected ID %X, %X, %X or %X\n",
+ devid, CS43130_CHIP_ID, CS4399_CHIP_ID,
+ CS43131_CHIP_ID, CS43198_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs43130->dev_id = devid;
+ ret = regmap_read(cs43130->regmap, CS43130_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(cs43130->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(cs43130->dev,
+ "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ mutex_init(&cs43130->clk_mutex);
+
+ init_completion(&cs43130->xtal_rdy);
+ init_completion(&cs43130->pll_rdy);
+ init_completion(&cs43130->hpload_evt);
+
+ if (!client->irq) {
+ dev_dbg(cs43130->dev, "IRQ not found, will poll instead\n");
+ cs43130->has_irq_line = 0;
+ } else {
+ ret = devm_request_threaded_irq(cs43130->dev, client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(cs43130->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ cs43130->has_irq_line = 1;
+ }
+
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+
+ pm_runtime_set_autosuspend_delay(cs43130->dev, 100);
+ pm_runtime_use_autosuspend(cs43130->dev);
+ pm_runtime_set_active(cs43130->dev);
+ pm_runtime_enable(cs43130->dev);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS43131_CHIP_ID:
+ component_driver = &soc_component_dev_cs43130_analog;
+ break;
+ case CS43198_CHIP_ID:
+ case CS4399_CHIP_ID:
+ component_driver = &soc_component_dev_cs43130_digital;
+ break;
+ }
+
+ ret = devm_snd_soc_register_component(cs43130->dev, component_driver,
+ cs43130_dai, ARRAY_SIZE(cs43130_dai));
+ if (ret < 0) {
+ dev_err(cs43130->dev,
+ "snd_soc_register_component failed with ret = %d\n", ret);
+ goto err;
+ }
+
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ CS43130_ASP_3ST_MASK, 0);
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ CS43130_XSP_3ST_MASK, 0);
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+err_supplies:
+ regulator_bulk_disable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+
+ return ret;
+}
+
+static void cs43130_i2c_remove(struct i2c_client *client)
+{
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED)
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT,
+ 1 << CS43130_XTAL_ERR_INT_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT,
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT);
+
+ if (cs43130->dc_meas) {
+ cancel_work_sync(&cs43130->work);
+ flush_workqueue(cs43130->wq);
+
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_r);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_r);
+ }
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ pm_runtime_disable(cs43130->dev);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+}
+
+static int cs43130_runtime_suspend(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED)
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT,
+ 1 << CS43130_XTAL_ERR_INT_SHIFT);
+
+ regcache_cache_only(cs43130->regmap, true);
+ regcache_mark_dirty(cs43130->regmap);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+
+ return 0;
+}
+
+static int cs43130_runtime_resume(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs43130->regmap, false);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ ret = regcache_sync(cs43130->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED)
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_ERR_INT, 0);
+
+ return 0;
+err:
+ regcache_cache_only(cs43130->regmap, true);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+
+ return ret;
+}
+
+static const struct dev_pm_ops cs43130_runtime_pm = {
+ RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume, NULL)
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id cs43130_of_match[] = {
+ {.compatible = "cirrus,cs43130",},
+ {.compatible = "cirrus,cs4399",},
+ {.compatible = "cirrus,cs43131",},
+ {.compatible = "cirrus,cs43198",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs43130_of_match);
+#endif
+
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id cs43130_acpi_match[] = {
+ { "CSC4399", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match);
+#endif
+
+
+static const struct i2c_device_id cs43130_i2c_id[] = {
+ {"cs43130"},
+ {"cs4399"},
+ {"cs43131"},
+ {"cs43198"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
+
+static struct i2c_driver cs43130_i2c_driver = {
+ .driver = {
+ .name = "cs43130",
+ .of_match_table = of_match_ptr(cs43130_of_match),
+ .acpi_match_table = ACPI_PTR(cs43130_acpi_match),
+ .pm = pm_ptr(&cs43130_runtime_pm),
+ },
+ .id_table = cs43130_i2c_id,
+ .probe = cs43130_i2c_probe,
+ .remove = cs43130_i2c_remove,
+};
+
+module_i2c_driver(cs43130_i2c_driver);
+
+MODULE_AUTHOR("Li Xu <li.xu@cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS43130 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
new file mode 100644
index 000000000000..dbdb5b262f1b
--- /dev/null
+++ b/sound/soc/codecs/cs43130.h
@@ -0,0 +1,541 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS43130 codec driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu@cirrus.com>
+ */
+
+#ifndef __CS43130_H__
+#define __CS43130_H__
+
+#include <linux/math.h>
+
+/* CS43130 registers addresses */
+/* all reg address is shifted by a byte for control byte to be LSB */
+#define CS43130_FIRSTREG 0x010000
+#define CS43130_LASTREG 0x190000
+#define CS43130_CHIP_ID 0x00043130
+#define CS4399_CHIP_ID 0x00043990
+#define CS43131_CHIP_ID 0x00043131
+#define CS43198_CHIP_ID 0x00043198
+#define CS43130_DEVID_AB 0x010000 /* Device ID A & B [RO] */
+#define CS43130_DEVID_CD 0x010001 /* Device ID C & D [RO] */
+#define CS43130_DEVID_E 0x010002 /* Device ID E [RO] */
+#define CS43130_FAB_ID 0x010003 /* Fab ID [RO] */
+#define CS43130_REV_ID 0x010004 /* Revision ID [RO] */
+#define CS43130_SUBREV_ID 0x010005 /* Subrevision ID */
+#define CS43130_SYS_CLK_CTL_1 0x010006 /* System Clocking Ctl 1 */
+#define CS43130_SP_SRATE 0x01000B /* Serial Port Sample Rate */
+#define CS43130_SP_BITSIZE 0x01000C /* Serial Port Bit Size */
+#define CS43130_PAD_INT_CFG 0x01000D /* Pad Interface Config */
+#define CS43130_DXD1 0x010010 /* DXD1 */
+#define CS43130_DXD7 0x010025 /* DXD7 */
+#define CS43130_DXD19 0x010026 /* DXD19 */
+#define CS43130_DXD17 0x010027 /* DXD17 */
+#define CS43130_DXD18 0x010028 /* DXD18 */
+#define CS43130_DXD12 0x01002C /* DXD12 */
+#define CS43130_DXD8 0x01002E /* DXD8 */
+#define CS43130_PWDN_CTL 0x020000 /* Power Down Ctl */
+#define CS43130_DXD2 0x020019 /* DXD2 */
+#define CS43130_CRYSTAL_SET 0x020052 /* Crystal Setting */
+#define CS43130_PLL_SET_1 0x030001 /* PLL Setting 1 */
+#define CS43130_PLL_SET_2 0x030002 /* PLL Setting 2 */
+#define CS43130_PLL_SET_3 0x030003 /* PLL Setting 3 */
+#define CS43130_PLL_SET_4 0x030004 /* PLL Setting 4 */
+#define CS43130_PLL_SET_5 0x030005 /* PLL Setting 5 */
+#define CS43130_PLL_SET_6 0x030008 /* PLL Setting 6 */
+#define CS43130_PLL_SET_7 0x03000A /* PLL Setting 7 */
+#define CS43130_PLL_SET_8 0x03001B /* PLL Setting 8 */
+#define CS43130_PLL_SET_9 0x040002 /* PLL Setting 9 */
+#define CS43130_PLL_SET_10 0x040003 /* PLL Setting 10 */
+#define CS43130_CLKOUT_CTL 0x040004 /* CLKOUT Ctl */
+#define CS43130_ASP_NUM_1 0x040010 /* ASP Numerator 1 */
+#define CS43130_ASP_NUM_2 0x040011 /* ASP Numerator 2 */
+#define CS43130_ASP_DEN_1 0x040012 /* ASP Denominator 1 */
+#define CS43130_ASP_DEN_2 0x040013 /* ASP Denominator 2 */
+#define CS43130_ASP_LRCK_HI_TIME_1 0x040014 /* ASP LRCK High Time 1 */
+#define CS43130_ASP_LRCK_HI_TIME_2 0x040015 /* ASP LRCK High Time 2 */
+#define CS43130_ASP_LRCK_PERIOD_1 0x040016 /* ASP LRCK Period 1 */
+#define CS43130_ASP_LRCK_PERIOD_2 0x040017 /* ASP LRCK Period 2 */
+#define CS43130_ASP_CLOCK_CONF 0x040018 /* ASP Clock Config */
+#define CS43130_ASP_FRAME_CONF 0x040019 /* ASP Frame Config */
+#define CS43130_XSP_NUM_1 0x040020 /* XSP Numerator 1 */
+#define CS43130_XSP_NUM_2 0x040021 /* XSP Numerator 2 */
+#define CS43130_XSP_DEN_1 0x040022 /* XSP Denominator 1 */
+#define CS43130_XSP_DEN_2 0x040023 /* XSP Denominator 2 */
+#define CS43130_XSP_LRCK_HI_TIME_1 0x040024 /* XSP LRCK High Time 1 */
+#define CS43130_XSP_LRCK_HI_TIME_2 0x040025 /* XSP LRCK High Time 2 */
+#define CS43130_XSP_LRCK_PERIOD_1 0x040026 /* XSP LRCK Period 1 */
+#define CS43130_XSP_LRCK_PERIOD_2 0x040027 /* XSP LRCK Period 2 */
+#define CS43130_XSP_CLOCK_CONF 0x040028 /* XSP Clock Config */
+#define CS43130_XSP_FRAME_CONF 0x040029 /* XSP Frame Config */
+#define CS43130_ASP_CH_1_LOC 0x050000 /* ASP Chan 1 Location */
+#define CS43130_ASP_CH_2_LOC 0x050001 /* ASP Chan 2 Location */
+#define CS43130_ASP_CH_1_SZ_EN 0x05000A /* ASP Chan 1 Size, Enable */
+#define CS43130_ASP_CH_2_SZ_EN 0x05000B /* ASP Chan 2 Size, Enable */
+#define CS43130_XSP_CH_1_LOC 0x060000 /* XSP Chan 1 Location */
+#define CS43130_XSP_CH_2_LOC 0x060001 /* XSP Chan 2 Location */
+#define CS43130_XSP_CH_1_SZ_EN 0x06000A /* XSP Chan 1 Size, Enable */
+#define CS43130_XSP_CH_2_SZ_EN 0x06000B /* XSP Chan 2 Size, Enable */
+#define CS43130_DSD_VOL_B 0x070000 /* DSD Volume B */
+#define CS43130_DSD_VOL_A 0x070001 /* DSD Volume A */
+#define CS43130_DSD_PATH_CTL_1 0x070002 /* DSD Proc Path Sig Ctl 1 */
+#define CS43130_DSD_INT_CFG 0x070003 /* DSD Interface Config */
+#define CS43130_DSD_PATH_CTL_2 0x070004 /* DSD Proc Path Sig Ctl 2 */
+#define CS43130_DSD_PCM_MIX_CTL 0x070005 /* DSD and PCM Mixing Ctl */
+#define CS43130_DSD_PATH_CTL_3 0x070006 /* DSD Proc Path Sig Ctl 3 */
+#define CS43130_HP_OUT_CTL_1 0x080000 /* HP Output Ctl 1 */
+#define CS43130_DXD16 0x080024 /* DXD16 */
+#define CS43130_DXD13 0x080032 /* DXD13 */
+#define CS43130_PCM_FILT_OPT 0x090000 /* PCM Filter Option */
+#define CS43130_PCM_VOL_B 0x090001 /* PCM Volume B */
+#define CS43130_PCM_VOL_A 0x090002 /* PCM Volume A */
+#define CS43130_PCM_PATH_CTL_1 0x090003 /* PCM Path Signal Ctl 1 */
+#define CS43130_PCM_PATH_CTL_2 0x090004 /* PCM Path Signal Ctl 2 */
+#define CS43130_DXD6 0x090097 /* DXD6 */
+#define CS43130_CLASS_H_CTL 0x0B0000 /* Class H Ctl */
+#define CS43130_DXD15 0x0B0005 /* DXD15 */
+#define CS43130_DXD14 0x0B0006 /* DXD14 */
+#define CS43130_DXD3 0x0C0002 /* DXD3 */
+#define CS43130_DXD10 0x0C0003 /* DXD10 */
+#define CS43130_DXD11 0x0C0005 /* DXD11 */
+#define CS43130_DXD9 0x0C0006 /* DXD9 */
+#define CS43130_DXD4 0x0C0009 /* DXD4 */
+#define CS43130_DXD5 0x0C000E /* DXD5 */
+#define CS43130_HP_DETECT 0x0D0000 /* HP Detect */
+#define CS43130_HP_STATUS 0x0D0001 /* HP Status [RO] */
+#define CS43130_HP_LOAD_1 0x0E0000 /* HP Load 1 */
+#define CS43130_HP_MEAS_LOAD_1 0x0E0003 /* HP Load Measurement 1 */
+#define CS43130_HP_MEAS_LOAD_2 0x0E0004 /* HP Load Measurement 2 */
+#define CS43130_HP_DC_STAT_1 0x0E000D /* HP DC Load Status 0 [RO] */
+#define CS43130_HP_DC_STAT_2 0x0E000E /* HP DC Load Status 1 [RO] */
+#define CS43130_HP_AC_STAT_1 0x0E0010 /* HP AC Load Status 0 [RO] */
+#define CS43130_HP_AC_STAT_2 0x0E0011 /* HP AC Load Status 1 [RO] */
+#define CS43130_HP_LOAD_STAT 0x0E001A /* HP Load Status [RO] */
+#define CS43130_INT_STATUS_1 0x0F0000 /* Interrupt Status 1 */
+#define CS43130_INT_STATUS_2 0x0F0001 /* Interrupt Status 2 */
+#define CS43130_INT_STATUS_3 0x0F0002 /* Interrupt Status 3 */
+#define CS43130_INT_STATUS_4 0x0F0003 /* Interrupt Status 4 */
+#define CS43130_INT_STATUS_5 0x0F0004 /* Interrupt Status 5 */
+#define CS43130_INT_MASK_1 0x0F0010 /* Interrupt Mask 1 */
+#define CS43130_INT_MASK_2 0x0F0011 /* Interrupt Mask 2 */
+#define CS43130_INT_MASK_3 0x0F0012 /* Interrupt Mask 3 */
+#define CS43130_INT_MASK_4 0x0F0013 /* Interrupt Mask 4 */
+#define CS43130_INT_MASK_5 0x0F0014 /* Interrupt Mask 5 */
+
+#define CS43130_MCLK_SRC_SEL_MASK 0x03
+#define CS43130_MCLK_SRC_SEL_SHIFT 0
+#define CS43130_MCLK_INT_MASK 0x04
+#define CS43130_MCLK_INT_SHIFT 2
+#define CS43130_CH_BITSIZE_MASK 0x03
+#define CS43130_CH_EN_MASK 0x04
+#define CS43130_CH_EN_SHIFT 2
+#define CS43130_ASP_BITSIZE_MASK 0x03
+#define CS43130_XSP_BITSIZE_MASK 0x0C
+#define CS43130_XSP_BITSIZE_SHIFT 2
+#define CS43130_SP_BITSIZE_ASP_SHIFT 0
+#define CS43130_HP_DETECT_CTRL_SHIFT 6
+#define CS43130_HP_DETECT_CTRL_MASK (0x03 << CS43130_HP_DETECT_CTRL_SHIFT)
+#define CS43130_HP_DETECT_INV_SHIFT 5
+#define CS43130_HP_DETECT_INV_MASK (1 << CS43130_HP_DETECT_INV_SHIFT)
+
+/* CS43130_INT_MASK_1 */
+#define CS43130_HP_PLUG_INT_SHIFT 6
+#define CS43130_HP_PLUG_INT (1 << CS43130_HP_PLUG_INT_SHIFT)
+#define CS43130_HP_UNPLUG_INT_SHIFT 5
+#define CS43130_HP_UNPLUG_INT (1 << CS43130_HP_UNPLUG_INT_SHIFT)
+#define CS43130_XTAL_RDY_INT_SHIFT 4
+#define CS43130_XTAL_RDY_INT_MASK 0x10
+#define CS43130_XTAL_RDY_INT (1 << CS43130_XTAL_RDY_INT_SHIFT)
+#define CS43130_XTAL_ERR_INT_SHIFT 3
+#define CS43130_XTAL_ERR_INT (1 << CS43130_XTAL_ERR_INT_SHIFT)
+#define CS43130_PLL_RDY_INT_MASK 0x04
+#define CS43130_PLL_RDY_INT_SHIFT 2
+#define CS43130_PLL_RDY_INT (1 << CS43130_PLL_RDY_INT_SHIFT)
+
+/* CS43130_INT_MASK_4 */
+#define CS43130_INT_MASK_ALL 0xFF
+#define CS43130_HPLOAD_NO_DC_INT_SHIFT 7
+#define CS43130_HPLOAD_NO_DC_INT (1 << CS43130_HPLOAD_NO_DC_INT_SHIFT)
+#define CS43130_HPLOAD_UNPLUG_INT_SHIFT 6
+#define CS43130_HPLOAD_UNPLUG_INT (1 << CS43130_HPLOAD_UNPLUG_INT_SHIFT)
+#define CS43130_HPLOAD_OOR_INT_SHIFT 4
+#define CS43130_HPLOAD_OOR_INT (1 << CS43130_HPLOAD_OOR_INT_SHIFT)
+#define CS43130_HPLOAD_AC_INT_SHIFT 3
+#define CS43130_HPLOAD_AC_INT (1 << CS43130_HPLOAD_AC_INT_SHIFT)
+#define CS43130_HPLOAD_DC_INT_SHIFT 2
+#define CS43130_HPLOAD_DC_INT (1 << CS43130_HPLOAD_DC_INT_SHIFT)
+#define CS43130_HPLOAD_OFF_INT_SHIFT 1
+#define CS43130_HPLOAD_OFF_INT (1 << CS43130_HPLOAD_OFF_INT_SHIFT)
+#define CS43130_HPLOAD_ON_INT 1
+
+/* CS43130_HP_LOAD_1 */
+#define CS43130_HPLOAD_EN_SHIFT 7
+#define CS43130_HPLOAD_EN (1 << CS43130_HPLOAD_EN_SHIFT)
+#define CS43130_HPLOAD_CHN_SEL_SHIFT 4
+#define CS43130_HPLOAD_CHN_SEL (1 << CS43130_HPLOAD_CHN_SEL_SHIFT)
+#define CS43130_HPLOAD_AC_START_SHIFT 1
+#define CS43130_HPLOAD_AC_START (1 << CS43130_HPLOAD_AC_START_SHIFT)
+#define CS43130_HPLOAD_DC_START 1
+
+/* Reg CS43130_SP_BITSIZE */
+#define CS43130_SP_BIT_SIZE_8 0x03
+#define CS43130_SP_BIT_SIZE_16 0x02
+#define CS43130_SP_BIT_SIZE_24 0x01
+#define CS43130_SP_BIT_SIZE_32 0x00
+
+/* Reg CS43130_SP_CH_SZ_EN */
+#define CS43130_CH_BIT_SIZE_8 0x00
+#define CS43130_CH_BIT_SIZE_16 0x01
+#define CS43130_CH_BIT_SIZE_24 0x02
+#define CS43130_CH_BIT_SIZE_32 0x03
+
+/* PLL */
+#define CS43130_PLL_START_MASK 0x01
+#define CS43130_PLL_MODE_MASK 0x02
+#define CS43130_PLL_MODE_SHIFT 1
+
+#define CS43130_PLL_REF_PREDIV_MASK 0x3
+
+#define CS43130_SP_STP_MASK 0x10
+#define CS43130_SP_STP_SHIFT 4
+#define CS43130_SP_5050_MASK 0x08
+#define CS43130_SP_5050_SHIFT 3
+#define CS43130_SP_FSD_MASK 0x07
+
+#define CS43130_SP_MODE_MASK 0x10
+#define CS43130_SP_MODE_SHIFT 4
+#define CS43130_SP_SCPOL_OUT_MASK 0x08
+#define CS43130_SP_SCPOL_OUT_SHIFT 3
+#define CS43130_SP_SCPOL_IN_MASK 0x04
+#define CS43130_SP_SCPOL_IN_SHIFT 2
+#define CS43130_SP_LCPOL_OUT_MASK 0x02
+#define CS43130_SP_LCPOL_OUT_SHIFT 1
+#define CS43130_SP_LCPOL_IN_MASK 0x01
+#define CS43130_SP_LCPOL_IN_SHIFT 0
+
+/* Reg CS43130_PWDN_CTL */
+#define CS43130_PDN_XSP_MASK 0x80
+#define CS43130_PDN_XSP_SHIFT 7
+#define CS43130_PDN_ASP_MASK 0x40
+#define CS43130_PDN_ASP_SHIFT 6
+#define CS43130_PDN_DSPIF_MASK 0x20
+#define CS43130_PDN_DSDIF_SHIFT 5
+#define CS43130_PDN_HP_MASK 0x10
+#define CS43130_PDN_HP_SHIFT 4
+#define CS43130_PDN_XTAL_MASK 0x08
+#define CS43130_PDN_XTAL_SHIFT 3
+#define CS43130_PDN_PLL_MASK 0x04
+#define CS43130_PDN_PLL_SHIFT 2
+#define CS43130_PDN_CLKOUT_MASK 0x02
+#define CS43130_PDN_CLKOUT_SHIFT 1
+
+/* Reg CS43130_HP_OUT_CTL_1 */
+#define CS43130_HP_IN_EN_SHIFT 3
+#define CS43130_HP_IN_EN_MASK 0x08
+
+/* Reg CS43130_PAD_INT_CFG */
+#define CS43130_ASP_3ST_MASK 0x01
+#define CS43130_XSP_3ST_MASK 0x02
+
+/* Reg CS43130_PLL_SET_2 */
+#define CS43130_PLL_DIV_DATA_MASK 0x000000FF
+#define CS43130_PLL_DIV_FRAC_0_DATA_SHIFT 0
+
+/* Reg CS43130_PLL_SET_3 */
+#define CS43130_PLL_DIV_FRAC_1_DATA_SHIFT 8
+
+/* Reg CS43130_PLL_SET_4 */
+#define CS43130_PLL_DIV_FRAC_2_DATA_SHIFT 16
+
+/* Reg CS43130_SP_DEN_1 */
+#define CS43130_SP_M_LSB_DATA_MASK 0x00FF
+#define CS43130_SP_M_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_DEN_2 */
+#define CS43130_SP_M_MSB_DATA_MASK 0xFF00
+#define CS43130_SP_M_MSB_DATA_SHIFT 8
+
+/* Reg CS43130_SP_NUM_1 */
+#define CS43130_SP_N_LSB_DATA_MASK 0x00FF
+#define CS43130_SP_N_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_NUM_2 */
+#define CS43130_SP_N_MSB_DATA_MASK 0xFF00
+#define CS43130_SP_N_MSB_DATA_SHIFT 8
+
+/* Reg CS43130_SP_LRCK_HI_TIME_1 */
+#define CS43130_SP_LCHI_DATA_MASK 0x00FF
+#define CS43130_SP_LCHI_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_LRCK_HI_TIME_2 */
+#define CS43130_SP_LCHI_MSB_DATA_SHIFT 8
+
+/* Reg CS43130_SP_LRCK_PERIOD_1 */
+#define CS43130_SP_LCPR_DATA_MASK 0x00FF
+#define CS43130_SP_LCPR_LSB_DATA_SHIFT 0
+
+/* Reg CS43130_SP_LRCK_PERIOD_2 */
+#define CS43130_SP_LCPR_MSB_DATA_SHIFT 8
+
+#define CS43130_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS43130_DOP_FORMATS (SNDRV_PCM_FMTBIT_DSD_U16_LE | \
+ SNDRV_PCM_FMTBIT_DSD_U16_BE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* Reg CS43130_CRYSTAL_SET */
+#define CS43130_XTAL_IBIAS_MASK 0x07
+
+/* Reg CS43130_PATH_CTL_1 */
+#define CS43130_MUTE_MASK 0x03
+#define CS43130_MUTE_EN 0x03
+
+/* Reg CS43130_DSD_INT_CFG */
+#define CS43130_DSD_MASTER 0x04
+
+/* Reg CS43130_DSD_PATH_CTL_2 */
+#define CS43130_DSD_SRC_MASK 0x60
+#define CS43130_DSD_SRC_SHIFT 5
+#define CS43130_DSD_EN_SHIFT 4
+#define CS43130_DSD_SPEED_MASK 0x04
+#define CS43130_DSD_SPEED_SHIFT 2
+
+/* Reg CS43130_DSD_PCM_MIX_CTL */
+#define CS43130_MIX_PCM_PREP_SHIFT 1
+#define CS43130_MIX_PCM_PREP_MASK 0x02
+
+#define CS43130_MIX_PCM_DSD_SHIFT 0
+#define CS43130_MIX_PCM_DSD_MASK 0x01
+
+/* Reg CS43130_HP_MEAS_LOAD */
+#define CS43130_HP_MEAS_LOAD_MASK 0x000000FF
+#define CS43130_HP_MEAS_LOAD_1_SHIFT 0
+#define CS43130_HP_MEAS_LOAD_2_SHIFT 8
+
+#define CS43130_MCLK_22M 22579200
+#define CS43130_MCLK_24M 24576000
+
+#define CS43130_LINEOUT_LOAD 5000
+#define CS43130_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
+#define CS43130_JACK_HEADPHONE (SND_JACK_MECHANICAL | \
+ SND_JACK_HEADPHONE)
+#define CS43130_JACK_MASK (SND_JACK_MECHANICAL | \
+ SND_JACK_LINEOUT | \
+ SND_JACK_HEADPHONE)
+
+enum cs43130_dsd_src {
+ CS43130_DSD_SRC_DSD = 0,
+ CS43130_DSD_SRC_ASP = 2,
+ CS43130_DSD_SRC_XSP = 3,
+};
+
+enum cs43130_asp_rate {
+ CS43130_ASP_SPRATE_32K = 0,
+ CS43130_ASP_SPRATE_44_1K,
+ CS43130_ASP_SPRATE_48K,
+ CS43130_ASP_SPRATE_88_2K,
+ CS43130_ASP_SPRATE_96K,
+ CS43130_ASP_SPRATE_176_4K,
+ CS43130_ASP_SPRATE_192K,
+ CS43130_ASP_SPRATE_352_8K,
+ CS43130_ASP_SPRATE_384K,
+};
+
+enum cs43130_mclk_src_sel {
+ CS43130_MCLK_SRC_EXT = 0,
+ CS43130_MCLK_SRC_PLL,
+ CS43130_MCLK_SRC_RCO
+};
+
+enum cs43130_mclk_int_freq {
+ CS43130_MCLK_24P5 = 0,
+ CS43130_MCLK_22P5,
+};
+
+enum cs43130_xtal_ibias {
+ CS43130_XTAL_UNUSED = -1,
+ CS43130_XTAL_IBIAS_15UA = 2,
+ CS43130_XTAL_IBIAS_12_5UA = 4,
+ CS43130_XTAL_IBIAS_7_5UA = 6,
+};
+
+enum cs43130_dai_id {
+ CS43130_ASP_PCM_DAI = 0,
+ CS43130_ASP_DOP_DAI,
+ CS43130_XSP_DOP_DAI,
+ CS43130_XSP_DSD_DAI,
+ CS43130_DAI_ID_MAX,
+};
+
+struct cs43130_clk_gen {
+ unsigned int mclk_int;
+ int fs;
+ struct u16_fract v;
+};
+
+/* frm_size = 16 */
+static const struct cs43130_clk_gen cs43130_16_clk_gen[] = {
+ { 22579200, 32000, .v = { 10, 441, }, },
+ { 22579200, 44100, .v = { 1, 32, }, },
+ { 22579200, 48000, .v = { 5, 147, }, },
+ { 22579200, 88200, .v = { 1, 16, }, },
+ { 22579200, 96000, .v = { 10, 147, }, },
+ { 22579200, 176400, .v = { 1, 8, }, },
+ { 22579200, 192000, .v = { 20, 147, }, },
+ { 22579200, 352800, .v = { 1, 4, }, },
+ { 22579200, 384000, .v = { 40, 147, }, },
+ { 24576000, 32000, .v = { 1, 48, }, },
+ { 24576000, 44100, .v = { 147, 5120, }, },
+ { 24576000, 48000, .v = { 1, 32, }, },
+ { 24576000, 88200, .v = { 147, 2560, }, },
+ { 24576000, 96000, .v = { 1, 16, }, },
+ { 24576000, 176400, .v = { 147, 1280, }, },
+ { 24576000, 192000, .v = { 1, 8, }, },
+ { 24576000, 352800, .v = { 147, 640, }, },
+ { 24576000, 384000, .v = { 1, 4, }, },
+};
+
+/* frm_size = 32 */
+static const struct cs43130_clk_gen cs43130_32_clk_gen[] = {
+ { 22579200, 32000, .v = { 20, 441, }, },
+ { 22579200, 44100, .v = { 1, 16, }, },
+ { 22579200, 48000, .v = { 10, 147, }, },
+ { 22579200, 88200, .v = { 1, 8, }, },
+ { 22579200, 96000, .v = { 20, 147, }, },
+ { 22579200, 176400, .v = { 1, 4, }, },
+ { 22579200, 192000, .v = { 40, 147, }, },
+ { 22579200, 352800, .v = { 1, 2, }, },
+ { 22579200, 384000, .v = { 80, 147, }, },
+ { 24576000, 32000, .v = { 1, 24, }, },
+ { 24576000, 44100, .v = { 147, 2560, }, },
+ { 24576000, 48000, .v = { 1, 16, }, },
+ { 24576000, 88200, .v = { 147, 1280, }, },
+ { 24576000, 96000, .v = { 1, 8, }, },
+ { 24576000, 176400, .v = { 147, 640, }, },
+ { 24576000, 192000, .v = { 1, 4, }, },
+ { 24576000, 352800, .v = { 147, 320, }, },
+ { 24576000, 384000, .v = { 1, 2, }, },
+};
+
+/* frm_size = 48 */
+static const struct cs43130_clk_gen cs43130_48_clk_gen[] = {
+ { 22579200, 32000, .v = { 100, 147, }, },
+ { 22579200, 44100, .v = { 3, 32, }, },
+ { 22579200, 48000, .v = { 5, 49, }, },
+ { 22579200, 88200, .v = { 3, 16, }, },
+ { 22579200, 96000, .v = { 10, 49, }, },
+ { 22579200, 176400, .v = { 3, 8, }, },
+ { 22579200, 192000, .v = { 20, 49, }, },
+ { 22579200, 352800, .v = { 3, 4, }, },
+ { 22579200, 384000, .v = { 40, 49, }, },
+ { 24576000, 32000, .v = { 1, 16, }, },
+ { 24576000, 44100, .v = { 441, 5120, }, },
+ { 24576000, 48000, .v = { 3, 32, }, },
+ { 24576000, 88200, .v = { 441, 2560, }, },
+ { 24576000, 96000, .v = { 3, 16, }, },
+ { 24576000, 176400, .v = { 441, 1280, }, },
+ { 24576000, 192000, .v = { 3, 8, }, },
+ { 24576000, 352800, .v = { 441, 640, }, },
+ { 24576000, 384000, .v = { 3, 4, }, },
+};
+
+/* frm_size = 64 */
+static const struct cs43130_clk_gen cs43130_64_clk_gen[] = {
+ { 22579200, 32000, .v = { 40, 441, }, },
+ { 22579200, 44100, .v = { 1, 8, }, },
+ { 22579200, 48000, .v = { 20, 147, }, },
+ { 22579200, 88200, .v = { 1, 4, }, },
+ { 22579200, 96000, .v = { 40, 147, }, },
+ { 22579200, 176400, .v = { 1, 2, }, },
+ { 22579200, 192000, .v = { 80, 147, }, },
+ { 22579200, 352800, .v = { 1, 1, }, },
+ { 24576000, 32000, .v = { 1, 12, }, },
+ { 24576000, 44100, .v = { 147, 1280, }, },
+ { 24576000, 48000, .v = { 1, 8, }, },
+ { 24576000, 88200, .v = { 147, 640, }, },
+ { 24576000, 96000, .v = { 1, 4, }, },
+ { 24576000, 176400, .v = { 147, 320, }, },
+ { 24576000, 192000, .v = { 1, 2, }, },
+ { 24576000, 352800, .v = { 147, 160, }, },
+ { 24576000, 384000, .v = { 1, 1, }, },
+};
+
+struct cs43130_bitwidth_map {
+ unsigned int bitwidth;
+ u8 sp_bit;
+ u8 ch_bit;
+};
+
+struct cs43130_rate_map {
+ int fs;
+ int val;
+};
+
+#define HP_LEFT 0
+#define HP_RIGHT 1
+#define CS43130_AC_FREQ 10
+#define CS43130_DC_THRESHOLD 2
+
+#define CS43130_NUM_SUPPLIES 5
+static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD",
+ "VL",
+};
+
+#define CS43130_NUM_INT 5 /* number of interrupt status reg */
+
+struct cs43130_dai {
+ unsigned int sclk;
+ unsigned int dai_format;
+ unsigned int dai_mode;
+ unsigned int dai_invert;
+};
+
+struct cs43130_private {
+ struct device *dev;
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ unsigned int dev_id; /* codec device ID */
+ int xtal_ibias;
+ bool has_irq_line;
+
+ /* shared by both DAIs */
+ struct mutex clk_mutex;
+ int clk_req;
+ bool pll_bypass;
+ struct completion xtal_rdy;
+ struct completion pll_rdy;
+ unsigned int mclk;
+ unsigned int mclk_int;
+ int mclk_int_src;
+
+ /* DAI specific */
+ struct cs43130_dai dais[CS43130_DAI_ID_MAX];
+
+ /* HP load specific */
+ bool dc_meas;
+ bool ac_meas;
+ bool hpload_done;
+ struct completion hpload_evt;
+ unsigned int hpload_stat;
+ u16 hpload_dc[2];
+ u16 dc_threshold[CS43130_DC_THRESHOLD];
+ u16 ac_freq[CS43130_AC_FREQ];
+ u16 hpload_ac[CS43130_AC_FREQ][2];
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ struct snd_soc_jack jack;
+};
+
+#endif /* __CS43130_H__ */
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
new file mode 100644
index 000000000000..b726e22ef57d
--- /dev/null
+++ b/sound/soc/codecs/cs4341.c
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Cirrus Logic CS4341A ALSA SoC Codec Driver
+ * Author: Alexander Shiyan <shc_work@mail.ru>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define CS4341_REG_MODE1 0x00
+#define CS4341_REG_MODE2 0x01
+#define CS4341_REG_MIX 0x02
+#define CS4341_REG_VOLA 0x03
+#define CS4341_REG_VOLB 0x04
+
+#define CS4341_MODE2_DIF (7 << 4)
+#define CS4341_MODE2_DIF_I2S_24 (0 << 4)
+#define CS4341_MODE2_DIF_I2S_16 (1 << 4)
+#define CS4341_MODE2_DIF_LJ_24 (2 << 4)
+#define CS4341_MODE2_DIF_RJ_24 (3 << 4)
+#define CS4341_MODE2_DIF_RJ_16 (5 << 4)
+#define CS4341_VOLX_MUTE (1 << 7)
+
+struct cs4341_priv {
+ unsigned int fmt;
+ struct regmap *regmap;
+ struct regmap_config regcfg;
+};
+
+static const struct reg_default cs4341_reg_defaults[] = {
+ { CS4341_REG_MODE1, 0x00 },
+ { CS4341_REG_MODE2, 0x82 },
+ { CS4341_REG_MIX, 0x49 },
+ { CS4341_REG_VOLA, 0x80 },
+ { CS4341_REG_VOLB, 0x80 },
+};
+
+static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs4341_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
+ unsigned int mode = 0;
+ int b24 = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ b24 = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported PCM format 0x%08x.\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ switch (cs4341->fmt) {
+ case SND_SOC_DAIFMT_I2S:
+ mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode = CS4341_MODE2_DIF_LJ_24;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported DAI format 0x%08x.\n",
+ cs4341->fmt);
+ return -EINVAL;
+ }
+
+ return snd_soc_component_update_bits(component, CS4341_REG_MODE2,
+ CS4341_MODE2_DIF, mode);
+}
+
+static int cs4341_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA,
+ CS4341_VOLX_MUTE,
+ mute ? CS4341_VOLX_MUTE : 0);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_component_update_bits(component, CS4341_REG_VOLB,
+ CS4341_VOLX_MUTE,
+ mute ? CS4341_VOLX_MUTE : 0);
+}
+
+static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0);
+
+static const char * const deemph[] = {
+ "None", "44.1k", "48k", "32k",
+};
+
+static const struct soc_enum deemph_enum =
+ SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph);
+
+static const char * const srzc[] = {
+ "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",
+};
+
+static const struct soc_enum srzc_enum =
+ SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc);
+
+
+static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OutA"),
+ SND_SOC_DAPM_OUTPUT("OutB"),
+};
+
+static const struct snd_soc_dapm_route cs4341_routes[] = {
+ { "OutA", NULL, "HiFi DAC" },
+ { "OutB", NULL, "HiFi DAC" },
+ { "DAC Playback", NULL, "OutA" },
+ { "DAC Playback", NULL, "OutB" },
+};
+
+static const struct snd_kcontrol_new cs4341_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv),
+ SOC_ENUM("De-Emphasis Control", deemph_enum),
+ SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum),
+ SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0),
+ SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),
+};
+
+static const struct snd_soc_dai_ops cs4341_dai_ops = {
+ .set_fmt = cs4341_set_fmt,
+ .hw_params = cs4341_hw_params,
+ .mute_stream = cs4341_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver cs4341_dai = {
+ .name = "cs4341a-hifi",
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &cs4341_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_cs4341 = {
+ .controls = cs4341_controls,
+ .num_controls = ARRAY_SIZE(cs4341_controls),
+ .dapm_widgets = cs4341_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets),
+ .dapm_routes = cs4341_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4341_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct of_device_id __maybe_unused cs4341_dt_ids[] = {
+ { .compatible = "cirrus,cs4341a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4341_dt_ids);
+
+static int cs4341_probe(struct device *dev)
+{
+ struct cs4341_priv *cs4341 = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++)
+ regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg,
+ cs4341_reg_defaults[i].def);
+
+ return devm_snd_soc_register_component(dev, &soc_component_cs4341,
+ &cs4341_dai, 1);
+}
+
+#if IS_ENABLED(CONFIG_I2C)
+static int cs4341_i2c_probe(struct i2c_client *i2c)
+{
+ struct cs4341_priv *cs4341;
+
+ cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL);
+ if (!cs4341)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, cs4341);
+
+ cs4341->regcfg.reg_bits = 8;
+ cs4341->regcfg.val_bits = 8;
+ cs4341->regcfg.max_register = CS4341_REG_VOLB;
+ cs4341->regcfg.cache_type = REGCACHE_FLAT;
+ cs4341->regcfg.reg_defaults = cs4341_reg_defaults;
+ cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
+ cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg);
+ if (IS_ERR(cs4341->regmap))
+ return PTR_ERR(cs4341->regmap);
+
+ return cs4341_probe(&i2c->dev);
+}
+
+static const struct i2c_device_id cs4341_i2c_id[] = {
+ { "cs4341" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);
+
+static struct i2c_driver cs4341_i2c_driver = {
+ .driver = {
+ .name = "cs4341-i2c",
+ .of_match_table = of_match_ptr(cs4341_dt_ids),
+ },
+ .probe = cs4341_i2c_probe,
+ .id_table = cs4341_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static bool cs4341_reg_readable(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static int cs4341_spi_probe(struct spi_device *spi)
+{
+ struct cs4341_priv *cs4341;
+ int ret;
+
+ cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL);
+ if (!cs4341)
+ return -ENOMEM;
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = 6000000;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, cs4341);
+
+ cs4341->regcfg.reg_bits = 16;
+ cs4341->regcfg.val_bits = 8;
+ cs4341->regcfg.write_flag_mask = 0x20;
+ cs4341->regcfg.max_register = CS4341_REG_VOLB;
+ cs4341->regcfg.cache_type = REGCACHE_FLAT;
+ cs4341->regcfg.readable_reg = cs4341_reg_readable;
+ cs4341->regcfg.reg_defaults = cs4341_reg_defaults;
+ cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
+ cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg);
+ if (IS_ERR(cs4341->regmap))
+ return PTR_ERR(cs4341->regmap);
+
+ return cs4341_probe(&spi->dev);
+}
+
+static const struct spi_device_id cs4341_spi_ids[] = {
+ { "cs4341a" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cs4341_spi_ids);
+
+static struct spi_driver cs4341_spi_driver = {
+ .driver = {
+ .name = "cs4341-spi",
+ .of_match_table = of_match_ptr(cs4341_dt_ids),
+ },
+ .probe = cs4341_spi_probe,
+ .id_table = cs4341_spi_ids,
+};
+#endif
+
+static int __init cs4341_init(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&cs4341_i2c_driver);
+ if (ret)
+ return ret;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&cs4341_spi_driver);
+#endif
+
+ return ret;
+}
+module_init(cs4341_init);
+
+static void __exit cs4341_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&cs4341_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&cs4341_spi_driver);
+#endif
+}
+module_exit(cs4341_exit);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index 0ac8fc5ed4ae..d9a9c34fffe3 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -1,26 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs4349.c -- CS4349 ALSA Soc Audio driver
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Authors: Tim Howe <Tim.Howe@cirrus.com>
- *
- * This program is free software; you can 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -74,8 +70,8 @@ static bool cs4349_writeable_register(struct device *dev, unsigned int reg)
static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4349_private *cs4349 = snd_soc_component_get_drvdata(component);
unsigned int fmt;
fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
@@ -97,8 +93,8 @@ static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4349_private *cs4349 = snd_soc_component_get_drvdata(component);
int fmt, ret;
cs4349->rate = params_rate(params);
@@ -126,7 +122,7 @@ static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- ret = snd_soc_update_bits(codec, CS4349_MODE, DIF_MASK,
+ ret = snd_soc_component_update_bits(component, CS4349_MODE, DIF_MASK,
MODE_FORMAT(fmt));
if (ret < 0)
return ret;
@@ -134,16 +130,16 @@ static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cs4349_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs4349_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int reg;
reg = 0;
if (mute)
reg = MUTE_AB_MASK;
- return snd_soc_update_bits(codec, CS4349_MUTE, MUTE_AB_MASK, reg);
+ return snd_soc_component_update_bits(component, CS4349_MUTE, MUTE_AB_MASK, reg);
}
static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0);
@@ -226,12 +222,9 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
{"OutputB", NULL, "HiFi DAC"},
};
-#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
+#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000
@@ -239,7 +232,8 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
static const struct snd_soc_dai_ops cs4349_dai_ops = {
.hw_params = cs4349_pcm_hw_params,
.set_fmt = cs4349_set_dai_fmt,
- .digital_mute = cs4349_digital_mute,
+ .mute_stream = cs4349_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs4349_dai = {
@@ -252,17 +246,19 @@ static struct snd_soc_dai_driver cs4349_dai = {
.formats = CS4349_PCM_FORMATS,
},
.ops = &cs4349_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static struct snd_soc_codec_driver soc_codec_dev_cs4349 = {
+static const struct snd_soc_component_driver soc_component_dev_cs4349 = {
.controls = cs4349_snd_controls,
.num_controls = ARRAY_SIZE(cs4349_snd_controls),
-
.dapm_widgets = cs4349_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets),
.dapm_routes = cs4349_routes,
.num_dapm_routes = ARRAY_SIZE(cs4349_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config cs4349_regmap = {
@@ -274,11 +270,10 @@ static const struct regmap_config cs4349_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults),
.readable_reg = cs4349_readable_register,
.writeable_reg = cs4349_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs4349_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4349_i2c_probe(struct i2c_client *client)
{
struct cs4349_private *cs4349;
int ret;
@@ -304,23 +299,19 @@ static int cs4349_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, cs4349);
- return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4349,
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_cs4349,
&cs4349_dai, 1);
}
-static int cs4349_i2c_remove(struct i2c_client *client)
+static void cs4349_i2c_remove(struct i2c_client *client)
{
struct cs4349_private *cs4349 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
-
/* Hold down reset */
gpiod_set_value_cansleep(cs4349->reset_gpio, 0);
-
- return 0;
}
-#ifdef CONFIG_PM
static int cs4349_runtime_suspend(struct device *dev)
{
struct cs4349_private *cs4349 = dev_get_drvdata(dev);
@@ -354,11 +345,9 @@ static int cs4349_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops cs4349_runtime_pm = {
- SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, NULL)
};
static const struct of_device_id cs4349_of_match[] = {
@@ -369,7 +358,7 @@ static const struct of_device_id cs4349_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4349_of_match);
static const struct i2c_device_id cs4349_i2c_id[] = {
- {"cs4349", 0},
+ {"cs4349"},
{}
};
@@ -379,6 +368,7 @@ static struct i2c_driver cs4349_i2c_driver = {
.driver = {
.name = "cs4349",
.of_match_table = cs4349_of_match,
+ .pm = pm_ptr(&cs4349_runtime_pm),
},
.id_table = cs4349_i2c_id,
.probe = cs4349_i2c_probe,
diff --git a/sound/soc/codecs/cs4349.h b/sound/soc/codecs/cs4349.h
index d58c06a25358..bf31405f7f05 100644
--- a/sound/soc/codecs/cs4349.h
+++ b/sound/soc/codecs/cs4349.h
@@ -1,19 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS4349 codec driver
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Author: Tim Howe <Tim.Howe@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.
- *
*/
#ifndef __CS4349_H__
diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c
new file mode 100644
index 000000000000..da64e0a1db28
--- /dev/null
+++ b/sound/soc/codecs/cs47l15.c
@@ -0,0 +1,1505 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L15 codec
+//
+// Copyright (C) 2016-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define CS47L15_NUM_ADSP 1
+#define CS47L15_MONO_OUTPUTS 1
+
+/* Mid-mode registers */
+#define CS47L15_ADC_INT_BIAS_MASK 0x3800
+#define CS47L15_ADC_INT_BIAS_SHIFT 11
+#define CS47L15_PGA_BIAS_SEL_MASK 0x03
+#define CS47L15_PGA_BIAS_SEL_SHIFT 0
+
+#define DRV_NAME "cs47l15-codec"
+
+struct cs47l15 {
+ struct madera_priv core;
+ struct madera_fll fll[2];
+
+ bool in1_lp_mode;
+};
+
+static const struct cs_dsp_region cs47l15_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const char * const cs47l15_outdemux_texts[] = {
+ "HPOUT",
+ "EPOUT",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs47l15_outdemux_enum, SND_SOC_NOPM, 0,
+ cs47l15_outdemux_texts);
+
+static const struct snd_kcontrol_new cs47l15_outdemux =
+ SOC_DAPM_ENUM_EXT("HPOUT1 Demux", cs47l15_outdemux_enum,
+ madera_out1_demux_get, madera_out1_demux_put);
+
+static int cs47l15_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l15->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret);
+ return ret;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l15->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L15_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static int cs47l15_in1_adc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !!cs47l15->in1_lp_mode;
+
+ return 0;
+}
+
+static int cs47l15_in1_adc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+
+ if (!!ucontrol->value.integer.value[0] == cs47l15->in1_lp_mode)
+ return 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ /* Set IN1 to normal mode */
+ snd_soc_component_update_bits(component, MADERA_DMIC1L_CONTROL,
+ MADERA_IN1_OSR_MASK,
+ 5 << MADERA_IN1_OSR_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_ADC_INT_BIAS,
+ CS47L15_ADC_INT_BIAS_MASK,
+ 4 << CS47L15_ADC_INT_BIAS_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_PGA_BIAS_SEL,
+ CS47L15_PGA_BIAS_SEL_MASK, 0);
+ cs47l15->in1_lp_mode = false;
+ break;
+ default:
+ /* Set IN1 to LP mode */
+ snd_soc_component_update_bits(component, MADERA_DMIC1L_CONTROL,
+ MADERA_IN1_OSR_MASK,
+ 4 << MADERA_IN1_OSR_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_ADC_INT_BIAS,
+ CS47L15_ADC_INT_BIAS_MASK,
+ 1 << CS47L15_ADC_INT_BIAS_SHIFT);
+ snd_soc_component_update_bits(component, CS47L15_PGA_BIAS_SEL,
+ CS47L15_PGA_BIAS_SEL_MASK,
+ 3 << CS47L15_PGA_BIAS_SEL_SHIFT);
+ cs47l15->in1_lp_mode = true;
+ break;
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs47l15_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, MADERA_IN2R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUTL", MADERA_OUT4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+SOC_SINGLE_BOOL_EXT("IN1 LP Mode Switch", 0,
+ cs47l15_in1_adc_get, cs47l15_in1_adc_put),
+
+CS47L15_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L15_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L15_NG_SRC("SPKOUTL", MADERA_NOISE_GATE_SELECT_4L),
+CS47L15_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L15_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUTL, MADERA_OUT4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l15_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "SPKOUTL", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l15_aec_loopback_values[] = {
+ 0, 1, 6, 8, 9,
+};
+
+static const struct soc_enum cs47l15_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l15_aec_loopback_texts),
+ cs47l15_aec_loopback_texts,
+ cs47l15_aec_loopback_values);
+
+static const struct soc_enum cs47l15_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l15_aec_loopback_texts),
+ cs47l15_aec_loopback_texts,
+ cs47l15_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l15_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l15_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l15_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget cs47l15_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BRN"),
+SND_SOC_DAPM_INPUT("IN1BRP"),
+SND_SOC_DAPM_INPUT("IN2N"),
+SND_SOC_DAPM_INPUT("IN2P"),
+SND_SOC_DAPM_INPUT("SPKRXDAT"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux),
+SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in MADERA_MIXER_INPUT_ROUTES
+ */
+
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l15_aec_loopback_mux[0]),
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l15_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l15_adsp_power_ev),
+
+/* end of ordered widget list */
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }
+
+static const struct snd_soc_dapm_route cs47l15_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT4L", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BRN" },
+ { "IN1R Analog Mux", "B", "IN1BRP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1ALP" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1ALP" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Mode", "Analog", "IN2N" },
+ { "IN2L Mode", "Analog", "IN2P" },
+
+ { "IN2L Mode", "Digital", "SPKRXDAT" },
+ { "IN2R Mode", "Digital", "SPKRXDAT" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1 Demux", NULL, "OUT1L" },
+ { "HPOUT1 Demux", NULL, "OUT1R" },
+
+ { "OUT1R", NULL, "HPOUT1 Mono Mux" },
+ { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" },
+
+ { "HPOUTL", "HPOUT", "HPOUT1 Demux" },
+ { "HPOUTR", "HPOUT", "HPOUT1 Demux" },
+ { "EPOUTP", "EPOUT", "HPOUT1 Demux" },
+ { "EPOUTN", "EPOUT", "HPOUT1 Demux" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs47l15_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l15->fll[0], source, fref,
+ fout);
+ case MADERA_FLLAO_REFCLK:
+ return madera_set_fll_ao_refclk(&cs47l15->fll[1], source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l15->fll[0], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops cs47l15_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs47l15_dai[] = {
+ {
+ .name = "cs47l15-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l15-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l15-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l15-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l15_dai_ops,
+ },
+ {
+ .name = "cs47l15-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l15_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l15->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l15-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l15_adsp2_irq(int irq, void *data)
+{
+ struct cs47l15 *cs47l15 = data;
+ struct madera_priv *priv = &cs47l15->core;
+ struct madera *madera = priv->madera;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[0]);
+ if (ret == -ENODEV) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_route cs47l15_mono_routes[] = {
+ { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" },
+};
+
+static int cs47l15_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l15->core.madera;
+ int ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_to_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, cs47l15_mono_routes,
+ ARRAY_SIZE(cs47l15_mono_routes),
+ CS47L15_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L15_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs47l15->core.adsp[0], component);
+
+ return 0;
+}
+
+static void cs47l15_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l15->core.madera;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ wm_adsp2_component_remove(&cs47l15->core.adsp[0], component);
+}
+
+#define CS47L15_DIG_VU 0x0200
+
+static unsigned int cs47l15_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l15_compress_ops = {
+ .open = &cs47l15_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l15 = {
+ .probe = &cs47l15_component_probe,
+ .remove = &cs47l15_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l15_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l15_compress_ops,
+ .controls = cs47l15_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l15_snd_controls),
+ .dapm_widgets = cs47l15_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l15_dapm_widgets),
+ .dapm_routes = cs47l15_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l15_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l15_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l15 *cs47l15;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l15_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l15 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l15),
+ GFP_KERNEL);
+ if (!cs47l15)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l15);
+
+ cs47l15->core.madera = madera;
+ cs47l15->core.dev = &pdev->dev;
+ cs47l15->core.num_inputs = 4;
+
+ ret = madera_core_init(&cs47l15->core);
+ if (ret)
+ return ret;
+
+ ret = madera_init_overheat(&cs47l15->core);
+ if (ret)
+ goto error_core;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l15_adsp2_irq,
+ cs47l15);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_overheat;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ cs47l15->core.adsp[0].part = "cs47l15";
+ cs47l15->core.adsp[0].cs_dsp.num = 1;
+ cs47l15->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ cs47l15->core.adsp[0].cs_dsp.rev = 2;
+ cs47l15->core.adsp[0].cs_dsp.dev = madera->dev;
+ cs47l15->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l15->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1;
+ cs47l15->core.adsp[0].cs_dsp.mem = cs47l15_dsp1_regions;
+ cs47l15->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l15_dsp1_regions);
+
+ cs47l15->core.adsp[0].cs_dsp.lock_regions =
+ CS_ADSP2_REGION_1 | CS_ADSP2_REGION_2 | CS_ADSP2_REGION_3;
+
+ ret = wm_adsp2_init(&cs47l15->core.adsp[0]);
+ if (ret != 0)
+ goto error_dsp_irq;
+
+ ret = madera_init_bus_error_irq(&cs47l15->core, 0, wm_adsp2_bus_error);
+ if (ret)
+ goto error_adsp;
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l15->fll[0]);
+ madera_init_fll(madera, 4, MADERA_FLLAO_CONTROL_1 - 1,
+ &cs47l15->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l15_dai); i++)
+ madera_init_dai(&cs47l15->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l15_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l15_digital_vu[i],
+ CS47L15_DIG_VU, CS47L15_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l15,
+ cs47l15_dai,
+ ARRAY_SIZE(cs47l15_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+ madera_free_bus_error_irq(&cs47l15->core, 0);
+error_adsp:
+ wm_adsp2_remove(&cs47l15->core.adsp[0]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l15);
+error_overheat:
+ madera_free_overheat(&cs47l15->core);
+error_core:
+ madera_core_free(&cs47l15->core);
+
+ return ret;
+}
+
+static void cs47l15_remove(struct platform_device *pdev)
+{
+ struct cs47l15 *cs47l15 = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ madera_free_bus_error_irq(&cs47l15->core, 0);
+
+ wm_adsp2_remove(&cs47l15->core.adsp[0]);
+
+ madera_set_irq_wake(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l15);
+ madera_free_overheat(&cs47l15->core);
+ madera_core_free(&cs47l15->core);
+}
+
+static struct platform_driver cs47l15_codec_driver = {
+ .driver = {
+ .name = "cs47l15-codec",
+ },
+ .probe = &cs47l15_probe,
+ .remove = cs47l15_remove,
+};
+
+module_platform_driver(cs47l15_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L15 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Jaswinder Jassal <jjassal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l15-codec");
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index dc5ae7f7a1bd..5100b1aac902 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
*
* Copyright 2015 Cirrus Logic Inc.
*
* Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can 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>
@@ -33,30 +30,53 @@
#include "wm_adsp.h"
#include "cs47l24.h"
+#define DRV_NAME "cs47l24-codec"
+
struct cs47l24_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
};
-static const struct wm_adsp_region cs47l24_dsp2_regions[] = {
+static const struct cs_dsp_region cs47l24_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x200000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x280000 },
{ .type = WMFW_ADSP2_XM, .base = 0x290000 },
{ .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
};
-static const struct wm_adsp_region cs47l24_dsp3_regions[] = {
+static const struct cs_dsp_region cs47l24_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x300000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x380000 },
{ .type = WMFW_ADSP2_XM, .base = 0x390000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
};
-static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
+static const struct cs_dsp_region *cs47l24_dsp_regions[] = {
cs47l24_dsp2_regions,
cs47l24_dsp3_regions,
};
+static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to read SYSCLK state: %d\n", ret);
+ return ret;
+ }
+
+ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ wm_adsp2_set_dspclk(w, v);
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
@@ -154,6 +174,9 @@ SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+
ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE),
@@ -172,14 +195,13 @@ SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL,
SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
- ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+ ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
0xbf, 0, digital_tlv),
SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
- ARIZONA_OUT4L_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
+ ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv),
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -212,6 +234,9 @@ ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -316,9 +341,11 @@ static const struct snd_kcontrol_new cs47l24_aec_loopback_mux =
static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
- ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -340,6 +367,11 @@ SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -405,8 +437,8 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0),
-WM_ADSP2("DSP2", 1),
-WM_ADSP2("DSP3", 2),
+WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev),
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@ -463,77 +495,76 @@ SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3,
ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
- ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
- &cs47l24_aec_loopback_mux),
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &cs47l24_aec_loopback_mux),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
@@ -722,6 +753,16 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "IN2L", NULL, "SYSCLK" },
{ "IN2R", NULL, "SYSCLK" },
+ { "ASRC1L", NULL, "SYSCLK" },
+ { "ASRC1R", NULL, "SYSCLK" },
+ { "ASRC2L", NULL, "SYSCLK" },
+ { "ASRC2R", NULL, "SYSCLK" },
+
+ { "ASRC1L", NULL, "ASYNCCLK" },
+ { "ASRC1R", NULL, "ASYNCCLK" },
+ { "ASRC2L", NULL, "ASYNCCLK" },
+ { "ASRC2R", NULL, "ASYNCCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
@@ -779,12 +820,16 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "Voice Control DSP", NULL, "DSP3" },
+
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
{ "IN2L PGA", NULL, "IN2L" },
{ "IN2R PGA", NULL, "IN2R" },
+ { "Audio Trace DSP", NULL, "DSP2" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
@@ -874,16 +919,22 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
{ "DRC2 Signal Activity", NULL, "DRC2L" },
{ "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
};
-static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
+static int cs47l24_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int Fref, unsigned int Fout)
{
- struct cs47l24_priv *cs47l24 = snd_soc_codec_get_drvdata(codec);
+ struct cs47l24_priv *cs47l24 = snd_soc_component_get_drvdata(component);
switch (fll_id) {
case CS47L24_FLL1:
@@ -901,11 +952,15 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000
+#define CS47L24_RATES SNDRV_PCM_RATE_KNOT
#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const struct snd_soc_dai_ops cs47l24_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l24_dai[] = {
{
.name = "cs47l24-aif1",
@@ -926,8 +981,8 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "cs47l24-aif2",
@@ -948,8 +1003,8 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "cs47l24-aif3",
@@ -970,33 +1025,131 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l24-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &cs47l24_dai_ops,
+ },
+ {
+ .name = "cs47l24-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l24-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &cs47l24_dai_ops,
+ },
+ {
+ .name = "cs47l24-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
},
};
-static int cs47l24_codec_probe(struct snd_soc_codec *codec)
+static int cs47l24_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->core.arizona;
+ int n_adsp;
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-trace") == 0) {
+ n_adsp = 1;
+ } else {
+ dev_err(arizona->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct cs47l24_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 1; i <= 2; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cs47l24_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->core.arizona;
int ret;
- priv->core.arizona->dapm = dapm;
+ arizona->dapm = dapm;
+ snd_soc_component_init_regmap(component, arizona->regmap);
- arizona_init_spk(codec);
- arizona_init_gpio(codec);
- arizona_init_mono(codec);
+ ret = arizona_init_spk(component);
+ if (ret < 0)
+ return ret;
- ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
+ arizona_init_gpio(component);
+ arizona_init_mono(component);
+
+ ret = wm_adsp2_component_probe(&priv->core.adsp[1], component);
if (ret)
goto err_adsp2_codec_probe;
- ret = wm_adsp2_codec_probe(&priv->core.adsp[2], codec);
+ ret = wm_adsp2_component_probe(&priv->core.adsp[2], component);
if (ret)
goto err_adsp2_codec_probe;
- ret = snd_soc_add_codec_controls(codec,
- &arizona_adsp2_rate_controls[1], 2);
+ ret = snd_soc_add_component_controls(component,
+ &arizona_adsp2_rate_controls[1],
+ 2);
if (ret)
goto err_adsp2_codec_probe;
@@ -1005,23 +1158,20 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
return 0;
err_adsp2_codec_probe:
- wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
- wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[1], component);
+ wm_adsp2_component_remove(&priv->core.adsp[2], component);
return ret;
}
-static int cs47l24_codec_remove(struct snd_soc_codec *codec)
+static void cs47l24_component_remove(struct snd_soc_component *component)
{
- struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
-
+ struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component);
- wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
- wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[1], component);
+ wm_adsp2_component_remove(&priv->core.adsp[2], component);
priv->core.arizona->dapm = NULL;
-
- return 0;
}
#define CS47L24_DIG_VU 0x0200
@@ -1032,29 +1182,31 @@ static unsigned int cs47l24_digital_vu[] = {
ARIZONA_DAC_DIGITAL_VOLUME_4L,
};
-static struct regmap *cs47l24_get_regmap(struct device *dev)
-{
- struct cs47l24_priv *priv = dev_get_drvdata(dev);
-
- return priv->core.arizona->regmap;
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
- .probe = cs47l24_codec_probe,
- .remove = cs47l24_codec_remove,
- .get_regmap = cs47l24_get_regmap,
-
- .idle_bias_off = true,
-
- .set_sysclk = arizona_set_sysclk,
- .set_pll = cs47l24_set_fll,
+static const struct snd_compress_ops cs47l24_compress_ops = {
+ .open = cs47l24_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
+};
- .controls = cs47l24_snd_controls,
- .num_controls = ARRAY_SIZE(cs47l24_snd_controls),
- .dapm_widgets = cs47l24_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets),
- .dapm_routes = cs47l24_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_cs47l24 = {
+ .probe = cs47l24_component_probe,
+ .remove = cs47l24_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = cs47l24_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l24_compress_ops,
+ .controls = cs47l24_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l24_snd_controls),
+ .dapm_widgets = cs47l24_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets),
+ .dapm_routes = cs47l24_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int cs47l24_probe(struct platform_device *pdev)
@@ -1066,10 +1218,18 @@ static int cs47l24_probe(struct platform_device *pdev)
BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI);
cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv),
- GFP_KERNEL);
+ GFP_KERNEL);
if (!cs47l24)
return -ENOMEM;
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
platform_set_drvdata(pdev, cs47l24);
cs47l24->core.arizona = arizona;
@@ -1077,15 +1237,15 @@ static int cs47l24_probe(struct platform_device *pdev)
for (i = 1; i <= 2; i++) {
cs47l24->core.adsp[i].part = "cs47l24";
- cs47l24->core.adsp[i].num = i + 1;
- cs47l24->core.adsp[i].type = WMFW_ADSP2;
- cs47l24->core.adsp[i].dev = arizona->dev;
- cs47l24->core.adsp[i].regmap = arizona->regmap;
+ cs47l24->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l24->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l24->core.adsp[i].cs_dsp.dev = arizona->dev;
+ cs47l24->core.adsp[i].cs_dsp.regmap = arizona->regmap;
- cs47l24->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 +
+ cs47l24->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 +
(0x100 * i);
- cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1];
- cs47l24->core.adsp[i].num_mems =
+ cs47l24->core.adsp[i].cs_dsp.mem = cs47l24_dsp_regions[i - 1];
+ cs47l24->core.adsp[i].cs_dsp.num_mems =
ARRAY_SIZE(cs47l24_dsp2_regions);
ret = wm_adsp2_init(&cs47l24->core.adsp[i]);
@@ -1120,16 +1280,63 @@ static int cs47l24_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
- cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
+ cs47l24);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to set compressed IRQ as a wake source: %d\n",
+ ret);
+
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l24,
+ cs47l24_dai,
+ ARRAY_SIZE(cs47l24_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+
+ return ret;
}
-static int cs47l24_remove(struct platform_device *pdev)
+static void cs47l24_remove(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
+ struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
+ struct arizona *arizona = cs47l24->core.arizona;
+
pm_runtime_disable(&pdev->dev);
- return 0;
+ wm_adsp2_remove(&cs47l24->core.adsp[1]);
+ wm_adsp2_remove(&cs47l24->core.adsp[2]);
+
+ arizona_free_spk_irqs(arizona);
+
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
}
static struct platform_driver cs47l24_codec_driver = {
diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h
index 77ab2b77b2e6..9fd4b41f1f3a 100644
--- a/sound/soc/codecs/cs47l24.h
+++ b/sound/soc/codecs/cs47l24.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
*
* Copyright 2015 Cirrus Logic Inc.
*
* Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can 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 _CS47L24_H
diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c
new file mode 100644
index 000000000000..a8fe5a99a8bb
--- /dev/null
+++ b/sound/soc/codecs/cs47l35.c
@@ -0,0 +1,1782 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L35 codec
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define CS47L35_NUM_ADSP 3
+#define CS47L35_MONO_OUTPUTS 1
+
+#define DRV_NAME "cs47l35-codec"
+
+struct cs47l35 {
+ struct madera_priv core;
+ struct madera_fll fll;
+};
+
+static const struct cs_dsp_region cs47l35_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const struct cs_dsp_region cs47l35_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x160000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x120000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x140000 },
+};
+
+static const struct cs_dsp_region cs47l35_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
+};
+
+static const struct cs_dsp_region *cs47l35_dsp_regions[] = {
+ cs47l35_dsp1_regions,
+ cs47l35_dsp2_regions,
+ cs47l35_dsp3_regions,
+};
+
+static const int wm_adsp2_control_bases[] = {
+ MADERA_DSP1_CONFIG_1,
+ MADERA_DSP2_CONFIG_1,
+ MADERA_DSP3_CONFIG_1,
+};
+
+static const char * const cs47l35_outdemux_texts[] = {
+ "HPOUT",
+ "EPOUT",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs47l35_outdemux_enum, SND_SOC_NOPM, 0,
+ cs47l35_outdemux_texts);
+
+static const struct snd_kcontrol_new cs47l35_outdemux =
+ SOC_DAPM_ENUM_EXT("HPOUT1 Demux", cs47l35_outdemux_enum,
+ madera_out1_demux_get, madera_out1_demux_put);
+
+static int cs47l35_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l35->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_1, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_1: %d\n", ret);
+ return ret;
+ }
+
+ freq &= MADERA_DSP_CLK_FREQ_LEGACY_MASK;
+ freq >>= MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l35->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L35_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static void cs47l35_hp_post_enable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
+ val &= (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA);
+
+ if (val != (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA))
+ break;
+
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cs47l35_hp_post_disable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1L_CONTROL,
+ 0x2006);
+ break;
+ case MADERA_OUT1R_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1R_CONTROL,
+ 0x2006);
+ break;
+ default:
+ return;
+ }
+
+ /* Only get to here for OUT1L and OUT1R */
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 0);
+}
+
+static int cs47l35_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ return madera_hp_ev(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ ret = madera_hp_ev(w, kcontrol, event);
+ if (ret < 0)
+ return ret;
+
+ cs47l35_hp_post_enable(w);
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = madera_hp_ev(w, kcontrol, event);
+ cs47l35_hp_post_disable(w);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_kcontrol_new cs47l35_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUT", MADERA_OUT4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_OUT4L_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+CS47L35_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L35_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L35_NG_SRC("SPKOUT", MADERA_NOISE_GATE_SELECT_4L),
+CS47L35_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L35_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUT, MADERA_OUT4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l35_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "SPKOUT", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l35_aec_loopback_values[] = {
+ 0, 1, 6, 8, 9,
+};
+
+static const struct soc_enum cs47l35_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l35_aec_loopback_texts),
+ cs47l35_aec_loopback_texts,
+ cs47l35_aec_loopback_values);
+
+static const struct soc_enum cs47l35_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l35_aec_loopback_texts),
+ cs47l35_aec_loopback_texts,
+ cs47l35_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l35_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l35_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l35_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget cs47l35_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BRN"),
+SND_SOC_DAPM_INPUT("IN1BRP"),
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux),
+SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, cs47l35_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, cs47l35_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * Input mux widgets arranged in order of sources in MADERA_MIXER_INPUT_ROUTES
+ * to take advantage of cache lookup in DAPM
+ */
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l35_aec_loopback_mux[0]),
+
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l35_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l35_adsp_power_ev),
+WM_ADSP2("DSP2", 1, cs47l35_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l35_adsp_power_ev),
+
+/* End of ordered input mux widgets */
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+MADERA_DSP_WIDGETS(DSP2, "DSP2"),
+MADERA_DSP_WIDGETS(DSP3, "DSP3"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[1]),
+SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[2]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }
+
+static const struct snd_soc_dapm_route cs47l35_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT4L", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "DSP2", NULL, "DSP2CLK" },
+ { "DSP3", NULL, "DSP3CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD2" },
+ { "AIF3 Playback", NULL, "DBVDD2" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD2" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS2A", NULL, "MICBIAS2" },
+ { "MICBIAS2B", NULL, "MICBIAS2" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP3" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BRN" },
+ { "IN1R Analog Mux", "B", "IN1BRP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1ARN" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1ARN" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Mode", "Analog", "IN2LN" },
+ { "IN2L Mode", "Analog", "IN2LP" },
+ { "IN2R Mode", "Analog", "IN2RN" },
+ { "IN2R Mode", "Analog", "IN2RP" },
+
+ { "IN2L Mode", "Digital", "IN2LN" },
+ { "IN2L Mode", "Digital", "IN2RN" },
+ { "IN2R Mode", "Digital", "IN2LN" },
+ { "IN2R Mode", "Digital", "IN2RN" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+
+ MADERA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+ MADERA_DSP_ROUTES("DSP2"),
+ MADERA_DSP_ROUTES("DSP3"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP2 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP3 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+ { "DSP2 Trigger Output", "Switch", "DSP2" },
+ { "DSP3 Trigger Output", "Switch", "DSP3" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1 Demux", NULL, "OUT1L" },
+ { "HPOUT1 Demux", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "SPKOUT", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "OUT1R", NULL, "HPOUT1 Mono Mux" },
+ { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" },
+
+ { "HPOUTL", "HPOUT", "HPOUT1 Demux" },
+ { "HPOUTR", "HPOUT", "HPOUT1 Demux" },
+ { "EPOUTP", "EPOUT", "HPOUT1 Demux" },
+ { "EPOUTN", "EPOUT", "HPOUT1 Demux" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs47l35_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l35->fll, source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l35->fll, source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops cs47l35_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs47l35_dai[] = {
+ {
+ .name = "cs47l35-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l35-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l35-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l35-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l35-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l35-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l35_dai_ops,
+ },
+ {
+ .name = "cs47l35-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l35-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l35_dai_ops,
+ },
+ {
+ .name = "cs47l35-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l35_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l35->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l35_adsp2_irq(int irq, void *data)
+{
+ struct cs47l35 *cs47l35 = data;
+ struct madera_priv *priv = &cs47l35->core;
+ struct madera *madera = priv->madera;
+ struct madera_voice_trigger_info trig_info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < CS47L35_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ trig_info.core_num = i + 1;
+ blocking_notifier_call_chain(&madera->notifier,
+ MADERA_NOTIFY_VOICE_TRIGGER,
+ &trig_info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_route cs47l35_mono_routes[] = {
+ { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" },
+};
+
+static int cs47l35_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l35->core.madera;
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_to_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, cs47l35_mono_routes,
+ ARRAY_SIZE(cs47l35_mono_routes),
+ CS47L35_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L35_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_component_probe(&cs47l35->core.adsp[i], component);
+
+ return 0;
+}
+
+static void cs47l35_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l35->core.madera;
+ int i;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_component_remove(&cs47l35->core.adsp[i], component);
+}
+
+#define CS47L35_DIG_VU 0x0200
+
+static unsigned int cs47l35_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l35_compress_ops = {
+ .open = &cs47l35_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l35 = {
+ .probe = &cs47l35_component_probe,
+ .remove = &cs47l35_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l35_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l35_compress_ops,
+ .controls = cs47l35_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l35_snd_controls),
+ .dapm_widgets = cs47l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l35_dapm_widgets),
+ .dapm_routes = cs47l35_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l35_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l35_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l35 *cs47l35;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l35_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l35 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l35), GFP_KERNEL);
+ if (!cs47l35)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, cs47l35);
+
+ cs47l35->core.madera = madera;
+ cs47l35->core.dev = &pdev->dev;
+ cs47l35->core.num_inputs = 4;
+
+ ret = madera_core_init(&cs47l35->core);
+ if (ret)
+ return ret;
+
+ ret = madera_init_overheat(&cs47l35->core);
+ if (ret)
+ goto error_core;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l35_adsp2_irq,
+ cs47l35);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_overheat;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++) {
+ cs47l35->core.adsp[i].part = "cs47l35";
+ cs47l35->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l35->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l35->core.adsp[i].cs_dsp.rev = 1;
+ cs47l35->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l35->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l35->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i];
+ cs47l35->core.adsp[i].cs_dsp.mem = cs47l35_dsp_regions[i];
+ cs47l35->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l35_dsp1_regions);
+
+ ret = wm_adsp2_init(&cs47l35->core.adsp[i]);
+ if (ret) {
+ for (--i; i >= 0; --i)
+ wm_adsp2_remove(&cs47l35->core.adsp[i]);
+ goto error_dsp_irq;
+ }
+ }
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, &cs47l35->fll);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l35_dai); i++)
+ madera_init_dai(&cs47l35->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l35_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l35_digital_vu[i],
+ CS47L35_DIG_VU, CS47L35_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l35,
+ cs47l35_dai,
+ ARRAY_SIZE(cs47l35_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l35->core.adsp[i]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l35);
+error_overheat:
+ madera_free_overheat(&cs47l35->core);
+error_core:
+ madera_core_free(&cs47l35->core);
+
+ return ret;
+}
+
+static void cs47l35_remove(struct platform_device *pdev)
+{
+ struct cs47l35 *cs47l35 = platform_get_drvdata(pdev);
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L35_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l35->core.adsp[i]);
+
+ madera_set_irq_wake(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l35);
+ madera_free_overheat(&cs47l35->core);
+ madera_core_free(&cs47l35->core);
+}
+
+static struct platform_driver cs47l35_codec_driver = {
+ .driver = {
+ .name = "cs47l35-codec",
+ },
+ .probe = &cs47l35_probe,
+ .remove = cs47l35_remove,
+};
+
+module_platform_driver(cs47l35_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L35 driver");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l35-codec");
diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c
new file mode 100644
index 000000000000..42fafb0b392c
--- /dev/null
+++ b/sound/soc/codecs/cs47l85.c
@@ -0,0 +1,2733 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L85 codec
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define DRV_NAME "cs47l85-codec"
+
+#define CS47L85_NUM_ADSP 7
+#define CS47L85_MONO_OUTPUTS 4
+
+struct cs47l85 {
+ struct madera_priv core;
+ struct madera_fll fll[3];
+};
+
+static const struct cs_dsp_region cs47l85_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x160000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x120000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x140000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp4_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x260000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x220000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x240000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp5_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2c0000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp6_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x360000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x320000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x340000 },
+};
+
+static const struct cs_dsp_region cs47l85_dsp7_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x3a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3c0000 },
+};
+
+static const struct cs_dsp_region *cs47l85_dsp_regions[] = {
+ cs47l85_dsp1_regions,
+ cs47l85_dsp2_regions,
+ cs47l85_dsp3_regions,
+ cs47l85_dsp4_regions,
+ cs47l85_dsp5_regions,
+ cs47l85_dsp6_regions,
+ cs47l85_dsp7_regions,
+};
+
+static const unsigned int wm_adsp2_control_bases[] = {
+ MADERA_DSP1_CONFIG_1,
+ MADERA_DSP2_CONFIG_1,
+ MADERA_DSP3_CONFIG_1,
+ MADERA_DSP4_CONFIG_1,
+ MADERA_DSP5_CONFIG_1,
+ MADERA_DSP6_CONFIG_1,
+ MADERA_DSP7_CONFIG_1,
+};
+
+static int cs47l85_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l85->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_1, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_1: %d\n", ret);
+ return ret;
+ }
+
+ freq &= MADERA_DSP_CLK_FREQ_LEGACY_MASK;
+ freq >>= MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l85->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L85_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
+
+#define CS47L85_RXANC_INPUT_ROUTES(widget, name) \
+ { widget, NULL, name " NG Mux" }, \
+ { name " NG Internal", NULL, "RXANC NG Clock" }, \
+ { name " NG Internal", NULL, name " Channel" }, \
+ { name " NG External", NULL, "RXANC NG External Clock" }, \
+ { name " NG External", NULL, name " Channel" }, \
+ { name " NG Mux", "None", name " Channel" }, \
+ { name " NG Mux", "Internal", name " NG Internal" }, \
+ { name " NG Mux", "External", name " NG External" }, \
+ { name " Channel", "Left", name " Left Input" }, \
+ { name " Channel", "Combine", name " Left Input" }, \
+ { name " Channel", "Right", name " Right Input" }, \
+ { name " Channel", "Combine", name " Right Input" }, \
+ { name " Left Input", "IN1", "IN1L" }, \
+ { name " Right Input", "IN1", "IN1R" }, \
+ { name " Left Input", "IN2", "IN2L" }, \
+ { name " Right Input", "IN2", "IN2R" }, \
+ { name " Left Input", "IN3", "IN3L" }, \
+ { name " Right Input", "IN3", "IN3R" }, \
+ { name " Left Input", "IN4", "IN4L" }, \
+ { name " Right Input", "IN4", "IN4R" }, \
+ { name " Left Input", "IN5", "IN5L" }, \
+ { name " Right Input", "IN5", "IN5R" }, \
+ { name " Left Input", "IN6", "IN6L" }, \
+ { name " Right Input", "IN6", "IN6R" }
+
+#define CS47L85_RXANC_OUTPUT_ROUTES(widget, name) \
+ { widget, NULL, name " ANC Source" }, \
+ { name " ANC Source", "RXANCL", "RXANCL" }, \
+ { name " ANC Source", "RXANCR", "RXANCR" }
+
+static void cs47l85_hp_post_enable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
+ val &= (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA);
+
+ if (val != (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA))
+ break;
+
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cs47l85_hp_post_disable(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1L_CONTROL,
+ 0x2006);
+ break;
+ case MADERA_OUT1R_ENA_SHIFT:
+ snd_soc_component_write(component, MADERA_DCS_HP1R_CONTROL,
+ 0x2006);
+ break;
+ default:
+ return;
+ }
+
+ /* Only get to here for OUT1L and OUT1R */
+ snd_soc_component_update_bits(component,
+ MADERA_EDRE_HP_STEREO_CONTROL,
+ 0x0001, 0);
+}
+
+static int cs47l85_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ return madera_hp_ev(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ ret = madera_hp_ev(w, kcontrol, event);
+ if (ret < 0)
+ return ret;
+
+ cs47l85_hp_post_enable(w);
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = madera_hp_ev(w, kcontrol, event);
+ cs47l85_hp_post_disable(w);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_kcontrol_new cs47l85_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]),
+SOC_ENUM("IN5 OSR", madera_in_dmic_osr[4]),
+SOC_ENUM("IN6 OSR", madera_in_dmic_osr[5]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL,
+ MADERA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL,
+ MADERA_IN4R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5L HPF Switch", MADERA_IN5L_CONTROL,
+ MADERA_IN5L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5R HPF Switch", MADERA_IN5R_CONTROL,
+ MADERA_IN5R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN6L HPF Switch", MADERA_IN6L_CONTROL,
+ MADERA_IN6L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN6R HPF Switch", MADERA_IN6R_CONTROL,
+ MADERA_IN6R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L,
+ MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R,
+ MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L,
+ MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R,
+ MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5L,
+ MADERA_IN5L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5R,
+ MADERA_IN5R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN6L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_6L,
+ MADERA_IN6L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN6R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_6R,
+ MADERA_IN6R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+SND_SOC_BYTES("RXANC Coefficients", MADERA_ANC_COEFF_START,
+ MADERA_ANC_COEFF_END - MADERA_ANC_COEFF_START + 1),
+SND_SOC_BYTES("RXANCL Config", MADERA_FCL_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCL Coefficients", MADERA_FCL_COEFF_START,
+ MADERA_FCL_COEFF_END - MADERA_FCL_COEFF_START + 1),
+SND_SOC_BYTES("RXANCR Config", MADERA_FCR_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCR Coefficients", MADERA_FCR_COEFF_START,
+ MADERA_FCR_COEFF_END - MADERA_FCR_COEFF_START + 1),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC3 FSL", madera_isrc_fsl[2]),
+MADERA_RATE_ENUM("ISRC4 FSL", madera_isrc_fsl[3]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+MADERA_RATE_ENUM("ISRC3 FSH", madera_isrc_fsh[2]),
+MADERA_RATE_ENUM("ISRC4 FSH", madera_isrc_fsh[3]),
+MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_rate[0]),
+MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_rate[1]),
+MADERA_RATE_ENUM("ASRC2 Rate 1", madera_asrc2_rate[0]),
+MADERA_RATE_ENUM("ASRC2 Rate 2", madera_asrc2_rate[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+WM_ADSP2_PRELOAD_SWITCH("DSP4", 4),
+WM_ADSP2_PRELOAD_SWITCH("DSP5", 5),
+WM_ADSP2_PRELOAD_SWITCH("DSP6", 6),
+WM_ADSP2_PRELOAD_SWITCH("DSP7", 7),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4L", MADERA_DSP4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4R", MADERA_DSP4RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5L", MADERA_DSP5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5R", MADERA_DSP5RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6L", MADERA_DSP6LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6R", MADERA_DSP6RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7L", MADERA_DSP7LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7R", MADERA_DSP7RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUTL", MADERA_OUT4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKOUTR", MADERA_OUT4RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT2L", MADERA_OUT6LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT2R", MADERA_OUT6RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL,
+ MADERA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL,
+ MADERA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT2 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_6L,
+ MADERA_OUT6_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_4R, MADERA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_6L,
+ MADERA_DAC_DIGITAL_VOLUME_6R, MADERA_OUT6L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_4R, MADERA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_6L,
+ MADERA_DAC_DIGITAL_VOLUME_6R, MADERA_OUT6L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE("SPKDAT2 Switch", MADERA_PDM_SPK2_CTRL_1, MADERA_SPK2L_MUTE_SHIFT,
+ MADERA_SPK2R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+CS47L85_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L85_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L85_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L),
+CS47L85_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R),
+CS47L85_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L),
+CS47L85_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R),
+CS47L85_NG_SRC("SPKOUTL", MADERA_NOISE_GATE_SELECT_4L),
+CS47L85_NG_SRC("SPKOUTR", MADERA_NOISE_GATE_SELECT_4R),
+CS47L85_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L85_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+CS47L85_NG_SRC("SPKDAT2L", MADERA_NOISE_GATE_SELECT_6L),
+CS47L85_NG_SRC("SPKDAT2R", MADERA_NOISE_GATE_SELECT_6R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF4TX1", MADERA_AIF4TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF4TX2", MADERA_AIF4TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+WM_ADSP_FW_CONTROL("DSP4", 3),
+WM_ADSP_FW_CONTROL("DSP5", 4),
+WM_ADSP_FW_CONTROL("DSP6", 5),
+WM_ADSP_FW_CONTROL("DSP7", 6),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP4L, MADERA_DSP4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP4R, MADERA_DSP4RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP4, MADERA_DSP4AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP5L, MADERA_DSP5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP5R, MADERA_DSP5RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP5, MADERA_DSP5AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP6L, MADERA_DSP6LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP6R, MADERA_DSP6RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP6, MADERA_DSP6AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP7L, MADERA_DSP7LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP7R, MADERA_DSP7RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP7, MADERA_DSP7AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUTL, MADERA_OUT4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKOUTR, MADERA_OUT4RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT2L, MADERA_OUT6LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT2R, MADERA_OUT6RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF4TX1, MADERA_AIF4TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF4TX2, MADERA_AIF4TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1L, MADERA_ASRC2_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1R, MADERA_ASRC2_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2L, MADERA_ASRC2_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2R, MADERA_ASRC2_2RMIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3INT1, MADERA_ISRC3INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3INT2, MADERA_ISRC3INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3DEC1, MADERA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3DEC2, MADERA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4INT1, MADERA_ISRC4INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4INT2, MADERA_ISRC4INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4DEC1, MADERA_ISRC4DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4DEC2, MADERA_ISRC4DEC2MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l85_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R",
+};
+
+static const unsigned int cs47l85_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+};
+
+static const struct soc_enum cs47l85_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l85_aec_loopback_texts),
+ cs47l85_aec_loopback_texts,
+ cs47l85_aec_loopback_values);
+
+static const struct soc_enum cs47l85_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l85_aec_loopback_texts),
+ cs47l85_aec_loopback_texts,
+ cs47l85_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l85_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l85_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l85_aec2_loopback),
+};
+
+static const struct snd_kcontrol_new cs47l85_anc_input_mux[] = {
+ SOC_DAPM_ENUM("RXANCL Input", madera_anc_input_src[0]),
+ SOC_DAPM_ENUM("RXANCL Channel", madera_anc_input_src[1]),
+ SOC_DAPM_ENUM("RXANCR Input", madera_anc_input_src[2]),
+ SOC_DAPM_ENUM("RXANCR Channel", madera_anc_input_src[3]),
+};
+
+static const struct snd_kcontrol_new cs47l85_anc_ng_mux =
+ SOC_DAPM_ENUM("RXANC NG Source", madera_anc_ng_enum);
+
+static const struct snd_kcontrol_new cs47l85_output_anc_src[] = {
+ SOC_DAPM_ENUM("HPOUT1L ANC Source", madera_output_anc_src[0]),
+ SOC_DAPM_ENUM("HPOUT1R ANC Source", madera_output_anc_src[1]),
+ SOC_DAPM_ENUM("HPOUT2L ANC Source", madera_output_anc_src[2]),
+ SOC_DAPM_ENUM("HPOUT2R ANC Source", madera_output_anc_src[3]),
+ SOC_DAPM_ENUM("HPOUT3L ANC Source", madera_output_anc_src[4]),
+ SOC_DAPM_ENUM("HPOUT3R ANC Source", madera_output_anc_src[5]),
+ SOC_DAPM_ENUM("SPKOUTL ANC Source", madera_output_anc_src[6]),
+ SOC_DAPM_ENUM("SPKOUTR ANC Source", madera_output_anc_src[7]),
+ SOC_DAPM_ENUM("SPKDAT1L ANC Source", madera_output_anc_src[8]),
+ SOC_DAPM_ENUM("SPKDAT1R ANC Source", madera_output_anc_src[9]),
+ SOC_DAPM_ENUM("SPKDAT2L ANC Source", madera_output_anc_src[10]),
+ SOC_DAPM_ENUM("SPKDAT2R ANC Source", madera_output_anc_src[11]),
+};
+
+static const struct snd_soc_dapm_widget cs47l85_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
+ MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
+ MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD4", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", MADERA_MIC_BIAS_CTRL_3,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS4", MADERA_MIC_BIAS_CTRL_4,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP5CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP5, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP6CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP6, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP7CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP7, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF4TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
+ MADERA_EXT_NG_SEL_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
+ MADERA_CLK_NG_ENA_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BN"),
+SND_SOC_DAPM_INPUT("IN1BP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+SND_SOC_DAPM_INPUT("IN2ALN"),
+SND_SOC_DAPM_INPUT("IN2ALP"),
+SND_SOC_DAPM_INPUT("IN2ARN"),
+SND_SOC_DAPM_INPUT("IN2ARP"),
+SND_SOC_DAPM_INPUT("IN2BLN"),
+SND_SOC_DAPM_INPUT("IN2BLP"),
+SND_SOC_DAPM_INPUT("IN2BRN"),
+SND_SOC_DAPM_INPUT("IN2BRP"),
+SND_SOC_DAPM_INPUT("IN3LN"),
+SND_SOC_DAPM_INPUT("IN3LP"),
+SND_SOC_DAPM_INPUT("IN3RN"),
+SND_SOC_DAPM_INPUT("IN3RP"),
+SND_SOC_DAPM_INPUT("DMICCLK4"),
+SND_SOC_DAPM_INPUT("DMICDAT4"),
+SND_SOC_DAPM_INPUT("DMICCLK5"),
+SND_SOC_DAPM_INPUT("DMICDAT5"),
+SND_SOC_DAPM_INPUT("DMICCLK6"),
+SND_SOC_DAPM_INPUT("DMICDAT6"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]),
+SND_SOC_DAPM_MUX("IN2R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[3]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_MUX("IN3L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[2]),
+SND_SOC_DAPM_MUX("IN3R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[2]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[1]),
+SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &cs47l85_anc_ng_mux),
+SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l85_anc_input_mux[3]),
+SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &cs47l85_anc_ng_mux),
+
+SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, MADERA_CLK_L_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, MADERA_CLK_R_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[0]),
+SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[1]),
+SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[2]),
+SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[3]),
+SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[4]),
+SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[5]),
+SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[6]),
+SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[7]),
+SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[8]),
+SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[9]),
+SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[10]),
+SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l85_output_anc_src[11]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF4TX1", NULL, 0,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF4TX2", NULL, 1,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, cs47l85_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, cs47l85_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM,
+ MADERA_OUT4R_ENA_SHIFT, 0, NULL, 0, madera_spk_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT6L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT6R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * Input mux widgets arranged in order of sources in MADERA_MIXER_INPUT_ROUTES
+ * to take advantage of cache lookup in DAPM
+ */
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l85_aec_loopback_mux[0]),
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l85_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5L", MADERA_INPUT_ENABLES, MADERA_IN5L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5R", MADERA_INPUT_ENABLES, MADERA_IN5R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN6L", MADERA_INPUT_ENABLES, MADERA_IN6L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN6R", MADERA_INPUT_ENABLES, MADERA_IN6R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF4RX1", NULL, 0,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF4RX2", NULL, 1,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7,
+ MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN1L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN1R_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN2L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN2R_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC2IN1L", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN1L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN1R", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN1R_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2L", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN2L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2R", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN2R_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4DEC1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4DEC2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4INT1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4INT2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT2_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP2", 1, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP4", 3, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP5", 4, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP6", 5, cs47l85_adsp_power_ev),
+WM_ADSP2("DSP7", 6, cs47l85_adsp_power_ev),
+
+/* End of ordered input mux widgets */
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+MADERA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+MADERA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+MADERA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"),
+MADERA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"),
+MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MIXER_WIDGETS(AIF4TX1, "AIF4TX1"),
+MADERA_MIXER_WIDGETS(AIF4TX2, "AIF4TX2"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"),
+MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"),
+MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"),
+MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"),
+MADERA_MUX_WIDGETS(ASRC2IN1L, "ASRC2IN1L"),
+MADERA_MUX_WIDGETS(ASRC2IN1R, "ASRC2IN1R"),
+MADERA_MUX_WIDGETS(ASRC2IN2L, "ASRC2IN2L"),
+MADERA_MUX_WIDGETS(ASRC2IN2R, "ASRC2IN2R"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+MADERA_DSP_WIDGETS(DSP2, "DSP2"),
+MADERA_DSP_WIDGETS(DSP3, "DSP3"),
+MADERA_DSP_WIDGETS(DSP4, "DSP4"),
+MADERA_DSP_WIDGETS(DSP5, "DSP5"),
+MADERA_DSP_WIDGETS(DSP6, "DSP6"),
+MADERA_DSP_WIDGETS(DSP7, "DSP7"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[1]),
+SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[2]),
+SND_SOC_DAPM_SWITCH("DSP4 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[3]),
+SND_SOC_DAPM_SWITCH("DSP5 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[4]),
+SND_SOC_DAPM_SWITCH("DSP6 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[5]),
+SND_SOC_DAPM_SWITCH("DSP7 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[6]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+MADERA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+MADERA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+MADERA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+MADERA_MUX_WIDGETS(ISRC4DEC1, "ISRC4DEC1"),
+MADERA_MUX_WIDGETS(ISRC4DEC2, "ISRC4DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC4INT1, "ISRC4INT1"),
+MADERA_MUX_WIDGETS(ISRC4INT2, "ISRC4INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "IN3L", "IN3L" }, \
+ { name, "IN3R", "IN3R" }, \
+ { name, "IN4L", "IN4L" }, \
+ { name, "IN4R", "IN4R" }, \
+ { name, "IN5L", "IN5L" }, \
+ { name, "IN5R", "IN5R" }, \
+ { name, "IN6L", "IN6L" }, \
+ { name, "IN6R", "IN6R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF2RX7", "AIF2RX7" }, \
+ { name, "AIF2RX8", "AIF2RX8" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "AIF4RX1", "AIF4RX1" }, \
+ { name, "AIF4RX2", "AIF4RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1IN1L", "ASRC1IN1L" }, \
+ { name, "ASRC1IN1R", "ASRC1IN1R" }, \
+ { name, "ASRC1IN2L", "ASRC1IN2L" }, \
+ { name, "ASRC1IN2R", "ASRC1IN2R" }, \
+ { name, "ASRC2IN1L", "ASRC2IN1L" }, \
+ { name, "ASRC2IN1R", "ASRC2IN1R" }, \
+ { name, "ASRC2IN2L", "ASRC2IN2L" }, \
+ { name, "ASRC2IN2R", "ASRC2IN2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC4DEC1", "ISRC4DEC1" }, \
+ { name, "ISRC4DEC2", "ISRC4DEC2" }, \
+ { name, "ISRC4INT1", "ISRC4INT1" }, \
+ { name, "ISRC4INT2", "ISRC4INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }, \
+ { name, "DSP4.1", "DSP4" }, \
+ { name, "DSP4.2", "DSP4" }, \
+ { name, "DSP4.3", "DSP4" }, \
+ { name, "DSP4.4", "DSP4" }, \
+ { name, "DSP4.5", "DSP4" }, \
+ { name, "DSP4.6", "DSP4" }, \
+ { name, "DSP5.1", "DSP5" }, \
+ { name, "DSP5.2", "DSP5" }, \
+ { name, "DSP5.3", "DSP5" }, \
+ { name, "DSP5.4", "DSP5" }, \
+ { name, "DSP5.5", "DSP5" }, \
+ { name, "DSP5.6", "DSP5" }, \
+ { name, "DSP6.1", "DSP6" }, \
+ { name, "DSP6.2", "DSP6" }, \
+ { name, "DSP6.3", "DSP6" }, \
+ { name, "DSP6.4", "DSP6" }, \
+ { name, "DSP6.5", "DSP6" }, \
+ { name, "DSP6.6", "DSP6" }, \
+ { name, "DSP7.1", "DSP7" }, \
+ { name, "DSP7.2", "DSP7" }, \
+ { name, "DSP7.3", "DSP7" }, \
+ { name, "DSP7.4", "DSP7" }, \
+ { name, "DSP7.5", "DSP7" }, \
+ { name, "DSP7.6", "DSP7" }
+
+static const struct snd_soc_dapm_route cs47l85_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT2L", NULL, "OUTCLK" },
+ { "OUT2R", NULL, "OUTCLK" },
+ { "OUT3L", NULL, "OUTCLK" },
+ { "OUT3R", NULL, "OUTCLK" },
+ { "OUT4L", NULL, "OUTCLK" },
+ { "OUT4R", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "OUT6L", NULL, "OUTCLK" },
+ { "OUT6R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF1TX7", NULL, "AIF1TXCLK" },
+ { "AIF1TX8", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF2TX5", NULL, "AIF2TXCLK" },
+ { "AIF2TX6", NULL, "AIF2TXCLK" },
+ { "AIF2TX7", NULL, "AIF2TXCLK" },
+ { "AIF2TX8", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "AIF4TX1", NULL, "AIF4TXCLK" },
+ { "AIF4TX2", NULL, "AIF4TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SLIMTX7", NULL, "SLIMBUSCLK" },
+ { "SLIMTX8", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "DSP2", NULL, "DSP2CLK" },
+ { "DSP3", NULL, "DSP3CLK" },
+ { "DSP4", NULL, "DSP4CLK" },
+ { "DSP5", NULL, "DSP5CLK" },
+ { "DSP6", NULL, "DSP6CLK" },
+ { "DSP7", NULL, "DSP7CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+ { "ISRC3DEC1", NULL, "ISRC3CLK" },
+ { "ISRC3DEC2", NULL, "ISRC3CLK" },
+ { "ISRC3INT1", NULL, "ISRC3CLK" },
+ { "ISRC3INT2", NULL, "ISRC3CLK" },
+ { "ISRC4DEC1", NULL, "ISRC4CLK" },
+ { "ISRC4DEC2", NULL, "ISRC4CLK" },
+ { "ISRC4INT1", NULL, "ISRC4CLK" },
+ { "ISRC4INT2", NULL, "ISRC4CLK" },
+ { "ASRC1IN1L", NULL, "ASRC1CLK" },
+ { "ASRC1IN1R", NULL, "ASRC1CLK" },
+ { "ASRC1IN2L", NULL, "ASRC1CLK" },
+ { "ASRC1IN2R", NULL, "ASRC1CLK" },
+ { "ASRC2IN1L", NULL, "ASRC2CLK" },
+ { "ASRC2IN1R", NULL, "ASRC2CLK" },
+ { "ASRC2IN2L", NULL, "ASRC2CLK" },
+ { "ASRC2IN2R", NULL, "ASRC2CLK" },
+
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "AIF4 Capture", NULL, "DBVDD3" },
+ { "AIF4 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD2" },
+ { "OUT2L", NULL, "CPVDD1" },
+ { "OUT2L", NULL, "CPVDD2" },
+ { "OUT2R", NULL, "CPVDD1" },
+ { "OUT2R", NULL, "CPVDD2" },
+ { "OUT3L", NULL, "CPVDD1" },
+ { "OUT3L", NULL, "CPVDD2" },
+ { "OUT3R", NULL, "CPVDD1" },
+ { "OUT3R", NULL, "CPVDD2" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+ { "OUT6L", NULL, "SYSCLK" },
+ { "OUT6R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+ { "IN5L", NULL, "SYSCLK" },
+ { "IN5R", NULL, "SYSCLK" },
+ { "IN6L", NULL, "SYSCLK" },
+ { "IN6R", NULL, "SYSCLK" },
+
+ { "IN4L", NULL, "DBVDD4" },
+ { "IN4R", NULL, "DBVDD4" },
+ { "IN5L", NULL, "DBVDD4" },
+ { "IN5R", NULL, "DBVDD4" },
+ { "IN6L", NULL, "DBVDD4" },
+ { "IN6R", NULL, "DBVDD4" },
+
+ { "ASRC1IN1L", NULL, "SYSCLK" },
+ { "ASRC1IN1R", NULL, "SYSCLK" },
+ { "ASRC1IN2L", NULL, "SYSCLK" },
+ { "ASRC1IN2R", NULL, "SYSCLK" },
+ { "ASRC2IN1L", NULL, "SYSCLK" },
+ { "ASRC2IN1R", NULL, "SYSCLK" },
+ { "ASRC2IN2L", NULL, "SYSCLK" },
+ { "ASRC2IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "ASYNCCLK" },
+ { "ASRC1IN1R", NULL, "ASYNCCLK" },
+ { "ASRC1IN2L", NULL, "ASYNCCLK" },
+ { "ASRC1IN2R", NULL, "ASYNCCLK" },
+ { "ASRC2IN1L", NULL, "ASYNCCLK" },
+ { "ASRC2IN1R", NULL, "ASYNCCLK" },
+ { "ASRC2IN2L", NULL, "ASYNCCLK" },
+ { "ASRC2IN2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+ { "MICBIAS4", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+ { "AIF2 Capture", NULL, "AIF2TX7" },
+ { "AIF2 Capture", NULL, "AIF2TX8" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+ { "AIF2RX7", NULL, "AIF2 Playback" },
+ { "AIF2RX8", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF4 Capture", NULL, "AIF4TX1" },
+ { "AIF4 Capture", NULL, "AIF4TX2" },
+
+ { "AIF4RX1", NULL, "AIF4 Playback" },
+ { "AIF4RX2", NULL, "AIF4 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "AIF4 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "AIF4 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP6" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BN" },
+ { "IN1L Analog Mux", "B", "IN1BP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1RN" },
+ { "IN1R Mode", "Analog", "IN1RP" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1RN" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1RN" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Analog Mux", "A", "IN2ALN" },
+ { "IN2L Analog Mux", "A", "IN2ALP" },
+ { "IN2L Analog Mux", "B", "IN2BLN" },
+ { "IN2L Analog Mux", "B", "IN2BLP" },
+ { "IN2R Analog Mux", "A", "IN2ARN" },
+ { "IN2R Analog Mux", "A", "IN2ARP" },
+ { "IN2R Analog Mux", "B", "IN2BRN" },
+ { "IN2R Analog Mux", "B", "IN2BRP" },
+
+ { "IN2L Mode", "Analog", "IN2L Analog Mux" },
+ { "IN2R Mode", "Analog", "IN2R Analog Mux" },
+
+ { "IN2L Mode", "Digital", "IN2ALN" },
+ { "IN2L Mode", "Digital", "IN2ARN" },
+ { "IN2R Mode", "Digital", "IN2ALN" },
+ { "IN2R Mode", "Digital", "IN2ARN" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ { "IN3L Mode", "Analog", "IN3LN" },
+ { "IN3L Mode", "Analog", "IN3LP" },
+ { "IN3R Mode", "Analog", "IN3RN" },
+ { "IN3R Mode", "Analog", "IN3RP" },
+
+ { "IN3L Mode", "Digital", "IN3LN" },
+ { "IN3L Mode", "Digital", "IN3RN" },
+ { "IN3R Mode", "Digital", "IN3LN" },
+ { "IN3R Mode", "Digital", "IN3RN" },
+
+ { "IN3L", NULL, "IN3L Mode" },
+ { "IN3R", NULL, "IN3R Mode" },
+
+ { "IN4L", NULL, "DMICCLK4" },
+ { "IN4L", NULL, "DMICDAT4" },
+ { "IN4R", NULL, "DMICCLK4" },
+ { "IN4R", NULL, "DMICDAT4" },
+
+ { "IN5L", NULL, "DMICCLK5" },
+ { "IN5L", NULL, "DMICDAT5" },
+ { "IN5R", NULL, "DMICCLK5" },
+ { "IN5R", NULL, "DMICDAT5" },
+
+ { "IN6L", NULL, "DMICCLK6" },
+ { "IN6L", NULL, "DMICDAT6" },
+ { "IN6R", NULL, "DMICCLK6" },
+ { "IN6R", NULL, "DMICDAT6" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ MADERA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ MADERA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+ MADERA_MIXER_ROUTES("OUT6L", "SPKDAT2L"),
+ MADERA_MIXER_ROUTES("OUT6R", "SPKDAT2R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+ MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"),
+ MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MIXER_ROUTES("AIF4TX1", "AIF4TX1"),
+ MADERA_MIXER_ROUTES("AIF4TX2", "AIF4TX2"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"),
+ MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"),
+ MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"),
+ MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"),
+ MADERA_MUX_ROUTES("ASRC2IN1L", "ASRC2IN1L"),
+ MADERA_MUX_ROUTES("ASRC2IN1R", "ASRC2IN1R"),
+ MADERA_MUX_ROUTES("ASRC2IN2L", "ASRC2IN2L"),
+ MADERA_MUX_ROUTES("ASRC2IN2R", "ASRC2IN2R"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+ MADERA_DSP_ROUTES("DSP2"),
+ MADERA_DSP_ROUTES("DSP3"),
+ MADERA_DSP_ROUTES("DSP4"),
+ MADERA_DSP_ROUTES("DSP5"),
+ MADERA_DSP_ROUTES("DSP6"),
+ MADERA_DSP_ROUTES("DSP7"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP2 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP3 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP4 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP5 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP6 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP7 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+ { "DSP2 Trigger Output", "Switch", "DSP2" },
+ { "DSP3 Trigger Output", "Switch", "DSP3" },
+ { "DSP4 Trigger Output", "Switch", "DSP4" },
+ { "DSP5 Trigger Output", "Switch", "DSP5" },
+ { "DSP6 Trigger Output", "Switch", "DSP6" },
+ { "DSP7 Trigger Output", "Switch", "DSP7" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ MADERA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ MADERA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ MADERA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ MADERA_MUX_ROUTES("ISRC4INT1", "ISRC4INT1"),
+ MADERA_MUX_ROUTES("ISRC4INT2", "ISRC4INT2"),
+
+ MADERA_MUX_ROUTES("ISRC4DEC1", "ISRC4DEC1"),
+ MADERA_MUX_ROUTES("ISRC4DEC2", "ISRC4DEC2"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC1 Loopback", "HPOUT2R", "OUT2R" },
+ { "AEC2 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC2 Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC1 Loopback", "HPOUT3R", "OUT3R" },
+ { "AEC2 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC2 Loopback", "HPOUT3R", "OUT3R" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKOUTR", "OUT4R" },
+ { "AEC2 Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "AEC1 Loopback", "SPKDAT2L", "OUT6L" },
+ { "AEC1 Loopback", "SPKDAT2R", "OUT6R" },
+ { "AEC2 Loopback", "SPKDAT2L", "OUT6L" },
+ { "AEC2 Loopback", "SPKDAT2R", "OUT6R" },
+ { "SPKDAT2L", NULL, "OUT6L" },
+ { "SPKDAT2R", NULL, "OUT6R" },
+
+ CS47L85_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
+ CS47L85_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
+
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
+ CS47L85_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs47l85_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l85->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_REFCLK:
+ return madera_set_fll_refclk(&cs47l85->fll[1], source, fref,
+ fout);
+ case MADERA_FLL3_REFCLK:
+ return madera_set_fll_refclk(&cs47l85->fll[2], source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l85->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l85->fll[1], source, fref,
+ fout);
+ case MADERA_FLL3_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l85->fll[2], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops cs47l85_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs47l85_dai[] = {
+ {
+ .name = "cs47l85-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-aif4",
+ .id = 4,
+ .base = MADERA_AIF4_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l85-slim1",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l85-slim2",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l85-slim3",
+ .id = 7,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l85-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l85_dai_ops,
+ },
+ {
+ .name = "cs47l85-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l85-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l85_dai_ops,
+ },
+ {
+ .name = "cs47l85-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l85_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l85->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l85_adsp2_irq(int irq, void *data)
+{
+ struct cs47l85 *cs47l85 = data;
+ struct madera_priv *priv = &cs47l85->core;
+ struct madera *madera = priv->madera;
+ struct madera_voice_trigger_info trig_info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < CS47L85_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ trig_info.core_num = i + 1;
+ blocking_notifier_call_chain(&madera->notifier,
+ MADERA_NOTIFY_VOICE_TRIGGER,
+ &trig_info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cs47l85_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l85->core.madera;
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_to_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, NULL, CS47L85_MONO_OUTPUTS,
+ CS47L85_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L85_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_component_probe(&cs47l85->core.adsp[i], component);
+
+ return 0;
+}
+
+static void cs47l85_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l85->core.madera;
+ int i;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_component_remove(&cs47l85->core.adsp[i], component);
+}
+
+#define MADERA_DIG_VU 0x0200
+
+static const unsigned int cs47l85_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R,
+ MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R,
+ MADERA_DAC_DIGITAL_VOLUME_4L,
+ MADERA_DAC_DIGITAL_VOLUME_4R,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+ MADERA_DAC_DIGITAL_VOLUME_6L,
+ MADERA_DAC_DIGITAL_VOLUME_6R,
+};
+
+static const struct snd_compress_ops cs47l85_compress_ops = {
+ .open = &cs47l85_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l85 = {
+ .probe = &cs47l85_component_probe,
+ .remove = &cs47l85_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l85_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l85_compress_ops,
+ .controls = cs47l85_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l85_snd_controls),
+ .dapm_widgets = cs47l85_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l85_dapm_widgets),
+ .dapm_routes = cs47l85_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l85_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l85_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l85 *cs47l85;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l85_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l85 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l85),
+ GFP_KERNEL);
+ if (!cs47l85)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l85);
+
+ cs47l85->core.madera = madera;
+ cs47l85->core.dev = &pdev->dev;
+ cs47l85->core.num_inputs = 12;
+
+ ret = madera_core_init(&cs47l85->core);
+ if (ret)
+ return ret;
+
+ ret = madera_init_overheat(&cs47l85->core);
+ if (ret)
+ goto error_core;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l85_adsp2_irq,
+ cs47l85);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_overheat;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++) {
+ cs47l85->core.adsp[i].part = "cs47l85";
+ cs47l85->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l85->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l85->core.adsp[i].cs_dsp.rev = 1;
+ cs47l85->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l85->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l85->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i];
+ cs47l85->core.adsp[i].cs_dsp.mem = cs47l85_dsp_regions[i];
+ cs47l85->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l85_dsp1_regions);
+
+ ret = wm_adsp2_init(&cs47l85->core.adsp[i]);
+ if (ret) {
+ for (--i; i >= 0; --i)
+ wm_adsp2_remove(&cs47l85->core.adsp[i]);
+ goto error_dsp_irq;
+ }
+ }
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l85->fll[0]);
+ madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1,
+ &cs47l85->fll[1]);
+ madera_init_fll(madera, 3, MADERA_FLL3_CONTROL_1 - 1,
+ &cs47l85->fll[2]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l85_dai); i++)
+ madera_init_dai(&cs47l85->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l85_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l85_digital_vu[i],
+ MADERA_DIG_VU, MADERA_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l85,
+ cs47l85_dai,
+ ARRAY_SIZE(cs47l85_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l85->core.adsp[i]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l85);
+error_overheat:
+ madera_free_overheat(&cs47l85->core);
+error_core:
+ madera_core_free(&cs47l85->core);
+
+ return ret;
+}
+
+static void cs47l85_remove(struct platform_device *pdev)
+{
+ struct cs47l85 *cs47l85 = platform_get_drvdata(pdev);
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L85_NUM_ADSP; i++)
+ wm_adsp2_remove(&cs47l85->core.adsp[i]);
+
+ madera_set_irq_wake(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l85);
+ madera_free_overheat(&cs47l85->core);
+ madera_core_free(&cs47l85->core);
+}
+
+static struct platform_driver cs47l85_codec_driver = {
+ .driver = {
+ .name = "cs47l85-codec",
+ },
+ .probe = &cs47l85_probe,
+ .remove = cs47l85_remove,
+};
+
+module_platform_driver(cs47l85_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L85 driver");
+MODULE_AUTHOR("Nariman Poushin <nariman@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l85-codec");
diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c
new file mode 100644
index 000000000000..77e8aabb241a
--- /dev/null
+++ b/sound/soc/codecs/cs47l90.c
@@ -0,0 +1,2657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L90 codec
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define DRV_NAME "cs47l90-codec"
+
+#define CS47L90_NUM_ADSP 7
+#define CS47L90_MONO_OUTPUTS 3
+
+struct cs47l90 {
+ struct madera_priv core;
+ struct madera_fll fll[3];
+};
+
+static const struct cs_dsp_region cs47l90_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x160000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x120000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x140000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x1a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1c0000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp4_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x260000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x220000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x240000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp5_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2c0000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp6_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x360000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x320000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x340000 },
+};
+
+static const struct cs_dsp_region cs47l90_dsp7_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x3a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3c0000 },
+};
+
+static const struct cs_dsp_region *cs47l90_dsp_regions[] = {
+ cs47l90_dsp1_regions,
+ cs47l90_dsp2_regions,
+ cs47l90_dsp3_regions,
+ cs47l90_dsp4_regions,
+ cs47l90_dsp5_regions,
+ cs47l90_dsp6_regions,
+ cs47l90_dsp7_regions,
+};
+
+static const int cs47l90_dsp_control_bases[] = {
+ MADERA_DSP1_CONFIG_1,
+ MADERA_DSP2_CONFIG_1,
+ MADERA_DSP3_CONFIG_1,
+ MADERA_DSP4_CONFIG_1,
+ MADERA_DSP5_CONFIG_1,
+ MADERA_DSP6_CONFIG_1,
+ MADERA_DSP7_CONFIG_1,
+};
+
+static int cs47l90_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l90->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret);
+ return ret;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l90->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+#define CS47L90_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+#define CS47L90_RXANC_INPUT_ROUTES(widget, name) \
+ { widget, NULL, name " NG Mux" }, \
+ { name " NG Internal", NULL, "RXANC NG Clock" }, \
+ { name " NG Internal", NULL, name " Channel" }, \
+ { name " NG External", NULL, "RXANC NG External Clock" }, \
+ { name " NG External", NULL, name " Channel" }, \
+ { name " NG Mux", "None", name " Channel" }, \
+ { name " NG Mux", "Internal", name " NG Internal" }, \
+ { name " NG Mux", "External", name " NG External" }, \
+ { name " Channel", "Left", name " Left Input" }, \
+ { name " Channel", "Combine", name " Left Input" }, \
+ { name " Channel", "Right", name " Right Input" }, \
+ { name " Channel", "Combine", name " Right Input" }, \
+ { name " Left Input", "IN1", "IN1L" }, \
+ { name " Right Input", "IN1", "IN1R" }, \
+ { name " Left Input", "IN2", "IN2L" }, \
+ { name " Right Input", "IN2", "IN2R" }, \
+ { name " Left Input", "IN3", "IN3L" }, \
+ { name " Right Input", "IN3", "IN3R" }, \
+ { name " Left Input", "IN4", "IN4L" }, \
+ { name " Right Input", "IN4", "IN4R" }, \
+ { name " Left Input", "IN5", "IN5L" }, \
+ { name " Right Input", "IN5", "IN5R" }
+
+#define CS47L90_RXANC_OUTPUT_ROUTES(widget, name) \
+ { widget, NULL, name " ANC Source" }, \
+ { name " ANC Source", "RXANCL", "RXANCL" }, \
+ { name " ANC Source", "RXANCR", "RXANCR" }
+
+static const struct snd_kcontrol_new cs47l90_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]),
+SOC_ENUM("IN5 OSR", madera_in_dmic_osr[4]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2L LP Switch", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2R LP Switch", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL,
+ MADERA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL,
+ MADERA_IN4R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5L HPF Switch", MADERA_IN5L_CONTROL,
+ MADERA_IN5L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN5R HPF Switch", MADERA_IN5R_CONTROL,
+ MADERA_IN5R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L,
+ MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R,
+ MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L,
+ MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R,
+ MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5L,
+ MADERA_IN5L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN5R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5R,
+ MADERA_IN5R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+SND_SOC_BYTES("RXANC Coefficients", MADERA_ANC_COEFF_START,
+ MADERA_ANC_COEFF_END - MADERA_ANC_COEFF_START + 1),
+SND_SOC_BYTES("RXANCL Config", MADERA_FCL_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCL Coefficients", MADERA_FCL_COEFF_START,
+ MADERA_FCL_COEFF_END - MADERA_FCL_COEFF_START + 1),
+SND_SOC_BYTES("RXANCR Config", MADERA_FCR_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCR Coefficients", MADERA_FCR_COEFF_START,
+ MADERA_FCR_COEFF_END - MADERA_FCR_COEFF_START + 1),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC3 FSL", madera_isrc_fsl[2]),
+MADERA_RATE_ENUM("ISRC4 FSL", madera_isrc_fsl[3]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+MADERA_RATE_ENUM("ISRC3 FSH", madera_isrc_fsh[2]),
+MADERA_RATE_ENUM("ISRC4 FSH", madera_isrc_fsh[3]),
+MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_rate[0]),
+MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_rate[1]),
+MADERA_RATE_ENUM("ASRC2 Rate 1", madera_asrc2_rate[0]),
+MADERA_RATE_ENUM("ASRC2 Rate 2", madera_asrc2_rate[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+WM_ADSP2_PRELOAD_SWITCH("DSP4", 4),
+WM_ADSP2_PRELOAD_SWITCH("DSP5", 5),
+WM_ADSP2_PRELOAD_SWITCH("DSP6", 6),
+WM_ADSP2_PRELOAD_SWITCH("DSP7", 7),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4L", MADERA_DSP4LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP4R", MADERA_DSP4RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5L", MADERA_DSP5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP5R", MADERA_DSP5RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6L", MADERA_DSP6LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP6R", MADERA_DSP6RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7L", MADERA_DSP7LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP7R", MADERA_DSP7RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL,
+ MADERA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL,
+ MADERA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+SOC_ENUM_EXT("DFC1RX Width", madera_dfc_width[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1RX Type", madera_dfc_type[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Width", madera_dfc_width[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Type", madera_dfc_type[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Width", madera_dfc_width[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Type", madera_dfc_type[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Width", madera_dfc_width[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Type", madera_dfc_type[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Width", madera_dfc_width[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Type", madera_dfc_type[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Width", madera_dfc_width[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Type", madera_dfc_type[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Width", madera_dfc_width[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Type", madera_dfc_type[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Width", madera_dfc_width[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Type", madera_dfc_type[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Width", madera_dfc_width[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Type", madera_dfc_type[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Width", madera_dfc_width[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Type", madera_dfc_type[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Width", madera_dfc_width[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Type", madera_dfc_type[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Width", madera_dfc_width[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Type", madera_dfc_type[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Width", madera_dfc_width[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Type", madera_dfc_type[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Width", madera_dfc_width[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Type", madera_dfc_type[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Width", madera_dfc_width[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Type", madera_dfc_type[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Width", madera_dfc_width[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Type", madera_dfc_type[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+
+CS47L90_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L90_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L90_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L),
+CS47L90_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R),
+CS47L90_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L),
+CS47L90_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R),
+CS47L90_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L90_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF4TX1", MADERA_AIF4TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF4TX2", MADERA_AIF4TX2MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+WM_ADSP_FW_CONTROL("DSP4", 3),
+WM_ADSP_FW_CONTROL("DSP5", 4),
+WM_ADSP_FW_CONTROL("DSP6", 5),
+WM_ADSP_FW_CONTROL("DSP7", 6),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP4L, MADERA_DSP4LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP4R, MADERA_DSP4RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP4, MADERA_DSP4AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP5L, MADERA_DSP5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP5R, MADERA_DSP5RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP5, MADERA_DSP5AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP6L, MADERA_DSP6LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP6R, MADERA_DSP6RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP6, MADERA_DSP6AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP7L, MADERA_DSP7LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP7R, MADERA_DSP7RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP7, MADERA_DSP7AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF4TX1, MADERA_AIF4TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF4TX2, MADERA_AIF4TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1L, MADERA_ASRC2_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN1R, MADERA_ASRC2_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2L, MADERA_ASRC2_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC2IN2R, MADERA_ASRC2_2RMIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3INT1, MADERA_ISRC3INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3INT2, MADERA_ISRC3INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC3DEC1, MADERA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC3DEC2, MADERA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4INT1, MADERA_ISRC4INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4INT2, MADERA_ISRC4INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC4DEC1, MADERA_ISRC4DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC4DEC2, MADERA_ISRC4DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(DFC1, MADERA_DFC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC2, MADERA_DFC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC3, MADERA_DFC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC4, MADERA_DFC4MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC5, MADERA_DFC5MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC6, MADERA_DFC6MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC7, MADERA_DFC7MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC8, MADERA_DFC8MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l90_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l90_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9,
+};
+
+static const struct soc_enum cs47l90_aec1_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l90_aec_loopback_texts),
+ cs47l90_aec_loopback_texts,
+ cs47l90_aec_loopback_values);
+
+static const struct soc_enum cs47l90_aec2_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l90_aec_loopback_texts),
+ cs47l90_aec_loopback_texts,
+ cs47l90_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l90_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l90_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", cs47l90_aec2_loopback),
+};
+
+static const struct snd_kcontrol_new cs47l90_anc_input_mux[] = {
+ SOC_DAPM_ENUM("RXANCL Input", madera_anc_input_src[0]),
+ SOC_DAPM_ENUM("RXANCL Channel", madera_anc_input_src[1]),
+ SOC_DAPM_ENUM("RXANCR Input", madera_anc_input_src[2]),
+ SOC_DAPM_ENUM("RXANCR Channel", madera_anc_input_src[3]),
+};
+
+static const struct snd_kcontrol_new cs47l90_anc_ng_mux =
+ SOC_DAPM_ENUM("RXANC NG Source", madera_anc_ng_enum);
+
+static const struct snd_kcontrol_new cs47l90_output_anc_src[] = {
+ SOC_DAPM_ENUM("HPOUT1L ANC Source", madera_output_anc_src[0]),
+ SOC_DAPM_ENUM("HPOUT1R ANC Source", madera_output_anc_src[1]),
+ SOC_DAPM_ENUM("HPOUT2L ANC Source", madera_output_anc_src[2]),
+ SOC_DAPM_ENUM("HPOUT2R ANC Source", madera_output_anc_src[3]),
+ SOC_DAPM_ENUM("HPOUT3L ANC Source", madera_output_anc_src[4]),
+ SOC_DAPM_ENUM("HPOUT3R ANC Source", madera_output_anc_src[0]),
+ SOC_DAPM_ENUM("SPKDAT1L ANC Source", madera_output_anc_src[8]),
+ SOC_DAPM_ENUM("SPKDAT1R ANC Source", madera_output_anc_src[9]),
+};
+
+static const struct snd_soc_dapm_widget cs47l90_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
+ MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
+ MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD4", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1D", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1D_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2C", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2C_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2D", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2D_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP4CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP5CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP5, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP6CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP6, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP7CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP7, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF4TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF4, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DFCCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DFC, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BRN"),
+SND_SOC_DAPM_INPUT("IN1BRP"),
+SND_SOC_DAPM_INPUT("IN2ALN"),
+SND_SOC_DAPM_INPUT("IN2ALP"),
+SND_SOC_DAPM_INPUT("IN2BLN"),
+SND_SOC_DAPM_INPUT("IN2BLP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+SND_SOC_DAPM_INPUT("DMICCLK3"),
+SND_SOC_DAPM_INPUT("DMICDAT3"),
+SND_SOC_DAPM_INPUT("DMICCLK4"),
+SND_SOC_DAPM_INPUT("DMICDAT4"),
+SND_SOC_DAPM_INPUT("DMICCLK5"),
+SND_SOC_DAPM_INPUT("DMICDAT5"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
+ MADERA_EXT_NG_SEL_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
+ MADERA_CLK_NG_ENA_SET_SHIFT, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[1]),
+SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &cs47l90_anc_ng_mux),
+SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
+ &cs47l90_anc_input_mux[3]),
+SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &cs47l90_anc_ng_mux),
+
+SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, MADERA_CLK_L_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, MADERA_CLK_R_ENA_SET_SHIFT,
+ 0, NULL, 0, madera_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[0]),
+SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[1]),
+SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[2]),
+SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[3]),
+SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[4]),
+SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[5]),
+SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[6]),
+SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &cs47l90_output_anc_src[7]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF4TX1", NULL, 0,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF4TX2", NULL, 1,
+ MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", SND_SOC_NOPM,
+ MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", SND_SOC_NOPM,
+ MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", SND_SOC_NOPM,
+ MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", SND_SOC_NOPM,
+ MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in MADERA_MIXER_INPUT_ROUTES
+ */
+
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l90_aec_loopback_mux[0]),
+SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2,
+ MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l90_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5L", MADERA_INPUT_ENABLES, MADERA_IN5L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN5R", MADERA_INPUT_ENABLES, MADERA_IN5R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF4RX1", NULL, 0,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF4RX2", NULL, 1,
+ MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1R_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC2IN1L", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN1R", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN1R_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2L", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN2L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2IN2R", MADERA_ASRC2_ENABLE,
+ MADERA_ASRC2_IN2R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", MADERA_ISRC_3_CTRL_3,
+ MADERA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4DEC1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4DEC2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC4INT1", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC4INT2", MADERA_ISRC_4_CTRL_3,
+ MADERA_ISRC4_INT2_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP2", 1, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP4", 3, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP5", 4, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP6", 5, cs47l90_adsp_power_ev),
+WM_ADSP2("DSP7", 6, cs47l90_adsp_power_ev),
+
+/* end of ordered widget list */
+
+SND_SOC_DAPM_PGA("DFC1", MADERA_DFC1_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC2", MADERA_DFC2_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC3", MADERA_DFC3_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC4", MADERA_DFC4_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC5", MADERA_DFC5_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC6", MADERA_DFC6_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC7", MADERA_DFC7_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC8", MADERA_DFC8_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"),
+MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+MADERA_MIXER_WIDGETS(AIF4TX1, "AIF4TX1"),
+MADERA_MIXER_WIDGETS(AIF4TX2, "AIF4TX2"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"),
+
+MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"),
+MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"),
+MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"),
+MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"),
+MADERA_MUX_WIDGETS(ASRC2IN1L, "ASRC2IN1L"),
+MADERA_MUX_WIDGETS(ASRC2IN1R, "ASRC2IN1R"),
+MADERA_MUX_WIDGETS(ASRC2IN2L, "ASRC2IN2L"),
+MADERA_MUX_WIDGETS(ASRC2IN2R, "ASRC2IN2R"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+MADERA_DSP_WIDGETS(DSP2, "DSP2"),
+MADERA_DSP_WIDGETS(DSP3, "DSP3"),
+MADERA_DSP_WIDGETS(DSP4, "DSP4"),
+MADERA_DSP_WIDGETS(DSP5, "DSP5"),
+MADERA_DSP_WIDGETS(DSP6, "DSP6"),
+MADERA_DSP_WIDGETS(DSP7, "DSP7"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[1]),
+SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[2]),
+SND_SOC_DAPM_SWITCH("DSP4 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[3]),
+SND_SOC_DAPM_SWITCH("DSP5 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[4]),
+SND_SOC_DAPM_SWITCH("DSP6 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[5]),
+SND_SOC_DAPM_SWITCH("DSP7 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &madera_dsp_trigger_output_mux[6]),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+MADERA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+MADERA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+MADERA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+MADERA_MUX_WIDGETS(ISRC4DEC1, "ISRC4DEC1"),
+MADERA_MUX_WIDGETS(ISRC4DEC2, "ISRC4DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC4INT1, "ISRC4INT1"),
+MADERA_MUX_WIDGETS(ISRC4INT2, "ISRC4INT2"),
+
+MADERA_MUX_WIDGETS(DFC1, "DFC1"),
+MADERA_MUX_WIDGETS(DFC2, "DFC2"),
+MADERA_MUX_WIDGETS(DFC3, "DFC3"),
+MADERA_MUX_WIDGETS(DFC4, "DFC4"),
+MADERA_MUX_WIDGETS(DFC5, "DFC5"),
+MADERA_MUX_WIDGETS(DFC6, "DFC6"),
+MADERA_MUX_WIDGETS(DFC7, "DFC7"),
+MADERA_MUX_WIDGETS(DFC8, "DFC8"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "IN3L", "IN3L" }, \
+ { name, "IN3R", "IN3R" }, \
+ { name, "IN4L", "IN4L" }, \
+ { name, "IN4R", "IN4R" }, \
+ { name, "IN5L", "IN5L" }, \
+ { name, "IN5R", "IN5R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF2RX7", "AIF2RX7" }, \
+ { name, "AIF2RX8", "AIF2RX8" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "AIF4RX1", "AIF4RX1" }, \
+ { name, "AIF4RX2", "AIF4RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1IN1L", "ASRC1IN1L" }, \
+ { name, "ASRC1IN1R", "ASRC1IN1R" }, \
+ { name, "ASRC1IN2L", "ASRC1IN2L" }, \
+ { name, "ASRC1IN2R", "ASRC1IN2R" }, \
+ { name, "ASRC2IN1L", "ASRC2IN1L" }, \
+ { name, "ASRC2IN1R", "ASRC2IN1R" }, \
+ { name, "ASRC2IN2L", "ASRC2IN2L" }, \
+ { name, "ASRC2IN2R", "ASRC2IN2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC4DEC1", "ISRC4DEC1" }, \
+ { name, "ISRC4DEC2", "ISRC4DEC2" }, \
+ { name, "ISRC4INT1", "ISRC4INT1" }, \
+ { name, "ISRC4INT2", "ISRC4INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }, \
+ { name, "DSP4.1", "DSP4" }, \
+ { name, "DSP4.2", "DSP4" }, \
+ { name, "DSP4.3", "DSP4" }, \
+ { name, "DSP4.4", "DSP4" }, \
+ { name, "DSP4.5", "DSP4" }, \
+ { name, "DSP4.6", "DSP4" }, \
+ { name, "DSP5.1", "DSP5" }, \
+ { name, "DSP5.2", "DSP5" }, \
+ { name, "DSP5.3", "DSP5" }, \
+ { name, "DSP5.4", "DSP5" }, \
+ { name, "DSP5.5", "DSP5" }, \
+ { name, "DSP5.6", "DSP5" }, \
+ { name, "DSP6.1", "DSP6" }, \
+ { name, "DSP6.2", "DSP6" }, \
+ { name, "DSP6.3", "DSP6" }, \
+ { name, "DSP6.4", "DSP6" }, \
+ { name, "DSP6.5", "DSP6" }, \
+ { name, "DSP6.6", "DSP6" }, \
+ { name, "DSP7.1", "DSP7" }, \
+ { name, "DSP7.2", "DSP7" }, \
+ { name, "DSP7.3", "DSP7" }, \
+ { name, "DSP7.4", "DSP7" }, \
+ { name, "DSP7.5", "DSP7" }, \
+ { name, "DSP7.6", "DSP7" }, \
+ { name, "DFC1", "DFC1" }, \
+ { name, "DFC2", "DFC2" }, \
+ { name, "DFC3", "DFC3" }, \
+ { name, "DFC4", "DFC4" }, \
+ { name, "DFC5", "DFC5" }, \
+ { name, "DFC6", "DFC6" }, \
+ { name, "DFC7", "DFC7" }, \
+ { name, "DFC8", "DFC8" }
+
+static const struct snd_soc_dapm_route cs47l90_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT2L", NULL, "OUTCLK" },
+ { "OUT2R", NULL, "OUTCLK" },
+ { "OUT3L", NULL, "OUTCLK" },
+ { "OUT3R", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF1TX7", NULL, "AIF1TXCLK" },
+ { "AIF1TX8", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF2TX5", NULL, "AIF2TXCLK" },
+ { "AIF2TX6", NULL, "AIF2TXCLK" },
+ { "AIF2TX7", NULL, "AIF2TXCLK" },
+ { "AIF2TX8", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "AIF4TX1", NULL, "AIF4TXCLK" },
+ { "AIF4TX2", NULL, "AIF4TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SLIMTX7", NULL, "SLIMBUSCLK" },
+ { "SLIMTX8", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "DSP2", NULL, "DSP2CLK" },
+ { "DSP3", NULL, "DSP3CLK" },
+ { "DSP4", NULL, "DSP4CLK" },
+ { "DSP5", NULL, "DSP5CLK" },
+ { "DSP6", NULL, "DSP6CLK" },
+ { "DSP7", NULL, "DSP7CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1DEC3", NULL, "ISRC1CLK" },
+ { "ISRC1DEC4", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC1INT3", NULL, "ISRC1CLK" },
+ { "ISRC1INT4", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2DEC3", NULL, "ISRC2CLK" },
+ { "ISRC2DEC4", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ISRC2INT3", NULL, "ISRC2CLK" },
+ { "ISRC2INT4", NULL, "ISRC2CLK" },
+ { "ISRC3DEC1", NULL, "ISRC3CLK" },
+ { "ISRC3DEC2", NULL, "ISRC3CLK" },
+ { "ISRC3INT1", NULL, "ISRC3CLK" },
+ { "ISRC3INT2", NULL, "ISRC3CLK" },
+ { "ISRC4DEC1", NULL, "ISRC4CLK" },
+ { "ISRC4DEC2", NULL, "ISRC4CLK" },
+ { "ISRC4INT1", NULL, "ISRC4CLK" },
+ { "ISRC4INT2", NULL, "ISRC4CLK" },
+ { "ASRC1IN1L", NULL, "ASRC1CLK" },
+ { "ASRC1IN1R", NULL, "ASRC1CLK" },
+ { "ASRC1IN2L", NULL, "ASRC1CLK" },
+ { "ASRC1IN2R", NULL, "ASRC1CLK" },
+ { "ASRC2IN1L", NULL, "ASRC2CLK" },
+ { "ASRC2IN1R", NULL, "ASRC2CLK" },
+ { "ASRC2IN2L", NULL, "ASRC2CLK" },
+ { "ASRC2IN2R", NULL, "ASRC2CLK" },
+ { "DFC1", NULL, "DFCCLK" },
+ { "DFC2", NULL, "DFCCLK" },
+ { "DFC3", NULL, "DFCCLK" },
+ { "DFC4", NULL, "DFCCLK" },
+ { "DFC5", NULL, "DFCCLK" },
+ { "DFC6", NULL, "DFCCLK" },
+ { "DFC7", NULL, "DFCCLK" },
+ { "DFC8", NULL, "DFCCLK" },
+
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "AIF4 Capture", NULL, "DBVDD3" },
+ { "AIF4 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD2" },
+ { "OUT2L", NULL, "CPVDD1" },
+ { "OUT2L", NULL, "CPVDD2" },
+ { "OUT2R", NULL, "CPVDD1" },
+ { "OUT2R", NULL, "CPVDD2" },
+ { "OUT3L", NULL, "CPVDD1" },
+ { "OUT3L", NULL, "CPVDD2" },
+ { "OUT3R", NULL, "CPVDD1" },
+ { "OUT3R", NULL, "CPVDD2" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+ { "IN5L", NULL, "SYSCLK" },
+ { "IN5R", NULL, "SYSCLK" },
+
+ { "IN3L", NULL, "DBVDD4" },
+ { "IN3R", NULL, "DBVDD4" },
+ { "IN4L", NULL, "DBVDD4" },
+ { "IN4R", NULL, "DBVDD4" },
+ { "IN5L", NULL, "DBVDD4" },
+ { "IN5R", NULL, "DBVDD4" },
+
+ { "ASRC1IN1L", NULL, "SYSCLK" },
+ { "ASRC1IN1R", NULL, "SYSCLK" },
+ { "ASRC1IN2L", NULL, "SYSCLK" },
+ { "ASRC1IN2R", NULL, "SYSCLK" },
+ { "ASRC2IN1L", NULL, "SYSCLK" },
+ { "ASRC2IN1R", NULL, "SYSCLK" },
+ { "ASRC2IN2L", NULL, "SYSCLK" },
+ { "ASRC2IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "ASYNCCLK" },
+ { "ASRC1IN1R", NULL, "ASYNCCLK" },
+ { "ASRC1IN2L", NULL, "ASYNCCLK" },
+ { "ASRC1IN2R", NULL, "ASYNCCLK" },
+ { "ASRC2IN1L", NULL, "ASYNCCLK" },
+ { "ASRC2IN1R", NULL, "ASYNCCLK" },
+ { "ASRC2IN2L", NULL, "ASYNCCLK" },
+ { "ASRC2IN2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+ { "MICBIAS1D", NULL, "MICBIAS1" },
+
+ { "MICBIAS2A", NULL, "MICBIAS2" },
+ { "MICBIAS2B", NULL, "MICBIAS2" },
+ { "MICBIAS2C", NULL, "MICBIAS2" },
+ { "MICBIAS2D", NULL, "MICBIAS2" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+ { "AIF2 Capture", NULL, "AIF2TX7" },
+ { "AIF2 Capture", NULL, "AIF2TX8" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+ { "AIF2RX7", NULL, "AIF2 Playback" },
+ { "AIF2RX8", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "AIF4 Capture", NULL, "AIF4TX1" },
+ { "AIF4 Capture", NULL, "AIF4TX2" },
+
+ { "AIF4RX1", NULL, "AIF4 Playback" },
+ { "AIF4RX2", NULL, "AIF4 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "AIF4 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "AIF4 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "Voice Control DSP", NULL, "DSP6" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BRN" },
+ { "IN1R Analog Mux", "B", "IN1BRP" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ARN" },
+ { "IN1L Mode", "Digital", "IN1ARP" },
+ { "IN1R Mode", "Digital", "IN1ARN" },
+ { "IN1R Mode", "Digital", "IN1ARP" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Analog Mux", "A", "IN2ALN" },
+ { "IN2L Analog Mux", "A", "IN2ALP" },
+ { "IN2L Analog Mux", "B", "IN2BLN" },
+ { "IN2L Analog Mux", "B", "IN2BLP" },
+
+ { "IN2L Mode", "Analog", "IN2L Analog Mux" },
+ { "IN2R Mode", "Analog", "IN2RN" },
+ { "IN2R Mode", "Analog", "IN2RP" },
+
+ { "IN2L Mode", "Digital", "IN2ALN" },
+ { "IN2L Mode", "Digital", "IN2ALP" },
+ { "IN2R Mode", "Digital", "IN2ALN" },
+ { "IN2R Mode", "Digital", "IN2ALP" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ { "IN3L", NULL, "DMICCLK3" },
+ { "IN3L", NULL, "DMICDAT3" },
+ { "IN3R", NULL, "DMICCLK3" },
+ { "IN3R", NULL, "DMICDAT3" },
+
+ { "IN4L", NULL, "DMICCLK4" },
+ { "IN4L", NULL, "DMICDAT4" },
+ { "IN4R", NULL, "DMICCLK4" },
+ { "IN4R", NULL, "DMICDAT4" },
+
+ { "IN5L", NULL, "DMICCLK5" },
+ { "IN5L", NULL, "DMICDAT5" },
+ { "IN5R", NULL, "DMICCLK5" },
+ { "IN5R", NULL, "DMICDAT5" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+ MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"),
+ MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ MADERA_MIXER_ROUTES("AIF4TX1", "AIF4TX1"),
+ MADERA_MIXER_ROUTES("AIF4TX2", "AIF4TX2"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"),
+ MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"),
+ MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"),
+ MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"),
+ MADERA_MUX_ROUTES("ASRC2IN1L", "ASRC2IN1L"),
+ MADERA_MUX_ROUTES("ASRC2IN1R", "ASRC2IN1R"),
+ MADERA_MUX_ROUTES("ASRC2IN2L", "ASRC2IN2L"),
+ MADERA_MUX_ROUTES("ASRC2IN2R", "ASRC2IN2R"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+ MADERA_DSP_ROUTES("DSP2"),
+ MADERA_DSP_ROUTES("DSP3"),
+ MADERA_DSP_ROUTES("DSP4"),
+ MADERA_DSP_ROUTES("DSP5"),
+ MADERA_DSP_ROUTES("DSP6"),
+ MADERA_DSP_ROUTES("DSP7"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP2 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP3 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP4 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP5 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP6 Trigger Output" },
+ { "DSP Trigger Out", NULL, "DSP7 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+ { "DSP2 Trigger Output", "Switch", "DSP2" },
+ { "DSP3 Trigger Output", "Switch", "DSP3" },
+ { "DSP4 Trigger Output", "Switch", "DSP4" },
+ { "DSP5 Trigger Output", "Switch", "DSP5" },
+ { "DSP6 Trigger Output", "Switch", "DSP6" },
+ { "DSP7 Trigger Output", "Switch", "DSP7" },
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ MADERA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ MADERA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ MADERA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ MADERA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ MADERA_MUX_ROUTES("ISRC4INT1", "ISRC4INT1"),
+ MADERA_MUX_ROUTES("ISRC4INT2", "ISRC4INT2"),
+
+ MADERA_MUX_ROUTES("ISRC4DEC1", "ISRC4DEC1"),
+ MADERA_MUX_ROUTES("ISRC4DEC2", "ISRC4DEC2"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "AEC2 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC2 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC1 Loopback", "HPOUT2R", "OUT2R" },
+ { "AEC2 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC2 Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC1 Loopback", "HPOUT3R", "OUT3R" },
+ { "AEC2 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC2 Loopback", "HPOUT3R", "OUT3R" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "AEC2 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC2 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ CS47L90_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
+ CS47L90_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
+
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
+ CS47L90_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+
+ MADERA_MUX_ROUTES("DFC1", "DFC1"),
+ MADERA_MUX_ROUTES("DFC2", "DFC2"),
+ MADERA_MUX_ROUTES("DFC3", "DFC3"),
+ MADERA_MUX_ROUTES("DFC4", "DFC4"),
+ MADERA_MUX_ROUTES("DFC5", "DFC5"),
+ MADERA_MUX_ROUTES("DFC6", "DFC6"),
+ MADERA_MUX_ROUTES("DFC7", "DFC7"),
+ MADERA_MUX_ROUTES("DFC8", "DFC8"),
+};
+
+static int cs47l90_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_set_fll_refclk(&cs47l90->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_REFCLK:
+ return madera_set_fll_refclk(&cs47l90->fll[1], source, fref,
+ fout);
+ case MADERA_FLLAO_REFCLK:
+ return madera_set_fll_ao_refclk(&cs47l90->fll[2], source, fref,
+ fout);
+ case MADERA_FLL1_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l90->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_SYNCCLK:
+ return madera_set_fll_syncclk(&cs47l90->fll[1], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops cs47l90_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs47l90_dai[] = {
+ {
+ .name = "cs47l90-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-aif4",
+ .id = 4,
+ .base = MADERA_AIF4_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l90-slim1",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l90-slim2",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l90-slim3",
+ .id = 7,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l90-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l90_dai_ops,
+ },
+ {
+ .name = "cs47l90-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+ {
+ .name = "cs47l90-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l90_dai_ops,
+ },
+ {
+ .name = "cs47l90-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l90_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l90->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l90_adsp2_irq(int irq, void *data)
+{
+ struct cs47l90 *cs47l90 = data;
+ struct madera_priv *priv = &cs47l90->core;
+ struct madera *madera = priv->madera;
+ struct madera_voice_trigger_info trig_info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < CS47L90_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ trig_info.core_num = i + 1;
+ blocking_notifier_call_chain(&madera->notifier,
+ MADERA_NOTIFY_VOICE_TRIGGER,
+ &trig_info);
+ }
+ }
+
+ if (!serviced) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cs47l90_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l90->core.madera;
+ int ret, i;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_to_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, NULL, CS47L90_MONO_OUTPUTS,
+ CS47L90_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L90_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++)
+ wm_adsp2_component_probe(&cs47l90->core.adsp[i], component);
+
+ return 0;
+}
+
+static void cs47l90_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l90->core.madera;
+ int i;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++)
+ wm_adsp2_component_remove(&cs47l90->core.adsp[i], component);
+}
+
+#define CS47L90_DIG_VU 0x0200
+
+static unsigned int cs47l90_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R,
+ MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l90_compress_ops = {
+ .open = &cs47l90_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l90 = {
+ .probe = &cs47l90_component_probe,
+ .remove = &cs47l90_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l90_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l90_compress_ops,
+ .controls = cs47l90_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l90_snd_controls),
+ .dapm_widgets = cs47l90_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l90_dapm_widgets),
+ .dapm_routes = cs47l90_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l90_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l90_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l90 *cs47l90;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l90_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l90 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l90),
+ GFP_KERNEL);
+ if (!cs47l90)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l90);
+
+ cs47l90->core.madera = madera;
+ cs47l90->core.dev = &pdev->dev;
+ cs47l90->core.num_inputs = 10;
+
+ ret = madera_core_init(&cs47l90->core);
+ if (ret)
+ return ret;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l90_adsp2_irq,
+ cs47l90);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_core;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++) {
+ cs47l90->core.adsp[i].part = "cs47l90";
+ cs47l90->core.adsp[i].cs_dsp.num = i + 1;
+ cs47l90->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ cs47l90->core.adsp[i].cs_dsp.rev = 2;
+ cs47l90->core.adsp[i].cs_dsp.dev = madera->dev;
+ cs47l90->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l90->core.adsp[i].cs_dsp.base = cs47l90_dsp_control_bases[i];
+ cs47l90->core.adsp[i].cs_dsp.mem = cs47l90_dsp_regions[i];
+ cs47l90->core.adsp[i].cs_dsp.num_mems =
+ ARRAY_SIZE(cs47l90_dsp1_regions);
+
+ cs47l90->core.adsp[i].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9;
+
+ ret = wm_adsp2_init(&cs47l90->core.adsp[i]);
+
+ if (ret == 0) {
+ ret = madera_init_bus_error_irq(&cs47l90->core, i,
+ wm_adsp2_bus_error);
+ if (ret != 0)
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+
+ if (ret) {
+ for (--i; i >= 0; --i) {
+ madera_free_bus_error_irq(&cs47l90->core, i);
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+ goto error_dsp_irq;
+ }
+ }
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l90->fll[0]);
+ madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1,
+ &cs47l90->fll[1]);
+ madera_init_fll(madera, 4, MADERA_FLLAO_CONTROL_1 - 1,
+ &cs47l90->fll[2]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l90_dai); i++)
+ madera_init_dai(&cs47l90->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l90_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l90_digital_vu[i],
+ CS47L90_DIG_VU, CS47L90_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l90,
+ cs47l90_dai,
+ ARRAY_SIZE(cs47l90_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++) {
+ madera_free_bus_error_irq(&cs47l90->core, i);
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l90);
+error_core:
+ madera_core_free(&cs47l90->core);
+
+ return ret;
+}
+
+static void cs47l90_remove(struct platform_device *pdev)
+{
+ struct cs47l90 *cs47l90 = platform_get_drvdata(pdev);
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for (i = 0; i < CS47L90_NUM_ADSP; i++) {
+ madera_free_bus_error_irq(&cs47l90->core, i);
+ wm_adsp2_remove(&cs47l90->core.adsp[i]);
+ }
+
+ madera_set_irq_wake(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l90);
+ madera_core_free(&cs47l90->core);
+}
+
+static struct platform_driver cs47l90_codec_driver = {
+ .driver = {
+ .name = "cs47l90-codec",
+ },
+ .probe = &cs47l90_probe,
+ .remove = cs47l90_remove,
+};
+
+module_platform_driver(cs47l90_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L90 driver");
+MODULE_AUTHOR("Nikesh Oswal <nikesh@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l90-codec");
diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c
new file mode 100644
index 000000000000..868237bd6d91
--- /dev/null
+++ b/sound/soc/codecs/cs47l92.c
@@ -0,0 +1,2103 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC Audio driver for CS47L92 codec
+//
+// Copyright (C) 2016-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#include "madera.h"
+#include "wm_adsp.h"
+
+#define CS47L92_NUM_ADSP 1
+#define CS47L92_MONO_OUTPUTS 3
+
+#define DRV_NAME "cs47l92-codec"
+
+struct cs47l92 {
+ struct madera_priv core;
+ struct madera_fll fll[2];
+};
+
+static const struct cs_dsp_region cs47l92_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x080000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
+};
+
+static const char * const cs47l92_outdemux_texts[] = {
+ "HPOUT3",
+ "HPOUT4",
+};
+
+static int cs47l92_put_demux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int ep_sel, mux, change, cur;
+ bool out_mono;
+ int ret;
+
+ if (ucontrol->value.enumerated.item[0] > e->items - 1)
+ return -EINVAL;
+
+ mux = ucontrol->value.enumerated.item[0];
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ep_sel = mux << e->shift_l;
+
+ change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_MASK,
+ ep_sel);
+ if (!change)
+ goto end;
+
+ ret = regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &cur);
+ if (ret != 0)
+ dev_warn(madera->dev, "Failed to read outputs: %d\n", ret);
+
+ /* EP_SEL should not be modified while HPOUT3 or 4 is enabled */
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA | MADERA_OUT3R_ENA, 0);
+ if (ret)
+ dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret);
+
+ usleep_range(2000, 3000); /* wait for wseq to complete */
+
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL, ep_sel);
+ if (ret) {
+ dev_err(madera->dev, "Failed to set OUT3 demux: %d\n", ret);
+ } else {
+ out_mono = madera->pdata.codec.out_mono[2 + mux];
+
+ ret = madera_set_output_mode(component, 3, out_mono);
+ if (ret < 0)
+ dev_warn(madera->dev,
+ "Failed to set output mode: %d\n", ret);
+ }
+
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA | MADERA_OUT3R_ENA, cur);
+ if (ret) {
+ dev_warn(madera->dev, "Failed to restore outputs: %d\n", ret);
+ } else {
+ /* wait for wseq */
+ if (cur & (MADERA_OUT3L_ENA | MADERA_OUT3R_ENA))
+ msleep(34); /* enable delay */
+ else
+ usleep_range(2000, 3000); /* disable delay */
+ }
+
+end:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+ if (ret < 0) {
+ dev_err(madera->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
+
+ return change;
+}
+
+static SOC_ENUM_SINGLE_DECL(cs47l92_outdemux_enum,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_SHIFT,
+ cs47l92_outdemux_texts);
+
+static const struct snd_kcontrol_new cs47l92_outdemux =
+ SOC_DAPM_ENUM_EXT("OUT3 Demux", cs47l92_outdemux_enum,
+ snd_soc_dapm_get_enum_double, cs47l92_put_demux);
+
+static int cs47l92_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ unsigned int freq;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq);
+ if (ret != 0) {
+ dev_err(madera->dev,
+ "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret);
+ return ret;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = madera_set_adsp_clk(&cs47l92->core, w->shift, freq);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
+static int cs47l92_outclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_OUTPUT_RATE_1, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to read OUTCLK source: %d\n", ret);
+ return ret;
+ }
+
+ val &= MADERA_OUT_CLK_SRC_MASK;
+
+ switch (val) {
+ case MADERA_OUTCLK_MCLK1:
+ case MADERA_OUTCLK_MCLK2:
+ case MADERA_OUTCLK_MCLK3:
+ val -= (MADERA_OUTCLK_MCLK1 - MADERA_MCLK1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = clk_prepare_enable(madera->mclk[val].clk);
+ if (ret)
+ return ret;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(madera->mclk[val].clk);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return madera_domain_clk_ev(w, kcontrol, event);
+}
+
+#define CS47L92_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new cs47l92_snd_controls[] = {
+SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2L LP Switch", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+SOC_SINGLE_EXT("IN2R LP Switch", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN1L_LP_MODE_SHIFT, 1, 0,
+ snd_soc_get_volsw, madera_lp_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL,
+ MADERA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL,
+ MADERA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL,
+ MADERA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL,
+ MADERA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL,
+ MADERA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL,
+ MADERA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL,
+ MADERA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL,
+ MADERA_IN4R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L,
+ MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R,
+ MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L,
+ MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R,
+ MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", madera_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", madera_in_vd_ramp),
+
+MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE),
+
+MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2),
+SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2),
+SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2),
+SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, madera_eq_tlv),
+
+SOC_SINGLE("DAC High Performance Mode Switch", MADERA_OUTPUT_RATE_1,
+ MADERA_CP_DAC_MODE_SHIFT, 1, 0),
+
+MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5,
+ MADERA_DRC1R_ENA | MADERA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5,
+ MADERA_DRC2R_ENA | MADERA_DRC2L_ENA),
+
+MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE),
+
+MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2),
+MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2),
+MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2),
+MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2),
+
+SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode),
+
+MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]),
+MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]),
+MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]),
+MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]),
+MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_bidir_rate[0]),
+MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_bidir_rate[1]),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv),
+
+MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL,
+ MADERA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL,
+ MADERA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL,
+ MADERA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, madera_digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT,
+ MADERA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", madera_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", madera_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv),
+SOC_ENUM("Noise Gate Hold", madera_ng_hold),
+
+SOC_ENUM_EXT("DFC1RX Width", madera_dfc_width[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1RX Type", madera_dfc_type[0],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Width", madera_dfc_width[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC1TX Type", madera_dfc_type[1],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Width", madera_dfc_width[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2RX Type", madera_dfc_type[2],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Width", madera_dfc_width[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC2TX Type", madera_dfc_type[3],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Width", madera_dfc_width[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3RX Type", madera_dfc_type[4],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Width", madera_dfc_width[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC3TX Type", madera_dfc_type[5],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Width", madera_dfc_width[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4RX Type", madera_dfc_type[6],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Width", madera_dfc_width[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC4TX Type", madera_dfc_type[7],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Width", madera_dfc_width[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5RX Type", madera_dfc_type[8],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Width", madera_dfc_width[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC5TX Type", madera_dfc_type[9],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Width", madera_dfc_width[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6RX Type", madera_dfc_type[10],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Width", madera_dfc_width[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC6TX Type", madera_dfc_type[11],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Width", madera_dfc_width[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7RX Type", madera_dfc_type[12],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Width", madera_dfc_width[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC7TX Type", madera_dfc_type[13],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Width", madera_dfc_width[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8RX Type", madera_dfc_type[14],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Width", madera_dfc_width[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+SOC_ENUM_EXT("DFC8TX Type", madera_dfc_type[15],
+ snd_soc_get_enum_double, madera_dfc_put),
+
+CS47L92_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L),
+CS47L92_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R),
+CS47L92_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L),
+CS47L92_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R),
+CS47L92_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L),
+CS47L92_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R),
+CS47L92_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L),
+CS47L92_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R),
+
+MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX3", MADERA_AIF3TX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("AIF3TX4", MADERA_AIF3TX4MIX_INPUT_1_SOURCE),
+
+MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE),
+MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+MADERA_GAINMUX_CONTROLS("SPDIFTX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE),
+MADERA_GAINMUX_CONTROLS("SPDIFTX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+};
+
+MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE);
+MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX3, MADERA_AIF3TX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(AIF3TX4, MADERA_AIF3TX4MIX_INPUT_1_SOURCE);
+
+MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE);
+MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+MADERA_MUX_ENUMS(DFC1, MADERA_DFC1MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC2, MADERA_DFC2MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC3, MADERA_DFC3MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC4, MADERA_DFC4MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC5, MADERA_DFC5MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC6, MADERA_DFC6MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC7, MADERA_DFC7MIX_INPUT_1_SOURCE);
+MADERA_MUX_ENUMS(DFC8, MADERA_DFC8MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l92_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int cs47l92_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9
+};
+
+static const struct soc_enum cs47l92_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l92_aec_loopback_texts),
+ cs47l92_aec_loopback_texts,
+ cs47l92_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l92_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC1 Loopback", cs47l92_aec_loopback);
+
+static const struct snd_soc_dapm_widget cs47l92_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
+ 0, madera_sysclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1,
+ MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
+ MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK,
+ MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
+ 0, madera_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2,
+ MADERA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1D", MADERA_MIC_BIAS_CTRL_5,
+ MADERA_MICB1D_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6,
+ MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_FX, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ASRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_ISRC2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_OUT, 0,
+ cs47l92_outclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SPD, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DSP1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF1, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF2, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_AIF3, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_SLIMBUS, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_PWM, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("DFCCLK", SND_SOC_NOPM,
+ MADERA_DOM_GRP_DFC, 0,
+ madera_domain_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1ALN"),
+SND_SOC_DAPM_INPUT("IN1ALP"),
+SND_SOC_DAPM_INPUT("IN1BLN"),
+SND_SOC_DAPM_INPUT("IN1BLP"),
+SND_SOC_DAPM_INPUT("IN1ARN"),
+SND_SOC_DAPM_INPUT("IN1ARP"),
+SND_SOC_DAPM_INPUT("IN1BR"),
+SND_SOC_DAPM_INPUT("IN2ALN"),
+SND_SOC_DAPM_INPUT("IN2ALP"),
+SND_SOC_DAPM_INPUT("IN2BL"),
+SND_SOC_DAPM_INPUT("IN2ARN"),
+SND_SOC_DAPM_INPUT("IN2ARP"),
+SND_SOC_DAPM_INPUT("IN2BR"),
+
+SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]),
+SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]),
+SND_SOC_DAPM_MUX("IN2R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[3]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]),
+
+SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]),
+
+SND_SOC_DAPM_DEMUX("OUT3 Demux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux),
+SND_SOC_DAPM_MUX("OUT3 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
+ MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7,
+ MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
+ MADERA_SLIMBUS_TX_CHANNEL_ENABLE,
+ MADERA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX3", NULL, 2,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX4", NULL, 3,
+ MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", SND_SOC_NOPM,
+ MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", SND_SOC_NOPM,
+ MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL,
+ MADERA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in MADERA_MIXER_INPUT_ROUTES
+ */
+
+SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR,
+ MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1,
+ MADERA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1,
+ MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l92_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, madera_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
+ MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7,
+ MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX3", NULL, 2,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX4", NULL, 3,
+ MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, MADERA_SLIMBUS_RX_CHANNEL_ENABLE,
+ MADERA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN1R_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE,
+ MADERA_ASRC1_IN2R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3,
+ MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3,
+ MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, cs47l92_adsp_power_ev),
+
+/* end of ordered widget list */
+
+SND_SOC_DAPM_PGA("DFC1", MADERA_DFC1_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC2", MADERA_DFC2_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC3", MADERA_DFC3_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC4", MADERA_DFC4_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC5", MADERA_DFC5_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC6", MADERA_DFC6_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC7", MADERA_DFC7_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DFC8", MADERA_DFC8_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0),
+
+MADERA_MIXER_WIDGETS(EQ1, "EQ1"),
+MADERA_MIXER_WIDGETS(EQ2, "EQ2"),
+MADERA_MIXER_WIDGETS(EQ3, "EQ3"),
+MADERA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &madera_drc_activity_output_mux[1]),
+
+MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+MADERA_MIXER_WIDGETS(PWM1, "PWM1"),
+MADERA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"),
+MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"),
+
+MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+MADERA_MIXER_WIDGETS(AIF3TX3, "AIF3TX3"),
+MADERA_MIXER_WIDGETS(AIF3TX4, "AIF3TX4"),
+
+MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+MADERA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"),
+MADERA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"),
+
+MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"),
+MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"),
+MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"),
+MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"),
+
+MADERA_DSP_WIDGETS(DSP1, "DSP1"),
+
+MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+MADERA_MUX_WIDGETS(DFC1, "DFC1"),
+MADERA_MUX_WIDGETS(DFC2, "DFC2"),
+MADERA_MUX_WIDGETS(DFC3, "DFC3"),
+MADERA_MUX_WIDGETS(DFC4, "DFC4"),
+MADERA_MUX_WIDGETS(DFC5, "DFC5"),
+MADERA_MUX_WIDGETS(DFC6, "DFC6"),
+MADERA_MUX_WIDGETS(DFC7, "DFC7"),
+MADERA_MUX_WIDGETS(DFC8, "DFC8"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("HPOUT4L"),
+SND_SOC_DAPM_OUTPUT("HPOUT4R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPDIF1"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define MADERA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC1", "AEC1 Loopback" }, \
+ { name, "IN1L", "IN1L" }, \
+ { name, "IN1R", "IN1R" }, \
+ { name, "IN2L", "IN2L" }, \
+ { name, "IN2R", "IN2R" }, \
+ { name, "IN3L", "IN3L" }, \
+ { name, "IN3R", "IN3R" }, \
+ { name, "IN4L", "IN4L" }, \
+ { name, "IN4R", "IN4R" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF2RX7", "AIF2RX7" }, \
+ { name, "AIF2RX8", "AIF2RX8" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "AIF3RX3", "AIF3RX3" }, \
+ { name, "AIF3RX4", "AIF3RX4" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1IN1L", "ASRC1IN1L" }, \
+ { name, "ASRC1IN1R", "ASRC1IN1R" }, \
+ { name, "ASRC1IN2L", "ASRC1IN2L" }, \
+ { name, "ASRC1IN2R", "ASRC1IN2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DFC1", "DFC1" }, \
+ { name, "DFC2", "DFC2" }, \
+ { name, "DFC3", "DFC3" }, \
+ { name, "DFC4", "DFC4" }, \
+ { name, "DFC5", "DFC5" }, \
+ { name, "DFC6", "DFC6" }, \
+ { name, "DFC7", "DFC7" }, \
+ { name, "DFC8", "DFC8" }
+
+static const struct snd_soc_dapm_route cs47l92_dapm_routes[] = {
+ /* Internal clock domains */
+ { "EQ1", NULL, "FXCLK" },
+ { "EQ2", NULL, "FXCLK" },
+ { "EQ3", NULL, "FXCLK" },
+ { "EQ4", NULL, "FXCLK" },
+ { "DRC1L", NULL, "FXCLK" },
+ { "DRC1R", NULL, "FXCLK" },
+ { "DRC2L", NULL, "FXCLK" },
+ { "DRC2R", NULL, "FXCLK" },
+ { "LHPF1", NULL, "FXCLK" },
+ { "LHPF2", NULL, "FXCLK" },
+ { "LHPF3", NULL, "FXCLK" },
+ { "LHPF4", NULL, "FXCLK" },
+ { "PWM1 Mixer", NULL, "PWMCLK" },
+ { "PWM2 Mixer", NULL, "PWMCLK" },
+ { "OUT1L", NULL, "OUTCLK" },
+ { "OUT1R", NULL, "OUTCLK" },
+ { "OUT2L", NULL, "OUTCLK" },
+ { "OUT2R", NULL, "OUTCLK" },
+ { "OUT3L", NULL, "OUTCLK" },
+ { "OUT3R", NULL, "OUTCLK" },
+ { "OUT5L", NULL, "OUTCLK" },
+ { "OUT5R", NULL, "OUTCLK" },
+ { "AIF1TX1", NULL, "AIF1TXCLK" },
+ { "AIF1TX2", NULL, "AIF1TXCLK" },
+ { "AIF1TX3", NULL, "AIF1TXCLK" },
+ { "AIF1TX4", NULL, "AIF1TXCLK" },
+ { "AIF1TX5", NULL, "AIF1TXCLK" },
+ { "AIF1TX6", NULL, "AIF1TXCLK" },
+ { "AIF1TX7", NULL, "AIF1TXCLK" },
+ { "AIF1TX8", NULL, "AIF1TXCLK" },
+ { "AIF2TX1", NULL, "AIF2TXCLK" },
+ { "AIF2TX2", NULL, "AIF2TXCLK" },
+ { "AIF2TX3", NULL, "AIF2TXCLK" },
+ { "AIF2TX4", NULL, "AIF2TXCLK" },
+ { "AIF2TX5", NULL, "AIF2TXCLK" },
+ { "AIF2TX6", NULL, "AIF2TXCLK" },
+ { "AIF2TX7", NULL, "AIF2TXCLK" },
+ { "AIF2TX8", NULL, "AIF2TXCLK" },
+ { "AIF3TX1", NULL, "AIF3TXCLK" },
+ { "AIF3TX2", NULL, "AIF3TXCLK" },
+ { "AIF3TX3", NULL, "AIF3TXCLK" },
+ { "AIF3TX4", NULL, "AIF3TXCLK" },
+ { "SLIMTX1", NULL, "SLIMBUSCLK" },
+ { "SLIMTX2", NULL, "SLIMBUSCLK" },
+ { "SLIMTX3", NULL, "SLIMBUSCLK" },
+ { "SLIMTX4", NULL, "SLIMBUSCLK" },
+ { "SLIMTX5", NULL, "SLIMBUSCLK" },
+ { "SLIMTX6", NULL, "SLIMBUSCLK" },
+ { "SLIMTX7", NULL, "SLIMBUSCLK" },
+ { "SLIMTX8", NULL, "SLIMBUSCLK" },
+ { "SPD1TX1", NULL, "SPDCLK" },
+ { "SPD1TX2", NULL, "SPDCLK" },
+ { "DSP1", NULL, "DSP1CLK" },
+ { "ISRC1DEC1", NULL, "ISRC1CLK" },
+ { "ISRC1DEC2", NULL, "ISRC1CLK" },
+ { "ISRC1INT1", NULL, "ISRC1CLK" },
+ { "ISRC1INT2", NULL, "ISRC1CLK" },
+ { "ISRC2DEC1", NULL, "ISRC2CLK" },
+ { "ISRC2DEC2", NULL, "ISRC2CLK" },
+ { "ISRC2INT1", NULL, "ISRC2CLK" },
+ { "ISRC2INT2", NULL, "ISRC2CLK" },
+ { "ASRC1IN1L", NULL, "ASRC1CLK" },
+ { "ASRC1IN1R", NULL, "ASRC1CLK" },
+ { "ASRC1IN2L", NULL, "ASRC1CLK" },
+ { "ASRC1IN2R", NULL, "ASRC1CLK" },
+ { "DFC1", NULL, "DFCCLK" },
+ { "DFC2", NULL, "DFCCLK" },
+ { "DFC3", NULL, "DFCCLK" },
+ { "DFC4", NULL, "DFCCLK" },
+ { "DFC5", NULL, "DFCCLK" },
+ { "DFC6", NULL, "DFCCLK" },
+ { "DFC7", NULL, "DFCCLK" },
+ { "DFC8", NULL, "DFCCLK" },
+
+ { "OUT1L", NULL, "CPVDD1" },
+ { "OUT1L", NULL, "CPVDD2" },
+ { "OUT1R", NULL, "CPVDD1" },
+ { "OUT1R", NULL, "CPVDD2" },
+ { "OUT2L", NULL, "CPVDD1" },
+ { "OUT2L", NULL, "CPVDD2" },
+ { "OUT2R", NULL, "CPVDD1" },
+ { "OUT2R", NULL, "CPVDD2" },
+ { "OUT3L", NULL, "CPVDD1" },
+ { "OUT3L", NULL, "CPVDD2" },
+ { "OUT3R", NULL, "CPVDD1" },
+ { "OUT3R", NULL, "CPVDD2" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "SYSCLK" },
+ { "ASRC1IN1R", NULL, "SYSCLK" },
+ { "ASRC1IN2L", NULL, "SYSCLK" },
+ { "ASRC1IN2R", NULL, "SYSCLK" },
+
+ { "ASRC1IN1L", NULL, "ASYNCCLK" },
+ { "ASRC1IN1R", NULL, "ASYNCCLK" },
+ { "ASRC1IN2L", NULL, "ASYNCCLK" },
+ { "ASRC1IN2R", NULL, "ASYNCCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+ { "MICBIAS1D", NULL, "MICBIAS1" },
+
+ { "MICBIAS2A", NULL, "MICBIAS2" },
+ { "MICBIAS2B", NULL, "MICBIAS2" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+ { "AIF2 Capture", NULL, "AIF2TX7" },
+ { "AIF2 Capture", NULL, "AIF2TX8" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+ { "AIF2RX7", NULL, "AIF2 Playback" },
+ { "AIF2RX8", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+ { "AIF3 Capture", NULL, "AIF3TX3" },
+ { "AIF3 Capture", NULL, "AIF3TX4" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+ { "AIF3RX3", NULL, "AIF3 Playback" },
+ { "AIF3RX4", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+
+ { "IN1L Analog Mux", "A", "IN1ALN" },
+ { "IN1L Analog Mux", "A", "IN1ALP" },
+ { "IN1L Analog Mux", "B", "IN1BLN" },
+ { "IN1L Analog Mux", "B", "IN1BLP" },
+ { "IN1R Analog Mux", "A", "IN1ARN" },
+ { "IN1R Analog Mux", "A", "IN1ARP" },
+ { "IN1R Analog Mux", "B", "IN1BR" },
+ { "IN1R Analog Mux", "B", "IN1ALN" },
+
+ { "IN1L Mode", "Analog", "IN1L Analog Mux" },
+ { "IN1R Mode", "Analog", "IN1R Analog Mux" },
+
+ { "IN1L Mode", "Digital", "IN1ALN" },
+ { "IN1L Mode", "Digital", "IN1ALP" },
+ { "IN1R Mode", "Digital", "IN1ALN" },
+ { "IN1R Mode", "Digital", "IN1ALP" },
+
+ { "IN1L", NULL, "IN1L Mode" },
+ { "IN1R", NULL, "IN1R Mode" },
+
+ { "IN2L Analog Mux", "A", "IN2ALN" },
+ { "IN2L Analog Mux", "A", "IN2ALP" },
+ { "IN2L Analog Mux", "B", "IN2ALN" },
+ { "IN2L Analog Mux", "B", "IN2BL" },
+ { "IN2R Analog Mux", "A", "IN2ARN" },
+ { "IN2R Analog Mux", "A", "IN2ARP" },
+ { "IN2R Analog Mux", "B", "IN2ARN" },
+ { "IN2R Analog Mux", "B", "IN2BR" },
+
+ { "IN2L Mode", "Analog", "IN2L Analog Mux" },
+ { "IN2R Mode", "Analog", "IN2R Analog Mux" },
+
+ { "IN2L Mode", "Digital", "IN2ALN" },
+ { "IN2L Mode", "Digital", "IN2ALP" },
+ { "IN2R Mode", "Digital", "IN2ALN" },
+ { "IN2R Mode", "Digital", "IN2ALP" },
+
+ { "IN2L", NULL, "IN2L Mode" },
+ { "IN2R", NULL, "IN2R Mode" },
+
+ { "IN3L", NULL, "IN1ARN" },
+ { "IN3L", NULL, "IN1ARP" },
+ { "IN3R", NULL, "IN1ARN" },
+ { "IN3R", NULL, "IN1ARP" },
+
+ { "IN4L", NULL, "IN2ARN" },
+ { "IN4L", NULL, "IN2ARP" },
+ { "IN4R", NULL, "IN2ARN" },
+ { "IN4R", NULL, "IN2ARP" },
+
+ MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+ MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"),
+ MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"),
+
+ MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+ MADERA_MIXER_ROUTES("AIF3TX3", "AIF3TX3"),
+ MADERA_MIXER_ROUTES("AIF3TX4", "AIF3TX4"),
+
+ MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ MADERA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"),
+ MADERA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"),
+
+ MADERA_MIXER_ROUTES("EQ1", "EQ1"),
+ MADERA_MIXER_ROUTES("EQ2", "EQ2"),
+ MADERA_MIXER_ROUTES("EQ3", "EQ3"),
+ MADERA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ MADERA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ MADERA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ MADERA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ MADERA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ MADERA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ MADERA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ MADERA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ MADERA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"),
+ MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"),
+ MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"),
+ MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"),
+
+ MADERA_DSP_ROUTES("DSP1"),
+
+ MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+
+ MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+ MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC1 Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC1 Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC1 Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC1 Loopback", "HPOUT3R", "OUT3R" },
+ { "OUT3 Demux", NULL, "OUT3L" },
+ { "OUT3 Demux", NULL, "OUT3R" },
+
+ { "OUT3R", NULL, "OUT3 Mono Mux" },
+
+ { "HPOUT3L", "HPOUT3", "OUT3 Demux" },
+ { "HPOUT3R", "HPOUT3", "OUT3 Demux" },
+ { "HPOUT4L", "HPOUT4", "OUT3 Demux" },
+ { "HPOUT4R", "HPOUT4", "OUT3 Demux" },
+
+ { "AEC1 Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC1 Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "SPDIF1", NULL, "SPD1" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+
+ MADERA_MUX_ROUTES("DFC1", "DFC1"),
+ MADERA_MUX_ROUTES("DFC2", "DFC2"),
+ MADERA_MUX_ROUTES("DFC3", "DFC3"),
+ MADERA_MUX_ROUTES("DFC4", "DFC4"),
+ MADERA_MUX_ROUTES("DFC5", "DFC5"),
+ MADERA_MUX_ROUTES("DFC6", "DFC6"),
+ MADERA_MUX_ROUTES("DFC7", "DFC7"),
+ MADERA_MUX_ROUTES("DFC8", "DFC8"),
+};
+
+static int cs47l92_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case MADERA_FLL1_REFCLK:
+ return madera_fllhj_set_refclk(&cs47l92->fll[0], source, fref,
+ fout);
+ case MADERA_FLL2_REFCLK:
+ return madera_fllhj_set_refclk(&cs47l92->fll[1], source, fref,
+ fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops cs47l92_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs47l92_dai[] = {
+ {
+ .name = "cs47l92-aif1",
+ .id = 1,
+ .base = MADERA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l92-aif2",
+ .id = 2,
+ .base = MADERA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l92-aif3",
+ .id = 3,
+ .base = MADERA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs47l92-slim1",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l92-slim2",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l92-slim3",
+ .id = 7,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &madera_simple_dai_ops,
+ },
+ {
+ .name = "cs47l92-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ .ops = &cs47l92_dai_ops,
+ },
+ {
+ .name = "cs47l92-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MADERA_RATES,
+ .formats = MADERA_FORMATS,
+ },
+ },
+};
+
+static int cs47l92_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ int n_adsp;
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l92-dsp-trace") == 0) {
+ n_adsp = 0;
+ } else {
+ dev_err(madera->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l92_adsp2_irq(int irq, void *data)
+{
+ struct cs47l92 *cs47l92 = data;
+ struct madera_priv *priv = &cs47l92->core;
+ struct madera *madera = priv->madera;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&priv->adsp[0]);
+ if (ret == -ENODEV) {
+ dev_err(madera->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_route cs47l92_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3 Mono Mux", "HPOUT3", "OUT3L" },
+ { "OUT3 Mono Mux", "HPOUT4", "OUT3L" },
+};
+
+static int cs47l92_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l92->core.madera;
+ int ret;
+
+ snd_soc_component_init_regmap(component, madera->regmap);
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = snd_soc_component_to_dapm(component);
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ ret = madera_init_inputs(component);
+ if (ret)
+ return ret;
+
+ ret = madera_init_outputs(component, cs47l92_mono_routes,
+ ARRAY_SIZE(cs47l92_mono_routes),
+ CS47L92_MONO_OUTPUTS);
+ if (ret)
+ return ret;
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ ret = snd_soc_add_component_controls(component,
+ madera_adsp_rate_controls,
+ CS47L92_NUM_ADSP);
+ if (ret)
+ return ret;
+
+ return wm_adsp2_component_probe(&cs47l92->core.adsp[0], component);
+}
+
+static void cs47l92_component_remove(struct snd_soc_component *component)
+{
+ struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component);
+ struct madera *madera = cs47l92->core.madera;
+
+ mutex_lock(&madera->dapm_ptr_lock);
+ madera->dapm = NULL;
+ mutex_unlock(&madera->dapm_ptr_lock);
+
+ wm_adsp2_component_remove(&cs47l92->core.adsp[0], component);
+}
+
+#define CS47L92_DIG_VU 0x0200
+
+static unsigned int cs47l92_digital_vu[] = {
+ MADERA_DAC_DIGITAL_VOLUME_1L,
+ MADERA_DAC_DIGITAL_VOLUME_1R,
+ MADERA_DAC_DIGITAL_VOLUME_2L,
+ MADERA_DAC_DIGITAL_VOLUME_2R,
+ MADERA_DAC_DIGITAL_VOLUME_3L,
+ MADERA_DAC_DIGITAL_VOLUME_3R,
+ MADERA_DAC_DIGITAL_VOLUME_5L,
+ MADERA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static const struct snd_compress_ops cs47l92_compress_ops = {
+ .open = &cs47l92_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs47l92 = {
+ .probe = &cs47l92_component_probe,
+ .remove = &cs47l92_component_remove,
+ .set_sysclk = &madera_set_sysclk,
+ .set_pll = &cs47l92_set_fll,
+ .name = DRV_NAME,
+ .compress_ops = &cs47l92_compress_ops,
+ .controls = cs47l92_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l92_snd_controls),
+ .dapm_widgets = cs47l92_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l92_dapm_widgets),
+ .dapm_routes = cs47l92_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l92_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs47l92_probe(struct platform_device *pdev)
+{
+ struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l92 *cs47l92;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l92_dai) > MADERA_MAX_DAI);
+
+ /* quick exit if Madera irqchip driver hasn't completed probe */
+ if (!madera->irq_dev) {
+ dev_dbg(&pdev->dev, "irqchip driver not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ cs47l92 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l92), GFP_KERNEL);
+ if (!cs47l92)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l92);
+
+ cs47l92->core.madera = madera;
+ cs47l92->core.dev = &pdev->dev;
+ cs47l92->core.num_inputs = 8;
+
+ ret = madera_core_init(&cs47l92->core);
+ if (ret)
+ return ret;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l92_adsp2_irq,
+ cs47l92);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto error_core;
+ }
+
+ ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
+
+ cs47l92->core.adsp[0].part = "cs47l92";
+ cs47l92->core.adsp[0].cs_dsp.num = 1;
+ cs47l92->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ cs47l92->core.adsp[0].cs_dsp.rev = 2;
+ cs47l92->core.adsp[0].cs_dsp.dev = madera->dev;
+ cs47l92->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit;
+
+ cs47l92->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1;
+ cs47l92->core.adsp[0].cs_dsp.mem = cs47l92_dsp1_regions;
+ cs47l92->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l92_dsp1_regions);
+
+ cs47l92->core.adsp[0].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9;
+
+ ret = wm_adsp2_init(&cs47l92->core.adsp[0]);
+ if (ret != 0)
+ goto error_dsp_irq;
+
+ ret = madera_init_bus_error_irq(&cs47l92->core, 0, wm_adsp2_bus_error);
+ if (ret != 0)
+ goto error_adsp;
+
+ madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1,
+ &cs47l92->fll[0]);
+ madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1,
+ &cs47l92->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l92_dai); i++)
+ madera_init_dai(&cs47l92->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l92_digital_vu); i++)
+ regmap_update_bits(madera->regmap, cs47l92_digital_vu[i],
+ CS47L92_DIG_VU, CS47L92_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cs47l92,
+ cs47l92_dai,
+ ARRAY_SIZE(cs47l92_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+ madera_free_bus_error_irq(&cs47l92->core, 0);
+error_adsp:
+ wm_adsp2_remove(&cs47l92->core.adsp[0]);
+error_dsp_irq:
+ madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l92);
+error_core:
+ madera_core_free(&cs47l92->core);
+
+ return ret;
+}
+
+static void cs47l92_remove(struct platform_device *pdev)
+{
+ struct cs47l92 *cs47l92 = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ madera_free_bus_error_irq(&cs47l92->core, 0);
+ wm_adsp2_remove(&cs47l92->core.adsp[0]);
+
+ madera_set_irq_wake(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
+ madera_free_irq(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l92);
+
+ madera_core_free(&cs47l92->core);
+}
+
+static struct platform_driver cs47l92_codec_driver = {
+ .driver = {
+ .name = "cs47l92-codec",
+ },
+ .probe = &cs47l92_probe,
+ .remove = cs47l92_remove,
+};
+
+module_platform_driver(cs47l92_codec_driver);
+
+MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp");
+MODULE_DESCRIPTION("ASoC CS47L92 driver");
+MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l92-codec");
diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c
new file mode 100644
index 000000000000..8ff3652a010e
--- /dev/null
+++ b/sound/soc/codecs/cs48l32-tables.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Regmap tables and other data for Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/device.h>
+#include <linux/linear_range.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+
+#include "cs48l32.h"
+
+static const struct reg_sequence cs48l32_reva_patch[] = {
+ { 0x00001044, 0x0005000f },
+ { 0x00001c34, 0x000037e8 },
+ { 0x000046d8, 0x00000fe0 },
+};
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs48l32->regmap, cs48l32_reva_patch,
+ ARRAY_SIZE(cs48l32_reva_patch));
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to apply patch\n");
+
+ return 0;
+}
+
+static const struct reg_default cs48l32_reg_default[] = {
+ { 0x00000c08, 0xe1000001 }, /* GPIO1_CTRL1 */
+ { 0x00000c0c, 0xe1000001 }, /* GPIO2_CTRL1 */
+ { 0x00000c10, 0xe1000001 }, /* GPIO3_CTRL1 */
+ { 0x00000c14, 0xe1000001 }, /* GPIO4_CTRL1 */
+ { 0x00000c18, 0xe1000001 }, /* GPIO5_CTRL1 */
+ { 0x00000c1c, 0xe1000001 }, /* GPIO6_CTRL1 */
+ { 0x00000c20, 0xe1000001 }, /* GPIO7_CTRL1 */
+ { 0x00000c24, 0xe1000001 }, /* GPIO8_CTRL1 */
+ { 0x00000c28, 0xe1000001 }, /* GPIO9_CTRL1 */
+ { 0x00000c2c, 0xe1000001 }, /* GPIO10_CTRL1 */
+ { 0x00000c30, 0xe1000001 }, /* GPIO11_CTRL1 */
+ { 0x00000c34, 0xe1000001 }, /* GPIO12_CTRL1 */
+ { 0x00000c38, 0xe1000001 }, /* GPIO13_CTRL1 */
+ { 0x00000c3c, 0xe1000001 }, /* GPIO14_CTRL1 */
+ { 0x00000c40, 0xe1000001 }, /* GPIO15_CTRL1 */
+ { 0x00000c44, 0xe1000001 }, /* GPIO16_CTRL1 */
+ { 0x00001020, 0x00000000 }, /* OUTPUT_SYS_CLK */
+ { 0x00001044, 0x0005000f }, /* AUXPDM_CTRL */
+ { 0x0000105c, 0x00000000 }, /* AUXPDM_CTRL2 */
+ { 0x00001400, 0x00000002 }, /* CLOCK32K */
+ { 0x00001404, 0x00000404 }, /* SYSTEM_CLOCK1 */
+ { 0x00001420, 0x00000003 }, /* SAMPLE_RATE1 */
+ { 0x00001424, 0x00000003 }, /* SAMPLE_RATE2 */
+ { 0x00001428, 0x00000003 }, /* SAMPLE_RATE3 */
+ { 0x0000142c, 0x00000003 }, /* SAMPLE_RATE4 */
+ { 0x00001c00, 0x00000002 }, /* FLL1_CONTROL1 */
+ { 0x00001c04, 0x88203004 }, /* FLL1_CONTROL2 */
+ { 0x00001c08, 0x00000000 }, /* FLL1_CONTROL3 */
+ { 0x00001c0c, 0x21f05001 }, /* FLL1_CONTROL4 */
+ { 0x00001ca0, 0x00000c04 }, /* FLL1_GPIO_CLOCK */
+ { 0x00002000, 0x00000006 }, /* CHARGE_PUMP1 */
+ { 0x00002408, 0x000003e4 }, /* LDO2_CTRL1 */
+ { 0x00002410, 0x000000e6 }, /* MICBIAS_CTRL1 */
+ { 0x00002418, 0x00000222 }, /* MICBIAS_CTRL5 */
+ { 0x00002710, 0x00004600 }, /* IRQ1_CTRL_AOD */
+ { 0x00004000, 0x00000000 }, /* INPUT_CONTROL */
+ { 0x00004008, 0x00000400 }, /* INPUT_RATE_CONTROL */
+ { 0x0000400c, 0x00000000 }, /* INPUT_CONTROL2 */
+ { 0x00004020, 0x00050020 }, /* INPUT1_CONTROL1 */
+ { 0x00004024, 0x00000000 }, /* IN1L_CONTROL1 */
+ { 0x00004028, 0x10800080 }, /* IN1L_CONTROL2 */
+ { 0x00004044, 0x00000000 }, /* IN1R_CONTROL1 */
+ { 0x00004048, 0x10800080 }, /* IN1R_CONTROL2 */
+ { 0x00004060, 0x00050020 }, /* INPUT2_CONTROL1 */
+ { 0x00004064, 0x00000000 }, /* IN2L_CONTROL1 */
+ { 0x00004068, 0x10800000 }, /* IN2L_CONTROL2 */
+ { 0x00004084, 0x00000000 }, /* IN2R_CONTROL1 */
+ { 0x00004088, 0x10800000 }, /* IN2R_CONTROL2 */
+ { 0x00004244, 0x00000002 }, /* INPUT_HPF_CONTROL */
+ { 0x00004248, 0x00000022 }, /* INPUT_VOL_CONTROL */
+ { 0x00004300, 0x00000000 }, /* AUXPDM_CONTROL1 */
+ { 0x00004304, 0x00000000 }, /* AUXPDM_CONTROL2 */
+ { 0x00004308, 0x00010008 }, /* AUXPDM1_CONTROL1 */
+ { 0x00004310, 0x00010008 }, /* AUXPDM2_CONTROL1 */
+ { 0x00004688, 0x00000000 }, /* ADC1L_ANA_CONTROL1 */
+ { 0x0000468c, 0x00000000 }, /* ADC1R_ANA_CONTROL1 */
+ { 0x00006000, 0x00000000 }, /* ASP1_ENABLES1 */
+ { 0x00006004, 0x00000028 }, /* ASP1_CONTROL1 */
+ { 0x00006008, 0x18180200 }, /* ASP1_CONTROL2 */
+ { 0x0000600c, 0x00000002 }, /* ASP1_CONTROL3 */
+ { 0x00006010, 0x03020100 }, /* ASP1_FRAME_CONTROL1 */
+ { 0x00006014, 0x07060504 }, /* ASP1_FRAME_CONTROL2 */
+ { 0x00006020, 0x03020100 }, /* ASP1_FRAME_CONTROL5 */
+ { 0x00006024, 0x07060504 }, /* ASP1_FRAME_CONTROL6 */
+ { 0x00006030, 0x00000020 }, /* ASP1_DATA_CONTROL1 */
+ { 0x00006040, 0x00000020 }, /* ASP1_DATA_CONTROL5 */
+ { 0x00006080, 0x00000000 }, /* ASP2_ENABLES1 */
+ { 0x00006084, 0x00000028 }, /* ASP2_CONTROL1 */
+ { 0x00006088, 0x18180200 }, /* ASP2_CONTROL2 */
+ { 0x0000608c, 0x00000002 }, /* ASP2_CONTROL3 */
+ { 0x00006090, 0x03020100 }, /* ASP2_FRAME_CONTROL1 */
+ { 0x000060a0, 0x03020100 }, /* ASP2_FRAME_CONTROL5 */
+ { 0x000060b0, 0x00000020 }, /* ASP2_DATA_CONTROL1 */
+ { 0x000060c0, 0x00000020 }, /* ASP2_DATA_CONTROL5 */
+ { 0x00008200, 0x00800000 }, /* ASP1TX1_INPUT1 */
+ { 0x00008204, 0x00800000 }, /* ASP1TX1_INPUT2 */
+ { 0x00008208, 0x00800000 }, /* ASP1TX1_INPUT3 */
+ { 0x0000820c, 0x00800000 }, /* ASP1TX1_INPUT4 */
+ { 0x00008210, 0x00800000 }, /* ASP1TX2_INPUT1 */
+ { 0x00008214, 0x00800000 }, /* ASP1TX2_INPUT2 */
+ { 0x00008218, 0x00800000 }, /* ASP1TX2_INPUT3 */
+ { 0x0000821c, 0x00800000 }, /* ASP1TX2_INPUT4 */
+ { 0x00008220, 0x00800000 }, /* ASP1TX3_INPUT1 */
+ { 0x00008224, 0x00800000 }, /* ASP1TX3_INPUT2 */
+ { 0x00008228, 0x00800000 }, /* ASP1TX3_INPUT3 */
+ { 0x0000822c, 0x00800000 }, /* ASP1TX3_INPUT4 */
+ { 0x00008230, 0x00800000 }, /* ASP1TX4_INPUT1 */
+ { 0x00008234, 0x00800000 }, /* ASP1TX4_INPUT2 */
+ { 0x00008238, 0x00800000 }, /* ASP1TX4_INPUT3 */
+ { 0x0000823c, 0x00800000 }, /* ASP1TX4_INPUT4 */
+ { 0x00008240, 0x00800000 }, /* ASP1TX5_INPUT1 */
+ { 0x00008244, 0x00800000 }, /* ASP1TX5_INPUT2 */
+ { 0x00008248, 0x00800000 }, /* ASP1TX5_INPUT3 */
+ { 0x0000824c, 0x00800000 }, /* ASP1TX5_INPUT4 */
+ { 0x00008250, 0x00800000 }, /* ASP1TX6_INPUT1 */
+ { 0x00008254, 0x00800000 }, /* ASP1TX6_INPUT2 */
+ { 0x00008258, 0x00800000 }, /* ASP1TX6_INPUT3 */
+ { 0x0000825c, 0x00800000 }, /* ASP1TX6_INPUT4 */
+ { 0x00008260, 0x00800000 }, /* ASP1TX7_INPUT1 */
+ { 0x00008264, 0x00800000 }, /* ASP1TX7_INPUT2 */
+ { 0x00008268, 0x00800000 }, /* ASP1TX7_INPUT3 */
+ { 0x0000826c, 0x00800000 }, /* ASP1TX7_INPUT4 */
+ { 0x00008270, 0x00800000 }, /* ASP1TX8_INPUT1 */
+ { 0x00008274, 0x00800000 }, /* ASP1TX8_INPUT2 */
+ { 0x00008278, 0x00800000 }, /* ASP1TX8_INPUT3 */
+ { 0x0000827c, 0x00800000 }, /* ASP1TX8_INPUT4 */
+ { 0x00008300, 0x00800000 }, /* ASP2TX1_INPUT1 */
+ { 0x00008304, 0x00800000 }, /* ASP2TX1_INPUT2 */
+ { 0x00008308, 0x00800000 }, /* ASP2TX1_INPUT3 */
+ { 0x0000830c, 0x00800000 }, /* ASP2TX1_INPUT4 */
+ { 0x00008310, 0x00800000 }, /* ASP2TX2_INPUT1 */
+ { 0x00008314, 0x00800000 }, /* ASP2TX2_INPUT2 */
+ { 0x00008318, 0x00800000 }, /* ASP2TX2_INPUT3 */
+ { 0x0000831c, 0x00800000 }, /* ASP2TX2_INPUT4 */
+ { 0x00008320, 0x00800000 }, /* ASP2TX3_INPUT1 */
+ { 0x00008324, 0x00800000 }, /* ASP2TX3_INPUT2 */
+ { 0x00008328, 0x00800000 }, /* ASP2TX3_INPUT3 */
+ { 0x0000832c, 0x00800000 }, /* ASP2TX3_INPUT4 */
+ { 0x00008330, 0x00800000 }, /* ASP2TX4_INPUT1 */
+ { 0x00008334, 0x00800000 }, /* ASP2TX4_INPUT2 */
+ { 0x00008338, 0x00800000 }, /* ASP2TX4_INPUT3 */
+ { 0x0000833c, 0x00800000 }, /* ASP2TX4_INPUT4 */
+ { 0x00008980, 0x00000000 }, /* ISRC1INT1_INPUT1 */
+ { 0x00008990, 0x00000000 }, /* ISRC1INT2_INPUT1 */
+ { 0x000089a0, 0x00000000 }, /* ISRC1INT3_INPUT1 */
+ { 0x000089b0, 0x00000000 }, /* ISRC1INT4_INPUT1 */
+ { 0x000089c0, 0x00000000 }, /* ISRC1DEC1_INPUT1 */
+ { 0x000089d0, 0x00000000 }, /* ISRC1DEC2_INPUT1 */
+ { 0x000089e0, 0x00000000 }, /* ISRC1DEC3_INPUT1 */
+ { 0x000089f0, 0x00000000 }, /* ISRC1DEC4_INPUT1 */
+ { 0x00008a00, 0x00000000 }, /* ISRC2INT1_INPUT1 */
+ { 0x00008a10, 0x00000000 }, /* ISRC2INT2_INPUT1 */
+ { 0x00008a40, 0x00000000 }, /* ISRC2DEC1_INPUT1 */
+ { 0x00008a50, 0x00000000 }, /* ISRC2DEC2_INPUT1 */
+ { 0x00008a80, 0x00000000 }, /* ISRC3INT1_INPUT1 */
+ { 0x00008a90, 0x00000000 }, /* ISRC3INT2_INPUT1 */
+ { 0x00008ac0, 0x00000000 }, /* ISRC3DEC1_INPUT1 */
+ { 0x00008ad0, 0x00000000 }, /* ISRC3DEC2_INPUT1 */
+ { 0x00008b80, 0x00800000 }, /* EQ1_INPUT1 */
+ { 0x00008b84, 0x00800000 }, /* EQ1_INPUT2 */
+ { 0x00008b88, 0x00800000 }, /* EQ1_INPUT3 */
+ { 0x00008b8c, 0x00800000 }, /* EQ1_INPUT4 */
+ { 0x00008b90, 0x00800000 }, /* EQ2_INPUT1 */
+ { 0x00008b94, 0x00800000 }, /* EQ2_INPUT2 */
+ { 0x00008b98, 0x00800000 }, /* EQ2_INPUT3 */
+ { 0x00008b9c, 0x00800000 }, /* EQ2_INPUT4 */
+ { 0x00008ba0, 0x00800000 }, /* EQ3_INPUT1 */
+ { 0x00008ba4, 0x00800000 }, /* EQ3_INPUT2 */
+ { 0x00008ba8, 0x00800000 }, /* EQ3_INPUT3 */
+ { 0x00008bac, 0x00800000 }, /* EQ3_INPUT4 */
+ { 0x00008bb0, 0x00800000 }, /* EQ4_INPUT1 */
+ { 0x00008bb4, 0x00800000 }, /* EQ4_INPUT2 */
+ { 0x00008bb8, 0x00800000 }, /* EQ4_INPUT3 */
+ { 0x00008bbc, 0x00800000 }, /* EQ4_INPUT4 */
+ { 0x00008c00, 0x00800000 }, /* DRC1L_INPUT1 */
+ { 0x00008c04, 0x00800000 }, /* DRC1L_INPUT2 */
+ { 0x00008c08, 0x00800000 }, /* DRC1L_INPUT3 */
+ { 0x00008c0c, 0x00800000 }, /* DRC1L_INPUT4 */
+ { 0x00008c10, 0x00800000 }, /* DRC1R_INPUT1 */
+ { 0x00008c14, 0x00800000 }, /* DRC1R_INPUT2 */
+ { 0x00008c18, 0x00800000 }, /* DRC1R_INPUT3 */
+ { 0x00008c1c, 0x00800000 }, /* DRC1R_INPUT4 */
+ { 0x00008c20, 0x00800000 }, /* DRC2L_INPUT1 */
+ { 0x00008c24, 0x00800000 }, /* DRC2L_INPUT2 */
+ { 0x00008c28, 0x00800000 }, /* DRC2L_INPUT3 */
+ { 0x00008c2c, 0x00800000 }, /* DRC2L_INPUT4 */
+ { 0x00008c30, 0x00800000 }, /* DRC2R_INPUT1 */
+ { 0x00008c34, 0x00800000 }, /* DRC2R_INPUT2 */
+ { 0x00008c38, 0x00800000 }, /* DRC2R_INPUT3 */
+ { 0x00008c3c, 0x00800000 }, /* DRC2R_INPUT4 */
+ { 0x00008c80, 0x00800000 }, /* LHPF1_INPUT1 */
+ { 0x00008c84, 0x00800000 }, /* LHPF1_INPUT2 */
+ { 0x00008c88, 0x00800000 }, /* LHPF1_INPUT3 */
+ { 0x00008c8c, 0x00800000 }, /* LHPF1_INPUT4 */
+ { 0x00008c90, 0x00800000 }, /* LHPF2_INPUT1 */
+ { 0x00008c94, 0x00800000 }, /* LHPF2_INPUT2 */
+ { 0x00008c98, 0x00800000 }, /* LHPF2_INPUT3 */
+ { 0x00008c9c, 0x00800000 }, /* LHPF2_INPUT4 */
+ { 0x00008ca0, 0x00800000 }, /* LHPF3_INPUT1 */
+ { 0x00008ca4, 0x00800000 }, /* LHPF3_INPUT2 */
+ { 0x00008ca8, 0x00800000 }, /* LHPF3_INPUT3 */
+ { 0x00008cac, 0x00800000 }, /* LHPF3_INPUT4 */
+ { 0x00008cb0, 0x00800000 }, /* LHPF4_INPUT1 */
+ { 0x00008cb4, 0x00800000 }, /* LHPF4_INPUT2 */
+ { 0x00008cb8, 0x00800000 }, /* LHPF4_INPUT3 */
+ { 0x00008cbc, 0x00800000 }, /* LHPF4_INPUT4 */
+ { 0x00009000, 0x00800000 }, /* DSP1RX1_INPUT1 */
+ { 0x00009004, 0x00800000 }, /* DSP1RX1_INPUT2 */
+ { 0x00009008, 0x00800000 }, /* DSP1RX1_INPUT3 */
+ { 0x0000900c, 0x00800000 }, /* DSP1RX1_INPUT4 */
+ { 0x00009010, 0x00800000 }, /* DSP1RX2_INPUT1 */
+ { 0x00009014, 0x00800000 }, /* DSP1RX2_INPUT2 */
+ { 0x00009018, 0x00800000 }, /* DSP1RX2_INPUT3 */
+ { 0x0000901c, 0x00800000 }, /* DSP1RX2_INPUT4 */
+ { 0x00009020, 0x00800000 }, /* DSP1RX3_INPUT1 */
+ { 0x00009024, 0x00800000 }, /* DSP1RX3_INPUT2 */
+ { 0x00009028, 0x00800000 }, /* DSP1RX3_INPUT3 */
+ { 0x0000902c, 0x00800000 }, /* DSP1RX3_INPUT4 */
+ { 0x00009030, 0x00800000 }, /* DSP1RX4_INPUT1 */
+ { 0x00009034, 0x00800000 }, /* DSP1RX4_INPUT2 */
+ { 0x00009038, 0x00800000 }, /* DSP1RX4_INPUT3 */
+ { 0x0000903c, 0x00800000 }, /* DSP1RX4_INPUT4 */
+ { 0x00009040, 0x00800000 }, /* DSP1RX5_INPUT1 */
+ { 0x00009044, 0x00800000 }, /* DSP1RX5_INPUT2 */
+ { 0x00009048, 0x00800000 }, /* DSP1RX5_INPUT3 */
+ { 0x0000904c, 0x00800000 }, /* DSP1RX5_INPUT4 */
+ { 0x00009050, 0x00800000 }, /* DSP1RX6_INPUT1 */
+ { 0x00009054, 0x00800000 }, /* DSP1RX6_INPUT2 */
+ { 0x00009058, 0x00800000 }, /* DSP1RX6_INPUT3 */
+ { 0x0000905c, 0x00800000 }, /* DSP1RX6_INPUT4 */
+ { 0x00009060, 0x00800000 }, /* DSP1RX7_INPUT1 */
+ { 0x00009064, 0x00800000 }, /* DSP1RX7_INPUT2 */
+ { 0x00009068, 0x00800000 }, /* DSP1RX7_INPUT3 */
+ { 0x0000906c, 0x00800000 }, /* DSP1RX7_INPUT4 */
+ { 0x00009070, 0x00800000 }, /* DSP1RX8_INPUT1 */
+ { 0x00009074, 0x00800000 }, /* DSP1RX8_INPUT2 */
+ { 0x00009078, 0x00800000 }, /* DSP1RX8_INPUT3 */
+ { 0x0000907c, 0x00800000 }, /* DSP1RX8_INPUT4 */
+ { 0x0000a400, 0x00000000 }, /* ISRC1_CONTROL1 */
+ { 0x0000a404, 0x00000000 }, /* ISRC1_CONTROL2 */
+ { 0x0000a510, 0x00000000 }, /* ISRC2_CONTROL1 */
+ { 0x0000a514, 0x00000000 }, /* ISRC2_CONTROL2 */
+ { 0x0000a620, 0x00000000 }, /* ISRC3_CONTROL1 */
+ { 0x0000a624, 0x00000000 }, /* ISRC3_CONTROL2 */
+ { 0x0000a800, 0x00000000 }, /* FX_SAMPLE_RATE */
+ { 0x0000a808, 0x00000000 }, /* EQ_CONTROL1 */
+ { 0x0000a80c, 0x00000000 }, /* EQ_CONTROL2 */
+ { 0x0000a810, 0x0c0c0c0c }, /* EQ1_GAIN1 */
+ { 0x0000a814, 0x0000000c }, /* EQ1_GAIN2 */
+ { 0x0000a818, 0x03fe0fc8 }, /* EQ1_BAND1_COEFF1 */
+ { 0x0000a81c, 0x00000b75 }, /* EQ1_BAND1_COEFF2 */
+ { 0x0000a820, 0x000000e0 }, /* EQ1_BAND1_PG */
+ { 0x0000a824, 0xf1361ec4 }, /* EQ1_BAND2_COEFF1 */
+ { 0x0000a828, 0x00000409 }, /* EQ1_BAND2_COEFF2 */
+ { 0x0000a82c, 0x000004cc }, /* EQ1_BAND2_PG */
+ { 0x0000a830, 0xf3371c9b }, /* EQ1_BAND3_COEFF1 */
+ { 0x0000a834, 0x0000040b }, /* EQ1_BAND3_COEFF2 */
+ { 0x0000a838, 0x00000cbb }, /* EQ1_BAND3_PG */
+ { 0x0000a83c, 0xf7d916f8 }, /* EQ1_BAND4_COEFF1 */
+ { 0x0000a840, 0x0000040a }, /* EQ1_BAND4_COEFF2 */
+ { 0x0000a844, 0x00001f14 }, /* EQ1_BAND4_PG */
+ { 0x0000a848, 0x0563058c }, /* EQ1_BAND5_COEFF1 */
+ { 0x0000a84c, 0x00000000 }, /* EQ1_BAND5_COEFF1 + 4 */
+ { 0x0000a850, 0x00004000 }, /* EQ1_BAND5_PG */
+ { 0x0000a854, 0x0c0c0c0c }, /* EQ2_GAIN1 */
+ { 0x0000a858, 0x0000000c }, /* EQ2_GAIN2 */
+ { 0x0000a85c, 0x03fe0fc8 }, /* EQ2_BAND1_COEFF1 */
+ { 0x0000a860, 0x00000b75 }, /* EQ2_BAND1_COEFF2 */
+ { 0x0000a864, 0x000000e0 }, /* EQ2_BAND1_PG */
+ { 0x0000a868, 0xf1361ec4 }, /* EQ2_BAND2_COEFF1 */
+ { 0x0000a86c, 0x00000409 }, /* EQ2_BAND2_COEFF2 */
+ { 0x0000a870, 0x000004cc }, /* EQ2_BAND2_PG */
+ { 0x0000a874, 0xf3371c9b }, /* EQ2_BAND3_COEFF1 */
+ { 0x0000a878, 0x0000040b }, /* EQ2_BAND3_COEFF2 */
+ { 0x0000a87c, 0x00000cbb }, /* EQ2_BAND3_PG */
+ { 0x0000a880, 0xf7d916f8 }, /* EQ2_BAND4_COEFF1 */
+ { 0x0000a884, 0x0000040a }, /* EQ2_BAND4_COEFF2 */
+ { 0x0000a888, 0x00001f14 }, /* EQ2_BAND4_PG */
+ { 0x0000a88c, 0x0563058c }, /* EQ2_BAND5_COEFF1 */
+ { 0x0000a890, 0x00000000 }, /* EQ2_BAND5_COEFF1 + 4 */
+ { 0x0000a894, 0x00004000 }, /* EQ2_BAND5_PG */
+ { 0x0000a898, 0x0c0c0c0c }, /* EQ3_GAIN1 */
+ { 0x0000a89c, 0x0000000c }, /* EQ3_GAIN2 */
+ { 0x0000a8a0, 0x03fe0fc8 }, /* EQ3_BAND1_COEFF1 */
+ { 0x0000a8a4, 0x00000b75 }, /* EQ3_BAND1_COEFF2 */
+ { 0x0000a8a8, 0x000000e0 }, /* EQ3_BAND1_PG */
+ { 0x0000a8ac, 0xf1361ec4 }, /* EQ3_BAND2_COEFF1 */
+ { 0x0000a8b0, 0x00000409 }, /* EQ3_BAND2_COEFF2 */
+ { 0x0000a8b4, 0x000004cc }, /* EQ3_BAND2_PG */
+ { 0x0000a8b8, 0xf3371c9b }, /* EQ3_BAND3_COEFF1 */
+ { 0x0000a8bc, 0x0000040b }, /* EQ3_BAND3_COEFF2 */
+ { 0x0000a8c0, 0x00000cbb }, /* EQ3_BAND3_PG */
+ { 0x0000a8c4, 0xf7d916f8 }, /* EQ3_BAND4_COEFF1 */
+ { 0x0000a8c8, 0x0000040a }, /* EQ3_BAND4_COEFF2 */
+ { 0x0000a8cc, 0x00001f14 }, /* EQ3_BAND4_PG */
+ { 0x0000a8d0, 0x0563058c }, /* EQ3_BAND5_COEFF1 */
+ { 0x0000a8d4, 0x00000000 }, /* EQ3_BAND5_COEFF1 + 4 */
+ { 0x0000a8d8, 0x00004000 }, /* EQ3_BAND5_PG */
+ { 0x0000a8dc, 0x0c0c0c0c }, /* EQ4_GAIN1 */
+ { 0x0000a8e0, 0x0000000c }, /* EQ4_GAIN2 */
+ { 0x0000a8e4, 0x03fe0fc8 }, /* EQ4_BAND1_COEFF1 */
+ { 0x0000a8e8, 0x00000b75 }, /* EQ4_BAND1_COEFF2 */
+ { 0x0000a8ec, 0x000000e0 }, /* EQ4_BAND1_PG */
+ { 0x0000a8f0, 0xf1361ec4 }, /* EQ4_BAND2_COEFF1 */
+ { 0x0000a8f4, 0x00000409 }, /* EQ4_BAND2_COEFF2 */
+ { 0x0000a8f8, 0x000004cc }, /* EQ4_BAND2_PG */
+ { 0x0000a8fc, 0xf3371c9b }, /* EQ4_BAND3_COEFF1 */
+ { 0x0000a900, 0x0000040b }, /* EQ4_BAND3_COEFF2 */
+ { 0x0000a904, 0x00000cbb }, /* EQ4_BAND3_PG */
+ { 0x0000a908, 0xf7d916f8 }, /* EQ4_BAND4_COEFF1 */
+ { 0x0000a90c, 0x0000040a }, /* EQ4_BAND4_COEFF2 */
+ { 0x0000a910, 0x00001f14 }, /* EQ4_BAND4_PG */
+ { 0x0000a914, 0x0563058c }, /* EQ4_BAND5_COEFF1 */
+ { 0x0000a918, 0x00000000 }, /* EQ4_BAND5_COEFF1 + 4 */
+ { 0x0000a91c, 0x00004000 }, /* EQ4_BAND5_PG */
+ { 0x0000aa30, 0x00000000 }, /* LHPF_CONTROL1 */
+ { 0x0000aa34, 0x00000000 }, /* LHPF_CONTROL2 */
+ { 0x0000aa38, 0x00000000 }, /* LHPF1_COEFF */
+ { 0x0000aa3c, 0x00000000 }, /* LHPF2_COEFF */
+ { 0x0000aa40, 0x00000000 }, /* LHPF3_COEFF */
+ { 0x0000aa44, 0x00000000 }, /* LHPF4_COEFF */
+ { 0x0000ab00, 0x00000000 }, /* DRC1_CONTROL1 */
+ { 0x0000ab04, 0x49130018 }, /* DRC1_CONTROL2 */
+ { 0x0000ab08, 0x00000018 }, /* DRC1_CONTROL3 */
+ { 0x0000ab0c, 0x00000000 }, /* DRC1_CONTROL4 */
+ { 0x0000ab14, 0x00000000 }, /* DRC2_CONTROL1 */
+ { 0x0000ab18, 0x49130018 }, /* DRC2_CONTROL2 */
+ { 0x0000ab1c, 0x00000018 }, /* DRC2_CONTROL3 */
+ { 0x0000ab20, 0x00000000 }, /* DRC2_CONTROL4 */
+ { 0x0000b000, 0x00000000 }, /* TONE_GENERATOR1 */
+ { 0x0000b004, 0x00100000 }, /* TONE_GENERATOR2 */
+ { 0x0000b400, 0x00000000 }, /* COMFORT_NOISE_GENERATOR */
+ { 0x0000b800, 0x00000000 }, /* US_CONTROL */
+ { 0x0000b804, 0x00002020 }, /* US1_CONTROL */
+ { 0x0000b808, 0x00000000 }, /* US1_DET_CONTROL */
+ { 0x0000b814, 0x00002020 }, /* US2_CONTROL */
+ { 0x0000b818, 0x00000000 }, /* US2_DET_CONTROL */
+ { 0x00018110, 0x00000700 }, /* IRQ1_MASK_1 */
+ { 0x00018114, 0x00000004 }, /* IRQ1_MASK_2 */
+ { 0x00018120, 0x03ff0000 }, /* IRQ1_MASK_5 */
+ { 0x00018124, 0x00000103 }, /* IRQ1_MASK_6 */
+ { 0x00018128, 0x003f0000 }, /* IRQ1_MASK_7 */
+ { 0x00018130, 0xff00000f }, /* IRQ1_MASK_9 */
+ { 0x00018138, 0xffff0000 }, /* IRQ1_MASK_11 */
+};
+
+static bool cs48l32_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_GPIO1_CTRL1 ... CS48L32_GPIO16_CTRL1:
+ case CS48L32_OUTPUT_SYS_CLK:
+ case CS48L32_AUXPDM_CTRL:
+ case CS48L32_AUXPDM_CTRL2:
+ case CS48L32_CLOCK32K:
+ case CS48L32_SYSTEM_CLOCK1 ... CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_SAMPLE_RATE1 ... CS48L32_SAMPLE_RATE4:
+ case CS48L32_FLL1_CONTROL1 ... CS48L32_FLL1_GPIO_CLOCK:
+ case CS48L32_CHARGE_PUMP1:
+ case CS48L32_LDO2_CTRL1:
+ case CS48L32_MICBIAS_CTRL1:
+ case CS48L32_MICBIAS_CTRL5:
+ case CS48L32_IRQ1_CTRL_AOD:
+ case CS48L32_INPUT_CONTROL:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_RATE_CONTROL:
+ case CS48L32_INPUT_CONTROL2:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_INPUT1_CONTROL1:
+ case CS48L32_IN1L_CONTROL1 ... CS48L32_IN1L_CONTROL2:
+ case CS48L32_IN1R_CONTROL1 ... CS48L32_IN1R_CONTROL2:
+ case CS48L32_INPUT2_CONTROL1:
+ case CS48L32_IN2L_CONTROL1 ... CS48L32_IN2L_CONTROL2:
+ case CS48L32_IN2R_CONTROL1 ... CS48L32_IN2R_CONTROL2:
+ case CS48L32_INPUT_HPF_CONTROL:
+ case CS48L32_INPUT_VOL_CONTROL:
+ case CS48L32_AUXPDM_CONTROL1:
+ case CS48L32_AUXPDM_CONTROL2:
+ case CS48L32_AUXPDM1_CONTROL1:
+ case CS48L32_AUXPDM2_CONTROL1:
+ case CS48L32_ADC1L_ANA_CONTROL1:
+ case CS48L32_ADC1R_ANA_CONTROL1:
+ case CS48L32_ASP1_ENABLES1 ... CS48L32_ASP1_DATA_CONTROL5:
+ case CS48L32_ASP2_ENABLES1 ... CS48L32_ASP2_DATA_CONTROL5:
+ case CS48L32_ASP1TX1_INPUT1 ... CS48L32_ASP1TX8_INPUT4:
+ case CS48L32_ASP2TX1_INPUT1 ... CS48L32_ASP2TX4_INPUT4:
+ case CS48L32_ISRC1INT1_INPUT1 ... CS48L32_ISRC1DEC4_INPUT1:
+ case CS48L32_ISRC2INT1_INPUT1 ... CS48L32_ISRC2DEC2_INPUT1:
+ case CS48L32_ISRC3INT1_INPUT1 ... CS48L32_ISRC3DEC2_INPUT1:
+ case CS48L32_EQ1_INPUT1 ... CS48L32_EQ4_INPUT4:
+ case CS48L32_DRC1L_INPUT1 ... CS48L32_DRC1R_INPUT4:
+ case CS48L32_DRC2L_INPUT1 ... CS48L32_DRC2R_INPUT4:
+ case CS48L32_LHPF1_INPUT1 ... CS48L32_LHPF1_INPUT4:
+ case CS48L32_LHPF2_INPUT1 ... CS48L32_LHPF2_INPUT4:
+ case CS48L32_LHPF3_INPUT1 ... CS48L32_LHPF3_INPUT4:
+ case CS48L32_LHPF4_INPUT1 ... CS48L32_LHPF4_INPUT4:
+ case CS48L32_DSP1RX1_INPUT1 ... CS48L32_DSP1RX8_INPUT4:
+ case CS48L32_ISRC1_CONTROL1 ... CS48L32_ISRC1_CONTROL2:
+ case CS48L32_ISRC2_CONTROL1 ... CS48L32_ISRC2_CONTROL2:
+ case CS48L32_ISRC3_CONTROL1 ... CS48L32_ISRC3_CONTROL2:
+ case CS48L32_FX_SAMPLE_RATE:
+ case CS48L32_EQ_CONTROL1 ... CS48L32_EQ_CONTROL2:
+ case CS48L32_EQ1_GAIN1 ... CS48L32_EQ1_BAND5_PG:
+ case CS48L32_EQ2_GAIN1 ... CS48L32_EQ2_BAND5_PG:
+ case CS48L32_EQ3_GAIN1 ... CS48L32_EQ3_BAND5_PG:
+ case CS48L32_EQ4_GAIN1 ... CS48L32_EQ4_BAND5_PG:
+ case CS48L32_LHPF_CONTROL1 ... CS48L32_LHPF_CONTROL2:
+ case CS48L32_LHPF1_COEFF ... CS48L32_LHPF4_COEFF:
+ case CS48L32_DRC1_CONTROL1 ... CS48L32_DRC1_CONTROL4:
+ case CS48L32_DRC2_CONTROL1 ... CS48L32_DRC2_CONTROL4:
+ case CS48L32_TONE_GENERATOR1 ... CS48L32_TONE_GENERATOR2:
+ case CS48L32_COMFORT_NOISE_GENERATOR:
+ case CS48L32_US_CONTROL:
+ case CS48L32_US1_CONTROL:
+ case CS48L32_US1_DET_CONTROL:
+ case CS48L32_US2_CONTROL:
+ case CS48L32_US2_DET_CONTROL:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_IRQ1_MASK_1 ... CS48L32_IRQ1_MASK_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs48l32_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_FLL1_CONTROL5:
+ case CS48L32_FLL1_CONTROL6:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * The bus bridge requires DSP packed memory registers to be accessed in
+ * aligned block multiples.
+ * Mark precious to prevent regmap debugfs causing an illegal bus transaction.
+ */
+static bool cs48l32_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs48l32_regmap = {
+ .name = "cs48l32",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .pad_bits = 32,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS48L32_DSP1_PMEM_LAST,
+ .readable_reg = &cs48l32_readable_register,
+ .volatile_reg = &cs48l32_volatile_register,
+ .precious_reg = &cs48l32_precious_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs48l32_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs48l32_reg_default),
+};
+
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32)
+{
+ cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap);
+
+ return PTR_ERR_OR_ZERO(cs48l32->regmap);
+}
diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c
new file mode 100644
index 000000000000..086ed0f57a85
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.c
@@ -0,0 +1,4076 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <dt-bindings/sound/cs48l32.h>
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gcd.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string_choices.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs48l32.h"
+
+static const char * const cs48l32_core_supplies[] = { "vdd-a", "vdd-io" };
+
+static const struct cs_dsp_region cs48l32_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
+ { .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2800000 },
+ { .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3400000 },
+};
+
+static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = {
+ { CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 },
+ { CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 },
+ { CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 },
+};
+
+static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = {
+ CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0,
+};
+
+static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = {
+ .ext = cs48l32_dsp1_sram_ext_regs,
+ .n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs),
+ .pwd = cs48l32_dsp1_sram_pwd_regs,
+ .n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs),
+};
+
+static const char * const cs48l32_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "ASP1RX1",
+ "ASP1RX2",
+ "ASP1RX3",
+ "ASP1RX4",
+ "ASP1RX5",
+ "ASP1RX6",
+ "ASP1RX7",
+ "ASP1RX8",
+ "ASP2RX1",
+ "ASP2RX2",
+ "ASP2RX3",
+ "ASP2RX4",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "Ultrasonic 1",
+ "Ultrasonic 2",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP1.7",
+ "DSP1.8",
+};
+
+static unsigned int cs48l32_mixer_values[] = {
+ 0x000, /* Silence (mute) */
+ 0x004, /* Tone generator 1 */
+ 0x005, /* Tone generator 2 */
+ 0x00C, /* Noise Generator */
+ 0x010, /* IN1L signal path */
+ 0x011, /* IN1R signal path */
+ 0x012, /* IN2L signal path */
+ 0x013, /* IN2R signal path */
+ 0x020, /* ASP1 RX1 */
+ 0x021, /* ASP1 RX2 */
+ 0x022, /* ASP1 RX3 */
+ 0x023, /* ASP1 RX4 */
+ 0x024, /* ASP1 RX5 */
+ 0x025, /* ASP1 RX6 */
+ 0x026, /* ASP1 RX7 */
+ 0x027, /* ASP1 RX8 */
+ 0x030, /* ASP2 RX1 */
+ 0x031, /* ASP2 RX2 */
+ 0x032, /* ASP2 RX3 */
+ 0x033, /* ASP2 RX4 */
+ 0x098, /* ISRC1 INT1 */
+ 0x099, /* ISRC1 INT2 */
+ 0x09a, /* ISRC1 INT3 */
+ 0x09b, /* ISRC1 INT4 */
+ 0x09C, /* ISRC1 DEC1 */
+ 0x09D, /* ISRC1 DEC2 */
+ 0x09e, /* ISRC1 DEC3 */
+ 0x09f, /* ISRC1 DEC4 */
+ 0x0A0, /* ISRC2 INT1 */
+ 0x0A1, /* ISRC2 INT2 */
+ 0x0A4, /* ISRC2 DEC1 */
+ 0x0A5, /* ISRC2 DEC2 */
+ 0x0A8, /* ISRC3 INT1 */
+ 0x0A9, /* ISRC3 INT2 */
+ 0x0AC, /* ISRC3 DEC1 */
+ 0x0AD, /* ISRC3 DEC2 */
+ 0x0B8, /* EQ1 */
+ 0x0B9, /* EQ2 */
+ 0x0BA, /* EQ3 */
+ 0x0BB, /* EQ4 */
+ 0x0C0, /* DRC1 Left */
+ 0x0C1, /* DRC1 Right */
+ 0x0C2, /* DRC2 Left */
+ 0x0C3, /* DRC2 Right */
+ 0x0C8, /* LHPF1 */
+ 0x0C9, /* LHPF2 */
+ 0x0CA, /* LHPF3 */
+ 0x0CB, /* LHPF4 */
+ 0x0D8, /* Ultrasonic 1 */
+ 0x0D9, /* Ultrasonic 2 */
+ 0x100, /* DSP1 channel 1 */
+ 0x101, /* DSP1 channel 2 */
+ 0x102, /* DSP1 channel 3 */
+ 0x103, /* DSP1 channel 4 */
+ 0x104, /* DSP1 channel 5 */
+ 0x105, /* DSP1 channel 6 */
+ 0x106, /* DSP1 channel 7 */
+ 0x107, /* DSP1 channel 8 */
+};
+static_assert(ARRAY_SIZE(cs48l32_mixer_texts) == ARRAY_SIZE(cs48l32_mixer_values));
+#define CS48L32_NUM_MIXER_INPUTS ARRAY_SIZE(cs48l32_mixer_values)
+
+static const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0);
+
+static void cs48l32_spin_sysclk(struct cs48l32_codec *cs48l32_codec)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret, i;
+
+ /* Skip this if the chip is down */
+ if (pm_runtime_suspended(cs48l32->dev))
+ return;
+
+ /*
+ * Just read a register a few times to ensure the internal
+ * oscillator sends out some clocks.
+ */
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &val);
+ if (ret)
+ dev_err(cs48l32_codec->core.dev, "%s Failed to read register: %d (%d)\n",
+ __func__, ret, i);
+ }
+
+ udelay(300);
+}
+
+static const char * const cs48l32_rate_text[] = {
+ "Sample Rate 1", "Sample Rate 2", "Sample Rate 3", "Sample Rate 4",
+};
+
+static const unsigned int cs48l32_rate_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+static_assert(ARRAY_SIZE(cs48l32_rate_val) == ARRAY_SIZE(cs48l32_rate_text));
+
+static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Prevent any mixer mux changes while we do this */
+ mutex_lock(&cs48l32_codec->rate_lock);
+
+ /* The write must be guarded by a number of SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ mutex_unlock(&cs48l32_codec->rate_lock);
+
+ return ret;
+}
+
+static const char * const cs48l32_sample_rate_text[] = {
+ "12kHz",
+ "24kHz",
+ "48kHz",
+ "96kHz",
+ "192kHz",
+ "384kHz",
+ "768kHz",
+ "11.025kHz",
+ "22.05kHz",
+ "44.1kHz",
+ "88.2kHz",
+ "176.4kHz",
+ "352.8kHz",
+ "705.6kHz",
+ "8kHz",
+ "16kHz",
+ "32kHz",
+};
+
+static const unsigned int cs48l32_sample_rate_val[] = {
+ 0x01, /* 12kHz */
+ 0x02, /* 24kHz */
+ 0x03, /* 48kHz */
+ 0x04, /* 96kHz */
+ 0x05, /* 192kHz */
+ 0x06, /* 384kHz */
+ 0x07, /* 768kHz */
+ 0x09, /* 11.025kHz */
+ 0x0a, /* 22.05kHz */
+ 0x0b, /* 44.1kHz */
+ 0x0c, /* 88.2kHz */
+ 0x0d, /* 176.4kHz */
+ 0x0e, /* 352.8kHz */
+ 0x0f, /* 705.6kHz */
+ 0x11, /* 8kHz */
+ 0x12, /* 16kHz */
+ 0x13, /* 32kHz */
+};
+static_assert(ARRAY_SIZE(cs48l32_sample_rate_val) == ARRAY_SIZE(cs48l32_sample_rate_text));
+#define CS48L32_SAMPLE_RATE_ENUM_SIZE ARRAY_SIZE(cs48l32_sample_rate_val)
+
+static const struct soc_enum cs48l32_sample_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+};
+
+static int cs48l32_inmux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mux, src_val, in_type;
+ int ret;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ switch (e->reg) {
+ case CS48L32_IN1L_CONTROL1:
+ in_type = cs48l32_codec->in_type[0][mux];
+ break;
+ case CS48L32_IN1R_CONTROL1:
+ in_type = cs48l32_codec->in_type[1][mux];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ src_val = mux << e->shift_l;
+
+ if (in_type == CS48L32_IN_TYPE_SE)
+ src_val |= 1 << CS48L32_INx_SRC_SHIFT;
+
+ ret = snd_soc_component_update_bits(component,
+ e->reg,
+ CS48L32_INx_SRC_MASK,
+ src_val);
+ if (ret > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return ret;
+}
+
+static const char * const cs48l32_inmux_texts[] = {
+ "Analog 1", "Analog 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static const struct snd_kcontrol_new cs48l32_inmux[] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+};
+
+static const char * const cs48l32_dmode_texts[] = {
+ "Analog", "Digital",
+};
+
+static int cs48l32_dmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mode;
+ int ret, result;
+
+ mode = ucontrol->value.enumerated.item[0];
+ switch (mode) {
+ case 0:
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ result = snd_soc_component_update_bits(component,
+ e->reg,
+ BIT(CS48L32_IN1_MODE_SHIFT),
+ 0);
+ if (result < 0) {
+ dev_err(component->dev, "Failed to set input mode: %d\n", result);
+ return result;
+ }
+
+ usleep_range(200, 300);
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ if (result > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL);
+
+ return result;
+ case 1:
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ default:
+ return -EINVAL;
+ }
+}
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum,
+ CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_MODE_SHIFT,
+ cs48l32_dmode_texts);
+
+static const struct snd_kcontrol_new cs48l32_dmode_mux[] = {
+ SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_dmode_put),
+};
+
+static const char * const cs48l32_in_texts[] = {
+ "IN1L", "IN1R", "IN2L", "IN2R",
+};
+static_assert(ARRAY_SIZE(cs48l32_in_texts) == CS48L32_MAX_INPUT);
+
+static const char * const cs48l32_us_freq_texts[] = {
+ "16-24kHz", "20-28kHz",
+};
+
+static const unsigned int cs48l32_us_freq_val[] = {
+ 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_freq[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+};
+
+static const unsigned int cs48l32_us_in_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_inmux[] = {
+ SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]),
+ SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]),
+};
+
+static const char * const cs48l32_us_det_thr_texts[] = {
+ "-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB",
+};
+
+static const struct soc_enum cs48l32_us_det_thr[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+};
+
+static const char * const cs48l32_us_det_num_texts[] = {
+ "1 Sample",
+ "2 Samples",
+ "4 Samples",
+ "8 Samples",
+ "16 Samples",
+ "32 Samples",
+ "64 Samples",
+ "128 Samples",
+ "256 Samples",
+ "512 Samples",
+ "1024 Samples",
+ "2048 Samples",
+ "4096 Samples",
+ "8192 Samples",
+ "16384 Samples",
+ "32768 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_num[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+};
+
+static const char * const cs48l32_us_det_hold_texts[] = {
+ "0 Samples",
+ "31 Samples",
+ "63 Samples",
+ "127 Samples",
+ "255 Samples",
+ "511 Samples",
+ "1023 Samples",
+ "2047 Samples",
+ "4095 Samples",
+ "8191 Samples",
+ "16383 Samples",
+ "32767 Samples",
+ "65535 Samples",
+ "131071 Samples",
+ "262143 Samples",
+ "524287 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_hold[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+};
+
+static const struct soc_enum cs48l32_us_output_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const char * const cs48l32_us_det_lpf_cut_texts[] = {
+ "1722Hz", "833Hz", "408Hz", "203Hz",
+};
+
+static const struct soc_enum cs48l32_us_det_lpf_cut[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+};
+
+static const char * const cs48l32_us_det_dcy_texts[] = {
+ "0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", "25.34 ms", "50.69 ms",
+};
+
+static const struct soc_enum cs48l32_us_det_dcy[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const char * const cs48l32_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "16ms/6dB", "32ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VD_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VI_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static const char * const cs48l32_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum,
+ CS48L32_INPUT_HPF_CONTROL,
+ CS48L32_IN_HPF_CUT_SHIFT,
+ cs48l32_in_hpf_cut_text);
+
+static const char * const cs48l32_in_dmic_osr_text[] = {
+ "384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", "6.144MHz",
+};
+
+static const struct soc_enum cs48l32_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+};
+
+static bool cs48l32_is_input_enabled(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int input_active;
+
+ input_active = snd_soc_component_read(component, CS48L32_INPUT_CONTROL);
+ switch (reg) {
+ case CS48L32_IN1L_CONTROL1:
+ return input_active & BIT(CS48L32_IN1L_EN_SHIFT);
+ case CS48L32_IN1R_CONTROL1:
+ return input_active & BIT(CS48L32_IN1R_EN_SHIFT);
+ case CS48L32_IN2L_CONTROL1:
+ return input_active & BIT(CS48L32_IN2L_EN_SHIFT);
+ case CS48L32_IN2R_CONTROL1:
+ return input_active & BIT(CS48L32_IN2R_EN_SHIFT);
+ default:
+ return false;
+ }
+}
+
+static int cs48l32_in_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, e->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const struct soc_enum cs48l32_input_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static int cs48l32_low_power_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, mc->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static const struct soc_enum noise_gen_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_RATE_SHIFT,
+ CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_auxpdm_freq_texts[] = {
+ "3.072MHz", "2.048MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq,
+ CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq,
+ CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static const char * const cs48l32_auxpdm_src_texts[] = {
+ "Analog", "IN1 Digital", "IN2 Digital",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT1_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT2_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in),
+ SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in),
+};
+
+static const unsigned int cs48l32_auxpdm_analog_in_val[] = {
+ 0x0, 0x1,
+};
+
+static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]),
+ SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct soc_enum cs48l32_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_fx_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE,
+ CS48L32_FX_RATE_SHIFT,
+ CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+static const struct soc_enum cs48l32_lhpf_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+};
+
+static int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ __be32 *data = (__be32 *)ucontrol->value.bytes.data;
+ s16 val = (s16)be32_to_cpu(*data);
+
+ if (abs(val) > CS48L32_LHPF_MAX_COEFF) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+
+static const char * const cs48l32_eq_mode_text[] = {
+ "Low-pass", "High-pass",
+};
+
+static const struct soc_enum cs48l32_eq_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+};
+
+static int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int item;
+
+ item = snd_soc_enum_val_to_item(e, cs48l32_codec->eq_mode[e->shift_l]);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_eq_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val;
+ bool changed = false;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->eq_mode[e->shift_l] != val) {
+ cs48l32_codec->eq_mode[e->shift_l] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = ctl->max;
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* High __be16 is in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]);
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* Put high __be16 in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int cached_rate;
+ const unsigned int rate_num = e->mask;
+ int item;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ cached_rate = cs48l32_codec->dsp_dma_rates[rate_num];
+ item = snd_soc_enum_val_to_item(e, cached_rate);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_dsp_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ const unsigned int rate_num = e->mask;
+ const unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ bool changed = false;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->dsp_dma_rates[rate_num] != val) {
+ cs48l32_codec->dsp_dma_rates[rate_num] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static const struct soc_enum cs48l32_dsp_rate_enum[] = {
+ /* RX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 0,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 1,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 2,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 3,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 4,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 5,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 6,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 7,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ /* TX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 8,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 9,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 10,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 11,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 12,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 13,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 14,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 15,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+};
+
+static int cs48l32_dsp_pre_run(struct wm_adsp *dsp)
+{
+ struct cs48l32_codec *cs48l32_codec = container_of(dsp, struct cs48l32_codec, dsp);
+ unsigned int reg;
+ const u8 *rate = cs48l32_codec->dsp_dma_rates;
+ int i;
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1;
+ for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1;
+ for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ usleep_range(300, 600);
+
+ return 0;
+}
+
+static void cs48l32_dsp_memory_disable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return;
+
+err:
+ dev_warn(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+}
+
+static int cs48l32_dsp_memory_enable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ /* disable power-off */
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0x3);
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* power-up the banks in sequence */
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0x1);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+
+ ret = regmap_write(regmap, regs->pwd[i], 0x3);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+ }
+
+ return 0;
+
+err:
+ dev_err(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+ cs48l32_dsp_memory_disable(cs48l32_codec, regs);
+
+ return ret;
+}
+
+static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg,
+ unsigned int freqsel_reg)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ struct wm_adsp *dsp = &cs48l32_codec->dsp;
+ int ret;
+ unsigned int freq, freq_sel, freq_sts;
+
+ if (!freq_reg)
+ return -EINVAL;
+
+ ret = regmap_read(regmap, freq_reg, &freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freq_reg, ret);
+ return ret;
+ }
+
+ if (freqsel_reg) {
+ freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT;
+
+ ret = regmap_read(regmap, freqsel_reg, &freq_sel);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freqsel_reg, ret);
+ return ret;
+ }
+ freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT;
+
+ if (freq_sts != freq_sel) {
+ dev_err(component->dev, "SYSCLK FREQ (#%x) != FREQ STS (#%x)\n",
+ freq_sel, freq_sts);
+ return -ETIMEDOUT;
+ }
+ }
+
+ freq &= CS48L32_DSP_CLK_FREQ_MASK;
+ freq >>= CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1);
+ default:
+ return 0;
+ }
+}
+
+static irqreturn_t cs48l32_irq(int irq, void *data)
+{
+ static const unsigned int eint1_regs[] = {
+ CS48L32_IRQ1_EINT_9, CS48L32_IRQ1_MASK_9,
+ CS48L32_IRQ1_EINT_7, CS48L32_IRQ1_MASK_7
+ };
+ u32 reg_vals[4];
+ struct cs48l32_codec *cs48l32_codec = data;
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ irqreturn_t result = IRQ_NONE;
+ unsigned int eint_pending;
+ int i, ret;
+
+ static_assert(ARRAY_SIZE(eint1_regs) == ARRAY_SIZE(reg_vals));
+
+ ret = pm_runtime_resume_and_get(cs48l32_codec->core.dev);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "irq could not get pm runtime: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(regmap, CS48L32_IRQ1_STATUS, &eint_pending);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ1_STATUS failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+ if ((eint_pending & CS48L32_IRQ1_STS_MASK) == 0)
+ goto out;
+
+ ret = regmap_multi_reg_read(regmap, eint1_regs, reg_vals, ARRAY_SIZE(reg_vals));
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ regs failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg_vals); i += 2) {
+ reg_vals[i] &= ~reg_vals[i + 1];
+ regmap_write(regmap, eint1_regs[i], reg_vals[i]);
+ }
+
+ if (reg_vals[0] & CS48L32_DSP1_IRQ0_EINT1_MASK)
+ wm_adsp_compr_handle_irq(&cs48l32_codec->dsp);
+
+ if (reg_vals[2] & CS48L32_DSP1_MPU_ERR_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "MPU err IRQ\n");
+ wm_halo_bus_error(irq, &cs48l32_codec->dsp);
+ }
+
+ if (reg_vals[2] & CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "WDT expire IRQ\n");
+ wm_halo_wdt_expire(irq, &cs48l32_codec->dsp);
+ }
+
+ result = IRQ_HANDLED;
+
+out:
+ pm_runtime_put_autosuspend(cs48l32_codec->core.dev);
+
+ return result;
+}
+
+static int cs48l32_get_dspclk_setting(struct cs48l32_codec *cs48l32_codec, unsigned int freq,
+ int src, unsigned int *val)
+{
+ freq /= 15625; /* convert to 1/64ths of 1MHz */
+ *val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ return 0;
+}
+
+static int cs48l32_get_sysclk_setting(unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ case 5644800:
+ case 6144000:
+ return CS48L32_SYSCLK_RATE_6MHZ;
+ case 11289600:
+ case 12288000:
+ return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 22579200:
+ case 24576000:
+ return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 45158400:
+ case 49152000:
+ return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 90316800:
+ case 98304000:
+ return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val;
+
+ switch (source) {
+ case CS48L32_PDMCLK_SRC_IN1_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN2_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN3_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN4_PDMCLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM1_CLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM2_CLK:
+ val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT;
+ break;
+ default:
+ dev_err(cs48l32_codec->core.dev, "Invalid PDM FLLCLK src %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS48L32_INPUT_CONTROL2,
+ CS48L32_PDM_FLLCLK_SRC_MASK, val);
+}
+
+static int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ char *name;
+ unsigned int reg;
+ unsigned int mask = CS48L32_SYSCLK_SRC_MASK;
+ unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT;
+ int clk_freq_sel, *clk;
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ name = "SYSCLK";
+ reg = CS48L32_SYSTEM_CLOCK1;
+ clk = &cs48l32_codec->sysclk;
+ clk_freq_sel = cs48l32_get_sysclk_setting(freq);
+ mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK;
+ break;
+ case CS48L32_CLK_DSPCLK:
+ name = "DSPCLK";
+ reg = CS48L32_DSP_CLOCK1;
+ clk = &cs48l32_codec->dspclk;
+ clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32_codec, freq, source, &val);
+ mask |= CS48L32_DSP_CLK_FREQ_MASK;
+ break;
+ case CS48L32_CLK_PDM_FLLCLK:
+ return cs48l32_set_pdm_fllclk(component, source);
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_freq_sel < 0) {
+ dev_err(cs48l32_codec->core.dev, "Failed to get %s setting for %dHZ\n", name, freq);
+ return clk_freq_sel;
+ }
+
+ *clk = freq;
+
+ if (freq == 0) {
+ dev_dbg(cs48l32_codec->core.dev, "%s cleared\n", name);
+ return 0;
+ }
+
+ val |= clk_freq_sel;
+
+ if (freq % 6144000)
+ val |= CS48L32_SYSCLK_FRAC_MASK;
+
+ dev_dbg(cs48l32_codec->core.dev, "%s set to %uHz", name, freq);
+
+ return regmap_update_bits(regmap, reg, mask, val);
+}
+
+static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, base + CS48L32_FLL_CONTROL1_OFFS, &reg);
+ if (ret != 0) {
+ cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret);
+ return ret;
+ }
+
+ return reg & CS48L32_FLL_EN_MASK;
+}
+
+static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int val = 0;
+ int i;
+
+ cs48l32_fll_dbg(fll, "Waiting for FLL...\n");
+
+ for (i = 0; i < 30; i++) {
+ regmap_read(regmap, fll->sts_addr, &val);
+ if (!!(val & fll->sts_mask) == requested)
+ return 0;
+
+ switch (i) {
+ case 0 ... 5:
+ usleep_range(75, 125);
+ break;
+ case 6 ... 20:
+ usleep_range(750, 1250);
+ break;
+ default:
+ fsleep(20000);
+ break;
+ }
+ }
+
+ cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock");
+
+ return -ETIMEDOUT;
+}
+
+static int cs48l32_fllhj_disable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ bool change;
+
+ cs48l32_fll_dbg(fll, "Disabling FLL\n");
+
+ /*
+ * Disable lockdet, but don't set ctrl_upd update bit. This allows the
+ * lock status bit to clear as normal, but should the FLL be enabled
+ * again due to a control clock being required, the lock won't re-assert
+ * as the FLL config registers are automatically applied when the FLL
+ * enables.
+ */
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL5_OFFS,
+ CS48L32_FLL_FRC_INTEG_UPD_MASK);
+ regmap_update_bits_check(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK,
+ 0,
+ &change);
+
+ cs48l32_wait_for_fll(fll, false);
+
+ /*
+ * ctrl_up gates the writes to all the fll's registers, setting it to 0
+ * here ensures that after a runtime suspend/resume cycle when one
+ * enables the fll then ctrl_up is the last bit that is configured
+ * by the fll enable code rather than the cache sync operation which
+ * would have updated it much earlier before writing out all fll
+ * registers
+ */
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ if (change)
+ pm_runtime_put_autosuspend(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd;
+ bool frac = false;
+ unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp;
+ unsigned int gains, num;
+
+ cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
+
+ for (refdiv = 0; refdiv < 4; refdiv++) {
+ if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH)
+ break;
+ }
+
+ fref = fin / (1 << refdiv);
+ fout = fll->fout;
+ frac = fout % fref;
+
+ /*
+ * Use simple heuristic approach to find a configuration that
+ * should work for most input clocks.
+ */
+ if (fref < CS48L32_FLLHJ_LOW_THRESH) {
+ lockdet_thr = 2;
+ gains = CS48L32_FLLHJ_LOW_GAINS;
+
+ if (frac)
+ fbdiv = 256;
+ else
+ fbdiv = 4;
+ } else if (fref < CS48L32_FLLHJ_MID_THRESH) {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_MID_GAINS;
+ fbdiv = (frac) ? 16 : 2;
+ } else {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_HIGH_GAINS;
+ fbdiv = 1;
+ }
+ /* Use high performance mode for fractional configurations. */
+ if (frac) {
+ hp = 3;
+ min_n = CS48L32_FLLHJ_FRAC_MIN_N;
+ max_n = CS48L32_FLLHJ_FRAC_MAX_N;
+ } else {
+ if (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH)
+ hp = 0;
+ else
+ hp = 1;
+
+ min_n = CS48L32_FLLHJ_INT_MIN_N;
+ max_n = CS48L32_FLLHJ_INT_MAX_N;
+ }
+
+ ratio = fout / fref;
+
+ cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac);
+
+ while (ratio / fbdiv < min_n) {
+ fbdiv /= 2;
+ if (fbdiv < min_n) {
+ cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n);
+ return -EINVAL;
+ }
+ }
+ while (frac && (ratio / fbdiv > max_n)) {
+ fbdiv *= 2;
+ if (fbdiv >= 1024) {
+ cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+
+ cs48l32_fll_dbg(fll, "lockdet=%d, hp=#%x, fbdiv:%d\n", lockdet_thr, hp, fbdiv);
+
+ /* Calculate N.K values */
+ fllgcd = gcd(fout, fbdiv * fref);
+ num = fout / fllgcd;
+ lambda = (fref * fbdiv) / fllgcd;
+ fll_n = num / lambda;
+ theta = num % lambda;
+
+ cs48l32_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
+ fll_n, fllgcd, theta, lambda);
+
+ /* Some sanity checks before any registers are written. */
+ if (fll_n < min_n || fll_n > max_n) {
+ cs48l32_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
+ frac ? "fractional" : "integer", min_n, max_n, fll_n);
+ return -EINVAL;
+ }
+ if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
+ cs48l32_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
+ frac ? "fractional" : "integer", fbdiv);
+ return -EINVAL;
+ }
+
+ /* clear the ctrl_upd bit to guarantee we write to it later. */
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_THR_MASK |
+ CS48L32_FLL_PHASEDET_MASK |
+ CS48L32_FLL_REFCLK_DIV_MASK |
+ CS48L32_FLL_N_MASK |
+ CS48L32_FLL_CTRL_UPD_MASK,
+ (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) |
+ (1 << CS48L32_FLL_PHASEDET_SHIFT) |
+ (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) |
+ (fll_n << CS48L32_FLL_N_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL3_OFFS,
+ CS48L32_FLL_LAMBDA_MASK |
+ CS48L32_FLL_THETA_MASK,
+ (lambda << CS48L32_FLL_LAMBDA_SHIFT) |
+ (theta << CS48L32_FLL_THETA_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL4_OFFS,
+ (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ CS48L32_FLL_HP_MASK |
+ CS48L32_FLL_FB_DIV_MASK,
+ (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ (hp << CS48L32_FLL_HP_SHIFT) |
+ (fbdiv << CS48L32_FLL_FB_DIV_SHIFT));
+
+ return 0;
+}
+
+static int cs48l32_fllhj_enable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ int already_enabled = cs48l32_is_enabled_fll(fll, fll->base);
+ int ret;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(cs48l32->dev);
+
+ cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ /* FLLn_HOLD must be set before configuring any registers */
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ /* Apply refclk */
+ ret = cs48l32_fllhj_apply(fll, fll->ref_freq);
+ if (ret) {
+ cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret);
+ goto out;
+ }
+ regmap_update_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_REFCLK_SRC_MASK,
+ fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK);
+
+out:
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ /* Release the hold so that flln locks to external frequency */
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ if (!already_enabled)
+ cs48l32_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_validate(struct cs48l32_fll *fll,
+ unsigned int ref_in,
+ unsigned int fout)
+{
+ if (fout && !ref_in) {
+ cs48l32_fll_err(fll, "fllout set without valid input clk\n");
+ return -EINVAL;
+ }
+
+ if (fll->fout && fout != fll->fout) {
+ cs48l32_fll_err(fll, "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) {
+ cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
+ return -EINVAL;
+ }
+
+ if (fout > CS48L32_FLL_MAX_FOUT) {
+ cs48l32_fll_err(fll, "Fout=%dMHz exceeds maximum %dMHz\n",
+ fout, CS48L32_FLL_MAX_FOUT);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+
+ if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout)
+ return 0;
+
+ if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout))
+ return -EINVAL;
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = cs48l32_fllhj_enable(fll);
+ else
+ cs48l32_fllhj_disable(fll);
+
+ return ret;
+}
+
+static int cs48l32_init_fll(struct cs48l32_fll *fll)
+{
+ fll->ref_src = CS48L32_FLL_SRC_NONE;
+
+ return 0;
+}
+
+static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case CS48L32_FLL1_REFCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cs48l32_fllhj_set_refclk(&cs48l32_codec->fll, source, fref, fout);
+}
+
+static int cs48l32_asp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int pin_reg, last_pin_reg, hiz_reg;
+
+ switch (dai->id) {
+ case 1:
+ pin_reg = CS48L32_GPIO3_CTRL1;
+ hiz_reg = CS48L32_ASP1_CONTROL3;
+ break;
+ case 2:
+ pin_reg = CS48L32_GPIO7_CTRL1;
+ hiz_reg = CS48L32_ASP2_CONTROL3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (last_pin_reg = pin_reg + 12; pin_reg <= last_pin_reg; ++pin_reg)
+ regmap_clear_bits(regmap, pin_reg, CS48L32_GPIOX_CTRL1_FN_MASK);
+
+ /* DOUT high-impendance when not transmitting */
+ regmap_set_bits(regmap, hiz_reg, CS48L32_ASP_DOUT_HIZ_MASK);
+
+ return 0;
+}
+
+static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val = 0U;
+ unsigned int base = dai->driver->base;
+ unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK |
+ CS48L32_ASP_BCLK_MSTR_MASK |
+ CS48L32_ASP_FSYNC_INV_MASK |
+ CS48L32_ASP_FSYNC_MSTR_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported clock direction %d\n",
+ fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, base + CS48L32_ASP_CONTROL2, mask, val);
+
+ return 0;
+}
+
+static const struct {
+ u32 freq;
+ u32 id;
+} cs48l32_sclk_rates[] = {
+ { 128000, 12 },
+ { 176400, 13 },
+ { 192000, 14 },
+ { 256000, 15 },
+ { 352800, 16 },
+ { 384000, 17 },
+ { 512000, 18 },
+ { 705600, 19 },
+ { 768000, 21 },
+ { 1024000, 23 },
+ { 1411200, 25 },
+ { 1536000, 27 },
+ { 2048000, 29 },
+ { 2822400, 31 },
+ { 3072000, 33 },
+ { 4096000, 36 },
+ { 5644800, 38 },
+ { 6144000, 40 },
+ { 8192000, 47 },
+ { 11289600, 49 },
+ { 12288000, 51 },
+ { 22579200, 57 },
+ { 24576000, 59 },
+};
+
+#define CS48L32_48K_RATE_MASK 0x0e00fe
+#define CS48L32_44K1_RATE_MASK 0x00fe00
+#define CS48L32_RATE_MASK (CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK)
+
+static const unsigned int cs48l32_sr_vals[] = {
+ 0,
+ 12000, /* CS48L32_48K_RATE_MASK */
+ 24000, /* CS48L32_48K_RATE_MASK */
+ 48000, /* CS48L32_48K_RATE_MASK */
+ 96000, /* CS48L32_48K_RATE_MASK */
+ 192000, /* CS48L32_48K_RATE_MASK */
+ 384000, /* CS48L32_48K_RATE_MASK */
+ 768000, /* CS48L32_48K_RATE_MASK */
+ 0,
+ 11025, /* CS48L32_44K1_RATE_MASK */
+ 22050, /* CS48L32_44K1_RATE_MASK */
+ 44100, /* CS48L32_44K1_RATE_MASK */
+ 88200, /* CS48L32_44K1_RATE_MASK */
+ 176400, /* CS48L32_44K1_RATE_MASK */
+ 352800, /* CS48L32_44K1_RATE_MASK */
+ 705600, /* CS48L32_44K1_RATE_MASK */
+ 0,
+ 8000, /* CS48L32_48K_RATE_MASK */
+ 16000, /* CS48L32_48K_RATE_MASK */
+ 32000, /* CS48L32_48K_RATE_MASK */
+};
+
+static const struct snd_pcm_hw_constraint_list cs48l32_constraint = {
+ .count = ARRAY_SIZE(cs48l32_sr_vals),
+ .list = cs48l32_sr_vals,
+};
+
+static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ base_rate = cs48l32_codec->sysclk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (base_rate == 0)
+ dai_priv->constraint.mask = CS48L32_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = CS48L32_48K_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static int cs48l32_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int sr_val, sr_reg, rate;
+
+ rate = params_rate(params);
+ for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++)
+ if (cs48l32_sr_vals[sr_val] == rate)
+ break;
+
+ if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ sr_reg = CS48L32_SAMPLE_RATE1;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ sr_reg = CS48L32_SAMPLE_RATE2;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ sr_reg = CS48L32_SAMPLE_RATE3;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ sr_reg = CS48L32_SAMPLE_RATE4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val);
+
+ return 0;
+}
+
+static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component,
+ unsigned int base, unsigned int sclk,
+ unsigned int slotws, unsigned int dataw)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1);
+ if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1);
+ if (dataw != (val & (CS48L32_ASP_TX_WL_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5);
+ if (dataw != (val & (CS48L32_ASP_RX_WL_MASK)))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int base = dai->driver->base;
+ int dai_id = dai->id - 1;
+ unsigned int rate = params_rate(params);
+ unsigned int dataw = snd_pcm_format_width(params_format(params));
+ unsigned int asp_state = 0;
+ int sclk, sclk_target;
+ unsigned int slotw, n_slots, n_slots_multiple, val;
+ int i, ret;
+
+ cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n",
+ params_channels(params), dataw, rate);
+ /*
+ * The following calculations hold only under the assumption that
+ * symmetric_[rates|channels|samplebits] are set to 1
+ */
+ if (cs48l32_codec->tdm_slots[dai_id]) {
+ n_slots = cs48l32_codec->tdm_slots[dai_id];
+ slotw = cs48l32_codec->tdm_width[dai_id];
+ } else {
+ n_slots = params_channels(params);
+ slotw = dataw;
+ }
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT;
+ if (val == CS48L32_ASP_FMT_I2S_MODE)
+ n_slots_multiple = 2;
+ else
+ n_slots_multiple = 1;
+
+ sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple);
+ if (sclk_target < 0) {
+ cs48l32_asp_err(dai, "Invalid parameters\n");
+ return sclk_target;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) {
+ if ((cs48l32_sclk_rates[i].freq >= sclk_target) &&
+ (cs48l32_sclk_rates[i].freq % rate == 0)) {
+ sclk = cs48l32_sclk_rates[i].id;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(cs48l32_sclk_rates)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n",
+ n_slots, dataw, slotw, sclk_target, sclk);
+
+ slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) |
+ (slotw << CS48L32_ASP_RX_WIDTH_SHIFT);
+
+ if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw))
+ return cs48l32_hw_params_rate(substream, params, dai);
+
+ /* ASP must be disabled while changing configuration */
+ asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1);
+ regmap_clear_bits(regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff);
+
+ ret = cs48l32_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_asp;
+
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_BCLK_FREQ_MASK,
+ sclk);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL2,
+ CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK,
+ slotw);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_DATA_CONTROL1,
+ CS48L32_ASP_TX_WL_MASK,
+ dataw);
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_DATA_CONTROL5,
+ CS48L32_ASP_RX_WL_MASK,
+ dataw);
+
+restore_asp:
+ /* Restore ASP TX/RX enable state */
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_ENABLES1,
+ 0xff00ff,
+ asp_state);
+ return ret;
+}
+
+static const char *cs48l32_dai_clk_str(int clk_id)
+{
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ return "SYSCLK";
+ default:
+ return "Unknown clock";
+ }
+}
+
+static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base = dai->driver->base;
+ unsigned int current_asp_rate, target_asp_rate;
+ bool change_rate_domain = false;
+ int ret;
+
+ if (clk_id == dai_priv->clk)
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ cs48l32_asp_err(dai, "Can't change clock on active DAI\n");
+ return -EBUSY;
+ }
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_priv->clk = clk_id;
+ cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id));
+
+ if (base) {
+ ret = regmap_read(cs48l32_codec->core.regmap,
+ base + CS48L32_ASP_CONTROL1,
+ &current_asp_rate);
+ if (ret != 0) {
+ cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((current_asp_rate & CS48L32_ASP_RATE_MASK) !=
+ (target_asp_rate & CS48L32_ASP_RATE_MASK)) {
+ change_rate_domain = true;
+
+ mutex_lock(&cs48l32_codec->rate_lock);
+ /* Guard the rate change with SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ }
+
+ snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_RATE_MASK, target_asp_rate);
+
+ if (change_rate_domain) {
+ cs48l32_spin_sysclk(cs48l32_codec);
+ mutex_unlock(&cs48l32_codec->rate_lock);
+ }
+ }
+
+ return 0;
+}
+
+static void cs48l32_set_channels_to_mask(struct snd_soc_dai *dai,
+ unsigned int base,
+ int channels, unsigned int mask)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int slot, i, j = 0, shift;
+ unsigned int frame_ctls[2] = {0, 0};
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ if (i - (j * 4) >= 4) {
+ ++j;
+ if (j >= 2)
+ break;
+ }
+
+ shift = (8 * (i - j * 4));
+
+ frame_ctls[j] |= slot << shift;
+
+ mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */
+ }
+
+ regmap_write(regmap, base, frame_ctls[0]);
+ regmap_write(regmap, base + 0x4, frame_ctls[1]);
+
+ if (mask)
+ cs48l32_asp_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int cs48l32_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical ASPs */
+ if (dai->id > CS48L32_MAX_ASP)
+ return -EINVAL;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1,
+ tx_max_chan, tx_mask);
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5,
+ rx_max_chan, rx_mask);
+
+ cs48l32_codec->tdm_width[dai->id - 1] = slot_width;
+ cs48l32_codec->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs48l32_dai_ops = {
+ .probe = &cs48l32_asp_dai_probe,
+ .startup = &cs48l32_startup,
+ .set_fmt = &cs48l32_set_fmt,
+ .set_tdm_slot = &cs48l32_set_tdm_slot,
+ .hw_params = &cs48l32_hw_params,
+ .set_sysclk = &cs48l32_dai_set_sysclk,
+};
+
+static int cs48l32_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ return 0;
+}
+
+static int cs48l32_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = CS48L32_IN1L_CONTROL2;
+ else
+ reg = CS48L32_IN1R_CONTROL2;
+
+ reg += (w->shift / 2) * (CS48L32_IN2L_CONTROL2 - CS48L32_IN1L_CONTROL2);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(200, 300);
+
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending--;
+ snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0);
+
+ /* Uncached write-only register, no need for update_bits */
+ if (!cs48l32_codec->in_up_pending) {
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK);
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Uncached write-only register, no need for update_bits.
+ * Will fail if codec is off but that will be handled by cs48l32_in_ev
+ */
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg, CS48L32_IN_VU);
+
+ return ret;
+}
+
+static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b)
+{
+ s16 a = be16_to_cpu(in_a);
+ s16 b = be16_to_cpu(in_b);
+
+ if (!mode)
+ return abs(a) > CS48L32_EQ_MAX_COEFF;
+
+ if (abs(b) > CS48L32_EQ_MAX_COEFF)
+ return true;
+
+ if (abs((a << 16) / (CS48L32_EQ_MAX_COEFF + 1 - b)) >= ((CS48L32_EQ_MAX_COEFF + 1) << 4))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_eq_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int mode = cs48l32_codec->eq_mode[w->shift];
+ unsigned int reg;
+ __be16 *data = &cs48l32_codec->eq_coefficients[w->shift][0];
+ int ret = 0;
+
+ reg = CS48L32_EQ1_BAND1_COEFF1;
+ reg += w->shift * (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) ||
+ cs48l32_eq_filter_unstable(true, data[7], data[6]) ||
+ cs48l32_eq_filter_unstable(true, data[13], data[12]) ||
+ cs48l32_eq_filter_unstable(true, data[19], data[18]) ||
+ cs48l32_eq_filter_unstable(false, data[25], data[24])) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable EQ coefficients.\n");
+ ret = -EINVAL;
+ } else {
+ ret = regmap_raw_write(regmap, reg, data, CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ coefficients: %d\n", ret);
+ goto out;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_EQ_CONTROL2,
+ w->mask,
+ mode << w->shift);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ mode: %d\n", ret);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs48l32_snd_controls[] = {
+SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp),
+
+CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]),
+CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]),
+
+SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]),
+SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]),
+
+SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+
+SOC_ENUM("Ultrasonic 1 Detect Threshold", cs48l32_us_det_thr[0]),
+SOC_ENUM("Ultrasonic 2 Detect Threshold", cs48l32_us_det_thr[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Pulse Length", cs48l32_us_det_num[0]),
+SOC_ENUM("Ultrasonic 2 Detect Pulse Length", cs48l32_us_det_num[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Hold", cs48l32_us_det_hold[0]),
+SOC_ENUM("Ultrasonic 2 Detect Hold", cs48l32_us_det_hold[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Decay", cs48l32_us_det_dcy[0]),
+SOC_ENUM("Ultrasonic 2 Detect Decay", cs48l32_us_det_dcy[1]),
+
+SOC_SINGLE("Ultrasonic 1 Detect LPF Switch",
+ CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+SOC_SINGLE("Ultrasonic 2 Detect LPF Switch",
+ CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+
+SOC_ENUM("Ultrasonic 1 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]),
+SOC_ENUM("Ultrasonic 2 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]),
+
+CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1),
+
+SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+
+CS48L32_EQ_COEFF_CONTROLS(EQ1),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ3),
+SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ4),
+SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1),
+
+SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+
+CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1),
+
+CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF),
+CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF),
+CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF),
+CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF),
+
+SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]),
+SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]),
+SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]),
+SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]),
+
+CS48L32_RATE_CONTROL("Sample Rate 1", 1),
+CS48L32_RATE_CONTROL("Sample Rate 2", 2),
+CS48L32_RATE_CONTROL("Sample Rate 3", 3),
+CS48L32_RATE_CONTROL("Sample Rate 4", 4),
+
+CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate),
+
+CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]),
+CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]),
+CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]),
+CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]),
+CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]),
+CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]),
+
+SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq),
+SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq),
+
+SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put),
+
+CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate),
+
+SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv),
+
+CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1),
+
+CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+
+CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0),
+CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1),
+CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2),
+CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3),
+CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4),
+CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5),
+CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6),
+CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7),
+CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8),
+CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9),
+CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10),
+CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11),
+CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12),
+CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13),
+CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14),
+CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15),
+};
+
+CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1);
+CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1);
+CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1);
+CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1);
+
+CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1);
+
+CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1);
+
+CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1);
+
+static int cs48l32_dsp_mem_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_memory_enable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ case SND_SOC_DAPM_PRE_PMD:
+ cs48l32_dsp_memory_disable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0,
+ cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-cp", 20, 0),
+
+SND_SOC_DAPM_SUPPLY("VOUT_MIC", CS48L32_CHARGE_PUMP1, CS48L32_CP2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("VOUT_MIC_REGULATED", CS48L32_CHARGE_PUMP1, CS48L32_CP2_BYPASS_SHIFT,
+ 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1LN_1"),
+SND_SOC_DAPM_INPUT("IN1LN_2"),
+SND_SOC_DAPM_INPUT("IN1LP_1"),
+SND_SOC_DAPM_INPUT("IN1LP_2"),
+SND_SOC_DAPM_INPUT("IN1RN_1"),
+SND_SOC_DAPM_INPUT("IN1RN_2"),
+SND_SOC_DAPM_INPUT("IN1RP_1"),
+SND_SOC_DAPM_INPUT("IN1RP_2"),
+SND_SOC_DAPM_INPUT("IN1_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN1_PDMDATA"),
+
+SND_SOC_DAPM_INPUT("IN2_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN2_PDMDATA"),
+
+SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]),
+SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+
+SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0),
+
+SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0),
+
+SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]),
+SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[1]),
+
+SND_SOC_DAPM_SWITCH("Ultrasonic 1 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]),
+SND_SOC_DAPM_SWITCH("Ultrasonic 2 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in CS48L32_MIXER_INPUT_ROUTES
+ */
+SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0),
+
+SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, wm_adsp_early_event),
+
+/* end of ordered widget list */
+
+CS48L32_MIXER_WIDGETS(EQ1, "EQ1"),
+CS48L32_MIXER_WIDGETS(EQ2, "EQ2"),
+CS48L32_MIXER_WIDGETS(EQ3, "EQ3"),
+CS48L32_MIXER_WIDGETS(EQ4, "EQ4"),
+
+CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"),
+CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"),
+CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"),
+CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[1]),
+
+CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"),
+CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"),
+CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"),
+CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"),
+CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"),
+CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"),
+CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"),
+CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"),
+CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"),
+CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"),
+CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"),
+
+CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"),
+CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"),
+CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"),
+CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"),
+
+CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"),
+CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"),
+CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"),
+CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"),
+CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"),
+CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"),
+CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"),
+CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_dsp_trigger_output_mux[0]),
+
+SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+
+SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"),
+};
+
+static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = {
+ { "IN1LN_1", NULL, "SYSCLK" },
+ { "IN1LN_2", NULL, "SYSCLK" },
+ { "IN1LP_1", NULL, "SYSCLK" },
+ { "IN1LP_2", NULL, "SYSCLK" },
+ { "IN1RN_1", NULL, "SYSCLK" },
+ { "IN1RN_2", NULL, "SYSCLK" },
+ { "IN1RP_1", NULL, "SYSCLK" },
+ { "IN1RP_2", NULL, "SYSCLK" },
+
+ { "IN1_PDMCLK", NULL, "SYSCLK" },
+ { "IN1_PDMDATA", NULL, "SYSCLK" },
+ { "IN2_PDMCLK", NULL, "SYSCLK" },
+ { "IN2_PDMDATA", NULL, "SYSCLK" },
+
+ { "DSP1 Preloader", NULL, "DSP1MEM" },
+ { "DSP1", NULL, "DSP1FREQ" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+ { "Voice Ctrl DSP", NULL, "DSP1" },
+
+ { "VOUT_MIC_REGULATED", NULL, "VOUT_MIC" },
+ { "MICBIAS1", NULL, "VOUT_MIC_REGULATED" },
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+ { "Noise Generator", NULL, "SYSCLK" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+ { "Noise Generator", NULL, "NOISE" },
+
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+ { "ASP1 Capture", NULL, "ASP1TX5" },
+ { "ASP1 Capture", NULL, "ASP1TX6" },
+ { "ASP1 Capture", NULL, "ASP1TX7" },
+ { "ASP1 Capture", NULL, "ASP1TX8" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "ASP1RX3", NULL, "ASP1 Playback" },
+ { "ASP1RX4", NULL, "ASP1 Playback" },
+ { "ASP1RX5", NULL, "ASP1 Playback" },
+ { "ASP1RX6", NULL, "ASP1 Playback" },
+ { "ASP1RX7", NULL, "ASP1 Playback" },
+ { "ASP1RX8", NULL, "ASP1 Playback" },
+
+ { "ASP2 Capture", NULL, "ASP2TX1" },
+ { "ASP2 Capture", NULL, "ASP2TX2" },
+ { "ASP2 Capture", NULL, "ASP2TX3" },
+ { "ASP2 Capture", NULL, "ASP2TX4" },
+
+ { "ASP2RX1", NULL, "ASP2 Playback" },
+ { "ASP2RX2", NULL, "ASP2 Playback" },
+ { "ASP2RX3", NULL, "ASP2 Playback" },
+ { "ASP2RX4", NULL, "ASP2 Playback" },
+
+ { "ASP1 Playback", NULL, "SYSCLK" },
+ { "ASP2 Playback", NULL, "SYSCLK" },
+
+ { "ASP1 Capture", NULL, "SYSCLK" },
+ { "ASP2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "Analog 1", "IN1LN_1" },
+ { "IN1L Mux", "Analog 2", "IN1LN_2" },
+ { "IN1L Mux", "Analog 1", "IN1LP_1" },
+ { "IN1L Mux", "Analog 2", "IN1LP_2" },
+ { "IN1R Mux", "Analog 1", "IN1RN_1" },
+ { "IN1R Mux", "Analog 2", "IN1RN_2" },
+ { "IN1R Mux", "Analog 1", "IN1RP_1" },
+ { "IN1R Mux", "Analog 2", "IN1RP_2" },
+
+ { "IN1L PGA", NULL, "IN1L Mode" },
+ { "IN1R PGA", NULL, "IN1R Mode" },
+
+ { "IN1L Mode", "Analog", "IN1L Mux" },
+ { "IN1R Mode", "Analog", "IN1R Mux" },
+
+ { "IN1L Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1L Mode", "Digital", "IN1_PDMDATA" },
+ { "IN1R Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1R Mode", "Digital", "IN1_PDMDATA" },
+
+ { "IN1L PGA", NULL, "VOUT_MIC" },
+ { "IN1R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "VOUT_MIC" },
+ { "IN2R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "IN2_PDMCLK" },
+ { "IN2R PGA", NULL, "IN2_PDMCLK" },
+ { "IN2L PGA", NULL, "IN2_PDMDATA" },
+ { "IN2R PGA", NULL, "IN2_PDMDATA" },
+
+ { "Ultrasonic 1", NULL, "Ultrasonic 1 Input" },
+ { "Ultrasonic 2", NULL, "Ultrasonic 2 Input" },
+
+ { "Ultrasonic 1 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 1 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 1 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 1 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 2 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 2 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 2 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 2 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 1 Detect", "Switch", "Ultrasonic 1 Input" },
+ { "Ultrasonic 2 Detect", "Switch", "Ultrasonic 2 Input" },
+
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Detect" },
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Detect" },
+
+ CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"),
+ CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"),
+ CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"),
+ CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"),
+ CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"),
+ CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"),
+ CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"),
+ CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"),
+
+ CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"),
+ CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"),
+ CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"),
+ CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"),
+
+ CS48L32_MIXER_ROUTES("EQ1", "EQ1"),
+ CS48L32_MIXER_ROUTES("EQ2", "EQ2"),
+ CS48L32_MIXER_ROUTES("EQ3", "EQ3"),
+ CS48L32_MIXER_ROUTES("EQ4", "EQ4"),
+
+ CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"),
+ CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"),
+ CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"),
+ CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"),
+ CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"),
+ CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"),
+ CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+
+ { "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM1 Output", "Switch", "AUXPDM1 Input" },
+ { "AUXPDM1_CLK", NULL, "AUXPDM1 Output" },
+ { "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" },
+
+ { "AUXPDM2 Output", "Switch", "AUXPDM2 Input" },
+ { "AUXPDM2_CLK", NULL, "AUXPDM2 Output" },
+ { "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs48l32_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") &&
+ strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) {
+ dev_err(cs48l32_codec->core.dev, "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&cs48l32_codec->dsp, stream);
+}
+
+static const struct snd_compress_ops cs48l32_compress_ops = {
+ .open = &cs48l32_compr_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_dai_ops cs48l32_compress_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs48l32_dai[] = {
+ {
+ .name = "cs48l32-asp1",
+ .id = 1,
+ .base = CS48L32_ASP1_ENABLES1,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-asp2",
+ .id = 2,
+ .base = CS48L32_ASP2_ENABLES1,
+ .playback = {
+ .stream_name = "ASP2 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-cpu-trace",
+ .id = 3,
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-trace",
+ .id = 4,
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+ {
+ .name = "cs48l32-cpu-voicectrl",
+ .id = 5,
+ .capture = {
+ .stream_name = "Voice Ctrl CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-voicectrl",
+ .id = 6,
+ .capture = {
+ .stream_name = "Voice Ctrl DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+};
+
+static int cs48l32_init_inputs(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int ana_mode_l, ana_mode_r, dig_mode;
+ int i;
+
+ /*
+ * Initialize input modes from the A settings. For muxed inputs the
+ * B settings will be applied if the mux is changed
+ */
+ switch (cs48l32_codec->in_type[0][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_l = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ switch (cs48l32_codec->in_type[1][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_r = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ dev_dbg(cs48l32_codec->core.dev, "IN1_1 Analogue mode=#%x,#%x\n",
+ ana_mode_l, ana_mode_r);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_l);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_r);
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ dig_mode = cs48l32_codec->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT;
+
+ dev_dbg(cs48l32_codec->core.dev, "IN%d PDM_SUP=#%x\n", i + 1, dig_mode);
+
+ regmap_update_bits(regmap,
+ CS48L32_INPUT1_CONTROL1 + (i * 0x40),
+ CS48L32_IN1_PDM_SUP_MASK, dig_mode);
+ }
+
+ return 0;
+}
+
+static int cs48l32_init_dai(struct cs48l32_codec *cs48l32_codec, int id)
+{
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[id];
+
+ dai_priv->clk = CS48L32_CLK_SYSCLK_1;
+ dai_priv->constraint = cs48l32_constraint;
+
+ return 0;
+}
+
+static int cs48l32_init_eq(struct cs48l32_codec *cs48l32_codec)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode;
+ __be16 *data;
+ int i, ret;
+
+ ret = regmap_read(regmap, CS48L32_EQ_CONTROL2, &mode);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev, "Error reading EQ mode: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ cs48l32_codec->eq_mode[i] = (mode >> i) & 0x1;
+
+ data = &cs48l32_codec->eq_coefficients[i][0];
+ ret = regmap_raw_read(regmap, reg + (i * 68), data,
+ CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error reading EQ coefficients: %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int cs48l32_component_probe(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, cs48l32_codec->core.regmap);
+
+ ret = cs48l32_init_inputs(component);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++)
+ cs48l32_init_dai(cs48l32_codec, i);
+
+ ret = cs48l32_init_eq(cs48l32_codec);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs48l32_codec->dsp, component);
+
+ /* Unmask DSP IRQs */
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ return 0;
+}
+
+static void cs48l32_component_remove(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ /* Mask DSP IRQs */
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ wm_adsp2_component_remove(&cs48l32_codec->dsp, component);
+}
+
+static const struct snd_soc_component_driver cs48l32_soc_component_drv = {
+ .probe = &cs48l32_component_probe,
+ .remove = &cs48l32_component_remove,
+ .set_sysclk = &cs48l32_set_sysclk,
+ .set_pll = &cs48l32_set_fll,
+ .name = "cs48l32-codec",
+ .compress_ops = &cs48l32_compress_ops,
+ .controls = cs48l32_snd_controls,
+ .num_controls = ARRAY_SIZE(cs48l32_snd_controls),
+ .dapm_widgets = cs48l32_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs48l32_dapm_widgets),
+ .dapm_routes = cs48l32_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs48l32_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs48l32_prop_read_u32_array(struct cs48l32_codec *cs48l32_codec,
+ const char *propname,
+ u32 *dest,
+ int n_max)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ int ret;
+
+ ret = device_property_read_u32_array(cs48l32->dev, propname, dest, n_max);
+ if (ret == -EINVAL)
+ return -ENOENT;
+
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "%s malformed\n", propname);
+
+ return 0;
+}
+
+static void cs48l32_prop_get_in_type(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,in-type";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT * CS48L32_MAX_IN_MUX_WAYS];
+ int i, in_idx, mux_way_idx, ret;
+
+ static_assert(ARRAY_SIZE(tmp) ==
+ ARRAY_SIZE(cs48l32_codec->in_type) * ARRAY_SIZE(cs48l32_codec->in_type[0]));
+
+ ret = cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+ if (ret < 0)
+ return;
+
+ in_idx = 0;
+ mux_way_idx = 0;
+ for (i = 0; i < ARRAY_SIZE(tmp); ++i) {
+ switch (tmp[i]) {
+ case CS48L32_IN_TYPE_DIFF:
+ case CS48L32_IN_TYPE_SE:
+ cs48l32_codec->in_type[in_idx][mux_way_idx] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, tmp[i]);
+ break;
+ }
+
+ /*
+ * Property array is [mux_way][in_channel]. Swap to
+ * [in_channel][mux_way] for convenience.
+ */
+ if (++in_idx == ARRAY_SIZE(cs48l32_codec->in_type)) {
+ in_idx = 0;
+ ++mux_way_idx;
+ }
+ }
+}
+
+static void cs48l32_prop_get_pdm_sup(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,pdm-sup";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT];
+ int i;
+
+ static_assert(ARRAY_SIZE(tmp) == ARRAY_SIZE(cs48l32_codec->pdm_sup));
+
+ cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ switch (tmp[i]) {
+ case CS48L32_PDM_SUP_VOUT_MIC:
+ case CS48L32_PDM_SUP_MICBIAS1:
+ cs48l32_codec->pdm_sup[i] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, cs48l32_codec->pdm_sup[i]);
+ break;
+ }
+ }
+}
+
+static void cs48l32_handle_properties(struct cs48l32_codec *cs48l32_codec)
+{
+ cs48l32_prop_get_in_type(cs48l32_codec);
+ cs48l32_prop_get_pdm_sup(cs48l32_codec);
+}
+
+static int cs48l32_request_interrupt(struct cs48l32_codec *cs48l32_codec)
+{
+ int irq = cs48l32_codec->core.irq;
+ int ret;
+
+ if (irq < 1)
+ return 0;
+
+ /*
+ * Don't use devm because this must be freed before destroying the
+ * rest of the driver
+ */
+ ret = request_threaded_irq(irq, NULL, cs48l32_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs48l32", cs48l32_codec);
+ if (ret)
+ return dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to get IRQ\n");
+
+ return 0;
+}
+
+static int cs48l32_create_codec_component(struct cs48l32_codec *cs48l32_codec)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ ASSERT_STRUCT_OFFSET(struct cs48l32_codec, dsp, 0);
+ static_assert(ARRAY_SIZE(cs48l32_dai) == ARRAY_SIZE(cs48l32_codec->dai));
+
+ cs48l32_handle_properties(cs48l32_codec);
+
+ dsp = &cs48l32_codec->dsp;
+ dsp->part = "cs48l32";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs48l32_codec->core.dev;
+ dsp->cs_dsp.regmap = cs48l32_codec->core.regmap;
+ dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID;
+ dsp->cs_dsp.mem = cs48l32_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions);
+ dsp->pre_run = cs48l32_dsp_pre_run;
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0)
+ return ret;
+
+ cs48l32_codec->fll.codec = cs48l32_codec;
+ cs48l32_codec->fll.id = 1;
+ cs48l32_codec->fll.base = CS48L32_FLL1_CONTROL1;
+ cs48l32_codec->fll.sts_addr = CS48L32_IRQ1_STS_6;
+ cs48l32_codec->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK;
+ cs48l32_init_fll(&cs48l32_codec->fll);
+
+ ret = cs48l32_request_interrupt(cs48l32_codec);
+ if (ret)
+ goto err_dsp;
+
+ ret = devm_snd_soc_register_component(cs48l32_codec->core.dev,
+ &cs48l32_soc_component_drv,
+ cs48l32_dai,
+ ARRAY_SIZE(cs48l32_dai));
+ if (ret < 0) {
+ dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to register component\n");
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(&cs48l32_codec->dsp);
+
+ return ret;
+}
+
+static int cs48l32_wait_for_boot(struct cs48l32 *cs48l32)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs48l32->regmap, CS48L32_IRQ1_EINT_2, val,
+ ((val < 0xffffffff) && (val & CS48L32_BOOT_DONE_EINT1_MASK)),
+ 1000, CS48L32_BOOT_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs48l32->dev, "BOOT_DONE timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret);
+ return ret;
+ }
+
+ if (val & BIT(CS48L32_MCU_STS_SHIFT)) {
+ dev_err(cs48l32->dev, "MCU boot failed\n");
+ return -EIO;
+ }
+
+ pm_runtime_mark_last_busy(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_soft_reset(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC);
+ if (ret != 0) {
+ dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ return 0;
+}
+
+static void cs48l32_enable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio)
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 0);
+}
+
+static void cs48l32_disable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio) {
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 1);
+ usleep_range(CS48L32_HARD_RESET_MIN_US, CS48L32_HARD_RESET_MIN_US + 1000);
+ }
+}
+
+static int cs48l32_runtime_resume(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret;
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret)
+ goto err;
+
+ /* Check whether registers reset during suspend */
+ regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val);
+ if (!val)
+ regcache_mark_dirty(cs48l32->regmap);
+ else
+ dev_dbg(cs48l32->dev, "Did not reset during suspend\n");
+
+ ret = regcache_sync(cs48l32->regmap);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return ret;
+}
+
+static int cs48l32_runtime_suspend(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Flag to detect if the registers reset during suspend */
+ regmap_write(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, 1);
+
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs48l32_pm_ops = {
+ RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL)
+};
+
+static int cs48l32_configure_clk32k(struct cs48l32 *cs48l32)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(cs48l32->mclk1);
+ if (ret)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to enable 32k clock\n");
+
+ ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1);
+ if (ret) {
+ clk_disable_unprepare(cs48l32->mclk1);
+ return dev_err_probe(cs48l32->dev, ret, "Failed to init 32k clock\n");
+ }
+
+ return 0;
+}
+
+static int cs48l32_get_clocks(struct cs48l32 *cs48l32)
+{
+ cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1");
+ if (IS_ERR(cs48l32->mclk1))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1),
+ "Failed to get mclk1\n");
+
+ return 0;
+}
+
+static int cs48l32_get_reset_gpio(struct cs48l32 *cs48l32)
+{
+ struct gpio_desc *reset;
+
+ reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n");
+
+ /* ACPI can override the GPIOD_OUT_LOW so ensure it starts low */
+ gpiod_set_raw_value_cansleep(reset, 0);
+
+ cs48l32->reset_gpio = reset;
+
+ return 0;
+}
+
+static int cs48l32_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct cs48l32_codec *cs48l32_codec;
+ struct cs48l32 *cs48l32;
+ unsigned int hwid, rev, otp_rev;
+ int i, ret;
+
+ cs48l32_codec = devm_kzalloc(&spi->dev, sizeof(*cs48l32_codec), GFP_KERNEL);
+ if (!cs48l32_codec)
+ return -ENOMEM;
+
+ cs48l32 = &cs48l32_codec->core;
+ cs48l32->dev = dev;
+ cs48l32->irq = spi->irq;
+ mutex_init(&cs48l32_codec->rate_lock);
+ cs48l32_codec->in_vu_reg = CS48L32_INPUT_CONTROL3;
+
+ dev_set_drvdata(cs48l32->dev, cs48l32_codec);
+
+ ret = cs48l32_create_regmap(spi, cs48l32);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n");
+
+ regcache_cache_only(cs48l32->regmap, true);
+
+ ret = cs48l32_get_reset_gpio(cs48l32);
+ if (ret)
+ return ret;
+
+ ret = cs48l32_get_clocks(cs48l32);
+ if (ret)
+ return ret;
+
+ static_assert(ARRAY_SIZE(cs48l32_core_supplies) == ARRAY_SIZE(cs48l32->core_supplies));
+ for (i = 0; i < ARRAY_SIZE(cs48l32->core_supplies); i++)
+ cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs48l32->core_supplies),
+ cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request core supplies\n");
+
+ cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "vdd-d");
+ if (IS_ERR(cs48l32->vdd_d))
+ return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request vdd-d\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable core supplies\n");
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(dev, "Failed to enable vdd-d: %d\n", ret);
+ goto err_enable;
+ }
+
+ cs48l32_disable_hard_reset(cs48l32);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ /* If we don't have a reset GPIO use a soft reset */
+ if (!cs48l32->reset_gpio) {
+ ret = cs48l32_soft_reset(cs48l32);
+ if (ret)
+ goto err_reset;
+ }
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid);
+ if (ret) {
+ dev_err(dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+ hwid &= CS48L32_DEVID_MASK;
+
+ switch (hwid) {
+ case CS48L32_SILICON_ID:
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err_probe(cs48l32->dev, ret, "Unknown device ID: %#x\n", hwid);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev);
+ if (ret) {
+ dev_err(dev, "Failed to read revision register: %d\n", ret);
+ goto err_reset;
+ }
+ rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK;
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev);
+ if (ret) {
+ dev_err(dev, "Failed to read OTP revision register: %d\n", ret);
+ goto err_reset;
+ }
+ otp_rev &= CS48L32_OTPID_MASK;
+
+ dev_info(dev, "CS48L%x revision %X%u OTP%u\n", hwid & 0xff,
+ rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev);
+
+ /* Apply hardware patch */
+ ret = cs48l32_apply_patch(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret);
+ goto err_reset;
+ }
+
+ /* BOOT_DONE interrupt is unmasked by default, so mask it */
+ ret = regmap_set_bits(cs48l32->regmap, CS48L32_IRQ1_MASK_2, CS48L32_BOOT_DONE_EINT1_MASK);
+
+ ret = cs48l32_configure_clk32k(cs48l32);
+ if (ret)
+ goto err_reset;
+
+ pm_runtime_set_active(cs48l32->dev);
+ pm_runtime_set_autosuspend_delay(cs48l32->dev, 100);
+ pm_runtime_use_autosuspend(cs48l32->dev);
+ pm_runtime_enable(cs48l32->dev);
+
+ ret = cs48l32_create_codec_component(cs48l32_codec);
+ if (ret)
+ goto err_clk32k;
+
+ return 0;
+
+err_clk32k:
+ clk_disable_unprepare(cs48l32->mclk1);
+err_reset:
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_disable(cs48l32->vdd_d);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ return ret;
+}
+
+static void cs48l32_spi_remove(struct spi_device *spi)
+{
+ struct cs48l32_codec *cs48l32_codec = spi_get_drvdata(spi);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Remove IRQ handler before destroying anything else */
+ if (cs48l32->irq >= 1)
+ free_irq(cs48l32->irq, cs48l32_codec);
+
+ pm_runtime_disable(cs48l32->dev);
+ regulator_disable(cs48l32->vdd_d);
+ clk_disable_unprepare(cs48l32->mclk1);
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ mutex_destroy(&cs48l32_codec->rate_lock);
+}
+
+static const struct of_device_id cs48l32_of_match[] = {
+ { .compatible = "cirrus,cs48l32", },
+ {},
+};
+
+static const struct spi_device_id cs48l32_spi_ids[] = {
+ { "cs48l32", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids);
+
+static struct spi_driver cs48l32_spi_driver = {
+ .driver = {
+ .name = "cs48l32",
+ .pm = pm_ptr(&cs48l32_pm_ops),
+ .of_match_table = cs48l32_of_match,
+ },
+ .probe = &cs48l32_spi_probe,
+ .remove = &cs48l32_spi_remove,
+ .id_table = cs48l32_spi_ids,
+};
+module_spi_driver(cs48l32_spi_driver);
+
+MODULE_DESCRIPTION("CS48L32 ASoC codec driver");
+MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs48l32.h b/sound/soc/codecs/cs48l32.h
new file mode 100644
index 000000000000..c1b4e13feae4
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Cirrus Logic CS48L32 audio DSP.
+ *
+ * Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+#ifndef SND_SOC_CS48L32_H
+#define SND_SOC_CS48L32_H
+
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include "wm_adsp.h"
+
+#define CS48L32_SILICON_ID 0x48a32
+
+#define CS48L32_32K_MCLK1 0
+
+#define CS48L32_SFT_RESET_MAGIC 0x5a000000
+#define CS48L32_SOFT_RESET_US 2000
+#define CS48L32_HARD_RESET_MIN_US 1000
+
+#define CS48L32_SEEN_BOOT_DONE BIT(0)
+#define CS48L32_BOOT_TIMEOUT_US 25000
+
+#define CS48L32_ASP_ENABLES1 0x00
+#define CS48L32_ASP_CONTROL1 0x04
+#define CS48L32_ASP_CONTROL2 0x08
+#define CS48L32_ASP_CONTROL3 0x0c
+#define CS48L32_ASP_FRAME_CONTROL1 0x10
+#define CS48L32_ASP_FRAME_CONTROL2 0x14
+#define CS48L32_ASP_FRAME_CONTROL5 0x20
+#define CS48L32_ASP_FRAME_CONTROL6 0x24
+#define CS48L32_ASP_DATA_CONTROL1 0x30
+#define CS48L32_ASP_DATA_CONTROL5 0x40
+#define CS48L32_SYSCLK_RATE_6MHZ 0
+#define CS48L32_SYSCLK_RATE_12MHZ 1
+#define CS48L32_SYSCLK_RATE_24MHZ 2
+#define CS48L32_SYSCLK_RATE_49MHZ 3
+#define CS48L32_SYSCLK_RATE_98MHZ 4
+#define CS48L32_FLLHJ_INT_MAX_N 1023
+#define CS48L32_FLLHJ_INT_MIN_N 1
+#define CS48L32_FLLHJ_FRAC_MAX_N 255
+#define CS48L32_FLLHJ_FRAC_MIN_N 2
+#define CS48L32_FLLHJ_LP_INT_MODE_THRESH 100000
+#define CS48L32_FLLHJ_LOW_THRESH 192000
+#define CS48L32_FLLHJ_MID_THRESH 1152000
+#define CS48L32_FLLHJ_MAX_THRESH 13000000
+#define CS48L32_FLLHJ_LOW_GAINS 0x23f0
+#define CS48L32_FLLHJ_MID_GAINS 0x22f2
+#define CS48L32_FLLHJ_HIGH_GAINS 0x21f0
+#define CS48L32_FLL_MAX_FOUT 50000000
+#define CS48L32_FLL_MAX_REFDIV 8
+#define CS48L32_FLL_CONTROL1_OFFS 0x00
+#define CS48L32_FLL_CONTROL2_OFFS 0x04
+#define CS48L32_FLL_CONTROL3_OFFS 0x08
+#define CS48L32_FLL_CONTROL4_OFFS 0x0c
+#define CS48L32_FLL_CONTROL5_OFFS 0x10
+#define CS48L32_FLL_CONTROL6_OFFS 0x14
+#define CS48L32_FLL_DIGITAL_TEST2_OFFS 0x34
+#define CS48L32_FLL_GPIO_CLOCK_OFFS 0xa0
+#define CS48L32_DSP_CLOCK_FREQ_OFFS 0x00000
+#define CS48L32_ASP_FMT_DSP_MODE_A 0
+#define CS48L32_ASP_FMT_DSP_MODE_B 1
+#define CS48L32_ASP_FMT_I2S_MODE 2
+#define CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE 3
+#define CS48L32_HALO_SAMPLE_RATE_RX1 0x00080
+#define CS48L32_HALO_SAMPLE_RATE_TX1 0x00280
+#define CS48L32_HALO_DSP_RATE_MASK 0x1f
+
+#define CS48L32_PDMCLK_SRC_IN1_PDMCLK 0x0
+#define CS48L32_PDMCLK_SRC_IN2_PDMCLK 0x1
+#define CS48L32_PDMCLK_SRC_IN3_PDMCLK 0x2
+#define CS48L32_PDMCLK_SRC_IN4_PDMCLK 0x3
+#define CS48L32_PDMCLK_SRC_AUXPDM1_CLK 0x8
+#define CS48L32_PDMCLK_SRC_AUXPDM2_CLK 0x9
+
+#define CS48L32_MAX_DAI 6
+#define CS48L32_MAX_INPUT 4
+#define CS48L32_MAX_ANALOG_INPUT 2
+#define CS48L32_MAX_IN_MUX_WAYS 2
+#define CS48L32_MAX_ASP 2
+
+#define CS48L32_EQ_BLOCK_SZ 60
+#define CS48L32_N_EQ_BLOCKS 4
+
+#define CS48L32_DSP_N_RX_CHANNELS 8
+#define CS48L32_DSP_N_TX_CHANNELS 8
+
+#define CS48L32_LHPF_MAX_COEFF 4095
+#define CS48L32_EQ_MAX_COEFF 4095
+
+#define CS48L32_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 4, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 8, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 12, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv)
+
+#define CS48L32_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL( \
+ name, reg, 0, CS48L32_MIXER_SRC_MASK, \
+ cs48l32_mixer_texts, cs48l32_mixer_values)
+
+#define CS48L32_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = SOC_DAPM_ENUM("Route", name##_enum)
+
+#define CS48L32_MUX_ENUMS(name, base_reg) \
+ static CS48L32_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static CS48L32_MUX_CTL_DECL(name)
+
+#define CS48L32_MIXER_ENUMS(name, base_reg) \
+ CS48L32_MUX_ENUMS(name##_in1, base_reg); \
+ CS48L32_MUX_ENUMS(name##_in2, base_reg + 4); \
+ CS48L32_MUX_ENUMS(name##_in3, base_reg + 8); \
+ CS48L32_MUX_ENUMS(name##_in4, base_reg + 12)
+
+#define CS48L32_MUX(name, ctrl) SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define CS48L32_MUX_WIDGETS(name, name_str) CS48L32_MUX(name_str " Input 1", &name##_mux)
+
+#define CS48L32_MIXER_WIDGETS(name, name_str) \
+ CS48L32_MUX(name_str " Input 1", &name##_in1_mux), \
+ CS48L32_MUX(name_str " Input 2", &name##_in2_mux), \
+ CS48L32_MUX(name_str " Input 3", &name##_in3_mux), \
+ CS48L32_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS48L32_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input 1" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1")
+
+#define CS48L32_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 2"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 3"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+ { name, NULL, name " Preloader" }, \
+ { name, NULL, "SYSCLK" }, \
+ { name " Preload", NULL, name " Preloader" }, \
+ CS48L32_MIXER_ROUTES(name, name "RX1"), \
+ CS48L32_MIXER_ROUTES(name, name "RX2"), \
+ CS48L32_MIXER_ROUTES(name, name "RX3"), \
+ CS48L32_MIXER_ROUTES(name, name "RX4"), \
+ CS48L32_MIXER_ROUTES(name, name "RX5"), \
+ CS48L32_MIXER_ROUTES(name, name "RX6"), \
+ CS48L32_MIXER_ROUTES(name, name "RX7"), \
+ CS48L32_MIXER_ROUTES(name, name "RX8") \
+
+#define CS48L32_DSP_ROUTES_1_8(name) \
+ { name, NULL, "DSPCLK" }, \
+ CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+
+#define CS48L32_RATE_CONTROL(name, domain) SOC_ENUM(name, cs48l32_sample_rate[(domain) - 1])
+
+#define CS48L32_RATE_ENUM(name, enum) \
+ SOC_ENUM_EXT(name, enum, snd_soc_get_enum_double, cs48l32_rate_put)
+
+#define CS48L32_DSP_RATE_CONTROL(name, num) \
+ SOC_ENUM_EXT(name " Rate", cs48l32_dsp_rate_enum[num], \
+ cs48l32_dsp_rate_get, cs48l32_dsp_rate_put)
+
+#define CS48L32_EQ_COEFF_CONTROL(xname, xreg, xbase, xshift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = cs48l32_eq_coeff_info, .get = cs48l32_eq_coeff_get, \
+ .put = cs48l32_eq_coeff_put, .private_value = \
+ (unsigned long)&(struct cs48l32_eq_control) { .reg = xreg,\
+ .shift = xshift, .block_base = xbase, .max = 65535 } }
+
+#define CS48L32_EQ_REG_NAME_PASTER(eq, band, type) \
+ CS48L32_ ## eq ## _ ## band ## _ ## type
+
+#define CS48L32_EQ_BAND_COEFF_CONTROLS(name, band) \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " C", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF2), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_EQ_COEFF_CONTROLS(name) \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND1), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND2), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND3), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND4), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = cs48l32_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+/* these have a subseq number so they run after SYSCLK and DSPCLK widgets */
+#define CS48L32_DSP_FREQ_WIDGET_EV(name, num, event) \
+ SND_SOC_DAPM_SUPPLY_S(name "FREQ", 100, SND_SOC_NOPM, num, 0, \
+ event, SND_SOC_DAPM_POST_PMU)
+
+#define CS48L32_RATES SNDRV_PCM_RATE_KNOT
+
+#define CS48L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS48L32_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "ASP1RX1", "ASP1RX1" }, \
+ { name, "ASP1RX2", "ASP1RX2" }, \
+ { name, "ASP1RX3", "ASP1RX3" }, \
+ { name, "ASP1RX4", "ASP1RX4" }, \
+ { name, "ASP1RX5", "ASP1RX5" }, \
+ { name, "ASP1RX6", "ASP1RX6" }, \
+ { name, "ASP1RX7", "ASP1RX7" }, \
+ { name, "ASP1RX8", "ASP1RX8" }, \
+ { name, "ASP2RX1", "ASP2RX1" }, \
+ { name, "ASP2RX2", "ASP2RX2" }, \
+ { name, "ASP2RX3", "ASP2RX3" }, \
+ { name, "ASP2RX4", "ASP2RX4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "Ultrasonic 1", "Ultrasonic 1" }, \
+ { name, "Ultrasonic 2", "Ultrasonic 2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP1.7", "DSP1" }, \
+ { name, "DSP1.8", "DSP1" }
+
+struct cs48l32_enum {
+ struct soc_enum mixer_enum;
+ int val;
+};
+
+struct cs48l32_eq_control {
+ unsigned int reg;
+ unsigned int shift;
+ unsigned int block_base;
+ unsigned int max;
+};
+
+struct cs48l32_dai_priv {
+ int clk;
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct cs48l32_dsp_power_reg_block {
+ unsigned int start;
+ unsigned int end;
+};
+
+struct cs48l32_dsp_power_regs {
+ const unsigned int *pwd;
+ unsigned int n_pwd;
+ const struct cs48l32_dsp_power_reg_block *ext;
+ unsigned int n_ext;
+};
+
+struct cs48l32;
+struct cs48l32_codec;
+struct spi_device;
+
+struct cs48l32_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int fratio;
+ int gain;
+ int alt_gain;
+};
+
+struct cs48l32_fll {
+ struct cs48l32_codec *codec;
+ int id;
+ unsigned int base;
+
+ unsigned int sts_addr;
+ unsigned int sts_mask;
+ unsigned int fout;
+ int ref_src;
+ unsigned int ref_freq;
+
+ struct cs48l32_fll_cfg ref_cfg;
+};
+
+struct cs48l32_codec {
+ struct wm_adsp dsp; /* must be first */
+ struct cs48l32 core;
+ int sysclk;
+ int dspclk;
+ struct cs48l32_dai_priv dai[CS48L32_MAX_DAI];
+ struct cs48l32_fll fll;
+
+ unsigned int in_up_pending;
+ unsigned int in_vu_reg;
+
+ struct mutex rate_lock;
+
+ u8 dsp_dma_rates[CS48L32_DSP_N_RX_CHANNELS + CS48L32_DSP_N_TX_CHANNELS];
+
+ u8 in_type[CS48L32_MAX_ANALOG_INPUT][CS48L32_MAX_IN_MUX_WAYS];
+ u8 pdm_sup[CS48L32_MAX_ANALOG_INPUT];
+ u8 tdm_width[CS48L32_MAX_ASP];
+ u8 tdm_slots[CS48L32_MAX_ASP];
+
+ unsigned int eq_mode[CS48L32_N_EQ_BLOCKS];
+ __be16 eq_coefficients[CS48L32_N_EQ_BLOCKS][CS48L32_EQ_BLOCK_SZ / 2];
+
+ const struct cs48l32_dsp_power_regs *dsp_power_regs;
+};
+
+#define cs48l32_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define cs48l32_asp_err(_dai, fmt, ...) \
+ dev_err(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_warn(_dai, fmt, ...) \
+ dev_warn(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32);
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32);
+int cs48l32_enable_asp1_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_enable_asp2_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_micvdd_voltage_index(u32 voltage);
+int cs48l32_micbias1_voltage_index(u32 voltage);
+
+#endif
diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c
new file mode 100644
index 000000000000..52b02ceaa7e3
--- /dev/null
+++ b/sound/soc/codecs/cs530x-i2c.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2024-2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs530x.h"
+
+static const struct of_device_id cs530x_of_match[] = {
+ {
+ .compatible = "cirrus,cs4282",
+ .data = (void *)CS4282,
+ }, {
+ .compatible = "cirrus,cs4302",
+ .data = (void *)CS4302,
+ }, {
+ .compatible = "cirrus,cs4304",
+ .data = (void *)CS4304,
+ }, {
+ .compatible = "cirrus,cs4308",
+ .data = (void *)CS4308,
+ }, {
+ .compatible = "cirrus,cs5302",
+ .data = (void *)CS5302,
+ }, {
+ .compatible = "cirrus,cs5304",
+ .data = (void *)CS5304,
+ }, {
+ .compatible = "cirrus,cs5308",
+ .data = (void *)CS5308,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs530x_of_match);
+
+static const struct i2c_device_id cs530x_i2c_id[] = {
+ { "cs4282", CS4282 },
+ { "cs4302", CS4302 },
+ { "cs4304", CS4304 },
+ { "cs4308", CS4308 },
+ { "cs5302", CS5302 },
+ { "cs5304", CS5304 },
+ { "cs5308", CS5308 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id);
+
+static int cs530x_i2c_probe(struct i2c_client *client)
+{
+ struct cs530x_priv *cs530x;
+
+ cs530x = devm_kzalloc(&client->dev, sizeof(*cs530x), GFP_KERNEL);
+ if (!cs530x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs530x);
+
+ cs530x->regmap = devm_regmap_init_i2c(client, &cs530x_regmap_i2c);
+ if (IS_ERR(cs530x->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(cs530x->regmap),
+ "Failed to allocate register map\n");
+
+ cs530x->devtype = (uintptr_t)i2c_get_match_data(client);
+ cs530x->dev = &client->dev;
+
+ return cs530x_probe(cs530x);
+}
+
+static struct i2c_driver cs530x_i2c_driver = {
+ .driver = {
+ .name = "cs530x",
+ .of_match_table = cs530x_of_match,
+ },
+ .probe = cs530x_i2c_probe,
+ .id_table = cs530x_i2c_id,
+};
+module_i2c_driver(cs530x_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS530X driver");
+MODULE_IMPORT_NS("SND_SOC_CS530X");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paulha@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x-spi.c b/sound/soc/codecs/cs530x-spi.c
new file mode 100644
index 000000000000..dbf1e7bbec19
--- /dev/null
+++ b/sound/soc/codecs/cs530x-spi.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include "cs530x.h"
+
+static const struct of_device_id cs530x_of_match[] = {
+ {
+ .compatible = "cirrus,cs4282",
+ .data = (void *)CS4282,
+ }, {
+ .compatible = "cirrus,cs4302",
+ .data = (void *)CS4302,
+ }, {
+ .compatible = "cirrus,cs4304",
+ .data = (void *)CS4304,
+ }, {
+ .compatible = "cirrus,cs4308",
+ .data = (void *)CS4308,
+ }, {
+ .compatible = "cirrus,cs5302",
+ .data = (void *)CS5302,
+ }, {
+ .compatible = "cirrus,cs5304",
+ .data = (void *)CS5304,
+ }, {
+ .compatible = "cirrus,cs5304",
+ .data = (void *)CS5308,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs530x_of_match);
+
+static const struct spi_device_id cs530x_spi_id[] = {
+ { "cs4282", CS4282 },
+ { "cs4302", CS4302 },
+ { "cs4304", CS4304 },
+ { "cs4308", CS4308 },
+ { "cs5302", CS5302 },
+ { "cs5304", CS5304 },
+ { "cs5308", CS5308 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cs530x_spi_id);
+
+static int cs530x_spi_probe(struct spi_device *spi)
+{
+ struct cs530x_priv *cs530x;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ cs530x = devm_kzalloc(dev, sizeof(struct cs530x_priv), GFP_KERNEL);
+ if (cs530x == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs530x);
+
+ cs530x->regmap = devm_regmap_init_spi(spi, &cs530x_regmap_spi);
+ if (IS_ERR(cs530x->regmap)) {
+ ret = PTR_ERR(cs530x->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs530x->devtype = (unsigned long)spi_get_device_match_data(spi);
+ cs530x->dev = &spi->dev;
+
+ return cs530x_probe(cs530x);
+}
+
+static struct spi_driver cs530x_spi_driver = {
+ .driver = {
+ .name = "cs530x",
+ .of_match_table = cs530x_of_match,
+ },
+ .id_table = cs530x_spi_id,
+ .probe = cs530x_spi_probe,
+};
+
+module_spi_driver(cs530x_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS530X driver");
+MODULE_IMPORT_NS("SND_SOC_CS530X");
+MODULE_AUTHOR("Vitaly Rodionov <vitalyr@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
new file mode 100644
index 000000000000..18b5ff75feec
--- /dev/null
+++ b/sound/soc/codecs/cs530x.c
@@ -0,0 +1,1343 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2024-2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.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 "cs530x.h"
+
+static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = {
+ "vdd-a",
+ "vdd-io",
+};
+
+static const struct reg_default cs530x_reg_defaults[] = {
+ { CS530X_CLK_CFG_0, 0x30 },
+ { CS530X_CLK_CFG_1, 0x0001 },
+ { CS530X_CHIP_ENABLE, 0 },
+ { CS530X_ASP_CFG, 0 },
+ { CS530X_SIGNAL_PATH_CFG, 0 },
+ { CS530X_IN_ENABLES, 0 },
+ { CS530X_IN_RAMP_SUM, 0x0022 },
+ { CS530X_IN_FILTER, 0 },
+ { CS530X_IN_HIZ, 0 },
+ { CS530X_IN_INV, 0 },
+ { CS530X_IN_VOL_CTRL1_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL1_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL2_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL2_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL3_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL3_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL4_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL4_1, 0x8000 },
+ { CS530X_OUT_ENABLES, 0 },
+ { CS530X_OUT_RAMP_SUM, 0x0022 },
+ { CS530X_OUT_FILTER, 0 },
+ { CS530X_OUT_INV, 0 },
+ { CS530X_OUT_VOL_CTRL1_0, 0x8000 },
+ { CS530X_OUT_VOL_CTRL1_1, 0x8000 },
+ { CS530X_OUT_VOL_CTRL2_0, 0x8000 },
+ { CS530X_OUT_VOL_CTRL2_1, 0x8000 },
+ { CS530X_OUT_VOL_CTRL3_0, 0x8000 },
+ { CS530X_OUT_VOL_CTRL3_1, 0x8000 },
+ { CS530X_OUT_VOL_CTRL4_0, 0x8000 },
+ { CS530X_OUT_VOL_CTRL4_1, 0x8000 },
+ { CS530X_PAD_FN, 0 },
+ { CS530X_PAD_LVL, 0 },
+};
+
+static bool cs530x_read_and_write_regs(unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_CLK_CFG_0:
+ case CS530X_CLK_CFG_1:
+ case CS530X_CHIP_ENABLE:
+ case CS530X_ASP_CFG:
+ case CS530X_SIGNAL_PATH_CFG:
+ case CS530X_IN_ENABLES:
+ case CS530X_IN_RAMP_SUM:
+ case CS530X_IN_FILTER:
+ case CS530X_IN_HIZ:
+ case CS530X_IN_INV:
+ case CS530X_IN_VOL_CTRL1_0:
+ case CS530X_IN_VOL_CTRL1_1:
+ case CS530X_IN_VOL_CTRL2_0:
+ case CS530X_IN_VOL_CTRL2_1:
+ case CS530X_IN_VOL_CTRL3_0:
+ case CS530X_IN_VOL_CTRL3_1:
+ case CS530X_IN_VOL_CTRL4_0:
+ case CS530X_IN_VOL_CTRL4_1:
+ case CS530X_OUT_ENABLES:
+ case CS530X_OUT_RAMP_SUM:
+ case CS530X_OUT_DEEMPH:
+ case CS530X_OUT_FILTER:
+ case CS530X_OUT_INV:
+ case CS530X_OUT_VOL_CTRL1_0:
+ case CS530X_OUT_VOL_CTRL1_1:
+ case CS530X_OUT_VOL_CTRL2_0:
+ case CS530X_OUT_VOL_CTRL2_1:
+ case CS530X_OUT_VOL_CTRL3_0:
+ case CS530X_OUT_VOL_CTRL3_1:
+ case CS530X_OUT_VOL_CTRL4_0:
+ case CS530X_OUT_VOL_CTRL4_1:
+ case CS530X_PAD_FN:
+ case CS530X_PAD_LVL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs530x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_DEVID:
+ case CS530X_REVID:
+ return true;
+ default:
+ return cs530x_read_and_write_regs(reg);
+ }
+}
+
+static bool cs530x_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_SW_RESET:
+ case CS530X_IN_VOL_CTRL5:
+ case CS530X_OUT_VOL_CTRL5:
+ return true;
+ default:
+ return cs530x_read_and_write_regs(reg);
+ }
+}
+
+static int cs530x_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret)
+ goto volsw_err;
+
+ /* Write INOUT_VU bit for the volume change to take effect */
+ regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_INOUT_VU);
+
+volsw_err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0);
+
+static const char * const cs530x_inout_filter_text[] = {
+ "Min Phase Slow Roll-off",
+ "Min Phase Fast Roll-off",
+ "Linear Phase Slow Roll-off",
+ "Linear Phase Fast Roll-off",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_filter_enum, CS530X_IN_FILTER,
+ CS530X_INOUT_FILTER_SHIFT,
+ cs530x_inout_filter_text);
+
+static SOC_ENUM_SINGLE_DECL(cs530x_out_filter_enum, CS530X_OUT_FILTER,
+ CS530X_INOUT_FILTER_SHIFT,
+ cs530x_inout_filter_text);
+
+static const char * const cs530x_4ch_sum_text[] = {
+ "None",
+ "Groups of 2",
+ "Groups of 4",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM,
+ CS530X_INOUT_SUM_MODE_SHIFT,
+ cs530x_4ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = {
+SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum),
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_out_sum_ch4_enum, CS530X_OUT_RAMP_SUM,
+ CS530X_INOUT_SUM_MODE_SHIFT,
+ cs530x_4ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_out_sum_4ch_controls[] = {
+SOC_ENUM("OUT Sum Select", cs530x_out_sum_ch4_enum),
+};
+
+static const char * const cs530x_8ch_sum_text[] = {
+ "None",
+ "Groups of 2",
+ "Groups of 4",
+ "Groups of 8",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM,
+ CS530X_INOUT_SUM_MODE_SHIFT,
+ cs530x_8ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = {
+SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum),
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_out_sum_ch8_enum, CS530X_OUT_RAMP_SUM,
+ CS530X_INOUT_SUM_MODE_SHIFT,
+ cs530x_8ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_out_sum_8ch_controls[] = {
+SOC_ENUM("OUT Sum Select", cs530x_out_sum_ch8_enum),
+};
+
+static const char * const cs530x_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM,
+ CS530X_RAMP_RATE_INC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM,
+ CS530X_RAMP_RATE_DEC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = {
+SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_ENUM("IN DEC Filter Select", cs530x_in_filter_enum),
+SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum),
+SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum),
+
+SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_INOUT1_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_INOUT2_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = {
+SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_INOUT3_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_INOUT4_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = {
+SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_INOUT5_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_INOUT6_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_INOUT7_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_INOUT8_INV_SHIFT, 1, 0),
+};
+
+static int cs530x_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs530x->adc_pairs_count++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_INOUT_MUTE);
+ regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
+
+ cs530x->adc_pairs_count--;
+ if (!cs530x->adc_pairs_count) {
+ usleep_range(1000, 1100);
+ return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
+ CS530X_INOUT_VU);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_INOUT_MUTE);
+ regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
+ return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
+ CS530X_INOUT_VU);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_out_inc_enum, CS530X_OUT_RAMP_SUM,
+ CS530X_RAMP_RATE_INC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_out_dec_enum, CS530X_OUT_RAMP_SUM,
+ CS530X_RAMP_RATE_DEC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static const struct snd_kcontrol_new cs530x_out_1_to_2_controls[] = {
+SOC_SINGLE_EXT_TLV("OUT1 Volume", CS530X_OUT_VOL_CTRL1_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT2 Volume", CS530X_OUT_VOL_CTRL1_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_ENUM("OUT DEC Filter Select", cs530x_out_filter_enum),
+SOC_ENUM("Output Ramp Up", cs530x_ramp_out_inc_enum),
+SOC_ENUM("Output Ramp Down", cs530x_ramp_out_dec_enum),
+
+SOC_SINGLE("DAC1 Invert Switch", CS530X_OUT_INV, CS530X_INOUT1_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC2 Invert Switch", CS530X_OUT_INV, CS530X_INOUT2_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_out_3_to_4_controls[] = {
+SOC_SINGLE_EXT_TLV("OUT3 Volume", CS530X_OUT_VOL_CTRL2_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT4 Volume", CS530X_OUT_VOL_CTRL2_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("DAC3 Invert Switch", CS530X_OUT_INV, CS530X_INOUT3_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC4 Invert Switch", CS530X_OUT_INV, CS530X_INOUT4_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_out_5_to_8_controls[] = {
+SOC_SINGLE_EXT_TLV("OUT5 Volume", CS530X_OUT_VOL_CTRL3_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT6 Volume", CS530X_OUT_VOL_CTRL3_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT7 Volume", CS530X_OUT_VOL_CTRL4_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT8 Volume", CS530X_OUT_VOL_CTRL4_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("DAC5 Invert Switch", CS530X_OUT_INV, CS530X_INOUT5_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC6 Invert Switch", CS530X_OUT_INV, CS530X_INOUT6_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC7 Invert Switch", CS530X_OUT_INV, CS530X_INOUT7_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC8 Invert Switch", CS530X_OUT_INV, CS530X_INOUT8_INV_SHIFT, 1, 0),
+};
+
+static int cs530x_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs530x->dac_pairs_count++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_clear_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_INOUT_MUTE);
+ regmap_clear_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+ ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
+
+ cs530x->dac_pairs_count--;
+ if (!cs530x->dac_pairs_count) {
+ usleep_range(1000, 1100);
+ return regmap_write(regmap, CS530X_OUT_VOL_CTRL5,
+ CS530X_INOUT_VU);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_set_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_INOUT_MUTE);
+ regmap_set_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+ ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
+ return regmap_write(regmap, CS530X_OUT_VOL_CTRL5,
+ CS530X_INOUT_VU);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new adc12_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new adc34_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new adc56_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new adc78_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac12_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac34_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac56_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac78_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new in_hpf_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new out_hpf_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+/* General DAPM widgets for all devices */
+static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0),
+};
+
+/* ADC's Channels 1 and 2 plus generic ADC DAPM events */
+static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1"),
+SND_SOC_DAPM_INPUT("IN2"),
+SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0),
+SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl),
+SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_INOUT_HPF_EN_SHIFT,
+ 0, &in_hpf_ctrl),
+};
+
+/* ADC's Channels 3 and 4 */
+static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN3"),
+SND_SOC_DAPM_INPUT("IN4"),
+SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0),
+SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl),
+};
+
+/* ADC's Channels 5 to 8 */
+static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN5"),
+SND_SOC_DAPM_INPUT("IN6"),
+SND_SOC_DAPM_INPUT("IN7"),
+SND_SOC_DAPM_INPUT("IN8"),
+SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0),
+SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl),
+SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0),
+SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl),
+};
+
+static const struct snd_soc_dapm_route adc_ch1_2_routes[] = {
+ { "ADC1", NULL, "Global Enable" },
+ { "ADC2", NULL, "Global Enable" },
+
+ { "ADC12 Enable", "Switch", "IN1" },
+ { "ADC12 Enable", "Switch", "IN2" },
+ { "ADC1", NULL, "ADC12 Enable" },
+ { "ADC2", NULL, "ADC12 Enable" },
+ { "IN HPF", "Switch", "ADC1" },
+ { "IN HPF", "Switch", "ADC2" },
+
+ { "AIF Capture", NULL, "IN HPF" },
+ { "AIF Capture", NULL, "ADC1" },
+ { "AIF Capture", NULL, "ADC2" },
+};
+
+static const struct snd_soc_dapm_route adc_ch3_4_routes[] = {
+ { "ADC3", NULL, "Global Enable" },
+ { "ADC4", NULL, "Global Enable" },
+
+ { "ADC34 Enable", "Switch", "IN3" },
+ { "ADC34 Enable", "Switch", "IN4" },
+ { "ADC3", NULL, "ADC34 Enable" },
+ { "ADC4", NULL, "ADC34 Enable" },
+ { "IN HPF", "Switch", "ADC3" },
+ { "IN HPF", "Switch", "ADC4" },
+
+ { "AIF Capture", NULL, "ADC3" },
+ { "AIF Capture", NULL, "ADC4" },
+};
+
+static const struct snd_soc_dapm_route adc_ch5_8_routes[] = {
+ { "ADC5", NULL, "Global Enable" },
+ { "ADC6", NULL, "Global Enable" },
+ { "ADC7", NULL, "Global Enable" },
+ { "ADC8", NULL, "Global Enable" },
+
+ { "ADC56 Enable", "Switch", "IN5" },
+ { "ADC56 Enable", "Switch", "IN6" },
+ { "ADC5", NULL, "ADC56 Enable" },
+ { "ADC6", NULL, "ADC56 Enable" },
+ { "IN HPF", "Switch", "ADC5" },
+ { "IN HPF", "Switch", "ADC6" },
+
+ { "AIF Capture", NULL, "ADC5" },
+ { "AIF Capture", NULL, "ADC6" },
+
+ { "ADC78 Enable", "Switch", "IN7" },
+ { "ADC78 Enable", "Switch", "IN8" },
+ { "ADC7", NULL, "ADC78 Enable" },
+ { "ADC8", NULL, "ADC78 Enable" },
+ { "IN HPF", "Switch", "ADC7" },
+ { "IN HPF", "Switch", "ADC8" },
+
+ { "AIF Capture", NULL, "ADC7" },
+ { "AIF Capture", NULL, "ADC8" },
+};
+
+static void cs530x_add_12_adc_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_in_1_to_2_controls,
+ ARRAY_SIZE(cs530x_in_1_to_2_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets,
+ ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes,
+ ARRAY_SIZE(adc_ch1_2_routes));
+}
+
+static void cs530x_add_34_adc_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_in_3_to_4_controls,
+ ARRAY_SIZE(cs530x_in_3_to_4_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets,
+ ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes,
+ ARRAY_SIZE(adc_ch3_4_routes));
+}
+
+/* DAC's Channels 1 and 2 plus generic DAC DAPM events */
+static const struct snd_soc_dapm_widget cs530x_dac_ch12_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("OUT1"),
+SND_SOC_DAPM_OUTPUT("OUT2"),
+SND_SOC_DAPM_DAC_E("DAC1", NULL, CS530X_OUT_ENABLES, 0, 0,
+ cs530x_dac_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC2", NULL, CS530X_OUT_ENABLES, 1, 0),
+SND_SOC_DAPM_SWITCH("DAC12 Enable", SND_SOC_NOPM, 0, 0, &dac12_ctrl),
+SND_SOC_DAPM_SWITCH("OUT HPF", CS530X_OUT_FILTER, CS530X_INOUT_HPF_EN_SHIFT,
+ 0, &out_hpf_ctrl),
+};
+
+/* DAC's Channels 3 and 4 */
+static const struct snd_soc_dapm_widget cs530x_dac_ch34_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_DAC_E("DAC3", NULL, CS530X_OUT_ENABLES, 2, 0,
+ cs530x_dac_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC4", NULL, CS530X_OUT_ENABLES, 3, 0),
+SND_SOC_DAPM_SWITCH("DAC34 Enable", SND_SOC_NOPM, 0, 0, &dac34_ctrl),
+};
+
+/* DAC's Channels 5 to 8 */
+static const struct snd_soc_dapm_widget cs530x_dac_ch58_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("OUT5"),
+SND_SOC_DAPM_OUTPUT("OUT6"),
+SND_SOC_DAPM_OUTPUT("OUT7"),
+SND_SOC_DAPM_OUTPUT("OUT8"),
+SND_SOC_DAPM_DAC_E("DAC5", NULL, CS530X_OUT_ENABLES, 4, 0,
+ cs530x_dac_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC6", NULL, CS530X_OUT_ENABLES, 5, 0),
+SND_SOC_DAPM_SWITCH("DAC56 Enable", SND_SOC_NOPM, 0, 0, &dac56_ctrl),
+SND_SOC_DAPM_DAC_E("DAC7", NULL, CS530X_OUT_ENABLES, 6, 0,
+ cs530x_dac_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC8", NULL, CS530X_OUT_ENABLES, 7, 0),
+SND_SOC_DAPM_SWITCH("DAC78 Enable", SND_SOC_NOPM, 0, 0, &dac78_ctrl),
+};
+
+static const struct snd_soc_dapm_route dac_ch1_2_routes[] = {
+ { "DAC1", NULL, "Global Enable" },
+ { "DAC2", NULL, "Global Enable" },
+
+ { "DAC12 Enable", "Switch", "OUT1" },
+ { "DAC12 Enable", "Switch", "OUT2" },
+ { "DAC1", NULL, "DAC12 Enable" },
+ { "DAC2", NULL, "DAC12 Enable" },
+ { "OUT HPF", "Switch", "DAC1" },
+ { "OUT HPF", "Switch", "DAC2" },
+
+ { "OUT HPF", NULL, "AIF Playback" },
+ { "DAC1", NULL, "AIF Playback" },
+ { "DAC2", NULL, "AIF Playback" },
+
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+};
+
+static const struct snd_soc_dapm_route dac_ch3_4_routes[] = {
+ { "DAC3", NULL, "Global Enable" },
+ { "DAC4", NULL, "Global Enable" },
+
+ { "DAC34 Enable", "Switch", "OUT3" },
+ { "DAC34 Enable", "Switch", "OUT4" },
+ { "DAC3", NULL, "DAC34 Enable" },
+ { "DAC4", NULL, "DAC34 Enable" },
+ { "OUT HPF", "Switch", "DAC3" },
+ { "OUT HPF", "Switch", "DAC4" },
+
+ { "DAC3", NULL, "AIF Playback" },
+ { "DAC4", NULL, "AIF Playback" },
+
+ { "OUT3", NULL, "DAC3" },
+ { "OUT4", NULL, "DAC4" },
+};
+
+static const struct snd_soc_dapm_route dac_ch5_8_routes[] = {
+ { "DAC5", NULL, "Global Enable" },
+ { "DAC6", NULL, "Global Enable" },
+
+ { "DAC56 Enable", "Switch", "OUT5" },
+ { "DAC56 Enable", "Switch", "OUT6" },
+ { "DAC5", NULL, "DAC56 Enable" },
+ { "DAC6", NULL, "DAC56 Enable" },
+ { "OUT HPF", "Switch", "DAC5" },
+ { "OUT HPF", "Switch", "DAC6" },
+
+ { "DAC5", NULL, "AIF Playback" },
+ { "DAC6", NULL, "AIF Playback" },
+
+ { "OUT5", NULL, "DAC5" },
+ { "OUT6", NULL, "DAC6" },
+
+ { "DAC7", NULL, "Global Enable" },
+ { "DAC8", NULL, "Global Enable" },
+
+ { "DAC78 Enable", "Switch", "OUT7" },
+ { "DAC78 Enable", "Switch", "OUT8" },
+ { "DAC7", NULL, "DAC78 Enable" },
+ { "DAC8", NULL, "DAC78 Enable" },
+ { "OUT HPF", "Switch", "DAC7" },
+ { "OUT HPF", "Switch", "DAC8" },
+
+ { "DAC7", NULL, "AIF Playback" },
+ { "DAC8", NULL, "AIF Playback" },
+
+ { "OUT7", NULL, "DAC7" },
+ { "OUT8", NULL, "DAC8" },
+};
+
+static void cs530x_add_12_dac_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_out_1_to_2_controls,
+ ARRAY_SIZE(cs530x_out_1_to_2_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_dac_ch12_dapm_widgets,
+ ARRAY_SIZE(cs530x_dac_ch12_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, dac_ch1_2_routes,
+ ARRAY_SIZE(dac_ch1_2_routes));
+}
+
+static void cs530x_add_34_dac_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_out_3_to_4_controls,
+ ARRAY_SIZE(cs530x_out_3_to_4_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_dac_ch34_dapm_widgets,
+ ARRAY_SIZE(cs530x_dac_ch34_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, dac_ch3_4_routes,
+ ARRAY_SIZE(dac_ch3_4_routes));
+}
+
+static int cs530x_set_bclk(struct snd_soc_component *component, const int freq)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int bclk_val;
+
+ switch (freq) {
+ case 2822400:
+ case 3072000:
+ bclk_val = CS530X_BCLK_2P822_3P072;
+ break;
+ case 5644800:
+ case 6144000:
+ bclk_val = CS530X_BCLK_5P6448_6P144;
+ break;
+ case 11289600:
+ case 12288000:
+ bclk_val = CS530X_BCLK_11P2896_12P288;
+ break;
+ case 22579200:
+ case 24576000:
+ bclk_val = CS530X_BCLK_24P5792_24P576;
+ break;
+ default:
+ dev_err(component->dev, "Invalid BCLK frequency %d\n", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "BCLK frequency is %d\n", freq);
+
+ return regmap_update_bits(regmap, CS530X_ASP_CFG,
+ CS530X_ASP_BCLK_FREQ_MASK, bclk_val);
+}
+
+static int cs530x_set_pll_refclk(struct snd_soc_component *component,
+ const unsigned int freq)
+{
+ struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = priv->regmap;
+ unsigned int refclk;
+
+ switch (freq) {
+ case 2822400:
+ case 3072000:
+ refclk = CS530X_REFCLK_2P822_3P072;
+ break;
+ case 5644800:
+ case 6144000:
+ refclk = CS530X_REFCLK_5P6448_6P144;
+ break;
+ case 11289600:
+ case 12288000:
+ refclk = CS530X_REFCLK_11P2896_12P288;
+ break;
+ case 22579200:
+ case 24576000:
+ refclk = CS530X_REFCLK_24P5792_24P576;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL refclk %d\n", freq);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_FREQ_MASK, refclk);
+}
+
+static int cs530x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ int ret = 0, fs = params_rate(params), bclk;
+ unsigned int fs_val;
+
+ switch (fs) {
+ case 32000:
+ fs_val = CS530X_FS_32K;
+ break;
+ case 44100:
+ case 48000:
+ fs_val = CS530X_FS_44P1K_48K;
+ break;
+ case 88200:
+ case 96000:
+ fs_val = CS530X_FS_88P2K_96K;
+ break;
+ case 176400:
+ case 192000:
+ fs_val = CS530X_FS_176P4K_192K;
+ break;
+ case 356800:
+ case 384000:
+ fs_val = CS530X_FS_356P8K_384K;
+ break;
+ case 705600:
+ case 768000:
+ fs_val = CS530X_FS_705P6K_768K;
+ break;
+ default:
+ dev_err(component->dev, "Invalid sample rate %d\n", fs);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, CS530X_CLK_CFG_1,
+ CS530X_SAMPLE_RATE_MASK, fs_val);
+
+
+ if (regmap_test_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_TDM_EN_MASK)) {
+ dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n",
+ cs530x->tdm_slots, cs530x->tdm_width);
+ bclk = snd_soc_tdm_params_to_bclk(params,
+ cs530x->tdm_width,
+ cs530x->tdm_slots,
+ 1);
+ } else {
+ bclk = snd_soc_params_to_bclk(params);
+ }
+
+ if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_SRC_MASK)) {
+ ret = cs530x_set_pll_refclk(component, bclk);
+ if (ret)
+ return ret;
+ }
+
+ return cs530x_set_bclk(component, bclk);
+}
+
+static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = priv->regmap;
+ unsigned int asp_fmt, asp_cfg = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ asp_cfg = CS530X_ASP_PRIMARY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = CS530X_ASP_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = CS530X_ASP_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ asp_fmt = CS530X_ASP_FMT_LJ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asp_cfg |= CS530X_ASP_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, CS530X_ASP_CFG,
+ CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV,
+ asp_cfg);
+
+ return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_ASP_FMT_MASK, asp_fmt);
+}
+
+static bool cs530x_check_mclk_freq(struct snd_soc_component *component,
+ const unsigned int freq)
+{
+ switch (freq) {
+ case 24576000:
+ case 22579200:
+ case 12288000:
+ case 11289600:
+ return true;
+ default:
+ dev_err(component->dev, "Invalid MCLK %d\n", freq);
+ return false;
+ }
+}
+
+static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int val;
+
+ switch (tx_mask) {
+ case CS530X_0_1_TDM_SLOT_MASK:
+ case CS530X_0_3_TDM_SLOT_MASK:
+ case CS530X_0_7_TDM_SLOT_MASK:
+ val = CS530X_0_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_2_3_TDM_SLOT_MASK:
+ val = CS530X_2_3_TDM_SLOT_VAL;
+ break;
+ case CS530X_4_5_TDM_SLOT_MASK:
+ case CS530X_4_7_TDM_SLOT_MASK:
+ val = CS530X_4_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_6_7_TDM_SLOT_MASK:
+ val = CS530X_6_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_8_9_TDM_SLOT_MASK:
+ case CS530X_8_11_TDM_SLOT_MASK:
+ case CS530X_8_15_TDM_SLOT_MASK:
+ val = CS530X_8_15_TDM_SLOT_VAL;
+ break;
+ case CS530X_10_11_TDM_SLOT_MASK:
+ val = CS530X_10_11_TDM_SLOT_VAL;
+ break;
+ case CS530X_12_13_TDM_SLOT_MASK:
+ case CS530X_12_15_TDM_SLOT_MASK:
+ val = CS530X_12_15_TDM_SLOT_VAL;
+ break;
+ case CS530X_14_15_TDM_SLOT_MASK:
+ val = CS530X_14_15_TDM_SLOT_VAL;
+ break;
+ default:
+ dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask);
+ return -EINVAL;
+ }
+
+ cs530x->tdm_width = slot_width;
+ cs530x->tdm_slots = slots;
+
+ return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_ASP_TDM_SLOT_MASK,
+ val << CS530X_ASP_TDM_SLOT_SHIFT);
+}
+
+static const struct snd_soc_dai_ops cs530x_dai_ops = {
+ .set_fmt = cs530x_set_fmt,
+ .hw_params = cs530x_hw_params,
+ .set_tdm_slot = cs530x_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_driver cs530x_dai = {
+ .name = "cs530x-dai",
+ .capture = {
+ .stream_name = "AIF Capture",
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .playback = {
+ .stream_name = "AIF Playback",
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &cs530x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int cs530x_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int sysclk_src;
+ int ret;
+
+ regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src);
+
+ /* Check if the source is the PLL */
+ if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0)
+ return 0;
+
+ switch (source) {
+ case CS530X_PLL_SRC_MCLK:
+ if (!cs530x_check_mclk_freq(component, freq_in))
+ return -EINVAL;
+
+ ret = cs530x_set_pll_refclk(component, freq_in);
+ if (ret)
+ return ret;
+
+ break;
+ case CS530X_PLL_SRC_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_SRC_MASK, source);
+}
+
+static int cs530x_component_probe(struct snd_soc_component *component)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int num_widgets;
+
+ snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets,
+ ARRAY_SIZE(cs530x_gen_dapm_widgets));
+
+ switch (cs530x->devtype) {
+ case CS4282:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_12_dac_widgets(component);
+ break;
+ case CS4302:
+ cs530x_add_12_dac_widgets(component);
+ break;
+ case CS4304:
+ cs530x_add_12_dac_widgets(component);
+ cs530x_add_34_dac_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_out_sum_4ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_out_sum_4ch_controls,
+ num_widgets);
+ break;
+ case CS4308:
+ cs530x_add_12_dac_widgets(component);
+ cs530x_add_34_dac_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_out_5_to_8_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_out_5_to_8_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_out_sum_8ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_out_sum_8ch_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_dac_ch58_dapm_widgets);
+ snd_soc_dapm_new_controls(dapm, cs530x_dac_ch58_dapm_widgets,
+ num_widgets);
+
+ snd_soc_dapm_add_routes(dapm, dac_ch5_8_routes,
+ ARRAY_SIZE(dac_ch5_8_routes));
+ break;
+ case CS5302:
+ cs530x_add_12_adc_widgets(component);
+ break;
+ case CS5304:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_34_adc_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_sum_4ch_controls,
+ num_widgets);
+ break;
+ case CS5308:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_34_adc_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_5_to_8_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_sum_8ch_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets);
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets,
+ num_widgets);
+
+ snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes,
+ ARRAY_SIZE(adc_ch5_8_routes));
+ break;
+ default:
+ dev_err(component->dev, "Invalid device type %d\n",
+ cs530x->devtype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (source) {
+ case CS530X_SYSCLK_SRC_MCLK:
+ switch (freq) {
+ case CS530X_SYSCLK_REF_45_1MHZ:
+ case CS530X_SYSCLK_REF_49_1MHZ:
+ break;
+ default:
+ dev_err(component->dev, "Invalid MCLK source rate %d\n", freq);
+ return -EINVAL;
+ }
+ break;
+ case CS530X_SYSCLK_SRC_PLL:
+ break;
+ default:
+ dev_err(component->dev, "Invalid sysclk source: %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_SYSCLK_SRC_MASK,
+ source << CS530X_SYSCLK_SRC_SHIFT);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs530x = {
+ .probe = cs530x_component_probe,
+ .set_sysclk = cs530x_set_sysclk,
+ .set_pll = cs530x_set_pll,
+ .endianness = 1,
+};
+
+const struct regmap_config cs530x_regmap_i2c = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = CS530X_MAX_REGISTER,
+ .readable_reg = cs530x_readable_register,
+ .writeable_reg = cs530x_writeable_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs530x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),
+};
+EXPORT_SYMBOL_NS_GPL(cs530x_regmap_i2c, "SND_SOC_CS530X");
+
+const struct regmap_config cs530x_regmap_spi = {
+ .reg_bits = 16,
+ .pad_bits = 16,
+ .val_bits = 16,
+
+ .reg_stride = 2,
+
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS530X_MAX_REGISTER,
+ .writeable_reg = cs530x_writeable_register,
+ .readable_reg = cs530x_readable_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs530x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),
+};
+EXPORT_SYMBOL_NS_GPL(cs530x_regmap_spi, "SND_SOC_CS530X");
+
+static int cs530x_check_device_id(struct cs530x_priv *cs530x)
+{
+ struct device *dev = cs530x->dev;
+ unsigned int dev_id, rev;
+ int ret;
+
+ ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't read device ID\n");
+
+ ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't read REV ID\n");
+
+ switch (dev_id) {
+ case CS530X_2CH_CODEC_DEV_ID:
+ cs530x->num_dacs = 2;
+ cs530x->num_adcs = 2;
+ break;
+ case CS530X_2CH_DAC_DEV_ID:
+ cs530x->num_dacs = 2;
+ break;
+ case CS530X_4CH_DAC_DEV_ID:
+ cs530x->num_dacs = 4;
+ break;
+ case CS530X_8CH_DAC_DEV_ID:
+ cs530x->num_dacs = 8;
+ break;
+ case CS530X_2CH_ADC_DEV_ID:
+ cs530x->num_adcs = 2;
+ break;
+ case CS530X_4CH_ADC_DEV_ID:
+ cs530x->num_adcs = 4;
+ break;
+ case CS530X_8CH_ADC_DEV_ID:
+ cs530x->num_adcs = 8;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n",
+ dev_id);
+ }
+
+ if (cs530x->devtype != dev_id) {
+ dev_err(dev, "Read device ID 0x%x is not the expected devtype 0x%x\n",
+ dev_id, cs530x->devtype);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x (%d in %d out)\n", dev_id, rev,
+ cs530x->num_adcs, cs530x->num_dacs);
+
+ return 0;
+}
+
+static int cs530x_parse_device_properties(struct cs530x_priv *cs530x)
+{
+ struct regmap *regmap = cs530x->regmap;
+ struct device *dev = cs530x->dev;
+ unsigned int val = 0;
+
+ switch (cs530x->num_adcs) {
+ case 8:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin78"))
+ val = CS530X_IN78_HIZ;
+
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin56"))
+ val |= CS530X_IN56_HIZ;
+
+ fallthrough;
+ case 4:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin34"))
+ val |= CS530X_IN34_HIZ;
+
+ fallthrough;
+ case 2:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin12"))
+ val |= CS530X_IN12_HIZ;
+
+ return regmap_set_bits(regmap, CS530X_IN_HIZ, val);
+ case 0:
+ /* No ADCs */
+ return 0;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid number of adcs %d\n",
+ cs530x->num_adcs);
+ }
+}
+
+int cs530x_probe(struct cs530x_priv *cs530x)
+{
+ struct device *dev = cs530x->dev;
+ int ret, i;
+
+ cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai,
+ sizeof(*(cs530x->dev_dai)),
+ GFP_KERNEL);
+ if (!cs530x->dev_dai)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++)
+ cs530x->supplies[i].supply = cs530x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+ if (ret != 0)
+ return dev_err_probe(dev, ret, "Failed to request supplies");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+ if (ret != 0)
+ return dev_err_probe(dev, ret, "Failed to enable supplies");
+
+ cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs530x->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio),
+ "Reset gpio not available\n");
+ goto err_regulator;
+ }
+
+ if (cs530x->reset_gpio) {
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs530x->reset_gpio, 0);
+ }
+
+ usleep_range(5000, 5100);
+ ret = cs530x_check_device_id(cs530x);
+ if (ret)
+ goto err_reset;
+
+ if (!cs530x->reset_gpio) {
+ ret = regmap_write(cs530x->regmap, CS530X_SW_RESET,
+ CS530X_SW_RST_VAL);
+ if (ret) {
+ dev_err_probe(dev, ret, "Soft Reset Failed\n");
+ goto err_reset;
+ }
+ }
+
+ ret = cs530x_parse_device_properties(cs530x);
+ if (ret)
+ goto err_reset;
+
+ if (cs530x->num_adcs) {
+ cs530x->dev_dai->capture.channels_min = 2;
+ cs530x->dev_dai->capture.channels_max = cs530x->num_adcs;
+ }
+
+ if (cs530x->num_dacs) {
+ cs530x->dev_dai->playback.channels_min = 2;
+ cs530x->dev_dai->playback.channels_max = cs530x->num_dacs;
+ }
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_dev_cs530x,
+ cs530x->dev_dai, 1);
+ if (ret) {
+ dev_err_probe(dev, ret, "Can't register cs530x component\n");
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs530x->reset_gpio, 1);
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs530x_probe, "SND_SOC_CS530X");
+
+MODULE_DESCRIPTION("CS530X CODEC Driver");
+MODULE_AUTHOR("Paul Handrigan <paulha@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h
new file mode 100644
index 000000000000..1e2f6a7a589c
--- /dev/null
+++ b/sound/soc/codecs/cs530x.h
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS530x CODEC driver internal data
+ *
+ * Copyright (C) 2023-2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef _CS530X_H
+#define _CS530X_H
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+/* Devices */
+#define CS530X_2CH_CODEC_DEV_ID 0x4282
+#define CS530X_2CH_DAC_DEV_ID 0x4302
+#define CS530X_4CH_DAC_DEV_ID 0x4304
+#define CS530X_8CH_DAC_DEV_ID 0x4308
+#define CS530X_2CH_ADC_DEV_ID 0x5302
+#define CS530X_4CH_ADC_DEV_ID 0x5304
+#define CS530X_8CH_ADC_DEV_ID 0x5308
+
+/* Registers */
+
+#define CS530X_DEVID 0x0000000
+#define CS530X_REVID 0x0000004
+#define CS530X_SW_RESET 0x0000022
+
+#define CS530X_CLK_CFG_0 0x0000040
+#define CS530X_CLK_CFG_1 0x0000042
+#define CS530X_CHIP_ENABLE 0x0000044
+#define CS530X_ASP_CFG 0x0000048
+#define CS530X_SIGNAL_PATH_CFG 0x0000050
+#define CS530X_IN_ENABLES 0x0000080
+#define CS530X_IN_RAMP_SUM 0x0000082
+#define CS530X_IN_FILTER 0x0000086
+#define CS530X_IN_HIZ 0x0000088
+#define CS530X_IN_INV 0x000008A
+#define CS530X_IN_VOL_CTRL1_0 0x0000090
+#define CS530X_IN_VOL_CTRL1_1 0x0000092
+#define CS530X_IN_VOL_CTRL2_0 0x0000094
+#define CS530X_IN_VOL_CTRL2_1 0x0000096
+#define CS530X_IN_VOL_CTRL3_0 0x0000098
+#define CS530X_IN_VOL_CTRL3_1 0x000009A
+#define CS530X_IN_VOL_CTRL4_0 0x000009C
+#define CS530X_IN_VOL_CTRL4_1 0x000009E
+#define CS530X_IN_VOL_CTRL5 0x00000A0
+
+#define CS530X_OUT_ENABLES 0x00000C0
+#define CS530X_OUT_RAMP_SUM 0x00000C2
+#define CS530X_OUT_DEEMPH 0x00000C4
+#define CS530X_OUT_FILTER 0x00000C6
+#define CS530X_OUT_INV 0x00000CA
+#define CS530X_OUT_VOL_CTRL1_0 0x00000D0
+#define CS530X_OUT_VOL_CTRL1_1 0x00000D2
+#define CS530X_OUT_VOL_CTRL2_0 0x00000D4
+#define CS530X_OUT_VOL_CTRL2_1 0x00000D6
+#define CS530X_OUT_VOL_CTRL3_0 0x00000D8
+#define CS530X_OUT_VOL_CTRL3_1 0x00000DA
+#define CS530X_OUT_VOL_CTRL4_0 0x00000DC
+#define CS530X_OUT_VOL_CTRL4_1 0x00000DE
+#define CS530X_OUT_VOL_CTRL5 0x00000E0
+
+#define CS530X_PAD_FN 0x0003D24
+#define CS530X_PAD_LVL 0x0003D28
+
+#define CS530X_MAX_REGISTER CS530X_PAD_LVL
+
+/* Register Fields */
+
+/* REVID */
+#define CS530X_MTLREVID GENMASK(3, 0)
+#define CS530X_AREVID GENMASK(7, 4)
+
+/* SW_RESET */
+#define CS530X_SW_RST_SHIFT 8
+#define CS530X_SW_RST_VAL (0x5A << CS530X_SW_RST_SHIFT)
+
+/* CLK_CFG_0 */
+#define CS530X_PLL_REFCLK_SRC_MASK BIT(0)
+#define CS530X_PLL_REFCLK_FREQ_MASK GENMASK(5, 4)
+#define CS530X_SYSCLK_SRC_MASK BIT(12)
+#define CS530X_SYSCLK_SRC_SHIFT 12
+#define CS530X_REFCLK_2P822_3P072 0
+#define CS530X_REFCLK_5P6448_6P144 0x10
+#define CS530X_REFCLK_11P2896_12P288 0x20
+#define CS530X_REFCLK_24P5792_24P576 0x30
+
+/* CLK_CFG_1 */
+#define CS530X_SAMPLE_RATE_MASK GENMASK(2, 0)
+#define CS530X_FS_32K 0
+#define CS530X_FS_44P1K_48K 1
+#define CS530X_FS_88P2K_96K 2
+#define CS530X_FS_176P4K_192K 3
+#define CS530X_FS_356P8K_384K 4
+#define CS530X_FS_705P6K_768K 5
+
+/* CHIP_ENABLE */
+#define CS530X_GLOBAL_EN BIT(0)
+
+/* ASP_CFG */
+#define CS530X_ASP_BCLK_FREQ_MASK GENMASK(1, 0)
+#define CS530X_ASP_PRIMARY BIT(5)
+#define CS530X_ASP_BCLK_INV BIT(6)
+#define CS530X_BCLK_2P822_3P072 0
+#define CS530X_BCLK_5P6448_6P144 1
+#define CS530X_BCLK_11P2896_12P288 2
+#define CS530X_BCLK_24P5792_24P576 3
+
+/* SIGNAL_PATH_CFG */
+#define CS530X_ASP_FMT_MASK GENMASK(2, 0)
+#define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3)
+#define CS530X_ASP_TDM_SLOT_SHIFT 3
+#define CS530X_ASP_CH_REVERSE BIT(9)
+#define CS530X_TDM_EN_MASK BIT(2)
+#define CS530X_ASP_FMT_I2S 0
+#define CS530X_ASP_FMT_LJ 1
+#define CS530X_ASP_FMT_DSP_A 6
+
+/* TDM Slots */
+#define CS530X_0_1_TDM_SLOT_MASK GENMASK(1, 0)
+#define CS530X_0_3_TDM_SLOT_MASK GENMASK(3, 0)
+#define CS530X_0_7_TDM_SLOT_MASK GENMASK(7, 0)
+#define CS530X_0_7_TDM_SLOT_VAL 0
+
+#define CS530X_2_3_TDM_SLOT_MASK GENMASK(3, 2)
+#define CS530X_2_3_TDM_SLOT_VAL 1
+
+#define CS530X_4_5_TDM_SLOT_MASK GENMASK(5, 4)
+#define CS530X_4_7_TDM_SLOT_MASK GENMASK(7, 4)
+#define CS530X_4_7_TDM_SLOT_VAL 2
+
+#define CS530X_6_7_TDM_SLOT_MASK GENMASK(7, 6)
+#define CS530X_6_7_TDM_SLOT_VAL 3
+
+#define CS530X_8_9_TDM_SLOT_MASK GENMASK(9, 8)
+#define CS530X_8_11_TDM_SLOT_MASK GENMASK(11, 8)
+#define CS530X_8_15_TDM_SLOT_MASK GENMASK(15, 8)
+#define CS530X_8_15_TDM_SLOT_VAL 4
+
+#define CS530X_10_11_TDM_SLOT_MASK GENMASK(11, 10)
+#define CS530X_10_11_TDM_SLOT_VAL 5
+
+#define CS530X_12_13_TDM_SLOT_MASK GENMASK(13, 12)
+#define CS530X_12_15_TDM_SLOT_MASK GENMASK(15, 12)
+#define CS530X_12_15_TDM_SLOT_VAL 6
+
+#define CS530X_14_15_TDM_SLOT_MASK GENMASK(15, 14)
+#define CS530X_14_15_TDM_SLOT_VAL 7
+
+/* IN_RAMP_SUM and OUT_RAMP_SUM */
+#define CS530X_RAMP_RATE_INC_SHIFT 0
+#define CS530X_RAMP_RATE_DEC_SHIFT 4
+#define CS530X_INOUT_SUM_MODE_SHIFT 13
+
+/* IN_FILTER and OUT_FILTER */
+#define CS530X_INOUT_FILTER_SHIFT 8
+#define CS530X_INOUT_HPF_EN_SHIFT 12
+
+/* IN_HIZ */
+#define CS530X_IN12_HIZ BIT(0)
+#define CS530X_IN34_HIZ BIT(1)
+#define CS530X_IN56_HIZ BIT(2)
+#define CS530X_IN78_HIZ BIT(3)
+
+/* IN_INV and OUT_INV */
+#define CS530X_INOUT1_INV_SHIFT 0
+#define CS530X_INOUT2_INV_SHIFT 1
+#define CS530X_INOUT3_INV_SHIFT 2
+#define CS530X_INOUT4_INV_SHIFT 3
+#define CS530X_INOUT5_INV_SHIFT 4
+#define CS530X_INOUT6_INV_SHIFT 5
+#define CS530X_INOUT7_INV_SHIFT 6
+#define CS530X_INOUT8_INV_SHIFT 7
+
+/* IN_VOL_CTLy_z and OUT_VOL_CTLy_z */
+#define CS530X_INOUT_MUTE BIT(15)
+
+/* IN_VOL_CTL5 */
+#define CS530X_IN_VU BIT(0)
+
+/* PAD_FN */
+#define CS530X_DOUT2_FN BIT(0)
+#define CS530X_DOUT3_FN BIT(1)
+#define CS530X_DOUT4_FN BIT(2)
+#define CS530X_SPI_CS_FN BIT(3)
+#define CS530X_CONFIG2_FN BIT(6)
+#define CS530X_CONFIG3_FN BIT(7)
+#define CS530X_CONFIG4_FN BIT(8)
+#define CS530X_CONFIG5_FN BIT(9)
+
+/* PAD_LVL */
+#define CS530X_CONFIG2_LVL BIT(6)
+#define CS530X_CONFIG3_LVL BIT(7)
+#define CS530X_CONFIG4_LVL BIT(8)
+#define CS530X_CONFIG5_LVL BIT(9)
+/* IN_VOL_CTL5 and OUT_VOL_CTL5 */
+#define CS530X_INOUT_VU BIT(0)
+
+/* MCLK Reference Source Frequency */
+/* 41KHz related */
+#define CS530X_SYSCLK_REF_45_1MHZ 45158400
+/* 48KHz related */
+#define CS530X_SYSCLK_REF_49_1MHZ 49152000
+
+/* System Clock Source */
+#define CS530X_SYSCLK_SRC_MCLK 0
+#define CS530X_SYSCLK_SRC_PLL 1
+
+/* PLL Reference Clock Source */
+#define CS530X_PLL_SRC_BCLK 0
+#define CS530X_PLL_SRC_MCLK 1
+
+#define CS530X_NUM_SUPPLIES 2
+
+enum cs530x_type {
+ CS4282 = CS530X_2CH_CODEC_DEV_ID,
+ CS4302 = CS530X_2CH_DAC_DEV_ID,
+ CS4304 = CS530X_4CH_DAC_DEV_ID,
+ CS4308 = CS530X_8CH_DAC_DEV_ID,
+ CS5302 = CS530X_2CH_ADC_DEV_ID,
+ CS5304 = CS530X_4CH_ADC_DEV_ID,
+ CS5308 = CS530X_8CH_ADC_DEV_ID,
+};
+
+/* codec private data */
+struct cs530x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct snd_soc_dai_driver *dev_dai;
+
+ enum cs530x_type devtype;
+ int num_adcs;
+ int num_dacs;
+
+ struct regulator_bulk_data supplies[CS530X_NUM_SUPPLIES];
+
+ int tdm_width;
+ int tdm_slots;
+ int adc_pairs_count;
+ int dac_pairs_count;
+
+ struct gpio_desc *reset_gpio;
+};
+
+extern const struct regmap_config cs530x_regmap_i2c;
+extern const struct regmap_config cs530x_regmap_spi;
+int cs530x_probe(struct cs530x_priv *cs530x);
+
+#endif
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 000000000000..93ea2fb4dae9
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,1107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs53l30.c -- CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs53l30.h"
+#include "cirrus_legacy.h"
+
+#define CS53L30_NUM_SUPPLIES 2
+static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs53l30_private {
+ struct regulator_bulk_data supplies[CS53L30_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *mute_gpio;
+ struct clk *mclk;
+ bool use_sdout2;
+ u32 mclk_rate;
+};
+
+static const struct reg_default cs53l30_reg_defaults[] = {
+ { CS53L30_PWRCTL, CS53L30_PWRCTL_DEFAULT },
+ { CS53L30_MCLKCTL, CS53L30_MCLKCTL_DEFAULT },
+ { CS53L30_INT_SR_CTL, CS53L30_INT_SR_CTL_DEFAULT },
+ { CS53L30_MICBIAS_CTL, CS53L30_MICBIAS_CTL_DEFAULT },
+ { CS53L30_ASPCFG_CTL, CS53L30_ASPCFG_CTL_DEFAULT },
+ { CS53L30_ASP_CTL1, CS53L30_ASP_CTL1_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL1, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL2, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL3, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL4, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN1, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN2, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN3, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN4, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN5, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN6, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_CTL2, CS53L30_ASP_CTL2_DEFAULT },
+ { CS53L30_SFT_RAMP, CS53L30_SFT_RMP_DEFAULT },
+ { CS53L30_LRCK_CTL1, CS53L30_LRCK_CTLx_DEFAULT },
+ { CS53L30_LRCK_CTL2, CS53L30_LRCK_CTLx_DEFAULT },
+ { CS53L30_MUTEP_CTL1, CS53L30_MUTEP_CTL1_DEFAULT },
+ { CS53L30_MUTEP_CTL2, CS53L30_MUTEP_CTL2_DEFAULT },
+ { CS53L30_INBIAS_CTL1, CS53L30_INBIAS_CTL1_DEFAULT },
+ { CS53L30_INBIAS_CTL2, CS53L30_INBIAS_CTL2_DEFAULT },
+ { CS53L30_DMIC1_STR_CTL, CS53L30_DMIC1_STR_CTL_DEFAULT },
+ { CS53L30_DMIC2_STR_CTL, CS53L30_DMIC2_STR_CTL_DEFAULT },
+ { CS53L30_ADCDMIC1_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
+ { CS53L30_ADCDMIC1_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
+ { CS53L30_ADC1_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
+ { CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
+ { CS53L30_ADC1A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC1A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADC1B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADCDMIC2_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
+ { CS53L30_ADCDMIC2_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
+ { CS53L30_ADC2_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
+ { CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
+ { CS53L30_ADC2A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC2A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADC2B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_INT_MASK, CS53L30_DEVICE_INT_MASK },
+};
+
+static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == CS53L30_IS)
+ return true;
+ else
+ return false;
+}
+
+static bool cs53l30_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_IS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_PWRCTL:
+ case CS53L30_MCLKCTL:
+ case CS53L30_INT_SR_CTL:
+ case CS53L30_MICBIAS_CTL:
+ case CS53L30_ASPCFG_CTL:
+ case CS53L30_ASP_CTL1:
+ case CS53L30_ASP_TDMTX_CTL1:
+ case CS53L30_ASP_TDMTX_CTL2:
+ case CS53L30_ASP_TDMTX_CTL3:
+ case CS53L30_ASP_TDMTX_CTL4:
+ case CS53L30_ASP_TDMTX_EN1:
+ case CS53L30_ASP_TDMTX_EN2:
+ case CS53L30_ASP_TDMTX_EN3:
+ case CS53L30_ASP_TDMTX_EN4:
+ case CS53L30_ASP_TDMTX_EN5:
+ case CS53L30_ASP_TDMTX_EN6:
+ case CS53L30_ASP_CTL2:
+ case CS53L30_SFT_RAMP:
+ case CS53L30_LRCK_CTL1:
+ case CS53L30_LRCK_CTL2:
+ case CS53L30_MUTEP_CTL1:
+ case CS53L30_MUTEP_CTL2:
+ case CS53L30_INBIAS_CTL1:
+ case CS53L30_INBIAS_CTL2:
+ case CS53L30_DMIC1_STR_CTL:
+ case CS53L30_DMIC2_STR_CTL:
+ case CS53L30_ADCDMIC1_CTL1:
+ case CS53L30_ADCDMIC1_CTL2:
+ case CS53L30_ADC1_CTL3:
+ case CS53L30_ADC1_NG_CTL:
+ case CS53L30_ADC1A_AFE_CTL:
+ case CS53L30_ADC1B_AFE_CTL:
+ case CS53L30_ADC1A_DIG_VOL:
+ case CS53L30_ADC1B_DIG_VOL:
+ case CS53L30_ADCDMIC2_CTL1:
+ case CS53L30_ADCDMIC2_CTL2:
+ case CS53L30_ADC2_CTL3:
+ case CS53L30_ADC2_NG_CTL:
+ case CS53L30_ADC2A_AFE_CTL:
+ case CS53L30_ADC2B_AFE_CTL:
+ case CS53L30_ADC2A_DIG_VOL:
+ case CS53L30_ADC2B_DIG_VOL:
+ case CS53L30_INT_MASK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
+static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
+static DECLARE_TLV_DB_SCALE(pga_preamp_tlv, 0, 10000, 0);
+
+static const char * const input1_sel_text[] = {
+ "DMIC1 On AB In",
+ "DMIC1 On A In",
+ "DMIC1 On B In",
+ "ADC1 On AB In",
+ "ADC1 On A In",
+ "ADC1 On B In",
+ "DMIC1 Off ADC1 Off",
+};
+
+static unsigned int const input1_sel_values[] = {
+ CS53L30_CH_TYPE,
+ CS53L30_ADCxB_PDN | CS53L30_CH_TYPE,
+ CS53L30_ADCxA_PDN | CS53L30_CH_TYPE,
+ CS53L30_DMICx_PDN,
+ CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input2_sel_text[] = {
+ "DMIC2 On AB In",
+ "DMIC2 On A In",
+ "DMIC2 On B In",
+ "ADC2 On AB In",
+ "ADC2 On A In",
+ "ADC2 On B In",
+ "DMIC2 Off ADC2 Off",
+};
+
+static unsigned int const input2_sel_values[] = {
+ 0x0,
+ CS53L30_ADCxB_PDN,
+ CS53L30_ADCxA_PDN,
+ CS53L30_DMICx_PDN,
+ CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input1_route_sel_text[] = {
+ "ADC1_SEL", "DMIC1_SEL",
+};
+
+static const struct soc_enum input1_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, CS53L30_CH_TYPE_SHIFT,
+ ARRAY_SIZE(input1_route_sel_text),
+ input1_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
+ CS53L30_ADCDMICx_PDN_MASK, input1_sel_text,
+ input1_sel_values);
+
+static const struct snd_kcontrol_new input1_route_sel_mux =
+ SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
+
+static const char * const input2_route_sel_text[] = {
+ "ADC2_SEL", "DMIC2_SEL",
+};
+
+/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
+static const struct soc_enum input2_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+ ARRAY_SIZE(input2_route_sel_text),
+ input2_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
+ CS53L30_ADCDMICx_PDN_MASK, input2_sel_text,
+ input2_sel_values);
+
+static const struct snd_kcontrol_new input2_route_sel_mux =
+ SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
+
+/*
+ * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
+ * TB - Time base
+ * NOTE: If MCLK_INT_SCALE = 0, then TB=1
+ */
+static const char * const cs53l30_ng_delay_text[] = {
+ "TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms",
+};
+
+static const struct soc_enum adc1_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_delay_text),
+ cs53l30_ng_delay_text);
+
+static const struct soc_enum adc2_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_delay_text),
+ cs53l30_ng_delay_text);
+
+/* The noise gate threshold selected will depend on NG Boost */
+static const char * const cs53l30_ng_thres_text[] = {
+ "-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
+ "-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB",
+};
+
+static const struct soc_enum adc1_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_thres_text),
+ cs53l30_ng_thres_text);
+
+static const struct soc_enum adc2_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_thres_text),
+ cs53l30_ng_thres_text);
+
+/* Corner frequencies are with an Fs of 48kHz. */
+static const char * const hpf_corner_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz",
+};
+
+static const struct soc_enum adc1_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct soc_enum adc2_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
+ SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP,
+ CS53L30_DIGSFT_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1 Noise Gate Ganging Switch", CS53L30_ADC1_CTL3,
+ CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2 Noise Gate Ganging Switch", CS53L30_ADC2_CTL3,
+ CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1A Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCxA_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1B Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCxB_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2A Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCxA_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2B Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCxB_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1 Notch Filter Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 Notch Filter Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+ SOC_SINGLE("ADC1A Invert Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxA_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1B Invert Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxB_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2A Invert Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxA_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2B Invert Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxB_INV_SHIFT, 1, 0),
+
+ SOC_SINGLE_TLV("ADC1A Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1B Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2A Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2B Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1 NG Boost Volume", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+ SOC_SINGLE_TLV("ADC2 NG Boost Volume", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC1 Preamplifier Volume", CS53L30_ADC1A_AFE_CTL,
+ CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+ 2, 0, pga_preamp_tlv),
+ SOC_DOUBLE_R_TLV("ADC2 Preamplifier Volume", CS53L30_ADC2A_AFE_CTL,
+ CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+ 2, 0, pga_preamp_tlv),
+
+ SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
+ SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
+
+ SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
+ SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
+ SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
+ SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
+ SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
+ SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
+
+ SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
+ CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+ CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+ CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+ CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x24, pga_tlv),
+
+ SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+ CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+ CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+ CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+ CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN1_DMIC1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3_DMIC2"),
+ SND_SOC_DAPM_INPUT("IN4"),
+ SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC1_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC2_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC3_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC4_BIAS_PDN_SHIFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASP_SDOUT1", NULL, 0, CS53L30_ASP_CTL1,
+ CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("ASP_SDOUT2", NULL, 0, CS53L30_ASP_CTL2,
+ CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+
+ SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
+ &input1_route_sel_mux),
+ SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
+ &input2_route_sel_mux),
+
+ SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_ADCxA_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_ADCxB_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_ADCxA_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_ADCxB_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_DMICx_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_DMICx_PDN_SHIFT, 1),
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes[] = {
+ /* ADC Input Paths */
+ {"ADC1A", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "ADC1_SEL", "ADC1A"},
+ {"ADC1B", NULL, "IN2"},
+
+ {"ADC2A", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "ADC2_SEL", "ADC2A"},
+ {"ADC2B", NULL, "IN4"},
+
+ /* MIC Bias Paths */
+ {"ADC1A", NULL, "MIC1 Bias"},
+ {"ADC1B", NULL, "MIC2 Bias"},
+ {"ADC2A", NULL, "MIC3 Bias"},
+ {"ADC2B", NULL, "MIC4 Bias"},
+
+ /* DMIC Paths */
+ {"DMIC1", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "DMIC1_SEL", "DMIC1"},
+
+ {"DMIC2", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "DMIC2_SEL", "DMIC2"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout1[] = {
+ /* Output Paths when using SDOUT1 only */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT1", NULL, "ADC2A"},
+ {"ASP_SDOUT1", NULL, "Input Mux 2"},
+ {"ASP_SDOUT1", NULL, "ADC2B"},
+
+ {"Capture", NULL, "ASP_SDOUT1"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout2[] = {
+ /* Output Paths when using both SDOUT1 and SDOUT2 */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT2", NULL, "ADC2A"},
+ {"ASP_SDOUT2", NULL, "Input Mux 2"},
+ {"ASP_SDOUT2", NULL, "ADC2B"},
+
+ {"Capture", NULL, "ASP_SDOUT1"},
+ {"Capture", NULL, "ASP_SDOUT2"},
+};
+
+struct cs53l30_mclk_div {
+ u32 mclk_rate;
+ u32 srate;
+ u8 asp_rate;
+ u8 internal_fs_ratio;
+ u8 mclk_int_scale;
+};
+
+static const struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
+ /* NOTE: Enable MCLK_INT_SCALE to save power. */
+
+ /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
+ {5644800, 11025, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {5644800, 22050, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {5644800, 44100, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+ {6000000, 8000, 0x1, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 11025, 0x2, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 12000, 0x4, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 16000, 0x5, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 22050, 0x6, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 24000, 0x8, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 32000, 0x9, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 44100, 0xA, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 48000, 0xC, 0, CS53L30_MCLK_INT_SCALE},
+
+ {6144000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+ {6400000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+};
+
+struct cs53l30_mclkx_div {
+ u32 mclkx;
+ u8 ratio;
+ u8 mclkdiv;
+};
+
+static const struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
+ {5644800, 1, CS53L30_MCLK_DIV_BY_1},
+ {6000000, 1, CS53L30_MCLK_DIV_BY_1},
+ {6144000, 1, CS53L30_MCLK_DIV_BY_1},
+ {11289600, 2, CS53L30_MCLK_DIV_BY_2},
+ {12288000, 2, CS53L30_MCLK_DIV_BY_2},
+ {12000000, 2, CS53L30_MCLK_DIV_BY_2},
+ {19200000, 3, CS53L30_MCLK_DIV_BY_3},
+};
+
+static int cs53l30_get_mclkx_coeff(int mclkx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
+ if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int cs53l30_get_mclk_coeff(int mclk_rate, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
+ if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate &&
+ cs53l30_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ int mclkx_coeff;
+ u32 mclk_rate;
+
+ /* MCLKX -> MCLK */
+ mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
+ if (mclkx_coeff < 0)
+ return mclkx_coeff;
+
+ mclk_rate = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
+ cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIV_MASK,
+ cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv);
+
+ priv->mclk_rate = mclk_rate;
+
+ return 0;
+}
+
+static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ u8 aspcfg = 0, aspctl1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ aspcfg |= CS53L30_ASP_MS;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Set TDM_PDN to turn off TDM mode -- Reset default */
+ aspctl1 |= CS53L30_ASP_TDM_PDN;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0
+ * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in
+ * the CS53L30 datasheet
+ */
+ aspctl1 |= CS53L30_SHIFT_LEFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Check to see if the SCLK is inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ aspcfg ^= CS53L30_ASP_SCLK_INV;
+ break;
+ default:
+ break;
+ }
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1);
+
+ return 0;
+}
+
+static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ int srate = params_rate(params);
+ int mclk_coeff;
+
+ /* MCLK -> srate */
+ mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk_rate, srate);
+ if (mclk_coeff < 0)
+ return -EINVAL;
+
+ regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
+ CS53L30_INTRNL_FS_RATIO_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio);
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_INT_SCALE_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_RATE_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].asp_rate);
+
+ return 0;
+}
+
+static int cs53l30_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(component);
+ enum snd_soc_bias_level bias_level = snd_soc_dapm_get_bias_level(dapm);
+ unsigned int reg;
+ int i, inter_max_check, ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ if (bias_level == SND_SOC_BIAS_STANDBY)
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_LP_MASK, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (bias_level == SND_SOC_BIAS_OFF) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS_MASK, 0);
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK, 0);
+ msleep(50);
+ } else {
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK,
+ CS53L30_PDN_ULP);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, 0);
+ /*
+ * If digital softramp is set, the amount of time required
+ * for power down increases and depends on the digital
+ * volume setting.
+ */
+
+ /* Set the max possible time if digsft is set */
+ regmap_read(priv->regmap, CS53L30_SFT_RAMP, &reg);
+ if (reg & CS53L30_DIGSFT_MASK)
+ inter_max_check = CS53L30_PDN_POLL_MAX;
+ else
+ inter_max_check = 10;
+
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK,
+ CS53L30_PDN_ULP);
+ /* PDN_DONE will take a min of 20ms to be set.*/
+ msleep(20);
+ /* Clr status */
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ for (i = 0; i < inter_max_check; i++) {
+ if (inter_max_check < 10) {
+ usleep_range(1000, 1100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ } else {
+ usleep_range(10000, 10100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ }
+ }
+ /* PDN_DONE is set. We now can disable the MCLK */
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, CS53L30_PDN_DONE);
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS_MASK,
+ CS53L30_MCLK_DIS);
+ clk_disable_unprepare(priv->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ u8 val = tristate ? CS53L30_ASP_3ST : 0;
+
+ return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_3ST_MASK, val);
+}
+
+/*
+ * Note: CS53L30 counts the slot number per byte while ASoC counts the slot
+ * number per slot_width. So there is a difference between the slots of ASoC
+ * and the slots of CS53L30.
+ */
+static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+ unsigned int loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48};
+ unsigned int slot_next, slot_step;
+ u64 tx_enable = 0;
+ int i;
+
+ if (!rx_mask) {
+ dev_err(dai->dev, "rx masks must not be 0\n");
+ return -EINVAL;
+ }
+
+ /* Assuming slot_width is not supposed to be greater than 64 */
+ if (slots <= 0 || slot_width <= 0 || slot_width > 64) {
+ dev_err(dai->dev, "invalid slot number or slot width\n");
+ return -EINVAL;
+ }
+
+ if (slot_width & 0x7) {
+ dev_err(dai->dev, "slot width must count in byte\n");
+ return -EINVAL;
+ }
+
+ /* How many bytes in each ASoC slot */
+ slot_step = slot_width >> 3;
+
+ for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) {
+ /* Find the first slot from LSB */
+ slot_next = __ffs(rx_mask);
+ /* Save the slot location by converting to CS53L30 slot */
+ loc[i] = slot_next * slot_step;
+ /* Create the mask of CS53L30 slot */
+ tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i];
+ /* Clear this slot from rx_mask */
+ rx_mask &= ~(1 << slot_next);
+ }
+
+ /* Error out to avoid slot shift */
+ if (rx_mask && i == CS53L30_TDM_SLOT_MAX) {
+ dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n",
+ CS53L30_TDM_SLOT_MAX);
+ return -EINVAL;
+ }
+
+ /* Validate the last active CS53L30 slot */
+ slot_next = loc[i - 1] + slot_step - 1;
+ if (slot_next > 47) {
+ dev_err(dai->dev, "slot selection out of bounds: %u\n",
+ slot_next);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) {
+ regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i),
+ CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]);
+ dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]);
+ }
+
+ for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) {
+ regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i),
+ tx_enable & 0xff);
+ tx_enable >>= 8;
+ dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n",
+ CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff);
+ }
+
+ return 0;
+}
+
+static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
+
+ gpiod_set_value_cansleep(priv->mute_gpio, mute);
+
+ return 0;
+}
+
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+ .hw_params = cs53l30_pcm_hw_params,
+ .set_fmt = cs53l30_set_dai_fmt,
+ .set_sysclk = cs53l30_set_sysclk,
+ .set_tristate = cs53l30_set_tristate,
+ .set_tdm_slot = cs53l30_set_dai_tdm_slot,
+ .mute_stream = cs53l30_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai = {
+ .name = "cs53l30",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS53L30_RATES,
+ .formats = CS53L30_FORMATS,
+ },
+ .ops = &cs53l30_ops,
+ .symmetric_rate = 1,
+};
+
+static int cs53l30_component_probe(struct snd_soc_component *component)
+{
+ struct cs53l30_private *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ if (priv->use_sdout2)
+ snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout2,
+ ARRAY_SIZE(cs53l30_dapm_routes_sdout2));
+ else
+ snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout1,
+ ARRAY_SIZE(cs53l30_dapm_routes_sdout1));
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver cs53l30_driver = {
+ .probe = cs53l30_component_probe,
+ .set_bias_level = cs53l30_set_bias_level,
+ .controls = cs53l30_snd_controls,
+ .num_controls = ARRAY_SIZE(cs53l30_snd_controls),
+ .dapm_widgets = cs53l30_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
+ .dapm_routes = cs53l30_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config cs53l30_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS53L30_MAX_REGISTER,
+ .reg_defaults = cs53l30_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+ .volatile_reg = cs53l30_volatile_register,
+ .writeable_reg = cs53l30_writeable_register,
+ .readable_reg = cs53l30_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client)
+{
+ const struct device_node *np = client->dev.of_node;
+ struct device *dev = &client->dev;
+ struct cs53l30_private *cs53l30;
+ unsigned int reg;
+ int ret = 0, i, devid;
+ u8 val;
+
+ cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
+ if (!cs53l30)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++)
+ cs53l30->supplies[i].supply = cs53l30_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs53l30->reset_gpio)) {
+ ret = PTR_ERR(cs53l30->reset_gpio);
+ goto error_supplies;
+ }
+
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ i2c_set_clientdata(client, cs53l30);
+
+ cs53l30->mclk_rate = 0;
+
+ cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
+ if (IS_ERR(cs53l30->regmap)) {
+ ret = PTR_ERR(cs53l30->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ goto error;
+ }
+
+ /* Initialize codec */
+ devid = cirrus_read_device_id(cs53l30->regmap, CS53L30_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto error;
+ }
+
+ if (devid != CS53L30_DEVID) {
+ ret = -ENODEV;
+ dev_err(dev, "Device ID (%X). Expected %X\n",
+ devid, CS53L30_DEVID);
+ goto error;
+ }
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_REVID, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to get Revision ID: %d\n", ret);
+ goto error;
+ }
+
+ /* Check if MCLK provided */
+ cs53l30->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(cs53l30->mclk)) {
+ ret = PTR_ERR(cs53l30->mclk);
+ goto error;
+ }
+
+ /* Fetch the MUTE control */
+ cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs53l30->mute_gpio)) {
+ ret = PTR_ERR(cs53l30->mute_gpio);
+ goto error;
+ }
+
+ if (cs53l30->mute_gpio) {
+ /* Enable MUTE controls via MUTE pin */
+ regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1,
+ CS53L30_MUTEP_CTL1_MUTEALL);
+ /* Flip the polarity of MUTE pin */
+ if (gpiod_is_active_low(cs53l30->mute_gpio))
+ regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2,
+ CS53L30_MUTE_PIN_POLARITY, 0);
+ }
+
+ if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val))
+ regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL,
+ CS53L30_MIC_BIAS_CTRL_MASK, val);
+
+ if (of_property_read_bool(np, "cirrus,use-sdout2"))
+ cs53l30->use_sdout2 = true;
+
+ dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
+
+ ret = devm_snd_soc_register_component(dev, &cs53l30_driver, &cs53l30_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register component: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+error_supplies:
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ return ret;
+}
+
+static void cs53l30_i2c_remove(struct i2c_client *client)
+{
+ struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+}
+
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs53l30->regmap, true);
+
+ /* Hold down reset */
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+
+ return 0;
+}
+
+static int cs53l30_runtime_resume(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ regcache_cache_only(cs53l30->regmap, false);
+ ret = regcache_sync(cs53l30->regmap);
+ if (ret) {
+ dev_err(dev, "failed to synchronize regcache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+ RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume, NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+ { .compatible = "cirrus,cs53l30", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+ { "cs53l30" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+ .driver = {
+ .name = "cs53l30",
+ .of_match_table = cs53l30_of_match,
+ .pm = pm_ptr(&cs53l30_runtime_pm),
+ },
+ .id_table = cs53l30_id,
+ .probe = cs53l30_i2c_probe,
+ .remove = cs53l30_i2c_remove,
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644
index 000000000000..071547c55719
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.h
@@ -0,0 +1,455 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#ifndef __CS53L30_H__
+#define __CS53L30_H__
+
+/* I2C Registers */
+#define CS53L30_DEVID_AB 0x01 /* Device ID A & B [RO]. */
+#define CS53L30_DEVID_CD 0x02 /* Device ID C & D [RO]. */
+#define CS53L30_DEVID_E 0x03 /* Device ID E [RO]. */
+#define CS53L30_REVID 0x05 /* Revision ID [RO]. */
+#define CS53L30_PWRCTL 0x06 /* Power Control. */
+#define CS53L30_MCLKCTL 0x07 /* MCLK Control. */
+#define CS53L30_INT_SR_CTL 0x08 /* Internal Sample Rate Control. */
+#define CS53L30_MICBIAS_CTL 0x0A /* Mic Bias Control. */
+#define CS53L30_ASPCFG_CTL 0x0C /* ASP Config Control. */
+#define CS53L30_ASP_CTL1 0x0D /* ASP1 Control. */
+#define CS53L30_ASP_TDMTX_CTL1 0x0E /* ASP1 TDM TX Control 1 */
+#define CS53L30_ASP_TDMTX_CTL2 0x0F /* ASP1 TDM TX Control 2 */
+#define CS53L30_ASP_TDMTX_CTL3 0x10 /* ASP1 TDM TX Control 3 */
+#define CS53L30_ASP_TDMTX_CTL4 0x11 /* ASP1 TDM TX Control 4 */
+#define CS53L30_ASP_TDMTX_EN1 0x12 /* ASP1 TDM TX Enable 1 */
+#define CS53L30_ASP_TDMTX_EN2 0x13 /* ASP1 TDM TX Enable 2 */
+#define CS53L30_ASP_TDMTX_EN3 0x14 /* ASP1 TDM TX Enable 3 */
+#define CS53L30_ASP_TDMTX_EN4 0x15 /* ASP1 TDM TX Enable 4 */
+#define CS53L30_ASP_TDMTX_EN5 0x16 /* ASP1 TDM TX Enable 5 */
+#define CS53L30_ASP_TDMTX_EN6 0x17 /* ASP1 TDM TX Enable 6 */
+#define CS53L30_ASP_CTL2 0x18 /* ASP2 Control. */
+#define CS53L30_SFT_RAMP 0x1A /* Soft Ramp Control. */
+#define CS53L30_LRCK_CTL1 0x1B /* LRCK Control 1. */
+#define CS53L30_LRCK_CTL2 0x1C /* LRCK Control 2. */
+#define CS53L30_MUTEP_CTL1 0x1F /* Mute Pin Control 1. */
+#define CS53L30_MUTEP_CTL2 0x20 /* Mute Pin Control 2. */
+#define CS53L30_INBIAS_CTL1 0x21 /* Input Bias Control 1. */
+#define CS53L30_INBIAS_CTL2 0x22 /* Input Bias Control 2. */
+#define CS53L30_DMIC1_STR_CTL 0x23 /* DMIC1 Stereo Control. */
+#define CS53L30_DMIC2_STR_CTL 0x24 /* DMIC2 Stereo Control. */
+#define CS53L30_ADCDMIC1_CTL1 0x25 /* ADC1/DMIC1 Control 1. */
+#define CS53L30_ADCDMIC1_CTL2 0x26 /* ADC1/DMIC1 Control 2. */
+#define CS53L30_ADC1_CTL3 0x27 /* ADC1 Control 3. */
+#define CS53L30_ADC1_NG_CTL 0x28 /* ADC1 Noise Gate Control. */
+#define CS53L30_ADC1A_AFE_CTL 0x29 /* ADC1A AFE Control. */
+#define CS53L30_ADC1B_AFE_CTL 0x2A /* ADC1B AFE Control. */
+#define CS53L30_ADC1A_DIG_VOL 0x2B /* ADC1A Digital Volume. */
+#define CS53L30_ADC1B_DIG_VOL 0x2C /* ADC1B Digital Volume. */
+#define CS53L30_ADCDMIC2_CTL1 0x2D /* ADC2/DMIC2 Control 1. */
+#define CS53L30_ADCDMIC2_CTL2 0x2E /* ADC2/DMIC2 Control 2. */
+#define CS53L30_ADC2_CTL3 0x2F /* ADC2 Control 3. */
+#define CS53L30_ADC2_NG_CTL 0x30 /* ADC2 Noise Gate Control. */
+#define CS53L30_ADC2A_AFE_CTL 0x31 /* ADC2A AFE Control. */
+#define CS53L30_ADC2B_AFE_CTL 0x32 /* ADC2B AFE Control. */
+#define CS53L30_ADC2A_DIG_VOL 0x33 /* ADC2A Digital Volume. */
+#define CS53L30_ADC2B_DIG_VOL 0x34 /* ADC2B Digital Volume. */
+#define CS53L30_INT_MASK 0x35 /* Interrupt Mask. */
+#define CS53L30_IS 0x36 /* Interrupt Status. */
+#define CS53L30_MAX_REGISTER 0x36
+
+#define CS53L30_TDM_SLOT_MAX 4
+#define CS53L30_ASP_TDMTX_CTL(x) (CS53L30_ASP_TDMTX_CTL1 + (x))
+/* x : index for registers; n : index for slot; 8 slots per register */
+#define CS53L30_ASP_TDMTX_ENx(x) (CS53L30_ASP_TDMTX_EN6 - (x))
+#define CS53L30_ASP_TDMTX_ENn(n) CS53L30_ASP_TDMTX_ENx((n) >> 3)
+#define CS53L30_ASP_TDMTX_ENx_MAX 6
+
+/* Device ID */
+#define CS53L30_DEVID 0x53A30
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define CS53L30_PDN_POLL_MAX 90
+
+/* Bitfield Definitions */
+
+/* R6 (0x06) CS53L30_PWRCTL - Power Control */
+#define CS53L30_PDN_ULP_SHIFT 7
+#define CS53L30_PDN_ULP_MASK (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_ULP (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_LP_SHIFT 6
+#define CS53L30_PDN_LP_MASK (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_PDN_LP (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_DISCHARGE_FILT_SHIFT 5
+#define CS53L30_DISCHARGE_FILT_MASK (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_DISCHARGE_FILT (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_THMS_PDN_SHIFT 4
+#define CS53L30_THMS_PDN_MASK (1 << CS53L30_THMS_PDN_SHIFT)
+#define CS53L30_THMS_PDN (1 << CS53L30_THMS_PDN_SHIFT)
+
+#define CS53L30_PWRCTL_DEFAULT (CS53L30_THMS_PDN)
+
+/* R7 (0x07) CS53L30_MCLKCTL - MCLK Control */
+#define CS53L30_MCLK_DIS_SHIFT 7
+#define CS53L30_MCLK_DIS_MASK (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_DIS (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_INT_SCALE_SHIFT 6
+#define CS53L30_MCLK_INT_SCALE_MASK (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_MCLK_INT_SCALE (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_DMIC_DRIVE_SHIFT 5
+#define CS53L30_DMIC_DRIVE_MASK (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_DMIC_DRIVE (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_MCLK_DIV_SHIFT 2
+#define CS53L30_MCLK_DIV_WIDTH 2
+#define CS53L30_MCLK_DIV_MASK (((1 << CS53L30_MCLK_DIV_WIDTH) - 1) << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_1 (0x0 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_2 (0x1 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_3 (0x2 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_SYNC_EN_SHIFT 1
+#define CS53L30_SYNC_EN_MASK (1 << CS53L30_SYNC_EN_SHIFT)
+#define CS53L30_SYNC_EN (1 << CS53L30_SYNC_EN_SHIFT)
+
+#define CS53L30_MCLKCTL_DEFAULT (CS53L30_MCLK_DIV_BY_2)
+
+/* R8 (0x08) CS53L30_INT_SR_CTL - Internal Sample Rate Control */
+#define CS53L30_INTRNL_FS_RATIO_SHIFT 4
+#define CS53L30_INTRNL_FS_RATIO_MASK (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_INTRNL_FS_RATIO (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN_SHIFT 0
+#define CS53L30_MCLK_19MHZ_EN_MASK (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+
+/* 0x6 << 1 is reserved bits */
+#define CS53L30_INT_SR_CTL_DEFAULT (CS53L30_INTRNL_FS_RATIO | 0x6 << 1)
+
+/* R10 (0x0A) CS53L30_MICBIAS_CTL - Mic Bias Control */
+#define CS53L30_MIC4_BIAS_PDN_SHIFT 7
+#define CS53L30_MIC4_BIAS_PDN_MASK (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC4_BIAS_PDN (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN_SHIFT 6
+#define CS53L30_MIC3_BIAS_PDN_MASK (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN_SHIFT 5
+#define CS53L30_MIC2_BIAS_PDN_MASK (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN_SHIFT 4
+#define CS53L30_MIC1_BIAS_PDN_MASK (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MICx_BIAS_PDN (0xf << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_VP_MIN_SHIFT 2
+#define CS53L30_VP_MIN_MASK (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_VP_MIN (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_SHIFT 0
+#define CS53L30_MIC_BIAS_CTRL_WIDTH 2
+#define CS53L30_MIC_BIAS_CTRL_MASK (((1 << CS53L30_MIC_BIAS_CTRL_WIDTH) - 1) << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_HIZ (0 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_1V8 (1 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_2V75 (2 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+
+#define CS53L30_MICBIAS_CTL_DEFAULT (CS53L30_MICx_BIAS_PDN | CS53L30_VP_MIN)
+
+/* R12 (0x0C) CS53L30_ASPCFG_CTL - ASP Configuration Control */
+#define CS53L30_ASP_MS_SHIFT 7
+#define CS53L30_ASP_MS_MASK (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_MS (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_SCLK_INV_SHIFT 4
+#define CS53L30_ASP_SCLK_INV_MASK (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_SCLK_INV (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_RATE_SHIFT 0
+#define CS53L30_ASP_RATE_WIDTH 4
+#define CS53L30_ASP_RATE_MASK (((1 << CS53L30_ASP_RATE_WIDTH) - 1) << CS53L30_ASP_RATE_SHIFT)
+#define CS53L30_ASP_RATE_48K (0xc << CS53L30_ASP_RATE_SHIFT)
+
+#define CS53L30_ASPCFG_CTL_DEFAULT (CS53L30_ASP_RATE_48K)
+
+/* R13/R24 (0x0D/0x18) CS53L30_ASP_CTL1 & CS53L30_ASP_CTL2 - ASP Control 1~2 */
+#define CS53L30_ASP_TDM_PDN_SHIFT 7
+#define CS53L30_ASP_TDM_PDN_MASK (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_TDM_PDN (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN_SHIFT 6
+#define CS53L30_ASP_SDOUTx_PDN_MASK (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_3ST_SHIFT 5
+#define CS53L30_ASP_3ST_MASK (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_ASP_3ST (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_SHIFT_LEFT_SHIFT 4
+#define CS53L30_SHIFT_LEFT_MASK (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_SHIFT_LEFT (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE_SHIFT 0
+#define CS53L30_ASP_SDOUTx_DRIVE_MASK (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+
+#define CS53L30_ASP_CTL1_DEFAULT (CS53L30_ASP_TDM_PDN)
+#define CS53L30_ASP_CTL2_DEFAULT (0)
+
+/* R14 (0x0E) ~ R17 (0x11) CS53L30_ASP_TDMTX_CTLx - ASP TDM TX Control 1~4 */
+#define CS53L30_ASP_CHx_TX_STATE_SHIFT 7
+#define CS53L30_ASP_CHx_TX_STATE_MASK (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_STATE (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_SHIFT 0
+#define CS53L30_ASP_CHx_TX_LOC_WIDTH 6
+#define CS53L30_ASP_CHx_TX_LOC_MASK (((1 << CS53L30_ASP_CHx_TX_LOC_WIDTH) - 1) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_MAX (47 << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC(x) ((x) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+
+#define CS53L30_ASP_TDMTX_CTLx_DEFAULT (CS53L30_ASP_CHx_TX_LOC_MAX)
+
+/* R18 (0x12) ~ R23 (0x17) CS53L30_ASP_TDMTX_ENx - ASP TDM TX Enable 1~6 */
+#define CS53L30_ASP_TDMTX_ENx_DEFAULT (0)
+
+/* R26 (0x1A) CS53L30_SFT_RAMP - Soft Ramp Control */
+#define CS53L30_DIGSFT_SHIFT 5
+#define CS53L30_DIGSFT_MASK (1 << CS53L30_DIGSFT_SHIFT)
+#define CS53L30_DIGSFT (1 << CS53L30_DIGSFT_SHIFT)
+
+#define CS53L30_SFT_RMP_DEFAULT (0)
+
+/* R28 (0x1C) CS53L30_LRCK_CTL2 - LRCK Control 2 */
+#define CS53L30_LRCK_50_NPW_SHIFT 3
+#define CS53L30_LRCK_50_NPW_MASK (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_50_NPW (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_TPWH_SHIFT 0
+#define CS53L30_LRCK_TPWH_WIDTH 3
+#define CS53L30_LRCK_TPWH_MASK (((1 << CS53L30_LRCK_TPWH_WIDTH) - 1) << CS53L30_LRCK_TPWH_SHIFT)
+#define CS53L30_LRCK_TPWH(x) (((x) << CS53L30_LRCK_TPWH_SHIFT) & CS53L30_LRCK_TPWH_MASK)
+
+#define CS53L30_LRCK_CTLx_DEFAULT (0)
+
+/* R31 (0x1F) CS53L30_MUTEP_CTL1 - MUTE Pin Control 1 */
+#define CS53L30_MUTE_PDN_ULP_SHIFT 7
+#define CS53L30_MUTE_PDN_ULP_MASK (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_ULP (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_LP_SHIFT 6
+#define CS53L30_MUTE_PDN_LP_MASK (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_PDN_LP (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_M4B_PDN_SHIFT 4
+#define CS53L30_MUTE_M4B_PDN_MASK (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M4B_PDN (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN_SHIFT 3
+#define CS53L30_MUTE_M3B_PDN_MASK (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN_SHIFT 2
+#define CS53L30_MUTE_M2B_PDN_MASK (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN_SHIFT 1
+#define CS53L30_MUTE_M1B_PDN_MASK (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_MxB_PDN_SHIFT(x) (CS53L30_MUTE_M1B_PDN_SHIFT + (x))
+#define CS53L30_MUTE_MxB_PDN_MASK(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MxB_PDN(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MB_ALL_PDN_SHIFT 0
+#define CS53L30_MUTE_MB_ALL_PDN_MASK (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+#define CS53L30_MUTE_MB_ALL_PDN (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL1_MUTEALL (0xdf)
+#define CS53L30_MUTEP_CTL1_DEFAULT (0)
+
+/* R32 (0x20) CS53L30_MUTEP_CTL2 - MUTE Pin Control 2 */
+#define CS53L30_MUTE_PIN_POLARITY_SHIFT 7
+#define CS53L30_MUTE_PIN_POLARITY_MASK (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_PIN_POLARITY (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN_SHIFT 6
+#define CS53L30_MUTE_ASP_TDM_PDN_MASK (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT 5
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT 4
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x) ((x) + CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_MASK(x) (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ASP_SDOUTx_PDN (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ADC2B_PDN_SHIFT 3
+#define CS53L30_MUTE_ADC2B_PDN_MASK (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2B_PDN (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN_SHIFT 2
+#define CS53L30_MUTE_ADC2A_PDN_MASK (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN_SHIFT 1
+#define CS53L30_MUTE_ADC1B_PDN_MASK (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN_SHIFT 0
+#define CS53L30_MUTE_ADC1A_PDN_MASK (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL2_DEFAULT (CS53L30_MUTE_PIN_POLARITY)
+
+/* R33 (0x21) CS53L30_INBIAS_CTL1 - Input Bias Control 1 */
+#define CS53L30_IN4M_BIAS_SHIFT 6
+#define CS53L30_IN4M_BIAS_WIDTH 2
+#define CS53L30_IN4M_BIAS_MASK (((1 << CS53L30_IN4M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_OPEN (0 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_PULL_DOWN (1 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_VCM (2 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_SHIFT 4
+#define CS53L30_IN4P_BIAS_WIDTH 2
+#define CS53L30_IN4P_BIAS_MASK (((1 << CS53L30_IN4P_BIAS_WIDTH) - 1) << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_OPEN (0 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_PULL_DOWN (1 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_VCM (2 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_SHIFT 2
+#define CS53L30_IN3M_BIAS_WIDTH 2
+#define CS53L30_IN3M_BIAS_MASK (((1 << CS53L30_IN3M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_OPEN (0 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_PULL_DOWN (1 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_VCM (2 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_SHIFT 0
+#define CS53L30_IN3P_BIAS_WIDTH 2
+#define CS53L30_IN3P_BIAS_MASK (((1 << CS53L30_IN3P_BIAS_WIDTH) - 1) << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_OPEN (0 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_PULL_DOWN (1 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_VCM (2 << CS53L30_IN3P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL1_DEFAULT (CS53L30_IN4M_BIAS_VCM | CS53L30_IN4P_BIAS_VCM |\
+ CS53L30_IN3M_BIAS_VCM | CS53L30_IN3P_BIAS_VCM)
+
+/* R34 (0x22) CS53L30_INBIAS_CTL2 - Input Bias Control 2 */
+#define CS53L30_IN2M_BIAS_SHIFT 6
+#define CS53L30_IN2M_BIAS_WIDTH 2
+#define CS53L30_IN2M_BIAS_MASK (((1 << CS53L30_IN2M_BIAS_WIDTH) - 1) << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_OPEN (0 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_PULL_DOWN (1 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_VCM (2 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_SHIFT 4
+#define CS53L30_IN2P_BIAS_WIDTH 2
+#define CS53L30_IN2P_BIAS_MASK (((1 << CS53L30_IN2P_BIAS_WIDTH) - 1) << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_OPEN (0 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_PULL_DOWN (1 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_VCM (2 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_SHIFT 2
+#define CS53L30_IN1M_BIAS_WIDTH 2
+#define CS53L30_IN1M_BIAS_MASK (((1 << CS53L30_IN1M_BIAS_WIDTH) - 1) << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_OPEN (0 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_PULL_DOWN (1 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_VCM (2 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_SHIFT 0
+#define CS53L30_IN1P_BIAS_WIDTH 2
+#define CS53L30_IN1P_BIAS_MASK (((1 << CS53L30_IN1P_BIAS_WIDTH) - 1) << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_OPEN (0 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_PULL_DOWN (1 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_VCM (2 << CS53L30_IN1P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL2_DEFAULT (CS53L30_IN2M_BIAS_VCM | CS53L30_IN2P_BIAS_VCM |\
+ CS53L30_IN1M_BIAS_VCM | CS53L30_IN1P_BIAS_VCM)
+
+/* R35 (0x23) & R36 (0x24) CS53L30_DMICx_STR_CTL - DMIC1 & DMIC2 Stereo Control */
+#define CS53L30_DMICx_STEREO_ENB_SHIFT 5
+#define CS53L30_DMICx_STEREO_ENB_MASK (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+#define CS53L30_DMICx_STEREO_ENB (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+
+/* 0x88 and 0xCC are reserved bits */
+#define CS53L30_DMIC1_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0x88)
+#define CS53L30_DMIC2_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0xCC)
+
+/* R37/R45 (0x25/0x2D) CS53L30_ADCDMICx_CTL1 - ADC1/DMIC1 & ADC2/DMIC2 Control 1 */
+#define CS53L30_ADCxB_PDN_SHIFT 7
+#define CS53L30_ADCxB_PDN_MASK (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxB_PDN (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN_SHIFT 6
+#define CS53L30_ADCxA_PDN_MASK (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_DMICx_PDN_SHIFT 2
+#define CS53L30_DMICx_PDN_MASK (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_PDN (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV_SHIFT 1
+#define CS53L30_DMICx_SCLK_DIV_MASK (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_CH_TYPE_SHIFT 0
+#define CS53L30_CH_TYPE_MASK (1 << CS53L30_CH_TYPE_SHIFT)
+#define CS53L30_CH_TYPE (1 << CS53L30_CH_TYPE_SHIFT)
+
+#define CS53L30_ADCDMICx_PDN_MASK 0xFF
+#define CS53L30_ADCDMICx_CTL1_DEFAULT (CS53L30_DMICx_PDN)
+
+/* R38/R46 (0x26/0x2E) CS53L30_ADCDMICx_CTL2 - ADC1/DMIC1 & ADC2/DMIC2 Control 2 */
+#define CS53L30_ADCx_NOTCH_DIS_SHIFT 7
+#define CS53L30_ADCx_NOTCH_DIS_MASK (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCx_NOTCH_DIS (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCxB_INV_SHIFT 5
+#define CS53L30_ADCxB_INV_MASK (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxB_INV (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxA_INV_SHIFT 4
+#define CS53L30_ADCxA_INV_MASK (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxA_INV (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST_SHIFT 1
+#define CS53L30_ADCxB_DIG_BOOST_MASK (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST_SHIFT 0
+#define CS53L30_ADCxA_DIG_BOOST_MASK (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+
+#define CS53L30_ADCDMIC1_CTL2_DEFAULT (0)
+
+/* R39/R47 (0x27/0x2F) CS53L30_ADCx_CTL3 - ADC1/ADC2 Control 3 */
+#define CS53L30_ADCx_HPF_EN_SHIFT 3
+#define CS53L30_ADCx_HPF_EN_MASK (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_EN (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_CF_SHIFT 1
+#define CS53L30_ADCx_HPF_CF_WIDTH 2
+#define CS53L30_ADCx_HPF_CF_MASK (((1 << CS53L30_ADCx_HPF_CF_WIDTH) - 1) << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_1HZ86 (0 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_120HZ (1 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_235HZ (2 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_466HZ (3 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_NG_ALL_SHIFT 0
+#define CS53L30_ADCx_NG_ALL_MASK (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+#define CS53L30_ADCx_NG_ALL (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+
+#define CS53L30_ADCx_CTL3_DEFAULT (CS53L30_ADCx_HPF_EN)
+
+/* R40/R48 (0x28/0x30) CS53L30_ADCx_NG_CTL - ADC1/ADC2 Noise Gate Control */
+#define CS53L30_ADCxB_NG_SHIFT 7
+#define CS53L30_ADCxB_NG_MASK (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxB_NG (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxA_NG_SHIFT 6
+#define CS53L30_ADCxA_NG_MASK (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCxA_NG (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCx_NG_BOOST_SHIFT 5
+#define CS53L30_ADCx_NG_BOOST_MASK (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_BOOST (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_THRESH_SHIFT 2
+#define CS53L30_ADCx_NG_THRESH_WIDTH 3
+#define CS53L30_ADCx_NG_THRESH_MASK (((1 << CS53L30_ADCx_NG_THRESH_WIDTH) - 1) << CS53L30_ADCx_NG_THRESH_SHIFT)
+#define CS53L30_ADCx_NG_DELAY_SHIFT 0
+#define CS53L30_ADCx_NG_DELAY_WIDTH 2
+#define CS53L30_ADCx_NG_DELAY_MASK (((1 << CS53L30_ADCx_NG_DELAY_WIDTH) - 1) << CS53L30_ADCx_NG_DELAY_SHIFT)
+
+#define CS53L30_ADCx_NG_CTL_DEFAULT (0)
+
+/* R41/R42/R49/R50 (0x29/0x2A/0x31/0x32) CS53L30_ADCxy_AFE_CTL - ADC1A/1B/2A/2B AFE Control */
+#define CS53L30_ADCxy_PREAMP_SHIFT 6
+#define CS53L30_ADCxy_PREAMP_WIDTH 2
+#define CS53L30_ADCxy_PREAMP_MASK (((1 << CS53L30_ADCxy_PREAMP_WIDTH) - 1) << CS53L30_ADCxy_PREAMP_SHIFT)
+#define CS53L30_ADCxy_PGA_VOL_SHIFT 0
+#define CS53L30_ADCxy_PGA_VOL_WIDTH 6
+#define CS53L30_ADCxy_PGA_VOL_MASK (((1 << CS53L30_ADCxy_PGA_VOL_WIDTH) - 1) << CS53L30_ADCxy_PGA_VOL_SHIFT)
+
+#define CS53L30_ADCxy_AFE_CTL_DEFAULT (0)
+
+/* R43/R44/R51/R52 (0x2B/0x2C/0x33/0x34) CS53L30_ADCxy_DIG_VOL - ADC1A/1B/2A/2B Digital Volume */
+#define CS53L30_ADCxy_VOL_MUTE (0x80)
+
+#define CS53L30_ADCxy_DIG_VOL_DEFAULT (0x0)
+
+/* CS53L30_INT */
+#define CS53L30_PDN_DONE (1 << 7)
+#define CS53L30_THMS_TRIP (1 << 6)
+#define CS53L30_SYNC_DONE (1 << 5)
+#define CS53L30_ADC2B_OVFL (1 << 4)
+#define CS53L30_ADC2A_OVFL (1 << 3)
+#define CS53L30_ADC1B_OVFL (1 << 2)
+#define CS53L30_ADC1A_OVFL (1 << 1)
+#define CS53L30_MUTE_PIN (1 << 0)
+#define CS53L30_DEVICE_INT_MASK 0xFF
+
+#endif /* __CS53L30_H__ */
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index d6f4abbbf8a7..d6121c0a2616 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cx20442.c -- CX20442 ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
* Initially based on sound/soc/codecs/wm8400.c
* Copyright 2008, 2009 Wolfson Microelectronics PLC.
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/tty.h>
@@ -26,8 +22,9 @@
struct cx20442_priv {
- void *control_data;
+ struct tty_struct *tty;
struct regulator *por;
+ u8 reg_cache;
};
#define CX20442_PM 0x0
@@ -88,15 +85,15 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"},
};
-static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int cx20442_read_reg_cache(struct snd_soc_component *component,
+ unsigned int reg)
{
- u8 *reg_cache = codec->reg_cache;
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
- if (reg >= codec->driver->reg_cache_size)
+ if (reg >= 1)
return -EINVAL;
- return reg_cache[reg];
+ return cx20442->reg_cache;
}
enum v253_vls {
@@ -152,24 +149,23 @@ static int cx20442_pm_to_v253_vsp(u8 value)
return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
}
-static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
+static int cx20442_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
- u8 *reg_cache = codec->reg_cache;
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
int vls, vsp, old, len;
char buf[18];
- if (reg >= codec->driver->reg_cache_size)
+ if (reg >= 1)
return -EINVAL;
- /* hw_write and control_data pointers required for talking to the modem
+ /* tty and write pointers required for talking to the modem
* are expected to be set by the line discipline initialization code */
- if (!codec->hw_write || !cx20442->control_data)
+ if (!cx20442->tty || !cx20442->tty->ops->write)
return -EIO;
- old = reg_cache[reg];
- reg_cache[reg] = value;
+ old = cx20442->reg_cache;
+ cx20442->reg_cache = value;
vls = cx20442_pm_to_v253_vls(value);
if (vls < 0)
@@ -193,14 +189,13 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
return -ENOMEM;
- dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
- if (codec->hw_write(cx20442->control_data, buf, len) != len)
+ dev_dbg(component->dev, "%s: %s\n", __func__, buf);
+ if (cx20442->tty->ops->write(cx20442->tty, buf, len) != len)
return -EIO;
return 0;
}
-
/*
* Line discpline related code
*
@@ -211,7 +206,7 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
*/
/* Modem init: echo off, digital speaker off, quiet off, voice mode */
-static const char *v253_init = "ate0m0q0+fclass=8\r";
+static const char v253_init[] = "ate0m0q0+fclass=8\r";
/* Line discipline .open() */
static int v253_open(struct tty_struct *tty)
@@ -226,6 +221,7 @@ static int v253_open(struct tty_struct *tty)
if (!tty->disc_data)
return -ENODEV;
+ tty->receive_room = 16;
if (tty->ops->write(tty, v253_init, len) != len) {
ret = -EIO;
goto err;
@@ -240,65 +236,55 @@ err:
/* Line discipline .close() */
static void v253_close(struct tty_struct *tty)
{
- struct snd_soc_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = tty->disc_data;
struct cx20442_priv *cx20442;
tty->disc_data = NULL;
- if (!codec)
+ if (!component)
return;
- cx20442 = snd_soc_codec_get_drvdata(codec);
+ cx20442 = snd_soc_component_get_drvdata(component);
/* Prevent the codec driver from further accessing the modem */
- codec->hw_write = NULL;
- cx20442->control_data = NULL;
- codec->component.card->pop_time = 0;
+ cx20442->tty = NULL;
+ component->card->pop_time = 0;
}
/* Line discipline .hangup() */
-static int v253_hangup(struct tty_struct *tty)
+static void v253_hangup(struct tty_struct *tty)
{
v253_close(tty);
- return 0;
}
/* Line discipline .receive_buf() */
-static void v253_receive(struct tty_struct *tty,
- const unsigned char *cp, char *fp, int count)
+static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp,
+ size_t count)
{
- struct snd_soc_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = tty->disc_data;
struct cx20442_priv *cx20442;
- if (!codec)
+ if (!component)
return;
- cx20442 = snd_soc_codec_get_drvdata(codec);
+ cx20442 = snd_soc_component_get_drvdata(component);
- if (!cx20442->control_data) {
+ if (!cx20442->tty) {
/* First modem response, complete setup procedure */
/* Set up codec driver access to modem controls */
- cx20442->control_data = tty;
- codec->hw_write = (hw_write_t)tty->ops->write;
- codec->component.card->pop_time = 1;
+ cx20442->tty = tty;
+ component->card->pop_time = 1;
}
}
-/* Line discipline .write_wakeup() */
-static void v253_wakeup(struct tty_struct *tty)
-{
-}
-
struct tty_ldisc_ops v253_ops = {
- .magic = TTY_LDISC_MAGIC,
.name = "cx20442",
.owner = THIS_MODULE,
.open = v253_open,
.close = v253_close,
.hangup = v253_hangup,
.receive_buf = v253_receive,
- .write_wakeup = v253_wakeup,
};
EXPORT_SYMBOL_GPL(v253_ops);
@@ -325,15 +311,16 @@ static struct snd_soc_dai_driver cx20442_dai = {
},
};
-static int cx20442_set_bias_level(struct snd_soc_codec *codec,
+static int cx20442_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int err = 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_STANDBY)
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_STANDBY)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -341,7 +328,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
err = regulator_enable(cx20442->por);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_PREPARE)
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_PREPARE)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -355,7 +342,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
return err;
}
-static int cx20442_codec_probe(struct snd_soc_codec *codec)
+static int cx20442_component_probe(struct snd_soc_component *component)
{
struct cx20442_priv *cx20442;
@@ -363,25 +350,43 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
if (cx20442 == NULL)
return -ENOMEM;
- cx20442->por = regulator_get(codec->dev, "POR");
- if (IS_ERR(cx20442->por))
- dev_warn(codec->dev, "failed to get the regulator");
- cx20442->control_data = NULL;
+ cx20442->por = regulator_get(component->dev, "POR");
+ if (IS_ERR(cx20442->por)) {
+ int err = PTR_ERR(cx20442->por);
+
+ dev_warn(component->dev, "failed to get POR supply (%d)", err);
+ /*
+ * When running on a non-dt platform and requested regulator
+ * is not available, regulator_get() never returns
+ * -EPROBE_DEFER as it is not able to justify if the regulator
+ * may still appear later. On the other hand, the board can
+ * still set full constraints flag at late_initcall in order
+ * to instruct regulator_get() to return a dummy one if
+ * sufficient. Hence, if we get -ENODEV here, let's convert
+ * it to -EPROBE_DEFER and wait for the board to decide or
+ * let Deferred Probe infrastructure handle this error.
+ */
+ if (err == -ENODEV)
+ err = -EPROBE_DEFER;
+ kfree(cx20442);
+ return err;
+ }
- snd_soc_codec_set_drvdata(codec, cx20442);
- codec->hw_write = NULL;
- codec->component.card->pop_time = 0;
+ cx20442->tty = NULL;
+
+ snd_soc_component_set_drvdata(component, cx20442);
+ component->card->pop_time = 0;
return 0;
}
/* power down chip */
-static int cx20442_codec_remove(struct snd_soc_codec *codec)
+static void cx20442_component_remove(struct snd_soc_component *component)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
- if (cx20442->control_data) {
- struct tty_struct *tty = cx20442->control_data;
+ if (cx20442->tty) {
+ struct tty_struct *tty = cx20442->tty;
tty_hangup(tty);
}
@@ -390,38 +395,29 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec)
regulator_put(cx20442->por);
}
- snd_soc_codec_set_drvdata(codec, NULL);
+ snd_soc_component_set_drvdata(component, NULL);
kfree(cx20442);
- return 0;
}
-static const u8 cx20442_reg;
-
-static struct snd_soc_codec_driver cx20442_codec_dev = {
- .probe = cx20442_codec_probe,
- .remove = cx20442_codec_remove,
- .set_bias_level = cx20442_set_bias_level,
- .reg_cache_default = &cx20442_reg,
- .reg_cache_size = 1,
- .reg_word_size = sizeof(u8),
- .read = cx20442_read_reg_cache,
- .write = cx20442_write,
- .dapm_widgets = cx20442_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
- .dapm_routes = cx20442_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
+static const struct snd_soc_component_driver cx20442_component_dev = {
+ .probe = cx20442_component_probe,
+ .remove = cx20442_component_remove,
+ .set_bias_level = cx20442_set_bias_level,
+ .read = cx20442_read_reg_cache,
+ .write = cx20442_write,
+ .dapm_widgets = cx20442_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
+ .dapm_routes = cx20442_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int cx20442_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &cx20442_codec_dev, &cx20442_dai, 1);
-}
-
-static int cx20442_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cx20442_component_dev, &cx20442_dai, 1);
}
static struct platform_driver cx20442_platform_driver = {
@@ -429,7 +425,6 @@ static struct platform_driver cx20442_platform_driver = {
.name = "cx20442-codec",
},
.probe = cx20442_platform_probe,
- .remove = cx20442_platform_remove,
};
module_platform_driver(cx20442_platform_driver);
diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h
index c7a7c79ef0cd..bb897bcb2486 100644
--- a/sound/soc/codecs/cx20442.h
+++ b/sound/soc/codecs/cx20442.h
@@ -1,13 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cx20442.h -- audio driver for CX20442
*
* Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _CX20442_CODEC_H
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
new file mode 100644
index 000000000000..b0033bf9be3a
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.c
@@ -0,0 +1,1718 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC CX20721/CX20723 codec driver
+//
+// Copyright: (C) 2017 Conexant Systems, Inc.
+// Author: Simon Ho, <Simon.ho@conexant.com>
+//
+// TODO: add support for TDM mode.
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "cx2072x.h"
+
+#define PLL_OUT_HZ_48 (1024 * 3 * 48000)
+#define BITS_PER_SLOT 8
+
+/* codec private data */
+struct cx2072x_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ struct device *dev;
+ struct snd_soc_component *codec;
+ struct snd_soc_jack_gpio jack_gpio;
+ struct mutex lock;
+ unsigned int bclk_ratio;
+ bool pll_changed;
+ bool i2spcm_changed;
+ int sample_size;
+ int frame_size;
+ int sample_rate;
+ unsigned int dai_fmt;
+ bool en_aec_ref;
+};
+
+/*
+ * DAC/ADC Volume
+ *
+ * max : 74 : 0 dB
+ * ( in 1 dB step )
+ * min : 0 : -74 dB
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0);
+
+static const DECLARE_TLV_DB_RANGE(hpf_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(120, 0, 0),
+ 1, 63, TLV_DB_SCALE_ITEM(30, 30, 0)
+);
+
+/* Lookup table for PRE_DIV */
+static const struct {
+ unsigned int mclk;
+ unsigned int div;
+} mclk_pre_div[] = {
+ { 6144000, 1 },
+ { 12288000, 2 },
+ { 19200000, 3 },
+ { 26000000, 4 },
+ { 28224000, 5 },
+ { 36864000, 6 },
+ { 36864000, 7 },
+ { 48000000, 8 },
+ { 49152000, 8 },
+};
+
+/*
+ * cx2072x register cache.
+ */
+static const struct reg_default cx2072x_reg_defaults[] = {
+ { CX2072X_AFG_POWER_STATE, 0x00000003 },
+ { CX2072X_UM_RESPONSE, 0x00000000 },
+ { CX2072X_GPIO_DATA, 0x00000000 },
+ { CX2072X_GPIO_ENABLE, 0x00000000 },
+ { CX2072X_GPIO_DIRECTION, 0x00000000 },
+ { CX2072X_GPIO_WAKE, 0x00000000 },
+ { CX2072X_GPIO_UM_ENABLE, 0x00000000 },
+ { CX2072X_GPIO_STICKY_MASK, 0x00000000 },
+ { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a },
+ { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a },
+ { CX2072X_DAC1_POWER_STATE, 0x00000433 },
+ { CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_DAC1_EAPD_ENABLE, 0x00000000 },
+ { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a },
+ { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a },
+ { CX2072X_DAC2_POWER_STATE, 0x00000433 },
+ { CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a },
+ { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a },
+ { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 },
+ { CX2072X_ADC1_POWER_STATE, 0x00000433 },
+ { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 },
+ { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a },
+ { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a },
+ { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 },
+ { CX2072X_ADC2_POWER_STATE, 0x00000433 },
+ { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+ { CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTA_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTA_PIN_CTRL, 0x000000c0 },
+ { CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTA_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTA_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTB_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTB_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTB_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTB_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTB_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTB_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTC_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTC_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTC_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTD_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTD_PIN_CTRL, 0x00000020 },
+ { CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTD_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTD_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTD_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTE_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTE_PIN_CTRL, 0x00000040 },
+ { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTE_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTE_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTE_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTF_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTF_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 },
+ { CX2072X_PORTF_PIN_SENSE, 0x00000000 },
+ { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 },
+ { CX2072X_PORTF_GAIN_LEFT, 0x00000000 },
+ { CX2072X_PORTG_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTG_PIN_CTRL, 0x00000040 },
+ { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTG_EAPD_BTL, 0x00000002 },
+ { CX2072X_PORTM_POWER_STATE, 0x00000433 },
+ { CX2072X_PORTM_PIN_CTRL, 0x00000000 },
+ { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 },
+ { CX2072X_PORTM_EAPD_BTL, 0x00000002 },
+ { CX2072X_MIXER_POWER_STATE, 0x00000433 },
+ { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a },
+ { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a },
+ { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a },
+ { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a },
+ { CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 },
+ { CX2072X_SPKR_DRC_CONTROL, 0x007b0024 },
+ { CX2072X_SPKR_DRC_TEST, 0x00000000 },
+ { CX2072X_DIGITAL_BIOS_TEST0, 0x001f008a },
+ { CX2072X_DIGITAL_BIOS_TEST2, 0x00990026 },
+ { CX2072X_I2SPCM_CONTROL1, 0x00010001 },
+ { CX2072X_I2SPCM_CONTROL2, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL3, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL4, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL5, 0x00000000 },
+ { CX2072X_I2SPCM_CONTROL6, 0x00000000 },
+ { CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 },
+ { CX2072X_CODEC_TEST2, 0x00000000 },
+ { CX2072X_CODEC_TEST9, 0x00000004 },
+ { CX2072X_CODEC_TEST20, 0x00000600 },
+ { CX2072X_CODEC_TEST26, 0x00000208 },
+ { CX2072X_ANALOG_TEST4, 0x00000000 },
+ { CX2072X_ANALOG_TEST5, 0x00000000 },
+ { CX2072X_ANALOG_TEST6, 0x0000059a },
+ { CX2072X_ANALOG_TEST7, 0x000000a7 },
+ { CX2072X_ANALOG_TEST8, 0x00000017 },
+ { CX2072X_ANALOG_TEST9, 0x00000000 },
+ { CX2072X_ANALOG_TEST10, 0x00000285 },
+ { CX2072X_ANALOG_TEST11, 0x00000000 },
+ { CX2072X_ANALOG_TEST12, 0x00000000 },
+ { CX2072X_ANALOG_TEST13, 0x00000000 },
+ { CX2072X_DIGITAL_TEST1, 0x00000242 },
+ { CX2072X_DIGITAL_TEST11, 0x00000000 },
+ { CX2072X_DIGITAL_TEST12, 0x00000084 },
+ { CX2072X_DIGITAL_TEST15, 0x00000077 },
+ { CX2072X_DIGITAL_TEST16, 0x00000021 },
+ { CX2072X_DIGITAL_TEST17, 0x00000018 },
+ { CX2072X_DIGITAL_TEST18, 0x00000024 },
+ { CX2072X_DIGITAL_TEST19, 0x00000001 },
+ { CX2072X_DIGITAL_TEST20, 0x00000002 },
+};
+
+/*
+ * register initialization
+ */
+static const struct reg_sequence cx2072x_reg_init[] = {
+ { CX2072X_ANALOG_TEST9, 0x080 }, /* DC offset Calibration */
+ { CX2072X_CODEC_TEST26, 0x65f }, /* Disable the PA */
+ { CX2072X_ANALOG_TEST10, 0x289 }, /* Set the speaker output gain */
+ { CX2072X_CODEC_TEST20, 0xf05 },
+ { CX2072X_CODEC_TESTXX, 0x380 },
+ { CX2072X_CODEC_TEST26, 0xb90 },
+ { CX2072X_CODEC_TEST9, 0x001 }, /* Enable 30 Hz High pass filter */
+ { CX2072X_ANALOG_TEST3, 0x300 }, /* Disable PCBEEP pad */
+ { CX2072X_CODEC_TEST24, 0x100 }, /* Disable SnM mode */
+ { CX2072X_PORTD_PIN_CTRL, 0x020 }, /* Enable PortD input */
+ { CX2072X_GPIO_ENABLE, 0x040 }, /* Enable GPIO7 pin for button */
+ { CX2072X_GPIO_UM_ENABLE, 0x040 }, /* Enable UM for GPIO7 */
+ { CX2072X_UM_RESPONSE, 0x080 }, /* Enable button response */
+ { CX2072X_DIGITAL_TEST12, 0x0c4 }, /* Enable headset button */
+ { CX2072X_DIGITAL_TEST0, 0x415 }, /* Power down class-D during idle */
+ { CX2072X_I2SPCM_CONTROL2, 0x00f }, /* Enable I2S TX */
+ { CX2072X_I2SPCM_CONTROL3, 0x00f }, /* Enable I2S RX */
+};
+
+static unsigned int cx2072x_register_size(unsigned int reg)
+{
+ switch (reg) {
+ case CX2072X_VENDOR_ID:
+ case CX2072X_REVISION_ID:
+ case CX2072X_PORTA_PIN_SENSE:
+ case CX2072X_PORTB_PIN_SENSE:
+ case CX2072X_PORTD_PIN_SENSE:
+ case CX2072X_PORTE_PIN_SENSE:
+ case CX2072X_PORTF_PIN_SENSE:
+ case CX2072X_I2SPCM_CONTROL1:
+ case CX2072X_I2SPCM_CONTROL2:
+ case CX2072X_I2SPCM_CONTROL3:
+ case CX2072X_I2SPCM_CONTROL4:
+ case CX2072X_I2SPCM_CONTROL5:
+ case CX2072X_I2SPCM_CONTROL6:
+ case CX2072X_UM_INTERRUPT_CRTL_E:
+ case CX2072X_EQ_G_COEFF:
+ case CX2072X_SPKR_DRC_CONTROL:
+ case CX2072X_SPKR_DRC_TEST:
+ case CX2072X_DIGITAL_BIOS_TEST0:
+ case CX2072X_DIGITAL_BIOS_TEST2:
+ return 4;
+ case CX2072X_EQ_ENABLE_BYPASS:
+ case CX2072X_EQ_B0_COEFF:
+ case CX2072X_EQ_B1_COEFF:
+ case CX2072X_EQ_B2_COEFF:
+ case CX2072X_EQ_A1_COEFF:
+ case CX2072X_EQ_A2_COEFF:
+ case CX2072X_DAC1_CONVERTER_FORMAT:
+ case CX2072X_DAC2_CONVERTER_FORMAT:
+ case CX2072X_ADC1_CONVERTER_FORMAT:
+ case CX2072X_ADC2_CONVERTER_FORMAT:
+ case CX2072X_CODEC_TEST2:
+ case CX2072X_CODEC_TEST9:
+ case CX2072X_CODEC_TEST20:
+ case CX2072X_CODEC_TEST26:
+ case CX2072X_ANALOG_TEST3:
+ case CX2072X_ANALOG_TEST4:
+ case CX2072X_ANALOG_TEST5:
+ case CX2072X_ANALOG_TEST6:
+ case CX2072X_ANALOG_TEST7:
+ case CX2072X_ANALOG_TEST8:
+ case CX2072X_ANALOG_TEST9:
+ case CX2072X_ANALOG_TEST10:
+ case CX2072X_ANALOG_TEST11:
+ case CX2072X_ANALOG_TEST12:
+ case CX2072X_ANALOG_TEST13:
+ case CX2072X_DIGITAL_TEST0:
+ case CX2072X_DIGITAL_TEST1:
+ case CX2072X_DIGITAL_TEST11:
+ case CX2072X_DIGITAL_TEST12:
+ case CX2072X_DIGITAL_TEST15:
+ case CX2072X_DIGITAL_TEST16:
+ case CX2072X_DIGITAL_TEST17:
+ case CX2072X_DIGITAL_TEST18:
+ case CX2072X_DIGITAL_TEST19:
+ case CX2072X_DIGITAL_TEST20:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static bool cx2072x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CX2072X_VENDOR_ID:
+ case CX2072X_REVISION_ID:
+ case CX2072X_CURRENT_BCLK_FREQUENCY:
+ case CX2072X_AFG_POWER_STATE:
+ case CX2072X_UM_RESPONSE:
+ case CX2072X_GPIO_DATA:
+ case CX2072X_GPIO_ENABLE:
+ case CX2072X_GPIO_DIRECTION:
+ case CX2072X_GPIO_WAKE:
+ case CX2072X_GPIO_UM_ENABLE:
+ case CX2072X_GPIO_STICKY_MASK:
+ case CX2072X_DAC1_CONVERTER_FORMAT:
+ case CX2072X_DAC1_AMP_GAIN_RIGHT:
+ case CX2072X_DAC1_AMP_GAIN_LEFT:
+ case CX2072X_DAC1_POWER_STATE:
+ case CX2072X_DAC1_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_DAC1_EAPD_ENABLE:
+ case CX2072X_DAC2_CONVERTER_FORMAT:
+ case CX2072X_DAC2_AMP_GAIN_RIGHT:
+ case CX2072X_DAC2_AMP_GAIN_LEFT:
+ case CX2072X_DAC2_POWER_STATE:
+ case CX2072X_DAC2_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_ADC1_CONVERTER_FORMAT:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_0:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_0:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_1:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_1:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_2:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_2:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_3:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_3:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_4:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_4:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_5:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_5:
+ case CX2072X_ADC1_AMP_GAIN_RIGHT_6:
+ case CX2072X_ADC1_AMP_GAIN_LEFT_6:
+ case CX2072X_ADC1_CONNECTION_SELECT_CONTROL:
+ case CX2072X_ADC1_POWER_STATE:
+ case CX2072X_ADC1_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_ADC2_CONVERTER_FORMAT:
+ case CX2072X_ADC2_AMP_GAIN_RIGHT_0:
+ case CX2072X_ADC2_AMP_GAIN_LEFT_0:
+ case CX2072X_ADC2_AMP_GAIN_RIGHT_1:
+ case CX2072X_ADC2_AMP_GAIN_LEFT_1:
+ case CX2072X_ADC2_AMP_GAIN_RIGHT_2:
+ case CX2072X_ADC2_AMP_GAIN_LEFT_2:
+ case CX2072X_ADC2_CONNECTION_SELECT_CONTROL:
+ case CX2072X_ADC2_POWER_STATE:
+ case CX2072X_ADC2_CONVERTER_STREAM_CHANNEL:
+ case CX2072X_PORTA_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTA_POWER_STATE:
+ case CX2072X_PORTA_PIN_CTRL:
+ case CX2072X_PORTA_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTA_PIN_SENSE:
+ case CX2072X_PORTA_EAPD_BTL:
+ case CX2072X_PORTB_POWER_STATE:
+ case CX2072X_PORTB_PIN_CTRL:
+ case CX2072X_PORTB_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTB_PIN_SENSE:
+ case CX2072X_PORTB_EAPD_BTL:
+ case CX2072X_PORTB_GAIN_RIGHT:
+ case CX2072X_PORTB_GAIN_LEFT:
+ case CX2072X_PORTC_POWER_STATE:
+ case CX2072X_PORTC_PIN_CTRL:
+ case CX2072X_PORTC_GAIN_RIGHT:
+ case CX2072X_PORTC_GAIN_LEFT:
+ case CX2072X_PORTD_POWER_STATE:
+ case CX2072X_PORTD_PIN_CTRL:
+ case CX2072X_PORTD_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTD_PIN_SENSE:
+ case CX2072X_PORTD_GAIN_RIGHT:
+ case CX2072X_PORTD_GAIN_LEFT:
+ case CX2072X_PORTE_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTE_POWER_STATE:
+ case CX2072X_PORTE_PIN_CTRL:
+ case CX2072X_PORTE_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTE_PIN_SENSE:
+ case CX2072X_PORTE_EAPD_BTL:
+ case CX2072X_PORTE_GAIN_RIGHT:
+ case CX2072X_PORTE_GAIN_LEFT:
+ case CX2072X_PORTF_POWER_STATE:
+ case CX2072X_PORTF_PIN_CTRL:
+ case CX2072X_PORTF_UNSOLICITED_RESPONSE:
+ case CX2072X_PORTF_PIN_SENSE:
+ case CX2072X_PORTF_GAIN_RIGHT:
+ case CX2072X_PORTF_GAIN_LEFT:
+ case CX2072X_PORTG_POWER_STATE:
+ case CX2072X_PORTG_PIN_CTRL:
+ case CX2072X_PORTG_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTG_EAPD_BTL:
+ case CX2072X_PORTM_POWER_STATE:
+ case CX2072X_PORTM_PIN_CTRL:
+ case CX2072X_PORTM_CONNECTION_SELECT_CTRL:
+ case CX2072X_PORTM_EAPD_BTL:
+ case CX2072X_MIXER_POWER_STATE:
+ case CX2072X_MIXER_GAIN_RIGHT_0:
+ case CX2072X_MIXER_GAIN_LEFT_0:
+ case CX2072X_MIXER_GAIN_RIGHT_1:
+ case CX2072X_MIXER_GAIN_LEFT_1:
+ case CX2072X_EQ_ENABLE_BYPASS:
+ case CX2072X_EQ_B0_COEFF:
+ case CX2072X_EQ_B1_COEFF:
+ case CX2072X_EQ_B2_COEFF:
+ case CX2072X_EQ_A1_COEFF:
+ case CX2072X_EQ_A2_COEFF:
+ case CX2072X_EQ_G_COEFF:
+ case CX2072X_SPKR_DRC_ENABLE_STEP:
+ case CX2072X_SPKR_DRC_CONTROL:
+ case CX2072X_SPKR_DRC_TEST:
+ case CX2072X_DIGITAL_BIOS_TEST0:
+ case CX2072X_DIGITAL_BIOS_TEST2:
+ case CX2072X_I2SPCM_CONTROL1:
+ case CX2072X_I2SPCM_CONTROL2:
+ case CX2072X_I2SPCM_CONTROL3:
+ case CX2072X_I2SPCM_CONTROL4:
+ case CX2072X_I2SPCM_CONTROL5:
+ case CX2072X_I2SPCM_CONTROL6:
+ case CX2072X_UM_INTERRUPT_CRTL_E:
+ case CX2072X_CODEC_TEST2:
+ case CX2072X_CODEC_TEST9:
+ case CX2072X_CODEC_TEST20:
+ case CX2072X_CODEC_TEST26:
+ case CX2072X_ANALOG_TEST4:
+ case CX2072X_ANALOG_TEST5:
+ case CX2072X_ANALOG_TEST6:
+ case CX2072X_ANALOG_TEST7:
+ case CX2072X_ANALOG_TEST8:
+ case CX2072X_ANALOG_TEST9:
+ case CX2072X_ANALOG_TEST10:
+ case CX2072X_ANALOG_TEST11:
+ case CX2072X_ANALOG_TEST12:
+ case CX2072X_ANALOG_TEST13:
+ case CX2072X_DIGITAL_TEST0:
+ case CX2072X_DIGITAL_TEST1:
+ case CX2072X_DIGITAL_TEST11:
+ case CX2072X_DIGITAL_TEST12:
+ case CX2072X_DIGITAL_TEST15:
+ case CX2072X_DIGITAL_TEST16:
+ case CX2072X_DIGITAL_TEST17:
+ case CX2072X_DIGITAL_TEST18:
+ case CX2072X_DIGITAL_TEST19:
+ case CX2072X_DIGITAL_TEST20:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cx2072x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CX2072X_VENDOR_ID:
+ case CX2072X_REVISION_ID:
+ case CX2072X_UM_INTERRUPT_CRTL_E:
+ case CX2072X_DIGITAL_TEST11:
+ case CX2072X_PORTA_PIN_SENSE:
+ case CX2072X_PORTB_PIN_SENSE:
+ case CX2072X_PORTD_PIN_SENSE:
+ case CX2072X_PORTE_PIN_SENSE:
+ case CX2072X_PORTF_PIN_SENSE:
+ case CX2072X_EQ_G_COEFF:
+ case CX2072X_EQ_BAND:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cx2072x_reg_raw_write(struct i2c_client *client,
+ unsigned int reg,
+ const void *val, size_t val_count)
+{
+ struct device *dev = &client->dev;
+ u8 buf[2 + CX2072X_MAX_EQ_COEFF];
+ int ret;
+
+ if (WARN_ON(val_count + 2 > sizeof(buf)))
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ memcpy(buf + 2, val, val_count);
+
+ ret = i2c_master_send(client, buf, val_count + 2);
+ if (ret != val_count + 2) {
+ dev_err(dev, "I2C write failed, ret = %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int cx2072x_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ __le32 raw_value;
+ unsigned int size;
+
+ size = cx2072x_register_size(reg);
+
+ if (reg == CX2072X_UM_INTERRUPT_CRTL_E) {
+ /* Update the MSB byte only */
+ reg += 3;
+ size = 1;
+ value >>= 24;
+ }
+
+ raw_value = cpu_to_le32(value);
+ return cx2072x_reg_raw_write(context, reg, &raw_value, size);
+}
+
+static int cx2072x_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct device *dev = &client->dev;
+ __le32 recv_buf = 0;
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ u8 send_buf[2];
+ int ret;
+
+ size = cx2072x_register_size(reg);
+
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = (u8 *)&recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(dev, "Failed to read register, ret = %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ *value = le32_to_cpu(recv_buf);
+ return 0;
+}
+
+/* get suggested pre_div valuce from mclk frequency */
+static unsigned int get_div_from_mclk(unsigned int mclk)
+{
+ unsigned int div = 8;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mclk_pre_div); i++) {
+ if (mclk <= mclk_pre_div[i].mclk) {
+ div = mclk_pre_div[i].div;
+ break;
+ }
+ }
+ return div;
+}
+
+static int cx2072x_config_pll(struct cx2072x_priv *cx2072x)
+{
+ struct device *dev = cx2072x->dev;
+ unsigned int pre_div;
+ unsigned int pre_div_val;
+ unsigned int pll_input;
+ unsigned int pll_output;
+ unsigned int int_div;
+ unsigned int frac_div;
+ u64 frac_num;
+ unsigned int frac;
+ unsigned int sample_rate = cx2072x->sample_rate;
+ int pt_sample_per_sync = 2;
+ int pt_clock_per_sample = 96;
+
+ switch (sample_rate) {
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ break;
+
+ case 96000:
+ pt_sample_per_sync = 1;
+ pt_clock_per_sample = 48;
+ break;
+
+ case 192000:
+ pt_sample_per_sync = 0;
+ pt_clock_per_sample = 24;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ /* Configure PLL settings */
+ pre_div = get_div_from_mclk(cx2072x->mclk_rate);
+ pll_input = cx2072x->mclk_rate / pre_div;
+ pll_output = sample_rate * 3072;
+ int_div = pll_output / pll_input;
+ frac_div = pll_output - (int_div * pll_input);
+
+ if (frac_div) {
+ frac_div *= 1000;
+ frac_div /= pll_input;
+ frac_num = (u64)(4000 + frac_div) * ((1 << 20) - 4);
+ do_div(frac_num, 7);
+ frac = ((u32)frac_num + 499) / 1000;
+ }
+ pre_div_val = (pre_div - 1) * 2;
+
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST4,
+ 0x40 | (pre_div_val << 8));
+ if (frac_div == 0) {
+ /* Int mode */
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7, 0x100);
+ } else {
+ /* frac mode */
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST6,
+ frac & 0xfff);
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7,
+ (u8)(frac >> 12));
+ }
+
+ int_div--;
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST8, int_div);
+
+ /* configure PLL tracking */
+ if (frac_div == 0) {
+ /* disable PLL tracking */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16, 0x00);
+ } else {
+ /* configure and enable PLL tracking */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
+ (pt_sample_per_sync << 4) & 0xf0);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST17,
+ pt_clock_per_sample);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST18,
+ pt_clock_per_sample * 3 / 2);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST19, 0x01);
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST20, 0x02);
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
+ 0x01, 0x01);
+ }
+
+ return 0;
+}
+
+static int cx2072x_config_i2spcm(struct cx2072x_priv *cx2072x)
+{
+ struct device *dev = cx2072x->dev;
+ unsigned int bclk_rate = 0;
+ int is_i2s = 0;
+ int has_one_bit_delay = 0;
+ int is_frame_inv = 0;
+ int is_bclk_inv = 0;
+ int pulse_len;
+ int frame_len = cx2072x->frame_size;
+ int sample_size = cx2072x->sample_size;
+ int i2s_right_slot;
+ int i2s_right_pause_interval = 0;
+ int i2s_right_pause_pos;
+ int is_big_endian = 1;
+ u64 div;
+ unsigned int mod;
+ union cx2072x_reg_i2spcm_ctrl_reg1 reg1;
+ union cx2072x_reg_i2spcm_ctrl_reg2 reg2;
+ union cx2072x_reg_i2spcm_ctrl_reg3 reg3;
+ union cx2072x_reg_i2spcm_ctrl_reg4 reg4;
+ union cx2072x_reg_i2spcm_ctrl_reg5 reg5;
+ union cx2072x_reg_i2spcm_ctrl_reg6 reg6;
+ union cx2072x_reg_digital_bios_test2 regdbt2;
+ const unsigned int fmt = cx2072x->dai_fmt;
+
+ if (frame_len <= 0) {
+ dev_err(dev, "Incorrect frame len %d\n", frame_len);
+ return -EINVAL;
+ }
+
+ if (sample_size <= 0) {
+ dev_err(dev, "Incorrect sample size %d\n", sample_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "config_i2spcm set_dai_fmt- %08x\n", fmt);
+
+ regdbt2.ulval = 0xac;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ reg2.r.tx_master = 1;
+ reg3.r.rx_master = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg2.r.tx_master = 0;
+ reg3.r.rx_master = 0;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI clocking mode\n");
+ return -EINVAL;
+ }
+
+ /* set format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ is_i2s = 1;
+ has_one_bit_delay = 1;
+ pulse_len = frame_len / 2;
+ break;
+
+ case SND_SOC_DAIFMT_RIGHT_J:
+ is_i2s = 1;
+ pulse_len = frame_len / 2;
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ is_i2s = 1;
+ pulse_len = frame_len / 2;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ is_frame_inv = is_i2s;
+ is_bclk_inv = is_i2s;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ is_frame_inv = !is_i2s;
+ is_bclk_inv = !is_i2s;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ is_frame_inv = is_i2s;
+ is_bclk_inv = !is_i2s;
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ is_frame_inv = !is_i2s;
+ is_bclk_inv = is_i2s;
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI clock inversion\n");
+ return -EINVAL;
+ }
+
+ reg1.r.rx_data_one_line = 1;
+ reg1.r.tx_data_one_line = 1;
+
+ if (is_i2s) {
+ i2s_right_slot = (frame_len / 2) / BITS_PER_SLOT;
+ i2s_right_pause_interval = (frame_len / 2) % BITS_PER_SLOT;
+ i2s_right_pause_pos = i2s_right_slot * BITS_PER_SLOT;
+ }
+
+ reg1.r.rx_ws_pol = is_frame_inv;
+ reg1.r.rx_ws_wid = pulse_len - 1;
+
+ reg1.r.rx_frm_len = frame_len / BITS_PER_SLOT - 1;
+ reg1.r.rx_sa_size = (sample_size / BITS_PER_SLOT) - 1;
+
+ reg1.r.tx_ws_pol = reg1.r.rx_ws_pol;
+ reg1.r.tx_ws_wid = pulse_len - 1;
+ reg1.r.tx_frm_len = reg1.r.rx_frm_len;
+ reg1.r.tx_sa_size = reg1.r.rx_sa_size;
+
+ reg2.r.tx_endian_sel = !is_big_endian;
+ reg2.r.tx_dstart_dly = has_one_bit_delay;
+ if (cx2072x->en_aec_ref)
+ reg2.r.tx_dstart_dly = 0;
+
+ reg3.r.rx_endian_sel = !is_big_endian;
+ reg3.r.rx_dstart_dly = has_one_bit_delay;
+
+ reg4.ulval = 0;
+
+ if (is_i2s) {
+ reg2.r.tx_slot_1 = 0;
+ reg2.r.tx_slot_2 = i2s_right_slot;
+ reg3.r.rx_slot_1 = 0;
+ if (cx2072x->en_aec_ref)
+ reg3.r.rx_slot_2 = 0;
+ else
+ reg3.r.rx_slot_2 = i2s_right_slot;
+ reg6.r.rx_pause_start_pos = i2s_right_pause_pos;
+ reg6.r.rx_pause_cycles = i2s_right_pause_interval;
+ reg6.r.tx_pause_start_pos = i2s_right_pause_pos;
+ reg6.r.tx_pause_cycles = i2s_right_pause_interval;
+ } else {
+ dev_err(dev, "TDM mode is not implemented yet\n");
+ return -EINVAL;
+ }
+ regdbt2.r.i2s_bclk_invert = is_bclk_inv;
+
+ /* Configures the BCLK output */
+ bclk_rate = cx2072x->sample_rate * frame_len;
+ reg5.r.i2s_pcm_clk_div_chan_en = 0;
+
+ /* Disables bclk output before setting new value */
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, 0);
+
+ if (reg2.r.tx_master) {
+ /* Configures BCLK rate */
+ div = PLL_OUT_HZ_48;
+ mod = do_div(div, bclk_rate);
+ if (mod) {
+ dev_err(dev, "Unsupported BCLK %dHz\n", bclk_rate);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "enables BCLK %dHz output\n", bclk_rate);
+ reg5.r.i2s_pcm_clk_div = (u32)div - 1;
+ reg5.r.i2s_pcm_clk_div_chan_en = 1;
+ }
+
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL1, reg1.ulval);
+ regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL2, 0xffffffc0,
+ reg2.ulval);
+ regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL3, 0xffffffc0,
+ reg3.ulval);
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4, reg4.ulval);
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL6, reg6.ulval);
+ regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, reg5.ulval);
+
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
+ regdbt2.ulval);
+
+ return 0;
+}
+
+static int afg_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
+ 0x00, 0x10);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
+ 0x10, 0x10);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cx2072x_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("PortD Boost Volume", CX2072X_PORTD_GAIN_LEFT,
+ CX2072X_PORTD_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("PortC Boost Volume", CX2072X_PORTC_GAIN_LEFT,
+ CX2072X_PORTC_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("PortB Boost Volume", CX2072X_PORTB_GAIN_LEFT,
+ CX2072X_PORTB_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("PortD ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_1,
+ CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0, 0x4a, 0, adc_tlv),
+ SOC_DOUBLE_R_TLV("PortC ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_2,
+ CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0, 0x4a, 0, adc_tlv),
+ SOC_DOUBLE_R_TLV("PortB ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_0,
+ CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0, 0x4a, 0, adc_tlv),
+ SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT,
+ CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
+ SOC_DOUBLE_R("DAC1 Switch", CX2072X_DAC1_AMP_GAIN_LEFT,
+ CX2072X_DAC1_AMP_GAIN_RIGHT, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("DAC2 Volume", CX2072X_DAC2_AMP_GAIN_LEFT,
+ CX2072X_DAC2_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
+ SOC_SINGLE_TLV("HPF Freq", CX2072X_CODEC_TEST9, 0, 0x3f, 0, hpf_tlv),
+ SOC_DOUBLE("HPF Switch", CX2072X_CODEC_TEST9, 8, 9, 1, 1),
+ SOC_SINGLE("PortA HP Amp Switch", CX2072X_PORTA_PIN_CTRL, 7, 1, 0),
+};
+
+static int cx2072x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct device *dev = codec->dev;
+ const unsigned int sample_rate = params_rate(params);
+ int sample_size, frame_size;
+
+ /* Data sizes if not using TDM */
+ sample_size = params_width(params);
+
+ if (sample_size < 0)
+ return sample_size;
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0)
+ return frame_size;
+
+ if (cx2072x->mclk_rate == 0) {
+ dev_err(dev, "Master clock rate is not configured\n");
+ return -EINVAL;
+ }
+
+ if (cx2072x->bclk_ratio)
+ frame_size = cx2072x->bclk_ratio;
+
+ switch (sample_rate) {
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 96000:
+ case 192000:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Sample size %d bits, frame = %d bits, rate = %d Hz\n",
+ sample_size, frame_size, sample_rate);
+
+ cx2072x->frame_size = frame_size;
+ cx2072x->sample_size = sample_size;
+ cx2072x->sample_rate = sample_rate;
+
+ if (dai->id == CX2072X_DAI_DSP) {
+ cx2072x->en_aec_ref = true;
+ dev_dbg(cx2072x->dev, "enables aec reference\n");
+ regmap_write(cx2072x->regmap,
+ CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 3);
+ }
+
+ if (cx2072x->pll_changed) {
+ cx2072x_config_pll(cx2072x);
+ cx2072x->pll_changed = false;
+ }
+
+ if (cx2072x->i2spcm_changed) {
+ cx2072x_config_i2spcm(cx2072x);
+ cx2072x->i2spcm_changed = false;
+ }
+
+ return 0;
+}
+
+static int cx2072x_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ cx2072x->bclk_ratio = ratio;
+ return 0;
+}
+
+static int cx2072x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ if (clk_set_rate(cx2072x->mclk, freq)) {
+ dev_err(codec->dev, "set clk rate failed\n");
+ return -EINVAL;
+ }
+
+ cx2072x->mclk_rate = freq;
+ return 0;
+}
+
+static int cx2072x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct device *dev = codec->dev;
+
+ dev_dbg(dev, "set_dai_fmt- %08x\n", fmt);
+ /* set master/slave */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI master mode\n");
+ return -EINVAL;
+ }
+
+ /* set format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+
+ default:
+ dev_err(dev, "Unsupported DAI clock inversion\n");
+ return -EINVAL;
+ }
+
+ cx2072x->dai_fmt = fmt;
+ return 0;
+}
+
+static const struct snd_kcontrol_new portaouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTA_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new porteouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portgouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTG_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portmouten_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTM_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portbinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTB_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new portcinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTC_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new portdinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTD_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new porteinen_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc1l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 0, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc1r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 1, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc2l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 2, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc2r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 3, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac1l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 0, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac1r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 1, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac2l_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 2, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac2r_ctl =
+ SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 3, 1, 0);
+
+static const char * const dac_enum_text[] = {
+ "DAC1 Switch", "DAC2 Switch",
+};
+
+static const struct soc_enum porta_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new porta_mux =
+SOC_DAPM_ENUM("PortA Mux", porta_dac_enum);
+
+static const struct soc_enum portg_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new portg_mux =
+SOC_DAPM_ENUM("PortG Mux", portg_dac_enum);
+
+static const struct soc_enum porte_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new porte_mux =
+SOC_DAPM_ENUM("PortE Mux", porte_dac_enum);
+
+static const struct soc_enum portm_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new portm_mux =
+SOC_DAPM_ENUM("PortM Mux", portm_dac_enum);
+
+static const char * const adc1in_sel_text[] = {
+ "PortB Switch", "PortD Switch", "PortC Switch", "Widget15 Switch",
+ "PortE Switch", "PortF Switch", "PortH Switch"
+};
+
+static const struct soc_enum adc1in_sel_enum =
+SOC_ENUM_SINGLE(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0, 7, adc1in_sel_text);
+
+static const struct snd_kcontrol_new adc1_mux =
+SOC_DAPM_ENUM("ADC1 Mux", adc1in_sel_enum);
+
+static const char * const adc2in_sel_text[] = {
+ "PortC Switch", "Widget15 Switch", "PortH Switch"
+};
+
+static const struct soc_enum adc2in_sel_enum =
+SOC_ENUM_SINGLE(CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0, 3, adc2in_sel_text);
+
+static const struct snd_kcontrol_new adc2_mux =
+SOC_DAPM_ENUM("ADC2 Mux", adc2in_sel_enum);
+
+static const struct snd_kcontrol_new wid15_mix[] = {
+ SOC_DAPM_SINGLE("DAC1L Switch", CX2072X_MIXER_GAIN_LEFT_0, 7, 1, 1),
+ SOC_DAPM_SINGLE("DAC1R Switch", CX2072X_MIXER_GAIN_RIGHT_0, 7, 1, 1),
+ SOC_DAPM_SINGLE("DAC2L Switch", CX2072X_MIXER_GAIN_LEFT_1, 7, 1, 1),
+ SOC_DAPM_SINGLE("DAC2R Switch", CX2072X_MIXER_GAIN_RIGHT_1, 7, 1, 1),
+};
+
+#define CX2072X_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, wmask, won_val, \
+ woff_val, wevent, wflags) \
+ {.id = snd_soc_dapm_supply, .name = wname, .kcontrol_news = NULL, \
+ .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .subseq = wsubseq, .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \
+ wevent, wflags) \
+ {.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
+ .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \
+ wevent, wflags) \
+ {.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
+ .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_REG_E(wid, wname, wreg, wshift, wmask, won_val, woff_val, \
+ wevent, wflags) \
+ {.id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .reg = wreg, .shift = wshift, .mask = wmask, \
+ .on_val = won_val, .off_val = woff_val, \
+ .event = wevent, .event_flags = wflags}
+
+static const struct snd_soc_dapm_widget cx2072x_dapm_widgets[] = {
+ /*Playback*/
+ SND_SOC_DAPM_AIF_IN("In AIF", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("I2S DAC1L", SND_SOC_NOPM, 0, 0, &i2sdac1l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S DAC1R", SND_SOC_NOPM, 0, 0, &i2sdac1r_ctl),
+ SND_SOC_DAPM_SWITCH("I2S DAC2L", SND_SOC_NOPM, 0, 0, &i2sdac2l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S DAC2R", SND_SOC_NOPM, 0, 0, &i2sdac2r_ctl),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC1", CX2072X_DAC1_POWER_STATE,
+ 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC2", CX2072X_DAC2_POWER_STATE,
+ 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_MUX("PortA Mux", SND_SOC_NOPM, 0, 0, &porta_mux),
+ SND_SOC_DAPM_MUX("PortG Mux", SND_SOC_NOPM, 0, 0, &portg_mux),
+ SND_SOC_DAPM_MUX("PortE Mux", SND_SOC_NOPM, 0, 0, &porte_mux),
+ SND_SOC_DAPM_MUX("PortM Mux", SND_SOC_NOPM, 0, 0, &portm_mux),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortA Power",
+ CX2072X_PORTA_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortM Power",
+ CX2072X_PORTM_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortG Power",
+ CX2072X_PORTG_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ CX2072X_DAPM_SUPPLY_S("AFG Power", 0, CX2072X_AFG_POWER_STATE,
+ 0, 0xfff, 0x00, 0x03, afg_power_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SWITCH("PortA Out En", SND_SOC_NOPM, 0, 0,
+ &portaouten_ctl),
+ SND_SOC_DAPM_SWITCH("PortE Out En", SND_SOC_NOPM, 0, 0,
+ &porteouten_ctl),
+ SND_SOC_DAPM_SWITCH("PortG Out En", SND_SOC_NOPM, 0, 0,
+ &portgouten_ctl),
+ SND_SOC_DAPM_SWITCH("PortM Out En", SND_SOC_NOPM, 0, 0,
+ &portmouten_ctl),
+
+ SND_SOC_DAPM_OUTPUT("PORTA"),
+ SND_SOC_DAPM_OUTPUT("PORTG"),
+ SND_SOC_DAPM_OUTPUT("PORTE"),
+ SND_SOC_DAPM_OUTPUT("PORTM"),
+ SND_SOC_DAPM_OUTPUT("AEC REF"),
+
+ /*Capture*/
+ SND_SOC_DAPM_AIF_OUT("Out AIF", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("I2S ADC1L", SND_SOC_NOPM, 0, 0, &i2sadc1l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S ADC1R", SND_SOC_NOPM, 0, 0, &i2sadc1r_ctl),
+ SND_SOC_DAPM_SWITCH("I2S ADC2L", SND_SOC_NOPM, 0, 0, &i2sadc2l_ctl),
+ SND_SOC_DAPM_SWITCH("I2S ADC2R", SND_SOC_NOPM, 0, 0, &i2sadc2r_ctl),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1", CX2072X_ADC1_POWER_STATE,
+ 0, 0xff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2", CX2072X_ADC2_POWER_STATE,
+ 0, 0xff, 0x00, 0x03),
+
+ SND_SOC_DAPM_MUX("ADC1 Mux", SND_SOC_NOPM, 0, 0, &adc1_mux),
+ SND_SOC_DAPM_MUX("ADC2 Mux", SND_SOC_NOPM, 0, 0, &adc2_mux),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortB Power",
+ CX2072X_PORTB_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortC Power",
+ CX2072X_PORTC_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortD Power",
+ CX2072X_PORTD_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortE Power",
+ CX2072X_PORTE_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Widget15 Power",
+ CX2072X_MIXER_POWER_STATE, 0, 0xfff, 0x00, 0x03),
+
+ SND_SOC_DAPM_MIXER("Widget15 Mixer", SND_SOC_NOPM, 0, 0,
+ wid15_mix, ARRAY_SIZE(wid15_mix)),
+ SND_SOC_DAPM_SWITCH("PortB In En", SND_SOC_NOPM, 0, 0, &portbinen_ctl),
+ SND_SOC_DAPM_SWITCH("PortC In En", SND_SOC_NOPM, 0, 0, &portcinen_ctl),
+ SND_SOC_DAPM_SWITCH("PortD In En", SND_SOC_NOPM, 0, 0, &portdinen_ctl),
+ SND_SOC_DAPM_SWITCH("PortE In En", SND_SOC_NOPM, 0, 0, &porteinen_ctl),
+
+ SND_SOC_DAPM_MICBIAS("Headset Bias", CX2072X_ANALOG_TEST11, 1, 0),
+ SND_SOC_DAPM_MICBIAS("PortB Mic Bias", CX2072X_PORTB_PIN_CTRL, 2, 0),
+ SND_SOC_DAPM_MICBIAS("PortD Mic Bias", CX2072X_PORTD_PIN_CTRL, 2, 0),
+ SND_SOC_DAPM_MICBIAS("PortE Mic Bias", CX2072X_PORTE_PIN_CTRL, 2, 0),
+ SND_SOC_DAPM_INPUT("PORTB"),
+ SND_SOC_DAPM_INPUT("PORTC"),
+ SND_SOC_DAPM_INPUT("PORTD"),
+ SND_SOC_DAPM_INPUT("PORTEIN"),
+
+};
+
+static const struct snd_soc_dapm_route cx2072x_intercon[] = {
+ /* Playback */
+ {"In AIF", NULL, "AFG Power"},
+ {"I2S DAC1L", "Switch", "In AIF"},
+ {"I2S DAC1R", "Switch", "In AIF"},
+ {"I2S DAC2L", "Switch", "In AIF"},
+ {"I2S DAC2R", "Switch", "In AIF"},
+ {"DAC1", NULL, "I2S DAC1L"},
+ {"DAC1", NULL, "I2S DAC1R"},
+ {"DAC2", NULL, "I2S DAC2L"},
+ {"DAC2", NULL, "I2S DAC2R"},
+ {"PortA Mux", "DAC1 Switch", "DAC1"},
+ {"PortA Mux", "DAC2 Switch", "DAC2"},
+ {"PortG Mux", "DAC1 Switch", "DAC1"},
+ {"PortG Mux", "DAC2 Switch", "DAC2"},
+ {"PortE Mux", "DAC1 Switch", "DAC1"},
+ {"PortE Mux", "DAC2 Switch", "DAC2"},
+ {"PortM Mux", "DAC1 Switch", "DAC1"},
+ {"PortM Mux", "DAC2 Switch", "DAC2"},
+ {"Widget15 Mixer", "DAC1L Switch", "DAC1"},
+ {"Widget15 Mixer", "DAC1R Switch", "DAC2"},
+ {"Widget15 Mixer", "DAC2L Switch", "DAC1"},
+ {"Widget15 Mixer", "DAC2R Switch", "DAC2"},
+ {"Widget15 Mixer", NULL, "Widget15 Power"},
+ {"PortA Out En", "Switch", "PortA Mux"},
+ {"PortG Out En", "Switch", "PortG Mux"},
+ {"PortE Out En", "Switch", "PortE Mux"},
+ {"PortM Out En", "Switch", "PortM Mux"},
+ {"PortA Mux", NULL, "PortA Power"},
+ {"PortG Mux", NULL, "PortG Power"},
+ {"PortE Mux", NULL, "PortE Power"},
+ {"PortM Mux", NULL, "PortM Power"},
+ {"PortA Out En", NULL, "PortA Power"},
+ {"PortG Out En", NULL, "PortG Power"},
+ {"PortE Out En", NULL, "PortE Power"},
+ {"PortM Out En", NULL, "PortM Power"},
+ {"PORTA", NULL, "PortA Out En"},
+ {"PORTG", NULL, "PortG Out En"},
+ {"PORTE", NULL, "PortE Out En"},
+ {"PORTM", NULL, "PortM Out En"},
+
+ /* Capture */
+ {"PORTD", NULL, "Headset Bias"},
+ {"PortB In En", "Switch", "PORTB"},
+ {"PortC In En", "Switch", "PORTC"},
+ {"PortD In En", "Switch", "PORTD"},
+ {"PortE In En", "Switch", "PORTEIN"},
+ {"ADC1 Mux", "PortB Switch", "PortB In En"},
+ {"ADC1 Mux", "PortC Switch", "PortC In En"},
+ {"ADC1 Mux", "PortD Switch", "PortD In En"},
+ {"ADC1 Mux", "PortE Switch", "PortE In En"},
+ {"ADC1 Mux", "Widget15 Switch", "Widget15 Mixer"},
+ {"ADC2 Mux", "PortC Switch", "PortC In En"},
+ {"ADC2 Mux", "Widget15 Switch", "Widget15 Mixer"},
+ {"ADC1", NULL, "ADC1 Mux"},
+ {"ADC2", NULL, "ADC2 Mux"},
+ {"I2S ADC1L", "Switch", "ADC1"},
+ {"I2S ADC1R", "Switch", "ADC1"},
+ {"I2S ADC2L", "Switch", "ADC2"},
+ {"I2S ADC2R", "Switch", "ADC2"},
+ {"Out AIF", NULL, "I2S ADC1L"},
+ {"Out AIF", NULL, "I2S ADC1R"},
+ {"Out AIF", NULL, "I2S ADC2L"},
+ {"Out AIF", NULL, "I2S ADC2R"},
+ {"Out AIF", NULL, "AFG Power"},
+ {"AEC REF", NULL, "Out AIF"},
+ {"PortB In En", NULL, "PortB Power"},
+ {"PortC In En", NULL, "PortC Power"},
+ {"PortD In En", NULL, "PortD Power"},
+ {"PortE In En", NULL, "PortE Power"},
+};
+
+static int cx2072x_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+ const enum snd_soc_bias_level old_level = snd_soc_dapm_get_bias_level(dapm);
+
+ if (level == SND_SOC_BIAS_STANDBY && old_level == SND_SOC_BIAS_OFF)
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0);
+ else if (level == SND_SOC_BIAS_OFF && old_level != SND_SOC_BIAS_OFF)
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
+
+ return 0;
+}
+
+/*
+ * FIXME: the whole jack detection code below is pretty platform-specific;
+ * it has lots of implicit assumptions about the pins, etc.
+ * However, since we have no other code and reference, take this hard-coded
+ * setup for now. Once when we have different platform implementations,
+ * this needs to be rewritten in a more generic form, or moving into the
+ * platform data.
+ */
+static void cx2072x_enable_jack_detect(struct snd_soc_component *codec)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+
+ /* No-sticky input type */
+ regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
+
+ /* Use GPOI0 as interrupt pin */
+ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
+
+ /* Enables unsolitited message on PortA */
+ regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x80);
+
+ /* support both nokia and apple headset set. Monitor time = 275 ms */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST15, 0x73);
+
+ /* Disable TIP detection */
+ regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST12, 0x300);
+
+ /* Switch MusicD3Live pin to GPIO */
+ regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST1, 0);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PORTD");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Bias");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PortD Mic Bias");
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void cx2072x_disable_jack_detect(struct snd_soc_component *codec)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0);
+ regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0);
+}
+
+static int cx2072x_jack_status_check(void *data)
+{
+ struct snd_soc_component *codec = data;
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ unsigned int jack;
+ unsigned int type = 0;
+ int state = 0;
+
+ mutex_lock(&cx2072x->lock);
+
+ regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack);
+ jack = jack >> 24;
+ regmap_read(cx2072x->regmap, CX2072X_DIGITAL_TEST11, &type);
+
+ if (jack == 0x80) {
+ type = type >> 8;
+
+ if (type & 0x8) {
+ /* Apple headset */
+ state |= SND_JACK_HEADSET;
+ if (type & 0x2)
+ state |= SND_JACK_BTN_0;
+ } else {
+ /*
+ * Nokia headset (type & 0x4) and
+ * regular Headphone
+ */
+ state |= SND_JACK_HEADPHONE;
+ }
+ }
+
+ /* clear interrupt */
+ regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
+
+ mutex_unlock(&cx2072x->lock);
+
+ dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
+ type, state);
+ return state;
+}
+
+static const struct snd_soc_jack_gpio cx2072x_jack_gpio = {
+ .name = "headset",
+ .report = SND_JACK_HEADSET | SND_JACK_BTN_0,
+ .debounce_time = 150,
+ .wake = true,
+ .jack_status_check = cx2072x_jack_status_check,
+};
+
+static int cx2072x_set_jack(struct snd_soc_component *codec,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+ int err;
+
+ if (!jack) {
+ cx2072x_disable_jack_detect(codec);
+ return 0;
+ }
+
+ if (!cx2072x->jack_gpio.gpiod_dev) {
+ cx2072x->jack_gpio = cx2072x_jack_gpio;
+ cx2072x->jack_gpio.gpiod_dev = codec->dev;
+ cx2072x->jack_gpio.data = codec;
+ err = snd_soc_jack_add_gpios(jack, 1, &cx2072x->jack_gpio);
+ if (err) {
+ cx2072x->jack_gpio.gpiod_dev = NULL;
+ return err;
+ }
+ }
+
+ cx2072x_enable_jack_detect(codec);
+ return 0;
+}
+
+static int cx2072x_probe(struct snd_soc_component *codec)
+{
+ struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+ cx2072x->codec = codec;
+
+ /*
+ * FIXME: below is, again, a very platform-specific init sequence,
+ * but we keep the code here just for simplicity. It seems that all
+ * existing hardware implementations require this, so there is no very
+ * much reason to move this out of the codec driver to the platform
+ * data.
+ * But of course it's no "right" thing; if you are a good boy, don't
+ * read and follow the code like this!
+ */
+ pm_runtime_get_sync(codec->dev);
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0);
+
+ regmap_multi_reg_write(cx2072x->regmap, cx2072x_reg_init,
+ ARRAY_SIZE(cx2072x_reg_init));
+
+ /* configure PortC as input device */
+ regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
+ 0x20, 0x20);
+
+ regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
+ 0x84, 0xff);
+
+ regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
+ pm_runtime_put(codec->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
+ .probe = cx2072x_probe,
+ .set_bias_level = cx2072x_set_bias_level,
+ .set_jack = cx2072x_set_jack,
+ .controls = cx2072x_snd_controls,
+ .num_controls = ARRAY_SIZE(cx2072x_snd_controls),
+ .dapm_widgets = cx2072x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets),
+ .dapm_routes = cx2072x_intercon,
+ .num_dapm_routes = ARRAY_SIZE(cx2072x_intercon),
+ .endianness = 1,
+};
+
+/*
+ * DAI ops
+ */
+static const struct snd_soc_dai_ops cx2072x_dai_ops = {
+ .set_sysclk = cx2072x_set_dai_sysclk,
+ .set_fmt = cx2072x_set_dai_fmt,
+ .hw_params = cx2072x_hw_params,
+ .set_bclk_ratio = cx2072x_set_dai_bclk_ratio,
+};
+
+static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct cx2072x_priv *cx2072x =
+ snd_soc_component_get_drvdata(dai->component);
+
+ cx2072x->en_aec_ref = true;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cx2072x_dai_ops2 = {
+ .probe = cx2072x_dsp_dai_probe,
+ .set_sysclk = cx2072x_set_dai_sysclk,
+ .set_fmt = cx2072x_set_dai_fmt,
+ .hw_params = cx2072x_hw_params,
+ .set_bclk_ratio = cx2072x_set_dai_bclk_ratio,
+};
+
+#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = {
+ { /* playback and capture */
+ .name = "cx2072x-hifi",
+ .id = CX2072X_DAI_HIFI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .ops = &cx2072x_dai_ops,
+ .symmetric_rate = 1,
+ },
+ { /* plabayck only, return echo reference to Conexant DSP chip */
+ .name = "cx2072x-dsp",
+ .id = CX2072X_DAI_DSP,
+ .playback = {
+ .stream_name = "DSP Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ .ops = &cx2072x_dai_ops2,
+ },
+ { /* plabayck only, return echo reference through I2S TX */
+ .name = "cx2072x-aec",
+ .id = 3,
+ .capture = {
+ .stream_name = "AEC Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CX2072X_RATES_DSP,
+ .formats = CX2072X_FORMATS,
+ },
+ },
+};
+
+static const struct regmap_config cx2072x_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = CX2072X_REG_MAX,
+ .reg_defaults = cx2072x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cx2072x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .readable_reg = cx2072x_readable_register,
+ .volatile_reg = cx2072x_volatile_register,
+ /* Needs custom read/write functions for various register lengths */
+ .reg_read = cx2072x_reg_read,
+ .reg_write = cx2072x_reg_write,
+};
+
+static int cx2072x_runtime_suspend(struct device *dev)
+{
+ struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(cx2072x->mclk);
+ return 0;
+}
+
+static int cx2072x_runtime_resume(struct device *dev)
+{
+ struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(cx2072x->mclk);
+}
+
+static int cx2072x_i2c_probe(struct i2c_client *i2c)
+{
+ struct cx2072x_priv *cx2072x;
+ unsigned int ven_id, rev_id;
+ int ret;
+
+ cx2072x = devm_kzalloc(&i2c->dev, sizeof(struct cx2072x_priv),
+ GFP_KERNEL);
+ if (!cx2072x)
+ return -ENOMEM;
+
+ cx2072x->regmap = devm_regmap_init(&i2c->dev, NULL, i2c,
+ &cx2072x_regmap);
+ if (IS_ERR(cx2072x->regmap))
+ return PTR_ERR(cx2072x->regmap);
+
+ mutex_init(&cx2072x->lock);
+
+ i2c_set_clientdata(i2c, cx2072x);
+
+ cx2072x->dev = &i2c->dev;
+ cx2072x->pll_changed = true;
+ cx2072x->i2spcm_changed = true;
+ cx2072x->bclk_ratio = 0;
+
+ cx2072x->mclk = devm_clk_get(cx2072x->dev, "mclk");
+ if (IS_ERR(cx2072x->mclk)) {
+ dev_err(cx2072x->dev, "Failed to get MCLK\n");
+ return PTR_ERR(cx2072x->mclk);
+ }
+
+ regmap_read(cx2072x->regmap, CX2072X_VENDOR_ID, &ven_id);
+ regmap_read(cx2072x->regmap, CX2072X_REVISION_ID, &rev_id);
+
+ dev_info(cx2072x->dev, "codec version: %08x,%08x\n", ven_id, rev_id);
+
+ ret = devm_snd_soc_register_component(cx2072x->dev,
+ &soc_codec_driver_cx2072x,
+ soc_codec_cx2072x_dai,
+ ARRAY_SIZE(soc_codec_cx2072x_dai));
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_use_autosuspend(cx2072x->dev);
+ pm_runtime_enable(cx2072x->dev);
+
+ return 0;
+}
+
+static void cx2072x_i2c_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+}
+
+static const struct i2c_device_id cx2072x_i2c_id[] = {
+ { "cx20721" },
+ { "cx20723" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id cx2072x_acpi_match[] = {
+ { "14F10720", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cx2072x_acpi_match);
+#endif
+
+static const struct dev_pm_ops cx2072x_runtime_pm = {
+ RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct i2c_driver cx2072x_i2c_driver = {
+ .driver = {
+ .name = "cx2072x",
+ .acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
+ .pm = pm_ptr(&cx2072x_runtime_pm),
+ },
+ .probe = cx2072x_i2c_probe,
+ .remove = cx2072x_i2c_remove,
+ .id_table = cx2072x_i2c_id,
+};
+
+module_i2c_driver(cx2072x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC cx2072x Codec Driver");
+MODULE_AUTHOR("Simon Ho <simon.ho@conexant.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h
new file mode 100644
index 000000000000..09e3a92b184f
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.h
@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA SoC CX20721/CX20723 codec driver
+ *
+ * Copyright: (C) 2017 Conexant Systems, Inc.
+ * Author: Simon Ho, <Simon.ho@conexant.com>
+ */
+
+#ifndef __CX2072X_H__
+#define __CX2072X_H__
+
+#define CX2072X_MCLK_PLL 1
+#define CX2072X_MCLK_EXTERNAL_PLL 1
+#define CX2072X_MCLK_INTERNAL_OSC 2
+
+/*#define CX2072X_RATES SNDRV_PCM_RATE_8000_192000*/
+#define CX2072X_RATES_DSP SNDRV_PCM_RATE_48000
+
+#define CX2072X_REG_MAX 0x8a3c
+
+#define CX2072X_VENDOR_ID 0x0200
+#define CX2072X_REVISION_ID 0x0208
+#define CX2072X_CURRENT_BCLK_FREQUENCY 0x00dc
+#define CX2072X_AFG_POWER_STATE 0x0414
+#define CX2072X_UM_RESPONSE 0x0420
+#define CX2072X_GPIO_DATA 0x0454
+#define CX2072X_GPIO_ENABLE 0x0458
+#define CX2072X_GPIO_DIRECTION 0x045c
+#define CX2072X_GPIO_WAKE 0x0460
+#define CX2072X_GPIO_UM_ENABLE 0x0464
+#define CX2072X_GPIO_STICKY_MASK 0x0468
+#define CX2072X_AFG_FUNCTION_RESET 0x07fc
+#define CX2072X_DAC1_CONVERTER_FORMAT 0x43c8
+#define CX2072X_DAC1_AMP_GAIN_RIGHT 0x41c0
+#define CX2072X_DAC1_AMP_GAIN_LEFT 0x41e0
+#define CX2072X_DAC1_POWER_STATE 0x4014
+#define CX2072X_DAC1_CONVERTER_STREAM_CHANNEL 0x4018
+#define CX2072X_DAC1_EAPD_ENABLE 0x4030
+#define CX2072X_DAC2_CONVERTER_FORMAT 0x47c8
+#define CX2072X_DAC2_AMP_GAIN_RIGHT 0x45c0
+#define CX2072X_DAC2_AMP_GAIN_LEFT 0x45e0
+#define CX2072X_DAC2_POWER_STATE 0x4414
+#define CX2072X_DAC2_CONVERTER_STREAM_CHANNEL 0x4418
+#define CX2072X_ADC1_CONVERTER_FORMAT 0x4fc8
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_0 0x4d80
+#define CX2072X_ADC1_AMP_GAIN_LEFT_0 0x4da0
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_1 0x4d84
+#define CX2072X_ADC1_AMP_GAIN_LEFT_1 0x4da4
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_2 0x4d88
+#define CX2072X_ADC1_AMP_GAIN_LEFT_2 0x4da8
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_3 0x4d8c
+#define CX2072X_ADC1_AMP_GAIN_LEFT_3 0x4dac
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_4 0x4d90
+#define CX2072X_ADC1_AMP_GAIN_LEFT_4 0x4db0
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_5 0x4d94
+#define CX2072X_ADC1_AMP_GAIN_LEFT_5 0x4db4
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_6 0x4d98
+#define CX2072X_ADC1_AMP_GAIN_LEFT_6 0x4db8
+#define CX2072X_ADC1_CONNECTION_SELECT_CONTROL 0x4c04
+#define CX2072X_ADC1_POWER_STATE 0x4c14
+#define CX2072X_ADC1_CONVERTER_STREAM_CHANNEL 0x4c18
+#define CX2072X_ADC2_CONVERTER_FORMAT 0x53c8
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_0 0x5180
+#define CX2072X_ADC2_AMP_GAIN_LEFT_0 0x51a0
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_1 0x5184
+#define CX2072X_ADC2_AMP_GAIN_LEFT_1 0x51a4
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_2 0x5188
+#define CX2072X_ADC2_AMP_GAIN_LEFT_2 0x51a8
+#define CX2072X_ADC2_CONNECTION_SELECT_CONTROL 0x5004
+#define CX2072X_ADC2_POWER_STATE 0x5014
+#define CX2072X_ADC2_CONVERTER_STREAM_CHANNEL 0x5018
+#define CX2072X_PORTA_CONNECTION_SELECT_CTRL 0x5804
+#define CX2072X_PORTA_POWER_STATE 0x5814
+#define CX2072X_PORTA_PIN_CTRL 0x581c
+#define CX2072X_PORTA_UNSOLICITED_RESPONSE 0x5820
+#define CX2072X_PORTA_PIN_SENSE 0x5824
+#define CX2072X_PORTA_EAPD_BTL 0x5830
+#define CX2072X_PORTB_POWER_STATE 0x6014
+#define CX2072X_PORTB_PIN_CTRL 0x601c
+#define CX2072X_PORTB_UNSOLICITED_RESPONSE 0x6020
+#define CX2072X_PORTB_PIN_SENSE 0x6024
+#define CX2072X_PORTB_EAPD_BTL 0x6030
+#define CX2072X_PORTB_GAIN_RIGHT 0x6180
+#define CX2072X_PORTB_GAIN_LEFT 0x61a0
+#define CX2072X_PORTC_POWER_STATE 0x6814
+#define CX2072X_PORTC_PIN_CTRL 0x681c
+#define CX2072X_PORTC_GAIN_RIGHT 0x6980
+#define CX2072X_PORTC_GAIN_LEFT 0x69a0
+#define CX2072X_PORTD_POWER_STATE 0x6414
+#define CX2072X_PORTD_PIN_CTRL 0x641c
+#define CX2072X_PORTD_UNSOLICITED_RESPONSE 0x6420
+#define CX2072X_PORTD_PIN_SENSE 0x6424
+#define CX2072X_PORTD_GAIN_RIGHT 0x6580
+#define CX2072X_PORTD_GAIN_LEFT 0x65a0
+#define CX2072X_PORTE_CONNECTION_SELECT_CTRL 0x7404
+#define CX2072X_PORTE_POWER_STATE 0x7414
+#define CX2072X_PORTE_PIN_CTRL 0x741c
+#define CX2072X_PORTE_UNSOLICITED_RESPONSE 0x7420
+#define CX2072X_PORTE_PIN_SENSE 0x7424
+#define CX2072X_PORTE_EAPD_BTL 0x7430
+#define CX2072X_PORTE_GAIN_RIGHT 0x7580
+#define CX2072X_PORTE_GAIN_LEFT 0x75a0
+#define CX2072X_PORTF_POWER_STATE 0x7814
+#define CX2072X_PORTF_PIN_CTRL 0x781c
+#define CX2072X_PORTF_UNSOLICITED_RESPONSE 0x7820
+#define CX2072X_PORTF_PIN_SENSE 0x7824
+#define CX2072X_PORTF_GAIN_RIGHT 0x7980
+#define CX2072X_PORTF_GAIN_LEFT 0x79a0
+#define CX2072X_PORTG_POWER_STATE 0x5c14
+#define CX2072X_PORTG_PIN_CTRL 0x5c1c
+#define CX2072X_PORTG_CONNECTION_SELECT_CTRL 0x5c04
+#define CX2072X_PORTG_EAPD_BTL 0x5c30
+#define CX2072X_PORTM_POWER_STATE 0x8814
+#define CX2072X_PORTM_PIN_CTRL 0x881c
+#define CX2072X_PORTM_CONNECTION_SELECT_CTRL 0x8804
+#define CX2072X_PORTM_EAPD_BTL 0x8830
+#define CX2072X_MIXER_POWER_STATE 0x5414
+#define CX2072X_MIXER_GAIN_RIGHT_0 0x5580
+#define CX2072X_MIXER_GAIN_LEFT_0 0x55a0
+#define CX2072X_MIXER_GAIN_RIGHT_1 0x5584
+#define CX2072X_MIXER_GAIN_LEFT_1 0x55a4
+#define CX2072X_EQ_ENABLE_BYPASS 0x6d00
+#define CX2072X_EQ_B0_COEFF 0x6d02
+#define CX2072X_EQ_B1_COEFF 0x6d04
+#define CX2072X_EQ_B2_COEFF 0x6d06
+#define CX2072X_EQ_A1_COEFF 0x6d08
+#define CX2072X_EQ_A2_COEFF 0x6d0a
+#define CX2072X_EQ_G_COEFF 0x6d0c
+#define CX2072X_EQ_BAND 0x6d0d
+#define CX2072X_SPKR_DRC_ENABLE_STEP 0x6d10
+#define CX2072X_SPKR_DRC_CONTROL 0x6d14
+#define CX2072X_SPKR_DRC_TEST 0x6d18
+#define CX2072X_DIGITAL_BIOS_TEST0 0x6d80
+#define CX2072X_DIGITAL_BIOS_TEST2 0x6d84
+#define CX2072X_I2SPCM_CONTROL1 0x6e00
+#define CX2072X_I2SPCM_CONTROL2 0x6e04
+#define CX2072X_I2SPCM_CONTROL3 0x6e08
+#define CX2072X_I2SPCM_CONTROL4 0x6e0c
+#define CX2072X_I2SPCM_CONTROL5 0x6e10
+#define CX2072X_I2SPCM_CONTROL6 0x6e18
+#define CX2072X_UM_INTERRUPT_CRTL_E 0x6e14
+#define CX2072X_CODEC_TEST2 0x7108
+#define CX2072X_CODEC_TEST9 0x7124
+#define CX2072X_CODEC_TESTXX 0x7290
+#define CX2072X_CODEC_TEST20 0x7310
+#define CX2072X_CODEC_TEST24 0x731c
+#define CX2072X_CODEC_TEST26 0x7328
+#define CX2072X_ANALOG_TEST3 0x718c
+#define CX2072X_ANALOG_TEST4 0x7190
+#define CX2072X_ANALOG_TEST5 0x7194
+#define CX2072X_ANALOG_TEST6 0x7198
+#define CX2072X_ANALOG_TEST7 0x719c
+#define CX2072X_ANALOG_TEST8 0x71a0
+#define CX2072X_ANALOG_TEST9 0x71a4
+#define CX2072X_ANALOG_TEST10 0x71a8
+#define CX2072X_ANALOG_TEST11 0x71ac
+#define CX2072X_ANALOG_TEST12 0x71b0
+#define CX2072X_ANALOG_TEST13 0x71b4
+#define CX2072X_DIGITAL_TEST0 0x7200
+#define CX2072X_DIGITAL_TEST1 0x7204
+#define CX2072X_DIGITAL_TEST11 0x722c
+#define CX2072X_DIGITAL_TEST12 0x7230
+#define CX2072X_DIGITAL_TEST15 0x723c
+#define CX2072X_DIGITAL_TEST16 0x7080
+#define CX2072X_DIGITAL_TEST17 0x7084
+#define CX2072X_DIGITAL_TEST18 0x7088
+#define CX2072X_DIGITAL_TEST19 0x708c
+#define CX2072X_DIGITAL_TEST20 0x7090
+
+/* not used in the current code, for future extensions (if any) */
+#define CX2072X_MAX_EQ_BAND 7
+#define CX2072X_MAX_EQ_COEFF 11
+#define CX2072X_MAX_DRC_REGS 9
+#define CX2072X_MIC_EQ_COEFF 10
+#define CX2072X_PLBK_EQ_BAND_NUM 7
+#define CX2072X_PLBK_EQ_COEF_LEN 11
+#define CX2072X_PLBK_DRC_PARM_LEN 9
+#define CX2072X_CLASSD_AMP_LEN 6
+
+/* DAI interface type */
+#define CX2072X_DAI_HIFI 1
+#define CX2072X_DAI_DSP 2
+#define CX2072X_DAI_DSP_PWM 3 /* 4 ch, including mic and AEC */
+
+enum cx2072x_reg_sample_size {
+ CX2072X_SAMPLE_SIZE_8_BITS = 0,
+ CX2072X_SAMPLE_SIZE_16_BITS = 1,
+ CX2072X_SAMPLE_SIZE_24_BITS = 2,
+ CX2072X_SAMPLE_SIZE_RESERVED = 3,
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg1 {
+ struct {
+ u32 rx_data_one_line:1;
+ u32 rx_ws_pol:1;
+ u32 rx_ws_wid:7;
+ u32 rx_frm_len:5;
+ u32 rx_sa_size:2;
+ u32 tx_data_one_line:1;
+ u32 tx_ws_pol:1;
+ u32 tx_ws_wid:7;
+ u32 tx_frm_len:5;
+ u32 tx_sa_size:2;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg2 {
+ struct {
+ u32 tx_en_ch1:1;
+ u32 tx_en_ch2:1;
+ u32 tx_en_ch3:1;
+ u32 tx_en_ch4:1;
+ u32 tx_en_ch5:1;
+ u32 tx_en_ch6:1;
+ u32 tx_slot_1:5;
+ u32 tx_slot_2:5;
+ u32 tx_slot_3:5;
+ u32 tx_slot_4:5;
+ u32 res:1;
+ u32 tx_data_neg_bclk:1;
+ u32 tx_master:1;
+ u32 tx_tri_n:1;
+ u32 tx_endian_sel:1;
+ u32 tx_dstart_dly:1;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg3 {
+ struct {
+ u32 rx_en_ch1:1;
+ u32 rx_en_ch2:1;
+ u32 rx_en_ch3:1;
+ u32 rx_en_ch4:1;
+ u32 rx_en_ch5:1;
+ u32 rx_en_ch6:1;
+ u32 rx_slot_1:5;
+ u32 rx_slot_2:5;
+ u32 rx_slot_3:5;
+ u32 rx_slot_4:5;
+ u32 res:1;
+ u32 rx_data_neg_bclk:1;
+ u32 rx_master:1;
+ u32 rx_tri_n:1;
+ u32 rx_endian_sel:1;
+ u32 rx_dstart_dly:1;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg4 {
+ struct {
+ u32 rx_mute:1;
+ u32 tx_mute:1;
+ u32 reserved:1;
+ u32 dac_34_independent:1;
+ u32 dac_bclk_lrck_share:1;
+ u32 bclk_lrck_share_en:1;
+ u32 reserved2:2;
+ u32 rx_last_dac_ch_en:1;
+ u32 rx_last_dac_ch:3;
+ u32 tx_last_adc_ch_en:1;
+ u32 tx_last_adc_ch:3;
+ u32 rx_slot_5:5;
+ u32 rx_slot_6:5;
+ u32 reserved3:6;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg5 {
+ struct {
+ u32 tx_slot_5:5;
+ u32 reserved:3;
+ u32 tx_slot_6:5;
+ u32 reserved2:3;
+ u32 reserved3:8;
+ u32 i2s_pcm_clk_div:7;
+ u32 i2s_pcm_clk_div_chan_en:1;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg6 {
+ struct {
+ u32 reserved:5;
+ u32 rx_pause_cycles:3;
+ u32 rx_pause_start_pos:8;
+ u32 reserved2:5;
+ u32 tx_pause_cycles:3;
+ u32 tx_pause_start_pos:8;
+ } r;
+ u32 ulval;
+};
+
+union cx2072x_reg_digital_bios_test2 {
+ struct {
+ u32 pull_down_eapd:2;
+ u32 input_en_eapd_pad:1;
+ u32 push_pull_mode:1;
+ u32 eapd_pad_output_driver:2;
+ u32 pll_source:1;
+ u32 i2s_bclk_en:1;
+ u32 i2s_bclk_invert:1;
+ u32 pll_ref_clock:1;
+ u32 class_d_shield_clk:1;
+ u32 audio_pll_bypass_mode:1;
+ u32 reserved:4;
+ } r;
+ u32 ulval;
+};
+
+#endif /* __CX2072X_H__ */
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index af23a61b7b28..94e59546c2fe 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1,19 +1,14 @@
-/*
- * DA7210 ALSA Soc codec driver
- *
- * Copyright (c) 2009 Dialog Semiconductor
- * Written by David Chen <Dajun.chen@diasemi.com>
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// DA7210 ALSA Soc codec driver
+//
+// Copyright (c) 2009 Dialog Semiconductor
+// Written by David Chen <Dajun.chen@diasemi.com>
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -331,12 +326,12 @@ static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
if (ucontrol->value.integer.value[0]) {
/* Check if noise suppression is enabled */
- if (snd_soc_read(codec, DA7210_CONTROL) & DA7210_NOISE_SUP_EN) {
- dev_dbg(codec->dev,
+ if (snd_soc_component_read(component, DA7210_CONTROL) & DA7210_NOISE_SUP_EN) {
+ dev_dbg(component->dev,
"Disable noise suppression to enable ALC\n");
return -EINVAL;
}
@@ -354,32 +349,32 @@ static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
u8 val;
if (ucontrol->value.integer.value[0]) {
/* Check if ALC is enabled */
- if (snd_soc_read(codec, DA7210_ADC) & DA7210_ADC_ALC_EN)
+ if (snd_soc_component_read(component, DA7210_ADC) & DA7210_ADC_ALC_EN)
goto err;
/* Check ZC for HP and AUX1 PGA */
- if ((snd_soc_read(codec, DA7210_ZERO_CROSS) &
+ if ((snd_soc_component_read(component, DA7210_ZERO_CROSS) &
(DA7210_AUX1_L_ZC | DA7210_AUX1_R_ZC | DA7210_HP_L_ZC |
DA7210_HP_R_ZC)) != (DA7210_AUX1_L_ZC |
DA7210_AUX1_R_ZC | DA7210_HP_L_ZC | DA7210_HP_R_ZC))
goto err;
/* Check INPGA_L_VOL and INPGA_R_VOL */
- val = snd_soc_read(codec, DA7210_IN_GAIN);
+ val = snd_soc_component_read(component, DA7210_IN_GAIN);
if (((val & DA7210_INPGA_L_VOL) < DA7210_INPGA_MIN_VOL_NS) ||
(((val & DA7210_INPGA_R_VOL) >> 4) <
DA7210_INPGA_MIN_VOL_NS))
goto err;
/* Check AUX1_L_VOL and AUX1_R_VOL */
- if (((snd_soc_read(codec, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
+ if (((snd_soc_component_read(component, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
DA7210_AUX1_MIN_VOL_NS) ||
- ((snd_soc_read(codec, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
+ ((snd_soc_component_read(component, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
DA7210_AUX1_MIN_VOL_NS))
goto err;
}
@@ -760,19 +755,19 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
u32 dai_cfg1;
u32 fs, sysclk;
/* set DAI source to Left and Right ADC */
- snd_soc_write(codec, DA7210_DAI_SRC_SEL,
+ snd_soc_component_write(component, DA7210_DAI_SRC_SEL,
DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
/* Enable DAI */
- snd_soc_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
+ snd_soc_component_write(component, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
- dai_cfg1 = 0xFC & snd_soc_read(codec, DA7210_DAI_CFG1);
+ dai_cfg1 = 0xFC & snd_soc_component_read(component, DA7210_DAI_CFG1);
switch (params_width(params)) {
case 16:
@@ -791,7 +786,7 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+ snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1);
switch (params_rate(params)) {
case 8000:
@@ -839,17 +834,17 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
}
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
if (da7210->mclk_rate && (da7210->mclk_rate != sysclk)) {
/* PLL mode, disable PLL bypass */
- snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
if (!da7210->master) {
/* PLL slave mode, also enable SRM */
- snd_soc_update_bits(codec, DA7210_PLL,
+ snd_soc_component_update_bits(component, DA7210_PLL,
(DA7210_MCLK_SRM_EN |
DA7210_MCLK_DET_EN),
(DA7210_MCLK_SRM_EN |
@@ -857,13 +852,13 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
}
} else {
/* PLL bypass mode, enable PLL bypass and Auto Detection */
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN,
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_MCLK_DET_EN,
DA7210_MCLK_DET_EN);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP,
DA7210_PLL_BYP);
}
/* Enable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1,
+ snd_soc_component_update_bits(component, DA7210_STARTUP1,
DA7210_SC_MST_EN, DA7210_SC_MST_EN);
return 0;
@@ -874,24 +869,24 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
*/
static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
u32 dai_cfg1;
u32 dai_cfg3;
- dai_cfg1 = 0x7f & snd_soc_read(codec, DA7210_DAI_CFG1);
- dai_cfg3 = 0xfc & snd_soc_read(codec, DA7210_DAI_CFG3);
+ dai_cfg1 = 0x7f & snd_soc_component_read(component, DA7210_DAI_CFG1);
+ dai_cfg3 = 0xfc & snd_soc_component_read(component, DA7210_DAI_CFG3);
- if ((snd_soc_read(codec, DA7210_PLL) & DA7210_PLL_EN) &&
- (!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+ if ((snd_soc_component_read(component, DA7210_PLL) & DA7210_PLL_EN) &&
+ (!(snd_soc_component_read(component, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
return -EINVAL;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7210->master = 1;
dai_cfg1 |= DA7210_DAI_MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7210->master = 0;
dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
break;
@@ -923,21 +918,21 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
*/
dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
- snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
- snd_soc_write(codec, DA7210_DAI_CFG3, dai_cfg3);
+ snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1);
+ snd_soc_component_write(component, DA7210_DAI_CFG3, dai_cfg3);
return 0;
}
-static int da7210_mute(struct snd_soc_dai *dai, int mute)
+static int da7210_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 mute_reg = snd_soc_read(codec, DA7210_DAC_HPF) & 0xFB;
+ struct snd_soc_component *component = dai->component;
+ u8 mute_reg = snd_soc_component_read(component, DA7210_DAC_HPF) & 0xFB;
if (mute)
- snd_soc_write(codec, DA7210_DAC_HPF, mute_reg | 0x4);
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg | 0x4);
else
- snd_soc_write(codec, DA7210_DAC_HPF, mute_reg);
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg);
return 0;
}
@@ -947,8 +942,8 @@ static int da7210_mute(struct snd_soc_dai *dai, int mute)
static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case DA7210_CLKSRC_MCLK:
@@ -976,20 +971,22 @@ static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
/**
* da7210_set_dai_pll :Configure the codec PLL
- * @param codec_dai : pointer to codec DAI
- * @param pll_id : da7210 has only one pll, so pll_id is always zero
- * @param fref : MCLK frequency, should be < 20MHz
- * @param fout : FsDM value, Refer page 44 & 45 of datasheet
- * @return int : Zero for success, negative error code for error
+ * @codec_dai: pointer to codec DAI
+ * @pll_id: da7210 has only one pll, so pll_id is always zero
+ * @source: clock source
+ * @fref: MCLK frequency, should be < 20MHz
+ * @fout: FsDM value, Refer page 44 & 45 of datasheet
*
* Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz,
* 19.2MHz, 19.6MHz and 19.8MHz
+ *
+ * Return: Zero for success, negative error code for error
*/
static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int fref, unsigned int fout)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
u8 pll_div1, pll_div2, pll_div3, cnt;
@@ -1014,18 +1011,18 @@ static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
goto err;
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
/* Write PLL dividers */
- snd_soc_write(codec, DA7210_PLL_DIV1, pll_div1);
- snd_soc_write(codec, DA7210_PLL_DIV2, pll_div2);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3,
+ snd_soc_component_write(component, DA7210_PLL_DIV1, pll_div1);
+ snd_soc_component_write(component, DA7210_PLL_DIV2, pll_div2);
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3,
DA7210_PLL_DIV_L_MASK, pll_div3);
/* Enable PLL */
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
/* Enable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN,
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN,
DA7210_SC_MST_EN);
return 0;
err:
@@ -1039,7 +1036,8 @@ static const struct snd_soc_dai_ops da7210_dai_ops = {
.set_fmt = da7210_set_dai_fmt,
.set_sysclk = da7210_set_dai_sysclk,
.set_pll = da7210_set_dai_pll,
- .digital_mute = da7210_mute,
+ .mute_stream = da7210_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da7210_dai = {
@@ -1061,56 +1059,56 @@ static struct snd_soc_dai_driver da7210_dai = {
.formats = DA7210_FORMATS,
},
.ops = &da7210_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da7210_probe(struct snd_soc_codec *codec)
+static int da7210_probe(struct snd_soc_component *component)
{
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
da7210->mclk_rate = 0; /* This will be set from set_sysclk() */
da7210->master = 0; /* This will be set from set_fmt() */
/* Enable internal regulator & bias current */
- snd_soc_write(codec, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
+ snd_soc_component_write(component, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
/*
* ADC settings
*/
/* Enable Left & Right MIC PGA and Mic Bias */
- snd_soc_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
- snd_soc_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
+ snd_soc_component_write(component, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
+ snd_soc_component_write(component, DA7210_MIC_R, DA7210_MIC_R_EN);
/* Enable Left and Right input PGA */
- snd_soc_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
- snd_soc_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
+ snd_soc_component_write(component, DA7210_INMIX_L, DA7210_IN_L_EN);
+ snd_soc_component_write(component, DA7210_INMIX_R, DA7210_IN_R_EN);
/* Enable Left and Right ADC */
- snd_soc_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
+ snd_soc_component_write(component, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
/*
* DAC settings
*/
/* Enable Left and Right DAC */
- snd_soc_write(codec, DA7210_DAC_SEL,
+ snd_soc_component_write(component, DA7210_DAC_SEL,
DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
/* Enable Left and Right out PGA */
- snd_soc_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
- snd_soc_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
+ snd_soc_component_write(component, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
+ snd_soc_component_write(component, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
/* Enable Left and Right HeadPhone PGA */
- snd_soc_write(codec, DA7210_HP_CFG,
+ snd_soc_component_write(component, DA7210_HP_CFG,
DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
/* Enable ramp mode for DAC gain update */
- snd_soc_write(codec, DA7210_SOFTMUTE, DA7210_RAMP_EN);
+ snd_soc_component_write(component, DA7210_SOFTMUTE, DA7210_RAMP_EN);
/*
* For DA7210 codec, there are two ways to enable/disable analog IOs
@@ -1138,42 +1136,43 @@ static int da7210_probe(struct snd_soc_codec *codec)
*/
/* Enable Line out amplifiers */
- snd_soc_write(codec, DA7210_OUT1_L, DA7210_OUT1_L_EN);
- snd_soc_write(codec, DA7210_OUT1_R, DA7210_OUT1_R_EN);
- snd_soc_write(codec, DA7210_OUT2, DA7210_OUT2_EN |
+ snd_soc_component_write(component, DA7210_OUT1_L, DA7210_OUT1_L_EN);
+ snd_soc_component_write(component, DA7210_OUT1_R, DA7210_OUT1_R_EN);
+ snd_soc_component_write(component, DA7210_OUT2, DA7210_OUT2_EN |
DA7210_OUT2_OUTMIX_L | DA7210_OUT2_OUTMIX_R);
/* Enable Aux1 */
- snd_soc_write(codec, DA7210_AUX1_L, DA7210_AUX1_L_EN);
- snd_soc_write(codec, DA7210_AUX1_R, DA7210_AUX1_R_EN);
+ snd_soc_component_write(component, DA7210_AUX1_L, DA7210_AUX1_L_EN);
+ snd_soc_component_write(component, DA7210_AUX1_R, DA7210_AUX1_R_EN);
/* Enable Aux2 */
- snd_soc_write(codec, DA7210_AUX2, DA7210_AUX2_EN);
+ snd_soc_component_write(component, DA7210_AUX2, DA7210_AUX2_EN);
/* Set PLL Master clock range 10-20 MHz, enable PLL bypass */
- snd_soc_write(codec, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
+ snd_soc_component_write(component, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
DA7210_PLL_BYP);
/* Diable PLL and bypass it */
- snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+ snd_soc_component_write(component, DA7210_PLL, DA7210_PLL_FS_48000);
/* Activate all enabled subsystem */
- snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+ snd_soc_component_write(component, DA7210_STARTUP1, DA7210_SC_MST_EN);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
+static const struct snd_soc_component_driver soc_component_dev_da7210 = {
.probe = da7210_probe,
-
.controls = da7210_snd_controls,
.num_controls = ARRAY_SIZE(da7210_snd_controls),
-
.dapm_widgets = da7210_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets),
.dapm_routes = da7210_audio_map,
.num_dapm_routes = ARRAY_SIZE(da7210_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
#if IS_ENABLED(CONFIG_I2C)
@@ -1206,8 +1205,7 @@ static const struct regmap_config da7210_regmap_config_i2c = {
.cache_type = REGCACHE_RBTREE,
};
-static int da7210_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7210_i2c_probe(struct i2c_client *i2c)
{
struct da7210_priv *da7210;
int ret;
@@ -1231,22 +1229,16 @@ static int da7210_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
if (ret < 0)
- dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int da7210_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id da7210_i2c_id[] = {
- { "da7210", 0 },
+ { "da7210" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
@@ -1257,7 +1249,6 @@ static struct i2c_driver da7210_i2c_driver = {
.name = "da7210",
},
.probe = da7210_i2c_probe,
- .remove = da7210_i2c_remove,
.id_table = da7210_i2c_id,
};
#endif
@@ -1324,24 +1315,17 @@ static int da7210_spi_probe(struct spi_device *spi)
if (ret != 0)
dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
return ret;
}
-static int da7210_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver da7210_spi_driver = {
.driver = {
.name = "da7210",
},
.probe = da7210_spi_probe,
- .remove = da7210_spi_remove
};
#endif
@@ -1350,6 +1334,8 @@ static int __init da7210_modinit(void)
int ret = 0;
#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&da7210_i2c_driver);
+ if (ret)
+ return ret;
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&da7210_spi_driver);
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 7278f93460c1..0a2b50cdea95 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA7213 ALSA SoC Codec Driver
*
@@ -5,13 +6,11 @@
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
* Based on DA9055 ALSA SoC codec driver.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -20,6 +19,8 @@
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/units.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -55,6 +56,7 @@ static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0);
static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0);
static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0);
static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7213_tonegen_gain_tlv, -4500, 300, 0);
/* ADC and DAC voice mode (8kHz) high pass cutoff value */
static const char * const da7213_voice_hpf_corner_txt[] = {
@@ -86,6 +88,23 @@ static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
DA7213_AUDIO_HPF_CORNER_SHIFT,
da7213_audio_hpf_corner_txt);
+static const char * const da7213_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7213_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7213_TONE_GEN_CFG1, DA7213_DTMF_REG_SHIFT,
+ DA7213_DTMF_REG_MAX, da7213_tonegen_dtmf_key_txt);
+
+static const char * const da7213_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "Sum"
+};
+
+static const struct soc_enum da7213_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7213_TONE_GEN_CFG2, DA7213_SWG_SEL_SHIFT,
+ DA7213_SWG_SEL_MAX, da7213_tonegen_swg_sel_txt);
+
/* Gain ramping rate value */
static const char * const da7213_gain_ramp_rate_txt[] = {
"nominal rate * 8", "nominal rate * 16", "nominal rate / 16",
@@ -191,7 +210,65 @@ static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate,
* Control Functions
*/
-static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+/* Locked Kcontrol calls */
+static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+/* ALC */
+static int da7213_get_alc_data(struct snd_soc_component *component, u8 reg_val)
{
int mid_data, top_data;
int sum = 0;
@@ -200,17 +277,17 @@ static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS;
iteration++) {
/* Select the left or right channel and capture data */
- snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val);
/* Select middle 8 bits for read back from data register */
- snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
reg_val | DA7213_ALC_DATA_MIDDLE);
- mid_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+ mid_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA);
/* Select top 8 bits for read back from data register */
- snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
reg_val | DA7213_ALC_DATA_TOP);
- top_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+ top_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA);
sum += ((mid_data << 8) | (top_data << 16));
}
@@ -218,17 +295,17 @@ static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
return sum / DA7213_ALC_AVG_ITERATIONS;
}
-static void da7213_alc_calib_man(struct snd_soc_codec *codec)
+static void da7213_alc_calib_man(struct snd_soc_component *component)
{
u8 reg_val;
int avg_left_data, avg_right_data, offset_l, offset_r;
/* Calculate average for Left and Right data */
/* Left Data */
- avg_left_data = da7213_get_alc_data(codec,
+ avg_left_data = da7213_get_alc_data(component,
DA7213_ALC_CIC_OP_CHANNEL_LEFT);
/* Right Data */
- avg_right_data = da7213_get_alc_data(codec,
+ avg_right_data = da7213_get_alc_data(component,
DA7213_ALC_CIC_OP_CHANNEL_RIGHT);
/* Calculate DC offset */
@@ -236,122 +313,122 @@ static void da7213_alc_calib_man(struct snd_soc_codec *codec)
offset_r = -avg_right_data;
reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_M_L, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_M_L, reg_val);
reg_val = (offset_l & DA7213_ALC_OFFSET_19_16) >> 16;
- snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_U_L, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_L, reg_val);
reg_val = (offset_r & DA7213_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_M_R, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_M_R, reg_val);
reg_val = (offset_r & DA7213_ALC_OFFSET_19_16) >> 16;
- snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
/* Enable analog/digital gain mode & offset cancellation */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE,
DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE);
}
-static void da7213_alc_calib_auto(struct snd_soc_codec *codec)
+static void da7213_alc_calib_auto(struct snd_soc_component *component)
{
u8 alc_ctrl1;
/* Begin auto calibration and wait for completion */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
DA7213_ALC_AUTO_CALIB_EN);
do {
- alc_ctrl1 = snd_soc_read(codec, DA7213_ALC_CTRL1);
+ alc_ctrl1 = snd_soc_component_read(component, DA7213_ALC_CTRL1);
} while (alc_ctrl1 & DA7213_ALC_AUTO_CALIB_EN);
/* If auto calibration fails, fall back to digital gain only mode */
if (alc_ctrl1 & DA7213_ALC_CALIB_OVERFLOW) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed with overflow\n");
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE,
0);
} else {
/* Enable analog/digital gain mode & offset cancellation */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE,
DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE);
}
}
-static void da7213_alc_calib(struct snd_soc_codec *codec)
+static void da7213_alc_calib(struct snd_soc_component *component)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
u8 adc_l_ctrl, adc_r_ctrl;
u8 mixin_l_sel, mixin_r_sel;
u8 mic_1_ctrl, mic_2_ctrl;
/* Save current values from ADC control registers */
- adc_l_ctrl = snd_soc_read(codec, DA7213_ADC_L_CTRL);
- adc_r_ctrl = snd_soc_read(codec, DA7213_ADC_R_CTRL);
+ adc_l_ctrl = snd_soc_component_read(component, DA7213_ADC_L_CTRL);
+ adc_r_ctrl = snd_soc_component_read(component, DA7213_ADC_R_CTRL);
/* Save current values from MIXIN_L/R_SELECT registers */
- mixin_l_sel = snd_soc_read(codec, DA7213_MIXIN_L_SELECT);
- mixin_r_sel = snd_soc_read(codec, DA7213_MIXIN_R_SELECT);
+ mixin_l_sel = snd_soc_component_read(component, DA7213_MIXIN_L_SELECT);
+ mixin_r_sel = snd_soc_component_read(component, DA7213_MIXIN_R_SELECT);
/* Save current values from MIC control registers */
- mic_1_ctrl = snd_soc_read(codec, DA7213_MIC_1_CTRL);
- mic_2_ctrl = snd_soc_read(codec, DA7213_MIC_2_CTRL);
+ mic_1_ctrl = snd_soc_component_read(component, DA7213_MIC_1_CTRL);
+ mic_2_ctrl = snd_soc_component_read(component, DA7213_MIC_2_CTRL);
/* Enable ADC Left and Right */
- snd_soc_update_bits(codec, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
DA7213_ADC_EN);
- snd_soc_update_bits(codec, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
DA7213_ADC_EN);
/* Enable MIC paths */
- snd_soc_update_bits(codec, DA7213_MIXIN_L_SELECT,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_SELECT,
DA7213_MIXIN_L_MIX_SELECT_MIC_1 |
DA7213_MIXIN_L_MIX_SELECT_MIC_2,
DA7213_MIXIN_L_MIX_SELECT_MIC_1 |
DA7213_MIXIN_L_MIX_SELECT_MIC_2);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_SELECT,
DA7213_MIXIN_R_MIX_SELECT_MIC_2 |
DA7213_MIXIN_R_MIX_SELECT_MIC_1,
DA7213_MIXIN_R_MIX_SELECT_MIC_2 |
DA7213_MIXIN_R_MIX_SELECT_MIC_1);
/* Mute MIC PGAs */
- snd_soc_update_bits(codec, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
+ snd_soc_component_update_bits(component, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
DA7213_MUTE_EN);
- snd_soc_update_bits(codec, DA7213_MIC_2_CTRL, DA7213_MUTE_EN,
+ snd_soc_component_update_bits(component, DA7213_MIC_2_CTRL, DA7213_MUTE_EN,
DA7213_MUTE_EN);
/* Perform calibration */
if (da7213->alc_calib_auto)
- da7213_alc_calib_auto(codec);
+ da7213_alc_calib_auto(component);
else
- da7213_alc_calib_man(codec);
+ da7213_alc_calib_man(component);
/* Restore MIXIN_L/R_SELECT registers to their original states */
- snd_soc_write(codec, DA7213_MIXIN_L_SELECT, mixin_l_sel);
- snd_soc_write(codec, DA7213_MIXIN_R_SELECT, mixin_r_sel);
+ snd_soc_component_write(component, DA7213_MIXIN_L_SELECT, mixin_l_sel);
+ snd_soc_component_write(component, DA7213_MIXIN_R_SELECT, mixin_r_sel);
/* Restore ADC control registers to their original states */
- snd_soc_write(codec, DA7213_ADC_L_CTRL, adc_l_ctrl);
- snd_soc_write(codec, DA7213_ADC_R_CTRL, adc_r_ctrl);
+ snd_soc_component_write(component, DA7213_ADC_L_CTRL, adc_l_ctrl);
+ snd_soc_component_write(component, DA7213_ADC_R_CTRL, adc_r_ctrl);
/* Restore original values of MIC control registers */
- snd_soc_write(codec, DA7213_MIC_1_CTRL, mic_1_ctrl);
- snd_soc_write(codec, DA7213_MIC_2_CTRL, mic_2_ctrl);
+ snd_soc_component_write(component, DA7213_MIC_1_CTRL, mic_1_ctrl);
+ snd_soc_component_write(component, DA7213_MIC_2_CTRL, mic_2_ctrl);
}
static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
int ret;
ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
/* If ALC in operation, make sure calibrated offsets are updated */
if ((!ret) && (da7213->alc_en))
- da7213_alc_calib(codec);
+ da7213_alc_calib(component);
return ret;
}
@@ -359,14 +436,14 @@ static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
/* Force ALC offset calibration if enabling ALC */
if (ucontrol->value.integer.value[0] ||
ucontrol->value.integer.value[1]) {
if (!da7213->alc_en) {
- da7213_alc_calib(codec);
+ da7213_alc_calib(component);
da7213->alc_en = true;
}
} else {
@@ -376,6 +453,64 @@ static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
return snd_soc_put_volsw(kcontrol, ucontrol);
}
+/* ToneGen */
+static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ __le16 val;
+ int ret;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7213->ctrl_lock);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to host endianness here.
+ */
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ __le16 val_new, val_old;
+ int ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to little endian here to align with
+ * HW registers.
+ */
+ val_new = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old));
+ if (ret == 0 && (val_old != val_new))
+ ret = regmap_raw_write(da7213->regmap, reg,
+ &val_new, sizeof(val_new));
+ mutex_unlock(&da7213->ctrl_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return val_old != val_new;
+}
/*
* KControls
@@ -477,6 +612,37 @@ static const struct snd_kcontrol_new da7213_snd_controls[] = {
SOC_DOUBLE_R("Headphone ZC Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL,
DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT),
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7213_TONE_GEN_CFG2,
+ DA7213_TONE_GEN_GAIN_SHIFT, DA7213_TONE_GEN_GAIN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put, da7213_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7213_tonegen_dtmf_key,
+ da7213_enum_locked_get, da7213_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7213_TONE_GEN_CFG1,
+ DA7213_DTMF_EN_SHIFT, DA7213_SWITCH_EN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_SINGLE_EXT("ToneGen Start", DA7213_TONE_GEN_CFG1,
+ DA7213_START_STOPN_SHIFT, DA7213_SWITCH_EN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7213_tonegen_swg_sel,
+ da7213_enum_locked_get, da7213_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7213_TONE_GEN_FREQ1_L,
+ DA7213_FREQ1_L_SHIFT, DA7213_FREQ_MAX, DA7213_NO_INVERT,
+ da7213_tonegen_freq_get, da7213_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7213_TONE_GEN_FREQ2_L,
+ DA7213_FREQ2_L_SHIFT, DA7213_FREQ_MAX, DA7213_NO_INVERT,
+ da7213_tonegen_freq_get, da7213_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7213_TONE_GEN_ON_PER,
+ DA7213_BEEP_ON_PER_SHIFT, DA7213_BEEP_ON_OFF_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7213_TONE_GEN_OFF_PER,
+ DA7213_BEEP_OFF_PER_SHIFT, DA7213_BEEP_ON_OFF_MAX,
+ DA7213_NO_INVERT),
+
/* Gain Ramping controls */
SOC_DOUBLE_R("Aux Gain Ramping Switch", DA7213_AUX_L_CTRL,
DA7213_AUX_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
@@ -726,17 +892,100 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
/*
+ * DAPM Events
+ */
+
+static int da7213_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 pll_ctrl, pll_status;
+ int i = 0;
+ bool srm_lock = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable DAI clks for master mode */
+ if (da7213->master)
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_CLK_EN_MASK,
+ DA7213_DAI_CLK_EN_MASK);
+
+ /* PC synchronised to DAI */
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT,
+ DA7213_PC_FREERUN_MASK, 0);
+
+ /* If SRM not enabled then nothing more to do */
+ pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL);
+ if (!(pll_ctrl & DA7213_PLL_SRM_EN))
+ return 0;
+
+ /* Assist 32KHz mode PLL lock */
+ if (pll_ctrl & DA7213_PLL_32K_MODE) {
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0xF2, 0x03);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ }
+
+ /* Check SRM has locked */
+ do {
+ pll_status = snd_soc_component_read(component, DA7213_PLL_STATUS);
+ if (pll_status & DA7213_PLL_SRM_LOCK) {
+ srm_lock = true;
+ } else {
+ ++i;
+ msleep(50);
+ }
+ } while ((i < DA7213_SRM_CHECK_RETRIES) && (!srm_lock));
+
+ if (!srm_lock)
+ dev_warn(component->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Revert 32KHz PLL lock udpates if applied previously */
+ pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL);
+ if (pll_ctrl & DA7213_PLL_32K_MODE) {
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0xF2, 0x01);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ }
+
+ /* PC free-running */
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT,
+ DA7213_PC_FREERUN_MASK,
+ DA7213_PC_FREERUN_MASK);
+
+ /* Disable DAI clks if in master mode */
+ if (da7213->master)
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_CLK_EN_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
* DAPM widgets
*/
static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = {
/*
+ * Power Supply
+ */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDMIC", 0, 0),
+
+ /*
* Input & Output
*/
/* Use a supply here as this controls both input & output DAIs */
SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT,
- DA7213_NO_INVERT, NULL, 0),
+ DA7213_NO_INVERT, da7213_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/*
* Input
@@ -855,6 +1104,9 @@ static const struct snd_soc_dapm_route da7213_audio_map[] = {
/* Dest Connecting Widget source */
/* Input path */
+ {"Mic Bias 1", NULL, "VDDMIC"},
+ {"Mic Bias 2", NULL, "VDDMIC"},
+
{"MIC1", NULL, "Mic Bias 1"},
{"MIC2", NULL, "Mic Bias 2"},
@@ -1059,9 +1311,9 @@ static bool da7213_volatile_register(struct device *dev, unsigned int reg)
case DA7213_ALC_OFFSET_AUTO_M_R:
case DA7213_ALC_OFFSET_AUTO_U_R:
case DA7213_ALC_CIC_OP_LVL_DATA:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1069,14 +1321,33 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_64;
u8 dai_ctrl = 0;
u8 fs;
+ /* Set channels */
+ switch (params_channels(params)) {
+ case 1:
+ if (da7213->fmt != DA7213_DAI_FORMAT_DSP) {
+ dev_err(component->dev, "Mono supported only in DSP mode\n");
+ return -EINVAL;
+ }
+ dai_ctrl |= DA7213_DAI_MONO_MODE_EN;
+ break;
+ case 2:
+ dai_ctrl &= ~(DA7213_DAI_MONO_MODE_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
/* Set DAI format */
switch (params_width(params)) {
case 16:
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
+ dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_32; /* 32bit for 1ch and 2ch */
break;
case 20:
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
@@ -1095,59 +1366,71 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) {
case 8000:
fs = DA7213_SR_8000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
break;
case 11025:
fs = DA7213_SR_11025;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
break;
case 12000:
fs = DA7213_SR_12000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
break;
case 16000:
fs = DA7213_SR_16000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
break;
case 22050:
fs = DA7213_SR_22050;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
break;
case 32000:
fs = DA7213_SR_32000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
break;
case 44100:
fs = DA7213_SR_44100;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
break;
case 48000:
fs = DA7213_SR_48000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
break;
case 88200:
fs = DA7213_SR_88200;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800;
break;
case 96000:
fs = DA7213_SR_96000;
+ da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000;
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_WORD_LENGTH_MASK,
- dai_ctrl);
- snd_soc_write(codec, DA7213_SR, fs);
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_BCLKS_PER_WCLK_MASK, dai_clk_mode);
+
+ snd_soc_component_update_bits(component, DA7213_DAI_CTRL,
+ DA7213_DAI_WORD_LENGTH_MASK | DA7213_DAI_MONO_MODE_MASK, dai_ctrl);
+ snd_soc_component_write(component, DA7213_SR, fs);
return 0;
}
static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
u8 dai_clk_mode = 0, dai_ctrl = 0;
+ u8 dai_offset = 0;
/* Set master/slave mode */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE;
+ case SND_SOC_DAIFMT_CBP_CFP:
da7213->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE;
+ case SND_SOC_DAIFMT_CBC_CFC:
da7213->master = false;
break;
default:
@@ -1155,17 +1438,46 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* Set clock normal/inverted */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_NB_IF:
- dai_clk_mode |= DA7213_DAI_WCLK_POL_INV;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- dai_clk_mode |= DA7213_DAI_CLK_POL_INV;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7213_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV |
+ DA7213_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case SND_SOC_DAIFMT_IB_IF:
- dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | DA7213_DAI_CLK_POL_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* The bclk is inverted wrt ASoC conventions */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dai_clk_mode |= DA7213_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV |
+ DA7213_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -1175,40 +1487,56 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE;
+ da7213->fmt = DA7213_DAI_FORMAT_I2S_MODE;
break;
case SND_SOC_DAIFMT_LEFT_J:
dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J;
+ da7213->fmt = DA7213_DAI_FORMAT_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J;
+ da7213->fmt = DA7213_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
+ dai_ctrl |= DA7213_DAI_FORMAT_DSP;
+ dai_offset = 1;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
+ dai_ctrl |= DA7213_DAI_FORMAT_DSP;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
break;
default:
return -EINVAL;
}
- /* By default only 32 BCLK per WCLK is supported */
- dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_32;
+ /* By default only 64 BCLK per WCLK is supported */
+ dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64;
- snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode);
- snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_BCLKS_PER_WCLK_MASK |
+ DA7213_DAI_CLK_POL_MASK | DA7213_DAI_WCLK_POL_MASK,
+ dai_clk_mode);
+ snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
dai_ctrl);
+ snd_soc_component_write(component, DA7213_DAI_OFFSET, dai_offset);
return 0;
}
-static int da7213_mute(struct snd_soc_dai *dai, int mute)
+static int da7213_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_MUTE_EN, DA7213_MUTE_EN);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_MUTE_EN, DA7213_MUTE_EN);
} else {
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_MUTE_EN, 0);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_MUTE_EN, 0);
}
@@ -1218,31 +1546,38 @@ static int da7213_mute(struct snd_soc_dai *dai, int mute)
#define DA7213_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
+static int da7213_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
int ret = 0;
if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq))
return 0;
- if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) {
- dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ /* Maybe audio stream is closing. */
+ if (freq == 0)
+ return 0;
+
+ if (((freq < da7213->fin_min_rate) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(component->dev, "Unsupported MCLK value %d\n",
freq);
return -EINVAL;
}
switch (clk_id) {
case DA7213_CLKSRC_MCLK:
- da7213->mclk_squarer_en = false;
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_MCLK_SQR_EN, 0);
break;
case DA7213_CLKSRC_MCLK_SQR:
- da7213->mclk_squarer_en = true;
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_MCLK_SQR_EN,
+ DA7213_PLL_MCLK_SQR_EN);
break;
default:
- dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ dev_err(component->dev, "Unknown clock source %d\n", clk_id);
return -EINVAL;
}
@@ -1252,7 +1587,7 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
freq = clk_round_rate(da7213->mclk, freq);
ret = clk_set_rate(da7213->mclk, freq);
if (ret) {
- dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ dev_err(component->dev, "Failed to set clock rate %d\n",
freq);
return ret;
}
@@ -1263,73 +1598,89 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-/* Supported PLL input frequencies are 5MHz - 54MHz. */
-static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- int source, unsigned int fref, unsigned int fout)
+/* Supported PLL input frequencies are 32KHz, 5MHz - 54MHz. */
+static int _da7213_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source,
+ unsigned int fref, unsigned int fout)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
u8 pll_ctrl, indiv_bits, indiv;
u8 pll_frac_top, pll_frac_bot, pll_integer;
u32 freq_ref;
u64 frac_div;
- /* Reset PLL configuration */
- snd_soc_write(codec, DA7213_PLL_CTRL, 0);
-
- pll_ctrl = 0;
-
/* Workout input divider based on MCLK rate */
- if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) {
+ if (da7213->mclk_rate == 32768) {
+ if (!da7213->master) {
+ dev_err(component->dev,
+ "32KHz only valid if codec is clock master\n");
+ return -EINVAL;
+ }
+
/* 32KHz PLL Mode */
- indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
- indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
+ indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
+ source = DA7213_SYSCLK_PLL_32KHZ;
freq_ref = 3750000;
- pll_ctrl |= DA7213_PLL_32K_MODE;
+
} else {
- /* 5 - 54MHz MCLK */
if (da7213->mclk_rate < 5000000) {
- goto pll_err;
- } else if (da7213->mclk_rate <= 10000000) {
- indiv_bits = DA7213_PLL_INDIV_5_10_MHZ;
- indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL;
- } else if (da7213->mclk_rate <= 20000000) {
- indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
- indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
- } else if (da7213->mclk_rate <= 40000000) {
- indiv_bits = DA7213_PLL_INDIV_20_40_MHZ;
- indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL;
+ dev_err(component->dev,
+ "PLL input clock %d below valid range\n",
+ da7213->mclk_rate);
+ return -EINVAL;
+ } else if (da7213->mclk_rate <= 9000000) {
+ indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ;
+ indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL;
+ } else if (da7213->mclk_rate <= 18000000) {
+ indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
+ } else if (da7213->mclk_rate <= 36000000) {
+ indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ;
+ indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL;
} else if (da7213->mclk_rate <= 54000000) {
- indiv_bits = DA7213_PLL_INDIV_40_54_MHZ;
- indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL;
+ indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- goto pll_err;
+ dev_err(component->dev,
+ "PLL input clock %d above valid range\n",
+ da7213->mclk_rate);
+ return -EINVAL;
}
freq_ref = (da7213->mclk_rate / indiv);
}
- pll_ctrl |= indiv_bits;
+ pll_ctrl = indiv_bits;
- /* PLL Bypass mode */
- if (source == DA7213_SYSCLK_MCLK) {
- snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+ /* Configure PLL */
+ switch (source) {
+ case DA7213_SYSCLK_MCLK:
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_INDIV_MASK |
+ DA7213_PLL_MODE_MASK, pll_ctrl);
return 0;
- }
+ case DA7213_SYSCLK_PLL:
+ break;
+ case DA7213_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7213_PLL_SRM_EN;
+ fout = DA7213_PLL_FREQ_OUT_94310400;
+ break;
+ case DA7213_SYSCLK_PLL_32KHZ:
+ if (da7213->mclk_rate != 32768) {
+ dev_err(component->dev,
+ "32KHz mode only valid with 32KHz MCLK\n");
+ return -EINVAL;
+ }
- /*
- * If Codec is slave and SRM enabled,
- * freq_out is (98304000 + 90316800)/2 = 94310400
- */
- if (!da7213->master && da7213->srm_en) {
+ pll_ctrl |= DA7213_PLL_32K_MODE | DA7213_PLL_SRM_EN;
fout = DA7213_PLL_FREQ_OUT_94310400;
- pll_ctrl |= DA7213_PLL_SRM_EN;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL config\n");
+ return -EINVAL;
}
- /* Enable MCLK squarer if required */
- if (da7213->mclk_squarer_en)
- pll_ctrl |= DA7213_PLL_MCLK_SQR_EN;
-
/* Calculate dividers for PLL */
pll_integer = fout / freq_ref;
frac_div = (u64)(fout % freq_ref) * 8192ULL;
@@ -1338,29 +1689,61 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_frac_bot = (frac_div) & DA7213_BYTE_MASK;
/* Write PLL dividers */
- snd_soc_write(codec, DA7213_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7213_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7213_PLL_INTEGER, pll_integer);
+ snd_soc_component_write(component, DA7213_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA7213_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA7213_PLL_INTEGER, pll_integer);
/* Enable PLL */
pll_ctrl |= DA7213_PLL_EN;
- snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+ snd_soc_component_update_bits(component, DA7213_PLL_CTRL,
+ DA7213_PLL_INDIV_MASK | DA7213_PLL_MODE_MASK,
+ pll_ctrl);
+
+ /* Assist 32KHz mode PLL lock */
+ if (source == DA7213_SYSCLK_PLL_32KHZ) {
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0xF1, 0x03);
+ snd_soc_component_write(component, 0xF1, 0x01);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ }
return 0;
+}
-pll_err:
- dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n",
- da7213->mclk_rate);
- return -EINVAL;
+static int da7213_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source,
+ unsigned int fref, unsigned int fout)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ da7213->fixed_clk_auto_pll = false;
+
+ return _da7213_set_component_pll(component, pll_id, source, fref, fout);
}
+/*
+ * Select below from Sound Card, not Auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 da7213_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
/* DAI operations */
static const struct snd_soc_dai_ops da7213_dai_ops = {
.hw_params = da7213_hw_params,
.set_fmt = da7213_set_dai_fmt,
- .set_sysclk = da7213_set_dai_sysclk,
- .set_pll = da7213_set_dai_pll,
- .digital_mute = da7213_mute,
+ .mute_stream = da7213_mute,
+ .no_capture_mute = 1,
+ .auto_selectable_formats = &da7213_dai_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver da7213_dai = {
@@ -1382,59 +1765,125 @@ static struct snd_soc_dai_driver da7213_dai = {
.formats = DA7213_FORMATS,
},
.ops = &da7213_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da7213_set_bias_level(struct snd_soc_codec *codec,
+static int da7213_set_auto_pll(struct snd_soc_component *component, bool enable)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ int mode;
+
+ if (!da7213->fixed_clk_auto_pll)
+ return 0;
+
+ da7213->mclk_rate = clk_get_rate(da7213->mclk);
+
+ if (enable) {
+ /* Slave mode needs SRM for non-harmonic frequencies */
+ if (da7213->master)
+ mode = DA7213_SYSCLK_PLL;
+ else
+ mode = DA7213_SYSCLK_PLL_SRM;
+
+ /* PLL is not required for harmonic frequencies */
+ switch (da7213->out_rate) {
+ case DA7213_PLL_FREQ_OUT_90316800:
+ if (da7213->mclk_rate == 11289600 ||
+ da7213->mclk_rate == 22579200 ||
+ da7213->mclk_rate == 45158400)
+ mode = DA7213_SYSCLK_MCLK;
+ break;
+ case DA7213_PLL_FREQ_OUT_98304000:
+ if (da7213->mclk_rate == 12288000 ||
+ da7213->mclk_rate == 24576000 ||
+ da7213->mclk_rate == 49152000)
+ mode = DA7213_SYSCLK_MCLK;
+
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ /* Disable PLL in standby */
+ mode = DA7213_SYSCLK_MCLK;
+ }
+
+ return _da7213_set_component_pll(component, 0, mode,
+ da7213->mclk_rate, da7213->out_rate);
+}
+
+static int da7213_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
break;
- case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- /* MCLK */
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
if (da7213->mclk) {
ret = clk_prepare_enable(da7213->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable mclk\n");
return ret;
}
- }
+ da7213_set_auto_pll(component, true);
+ }
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
- snd_soc_update_bits(codec, DA7213_REFERENCES,
+ snd_soc_component_update_bits(component, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN,
DA7213_VMID_EN | DA7213_BIAS_EN);
+ } else {
+ /* Remove MCLK */
+ if (da7213->mclk) {
+ da7213_set_auto_pll(component, false);
+ clk_disable_unprepare(da7213->mclk);
+ }
}
break;
case SND_SOC_BIAS_OFF:
/* Disable VMID reference & master bias */
- snd_soc_update_bits(codec, DA7213_REFERENCES,
+ snd_soc_component_update_bits(component, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
-
- /* MCLK */
- if (da7213->mclk)
- clk_disable_unprepare(da7213->mclk);
break;
}
return 0;
}
+#define DA7213_FIN_MIN_RATE (5 * MEGA)
+#define DA7212_FIN_MIN_RATE (2 * MEGA)
+
+#if defined(CONFIG_OF)
/* DT */
static const struct of_device_id da7213_of_match[] = {
- { .compatible = "dlg,da7213", },
+ { .compatible = "dlg,da7212", .data = (void *)DA7212_FIN_MIN_RATE },
+ { .compatible = "dlg,da7213", .data = (void *)DA7213_FIN_MIN_RATE },
{ }
};
MODULE_DEVICE_TABLE(of, da7213_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7213_acpi_match[] = {
+ { "DLGS7212", DA7212_FIN_MIN_RATE },
+ { "DLGS7213", DA7213_FIN_MIN_RATE },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
+#endif
static enum da7213_micbias_voltage
- da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+ da7213_of_micbias_lvl(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1600:
@@ -1446,39 +1895,39 @@ static enum da7213_micbias_voltage
case 3000:
return DA7213_MICBIAS_3_0V;
default:
- dev_warn(codec->dev, "Invalid micbias level\n");
+ dev_warn(component->dev, "Invalid micbias level\n");
return DA7213_MICBIAS_2_2V;
}
}
static enum da7213_dmic_data_sel
- da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+ da7213_of_dmic_data_sel(struct snd_soc_component *component, const char *str)
{
if (!strcmp(str, "lrise_rfall")) {
return DA7213_DMIC_DATA_LRISE_RFALL;
} else if (!strcmp(str, "lfall_rrise")) {
return DA7213_DMIC_DATA_LFALL_RRISE;
} else {
- dev_warn(codec->dev, "Invalid DMIC data select type\n");
+ dev_warn(component->dev, "Invalid DMIC data select type\n");
return DA7213_DMIC_DATA_LRISE_RFALL;
}
}
static enum da7213_dmic_samplephase
- da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+ da7213_of_dmic_samplephase(struct snd_soc_component *component, const char *str)
{
if (!strcmp(str, "on_clkedge")) {
return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
} else if (!strcmp(str, "between_clkedge")) {
return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE;
} else {
- dev_warn(codec->dev, "Invalid DMIC sample phase\n");
+ dev_warn(component->dev, "Invalid DMIC sample phase\n");
return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
}
}
static enum da7213_dmic_clk_rate
- da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+ da7213_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1500000:
@@ -1486,89 +1935,89 @@ static enum da7213_dmic_clk_rate
case 3000000:
return DA7213_DMIC_CLK_3_0MHZ;
default:
- dev_warn(codec->dev, "Invalid DMIC clock rate\n");
+ dev_warn(component->dev, "Invalid DMIC clock rate\n");
return DA7213_DMIC_CLK_1_5MHZ;
}
}
static struct da7213_platform_data
- *da7213_of_to_pdata(struct snd_soc_codec *codec)
+ *da7213_fw_to_pdata(struct snd_soc_component *component)
{
- struct device_node *np = codec->dev->of_node;
+ struct device *dev = component->dev;
struct da7213_platform_data *pdata;
- const char *of_str;
- u32 of_val32;
+ const char *fw_str;
+ u32 fw_val32;
- pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_warn(codec->dev, "Failed to allocate memory for pdata\n");
+ pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
return NULL;
- }
- if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0)
- pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ if (device_property_read_u32(dev, "dlg,micbias1-lvl", &fw_val32) >= 0)
+ pdata->micbias1_lvl = da7213_of_micbias_lvl(component, fw_val32);
else
pdata->micbias1_lvl = DA7213_MICBIAS_2_2V;
- if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0)
- pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ if (device_property_read_u32(dev, "dlg,micbias2-lvl", &fw_val32) >= 0)
+ pdata->micbias2_lvl = da7213_of_micbias_lvl(component, fw_val32);
else
pdata->micbias2_lvl = DA7213_MICBIAS_2_2V;
- if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str))
- pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str);
+ if (!device_property_read_string(dev, "dlg,dmic-data-sel", &fw_str))
+ pdata->dmic_data_sel = da7213_of_dmic_data_sel(component, fw_str);
else
pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL;
- if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str))
+ if (!device_property_read_string(dev, "dlg,dmic-samplephase", &fw_str))
pdata->dmic_samplephase =
- da7213_of_dmic_samplephase(codec, of_str);
+ da7213_of_dmic_samplephase(component, fw_str);
else
pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE;
- if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0)
- pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32);
+ if (device_property_read_u32(dev, "dlg,dmic-clkrate", &fw_val32) >= 0)
+ pdata->dmic_clk_rate = da7213_of_dmic_clkrate(component, fw_val32);
else
pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ;
return pdata;
}
-
-static int da7213_probe(struct snd_soc_codec *codec)
+static int da7213_probe(struct snd_soc_component *component)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+
+ pm_runtime_get_sync(component->dev);
/* Default to using ALC auto offset calibration mode. */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
DA7213_ALC_CALIB_MODE_MAN, 0);
da7213->alc_calib_auto = true;
- /* Default to using SRM for slave mode */
- da7213->srm_en = true;
+ /* Default PC counter to free-running */
+ snd_soc_component_update_bits(component, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
+ DA7213_PC_FREERUN_MASK);
/* Enable all Gain Ramps */
- snd_soc_update_bits(codec, DA7213_AUX_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_AUX_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_AUX_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_AUX_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA7213_LINE_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
/*
@@ -1579,29 +2028,28 @@ static int da7213_probe(struct snd_soc_codec *codec)
* being managed by DAPM while other (non power related) bits are
* enabled here
*/
- snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_L_CTRL,
DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXOUT_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_R_CTRL,
DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
- snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
- snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
- snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA7213_LINE_CTRL,
DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
/* Handle DT/Platform data */
- if (codec->dev->of_node)
- da7213->pdata = da7213_of_to_pdata(codec);
- else
- da7213->pdata = dev_get_platdata(codec->dev);
+ da7213->pdata = dev_get_platdata(component->dev);
+ if (!da7213->pdata)
+ da7213->pdata = da7213_fw_to_pdata(component);
/* Set platform data values */
if (da7213->pdata) {
@@ -1627,7 +2075,7 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_MICBIAS2_LEVEL_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7213_MICBIAS_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MICBIAS_CTRL,
DA7213_MICBIAS1_LEVEL_MASK |
DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl);
@@ -1653,60 +2101,143 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_CLK_RATE_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7213_MIC_CONFIG,
+ snd_soc_component_update_bits(component, DA7213_MIC_CONFIG,
DA7213_DMIC_DATA_SEL_MASK |
DA7213_DMIC_SAMPLEPHASE_MASK |
DA7213_DMIC_CLK_RATE_MASK, dmic_cfg);
}
+ pm_runtime_put_sync(component->dev);
+
/* Check if MCLK provided */
- da7213->mclk = devm_clk_get(codec->dev, "mclk");
- if (IS_ERR(da7213->mclk)) {
- if (PTR_ERR(da7213->mclk) != -ENOENT)
- return PTR_ERR(da7213->mclk);
- else
- da7213->mclk = NULL;
- }
+ da7213->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(da7213->mclk))
+ return PTR_ERR(da7213->mclk);
+ if (da7213->mclk)
+ /* Do automatic PLL handling assuming fixed clock until
+ * set_pll() has been called. This makes the codec usable
+ * with the simple-audio-card driver. */
+ da7213->fixed_clk_auto_pll = true;
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK);
+
+ return 0;
+}
+
+static int da7213_runtime_suspend(struct device *dev)
+{
+ struct da7213_priv *da7213 = dev_get_drvdata(dev);
+
+ regcache_cache_only(da7213->regmap, true);
+ regcache_mark_dirty(da7213->regmap);
+ regulator_bulk_disable(DA7213_NUM_SUPPLIES, da7213->supplies);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da7213 = {
+static int da7213_runtime_resume(struct device *dev)
+{
+ struct da7213_priv *da7213 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(DA7213_NUM_SUPPLIES, da7213->supplies);
+ if (ret < 0)
+ return ret;
+ regcache_cache_only(da7213->regmap, false);
+ return regcache_sync(da7213->regmap);
+}
+
+static int da7213_suspend(struct snd_soc_component *component)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+
+ return da7213_runtime_suspend(da7213->dev);
+}
+
+static int da7213_resume(struct snd_soc_component *component)
+{
+ struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+
+ return da7213_runtime_resume(da7213->dev);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_da7213 = {
.probe = da7213_probe,
.set_bias_level = da7213_set_bias_level,
-
.controls = da7213_snd_controls,
.num_controls = ARRAY_SIZE(da7213_snd_controls),
-
+ .suspend = da7213_suspend,
+ .resume = da7213_resume,
.dapm_widgets = da7213_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets),
.dapm_routes = da7213_audio_map,
.num_dapm_routes = ARRAY_SIZE(da7213_audio_map),
+ .set_sysclk = da7213_set_component_sysclk,
+ .set_pll = da7213_set_component_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config da7213_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
+ .max_register = DA7213_TONE_GEN_OFF_PER,
.reg_defaults = da7213_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults),
.volatile_reg = da7213_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
-static int da7213_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static void da7213_power_off(void *data)
+{
+ struct da7213_priv *da7213 = data;
+ regulator_bulk_disable(DA7213_NUM_SUPPLIES, da7213->supplies);
+}
+
+static const char *da7213_supply_names[DA7213_NUM_SUPPLIES] = {
+ [DA7213_SUPPLY_VDDA] = "VDDA",
+ [DA7213_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7213_i2c_probe(struct i2c_client *i2c)
{
struct da7213_priv *da7213;
- int ret;
+ int i, ret;
- da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
- GFP_KERNEL);
+ da7213 = devm_kzalloc(&i2c->dev, sizeof(*da7213), GFP_KERNEL);
if (!da7213)
return -ENOMEM;
+ da7213->fin_min_rate = (uintptr_t)i2c_get_match_data(i2c);
+ if (!da7213->fin_min_rate)
+ return -EINVAL;
+
+ da7213->dev = &i2c->dev;
+
i2c_set_clientdata(i2c, da7213);
+ /* Get required supplies */
+ for (i = 0; i < DA7213_NUM_SUPPLIES; ++i)
+ da7213->supplies[i].supply = da7213_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, DA7213_NUM_SUPPLIES,
+ da7213->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(DA7213_NUM_SUPPLIES, da7213->supplies);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev, da7213_power_off, da7213);
+ if (ret < 0)
+ return ret;
+
da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
if (IS_ERR(da7213->regmap)) {
ret = PTR_ERR(da7213->regmap);
@@ -1714,23 +2245,33 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7213, &da7213_dai, 1);
+ mutex_init(&da7213->ctrl_lock);
+
+ pm_runtime_set_autosuspend_delay(&i2c->dev, 100);
+ pm_runtime_use_autosuspend(&i2c->dev);
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7213, &da7213_dai, 1);
if (ret < 0) {
- dev_err(&i2c->dev, "Failed to register da7213 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da7213 component: %d\n",
ret);
}
return ret;
}
-static int da7213_remove(struct i2c_client *client)
+static void da7213_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ pm_runtime_disable(&i2c->dev);
}
+static const struct dev_pm_ops da7213_pm = {
+ RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL)
+};
+
static const struct i2c_device_id da7213_i2c_id[] = {
- { "da7213", 0 },
+ { "da7213" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
@@ -1740,9 +2281,11 @@ static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
.of_match_table = of_match_ptr(da7213_of_match),
+ .acpi_match_table = ACPI_PTR(da7213_acpi_match),
+ .pm = pm_ptr(&da7213_pm),
},
.probe = da7213_i2c_probe,
- .remove = da7213_remove,
+ .remove = da7213_i2c_remove,
.id_table = da7213_i2c_id,
};
@@ -1750,4 +2293,5 @@ module_i2c_driver(da7213_i2c_driver);
MODULE_DESCRIPTION("ASoC DA7213 Codec driver");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
index 030fd691b076..29cbf0eb6124 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -1,13 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* da7213.h - DA7213 ASoC Codec Driver
*
* Copyright (c) 2013 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * Author: David Rau <David.Rau.opensource@dm.renesas.com>
*/
#ifndef _DA7213_H
@@ -15,6 +13,7 @@
#include <linux/clk.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/da7213.h>
/*
@@ -137,11 +136,25 @@
#define DA7213_DAC_NG_ON_THRESHOLD 0xB1
#define DA7213_DAC_NG_CTRL 0xB2
+#define DA7213_TONE_GEN_CFG1 0xB4
+#define DA7213_TONE_GEN_CFG2 0xB5
+#define DA7213_TONE_GEN_CYCLES 0xB6
+#define DA7213_TONE_GEN_FREQ1_L 0xB7
+#define DA7213_TONE_GEN_FREQ1_U 0xB8
+#define DA7213_TONE_GEN_FREQ2_L 0xB9
+#define DA7213_TONE_GEN_FREQ2_U 0xBA
+#define DA7213_TONE_GEN_ON_PER 0xBB
+#define DA7213_TONE_GEN_OFF_PER 0xBC
/*
* Bit fields
*/
+#define DA7213_SWITCH_EN_MAX 0x1
+
+/* DA7213_PLL_STATUS = 0x03 */
+#define DA7213_PLL_SRM_LOCK (0x1 << 1)
+
/* DA7213_SR = 0x22 */
#define DA7213_SR_8000 (0x1 << 0)
#define DA7213_SR_11025 (0x2 << 0)
@@ -160,15 +173,16 @@
#define DA7213_VMID_EN (0x1 << 7)
/* DA7213_PLL_CTRL = 0x27 */
-#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2)
-#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2)
-#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2)
-#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2)
+#define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2)
+#define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2)
+#define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2)
+#define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2)
#define DA7213_PLL_INDIV_MASK (0x3 << 2)
#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4)
#define DA7213_PLL_32K_MODE (0x1 << 5)
#define DA7213_PLL_SRM_EN (0x1 << 6)
#define DA7213_PLL_EN (0x1 << 7)
+#define DA7213_PLL_MODE_MASK (0x7 << 5)
/* DA7213_DAI_CLK_MODE = 0x28 */
#define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
@@ -177,21 +191,24 @@
#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7213_DAI_CLK_POL_MASK (0x1 << 2)
#define DA7213_DAI_WCLK_POL_INV (0x1 << 3)
-#define DA7213_DAI_CLK_EN_SLAVE_MODE (0x0 << 7)
-#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7)
+#define DA7213_DAI_WCLK_POL_MASK (0x1 << 3)
#define DA7213_DAI_CLK_EN_MASK (0x1 << 7)
/* DA7213_DAI_CTRL = 0x29 */
#define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0)
#define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0)
#define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7213_DAI_FORMAT_DSP (0x3 << 0)
#define DA7213_DAI_FORMAT_MASK (0x3 << 0)
#define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
#define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
#define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
#define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
#define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7213_DAI_MONO_MODE_EN (0x1 << 4)
+#define DA7213_DAI_MONO_MODE_MASK (0x1 << 4)
#define DA7213_DAI_EN_SHIFT 7
/* DA7213_DIG_ROUTING_DAI = 0x21 */
@@ -412,6 +429,9 @@
#define DA7213_DMIC_CLK_RATE_SHIFT 2
#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2)
+/* DA7213_PC_COUNT = 0x94 */
+#define DA7213_PC_FREERUN_MASK (0x1 << 0)
+
/* DA7213_DIG_CTRL = 0x99 */
#define DA7213_DAC_L_INV_SHIFT 3
#define DA7213_DAC_R_INV_SHIFT 7
@@ -476,6 +496,55 @@
#define DA7213_DAC_NG_EN_SHIFT 7
#define DA7213_DAC_NG_EN_MAX 0x1
+/* DA7213_TONE_GEN_CFG1 = 0xB4 */
+#define DA7213_DTMF_REG_SHIFT 0
+#define DA7213_DTMF_REG_MASK (0xF << 0)
+#define DA7213_DTMF_REG_MAX 16
+#define DA7213_DTMF_EN_SHIFT 4
+#define DA7213_DTMF_EN_MASK (0x1 << 4)
+#define DA7213_START_STOPN_SHIFT 7
+#define DA7213_START_STOPN_MASK (0x1 << 7)
+
+/* DA7213_TONE_GEN_CFG2 = 0xB5 */
+#define DA7213_SWG_SEL_SHIFT 0
+#define DA7213_SWG_SEL_MASK (0x3 << 0)
+#define DA7213_SWG_SEL_MAX 4
+#define DA7213_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7213_TONE_GEN_GAIN_SHIFT 4
+#define DA7213_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7213_TONE_GEN_GAIN_MAX 0xF
+#define DA7213_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7213_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7213_TONE_GEN_CYCLES = 0xB6 */
+#define DA7213_BEEP_CYCLES_SHIFT 0
+#define DA7213_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7213_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7213_FREQ1_L_SHIFT 0
+#define DA7213_FREQ1_L_MASK (0xFF << 0)
+#define DA7213_FREQ_MAX 0xFFFF
+
+/* DA7213_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7213_FREQ1_U_SHIFT 0
+#define DA7213_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7213_FREQ2_L_SHIFT 0
+#define DA7213_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7213_FREQ2_U_SHIFT 0
+#define DA7213_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_ON_PER = 0xBB */
+#define DA7213_BEEP_ON_PER_SHIFT 0
+#define DA7213_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7213_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7213_TONE_GEN_OFF_PER = 0xBC */
+#define DA7213_BEEP_OFF_PER_SHIFT 0
+#define DA7213_BEEP_OFF_PER_MASK (0x3F << 0)
/*
* General defines
@@ -495,33 +564,51 @@
#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
+#define DA7213_PLL_FREQ_OUT_90316800 90316800
+#define DA7213_PLL_FREQ_OUT_98304000 98304000
+#define DA7213_PLL_FREQ_OUT_94310400 94310400
+#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2
+#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4
+#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8
+#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16
+#define DA7213_SRM_CHECK_RETRIES 8
enum da7213_clk_src {
DA7213_CLKSRC_MCLK = 0,
DA7213_CLKSRC_MCLK_SQR,
};
+enum da7213_sys_clk {
+ DA7213_SYSCLK_MCLK = 0,
+ DA7213_SYSCLK_PLL,
+ DA7213_SYSCLK_PLL_SRM,
+ DA7213_SYSCLK_PLL_32KHZ
+};
+
+/* Regulators */
+enum da7213_supplies {
+ DA7213_SUPPLY_VDDA = 0,
+ DA7213_SUPPLY_VDDIO,
+ DA7213_NUM_SUPPLIES,
+};
+
/* Codec private data */
struct da7213_priv {
struct regmap *regmap;
+ struct device *dev;
+ struct mutex ctrl_lock;
+ struct regulator_bulk_data supplies[DA7213_NUM_SUPPLIES];
struct clk *mclk;
unsigned int mclk_rate;
+ unsigned int out_rate;
+ unsigned int fin_min_rate;
int clk_src;
bool master;
- bool mclk_squarer_en;
- bool srm_en;
bool alc_calib_auto;
bool alc_en;
+ bool fixed_clk_auto_pll;
struct da7213_platform_data *pdata;
+ int fmt;
};
#endif /* _DA7213_H */
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 93575f251866..5c80839704c7 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* da7218.c - DA7218 ALSA SoC Codec Driver
*
* Copyright (c) 2015 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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/clk.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm.h>
@@ -291,7 +287,7 @@ static const struct soc_enum da7218_cp_tau_delay =
*/
/* ALC */
-static void da7218_alc_calib(struct snd_soc_codec *codec)
+static void da7218_alc_calib(struct snd_soc_component *component)
{
u8 mic_1_ctrl, mic_2_ctrl;
u8 mixin_1_ctrl, mixin_2_ctrl;
@@ -302,59 +298,59 @@ static void da7218_alc_calib(struct snd_soc_codec *codec)
bool calibrated = false;
/* Save current state of MIC control registers */
- mic_1_ctrl = snd_soc_read(codec, DA7218_MIC_1_CTRL);
- mic_2_ctrl = snd_soc_read(codec, DA7218_MIC_2_CTRL);
+ mic_1_ctrl = snd_soc_component_read(component, DA7218_MIC_1_CTRL);
+ mic_2_ctrl = snd_soc_component_read(component, DA7218_MIC_2_CTRL);
/* Save current state of input mixer control registers */
- mixin_1_ctrl = snd_soc_read(codec, DA7218_MIXIN_1_CTRL);
- mixin_2_ctrl = snd_soc_read(codec, DA7218_MIXIN_2_CTRL);
+ mixin_1_ctrl = snd_soc_component_read(component, DA7218_MIXIN_1_CTRL);
+ mixin_2_ctrl = snd_soc_component_read(component, DA7218_MIXIN_2_CTRL);
/* Save current state of input filter control registers */
- in_1l_filt_ctrl = snd_soc_read(codec, DA7218_IN_1L_FILTER_CTRL);
- in_1r_filt_ctrl = snd_soc_read(codec, DA7218_IN_1R_FILTER_CTRL);
- in_2l_filt_ctrl = snd_soc_read(codec, DA7218_IN_2L_FILTER_CTRL);
- in_2r_filt_ctrl = snd_soc_read(codec, DA7218_IN_2R_FILTER_CTRL);
+ in_1l_filt_ctrl = snd_soc_component_read(component, DA7218_IN_1L_FILTER_CTRL);
+ in_1r_filt_ctrl = snd_soc_component_read(component, DA7218_IN_1R_FILTER_CTRL);
+ in_2l_filt_ctrl = snd_soc_component_read(component, DA7218_IN_2L_FILTER_CTRL);
+ in_2r_filt_ctrl = snd_soc_component_read(component, DA7218_IN_2R_FILTER_CTRL);
/* Save current state of input HPF control registers */
- in_1_hpf_ctrl = snd_soc_read(codec, DA7218_IN_1_HPF_FILTER_CTRL);
- in_2_hpf_ctrl = snd_soc_read(codec, DA7218_IN_2_HPF_FILTER_CTRL);
+ in_1_hpf_ctrl = snd_soc_component_read(component, DA7218_IN_1_HPF_FILTER_CTRL);
+ in_2_hpf_ctrl = snd_soc_component_read(component, DA7218_IN_2_HPF_FILTER_CTRL);
/* Enable then Mute MIC PGAs */
- snd_soc_update_bits(codec, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK,
DA7218_MIC_1_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK,
DA7218_MIC_2_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_MIC_1_CTRL,
+ snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL,
DA7218_MIC_1_AMP_MUTE_EN_MASK,
DA7218_MIC_1_AMP_MUTE_EN_MASK);
- snd_soc_update_bits(codec, DA7218_MIC_2_CTRL,
+ snd_soc_component_update_bits(component, DA7218_MIC_2_CTRL,
DA7218_MIC_2_AMP_MUTE_EN_MASK,
DA7218_MIC_2_AMP_MUTE_EN_MASK);
/* Enable input mixers unmuted */
- snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL,
+ snd_soc_component_update_bits(component, DA7218_MIXIN_1_CTRL,
DA7218_MIXIN_1_AMP_EN_MASK |
DA7218_MIXIN_1_AMP_MUTE_EN_MASK,
DA7218_MIXIN_1_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL,
+ snd_soc_component_update_bits(component, DA7218_MIXIN_2_CTRL,
DA7218_MIXIN_2_AMP_EN_MASK |
DA7218_MIXIN_2_AMP_MUTE_EN_MASK,
DA7218_MIXIN_2_AMP_EN_MASK);
/* Enable input filters unmuted */
- snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_1L_FILTER_CTRL,
DA7218_IN_1L_FILTER_EN_MASK |
DA7218_IN_1L_MUTE_EN_MASK,
DA7218_IN_1L_FILTER_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_1R_FILTER_CTRL,
DA7218_IN_1R_FILTER_EN_MASK |
DA7218_IN_1R_MUTE_EN_MASK,
DA7218_IN_1R_FILTER_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_2L_FILTER_CTRL,
DA7218_IN_2L_FILTER_EN_MASK |
DA7218_IN_2L_MUTE_EN_MASK,
DA7218_IN_2L_FILTER_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_2R_FILTER_CTRL,
DA7218_IN_2R_FILTER_EN_MASK |
DA7218_IN_2R_MUTE_EN_MASK,
DA7218_IN_2R_FILTER_EN_MASK);
@@ -364,16 +360,16 @@ static void da7218_alc_calib(struct snd_soc_codec *codec)
* rates above 32KHz the ADC signals will be stopped and will cause
* calibration to lock up.
*/
- snd_soc_update_bits(codec, DA7218_IN_1_HPF_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_1_HPF_FILTER_CTRL,
DA7218_IN_1_VOICE_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7218_IN_2_HPF_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_2_HPF_FILTER_CTRL,
DA7218_IN_2_VOICE_EN_MASK, 0);
/* Perform auto calibration */
- snd_soc_update_bits(codec, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK,
DA7218_CALIB_AUTO_EN_MASK);
do {
- calib_ctrl = snd_soc_read(codec, DA7218_CALIB_CTRL);
+ calib_ctrl = snd_soc_component_read(component, DA7218_CALIB_CTRL);
if (calib_ctrl & DA7218_CALIB_AUTO_EN_MASK) {
++i;
usleep_range(DA7218_ALC_CALIB_DELAY_MIN,
@@ -386,51 +382,51 @@ static void da7218_alc_calib(struct snd_soc_codec *codec)
/* If auto calibration fails, disable DC offset, hybrid ALC */
if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed - %s\n",
(calibrated) ? "overflow" : "timeout");
- snd_soc_update_bits(codec, DA7218_CALIB_CTRL,
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL,
DA7218_CALIB_OFFSET_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7218_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7218_ALC_CTRL1,
DA7218_ALC_SYNC_MODE_MASK, 0);
} else {
/* Enable DC offset cancellation */
- snd_soc_update_bits(codec, DA7218_CALIB_CTRL,
+ snd_soc_component_update_bits(component, DA7218_CALIB_CTRL,
DA7218_CALIB_OFFSET_EN_MASK,
DA7218_CALIB_OFFSET_EN_MASK);
/* Enable ALC hybrid mode */
- snd_soc_update_bits(codec, DA7218_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7218_ALC_CTRL1,
DA7218_ALC_SYNC_MODE_MASK,
DA7218_ALC_SYNC_MODE_CH1 |
DA7218_ALC_SYNC_MODE_CH2);
}
/* Restore input HPF control registers to original states */
- snd_soc_write(codec, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl);
- snd_soc_write(codec, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl);
+ snd_soc_component_write(component, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl);
+ snd_soc_component_write(component, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl);
/* Restore input filter control registers to original states */
- snd_soc_write(codec, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl);
- snd_soc_write(codec, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl);
- snd_soc_write(codec, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl);
- snd_soc_write(codec, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl);
+ snd_soc_component_write(component, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl);
/* Restore input mixer control registers to original state */
- snd_soc_write(codec, DA7218_MIXIN_1_CTRL, mixin_1_ctrl);
- snd_soc_write(codec, DA7218_MIXIN_2_CTRL, mixin_2_ctrl);
+ snd_soc_component_write(component, DA7218_MIXIN_1_CTRL, mixin_1_ctrl);
+ snd_soc_component_write(component, DA7218_MIXIN_2_CTRL, mixin_2_ctrl);
/* Restore MIC control registers to original states */
- snd_soc_write(codec, DA7218_MIC_1_CTRL, mic_1_ctrl);
- snd_soc_write(codec, DA7218_MIC_2_CTRL, mic_2_ctrl);
+ snd_soc_component_write(component, DA7218_MIC_1_CTRL, mic_1_ctrl);
+ snd_soc_component_write(component, DA7218_MIC_2_CTRL, mic_2_ctrl);
}
static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
int ret;
ret = snd_soc_put_volsw(kcontrol, ucontrol);
@@ -440,7 +436,7 @@ static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol,
* make sure calibrated offsets are updated.
*/
if ((ret == 1) && (da7218->alc_en))
- da7218_alc_calib(codec);
+ da7218_alc_calib(component);
return ret;
}
@@ -450,8 +446,8 @@ static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *) kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
unsigned int lvalue = ucontrol->value.integer.value[0];
unsigned int rvalue = ucontrol->value.integer.value[1];
unsigned int lshift = mc->shift;
@@ -460,7 +456,7 @@ static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
/* Force ALC offset calibration if enabling ALC */
if ((lvalue || rvalue) && (!da7218->alc_en))
- da7218_alc_calib(codec);
+ da7218_alc_calib(component);
/* Update bits to detail which channels are enabled/disabled */
da7218->alc_en &= ~mask;
@@ -473,8 +469,8 @@ static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mixer_ctrl =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int reg = mixer_ctrl->reg;
@@ -497,8 +493,8 @@ static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol,
static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mixer_ctrl =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int reg = mixer_ctrl->reg;
@@ -517,8 +513,8 @@ static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol,
static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mixer_ctrl =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int lvalue = ucontrol->value.integer.value[0];
@@ -537,15 +533,15 @@ static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol,
* power the path (IN_FILTER widget events). This handling avoids
* unwanted level detect events.
*/
- return snd_soc_write(codec, mixer_ctrl->reg,
+ return snd_soc_component_write(component, mixer_ctrl->reg,
(da7218->in_filt_en & da7218->mic_lvl_det_en));
}
static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mixer_ctrl =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int lshift = mixer_ctrl->shift;
@@ -564,8 +560,8 @@ static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol,
static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *) kcontrol->private_value;
@@ -589,8 +585,8 @@ static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol,
static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *) kcontrol->private_value;
u8 reg, out_filt1l;
@@ -617,8 +613,8 @@ static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
}
/* Make sure at least out filter1 enabled to allow programming */
- out_filt1l = snd_soc_read(codec, DA7218_OUT_1L_FILTER_CTRL);
- snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL,
+ out_filt1l = snd_soc_component_read(component, DA7218_OUT_1L_FILTER_CTRL);
+ snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL,
out_filt1l | DA7218_OUT_1L_FILTER_EN_MASK);
for (i = 0; i < bytes_ext->max; ++i) {
@@ -628,7 +624,7 @@ static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
}
/* Restore filter to previous setting */
- snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL, out_filt1l);
+ snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL, out_filt1l);
return 0;
}
@@ -1349,8 +1345,8 @@ static const struct snd_kcontrol_new da7218_st_out_filtr_mix_controls[] = {
static int da7218_in_filter_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
u8 mask;
switch (w->reg) {
@@ -1389,7 +1385,7 @@ static int da7218_in_filter_event(struct snd_soc_dapm_widget *w,
}
/* Enable configured level detection paths */
- snd_soc_write(codec, DA7218_LVL_DET_CTRL,
+ snd_soc_component_write(component, DA7218_LVL_DET_CTRL,
(da7218->in_filt_en & da7218->mic_lvl_det_en));
return 0;
@@ -1398,8 +1394,8 @@ static int da7218_in_filter_event(struct snd_soc_dapm_widget *w,
static int da7218_dai_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
u8 pll_ctrl, pll_status, refosc_cal;
int i;
bool success;
@@ -1408,14 +1404,14 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
if (da7218->master)
/* Enable DAI clks for master mode */
- snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_CLK_EN_MASK,
DA7218_DAI_CLK_EN_MASK);
/* Tune reference oscillator */
- snd_soc_write(codec, DA7218_PLL_REFOSC_CAL,
+ snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL,
DA7218_PLL_REFOSC_CAL_START_MASK);
- snd_soc_write(codec, DA7218_PLL_REFOSC_CAL,
+ snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL,
DA7218_PLL_REFOSC_CAL_START_MASK |
DA7218_PLL_REFOSC_CAL_EN_MASK);
@@ -1423,7 +1419,7 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
i = 0;
success = false;
do {
- refosc_cal = snd_soc_read(codec, DA7218_PLL_REFOSC_CAL);
+ refosc_cal = snd_soc_component_read(component, DA7218_PLL_REFOSC_CAL);
if (!(refosc_cal & DA7218_PLL_REFOSC_CAL_START_MASK)) {
success = true;
} else {
@@ -1434,15 +1430,15 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
} while ((i < DA7218_REF_OSC_CHECK_TRIES) && (!success));
if (!success)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Reference oscillator failed calibration\n");
/* PC synchronised to DAI */
- snd_soc_write(codec, DA7218_PC_COUNT,
+ snd_soc_component_write(component, DA7218_PC_COUNT,
DA7218_PC_RESYNC_AUTO_MASK);
/* If SRM not enabled, we don't need to check status */
- pll_ctrl = snd_soc_read(codec, DA7218_PLL_CTRL);
+ pll_ctrl = snd_soc_component_read(component, DA7218_PLL_CTRL);
if ((pll_ctrl & DA7218_PLL_MODE_MASK) != DA7218_PLL_MODE_SRM)
return 0;
@@ -1450,26 +1446,26 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
i = 0;
success = false;
do {
- pll_status = snd_soc_read(codec, DA7218_PLL_STATUS);
+ pll_status = snd_soc_component_read(component, DA7218_PLL_STATUS);
if (pll_status & DA7218_PLL_SRM_STATUS_SRM_LOCK) {
success = true;
} else {
++i;
msleep(DA7218_SRM_CHECK_DELAY);
}
- } while ((i < DA7218_SRM_CHECK_TRIES) & (!success));
+ } while ((i < DA7218_SRM_CHECK_TRIES) && (!success));
if (!success)
- dev_warn(codec->dev, "SRM failed to lock\n");
+ dev_warn(component->dev, "SRM failed to lock\n");
return 0;
case SND_SOC_DAPM_POST_PMD:
/* PC free-running */
- snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+ snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
if (da7218->master)
/* Disable DAI clks for master mode */
- snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_CLK_EN_MASK, 0);
return 0;
@@ -1481,8 +1477,8 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w,
static int da7218_cp_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
/*
* If this is DA7217 and we're using single supply for differential
@@ -1493,11 +1489,11 @@ static int da7218_cp_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
DA7218_CP_EN_MASK);
return 0;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
0);
return 0;
default:
@@ -1508,17 +1504,17 @@ static int da7218_cp_event(struct snd_soc_dapm_widget *w,
static int da7218_hp_pga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Enable headphone output */
- snd_soc_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK,
DA7218_HP_AMP_OE_MASK);
return 0;
case SND_SOC_DAPM_PRE_PMD:
/* Headphone output high impedance */
- snd_soc_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK, 0);
return 0;
default:
return -EINVAL;
@@ -1634,7 +1630,8 @@ static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = {
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* DAI */
- SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7218_DAI_TDM_CTRL,
+ DA7218_DAI_OE_SHIFT, DA7218_NO_INVERT),
SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
/* Output Mixers */
@@ -1812,14 +1809,14 @@ static const struct snd_soc_dapm_route da7218_audio_map[] = {
static int da7218_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
int ret;
if (da7218->mclk_rate == freq)
return 0;
- if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+ if ((freq < 2000000) || (freq > 54000000)) {
dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
freq);
return -EINVAL;
@@ -1827,12 +1824,12 @@ static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (clk_id) {
case DA7218_CLKSRC_MCLK_SQR:
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
DA7218_PLL_MCLK_SQR_EN_MASK,
DA7218_PLL_MCLK_SQR_EN_MASK);
break;
case DA7218_CLKSRC_MCLK:
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
DA7218_PLL_MCLK_SQR_EN_MASK, 0);
break;
default:
@@ -1858,39 +1855,36 @@ static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int fref, unsigned int fout)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
u8 pll_ctrl, indiv_bits, indiv;
u8 pll_frac_top, pll_frac_bot, pll_integer;
u32 freq_ref;
u64 frac_div;
- /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
- if (da7218->mclk_rate == 32768) {
- indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
- indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
- } else if (da7218->mclk_rate < 2000000) {
- dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ /* Verify 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7218->mclk_rate < 2000000) {
+ dev_err(component->dev, "PLL input clock %d below valid range\n",
da7218->mclk_rate);
return -EINVAL;
- } else if (da7218->mclk_rate <= 5000000) {
- indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
- indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
- } else if (da7218->mclk_rate <= 10000000) {
- indiv_bits = DA7218_PLL_INDIV_5_10_MHZ;
- indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
- } else if (da7218->mclk_rate <= 20000000) {
- indiv_bits = DA7218_PLL_INDIV_10_20_MHZ;
- indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL;
- } else if (da7218->mclk_rate <= 40000000) {
- indiv_bits = DA7218_PLL_INDIV_20_40_MHZ;
- indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 4500000) {
+ indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ;
+ indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 9000000) {
+ indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ;
+ indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 18000000) {
+ indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 36000000) {
+ indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ;
+ indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL;
} else if (da7218->mclk_rate <= 54000000) {
- indiv_bits = DA7218_PLL_INDIV_40_54_MHZ;
- indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL;
+ indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ dev_err(component->dev, "PLL input clock %d above valid range\n",
da7218->mclk_rate);
return -EINVAL;
}
@@ -1901,7 +1895,7 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
switch (source) {
case DA7218_SYSCLK_MCLK:
pll_ctrl |= DA7218_PLL_MODE_BYPASS;
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
DA7218_PLL_INDIV_MASK |
DA7218_PLL_MODE_MASK, pll_ctrl);
return 0;
@@ -1911,11 +1905,8 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
case DA7218_SYSCLK_PLL_SRM:
pll_ctrl |= DA7218_PLL_MODE_SRM;
break;
- case DA7218_SYSCLK_PLL_32KHZ:
- pll_ctrl |= DA7218_PLL_MODE_32KHZ;
- break;
default:
- dev_err(codec->dev, "Invalid PLL config\n");
+ dev_err(component->dev, "Invalid PLL config\n");
return -EINVAL;
}
@@ -1927,10 +1918,10 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_frac_bot = (frac_div) & DA7218_BYTE_MASK;
/* Write PLL config & dividers */
- snd_soc_write(codec, DA7218_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7218_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7218_PLL_INTEGER, pll_integer);
- snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ snd_soc_component_write(component, DA7218_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA7218_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA7218_PLL_INTEGER, pll_integer);
+ snd_soc_component_update_bits(component, DA7218_PLL_CTRL,
DA7218_PLL_MODE_MASK | DA7218_PLL_INDIV_MASK,
pll_ctrl);
@@ -1939,15 +1930,15 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
u8 dai_clk_mode = 0, dai_ctrl = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7218->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7218->master = false;
break;
default:
@@ -2017,8 +2008,8 @@ static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default 64 BCLKs per WCLK is supported */
dai_clk_mode |= DA7218_DAI_BCLKS_PER_WCLK_64;
- snd_soc_write(codec, DA7218_DAI_CLK_MODE, dai_clk_mode);
- snd_soc_update_bits(codec, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK,
+ snd_soc_component_write(component, DA7218_DAI_CLK_MODE, dai_clk_mode);
+ snd_soc_component_update_bits(component, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK,
dai_ctrl);
return 0;
@@ -2028,16 +2019,16 @@ static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 dai_bclks_per_wclk;
u32 frame_size;
/* No channels enabled so disable TDM, revert to 64-bit frames */
if (!tx_mask) {
- snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DAI_TDM_CTRL,
DA7218_DAI_TDM_CH_EN_MASK |
DA7218_DAI_TDM_MODE_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_BCLKS_PER_WCLK_MASK,
DA7218_DAI_BCLKS_PER_WCLK_64);
return 0;
@@ -2045,14 +2036,14 @@ static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai,
/* Check we have valid slots */
if (fls(tx_mask) > DA7218_DAI_TDM_MAX_SLOTS) {
- dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ dev_err(component->dev, "Invalid number of slots, max = %d\n",
DA7218_DAI_TDM_MAX_SLOTS);
return -EINVAL;
}
/* Check we have a valid offset given (first 2 bytes of rx_mask) */
if (rx_mask >> DA7218_2BYTE_SHIFT) {
- dev_err(codec->dev, "Invalid slot offset, max = %d\n",
+ dev_err(component->dev, "Invalid slot offset, max = %d\n",
DA7218_2BYTE_MASK);
return -EINVAL;
}
@@ -2073,18 +2064,18 @@ static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai,
dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_256;
break;
default:
- dev_err(codec->dev, "Invalid frame size\n");
+ dev_err(component->dev, "Invalid frame size\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE,
DA7218_DAI_BCLKS_PER_WCLK_MASK,
dai_bclks_per_wclk);
- snd_soc_write(codec, DA7218_DAI_OFFSET_LOWER,
+ snd_soc_component_write(component, DA7218_DAI_OFFSET_LOWER,
(rx_mask & DA7218_BYTE_MASK));
- snd_soc_write(codec, DA7218_DAI_OFFSET_UPPER,
+ snd_soc_component_write(component, DA7218_DAI_OFFSET_UPPER,
((rx_mask >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK));
- snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DAI_TDM_CTRL,
DA7218_DAI_TDM_CH_EN_MASK |
DA7218_DAI_TDM_MODE_EN_MASK,
(tx_mask << DA7218_DAI_TDM_CH_EN_SHIFT) |
@@ -2097,7 +2088,7 @@ static int da7218_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 dai_ctrl = 0, fs;
unsigned int channels;
@@ -2120,7 +2111,7 @@ static int da7218_hw_params(struct snd_pcm_substream *substream,
channels = params_channels(params);
if ((channels < 1) || (channels > DA7218_DAI_CH_NUM_MAX)) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Invalid number of channels, only 1 to %d supported\n",
DA7218_DAI_CH_NUM_MAX);
return -EINVAL;
@@ -2165,11 +2156,11 @@ static int da7218_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7218_DAI_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DAI_CTRL,
DA7218_DAI_WORD_LENGTH_MASK | DA7218_DAI_CH_NUM_MASK,
dai_ctrl);
/* SRs tied for ADCs and DACs. */
- snd_soc_write(codec, DA7218_SR,
+ snd_soc_component_write(component, DA7218_SR,
(fs << DA7218_SR_DAC_SHIFT) | (fs << DA7218_SR_ADC_SHIFT));
return 0;
@@ -2203,9 +2194,9 @@ static struct snd_soc_dai_driver da7218_dai = {
.formats = DA7218_FORMATS,
},
.ops = &da7218_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.symmetric_channels = 1,
- .symmetric_samplebits = 1,
+ .symmetric_sample_bits = 1,
};
@@ -2213,15 +2204,15 @@ static struct snd_soc_dai_driver da7218_dai = {
* HP Detect
*/
-int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
if (da7218->dev_id == DA7217_DEV_ID)
return -EINVAL;
da7218->jack = jack;
- snd_soc_update_bits(codec, DA7218_HPLDET_JACK,
+ snd_soc_component_update_bits(component, DA7218_HPLDET_JACK,
DA7218_HPLDET_JACK_EN_MASK,
jack ? DA7218_HPLDET_JACK_EN_MASK : 0);
@@ -2229,23 +2220,23 @@ int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
}
EXPORT_SYMBOL_GPL(da7218_hpldet);
-static void da7218_micldet_irq(struct snd_soc_codec *codec)
+static void da7218_micldet_irq(struct snd_soc_component *component)
{
char *envp[] = {
"EVENT=MIC_LEVEL_DETECT",
NULL,
};
- kobject_uevent_env(&codec->dev->kobj, KOBJ_CHANGE, envp);
+ kobject_uevent_env(&component->dev->kobj, KOBJ_CHANGE, envp);
}
-static void da7218_hpldet_irq(struct snd_soc_codec *codec)
+static void da7218_hpldet_irq(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
u8 jack_status;
int report;
- jack_status = snd_soc_read(codec, DA7218_EVENT_STATUS);
+ jack_status = snd_soc_component_read(component, DA7218_EVENT_STATUS);
if (jack_status & DA7218_HPLDET_JACK_STS_MASK)
report = SND_JACK_HEADPHONE;
@@ -2261,24 +2252,24 @@ static void da7218_hpldet_irq(struct snd_soc_codec *codec)
static irqreturn_t da7218_irq_thread(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
+ struct snd_soc_component *component = data;
u8 status;
/* Read IRQ status reg */
- status = snd_soc_read(codec, DA7218_EVENT);
+ status = snd_soc_component_read(component, DA7218_EVENT);
if (!status)
return IRQ_NONE;
/* Mic level detect */
if (status & DA7218_LVL_DET_EVENT_MASK)
- da7218_micldet_irq(codec);
+ da7218_micldet_irq(component);
/* HP detect */
if (status & DA7218_HPLDET_JACK_EVENT_MASK)
- da7218_hpldet_irq(codec);
+ da7218_hpldet_irq(component);
/* Clear interrupts */
- snd_soc_write(codec, DA7218_EVENT, status);
+ snd_soc_component_write(component, DA7218_EVENT, status);
return IRQ_HANDLED;
}
@@ -2294,18 +2285,8 @@ static const struct of_device_id da7218_of_match[] = {
};
MODULE_DEVICE_TABLE(of, da7218_of_match);
-static inline int da7218_of_get_id(struct device *dev)
-{
- const struct of_device_id *id = of_match_device(da7218_of_match, dev);
-
- if (id)
- return (uintptr_t)id->data;
- else
- return -EINVAL;
-}
-
static enum da7218_micbias_voltage
- da7218_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+ da7218_of_micbias_lvl(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1200:
@@ -2327,13 +2308,13 @@ static enum da7218_micbias_voltage
case 3000:
return DA7218_MICBIAS_3_0V;
default:
- dev_warn(codec->dev, "Invalid micbias level");
+ dev_warn(component->dev, "Invalid micbias level");
return DA7218_MICBIAS_1_6V;
}
}
static enum da7218_mic_amp_in_sel
- da7218_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+ da7218_of_mic_amp_in_sel(struct snd_soc_component *component, const char *str)
{
if (!strcmp(str, "diff")) {
return DA7218_MIC_AMP_IN_SEL_DIFF;
@@ -2342,39 +2323,39 @@ static enum da7218_mic_amp_in_sel
} else if (!strcmp(str, "se_n")) {
return DA7218_MIC_AMP_IN_SEL_SE_N;
} else {
- dev_warn(codec->dev, "Invalid mic input type selection");
+ dev_warn(component->dev, "Invalid mic input type selection");
return DA7218_MIC_AMP_IN_SEL_DIFF;
}
}
static enum da7218_dmic_data_sel
- da7218_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+ da7218_of_dmic_data_sel(struct snd_soc_component *component, const char *str)
{
if (!strcmp(str, "lrise_rfall")) {
return DA7218_DMIC_DATA_LRISE_RFALL;
} else if (!strcmp(str, "lfall_rrise")) {
return DA7218_DMIC_DATA_LFALL_RRISE;
} else {
- dev_warn(codec->dev, "Invalid DMIC data type selection");
+ dev_warn(component->dev, "Invalid DMIC data type selection");
return DA7218_DMIC_DATA_LRISE_RFALL;
}
}
static enum da7218_dmic_samplephase
- da7218_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+ da7218_of_dmic_samplephase(struct snd_soc_component *component, const char *str)
{
if (!strcmp(str, "on_clkedge")) {
return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
} else if (!strcmp(str, "between_clkedge")) {
return DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE;
} else {
- dev_warn(codec->dev, "Invalid DMIC sample phase");
+ dev_warn(component->dev, "Invalid DMIC sample phase");
return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
}
}
static enum da7218_dmic_clk_rate
- da7218_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+ da7218_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 1500000:
@@ -2382,13 +2363,13 @@ static enum da7218_dmic_clk_rate
case 3000000:
return DA7218_DMIC_CLK_3_0MHZ;
default:
- dev_warn(codec->dev, "Invalid DMIC clock rate");
+ dev_warn(component->dev, "Invalid DMIC clock rate");
return DA7218_DMIC_CLK_3_0MHZ;
}
}
static enum da7218_hpldet_jack_rate
- da7218_of_jack_rate(struct snd_soc_codec *codec, u32 val)
+ da7218_of_jack_rate(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 5:
@@ -2408,13 +2389,13 @@ static enum da7218_hpldet_jack_rate
case 640:
return DA7218_HPLDET_JACK_RATE_640US;
default:
- dev_warn(codec->dev, "Invalid jack detect rate");
+ dev_warn(component->dev, "Invalid jack detect rate");
return DA7218_HPLDET_JACK_RATE_40US;
}
}
static enum da7218_hpldet_jack_debounce
- da7218_of_jack_debounce(struct snd_soc_codec *codec, u32 val)
+ da7218_of_jack_debounce(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 0:
@@ -2426,13 +2407,13 @@ static enum da7218_hpldet_jack_debounce
case 4:
return DA7218_HPLDET_JACK_DEBOUNCE_4;
default:
- dev_warn(codec->dev, "Invalid jack debounce");
+ dev_warn(component->dev, "Invalid jack debounce");
return DA7218_HPLDET_JACK_DEBOUNCE_2;
}
}
static enum da7218_hpldet_jack_thr
- da7218_of_jack_thr(struct snd_soc_codec *codec, u32 val)
+ da7218_of_jack_thr(struct snd_soc_component *component, u32 val)
{
switch (val) {
case 84:
@@ -2444,78 +2425,76 @@ static enum da7218_hpldet_jack_thr
case 96:
return DA7218_HPLDET_JACK_THR_96PCT;
default:
- dev_warn(codec->dev, "Invalid jack threshold level");
+ dev_warn(component->dev, "Invalid jack threshold level");
return DA7218_HPLDET_JACK_THR_84PCT;
}
}
-static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
- struct device_node *np = codec->dev->of_node;
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct device_node *np = component->dev->of_node;
struct device_node *hpldet_np;
struct da7218_pdata *pdata;
struct da7218_hpldet_pdata *hpldet_pdata;
const char *of_str;
u32 of_val32;
- pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_warn(codec->dev, "Failed to allocate memory for pdata\n");
+ pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
return NULL;
- }
if (of_property_read_u32(np, "dlg,micbias1-lvl-millivolt", &of_val32) >= 0)
- pdata->micbias1_lvl = da7218_of_micbias_lvl(codec, of_val32);
+ pdata->micbias1_lvl = da7218_of_micbias_lvl(component, of_val32);
else
pdata->micbias1_lvl = DA7218_MICBIAS_1_6V;
if (of_property_read_u32(np, "dlg,micbias2-lvl-millivolt", &of_val32) >= 0)
- pdata->micbias2_lvl = da7218_of_micbias_lvl(codec, of_val32);
+ pdata->micbias2_lvl = da7218_of_micbias_lvl(component, of_val32);
else
pdata->micbias2_lvl = DA7218_MICBIAS_1_6V;
if (!of_property_read_string(np, "dlg,mic1-amp-in-sel", &of_str))
pdata->mic1_amp_in_sel =
- da7218_of_mic_amp_in_sel(codec, of_str);
+ da7218_of_mic_amp_in_sel(component, of_str);
else
pdata->mic1_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
if (!of_property_read_string(np, "dlg,mic2-amp-in-sel", &of_str))
pdata->mic2_amp_in_sel =
- da7218_of_mic_amp_in_sel(codec, of_str);
+ da7218_of_mic_amp_in_sel(component, of_str);
else
pdata->mic2_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
if (!of_property_read_string(np, "dlg,dmic1-data-sel", &of_str))
- pdata->dmic1_data_sel = da7218_of_dmic_data_sel(codec, of_str);
+ pdata->dmic1_data_sel = da7218_of_dmic_data_sel(component, of_str);
else
pdata->dmic1_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
if (!of_property_read_string(np, "dlg,dmic1-samplephase", &of_str))
pdata->dmic1_samplephase =
- da7218_of_dmic_samplephase(codec, of_str);
+ da7218_of_dmic_samplephase(component, of_str);
else
pdata->dmic1_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
if (of_property_read_u32(np, "dlg,dmic1-clkrate-hz", &of_val32) >= 0)
- pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(codec, of_val32);
+ pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
else
pdata->dmic1_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
if (!of_property_read_string(np, "dlg,dmic2-data-sel", &of_str))
- pdata->dmic2_data_sel = da7218_of_dmic_data_sel(codec, of_str);
+ pdata->dmic2_data_sel = da7218_of_dmic_data_sel(component, of_str);
else
pdata->dmic2_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
if (!of_property_read_string(np, "dlg,dmic2-samplephase", &of_str))
pdata->dmic2_samplephase =
- da7218_of_dmic_samplephase(codec, of_str);
+ da7218_of_dmic_samplephase(component, of_str);
else
pdata->dmic2_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
if (of_property_read_u32(np, "dlg,dmic2-clkrate-hz", &of_val32) >= 0)
- pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(codec, of_val32);
+ pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
else
pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
@@ -2525,15 +2504,13 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
}
if (da7218->dev_id == DA7218_DEV_ID) {
- hpldet_np = of_find_node_by_name(np, "da7218_hpldet");
+ hpldet_np = of_get_child_by_name(np, "da7218_hpldet");
if (!hpldet_np)
return pdata;
- hpldet_pdata = devm_kzalloc(codec->dev, sizeof(*hpldet_pdata),
+ hpldet_pdata = devm_kzalloc(component->dev, sizeof(*hpldet_pdata),
GFP_KERNEL);
if (!hpldet_pdata) {
- dev_warn(codec->dev,
- "Failed to allocate memory for hpldet pdata\n");
of_node_put(hpldet_np);
return pdata;
}
@@ -2542,14 +2519,14 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us",
&of_val32) >= 0)
hpldet_pdata->jack_rate =
- da7218_of_jack_rate(codec, of_val32);
+ da7218_of_jack_rate(component, of_val32);
else
hpldet_pdata->jack_rate = DA7218_HPLDET_JACK_RATE_40US;
if (of_property_read_u32(hpldet_np, "dlg,jack-debounce",
&of_val32) >= 0)
hpldet_pdata->jack_debounce =
- da7218_of_jack_debounce(codec, of_val32);
+ da7218_of_jack_debounce(component, of_val32);
else
hpldet_pdata->jack_debounce =
DA7218_HPLDET_JACK_DEBOUNCE_2;
@@ -2557,7 +2534,7 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct",
&of_val32) >= 0)
hpldet_pdata->jack_thr =
- da7218_of_jack_thr(codec, of_val32);
+ da7218_of_jack_thr(component, of_val32);
else
hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT;
@@ -2581,54 +2558,57 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
* Codec driver functions
*/
-static int da7218_set_bias_level(struct snd_soc_codec *codec,
+static int da7218_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
break;
- case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- /* MCLK */
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
if (da7218->mclk) {
ret = clk_prepare_enable(da7218->mclk);
if (ret) {
- dev_err(codec->dev,
- "Failed to enable mclk\n");
+ dev_err(component->dev, "Failed to enable mclk\n");
return ret;
}
}
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Master bias */
- snd_soc_update_bits(codec, DA7218_REFERENCES,
+ snd_soc_component_update_bits(component, DA7218_REFERENCES,
DA7218_BIAS_EN_MASK,
DA7218_BIAS_EN_MASK);
/* Internal LDO */
- snd_soc_update_bits(codec, DA7218_LDO_CTRL,
+ snd_soc_component_update_bits(component, DA7218_LDO_CTRL,
DA7218_LDO_EN_MASK,
DA7218_LDO_EN_MASK);
+ } else {
+ /* Remove MCLK */
+ if (da7218->mclk)
+ clk_disable_unprepare(da7218->mclk);
}
break;
case SND_SOC_BIAS_OFF:
/* Only disable if jack detection disabled */
if (!da7218->jack) {
/* Internal LDO */
- snd_soc_update_bits(codec, DA7218_LDO_CTRL,
+ snd_soc_component_update_bits(component, DA7218_LDO_CTRL,
DA7218_LDO_EN_MASK, 0);
/* Master bias */
- snd_soc_update_bits(codec, DA7218_REFERENCES,
+ snd_soc_component_update_bits(component, DA7218_REFERENCES,
DA7218_BIAS_EN_MASK, 0);
}
-
- /* MCLK */
- if (da7218->mclk)
- clk_disable_unprepare(da7218->mclk);
break;
}
@@ -2641,9 +2621,9 @@ static const char *da7218_supply_names[DA7218_NUM_SUPPLIES] = {
[DA7218_SUPPLY_VDDIO] = "VDDIO",
};
-static int da7218_handle_supplies(struct snd_soc_codec *codec)
+static int da7218_handle_supplies(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct regulator *vddio;
u8 io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V;
int i, ret;
@@ -2652,10 +2632,10 @@ static int da7218_handle_supplies(struct snd_soc_codec *codec)
for (i = 0; i < DA7218_NUM_SUPPLIES; ++i)
da7218->supplies[i].supply = da7218_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, DA7218_NUM_SUPPLIES,
+ ret = devm_regulator_bulk_get(component->dev, DA7218_NUM_SUPPLIES,
da7218->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to get supplies\n");
+ dev_err(component->dev, "Failed to get supplies\n");
return ret;
}
@@ -2663,29 +2643,29 @@ static int da7218_handle_supplies(struct snd_soc_codec *codec)
vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer;
ret = regulator_get_voltage(vddio);
if (ret < 1500000)
- dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ dev_warn(component->dev, "Invalid VDDIO voltage\n");
else if (ret < 2500000)
io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V;
/* Enable main supplies */
ret = regulator_bulk_enable(DA7218_NUM_SUPPLIES, da7218->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to enable supplies\n");
+ dev_err(component->dev, "Failed to enable supplies\n");
return ret;
}
/* Ensure device in active mode */
- snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK);
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK);
/* Update IO voltage level range */
- snd_soc_write(codec, DA7218_IO_CTRL, io_voltage_lvl);
+ snd_soc_component_write(component, DA7218_IO_CTRL, io_voltage_lvl);
return 0;
}
-static void da7218_handle_pdata(struct snd_soc_codec *codec)
+static void da7218_handle_pdata(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct da7218_pdata *pdata = da7218->pdata;
if (pdata) {
@@ -2726,14 +2706,14 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
break;
}
- snd_soc_write(codec, DA7218_MICBIAS_CTRL, micbias_lvl);
+ snd_soc_component_write(component, DA7218_MICBIAS_CTRL, micbias_lvl);
/* Mic */
switch (pdata->mic1_amp_in_sel) {
case DA7218_MIC_AMP_IN_SEL_DIFF:
case DA7218_MIC_AMP_IN_SEL_SE_P:
case DA7218_MIC_AMP_IN_SEL_SE_N:
- snd_soc_write(codec, DA7218_MIC_1_SELECT,
+ snd_soc_component_write(component, DA7218_MIC_1_SELECT,
pdata->mic1_amp_in_sel);
break;
}
@@ -2742,7 +2722,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
case DA7218_MIC_AMP_IN_SEL_DIFF:
case DA7218_MIC_AMP_IN_SEL_SE_P:
case DA7218_MIC_AMP_IN_SEL_SE_N:
- snd_soc_write(codec, DA7218_MIC_2_SELECT,
+ snd_soc_component_write(component, DA7218_MIC_2_SELECT,
pdata->mic2_amp_in_sel);
break;
}
@@ -2772,7 +2752,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, DA7218_DMIC_1_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DMIC_1_CTRL,
DA7218_DMIC_1_DATA_SEL_MASK |
DA7218_DMIC_1_SAMPLEPHASE_MASK |
DA7218_DMIC_1_CLK_RATE_MASK, dmic_cfg);
@@ -2802,7 +2782,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, DA7218_DMIC_2_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DMIC_2_CTRL,
DA7218_DMIC_2_DATA_SEL_MASK |
DA7218_DMIC_2_SAMPLEPHASE_MASK |
DA7218_DMIC_2_CLK_RATE_MASK, dmic_cfg);
@@ -2813,9 +2793,9 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
pdata->hp_diff_single_supply;
if (da7218->hp_single_supply) {
- snd_soc_write(codec, DA7218_HP_DIFF_UNLOCK,
+ snd_soc_component_write(component, DA7218_HP_DIFF_UNLOCK,
DA7218_HP_DIFF_UNLOCK_VAL);
- snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL,
+ snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL,
DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK,
DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK);
}
@@ -2864,7 +2844,7 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
DA7218_HPLDET_JACK_THR_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7218_HPLDET_JACK,
+ snd_soc_component_update_bits(component, DA7218_HPLDET_JACK,
DA7218_HPLDET_JACK_RATE_MASK |
DA7218_HPLDET_JACK_DEBOUNCE_MASK |
DA7218_HPLDET_JACK_THR_MASK,
@@ -2880,109 +2860,105 @@ static void da7218_handle_pdata(struct snd_soc_codec *codec)
if (hpldet_pdata->discharge)
hpldet_cfg |= DA7218_HPLDET_DISCHARGE_EN_MASK;
- snd_soc_write(codec, DA7218_HPLDET_CTRL, hpldet_cfg);
+ snd_soc_component_write(component, DA7218_HPLDET_CTRL, hpldet_cfg);
}
}
}
-static int da7218_probe(struct snd_soc_codec *codec)
+static int da7218_probe(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
int ret;
/* Regulator configuration */
- ret = da7218_handle_supplies(codec);
+ ret = da7218_handle_supplies(component);
if (ret)
return ret;
/* Handle DT/Platform data */
- if (codec->dev->of_node)
- da7218->pdata = da7218_of_to_pdata(codec);
+ if (component->dev->of_node)
+ da7218->pdata = da7218_of_to_pdata(component);
else
- da7218->pdata = dev_get_platdata(codec->dev);
+ da7218->pdata = dev_get_platdata(component->dev);
- da7218_handle_pdata(codec);
+ da7218_handle_pdata(component);
/* Check if MCLK provided, if not the clock is NULL */
- da7218->mclk = devm_clk_get(codec->dev, "mclk");
+ da7218->mclk = devm_clk_get_optional(component->dev, "mclk");
if (IS_ERR(da7218->mclk)) {
- if (PTR_ERR(da7218->mclk) != -ENOENT) {
- ret = PTR_ERR(da7218->mclk);
- goto err_disable_reg;
- } else {
- da7218->mclk = NULL;
- }
+ ret = PTR_ERR(da7218->mclk);
+ goto err_disable_reg;
}
/* Default PC to free-running */
- snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+ snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
/*
* Default Output Filter mixers to off otherwise DAPM will power
* Mic to HP passthrough paths by default at startup.
*/
- snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1L, 0);
- snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1R, 0);
+ snd_soc_component_write(component, DA7218_DROUTING_OUTFILT_1L, 0);
+ snd_soc_component_write(component, DA7218_DROUTING_OUTFILT_1R, 0);
/* Default CP to normal load, power mode */
- snd_soc_update_bits(codec, DA7218_CP_CTRL,
+ snd_soc_component_update_bits(component, DA7218_CP_CTRL,
DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK, 0);
/* Default gain ramping */
- snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL,
+ snd_soc_component_update_bits(component, DA7218_MIXIN_1_CTRL,
DA7218_MIXIN_1_AMP_RAMP_EN_MASK,
DA7218_MIXIN_1_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL,
+ snd_soc_component_update_bits(component, DA7218_MIXIN_2_CTRL,
DA7218_MIXIN_2_AMP_RAMP_EN_MASK,
DA7218_MIXIN_2_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_1L_FILTER_CTRL,
DA7218_IN_1L_RAMP_EN_MASK,
DA7218_IN_1L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_1R_FILTER_CTRL,
DA7218_IN_1R_RAMP_EN_MASK,
DA7218_IN_1R_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_2L_FILTER_CTRL,
DA7218_IN_2L_RAMP_EN_MASK,
DA7218_IN_2L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_IN_2R_FILTER_CTRL,
DA7218_IN_2R_RAMP_EN_MASK,
DA7218_IN_2R_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_DGS_GAIN_CTRL,
+ snd_soc_component_update_bits(component, DA7218_DGS_GAIN_CTRL,
DA7218_DGS_RAMP_EN_MASK, DA7218_DGS_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_OUT_1L_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_OUT_1L_FILTER_CTRL,
DA7218_OUT_1L_RAMP_EN_MASK,
DA7218_OUT_1L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_OUT_1R_FILTER_CTRL,
+ snd_soc_component_update_bits(component, DA7218_OUT_1R_FILTER_CTRL,
DA7218_OUT_1R_RAMP_EN_MASK,
DA7218_OUT_1R_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7218_HP_L_CTRL,
DA7218_HP_L_AMP_RAMP_EN_MASK,
DA7218_HP_L_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7218_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7218_HP_R_CTRL,
DA7218_HP_R_AMP_RAMP_EN_MASK,
DA7218_HP_R_AMP_RAMP_EN_MASK);
/* Default infinite tone gen, start/stop by Kcontrol */
- snd_soc_write(codec, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK);
+ snd_soc_component_write(component, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK);
/* DA7217 specific config */
if (da7218->dev_id == DA7217_DEV_ID) {
- snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL,
+ snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL,
DA7218_HP_AMP_DIFF_MODE_EN_MASK,
DA7218_HP_AMP_DIFF_MODE_EN_MASK);
/* Only DA7218 supports HP detect, mask off for DA7217 */
- snd_soc_write(codec, DA7218_EVENT_MASK,
+ snd_soc_component_write(component, DA7218_EVENT_MASK,
DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK);
}
if (da7218->irq) {
- ret = devm_request_threaded_irq(codec->dev, da7218->irq, NULL,
+ ret = devm_request_threaded_irq(component->dev, da7218->irq, NULL,
da7218_irq_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "da7218", codec);
+ "da7218", component);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
+ dev_err(component->dev, "Failed to request IRQ %d: %d\n",
da7218->irq, ret);
goto err_disable_reg;
}
@@ -2997,39 +2973,37 @@ err_disable_reg:
return ret;
}
-static int da7218_remove(struct snd_soc_codec *codec)
+static void da7218_remove(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
-
- return 0;
}
#ifdef CONFIG_PM
-static int da7218_suspend(struct snd_soc_codec *codec)
+static int da7218_suspend(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
- da7218_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ da7218_set_bias_level(component, SND_SOC_BIAS_OFF);
/* Put device into standby mode if jack detection disabled */
if (!da7218->jack)
- snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, 0);
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, 0);
return 0;
}
-static int da7218_resume(struct snd_soc_codec *codec)
+static int da7218_resume(struct snd_soc_component *component)
{
- struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
/* Put device into active mode if previously moved to standby */
if (!da7218->jack)
- snd_soc_write(codec, DA7218_SYSTEM_ACTIVE,
+ snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE,
DA7218_SYSTEM_ACTIVE_MASK);
- da7218_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ da7218_set_bias_level(component, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -3038,20 +3012,21 @@ static int da7218_resume(struct snd_soc_codec *codec)
#define da7218_resume NULL
#endif
-static struct snd_soc_codec_driver soc_codec_dev_da7218 = {
+static const struct snd_soc_component_driver soc_component_dev_da7218 = {
.probe = da7218_probe,
.remove = da7218_remove,
.suspend = da7218_suspend,
.resume = da7218_resume,
.set_bias_level = da7218_set_bias_level,
-
.controls = da7218_snd_controls,
.num_controls = ARRAY_SIZE(da7218_snd_controls),
-
.dapm_widgets = da7218_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets),
.dapm_routes = da7218_audio_map,
.num_dapm_routes = ARRAY_SIZE(da7218_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
@@ -3059,7 +3034,7 @@ static struct snd_soc_codec_driver soc_codec_dev_da7218 = {
* Regmap configs
*/
-static struct reg_default da7218_reg_defaults[] = {
+static const struct reg_default da7218_reg_defaults[] = {
{ DA7218_SYSTEM_ACTIVE, 0x00 },
{ DA7218_CIF_CTRL, 0x00 },
{ DA7218_SPARE1, 0x00 },
@@ -3269,23 +3244,18 @@ static const struct regmap_config da7218_regmap_config = {
* I2C layer
*/
-static int da7218_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7218_i2c_probe(struct i2c_client *i2c)
{
struct da7218_priv *da7218;
int ret;
- da7218 = devm_kzalloc(&i2c->dev, sizeof(struct da7218_priv),
- GFP_KERNEL);
+ da7218 = devm_kzalloc(&i2c->dev, sizeof(*da7218), GFP_KERNEL);
if (!da7218)
return -ENOMEM;
i2c_set_clientdata(i2c, da7218);
- if (i2c->dev.of_node)
- da7218->dev_id = da7218_of_get_id(&i2c->dev);
- else
- da7218->dev_id = id->driver_data;
+ da7218->dev_id = (uintptr_t)i2c_get_match_data(i2c);
if ((da7218->dev_id != DA7217_DEV_ID) &&
(da7218->dev_id != DA7218_DEV_ID)) {
@@ -3302,21 +3272,15 @@ static int da7218_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7218, &da7218_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da7218, &da7218_dai, 1);
if (ret < 0) {
- dev_err(&i2c->dev, "Failed to register da7218 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da7218 component: %d\n",
ret);
}
return ret;
}
-static int da7218_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id da7218_i2c_id[] = {
{ "da7217", DA7217_DEV_ID },
{ "da7218", DA7218_DEV_ID },
@@ -3327,10 +3291,9 @@ MODULE_DEVICE_TABLE(i2c, da7218_i2c_id);
static struct i2c_driver da7218_i2c_driver = {
.driver = {
.name = "da7218",
- .of_match_table = of_match_ptr(da7218_of_match),
+ .of_match_table = da7218_of_match,
},
.probe = da7218_i2c_probe,
- .remove = da7218_i2c_remove,
.id_table = da7218_i2c_id,
};
diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h
index c2c59049a2ad..7f6a4aea2c7a 100644
--- a/sound/soc/codecs/da7218.h
+++ b/sound/soc/codecs/da7218.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* da7218.h - DA7218 ALSA SoC Codec Driver
*
* Copyright (c) 2015 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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 _DA7218_H
@@ -876,15 +872,11 @@
/* DA7218_PLL_CTRL = 0x91 */
#define DA7218_PLL_INDIV_SHIFT 0
#define DA7218_PLL_INDIV_MASK (0x7 << 0)
-#define DA7218_PLL_INDIV_2_5_MHZ (0x0 << 0)
-#define DA7218_PLL_INDIV_5_10_MHZ (0x1 << 0)
-#define DA7218_PLL_INDIV_10_20_MHZ (0x2 << 0)
-#define DA7218_PLL_INDIV_20_40_MHZ (0x3 << 0)
-#define DA7218_PLL_INDIV_40_54_MHZ (0x4 << 0)
-#define DA7218_PLL_INDIV_2_10_MHZ_VAL 2
-#define DA7218_PLL_INDIV_10_20_MHZ_VAL 4
-#define DA7218_PLL_INDIV_20_40_MHZ_VAL 8
-#define DA7218_PLL_INDIV_40_54_MHZ_VAL 16
+#define DA7218_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 0)
+#define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0)
+#define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0)
+#define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0)
+#define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0)
#define DA7218_PLL_MCLK_SQR_EN_SHIFT 4
#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4)
#define DA7218_PLL_MODE_SHIFT 6
@@ -892,7 +884,6 @@
#define DA7218_PLL_MODE_BYPASS (0x0 << 6)
#define DA7218_PLL_MODE_NORMAL (0x1 << 6)
#define DA7218_PLL_MODE_SRM (0x2 << 6)
-#define DA7218_PLL_MODE_32KHZ (0x3 << 6)
/* DA7218_PLL_FRAC_TOP = 0x92 */
#define DA7218_PLL_FBDIV_FRAC_TOP_SHIFT 0
@@ -1336,6 +1327,13 @@
#define DA7218_PLL_FREQ_OUT_90316 90316800
#define DA7218_PLL_FREQ_OUT_98304 98304000
+/* PLL Frequency Dividers */
+#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
+#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
+#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL 4
+#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL 8
+#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL 16
+
/* ALC Calibration */
#define DA7218_ALC_CALIB_DELAY_MIN 2500
#define DA7218_ALC_CALIB_DELAY_MAX 5000
@@ -1368,11 +1366,10 @@ enum da7218_sys_clk {
DA7218_SYSCLK_MCLK = 0,
DA7218_SYSCLK_PLL,
DA7218_SYSCLK_PLL_SRM,
- DA7218_SYSCLK_PLL_32KHZ
};
enum da7218_dev_id {
- DA7217_DEV_ID = 0,
+ DA7217_DEV_ID = 1,
DA7218_DEV_ID,
};
@@ -1409,6 +1406,6 @@ struct da7218_priv {
};
/* HP detect control */
-int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack);
#endif /* _DA7218_H */
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 9459593eef13..d9d932a78b71 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -1,20 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
*
* Copyright (c) 2015 Dialog Semiconductor Ltd.
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
#include <linux/pm_wakeirq.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -31,9 +28,9 @@
* Detection control
*/
-void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
da7219->aad->jack = jack;
da7219->aad->jack_inserted = false;
@@ -42,11 +39,10 @@ void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
/* Enable/Disable jack detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_ACCDET_EN_MASK,
(jack ? DA7219_ACCDET_EN_MASK : 0));
}
-EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
/*
* Button/HPTest work
@@ -56,17 +52,18 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
{
struct da7219_aad_priv *da7219_aad =
container_of(work, struct da7219_aad_priv, btn_det_work);
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = da7219_aad->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
u8 statusa, micbias_ctrl;
bool micbias_up = false;
int retries = 0;
/* Drive headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_OE_MASK,
DA7219_HP_L_AMP_OE_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_OE_MASK,
DA7219_HP_R_AMP_OE_MASK);
@@ -75,7 +72,7 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
snd_soc_dapm_sync(dapm);
do {
- statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+ statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
if (statusa & DA7219_MICBIAS_UP_STS_MASK)
micbias_up = true;
else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
@@ -83,7 +80,9 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
} while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
- dev_warn(codec->dev, "Mic bias status check timed out");
+ dev_warn(component->dev, "Mic bias status check timed out");
+
+ da7219->micbias_on_event = true;
/*
* Mic bias pulse required to enable mic, must be done before enabling
@@ -91,16 +90,16 @@ static void da7219_aad_btn_det_work(struct work_struct *work)
*/
if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
/* Pulse higher level voltage */
- micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
- snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
+ micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL);
+ snd_soc_component_update_bits(component, DA7219_MICBIAS_CTRL,
DA7219_MICBIAS1_LEVEL_MASK,
da7219_aad->micbias_pulse_lvl);
msleep(da7219_aad->micbias_pulse_time);
- snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
+ snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_ctrl);
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_BUTTON_CONFIG_MASK,
da7219_aad->btn_cfg);
}
@@ -109,102 +108,153 @@ static void da7219_aad_hptest_work(struct work_struct *work)
{
struct da7219_aad_priv *da7219_aad =
container_of(work, struct da7219_aad_priv, hptest_work);
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = da7219_aad->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
- u16 tonegen_freq_hptest;
- u8 accdet_cfg8;
- int report = 0;
+ __le16 tonegen_freq_hptest;
+ u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8;
+ int report = 0, ret;
- /* Lock DAPM and any Kcontrols that are affected by this test */
+ /* Lock DAPM, Kcontrols affected by this test and the PLL */
snd_soc_dapm_mutex_lock(dapm);
- mutex_lock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
+ mutex_lock(&da7219->pll_lock);
+
+ /* Ensure MCLK is available for HP test procedure */
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable mclk - %d\n", ret);
+ mutex_unlock(&da7219->pll_lock);
+ mutex_unlock(&da7219->ctrl_lock);
+ snd_soc_dapm_mutex_unlock(dapm);
+ return;
+ }
+ }
+
+ /*
+ * If MCLK not present, then we're using the internal oscillator and
+ * require different frequency settings to achieve the same result.
+ *
+ * If MCLK is present, but PLL is not enabled then we enable it here to
+ * ensure a consistent detection procedure.
+ */
+ pll_srm_sts = snd_soc_component_read(component, DA7219_PLL_SRM_STS);
+ if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) {
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+
+ pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL);
+ if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)
+ da7219_set_pll(component, DA7219_SYSCLK_PLL,
+ DA7219_PLL_FREQ_OUT_98304);
+ } else {
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC);
+ }
+
+ /* Ensure gain ramping at fastest rate */
+ gain_ramp_ctrl = snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8);
/* Bypass cache so it saves current settings */
regcache_cache_bypass(da7219->regmap, true);
/* Make sure Tone Generator is disabled */
- snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
/* Enable HPTest block, 1KOhms check */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
DA7219_HPTEST_EN_MASK |
DA7219_HPTEST_RES_SEL_1KOHMS);
/* Set gains to 0db */
- snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
- snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
- snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
- snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
+ snd_soc_component_write(component, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
/* Disable DAC filters, EQs and soft mute */
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
0);
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
0);
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
+ snd_soc_component_update_bits(component, DA7219_DAC_FILTERS5,
DA7219_DAC_SOFTMUTE_EN_MASK, 0);
/* Enable HP left & right paths */
- snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
DA7219_CP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
+ snd_soc_component_update_bits(component, DA7219_DIG_ROUTING_DAC,
DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
DA7219_DAC_L_SRC_TONEGEN |
DA7219_DAC_R_SRC_TONEGEN);
- snd_soc_update_bits(codec, DA7219_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL,
DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
DA7219_DAC_L_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL,
DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
DA7219_DAC_R_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_L_SELECT,
DA7219_MIXOUT_L_MIX_SELECT_MASK,
DA7219_MIXOUT_L_MIX_SELECT_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_R_SELECT,
DA7219_MIXOUT_R_MIX_SELECT_MASK,
DA7219_MIXOUT_R_MIX_SELECT_MASK);
- snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
+ snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1L,
DA7219_OUTFILT_ST_1L_SRC_MASK,
DA7219_DMIX_ST_SRC_OUTFILT1L);
- snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
+ snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1R,
DA7219_OUTFILT_ST_1R_SRC_MASK,
DA7219_DMIX_ST_SRC_OUTFILT1R);
- snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_L_AMP_EN_MASK,
DA7219_MIXOUT_L_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_MIXOUT_R_CTRL,
DA7219_MIXOUT_R_AMP_EN_MASK,
DA7219_MIXOUT_R_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ msleep(DA7219_SETTLING_DELAY);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_MASK |
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MUTE_EN_MASK |
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0);
+
+ /*
+ * If we're running from the internal oscillator then give audio paths
+ * time to settle before running test.
+ */
+ if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
+ msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
/* Configure & start Tone Generator */
- snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
- tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+ snd_soc_component_write(component, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
&tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
- snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2,
+ snd_soc_component_update_bits(component, DA7219_TONE_GEN_CFG2,
DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
DA7219_SWG_SEL_SRAMP |
DA7219_TONE_GEN_GAIN_MINUS_15DB);
- snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
msleep(DA7219_AAD_HPTEST_PERIOD);
/* Grab comparator reading */
- accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8);
+ accdet_cfg8 = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_8);
if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
report |= SND_JACK_HEADPHONE;
else
report |= SND_JACK_LINEOUT;
/* Stop tone generator */
- snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+ snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
msleep(DA7219_AAD_HPTEST_PERIOD);
@@ -212,6 +262,7 @@ static void da7219_aad_hptest_work(struct work_struct *work)
regcache_mark_dirty(da7219->regmap);
regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
DA7219_HP_R_CTRL);
+ msleep(DA7219_SETTLING_DELAY);
regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_R_CTRL);
regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
@@ -241,16 +292,36 @@ static void da7219_aad_hptest_work(struct work_struct *work)
regcache_cache_bypass(da7219->regmap, false);
/* Disable HPTest block */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
DA7219_HPTEST_EN_MASK, 0);
+ /*
+ * If we're running from the internal oscillator then give audio paths
+ * time to settle before allowing headphones to be driven as required.
+ */
+ if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
+ msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
+
+ /* Restore gain ramping rate */
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl);
+
/* Drive Headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
DA7219_HP_L_AMP_OE_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
DA7219_HP_R_AMP_OE_MASK);
- mutex_unlock(&da7219->lock);
+ /* Restore PLL to previous configuration, if re-configured */
+ if ((pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) &&
+ ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS))
+ da7219_set_pll(component, DA7219_SYSCLK_MCLK, 0);
+
+ /* Remove MCLK, if previously enabled */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+
+ mutex_unlock(&da7219->pll_lock);
+ mutex_unlock(&da7219->ctrl_lock);
snd_soc_dapm_mutex_unlock(dapm);
/*
@@ -262,6 +333,15 @@ static void da7219_aad_hptest_work(struct work_struct *work)
SND_JACK_HEADSET | SND_JACK_LINEOUT);
}
+static void da7219_aad_jack_det_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, jack_det_work.work);
+ struct snd_soc_component *component = da7219_aad->component;
+
+ /* Enable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01);
+}
/*
* IRQ
@@ -270,28 +350,44 @@ static void da7219_aad_hptest_work(struct work_struct *work)
static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
{
struct da7219_aad_priv *da7219_aad = data;
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = da7219_aad->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
u8 events[DA7219_AAD_IRQ_REG_MAX];
u8 statusa;
- int i, report = 0, mask = 0;
+ int i, ret, report = 0, mask = 0;
/* Read current IRQ events */
- regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
- events, DA7219_AAD_IRQ_REG_MAX);
+ ret = regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+ if (ret) {
+ dev_warn_ratelimited(component->dev, "Failed to read IRQ events: %d\n", ret);
+ return IRQ_NONE;
+ }
if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
return IRQ_NONE;
/* Read status register for jack insertion & type status */
- statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+ statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
+
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_INSERTED_MASK) {
+ u8 srm_st;
+ int delay = 0;
+
+ srm_st = snd_soc_component_read(component,
+ DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK;
+ delay = (da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 2);
+ queue_delayed_work(da7219_aad->aad_wq,
+ &da7219_aad->jack_det_work,
+ msecs_to_jiffies(delay));
+ }
/* Clear events */
regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
events, DA7219_AAD_IRQ_REG_MAX);
- dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+ dev_dbg(component->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
statusa);
@@ -319,12 +415,17 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
* handle a removal, and we can check at the end of
* hptest if we have a valid result or not.
*/
+
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+
if (statusa & DA7219_JACK_TYPE_STS_MASK) {
report |= SND_JACK_HEADSET;
mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
- schedule_work(&da7219_aad->btn_det_work);
+ queue_work(da7219_aad->aad_wq, &da7219_aad->btn_det_work);
} else {
- schedule_work(&da7219_aad->hptest_work);
+ queue_work(da7219_aad->aad_wq, &da7219_aad->hptest_work);
}
}
@@ -356,23 +457,29 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
mask |= DA7219_AAD_REPORT_ALL_MASK;
da7219_aad->jack_inserted = false;
+ /* Cancel any pending work */
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+
/* Un-drive headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_OE_MASK, 0);
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_OE_MASK, 0);
/* Ensure button detection disabled */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_BUTTON_CONFIG_MASK, 0);
+ da7219->micbias_on_event = false;
+
/* Disable mic bias */
snd_soc_dapm_disable_pin(dapm, "Mic Bias");
snd_soc_dapm_sync(dapm);
- /* Cancel any pending work */
- cancel_work_sync(&da7219_aad->btn_det_work);
- cancel_work_sync(&da7219_aad->hptest_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
}
}
@@ -382,11 +489,11 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
}
/*
- * DT to pdata conversion
+ * DT/ACPI to pdata conversion
*/
static enum da7219_aad_micbias_pulse_lvl
- da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val)
{
switch (val) {
case 2800:
@@ -394,13 +501,13 @@ static enum da7219_aad_micbias_pulse_lvl
case 2900:
return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
default:
- dev_warn(codec->dev, "Invalid micbias pulse level");
+ dev_warn(dev, "Invalid micbias pulse level");
return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
}
}
static enum da7219_aad_btn_cfg
- da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_cfg(struct device *dev, u32 val)
{
switch (val) {
case 2:
@@ -418,13 +525,13 @@ static enum da7219_aad_btn_cfg
case 500:
return DA7219_AAD_BTN_CFG_500MS;
default:
- dev_warn(codec->dev, "Invalid button config");
+ dev_warn(dev, "Invalid button config");
return DA7219_AAD_BTN_CFG_10MS;
}
}
static enum da7219_aad_mic_det_thr
- da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_mic_det_thr(struct device *dev, u32 val)
{
switch (val) {
case 200:
@@ -436,13 +543,13 @@ static enum da7219_aad_mic_det_thr
case 1000:
return DA7219_AAD_MIC_DET_THR_1000_OHMS;
default:
- dev_warn(codec->dev, "Invalid mic detect threshold");
+ dev_warn(dev, "Invalid mic detect threshold");
return DA7219_AAD_MIC_DET_THR_500_OHMS;
}
}
static enum da7219_aad_jack_ins_deb
- da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val)
{
switch (val) {
case 5:
@@ -462,30 +569,43 @@ static enum da7219_aad_jack_ins_deb
case 1000:
return DA7219_AAD_JACK_INS_DEB_1S;
default:
- dev_warn(codec->dev, "Invalid jack insert debounce");
+ dev_warn(dev, "Invalid jack insert debounce");
return DA7219_AAD_JACK_INS_DEB_20MS;
}
}
+static enum da7219_aad_jack_ins_det_pty
+ da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str)
+{
+ if (!strcmp(str, "low")) {
+ return DA7219_AAD_JACK_INS_DET_PTY_LOW;
+ } else if (!strcmp(str, "high")) {
+ return DA7219_AAD_JACK_INS_DET_PTY_HIGH;
+ } else {
+ dev_warn(dev, "Invalid jack insertion detection polarity");
+ return DA7219_AAD_JACK_INS_DET_PTY_LOW;
+ }
+}
+
static enum da7219_aad_jack_det_rate
- da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+ da7219_aad_fw_jack_det_rate(struct device *dev, const char *str)
{
- if (!strcmp(str, "32ms_64ms")) {
+ if (!strcmp(str, "32_64")) {
return DA7219_AAD_JACK_DET_RATE_32_64MS;
- } else if (!strcmp(str, "64ms_128ms")) {
+ } else if (!strcmp(str, "64_128")) {
return DA7219_AAD_JACK_DET_RATE_64_128MS;
- } else if (!strcmp(str, "128ms_256ms")) {
+ } else if (!strcmp(str, "128_256")) {
return DA7219_AAD_JACK_DET_RATE_128_256MS;
- } else if (!strcmp(str, "256ms_512ms")) {
+ } else if (!strcmp(str, "256_512")) {
return DA7219_AAD_JACK_DET_RATE_256_512MS;
} else {
- dev_warn(codec->dev, "Invalid jack detect rate");
+ dev_warn(dev, "Invalid jack detect rate");
return DA7219_AAD_JACK_DET_RATE_256_512MS;
}
}
static enum da7219_aad_jack_rem_deb
- da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val)
{
switch (val) {
case 1:
@@ -497,13 +617,13 @@ static enum da7219_aad_jack_rem_deb
case 20:
return DA7219_AAD_JACK_REM_DEB_20MS;
default:
- dev_warn(codec->dev, "Invalid jack removal debounce");
+ dev_warn(dev, "Invalid jack removal debounce");
return DA7219_AAD_JACK_REM_DEB_1MS;
}
}
static enum da7219_aad_btn_avg
- da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_avg(struct device *dev, u32 val)
{
switch (val) {
case 1:
@@ -515,13 +635,13 @@ static enum da7219_aad_btn_avg
case 8:
return DA7219_AAD_BTN_AVG_8;
default:
- dev_warn(codec->dev, "Invalid button average value");
+ dev_warn(dev, "Invalid button average value");
return DA7219_AAD_BTN_AVG_2;
}
}
static enum da7219_aad_adc_1bit_rpt
- da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val)
{
switch (val) {
case 1:
@@ -533,108 +653,116 @@ static enum da7219_aad_adc_1bit_rpt
case 8:
return DA7219_AAD_ADC_1BIT_RPT_8;
default:
- dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
+ dev_warn(dev, "Invalid ADC 1-bit repeat value");
return DA7219_AAD_ADC_1BIT_RPT_1;
}
}
-static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
{
- struct device_node *np = codec->dev->of_node;
- struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct fwnode_handle *aad_np;
struct da7219_aad_pdata *aad_pdata;
- const char *of_str;
- u32 of_val32;
+ const char *fw_str;
+ u32 fw_val32;
+ aad_np = device_get_named_child_node(dev, "da7219_aad");
if (!aad_np)
return NULL;
- aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
- if (!aad_pdata)
- goto out;
+ aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL);
+ if (!aad_pdata) {
+ fwnode_handle_put(aad_np);
+ return NULL;
+ }
- aad_pdata->irq = irq_of_parse_and_map(np, 0);
+ aad_pdata->irq = i2c->irq;
- if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
- &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+ &fw_val32) >= 0)
aad_pdata->micbias_pulse_lvl =
- da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+ da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32);
else
aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
- if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
- &of_val32) >= 0)
- aad_pdata->micbias_pulse_time = of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+ &fw_val32) >= 0)
+ aad_pdata->micbias_pulse_time = fw_val32;
- if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
- aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+ if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
+ aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, fw_val32);
else
aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
- if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
aad_pdata->mic_det_thr =
- da7219_aad_of_mic_det_thr(codec, of_val32);
+ da7219_aad_fw_mic_det_thr(dev, fw_val32);
else
- aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+ aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_200_OHMS;
- if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
aad_pdata->jack_ins_deb =
- da7219_aad_of_jack_ins_deb(codec, of_val32);
+ da7219_aad_fw_jack_ins_deb(dev, fw_val32);
else
aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
- if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+ if (!fwnode_property_read_string(aad_np, "dlg,jack-ins-det-pty", &fw_str))
+ aad_pdata->jack_ins_det_pty =
+ da7219_aad_fw_jack_ins_det_pty(dev, fw_str);
+ else
+ aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW;
+
+ if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
aad_pdata->jack_det_rate =
- da7219_aad_of_jack_det_rate(codec, of_str);
+ da7219_aad_fw_jack_det_rate(dev, fw_str);
else
aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
- if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
aad_pdata->jack_rem_deb =
- da7219_aad_of_jack_rem_deb(codec, of_val32);
+ da7219_aad_fw_jack_rem_deb(dev, fw_val32);
else
aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
- if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
- aad_pdata->a_d_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
+ aad_pdata->a_d_btn_thr = (u8) fw_val32;
else
aad_pdata->a_d_btn_thr = 0xA;
- if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
- aad_pdata->d_b_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
+ aad_pdata->d_b_btn_thr = (u8) fw_val32;
else
aad_pdata->d_b_btn_thr = 0x16;
- if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
- aad_pdata->b_c_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
+ aad_pdata->b_c_btn_thr = (u8) fw_val32;
else
aad_pdata->b_c_btn_thr = 0x21;
- if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
- aad_pdata->c_mic_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
+ aad_pdata->c_mic_btn_thr = (u8) fw_val32;
else
aad_pdata->c_mic_btn_thr = 0x3E;
- if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
- aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+ if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
+ aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, fw_val32);
else
aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
- if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
aad_pdata->adc_1bit_rpt =
- da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+ da7219_aad_fw_adc_1bit_rpt(dev, fw_val32);
else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
-out:
- of_node_put(aad_np);
+ fwnode_handle_put(aad_np);
return aad_pdata;
}
-static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
+static void da7219_aad_handle_pdata(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_aad_priv *da7219_aad = da7219->aad;
struct da7219_pdata *pdata = da7219->pdata;
@@ -680,7 +808,7 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
DA7219_MIC_DET_THRESH_SHIFT);
mask |= DA7219_MIC_DET_THRESH_MASK;
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, mask, cfg);
cfg = 0;
mask = 0;
@@ -715,15 +843,15 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
DA7219_JACKDET_REM_DEB_SHIFT);
mask |= DA7219_JACKDET_REM_DEB_MASK;
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_2, mask, cfg);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_3,
aad_pdata->a_d_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_4,
aad_pdata->d_b_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_5,
aad_pdata->b_c_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
+ snd_soc_component_write(component, DA7219_ACCDET_CONFIG_6,
aad_pdata->c_mic_btn_thr);
cfg = 0;
@@ -746,39 +874,144 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
DA7219_ADC_1_BIT_REPEAT_SHIFT);
mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
}
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg);
+
+ switch (aad_pdata->jack_ins_det_pty) {
+ case DA7219_AAD_JACK_INS_DET_PTY_LOW:
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0x75, 0x80);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ break;
+ case DA7219_AAD_JACK_INS_DET_PTY_HIGH:
+ snd_soc_component_write(component, 0xF0, 0x8B);
+ snd_soc_component_write(component, 0x75, 0x00);
+ snd_soc_component_write(component, 0xF0, 0x00);
+ break;
+ default:
+ break;
+ }
}
}
+static void da7219_aad_handle_gnd_switch_time(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 jack_det;
+
+ jack_det = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_2)
+ & DA7219_JACK_DETECT_RATE_MASK;
+ switch (jack_det) {
+ case 0x00:
+ da7219_aad->gnd_switch_delay = 32;
+ break;
+ case 0x10:
+ da7219_aad->gnd_switch_delay = 64;
+ break;
+ case 0x20:
+ da7219_aad->gnd_switch_delay = 128;
+ break;
+ case 0x30:
+ da7219_aad->gnd_switch_delay = 256;
+ break;
+ default:
+ da7219_aad->gnd_switch_delay = 32;
+ break;
+ }
+}
+
+/*
+ * Suspend/Resume
+ */
+
+void da7219_aad_suspend(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u8 micbias_ctrl;
+
+ disable_irq(da7219_aad->irq);
+
+ if (da7219_aad->jack) {
+ /* Disable jack detection during suspend */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK, 0);
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+
+ /*
+ * If we have a 4-pole jack inserted, then micbias will be
+ * enabled. We can disable micbias here, and keep a note to
+ * re-enable it on resume. If jack removal occurred during
+ * suspend then this will be dealt with through the IRQ handler.
+ */
+ if (da7219_aad->jack_inserted) {
+ micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL);
+ if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) {
+ snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+ da7219_aad->micbias_resume_enable = true;
+ }
+ }
+ }
+}
+
+void da7219_aad_resume(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ if (da7219_aad->jack) {
+ /* Re-enable micbias if previously enabled for 4-pole jack */
+ if (da7219_aad->jack_inserted &&
+ da7219_aad->micbias_resume_enable) {
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+ da7219_aad->micbias_resume_enable = false;
+ }
+
+ /* Re-enable jack detection */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ DA7219_ACCDET_EN_MASK);
+ }
+
+ enable_irq(da7219_aad->irq);
+}
+
/*
* Init/Exit
*/
-int da7219_aad_init(struct snd_soc_codec *codec)
+int da7219_aad_init(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
u8 mask[DA7219_AAD_IRQ_REG_MAX];
int ret;
- da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
- if (!da7219_aad)
- return -ENOMEM;
-
- da7219->aad = da7219_aad;
- da7219_aad->codec = codec;
-
- /* Handle any DT/platform data */
- if ((codec->dev->of_node) && (da7219->pdata))
- da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+ da7219_aad->component = component;
- da7219_aad_handle_pdata(codec);
+ /* Handle any DT/ACPI/platform data */
+ da7219_aad_handle_pdata(component);
/* Disable button detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_BUTTON_CONFIG_MASK, 0);
+ da7219_aad_handle_gnd_switch_time(component);
+
+ da7219_aad->aad_wq = create_singlethread_workqueue("da7219-aad");
+ if (!da7219_aad->aad_wq) {
+ dev_err(component->dev, "Failed to create aad workqueue\n");
+ return -ENOMEM;
+ }
+
+ INIT_DELAYED_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work);
INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
@@ -787,7 +1020,7 @@ int da7219_aad_init(struct snd_soc_codec *codec)
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"da7219-aad", da7219_aad);
if (ret) {
- dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
return ret;
}
@@ -798,11 +1031,10 @@ int da7219_aad_init(struct snd_soc_codec *codec)
return 0;
}
-EXPORT_SYMBOL_GPL(da7219_aad_init);
-void da7219_aad_exit(struct snd_soc_codec *codec)
+void da7219_aad_exit(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_aad_priv *da7219_aad = da7219->aad;
u8 mask[DA7219_AAD_IRQ_REG_MAX];
@@ -813,11 +1045,36 @@ void da7219_aad_exit(struct snd_soc_codec *codec)
free_irq(da7219_aad->irq, da7219_aad);
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
cancel_work_sync(&da7219_aad->btn_det_work);
cancel_work_sync(&da7219_aad->hptest_work);
+ destroy_workqueue(da7219_aad->aad_wq);
+}
+
+/*
+ * AAD related I2C probe handling
+ */
+
+int da7219_aad_probe(struct i2c_client *i2c)
+{
+ struct da7219_priv *da7219 = i2c_get_clientdata(i2c);
+ struct device *dev = &i2c->dev;
+ struct da7219_aad_priv *da7219_aad;
+
+ da7219_aad = devm_kzalloc(dev, sizeof(*da7219_aad), GFP_KERNEL);
+ if (!da7219_aad)
+ return -ENOMEM;
+
+ da7219->aad = da7219_aad;
+
+ /* Retrieve any DT/ACPI/platform data */
+ if (da7219->pdata && !da7219->pdata->aad_pdata)
+ da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(da7219_aad_exit);
MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
index 4fccf677cd06..fbfbf3e67918 100644
--- a/sound/soc/codecs/da7219-aad.h
+++ b/sound/soc/codecs/da7219-aad.h
@@ -1,20 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* da7219-aad.h - DA7322 ASoC AAD Driver
*
* Copyright (c) 2015 Dialog Semiconductor Ltd.
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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 __DA7219_AAD_H
#define __DA7219_AAD_H
#include <linux/timer.h>
+#include <linux/mutex.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/da7219-aad.h>
@@ -176,8 +173,10 @@
#define DA7219_AAD_MICBIAS_CHK_DELAY 10
#define DA7219_AAD_MICBIAS_CHK_RETRIES 5
-#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28
-#define DA7219_AAD_HPTEST_PERIOD 65
+#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28
+#define DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC 0x4D
+#define DA7219_AAD_HPTEST_PERIOD 65
+#define DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY 20
enum da7219_aad_event_regs {
DA7219_AAD_IRQ_REG_A = 0,
@@ -187,8 +186,9 @@ enum da7219_aad_event_regs {
/* Private data */
struct da7219_aad_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
int irq;
+ int gnd_switch_delay;
u8 micbias_pulse_lvl;
u32 micbias_pulse_time;
@@ -197,16 +197,26 @@ struct da7219_aad_priv {
struct work_struct btn_det_work;
struct work_struct hptest_work;
+ struct delayed_work jack_det_work;
+ struct workqueue_struct *aad_wq;
struct snd_soc_jack *jack;
+ bool micbias_resume_enable;
bool jack_inserted;
};
/* AAD control */
-void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack);
+
+/* Suspend/Resume */
+void da7219_aad_suspend(struct snd_soc_component *component);
+void da7219_aad_resume(struct snd_soc_component *component);
/* Init/Exit */
-int da7219_aad_init(struct snd_soc_codec *codec);
-void da7219_aad_exit(struct snd_soc_codec *codec);
+int da7219_aad_init(struct snd_soc_component *component);
+void da7219_aad_exit(struct snd_soc_component *component);
+
+/* I2C Probe */
+int da7219_aad_probe(struct i2c_client *i2c);
#endif /* __DA7219_AAD_H */
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 81c0708b85c1..298a626df3ad 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -1,19 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* da7219.c - DA7219 ALSA SoC Codec Driver
*
* Copyright (c) 2015 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm.h>
@@ -254,13 +254,13 @@ static const struct soc_enum da7219_cp_track_mode =
static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret;
- mutex_lock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
ret = snd_soc_get_volsw(kcontrol, ucontrol);
- mutex_unlock(&da7219->lock);
+ mutex_unlock(&da7219->ctrl_lock);
return ret;
}
@@ -268,13 +268,13 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret;
- mutex_lock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
ret = snd_soc_put_volsw(kcontrol, ucontrol);
- mutex_unlock(&da7219->lock);
+ mutex_unlock(&da7219->ctrl_lock);
return ret;
}
@@ -282,13 +282,13 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret;
- mutex_lock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
ret = snd_soc_get_enum_double(kcontrol, ucontrol);
- mutex_unlock(&da7219->lock);
+ mutex_unlock(&da7219->ctrl_lock);
return ret;
}
@@ -296,67 +296,67 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret;
- mutex_lock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
- mutex_unlock(&da7219->lock);
+ mutex_unlock(&da7219->ctrl_lock);
return ret;
}
/* ALC */
-static void da7219_alc_calib(struct snd_soc_codec *codec)
+static void da7219_alc_calib(struct snd_soc_component *component)
{
u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl;
/* Save current state of mic control register */
- mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL);
+ mic_ctrl = snd_soc_component_read(component, DA7219_MIC_1_CTRL);
/* Save current state of input mixer control register */
- mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL);
+ mixin_ctrl = snd_soc_component_read(component, DA7219_MIXIN_L_CTRL);
/* Save current state of input ADC control register */
- adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL);
+ adc_ctrl = snd_soc_component_read(component, DA7219_ADC_L_CTRL);
/* Enable then Mute MIC PGAs */
- snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+ snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
DA7219_MIC_1_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIC_1_CTRL,
+ snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL,
DA7219_MIC_1_AMP_MUTE_EN_MASK,
DA7219_MIC_1_AMP_MUTE_EN_MASK);
/* Enable input mixers unmuted */
- snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL,
DA7219_MIXIN_L_AMP_EN_MASK |
DA7219_MIXIN_L_AMP_MUTE_EN_MASK,
DA7219_MIXIN_L_AMP_EN_MASK);
/* Enable input filters unmuted */
- snd_soc_update_bits(codec, DA7219_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL,
DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK,
DA7219_ADC_L_EN_MASK);
/* Perform auto calibration */
- snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
DA7219_ALC_AUTO_CALIB_EN_MASK,
DA7219_ALC_AUTO_CALIB_EN_MASK);
do {
- calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1);
+ calib_ctrl = snd_soc_component_read(component, DA7219_ALC_CTRL1);
} while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK);
/* If auto calibration fails, disable DC offset, hybrid ALC */
if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed with overflow\n");
- snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
DA7219_ALC_OFFSET_EN_MASK |
DA7219_ALC_SYNC_MODE_MASK, 0);
} else {
/* Enable DC offset cancellation, hybrid mode */
- snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7219_ALC_CTRL1,
DA7219_ALC_OFFSET_EN_MASK |
DA7219_ALC_SYNC_MODE_MASK,
DA7219_ALC_OFFSET_EN_MASK |
@@ -364,20 +364,20 @@ static void da7219_alc_calib(struct snd_soc_codec *codec)
}
/* Restore input filter control register to original state */
- snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl);
+ snd_soc_component_write(component, DA7219_ADC_L_CTRL, adc_ctrl);
/* Restore input mixer control registers to original state */
- snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+ snd_soc_component_write(component, DA7219_MIXIN_L_CTRL, mixin_ctrl);
/* Restore MIC control registers to original states */
- snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl);
+ snd_soc_component_write(component, DA7219_MIC_1_CTRL, mic_ctrl);
}
static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret;
ret = snd_soc_put_volsw(kcontrol, ucontrol);
@@ -387,7 +387,7 @@ static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
* make sure calibrated offsets are updated.
*/
if ((ret == 1) && (da7219->alc_en))
- da7219_alc_calib(codec);
+ da7219_alc_calib(component);
return ret;
}
@@ -395,13 +395,13 @@ static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
/* Force ALC offset calibration if enabling ALC */
if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) {
- da7219_alc_calib(codec);
+ da7219_alc_calib(component);
da7219->alc_en = true;
} else {
da7219->alc_en = false;
@@ -414,17 +414,17 @@ static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mixer_ctrl =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int reg = mixer_ctrl->reg;
- u16 val;
+ __le16 val;
int ret;
- mutex_lock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val));
- mutex_unlock(&da7219->lock);
+ mutex_unlock(&da7219->ctrl_lock);
if (ret)
return ret;
@@ -441,12 +441,12 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mixer_ctrl =
(struct soc_mixer_control *) kcontrol->private_value;
unsigned int reg = mixer_ctrl->reg;
- u16 val;
+ __le16 val_new, val_old;
int ret;
/*
@@ -454,13 +454,19 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
* Therefore we need to convert to little endian here to align with
* HW registers.
*/
- val = cpu_to_le16(ucontrol->value.integer.value[0]);
+ val_new = cpu_to_le16(ucontrol->value.integer.value[0]);
- mutex_lock(&da7219->lock);
- ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val));
- mutex_unlock(&da7219->lock);
+ mutex_lock(&da7219->ctrl_lock);
+ ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old));
+ if (ret == 0 && (val_old != val_new))
+ ret = regmap_raw_write(da7219->regmap, reg,
+ &val_new, sizeof(val_new));
+ mutex_unlock(&da7219->ctrl_lock);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return val_old != val_new;
}
@@ -764,63 +770,185 @@ static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
* DAPM Events
*/
+static int da7219_mic_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (da7219->micbias_on_event) {
+ /*
+ * Delay only for first capture after bias enabled to
+ * avoid possible DC offset related noise.
+ */
+ da7219->micbias_on_event = false;
+ msleep(da7219->mic_pga_delay);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int da7219_dai_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
u8 pll_ctrl, pll_status;
- int i = 0;
+ int i = 0, ret;
bool srm_lock = false;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (da7219->master)
+ if (da7219->master) {
/* Enable DAI clks for master mode */
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_CLK_EN_MASK,
- DA7219_DAI_CLK_EN_MASK);
+ if (bclk) {
+ ret = clk_prepare_enable(bclk);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable DAI clks\n");
+ return ret;
+ }
+ } else {
+ snd_soc_component_update_bits(component,
+ DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
+ }
+ }
/* PC synchronised to DAI */
- snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ snd_soc_component_update_bits(component, DA7219_PC_COUNT,
DA7219_PC_FREERUN_MASK, 0);
/* Slave mode, if SRM not enabled no need for status checks */
- pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL);
+ pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL);
if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM)
return 0;
/* Check SRM has locked */
do {
- pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS);
+ pll_status = snd_soc_component_read(component, DA7219_PLL_SRM_STS);
if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) {
srm_lock = true;
} else {
++i;
msleep(50);
}
- } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock));
+ } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock));
if (!srm_lock)
- dev_warn(codec->dev, "SRM failed to lock\n");
+ dev_warn(component->dev, "SRM failed to lock\n");
return 0;
case SND_SOC_DAPM_POST_PMD:
/* PC free-running */
- snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ snd_soc_component_update_bits(component, DA7219_PC_COUNT,
DA7219_PC_FREERUN_MASK,
DA7219_PC_FREERUN_MASK);
/* Disable DAI clks if in master mode */
- if (da7219->master)
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_CLK_EN_MASK, 0);
+ if (da7219->master) {
+ if (bclk)
+ clk_disable_unprepare(bclk);
+ else
+ snd_soc_component_update_bits(component,
+ DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ 0);
+ }
+
return 0;
default:
return -EINVAL;
}
}
+static int da7219_settling_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(DA7219_SETTLING_DELAY);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u8 hp_ctrl, min_gain_mask;
+
+ switch (w->reg) {
+ case DA7219_MIXOUT_L_CTRL:
+ hp_ctrl = DA7219_HP_L_CTRL;
+ min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK;
+ break;
+ case DA7219_MIXOUT_R_CTRL:
+ hp_ctrl = DA7219_HP_R_CTRL;
+ min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Enable minimum gain on HP to avoid pops */
+ snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask,
+ min_gain_mask);
+
+ msleep(DA7219_MIN_GAIN_DELAY);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Remove minimum gain on HP */
+ snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask, 0);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Ensure nominal gain ramping for DAPM sequence */
+ da7219->gain_ramp_ctrl =
+ snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL,
+ DA7219_GAIN_RAMP_RATE_NOMINAL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ /* Restore previous gain ramp settings */
+ snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL,
+ da7219->gain_ramp_ctrl);
+ break;
+ }
+
+ return 0;
+}
+
/*
* DAPM Widgets
@@ -836,12 +964,12 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC"),
/* Input PGAs */
- SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL,
- DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL,
- DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
+ SND_SOC_DAPM_PGA_E("Mic PGA", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mic_pga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("Mixin PGA", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_settling_event, SND_SOC_DAPM_POST_PMU),
/* Input Filters */
SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT,
@@ -878,7 +1006,8 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* DAI */
- SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT),
SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
/* Output Muxes */
@@ -904,30 +1033,46 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
/* DACs */
- SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
- DA7219_NO_INVERT),
- SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
- DA7219_NO_INVERT),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output PGAs */
- SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
- DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
- DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
- SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output Supplies */
- SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
- DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL,
+ DA7219_CP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU),
/* Outputs */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
+
+ /* Pre/Post Power */
+ SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event),
+ SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event),
};
@@ -1000,8 +1145,8 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
{"Mixout Left PGA", NULL, "DACL"},
{"Mixout Right PGA", NULL, "DACR"},
- {"Headphone Left PGA", NULL, "Mixout Left PGA"},
- {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+ {"HPL", NULL, "Mixout Left PGA"},
+ {"HPR", NULL, "Mixout Right PGA"},
{"HPL", NULL, "Headphone Left PGA"},
{"HPR", NULL, "Headphone Right PGA"},
@@ -1018,14 +1163,19 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
static int da7219_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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
int ret = 0;
- if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
+ mutex_lock(&da7219->pll_lock);
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) {
+ mutex_unlock(&da7219->pll_lock);
return 0;
+ }
- if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+ if ((freq < 2000000) || (freq > 54000000)) {
+ mutex_unlock(&da7219->pll_lock);
dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
freq);
return -EINVAL;
@@ -1033,16 +1183,17 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (clk_id) {
case DA7219_CLKSRC_MCLK_SQR:
- snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
DA7219_PLL_MCLK_SQR_EN_MASK,
DA7219_PLL_MCLK_SQR_EN_MASK);
break;
case DA7219_CLKSRC_MCLK:
- snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
DA7219_PLL_MCLK_SQR_EN_MASK, 0);
break;
default:
dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ mutex_unlock(&da7219->pll_lock);
return -EINVAL;
}
@@ -1054,20 +1205,21 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
if (ret) {
dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
freq);
+ mutex_unlock(&da7219->pll_lock);
return ret;
}
}
da7219->mclk_rate = freq;
+ mutex_unlock(&da7219->pll_lock);
+
return 0;
}
-static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- int source, unsigned int fref, unsigned int fout)
+int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
u8 pll_ctrl, indiv_bits, indiv;
u8 pll_frac_top, pll_frac_bot, pll_integer;
@@ -1076,26 +1228,26 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* Verify 2MHz - 54MHz MCLK provided, and set input divider */
if (da7219->mclk_rate < 2000000) {
- dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ dev_err(component->dev, "PLL input clock %d below valid range\n",
da7219->mclk_rate);
return -EINVAL;
- } else if (da7219->mclk_rate <= 5000000) {
- indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
- indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
- } else if (da7219->mclk_rate <= 10000000) {
- indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
- indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
- } else if (da7219->mclk_rate <= 20000000) {
- indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
- indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
- } else if (da7219->mclk_rate <= 40000000) {
- indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
- indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 4500000) {
+ indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 9000000) {
+ indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ;
+ indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 18000000) {
+ indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ;
+ indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 36000000) {
+ indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ;
+ indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL;
} else if (da7219->mclk_rate <= 54000000) {
- indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
- indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
+ indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ dev_err(component->dev, "PLL input clock %d above valid range\n",
da7219->mclk_rate);
return -EINVAL;
}
@@ -1106,7 +1258,7 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
switch (source) {
case DA7219_SYSCLK_MCLK:
pll_ctrl |= DA7219_PLL_MODE_BYPASS;
- snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
DA7219_PLL_INDIV_MASK |
DA7219_PLL_MODE_MASK, pll_ctrl);
return 0;
@@ -1117,7 +1269,7 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_ctrl |= DA7219_PLL_MODE_SRM;
break;
default:
- dev_err(codec->dev, "Invalid PLL config\n");
+ dev_err(component->dev, "Invalid PLL config\n");
return -EINVAL;
}
@@ -1129,27 +1281,41 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
/* Write PLL config & dividers */
- snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer);
- snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ snd_soc_component_write(component, DA7219_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA7219_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA7219_PLL_INTEGER, pll_integer);
+ snd_soc_component_update_bits(component, DA7219_PLL_CTRL,
DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK,
pll_ctrl);
return 0;
}
+static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&da7219->pll_lock);
+ ret = da7219_set_pll(component, source, fout);
+ mutex_unlock(&da7219->pll_lock);
+
+ return ret;
+}
+
static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
u8 dai_clk_mode = 0, dai_ctrl = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7219->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7219->master = false;
break;
default:
@@ -1216,126 +1382,138 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- /* By default 64 BCLKs per WCLK is supported */
- dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64;
-
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK |
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK,
dai_clk_mode);
- snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+ snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
dai_ctrl);
return 0;
}
+static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
+ unsigned long factor)
+{
+ u8 bclks_per_wclk;
+
+ switch (factor) {
+ case 32:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ bclks_per_wclk);
+
+ return 0;
+}
+
static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- u8 dai_bclks_per_wclk;
+ struct snd_soc_component *component = dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+ unsigned int ch_mask;
+ unsigned long sr, bclk_rate;
+ u8 slot_offset;
u16 offset;
+ __le16 dai_offset;
u32 frame_size;
+ int ret;
- /* No channels enabled so disable TDM, revert to 64-bit frames */
+ /* No channels enabled so disable TDM */
if (!tx_mask) {
- snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL,
DA7219_DAI_TDM_CH_EN_MASK |
DA7219_DAI_TDM_MODE_EN_MASK, 0);
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK,
- DA7219_DAI_BCLKS_PER_WCLK_64);
+ da7219->tdm_en = false;
return 0;
}
/* Check we have valid slots */
- if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
- dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ slot_offset = ffs(tx_mask) - 1;
+ ch_mask = (tx_mask >> slot_offset);
+ if (fls(ch_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
+ dev_err(component->dev,
+ "Invalid number of slots, max = %d\n",
DA7219_DAI_TDM_MAX_SLOTS);
return -EINVAL;
}
- /* Check we have a valid offset given */
- if (rx_mask > DA7219_DAI_OFFSET_MAX) {
- dev_err(codec->dev, "Invalid slot offset, max = %d\n",
- DA7219_DAI_OFFSET_MAX);
+ /*
+ * Ensure we have a valid offset into the frame, based on slot width
+ * and slot offset of first slot we're interested in.
+ */
+ offset = slot_offset * slot_width;
+ if (offset > DA7219_DAI_OFFSET_MAX) {
+ dev_err(component->dev, "Invalid frame offset %d\n", offset);
return -EINVAL;
}
- /* Calculate & validate frame size based on slot info provided. */
- frame_size = slots * slot_width;
- switch (frame_size) {
- case 32:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
- break;
- case 64:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
- break;
- case 128:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
- break;
- case 256:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
- break;
- default:
- dev_err(codec->dev, "Invalid frame size %d\n", frame_size);
- return -EINVAL;
+ /*
+ * If we're master, calculate & validate frame size based on slot info
+ * provided as we have a limited set of rates available.
+ */
+ if (da7219->master) {
+ frame_size = slots * slot_width;
+
+ if (bclk) {
+ sr = clk_get_rate(wclk);
+ bclk_rate = sr * frame_size;
+ ret = clk_set_rate(bclk, bclk_rate);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set TDM BCLK rate %lu: %d\n",
+ bclk_rate, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_bclks_per_wclk(component, frame_size);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set TDM BCLKs per WCLK %d: %d\n",
+ frame_size, ret);
+ return ret;
+ }
+ }
}
- snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK,
- dai_bclks_per_wclk);
-
- offset = cpu_to_le16(rx_mask);
+ dai_offset = cpu_to_le16(offset);
regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
- &offset, sizeof(offset));
+ &dai_offset, sizeof(dai_offset));
- snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL,
DA7219_DAI_TDM_CH_EN_MASK |
DA7219_DAI_TDM_MODE_EN_MASK,
- (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+ (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
DA7219_DAI_TDM_MODE_EN_MASK);
+ da7219->tdm_en = true;
+
return 0;
}
-static int da7219_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int da7219_set_sr(struct snd_soc_component *component,
+ unsigned long rate)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 dai_ctrl = 0, fs;
- unsigned int channels;
-
- switch (params_width(params)) {
- case 16:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
- break;
- case 20:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
- break;
- case 24:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
- break;
- case 32:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
- break;
- default:
- return -EINVAL;
- }
-
- channels = params_channels(params);
- if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
- dev_err(codec->dev,
- "Invalid number of channels, only 1 to %d supported\n",
- DA7219_DAI_CH_NUM_MAX);
- return -EINVAL;
- }
- dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+ u8 fs;
- switch (params_rate(params)) {
+ switch (rate) {
case 8000:
fs = DA7219_SR_8000;
break;
@@ -1373,11 +1551,118 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, DA7219_DAI_CTRL,
+ snd_soc_component_write(component, DA7219_SR, fs);
+
+ return 0;
+}
+
+static int da7219_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+ u8 dai_ctrl = 0;
+ unsigned int channels;
+ unsigned long sr, bclk_rate;
+ int word_len = params_width(params);
+ int frame_size, ret;
+
+ switch (word_len) {
+ case 16:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
+ dev_err(component->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7219_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+ sr = params_rate(params);
+ if (da7219->master && wclk) {
+ ret = clk_set_rate(wclk, sr);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set WCLK SR %lu: %d\n", sr, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_sr(component, sr);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set SR %lu: %d\n", sr, ret);
+ return ret;
+ }
+ }
+
+ /*
+ * If we're master, then we have a limited set of BCLK rates we
+ * support. For slave mode this isn't the case and the codec can detect
+ * the BCLK rate automatically.
+ */
+ if (da7219->master && !da7219->tdm_en) {
+ if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
+ frame_size = 32;
+ else
+ frame_size = 64;
+
+ if (bclk) {
+ bclk_rate = frame_size * sr;
+ /*
+ * Rounding the rate here avoids failure trying to set a
+ * new rate on an already enabled bclk. In that
+ * instance this will just set the same rate as is
+ * currently in use, and so should continue without
+ * problem, as long as the BCLK rate is suitable for the
+ * desired frame size.
+ */
+ bclk_rate = clk_round_rate(bclk, bclk_rate);
+ if ((bclk_rate / sr) < frame_size) {
+ dev_err(component->dev,
+ "BCLK rate mismatch against frame size");
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(bclk, bclk_rate);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set BCLK rate %lu: %d\n",
+ bclk_rate, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_bclks_per_wclk(component, frame_size);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set BCLKs per WCLK %d: %d\n",
+ frame_size, ret);
+ return ret;
+ }
+ }
+ }
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
DA7219_DAI_WORD_LENGTH_MASK |
DA7219_DAI_CH_NUM_MASK,
dai_ctrl);
- snd_soc_write(codec, DA7219_SR, fs);
return 0;
}
@@ -1393,41 +1678,57 @@ static const struct snd_soc_dai_ops da7219_dai_ops = {
#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#define DA7219_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000)
+
static struct snd_soc_dai_driver da7219_dai = {
.name = "da7219-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = DA7219_DAI_CH_NUM_MAX,
- .rates = SNDRV_PCM_RATE_8000_96000,
+ .rates = DA7219_RATES,
.formats = DA7219_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = DA7219_DAI_CH_NUM_MAX,
- .rates = SNDRV_PCM_RATE_8000_96000,
+ .rates = DA7219_RATES,
.formats = DA7219_FORMATS,
},
.ops = &da7219_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.symmetric_channels = 1,
- .symmetric_samplebits = 1,
+ .symmetric_sample_bits = 1,
};
/*
- * DT
+ * DT/ACPI
*/
+#ifdef CONFIG_OF
static const struct of_device_id da7219_of_match[] = {
{ .compatible = "dlg,da7219", },
{ }
};
MODULE_DEVICE_TABLE(of, da7219_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7219_acpi_match[] = {
+ { .id = "DLGS7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
+#endif
static enum da7219_micbias_voltage
- da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+ da7219_fw_micbias_lvl(struct device *dev, u32 val)
{
switch (val) {
case 1600:
@@ -1443,13 +1744,13 @@ static enum da7219_micbias_voltage
case 2600:
return DA7219_MICBIAS_2_6V;
default:
- dev_warn(codec->dev, "Invalid micbias level");
+ dev_warn(dev, "Invalid micbias level");
return DA7219_MICBIAS_2_2V;
}
}
static enum da7219_mic_amp_in_sel
- da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+ da7219_fw_mic_amp_in_sel(struct device *dev, const char *str)
{
if (!strcmp(str, "diff")) {
return DA7219_MIC_AMP_IN_SEL_DIFF;
@@ -1458,29 +1759,39 @@ static enum da7219_mic_amp_in_sel
} else if (!strcmp(str, "se_n")) {
return DA7219_MIC_AMP_IN_SEL_SE_N;
} else {
- dev_warn(codec->dev, "Invalid mic input type selection");
+ dev_warn(dev, "Invalid mic input type selection");
return DA7219_MIC_AMP_IN_SEL_DIFF;
}
}
-static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_pdata *da7219_fw_to_pdata(struct device *dev)
{
- struct device_node *np = codec->dev->of_node;
struct da7219_pdata *pdata;
const char *of_str;
u32 of_val32;
- pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
- if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
- pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+ pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
+
+ pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
+ pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
+ if (device_property_read_string_array(dev, "clock-output-names",
+ pdata->dai_clk_names,
+ DA7219_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
+ pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
+
+ if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
+ pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
else
pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
- if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
- pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+ if (!device_property_read_string(dev, "dlg,mic-amp-in-sel", &of_str))
+ pdata->mic_amp_in_sel = da7219_fw_mic_amp_in_sel(dev, of_str);
else
pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
@@ -1492,43 +1803,49 @@ static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
* Codec driver functions
*/
-static int da7219_set_bias_level(struct snd_soc_codec *codec,
+static int da7219_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
break;
- case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- /* MCLK */
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
if (da7219->mclk) {
ret = clk_prepare_enable(da7219->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable mclk\n");
return ret;
}
}
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
/* Master bias */
- snd_soc_update_bits(codec, DA7219_REFERENCES,
+ snd_soc_component_update_bits(component, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK,
DA7219_BIAS_EN_MASK);
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) {
+ /* Remove MCLK */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
}
break;
case SND_SOC_BIAS_OFF:
- /* Only disable master bias if jack detection not active */
- if (!da7219->aad->jack)
- snd_soc_update_bits(codec, DA7219_REFERENCES,
+ /* Only disable master bias if we're not a wake-up source */
+ if (!da7219->wakeup_source)
+ snd_soc_component_update_bits(component, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK, 0);
- /* MCLK */
- if (da7219->mclk)
- clk_disable_unprepare(da7219->mclk);
break;
}
@@ -1541,240 +1858,466 @@ static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
[DA7219_SUPPLY_VDDIO] = "VDDIO",
};
-static int da7219_handle_supplies(struct snd_soc_codec *codec)
+static int da7219_handle_supplies(struct snd_soc_component *component,
+ u8 *io_voltage_lvl)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct regulator *vddio;
- u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
int i, ret;
/* Get required supplies */
for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
da7219->supplies[i].supply = da7219_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES,
- da7219->supplies);
+ ret = regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES,
+ da7219->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to get supplies");
+ dev_err(component->dev, "Failed to get supplies");
return ret;
}
+ /* Default to upper range */
+ *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+
/* Determine VDDIO voltage provided */
vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
ret = regulator_get_voltage(vddio);
if (ret < 1200000)
- dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ dev_warn(component->dev, "Invalid VDDIO voltage\n");
else if (ret < 2800000)
- io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+ *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
/* Enable main supplies */
ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to enable supplies");
+ dev_err(component->dev, "Failed to enable supplies");
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
return ret;
}
- /* Ensure device in active mode */
- snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
+ return 0;
+}
- /* Update IO voltage level range */
- snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl);
+#ifdef CONFIG_COMMON_CLK
+static int da7219_wclk_prepare(struct clk_hw *hw)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
return 0;
}
-static void da7219_handle_pdata(struct snd_soc_codec *codec)
+static void da7219_wclk_unprepare(struct clk_hw *hw)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_pdata *pdata = da7219->pdata;
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
- if (pdata) {
- u8 micbias_lvl = 0;
+ if (!da7219->master)
+ return;
- /* Mic Bias voltages */
- switch (pdata->micbias_lvl) {
- case DA7219_MICBIAS_1_6V:
- case DA7219_MICBIAS_1_8V:
- case DA7219_MICBIAS_2_0V:
- case DA7219_MICBIAS_2_2V:
- case DA7219_MICBIAS_2_4V:
- case DA7219_MICBIAS_2_6V:
- micbias_lvl |= (pdata->micbias_lvl <<
- DA7219_MICBIAS1_LEVEL_SHIFT);
- break;
- }
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK, 0);
+}
- snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl);
+static int da7219_wclk_is_prepared(struct clk_hw *hw)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 clk_reg;
- /* Mic */
- switch (pdata->mic_amp_in_sel) {
- case DA7219_MIC_AMP_IN_SEL_DIFF:
- case DA7219_MIC_AMP_IN_SEL_SE_P:
- case DA7219_MIC_AMP_IN_SEL_SE_N:
- snd_soc_write(codec, DA7219_MIC_1_SELECT,
- pdata->mic_amp_in_sel);
- break;
- }
- }
+ if (!da7219->master)
+ return -EINVAL;
+
+ clk_reg = snd_soc_component_read(component, DA7219_DAI_CLK_MODE);
+
+ return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
}
-static struct reg_sequence da7219_rev_aa_patch[] = {
- { DA7219_REFERENCES, 0x08 },
-};
+static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 fs = snd_soc_component_read(component, DA7219_SR);
+
+ switch (fs & DA7219_SR_MASK) {
+ case DA7219_SR_8000:
+ return 8000;
+ case DA7219_SR_11025:
+ return 11025;
+ case DA7219_SR_12000:
+ return 12000;
+ case DA7219_SR_16000:
+ return 16000;
+ case DA7219_SR_22050:
+ return 22050;
+ case DA7219_SR_24000:
+ return 24000;
+ case DA7219_SR_32000:
+ return 32000;
+ case DA7219_SR_44100:
+ return 44100;
+ case DA7219_SR_48000:
+ return 48000;
+ case DA7219_SR_88200:
+ return 88200;
+ case DA7219_SR_96000:
+ return 96000;
+ default:
+ return 0;
+ }
+}
-static int da7219_probe(struct snd_soc_codec *codec)
+static int da7219_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- unsigned int rev;
- int ret;
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
- mutex_init(&da7219->lock);
+ if (!da7219->master)
+ return -EINVAL;
- /* Regulator configuration */
- ret = da7219_handle_supplies(codec);
- if (ret)
- return ret;
+ if (req->rate < 11025)
+ req->rate = 8000;
+ else if (req->rate < 12000)
+ req->rate = 11025;
+ else if (req->rate < 16000)
+ req->rate = 12000;
+ else if (req->rate < 22050)
+ req->rate = 16000;
+ else if (req->rate < 24000)
+ req->rate = 22050;
+ else if (req->rate < 32000)
+ req->rate = 24000;
+ else if (req->rate < 44100)
+ req->rate = 32000;
+ else if (req->rate < 48000)
+ req->rate = 44100;
+ else if (req->rate < 88200)
+ req->rate = 48000;
+ else if (req->rate < 96000)
+ req->rate = 88200;
+ else
+ req->rate = 96000;
- ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
- if (ret) {
- dev_err(codec->dev, "Failed to read chip revision: %d\n", ret);
- goto err_disable_reg;
- }
+ return 0;
+}
- switch (rev & DA7219_CHIP_MINOR_MASK) {
- case 0:
- ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
- ARRAY_SIZE(da7219_rev_aa_patch));
- if (ret) {
- dev_err(codec->dev, "Failed to register AA patch: %d\n",
- ret);
- goto err_disable_reg;
- }
- break;
+static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ return da7219_set_sr(component, rate);
+}
+
+static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 bclks_per_wclk = snd_soc_component_read(component,
+ DA7219_DAI_CLK_MODE);
+
+ switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
+ case DA7219_DAI_BCLKS_PER_WCLK_32:
+ return parent_rate * 32;
+ case DA7219_DAI_BCLKS_PER_WCLK_64:
+ return parent_rate * 64;
+ case DA7219_DAI_BCLKS_PER_WCLK_128:
+ return parent_rate * 128;
+ case DA7219_DAI_BCLKS_PER_WCLK_256:
+ return parent_rate * 256;
default:
- break;
+ return 0;
}
+}
- /* Handle DT/Platform data */
- if (codec->dev->of_node)
- da7219->pdata = da7219_of_to_pdata(codec);
+static unsigned long da7219_bclk_get_factor(unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long factor;
+
+ factor = rate / parent_rate;
+ if (factor < 64)
+ return 32;
+ else if (factor < 128)
+ return 64;
+ else if (factor < 256)
+ return 128;
else
- da7219->pdata = dev_get_platdata(codec->dev);
+ return 256;
+}
- da7219_handle_pdata(codec);
+static int da7219_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ unsigned long factor;
- /* Check if MCLK provided */
- da7219->mclk = devm_clk_get(codec->dev, "mclk");
- if (IS_ERR(da7219->mclk)) {
- if (PTR_ERR(da7219->mclk) != -ENOENT) {
- ret = PTR_ERR(da7219->mclk);
- goto err_disable_reg;
- } else {
- da7219->mclk = NULL;
- }
- }
+ if (!req->best_parent_rate || !da7219->master)
+ return -EINVAL;
- /* Default PC counter to free-running */
- snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
- DA7219_PC_FREERUN_MASK);
+ /*
+ * We don't allow changing the parent rate as some BCLK rates can be
+ * derived from multiple parent WCLK rates (BCLK rates are set as a
+ * multiplier of WCLK in HW). We just do some rounding down based on the
+ * parent WCLK rate set and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = da7219_bclk_get_factor(req->rate, req->best_parent_rate);
- /* Default gain ramping */
- snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
- DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
- DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
- DA7219_ADC_L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
- DA7219_DAC_L_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
- DA7219_DAC_R_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_RAMP_EN_MASK,
- DA7219_HP_L_AMP_RAMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_RAMP_EN_MASK,
- DA7219_HP_R_AMP_RAMP_EN_MASK);
+ req->rate = req->best_parent_rate * factor;
- /* Default infinite tone gen, start/stop by Kcontrol */
- snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+ return 0;
+}
- /* Initialise AAD block */
- ret = da7219_aad_init(codec);
- if (ret)
- goto err_disable_reg;
+static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ unsigned long factor;
- return 0;
+ if (!da7219->master)
+ return -EINVAL;
-err_disable_reg:
- regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ factor = da7219_bclk_get_factor(rate, parent_rate);
- return ret;
+ return da7219_set_bclks_per_wclk(component, factor);
}
-static int da7219_remove(struct snd_soc_codec *codec)
+static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
+ [DA7219_DAI_WCLK_IDX] = {
+ .prepare = da7219_wclk_prepare,
+ .unprepare = da7219_wclk_unprepare,
+ .is_prepared = da7219_wclk_is_prepared,
+ .recalc_rate = da7219_wclk_recalc_rate,
+ .determine_rate = da7219_wclk_determine_rate,
+ .set_rate = da7219_wclk_set_rate,
+ },
+ [DA7219_DAI_BCLK_IDX] = {
+ .recalc_rate = da7219_bclk_recalc_rate,
+ .determine_rate = da7219_bclk_determine_rate,
+ .set_rate = da7219_bclk_set_rate,
+ },
+};
+
+static int da7219_register_dai_clks(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct device *dev = component->dev;
+ struct device_node *np = dev->of_node;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_pdata *pdata = da7219->pdata;
+ const char *parent_name;
+ struct clk_hw_onecell_data *clk_data;
+ int i, ret;
- da7219_aad_exit(codec);
+ /* For DT platforms allocate onecell data for clock registration */
+ if (np) {
+ clk_data = kzalloc(struct_size(clk_data, hws, DA7219_DAI_NUM_CLKS),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
- /* Supplies */
- return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
-}
+ clk_data->num = DA7219_DAI_NUM_CLKS;
+ da7219->clk_hw_data = clk_data;
+ }
-#ifdef CONFIG_PM
-static int da7219_suspend(struct snd_soc_codec *codec)
-{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = {};
+ struct clk_lookup *dai_clk_lookup;
+ struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
+
+ switch (i) {
+ case DA7219_DAI_WCLK_IDX:
+ /*
+ * If we can, make MCLK the parent of WCLK to ensure
+ * it's enabled as required.
+ */
+ if (da7219->mclk) {
+ parent_name = __clk_get_name(da7219->mclk);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ } else {
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ }
+ break;
+ case DA7219_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &da7219_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ ret = clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n", init.name,
+ ret);
+ goto err;
+ }
+ da7219->dai_clks[i] = dai_clk_hw->clk;
+
+ /* For DT setup onecell data, otherwise create lookup */
+ if (np) {
+ da7219->clk_hw_data->hws[i] = dai_clk_hw;
+ } else {
+ dai_clk_lookup = clkdev_hw_create(dai_clk_hw, init.name,
+ "%s", dev_name(dev));
+ if (!dai_clk_lookup) {
+ clk_hw_unregister(dai_clk_hw);
+ ret = -ENOMEM;
+ goto err;
+ } else {
+ da7219->dai_clks_lookup[i] = dai_clk_lookup;
+ }
+ }
+ }
- /* Put device into standby mode if jack detection disabled */
- if (!da7219->aad->jack)
- snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0);
+ /* If we're using DT, then register as provider accordingly */
+ if (np) {
+ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ da7219->clk_hw_data);
+ if (ret) {
+ dev_err(dev, "Failed to register clock provider\n");
+ goto err;
+ }
+ }
return 0;
+
+err:
+ while (--i >= 0) {
+ if (da7219->dai_clks_lookup[i])
+ clkdev_drop(da7219->dai_clks_lookup[i]);
+
+ clk_hw_unregister(&da7219->dai_clks_hw[i]);
+ }
+
+ if (np)
+ kfree(da7219->clk_hw_data);
+
+ return ret;
}
-static int da7219_resume(struct snd_soc_codec *codec)
+static void da7219_free_dai_clks(struct snd_soc_component *component)
{
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct device_node *np = component->dev->of_node;
+ int i;
- /* Put device into active mode if previously pushed to standby */
- if (!da7219->aad->jack)
- snd_soc_write(codec, DA7219_SYSTEM_ACTIVE,
- DA7219_SYSTEM_ACTIVE_MASK);
+ if (np)
+ of_clk_del_provider(np);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
+ if (da7219->dai_clks_lookup[i])
+ clkdev_drop(da7219->dai_clks_lookup[i]);
- return 0;
+ clk_hw_unregister(&da7219->dai_clks_hw[i]);
+ }
+
+ if (np)
+ kfree(da7219->clk_hw_data);
}
#else
-#define da7219_suspend NULL
-#define da7219_resume NULL
-#endif
+static inline int da7219_register_dai_clks(struct snd_soc_component *component)
+{
+ return 0;
+}
-static struct snd_soc_codec_driver soc_codec_dev_da7219 = {
- .probe = da7219_probe,
- .remove = da7219_remove,
- .suspend = da7219_suspend,
- .resume = da7219_resume,
- .set_bias_level = da7219_set_bias_level,
+static void da7219_free_dai_clks(struct snd_soc_component *component) {}
+#endif /* CONFIG_COMMON_CLK */
- .controls = da7219_snd_controls,
- .num_controls = ARRAY_SIZE(da7219_snd_controls),
+static void da7219_handle_pdata(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct da7219_pdata *pdata = da7219->pdata;
- .dapm_widgets = da7219_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
- .dapm_routes = da7219_audio_map,
- .num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
-};
+ if (pdata) {
+ u8 micbias_lvl = 0;
+
+ da7219->wakeup_source = pdata->wakeup_source;
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias_lvl) {
+ case DA7219_MICBIAS_1_6V:
+ case DA7219_MICBIAS_1_8V:
+ case DA7219_MICBIAS_2_0V:
+ case DA7219_MICBIAS_2_2V:
+ case DA7219_MICBIAS_2_4V:
+ case DA7219_MICBIAS_2_6V:
+ micbias_lvl |= (pdata->micbias_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_lvl);
+
+ /*
+ * Calculate delay required to compensate for DC offset in
+ * Mic PGA, based on Mic Bias voltage.
+ */
+ da7219->mic_pga_delay = DA7219_MIC_PGA_BASE_DELAY +
+ (pdata->micbias_lvl *
+ DA7219_MIC_PGA_OFFSET_DELAY);
+
+ /* Mic */
+ switch (pdata->mic_amp_in_sel) {
+ case DA7219_MIC_AMP_IN_SEL_DIFF:
+ case DA7219_MIC_AMP_IN_SEL_SE_P:
+ case DA7219_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_component_write(component, DA7219_MIC_1_SELECT,
+ pdata->mic_amp_in_sel);
+ break;
+ }
+ }
+}
/*
* Regmap configs
*/
-static struct reg_default da7219_reg_defaults[] = {
+static const struct reg_default da7219_reg_defaults[] = {
{ DA7219_MIC_1_SELECT, 0x00 },
{ DA7219_CIF_TIMEOUT_CTRL, 0x01 },
{ DA7219_SR_24_48, 0x00 },
@@ -1888,9 +2431,9 @@ static bool da7219_volatile_register(struct device *dev, unsigned int reg)
case DA7219_ACCDET_IRQ_EVENT_B:
case DA7219_ACCDET_CONFIG_8:
case DA7219_SYSTEM_STATUS:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1905,18 +2448,239 @@ static const struct regmap_config da7219_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct reg_sequence da7219_rev_aa_patch[] = {
+ { DA7219_REFERENCES, 0x08 },
+};
+
+static int da7219_probe(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ unsigned int system_active, system_status, rev;
+ u8 io_voltage_lvl;
+ int i, ret;
+
+ da7219->component = component;
+ mutex_init(&da7219->ctrl_lock);
+ mutex_init(&da7219->pll_lock);
+
+ /* Regulator configuration */
+ ret = da7219_handle_supplies(component, &io_voltage_lvl);
+ if (ret)
+ return ret;
+
+ regcache_cache_bypass(da7219->regmap, true);
+
+ /* Disable audio paths if still active from previous start */
+ regmap_read(da7219->regmap, DA7219_SYSTEM_ACTIVE, &system_active);
+ if (system_active) {
+ regmap_write(da7219->regmap, DA7219_GAIN_RAMP_CTRL,
+ DA7219_GAIN_RAMP_RATE_NOMINAL);
+ regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_INPUT, 0x00);
+ regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_OUTPUT, 0x01);
+
+ for (i = 0; i < DA7219_SYS_STAT_CHECK_RETRIES; ++i) {
+ regmap_read(da7219->regmap, DA7219_SYSTEM_STATUS,
+ &system_status);
+ if (!system_status)
+ break;
+
+ msleep(DA7219_SYS_STAT_CHECK_DELAY);
+ }
+ }
+
+ /* Soft reset component */
+ regmap_write_bits(da7219->regmap, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK, 0);
+ regmap_write_bits(da7219->regmap, DA7219_CIF_CTRL,
+ DA7219_CIF_REG_SOFT_RESET_MASK,
+ DA7219_CIF_REG_SOFT_RESET_MASK);
+ regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK, 0);
+ regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK, 1);
+
+ regcache_cache_bypass(da7219->regmap, false);
+ regmap_reinit_cache(da7219->regmap, &da7219_regmap_config);
+
+ /* Update IO voltage level range based on supply level */
+ snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl);
+
+ ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
+ if (ret) {
+ dev_err(component->dev, "Failed to read chip revision: %d\n", ret);
+ goto err_disable_reg;
+ }
+
+ switch (rev & DA7219_CHIP_MINOR_MASK) {
+ case 0:
+ ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
+ ARRAY_SIZE(da7219_rev_aa_patch));
+ if (ret) {
+ dev_err(component->dev, "Failed to register AA patch: %d\n",
+ ret);
+ goto err_disable_reg;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Handle DT/ACPI/Platform data */
+ da7219_handle_pdata(component);
+
+ /* Check if MCLK provided */
+ da7219->mclk = clk_get(component->dev, "mclk");
+ if (IS_ERR(da7219->mclk)) {
+ if (PTR_ERR(da7219->mclk) != -ENOENT) {
+ ret = PTR_ERR(da7219->mclk);
+ goto err_disable_reg;
+ } else {
+ da7219->mclk = NULL;
+ }
+ }
+
+ /* Register CCF DAI clock control */
+ ret = da7219_register_dai_clks(component);
+ if (ret)
+ goto err_put_clk;
+
+ /* Default PC counter to free-running */
+ snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Default gain ramping */
+ snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
+ DA7219_ADC_L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
+ DA7219_DAC_L_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
+ DA7219_DAC_R_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_RAMP_EN_MASK,
+ DA7219_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_RAMP_EN_MASK,
+ DA7219_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default minimum gain on HP to avoid pops during DAPM sequencing */
+ snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
+ snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_component_write(component, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+
+ /* Initialise AAD block */
+ ret = da7219_aad_init(component);
+ if (ret)
+ goto err_free_dai_clks;
+
+ return 0;
+
+err_free_dai_clks:
+ da7219_free_dai_clks(component);
+
+err_put_clk:
+ clk_put(da7219->mclk);
+
+err_disable_reg:
+ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
+
+ return ret;
+}
+
+static void da7219_remove(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+
+ da7219_aad_exit(component);
+
+ da7219_free_dai_clks(component);
+ clk_put(da7219->mclk);
+
+ /* Supplies */
+ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7219_suspend(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ /* Suspend AAD if we're not a wake-up source */
+ if (!da7219->wakeup_source)
+ da7219_aad_suspend(component);
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int da7219_resume(struct snd_soc_component *component)
+{
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
+
+ /* Resume AAD if previously suspended */
+ if (!da7219->wakeup_source)
+ da7219_aad_resume(component);
+
+ return 0;
+}
+#else
+#define da7219_suspend NULL
+#define da7219_resume NULL
+#endif
+
+static int da7219_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack,
+ void *data)
+{
+ da7219_aad_jack_det(component, jack);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_da7219 = {
+ .probe = da7219_probe,
+ .remove = da7219_remove,
+ .suspend = da7219_suspend,
+ .resume = da7219_resume,
+ .set_jack = da7219_set_jack,
+ .set_bias_level = da7219_set_bias_level,
+ .controls = da7219_snd_controls,
+ .num_controls = ARRAY_SIZE(da7219_snd_controls),
+ .dapm_widgets = da7219_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
+ .dapm_routes = da7219_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
/*
* I2C layer
*/
-static int da7219_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7219_i2c_probe(struct i2c_client *i2c)
{
+ struct device *dev = &i2c->dev;
struct da7219_priv *da7219;
int ret;
- da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
+ da7219 = devm_kzalloc(dev, sizeof(struct da7219_priv),
GFP_KERNEL);
if (!da7219)
return -ENOMEM;
@@ -1926,25 +2690,28 @@ static int da7219_i2c_probe(struct i2c_client *i2c,
da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
if (IS_ERR(da7219->regmap)) {
ret = PTR_ERR(da7219->regmap);
- dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
- &da7219_dai, 1);
+ /* Retrieve DT/ACPI/Platform data */
+ da7219->pdata = dev_get_platdata(dev);
+ if (!da7219->pdata)
+ da7219->pdata = da7219_fw_to_pdata(dev);
+
+ /* AAD */
+ ret = da7219_aad_probe(i2c);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_da7219,
+ &da7219_dai, 1);
if (ret < 0) {
- dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n",
- ret);
+ dev_err(dev, "Failed to register da7219 component: %d\n", ret);
}
return ret;
}
-static int da7219_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id da7219_i2c_id[] = {
{ "da7219", },
{ }
@@ -1955,9 +2722,9 @@ static struct i2c_driver da7219_i2c_driver = {
.driver = {
.name = "da7219",
.of_match_table = of_match_ptr(da7219_of_match),
+ .acpi_match_table = ACPI_PTR(da7219_acpi_match),
},
.probe = da7219_i2c_probe,
- .remove = da7219_i2c_remove,
.id_table = da7219_i2c_id,
};
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index 5a787e738084..94af88f52589 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -1,19 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* da7219.h - DA7219 ALSA SoC Codec Driver
*
* Copyright (c) 2015 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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 __DA7219_H
#define __DA7219_H
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/da7219.h>
@@ -194,11 +193,11 @@
/* DA7219_PLL_CTRL = 0x20 */
#define DA7219_PLL_INDIV_SHIFT 2
#define DA7219_PLL_INDIV_MASK (0x7 << 2)
-#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2)
-#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2)
-#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2)
-#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2)
-#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2)
+#define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2)
+#define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2)
+#define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2)
+#define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 2)
+#define DA7219_PLL_INDIV_36_TO_54_MHZ (0x4 << 2)
#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5
#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5)
#define DA7219_PLL_MODE_SHIFT 6
@@ -224,6 +223,7 @@
#define DA7219_PLL_SRM_STATE_MASK (0xF << 0)
#define DA7219_PLL_SRM_STATUS_SHIFT 4
#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4)
+#define DA7219_PLL_SRM_STS_MCLK (0x1 << 4)
#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7)
/* DA7219_DIG_ROUTING_DAI = 0x2A */
@@ -576,6 +576,8 @@
/* DA7219_GAIN_RAMP_CTRL = 0x92 */
#define DA7219_GAIN_RAMP_RATE_SHIFT 0
#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7219_GAIN_RAMP_RATE_X8 (0x0 << 0)
+#define DA7219_GAIN_RAMP_RATE_NOMINAL (0x1 << 0)
#define DA7219_GAIN_RAMP_RATE_MAX 4
/* DA7219_PC_COUNT = 0x94 */
@@ -761,15 +763,25 @@
#define DA7219_PLL_FREQ_OUT_98304 98304000
/* PLL Frequency Dividers */
-#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1
-#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2
-#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
-#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
-#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
+#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
+#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
+#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4
+#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8
+#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16
/* SRM */
#define DA7219_SRM_CHECK_RETRIES 8
+/* System Controller */
+#define DA7219_SYS_STAT_CHECK_RETRIES 6
+#define DA7219_SYS_STAT_CHECK_DELAY 50
+
+/* Power up/down Delays */
+#define DA7219_SETTLING_DELAY 40
+#define DA7219_MIN_GAIN_DELAY 30
+#define DA7219_MIC_PGA_BASE_DELAY 100
+#define DA7219_MIC_PGA_OFFSET_DELAY 40
+
enum da7219_clk_src {
DA7219_CLKSRC_MCLK = 0,
DA7219_CLKSRC_MCLK_SQR,
@@ -793,19 +805,35 @@ struct da7219_aad_priv;
/* Private data */
struct da7219_priv {
+ struct snd_soc_component *component;
struct da7219_aad_priv *aad;
struct da7219_pdata *pdata;
+ bool wakeup_source;
struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES];
struct regmap *regmap;
- struct mutex lock;
+ struct mutex ctrl_lock;
+ struct mutex pll_lock;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
+ struct clk_hw_onecell_data *clk_hw_data;
+#endif
+ struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
+ struct clk *dai_clks[DA7219_DAI_NUM_CLKS];
struct clk *mclk;
unsigned int mclk_rate;
int clk_src;
bool master;
+ bool tdm_en;
bool alc_en;
+ bool micbias_on_event;
+ unsigned int mic_pga_delay;
+ u8 gain_ramp_ctrl;
};
+int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout);
+
#endif /* __DA7219_H */
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 1d5a89c5164b..140e449d3ef4 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* da732x.c --- Dialog DA732X ALSA SoC Audio Driver
*
* Copyright (C) 2012 Dialog Semiconductor GmbH
*
* Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -168,52 +165,47 @@ static const struct reg_default da732x_reg_cache[] = {
{ DA732X_REG_UNLOCK , 0x00 },
};
-static inline int da732x_get_input_div(struct snd_soc_codec *codec, int sysclk)
+static inline int da732x_get_input_div(struct snd_soc_component *component, int sysclk)
{
int val;
- int ret;
if (sysclk < DA732X_MCLK_10MHZ) {
- val = DA732X_MCLK_RET_0_10MHZ;
- ret = DA732X_MCLK_VAL_0_10MHZ;
+ val = DA732X_MCLK_VAL_0_10MHZ;
} else if ((sysclk >= DA732X_MCLK_10MHZ) &&
(sysclk < DA732X_MCLK_20MHZ)) {
- val = DA732X_MCLK_RET_10_20MHZ;
- ret = DA732X_MCLK_VAL_10_20MHZ;
+ val = DA732X_MCLK_VAL_10_20MHZ;
} else if ((sysclk >= DA732X_MCLK_20MHZ) &&
(sysclk < DA732X_MCLK_40MHZ)) {
- val = DA732X_MCLK_RET_20_40MHZ;
- ret = DA732X_MCLK_VAL_20_40MHZ;
+ val = DA732X_MCLK_VAL_20_40MHZ;
} else if ((sysclk >= DA732X_MCLK_40MHZ) &&
(sysclk <= DA732X_MCLK_54MHZ)) {
- val = DA732X_MCLK_RET_40_54MHZ;
- ret = DA732X_MCLK_VAL_40_54MHZ;
+ val = DA732X_MCLK_VAL_40_54MHZ;
} else {
return -EINVAL;
}
- snd_soc_write(codec, DA732X_REG_PLL_CTRL, val);
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL, val);
- return ret;
+ return val;
}
-static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state)
+static void da732x_set_charge_pump(struct snd_soc_component *component, int state)
{
switch (state) {
case DA732X_ENABLE_CP:
- snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN);
- snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_EN |
+ snd_soc_component_write(component, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN);
+ snd_soc_component_write(component, DA732X_REG_CP_HP2, DA732X_HP_CP_EN |
DA732X_HP_CP_REG | DA732X_HP_CP_PULSESKIP);
- snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
DA732X_CP_CTRL_CPVDD1);
- snd_soc_write(codec, DA732X_REG_CP_CTRL2,
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL2,
DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST);
- snd_soc_write(codec, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
break;
case DA732X_DISABLE_CP:
- snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS);
- snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS);
- snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
+ snd_soc_component_write(component, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS);
+ snd_soc_component_write(component, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
break;
default:
pr_err("Wrong charge pump state\n");
@@ -331,10 +323,10 @@ static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
unsigned int reg = enum_ctrl->reg;
- unsigned int sel = ucontrol->value.integer.value[0];
+ unsigned int sel = ucontrol->value.enumerated.item[0];
unsigned int bits;
switch (sel) {
@@ -351,7 +343,7 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- snd_soc_update_bits(codec, reg, DA732X_HPF_MASK, bits);
+ snd_soc_component_update_bits(component, reg, DA732X_HPF_MASK, bits);
return 0;
}
@@ -359,22 +351,22 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
unsigned int reg = enum_ctrl->reg;
int val;
- val = snd_soc_read(codec, reg) & DA732X_HPF_MASK;
+ val = snd_soc_component_read(component, reg) & DA732X_HPF_MASK;
switch (val) {
case DA732X_HPF_VOICE_EN:
- ucontrol->value.integer.value[0] = DA732X_HPF_VOICE;
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_VOICE;
break;
case DA732X_HPF_MUSIC_EN:
- ucontrol->value.integer.value[0] = DA732X_HPF_MUSIC;
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_MUSIC;
break;
default:
- ucontrol->value.integer.value[0] = DA732X_HPF_DISABLED;
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_DISABLED;
break;
}
@@ -609,18 +601,18 @@ static const struct snd_kcontrol_new da732x_snd_controls[] = {
static int da732x_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
switch (w->reg) {
case DA732X_REG_ADC1_PD:
- snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCA_BB_CLK_EN,
DA732X_ADCA_BB_CLK_EN);
break;
case DA732X_REG_ADC2_PD:
- snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCC_BB_CLK_EN,
DA732X_ADCC_BB_CLK_EN);
break;
@@ -628,24 +620,24 @@ static int da732x_adc_event(struct snd_soc_dapm_widget *w,
return -EINVAL;
}
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK,
DA732X_ADC_SET_ACT);
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
DA732X_ADC_ON);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
DA732X_ADC_OFF);
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK,
DA732X_ADC_SET_RST);
switch (w->reg) {
case DA732X_REG_ADC1_PD:
- snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCA_BB_CLK_EN, 0);
break;
case DA732X_REG_ADC2_PD:
- snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCC_BB_CLK_EN, 0);
break;
default:
@@ -663,16 +655,16 @@ static int da732x_adc_event(struct snd_soc_dapm_widget *w,
static int da732x_out_pga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift) | DA732X_OUT_HIZ_EN,
(1 << w->shift) | DA732X_OUT_HIZ_EN);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift) | DA732X_OUT_HIZ_EN,
(1 << w->shift) | DA732X_OUT_HIZ_DIS);
break;
@@ -949,7 +941,7 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u32 aif = 0;
u32 reg_aif;
u32 fs;
@@ -1011,15 +1003,15 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, reg_aif, DA732X_AIF_WORD_MASK, aif);
- snd_soc_update_bits(codec, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs);
+ snd_soc_component_update_bits(component, reg_aif, DA732X_AIF_WORD_MASK, aif);
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs);
return 0;
}
static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u32 aif_mclk, pc_count;
u32 reg_aif1, aif1;
u32 reg_aif3, aif3;
@@ -1042,11 +1034,11 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
aif1 = DA732X_AIF_SLAVE;
aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif1 = DA732X_AIF_CLK_FROM_SRC;
aif_mclk = DA732X_CLK_GENERATION_AIF_A;
break;
@@ -1107,29 +1099,29 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
return -EINVAL;
}
- snd_soc_write(codec, DA732X_REG_AIF_MCLK, aif_mclk);
- snd_soc_update_bits(codec, reg_aif1, DA732X_AIF1_CLK_MASK, aif1);
- snd_soc_update_bits(codec, reg_aif3, DA732X_AIF_BCLK_INV |
+ snd_soc_component_write(component, DA732X_REG_AIF_MCLK, aif_mclk);
+ snd_soc_component_update_bits(component, reg_aif1, DA732X_AIF1_CLK_MASK, aif1);
+ snd_soc_component_update_bits(component, reg_aif3, DA732X_AIF_BCLK_INV |
DA732X_AIF_WCLK_INV | DA732X_AIF_MODE_MASK, aif3);
- snd_soc_write(codec, DA732X_REG_PC_CTRL, pc_count);
+ snd_soc_component_write(component, DA732X_REG_PC_CTRL, pc_count);
return 0;
}
-static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
+static int da732x_set_dai_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
int fref, indiv;
u8 div_lo, div_mid, div_hi;
u64 frac_div;
/* Disable PLL */
if (freq_out == 0) {
- snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_EN, 0);
da732x->pll_en = false;
return 0;
@@ -1147,32 +1139,32 @@ static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
case 24576000:
case 45160000:
case 49152000:
- snd_soc_write(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_BYPASS);
return 0;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Cannot use PLL Bypass, invalid SYSCLK rate\n");
return -EINVAL;
}
}
- indiv = da732x_get_input_div(codec, da732x->sysclk);
+ indiv = da732x_get_input_div(component, da732x->sysclk);
if (indiv < 0)
return indiv;
- fref = (da732x->sysclk / indiv);
+ fref = da732x->sysclk / BIT(indiv);
div_hi = freq_out / fref;
frac_div = (u64)(freq_out % fref) * 8192ULL;
do_div(frac_div, fref);
div_mid = (frac_div >> DA732X_1BYTE_SHIFT) & DA732X_U8_MASK;
div_lo = (frac_div) & DA732X_U8_MASK;
- snd_soc_write(codec, DA732X_REG_PLL_DIV_LO, div_lo);
- snd_soc_write(codec, DA732X_REG_PLL_DIV_MID, div_mid);
- snd_soc_write(codec, DA732X_REG_PLL_DIV_HI, div_hi);
+ snd_soc_component_write(component, DA732X_REG_PLL_DIV_LO, div_lo);
+ snd_soc_component_write(component, DA732X_REG_PLL_DIV_MID, div_mid);
+ snd_soc_component_write(component, DA732X_REG_PLL_DIV_HI, div_hi);
- snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
DA732X_PLL_EN);
da732x->pll_en = true;
@@ -1183,8 +1175,8 @@ static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
da732x->sysclk = freq;
@@ -1268,21 +1260,21 @@ static const struct regmap_config da732x_regmap = {
};
-static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
+static void da732x_dac_offset_adjust(struct snd_soc_component *component)
{
u8 offset[DA732X_HP_DACS];
u8 sign[DA732X_HP_DACS];
u8 step = DA732X_DAC_OFFSET_STEP;
/* Initialize DAC offset calibration circuits and registers */
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
DA732X_HP_DAC_OFFSET_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
DA732X_HP_DAC_OFFSET_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_CALIBRATION |
DA732X_HP_DAC_OFF_SCALE_STEPS);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_CALIBRATION |
DA732X_HP_DAC_OFF_SCALE_STEPS);
@@ -1290,9 +1282,9 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
msleep(DA732X_WAIT_FOR_STABILIZATION);
/* Check DAC offset sign */
- sign[DA732X_HPL_DAC] = (snd_soc_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ sign[DA732X_HPL_DAC] = (snd_soc_component_read(component, DA732X_REG_HPL_DAC_OFF_CNTL) &
DA732X_HP_DAC_OFF_CNTL_COMPO);
- sign[DA732X_HPR_DAC] = (snd_soc_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ sign[DA732X_HPR_DAC] = (snd_soc_component_read(component, DA732X_REG_HPR_DAC_OFF_CNTL) &
DA732X_HP_DAC_OFF_CNTL_COMPO);
/* Binary search DAC offset values (both channels at once) */
@@ -1302,17 +1294,17 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
do {
offset[DA732X_HPL_DAC] |= step;
offset[DA732X_HPR_DAC] |= step;
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK);
msleep(DA732X_WAIT_FOR_STABILIZATION);
- if ((snd_soc_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ if ((snd_soc_component_read(component, DA732X_REG_HPL_DAC_OFF_CNTL) &
DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPL_DAC])
offset[DA732X_HPL_DAC] &= ~step;
- if ((snd_soc_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ if ((snd_soc_component_read(component, DA732X_REG_HPR_DAC_OFF_CNTL) &
DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPR_DAC])
offset[DA732X_HPR_DAC] &= ~step;
@@ -1320,19 +1312,19 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
} while (step);
/* Write final DAC offsets to registers */
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK);
/* End DAC calibration mode */
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_SCALE_STEPS);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_SCALE_STEPS);
}
-static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
+static void da732x_output_offset_adjust(struct snd_soc_component *component)
{
u8 offset[DA732X_HP_AMPS];
u8 sign[DA732X_HP_AMPS];
@@ -1342,26 +1334,26 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL;
/* Initialize output offset calibration circuits and registers */
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPL,
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
+ snd_soc_component_write(component, DA732X_REG_HPL,
DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
- snd_soc_write(codec, DA732X_REG_HPR,
+ snd_soc_component_write(component, DA732X_REG_HPR,
DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
/* Wait for voltage stabilization */
msleep(DA732X_WAIT_FOR_STABILIZATION);
/* Check output offset sign */
- sign[DA732X_HPL_AMP] = snd_soc_read(codec, DA732X_REG_HPL) &
+ sign[DA732X_HPL_AMP] = snd_soc_component_read(component, DA732X_REG_HPL) &
DA732X_HP_OUT_COMPO;
- sign[DA732X_HPR_AMP] = snd_soc_read(codec, DA732X_REG_HPR) &
+ sign[DA732X_HPR_AMP] = snd_soc_component_read(component, DA732X_REG_HPR) &
DA732X_HP_OUT_COMPO;
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_COMP |
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_COMP |
(sign[DA732X_HPL_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
DA732X_HP_OUT_EN);
- snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_COMP |
+ snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_OUT_COMP |
(sign[DA732X_HPR_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
DA732X_HP_OUT_EN);
@@ -1369,17 +1361,17 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
do {
offset[DA732X_HPL_AMP] |= step;
offset[DA732X_HPR_AMP] |= step;
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET,
offset[DA732X_HPL_AMP]);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET,
offset[DA732X_HPR_AMP]);
msleep(DA732X_WAIT_FOR_STABILIZATION);
- if ((snd_soc_read(codec, DA732X_REG_HPL) &
+ if ((snd_soc_component_read(component, DA732X_REG_HPL) &
DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPL_AMP])
offset[DA732X_HPL_AMP] &= ~step;
- if ((snd_soc_read(codec, DA732X_REG_HPR) &
+ if ((snd_soc_component_read(component, DA732X_REG_HPR) &
DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP])
offset[DA732X_HPR_AMP] &= ~step;
@@ -1387,80 +1379,81 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
} while (step);
/* Write final DAC offsets to registers */
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
}
-static void da732x_hp_dc_offset_cancellation(struct snd_soc_codec *codec)
+static void da732x_hp_dc_offset_cancellation(struct snd_soc_component *component)
{
/* Make sure that we have Soft Mute enabled */
- snd_soc_write(codec, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
+ snd_soc_component_write(component, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
DA732X_GAIN_RAMPED | DA732X_16_SAMPLES);
- snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACL_EN |
+ snd_soc_component_write(component, DA732X_REG_DAC1_SEL, DA732X_DACL_EN |
DA732X_DACR_EN | DA732X_DACL_SDM | DA732X_DACR_SDM |
DA732X_DACL_MUTE | DA732X_DACR_MUTE);
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN);
- snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_EN |
+ snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_OUT_EN |
DA732X_HP_OUT_MUTE | DA732X_HP_OUT_DAC_EN);
- da732x_dac_offset_adjust(codec);
- da732x_output_offset_adjust(codec);
+ da732x_dac_offset_adjust(component);
+ da732x_output_offset_adjust(component);
- snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS);
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_DIS);
- snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_DIS);
+ snd_soc_component_write(component, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS);
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_DIS);
+ snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_DIS);
}
-static int da732x_set_bias_level(struct snd_soc_codec *codec,
+static int da732x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN,
DA732X_BIAS_BOOST_MASK,
DA732X_BIAS_BOOST_100PC);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Init Codec */
- snd_soc_write(codec, DA732X_REG_REF1,
+ snd_soc_component_write(component, DA732X_REG_REF1,
DA732X_VMID_FASTCHG);
- snd_soc_write(codec, DA732X_REG_BIAS_EN,
+ snd_soc_component_write(component, DA732X_REG_BIAS_EN,
DA732X_BIAS_EN);
mdelay(DA732X_STARTUP_DELAY);
/* Disable Fast Charge and enable DAC ref voltage */
- snd_soc_write(codec, DA732X_REG_REF1,
+ snd_soc_component_write(component, DA732X_REG_REF1,
DA732X_REFBUFX2_EN);
/* Enable bypass DSP routing */
- snd_soc_write(codec, DA732X_REG_DATA_ROUTE,
+ snd_soc_component_write(component, DA732X_REG_DATA_ROUTE,
DA732X_BYPASS_DSP);
/* Enable Digital subsystem */
- snd_soc_write(codec, DA732X_REG_DSP_CTRL,
+ snd_soc_component_write(component, DA732X_REG_DSP_CTRL,
DA732X_DIGITAL_EN);
- snd_soc_write(codec, DA732X_REG_SPARE1_OUT,
+ snd_soc_component_write(component, DA732X_REG_SPARE1_OUT,
DA732X_HP_DRIVER_EN |
DA732X_HP_GATE_LOW |
DA732X_HP_LOOP_GAIN_CTRL);
- snd_soc_write(codec, DA732X_REG_HP_LIN1_GNDSEL,
+ snd_soc_component_write(component, DA732X_REG_HP_LIN1_GNDSEL,
DA732X_HP_OUT_GNDSEL);
- da732x_set_charge_pump(codec, DA732X_ENABLE_CP);
+ da732x_set_charge_pump(component, DA732X_ENABLE_CP);
- snd_soc_write(codec, DA732X_REG_CLK_EN1,
+ snd_soc_component_write(component, DA732X_REG_CLK_EN1,
DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN);
/* Enable Zero Crossing */
- snd_soc_write(codec, DA732X_REG_INP_ZC_EN,
+ snd_soc_component_write(component, DA732X_REG_INP_ZC_EN,
DA732X_MIC1_PRE_ZC_EN |
DA732X_MIC1_ZC_EN |
DA732X_MIC2_PRE_ZC_EN |
@@ -1469,28 +1462,28 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
DA732X_AUXR_ZC_EN |
DA732X_MIC3_PRE_ZC_EN |
DA732X_MIC3_ZC_EN);
- snd_soc_write(codec, DA732X_REG_OUT_ZC_EN,
+ snd_soc_component_write(component, DA732X_REG_OUT_ZC_EN,
DA732X_HPL_ZC_EN | DA732X_HPR_ZC_EN |
DA732X_LIN2_ZC_EN | DA732X_LIN3_ZC_EN |
DA732X_LIN4_ZC_EN);
- da732x_hp_dc_offset_cancellation(codec);
+ da732x_hp_dc_offset_cancellation(component);
regcache_cache_only(da732x->regmap, false);
regcache_sync(da732x->regmap);
} else {
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN,
DA732X_BIAS_BOOST_MASK,
DA732X_BIAS_BOOST_50PC);
- snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_EN, 0);
da732x->pll_en = false;
}
break;
case SND_SOC_BIAS_OFF:
regcache_cache_only(da732x->regmap, true);
- da732x_set_charge_pump(codec, DA732X_DISABLE_CP);
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
+ da732x_set_charge_pump(component, DA732X_DISABLE_CP);
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
DA732X_BIAS_DIS);
da732x->pll_en = false;
break;
@@ -1499,7 +1492,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da732x = {
+static const struct snd_soc_component_driver soc_component_dev_da732x = {
.set_bias_level = da732x_set_bias_level,
.controls = da732x_snd_controls,
.num_controls = ARRAY_SIZE(da732x_snd_controls),
@@ -1508,10 +1501,12 @@ static struct snd_soc_codec_driver soc_codec_dev_da732x = {
.dapm_routes = da732x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes),
.set_pll = da732x_set_dai_pll,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int da732x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da732x_i2c_probe(struct i2c_client *i2c)
{
struct da732x_priv *da732x;
unsigned int reg;
@@ -1541,24 +1536,18 @@ static int da732x_i2c_probe(struct i2c_client *i2c,
(reg & DA732X_ID_MAJOR_MASK) >> 4,
(reg & DA732X_ID_MINOR_MASK));
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da732x,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da732x,
da732x_dai, ARRAY_SIZE(da732x_dai));
if (ret != 0)
- dev_err(&i2c->dev, "Failed to register codec.\n");
+ dev_err(&i2c->dev, "Failed to register component.\n");
err:
return ret;
}
-static int da732x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id da732x_i2c_id[] = {
- { "da7320", 0},
+ { "da7320"},
{ }
};
MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
@@ -1568,7 +1557,6 @@ static struct i2c_driver da732x_i2c_driver = {
.name = "da7320",
},
.probe = da732x_i2c_probe,
- .remove = da732x_i2c_remove,
.id_table = da732x_i2c_id,
};
diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h
index f586cbd30b77..c2f784c3f359 100644
--- a/sound/soc/codecs/da732x.h
+++ b/sound/soc/codecs/da732x.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* da732x.h -- Dialog DA732X ALSA SoC Audio Driver Header File
*
* Copyright (C) 2012 Dialog Semiconductor GmbH
*
* Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __DA732X_H_
@@ -51,14 +48,10 @@
#define DA732X_MCLK_20MHZ 20000000
#define DA732X_MCLK_40MHZ 40000000
#define DA732X_MCLK_54MHZ 54000000
-#define DA732X_MCLK_RET_0_10MHZ 0
-#define DA732X_MCLK_VAL_0_10MHZ 1
-#define DA732X_MCLK_RET_10_20MHZ 1
-#define DA732X_MCLK_VAL_10_20MHZ 2
-#define DA732X_MCLK_RET_20_40MHZ 2
-#define DA732X_MCLK_VAL_20_40MHZ 4
-#define DA732X_MCLK_RET_40_54MHZ 3
-#define DA732X_MCLK_VAL_40_54MHZ 8
+#define DA732X_MCLK_VAL_0_10MHZ 0
+#define DA732X_MCLK_VAL_10_20MHZ 1
+#define DA732X_MCLK_VAL_20_40MHZ 2
+#define DA732X_MCLK_VAL_40_54MHZ 3
#define DA732X_DAI_ID1 0
#define DA732X_DAI_ID2 1
#define DA732X_SRCCLK_PLL 0
diff --git a/sound/soc/codecs/da732x_reg.h b/sound/soc/codecs/da732x_reg.h
index bdd03ca4b2de..a493e0b46f5d 100644
--- a/sound/soc/codecs/da732x_reg.h
+++ b/sound/soc/codecs/da732x_reg.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* da732x_reg.h --- Dialog DA732X ALSA SoC Audio Registers Header File
*
* Copyright (C) 2012 Dialog Semiconductor GmbH
*
* Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __DA732X_REG_H_
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index 0b2ede8db978..a52276e32f2f 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA9055 ALSA Soc codec driver
*
@@ -6,11 +7,6 @@
* Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C
* Written by David Chen <david.chen@diasemi.com> and
* Ashish Chavan <ashish.chavan@kpitcummins.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/delay.h>
@@ -19,7 +15,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -451,7 +446,7 @@ static const char * const da9055_hold_time_txt[] = {
static SOC_ENUM_SINGLE_DECL(da9055_hold_time,
DA9055_ALC_CTRL3, 0, da9055_hold_time_txt);
-static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+static int da9055_get_alc_data(struct snd_soc_component *component, u8 reg_val)
{
int mid_data, top_data;
int sum = 0;
@@ -460,17 +455,17 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS;
iteration++) {
/* Select the left or right channel and capture data */
- snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val);
/* Select middle 8 bits for read back from data register */
- snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
reg_val | DA9055_ALC_DATA_MIDDLE);
- mid_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+ mid_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA);
/* Select top 8 bits for read back from data register */
- snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
reg_val | DA9055_ALC_DATA_TOP);
- top_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+ top_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA);
sum += ((mid_data << 8) | (top_data << 16));
}
@@ -481,7 +476,7 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
u8 reg_val, adc_left, adc_right, mic_left, mic_right;
int avg_left_data, avg_right_data, offset_l, offset_r;
@@ -492,31 +487,31 @@ static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
*/
/* Save current values from Mic control registers */
- mic_left = snd_soc_read(codec, DA9055_MIC_L_CTRL);
- mic_right = snd_soc_read(codec, DA9055_MIC_R_CTRL);
+ mic_left = snd_soc_component_read(component, DA9055_MIC_L_CTRL);
+ mic_right = snd_soc_component_read(component, DA9055_MIC_R_CTRL);
/* Mute Mic PGA Left and Right */
- snd_soc_update_bits(codec, DA9055_MIC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIC_L_CTRL,
DA9055_MIC_L_MUTE_EN, DA9055_MIC_L_MUTE_EN);
- snd_soc_update_bits(codec, DA9055_MIC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIC_R_CTRL,
DA9055_MIC_R_MUTE_EN, DA9055_MIC_R_MUTE_EN);
/* Save current values from ADC control registers */
- adc_left = snd_soc_read(codec, DA9055_ADC_L_CTRL);
- adc_right = snd_soc_read(codec, DA9055_ADC_R_CTRL);
+ adc_left = snd_soc_component_read(component, DA9055_ADC_L_CTRL);
+ adc_right = snd_soc_component_read(component, DA9055_ADC_R_CTRL);
/* Enable ADC Left and Right */
- snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
DA9055_ADC_L_EN, DA9055_ADC_L_EN);
- snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL,
DA9055_ADC_R_EN, DA9055_ADC_R_EN);
/* Calculate average for Left and Right data */
/* Left Data */
- avg_left_data = da9055_get_alc_data(codec,
+ avg_left_data = da9055_get_alc_data(component,
DA9055_ALC_CIC_OP_CHANNEL_LEFT);
/* Right Data */
- avg_right_data = da9055_get_alc_data(codec,
+ avg_right_data = da9055_get_alc_data(component,
DA9055_ALC_CIC_OP_CHANNEL_RIGHT);
/* Calculate DC offset */
@@ -524,22 +519,22 @@ static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
offset_r = -avg_right_data;
reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_L, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_L, reg_val);
reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_L, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_L, reg_val);
reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_R, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_R, reg_val);
reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_R, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_R, reg_val);
/* Restore original values of ADC control registers */
- snd_soc_write(codec, DA9055_ADC_L_CTRL, adc_left);
- snd_soc_write(codec, DA9055_ADC_R_CTRL, adc_right);
+ snd_soc_component_write(component, DA9055_ADC_L_CTRL, adc_left);
+ snd_soc_component_write(component, DA9055_ADC_R_CTRL, adc_right);
/* Restore original values of Mic control registers */
- snd_soc_write(codec, DA9055_MIC_L_CTRL, mic_left);
- snd_soc_write(codec, DA9055_MIC_R_CTRL, mic_right);
+ snd_soc_component_write(component, DA9055_MIC_L_CTRL, mic_left);
+ snd_soc_component_write(component, DA9055_MIC_R_CTRL, mic_right);
}
return snd_soc_put_volsw(kcontrol, ucontrol);
@@ -1041,9 +1036,9 @@ static bool da9055_volatile_register(struct device *dev,
case DA9055_HP_R_GAIN_STATUS:
case DA9055_LINE_GAIN_STATUS:
case DA9055_ALC_CIC_OP_LVL_DATA:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1052,8 +1047,8 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
u8 aif_ctrl, fs;
u32 sysclk;
@@ -1075,7 +1070,7 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
}
/* Set AIF format */
- snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
+ snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
aif_ctrl);
switch (params_rate(params)) {
@@ -1125,7 +1120,7 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
if (da9055->mclk_rate) {
/* PLL Mode, Write actual FS */
- snd_soc_write(codec, DA9055_SR, fs);
+ snd_soc_component_write(component, DA9055_SR, fs);
} else {
/*
* Non-PLL Mode
@@ -1134,24 +1129,24 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
* to derive its sys clk. As sys clk has to be 256 * Fs, we
* need to write constant sample rate i.e. 48KHz.
*/
- snd_soc_write(codec, DA9055_SR, DA9055_SR_48000);
+ snd_soc_component_write(component, DA9055_SR, DA9055_SR_48000);
}
if (da9055->mclk_rate && (da9055->mclk_rate != sysclk)) {
/* PLL Mode */
if (!da9055->master) {
/* PLL slave mode, enable PLL and also SRM */
- snd_soc_update_bits(codec, DA9055_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL,
DA9055_PLL_EN | DA9055_PLL_SRM_EN,
DA9055_PLL_EN | DA9055_PLL_SRM_EN);
} else {
/* PLL master mode, only enable PLL */
- snd_soc_update_bits(codec, DA9055_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL,
DA9055_PLL_EN, DA9055_PLL_EN);
}
} else {
/* Non PLL Mode, disable PLL */
- snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
}
return 0;
@@ -1160,17 +1155,17 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
/* Set DAI mode and Format */
static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
u8 aif_clk_mode, aif_ctrl, mode;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
/* DA9055 in I2S Master Mode */
mode = 1;
aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
/* DA9055 in I2S Slave Mode */
mode = 0;
aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE;
@@ -1180,7 +1175,7 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* Don't allow change of mode if PLL is enabled */
- if ((snd_soc_read(codec, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
+ if ((snd_soc_component_read(component, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
(da9055->master != mode))
return -EINVAL;
@@ -1207,27 +1202,27 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default only 32 BCLK per WCLK is supported */
aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32;
- snd_soc_update_bits(codec, DA9055_AIF_CLK_MODE,
+ snd_soc_component_update_bits(component, DA9055_AIF_CLK_MODE,
(DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK),
aif_clk_mode);
- snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK,
+ snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK,
aif_ctrl);
return 0;
}
-static int da9055_mute(struct snd_soc_dai *dai, int mute)
+static int da9055_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN);
} else {
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_DAC_L_MUTE_EN, 0);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_DAC_R_MUTE_EN, 0);
}
@@ -1240,8 +1235,8 @@ static int da9055_mute(struct snd_soc_dai *dai, int mute)
static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case DA9055_CLKSRC_MCLK:
@@ -1283,13 +1278,13 @@ static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int fref, unsigned int fout)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
u8 pll_frac_top, pll_frac_bot, pll_integer, cnt;
/* Disable PLL before setting the divisors */
- snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
/* In slave mode, there is only one set of divisors */
if (!da9055->master && (fout != 2822400))
@@ -1312,9 +1307,9 @@ static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
goto pll_err;
/* Write PLL dividers */
- snd_soc_write(codec, DA9055_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA9055_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA9055_PLL_INTEGER, pll_integer);
+ snd_soc_component_write(component, DA9055_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_component_write(component, DA9055_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_component_write(component, DA9055_PLL_INTEGER, pll_integer);
return 0;
pll_err:
@@ -1328,7 +1323,8 @@ static const struct snd_soc_dai_ops da9055_dai_ops = {
.set_fmt = da9055_set_dai_fmt,
.set_sysclk = da9055_set_dai_sysclk,
.set_pll = da9055_set_dai_pll,
- .digital_mute = da9055_mute,
+ .mute_stream = da9055_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da9055_dai = {
@@ -1350,59 +1346,61 @@ static struct snd_soc_dai_driver da9055_dai = {
.formats = DA9055_FORMATS,
},
.ops = &da9055_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da9055_set_bias_level(struct snd_soc_codec *codec,
+static int da9055_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
- snd_soc_update_bits(codec, DA9055_REFERENCES,
+ snd_soc_component_update_bits(component, DA9055_REFERENCES,
DA9055_VMID_EN | DA9055_BIAS_EN,
DA9055_VMID_EN | DA9055_BIAS_EN);
}
break;
case SND_SOC_BIAS_OFF:
/* Disable VMID reference & master bias */
- snd_soc_update_bits(codec, DA9055_REFERENCES,
+ snd_soc_component_update_bits(component, DA9055_REFERENCES,
DA9055_VMID_EN | DA9055_BIAS_EN, 0);
break;
}
return 0;
}
-static int da9055_probe(struct snd_soc_codec *codec)
+static int da9055_probe(struct snd_soc_component *component)
{
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
/* Enable all Gain Ramps */
- snd_soc_update_bits(codec, DA9055_AUX_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_AUX_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_AUX_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_AUX_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_HP_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_HP_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA9055_LINE_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
/*
@@ -1412,28 +1410,28 @@ static int da9055_probe(struct snd_soc_codec *codec)
* being managed by DAPM while other (non power related) bits are
* enabled here
*/
- snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXOUT_L_CTRL,
DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXOUT_R_CTRL,
DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN);
/* Set this as per your system configuration */
- snd_soc_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
+ snd_soc_component_write(component, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
/* Set platform data values */
if (da9055->pdata) {
/* set mic bias source */
if (da9055->pdata->micbias_source) {
- snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
DA9055_MICBIAS2_EN,
DA9055_MICBIAS2_EN);
} else {
- snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
DA9055_MICBIAS2_EN, 0);
}
/* set mic bias voltage */
@@ -1442,7 +1440,7 @@ static int da9055_probe(struct snd_soc_codec *codec)
case DA9055_MICBIAS_2_1V:
case DA9055_MICBIAS_1_8V:
case DA9055_MICBIAS_1_6V:
- snd_soc_update_bits(codec, DA9055_MIC_CONFIG,
+ snd_soc_component_update_bits(component, DA9055_MIC_CONFIG,
DA9055_MICBIAS_LEVEL_MASK,
(da9055->pdata->micbias) << 4);
break;
@@ -1451,17 +1449,18 @@ static int da9055_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da9055 = {
+static const struct snd_soc_component_driver soc_component_dev_da9055 = {
.probe = da9055_probe,
.set_bias_level = da9055_set_bias_level,
-
.controls = da9055_snd_controls,
.num_controls = ARRAY_SIZE(da9055_snd_controls),
-
.dapm_widgets = da9055_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets),
.dapm_routes = da9055_audio_map,
.num_dapm_routes = ARRAY_SIZE(da9055_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config da9055_regmap_config = {
@@ -1474,8 +1473,7 @@ static const struct regmap_config da9055_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int da9055_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da9055_i2c_probe(struct i2c_client *i2c)
{
struct da9055_priv *da9055;
struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
@@ -1498,21 +1496,15 @@ static int da9055_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da9055, &da9055_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_da9055, &da9055_dai, 1);
if (ret < 0) {
- dev_err(&i2c->dev, "Failed to register da9055 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da9055 component: %d\n",
ret);
}
return ret;
}
-static int da9055_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
/*
* DO NOT change the device Ids. The naming is intentionally specific as both
* the CODEC and PMIC parts of this chip are instantiated separately as I2C
@@ -1521,16 +1513,18 @@ static int da9055_remove(struct i2c_client *client)
* and PMIC, which must be different to operate together.
*/
static const struct i2c_device_id da9055_i2c_id[] = {
- { "da9055-codec", 0 },
+ { "da9055-codec" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+#ifdef CONFIG_OF
static const struct of_device_id da9055_of_match[] = {
{ .compatible = "dlg,da9055-codec", },
{ }
};
MODULE_DEVICE_TABLE(of, da9055_of_match);
+#endif
/* I2C codec control layer */
static struct i2c_driver da9055_i2c_driver = {
@@ -1539,7 +1533,6 @@ static struct i2c_driver da9055_i2c_driver = {
.of_match_table = of_match_ptr(da9055_of_match),
},
.probe = da9055_i2c_probe,
- .remove = da9055_remove,
.id_table = da9055_i2c_id,
};
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index fde53251c047..61e1bf1b3c9e 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -1,25 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* dmic.c -- SoC audio for Generic Digital MICs
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -27,6 +17,75 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#define MAX_MODESWITCH_DELAY 70
+static int modeswitch_delay;
+module_param(modeswitch_delay, uint, 0644);
+
+static int wakeup_delay;
+module_param(wakeup_delay, uint, 0644);
+
+struct dmic {
+ struct gpio_desc *gpio_en;
+ struct regulator *vref;
+ int wakeup_delay;
+ /* Delay after DMIC mode switch */
+ int modeswitch_delay;
+};
+
+static int dmic_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct dmic *dmic = snd_soc_component_get_drvdata(component);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (dmic->modeswitch_delay)
+ mdelay(dmic->modeswitch_delay);
+
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops dmic_dai_ops = {
+ .trigger = dmic_daiops_trigger,
+};
+
+static int dmic_aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event) {
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct dmic *dmic = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (dmic->gpio_en)
+ gpiod_set_value_cansleep(dmic->gpio_en, 1);
+
+ if (dmic->vref) {
+ ret = regulator_enable(dmic->vref);
+ if (ret)
+ return ret;
+ }
+
+ if (dmic->wakeup_delay)
+ msleep(dmic->wakeup_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (dmic->gpio_en)
+ gpiod_set_value_cansleep(dmic->gpio_en, 0);
+
+ if (dmic->vref)
+ ret = regulator_disable(dmic->vref);
+
+ break;
+ }
+
+ return ret;
+}
+
static struct snd_soc_dai_driver dmic_dai = {
.name = "dmic-hifi",
.capture = {
@@ -36,13 +95,58 @@ static struct snd_soc_dai_driver dmic_dai = {
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
- | SNDRV_PCM_FMTBIT_S16_LE,
+ | SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U8
+ | SNDRV_PCM_FMTBIT_DSD_U16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U32_LE
+ | SNDRV_PCM_FMTBIT_DSD_U16_BE
+ | SNDRV_PCM_FMTBIT_DSD_U32_BE,
},
+ .ops = &dmic_dai_ops,
};
+static int dmic_component_probe(struct snd_soc_component *component)
+{
+ struct dmic *dmic;
+
+ dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL);
+ if (!dmic)
+ return -ENOMEM;
+
+ dmic->vref = devm_regulator_get_optional(component->dev, "vref");
+ if (IS_ERR(dmic->vref)) {
+ if (PTR_ERR(dmic->vref) != -ENODEV)
+ return dev_err_probe(component->dev, PTR_ERR(dmic->vref),
+ "Failed to get vref\n");
+ dmic->vref = NULL;
+ }
+
+ dmic->gpio_en = devm_gpiod_get_optional(component->dev,
+ "dmicen", GPIOD_OUT_LOW);
+ if (IS_ERR(dmic->gpio_en))
+ return PTR_ERR(dmic->gpio_en);
+
+ device_property_read_u32(component->dev, "wakeup-delay-ms",
+ &dmic->wakeup_delay);
+ device_property_read_u32(component->dev, "modeswitch-delay-ms",
+ &dmic->modeswitch_delay);
+ if (wakeup_delay)
+ dmic->wakeup_delay = wakeup_delay;
+ if (modeswitch_delay)
+ dmic->modeswitch_delay = modeswitch_delay;
+
+ if (dmic->modeswitch_delay > MAX_MODESWITCH_DELAY)
+ dmic->modeswitch_delay = MAX_MODESWITCH_DELAY;
+
+ snd_soc_component_set_drvdata(component, dmic);
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
- SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0,
- SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("DMIC AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0, dmic_aif_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("DMic"),
};
@@ -50,33 +154,59 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
-static struct snd_soc_codec_driver soc_dmic = {
- .dapm_widgets = dmic_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
- .dapm_routes = intercon,
- .num_dapm_routes = ARRAY_SIZE(intercon),
+static const struct snd_soc_component_driver soc_dmic = {
+ .probe = dmic_component_probe,
+ .dapm_widgets = dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int dmic_dev_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_dmic, &dmic_dai, 1);
-}
+ int err;
+ u32 chans;
+ struct snd_soc_dai_driver *dai_drv = &dmic_dai;
-static int dmic_dev_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ if (pdev->dev.of_node) {
+ err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans);
+ if (err && (err != -EINVAL))
+ return err;
+
+ if (!err) {
+ if (chans < 1 || chans > 8)
+ return -EINVAL;
+
+ dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv));
+ dai_drv->capture.channels_max = chans;
+ }
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_dmic, dai_drv, 1);
}
MODULE_ALIAS("platform:dmic-codec");
+static const struct of_device_id dmic_dev_match[] = {
+ {.compatible = "dmic-codec"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, dmic_dev_match);
+
static struct platform_driver dmic_driver = {
.driver = {
.name = "dmic-codec",
+ .of_match_table = dmic_dev_match,
},
.probe = dmic_dev_probe,
- .remove = dmic_dev_remove,
};
module_platform_driver(dmic_driver);
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
new file mode 100644
index 000000000000..441df1523f1c
--- /dev/null
+++ b/sound/soc/codecs/es7134.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * The everest 7134 is a very simple DA converter with no register
+ */
+
+struct es7134_clock_mode {
+ unsigned int rate_min;
+ unsigned int rate_max;
+ unsigned int *mclk_fs;
+ unsigned int mclk_fs_num;
+};
+
+struct es7134_chip {
+ struct snd_soc_dai_driver *dai_drv;
+ const struct es7134_clock_mode *modes;
+ unsigned int mode_num;
+ const struct snd_soc_dapm_widget *extra_widgets;
+ unsigned int extra_widget_num;
+ const struct snd_soc_dapm_route *extra_routes;
+ unsigned int extra_route_num;
+};
+
+struct es7134_data {
+ unsigned int mclk;
+ const struct es7134_chip *chip;
+};
+
+static int es7134_check_mclk(struct snd_soc_dai *dai,
+ struct es7134_data *priv,
+ unsigned int rate)
+{
+ unsigned int mfs = priv->mclk / rate;
+ int i, j;
+
+ for (i = 0; i < priv->chip->mode_num; i++) {
+ const struct es7134_clock_mode *mode = &priv->chip->modes[i];
+
+ if (rate < mode->rate_min || rate > mode->rate_max)
+ continue;
+
+ for (j = 0; j < mode->mclk_fs_num; j++) {
+ if (mode->mclk_fs[j] == mfs)
+ return 0;
+ }
+
+ dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n",
+ mfs, rate);
+ return -EINVAL;
+ }
+
+ /* should not happen */
+ dev_err(dai->dev, "unsupported rate: %u\n", rate);
+ return -EINVAL;
+}
+
+static int es7134_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ /* mclk has not been provided, assume it is OK */
+ if (!priv->mclk)
+ return 0;
+
+ return es7134_check_mclk(dai, priv, params_rate(params));
+}
+
+static int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ if (dir == SND_SOC_CLOCK_IN && clk_id == 0) {
+ priv->mclk = freq;
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK);
+
+ if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC)) {
+ dev_err(codec_dai->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int es7134_component_probe(struct snd_soc_component *c)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(c);
+ struct es7134_data *priv = snd_soc_component_get_drvdata(c);
+ const struct es7134_chip *chip = priv->chip;
+ int ret;
+
+ if (chip->extra_widget_num) {
+ ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets,
+ chip->extra_widget_num);
+ if (ret) {
+ dev_err(c->dev, "failed to add extra widgets\n");
+ return ret;
+ }
+ }
+
+ if (chip->extra_route_num) {
+ ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes,
+ chip->extra_route_num);
+ if (ret) {
+ dev_err(c->dev, "failed to add extra routes\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es7134_dai_ops = {
+ .set_fmt = es7134_set_fmt,
+ .hw_params = es7134_hw_params,
+ .set_sysclk = es7134_set_sysclk,
+};
+
+static struct snd_soc_dai_driver es7134_dai = {
+ .name = "es7134-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7134_dai_ops,
+};
+
+static const struct es7134_clock_mode es7134_modes[] = {
+ {
+ /* Single speed mode */
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 },
+ .mclk_fs_num = 5,
+ }, {
+ /* Double speed mode */
+ .rate_min = 84000,
+ .rate_max = 100000,
+ .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 },
+ .mclk_fs_num = 5,
+ }, {
+ /* Quad speed mode */
+ .rate_min = 167000,
+ .rate_max = 192000,
+ .mclk_fs = (unsigned int[]) { 128, 192, 256 },
+ .mclk_fs_num = 3,
+ },
+};
+
+/* Digital I/O are also supplied by VDD on the es7134 */
+static const struct snd_soc_dapm_route es7134_extra_routes[] = {
+ { "Playback", NULL, "VDD", }
+};
+
+static const struct es7134_chip es7134_chip __maybe_unused = {
+ .dai_drv = &es7134_dai,
+ .modes = es7134_modes,
+ .mode_num = ARRAY_SIZE(es7134_modes),
+ .extra_routes = es7134_extra_routes,
+ .extra_route_num = ARRAY_SIZE(es7134_extra_routes),
+};
+
+static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("AOUTL"),
+ SND_SOC_DAPM_OUTPUT("AOUTR"),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7134_dapm_routes[] = {
+ { "AOUTL", NULL, "DAC" },
+ { "AOUTR", NULL, "DAC" },
+ { "DAC", NULL, "VDD" },
+};
+
+static const struct snd_soc_component_driver es7134_component_driver = {
+ .probe = es7134_component_probe,
+ .dapm_widgets = es7134_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es7134_dapm_widgets),
+ .dapm_routes = es7134_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es7134_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver es7154_dai = {
+ .name = "es7154-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7134_dai_ops,
+};
+
+static const struct es7134_clock_mode es7154_modes[] = {
+ {
+ /* Single speed mode */
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256,
+ 384, 512, 768, 1024 },
+ .mclk_fs_num = 9,
+ }, {
+ /* Double speed mode */
+ .rate_min = 84000,
+ .rate_max = 100000,
+ .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512,
+ 768, 1024},
+ .mclk_fs_num = 7,
+ }
+};
+
+/* Es7154 has a separate supply for digital I/O */
+static const struct snd_soc_dapm_widget es7154_extra_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7154_extra_routes[] = {
+ { "Playback", NULL, "PVDD", }
+};
+
+static const struct es7134_chip es7154_chip __maybe_unused = {
+ .dai_drv = &es7154_dai,
+ .modes = es7154_modes,
+ .mode_num = ARRAY_SIZE(es7154_modes),
+ .extra_routes = es7154_extra_routes,
+ .extra_route_num = ARRAY_SIZE(es7154_extra_routes),
+ .extra_widgets = es7154_extra_widgets,
+ .extra_widget_num = ARRAY_SIZE(es7154_extra_widgets),
+};
+
+static int es7134_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct es7134_data *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->chip = of_device_get_match_data(dev);
+ if (!priv->chip) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &es7134_component_driver,
+ priv->chip->dai_drv, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es7134_ids[] = {
+ { .compatible = "everest,es7134", .data = &es7134_chip },
+ { .compatible = "everest,es7144", .data = &es7134_chip },
+ { .compatible = "everest,es7154", .data = &es7154_chip },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es7134_ids);
+#endif
+
+static struct platform_driver es7134_driver = {
+ .driver = {
+ .name = "es7134",
+ .of_match_table = of_match_ptr(es7134_ids),
+ },
+ .probe = es7134_probe,
+};
+
+module_platform_driver(es7134_driver);
+
+MODULE_DESCRIPTION("ASoC ES7134 audio codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c
new file mode 100644
index 000000000000..339553cfbb48
--- /dev/null
+++ b/sound/soc/codecs/es7241.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+struct es7241_clock_mode {
+ unsigned int rate_min;
+ unsigned int rate_max;
+ unsigned int *slv_mfs;
+ unsigned int slv_mfs_num;
+ unsigned int mst_mfs;
+ unsigned int mst_m0:1;
+ unsigned int mst_m1:1;
+};
+
+struct es7241_chip {
+ const struct es7241_clock_mode *modes;
+ unsigned int mode_num;
+};
+
+struct es7241_data {
+ struct gpio_desc *reset;
+ struct gpio_desc *m0;
+ struct gpio_desc *m1;
+ unsigned int fmt;
+ unsigned int mclk;
+ bool is_consumer;
+ const struct es7241_chip *chip;
+};
+
+static void es7241_set_mode(struct es7241_data *priv, int m0, int m1)
+{
+ /* put the device in reset */
+ gpiod_set_value_cansleep(priv->reset, 0);
+
+ /* set the mode */
+ gpiod_set_value_cansleep(priv->m0, m0);
+ gpiod_set_value_cansleep(priv->m1, m1);
+
+ /* take the device out of reset - datasheet does not specify a delay */
+ gpiod_set_value_cansleep(priv->reset, 1);
+}
+
+static int es7241_set_consumer_mode(struct es7241_data *priv,
+ const struct es7241_clock_mode *mode,
+ unsigned int mfs)
+{
+ int j;
+
+ if (!mfs)
+ goto out_ok;
+
+ for (j = 0; j < mode->slv_mfs_num; j++) {
+ if (mode->slv_mfs[j] == mfs)
+ goto out_ok;
+ }
+
+ return -EINVAL;
+
+out_ok:
+ es7241_set_mode(priv, 1, 1);
+ return 0;
+}
+
+static int es7241_set_provider_mode(struct es7241_data *priv,
+ const struct es7241_clock_mode *mode,
+ unsigned int mfs)
+{
+ /*
+ * We can't really set clock ratio, if the mclk/lrclk is different
+ * from what we provide, then error out
+ */
+ if (mfs && mfs != mode->mst_mfs)
+ return -EINVAL;
+
+ es7241_set_mode(priv, mode->mst_m0, mode->mst_m1);
+
+ return 0;
+}
+
+static int es7241_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct es7241_data *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int mfs = priv->mclk / rate;
+ int i;
+
+ for (i = 0; i < priv->chip->mode_num; i++) {
+ const struct es7241_clock_mode *mode = &priv->chip->modes[i];
+
+ if (rate < mode->rate_min || rate >= mode->rate_max)
+ continue;
+
+ if (priv->is_consumer)
+ return es7241_set_consumer_mode(priv, mode, mfs);
+ else
+ return es7241_set_provider_mode(priv, mode, mfs);
+ }
+
+ /* should not happen */
+ dev_err(dai->dev, "unsupported rate: %u\n", rate);
+ return -EINVAL;
+}
+
+static int es7241_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct es7241_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ if (dir == SND_SOC_CLOCK_IN && clk_id == 0) {
+ priv->mclk = freq;
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct es7241_data *priv = snd_soc_dai_get_drvdata(dai);
+
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ dev_err(dai->dev, "Unsupported dai clock inversion\n");
+ return -EINVAL;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != priv->fmt) {
+ dev_err(dai->dev, "Invalid dai format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->is_consumer = true;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ priv->is_consumer = false;
+ break;
+
+ default:
+ dev_err(dai->dev, "Unsupported clock configuration\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es7241_dai_ops = {
+ .set_fmt = es7241_set_fmt,
+ .hw_params = es7241_hw_params,
+ .set_sysclk = es7241_set_sysclk,
+};
+
+static struct snd_soc_dai_driver es7241_dai = {
+ .name = "es7241-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7241_dai_ops,
+};
+
+static const struct es7241_clock_mode es7241_modes[] = {
+ {
+ /* Single speed mode */
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .slv_mfs = (unsigned int[]) { 256, 384, 512, 768, 1024 },
+ .slv_mfs_num = 5,
+ .mst_mfs = 256,
+ .mst_m0 = 0,
+ .mst_m1 = 0,
+ }, {
+ /* Double speed mode */
+ .rate_min = 50000,
+ .rate_max = 100000,
+ .slv_mfs = (unsigned int[]) { 128, 192 },
+ .slv_mfs_num = 2,
+ .mst_mfs = 128,
+ .mst_m0 = 1,
+ .mst_m1 = 0,
+ }, {
+ /* Quad speed mode */
+ .rate_min = 100000,
+ .rate_max = 200000,
+ .slv_mfs = (unsigned int[]) { 64 },
+ .slv_mfs_num = 1,
+ .mst_mfs = 64,
+ .mst_m0 = 0,
+ .mst_m1 = 1,
+ },
+};
+
+static const struct es7241_chip es7241_chip __maybe_unused = {
+ .modes = es7241_modes,
+ .mode_num = ARRAY_SIZE(es7241_modes),
+};
+
+static const struct snd_soc_dapm_widget es7241_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("AINL"),
+ SND_SOC_DAPM_INPUT("AINR"),
+ SND_SOC_DAPM_DAC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDP", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDD", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDDA", 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7241_dapm_routes[] = {
+ { "ADC", NULL, "AINL", },
+ { "ADC", NULL, "AINR", },
+ { "ADC", NULL, "VDDA", },
+ { "Capture", NULL, "VDDP", },
+ { "Capture", NULL, "VDDD", },
+};
+
+static const struct snd_soc_component_driver es7241_component_driver = {
+ .dapm_widgets = es7241_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es7241_dapm_widgets),
+ .dapm_routes = es7241_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es7241_dapm_routes),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void es7241_parse_fmt(struct device *dev, struct es7241_data *priv)
+{
+ bool is_leftj;
+
+ /*
+ * The format is given by a pull resistor on the SDOUT pin:
+ * pull-up for i2s, pull-down for left justified.
+ */
+ is_leftj = of_property_read_bool(dev->of_node,
+ "everest,sdout-pull-down");
+ if (is_leftj)
+ priv->fmt = SND_SOC_DAIFMT_LEFT_J;
+ else
+ priv->fmt = SND_SOC_DAIFMT_I2S;
+}
+
+static int es7241_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct es7241_data *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->chip = of_device_get_match_data(dev);
+ if (!priv->chip) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ es7241_parse_fmt(dev, priv);
+
+ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(dev, PTR_ERR(priv->reset),
+ "Failed to get 'reset' gpio");
+
+ priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->m0))
+ return dev_err_probe(dev, PTR_ERR(priv->m0),
+ "Failed to get 'm0' gpio");
+
+ priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->m1))
+ return dev_err_probe(dev, PTR_ERR(priv->m1),
+ "Failed to get 'm1' gpio");
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &es7241_component_driver,
+ &es7241_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es7241_ids[] = {
+ { .compatible = "everest,es7241", .data = &es7241_chip },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es7241_ids);
+#endif
+
+static struct platform_driver es7241_driver = {
+ .driver = {
+ .name = "es7241",
+ .of_match_table = of_match_ptr(es7241_ids),
+ },
+ .probe = es7241_probe,
+};
+
+module_platform_driver(es7241_driver);
+
+MODULE_DESCRIPTION("ASoC ES7241 audio codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c
new file mode 100644
index 000000000000..0b07a53cc792
--- /dev/null
+++ b/sound/soc/codecs/es8311.c
@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8311.c -- es8311 ALSA SoC audio driver
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ *
+ * Author: Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#include "linux/array_size.h"
+#include "sound/pcm.h"
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "es8311.h"
+
+#define ES8311_NUM_RATES 10
+#define ES8311_RATES (SNDRV_PCM_RATE_8000_96000)
+#define ES8311_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct es8311_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned long mclk_freq;
+ bool provider;
+ unsigned int rates[ES8311_NUM_RATES];
+ struct snd_pcm_hw_constraint_list constraints;
+};
+
+static const DECLARE_TLV_DB_SCALE(es8311_adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8311_pga_gain_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(es8311_adc_scale_tlv, 0, 600, 0);
+
+#define ES8311_DB_LRCK_STEPS \
+ "0.25db/4LRCK", \
+ "0.25db/8LRCK", \
+ "0.25db/16LRCK", \
+ "0.25db/32LRCK", \
+ "0.25db/64LRCK", \
+ "0.25db/128LRCK", \
+ "0.25db/256LRCK", \
+ "0.25db/512LRCK", \
+ "0.25db/1024LRCK", \
+ "0.25db/2048LRCK", \
+ "0.25db/4096LRCK", \
+ "0.25db/8192LRCK", \
+ "0.25db/16384LRCK", \
+ "0.25db/32768LRCK", \
+ "0.25db/65536LRCK",
+
+static const char *const es8311_level_winsize_txt[] = {
+ "0.25db/2LRCK",
+ ES8311_DB_LRCK_STEPS
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ es8311_alc_winsize, ES8311_ADC4,
+ ES8311_ADC4_ALC_WINSIZE_SHIFT, es8311_level_winsize_txt);
+static const DECLARE_TLV_DB_RANGE(es8311_level_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-3010, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-2060, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-1610, 160, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-1320, 120, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-1100, 90, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-930, 80, 0),
+ 12, 15, TLV_DB_SCALE_ITEM(-780, 60, 0),
+);
+
+static const char *const es8311_ramprate_txt[] = {
+ "Disabled",
+ ES8311_DB_LRCK_STEPS
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_adc_ramprate, ES8311_ADC1,
+ ES8311_ADC1_RAMPRATE_SHIFT, es8311_ramprate_txt);
+
+static const char *const es8311_automute_winsize_txt[] = {
+ "2048 samples",
+ "4096 samples",
+ "6144 samples",
+ "8192 samples",
+ "10240 samples",
+ "12288 samples",
+ "14336 samples",
+ "16384 samples",
+ "18432 samples",
+ "20480 samples",
+ "22528 samples",
+ "24576 samples",
+ "26624 samples",
+ "28672 samples",
+ "30720 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_automute_winsize, ES8311_ADC6,
+ ES8311_ADC6_AUTOMUTE_WS_SHIFT, es8311_automute_winsize_txt);
+static const DECLARE_TLV_DB_RANGE(es8311_automute_ng_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-9600, 600, 0),
+ 8, 15, TLV_DB_SCALE_ITEM(-5100, 300, 0),
+);
+static const DECLARE_TLV_DB_SCALE(es8311_automute_vol_tlv, -2800, 400, 0);
+
+static const DECLARE_TLV_DB_SCALE(es8311_dac_vol_tlv, -9550, 50, 0);
+static SOC_ENUM_SINGLE_DECL(
+ es8311_drc_winsize, ES8311_DAC4,
+ ES8311_DAC4_DRC_WINSIZE_SHIFT, es8311_level_winsize_txt);
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dac_ramprate, ES8311_DAC6,
+ ES8311_DAC6_RAMPRATE_SHIFT, es8311_ramprate_txt);
+
+static const char *const es8311_out_mode_txt[] = {
+ "Lineout",
+ "Headphones"
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_out_mode, ES8311_SYS9,
+ ES8311_SYS9_HPSW_SHIFT, es8311_out_mode_txt);
+
+static const struct snd_kcontrol_new es8311_snd_controls[] = {
+ /* Capture path */
+ SOC_SINGLE_TLV("PGA Capture Volume", ES8311_SYS10,
+ ES8311_SYS10_PGAGAIN_SHIFT, ES8311_SYS10_PGAGAIN_MAX, 0,
+ es8311_pga_gain_tlv),
+ SOC_SINGLE("ADC Polarity Invert Capture Switch", ES8311_ADC2,
+ ES8311_ADC2_INV_SHIFT, 1, 0),
+ SOC_SINGLE_TLV("ADC Scale Capture Volume", ES8311_ADC2,
+ ES8311_ADC2_SCALE_SHIFT, ES8311_ADC2_SCALE_MAX, 0,
+ es8311_adc_scale_tlv),
+ SOC_SINGLE_TLV("ADC Capture Volume", ES8311_ADC3,
+ ES8311_ADC3_VOLUME_SHIFT, ES8311_ADC3_VOLUME_MAX, 0,
+ es8311_adc_vol_tlv),
+ SOC_ENUM("ADC Capture Ramp Rate", es8311_adc_ramprate),
+ SOC_SINGLE("ADC Automute Capture Switch", ES8311_ADC4,
+ ES8311_ADC4_AUTOMUTE_EN_SHIFT, 1, 0),
+ SOC_ENUM("ADC Automute Capture Winsize", es8311_automute_winsize),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate Capture Volume", ES8311_ADC6,
+ ES8311_ADC6_AUTOMUTE_NG_SHIFT,
+ ES8311_ADC6_AUTOMUTE_NG_MAX, 0, es8311_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Capture Volume", ES8311_ADC7,
+ ES8311_ADC7_AUTOMUTE_VOL_SHIFT,
+ ES8311_ADC7_AUTOMUTE_VOL_MAX, 0,
+ es8311_automute_vol_tlv),
+ SOC_SINGLE("ADC HPF Capture Switch", ES8311_ADC8, ES8311_ADC8_HPF_SHIFT,
+ 1, 0),
+ SOC_SINGLE("ADC EQ Capture Switch", ES8311_ADC8,
+ ES8311_ADC8_EQBYPASS_SHIFT, 1, 1),
+ SOC_SINGLE("ALC Capture Switch", ES8311_ADC4, ES8311_ADC4_ALC_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", ES8311_ADC5,
+ ES8311_ADC5_ALC_MAXLEVEL_SHIFT,
+ ES8311_ADC5_ALC_MAXLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", ES8311_ADC5,
+ ES8311_ADC5_ALC_MINLEVEL_SHIFT,
+ ES8311_ADC5_ALC_MINLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_ENUM("ALC Capture Winsize", es8311_alc_winsize),
+
+ /* Playback path */
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8311_DAC2, 0,
+ ES8311_DAC2_VOLUME_MAX, 0, es8311_dac_vol_tlv),
+ SOC_SINGLE("DRC Playback Switch", ES8311_DAC4, ES8311_DAC4_DRC_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE_TLV("DRC Playback Max Volume", ES8311_DAC5,
+ ES8311_DAC5_DRC_MAXLEVEL_SHIFT,
+ ES8311_DAC5_DRC_MAXLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_SINGLE_TLV("DRC Playback Min Volume", ES8311_DAC5,
+ ES8311_DAC5_DRC_MINLEVEL_SHIFT,
+ ES8311_DAC5_DRC_MINLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_ENUM("DRC Playback Winsize", es8311_drc_winsize),
+ SOC_ENUM("DAC Playback Ramp Rate", es8311_dac_ramprate),
+ SOC_SINGLE("DAC EQ Playback Switch", ES8311_DAC6,
+ ES8311_DAC6_EQBYPASS_SHIFT, 1, 1),
+
+ SOC_ENUM("Output Mode", es8311_out_mode),
+};
+
+static const char *const es8311_diff_src_txt[] = {
+ "Disabled",
+ "MIC1P-MIC1N",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_diff_src_enum, ES8311_SYS10,
+ ES8311_SYS10_LINESEL_SHIFT, es8311_diff_src_txt);
+static const struct snd_kcontrol_new es8311_diff_src_mux =
+ SOC_DAPM_ENUM("Differential Source", es8311_diff_src_enum);
+
+static const char *const es8311_dmic_src_txt[] = {
+ "Disabled",
+ "DMIC from MIC1P",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dmic_src_enum, ES8311_SYS10,
+ ES8311_SYS10_DMIC_ON_SHIFT, es8311_dmic_src_txt);
+static const struct snd_kcontrol_new es8311_dmic_src_mux =
+ SOC_DAPM_ENUM("Digital Mic Source", es8311_dmic_src_enum);
+
+static const char * const es8311_aif1tx_src_txt[] = {
+ "ADC + ADC",
+ "ADC + 0",
+ "0 + ADC",
+ "0 + 0",
+ "DACL + ADC",
+ "ADC + DACR",
+ "DACL + DACR",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_aif1tx_src_enum, ES8311_GPIO,
+ ES8311_GPIO_ADCDAT_SEL_SHIFT, es8311_aif1tx_src_txt);
+static const struct snd_kcontrol_new es8311_aif1tx_src_mux =
+ SOC_DAPM_ENUM("AIF1TX Source", es8311_aif1tx_src_enum);
+
+static const char * const es8311_dac_src_txt[] = {
+ "Left",
+ "Right"
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dac_src_enum, ES8311_SDP_IN,
+ ES8311_SDP_IN_SEL_SHIFT, es8311_dac_src_txt);
+static const struct snd_kcontrol_new es8311_dac_src_mux =
+ SOC_DAPM_ENUM("Mono DAC Source", es8311_dac_src_enum);
+
+static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Bias", ES8311_SYS3, ES8311_SYS3_PDN_IBIASGEN_SHIFT,
+ 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Analog power", ES8311_SYS3,
+ ES8311_SYS3_PDN_ANA_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref", ES8311_SYS3, ES8311_SYS3_PDN_VREF_SHIFT, 1,
+ NULL, 0),
+
+ /* Capture path */
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_diff_src_mux),
+ SND_SOC_DAPM_SUPPLY("ADC Bias Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_ADCBIASGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_ADCVREFGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_CLKADC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Analog Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_ANACLKADC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA", ES8311_SYS4, ES8311_SYS4_PDN_PGA_SHIFT, 1, NULL,
+ 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8311_SYS4,
+ ES8311_SYS4_PDN_MOD_SHIFT, 1),
+ SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_dmic_src_mux),
+ SND_SOC_DAPM_MUX("AIF1TX Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_aif1tx_src_mux),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8311_SDP_OUT,
+ ES8311_SDP_MUTE_SHIFT, 1),
+
+ /* Playback path */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8311_SDP_IN,
+ ES8311_SDP_MUTE_SHIFT, 1),
+ SND_SOC_DAPM_MUX("Mono DAC Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_dac_src_mux),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, ES8311_SYS8,
+ ES8311_SYS8_PDN_DAC_SHIFT, 1),
+ SND_SOC_DAPM_SUPPLY("DAC Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_CLKDAC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Analog Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_DACVREFGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8311_dapm_routes[] = {
+ /* Capture Path */
+ { "MIC1", NULL, "Bias" },
+ { "MIC1", NULL, "Analog power" },
+ { "MIC1", NULL, "Vref" },
+ { "Differential Mux", "MIC1P-MIC1N", "MIC1" },
+ { "PGA", NULL, "Differential Mux" },
+ { "Mono ADC", NULL, "PGA" },
+ { "Mono ADC", NULL, "ADC Bias Gen" },
+ { "Mono ADC", NULL, "ADC Vref Gen" },
+ { "Mono ADC", NULL, "ADC Clock" },
+ { "Mono ADC", NULL, "ADC Analog Clock" },
+ { "Digital Mic Mux", "Disabled", "Mono ADC" },
+ { "Digital Mic Mux", "DMIC from MIC1P", "DMIC" },
+
+ { "AIF1TX Source Mux", "ADC + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "ADC + 0", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "0 + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "DACL + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "ADC + DACR", "Digital Mic Mux" },
+
+ { "AIF1TX", NULL, "AIF1TX Source Mux" },
+
+ /* Playback Path */
+ { "Mono DAC Source Mux", "Left", "AIF1RX" },
+ { "Mono DAC Source Mux", "Right", "AIF1RX" },
+ { "Mono DAC", NULL, "Mono DAC Source Mux" },
+ { "Mono DAC", NULL, "DAC Clock" },
+ { "Mono DAC", NULL, "DAC Analog Clock" },
+ { "OUT", NULL, "Mono DAC" },
+ { "OUT", NULL, "Bias" },
+ { "OUT", NULL, "Analog power" },
+ { "OUT", NULL, "Vref" },
+ { "OUT", NULL, "DAC Vref Gen" },
+};
+
+/* Bit clock divider values:
+ * from 1 to 20: the register takes the div value - 1
+ * above 20: the register takes the corresponding idx of the div value
+ * in the following table + 20
+ */
+#define ES8311_BCLK_DIV_IDX_OFFSET 20
+static const unsigned int es8311_bclk_divs[] = {
+ 22, 24, 25, 30, 32, 33, 34, 36, 44, 48, 66, 72
+};
+
+struct es8311_mclk_coeff {
+ unsigned int rate;
+ unsigned int mclk;
+ unsigned int div;
+ unsigned int mult;
+ unsigned int div_adc_dac;
+};
+
+#define ES8311_MCLK_MAX_FREQ 49200000
+
+/* Coefficients for common master clock frequencies based on clock table from
+ * documentation. Limited to have a ratio of adc (or dac) clock to lrclk equal
+ * to 256. This to keep the default adc and dac oversampling and adc scale
+ * settings. Internal mclk dividers and multipliers are dynamically adjusted to
+ * support, respectively, multiples (up to x8) and factors (/2,4,8) of listed
+ * mclks frequencies (see es8311_cmp_adj_mclk_coeff).
+ * All rates are supported when mclk/rate ratio is 32, 64, 128, 256, 384 or 512
+ * (upper limit due to max mclk freq of 49.2MHz).
+ */
+static const struct es8311_mclk_coeff es8311_mclk_coeffs[] = {
+ { 8000, 2048000, 1, 1, 1 },
+ { 8000, 6144000, 3, 1, 1 },
+ { 8000, 18432000, 3, 1, 3 },
+ { 11025, 2822400, 1, 1, 1 },
+ { 11025, 8467200, 3, 1, 1 },
+ { 16000, 4096000, 1, 1, 1 },
+ { 16000, 12288000, 3, 1, 1 },
+ { 16000, 18432000, 3, 2, 3 },
+ { 22050, 5644800, 1, 1, 1 },
+ { 22050, 16934400, 3, 1, 1 },
+ { 32000, 8192000, 1, 1, 1 },
+ { 32000, 12288000, 3, 2, 1 },
+ { 32000, 18432000, 3, 4, 3 },
+ { 44100, 11289600, 1, 1, 1 },
+ { 44100, 33868800, 3, 1, 1 },
+ { 48000, 12288000, 1, 1, 1 },
+ { 48000, 18432000, 3, 2, 1 },
+ { 64000, 8192000, 1, 2, 1 },
+ { 64000, 12288000, 3, 4, 1 },
+ { 88200, 11289600, 1, 2, 1 },
+ { 88200, 33868800, 3, 2, 1 },
+ { 96000, 12288000, 1, 2, 1 },
+ { 96000, 18432000, 3, 4, 1 },
+};
+
+/* Compare coeff with provided mclk_freq and adjust it if needed.
+ * If frequencies match, return 0 and the unaltered coeff copy into out_coeff.
+ * If mclk_freq is a valid multiple or factor of coeff mclk freq, return 0 and
+ * the adjusted coeff copy into out_coeff.
+ * Return -EINVAL otherwise.
+ */
+static int es8311_cmp_adj_mclk_coeff(unsigned int mclk_freq,
+ const struct es8311_mclk_coeff *coeff,
+ struct es8311_mclk_coeff *out_coeff)
+{
+ if (WARN_ON_ONCE(!coeff))
+ return -EINVAL;
+
+ unsigned int div = coeff->div;
+ unsigned int mult = coeff->mult;
+ bool match = false;
+
+ if (coeff->mclk == mclk_freq) {
+ match = true;
+ } else if (mclk_freq % coeff->mclk == 0) {
+ div = mclk_freq / coeff->mclk;
+ div *= coeff->div;
+ if (div <= 8)
+ match = true;
+ } else if (coeff->mclk % mclk_freq == 0) {
+ mult = coeff->mclk / mclk_freq;
+ if (mult == 2 || mult == 4 || mult == 8) {
+ mult *= coeff->mult;
+ if (mult <= 8)
+ match = true;
+ }
+ }
+ if (!match)
+ return -EINVAL;
+ if (out_coeff) {
+ *out_coeff = *coeff;
+ out_coeff->div = div;
+ out_coeff->mult = mult;
+ }
+ return 0;
+}
+
+static int es8311_get_mclk_coeff(unsigned int mclk_freq, unsigned int rate,
+ struct es8311_mclk_coeff *out_coeff)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs); i++) {
+ const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i];
+
+ if (coeff->rate != rate)
+ continue;
+
+ int ret =
+ es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, out_coeff);
+ if (ret == 0)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void es8311_set_sysclk_constraints(unsigned int mclk_freq,
+ struct es8311_priv *es8311)
+{
+ unsigned int count = 0;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs) &&
+ count < ARRAY_SIZE(es8311->rates); i++) {
+ const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i];
+
+ if (count > 0 && coeff->rate == es8311->rates[count - 1])
+ continue;
+
+ int ret = es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, NULL);
+ if (ret == 0)
+ es8311->rates[count++] = coeff->rate;
+ }
+ if (count) {
+ es8311->constraints.list = es8311->rates;
+ es8311->constraints.count = count;
+ }
+}
+
+static int es8311_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int mask = ES8311_DAC1_DAC_DSMMUTE |
+ ES8311_DAC1_DAC_DEMMUTE;
+ unsigned int val = mute ? mask : 0;
+
+ regmap_update_bits(es8311->regmap, ES8311_DAC1, mask, val);
+ }
+
+ return 0;
+}
+
+static int es8311_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (es8311->constraints.list) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &es8311->constraints);
+ }
+
+ return 0;
+}
+
+static int es8311_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+ unsigned int wl;
+ int par_width = params_width(params);
+
+ switch (par_width) {
+ case 16:
+ wl = ES8311_SDP_WL_16;
+ break;
+ case 18:
+ wl = ES8311_SDP_WL_18;
+ break;
+ case 20:
+ wl = ES8311_SDP_WL_20;
+ break;
+ case 24:
+ wl = ES8311_SDP_WL_24;
+ break;
+ case 32:
+ wl = ES8311_SDP_WL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ unsigned int width = (unsigned int)par_width;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_component_update_bits(component, ES8311_SDP_IN,
+ ES8311_SDP_WL_MASK,
+ wl << ES8311_SDP_WL_SHIFT);
+ } else {
+ snd_soc_component_update_bits(component, ES8311_SDP_OUT,
+ ES8311_SDP_WL_MASK,
+ wl << ES8311_SDP_WL_SHIFT);
+ }
+
+ if (es8311->mclk_freq > ES8311_MCLK_MAX_FREQ) {
+ dev_err(component->dev, "mclk frequency %lu too high\n",
+ es8311->mclk_freq);
+ return -EINVAL;
+ }
+
+ unsigned int mclk_freq = es8311->mclk_freq;
+ unsigned int rate = params_rate(params);
+ unsigned int clkmgr = ES8311_CLKMGR1_MCLK_ON;
+
+ if (!mclk_freq) {
+ if (es8311->provider) {
+ dev_err(component->dev,
+ "mclk not configured, cannot run as master\n");
+ return -EINVAL;
+ }
+ dev_dbg(component->dev,
+ "mclk not configured, use bclk as internal mclk\n");
+
+ clkmgr = ES8311_CLKMGR1_MCLK_SEL;
+
+ mclk_freq = rate * width * 2;
+ }
+
+ struct es8311_mclk_coeff coeff;
+ int ret = es8311_get_mclk_coeff(mclk_freq, rate, &coeff);
+ if (ret) {
+ dev_err(component->dev, "unable to find mclk coefficient\n");
+ return ret;
+ }
+
+ unsigned int mask = ES8311_CLKMGR1_MCLK_SEL | ES8311_CLKMGR1_MCLK_ON |
+ ES8311_CLKMGR1_BCLK_ON;
+
+ clkmgr |= ES8311_CLKMGR1_BCLK_ON;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR1, mask, clkmgr);
+
+ if (WARN_ON_ONCE(coeff.div == 0 || coeff.div > 8 ||
+ coeff.div_adc_dac == 0 || coeff.div_adc_dac > 8))
+ return -EINVAL;
+
+ unsigned int mult;
+
+ switch (coeff.mult) {
+ case 1:
+ mult = 0;
+ break;
+ case 2:
+ mult = 1;
+ break;
+ case 4:
+ mult = 2;
+ break;
+ case 8:
+ mult = 3;
+ break;
+ default:
+ WARN_ON_ONCE(true);
+ return -EINVAL;
+ }
+
+ mask = ES8311_CLKMGR2_DIV_PRE_MASK | ES8311_CLKMGR2_MULT_PRE_MASK;
+ clkmgr = (coeff.div - 1) << ES8311_CLKMGR2_DIV_PRE_SHIFT |
+ mult << ES8311_CLKMGR2_MULT_PRE_SHIFT;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR2, mask, clkmgr);
+
+ mask = ES8311_CLKMGR5_ADC_DIV_MASK | ES8311_CLKMGR5_DAC_DIV_MASK;
+ clkmgr = (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_ADC_DIV_SHIFT |
+ (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_DAC_DIV_SHIFT;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR5, mask, clkmgr);
+
+ if (es8311->provider) {
+ unsigned int div_lrclk = mclk_freq / rate;
+
+ if (WARN_ON_ONCE(div_lrclk == 0 ||
+ div_lrclk > ES8311_CLKMGR_LRCLK_DIV_MAX + 1))
+ return -EINVAL;
+
+ mask = ES8311_CLKMGR7_LRCLK_DIV_H_MASK;
+ clkmgr = (div_lrclk - 1) >> 8;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR7, mask,
+ clkmgr);
+ clkmgr = (div_lrclk - 1) & 0xFF;
+ snd_soc_component_write(component, ES8311_CLKMGR8, clkmgr);
+
+ if (div_lrclk % (2 * width) != 0) {
+ dev_err(component->dev,
+ "unable to divide mclk %u to generate bclk\n",
+ mclk_freq);
+ return -EINVAL;
+ }
+
+ unsigned int div_bclk = div_lrclk / (2 * width);
+
+ mask = ES8311_CLKMGR6_DIV_BCLK_MASK;
+ if (div_bclk <= ES8311_BCLK_DIV_IDX_OFFSET) {
+ clkmgr = div_bclk - 1;
+ } else {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(es8311_bclk_divs); i++) {
+ if (es8311_bclk_divs[i] == div_bclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(es8311_bclk_divs)) {
+ dev_err(component->dev,
+ "bclk divider %u not supported\n",
+ div_bclk);
+ return -EINVAL;
+ }
+
+ clkmgr = i + ES8311_BCLK_DIV_IDX_OFFSET;
+ }
+ snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask,
+ clkmgr);
+ }
+
+ return 0;
+}
+
+static int es8311_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (freq > ES8311_MCLK_MAX_FREQ) {
+ dev_err(component->dev, "invalid frequency %u: too high\n",
+ freq);
+ return -EINVAL;
+ }
+
+ if (es8311->mclk_freq == freq)
+ return 0;
+
+ es8311->mclk_freq = freq;
+ es8311->constraints.list = NULL;
+ es8311->constraints.count = 0;
+
+ if (freq == 0)
+ return 0;
+
+ int ret = clk_set_rate(es8311->mclk, freq);
+ if (ret) {
+ dev_err(component->dev, "unable to set mclk rate\n");
+ return ret;
+ }
+
+ es8311_set_sysclk_constraints(freq, es8311);
+
+ return ret;
+}
+
+static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Master mode */
+ es8311->provider = true;
+
+ snd_soc_component_update_bits(component, ES8311_RESET,
+ ES8311_RESET_MSC,
+ ES8311_RESET_MSC);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Slave mode */
+ es8311->provider = false;
+ snd_soc_component_update_bits(component, ES8311_RESET,
+ ES8311_RESET_MSC, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int sdp = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sdp |= ES8311_SDP_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ sdp |= ES8311_SDP_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "right justified mode not supported\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_DSP_B:
+ sdp |= ES8311_SDP_LRP;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_A:
+ sdp |= ES8311_SDP_FMT_DSP;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ dev_err(component->dev,
+ "inverted fsync not supported in dsp mode\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int clkmgr = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sdp |= ES8311_SDP_LRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ clkmgr |= ES8311_CLKMGR6_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ clkmgr |= ES8311_CLKMGR6_BCLK_INV;
+ sdp |= ES8311_SDP_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int mask = ES8311_CLKMGR6_BCLK_INV;
+
+ snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, clkmgr);
+
+ mask = ES8311_SDP_FMT_MASK | ES8311_SDP_LRP;
+ snd_soc_component_update_bits(component, ES8311_SDP_IN, mask, sdp);
+ snd_soc_component_update_bits(component, ES8311_SDP_OUT, mask, sdp);
+
+ return 0;
+}
+
+static int es8311_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ int ret = clk_prepare_enable(es8311->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "unable to prepare mclk\n");
+ return ret;
+ }
+
+ snd_soc_component_update_bits(
+ component, ES8311_SYS3,
+ ES8311_SYS3_PDN_VMIDSEL_MASK,
+ ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8311->mclk);
+ snd_soc_component_update_bits(
+ component, ES8311_SYS3, ES8311_SYS3_PDN_VMIDSEL_MASK,
+ ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8311_dai_ops = {
+ .startup = es8311_startup,
+ .hw_params = es8311_hw_params,
+ .mute_stream = es8311_mute,
+ .set_sysclk = es8311_set_sysclk,
+ .set_fmt = es8311_set_dai_fmt,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8311_dai = {
+ .name = "es8311",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ES8311_RATES,
+ .formats = ES8311_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ES8311_RATES,
+ .formats = ES8311_FORMATS,
+ },
+ .ops = &es8311_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8311_reset(struct snd_soc_component *component, bool reset)
+{
+ /* Reset procedure:
+ * (1) power down state machine and reset codec blocks then,
+ * (2) after a short delay, power up state machine and leave reset mode.
+ * Specific delay is not documented, using the same as es8316.
+ */
+ unsigned int mask = ES8311_RESET_CSM_ON | ES8311_RESET_RST_MASK;
+
+ if (reset) {
+ /* Enter reset mode */
+ snd_soc_component_update_bits(component, ES8311_RESET, mask,
+ ES8311_RESET_RST_MASK);
+ } else {
+ /* Leave reset mode */
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, ES8311_RESET, mask,
+ ES8311_RESET_CSM_ON);
+ }
+}
+
+static int es8311_suspend(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311_reset(component, true);
+
+ regcache_cache_only(es8311->regmap, true);
+ regcache_mark_dirty(es8311->regmap);
+
+ return 0;
+}
+
+static int es8311_resume(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311_reset(component, false);
+
+ regcache_cache_only(es8311->regmap, false);
+ regcache_sync(es8311->regmap);
+
+ return 0;
+}
+
+static int es8311_component_probe(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8311->mclk)) {
+ dev_err(component->dev, "invalid mclk\n");
+ return PTR_ERR(es8311->mclk);
+ }
+
+ es8311->mclk_freq = clk_get_rate(es8311->mclk);
+ if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ)
+ es8311_set_sysclk_constraints(es8311->mclk_freq, es8311);
+
+ es8311_reset(component, true);
+ es8311_reset(component, false);
+
+ /* Set minimal power up time */
+ snd_soc_component_write(component, ES8311_SYS1, 0);
+ snd_soc_component_write(component, ES8311_SYS2, 0);
+
+ return 0;
+}
+
+static const struct regmap_config es8311_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8311_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct snd_soc_component_driver es8311_component_driver = {
+ .probe = es8311_component_probe,
+ .suspend = es8311_suspend,
+ .resume = es8311_resume,
+ .set_bias_level = es8311_set_bias_level,
+ .controls = es8311_snd_controls,
+ .num_controls = ARRAY_SIZE(es8311_snd_controls),
+ .dapm_widgets = es8311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8311_dapm_widgets),
+ .dapm_routes = es8311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8311_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int es8311_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8311_priv *es8311;
+
+ struct device *dev = &i2c_client->dev;
+
+ es8311 = devm_kzalloc(dev, sizeof(*es8311), GFP_KERNEL);
+ if (es8311 == NULL)
+ return -ENOMEM;
+
+ es8311->regmap =
+ devm_regmap_init_i2c(i2c_client, &es8311_regmap_config);
+ if (IS_ERR(es8311->regmap))
+ return PTR_ERR(es8311->regmap);
+
+ i2c_set_clientdata(i2c_client, es8311);
+
+ return devm_snd_soc_register_component(dev, &es8311_component_driver,
+ &es8311_dai, 1);
+}
+
+static const struct i2c_device_id es8311_id[] = {
+ { "es8311" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8311_id);
+
+static const struct of_device_id es8311_of_match[] = {
+ {
+ .compatible = "everest,es8311",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es8311_of_match);
+
+static struct i2c_driver es8311_i2c_driver = {
+ .driver = {
+ .name = "es8311",
+ .of_match_table = es8311_of_match,
+ },
+ .probe = es8311_i2c_probe,
+ .id_table = es8311_id,
+};
+
+module_i2c_driver(es8311_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8311 driver");
+MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8311.h b/sound/soc/codecs/es8311.h
new file mode 100644
index 000000000000..8a3105bb8443
--- /dev/null
+++ b/sound/soc/codecs/es8311.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8311.c -- es8311 ALSA SoC audio driver
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ *
+ * Author: Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#ifndef _ES8311_H
+#define _ES8311_H
+
+#include <linux/bitops.h>
+
+#define ES8311_RESET 0x00
+#define ES8311_RESET_CSM_ON BIT(7)
+#define ES8311_RESET_MSC BIT(6)
+#define ES8311_RESET_RST_MASK GENMASK(4, 0)
+
+/* Clock Manager Registers */
+#define ES8311_CLKMGR1 0x01
+#define ES8311_CLKMGR1_MCLK_SEL BIT(7)
+#define ES8311_CLKMGR1_MCLK_ON BIT(5)
+#define ES8311_CLKMGR1_BCLK_ON BIT(4)
+#define ES8311_CLKMGR1_CLKADC_ON_SHIFT 3
+#define ES8311_CLKMGR1_CLKDAC_ON_SHIFT 2
+#define ES8311_CLKMGR1_ANACLKADC_ON_SHIFT 1
+#define ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT 0
+#define ES8311_CLKMGR2 0x02
+#define ES8311_CLKMGR2_DIV_PRE_MASK GENMASK(7, 5)
+#define ES8311_CLKMGR2_DIV_PRE_SHIFT 5
+#define ES8311_CLKMGR2_DIV_PRE_MAX 0x07
+#define ES8311_CLKMGR2_MULT_PRE_MASK GENMASK(4, 3)
+#define ES8311_CLKMGR2_MULT_PRE_SHIFT 3
+#define ES8311_CLKMGR3 0x03
+#define ES8311_CLKMGR4 0x04
+#define ES8311_CLKMGR5 0x05
+#define ES8311_CLKMGR5_ADC_DIV_MASK GENMASK(7, 4)
+#define ES8311_CLKMGR5_ADC_DIV_SHIFT 4
+#define ES8311_CLKMGR5_DAC_DIV_MASK GENMASK(3, 0)
+#define ES8311_CLKMGR5_DAC_DIV_SHIFT 0
+#define ES8311_CLKMGR6 0x06
+#define ES8311_CLKMGR6_BCLK_INV BIT(5)
+#define ES8311_CLKMGR6_DIV_BCLK_MASK GENMASK(4, 0)
+#define ES8311_CLKMGR7 0x07
+#define ES8311_CLKMGR7_LRCLK_DIV_H_MASK GENMASK(3, 0)
+#define ES8311_CLKMGR8 0x08
+#define ES8311_CLKMGR_LRCLK_DIV_MAX 0x0FFF
+
+/* SDP Mode Registers */
+#define ES8311_SDP_IN 0x09
+#define ES8311_SDP_IN_SEL_SHIFT 7
+#define ES8311_SDP_OUT 0x0A
+/* Following values are the same for both SPD_IN and SDP_OUT */
+#define ES8311_SDP_MUTE_SHIFT 6
+#define ES8311_SDP_LRP BIT(5)
+#define ES8311_SDP_WL_MASK GENMASK(4, 2)
+#define ES8311_SDP_WL_SHIFT 2
+#define ES8311_SDP_WL_24 0x00
+#define ES8311_SDP_WL_20 0x01
+#define ES8311_SDP_WL_18 0x02
+#define ES8311_SDP_WL_16 0x03
+#define ES8311_SDP_WL_32 0x04
+#define ES8311_SDP_FMT_MASK GENMASK(1, 0)
+#define ES8311_SDP_FMT_I2S 0x00
+#define ES8311_SDP_FMT_LEFT_J 0x01
+#define ES8311_SDP_FMT_DSP 0x03
+
+/* System registers */
+#define ES8311_SYS1 0x0B
+#define ES8311_SYS2 0x0C
+#define ES8311_SYS3 0x0D
+#define ES8311_SYS3_PDN_ANA_SHIFT 7
+#define ES8311_SYS3_PDN_IBIASGEN_SHIFT 6
+#define ES8311_SYS3_PDN_ADCBIASGEN_SHIFT 5
+#define ES8311_SYS3_PDN_ADCVREFGEN_SHIFT 4
+#define ES8311_SYS3_PDN_DACVREFGEN_SHIFT 3
+#define ES8311_SYS3_PDN_VREF_SHIFT 2
+#define ES8311_SYS3_PDN_VMIDSEL_MASK GENMASK(1, 0)
+#define ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN 0
+#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED 1
+#define ES8311_SYS3_PDN_VMIDSEL_NORMAL_OPERATION 2
+#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_FAST_SPEED 3
+#define ES8311_SYS4 0x0E
+#define ES8311_SYS4_PDN_PGA_SHIFT 6
+#define ES8311_SYS4_PDN_MOD_SHIFT 5
+#define ES8311_SYS5 0x0F
+#define ES8311_SYS6 0x10
+#define ES8311_SYS7 0x11
+#define ES8311_SYS8 0x12
+#define ES8311_SYS8_PDN_DAC_SHIFT 1
+#define ES8311_SYS9 0x13
+#define ES8311_SYS9_HPSW_SHIFT 4
+#define ES8311_SYS10 0x14
+#define ES8311_SYS10_DMIC_ON_SHIFT 6
+#define ES8311_SYS10_LINESEL_SHIFT 4
+#define ES8311_SYS10_PGAGAIN_SHIFT 0
+#define ES8311_SYS10_PGAGAIN_MAX 0x0A
+
+/* ADC Registers*/
+#define ES8311_ADC1 0x15
+#define ES8311_ADC1_RAMPRATE_SHIFT 4
+#define ES8311_ADC2 0x16
+#define ES8311_ADC2_INV_SHIFT 4
+#define ES8311_ADC2_SCALE_SHIFT 0
+#define ES8311_ADC2_SCALE_MAX 0x07
+#define ES8311_ADC3 0x17
+#define ES8311_ADC3_VOLUME_SHIFT 0
+#define ES8311_ADC3_VOLUME_MAX 0xFF
+#define ES8311_ADC4 0x18
+#define ES8311_ADC4_ALC_EN_SHIFT 7
+#define ES8311_ADC4_AUTOMUTE_EN_SHIFT 6
+#define ES8311_ADC4_ALC_WINSIZE_SHIFT 0
+#define ES8311_ADC5 0x19
+#define ES8311_ADC5_ALC_MAXLEVEL_SHIFT 4
+#define ES8311_ADC5_ALC_MAXLEVEL_MAX 0x0F
+#define ES8311_ADC5_ALC_MINLEVEL_SHIFT 0
+#define ES8311_ADC5_ALC_MINLEVEL_MAX 0x0F
+#define ES8311_ADC6 0x1A
+#define ES8311_ADC6_AUTOMUTE_WS_SHIFT 4
+#define ES8311_ADC6_AUTOMUTE_NG_SHIFT 0
+#define ES8311_ADC6_AUTOMUTE_NG_MAX 0x0F
+
+#define ES8311_ADC7 0x1B
+#define ES8311_ADC7_AUTOMUTE_VOL_SHIFT 5
+#define ES8311_ADC7_AUTOMUTE_VOL_MAX 0x07
+#define ES8311_ADC8 0x1C
+#define ES8311_ADC8_EQBYPASS_SHIFT 6
+#define ES8311_ADC8_HPF_SHIFT 5
+
+/* DAC Registers */
+#define ES8311_DAC1 0x31
+#define ES8311_DAC1_DAC_DSMMUTE BIT(6)
+#define ES8311_DAC1_DAC_DEMMUTE BIT(5)
+#define ES8311_DAC2 0x32
+#define ES8311_DAC2_VOLUME_MAX 0xFF
+#define ES8311_DAC3 0x33
+#define ES8311_DAC4 0x34
+#define ES8311_DAC4_DRC_EN_SHIFT 7
+#define ES8311_DAC4_DRC_WINSIZE_SHIFT 0
+#define ES8311_DAC5 0x35
+#define ES8311_DAC5_DRC_MAXLEVEL_SHIFT 4
+#define ES8311_DAC5_DRC_MAXLEVEL_MAX 0x0F
+#define ES8311_DAC5_DRC_MINLEVEL_SHIFT 0
+#define ES8311_DAC5_DRC_MINLEVEL_MAX 0x0F
+#define ES8311_DAC6 0x37
+#define ES8311_DAC6_RAMPRATE_SHIFT 4
+#define ES8311_DAC6_EQBYPASS_SHIFT 3
+
+/* GPIO Registers */
+#define ES8311_GPIO 0x44
+#define ES8311_GPIO_ADC2DAC_SEL_SHIFT 7
+#define ES8311_GPIO_ADCDAT_SEL_SHIFT 4
+
+/* Chip Info Registers */
+#define ES8311_CHIPID1 0xFD /* 0x83 */
+#define ES8311_CHIPID2 0xFE /* 0x11 */
+#define ES8311_CHIPVER 0xFF
+
+#define ES8311_REG_MAX 0xFF
+
+#endif
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
new file mode 100644
index 000000000000..9245c33700de
--- /dev/null
+++ b/sound/soc/codecs/es8316.c
@@ -0,0 +1,933 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8316.c -- es8316 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>,
+ * Daniel Drake <drake@endlessm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include "es8316.h"
+
+/* In slave mode at single speed, the codec is documented as accepting 5
+ * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on
+ * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK).
+ */
+static const unsigned int supported_mclk_lrck_ratios[] = {
+ 256, 384, 400, 500, 512, 768, 1024
+};
+
+struct es8316_priv {
+ struct mutex lock;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int irq;
+ unsigned int sysclk;
+ /* ES83xx supports halving the MCLK so it supports twice as many rates
+ */
+ unsigned int allowed_rates[ARRAY_SIZE(supported_mclk_lrck_ratios) * 2];
+ struct snd_pcm_hw_constraint_list sysclk_constraints;
+ bool jd_inverted;
+};
+
+/*
+ * ES8316 controls
+ */
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv,
+ 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0),
+ 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0),
+ 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-350, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(250, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(700, 300, 0),
+ 8, 10, TLV_DB_SCALE_ITEM(1800, 300, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpout_vol_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-4800, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(-2400, 1200, 0),
+);
+
+static const char * const ng_type_txt[] =
+ { "Constant PGA Gain", "Mute ADC Output" };
+static const struct soc_enum ng_type =
+ SOC_ENUM_SINGLE(ES8316_ADC_ALC_NG, 6, 2, ng_type_txt);
+
+static const char * const adcpol_txt[] = { "Normal", "Invert" };
+static const struct soc_enum adcpol =
+ SOC_ENUM_SINGLE(ES8316_ADC_MUTE, 1, 2, adcpol_txt);
+static const char *const dacpol_txt[] =
+ { "Normal", "R Invert", "L Invert", "L + R Invert" };
+static const struct soc_enum dacpol =
+ SOC_ENUM_SINGLE(ES8316_DAC_SET1, 0, 4, dacpol_txt);
+
+static const struct snd_kcontrol_new es8316_snd_controls[] = {
+ SOC_DOUBLE_TLV("Headphone Playback Volume", ES8316_CPHP_ICAL_VOL,
+ 4, 0, 3, 1, hpout_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Mixer Volume", ES8316_HPMIX_VOL,
+ 4, 0, 11, 0, hpmixer_gain_tlv),
+
+ SOC_ENUM("Playback Polarity", dacpol),
+ SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL,
+ ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv),
+ SOC_SINGLE("DAC Soft Ramp Switch", ES8316_DAC_SET1, 4, 1, 1),
+ SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 3, 0),
+ SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0),
+ SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0),
+ SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0),
+ SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0),
+
+ SOC_ENUM("Capture Polarity", adcpol),
+ SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0),
+ SOC_SINGLE_TLV("ADC Capture Volume", ES8316_ADC_VOLUME,
+ 0, 0xc0, 1, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8316_ADC_PGAGAIN,
+ 4, 10, 0, adc_pga_gain_tlv),
+ SOC_SINGLE("ADC Soft Ramp Switch", ES8316_ADC_MUTE, 4, 1, 0),
+ SOC_SINGLE("ADC Double Fs Switch", ES8316_ADC_DMIC, 4, 1, 0),
+
+ SOC_SINGLE("ALC Capture Switch", ES8316_ADC_ALC1, 6, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", ES8316_ADC_ALC1, 0, 28, 0,
+ alc_max_gain_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0,
+ alc_min_gain_tlv),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 0,
+ alc_target_tlv),
+ SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8316_ADC_ALC4, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Switch", ES8316_ADC_ALC_NG,
+ 5, 1, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Threshold", ES8316_ADC_ALC_NG,
+ 0, 31, 0),
+ SOC_ENUM("ALC Capture Noise Gate Type", ng_type),
+};
+
+/* Analog Input Mux */
+static const char * const es8316_analog_in_txt[] = {
+ "lin1-rin1",
+ "lin2-rin2",
+ "lin1-rin1 with 20db Boost",
+ "lin2-rin2 with 20db Boost"
+};
+static const unsigned int es8316_analog_in_values[] = { 0, 1, 2, 3 };
+static const struct soc_enum es8316_analog_input_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8316_ADC_PDN_LINSEL, 4, 3,
+ ARRAY_SIZE(es8316_analog_in_txt),
+ es8316_analog_in_txt,
+ es8316_analog_in_values);
+static const struct snd_kcontrol_new es8316_analog_in_mux_controls =
+ SOC_DAPM_ENUM("Route", es8316_analog_input_enum);
+
+static const char * const es8316_dmic_txt[] = {
+ "dmic disable",
+ "dmic data at high level",
+ "dmic data at low level",
+};
+static const unsigned int es8316_dmic_values[] = { 0, 2, 3 };
+static const struct soc_enum es8316_dmic_src_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3,
+ ARRAY_SIZE(es8316_dmic_txt),
+ es8316_dmic_txt,
+ es8316_dmic_values);
+static const struct snd_kcontrol_new es8316_dmic_src_controls =
+ SOC_DAPM_ENUM("Route", es8316_dmic_src_enum);
+
+/* hp mixer mux */
+static const char * const es8316_hpmux_texts[] = {
+ "lin1-rin1",
+ "lin2-rin2",
+ "lin-rin with Boost",
+ "lin-rin with Boost and PGA"
+};
+
+static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL,
+ 4, es8316_hpmux_texts);
+
+static const struct snd_kcontrol_new es8316_left_hpmux_controls =
+ SOC_DAPM_ENUM("Route", es8316_left_hpmux_enum);
+
+static SOC_ENUM_SINGLE_DECL(es8316_right_hpmux_enum, ES8316_HPMIX_SEL,
+ 0, es8316_hpmux_texts);
+
+static const struct snd_kcontrol_new es8316_right_hpmux_controls =
+ SOC_DAPM_ENUM("Route", es8316_right_hpmux_enum);
+
+/* headphone Output Mixer */
+static const struct snd_kcontrol_new es8316_out_left_mix[] = {
+ SOC_DAPM_SINGLE("LLIN Switch", ES8316_HPMIX_SWITCH, 6, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", ES8316_HPMIX_SWITCH, 7, 1, 0),
+};
+static const struct snd_kcontrol_new es8316_out_right_mix[] = {
+ SOC_DAPM_SINGLE("RLIN Switch", ES8316_HPMIX_SWITCH, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", ES8316_HPMIX_SWITCH, 3, 1, 0),
+};
+
+/* DAC data source mux */
+static const char * const es8316_dacsrc_texts[] = {
+ "LDATA TO LDAC, RDATA TO RDAC",
+ "LDATA TO LDAC, LDATA TO RDAC",
+ "RDATA TO LDAC, RDATA TO RDAC",
+ "RDATA TO LDAC, LDATA TO RDAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1,
+ 6, es8316_dacsrc_texts);
+
+static const struct snd_kcontrol_new es8316_dacsrc_mux_controls =
+ SOC_DAPM_ENUM("Route", es8316_dacsrc_mux_enum);
+
+static const struct snd_soc_dapm_widget es8316_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Bias", ES8316_SYS_PDN, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Analog power", ES8316_SYS_PDN, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8316_SYS_PDN, 5, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+
+ /* Input Mux */
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_analog_in_mux_controls),
+
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8316_SYS_PDN, 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC bias", ES8316_SYS_PDN, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", ES8316_CLKMGR_CLKSW, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line input PGA", ES8316_ADC_PDN_LINSEL,
+ 7, 1, NULL, 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8316_ADC_PDN_LINSEL, 6, 1),
+ SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_dmic_src_controls),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1,
+ ES8316_SERDATA_ADC, 6, 1),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("DAC Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_dacsrc_mux_controls),
+
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8316_SYS_PDN, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Clock", ES8316_CLKMGR_CLKSW, 2, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8316_DAC_PDN, 0, 1),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ES8316_DAC_PDN, 4, 1),
+
+ /* Headphone Output Side */
+ SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_left_hpmux_controls),
+ SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &es8316_right_hpmux_controls),
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", ES8316_HPMIX_PDN,
+ 5, 1, &es8316_out_left_mix[0],
+ ARRAY_SIZE(es8316_out_left_mix)),
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", ES8316_HPMIX_PDN,
+ 1, 1, &es8316_out_right_mix[0],
+ ARRAY_SIZE(es8316_out_right_mix)),
+ SND_SOC_DAPM_PGA("Left Headphone Mixer Out", ES8316_HPMIX_PDN,
+ 4, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Mixer Out", ES8316_HPMIX_PDN,
+ 0, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("Left Headphone Charge Pump", ES8316_CPHP_OUTEN,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Right Headphone Charge Pump", ES8316_CPHP_OUTEN,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8316_CPHP_PDN2,
+ 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump Clock", ES8316_CLKMGR_CLKSW,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("Left Headphone Driver", ES8316_CPHP_OUTEN,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Right Headphone Driver", ES8316_CPHP_OUTEN,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Out", ES8316_CPHP_PDN1, 2, 1, NULL, 0),
+
+ /* pdn_Lical and pdn_Rical bits are documented as Reserved, but must
+ * be explicitly unset in order to enable HP output
+ */
+ SND_SOC_DAPM_SUPPLY("Left Headphone ical", ES8316_CPHP_ICAL_VOL,
+ 7, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right Headphone ical", ES8316_CPHP_ICAL_VOL,
+ 3, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8316_dapm_routes[] = {
+ /* Recording */
+ {"MIC1", NULL, "Mic Bias"},
+ {"MIC2", NULL, "Mic Bias"},
+ {"MIC1", NULL, "Bias"},
+ {"MIC2", NULL, "Bias"},
+ {"MIC1", NULL, "Analog power"},
+ {"MIC2", NULL, "Analog power"},
+
+ {"Differential Mux", "lin1-rin1", "MIC1"},
+ {"Differential Mux", "lin2-rin2", "MIC2"},
+ {"Line input PGA", NULL, "Differential Mux"},
+
+ {"Mono ADC", NULL, "ADC Clock"},
+ {"Mono ADC", NULL, "ADC Vref"},
+ {"Mono ADC", NULL, "ADC bias"},
+ {"Mono ADC", NULL, "Line input PGA"},
+
+ /* It's not clear why, but to avoid recording only silence,
+ * the DAC clock must be running for the ADC to work.
+ */
+ {"Mono ADC", NULL, "DAC Clock"},
+
+ {"Digital Mic Mux", "dmic disable", "Mono ADC"},
+
+ {"I2S OUT", NULL, "Digital Mic Mux"},
+
+ /* Playback */
+ {"DAC Source Mux", "LDATA TO LDAC, RDATA TO RDAC", "I2S IN"},
+
+ {"Left DAC", NULL, "DAC Clock"},
+ {"Right DAC", NULL, "DAC Clock"},
+
+ {"Left DAC", NULL, "DAC Vref"},
+ {"Right DAC", NULL, "DAC Vref"},
+
+ {"Left DAC", NULL, "DAC Source Mux"},
+ {"Right DAC", NULL, "DAC Source Mux"},
+
+ {"Left Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"},
+ {"Right Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"},
+
+ {"Left Headphone Mixer", "LLIN Switch", "Left Headphone Mux"},
+ {"Left Headphone Mixer", "Left DAC Switch", "Left DAC"},
+
+ {"Right Headphone Mixer", "RLIN Switch", "Right Headphone Mux"},
+ {"Right Headphone Mixer", "Right DAC Switch", "Right DAC"},
+
+ {"Left Headphone Mixer Out", NULL, "Left Headphone Mixer"},
+ {"Right Headphone Mixer Out", NULL, "Right Headphone Mixer"},
+
+ {"Left Headphone Charge Pump", NULL, "Left Headphone Mixer Out"},
+ {"Right Headphone Charge Pump", NULL, "Right Headphone Mixer Out"},
+
+ {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump"},
+ {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump"},
+
+ {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"},
+ {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"},
+
+ {"Left Headphone Driver", NULL, "Left Headphone Charge Pump"},
+ {"Right Headphone Driver", NULL, "Right Headphone Charge Pump"},
+
+ {"HPOL", NULL, "Left Headphone Driver"},
+ {"HPOR", NULL, "Right Headphone Driver"},
+
+ {"HPOL", NULL, "Left Headphone ical"},
+ {"HPOR", NULL, "Right Headphone ical"},
+
+ {"Headphone Out", NULL, "Bias"},
+ {"Headphone Out", NULL, "Analog power"},
+ {"HPOL", NULL, "Headphone Out"},
+ {"HPOR", NULL, "Headphone Out"},
+};
+
+static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ int i, ret;
+ int count = 0;
+
+ es8316->sysclk = freq;
+ es8316->sysclk_constraints.list = NULL;
+ es8316->sysclk_constraints.count = 0;
+
+ if (freq == 0)
+ return 0;
+
+ ret = clk_set_rate(es8316->mclk, freq);
+ if (ret)
+ return ret;
+
+ /* Limit supported sample rates to ones that can be autodetected
+ * by the codec running in slave mode.
+ */
+ for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (freq % ratio == 0)
+ es8316->allowed_rates[count++] = freq / ratio;
+
+ /* We also check if the halved MCLK produces a valid rate
+ * since the codec supports halving the MCLK.
+ */
+ if ((freq / ratio) % 2 == 0)
+ es8316->allowed_rates[count++] = freq / ratio / 2;
+ }
+
+ if (count) {
+ es8316->sysclk_constraints.list = es8316->allowed_rates;
+ es8316->sysclk_constraints.count = count;
+ }
+
+ return 0;
+}
+
+static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 serdata1 = 0;
+ u8 serdata2 = 0;
+ u8 clksw;
+ u8 mask;
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP)
+ serdata1 |= ES8316_SERDATA1_MASTER;
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
+ dev_err(component->dev, "Codec driver only supports I2S format\n");
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ serdata1 |= ES8316_SERDATA1_BCLK_INV;
+ serdata2 |= ES8316_SERDATA2_ADCLRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ serdata1 |= ES8316_SERDATA1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ serdata2 |= ES8316_SERDATA2_ADCLRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV;
+ snd_soc_component_update_bits(component, ES8316_SERDATA1, mask, serdata1);
+
+ mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP;
+ snd_soc_component_update_bits(component, ES8316_SERDATA_ADC, mask, serdata2);
+ snd_soc_component_update_bits(component, ES8316_SERDATA_DAC, mask, serdata2);
+
+ /* Enable BCLK and MCLK inputs in slave mode */
+ clksw = ES8316_CLKMGR_CLKSW_MCLK_ON | ES8316_CLKMGR_CLKSW_BCLK_ON;
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, clksw, clksw);
+
+ return 0;
+}
+
+static int es8316_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ if (es8316->sysclk_constraints.list)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &es8316->sysclk_constraints);
+
+ return 0;
+}
+
+static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ u8 wordlen = 0;
+ u8 bclk_divider;
+ u16 lrck_divider;
+ int i;
+ unsigned int clk = es8316->sysclk / 2;
+ bool clk_valid = false;
+
+ /* We will start with halved sysclk and see if we can use it
+ * for proper clocking. This is to minimise the risk of running
+ * the CODEC with a too high frequency. We have an SKU where
+ * the sysclk frequency is 48Mhz and this causes the sound to be
+ * sped up. If we can run with a halved sysclk, we will use it,
+ * if we can't use it, then full sysclk will be used.
+ */
+ do {
+ /* Validate supported sample rates that are autodetected from MCLK */
+ for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (clk % ratio != 0)
+ continue;
+ if (clk / ratio == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(supported_mclk_lrck_ratios)) {
+ if (clk == es8316->sysclk)
+ return -EINVAL;
+ clk = es8316->sysclk;
+ } else {
+ clk_valid = true;
+ }
+ } while (!clk_valid);
+
+ if (clk != es8316->sysclk) {
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW,
+ ES8316_CLKMGR_CLKSW_MCLK_DIV,
+ ES8316_CLKMGR_CLKSW_MCLK_DIV);
+ }
+
+ lrck_divider = clk / params_rate(params);
+ bclk_divider = lrck_divider / 4;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ wordlen = ES8316_SERDATA2_LEN_16;
+ bclk_divider /= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ wordlen = ES8316_SERDATA2_LEN_20;
+ bclk_divider /= 20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ wordlen = ES8316_SERDATA2_LEN_24;
+ bclk_divider /= 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ wordlen = ES8316_SERDATA2_LEN_32;
+ bclk_divider /= 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ES8316_SERDATA_DAC,
+ ES8316_SERDATA2_LEN_MASK, wordlen);
+ snd_soc_component_update_bits(component, ES8316_SERDATA_ADC,
+ ES8316_SERDATA2_LEN_MASK, wordlen);
+ snd_soc_component_update_bits(component, ES8316_SERDATA1, 0x1f, bclk_divider);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV1, 0x0f, lrck_divider >> 8);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV2, 0xff, lrck_divider & 0xff);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV1, 0x0f, lrck_divider >> 8);
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV2, 0xff, lrck_divider & 0xff);
+ return 0;
+}
+
+static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ snd_soc_component_update_bits(dai->component, ES8316_DAC_SET1, 0x20,
+ mute ? 0x20 : 0);
+ return 0;
+}
+
+#define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8316_ops = {
+ .startup = es8316_pcm_startup,
+ .hw_params = es8316_pcm_hw_params,
+ .set_fmt = es8316_set_dai_fmt,
+ .set_sysclk = es8316_set_dai_sysclk,
+ .mute_stream = es8316_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8316_dai = {
+ .name = "ES8316 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ES8316_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ES8316_FORMATS,
+ },
+ .ops = &es8316_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8316_enable_micbias_for_mic_gnd_short_detect(
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ msleep(20);
+}
+
+static void es8316_disable_micbias_for_mic_gnd_short_detect(
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Bias");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t es8316_irq(int irq, void *data)
+{
+ struct es8316_priv *es8316 = data;
+ struct snd_soc_component *comp = es8316->component;
+ unsigned int flags;
+
+ mutex_lock(&es8316->lock);
+
+ regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+ if (flags == 0x00)
+ goto out; /* Powered-down / reset */
+
+ /* Catch spurious IRQ before set_jack is called */
+ if (!es8316->jack)
+ goto out;
+
+ if (es8316->jd_inverted)
+ flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
+
+ dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
+ if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
+ /* Jack removed, or spurious IRQ? */
+ if (es8316->jack->status & SND_JACK_MICROPHONE)
+ es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+
+ if (es8316->jack->status & SND_JACK_HEADPHONE) {
+ snd_soc_jack_report(es8316->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ dev_dbg(comp->dev, "jack unplugged\n");
+ }
+ } else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) {
+ /* Jack inserted, determine type */
+ es8316_enable_micbias_for_mic_gnd_short_detect(comp);
+ regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+ if (es8316->jd_inverted)
+ flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
+ dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
+ if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
+ /* Jack unplugged underneath us */
+ es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+ } else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
+ /* Open, headset */
+ snd_soc_jack_report(es8316->jack,
+ SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ /* Keep mic-gnd-short detection on for button press */
+ } else {
+ /* Shorted, headphones */
+ snd_soc_jack_report(es8316->jack,
+ SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ /* No longer need mic-gnd-short detection */
+ es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+ }
+ } else if (es8316->jack->status & SND_JACK_MICROPHONE) {
+ /* Interrupt while jack inserted, report button state */
+ if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
+ /* Open, button release */
+ snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
+ } else {
+ /* Short, button press */
+ snd_soc_jack_report(es8316->jack,
+ SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ }
+ }
+
+out:
+ mutex_unlock(&es8316->lock);
+ return IRQ_HANDLED;
+}
+
+static void es8316_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * Init es8316->jd_inverted here and not in the probe, as we cannot
+ * guarantee that the bytchr-es8316 driver, which might set this
+ * property, will probe before us.
+ */
+ es8316->jd_inverted = device_property_read_bool(component->dev,
+ "everest,jack-detect-inverted");
+
+ mutex_lock(&es8316->lock);
+
+ es8316->jack = jack;
+
+ if (es8316->jack->status & SND_JACK_MICROPHONE)
+ es8316_enable_micbias_for_mic_gnd_short_detect(component);
+
+ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
+ ES8316_GPIO_ENABLE_INTERRUPT,
+ ES8316_GPIO_ENABLE_INTERRUPT);
+
+ mutex_unlock(&es8316->lock);
+
+ /* Enable irq and sync initial jack state */
+ enable_irq(es8316->irq);
+ es8316_irq(es8316->irq, es8316);
+}
+
+static void es8316_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ if (!es8316->jack)
+ return; /* Already disabled (or never enabled) */
+
+ disable_irq(es8316->irq);
+
+ mutex_lock(&es8316->lock);
+
+ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
+ ES8316_GPIO_ENABLE_INTERRUPT, 0);
+
+ if (es8316->jack->status & SND_JACK_MICROPHONE) {
+ es8316_disable_micbias_for_mic_gnd_short_detect(component);
+ snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
+ }
+
+ es8316->jack = NULL;
+
+ mutex_unlock(&es8316->lock);
+}
+
+static int es8316_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ es8316_enable_jack_detect(component, jack);
+ else
+ es8316_disable_jack_detect(component);
+
+ return 0;
+}
+
+static int es8316_probe(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8316->component = component;
+
+ es8316->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8316->mclk)) {
+ dev_err(component->dev, "unable to get mclk\n");
+ return PTR_ERR(es8316->mclk);
+ }
+ if (!es8316->mclk)
+ dev_warn(component->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8316->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to enable mclk\n");
+ return ret;
+ }
+
+ /* Reset codec and enable current state machine */
+ snd_soc_component_write(component, ES8316_RESET, 0x3f);
+ usleep_range(5000, 5500);
+ snd_soc_component_write(component, ES8316_RESET, ES8316_RESET_CSM_ON);
+ msleep(30);
+
+ /*
+ * Documentation is unclear, but this value from the vendor driver is
+ * needed otherwise audio output is silent.
+ */
+ snd_soc_component_write(component, ES8316_SYS_VMIDSEL, 0xff);
+
+ /*
+ * Documentation for this register is unclear and incomplete,
+ * but here is a vendor-provided value that improves volume
+ * and quality for Intel CHT platforms.
+ */
+ snd_soc_component_write(component, ES8316_CLKMGR_ADCOSR, 0x32);
+
+ return 0;
+}
+
+static void es8316_remove(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8316->mclk);
+}
+
+static int es8316_resume(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8316->regmap, false);
+ regcache_sync(es8316->regmap);
+
+ return 0;
+}
+
+static int es8316_suspend(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8316->regmap, true);
+ regcache_mark_dirty(es8316->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8316 = {
+ .probe = es8316_probe,
+ .remove = es8316_remove,
+ .resume = es8316_resume,
+ .suspend = es8316_suspend,
+ .set_jack = es8316_set_jack,
+ .controls = es8316_snd_controls,
+ .num_controls = ARRAY_SIZE(es8316_snd_controls),
+ .dapm_widgets = es8316_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8316_dapm_widgets),
+ .dapm_routes = es8316_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8316_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static bool es8316_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8316_GPIO_FLAG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config es8316_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x53,
+ .volatile_reg = es8316_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int es8316_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct device *dev = &i2c_client->dev;
+ struct es8316_priv *es8316;
+ int ret;
+
+ es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
+ GFP_KERNEL);
+ if (es8316 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8316);
+
+ es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
+ if (IS_ERR(es8316->regmap))
+ return PTR_ERR(es8316->regmap);
+
+ es8316->irq = i2c_client->irq;
+ mutex_init(&es8316->lock);
+
+ if (es8316->irq > 0) {
+ ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ "es8316", es8316);
+ if (ret) {
+ dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
+ es8316->irq = -ENXIO;
+ }
+ }
+
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_es8316,
+ &es8316_dai, 1);
+}
+
+static const struct i2c_device_id es8316_i2c_id[] = {
+ {"es8316" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, es8316_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8316_of_match[] = {
+ { .compatible = "everest,es8316", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, es8316_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8316_acpi_match[] = {
+ {"ESSX8316", 0},
+ {"ESSX8336", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, es8316_acpi_match);
+#endif
+
+static struct i2c_driver es8316_i2c_driver = {
+ .driver = {
+ .name = "es8316",
+ .acpi_match_table = ACPI_PTR(es8316_acpi_match),
+ .of_match_table = of_match_ptr(es8316_of_match),
+ },
+ .probe = es8316_i2c_probe,
+ .id_table = es8316_i2c_id,
+};
+module_i2c_driver(es8316_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES8316 ALSA SoC Codec Driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h
new file mode 100644
index 000000000000..0ff16f948690
--- /dev/null
+++ b/sound/soc/codecs/es8316.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Author: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#ifndef _ES8316_H
+#define _ES8316_H
+
+/*
+ * ES8316 register space
+ */
+
+/* Reset Control */
+#define ES8316_RESET 0x00
+
+/* Clock Management */
+#define ES8316_CLKMGR_CLKSW 0x01
+#define ES8316_CLKMGR_CLKSEL 0x02
+#define ES8316_CLKMGR_ADCOSR 0x03
+#define ES8316_CLKMGR_ADCDIV1 0x04
+#define ES8316_CLKMGR_ADCDIV2 0x05
+#define ES8316_CLKMGR_DACDIV1 0x06
+#define ES8316_CLKMGR_DACDIV2 0x07
+#define ES8316_CLKMGR_CPDIV 0x08
+
+/* Serial Data Port Control */
+#define ES8316_SERDATA1 0x09
+#define ES8316_SERDATA_ADC 0x0a
+#define ES8316_SERDATA_DAC 0x0b
+
+/* System Control */
+#define ES8316_SYS_VMIDSEL 0x0c
+#define ES8316_SYS_PDN 0x0d
+#define ES8316_SYS_LP1 0x0e
+#define ES8316_SYS_LP2 0x0f
+#define ES8316_SYS_VMIDLOW 0x10
+#define ES8316_SYS_VSEL 0x11
+#define ES8316_SYS_REF 0x12
+
+/* Headphone Mixer */
+#define ES8316_HPMIX_SEL 0x13
+#define ES8316_HPMIX_SWITCH 0x14
+#define ES8316_HPMIX_PDN 0x15
+#define ES8316_HPMIX_VOL 0x16
+
+/* Charge Pump Headphone driver */
+#define ES8316_CPHP_OUTEN 0x17
+#define ES8316_CPHP_ICAL_VOL 0x18
+#define ES8316_CPHP_PDN1 0x19
+#define ES8316_CPHP_PDN2 0x1a
+#define ES8316_CPHP_LDOCTL 0x1b
+
+/* Calibration */
+#define ES8316_CAL_TYPE 0x1c
+#define ES8316_CAL_SET 0x1d
+#define ES8316_CAL_HPLIV 0x1e
+#define ES8316_CAL_HPRIV 0x1f
+#define ES8316_CAL_HPLMV 0x20
+#define ES8316_CAL_HPRMV 0x21
+
+/* ADC Control */
+#define ES8316_ADC_PDN_LINSEL 0x22
+#define ES8316_ADC_PGAGAIN 0x23
+#define ES8316_ADC_D2SEPGA 0x24
+#define ES8316_ADC_DMIC 0x25
+#define ES8316_ADC_MUTE 0x26
+#define ES8316_ADC_VOLUME 0x27
+#define ES8316_ADC_ALC1 0x29
+#define ES8316_ADC_ALC2 0x2a
+#define ES8316_ADC_ALC3 0x2b
+#define ES8316_ADC_ALC4 0x2c
+#define ES8316_ADC_ALC5 0x2d
+#define ES8316_ADC_ALC_NG 0x2e
+
+/* DAC Control */
+#define ES8316_DAC_PDN 0x2f
+#define ES8316_DAC_SET1 0x30
+#define ES8316_DAC_SET2 0x31
+#define ES8316_DAC_SET3 0x32
+#define ES8316_DAC_VOLL 0x33
+#define ES8316_DAC_VOLR 0x34
+
+/* GPIO */
+#define ES8316_GPIO_SEL 0x4d
+#define ES8316_GPIO_DEBOUNCE 0x4e
+#define ES8316_GPIO_FLAG 0x4f
+
+/* Test mode */
+#define ES8316_TESTMODE 0x50
+#define ES8316_TEST1 0x51
+#define ES8316_TEST2 0x52
+#define ES8316_TEST3 0x53
+
+/*
+ * Field definitions
+ */
+
+/* ES8316_RESET */
+#define ES8316_RESET_CSM_ON 0x80
+
+/* ES8316_CLKMGR_CLKSW */
+#define ES8316_CLKMGR_CLKSW_MCLK_ON 0x40
+#define ES8316_CLKMGR_CLKSW_BCLK_ON 0x20
+
+/* ES8316_SERDATA1 */
+#define ES8316_SERDATA1_MASTER 0x80
+#define ES8316_SERDATA1_BCLK_INV 0x20
+
+/* ES8316_SERDATA_ADC and _DAC */
+#define ES8316_SERDATA2_FMT_MASK 0x3
+#define ES8316_SERDATA2_FMT_I2S 0x00
+#define ES8316_SERDATA2_FMT_LEFTJ 0x01
+#define ES8316_SERDATA2_FMT_RIGHTJ 0x02
+#define ES8316_SERDATA2_FMT_PCM 0x03
+#define ES8316_SERDATA2_ADCLRP 0x20
+#define ES8316_SERDATA2_LEN_MASK 0x1c
+#define ES8316_SERDATA2_LEN_24 0x00
+#define ES8316_SERDATA2_LEN_20 0x04
+#define ES8316_SERDATA2_LEN_18 0x08
+#define ES8316_SERDATA2_LEN_16 0x0c
+#define ES8316_SERDATA2_LEN_32 0x10
+
+/* ES8316_GPIO_DEBOUNCE */
+#define ES8316_GPIO_ENABLE_INTERRUPT 0x02
+
+/* ES8316_GPIO_FLAG */
+#define ES8316_GPIO_FLAG_GM_NOT_SHORTED 0x02
+#define ES8316_GPIO_FLAG_HP_NOT_INSERTED 0x04
+
+/* ES8316_CLKMGR_CLKSW */
+#define ES8316_CLKMGR_CLKSW_MCLK_DIV 0x80
+
+#endif
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
new file mode 100644
index 000000000000..eb85b71e87f3
--- /dev/null
+++ b/sound/soc/codecs/es8323.c
@@ -0,0 +1,791 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8323.c -- es8323 ALSA SoC audio driver
+//
+// Copyright 2024 Rockchip Electronics Co. Ltd.
+// Copyright 2024 Everest Semiconductor Co.,Ltd.
+// Copyright 2024 Loongson Technology Co.,Ltd.
+//
+// Author: Mark Brown <broonie@kernel.org>
+// Jianqun Xu <jay.xu@rock-chips.com>
+// Nickey Yang <nickey.yang@rock-chips.com>
+// Further cleanup and restructuring by:
+// Binbin Zhou <zhoubinbin@loongson.cn>
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "es8323.h"
+
+struct es8323_priv {
+ unsigned int sysclk;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ struct snd_soc_component *component;
+};
+
+/* es8323 register cache */
+static const struct reg_default es8323_reg_defaults[] = {
+ { ES8323_CONTROL1, 0x06 },
+ { ES8323_CONTROL2, 0x1c },
+ { ES8323_CHIPPOWER, 0xc3 },
+ { ES8323_ADCPOWER, 0xfc },
+ { ES8323_DACPOWER, 0xc0 },
+ { ES8323_CHIPLOPOW1, 0x00 },
+ { ES8323_CHIPLOPOW2, 0x00 },
+ { ES8323_ANAVOLMANAG, 0x7c },
+ { ES8323_MASTERMODE, 0x80 },
+ { ES8323_ADCCONTROL1, 0x00 },
+ { ES8323_ADCCONTROL2, 0x00 },
+ { ES8323_ADCCONTROL3, 0x06 },
+ { ES8323_ADCCONTROL4, 0x00 },
+ { ES8323_ADCCONTROL5, 0x06 },
+ { ES8323_ADCCONTROL6, 0x30 },
+ { ES8323_ADC_MUTE, 0x30 },
+ { ES8323_LADC_VOL, 0xc0 },
+ { ES8323_RADC_VOL, 0xc0 },
+ { ES8323_ADCCONTROL10, 0x38 },
+ { ES8323_ADCCONTROL11, 0xb0 },
+ { ES8323_ADCCONTROL12, 0x32 },
+ { ES8323_ADCCONTROL13, 0x06 },
+ { ES8323_ADCCONTROL14, 0x00 },
+ { ES8323_DACCONTROL1, 0x00 },
+ { ES8323_DACCONTROL2, 0x06 },
+ { ES8323_DAC_MUTE, 0x30 },
+ { ES8323_LDAC_VOL, 0xc0 },
+ { ES8323_RDAC_VOL, 0xc0 },
+ { ES8323_DACCONTROL6, 0x08 },
+ { ES8323_DACCONTROL7, 0x06 },
+ { ES8323_DACCONTROL8, 0x1f },
+ { ES8323_DACCONTROL9, 0xf7 },
+ { ES8323_DACCONTROL10, 0xfd },
+ { ES8323_DACCONTROL11, 0xff },
+ { ES8323_DACCONTROL12, 0x1f },
+ { ES8323_DACCONTROL13, 0xf7 },
+ { ES8323_DACCONTROL14, 0xfd },
+ { ES8323_DACCONTROL15, 0xff },
+ { ES8323_DACCONTROL16, 0x00 },
+ { ES8323_DACCONTROL17, 0x38 },
+ { ES8323_DACCONTROL18, 0x38 },
+ { ES8323_DACCONTROL19, 0x38 },
+ { ES8323_DACCONTROL20, 0x38 },
+ { ES8323_DACCONTROL21, 0x38 },
+ { ES8323_DACCONTROL22, 0x38 },
+ { ES8323_DACCONTROL23, 0x00 },
+ { ES8323_LOUT1_VOL, 0x00 },
+ { ES8323_ROUT1_VOL, 0x00 },
+};
+
+static const char *const es8323_stereo_3d_texts[] = { "No 3D ", "Level 1", "Level 2", "Level 3",
+ "Level 4", "Level 5", "Level 6", "Level 7" };
+static SOC_ENUM_SINGLE_DECL(es8323_stereo_3d_enum, ES8323_DACCONTROL7, 2, es8323_stereo_3d_texts);
+
+static const char *const es8323_alc_func_texts[] = { "Off", "Right", "Left", "Stereo" };
+static SOC_ENUM_SINGLE_DECL(es8323_alc_function_enum,
+ ES8323_ADCCONTROL10, 6, es8323_alc_func_texts);
+
+static const char *const es8323_ng_type_texts[] = { "Constant PGA Gain", "Mute ADC Output" };
+static SOC_ENUM_SINGLE_DECL(es8323_alc_ng_type_enum, ES8323_ADCCONTROL14, 1, es8323_ng_type_texts);
+
+static const char *const es8323_deemph_texts[] = { "None", "32Khz", "44.1Khz", "48Khz" };
+static SOC_ENUM_SINGLE_DECL(es8323_playback_deemphasis_enum,
+ ES8323_DACCONTROL6, 6, es8323_deemph_texts);
+
+static const char *const es8323_adcpol_texts[] = { "Normal", "L Invert",
+ "R Invert", "L + R Invert" };
+static SOC_ENUM_SINGLE_DECL(es8323_capture_polarity_enum,
+ ES8323_ADCCONTROL6, 6, es8323_adcpol_texts);
+
+static const DECLARE_TLV_DB_SCALE(es8323_adc_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(es8323_dac_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(es8323_out_tlv, -4500, 150, 0);
+static const DECLARE_TLV_DB_SCALE(es8323_bypass_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(es8323_bypass_tlv2, -15, 300, 0);
+
+static const struct snd_kcontrol_new es8323_snd_controls[] = {
+ SOC_ENUM("3D Mode", es8323_stereo_3d_enum),
+ SOC_ENUM("ALC Capture Function", es8323_alc_function_enum),
+ SOC_ENUM("ALC Capture NG Type", es8323_alc_ng_type_enum),
+ SOC_ENUM("Playback De-emphasis", es8323_playback_deemphasis_enum),
+ SOC_ENUM("Capture Polarity", es8323_capture_polarity_enum),
+ SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0),
+ SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0),
+ SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0),
+ SOC_SINGLE("Capture Mute Switch", ES8323_ADC_MUTE, 2, 1, 0),
+ SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8,
+ 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0,
+ 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3,
+ 7, 1, es8323_bypass_tlv2),
+ SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20,
+ 3, 7, 1, es8323_bypass_tlv2),
+ SOC_DOUBLE_R_TLV("PCM Volume", ES8323_LDAC_VOL, ES8323_RDAC_VOL,
+ 0, 192, 1, es8323_dac_tlv),
+ SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_LADC_VOL,
+ ES8323_RADC_VOL, 0, 192, 1, es8323_adc_tlv),
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_LOUT1_VOL,
+ ES8323_ROUT1_VOL, 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_LOUT2_VOL,
+ ES8323_ROUT2_VOL, 0, 33, 0, es8323_out_tlv),
+};
+
+/* Left DAC Route */
+static const char *const es8323_pga_sell[] = { "Line 1L", "Line 2L", "NC", "DifferentialL" };
+static SOC_ENUM_SINGLE_DECL(es8323_left_dac_enum, ES8323_ADCCONTROL2, 6, es8323_pga_sell);
+static const struct snd_kcontrol_new es8323_left_dac_mux_controls =
+ SOC_DAPM_ENUM("Left DAC Route", es8323_left_dac_enum);
+
+/* Right DAC Route */
+static const char *const es8323_pga_selr[] = { "Line 1R", "Line 2R", "NC", "DifferentialR" };
+static SOC_ENUM_SINGLE_DECL(es8323_right_dac_enum, ES8323_ADCCONTROL2, 4, es8323_pga_selr);
+static const struct snd_kcontrol_new es8323_right_dac_mux_controls =
+ SOC_DAPM_ENUM("Right DAC Route", es8323_right_dac_enum);
+
+/* Left Line Mux */
+static const char *const es8323_lin_sell[] = { "Line 1L", "Line 2L", "NC", "MicL" };
+static SOC_ENUM_SINGLE_DECL(es8323_llin_enum, ES8323_DACCONTROL16, 3, es8323_lin_sell);
+static const struct snd_kcontrol_new es8323_left_line_controls =
+ SOC_DAPM_ENUM("LLIN Mux", es8323_llin_enum);
+
+/* Right Line Mux */
+static const char *const es8323_lin_selr[] = { "Line 1R", "Line 2R", "NC", "MicR" };
+static SOC_ENUM_SINGLE_DECL(es8323_rlin_enum, ES8323_DACCONTROL16, 0, es8323_lin_selr);
+static const struct snd_kcontrol_new es8323_right_line_controls =
+ SOC_DAPM_ENUM("RLIN Mux", es8323_rlin_enum);
+
+/* Differential Mux */
+static const char *const es8323_diffmux_sel[] = { "Line 1", "Line 2" };
+static SOC_ENUM_SINGLE_DECL(es8323_diffmux_enum, ES8323_ADCCONTROL3, 7, es8323_diffmux_sel);
+static const struct snd_kcontrol_new es8323_diffmux_controls =
+ SOC_DAPM_ENUM("Route2", es8323_diffmux_enum);
+
+/* Mono ADC Mux */
+static const char *const es8323_mono_adc_mux[] = { "Stereo", "Mono (Left)", "Mono (Right)" };
+static SOC_ENUM_SINGLE_DECL(es8323_mono_adc_mux_enum, ES8323_ADCCONTROL3, 3, es8323_mono_adc_mux);
+static const struct snd_kcontrol_new es8323_mono_adc_mux_controls =
+ SOC_DAPM_ENUM("Mono Mux", es8323_mono_adc_mux_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new es8323_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new es8323_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 3, 1),
+
+ /* Muxes */
+ SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_left_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_right_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, &es8323_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, &es8323_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, &es8323_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &es8323_left_mixer_controls[0],
+ ARRAY_SIZE(es8323_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &es8323_right_mixer_controls[0],
+ ARRAY_SIZE(es8323_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("VREF"),
+};
+
+static const struct snd_soc_dapm_route es8323_dapm_routes[] = {
+ /*12.22*/
+ {"Left PGA Mux", "Line 1L", "LINPUT1"},
+ {"Left PGA Mux", "Line 2L", "LINPUT2"},
+ {"Left PGA Mux", "DifferentialL", "Differential Mux"},
+
+ {"Right PGA Mux", "Line 1R", "RINPUT1"},
+ {"Right PGA Mux", "Line 2R", "RINPUT2"},
+ {"Right PGA Mux", "DifferentialR", "Differential Mux"},
+
+ {"Differential Mux", "Line 1", "LINPUT1"},
+ {"Differential Mux", "Line 1", "RINPUT1"},
+ {"Differential Mux", "Line 2", "LINPUT2"},
+ {"Differential Mux", "Line 2", "RINPUT2"},
+
+ {"Left ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+
+ {"Right ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+
+ {"Left ADC Power", NULL, "Left ADC Mux"},
+ {"Right ADC Power", NULL, "Right ADC Mux"},
+ {"Left ADC", NULL, "Left ADC Power"},
+ {"Right ADC", NULL, "Right ADC Power"},
+
+ {"Left Line Mux", "Line 1L", "LINPUT1"},
+ {"Left Line Mux", "Line 2L", "LINPUT2"},
+ {"Left Line Mux", "MicL", "Left PGA Mux"},
+
+ {"Right Line Mux", "Line 1R", "RINPUT1"},
+ {"Right Line Mux", "Line 2R", "RINPUT2"},
+ {"Right Line Mux", "MicR", "Right PGA Mux"},
+
+ {"Left Mixer", "Left Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+
+ {"Right Mixer", "Right Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT2", NULL, "Left Out 2"},
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT2", NULL, "Right Out 2"},
+};
+
+struct coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct coeff_div es8323_coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0xa, 0x0},
+ {11289600, 8000, 1408, 0x9, 0x0},
+ {18432000, 8000, 2304, 0xc, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x0},
+ {12000000, 8000, 1500, 0xb, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x7, 0x0},
+ {16934400, 11025, 1536, 0xa, 0x0},
+ {12000000, 11025, 1088, 0x9, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0x6, 0x0},
+ {18432000, 16000, 1152, 0x8, 0x0},
+ {12000000, 16000, 750, 0x7, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x4, 0x0},
+ {16934400, 22050, 768, 0x6, 0x0},
+ {12000000, 22050, 544, 0x6, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x3, 0x0},
+ {18432000, 32000, 576, 0x5, 0x0},
+ {12000000, 32000, 375, 0x4, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x2, 0x0},
+ {16934400, 44100, 384, 0x3, 0x0},
+ {12000000, 44100, 272, 0x3, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x2, 0x0},
+ {18432000, 48000, 384, 0x3, 0x0},
+ {12000000, 48000, 250, 0x2, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x0, 0x0},
+ {16934400, 88200, 192, 0x1, 0x0},
+ {12000000, 88200, 136, 0x1, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x0, 0x0},
+ {18432000, 96000, 192, 0x1, 0x0},
+ {12000000, 96000, 125, 0x0, 0x1},
+};
+
+static unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static unsigned int rates_112896[] = {
+ 8000, 11025, 22050, 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_112896 = {
+ .count = ARRAY_SIZE(rates_112896),
+ .list = rates_112896,
+};
+
+static unsigned int rates_12[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 48000, 88235, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12 = {
+ .count = ARRAY_SIZE(rates_12),
+ .list = rates_12,
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(es8323_coeff_div); i++) {
+ if (es8323_coeff_div[i].rate == rate &&
+ es8323_coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int es8323_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 11289600:
+ case 18432000:
+ case 22579200:
+ case 36864000:
+ es8323->sysclk_constraints = &constraints_112896;
+ break;
+ case 12288000:
+ case 16934400:
+ case 24576000:
+ case 33868800:
+ es8323->sysclk_constraints = &constraints_12288;
+ break;
+ case 12000000:
+ case 24000000:
+ es8323->sysclk_constraints = &constraints_12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ es8323->sysclk = freq;
+ return 0;
+}
+
+static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 iface = snd_soc_component_read(component, ES8323_MASTERMODE);
+ u8 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE);
+ u8 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
+ iface |= 0x80;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ iface &= 0x7f;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ adciface &= 0xfc;
+ daciface &= 0xf8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ adciface &= 0xfd;
+ daciface &= 0xf9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ adciface &= 0xfe;
+ daciface &= 0xfa;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ adciface &= 0xff;
+ daciface &= 0xfb;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xdf;
+ adciface &= 0xdf;
+ daciface &= 0xbf;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x20;
+ adciface |= 0x20;
+ daciface |= 0x40;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x20;
+ adciface &= 0xdf;
+ daciface &= 0xbf;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xdf;
+ adciface |= 0x20;
+ daciface |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, ES8323_MASTERMODE, iface);
+ snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+ snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+
+ return 0;
+}
+
+static int es8323_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ if (es8323->sysclk) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ es8323->sysclk_constraints);
+ }
+
+ return 0;
+}
+
+static int es8323_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ u16 srate = snd_soc_component_read(component, ES8323_MASTERMODE) & 0x80;
+ u16 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE) & 0xe3;
+ u16 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE) & 0xc7;
+ int coeff;
+
+ coeff = get_coeff(es8323->sysclk, params_rate(params));
+ if (coeff < 0) {
+ coeff = get_coeff(es8323->sysclk / 2, params_rate(params));
+ srate |= 0x40;
+ }
+
+ if (coeff < 0) {
+ dev_err(component->dev,
+ "Unable to configure sample rate %dHz with %dHz MCLK\n",
+ params_rate(params), es8323->sysclk);
+ return coeff;
+ }
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ adciface |= 0xc;
+ daciface |= 0x18;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ adciface |= 0x4;
+ daciface |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ adciface |= 0x10;
+ daciface |= 0x20;
+ break;
+ }
+
+ snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+ snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+
+ snd_soc_component_write(component, ES8323_MASTERMODE, srate);
+ snd_soc_component_write(component, ES8323_ADCCONTROL5,
+ es8323_coeff_div[coeff].sr |
+ (es8323_coeff_div[coeff].usb) << 4);
+ snd_soc_component_write(component, ES8323_DACCONTROL2,
+ es8323_coeff_div[coeff].sr |
+ (es8323_coeff_div[coeff].usb) << 4);
+
+ snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+
+ return 0;
+}
+
+static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 val = mute ? 0x6 : 0x2;
+
+ snd_soc_component_write(component, ES8323_DAC_MUTE, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8323_ops = {
+ .startup = es8323_pcm_startup,
+ .hw_params = es8323_pcm_hw_params,
+ .set_fmt = es8323_set_dai_fmt,
+ .set_sysclk = es8323_set_dai_sysclk,
+ .mute_stream = es8323_mute_stream,
+};
+
+#define ES8323_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver es8323_dai = {
+ .name = "ES8323 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ES8323_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ES8323_FORMATS,
+ },
+ .ops = &es8323_ops,
+ .symmetric_rate = 1,
+};
+
+static int es8323_probe(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8323->component = component;
+
+ es8323->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8323->mclk)) {
+ dev_err(component->dev, "unable to get mclk\n");
+ return PTR_ERR(es8323->mclk);
+ }
+
+ if (!es8323->mclk)
+ dev_warn(component->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8323->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to enable mclk\n");
+ return ret;
+ }
+
+ snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+
+ return 0;
+}
+
+static int es8323_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8323->mclk);
+ if (ret)
+ return ret;
+
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0);
+ usleep_range(18000, 20000);
+ snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0x09);
+ snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0x59);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8323->mclk);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0xff);
+ snd_soc_component_write(component, ES8323_DACPOWER, 0xC0);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff);
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7b);
+ break;
+ }
+
+ return 0;
+}
+
+static void es8323_remove(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8323->mclk);
+ es8323_set_bias_level(component, SND_SOC_BIAS_OFF);
+}
+
+static int es8323_suspend(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8323->regmap, true);
+ regcache_mark_dirty(es8323->regmap);
+
+ return 0;
+}
+
+static int es8323_resume(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8323->regmap, false);
+ regcache_sync(es8323->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8323 = {
+ .probe = es8323_probe,
+ .remove = es8323_remove,
+ .suspend = es8323_suspend,
+ .resume = es8323_resume,
+ .set_bias_level = es8323_set_bias_level,
+ .controls = es8323_snd_controls,
+ .num_controls = ARRAY_SIZE(es8323_snd_controls),
+ .dapm_widgets = es8323_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8323_dapm_widgets),
+ .dapm_routes = es8323_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8323_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config es8323_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x53,
+ .reg_defaults = es8323_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(es8323_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int es8323_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8323_priv *es8323;
+ struct device *dev = &i2c_client->dev;
+
+ es8323 = devm_kzalloc(dev, sizeof(*es8323), GFP_KERNEL);
+ if (!es8323)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8323);
+
+ es8323->regmap = devm_regmap_init_i2c(i2c_client, &es8323_regmap);
+ if (IS_ERR(es8323->regmap))
+ return PTR_ERR(es8323->regmap);
+
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_es8323,
+ &es8323_dai, 1);
+}
+
+static const struct i2c_device_id es8323_i2c_id[] = {
+ { "es8323" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);
+
+static const struct acpi_device_id es8323_acpi_match[] = {
+ { "ESSX8323", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, es8323_acpi_match);
+
+static const struct of_device_id es8323_of_match[] = {
+ { .compatible = "everest,es8323" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8323_of_match);
+
+static struct i2c_driver es8323_i2c_driver = {
+ .driver = {
+ .name = "ES8323",
+ .acpi_match_table = es8323_acpi_match,
+ .of_match_table = es8323_of_match,
+ },
+ .probe = es8323_i2c_probe,
+ .id_table = es8323_i2c_id,
+};
+module_i2c_driver(es8323_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES8323 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h
new file mode 100644
index 000000000000..f986c9301dc6
--- /dev/null
+++ b/sound/soc/codecs/es8323.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ * Binbin Zhou <zhoubinbin@loongson.cn>
+ *
+ */
+
+#ifndef _ES8323_H
+#define _ES8323_H
+
+/* ES8323 register space */
+
+/* Chip Control and Power Management */
+#define ES8323_CONTROL1 0x00
+#define ES8323_CONTROL2 0x01
+#define ES8323_CHIPPOWER 0x02
+#define ES8323_ADCPOWER 0x03
+#define ES8323_DACPOWER 0x04
+#define ES8323_CHIPLOPOW1 0x05
+#define ES8323_CHIPLOPOW2 0x06
+#define ES8323_ANAVOLMANAG 0x07
+#define ES8323_MASTERMODE 0x08
+
+/* ADC Control */
+#define ES8323_ADCCONTROL1 0x09
+#define ES8323_ADCCONTROL2 0x0a
+#define ES8323_ADCCONTROL3 0x0b
+#define ES8323_ADCCONTROL4 0x0c
+#define ES8323_ADCCONTROL5 0x0d
+#define ES8323_ADCCONTROL6 0x0e
+#define ES8323_ADC_MUTE 0x0f
+#define ES8323_LADC_VOL 0x10
+#define ES8323_RADC_VOL 0x11
+#define ES8323_ADCCONTROL10 0x12
+#define ES8323_ADCCONTROL11 0x13
+#define ES8323_ADCCONTROL12 0x14
+#define ES8323_ADCCONTROL13 0x15
+#define ES8323_ADCCONTROL14 0x16
+
+/* DAC Control */
+#define ES8323_DACCONTROL1 0x17
+#define ES8323_DACCONTROL2 0x18
+#define ES8323_DAC_MUTE 0x19
+#define ES8323_LDAC_VOL 0x1a
+#define ES8323_RDAC_VOL 0x1b
+#define ES8323_DACCONTROL6 0x1c
+#define ES8323_DACCONTROL7 0x1d
+#define ES8323_DACCONTROL8 0x1e
+#define ES8323_DACCONTROL9 0x1f
+#define ES8323_DACCONTROL10 0x20
+#define ES8323_DACCONTROL11 0x21
+#define ES8323_DACCONTROL12 0x22
+#define ES8323_DACCONTROL13 0x23
+#define ES8323_DACCONTROL14 0x24
+#define ES8323_DACCONTROL15 0x25
+#define ES8323_DACCONTROL16 0x26
+#define ES8323_DACCONTROL17 0x27
+#define ES8323_DACCONTROL18 0x28
+#define ES8323_DACCONTROL19 0x29
+#define ES8323_DACCONTROL20 0x2a
+#define ES8323_DACCONTROL21 0x2b
+#define ES8323_DACCONTROL22 0x2c
+#define ES8323_DACCONTROL23 0x2d
+#define ES8323_LOUT1_VOL 0x2e
+#define ES8323_ROUT1_VOL 0x2f
+#define ES8323_LOUT2_VOL 0x30
+#define ES8323_ROUT2_VOL 0x31
+#define ES8323_DACCONTROL28 0x32
+#define ES8323_DACCONTROL29 0x33
+#define ES8323_DACCONTROL30 0x34
+
+#define ES8323_ADC_IFACE ES8323_ADCCONTROL4
+#define ES8323_ADC_SRATE ES8323_ADCCONTROL5
+#define ES8323_DAC_IFACE ES8323_DACCONTROL1
+#define ES8323_DAC_SRATE ES8323_DACCONTROL2
+#endif
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
new file mode 100644
index 000000000000..05b13661c38c
--- /dev/null
+++ b/sound/soc/codecs/es8326.c
@@ -0,0 +1,1374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8326.c -- es8326 ALSA SoC audio driver
+// Copyright Everest Semiconductor Co., Ltd
+//
+// Authors: David Yang <yangxiaohua@everest-semi.com>
+//
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "es8326.h"
+
+struct es8326_priv {
+ struct clk *mclk;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct delayed_work jack_detect_work;
+ struct delayed_work button_press_work;
+ struct snd_soc_jack *jack;
+ int irq;
+ /* The lock protects the situation that an irq is generated
+ * while enabling or disabling or during an irq.
+ */
+ struct mutex lock;
+ u8 jack_pol;
+ u8 interrupt_src;
+ u8 interrupt_clk;
+ u8 hpl_vol;
+ u8 hpr_vol;
+ bool jd_inverted;
+ unsigned int sysclk;
+
+ bool calibrated;
+ int version;
+ int hp;
+ int jack_remove_retry;
+};
+
+static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x20;
+ crosstalk_l &= 0xf0;
+ crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = (crosstalk & 0x10) << 1;
+ crosstalk_l &= 0x0f;
+ crosstalk_l |= (crosstalk & 0x0f) << 4;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x20, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x10;
+ crosstalk_l &= 0x0f;
+ crosstalk = crosstalk_h | crosstalk_l;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = crosstalk & 0x10;
+ crosstalk_l &= 0xf0;
+ crosstalk_l |= crosstalk & 0x0f;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x10, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_hplvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpl_vol;
+
+ return 0;
+}
+
+static int es8326_hplvol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int hp_vol;
+
+ hp_vol = ucontrol->value.integer.value[0];
+ if (hp_vol > 5)
+ return -EINVAL;
+ if (es8326->hpl_vol != hp_vol) {
+ es8326->hpl_vol = hp_vol;
+ if (hp_vol >= 3)
+ hp_vol++;
+ regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+ 0x70, (hp_vol << 4));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int es8326_hprvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpr_vol;
+
+ return 0;
+}
+
+static int es8326_hprvol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int hp_vol;
+
+ hp_vol = ucontrol->value.integer.value[0];
+ if (hp_vol > 5)
+ return -EINVAL;
+ if (es8326->hpr_vol != hp_vol) {
+ es8326->hpr_vol = hp_vol;
+ if (hp_vol >= 3)
+ hp_vol++;
+ regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+ 0x07, hp_vol);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0);
+
+static const char *const winsize[] = {
+ "0.25db/2 LRCK",
+ "0.25db/4 LRCK",
+ "0.25db/8 LRCK",
+ "0.25db/16 LRCK",
+ "0.25db/32 LRCK",
+ "0.25db/64 LRCK",
+ "0.25db/128 LRCK",
+ "0.25db/256 LRCK",
+ "0.25db/512 LRCK",
+ "0.25db/1024 LRCK",
+ "0.25db/2048 LRCK",
+ "0.25db/4096 LRCK",
+ "0.25db/8192 LRCK",
+ "0.25db/16384 LRCK",
+ "0.25db/32768 LRCK",
+ "0.25db/65536 LRCK",
+};
+
+static const char *const dacpol_txt[] = {
+ "Normal", "R Invert", "L Invert", "L + R Invert" };
+
+static const char *const hp_spkvol_switch[] = {
+ "HPVOL: HPL+HPL, SPKVOL: HPL+HPL",
+ "HPVOL: HPL+HPR, SPKVOL: HPL+HPR",
+ "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR",
+ "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR",
+};
+
+static const struct soc_enum dacpol =
+ SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize);
+static const struct soc_enum drc_winsize =
+ SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize);
+static const struct soc_enum hpvol_spkvol_switch =
+ SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch);
+
+static const struct snd_kcontrol_new es8326_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_ENUM("Playback Polarity", dacpol),
+ SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv),
+ SOC_ENUM("DRC Winsize", drc_winsize),
+ SOC_SINGLE_TLV("DRC Target Level", ES8326_DRC_WINSIZE, 0, 0x0f, 0, drc_target_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC Capture Volume", ES8326_ADC1_VOL, ES8326_ADC2_VOL, 0, 0xff, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv),
+ SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv),
+ SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL,
+ 0, 4, 0, drc_recovery_tlv),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,
+ 0, 0x0f, 0, drc_target_tlv),
+
+ SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk1_get, es8326_crosstalk1_set),
+ SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk2_get, es8326_crosstalk2_set),
+ SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0,
+ es8326_hplvol_get, es8326_hplvol_set),
+ SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0,
+ es8326_hprvol_get, es8326_hprvol_set),
+
+ SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+
+ SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch),
+};
+
+static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Analog Power Supply*/
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ES8326_ANA_MICBIAS, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", ES8326_ANA_MICBIAS, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
+ {"ADC L", NULL, "MIC1"},
+ {"ADC R", NULL, "MIC2"},
+ {"ADC L", NULL, "MIC3"},
+ {"ADC R", NULL, "MIC4"},
+
+ {"I2S OUT", NULL, "ADC L"},
+ {"I2S OUT", NULL, "ADC R"},
+
+ {"Right DAC", NULL, "I2S IN"},
+ {"Left DAC", NULL, "I2S IN"},
+
+ {"LHPMIX", NULL, "Left DAC"},
+ {"RHPMIX", NULL, "Right DAC"},
+
+ {"HPOL", NULL, "LHPMIX"},
+ {"HPOR", NULL, "RHPMIX"},
+};
+
+static bool es8326_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8326_HPL_OFFSET_INI:
+ case ES8326_HPR_OFFSET_INI:
+ case ES8326_HPDET_STA:
+ case ES8326_CTIA_OMTP_STA:
+ case ES8326_CSM_MUTE_STA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool es8326_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8326_BIAS_SW1:
+ case ES8326_BIAS_SW2:
+ case ES8326_BIAS_SW3:
+ case ES8326_BIAS_SW4:
+ case ES8326_ADC_HPFS1:
+ case ES8326_ADC_HPFS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .use_single_read = true,
+ .use_single_write = true,
+ .volatile_reg = es8326_volatile_register,
+ .writeable_reg = es8326_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 rate;
+ u32 mclk;
+ u8 reg4;
+ u8 reg5;
+ u8 reg6;
+ u8 reg7;
+ u8 reg8;
+ u8 reg9;
+ u8 rega;
+ u8 regb;
+};
+
+/* codec hifi mclk clock divider coefficients */
+/* {ratio, LRCK, MCLK, REG04, REG05, REG06, REG07, REG08, REG09, REG10, REG11} */
+static const struct _coeff_div coeff_div_v0[] = {
+ {64, 8000, 512000, 0x60, 0x01, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 8000, 1024000, 0x60, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {192, 32000, 6144000, 0xE0, 0x02, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 8000, 2048000, 0x60, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {384, 32000, 12288000, 0xE0, 0x05, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {400, 48000, 19200000, 0xE9, 0x04, 0x0F, 0x6d, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+};
+
+static const struct _coeff_div coeff_div_v3[] = {
+ {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {64, 8000, 512000, 0x60, 0x00, 0x35, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0xB8, 0x08, 0x4f, 0x1f},
+ {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+
+ {128, 8000, 1024000, 0x60, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {192, 32000, 6144000, 0xE0, 0x02, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0xCA, 0x1B, 0x1F, 0x3F},
+
+ {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x27, 0x27},
+ {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {384, 32000, 12288000, 0xE0, 0x02, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {400, 48000, 19200000, 0xE4, 0x04, 0x35, 0x6d, 0xCA, 0x0A, 0x1F, 0x1F},
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x08, 0x19, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x5F},
+ {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+};
+
+static inline int get_coeff(int mclk, int rate, int array,
+ const struct _coeff_div *coeff_div)
+{
+ int i;
+
+ for (i = 0; i < array; i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int es8326_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = codec_dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec);
+
+ es8326->sysclk = freq;
+
+ return 0;
+}
+
+static int es8326_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ snd_soc_component_update_bits(component, ES8326_RESET,
+ ES8326_MASTER_MODE_EN, ES8326_MASTER_MODE_EN);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "Codec driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ES8326_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ES8326_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ES8326_DAIFMT_DSP_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DAIFMT_MASK, iface);
+
+ return 0;
+}
+
+static int es8326_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ const struct _coeff_div *coeff_div;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ u8 srate = 0;
+ int coeff, array;
+
+ if (es8326->version == 0) {
+ coeff_div = coeff_div_v0;
+ array = ARRAY_SIZE(coeff_div_v0);
+ } else {
+ coeff_div = coeff_div_v3;
+ array = ARRAY_SIZE(coeff_div_v3);
+ }
+ coeff = get_coeff(es8326->sysclk, params_rate(params), array, coeff_div);
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srate |= ES8326_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srate |= ES8326_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ srate |= ES8326_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srate |= ES8326_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ srate |= ES8326_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DATA_LEN_MASK, srate);
+
+ if (coeff >= 0) {
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1,
+ coeff_div[coeff].reg4);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV2,
+ coeff_div[coeff].reg5);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL,
+ coeff_div[coeff].reg6);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX,
+ coeff_div[coeff].reg7);
+ regmap_write(es8326->regmap, ES8326_CLK_ADC_SEL,
+ coeff_div[coeff].reg8);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL,
+ coeff_div[coeff].reg9);
+ regmap_write(es8326->regmap, ES8326_CLK_ADC_OSR,
+ coeff_div[coeff].rega);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_OSR,
+ coeff_div[coeff].regb);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int offset_l, offset_r;
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ES8326_MUTE);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF,
+ 0x30, 0x00);
+ } else {
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x0F);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
+ }
+ }
+ } else {
+ if (!es8326->calibrated) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL);
+ msleep(30);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l);
+ regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l);
+ regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
+ es8326->calibrated = true;
+ }
+ regmap_update_bits(es8326->regmap, ES8326_CLK_INV, 0xc0, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_MUX, 0x80, 0x00);
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ~(ES8326_MUTE));
+ } else {
+ msleep(300);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70);
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00);
+ }
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x00);
+ }
+ }
+ return 0;
+}
+
+static int es8326_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8326->mclk);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02);
+ usleep_range(5000, 10000);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
+ (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
+ }
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x10);
+ }
+ regmap_update_bits(es8326->regmap, ES8326_CLK_INV, 0xc0, 0xc0);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_MUX, 0x80, 0x80);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8326->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+#define es8326_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops es8326_ops = {
+ .hw_params = es8326_pcm_hw_params,
+ .set_fmt = es8326_set_dai_fmt,
+ .set_sysclk = es8326_set_dai_sysclk,
+ .mute_stream = es8326_mute,
+ .no_capture_mute = 0,
+};
+
+static struct snd_soc_dai_driver es8326_dai = {
+ .name = "ES8326 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = es8326_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = es8326_FORMATS,
+ },
+ .ops = &es8326_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8326_enable_micbias(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void es8326_disable_micbias(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+/*
+ * For button detection, set the following in soundcard
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ */
+static void es8326_jack_button_handler(struct work_struct *work)
+{
+ struct es8326_priv *es8326 =
+ container_of(work, struct es8326_priv, button_press_work.work);
+ struct snd_soc_component *comp = es8326->component;
+ unsigned int iface;
+ static int button_to_report, press_count;
+ static int prev_button, cur_button;
+
+ if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */
+ return;
+
+ mutex_lock(&es8326->lock);
+ iface = snd_soc_component_read(comp, ES8326_HPDET_STA);
+ switch (iface) {
+ case 0x93:
+ /* pause button detected */
+ cur_button = SND_JACK_BTN_0;
+ break;
+ case 0x6f:
+ case 0x4b:
+ /* button volume up */
+ if ((iface == 0x6f) && (es8326->version > ES8326_VERSION_B))
+ cur_button = SND_JACK_BTN_0;
+ else
+ cur_button = SND_JACK_BTN_1;
+ break;
+ case 0x27:
+ /* button volume down */
+ cur_button = SND_JACK_BTN_2;
+ break;
+ case 0x1e:
+ case 0xe2:
+ /* button released or not pressed */
+ cur_button = 0;
+ break;
+ default:
+ break;
+ }
+
+ if ((prev_button == cur_button) && (cur_button != 0)) {
+ press_count++;
+ if (press_count > 3) {
+ /* report a press every 120ms */
+ snd_soc_jack_report(es8326->jack, cur_button,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ press_count = 0;
+ }
+ button_to_report = cur_button;
+ queue_delayed_work(system_dfl_wq, &es8326->button_press_work,
+ msecs_to_jiffies(35));
+ } else if (prev_button != cur_button) {
+ /* mismatch, detect again */
+ prev_button = cur_button;
+ queue_delayed_work(system_dfl_wq, &es8326->button_press_work,
+ msecs_to_jiffies(35));
+ } else {
+ /* released or no pressed */
+ if (button_to_report != 0) {
+ snd_soc_jack_report(es8326->jack, button_to_report,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ snd_soc_jack_report(es8326->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ button_to_report = 0;
+ }
+ es8326_disable_micbias(es8326->component);
+ }
+ mutex_unlock(&es8326->lock);
+}
+
+static void es8326_jack_detect_handler(struct work_struct *work)
+{
+ struct es8326_priv *es8326 =
+ container_of(work, struct es8326_priv, jack_detect_work.work);
+ struct snd_soc_component *comp = es8326->component;
+ unsigned int iface;
+
+ mutex_lock(&es8326->lock);
+ iface = snd_soc_component_read(comp, ES8326_HPDET_STA);
+ dev_dbg(comp->dev, "gpio flag %#04x", iface);
+
+ if ((es8326->jack_remove_retry == 1) && (es8326->version < ES8326_VERSION_B)) {
+ if (iface & ES8326_HPINSERT_FLAG)
+ es8326->jack_remove_retry = 2;
+ else
+ es8326->jack_remove_retry = 0;
+
+ dev_dbg(comp->dev, "remove event check, set HPJACK_POL normal, cnt = %d\n",
+ es8326->jack_remove_retry);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ?
+ ~es8326->jack_pol : es8326->jack_pol));
+ goto exit;
+ }
+
+ if ((iface & ES8326_HPINSERT_FLAG) == 0) {
+ /* Jack unplugged or spurious IRQ */
+ dev_dbg(comp->dev, "No headset detected\n");
+ es8326_disable_micbias(es8326->component);
+ if (es8326->jack->status & SND_JACK_HEADPHONE) {
+ dev_dbg(comp->dev, "Report hp remove event\n");
+ snd_soc_jack_report(es8326->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ /* mute adc when mic path switch */
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66);
+ }
+ es8326->hp = 0;
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ if ((es8326->jack_remove_retry == 0) && (es8326->version < ES8326_VERSION_B)) {
+ es8326->jack_remove_retry = 1;
+ dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n",
+ es8326->jack_remove_retry);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ?
+ es8326->jack_pol : ~es8326->jack_pol));
+
+ } else {
+ es8326->jack_remove_retry = 0;
+ }
+ } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) {
+ es8326->jack_remove_retry = 0;
+ if (es8326->hp == 0) {
+ dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n");
+ /*
+ * set auto-check mode, then restart jack_detect_work after 400ms.
+ * Don't report jack status.
+ */
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x00);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x10);
+ usleep_range(50000, 70000);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x0d);
+ queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(400));
+ es8326->hp = 1;
+ goto exit;
+ }
+ if (es8326->jack->status & SND_JACK_HEADSET) {
+ /* detect button */
+ dev_dbg(comp->dev, "button pressed\n");
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ es8326_enable_micbias(es8326->component);
+ queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10);
+ goto exit;
+ }
+ if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) {
+ dev_dbg(comp->dev, "Headphone detected\n");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ } else {
+ dev_dbg(comp->dev, "Headset detected\n");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x08);
+ regmap_update_bits(es8326->regmap, ES8326_PGAGAIN,
+ 0x80, 0x80);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x00);
+ usleep_range(10000, 15000);
+ }
+ }
+exit:
+ mutex_unlock(&es8326->lock);
+}
+
+static irqreturn_t es8326_irq(int irq, void *dev_id)
+{
+ struct es8326_priv *es8326 = dev_id;
+
+ if (!es8326->jack)
+ goto out;
+
+ if (es8326->jack->status & SND_JACK_HEADSET)
+ queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(10));
+ else
+ queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(300));
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int es8326_calibrate(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ unsigned int offset_l, offset_r;
+
+ regmap_read(es8326->regmap, ES8326_CHIP_VERSION, &reg);
+ es8326->version = reg;
+
+ if ((es8326->version >= ES8326_VERSION_B) && (es8326->calibrated == false)) {
+ dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n");
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x03);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08);
+ regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1);
+ regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03);
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f);
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88);
+ usleep_range(15000, 20000);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ usleep_range(15000, 20000);
+ regmap_write(es8326->regmap, ES8326_RESET, 0xc0);
+ usleep_range(15000, 20000);
+
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_CSM_MUTE_STA, &reg);
+ if ((reg & 0xf0) != 0x40)
+ msleep(50);
+
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0xd4);
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x4d);
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l);
+ regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l);
+ regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+
+ es8326->calibrated = true;
+ }
+
+ return 0;
+}
+
+static void es8326_init(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8326->regmap, ES8326_RESET, 0x1f);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x3E);
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
+ usleep_range(10000, 15000);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xd9);
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xd8);
+ /* set headphone default type and detect pin */
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83);
+ regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
+
+ /* set internal oscillator as clock source of headpone cp */
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x89);
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ /* clock manager reset release */
+ regmap_write(es8326->regmap, ES8326_RESET, 0x17);
+ /* set headphone detection as half scan mode */
+ regmap_write(es8326->regmap, ES8326_HP_MISC, 0x3d);
+ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
+
+ /* enable headphone driver */
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7);
+ usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xc4);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x81);
+ regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00);
+ /* calibrate for B version */
+ es8326_calibrate(component);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa);
+ regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00);
+ /* turn off headphone out */
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00);
+ /* set ADC and DAC in low power mode */
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
+
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
+ /* select vdda as micbias source */
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x03);
+ /* set dac dsmclip = 1 */
+ regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08);
+ regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15);
+
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
+ ((es8326->version >= ES8326_VERSION_B) ?
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
+ usleep_range(5000, 10000);
+ es8326_enable_micbias(es8326->component);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO,
+ es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK,
+ ES8326_MIC1_SEL);
+
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK,
+ ES8326_MUTE);
+
+ regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_LRCK, 0xff);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66);
+ es8326_disable_micbias(es8326->component);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x73, 0x10);
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ }
+
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9);
+}
+
+static int es8326_resume(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8326->regmap, false);
+ regcache_cache_bypass(es8326->regmap, true);
+ regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, &reg);
+ regcache_cache_bypass(es8326->regmap, false);
+ /* reset internal clock state */
+ if (reg == 0x05)
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ else
+ es8326_init(component);
+
+ regcache_sync(es8326->regmap);
+
+ es8326_irq(es8326->irq, es8326);
+ return 0;
+}
+
+static int es8326_suspend(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+ es8326_disable_micbias(component);
+ es8326->calibrated = false;
+ regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x00);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF);
+ regcache_cache_only(es8326->regmap, true);
+
+ /* reset register value to default */
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+
+ regcache_mark_dirty(es8326->regmap);
+ return 0;
+}
+
+static int es8326_probe(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8326->component = component;
+ es8326->jd_inverted = device_property_read_bool(component->dev,
+ "everest,jack-detect-inverted");
+
+ ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol);
+ if (ret != 0) {
+ dev_dbg(component->dev, "jack-pol return %d", ret);
+ es8326->jack_pol = ES8326_HP_TYPE_AUTO;
+ }
+ dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol);
+
+ ret = device_property_read_u8(component->dev, "everest,interrupt-src",
+ &es8326->interrupt_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "interrupt-src return %d", ret);
+ es8326->interrupt_src = ES8326_HP_DET_SRC_PIN9;
+ }
+ dev_dbg(component->dev, "interrupt-src %x", es8326->interrupt_src);
+
+ ret = device_property_read_u8(component->dev, "everest,interrupt-clk",
+ &es8326->interrupt_clk);
+ if (ret != 0) {
+ dev_dbg(component->dev, "interrupt-clk return %d", ret);
+ es8326->interrupt_clk = 0x00;
+ }
+ dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
+
+ es8326_init(component);
+ return 0;
+}
+
+static void es8326_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&es8326->lock);
+ if (es8326->jd_inverted)
+ snd_soc_component_update_bits(component, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol);
+ es8326->jack = jack;
+
+ mutex_unlock(&es8326->lock);
+ es8326_irq(es8326->irq, es8326);
+}
+
+static void es8326_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Enter into %s\n", __func__);
+ if (!es8326->jack)
+ return; /* Already disabled (or never enabled) */
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+
+ mutex_lock(&es8326->lock);
+ if (es8326->jack->status & SND_JACK_MICROPHONE) {
+ es8326_disable_micbias(component);
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ }
+ es8326->jack = NULL;
+ mutex_unlock(&es8326->lock);
+}
+
+static int es8326_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ es8326_enable_jack_detect(component, jack);
+ else
+ es8326_disable_jack_detect(component);
+
+ return 0;
+}
+
+static void es8326_remove(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ es8326_disable_jack_detect(component);
+ es8326_set_bias_level(component, SND_SOC_BIAS_OFF);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8326 = {
+ .probe = es8326_probe,
+ .remove = es8326_remove,
+ .resume = es8326_resume,
+ .suspend = es8326_suspend,
+ .set_bias_level = es8326_set_bias_level,
+ .set_jack = es8326_set_jack,
+ .dapm_widgets = es8326_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8326_dapm_widgets),
+ .dapm_routes = es8326_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8326_dapm_routes),
+ .controls = es8326_snd_controls,
+ .num_controls = ARRAY_SIZE(es8326_snd_controls),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int es8326_i2c_probe(struct i2c_client *i2c)
+{
+ struct es8326_priv *es8326;
+ int ret;
+
+ es8326 = devm_kzalloc(&i2c->dev, sizeof(struct es8326_priv), GFP_KERNEL);
+ if (!es8326)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, es8326);
+ es8326->i2c = i2c;
+ mutex_init(&es8326->lock);
+ es8326->regmap = devm_regmap_init_i2c(i2c, &es8326_regmap_config);
+ if (IS_ERR(es8326->regmap)) {
+ ret = PTR_ERR(es8326->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ es8326->irq = i2c->irq;
+ es8326->jack_remove_retry = 0;
+ es8326->hp = 0;
+ es8326->hpl_vol = 0x03;
+ es8326->hpr_vol = 0x03;
+ INIT_DELAYED_WORK(&es8326->jack_detect_work,
+ es8326_jack_detect_handler);
+ INIT_DELAYED_WORK(&es8326->button_press_work,
+ es8326_jack_button_handler);
+ /* ES8316 is level-based while ES8326 is edge-based */
+ ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "es8326", es8326);
+ if (ret) {
+ dev_warn(&i2c->dev, "Failed to request IRQ: %d: %d\n",
+ es8326->irq, ret);
+ es8326->irq = -ENXIO;
+ }
+
+ es8326->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(es8326->mclk)) {
+ dev_err(&i2c->dev, "unable to get mclk\n");
+ return PTR_ERR(es8326->mclk);
+ }
+ if (!es8326->mclk)
+ dev_warn(&i2c->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8326->mclk);
+ if (ret) {
+ dev_err(&i2c->dev, "unable to enable mclk\n");
+ return ret;
+ }
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_es8326,
+ &es8326_dai, 1);
+}
+
+
+static void es8326_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct snd_soc_component *component;
+ struct es8326_priv *es8326;
+
+ es8326 = i2c_get_clientdata(i2c);
+ component = es8326->component;
+ dev_dbg(component->dev, "Enter into %s\n", __func__);
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+ cancel_delayed_work_sync(&es8326->button_press_work);
+
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+
+}
+
+static void es8326_i2c_remove(struct i2c_client *i2c)
+{
+ es8326_i2c_shutdown(i2c);
+}
+
+static const struct i2c_device_id es8326_i2c_id[] = {
+ {"es8326" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, es8326_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8326_of_match[] = {
+ { .compatible = "everest,es8326", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es8326_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8326_acpi_match[] = {
+ {"ESSX8326", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, es8326_acpi_match);
+#endif
+
+static struct i2c_driver es8326_i2c_driver = {
+ .driver = {
+ .name = "es8326",
+ .acpi_match_table = ACPI_PTR(es8326_acpi_match),
+ .of_match_table = of_match_ptr(es8326_of_match),
+ },
+ .probe = es8326_i2c_probe,
+ .shutdown = es8326_i2c_shutdown,
+ .remove = es8326_i2c_remove,
+ .id_table = es8326_i2c_id,
+};
+module_i2c_driver(es8326_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8326 driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
new file mode 100644
index 000000000000..c3e52e7bdef5
--- /dev/null
+++ b/sound/soc/codecs/es8326.h
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8326.h -- es8326 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#ifndef _ES8326_H
+#define _ES8326_H
+
+/* ES8326 register space */
+#define ES8326_RESET 0x00
+#define ES8326_CLK_CTL 0x01
+#define ES8326_CLK_INV 0x02
+#define ES8326_CLK_RESAMPLE 0x03
+#define ES8326_CLK_DIV1 0x04
+#define ES8326_CLK_DIV2 0x05
+#define ES8326_CLK_DLL 0x06
+#define ES8326_CLK_MUX 0x07
+#define ES8326_CLK_ADC_SEL 0x08
+#define ES8326_CLK_DAC_SEL 0x09
+#define ES8326_CLK_ADC_OSR 0x0a
+#define ES8326_CLK_DAC_OSR 0x0b
+#define ES8326_CLK_DIV_CPC 0x0c
+#define ES8326_CLK_DIV_BCLK 0x0d
+#define ES8326_CLK_TRI 0x0e
+#define ES8326_CLK_DIV_LRCK 0x0f
+#define ES8326_CLK_VMIDS1 0x10
+#define ES8326_CLK_VMIDS2 0x11
+#define ES8326_CLK_CAL_TIME 0x12
+#define ES8326_FMT 0x13
+
+#define ES8326_DAC_MUTE 0x14
+#define ES8326_ADC_MUTE 0x15
+#define ES8326_ANA_PDN 0x16
+#define ES8326_PGA_PDN 0x17
+#define ES8326_VMIDSEL 0x18
+#define ES8326_ANA_LP 0x19
+#define ES8326_ANA_DMS 0x1a
+#define ES8326_ANA_MICBIAS 0x1b
+#define ES8326_ANA_VSEL 0x1c
+#define ES8326_SYS_BIAS 0x1d
+#define ES8326_BIAS_SW1 0x1e
+#define ES8326_BIAS_SW2 0x1f
+#define ES8326_BIAS_SW3 0x20
+#define ES8326_BIAS_SW4 0x21
+#define ES8326_VMIDLOW 0x22
+#define ES8326_PGAGAIN 0x23
+#define ES8326_HP_DRIVER 0x24
+#define ES8326_DAC2HPMIX 0x25
+#define ES8326_HP_VOL 0x26
+#define ES8326_HP_CAL 0x27
+#define ES8326_HP_DRIVER_REF 0x28
+#define ES8326_ADC_SCALE 0x29
+#define ES8326_ADC1_SRC 0x2a
+#define ES8326_ADC2_SRC 0x2b
+#define ES8326_ADC1_VOL 0x2c
+#define ES8326_ADC2_VOL 0x2d
+#define ES8326_ADC_RAMPRATE 0x2e
+#define ES8326_ALC_RECOVERY 0x32
+#define ES8326_ALC_LEVEL 0x33
+#define ES8326_ADC_HPFS1 0x34
+#define ES8326_ADC_HPFS2 0x35
+#define ES8326_ADC_EQ 0x36
+#define ES8326_HP_OFFSET_CAL 0x4A
+#define ES8326_HPL_OFFSET_INI 0x4B
+#define ES8326_HPR_OFFSET_INI 0x4C
+#define ES8326_DAC_DSM 0x4D
+#define ES8326_DAC_RAMPRATE 0x4E
+#define ES8326_DAC_VPPSCALE 0x4F
+#define ES8326_DACL_VOL 0x50
+#define ES8326_DRC_RECOVERY 0x53
+#define ES8326_DRC_WINSIZE 0x54
+#define ES8326_DAC_CROSSTALK 0x55
+#define ES8326_HPJACK_TIMER 0x56
+#define ES8326_HPDET_TYPE 0x57
+#define ES8326_INT_SOURCE 0x58
+#define ES8326_INTOUT_IO 0x59
+#define ES8326_SDINOUT1_IO 0x5A
+#define ES8326_SDINOUT23_IO 0x5B
+#define ES8326_JACK_PULSE 0x5C
+
+#define ES8326_DACR_VOL 0xF4
+#define ES8326_SPKL_VOL 0xF5
+#define ES8326_SPKR_VOL 0xF6
+#define ES8326_HP_MISC 0xF7
+#define ES8326_CTIA_OMTP_STA 0xF8
+#define ES8326_PULLUP_CTL 0xF9
+#define ES8326_CSM_I2C_STA 0xFA
+#define ES8326_HPDET_STA 0xFB
+#define ES8326_CSM_MUTE_STA 0xFC
+#define ES8326_CHIP_ID1 0xFD
+#define ES8326_CHIP_ID2 0xFE
+#define ES8326_CHIP_VERSION 0xFF
+
+/* ES8326_RESET */
+#define ES8326_CSM_ON (1 << 7)
+#define ES8326_MASTER_MODE_EN (1 << 6)
+#define ES8326_PWRUP_SEQ_EN (1 << 5)
+#define ES8326_CODEC_RESET (0x0f << 0)
+#define ES8326_CSM_OFF (0 << 7)
+#define ES8326_MUTE_MASK (3 << 0)
+#define ES8326_MUTE (3 << 0)
+
+/* ES8326_CLK_CTL */
+#define ES8326_CLK_ON (0x7f << 0)
+#define ES8326_CLK_OFF (0 << 0)
+
+/* ES8326_CLK_INV */
+#define ES8326_BCLK_AS_MCLK (1 << 3)
+
+/* ES8326_FMT */
+#define ES8326_S24_LE (0 << 2)
+#define ES8326_S20_3_LE (1 << 2)
+#define ES8326_S18_LE (2 << 2)
+#define ES8326_S16_LE (3 << 2)
+#define ES8326_S32_LE (4 << 2)
+#define ES8326_DATA_LEN_MASK (7 << 2)
+
+#define ES8326_DAIFMT_MASK ((1 << 5) | (3 << 0))
+#define ES8326_DAIFMT_I2S 0
+#define ES8326_DAIFMT_LEFT_J (1 << 0)
+#define ES8326_DAIFMT_DSP_A (3 << 0)
+#define ES8326_DAIFMT_DSP_B ((1 << 5) | (3 << 0))
+
+/* ES8326_PGAGAIN */
+#define ES8326_MIC_SEL_MASK (3 << 4)
+#define ES8326_MIC1_SEL (1 << 4)
+#define ES8326_MIC2_SEL (1 << 5)
+
+/* ES8326_HP_CAL */
+#define ES8326_HP_OFF 0
+#define ES8326_HP_FORCE_CAL ((1 << 7) | (1 << 3))
+#define ES8326_HP_ON ((7 << 4) | (7 << 0))
+
+/* ES8326_ADC1_SRC */
+#define ES8326_ADC1_SHIFT 0
+#define ES8326_ADC2_SHIFT 4
+#define ES8326_ADC_SRC_ANA 0
+#define ES8326_ADC_SRC_ANA_INV_SW0 1
+#define ES8326_ADC_SRC_ANA_INV_SW1 2
+#define ES8326_ADC_SRC_DMIC_MCLK 3
+#define ES8326_ADC_SRC_DMIC_SDIN2 4
+#define ES8326_ADC_SRC_DMIC_SDIN2_INV 5
+#define ES8326_ADC_SRC_DMIC_SDIN3 6
+#define ES8326_ADC_SRC_DMIC_SDIN3_INV 7
+
+#define ES8326_ADC_AMIC ((ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC2_SHIFT) \
+ | (ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC1_SHIFT))
+#define ES8326_ADC_DMIC ((ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC2_SHIFT) \
+ | (ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC1_SHIFT))
+/* ES8326_ADC2_SRC */
+#define ES8326_ADC3_SHIFT 0
+#define ES8326_ADC4_SHIFT 3
+
+/* ES8326_HPDET_TYPE */
+#define ES8326_HP_DET_SRC_PIN27 (1 << 5)
+#define ES8326_HP_DET_SRC_PIN9 (1 << 4)
+#define ES8326_HP_DET_JACK_POL (1 << 3)
+#define ES8326_HP_DET_BUTTON_POL (1 << 2)
+#define ES8326_HP_TYPE_OMTP (3 << 0)
+#define ES8326_HP_TYPE_CTIA (2 << 0)
+#define ES8326_HP_TYPE_AUTO (1 << 0)
+#define ES8326_HP_TYPE_AUTO_INV (0 << 0)
+
+/* ES8326_INT_SOURCE */
+#define ES8326_INT_SRC_DAC_MOZ (1 << 0)
+#define ES8326_INT_SRC_ADC_MOZ (1 << 1)
+#define ES8326_INT_SRC_BUTTON (1 << 2)
+#define ES8326_INT_SRC_PIN9 (1 << 3)
+#define ES8326_INT_SRC_PIN27 (1 << 4)
+
+/* ES8326_SDINOUT1_IO */
+#define ES8326_IO_INPUT (0 << 0)
+#define ES8326_IO_SDIN_SLOT0 (1 << 0)
+#define ES8326_IO_SDIN_SLOT1 (2 << 0)
+#define ES8326_IO_SDIN_SLOT2 (3 << 0)
+#define ES8326_IO_SDIN_SLOT7 (8 << 0)
+#define ES8326_IO_DMIC_CLK (9 << 0)
+#define ES8326_IO_DMIC_CLK_INV (0x0a << 0)
+#define ES8326_IO_SDOUT2 (0x0b << 0)
+#define ES8326_IO_LOW (0x0e << 0)
+#define ES8326_IO_HIGH (0x0f << 0)
+#define ES8326_ADC2DAC (1 << 3)
+#define ES8326_SDINOUT1_SHIFT 4
+
+/* ES8326_SDINOUT23_IO */
+#define ES8326_SDINOUT2_SHIFT 4
+#define ES8326_SDINOUT3_SHIFT 0
+
+/* ES8326_HPDET_STA */
+#define ES8326_HPINSERT_FLAG (1 << 1)
+#define ES8326_HPBUTTON_FLAG (1 << 0)
+
+/* ES8326_CHIP_VERSION 0xFF */
+#define ES8326_VERSION (1 << 0)
+#define ES8326_VERSION_B (3 << 0)
+
+#endif
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index 2d05b5d3a6ce..56bfbe9261ce 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -19,37 +16,31 @@
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "es8328", 0 },
+ { "es8328" },
+ { "es8388" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
static const struct of_device_id es8328_of_match[] = {
{ .compatible = "everest,es8328", },
+ { .compatible = "everest,es8388", },
{ }
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
-static int es8328_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int es8328_i2c_probe(struct i2c_client *i2c)
{
return es8328_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
}
-static int es8328_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
-}
-
static struct i2c_driver es8328_i2c_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
- .probe = es8328_i2c_probe,
- .remove = es8328_i2c_remove,
+ .probe = es8328_i2c_probe,
.id_table = es8328_id,
};
diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c
index 8fbd935e1c76..88e353ae52a1 100644
--- a/sound/soc/codecs/es8328-spi.c
+++ b/sound/soc/codecs/es8328-spi.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* es8328.c -- ES8328 ALSA SoC SPI Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -28,19 +25,12 @@ static int es8328_spi_probe(struct spi_device *spi)
devm_regmap_init_spi(spi, &es8328_regmap_config));
}
-static int es8328_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver es8328_spi_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_spi_probe,
- .remove = es8328_spi_remove,
};
module_spi_driver(es8328_spi_driver);
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index afa6c5db9dcc..1e11175cfbbb 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -1,18 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* es8328.c -- ES8328 ALSA SoC Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/regmap.h>
@@ -26,18 +22,30 @@
#include <sound/tlv.h>
#include "es8328.h"
-#define ES8328_SYSCLK_RATE_1X 11289600
-#define ES8328_SYSCLK_RATE_2X 22579200
+static const unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
-/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
-static struct {
- int rate;
- u8 ratio;
-} mclk_ratios[] = {
- { 8000, 9 },
- {11025, 7 },
- {22050, 4 },
- {44100, 2 },
+static const int ratios_12288[] = {
+ 10, 7, 6, 4, 3, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static const unsigned int rates_11289[] = {
+ 8018, 11025, 22050, 44100, 88200,
+};
+
+static const int ratios_11289[] = {
+ 9, 7, 4, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
+ .count = ARRAY_SIZE(rates_11289),
+ .list = rates_11289,
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -57,16 +65,25 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"HPVDD",
};
-#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_22050 | \
- SNDRV_PCM_RATE_11025)
-#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+#define ES8328_RATES (SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_8000_48000)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
struct es8328_priv {
struct regmap *regmap;
struct clk *clk;
int playback_fs;
bool deemph;
+ int mclkdiv2;
+ const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ const int *mclk_ratios;
+ bool provider;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
};
@@ -81,7 +98,6 @@ static SOC_ENUM_SINGLE_DECL(adcpol,
static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
-static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
@@ -95,9 +111,9 @@ static const struct {
{ 48000, ES8328_DACCONTROL6_DEEMPH_48k },
};
-static int es8328_set_deemph(struct snd_soc_codec *codec)
+static int es8328_set_deemph(struct snd_soc_component *component)
{
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int val, i, best;
/*
@@ -117,17 +133,17 @@ static int es8328_set_deemph(struct snd_soc_codec *codec)
val = ES8328_DACCONTROL6_DEEMPH_OFF;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, ES8328_DACCONTROL6,
+ return snd_soc_component_update_bits(component, ES8328_DACCONTROL6,
ES8328_DACCONTROL6_DEEMPH_MASK, val);
}
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = es8328->deemph;
return 0;
@@ -136,21 +152,24 @@ static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
int ret;
if (deemph > 1)
return -EINVAL;
- ret = es8328_set_deemph(codec);
+ if (es8328->deemph == deemph)
+ return 0;
+
+ ret = es8328_set_deemph(component);
if (ret < 0)
return ret;
es8328->deemph = deemph;
- return 0;
+ return 1;
}
@@ -210,11 +229,10 @@ static const struct soc_enum es8328_rline_enum =
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_right_line_controls =
- SOC_DAPM_ENUM("Route", es8328_lline_enum);
+ SOC_DAPM_ENUM("Route", es8328_rline_enum);
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
@@ -224,7 +242,6 @@ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
};
@@ -317,10 +334,10 @@ static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
ES8328_DACPOWER_LDAC_OFF, 1),
- SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_MIXER("Left Mixer", ES8328_DACCONTROL17, 7, 0,
&es8328_left_mixer_controls[0],
ARRAY_SIZE(es8328_left_mixer_controls)),
- SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_MIXER("Right Mixer", ES8328_DACCONTROL20, 7, 0,
&es8328_right_mixer_controls[0],
ARRAY_SIZE(es8328_right_mixer_controls)),
@@ -399,19 +416,14 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "Right Line Mux", "PGA", "Right PGA Mux" },
{ "Right Line Mux", "Differential", "Differential Mux" },
- { "Left Out 1", NULL, "Left DAC" },
- { "Right Out 1", NULL, "Right DAC" },
- { "Left Out 2", NULL, "Left DAC" },
- { "Right Out 2", NULL, "Right DAC" },
-
- { "Left Mixer", "Playback Switch", "Left DAC" },
+ { "Left Mixer", NULL, "Left DAC" },
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "Right Mixer", "Left Playback Switch", "Left DAC" },
{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
- { "Right Mixer", "Playback Switch", "Right DAC" },
+ { "Right Mixer", NULL, "Right DAC" },
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "DAC DIG", NULL, "DAC STM" },
@@ -432,76 +444,185 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "ROUT2", NULL, "Right Out 2" },
};
-static int es8328_mute(struct snd_soc_dai *dai, int mute)
+static int es8328_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
+ return snd_soc_component_update_bits(dai->component, ES8328_DACCONTROL3,
ES8328_DACCONTROL3_DACMUTE,
mute ? ES8328_DACCONTROL3_DACMUTE : 0);
}
+static int es8328_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+
+ if (es8328->provider && es8328->sysclk_constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ es8328->sysclk_constraints);
+
+ return 0;
+}
+
static int es8328_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int clk_rate;
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int i;
int reg;
- u8 ratio;
+ int wl;
+ int ratio;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2;
else
reg = ES8328_ADCCONTROL5;
- clk_rate = clk_get_rate(es8328->clk);
+ if (es8328->provider) {
+ if (!es8328->sysclk_constraints) {
+ dev_err(component->dev, "No MCLK configured\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < es8328->sysclk_constraints->count; i++)
+ if (es8328->sysclk_constraints->list[i] ==
+ params_rate(params))
+ break;
- 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;
+ if (i == es8328->sysclk_constraints->count) {
+ dev_err(component->dev,
+ "LRCLK %d unsupported with current clock\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ ratio = es8328->mclk_ratios[i];
+ } else {
+ ratio = 0;
+ es8328->mclkdiv2 = 0;
}
- /* find master mode MCLK to sampling frequency ratio */
- ratio = mclk_ratios[0].rate;
- for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
- if (params_rate(params) <= mclk_ratios[i].rate)
- ratio = mclk_ratios[i].ratio;
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MCLKDIV2,
+ es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+
+ switch (params_width(params)) {
+ case 16:
+ wl = 3;
+ break;
+ case 18:
+ wl = 2;
+ break;
+ case 20:
+ wl = 1;
+ break;
+ case 24:
+ wl = 0;
+ break;
+ case 32:
+ wl = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACWL_MASK,
+ wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+
es8328->playback_fs = params_rate(params);
- es8328_set_deemph(codec);
+ es8328_set_deemph(component);
+ } else
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCWL_MASK,
+ wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+
+ return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+}
+
+static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int mclkdiv2 = 0;
+ unsigned int round_freq;
+
+ /*
+ * Allow a small tolerance for frequencies within 100hz. Note
+ * this value is chosen arbitrarily.
+ */
+ round_freq = DIV_ROUND_CLOSEST(freq, 100) * 100;
+
+ switch (round_freq) {
+ case 0:
+ es8328->sysclk_constraints = NULL;
+ es8328->mclk_ratios = NULL;
+ break;
+ case 22579200:
+ mclkdiv2 = 1;
+ fallthrough;
+ case 11289600:
+ es8328->sysclk_constraints = &constraints_11289;
+ es8328->mclk_ratios = ratios_11289;
+ break;
+ case 24576000:
+ mclkdiv2 = 1;
+ fallthrough;
+ case 12288000:
+ es8328->sysclk_constraints = &constraints_12288;
+ es8328->mclk_ratios = ratios_12288;
+ break;
+ default:
+ return -EINVAL;
}
- return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
+ es8328->mclkdiv2 = mclkdiv2;
+ return 0;
}
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int clk_rate;
- u8 mode = ES8328_DACCONTROL1_DACWL_16;
-
- /* set master/slave audio interface */
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ u8 dac_mode = 0;
+ u8 adc_mode = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Master serial port mode, with BCLK generated automatically */
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ ES8328_MASTERMODE_MSC);
+ es8328->provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Slave serial port mode */
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC, 0);
+ es8328->provider = false;
+ break;
+ default:
return -EINVAL;
+ }
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
- mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
- mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
break;
default:
return -EINVAL;
@@ -511,33 +632,27 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
- snd_soc_write(codec, ES8328_DACCONTROL1, mode);
- snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
-
- /* Master serial port mode, with BCLK generated automatically */
- clk_rate = clk_get_rate(es8328->clk);
- if (clk_rate == ES8328_SYSCLK_RATE_1X)
- snd_soc_write(codec, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC);
- else
- snd_soc_write(codec, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MCLKDIV2 |
- ES8328_MASTERMODE_MSC);
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
return 0;
}
-static int es8328_set_bias_level(struct snd_soc_codec *codec,
+static int es8328_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
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,
+ snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_50k |
@@ -545,8 +660,8 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_5k |
@@ -556,12 +671,12 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
msleep(100);
}
- snd_soc_write(codec, ES8328_CONTROL2,
+ snd_soc_component_write(component, ES8328_CONTROL2,
ES8328_CONTROL2_OVERCURRENT_ON |
ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
/* VREF, VMID=2*500k, digital stopped */
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_500k |
@@ -569,7 +684,7 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
0);
@@ -579,9 +694,12 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
}
static const struct snd_soc_dai_ops es8328_dai_ops = {
+ .startup = es8328_startup,
.hw_params = es8328_hw_params,
- .digital_mute = es8328_mute,
+ .mute_stream = es8328_mute,
+ .set_sysclk = es8328_set_sysclk,
.set_fmt = es8328_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver es8328_dai = {
@@ -601,82 +719,83 @@ static struct snd_soc_dai_driver es8328_dai = {
.formats = ES8328_FORMATS,
},
.ops = &es8328_dai_ops,
+ .symmetric_rate = 1,
};
-static int es8328_suspend(struct snd_soc_codec *codec)
+static int es8328_suspend(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
clk_disable_unprepare(es8328->clk);
ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
- dev_err(codec->dev, "unable to disable regulators\n");
+ dev_err(component->dev, "unable to disable regulators\n");
return ret;
}
return 0;
}
-static int es8328_resume(struct snd_soc_codec *codec)
+static int es8328_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
ret = clk_prepare_enable(es8328->clk);
if (ret) {
- dev_err(codec->dev, "unable to enable clock\n");
+ dev_err(component->dev, "unable to enable clock\n");
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
- dev_err(codec->dev, "unable to enable regulators\n");
+ dev_err(component->dev, "unable to enable regulators\n");
return ret;
}
regcache_mark_dirty(regmap);
ret = regcache_sync(regmap);
if (ret) {
- dev_err(codec->dev, "unable to sync regcache\n");
+ dev_err(component->dev, "unable to sync regcache\n");
return ret;
}
return 0;
}
-static int es8328_codec_probe(struct snd_soc_codec *codec)
+static int es8328_component_probe(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
- dev_err(codec->dev, "unable to enable regulators\n");
+ dev_err(component->dev, "unable to enable regulators\n");
return ret;
}
/* Setup clocks */
- es8328->clk = devm_clk_get(codec->dev, NULL);
+ es8328->clk = devm_clk_get(component->dev, NULL);
if (IS_ERR(es8328->clk)) {
- dev_err(codec->dev, "codec clock missing or invalid\n");
+ dev_err(component->dev, "codec clock missing or invalid\n");
ret = PTR_ERR(es8328->clk);
goto clk_fail;
}
ret = clk_prepare_enable(es8328->clk);
if (ret) {
- dev_err(codec->dev, "unable to prepare codec clk\n");
+ dev_err(component->dev, "unable to prepare codec clk\n");
goto clk_fail;
}
@@ -688,43 +807,44 @@ clk_fail:
return ret;
}
-static int es8328_remove(struct snd_soc_codec *codec)
+static void es8328_remove(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
- if (es8328->clk)
- clk_disable_unprepare(es8328->clk);
+ clk_disable_unprepare(es8328->clk);
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
-
- return 0;
}
const struct regmap_config es8328_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ES8328_REG_MAX,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
EXPORT_SYMBOL_GPL(es8328_regmap_config);
-static struct snd_soc_codec_driver es8328_codec_driver = {
- .probe = es8328_codec_probe,
- .suspend = es8328_suspend,
- .resume = es8328_resume,
- .remove = es8328_remove,
- .set_bias_level = es8328_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = es8328_snd_controls,
- .num_controls = ARRAY_SIZE(es8328_snd_controls),
- .dapm_widgets = es8328_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
- .dapm_routes = es8328_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
+static const struct snd_soc_component_driver es8328_component_driver = {
+ .probe = es8328_component_probe,
+ .remove = es8328_remove,
+ .suspend = es8328_suspend,
+ .resume = es8328_resume,
+ .set_bias_level = es8328_set_bias_level,
+ .controls = es8328_snd_controls,
+ .num_controls = ARRAY_SIZE(es8328_snd_controls),
+ .dapm_widgets = es8328_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
+ .dapm_routes = es8328_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
int es8328_probe(struct device *dev, struct regmap *regmap)
@@ -754,8 +874,8 @@ int es8328_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, es8328);
- return snd_soc_register_codec(dev,
- &es8328_codec_driver, &es8328_dai, 1);
+ return devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai, 1);
}
EXPORT_SYMBOL_GPL(es8328_probe);
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
index 156c748c89c7..9109f6b5b045 100644
--- a/sound/soc/codecs/es8328.h
+++ b/sound/soc/codecs/es8328.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* es8328.h -- ES8328 ALSA SoC Audio driver
*/
@@ -22,7 +23,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
-#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
#define ES8328_CONTROL1_ENREF (1 << 2)
#define ES8328_CONTROL1_SEQEN (1 << 3)
#define ES8328_CONTROL1_SAMEFS (1 << 4)
@@ -84,7 +85,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_ADCCONTROL1 0x09
#define ES8328_ADCCONTROL2 0x0a
#define ES8328_ADCCONTROL3 0x0b
+
#define ES8328_ADCCONTROL4 0x0c
+#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
+#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
+#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
+
#define ES8328_ADCCONTROL5 0x0d
#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
@@ -109,15 +123,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_ADCCONTROL14 0x16
#define ES8328_DACCONTROL1 0x17
+#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
-#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
-#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
-#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
-#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
-#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#define ES8328_DACCONTROL1_DACWL_SHIFT 3
+#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
@@ -267,43 +279,6 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_REG_MAX 0x35
-#define ES8328_PLL1 0
-#define ES8328_PLL2 1
-
-/* clock inputs */
-#define ES8328_MCLK 0
-#define ES8328_PCMCLK 1
-
-/* clock divider id's */
-#define ES8328_PCMDIV 0
-#define ES8328_BCLKDIV 1
-#define ES8328_VXCLKDIV 2
-
-/* PCM clock dividers */
-#define ES8328_PCM_DIV_1 (0 << 6)
-#define ES8328_PCM_DIV_3 (2 << 6)
-#define ES8328_PCM_DIV_5_5 (3 << 6)
-#define ES8328_PCM_DIV_2 (4 << 6)
-#define ES8328_PCM_DIV_4 (5 << 6)
-#define ES8328_PCM_DIV_6 (6 << 6)
-#define ES8328_PCM_DIV_8 (7 << 6)
-
-/* BCLK clock dividers */
-#define ES8328_BCLK_DIV_1 (0 << 7)
-#define ES8328_BCLK_DIV_2 (1 << 7)
-#define ES8328_BCLK_DIV_4 (2 << 7)
-#define ES8328_BCLK_DIV_8 (3 << 7)
-
-/* VXCLK clock dividers */
-#define ES8328_VXCLK_DIV_1 (0 << 6)
-#define ES8328_VXCLK_DIV_2 (1 << 6)
-#define ES8328_VXCLK_DIV_4 (2 << 6)
-#define ES8328_VXCLK_DIV_8 (3 << 6)
-#define ES8328_VXCLK_DIV_16 (4 << 6)
-
-#define ES8328_DAI_HIFI 0
-#define ES8328_DAI_VOICE 1
-
#define ES8328_1536FS 1536
#define ES8328_1024FS 1024
#define ES8328_768FS 768
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
new file mode 100644
index 000000000000..36b0ebdce514
--- /dev/null
+++ b/sound/soc/codecs/es8375.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8375.c -- ES8375 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <linux/acpi.h>
+#include "es8375.h"
+
+struct es8375_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data core_supply[2];
+ unsigned int mclk_freq;
+ int mastermode;
+ u8 mclk_src;
+ u8 vddd;
+ enum snd_soc_bias_level bias_level;
+};
+
+static const char * const es8375_core_supplies[] = {
+ "vddd",
+ "vdda",
+};
+
+static const DECLARE_TLV_DB_SCALE(es8375_adc_osr_gain_tlv, -3100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_automute_attn_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_dmic_volume_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_vppscale_tlv, -388, 12, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_automute_attn_tlv, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_automute_ng_tlv, -9600, 600, 0);
+
+static const char *const es8375_ramprate_txt[] = {
+ "0.125dB/LRCK",
+ "0.125dB/2LRCK",
+ "0.125dB/4LRCK",
+ "0.125dB/8LRCK",
+ "0.125dB/16LRCK",
+ "0.125dB/32LRCK",
+ "0.125dB/64LRCK",
+ "0.125dB/128LRCK",
+ "disable softramp",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_ramprate, ES8375_ADC2,
+ ADC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_ramprate, ES8375_DAC2,
+ DAC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+
+static const char *const es8375_automute_ws_txt[] = {
+ "256 samples",
+ "512 samples",
+ "1024 samples",
+ "2048 samples",
+ "4096 samples",
+ "8192 samples",
+ "16384 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_automute_ws, ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_WS_SHIFT_3, es8375_automute_ws_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_automute_ws, ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_WS_SHIFT_5, es8375_automute_ws_txt);
+
+static const char *const es8375_dmic_pol_txt[] = {
+ "Low",
+ "High",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_dmic_pol, ES8375_ADC1,
+ DMIC_POL_SHIFT_4, es8375_dmic_pol_txt);
+
+static const char *const es8375_adc_hpf_txt[] = {
+ "Freeze Offset",
+ "Dynamic HPF",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_adc_hpf, ES8375_HPF1,
+ ADC_HPF_SHIFT_5, es8375_adc_hpf_txt);
+
+static const char *const es8375_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+static const struct soc_enum es8375_dmic_mux_enum =
+ SOC_ENUM_SINGLE(ES8375_ADC1, ADC_SRC_SHIFT_7,
+ ARRAY_SIZE(es8375_dmic_mux_txt), es8375_dmic_mux_txt);
+
+static const struct snd_kcontrol_new es8375_dmic_mux_controls =
+ SOC_DAPM_ENUM("ADC MUX", es8375_dmic_mux_enum);
+
+static const struct snd_kcontrol_new es8375_snd_controls[] = {
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8375_ADC_OSR_GAIN,
+ ADC_OSR_GAIN_SHIFT_0, ES8375_ADC_OSR_GAIN_MAX, 0,
+ es8375_adc_osr_gain_tlv),
+ SOC_SINGLE("ADC Invert Switch", ES8375_ADC1, ADC_INV_SHIFT_6, 1, 0),
+ SOC_SINGLE("ADC RAM Clear", ES8375_ADC1, ADC_RAMCLR_SHIFT_5, 1, 0),
+ SOC_ENUM("DMIC Polarity", es8375_dmic_pol),
+ SOC_SINGLE_TLV("DMIC Volume", ES8375_ADC1,
+ DMIC_GAIN_SHIFT_2, ES8375_DMIC_GAIN_MAX,
+ 0, es8375_adc_dmic_volume_tlv),
+ SOC_ENUM("ADC Ramp Rate", es8375_adc_ramprate),
+ SOC_SINGLE_TLV("ADC Volume", ES8375_ADC_VOLUME,
+ ADC_VOLUME_SHIFT_0, ES8375_ADC_VOLUME_MAX,
+ 0, es8375_adc_volume_tlv),
+ SOC_SINGLE("ADC Automute Switch", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_SHIFT_7, 1, 0),
+ SOC_ENUM("ADC Automute Winsize", es8375_adc_automute_ws),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Volume", ES8375_ADC_AUTOMUTE_ATTN,
+ ADC_AUTOMUTE_ATTN_SHIFT_0, ES8375_ADC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_adc_automute_attn_tlv),
+ SOC_ENUM("ADC HPF", es8375_adc_hpf),
+
+ SOC_SINGLE("DAC DSM Mute Switch", ES8375_DAC1, DAC_DSMMUTE_SHIFT_7, 1, 0),
+ SOC_SINGLE("DAC DEM Mute Switch", ES8375_DAC1, DAC_DEMMUTE_SHIFT_6, 1, 0),
+ SOC_SINGLE("DAC Invert Switch", ES8375_DAC1, DAC_INV_SHIFT_5, 1, 0),
+ SOC_SINGLE("DAC RAM Clear", ES8375_DAC1, DAC_RAMCLR_SHIFT_4, 1, 0),
+ SOC_ENUM("DAC Ramp Rate", es8375_dac_ramprate),
+ SOC_SINGLE_TLV("DAC Volume", ES8375_DAC_VOLUME,
+ DAC_VOLUME_SHIFT_0, ES8375_DAC_VOLUME_MAX,
+ 0, es8375_dac_volume_tlv),
+ SOC_SINGLE_TLV("DAC VPP Scale", ES8375_DAC_VPPSCALE,
+ DAC_VPPSCALE_SHIFT_0, ES8375_DAC_VPPSCALE_MAX,
+ 0, es8375_dac_vppscale_tlv),
+ SOC_SINGLE("DAC Automute Switch", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_EN_SHIFT_7, 1, 0),
+ SOC_SINGLE_TLV("DAC Automute Noise Gate", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_ENUM("DAC Automute Winsize", es8375_dac_automute_ws),
+ SOC_SINGLE_TLV("DAC Automute Volume", ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_ATTN_SHIFT_0, ES8375_DAC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_dac_automute_attn_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8375_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8375_SDP2,
+ ES8375_ADC_P2S_MUTE_SHIFT_5, 1),
+
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8375_dmic_mux_controls),
+
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8375_SDP,
+ SND_SOC_NOPM, 0),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8375_dapm_routes[] = {
+ {"ADC MUX", "AMIC", "MIC1"},
+ {"ADC MUX", "DMIC", "DMIC"},
+ {"PGA", NULL, "ADC MUX"},
+ {"Mono ADC", NULL, "PGA"},
+ {"AIF1TX", NULL, "Mono ADC"},
+
+ {"Mono DAC", NULL, "AIF1RX"},
+ {"OUT", NULL, "Mono DAC"},
+};
+
+struct _coeff_div {
+ u16 mclk_lrck_ratio;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0B;
+ u8 Reg0x19;
+ u8 dvdd_vol;
+ u8 dmic_sel;
+};
+
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
+ {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
+ {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2},
+ {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2},
+ {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2},
+ {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2},
+ {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2},
+ {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2},
+ {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2},
+ {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2},
+ {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2},
+ {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2},
+ {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2},
+};
+
+static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
+{
+ int i;
+ u8 dmic_det, vddd_det;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
+ vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01;
+ dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01;
+ vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01;
+ dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01;
+
+ if (vddd_det && dmic_det)
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int es8375_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int par_width = params_width(params);
+ u8 dmic_enable, iface = 0;
+ unsigned int regv;
+ int coeff, ret;
+
+ if (es8375->mclk_src == ES8375_BCLK_PIN) {
+ regmap_update_bits(es8375->regmap,
+ ES8375_MCLK_SEL, 0x80, 0x80);
+
+ es8375->mclk_freq = 2 * (unsigned int)par_width * params_rate(params);
+ }
+
+ regmap_read(es8375->regmap, ES8375_ADC1, &regv);
+ dmic_enable = regv >> 7 & 0x01;
+
+ ret = regulator_get_voltage(es8375->core_supply[ES8375_SUPPLY_VD].consumer);
+ switch (ret) {
+ case 1800000 ... 2000000:
+ es8375->vddd = ES8375_1V8;
+ break;
+ case 2500000 ... 3300000:
+ es8375->vddd = ES8375_3V3;
+ break;
+ default:
+ es8375->vddd = ES8375_3V3;
+ break;
+ }
+
+ coeff = get_coeff(es8375->vddd, dmic_enable, es8375->mclk_freq, params_rate(params));
+ if (coeff < 0) {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ return coeff;
+ }
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4,
+ coeff_div[coeff].Reg0x04);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5,
+ coeff_div[coeff].Reg0x05);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6,
+ coeff_div[coeff].Reg0x06);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7,
+ coeff_div[coeff].Reg0x07);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8,
+ coeff_div[coeff].Reg0x08);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9,
+ coeff_div[coeff].Reg0x09);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10,
+ coeff_div[coeff].Reg0x0A);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR11,
+ coeff_div[coeff].Reg0x0B);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN,
+ coeff_div[coeff].Reg0x19);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iface |= 0x0c;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x10;
+ break;
+ }
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x1c, iface);
+
+ return 0;
+}
+
+static int es8375_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mclk_freq = freq;
+
+ return 0;
+}
+
+static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int iface, codeciface;
+
+ regmap_read(es8375->regmap, ES8375_SDP, &codeciface);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ es8375->mastermode = 1;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8375->mastermode = 0;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codeciface &= 0xFC;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ codeciface &= 0xFC;
+ codeciface |= 0x01;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ codeciface &= 0xDC;
+ codeciface |= 0x03;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ codeciface &= 0xDC;
+ codeciface |= 0x23;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_read(es8375->regmap, ES8375_CLK_MGR3, &iface);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xFE;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x01;
+ codeciface |= 0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x01;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xFE;
+ codeciface |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, iface);
+ regmap_write(es8375->regmap, ES8375_SDP, codeciface);
+
+ return 0;
+}
+
+static int es8375_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+ regmap_write(es8375->regmap, ES8375_CSM1, 0xA6);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ clk_disable_unprepare(es8375->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+static int es8375_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x00);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x00);
+ }
+
+ return 0;
+}
+
+#define es8375_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8375_ops = {
+ .hw_params = es8375_hw_params,
+ .mute_stream = es8375_mute,
+ .set_sysclk = es8375_set_sysclk,
+ .set_fmt = es8375_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver es8375_dai = {
+ .name = "ES8375 HiFi",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .ops = &es8375_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8375_init(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x95);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_DIV_SPKCLK, 0x18);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x82);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20);
+ regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0);
+ regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE);
+ regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ msleep(80);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x86);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6, 0x31);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7, 0x11);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8, 0x1F);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9, 0x00);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN, 0x1F);
+ regmap_write(es8375->regmap, ES8375_ADC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7);
+ regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0);
+ regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE);
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+}
+
+static int es8375_suspend(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ regcache_cache_only(es8375->regmap, true);
+ regcache_mark_dirty(es8375->regmap);
+ return 0;
+}
+
+static int es8375_resume(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8375->regmap, false);
+ regcache_cache_bypass(es8375->regmap, true);
+ regmap_read(es8375->regmap, ES8375_CLK_MGR2, &reg);
+ regcache_cache_bypass(es8375->regmap, false);
+
+ if (reg == 0x00)
+ es8375_init(component);
+ else
+ es8375_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8375->regmap);
+
+ return 0;
+}
+
+static int es8375_codec_probe(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mastermode = 0;
+
+ es8375_init(component);
+
+ return 0;
+}
+
+static bool es8375_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8375_CHIP_VERSION:
+ case ES8375_CHIP_ID0:
+ case ES8375_CHIP_ID1:
+ case ES8375_SPK_OFFSET:
+ case ES8375_FLAGS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8375_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8375_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .writeable_reg = es8375_writeable_register,
+};
+
+static struct snd_soc_component_driver es8375_codec_driver = {
+ .probe = es8375_codec_probe,
+ .suspend = es8375_suspend,
+ .resume = es8375_resume,
+ .set_bias_level = es8375_set_bias_level,
+ .controls = es8375_snd_controls,
+ .num_controls = ARRAY_SIZE(es8375_snd_controls),
+ .dapm_widgets = es8375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8375_dapm_widgets),
+ .dapm_routes = es8375_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8375_dapm_routes),
+
+ .idle_bias_on = 1,
+ .suspend_bias_off = 1,
+};
+
+static int es8375_read_device_properities(struct device *dev, struct es8375_priv *es8375)
+{
+ int ret, i;
+
+ ret = device_property_read_u8(dev, "everest,mclk-src", &es8375->mclk_src);
+ if (ret != 0)
+ es8375->mclk_src = ES8375_MCLK_SOURCE;
+ dev_dbg(dev, "mclk-src %x", es8375->mclk_src);
+
+ for (i = 0; i < ARRAY_SIZE(es8375_core_supplies); i++)
+ es8375->core_supply[i].supply = es8375_core_supplies[i];
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ es8375->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(es8375->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8375->mclk), "unable to get mclk\n");
+
+ if (!es8375->mclk)
+ dev_warn(dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(dev, "unable to enable mclk\n");
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(es8375->mclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8375_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8375_priv *es8375;
+ struct device *dev = &i2c_client->dev;
+ int ret;
+ unsigned int val;
+
+ es8375 = devm_kzalloc(&i2c_client->dev, sizeof(*es8375), GFP_KERNEL);
+ if (!es8375)
+ return -ENOMEM;
+
+ es8375->regmap = devm_regmap_init_i2c(i2c_client,
+ &es8375_regmap_config);
+ if (IS_ERR(es8375->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8375->regmap),
+ "regmap_init() failed\n");
+
+ i2c_set_clientdata(i2c_client, es8375);
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID1, &val);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
+ i2c_client->addr);
+ return ret;
+ }
+
+ if (val != 0x83) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID0, &val);
+ if (val != 0x75) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = es8375_read_device_properities(dev, es8375);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev, "get an error from dts info %X\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c_client->dev, &es8375_codec_driver,
+ &es8375_dai, 1);
+}
+
+static void es8375_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8375_priv *es8375;
+
+ es8375 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3C);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x80);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3E);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x15);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x0C);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x00);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ clk_disable_unprepare(es8375->mclk);
+}
+
+static const struct i2c_device_id es8375_id[] = {
+ {"es8375"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8375_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8375_acpi_match[] = {
+ {"ESSX8375", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, es8375_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8375_of_match[] = {
+ {.compatible = "everest,es8375",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, es8375_of_match);
+#endif
+
+static struct i2c_driver es8375_i2c_driver = {
+ .driver = {
+ .name = "es8375",
+ .of_match_table = of_match_ptr(es8375_of_match),
+ .acpi_match_table = ACPI_PTR(es8375_acpi_match),
+ },
+ .shutdown = es8375_i2c_shutdown,
+ .probe = es8375_i2c_probe,
+ .id_table = es8375_id,
+};
+module_i2c_driver(es8375_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8375 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h
new file mode 100644
index 000000000000..11e3ceec9b68
--- /dev/null
+++ b/sound/soc/codecs/es8375.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8375.h -- ES8375 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8375.h by Michael Zhang
+*/
+#ifndef _ES8375_H
+#define _ES8375_H
+
+// Registors
+#define ES8375_RESET1 0x00
+#define ES8375_MCLK_SEL 0x01
+#define ES8375_CLK_MGR2 0x02
+#define ES8375_CLK_MGR3 0x03
+#define ES8375_CLK_MGR4 0x04
+#define ES8375_CLK_MGR5 0x05
+#define ES8375_CLK_MGR6 0x06
+#define ES8375_CLK_MGR7 0x07
+#define ES8375_CLK_MGR8 0x08
+#define ES8375_CLK_MGR9 0x09
+#define ES8375_CLK_MGR10 0x0A
+#define ES8375_CLK_MGR11 0x0B
+#define ES8375_CLK_MGR12 0x0C
+#define ES8375_DIV_SPKCLK 0x0E
+#define ES8375_CSM1 0x0F
+#define ES8375_CSM2 0x10
+#define ES8375_VMID_CHARGE2 0x11
+#define ES8375_VMID_CHARGE3 0x12
+#define ES8375_SDP 0x15
+#define ES8375_SDP2 0x16
+#define ES8375_ADC1 0x17
+#define ES8375_ADC2 0x18
+#define ES8375_ADC_OSR_GAIN 0x19
+#define ES8375_ADC_VOLUME 0x1A
+#define ES8375_ADC_AUTOMUTE 0x1B
+#define ES8375_ADC_AUTOMUTE_ATTN 0x1C
+#define ES8375_HPF1 0x1D
+#define ES8375_DAC1 0x1F
+#define ES8375_DAC2 0x20
+#define ES8375_DAC_VOLUME 0x21
+#define ES8375_DAC_VPPSCALE 0x22
+#define ES8375_DAC_AUTOMUTE1 0x23
+#define ES8375_DAC_AUTOMUTE 0x24
+#define ES8375_DAC_CAL 0x25
+#define ES8375_DAC_OTP 0x27
+#define ES8375_ANALOG_SPK1 0x28
+#define ES8375_ANALOG_SPK2 0x29
+#define ES8375_VMID_SEL 0x2D
+#define ES8375_ANALOG1 0x2E
+#define ES8375_ANALOG2 0x32
+#define ES8375_ANALOG3 0x37
+#define ES8375_ADC2DAC_CLKTRI 0xF8
+#define ES8375_SYS_CTRL2 0xF9
+#define ES8375_FLAGS2 0xFB
+#define ES8375_SPK_OFFSET 0xFC
+#define ES8375_CHIP_ID1 0xFD
+#define ES8375_CHIP_ID0 0xFE
+#define ES8375_CHIP_VERSION 0xFF
+
+// Bit Shifts
+#define ADC_OSR_GAIN_SHIFT_0 0
+#define ADC_RAMPRATE_SHIFT_0 0
+#define ADC_VOLUME_SHIFT_0 0
+#define ADC_AUTOMUTE_NG_SHIFT_0 0
+#define ADC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DAC_RAMPRATE_SHIFT_0 0
+#define DAC_VOLUME_SHIFT_0 0
+#define DAC_VPPSCALE_SHIFT_0 0
+#define DAC_AUTOMUTE_NG_SHIFT_0 0
+#define DAC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DMIC_GAIN_SHIFT_2 2
+#define ADC_AUTOMUTE_WS_SHIFT_3 3
+#define DMIC_POL_SHIFT_4 4
+#define DAC_RAMCLR_SHIFT_4 4
+#define ES8375_EN_MODL_SHIFT_4 4
+#define ADC_RAMCLR_SHIFT_5 5
+#define ADC_HPF_SHIFT_5 5
+#define DAC_INV_SHIFT_5 5
+#define DAC_AUTOMUTE_WS_SHIFT_5 5
+#define ES8375_EN_PGAL_SHIFT_5 5
+#define ES8375_ADC_P2S_MUTE_SHIFT_5 5
+#define ADC_INV_SHIFT_6 6
+#define DAC_DEMMUTE_SHIFT_6 6
+#define ES8375_DAC_S2P_MUTE_SHIFT_6 6
+#define ADC_SRC_SHIFT_7 7
+#define ADC_AUTOMUTE_SHIFT_7 7
+#define DAC_DSMMUTE_SHIFT_7 7
+#define DAC_AUTOMUTE_EN_SHIFT_7 7
+
+// Function values
+#define ES8375_ADC_OSR_GAIN_MAX 0x3F
+#define ES8375_DMIC_GAIN_MAX 0x04
+#define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F
+#define ES8375_AUTOMUTE_NG_MAX 0x07
+#define ES8375_ADC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VPPSCALE_MAX 0x3F
+#define ES8375_DAC_AUTOMUTE_ATTN_MAX 0x17
+#define ES8375_REG_MAX 0xFF
+
+enum ES8375_supplies {
+ ES8375_SUPPLY_VD = 0,
+ ES8375_SUPPLY_VA,
+};
+
+// Properties
+#define ES8375_3V3 1
+#define ES8375_1V8 0
+
+#define ES8375_MCLK_PIN 0
+#define ES8375_BCLK_PIN 1
+#define ES8375_MCLK_SOURCE ES8375_MCLK_PIN
+
+#define DMIC_POSITIVE_EDGE 0
+#define DMIC_NEGATIVE_EDGE 1
+#define DMIC_POL DMIC_POSITIVE_EDGE
+
+#define PA_SHUTDOWN 0
+#define PA_ENABLE 1
+
+#endif
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
new file mode 100644
index 000000000000..a84d79f9d3d1
--- /dev/null
+++ b/sound/soc/codecs/es8389.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8389.c -- ES8389 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "es8389.h"
+
+
+/* codec private data */
+
+struct es8389_private {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int sysclk;
+ int mastermode;
+
+ u8 mclk_src;
+ enum snd_soc_bias_level bias_level;
+};
+
+static bool es8389_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ if ((reg <= 0xff))
+ return true;
+ else
+ return false;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0);
+
+static int es8389_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ bool changed1, changed2;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > 1)
+ return -EINVAL;
+
+ if (val) {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0xC0, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x03, &changed2);
+ } else {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00, &changed2);
+ }
+
+ if (changed1 & changed2)
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol, val, e, NULL);
+ else
+ return 0;
+}
+
+static const char *const alc[] = {
+ "ALC OFF",
+ "ADCR ALC ON",
+ "ADCL ALC ON",
+ "ADCL & ADCL ALC ON",
+};
+
+static const char *const ramprate[] = {
+ "0.125db/1 LRCK",
+ "0.125db/4 LRCK",
+ "0.125db/8 LRCK",
+ "0.125db/16 LRCK",
+ "0.125db/32 LRCK",
+ "0.125db/64 LRCK",
+ "0.125db/128 LRCK",
+ "0.125db/256 LRCK",
+ "0.125db/512 LRCK",
+ "0.125db/1024 LRCK",
+ "0.125db/2048 LRCK",
+ "0.125db/4096 LRCK",
+ "0.125db/8192 LRCK",
+ "0.125db/16384 LRCK",
+ "0.125db/32768 LRCK",
+ "0.125db/65536 LRCK",
+};
+
+static const char *const winsize[] = {
+ "2 LRCK",
+ "4 LRCK",
+ "8 LRCK",
+ "16 LRCK",
+ "32 LRCK",
+ "64 LRCK",
+ "128 LRCK",
+ "256 LRCK",
+ "512 LRCK",
+ "1024 LRCK",
+ "2048 LRCK",
+ "4096 LRCK",
+ "8192 LRCK",
+ "16384 LRCK",
+ "32768 LRCK",
+ "65536 LRCK",
+};
+
+static const struct soc_enum alc_enable =
+ SOC_ENUM_SINGLE(ES8389_ALC_ON, 5, 4, alc);
+static const struct soc_enum alc_ramprate =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 4, 16, ramprate);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize);
+
+static const char *const es8389_outl_mux_txt[] = {
+ "Normal",
+ "DAC2 channel to DAC1 channel",
+};
+
+static const char *const es8389_outr_mux_txt[] = {
+ "Normal",
+ "DAC1 channel to DAC2 channel",
+};
+
+static const char *const es8389_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+
+static const char *const es8389_pga1_texts[] = {
+ "DifferentialL", "Line 1P", "Line 2P"
+};
+
+static const char *const es8389_pga2_texts[] = {
+ "DifferentialR", "Line 2N", "Line 1N"
+};
+
+static const unsigned int es8389_pga_values[] = {
+ 1, 5, 6
+};
+
+static const struct soc_enum es8389_outl_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5,
+ ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outl_mux_controls =
+ SOC_DAPM_ENUM("OUTL MUX", es8389_outl_mux_enum);
+
+static const struct soc_enum es8389_outr_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 4,
+ ARRAY_SIZE(es8389_outr_mux_txt), es8389_outr_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outr_mux_controls =
+ SOC_DAPM_ENUM("OUTR MUX", es8389_outr_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ es8389_dmic_mux_enum, ES8389_DMIC_EN, 6, es8389_dmic_mux_txt);
+
+static const struct soc_enum es8389_pgal_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC1_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga1_texts), es8389_pga1_texts,
+ es8389_pga_values);
+
+static const struct soc_enum es8389_pgar_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC2_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga2_texts), es8389_pga2_texts,
+ es8389_pga_values);
+
+static const struct snd_kcontrol_new es8389_dmic_mux_controls =
+ SOC_DAPM_ENUM_EXT("ADC MUX", es8389_dmic_mux_enum,
+ snd_soc_dapm_get_enum_double, es8389_dmic_set);
+
+static const struct snd_kcontrol_new es8389_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR DACL Mixer", ES8389_DAC_MIX, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL DACR Mixer", ES8389_DAC_MIX, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_leftadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCL DACL Mixer", ES8389_DAC_MIX, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_rightadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCR DACR Mixer", ES8389_DAC_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL ADCL Mixer", ES8389_ADC_RESET, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR ADCR Mixer", ES8389_ADC_RESET, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_snd_controls[] = {
+ SOC_SINGLE_TLV("ADCL Capture Volume", ES8389_ADCL_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCR Capture Volume", ES8389_ADCR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCL PGA Volume", ES8389_MIC1_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+ SOC_SINGLE_TLV("ADCR PGA Volume", ES8389_MIC2_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+
+ SOC_ENUM("PGAL Select", es8389_pgal_enum),
+ SOC_ENUM("PGAR Select", es8389_pgar_enum),
+ SOC_ENUM("ALC Capture Switch", alc_enable),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8389_ALC_TARGET,
+ 0, 0x0f, 0, alc_target_tlv),
+ SOC_SINGLE_TLV("ALC Capture Max Gain", ES8389_ALC_GAIN,
+ 0, 0x0f, 0, alc_max_level),
+ SOC_ENUM("ADC Ramp Rate", alc_ramprate),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0),
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0),
+
+ SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC OUTPUT Invert Switch", ES8389_DAC_INV, 5, 6, 1, 0),
+ SOC_SINGLE_TLV("ADC2DAC Mixer Volume", ES8389_MIX_VOL, 0, 0x7F, 0, mix_vol_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = {
+ /*Input Side*/
+ SND_SOC_DAPM_INPUT("INPUT1"),
+ SND_SOC_DAPM_INPUT("INPUT2"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGAL", SND_SOC_NOPM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGAR", SND_SOC_NOPM, 4, 0, NULL, 0),
+
+ /*ADCs*/
+ SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /*DACs*/
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /*Output Side*/
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MIXER("IF DACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_left_mixer_controls[0],
+ ARRAY_SIZE(es8389_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF DACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_right_mixer_controls[0],
+ ARRAY_SIZE(es8389_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_leftadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_leftadc_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_rightadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_rightadc_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_adc_mixer_controls[0],
+ ARRAY_SIZE(es8389_adc_mixer_controls)),
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls),
+
+ SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls),
+ SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls),
+};
+
+
+static const struct snd_soc_dapm_route es8389_dapm_routes[] = {
+ {"PGAL", NULL, "INPUT1"},
+ {"PGAR", NULL, "INPUT2"},
+
+ {"ADCL", NULL, "PGAL"},
+ {"ADCR", NULL, "PGAR"},
+
+ {"ADC Mixer", "DACL ADCL Mixer", "DACL"},
+ {"ADC Mixer", "DACR ADCR Mixer", "DACR"},
+ {"ADC Mixer", NULL, "ADCL"},
+ {"ADC Mixer", NULL, "ADCR"},
+
+ {"ADC MUX", "AMIC", "ADC Mixer"},
+ {"ADC MUX", "DMIC", "DMIC"},
+
+ {"I2S OUT", NULL, "ADC MUX"},
+
+ {"DACL", NULL, "I2S IN"},
+ {"DACR", NULL, "I2S IN"},
+
+ {"IF DACL1", NULL, "DACL"},
+ {"IF DACR1", NULL, "DACR"},
+ {"IF DACL2", NULL, "DACL"},
+ {"IF DACR2", NULL, "DACR"},
+ {"IF DACL3", NULL, "DACL"},
+ {"IF DACR3", NULL, "DACR"},
+
+ {"IF DACL Mixer", NULL, "IF DACL2"},
+ {"IF DACL Mixer", "DACR DACL Mixer", "IF DACR1"},
+ {"IF DACR Mixer", NULL, "IF DACR2"},
+ {"IF DACR Mixer", "DACL DACR Mixer", "IF DACL1"},
+
+ {"IF ADCDACL Mixer", NULL, "IF DACL Mixer"},
+ {"IF ADCDACL Mixer", "ADCL DACL Mixer", "IF DACL3"},
+ {"IF ADCDACR Mixer", NULL, "IF DACR Mixer"},
+ {"IF ADCDACR Mixer", "ADCR DACR Mixer", "IF DACR3"},
+
+ {"OUTL MUX", "Normal", "IF ADCDACL Mixer"},
+ {"OUTL MUX", "DAC2 channel to DAC1 channel", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "Normal", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "DAC1 channel to DAC2 channel", "IF ADCDACL Mixer"},
+
+ {"HPOL", NULL, "OUTL MUX"},
+ {"HPOR", NULL, "OUTR MUX"},
+
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0F;
+ u8 Reg0x11;
+ u8 Reg0x21;
+ u8 Reg0x22;
+ u8 Reg0x26;
+ u8 Reg0x30;
+ u8 Reg0x41;
+ u8 Reg0x42;
+ u8 Reg0x43;
+ u8 Reg0xF0;
+ u8 Reg0xF1;
+ u8 Reg0x16;
+ u8 Reg0x18;
+ u8 Reg0x19;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14},
+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B},
+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25},
+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F},
+
+ {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * if PLL not be used, use internal clk1 for mclk,otherwise, use internal clk2 for PLL source.
+ */
+static int es8389_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389->sysclk = freq;
+
+ return 0;
+}
+
+static int es8389_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(es8389->regmap, ES8389_PTDM_SLOT,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+ regmap_update_bits(es8389->regmap, ES8389_DAC_RAMP,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+
+ return 0;
+}
+
+static int es8389_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ u8 state = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_MODE,
+ ES8389_MASTER_MODE_EN, ES8389_MASTER_MODE_EN);
+ es8389->mastermode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8389->mastermode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ state |= ES8389_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "component driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ state |= ES8389_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ state |= ES8389_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ state |= ES8389_DAIFMT_DSP_B;
+ break;
+ default:
+ break;
+ }
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+
+ return 0;
+}
+
+static int es8389_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ int coeff;
+ u8 state = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ state |= ES8389_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ state |= ES8389_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ state |= ES8389_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ state |= ES8389_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ state |= ES8389_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+
+ if (es8389->mclk_src == ES8389_SCLK_PIN) {
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK,
+ ES8389_MCLK_SOURCE, es8389->mclk_src);
+ es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params);
+ }
+
+ coeff = get_coeff(es8389->sysclk, params_rate(params));
+ if (coeff >= 0) {
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, coeff_div[coeff].Reg0x06);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, coeff_div[coeff].Reg0x07);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, coeff_div[coeff].Reg0x08);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, coeff_div[coeff].Reg0x09);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, coeff_div[coeff].Reg0x0A);
+ regmap_update_bits(es8389->regmap, ES8389_OSC_CLK,
+ 0xC0, coeff_div[coeff].Reg0x0F);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV2, coeff_div[coeff].Reg0x11);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, coeff_div[coeff].Reg0x21);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, coeff_div[coeff].Reg0x22);
+ regmap_write(es8389->regmap, ES8389_OSR_VOL, coeff_div[coeff].Reg0x26);
+ regmap_update_bits(es8389->regmap, ES8389_SYSTEM30,
+ 0xC0, coeff_div[coeff].Reg0x30);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, coeff_div[coeff].Reg0x41);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, coeff_div[coeff].Reg0x42);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_MISC,
+ 0x81, coeff_div[coeff].Reg0x43);
+ regmap_update_bits(es8389->regmap, ES8389_CHIP_MISC,
+ 0x72, coeff_div[coeff].Reg0xF0);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, coeff_div[coeff].Reg0xF1);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, coeff_div[coeff].Reg0x16);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, coeff_div[coeff].Reg0x18);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, coeff_div[coeff].Reg0x19);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8389_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x3E);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80);
+ usleep_range(8000, 8500);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00);
+
+ clk_disable_unprepare(es8389->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+
+
+static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x03);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x03);
+ }
+ } else {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x00);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x00);
+ }
+ }
+
+ return 0;
+}
+
+#define es8389_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8389_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8389_ops = {
+ .hw_params = es8389_pcm_hw_params,
+ .set_fmt = es8389_set_dai_fmt,
+ .set_sysclk = es8389_set_dai_sysclk,
+ .set_tdm_slot = es8389_set_tdm_slot,
+ .mute_stream = es8389_mute,
+};
+
+static struct snd_soc_dai_driver es8389_dai = {
+ .name = "ES8389 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .ops = &es8389_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8389_init(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF2, 0x04);
+ regmap_write(es8389->regmap, ES8389_DAC_INV, 0x03);
+
+ regmap_write(es8389->regmap, ES8389_VMID, 0x2A);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xC9);
+ regmap_write(es8389->regmap, ES8389_ANA_VSEL, 0x4F);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL2, 0x06);
+ regmap_write(es8389->regmap, ES8389_LOW_POWER1, 0x00);
+ regmap_write(es8389->regmap, ES8389_DMIC_EN, 0x16);
+
+ regmap_write(es8389->regmap, ES8389_PGA_SW, 0xAA);
+ regmap_write(es8389->regmap, ES8389_MOD_SW1, 0x66);
+ regmap_write(es8389->regmap, ES8389_MOD_SW2, 0x99);
+ regmap_write(es8389->regmap, ES8389_ADC_MODE, (0x00 | ES8389_TDM_MODE));
+ regmap_update_bits(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00);
+
+ regmap_update_bits(es8389->regmap, ES8389_MIC1_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_update_bits(es8389->regmap, ES8389_MIC2_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xC4);
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x08);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, 0x00);
+ regmap_write(es8389->regmap, ES8389_SYSTEM12, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM13, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM14, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM15, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, 0x35);
+ regmap_write(es8389->regmap, ES8389_SYSTEM17, 0x09);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, 0x91);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, 0x28);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1A, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1B, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1C, 0x11);
+
+ regmap_write(es8389->regmap, ES8389_CHIP_MISC, 0x13);
+ regmap_write(es8389->regmap, ES8389_MASTER_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, 0x10);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, 0x80);
+ regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00);
+ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F);
+ regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0);
+ regmap_write(es8389->regmap, ES8389_SYSTEM30, 0xF4);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_MISC, 0x10);
+ regmap_write(es8389->regmap, ES8389_DAC_RAMP, 0x0F);
+ regmap_write(es8389->regmap, ES8389_SYSTEM4C, 0xC0);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC1);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0x02);
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x03);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x03);
+}
+
+static int es8389_suspend(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+ regcache_cache_only(es8389->regmap, true);
+ regcache_mark_dirty(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_resume(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int regv;
+
+ regcache_cache_only(es8389->regmap, false);
+ regcache_cache_bypass(es8389->regmap, true);
+ regmap_read(es8389->regmap, ES8389_RESET, &regv);
+ regcache_cache_bypass(es8389->regmap, false);
+
+ if (regv == 0xff)
+ es8389_init(component);
+ else
+ es8389_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mclk-src return %d", ret);
+ es8389->mclk_src = ES8389_MCLK_SOURCE;
+ }
+
+ es8389->mclk = devm_clk_get(component->dev, "mclk");
+ if (IS_ERR(es8389->mclk))
+ return dev_err_probe(component->dev, PTR_ERR(es8389->mclk),
+ "ES8389 is unable to get mclk\n");
+
+ if (!es8389->mclk)
+ dev_err(component->dev, "%s, assuming static mclk\n", __func__);
+
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret) {
+ dev_err(component->dev, "%s, unable to enable mclk\n", __func__);
+ return ret;
+ }
+
+ es8389_init(component);
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static void es8389_remove(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_es8389 = {
+ .probe = es8389_probe,
+ .remove = es8389_remove,
+ .suspend = es8389_suspend,
+ .resume = es8389_resume,
+ .set_bias_level = es8389_set_bias_level,
+
+ .controls = es8389_snd_controls,
+ .num_controls = ARRAY_SIZE(es8389_snd_controls),
+ .dapm_widgets = es8389_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8389_dapm_widgets),
+ .dapm_routes = es8389_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8389_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+};
+
+static const struct regmap_config es8389_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ES8389_MAX_REGISTER,
+
+ .volatile_reg = es8389_volatile_register,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static void es8389_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8389_private *es8389;
+
+ es8389 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+}
+
+static int es8389_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8389_private *es8389;
+ int ret;
+
+ es8389 = devm_kzalloc(&i2c_client->dev, sizeof(*es8389), GFP_KERNEL);
+ if (es8389 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8389);
+ es8389->regmap = devm_regmap_init_i2c(i2c_client, &es8389_regmap);
+ if (IS_ERR(es8389->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->regmap),
+ "regmap_init() failed\n");
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_codec_dev_es8389,
+ &es8389_dai,
+ 1);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8389_if_dt_ids[] = {
+ { .compatible = "everest,es8389", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8389_if_dt_ids);
+#endif
+
+static const struct i2c_device_id es8389_i2c_id[] = {
+ {"es8389"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8389_i2c_id);
+
+static struct i2c_driver es8389_i2c_driver = {
+ .driver = {
+ .name = "es8389",
+ .of_match_table = of_match_ptr(es8389_if_dt_ids),
+ },
+ .shutdown = es8389_i2c_shutdown,
+ .probe = es8389_i2c_probe,
+ .id_table = es8389_i2c_id,
+};
+module_i2c_driver(es8389_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8389 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h
new file mode 100644
index 000000000000..123d1e4b2d53
--- /dev/null
+++ b/sound/soc/codecs/es8389.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8389.h -- ES8389 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8374.h by Michael Zhang
+*/
+
+#ifndef _ES8389_H
+#define _ES8389_H
+
+/*
+* ES8389_REGISTER NAME_REG_REGISTER ADDRESS
+*/
+#define ES8389_RESET 0x00 /*reset digital,csm,clock manager etc.*/
+
+/*
+* Clock Scheme Register definition
+*/
+#define ES8389_MASTER_MODE 0x01
+#define ES8389_MASTER_CLK 0x02
+#define ES8389_CLK_OFF1 0x03
+#define ES8389_CLK_DIV1 0x04
+#define ES8389_CLK_MUL 0x05
+#define ES8389_CLK_MUX1 0x06
+#define ES8389_CLK_MUX2 0x07
+#define ES8389_CLK_CTL1 0x08
+#define ES8389_CLK_CTL2 0x09
+#define ES8389_CLK_CTL3 0x0A
+#define ES8389_SCLK_DIV 0x0B
+#define ES8389_LRCK_DIV1 0x0C
+#define ES8389_LRCK_DIV2 0x0D
+#define ES8389_CLK_OFF2 0x0E
+#define ES8389_OSC_CLK 0x0F
+#define ES8389_CSM_JUMP 0x10
+#define ES8389_CLK_DIV2 0x11
+#define ES8389_SYSTEM12 0x12
+#define ES8389_SYSTEM13 0x13
+#define ES8389_SYSTEM14 0x14
+#define ES8389_SYSTEM15 0x15
+#define ES8389_SYSTEM16 0x16
+#define ES8389_SYSTEM17 0x17
+#define ES8389_SYSTEM18 0x18
+#define ES8389_SYSTEM19 0x19
+#define ES8389_SYSTEM1A 0x1A
+#define ES8389_SYSTEM1B 0x1B
+#define ES8389_SYSTEM1C 0x1C
+#define ES8389_ADC_FORMAT_MUTE 0x20
+#define ES8389_ADC_OSR 0x21
+#define ES8389_ADC_DSP 0x22
+#define ES8389_ADC_MODE 0x23
+#define ES8389_ADC_HPF1 0x24
+#define ES8389_ADC_HPF2 0x25
+#define ES8389_OSR_VOL 0x26
+#define ES8389_ADCL_VOL 0x27
+#define ES8389_ADCR_VOL 0x28
+#define ES8389_ALC_CTL 0x29
+#define ES8389_PTDM_SLOT 0x2A
+#define ES8389_ALC_ON 0x2B
+#define ES8389_ALC_TARGET 0x2C
+#define ES8389_ALC_GAIN 0x2D
+#define ES8389_SYSTEM2E 0x2E
+#define ES8389_ADC_MUTE 0x2F
+#define ES8389_SYSTEM30 0x30
+#define ES8389_ADC_RESET 0x31
+#define ES8389_DAC_FORMAT_MUTE 0x40
+#define ES8389_DAC_DSM_OSR 0x41
+#define ES8389_DAC_DSP_OSR 0x42
+#define ES8389_DAC_MISC 0x43
+#define ES8389_DAC_MIX 0x44
+#define ES8389_DAC_INV 0x45
+#define ES8389_DACL_VOL 0x46
+#define ES8389_DACR_VOL 0x47
+#define ES8389_MIX_VOL 0x48
+#define ES8389_DAC_RAMP 0x49
+#define ES8389_SYSTEM4C 0x4C
+#define ES8389_DAC_RESET 0x4D
+#define ES8389_VMID 0x60
+#define ES8389_ANA_CTL1 0x61
+#define ES8389_ANA_VSEL 0x62
+#define ES8389_ANA_CTL2 0x63
+#define ES8389_ADC_EN 0x64
+#define ES8389_HPSW 0x69
+#define ES8389_LOW_POWER1 0x6B
+#define ES8389_LOW_POWER2 0x6C
+#define ES8389_DMIC_EN 0x6D
+#define ES8389_PGA_SW 0x6E
+#define ES8389_MOD_SW1 0x6F
+#define ES8389_MOD_SW2 0x70
+#define ES8389_MOD_SW3 0x71
+#define ES8389_MIC1_GAIN 0x72
+#define ES8389_MIC2_GAIN 0x73
+
+#define ES8389_CHIP_MISC 0xF0
+#define ES8389_CSM_STATE1 0xF1
+#define ES8389_PULL_DOWN 0xF2
+#define ES8389_ISO_CTL 0xF3
+#define ES8389_CSM_STATE2 0xF4
+
+#define ES8389_CHIP_ID0 0xFD
+#define ES8389_CHIP_ID1 0xFE
+
+#define ES8389_MAX_REGISTER 0xFF
+
+#define ES8389_MIC_SEL_MASK (7 << 4)
+#define ES8389_MIC_DEFAULT (1 << 4)
+
+#define ES8389_MASTER_MODE_EN (1 << 0)
+
+#define ES8389_TDM_OFF (0 << 0)
+#define ES8389_STDM_ON (1 << 7)
+#define ES8389_PTDM_ON (1 << 6)
+
+#define ES8389_TDM_MODE ES8389_TDM_OFF
+#define ES8389_TDM_SLOT (0x70 << 0)
+#define ES8389_TDM_SHIFT 4
+
+#define ES8389_MCLK_SOURCE (1 << 6)
+#define ES8389_MCLK_PIN (1 << 6)
+#define ES8389_SCLK_PIN (0 << 6)
+
+/* ES8389_FMT */
+#define ES8389_S24_LE (0 << 5)
+#define ES8389_S20_3_LE (1 << 5)
+#define ES8389_S18_LE (2 << 5)
+#define ES8389_S16_LE (3 << 5)
+#define ES8389_S32_LE (4 << 5)
+#define ES8389_DATA_LEN_MASK (7 << 5)
+
+#define ES8389_DAIFMT_MASK (7 << 2)
+#define ES8389_DAIFMT_I2S 0
+#define ES8389_DAIFMT_LEFT_J (1 << 2)
+#define ES8389_DAIFMT_DSP_A (1 << 3)
+#define ES8389_DAIFMT_DSP_B (3 << 3)
+
+#define ES8389_STATE_ON (13 << 0)
+#define ES8389_STATE_STANDBY (7 << 0)
+
+#endif
diff --git a/sound/soc/codecs/es83xx-dsm-common.c b/sound/soc/codecs/es83xx-dsm-common.c
new file mode 100644
index 000000000000..94fd7d54c53b
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) Intel Corporation, 2022
+// Copyright Everest Semiconductor Co.,Ltd
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include "es83xx-dsm-common.h"
+
+/* UUID ("a9800c04-e016-343e-41f4-6bcce70f4332") */
+static const guid_t es83xx_dsm_guid =
+ GUID_INIT(0xa9800c04, 0xe016, 0x343e,
+ 0x41, 0xf4, 0x6b, 0xcc, 0xe7, 0x0f, 0x43, 0x32);
+
+#define ES83xx_DSM_REVID 1
+
+int es83xx_dsm(struct device *dev, int arg, int *value)
+{
+ acpi_handle dhandle;
+ union acpi_object *obj;
+ int ret = 0;
+
+ dhandle = ACPI_HANDLE(dev);
+ if (!dhandle)
+ return -ENOENT;
+
+ obj = acpi_evaluate_dsm(dhandle, &es83xx_dsm_guid, ES83xx_DSM_REVID,
+ arg, NULL);
+ if (!obj) {
+ dev_err(dev, "%s: acpi_evaluate_dsm() failed\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ dev_err(dev, "%s: object is not ACPI_TYPE_INTEGER\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ *value = obj->integer.value;
+err:
+ ACPI_FREE(obj);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm);
+
+int es83xx_dsm_dump(struct device *dev)
+{
+ int value;
+ int ret;
+
+ ret = es83xx_dsm(dev, PLATFORM_MAINMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MAINMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_SPK_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_SPK_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPDET_INV_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPDET_INV %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_PCM_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_PCM_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_MIC_DE_POP_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MIC_DE_POP %#x\n", value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm_dump);
+
+MODULE_DESCRIPTION("Everest Semi ES83xx DSM helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es83xx-dsm-common.h b/sound/soc/codecs/es83xx-dsm-common.h
new file mode 100644
index 000000000000..91c9a89e75e9
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.h
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Intel Corporation, 2022
+ * Copyright Everest Semiconductor Co.,Ltd
+ */
+
+/* Definitions extracted from ASL file provided at
+ * https://github.com/thesofproject/linux/files/9398723/ESSX8326.zip
+ */
+
+#ifndef _ES83XX_DSM_COMMON_H
+#define _ES83XX_DSM_COMMON_H
+
+/***************************************************
+ * DSM arguments *
+ ***************************************************/
+
+#define PLATFORM_MAINMIC_TYPE_ARG 0x00
+#define PLATFORM_HPMIC_TYPE_ARG 0x01
+#define PLATFORM_SPK_TYPE_ARG 0x02
+#define PLATFORM_HPDET_INV_ARG 0x03
+#define PLATFORM_PCM_TYPE_ARG 0x04
+
+#define PLATFORM_MIC_DE_POP_ARG 0x06
+#define PLATFORM_CODEC_TYPE_ARG 0x0E
+#define PLATFORM_BUS_SLOT_ARG 0x0F
+
+#define HP_CODEC_LINEIN_PGA_GAIN_ARG 0x10
+#define MAIN_CODEC_LINEIN_PGA_GAIN_ARG 0x20
+
+#define HP_CODEC_D2SEPGA_GAIN_ARG 0x11
+#define MAIN_CODEC_D2SEPGA_GAIN_ARG 0x21
+
+#define HP_CODEC_ADC_VOLUME_ARG 0x12
+#define MAIN_CODEC_ADC_VOLUME_ARG 0x22
+
+#define HP_CODEC_ADC_ALC_ENABLE_ARG 0x13
+#define MAIN_CODEC_ADC_ALC_ENABLE_ARG 0x23
+
+#define HP_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x14
+#define MAIN_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x24
+
+#define HP_CODEC_ADC_ALC_MAXGAIN_ARG 0x15
+#define MAIN_CODEC_ADC_ALC_MAXGAIN_ARG 0x25
+
+#define HP_CODEC_ADC_ALC_MINGAIN_ARG 0x16
+#define MAIN_CODEC_ADC_ALC_MINGAIN_ARG 0x26
+
+#define HP_CODEC_ADC_ALC_HLDTIME_ARG 0x17
+#define MAIN_CODEC_ADC_ALC_HLDTIME_ARG 0x27
+
+#define HP_CODEC_ADC_ALC_DCYTIME_ARG 0x18
+#define MAIN_CODEC_ADC_ALC_DCYTIME_ARG 0x28
+
+#define HP_CODEC_ADC_ALC_ATKTIME_ARG 0x19
+#define MAIN_CODEC_ADC_ALC_ATKTIME_ARG 0x29
+
+#define HP_CODEC_ADC_ALC_NGTYPE_ARG 0x1a
+#define MAIN_CODEC_ADC_ALC_NGTYPE_ARG 0x2a
+
+#define HP_CODEC_ADC_ALC_NGTHLD_ARG 0x1b
+#define MAIN_CODEC_ADC_ALC_NGTHLD_ARG 0x2b
+
+#define MAIN_CODEC_ADC_GUI_STEP_ARG 0x2c
+#define MAIN_CODEC_ADC_GUI_GAIN_RANGE_ARG 0x2c
+
+#define HEADPHONE_DUMMY_REMOVE_ENABLE_ARG 0x2e
+
+#define HP_CODEC_DAC_HPMIX_HIGAIN_ARG 0x40
+#define SPK_CODEC_DAC_HPMIX_HIGAIN_ARG 0x50
+
+#define HP_CODEC_DAC_HPMIX_VOLUME_ARG 0x41
+#define SPK_CODEC_DAC_HPMIX_VOLUME_ARG 0x51
+
+#define HP_CODEC_DAC_HPOUT_VOLUME_ARG 0x42
+#define SPK_CODEC_DAC_HPOUT_VOLUME_ARG 0x52
+
+#define HP_CODEC_LDAC_VOLUME_ARG 0x44
+#define HP_CODEC_RDAC_VOLUME_ARG 0x54
+
+#define SPK_CODEC_LDAC_VOLUME_ARG 0x45
+#define SPK_CODEC_RDAC_VOLUME_ARG 0x55
+
+#define HP_CODEC_DAC_AUTOMUTE_ARG 0x46
+#define SPK_CODEC_DAC_AUTOMUTE_ARG 0x56
+
+#define HP_CODEC_DAC_MONO_ARG 0x4A
+#define SPK_CODEC_DAC_MONO_ARG 0x5A
+
+#define HP_CTL_IO_LEVEL_ARG 0x4B
+#define SPK_CTL_IO_LEVEL_ARG 0x5B
+
+#define CODEC_GPIO0_FUNC_ARG 0x80
+#define CODEC_GPIO1_FUNC_ARG 0x81
+#define CODEC_GPIO2_FUNC_ARG 0x82
+#define CODEC_GPIO3_FUNC_ARG 0x83
+#define CODEC_GPIO4_FUNC_ARG 0x84
+
+#define PLATFORM_MCLK_LRCK_FREQ_ARG 0x85
+
+/***************************************************
+ * Values for arguments *
+ ***************************************************/
+
+/* Main and HP Mic */
+#define PLATFORM_MIC_DMIC_HIGH_LEVEL 0xAA
+#define PLATFORM_MIC_DMIC_LOW_LEVEL 0x55
+#define PLATFORM_MIC_AMIC_LIN1RIN1 0xBB
+#define PLATFORM_MIC_AMIC_LIN2RIN2 0xCC
+
+/* Speaker */
+#define PLATFORM_SPK_NONE 0x00
+#define PLATFORM_SPK_MONO 0x01
+#define PLATFORM_SPK_STEREO 0x02
+
+/* Jack Detection */
+#define PLATFORM_HPDET_NORMAL 0x00
+#define PLATFORM_HPDET_INVERTED 0x01
+
+/* PCM type (Port number + protocol) */
+/*
+ * RETURNED VALUE = 0x00, PCM PORT0, I2S
+ * 0x01, PCM PORT0, LJ
+ * 0x02, PCM PORT0, RJ
+ * 0x03, PCM PORT0, DSP-A
+ * 0x04, PCM PORT0, DSP-B
+ * 0x10, PCM PORT1, I2S
+ * 0x11, PCM PORT1, LJ
+ * 0x12, PCM PORT1, RJ
+ * 0x13, PCM PORT1, DSP-A
+ * 0x14, PCM PORT1, DSP-B
+ * 0xFF, Use default
+ *
+ * This is not used in Linux (defined by topology) and in
+ * Windows it's always DSP-A
+ */
+
+/* Depop */
+#define PLATFORM_MIC_DE_POP_OFF 0x00
+#define PLATFORM_MIC_DE_POP_ON 0x01
+
+/* Codec type */
+#define PLATFORM_CODEC_8316 16
+#define PLATFORM_CODEC_8326 26
+#define PLATFORM_CODEC_8336 36
+#define PLATFORM_CODEC_8395 95
+#define PLATFORM_CODEC_8396 96
+
+/* Bus slot (on the host) */
+/* BIT[3:0] FOR BUS NUMBER, BIT[7:4] FOR SLOT NUMBER
+ * BIT[3:0] 0 for I2S0, 1 for IS21, 2 for I2S2.
+ *
+ * On Intel platforms this refers to SSP0..2. This information
+ * is not really useful for Linux, the information is already
+ * inferred from NHLT but can be used to double-check NHLT
+ */
+
+/* Volume - Gain */
+#define LINEIN_GAIN_0db 0x00 /* gain = 0db */
+#define LINEIN_GAIN_3db 0x01 /* gain = +3db */
+#define LINEIN_GAIN_6db 0x02 /* gain = +6db */
+#define LINEIN_GAIN_9db 0x03 /* gain = +9db */
+#define LINEIN_GAIN_12db 0x04 /* gain = +12db */
+#define LINEIN_GAIN_15db 0x05 /* gain = +15db */
+#define LINEIN_GAIN_18db 0x06 /* gain = +18db */
+#define LINEIN_GAIN_21db 0x07 /* gain = +21db */
+#define LINEIN_GAIN_24db 0x08 /* gain = +24db */
+#define LINEIN_GAIN_27db 0x09 /* gain = +27db */
+#define LINEIN_GAIN_30db 0x0a /* gain = +30db */
+
+#define ADC_GUI_STEP_3db 0x03 /* gain = +3db */
+#define ADC_GUI_STEP_6db 0x06 /* gain = +6db */
+#define ADC_GUI_STEP_10db 0x0a /* gain = +10db */
+
+#define D2SEPGA_GAIN_0db 0x00 /* gain = 0db */
+#define D2SEPGA_GAIN_15db 0x01 /* gain = +15db */
+
+/* ADC volume: base = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+#define ADC_ALC_DISABLE 0x00
+#define ADC_ALC_ENABLE 0x01
+
+#define ADC_ALC_TARGET_LEVEL_m16_5db 0x00 /* gain = -16.5db */
+#define ADC_ALC_TARGET_LEVEL_m15db 0x01 /* gain = -15db */
+#define ADC_ALC_TARGET_LEVEL_m13_5db 0x02 /* gain = -13.5db */
+#define ADC_ALC_TARGET_LEVEL_m12db 0x03 /* gain = -12db */
+#define ADC_ALC_TARGET_LEVEL_m10_5db 0x04 /* gain = -10.5db */
+#define ADC_ALC_TARGET_LEVEL_m9db 0x05 /* gain = -9db */
+#define ADC_ALC_TARGET_LEVEL_m7_5db 0x06 /* gain = -7.5db */
+#define ADC_ALC_TARGET_LEVEL_m6db 0x07 /* gain = -6db */
+#define ADC_ALC_TARGET_LEVEL_m4_5db 0x08 /* gain = -4.5db */
+#define ADC_ALC_TARGET_LEVEL_m_3db 0x09 /* gain = -3db */
+#define ADC_ALC_TARGET_LEVEL_m1_5db 0x0a /* gain = -1.5db */
+
+#define ADC_ALC_MAXGAIN_m6_5db 0x00 /* gain = -6.5db */
+#define ADC_ALC_MAXGAIN_m5db 0x01 /* gain = -5db */
+#define ADC_ALC_MAXGAIN_m3_5db 0x02 /* gain = -3.5db */
+#define ADC_ALC_MAXGAIN_m2db 0x03 /* gain = -2db */
+#define ADC_ALC_MAXGAIN_m0_5db 0x04 /* gain = -0.5db */
+#define ADC_ALC_MAXGAIN_1db 0x05 /* gain = +1db */
+#define ADC_ALC_MAXGAIN_2_5db 0x06 /* gain = +2.5db */
+#define ADC_ALC_MAXGAIN_4db 0x07 /* gain = +4db */
+#define ADC_ALC_MAXGAIN_5_5db 0x08 /* gain = +5.5db */
+#define ADC_ALC_MAXGAIN_7db 0x09 /* gain = +7db */
+#define ADC_ALC_MAXGAIN_8_5db 0x0a /* gain = +8.5db */
+#define ADC_ALC_MAXGAIN_10db 0x0b /* gain = +10db */
+#define ADC_ALC_MAXGAIN_11_5db 0x0c /* gain = +11.5db */
+#define ADC_ALC_MAXGAIN_13db 0x0d /* gain = +13db */
+#define ADC_ALC_MAXGAIN_14_5db 0x0e /* gain = +14.5db */
+#define ADC_ALC_MAXGAIN_16db 0x0f /* gain = +16db */
+#define ADC_ALC_MAXGAIN_17_5db 0x10 /* gain = +17.5db */
+#define ADC_ALC_MAXGAIN_19db 0x11 /* gain = +19db */
+#define ADC_ALC_MAXGAIN_20_5db 0x12 /* gain = +20.5db */
+#define ADC_ALC_MAXGAIN_22db 0x13 /* gain = +22db */
+#define ADC_ALC_MAXGAIN_23_5db 0x14 /* gain = +23.5db */
+#define ADC_ALC_MAXGAIN_25db 0x15 /* gain = +25db */
+#define ADC_ALC_MAXGAIN_26_5db 0x16 /* gain = +26.5db */
+#define ADC_ALC_MAXGAIN_28db 0x17 /* gain = +28db */
+#define ADC_ALC_MAXGAIN_29_5db 0x18 /* gain = +29.5db */
+#define ADC_ALC_MAXGAIN_31db 0x19 /* gain = +31db */
+#define ADC_ALC_MAXGAIN_32_5db 0x1a /* gain = +32.5db */
+#define ADC_ALC_MAXGAIN_34db 0x1b /* gain = +34db */
+#define ADC_ALC_MAXGAIN_35_5db 0x1c /* gain = +35.5db */
+
+#define ADC_ALC_MINGAIN_m12db 0x00 /* gain = -12db */
+#define ADC_ALC_MINGAIN_m10_5db 0x01 /* gain = -10.5db */
+#define ADC_ALC_MINGAIN_m9db 0x02 /* gain = -9db */
+#define ADC_ALC_MINGAIN_m7_5db 0x03 /* gain = -7.5db */
+#define ADC_ALC_MINGAIN_m6db 0x04 /* gain = -6db */
+#define ADC_ALC_MINGAIN_m4_51db 0x05 /* gain = -4.51db */
+#define ADC_ALC_MINGAIN_m3db 0x06 /* gain = -3db */
+#define ADC_ALC_MINGAIN_m1_5db 0x07 /* gain = -1.5db */
+#define ADC_ALC_MINGAIN_0db 0x08 /* gain = 0db */
+#define ADC_ALC_MINGAIN_1_5db 0x09 /* gain = +1.5db */
+#define ADC_ALC_MINGAIN_3db 0x0a /* gain = +3db */
+#define ADC_ALC_MINGAIN_4_5db 0x0b /* gain = +4.5db */
+#define ADC_ALC_MINGAIN_6db 0x0c /* gain = +6db */
+#define ADC_ALC_MINGAIN_7_5db 0x0d /* gain = +7.5db */
+#define ADC_ALC_MINGAIN_9db 0x0e /* gain = +9db */
+#define ADC_ALC_MINGAIN_10_5db 0x0f /* gain = +10.5db */
+#define ADC_ALC_MINGAIN_12db 0x10 /* gain = +12db */
+#define ADC_ALC_MINGAIN_13_5db 0x11 /* gain = +13.5db */
+#define ADC_ALC_MINGAIN_15db 0x12 /* gain = +15db */
+#define ADC_ALC_MINGAIN_16_5db 0x13 /* gain = +16.5db */
+#define ADC_ALC_MINGAIN_18db 0x14 /* gain = +18db */
+#define ADC_ALC_MINGAIN_19_5db 0x15 /* gain = +19.5db */
+#define ADC_ALC_MINGAIN_21db 0x16 /* gain = +21db */
+#define ADC_ALC_MINGAIN_22_5db 0x17 /* gain = +22.5db */
+#define ADC_ALC_MINGAIN_24db 0x18 /* gain = +24db */
+#define ADC_ALC_MINGAIN_25_5db 0x19 /* gain = +25.5db */
+#define ADC_ALC_MINGAIN_27db 0x1a /* gain = +27db */
+#define ADC_ALC_MINGAIN_28_5db 0x1b /* gain = +28.5db */
+#define ADC_ALC_MINGAIN_30db 0x1c /* gain = +30db */
+
+/* ADC volume: step 1dB */
+
+/* ALC Hold, Decay, Attack */
+#define ADC_ALC_HLDTIME_0_US 0x00
+#define ADC_ALC_HLDTIME_0000266_US 0x01 //time = 2.67ms
+#define ADC_ALC_HLDTIME_0000533_US 0x02 //time = 5.33ms
+#define ADC_ALC_HLDTIME_0001066_US 0x03 //time = 10.66ms
+#define ADC_ALC_HLDTIME_0002132_US 0x04 //time = 21.32ms
+#define ADC_ALC_HLDTIME_0004264_US 0x05 //time = 42.64ms
+#define ADC_ALC_HLDTIME_0008538_US 0x06 //time = 85.38ms
+#define ADC_ALC_HLDTIME_0017076_US 0x07 //time = 170.76ms
+#define ADC_ALC_HLDTIME_0034152_US 0x08 //time = 341.52ms
+#define ADC_ALC_HLDTIME_0680000_US 0x09 //time = 0.68s
+#define ADC_ALC_HLDTIME_1360000_US 0x0a //time = 1.36s
+
+#define ADC_ALC_DCYTIME_000410_US 0x00 //time = 410us
+#define ADC_ALC_DCYTIME_000820_US 0x01 //time = 820us
+#define ADC_ALC_DCYTIME_001640_US 0x02 //time = 1.64ms
+#define ADC_ALC_DCYTIME_003280_US 0x03 //time = 3.28ms
+#define ADC_ALC_DCYTIME_006560_US 0x04 //time = 6.56ms
+#define ADC_ALC_DCYTIME_013120_US 0x05 //time = 13.12ms
+#define ADC_ALC_DCYTIME_026240_US 0x06 //time = 26.24ms
+#define ADC_ALC_DCYTIME_058480_US 0x07 //time = 52.48ms
+#define ADC_ALC_DCYTIME_104960_US 0x08 //time = 104.96ms
+#define ADC_ALC_DCYTIME_209920_US 0x09 //time = 209.92ms
+#define ADC_ALC_DCYTIME_420000_US 0x0a //time = 420ms
+
+#define ADC_ALC_ATKTIME_000104_US 0x00 //time = 104us
+#define ADC_ALC_ATKTIME_000208_US 0x01 //time = 208us
+#define ADC_ALC_ATKTIME_000416_US 0x02 //time = 416ms
+#define ADC_ALC_ATKTIME_003832_US 0x03 //time = 832ms
+#define ADC_ALC_ATKTIME_001664_US 0x04 //time = 1.664ms
+#define ADC_ALC_ATKTIME_003328_US 0x05 //time = 3.328ms
+#define ADC_ALC_ATKTIME_006656_US 0x06 //time = 6.656ms
+#define ADC_ALC_ATKTIME_013312_US 0x07 //time = 13.312ms
+#define ADC_ALC_ATKTIME_026624_US 0x08 //time = 26.624ms
+#define ADC_ALC_ATKTIME_053248_US 0x09 //time = 53.248ms
+#define ADC_ALC_ATKTIME_106496_US 0x0a //time = 106.496ms
+
+/* ALC Noise Gate */
+#define ADC_ALC_NGTYPE_DISABLE 0x00 //noise gate disable
+#define ADC_ALC_NGTYPE_ENABLE_HOLD 0x01 //noise gate enable, hold gain type
+#define ADC_ALC_NGTYPE_ENABLE_MUTE 0x03 //noise gate enable, mute type
+
+#define ADC_ALC_NGTHLD_m76_5db 0x00 /* Threshold = -76.5db */
+#define ADC_ALC_NGTHLD_m75db 0x01 /* Threshold = -75db */
+#define ADC_ALC_NGTHLD_m73_5db 0x02 /* Threshold = -73.5db */
+#define ADC_ALC_NGTHLD_m72db 0x03 /* Threshold = -72db */
+#define ADC_ALC_NGTHLD_m70_5db 0x04 /* Threshold = -70.5db */
+#define ADC_ALC_NGTHLD_m69db 0x05 /* Threshold = -69db */
+#define ADC_ALC_NGTHLD_m67_5db 0x06 /* Threshold = -67.5db */
+#define ADC_ALC_NGTHLD_m66db 0x07 /* Threshold = -66db */
+#define ADC_ALC_NGTHLD_m64_5db 0x08 /* Threshold = -64.5db */
+#define ADC_ALC_NGTHLD_m63db 0x09 /* Threshold = -63db */
+#define ADC_ALC_NGTHLD_m61_5db 0x0a /* Threshold = -61.5db */
+#define ADC_ALC_NGTHLD_m60db 0x0b /* Threshold = -60db */
+#define ADC_ALC_NGTHLD_m58_5db 0x0c /* Threshold = -58.5db */
+#define ADC_ALC_NGTHLD_m57db 0x0d /* Threshold = -57db */
+#define ADC_ALC_NGTHLD_m55_5db 0x0e /* Threshold = -55.5db */
+#define ADC_ALC_NGTHLD_m54db 0x0f /* Threshold = -54db */
+#define ADC_ALC_NGTHLD_m52_5db 0x10 /* Threshold = -52.5db */
+#define ADC_ALC_NGTHLD_m51db 0x11 /* Threshold = -51db */
+#define ADC_ALC_NGTHLD_m49_5db 0x12 /* Threshold = -49.5db */
+#define ADC_ALC_NGTHLD_m48db 0x13 /* Threshold = -48db */
+#define ADC_ALC_NGTHLD_m46_5db 0x14 /* Threshold = -46.5db */
+#define ADC_ALC_NGTHLD_m45db 0x15 /* Threshold = -45db */
+#define ADC_ALC_NGTHLD_m43_5db 0x16 /* Threshold = -43.5db */
+#define ADC_ALC_NGTHLD_m42db 0x17 /* Threshold = -42db */
+#define ADC_ALC_NGTHLD_m40_5db 0x18 /* Threshold = -40.5db */
+#define ADC_ALC_NGTHLD_m39db 0x19 /* Threshold = -39db */
+#define ADC_ALC_NGTHLD_m37_5db 0x1a /* Threshold = -37.5db */
+#define ADC_ALC_NGTHLD_m36db 0x1b /* Threshold = -36db */
+#define ADC_ALC_NGTHLD_m34_5db 0x1c /* Threshold = -34.5db */
+#define ADC_ALC_NGTHLD_m33db 0x1d /* Threshold = -33db */
+#define ADC_ALC_NGTHLD_m31_5db 0x1e /* Threshold = -31.5db */
+#define ADC_ALC_NGTHLD_m30db 0x1f /* Threshold = -30db */
+
+/* Headphone dummy - Windows Specific flag, not needed for Linux */
+
+/* HPMIX HIGAIN and VOLUME */
+#define DAC_HPMIX_HIGAIN_0db 0x00 /* gain = 0db */
+#define DAC_HPMIX_HIGAIN_m6db 0x88 /* gain = -6db */
+
+#define DAC_HPMIX_VOLUME_m12db 0x00 /* volume = -12db */
+#define DAC_HPMIX_VOLUME_m10_5db 0x11 /* volume = -10.5db */
+#define DAC_HPMIX_VOLUME_m9db 0x22 /* volume = -9db */
+#define DAC_HPMIX_VOLUME_m7_5db 0x33 /* volume = -7.5db */
+#define DAC_HPMIX_VOLUME_m6db 0x44 /* volume = -6db */
+#define DAC_HPMIX_VOLUME_m4_5db 0x88 /* volume = -4.5db */
+#define DAC_HPMIX_VOLUME_m3db 0x99 /* volume = -3db */
+#define DAC_HPMIX_VOLUME_m1_5db 0xaa /* volume = -1.5db */
+#define DAC_HPMIX_VOLUME_0db 0xbb /* volume = 0db */
+
+/* HPOUT VOLUME */
+#define DAC_HPOUT_VOLUME_0db 0x00 /* volume = 0db */
+#define DAC_HPOUT_VOLUME_m12db 0x11 /* volume = -12db */
+#define DAC_HPOUT_VOLUME_m24db 0x22 /* volume = -24db */
+#define DAC_HPOUT_VOLUME_m48db 0x33 /* volume = -48db */
+
+/* LDAC/RDAC volume = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+/* Automute */
+#define DAC_AUTOMUTE_NONE 0x00 /* no automute */
+#define DAC_AUTOMUTE_DIGITAL 0x01 /* digital mute */
+#define DAC_AUTOMUTE_ANALOG 0x02 /* analog mute */
+
+/* Mono - Windows specific, on Linux the information comes from DAI/topology */
+#define HEADPHONE_MONO 0x01 /* on channel */
+#define HEADPHONE_STEREO 0x00 /* stereo */
+
+/* Speaker and headphone GPIO control */
+#define GPIO_CTL_IO_LEVEL_LOW 0x00 /* low level enable */
+#define GPIO_CTL_IO_LEVEL_HIGH 0x01 /* high level enable */
+
+/* GPIO */
+/* FIXME: for ES8396, no need to use */
+
+/* Platform clocks */
+/*
+ * BCLK AND MCLK FREQ
+ * BIT[7:4] MCLK FREQ
+ * 0 - 19.2MHz
+ * 1 - 24MHz
+ * 2 - 12.288MHz
+ * F - Default for 19.2MHz
+ *
+ * BIT[3:0] BCLK FREQ
+ * 0 - 4.8MHz
+ * 1 - 2.4MHz
+ * 2 - 2.304MHz
+ * 3 - 3.072MHz
+ * 4 - 4.096MHz
+ * F - Default for 4.8MHz
+ */
+
+int es83xx_dsm(struct device *dev, int arg, int *value);
+int es83xx_dsm_dump(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c
new file mode 100644
index 000000000000..6f57a3aeecc8
--- /dev/null
+++ b/sound/soc/codecs/framer-codec.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Framer ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/framer/framer.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define FRAMER_NB_CHANNEL 32
+#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT)
+
+struct framer_codec {
+ struct framer *framer;
+ struct device *dev;
+ struct snd_soc_jack jack;
+ struct notifier_block nb;
+ struct work_struct carrier_work;
+ int max_chan_playback;
+ int max_chan_capture;
+};
+
+static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ switch (width) {
+ case 0:
+ /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ framer->max_chan_playback = hweight32(tx_mask);
+ if (framer->max_chan_playback > FRAMER_NB_CHANNEL) {
+ dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n",
+ tx_mask, FRAMER_NB_CHANNEL);
+ return -EINVAL;
+ }
+
+ framer->max_chan_capture = hweight32(rx_mask);
+ if (framer->max_chan_capture > FRAMER_NB_CHANNEL) {
+ dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n",
+ rx_mask, FRAMER_NB_CHANNEL);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The constraints for format/channel is to match with the number of 8bit
+ * time-slots available.
+ */
+static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_pcm_format_t format = params_format(params);
+ struct snd_interval ch = {0};
+ int width;
+
+ width = snd_pcm_format_physical_width(format);
+ if (width == 8 || width == 16 || width == 32 || width == 64) {
+ ch.max = nb_ts * 8 / width;
+ } else {
+ dev_err(dai->dev, "format physical width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ ch.min = ch.max ? 1 : 0;
+
+ return snd_interval_refine(c, &ch);
+}
+
+static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture);
+}
+
+static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ unsigned int channels = params_channels(params);
+ unsigned int slot_width;
+ snd_pcm_format_t format;
+ struct snd_mask f_new;
+
+ if (!channels || channels > nb_ts) {
+ dev_err(dai->dev, "channels %u not supported\n", nb_ts);
+ return -EINVAL;
+ }
+
+ slot_width = (nb_ts / channels) * 8;
+
+ snd_mask_none(&f_new);
+ pcm_for_each_format(format) {
+ if (snd_mask_test_format(f_old, format)) {
+ if (snd_pcm_format_physical_width(format) <= slot_width)
+ snd_mask_set_format(&f_new, format);
+ }
+ }
+
+ return snd_mask_refine(f_old, &f_new);
+}
+
+static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture);
+}
+
+static u64 framer_formats(u8 nb_ts)
+{
+ unsigned int format_width;
+ unsigned int chan_width;
+ snd_pcm_format_t format;
+ u64 formats_mask;
+
+ if (!nb_ts)
+ return 0;
+
+ formats_mask = 0;
+ chan_width = nb_ts * 8;
+ pcm_for_each_format(format) {
+ /* Support physical width multiple of 8bit */
+ format_width = snd_pcm_format_physical_width(format);
+ if (format_width == 0 || format_width % 8)
+ continue;
+
+ /*
+ * And support physical width that can fit N times in the
+ * channel
+ */
+ if (format_width > chan_width || chan_width % format_width)
+ continue;
+
+ formats_mask |= pcm_format_to_bits(format);
+ }
+ return formats_mask;
+}
+
+static int framer_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+ snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
+ snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
+ unsigned int frame_bits;
+ u64 format;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ format = framer_formats(framer->max_chan_capture);
+ hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format;
+ hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels;
+ frame_bits = framer->max_chan_capture * 8;
+ } else {
+ format = framer_formats(framer->max_chan_playback);
+ hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format;
+ hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels;
+ frame_bits = framer->max_chan_playback * 8;
+ }
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT, format);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels_by_format, dai,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_format_by_channels, dai,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const u64 framer_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops framer_dai_ops = {
+ .startup = framer_dai_startup,
+ .set_tdm_slot = framer_dai_set_tdm_slot,
+ .auto_selectable_formats = framer_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats),
+};
+
+static struct snd_soc_dai_driver framer_dai_driver = {
+ .name = "framer",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = FRAMER_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = U64_MAX, /* Will be refined on DAI .startup() */
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = FRAMER_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = U64_MAX, /* Will be refined on DAI .startup() */
+ },
+ .ops = &framer_dai_ops,
+};
+
+static void framer_carrier_work(struct work_struct *work)
+{
+ struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work);
+ struct framer_status framer_status;
+ int jack_status;
+ int ret;
+
+ ret = framer_get_status(framer->framer, &framer_status);
+ if (ret) {
+ dev_err(framer->dev, "get framer status failed (%d)\n", ret);
+ return;
+ }
+
+ jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0;
+ snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK);
+}
+
+static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct framer_codec *framer = container_of(nb, struct framer_codec, nb);
+
+ switch (action) {
+ case FRAMER_EVENT_STATUS:
+ queue_work(system_power_efficient_wq, &framer->carrier_work);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int framer_component_probe(struct snd_soc_component *component)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+ struct framer_status status;
+ char *name;
+ int ret;
+
+ INIT_WORK(&framer->carrier_work, framer_carrier_work);
+
+ name = "carrier";
+ if (component->name_prefix) {
+ name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix);
+ if (!name)
+ return -ENOMEM;
+ }
+
+ ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack);
+ if (component->name_prefix)
+ kfree(name); /* A copy is done by snd_soc_card_jack_new */
+ if (ret) {
+ dev_err(component->dev, "Cannot create jack\n");
+ return ret;
+ }
+
+ ret = framer_init(framer->framer);
+ if (ret) {
+ dev_err(component->dev, "framer init failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = framer_power_on(framer->framer);
+ if (ret) {
+ dev_err(component->dev, "framer power-on failed (%d)\n", ret);
+ goto framer_exit;
+ }
+
+ /* Be sure that get_status is supported */
+ ret = framer_get_status(framer->framer, &status);
+ if (ret) {
+ dev_err(component->dev, "get framer status failed (%d)\n", ret);
+ goto framer_power_off;
+ }
+
+ framer->nb.notifier_call = framer_carrier_notifier;
+ ret = framer_notifier_register(framer->framer, &framer->nb);
+ if (ret) {
+ dev_err(component->dev, "Cannot register event notifier\n");
+ goto framer_power_off;
+ }
+
+ /* Queue work to set the initial value */
+ queue_work(system_power_efficient_wq, &framer->carrier_work);
+
+ return 0;
+
+framer_power_off:
+ framer_power_off(framer->framer);
+framer_exit:
+ framer_exit(framer->framer);
+ return ret;
+}
+
+static void framer_component_remove(struct snd_soc_component *component)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+
+ framer_notifier_unregister(framer->framer, &framer->nb);
+ cancel_work_sync(&framer->carrier_work);
+ framer_power_off(framer->framer);
+ framer_exit(framer->framer);
+}
+
+static const struct snd_soc_component_driver framer_component_driver = {
+ .probe = framer_component_probe,
+ .remove = framer_component_remove,
+ .endianness = 1,
+};
+
+static int framer_codec_probe(struct platform_device *pdev)
+{
+ struct framer_codec *framer;
+
+ framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL);
+ if (!framer)
+ return -ENOMEM;
+
+ framer->dev = &pdev->dev;
+
+ /* Get framer from parents node */
+ framer->framer = devm_framer_get(&pdev->dev, NULL);
+ if (IS_ERR(framer->framer))
+ return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n");
+
+ platform_set_drvdata(pdev, framer);
+
+ return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver,
+ &framer_dai_driver, 1);
+}
+
+static struct platform_driver framer_codec_driver = {
+ .driver = {
+ .name = "framer-codec",
+ },
+ .probe = framer_codec_probe,
+};
+module_platform_driver(framer_codec_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("FRAMER ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c
new file mode 100644
index 000000000000..c8f56617e370
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// fs-amp-lib.c --- Common library for FourSemi Audio Amplifiers
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/crc16.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fs-amp-lib.h"
+
+static int fs_get_scene_count(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ int count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ if (!table)
+ return -EFAULT;
+
+ count = table->size / sizeof(struct fs_scene_index);
+ if (count < 1 || count > FS_SCENE_COUNT_MAX) {
+ dev_err(amp_lib->dev, "Invalid scene count: %d\n", count);
+ return -ERANGE;
+ }
+
+ return count;
+}
+
+static void fs_get_fwm_string(struct fs_amp_lib *amp_lib,
+ int offset, const char **pstr)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !pstr)
+ return;
+
+ table = amp_lib->table[FS_INDEX_STRING];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ *pstr = (char *)table + offset;
+ else
+ *pstr = NULL;
+}
+
+static void fs_get_scene_reg(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_REG];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->reg = (struct fs_reg_table *)((char *)table + offset);
+ else
+ scene->reg = NULL;
+}
+
+static void fs_get_scene_model(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_MODEL];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->model = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->model = NULL;
+}
+
+static void fs_get_scene_effect(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_EFFECT];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->effect = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->effect = NULL;
+}
+
+static int fs_parse_scene_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_scene_index *scene_index;
+ const struct fs_fwm_table *table;
+ struct fs_amp_scene *scene;
+ int idx, count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ count = fs_get_scene_count(amp_lib);
+ if (count <= 0)
+ return -EFAULT;
+
+ scene = devm_kcalloc(amp_lib->dev, count, sizeof(*scene), GFP_KERNEL);
+ if (!scene)
+ return -ENOMEM;
+
+ amp_lib->scene_count = count;
+ amp_lib->scene = scene;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ scene_index = (struct fs_scene_index *)table->buf;
+
+ for (idx = 0; idx < count; idx++) {
+ fs_get_fwm_string(amp_lib, scene_index->name, &scene->name);
+ if (!scene->name)
+ scene->name = devm_kasprintf(amp_lib->dev,
+ GFP_KERNEL, "S%d", idx);
+ dev_dbg(amp_lib->dev, "scene.%d name: %s\n", idx, scene->name);
+ fs_get_scene_reg(amp_lib, scene_index->reg, scene);
+ fs_get_scene_model(amp_lib, scene_index->model, scene);
+ fs_get_scene_effect(amp_lib, scene_index->effect, scene);
+ scene++;
+ scene_index++;
+ }
+
+ return 0;
+}
+
+static int fs_parse_all_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ const struct fs_fwm_index *index;
+ const char *ptr;
+ int idx, count;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ /* Parse all fwm tables */
+ table = (struct fs_fwm_table *)amp_lib->hdr->params;
+ index = (struct fs_fwm_index *)table->buf;
+ count = table->size / sizeof(*index);
+
+ for (idx = 0; idx < count; idx++, index++) {
+ if (index->type >= FS_INDEX_MAX)
+ return -ERANGE;
+ ptr = (char *)table + (int)index->offset;
+ amp_lib->table[index->type] = (struct fs_fwm_table *)ptr;
+ }
+
+ /* Parse all scene tables */
+ ret = fs_parse_scene_tables(amp_lib);
+ if (ret)
+ dev_err(amp_lib->dev, "Failed to parse scene: %d\n", ret);
+
+ return ret;
+}
+
+static int fs_verify_firmware(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ int crcsum;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ hdr = amp_lib->hdr;
+
+ /* Verify the crcsum code */
+ crcsum = crc16(0x0000, (const char *)&hdr->crc_size, hdr->crc_size);
+ if (crcsum != hdr->crc16) {
+ dev_err(amp_lib->dev, "Failed to checksum: %x-%x\n",
+ crcsum, hdr->crc16);
+ return -EFAULT;
+ }
+
+ /* Verify the devid(chip_type) */
+ if (amp_lib->devid != LO_U16(hdr->chip_type)) {
+ dev_err(amp_lib->dev, "DEVID dismatch: %04X#%04X\n",
+ amp_lib->devid, hdr->chip_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fs_print_firmware_info(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ const char *pro_name = NULL;
+ const char *dev_name = NULL;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return;
+
+ hdr = amp_lib->hdr;
+
+ fs_get_fwm_string(amp_lib, hdr->project, &pro_name);
+ fs_get_fwm_string(amp_lib, hdr->device, &dev_name);
+
+ dev_info(amp_lib->dev, "Project: %s Device: %s\n",
+ pro_name ? pro_name : "null",
+ dev_name ? dev_name : "null");
+
+ dev_info(amp_lib->dev, "Date: %04d%02d%02d-%02d%02d\n",
+ hdr->date.year, hdr->date.month, hdr->date.day,
+ hdr->date.hour, hdr->date.minute);
+}
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name)
+{
+ const struct firmware *cont;
+ struct fs_fwm_header *hdr;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !name)
+ return -EINVAL;
+
+ ret = request_firmware(&cont, name, amp_lib->dev);
+ if (ret) {
+ dev_err(amp_lib->dev, "Failed to request %s: %d\n", name, ret);
+ return ret;
+ }
+
+ dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size);
+
+ hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL);
+ release_firmware(cont);
+ if (!hdr)
+ return -ENOMEM;
+
+ amp_lib->hdr = hdr;
+ ret = fs_verify_firmware(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ ret = fs_parse_all_tables(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ fs_print_firmware_info(amp_lib);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fs_amp_load_firmware);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FourSemi audio amplifier library");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.h b/sound/soc/codecs/fs-amp-lib.h
new file mode 100644
index 000000000000..4a77c7b383cd
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * fs-amp-lib.h --- Common library for FourSemi Audio Amplifiers
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS_AMP_LIB_H__
+#define __FS_AMP_LIB_H__
+
+#define HI_U16(a) (((a) >> 8) & 0xFF)
+#define LO_U16(a) ((a) & 0xFF)
+#define FS_TABLE_NAME_LEN (4)
+#define FS_SCENE_COUNT_MAX (16)
+#define FS_CMD_DELAY_MS_MAX (100) /* 100ms */
+
+#define FS_CMD_DELAY (0xFF)
+#define FS_CMD_BURST (0xFE)
+#define FS_CMD_UPDATE (0xFD)
+
+#define FS_SOC_ENUM_EXT(xname, xhandler_info, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = xhandler_info, \
+ .get = xhandler_get, .put = xhandler_put \
+}
+
+enum fs_index_type {
+ FS_INDEX_INFO = 0,
+ FS_INDEX_STCOEF,
+ FS_INDEX_SCENE,
+ FS_INDEX_MODEL,
+ FS_INDEX_REG,
+ FS_INDEX_EFFECT,
+ FS_INDEX_STRING,
+ FS_INDEX_WOOFER,
+ FS_INDEX_MAX,
+};
+
+#pragma pack(push, 1)
+
+struct fs_reg_val {
+ u8 reg;
+ u16 val;
+};
+
+struct fs_reg_bits {
+ u8 cmd; /* FS_CMD_UPDATE */
+ u8 reg;
+ u16 val;
+ u16 mask;
+};
+
+struct fs_cmd_pkg {
+ union {
+ u8 cmd;
+ struct fs_reg_val regv;
+ struct fs_reg_bits regb;
+ };
+};
+
+struct fs_fwm_index {
+ /* Index type */
+ u16 type;
+ /* Offset address starting from the end of header */
+ u16 offset;
+};
+
+struct fs_fwm_table {
+ char name[FS_TABLE_NAME_LEN];
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_scene_index {
+ /* Offset address(scene name) in string table */
+ u16 name;
+ /* Offset address(scene reg) in register table */
+ u16 reg;
+ /* Offset address(scene model) in model table */
+ u16 model;
+ /* Offset address(scene effect) in effect table */
+ u16 effect;
+};
+
+struct fs_reg_table {
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_file_table {
+ u16 name;
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_fwm_date {
+ u32 year:12;
+ u32 month:4;
+ u32 day:5;
+ u32 hour:5;
+ u32 minute:6;
+};
+
+struct fs_fwm_header {
+ u16 version;
+ u16 project; /* Offset address(project name) in string table */
+ u16 device; /* Offset address(device name) in string table */
+ struct fs_fwm_date date;
+ u16 crc16;
+ u16 crc_size; /* Starting position for CRC checking */
+ u16 chip_type;
+ u16 addr; /* 7-bit i2c address */
+ u16 spkid;
+ u16 rsvd[6];
+ u8 params[];
+};
+
+#pragma pack(pop)
+
+struct fs_i2s_srate {
+ u32 srate; /* Sample rate */
+ u16 i2ssr; /* Value of Bit field[I2SSR] */
+};
+
+struct fs_pll_div {
+ unsigned int bclk; /* Rate of bit clock */
+ u16 pll1;
+ u16 pll2;
+ u16 pll3;
+};
+
+struct fs_amp_scene {
+ const char *name;
+ const struct fs_reg_table *reg;
+ const struct fs_file_table *model;
+ const struct fs_file_table *effect;
+};
+
+struct fs_amp_lib {
+ const struct fs_fwm_header *hdr;
+ const struct fs_fwm_table *table[FS_INDEX_MAX];
+ struct fs_amp_scene *scene;
+ struct device *dev;
+ int scene_count;
+ u16 devid;
+};
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name);
+
+#endif // __FS_AMP_LIB_H__
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
new file mode 100644
index 000000000000..e6195b71adad
--- /dev/null
+++ b/sound/soc/codecs/fs210x.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// fs210x.c -- Driver for the FS2104/5S Audio Amplifier
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "fs210x.h"
+#include "fs-amp-lib.h"
+
+#define FS210X_DEFAULT_FWM_NAME "fs210x_fwm.bin"
+#define FS210X_DEFAULT_DAI_NAME "fs210x-aif"
+#define FS2105S_DEVICE_ID 0x20 /* FS2105S */
+#define FS210X_DEVICE_ID 0x45 /* FS2104 */
+#define FS210X_REG_MAX 0xF8
+#define FS210X_INIT_SCENE 0
+#define FS210X_DEFAULT_SCENE 1
+#define FS210X_START_DELAY_MS 5
+#define FS210X_FAULT_CHECK_INTERVAL_MS 2000
+#define FS2105S_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+#define FS210X_RATES (SNDRV_PCM_RATE_16000 | FS2105S_RATES)
+#define FS210X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define FS210X_NUM_SUPPLIES ARRAY_SIZE(fs210x_supply_names)
+
+static const char *const fs210x_supply_names[] = {
+ "pvdd",
+ "dvdd",
+};
+
+struct fs210x_platform_data {
+ const char *fwm_name;
+};
+
+struct fs210x_priv {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ struct fs210x_platform_data pdata;
+ struct regulator_bulk_data supplies[FS210X_NUM_SUPPLIES];
+ struct gpio_desc *gpio_sdz;
+ struct delayed_work start_work;
+ struct delayed_work fault_check_work;
+ struct fs_amp_lib amp_lib;
+ const struct fs_amp_scene *cur_scene;
+ struct clk *clk_bclk;
+ /*
+ * @lock: Mutex ensuring exclusive access for critical device operations
+ *
+ * This lock serializes access between the following actions:
+ * - Device initialization procedures(probe)
+ * - Enable/disable device(DAPM event)
+ * - Suspend/resume device(PM)
+ * - Runtime scene switching(control)
+ * - Scheduling/execution of delayed works items(delayed works)
+ */
+ struct mutex lock;
+ unsigned int check_interval_ms;
+ unsigned int bclk;
+ unsigned int srate;
+ int scene_id;
+ u16 devid;
+ bool is_inited;
+ bool is_suspended;
+ bool is_bclk_on;
+ bool is_playing;
+};
+
+static const unsigned int fs2105s_rates[] = {
+ 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs2105s_constraints = {
+ .count = ARRAY_SIZE(fs2105s_rates),
+ .list = fs2105s_rates,
+};
+
+static const unsigned int fs210x_rates[] = {
+ 16000, 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs210x_constraints = {
+ .count = ARRAY_SIZE(fs210x_rates),
+ .list = fs210x_rates,
+};
+
+static const struct fs_pll_div fs210x_pll_div[] = {
+ /* bclk, pll1, pll2, pll3 */
+ { 512000, 0x006C, 0x0120, 0x0001 },
+ { 768000, 0x016C, 0x00C0, 0x0001 },
+ { 1024000, 0x016C, 0x0090, 0x0001 },
+ { 1536000, 0x016C, 0x0060, 0x0001 },
+ { 2048000, 0x016C, 0x0090, 0x0002 },
+ { 2304000, 0x016C, 0x0080, 0x0002 },
+ { 3072000, 0x016C, 0x0090, 0x0003 },
+ { 4096000, 0x016C, 0x0090, 0x0004 },
+ { 4608000, 0x016C, 0x0080, 0x0004 },
+ { 6144000, 0x016C, 0x0090, 0x0006 },
+ { 8192000, 0x016C, 0x0090, 0x0008 },
+ { 9216000, 0x016C, 0x0090, 0x0009 },
+ { 12288000, 0x016C, 0x0090, 0x000C },
+ { 16384000, 0x016C, 0x0090, 0x0010 },
+ { 18432000, 0x016C, 0x0090, 0x0012 },
+ { 24576000, 0x016C, 0x0090, 0x0018 },
+ { 1411200, 0x016C, 0x0060, 0x0001 },
+ { 2116800, 0x016C, 0x0080, 0x0002 },
+ { 2822400, 0x016C, 0x0090, 0x0003 },
+ { 4233600, 0x016C, 0x0080, 0x0004 },
+ { 5644800, 0x016C, 0x0090, 0x0006 },
+ { 8467200, 0x016C, 0x0090, 0x0009 },
+ { 11289600, 0x016C, 0x0090, 0x000C },
+ { 16934400, 0x016C, 0x0090, 0x0012 },
+ { 22579200, 0x016C, 0x0090, 0x0018 },
+ { 2000000, 0x017C, 0x0093, 0x0002 },
+};
+
+static int fs210x_bclk_set(struct fs210x_priv *fs210x, bool on)
+{
+ int ret = 0;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if ((fs210x->is_bclk_on ^ on) == 0)
+ return 0;
+
+ if (on) {
+ clk_set_rate(fs210x->clk_bclk, fs210x->bclk);
+ ret = clk_prepare_enable(fs210x->clk_bclk);
+ fs210x->is_bclk_on = true;
+ fsleep(2000); /* >= 2ms */
+ } else {
+ clk_disable_unprepare(fs210x->clk_bclk);
+ fs210x->is_bclk_on = false;
+ }
+
+ return ret;
+}
+
+static int fs210x_reg_write(struct fs210x_priv *fs210x,
+ u8 reg, u16 val)
+{
+ int ret;
+
+ ret = regmap_write(fs210x->regmap, reg, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_read(struct fs210x_priv *fs210x,
+ u8 reg, u16 *pval)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(fs210x->regmap, reg, &val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to read %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ *pval = (u16)val;
+
+ return 0;
+}
+
+static int fs210x_reg_update_bits(struct fs210x_priv *fs210x,
+ u8 reg, u16 mask, u16 val)
+{
+ int ret;
+
+ ret = regmap_update_bits(fs210x->regmap, reg, mask, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to update %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_bulk_write(struct fs210x_priv *fs210x,
+ u8 reg, const void *val, u32 size)
+{
+ int ret;
+
+ ret = regmap_bulk_write(fs210x->regmap, reg, val, size / 2);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to bulk write %02Xh: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int fs210x_write_reg_val(struct fs210x_priv *fs210x,
+ const struct fs_reg_val *regv)
+{
+ return fs210x_reg_write(fs210x, regv->reg, regv->val);
+}
+
+static inline int fs210x_write_reg_bits(struct fs210x_priv *fs210x,
+ const struct fs_reg_bits *regu)
+{
+ return fs210x_reg_update_bits(fs210x,
+ regu->reg,
+ regu->mask,
+ regu->val);
+}
+
+static inline int fs210x_set_cmd_pkg(struct fs210x_priv *fs210x,
+ const struct fs_cmd_pkg *pkg,
+ unsigned int *offset)
+{
+ int delay_us;
+
+ if (pkg->cmd >= 0x00 && pkg->cmd <= FS210X_REG_MAX) {
+ *offset = sizeof(pkg->regv);
+ return fs210x_write_reg_val(fs210x, &pkg->regv);
+ } else if (pkg->cmd == FS_CMD_UPDATE) {
+ *offset = sizeof(pkg->regb);
+ return fs210x_write_reg_bits(fs210x, &pkg->regb);
+ } else if (pkg->cmd == FS_CMD_DELAY) {
+ if (pkg->regv.val > FS_CMD_DELAY_MS_MAX)
+ return -EOPNOTSUPP;
+ delay_us = pkg->regv.val * 1000; /* ms -> us */
+ fsleep(delay_us);
+ *offset = sizeof(pkg->regv);
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "Invalid pkg cmd: %d\n", pkg->cmd);
+
+ return -EOPNOTSUPP;
+}
+
+static int fs210x_reg_write_table(struct fs210x_priv *fs210x,
+ const struct fs_reg_table *reg)
+{
+ const struct fs_cmd_pkg *pkg;
+ unsigned int index, offset;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (!reg || reg->size == 0)
+ return -EFAULT;
+
+ for (index = 0; index < reg->size; index += offset) {
+ pkg = (struct fs_cmd_pkg *)(reg->buf + index);
+ ret = fs210x_set_cmd_pkg(fs210x, pkg, &offset);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set cmd pkg: %02X-%d\n",
+ pkg->cmd, ret);
+ return ret;
+ }
+ }
+
+ if (index != reg->size) {
+ dev_err(fs210x->dev, "Invalid reg table size: %d-%d\n",
+ index, reg->size);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int fs210x_dev_play(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PLAY);
+ if (!ret)
+ fs210x->is_playing = true;
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_dev_stop(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (!fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fs210x->is_playing = false;
+
+ fsleep(30000); /* >= 30ms */
+
+ return ret;
+}
+
+static int fs210x_set_reg_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_reg_table *reg;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->reg || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing reg table\n");
+ return 0;
+ }
+
+ reg = scene->reg;
+ dev_dbg(fs210x->dev, "reg table size: %d\n", reg->size);
+
+ return fs210x_reg_write_table(fs210x, reg);
+}
+
+static int fs210x_set_woofer_table(struct fs210x_priv *fs210x)
+{
+ const struct fs_file_table *woofer;
+ const struct fs_fwm_table *table;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ /* NOTE: fs2105s has woofer ram only */
+ if (fs210x->devid != FS2105S_DEVICE_ID)
+ return 0;
+
+ table = fs210x->amp_lib.table[FS_INDEX_WOOFER];
+ if (!table) {
+ dev_dbg(fs210x->dev, "Skip writing woofer table\n");
+ return 0;
+ }
+
+ woofer = (struct fs_file_table *)table->buf;
+ dev_dbg(fs210x->dev, "woofer table size: %d\n", woofer->size);
+ /* Unit of woofer data is u32(4 bytes) */
+ if (woofer->size == 0 || (woofer->size & 0x3)) {
+ dev_err(fs210x->dev, "Invalid woofer size: %d\n",
+ woofer->size);
+ return -EINVAL;
+ }
+
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS2105S_46H_CAM_BURST_W);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ woofer->buf, woofer->size);
+
+ return ret;
+}
+
+static int fs210x_set_effect_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_file_table *effect;
+ int half_size;
+ int ret;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->effect || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing effect table\n");
+ return 0;
+ }
+
+ effect = scene->effect;
+ dev_dbg(fs210x->dev, "effect table size: %d\n", effect->size);
+
+ /* Unit of effect data is u32(4 bytes), 2 channels */
+ if (effect->size == 0 || (effect->size & 0x7)) {
+ dev_err(fs210x->dev, "Invalid effect size: %d\n",
+ effect->size);
+ return -EINVAL;
+ }
+
+ half_size = effect->size / 2;
+
+ /* Left channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_L);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf, half_size);
+ if (ret)
+ return ret;
+
+ /* Right channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_R);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf + half_size, half_size);
+
+ return ret;
+}
+
+static int fs210x_access_dsp_ram(struct fs210x_priv *fs210x, bool enable)
+{
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (enable) {
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_HIZ);
+ ret |= fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_ON);
+ } else {
+ ret = fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_OFF);
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_write_dsp_effect(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene,
+ int scene_id)
+{
+ int ret;
+
+ if (!fs210x || !scene)
+ return -EINVAL;
+
+ ret = fs210x_access_dsp_ram(fs210x, true);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to access dsp: %d\n", ret);
+ goto tag_exit;
+ }
+
+ ret = fs210x_set_effect_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set effect: %d\n", ret);
+ goto tag_exit;
+ }
+
+ if (scene_id == FS210X_INIT_SCENE)
+ ret = fs210x_set_woofer_table(fs210x);
+
+tag_exit:
+ fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_CLEAR);
+ fs210x_access_dsp_ram(fs210x, false);
+
+ return ret;
+}
+
+static int fs210x_check_scene(struct fs210x_priv *fs210x,
+ int scene_id, bool *skip_set)
+{
+ struct fs_amp_lib *amp_lib;
+
+ if (!fs210x || !skip_set)
+ return -EINVAL;
+
+ amp_lib = &fs210x->amp_lib;
+ if (amp_lib->scene_count == 0 || !amp_lib->scene) {
+ dev_err(fs210x->dev, "There's no scene data\n");
+ return -EINVAL;
+ }
+
+ if (scene_id < 0 || scene_id >= amp_lib->scene_count) {
+ dev_err(fs210x->dev, "Invalid scene_id: %d\n", scene_id);
+ return -EINVAL;
+ }
+
+ if (fs210x->scene_id == scene_id) {
+ dev_dbg(fs210x->dev, "Skip to set same scene\n");
+ return 0;
+ }
+
+ *skip_set = false;
+
+ return 0;
+}
+
+static int fs210x_set_scene(struct fs210x_priv *fs210x, int scene_id)
+{
+ const struct fs_amp_scene *scene;
+ bool skip_set = true;
+ bool is_playing;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = fs210x_check_scene(fs210x, scene_id, &skip_set);
+ if (ret || skip_set)
+ return ret;
+
+ scene = fs210x->amp_lib.scene + scene_id;
+ dev_info(fs210x->dev, "Switch scene.%d: %s\n",
+ scene_id, scene->name);
+
+ is_playing = fs210x->is_playing;
+ if (is_playing)
+ fs210x_dev_stop(fs210x);
+
+ ret = fs210x_set_reg_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = fs210x_write_dsp_effect(fs210x, scene, scene_id);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write ram: %d\n", ret);
+ return ret;
+ }
+
+ fs210x->cur_scene = scene;
+ fs210x->scene_id = scene_id;
+
+ if (is_playing)
+ fs210x_dev_play(fs210x);
+
+ return 0;
+}
+
+static int fs210x_init_chip(struct fs210x_priv *fs210x)
+{
+ int scene_id;
+ int ret;
+
+ regcache_cache_bypass(fs210x->regmap, true);
+
+ if (!fs210x->gpio_sdz) {
+ /* Gpio is not found, i2c reset */
+ ret = fs210x_reg_write(fs210x, FS210X_10H_PWRCTRL,
+ FS210X_10H_I2C_RESET);
+ if (ret)
+ goto tag_power_down;
+ } else {
+ /* gpio reset, deactivate */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ /* Backup scene id */
+ scene_id = fs210x->scene_id;
+ fs210x->scene_id = -1;
+
+ /* Init registers/RAM by init scene */
+ ret = fs210x_set_scene(fs210x, FS210X_INIT_SCENE);
+ if (ret)
+ goto tag_power_down;
+
+ /*
+ * If the firmware has effect scene(s),
+ * we load effect scene by default scene or scene_id
+ */
+ if (fs210x->amp_lib.scene_count > 1) {
+ if (scene_id < FS210X_DEFAULT_SCENE)
+ scene_id = FS210X_DEFAULT_SCENE;
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ goto tag_power_down;
+ }
+
+tag_power_down:
+ /* Power down the device */
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fsleep(10000); /* >= 10ms */
+
+ regcache_cache_bypass(fs210x->regmap, false);
+ if (!ret) {
+ regcache_mark_dirty(fs210x->regmap);
+ regcache_sync(fs210x->regmap);
+ fs210x->is_inited = true;
+ }
+
+ return ret;
+}
+
+static int fs210x_set_i2s_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_i2s_srate params[] = {
+ { 16000, 0x3 },
+ { 32000, 0x7 },
+ { 44100, 0x8 },
+ { 48000, 0x9 },
+ { 88200, 0xA },
+ { 96000, 0xB },
+ };
+ u16 val;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ if (params[i].srate != fs210x->srate)
+ continue;
+ val = params[i].i2ssr << FS210X_17H_I2SSR_SHIFT;
+ ret = fs210x_reg_update_bits(fs210x,
+ FS210X_17H_I2SCTRL,
+ FS210X_17H_I2SSR_MASK,
+ val);
+ return ret;
+ }
+
+ dev_err(fs210x->dev, "Invalid sample rate: %d\n", fs210x->srate);
+
+ return -EINVAL;
+}
+
+static int fs210x_get_pll_div(struct fs210x_priv *fs210x,
+ const struct fs_pll_div **pll_div)
+{
+ int i;
+
+ if (!fs210x || !pll_div)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(fs210x_pll_div); i++) {
+ if (fs210x_pll_div[i].bclk != fs210x->bclk)
+ continue;
+ *pll_div = fs210x_pll_div + i;
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "No PLL table for bclk: %d\n", fs210x->bclk);
+
+ return -EFAULT;
+}
+
+static int fs210x_set_hw_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_pll_div *pll_div;
+ int ret;
+
+ ret = fs210x_set_i2s_params(fs210x);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set i2s params: %d\n", ret);
+ return ret;
+ }
+
+ /* Set pll params */
+ ret = fs210x_get_pll_div(fs210x, &pll_div);
+ if (ret)
+ return ret;
+
+ ret = fs210x_reg_write(fs210x, FS210X_A1H_PLLCTRL1, pll_div->pll1);
+ ret |= fs210x_reg_write(fs210x, FS210X_A2H_PLLCTRL2, pll_div->pll2);
+ ret |= fs210x_reg_write(fs210x, FS210X_A3H_PLLCTRL3, pll_div->pll3);
+
+ return ret;
+}
+
+static int fs210x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ const struct snd_pcm_hw_constraint_list *list;
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+ if (!fs210x) {
+ pr_err("dai_startup: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ if (!substream->runtime)
+ return 0;
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ FS210X_FORMATS);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param format: %d\n", ret);
+ return ret;
+ }
+
+ if (fs210x->devid == FS2105S_DEVICE_ID)
+ list = &fs2105s_constraints;
+ else
+ list = &fs210x_constraints;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ list);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Only supports consumer mode */
+ break;
+ default:
+ dev_err(fs210x->dev, "Only supports consumer mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+ int chn_num;
+ int ret;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ fs210x->srate = params_rate(params);
+ fs210x->bclk = snd_soc_params_to_bclk(params);
+ chn_num = params_channels(params);
+ if (chn_num == 1) /* mono */
+ fs210x->bclk *= 2; /* I2S bus has 2 channels */
+
+ /* The FS2105S can't support 16kHz sample rate. */
+ if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_set_hw_params(fs210x);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret);
+
+ return ret;
+}
+
+static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct fs210x_priv *fs210x;
+ unsigned long delay;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ if (mute) {
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+ cancel_delayed_work_sync(&fs210x->start_work);
+ } else {
+ delay = msecs_to_jiffies(fs210x->check_interval_ms);
+ schedule_delayed_work(&fs210x->fault_check_work, delay);
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /*
+ * According to the power up/down sequence of FS210x,
+ * it requests the I2S clock has been present
+ * and stable(>= 2ms) before playing.
+ */
+ schedule_delayed_work(&fs210x->start_work,
+ msecs_to_jiffies(FS210X_START_DELAY_MS));
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void fs210x_start_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, start_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ ret = fs210x_dev_play(fs210x);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to start playing: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+}
+
+static void fs210x_fault_check_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ u16 status;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, fault_check_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return;
+ }
+
+ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ return;
+
+ if (!(status & FS210X_05H_PVDD_MASK))
+ dev_err(fs210x->dev, "PVDD fault\n");
+ if (status & FS210X_05H_OCDL_MASK)
+ dev_err(fs210x->dev, "OC detected\n");
+ if (status & FS210X_05H_UVDL_MASK)
+ dev_err(fs210x->dev, "UV detected\n");
+ if (status & FS210X_05H_OVDL_MASK)
+ dev_err(fs210x->dev, "OV detected\n");
+ if (status & FS210X_05H_OTPDL_MASK)
+ dev_err(fs210x->dev, "OT detected\n");
+ if (status & FS210X_05H_OCRDL_MASK)
+ dev_err(fs210x->dev, "OCR detected\n");
+ if (status & FS210X_05H_OCLDL_MASK)
+ dev_err(fs210x->dev, "OCL detected\n");
+ if (status & FS210X_05H_DCRDL_MASK)
+ dev_err(fs210x->dev, "DCR detected\n");
+ if (status & FS210X_05H_DCLDL_MASK)
+ dev_err(fs210x->dev, "DCL detected\n");
+ if (status & FS210X_05H_SRDL_MASK)
+ dev_err(fs210x->dev, "SR detected\n");
+ if (status & FS210X_05H_OTWDL_MASK)
+ dev_err(fs210x->dev, "OTW detected\n");
+ if (!(status & FS210X_05H_AMPS_MASK))
+ dev_dbg(fs210x->dev, "Amplifier unready\n");
+ if (!(status & FS210X_05H_PLLS_MASK))
+ dev_err(fs210x->dev, "PLL unlock\n");
+ if (!(status & FS210X_05H_ANAS_MASK))
+ dev_err(fs210x->dev, "Analog power fault\n");
+
+ schedule_delayed_work(&fs210x->fault_check_work,
+ msecs_to_jiffies(fs210x->check_interval_ms));
+}
+
+static int fs210x_get_drvdata_from_kctrl(struct snd_kcontrol *kctrl,
+ struct fs210x_priv **fs210x)
+{
+ struct snd_soc_component *cmpnt;
+
+ if (!kctrl) {
+ pr_err("fs210x: kcontrol is null\n");
+ return -EINVAL;
+ }
+
+ cmpnt = snd_kcontrol_chip(kctrl);
+ if (!cmpnt) {
+ pr_err("fs210x: component is null\n");
+ return -EINVAL;
+ }
+
+ *fs210x = snd_soc_component_get_drvdata(cmpnt);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const struct fs_amp_scene *scene;
+ struct fs210x_priv *fs210x;
+ const char *name = "N/A";
+ int idx, count;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_info: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (count < 1) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ idx = uinfo->value.enumerated.item;
+ scene = fs210x->amp_lib.scene + idx + 1;
+ if (scene->name)
+ name = scene->name;
+
+ strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int index;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_get: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ /* The id of effect scene is from 1 to N. */
+ if (fs210x->scene_id < 1)
+ return -EINVAL;
+
+ mutex_lock(&fs210x->lock);
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0
+ * effect scene(s): id = 1~N (optional)
+ * effect_index = scene_id - 1
+ */
+ index = fs210x->scene_id - 1;
+ ucontrol->value.integer.value[0] = index;
+ mutex_unlock(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int scene_id, scene_count;
+ bool is_changed = false;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_put: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0 (It's set in fs210x_init_chip() only)
+ * effect scene(s): id = 1~N (optional)
+ * scene_id = effect_index + 1.
+ */
+ scene_id = ucontrol->value.integer.value[0] + 1;
+ scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (scene_id < 1 || scene_id > scene_count) {
+ mutex_unlock(&fs210x->lock);
+ return -ERANGE;
+ }
+
+ if (scene_id != fs210x->scene_id)
+ is_changed = true;
+
+ if (fs210x->is_suspended) {
+ fs210x->scene_id = scene_id;
+ mutex_unlock(&fs210x->lock);
+ return is_changed;
+ }
+
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set scene: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+
+ if (!ret && is_changed)
+ return 1;
+
+ return ret;
+}
+
+static int fs210x_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt);
+ int ret = 0;
+
+ mutex_lock(&fs210x->lock);
+
+ if (fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * If there is no bclk for us to set the clock output,
+ * we will enable the device(start_work) in dai trigger.
+ */
+ if (!fs210x->clk_bclk)
+ break;
+ fs210x_bclk_set(fs210x, true);
+ ret = fs210x_dev_play(fs210x);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = fs210x_dev_stop(fs210x);
+ fs210x_bclk_set(fs210x, false);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops fs210x_dai_ops = {
+ .startup = fs210x_dai_startup,
+ .set_fmt = fs210x_dai_set_fmt,
+ .hw_params = fs210x_dai_hw_params,
+ .mute_stream = fs210x_dai_mute,
+ .trigger = fs210x_dai_trigger,
+};
+
+static const struct snd_soc_dai_driver fs210x_dai = {
+ .name = FS210X_DEFAULT_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .ops = &fs210x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(fs2105s_vol_tlv, -9709, 19, 1);
+static const DECLARE_TLV_DB_SCALE(fs210x_vol_tlv, -13357, 19, 1);
+
+static const struct snd_kcontrol_new fs2105s_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 7, 0x1FF, 0, fs2105s_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 6, 0x2BF, 0, fs210x_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_controls[] = {
+ SOC_DOUBLE("DAC Mute Switch", FS210X_30H_DACCTRL, 4, 8, 1, 0),
+ SOC_DOUBLE("DAC Fade Switch", FS210X_30H_DACCTRL, 5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new fs210x_scene_control[] = {
+ FS_SOC_ENUM_EXT("Effect Scene",
+ fs210x_effect_scene_info,
+ fs210x_effect_scene_get,
+ fs210x_effect_scene_put),
+};
+
+static const struct snd_soc_dapm_widget fs210x_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0,
+ fs210x_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_INPUT("SDO"),
+};
+
+static const struct snd_soc_dapm_route fs210x_dapm_routes[] = {
+ { "OUTL", NULL, "AIF IN" },
+ { "OUTR", NULL, "AIF IN" },
+ { "AIF OUT", NULL, "SDO" },
+};
+
+static int fs210x_add_mixer_controls(struct fs210x_priv *fs210x,
+ struct snd_soc_component *cmpnt)
+{
+ const struct snd_kcontrol_new *kctrl;
+ int count;
+ int ret;
+
+ if (!fs210x || !cmpnt)
+ return -EINVAL;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ kctrl = fs2105s_vol_control;
+ count = ARRAY_SIZE(fs2105s_vol_control);
+ } else {
+ kctrl = fs210x_vol_control;
+ count = ARRAY_SIZE(fs210x_vol_control);
+ }
+
+ ret = snd_soc_add_component_controls(cmpnt, kctrl, count);
+ if (ret)
+ return ret;
+
+ /*
+ * If the firmware has no scene or only init scene,
+ * we skip adding this mixer control.
+ */
+ if (fs210x->amp_lib.scene_count < 2)
+ return 0;
+
+ kctrl = fs210x_scene_control;
+ count = ARRAY_SIZE(fs210x_scene_control);
+
+ return snd_soc_add_component_controls(cmpnt, kctrl, count);
+}
+
+static int fs210x_probe(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ fs210x->amp_lib.dev = fs210x->dev;
+ fs210x->amp_lib.devid = fs210x->devid;
+
+ ret = fs_amp_load_firmware(&fs210x->amp_lib, fs210x->pdata.fwm_name);
+ if (ret)
+ return ret;
+
+ ret = fs210x_add_mixer_controls(fs210x, cmpnt);
+ if (ret)
+ return ret;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_init_chip(fs210x);
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static void fs210x_remove(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return;
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+}
+
+#ifdef CONFIG_PM
+static int fs210x_suspend(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ regcache_cache_only(fs210x->regmap, true);
+
+ mutex_lock(&fs210x->lock);
+ fs210x->cur_scene = NULL;
+ fs210x->is_inited = false;
+ fs210x->is_playing = false;
+ fs210x->is_suspended = true;
+
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(30000); /* >= 30ms */
+ mutex_unlock(&fs210x->lock);
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+
+ ret = regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to suspend: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_resume(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ fs210x->is_suspended = false;
+ ret = fs210x_init_chip(fs210x);
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+#else
+#define fs210x_suspend NULL
+#define fs210x_resume NULL
+#endif // CONFIG_PM
+
+static bool fs210x_volatile_registers(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FS210X_00H_STATUS ... FS210X_0FH_I2CADDR:
+ case FS210X_ABH_INTSTAT:
+ case FS210X_ACH_INTSTATR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_soc_component_driver fs210x_soc_component_dev = {
+ .probe = fs210x_probe,
+ .remove = fs210x_remove,
+ .suspend = fs210x_suspend,
+ .resume = fs210x_resume,
+ .controls = fs210x_controls,
+ .num_controls = ARRAY_SIZE(fs210x_controls),
+ .dapm_widgets = fs210x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(fs210x_dapm_widgets),
+ .dapm_routes = fs210x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(fs210x_dapm_routes),
+};
+
+static const struct regmap_config fs210x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = FS210X_REG_MAX,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = fs210x_volatile_registers,
+};
+
+static int fs210x_detect_device(struct fs210x_priv *fs210x)
+{
+ u16 devid;
+ int ret;
+
+ ret = fs210x_reg_read(fs210x, FS210X_03H_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ fs210x->devid = HI_U16(devid);
+
+ switch (fs210x->devid) {
+ case FS210X_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2104 detected\n");
+ break;
+ case FS2105S_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2105S detected\n");
+ break;
+ default:
+ dev_err(fs210x->dev, "DEVID: 0x%04X dismatch\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int fs210x_parse_dts(struct fs210x_priv *fs210x,
+ struct fs210x_platform_data *pdata)
+{
+ struct device_node *node = fs210x->dev->of_node;
+ int i, ret;
+
+ if (!node)
+ return 0;
+
+ ret = of_property_read_string(node, "firmware-name", &pdata->fwm_name);
+ if (ret)
+ pdata->fwm_name = FS210X_DEFAULT_FWM_NAME;
+
+ fs210x->gpio_sdz = devm_gpiod_get_optional(fs210x->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(fs210x->gpio_sdz))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->gpio_sdz),
+ "Failed to get reset-gpios\n");
+
+ for (i = 0; i < FS210X_NUM_SUPPLIES; i++)
+ fs210x->supplies[i].supply = fs210x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(fs210x->dev,
+ ARRAY_SIZE(fs210x->supplies),
+ fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to get supplies\n");
+
+ return 0;
+}
+
+static void fs210x_deinit(struct fs210x_priv *fs210x)
+{
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(10000); /* >= 10ms */
+
+ regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+}
+
+static int fs210x_init(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ ret = fs210x_parse_dts(fs210x, &fs210x->pdata);
+ if (ret)
+ return ret;
+
+ fs210x->clk_bclk = devm_clk_get_optional(fs210x->dev, "bclk");
+ if (IS_ERR(fs210x->clk_bclk))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->clk_bclk),
+ "Failed to get bclk\n");
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to enable supplies\n");
+
+ /* Make sure the SDZ pin is pulled down enough time. */
+ fsleep(10000); /* >= 10ms */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); /* Deactivate */
+ fsleep(10000); /* >= 10ms */
+
+ ret = fs210x_detect_device(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return ret;
+ }
+
+ fs210x->scene_id = -1; /* Invalid scene */
+ fs210x->cur_scene = NULL;
+ fs210x->is_playing = false;
+ fs210x->is_inited = false;
+ fs210x->is_suspended = false;
+ fs210x->check_interval_ms = FS210X_FAULT_CHECK_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&fs210x->fault_check_work, fs210x_fault_check_work);
+ INIT_DELAYED_WORK(&fs210x->start_work, fs210x_start_work);
+ mutex_init(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_register_snd_component(struct fs210x_priv *fs210x)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ static int instance_id;
+ int ret;
+
+ dai_drv = devm_kmemdup(fs210x->dev, &fs210x_dai,
+ sizeof(fs210x_dai), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ dai_drv->name = devm_kasprintf(fs210x->dev,
+ GFP_KERNEL, "%s-%d",
+ dai_drv->name, instance_id);
+ if (!dai_drv->name)
+ return -ENOMEM;
+
+ instance_id++;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ dai_drv->playback.rates = FS2105S_RATES;
+ dai_drv->capture.rates = FS2105S_RATES;
+ }
+
+ ret = snd_soc_register_component(fs210x->dev,
+ &fs210x_soc_component_dev,
+ dai_drv, 1);
+ return ret;
+}
+
+static ssize_t check_interval_ms_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", fs210x->check_interval_ms);
+}
+
+static ssize_t check_interval_ms_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtouint(buf, 10, &fs210x->check_interval_ms);
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_interval_ms);
+
+static struct attribute *fs210x_attrs[] = {
+ &dev_attr_check_interval_ms.attr,
+ NULL,
+};
+
+static struct attribute_group fs210x_attr_group = {
+ .attrs = fs210x_attrs,
+};
+
+static int fs210x_i2c_probe(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = devm_kzalloc(&client->dev, sizeof(*fs210x), GFP_KERNEL);
+ if (!fs210x)
+ return -ENOMEM;
+
+ fs210x->i2c = client;
+ fs210x->dev = &client->dev;
+ i2c_set_clientdata(client, fs210x);
+
+ fs210x->regmap = devm_regmap_init_i2c(client, &fs210x_regmap);
+ if (IS_ERR(fs210x->regmap))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->regmap),
+ "Failed to get regmap\n");
+
+ ret = fs210x_init(fs210x);
+ if (ret)
+ return ret;
+
+ ret = devm_device_add_group(fs210x->dev, &fs210x_attr_group);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to create sysfs group\n");
+ }
+
+ ret = fs210x_register_snd_component(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to register component\n");
+ }
+
+ return 0;
+}
+
+static void fs210x_i2c_remove(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x = i2c_get_clientdata(client);
+
+ snd_soc_unregister_component(fs210x->dev);
+ fs210x_deinit(fs210x);
+}
+
+static const struct i2c_device_id fs210x_i2c_id[] = {
+ { "fs2104" },
+ { "fs2105s" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id);
+
+static const struct of_device_id fs210x_of_match[] = {
+ { .compatible = "foursemi,fs2105s", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fs210x_of_match);
+
+static struct i2c_driver fs210x_i2c_driver = {
+ .driver = {
+ .name = "fs210x",
+ .of_match_table = fs210x_of_match,
+ },
+ .id_table = fs210x_i2c_id,
+ .probe = fs210x_i2c_probe,
+ .remove = fs210x_i2c_remove,
+};
+
+module_i2c_driver(fs210x_i2c_driver);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FS2104/5S Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs210x.h b/sound/soc/codecs/fs210x.h
new file mode 100644
index 000000000000..78e1760332ca
--- /dev/null
+++ b/sound/soc/codecs/fs210x.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * fs210x.h -- Driver for the FS2104/5S Audio Amplifier
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS210X_H__
+#define __FS210X_H__
+
+#define FS210X_00H_STATUS 0x00
+#define FS210X_03H_DEVID 0x03
+#define FS210X_05H_ANASTAT 0x05
+#define FS210X_06H_DIGSTAT 0x06
+#define FS210X_0BH_ACCKEY 0x0B
+#define FS210X_0FH_I2CADDR 0x0F
+#define FS210X_10H_PWRCTRL 0x10
+#define FS210X_11H_SYSCTRL 0x11
+#define FS210X_17H_I2SCTRL 0x17
+#define FS210X_30H_DACCTRL 0x30
+#define FS210X_39H_LVOLCTRL 0x39
+#define FS210X_3AH_RVOLCTRL 0x3A
+#define FS210X_42H_DACEQWL 0x42
+#define FS210X_46H_DACEQA 0x46
+#define FS210X_A1H_PLLCTRL1 0xA1
+#define FS210X_A2H_PLLCTRL2 0xA2
+#define FS210X_A3H_PLLCTRL3 0xA3
+#define FS210X_ABH_INTSTAT 0xAB
+#define FS210X_ACH_INTSTATR 0xAC
+
+#define FS210X_05H_PVDD_SHIFT 14
+#define FS210X_05H_PVDD_MASK BIT(14)
+#define FS210X_05H_OCDL_SHIFT 13
+#define FS210X_05H_OCDL_MASK BIT(13)
+#define FS210X_05H_UVDL_SHIFT 12
+#define FS210X_05H_UVDL_MASK BIT(12)
+#define FS210X_05H_OVDL_SHIFT 11
+#define FS210X_05H_OVDL_MASK BIT(11)
+#define FS210X_05H_OTPDL_SHIFT 10
+#define FS210X_05H_OTPDL_MASK BIT(10)
+#define FS210X_05H_OCRDL_SHIFT 9
+#define FS210X_05H_OCRDL_MASK BIT(9)
+#define FS210X_05H_OCLDL_SHIFT 8
+#define FS210X_05H_OCLDL_MASK BIT(8)
+#define FS210X_05H_DCRDL_SHIFT 7
+#define FS210X_05H_DCRDL_MASK BIT(7)
+#define FS210X_05H_DCLDL_SHIFT 6
+#define FS210X_05H_DCLDL_MASK BIT(6)
+#define FS210X_05H_SRDL_SHIFT 5
+#define FS210X_05H_SRDL_MASK BIT(5)
+#define FS210X_05H_OTWDL_SHIFT 4
+#define FS210X_05H_OTWDL_MASK BIT(4)
+#define FS210X_05H_AMPS_SHIFT 3
+#define FS210X_05H_AMPS_MASK BIT(3)
+#define FS210X_05H_PLLS_SHIFT 1
+#define FS210X_05H_PLLS_MASK BIT(1)
+#define FS210X_05H_ANAS_SHIFT 0
+#define FS210X_05H_ANAS_MASK BIT(0)
+#define FS210X_17H_I2SSR_SHIFT 12
+#define FS210X_17H_I2SSR_MASK GENMASK(15, 12)
+#define FS210X_30H_RMUTE_SHIFT 8
+#define FS210X_30H_LMUTE_SHIFT 4
+
+#define FS210X_0BH_ACCKEY_ON 0x0091
+#define FS210X_0BH_ACCKEY_OFF 0x0000
+#define FS210X_10H_I2C_RESET 0x0002
+#define FS210X_11H_DPS_HIZ 0x0100
+#define FS210X_11H_DPS_PWDN 0x0000
+#define FS210X_11H_DPS_PLAY 0x0300
+#define FS210X_46H_CAM_BURST_L 0x8000
+#define FS210X_46H_CAM_BURST_R 0x8200
+#define FS2105S_46H_CAM_BURST_W 0x8400
+#define FS210X_46H_CAM_CLEAR 0x0000
+
+#endif /* __FS210X_H__ */
diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c
index 0b80052996d3..1f165e46701f 100644
--- a/sound/soc/codecs/gtm601.c
+++ b/sound/soc/codecs/gtm601.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* This is a simple driver for the GTM601 Voice PCM interface
*
@@ -6,20 +7,15 @@
* Author: Marek Belisko <marek@goldelico.com>
*
* Based on wm8727.c 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.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
@@ -51,32 +47,51 @@ static struct snd_soc_dai_driver gtm601_dai = {
},
};
-static const struct snd_soc_codec_driver soc_codec_dev_gtm601 = {
- .dapm_widgets = gtm601_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets),
- .dapm_routes = gtm601_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes),
+static struct snd_soc_dai_driver bm818_dai = {
+ .name = "bm818",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_gtm601 = {
+ .dapm_widgets = gtm601_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets),
+ .dapm_routes = gtm601_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int gtm601_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_gtm601, &gtm601_dai, 1);
-}
+ const struct snd_soc_dai_driver *dai_driver;
-static int gtm601_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ dai_driver = of_device_get_match_data(&pdev->dev);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_gtm601,
+ (struct snd_soc_dai_driver *)dai_driver, 1);
}
-#if defined(CONFIG_OF)
-static const struct of_device_id gtm601_codec_of_match[] = {
- { .compatible = "option,gtm601", },
+static const struct of_device_id gtm601_codec_of_match[] __maybe_unused = {
+ { .compatible = "option,gtm601", .data = (void *)&gtm601_dai },
+ { .compatible = "broadmobi,bm818", .data = (void *)&bm818_dai },
{},
};
MODULE_DEVICE_TABLE(of, gtm601_codec_of_match);
-#endif
static struct platform_driver gtm601_codec_driver = {
.driver = {
@@ -84,7 +99,6 @@ static struct platform_driver gtm601_codec_driver = {
.of_match_table = of_match_ptr(gtm601_codec_of_match),
},
.probe = gtm601_platform_probe,
- .remove = gtm601_platform_remove,
};
module_platform_driver(gtm601_codec_driver);
diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c
new file mode 100644
index 000000000000..b9caae7e4817
--- /dev/null
+++ b/sound/soc/codecs/hda-dai.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <sound/soc.h>
+#include <sound/hda_codec.h>
+#include "hda.h"
+
+static int hda_codec_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hda_pcm_stream *stream_info;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+ pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
+
+ dev_dbg(dai->dev, "open stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
+ codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
+
+ snd_hda_codec_pcm_get(pcm);
+
+ ret = stream_info->ops.open(stream_info, codec, substream);
+ if (ret < 0) {
+ dev_err(dai->dev, "codec open failed: %d\n", ret);
+ snd_hda_codec_pcm_put(pcm);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hda_codec_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hda_pcm_stream *stream_info;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+ pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
+
+ dev_dbg(dai->dev, "close stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
+ codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
+
+ ret = stream_info->ops.close(stream_info, codec, substream);
+ if (ret < 0)
+ dev_err(dai->dev, "codec close failed: %d\n", ret);
+
+ snd_hda_codec_pcm_put(pcm);
+}
+
+static int hda_codec_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hda_pcm_stream *stream_info;
+ struct hda_codec *codec;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_hda_codec_cleanup(codec, stream_info, substream);
+
+ return 0;
+}
+
+static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hda_pcm_stream *stream_info;
+ struct hdac_stream *stream;
+ struct hda_codec *codec;
+ unsigned int format;
+ unsigned int bits;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream = substream->runtime->private_data;
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->maxbps);
+ format = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
+
+ ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream);
+ if (ret < 0) {
+ dev_err(dai->dev, "codec prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops = {
+ .startup = hda_codec_dai_startup,
+ .shutdown = hda_codec_dai_shutdown,
+ .hw_free = hda_codec_dai_hw_free,
+ .prepare = hda_codec_dai_prepare,
+};
+EXPORT_SYMBOL_GPL(snd_soc_hda_codec_dai_ops);
diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
new file mode 100644
index 000000000000..237b0b060457
--- /dev/null
+++ b/sound/soc/codecs/hda.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include "hda.h"
+
+static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count,
+ struct snd_soc_dai_driver **drivers)
+{
+ struct device *dev = &codec->core.dev;
+ struct snd_soc_dai_driver *drvs;
+ struct hda_pcm *pcm;
+ int i;
+
+ drvs = devm_kcalloc(dev, pcm_count, sizeof(*drvs), GFP_KERNEL);
+ if (!drvs)
+ return -ENOMEM;
+
+ pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+ for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+ struct snd_soc_pcm_stream *stream;
+ int dir;
+
+ dev_info(dev, "creating for %s %d\n", pcm->name, i);
+ drvs[i].id = i;
+ drvs[i].name = pcm->name;
+ drvs[i].ops = &snd_soc_hda_codec_dai_ops;
+
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ stream = &drvs[i].playback;
+ if (!pcm->stream[dir].substreams) {
+ dev_info(dev, "skipping playback dai for %s\n", pcm->name);
+ goto capture_dais;
+ }
+
+ stream->stream_name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
+ snd_pcm_direction_name(dir));
+ if (!stream->stream_name)
+ return -ENOMEM;
+ stream->channels_min = pcm->stream[dir].channels_min;
+ stream->channels_max = pcm->stream[dir].channels_max;
+ stream->rates = pcm->stream[dir].rates;
+ stream->formats = pcm->stream[dir].formats;
+ stream->subformats = pcm->stream[dir].subformats;
+ stream->sig_bits = pcm->stream[dir].maxbps;
+
+capture_dais:
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ stream = &drvs[i].capture;
+ if (!pcm->stream[dir].substreams) {
+ dev_info(dev, "skipping capture dai for %s\n", pcm->name);
+ continue;
+ }
+
+ stream->stream_name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
+ snd_pcm_direction_name(dir));
+ if (!stream->stream_name)
+ return -ENOMEM;
+ stream->channels_min = pcm->stream[dir].channels_min;
+ stream->channels_max = pcm->stream[dir].channels_max;
+ stream->rates = pcm->stream[dir].rates;
+ stream->formats = pcm->stream[dir].formats;
+ stream->subformats = pcm->stream[dir].subformats;
+ stream->sig_bits = pcm->stream[dir].maxbps;
+ }
+
+ *drivers = drvs;
+ return 0;
+}
+
+static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_component *component)
+{
+ struct snd_soc_dai_driver *drvs = NULL;
+ struct snd_soc_dapm_context *dapm;
+ struct hda_pcm *pcm;
+ int ret, pcm_count = 0;
+
+ if (list_empty(&codec->pcm_list_head))
+ return -EINVAL;
+ list_for_each_entry(pcm, &codec->pcm_list_head, list)
+ pcm_count++;
+
+ ret = hda_codec_create_dais(codec, pcm_count, &drvs);
+ if (ret < 0)
+ return ret;
+
+ dapm = snd_soc_component_to_dapm(component);
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ struct snd_soc_dai *dai;
+
+ dai = snd_soc_register_dai(component, drvs, false);
+ if (!dai) {
+ dev_err(component->dev, "register dai for %s failed\n", pcm->name);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+ if (ret < 0) {
+ dev_err(component->dev, "create widgets failed: %d\n", ret);
+ snd_soc_unregister_dai(dai);
+ return ret;
+ }
+
+ snd_soc_dai_init_dma_data(dai, &pcm->stream[0], &pcm->stream[1]);
+ drvs++;
+ }
+
+ return 0;
+}
+
+static void hda_codec_unregister_dais(struct hda_codec *codec,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai, *save;
+ struct hda_pcm *pcm;
+
+ for_each_component_dais_safe(component, dai, save) {
+ int stream;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ if (strcmp(dai->driver->name, pcm->name))
+ continue;
+
+ for_each_pcm_streams(stream)
+ snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream));
+
+ snd_soc_unregister_dai(dai);
+ break;
+ }
+ }
+}
+
+int hda_codec_probe_complete(struct hda_codec *codec)
+{
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ int ret;
+
+ ret = snd_hda_codec_build_controls(codec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to create controls %d\n", ret);
+ return ret;
+ }
+
+ /* Bus suspended codecs as it does not manage their pm */
+ pm_runtime_set_active(&hdev->dev);
+ /* rpm was forbidden in snd_hda_codec_device_new() */
+ snd_hda_codec_set_power_save(codec, 2000);
+ snd_hda_codec_register(codec);
+
+ /* Complement pm_runtime_get_sync(bus) in probe */
+ pm_runtime_put_autosuspend(bus->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hda_codec_probe_complete);
+
+/* Expects codec with usage_count=1 and status=suspended */
+static int hda_codec_probe(struct snd_soc_component *component)
+{
+ struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+#ifdef CONFIG_PM
+ WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
+ !pm_runtime_status_suspended(&hdev->dev));
+#endif
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr);
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ pm_runtime_get_sync(bus->dev);
+ if (hda_codec_is_display(codec))
+ snd_hdac_display_power(bus, hdev->addr, true);
+ snd_hdac_ext_bus_link_get(bus, hlink);
+
+ ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec,
+ false);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "codec create failed: %d\n", ret);
+ goto device_new_err;
+ }
+
+ ret = snd_hda_codec_set_name(codec, codec->preset->name);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "set name: %s failed: %d\n", codec->preset->name, ret);
+ goto err;
+ }
+
+ ret = snd_hdac_regmap_init(&codec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "regmap init failed: %d\n", ret);
+ goto err;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = driver->ops->probe(codec, codec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "codec init failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_hda_codec_parse_pcms(codec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "unable to map pcms to dai: %d\n", ret);
+ goto parse_pcms_err;
+ }
+
+ ret = hda_codec_register_dais(codec, component);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "update dais failed: %d\n", ret);
+ goto parse_pcms_err;
+ }
+
+ if (!hda_codec_is_display(codec)) {
+ ret = hda_codec_probe_complete(codec);
+ if (ret < 0)
+ goto complete_err;
+ }
+
+ codec->core.lazy_cache = true;
+
+ return 0;
+
+complete_err:
+ hda_codec_unregister_dais(codec, component);
+parse_pcms_err:
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
+err:
+ snd_hda_codec_cleanup_for_unbind(codec);
+device_new_err:
+ if (hda_codec_is_display(codec))
+ snd_hdac_display_power(bus, hdev->addr, false);
+
+ snd_hdac_ext_bus_link_put(bus, hlink);
+
+ pm_runtime_put_autosuspend(bus->dev);
+ return ret;
+}
+
+/* Leaves codec with usage_count=1 and status=suspended */
+static void hda_codec_remove(struct snd_soc_component *component)
+{
+ struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+ bool was_registered = codec->core.registered;
+
+ /* Don't allow any more runtime suspends */
+ pm_runtime_forbid(&hdev->dev);
+
+ hda_codec_unregister_dais(codec, component);
+
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
+
+ snd_hda_codec_cleanup_for_unbind(codec);
+ pm_runtime_put_noidle(&hdev->dev);
+ /* snd_hdac_device_exit() is only called on bus remove */
+ pm_runtime_set_suspended(&hdev->dev);
+
+ if (hda_codec_is_display(codec))
+ snd_hdac_display_power(bus, hdev->addr, false);
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr);
+ if (hlink)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+ /*
+ * HDMI card's hda_codec_probe_complete() (see late_probe()) may
+ * not be called due to early error, leaving bus uc unbalanced
+ */
+ if (!was_registered) {
+ pm_runtime_put_autosuspend(bus->dev);
+ }
+
+#ifdef CONFIG_PM
+ WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
+ !pm_runtime_status_suspended(&hdev->dev));
+#endif
+}
+
+static const struct snd_soc_dapm_route hda_dapm_routes[] = {
+ {"AIF1TX", NULL, "Codec Input Pin1"},
+ {"AIF2TX", NULL, "Codec Input Pin2"},
+ {"AIF3TX", NULL, "Codec Input Pin3"},
+
+ {"Codec Output Pin1", NULL, "AIF1RX"},
+ {"Codec Output Pin2", NULL, "AIF2RX"},
+ {"Codec Output Pin3", NULL, "AIF3RX"},
+};
+
+static const struct snd_soc_dapm_widget hda_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Input Pins */
+ SND_SOC_DAPM_INPUT("Codec Input Pin1"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin2"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin3"),
+
+ /* Output Pins */
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin1"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin2"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin3"),
+};
+
+static struct snd_soc_dai_driver card_binder_dai = {
+ .id = -1,
+ .name = "codec-probing-DAI",
+};
+
+static int hda_hdev_attach(struct hdac_device *hdev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
+ struct snd_soc_component_driver *comp_drv;
+
+ if (hda_codec_is_display(codec) && !hdev->bus->audio_component) {
+ dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id);
+ return -ENODEV;
+ }
+
+ comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL);
+ if (!comp_drv)
+ return -ENOMEM;
+
+ /*
+ * It's save to rely on dev_name() rather than a copy as component
+ * driver's lifetime is directly tied to hda codec one
+ */
+ comp_drv->name = dev_name(&hdev->dev);
+ comp_drv->probe = hda_codec_probe;
+ comp_drv->remove = hda_codec_remove;
+ comp_drv->idle_bias_on = false;
+ if (!hda_codec_is_display(codec)) {
+ comp_drv->dapm_widgets = hda_dapm_widgets;
+ comp_drv->num_dapm_widgets = ARRAY_SIZE(hda_dapm_widgets);
+ comp_drv->dapm_routes = hda_dapm_routes;
+ comp_drv->num_dapm_routes = ARRAY_SIZE(hda_dapm_routes);
+ }
+
+ return snd_soc_register_component(&hdev->dev, comp_drv, &card_binder_dai, 1);
+}
+
+static int hda_hdev_detach(struct hdac_device *hdev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
+
+ if (codec->core.registered)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+
+ snd_soc_unregister_component(&hdev->dev);
+
+ return 0;
+}
+
+const struct hdac_ext_bus_ops soc_hda_ext_bus_ops = {
+ .hdev_attach = hda_hdev_attach,
+ .hdev_detach = hda_hdev_detach,
+};
+EXPORT_SYMBOL_GPL(soc_hda_ext_bus_ops);
+
+MODULE_DESCRIPTION("HD-Audio codec driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/hda.h b/sound/soc/codecs/hda.h
new file mode 100644
index 000000000000..59308cc6afef
--- /dev/null
+++ b/sound/soc/codecs/hda.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef SND_SOC_CODECS_HDA_H
+#define SND_SOC_CODECS_HDA_H
+
+#define hda_codec_is_display(codec) \
+ ((((codec)->core.vendor_id >> 16) & 0xFFFF) == 0x8086)
+
+extern const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops;
+
+extern const struct hdac_ext_bus_ops soc_hda_ext_bus_ops;
+int hda_codec_probe_complete(struct hda_codec *codec);
+
+#endif
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
new file mode 100644
index 000000000000..680e341aa7f1
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2015-18 Intel Corporation.
+
+/*
+ * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers
+ * with ASoC platform drivers. These APIs are called by the legacy HDA
+ * codec drivers using hdac_ext_bus_ops ops.
+ */
+
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+
+#include "hdac_hda.h"
+
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE | \
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+#define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+static char *loadable_patch[HDA_MAX_CODECS];
+
+module_param_array_named(patch, loadable_patch, charp, NULL, 0444);
+MODULE_PARM_DESC(patch, "Patch file array for Intel HD audio interface. The array index is the codec address.");
+#endif
+
+static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream,
+ int direction);
+static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
+ struct snd_soc_dai *dai);
+
+static const struct snd_soc_dai_ops hdac_hda_dai_ops = {
+ .startup = hdac_hda_dai_open,
+ .shutdown = hdac_hda_dai_close,
+ .prepare = hdac_hda_dai_prepare,
+ .hw_params = hdac_hda_dai_hw_params,
+ .hw_free = hdac_hda_dai_hw_free,
+ .set_stream = hdac_hda_dai_set_stream,
+};
+
+static struct snd_soc_dai_driver hdac_hda_dais[] = {
+{
+ .id = HDAC_ANALOG_DAI_ID,
+ .name = "Analog Codec DAI",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "Analog Codec Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Analog Codec Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_DIGITAL_DAI_ID,
+ .name = "Digital Codec DAI",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "Digital Codec Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Digital Codec Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_ALT_ANALOG_DAI_ID,
+ .name = "Alt Analog Codec DAI",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "Alt Analog Codec Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Alt Analog Codec Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+};
+
+static struct snd_soc_dai_driver hdac_hda_hdmi_dais[] = {
+{
+ .id = HDAC_HDMI_0_DAI_ID,
+ .name = "intel-hdmi-hifi1",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi1",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_1_DAI_ID,
+ .name = "intel-hdmi-hifi2",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi2",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_2_DAI_ID,
+ .name = "intel-hdmi-hifi3",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi3",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+{
+ .id = HDAC_HDMI_3_DAI_ID,
+ .name = "intel-hdmi-hifi4",
+ .ops = &hdac_hda_dai_ops,
+ .playback = {
+ .stream_name = "hifi4",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = STUB_HDMI_RATES,
+ .formats = STUB_FORMATS,
+ .sig_bits = 24,
+ },
+},
+
+};
+
+static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hdac_hda_pcm *pcm;
+ struct hdac_stream *hstream;
+
+ if (!stream)
+ return -EINVAL;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = &hda_pvt->pcm[dai->id];
+ hstream = (struct hdac_stream *)stream;
+
+ pcm->stream_tag[direction] = hstream->stream_tag;
+
+ return 0;
+}
+
+static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ unsigned int format_val;
+ unsigned int maxbps;
+ unsigned int bits;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ maxbps = dai->driver->playback.sig_bits;
+ else
+ maxbps = dai->driver->capture.sig_bits;
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, maxbps);
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
+ if (!format_val) {
+ dev_err(dai->dev,
+ "%s: invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n",
+ __func__,
+ params_rate(params), params_channels(params),
+ params_format(params), maxbps);
+
+ return -EINVAL;
+ }
+
+ hda_pvt->pcm[dai->id].format_val[substream->stream] = format_val;
+ return 0;
+}
+
+static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_pcm_stream *hda_stream;
+ struct hda_pcm *pcm;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return -EINVAL;
+
+ hda_stream = &pcm->stream[substream->stream];
+ snd_hda_codec_cleanup(hda_pvt->codec, hda_stream, substream);
+
+ return 0;
+}
+
+static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hda_pcm_stream *hda_stream;
+ struct hdac_hda_priv *hda_pvt;
+ unsigned int format_val;
+ struct hda_pcm *pcm;
+ unsigned int stream;
+ int ret = 0;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return -EINVAL;
+
+ hda_stream = &pcm->stream[substream->stream];
+
+ stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream];
+ format_val = hda_pvt->pcm[dai->id].format_val[substream->stream];
+
+ ret = snd_hda_codec_prepare(hda_pvt->codec, hda_stream,
+ stream, format_val, substream);
+ if (ret < 0)
+ dev_err(dai->dev, "%s: failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_pcm_stream *hda_stream;
+ struct hda_pcm *pcm;
+ int ret;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return -EINVAL;
+
+ snd_hda_codec_pcm_get(pcm);
+
+ hda_stream = &pcm->stream[substream->stream];
+
+ ret = hda_stream->ops.open(hda_stream, hda_pvt->codec, substream);
+ if (ret < 0)
+ dev_err(dai->dev, "%s: failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_pcm_stream *hda_stream;
+ struct hda_pcm *pcm;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+ if (!pcm)
+ return;
+
+ hda_stream = &pcm->stream[substream->stream];
+
+ hda_stream->ops.close(hda_stream, hda_pvt->codec, substream);
+
+ snd_hda_codec_pcm_put(pcm);
+}
+
+static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
+ struct snd_soc_dai *dai)
+{
+ struct hda_codec *hcodec = hda_pvt->codec;
+ struct hda_pcm *cpcm;
+ const char *pcm_name;
+
+ /*
+ * map DAI ID to the closest matching PCM name, using the naming
+ * scheme used by hda-codec snd_hda_gen_build_pcms() and for
+ * HDMI in hda_codec patch_hdmi.c)
+ */
+
+ switch (dai->id) {
+ case HDAC_ANALOG_DAI_ID:
+ pcm_name = "Analog";
+ break;
+ case HDAC_DIGITAL_DAI_ID:
+ pcm_name = "Digital";
+ break;
+ case HDAC_ALT_ANALOG_DAI_ID:
+ pcm_name = "Alt Analog";
+ break;
+ case HDAC_HDMI_0_DAI_ID:
+ pcm_name = "HDMI 0";
+ break;
+ case HDAC_HDMI_1_DAI_ID:
+ pcm_name = "HDMI 1";
+ break;
+ case HDAC_HDMI_2_DAI_ID:
+ pcm_name = "HDMI 2";
+ break;
+ case HDAC_HDMI_3_DAI_ID:
+ pcm_name = "HDMI 3";
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id %d\n", __func__, dai->id);
+ return NULL;
+ }
+
+ list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+ if (strstr(cpcm->name, pcm_name)) {
+ if (strcmp(pcm_name, "Analog") == 0) {
+ if (strstr(cpcm->name, "Alt Analog"))
+ continue;
+ }
+ return cpcm;
+ }
+ }
+
+ dev_err(dai->dev, "%s: didn't find PCM for DAI %s\n", __func__, dai->name);
+ return NULL;
+}
+
+static bool is_hdmi_codec(struct hda_codec *hcodec)
+{
+ struct hda_pcm *cpcm;
+
+ list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+ if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
+ return true;
+ }
+
+ return false;
+}
+
+static int hdac_hda_codec_probe(struct snd_soc_component *component)
+{
+ struct hdac_hda_priv *hda_pvt =
+ snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = &hda_pvt->codec->core;
+ struct hda_codec *hcodec = hda_pvt->codec;
+ struct hda_codec_driver *driver = hda_codec_to_driver(hcodec);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "%s: hdac link not found\n", __func__);
+ return -EIO;
+ }
+
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ /*
+ * Ensure any HDA display is powered at codec probe.
+ * After snd_hda_codec_device_new(), display power is
+ * managed by runtime PM.
+ */
+ if (hda_pvt->need_display_power)
+ snd_hdac_display_power(hdev->bus,
+ HDA_CODEC_IDX_CONTROLLER, true);
+
+ ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
+ hdev->addr, hcodec, true);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: failed to create hda codec %d\n", __func__, ret);
+ goto error_no_pm;
+ }
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) {
+ const struct firmware *fw;
+
+ dev_info(&hdev->dev, "Applying patch firmware '%s'\n",
+ loadable_patch[hda_pvt->dev_index]);
+ ret = request_firmware(&fw, loadable_patch[hda_pvt->dev_index],
+ &hdev->dev);
+ if (ret < 0)
+ goto error_no_pm;
+ if (fw) {
+ ret = snd_hda_load_patch(hcodec->bus, fw->size, fw->data);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret);
+ goto error_no_pm;
+ }
+ release_firmware(fw);
+ }
+ }
+#endif
+ /*
+ * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver
+ * hda_codec.c will check this flag to determine if unregister
+ * device is needed.
+ */
+ hdev->type = HDA_DEV_ASOC;
+
+ /*
+ * snd_hda_codec_device_new decrements the usage count so call get pm
+ * else the device will be powered off
+ */
+ pm_runtime_get_noresume(&hdev->dev);
+
+ hcodec->bus->card = component->card->snd_card;
+
+ ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: name failed %s\n", __func__, hcodec->preset->name);
+ goto error_pm;
+ }
+
+ ret = snd_hdac_regmap_init(&hcodec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: regmap init failed\n", __func__);
+ goto error_pm;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto error_regmap;
+ }
+
+ ret = driver->ops->probe(hcodec, hcodec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret);
+ goto error_regmap;
+ }
+
+ ret = snd_hda_codec_parse_pcms(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: unable to map pcms to dai %d\n", __func__, ret);
+ goto error_patch;
+ }
+
+ /* HDMI controls need to be created in machine drivers */
+ if (!is_hdmi_codec(hcodec)) {
+ ret = snd_hda_codec_build_controls(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: unable to create controls %d\n",
+ __func__, ret);
+ goto error_patch;
+ }
+ }
+
+ hcodec->core.lazy_cache = true;
+
+ if (hda_pvt->need_display_power)
+ snd_hdac_display_power(hdev->bus,
+ HDA_CODEC_IDX_CONTROLLER, false);
+
+ /* match for forbid call in snd_hda_codec_device_new() */
+ pm_runtime_allow(&hdev->dev);
+
+ /*
+ * hdac_device core already sets the state to active and calls
+ * get_noresume. So enable runtime and set the device to suspend.
+ * pm_runtime_enable is also called during codec registeration
+ */
+ pm_runtime_put(&hdev->dev);
+ pm_runtime_suspend(&hdev->dev);
+
+ return 0;
+
+error_patch:
+ if (driver->ops->remove)
+ driver->ops->remove(hcodec);
+error_regmap:
+ snd_hdac_regmap_exit(hdev);
+error_pm:
+ pm_runtime_put(&hdev->dev);
+error_no_pm:
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+ return ret;
+}
+
+static void hdac_hda_codec_remove(struct snd_soc_component *component)
+{
+ struct hdac_hda_priv *hda_pvt =
+ snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = &hda_pvt->codec->core;
+ struct hda_codec *codec = hda_pvt->codec;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hdac_ext_link *hlink = NULL;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "%s: hdac link not found\n", __func__);
+ return;
+ }
+
+ pm_runtime_disable(&hdev->dev);
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
+
+ snd_hda_codec_cleanup_for_unbind(codec);
+}
+
+static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
+ {"AIF1TX", NULL, "Codec Input Pin1"},
+ {"AIF2TX", NULL, "Codec Input Pin2"},
+ {"AIF3TX", NULL, "Codec Input Pin3"},
+
+ {"Codec Output Pin1", NULL, "AIF1RX"},
+ {"Codec Output Pin2", NULL, "AIF2RX"},
+ {"Codec Output Pin3", NULL, "AIF3RX"},
+};
+
+static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ /* Input Pins */
+ SND_SOC_DAPM_INPUT("Codec Input Pin1"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin2"),
+ SND_SOC_DAPM_INPUT("Codec Input Pin3"),
+
+ /* Output Pins */
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin1"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin2"),
+ SND_SOC_DAPM_OUTPUT("Codec Output Pin3"),
+};
+
+static const struct snd_soc_component_driver hdac_hda_codec = {
+ .probe = hdac_hda_codec_probe,
+ .remove = hdac_hda_codec_remove,
+ .dapm_widgets = hdac_hda_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets),
+ .dapm_routes = hdac_hda_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes),
+ .idle_bias_on = false,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver hdac_hda_hdmi_codec = {
+ .probe = hdac_hda_codec_probe,
+ .remove = hdac_hda_codec_remove,
+ .idle_bias_on = false,
+ .endianness = 1,
+};
+
+static int hdac_hda_dev_probe(struct hdac_device *hdev)
+{
+ struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ /* hold the ref while we probe */
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "%s: hdac link not found\n", __func__);
+ return -EIO;
+ }
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+ /* ASoC specific initialization */
+ if (hda_pvt->need_display_power)
+ ret = devm_snd_soc_register_component(&hdev->dev,
+ &hdac_hda_hdmi_codec, hdac_hda_hdmi_dais,
+ ARRAY_SIZE(hdac_hda_hdmi_dais));
+ else
+ ret = devm_snd_soc_register_component(&hdev->dev,
+ &hdac_hda_codec, hdac_hda_dais,
+ ARRAY_SIZE(hdac_hda_dais));
+
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: failed to register HDA codec %d\n", __func__, ret);
+ return ret;
+ }
+
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ return ret;
+}
+
+static int hdac_hda_dev_remove(struct hdac_device *hdev)
+{
+ /*
+ * Resources are freed in hdac_hda_codec_remove(). This
+ * function is kept to keep hda_codec_driver_remove() happy.
+ */
+ return 0;
+}
+
+static struct hdac_ext_bus_ops hdac_ops = {
+ .hdev_attach = hdac_hda_dev_probe,
+ .hdev_detach = hdac_hda_dev_remove,
+};
+
+struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void)
+{
+ return &hdac_ops;
+}
+EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers");
+MODULE_AUTHOR("Rakesh Ughreja<rakesh.a.ughreja@intel.com>");
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
new file mode 100644
index 000000000000..d03a5d4e7288
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2015-18 Intel Corporation.
+ */
+
+#ifndef __HDAC_HDA_H__
+#define __HDAC_HDA_H__
+
+enum {
+ HDAC_ANALOG_DAI_ID = 0,
+ HDAC_DIGITAL_DAI_ID,
+ HDAC_ALT_ANALOG_DAI_ID,
+ HDAC_HDMI_0_DAI_ID,
+ HDAC_HDMI_1_DAI_ID,
+ HDAC_HDMI_2_DAI_ID,
+ HDAC_HDMI_3_DAI_ID,
+ HDAC_DAI_ID_NUM
+};
+
+struct hdac_hda_pcm {
+ int stream_tag[2];
+ unsigned int format_val[2];
+};
+
+struct hdac_hda_priv {
+ struct hda_codec *codec;
+ struct hdac_hda_pcm pcm[HDAC_DAI_ID_NUM];
+ bool need_display_power;
+ int dev_index;
+};
+
+struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void);
+
+#endif /* __HDAC_HDA_H__ */
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 5a1ec0f7a1a6..2652fcf2a3a3 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
*
@@ -6,27 +7,25 @@
* Subhransu S. Prusty <subhransu.s.prusty@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/hdmi.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
#include <sound/pcm_params.h>
+#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_i915.h>
-#include "../../hda/local.h"
+#include <sound/pcm_drm_eld.h>
+#include <sound/hda_chmap.h>
+
+#define NAME_SIZE 32
#define AMP_OUT_MUTE 0xb080
#define AMP_OUT_UNMUTE 0xb000
@@ -34,6 +33,16 @@
#define HDA_MAX_CONNECTIONS 32
+#define HDA_MAX_CVTS 3
+#define HDA_MAX_PORTS 3
+
+#define ELD_MAX_SIZE 256
+#define ELD_FIXED_BYTES 20
+
+#define ELD_VER_CEA_861D 2
+#define ELD_VER_PARTIAL 31
+#define ELD_MAX_MNL 16
+
struct hdac_hdmi_cvt_params {
unsigned int channels_min;
unsigned int channels_max;
@@ -45,384 +54,1461 @@ struct hdac_hdmi_cvt_params {
struct hdac_hdmi_cvt {
struct list_head head;
hda_nid_t nid;
+ const char *name;
struct hdac_hdmi_cvt_params params;
};
+/* Currently only spk_alloc, more to be added */
+struct hdac_hdmi_parsed_eld {
+ u8 spk_alloc;
+};
+
+struct hdac_hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
+ int eld_size;
+ char eld_buffer[ELD_MAX_SIZE];
+ struct hdac_hdmi_parsed_eld info;
+};
+
struct hdac_hdmi_pin {
struct list_head head;
hda_nid_t nid;
+ bool mst_capable;
+ struct hdac_hdmi_port *ports;
+ int num_ports;
+ struct hdac_device *hdev;
+};
+
+struct hdac_hdmi_port {
+ struct list_head head;
+ int id;
+ struct hdac_hdmi_pin *pin;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ struct hdac_hdmi_eld eld;
+ const char *jack_pin;
+ bool is_connect;
+ struct snd_soc_dapm_context *dapm;
+ const char *output_pin;
+ struct work_struct dapm_work;
+};
+
+struct hdac_hdmi_pcm {
+ struct list_head head;
+ int pcm_id;
+ struct list_head port_list;
+ struct hdac_hdmi_cvt *cvt;
+ struct snd_soc_jack *jack;
+ int stream_tag;
+ int channels;
+ int format;
+ bool chmap_set;
+ unsigned char chmap[8]; /* ALSA API channel-map */
+ struct mutex lock;
+ int jack_event;
+ struct snd_kcontrol *eld_ctl;
};
-struct hdac_hdmi_dai_pin_map {
+struct hdac_hdmi_dai_port_map {
int dai_id;
- struct hdac_hdmi_pin *pin;
+ struct hdac_hdmi_port *port;
struct hdac_hdmi_cvt *cvt;
};
+struct hdac_hdmi_drv_data {
+ unsigned int vendor_nid;
+};
+
struct hdac_hdmi_priv {
- struct hdac_hdmi_dai_pin_map dai_map[3];
+ struct hdac_device *hdev;
+ struct snd_soc_component *component;
+ struct snd_card *card;
+ struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS];
struct list_head pin_list;
struct list_head cvt_list;
+ struct list_head pcm_list;
int num_pin;
int num_cvt;
+ int num_ports;
+ struct mutex pin_mutex;
+ struct hdac_chmap chmap;
+ struct hdac_hdmi_drv_data *drv_data;
+ struct snd_soc_dai_driver *dai_drv;
};
-static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
+#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev)
+
+static struct hdac_hdmi_pcm *
+hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
+ struct hdac_hdmi_cvt *cvt)
+{
+ struct hdac_hdmi_pcm *pcm;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->cvt == cvt)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
+ struct hdac_hdmi_port *port, bool is_connect)
+{
+ struct hdac_device *hdev = port->pin->hdev;
+
+ port->is_connect = is_connect;
+ if (is_connect) {
+ /*
+ * Report Jack connect event when a device is connected
+ * for the first time where same PCM is attached to multiple
+ * ports.
+ */
+ if (pcm->jack_event == 0) {
+ dev_dbg(&hdev->dev,
+ "jack report for pcm=%d\n",
+ pcm->pcm_id);
+ snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT,
+ SND_JACK_AVOUT);
+ }
+ pcm->jack_event++;
+ } else {
+ /*
+ * Report Jack disconnect event when a device is disconnected
+ * is the only last connected device when same PCM is attached
+ * to multiple ports.
+ */
+ if (pcm->jack_event == 1)
+ snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT);
+ if (pcm->jack_event > 0)
+ pcm->jack_event--;
+ }
+}
+
+static void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port)
{
- struct hdac_device *hdac = dev_to_hdac_dev(dev);
+ if (port->is_connect)
+ snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
+ else
+ snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
+ snd_soc_dapm_sync(port->dapm);
+}
+
+static void hdac_hdmi_jack_dapm_work(struct work_struct *work)
+{
+ struct hdac_hdmi_port *port;
+
+ port = container_of(work, struct hdac_hdmi_port, dapm_work);
+ hdac_hdmi_port_dapm_update(port);
+}
+
+static void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm,
+ struct hdac_hdmi_port *port, bool is_connect)
+{
+ hdac_hdmi_jack_report(pcm, port, is_connect);
+ hdac_hdmi_port_dapm_update(port);
+}
+
+/* MST supported verbs */
+/*
+ * Get the no devices that can be connected to a port on the Pin widget.
+ */
+static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid)
+{
+ unsigned int caps;
+ unsigned int type, param;
+
+ caps = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN))
+ return 0;
+
+ param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN);
+ if (param == -1)
+ return param;
+
+ return param & AC_DEV_LIST_LEN_MASK;
+}
- return to_ehdac_device(hdac);
+/*
+ * Get the port entry select on the pin. Return the port entry
+ * id selected on the pin. Return 0 means the first port entry
+ * is selected or MST is not supported.
+ */
+static int hdac_hdmi_port_select_get(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ return snd_hdac_codec_read(hdev, port->pin->nid,
+ 0, AC_VERB_GET_DEVICE_SEL, 0);
}
-static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
- hda_nid_t cvt_nid, hda_nid_t pin_nid,
- u32 stream_tag, int format)
+/*
+ * Sets the selected port entry for the configuring Pin widget verb.
+ * returns error if port set is not equal to port get otherwise success
+ */
+static int hdac_hdmi_port_select_set(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
{
- unsigned int val;
+ int num_ports;
- dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
- cvt_nid, pin_nid, stream_tag, format);
+ if (!port->pin->mst_capable)
+ return 0;
- val = (stream_tag << 4);
+ /* AC_PAR_DEVLIST_LEN is 0 based. */
+ num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid);
+ if (num_ports < 0)
+ return -EIO;
+ /*
+ * Device List Length is a 0 based integer value indicating the
+ * number of sink device that a MST Pin Widget can support.
+ */
+ if (num_ports + 1 < port->id)
+ return 0;
+
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_DEVICE_SEL, port->id);
+
+ if (port->id != hdac_hdmi_port_select_get(hdev, port))
+ return -EIO;
- snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID, val);
- snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
- AC_VERB_SET_STREAM_FORMAT, format);
+ dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id);
return 0;
}
+static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
+ int pcm_idx)
+{
+ struct hdac_hdmi_pcm *pcm;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->pcm_id == pcm_idx)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static unsigned int sad_format(const u8 *sad)
+{
+ return ((sad[0] >> 0x3) & 0x1f);
+}
+
+static unsigned int sad_sample_bits_lpcm(const u8 *sad)
+{
+ return (sad[2] & 7);
+}
+
+static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
+ void *eld)
+{
+ u64 formats = SNDRV_PCM_FMTBIT_S16;
+ int i;
+ const u8 *sad, *eld_buf = eld;
+
+ sad = drm_eld_sad(eld_buf);
+ if (!sad)
+ goto format_constraint;
+
+ for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
+ if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
+
+ /*
+ * the controller support 20 and 24 bits in 32 bit
+ * container so we set S32
+ */
+ if (sad_sample_bits_lpcm(sad) & 0x6)
+ formats |= SNDRV_PCM_FMTBIT_S32;
+ }
+ }
+
+format_constraint:
+ return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ formats);
+
+}
+
static void
-hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
+hdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid,
int packet_index, int byte_index)
{
int val;
val = (packet_index << 5) | (byte_index & 0x1f);
-
- snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
- AC_VERB_SET_HDMI_DIP_INDEX, val);
+ snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
}
-static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
- hda_nid_t cvt_nid, hda_nid_t pin_nid)
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev,
+ struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port)
{
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
struct hdmi_audio_infoframe frame;
- u8 *dip = (u8 *)&frame;
+ struct hdac_hdmi_pin *pin = port->pin;
+ struct dp_audio_infoframe dp_ai;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_cvt *cvt = pcm->cvt;
+ u8 *dip;
int ret;
int i;
+ const u8 *eld_buf;
+ u8 conn_type;
+ int channels, ca;
- hdmi_audio_infoframe_init(&frame);
+ ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc,
+ pcm->channels, pcm->chmap_set, true, pcm->chmap);
- /* Default stereo for now */
- frame.channels = 2;
+ channels = snd_hdac_get_active_channels(ca);
+ hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels);
- /* setup channel count */
- snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
+ snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
+ pcm->channels, pcm->chmap, pcm->chmap_set);
- ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
- if (ret < 0)
- return ret;
+ eld_buf = port->eld.eld_buffer;
+ conn_type = drm_eld_get_conn_type(eld_buf);
+
+ switch (conn_type) {
+ case DRM_ELD_CONN_TYPE_HDMI:
+ hdmi_audio_infoframe_init(&frame);
+
+ frame.channels = channels;
+ frame.channel_allocation = ca;
+
+ ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case DRM_ELD_CONN_TYPE_DP:
+ memset(&dp_ai, 0, sizeof(dp_ai));
+ dp_ai.type = 0x84;
+ dp_ai.len = 0x1b;
+ dp_ai.ver = 0x11 << 2;
+ dp_ai.CC02_CT47 = channels - 1;
+ dp_ai.CA = ca;
+
+ dip = (u8 *)&dp_ai;
+ break;
+
+ default:
+ dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type);
+ return -EIO;
+ }
/* stop infoframe transmission */
- hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
- snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+ hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
+ snd_hdac_codec_write(hdev, pin->nid, 0,
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
/* Fill infoframe. Index auto-incremented */
- hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(frame); i++)
- snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+ hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
+ if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
+ for (i = 0; i < sizeof(buffer); i++)
+ snd_hdac_codec_write(hdev, pin->nid, 0,
+ AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);
+ } else {
+ for (i = 0; i < sizeof(dp_ai); i++)
+ snd_hdac_codec_write(hdev, pin->nid, 0,
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
+ }
/* Start infoframe */
- hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
- snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+ hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
+ snd_hdac_codec_write(hdev, pin->nid, 0,
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
return 0;
}
-static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
- struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
+static int hdac_hdmi_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
{
- /* Power up pin widget */
- if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
- pwr_state))
- snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
- AC_VERB_SET_POWER_STATE, pwr_state);
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_stream *hstream;
- /* Power up converter */
- if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
- pwr_state))
- snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
- AC_VERB_SET_POWER_STATE, pwr_state);
-}
+ if (!stream)
+ return -EINVAL;
-static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdac->private_data;
- struct hdac_hdmi_dai_pin_map *dai_map;
- struct hdac_ext_dma_params *dd;
- int ret;
+ hstream = (struct hdac_stream *)stream;
- if (dai->id > 0) {
- dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
- return -ENODEV;
- }
+ dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, hstream->stream_tag);
dai_map = &hdmi->dai_map[dai->id];
- dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
- dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
- dd->stream_tag, dd->format);
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
- ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
- dai_map->pin->nid);
- if (ret < 0)
- return ret;
+ if (pcm)
+ pcm->stream_tag = (hstream->stream_tag << 4);
- return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
- dai_map->pin->nid, dd->stream_tag, dd->format);
+ return 0;
}
static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
{
- struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
- struct hdac_ext_dma_params *dd;
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_pcm *pcm;
+ unsigned int bits;
+ int format;
- if (dai->id > 0) {
- dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
- return -ENODEV;
- }
+ dai_map = &hdmi->dai_map[dai->id];
- dd = kzalloc(sizeof(*dd), GFP_KERNEL);
- if (!dd)
- return -ENOMEM;
- dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
- params_channels(hparams), params_format(hparams),
- 24, 0);
+ bits = snd_hdac_stream_format_bits(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD,
+ dai->driver->playback.sig_bits);
+ format = snd_hdac_stream_format(params_channels(hparams), bits, params_rate(hparams));
+
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+ if (!pcm)
+ return -EIO;
- snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
+ pcm->format = format;
+ pcm->channels = params_channels(hparams);
return 0;
}
-static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev,
+ struct hdac_hdmi_pin *pin,
+ struct hdac_hdmi_port *port)
{
- struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
- struct hdac_ext_dma_params *dd;
- struct hdac_hdmi_priv *hdmi = edev->private_data;
- struct hdac_hdmi_dai_pin_map *dai_map;
+ if (!(snd_hdac_get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) {
+ dev_warn(&hdev->dev,
+ "HDMI: pin %d wcaps %#x does not support connection list\n",
+ pin->nid, snd_hdac_get_wcaps(hdev, pin->nid));
+ return -EINVAL;
+ }
- dai_map = &hdmi->dai_map[dai->id];
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
+ return -EIO;
- snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID, 0);
- snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
- AC_VERB_SET_STREAM_FORMAT, 0);
+ port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid,
+ port->mux_nids, HDA_MAX_CONNECTIONS);
+ if (port->num_mux_nids == 0)
+ dev_warn(&hdev->dev,
+ "No connections found for pin:port %d:%d\n",
+ pin->nid, port->id);
- dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
- snd_soc_dai_set_dma_data(dai, substream, NULL);
+ dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n",
+ port->num_mux_nids, pin->nid, port->id);
- kfree(dd);
+ return port->num_mux_nids;
+}
- return 0;
+/*
+ * Query pcm list and return port to which stream is routed.
+ *
+ * Also query connection list of the pin, to validate the cvt to port map.
+ *
+ * Same stream rendering to multiple ports simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first port
+ * connected.
+ */
+static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
+ struct hdac_device *hdev,
+ struct hdac_hdmi_priv *hdmi,
+ struct hdac_hdmi_cvt *cvt)
+{
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_port *port;
+ int ret, i;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->cvt == cvt) {
+ if (list_empty(&pcm->port_list))
+ continue;
+
+ list_for_each_entry(port, &pcm->port_list, head) {
+ mutex_lock(&pcm->lock);
+ ret = hdac_hdmi_query_port_connlist(hdev,
+ port->pin, port);
+ mutex_unlock(&pcm->lock);
+ if (ret < 0)
+ continue;
+
+ for (i = 0; i < port->num_mux_nids; i++) {
+ if (port->mux_nids[i] == cvt->nid &&
+ port->eld.monitor_present &&
+ port->eld.eld_valid)
+ return port;
+ }
+ }
+ }
+ }
+
+ return NULL;
}
+/*
+ * Go through all converters and ensure connection is set to
+ * the correct pin as set via kcontrols.
+ */
+static void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_port *port;
+ struct hdac_hdmi_cvt *cvt;
+ int cvt_idx = 0;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
+ if (port && port->pin) {
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_CONNECT_SEL, cvt_idx);
+ dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n",
+ __func__, cvt->name, port->pin->nid, cvt_idx);
+ }
+ ++cvt_idx;
+ }
+}
+
+/*
+ * This tries to get a valid pin and set the HW constraints based on the
+ * ELD. Even if a valid pin is not found return success so that device open
+ * doesn't fail.
+ */
static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdac->private_data;
- struct hdac_hdmi_dai_pin_map *dai_map;
- int val;
-
- if (dai->id > 0) {
- dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
- return -ENODEV;
- }
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_cvt *cvt;
+ struct hdac_hdmi_port *port;
+ int ret;
dai_map = &hdmi->dai_map[dai->id];
- val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
+ cvt = dai_map->cvt;
+ port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
- if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
- dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
- return -ENODEV;
- }
+ /*
+ * To make PA and other userland happy.
+ * userland scans devices so returning error does not help.
+ */
+ if (!port)
+ return 0;
+ if ((!port->eld.monitor_present) ||
+ (!port->eld.eld_valid)) {
- hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+ dev_warn(&hdev->dev,
+ "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n",
+ port->eld.monitor_present, port->eld.eld_valid,
+ port->pin->nid, port->id);
- snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ return 0;
+ }
- snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ dai_map->port = port;
- return 0;
+ ret = hdac_hdmi_eld_limit_formats(substream->runtime,
+ port->eld.eld_buffer);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_eld(substream->runtime,
+ port->eld.eld_buffer);
}
static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
- struct hdac_hdmi_priv *hdmi = hdac->private_data;
- struct hdac_hdmi_dai_pin_map *dai_map;
+ struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_pcm *pcm;
dai_map = &hdmi->dai_map[dai->id];
- hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+
+ if (pcm) {
+ mutex_lock(&pcm->lock);
+ pcm->chmap_set = false;
+ memset(pcm->chmap, 0, sizeof(pcm->chmap));
+ pcm->channels = 0;
+ mutex_unlock(&pcm->lock);
+ }
- snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (dai_map->port)
+ dai_map->port = NULL;
}
static int
-hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
+hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
{
+ unsigned int chans;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
int err;
- /* Only stereo supported as of now */
- cvt->params.channels_min = cvt->params.channels_max = 2;
+ chans = snd_hdac_get_wcaps(hdev, cvt->nid);
+ chans = snd_hdac_get_wcaps_channels(chans);
+
+ cvt->params.channels_min = 2;
- err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
+ cvt->params.channels_max = chans;
+ if (chans > hdmi->chmap.channels_max)
+ hdmi->chmap.channels_max = chans;
+
+ err = snd_hdac_query_supported_pcm(hdev, cvt->nid,
&cvt->params.rates,
&cvt->params.formats,
+ NULL,
&cvt->params.maxbps);
if (err < 0)
- dev_err(&hdac->dev,
+ dev_err(&hdev->dev,
"Failed to query pcm params for nid %d: %d\n",
cvt->nid, err);
return err;
}
-static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
- enum snd_soc_dapm_type id,
- const char *wname, const char *stream)
+static int hdac_hdmi_fill_widget_info(struct device *dev,
+ struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
+ void *priv, const char *wname, const char *stream,
+ struct snd_kcontrol_new *wc, int numkc,
+ int (*event)(struct snd_soc_dapm_widget *,
+ struct snd_kcontrol *, int), unsigned short event_flags)
{
w->id = id;
- w->name = wname;
+ w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
+ if (!w->name)
+ return -ENOMEM;
+
w->sname = stream;
w->reg = SND_SOC_NOPM;
w->shift = 0;
- w->kcontrol_news = NULL;
- w->num_kcontrols = 0;
- w->priv = NULL;
+ w->kcontrol_news = wc;
+ w->num_kcontrols = numkc;
+ w->priv = priv;
+ w->event = event;
+ w->event_flags = event_flags;
+
+ return 0;
}
static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
- const char *sink, const char *control, const char *src)
+ const char *sink, const char *control, const char *src,
+ int (*handler)(struct snd_soc_dapm_widget *src,
+ struct snd_soc_dapm_widget *sink))
{
route->sink = sink;
route->source = src;
route->control = control;
- route->connected = NULL;
+ route->connected = handler;
+}
+
+static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_port *p;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (list_empty(&pcm->port_list))
+ continue;
+
+ list_for_each_entry(p, &pcm->port_list, head) {
+ if (p->id == port->id && port->pin == p->pin)
+ return pcm;
+ }
+ }
+
+ return NULL;
+}
+
+static void hdac_hdmi_set_power_state(struct hdac_device *hdev,
+ hda_nid_t nid, unsigned int pwr_state)
+{
+ int count;
+ unsigned int state;
+
+ if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_POWER) {
+ if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) {
+ for (count = 0; count < 10; count++) {
+ snd_hdac_codec_read(hdev, nid, 0,
+ AC_VERB_SET_POWER_STATE,
+ pwr_state);
+ state = snd_hdac_sync_power_state(hdev,
+ nid, pwr_state);
+ if (!(state & AC_PWRST_ERROR))
+ break;
+ }
+ }
+ }
+}
+
+static void hdac_hdmi_set_amp(struct hdac_device *hdev,
+ hda_nid_t nid, int val)
+{
+ if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP)
+ snd_hdac_codec_write(hdev, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+
+static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct hdac_hdmi_port *port = w->priv;
+ struct device *dev = snd_soc_dapm_to_dev(w->dapm);
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_hdmi_pcm *pcm;
+
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
+ __func__, w->name, event);
+
+ pcm = hdac_hdmi_get_pcm(hdev, port);
+ if (!pcm)
+ return -EIO;
+
+ /* set the device if pin is mst_capable */
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
+ return -EIO;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0);
+
+ /* Enable out path for this pin widget */
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE);
+
+ return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
+
+ case SND_SOC_DAPM_POST_PMD:
+ hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE);
+
+ /* Disable out path for this pin widget */
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+
+ hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct hdac_hdmi_cvt *cvt = w->priv;
+ struct device *dev = snd_soc_dapm_to_dev(w->dapm);
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
+ __func__, w->name, event);
+
+ pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
+ if (!pcm)
+ return -EIO;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0);
+
+ /* Enable transmission */
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+ /* Category Code (CC) to zero */
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_2, 0);
+
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, pcm->format);
+
+ /*
+ * The connection indices are shared by all converters and
+ * may interfere with each other. Ensure correct
+ * routing for all converters at stream start.
+ */
+ hdac_hdmi_verify_connect_sel_all_pins(hdev);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ snd_hdac_codec_write(hdev, cvt->nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, 0);
+
+ hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct hdac_hdmi_port *port = w->priv;
+ struct device *dev = snd_soc_dapm_to_dev(w->dapm);
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ int mux_idx;
+
+ dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
+ __func__, w->name, event);
+
+ if (!kc)
+ kc = w->kcontrols[0];
+
+ mux_idx = snd_soc_dapm_kcontrol_get_value(kc);
+
+ /* set the device if pin is mst_capable */
+ if (hdac_hdmi_port_select_set(hdev, port) < 0)
+ return -EIO;
+
+ if (mux_idx > 0) {
+ snd_hdac_codec_write(hdev, port->pin->nid, 0,
+ AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
+ }
+
+ return 0;
}
-static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
- struct hdac_hdmi_dai_pin_map *dai_map)
+/*
+ * Based on user selection, map the PINs with the PCMs.
+ */
+static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_route route[1];
- struct snd_soc_dapm_widget widgets[2] = { {0} };
+ int ret;
+ struct hdac_hdmi_port *p, *p_next;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
+ struct hdac_hdmi_port *port = w->priv;
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+ const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
- memset(&route, 0, sizeof(route));
+ if (port == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hdmi->pin_mutex);
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (list_empty(&pcm->port_list))
+ continue;
- hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
- "hif1 Output", NULL);
- hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
- "Coverter 1", "hif1");
+ list_for_each_entry_safe(p, p_next, &pcm->port_list, head) {
+ if (p == port && p->id == port->id &&
+ p->pin == port->pin) {
+ hdac_hdmi_jack_report_sync(pcm, port, false);
+ list_del(&p->head);
+ }
+ }
+ }
- hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
+ /*
+ * Jack status is not reported during device probe as the
+ * PCMs are not registered by then. So report it here.
+ */
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (!strcmp(cvt_name, pcm->cvt->name)) {
+ list_add_tail(&port->head, &pcm->port_list);
+ if (port->eld.monitor_present && port->eld.eld_valid) {
+ hdac_hdmi_jack_report_sync(pcm, port, true);
+ mutex_unlock(&hdmi->pin_mutex);
+ return ret;
+ }
+ }
+ }
+ mutex_unlock(&hdmi->pin_mutex);
- snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
- snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
+ return ret;
}
-static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
+/*
+ * Ideally the Mux inputs should be based on the num_muxs enumerated, but
+ * the display driver seem to be programming the connection list for the pin
+ * widget runtime.
+ *
+ * So programming all the possible inputs for the mux, the user has to take
+ * care of selecting the right one and leaving all other inputs selected to
+ * "NONE"
+ */
+static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port,
+ struct snd_soc_dapm_widget *widget,
+ const char *widget_name)
{
- struct hdac_hdmi_priv *hdmi = edev->private_data;
- struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin = port->pin;
+ struct snd_kcontrol_new *kc;
+ struct hdac_hdmi_cvt *cvt;
+ struct soc_enum *se;
+ char kc_name[NAME_SIZE];
+ char mux_items[NAME_SIZE];
+ /* To hold inputs to the Pin mux */
+ char *items[HDA_MAX_CONNECTIONS];
+ int i = 0;
+ int num_items = hdmi->num_cvt + 1;
+
+ kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL);
+ if (!kc)
+ return -ENOMEM;
+
+ se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL);
+ if (!se)
+ return -ENOMEM;
+
+ snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input",
+ pin->nid, port->id);
+ kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+
+ kc->private_value = (long)se;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = 0;
+ kc->info = snd_soc_info_enum_double;
+ kc->put = hdac_hdmi_set_pin_port_mux;
+ kc->get = snd_soc_dapm_get_enum_double;
+
+ se->reg = SND_SOC_NOPM;
+
+ /* enum texts: ["NONE", "cvt #", "cvt #", ...] */
+ se->items = num_items;
+ se->mask = roundup_pow_of_two(se->items) - 1;
+
+ sprintf(mux_items, "NONE");
+ items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
+ if (!items[i])
+ return -ENOMEM;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ i++;
+ sprintf(mux_items, "cvt %d", cvt->nid);
+ items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
+ if (!items[i])
+ return -ENOMEM;
+ }
+
+ se->texts = devm_kmemdup_array(&hdev->dev, items, num_items, sizeof(items[0]), GFP_KERNEL);
+ if (!se->texts)
+ return -ENOMEM;
+
+ return hdac_hdmi_fill_widget_info(&hdev->dev, widget,
+ snd_soc_dapm_mux, port, widget_name, NULL, kc, 1,
+ hdac_hdmi_pin_mux_widget_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);
+}
+
+/* Add cvt <- input <- mux route map */
+static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev,
+ struct snd_soc_dapm_widget *widgets,
+ struct snd_soc_dapm_route *route, int rindex)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ const struct snd_kcontrol_new *kc;
+ struct soc_enum *se;
+ int mux_index = hdmi->num_cvt + hdmi->num_ports;
+ int i, j;
+
+ for (i = 0; i < hdmi->num_ports; i++) {
+ kc = widgets[mux_index].kcontrol_news;
+ se = (struct soc_enum *)kc->private_value;
+ for (j = 0; j < hdmi->num_cvt; j++) {
+ hdac_hdmi_fill_route(&route[rindex],
+ widgets[mux_index].name,
+ se->texts[j + 1],
+ widgets[j].name, NULL);
+
+ rindex++;
+ }
+
+ mux_index++;
+ }
+}
+
+/*
+ * Widgets are added in the below sequence
+ * Converter widgets for num converters enumerated
+ * Pin-port widgets for num ports for Pins enumerated
+ * Pin-port mux widgets to represent connenction list of pin widget
+ *
+ * For each port, one Mux and One output widget is added
+ * Total widgets elements = num_cvt + (num_ports * 2);
+ *
+ * Routes are added as below:
+ * pin-port mux -> pin (based on num_ports)
+ * cvt -> "Input sel control" -> pin-port_mux
+ *
+ * Total route elements:
+ * num_ports + (pin_muxes * num_cvt)
+ */
+static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
+{
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct snd_soc_dapm_widget *widgets;
+ struct snd_soc_dapm_route *route;
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv;
+ char widget_name[NAME_SIZE];
struct hdac_hdmi_cvt *cvt;
struct hdac_hdmi_pin *pin;
+ int ret, i = 0, num_routes = 0, j;
if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
return -EINVAL;
- /*
- * Currently on board only 1 pin and 1 converter is enabled for
- * simplification, more will be added eventually
- * So using fixed map for dai_id:pin:cvt
- */
- cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
- pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
+ widgets = devm_kzalloc(dev, (sizeof(*widgets) *
+ ((2 * hdmi->num_ports) + hdmi->num_cvt)),
+ GFP_KERNEL);
- dai_map->dai_id = 0;
- dai_map->pin = pin;
+ if (!widgets)
+ return -ENOMEM;
- dai_map->cvt = cvt;
+ /* DAPM widgets to represent each converter widget */
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ sprintf(widget_name, "Converter %d", cvt->nid);
+ ret = hdac_hdmi_fill_widget_info(dev, &widgets[i],
+ snd_soc_dapm_aif_in, cvt,
+ widget_name, dai_drv[i].playback.stream_name, NULL, 0,
+ hdac_hdmi_cvt_output_widget_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
- /* Enable out path for this pin widget */
- snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ sprintf(widget_name, "hif%d-%d Output",
+ pin->nid, pin->ports[j].id);
+ ret = hdac_hdmi_fill_widget_info(dev, &widgets[i],
+ snd_soc_dapm_output, &pin->ports[j],
+ widget_name, NULL, NULL, 0,
+ hdac_hdmi_pin_output_widget_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ pin->ports[j].output_pin = widgets[i].name;
+ i++;
+ }
+ }
- /* Enable transmission */
- snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1, 1);
+ /* DAPM widgets to represent the connection list to pin widget */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ sprintf(widget_name, "Pin%d-Port%d Mux",
+ pin->nid, pin->ports[j].id);
+ ret = hdac_hdmi_create_pin_port_muxs(hdev,
+ &pin->ports[j], &widgets[i],
+ widget_name);
+ if (ret < 0)
+ return ret;
+ i++;
- /* Category Code (CC) to zero */
- snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
- AC_VERB_SET_DIGI_CONVERT_2, 0);
+ /* For cvt to pin_mux mapping */
+ num_routes += hdmi->num_cvt;
+
+ /* For pin_mux to pin mapping */
+ num_routes++;
+ }
+ }
+
+ route = devm_kzalloc(dev, (sizeof(*route) * num_routes),
+ GFP_KERNEL);
+ if (!route)
+ return -ENOMEM;
+
+ i = 0;
+ /* Add pin <- NULL <- mux route map */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ for (j = 0; j < pin->num_ports; j++) {
+ int sink_index = i + hdmi->num_cvt;
+ int src_index = sink_index + pin->num_ports *
+ hdmi->num_pin;
+
+ hdac_hdmi_fill_route(&route[i],
+ widgets[sink_index].name, NULL,
+ widgets[src_index].name, NULL);
+ i++;
+ }
+ }
+
+ hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i);
+
+ snd_soc_dapm_new_controls(dapm, widgets,
+ ((2 * hdmi->num_ports) + hdmi->num_cvt));
+
+ snd_soc_dapm_add_routes(dapm, route, num_routes);
+ snd_soc_dapm_new_widgets(card);
+
+ return 0;
+
+}
+
+static int hdac_hdmi_init_dai_map(struct hdac_device *hdev)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_dai_port_map *dai_map;
+ struct hdac_hdmi_cvt *cvt;
+ int dai_id = 0;
+
+ if (list_empty(&hdmi->cvt_list))
+ return -EINVAL;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ dai_map = &hdmi->dai_map[dai_id];
+ dai_map->dai_id = dai_id;
+ dai_map->cvt = cvt;
- snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
- AC_VERB_SET_CONNECT_SEL, 0);
+ dai_id++;
+
+ if (dai_id == HDA_MAX_CVTS) {
+ dev_warn(&hdev->dev,
+ "Max dais supported: %d\n", dai_id);
+ break;
+ }
+ }
return 0;
}
-static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
+static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid)
{
- struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_cvt *cvt;
+ char name[NAME_SIZE];
- cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
+ cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL);
if (!cvt)
return -ENOMEM;
cvt->nid = nid;
+ sprintf(name, "cvt %d", cvt->nid);
+ cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL);
+ if (!cvt->name)
+ return -ENOMEM;
list_add_tail(&cvt->head, &hdmi->cvt_list);
hdmi->num_cvt++;
- return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
+ return hdac_hdmi_query_cvt_params(hdev, cvt);
+}
+
+static int hdac_hdmi_parse_eld(struct hdac_device *hdev,
+ struct hdac_hdmi_port *port)
+{
+ unsigned int ver, mnl;
+
+ ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK)
+ >> DRM_ELD_VER_SHIFT;
+
+ if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
+ dev_err_ratelimited(&hdev->dev,
+ "HDMI: Unknown ELD version %d\n", ver);
+ return -EINVAL;
+ }
+
+ mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] &
+ DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
+
+ if (mnl > ELD_MAX_MNL) {
+ dev_err_ratelimited(&hdev->dev,
+ "HDMI: MNL Invalid %d\n", mnl);
+ return -EINVAL;
+ }
+
+ port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER];
+
+ return 0;
+}
+
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
+ struct hdac_hdmi_port *port)
+{
+ struct hdac_device *hdev = pin->hdev;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm;
+ int size = 0;
+ int port_id = -1;
+ bool eld_valid, eld_changed;
+
+ if (!hdmi)
+ return;
+
+ /*
+ * In case of non MST pin, get_eld info API expectes port
+ * to be -1.
+ */
+ mutex_lock(&hdmi->pin_mutex);
+ port->eld.monitor_present = false;
+
+ if (pin->mst_capable)
+ port_id = port->id;
+
+ size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id,
+ &port->eld.monitor_present,
+ port->eld.eld_buffer,
+ ELD_MAX_SIZE);
+
+ if (size > 0) {
+ size = min(size, ELD_MAX_SIZE);
+ if (hdac_hdmi_parse_eld(hdev, port) < 0)
+ size = -EINVAL;
+ }
+
+ eld_valid = port->eld.eld_valid;
+
+ if (size > 0) {
+ port->eld.eld_valid = true;
+ port->eld.eld_size = size;
+ } else {
+ port->eld.eld_valid = false;
+ port->eld.eld_size = 0;
+ }
+
+ eld_changed = (eld_valid != port->eld.eld_valid);
+
+ pcm = hdac_hdmi_get_pcm(hdev, port);
+
+ if (!port->eld.monitor_present || !port->eld.eld_valid) {
+
+ dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n",
+ __func__, pin->nid, port->id);
+
+ /*
+ * PCMs are not registered during device probe, so don't
+ * report jack here. It will be done in usermode mux
+ * control select.
+ */
+ if (pcm) {
+ hdac_hdmi_jack_report(pcm, port, false);
+ schedule_work(&port->dapm_work);
+ }
+
+ mutex_unlock(&hdmi->pin_mutex);
+ return;
+ }
+
+ if (port->eld.monitor_present && port->eld.eld_valid) {
+ if (pcm) {
+ hdac_hdmi_jack_report(pcm, port, true);
+ schedule_work(&port->dapm_work);
+ }
+
+ print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1,
+ port->eld.eld_buffer, port->eld.eld_size, false);
+
+ }
+ mutex_unlock(&hdmi->pin_mutex);
+
+ if (eld_changed && pcm)
+ snd_ctl_notify(hdmi->card,
+ SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &pcm->eld_ctl->id);
+}
+
+static int hdac_hdmi_add_ports(struct hdac_device *hdev,
+ struct hdac_hdmi_pin *pin)
+{
+ struct hdac_hdmi_port *ports;
+ int max_ports = HDA_MAX_PORTS;
+ int i;
+
+ /*
+ * FIXME: max_port may vary for each platform, so pass this as
+ * as driver data or query from i915 interface when this API is
+ * implemented.
+ */
+
+ ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ for (i = 0; i < max_ports; i++) {
+ ports[i].id = i;
+ ports[i].pin = pin;
+ INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work);
+ }
+ pin->ports = ports;
+ pin->num_ports = max_ports;
+ return 0;
}
-static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
+static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid)
{
- struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_pin *pin;
+ int ret;
- pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+ pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL);
if (!pin)
return -ENOMEM;
pin->nid = nid;
+ pin->mst_capable = false;
+ pin->hdev = hdev;
+ ret = hdac_hdmi_add_ports(hdev, pin);
+ if (ret < 0)
+ return ret;
list_add_tail(&pin->head, &hdmi->pin_list);
hdmi->num_pin++;
+ hdmi->num_ports += pin->num_ports;
+
+ return 0;
+}
+
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GLK_VENDOR_NID 0x0b
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev)
+{
+ unsigned int vendor_param;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ unsigned int vendor_nid = hdmi->drv_data->vendor_nid;
+
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+ return;
+
+ vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+}
+
+static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
+{
+ unsigned int vendor_param;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ unsigned int vendor_nid = hdmi->drv_data->vendor_nid;
+
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ return;
+
+ /* enable DP1.2 mode */
+ vendor_param |= INTEL_EN_DP12;
+ vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+
+}
+
+static const struct snd_soc_dai_ops hdmi_dai_ops = {
+ .startup = hdac_hdmi_pcm_open,
+ .shutdown = hdac_hdmi_pcm_close,
+ .hw_params = hdac_hdmi_set_hw_params,
+ .set_stream = hdac_hdmi_set_stream,
+};
+
+/*
+ * Each converter can support a stream independently. So a dai is created
+ * based on the number of converter queried.
+ */
+static int hdac_hdmi_create_dais(struct hdac_device *hdev,
+ struct snd_soc_dai_driver **dais,
+ struct hdac_hdmi_priv *hdmi, int num_dais)
+{
+ struct snd_soc_dai_driver *hdmi_dais;
+ struct hdac_hdmi_cvt *cvt;
+ char name[NAME_SIZE], dai_name[NAME_SIZE];
+ int i = 0;
+ u32 rates, bps;
+ unsigned int rate_max = 384000, rate_min = 8000;
+ u64 formats;
+ int ret;
+
+ hdmi_dais = devm_kzalloc(&hdev->dev,
+ (sizeof(*hdmi_dais) * num_dais),
+ GFP_KERNEL);
+ if (!hdmi_dais)
+ return -ENOMEM;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ ret = snd_hdac_query_supported_pcm(hdev, cvt->nid,
+ &rates, &formats, NULL, &bps);
+ if (ret)
+ return ret;
+
+ /* Filter out 44.1, 88.2 and 176.4Khz */
+ rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_176400);
+ if (!rates)
+ return -EINVAL;
+
+ sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
+ hdmi_dais[i].name = devm_kstrdup(&hdev->dev,
+ dai_name, GFP_KERNEL);
+
+ if (!hdmi_dais[i].name)
+ return -ENOMEM;
+
+ snprintf(name, sizeof(name), "hifi%d", i+1);
+ hdmi_dais[i].playback.stream_name =
+ devm_kstrdup(&hdev->dev, name, GFP_KERNEL);
+ if (!hdmi_dais[i].playback.stream_name)
+ return -ENOMEM;
+
+ /*
+ * Set caps based on capability queried from the converter.
+ * It will be constrained runtime based on ELD queried.
+ */
+ hdmi_dais[i].playback.formats = formats;
+ hdmi_dais[i].playback.rates = rates;
+ hdmi_dais[i].playback.rate_max = rate_max;
+ hdmi_dais[i].playback.rate_min = rate_min;
+ hdmi_dais[i].playback.channels_min = 2;
+ hdmi_dais[i].playback.channels_max = 2;
+ hdmi_dais[i].playback.sig_bits = bps;
+ hdmi_dais[i].ops = &hdmi_dai_ops;
+ i++;
+ }
+
+ *dais = hdmi_dais;
+ hdmi->dai_drv = hdmi_dais;
return 0;
}
@@ -431,29 +1517,29 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
* Parse all nodes and store the cvt/pin nids in array
* Add one time initialization for pin and cvt widgets
*/
-static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
+static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev,
+ struct snd_soc_dai_driver **dais, int *num_dais)
{
hda_nid_t nid;
int i, num_nodes;
- struct hdac_device *hdac = &edev->hdac;
- struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
int ret;
- num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
+ hdac_hdmi_skl_enable_all_pins(hdev);
+ hdac_hdmi_skl_enable_dp12(hdev);
+
+ num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid);
if (!nid || num_nodes <= 0) {
- dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
+ dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
- hdac->num_nodes = num_nodes;
- hdac->start_nid = nid;
-
- for (i = 0; i < hdac->num_nodes; i++, nid++) {
+ for (i = 0; i < num_nodes; i++, nid++) {
unsigned int caps;
unsigned int type;
- caps = get_wcaps(hdac, nid);
- type = get_wcaps_type(caps);
+ caps = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
continue;
@@ -461,148 +1547,396 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
switch (type) {
case AC_WID_AUD_OUT:
- ret = hdac_hdmi_add_cvt(edev, nid);
+ ret = hdac_hdmi_add_cvt(hdev, nid);
if (ret < 0)
return ret;
break;
case AC_WID_PIN:
- ret = hdac_hdmi_add_pin(edev, nid);
+ ret = hdac_hdmi_add_pin(hdev, nid);
if (ret < 0)
return ret;
break;
}
}
- hdac->end_nid = nid;
+ if (!hdmi->num_pin || !hdmi->num_cvt) {
+ ret = -EIO;
+ dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__);
+ return ret;
+ }
- if (!hdmi->num_pin || !hdmi->num_cvt)
- return -EIO;
+ ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt);
+ if (ret) {
+ dev_err(&hdev->dev, "Failed to create dais with err: %d\n",
+ ret);
+ return ret;
+ }
+
+ *num_dais = hdmi->num_cvt;
+ ret = hdac_hdmi_init_dai_map(hdev);
+ if (ret < 0)
+ dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n",
+ ret);
+ return ret;
+}
+
+static int hdac_hdmi_pin2port(void *aptr, int pin)
+{
+ return pin - 4; /* map NID 0x05 -> port #1 */
+}
+
+static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
+{
+ struct hdac_device *hdev = aptr;
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin;
+ struct hdac_hdmi_port *hport = NULL;
+ struct snd_soc_component *component = hdmi->component;
+ int i;
+
+ /* Don't know how this mapping is derived */
+ hda_nid_t pin_nid = port + 0x04;
+
+ dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__,
+ pin_nid, pipe);
+
+ /*
+ * skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume. Also since the ELD and
+ * connection states are updated in anyway at the end of the resume,
+ * we can skip it when received during PM process.
+ */
+ if (snd_power_get_state(component->card->snd_card) !=
+ SNDRV_CTL_POWER_D0)
+ return;
+
+ if (atomic_read(&hdev->in_pm))
+ return;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ if (pin->nid != pin_nid)
+ continue;
+
+ /* In case of non MST pin, pipe is -1 */
+ if (pipe == -1) {
+ pin->mst_capable = false;
+ /* if not MST, default is port[0] */
+ hport = &pin->ports[0];
+ } else {
+ for (i = 0; i < pin->num_ports; i++) {
+ pin->mst_capable = true;
+ if (pin->ports[i].id == pipe) {
+ hport = &pin->ports[i];
+ break;
+ }
+ }
+ }
+
+ if (hport)
+ hdac_hdmi_present_sense(pin, hport);
+ }
- return hdac_hdmi_init_dai_map(edev);
}
-static int hdmi_codec_probe(struct snd_soc_codec *codec)
+static struct drm_audio_component_audio_ops aops = {
+ .pin2port = hdac_hdmi_pin2port,
+ .pin_eld_notify = hdac_hdmi_eld_notify_cb,
+};
+
+static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev,
+ struct hdac_hdmi_priv *hdmi, bool detect_pin_caps)
{
- struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
- struct hdac_hdmi_priv *hdmi = edev->private_data;
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(&codec->component);
+ int i;
+ struct hdac_hdmi_pin *pin;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ if (detect_pin_caps) {
+
+ if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0)
+ pin->mst_capable = false;
+ else
+ pin->mst_capable = true;
+ }
+
+ for (i = 0; i < pin->num_ports; i++) {
+ if (!pin->mst_capable && i > 0)
+ continue;
+
+ hdac_hdmi_present_sense(pin, &pin->ports[i]);
+ }
+ }
+}
+
+static int hdmi_codec_probe(struct snd_soc_component *component)
+{
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ hdmi->component = component;
+
+ /*
+ * hold the ref while we probe, also no need to drop the ref on
+ * exit, we call pm_runtime_suspend() so that will do for us
+ */
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
- edev->scodec = codec;
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
- create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
+ ret = create_fill_widget_route_map(dapm);
+ if (ret < 0)
+ return ret;
+
+ aops.audio_ptr = hdev;
+ ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret);
+ return ret;
+ }
+ hdac_hdmi_present_sense_all_pins(hdev, hdmi, true);
/* Imp: Store the card pointer in hda_codec */
- edev->card = dapm->card->snd_card;
+ hdmi->card = component->card->snd_card;
/*
+ * Setup a device_link between card device and HDMI codec device.
+ * The card device is the consumer and the HDMI codec device is
+ * the supplier. With this setting, we can make sure that the audio
+ * domain in display power will be always turned on before operating
+ * on the HDMI audio codec registers.
+ * Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make
+ * sure the device link is freed when the machine driver is removed.
+ */
+ device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE |
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ /*
* hdac_device core already sets the state to active and calls
* get_noresume. So enable runtime and set the device to suspend.
*/
- pm_runtime_enable(&edev->hdac.dev);
- pm_runtime_put(&edev->hdac.dev);
- pm_runtime_suspend(&edev->hdac.dev);
+ pm_runtime_enable(&hdev->dev);
+ pm_runtime_put(&hdev->dev);
+ pm_runtime_suspend(&hdev->dev);
return 0;
}
-static int hdmi_codec_remove(struct snd_soc_codec *codec)
+static void hdmi_codec_remove(struct snd_soc_component *component)
{
- struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+ struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
+ struct hdac_device *hdev = hdmi->hdev;
+ int ret;
+
+ ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL);
+ if (ret < 0)
+ dev_err(&hdev->dev, "notifier unregister failed: err: %d\n",
+ ret);
+
+ pm_runtime_disable(&hdev->dev);
+}
- pm_runtime_disable(&edev->hdac.dev);
+static int hdmi_codec_resume(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+ /*
+ * As the ELD notify callback request is not entertained while the
+ * device is in suspend state. Need to manually check detection of
+ * all pins here. pin capablity change is not support, so use the
+ * already set pin caps.
+ *
+ * NOTE: this is safe to call even if the codec doesn't actually resume.
+ * The pin check involves only with DRM audio component hooks, so it
+ * works even if the HD-audio side is still dreaming peacefully.
+ */
+ hdac_hdmi_present_sense_all_pins(hdev, hdmi, false);
return 0;
}
-static struct snd_soc_codec_driver hdmi_hda_codec = {
- .probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
- .idle_bias_off = true,
+static const struct snd_soc_component_driver hdmi_hda_codec = {
+ .probe = hdmi_codec_probe,
+ .remove = hdmi_codec_remove,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static struct snd_soc_dai_ops hdmi_dai_ops = {
- .startup = hdac_hdmi_pcm_open,
- .shutdown = hdac_hdmi_pcm_close,
- .hw_params = hdac_hdmi_set_hw_params,
- .prepare = hdac_hdmi_playback_prepare,
- .hw_free = hdac_hdmi_playback_cleanup,
+static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx,
+ unsigned char *chmap)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+
+ memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap));
+}
+
+static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx,
+ unsigned char *chmap, int prepared)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+ struct hdac_hdmi_port *port;
+
+ if (!pcm)
+ return;
+
+ if (list_empty(&pcm->port_list))
+ return;
+
+ mutex_lock(&pcm->lock);
+ pcm->chmap_set = true;
+ memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap));
+ list_for_each_entry(port, &pcm->port_list, head)
+ if (prepared)
+ hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
+ mutex_unlock(&pcm->lock);
+}
+
+static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+
+ if (!pcm)
+ return false;
+
+ if (list_empty(&pcm->port_list))
+ return false;
+
+ return true;
+}
+
+static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx)
+{
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+ struct hdac_hdmi_port *port;
+
+ if (!pcm)
+ return 0;
+
+ if (list_empty(&pcm->port_list))
+ return 0;
+
+ port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head);
+
+ if (!port || !port->eld.eld_valid)
+ return 0;
+
+ return port->eld.info.spk_alloc;
+}
+
+static struct hdac_hdmi_drv_data intel_glk_drv_data = {
+ .vendor_nid = INTEL_GLK_VENDOR_NID,
};
-static struct snd_soc_dai_driver hdmi_dais[] = {
- { .name = "intel-hdmi-hif1",
- .playback = {
- .stream_name = "hif1",
- .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_S20_3LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
-
- },
- .ops = &hdmi_dai_ops,
- },
+static struct hdac_hdmi_drv_data intel_drv_data = {
+ .vendor_nid = INTEL_VENDOR_NID,
};
-static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
+static int hdac_hdmi_dev_probe(struct hdac_device *hdev)
{
- struct hdac_device *codec = &edev->hdac;
struct hdac_hdmi_priv *hdmi_priv;
- int ret = 0;
+ struct snd_soc_dai_driver *hdmi_dais = NULL;
+ struct hdac_ext_link *hlink;
+ int num_dais = 0;
+ int ret;
+ struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver);
+ const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv);
+
+ /* hold the ref while we probe */
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "hdac link not found\n");
+ return -EIO;
+ }
+
+ snd_hdac_ext_bus_link_get(hdev->bus, hlink);
- hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
+ hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL);
if (hdmi_priv == NULL)
return -ENOMEM;
- edev->private_data = hdmi_priv;
+ snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap);
+ hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
+ hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
+ hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
+ hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
+ hdmi_priv->hdev = hdev;
- dev_set_drvdata(&codec->dev, edev);
+ if (!hdac_id)
+ return -ENODEV;
+
+ if (hdac_id->driver_data)
+ hdmi_priv->drv_data =
+ (struct hdac_hdmi_drv_data *)hdac_id->driver_data;
+ else
+ hdmi_priv->drv_data = &intel_drv_data;
+
+ dev_set_drvdata(&hdev->dev, hdmi_priv);
INIT_LIST_HEAD(&hdmi_priv->pin_list);
INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+ INIT_LIST_HEAD(&hdmi_priv->pcm_list);
+ mutex_init(&hdmi_priv->pin_mutex);
- ret = hdac_hdmi_parse_and_map_nid(edev);
- if (ret < 0)
+ /*
+ * Turned off in the runtime_suspend during the first explicit
+ * pm_runtime_suspend call.
+ */
+ snd_hdac_display_power(hdev->bus, hdev->addr, true);
+
+ ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais);
+ if (ret < 0) {
+ dev_err(&hdev->dev,
+ "Failed in parse and map nid with err: %d\n", ret);
return ret;
+ }
+ snd_hdac_refresh_widgets(hdev);
/* ASoC specific initialization */
- return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
- hdmi_dais, ARRAY_SIZE(hdmi_dais));
+ ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec,
+ hdmi_dais, num_dais);
+
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ return ret;
}
-static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
+static void clear_dapm_works(struct hdac_device *hdev)
{
- struct hdac_hdmi_priv *hdmi = edev->private_data;
- struct hdac_hdmi_pin *pin, *pin_next;
- struct hdac_hdmi_cvt *cvt, *cvt_next;
-
- snd_soc_unregister_codec(&edev->hdac.dev);
+ struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+ struct hdac_hdmi_pin *pin;
+ int i;
- list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
- list_del(&cvt->head);
- kfree(cvt);
- }
+ list_for_each_entry(pin, &hdmi->pin_list, head)
+ for (i = 0; i < pin->num_ports; i++)
+ cancel_work_sync(&pin->ports[i].dapm_work);
+}
- list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
- list_del(&pin->head);
- kfree(pin);
- }
+static int hdac_hdmi_dev_remove(struct hdac_device *hdev)
+{
+ clear_dapm_works(hdev);
+ snd_hdac_display_power(hdev->bus, hdev->addr, false);
return 0;
}
-#ifdef CONFIG_PM
static int hdac_hdmi_runtime_suspend(struct device *dev)
{
- struct hdac_ext_device *edev = to_hda_ext_device(dev);
- struct hdac_device *hdac = &edev->hdac;
- struct hdac_bus *bus = hdac->bus;
- int err;
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
dev_dbg(dev, "Enter: %s\n", __func__);
@@ -610,26 +1944,35 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
if (!bus)
return 0;
- /* Power down afg */
- if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
- snd_hdac_codec_write(hdac, hdac->afg, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ /*
+ * Power down afg.
+ * codec_read is preferred over codec_write to set the power state.
+ * This way verb is send to set the power state and response
+ * is received. So setting power state is ensured without using loop
+ * to read the state.
+ */
+ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
- err = snd_hdac_display_power(bus, false);
- if (err < 0) {
- dev_err(bus->dev, "Cannot turn on display power on i915\n");
- return err;
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
}
+ snd_hdac_codec_link_down(hdev);
+ snd_hdac_ext_bus_link_put(bus, hlink);
+
+ snd_hdac_display_power(bus, hdev->addr, false);
+
return 0;
}
static int hdac_hdmi_runtime_resume(struct device *dev)
{
- struct hdac_ext_device *edev = to_hda_ext_device(dev);
- struct hdac_device *hdac = &edev->hdac;
- struct hdac_bus *bus = hdac->bus;
- int err;
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
dev_dbg(dev, "Enter: %s\n", __func__);
@@ -637,43 +1980,51 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
if (!bus)
return 0;
- err = snd_hdac_display_power(bus, true);
- if (err < 0) {
- dev_err(bus->dev, "Cannot turn on display power on i915\n");
- return err;
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
}
+ snd_hdac_ext_bus_link_get(bus, hlink);
+ snd_hdac_codec_link_up(hdev);
+
+ snd_hdac_display_power(bus, hdev->addr, true);
+
+ hdac_hdmi_skl_enable_all_pins(hdev);
+ hdac_hdmi_skl_enable_dp12(hdev);
+
/* Power up afg */
- if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
- snd_hdac_codec_write(hdac, hdac->afg, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
return 0;
}
-#else
-#define hdac_hdmi_runtime_suspend NULL
-#define hdac_hdmi_runtime_resume NULL
-#endif
static const struct dev_pm_ops hdac_hdmi_pm = {
- SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
+ RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume)
};
static const struct hda_device_id hdmi_list[] = {
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI",
+ &intel_glk_drv_data),
+ HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI",
+ &intel_glk_drv_data),
{}
};
MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
-static struct hdac_ext_driver hdmi_driver = {
- . hdac = {
- .driver = {
- .name = "HDMI HDA Codec",
- .pm = &hdac_hdmi_pm,
- },
- .id_table = hdmi_list,
+static struct hdac_driver hdmi_driver = {
+ .driver = {
+ .name = "HDMI HDA Codec",
+ .pm = pm_ptr(&hdac_hdmi_pm),
},
+ .id_table = hdmi_list,
.probe = hdac_hdmi_dev_probe,
.remove = hdac_hdmi_dev_remove,
};
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
new file mode 100644
index 000000000000..13ae9e83bc21
--- /dev/null
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -0,0 +1,1212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA SoC codec for HDMI encoder drivers
+ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Jyri Sarha <jsarha@ti.com>
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/hdmi-codec.h>
+#include <sound/pcm_iec958.h>
+
+#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+#include <drm/drm_eld.h>
+
+#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
+
+/*
+ * CEA speaker placement for HDMI 1.4:
+ *
+ * FL FLC FC FRC FR FRW
+ *
+ * LFE
+ *
+ * RL RLC RC RRC RR
+ *
+ * Speaker placement has to be extended to support HDMI 2.0
+ */
+enum hdmi_codec_cea_spk_placement {
+ FL = BIT(0), /* Front Left */
+ FC = BIT(1), /* Front Center */
+ FR = BIT(2), /* Front Right */
+ FLC = BIT(3), /* Front Left Center */
+ FRC = BIT(4), /* Front Right Center */
+ RL = BIT(5), /* Rear Left */
+ RC = BIT(6), /* Rear Center */
+ RR = BIT(7), /* Rear Right */
+ RLC = BIT(8), /* Rear Left Center */
+ RRC = BIT(9), /* Rear Right Center */
+ LFE = BIT(10), /* Low Frequency Effect */
+};
+
+/*
+ * cea Speaker allocation structure
+ */
+struct hdmi_codec_cea_spk_alloc {
+ const int ca_id;
+ unsigned int n_ch;
+ unsigned long mask;
+};
+
+/* Channel maps stereo HDMI */
+static const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { }
+};
+
+/* Channel maps for multi-channel playbacks, up to 8 n_ch */
+static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
+ { .channels = 2, /* CA_ID 0x00 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4, /* CA_ID 0x01 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA } },
+ { .channels = 4, /* CA_ID 0x02 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC } },
+ { .channels = 4, /* CA_ID 0x03 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC } },
+ { .channels = 6, /* CA_ID 0x04 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x05 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x06 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x07 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 6, /* CA_ID 0x08 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6, /* CA_ID 0x09 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6, /* CA_ID 0x0A */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6, /* CA_ID 0x0B */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 8, /* CA_ID 0x0C */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x0D */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x0E */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x0F */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+ { .channels = 8, /* CA_ID 0x10 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x11 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x12 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x13 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+ { .channels = 8, /* CA_ID 0x14 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x15 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x16 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x17 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x18 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x19 */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1A */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1B */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1C */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1D */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1E */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { .channels = 8, /* CA_ID 0x1F */
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+ SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+ { }
+};
+
+/*
+ * hdmi_codec_channel_alloc: speaker configuration available for CEA
+ *
+ * This is an ordered list where ca_id must exist in hdmi_codec_8ch_chmaps
+ * The preceding ones have better chances to be selected by
+ * hdmi_codec_get_ch_alloc_table_idx().
+ */
+static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = {
+ { .ca_id = 0x00, .n_ch = 2,
+ .mask = FL | FR },
+ { .ca_id = 0x03, .n_ch = 4,
+ .mask = FL | FR | LFE | FC },
+ { .ca_id = 0x02, .n_ch = 4,
+ .mask = FL | FR | FC },
+ { .ca_id = 0x01, .n_ch = 4,
+ .mask = FL | FR | LFE },
+ { .ca_id = 0x0b, .n_ch = 6,
+ .mask = FL | FR | LFE | FC | RL | RR },
+ { .ca_id = 0x0a, .n_ch = 6,
+ .mask = FL | FR | FC | RL | RR },
+ { .ca_id = 0x09, .n_ch = 6,
+ .mask = FL | FR | LFE | RL | RR },
+ { .ca_id = 0x08, .n_ch = 6,
+ .mask = FL | FR | RL | RR },
+ { .ca_id = 0x07, .n_ch = 6,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x06, .n_ch = 6,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x05, .n_ch = 6,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x04, .n_ch = 6,
+ .mask = FL | FR | RC },
+ { .ca_id = 0x13, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
+ { .ca_id = 0x1f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
+ { .ca_id = 0x12, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RLC | RRC },
+ { .ca_id = 0x1e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | FLC | FRC },
+ { .ca_id = 0x11, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RLC | RRC },
+ { .ca_id = 0x1d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | FLC | FRC },
+ { .ca_id = 0x10, .n_ch = 8,
+ .mask = FL | FR | RL | RR | RLC | RRC },
+ { .ca_id = 0x1c, .n_ch = 8,
+ .mask = FL | FR | RL | RR | FLC | FRC },
+ { .ca_id = 0x0f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RC },
+ { .ca_id = 0x1b, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FC | FLC | FRC },
+ { .ca_id = 0x0e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RC },
+ { .ca_id = 0x1a, .n_ch = 8,
+ .mask = FL | FR | RC | FC | FLC | FRC },
+ { .ca_id = 0x0d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RC },
+ { .ca_id = 0x19, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FLC | FRC },
+ { .ca_id = 0x0c, .n_ch = 8,
+ .mask = FL | FR | RC | RL | RR },
+ { .ca_id = 0x18, .n_ch = 8,
+ .mask = FL | FR | RC | FLC | FRC },
+ { .ca_id = 0x17, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | FLC | FRC },
+ { .ca_id = 0x16, .n_ch = 8,
+ .mask = FL | FR | FC | FLC | FRC },
+ { .ca_id = 0x15, .n_ch = 8,
+ .mask = FL | FR | LFE | FLC | FRC },
+ { .ca_id = 0x14, .n_ch = 8,
+ .mask = FL | FR | FLC | FRC },
+ { .ca_id = 0x0b, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR },
+ { .ca_id = 0x0a, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR },
+ { .ca_id = 0x09, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR },
+ { .ca_id = 0x08, .n_ch = 8,
+ .mask = FL | FR | RL | RR },
+ { .ca_id = 0x07, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x06, .n_ch = 8,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x05, .n_ch = 8,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x04, .n_ch = 8,
+ .mask = FL | FR | RC },
+ { .ca_id = 0x03, .n_ch = 8,
+ .mask = FL | FR | LFE | FC },
+ { .ca_id = 0x02, .n_ch = 8,
+ .mask = FL | FR | FC },
+ { .ca_id = 0x01, .n_ch = 8,
+ .mask = FL | FR | LFE },
+};
+
+struct hdmi_codec_priv {
+ struct hdmi_codec_pdata hcd;
+ uint8_t eld[MAX_ELD_BYTES];
+ struct snd_parsed_hdmi_eld eld_parsed;
+ struct snd_pcm_chmap *chmap_info;
+ unsigned int chmap_idx;
+ struct mutex lock;
+ bool busy;
+ struct snd_soc_jack *jack;
+ unsigned int jack_status;
+ u8 iec_status[AES_IEC958_STATUS_SIZE];
+ struct snd_info_entry *proc_entry;
+};
+
+static const struct snd_soc_dapm_widget hdmi_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("TX"),
+ SND_SOC_DAPM_OUTPUT("RX"),
+};
+
+enum {
+ DAI_ID_I2S = 0,
+ DAI_ID_SPDIF,
+};
+
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof_field(struct hdmi_codec_priv, eld);
+
+ return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld));
+
+ return 0;
+}
+
+static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc)
+{
+ int i;
+ static const unsigned long hdmi_codec_eld_spk_alloc_bits[] = {
+ [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR,
+ [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC,
+ };
+ unsigned long spk_mask = 0;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= hdmi_codec_eld_spk_alloc_bits[i];
+ }
+
+ return spk_mask;
+}
+
+static void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp)
+{
+ u8 spk_alloc;
+ unsigned long spk_mask;
+
+ spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
+ spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
+
+ /* Detect if only stereo supported, else return 8 channels mappings */
+ if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2)
+ hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps;
+ else
+ hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
+}
+
+static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp,
+ unsigned char channels)
+{
+ int i;
+ u8 spk_alloc;
+ unsigned long spk_mask;
+ const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc;
+
+ spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
+ spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) {
+ /* If spk_alloc == 0, HDMI is unplugged return stereo config*/
+ if (!spk_alloc && cap->ca_id == 0)
+ return i;
+ if (cap->n_ch != channels)
+ continue;
+ if (!(cap->mask == (spk_mask & cap->mask)))
+ continue;
+ return i;
+ }
+
+ return -EINVAL;
+}
+static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned const char *map;
+ unsigned int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = info->private_data;
+
+ if (hcp->chmap_idx != HDMI_CODEC_CHMAP_IDX_UNKNOWN)
+ map = info->chmap[hcp->chmap_idx].map;
+
+ for (i = 0; i < info->max_channels; i++) {
+ if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN)
+ ucontrol->value.integer.value[i] = 0;
+ else
+ ucontrol->value.integer.value[i] = map[i];
+ }
+
+ return 0;
+}
+
+static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.iec958.status, hcp->iec_status,
+ sizeof(hcp->iec_status));
+
+ return 0;
+}
+
+static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ memcpy(hcp->iec_status, ucontrol->value.iec958.status,
+ sizeof(hcp->iec_status));
+
+ return 0;
+}
+
+static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ memset(ucontrol->value.iec958.status, 0xff,
+ sizeof_field(struct hdmi_codec_priv, iec_status));
+
+ return 0;
+}
+
+static int hdmi_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool has_capture = !hcp->hcd.no_i2s_capture;
+ bool has_playback = !hcp->hcd.no_i2s_playback;
+ int ret = 0;
+
+ if (!((has_playback && tx) || (has_capture && !tx)))
+ return 0;
+
+ mutex_lock(&hcp->lock);
+ if (hcp->busy) {
+ dev_err(dai->dev, "Only one simultaneous stream supported!\n");
+ mutex_unlock(&hcp->lock);
+ return -EINVAL;
+ }
+
+ if (hcp->hcd.ops->audio_startup) {
+ ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
+ if (ret)
+ goto err;
+ }
+
+ if (tx && hcp->hcd.ops->get_eld) {
+ ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
+ if (ret)
+ goto err;
+
+ snd_parse_eld(dai->dev, &hcp->eld_parsed,
+ hcp->eld, sizeof(hcp->eld));
+
+ ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld);
+ if (ret)
+ goto err;
+
+ /* Select chmap supported */
+ hdmi_codec_eld_chmap(hcp);
+ }
+
+ hcp->busy = true;
+
+err:
+ mutex_unlock(&hcp->lock);
+ return ret;
+}
+
+static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool has_capture = !hcp->hcd.no_i2s_capture;
+ bool has_playback = !hcp->hcd.no_i2s_playback;
+
+ if (!((has_playback && tx) || (has_capture && !tx)))
+ return;
+
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+ hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
+
+ mutex_lock(&hcp->lock);
+ hcp->busy = false;
+ mutex_unlock(&hcp->lock);
+}
+
+static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
+ unsigned int sample_width,
+ unsigned int sample_rate,
+ unsigned int channels,
+ struct hdmi_codec_params *hp)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+ u8 ca_id = 0;
+ bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO);
+
+ if (pcm_audio) {
+ /* Select a channel allocation that matches with ELD and pcm channels */
+ idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+
+ if (idx < 0) {
+ dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+ idx);
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+ return idx;
+ }
+
+ ca_id = hdmi_codec_channel_alloc[idx].ca_id;
+ }
+
+ memset(hp, 0, sizeof(*hp));
+
+ hdmi_audio_infoframe_init(&hp->cea);
+
+ if (pcm_audio)
+ hp->cea.channels = channels;
+ else
+ hp->cea.channels = 0;
+
+ hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+ hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+ hp->cea.channel_allocation = ca_id;
+
+ hp->sample_width = sample_width;
+ hp->sample_rate = sample_rate;
+ hp->channels = channels;
+
+ if (pcm_audio)
+ hcp->chmap_idx = ca_id;
+ else
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+
+ return 0;
+}
+
+static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
+ struct hdmi_codec_params hp = {
+ .iec = {
+ .status = { 0 },
+ .subcode = { 0 },
+ .pad = 0,
+ .dig_subframe = { 0 },
+ }
+ };
+ int ret;
+
+ if (!hcp->hcd.ops->hw_params)
+ return 0;
+
+ dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+ params_width(params), params_rate(params),
+ params_channels(params));
+
+ ret = hdmi_codec_fill_codec_params(dai,
+ params_width(params),
+ params_rate(params),
+ params_channels(params),
+ &hp);
+ if (ret < 0)
+ return ret;
+
+ memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+ ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
+ sizeof(hp.iec.status));
+ if (ret < 0) {
+ dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+ ret);
+ return ret;
+ }
+
+ cf->bit_fmt = params_format(params);
+ return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
+ cf, &hp);
+}
+
+static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channels = runtime->channels;
+ unsigned int width = snd_pcm_format_width(runtime->format);
+ unsigned int rate = runtime->rate;
+ struct hdmi_codec_params hp;
+ int ret;
+
+ if (!hcp->hcd.ops->prepare)
+ return 0;
+
+ dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+ width, rate, channels);
+
+ ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
+ if (ret < 0)
+ return ret;
+
+ memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+ ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
+ sizeof(hp.iec.status));
+ if (ret < 0) {
+ dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+ ret);
+ return ret;
+ }
+
+ cf->bit_fmt = runtime->format;
+ return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
+ cf, &hp);
+}
+
+static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
+
+ /* Reset daifmt */
+ memset(cf, 0, sizeof(*cf));
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cf->bit_clk_provider = 1;
+ cf->frame_clk_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ cf->frame_clk_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ cf->bit_clk_provider = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cf->frame_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cf->bit_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cf->frame_clk_inv = 1;
+ cf->bit_clk_inv = 1;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cf->fmt = HDMI_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cf->fmt = HDMI_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ cf->fmt = HDMI_DSP_B;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cf->fmt = HDMI_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cf->fmt = HDMI_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_AC97:
+ cf->fmt = HDMI_AC97;
+ break;
+ default:
+ dev_err(dai->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+
+ /*
+ * ignore if direction was CAPTURE
+ * and it had .no_capture_mute flag
+ * see
+ * snd_soc_dai_digital_mute()
+ */
+ if (hcp->hcd.ops->mute_stream &&
+ (direction == SNDRV_PCM_STREAM_PLAYBACK ||
+ !hcp->hcd.no_capture_mute))
+ return hcp->hcd.ops->mute_stream(dai->dev->parent,
+ hcp->hcd.data,
+ mute, direction);
+
+ return -ENOTSUPP;
+}
+
+/*
+ * This driver can select all SND_SOC_DAIFMT_CBx_CFx,
+ * but need to be selected from Sound Card, not be auto selected.
+ * Because it might be used from other driver.
+ * For example,
+ * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
+ */
+static const u64 hdmi_codec_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_AC97;
+
+#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+/*
+ * This list is only for formats allowed on the I2S bus. So there is
+ * some formats listed that are not supported by HDMI interface. For
+ * instance allowing the 32-bit formats enables 24-precision with CPU
+ * DAIs that do not support 24-bit formats. If the extra formats cause
+ * problems, we should add the video side driver an option to disable
+ * them.
+ */
+#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+static struct snd_kcontrol_new hdmi_codec_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .info = hdmi_codec_iec958_info,
+ .get = hdmi_codec_iec958_mask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = hdmi_codec_iec958_info,
+ .get = hdmi_codec_iec958_default_get,
+ .put = hdmi_codec_iec958_default_put,
+ },
+ {
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE),
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdmi_eld_ctl_info,
+ .get = hdmi_eld_ctl_get,
+ },
+};
+
+static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_dai_driver *drv = dai->driver;
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+ unsigned int i;
+ int ret;
+
+ ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, drv->playback.channels_max, 0,
+ &hcp->chmap_info);
+ if (ret < 0)
+ return ret;
+
+ /* override handlers */
+ hcp->chmap_info->private_data = hcp;
+ hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get;
+
+ /* default chmap supported is stereo */
+ hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
+ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
+ struct snd_kcontrol *kctl;
+
+ /* add ELD ctl with the device number corresponding to the PCM stream */
+ kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
+ if (!kctl)
+ return -ENOMEM;
+
+ kctl->id.device = rtd->pcm->device;
+ ret = snd_ctl_add(rtd->card->snd_card, kctl);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_codec_priv *hcp = entry->private_data;
+
+ snd_print_eld_info(&hcp->eld_parsed, buffer);
+}
+
+static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_dai *d;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_info_entry *entry;
+ char name[32];
+ int err, i, id = 0;
+
+ /*
+ * To avoid duplicate proc entry, find its rtd and use rtd->id
+ * instead of dai->id
+ */
+ for_each_card_rtds(card, rtd) {
+ for_each_rtd_dais(rtd, i, d)
+ if (d == dai) {
+ id = rtd->id;
+ goto found;
+ }
+ }
+found:
+ snprintf(name, sizeof(name), "eld#%d", id);
+ err = snd_card_proc_new(card->snd_card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, hcp, print_eld_info);
+ hcp->proc_entry = entry;
+
+ return 0;
+}
+
+static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp)
+{
+ snd_info_free_entry(hcp->proc_entry);
+ hcp->proc_entry = NULL;
+}
+#else
+static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp)
+{
+}
+#endif
+
+static int hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp =
+ snd_soc_component_get_drvdata(dai->component);
+ struct snd_soc_dapm_context *dapm;
+ struct hdmi_codec_daifmt *daifmt;
+ struct snd_soc_dapm_route route[] = {
+ {
+ .sink = "TX",
+ .source = dai->driver->playback.stream_name,
+ },
+ {
+ .sink = dai->driver->capture.stream_name,
+ .source = "RX",
+ },
+ };
+ int ret, i;
+
+ dapm = snd_soc_component_to_dapm(dai->component);
+
+ /* One of the directions might be omitted for unidirectional DAIs */
+ for (i = 0; i < ARRAY_SIZE(route); i++) {
+ if (!route[i].source || !route[i].sink)
+ continue;
+
+ ret = snd_soc_dapm_add_routes(dapm, &route[i], 1);
+ if (ret)
+ return ret;
+ }
+
+ daifmt = devm_kzalloc(dai->dev, sizeof(*daifmt), GFP_KERNEL);
+ if (!daifmt)
+ return -ENOMEM;
+
+ snd_soc_dai_dma_data_set_playback(dai, daifmt);
+
+ return hdmi_dai_proc_new(hcp, dai);
+}
+
+static int hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp =
+ snd_soc_component_get_drvdata(dai->component);
+
+ hdmi_dai_proc_free(hcp);
+ return 0;
+}
+
+static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp,
+ unsigned int jack_status)
+{
+ if (jack_status != hcp->jack_status) {
+ if (hcp->jack)
+ snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_AVOUT);
+ hcp->jack_status = jack_status;
+ }
+}
+
+static void plugged_cb(struct device *dev, bool plugged)
+{
+ struct hdmi_codec_priv *hcp = dev_get_drvdata(dev);
+ int ret;
+
+ if (plugged) {
+ if (hcp->hcd.ops->get_eld) {
+ hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
+ ret = snd_parse_eld(dev, &hcp->eld_parsed,
+ hcp->eld, sizeof(hcp->eld));
+ if (ret < 0)
+ dev_dbg(dev, "Failed to parse ELD: %d\n", ret);
+ else
+ snd_show_eld(dev, &hcp->eld_parsed);
+ }
+ hdmi_codec_jack_report(hcp, SND_JACK_AVOUT);
+ } else {
+ hdmi_codec_jack_report(hcp, 0);
+ memset(hcp->eld, 0, sizeof(hcp->eld));
+ }
+}
+
+static int hdmi_codec_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack,
+ void *data)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ if (hcp->hcd.ops->hook_plugged_cb) {
+ hcp->jack = jack;
+
+ /*
+ * Report the initial jack status which may have been provided
+ * by the parent hdmi driver while the hpd hook was registered.
+ */
+ snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_AVOUT);
+
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_daifmt *cf;
+ int ret;
+
+ ret = hdmi_dai_probe(dai);
+ if (ret)
+ return ret;
+
+ cf = snd_soc_dai_dma_data_get_playback(dai);
+ cf->fmt = HDMI_SPDIF;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
+ .probe = hdmi_dai_probe,
+ .remove = hdmi_dai_remove,
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .prepare = hdmi_codec_prepare,
+ .set_fmt = hdmi_codec_i2s_set_fmt,
+ .mute_stream = hdmi_codec_mute,
+ .pcm_new = hdmi_codec_pcm_new,
+ .auto_selectable_formats = &hdmi_codec_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
+ .probe = hdmi_dai_spdif_probe,
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .prepare = hdmi_codec_prepare,
+ .mute_stream = hdmi_codec_mute,
+ .pcm_new = hdmi_codec_pcm_new,
+};
+
+static const struct snd_soc_dai_driver hdmi_i2s_dai = {
+ .name = "i2s-hifi",
+ .id = DAI_ID_I2S,
+ .playback = {
+ .stream_name = "I2S Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = HDMI_RATES,
+ .formats = I2S_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = HDMI_RATES,
+ .formats = I2S_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &hdmi_codec_i2s_dai_ops,
+};
+
+static const struct snd_soc_dai_driver hdmi_spdif_dai = {
+ .name = "spdif-hifi",
+ .id = DAI_ID_SPDIF,
+ .playback = {
+ .stream_name = "SPDIF Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = HDMI_RATES,
+ .formats = SPDIF_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = HDMI_RATES,
+ .formats = SPDIF_FORMATS,
+ },
+ .ops = &hdmi_codec_spdif_dai_ops,
+};
+
+static int hdmi_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+ int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */
+
+ if (hcp->hcd.ops->get_dai_id)
+ ret = hcp->hcd.ops->get_dai_id(component, endpoint, hcp->hcd.data);
+
+ return ret;
+}
+
+static int hdmi_probe(struct snd_soc_component *component)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if (hcp->hcd.ops->hook_plugged_cb) {
+ ret = hcp->hcd.ops->hook_plugged_cb(component->dev->parent,
+ hcp->hcd.data,
+ plugged_cb,
+ component->dev);
+ }
+
+ return ret;
+}
+
+static void hdmi_remove(struct snd_soc_component *component)
+{
+ struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+ if (hcp->hcd.ops->hook_plugged_cb)
+ hcp->hcd.ops->hook_plugged_cb(component->dev->parent,
+ hcp->hcd.data, NULL, NULL);
+}
+
+static const struct snd_soc_component_driver hdmi_driver = {
+ .probe = hdmi_probe,
+ .remove = hdmi_remove,
+ .dapm_widgets = hdmi_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
+ .of_xlate_dai_id = hdmi_of_xlate_dai_id,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .set_jack = hdmi_codec_set_jack,
+};
+
+static int hdmi_codec_probe(struct platform_device *pdev)
+{
+ struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
+ struct snd_soc_dai_driver *daidrv;
+ struct device *dev = &pdev->dev;
+ struct hdmi_codec_priv *hcp;
+ int dai_count, i = 0;
+ int ret;
+
+ if (!hcd) {
+ dev_err(dev, "%s: No platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ dai_count = hcd->i2s + hcd->spdif;
+ if (dai_count < 1 || !hcd->ops ||
+ (!hcd->ops->hw_params && !hcd->ops->prepare) ||
+ !hcd->ops->audio_shutdown) {
+ dev_err(dev, "%s: Invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
+ if (!hcp)
+ return -ENOMEM;
+
+ hcp->hcd = *hcd;
+ mutex_init(&hcp->lock);
+
+ ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status,
+ sizeof(hcp->iec_status));
+ if (ret < 0)
+ return ret;
+
+ daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
+ if (!daidrv)
+ return -ENOMEM;
+
+ if (hcd->i2s) {
+ daidrv[i] = hdmi_i2s_dai;
+ daidrv[i].playback.channels_max = hcd->max_i2s_channels;
+ if (hcd->i2s_formats) {
+ daidrv[i].playback.formats = hcd->i2s_formats;
+ daidrv[i].capture.formats = hcd->i2s_formats;
+ }
+ if (hcd->no_i2s_playback)
+ memset(&daidrv[i].playback, 0,
+ sizeof(daidrv[i].playback));
+ if (hcd->no_i2s_capture)
+ memset(&daidrv[i].capture, 0,
+ sizeof(daidrv[i].capture));
+ i++;
+ }
+
+ if (hcd->spdif) {
+ daidrv[i] = hdmi_spdif_dai;
+ if (hcd->no_spdif_playback)
+ memset(&daidrv[i].playback, 0,
+ sizeof(daidrv[i].playback));
+ if (hcd->no_spdif_capture)
+ memset(&daidrv[i].capture, 0,
+ sizeof(daidrv[i].capture));
+ }
+
+ dev_set_drvdata(dev, hcp);
+
+ ret = devm_snd_soc_register_component(dev, &hdmi_driver, daidrv,
+ dai_count);
+ if (ret) {
+ dev_err(dev, "%s: snd_soc_register_component() failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static struct platform_driver hdmi_codec_driver = {
+ .driver = {
+ .name = HDMI_CODEC_DRV_NAME,
+ },
+ .probe = hdmi_codec_probe,
+};
+
+module_platform_driver(hdmi_codec_driver);
+
+MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
+MODULE_DESCRIPTION("HDMI Audio Codec Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c
index dd850b93938d..58a382254718 100644
--- a/sound/soc/codecs/ics43432.c
+++ b/sound/soc/codecs/ics43432.c
@@ -1,12 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * I2S MEMS microphone driver for InvenSense ICS-43432
+ * I2S MEMS microphone driver for InvenSense ICS-43432 and similar
+ * MEMS-based microphones.
*
* - Non configurable.
* - I2S interface, 64 BCLs per frame, 32 bits per channel, 24 bit data
*
* Copyright (c) 2015 Axis Communications AB
- *
- * Licensed under GPL v2.
*/
#include <linux/module.h>
@@ -37,24 +37,23 @@ static struct snd_soc_dai_driver ics43432_dai = {
},
};
-static struct snd_soc_codec_driver ics43432_codec_driver = {
+static const struct snd_soc_component_driver ics43432_component_driver = {
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int ics43432_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &ics43432_codec_driver,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &ics43432_component_driver,
&ics43432_dai, 1);
}
-static int ics43432_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id ics43432_ids[] = {
{ .compatible = "invensense,ics43432", },
+ { .compatible = "cui,cmm-4030d-261", },
{ }
};
MODULE_DEVICE_TABLE(of, ics43432_ids);
@@ -66,7 +65,6 @@ static struct platform_driver ics43432_driver = {
.of_match_table = of_match_ptr(ics43432_ids),
},
.probe = ics43432_probe,
- .remove = ics43432_remove,
};
module_platform_driver(ics43432_driver);
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
new file mode 100644
index 000000000000..39bafefa6a18
--- /dev/null
+++ b/sound/soc/codecs/idt821034.c
@@ -0,0 +1,1183 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// IDT821034 ALSA SoC driver
+//
+// Copyright 2022 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/bitrev.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define IDT821034_NB_CHANNEL 4
+
+struct idt821034_amp {
+ u16 gain;
+ bool is_muted;
+};
+
+struct idt821034 {
+ struct spi_device *spi;
+ struct mutex mutex;
+ u8 spi_tx_buf; /* Cannot use stack area for SPI (dma-safe memory) */
+ u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */
+ struct {
+ u8 codec_conf;
+ struct {
+ u8 power;
+ u8 tx_slot;
+ u8 rx_slot;
+ u8 slic_conf;
+ u8 slic_control;
+ } ch[IDT821034_NB_CHANNEL];
+ } cache;
+ struct {
+ struct {
+ struct idt821034_amp amp_out;
+ struct idt821034_amp amp_in;
+ } ch[IDT821034_NB_CHANNEL];
+ } amps;
+ int max_ch_playback;
+ int max_ch_capture;
+ struct gpio_chip gpio_chip;
+};
+
+static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val)
+{
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }, {
+ .cs_off = 1,
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }
+ };
+
+ idt821034->spi_tx_buf = val;
+
+ dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val);
+
+ return spi_sync_transfer(idt821034->spi, xfer, 2);
+}
+
+static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2)
+{
+ int ret;
+
+ ret = idt821034_8bit_write(idt821034, val1);
+ if (ret)
+ return ret;
+ return idt821034_8bit_write(idt821034, val2);
+}
+
+static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr)
+{
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &idt821034->spi_tx_buf,
+ .rx_buf = &idt821034->spi_rx_buf,
+ .len = 1,
+ }, {
+ .cs_off = 1,
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }
+ };
+ int ret;
+
+ idt821034->spi_tx_buf = valw;
+
+ ret = spi_sync_transfer(idt821034->spi, xfer, 2);
+ if (ret)
+ return ret;
+
+ *valr = idt821034->spi_rx_buf;
+
+ dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n",
+ valw, *valr);
+
+ return 0;
+}
+
+/* Available mode for the programming sequence */
+#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2))
+#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2))
+#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2))
+
+/* Power values that can be used in 'power' (can be ORed) */
+#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */
+#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */
+
+static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power);
+
+ conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
+
+ if (power & IDT821034_CONF_PWRUP_RX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_RX,
+ idt821034->cache.ch[ch].rx_slot);
+ if (ret)
+ return ret;
+ }
+ if (power & IDT821034_CONF_PWRUP_TX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_TX,
+ idt821034->cache.ch[ch].tx_slot);
+ if (ret)
+ return ret;
+ }
+ if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) {
+ ret = idt821034_2x8bit_write(idt821034, conf, 0);
+ if (ret)
+ return ret;
+ }
+
+ idt821034->cache.ch[ch].power = power;
+
+ return 0;
+}
+
+static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].power;
+}
+
+/* Codec configuration values that can be used in 'codec_conf' (can be ORed) */
+#define IDT821034_CONF_ALAW_MODE BIT(5)
+#define IDT821034_CONF_DELAY_MODE BIT(4)
+
+static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf)
+{
+ u8 conf;
+ u8 ts;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf);
+
+ /* codec conf fields are common to all channel.
+ * Arbitrary use of channel 0 for this configuration.
+ */
+
+ /* Set Configuration Register */
+ conf = IDT821034_MODE_CODEC(0) | codec_conf;
+
+ /* Update conf value and timeslot register value according
+ * to cache values
+ */
+ if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) {
+ conf |= IDT821034_CONF_PWRUP_RX;
+ ts = idt821034->cache.ch[0].rx_slot;
+ } else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) {
+ conf |= IDT821034_CONF_PWRUP_TX;
+ ts = idt821034->cache.ch[0].tx_slot;
+ } else {
+ ts = 0x00;
+ }
+
+ /* Write configuration register and time-slot register */
+ ret = idt821034_2x8bit_write(idt821034, conf, ts);
+ if (ret)
+ return ret;
+
+ idt821034->cache.codec_conf = codec_conf;
+ return 0;
+}
+
+static u8 idt821034_get_codec_conf(struct idt821034 *idt821034)
+{
+ return idt821034->cache.codec_conf;
+}
+
+/* Channel direction values that can be used in 'ch_dir' (can be ORed) */
+#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */
+#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */
+
+static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num);
+
+ conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
+
+ if (ch_dir & IDT821034_CH_RX) {
+ if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_RX,
+ ts_num);
+ if (ret)
+ return ret;
+ }
+ idt821034->cache.ch[ch].rx_slot = ts_num;
+ }
+ if (ch_dir & IDT821034_CH_TX) {
+ if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_TX,
+ ts_num);
+ if (ret)
+ return ret;
+ }
+ idt821034->cache.ch[ch].tx_slot = ts_num;
+ }
+
+ return 0;
+}
+
+/* SLIC direction values that can be used in 'slic_dir' (can be ORed) */
+#define IDT821034_SLIC_IO1_IN BIT(1)
+#define IDT821034_SLIC_IO0_IN BIT(0)
+
+static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir);
+
+ conf = IDT821034_MODE_SLIC(ch) | slic_dir;
+ ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control);
+ if (ret)
+ return ret;
+
+ idt821034->cache.ch[ch].slic_conf = slic_dir;
+
+ return 0;
+}
+
+static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].slic_conf;
+}
+
+static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw);
+
+ /*
+ * On write, slic_raw is mapped as follow :
+ * b4: O_4
+ * b3: O_3
+ * b2: O_2
+ * b1: I/O_1
+ * b0: I/O_0
+ */
+
+ conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
+ ret = idt821034_2x8bit_write(idt821034, conf, slic_raw);
+ if (ret)
+ return ret;
+
+ idt821034->cache.ch[ch].slic_control = slic_raw;
+ return 0;
+}
+
+static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].slic_control;
+}
+
+static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw)
+{
+ u8 val;
+ int ret;
+
+ /*
+ * On read, slic_raw is mapped as follow :
+ * b7: I/O_0
+ * b6: I/O_1
+ * b5: O_2
+ * b4: O_3
+ * b3: O_4
+ * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value)
+ * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value)
+ * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value)
+ */
+
+ val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
+ ret = idt821034_8bit_write(idt821034, val);
+ if (ret)
+ return ret;
+
+ ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw);
+ if (ret)
+ return ret;
+
+ dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw);
+
+ return 0;
+}
+
+/* Gain type values that can be used in 'gain_type' (cannot be ORed) */
+#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */
+#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */
+
+static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch,
+ u8 gain_type, u16 gain_val)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n",
+ ch, gain_type, gain_val, gain_val);
+
+ /*
+ * The gain programming coefficients should be calculated as:
+ * Transmit : Coeff_X = round [ gain_X0dB × gain_X ]
+ * Receive: Coeff_R = round [ gain_R0dB × gain_R ]
+ * where:
+ * gain_X0dB = 1820;
+ * gain_X is the target gain;
+ * Coeff_X should be in the range of 0 to 8192.
+ * gain_R0dB = 2506;
+ * gain_R is the target gain;
+ * Coeff_R should be in the range of 0 to 8192.
+ *
+ * A gain programming coefficient is 14-bit wide and in binary format.
+ * The 7 Most Significant Bits of the coefficient is called
+ * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for
+ * receive path; The 7 Least Significant Bits of the coefficient is
+ * called GA_LSB_ Transmit for transmit path, or is called
+ * GA_LSB_Receive for receive path.
+ *
+ * An example is given below to clarify the calculation of the
+ * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB
+ * gain in receive path:
+ *
+ * Linear Code of +3dB = 10^(3/20)= 1.412537545
+ * Coeff_X = round (1820 × 1.412537545) = 2571
+ * = 0b001010_00001011
+ * GA_MSB_Transmit = 0b0010100
+ * GA_LSB_Transmit = 0b0001011
+ *
+ * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917
+ * Coeff_R= round (2506 × 0.668343917) = 1675
+ * = 0b0001101_0001011
+ * GA_MSB_Receive = 0b0001101
+ * GA_LSB_Receive = 0b0001011
+ */
+
+ conf = IDT821034_MODE_GAIN(ch) | gain_type;
+
+ ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F);
+ if (ret)
+ return ret;
+
+ ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Id helpers used in controls and dapm */
+#define IDT821034_DIR_OUT (1 << 3)
+#define IDT821034_DIR_IN (0 << 3)
+#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir))
+#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT)
+#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN)
+
+#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03)
+#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3))
+#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT)
+
+static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int val;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(mc->reg);
+
+ mutex_lock(&idt821034->mutex);
+ if (IDT821034_ID_IS_OUT(mc->reg))
+ val = idt821034->amps.ch[ch].amp_out.gain;
+ else
+ val = idt821034->amps.ch[ch].amp_in.gain;
+ mutex_unlock(&idt821034->mutex);
+
+ ucontrol->value.integer.value[0] = val & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
+ else
+ ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min;
+
+ return 0;
+}
+
+static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ struct idt821034_amp *amp;
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret;
+ u8 gain_type;
+ u8 ch;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > max - min)
+ return -EINVAL;
+
+ if (invert)
+ val = (max - val) & mask;
+ else
+ val = (val + min) & mask;
+
+ ch = IDT821034_ID_GET_CHAN(mc->reg);
+
+ mutex_lock(&idt821034->mutex);
+
+ if (IDT821034_ID_IS_OUT(mc->reg)) {
+ amp = &idt821034->amps.ch[ch].amp_out;
+ gain_type = IDT821034_GAIN_RX;
+ } else {
+ amp = &idt821034->amps.ch[ch].amp_in;
+ gain_type = IDT821034_GAIN_TX;
+ }
+
+ if (amp->gain == val) {
+ ret = 0;
+ goto end;
+ }
+
+ if (!amp->is_muted) {
+ ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val);
+ if (ret)
+ goto end;
+ }
+
+ amp->gain = val;
+ ret = 1; /* The value changed */
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int id = kcontrol->private_value;
+ bool is_muted;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+
+ mutex_lock(&idt821034->mutex);
+ is_muted = IDT821034_ID_IS_OUT(id) ?
+ idt821034->amps.ch[ch].amp_out.is_muted :
+ idt821034->amps.ch[ch].amp_in.is_muted;
+ mutex_unlock(&idt821034->mutex);
+
+ ucontrol->value.integer.value[0] = !is_muted;
+
+ return 0;
+}
+
+static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int id = kcontrol->private_value;
+ struct idt821034_amp *amp;
+ bool is_mute;
+ u8 gain_type;
+ int ret;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+ is_mute = !ucontrol->value.integer.value[0];
+
+ mutex_lock(&idt821034->mutex);
+
+ if (IDT821034_ID_IS_OUT(id)) {
+ amp = &idt821034->amps.ch[ch].amp_out;
+ gain_type = IDT821034_GAIN_RX;
+ } else {
+ amp = &idt821034->amps.ch[ch].amp_in;
+ gain_type = IDT821034_GAIN_TX;
+ }
+
+ if (amp->is_muted == is_mute) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = idt821034_set_gain_channel(idt821034, ch, gain_type,
+ is_mute ? 0 : amp->gain);
+ if (ret)
+ goto end;
+
+ amp->is_muted = is_mute;
+ ret = 1; /* The value changed */
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300);
+#define IDT821034_GAIN_IN_MIN_RAW 1288 /* -3.0 dB -> 10^(-3.0/20.0) * 1820 = 1288 */
+#define IDT821034_GAIN_IN_MAX_RAW 8130 /* 13.0 dB -> 10^(13.0/20.0) * 1820 = 8130 */
+#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */
+
+static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -1300, 300);
+#define IDT821034_GAIN_OUT_MIN_RAW 561 /* -13.0 dB -> 10^(-13.0/20.0) * 2506 = 561 */
+#define IDT821034_GAIN_OUT_MAX_RAW 3540 /* 3.0 dB -> 10^(3.0/20.0) * 2506 = 3540 */
+#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */
+
+static const struct snd_kcontrol_new idt821034_controls[] = {
+ /* DAC volume control */
+ SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+
+ /* DAC mute control */
+ SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+
+ /* ADC volume control */
+ SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+
+ /* ADC mute control */
+ SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+};
+
+static int idt821034_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ unsigned int id = w->shift;
+ u8 power, mask;
+ int ret;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+ mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX;
+
+ mutex_lock(&idt821034->mutex);
+
+ power = idt821034_get_channel_power(idt821034, ch);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ power |= mask;
+ else
+ power &= ~mask;
+ ret = idt821034_set_channel_power(idt821034, ch, power);
+
+ mutex_unlock(&idt821034->mutex);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+};
+
+static const struct snd_soc_dapm_route idt821034_dapm_routes[] = {
+ { "OUT0", NULL, "DAC0" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+
+ { "ADC0", NULL, "IN0" },
+ { "ADC1", NULL, "IN1" },
+ { "ADC2", NULL, "IN2" },
+ { "ADC3", NULL, "IN3" },
+};
+
+static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int width)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int mask;
+ u8 slot;
+ int ret;
+ u8 ch;
+
+ switch (width) {
+ case 0: /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ mask = tx_mask;
+ slot = 0;
+ ch = 0;
+ while (mask && ch < IDT821034_NB_CHANNEL) {
+ if (mask & 0x1) {
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n",
+ ch, ret);
+ return ret;
+ }
+ ch++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
+ tx_mask, IDT821034_NB_CHANNEL);
+ return -EINVAL;
+ }
+ idt821034->max_ch_playback = ch;
+
+ mask = rx_mask;
+ slot = 0;
+ ch = 0;
+ while (mask && ch < IDT821034_NB_CHANNEL) {
+ if (mask & 0x1) {
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n",
+ ch, ret);
+ return ret;
+ }
+ ch++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
+ rx_mask, IDT821034_NB_CHANNEL);
+ return -EINVAL;
+ }
+ idt821034->max_ch_capture = ch;
+
+ return 0;
+}
+
+static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ u8 conf;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ conf = idt821034_get_codec_conf(idt821034);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ conf |= IDT821034_CONF_DELAY_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ conf &= ~IDT821034_CONF_DELAY_MODE;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = idt821034_set_codec_conf(idt821034, conf);
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ u8 conf;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ conf = idt821034_get_codec_conf(idt821034);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_A_LAW:
+ conf |= IDT821034_CONF_ALAW_MODE;
+ break;
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ conf &= ~IDT821034_CONF_ALAW_MODE;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported PCM format 0x%x\n",
+ params_format(params));
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = idt821034_set_codec_conf(idt821034, conf);
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static const unsigned int idt821034_sample_bits[] = {8};
+
+static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = {
+ .list = idt821034_sample_bits,
+ .count = ARRAY_SIZE(idt821034_sample_bits),
+};
+
+static int idt821034_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_ch = 0;
+ int ret;
+
+ max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ idt821034->max_ch_playback : idt821034->max_ch_capture;
+
+ /*
+ * Disable stream support (min = 0, max = 0) if no timeslots were
+ * configured otherwise, limit the number of channels to those
+ * configured.
+ */
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ max_ch ? 1 : 0, max_ch);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &idt821034_sample_bits_constr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u64 idt821034_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops idt821034_dai_ops = {
+ .startup = idt821034_dai_startup,
+ .hw_params = idt821034_dai_hw_params,
+ .set_tdm_slot = idt821034_dai_set_tdm_slot,
+ .set_fmt = idt821034_dai_set_fmt,
+ .auto_selectable_formats = idt821034_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats),
+};
+
+static struct snd_soc_dai_driver idt821034_dai_driver = {
+ .name = "idt821034",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = IDT821034_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = IDT821034_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .ops = &idt821034_dai_ops,
+};
+
+static int idt821034_reset_audio(struct idt821034 *idt821034)
+{
+ int ret;
+ u8 i;
+
+ mutex_lock(&idt821034->mutex);
+
+ ret = idt821034_set_codec_conf(idt821034, 0);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
+ idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW;
+ idt821034->amps.ch[i].amp_out.is_muted = false;
+ ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX,
+ idt821034->amps.ch[i].amp_out.gain);
+ if (ret)
+ goto end;
+
+ idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW;
+ idt821034->amps.ch[i].amp_in.is_muted = false;
+ ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX,
+ idt821034->amps.ch[i].amp_in.gain);
+ if (ret)
+ goto end;
+
+ ret = idt821034_set_channel_power(idt821034, i, 0);
+ if (ret)
+ goto end;
+ }
+
+ ret = 0;
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_component_probe(struct snd_soc_component *component)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* reset idt821034 audio part*/
+ ret = idt821034_reset_audio(idt821034);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver idt821034_component_driver = {
+ .probe = idt821034_component_probe,
+ .controls = idt821034_controls,
+ .num_controls = ARRAY_SIZE(idt821034_controls),
+ .dapm_widgets = idt821034_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets),
+ .dapm_routes = idt821034_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes),
+ .endianness = 1,
+};
+
+#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4)
+#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5)
+
+static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset,
+ int val)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_raw;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_raw = idt821034_get_written_slic_raw(idt821034, ch);
+ if (val)
+ slic_raw |= mask;
+ else
+ slic_raw &= ~mask;
+ ret = idt821034_write_slic_raw(idt821034, ch, slic_raw);
+
+ mutex_unlock(&idt821034->mutex);
+
+ if (ret)
+ dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+
+ return ret;
+}
+
+static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_raw;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ return ret;
+ }
+
+ /*
+ * SLIC IOs are read in reverse order compared to write.
+ * Reverse the read value here in order to have IO0 at lsb (ie same
+ * order as write)
+ */
+ return !!(bitrev8(slic_raw) & mask);
+}
+
+static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_dir;
+
+ mutex_lock(&idt821034->mutex);
+ slic_dir = idt821034_get_slic_conf(idt821034, ch);
+ mutex_unlock(&idt821034->mutex);
+
+ return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
+}
+
+static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_conf;
+ int ret;
+
+ /* Only IO0 and IO1 can be set as input */
+ if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN))
+ return -EPERM;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask;
+
+ ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_conf;
+ int ret;
+
+ ret = idt821034_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask;
+
+ ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "dir out gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_reset_gpio(struct idt821034 *idt821034)
+{
+ int ret;
+ u8 i;
+
+ mutex_lock(&idt821034->mutex);
+
+ /* IO0 and IO1 as input for all channels and output IO set to 0 */
+ for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
+ ret = idt821034_set_slic_conf(idt821034, i,
+ IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN);
+ if (ret)
+ goto end;
+
+ ret = idt821034_write_slic_raw(idt821034, i, 0);
+ if (ret)
+ goto end;
+
+ }
+ ret = 0;
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_gpio_init(struct idt821034 *idt821034)
+{
+ int ret;
+
+ ret = idt821034_reset_gpio(idt821034);
+ if (ret)
+ return ret;
+
+ idt821034->gpio_chip.owner = THIS_MODULE;
+ idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev);
+ idt821034->gpio_chip.parent = &idt821034->spi->dev;
+ idt821034->gpio_chip.base = -1;
+ idt821034->gpio_chip.ngpio = 5 * 4; /* 5 GPIOs on 4 channels */
+ idt821034->gpio_chip.get_direction = idt821034_chip_get_direction;
+ idt821034->gpio_chip.direction_input = idt821034_chip_direction_input;
+ idt821034->gpio_chip.direction_output = idt821034_chip_direction_output;
+ idt821034->gpio_chip.get = idt821034_chip_gpio_get;
+ idt821034->gpio_chip.set = idt821034_chip_gpio_set;
+ idt821034->gpio_chip.can_sleep = true;
+
+ return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip,
+ idt821034);
+}
+
+static int idt821034_spi_probe(struct spi_device *spi)
+{
+ struct idt821034 *idt821034;
+ int ret;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL);
+ if (!idt821034)
+ return -ENOMEM;
+
+ idt821034->spi = spi;
+
+ mutex_init(&idt821034->mutex);
+
+ spi_set_drvdata(spi, idt821034);
+
+ ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver,
+ &idt821034_dai_driver, 1);
+ if (ret)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB))
+ return idt821034_gpio_init(idt821034);
+
+ return 0;
+}
+
+static const struct of_device_id idt821034_of_match[] = {
+ { .compatible = "renesas,idt821034", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, idt821034_of_match);
+
+static const struct spi_device_id idt821034_id_table[] = {
+ { "idt821034", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, idt821034_id_table);
+
+static struct spi_driver idt821034_spi_driver = {
+ .driver = {
+ .name = "idt821034",
+ .of_match_table = idt821034_of_match,
+ },
+ .id_table = idt821034_id_table,
+ .probe = idt821034_spi_probe,
+};
+
+module_spi_driver(idt821034_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("IDT821034 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c
index 9b6e8840a1b5..fdd19f8e8864 100644
--- a/sound/soc/codecs/inno_rk3036.c
+++ b/sound/soc/codecs/inno_rk3036.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver of Inno codec for rk3036 by Rockchip Inc.
*
@@ -47,11 +48,9 @@ static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- int val, ret, regval;
+ int val, regval;
- ret = snd_soc_component_read(component, INNO_R09, &regval);
- if (ret)
- return ret;
+ regval = snd_soc_component_read(component, INNO_R09);
val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
ucontrol->value.integer.value[0] = val;
@@ -196,22 +195,22 @@ static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
- dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
+ dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
INNO_R01_I2SMODE_SLAVE;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
INNO_R01_I2SMODE_MASTER;
break;
default:
- dev_err(codec->dev, "invalid fmt\n");
+ dev_err(component->dev, "invalid fmt\n");
return -EINVAL;
}
@@ -229,7 +228,7 @@ static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
reg02_val |= INNO_R02_DACM_LJM;
break;
default:
- dev_err(codec->dev, "set dai format failed\n");
+ dev_err(component->dev, "set dai format failed\n");
return -EINVAL;
}
@@ -251,15 +250,15 @@ static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
reg03_val |= INNO_R03_BCP_REVERSAL;
break;
default:
- dev_err(codec->dev, "set dai format failed\n");
+ dev_err(component->dev, "set dai format failed\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
+ snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK |
INNO_R01_PINDIR_MSK, reg01_val);
- snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
+ snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_DACM_MSK, reg02_val);
- snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
+ snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
return 0;
}
@@ -268,7 +267,7 @@ static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int reg02_val = 0, reg03_val = 0;
switch (params_format(hw_params)) {
@@ -291,9 +290,9 @@ static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
- snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
+ snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_VWL_MSK, reg02_val);
- snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
+ snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK |
INNO_R03_FWL_MSK, reg03_val);
return 0;
}
@@ -310,7 +309,7 @@ static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops rk3036_codec_dai_ops = {
+static const struct snd_soc_dai_ops rk3036_codec_dai_ops = {
.set_fmt = rk3036_codec_dai_set_fmt,
.hw_params = rk3036_codec_dai_hw_params,
};
@@ -326,47 +325,46 @@ static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
.formats = RK3036_CODEC_FMTS,
},
.ops = &rk3036_codec_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static void rk3036_codec_reset(struct snd_soc_codec *codec)
+static void rk3036_codec_reset(struct snd_soc_component *component)
{
- snd_soc_write(codec, INNO_R00,
+ snd_soc_component_write(component, INNO_R00,
INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
- snd_soc_write(codec, INNO_R00,
+ snd_soc_component_write(component, INNO_R00,
INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
}
-static int rk3036_codec_probe(struct snd_soc_codec *codec)
+static int rk3036_codec_probe(struct snd_soc_component *component)
{
- rk3036_codec_reset(codec);
+ rk3036_codec_reset(component);
return 0;
}
-static int rk3036_codec_remove(struct snd_soc_codec *codec)
+static void rk3036_codec_remove(struct snd_soc_component *component)
{
- rk3036_codec_reset(codec);
- return 0;
+ rk3036_codec_reset(component);
}
-static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
+static int rk3036_codec_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_STANDBY:
/* set a big current for capacitor charging. */
- snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
+ snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
/* start precharge */
- snd_soc_write(codec, INNO_R06, INNO_R06_DAC_PRECHARGE);
+ snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE);
break;
case SND_SOC_BIAS_OFF:
/* set a big current for capacitor discharging. */
- snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
+ snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR);
/* start discharge. */
- snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
+ snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE);
break;
default:
@@ -376,7 +374,7 @@ static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static struct snd_soc_codec_driver rk3036_codec_driver = {
+static const struct snd_soc_component_driver rk3036_codec_driver = {
.probe = rk3036_codec_probe,
.remove = rk3036_codec_remove,
.set_bias_level = rk3036_codec_set_bias_level,
@@ -386,6 +384,9 @@ static struct snd_soc_codec_driver rk3036_codec_driver = {
.num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
.dapm_widgets = rk3036_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rk3036_codec_regmap_config = {
@@ -401,7 +402,6 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv;
struct device_node *of_node = pdev->dev.of_node;
- struct resource *res;
void __iomem *base;
struct regmap *grf;
int ret;
@@ -410,8 +410,7 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -447,7 +446,7 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, priv);
- ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
+ ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver,
rk3036_codec_dai_driver,
ARRAY_SIZE(rk3036_codec_dai_driver));
if (ret) {
@@ -458,17 +457,14 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
return ret;
}
-static int rk3036_codec_platform_remove(struct platform_device *pdev)
+static void rk3036_codec_platform_remove(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_codec(&pdev->dev);
clk_disable_unprepare(priv->pclk);
-
- return 0;
}
-static const struct of_device_id rk3036_codec_of_match[] = {
+static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {
{ .compatible = "rockchip,rk3036-codec", },
{}
};
diff --git a/sound/soc/codecs/inno_rk3036.h b/sound/soc/codecs/inno_rk3036.h
index da759c6c7501..44bb2404198d 100644
--- a/sound/soc/codecs/inno_rk3036.h
+++ b/sound/soc/codecs/inno_rk3036.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver of Inno Codec for rk3036 by Rockchip Inc.
*
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index be448373d39a..b7a94631d77d 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* isabelle.c - Low power high fidelity audio codec driver
*
* Copyright (c) 2012 Texas Instruments, Inc
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- *
* Initially based on sound/soc/codecs/twl6040.c
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -865,31 +860,31 @@ static const struct snd_soc_dapm_route isabelle_intercon[] = {
{ "LINEOUT2", NULL, "LINEOUT2 Driver" },
};
-static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute)
+static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC1_SOFTRAMP_REG,
+ snd_soc_component_update_bits(dai->component, ISABELLE_DAC1_SOFTRAMP_REG,
BIT(4), (mute ? BIT(4) : 0));
return 0;
}
-static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute)
+static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC2_SOFTRAMP_REG,
+ snd_soc_component_update_bits(dai->component, ISABELLE_DAC2_SOFTRAMP_REG,
BIT(4), (mute ? BIT(4) : 0));
return 0;
}
-static int isabelle_line_mute(struct snd_soc_dai *dai, int mute)
+static int isabelle_line_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC3_SOFTRAMP_REG,
+ snd_soc_component_update_bits(dai->component, ISABELLE_DAC3_SOFTRAMP_REG,
BIT(4), (mute ? BIT(4) : 0));
return 0;
}
-static int isabelle_set_bias_level(struct snd_soc_codec *codec,
+static int isabelle_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -899,12 +894,12 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
ISABELLE_CHIP_EN, BIT(0));
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
ISABELLE_CHIP_EN, 0);
break;
}
@@ -916,7 +911,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 aif = 0;
unsigned int fs_val = 0;
@@ -952,7 +947,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_FS_RATE_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_FS_RATE_CFG_REG,
ISABELLE_FS_RATE_MASK, fs_val);
/* bit size */
@@ -967,7 +962,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
ISABELLE_AIF_LENGTH_MASK, aif);
return 0;
@@ -975,14 +970,14 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int aif_val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
aif_val &= ~ISABELLE_AIF_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif_val |= ISABELLE_AIF_MS;
break;
default:
@@ -1003,7 +998,7 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
(ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val);
return 0;
@@ -1019,19 +1014,22 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static const struct snd_soc_dai_ops isabelle_hs_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_hs_mute,
+ .mute_stream = isabelle_hs_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops isabelle_hf_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_hf_mute,
+ .mute_stream = isabelle_hf_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops isabelle_line_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_line_mute,
+ .mute_stream = isabelle_line_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops isabelle_ul_dai_ops = {
@@ -1087,15 +1085,16 @@ static struct snd_soc_dai_driver isabelle_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_isabelle = {
- .set_bias_level = isabelle_set_bias_level,
- .controls = isabelle_snd_controls,
- .num_controls = ARRAY_SIZE(isabelle_snd_controls),
- .dapm_widgets = isabelle_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets),
- .dapm_routes = isabelle_intercon,
- .num_dapm_routes = ARRAY_SIZE(isabelle_intercon),
- .idle_bias_off = true,
+static const struct snd_soc_component_driver soc_component_dev_isabelle = {
+ .set_bias_level = isabelle_set_bias_level,
+ .controls = isabelle_snd_controls,
+ .num_controls = ARRAY_SIZE(isabelle_snd_controls),
+ .dapm_widgets = isabelle_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets),
+ .dapm_routes = isabelle_intercon,
+ .num_dapm_routes = ARRAY_SIZE(isabelle_intercon),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config isabelle_regmap_config = {
@@ -1108,8 +1107,7 @@ static const struct regmap_config isabelle_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int isabelle_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int isabelle_i2c_probe(struct i2c_client *i2c)
{
struct regmap *isabelle_regmap;
int ret = 0;
@@ -1123,25 +1121,19 @@ static int isabelle_i2c_probe(struct i2c_client *i2c,
}
i2c_set_clientdata(i2c, isabelle_regmap);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_isabelle, isabelle_dai,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_isabelle, isabelle_dai,
ARRAY_SIZE(isabelle_dai));
if (ret < 0) {
- dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int isabelle_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id isabelle_i2c_id[] = {
- { "isabelle", 0 },
+ { "isabelle" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
@@ -1151,7 +1143,6 @@ static struct i2c_driver isabelle_i2c_driver = {
.name = "isabelle",
},
.probe = isabelle_i2c_probe,
- .remove = isabelle_i2c_remove,
.id_table = isabelle_i2c_id,
};
diff --git a/sound/soc/codecs/isabelle.h b/sound/soc/codecs/isabelle.h
index 96d839a8c956..23afc77cdc99 100644
--- a/sound/soc/codecs/isabelle.h
+++ b/sound/soc/codecs/isabelle.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* isabelle.h - Low power high fidelity audio codec driver header file
*
* Copyright (c) 2012 Texas Instruments, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
*/
#ifndef _ISABELLE_H
diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c
new file mode 100644
index 000000000000..39cebaa167be
--- /dev/null
+++ b/sound/soc/codecs/jz4725b.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// JZ4725B CODEC driver
+//
+// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define ICDC_RGADW_OFFSET 0x00
+#define ICDC_RGDATA_OFFSET 0x04
+
+/* ICDC internal register access control register(RGADW) */
+#define ICDC_RGADW_RGWR BIT(16)
+
+#define ICDC_RGADW_RGADDR_OFFSET 8
+#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
+
+#define ICDC_RGADW_RGDIN_OFFSET 0
+#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
+
+/* ICDC internal register data output register (RGDATA)*/
+#define ICDC_RGDATA_IRQ BIT(8)
+
+#define ICDC_RGDATA_RGDOUT_OFFSET 0
+#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
+
+/* JZ internal register space */
+enum {
+ JZ4725B_CODEC_REG_AICR,
+ JZ4725B_CODEC_REG_CR1,
+ JZ4725B_CODEC_REG_CR2,
+ JZ4725B_CODEC_REG_CCR1,
+ JZ4725B_CODEC_REG_CCR2,
+ JZ4725B_CODEC_REG_PMR1,
+ JZ4725B_CODEC_REG_PMR2,
+ JZ4725B_CODEC_REG_CRR,
+ JZ4725B_CODEC_REG_ICR,
+ JZ4725B_CODEC_REG_IFR,
+ JZ4725B_CODEC_REG_CGR1,
+ JZ4725B_CODEC_REG_CGR2,
+ JZ4725B_CODEC_REG_CGR3,
+ JZ4725B_CODEC_REG_CGR4,
+ JZ4725B_CODEC_REG_CGR5,
+ JZ4725B_CODEC_REG_CGR6,
+ JZ4725B_CODEC_REG_CGR7,
+ JZ4725B_CODEC_REG_CGR8,
+ JZ4725B_CODEC_REG_CGR9,
+ JZ4725B_CODEC_REG_CGR10,
+ JZ4725B_CODEC_REG_TR1,
+ JZ4725B_CODEC_REG_TR2,
+ JZ4725B_CODEC_REG_CR3,
+ JZ4725B_CODEC_REG_AGC1,
+ JZ4725B_CODEC_REG_AGC2,
+ JZ4725B_CODEC_REG_AGC3,
+ JZ4725B_CODEC_REG_AGC4,
+ JZ4725B_CODEC_REG_AGC5,
+};
+
+#define REG_AICR_CONFIG1_OFFSET 0
+#define REG_AICR_CONFIG1_MASK (0xf << REG_AICR_CONFIG1_OFFSET)
+
+#define REG_CR1_SB_MICBIAS_OFFSET 7
+#define REG_CR1_MONO_OFFSET 6
+#define REG_CR1_DAC_MUTE_OFFSET 5
+#define REG_CR1_HP_DIS_OFFSET 4
+#define REG_CR1_DACSEL_OFFSET 3
+#define REG_CR1_BYPASS_OFFSET 2
+
+#define REG_CR2_DAC_DEEMP_OFFSET 7
+#define REG_CR2_DAC_ADWL_OFFSET 5
+#define REG_CR2_DAC_ADWL_MASK (0x3 << REG_CR2_DAC_ADWL_OFFSET)
+#define REG_CR2_ADC_ADWL_OFFSET 3
+#define REG_CR2_ADC_ADWL_MASK (0x3 << REG_CR2_ADC_ADWL_OFFSET)
+#define REG_CR2_ADC_HPF_OFFSET 2
+
+#define REG_CR3_SB_MIC1_OFFSET 7
+#define REG_CR3_SB_MIC2_OFFSET 6
+#define REG_CR3_SIDETONE1_OFFSET 5
+#define REG_CR3_SIDETONE2_OFFSET 4
+#define REG_CR3_MICDIFF_OFFSET 3
+#define REG_CR3_MICSTEREO_OFFSET 2
+#define REG_CR3_INSEL_OFFSET 0
+#define REG_CR3_INSEL_MASK (0x3 << REG_CR3_INSEL_OFFSET)
+
+#define REG_CCR1_CONFIG4_OFFSET 0
+#define REG_CCR1_CONFIG4_MASK (0xf << REG_CCR1_CONFIG4_OFFSET)
+
+#define REG_CCR2_DFREQ_OFFSET 4
+#define REG_CCR2_DFREQ_MASK (0xf << REG_CCR2_DFREQ_OFFSET)
+#define REG_CCR2_AFREQ_OFFSET 0
+#define REG_CCR2_AFREQ_MASK (0xf << REG_CCR2_AFREQ_OFFSET)
+
+#define REG_PMR1_SB_DAC_OFFSET 7
+#define REG_PMR1_SB_OUT_OFFSET 6
+#define REG_PMR1_SB_MIX_OFFSET 5
+#define REG_PMR1_SB_ADC_OFFSET 4
+#define REG_PMR1_SB_LIN_OFFSET 3
+#define REG_PMR1_SB_IND_OFFSET 0
+
+#define REG_PMR2_LRGI_OFFSET 7
+#define REG_PMR2_RLGI_OFFSET 6
+#define REG_PMR2_LRGOD_OFFSET 5
+#define REG_PMR2_RLGOD_OFFSET 4
+#define REG_PMR2_GIM_OFFSET 3
+#define REG_PMR2_SB_MC_OFFSET 2
+#define REG_PMR2_SB_OFFSET 1
+#define REG_PMR2_SB_SLEEP_OFFSET 0
+
+#define REG_IFR_RAMP_UP_DONE_OFFSET 3
+#define REG_IFR_RAMP_DOWN_DONE_OFFSET 2
+
+#define REG_CGR1_GODL_OFFSET 4
+#define REG_CGR1_GODL_MASK (0xf << REG_CGR1_GODL_OFFSET)
+#define REG_CGR1_GODR_OFFSET 0
+#define REG_CGR1_GODR_MASK (0xf << REG_CGR1_GODR_OFFSET)
+
+#define REG_CGR2_GO1R_OFFSET 0
+#define REG_CGR2_GO1R_MASK (0x1f << REG_CGR2_GO1R_OFFSET)
+
+#define REG_CGR3_GO1L_OFFSET 0
+#define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET)
+
+#define REG_CGR4_GO2R_OFFSET 0
+#define REG_CGR4_GO2R_MASK (0x1f << REG_CGR4_GO2R_OFFSET)
+
+#define REG_CGR5_GO2L_OFFSET 0
+#define REG_CGR5_GO2L_MASK (0x1f << REG_CGR5_GO2L_OFFSET)
+
+#define REG_CGR6_GO3R_OFFSET 0
+#define REG_CGR6_GO3R_MASK (0x1f << REG_CGR6_GO3R_OFFSET)
+
+#define REG_CGR7_GO3L_OFFSET 0
+#define REG_CGR7_GO3L_MASK (0x1f << REG_CGR7_GO3L_OFFSET)
+
+#define REG_CGR8_GOR_OFFSET 0
+#define REG_CGR8_GOR_MASK (0x1f << REG_CGR8_GOR_OFFSET)
+
+#define REG_CGR9_GOL_OFFSET 0
+#define REG_CGR9_GOL_MASK (0x1f << REG_CGR9_GOL_OFFSET)
+
+#define REG_CGR10_GIL_OFFSET 0
+#define REG_CGR10_GIR_OFFSET 4
+
+struct jz_icdc {
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_adc_tlv, 0, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_dac_tlv, -2250, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_mix_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(-2250, 0, 0),
+ 12, 31, TLV_DB_SCALE_ITEM(-2250, 150, 0),
+);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_out_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(-3350, 200, 0),
+ 12, 23, TLV_DB_SCALE_ITEM(-1050, 100, 0),
+ 24, 31, TLV_DB_SCALE_ITEM( 100, 50, 0),
+);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_mic_boost_tlv, 0, 2000, 0);
+
+static const char * const jz4725b_mic_mode_texts[] = {
+ "Single Ended", "Differential",
+};
+
+static const struct soc_enum jz4725b_mic_mode_enum =
+ SOC_ENUM_SINGLE(JZ4725B_CODEC_REG_CR3, REG_CR3_MICDIFF_OFFSET,
+ 2, jz4725b_mic_mode_texts);
+
+static const struct snd_kcontrol_new jz4725b_codec_controls[] = {
+ SOC_DOUBLE_TLV("DAC Playback Volume",
+ JZ4725B_CODEC_REG_CGR1,
+ REG_CGR1_GODL_OFFSET,
+ REG_CGR1_GODR_OFFSET,
+ 0xf, 1, jz4725b_dac_tlv),
+ SOC_DOUBLE_TLV("Master Capture Volume",
+ JZ4725B_CODEC_REG_CGR10,
+ REG_CGR10_GIL_OFFSET,
+ REG_CGR10_GIR_OFFSET,
+ 0xf, 0, jz4725b_adc_tlv),
+ SOC_DOUBLE_R_TLV("Mixer Line In Bypass Playback Volume",
+ JZ4725B_CODEC_REG_CGR3,
+ JZ4725B_CODEC_REG_CGR2,
+ REG_CGR2_GO1R_OFFSET,
+ 0x1f, 1, jz4725b_mix_tlv),
+ SOC_DOUBLE_R_TLV("Mixer Mic 1 Bypass Playback Volume",
+ JZ4725B_CODEC_REG_CGR5,
+ JZ4725B_CODEC_REG_CGR4,
+ REG_CGR4_GO2R_OFFSET,
+ 0x1f, 1, jz4725b_mix_tlv),
+ SOC_DOUBLE_R_TLV("Mixer Mic 2 Bypass Playback Volume",
+ JZ4725B_CODEC_REG_CGR7,
+ JZ4725B_CODEC_REG_CGR6,
+ REG_CGR6_GO3R_OFFSET,
+ 0x1f, 1, jz4725b_mix_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ JZ4725B_CODEC_REG_CGR9,
+ JZ4725B_CODEC_REG_CGR8,
+ REG_CGR8_GOR_OFFSET,
+ 0x1f, 1, jz4725b_out_tlv),
+
+ SOC_SINGLE("DAC Playback Switch", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_DAC_MUTE_OFFSET, 1, 1),
+
+ SOC_SINGLE("Deemphasize Filter Playback Switch",
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_DAC_DEEMP_OFFSET, 1, 0),
+
+ SOC_SINGLE("High-Pass Filter Capture Switch",
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_ADC_HPF_OFFSET, 1, 0),
+
+ SOC_ENUM("Mic Mode Capture Switch", jz4725b_mic_mode_enum),
+
+ SOC_SINGLE_TLV("Mic1 Boost Capture Volume",
+ JZ4725B_CODEC_REG_PMR2,
+ REG_PMR2_GIM_OFFSET,
+ 1, 0, jz4725b_mic_boost_tlv),
+};
+
+static const char * const jz4725b_codec_adc_src_texts[] = {
+ "Mic 1", "Mic 2", "Line In", "Mixer",
+};
+static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, };
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum,
+ JZ4725B_CODEC_REG_CR3,
+ REG_CR3_INSEL_OFFSET,
+ REG_CR3_INSEL_MASK,
+ jz4725b_codec_adc_src_texts,
+ jz4725b_codec_adc_src_values);
+static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl =
+ SOC_DAPM_ENUM("ADC Source Capture Route", jz4725b_codec_adc_src_enum);
+
+static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line In Bypass Playback Switch", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_BYPASS_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic 1 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SIDETONE1_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic 2 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SIDETONE2_OFFSET, 1, 0),
+};
+
+static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(codec);
+ struct regmap *map = icdc->regmap;
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR,
+ BIT(REG_IFR_RAMP_UP_DONE_OFFSET));
+ case SND_SOC_DAPM_POST_PMU:
+ return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR,
+ val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET),
+ 100000, 500000);
+ case SND_SOC_DAPM_PRE_PMD:
+ return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR,
+ BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET));
+ case SND_SOC_DAPM_POST_PMD:
+ return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR,
+ val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET),
+ 100000, 500000);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = {
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC", "Playback",
+ JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC", "Capture",
+ JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1),
+
+ SND_SOC_DAPM_MUX("ADC Source Capture Route", SND_SOC_NOPM, 0, 0,
+ &jz4725b_codec_adc_src_ctrl),
+
+ /* Mixer */
+ SND_SOC_DAPM_MIXER("Mixer", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_MIX_OFFSET, 1,
+ jz4725b_codec_mixer_controls,
+ ARRAY_SIZE(jz4725b_codec_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_DACSEL_OFFSET, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Line In", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_LIN_OFFSET, 1, NULL, 0),
+ SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_HP_DIS_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mic 1", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mic 2", JZ4725B_CODEC_REG_CR3,
+ REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("Out Stage", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0,
+ jz4725b_out_stage_enable,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Mixer to ADC", JZ4725B_CODEC_REG_PMR1,
+ REG_PMR1_SB_IND_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", JZ4725B_CODEC_REG_CR1,
+ REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0),
+
+ /* Pins */
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = {
+ {"Mic 1", NULL, "MIC1P"},
+ {"Mic 1", NULL, "MIC1N"},
+ {"Mic 2", NULL, "MIC2P"},
+ {"Mic 2", NULL, "MIC2N"},
+
+ {"Line In", NULL, "LLINEIN"},
+ {"Line In", NULL, "RLINEIN"},
+
+ {"Mixer", "Mic 1 Bypass Playback Switch", "Mic 1"},
+ {"Mixer", "Mic 2 Bypass Playback Switch", "Mic 2"},
+ {"Mixer", "Line In Bypass Playback Switch", "Line In"},
+ {"DAC to Mixer", NULL, "DAC"},
+ {"Mixer", NULL, "DAC to Mixer"},
+
+ {"Mixer to ADC", NULL, "Mixer"},
+ {"ADC Source Capture Route", "Mixer", "Mixer to ADC"},
+ {"ADC Source Capture Route", "Line In", "Line In"},
+ {"ADC Source Capture Route", "Mic 1", "Mic 1"},
+ {"ADC Source Capture Route", "Mic 2", "Mic 2"},
+ {"ADC", NULL, "ADC Source Capture Route"},
+
+ {"Out Stage", NULL, "Mixer"},
+ {"HP Out", NULL, "Out Stage"},
+ {"LHPOUT", NULL, "HP Out"},
+ {"RHPOUT", NULL, "HP Out"},
+};
+
+static int jz4725b_codec_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
+ struct regmap *map = icdc->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_SLEEP_OFFSET));
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable sound hardware */
+ regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_OFFSET));
+ msleep(224);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_SLEEP_OFFSET));
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2,
+ BIT(REG_PMR2_SB_OFFSET));
+ break;
+ }
+
+ return 0;
+}
+
+static int jz4725b_codec_dev_probe(struct snd_soc_component *component)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
+ struct regmap *map = icdc->regmap;
+
+ clk_prepare_enable(icdc->clk);
+
+ /* Write CONFIGn (n=1 to 8) bits.
+ * The value 0x0f is specified in the datasheet as a requirement.
+ */
+ regmap_write(map, JZ4725B_CODEC_REG_AICR,
+ 0xf << REG_AICR_CONFIG1_OFFSET);
+ regmap_write(map, JZ4725B_CODEC_REG_CCR1,
+ 0x0 << REG_CCR1_CONFIG4_OFFSET);
+
+ return 0;
+}
+
+static void jz4725b_codec_dev_remove(struct snd_soc_component *component)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(icdc->clk);
+}
+
+static const struct snd_soc_component_driver jz4725b_codec = {
+ .probe = jz4725b_codec_dev_probe,
+ .remove = jz4725b_codec_dev_remove,
+ .set_bias_level = jz4725b_codec_set_bias_level,
+ .controls = jz4725b_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4725b_codec_controls),
+ .dapm_widgets = jz4725b_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4725b_codec_dapm_widgets),
+ .dapm_routes = jz4725b_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4725b_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .use_pmdown_time = 1,
+};
+
+static const unsigned int jz4725b_codec_sample_rates[] = {
+ 96000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000,
+ 11025, 9600, 8000,
+};
+
+static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct jz_icdc *icdc = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate, bit_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_width = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ bit_width = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bit_width = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) {
+ if (jz4725b_codec_sample_rates[rate] == params_rate(params))
+ break;
+ }
+
+ if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates))
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_DAC_ADWL_MASK,
+ bit_width << REG_CR2_DAC_ADWL_OFFSET);
+
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CCR2,
+ REG_CCR2_DFREQ_MASK,
+ rate << REG_CCR2_DFREQ_OFFSET);
+ } else {
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CR2,
+ REG_CR2_ADC_ADWL_MASK,
+ bit_width << REG_CR2_ADC_ADWL_OFFSET);
+
+ regmap_update_bits(icdc->regmap,
+ JZ4725B_CODEC_REG_CCR2,
+ REG_CCR2_AFREQ_MASK,
+ rate << REG_CCR2_AFREQ_OFFSET);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = {
+ .hw_params = jz4725b_codec_hw_params,
+};
+
+#define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver jz4725b_codec_dai = {
+ .name = "jz4725b-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_ICDC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_ICDC_FORMATS,
+ },
+ .ops = &jz4725b_codec_dai_ops,
+};
+
+static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == JZ4725B_CODEC_REG_IFR;
+}
+
+static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg)
+{
+ return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2);
+}
+
+static int jz4725b_codec_io_wait(struct jz_icdc *icdc)
+{
+ u32 reg;
+
+ return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg,
+ !(reg & ICDC_RGADW_RGWR), 1000, 10000);
+}
+
+static int jz4725b_codec_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct jz_icdc *icdc = context;
+ unsigned int i;
+ u32 tmp;
+ int ret;
+
+ ret = jz4725b_codec_io_wait(icdc);
+ if (ret)
+ return ret;
+
+ tmp = readl(icdc->base + ICDC_RGADW_OFFSET);
+ tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
+ | (reg << ICDC_RGADW_RGADDR_OFFSET);
+ writel(tmp, icdc->base + ICDC_RGADW_OFFSET);
+
+ /* wait 6+ cycles */
+ for (i = 0; i < 6; i++)
+ *val = readl(icdc->base + ICDC_RGDATA_OFFSET) &
+ ICDC_RGDATA_RGDOUT_MASK;
+
+ return 0;
+}
+
+static int jz4725b_codec_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct jz_icdc *icdc = context;
+ int ret;
+
+ ret = jz4725b_codec_io_wait(icdc);
+ if (ret)
+ return ret;
+
+ writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
+ icdc->base + ICDC_RGADW_OFFSET);
+
+ ret = jz4725b_codec_io_wait(icdc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u8 jz4725b_codec_reg_defaults[] = {
+ 0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51,
+ 0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34,
+ 0x07, 0x44, 0x1f, 0x00,
+};
+
+static const struct regmap_config jz4725b_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .max_register = JZ4725B_CODEC_REG_AGC5,
+ .volatile_reg = jz4725b_codec_volatile,
+ .readable_reg = jz4725b_codec_can_access_reg,
+ .writeable_reg = jz4725b_codec_can_access_reg,
+
+ .reg_read = jz4725b_codec_reg_read,
+ .reg_write = jz4725b_codec_reg_write,
+
+ .reg_defaults_raw = jz4725b_codec_reg_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int jz4725b_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_icdc *icdc;
+ int ret;
+
+ icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL);
+ if (!icdc)
+ return -ENOMEM;
+
+ icdc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(icdc->base))
+ return PTR_ERR(icdc->base);
+
+ icdc->regmap = devm_regmap_init(dev, NULL, icdc,
+ &jz4725b_codec_regmap_config);
+ if (IS_ERR(icdc->regmap))
+ return PTR_ERR(icdc->regmap);
+
+ icdc->clk = devm_clk_get(&pdev->dev, "aic");
+ if (IS_ERR(icdc->clk))
+ return PTR_ERR(icdc->clk);
+
+ platform_set_drvdata(pdev, icdc);
+
+ ret = devm_snd_soc_register_component(dev, &jz4725b_codec,
+ &jz4725b_codec_dai, 1);
+ if (ret)
+ dev_err(dev, "Failed to register codec\n");
+
+ return ret;
+}
+
+static const struct of_device_id jz4725b_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4725b-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches);
+
+static struct platform_driver jz4725b_codec_driver = {
+ .probe = jz4725b_codec_probe,
+ .driver = {
+ .name = "jz4725b-codec",
+ .of_match_table = jz4725b_codec_of_matches,
+ },
+};
+module_platform_driver(jz4725b_codec_driver);
+
+MODULE_DESCRIPTION("JZ4725B SoC internal codec driver");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 1f5ab99956ed..d3d801d850a1 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -1,15 +1,8 @@
-/*
- * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// JZ4740 CODEC driver
+//
+// Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -157,7 +150,7 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(dai->component);
uint32_t val;
switch (params_rate(params)) {
@@ -221,28 +214,27 @@ static struct snd_soc_dai_driver jz4740_codec_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
},
.ops = &jz4740_codec_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static void jz4740_codec_wakeup(struct regmap *regmap)
{
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET);
udelay(2);
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+ regmap_clear_bits(regmap, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET);
regcache_sync(regmap);
}
-static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
+static int jz4740_codec_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component);
struct regmap *regmap = jz4740_codec->regmap;
unsigned int mask;
- unsigned int value;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -251,29 +243,23 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- value = 0;
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regmap_clear_bits(regmap, JZ4740_REG_CODEC_1, mask);
break;
case SND_SOC_BIAS_STANDBY:
/* The only way to clear the suspend flag is to reset the codec */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
jz4740_codec_wakeup(regmap);
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- value = JZ4740_CODEC_1_VREF_DISABLE |
- JZ4740_CODEC_1_VREF_AMP_DISABLE |
- JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
break;
case SND_SOC_BIAS_OFF:
mask = JZ4740_CODEC_1_SUSPEND;
- value = JZ4740_CODEC_1_SUSPEND;
-
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
regcache_mark_dirty(regmap);
break;
default:
@@ -283,9 +269,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
+static int jz4740_codec_dev_probe(struct snd_soc_component *component)
{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component);
regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
@@ -293,17 +279,19 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
- .probe = jz4740_codec_dev_probe,
- .set_bias_level = jz4740_codec_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = jz4740_codec_controls,
- .num_controls = ARRAY_SIZE(jz4740_codec_controls),
- .dapm_widgets = jz4740_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
- .dapm_routes = jz4740_codec_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
+static const struct snd_soc_component_driver soc_codec_dev_jz4740_codec = {
+ .probe = jz4740_codec_dev_probe,
+ .set_bias_level = jz4740_codec_set_bias_level,
+ .controls = jz4740_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4740_codec_controls),
+ .dapm_widgets = jz4740_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
+ .dapm_routes = jz4740_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config jz4740_codec_regmap_config = {
@@ -314,14 +302,13 @@ static const struct regmap_config jz4740_codec_regmap_config = {
.reg_defaults = jz4740_codec_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int jz4740_codec_probe(struct platform_device *pdev)
{
int ret;
struct jz4740_codec *jz4740_codec;
- struct resource *mem;
void __iomem *base;
jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
@@ -329,8 +316,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
if (!jz4740_codec)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -341,7 +327,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, jz4740_codec);
- ret = snd_soc_register_codec(&pdev->dev,
+ ret = devm_snd_soc_register_component(&pdev->dev,
&soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
if (ret)
dev_err(&pdev->dev, "Failed to register codec\n");
@@ -349,18 +335,17 @@ static int jz4740_codec_probe(struct platform_device *pdev)
return ret;
}
-static int jz4740_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
+static const struct of_device_id jz4740_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4740-codec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jz4740_codec_of_matches);
static struct platform_driver jz4740_codec_driver = {
.probe = jz4740_codec_probe,
- .remove = jz4740_codec_remove,
.driver = {
.name = "jz4740-codec",
+ .of_match_table = jz4740_codec_of_matches,
},
};
diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c
new file mode 100644
index 000000000000..344c251be397
--- /dev/null
+++ b/sound/soc/codecs/jz4760.c
@@ -0,0 +1,872 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Ingenic JZ4760 CODEC driver
+//
+// Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com>
+// Copyright (C) 2021, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define ICDC_RGADW_OFFSET 0x00
+#define ICDC_RGDATA_OFFSET 0x04
+
+/* ICDC internal register access control register(RGADW) */
+#define ICDC_RGADW_RGWR BIT(16)
+#define ICDC_RGADW_RGADDR_MASK GENMASK(14, 8)
+#define ICDC_RGADW_RGDIN_MASK GENMASK(7, 0)
+
+/* ICDC internal register data output register (RGDATA)*/
+#define ICDC_RGDATA_IRQ BIT(8)
+#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, 0)
+
+/* Internal register space, accessed through regmap */
+enum {
+ JZ4760_CODEC_REG_SR,
+ JZ4760_CODEC_REG_AICR,
+ JZ4760_CODEC_REG_CR1,
+ JZ4760_CODEC_REG_CR2,
+ JZ4760_CODEC_REG_CR3,
+ JZ4760_CODEC_REG_CR4,
+ JZ4760_CODEC_REG_CCR1,
+ JZ4760_CODEC_REG_CCR2,
+ JZ4760_CODEC_REG_PMR1,
+ JZ4760_CODEC_REG_PMR2,
+ JZ4760_CODEC_REG_ICR,
+ JZ4760_CODEC_REG_IFR,
+ JZ4760_CODEC_REG_GCR1,
+ JZ4760_CODEC_REG_GCR2,
+ JZ4760_CODEC_REG_GCR3,
+ JZ4760_CODEC_REG_GCR4,
+ JZ4760_CODEC_REG_GCR5,
+ JZ4760_CODEC_REG_GCR6,
+ JZ4760_CODEC_REG_GCR7,
+ JZ4760_CODEC_REG_GCR8,
+ JZ4760_CODEC_REG_GCR9,
+ JZ4760_CODEC_REG_AGC1,
+ JZ4760_CODEC_REG_AGC2,
+ JZ4760_CODEC_REG_AGC3,
+ JZ4760_CODEC_REG_AGC4,
+ JZ4760_CODEC_REG_AGC5,
+ JZ4760_CODEC_REG_MIX1,
+ JZ4760_CODEC_REG_MIX2,
+};
+
+#define REG_AICR_DAC_ADWL_MASK GENMASK(7, 6)
+#define REG_AICR_DAC_SERIAL BIT(3)
+#define REG_AICR_DAC_I2S BIT(1)
+
+#define REG_AICR_ADC_ADWL_MASK GENMASK(5, 4)
+
+#define REG_AICR_ADC_SERIAL BIT(2)
+#define REG_AICR_ADC_I2S BIT(0)
+
+#define REG_CR1_HP_LOAD BIT(7)
+#define REG_CR1_HP_MUTE BIT(5)
+#define REG_CR1_LO_MUTE_OFFSET 4
+#define REG_CR1_BTL_MUTE_OFFSET 3
+#define REG_CR1_OUTSEL_OFFSET 0
+#define REG_CR1_OUTSEL_MASK GENMASK(1, REG_CR1_OUTSEL_OFFSET)
+
+#define REG_CR2_DAC_MONO BIT(7)
+#define REG_CR2_DAC_MUTE BIT(5)
+#define REG_CR2_DAC_NOMAD BIT(1)
+#define REG_CR2_DAC_RIGHT_ONLY BIT(0)
+
+#define REG_CR3_ADC_INSEL_OFFSET 2
+#define REG_CR3_ADC_INSEL_MASK GENMASK(3, REG_CR3_ADC_INSEL_OFFSET)
+#define REG_CR3_MICSTEREO_OFFSET 1
+#define REG_CR3_MICDIFF_OFFSET 0
+
+#define REG_CR4_ADC_HPF_OFFSET 7
+#define REG_CR4_ADC_RIGHT_ONLY BIT(0)
+
+#define REG_CCR1_CRYSTAL_MASK GENMASK(3, 0)
+
+#define REG_CCR2_DAC_FREQ_MASK GENMASK(7, 4)
+#define REG_CCR2_ADC_FREQ_MASK GENMASK(3, 0)
+
+#define REG_PMR1_SB BIT(7)
+#define REG_PMR1_SB_SLEEP BIT(6)
+#define REG_PMR1_SB_AIP_OFFSET 5
+#define REG_PMR1_SB_LINE_OFFSET 4
+#define REG_PMR1_SB_MIC1_OFFSET 3
+#define REG_PMR1_SB_MIC2_OFFSET 2
+#define REG_PMR1_SB_BYPASS_OFFSET 1
+#define REG_PMR1_SB_MICBIAS_OFFSET 0
+
+#define REG_PMR2_SB_ADC_OFFSET 4
+#define REG_PMR2_SB_HP_OFFSET 3
+#define REG_PMR2_SB_BTL_OFFSET 2
+#define REG_PMR2_SB_LOUT_OFFSET 1
+#define REG_PMR2_SB_DAC_OFFSET 0
+
+#define REG_ICR_INT_FORM_MASK GENMASK(7, 6)
+#define REG_ICR_ALL_MASK GENMASK(5, 0)
+#define REG_ICR_JACK_MASK BIT(5)
+#define REG_ICR_SCMC_MASK BIT(4)
+#define REG_ICR_RUP_MASK BIT(3)
+#define REG_ICR_RDO_MASK BIT(2)
+#define REG_ICR_GUP_MASK BIT(1)
+#define REG_ICR_GDO_MASK BIT(0)
+
+#define REG_IFR_ALL_MASK GENMASK(5, 0)
+#define REG_IFR_JACK BIT(6)
+#define REG_IFR_JACK_EVENT BIT(5)
+#define REG_IFR_SCMC BIT(4)
+#define REG_IFR_RUP BIT(3)
+#define REG_IFR_RDO BIT(2)
+#define REG_IFR_GUP BIT(1)
+#define REG_IFR_GDO BIT(0)
+
+#define REG_GCR_GAIN_OFFSET 0
+#define REG_GCR_GAIN_MAX 0x1f
+
+#define REG_GCR_RL BIT(7)
+
+#define REG_GCR_GIM1_MASK GENMASK(5, 3)
+#define REG_GCR_GIM2_MASK GENMASK(2, 0)
+#define REG_GCR_GIM_GAIN_MAX 7
+
+#define REG_AGC1_EN BIT(7)
+#define REG_AGC1_TARGET_MASK GENMASK(5, 2)
+
+#define REG_AGC2_NG_THR_MASK GENMASK(6, 4)
+#define REG_AGC2_HOLD_MASK GENMASK(3, 0)
+
+#define REG_AGC3_ATK_MASK GENMASK(7, 4)
+#define REG_AGC3_DCY_MASK GENMASK(3, 0)
+
+#define REG_AGC4_AGC_MAX_MASK GENMASK(4, 0)
+
+#define REG_AGC5_AGC_MIN_MASK GENMASK(4, 0)
+
+#define REG_MIX1_MIX_REC_MASK GENMASK(7, 6)
+#define REG_MIX1_GIMIX_MASK GENMASK(4, 0)
+
+#define REG_MIX2_DAC_MIX_MASK GENMASK(7, 6)
+#define REG_MIX2_GOMIX_MASK GENMASK(4, 0)
+
+/* codec private data */
+struct jz_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static int jz4760_codec_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ /* Reset all interrupt flags. */
+ regmap_write(regmap, JZ4760_CODEC_REG_IFR, REG_IFR_ALL_MASK);
+
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB);
+ msleep(250);
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP);
+ msleep(400);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP);
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int jz4760_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+ int ret = 0;
+
+ /*
+ * SYSCLK output from the codec to the AIC is required to keep the
+ * DMA transfer going during playback when all audible outputs have
+ * been disabled.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
+ return ret;
+}
+
+static void jz4760_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_disable_pin(dapm, "SYSCLK");
+}
+
+
+static int jz4760_codec_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_ON);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* do nothing */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int jz4760_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
+ unsigned int val, reg;
+ int change, err;
+
+ change = snd_soc_component_update_bits(codec, JZ4760_CODEC_REG_CR2,
+ REG_CR2_DAC_MUTE,
+ mute ? REG_CR2_DAC_MUTE : 0);
+ if (change == 1) {
+ regmap_read(jz_codec->regmap, JZ4760_CODEC_REG_PMR2, &val);
+
+ if (val & BIT(REG_PMR2_SB_DAC_OFFSET))
+ return 1;
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4760_CODEC_REG_IFR,
+ val, val & gain_bit,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev,
+ "Timeout while setting digital mute: %d", err);
+ return err;
+ }
+
+ /* clear GUP/GDO flag */
+ regmap_write(jz_codec->regmap, JZ4760_CODEC_REG_IFR, gain_bit);
+ }
+
+ regmap_read(jz_codec->regmap, JZ4760_CODEC_REG_CR2, &reg);
+
+ return 0;
+}
+
+/* unit: 0.01dB */
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 100);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 100);
+static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
+
+/* Unconditional controls. */
+static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = {
+ /* record gain control */
+ SOC_DOUBLE_R_TLV("PCM Capture Volume",
+ JZ4760_CODEC_REG_GCR9, JZ4760_CODEC_REG_GCR8,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 0, adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
+ JZ4760_CODEC_REG_GCR4, JZ4760_CODEC_REG_GCR3,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
+
+ SOC_SINGLE_TLV("Mixer Capture Volume",
+ JZ4760_CODEC_REG_MIX1,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE_TLV("Mixer Playback Volume",
+ JZ4760_CODEC_REG_MIX2,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE("High-Pass Filter Capture Switch",
+ JZ4760_CODEC_REG_CR4,
+ REG_CR4_ADC_HPF_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_pcm_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR6, JZ4760_CODEC_REG_GCR5,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR2, JZ4760_CODEC_REG_GCR1,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, out_tlv),
+};
+
+static int hpout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int val;
+ int err;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* unmute HP */
+ regmap_clear_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR1,
+ REG_CR1_HP_MUTE);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for ramp-up complete (RUP) */
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4760_CODEC_REG_IFR,
+ val, val & REG_IFR_RUP,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RUP timeout: %d", err);
+ return err;
+ }
+
+ /* clear RUP flag */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_IFR,
+ REG_IFR_RUP);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* mute HP */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR1,
+ REG_CR1_HP_MUTE);
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4760_CODEC_REG_IFR,
+ val, val & REG_IFR_RDO,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RDO timeout: %d", err);
+ return err;
+ }
+
+ /* clear RDO flag */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_IFR,
+ REG_IFR_RDO);
+
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const jz4760_codec_hp_texts[] = {
+ "PCM", "Line In", "Mic 1", "Mic 2"
+};
+
+static const unsigned int jz4760_codec_hp_values[] = { 3, 2, 0, 1 };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_hp_enum,
+ JZ4760_CODEC_REG_CR1,
+ REG_CR1_OUTSEL_OFFSET,
+ REG_CR1_OUTSEL_MASK >> REG_CR1_OUTSEL_OFFSET,
+ jz4760_codec_hp_texts,
+ jz4760_codec_hp_values);
+static const struct snd_kcontrol_new jz4760_codec_hp_source =
+ SOC_DAPM_ENUM("Route", jz4760_codec_hp_enum);
+
+static const char * const jz4760_codec_cap_texts[] = {
+ "Line In", "Mic 1", "Mic 2"
+};
+
+static const unsigned int jz4760_codec_cap_values[] = { 2, 0, 1 };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_cap_enum,
+ JZ4760_CODEC_REG_CR3,
+ REG_CR3_ADC_INSEL_OFFSET,
+ REG_CR3_ADC_INSEL_MASK >> REG_CR3_ADC_INSEL_OFFSET,
+ jz4760_codec_cap_texts,
+ jz4760_codec_cap_values);
+static const struct snd_kcontrol_new jz4760_codec_cap_source =
+ SOC_DAPM_ENUM("Route", jz4760_codec_cap_enum);
+
+static const struct snd_kcontrol_new jz4760_codec_mic_controls[] = {
+ SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4760_CODEC_REG_CR3,
+ REG_CR3_MICSTEREO_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_line_out_switch =
+ SOC_DAPM_SINGLE("Switch", JZ4760_CODEC_REG_CR1,
+ REG_CR1_LO_MUTE_OFFSET, 0, 0);
+static const struct snd_kcontrol_new jz4760_codec_btl_out_switch =
+ SOC_DAPM_SINGLE("Switch", JZ4760_CODEC_REG_CR1,
+ REG_CR1_BTL_MUTE_OFFSET, 0, 0);
+
+static const struct snd_soc_dapm_widget jz4760_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("HP Out", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_HP_OFFSET, 1, NULL, 0, hpout_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SWITCH("Line Out", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_LOUT_OFFSET, 1,
+ &jz4760_codec_line_out_switch),
+
+ SND_SOC_DAPM_SWITCH("BTL Out", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_BTL_OFFSET, 1,
+ &jz4760_codec_btl_out_switch),
+
+ SND_SOC_DAPM_PGA("Line In", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_LINE_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
+ &jz4760_codec_hp_source),
+
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &jz4760_codec_cap_source),
+
+ SND_SOC_DAPM_PGA("Mic 1", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_MIC1_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Mic 2", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_MIC2_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Mic Diff", JZ4760_CODEC_REG_CR3,
+ REG_CR3_MICDIFF_OFFSET, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
+ jz4760_codec_mic_controls,
+ ARRAY_SIZE(jz4760_codec_mic_controls)),
+
+ SND_SOC_DAPM_PGA("Line In Bypass", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_BYPASS_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_ADC_OFFSET, 1),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", JZ4760_CODEC_REG_PMR2,
+ REG_PMR2_SB_DAC_OFFSET, 1),
+
+ SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
+ jz4760_codec_pcm_playback_controls,
+ ARRAY_SIZE(jz4760_codec_pcm_playback_controls)),
+
+ SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
+ jz4760_codec_hp_playback_controls,
+ ARRAY_SIZE(jz4760_codec_hp_playback_controls)),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4760_CODEC_REG_PMR1,
+ REG_PMR1_SB_MICBIAS_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_OUTPUT("BTLP"),
+ SND_SOC_DAPM_OUTPUT("BTLN"),
+
+ SND_SOC_DAPM_OUTPUT("SYSCLK"),
+};
+
+/* Unconditional routes. */
+static const struct snd_soc_dapm_route jz4760_codec_dapm_routes[] = {
+ { "Mic 1", NULL, "MIC1P" },
+ { "Mic Diff", NULL, "MIC1N" },
+ { "Mic 1", NULL, "Mic Diff" },
+ { "Mic 2", NULL, "MIC2P" },
+ { "Mic Diff", NULL, "MIC2N" },
+ { "Mic 2", NULL, "Mic Diff" },
+
+ { "Line In", NULL, "LLINEIN" },
+ { "Line In", NULL, "RLINEIN" },
+
+ { "Mic", "Stereo Capture Switch", "Mic 1" },
+ { "Mic", "Stereo Capture Switch", "Mic 2" },
+ { "Headphones Source", "Mic 1", "Mic" },
+ { "Headphones Source", "Mic 2", "Mic" },
+ { "Capture Source", "Mic 1", "Mic" },
+ { "Capture Source", "Mic 2", "Mic" },
+
+ { "Capture Source", "Line In", "Line In" },
+ { "Capture Source", "Mic 1", "Mic 1" },
+ { "Capture Source", "Mic 2", "Mic 2" },
+ { "ADC", NULL, "Capture Source" },
+
+ { "Line In Bypass", NULL, "Line In" },
+
+ { "Headphones Source", "Mic 1", "Mic 1" },
+ { "Headphones Source", "Mic 2", "Mic 2" },
+ { "Headphones Source", "Line In", "Line In Bypass" },
+ { "Headphones Source", "PCM", "Headphones Playback" },
+ { "HP Out", NULL, "Headphones Source" },
+
+ { "LHPOUT", NULL, "HP Out" },
+ { "RHPOUT", NULL, "HP Out" },
+ { "Line Out", "Switch", "HP Out" },
+
+ { "LOUT", NULL, "Line Out" },
+ { "ROUT", NULL, "Line Out" },
+ { "BTL Out", "Switch", "Line Out" },
+
+ { "BTLP", NULL, "BTL Out"},
+ { "BTLN", NULL, "BTL Out"},
+
+ { "PCM Playback", "Volume", "DAC" },
+ { "Headphones Playback", "Volume", "PCM Playback" },
+
+ { "SYSCLK", NULL, "DAC" },
+};
+
+static void jz4760_codec_codec_init_regs(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ /* Collect updates for later sending. */
+ regcache_cache_only(regmap, true);
+
+ /* default Amp output to PCM */
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_CR1, REG_CR1_OUTSEL_MASK);
+
+ /* Disable stereo mic */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR3,
+ BIT(REG_CR3_MICSTEREO_OFFSET));
+
+ /* Set mic 1 as default source for ADC */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR3,
+ REG_CR3_ADC_INSEL_MASK);
+
+ /* ADC/DAC: serial + i2s */
+ regmap_set_bits(regmap, JZ4760_CODEC_REG_AICR,
+ REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S |
+ REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
+
+ /* The generated IRQ is a high level */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
+ regmap_update_bits(regmap, JZ4760_CODEC_REG_ICR, REG_ICR_ALL_MASK,
+ REG_ICR_JACK_MASK | REG_ICR_RUP_MASK |
+ REG_ICR_RDO_MASK | REG_ICR_GUP_MASK |
+ REG_ICR_GDO_MASK);
+
+ /* 12M oscillator */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CCR1, REG_CCR1_CRYSTAL_MASK);
+
+ /* 0: 16ohm/220uF, 1: 10kohm/1uF */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR1, REG_CR1_HP_LOAD);
+
+ /* default to NOMAD */
+ regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR2,
+ REG_CR2_DAC_NOMAD);
+
+ /* disable automatic gain */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_AGC1, REG_AGC1_EN);
+
+ /* Independent L/R DAC gain control */
+ regmap_clear_bits(regmap, JZ4760_CODEC_REG_GCR5,
+ REG_GCR_RL);
+
+ /* Send collected updates. */
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+}
+
+static int jz4760_codec_codec_probe(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_prepare_enable(jz_codec->clk);
+
+ jz4760_codec_codec_init_regs(codec);
+
+ return 0;
+}
+
+static void jz4760_codec_codec_remove(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_disable_unprepare(jz_codec->clk);
+}
+
+static const struct snd_soc_component_driver jz4760_codec_soc_codec_dev = {
+ .probe = jz4760_codec_codec_probe,
+ .remove = jz4760_codec_codec_remove,
+ .set_bias_level = jz4760_codec_set_bias_level,
+ .controls = jz4760_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(jz4760_codec_snd_controls),
+ .dapm_widgets = jz4760_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4760_codec_dapm_widgets),
+ .dapm_routes = jz4760_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4760_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .use_pmdown_time = 1,
+};
+
+static const unsigned int jz4760_codec_sample_rates[] = {
+ 96000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000,
+ 11025, 9600, 8000,
+};
+
+static int jz4760_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate, bit_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_width = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ bit_width = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bit_width = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (rate = 0; rate < ARRAY_SIZE(jz4760_codec_sample_rates); rate++) {
+ if (jz4760_codec_sample_rates[rate] == params_rate(params))
+ break;
+ }
+
+ if (rate == ARRAY_SIZE(jz4760_codec_sample_rates))
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_AICR,
+ REG_AICR_DAC_ADWL_MASK,
+ FIELD_PREP(REG_AICR_DAC_ADWL_MASK, bit_width));
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_CCR2,
+ REG_CCR2_DAC_FREQ_MASK,
+ FIELD_PREP(REG_CCR2_DAC_FREQ_MASK, rate));
+ } else {
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_AICR,
+ REG_AICR_ADC_ADWL_MASK,
+ FIELD_PREP(REG_AICR_ADC_ADWL_MASK, bit_width));
+ regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_CCR2,
+ REG_CCR2_ADC_FREQ_MASK,
+ FIELD_PREP(REG_CCR2_ADC_FREQ_MASK, rate));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4760_codec_dai_ops = {
+ .startup = jz4760_codec_startup,
+ .shutdown = jz4760_codec_shutdown,
+ .hw_params = jz4760_codec_hw_params,
+ .trigger = jz4760_codec_pcm_trigger,
+ .mute_stream = jz4760_codec_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver jz4760_codec_dai = {
+ .name = "jz4760-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .ops = &jz4760_codec_dai_ops,
+};
+
+static bool jz4760_codec_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == JZ4760_CODEC_REG_SR || reg == JZ4760_CODEC_REG_IFR;
+}
+
+static bool jz4760_codec_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JZ4760_CODEC_REG_SR:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int jz4760_codec_io_wait(struct jz_codec *codec)
+{
+ u32 reg;
+
+ return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
+ !(reg & ICDC_RGADW_RGWR),
+ 1000, 1 * USEC_PER_SEC);
+}
+
+static int jz4760_codec_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct jz_codec *codec = context;
+ unsigned int i;
+ u32 tmp;
+ int ret;
+
+ ret = jz4760_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ tmp = readl(codec->base + ICDC_RGADW_OFFSET);
+ tmp &= ~ICDC_RGADW_RGADDR_MASK;
+ tmp |= FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg);
+ writel(tmp, codec->base + ICDC_RGADW_OFFSET);
+
+ /* wait 6+ cycles */
+ for (i = 0; i < 6; i++)
+ *val = readl(codec->base + ICDC_RGDATA_OFFSET) &
+ ICDC_RGDATA_RGDOUT_MASK;
+
+ return 0;
+}
+
+static int jz4760_codec_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct jz_codec *codec = context;
+ int ret;
+
+ ret = jz4760_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ writel(ICDC_RGADW_RGWR | FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg) | val,
+ codec->base + ICDC_RGADW_OFFSET);
+
+ ret = jz4760_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u8 jz4760_codec_reg_defaults[] = {
+ 0x00, 0xFC, 0x1B, 0x20, 0x00, 0x80, 0x00, 0x00,
+ 0xFF, 0x1F, 0x3F, 0x00, 0x06, 0x06, 0x06, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x07, 0x44,
+ 0x1F, 0x00, 0x00, 0x00
+};
+
+static const struct regmap_config jz4760_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .max_register = JZ4760_CODEC_REG_MIX2,
+ .volatile_reg = jz4760_codec_volatile,
+ .writeable_reg = jz4760_codec_writeable,
+
+ .reg_read = jz4760_codec_reg_read,
+ .reg_write = jz4760_codec_reg_write,
+
+ .reg_defaults_raw = jz4760_codec_reg_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(jz4760_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int jz4760_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_codec *codec;
+ int ret;
+
+ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->dev = dev;
+
+ codec->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(codec->base))
+ return PTR_ERR(codec->base);
+
+ codec->regmap = devm_regmap_init(dev, NULL, codec,
+ &jz4760_codec_regmap_config);
+ if (IS_ERR(codec->regmap))
+ return PTR_ERR(codec->regmap);
+
+ codec->clk = devm_clk_get(dev, "aic");
+ if (IS_ERR(codec->clk))
+ return PTR_ERR(codec->clk);
+
+ platform_set_drvdata(pdev, codec);
+
+ ret = devm_snd_soc_register_component(dev, &jz4760_codec_soc_codec_dev,
+ &jz4760_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id jz4760_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4760-codec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jz4760_codec_of_matches);
+
+static struct platform_driver jz4760_codec_driver = {
+ .probe = jz4760_codec_probe,
+ .driver = {
+ .name = "jz4760-codec",
+ .of_match_table = jz4760_codec_of_matches,
+ },
+};
+module_platform_driver(jz4760_codec_driver);
+
+MODULE_DESCRIPTION("JZ4760 SoC internal codec driver");
+MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c
new file mode 100644
index 000000000000..6b86d47028d7
--- /dev/null
+++ b/sound/soc/codecs/jz4770.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Ingenic JZ4770 CODEC driver
+//
+// Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org>
+// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define ICDC_RGADW_OFFSET 0x00
+#define ICDC_RGDATA_OFFSET 0x04
+
+/* ICDC internal register access control register(RGADW) */
+#define ICDC_RGADW_RGWR BIT(16)
+
+#define ICDC_RGADW_RGADDR_OFFSET 8
+#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
+
+#define ICDC_RGADW_RGDIN_OFFSET 0
+#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
+
+/* ICDC internal register data output register (RGDATA)*/
+#define ICDC_RGDATA_IRQ BIT(8)
+
+#define ICDC_RGDATA_RGDOUT_OFFSET 0
+#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
+
+/* Internal register space, accessed through regmap */
+enum {
+ JZ4770_CODEC_REG_SR,
+ JZ4770_CODEC_REG_AICR_DAC,
+ JZ4770_CODEC_REG_AICR_ADC,
+ JZ4770_CODEC_REG_CR_LO,
+ JZ4770_CODEC_REG_CR_HP,
+
+ JZ4770_CODEC_REG_MISSING_REG1,
+
+ JZ4770_CODEC_REG_CR_DAC,
+ JZ4770_CODEC_REG_CR_MIC,
+ JZ4770_CODEC_REG_CR_LI,
+ JZ4770_CODEC_REG_CR_ADC,
+ JZ4770_CODEC_REG_CR_MIX,
+ JZ4770_CODEC_REG_CR_VIC,
+ JZ4770_CODEC_REG_CCR,
+ JZ4770_CODEC_REG_FCR_DAC,
+ JZ4770_CODEC_REG_FCR_ADC,
+ JZ4770_CODEC_REG_ICR,
+ JZ4770_CODEC_REG_IMR,
+ JZ4770_CODEC_REG_IFR,
+ JZ4770_CODEC_REG_GCR_HPL,
+ JZ4770_CODEC_REG_GCR_HPR,
+ JZ4770_CODEC_REG_GCR_LIBYL,
+ JZ4770_CODEC_REG_GCR_LIBYR,
+ JZ4770_CODEC_REG_GCR_DACL,
+ JZ4770_CODEC_REG_GCR_DACR,
+ JZ4770_CODEC_REG_GCR_MIC1,
+ JZ4770_CODEC_REG_GCR_MIC2,
+ JZ4770_CODEC_REG_GCR_ADCL,
+ JZ4770_CODEC_REG_GCR_ADCR,
+
+ JZ4770_CODEC_REG_MISSING_REG2,
+
+ JZ4770_CODEC_REG_GCR_MIXADC,
+ JZ4770_CODEC_REG_GCR_MIXDAC,
+ JZ4770_CODEC_REG_AGC1,
+ JZ4770_CODEC_REG_AGC2,
+ JZ4770_CODEC_REG_AGC3,
+ JZ4770_CODEC_REG_AGC4,
+ JZ4770_CODEC_REG_AGC5,
+};
+
+#define REG_AICR_DAC_ADWL_OFFSET 6
+#define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET)
+#define REG_AICR_DAC_SERIAL BIT(1)
+#define REG_AICR_DAC_I2S BIT(0)
+
+#define REG_AICR_ADC_ADWL_OFFSET 6
+#define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET)
+#define REG_AICR_ADC_SERIAL BIT(1)
+#define REG_AICR_ADC_I2S BIT(0)
+
+#define REG_CR_LO_MUTE_OFFSET 7
+#define REG_CR_LO_SB_OFFSET 4
+#define REG_CR_LO_SEL_OFFSET 0
+#define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET)
+
+#define REG_CR_HP_MUTE BIT(7)
+#define REG_CR_HP_LOAD BIT(6)
+#define REG_CR_HP_SB_OFFSET 4
+#define REG_CR_HP_SB_HPCM_OFFSET 3
+#define REG_CR_HP_SEL_OFFSET 0
+#define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET)
+
+#define REG_CR_DAC_MUTE BIT(7)
+#define REG_CR_DAC_MONO BIT(6)
+#define REG_CR_DAC_LEFT_ONLY BIT(5)
+#define REG_CR_DAC_SB_OFFSET 4
+#define REG_CR_DAC_LRSWAP BIT(3)
+
+#define REG_CR_MIC_STEREO_OFFSET 7
+#define REG_CR_MIC_IDIFF_OFFSET 6
+#define REG_CR_MIC_SB_MIC2_OFFSET 5
+#define REG_CR_MIC_SB_MIC1_OFFSET 4
+#define REG_CR_MIC_BIAS_V0_OFFSET 1
+#define REG_CR_MIC_BIAS_SB_OFFSET 0
+
+#define REG_CR_LI_LIBY_OFFSET 4
+#define REG_CR_LI_SB_OFFSET 0
+
+#define REG_CR_ADC_DMIC_SEL BIT(7)
+#define REG_CR_ADC_MONO BIT(6)
+#define REG_CR_ADC_LEFT_ONLY BIT(5)
+#define REG_CR_ADC_SB_OFFSET 4
+#define REG_CR_ADC_LRSWAP BIT(3)
+#define REG_CR_ADC_IN_SEL_OFFSET 0
+#define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET)
+
+#define REG_CR_VIC_SB_SLEEP BIT(1)
+#define REG_CR_VIC_SB BIT(0)
+
+#define REG_CCR_CRYSTAL_OFFSET 0
+#define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET)
+
+#define REG_FCR_DAC_FREQ_OFFSET 0
+#define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET)
+
+#define REG_FCR_ADC_FREQ_OFFSET 0
+#define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET)
+
+#define REG_ICR_INT_FORM_OFFSET 6
+#define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET)
+
+#define REG_IMR_ALL_MASK (0x7f)
+#define REG_IMR_SCLR_MASK BIT(6)
+#define REG_IMR_JACK_MASK BIT(5)
+#define REG_IMR_SCMC_MASK BIT(4)
+#define REG_IMR_RUP_MASK BIT(3)
+#define REG_IMR_RDO_MASK BIT(2)
+#define REG_IMR_GUP_MASK BIT(1)
+#define REG_IMR_GDO_MASK BIT(0)
+
+#define REG_IFR_ALL_MASK (0x7f)
+#define REG_IFR_SCLR BIT(6)
+#define REG_IFR_JACK BIT(5)
+#define REG_IFR_SCMC BIT(4)
+#define REG_IFR_RUP BIT(3)
+#define REG_IFR_RDO BIT(2)
+#define REG_IFR_GUP BIT(1)
+#define REG_IFR_GDO BIT(0)
+
+#define REG_GCR_HPL_LRGO BIT(7)
+
+#define REG_GCR_DACL_RLGOD BIT(7)
+
+#define REG_GCR_GAIN_OFFSET 0
+#define REG_GCR_GAIN_MAX 0x1f
+
+#define REG_GCR_MIC_GAIN_OFFSET 0
+#define REG_GCR_MIC_GAIN_MAX 5
+
+#define REG_GCR_ADC_GAIN_OFFSET 0
+#define REG_GCR_ADC_GAIN_MAX 23
+
+#define REG_AGC1_EN BIT(7)
+
+/* codec private data */
+struct jz_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static int jz4770_codec_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ /* Reset all interrupt flags. */
+ regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK);
+
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB);
+ msleep(250);
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB_SLEEP);
+ msleep(400);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB_SLEEP);
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
+ REG_CR_VIC_SB);
+ fallthrough;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int jz4770_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+
+ /*
+ * SYSCLK output from the codec to the AIC is required to keep the
+ * DMA transfer going during playback when all audible outputs have
+ * been disabled.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
+
+ return 0;
+}
+
+static void jz4770_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_disable_pin(dapm, "SYSCLK");
+}
+
+
+static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_ON);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* do nothing */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
+ unsigned int val;
+ int change, err;
+
+ change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC,
+ REG_CR_DAC_MUTE,
+ mute ? REG_CR_DAC_MUTE : 0);
+ if (change == 1) {
+ regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val);
+
+ if (val & BIT(REG_CR_DAC_SB_OFFSET))
+ return 1;
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4770_CODEC_REG_IFR,
+ val, val & gain_bit,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev,
+ "Timeout while setting digital mute: %d", err);
+ return err;
+ }
+
+ /* clear GUP/GDO flag */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
+ gain_bit);
+ }
+
+ return 0;
+}
+
+/* unit: 0.01dB */
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600);
+static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
+
+/* Unconditional controls. */
+static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
+ /* record gain control */
+ SOC_DOUBLE_R_TLV("PCM Capture Volume",
+ JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR,
+ REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX,
+ 0, adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
+ JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
+
+ SOC_SINGLE_TLV("Mixer Capture Volume",
+ JZ4770_CODEC_REG_GCR_MIXADC,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE_TLV("Mixer Playback Volume",
+ JZ4770_CODEC_REG_GCR_MIXDAC,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+};
+
+static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_DACR,
+ JZ4770_CODEC_REG_GCR_DACL, REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_HPR,
+ JZ4770_CODEC_REG_GCR_HPL, REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1, out_tlv),
+};
+
+static int hpout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ unsigned int val;
+ int err;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* unmute HP */
+ regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_MUTE);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for ramp-up complete (RUP) */
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4770_CODEC_REG_IFR,
+ val, val & REG_IFR_RUP,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RUP timeout: %d", err);
+ return err;
+ }
+
+ /* clear RUP flag */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
+ REG_IFR_RUP);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* mute HP */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_MUTE);
+
+ err = regmap_read_poll_timeout(jz_codec->regmap,
+ JZ4770_CODEC_REG_IFR,
+ val, val & REG_IFR_RDO,
+ 1000, 1 * USEC_PER_SEC);
+ if (err) {
+ dev_err(jz_codec->dev, "RDO timeout: %d", err);
+ return err;
+ }
+
+ /* clear RDO flag */
+ regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
+ REG_IFR_RDO);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int adc_poweron_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event == SND_SOC_DAPM_POST_PMU)
+ msleep(1000);
+
+ return 0;
+}
+
+static const char * const jz4770_codec_hp_texts[] = {
+ "PCM", "Line In", "Mic 1", "Mic 2"
+};
+static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 };
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum,
+ JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_SEL_OFFSET,
+ REG_CR_HP_SEL_MASK,
+ jz4770_codec_hp_texts,
+ jz4770_codec_hp_values);
+static const struct snd_kcontrol_new jz4770_codec_hp_source =
+ SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum,
+ JZ4770_CODEC_REG_CR_LO,
+ REG_CR_LO_SEL_OFFSET,
+ REG_CR_LO_SEL_MASK,
+ jz4770_codec_hp_texts,
+ jz4770_codec_hp_values);
+static const struct snd_kcontrol_new jz4770_codec_lo_source =
+ SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum);
+
+static const char * const jz4770_codec_cap_texts[] = {
+ "Line In", "Mic 1", "Mic 2"
+};
+static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 };
+static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum,
+ JZ4770_CODEC_REG_CR_ADC,
+ REG_CR_ADC_IN_SEL_OFFSET,
+ REG_CR_ADC_IN_SEL_MASK,
+ jz4770_codec_cap_texts,
+ jz4770_codec_cap_values);
+static const struct snd_kcontrol_new jz4770_codec_cap_source =
+ SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum);
+
+static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = {
+ SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_STEREO_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO,
+ REG_CR_LO_SB_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO,
+ REG_CR_LO_MUTE_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI,
+ REG_CR_LI_SB_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
+ &jz4770_codec_hp_source),
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &jz4770_codec_cap_source),
+ SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0,
+ &jz4770_codec_lo_source),
+
+ SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
+ jz4770_codec_mic_controls,
+ ARRAY_SIZE(jz4770_codec_mic_controls)),
+
+ SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI,
+ REG_CR_LI_LIBY_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC,
+ REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC,
+ REG_CR_DAC_SB_OFFSET, 1),
+
+ SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
+ jz4770_codec_pcm_playback_controls,
+ ARRAY_SIZE(jz4770_codec_pcm_playback_controls)),
+ SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
+ jz4770_codec_hp_playback_controls,
+ ARRAY_SIZE(jz4770_codec_hp_playback_controls)),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC,
+ REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Cap-less", JZ4770_CODEC_REG_CR_HP,
+ REG_CR_HP_SB_HPCM_OFFSET, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_OUTPUT("SYSCLK"),
+};
+
+/* Unconditional routes. */
+static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = {
+ { "Mic 1", NULL, "MIC1P" },
+ { "Mic Diff", NULL, "MIC1N" },
+ { "Mic 1", NULL, "Mic Diff" },
+ { "Mic 2", NULL, "MIC2P" },
+ { "Mic Diff", NULL, "MIC2N" },
+ { "Mic 2", NULL, "Mic Diff" },
+
+ { "Line In", NULL, "LLINEIN" },
+ { "Line In", NULL, "RLINEIN" },
+
+ { "Mic", "Stereo Capture Switch", "Mic 1" },
+ { "Mic", "Stereo Capture Switch", "Mic 2" },
+ { "Headphones Source", "Mic 1", "Mic" },
+ { "Headphones Source", "Mic 2", "Mic" },
+ { "Capture Source", "Mic 1", "Mic" },
+ { "Capture Source", "Mic 2", "Mic" },
+
+ { "Headphones Source", "Mic 1", "Mic 1" },
+ { "Headphones Source", "Mic 2", "Mic 2" },
+ { "Headphones Source", "Line In", "Line In Bypass" },
+ { "Headphones Source", "PCM", "Headphones Playback" },
+ { "HP Out", NULL, "Headphones Source" },
+
+ { "Capture Source", "Line In", "Line In" },
+ { "Capture Source", "Mic 1", "Mic 1" },
+ { "Capture Source", "Mic 2", "Mic 2" },
+ { "ADC", NULL, "Capture Source" },
+
+ { "Line In Bypass", NULL, "Line In" },
+ { "Line Out Source", "Line In", "Line In Bypass" },
+ { "Line Out Source", "PCM", "PCM Playback" },
+
+ { "LHPOUT", NULL, "HP Out"},
+ { "RHPOUT", NULL, "HP Out"},
+
+ { "Line Out", NULL, "Line Out Source" },
+ { "Line Out Switch 2", NULL, "Line Out" },
+
+ { "LOUT", NULL, "Line Out Switch 2"},
+ { "ROUT", NULL, "Line Out Switch 2"},
+
+ { "PCM Playback", "Volume", "DAC" },
+ { "Headphones Playback", "Volume", "PCM Playback" },
+
+ { "SYSCLK", NULL, "DAC" },
+};
+
+static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jz_codec->regmap;
+
+ /* Collect updates for later sending. */
+ regcache_cache_only(regmap, true);
+
+ /* default HP output to PCM */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK);
+
+ /* default line output to PCM */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK);
+
+ /* Disable stereo mic */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC,
+ BIT(REG_CR_MIC_STEREO_OFFSET));
+
+ /* Set mic 1 as default source for ADC */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
+ REG_CR_ADC_IN_SEL_MASK);
+
+ /* ADC/DAC: serial + i2s */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC,
+ REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S);
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC,
+ REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
+
+ /* The generated IRQ is a high level */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
+ regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK,
+ REG_IMR_JACK_MASK | REG_IMR_RUP_MASK |
+ REG_IMR_RDO_MASK | REG_IMR_GUP_MASK |
+ REG_IMR_GDO_MASK);
+
+ /* 12M oscillator */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK);
+
+ /* 0: 16ohm/220uF, 1: 10kohm/1uF */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD);
+
+ /* disable automatic gain */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN);
+
+ /* Disable DAC lrswap */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP);
+
+ /* Independent L/R DAC gain control */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL,
+ REG_GCR_DACL_RLGOD);
+
+ /* Disable ADC lrswap */
+ regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP);
+
+ /* default to cap-less mode(0) */
+ regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP,
+ BIT(REG_CR_HP_SB_HPCM_OFFSET));
+
+ /* Send collected updates. */
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+}
+
+static int jz4770_codec_codec_probe(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_prepare_enable(jz_codec->clk);
+
+ jz4770_codec_codec_init_regs(codec);
+
+ return 0;
+}
+
+static void jz4770_codec_codec_remove(struct snd_soc_component *codec)
+{
+ struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
+
+ clk_disable_unprepare(jz_codec->clk);
+}
+
+static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = {
+ .probe = jz4770_codec_codec_probe,
+ .remove = jz4770_codec_codec_remove,
+ .set_bias_level = jz4770_codec_set_bias_level,
+ .controls = jz4770_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls),
+ .dapm_widgets = jz4770_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets),
+ .dapm_routes = jz4770_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes),
+ .suspend_bias_off = 1,
+ .use_pmdown_time = 1,
+};
+
+static const unsigned int jz4770_codec_sample_rates[] = {
+ 96000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000,
+ 11025, 9600, 8000,
+};
+
+static int jz4770_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate, bit_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_width = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ bit_width = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bit_width = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) {
+ if (jz4770_codec_sample_rates[rate] == params_rate(params))
+ break;
+ }
+
+ if (rate == ARRAY_SIZE(jz4770_codec_sample_rates))
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC,
+ REG_AICR_DAC_ADWL_MASK,
+ bit_width << REG_AICR_DAC_ADWL_OFFSET);
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC,
+ REG_FCR_DAC_FREQ_MASK,
+ rate << REG_FCR_DAC_FREQ_OFFSET);
+ } else {
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC,
+ REG_AICR_ADC_ADWL_MASK,
+ bit_width << REG_AICR_ADC_ADWL_OFFSET);
+ regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC,
+ REG_FCR_ADC_FREQ_MASK,
+ rate << REG_FCR_ADC_FREQ_OFFSET);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4770_codec_dai_ops = {
+ .startup = jz4770_codec_startup,
+ .shutdown = jz4770_codec_shutdown,
+ .hw_params = jz4770_codec_hw_params,
+ .trigger = jz4770_codec_pcm_trigger,
+ .mute_stream = jz4770_codec_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver jz4770_codec_dai = {
+ .name = "jz4770-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = JZ_CODEC_FORMATS,
+ },
+ .ops = &jz4770_codec_dai_ops,
+};
+
+static bool jz4770_codec_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR;
+}
+
+static bool jz4770_codec_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JZ4770_CODEC_REG_MISSING_REG1:
+ case JZ4770_CODEC_REG_MISSING_REG2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool jz4770_codec_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JZ4770_CODEC_REG_SR:
+ case JZ4770_CODEC_REG_MISSING_REG1:
+ case JZ4770_CODEC_REG_MISSING_REG2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int jz4770_codec_io_wait(struct jz_codec *codec)
+{
+ u32 reg;
+
+ return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
+ !(reg & ICDC_RGADW_RGWR),
+ 1000, 1 * USEC_PER_SEC);
+}
+
+static int jz4770_codec_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct jz_codec *codec = context;
+ unsigned int i;
+ u32 tmp;
+ int ret;
+
+ ret = jz4770_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ tmp = readl(codec->base + ICDC_RGADW_OFFSET);
+ tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
+ | (reg << ICDC_RGADW_RGADDR_OFFSET);
+ writel(tmp, codec->base + ICDC_RGADW_OFFSET);
+
+ /* wait 6+ cycles */
+ for (i = 0; i < 6; i++)
+ *val = readl(codec->base + ICDC_RGDATA_OFFSET) &
+ ICDC_RGDATA_RGDOUT_MASK;
+
+ return 0;
+}
+
+static int jz4770_codec_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct jz_codec *codec = context;
+ int ret;
+
+ ret = jz4770_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
+ codec->base + ICDC_RGADW_OFFSET);
+
+ ret = jz4770_codec_io_wait(codec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u8 jz4770_codec_reg_defaults[] = {
+ 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1,
+ 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00,
+ 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34,
+ 0x07, 0x44, 0x1F, 0x00
+};
+
+static const struct regmap_config jz4770_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .max_register = JZ4770_CODEC_REG_AGC5,
+ .volatile_reg = jz4770_codec_volatile,
+ .readable_reg = jz4770_codec_readable,
+ .writeable_reg = jz4770_codec_writeable,
+
+ .reg_read = jz4770_codec_reg_read,
+ .reg_write = jz4770_codec_reg_write,
+
+ .reg_defaults_raw = jz4770_codec_reg_defaults,
+ .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int jz4770_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_codec *codec;
+ int ret;
+
+ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->dev = dev;
+
+ codec->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(codec->base))
+ return PTR_ERR(codec->base);
+
+ codec->regmap = devm_regmap_init(dev, NULL, codec,
+ &jz4770_codec_regmap_config);
+ if (IS_ERR(codec->regmap))
+ return PTR_ERR(codec->regmap);
+
+ codec->clk = devm_clk_get(dev, "aic");
+ if (IS_ERR(codec->clk))
+ return PTR_ERR(codec->clk);
+
+ platform_set_drvdata(pdev, codec);
+
+ ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev,
+ &jz4770_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id jz4770_codec_of_matches[] = {
+ { .compatible = "ingenic,jz4770-codec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches);
+
+static struct platform_driver jz4770_codec_driver = {
+ .probe = jz4770_codec_probe,
+ .driver = {
+ .name = "jz4770-codec",
+ .of_match_table = jz4770_codec_of_matches,
+ },
+};
+module_platform_driver(jz4770_codec_driver);
+
+MODULE_DESCRIPTION("JZ4770 SoC internal codec driver");
+MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
deleted file mode 100644
index 5353af58862c..000000000000
--- a/sound/soc/codecs/l3.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * L3 code
- *
- * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- *
- * based on:
- *
- * L3 bus algorithm module.
- *
- * Copyright (C) 2001 Russell King, All Rights Reserved.
- *
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-
-#include <sound/l3.h>
-
-/*
- * Send one byte of data to the chip. Data is latched into the chip on
- * the rising edge of the clock.
- */
-static void sendbyte(struct l3_pins *adap, unsigned int byte)
-{
- int i;
-
- for (i = 0; i < 8; i++) {
- adap->setclk(0);
- udelay(adap->data_hold);
- adap->setdat(byte & 1);
- udelay(adap->data_setup);
- adap->setclk(1);
- udelay(adap->clock_high);
- byte >>= 1;
- }
-}
-
-/*
- * Send a set of bytes to the chip. We need to pulse the MODE line
- * between each byte, but never at the start nor at the end of the
- * transfer.
- */
-static void sendbytes(struct l3_pins *adap, const u8 *buf,
- int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (i) {
- udelay(adap->mode_hold);
- adap->setmode(0);
- udelay(adap->mode);
- }
- adap->setmode(1);
- udelay(adap->mode_setup);
- sendbyte(adap, buf[i]);
- }
-}
-
-int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
-{
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(1);
- udelay(adap->mode);
-
- adap->setmode(0);
- udelay(adap->mode_setup);
- sendbyte(adap, addr);
- udelay(adap->mode_hold);
-
- sendbytes(adap, data, len);
-
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(0);
-
- return len;
-}
-EXPORT_SYMBOL_GPL(l3_write);
-
-MODULE_DESCRIPTION("L3 bit-banging driver");
-MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index 558de1053f73..26cdb750cbca 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* LM4857 AMP driver
*
@@ -5,12 +6,6 @@
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/init.h>
@@ -100,7 +95,7 @@ static const struct snd_soc_dapm_route lm4857_routes[] = {
{ "EP", "Earpiece", "Mode" },
};
-static struct snd_soc_component_driver lm4857_component_driver = {
+static const struct snd_soc_component_driver lm4857_component_driver = {
.controls = lm4857_controls,
.num_controls = ARRAY_SIZE(lm4857_controls),
.dapm_widgets = lm4857_dapm_widgets,
@@ -120,8 +115,7 @@ static const struct regmap_config lm4857_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
};
-static int lm4857_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm4857_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -134,7 +128,7 @@ static int lm4857_i2c_probe(struct i2c_client *i2c,
}
static const struct i2c_device_id lm4857_i2c_id[] = {
- { "lm4857", 0 },
+ { "lm4857" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 9af5640e3446..043030509795 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* lm49453.c - LM49453 ALSA Soc Audio driver
*
* Copyright (c) 2012 Texas Instruments, Inc
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
* Initially based on sound/soc/codecs/wm8350.c
*/
@@ -1110,7 +1107,7 @@ 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 snd_soc_component *component = dai->component;
u16 clk_div = 0;
/* Setting DAC clock dividers based on substream sample rate. */
@@ -1134,32 +1131,32 @@ static int lm49453_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
- snd_soc_write(codec, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
+ snd_soc_component_write(component, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
+ snd_soc_component_write(component, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
return 0;
}
static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 aif_val;
int mode = 0;
int clk_phase = 0;
int clk_shift = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
aif_val = 0;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
break;
@@ -1185,11 +1182,11 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ snd_soc_component_update_bits(component, LM49453_P0_AUDIO_PORT1_BASIC_REG,
LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(0)|BIT(5),
(aif_val | mode | clk_phase));
- snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
+ snd_soc_component_write(component, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
return 0;
}
@@ -1197,7 +1194,7 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 pll_clk = 0;
switch (freq) {
@@ -1209,57 +1206,56 @@ static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
break;
case 48000:
case 32576:
- /* fll clk slection */
- pll_clk = BIT(4);
return 0;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
return 0;
}
-static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute)
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
(mute ? (BIT(1)|BIT(0)) : 0));
return 0;
}
-static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute)
+static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
(mute ? (BIT(3)|BIT(2)) : 0));
return 0;
}
-static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute)
+static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
(mute ? (BIT(5)|BIT(4)) : 0));
return 0;
}
-static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute)
+static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(4),
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(4),
(mute ? BIT(4) : 0));
return 0;
}
-static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute)
+static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
+ snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
(mute ? (BIT(7)|BIT(6)) : 0));
return 0;
}
-static int lm49453_set_bias_level(struct snd_soc_codec *codec,
+static int lm49453_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ struct lm49453_priv *lm49453 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1267,15 +1263,15 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
regcache_sync(lm49453->regmap);
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG,
LM49453_PMC_SETUP_CHIP_EN, LM49453_CHIP_EN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG,
LM49453_PMC_SETUP_CHIP_EN, 0);
break;
}
@@ -1291,35 +1287,40 @@ static const struct snd_soc_dai_ops lm49453_headset_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
- .digital_mute = lm49453_hp_mute,
+ .mute_stream = lm49453_hp_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
- .digital_mute = lm49453_ls_mute,
+ .mute_stream = lm49453_ls_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
- .digital_mute = lm49453_ha_mute,
+ .mute_stream = lm49453_ha_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops lm49453_ep_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
- .digital_mute = lm49453_ep_mute,
+ .mute_stream = lm49453_ep_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
- .digital_mute = lm49453_lo_mute,
+ .mute_stream = lm49453_lo_mute,
+ .no_capture_mute = 1,
};
/* LM49453 dai structure. */
@@ -1341,7 +1342,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
.formats = LM49453_FORMATS,
},
.ops = &lm49453_headset_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "LM49453 Speaker",
@@ -1389,15 +1390,16 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
- .set_bias_level = lm49453_set_bias_level,
- .controls = lm49453_snd_controls,
- .num_controls = ARRAY_SIZE(lm49453_snd_controls),
- .dapm_widgets = lm49453_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets),
- .dapm_routes = lm49453_audio_map,
- .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map),
- .idle_bias_off = true,
+static const struct snd_soc_component_driver soc_component_dev_lm49453 = {
+ .set_bias_level = lm49453_set_bias_level,
+ .controls = lm49453_snd_controls,
+ .num_controls = ARRAY_SIZE(lm49453_snd_controls),
+ .dapm_widgets = lm49453_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets),
+ .dapm_routes = lm49453_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config lm49453_regmap_config = {
@@ -1410,8 +1412,7 @@ static const struct regmap_config lm49453_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int lm49453_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm49453_i2c_probe(struct i2c_client *i2c)
{
struct lm49453_priv *lm49453;
int ret = 0;
@@ -1432,23 +1433,17 @@ static int lm49453_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_lm49453,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_lm49453,
lm49453_dai, ARRAY_SIZE(lm49453_dai));
if (ret < 0)
- dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int lm49453_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id lm49453_i2c_id[] = {
- { "lm49453", 0 },
+ { "lm49453" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
@@ -1458,7 +1453,6 @@ static struct i2c_driver lm49453_i2c_driver = {
.name = "lm49453",
},
.probe = lm49453_i2c_probe,
- .remove = lm49453_i2c_remove,
.id_table = lm49453_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h
index a63cfa5c0883..578a773e6fc9 100644
--- a/sound/soc/codecs/lm49453.h
+++ b/sound/soc/codecs/lm49453.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* lm49453.h - LM49453 ALSA Soc Audio drive
*
* Copyright (c) 2012 Texas Instruments, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
*/
#ifndef _LM49453_H
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
new file mode 100644
index 000000000000..a3d6318c9050
--- /dev/null
+++ b/sound/soc/codecs/lochnagar-sc.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Lochnagar sound card driver
+//
+// Copyright (c) 2017-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
+// Piotr Stankiewicz <piotrs@opensource.cirrus.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar1_regs.h>
+#include <linux/mfd/lochnagar2_regs.h>
+
+struct lochnagar_sc_priv {
+ struct clk *mclk;
+};
+
+static const struct snd_soc_dapm_widget lochnagar_sc_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+ SND_SOC_DAPM_LINE("USB Audio", NULL),
+};
+
+static const struct snd_soc_dapm_route lochnagar_sc_routes[] = {
+ { "Line Jack", NULL, "AIF1 Playback" },
+ { "AIF1 Capture", NULL, "Line Jack" },
+
+ { "USB Audio", NULL, "USB1 Playback" },
+ { "USB Audio", NULL, "USB2 Playback" },
+ { "USB1 Capture", NULL, "USB Audio" },
+ { "USB2 Capture", NULL, "USB Audio" },
+};
+
+static const unsigned int lochnagar_sc_chan_vals[] = {
+ 4, 8,
+};
+
+static const struct snd_pcm_hw_constraint_list lochnagar_sc_chan_constraint = {
+ .count = ARRAY_SIZE(lochnagar_sc_chan_vals),
+ .list = lochnagar_sc_chan_vals,
+};
+
+static const unsigned int lochnagar_sc_rate_vals[] = {
+ 8000, 16000, 24000, 32000, 48000, 96000, 192000,
+ 22050, 44100, 88200, 176400,
+};
+
+static const struct snd_pcm_hw_constraint_list lochnagar_sc_rate_constraint = {
+ .count = ARRAY_SIZE(lochnagar_sc_rate_vals),
+ .list = lochnagar_sc_rate_vals,
+};
+
+static int lochnagar_sc_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval range = {
+ .min = 8000,
+ .max = 24576000 / hw_param_interval(params, rule->deps[0])->max,
+ };
+
+ return snd_interval_refine(hw_param_interval(params, rule->var),
+ &range);
+}
+
+static int lochnagar_sc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &lochnagar_sc_rate_constraint);
+ if (ret)
+ return ret;
+
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ lochnagar_sc_hw_rule_rate, priv,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+}
+
+static int lochnagar_sc_line_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = lochnagar_sc_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &lochnagar_sc_chan_constraint);
+}
+
+static void lochnagar_sc_line_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+
+ clk_disable_unprepare(priv->mclk);
+}
+
+static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+ unsigned int tar)
+{
+ tar |= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
+
+ if ((fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) != tar)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBC_CFC);
+}
+
+static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP);
+}
+
+static const struct snd_soc_dai_ops lochnagar_sc_line_ops = {
+ .startup = lochnagar_sc_line_startup,
+ .shutdown = lochnagar_sc_line_shutdown,
+ .set_fmt = lochnagar_sc_set_line_fmt,
+};
+
+static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = {
+ .startup = lochnagar_sc_startup,
+ .set_fmt = lochnagar_sc_set_usb_fmt,
+};
+
+static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
+ {
+ .name = "lochnagar-line",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_line_ops,
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ },
+ {
+ .name = "lochnagar-usb1",
+ .playback = {
+ .stream_name = "USB1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "USB1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_usb_ops,
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ },
+ {
+ .name = "lochnagar-usb2",
+ .playback = {
+ .stream_name = "USB2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "USB2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_usb_ops,
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ },
+};
+
+static const struct snd_soc_component_driver lochnagar_sc_driver = {
+ .dapm_widgets = lochnagar_sc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
+ .dapm_routes = lochnagar_sc_routes,
+ .num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
+
+ .endianness = 1,
+};
+
+static int lochnagar_sc_probe(struct platform_device *pdev)
+{
+ struct lochnagar_sc_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ ret = PTR_ERR(priv->mclk);
+ dev_err(&pdev->dev, "Failed to get MCLK: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &lochnagar_sc_driver,
+ lochnagar_sc_dai,
+ ARRAY_SIZE(lochnagar_sc_dai));
+}
+
+static const struct of_device_id lochnagar_of_match[] = {
+ { .compatible = "cirrus,lochnagar2-soundcard" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lochnagar_of_match);
+
+static struct platform_driver lochnagar_sc_codec_driver = {
+ .driver = {
+ .name = "lochnagar-soundcard",
+ .of_match_table = lochnagar_of_match,
+ },
+
+ .probe = lochnagar_sc_probe,
+};
+module_platform_driver(lochnagar_sc_codec_driver);
+
+MODULE_DESCRIPTION("ASoC Lochnagar Sound Card Driver");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-soundcard");
diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c
new file mode 100644
index 000000000000..6e3b8d0897dd
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, The Linux Foundation. All rights reserved.
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include "lpass-macro-common.h"
+
+static DEFINE_MUTEX(lpass_codec_mutex);
+static enum lpass_codec_version lpass_codec_version;
+
+struct lpass_macro *lpass_macro_pds_init(struct device *dev)
+{
+ struct lpass_macro *l_pds;
+ int ret;
+
+ if (!of_property_present(dev->of_node, "power-domains"))
+ return NULL;
+
+ l_pds = devm_kzalloc(dev, sizeof(*l_pds), GFP_KERNEL);
+ if (!l_pds)
+ return ERR_PTR(-ENOMEM);
+
+ l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro");
+ if (IS_ERR_OR_NULL(l_pds->macro_pd)) {
+ ret = l_pds->macro_pd ? PTR_ERR(l_pds->macro_pd) : -ENODATA;
+ goto macro_err;
+ }
+
+ ret = pm_runtime_resume_and_get(l_pds->macro_pd);
+ if (ret < 0)
+ goto macro_sync_err;
+
+ l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec");
+ if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) {
+ ret = l_pds->dcodec_pd ? PTR_ERR(l_pds->dcodec_pd) : -ENODATA;
+ goto dcodec_err;
+ }
+
+ ret = pm_runtime_resume_and_get(l_pds->dcodec_pd);
+ if (ret < 0)
+ goto dcodec_sync_err;
+ return l_pds;
+
+dcodec_sync_err:
+ dev_pm_domain_detach(l_pds->dcodec_pd, false);
+dcodec_err:
+ pm_runtime_put(l_pds->macro_pd);
+macro_sync_err:
+ dev_pm_domain_detach(l_pds->macro_pd, false);
+macro_err:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(lpass_macro_pds_init);
+
+void lpass_macro_pds_exit(struct lpass_macro *pds)
+{
+ if (pds) {
+ pm_runtime_put(pds->macro_pd);
+ dev_pm_domain_detach(pds->macro_pd, false);
+ pm_runtime_put(pds->dcodec_pd);
+ dev_pm_domain_detach(pds->dcodec_pd, false);
+ }
+}
+EXPORT_SYMBOL_GPL(lpass_macro_pds_exit);
+
+void lpass_macro_set_codec_version(enum lpass_codec_version version)
+{
+ mutex_lock(&lpass_codec_mutex);
+ lpass_codec_version = version;
+ mutex_unlock(&lpass_codec_mutex);
+}
+EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version);
+
+enum lpass_codec_version lpass_macro_get_codec_version(void)
+{
+ enum lpass_codec_version ver;
+
+ mutex_lock(&lpass_codec_mutex);
+ ver = lpass_codec_version;
+ mutex_unlock(&lpass_codec_mutex);
+
+ return ver;
+}
+EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version);
+
+MODULE_DESCRIPTION("Common macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h
new file mode 100644
index 000000000000..10ad682019fa
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __LPASS_MACRO_COMMON_H__
+#define __LPASS_MACRO_COMMON_H__
+
+/* NPL clock is expected */
+#define LPASS_MACRO_FLAG_HAS_NPL_CLOCK BIT(0)
+/* The soundwire block should be internally reset at probe */
+#define LPASS_MACRO_FLAG_RESET_SWR BIT(1)
+
+enum lpass_version {
+ LPASS_VER_9_0_0,
+ LPASS_VER_9_2_0,
+ LPASS_VER_10_0_0,
+ LPASS_VER_11_0_0,
+};
+
+enum lpass_codec_version {
+ LPASS_CODEC_VERSION_UNKNOWN,
+ LPASS_CODEC_VERSION_1_0,
+ LPASS_CODEC_VERSION_1_1,
+ LPASS_CODEC_VERSION_1_2,
+ LPASS_CODEC_VERSION_2_0,
+ LPASS_CODEC_VERSION_2_1,
+ LPASS_CODEC_VERSION_2_5,
+ LPASS_CODEC_VERSION_2_6,
+ LPASS_CODEC_VERSION_2_7,
+ LPASS_CODEC_VERSION_2_8,
+ LPASS_CODEC_VERSION_2_9,
+};
+
+struct lpass_macro {
+ struct device *macro_pd;
+ struct device *dcodec_pd;
+};
+
+struct lpass_macro *lpass_macro_pds_init(struct device *dev);
+void lpass_macro_pds_exit(struct lpass_macro *pds);
+void lpass_macro_set_codec_version(enum lpass_codec_version version);
+enum lpass_codec_version lpass_macro_get_codec_version(void);
+
+static inline void lpass_macro_pds_exit_action(void *pds)
+{
+ lpass_macro_pds_exit(pds);
+}
+
+static inline const char *lpass_macro_get_codec_version_string(int version)
+{
+ switch (version) {
+ case LPASS_CODEC_VERSION_1_0:
+ return "v1.0";
+ case LPASS_CODEC_VERSION_1_1:
+ return "v1.1";
+ case LPASS_CODEC_VERSION_1_2:
+ return "v1.2";
+ case LPASS_CODEC_VERSION_2_0:
+ return "v2.0";
+ case LPASS_CODEC_VERSION_2_1:
+ return "v2.1";
+ case LPASS_CODEC_VERSION_2_5:
+ return "v2.5";
+ case LPASS_CODEC_VERSION_2_6:
+ return "v2.6";
+ case LPASS_CODEC_VERSION_2_7:
+ return "v2.7";
+ case LPASS_CODEC_VERSION_2_8:
+ return "v2.8";
+ default:
+ break;
+ }
+ return "NA";
+}
+
+#endif /* __LPASS_MACRO_COMMON_H__ */
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
new file mode 100644
index 000000000000..0a8de5620e72
--- /dev/null
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -0,0 +1,4035 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+
+#include "lpass-macro-common.h"
+
+#define CDC_RX_TOP_TOP_CFG0 (0x0000)
+#define CDC_RX_TOP_SWR_CTRL (0x0008)
+#define CDC_RX_TOP_DEBUG (0x000C)
+#define CDC_RX_TOP_DEBUG_BUS (0x0010)
+#define CDC_RX_TOP_DEBUG_EN0 (0x0014)
+#define CDC_RX_TOP_DEBUG_EN1 (0x0018)
+#define CDC_RX_TOP_DEBUG_EN2 (0x001C)
+#define CDC_RX_TOP_HPHL_COMP_WR_LSB (0x0020)
+#define CDC_RX_TOP_HPHL_COMP_WR_MSB (0x0024)
+#define CDC_RX_TOP_HPHL_COMP_LUT (0x0028)
+#define CDC_RX_TOP_HPH_LUT_BYPASS_MASK BIT(7)
+#define CDC_RX_TOP_HPHL_COMP_RD_LSB (0x002C)
+#define CDC_RX_TOP_HPHL_COMP_RD_MSB (0x0030)
+#define CDC_RX_TOP_HPHR_COMP_WR_LSB (0x0034)
+#define CDC_RX_TOP_HPHR_COMP_WR_MSB (0x0038)
+#define CDC_RX_TOP_HPHR_COMP_LUT (0x003C)
+#define CDC_RX_TOP_HPHR_COMP_RD_LSB (0x0040)
+#define CDC_RX_TOP_HPHR_COMP_RD_MSB (0x0044)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG0 (0x0070)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG1 (0x0074)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG2 (0x0078)
+#define CDC_RX_TOP_DSD0_DEBUG_CFG3 (0x007C)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG0 (0x0080)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG1 (0x0084)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG2 (0x0088)
+#define CDC_RX_TOP_DSD1_DEBUG_CFG3 (0x008C)
+#define CDC_RX_TOP_RX_I2S_CTL (0x0090)
+#define CDC_RX_TOP_TX_I2S2_CTL (0x0094)
+#define CDC_RX_TOP_I2S_CLK (0x0098)
+#define CDC_RX_TOP_I2S_RESET (0x009C)
+#define CDC_RX_TOP_I2S_MUX (0x00A0)
+#define CDC_RX_CLK_RST_CTRL_MCLK_CONTROL (0x0100)
+#define CDC_RX_CLK_MCLK_EN_MASK BIT(0)
+#define CDC_RX_CLK_MCLK_ENABLE BIT(0)
+#define CDC_RX_CLK_MCLK2_EN_MASK BIT(1)
+#define CDC_RX_CLK_MCLK2_ENABLE BIT(1)
+#define CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL (0x0104)
+#define CDC_RX_FS_MCLK_CNT_EN_MASK BIT(0)
+#define CDC_RX_FS_MCLK_CNT_ENABLE BIT(0)
+#define CDC_RX_FS_MCLK_CNT_CLR_MASK BIT(1)
+#define CDC_RX_FS_MCLK_CNT_CLR BIT(1)
+#define CDC_RX_CLK_RST_CTRL_SWR_CONTROL (0x0108)
+#define CDC_RX_SWR_CLK_EN_MASK BIT(0)
+#define CDC_RX_SWR_RESET_MASK BIT(1)
+#define CDC_RX_SWR_RESET BIT(1)
+#define CDC_RX_CLK_RST_CTRL_DSD_CONTROL (0x010C)
+#define CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL (0x0110)
+#define CDC_RX_SOFTCLIP_CRC (0x0140)
+#define CDC_RX_SOFTCLIP_CLK_EN_MASK BIT(0)
+#define CDC_RX_SOFTCLIP_SOFTCLIP_CTRL (0x0144)
+#define CDC_RX_SOFTCLIP_EN_MASK BIT(0)
+#define CDC_RX_INP_MUX_RX_INT0_CFG0 (0x0180)
+#define CDC_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(3, 0)
+#define CDC_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(7, 4)
+#define CDC_RX_INP_MUX_RX_INT0_CFG1 (0x0184)
+#define CDC_RX_INTX_2_SEL_MASK GENMASK(3, 0)
+#define CDC_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(7, 4)
+#define CDC_RX_INP_MUX_RX_INT1_CFG0 (0x0188)
+#define CDC_RX_INP_MUX_RX_INT1_CFG1 (0x018C)
+#define CDC_RX_INP_MUX_RX_INT2_CFG0 (0x0190)
+#define CDC_RX_INP_MUX_RX_INT2_CFG1 (0x0194)
+#define CDC_RX_INP_MUX_RX_MIX_CFG4 (0x0198)
+#define CDC_RX_INP_MUX_RX_MIX_CFG5 (0x019C)
+#define CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 (0x01A0)
+#define CDC_RX_CLSH_CRC (0x0200)
+#define CDC_RX_CLSH_CLK_EN_MASK BIT(0)
+#define CDC_RX_CLSH_DLY_CTRL (0x0204)
+#define CDC_RX_CLSH_DECAY_CTRL (0x0208)
+#define CDC_RX_CLSH_DECAY_RATE_MASK GENMASK(2, 0)
+#define CDC_RX_CLSH_HPH_V_PA (0x020C)
+#define CDC_RX_CLSH_HPH_V_PA_MIN_MASK GENMASK(5, 0)
+#define CDC_RX_CLSH_EAR_V_PA (0x0210)
+#define CDC_RX_CLSH_HPH_V_HD (0x0214)
+#define CDC_RX_CLSH_EAR_V_HD (0x0218)
+#define CDC_RX_CLSH_K1_MSB (0x021C)
+#define CDC_RX_CLSH_K1_MSB_COEFF_MASK GENMASK(3, 0)
+#define CDC_RX_CLSH_K1_LSB (0x0220)
+#define CDC_RX_CLSH_K2_MSB (0x0224)
+#define CDC_RX_CLSH_K2_LSB (0x0228)
+#define CDC_RX_CLSH_IDLE_CTRL (0x022C)
+#define CDC_RX_CLSH_IDLE_HPH (0x0230)
+#define CDC_RX_CLSH_IDLE_EAR (0x0234)
+#define CDC_RX_CLSH_TEST0 (0x0238)
+#define CDC_RX_CLSH_TEST1 (0x023C)
+#define CDC_RX_CLSH_OVR_VREF (0x0240)
+#define CDC_RX_CLSH_CLSG_CTL (0x0244)
+#define CDC_RX_CLSH_CLSG_CFG1 (0x0248)
+#define CDC_RX_CLSH_CLSG_CFG2 (0x024C)
+#define CDC_RX_BCL_VBAT_PATH_CTL (0x0280)
+#define CDC_RX_BCL_VBAT_CFG (0x0284)
+#define CDC_RX_BCL_VBAT_ADC_CAL1 (0x0288)
+#define CDC_RX_BCL_VBAT_ADC_CAL2 (0x028C)
+#define CDC_RX_BCL_VBAT_ADC_CAL3 (0x0290)
+#define CDC_RX_BCL_VBAT_PK_EST1 (0x0294)
+#define CDC_RX_BCL_VBAT_PK_EST2 (0x0298)
+#define CDC_RX_BCL_VBAT_PK_EST3 (0x029C)
+#define CDC_RX_BCL_VBAT_RF_PROC1 (0x02A0)
+#define CDC_RX_BCL_VBAT_RF_PROC2 (0x02A4)
+#define CDC_RX_BCL_VBAT_TAC1 (0x02A8)
+#define CDC_RX_BCL_VBAT_TAC2 (0x02AC)
+#define CDC_RX_BCL_VBAT_TAC3 (0x02B0)
+#define CDC_RX_BCL_VBAT_TAC4 (0x02B4)
+#define CDC_RX_BCL_VBAT_GAIN_UPD1 (0x02B8)
+#define CDC_RX_BCL_VBAT_GAIN_UPD2 (0x02BC)
+#define CDC_RX_BCL_VBAT_GAIN_UPD3 (0x02C0)
+#define CDC_RX_BCL_VBAT_GAIN_UPD4 (0x02C4)
+#define CDC_RX_BCL_VBAT_GAIN_UPD5 (0x02C8)
+#define CDC_RX_BCL_VBAT_DEBUG1 (0x02CC)
+#define CDC_RX_BCL_VBAT_GAIN_UPD_MON (0x02D0)
+#define CDC_RX_BCL_VBAT_GAIN_MON_VAL (0x02D4)
+#define CDC_RX_BCL_VBAT_BAN (0x02D8)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD1 (0x02DC)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD2 (0x02E0)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD3 (0x02E4)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD4 (0x02E8)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD5 (0x02EC)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD6 (0x02F0)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD7 (0x02F4)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD8 (0x02F8)
+#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD9 (0x02FC)
+#define CDC_RX_BCL_VBAT_ATTN1 (0x0300)
+#define CDC_RX_BCL_VBAT_ATTN2 (0x0304)
+#define CDC_RX_BCL_VBAT_ATTN3 (0x0308)
+#define CDC_RX_BCL_VBAT_DECODE_CTL1 (0x030C)
+#define CDC_RX_BCL_VBAT_DECODE_CTL2 (0x0310)
+#define CDC_RX_BCL_VBAT_DECODE_CFG1 (0x0314)
+#define CDC_RX_BCL_VBAT_DECODE_CFG2 (0x0318)
+#define CDC_RX_BCL_VBAT_DECODE_CFG3 (0x031C)
+#define CDC_RX_BCL_VBAT_DECODE_CFG4 (0x0320)
+#define CDC_RX_BCL_VBAT_DECODE_ST (0x0324)
+#define CDC_RX_INTR_CTRL_CFG (0x0340)
+#define CDC_RX_INTR_CTRL_CLR_COMMIT (0x0344)
+#define CDC_RX_INTR_CTRL_PIN1_MASK0 (0x0360)
+#define CDC_RX_INTR_CTRL_PIN1_STATUS0 (0x0368)
+#define CDC_RX_INTR_CTRL_PIN1_CLEAR0 (0x0370)
+#define CDC_RX_INTR_CTRL_PIN2_MASK0 (0x0380)
+#define CDC_RX_INTR_CTRL_PIN2_STATUS0 (0x0388)
+#define CDC_RX_INTR_CTRL_PIN2_CLEAR0 (0x0390)
+#define CDC_RX_INTR_CTRL_LEVEL0 (0x03C0)
+#define CDC_RX_INTR_CTRL_BYPASS0 (0x03C8)
+#define CDC_RX_INTR_CTRL_SET0 (0x03D0)
+#define CDC_RX_RXn_RX_PATH_CTL(rx, n) (0x0400 + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_PATH_CTL (0x0400)
+#define CDC_RX_PATH_RESET_EN_MASK BIT(6)
+#define CDC_RX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_RX_PATH_CLK_ENABLE BIT(5)
+#define CDC_RX_PATH_PGA_MUTE_MASK BIT(4)
+#define CDC_RX_PATH_PGA_MUTE_ENABLE BIT(4)
+#define CDC_RX_PATH_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_RX_RXn_RX_PATH_CFG0(rx, n) (0x0404 + rx->rxn_reg_stride * n)
+#define CDC_RX_RXn_COMP_EN_MASK BIT(1)
+#define CDC_RX_RX0_RX_PATH_CFG0 (0x0404)
+#define CDC_RX_RXn_CLSH_EN_MASK BIT(6)
+#define CDC_RX_DLY_ZN_EN_MASK BIT(3)
+#define CDC_RX_DLY_ZN_ENABLE BIT(3)
+#define CDC_RX_RXn_HD2_EN_MASK BIT(2)
+#define CDC_RX_RXn_RX_PATH_CFG1(rx, n) (0x0408 + rx->rxn_reg_stride * n)
+#define CDC_RX_RXn_SIDETONE_EN_MASK BIT(4)
+#define CDC_RX_RX0_RX_PATH_CFG1 (0x0408)
+#define CDC_RX_RX0_HPH_L_EAR_SEL_MASK BIT(1)
+#define CDC_RX_RXn_RX_PATH_CFG2(rx, n) (0x040C + rx->rxn_reg_stride * n)
+#define CDC_RX_RXn_HPF_CUT_FREQ_MASK GENMASK(1, 0)
+#define CDC_RX_RX0_RX_PATH_CFG2 (0x040C)
+#define CDC_RX_RXn_RX_PATH_CFG3(rx, n) (0x0410 + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_PATH_CFG3 (0x0410)
+#define CDC_RX_DC_COEFF_SEL_MASK GENMASK(1, 0)
+#define CDC_RX_DC_COEFF_SEL_TWO 0x2
+#define CDC_RX_RXn_RX_VOL_CTL(rx, n) (0x0414 + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_VOL_CTL (0x0414)
+#define CDC_RX_RXn_RX_PATH_MIX_CTL(rx, n) (0x0418 + rx->rxn_reg_stride * n)
+#define CDC_RX_RXn_MIX_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_RX_RXn_MIX_RESET_MASK BIT(6)
+#define CDC_RX_RXn_MIX_RESET BIT(6)
+#define CDC_RX_RXn_MIX_CLK_EN_MASK BIT(5)
+#define CDC_RX_RX0_RX_PATH_MIX_CTL (0x0418)
+#define CDC_RX_RX0_RX_PATH_MIX_CFG (0x041C)
+#define CDC_RX_RXn_RX_VOL_MIX_CTL(rx, n) (0x0420 + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_VOL_MIX_CTL (0x0420)
+#define CDC_RX_RX0_RX_PATH_SEC1 (0x0424)
+#define CDC_RX_RX0_RX_PATH_SEC2 (0x0428)
+#define CDC_RX_RX0_RX_PATH_SEC3 (0x042C)
+#define CDC_RX_RXn_RX_PATH_SEC3(rx, n) (0x042c + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_PATH_SEC4 (0x0430)
+#define CDC_RX_RX0_RX_PATH_SEC7 (0x0434)
+#define CDC_RX_RXn_RX_PATH_SEC7(rx, n) \
+ (0x0434 + (rx->rxn_reg_stride * n) + ((n > 1) ? rx->rxn_reg_stride2 : 0))
+#define CDC_RX_DSM_OUT_DELAY_SEL_MASK GENMASK(2, 0)
+#define CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE 0x2
+#define CDC_RX_RX0_RX_PATH_MIX_SEC0 (0x0438)
+#define CDC_RX_RX0_RX_PATH_MIX_SEC1 (0x043C)
+#define CDC_RX_RXn_RX_PATH_DSM_CTL(rx, n) \
+ (0x0440 + (rx->rxn_reg_stride * n) + ((n > 1) ? rx->rxn_reg_stride2 : 0))
+#define CDC_RX_RXn_DSM_CLK_EN_MASK BIT(0)
+#define CDC_RX_RX0_RX_PATH_DSM_CTL (0x0440)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA1 (0x0444)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA2 (0x0448)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA3 (0x044C)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA4 (0x0450)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA5 (0x0454)
+#define CDC_RX_RX0_RX_PATH_DSM_DATA6 (0x0458)
+/* RX offsets prior to 2.5 codec version */
+#define CDC_RX_RX1_RX_PATH_CTL (0x0480)
+#define CDC_RX_RX1_RX_PATH_CFG0 (0x0484)
+#define CDC_RX_RX1_RX_PATH_CFG1 (0x0488)
+#define CDC_RX_RX1_RX_PATH_CFG2 (0x048C)
+#define CDC_RX_RX1_RX_PATH_CFG3 (0x0490)
+#define CDC_RX_RX1_RX_VOL_CTL (0x0494)
+#define CDC_RX_RX1_RX_PATH_MIX_CTL (0x0498)
+#define CDC_RX_RX1_RX_PATH_MIX_CFG (0x049C)
+#define CDC_RX_RX1_RX_VOL_MIX_CTL (0x04A0)
+#define CDC_RX_RX1_RX_PATH_SEC1 (0x04A4)
+#define CDC_RX_RX1_RX_PATH_SEC2 (0x04A8)
+#define CDC_RX_RX1_RX_PATH_SEC3 (0x04AC)
+#define CDC_RX_RXn_HD2_ALPHA_MASK GENMASK(5, 2)
+#define CDC_RX_RX1_RX_PATH_SEC4 (0x04B0)
+#define CDC_RX_RX1_RX_PATH_SEC7 (0x04B4)
+#define CDC_RX_RX1_RX_PATH_MIX_SEC0 (0x04B8)
+#define CDC_RX_RX1_RX_PATH_MIX_SEC1 (0x04BC)
+#define CDC_RX_RX1_RX_PATH_DSM_CTL (0x04C0)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA1 (0x04C4)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA2 (0x04C8)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA3 (0x04CC)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA4 (0x04D0)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA5 (0x04D4)
+#define CDC_RX_RX1_RX_PATH_DSM_DATA6 (0x04D8)
+#define CDC_RX_RX2_RX_PATH_CTL (0x0500)
+#define CDC_RX_RX2_RX_PATH_CFG0 (0x0504)
+#define CDC_RX_RX2_CLSH_EN_MASK BIT(4)
+#define CDC_RX_RX2_DLY_Z_EN_MASK BIT(3)
+#define CDC_RX_RX2_RX_PATH_CFG1 (0x0508)
+#define CDC_RX_RX2_RX_PATH_CFG2 (0x050C)
+#define CDC_RX_RX2_RX_PATH_CFG3 (0x0510)
+#define CDC_RX_RX2_RX_VOL_CTL (0x0514)
+#define CDC_RX_RX2_RX_PATH_MIX_CTL (0x0518)
+#define CDC_RX_RX2_RX_PATH_MIX_CFG (0x051C)
+#define CDC_RX_RX2_RX_VOL_MIX_CTL (0x0520)
+#define CDC_RX_RX2_RX_PATH_SEC0 (0x0524)
+#define CDC_RX_RX2_RX_PATH_SEC1 (0x0528)
+#define CDC_RX_RX2_RX_PATH_SEC2 (0x052C)
+#define CDC_RX_RX2_RX_PATH_SEC3 (0x0530)
+#define CDC_RX_RX2_RX_PATH_SEC4 (0x0534)
+#define CDC_RX_RX2_RX_PATH_SEC5 (0x0538)
+#define CDC_RX_RX2_RX_PATH_SEC6 (0x053C)
+#define CDC_RX_RX2_RX_PATH_SEC7 (0x0540)
+#define CDC_RX_RX2_RX_PATH_MIX_SEC0 (0x0544)
+#define CDC_RX_RX2_RX_PATH_MIX_SEC1 (0x0548)
+#define CDC_RX_RX2_RX_PATH_DSM_CTL (0x054C)
+
+/* LPASS CODEC version 2.5 rx reg offsets */
+#define CDC_2_5_RX_RX1_RX_PATH_CTL (0x04c0)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG0 (0x04c4)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG1 (0x04c8)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG2 (0x04cC)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG3 (0x04d0)
+#define CDC_2_5_RX_RX1_RX_VOL_CTL (0x04d4)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_CTL (0x04d8)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_CFG (0x04dC)
+#define CDC_2_5_RX_RX1_RX_VOL_MIX_CTL (0x04e0)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC1 (0x04e4)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC2 (0x04e8)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC3 (0x04eC)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC4 (0x04f0)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC7 (0x04f4)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0 (0x04f8)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1 (0x04fC)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_CTL (0x0500)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1 (0x0504)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2 (0x0508)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3 (0x050C)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4 (0x0510)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5 (0x0514)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6 (0x0518)
+
+#define CDC_2_5_RX_RX2_RX_PATH_CTL (0x0580)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG0 (0x0584)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG1 (0x0588)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG2 (0x058C)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG3 (0x0590)
+#define CDC_2_5_RX_RX2_RX_VOL_CTL (0x0594)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_CTL (0x0598)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_CFG (0x059C)
+#define CDC_2_5_RX_RX2_RX_VOL_MIX_CTL (0x05a0)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC0 (0x05a4)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC1 (0x05a8)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC2 (0x05aC)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC3 (0x05b0)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC4 (0x05b4)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC5 (0x05b8)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC6 (0x05bC)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC7 (0x05c0)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0 (0x05c4)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1 (0x05c8)
+#define CDC_2_5_RX_RX2_RX_PATH_DSM_CTL (0x05cC)
+
+#define CDC_RX_IDLE_DETECT_PATH_CTL (0x0780)
+#define CDC_RX_IDLE_DETECT_CFG0 (0x0784)
+#define CDC_RX_IDLE_DETECT_CFG1 (0x0788)
+#define CDC_RX_IDLE_DETECT_CFG2 (0x078C)
+#define CDC_RX_IDLE_DETECT_CFG3 (0x0790)
+#define CDC_RX_COMPANDERn_CTL0(n) (0x0800 + 0x40 * n)
+#define CDC_RX_COMPANDERn_CLK_EN_MASK BIT(0)
+#define CDC_RX_COMPANDERn_SOFT_RST_MASK BIT(1)
+#define CDC_RX_COMPANDERn_HALT_MASK BIT(2)
+#define CDC_RX_COMPANDER0_CTL0 (0x0800)
+#define CDC_RX_COMPANDER0_CTL1 (0x0804)
+#define CDC_RX_COMPANDER0_CTL2 (0x0808)
+#define CDC_RX_COMPANDER0_CTL3 (0x080C)
+#define CDC_RX_COMPANDER0_CTL4 (0x0810)
+#define CDC_RX_COMPANDER0_CTL5 (0x0814)
+#define CDC_RX_COMPANDER0_CTL6 (0x0818)
+#define CDC_RX_COMPANDER0_CTL7 (0x081C)
+#define CDC_RX_COMPANDER1_CTL0 (0x0840)
+#define CDC_RX_COMPANDER1_CTL1 (0x0844)
+#define CDC_RX_COMPANDER1_CTL2 (0x0848)
+#define CDC_RX_COMPANDER1_CTL3 (0x084C)
+#define CDC_RX_COMPANDER1_CTL4 (0x0850)
+#define CDC_RX_COMPANDER1_CTL5 (0x0854)
+#define CDC_RX_COMPANDER1_CTL6 (0x0858)
+#define CDC_RX_COMPANDER1_CTL7 (0x085C)
+#define CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK BIT(5)
+#define CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL (0x0A00)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL (0x0A04)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL (0x0A08)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL (0x0A0C)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL (0x0A10)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL (0x0A14)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL (0x0A18)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL (0x0A1C)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL (0x0A20)
+#define CDC_RX_SIDETONE_IIR0_IIR_CTL (0x0A24)
+#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL (0x0A28)
+#define CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL (0x0A2C)
+#define CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL (0x0A30)
+#define CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL (0x0A80)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL (0x0A84)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL (0x0A88)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL (0x0A8C)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL (0x0A90)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL (0x0A94)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL (0x0A98)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL (0x0A9C)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL (0x0AA0)
+#define CDC_RX_SIDETONE_IIR1_IIR_CTL (0x0AA4)
+#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL (0x0AA8)
+#define CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL (0x0AAC)
+#define CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL (0x0AB0)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0 (0x0B00)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1 (0x0B04)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2 (0x0B08)
+#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3 (0x0B0C)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0 (0x0B10)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1 (0x0B14)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2 (0x0B18)
+#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3 (0x0B1C)
+#define CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL (0x0B40)
+#define CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1 (0x0B44)
+#define CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL (0x0B50)
+#define CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1 (0x0B54)
+#define CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL (0x0C00)
+#define CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 (0x0C04)
+#define CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL (0x0C40)
+#define CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0 (0x0C44)
+#define CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL (0x0C80)
+#define CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0 (0x0C84)
+#define CDC_RX_EC_ASRC0_CLK_RST_CTL (0x0D00)
+#define CDC_RX_EC_ASRC0_CTL0 (0x0D04)
+#define CDC_RX_EC_ASRC0_CTL1 (0x0D08)
+#define CDC_RX_EC_ASRC0_FIFO_CTL (0x0D0C)
+#define CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB (0x0D10)
+#define CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB (0x0D14)
+#define CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB (0x0D18)
+#define CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB (0x0D1C)
+#define CDC_RX_EC_ASRC0_STATUS_FIFO (0x0D20)
+#define CDC_RX_EC_ASRC1_CLK_RST_CTL (0x0D40)
+#define CDC_RX_EC_ASRC1_CTL0 (0x0D44)
+#define CDC_RX_EC_ASRC1_CTL1 (0x0D48)
+#define CDC_RX_EC_ASRC1_FIFO_CTL (0x0D4C)
+#define CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB (0x0D50)
+#define CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB (0x0D54)
+#define CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB (0x0D58)
+#define CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB (0x0D5C)
+#define CDC_RX_EC_ASRC1_STATUS_FIFO (0x0D60)
+#define CDC_RX_EC_ASRC2_CLK_RST_CTL (0x0D80)
+#define CDC_RX_EC_ASRC2_CTL0 (0x0D84)
+#define CDC_RX_EC_ASRC2_CTL1 (0x0D88)
+#define CDC_RX_EC_ASRC2_FIFO_CTL (0x0D8C)
+#define CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB (0x0D90)
+#define CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB (0x0D94)
+#define CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB (0x0D98)
+#define CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB (0x0D9C)
+#define CDC_RX_EC_ASRC2_STATUS_FIFO (0x0DA0)
+#define CDC_RX_DSD0_PATH_CTL (0x0F00)
+#define CDC_RX_DSD0_CFG0 (0x0F04)
+#define CDC_RX_DSD0_CFG1 (0x0F08)
+#define CDC_RX_DSD0_CFG2 (0x0F0C)
+#define CDC_RX_DSD1_PATH_CTL (0x0F80)
+#define CDC_RX_DSD1_CFG0 (0x0F84)
+#define CDC_RX_DSD1_CFG1 (0x0F88)
+#define CDC_RX_DSD1_CFG2 (0x0F8C)
+#define RX_MAX_OFFSET (0x0F8C)
+
+#define MCLK_FREQ 19200000
+
+#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define RX_MACRO_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define RX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define RX_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_48000)
+#define RX_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define RX_MACRO_MAX_DMA_CH_PER_PORT 2
+
+#define RX_MACRO_EC_MIX_TX0_MASK 0xf0
+#define RX_MACRO_EC_MIX_TX1_MASK 0x0f
+#define RX_MACRO_EC_MIX_TX2_MASK 0x0f
+
+#define COMP_MAX_COEFF 25
+#define RX_NUM_CLKS_MAX 5
+
+struct comp_coeff_val {
+ u8 lsb;
+ u8 msb;
+};
+
+enum {
+ HPH_ULP,
+ HPH_LOHIFI,
+ HPH_MODE_MAX,
+};
+
+static const struct comp_coeff_val comp_coeff_table[HPH_MODE_MAX][COMP_MAX_COEFF] = {
+ {
+ {0x40, 0x00},
+ {0x4C, 0x00},
+ {0x5A, 0x00},
+ {0x6B, 0x00},
+ {0x7F, 0x00},
+ {0x97, 0x00},
+ {0xB3, 0x00},
+ {0xD5, 0x00},
+ {0xFD, 0x00},
+ {0x2D, 0x01},
+ {0x66, 0x01},
+ {0xA7, 0x01},
+ {0xF8, 0x01},
+ {0x57, 0x02},
+ {0xC7, 0x02},
+ {0x4B, 0x03},
+ {0xE9, 0x03},
+ {0xA3, 0x04},
+ {0x7D, 0x05},
+ {0x90, 0x06},
+ {0xD1, 0x07},
+ {0x49, 0x09},
+ {0x00, 0x0B},
+ {0x01, 0x0D},
+ {0x59, 0x0F},
+ },
+ {
+ {0x40, 0x00},
+ {0x4C, 0x00},
+ {0x5A, 0x00},
+ {0x6B, 0x00},
+ {0x80, 0x00},
+ {0x98, 0x00},
+ {0xB4, 0x00},
+ {0xD5, 0x00},
+ {0xFE, 0x00},
+ {0x2E, 0x01},
+ {0x66, 0x01},
+ {0xA9, 0x01},
+ {0xF8, 0x01},
+ {0x56, 0x02},
+ {0xC4, 0x02},
+ {0x4F, 0x03},
+ {0xF0, 0x03},
+ {0xAE, 0x04},
+ {0x8B, 0x05},
+ {0x8E, 0x06},
+ {0xBC, 0x07},
+ {0x56, 0x09},
+ {0x0F, 0x0B},
+ {0x13, 0x0D},
+ {0x6F, 0x0F},
+ },
+};
+
+enum {
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_AUX,
+ INTERP_MAX
+};
+
+enum {
+ RX_MACRO_RX0,
+ RX_MACRO_RX1,
+ RX_MACRO_RX2,
+ RX_MACRO_RX3,
+ RX_MACRO_RX4,
+ RX_MACRO_RX5,
+ RX_MACRO_PORTS_MAX
+};
+
+enum {
+ RX_MACRO_COMP1, /* HPH_L */
+ RX_MACRO_COMP2, /* HPH_R */
+ RX_MACRO_COMP_MAX
+};
+
+enum {
+ RX_MACRO_EC0_MUX = 0,
+ RX_MACRO_EC1_MUX,
+ RX_MACRO_EC2_MUX,
+ RX_MACRO_EC_MUX_MAX,
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+ INTn_1_INP_SEL_IIR0,
+ INTn_1_INP_SEL_IIR1,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_RX4,
+ INTn_1_INP_SEL_RX5,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+ INTn_2_INP_SEL_RX4,
+ INTn_2_INP_SEL_RX5,
+};
+
+enum {
+ INTERP_MAIN_PATH,
+ INTERP_MIX_PATH,
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR0 = 0,
+ IIR1,
+ IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+#define RX_MACRO_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX)
+
+#define RX_MACRO_IIR_FILTER_CTL(xname, iidx, bidx) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rx_macro_iir_filter_info, \
+ .get = rx_macro_get_iir_band_audio_mixer, \
+ .put = rx_macro_put_iir_band_audio_mixer, \
+ .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \
+ .iir_idx = iidx, \
+ .band_idx = bidx, \
+ .bytes_ext = {.max = RX_MACRO_IIR_FILTER_SIZE, }, \
+ } \
+}
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate sr_val_tbl[] = {
+ {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5},
+ {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA},
+ {176400, 0xB}, {352800, 0xC},
+};
+
+/* Matches also rx_macro_mux_text */
+enum {
+ RX_MACRO_AIF1_PB,
+ RX_MACRO_AIF2_PB,
+ RX_MACRO_AIF3_PB,
+ RX_MACRO_AIF4_PB,
+ RX_MACRO_AIF_ECHO,
+ RX_MACRO_MAX_DAIS,
+};
+
+enum {
+ RX_MACRO_AIF1_CAP = 0,
+ RX_MACRO_AIF2_CAP,
+ RX_MACRO_AIF3_CAP,
+ RX_MACRO_MAX_AIF_CAP_DAIS
+};
+
+struct rx_macro {
+ struct device *dev;
+ int comp_enabled[RX_MACRO_COMP_MAX];
+ /* Main path clock users count */
+ int main_clk_users[INTERP_MAX];
+ int rx_port_value[RX_MACRO_PORTS_MAX];
+ u16 prim_int_users[INTERP_MAX];
+ int rx_mclk_users;
+ int clsh_users;
+ int rx_mclk_cnt;
+ enum lpass_codec_version codec_version;
+ int rxn_reg_stride;
+ int rxn_reg_stride2;
+ bool is_ear_mode_on;
+ bool hph_pwr_mode;
+ bool hph_hd2_mode;
+ struct snd_soc_component *component;
+ unsigned long active_ch_mask[RX_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[RX_MACRO_MAX_DAIS];
+ u16 bit_width[RX_MACRO_MAX_DAIS];
+ int is_softclip_on;
+ int is_aux_hpf_on;
+ int softclip_clk_users;
+ struct lpass_macro *pds;
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+};
+#define to_rx_macro(_hw) container_of(_hw, struct rx_macro, hw)
+
+struct wcd_iir_filter_ctl {
+ unsigned int iir_idx;
+ unsigned int band_idx;
+ struct soc_bytes_ext bytes_ext;
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char * const rx_int_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5"
+};
+
+static const char * const rx_prim_mix_text[] = {
+ "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+ "RX3", "RX4", "RX5"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const iir_inp_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3",
+ "RX0", "RX1", "RX2", "RX3", "RX4", "RX5"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+ "NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_1_interp_mux_text[] = {
+ "ZERO", "RX INT0_1 MIX1",
+};
+
+static const char * const rx_int1_1_interp_mux_text[] = {
+ "ZERO", "RX INT1_1 MIX1",
+};
+
+static const char * const rx_int2_1_interp_mux_text[] = {
+ "ZERO", "RX INT2_1 MIX1",
+};
+
+static const char * const rx_int0_2_interp_mux_text[] = {
+ "ZERO", "RX INT0_2 MUX",
+};
+
+static const char * const rx_int1_2_interp_mux_text[] = {
+ "ZERO", "RX INT1_2 MUX",
+};
+
+static const char * const rx_int2_2_interp_mux_text[] = {
+ "ZERO", "RX INT2_2 MUX",
+};
+
+/* Order must match RX_MACRO_MAX_DAIS enum (offset by 1) */
+static const char *const rx_macro_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB"
+};
+
+static const char *const rx_macro_hph_pwr_mode_text[] = {
+ "ULP", "LOHIFI"
+};
+
+static const char * const rx_echo_mux_text[] = {
+ "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2"
+};
+
+static const struct soc_enum rx_macro_hph_pwr_mode_enum =
+ SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_pwr_mode_text);
+static const struct soc_enum rx_mix_tx2_mux_enum =
+ SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG5, 0, 4, rx_echo_mux_text);
+static const struct soc_enum rx_mix_tx1_mux_enum =
+ SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 4, rx_echo_mux_text);
+static const struct soc_enum rx_mix_tx0_mux_enum =
+ SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG4, 4, 4, rx_echo_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_2_enum, CDC_RX_INP_MUX_RX_INT0_CFG1, 0,
+ rx_int_mix_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_2_enum, CDC_RX_INP_MUX_RX_INT1_CFG1, 0,
+ rx_int_mix_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_2_enum, CDC_RX_INP_MUX_RX_INT2_CFG1, 0,
+ rx_int_mix_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT0_CFG0, 0,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT0_CFG0, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT0_CFG1, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT1_CFG0, 0,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT1_CFG0, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT1_CFG1, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT2_CFG0, 0,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT2_CFG0, 4,
+ rx_prim_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT2_CFG1, 4,
+ rx_prim_mix_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2,
+ rx_sidetone_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4,
+ rx_sidetone_mix_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6,
+ rx_sidetone_mix_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp0_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp1_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp2_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir0_inp3_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp0_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp1_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp2_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0,
+ iir_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(iir1_inp3_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0,
+ iir_inp_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_int0_1_interp_enum, SND_SOC_NOPM, 0,
+ rx_int0_1_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_1_interp_enum, SND_SOC_NOPM, 0,
+ rx_int1_1_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_1_interp_enum, SND_SOC_NOPM, 0,
+ rx_int2_1_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_2_interp_enum, SND_SOC_NOPM, 0,
+ rx_int0_2_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_2_interp_enum, SND_SOC_NOPM, 0,
+ rx_int1_2_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int2_2_interp_enum, SND_SOC_NOPM, 0,
+ rx_int2_2_interp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int0_dem_inp_enum, CDC_RX_RX0_RX_PATH_CFG1, 0,
+ rx_int_dem_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_int1_dem_inp_enum, CDC_RX_RX1_RX_PATH_CFG1, 0,
+ rx_int_dem_inp_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_2_5_int1_dem_inp_enum, CDC_2_5_RX_RX1_RX_PATH_CFG1, 0,
+ rx_int_dem_inp_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx0_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx1_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx2_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx3_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx4_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+static SOC_ENUM_SINGLE_DECL(rx_macro_rx5_enum, SND_SOC_NOPM, 0, rx_macro_mux_text);
+
+static const struct snd_kcontrol_new rx_mix_tx1_mux =
+ SOC_DAPM_ENUM("RX MIX TX1_MUX Mux", rx_mix_tx1_mux_enum);
+static const struct snd_kcontrol_new rx_mix_tx2_mux =
+ SOC_DAPM_ENUM("RX MIX TX2_MUX Mux", rx_mix_tx2_mux_enum);
+static const struct snd_kcontrol_new rx_int0_2_mux =
+ SOC_DAPM_ENUM("rx_int0_2", rx_int0_2_enum);
+static const struct snd_kcontrol_new rx_int1_2_mux =
+ SOC_DAPM_ENUM("rx_int1_2", rx_int1_2_enum);
+static const struct snd_kcontrol_new rx_int2_2_mux =
+ SOC_DAPM_ENUM("rx_int2_2", rx_int2_2_enum);
+static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("rx_int0_1_mix_inp0", rx_int0_1_mix_inp0_enum);
+static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("rx_int0_1_mix_inp1", rx_int0_1_mix_inp1_enum);
+static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("rx_int0_1_mix_inp2", rx_int0_1_mix_inp2_enum);
+static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("rx_int1_1_mix_inp0", rx_int1_1_mix_inp0_enum);
+static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("rx_int1_1_mix_inp1", rx_int1_1_mix_inp1_enum);
+static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("rx_int1_1_mix_inp2", rx_int1_1_mix_inp2_enum);
+static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("rx_int2_1_mix_inp0", rx_int2_1_mix_inp0_enum);
+static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("rx_int2_1_mix_inp1", rx_int2_1_mix_inp1_enum);
+static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("rx_int2_1_mix_inp2", rx_int2_1_mix_inp2_enum);
+static const struct snd_kcontrol_new rx_int0_mix2_inp_mux =
+ SOC_DAPM_ENUM("rx_int0_mix2_inp", rx_int0_mix2_inp_enum);
+static const struct snd_kcontrol_new rx_int1_mix2_inp_mux =
+ SOC_DAPM_ENUM("rx_int1_mix2_inp", rx_int1_mix2_inp_enum);
+static const struct snd_kcontrol_new rx_int2_mix2_inp_mux =
+ SOC_DAPM_ENUM("rx_int2_mix2_inp", rx_int2_mix2_inp_enum);
+static const struct snd_kcontrol_new iir0_inp0_mux =
+ SOC_DAPM_ENUM("iir0_inp0", iir0_inp0_enum);
+static const struct snd_kcontrol_new iir0_inp1_mux =
+ SOC_DAPM_ENUM("iir0_inp1", iir0_inp1_enum);
+static const struct snd_kcontrol_new iir0_inp2_mux =
+ SOC_DAPM_ENUM("iir0_inp2", iir0_inp2_enum);
+static const struct snd_kcontrol_new iir0_inp3_mux =
+ SOC_DAPM_ENUM("iir0_inp3", iir0_inp3_enum);
+static const struct snd_kcontrol_new iir1_inp0_mux =
+ SOC_DAPM_ENUM("iir1_inp0", iir1_inp0_enum);
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("iir1_inp1", iir1_inp1_enum);
+static const struct snd_kcontrol_new iir1_inp2_mux =
+ SOC_DAPM_ENUM("iir1_inp2", iir1_inp2_enum);
+static const struct snd_kcontrol_new iir1_inp3_mux =
+ SOC_DAPM_ENUM("iir1_inp3", iir1_inp3_enum);
+static const struct snd_kcontrol_new rx_int0_1_interp_mux =
+ SOC_DAPM_ENUM("rx_int0_1_interp", rx_int0_1_interp_enum);
+static const struct snd_kcontrol_new rx_int1_1_interp_mux =
+ SOC_DAPM_ENUM("rx_int1_1_interp", rx_int1_1_interp_enum);
+static const struct snd_kcontrol_new rx_int2_1_interp_mux =
+ SOC_DAPM_ENUM("rx_int2_1_interp", rx_int2_1_interp_enum);
+static const struct snd_kcontrol_new rx_int0_2_interp_mux =
+ SOC_DAPM_ENUM("rx_int0_2_interp", rx_int0_2_interp_enum);
+static const struct snd_kcontrol_new rx_int1_2_interp_mux =
+ SOC_DAPM_ENUM("rx_int1_2_interp", rx_int1_2_interp_enum);
+static const struct snd_kcontrol_new rx_int2_2_interp_mux =
+ SOC_DAPM_ENUM("rx_int2_2_interp", rx_int2_2_interp_enum);
+static const struct snd_kcontrol_new rx_mix_tx0_mux =
+ SOC_DAPM_ENUM("RX MIX TX0_MUX Mux", rx_mix_tx0_mux_enum);
+
+static const struct reg_default rx_defaults[] = {
+ /* RX Macro */
+ { CDC_RX_TOP_TOP_CFG0, 0x00 },
+ { CDC_RX_TOP_SWR_CTRL, 0x00 },
+ { CDC_RX_TOP_DEBUG, 0x00 },
+ { CDC_RX_TOP_DEBUG_BUS, 0x00 },
+ { CDC_RX_TOP_DEBUG_EN0, 0x00 },
+ { CDC_RX_TOP_DEBUG_EN1, 0x00 },
+ { CDC_RX_TOP_DEBUG_EN2, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_WR_LSB, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_WR_MSB, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_LUT, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_RD_LSB, 0x00 },
+ { CDC_RX_TOP_HPHL_COMP_RD_MSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_WR_LSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_WR_MSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_LUT, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_RD_LSB, 0x00 },
+ { CDC_RX_TOP_HPHR_COMP_RD_MSB, 0x00 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG0, 0x11 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG1, 0x20 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG2, 0x00 },
+ { CDC_RX_TOP_DSD0_DEBUG_CFG3, 0x00 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG0, 0x11 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG1, 0x20 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG2, 0x00 },
+ { CDC_RX_TOP_DSD1_DEBUG_CFG3, 0x00 },
+ { CDC_RX_TOP_RX_I2S_CTL, 0x0C },
+ { CDC_RX_TOP_TX_I2S2_CTL, 0x0C },
+ { CDC_RX_TOP_I2S_CLK, 0x0C },
+ { CDC_RX_TOP_I2S_RESET, 0x00 },
+ { CDC_RX_TOP_I2S_MUX, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_SWR_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_DSD_CONTROL, 0x00 },
+ { CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x08 },
+ { CDC_RX_SOFTCLIP_CRC, 0x00 },
+ { CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x38 },
+ { CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 },
+ { CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 },
+ { CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 },
+ { CDC_RX_INP_MUX_RX_MIX_CFG5, 0x00 },
+ { CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 },
+ { CDC_RX_CLSH_CRC, 0x00 },
+ { CDC_RX_CLSH_DLY_CTRL, 0x03 },
+ { CDC_RX_CLSH_DECAY_CTRL, 0x02 },
+ { CDC_RX_CLSH_HPH_V_PA, 0x1C },
+ { CDC_RX_CLSH_EAR_V_PA, 0x39 },
+ { CDC_RX_CLSH_HPH_V_HD, 0x0C },
+ { CDC_RX_CLSH_EAR_V_HD, 0x0C },
+ { CDC_RX_CLSH_K1_MSB, 0x01 },
+ { CDC_RX_CLSH_K1_LSB, 0x00 },
+ { CDC_RX_CLSH_K2_MSB, 0x00 },
+ { CDC_RX_CLSH_K2_LSB, 0x80 },
+ { CDC_RX_CLSH_IDLE_CTRL, 0x00 },
+ { CDC_RX_CLSH_IDLE_HPH, 0x00 },
+ { CDC_RX_CLSH_IDLE_EAR, 0x00 },
+ { CDC_RX_CLSH_TEST0, 0x07 },
+ { CDC_RX_CLSH_TEST1, 0x00 },
+ { CDC_RX_CLSH_OVR_VREF, 0x00 },
+ { CDC_RX_CLSH_CLSG_CTL, 0x02 },
+ { CDC_RX_CLSH_CLSG_CFG1, 0x9A },
+ { CDC_RX_CLSH_CLSG_CFG2, 0x10 },
+ { CDC_RX_BCL_VBAT_PATH_CTL, 0x00 },
+ { CDC_RX_BCL_VBAT_CFG, 0x10 },
+ { CDC_RX_BCL_VBAT_ADC_CAL1, 0x00 },
+ { CDC_RX_BCL_VBAT_ADC_CAL2, 0x00 },
+ { CDC_RX_BCL_VBAT_ADC_CAL3, 0x04 },
+ { CDC_RX_BCL_VBAT_PK_EST1, 0xE0 },
+ { CDC_RX_BCL_VBAT_PK_EST2, 0x01 },
+ { CDC_RX_BCL_VBAT_PK_EST3, 0x40 },
+ { CDC_RX_BCL_VBAT_RF_PROC1, 0x2A },
+ { CDC_RX_BCL_VBAT_RF_PROC2, 0x00 },
+ { CDC_RX_BCL_VBAT_TAC1, 0x00 },
+ { CDC_RX_BCL_VBAT_TAC2, 0x18 },
+ { CDC_RX_BCL_VBAT_TAC3, 0x18 },
+ { CDC_RX_BCL_VBAT_TAC4, 0x03 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD1, 0x01 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD2, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD3, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD4, 0x64 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD5, 0x01 },
+ { CDC_RX_BCL_VBAT_DEBUG1, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_UPD_MON, 0x00 },
+ { CDC_RX_BCL_VBAT_GAIN_MON_VAL, 0x00 },
+ { CDC_RX_BCL_VBAT_BAN, 0x0C },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD1, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD2, 0x77 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD3, 0x01 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD4, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD5, 0x4B },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD6, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD7, 0x01 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD8, 0x00 },
+ { CDC_RX_BCL_VBAT_BCL_GAIN_UPD9, 0x00 },
+ { CDC_RX_BCL_VBAT_ATTN1, 0x04 },
+ { CDC_RX_BCL_VBAT_ATTN2, 0x08 },
+ { CDC_RX_BCL_VBAT_ATTN3, 0x0C },
+ { CDC_RX_BCL_VBAT_DECODE_CTL1, 0xE0 },
+ { CDC_RX_BCL_VBAT_DECODE_CTL2, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG1, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG2, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG3, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_CFG4, 0x00 },
+ { CDC_RX_BCL_VBAT_DECODE_ST, 0x00 },
+ { CDC_RX_INTR_CTRL_CFG, 0x00 },
+ { CDC_RX_INTR_CTRL_CLR_COMMIT, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN1_MASK0, 0xFF },
+ { CDC_RX_INTR_CTRL_PIN1_STATUS0, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN1_CLEAR0, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN2_MASK0, 0xFF },
+ { CDC_RX_INTR_CTRL_PIN2_STATUS0, 0x00 },
+ { CDC_RX_INTR_CTRL_PIN2_CLEAR0, 0x00 },
+ { CDC_RX_INTR_CTRL_LEVEL0, 0x00 },
+ { CDC_RX_INTR_CTRL_BYPASS0, 0x00 },
+ { CDC_RX_INTR_CTRL_SET0, 0x00 },
+ { CDC_RX_RX0_RX_PATH_CTL, 0x04 },
+ { CDC_RX_RX0_RX_PATH_CFG0, 0x00 },
+ { CDC_RX_RX0_RX_PATH_CFG1, 0x64 },
+ { CDC_RX_RX0_RX_PATH_CFG2, 0x8F },
+ { CDC_RX_RX0_RX_PATH_CFG3, 0x00 },
+ { CDC_RX_RX0_RX_VOL_CTL, 0x00 },
+ { CDC_RX_RX0_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_RX_RX0_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_RX_RX0_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC1, 0x08 },
+ { CDC_RX_RX0_RX_PATH_SEC2, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC3, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC4, 0x00 },
+ { CDC_RX_RX0_RX_PATH_SEC7, 0x00 },
+ { CDC_RX_RX0_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_RX_RX0_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_RX_RX0_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_RX_IDLE_DETECT_PATH_CTL, 0x00 },
+ { CDC_RX_IDLE_DETECT_CFG0, 0x07 },
+ { CDC_RX_IDLE_DETECT_CFG1, 0x3C },
+ { CDC_RX_IDLE_DETECT_CFG2, 0x00 },
+ { CDC_RX_IDLE_DETECT_CFG3, 0x00 },
+ { CDC_RX_COMPANDER0_CTL0, 0x60 },
+ { CDC_RX_COMPANDER0_CTL1, 0xDB },
+ { CDC_RX_COMPANDER0_CTL2, 0xFF },
+ { CDC_RX_COMPANDER0_CTL3, 0x35 },
+ { CDC_RX_COMPANDER0_CTL4, 0xFF },
+ { CDC_RX_COMPANDER0_CTL5, 0x00 },
+ { CDC_RX_COMPANDER0_CTL6, 0x01 },
+ { CDC_RX_COMPANDER0_CTL7, 0x28 },
+ { CDC_RX_COMPANDER1_CTL0, 0x60 },
+ { CDC_RX_COMPANDER1_CTL1, 0xDB },
+ { CDC_RX_COMPANDER1_CTL2, 0xFF },
+ { CDC_RX_COMPANDER1_CTL3, 0x35 },
+ { CDC_RX_COMPANDER1_CTL4, 0xFF },
+ { CDC_RX_COMPANDER1_CTL5, 0x00 },
+ { CDC_RX_COMPANDER1_CTL6, 0x01 },
+ { CDC_RX_COMPANDER1_CTL7, 0x28 },
+ { CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_CTL, 0x40 },
+ { CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_CTL, 0x40 },
+ { CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 },
+ { CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 },
+ { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 },
+ { CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 },
+ { CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 },
+ { CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 },
+ { CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 },
+ { CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00 },
+ { CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01 },
+ { CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00 },
+ { CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01 },
+ { CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL, 0x00 },
+ { CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0, 0x01 },
+ { CDC_RX_EC_ASRC0_CLK_RST_CTL, 0x00 },
+ { CDC_RX_EC_ASRC0_CTL0, 0x00 },
+ { CDC_RX_EC_ASRC0_CTL1, 0x00 },
+ { CDC_RX_EC_ASRC0_FIFO_CTL, 0xA8 },
+ { CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC0_STATUS_FIFO, 0x00 },
+ { CDC_RX_EC_ASRC1_CLK_RST_CTL, 0x00 },
+ { CDC_RX_EC_ASRC1_CTL0, 0x00 },
+ { CDC_RX_EC_ASRC1_CTL1, 0x00 },
+ { CDC_RX_EC_ASRC1_FIFO_CTL, 0xA8 },
+ { CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC1_STATUS_FIFO, 0x00 },
+ { CDC_RX_EC_ASRC2_CLK_RST_CTL, 0x00 },
+ { CDC_RX_EC_ASRC2_CTL0, 0x00 },
+ { CDC_RX_EC_ASRC2_CTL1, 0x00 },
+ { CDC_RX_EC_ASRC2_FIFO_CTL, 0xA8 },
+ { CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00 },
+ { CDC_RX_EC_ASRC2_STATUS_FIFO, 0x00 },
+ { CDC_RX_DSD0_PATH_CTL, 0x00 },
+ { CDC_RX_DSD0_CFG0, 0x00 },
+ { CDC_RX_DSD0_CFG1, 0x62 },
+ { CDC_RX_DSD0_CFG2, 0x96 },
+ { CDC_RX_DSD1_PATH_CTL, 0x00 },
+ { CDC_RX_DSD1_CFG0, 0x00 },
+ { CDC_RX_DSD1_CFG1, 0x62 },
+ { CDC_RX_DSD1_CFG2, 0x96 },
+};
+
+static const struct reg_default rx_2_5_defaults[] = {
+ { CDC_2_5_RX_RX1_RX_PATH_CTL, 0x04 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG0, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG1, 0x64 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG2, 0x8F },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_VOL_CTL, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC1, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC2, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC4, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC7, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_2_5_RX_RX2_RX_PATH_CTL, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG0, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG1, 0x64 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG2, 0x8F },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG3, 0x00 },
+ { CDC_2_5_RX_RX2_RX_VOL_CTL, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC0, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC1, 0x08 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC2, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC3, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC4, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC5, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC6, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC7, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_DSM_CTL, 0x00 },
+};
+
+static const struct reg_default rx_pre_2_5_defaults[] = {
+ { CDC_RX_RX1_RX_PATH_CTL, 0x04 },
+ { CDC_RX_RX1_RX_PATH_CFG0, 0x00 },
+ { CDC_RX_RX1_RX_PATH_CFG1, 0x64 },
+ { CDC_RX_RX1_RX_PATH_CFG2, 0x8F },
+ { CDC_RX_RX1_RX_PATH_CFG3, 0x00 },
+ { CDC_RX_RX1_RX_VOL_CTL, 0x00 },
+ { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC1, 0x08 },
+ { CDC_RX_RX1_RX_PATH_SEC2, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC3, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC4, 0x00 },
+ { CDC_RX_RX1_RX_PATH_SEC7, 0x00 },
+ { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_RX_RX2_RX_PATH_CTL, 0x04 },
+ { CDC_RX_RX2_RX_PATH_CFG0, 0x00 },
+ { CDC_RX_RX2_RX_PATH_CFG1, 0x64 },
+ { CDC_RX_RX2_RX_PATH_CFG2, 0x8F },
+ { CDC_RX_RX2_RX_PATH_CFG3, 0x00 },
+ { CDC_RX_RX2_RX_VOL_CTL, 0x00 },
+ { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC0, 0x04 },
+ { CDC_RX_RX2_RX_PATH_SEC1, 0x08 },
+ { CDC_RX_RX2_RX_PATH_SEC2, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC3, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC4, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC5, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC6, 0x00 },
+ { CDC_RX_RX2_RX_PATH_SEC7, 0x00 },
+ { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 },
+
+};
+
+static bool rx_is_wronly_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case CDC_RX_BCL_VBAT_GAIN_UPD_MON:
+ case CDC_RX_INTR_CTRL_CLR_COMMIT:
+ case CDC_RX_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_RX_INTR_CTRL_PIN2_CLEAR0:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* Update volatile list for rx/tx macros */
+ switch (reg) {
+ case CDC_RX_TOP_HPHL_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHL_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHL_COMP_RD_MSB:
+ case CDC_RX_TOP_HPHL_COMP_WR_MSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHR_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_MSB:
+ case CDC_RX_TOP_HPHR_COMP_WR_MSB:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG2:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG2:
+ case CDC_RX_BCL_VBAT_GAIN_MON_VAL:
+ case CDC_RX_BCL_VBAT_DECODE_ST:
+ case CDC_RX_INTR_CTRL_PIN1_STATUS0:
+ case CDC_RX_INTR_CTRL_PIN2_STATUS0:
+ case CDC_RX_COMPANDER0_CTL6:
+ case CDC_RX_COMPANDER1_CTL6:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FIFO:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FIFO:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FIFO:
+ return true;
+ }
+ return false;
+}
+
+static bool rx_pre_2_5_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_RX_RX1_RX_PATH_CTL:
+ case CDC_RX_RX1_RX_PATH_CFG0:
+ case CDC_RX_RX1_RX_PATH_CFG1:
+ case CDC_RX_RX1_RX_PATH_CFG2:
+ case CDC_RX_RX1_RX_PATH_CFG3:
+ case CDC_RX_RX1_RX_VOL_CTL:
+ case CDC_RX_RX1_RX_PATH_MIX_CTL:
+ case CDC_RX_RX1_RX_PATH_MIX_CFG:
+ case CDC_RX_RX1_RX_VOL_MIX_CTL:
+ case CDC_RX_RX1_RX_PATH_SEC1:
+ case CDC_RX_RX1_RX_PATH_SEC2:
+ case CDC_RX_RX1_RX_PATH_SEC3:
+ case CDC_RX_RX1_RX_PATH_SEC4:
+ case CDC_RX_RX1_RX_PATH_SEC7:
+ case CDC_RX_RX1_RX_PATH_MIX_SEC0:
+ case CDC_RX_RX1_RX_PATH_MIX_SEC1:
+ case CDC_RX_RX1_RX_PATH_DSM_CTL:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA1:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA2:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA3:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA4:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA5:
+ case CDC_RX_RX1_RX_PATH_DSM_DATA6:
+ case CDC_RX_RX2_RX_PATH_CTL:
+ case CDC_RX_RX2_RX_PATH_CFG0:
+ case CDC_RX_RX2_RX_PATH_CFG1:
+ case CDC_RX_RX2_RX_PATH_CFG2:
+ case CDC_RX_RX2_RX_PATH_CFG3:
+ case CDC_RX_RX2_RX_VOL_CTL:
+ case CDC_RX_RX2_RX_PATH_MIX_CTL:
+ case CDC_RX_RX2_RX_PATH_MIX_CFG:
+ case CDC_RX_RX2_RX_VOL_MIX_CTL:
+ case CDC_RX_RX2_RX_PATH_SEC0:
+ case CDC_RX_RX2_RX_PATH_SEC1:
+ case CDC_RX_RX2_RX_PATH_SEC2:
+ case CDC_RX_RX2_RX_PATH_SEC3:
+ case CDC_RX_RX2_RX_PATH_SEC4:
+ case CDC_RX_RX2_RX_PATH_SEC5:
+ case CDC_RX_RX2_RX_PATH_SEC6:
+ case CDC_RX_RX2_RX_PATH_SEC7:
+ case CDC_RX_RX2_RX_PATH_MIX_SEC0:
+ case CDC_RX_RX2_RX_PATH_MIX_SEC1:
+ case CDC_RX_RX2_RX_PATH_DSM_CTL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_2_5_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_RX_RX1_RX_PATH_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG0:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG1:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG2:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG3:
+ case CDC_2_5_RX_RX1_RX_VOL_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_CFG:
+ case CDC_2_5_RX_RX1_RX_VOL_MIX_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC1:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC2:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC3:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC4:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC7:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6:
+ case CDC_2_5_RX_RX2_RX_PATH_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG0:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG1:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG2:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG3:
+ case CDC_2_5_RX_RX2_RX_VOL_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_CFG:
+ case CDC_2_5_RX_RX2_RX_VOL_MIX_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC0:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC1:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC2:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC3:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC4:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC5:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC6:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC7:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1:
+ case CDC_2_5_RX_RX2_RX_PATH_DSM_CTL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_is_rw_register(struct device *dev, unsigned int reg)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+
+ switch (reg) {
+ case CDC_RX_TOP_TOP_CFG0:
+ case CDC_RX_TOP_SWR_CTRL:
+ case CDC_RX_TOP_DEBUG:
+ case CDC_RX_TOP_DEBUG_BUS:
+ case CDC_RX_TOP_DEBUG_EN0:
+ case CDC_RX_TOP_DEBUG_EN1:
+ case CDC_RX_TOP_DEBUG_EN2:
+ case CDC_RX_TOP_HPHL_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHL_COMP_WR_MSB:
+ case CDC_RX_TOP_HPHL_COMP_LUT:
+ case CDC_RX_TOP_HPHR_COMP_WR_LSB:
+ case CDC_RX_TOP_HPHR_COMP_WR_MSB:
+ case CDC_RX_TOP_HPHR_COMP_LUT:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG0:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG1:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG3:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG0:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG1:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG3:
+ case CDC_RX_TOP_RX_I2S_CTL:
+ case CDC_RX_TOP_TX_I2S2_CTL:
+ case CDC_RX_TOP_I2S_CLK:
+ case CDC_RX_TOP_I2S_RESET:
+ case CDC_RX_TOP_I2S_MUX:
+ case CDC_RX_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_DSD_CONTROL:
+ case CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL:
+ case CDC_RX_SOFTCLIP_CRC:
+ case CDC_RX_SOFTCLIP_SOFTCLIP_CTRL:
+ case CDC_RX_INP_MUX_RX_INT0_CFG0:
+ case CDC_RX_INP_MUX_RX_INT0_CFG1:
+ case CDC_RX_INP_MUX_RX_INT1_CFG0:
+ case CDC_RX_INP_MUX_RX_INT1_CFG1:
+ case CDC_RX_INP_MUX_RX_INT2_CFG0:
+ case CDC_RX_INP_MUX_RX_INT2_CFG1:
+ case CDC_RX_INP_MUX_RX_MIX_CFG4:
+ case CDC_RX_INP_MUX_RX_MIX_CFG5:
+ case CDC_RX_INP_MUX_SIDETONE_SRC_CFG0:
+ case CDC_RX_CLSH_CRC:
+ case CDC_RX_CLSH_DLY_CTRL:
+ case CDC_RX_CLSH_DECAY_CTRL:
+ case CDC_RX_CLSH_HPH_V_PA:
+ case CDC_RX_CLSH_EAR_V_PA:
+ case CDC_RX_CLSH_HPH_V_HD:
+ case CDC_RX_CLSH_EAR_V_HD:
+ case CDC_RX_CLSH_K1_MSB:
+ case CDC_RX_CLSH_K1_LSB:
+ case CDC_RX_CLSH_K2_MSB:
+ case CDC_RX_CLSH_K2_LSB:
+ case CDC_RX_CLSH_IDLE_CTRL:
+ case CDC_RX_CLSH_IDLE_HPH:
+ case CDC_RX_CLSH_IDLE_EAR:
+ case CDC_RX_CLSH_TEST0:
+ case CDC_RX_CLSH_TEST1:
+ case CDC_RX_CLSH_OVR_VREF:
+ case CDC_RX_CLSH_CLSG_CTL:
+ case CDC_RX_CLSH_CLSG_CFG1:
+ case CDC_RX_CLSH_CLSG_CFG2:
+ case CDC_RX_BCL_VBAT_PATH_CTL:
+ case CDC_RX_BCL_VBAT_CFG:
+ case CDC_RX_BCL_VBAT_ADC_CAL1:
+ case CDC_RX_BCL_VBAT_ADC_CAL2:
+ case CDC_RX_BCL_VBAT_ADC_CAL3:
+ case CDC_RX_BCL_VBAT_PK_EST1:
+ case CDC_RX_BCL_VBAT_PK_EST2:
+ case CDC_RX_BCL_VBAT_PK_EST3:
+ case CDC_RX_BCL_VBAT_RF_PROC1:
+ case CDC_RX_BCL_VBAT_RF_PROC2:
+ case CDC_RX_BCL_VBAT_TAC1:
+ case CDC_RX_BCL_VBAT_TAC2:
+ case CDC_RX_BCL_VBAT_TAC3:
+ case CDC_RX_BCL_VBAT_TAC4:
+ case CDC_RX_BCL_VBAT_GAIN_UPD1:
+ case CDC_RX_BCL_VBAT_GAIN_UPD2:
+ case CDC_RX_BCL_VBAT_GAIN_UPD3:
+ case CDC_RX_BCL_VBAT_GAIN_UPD4:
+ case CDC_RX_BCL_VBAT_GAIN_UPD5:
+ case CDC_RX_BCL_VBAT_DEBUG1:
+ case CDC_RX_BCL_VBAT_BAN:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD1:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD2:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD3:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD4:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD5:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD6:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD7:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD8:
+ case CDC_RX_BCL_VBAT_BCL_GAIN_UPD9:
+ case CDC_RX_BCL_VBAT_ATTN1:
+ case CDC_RX_BCL_VBAT_ATTN2:
+ case CDC_RX_BCL_VBAT_ATTN3:
+ case CDC_RX_BCL_VBAT_DECODE_CTL1:
+ case CDC_RX_BCL_VBAT_DECODE_CTL2:
+ case CDC_RX_BCL_VBAT_DECODE_CFG1:
+ case CDC_RX_BCL_VBAT_DECODE_CFG2:
+ case CDC_RX_BCL_VBAT_DECODE_CFG3:
+ case CDC_RX_BCL_VBAT_DECODE_CFG4:
+ case CDC_RX_INTR_CTRL_CFG:
+ case CDC_RX_INTR_CTRL_PIN1_MASK0:
+ case CDC_RX_INTR_CTRL_PIN2_MASK0:
+ case CDC_RX_INTR_CTRL_LEVEL0:
+ case CDC_RX_INTR_CTRL_BYPASS0:
+ case CDC_RX_INTR_CTRL_SET0:
+ case CDC_RX_RX0_RX_PATH_CTL:
+ case CDC_RX_RX0_RX_PATH_CFG0:
+ case CDC_RX_RX0_RX_PATH_CFG1:
+ case CDC_RX_RX0_RX_PATH_CFG2:
+ case CDC_RX_RX0_RX_PATH_CFG3:
+ case CDC_RX_RX0_RX_VOL_CTL:
+ case CDC_RX_RX0_RX_PATH_MIX_CTL:
+ case CDC_RX_RX0_RX_PATH_MIX_CFG:
+ case CDC_RX_RX0_RX_VOL_MIX_CTL:
+ case CDC_RX_RX0_RX_PATH_SEC1:
+ case CDC_RX_RX0_RX_PATH_SEC2:
+ case CDC_RX_RX0_RX_PATH_SEC3:
+ case CDC_RX_RX0_RX_PATH_SEC4:
+ case CDC_RX_RX0_RX_PATH_SEC7:
+ case CDC_RX_RX0_RX_PATH_MIX_SEC0:
+ case CDC_RX_RX0_RX_PATH_MIX_SEC1:
+ case CDC_RX_RX0_RX_PATH_DSM_CTL:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA1:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA2:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA3:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA4:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA5:
+ case CDC_RX_RX0_RX_PATH_DSM_DATA6:
+ case CDC_RX_IDLE_DETECT_PATH_CTL:
+ case CDC_RX_IDLE_DETECT_CFG0:
+ case CDC_RX_IDLE_DETECT_CFG1:
+ case CDC_RX_IDLE_DETECT_CFG2:
+ case CDC_RX_IDLE_DETECT_CFG3:
+ case CDC_RX_COMPANDER0_CTL0:
+ case CDC_RX_COMPANDER0_CTL1:
+ case CDC_RX_COMPANDER0_CTL2:
+ case CDC_RX_COMPANDER0_CTL3:
+ case CDC_RX_COMPANDER0_CTL4:
+ case CDC_RX_COMPANDER0_CTL5:
+ case CDC_RX_COMPANDER0_CTL7:
+ case CDC_RX_COMPANDER1_CTL0:
+ case CDC_RX_COMPANDER1_CTL1:
+ case CDC_RX_COMPANDER1_CTL2:
+ case CDC_RX_COMPANDER1_CTL3:
+ case CDC_RX_COMPANDER1_CTL4:
+ case CDC_RX_COMPANDER1_CTL5:
+ case CDC_RX_COMPANDER1_CTL7:
+ case CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL:
+ case CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL:
+ case CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2:
+ case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2:
+ case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3:
+ case CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL:
+ case CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1:
+ case CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL:
+ case CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1:
+ case CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL:
+ case CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0:
+ case CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL:
+ case CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0:
+ case CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL:
+ case CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0:
+ case CDC_RX_EC_ASRC0_CLK_RST_CTL:
+ case CDC_RX_EC_ASRC0_CTL0:
+ case CDC_RX_EC_ASRC0_CTL1:
+ case CDC_RX_EC_ASRC0_FIFO_CTL:
+ case CDC_RX_EC_ASRC1_CLK_RST_CTL:
+ case CDC_RX_EC_ASRC1_CTL0:
+ case CDC_RX_EC_ASRC1_CTL1:
+ case CDC_RX_EC_ASRC1_FIFO_CTL:
+ case CDC_RX_EC_ASRC2_CLK_RST_CTL:
+ case CDC_RX_EC_ASRC2_CTL0:
+ case CDC_RX_EC_ASRC2_CTL1:
+ case CDC_RX_EC_ASRC2_FIFO_CTL:
+ case CDC_RX_DSD0_PATH_CTL:
+ case CDC_RX_DSD0_CFG0:
+ case CDC_RX_DSD0_CFG1:
+ case CDC_RX_DSD0_CFG2:
+ case CDC_RX_DSD1_PATH_CTL:
+ case CDC_RX_DSD1_CFG0:
+ case CDC_RX_DSD1_CFG1:
+ case CDC_RX_DSD1_CFG2:
+ return true;
+ }
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ return rx_pre_2_5_is_rw_register(dev, reg);
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ return rx_2_5_is_rw_register(dev, reg);
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool rx_is_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool ret;
+
+ ret = rx_is_rw_register(dev, reg);
+ if (!ret)
+ return rx_is_wronly_register(dev, reg);
+
+ return ret;
+}
+
+static bool rx_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_RX_TOP_HPHL_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHL_COMP_RD_MSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_LSB:
+ case CDC_RX_TOP_HPHR_COMP_RD_MSB:
+ case CDC_RX_TOP_DSD0_DEBUG_CFG2:
+ case CDC_RX_TOP_DSD1_DEBUG_CFG2:
+ case CDC_RX_BCL_VBAT_GAIN_MON_VAL:
+ case CDC_RX_BCL_VBAT_DECODE_ST:
+ case CDC_RX_INTR_CTRL_PIN1_STATUS0:
+ case CDC_RX_INTR_CTRL_PIN2_STATUS0:
+ case CDC_RX_COMPANDER0_CTL6:
+ case CDC_RX_COMPANDER1_CTL6:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC0_STATUS_FIFO:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC1_STATUS_FIFO:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB:
+ case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB:
+ case CDC_RX_EC_ASRC2_STATUS_FIFO:
+ return true;
+ }
+
+ return rx_is_rw_register(dev, reg);
+}
+
+static const struct regmap_config rx_regmap_config = {
+ .name = "rx_macro",
+ .reg_bits = 16,
+ .val_bits = 32, /* 8 but with 32 bit read/write */
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = RX_MAX_OFFSET,
+ .writeable_reg = rx_is_writeable_register,
+ .volatile_reg = rx_is_volatile_register,
+ .readable_reg = rx_is_readable_register,
+};
+
+static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned short look_ahead_dly_reg;
+ unsigned int val;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 0))
+ look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0);
+ else if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 1))
+ look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1);
+
+ /* Set Look Ahead Delay */
+ if (val)
+ snd_soc_component_update_bits(component, look_ahead_dly_reg,
+ CDC_RX_DLY_ZN_EN_MASK,
+ CDC_RX_DLY_ZN_ENABLE);
+ else
+ snd_soc_component_update_bits(component, look_ahead_dly_reg,
+ CDC_RX_DLY_ZN_EN_MASK, 0);
+ /* Set DEM INP Select */
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new rx_int0_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int0_dem_inp", rx_int0_dem_inp_enum,
+ snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put);
+static const struct snd_kcontrol_new rx_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_int1_dem_inp_enum,
+ snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_2_5_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_2_5_int1_dem_inp_enum,
+ snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put);
+
+static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ int rate_reg_val, u32 sample_rate)
+{
+
+ u8 int_1_mix1_inp;
+ u32 j, port;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) {
+ int_1_mix1_inp = port;
+ int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0;
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the rx port
+ * is connected
+ */
+ for (j = 0; j < INTERP_MAX; j++) {
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+
+ inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP0_SEL_MASK);
+ inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP1_SEL_MASK);
+ inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_RX_INTX_1_MIX_INP2_SEL_MASK);
+
+ if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) {
+ int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(rx, j);
+ /* sample_rate is in Hz */
+ snd_soc_component_update_bits(component, int_fs_reg,
+ CDC_RX_PATH_PCM_RATE_MASK,
+ rate_reg_val);
+ }
+ int_mux_cfg0 += 8;
+ }
+ }
+
+ return 0;
+}
+
+static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ int rate_reg_val, u32 sample_rate)
+{
+
+ u8 int_2_inp;
+ u32 j, port;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) {
+ int_2_inp = port;
+
+ int_mux_cfg1 = CDC_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < INTERP_MAX; j++) {
+ int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_RX_INTX_2_SEL_MASK);
+
+ if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) {
+ int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j);
+ snd_soc_component_update_bits(component, int_fs_reg,
+ CDC_RX_RXn_MIX_PCM_RATE_MASK,
+ rate_reg_val);
+ }
+ int_mux_cfg1 += 8;
+ }
+ }
+ return 0;
+}
+
+static int rx_macro_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++)
+ if (sample_rate == sr_val_tbl[i].sample_rate)
+ rate_val = sr_val_tbl[i].rate_val;
+
+ ret = rx_macro_set_prim_interpolator_rate(dai, rate_val, sample_rate);
+ if (ret)
+ return ret;
+
+ ret = rx_macro_set_mix_interpolator_rate(dai, rate_val, sample_rate);
+
+ return ret;
+}
+
+static int rx_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = rx_macro_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(component->dev, "%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ rx->bit_width[dai->id] = params_width(params);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rx_macro_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 val, mask = 0, cnt = 0, temp;
+
+ switch (dai->id) {
+ case RX_MACRO_AIF1_PB:
+ case RX_MACRO_AIF2_PB:
+ case RX_MACRO_AIF3_PB:
+ case RX_MACRO_AIF4_PB:
+ for_each_set_bit(temp, &rx->active_ch_mask[dai->id],
+ RX_MACRO_PORTS_MAX) {
+ mask |= (1 << temp);
+ if (++cnt == RX_MACRO_MAX_DMA_CH_PER_PORT)
+ break;
+ }
+ /*
+ * CDC_DMA_RX_0 port drives RX0/RX1 -- ch_mask 0x1/0x2/0x3
+ * CDC_DMA_RX_1 port drives RX2/RX3 -- ch_mask 0x1/0x2/0x3
+ * CDC_DMA_RX_2 port drives RX4 -- ch_mask 0x1
+ * CDC_DMA_RX_3 port drives RX5 -- ch_mask 0x1
+ * AIFn can pair to any CDC_DMA_RX_n port.
+ * In general, below convention is used::
+ * CDC_DMA_RX_0(AIF1)/CDC_DMA_RX_1(AIF2)/
+ * CDC_DMA_RX_2(AIF3)/CDC_DMA_RX_3(AIF4)
+ */
+ if (mask & 0x0C)
+ mask = mask >> 2;
+ if ((mask & 0x10) || (mask & 0x20))
+ mask = 0x1;
+ *rx_slot = mask;
+ *rx_num = rx->active_ch_cnt[dai->id];
+ break;
+ case RX_MACRO_AIF_ECHO:
+ val = snd_soc_component_read(component, CDC_RX_INP_MUX_RX_MIX_CFG4);
+ if (val & RX_MACRO_EC_MIX_TX0_MASK) {
+ mask |= 0x1;
+ cnt++;
+ }
+ if (val & RX_MACRO_EC_MIX_TX1_MASK) {
+ mask |= 0x2;
+ cnt++;
+ }
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG5);
+ if (val & RX_MACRO_EC_MIX_TX2_MASK) {
+ mask |= 0x4;
+ cnt++;
+ }
+ *tx_slot = mask;
+ *tx_num = cnt;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u32 port, j, reg, mix_reg, int_mux_cfg0, int_mux_cfg1;
+ u32 mask, val;
+ u8 int_mux_cfg0_val, int_mux_cfg1_val;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ for (j = 0; j < INTERP_MAX; j++) {
+ reg = CDC_RX_RXn_RX_PATH_CTL(rx, j);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j);
+
+ mask = CDC_RX_PATH_PGA_MUTE_MASK;
+ val = 0;
+ if (mute)
+ val |= CDC_RX_PATH_PGA_MUTE_ENABLE;
+ if (rx->main_clk_users[j] > 0) {
+ mask |= CDC_RX_PATH_CLK_EN_MASK;
+ val |= CDC_RX_PATH_CLK_ENABLE;
+ }
+
+ int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8;
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+ int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0);
+ int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1);
+
+ for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) {
+ if (((int_mux_cfg0_val & 0x0f) == port + INTn_1_INP_SEL_RX0) ||
+ ((int_mux_cfg0_val >> 4) == port + INTn_1_INP_SEL_RX0) ||
+ ((int_mux_cfg1_val >> 4) == port + INTn_1_INP_SEL_RX0)) {
+ snd_soc_component_update_bits(component, reg, mask, val);
+ }
+
+ if ((int_mux_cfg1_val & 0x0f) == port + INTn_2_INP_SEL_RX0) {
+ snd_soc_component_update_bits(component, mix_reg, mask, val);
+ /* main clock needs to be enabled for mix to be useful: */
+ if (rx->main_clk_users[j] > 0) {
+ snd_soc_component_update_bits(component, reg,
+ CDC_RX_PATH_CLK_EN_MASK,
+ CDC_RX_PATH_CLK_ENABLE);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rx_macro_dai_ops = {
+ .hw_params = rx_macro_hw_params,
+ .get_channel_map = rx_macro_get_channel_map,
+ .mute_stream = rx_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver rx_macro_dai[] = {
+ {
+ .name = "rx_macro_rx1",
+ .id = RX_MACRO_AIF1_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF1 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_rx2",
+ .id = RX_MACRO_AIF2_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF2 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_rx3",
+ .id = RX_MACRO_AIF3_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF3 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_rx4",
+ .id = RX_MACRO_AIF4_PB,
+ .playback = {
+ .stream_name = "RX_MACRO_AIF4 Playback",
+ .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES,
+ .formats = RX_MACRO_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+ {
+ .name = "rx_macro_echo",
+ .id = RX_MACRO_AIF_ECHO,
+ .capture = {
+ .stream_name = "RX_AIF_ECHO Capture",
+ .rates = RX_MACRO_ECHO_RATES,
+ .formats = RX_MACRO_ECHO_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ .ops = &rx_macro_dai_ops,
+ },
+};
+
+static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable)
+{
+ struct regmap *regmap = rx->regmap;
+
+ if (mclk_enable) {
+ if (rx->rx_mclk_users == 0) {
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_RX_CLK_MCLK_EN_MASK |
+ CDC_RX_CLK_MCLK2_EN_MASK,
+ CDC_RX_CLK_MCLK_ENABLE |
+ CDC_RX_CLK_MCLK2_ENABLE);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_CLR_MASK, 0x00);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_EN_MASK,
+ CDC_RX_FS_MCLK_CNT_ENABLE);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ }
+ rx->rx_mclk_users++;
+ } else {
+ if (rx->rx_mclk_users <= 0) {
+ dev_err(rx->dev, "%s: clock already disabled\n", __func__);
+ rx->rx_mclk_users = 0;
+ return;
+ }
+ rx->rx_mclk_users--;
+ if (rx->rx_mclk_users == 0) {
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_EN_MASK, 0x0);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_RX_FS_MCLK_CNT_CLR_MASK,
+ CDC_RX_FS_MCLK_CNT_CLR);
+ regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_RX_CLK_MCLK_EN_MASK |
+ CDC_RX_CLK_MCLK2_EN_MASK, 0x0);
+ }
+ }
+}
+
+static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_mclk_enable(rx, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rx_macro_mclk_enable(rx, false);
+ break;
+ default:
+ dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static bool rx_macro_adie_lb(struct snd_soc_component *component,
+ int interp_idx)
+{
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u8 int_n_inp0, int_n_inp1, int_n_inp2;
+
+ int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8;
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+
+ int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP0_SEL_MASK);
+ int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0,
+ CDC_RX_INTX_1_MIX_INP1_SEL_MASK);
+ int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1,
+ CDC_RX_INTX_1_MIX_INP2_SEL_MASK);
+
+ if (int_n_inp0 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp0 == INTn_1_INP_SEL_DEC1 ||
+ int_n_inp0 == INTn_1_INP_SEL_IIR0 ||
+ int_n_inp0 == INTn_1_INP_SEL_IIR1)
+ return true;
+
+ if (int_n_inp1 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp1 == INTn_1_INP_SEL_DEC1 ||
+ int_n_inp1 == INTn_1_INP_SEL_IIR0 ||
+ int_n_inp1 == INTn_1_INP_SEL_IIR1)
+ return true;
+
+ if (int_n_inp2 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp2 == INTn_1_INP_SEL_DEC1 ||
+ int_n_inp2 == INTn_1_INP_SEL_IIR0 ||
+ int_n_inp2 == INTn_1_INP_SEL_IIR1)
+ return true;
+
+ return false;
+}
+
+static int rx_macro_enable_interp_clk(struct snd_soc_component *component,
+ int event, int interp_idx);
+static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 gain_reg, reg;
+
+ reg = CDC_RX_RXn_RX_PATH_CTL(rx, w->shift);
+ gain_reg = CDC_RX_RXn_RX_VOL_CTL(rx, w->shift);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ if (rx_macro_adie_lb(component, w->shift))
+ snd_soc_component_update_bits(component, reg,
+ CDC_RX_PATH_CLK_EN_MASK,
+ CDC_RX_PATH_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, gain_reg,
+ snd_soc_component_read(component, gain_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ break;
+ }
+
+ return 0;
+}
+
+static int rx_macro_config_compander(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ int comp, int event)
+{
+ u8 pcm_rate, val;
+
+ /* AUX does not have compander */
+ if (comp == INTERP_AUX)
+ return 0;
+
+ pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(rx, comp)) & 0x0F;
+ if (pcm_rate < 0x06)
+ val = 0x03;
+ else if (pcm_rate < 0x08)
+ val = 0x01;
+ else if (pcm_rate < 0x0B)
+ val = 0x02;
+ else
+ val = 0x00;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, comp),
+ CDC_RX_DC_COEFF_SEL_MASK, val);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, comp),
+ CDC_RX_DC_COEFF_SEL_MASK, 0x3);
+ if (!rx->comp_enabled[comp])
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_CLK_EN_MASK, 0x1);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x1);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x0);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(rx, comp),
+ CDC_RX_RXn_COMP_EN_MASK, 0x1);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_HALT_MASK, 0x1);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(rx, comp),
+ CDC_RX_RXn_COMP_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_CLK_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp),
+ CDC_RX_COMPANDERn_HALT_MASK, 0x0);
+ }
+
+ return 0;
+}
+
+static int rx_macro_load_compander_coeff(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ int comp, int event)
+{
+ u16 comp_coeff_lsb_reg, comp_coeff_msb_reg;
+ int i;
+ int hph_pwr_mode;
+
+ /* AUX does not have compander */
+ if (comp == INTERP_AUX)
+ return 0;
+
+ if (!rx->comp_enabled[comp])
+ return 0;
+
+ if (comp == INTERP_HPHL) {
+ comp_coeff_lsb_reg = CDC_RX_TOP_HPHL_COMP_WR_LSB;
+ comp_coeff_msb_reg = CDC_RX_TOP_HPHL_COMP_WR_MSB;
+ } else if (comp == INTERP_HPHR) {
+ comp_coeff_lsb_reg = CDC_RX_TOP_HPHR_COMP_WR_LSB;
+ comp_coeff_msb_reg = CDC_RX_TOP_HPHR_COMP_WR_MSB;
+ } else {
+ /* compander coefficients are loaded only for hph path */
+ return 0;
+ }
+
+ hph_pwr_mode = rx->hph_pwr_mode;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Load Compander Coeff */
+ for (i = 0; i < COMP_MAX_COEFF; i++) {
+ snd_soc_component_write(component, comp_coeff_lsb_reg,
+ comp_coeff_table[hph_pwr_mode][i].lsb);
+ snd_soc_component_write(component, comp_coeff_msb_reg,
+ comp_coeff_table[hph_pwr_mode][i].msb);
+ }
+ }
+
+ return 0;
+}
+
+static void rx_macro_enable_softclip_clk(struct snd_soc_component *component,
+ struct rx_macro *rx, bool enable)
+{
+ if (enable) {
+ if (rx->softclip_clk_users == 0)
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_CRC,
+ CDC_RX_SOFTCLIP_CLK_EN_MASK, 1);
+ rx->softclip_clk_users++;
+ } else {
+ rx->softclip_clk_users--;
+ if (rx->softclip_clk_users == 0)
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_CRC,
+ CDC_RX_SOFTCLIP_CLK_EN_MASK, 0);
+ }
+}
+
+static int rx_macro_config_softclip(struct snd_soc_component *component,
+ struct rx_macro *rx, int event)
+{
+
+ if (!rx->is_softclip_on)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Softclip clock */
+ rx_macro_enable_softclip_clk(component, rx, true);
+ /* Enable Softclip control */
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_SOFTCLIP_CTRL,
+ CDC_RX_SOFTCLIP_EN_MASK, 0x01);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_SOFTCLIP_CTRL,
+ CDC_RX_SOFTCLIP_EN_MASK, 0x0);
+ rx_macro_enable_softclip_clk(component, rx, false);
+ }
+
+ return 0;
+}
+
+static int rx_macro_config_aux_hpf(struct snd_soc_component *component,
+ struct rx_macro *rx, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Update Aux HPF control */
+ if (!rx->is_aux_hpf_on)
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x00);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Reset to default (HPF=ON) */
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x04);
+ }
+
+ return 0;
+}
+
+static inline void rx_macro_enable_clsh_block(struct rx_macro *rx, bool enable)
+{
+ if ((enable && ++rx->clsh_users == 1) || (!enable && --rx->clsh_users == 0))
+ snd_soc_component_update_bits(rx->component, CDC_RX_CLSH_CRC,
+ CDC_RX_CLSH_CLK_EN_MASK, enable);
+ if (rx->clsh_users < 0)
+ rx->clsh_users = 0;
+}
+
+static int rx_macro_config_classh(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ int interp_n, int event)
+{
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ rx_macro_enable_clsh_block(rx, false);
+ return 0;
+ }
+
+ if (!SND_SOC_DAPM_EVENT_ON(event))
+ return 0;
+
+ rx_macro_enable_clsh_block(rx, true);
+ if (interp_n == INTERP_HPHL ||
+ interp_n == INTERP_HPHR) {
+ /*
+ * These K1 values depend on the Headphone Impedance
+ * For now it is assumed to be 16 ohm
+ */
+ snd_soc_component_write(component, CDC_RX_CLSH_K1_LSB, 0xc0);
+ snd_soc_component_write_field(component, CDC_RX_CLSH_K1_MSB,
+ CDC_RX_CLSH_K1_MSB_COEFF_MASK, 0);
+ }
+ switch (interp_n) {
+ case INTERP_HPHL:
+ if (rx->is_ear_mode_on)
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x39);
+ else
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x1c);
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_DECAY_CTRL,
+ CDC_RX_CLSH_DECAY_RATE_MASK, 0x0);
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 0),
+ CDC_RX_RXn_CLSH_EN_MASK, 0x1);
+ break;
+ case INTERP_HPHR:
+ if (rx->is_ear_mode_on)
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x39);
+ else
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_HPH_V_PA,
+ CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x1c);
+ snd_soc_component_update_bits(component,
+ CDC_RX_CLSH_DECAY_CTRL,
+ CDC_RX_CLSH_DECAY_RATE_MASK, 0x0);
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 1),
+ CDC_RX_RXn_CLSH_EN_MASK, 0x1);
+ break;
+ case INTERP_AUX:
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 2),
+ CDC_RX_RX2_DLY_Z_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 2),
+ CDC_RX_RX2_CLSH_EN_MASK, 1);
+ break;
+ }
+
+ return 0;
+}
+
+static void rx_macro_hd2_control(struct snd_soc_component *component,
+ u16 interp_idx, int event)
+{
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 hd2_scale_reg, hd2_enable_reg;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 0);
+ hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0);
+ break;
+ case INTERP_HPHR:
+ hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 1);
+ hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1);
+ break;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_RX_RXn_HD2_ALPHA_MASK, 0x14);
+ snd_soc_component_write_field(component, hd2_enable_reg,
+ CDC_RX_RXn_HD2_EN_MASK, 1);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component, hd2_enable_reg,
+ CDC_RX_RXn_HD2_EN_MASK, 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_RX_RXn_HD2_ALPHA_MASK, 0x0);
+ }
+}
+
+static int rx_macro_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->comp_enabled[comp];
+ return 0;
+}
+
+static int rx_macro_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->comp_enabled[comp] = value;
+
+ return 0;
+}
+
+static int rx_macro_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] =
+ rx->rx_port_value[widget->shift];
+ return 0;
+}
+
+static int rx_macro_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 rx_port_value = ucontrol->value.enumerated.item[0];
+ unsigned int dai_id;
+ u32 aif_rst;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ aif_rst = rx->rx_port_value[widget->shift];
+ if (!rx_port_value) {
+ if (aif_rst == 0)
+ return 0;
+ if (aif_rst > RX_MACRO_AIF4_PB) {
+ dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
+ return 0;
+ }
+ }
+ rx->rx_port_value[widget->shift] = rx_port_value;
+
+ switch (rx_port_value) {
+ case 0:
+ /*
+ * active_ch_cnt and active_ch_mask use DAI IDs (RX_MACRO_MAX_DAIS).
+ * active_ch_cnt == 0 was tested in if() above.
+ */
+ dai_id = aif_rst - 1;
+ if (rx->active_ch_cnt[dai_id]) {
+ clear_bit(widget->shift, &rx->active_ch_mask[dai_id]);
+ rx->active_ch_cnt[dai_id]--;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS). */
+ dai_id = rx_port_value - 1;
+ set_bit(widget->shift, &rx->active_ch_mask[dai_id]);
+ rx->active_ch_cnt[dai_id]++;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s:Invalid AIF_ID for RX_MACRO MUX %d\n",
+ __func__, rx_port_value);
+ goto err;
+ }
+
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+ return 0;
+err:
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new rx_macro_rx0_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx0", rx_macro_rx0_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx1_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx1", rx_macro_rx1_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx2_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx2", rx_macro_rx2_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx3_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx3", rx_macro_rx3_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx4_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx4", rx_macro_rx4_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+static const struct snd_kcontrol_new rx_macro_rx5_mux =
+ SOC_DAPM_ENUM_EXT("rx_macro_rx5", rx_macro_rx5_enum,
+ rx_macro_mux_get, rx_macro_mux_put);
+
+static int rx_macro_get_ear_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->is_ear_mode_on;
+ return 0;
+}
+
+static int rx_macro_put_ear_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->is_ear_mode_on = (!ucontrol->value.integer.value[0] ? false : true);
+ return 0;
+}
+
+static int rx_macro_get_hph_hd2_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->hph_hd2_mode;
+ return 0;
+}
+
+static int rx_macro_put_hph_hd2_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->hph_hd2_mode = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = rx->hph_pwr_mode;
+ return 0;
+}
+
+static int rx_macro_put_hph_pwr_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->hph_pwr_mode = ucontrol->value.enumerated.item[0];
+ return 0;
+}
+
+static int rx_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->is_softclip_on;
+
+ return 0;
+}
+
+static int rx_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->is_softclip_on = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int rx_macro_aux_hpf_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rx->is_aux_hpf_on;
+
+ return 0;
+}
+
+static int rx_macro_aux_hpf_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ rx->is_aux_hpf_on = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component,
+ struct rx_macro *rx,
+ u16 interp_idx, int event)
+{
+ u16 hph_lut_bypass_reg;
+ u16 hph_comp_ctrl7;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hph_lut_bypass_reg = CDC_RX_TOP_HPHL_COMP_LUT;
+ hph_comp_ctrl7 = CDC_RX_COMPANDER0_CTL7;
+ break;
+ case INTERP_HPHR:
+ hph_lut_bypass_reg = CDC_RX_TOP_HPHR_COMP_LUT;
+ hph_comp_ctrl7 = CDC_RX_COMPANDER1_CTL7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ if (interp_idx == INTERP_HPHL) {
+ if (rx->is_ear_mode_on)
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG1(rx, 0),
+ CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x1);
+ else
+ snd_soc_component_write_field(component,
+ hph_lut_bypass_reg,
+ CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, hph_lut_bypass_reg,
+ CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 1);
+ }
+ if (rx->hph_pwr_mode)
+ snd_soc_component_write_field(component, hph_comp_ctrl7,
+ CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK, 0x0);
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG1(rx, 0),
+ CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x0);
+ snd_soc_component_update_bits(component, hph_lut_bypass_reg,
+ CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 0);
+ snd_soc_component_write_field(component, hph_comp_ctrl7,
+ CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK, 0x1);
+ }
+
+ return 0;
+}
+
+static int rx_macro_enable_interp_clk(struct snd_soc_component *component,
+ int event, int interp_idx)
+{
+ u16 main_reg, dsm_reg, rx_cfg2_reg;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ main_reg = CDC_RX_RXn_RX_PATH_CTL(rx, interp_idx);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, interp_idx);
+ rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(rx, interp_idx);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (rx->main_clk_users[interp_idx] == 0) {
+ /* Main path PGA mute enable */
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_PGA_MUTE_MASK, 0x1);
+ snd_soc_component_write_field(component, dsm_reg,
+ CDC_RX_RXn_DSM_CLK_EN_MASK, 0x1);
+ snd_soc_component_update_bits(component, rx_cfg2_reg,
+ CDC_RX_RXn_HPF_CUT_FREQ_MASK, 0x03);
+ rx_macro_load_compander_coeff(component, rx, interp_idx, event);
+ if (rx->hph_hd2_mode)
+ rx_macro_hd2_control(component, interp_idx, event);
+ rx_macro_hphdelay_lutbypass(component, rx, interp_idx, event);
+ rx_macro_config_compander(component, rx, interp_idx, event);
+ if (interp_idx == INTERP_AUX) {
+ rx_macro_config_softclip(component, rx, event);
+ rx_macro_config_aux_hpf(component, rx, event);
+ }
+ rx_macro_config_classh(component, rx, interp_idx, event);
+ }
+ rx->main_clk_users[interp_idx]++;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ rx->main_clk_users[interp_idx]--;
+ if (rx->main_clk_users[interp_idx] <= 0) {
+ rx->main_clk_users[interp_idx] = 0;
+ /* Main path PGA mute enable */
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_PGA_MUTE_MASK, 0x1);
+ /* Clk Disable */
+ snd_soc_component_write_field(component, dsm_reg,
+ CDC_RX_RXn_DSM_CLK_EN_MASK, 0);
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_CLK_EN_MASK, 0);
+ /* Reset enable and disable */
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_RESET_EN_MASK, 1);
+ snd_soc_component_write_field(component, main_reg,
+ CDC_RX_PATH_RESET_EN_MASK, 0);
+ /* Reset rate to 48K*/
+ snd_soc_component_update_bits(component, main_reg,
+ CDC_RX_PATH_PCM_RATE_MASK,
+ 0x04);
+ snd_soc_component_update_bits(component, rx_cfg2_reg,
+ CDC_RX_RXn_HPF_CUT_FREQ_MASK, 0x00);
+ rx_macro_config_classh(component, rx, interp_idx, event);
+ rx_macro_config_compander(component, rx, interp_idx, event);
+ if (interp_idx == INTERP_AUX) {
+ rx_macro_config_softclip(component, rx, event);
+ rx_macro_config_aux_hpf(component, rx, event);
+ }
+ rx_macro_hphdelay_lutbypass(component, rx, interp_idx, event);
+ if (rx->hph_hd2_mode)
+ rx_macro_hd2_control(component, interp_idx, event);
+ }
+ }
+
+ return rx->main_clk_users[interp_idx];
+}
+
+static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 gain_reg, mix_reg;
+
+ gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(rx, w->shift);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, w->shift);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, gain_reg,
+ snd_soc_component_read(component, gain_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Clk Disable */
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_RXn_MIX_CLK_EN_MASK, 0x00);
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ /* Reset enable and disable */
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_RXn_MIX_RESET_MASK,
+ CDC_RX_RXn_MIX_RESET);
+ snd_soc_component_update_bits(component, mix_reg,
+ CDC_RX_RXn_MIX_RESET_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(rx, w->shift),
+ CDC_RX_RXn_SIDETONE_EN_MASK, 1);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(rx, w->shift),
+ CDC_RX_PATH_CLK_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(rx, w->shift),
+ CDC_RX_RXn_SIDETONE_EN_MASK, 0);
+ rx_macro_enable_interp_clk(component, event, w->shift);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rx_macro_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU: /* fall through */
+ case SND_SOC_DAPM_PRE_PMD:
+ if (strnstr(w->name, "IIR0", sizeof("IIR0"))) {
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL));
+ } else {
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL));
+ snd_soc_component_write(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL,
+ snd_soc_component_read(component,
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL));
+ }
+ break;
+ }
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx, int coeff_idx)
+{
+ u32 value;
+ int reg, b2_reg;
+
+ /* Address does not automatically update if reading */
+ reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx;
+ b2_reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx;
+
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx) *
+ sizeof(uint32_t)) & 0x7F);
+
+ value = snd_soc_component_read(component, b2_reg);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_component_read(component, b2_reg) << 8);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_component_read(component, b2_reg) << 16);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= (snd_soc_component_read(component, b2_reg) << 24);
+ return value;
+}
+
+static void set_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx, uint32_t value)
+{
+ int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx;
+
+ snd_soc_component_write(component, reg, (value & 0xFF));
+ snd_soc_component_write(component, reg, (value >> 8) & 0xFF);
+ snd_soc_component_write(component, reg, (value >> 16) & 0xFF);
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_component_write(component, reg, (value >> 24) & 0x3F);
+}
+
+static int rx_macro_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+ int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx;
+
+ memcpy(&coeff[0], ucontrol->value.bytes.data, params->max);
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_component_write(component, reg, (band_idx * BAND_MAX *
+ sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]);
+
+ return 0;
+}
+
+static int rx_macro_get_iir_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0);
+ coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1);
+ coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2);
+ coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3);
+ coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4);
+
+ memcpy(ucontrol->value.bytes.data, &coeff[0], params->max);
+
+ return 0;
+}
+
+static int rx_macro_iir_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rx_macro_def_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_RX_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_RX_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_RX_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_RX_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+};
+
+static const struct snd_kcontrol_new rx_macro_2_5_snd_controls[] = {
+
+ SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_2_5_RX_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_2_5_RX_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_2_5_RX_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_2_5_RX_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+};
+
+static const struct snd_kcontrol_new rx_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0,
+ rx_macro_get_compander, rx_macro_set_compander),
+ SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0,
+ rx_macro_get_compander, rx_macro_set_compander),
+
+ SOC_SINGLE_EXT("RX_EAR Mode Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_get_ear_mode, rx_macro_put_ear_mode),
+
+ SOC_SINGLE_EXT("RX_HPH HD2 Mode Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_get_hph_hd2_mode, rx_macro_put_hph_hd2_mode),
+
+ SOC_ENUM_EXT("RX_HPH PWR Mode", rx_macro_hph_pwr_mode_enum,
+ rx_macro_get_hph_pwr_mode, rx_macro_put_hph_pwr_mode),
+
+ SOC_SINGLE_EXT("RX_Softclip Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_soft_clip_enable_get,
+ rx_macro_soft_clip_enable_put),
+ SOC_SINGLE_EXT("AUX_HPF Switch", SND_SOC_NOPM, 0, 1, 0,
+ rx_macro_aux_hpf_mode_get,
+ rx_macro_aux_hpf_mode_put),
+
+ SOC_SINGLE_S8_TLV("IIR0 INP0 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP1 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP2 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP3 Volume",
+ CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP0 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP1 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP2 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP3 Volume",
+ CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, -84, 40,
+ digital_gain),
+
+ SOC_SINGLE("IIR1 Band1 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 0, 1, 0),
+ SOC_SINGLE("IIR1 Band2 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 1, 1, 0),
+ SOC_SINGLE("IIR1 Band3 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 2, 1, 0),
+ SOC_SINGLE("IIR1 Band4 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 3, 1, 0),
+ SOC_SINGLE("IIR1 Band5 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL,
+ 4, 1, 0),
+ SOC_SINGLE("IIR2 Band1 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 0, 1, 0),
+ SOC_SINGLE("IIR2 Band2 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 1, 1, 0),
+ SOC_SINGLE("IIR2 Band3 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 2, 1, 0),
+ SOC_SINGLE("IIR2 Band4 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 3, 1, 0),
+ SOC_SINGLE("IIR2 Band5 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL,
+ 4, 1, 0),
+
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4),
+ RX_MACRO_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5),
+
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4),
+ RX_MACRO_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5),
+
+};
+
+static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 val, ec_hq_reg;
+ int ec_tx = -1;
+
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG4);
+ if (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX0 MUX")))
+ ec_tx = ((val & 0xf0) >> 0x4) - 1;
+ else if (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX1 MUX")))
+ ec_tx = (val & 0x0f) - 1;
+
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG5);
+ if (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX2 MUX")))
+ ec_tx = (val & 0x0f) - 1;
+
+ if (ec_tx < 0 || (ec_tx >= RX_MACRO_EC_MUX_MAX)) {
+ dev_err(component->dev, "%s: EC mix control not set correctly\n",
+ __func__);
+ return -EINVAL;
+ }
+ ec_hq_reg = CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL +
+ 0x40 * ec_tx;
+ snd_soc_component_update_bits(component, ec_hq_reg, 0x01, 0x01);
+ ec_hq_reg = CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 +
+ 0x40 * ec_tx;
+ /* default set to 48k */
+ snd_soc_component_update_bits(component, ec_hq_reg, 0x1E, 0x08);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rx_macro_2_5_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_2_5_int1_dem_inp_mux),
+};
+
+static const struct snd_soc_dapm_widget rx_macro_def_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+};
+
+static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("RX AIF2 PB", "RX_MACRO_AIF2 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("RX AIF3 PB", "RX_MACRO_AIF3 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("RX AIF4 PB", "RX_MACRO_AIF4 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("RX AIF_ECHO", "RX_AIF_ECHO Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("RX_MACRO RX0 MUX", SND_SOC_NOPM, RX_MACRO_RX0, 0,
+ &rx_macro_rx0_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX1 MUX", SND_SOC_NOPM, RX_MACRO_RX1, 0,
+ &rx_macro_rx1_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX2 MUX", SND_SOC_NOPM, RX_MACRO_RX2, 0,
+ &rx_macro_rx2_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX3 MUX", SND_SOC_NOPM, RX_MACRO_RX3, 0,
+ &rx_macro_rx3_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX4 MUX", SND_SOC_NOPM, RX_MACRO_RX4, 0,
+ &rx_macro_rx4_mux),
+ SND_SOC_DAPM_MUX("RX_MACRO RX5 MUX", SND_SOC_NOPM, RX_MACRO_RX5, 0,
+ &rx_macro_rx5_mux),
+
+ SND_SOC_DAPM_MIXER("RX_RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX_RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+ SND_SOC_DAPM_MUX_E("RX MIX TX0 MUX", SND_SOC_NOPM,
+ RX_MACRO_EC0_MUX, 0,
+ &rx_mix_tx0_mux, rx_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX MIX TX1 MUX", SND_SOC_NOPM,
+ RX_MACRO_EC1_MUX, 0,
+ &rx_mix_tx1_mux, rx_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX MIX TX2 MUX", SND_SOC_NOPM,
+ RX_MACRO_EC2_MUX, 0,
+ &rx_mix_tx2_mux, rx_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("IIR0", CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL,
+ 4, 0, NULL, 0, rx_macro_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("IIR1", CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL,
+ 4, 0, NULL, 0, rx_macro_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("SRC0", CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SRC1", CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int0_dem_inp_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int0_2_mux, rx_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int1_2_mux, rx_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_AUX, 0,
+ &rx_int2_2_mux, rx_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp2_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int0_1_interp_mux, rx_macro_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int1_1_interp_mux, rx_macro_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_AUX, 0,
+ &rx_int2_1_interp_mux, rx_macro_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int0_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int1_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int2_2_interp_mux),
+
+ SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL,
+ 0, &rx_int0_mix2_inp_mux, rx_macro_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR,
+ 0, &rx_int1_mix2_inp_mux, rx_macro_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_AUX,
+ 0, &rx_int2_mix2_inp_mux, rx_macro_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPHL_OUT"),
+ SND_SOC_DAPM_OUTPUT("HPHR_OUT"),
+ SND_SOC_DAPM_OUTPUT("AUX_OUT"),
+
+ SND_SOC_DAPM_INPUT("RX_TX DEC0_INP"),
+ SND_SOC_DAPM_INPUT("RX_TX DEC1_INP"),
+ SND_SOC_DAPM_INPUT("RX_TX DEC2_INP"),
+ SND_SOC_DAPM_INPUT("RX_TX DEC3_INP"),
+
+ SND_SOC_DAPM_SUPPLY_S("RX_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ rx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rx_audio_map[] = {
+ {"RX AIF1 PB", NULL, "RX_MCLK"},
+ {"RX AIF2 PB", NULL, "RX_MCLK"},
+ {"RX AIF3 PB", NULL, "RX_MCLK"},
+ {"RX AIF4 PB", NULL, "RX_MCLK"},
+
+ {"RX_MACRO RX0 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX1 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX2 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX3 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX4 MUX", "AIF1_PB", "RX AIF1 PB"},
+ {"RX_MACRO RX5 MUX", "AIF1_PB", "RX AIF1 PB"},
+
+ {"RX_MACRO RX0 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX1 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX2 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX3 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX4 MUX", "AIF2_PB", "RX AIF2 PB"},
+ {"RX_MACRO RX5 MUX", "AIF2_PB", "RX AIF2 PB"},
+
+ {"RX_MACRO RX0 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX1 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX2 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX3 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX4 MUX", "AIF3_PB", "RX AIF3 PB"},
+ {"RX_MACRO RX5 MUX", "AIF3_PB", "RX AIF3 PB"},
+
+ {"RX_MACRO RX0 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX1 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX2 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX3 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX4 MUX", "AIF4_PB", "RX AIF4 PB"},
+ {"RX_MACRO RX5 MUX", "AIF4_PB", "RX AIF4 PB"},
+
+ {"RX_RX0", NULL, "RX_MACRO RX0 MUX"},
+ {"RX_RX1", NULL, "RX_MACRO RX1 MUX"},
+ {"RX_RX2", NULL, "RX_MACRO RX2 MUX"},
+ {"RX_RX3", NULL, "RX_MACRO RX3 MUX"},
+ {"RX_RX4", NULL, "RX_MACRO RX4 MUX"},
+ {"RX_RX5", NULL, "RX_MACRO RX5 MUX"},
+
+ {"RX INT0_1 MIX1 INP0", "RX0", "RX_RX0"},
+ {"RX INT0_1 MIX1 INP0", "RX1", "RX_RX1"},
+ {"RX INT0_1 MIX1 INP0", "RX2", "RX_RX2"},
+ {"RX INT0_1 MIX1 INP0", "RX3", "RX_RX3"},
+ {"RX INT0_1 MIX1 INP0", "RX4", "RX_RX4"},
+ {"RX INT0_1 MIX1 INP0", "RX5", "RX_RX5"},
+ {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT0_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT0_1 MIX1 INP1", "RX0", "RX_RX0"},
+ {"RX INT0_1 MIX1 INP1", "RX1", "RX_RX1"},
+ {"RX INT0_1 MIX1 INP1", "RX2", "RX_RX2"},
+ {"RX INT0_1 MIX1 INP1", "RX3", "RX_RX3"},
+ {"RX INT0_1 MIX1 INP1", "RX4", "RX_RX4"},
+ {"RX INT0_1 MIX1 INP1", "RX5", "RX_RX5"},
+ {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT0_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT0_1 MIX1 INP2", "RX0", "RX_RX0"},
+ {"RX INT0_1 MIX1 INP2", "RX1", "RX_RX1"},
+ {"RX INT0_1 MIX1 INP2", "RX2", "RX_RX2"},
+ {"RX INT0_1 MIX1 INP2", "RX3", "RX_RX3"},
+ {"RX INT0_1 MIX1 INP2", "RX4", "RX_RX4"},
+ {"RX INT0_1 MIX1 INP2", "RX5", "RX_RX5"},
+ {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT0_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"},
+
+ {"RX INT1_1 MIX1 INP0", "RX0", "RX_RX0"},
+ {"RX INT1_1 MIX1 INP0", "RX1", "RX_RX1"},
+ {"RX INT1_1 MIX1 INP0", "RX2", "RX_RX2"},
+ {"RX INT1_1 MIX1 INP0", "RX3", "RX_RX3"},
+ {"RX INT1_1 MIX1 INP0", "RX4", "RX_RX4"},
+ {"RX INT1_1 MIX1 INP0", "RX5", "RX_RX5"},
+ {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT1_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT1_1 MIX1 INP1", "RX0", "RX_RX0"},
+ {"RX INT1_1 MIX1 INP1", "RX1", "RX_RX1"},
+ {"RX INT1_1 MIX1 INP1", "RX2", "RX_RX2"},
+ {"RX INT1_1 MIX1 INP1", "RX3", "RX_RX3"},
+ {"RX INT1_1 MIX1 INP1", "RX4", "RX_RX4"},
+ {"RX INT1_1 MIX1 INP1", "RX5", "RX_RX5"},
+ {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT1_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT1_1 MIX1 INP2", "RX0", "RX_RX0"},
+ {"RX INT1_1 MIX1 INP2", "RX1", "RX_RX1"},
+ {"RX INT1_1 MIX1 INP2", "RX2", "RX_RX2"},
+ {"RX INT1_1 MIX1 INP2", "RX3", "RX_RX3"},
+ {"RX INT1_1 MIX1 INP2", "RX4", "RX_RX4"},
+ {"RX INT1_1 MIX1 INP2", "RX5", "RX_RX5"},
+ {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT1_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"},
+
+ {"RX INT2_1 MIX1 INP0", "RX0", "RX_RX0"},
+ {"RX INT2_1 MIX1 INP0", "RX1", "RX_RX1"},
+ {"RX INT2_1 MIX1 INP0", "RX2", "RX_RX2"},
+ {"RX INT2_1 MIX1 INP0", "RX3", "RX_RX3"},
+ {"RX INT2_1 MIX1 INP0", "RX4", "RX_RX4"},
+ {"RX INT2_1 MIX1 INP0", "RX5", "RX_RX5"},
+ {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT2_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT2_1 MIX1 INP1", "RX0", "RX_RX0"},
+ {"RX INT2_1 MIX1 INP1", "RX1", "RX_RX1"},
+ {"RX INT2_1 MIX1 INP1", "RX2", "RX_RX2"},
+ {"RX INT2_1 MIX1 INP1", "RX3", "RX_RX3"},
+ {"RX INT2_1 MIX1 INP1", "RX4", "RX_RX4"},
+ {"RX INT2_1 MIX1 INP1", "RX5", "RX_RX5"},
+ {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT2_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"},
+ {"RX INT2_1 MIX1 INP2", "RX0", "RX_RX0"},
+ {"RX INT2_1 MIX1 INP2", "RX1", "RX_RX1"},
+ {"RX INT2_1 MIX1 INP2", "RX2", "RX_RX2"},
+ {"RX INT2_1 MIX1 INP2", "RX3", "RX_RX3"},
+ {"RX INT2_1 MIX1 INP2", "RX4", "RX_RX4"},
+ {"RX INT2_1 MIX1 INP2", "RX5", "RX_RX5"},
+ {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"},
+ {"RX INT2_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"},
+
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"},
+
+ {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX AIF_ECHO", NULL, "RX MIX TX0 MUX"},
+ {"RX AIF_ECHO", NULL, "RX MIX TX1 MUX"},
+ {"RX AIF_ECHO", NULL, "RX MIX TX2 MUX"},
+ {"RX AIF_ECHO", NULL, "RX_MCLK"},
+
+ /* Mixing path INT0 */
+ {"RX INT0_2 MUX", "RX0", "RX_RX0"},
+ {"RX INT0_2 MUX", "RX1", "RX_RX1"},
+ {"RX INT0_2 MUX", "RX2", "RX_RX2"},
+ {"RX INT0_2 MUX", "RX3", "RX_RX3"},
+ {"RX INT0_2 MUX", "RX4", "RX_RX4"},
+ {"RX INT0_2 MUX", "RX5", "RX_RX5"},
+ {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"},
+
+ /* Mixing path INT1 */
+ {"RX INT1_2 MUX", "RX0", "RX_RX0"},
+ {"RX INT1_2 MUX", "RX1", "RX_RX1"},
+ {"RX INT1_2 MUX", "RX2", "RX_RX2"},
+ {"RX INT1_2 MUX", "RX3", "RX_RX3"},
+ {"RX INT1_2 MUX", "RX4", "RX_RX4"},
+ {"RX INT1_2 MUX", "RX5", "RX_RX5"},
+ {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"},
+
+ /* Mixing path INT2 */
+ {"RX INT2_2 MUX", "RX0", "RX_RX0"},
+ {"RX INT2_2 MUX", "RX1", "RX_RX1"},
+ {"RX INT2_2 MUX", "RX2", "RX_RX2"},
+ {"RX INT2_2 MUX", "RX3", "RX_RX3"},
+ {"RX INT2_2 MUX", "RX4", "RX_RX4"},
+ {"RX INT2_2 MUX", "RX5", "RX_RX5"},
+ {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"},
+ {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"},
+
+ {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"},
+ {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"},
+ {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"},
+ {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"},
+ {"HPHL_OUT", NULL, "RX INT0 DEM MUX"},
+ {"HPHL_OUT", NULL, "RX_MCLK"},
+
+ {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"},
+ {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
+ {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"},
+ {"HPHR_OUT", NULL, "RX INT1 DEM MUX"},
+ {"HPHR_OUT", NULL, "RX_MCLK"},
+
+ {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"},
+
+ {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"},
+ {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
+ {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
+ {"AUX_OUT", NULL, "RX INT2 MIX2"},
+ {"AUX_OUT", NULL, "RX_MCLK"},
+
+ {"IIR0", NULL, "RX_MCLK"},
+ {"IIR0", NULL, "IIR0 INP0 MUX"},
+ {"IIR0 INP0 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP0 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP0 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP0 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP0 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP0 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP0 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP0 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP0 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP0 MUX", "RX5", "RX_RX5"},
+ {"IIR0", NULL, "IIR0 INP1 MUX"},
+ {"IIR0 INP1 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP1 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP1 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP1 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP1 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP1 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP1 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP1 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP1 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP1 MUX", "RX5", "RX_RX5"},
+ {"IIR0", NULL, "IIR0 INP2 MUX"},
+ {"IIR0 INP2 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP2 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP2 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP2 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP2 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP2 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP2 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP2 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP2 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP2 MUX", "RX5", "RX_RX5"},
+ {"IIR0", NULL, "IIR0 INP3 MUX"},
+ {"IIR0 INP3 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR0 INP3 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR0 INP3 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR0 INP3 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR0 INP3 MUX", "RX0", "RX_RX0"},
+ {"IIR0 INP3 MUX", "RX1", "RX_RX1"},
+ {"IIR0 INP3 MUX", "RX2", "RX_RX2"},
+ {"IIR0 INP3 MUX", "RX3", "RX_RX3"},
+ {"IIR0 INP3 MUX", "RX4", "RX_RX4"},
+ {"IIR0 INP3 MUX", "RX5", "RX_RX5"},
+
+ {"IIR1", NULL, "RX_MCLK"},
+ {"IIR1", NULL, "IIR1 INP0 MUX"},
+ {"IIR1 INP0 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP0 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP0 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP0 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP0 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP0 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP0 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP0 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP0 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP0 MUX", "RX5", "RX_RX5"},
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP1 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP1 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP1 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP1 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP1 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP1 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP1 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP1 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP1 MUX", "RX5", "RX_RX5"},
+ {"IIR1", NULL, "IIR1 INP2 MUX"},
+ {"IIR1 INP2 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP2 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP2 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP2 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP2 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP2 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP2 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP2 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP2 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP2 MUX", "RX5", "RX_RX5"},
+ {"IIR1", NULL, "IIR1 INP3 MUX"},
+ {"IIR1 INP3 MUX", "DEC0", "RX_TX DEC0_INP"},
+ {"IIR1 INP3 MUX", "DEC1", "RX_TX DEC1_INP"},
+ {"IIR1 INP3 MUX", "DEC2", "RX_TX DEC2_INP"},
+ {"IIR1 INP3 MUX", "DEC3", "RX_TX DEC3_INP"},
+ {"IIR1 INP3 MUX", "RX0", "RX_RX0"},
+ {"IIR1 INP3 MUX", "RX1", "RX_RX1"},
+ {"IIR1 INP3 MUX", "RX2", "RX_RX2"},
+ {"IIR1 INP3 MUX", "RX3", "RX_RX3"},
+ {"IIR1 INP3 MUX", "RX4", "RX_RX4"},
+ {"IIR1 INP3 MUX", "RX5", "RX_RX5"},
+
+ {"SRC0", NULL, "IIR0"},
+ {"SRC1", NULL, "IIR1"},
+ {"RX INT0 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT0 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT1 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT1 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT2 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT2 MIX2 INP", "SRC1", "SRC1"},
+};
+
+static int rx_macro_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ const struct snd_soc_dapm_widget *widgets;
+ const struct snd_kcontrol_new *controls;
+ unsigned int num_controls, num_widgets;
+ int ret;
+
+ snd_soc_component_init_regmap(component, rx->regmap);
+
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 0),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 1),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 2),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 0),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 1),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 2),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ controls = rx_macro_def_snd_controls;
+ num_controls = ARRAY_SIZE(rx_macro_def_snd_controls);
+ widgets = rx_macro_def_dapm_widgets;
+ num_widgets = ARRAY_SIZE(rx_macro_def_dapm_widgets);
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ controls = rx_macro_2_5_snd_controls;
+ num_controls = ARRAY_SIZE(rx_macro_2_5_snd_controls);
+ widgets = rx_macro_2_5_dapm_widgets;
+ num_widgets = ARRAY_SIZE(rx_macro_2_5_dapm_widgets);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rx->component = component;
+
+ ret = snd_soc_add_component_controls(component, controls, num_controls);
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ struct rx_macro *rx = to_rx_macro(hw);
+ int ret;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret) {
+ dev_err(rx->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ rx_macro_mclk_enable(rx, true);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 1);
+
+ return 0;
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ struct rx_macro *rx = to_rx_macro(hw);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 0);
+
+ rx_macro_mclk_enable(rx, false);
+ clk_disable_unprepare(rx->mclk);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct rx_macro *rx = to_rx_macro(hw);
+ int ret, val;
+
+ regmap_read(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+
+};
+
+static int rx_macro_register_mclk_output(struct rx_macro *rx)
+{
+ struct device *dev = rx->dev;
+ const char *parent_clk_name = NULL;
+ const char *clk_name = "lpass-rx-mclk";
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (rx->npl)
+ parent_clk_name = __clk_get_name(rx->npl);
+ else
+ parent_clk_name = __clk_get_name(rx->mclk);
+
+ init.name = clk_name;
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ rx->hw.init = &init;
+ hw = &rx->hw;
+ ret = devm_clk_hw_register(rx->dev, hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct snd_soc_component_driver rx_macro_component_drv = {
+ .name = "RX-MACRO",
+ .probe = rx_macro_component_probe,
+ .controls = rx_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(rx_macro_snd_controls),
+ .dapm_widgets = rx_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rx_macro_dapm_widgets),
+ .dapm_routes = rx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rx_audio_map),
+};
+
+static int rx_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ kernel_ulong_t flags;
+ struct rx_macro *rx;
+ void __iomem *base;
+ int ret, def_count;
+
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
+ rx = devm_kzalloc(dev, sizeof(*rx), GFP_KERNEL);
+ if (!rx)
+ return -ENOMEM;
+
+ rx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(rx->macro))
+ return dev_err_probe(dev, PTR_ERR(rx->macro), "unable to get macro clock\n");
+
+ rx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(rx->dcodec))
+ return dev_err_probe(dev, PTR_ERR(rx->dcodec), "unable to get dcodec clock\n");
+
+ rx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(rx->mclk))
+ return dev_err_probe(dev, PTR_ERR(rx->mclk), "unable to get mclk clock\n");
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ rx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(rx->npl))
+ return dev_err_probe(dev, PTR_ERR(rx->npl), "unable to get npl clock\n");
+ }
+
+ rx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(rx->fsgen))
+ return dev_err_probe(dev, PTR_ERR(rx->fsgen), "unable to get fsgen clock\n");
+
+ rx->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(rx->pds))
+ return PTR_ERR(rx->pds);
+
+ ret = devm_add_action_or_reset(dev, lpass_macro_pds_exit_action, rx->pds);
+ if (ret)
+ return ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rx->codec_version = lpass_macro_get_codec_version();
+ struct reg_default *reg_defaults __free(kfree) = NULL;
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ rx->rxn_reg_stride = 0x80;
+ rx->rxn_reg_stride2 = 0xc;
+ def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults);
+ reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], rx_defaults, sizeof(rx_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(rx_defaults)],
+ rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults));
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ rx->rxn_reg_stride = 0xc0;
+ rx->rxn_reg_stride2 = 0x0;
+ def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_2_5_defaults);
+ reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], rx_defaults, sizeof(rx_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(rx_defaults)],
+ rx_2_5_defaults, sizeof(rx_2_5_defaults));
+ break;
+ default:
+ dev_err(dev, "Unsupported Codec version (%d)\n", rx->codec_version);
+ return -EINVAL;
+ }
+
+ struct regmap_config *reg_config __free(kfree) = kmemdup(&rx_regmap_config,
+ sizeof(*reg_config),
+ GFP_KERNEL);
+ if (!reg_config)
+ return -ENOMEM;
+
+ reg_config->reg_defaults = reg_defaults;
+ reg_config->num_reg_defaults = def_count;
+
+ rx->regmap = devm_regmap_init_mmio(dev, base, reg_config);
+ if (IS_ERR(rx->regmap))
+ return PTR_ERR(rx->regmap);
+
+ dev_set_drvdata(dev, rx);
+
+ rx->dev = dev;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(rx->mclk, MCLK_FREQ);
+ clk_set_rate(rx->npl, MCLK_FREQ);
+
+ ret = clk_prepare_enable(rx->macro);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(rx->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(rx->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(rx->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ /* reset swr block */
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_RESET_MASK,
+ CDC_RX_SWR_RESET);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 1);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_RESET_MASK, 0);
+
+ ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv,
+ rx_macro_dai,
+ ARRAY_SIZE(rx_macro_dai));
+ if (ret)
+ goto err_clkout;
+
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = rx_macro_register_mclk_output(rx);
+ if (ret)
+ goto err_clkout;
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(rx->fsgen);
+err_fsgen:
+ clk_disable_unprepare(rx->npl);
+err_npl:
+ clk_disable_unprepare(rx->mclk);
+err_mclk:
+ clk_disable_unprepare(rx->dcodec);
+err_dcodec:
+ clk_disable_unprepare(rx->macro);
+
+ return ret;
+}
+
+static void rx_macro_remove(struct platform_device *pdev)
+{
+ struct rx_macro *rx = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(rx->mclk);
+ clk_disable_unprepare(rx->npl);
+ clk_disable_unprepare(rx->fsgen);
+ clk_disable_unprepare(rx->macro);
+ clk_disable_unprepare(rx->dcodec);
+}
+
+static const struct of_device_id rx_macro_dt_match[] = {
+ {
+ .compatible = "qcom,sc7280-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+
+ }, {
+ .compatible = "qcom,sm6115-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8250-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-rx-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rx_macro_dt_match);
+
+static int rx_macro_runtime_suspend(struct device *dev)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+
+ regcache_cache_only(rx->regmap, true);
+ regcache_mark_dirty(rx->regmap);
+
+ clk_disable_unprepare(rx->fsgen);
+ clk_disable_unprepare(rx->npl);
+ clk_disable_unprepare(rx->mclk);
+
+ return 0;
+}
+
+static int rx_macro_runtime_resume(struct device *dev)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rx->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclkx2\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(rx->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+ regcache_cache_only(rx->regmap, false);
+ regcache_sync(rx->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(rx->npl);
+err_npl:
+ clk_disable_unprepare(rx->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops rx_macro_pm_ops = {
+ RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL)
+};
+
+static struct platform_driver rx_macro_driver = {
+ .driver = {
+ .name = "rx_macro",
+ .of_match_table = rx_macro_dt_match,
+ .suppress_bind_attrs = true,
+ .pm = pm_ptr(&rx_macro_pm_ops),
+ },
+ .probe = rx_macro_probe,
+ .remove = rx_macro_remove,
+};
+
+module_platform_driver(rx_macro_driver);
+
+MODULE_DESCRIPTION("RX macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
new file mode 100644
index 000000000000..f7d168f557dd
--- /dev/null
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -0,0 +1,2543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+
+#include "lpass-macro-common.h"
+
+#define CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_TX_MCLK_EN_MASK BIT(0)
+#define CDC_TX_MCLK_ENABLE BIT(0)
+#define CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_TX_FS_CNT_EN_MASK BIT(0)
+#define CDC_TX_FS_CNT_ENABLE BIT(0)
+#define CDC_TX_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_TX_SWR_RESET_MASK BIT(1)
+#define CDC_TX_SWR_RESET_ENABLE BIT(1)
+#define CDC_TX_SWR_CLK_EN_MASK BIT(0)
+#define CDC_TX_SWR_CLK_ENABLE BIT(0)
+#define CDC_TX_TOP_CSR_TOP_CFG0 (0x0080)
+#define CDC_TX_TOP_CSR_ANC_CFG (0x0084)
+#define CDC_TX_TOP_CSR_SWR_CTRL (0x0088)
+#define CDC_TX_TOP_CSR_FREQ_MCLK (0x0090)
+#define CDC_TX_TOP_CSR_DEBUG_BUS (0x0094)
+#define CDC_TX_TOP_CSR_DEBUG_EN (0x0098)
+#define CDC_TX_TOP_CSR_TX_I2S_CTL (0x00A4)
+#define CDC_TX_TOP_CSR_I2S_CLK (0x00A8)
+#define CDC_TX_TOP_CSR_I2S_RESET (0x00AC)
+#define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n) (0x00C0 + n * 0x4)
+#define CDC_TX_TOP_CSR_SWR_DMIC0_CTL (0x00C0)
+/* Default divider for AMIC and DMIC clock: DIV2 */
+#define CDC_TX_SWR_MIC_CLK_DEFAULT 0
+#define CDC_TX_SWR_DMIC_CLK_SEL_MASK GENMASK(3, 1)
+#define CDC_TX_TOP_CSR_SWR_DMIC1_CTL (0x00C4)
+#define CDC_TX_TOP_CSR_SWR_DMIC2_CTL (0x00C8)
+#define CDC_TX_TOP_CSR_SWR_DMIC3_CTL (0x00CC)
+#define CDC_TX_TOP_CSR_SWR_AMIC0_CTL (0x00D0)
+#define CDC_TX_TOP_CSR_SWR_AMIC1_CTL (0x00D4)
+#define CDC_TX_INP_MUX_ADC_MUXn_CFG0(n) (0x0100 + 0x8 * n)
+#define CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK GENMASK(3, 0)
+#define CDC_TX_MACRO_DMIC_MUX_SEL_MASK GENMASK(7, 4)
+#define CDC_TX_INP_MUX_ADC_MUX0_CFG0 (0x0100)
+#define CDC_TX_INP_MUX_ADC_MUXn_CFG1(n) (0x0104 + 0x8 * n)
+#define CDC_TX_INP_MUX_ADC_MUX0_CFG1 (0x0104)
+#define CDC_TX_INP_MUX_ADC_MUX1_CFG0 (0x0108)
+#define CDC_TX_INP_MUX_ADC_MUX1_CFG1 (0x010C)
+#define CDC_TX_INP_MUX_ADC_MUX2_CFG0 (0x0110)
+#define CDC_TX_INP_MUX_ADC_MUX2_CFG1 (0x0114)
+#define CDC_TX_INP_MUX_ADC_MUX3_CFG0 (0x0118)
+#define CDC_TX_INP_MUX_ADC_MUX3_CFG1 (0x011C)
+#define CDC_TX_INP_MUX_ADC_MUX4_CFG0 (0x0120)
+#define CDC_TX_INP_MUX_ADC_MUX4_CFG1 (0x0124)
+#define CDC_TX_INP_MUX_ADC_MUX5_CFG0 (0x0128)
+#define CDC_TX_INP_MUX_ADC_MUX5_CFG1 (0x012C)
+#define CDC_TX_INP_MUX_ADC_MUX6_CFG0 (0x0130)
+#define CDC_TX_INP_MUX_ADC_MUX6_CFG1 (0x0134)
+#define CDC_TX_INP_MUX_ADC_MUX7_CFG0 (0x0138)
+#define CDC_TX_INP_MUX_ADC_MUX7_CFG1 (0x013C)
+#define CDC_TX_ANC0_CLK_RESET_CTL (0x0200)
+#define CDC_TX_ANC0_MODE_1_CTL (0x0204)
+#define CDC_TX_ANC0_MODE_2_CTL (0x0208)
+#define CDC_TX_ANC0_FF_SHIFT (0x020C)
+#define CDC_TX_ANC0_FB_SHIFT (0x0210)
+#define CDC_TX_ANC0_LPF_FF_A_CTL (0x0214)
+#define CDC_TX_ANC0_LPF_FF_B_CTL (0x0218)
+#define CDC_TX_ANC0_LPF_FB_CTL (0x021C)
+#define CDC_TX_ANC0_SMLPF_CTL (0x0220)
+#define CDC_TX_ANC0_DCFLT_SHIFT_CTL (0x0224)
+#define CDC_TX_ANC0_IIR_ADAPT_CTL (0x0228)
+#define CDC_TX_ANC0_IIR_COEFF_1_CTL (0x022C)
+#define CDC_TX_ANC0_IIR_COEFF_2_CTL (0x0230)
+#define CDC_TX_ANC0_FF_A_GAIN_CTL (0x0234)
+#define CDC_TX_ANC0_FF_B_GAIN_CTL (0x0238)
+#define CDC_TX_ANC0_FB_GAIN_CTL (0x023C)
+#define CDC_TXn_TX_PATH_CTL(n) (0x0400 + 0x80 * n)
+#define CDC_TXn_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_TXn_PGA_MUTE_MASK BIT(4)
+#define CDC_TXn_CLK_EN_MASK BIT(5)
+#define CDC_TX0_TX_PATH_CTL (0x0400)
+#define CDC_TXn_TX_PATH_CFG0(n) (0x0404 + 0x80 * n)
+#define CDC_TX0_TX_PATH_CFG0 (0x0404)
+#define CDC_TXn_PH_EN_MASK BIT(0)
+#define CDC_TXn_ADC_MODE_MASK GENMASK(2, 1)
+#define CDC_TXn_HPF_CUT_FREQ_MASK GENMASK(6, 5)
+#define CDC_TXn_ADC_DMIC_SEL_MASK BIT(7)
+#define CDC_TX0_TX_PATH_CFG1 (0x0408)
+#define CDC_TXn_TX_VOL_CTL(n) (0x040C + 0x80 * n)
+#define CDC_TX0_TX_VOL_CTL (0x040C)
+#define CDC_TX0_TX_PATH_SEC0 (0x0410)
+#define CDC_TX0_TX_PATH_SEC1 (0x0414)
+#define CDC_TXn_TX_PATH_SEC2(n) (0x0418 + 0x80 * n)
+#define CDC_TXn_HPF_F_CHANGE_MASK BIT(1)
+#define CDC_TXn_HPF_ZERO_GATE_MASK BIT(0)
+#define CDC_TX0_TX_PATH_SEC2 (0x0418)
+#define CDC_TX0_TX_PATH_SEC3 (0x041C)
+#define CDC_TX0_TX_PATH_SEC4 (0x0420)
+#define CDC_TX0_TX_PATH_SEC5 (0x0424)
+#define CDC_TX0_TX_PATH_SEC6 (0x0428)
+#define CDC_TX0_TX_PATH_SEC7 (0x042C)
+#define CDC_TX0_MBHC_CTL_EN_MASK BIT(6)
+#define CDC_TX1_TX_PATH_CTL (0x0480)
+#define CDC_TX1_TX_PATH_CFG0 (0x0484)
+#define CDC_TX1_TX_PATH_CFG1 (0x0488)
+#define CDC_TX1_TX_VOL_CTL (0x048C)
+#define CDC_TX1_TX_PATH_SEC0 (0x0490)
+#define CDC_TX1_TX_PATH_SEC1 (0x0494)
+#define CDC_TX1_TX_PATH_SEC2 (0x0498)
+#define CDC_TX1_TX_PATH_SEC3 (0x049C)
+#define CDC_TX1_TX_PATH_SEC4 (0x04A0)
+#define CDC_TX1_TX_PATH_SEC5 (0x04A4)
+#define CDC_TX1_TX_PATH_SEC6 (0x04A8)
+#define CDC_TX2_TX_PATH_CTL (0x0500)
+#define CDC_TX2_TX_PATH_CFG0 (0x0504)
+#define CDC_TX2_TX_PATH_CFG1 (0x0508)
+#define CDC_TX2_TX_VOL_CTL (0x050C)
+#define CDC_TX2_TX_PATH_SEC0 (0x0510)
+#define CDC_TX2_TX_PATH_SEC1 (0x0514)
+#define CDC_TX2_TX_PATH_SEC2 (0x0518)
+#define CDC_TX2_TX_PATH_SEC3 (0x051C)
+#define CDC_TX2_TX_PATH_SEC4 (0x0520)
+#define CDC_TX2_TX_PATH_SEC5 (0x0524)
+#define CDC_TX2_TX_PATH_SEC6 (0x0528)
+#define CDC_TX3_TX_PATH_CTL (0x0580)
+#define CDC_TX3_TX_PATH_CFG0 (0x0584)
+#define CDC_TX3_TX_PATH_CFG1 (0x0588)
+#define CDC_TX3_TX_VOL_CTL (0x058C)
+#define CDC_TX3_TX_PATH_SEC0 (0x0590)
+#define CDC_TX3_TX_PATH_SEC1 (0x0594)
+#define CDC_TX3_TX_PATH_SEC2 (0x0598)
+#define CDC_TX3_TX_PATH_SEC3 (0x059C)
+#define CDC_TX3_TX_PATH_SEC4 (0x05A0)
+#define CDC_TX3_TX_PATH_SEC5 (0x05A4)
+#define CDC_TX3_TX_PATH_SEC6 (0x05A8)
+#define CDC_TX4_TX_PATH_CTL (0x0600)
+#define CDC_TX4_TX_PATH_CFG0 (0x0604)
+#define CDC_TX4_TX_PATH_CFG1 (0x0608)
+#define CDC_TX4_TX_VOL_CTL (0x060C)
+#define CDC_TX4_TX_PATH_SEC0 (0x0610)
+#define CDC_TX4_TX_PATH_SEC1 (0x0614)
+#define CDC_TX4_TX_PATH_SEC2 (0x0618)
+#define CDC_TX4_TX_PATH_SEC3 (0x061C)
+#define CDC_TX4_TX_PATH_SEC4 (0x0620)
+#define CDC_TX4_TX_PATH_SEC5 (0x0624)
+#define CDC_TX4_TX_PATH_SEC6 (0x0628)
+#define CDC_TX5_TX_PATH_CTL (0x0680)
+#define CDC_TX5_TX_PATH_CFG0 (0x0684)
+#define CDC_TX5_TX_PATH_CFG1 (0x0688)
+#define CDC_TX5_TX_VOL_CTL (0x068C)
+#define CDC_TX5_TX_PATH_SEC0 (0x0690)
+#define CDC_TX5_TX_PATH_SEC1 (0x0694)
+#define CDC_TX5_TX_PATH_SEC2 (0x0698)
+#define CDC_TX5_TX_PATH_SEC3 (0x069C)
+#define CDC_TX5_TX_PATH_SEC4 (0x06A0)
+#define CDC_TX5_TX_PATH_SEC5 (0x06A4)
+#define CDC_TX5_TX_PATH_SEC6 (0x06A8)
+#define CDC_TX6_TX_PATH_CTL (0x0700)
+#define CDC_TX6_TX_PATH_CFG0 (0x0704)
+#define CDC_TX6_TX_PATH_CFG1 (0x0708)
+#define CDC_TX6_TX_VOL_CTL (0x070C)
+#define CDC_TX6_TX_PATH_SEC0 (0x0710)
+#define CDC_TX6_TX_PATH_SEC1 (0x0714)
+#define CDC_TX6_TX_PATH_SEC2 (0x0718)
+#define CDC_TX6_TX_PATH_SEC3 (0x071C)
+#define CDC_TX6_TX_PATH_SEC4 (0x0720)
+#define CDC_TX6_TX_PATH_SEC5 (0x0724)
+#define CDC_TX6_TX_PATH_SEC6 (0x0728)
+#define CDC_TX7_TX_PATH_CTL (0x0780)
+#define CDC_TX7_TX_PATH_CFG0 (0x0784)
+#define CDC_TX7_TX_PATH_CFG1 (0x0788)
+#define CDC_TX7_TX_VOL_CTL (0x078C)
+#define CDC_TX7_TX_PATH_SEC0 (0x0790)
+#define CDC_TX7_TX_PATH_SEC1 (0x0794)
+#define CDC_TX7_TX_PATH_SEC2 (0x0798)
+#define CDC_TX7_TX_PATH_SEC3 (0x079C)
+#define CDC_TX7_TX_PATH_SEC4 (0x07A0)
+#define CDC_TX7_TX_PATH_SEC5 (0x07A4)
+#define CDC_TX7_TX_PATH_SEC6 (0x07A8)
+#define TX_MAX_OFFSET (0x07A8)
+
+#define TX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define TX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+#define TX_ADC_MAX 5
+#define TX_ADC_TO_DMIC(n) ((n - TX_ADC_MAX)/2)
+#define NUM_DECIMATORS 8
+#define TX_NUM_CLKS_MAX 5
+#define TX_MACRO_DMIC_UNMUTE_DELAY_MS 40
+#define TX_MACRO_AMIC_UNMUTE_DELAY_MS 100
+#define TX_MACRO_DMIC_HPF_DELAY_MS 300
+#define TX_MACRO_AMIC_HPF_DELAY_MS 300
+#define MCLK_FREQ 19200000
+
+enum {
+ TX_MACRO_AIF1_CAP,
+ TX_MACRO_AIF2_CAP,
+ TX_MACRO_AIF3_CAP,
+ TX_MACRO_MAX_DAIS
+};
+
+enum {
+ TX_MACRO_DEC0,
+ TX_MACRO_DEC1,
+ TX_MACRO_DEC2,
+ TX_MACRO_DEC3,
+ TX_MACRO_DEC4,
+ TX_MACRO_DEC5,
+ TX_MACRO_DEC6,
+ TX_MACRO_DEC7,
+ TX_MACRO_DEC_MAX,
+};
+
+enum {
+ TX_MACRO_CLK_DIV_2,
+ TX_MACRO_CLK_DIV_3,
+ TX_MACRO_CLK_DIV_4,
+ TX_MACRO_CLK_DIV_6,
+ TX_MACRO_CLK_DIV_8,
+ TX_MACRO_CLK_DIV_16,
+};
+
+enum {
+ MSM_DMIC,
+ SWR_MIC,
+ ANC_FB_TUNE1
+};
+
+struct tx_mute_work {
+ struct tx_macro *tx;
+ u8 decimator;
+ struct delayed_work dwork;
+};
+
+struct hpf_work {
+ struct tx_macro *tx;
+ u8 decimator;
+ u8 hpf_cut_off_freq;
+ struct delayed_work dwork;
+};
+
+struct tx_macro_data {
+ unsigned int flags;
+ unsigned int ver;
+ const struct snd_soc_dapm_widget *extra_widgets;
+ size_t extra_widgets_num;
+ const struct snd_soc_dapm_route *extra_routes;
+ size_t extra_routes_num;
+};
+
+struct tx_macro {
+ struct device *dev;
+ const struct tx_macro_data *data;
+ struct snd_soc_component *component;
+ struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+ struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS];
+ unsigned long active_ch_mask[TX_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS];
+ int active_decimator[TX_MACRO_MAX_DAIS];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+ bool dec_active[NUM_DECIMATORS];
+ int tx_mclk_users;
+ bool bcs_enable;
+ int dec_mode[NUM_DECIMATORS];
+ struct lpass_macro *pds;
+ bool bcs_clk_en;
+};
+#define to_tx_macro(_hw) container_of(_hw, struct tx_macro, hw)
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static struct reg_default tx_defaults[] = {
+ /* TX Macro */
+ { CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 },
+ { CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 },
+ { CDC_TX_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_TX_TOP_CSR_TOP_CFG0, 0x00},
+ { CDC_TX_TOP_CSR_ANC_CFG, 0x00},
+ { CDC_TX_TOP_CSR_SWR_CTRL, 0x00},
+ { CDC_TX_TOP_CSR_FREQ_MCLK, 0x00},
+ { CDC_TX_TOP_CSR_DEBUG_BUS, 0x00},
+ { CDC_TX_TOP_CSR_DEBUG_EN, 0x00},
+ { CDC_TX_TOP_CSR_TX_I2S_CTL, 0x0C},
+ { CDC_TX_TOP_CSR_I2S_CLK, 0x00},
+ { CDC_TX_TOP_CSR_I2S_RESET, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC0_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC1_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC2_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_DMIC3_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0x00},
+ { CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX4_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX5_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX6_CFG1, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00},
+ { CDC_TX_INP_MUX_ADC_MUX7_CFG1, 0x00},
+ { CDC_TX_ANC0_CLK_RESET_CTL, 0x00},
+ { CDC_TX_ANC0_MODE_1_CTL, 0x00},
+ { CDC_TX_ANC0_MODE_2_CTL, 0x00},
+ { CDC_TX_ANC0_FF_SHIFT, 0x00},
+ { CDC_TX_ANC0_FB_SHIFT, 0x00},
+ { CDC_TX_ANC0_LPF_FF_A_CTL, 0x00},
+ { CDC_TX_ANC0_LPF_FF_B_CTL, 0x00},
+ { CDC_TX_ANC0_LPF_FB_CTL, 0x00},
+ { CDC_TX_ANC0_SMLPF_CTL, 0x00},
+ { CDC_TX_ANC0_DCFLT_SHIFT_CTL, 0x00},
+ { CDC_TX_ANC0_IIR_ADAPT_CTL, 0x00},
+ { CDC_TX_ANC0_IIR_COEFF_1_CTL, 0x00},
+ { CDC_TX_ANC0_IIR_COEFF_2_CTL, 0x00},
+ { CDC_TX_ANC0_FF_A_GAIN_CTL, 0x00},
+ { CDC_TX_ANC0_FF_B_GAIN_CTL, 0x00},
+ { CDC_TX_ANC0_FB_GAIN_CTL, 0x00},
+ { CDC_TX0_TX_PATH_CTL, 0x04},
+ { CDC_TX0_TX_PATH_CFG0, 0x10},
+ { CDC_TX0_TX_PATH_CFG1, 0x0B},
+ { CDC_TX0_TX_VOL_CTL, 0x00},
+ { CDC_TX0_TX_PATH_SEC0, 0x00},
+ { CDC_TX0_TX_PATH_SEC1, 0x00},
+ { CDC_TX0_TX_PATH_SEC2, 0x01},
+ { CDC_TX0_TX_PATH_SEC3, 0x3C},
+ { CDC_TX0_TX_PATH_SEC4, 0x20},
+ { CDC_TX0_TX_PATH_SEC5, 0x00},
+ { CDC_TX0_TX_PATH_SEC6, 0x00},
+ { CDC_TX0_TX_PATH_SEC7, 0x25},
+ { CDC_TX1_TX_PATH_CTL, 0x04},
+ { CDC_TX1_TX_PATH_CFG0, 0x10},
+ { CDC_TX1_TX_PATH_CFG1, 0x0B},
+ { CDC_TX1_TX_VOL_CTL, 0x00},
+ { CDC_TX1_TX_PATH_SEC0, 0x00},
+ { CDC_TX1_TX_PATH_SEC1, 0x00},
+ { CDC_TX1_TX_PATH_SEC2, 0x01},
+ { CDC_TX1_TX_PATH_SEC3, 0x3C},
+ { CDC_TX1_TX_PATH_SEC4, 0x20},
+ { CDC_TX1_TX_PATH_SEC5, 0x00},
+ { CDC_TX1_TX_PATH_SEC6, 0x00},
+ { CDC_TX2_TX_PATH_CTL, 0x04},
+ { CDC_TX2_TX_PATH_CFG0, 0x10},
+ { CDC_TX2_TX_PATH_CFG1, 0x0B},
+ { CDC_TX2_TX_VOL_CTL, 0x00},
+ { CDC_TX2_TX_PATH_SEC0, 0x00},
+ { CDC_TX2_TX_PATH_SEC1, 0x00},
+ { CDC_TX2_TX_PATH_SEC2, 0x01},
+ { CDC_TX2_TX_PATH_SEC3, 0x3C},
+ { CDC_TX2_TX_PATH_SEC4, 0x20},
+ { CDC_TX2_TX_PATH_SEC5, 0x00},
+ { CDC_TX2_TX_PATH_SEC6, 0x00},
+ { CDC_TX3_TX_PATH_CTL, 0x04},
+ { CDC_TX3_TX_PATH_CFG0, 0x10},
+ { CDC_TX3_TX_PATH_CFG1, 0x0B},
+ { CDC_TX3_TX_VOL_CTL, 0x00},
+ { CDC_TX3_TX_PATH_SEC0, 0x00},
+ { CDC_TX3_TX_PATH_SEC1, 0x00},
+ { CDC_TX3_TX_PATH_SEC2, 0x01},
+ { CDC_TX3_TX_PATH_SEC3, 0x3C},
+ { CDC_TX3_TX_PATH_SEC4, 0x20},
+ { CDC_TX3_TX_PATH_SEC5, 0x00},
+ { CDC_TX3_TX_PATH_SEC6, 0x00},
+ { CDC_TX4_TX_PATH_CTL, 0x04},
+ { CDC_TX4_TX_PATH_CFG0, 0x10},
+ { CDC_TX4_TX_PATH_CFG1, 0x0B},
+ { CDC_TX4_TX_VOL_CTL, 0x00},
+ { CDC_TX4_TX_PATH_SEC0, 0x00},
+ { CDC_TX4_TX_PATH_SEC1, 0x00},
+ { CDC_TX4_TX_PATH_SEC2, 0x01},
+ { CDC_TX4_TX_PATH_SEC3, 0x3C},
+ { CDC_TX4_TX_PATH_SEC4, 0x20},
+ { CDC_TX4_TX_PATH_SEC5, 0x00},
+ { CDC_TX4_TX_PATH_SEC6, 0x00},
+ { CDC_TX5_TX_PATH_CTL, 0x04},
+ { CDC_TX5_TX_PATH_CFG0, 0x10},
+ { CDC_TX5_TX_PATH_CFG1, 0x0B},
+ { CDC_TX5_TX_VOL_CTL, 0x00},
+ { CDC_TX5_TX_PATH_SEC0, 0x00},
+ { CDC_TX5_TX_PATH_SEC1, 0x00},
+ { CDC_TX5_TX_PATH_SEC2, 0x01},
+ { CDC_TX5_TX_PATH_SEC3, 0x3C},
+ { CDC_TX5_TX_PATH_SEC4, 0x20},
+ { CDC_TX5_TX_PATH_SEC5, 0x00},
+ { CDC_TX5_TX_PATH_SEC6, 0x00},
+ { CDC_TX6_TX_PATH_CTL, 0x04},
+ { CDC_TX6_TX_PATH_CFG0, 0x10},
+ { CDC_TX6_TX_PATH_CFG1, 0x0B},
+ { CDC_TX6_TX_VOL_CTL, 0x00},
+ { CDC_TX6_TX_PATH_SEC0, 0x00},
+ { CDC_TX6_TX_PATH_SEC1, 0x00},
+ { CDC_TX6_TX_PATH_SEC2, 0x01},
+ { CDC_TX6_TX_PATH_SEC3, 0x3C},
+ { CDC_TX6_TX_PATH_SEC4, 0x20},
+ { CDC_TX6_TX_PATH_SEC5, 0x00},
+ { CDC_TX6_TX_PATH_SEC6, 0x00},
+ { CDC_TX7_TX_PATH_CTL, 0x04},
+ { CDC_TX7_TX_PATH_CFG0, 0x10},
+ { CDC_TX7_TX_PATH_CFG1, 0x0B},
+ { CDC_TX7_TX_VOL_CTL, 0x00},
+ { CDC_TX7_TX_PATH_SEC0, 0x00},
+ { CDC_TX7_TX_PATH_SEC1, 0x00},
+ { CDC_TX7_TX_PATH_SEC2, 0x01},
+ { CDC_TX7_TX_PATH_SEC3, 0x3C},
+ { CDC_TX7_TX_PATH_SEC4, 0x20},
+ { CDC_TX7_TX_PATH_SEC5, 0x00},
+ { CDC_TX7_TX_PATH_SEC6, 0x00},
+};
+
+static bool tx_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* Update volatile list for tx/tx macros */
+ switch (reg) {
+ case CDC_TX_TOP_CSR_SWR_DMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC1_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC2_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC3_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
+ return true;
+ }
+ return false;
+}
+
+static bool tx_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_TX_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_TX_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_TX_TOP_CSR_TOP_CFG0:
+ case CDC_TX_TOP_CSR_ANC_CFG:
+ case CDC_TX_TOP_CSR_SWR_CTRL:
+ case CDC_TX_TOP_CSR_FREQ_MCLK:
+ case CDC_TX_TOP_CSR_DEBUG_BUS:
+ case CDC_TX_TOP_CSR_DEBUG_EN:
+ case CDC_TX_TOP_CSR_TX_I2S_CTL:
+ case CDC_TX_TOP_CSR_I2S_CLK:
+ case CDC_TX_TOP_CSR_I2S_RESET:
+ case CDC_TX_TOP_CSR_SWR_DMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC1_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC2_CTL:
+ case CDC_TX_TOP_CSR_SWR_DMIC3_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
+ case CDC_TX_ANC0_CLK_RESET_CTL:
+ case CDC_TX_ANC0_MODE_1_CTL:
+ case CDC_TX_ANC0_MODE_2_CTL:
+ case CDC_TX_ANC0_FF_SHIFT:
+ case CDC_TX_ANC0_FB_SHIFT:
+ case CDC_TX_ANC0_LPF_FF_A_CTL:
+ case CDC_TX_ANC0_LPF_FF_B_CTL:
+ case CDC_TX_ANC0_LPF_FB_CTL:
+ case CDC_TX_ANC0_SMLPF_CTL:
+ case CDC_TX_ANC0_DCFLT_SHIFT_CTL:
+ case CDC_TX_ANC0_IIR_ADAPT_CTL:
+ case CDC_TX_ANC0_IIR_COEFF_1_CTL:
+ case CDC_TX_ANC0_IIR_COEFF_2_CTL:
+ case CDC_TX_ANC0_FF_A_GAIN_CTL:
+ case CDC_TX_ANC0_FF_B_GAIN_CTL:
+ case CDC_TX_ANC0_FB_GAIN_CTL:
+ case CDC_TX_INP_MUX_ADC_MUX0_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX1_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX2_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX3_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX4_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX5_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX6_CFG1:
+ case CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+ case CDC_TX_INP_MUX_ADC_MUX7_CFG1:
+ case CDC_TX0_TX_PATH_CTL:
+ case CDC_TX0_TX_PATH_CFG0:
+ case CDC_TX0_TX_PATH_CFG1:
+ case CDC_TX0_TX_VOL_CTL:
+ case CDC_TX0_TX_PATH_SEC0:
+ case CDC_TX0_TX_PATH_SEC1:
+ case CDC_TX0_TX_PATH_SEC2:
+ case CDC_TX0_TX_PATH_SEC3:
+ case CDC_TX0_TX_PATH_SEC4:
+ case CDC_TX0_TX_PATH_SEC5:
+ case CDC_TX0_TX_PATH_SEC6:
+ case CDC_TX0_TX_PATH_SEC7:
+ case CDC_TX1_TX_PATH_CTL:
+ case CDC_TX1_TX_PATH_CFG0:
+ case CDC_TX1_TX_PATH_CFG1:
+ case CDC_TX1_TX_VOL_CTL:
+ case CDC_TX1_TX_PATH_SEC0:
+ case CDC_TX1_TX_PATH_SEC1:
+ case CDC_TX1_TX_PATH_SEC2:
+ case CDC_TX1_TX_PATH_SEC3:
+ case CDC_TX1_TX_PATH_SEC4:
+ case CDC_TX1_TX_PATH_SEC5:
+ case CDC_TX1_TX_PATH_SEC6:
+ case CDC_TX2_TX_PATH_CTL:
+ case CDC_TX2_TX_PATH_CFG0:
+ case CDC_TX2_TX_PATH_CFG1:
+ case CDC_TX2_TX_VOL_CTL:
+ case CDC_TX2_TX_PATH_SEC0:
+ case CDC_TX2_TX_PATH_SEC1:
+ case CDC_TX2_TX_PATH_SEC2:
+ case CDC_TX2_TX_PATH_SEC3:
+ case CDC_TX2_TX_PATH_SEC4:
+ case CDC_TX2_TX_PATH_SEC5:
+ case CDC_TX2_TX_PATH_SEC6:
+ case CDC_TX3_TX_PATH_CTL:
+ case CDC_TX3_TX_PATH_CFG0:
+ case CDC_TX3_TX_PATH_CFG1:
+ case CDC_TX3_TX_VOL_CTL:
+ case CDC_TX3_TX_PATH_SEC0:
+ case CDC_TX3_TX_PATH_SEC1:
+ case CDC_TX3_TX_PATH_SEC2:
+ case CDC_TX3_TX_PATH_SEC3:
+ case CDC_TX3_TX_PATH_SEC4:
+ case CDC_TX3_TX_PATH_SEC5:
+ case CDC_TX3_TX_PATH_SEC6:
+ case CDC_TX4_TX_PATH_CTL:
+ case CDC_TX4_TX_PATH_CFG0:
+ case CDC_TX4_TX_PATH_CFG1:
+ case CDC_TX4_TX_VOL_CTL:
+ case CDC_TX4_TX_PATH_SEC0:
+ case CDC_TX4_TX_PATH_SEC1:
+ case CDC_TX4_TX_PATH_SEC2:
+ case CDC_TX4_TX_PATH_SEC3:
+ case CDC_TX4_TX_PATH_SEC4:
+ case CDC_TX4_TX_PATH_SEC5:
+ case CDC_TX4_TX_PATH_SEC6:
+ case CDC_TX5_TX_PATH_CTL:
+ case CDC_TX5_TX_PATH_CFG0:
+ case CDC_TX5_TX_PATH_CFG1:
+ case CDC_TX5_TX_VOL_CTL:
+ case CDC_TX5_TX_PATH_SEC0:
+ case CDC_TX5_TX_PATH_SEC1:
+ case CDC_TX5_TX_PATH_SEC2:
+ case CDC_TX5_TX_PATH_SEC3:
+ case CDC_TX5_TX_PATH_SEC4:
+ case CDC_TX5_TX_PATH_SEC5:
+ case CDC_TX5_TX_PATH_SEC6:
+ case CDC_TX6_TX_PATH_CTL:
+ case CDC_TX6_TX_PATH_CFG0:
+ case CDC_TX6_TX_PATH_CFG1:
+ case CDC_TX6_TX_VOL_CTL:
+ case CDC_TX6_TX_PATH_SEC0:
+ case CDC_TX6_TX_PATH_SEC1:
+ case CDC_TX6_TX_PATH_SEC2:
+ case CDC_TX6_TX_PATH_SEC3:
+ case CDC_TX6_TX_PATH_SEC4:
+ case CDC_TX6_TX_PATH_SEC5:
+ case CDC_TX6_TX_PATH_SEC6:
+ case CDC_TX7_TX_PATH_CTL:
+ case CDC_TX7_TX_PATH_CFG0:
+ case CDC_TX7_TX_PATH_CFG1:
+ case CDC_TX7_TX_VOL_CTL:
+ case CDC_TX7_TX_PATH_SEC0:
+ case CDC_TX7_TX_PATH_SEC1:
+ case CDC_TX7_TX_PATH_SEC2:
+ case CDC_TX7_TX_PATH_SEC3:
+ case CDC_TX7_TX_PATH_SEC4:
+ case CDC_TX7_TX_PATH_SEC5:
+ case CDC_TX7_TX_PATH_SEC6:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config tx_regmap_config = {
+ .name = "tx_macro",
+ .reg_bits = 16,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = TX_MAX_OFFSET,
+ .reg_defaults = tx_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tx_defaults),
+ .writeable_reg = tx_is_rw_register,
+ .volatile_reg = tx_is_volatile_register,
+ .readable_reg = tx_is_rw_register,
+};
+
+static int tx_macro_mclk_enable(struct tx_macro *tx,
+ bool mclk_enable)
+{
+ struct regmap *regmap = tx->regmap;
+
+ if (mclk_enable) {
+ if (tx->tx_mclk_users == 0) {
+ /* 9.6MHz MCLK, set value 0x00 if other frequency */
+ regmap_update_bits(regmap, CDC_TX_TOP_CSR_FREQ_MCLK, 0x01, 0x01);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_TX_MCLK_EN_MASK,
+ CDC_TX_MCLK_ENABLE);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_TX_FS_CNT_EN_MASK,
+ CDC_TX_FS_CNT_ENABLE);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ }
+ tx->tx_mclk_users++;
+ } else {
+ if (tx->tx_mclk_users <= 0) {
+ dev_err(tx->dev, "clock already disabled\n");
+ tx->tx_mclk_users = 0;
+ goto exit;
+ }
+ tx->tx_mclk_users--;
+ if (tx->tx_mclk_users == 0) {
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_TX_FS_CNT_EN_MASK, 0x0);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_TX_MCLK_EN_MASK, 0x0);
+ }
+ }
+exit:
+ return 0;
+}
+
+static bool is_amic_enabled(struct snd_soc_component *component,
+ struct tx_macro *tx, u8 decimator)
+{
+ u16 adc_mux_reg, adc_reg, adc_n;
+
+ adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator);
+
+ if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) {
+ if (tx->data->ver > LPASS_VER_9_0_0)
+ return true;
+
+ /* else: LPASS <= v9.0.0 */
+ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator);
+ adc_n = snd_soc_component_read_field(component, adc_reg,
+ CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK);
+ if (adc_n < TX_ADC_MAX)
+ return true;
+ }
+
+ return false;
+}
+
+static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct tx_macro *tx;
+ struct snd_soc_component *component;
+ u16 dec_cfg_reg, hpf_gate_reg;
+ u8 hpf_cut_off_freq;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ tx = hpf_work->tx;
+ component = tx->component;
+ hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
+
+ dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator);
+ hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator);
+
+ if (is_amic_enabled(component, tx, hpf_work->decimator)) {
+ snd_soc_component_write_field(component,
+ dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ hpf_cut_off_freq);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x02);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x01);
+ } else {
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ hpf_cut_off_freq);
+ snd_soc_component_write_field(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK, 0x1);
+ /* Minimum 1 clk cycle delay is required as per HW spec */
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK, 0x0);
+ }
+}
+
+static void tx_macro_mute_update_callback(struct work_struct *work)
+{
+ struct tx_mute_work *tx_mute_dwork;
+ struct snd_soc_component *component;
+ struct tx_macro *tx;
+ struct delayed_work *delayed_work;
+ u8 decimator;
+
+ delayed_work = to_delayed_work(work);
+ tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+ tx = tx_mute_dwork->tx;
+ component = tx->component;
+ decimator = tx_mute_dwork->decimator;
+
+ snd_soc_component_write_field(component, CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PGA_MUTE_MASK, 0x0);
+}
+
+static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tx_macro_mclk_enable(tx, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tx_macro_mclk_enable(tx, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void tx_macro_update_smic_sel_v9(struct snd_soc_component *component,
+ struct snd_soc_dapm_widget *widget,
+ struct tx_macro *tx, u16 mic_sel_reg,
+ unsigned int val)
+{
+ unsigned int dmic;
+ u16 dmic_clk_reg;
+
+ if (val < 5) {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 0);
+ } else {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+ dmic = TX_ADC_TO_DMIC(val);
+ dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ }
+}
+
+static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component,
+ struct snd_soc_dapm_widget *widget,
+ struct tx_macro *tx, u16 mic_sel_reg,
+ unsigned int val)
+{
+ unsigned int dmic;
+ u16 dmic_clk_reg;
+
+ if (widget->shift) {
+ /* MSM DMIC */
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+
+ dmic = TX_ADC_TO_DMIC(val);
+ dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ } else {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 0);
+ }
+}
+
+static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ u16 mic_sel_reg;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val >= e->items)
+ return -EINVAL;
+
+ switch (e->reg) {
+ case CDC_TX_INP_MUX_ADC_MUX0_CFG0:
+ mic_sel_reg = CDC_TX0_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX1_CFG0:
+ mic_sel_reg = CDC_TX1_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX2_CFG0:
+ mic_sel_reg = CDC_TX2_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX3_CFG0:
+ mic_sel_reg = CDC_TX3_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+ mic_sel_reg = CDC_TX4_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+ mic_sel_reg = CDC_TX5_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+ mic_sel_reg = CDC_TX6_TX_PATH_CFG0;
+ break;
+ case CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+ mic_sel_reg = CDC_TX7_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(component->dev, "Error in configuration!!\n");
+ return -EINVAL;
+ }
+
+ if (val != 0) {
+ if (widget->shift) /* MSM DMIC */
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 1);
+ else if (tx->data->ver <= LPASS_VER_9_0_0)
+ tx_macro_update_smic_sel_v9(component, widget, tx,
+ mic_sel_reg, val);
+ else
+ tx_macro_update_smic_sel_v9_2(component, widget, tx,
+ mic_sel_reg, val);
+ }
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tx_macro_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(dec_id, &tx->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ if (tx->active_decimator[dai_id] == dec_id)
+ return 0;
+
+ set_bit(dec_id, &tx->active_ch_mask[dai_id]);
+ tx->active_ch_cnt[dai_id]++;
+ tx->active_decimator[dai_id] = dec_id;
+ } else {
+ if (tx->active_decimator[dai_id] == -1)
+ return 0;
+
+ tx->active_ch_cnt[dai_id]--;
+ clear_bit(dec_id, &tx->active_ch_mask[dai_id]);
+ tx->active_decimator[dai_id] = -1;
+ }
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 1;
+}
+
+static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u8 decimator;
+ u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg, tx_gain_ctl_reg;
+ u8 hpf_cut_off_freq;
+ int hpf_delay = TX_MACRO_DMIC_HPF_DELAY_MS;
+ int unmute_delay = TX_MACRO_DMIC_UNMUTE_DELAY_MS;
+ u16 adc_mux_reg, adc_reg, adc_n, dmic;
+ u16 dmic_clk_reg;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ decimator = w->shift;
+ tx_vol_ctl_reg = CDC_TXn_TX_PATH_CTL(decimator);
+ hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(decimator);
+ dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(decimator);
+ tx_gain_ctl_reg = CDC_TXn_TX_VOL_CTL(decimator);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator);
+ if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) {
+ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator);
+ adc_n = snd_soc_component_read(component, adc_reg) &
+ CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK;
+ if (adc_n >= TX_ADC_MAX) {
+ dmic = TX_ADC_TO_DMIC(adc_n);
+ dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic);
+
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ CDC_TX_SWR_DMIC_CLK_SEL_MASK,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ }
+ }
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_ADC_MODE_MASK,
+ tx->dec_mode[decimator]);
+ /* Enable TX PGA Mute */
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_PGA_MUTE_MASK, 0x1);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_CLK_EN_MASK, 0x1);
+ if (!is_amic_enabled(component, tx, decimator)) {
+ snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00);
+ /* Minimum 1 clk cycle delay is required as per HW spec */
+ usleep_range(1000, 1010);
+ }
+ hpf_cut_off_freq = snd_soc_component_read_field(component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK);
+
+ tx->tx_hpf_work[decimator].hpf_cut_off_freq =
+ hpf_cut_off_freq;
+
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ)
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ CF_MIN_3DB_150HZ);
+
+ if (is_amic_enabled(component, tx, decimator)) {
+ hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS;
+ unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS;
+ }
+ /* schedule work queue to Remove Mute */
+ queue_delayed_work(system_freezable_wq,
+ &tx->tx_mute_dwork[decimator].dwork,
+ msecs_to_jiffies(unmute_delay));
+ if (tx->tx_hpf_work[decimator].hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ queue_delayed_work(system_freezable_wq,
+ &tx->tx_hpf_work[decimator].dwork,
+ msecs_to_jiffies(hpf_delay));
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x02);
+ if (!is_amic_enabled(component, tx, decimator))
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x00);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x01);
+
+ /*
+ * 6ms delay is required as per HW spec
+ */
+ usleep_range(6000, 6010);
+ }
+ /* apply gain after decimator is enabled */
+ snd_soc_component_write(component, tx_gain_ctl_reg,
+ snd_soc_component_read(component,
+ tx_gain_ctl_reg));
+ if (tx->bcs_enable) {
+ snd_soc_component_update_bits(component, dec_cfg_reg,
+ 0x01, 0x01);
+ tx->bcs_clk_en = true;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ hpf_cut_off_freq =
+ tx->tx_hpf_work[decimator].hpf_cut_off_freq;
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_PGA_MUTE_MASK, 0x1);
+ if (cancel_delayed_work_sync(
+ &tx->tx_hpf_work[decimator].dwork)) {
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_write_field(
+ component, dec_cfg_reg,
+ CDC_TXn_HPF_CUT_FREQ_MASK,
+ hpf_cut_off_freq);
+ if (is_amic_enabled(component, tx, decimator))
+ snd_soc_component_update_bits(component,
+ hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x02);
+ else
+ snd_soc_component_update_bits(component,
+ hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x03);
+
+ /*
+ * Minimum 1 clk cycle delay is required
+ * as per HW spec
+ */
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, hpf_gate_reg,
+ CDC_TXn_HPF_F_CHANGE_MASK |
+ CDC_TXn_HPF_ZERO_GATE_MASK,
+ 0x1);
+ }
+ }
+ cancel_delayed_work_sync(&tx->tx_mute_dwork[decimator].dwork);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_CLK_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_ADC_MODE_MASK, 0x0);
+ snd_soc_component_write_field(component, tx_vol_ctl_reg,
+ CDC_TXn_PGA_MUTE_MASK, 0x0);
+ if (tx->bcs_enable) {
+ snd_soc_component_write_field(component, dec_cfg_reg,
+ CDC_TXn_PH_EN_MASK, 0x0);
+ snd_soc_component_write_field(component,
+ CDC_TX0_TX_PATH_SEC7,
+ CDC_TX0_MBHC_CTL_EN_MASK,
+ 0x0);
+ tx->bcs_clk_en = false;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tx_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.integer.value[0] = tx->dec_mode[path];
+
+ return 0;
+}
+
+static int tx_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int value = ucontrol->value.integer.value[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ if (tx->dec_mode[path] == value)
+ return 0;
+
+ tx->dec_mode[path] = value;
+
+ return 1;
+}
+
+static int tx_macro_get_bcs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = tx->bcs_enable;
+
+ return 0;
+}
+
+static int tx_macro_set_bcs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int value = ucontrol->value.integer.value[0];
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ tx->bcs_enable = value;
+
+ return 0;
+}
+
+static int tx_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 sample_rate;
+ u8 decimator;
+ int tx_fs_rate;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ sample_rate = params_rate(params);
+ switch (sample_rate) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ for_each_set_bit(decimator, &tx->active_ch_mask[dai->id], TX_MACRO_DEC_MAX)
+ snd_soc_component_update_bits(component, CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PCM_RATE_MASK,
+ tx_fs_rate);
+ return 0;
+}
+
+static int tx_macro_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+
+ switch (dai->id) {
+ case TX_MACRO_AIF1_CAP:
+ case TX_MACRO_AIF2_CAP:
+ case TX_MACRO_AIF3_CAP:
+ *tx_slot = tx->active_ch_mask[dai->id];
+ *tx_num = tx->active_ch_cnt[dai->id];
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int tx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ u8 decimator;
+
+ /* active decimator not set yet */
+ if (tx->active_decimator[dai->id] == -1)
+ return 0;
+
+ decimator = tx->active_decimator[dai->id];
+
+ if (mute)
+ snd_soc_component_write_field(component,
+ CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PGA_MUTE_MASK, 0x1);
+ else
+ snd_soc_component_update_bits(component,
+ CDC_TXn_TX_PATH_CTL(decimator),
+ CDC_TXn_PGA_MUTE_MASK, 0x0);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tx_macro_dai_ops = {
+ .hw_params = tx_macro_hw_params,
+ .get_channel_map = tx_macro_get_channel_map,
+ .mute_stream = tx_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver tx_macro_dai[] = {
+ {
+ .name = "tx_macro_tx1",
+ .id = TX_MACRO_AIF1_CAP,
+ .capture = {
+ .stream_name = "TX_AIF1 Capture",
+ .rates = TX_MACRO_RATES,
+ .formats = TX_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tx_macro_dai_ops,
+ },
+ {
+ .name = "tx_macro_tx2",
+ .id = TX_MACRO_AIF2_CAP,
+ .capture = {
+ .stream_name = "TX_AIF2 Capture",
+ .rates = TX_MACRO_RATES,
+ .formats = TX_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tx_macro_dai_ops,
+ },
+ {
+ .name = "tx_macro_tx3",
+ .id = TX_MACRO_AIF3_CAP,
+ .capture = {
+ .stream_name = "TX_AIF3 Capture",
+ .rates = TX_MACRO_RATES,
+ .formats = TX_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tx_macro_dai_ops,
+ },
+};
+
+static const char * const adc_mux_text[] = {
+ "MSM_DMIC", "SWR_MIC", "ANC_FB_TUNE1"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_dec0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(tx_dec7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG1,
+ 0, adc_mux_text);
+
+static const struct snd_kcontrol_new tx_dec0_mux = SOC_DAPM_ENUM("tx_dec0", tx_dec0_enum);
+static const struct snd_kcontrol_new tx_dec1_mux = SOC_DAPM_ENUM("tx_dec1", tx_dec1_enum);
+static const struct snd_kcontrol_new tx_dec2_mux = SOC_DAPM_ENUM("tx_dec2", tx_dec2_enum);
+static const struct snd_kcontrol_new tx_dec3_mux = SOC_DAPM_ENUM("tx_dec3", tx_dec3_enum);
+static const struct snd_kcontrol_new tx_dec4_mux = SOC_DAPM_ENUM("tx_dec4", tx_dec4_enum);
+static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_dec5_enum);
+static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum);
+static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum);
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+ "DMIC4", "DMIC5", "DMIC6", "DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_dmic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 4, dmic_mux_text);
+
+static const struct snd_kcontrol_new tx_dmic0_mux = SOC_DAPM_ENUM_EXT("tx_dmic0", tx_dmic0_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic1_mux = SOC_DAPM_ENUM_EXT("tx_dmic1", tx_dmic1_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic2_mux = SOC_DAPM_ENUM_EXT("tx_dmic2", tx_dmic2_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic3_mux = SOC_DAPM_ENUM_EXT("tx_dmic3", tx_dmic3_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic4_mux = SOC_DAPM_ENUM_EXT("tx_dmic4", tx_dmic4_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic5_mux = SOC_DAPM_ENUM_EXT("tx_dmic5", tx_dmic5_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic6_mux = SOC_DAPM_ENUM_EXT("tx_dmic6", tx_dmic6_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_dmic7_mux = SOC_DAPM_ENUM_EXT("tx_dmic7", tx_dmic7_enum,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const char * const dec_mode_mux_text[] = {
+ "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF",
+};
+
+static const struct soc_enum dec_mode_mux_enum[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 4, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 5, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 6, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 7, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+};
+
+static const struct snd_kcontrol_new tx_aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new tx_aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new tx_aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0,
+ tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("TX_AIF1 CAP", "TX_AIF1 Capture", 0,
+ SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("TX_AIF2 CAP", "TX_AIF2 Capture", 0,
+ SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("TX_AIF3 CAP", "TX_AIF3 Capture", 0,
+ SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0),
+
+ SND_SOC_DAPM_MIXER("TX_AIF1_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0,
+ tx_aif1_cap_mixer, ARRAY_SIZE(tx_aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("TX_AIF2_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0,
+ tx_aif2_cap_mixer, ARRAY_SIZE(tx_aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0,
+ tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX3", SND_SOC_NOPM, 4, 0, &tx_dmic3_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX4", SND_SOC_NOPM, 4, 0, &tx_dmic4_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX5", SND_SOC_NOPM, 4, 0, &tx_dmic5_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux),
+ SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux),
+
+ SND_SOC_DAPM_INPUT("TX DMIC0"),
+ SND_SOC_DAPM_INPUT("TX DMIC1"),
+ SND_SOC_DAPM_INPUT("TX DMIC2"),
+ SND_SOC_DAPM_INPUT("TX DMIC3"),
+ SND_SOC_DAPM_INPUT("TX DMIC4"),
+ SND_SOC_DAPM_INPUT("TX DMIC5"),
+ SND_SOC_DAPM_INPUT("TX DMIC6"),
+ SND_SOC_DAPM_INPUT("TX DMIC7"),
+
+ SND_SOC_DAPM_MUX_E("TX DEC0 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC0, 0,
+ &tx_dec0_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC1 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC1, 0,
+ &tx_dec1_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC2 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC2, 0,
+ &tx_dec2_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC3 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC3, 0,
+ &tx_dec3_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC4 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC4, 0,
+ &tx_dec4_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC5 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC5, 0,
+ &tx_dec5_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC6 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC6, 0,
+ &tx_dec6_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("TX DEC7 MUX", SND_SOC_NOPM,
+ TX_MACRO_DEC7, 0,
+ &tx_dec7_mux, tx_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TX_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ tx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TX_SWR_CLK", 0, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("VA_SWR_CLK", 0, SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map[] = {
+ {"TX_AIF1 CAP", NULL, "TX_MCLK"},
+ {"TX_AIF2 CAP", NULL, "TX_MCLK"},
+ {"TX_AIF3 CAP", NULL, "TX_MCLK"},
+
+ {"TX_AIF1 CAP", NULL, "TX_AIF1_CAP Mixer"},
+ {"TX_AIF2 CAP", NULL, "TX_AIF2_CAP Mixer"},
+ {"TX_AIF3 CAP", NULL, "TX_AIF3_CAP Mixer"},
+
+ {"TX_AIF1_CAP Mixer", "DEC0", "TX DEC0 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC1", "TX DEC1 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC2", "TX DEC2 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC3", "TX DEC3 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC4", "TX DEC4 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC5", "TX DEC5 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC6", "TX DEC6 MUX"},
+ {"TX_AIF1_CAP Mixer", "DEC7", "TX DEC7 MUX"},
+
+ {"TX_AIF2_CAP Mixer", "DEC0", "TX DEC0 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC1", "TX DEC1 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC2", "TX DEC2 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC3", "TX DEC3 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC4", "TX DEC4 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC5", "TX DEC5 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC6", "TX DEC6 MUX"},
+ {"TX_AIF2_CAP Mixer", "DEC7", "TX DEC7 MUX"},
+
+ {"TX_AIF3_CAP Mixer", "DEC0", "TX DEC0 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC1", "TX DEC1 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC2", "TX DEC2 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC3", "TX DEC3 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC4", "TX DEC4 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC5", "TX DEC5 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC6", "TX DEC6 MUX"},
+ {"TX_AIF3_CAP Mixer", "DEC7", "TX DEC7 MUX"},
+
+ {"TX DEC0 MUX", NULL, "TX_MCLK"},
+ {"TX DEC1 MUX", NULL, "TX_MCLK"},
+ {"TX DEC2 MUX", NULL, "TX_MCLK"},
+ {"TX DEC3 MUX", NULL, "TX_MCLK"},
+ {"TX DEC4 MUX", NULL, "TX_MCLK"},
+ {"TX DEC5 MUX", NULL, "TX_MCLK"},
+ {"TX DEC6 MUX", NULL, "TX_MCLK"},
+ {"TX DEC7 MUX", NULL, "TX_MCLK"},
+
+ {"TX DEC0 MUX", "MSM_DMIC", "TX DMIC MUX0"},
+ {"TX DMIC MUX0", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX0", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX0", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX0", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX0", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX0", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX0", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX0", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"},
+ {"TX DMIC MUX1", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX1", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX1", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX1", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX1", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX1", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX1", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX1", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"},
+ {"TX DMIC MUX2", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX2", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX2", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX2", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX2", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX2", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX2", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX2", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"},
+ {"TX DMIC MUX3", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX3", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX3", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX3", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX3", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX3", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX3", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX3", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"},
+ {"TX DMIC MUX4", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX4", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX4", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX4", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX4", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX4", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX4", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX4", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"},
+ {"TX DMIC MUX5", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX5", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX5", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX5", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX5", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX5", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX5", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX5", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"},
+ {"TX DMIC MUX6", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX6", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX6", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX6", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX6", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX6", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX6", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX6", "DMIC7", "TX DMIC7"},
+
+ {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"},
+ {"TX DMIC MUX7", "DMIC0", "TX DMIC0"},
+ {"TX DMIC MUX7", "DMIC1", "TX DMIC1"},
+ {"TX DMIC MUX7", "DMIC2", "TX DMIC2"},
+ {"TX DMIC MUX7", "DMIC3", "TX DMIC3"},
+ {"TX DMIC MUX7", "DMIC4", "TX DMIC4"},
+ {"TX DMIC MUX7", "DMIC5", "TX DMIC5"},
+ {"TX DMIC MUX7", "DMIC6", "TX DMIC6"},
+ {"TX DMIC MUX7", "DMIC7", "TX DMIC7"},
+};
+
+/* Controls and routes specific to LPASS <= v9.0.0 */
+static const char * const smic_mux_text_v9[] = {
+ "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0",
+ "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4",
+ "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text_v9);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = {
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9),
+
+ SND_SOC_DAPM_INPUT("TX SWR_ADC0"),
+ SND_SOC_DAPM_INPUT("TX SWR_ADC1"),
+ SND_SOC_DAPM_INPUT("TX SWR_ADC2"),
+ SND_SOC_DAPM_INPUT("TX SWR_ADC3"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC0"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC1"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC2"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC3"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC4"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC5"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC6"),
+ SND_SOC_DAPM_INPUT("TX SWR_DMIC7"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9[] = {
+ {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
+ {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX0", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX0", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX0", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX0", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX0", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX0", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX0", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX0", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX0", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
+ {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX1", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX1", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX1", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX1", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX1", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX1", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX1", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX1", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX1", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
+ {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX2", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX2", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX2", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX2", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX2", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX2", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX2", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX2", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX2", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
+ {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX3", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX3", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX3", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX3", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX3", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX3", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX3", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX3", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX3", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
+ {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX4", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX4", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX4", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX4", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX4", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX4", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX4", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX4", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX4", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
+ {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX5", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX5", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX5", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX5", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX5", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX5", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX5", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX5", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX5", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
+ {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX6", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX6", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX6", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX6", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX6", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX6", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX6", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX6", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX6", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"},
+
+ {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
+ {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"},
+ {"TX SMIC MUX7", "ADC1", "TX SWR_ADC1"},
+ {"TX SMIC MUX7", "ADC2", "TX SWR_ADC2"},
+ {"TX SMIC MUX7", "ADC3", "TX SWR_ADC3"},
+ {"TX SMIC MUX7", "SWR_DMIC0", "TX SWR_DMIC0"},
+ {"TX SMIC MUX7", "SWR_DMIC1", "TX SWR_DMIC1"},
+ {"TX SMIC MUX7", "SWR_DMIC2", "TX SWR_DMIC2"},
+ {"TX SMIC MUX7", "SWR_DMIC3", "TX SWR_DMIC3"},
+ {"TX SMIC MUX7", "SWR_DMIC4", "TX SWR_DMIC4"},
+ {"TX SMIC MUX7", "SWR_DMIC5", "TX SWR_DMIC5"},
+ {"TX SMIC MUX7", "SWR_DMIC6", "TX SWR_DMIC6"},
+ {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"},
+};
+
+/* Controls and routes specific to LPASS >= v9.2.0 */
+static const char * const smic_mux_text_v9_2[] = {
+ "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3",
+ "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7",
+ "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = {
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2),
+
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT0"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT1"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT2"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT3"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT4"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT5"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT6"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT7"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT8"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT9"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT10"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT11"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = {
+ {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
+ {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"},
+ {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"},
+
+ {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
+ {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
+ {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
+ {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
+ {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
+ {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
+ {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
+ {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"},
+};
+
+static const struct snd_kcontrol_new tx_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("TX_DEC0 Volume",
+ CDC_TX0_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC1 Volume",
+ CDC_TX1_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC2 Volume",
+ CDC_TX2_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC3 Volume",
+ CDC_TX3_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC4 Volume",
+ CDC_TX4_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC5 Volume",
+ CDC_TX5_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC6 Volume",
+ CDC_TX6_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX_DEC7 Volume",
+ CDC_TX7_TX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_ENUM_EXT("DEC0 MODE", dec_mode_mux_enum[0],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC1 MODE", dec_mode_mux_enum[1],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC2 MODE", dec_mode_mux_enum[2],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC3 MODE", dec_mode_mux_enum[3],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC4 MODE", dec_mode_mux_enum[4],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC5 MODE", dec_mode_mux_enum[5],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC6 MODE", dec_mode_mux_enum[6],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_ENUM_EXT("DEC7 MODE", dec_mode_mux_enum[7],
+ tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+ SOC_SINGLE_EXT("DEC0_BCS Switch", SND_SOC_NOPM, 0, 1, 0,
+ tx_macro_get_bcs, tx_macro_set_bcs),
+};
+
+static int tx_macro_component_extend(struct snd_soc_component *comp)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(comp);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ if (tx->data->extra_widgets_num) {
+ ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets,
+ tx->data->extra_widgets_num);
+ if (ret) {
+ dev_err(tx->dev, "failed to add extra widgets: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (tx->data->extra_routes_num) {
+ ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes,
+ tx->data->extra_routes_num);
+ if (ret) {
+ dev_err(tx->dev, "failed to add extra routes: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tx_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int i, ret;
+
+ ret = tx_macro_component_extend(comp);
+ if (ret)
+ return ret;
+
+ snd_soc_component_init_regmap(comp, tx->regmap);
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ tx->tx_hpf_work[i].tx = tx;
+ tx->tx_hpf_work[i].decimator = i;
+ INIT_DELAYED_WORK(&tx->tx_hpf_work[i].dwork,
+ tx_macro_tx_hpf_corner_freq_callback);
+ }
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ tx->tx_mute_dwork[i].tx = tx;
+ tx->tx_mute_dwork[i].decimator = i;
+ INIT_DELAYED_WORK(&tx->tx_mute_dwork[i].dwork,
+ tx_macro_mute_update_callback);
+ }
+ tx->component = comp;
+
+ snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F,
+ 0x0A);
+ /* Enable swr mic0 and mic1 clock */
+ snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+
+ return 0;
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ struct tx_macro *tx = to_tx_macro(hw);
+ struct regmap *regmap = tx->regmap;
+ int ret;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret) {
+ dev_err(tx->dev, "failed to enable mclk\n");
+ return ret;
+ }
+
+ tx_macro_mclk_enable(tx, true);
+
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK,
+ CDC_TX_SWR_CLK_ENABLE);
+ return 0;
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ struct tx_macro *tx = to_tx_macro(hw);
+ struct regmap *regmap = tx->regmap;
+
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK, 0x0);
+
+ tx_macro_mclk_enable(tx, false);
+ clk_disable_unprepare(tx->mclk);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct tx_macro *tx = to_tx_macro(hw);
+ int ret, val;
+
+ regmap_read(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+
+};
+
+static int tx_macro_register_mclk_output(struct tx_macro *tx)
+{
+ struct device *dev = tx->dev;
+ const char *parent_clk_name = NULL;
+ const char *clk_name = "lpass-tx-mclk";
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (tx->npl)
+ parent_clk_name = __clk_get_name(tx->npl);
+ else
+ parent_clk_name = __clk_get_name(tx->mclk);
+
+ init.name = clk_name;
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ tx->hw.init = &init;
+ hw = &tx->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct snd_soc_component_driver tx_macro_component_drv = {
+ .name = "TX-MACRO",
+ .probe = tx_macro_component_probe,
+ .controls = tx_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(tx_macro_snd_controls),
+ .dapm_widgets = tx_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tx_macro_dapm_widgets),
+ .dapm_routes = tx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tx_audio_map),
+};
+
+static int tx_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct tx_macro *tx;
+ void __iomem *base;
+ int ret, reg;
+
+ tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
+ if (!tx)
+ return -ENOMEM;
+
+ tx->data = device_get_match_data(dev);
+
+ tx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(tx->macro))
+ return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n");
+
+ tx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(tx->dcodec))
+ return dev_err_probe(dev, PTR_ERR(tx->dcodec), "unable to get dcodec clock\n");
+
+ tx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(tx->mclk))
+ return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n");
+
+ if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ tx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(tx->npl))
+ return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n");
+ }
+
+ tx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(tx->fsgen))
+ return dev_err_probe(dev, PTR_ERR(tx->fsgen), "unable to get fsgen clock\n");
+
+ tx->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(tx->pds))
+ return PTR_ERR(tx->pds);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
+
+ /* Update defaults for lpass sc7280 */
+ if (of_device_is_compatible(np, "qcom,sc7280-lpass-tx-macro")) {
+ for (reg = 0; reg < ARRAY_SIZE(tx_defaults); reg++) {
+ switch (tx_defaults[reg].reg) {
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_CTL:
+ tx_defaults[reg].def = 0x0E;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config);
+ if (IS_ERR(tx->regmap)) {
+ ret = PTR_ERR(tx->regmap);
+ goto err;
+ }
+
+ dev_set_drvdata(dev, tx);
+
+ tx->dev = dev;
+
+ /* Set active_decimator default value */
+ tx->active_decimator[TX_MACRO_AIF1_CAP] = -1;
+ tx->active_decimator[TX_MACRO_AIF2_CAP] = -1;
+ tx->active_decimator[TX_MACRO_AIF3_CAP] = -1;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(tx->mclk, MCLK_FREQ);
+ clk_set_rate(tx->npl, MCLK_FREQ);
+
+ ret = clk_prepare_enable(tx->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(tx->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(tx->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(tx->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+
+ /* reset soundwire block */
+ if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE);
+
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK,
+ CDC_TX_SWR_CLK_ENABLE);
+
+ if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, 0x0);
+
+ ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv,
+ tx_macro_dai,
+ ARRAY_SIZE(tx_macro_dai));
+ if (ret)
+ goto err_clkout;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = tx_macro_register_mclk_output(tx);
+ if (ret)
+ goto err_clkout;
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(tx->fsgen);
+err_fsgen:
+ clk_disable_unprepare(tx->npl);
+err_npl:
+ clk_disable_unprepare(tx->mclk);
+err_mclk:
+ clk_disable_unprepare(tx->dcodec);
+err_dcodec:
+ clk_disable_unprepare(tx->macro);
+err:
+ lpass_macro_pds_exit(tx->pds);
+
+ return ret;
+}
+
+static void tx_macro_remove(struct platform_device *pdev)
+{
+ struct tx_macro *tx = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(tx->macro);
+ clk_disable_unprepare(tx->dcodec);
+ clk_disable_unprepare(tx->mclk);
+ clk_disable_unprepare(tx->npl);
+ clk_disable_unprepare(tx->fsgen);
+
+ lpass_macro_pds_exit(tx->pds);
+}
+
+static int tx_macro_runtime_suspend(struct device *dev)
+{
+ struct tx_macro *tx = dev_get_drvdata(dev);
+
+ regcache_cache_only(tx->regmap, true);
+ regcache_mark_dirty(tx->regmap);
+
+ clk_disable_unprepare(tx->fsgen);
+ clk_disable_unprepare(tx->npl);
+ clk_disable_unprepare(tx->mclk);
+
+ return 0;
+}
+
+static int tx_macro_runtime_resume(struct device *dev)
+{
+ struct tx_macro *tx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(tx->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare npl\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(tx->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+
+ regcache_cache_only(tx->regmap, false);
+ regcache_sync(tx->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(tx->npl);
+err_npl:
+ clk_disable_unprepare(tx->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops tx_macro_pm_ops = {
+ RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL)
+};
+
+static const struct tx_macro_data lpass_ver_9 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_9_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9),
+ .extra_routes = tx_audio_map_v9,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9),
+};
+
+static const struct tx_macro_data lpass_ver_9_2 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_9_2_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_10_sm6115 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_10_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_11 = {
+ .flags = LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_11_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct of_device_id tx_macro_dt_match[] = {
+ {
+ /*
+ * The block is actually LPASS v9.4, but keep LPASS v9 match
+ * data and audio widgets, due to compatibility reasons.
+ * Microphones are working on SC7280 fine, so apparently the fix
+ * is not necessary.
+ */
+ .compatible = "qcom,sc7280-lpass-tx-macro",
+ .data = &lpass_ver_9,
+ }, {
+ .compatible = "qcom,sm6115-lpass-tx-macro",
+ .data = &lpass_ver_10_sm6115,
+ }, {
+ .compatible = "qcom,sm8250-lpass-tx-macro",
+ .data = &lpass_ver_9,
+ }, {
+ .compatible = "qcom,sm8450-lpass-tx-macro",
+ .data = &lpass_ver_9_2,
+ }, {
+ .compatible = "qcom,sm8550-lpass-tx-macro",
+ .data = &lpass_ver_11,
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-tx-macro",
+ /*
+ * The block is actually LPASS v9.3, but keep LPASS v9 match
+ * data and audio widgets, due to compatibility reasons.
+ * Microphones are working on SC8280xp fine, so apparently the
+ * fix is not necessary.
+ */
+ .data = &lpass_ver_9,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tx_macro_dt_match);
+static struct platform_driver tx_macro_driver = {
+ .driver = {
+ .name = "tx_macro",
+ .of_match_table = tx_macro_dt_match,
+ .suppress_bind_attrs = true,
+ .pm = pm_ptr(&tx_macro_pm_ops),
+ },
+ .probe = tx_macro_probe,
+ .remove = tx_macro_remove,
+};
+
+module_platform_driver(tx_macro_driver);
+
+MODULE_DESCRIPTION("TX macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
new file mode 100644
index 000000000000..528d5b167ecf
--- /dev/null
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -0,0 +1,1781 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_clk.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "lpass-macro-common.h"
+
+/* VA macro registers */
+#define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_VA_MCLK_CONTROL_EN BIT(0)
+#define CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_VA_FS_CONTROL_EN BIT(0)
+#define CDC_VA_FS_COUNTER_CLR BIT(1)
+#define CDC_VA_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_VA_SWR_RESET_MASK BIT(1)
+#define CDC_VA_SWR_RESET_ENABLE BIT(1)
+#define CDC_VA_SWR_CLK_EN_MASK BIT(0)
+#define CDC_VA_SWR_CLK_ENABLE BIT(0)
+#define CDC_VA_TOP_CSR_TOP_CFG0 (0x0080)
+#define CDC_VA_FS_BROADCAST_EN BIT(1)
+#define CDC_VA_TOP_CSR_DMIC0_CTL (0x0084)
+#define CDC_VA_TOP_CSR_DMIC1_CTL (0x0088)
+#define CDC_VA_TOP_CSR_DMIC2_CTL (0x008C)
+#define CDC_VA_TOP_CSR_DMIC3_CTL (0x0090)
+#define CDC_VA_DMIC_EN_MASK BIT(0)
+#define CDC_VA_DMIC_ENABLE BIT(0)
+#define CDC_VA_DMIC_CLK_SEL_MASK GENMASK(3, 1)
+#define CDC_VA_DMIC_CLK_SEL_SHFT 1
+#define CDC_VA_DMIC_CLK_SEL_DIV0 0x0
+#define CDC_VA_DMIC_CLK_SEL_DIV1 0x2
+#define CDC_VA_DMIC_CLK_SEL_DIV2 0x4
+#define CDC_VA_DMIC_CLK_SEL_DIV3 0x6
+#define CDC_VA_DMIC_CLK_SEL_DIV4 0x8
+#define CDC_VA_DMIC_CLK_SEL_DIV5 0xa
+#define CDC_VA_TOP_CSR_DMIC_CFG (0x0094)
+#define CDC_VA_RESET_ALL_DMICS_MASK BIT(7)
+#define CDC_VA_RESET_ALL_DMICS_RESET BIT(7)
+#define CDC_VA_RESET_ALL_DMICS_DISABLE 0
+#define CDC_VA_DMIC3_FREQ_CHANGE_MASK BIT(3)
+#define CDC_VA_DMIC3_FREQ_CHANGE_EN BIT(3)
+#define CDC_VA_DMIC2_FREQ_CHANGE_MASK BIT(2)
+#define CDC_VA_DMIC2_FREQ_CHANGE_EN BIT(2)
+#define CDC_VA_DMIC1_FREQ_CHANGE_MASK BIT(1)
+#define CDC_VA_DMIC1_FREQ_CHANGE_EN BIT(1)
+#define CDC_VA_DMIC0_FREQ_CHANGE_MASK BIT(0)
+#define CDC_VA_DMIC0_FREQ_CHANGE_EN BIT(0)
+#define CDC_VA_DMIC_FREQ_CHANGE_DISABLE 0
+#define CDC_VA_TOP_CSR_DEBUG_BUS (0x009C)
+#define CDC_VA_TOP_CSR_DEBUG_EN (0x00A0)
+#define CDC_VA_TOP_CSR_TX_I2S_CTL (0x00A4)
+#define CDC_VA_TOP_CSR_I2S_CLK (0x00A8)
+#define CDC_VA_TOP_CSR_I2S_RESET (0x00AC)
+#define CDC_VA_TOP_CSR_CORE_ID_0 (0x00C0)
+ #define CORE_ID_0_REV_MAJ GENMASK(7, 0)
+#define CDC_VA_TOP_CSR_CORE_ID_1 (0x00C4)
+#define CORE_ID_1_HAS_WSAMACRO BIT(0)
+#define CORE_ID_1_HAS_RXMACRO BIT(1)
+#define CORE_ID_1_HAS_TXMACRO BIT(2)
+#define CORE_ID_1_HAS_VAMACRO BIT(3)
+#define CDC_VA_TOP_CSR_CORE_ID_2 (0x00C8)
+ #define CORE_ID_2_REV_MIN GENMASK(7, 4)
+ #define CORE_ID_2_REV_STEP GENMASK(3, 0)
+#define CDC_VA_TOP_CSR_CORE_ID_3 (0x00CC)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL0 (0x00D0)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL1 (0x00D4)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL2 (0x00D8)
+#define CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK (0xEE)
+#define CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1 (0xCC)
+#define CDC_VA_TOP_CSR_SWR_CTRL (0x00DC)
+#define CDC_VA_INP_MUX_ADC_MUX0_CFG0 (0x0100)
+#define CDC_VA_INP_MUX_ADC_MUX0_CFG1 (0x0104)
+#define CDC_VA_INP_MUX_ADC_MUX1_CFG0 (0x0108)
+#define CDC_VA_INP_MUX_ADC_MUX1_CFG1 (0x010C)
+#define CDC_VA_INP_MUX_ADC_MUX2_CFG0 (0x0110)
+#define CDC_VA_INP_MUX_ADC_MUX2_CFG1 (0x0114)
+#define CDC_VA_INP_MUX_ADC_MUX3_CFG0 (0x0118)
+#define CDC_VA_INP_MUX_ADC_MUX3_CFG1 (0x011C)
+#define CDC_VA_TX0_TX_PATH_CTL (0x0400)
+#define CDC_VA_TX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_VA_TX_PATH_CLK_EN BIT(5)
+#define CDC_VA_TX_PATH_CLK_DISABLE 0
+#define CDC_VA_TX_PATH_PGA_MUTE_EN_MASK BIT(4)
+#define CDC_VA_TX_PATH_PGA_MUTE_EN BIT(4)
+#define CDC_VA_TX_PATH_PGA_MUTE_DISABLE 0
+#define CDC_VA_TX0_TX_PATH_CFG0 (0x0404)
+#define CDC_VA_ADC_MODE_MASK GENMASK(2, 1)
+#define CDC_VA_ADC_MODE_SHIFT 1
+#define TX_HPF_CUT_OFF_FREQ_MASK GENMASK(6, 5)
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+#define CDC_VA_TX0_TX_PATH_CFG1 (0x0408)
+#define CDC_VA_TX0_TX_VOL_CTL (0x040C)
+#define CDC_VA_TX0_TX_PATH_SEC0 (0x0410)
+#define CDC_VA_TX0_TX_PATH_SEC1 (0x0414)
+#define CDC_VA_TX0_TX_PATH_SEC2 (0x0418)
+#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK BIT(1)
+#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ BIT(1)
+#define CDC_VA_TX_HPF_ZERO_GATE_MASK BIT(0)
+#define CDC_VA_TX_HPF_ZERO_NO_GATE BIT(0)
+#define CDC_VA_TX_HPF_ZERO_GATE 0
+#define CDC_VA_TX0_TX_PATH_SEC3 (0x041C)
+#define CDC_VA_TX0_TX_PATH_SEC4 (0x0420)
+#define CDC_VA_TX0_TX_PATH_SEC5 (0x0424)
+#define CDC_VA_TX0_TX_PATH_SEC6 (0x0428)
+#define CDC_VA_TX0_TX_PATH_SEC7 (0x042C)
+#define CDC_VA_TX1_TX_PATH_CTL (0x0480)
+#define CDC_VA_TX1_TX_PATH_CFG0 (0x0484)
+#define CDC_VA_TX1_TX_PATH_CFG1 (0x0488)
+#define CDC_VA_TX1_TX_VOL_CTL (0x048C)
+#define CDC_VA_TX1_TX_PATH_SEC0 (0x0490)
+#define CDC_VA_TX1_TX_PATH_SEC1 (0x0494)
+#define CDC_VA_TX1_TX_PATH_SEC2 (0x0498)
+#define CDC_VA_TX1_TX_PATH_SEC3 (0x049C)
+#define CDC_VA_TX1_TX_PATH_SEC4 (0x04A0)
+#define CDC_VA_TX1_TX_PATH_SEC5 (0x04A4)
+#define CDC_VA_TX1_TX_PATH_SEC6 (0x04A8)
+#define CDC_VA_TX2_TX_PATH_CTL (0x0500)
+#define CDC_VA_TX2_TX_PATH_CFG0 (0x0504)
+#define CDC_VA_TX2_TX_PATH_CFG1 (0x0508)
+#define CDC_VA_TX2_TX_VOL_CTL (0x050C)
+#define CDC_VA_TX2_TX_PATH_SEC0 (0x0510)
+#define CDC_VA_TX2_TX_PATH_SEC1 (0x0514)
+#define CDC_VA_TX2_TX_PATH_SEC2 (0x0518)
+#define CDC_VA_TX2_TX_PATH_SEC3 (0x051C)
+#define CDC_VA_TX2_TX_PATH_SEC4 (0x0520)
+#define CDC_VA_TX2_TX_PATH_SEC5 (0x0524)
+#define CDC_VA_TX2_TX_PATH_SEC6 (0x0528)
+#define CDC_VA_TX3_TX_PATH_CTL (0x0580)
+#define CDC_VA_TX3_TX_PATH_CFG0 (0x0584)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK BIT(7)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC BIT(7)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC 0
+#define CDC_VA_TX3_TX_PATH_CFG1 (0x0588)
+#define CDC_VA_TX3_TX_VOL_CTL (0x058C)
+#define CDC_VA_TX3_TX_PATH_SEC0 (0x0590)
+#define CDC_VA_TX3_TX_PATH_SEC1 (0x0594)
+#define CDC_VA_TX3_TX_PATH_SEC2 (0x0598)
+#define CDC_VA_TX3_TX_PATH_SEC3 (0x059C)
+#define CDC_VA_TX3_TX_PATH_SEC4 (0x05A0)
+#define CDC_VA_TX3_TX_PATH_SEC5 (0x05A4)
+#define CDC_VA_TX3_TX_PATH_SEC6 (0x05A8)
+
+#define VA_MAX_OFFSET (0x07A8)
+
+#define VA_MACRO_NUM_DECIMATORS 4
+#define VA_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define VA_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define VA_MACRO_MCLK_FREQ 9600000
+#define VA_MACRO_TX_PATH_OFFSET 0x80
+#define VA_MACRO_SWR_MIC_MUX_SEL_MASK 0xF
+#define VA_MACRO_ADC_MUX_CFG_OFFSET 0x8
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+enum {
+ VA_MACRO_AIF1_CAP,
+ VA_MACRO_AIF2_CAP,
+ VA_MACRO_AIF3_CAP,
+ VA_MACRO_MAX_DAIS,
+};
+
+enum {
+ VA_MACRO_DEC0,
+ VA_MACRO_DEC1,
+ VA_MACRO_DEC2,
+ VA_MACRO_DEC3,
+ VA_MACRO_DEC4,
+ VA_MACRO_DEC5,
+ VA_MACRO_DEC6,
+ VA_MACRO_DEC7,
+ VA_MACRO_DEC_MAX,
+};
+
+enum {
+ VA_MACRO_CLK_DIV_2,
+ VA_MACRO_CLK_DIV_3,
+ VA_MACRO_CLK_DIV_4,
+ VA_MACRO_CLK_DIV_6,
+ VA_MACRO_CLK_DIV_8,
+ VA_MACRO_CLK_DIV_16,
+};
+
+#define VA_NUM_CLKS_MAX 3
+
+struct va_macro {
+ struct device *dev;
+ unsigned long active_ch_mask[VA_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS];
+ u16 dmic_clk_div;
+ bool has_swr_master;
+ bool has_npl_clk;
+
+ int dec_mode[VA_MACRO_NUM_DECIMATORS];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+ struct lpass_macro *pds;
+
+ s32 dmic_0_1_clk_cnt;
+ s32 dmic_2_3_clk_cnt;
+ s32 dmic_4_5_clk_cnt;
+ s32 dmic_6_7_clk_cnt;
+ u8 dmic_0_1_clk_div;
+ u8 dmic_2_3_clk_div;
+ u8 dmic_4_5_clk_div;
+ u8 dmic_6_7_clk_div;
+};
+
+#define to_va_macro(_hw) container_of(_hw, struct va_macro, hw)
+
+struct va_macro_data {
+ bool has_swr_master;
+ bool has_npl_clk;
+ int version;
+};
+
+static const struct va_macro_data sm8250_va_data = {
+ .has_swr_master = false,
+ .has_npl_clk = false,
+ .version = LPASS_CODEC_VERSION_1_0,
+};
+
+static const struct va_macro_data sm8450_va_data = {
+ .has_swr_master = true,
+ .has_npl_clk = true,
+};
+
+static const struct va_macro_data sm8550_va_data = {
+ .has_swr_master = true,
+ .has_npl_clk = false,
+};
+
+static bool va_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_TOP_CSR_CORE_ID_0:
+ case CDC_VA_TOP_CSR_CORE_ID_1:
+ case CDC_VA_TOP_CSR_CORE_ID_2:
+ case CDC_VA_TOP_CSR_CORE_ID_3:
+ case CDC_VA_TOP_CSR_DMIC0_CTL:
+ case CDC_VA_TOP_CSR_DMIC1_CTL:
+ case CDC_VA_TOP_CSR_DMIC2_CTL:
+ case CDC_VA_TOP_CSR_DMIC3_CTL:
+ return true;
+ }
+ return false;
+}
+
+static const struct reg_default va_defaults[] = {
+ /* VA macro */
+ { CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, 0x00},
+ { CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00},
+ { CDC_VA_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_VA_TOP_CSR_TOP_CFG0, 0x00},
+ { CDC_VA_TOP_CSR_DMIC0_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC1_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC2_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC3_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC_CFG, 0x80},
+ { CDC_VA_TOP_CSR_DEBUG_BUS, 0x00},
+ { CDC_VA_TOP_CSR_DEBUG_EN, 0x00},
+ { CDC_VA_TOP_CSR_TX_I2S_CTL, 0x0C},
+ { CDC_VA_TOP_CSR_I2S_CLK, 0x00},
+ { CDC_VA_TOP_CSR_I2S_RESET, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_0, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_1, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_2, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_3, 0x00},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL0, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL1, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL2, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_CTRL, 0x06},
+
+ /* VA core */
+ { CDC_VA_INP_MUX_ADC_MUX0_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX0_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX1_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX1_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX2_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX2_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX3_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX3_CFG1, 0x00},
+ { CDC_VA_TX0_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX0_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX0_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX0_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX0_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX0_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX0_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC7, 0x25},
+ { CDC_VA_TX1_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX1_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX1_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX1_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX1_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX1_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX1_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX2_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX2_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX2_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX2_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX2_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX2_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX2_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX3_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX3_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX3_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX3_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX3_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX3_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX3_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC6, 0x00},
+};
+
+static bool va_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_VA_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_VA_TOP_CSR_TOP_CFG0:
+ case CDC_VA_TOP_CSR_DMIC0_CTL:
+ case CDC_VA_TOP_CSR_DMIC1_CTL:
+ case CDC_VA_TOP_CSR_DMIC2_CTL:
+ case CDC_VA_TOP_CSR_DMIC3_CTL:
+ case CDC_VA_TOP_CSR_DMIC_CFG:
+ case CDC_VA_TOP_CSR_SWR_MIC_CTL0:
+ case CDC_VA_TOP_CSR_SWR_MIC_CTL1:
+ case CDC_VA_TOP_CSR_SWR_MIC_CTL2:
+ case CDC_VA_TOP_CSR_DEBUG_BUS:
+ case CDC_VA_TOP_CSR_DEBUG_EN:
+ case CDC_VA_TOP_CSR_TX_I2S_CTL:
+ case CDC_VA_TOP_CSR_I2S_CLK:
+ case CDC_VA_TOP_CSR_I2S_RESET:
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG1:
+ case CDC_VA_TX0_TX_PATH_CTL:
+ case CDC_VA_TX0_TX_PATH_CFG0:
+ case CDC_VA_TX0_TX_PATH_CFG1:
+ case CDC_VA_TX0_TX_VOL_CTL:
+ case CDC_VA_TX0_TX_PATH_SEC0:
+ case CDC_VA_TX0_TX_PATH_SEC1:
+ case CDC_VA_TX0_TX_PATH_SEC2:
+ case CDC_VA_TX0_TX_PATH_SEC3:
+ case CDC_VA_TX0_TX_PATH_SEC4:
+ case CDC_VA_TX0_TX_PATH_SEC5:
+ case CDC_VA_TX0_TX_PATH_SEC6:
+ case CDC_VA_TX0_TX_PATH_SEC7:
+ case CDC_VA_TX1_TX_PATH_CTL:
+ case CDC_VA_TX1_TX_PATH_CFG0:
+ case CDC_VA_TX1_TX_PATH_CFG1:
+ case CDC_VA_TX1_TX_VOL_CTL:
+ case CDC_VA_TX1_TX_PATH_SEC0:
+ case CDC_VA_TX1_TX_PATH_SEC1:
+ case CDC_VA_TX1_TX_PATH_SEC2:
+ case CDC_VA_TX1_TX_PATH_SEC3:
+ case CDC_VA_TX1_TX_PATH_SEC4:
+ case CDC_VA_TX1_TX_PATH_SEC5:
+ case CDC_VA_TX1_TX_PATH_SEC6:
+ case CDC_VA_TX2_TX_PATH_CTL:
+ case CDC_VA_TX2_TX_PATH_CFG0:
+ case CDC_VA_TX2_TX_PATH_CFG1:
+ case CDC_VA_TX2_TX_VOL_CTL:
+ case CDC_VA_TX2_TX_PATH_SEC0:
+ case CDC_VA_TX2_TX_PATH_SEC1:
+ case CDC_VA_TX2_TX_PATH_SEC2:
+ case CDC_VA_TX2_TX_PATH_SEC3:
+ case CDC_VA_TX2_TX_PATH_SEC4:
+ case CDC_VA_TX2_TX_PATH_SEC5:
+ case CDC_VA_TX2_TX_PATH_SEC6:
+ case CDC_VA_TX3_TX_PATH_CTL:
+ case CDC_VA_TX3_TX_PATH_CFG0:
+ case CDC_VA_TX3_TX_PATH_CFG1:
+ case CDC_VA_TX3_TX_VOL_CTL:
+ case CDC_VA_TX3_TX_PATH_SEC0:
+ case CDC_VA_TX3_TX_PATH_SEC1:
+ case CDC_VA_TX3_TX_PATH_SEC2:
+ case CDC_VA_TX3_TX_PATH_SEC3:
+ case CDC_VA_TX3_TX_PATH_SEC4:
+ case CDC_VA_TX3_TX_PATH_SEC5:
+ case CDC_VA_TX3_TX_PATH_SEC6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool va_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_TOP_CSR_CORE_ID_0:
+ case CDC_VA_TOP_CSR_CORE_ID_1:
+ case CDC_VA_TOP_CSR_CORE_ID_2:
+ case CDC_VA_TOP_CSR_CORE_ID_3:
+ return true;
+ }
+
+ return va_is_rw_register(dev, reg);
+}
+
+static const struct regmap_config va_regmap_config = {
+ .name = "va_macro",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = va_defaults,
+ .num_reg_defaults = ARRAY_SIZE(va_defaults),
+ .max_register = VA_MAX_OFFSET,
+ .volatile_reg = va_is_volatile_register,
+ .readable_reg = va_is_readable_register,
+ .writeable_reg = va_is_rw_register,
+};
+
+static int va_clk_rsc_fs_gen_request(struct va_macro *va, bool enable)
+{
+ struct regmap *regmap = va->regmap;
+
+ if (enable) {
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_VA_MCLK_CONTROL_EN,
+ CDC_VA_MCLK_CONTROL_EN);
+ /* clear the fs counter */
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR,
+ CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR);
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR,
+ CDC_VA_FS_CONTROL_EN);
+
+ regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0,
+ CDC_VA_FS_BROADCAST_EN,
+ CDC_VA_FS_BROADCAST_EN);
+ } else {
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_VA_MCLK_CONTROL_EN, 0x0);
+
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN, 0x0);
+
+ regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0,
+ CDC_VA_FS_BROADCAST_EN, 0x0);
+ }
+
+ return 0;
+}
+
+static int va_macro_mclk_enable(struct va_macro *va, bool mclk_enable)
+{
+ struct regmap *regmap = va->regmap;
+
+ if (mclk_enable) {
+ va_clk_rsc_fs_gen_request(va, true);
+ regcache_mark_dirty(regmap);
+ regcache_sync_region(regmap, 0x0, VA_MAX_OFFSET);
+ } else {
+ va_clk_rsc_fs_gen_request(va, false);
+ }
+
+ return 0;
+}
+
+static int va_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(va->fsgen);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(va->fsgen);
+ }
+
+ return 0;
+}
+
+static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ u16 mic_sel_reg;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ switch (e->reg) {
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG0:
+ mic_sel_reg = CDC_VA_TX0_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG0:
+ mic_sel_reg = CDC_VA_TX1_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG0:
+ mic_sel_reg = CDC_VA_TX2_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG0:
+ mic_sel_reg = CDC_VA_TX3_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(component->dev, "%s: e->reg: 0x%x not expected\n",
+ __func__, e->reg);
+ return -EINVAL;
+ }
+
+ if (val != 0)
+ snd_soc_component_update_bits(component, mic_sel_reg,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC);
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int va_macro_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(dec_id, &va->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int va_macro_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ set_bit(dec_id, &va->active_ch_mask[dai_id]);
+ va->active_ch_cnt[dai_id]++;
+ } else {
+ clear_bit(dec_id, &va->active_ch_mask[dai_id]);
+ va->active_ch_cnt[dai_id]--;
+ }
+
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 0;
+}
+
+static int va_dmic_clk_enable(struct snd_soc_component *component,
+ u32 dmic, bool enable)
+{
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 *dmic_clk_div;
+ u8 freq_change_mask;
+ u8 clk_div;
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &(va->dmic_0_1_clk_cnt);
+ dmic_clk_div = &(va->dmic_0_1_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC0_CTL;
+ freq_change_mask = CDC_VA_DMIC0_FREQ_CHANGE_MASK;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &(va->dmic_2_3_clk_cnt);
+ dmic_clk_div = &(va->dmic_2_3_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC1_CTL;
+ freq_change_mask = CDC_VA_DMIC1_FREQ_CHANGE_MASK;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &(va->dmic_4_5_clk_cnt);
+ dmic_clk_div = &(va->dmic_4_5_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC2_CTL;
+ freq_change_mask = CDC_VA_DMIC2_FREQ_CHANGE_MASK;
+ break;
+ case 6:
+ case 7:
+ dmic_clk_cnt = &(va->dmic_6_7_clk_cnt);
+ dmic_clk_div = &(va->dmic_6_7_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC3_CTL;
+ freq_change_mask = CDC_VA_DMIC3_FREQ_CHANGE_MASK;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ clk_div = va->dmic_clk_div;
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ CDC_VA_RESET_ALL_DMICS_MASK,
+ CDC_VA_RESET_ALL_DMICS_DISABLE);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_EN_MASK,
+ CDC_VA_DMIC_ENABLE);
+ } else {
+ if (*dmic_clk_div > clk_div) {
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ freq_change_mask);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ CDC_VA_DMIC_FREQ_CHANGE_DISABLE);
+ } else {
+ clk_div = *dmic_clk_div;
+ }
+ }
+ *dmic_clk_div = clk_div;
+ } else {
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_EN_MASK, 0);
+ clk_div = 0;
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ } else {
+ clk_div = va->dmic_clk_div;
+ if (*dmic_clk_div > clk_div) {
+ clk_div = va->dmic_clk_div;
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ freq_change_mask);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ CDC_VA_DMIC_FREQ_CHANGE_DISABLE);
+ } else {
+ clk_div = *dmic_clk_div;
+ }
+ }
+ *dmic_clk_div = clk_div;
+ }
+
+ return 0;
+}
+
+static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int dmic = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ va_dmic_clk_enable(comp, dmic, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ va_dmic_clk_enable(comp, dmic, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator;
+ u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ u8 hpf_cut_off_freq;
+
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ decimator = w->shift;
+
+ tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ hpf_gate_reg = CDC_VA_TX0_TX_PATH_SEC2 +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ dec_cfg_reg = CDC_VA_TX0_TX_PATH_CFG0 +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ tx_gain_ctl_reg = CDC_VA_TX0_TX_VOL_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(comp,
+ dec_cfg_reg, CDC_VA_ADC_MODE_MASK,
+ va->dec_mode[decimator] << CDC_VA_ADC_MODE_SHIFT);
+ /* Enable TX PGA Mute */
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable TX CLK */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_CLK_EN_MASK,
+ CDC_VA_TX_PATH_CLK_EN);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_GATE);
+
+ usleep_range(1000, 1010);
+ hpf_cut_off_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ);
+
+ /*
+ * Minimum 1 clk cycle delay is required as per HW spec
+ */
+ usleep_range(1000, 1010);
+
+ snd_soc_component_update_bits(comp,
+ hpf_gate_reg,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK,
+ 0x0);
+ }
+
+
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_NO_GATE);
+ /*
+ * 6ms delay is required as per HW spec
+ */
+ usleep_range(6000, 6010);
+ /* apply gain after decimator is enabled */
+ snd_soc_component_write(comp, tx_gain_ctl_reg,
+ snd_soc_component_read(comp, tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable TX CLK */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_CLK_EN_MASK,
+ CDC_VA_TX_PATH_CLK_DISABLE);
+ break;
+ }
+ return 0;
+}
+
+static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.enumerated.item[0] = va->dec_mode[path];
+
+ return 0;
+}
+
+static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ int value = ucontrol->value.enumerated.item[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ va->dec_mode[path] = value;
+
+ return 0;
+}
+
+static int va_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int tx_fs_rate;
+ struct snd_soc_component *component = dai->component;
+ u32 decimator, sample_rate;
+ u16 tx_fs_reg;
+ struct device *va_dev = component->dev;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ sample_rate = params_rate(params);
+ switch (sample_rate) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(va_dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ for_each_set_bit(decimator, &va->active_ch_mask[dai->id],
+ VA_MACRO_DEC_MAX) {
+ tx_fs_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ snd_soc_component_update_bits(component, tx_fs_reg, 0x0F,
+ tx_fs_rate);
+ }
+ return 0;
+}
+
+static int va_macro_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct device *va_dev = component->dev;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ switch (dai->id) {
+ case VA_MACRO_AIF1_CAP:
+ case VA_MACRO_AIF2_CAP:
+ case VA_MACRO_AIF3_CAP:
+ *tx_slot = va->active_ch_mask[dai->id];
+ *tx_num = va->active_ch_cnt[dai->id];
+ break;
+ default:
+ dev_err(va_dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static int va_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+ u16 tx_vol_ctl_reg, decimator;
+
+ for_each_set_bit(decimator, &va->active_ch_mask[dai->id],
+ VA_MACRO_DEC_MAX) {
+ tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ if (mute)
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_PGA_MUTE_EN_MASK,
+ CDC_VA_TX_PATH_PGA_MUTE_EN);
+ else
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_PGA_MUTE_EN_MASK,
+ CDC_VA_TX_PATH_PGA_MUTE_DISABLE);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops va_macro_dai_ops = {
+ .hw_params = va_macro_hw_params,
+ .get_channel_map = va_macro_get_channel_map,
+ .mute_stream = va_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver va_macro_dais[] = {
+ {
+ .name = "va_macro_tx1",
+ .id = VA_MACRO_AIF1_CAP,
+ .capture = {
+ .stream_name = "VA_AIF1 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+ {
+ .name = "va_macro_tx2",
+ .id = VA_MACRO_AIF2_CAP,
+ .capture = {
+ .stream_name = "VA_AIF2 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+ {
+ .name = "va_macro_tx3",
+ .id = VA_MACRO_AIF3_CAP,
+ .capture = {
+ .stream_name = "VA_AIF3 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+};
+
+static const char * const adc_mux_text[] = {
+ "VA_DMIC", "SWR_MIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_dec0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG1,
+ 0, adc_mux_text);
+
+static const struct snd_kcontrol_new va_dec0_mux = SOC_DAPM_ENUM("va_dec0",
+ va_dec0_enum);
+static const struct snd_kcontrol_new va_dec1_mux = SOC_DAPM_ENUM("va_dec1",
+ va_dec1_enum);
+static const struct snd_kcontrol_new va_dec2_mux = SOC_DAPM_ENUM("va_dec2",
+ va_dec2_enum);
+static const struct snd_kcontrol_new va_dec3_mux = SOC_DAPM_ENUM("va_dec3",
+ va_dec3_enum);
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+ "DMIC4", "DMIC5", "DMIC6", "DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_dmic0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG0,
+ 4, dmic_mux_text);
+
+static const struct snd_kcontrol_new va_dmic0_mux = SOC_DAPM_ENUM_EXT("va_dmic0",
+ va_dmic0_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic1_mux = SOC_DAPM_ENUM_EXT("va_dmic1",
+ va_dmic1_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic2_mux = SOC_DAPM_ENUM_EXT("va_dmic2",
+ va_dmic2_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic3_mux = SOC_DAPM_ENUM_EXT("va_dmic3",
+ va_dmic3_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new va_aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new va_aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("VA_AIF1 CAP", "VA_AIF1 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VA_AIF2 CAP", "VA_AIF2 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VA_AIF3 CAP", "VA_AIF3 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF3_CAP, 0),
+
+ SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF1_CAP, 0,
+ va_aif1_cap_mixer, ARRAY_SIZE(va_aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("VA_AIF2_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF2_CAP, 0,
+ va_aif2_cap_mixer, ARRAY_SIZE(va_aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("VA_AIF3_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF3_CAP, 0,
+ va_aif3_cap_mixer, ARRAY_SIZE(va_aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("VA DMIC MUX0", SND_SOC_NOPM, 0, 0, &va_dmic0_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX1", SND_SOC_NOPM, 0, 0, &va_dmic1_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX2", SND_SOC_NOPM, 0, 0, &va_dmic2_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX3", SND_SOC_NOPM, 0, 0, &va_dmic3_mux),
+
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micb", 0, 0),
+ SND_SOC_DAPM_INPUT("DMIC0 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC3 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC4 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC5 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC6 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC7 Pin"),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC1", NULL, SND_SOC_NOPM, 1, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC2", NULL, SND_SOC_NOPM, 2, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC3", NULL, SND_SOC_NOPM, 3, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC4", NULL, SND_SOC_NOPM, 4, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC5", NULL, SND_SOC_NOPM, 5, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC6", NULL, SND_SOC_NOPM, 6, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC7", NULL, SND_SOC_NOPM, 7, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VA SWR_ADC0"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC1"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC2"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC3"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC0"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC1"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC2"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC3"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC4"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC5"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC6"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC7"),
+
+ SND_SOC_DAPM_MUX_E("VA DEC0 MUX", SND_SOC_NOPM, VA_MACRO_DEC0, 0,
+ &va_dec0_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC1 MUX", SND_SOC_NOPM, VA_MACRO_DEC1, 0,
+ &va_dec1_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC2 MUX", SND_SOC_NOPM, VA_MACRO_DEC2, 0,
+ &va_dec2_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC3 MUX", SND_SOC_NOPM, VA_MACRO_DEC3, 0,
+ &va_dec3_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0,
+ va_macro_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route va_audio_map[] = {
+ {"VA_AIF1 CAP", NULL, "VA_MCLK"},
+ {"VA_AIF2 CAP", NULL, "VA_MCLK"},
+ {"VA_AIF3 CAP", NULL, "VA_MCLK"},
+
+ {"VA_AIF1 CAP", NULL, "VA_AIF1_CAP Mixer"},
+ {"VA_AIF2 CAP", NULL, "VA_AIF2_CAP Mixer"},
+ {"VA_AIF3 CAP", NULL, "VA_AIF3_CAP Mixer"},
+
+ {"VA_AIF1_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA_AIF2_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA_AIF3_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA DEC0 MUX", "VA_DMIC", "VA DMIC MUX0"},
+ {"VA DMIC MUX0", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX0", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX0", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX0", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX0", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX0", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX0", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX0", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC1 MUX", "VA_DMIC", "VA DMIC MUX1"},
+ {"VA DMIC MUX1", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX1", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX1", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX1", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX1", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX1", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX1", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX1", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC2 MUX", "VA_DMIC", "VA DMIC MUX2"},
+ {"VA DMIC MUX2", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX2", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX2", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX2", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX2", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX2", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX2", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX2", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC3 MUX", "VA_DMIC", "VA DMIC MUX3"},
+ {"VA DMIC MUX3", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX3", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX3", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX3", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX3", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX3", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX3", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX3", "DMIC7", "VA DMIC7"},
+
+ { "VA DMIC0", NULL, "DMIC0 Pin" },
+ { "VA DMIC1", NULL, "DMIC1 Pin" },
+ { "VA DMIC2", NULL, "DMIC2 Pin" },
+ { "VA DMIC3", NULL, "DMIC3 Pin" },
+ { "VA DMIC4", NULL, "DMIC4 Pin" },
+ { "VA DMIC5", NULL, "DMIC5 Pin" },
+ { "VA DMIC6", NULL, "DMIC6 Pin" },
+ { "VA DMIC7", NULL, "DMIC7 Pin" },
+};
+
+static const char * const dec_mode_mux_text[] = {
+ "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF",
+};
+
+static const struct soc_enum dec_mode_mux_enum[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+};
+
+static const struct snd_kcontrol_new va_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("VA_DEC0 Volume", CDC_VA_TX0_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC1 Volume", CDC_VA_TX1_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC2 Volume", CDC_VA_TX2_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC3 Volume", CDC_VA_TX3_TX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_ENUM_EXT("VA_DEC0 MODE", dec_mode_mux_enum[0],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC1 MODE", dec_mode_mux_enum[1],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC2 MODE", dec_mode_mux_enum[2],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC3 MODE", dec_mode_mux_enum[3],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+};
+
+static int va_macro_component_probe(struct snd_soc_component *component)
+{
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, va->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver va_macro_component_drv = {
+ .name = "VA MACRO",
+ .probe = va_macro_component_probe,
+ .controls = va_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(va_macro_snd_controls),
+ .dapm_widgets = va_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(va_macro_dapm_widgets),
+ .dapm_routes = va_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(va_audio_map),
+};
+
+static int fsgen_gate_enable(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ struct regmap *regmap = va->regmap;
+ int ret;
+
+ if (va->has_swr_master) {
+ ret = clk_prepare_enable(va->mclk);
+ if (ret)
+ return ret;
+ }
+
+ ret = va_macro_mclk_enable(va, true);
+ if (va->has_swr_master)
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
+
+ return ret;
+}
+
+static void fsgen_gate_disable(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ struct regmap *regmap = va->regmap;
+
+ if (va->has_swr_master)
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, 0x0);
+
+ va_macro_mclk_enable(va, false);
+ if (va->has_swr_master)
+ clk_disable_unprepare(va->mclk);
+}
+
+static int fsgen_gate_is_enabled(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ int val;
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_TOP_CFG0, &val);
+
+ return !!(val & CDC_VA_FS_BROADCAST_EN);
+}
+
+static const struct clk_ops fsgen_gate_ops = {
+ .prepare = fsgen_gate_enable,
+ .unprepare = fsgen_gate_disable,
+ .is_enabled = fsgen_gate_is_enabled,
+};
+
+static int va_macro_register_fsgen_output(struct va_macro *va)
+{
+ struct clk *parent = va->mclk;
+ struct device *dev = va->dev;
+ struct device_node *np = dev->of_node;
+ const char *parent_clk_name;
+ const char *clk_name = "fsgen";
+ struct clk_init_data init;
+ int ret;
+
+ if (va->has_npl_clk)
+ parent = va->npl;
+
+ parent_clk_name = __clk_get_name(parent);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &fsgen_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ va->hw.init = &init;
+ ret = devm_clk_hw_register(va->dev, &va->hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &va->hw);
+}
+
+static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
+ struct va_macro *va)
+{
+ u32 div_factor;
+ u32 mclk_rate = VA_MACRO_MCLK_FREQ;
+
+ if (!dmic_sample_rate || mclk_rate % dmic_sample_rate != 0)
+ goto undefined_rate;
+
+ div_factor = mclk_rate / dmic_sample_rate;
+
+ switch (div_factor) {
+ case 2:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_2;
+ break;
+ case 3:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_3;
+ break;
+ case 4:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_4;
+ break;
+ case 6:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_6;
+ break;
+ case 8:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_8;
+ break;
+ case 16:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_16;
+ break;
+ default:
+ /* Any other DIV factor is invalid */
+ goto undefined_rate;
+ }
+
+ return dmic_sample_rate;
+
+undefined_rate:
+ dev_err(va->dev, "%s: Invalid rate %d, for mclk %d\n",
+ __func__, dmic_sample_rate, mclk_rate);
+ dmic_sample_rate = 0;
+
+ return dmic_sample_rate;
+}
+
+static int va_macro_set_lpass_codec_version(struct va_macro *va)
+{
+ int version = LPASS_CODEC_VERSION_UNKNOWN;
+ u32 maj, min, step;
+ u32 val;
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_0, &val);
+ maj = FIELD_GET(CORE_ID_0_REV_MAJ, val);
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_1, &val);
+ if (!FIELD_GET(CORE_ID_1_HAS_VAMACRO, val)) {
+ dev_err(va->dev, "This is not a VA macro instance\n");
+ return -ENODEV;
+ }
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_2, &val);
+ min = FIELD_GET(CORE_ID_2_REV_MIN, val);
+ step = FIELD_GET(CORE_ID_2_REV_STEP, val);
+
+ if (maj == 1) {
+ version = LPASS_CODEC_VERSION_2_0;
+ } else if (maj == 2) {
+ switch (min) {
+ case 0:
+ version = LPASS_CODEC_VERSION_2_0;
+ break;
+ case 5:
+ version = LPASS_CODEC_VERSION_2_5;
+ break;
+ case 6:
+ version = LPASS_CODEC_VERSION_2_6;
+ break;
+ case 7:
+ version = LPASS_CODEC_VERSION_2_7;
+ break;
+ case 8:
+ version = LPASS_CODEC_VERSION_2_8;
+ break;
+ case 9:
+ version = LPASS_CODEC_VERSION_2_9;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (version == LPASS_CODEC_VERSION_UNKNOWN) {
+ dev_err(va->dev, "VA Macro v%u.%u.%u is not supported\n",
+ maj, min, step);
+ return -EOPNOTSUPP;
+ }
+
+ lpass_macro_set_codec_version(version);
+
+ dev_dbg(va->dev, "LPASS Codec Version %s\n", lpass_macro_get_codec_version_string(version));
+
+ return 0;
+}
+
+static int va_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct va_macro_data *data;
+ struct va_macro *va;
+ void __iomem *base;
+ u32 sample_rate = 0;
+ int ret;
+
+ va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL);
+ if (!va)
+ return -ENOMEM;
+
+ va->dev = dev;
+
+ va->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(va->macro))
+ return dev_err_probe(dev, PTR_ERR(va->macro), "unable to get macro clock\n");
+
+ va->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(va->dcodec))
+ return dev_err_probe(dev, PTR_ERR(va->dcodec), "unable to get dcodec clock\n");
+
+ va->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(va->mclk))
+ return dev_err_probe(dev, PTR_ERR(va->mclk), "unable to get mclk clock\n");
+
+ va->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(va->pds))
+ return PTR_ERR(va->pds);
+
+ ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate",
+ &sample_rate);
+ if (ret) {
+ dev_err(dev, "qcom,dmic-sample-rate dt entry missing\n");
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_2;
+ } else {
+ ret = va_macro_validate_dmic_sample_rate(sample_rate, va);
+ if (!ret) {
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
+
+ va->regmap = devm_regmap_init_mmio(dev, base, &va_regmap_config);
+ if (IS_ERR(va->regmap)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev_set_drvdata(dev, va);
+
+ data = of_device_get_match_data(dev);
+ va->has_swr_master = data->has_swr_master;
+ va->has_npl_clk = data->has_npl_clk;
+
+ /* mclk rate */
+ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ);
+
+ if (va->has_npl_clk) {
+ va->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(va->npl)) {
+ ret = PTR_ERR(va->npl);
+ goto err;
+ }
+
+ clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ);
+ }
+
+ ret = clk_prepare_enable(va->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(va->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(va->mclk);
+ if (ret)
+ goto err_mclk;
+
+ if (va->has_npl_clk) {
+ ret = clk_prepare_enable(va->npl);
+ if (ret)
+ goto err_npl;
+ }
+
+ /**
+ * old version of codecs do not have a reliable way to determine the
+ * version from registers, get them from soc specific data
+ */
+ if (data->version) {
+ lpass_macro_set_codec_version(data->version);
+ } else {
+ /* read version from register */
+ ret = va_macro_set_lpass_codec_version(va);
+ if (ret)
+ goto err_clkout;
+ }
+
+ if (va->has_swr_master) {
+ /* Set default CLK div to 1 */
+ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1);
+ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL1,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1);
+ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL2,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK,
+ CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1);
+
+ }
+
+ if (va->has_swr_master) {
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE);
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_RESET_MASK, 0x0);
+ }
+
+ ret = devm_snd_soc_register_component(dev, &va_macro_component_drv,
+ va_macro_dais,
+ ARRAY_SIZE(va_macro_dais));
+ if (ret)
+ goto err_clkout;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = va_macro_register_fsgen_output(va);
+ if (ret)
+ goto err_clkout;
+
+ va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen");
+ if (IS_ERR(va->fsgen)) {
+ ret = PTR_ERR(va->fsgen);
+ goto err_clkout;
+ }
+
+ return 0;
+
+err_clkout:
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+err_npl:
+ clk_disable_unprepare(va->mclk);
+err_mclk:
+ clk_disable_unprepare(va->dcodec);
+err_dcodec:
+ clk_disable_unprepare(va->macro);
+err:
+ lpass_macro_pds_exit(va->pds);
+
+ return ret;
+}
+
+static void va_macro_remove(struct platform_device *pdev)
+{
+ struct va_macro *va = dev_get_drvdata(&pdev->dev);
+
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+
+ clk_disable_unprepare(va->mclk);
+ clk_disable_unprepare(va->dcodec);
+ clk_disable_unprepare(va->macro);
+
+ lpass_macro_pds_exit(va->pds);
+}
+
+static int va_macro_runtime_suspend(struct device *dev)
+{
+ struct va_macro *va = dev_get_drvdata(dev);
+
+ regcache_cache_only(va->regmap, true);
+ regcache_mark_dirty(va->regmap);
+
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+
+ clk_disable_unprepare(va->mclk);
+
+ return 0;
+}
+
+static int va_macro_runtime_resume(struct device *dev)
+{
+ struct va_macro *va = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(va->mclk);
+ if (ret) {
+ dev_err(va->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ if (va->has_npl_clk) {
+ ret = clk_prepare_enable(va->npl);
+ if (ret) {
+ clk_disable_unprepare(va->mclk);
+ dev_err(va->dev, "unable to prepare npl\n");
+ return ret;
+ }
+ }
+
+ regcache_cache_only(va->regmap, false);
+ regcache_sync(va->regmap);
+
+ return 0;
+}
+
+
+static const struct dev_pm_ops va_macro_pm_ops = {
+ RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL)
+};
+
+static const struct of_device_id va_macro_dt_match[] = {
+ { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data },
+ { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data },
+ { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data },
+ { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data },
+ { .compatible = "qcom,sm8550-lpass-va-macro", .data = &sm8550_va_data },
+ { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, va_macro_dt_match);
+
+static struct platform_driver va_macro_driver = {
+ .driver = {
+ .name = "va_macro",
+ .of_match_table = va_macro_dt_match,
+ .suppress_bind_attrs = true,
+ .pm = pm_ptr(&va_macro_pm_ops),
+ },
+ .probe = va_macro_probe,
+ .remove = va_macro_remove,
+};
+
+module_platform_driver(va_macro_driver);
+MODULE_DESCRIPTION("VA macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
new file mode 100644
index 000000000000..b695c77c18ac
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -0,0 +1,2939 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <sound/tlv.h>
+
+#include "lpass-macro-common.h"
+#include "lpass-wsa-macro.h"
+
+#define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_WSA_MCLK_EN_MASK BIT(0)
+#define CDC_WSA_MCLK_ENABLE BIT(0)
+#define CDC_WSA_MCLK_DISABLE 0
+#define CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_WSA_FS_CNT_EN_MASK BIT(0)
+#define CDC_WSA_FS_CNT_ENABLE BIT(0)
+#define CDC_WSA_FS_CNT_DISABLE 0
+#define CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_WSA_SWR_CLK_EN_MASK BIT(0)
+#define CDC_WSA_SWR_CLK_ENABLE BIT(0)
+#define CDC_WSA_SWR_RST_EN_MASK BIT(1)
+#define CDC_WSA_SWR_RST_ENABLE BIT(1)
+#define CDC_WSA_SWR_RST_DISABLE 0
+#define CDC_WSA_TOP_TOP_CFG0 (0x0080)
+#define CDC_WSA_TOP_TOP_CFG1 (0x0084)
+#define CDC_WSA_TOP_FREQ_MCLK (0x0088)
+#define CDC_WSA_TOP_DEBUG_BUS_SEL (0x008C)
+#define CDC_WSA_TOP_DEBUG_EN0 (0x0090)
+#define CDC_WSA_TOP_DEBUG_EN1 (0x0094)
+#define CDC_WSA_TOP_DEBUG_DSM_LB (0x0098)
+#define CDC_WSA_TOP_RX_I2S_CTL (0x009C)
+#define CDC_WSA_TOP_TX_I2S_CTL (0x00A0)
+#define CDC_WSA_TOP_I2S_CLK (0x00A4)
+#define CDC_WSA_TOP_I2S_RESET (0x00A8)
+#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100)
+#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104)
+#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108)
+#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C)
+#define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110)
+#define CDC_WSA_RX_MIX_TX1_SEL_MASK GENMASK(5, 3)
+#define CDC_WSA_RX_MIX_TX1_SEL_SHFT 3
+#define CDC_WSA_RX_MIX_TX0_SEL_MASK GENMASK(2, 0)
+#define CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (0x0114)
+#define CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (0x0118)
+#define CDC_WSA_TX0_SPKR_PROT_PATH_CTL (0x0244)
+#define CDC_WSA_TX_SPKR_PROT_RESET_MASK BIT(5)
+#define CDC_WSA_TX_SPKR_PROT_RESET BIT(5)
+#define CDC_WSA_TX_SPKR_PROT_NO_RESET 0
+#define CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK BIT(4)
+#define CDC_WSA_TX_SPKR_PROT_CLK_ENABLE BIT(4)
+#define CDC_WSA_TX_SPKR_PROT_CLK_DISABLE 0
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K 0
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_16K 1
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K 2
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K 3
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K 4
+#define CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (0x0248)
+#define CDC_WSA_TX1_SPKR_PROT_PATH_CTL (0x0264)
+#define CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (0x0268)
+#define CDC_WSA_TX2_SPKR_PROT_PATH_CTL (0x0284)
+#define CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (0x0288)
+#define CDC_WSA_TX3_SPKR_PROT_PATH_CTL (0x02A4)
+#define CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (0x02A8)
+#define CDC_WSA_INTR_CTRL_CFG (0x0340)
+#define CDC_WSA_INTR_CTRL_CLR_COMMIT (0x0344)
+#define CDC_WSA_INTR_CTRL_PIN1_MASK0 (0x0360)
+#define CDC_WSA_INTR_CTRL_PIN1_STATUS0 (0x0368)
+#define CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (0x0370)
+#define CDC_WSA_INTR_CTRL_PIN2_MASK0 (0x0380)
+#define CDC_WSA_INTR_CTRL_PIN2_STATUS0 (0x0388)
+#define CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (0x0390)
+#define CDC_WSA_INTR_CTRL_LEVEL0 (0x03C0)
+#define CDC_WSA_INTR_CTRL_BYPASS0 (0x03C8)
+#define CDC_WSA_INTR_CTRL_SET0 (0x03D0)
+#define CDC_WSA_RX0_RX_PATH_CTL (0x0400)
+#define CDC_WSA_RX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_WSA_RX_PATH_CLK_ENABLE BIT(5)
+#define CDC_WSA_RX_PATH_CLK_DISABLE 0
+#define CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK BIT(4)
+#define CDC_WSA_RX_PATH_PGA_MUTE_ENABLE BIT(4)
+#define CDC_WSA_RX_PATH_PGA_MUTE_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_CFG0 (0x0404)
+#define CDC_WSA_RX_PATH_COMP_EN_MASK BIT(1)
+#define CDC_WSA_RX_PATH_COMP_ENABLE BIT(1)
+#define CDC_WSA_RX_PATH_HD2_EN_MASK BIT(2)
+#define CDC_WSA_RX_PATH_HD2_ENABLE BIT(2)
+#define CDC_WSA_RX_PATH_SPKR_RATE_MASK BIT(3)
+#define CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072 BIT(3)
+#define CDC_WSA_RX0_RX_PATH_CFG1 (0x0408)
+#define CDC_WSA_RX_PATH_SMART_BST_EN_MASK BIT(0)
+#define CDC_WSA_RX_PATH_SMART_BST_ENABLE BIT(0)
+#define CDC_WSA_RX_PATH_SMART_BST_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_CFG2 (0x040C)
+#define CDC_WSA_RX0_RX_PATH_CFG3 (0x0410)
+#define CDC_WSA_RX_DC_DCOEFF_MASK GENMASK(1, 0)
+#define CDC_WSA_RX0_RX_VOL_CTL (0x0414)
+#define CDC_WSA_RX0_RX_PATH_MIX_CTL (0x0418)
+#define CDC_WSA_RX_PATH_MIX_CLK_EN_MASK BIT(5)
+#define CDC_WSA_RX_PATH_MIX_CLK_ENABLE BIT(5)
+#define CDC_WSA_RX_PATH_MIX_CLK_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_MIX_CFG (0x041C)
+#define CDC_WSA_RX0_RX_VOL_MIX_CTL (0x0420)
+#define CDC_WSA_RX0_RX_PATH_SEC0 (0x0424)
+#define CDC_WSA_RX0_RX_PATH_SEC1 (0x0428)
+#define CDC_WSA_RX_PGA_HALF_DB_MASK BIT(0)
+#define CDC_WSA_RX_PGA_HALF_DB_ENABLE BIT(0)
+#define CDC_WSA_RX_PGA_HALF_DB_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_SEC2 (0x042C)
+#define CDC_WSA_RX0_RX_PATH_SEC3 (0x0430)
+#define CDC_WSA_RX_PATH_HD2_SCALE_MASK GENMASK(1, 0)
+#define CDC_WSA_RX_PATH_HD2_ALPHA_MASK GENMASK(5, 2)
+#define CDC_WSA_RX0_RX_PATH_SEC5 (0x0438)
+#define CDC_WSA_RX0_RX_PATH_SEC6 (0x043C)
+#define CDC_WSA_RX0_RX_PATH_SEC7 (0x0440)
+#define CDC_WSA_RX0_RX_PATH_MIX_SEC0 (0x0444)
+#define CDC_WSA_RX0_RX_PATH_MIX_SEC1 (0x0448)
+#define CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (0x044C)
+#define CDC_WSA_RX_DSMDEM_CLK_EN_MASK BIT(0)
+#define CDC_WSA_RX_DSMDEM_CLK_ENABLE BIT(0)
+#define CDC_WSA_RX1_RX_PATH_CTL (0x0480)
+#define CDC_WSA_RX1_RX_PATH_CFG0 (0x0484)
+#define CDC_WSA_RX1_RX_PATH_CFG1 (0x0488)
+#define CDC_WSA_RX1_RX_PATH_CFG2 (0x048C)
+#define CDC_WSA_RX1_RX_PATH_CFG3 (0x0490)
+#define CDC_WSA_RX1_RX_VOL_CTL (0x0494)
+#define CDC_WSA_RX1_RX_PATH_MIX_CTL (0x0498)
+#define CDC_WSA_RX1_RX_PATH_MIX_CFG (0x049C)
+#define CDC_WSA_RX1_RX_VOL_MIX_CTL (0x04A0)
+#define CDC_WSA_RX1_RX_PATH_SEC0 (0x04A4)
+#define CDC_WSA_RX1_RX_PATH_SEC1 (0x04A8)
+#define CDC_WSA_RX1_RX_PATH_SEC2 (0x04AC)
+#define CDC_WSA_RX1_RX_PATH_SEC3 (0x04B0)
+#define CDC_WSA_RX1_RX_PATH_SEC5 (0x04B8)
+#define CDC_WSA_RX1_RX_PATH_SEC6 (0x04BC)
+#define CDC_WSA_RX1_RX_PATH_SEC7 (0x04C0)
+#define CDC_WSA_RX1_RX_PATH_MIX_SEC0 (0x04C4)
+#define CDC_WSA_RX1_RX_PATH_MIX_SEC1 (0x04C8)
+#define CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (0x04CC)
+#define CDC_WSA_BOOST0_BOOST_PATH_CTL (0x0500)
+#define CDC_WSA_BOOST_PATH_CLK_EN_MASK BIT(4)
+#define CDC_WSA_BOOST_PATH_CLK_ENABLE BIT(4)
+#define CDC_WSA_BOOST_PATH_CLK_DISABLE 0
+#define CDC_WSA_BOOST0_BOOST_CTL (0x0504)
+#define CDC_WSA_BOOST0_BOOST_CFG1 (0x0508)
+#define CDC_WSA_BOOST0_BOOST_CFG2 (0x050C)
+#define CDC_WSA_BOOST1_BOOST_PATH_CTL (0x0540)
+#define CDC_WSA_BOOST1_BOOST_CTL (0x0544)
+#define CDC_WSA_BOOST1_BOOST_CFG1 (0x0548)
+#define CDC_WSA_BOOST1_BOOST_CFG2 (0x054C)
+#define CDC_WSA_COMPANDER0_CTL0 (0x0580)
+#define CDC_WSA_COMPANDER_CLK_EN_MASK BIT(0)
+#define CDC_WSA_COMPANDER_CLK_ENABLE BIT(0)
+#define CDC_WSA_COMPANDER_SOFT_RST_MASK BIT(1)
+#define CDC_WSA_COMPANDER_SOFT_RST_ENABLE BIT(1)
+#define CDC_WSA_COMPANDER_HALT_MASK BIT(2)
+#define CDC_WSA_COMPANDER_HALT BIT(2)
+#define CDC_WSA_COMPANDER0_CTL1 (0x0584)
+#define CDC_WSA_COMPANDER0_CTL2 (0x0588)
+#define CDC_WSA_COMPANDER0_CTL3 (0x058C)
+#define CDC_WSA_COMPANDER0_CTL4 (0x0590)
+#define CDC_WSA_COMPANDER0_CTL5 (0x0594)
+#define CDC_WSA_COMPANDER0_CTL6 (0x0598)
+#define CDC_WSA_COMPANDER0_CTL7 (0x059C)
+/* CDC_WSA_COMPANDER1_CTLx and CDC_WSA_SOFTCLIPx differ per LPASS codec versions */
+#define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680)
+#define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0)
+#define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0)
+#define CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (0x0684)
+#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK GENMASK(4, 1)
+#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K BIT(3)
+#define CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL (0x06C0)
+#define CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (0x06C4)
+#define CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (0x0700)
+#define CDC_WSA_SPLINE_ASRC0_CTL0 (0x0704)
+#define CDC_WSA_SPLINE_ASRC0_CTL1 (0x0708)
+#define CDC_WSA_SPLINE_ASRC0_FIFO_CTL (0x070C)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB (0x0710)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB (0x0714)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB (0x0718)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB (0x071C)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (0x0720)
+#define CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (0x0740)
+#define CDC_WSA_SPLINE_ASRC1_CTL0 (0x0744)
+#define CDC_WSA_SPLINE_ASRC1_CTL1 (0x0748)
+#define CDC_WSA_SPLINE_ASRC1_FIFO_CTL (0x074C)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB (0x0750)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB (0x0754)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB (0x0758)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB (0x075C)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760)
+#define WSA_MAX_OFFSET (0x0760)
+
+/* LPASS codec version <=2.4 register offsets */
+#define CDC_WSA_COMPANDER1_CTL0 (0x05C0)
+#define CDC_WSA_COMPANDER1_CTL1 (0x05C4)
+#define CDC_WSA_COMPANDER1_CTL2 (0x05C8)
+#define CDC_WSA_COMPANDER1_CTL3 (0x05CC)
+#define CDC_WSA_COMPANDER1_CTL4 (0x05D0)
+#define CDC_WSA_COMPANDER1_CTL5 (0x05D4)
+#define CDC_WSA_COMPANDER1_CTL6 (0x05D8)
+#define CDC_WSA_COMPANDER1_CTL7 (0x05DC)
+#define CDC_WSA_SOFTCLIP0_CRC (0x0600)
+#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0)
+#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0)
+#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604)
+#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0)
+#define CDC_WSA_SOFTCLIP_ENABLE BIT(0)
+#define CDC_WSA_SOFTCLIP1_CRC (0x0640)
+#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644)
+
+/* LPASS codec version >=2.5 register offsets */
+#define CDC_WSA_TOP_FS_UNGATE (0x00AC)
+#define CDC_WSA_TOP_GRP_SEL (0x00B0)
+#define CDC_WSA_TOP_FS_UNGATE2 (0x00DC)
+#define CDC_2_5_WSA_COMPANDER0_CTL8 (0x05A0)
+#define CDC_2_5_WSA_COMPANDER0_CTL9 (0x05A4)
+#define CDC_2_5_WSA_COMPANDER0_CTL10 (0x05A8)
+#define CDC_2_5_WSA_COMPANDER0_CTL11 (0x05AC)
+#define CDC_2_5_WSA_COMPANDER0_CTL12 (0x05B0)
+#define CDC_2_5_WSA_COMPANDER0_CTL13 (0x05B4)
+#define CDC_2_5_WSA_COMPANDER0_CTL14 (0x05B8)
+#define CDC_2_5_WSA_COMPANDER0_CTL15 (0x05BC)
+#define CDC_2_5_WSA_COMPANDER0_CTL16 (0x05C0)
+#define CDC_2_5_WSA_COMPANDER0_CTL17 (0x05C4)
+#define CDC_2_5_WSA_COMPANDER0_CTL18 (0x05C8)
+#define CDC_2_5_WSA_COMPANDER0_CTL19 (0x05CC)
+#define CDC_2_5_WSA_COMPANDER1_CTL0 (0x05E0)
+#define CDC_2_5_WSA_COMPANDER1_CTL1 (0x05E4)
+#define CDC_2_5_WSA_COMPANDER1_CTL2 (0x05E8)
+#define CDC_2_5_WSA_COMPANDER1_CTL3 (0x05EC)
+#define CDC_2_5_WSA_COMPANDER1_CTL4 (0x05F0)
+#define CDC_2_5_WSA_COMPANDER1_CTL5 (0x05F4)
+#define CDC_2_5_WSA_COMPANDER1_CTL6 (0x05F8)
+#define CDC_2_5_WSA_COMPANDER1_CTL7 (0x05FC)
+#define CDC_2_5_WSA_COMPANDER1_CTL8 (0x0600)
+#define CDC_2_5_WSA_COMPANDER1_CTL9 (0x0604)
+#define CDC_2_5_WSA_COMPANDER1_CTL10 (0x0608)
+#define CDC_2_5_WSA_COMPANDER1_CTL11 (0x060C)
+#define CDC_2_5_WSA_COMPANDER1_CTL12 (0x0610)
+#define CDC_2_5_WSA_COMPANDER1_CTL13 (0x0614)
+#define CDC_2_5_WSA_COMPANDER1_CTL14 (0x0618)
+#define CDC_2_5_WSA_COMPANDER1_CTL15 (0x061C)
+#define CDC_2_5_WSA_COMPANDER1_CTL16 (0x0620)
+#define CDC_2_5_WSA_COMPANDER1_CTL17 (0x0624)
+#define CDC_2_5_WSA_COMPANDER1_CTL18 (0x0628)
+#define CDC_2_5_WSA_COMPANDER1_CTL19 (0x062C)
+#define CDC_2_5_WSA_SOFTCLIP0_CRC (0x0640)
+#define CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0644)
+#define CDC_2_5_WSA_SOFTCLIP1_CRC (0x0660)
+#define CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0664)
+
+#define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_48000)
+#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define NUM_INTERPOLATORS 2
+#define WSA_NUM_CLKS_MAX 5
+#define WSA_MACRO_MCLK_FREQ 19200000
+#define WSA_MACRO_MUX_CFG_OFFSET 0x8
+#define WSA_MACRO_MUX_CFG1_OFFSET 0x4
+#define WSA_MACRO_RX_PATH_OFFSET 0x80
+#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10
+#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C
+#define WSA_MACRO_FS_RATE_MASK 0x0F
+#define WSA_MACRO_EC_MIX_TX0_MASK 0x03
+#define WSA_MACRO_EC_MIX_TX1_MASK 0x18
+#define WSA_MACRO_MAX_DMA_CH_PER_PORT 0x2
+
+enum {
+ WSA_MACRO_GAIN_OFFSET_M1P5_DB,
+ WSA_MACRO_GAIN_OFFSET_0_DB,
+};
+enum {
+ WSA_MACRO_RX0 = 0,
+ WSA_MACRO_RX1,
+ WSA_MACRO_RX_MIX0,
+ WSA_MACRO_RX_MIX1,
+ WSA_MACRO_RX_MAX,
+};
+
+enum {
+ WSA_MACRO_TX0 = 0,
+ WSA_MACRO_TX1,
+ WSA_MACRO_TX_MAX,
+};
+
+enum {
+ WSA_MACRO_EC0_MUX = 0,
+ WSA_MACRO_EC1_MUX,
+ WSA_MACRO_EC_MUX_MAX,
+};
+
+enum {
+ WSA_MACRO_COMP1, /* SPK_L */
+ WSA_MACRO_COMP2, /* SPK_R */
+ WSA_MACRO_COMP_MAX
+};
+
+enum {
+ WSA_MACRO_SOFTCLIP0, /* RX0 */
+ WSA_MACRO_SOFTCLIP1, /* RX1 */
+ WSA_MACRO_SOFTCLIP_MAX
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+};
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate int_prim_sample_rate_val[] = {
+ {8000, 0x0}, /* 8K */
+ {16000, 0x1}, /* 16K */
+ {24000, -EINVAL},/* 24K */
+ {32000, 0x3}, /* 32K */
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+ {384000, 0x7}, /* 384K */
+ {44100, 0x8}, /* 44.1K */
+};
+
+static struct interp_sample_rate int_mix_sample_rate_val[] = {
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+};
+
+/* Matches also rx_mux_text */
+enum {
+ WSA_MACRO_AIF1_PB,
+ WSA_MACRO_AIF_MIX1_PB,
+ WSA_MACRO_AIF_VI,
+ WSA_MACRO_AIF_ECHO,
+ WSA_MACRO_MAX_DAIS,
+};
+
+/**
+ * struct wsa_reg_layout - Register layout differences
+ * @rx_intx_1_mix_inp0_sel_mask: register mask for RX_INTX_1_MIX_INP0_SEL_MASK
+ * @rx_intx_1_mix_inp1_sel_mask: register mask for RX_INTX_1_MIX_INP1_SEL_MASK
+ * @rx_intx_1_mix_inp2_sel_mask: register mask for RX_INTX_1_MIX_INP2_SEL_MASK
+ * @rx_intx_2_sel_mask: register mask for RX_INTX_2_SEL_MASK
+ * @compander1_reg_offset: offset between compander registers (compander1 - compander0)
+ * @softclip0_reg_base: base address of softclip0 register
+ * @softclip1_reg_offset: offset between compander registers (softclip1 - softclip0)
+ */
+struct wsa_reg_layout {
+ unsigned int rx_intx_1_mix_inp0_sel_mask;
+ unsigned int rx_intx_1_mix_inp1_sel_mask;
+ unsigned int rx_intx_1_mix_inp2_sel_mask;
+ unsigned int rx_intx_2_sel_mask;
+ unsigned int compander1_reg_offset;
+ unsigned int softclip0_reg_base;
+ unsigned int softclip1_reg_offset;
+};
+
+struct wsa_macro {
+ struct device *dev;
+ int comp_enabled[WSA_MACRO_COMP_MAX];
+ int ec_hq[WSA_MACRO_RX1 + 1];
+ u16 prim_int_users[WSA_MACRO_RX1 + 1];
+ u16 wsa_mclk_users;
+ enum lpass_codec_version codec_version;
+ const struct wsa_reg_layout *reg_layout;
+ unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS];
+ int rx_port_value[WSA_MACRO_RX_MAX];
+ int ear_spkr_gain;
+ int spkr_gain_offset;
+ int spkr_mode;
+ u32 pcm_rate_vi;
+ int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX];
+ int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
+ struct clk_hw hw;
+};
+#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw)
+
+static const struct wsa_reg_layout wsa_codec_v2_1 = {
+ .rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0),
+ .rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3),
+ .rx_intx_1_mix_inp2_sel_mask = GENMASK(5, 3),
+ .rx_intx_2_sel_mask = GENMASK(2, 0),
+ .compander1_reg_offset = 0x40,
+ .softclip0_reg_base = 0x600,
+ .softclip1_reg_offset = 0x40,
+};
+
+static const struct wsa_reg_layout wsa_codec_v2_5 = {
+ .rx_intx_1_mix_inp0_sel_mask = GENMASK(3, 0),
+ .rx_intx_1_mix_inp1_sel_mask = GENMASK(7, 4),
+ .rx_intx_1_mix_inp2_sel_mask = GENMASK(7, 4),
+ .rx_intx_2_sel_mask = GENMASK(3, 0),
+ .compander1_reg_offset = 0x60,
+ .softclip0_reg_base = 0x640,
+ .softclip1_reg_offset = 0x20,
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char *const rx_text_v2_1[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1"
+};
+
+static const char *const rx_text_v2_5[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8", "DEC0", "DEC1"
+};
+
+static const char *const rx_mix_text_v2_1[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1"
+};
+
+static const char *const rx_mix_text_v2_5[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8"
+};
+
+static const char *const rx_mix_ec_text[] = {
+ "ZERO", "RX_MIX_TX0", "RX_MIX_TX1"
+};
+
+/* Order must match WSA_MACRO_MAX_DAIS enum (offset by 1) */
+static const char *const rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF_MIX1_PB"
+};
+
+static const char *const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0"
+};
+
+static const char * const wsa_macro_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB",
+ "G_4_DB", "G_5_DB", "G_6_DB"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum,
+ wsa_macro_ear_spkr_pa_gain_text);
+
+/* RX INT0 */
+static const struct soc_enum rx0_prim_inp0_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_mix_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 5, rx_mix_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp0_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_mix_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 10, rx_mix_text_v2_5);
+
+static const struct soc_enum rx0_sidetone_mix_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text);
+
+static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_mix_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_mix_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_sidetone_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum);
+
+/* RX INT1 */
+static const struct soc_enum rx1_prim_inp0_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_mix_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 5, rx_mix_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp0_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_mix_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 10, rx_mix_text_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_mix_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_mix_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_5);
+
+static const struct soc_enum rx_mix_ec0_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0,
+ 0, 3, rx_mix_ec_text);
+
+static const struct soc_enum rx_mix_ec1_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0,
+ 3, 3, rx_mix_ec_text);
+
+static const struct snd_kcontrol_new rx_mix_ec0_mux =
+ SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum);
+
+static const struct snd_kcontrol_new rx_mix_ec1_mux =
+ SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum);
+
+static const struct reg_default wsa_defaults[] = {
+ /* WSA Macro */
+ { CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00},
+ { CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00},
+ { CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_WSA_TOP_TOP_CFG0, 0x00},
+ { CDC_WSA_TOP_TOP_CFG1, 0x00},
+ { CDC_WSA_TOP_FREQ_MCLK, 0x00},
+ { CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00},
+ { CDC_WSA_TOP_DEBUG_EN0, 0x00},
+ { CDC_WSA_TOP_DEBUG_EN1, 0x00},
+ { CDC_WSA_TOP_DEBUG_DSM_LB, 0x88},
+ { CDC_WSA_TOP_RX_I2S_CTL, 0x0C},
+ { CDC_WSA_TOP_TX_I2S_CTL, 0x0C},
+ { CDC_WSA_TOP_I2S_CLK, 0x02},
+ { CDC_WSA_TOP_I2S_RESET, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00},
+ { CDC_WSA_INTR_CTRL_CFG, 0x00},
+ { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF},
+ { CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF},
+ { CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00},
+ { CDC_WSA_INTR_CTRL_LEVEL0, 0x00},
+ { CDC_WSA_INTR_CTRL_BYPASS0, 0x00},
+ { CDC_WSA_INTR_CTRL_SET0, 0x00},
+ { CDC_WSA_RX0_RX_PATH_CTL, 0x04},
+ { CDC_WSA_RX0_RX_PATH_CFG0, 0x00},
+ { CDC_WSA_RX0_RX_PATH_CFG1, 0x64},
+ { CDC_WSA_RX0_RX_PATH_CFG2, 0x8F},
+ { CDC_WSA_RX0_RX_PATH_CFG3, 0x00},
+ { CDC_WSA_RX0_RX_VOL_CTL, 0x00},
+ { CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04},
+ { CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E},
+ { CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC0, 0x04},
+ { CDC_WSA_RX0_RX_PATH_SEC1, 0x08},
+ { CDC_WSA_RX0_RX_PATH_SEC2, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC3, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC5, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC6, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC7, 0x00},
+ { CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08},
+ { CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00},
+ { CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_CFG0, 0x00},
+ { CDC_WSA_RX1_RX_PATH_CFG1, 0x64},
+ { CDC_WSA_RX1_RX_PATH_CFG2, 0x8F},
+ { CDC_WSA_RX1_RX_PATH_CFG3, 0x00},
+ { CDC_WSA_RX1_RX_VOL_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04},
+ { CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E},
+ { CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC0, 0x04},
+ { CDC_WSA_RX1_RX_PATH_SEC1, 0x08},
+ { CDC_WSA_RX1_RX_PATH_SEC2, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC3, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC5, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC6, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC7, 0x00},
+ { CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08},
+ { CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00},
+ { CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00},
+ { CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00},
+ { CDC_WSA_BOOST0_BOOST_CTL, 0xD0},
+ { CDC_WSA_BOOST0_BOOST_CFG1, 0x89},
+ { CDC_WSA_BOOST0_BOOST_CFG2, 0x04},
+ { CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00},
+ { CDC_WSA_BOOST1_BOOST_CTL, 0xD0},
+ { CDC_WSA_BOOST1_BOOST_CFG1, 0x89},
+ { CDC_WSA_BOOST1_BOOST_CFG2, 0x04},
+ { CDC_WSA_COMPANDER0_CTL0, 0x60},
+ { CDC_WSA_COMPANDER0_CTL1, 0xDB},
+ { CDC_WSA_COMPANDER0_CTL2, 0xFF},
+ { CDC_WSA_COMPANDER0_CTL3, 0x35},
+ { CDC_WSA_COMPANDER0_CTL4, 0xFF},
+ { CDC_WSA_COMPANDER0_CTL5, 0x00},
+ { CDC_WSA_COMPANDER0_CTL6, 0x01},
+ { CDC_WSA_COMPANDER0_CTL7, 0x28},
+ { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00},
+ { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01},
+ { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00},
+ { CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01},
+ { CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_CTL0, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_CTL1, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CTL0, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CTL1, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00},
+};
+
+static const struct reg_default wsa_defaults_v2_1[] = {
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_COMPANDER1_CTL0, 0x60},
+ { CDC_WSA_COMPANDER1_CTL1, 0xDB},
+ { CDC_WSA_COMPANDER1_CTL2, 0xFF},
+ { CDC_WSA_COMPANDER1_CTL3, 0x35},
+ { CDC_WSA_COMPANDER1_CTL4, 0xFF},
+ { CDC_WSA_COMPANDER1_CTL5, 0x00},
+ { CDC_WSA_COMPANDER1_CTL6, 0x01},
+ { CDC_WSA_COMPANDER1_CTL7, 0x28},
+ { CDC_WSA_SOFTCLIP0_CRC, 0x00},
+ { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38},
+ { CDC_WSA_SOFTCLIP1_CRC, 0x00},
+ { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38},
+};
+
+static const struct reg_default wsa_defaults_v2_5[] = {
+ { CDC_WSA_TOP_FS_UNGATE, 0xFF},
+ { CDC_WSA_TOP_GRP_SEL, 0x08},
+ { CDC_WSA_TOP_FS_UNGATE2, 0x1F},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_2_5_WSA_COMPANDER0_CTL8, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL9, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL10, 0x06},
+ { CDC_2_5_WSA_COMPANDER0_CTL11, 0x12},
+ { CDC_2_5_WSA_COMPANDER0_CTL12, 0x1E},
+ { CDC_2_5_WSA_COMPANDER0_CTL13, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL14, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL15, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL16, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL17, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL18, 0x2A},
+ { CDC_2_5_WSA_COMPANDER0_CTL19, 0x16},
+ { CDC_2_5_WSA_COMPANDER1_CTL0, 0x60},
+ { CDC_2_5_WSA_COMPANDER1_CTL1, 0xDB},
+ { CDC_2_5_WSA_COMPANDER1_CTL2, 0xFF},
+ { CDC_2_5_WSA_COMPANDER1_CTL3, 0x35},
+ { CDC_2_5_WSA_COMPANDER1_CTL4, 0xFF},
+ { CDC_2_5_WSA_COMPANDER1_CTL5, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL6, 0x01},
+ { CDC_2_5_WSA_COMPANDER1_CTL7, 0x28},
+ { CDC_2_5_WSA_COMPANDER1_CTL8, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL9, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL10, 0x06},
+ { CDC_2_5_WSA_COMPANDER1_CTL11, 0x12},
+ { CDC_2_5_WSA_COMPANDER1_CTL12, 0x1E},
+ { CDC_2_5_WSA_COMPANDER1_CTL13, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL14, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL15, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL16, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL17, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL18, 0x2A},
+ { CDC_2_5_WSA_COMPANDER1_CTL19, 0x16},
+ { CDC_2_5_WSA_SOFTCLIP0_CRC, 0x00},
+ { CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38},
+ { CDC_2_5_WSA_SOFTCLIP1_CRC, 0x00},
+ { CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38},
+};
+
+static bool wsa_is_wronly_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_CLR_COMMIT:
+ case CDC_WSA_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN2_CLEAR0:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL0:
+ case CDC_WSA_COMPANDER1_CTL1:
+ case CDC_WSA_COMPANDER1_CTL2:
+ case CDC_WSA_COMPANDER1_CTL3:
+ case CDC_WSA_COMPANDER1_CTL4:
+ case CDC_WSA_COMPANDER1_CTL5:
+ case CDC_WSA_COMPANDER1_CTL7:
+ case CDC_WSA_SOFTCLIP0_CRC:
+ case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL:
+ case CDC_WSA_SOFTCLIP1_CRC:
+ case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_TOP_FS_UNGATE:
+ case CDC_WSA_TOP_GRP_SEL:
+ case CDC_WSA_TOP_FS_UNGATE2:
+ case CDC_2_5_WSA_COMPANDER0_CTL8:
+ case CDC_2_5_WSA_COMPANDER0_CTL9:
+ case CDC_2_5_WSA_COMPANDER0_CTL10:
+ case CDC_2_5_WSA_COMPANDER0_CTL11:
+ case CDC_2_5_WSA_COMPANDER0_CTL12:
+ case CDC_2_5_WSA_COMPANDER0_CTL13:
+ case CDC_2_5_WSA_COMPANDER0_CTL14:
+ case CDC_2_5_WSA_COMPANDER0_CTL15:
+ case CDC_2_5_WSA_COMPANDER0_CTL16:
+ case CDC_2_5_WSA_COMPANDER0_CTL17:
+ case CDC_2_5_WSA_COMPANDER0_CTL18:
+ case CDC_2_5_WSA_COMPANDER0_CTL19:
+ case CDC_2_5_WSA_COMPANDER1_CTL0:
+ case CDC_2_5_WSA_COMPANDER1_CTL1:
+ case CDC_2_5_WSA_COMPANDER1_CTL2:
+ case CDC_2_5_WSA_COMPANDER1_CTL3:
+ case CDC_2_5_WSA_COMPANDER1_CTL4:
+ case CDC_2_5_WSA_COMPANDER1_CTL5:
+ case CDC_2_5_WSA_COMPANDER1_CTL7:
+ case CDC_2_5_WSA_COMPANDER1_CTL8:
+ case CDC_2_5_WSA_COMPANDER1_CTL9:
+ case CDC_2_5_WSA_COMPANDER1_CTL10:
+ case CDC_2_5_WSA_COMPANDER1_CTL11:
+ case CDC_2_5_WSA_COMPANDER1_CTL12:
+ case CDC_2_5_WSA_COMPANDER1_CTL13:
+ case CDC_2_5_WSA_COMPANDER1_CTL14:
+ case CDC_2_5_WSA_COMPANDER1_CTL15:
+ case CDC_2_5_WSA_COMPANDER1_CTL16:
+ case CDC_2_5_WSA_COMPANDER1_CTL17:
+ case CDC_2_5_WSA_COMPANDER1_CTL18:
+ case CDC_2_5_WSA_COMPANDER1_CTL19:
+ case CDC_2_5_WSA_SOFTCLIP0_CRC:
+ case CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL:
+ case CDC_2_5_WSA_SOFTCLIP1_CRC:
+ case CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ switch (reg) {
+ case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_WSA_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_WSA_TOP_TOP_CFG0:
+ case CDC_WSA_TOP_TOP_CFG1:
+ case CDC_WSA_TOP_FREQ_MCLK:
+ case CDC_WSA_TOP_DEBUG_BUS_SEL:
+ case CDC_WSA_TOP_DEBUG_EN0:
+ case CDC_WSA_TOP_DEBUG_EN1:
+ case CDC_WSA_TOP_DEBUG_DSM_LB:
+ case CDC_WSA_TOP_RX_I2S_CTL:
+ case CDC_WSA_TOP_TX_I2S_CTL:
+ case CDC_WSA_TOP_I2S_CLK:
+ case CDC_WSA_TOP_I2S_RESET:
+ case CDC_WSA_RX_INP_MUX_RX_INT0_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_INT0_CFG1:
+ case CDC_WSA_RX_INP_MUX_RX_INT1_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_INT1_CFG1:
+ case CDC_WSA_RX_INP_MUX_RX_MIX_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_EC_CFG0:
+ case CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0:
+ case CDC_WSA_TX0_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX0_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX1_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX1_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX2_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX2_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX3_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX3_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_INTR_CTRL_CFG:
+ case CDC_WSA_INTR_CTRL_PIN1_MASK0:
+ case CDC_WSA_INTR_CTRL_PIN2_MASK0:
+ case CDC_WSA_INTR_CTRL_LEVEL0:
+ case CDC_WSA_INTR_CTRL_BYPASS0:
+ case CDC_WSA_INTR_CTRL_SET0:
+ case CDC_WSA_RX0_RX_PATH_CTL:
+ case CDC_WSA_RX0_RX_PATH_CFG0:
+ case CDC_WSA_RX0_RX_PATH_CFG1:
+ case CDC_WSA_RX0_RX_PATH_CFG2:
+ case CDC_WSA_RX0_RX_PATH_CFG3:
+ case CDC_WSA_RX0_RX_VOL_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CFG:
+ case CDC_WSA_RX0_RX_VOL_MIX_CTL:
+ case CDC_WSA_RX0_RX_PATH_SEC0:
+ case CDC_WSA_RX0_RX_PATH_SEC1:
+ case CDC_WSA_RX0_RX_PATH_SEC2:
+ case CDC_WSA_RX0_RX_PATH_SEC3:
+ case CDC_WSA_RX0_RX_PATH_SEC5:
+ case CDC_WSA_RX0_RX_PATH_SEC6:
+ case CDC_WSA_RX0_RX_PATH_SEC7:
+ case CDC_WSA_RX0_RX_PATH_MIX_SEC0:
+ case CDC_WSA_RX0_RX_PATH_MIX_SEC1:
+ case CDC_WSA_RX0_RX_PATH_DSMDEM_CTL:
+ case CDC_WSA_RX1_RX_PATH_CTL:
+ case CDC_WSA_RX1_RX_PATH_CFG0:
+ case CDC_WSA_RX1_RX_PATH_CFG1:
+ case CDC_WSA_RX1_RX_PATH_CFG2:
+ case CDC_WSA_RX1_RX_PATH_CFG3:
+ case CDC_WSA_RX1_RX_VOL_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CFG:
+ case CDC_WSA_RX1_RX_VOL_MIX_CTL:
+ case CDC_WSA_RX1_RX_PATH_SEC0:
+ case CDC_WSA_RX1_RX_PATH_SEC1:
+ case CDC_WSA_RX1_RX_PATH_SEC2:
+ case CDC_WSA_RX1_RX_PATH_SEC3:
+ case CDC_WSA_RX1_RX_PATH_SEC5:
+ case CDC_WSA_RX1_RX_PATH_SEC6:
+ case CDC_WSA_RX1_RX_PATH_SEC7:
+ case CDC_WSA_RX1_RX_PATH_MIX_SEC0:
+ case CDC_WSA_RX1_RX_PATH_MIX_SEC1:
+ case CDC_WSA_RX1_RX_PATH_DSMDEM_CTL:
+ case CDC_WSA_BOOST0_BOOST_PATH_CTL:
+ case CDC_WSA_BOOST0_BOOST_CTL:
+ case CDC_WSA_BOOST0_BOOST_CFG1:
+ case CDC_WSA_BOOST0_BOOST_CFG2:
+ case CDC_WSA_BOOST1_BOOST_PATH_CTL:
+ case CDC_WSA_BOOST1_BOOST_CTL:
+ case CDC_WSA_BOOST1_BOOST_CFG1:
+ case CDC_WSA_BOOST1_BOOST_CFG2:
+ case CDC_WSA_COMPANDER0_CTL0:
+ case CDC_WSA_COMPANDER0_CTL1:
+ case CDC_WSA_COMPANDER0_CTL2:
+ case CDC_WSA_COMPANDER0_CTL3:
+ case CDC_WSA_COMPANDER0_CTL4:
+ case CDC_WSA_COMPANDER0_CTL5:
+ case CDC_WSA_COMPANDER0_CTL7:
+ case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL:
+ case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0:
+ case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL:
+ case CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0:
+ case CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL:
+ case CDC_WSA_SPLINE_ASRC0_CTL0:
+ case CDC_WSA_SPLINE_ASRC0_CTL1:
+ case CDC_WSA_SPLINE_ASRC0_FIFO_CTL:
+ case CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL:
+ case CDC_WSA_SPLINE_ASRC1_CTL0:
+ case CDC_WSA_SPLINE_ASRC1_CTL1:
+ case CDC_WSA_SPLINE_ASRC1_FIFO_CTL:
+ return true;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_rw_register_v2_5(dev, reg);
+
+ return wsa_is_rw_register_v2_1(dev, reg);
+}
+
+static bool wsa_is_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool ret;
+
+ ret = wsa_is_rw_register(dev, reg);
+ if (!ret)
+ return wsa_is_wronly_register(dev, reg);
+
+ return ret;
+}
+
+static bool wsa_is_readable_register_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_readable_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_readable_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_CLR_COMMIT:
+ case CDC_WSA_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN2_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN1_STATUS0:
+ case CDC_WSA_INTR_CTRL_PIN2_STATUS0:
+ case CDC_WSA_COMPANDER0_CTL6:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO:
+ return true;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_readable_register_v2_5(dev, reg);
+
+ return wsa_is_readable_register_v2_1(dev, reg);
+}
+
+static bool wsa_is_volatile_register_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_volatile_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ /* Update volatile list for rx/tx macros */
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_PIN1_STATUS0:
+ case CDC_WSA_INTR_CTRL_PIN2_STATUS0:
+ case CDC_WSA_COMPANDER0_CTL6:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO:
+ return true;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_volatile_register_v2_5(dev, reg);
+
+ return wsa_is_volatile_register_v2_1(dev, reg);
+}
+
+static const struct regmap_config wsa_regmap_config = {
+ .name = "wsa_macro",
+ .reg_bits = 16,
+ .val_bits = 32, /* 8 but with 32 bit read/write */
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ /* .reg_defaults and .num_reg_defaults set in probe() */
+ .max_register = WSA_MAX_OFFSET,
+ .writeable_reg = wsa_is_writeable_register,
+ .volatile_reg = wsa_is_volatile_register,
+ .readable_reg = wsa_is_readable_register,
+};
+
+/**
+ * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @component: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->spkr_mode = mode;
+
+ switch (mode) {
+ case WSA_MACRO_SPKR_MODE_1:
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44);
+ break;
+ default:
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(wsa_macro_set_spkr_mode);
+
+static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_prim_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_1_mix1_inp;
+ u32 j, port;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) {
+ int_1_mix1_inp = port;
+ int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0;
+
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the cdc_dma rx port
+ * is connected
+ */
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET;
+ inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask);
+ inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask);
+ inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1,
+ wsa->reg_layout->rx_intx_1_mix_inp2_sel_mask);
+
+ if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) {
+ int_fs_reg = CDC_WSA_RX0_RX_PATH_CTL +
+ WSA_MACRO_RX_PATH_OFFSET * j;
+ /* sample_rate is in Hz */
+ snd_soc_component_update_bits(component, int_fs_reg,
+ WSA_MACRO_FS_RATE_MASK,
+ int_prim_fs_rate_reg_val);
+ }
+ int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET;
+ }
+ }
+
+ return 0;
+}
+
+static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_mix_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_2_inp;
+ u32 j, port;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) {
+ int_2_inp = port;
+
+ int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1,
+ wsa->reg_layout->rx_intx_2_sel_mask);
+
+ if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) {
+ int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL +
+ WSA_MACRO_RX_PATH_OFFSET * j;
+
+ snd_soc_component_update_bits(component,
+ int_fs_reg,
+ WSA_MACRO_FS_RATE_MASK,
+ int_mix_fs_rate_reg_val);
+ }
+ int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET;
+ }
+ }
+ return 0;
+}
+
+static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ /* set mixing path rate */
+ for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) {
+ if (sample_rate == int_mix_sample_rate_val[i].sample_rate) {
+ rate_val = int_mix_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || (rate_val < 0))
+ goto prim_rate;
+
+ ret = wsa_macro_set_mix_interpolator_rate(dai, (u8) rate_val, sample_rate);
+ if (ret < 0)
+ return ret;
+prim_rate:
+ /* set primary path sample rate */
+ for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) {
+ if (sample_rate == int_prim_sample_rate_val[i].sample_rate) {
+ rate_val = int_prim_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || (rate_val < 0))
+ return -EINVAL;
+
+ ret = wsa_macro_set_prim_interpolator_rate(dai, (u8) rate_val, sample_rate);
+
+ return ret;
+}
+
+static int wsa_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = wsa_macro_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(component->dev,
+ "%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if (dai->id == WSA_MACRO_AIF_VI)
+ wsa->pcm_rate_vi = params_rate(params);
+
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u16 val, mask = 0, cnt = 0, temp;
+
+ switch (dai->id) {
+ case WSA_MACRO_AIF_VI:
+ *tx_slot = wsa->active_ch_mask[dai->id];
+ *tx_num = wsa->active_ch_cnt[dai->id];
+ break;
+ case WSA_MACRO_AIF1_PB:
+ case WSA_MACRO_AIF_MIX1_PB:
+ for_each_set_bit(temp, &wsa->active_ch_mask[dai->id],
+ WSA_MACRO_RX_MAX) {
+ mask |= (1 << temp);
+ if (++cnt == WSA_MACRO_MAX_DMA_CH_PER_PORT)
+ break;
+ }
+ if (mask & 0x0C)
+ mask = mask >> 0x2;
+ *rx_slot = mask;
+ *rx_num = cnt;
+ break;
+ case WSA_MACRO_AIF_ECHO:
+ val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0);
+ if (val & WSA_MACRO_EC_MIX_TX1_MASK) {
+ mask |= 0x2;
+ cnt++;
+ }
+ if (val & WSA_MACRO_EC_MIX_TX0_MASK) {
+ mask |= 0x1;
+ cnt++;
+ }
+ *tx_slot = mask;
+ *tx_num = cnt;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wsa_macro_dai_ops = {
+ .hw_params = wsa_macro_hw_params,
+ .get_channel_map = wsa_macro_get_channel_map,
+};
+
+static struct snd_soc_dai_driver wsa_macro_dai[] = {
+ {
+ .name = "wsa_macro_rx1",
+ .id = WSA_MACRO_AIF1_PB,
+ .playback = {
+ .stream_name = "WSA_AIF1 Playback",
+ .rates = WSA_MACRO_RX_RATES,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_rx_mix",
+ .id = WSA_MACRO_AIF_MIX1_PB,
+ .playback = {
+ .stream_name = "WSA_AIF_MIX1 Playback",
+ .rates = WSA_MACRO_RX_MIX_RATES,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_vifeedback",
+ .id = WSA_MACRO_AIF_VI,
+ .capture = {
+ .stream_name = "WSA_AIF_VI Capture",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_echo",
+ .id = WSA_MACRO_AIF_ECHO,
+ .capture = {
+ .stream_name = "WSA_AIF_ECHO Capture",
+ .rates = WSA_MACRO_ECHO_RATES,
+ .formats = WSA_MACRO_ECHO_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+};
+
+static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable)
+{
+ struct regmap *regmap = wsa->regmap;
+
+ if (mclk_enable) {
+ if (wsa->wsa_mclk_users == 0) {
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ /* 9.6MHz MCLK, set value 0x00 if other frequency */
+ regmap_update_bits(regmap, CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_WSA_MCLK_EN_MASK,
+ CDC_WSA_MCLK_ENABLE);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_WSA_FS_CNT_EN_MASK,
+ CDC_WSA_FS_CNT_ENABLE);
+ }
+ wsa->wsa_mclk_users++;
+ } else {
+ if (wsa->wsa_mclk_users <= 0) {
+ dev_err(wsa->dev, "clock already disabled\n");
+ wsa->wsa_mclk_users = 0;
+ return;
+ }
+ wsa->wsa_mclk_users--;
+ if (wsa->wsa_mclk_users == 0) {
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_WSA_FS_CNT_EN_MASK,
+ CDC_WSA_FS_CNT_DISABLE);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_WSA_MCLK_EN_MASK,
+ CDC_WSA_MCLK_DISABLE);
+ }
+ }
+}
+
+static void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *component, bool enable,
+ u32 tx_reg0, u32 tx_reg1, u32 val)
+{
+ if (enable) {
+ /* Enable V&I sensing */
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ val);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ val);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_ENABLE);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_ENABLE);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_NO_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_NO_RESET);
+ } else {
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_DISABLE);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_DISABLE);
+ }
+}
+
+static void wsa_macro_enable_disable_vi_feedback(struct snd_soc_component *component,
+ bool enable, u32 rate)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI]))
+ wsa_macro_enable_disable_vi_sense(component, enable,
+ CDC_WSA_TX0_SPKR_PROT_PATH_CTL,
+ CDC_WSA_TX1_SPKR_PROT_PATH_CTL, rate);
+
+ if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI]))
+ wsa_macro_enable_disable_vi_sense(component, enable,
+ CDC_WSA_TX2_SPKR_PROT_PATH_CTL,
+ CDC_WSA_TX3_SPKR_PROT_PATH_CTL, rate);
+}
+
+static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa_macro_mclk_enable(wsa, event == SND_SOC_DAPM_PRE_PMU);
+ return 0;
+}
+
+static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 rate_val;
+
+ switch (wsa->pcm_rate_vi) {
+ case 8000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K;
+ break;
+ case 16000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_16K;
+ break;
+ case 24000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K;
+ break;
+ case 32000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K;
+ break;
+ case 48000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K;
+ break;
+ default:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable V&I sensing */
+ wsa_macro_enable_disable_vi_feedback(component, true, rate_val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable V&I sensing */
+ wsa_macro_enable_disable_vi_feedback(component, false, rate_val);
+ break;
+ }
+
+ return 0;
+}
+
+static void wsa_macro_hd2_control(struct snd_soc_component *component,
+ u16 reg, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg;
+
+ if (reg == CDC_WSA_RX0_RX_PATH_CTL) {
+ hd2_scale_reg = CDC_WSA_RX0_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_WSA_RX0_RX_PATH_CFG0;
+ }
+ if (reg == CDC_WSA_RX1_RX_PATH_CTL) {
+ hd2_scale_reg = CDC_WSA_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_WSA_RX1_RX_PATH_CFG0;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_ALPHA_MASK,
+ 0x10);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_SCALE_MASK,
+ 0x1);
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ CDC_WSA_RX_PATH_HD2_EN_MASK,
+ CDC_WSA_RX_PATH_HD2_ENABLE);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ CDC_WSA_RX_PATH_HD2_EN_MASK, 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_SCALE_MASK,
+ 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_ALPHA_MASK,
+ 0);
+ }
+}
+
+static int wsa_macro_config_compander(struct snd_soc_component *component,
+ int comp, int event)
+{
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (!wsa->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 +
+ (comp * wsa->reg_layout->compander1_reg_offset);
+ rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 +
+ (comp * WSA_MACRO_RX_PATH_OFFSET);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_CLK_EN_MASK,
+ CDC_WSA_COMPANDER_CLK_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ CDC_WSA_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ 0);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ CDC_WSA_RX_PATH_COMP_EN_MASK,
+ CDC_WSA_RX_PATH_COMP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_HALT_MASK,
+ CDC_WSA_COMPANDER_HALT);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ CDC_WSA_RX_PATH_COMP_EN_MASK, 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ CDC_WSA_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_CLK_EN_MASK, 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_HALT_MASK, 0);
+ }
+
+ return 0;
+}
+
+static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component,
+ struct wsa_macro *wsa,
+ int path,
+ bool enable)
+{
+ u16 softclip_clk_reg = wsa->reg_layout->softclip0_reg_base +
+ (path * wsa->reg_layout->softclip1_reg_offset);
+ u8 softclip_mux_mask = (1 << path);
+ u8 softclip_mux_value = (1 << path);
+
+ if (enable) {
+ if (wsa->softclip_clk_users[path] == 0) {
+ snd_soc_component_update_bits(component,
+ softclip_clk_reg,
+ CDC_WSA_SOFTCLIP_CLK_EN_MASK,
+ CDC_WSA_SOFTCLIP_CLK_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+ softclip_mux_mask, softclip_mux_value);
+ }
+ wsa->softclip_clk_users[path]++;
+ } else {
+ wsa->softclip_clk_users[path]--;
+ if (wsa->softclip_clk_users[path] == 0) {
+ snd_soc_component_update_bits(component,
+ softclip_clk_reg,
+ CDC_WSA_SOFTCLIP_CLK_EN_MASK,
+ 0);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+ softclip_mux_mask, 0x00);
+ }
+ }
+}
+
+static int wsa_macro_config_softclip(struct snd_soc_component *component,
+ int path, int event)
+{
+ u16 softclip_ctrl_reg;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int softclip_path = 0;
+
+ if (path == WSA_MACRO_COMP1)
+ softclip_path = WSA_MACRO_SOFTCLIP0;
+ else if (path == WSA_MACRO_COMP2)
+ softclip_path = WSA_MACRO_SOFTCLIP1;
+
+ if (!wsa->is_softclip_on[softclip_path])
+ return 0;
+
+ softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL +
+ (softclip_path * wsa->reg_layout->softclip1_reg_offset);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Softclip clock and mux */
+ wsa_macro_enable_softclip_clk(component, wsa, softclip_path,
+ true);
+ /* Enable Softclip control */
+ snd_soc_component_update_bits(component, softclip_ctrl_reg,
+ CDC_WSA_SOFTCLIP_EN_MASK,
+ CDC_WSA_SOFTCLIP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, softclip_ctrl_reg,
+ CDC_WSA_SOFTCLIP_EN_MASK, 0);
+ wsa_macro_enable_softclip_clk(component, wsa, softclip_path,
+ false);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind)
+{
+ u16 prim_int_reg = 0;
+
+ switch (reg) {
+ case CDC_WSA_RX0_RX_PATH_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ prim_int_reg = CDC_WSA_RX0_RX_PATH_CTL;
+ *ind = 0;
+ break;
+ case CDC_WSA_RX1_RX_PATH_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ prim_int_reg = CDC_WSA_RX1_RX_PATH_CTL;
+ *ind = 1;
+ break;
+ }
+
+ return prim_int_reg;
+}
+
+static int wsa_macro_enable_prim_interpolator(struct snd_soc_component *component,
+ u16 reg, int event)
+{
+ u16 prim_int_reg;
+ u16 ind = 0;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wsa->prim_int_users[ind]++;
+ if (wsa->prim_int_users[ind] == 1) {
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET,
+ CDC_WSA_RX_DC_DCOEFF_MASK,
+ 0x3);
+ snd_soc_component_update_bits(component, prim_int_reg,
+ CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK,
+ CDC_WSA_RX_PATH_PGA_MUTE_ENABLE);
+ wsa_macro_hd2_control(component, prim_int_reg, event);
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET,
+ CDC_WSA_RX_DSMDEM_CLK_EN_MASK,
+ CDC_WSA_RX_DSMDEM_CLK_ENABLE);
+ }
+ if ((reg != prim_int_reg) &&
+ ((snd_soc_component_read(
+ component, prim_int_reg)) & 0x10))
+ snd_soc_component_update_bits(component, reg,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wsa->prim_int_users[ind]--;
+ if (wsa->prim_int_users[ind] == 0) {
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET,
+ CDC_WSA_RX_DSMDEM_CLK_EN_MASK, 0);
+ wsa_macro_hd2_control(component, prim_int_reg, event);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component,
+ struct wsa_macro *wsa,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+
+ switch (wsa->spkr_mode) {
+ /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */
+ case WSA_MACRO_SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (wsa->comp_enabled[WSA_MACRO_COMP1] &&
+ (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) &&
+ (wsa->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + wsa->ear_spkr_gain - 1;
+ snd_soc_component_write(component, gain_reg, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX0 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (wsa->comp_enabled[WSA_MACRO_COMP1] &&
+ (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) &&
+ (wsa->ear_spkr_gain != 0)) {
+ snd_soc_component_write(component, gain_reg, 0x0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg;
+ u16 reg;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (w->shift == WSA_MACRO_COMP1) {
+ reg = CDC_WSA_RX0_RX_PATH_CTL;
+ gain_reg = CDC_WSA_RX0_RX_VOL_CTL;
+ } else if (w->shift == WSA_MACRO_COMP2) {
+ reg = CDC_WSA_RX1_RX_PATH_CTL;
+ gain_reg = CDC_WSA_RX1_RX_VOL_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reset if needed */
+ wsa_macro_enable_prim_interpolator(component, reg, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wsa_macro_config_compander(component, w->shift, event);
+ wsa_macro_config_softclip(component, w->shift, event);
+ /* apply gain after int clk is enabled */
+ if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
+ (wsa->comp_enabled[WSA_MACRO_COMP1] ||
+ wsa->comp_enabled[WSA_MACRO_COMP2])) {
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ }
+ wsa_macro_config_ear_spkr_gain(component, wsa,
+ event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wsa_macro_config_compander(component, w->shift, event);
+ wsa_macro_config_softclip(component, w->shift, event);
+ wsa_macro_enable_prim_interpolator(component, reg, event);
+ if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
+ (wsa->comp_enabled[WSA_MACRO_COMP1] ||
+ wsa->comp_enabled[WSA_MACRO_COMP2])) {
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ }
+ wsa_macro_config_ear_spkr_gain(component, wsa,
+ event, gain_reg);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_spk_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 boost_path_ctl, boost_path_cfg1;
+ u16 reg, reg_mix;
+
+ if (!snd_soc_dapm_widget_name_cmp(w, "WSA_RX INT0 CHAIN")) {
+ boost_path_ctl = CDC_WSA_BOOST0_BOOST_PATH_CTL;
+ boost_path_cfg1 = CDC_WSA_RX0_RX_PATH_CFG1;
+ reg = CDC_WSA_RX0_RX_PATH_CTL;
+ reg_mix = CDC_WSA_RX0_RX_PATH_MIX_CTL;
+ } else if (!snd_soc_dapm_widget_name_cmp(w, "WSA_RX INT1 CHAIN")) {
+ boost_path_ctl = CDC_WSA_BOOST1_BOOST_PATH_CTL;
+ boost_path_cfg1 = CDC_WSA_RX1_RX_PATH_CFG1;
+ reg = CDC_WSA_RX1_RX_PATH_CTL;
+ reg_mix = CDC_WSA_RX1_RX_PATH_MIX_CTL;
+ } else {
+ dev_warn(component->dev, "Incorrect widget name in the driver\n");
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, boost_path_cfg1,
+ CDC_WSA_RX_PATH_SMART_BST_EN_MASK,
+ CDC_WSA_RX_PATH_SMART_BST_ENABLE);
+ snd_soc_component_update_bits(component, boost_path_ctl,
+ CDC_WSA_BOOST_PATH_CLK_EN_MASK,
+ CDC_WSA_BOOST_PATH_CLK_ENABLE);
+ if ((snd_soc_component_read(component, reg_mix)) & 0x10)
+ snd_soc_component_update_bits(component, reg_mix,
+ 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, reg, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, boost_path_ctl,
+ CDC_WSA_BOOST_PATH_CLK_EN_MASK,
+ CDC_WSA_BOOST_PATH_CLK_DISABLE);
+ snd_soc_component_update_bits(component, boost_path_cfg1,
+ CDC_WSA_RX_PATH_SMART_BST_EN_MASK,
+ CDC_WSA_RX_PATH_SMART_BST_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u16 val, ec_tx, ec_hq_reg;
+
+ val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0);
+
+ switch (w->shift) {
+ case WSA_MACRO_EC0_MUX:
+ val = val & CDC_WSA_RX_MIX_TX0_SEL_MASK;
+ ec_tx = val - 1;
+ break;
+ case WSA_MACRO_EC1_MUX:
+ val = val & CDC_WSA_RX_MIX_TX1_SEL_MASK;
+ ec_tx = (val >> CDC_WSA_RX_MIX_TX1_SEL_SHFT) - 1;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid shift %u\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+
+ if (wsa->ec_hq[ec_tx]) {
+ ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + 0x40 * ec_tx;
+ snd_soc_component_update_bits(component, ec_hq_reg,
+ CDC_WSA_EC_HQ_EC_CLK_EN_MASK,
+ CDC_WSA_EC_HQ_EC_CLK_ENABLE);
+ ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + 0x40 * ec_tx;
+ /* default set to 48k */
+ snd_soc_component_update_bits(component, ec_hq_reg,
+ CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK,
+ CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->ec_hq[ec_tx];
+
+ return 0;
+}
+
+static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->ec_hq[ec_tx] = value;
+
+ return 0;
+}
+
+static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->comp_enabled[comp];
+ return 0;
+}
+
+static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->comp_enabled[comp] = value;
+
+ return 0;
+}
+
+static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->ear_spkr_gain;
+
+ return 0;
+}
+
+static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] =
+ wsa->rx_port_value[widget->shift];
+ return 0;
+}
+
+static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 rx_port_value = ucontrol->value.integer.value[0];
+ u32 bit_input;
+ u32 aif_rst;
+ unsigned int dai_id;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ aif_rst = wsa->rx_port_value[widget->shift];
+ if (!rx_port_value) {
+ if (aif_rst == 0)
+ return 0;
+ if (aif_rst >= WSA_MACRO_RX_MAX) {
+ dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
+ return 0;
+ }
+ }
+ wsa->rx_port_value[widget->shift] = rx_port_value;
+
+ bit_input = widget->shift;
+
+ switch (rx_port_value) {
+ case 0:
+ /*
+ * active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS).
+ * active_ch_cnt == 0 was tested in if() above.
+ */
+ dai_id = aif_rst - 1;
+ if (wsa->active_ch_cnt[dai_id]) {
+ clear_bit(bit_input, &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ break;
+ case 1:
+ case 2:
+ /* active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS). */
+ dai_id = rx_port_value - 1;
+ set_bit(bit_input, &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Invalid AIF_ID for WSA RX MUX %d\n",
+ __func__, rx_port_value);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+ return 0;
+}
+
+static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int path = ((struct soc_mixer_control *)kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] = wsa->is_softclip_on[path];
+
+ return 0;
+}
+
+static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int path = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+
+ wsa->is_softclip_on[path] = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wsa_macro_snd_controls[] = {
+ SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum,
+ wsa_macro_ear_spkr_pa_gain_get,
+ wsa_macro_ear_spkr_pa_gain_put),
+ SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM,
+ WSA_MACRO_SOFTCLIP0, 1, 0,
+ wsa_macro_soft_clip_enable_get,
+ wsa_macro_soft_clip_enable_put),
+ SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM,
+ WSA_MACRO_SOFTCLIP1, 1, 0,
+ wsa_macro_soft_clip_enable_get,
+ wsa_macro_soft_clip_enable_put),
+
+ SOC_SINGLE_S8_TLV("WSA_RX0 Digital Volume", CDC_WSA_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("WSA_RX1 Digital Volume", CDC_WSA_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("WSA_RX0_MIX Digital Volume", CDC_WSA_RX0_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("WSA_RX1_MIX Digital Volume", CDC_WSA_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE("WSA_RX0 Digital Mute", CDC_WSA_RX0_RX_PATH_CTL, 4, 1, 0),
+ SOC_SINGLE("WSA_RX1 Digital Mute", CDC_WSA_RX1_RX_PATH_CTL, 4, 1, 0),
+ SOC_SINGLE("WSA_RX0_MIX Digital Mute", CDC_WSA_RX0_RX_PATH_MIX_CTL, 4,
+ 1, 0),
+ SOC_SINGLE("WSA_RX1_MIX Digital Mute", CDC_WSA_RX1_RX_PATH_MIX_CTL, 4,
+ 1, 0),
+ SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0,
+ wsa_macro_get_compander, wsa_macro_set_compander),
+ SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0,
+ wsa_macro_get_compander, wsa_macro_set_compander),
+ SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, 1, 0,
+ wsa_macro_get_ec_hq, wsa_macro_set_ec_hq),
+ SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, 1, 0,
+ wsa_macro_get_ec_hq, wsa_macro_set_ec_hq),
+};
+
+static const struct soc_enum rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text);
+
+static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+};
+
+static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 spk_tx_id = mixer->shift;
+ u32 dai_id = widget->shift;
+
+ if (test_bit(spk_tx_id, &wsa->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 enable = ucontrol->value.integer.value[0];
+ u32 spk_tx_id = mixer->shift;
+ u32 dai_id = widget->shift;
+
+ if (enable) {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ !test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id])) {
+ set_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ !test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id])) {
+ set_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ }
+ } else {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id])) {
+ clear_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id])) {
+ clear_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ }
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aif_vi_mixer[] = {
+ SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0,
+ wsa_macro_vi_feed_mixer_get,
+ wsa_macro_vi_feed_mixer_put),
+ SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0,
+ wsa_macro_vi_feed_mixer_get,
+ wsa_macro_vi_feed_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0,
+ SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0,
+ wsa_macro_enable_vi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI,
+ 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)),
+ SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM,
+ WSA_MACRO_EC0_MUX, 0,
+ &rx_mix_ec0_mux, wsa_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM,
+ WSA_MACRO_EC1_MUX, 0,
+ &rx_mix_ec1_mux, wsa_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0,
+ &rx_mux[WSA_MACRO_RX0]),
+ SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0,
+ &rx_mux[WSA_MACRO_RX1]),
+ SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0,
+ &rx_mux[WSA_MACRO_RX_MIX0]),
+ SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0,
+ &rx_mux[WSA_MACRO_RX_MIX1]),
+
+ SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA_RX INT1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("WSA_RX0 INT0 SIDETONE MIX", CDC_WSA_RX0_RX_PATH_CFG1,
+ 4, 0, &rx0_sidetone_mix_mux),
+
+ SND_SOC_DAPM_INPUT("WSA SRC0_INP"),
+ SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"),
+ SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM,
+ WSA_MACRO_COMP1, 0, NULL, 0,
+ wsa_macro_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM,
+ WSA_MACRO_COMP2, 0, NULL, 0,
+ wsa_macro_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wsa_macro_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wsa_macro_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VIINPUT_WSA"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"),
+
+ SND_SOC_DAPM_SUPPLY("WSA_RX0_CLK", CDC_WSA_RX0_RX_PATH_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX1_CLK", CDC_WSA_RX1_RX_PATH_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX_MIX0_CLK", CDC_WSA_RX0_RX_PATH_MIX_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX_MIX1_CLK", CDC_WSA_RX1_RX_PATH_MIX_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ wsa_macro_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_1[] = {
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 MIX INP", SND_SOC_NOPM, 0, 0, &rx0_mix_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 MIX INP", SND_SOC_NOPM, 0, 0, &rx1_mix_mux_v2_1),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_5[] = {
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 MIX INP", SND_SOC_NOPM, 0, 0, &rx0_mix_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 MIX INP", SND_SOC_NOPM, 0, 0, &rx1_mix_mux_v2_5),
+};
+
+static const struct snd_soc_dapm_route wsa_audio_map[] = {
+ /* VI Feedback */
+ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"},
+ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"},
+ {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"},
+ {"WSA AIF_VI", NULL, "WSA_MCLK"},
+
+ {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"},
+ {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"},
+ {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"},
+ {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"},
+ {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"},
+ {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"},
+ {"WSA AIF_ECHO", NULL, "WSA_MCLK"},
+
+ {"WSA AIF1 PB", NULL, "WSA_MCLK"},
+ {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"},
+
+ {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"},
+
+ {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+
+ {"WSA RX0", NULL, "WSA RX0 MUX"},
+ {"WSA RX1", NULL, "WSA RX1 MUX"},
+ {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"},
+ {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"},
+
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0_CLK"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1_CLK"},
+
+ {"WSA_RX0 INP0", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP0", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"},
+
+ {"WSA_RX0 INP1", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP1", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"},
+
+ {"WSA_RX0 INP2", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP2", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"},
+
+ {"WSA_RX0 MIX INP", "RX0", "WSA RX0"},
+ {"WSA_RX0 MIX INP", "RX1", "WSA RX1"},
+ {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 MIX INP", NULL, "WSA_RX0_CLK"},
+ {"WSA_RX0 MIX INP", NULL, "WSA_RX_MIX0_CLK"},
+ {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"},
+
+ {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"},
+ {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"},
+ {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"},
+ {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"},
+ {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"},
+
+ {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"},
+ {"WSA_SPK1 OUT", NULL, "WSA_MCLK"},
+
+ {"WSA_RX1 INP0", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP0", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"},
+
+ {"WSA_RX1 INP1", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP1", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"},
+
+ {"WSA_RX1 INP2", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP2", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"},
+
+ {"WSA_RX1 MIX INP", "RX0", "WSA RX0"},
+ {"WSA_RX1 MIX INP", "RX1", "WSA RX1"},
+ {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 MIX INP", NULL, "WSA_RX1_CLK"},
+ {"WSA_RX1 MIX INP", NULL, "WSA_RX_MIX1_CLK"},
+ {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"},
+
+ {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"},
+ {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"},
+
+ {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"},
+ {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"},
+ {"WSA_SPK2 OUT", NULL, "WSA_MCLK"},
+};
+
+static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable)
+{
+ struct regmap *regmap = wsa->regmap;
+
+ if (enable) {
+ int ret;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret) {
+ dev_err(wsa->dev, "failed to enable mclk\n");
+ return ret;
+ }
+ wsa_macro_mclk_enable(wsa, true);
+
+ regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK,
+ CDC_WSA_SWR_CLK_ENABLE);
+
+ } else {
+ regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK, 0);
+ wsa_macro_mclk_enable(wsa, false);
+ clk_disable_unprepare(wsa->mclk);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(comp);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp);
+ const struct snd_soc_dapm_widget *widgets;
+ unsigned int num_widgets;
+
+ snd_soc_component_init_regmap(comp, wsa->regmap);
+
+ wsa->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_M1P5_DB;
+
+ /* set SPKR rate to FS_2P4_3P072 */
+ snd_soc_component_update_bits(comp, CDC_WSA_RX0_RX_PATH_CFG1,
+ CDC_WSA_RX_PATH_SPKR_RATE_MASK,
+ CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072);
+
+ snd_soc_component_update_bits(comp, CDC_WSA_RX1_RX_PATH_CFG1,
+ CDC_WSA_RX_PATH_SPKR_RATE_MASK,
+ CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072);
+
+ wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1);
+
+ switch (wsa->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ widgets = wsa_macro_dapm_widgets_v2_1;
+ num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1);
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
+ widgets = wsa_macro_dapm_widgets_v2_5;
+ num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ return wsa_swrm_clock(to_wsa_macro(hw), true);
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ wsa_swrm_clock(to_wsa_macro(hw), false);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct wsa_macro *wsa = to_wsa_macro(hw);
+ int ret, val;
+
+ regmap_read(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+};
+
+static int wsa_macro_register_mclk_output(struct wsa_macro *wsa)
+{
+ struct device *dev = wsa->dev;
+ const char *parent_clk_name;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (wsa->npl)
+ parent_clk_name = __clk_get_name(wsa->npl);
+ else
+ parent_clk_name = __clk_get_name(wsa->mclk);
+
+ init.name = "mclk";
+ of_property_read_string(dev_of_node(dev), "clock-output-names",
+ &init.name);
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ wsa->hw.init = &init;
+ hw = &wsa->hw;
+ ret = clk_hw_register(wsa->dev, hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct snd_soc_component_driver wsa_macro_component_drv = {
+ .name = "WSA MACRO",
+ .probe = wsa_macro_component_probe,
+ .controls = wsa_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa_macro_snd_controls),
+ .dapm_widgets = wsa_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets),
+ .dapm_routes = wsa_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa_audio_map),
+};
+
+static int wsa_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wsa_macro *wsa;
+ kernel_ulong_t flags;
+ void __iomem *base;
+ int ret, def_count;
+
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
+ wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL);
+ if (!wsa)
+ return -ENOMEM;
+
+ wsa->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(wsa->macro))
+ return dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n");
+
+ wsa->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(wsa->dcodec))
+ return dev_err_probe(dev, PTR_ERR(wsa->dcodec), "unable to get dcodec clock\n");
+
+ wsa->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(wsa->mclk))
+ return dev_err_probe(dev, PTR_ERR(wsa->mclk), "unable to get mclk clock\n");
+
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ wsa->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(wsa->npl))
+ return dev_err_probe(dev, PTR_ERR(wsa->npl), "unable to get npl clock\n");
+ }
+
+ wsa->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(wsa->fsgen))
+ return dev_err_probe(dev, PTR_ERR(wsa->fsgen), "unable to get fsgen clock\n");
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ wsa->codec_version = lpass_macro_get_codec_version();
+ struct reg_default *reg_defaults __free(kfree) = NULL;
+
+ switch (wsa->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ wsa->reg_layout = &wsa_codec_v2_1;
+ def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1);
+ reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
+ GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], wsa_defaults, sizeof(wsa_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(wsa_defaults)],
+ wsa_defaults_v2_1, sizeof(wsa_defaults_v2_1));
+ break;
+
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
+ wsa->reg_layout = &wsa_codec_v2_5;
+ def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5);
+ reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
+ GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], wsa_defaults, sizeof(wsa_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(wsa_defaults)],
+ wsa_defaults_v2_5, sizeof(wsa_defaults_v2_5));
+ break;
+
+ default:
+ dev_err(dev, "Unsupported Codec version (%d)\n", wsa->codec_version);
+ return -EINVAL;
+ }
+
+ struct regmap_config *reg_config __free(kfree) = kmemdup(&wsa_regmap_config,
+ sizeof(*reg_config),
+ GFP_KERNEL);
+ if (!reg_config)
+ return -ENOMEM;
+
+ reg_config->reg_defaults = reg_defaults;
+ reg_config->num_reg_defaults = def_count;
+
+ wsa->regmap = devm_regmap_init_mmio(dev, base, reg_config);
+ if (IS_ERR(wsa->regmap))
+ return PTR_ERR(wsa->regmap);
+
+ dev_set_drvdata(dev, wsa);
+
+ wsa->dev = dev;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ);
+ clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ);
+
+ ret = clk_prepare_enable(wsa->macro);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(wsa->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ /* reset swr ip */
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE);
+
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK, CDC_WSA_SWR_CLK_ENABLE);
+
+ /* Bring out of reset */
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE);
+
+ ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv,
+ wsa_macro_dai,
+ ARRAY_SIZE(wsa_macro_dai));
+ if (ret)
+ goto err_clkout;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = wsa_macro_register_mclk_output(wsa);
+ if (ret)
+ goto err_clkout;
+
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(wsa->fsgen);
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+err_mclk:
+ clk_disable_unprepare(wsa->dcodec);
+err_dcodec:
+ clk_disable_unprepare(wsa->macro);
+err:
+ return ret;
+
+}
+
+static void wsa_macro_remove(struct platform_device *pdev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(wsa->macro);
+ clk_disable_unprepare(wsa->dcodec);
+ clk_disable_unprepare(wsa->mclk);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->fsgen);
+}
+
+static int wsa_macro_runtime_suspend(struct device *dev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ regcache_cache_only(wsa->regmap, true);
+ regcache_mark_dirty(wsa->regmap);
+
+ clk_disable_unprepare(wsa->fsgen);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->mclk);
+
+ return 0;
+}
+
+static int wsa_macro_runtime_resume(struct device *dev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclkx2\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+
+ regcache_cache_only(wsa->regmap, false);
+ regcache_sync(wsa->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops wsa_macro_pm_ops = {
+ RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL)
+};
+
+static const struct of_device_id wsa_macro_dt_match[] = {
+ {
+ .compatible = "qcom,sc7280-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8250-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-wsa-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wsa_macro_dt_match);
+
+static struct platform_driver wsa_macro_driver = {
+ .driver = {
+ .name = "wsa_macro",
+ .of_match_table = wsa_macro_dt_match,
+ .pm = pm_ptr(&wsa_macro_pm_ops),
+ },
+ .probe = wsa_macro_probe,
+ .remove = wsa_macro_remove,
+};
+
+module_platform_driver(wsa_macro_driver);
+MODULE_DESCRIPTION("WSA macro driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h
new file mode 100644
index 000000000000..d3d62b3f6500
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LPASS_WSA_MACRO_H__
+#define __LPASS_WSA_MACRO_H__
+
+/*
+ * Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+ WSA_MACRO_SPKR_MODE_DEFAULT,
+ WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode);
+
+#endif /* __LPASS_WSA_MACRO_H__ */
diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c
new file mode 100644
index 000000000000..98d72db599d8
--- /dev/null
+++ b/sound/soc/codecs/madera.c
@@ -0,0 +1,4796 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Cirrus Logic Madera class codecs common support
+//
+// Copyright (C) 2015-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/string_choices.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+#include <linux/mfd/madera/pdata.h>
+#include <sound/madera-pdata.h>
+
+#include <dt-bindings/sound/madera.h>
+
+#include "madera.h"
+
+#define MADERA_AIF_BCLK_CTRL 0x00
+#define MADERA_AIF_TX_PIN_CTRL 0x01
+#define MADERA_AIF_RX_PIN_CTRL 0x02
+#define MADERA_AIF_RATE_CTRL 0x03
+#define MADERA_AIF_FORMAT 0x04
+#define MADERA_AIF_RX_BCLK_RATE 0x06
+#define MADERA_AIF_FRAME_CTRL_1 0x07
+#define MADERA_AIF_FRAME_CTRL_2 0x08
+#define MADERA_AIF_FRAME_CTRL_3 0x09
+#define MADERA_AIF_FRAME_CTRL_4 0x0A
+#define MADERA_AIF_FRAME_CTRL_5 0x0B
+#define MADERA_AIF_FRAME_CTRL_6 0x0C
+#define MADERA_AIF_FRAME_CTRL_7 0x0D
+#define MADERA_AIF_FRAME_CTRL_8 0x0E
+#define MADERA_AIF_FRAME_CTRL_9 0x0F
+#define MADERA_AIF_FRAME_CTRL_10 0x10
+#define MADERA_AIF_FRAME_CTRL_11 0x11
+#define MADERA_AIF_FRAME_CTRL_12 0x12
+#define MADERA_AIF_FRAME_CTRL_13 0x13
+#define MADERA_AIF_FRAME_CTRL_14 0x14
+#define MADERA_AIF_FRAME_CTRL_15 0x15
+#define MADERA_AIF_FRAME_CTRL_16 0x16
+#define MADERA_AIF_FRAME_CTRL_17 0x17
+#define MADERA_AIF_FRAME_CTRL_18 0x18
+#define MADERA_AIF_TX_ENABLES 0x19
+#define MADERA_AIF_RX_ENABLES 0x1A
+#define MADERA_AIF_FORCE_WRITE 0x1B
+
+#define MADERA_DSP_CONFIG_1_OFFS 0x00
+#define MADERA_DSP_CONFIG_2_OFFS 0x02
+
+#define MADERA_DSP_CLK_SEL_MASK 0x70000
+#define MADERA_DSP_CLK_SEL_SHIFT 16
+
+#define MADERA_DSP_RATE_MASK 0x7800
+#define MADERA_DSP_RATE_SHIFT 11
+
+#define MADERA_SYSCLK_6MHZ 0
+#define MADERA_SYSCLK_12MHZ 1
+#define MADERA_SYSCLK_24MHZ 2
+#define MADERA_SYSCLK_49MHZ 3
+#define MADERA_SYSCLK_98MHZ 4
+
+#define MADERA_DSPCLK_9MHZ 0
+#define MADERA_DSPCLK_18MHZ 1
+#define MADERA_DSPCLK_36MHZ 2
+#define MADERA_DSPCLK_73MHZ 3
+#define MADERA_DSPCLK_147MHZ 4
+
+#define MADERA_FLL_VCO_CORNER 141900000
+#define MADERA_FLL_MAX_FREF 13500000
+#define MADERA_FLL_MAX_N 1023
+#define MADERA_FLL_MIN_FOUT 90000000
+#define MADERA_FLL_MAX_FOUT 100000000
+#define MADERA_FLL_MAX_FRATIO 16
+#define MADERA_FLL_MAX_REFDIV 8
+#define MADERA_FLL_OUTDIV 3
+#define MADERA_FLL_VCO_MULT 3
+#define MADERA_FLLAO_MAX_FREF 12288000
+#define MADERA_FLLAO_MIN_N 4
+#define MADERA_FLLAO_MAX_N 1023
+#define MADERA_FLLAO_MAX_FBDIV 254
+#define MADERA_FLLHJ_INT_MAX_N 1023
+#define MADERA_FLLHJ_INT_MIN_N 1
+#define MADERA_FLLHJ_FRAC_MAX_N 255
+#define MADERA_FLLHJ_FRAC_MIN_N 4
+#define MADERA_FLLHJ_LOW_THRESH 192000
+#define MADERA_FLLHJ_MID_THRESH 1152000
+#define MADERA_FLLHJ_MAX_THRESH 13000000
+#define MADERA_FLLHJ_LOW_GAINS 0x23f0
+#define MADERA_FLLHJ_MID_GAINS 0x22f2
+#define MADERA_FLLHJ_HIGH_GAINS 0x21f0
+
+#define MADERA_FLL_SYNCHRONISER_OFFS 0x10
+#define CS47L35_FLL_SYNCHRONISER_OFFS 0xE
+#define MADERA_FLL_CONTROL_1_OFFS 0x1
+#define MADERA_FLL_CONTROL_2_OFFS 0x2
+#define MADERA_FLL_CONTROL_3_OFFS 0x3
+#define MADERA_FLL_CONTROL_4_OFFS 0x4
+#define MADERA_FLL_CONTROL_5_OFFS 0x5
+#define MADERA_FLL_CONTROL_6_OFFS 0x6
+#define MADERA_FLL_GAIN_OFFS 0x8
+#define MADERA_FLL_CONTROL_7_OFFS 0x9
+#define MADERA_FLL_EFS_2_OFFS 0xA
+#define MADERA_FLL_SYNCHRONISER_1_OFFS 0x1
+#define MADERA_FLL_SYNCHRONISER_2_OFFS 0x2
+#define MADERA_FLL_SYNCHRONISER_3_OFFS 0x3
+#define MADERA_FLL_SYNCHRONISER_4_OFFS 0x4
+#define MADERA_FLL_SYNCHRONISER_5_OFFS 0x5
+#define MADERA_FLL_SYNCHRONISER_6_OFFS 0x6
+#define MADERA_FLL_SYNCHRONISER_7_OFFS 0x7
+#define MADERA_FLL_SPREAD_SPECTRUM_OFFS 0x9
+#define MADERA_FLL_GPIO_CLOCK_OFFS 0xA
+#define MADERA_FLL_CONTROL_10_OFFS 0xA
+#define MADERA_FLL_CONTROL_11_OFFS 0xB
+#define MADERA_FLL1_DIGITAL_TEST_1_OFFS 0xD
+
+#define MADERA_FLLAO_CONTROL_1_OFFS 0x1
+#define MADERA_FLLAO_CONTROL_2_OFFS 0x2
+#define MADERA_FLLAO_CONTROL_3_OFFS 0x3
+#define MADERA_FLLAO_CONTROL_4_OFFS 0x4
+#define MADERA_FLLAO_CONTROL_5_OFFS 0x5
+#define MADERA_FLLAO_CONTROL_6_OFFS 0x6
+#define MADERA_FLLAO_CONTROL_7_OFFS 0x8
+#define MADERA_FLLAO_CONTROL_8_OFFS 0xA
+#define MADERA_FLLAO_CONTROL_9_OFFS 0xB
+#define MADERA_FLLAO_CONTROL_10_OFFS 0xC
+#define MADERA_FLLAO_CONTROL_11_OFFS 0xD
+
+#define MADERA_FMT_DSP_MODE_A 0
+#define MADERA_FMT_DSP_MODE_B 1
+#define MADERA_FMT_I2S_MODE 2
+#define MADERA_FMT_LEFT_JUSTIFIED_MODE 3
+
+#define madera_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define madera_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define madera_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define madera_aif_err(_dai, fmt, ...) \
+ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define madera_aif_warn(_dai, fmt, ...) \
+ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define madera_aif_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = {
+ MADERA_IRQ_DSP1_BUS_ERR,
+ MADERA_IRQ_DSP2_BUS_ERR,
+ MADERA_IRQ_DSP3_BUS_ERR,
+ MADERA_IRQ_DSP4_BUS_ERR,
+ MADERA_IRQ_DSP5_BUS_ERR,
+ MADERA_IRQ_DSP6_BUS_ERR,
+ MADERA_IRQ_DSP7_BUS_ERR,
+};
+
+int madera_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(madera->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) {
+ case MADERA_CLK_SRC_MCLK1:
+ clk_idx = MADERA_MCLK1;
+ break;
+ case MADERA_CLK_SRC_MCLK2:
+ clk_idx = MADERA_MCLK2;
+ break;
+ case MADERA_CLK_SRC_MCLK3:
+ clk_idx = MADERA_MCLK3;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(madera->mclk[clk_idx].clk);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(madera->mclk[clk_idx].clk);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(madera_clk_ev);
+
+static void madera_spin_sysclk(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int ret, i;
+
+ /* Skip this if the chip is down */
+ if (pm_runtime_suspended(madera->dev))
+ return;
+
+ /*
+ * Just read a register a few times to ensure the internal
+ * oscillator sends out a few clocks.
+ */
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &val);
+ if (ret)
+ dev_err(madera->dev,
+ "Failed to read sysclk spin %d: %d\n", i, ret);
+ }
+
+ udelay(300);
+}
+
+int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ madera_spin_sysclk(priv);
+ break;
+ default:
+ break;
+ }
+
+ return madera_clk_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(madera_sysclk_ev);
+
+static int madera_check_speaker_overheat(struct madera *madera,
+ bool *warn, bool *shutdown)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_15, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to read thermal status: %d\n",
+ ret);
+ return ret;
+ }
+
+ *warn = val & MADERA_SPK_OVERHEAT_WARN_STS1;
+ *shutdown = val & MADERA_SPK_OVERHEAT_STS1;
+
+ return 0;
+}
+
+int madera_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ bool warn, shutdown;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = madera_check_speaker_overheat(madera, &warn, &shutdown);
+ if (ret)
+ return ret;
+
+ if (shutdown) {
+ dev_crit(madera->dev,
+ "Speaker not enabled due to temperature\n");
+ return -EBUSY;
+ }
+
+ regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 1 << w->shift);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_spk_ev);
+
+static irqreturn_t madera_thermal_warn(int irq, void *data)
+{
+ struct madera *madera = data;
+ bool warn, shutdown;
+ int ret;
+
+ ret = madera_check_speaker_overheat(madera, &warn, &shutdown);
+ if (ret || shutdown) { /* for safety attempt to shutdown on error */
+ dev_crit(madera->dev, "Thermal shutdown\n");
+ ret = regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT4L_ENA |
+ MADERA_OUT4R_ENA, 0);
+ if (ret != 0)
+ dev_crit(madera->dev,
+ "Failed to disable speaker outputs: %d\n",
+ ret);
+ } else if (warn) {
+ dev_alert(madera->dev, "Thermal warning\n");
+ } else {
+ dev_info(madera->dev, "Spurious thermal warning\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+int madera_init_overheat(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ struct device *dev = madera->dev;
+ int ret;
+
+ ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN,
+ "Thermal warning", madera_thermal_warn,
+ madera);
+ if (ret)
+ dev_err(dev, "Failed to get thermal warning IRQ: %d\n", ret);
+
+ ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT,
+ "Thermal shutdown", madera_thermal_warn,
+ madera);
+ if (ret)
+ dev_err(dev, "Failed to get thermal shutdown IRQ: %d\n", ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_overheat);
+
+int madera_free_overheat(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+
+ madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN, madera);
+ madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT, madera);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_free_overheat);
+
+static int madera_get_variable_u32_array(struct device *dev,
+ const char *propname,
+ u32 *dest, int n_max,
+ int multiple)
+{
+ int n, ret;
+
+ n = device_property_count_u32(dev, propname);
+ if (n < 0) {
+ if (n == -EINVAL)
+ return 0; /* missing, ignore */
+
+ dev_warn(dev, "%s malformed (%d)\n", propname, n);
+
+ return n;
+ } else if ((n % multiple) != 0) {
+ dev_warn(dev, "%s not a multiple of %d entries\n",
+ propname, multiple);
+
+ return -EINVAL;
+ }
+
+ if (n > n_max)
+ n = n_max;
+
+ ret = device_property_read_u32_array(dev, propname, dest, n);
+ if (ret < 0)
+ return ret;
+
+ return n;
+}
+
+static void madera_prop_get_inmode(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ struct madera_codec_pdata *pdata = &madera->pdata.codec;
+ u32 tmp[MADERA_MAX_INPUT * MADERA_MAX_MUXED_CHANNELS];
+ int n, i, in_idx, ch_idx;
+
+ BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode) != MADERA_MAX_INPUT);
+ BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode[0]) != MADERA_MAX_MUXED_CHANNELS);
+
+ n = madera_get_variable_u32_array(madera->dev, "cirrus,inmode",
+ tmp, ARRAY_SIZE(tmp),
+ MADERA_MAX_MUXED_CHANNELS);
+ if (n < 0)
+ return;
+
+ in_idx = 0;
+ ch_idx = 0;
+ for (i = 0; i < n; ++i) {
+ pdata->inmode[in_idx][ch_idx] = tmp[i];
+
+ if (++ch_idx == MADERA_MAX_MUXED_CHANNELS) {
+ ch_idx = 0;
+ ++in_idx;
+ }
+ }
+}
+
+static void madera_prop_get_pdata(struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ struct madera_codec_pdata *pdata = &madera->pdata.codec;
+ u32 out_mono[ARRAY_SIZE(pdata->out_mono)];
+ int i, n;
+
+ madera_prop_get_inmode(priv);
+
+ n = madera_get_variable_u32_array(madera->dev, "cirrus,out-mono",
+ out_mono, ARRAY_SIZE(out_mono), 1);
+ if (n > 0)
+ for (i = 0; i < n; ++i)
+ pdata->out_mono[i] = !!out_mono[i];
+
+ madera_get_variable_u32_array(madera->dev,
+ "cirrus,max-channels-clocked",
+ pdata->max_channels_clocked,
+ ARRAY_SIZE(pdata->max_channels_clocked),
+ 1);
+
+ madera_get_variable_u32_array(madera->dev, "cirrus,pdm-fmt",
+ pdata->pdm_fmt,
+ ARRAY_SIZE(pdata->pdm_fmt), 1);
+
+ madera_get_variable_u32_array(madera->dev, "cirrus,pdm-mute",
+ pdata->pdm_mute,
+ ARRAY_SIZE(pdata->pdm_mute), 1);
+
+ madera_get_variable_u32_array(madera->dev, "cirrus,dmic-ref",
+ pdata->dmic_ref,
+ ARRAY_SIZE(pdata->dmic_ref), 1);
+}
+
+int madera_core_init(struct madera_priv *priv)
+{
+ int i;
+
+ /* trap undersized array initializers */
+ BUILD_BUG_ON(!madera_mixer_texts[MADERA_NUM_MIXER_INPUTS - 1]);
+ BUILD_BUG_ON(!madera_mixer_values[MADERA_NUM_MIXER_INPUTS - 1]);
+
+ if (!dev_get_platdata(priv->madera->dev))
+ madera_prop_get_pdata(priv);
+
+ mutex_init(&priv->rate_lock);
+
+ for (i = 0; i < MADERA_MAX_HP_OUTPUT; i++)
+ priv->madera->out_clamp[i] = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_core_init);
+
+int madera_core_free(struct madera_priv *priv)
+{
+ mutex_destroy(&priv->rate_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_core_free);
+
+static void madera_debug_dump_domain_groups(const struct madera_priv *priv)
+{
+ struct madera *madera = priv->madera;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->domain_group_ref); ++i)
+ dev_dbg(madera->dev, "domain_grp_ref[%d]=%d\n", i,
+ priv->domain_group_ref[i]);
+}
+
+int madera_domain_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ int dom_grp = w->shift;
+
+ if (dom_grp >= ARRAY_SIZE(priv->domain_group_ref)) {
+ WARN(true, "%s dom_grp exceeds array size\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * We can't rely on the DAPM mutex for locking because we need a lock
+ * that can safely be called in hw_params
+ */
+ mutex_lock(&priv->rate_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(priv->madera->dev, "Inc ref on domain group %d\n",
+ dom_grp);
+ ++priv->domain_group_ref[dom_grp];
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(priv->madera->dev, "Dec ref on domain group %d\n",
+ dom_grp);
+ --priv->domain_group_ref[dom_grp];
+ break;
+ default:
+ break;
+ }
+
+ madera_debug_dump_domain_groups(priv);
+
+ mutex_unlock(&priv->rate_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_domain_clk_ev);
+
+int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int ep_sel, mux, change;
+ bool out_mono;
+ int ret;
+
+ if (ucontrol->value.enumerated.item[0] > e->items - 1)
+ return -EINVAL;
+
+ mux = ucontrol->value.enumerated.item[0];
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ep_sel = mux << MADERA_EP_SEL_SHIFT;
+
+ change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_MASK,
+ ep_sel);
+ if (!change)
+ goto end;
+
+ /* EP_SEL should not be modified while HP or EP driver is enabled */
+ ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT1L_ENA | MADERA_OUT1R_ENA, 0);
+ if (ret)
+ dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret);
+
+ usleep_range(2000, 3000); /* wait for wseq to complete */
+
+ /* change demux setting */
+ ret = 0;
+ if (madera->out_clamp[0])
+ ret = regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_EP_SEL_MASK, ep_sel);
+ if (ret) {
+ dev_err(madera->dev, "Failed to set OUT1 demux: %d\n", ret);
+ } else {
+ /* apply correct setting for mono mode */
+ if (!ep_sel && !madera->pdata.codec.out_mono[0])
+ out_mono = false; /* stereo HP */
+ else
+ out_mono = true; /* EP or mono HP */
+
+ ret = madera_set_output_mode(component, 1, out_mono);
+ if (ret)
+ dev_warn(madera->dev,
+ "Failed to set output mode: %d\n", ret);
+ }
+
+ /*
+ * if HPDET has disabled the clamp while switching to HPOUT
+ * OUT1 should remain disabled
+ */
+ if (ep_sel ||
+ (madera->out_clamp[0] && !madera->out_shorted[0])) {
+ ret = regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_ENABLES_1,
+ MADERA_OUT1L_ENA | MADERA_OUT1R_ENA,
+ madera->hp_ena);
+ if (ret)
+ dev_warn(madera->dev,
+ "Failed to restore earpiece outputs: %d\n",
+ ret);
+ else if (madera->hp_ena)
+ msleep(34); /* wait for enable wseq */
+ else
+ usleep_range(2000, 3000); /* wait for disable wseq */
+ }
+
+end:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+ if (ret < 0) {
+ dev_err(madera->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
+
+ return change;
+}
+EXPORT_SYMBOL_GPL(madera_out1_demux_put);
+
+int madera_out1_demux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1);
+ val &= MADERA_EP_SEL_MASK;
+ val >>= MADERA_EP_SEL_SHIFT;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_out1_demux_get);
+
+static int madera_inmux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ struct regmap *regmap = madera->regmap;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, val, mask;
+ unsigned int inmode;
+ bool changed;
+ int ret;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ val = mux << e->shift_l;
+ mask = (e->mask << e->shift_l) | MADERA_IN1L_SRC_SE_MASK;
+
+ switch (e->reg) {
+ case MADERA_ADC_DIGITAL_VOLUME_1L:
+ inmode = madera->pdata.codec.inmode[0][2 * mux];
+ break;
+ case MADERA_ADC_DIGITAL_VOLUME_1R:
+ inmode = madera->pdata.codec.inmode[0][1 + (2 * mux)];
+ break;
+ case MADERA_ADC_DIGITAL_VOLUME_2L:
+ inmode = madera->pdata.codec.inmode[1][2 * mux];
+ break;
+ case MADERA_ADC_DIGITAL_VOLUME_2R:
+ inmode = madera->pdata.codec.inmode[1][1 + (2 * mux)];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (inmode & MADERA_INMODE_SE)
+ val |= 1 << MADERA_IN1L_SRC_SE_SHIFT;
+
+ dev_dbg(madera->dev, "mux=%u reg=0x%x inmode=0x%x mask=0x%x val=0x%x\n",
+ mux, e->reg, inmode, mask, val);
+
+ ret = regmap_update_bits_check(regmap, e->reg, mask, val, &changed);
+ if (ret < 0)
+ return ret;
+
+ if (changed)
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ mux, e, NULL);
+ else
+ return 0;
+}
+
+static const char * const madera_inmux_texts[] = {
+ "A",
+ "B",
+};
+
+static SOC_ENUM_SINGLE_DECL(madera_in1muxl_enum,
+ MADERA_ADC_DIGITAL_VOLUME_1L,
+ MADERA_IN1L_SRC_SHIFT,
+ madera_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in1muxr_enum,
+ MADERA_ADC_DIGITAL_VOLUME_1R,
+ MADERA_IN1R_SRC_SHIFT,
+ madera_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in2muxl_enum,
+ MADERA_ADC_DIGITAL_VOLUME_2L,
+ MADERA_IN2L_SRC_SHIFT,
+ madera_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in2muxr_enum,
+ MADERA_ADC_DIGITAL_VOLUME_2R,
+ MADERA_IN2R_SRC_SHIFT,
+ madera_inmux_texts);
+
+const struct snd_kcontrol_new madera_inmux[] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", madera_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", madera_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN2L Mux", madera_in2muxl_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN2R Mux", madera_in2muxr_enum,
+ snd_soc_dapm_get_enum_double, madera_inmux_put),
+};
+EXPORT_SYMBOL_GPL(madera_inmux);
+
+static const char * const madera_dmode_texts[] = {
+ "Analog",
+ "Digital",
+};
+
+static SOC_ENUM_SINGLE_DECL(madera_in1dmode_enum,
+ MADERA_IN1L_CONTROL,
+ MADERA_IN1_MODE_SHIFT,
+ madera_dmode_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in2dmode_enum,
+ MADERA_IN2L_CONTROL,
+ MADERA_IN2_MODE_SHIFT,
+ madera_dmode_texts);
+
+static SOC_ENUM_SINGLE_DECL(madera_in3dmode_enum,
+ MADERA_IN3L_CONTROL,
+ MADERA_IN3_MODE_SHIFT,
+ madera_dmode_texts);
+
+const struct snd_kcontrol_new madera_inmode[] = {
+ SOC_DAPM_ENUM("IN1 Mode", madera_in1dmode_enum),
+ SOC_DAPM_ENUM("IN2 Mode", madera_in2dmode_enum),
+ SOC_DAPM_ENUM("IN3 Mode", madera_in3dmode_enum),
+};
+EXPORT_SYMBOL_GPL(madera_inmode);
+
+static bool madera_can_change_grp_rate(const struct madera_priv *priv,
+ unsigned int reg)
+{
+ int count;
+
+ switch (reg) {
+ case MADERA_FX_CTRL1:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_FX];
+ break;
+ case MADERA_ASRC1_RATE1:
+ case MADERA_ASRC1_RATE2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC1];
+ break;
+ case MADERA_ASRC2_RATE1:
+ case MADERA_ASRC2_RATE2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC2];
+ break;
+ case MADERA_ISRC_1_CTRL_1:
+ case MADERA_ISRC_1_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC1];
+ break;
+ case MADERA_ISRC_2_CTRL_1:
+ case MADERA_ISRC_2_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC2];
+ break;
+ case MADERA_ISRC_3_CTRL_1:
+ case MADERA_ISRC_3_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC3];
+ break;
+ case MADERA_ISRC_4_CTRL_1:
+ case MADERA_ISRC_4_CTRL_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC4];
+ break;
+ case MADERA_OUTPUT_RATE_1:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_OUT];
+ break;
+ case MADERA_SPD1_TX_CONTROL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_SPD];
+ break;
+ case MADERA_DSP1_CONFIG_1:
+ case MADERA_DSP1_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP1];
+ break;
+ case MADERA_DSP2_CONFIG_1:
+ case MADERA_DSP2_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP2];
+ break;
+ case MADERA_DSP3_CONFIG_1:
+ case MADERA_DSP3_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP3];
+ break;
+ case MADERA_DSP4_CONFIG_1:
+ case MADERA_DSP4_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP4];
+ break;
+ case MADERA_DSP5_CONFIG_1:
+ case MADERA_DSP5_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP5];
+ break;
+ case MADERA_DSP6_CONFIG_1:
+ case MADERA_DSP6_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP6];
+ break;
+ case MADERA_DSP7_CONFIG_1:
+ case MADERA_DSP7_CONFIG_2:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_DSP7];
+ break;
+ case MADERA_AIF1_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF1];
+ break;
+ case MADERA_AIF2_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF2];
+ break;
+ case MADERA_AIF3_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF3];
+ break;
+ case MADERA_AIF4_RATE_CTRL:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_AIF4];
+ break;
+ case MADERA_SLIMBUS_RATES_1:
+ case MADERA_SLIMBUS_RATES_2:
+ case MADERA_SLIMBUS_RATES_3:
+ case MADERA_SLIMBUS_RATES_4:
+ case MADERA_SLIMBUS_RATES_5:
+ case MADERA_SLIMBUS_RATES_6:
+ case MADERA_SLIMBUS_RATES_7:
+ case MADERA_SLIMBUS_RATES_8:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_SLIMBUS];
+ break;
+ case MADERA_PWM_DRIVE_1:
+ count = priv->domain_group_ref[MADERA_DOM_GRP_PWM];
+ break;
+ default:
+ return false;
+ }
+
+ dev_dbg(priv->madera->dev, "Rate reg 0x%x group ref %d\n", reg, count);
+
+ if (count)
+ return false;
+ else
+ return true;
+}
+
+static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int cached_rate;
+ const int adsp_num = e->shift_l;
+ int item;
+
+ mutex_lock(&priv->rate_lock);
+ cached_rate = priv->adsp_rate_cache[adsp_num];
+ mutex_unlock(&priv->rate_lock);
+
+ item = snd_soc_enum_val_to_item(e, cached_rate);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ const int adsp_num = e->shift_l;
+ const unsigned int item = ucontrol->value.enumerated.item[0];
+ int ret = 0;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ /*
+ * We don't directly write the rate register here but we want to
+ * maintain consistent behaviour that rate domains cannot be changed
+ * while in use since this is a hardware requirement
+ */
+ mutex_lock(&priv->rate_lock);
+
+ if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) {
+ dev_warn(priv->madera->dev,
+ "Cannot change '%s' while in use by active audio paths\n",
+ kcontrol->id.name);
+ ret = -EBUSY;
+ } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) {
+ /* Volatile register so defer until the codec is powered up */
+ priv->adsp_rate_cache[adsp_num] = e->values[item];
+ ret = 1;
+ }
+
+ mutex_unlock(&priv->rate_lock);
+
+ return ret;
+}
+
+static const struct soc_enum madera_adsp_rate_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 2, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 3, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 4, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 5, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 6, 0xf, MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+
+const struct snd_kcontrol_new madera_adsp_rate_controls[] = {
+ SOC_ENUM_EXT("DSP1 Rate", madera_adsp_rate_enum[0],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP2 Rate", madera_adsp_rate_enum[1],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP3 Rate", madera_adsp_rate_enum[2],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP4 Rate", madera_adsp_rate_enum[3],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP5 Rate", madera_adsp_rate_enum[4],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP6 Rate", madera_adsp_rate_enum[5],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+ SOC_ENUM_EXT("DSP7 Rate", madera_adsp_rate_enum[6],
+ madera_adsp_rate_get, madera_adsp_rate_put),
+};
+EXPORT_SYMBOL_GPL(madera_adsp_rate_controls);
+
+static int madera_write_adsp_clk_setting(struct madera_priv *priv,
+ struct wm_adsp *dsp,
+ unsigned int freq)
+{
+ unsigned int val;
+ unsigned int mask = MADERA_DSP_RATE_MASK;
+ int ret;
+
+ val = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
+
+ switch (priv->madera->type) {
+ case CS47L35:
+ case CS47L85:
+ case WM1840:
+ /* use legacy frequency registers */
+ mask |= MADERA_DSP_CLK_SEL_MASK;
+ val |= (freq << MADERA_DSP_CLK_SEL_SHIFT);
+ break;
+ default:
+ /* Configure exact dsp frequency */
+ dev_dbg(priv->madera->dev, "Set DSP frequency to 0x%x\n", freq);
+
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + MADERA_DSP_CONFIG_2_OFFS, freq);
+ if (ret)
+ goto err;
+ break;
+ }
+
+ ret = regmap_update_bits(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + MADERA_DSP_CONFIG_1_OFFS,
+ mask, val);
+ if (ret)
+ goto err;
+
+ dev_dbg(priv->madera->dev, "Set DSP clocking to 0x%x\n", val);
+
+ return 0;
+
+err:
+ dev_err(dsp->cs_dsp.dev, "Failed to set DSP%d clock: %d\n", dsp->cs_dsp.num, ret);
+
+ return ret;
+}
+
+int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
+ unsigned int freq)
+{
+ struct wm_adsp *dsp = &priv->adsp[dsp_num];
+ struct madera *madera = priv->madera;
+ unsigned int cur, new;
+ int ret;
+
+ /*
+ * This is called at a higher DAPM priority than the mux widgets so
+ * the muxes are still off at this point and it's safe to change
+ * the rate domain control.
+ * Also called at a lower DAPM priority than the domain group widgets
+ * so locking the reads of adsp_rate_cache is not necessary as we know
+ * changes are locked out by the domain_group_ref reference count.
+ */
+
+ ret = regmap_read(dsp->cs_dsp.regmap, dsp->cs_dsp.base, &cur);
+ if (ret) {
+ dev_err(madera->dev,
+ "Failed to read current DSP rate: %d\n", ret);
+ return ret;
+ }
+
+ cur &= MADERA_DSP_RATE_MASK;
+
+ new = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT;
+
+ if (new == cur) {
+ dev_dbg(madera->dev, "DSP rate not changed\n");
+ return madera_write_adsp_clk_setting(priv, dsp, freq);
+ } else {
+ dev_dbg(madera->dev, "DSP rate changed\n");
+
+ /* The write must be guarded by a number of SYSCLK cycles */
+ madera_spin_sysclk(priv);
+ ret = madera_write_adsp_clk_setting(priv, dsp, freq);
+ madera_spin_sysclk(priv);
+ return ret;
+ }
+}
+EXPORT_SYMBOL_GPL(madera_set_adsp_clk);
+
+int madera_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ int ret;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ /*
+ * Prevent the domain powering up while we're checking whether it's
+ * safe to change rate domain
+ */
+ mutex_lock(&priv->rate_lock);
+
+ val = snd_soc_component_read(component, e->reg);
+ val >>= e->shift_l;
+ val &= e->mask;
+ if (snd_soc_enum_item_to_val(e, item) == val) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!madera_can_change_grp_rate(priv, e->reg)) {
+ dev_warn(priv->madera->dev,
+ "Cannot change '%s' while in use by active audio paths\n",
+ kcontrol->id.name);
+ ret = -EBUSY;
+ } else {
+ /* The write must be guarded by a number of SYSCLK cycles */
+ madera_spin_sysclk(priv);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ madera_spin_sysclk(priv);
+ }
+out:
+ mutex_unlock(&priv->rate_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_rate_put);
+
+static void madera_configure_input_mode(struct madera *madera)
+{
+ unsigned int dig_mode, ana_mode_l, ana_mode_r;
+ int max_analogue_inputs, max_dmic_sup, i;
+
+ switch (madera->type) {
+ case CS47L15:
+ max_analogue_inputs = 1;
+ max_dmic_sup = 2;
+ break;
+ case CS47L35:
+ max_analogue_inputs = 2;
+ max_dmic_sup = 2;
+ break;
+ case CS47L85:
+ case WM1840:
+ max_analogue_inputs = 3;
+ max_dmic_sup = 3;
+ break;
+ case CS47L90:
+ case CS47L91:
+ max_analogue_inputs = 2;
+ max_dmic_sup = 2;
+ break;
+ default:
+ max_analogue_inputs = 2;
+ max_dmic_sup = 4;
+ break;
+ }
+
+ /*
+ * Initialize input modes from the A settings. For muxed inputs the
+ * B settings will be applied if the mux is changed
+ */
+ for (i = 0; i < max_dmic_sup; i++) {
+ dev_dbg(madera->dev, "IN%d mode %u:%u:%u:%u\n", i + 1,
+ madera->pdata.codec.inmode[i][0],
+ madera->pdata.codec.inmode[i][1],
+ madera->pdata.codec.inmode[i][2],
+ madera->pdata.codec.inmode[i][3]);
+
+ dig_mode = madera->pdata.codec.dmic_ref[i] <<
+ MADERA_IN1_DMIC_SUP_SHIFT;
+
+ switch (madera->pdata.codec.inmode[i][0]) {
+ case MADERA_INMODE_DIFF:
+ ana_mode_l = 0;
+ break;
+ case MADERA_INMODE_SE:
+ ana_mode_l = 1 << MADERA_IN1L_SRC_SE_SHIFT;
+ break;
+ default:
+ dev_warn(madera->dev,
+ "IN%dAL Illegal inmode %u ignored\n",
+ i + 1, madera->pdata.codec.inmode[i][0]);
+ continue;
+ }
+
+ switch (madera->pdata.codec.inmode[i][1]) {
+ case MADERA_INMODE_DIFF:
+ ana_mode_r = 0;
+ break;
+ case MADERA_INMODE_SE:
+ ana_mode_r = 1 << MADERA_IN1R_SRC_SE_SHIFT;
+ break;
+ default:
+ dev_warn(madera->dev,
+ "IN%dAR Illegal inmode %u ignored\n",
+ i + 1, madera->pdata.codec.inmode[i][1]);
+ continue;
+ }
+
+ dev_dbg(madera->dev,
+ "IN%dA DMIC mode=0x%x Analogue mode=0x%x,0x%x\n",
+ i + 1, dig_mode, ana_mode_l, ana_mode_r);
+
+ regmap_update_bits(madera->regmap,
+ MADERA_IN1L_CONTROL + (i * 8),
+ MADERA_IN1_DMIC_SUP_MASK, dig_mode);
+
+ if (i >= max_analogue_inputs)
+ continue;
+
+ regmap_update_bits(madera->regmap,
+ MADERA_ADC_DIGITAL_VOLUME_1L + (i * 8),
+ MADERA_IN1L_SRC_SE_MASK, ana_mode_l);
+
+ regmap_update_bits(madera->regmap,
+ MADERA_ADC_DIGITAL_VOLUME_1R + (i * 8),
+ MADERA_IN1R_SRC_SE_MASK, ana_mode_r);
+ }
+}
+
+int madera_init_inputs(struct snd_soc_component *component)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+
+ madera_configure_input_mode(madera);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_inputs);
+
+static const struct snd_soc_dapm_route madera_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3R", NULL, "OUT3L" },
+ { "OUT4R", NULL, "OUT4L" },
+ { "OUT5R", NULL, "OUT5L" },
+ { "OUT6R", NULL, "OUT6L" },
+};
+
+int madera_init_outputs(struct snd_soc_component *component,
+ const struct snd_soc_dapm_route *routes,
+ int n_mono_routes, int n_real)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ const struct madera_codec_pdata *pdata = &madera->pdata.codec;
+ unsigned int val;
+ int i;
+
+ if (n_mono_routes > MADERA_MAX_OUTPUT) {
+ dev_warn(madera->dev,
+ "Requested %d mono outputs, using maximum allowed %d\n",
+ n_mono_routes, MADERA_MAX_OUTPUT);
+ n_mono_routes = MADERA_MAX_OUTPUT;
+ }
+
+ if (!routes)
+ routes = madera_mono_routes;
+
+ for (i = 0; i < n_mono_routes; i++) {
+ /* Default is 0 so noop with defaults */
+ if (pdata->out_mono[i]) {
+ val = MADERA_OUT1_MONO;
+ snd_soc_dapm_add_routes(dapm, &routes[i], 1);
+ } else {
+ val = 0;
+ }
+
+ if (i >= n_real)
+ continue;
+
+ regmap_update_bits(madera->regmap,
+ MADERA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+ MADERA_OUT1_MONO, val);
+
+ dev_dbg(madera->dev, "OUT%d mono=0x%x\n", i + 1, val);
+ }
+
+ for (i = 0; i < MADERA_MAX_PDM_SPK; i++) {
+ dev_dbg(madera->dev, "PDM%d fmt=0x%x mute=0x%x\n", i + 1,
+ pdata->pdm_fmt[i], pdata->pdm_mute[i]);
+
+ if (pdata->pdm_mute[i])
+ regmap_update_bits(madera->regmap,
+ MADERA_PDM_SPK1_CTRL_1 + (i * 2),
+ MADERA_SPK1_MUTE_ENDIAN_MASK |
+ MADERA_SPK1_MUTE_SEQ1_MASK,
+ pdata->pdm_mute[i]);
+
+ if (pdata->pdm_fmt[i])
+ regmap_update_bits(madera->regmap,
+ MADERA_PDM_SPK1_CTRL_2 + (i * 2),
+ MADERA_SPK1_FMT_MASK,
+ pdata->pdm_fmt[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_outputs);
+
+int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num,
+ irq_handler_t handler)
+{
+ struct madera *madera = priv->madera;
+ int ret;
+
+ ret = madera_request_irq(madera,
+ madera_dsp_bus_error_irqs[dsp_num],
+ "ADSP2 bus error",
+ handler,
+ &priv->adsp[dsp_num]);
+ if (ret)
+ dev_err(madera->dev,
+ "Failed to request DSP Lock region IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_init_bus_error_irq);
+
+void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num)
+{
+ struct madera *madera = priv->madera;
+
+ madera_free_irq(madera,
+ madera_dsp_bus_error_irqs[dsp_num],
+ &priv->adsp[dsp_num]);
+}
+EXPORT_SYMBOL_GPL(madera_free_bus_error_irq);
+
+const char * const madera_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Haptics",
+ "AEC1",
+ "AEC2",
+ "Mic Mute Mixer",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "IN3L",
+ "IN3R",
+ "IN4L",
+ "IN4R",
+ "IN5L",
+ "IN5R",
+ "IN6L",
+ "IN6R",
+ "AIF1RX1",
+ "AIF1RX2",
+ "AIF1RX3",
+ "AIF1RX4",
+ "AIF1RX5",
+ "AIF1RX6",
+ "AIF1RX7",
+ "AIF1RX8",
+ "AIF2RX1",
+ "AIF2RX2",
+ "AIF2RX3",
+ "AIF2RX4",
+ "AIF2RX5",
+ "AIF2RX6",
+ "AIF2RX7",
+ "AIF2RX8",
+ "AIF3RX1",
+ "AIF3RX2",
+ "AIF3RX3",
+ "AIF3RX4",
+ "AIF4RX1",
+ "AIF4RX2",
+ "SLIMRX1",
+ "SLIMRX2",
+ "SLIMRX3",
+ "SLIMRX4",
+ "SLIMRX5",
+ "SLIMRX6",
+ "SLIMRX7",
+ "SLIMRX8",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+ "DSP3.1",
+ "DSP3.2",
+ "DSP3.3",
+ "DSP3.4",
+ "DSP3.5",
+ "DSP3.6",
+ "DSP4.1",
+ "DSP4.2",
+ "DSP4.3",
+ "DSP4.4",
+ "DSP4.5",
+ "DSP4.6",
+ "DSP5.1",
+ "DSP5.2",
+ "DSP5.3",
+ "DSP5.4",
+ "DSP5.5",
+ "DSP5.6",
+ "DSP6.1",
+ "DSP6.2",
+ "DSP6.3",
+ "DSP6.4",
+ "DSP6.5",
+ "DSP6.6",
+ "DSP7.1",
+ "DSP7.2",
+ "DSP7.3",
+ "DSP7.4",
+ "DSP7.5",
+ "DSP7.6",
+ "ASRC1IN1L",
+ "ASRC1IN1R",
+ "ASRC1IN2L",
+ "ASRC1IN2R",
+ "ASRC2IN1L",
+ "ASRC2IN1R",
+ "ASRC2IN2L",
+ "ASRC2IN2R",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2INT3",
+ "ISRC2INT4",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC2DEC3",
+ "ISRC2DEC4",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3INT3",
+ "ISRC3INT4",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "ISRC3DEC3",
+ "ISRC3DEC4",
+ "ISRC4INT1",
+ "ISRC4INT2",
+ "ISRC4DEC1",
+ "ISRC4DEC2",
+ "DFC1",
+ "DFC2",
+ "DFC3",
+ "DFC4",
+ "DFC5",
+ "DFC6",
+ "DFC7",
+ "DFC8",
+};
+EXPORT_SYMBOL_GPL(madera_mixer_texts);
+
+const unsigned int madera_mixer_values[] = {
+ 0x00, /* None */
+ 0x04, /* Tone Generator 1 */
+ 0x05, /* Tone Generator 2 */
+ 0x06, /* Haptics */
+ 0x08, /* AEC */
+ 0x09, /* AEC2 */
+ 0x0c, /* Noise mixer */
+ 0x0d, /* Comfort noise */
+ 0x10, /* IN1L */
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0x1A,
+ 0x1B,
+ 0x20, /* AIF1RX1 */
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28, /* AIF2RX1 */
+ 0x29,
+ 0x2a,
+ 0x2b,
+ 0x2c,
+ 0x2d,
+ 0x2e,
+ 0x2f,
+ 0x30, /* AIF3RX1 */
+ 0x31,
+ 0x32,
+ 0x33,
+ 0x34, /* AIF4RX1 */
+ 0x35,
+ 0x38, /* SLIMRX1 */
+ 0x39,
+ 0x3a,
+ 0x3b,
+ 0x3c,
+ 0x3d,
+ 0x3e,
+ 0x3f,
+ 0x50, /* EQ1 */
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x58, /* DRC1L */
+ 0x59,
+ 0x5a,
+ 0x5b,
+ 0x60, /* LHPF1 */
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x68, /* DSP1.1 */
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x70, /* DSP2.1 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+ 0x78, /* DSP3.1 */
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7c,
+ 0x7d,
+ 0x80, /* DSP4.1 */
+ 0x81,
+ 0x82,
+ 0x83,
+ 0x84,
+ 0x85,
+ 0x88, /* DSP5.1 */
+ 0x89,
+ 0x8a,
+ 0x8b,
+ 0x8c,
+ 0x8d,
+ 0xc0, /* DSP6.1 */
+ 0xc1,
+ 0xc2,
+ 0xc3,
+ 0xc4,
+ 0xc5,
+ 0xc8, /* DSP7.1 */
+ 0xc9,
+ 0xca,
+ 0xcb,
+ 0xcc,
+ 0xcd,
+ 0x90, /* ASRC1IN1L */
+ 0x91,
+ 0x92,
+ 0x93,
+ 0x94, /* ASRC2IN1L */
+ 0x95,
+ 0x96,
+ 0x97,
+ 0xa0, /* ISRC1INT1 */
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4, /* ISRC1DEC1 */
+ 0xa5,
+ 0xa6,
+ 0xa7,
+ 0xa8, /* ISRC2DEC1 */
+ 0xa9,
+ 0xaa,
+ 0xab,
+ 0xac, /* ISRC2INT1 */
+ 0xad,
+ 0xae,
+ 0xaf,
+ 0xb0, /* ISRC3DEC1 */
+ 0xb1,
+ 0xb2,
+ 0xb3,
+ 0xb4, /* ISRC3INT1 */
+ 0xb5,
+ 0xb6,
+ 0xb7,
+ 0xb8, /* ISRC4INT1 */
+ 0xb9,
+ 0xbc, /* ISRC4DEC1 */
+ 0xbd,
+ 0xf8, /* DFC1 */
+ 0xf9,
+ 0xfa,
+ 0xfb,
+ 0xfc,
+ 0xfd,
+ 0xfe,
+ 0xff, /* DFC8 */
+};
+EXPORT_SYMBOL_GPL(madera_mixer_values);
+
+const DECLARE_TLV_DB_SCALE(madera_ana_tlv, 0, 100, 0);
+EXPORT_SYMBOL_GPL(madera_ana_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_eq_tlv, -1200, 100, 0);
+EXPORT_SYMBOL_GPL(madera_eq_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_digital_tlv, -6400, 50, 0);
+EXPORT_SYMBOL_GPL(madera_digital_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_noise_tlv, -13200, 600, 0);
+EXPORT_SYMBOL_GPL(madera_noise_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_ng_tlv, -12000, 600, 0);
+EXPORT_SYMBOL_GPL(madera_ng_tlv);
+
+const DECLARE_TLV_DB_SCALE(madera_mixer_tlv, -3200, 100, 0);
+EXPORT_SYMBOL_GPL(madera_mixer_tlv);
+
+const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE] = {
+ "SYNCCLK rate 1", "SYNCCLK rate 2", "SYNCCLK rate 3",
+ "ASYNCCLK rate 1", "ASYNCCLK rate 2",
+};
+EXPORT_SYMBOL_GPL(madera_rate_text);
+
+const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE] = {
+ 0x0, 0x1, 0x2, 0x8, 0x9,
+};
+EXPORT_SYMBOL_GPL(madera_rate_val);
+
+static const char * const madera_dfc_width_text[MADERA_DFC_WIDTH_ENUM_SIZE] = {
+ "8 bit", "16 bit", "20 bit", "24 bit", "32 bit",
+};
+
+static const unsigned int madera_dfc_width_val[MADERA_DFC_WIDTH_ENUM_SIZE] = {
+ 7, 15, 19, 23, 31,
+};
+
+static const char * const madera_dfc_type_text[MADERA_DFC_TYPE_ENUM_SIZE] = {
+ "Fixed", "Unsigned Fixed", "Single Precision Floating",
+ "Half Precision Floating", "Arm Alternative Floating",
+};
+
+static const unsigned int madera_dfc_type_val[MADERA_DFC_TYPE_ENUM_SIZE] = {
+ 0, 1, 2, 4, 5,
+};
+
+const struct soc_enum madera_dfc_width[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX,
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_RX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_RX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX,
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ MADERA_DFC1_TX_DATA_WIDTH_MASK >>
+ MADERA_DFC1_TX_DATA_WIDTH_SHIFT,
+ ARRAY_SIZE(madera_dfc_width_text),
+ madera_dfc_width_text,
+ madera_dfc_width_val),
+};
+EXPORT_SYMBOL_GPL(madera_dfc_width);
+
+const struct soc_enum madera_dfc_type[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX,
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_RX_DATA_TYPE_MASK >>
+ MADERA_DFC1_RX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX,
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ MADERA_DFC1_TX_DATA_TYPE_MASK >>
+ MADERA_DFC1_TX_DATA_TYPE_SHIFT,
+ ARRAY_SIZE(madera_dfc_type_text),
+ madera_dfc_type_text,
+ madera_dfc_type_val),
+};
+EXPORT_SYMBOL_GPL(madera_dfc_type);
+
+const struct soc_enum madera_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_1,
+ MADERA_ISRC1_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_1,
+ MADERA_ISRC2_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_1,
+ MADERA_ISRC3_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_1,
+ MADERA_ISRC4_FSH_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+EXPORT_SYMBOL_GPL(madera_isrc_fsh);
+
+const struct soc_enum madera_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_2,
+ MADERA_ISRC1_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_2,
+ MADERA_ISRC2_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_2,
+ MADERA_ISRC3_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_2,
+ MADERA_ISRC4_FSL_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+EXPORT_SYMBOL_GPL(madera_isrc_fsl);
+
+const struct soc_enum madera_asrc1_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
+ MADERA_ASRC1_RATE1_SHIFT, 0xf,
+ MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
+ MADERA_ASRC1_RATE1_SHIFT, 0xf,
+ MADERA_ASYNC_RATE_ENUM_SIZE,
+ madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
+};
+EXPORT_SYMBOL_GPL(madera_asrc1_rate);
+
+const struct soc_enum madera_asrc1_bidir_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
+ MADERA_ASRC1_RATE1_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
+ MADERA_ASRC1_RATE2_SHIFT, 0xf,
+ MADERA_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+};
+EXPORT_SYMBOL_GPL(madera_asrc1_bidir_rate);
+
+const struct soc_enum madera_asrc2_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE1,
+ MADERA_ASRC2_RATE1_SHIFT, 0xf,
+ MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_text, madera_rate_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE2,
+ MADERA_ASRC2_RATE2_SHIFT, 0xf,
+ MADERA_ASYNC_RATE_ENUM_SIZE,
+ madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
+ madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
+};
+EXPORT_SYMBOL_GPL(madera_asrc2_rate);
+
+static const char * const madera_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+SOC_ENUM_SINGLE_DECL(madera_in_vd_ramp,
+ MADERA_INPUT_VOLUME_RAMP,
+ MADERA_IN_VD_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_in_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(madera_in_vi_ramp,
+ MADERA_INPUT_VOLUME_RAMP,
+ MADERA_IN_VI_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_in_vi_ramp);
+
+SOC_ENUM_SINGLE_DECL(madera_out_vd_ramp,
+ MADERA_OUTPUT_VOLUME_RAMP,
+ MADERA_OUT_VD_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_out_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(madera_out_vi_ramp,
+ MADERA_OUTPUT_VOLUME_RAMP,
+ MADERA_OUT_VI_RAMP_SHIFT,
+ madera_vol_ramp_text);
+EXPORT_SYMBOL_GPL(madera_out_vi_ramp);
+
+static const char * const madera_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf1_mode,
+ MADERA_HPLPF1_1,
+ MADERA_LHPF1_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf1_mode);
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf2_mode,
+ MADERA_HPLPF2_1,
+ MADERA_LHPF2_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf2_mode);
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf3_mode,
+ MADERA_HPLPF3_1,
+ MADERA_LHPF3_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf3_mode);
+
+SOC_ENUM_SINGLE_DECL(madera_lhpf4_mode,
+ MADERA_HPLPF4_1,
+ MADERA_LHPF4_MODE_SHIFT,
+ madera_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(madera_lhpf4_mode);
+
+static const char * const madera_ng_hold_text[] = {
+ "30ms", "120ms", "250ms", "500ms",
+};
+
+SOC_ENUM_SINGLE_DECL(madera_ng_hold,
+ MADERA_NOISE_GATE_CONTROL,
+ MADERA_NGATE_HOLD_SHIFT,
+ madera_ng_hold_text);
+EXPORT_SYMBOL_GPL(madera_ng_hold);
+
+static const char * const madera_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+SOC_ENUM_SINGLE_DECL(madera_in_hpf_cut_enum,
+ MADERA_HPF_CONTROL,
+ MADERA_IN_HPF_CUT_SHIFT,
+ madera_in_hpf_cut_text);
+EXPORT_SYMBOL_GPL(madera_in_hpf_cut_enum);
+
+static const char * const madera_in_dmic_osr_text[MADERA_OSR_ENUM_SIZE] = {
+ "384kHz", "768kHz", "1.536MHz", "3.072MHz", "6.144MHz",
+};
+
+static const unsigned int madera_in_dmic_osr_val[MADERA_OSR_ENUM_SIZE] = {
+ 2, 3, 4, 5, 6,
+};
+
+const struct soc_enum madera_in_dmic_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC1L_CONTROL, MADERA_IN1_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC2L_CONTROL, MADERA_IN2_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC3L_CONTROL, MADERA_IN3_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC4L_CONTROL, MADERA_IN4_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC5L_CONTROL, MADERA_IN5_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+ SOC_VALUE_ENUM_SINGLE(MADERA_DMIC6L_CONTROL, MADERA_IN6_OSR_SHIFT,
+ 0x7, MADERA_OSR_ENUM_SIZE,
+ madera_in_dmic_osr_text, madera_in_dmic_osr_val),
+};
+EXPORT_SYMBOL_GPL(madera_in_dmic_osr);
+
+static const char * const madera_anc_input_src_text[] = {
+ "None", "IN1", "IN2", "IN3", "IN4", "IN5", "IN6",
+};
+
+static const char * const madera_anc_channel_src_text[] = {
+ "None", "Left", "Right", "Combine",
+};
+
+const struct soc_enum madera_anc_input_src[] = {
+ SOC_ENUM_SINGLE(MADERA_ANC_SRC,
+ MADERA_IN_RXANCL_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_input_src_text),
+ madera_anc_input_src_text),
+ SOC_ENUM_SINGLE(MADERA_FCL_ADC_REFORMATTER_CONTROL,
+ MADERA_FCL_MIC_MODE_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_channel_src_text),
+ madera_anc_channel_src_text),
+ SOC_ENUM_SINGLE(MADERA_ANC_SRC,
+ MADERA_IN_RXANCR_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_input_src_text),
+ madera_anc_input_src_text),
+ SOC_ENUM_SINGLE(MADERA_FCR_ADC_REFORMATTER_CONTROL,
+ MADERA_FCR_MIC_MODE_SEL_SHIFT,
+ ARRAY_SIZE(madera_anc_channel_src_text),
+ madera_anc_channel_src_text),
+};
+EXPORT_SYMBOL_GPL(madera_anc_input_src);
+
+static const char * const madera_anc_ng_texts[] = {
+ "None", "Internal", "External",
+};
+
+SOC_ENUM_SINGLE_DECL(madera_anc_ng_enum, SND_SOC_NOPM, 0, madera_anc_ng_texts);
+EXPORT_SYMBOL_GPL(madera_anc_ng_enum);
+
+static const char * const madera_out_anc_src_text[] = {
+ "None", "RXANCL", "RXANCR",
+};
+
+const struct soc_enum madera_output_anc_src[] = {
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1L,
+ MADERA_OUT1L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1R,
+ MADERA_OUT1R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2L,
+ MADERA_OUT2L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2R,
+ MADERA_OUT2R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3L,
+ MADERA_OUT3L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3R,
+ MADERA_OUT3R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4L,
+ MADERA_OUT4L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4R,
+ MADERA_OUT4R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5L,
+ MADERA_OUT5L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5R,
+ MADERA_OUT5R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6L,
+ MADERA_OUT6L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+ SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6R,
+ MADERA_OUT6R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(madera_out_anc_src_text),
+ madera_out_anc_src_text),
+};
+EXPORT_SYMBOL_GPL(madera_output_anc_src);
+
+int madera_dfc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg = e->reg;
+ unsigned int val;
+ int ret = 0;
+
+ reg = ((reg / 6) * 6) - 2;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ val = snd_soc_component_read(component, reg);
+ if (val & MADERA_DFC1_ENA) {
+ ret = -EBUSY;
+ dev_err(component->dev, "Can't change mode on an active DFC\n");
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_dfc_put);
+
+int madera_lp_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int val, mask;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change lp mode on an active input */
+ val = snd_soc_component_read(component, MADERA_INPUT_ENABLES);
+ mask = (mc->reg - MADERA_ADC_DIGITAL_VOLUME_1L) / 4;
+ mask ^= 0x1; /* Flip bottom bit for channel order */
+
+ if (val & (1 << mask)) {
+ ret = -EBUSY;
+ dev_err(component->dev,
+ "Can't change lp mode on an active input\n");
+ goto exit;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_lp_mode_put);
+
+const struct snd_kcontrol_new madera_dsp_trigger_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+EXPORT_SYMBOL_GPL(madera_dsp_trigger_output_mux);
+
+const struct snd_kcontrol_new madera_drc_activity_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+EXPORT_SYMBOL_GPL(madera_drc_activity_output_mux);
+
+static void madera_in_set_vu(struct madera_priv *priv, bool enable)
+{
+ unsigned int val;
+ int i, ret;
+
+ if (enable)
+ val = MADERA_IN_VU;
+ else
+ val = 0;
+
+ for (i = 0; i < priv->num_inputs; i++) {
+ ret = regmap_update_bits(priv->madera->regmap,
+ MADERA_ADC_DIGITAL_VOLUME_1L + (i * 4),
+ MADERA_IN_VU, val);
+ if (ret)
+ dev_warn(priv->madera->dev,
+ "Failed to modify VU bits: %d\n", ret);
+ }
+}
+
+int madera_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int reg, val;
+
+ if (w->shift % 2)
+ reg = MADERA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
+ else
+ reg = MADERA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->in_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ priv->in_pending--;
+ snd_soc_component_update_bits(component, reg,
+ MADERA_IN1L_MUTE, 0);
+
+ /* If this is the last input pending then allow VU */
+ if (priv->in_pending == 0) {
+ usleep_range(1000, 3000);
+ madera_in_set_vu(priv, true);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ MADERA_IN1L_MUTE | MADERA_IN_VU,
+ MADERA_IN1L_MUTE | MADERA_IN_VU);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable volume updates if no inputs are enabled */
+ val = snd_soc_component_read(component, MADERA_INPUT_ENABLES);
+ if (!val)
+ madera_in_set_vu(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_in_ev);
+
+int madera_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int out_up_delay;
+
+ switch (madera->type) {
+ case CS47L90:
+ case CS47L91:
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ out_up_delay = 6000;
+ break;
+ default:
+ out_up_delay = 17000;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending++;
+ priv->out_up_delay += out_up_delay;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending--;
+ if (!priv->out_up_pending) {
+ fsleep(priv->out_up_delay);
+ priv->out_up_delay = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending++;
+ priv->out_down_delay += 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->shift) {
+ case MADERA_OUT1L_ENA_SHIFT:
+ case MADERA_OUT1R_ENA_SHIFT:
+ case MADERA_OUT2L_ENA_SHIFT:
+ case MADERA_OUT2R_ENA_SHIFT:
+ case MADERA_OUT3L_ENA_SHIFT:
+ case MADERA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending--;
+ if (!priv->out_down_pending) {
+ fsleep(priv->out_down_delay);
+ priv->out_down_delay = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_out_ev);
+
+int madera_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ unsigned int mask = 1 << w->shift;
+ unsigned int out_num = w->shift / 2;
+ unsigned int val;
+ unsigned int ep_sel = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = mask;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return madera_out_ev(w, kcontrol, event);
+ default:
+ return 0;
+ }
+
+ /* Store the desired state for the HP outputs */
+ madera->hp_ena &= ~mask;
+ madera->hp_ena |= val;
+
+ switch (madera->type) {
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ break;
+ default:
+ /* if OUT1 is routed to EPOUT, ignore HP clamp and impedance */
+ regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &ep_sel);
+ ep_sel &= MADERA_EP_SEL_MASK;
+ break;
+ }
+
+ /* Force off if HPDET has disabled the clamp for this output */
+ if (!ep_sel &&
+ (!madera->out_clamp[out_num] || madera->out_shorted[out_num]))
+ val = 0;
+
+ regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, mask, val);
+
+ return madera_out_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(madera_hp_ev);
+
+int madera_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1 << w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 1 << (w->shift + 1);
+ break;
+ default:
+ return 0;
+ }
+
+ snd_soc_component_write(component, MADERA_CLOCK_CONTROL, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_anc_ev);
+
+static const unsigned int madera_opclk_ref_48k_rates[] = {
+ 6144000,
+ 12288000,
+ 24576000,
+ 49152000,
+};
+
+static const unsigned int madera_opclk_ref_44k1_rates[] = {
+ 5644800,
+ 11289600,
+ 22579200,
+ 45158400,
+};
+
+static int madera_set_opclk(struct snd_soc_component *component,
+ unsigned int clk, unsigned int freq)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int mask = MADERA_OPCLK_DIV_MASK | MADERA_OPCLK_SEL_MASK;
+ unsigned int reg, val;
+ const unsigned int *rates;
+ int ref, div, refclk;
+
+ BUILD_BUG_ON(ARRAY_SIZE(madera_opclk_ref_48k_rates) !=
+ ARRAY_SIZE(madera_opclk_ref_44k1_rates));
+
+ switch (clk) {
+ case MADERA_CLK_OPCLK:
+ reg = MADERA_OUTPUT_SYSTEM_CLOCK;
+ refclk = priv->sysclk;
+ break;
+ case MADERA_CLK_ASYNC_OPCLK:
+ reg = MADERA_OUTPUT_ASYNC_CLOCK;
+ refclk = priv->asyncclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (refclk % 4000)
+ rates = madera_opclk_ref_44k1_rates;
+ else
+ rates = madera_opclk_ref_48k_rates;
+
+ for (ref = 0; ref < ARRAY_SIZE(madera_opclk_ref_48k_rates); ++ref) {
+ if (rates[ref] > refclk)
+ continue;
+
+ div = 2;
+ while ((rates[ref] / div >= freq) && (div <= 30)) {
+ if (rates[ref] / div == freq) {
+ dev_dbg(component->dev, "Configured %dHz OPCLK\n",
+ freq);
+
+ val = (div << MADERA_OPCLK_DIV_SHIFT) | ref;
+
+ snd_soc_component_update_bits(component, reg,
+ mask, val);
+ return 0;
+ }
+ div += 2;
+ }
+ }
+
+ dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq);
+
+ return -EINVAL;
+}
+
+static int madera_get_sysclk_setting(unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ case 5644800:
+ case 6144000:
+ return 0;
+ case 11289600:
+ case 12288000:
+ return MADERA_SYSCLK_12MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ case 22579200:
+ case 24576000:
+ return MADERA_SYSCLK_24MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ case 45158400:
+ case 49152000:
+ return MADERA_SYSCLK_49MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ case 90316800:
+ case 98304000:
+ return MADERA_SYSCLK_98MHZ << MADERA_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int madera_get_legacy_dspclk_setting(struct madera *madera,
+ unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ return 0;
+ case 45158400:
+ case 49152000:
+ switch (madera->type) {
+ case CS47L85:
+ case WM1840:
+ if (madera->rev < 3)
+ return -EINVAL;
+ else
+ return MADERA_SYSCLK_49MHZ <<
+ MADERA_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+ case 135475200:
+ case 147456000:
+ return MADERA_DSPCLK_147MHZ << MADERA_DSP_CLK_FREQ_LEGACY_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int madera_get_dspclk_setting(struct madera *madera,
+ unsigned int freq,
+ unsigned int *clock_2_val)
+{
+ switch (madera->type) {
+ case CS47L35:
+ case CS47L85:
+ case WM1840:
+ *clock_2_val = 0; /* don't use MADERA_DSP_CLOCK_2 */
+ return madera_get_legacy_dspclk_setting(madera, freq);
+ default:
+ if (freq > 150000000)
+ return -EINVAL;
+
+ /* Use new exact frequency control */
+ *clock_2_val = freq / 15625; /* freq * (2^6) / (10^6) */
+ return 0;
+ }
+}
+
+static int madera_set_outclk(struct snd_soc_component *component,
+ unsigned int source, unsigned int freq)
+{
+ int div, div_inc, rate;
+
+ switch (source) {
+ case MADERA_OUTCLK_SYSCLK:
+ dev_dbg(component->dev, "Configured OUTCLK to SYSCLK\n");
+ snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
+ MADERA_OUT_CLK_SRC_MASK, source);
+ return 0;
+ case MADERA_OUTCLK_ASYNCCLK:
+ dev_dbg(component->dev, "Configured OUTCLK to ASYNCCLK\n");
+ snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
+ MADERA_OUT_CLK_SRC_MASK, source);
+ return 0;
+ case MADERA_OUTCLK_MCLK1:
+ case MADERA_OUTCLK_MCLK2:
+ case MADERA_OUTCLK_MCLK3:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq % 4000)
+ rate = 5644800;
+ else
+ rate = 6144000;
+
+ div = 1;
+ div_inc = 0;
+ while (div <= 8) {
+ if (freq / div == rate && !(freq % div)) {
+ dev_dbg(component->dev, "Configured %dHz OUTCLK\n", rate);
+ snd_soc_component_update_bits(component,
+ MADERA_OUTPUT_RATE_1,
+ MADERA_OUT_EXT_CLK_DIV_MASK |
+ MADERA_OUT_CLK_SRC_MASK,
+ (div_inc << MADERA_OUT_EXT_CLK_DIV_SHIFT) |
+ source);
+ return 0;
+ }
+ div_inc++;
+ div *= 2;
+ }
+
+ dev_err(component->dev,
+ "Unable to generate %dHz OUTCLK from %dHz MCLK\n",
+ rate, freq);
+ return -EINVAL;
+}
+
+int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ char *name;
+ unsigned int reg, clock_2_val = 0;
+ unsigned int mask = MADERA_SYSCLK_FREQ_MASK | MADERA_SYSCLK_SRC_MASK;
+ unsigned int val = source << MADERA_SYSCLK_SRC_SHIFT;
+ int clk_freq_sel, *clk;
+ int ret = 0;
+
+ switch (clk_id) {
+ case MADERA_CLK_SYSCLK_1:
+ name = "SYSCLK";
+ reg = MADERA_SYSTEM_CLOCK_1;
+ clk = &priv->sysclk;
+ clk_freq_sel = madera_get_sysclk_setting(freq);
+ mask |= MADERA_SYSCLK_FRAC;
+ break;
+ case MADERA_CLK_ASYNCCLK_1:
+ name = "ASYNCCLK";
+ reg = MADERA_ASYNC_CLOCK_1;
+ clk = &priv->asyncclk;
+ clk_freq_sel = madera_get_sysclk_setting(freq);
+ break;
+ case MADERA_CLK_DSPCLK:
+ name = "DSPCLK";
+ reg = MADERA_DSP_CLOCK_1;
+ clk = &priv->dspclk;
+ clk_freq_sel = madera_get_dspclk_setting(madera, freq,
+ &clock_2_val);
+ break;
+ case MADERA_CLK_OPCLK:
+ case MADERA_CLK_ASYNC_OPCLK:
+ return madera_set_opclk(component, clk_id, freq);
+ case MADERA_CLK_OUTCLK:
+ return madera_set_outclk(component, source, freq);
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_freq_sel < 0) {
+ dev_err(madera->dev,
+ "Failed to get clk setting for %dHZ\n", freq);
+ return clk_freq_sel;
+ }
+
+ *clk = freq;
+
+ if (freq == 0) {
+ dev_dbg(madera->dev, "%s cleared\n", name);
+ return 0;
+ }
+
+ val |= clk_freq_sel;
+
+ if (clock_2_val) {
+ ret = regmap_write(madera->regmap, MADERA_DSP_CLOCK_2,
+ clock_2_val);
+ if (ret) {
+ dev_err(madera->dev,
+ "Failed to write DSP_CONFIG2: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * We're using the frequency setting in MADERA_DSP_CLOCK_2 so
+ * don't change the frequency select bits in MADERA_DSP_CLOCK_1
+ */
+ mask = MADERA_SYSCLK_SRC_MASK;
+ }
+
+ if (freq % 6144000)
+ val |= MADERA_SYSCLK_FRAC;
+
+ dev_dbg(madera->dev, "%s set to %uHz\n", name, freq);
+
+ return regmap_update_bits(madera->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(madera_set_sysclk);
+
+static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int lrclk, bclk, mode, base;
+
+ base = dai->driver->base;
+
+ lrclk = 0;
+ bclk = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = MADERA_FMT_DSP_MODE_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
+ SND_SOC_DAIFMT_CBP_CFP) {
+ madera_aif_err(dai, "DSP_B not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = MADERA_FMT_DSP_MODE_B;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ mode = MADERA_FMT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) !=
+ SND_SOC_DAIFMT_CBP_CFP) {
+ madera_aif_err(dai, "LEFT_J not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = MADERA_FMT_LEFT_JUSTIFIED_MODE;
+ break;
+ default:
+ madera_aif_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ bclk |= MADERA_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ bclk |= MADERA_AIF1_BCLK_MSTR;
+ lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
+ break;
+ default:
+ madera_aif_err(dai, "Unsupported master mode %d\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= MADERA_AIF1_BCLK_INV;
+ lrclk |= MADERA_AIF1TX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= MADERA_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk |= MADERA_AIF1TX_LRCLK_INV;
+ break;
+ default:
+ madera_aif_err(dai, "Unsupported invert mode %d\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_BCLK_CTRL,
+ MADERA_AIF1_BCLK_INV | MADERA_AIF1_BCLK_MSTR,
+ bclk);
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_TX_PIN_CTRL,
+ MADERA_AIF1TX_LRCLK_INV | MADERA_AIF1TX_LRCLK_MSTR,
+ lrclk);
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_RX_PIN_CTRL,
+ MADERA_AIF1RX_LRCLK_INV | MADERA_AIF1RX_LRCLK_MSTR,
+ lrclk);
+ regmap_update_bits(madera->regmap, base + MADERA_AIF_FORMAT,
+ MADERA_AIF1_FMT_MASK, mode);
+
+ return 0;
+}
+
+static const int madera_48k_bclk_rates[] = {
+ -1,
+ 48000,
+ 64000,
+ 96000,
+ 128000,
+ 192000,
+ 256000,
+ 384000,
+ 512000,
+ 768000,
+ 1024000,
+ 1536000,
+ 2048000,
+ 3072000,
+ 4096000,
+ 6144000,
+ 8192000,
+ 12288000,
+ 24576000,
+};
+
+static const int madera_44k1_bclk_rates[] = {
+ -1,
+ 44100,
+ 58800,
+ 88200,
+ 117600,
+ 177640,
+ 235200,
+ 352800,
+ 470400,
+ 705600,
+ 940800,
+ 1411200,
+ 1881600,
+ 2822400,
+ 3763200,
+ 5644800,
+ 7526400,
+ 11289600,
+ 22579200,
+};
+
+static const unsigned int madera_sr_vals[] = {
+ 0,
+ 12000,
+ 24000,
+ 48000,
+ 96000,
+ 192000,
+ 384000,
+ 768000,
+ 0,
+ 11025,
+ 22050,
+ 44100,
+ 88200,
+ 176400,
+ 352800,
+ 705600,
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 64000,
+ 128000,
+ 256000,
+ 512000,
+};
+
+#define MADERA_192K_48K_RATE_MASK 0x0F003E
+#define MADERA_192K_44K1_RATE_MASK 0x003E00
+#define MADERA_192K_RATE_MASK (MADERA_192K_48K_RATE_MASK | \
+ MADERA_192K_44K1_RATE_MASK)
+#define MADERA_384K_48K_RATE_MASK 0x0F007E
+#define MADERA_384K_44K1_RATE_MASK 0x007E00
+#define MADERA_384K_RATE_MASK (MADERA_384K_48K_RATE_MASK | \
+ MADERA_384K_44K1_RATE_MASK)
+
+static const struct snd_pcm_hw_constraint_list madera_constraint = {
+ .count = ARRAY_SIZE(madera_sr_vals),
+ .list = madera_sr_vals,
+};
+
+static int madera_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct madera *madera = priv->madera;
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case MADERA_CLK_SYSCLK_1:
+ case MADERA_CLK_SYSCLK_2:
+ case MADERA_CLK_SYSCLK_3:
+ base_rate = priv->sysclk;
+ break;
+ case MADERA_CLK_ASYNCCLK_1:
+ case MADERA_CLK_ASYNCCLK_2:
+ base_rate = priv->asyncclk;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (madera->type) {
+ case CS42L92:
+ case CS47L92:
+ case CS47L93:
+ if (base_rate == 0)
+ dai_priv->constraint.mask = MADERA_384K_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = MADERA_384K_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = MADERA_384K_48K_RATE_MASK;
+ break;
+ default:
+ if (base_rate == 0)
+ dai_priv->constraint.mask = MADERA_192K_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = MADERA_192K_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = MADERA_192K_48K_RATE_MASK;
+ break;
+ }
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static int madera_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ int base = dai->driver->base;
+ int i, sr_val;
+ unsigned int reg, cur, tar;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(madera_sr_vals); i++)
+ if (madera_sr_vals[i] == params_rate(params))
+ break;
+
+ if (i == ARRAY_SIZE(madera_sr_vals)) {
+ madera_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ sr_val = i;
+
+ switch (dai_priv->clk) {
+ case MADERA_CLK_SYSCLK_1:
+ reg = MADERA_SAMPLE_RATE_1;
+ tar = 0 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_SYSCLK_2:
+ reg = MADERA_SAMPLE_RATE_2;
+ tar = 1 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_SYSCLK_3:
+ reg = MADERA_SAMPLE_RATE_3;
+ tar = 2 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_ASYNCCLK_1:
+ reg = MADERA_ASYNC_SAMPLE_RATE_1;
+ tar = 8 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ case MADERA_CLK_ASYNCCLK_2:
+ reg = MADERA_ASYNC_SAMPLE_RATE_2;
+ tar = 9 << MADERA_AIF1_RATE_SHIFT;
+ break;
+ default:
+ madera_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, reg, MADERA_SAMPLE_RATE_1_MASK,
+ sr_val);
+
+ if (!base)
+ return 0;
+
+ ret = regmap_read(priv->madera->regmap,
+ base + MADERA_AIF_RATE_CTRL, &cur);
+ if (ret != 0) {
+ madera_aif_err(dai, "Failed to check rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK))
+ return 0;
+
+ mutex_lock(&priv->rate_lock);
+
+ if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) {
+ madera_aif_warn(dai, "Cannot change rate while active\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Guard the rate change with SYSCLK cycles */
+ madera_spin_sysclk(priv);
+ snd_soc_component_update_bits(component, base + MADERA_AIF_RATE_CTRL,
+ MADERA_AIF1_RATE_MASK, tar);
+ madera_spin_sysclk(priv);
+
+out:
+ mutex_unlock(&priv->rate_lock);
+
+ return ret;
+}
+
+static int madera_aif_cfg_changed(struct snd_soc_component *component,
+ int base, int bclk, int lrclk, int frame)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, base + MADERA_AIF_BCLK_CTRL);
+ if (bclk != (val & MADERA_AIF1_BCLK_FREQ_MASK))
+ return 1;
+
+ val = snd_soc_component_read(component, base + MADERA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & MADERA_AIF1RX_BCPF_MASK))
+ return 1;
+
+ val = snd_soc_component_read(component, base + MADERA_AIF_FRAME_CTRL_1);
+ if (frame != (val & (MADERA_AIF1TX_WL_MASK |
+ MADERA_AIF1TX_SLOT_LEN_MASK)))
+ return 1;
+
+ return 0;
+}
+
+static int madera_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int base = dai->driver->base;
+ const int *rates;
+ int i, ret;
+ unsigned int val;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ unsigned int chan_limit =
+ madera->pdata.codec.max_channels_clocked[dai->id - 1];
+ int tdm_width = priv->tdm_width[dai->id - 1];
+ int tdm_slots = priv->tdm_slots[dai->id - 1];
+ int bclk, lrclk, wl, frame, bclk_target, num_rates;
+ int reconfig;
+ unsigned int aif_tx_state = 0, aif_rx_state = 0;
+
+ if (rate % 4000) {
+ rates = &madera_44k1_bclk_rates[0];
+ num_rates = ARRAY_SIZE(madera_44k1_bclk_rates);
+ } else {
+ rates = &madera_48k_bclk_rates[0];
+ num_rates = ARRAY_SIZE(madera_48k_bclk_rates);
+ }
+
+ wl = snd_pcm_format_width(params_format(params));
+
+ if (tdm_slots) {
+ madera_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
+ tdm_slots, tdm_width);
+ bclk_target = tdm_slots * tdm_width * rate;
+ channels = tdm_slots;
+ } else {
+ bclk_target = snd_soc_params_to_bclk(params);
+ tdm_width = wl;
+ }
+
+ if (chan_limit && chan_limit < channels) {
+ madera_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+ bclk_target /= channels;
+ bclk_target *= chan_limit;
+ }
+
+ /* Force multiple of 2 channels for I2S mode */
+ val = snd_soc_component_read(component, base + MADERA_AIF_FORMAT);
+ val &= MADERA_AIF1_FMT_MASK;
+ if ((channels & 1) && val == MADERA_FMT_I2S_MODE) {
+ madera_aif_dbg(dai, "Forcing stereo mode\n");
+ bclk_target /= channels;
+ bclk_target *= channels + 1;
+ }
+
+ for (i = 0; i < num_rates; i++) {
+ if (rates[i] >= bclk_target && rates[i] % rate == 0) {
+ bclk = i;
+ break;
+ }
+ }
+
+ if (i == num_rates) {
+ madera_aif_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ lrclk = rates[bclk] / rate;
+
+ madera_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
+ rates[bclk], rates[bclk] / lrclk);
+
+ frame = wl << MADERA_AIF1TX_WL_SHIFT | tdm_width;
+
+ reconfig = madera_aif_cfg_changed(component, base, bclk, lrclk, frame);
+ if (reconfig < 0)
+ return reconfig;
+
+ if (reconfig) {
+ /* Save AIF TX/RX state */
+ regmap_read(madera->regmap, base + MADERA_AIF_TX_ENABLES,
+ &aif_tx_state);
+ regmap_read(madera->regmap, base + MADERA_AIF_RX_ENABLES,
+ &aif_rx_state);
+ /* Disable AIF TX/RX before reconfiguring it */
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_TX_ENABLES, 0xff, 0x0);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_RX_ENABLES, 0xff, 0x0);
+ }
+
+ ret = madera_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_aif;
+
+ if (reconfig) {
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_BCLK_CTRL,
+ MADERA_AIF1_BCLK_FREQ_MASK, bclk);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_RX_BCLK_RATE,
+ MADERA_AIF1RX_BCPF_MASK, lrclk);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_FRAME_CTRL_1,
+ MADERA_AIF1TX_WL_MASK |
+ MADERA_AIF1TX_SLOT_LEN_MASK, frame);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_FRAME_CTRL_2,
+ MADERA_AIF1RX_WL_MASK |
+ MADERA_AIF1RX_SLOT_LEN_MASK, frame);
+ }
+
+restore_aif:
+ if (reconfig) {
+ /* Restore AIF TX/RX state */
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_TX_ENABLES,
+ 0xff, aif_tx_state);
+ regmap_update_bits(madera->regmap,
+ base + MADERA_AIF_RX_ENABLES,
+ 0xff, aif_rx_state);
+ }
+
+ return ret;
+}
+
+static int madera_is_syncclk(int clk_id)
+{
+ switch (clk_id) {
+ case MADERA_CLK_SYSCLK_1:
+ case MADERA_CLK_SYSCLK_2:
+ case MADERA_CLK_SYSCLK_3:
+ return 1;
+ case MADERA_CLK_ASYNCCLK_1:
+ case MADERA_CLK_ASYNCCLK_2:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int madera_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct snd_soc_dapm_route routes[2];
+ int is_sync;
+
+ is_sync = madera_is_syncclk(clk_id);
+ if (is_sync < 0) {
+ dev_err(component->dev, "Illegal DAI clock id %d\n", clk_id);
+ return is_sync;
+ }
+
+ if (is_sync == madera_is_syncclk(dai_priv->clk))
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ dev_err(component->dev, "Can't change clock on active DAI %d\n",
+ dai->id);
+ return -EBUSY;
+ }
+
+ dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id,
+ is_sync ? "SYSCLK" : "ASYNCCLK");
+
+ /*
+ * A connection to SYSCLK is always required, we only add and remove
+ * a connection to ASYNCCLK
+ */
+ memset(&routes, 0, sizeof(routes));
+ routes[0].sink = dai->driver->capture.stream_name;
+ routes[1].sink = dai->driver->playback.stream_name;
+ routes[0].source = "ASYNCCLK";
+ routes[1].source = "ASYNCCLK";
+
+ if (is_sync)
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
+ else
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+
+ dai_priv->clk = clk_id;
+
+ return snd_soc_dapm_sync(dapm);
+}
+
+static int madera_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_component *component = dai->component;
+ int base = dai->driver->base;
+ unsigned int reg;
+ int ret;
+
+ if (tristate)
+ reg = MADERA_AIF1_TRI;
+ else
+ reg = 0;
+
+ ret = snd_soc_component_update_bits(component,
+ base + MADERA_AIF_RATE_CTRL,
+ MADERA_AIF1_TRI, reg);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static void madera_set_channels_to_mask(struct snd_soc_dai *dai,
+ unsigned int base,
+ int channels, unsigned int mask)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ int slot, i;
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ regmap_write(madera->regmap, base + i, slot);
+
+ mask &= ~(1 << slot);
+ }
+
+ if (mask)
+ madera_aif_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical AIFs */
+ if (dai->id > MADERA_MAX_AIF)
+ return -ENOTSUPP;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ madera_set_channels_to_mask(dai, base + MADERA_AIF_FRAME_CTRL_3,
+ tx_max_chan, tx_mask);
+ madera_set_channels_to_mask(dai, base + MADERA_AIF_FRAME_CTRL_11,
+ rx_max_chan, rx_mask);
+
+ priv->tdm_width[dai->id - 1] = slot_width;
+ priv->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops madera_dai_ops = {
+ .startup = &madera_startup,
+ .set_fmt = &madera_set_fmt,
+ .set_tdm_slot = &madera_set_tdm_slot,
+ .hw_params = &madera_hw_params,
+ .set_sysclk = &madera_dai_set_sysclk,
+ .set_tristate = &madera_set_tristate,
+};
+EXPORT_SYMBOL_GPL(madera_dai_ops);
+
+const struct snd_soc_dai_ops madera_simple_dai_ops = {
+ .startup = &madera_startup,
+ .hw_params = &madera_hw_params_rate,
+ .set_sysclk = &madera_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(madera_simple_dai_ops);
+
+int madera_init_dai(struct madera_priv *priv, int id)
+{
+ struct madera_dai_priv *dai_priv = &priv->dai[id];
+
+ dai_priv->clk = MADERA_CLK_SYSCLK_1;
+ dai_priv->constraint = madera_constraint;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_dai);
+
+static const struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fratio;
+ int ratio;
+} fll_sync_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static const unsigned int pseudo_fref_max[MADERA_FLL_MAX_FRATIO] = {
+ 13500000,
+ 6144000,
+ 6144000,
+ 3072000,
+ 3072000,
+ 2822400,
+ 2822400,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 768000,
+};
+
+struct madera_fll_gains {
+ unsigned int min;
+ unsigned int max;
+ int gain; /* main gain */
+ int alt_gain; /* alternate integer gain */
+};
+
+static const struct madera_fll_gains madera_fll_sync_gains[] = {
+ { 0, 256000, 0, -1 },
+ { 256000, 1000000, 2, -1 },
+ { 1000000, 13500000, 4, -1 },
+};
+
+static const struct madera_fll_gains madera_fll_main_gains[] = {
+ { 0, 100000, 0, 2 },
+ { 100000, 375000, 2, 2 },
+ { 375000, 768000, 3, 2 },
+ { 768001, 1500000, 3, 3 },
+ { 1500000, 6000000, 4, 3 },
+ { 6000000, 13500000, 5, 3 },
+};
+
+static int madera_find_sync_fratio(unsigned int fref, int *fratio)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fll_sync_fratios); i++) {
+ if (fll_sync_fratios[i].min <= fref &&
+ fref <= fll_sync_fratios[i].max) {
+ if (fratio)
+ *fratio = fll_sync_fratios[i].fratio;
+
+ return fll_sync_fratios[i].ratio;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int madera_find_main_fratio(unsigned int fref, unsigned int fout,
+ int *fratio)
+{
+ int ratio = 1;
+
+ while ((fout / (ratio * fref)) > MADERA_FLL_MAX_N)
+ ratio++;
+
+ if (fratio)
+ *fratio = ratio - 1;
+
+ return ratio;
+}
+
+static int madera_find_fratio(struct madera_fll *fll, unsigned int fref,
+ bool sync, int *fratio)
+{
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ /* rev A0 uses sync calculation for both loops */
+ return madera_find_sync_fratio(fref, fratio);
+ default:
+ if (sync)
+ return madera_find_sync_fratio(fref, fratio);
+ else
+ return madera_find_main_fratio(fref,
+ fll->fout,
+ fratio);
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ /* these use the same calculation for main and sync loops */
+ return madera_find_sync_fratio(fref, fratio);
+ default:
+ if (sync)
+ return madera_find_sync_fratio(fref, fratio);
+ else
+ return madera_find_main_fratio(fref, fll->fout, fratio);
+ }
+}
+
+static int madera_calc_fratio(struct madera_fll *fll,
+ struct madera_fll_cfg *cfg,
+ unsigned int fref, bool sync)
+{
+ int init_ratio, ratio;
+ int refdiv, div;
+
+ /* fref must be <=13.5MHz, find initial refdiv */
+ div = 1;
+ cfg->refdiv = 0;
+ while (fref > MADERA_FLL_MAX_FREF) {
+ div *= 2;
+ fref /= 2;
+ cfg->refdiv++;
+
+ if (div > MADERA_FLL_MAX_REFDIV)
+ return -EINVAL;
+ }
+
+ /* Find an appropriate FLL_FRATIO */
+ init_ratio = madera_find_fratio(fll, fref, sync, &cfg->fratio);
+ if (init_ratio < 0) {
+ madera_fll_err(fll, "Unable to find FRATIO for fref=%uHz\n",
+ fref);
+ return init_ratio;
+ }
+
+ if (!sync)
+ cfg->fratio = init_ratio - 1;
+
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ if (sync)
+ return init_ratio;
+ break;
+ default:
+ return init_ratio;
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ if (sync)
+ return init_ratio;
+ break;
+ default:
+ return init_ratio;
+ }
+
+ /*
+ * For CS47L35 rev A0, CS47L85 and WM1840 adjust FRATIO/refdiv to avoid
+ * integer mode if possible
+ */
+ refdiv = cfg->refdiv;
+
+ while (div <= MADERA_FLL_MAX_REFDIV) {
+ /*
+ * start from init_ratio because this may already give a
+ * fractional N.K
+ */
+ for (ratio = init_ratio; ratio > 0; ratio--) {
+ if (fll->fout % (ratio * fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ for (ratio = init_ratio + 1; ratio <= MADERA_FLL_MAX_FRATIO;
+ ratio++) {
+ if ((MADERA_FLL_VCO_CORNER / 2) /
+ (MADERA_FLL_VCO_MULT * ratio) < fref)
+ break;
+
+ if (fref > pseudo_fref_max[ratio - 1])
+ break;
+
+ if (fll->fout % (ratio * fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ div *= 2;
+ fref /= 2;
+ refdiv++;
+ init_ratio = madera_find_fratio(fll, fref, sync, NULL);
+ }
+
+ madera_fll_warn(fll, "Falling back to integer mode operation\n");
+
+ return cfg->fratio + 1;
+}
+
+static int madera_find_fll_gain(struct madera_fll *fll,
+ struct madera_fll_cfg *cfg,
+ unsigned int fref,
+ const struct madera_fll_gains *gains,
+ int n_gains)
+{
+ int i;
+
+ for (i = 0; i < n_gains; i++) {
+ if (gains[i].min <= fref && fref <= gains[i].max) {
+ cfg->gain = gains[i].gain;
+ cfg->alt_gain = gains[i].alt_gain;
+ return 0;
+ }
+ }
+
+ madera_fll_err(fll, "Unable to find gain for fref=%uHz\n", fref);
+
+ return -EINVAL;
+}
+
+static int madera_calc_fll(struct madera_fll *fll,
+ struct madera_fll_cfg *cfg,
+ unsigned int fref, bool sync)
+{
+ unsigned int gcd_fll;
+ const struct madera_fll_gains *gains;
+ int n_gains;
+ int ratio, ret;
+
+ madera_fll_dbg(fll, "fref=%u Fout=%u fvco=%u\n",
+ fref, fll->fout, fll->fout * MADERA_FLL_VCO_MULT);
+
+ /* Find an appropriate FLL_FRATIO and refdiv */
+ ratio = madera_calc_fratio(fll, cfg, fref, sync);
+ if (ratio < 0)
+ return ratio;
+
+ /* Apply the division for our remaining calculations */
+ fref = fref / (1 << cfg->refdiv);
+
+ cfg->n = fll->fout / (ratio * fref);
+
+ if (fll->fout % (ratio * fref)) {
+ gcd_fll = gcd(fll->fout, ratio * fref);
+ madera_fll_dbg(fll, "GCD=%u\n", gcd_fll);
+
+ cfg->theta = (fll->fout - (cfg->n * ratio * fref))
+ / gcd_fll;
+ cfg->lambda = (ratio * fref) / gcd_fll;
+ } else {
+ cfg->theta = 0;
+ cfg->lambda = 0;
+ }
+
+ /*
+ * Round down to 16bit range with cost of accuracy lost.
+ * Denominator must be bigger than numerator so we only
+ * take care of it.
+ */
+ while (cfg->lambda >= (1 << 16)) {
+ cfg->theta >>= 1;
+ cfg->lambda >>= 1;
+ }
+
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ /* Rev A0 uses the sync gains for both loops */
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ break;
+ default:
+ if (sync) {
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ } else {
+ gains = madera_fll_main_gains;
+ n_gains = ARRAY_SIZE(madera_fll_main_gains);
+ }
+ break;
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ /* These use the sync gains for both loops */
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ break;
+ default:
+ if (sync) {
+ gains = madera_fll_sync_gains;
+ n_gains = ARRAY_SIZE(madera_fll_sync_gains);
+ } else {
+ gains = madera_fll_main_gains;
+ n_gains = ARRAY_SIZE(madera_fll_main_gains);
+ }
+ break;
+ }
+
+ ret = madera_find_fll_gain(fll, cfg, fref, gains, n_gains);
+ if (ret)
+ return ret;
+
+ madera_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
+ cfg->n, cfg->theta, cfg->lambda);
+ madera_fll_dbg(fll, "FRATIO=0x%x(%d) REFCLK_DIV=0x%x(%d)\n",
+ cfg->fratio, ratio, cfg->refdiv, 1 << cfg->refdiv);
+ madera_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
+
+ return 0;
+}
+
+static bool madera_write_fll(struct madera *madera, unsigned int base,
+ struct madera_fll_cfg *cfg, int source,
+ bool sync, int gain)
+{
+ bool change, fll_change;
+
+ fll_change = false;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_3_OFFS,
+ MADERA_FLL1_THETA_MASK,
+ cfg->theta, &change);
+ fll_change |= change;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_4_OFFS,
+ MADERA_FLL1_LAMBDA_MASK,
+ cfg->lambda, &change);
+ fll_change |= change;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_5_OFFS,
+ MADERA_FLL1_FRATIO_MASK,
+ cfg->fratio << MADERA_FLL1_FRATIO_SHIFT,
+ &change);
+ fll_change |= change;
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_DIV_MASK |
+ MADERA_FLL1_REFCLK_SRC_MASK,
+ cfg->refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT |
+ source << MADERA_FLL1_REFCLK_SRC_SHIFT,
+ &change);
+ fll_change |= change;
+
+ if (sync) {
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_SYNCHRONISER_7_OFFS,
+ MADERA_FLL1_GAIN_MASK,
+ gain << MADERA_FLL1_GAIN_SHIFT,
+ &change);
+ fll_change |= change;
+ } else {
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_7_OFFS,
+ MADERA_FLL1_GAIN_MASK,
+ gain << MADERA_FLL1_GAIN_SHIFT,
+ &change);
+ fll_change |= change;
+ }
+
+ regmap_update_bits_check(madera->regmap,
+ base + MADERA_FLL_CONTROL_2_OFFS,
+ MADERA_FLL1_CTRL_UPD | MADERA_FLL1_N_MASK,
+ MADERA_FLL1_CTRL_UPD | cfg->n, &change);
+ fll_change |= change;
+
+ return fll_change;
+}
+
+static int madera_is_enabled_fll(struct madera_fll *fll, int base)
+{
+ struct madera *madera = fll->madera;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(madera->regmap,
+ base + MADERA_FLL_CONTROL_1_OFFS, &reg);
+ if (ret != 0) {
+ madera_fll_err(fll, "Failed to read current state: %d\n", ret);
+ return ret;
+ }
+
+ return reg & MADERA_FLL1_ENA;
+}
+
+static int madera_wait_for_fll(struct madera_fll *fll, bool requested)
+{
+ struct madera *madera = fll->madera;
+ unsigned int val = 0;
+ bool status;
+ int i;
+
+ madera_fll_dbg(fll, "Waiting for FLL...\n");
+
+ for (i = 0; i < 30; i++) {
+ regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_2, &val);
+ status = val & (MADERA_FLL1_LOCK_STS1 << (fll->id - 1));
+ if (status == requested)
+ return 0;
+
+ switch (i) {
+ case 0 ... 5:
+ usleep_range(75, 125);
+ break;
+ case 11 ... 20:
+ usleep_range(750, 1250);
+ break;
+ default:
+ msleep(20);
+ break;
+ }
+ }
+
+ madera_fll_warn(fll, "Timed out waiting for lock\n");
+
+ return -ETIMEDOUT;
+}
+
+static bool madera_set_fll_phase_integrator(struct madera_fll *fll,
+ struct madera_fll_cfg *ref_cfg,
+ bool sync)
+{
+ unsigned int val;
+ bool reg_change;
+
+ if (!sync && ref_cfg->theta == 0)
+ val = (1 << MADERA_FLL1_PHASE_ENA_SHIFT) |
+ (2 << MADERA_FLL1_PHASE_GAIN_SHIFT);
+ else
+ val = 2 << MADERA_FLL1_PHASE_GAIN_SHIFT;
+
+ regmap_update_bits_check(fll->madera->regmap,
+ fll->base + MADERA_FLL_EFS_2_OFFS,
+ MADERA_FLL1_PHASE_ENA_MASK |
+ MADERA_FLL1_PHASE_GAIN_MASK,
+ val, &reg_change);
+
+ return reg_change;
+}
+
+static int madera_set_fll_clks_reg(struct madera_fll *fll, bool ena,
+ unsigned int reg, unsigned int mask,
+ unsigned int shift)
+{
+ struct madera *madera = fll->madera;
+ unsigned int src;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(madera->regmap, reg, &src);
+ if (ret != 0) {
+ madera_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ src = (src & mask) >> shift;
+
+ switch (src) {
+ case MADERA_FLL_SRC_MCLK1:
+ clk = madera->mclk[MADERA_MCLK1].clk;
+ break;
+ case MADERA_FLL_SRC_MCLK2:
+ clk = madera->mclk[MADERA_MCLK2].clk;
+ break;
+ case MADERA_FLL_SRC_MCLK3:
+ clk = madera->mclk[MADERA_MCLK3].clk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
+static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_SRC_MASK,
+ MADERA_FLL1_REFCLK_SRC_SHIFT);
+}
+
+static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLLAO_CONTROL_6_OFFS,
+ MADERA_FLL_AO_REFCLK_SRC_MASK,
+ MADERA_FLL_AO_REFCLK_SRC_SHIFT);
+}
+
+static inline int madera_set_fllhj_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLL_CONTROL_1_OFFS,
+ CS47L92_FLL1_REFCLK_SRC_MASK,
+ CS47L92_FLL1_REFCLK_SRC_SHIFT);
+}
+
+static void madera_disable_fll(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ unsigned int sync_base;
+ bool ref_change, sync_change;
+
+ switch (madera->type) {
+ case CS47L35:
+ sync_base = fll->base + CS47L35_FLL_SYNCHRONISER_OFFS;
+ break;
+ default:
+ sync_base = fll->base + MADERA_FLL_SYNCHRONISER_OFFS;
+ break;
+ }
+
+ madera_fll_dbg(fll, "Disabling FLL\n");
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, MADERA_FLL1_FREERUN);
+ regmap_update_bits_check(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
+ MADERA_FLL1_SYNC_ENA, 0, &sync_change);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, 0);
+
+ madera_wait_for_fll(fll, false);
+
+ if (sync_change)
+ madera_set_fll_clks(fll, sync_base, false);
+
+ if (ref_change) {
+ madera_set_fll_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(madera->dev);
+ }
+}
+
+static int madera_enable_fll(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ bool have_sync = false;
+ int already_enabled = madera_is_enabled_fll(fll, fll->base);
+ int sync_enabled;
+ struct madera_fll_cfg cfg;
+ unsigned int sync_base;
+ int gain, ret;
+ bool fll_change = false;
+
+ if (already_enabled < 0)
+ return already_enabled; /* error getting current state */
+
+ if (fll->ref_src < 0 || fll->ref_freq == 0) {
+ madera_fll_err(fll, "No REFCLK\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ if (fll->fout < MADERA_FLL_MIN_FOUT ||
+ fll->fout > MADERA_FLL_MAX_FOUT) {
+ madera_fll_err(fll, "invalid fout %uHz\n", fll->fout);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (madera->type) {
+ case CS47L35:
+ sync_base = fll->base + CS47L35_FLL_SYNCHRONISER_OFFS;
+ break;
+ default:
+ sync_base = fll->base + MADERA_FLL_SYNCHRONISER_OFFS;
+ break;
+ }
+
+ sync_enabled = madera_is_enabled_fll(fll, sync_base);
+ if (sync_enabled < 0)
+ return sync_enabled;
+
+ if (already_enabled) {
+ /* Facilitate smooth refclk across the transition */
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN,
+ MADERA_FLL1_FREERUN);
+ udelay(32);
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_7_OFFS,
+ MADERA_FLL1_GAIN_MASK, 0);
+
+ if (sync_enabled > 0)
+ madera_set_fll_clks(fll, sync_base, false);
+ madera_set_fll_clks(fll, fll->base, false);
+ }
+
+ /* Apply SYNCCLK setting */
+ if (fll->sync_src >= 0) {
+ ret = madera_calc_fll(fll, &cfg, fll->sync_freq, true);
+ if (ret < 0)
+ goto err;
+
+ fll_change |= madera_write_fll(madera, sync_base,
+ &cfg, fll->sync_src,
+ true, cfg.gain);
+ have_sync = true;
+ }
+
+ if (already_enabled && !!sync_enabled != have_sync)
+ madera_fll_warn(fll, "Synchroniser changed on active FLL\n");
+
+ /* Apply REFCLK setting */
+ ret = madera_calc_fll(fll, &cfg, fll->ref_freq, false);
+ if (ret < 0)
+ goto err;
+
+ /* Ref path hardcodes lambda to 65536 when sync is on */
+ if (have_sync && cfg.lambda)
+ cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda;
+
+ switch (fll->madera->type) {
+ case CS47L35:
+ switch (fll->madera->rev) {
+ case 0:
+ gain = cfg.gain;
+ break;
+ default:
+ fll_change |=
+ madera_set_fll_phase_integrator(fll, &cfg,
+ have_sync);
+ if (!have_sync && cfg.theta == 0)
+ gain = cfg.alt_gain;
+ else
+ gain = cfg.gain;
+ break;
+ }
+ break;
+ case CS47L85:
+ case WM1840:
+ gain = cfg.gain;
+ break;
+ default:
+ fll_change |= madera_set_fll_phase_integrator(fll, &cfg,
+ have_sync);
+ if (!have_sync && cfg.theta == 0)
+ gain = cfg.alt_gain;
+ else
+ gain = cfg.gain;
+ break;
+ }
+
+ fll_change |= madera_write_fll(madera, fll->base,
+ &cfg, fll->ref_src,
+ false, gain);
+
+ /*
+ * Increase the bandwidth if we're not using a low frequency
+ * sync source.
+ */
+ if (have_sync && fll->sync_freq > 100000)
+ regmap_update_bits(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_7_OFFS,
+ MADERA_FLL1_SYNC_DFSAT_MASK, 0);
+ else
+ regmap_update_bits(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_7_OFFS,
+ MADERA_FLL1_SYNC_DFSAT_MASK,
+ MADERA_FLL1_SYNC_DFSAT);
+
+ if (!already_enabled)
+ pm_runtime_get_sync(madera->dev);
+
+ if (have_sync) {
+ madera_set_fll_clks(fll, sync_base, true);
+ regmap_update_bits(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
+ MADERA_FLL1_SYNC_ENA,
+ MADERA_FLL1_SYNC_ENA);
+ }
+
+ madera_set_fll_clks(fll, fll->base, true);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA, MADERA_FLL1_ENA);
+
+ if (already_enabled)
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, 0);
+
+ if (fll_change || !already_enabled)
+ madera_wait_for_fll(fll, true);
+
+ return 0;
+
+err:
+ /* In case of error don't leave the FLL running with an old config */
+ madera_disable_fll(fll);
+
+ return ret;
+}
+
+static int madera_apply_fll(struct madera_fll *fll)
+{
+ if (fll->fout) {
+ return madera_enable_fll(fll);
+ } else {
+ madera_disable_fll(fll);
+ return 0;
+ }
+}
+
+int madera_set_fll_syncclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout)
+{
+ /*
+ * fout is ignored, since the synchronizer is an optional extra
+ * constraint on the Fout generated from REFCLK, so the Fout is
+ * set when configuring REFCLK
+ */
+
+ if (fll->sync_src == source && fll->sync_freq == fref)
+ return 0;
+
+ fll->sync_src = source;
+ fll->sync_freq = fref;
+
+ return madera_apply_fll(fll);
+}
+EXPORT_SYMBOL_GPL(madera_set_fll_syncclk);
+
+int madera_set_fll_refclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout)
+{
+ int ret;
+
+ if (fll->ref_src == source &&
+ fll->ref_freq == fref && fll->fout == fout)
+ return 0;
+
+ /*
+ * Changes of fout on an enabled FLL aren't allowed except when
+ * setting fout==0 to disable the FLL
+ */
+ if (fout && fout != fll->fout) {
+ ret = madera_is_enabled_fll(fll, fll->base);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ madera_fll_err(fll, "Can't change Fout on active FLL\n");
+ return -EBUSY;
+ }
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = fref;
+ fll->fout = fout;
+
+ return madera_apply_fll(fll);
+}
+EXPORT_SYMBOL_GPL(madera_set_fll_refclk);
+
+int madera_init_fll(struct madera *madera, int id, int base,
+ struct madera_fll *fll)
+{
+ fll->id = id;
+ fll->base = base;
+ fll->madera = madera;
+ fll->ref_src = MADERA_FLL_SRC_NONE;
+ fll->sync_src = MADERA_FLL_SRC_NONE;
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_FREERUN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_init_fll);
+
+static const struct reg_sequence madera_fll_ao_32K_49M_patch[] = {
+ { MADERA_FLLAO_CONTROL_2, 0x02EE },
+ { MADERA_FLLAO_CONTROL_3, 0x0000 },
+ { MADERA_FLLAO_CONTROL_4, 0x0001 },
+ { MADERA_FLLAO_CONTROL_5, 0x0002 },
+ { MADERA_FLLAO_CONTROL_6, 0x8001 },
+ { MADERA_FLLAO_CONTROL_7, 0x0004 },
+ { MADERA_FLLAO_CONTROL_8, 0x0077 },
+ { MADERA_FLLAO_CONTROL_10, 0x06D8 },
+ { MADERA_FLLAO_CONTROL_11, 0x0085 },
+ { MADERA_FLLAO_CONTROL_2, 0x82EE },
+};
+
+static const struct reg_sequence madera_fll_ao_32K_45M_patch[] = {
+ { MADERA_FLLAO_CONTROL_2, 0x02B1 },
+ { MADERA_FLLAO_CONTROL_3, 0x0001 },
+ { MADERA_FLLAO_CONTROL_4, 0x0010 },
+ { MADERA_FLLAO_CONTROL_5, 0x0002 },
+ { MADERA_FLLAO_CONTROL_6, 0x8001 },
+ { MADERA_FLLAO_CONTROL_7, 0x0004 },
+ { MADERA_FLLAO_CONTROL_8, 0x0077 },
+ { MADERA_FLLAO_CONTROL_10, 0x06D8 },
+ { MADERA_FLLAO_CONTROL_11, 0x0005 },
+ { MADERA_FLLAO_CONTROL_2, 0x82B1 },
+};
+
+struct madera_fllao_patch {
+ unsigned int fin;
+ unsigned int fout;
+ const struct reg_sequence *patch;
+ unsigned int patch_size;
+};
+
+static const struct madera_fllao_patch madera_fllao_settings[] = {
+ {
+ .fin = 32768,
+ .fout = 49152000,
+ .patch = madera_fll_ao_32K_49M_patch,
+ .patch_size = ARRAY_SIZE(madera_fll_ao_32K_49M_patch),
+
+ },
+ {
+ .fin = 32768,
+ .fout = 45158400,
+ .patch = madera_fll_ao_32K_45M_patch,
+ .patch_size = ARRAY_SIZE(madera_fll_ao_32K_45M_patch),
+ },
+};
+
+static int madera_enable_fll_ao(struct madera_fll *fll,
+ const struct reg_sequence *patch,
+ unsigned int patch_size)
+{
+ struct madera *madera = fll->madera;
+ int already_enabled = madera_is_enabled_fll(fll, fll->base);
+ unsigned int val;
+ int i;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(madera->dev);
+
+ madera_fll_dbg(fll, "Enabling FLL_AO, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ /* FLL_AO_HOLD must be set before configuring any registers */
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD);
+
+ if (already_enabled)
+ madera_set_fllao_clks(fll, fll->base, false);
+
+ for (i = 0; i < patch_size; i++) {
+ val = patch[i].def;
+
+ /* modify the patch to apply fll->ref_src as input clock */
+ if (patch[i].reg == MADERA_FLLAO_CONTROL_6) {
+ val &= ~MADERA_FLL_AO_REFCLK_SRC_MASK;
+ val |= (fll->ref_src << MADERA_FLL_AO_REFCLK_SRC_SHIFT)
+ & MADERA_FLL_AO_REFCLK_SRC_MASK;
+ }
+
+ regmap_write(madera->regmap, patch[i].reg, val);
+ }
+
+ madera_set_fllao_clks(fll, fll->base, true);
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_ENA, MADERA_FLL_AO_ENA);
+
+ /* Release the hold so that fll_ao locks to external frequency */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_HOLD, 0);
+
+ if (!already_enabled)
+ madera_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int madera_disable_fll_ao(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ bool change;
+
+ madera_fll_dbg(fll, "Disabling FLL_AO\n");
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD);
+ regmap_update_bits_check(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
+ MADERA_FLL_AO_ENA, 0, &change);
+
+ madera_wait_for_fll(fll, false);
+
+ /*
+ * ctrl_up gates the writes to all fll_ao register, setting it to 0
+ * here ensures that after a runtime suspend/resume cycle when one
+ * enables the fllao then ctrl_up is the last bit that is configured
+ * by the fllao enable code rather than the cache sync operation which
+ * would have updated it much earlier before writing out all fllao
+ * registers
+ */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLLAO_CONTROL_2_OFFS,
+ MADERA_FLL_AO_CTRL_UPD_MASK, 0);
+
+ if (change) {
+ madera_set_fllao_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(madera->dev);
+ }
+
+ return 0;
+}
+
+int madera_set_fll_ao_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+ const struct reg_sequence *patch = NULL;
+ int patch_size = 0;
+ unsigned int i;
+
+ if (fll->ref_src == source &&
+ fll->ref_freq == fin && fll->fout == fout)
+ return 0;
+
+ madera_fll_dbg(fll, "Change FLL_AO refclk to fin=%u fout=%u source=%d\n",
+ fin, fout, source);
+
+ if (fout && (fll->ref_freq != fin || fll->fout != fout)) {
+ for (i = 0; i < ARRAY_SIZE(madera_fllao_settings); i++) {
+ if (madera_fllao_settings[i].fin == fin &&
+ madera_fllao_settings[i].fout == fout)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(madera_fllao_settings)) {
+ madera_fll_err(fll,
+ "No matching configuration for FLL_AO\n");
+ return -EINVAL;
+ }
+
+ patch = madera_fllao_settings[i].patch;
+ patch_size = madera_fllao_settings[i].patch_size;
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = madera_enable_fll_ao(fll, patch, patch_size);
+ else
+ madera_disable_fll_ao(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_set_fll_ao_refclk);
+
+static int madera_fllhj_disable(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ bool change;
+
+ madera_fll_dbg(fll, "Disabling FLL\n");
+
+ /* Disable lockdet, but don't set ctrl_upd update but. This allows the
+ * lock status bit to clear as normal, but should the FLL be enabled
+ * again due to a control clock being required, the lock won't re-assert
+ * as the FLL config registers are automatically applied when the FLL
+ * enables.
+ */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_11_OFFS,
+ MADERA_FLL1_LOCKDET_MASK, 0);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_HOLD_MASK, MADERA_FLL1_HOLD_MASK);
+ regmap_update_bits_check(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA_MASK, 0, &change);
+
+ madera_wait_for_fll(fll, false);
+
+ /* ctrl_up gates the writes to all the fll's registers, setting it to 0
+ * here ensures that after a runtime suspend/resume cycle when one
+ * enables the fll then ctrl_up is the last bit that is configured
+ * by the fll enable code rather than the cache sync operation which
+ * would have updated it much earlier before writing out all fll
+ * registers
+ */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_2_OFFS,
+ MADERA_FLL1_CTRL_UPD_MASK, 0);
+
+ if (change) {
+ madera_set_fllhj_clks(fll, fll->base, false);
+ pm_runtime_put_autosuspend(madera->dev);
+ }
+
+ return 0;
+}
+
+static int madera_fllhj_apply(struct madera_fll *fll, int fin)
+{
+ struct madera *madera = fll->madera;
+ int refdiv, fref, fout, lockdet_thr, fbdiv, hp, fast_clk, fllgcd;
+ bool frac = false;
+ unsigned int fll_n, min_n, max_n, ratio, theta, lambda;
+ unsigned int gains, val, num;
+
+ madera_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
+
+ for (refdiv = 0; refdiv < 4; refdiv++)
+ if ((fin / (1 << refdiv)) <= MADERA_FLLHJ_MAX_THRESH)
+ break;
+
+ fref = fin / (1 << refdiv);
+
+ /* Use simple heuristic approach to find a configuration that
+ * should work for most input clocks.
+ */
+ fast_clk = 0;
+ fout = fll->fout;
+ frac = fout % fref;
+
+ if (fref < MADERA_FLLHJ_LOW_THRESH) {
+ lockdet_thr = 2;
+ gains = MADERA_FLLHJ_LOW_GAINS;
+ if (frac)
+ fbdiv = 256;
+ else
+ fbdiv = 4;
+ } else if (fref < MADERA_FLLHJ_MID_THRESH) {
+ lockdet_thr = 8;
+ gains = MADERA_FLLHJ_MID_GAINS;
+ fbdiv = 1;
+ } else {
+ lockdet_thr = 8;
+ gains = MADERA_FLLHJ_HIGH_GAINS;
+ fbdiv = 1;
+ /* For high speed input clocks, enable 300MHz fast oscillator
+ * when we're in fractional divider mode.
+ */
+ if (frac) {
+ fast_clk = 0x3;
+ fout = fll->fout * 6;
+ }
+ }
+ /* Use high performance mode for fractional configurations. */
+ if (frac) {
+ hp = 0x3;
+ min_n = MADERA_FLLHJ_FRAC_MIN_N;
+ max_n = MADERA_FLLHJ_FRAC_MAX_N;
+ } else {
+ hp = 0x0;
+ min_n = MADERA_FLLHJ_INT_MIN_N;
+ max_n = MADERA_FLLHJ_INT_MAX_N;
+ }
+
+ ratio = fout / fref;
+
+ madera_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n",
+ refdiv, fref, frac);
+
+ while (ratio / fbdiv < min_n) {
+ fbdiv /= 2;
+ if (fbdiv < 1) {
+ madera_fll_err(fll, "FBDIV (%d) must be >= 1\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+ while (frac && (ratio / fbdiv > max_n)) {
+ fbdiv *= 2;
+ if (fbdiv >= 1024) {
+ madera_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+
+ madera_fll_dbg(fll, "lockdet=%d, hp=0x%x, fbdiv:%d\n",
+ lockdet_thr, hp, fbdiv);
+
+ /* Calculate N.K values */
+ fllgcd = gcd(fout, fbdiv * fref);
+ num = fout / fllgcd;
+ lambda = (fref * fbdiv) / fllgcd;
+ fll_n = num / lambda;
+ theta = num % lambda;
+
+ madera_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
+ fll_n, fllgcd, theta, lambda);
+
+ /* Some sanity checks before any registers are written. */
+ if (fll_n < min_n || fll_n > max_n) {
+ madera_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
+ frac ? "fractional" : "integer", min_n, max_n,
+ fll_n);
+ return -EINVAL;
+ }
+ if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
+ madera_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
+ frac ? "fractional" : "integer", fbdiv);
+ return -EINVAL;
+ }
+
+ /* clear the ctrl_upd bit to guarantee we write to it later. */
+ regmap_write(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_2_OFFS,
+ fll_n << MADERA_FLL1_N_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_3_OFFS,
+ MADERA_FLL1_THETA_MASK,
+ theta << MADERA_FLL1_THETA_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_4_OFFS,
+ MADERA_FLL1_LAMBDA_MASK,
+ lambda << MADERA_FLL1_LAMBDA_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_5_OFFS,
+ MADERA_FLL1_FB_DIV_MASK,
+ fbdiv << MADERA_FLL1_FB_DIV_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_DIV_MASK,
+ refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_GAIN_OFFS,
+ 0xffff,
+ gains);
+ val = hp << MADERA_FLL1_HP_SHIFT;
+ val |= 1 << MADERA_FLL1_PHASEDET_ENA_SHIFT;
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_10_OFFS,
+ MADERA_FLL1_HP_MASK | MADERA_FLL1_PHASEDET_ENA_MASK,
+ val);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_11_OFFS,
+ MADERA_FLL1_LOCKDET_THR_MASK,
+ lockdet_thr << MADERA_FLL1_LOCKDET_THR_SHIFT);
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL1_DIGITAL_TEST_1_OFFS,
+ MADERA_FLL1_SYNC_EFS_ENA_MASK |
+ MADERA_FLL1_CLK_VCO_FAST_SRC_MASK,
+ fast_clk);
+
+ return 0;
+}
+
+static int madera_fllhj_enable(struct madera_fll *fll)
+{
+ struct madera *madera = fll->madera;
+ int already_enabled = madera_is_enabled_fll(fll, fll->base);
+ int ret;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(madera->dev);
+
+ madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ /* FLLn_HOLD must be set before configuring any registers */
+ regmap_update_bits(fll->madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_HOLD_MASK,
+ MADERA_FLL1_HOLD_MASK);
+
+ if (already_enabled)
+ madera_set_fllhj_clks(fll, fll->base, false);
+
+ /* Apply refclk */
+ ret = madera_fllhj_apply(fll, fll->ref_freq);
+ if (ret) {
+ madera_fll_err(fll, "Failed to set FLL: %d\n", ret);
+ goto out;
+ }
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ CS47L92_FLL1_REFCLK_SRC_MASK,
+ fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT);
+
+ madera_set_fllhj_clks(fll, fll->base, true);
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_ENA_MASK,
+ MADERA_FLL1_ENA_MASK);
+
+out:
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_11_OFFS,
+ MADERA_FLL1_LOCKDET_MASK,
+ MADERA_FLL1_LOCKDET_MASK);
+
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_2_OFFS,
+ MADERA_FLL1_CTRL_UPD_MASK,
+ MADERA_FLL1_CTRL_UPD_MASK);
+
+ /* Release the hold so that flln locks to external frequency */
+ regmap_update_bits(madera->regmap,
+ fll->base + MADERA_FLL_CONTROL_1_OFFS,
+ MADERA_FLL1_HOLD_MASK,
+ 0);
+
+ if (!already_enabled)
+ madera_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int madera_fllhj_validate(struct madera_fll *fll,
+ unsigned int ref_in,
+ unsigned int fout)
+{
+ if (fout && !ref_in) {
+ madera_fll_err(fll, "fllout set without valid input clk\n");
+ return -EINVAL;
+ }
+
+ if (fll->fout && fout != fll->fout) {
+ madera_fll_err(fll, "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (ref_in / MADERA_FLL_MAX_REFDIV > MADERA_FLLHJ_MAX_THRESH) {
+ madera_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int madera_fllhj_set_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+
+ /* To remain consistent with previous FLLs, we expect fout to be
+ * provided in the form of the required sysclk rate, which is
+ * 2x the calculated fll out.
+ */
+ if (fout)
+ fout /= 2;
+
+ if (fll->ref_src == source && fll->ref_freq == fin &&
+ fll->fout == fout)
+ return 0;
+
+ if (fin && fout && madera_fllhj_validate(fll, fin, fout))
+ return -EINVAL;
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = madera_fllhj_enable(fll);
+ else
+ madera_fllhj_disable(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk);
+
+/**
+ * madera_set_output_mode - Set the mode of the specified output
+ *
+ * @component: Device to configure
+ * @output: Output number
+ * @differential: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device. In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int madera_set_output_mode(struct snd_soc_component *component, int output,
+ bool differential)
+{
+ unsigned int reg, val;
+ int ret;
+
+ if (output < 1 || output > MADERA_MAX_OUTPUT)
+ return -EINVAL;
+
+ reg = MADERA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+ if (differential)
+ val = MADERA_OUT1_MONO;
+ else
+ val = 0;
+
+ ret = snd_soc_component_update_bits(component, reg, MADERA_OUT1_MONO,
+ val);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(madera_set_output_mode);
+
+static bool madera_eq_filter_unstable(bool mode, __be16 _a, __be16 _b)
+{
+ s16 a = be16_to_cpu(_a);
+ s16 b = be16_to_cpu(_b);
+
+ if (!mode) {
+ return abs(a) >= 4096;
+ } else {
+ if (abs(b) >= 4096)
+ return true;
+
+ return (abs((a << 16) / (4096 - b)) >= 4096 << 4);
+ }
+}
+
+int madera_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ unsigned int val;
+ __be16 *data;
+ int len;
+ int ret;
+
+ len = params->num_regs * regmap_get_val_bytes(madera->regmap);
+
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ data[0] &= cpu_to_be16(MADERA_EQ1_B1_MODE);
+
+ if (madera_eq_filter_unstable(!!data[0], data[1], data[2]) ||
+ madera_eq_filter_unstable(true, data[4], data[5]) ||
+ madera_eq_filter_unstable(true, data[8], data[9]) ||
+ madera_eq_filter_unstable(true, data[12], data[13]) ||
+ madera_eq_filter_unstable(false, data[16], data[17])) {
+ dev_err(madera->dev, "Rejecting unstable EQ coefficients\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = regmap_read(madera->regmap, params->base, &val);
+ if (ret != 0)
+ goto out;
+
+ val &= ~MADERA_EQ1_B1_MODE;
+ data[0] |= cpu_to_be16(val);
+
+ ret = regmap_raw_write(madera->regmap, params->base, data, len);
+
+out:
+ kfree(data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(madera_eq_coeff_put);
+
+int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ __be16 *data = (__be16 *)ucontrol->value.bytes.data;
+ s16 val = be16_to_cpu(*data);
+
+ if (abs(val) >= 4096) {
+ dev_err(madera->dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(madera_lhpf_coeff_put);
+
+MODULE_SOFTDEP("pre: madera");
+MODULE_DESCRIPTION("ASoC Cirrus Logic Madera codec support");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h
new file mode 100644
index 000000000000..09ad6e9bce4b
--- /dev/null
+++ b/sound/soc/codecs/madera.h
@@ -0,0 +1,458 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Cirrus Logic Madera class codecs common support
+ *
+ * Copyright (C) 2015-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef ASOC_MADERA_H
+#define ASOC_MADERA_H
+
+#include <linux/completion.h>
+#include <sound/soc.h>
+#include <sound/madera-pdata.h>
+
+#include "wm_adsp.h"
+
+#define MADERA_FLL1_REFCLK 1
+#define MADERA_FLL2_REFCLK 2
+#define MADERA_FLL3_REFCLK 3
+#define MADERA_FLLAO_REFCLK 4
+#define MADERA_FLL1_SYNCCLK 5
+#define MADERA_FLL2_SYNCCLK 6
+#define MADERA_FLL3_SYNCCLK 7
+#define MADERA_FLLAO_SYNCCLK 8
+
+#define MADERA_FLL_SRC_NONE -1
+#define MADERA_FLL_SRC_MCLK1 0
+#define MADERA_FLL_SRC_MCLK2 1
+#define MADERA_FLL_SRC_MCLK3 2
+#define MADERA_FLL_SRC_SLIMCLK 3
+#define MADERA_FLL_SRC_FLL1 4
+#define MADERA_FLL_SRC_FLL2 5
+#define MADERA_FLL_SRC_AIF1BCLK 8
+#define MADERA_FLL_SRC_AIF2BCLK 9
+#define MADERA_FLL_SRC_AIF3BCLK 10
+#define MADERA_FLL_SRC_AIF4BCLK 11
+#define MADERA_FLL_SRC_AIF1LRCLK 12
+#define MADERA_FLL_SRC_AIF2LRCLK 13
+#define MADERA_FLL_SRC_AIF3LRCLK 14
+#define MADERA_FLL_SRC_AIF4LRCLK 15
+
+#define MADERA_CLK_SYSCLK_1 1
+#define MADERA_CLK_ASYNCCLK_1 2
+#define MADERA_CLK_OPCLK 3
+#define MADERA_CLK_ASYNC_OPCLK 4
+#define MADERA_CLK_SYSCLK_2 5
+#define MADERA_CLK_SYSCLK_3 6
+#define MADERA_CLK_ASYNCCLK_2 7
+#define MADERA_CLK_DSPCLK 8
+#define MADERA_CLK_OUTCLK 9
+
+#define MADERA_CLK_SRC_MCLK1 0x0
+#define MADERA_CLK_SRC_MCLK2 0x1
+#define MADERA_CLK_SRC_MCLK3 0x2
+#define MADERA_CLK_SRC_FLL1 0x4
+#define MADERA_CLK_SRC_FLL2 0x5
+#define MADERA_CLK_SRC_FLL3 0x6
+#define MADERA_CLK_SRC_FLLAO_HI 0x7
+#define MADERA_CLK_SRC_FLL1_DIV6 0x7
+#define MADERA_CLK_SRC_AIF1BCLK 0x8
+#define MADERA_CLK_SRC_AIF2BCLK 0x9
+#define MADERA_CLK_SRC_AIF3BCLK 0xA
+#define MADERA_CLK_SRC_AIF4BCLK 0xB
+#define MADERA_CLK_SRC_FLLAO 0xF
+
+#define MADERA_OUTCLK_SYSCLK 0
+#define MADERA_OUTCLK_ASYNCCLK 1
+#define MADERA_OUTCLK_MCLK1 4
+#define MADERA_OUTCLK_MCLK2 5
+#define MADERA_OUTCLK_MCLK3 6
+
+#define MADERA_MIXER_VOL_MASK 0x00FE
+#define MADERA_MIXER_VOL_SHIFT 1
+#define MADERA_MIXER_VOL_WIDTH 7
+
+#define MADERA_DOM_GRP_FX 0
+#define MADERA_DOM_GRP_ASRC1 1
+#define MADERA_DOM_GRP_ASRC2 2
+#define MADERA_DOM_GRP_ISRC1 3
+#define MADERA_DOM_GRP_ISRC2 4
+#define MADERA_DOM_GRP_ISRC3 5
+#define MADERA_DOM_GRP_ISRC4 6
+#define MADERA_DOM_GRP_OUT 7
+#define MADERA_DOM_GRP_SPD 8
+#define MADERA_DOM_GRP_DSP1 9
+#define MADERA_DOM_GRP_DSP2 10
+#define MADERA_DOM_GRP_DSP3 11
+#define MADERA_DOM_GRP_DSP4 12
+#define MADERA_DOM_GRP_DSP5 13
+#define MADERA_DOM_GRP_DSP6 14
+#define MADERA_DOM_GRP_DSP7 15
+#define MADERA_DOM_GRP_AIF1 16
+#define MADERA_DOM_GRP_AIF2 17
+#define MADERA_DOM_GRP_AIF3 18
+#define MADERA_DOM_GRP_AIF4 19
+#define MADERA_DOM_GRP_SLIMBUS 20
+#define MADERA_DOM_GRP_PWM 21
+#define MADERA_DOM_GRP_DFC 22
+#define MADERA_N_DOM_GRPS 23
+
+#define MADERA_MAX_DAI 11
+#define MADERA_MAX_ADSP 7
+
+#define MADERA_NUM_MIXER_INPUTS 148
+
+struct madera;
+struct wm_adsp;
+
+struct madera_voice_trigger_info {
+ /** Which core triggered, 1-based (1 = DSP1, ...) */
+ int core_num;
+};
+
+struct madera_dai_priv {
+ int clk;
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct madera_priv {
+ struct wm_adsp adsp[MADERA_MAX_ADSP];
+ struct madera *madera;
+ struct device *dev;
+ int sysclk;
+ int asyncclk;
+ int dspclk;
+ struct madera_dai_priv dai[MADERA_MAX_DAI];
+
+ int num_inputs;
+
+ unsigned int in_pending;
+
+ unsigned int out_up_pending;
+ unsigned int out_up_delay;
+ unsigned int out_down_pending;
+ unsigned int out_down_delay;
+
+ unsigned int adsp_rate_cache[MADERA_MAX_ADSP];
+
+ struct mutex rate_lock;
+
+ int tdm_width[MADERA_MAX_AIF];
+ int tdm_slots[MADERA_MAX_AIF];
+
+ int domain_group_ref[MADERA_N_DOM_GRPS];
+};
+
+struct madera_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int fratio;
+ int gain;
+ int alt_gain;
+};
+
+struct madera_fll {
+ struct madera *madera;
+ int id;
+ unsigned int base;
+
+ unsigned int fout;
+
+ int sync_src;
+ unsigned int sync_freq;
+
+ int ref_src;
+ unsigned int ref_freq;
+ struct madera_fll_cfg ref_cfg;
+};
+
+struct madera_enum {
+ struct soc_enum mixer_enum;
+ int val;
+};
+
+extern const unsigned int madera_ana_tlv[];
+extern const unsigned int madera_eq_tlv[];
+extern const unsigned int madera_digital_tlv[];
+extern const unsigned int madera_noise_tlv[];
+extern const unsigned int madera_ng_tlv[];
+
+extern const unsigned int madera_mixer_tlv[];
+extern const char * const madera_mixer_texts[MADERA_NUM_MIXER_INPUTS];
+extern const unsigned int madera_mixer_values[MADERA_NUM_MIXER_INPUTS];
+
+#define MADERA_GAINMUX_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv)
+
+#define MADERA_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \
+ MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ madera_mixer_tlv)
+
+#define MADERA_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \
+ name, reg, 0, 0xff, madera_mixer_texts, madera_mixer_values)
+
+#define MADERA_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM("Route", name##_enum)
+
+#define MADERA_MUX_ENUMS(name, base_reg) \
+ static MADERA_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static MADERA_MUX_CTL_DECL(name)
+
+#define MADERA_MIXER_ENUMS(name, base_reg) \
+ MADERA_MUX_ENUMS(name##_in1, base_reg); \
+ MADERA_MUX_ENUMS(name##_in2, base_reg + 2); \
+ MADERA_MUX_ENUMS(name##_in3, base_reg + 4); \
+ MADERA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define MADERA_DSP_AUX_ENUMS(name, base_reg) \
+ MADERA_MUX_ENUMS(name##_aux1, base_reg); \
+ MADERA_MUX_ENUMS(name##_aux2, base_reg + 8); \
+ MADERA_MUX_ENUMS(name##_aux3, base_reg + 16); \
+ MADERA_MUX_ENUMS(name##_aux4, base_reg + 24); \
+ MADERA_MUX_ENUMS(name##_aux5, base_reg + 32); \
+ MADERA_MUX_ENUMS(name##_aux6, base_reg + 40)
+
+#define MADERA_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define MADERA_MUX_WIDGETS(name, name_str) \
+ MADERA_MUX(name_str " Input 1", &name##_mux)
+
+#define MADERA_MIXER_WIDGETS(name, name_str) \
+ MADERA_MUX(name_str " Input 1", &name##_in1_mux), \
+ MADERA_MUX(name_str " Input 2", &name##_in2_mux), \
+ MADERA_MUX(name_str " Input 3", &name##_in3_mux), \
+ MADERA_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define MADERA_DSP_WIDGETS(name, name_str) \
+ MADERA_MIXER_WIDGETS(name##L, name_str "L"), \
+ MADERA_MIXER_WIDGETS(name##R, name_str "R"), \
+ MADERA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ MADERA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ MADERA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ MADERA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ MADERA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ MADERA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define MADERA_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input 1" }, \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 1")
+
+#define MADERA_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 1"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 2"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 3"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define MADERA_DSP_ROUTES(name) \
+ { name, NULL, name " Preloader"}, \
+ { name " Preload", NULL, name " Preloader"}, \
+ { name, NULL, "SYSCLK"}, \
+ { name, NULL, "DSPCLK"}, \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ MADERA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+ MADERA_MIXER_ROUTES(name, name "L"), \
+ MADERA_MIXER_ROUTES(name, name "R")
+
+#define MADERA_RATE_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_get_enum_double, .put = madera_rate_put, \
+ .private_value = (unsigned long)&xenum }
+
+#define MADERA_EQ_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = madera_eq_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 20, .mask = ~MADERA_EQ1_B1_MODE }) }
+
+#define MADERA_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = madera_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+#define MADERA_RATES SNDRV_PCM_RATE_KNOT
+
+#define MADERA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define MADERA_OSR_ENUM_SIZE 5
+#define MADERA_SYNC_RATE_ENUM_SIZE 3
+#define MADERA_ASYNC_RATE_ENUM_SIZE 2
+#define MADERA_RATE_ENUM_SIZE \
+ (MADERA_SYNC_RATE_ENUM_SIZE + MADERA_ASYNC_RATE_ENUM_SIZE)
+#define MADERA_SAMPLE_RATE_ENUM_SIZE 16
+#define MADERA_DFC_TYPE_ENUM_SIZE 5
+#define MADERA_DFC_WIDTH_ENUM_SIZE 5
+
+extern const struct snd_soc_dai_ops madera_dai_ops;
+extern const struct snd_soc_dai_ops madera_simple_dai_ops;
+
+extern const struct snd_kcontrol_new madera_inmux[];
+extern const struct snd_kcontrol_new madera_inmode[];
+
+extern const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE];
+extern const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE];
+
+extern const struct soc_enum madera_sample_rate[];
+extern const struct soc_enum madera_isrc_fsl[];
+extern const struct soc_enum madera_isrc_fsh[];
+extern const struct soc_enum madera_asrc1_rate[];
+extern const struct soc_enum madera_asrc1_bidir_rate[];
+extern const struct soc_enum madera_asrc2_rate[];
+extern const struct soc_enum madera_dfc_width[];
+extern const struct soc_enum madera_dfc_type[];
+
+extern const struct soc_enum madera_in_vi_ramp;
+extern const struct soc_enum madera_in_vd_ramp;
+
+extern const struct soc_enum madera_out_vi_ramp;
+extern const struct soc_enum madera_out_vd_ramp;
+
+extern const struct soc_enum madera_lhpf1_mode;
+extern const struct soc_enum madera_lhpf2_mode;
+extern const struct soc_enum madera_lhpf3_mode;
+extern const struct soc_enum madera_lhpf4_mode;
+
+extern const struct soc_enum madera_ng_hold;
+extern const struct soc_enum madera_in_hpf_cut_enum;
+extern const struct soc_enum madera_in_dmic_osr[];
+
+extern const struct soc_enum madera_output_anc_src[];
+extern const struct soc_enum madera_anc_input_src[];
+extern const struct soc_enum madera_anc_ng_enum;
+
+extern const struct snd_kcontrol_new madera_dsp_trigger_output_mux[];
+extern const struct snd_kcontrol_new madera_drc_activity_output_mux[];
+
+extern const struct snd_kcontrol_new madera_adsp_rate_controls[];
+
+int madera_dfc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_lp_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int madera_out1_demux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int madera_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_in_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_anc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int madera_domain_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event);
+
+int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num,
+ unsigned int freq);
+
+int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir);
+
+int madera_init_fll(struct madera *madera, int id, int base,
+ struct madera_fll *fll);
+int madera_set_fll_refclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout);
+int madera_set_fll_syncclk(struct madera_fll *fll, int source,
+ unsigned int fref, unsigned int fout);
+int madera_set_fll_ao_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout);
+int madera_fllhj_set_refclk(struct madera_fll *fll, int source,
+ unsigned int fin, unsigned int fout);
+
+int madera_core_init(struct madera_priv *priv);
+int madera_core_free(struct madera_priv *priv);
+int madera_init_overheat(struct madera_priv *priv);
+int madera_free_overheat(struct madera_priv *priv);
+int madera_init_inputs(struct snd_soc_component *component);
+int madera_init_outputs(struct snd_soc_component *component,
+ const struct snd_soc_dapm_route *routes,
+ int n_mono_routes, int n_real);
+int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num,
+ irq_handler_t handler);
+void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num);
+
+int madera_init_dai(struct madera_priv *priv, int id);
+
+int madera_set_output_mode(struct snd_soc_component *component, int output,
+ bool differential);
+
+/* Following functions are for use by machine drivers */
+static inline int madera_register_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+
+ return blocking_notifier_chain_register(&madera->notifier, nb);
+}
+
+static inline int
+madera_unregister_notifier(struct snd_soc_component *component,
+ struct notifier_block *nb)
+{
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+
+ return blocking_notifier_chain_unregister(&madera->notifier, nb);
+}
+
+#endif
diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c
new file mode 100644
index 000000000000..9760543f2922
--- /dev/null
+++ b/sound/soc/codecs/max9759.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MAX9759 Amplifier Driver
+ *
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define DRV_NAME "max9759"
+
+struct max9759 {
+ struct gpio_desc *gpiod_shutdown;
+ struct gpio_desc *gpiod_mute;
+ struct gpio_descs *gpiod_gain;
+ bool is_mute;
+ unsigned int gain;
+};
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpiod_set_value_cansleep(priv->gpiod_shutdown, 0);
+ else
+ gpiod_set_value_cansleep(priv->gpiod_shutdown, 1);
+
+ return 0;
+}
+
+/* From 6dB to 24dB in steps of 6dB */
+static const DECLARE_TLV_DB_SCALE(speaker_gain_tlv, 600, 600, 0);
+
+static int speaker_gain_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->gain;
+
+ return 0;
+}
+
+static const bool speaker_gain_table[4][2] = {
+ /* G1, G2 */
+ {true, true}, /* +6dB */
+ {false, true}, /* +12dB */
+ {true, false}, /* +18dB */
+ {false, false}, /* +24dB */
+};
+
+static int speaker_gain_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > 3)
+ return -EINVAL;
+
+ priv->gain = ucontrol->value.integer.value[0];
+
+ /* G1 */
+ gpiod_set_value_cansleep(priv->gpiod_gain->desc[0],
+ speaker_gain_table[priv->gain][0]);
+ /* G2 */
+ gpiod_set_value_cansleep(priv->gpiod_gain->desc[1],
+ speaker_gain_table[priv->gain][1]);
+
+ return 1;
+}
+
+static int speaker_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = !priv->is_mute;
+
+ return 0;
+}
+
+static int speaker_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct max9759 *priv = snd_soc_component_get_drvdata(c);
+
+ priv->is_mute = !ucontrol->value.integer.value[0];
+
+ gpiod_set_value_cansleep(priv->gpiod_mute, priv->is_mute);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new max9759_dapm_controls[] = {
+ SOC_SINGLE_EXT_TLV("Speaker Gain Volume", 0, 0, 3, 0,
+ speaker_gain_control_get, speaker_gain_control_put,
+ speaker_gain_tlv),
+ SOC_SINGLE_BOOL_EXT("Playback Switch", 0,
+ speaker_mute_get, speaker_mute_put),
+};
+
+static const struct snd_soc_dapm_widget max9759_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+ SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, pga_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route max9759_dapm_routes[] = {
+ { "PGA", NULL, "INL" },
+ { "PGA", NULL, "INR" },
+ { "OUTL", NULL, "PGA" },
+ { "OUTR", NULL, "PGA" },
+};
+
+static const struct snd_soc_component_driver max9759_component_driver = {
+ .controls = max9759_dapm_controls,
+ .num_controls = ARRAY_SIZE(max9759_dapm_controls),
+ .dapm_widgets = max9759_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9759_dapm_widgets),
+ .dapm_routes = max9759_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9759_dapm_routes),
+};
+
+static int max9759_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct max9759 *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_shutdown))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown),
+ "Failed to get 'shutdown' gpio");
+
+ priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_mute))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute),
+ "Failed to get 'mute' gpio");
+ priv->is_mute = true;
+
+ priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_gain))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain),
+ "Failed to get 'gain' gpios");
+ priv->gain = 0;
+
+ if (priv->gpiod_gain->ndescs != 2) {
+ dev_err(dev, "Invalid 'gain' gpios count: %d",
+ priv->gpiod_gain->ndescs);
+ return -EINVAL;
+ }
+
+ return devm_snd_soc_register_component(dev, &max9759_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max9759_ids[] = {
+ { .compatible = "maxim,max9759", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9759_ids);
+#endif
+
+static struct platform_driver max9759_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(max9759_ids),
+ },
+ .probe = max9759_probe,
+};
+
+module_platform_driver(max9759_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9759 amplifier driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index 5b82e26cd5d1..7ad7a9fb7255 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -1,18 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX9768 AMP driver
*
* Copyright (C) 2011, 2012 by Wolfram Sang, Pengutronix e.K.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -30,8 +27,8 @@
struct max9768 {
struct regmap *regmap;
- int mute_gpio;
- int shdn_gpio;
+ struct gpio_desc *mute;
+ struct gpio_desc *shdn;
u32 flags;
};
@@ -43,9 +40,9 @@ static const struct reg_default max9768_default_regs[] = {
static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
- int val = gpio_get_value_cansleep(max9768->mute_gpio);
+ int val = gpiod_get_value_cansleep(max9768->mute);
ucontrol->value.integer.value[0] = !val;
@@ -55,12 +52,19 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
+ bool val = !ucontrol->value.integer.value[0];
+ int ret;
- gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
+ if (val != gpiod_get_value_cansleep(max9768->mute))
+ ret = 1;
+ else
+ ret = 0;
- return 0;
+ gpiod_set_value_cansleep(max9768->mute, val);
+
+ return ret;
}
static const DECLARE_TLV_DB_RANGE(volume_tlv,
@@ -141,7 +145,7 @@ static int max9768_probe(struct snd_soc_component *component)
return ret;
}
- if (gpio_is_valid(max9768->mute_gpio)) {
+ if (max9768->mute) {
ret = snd_soc_add_component_controls(component, max9768_mute,
ARRAY_SIZE(max9768_mute));
if (ret)
@@ -151,7 +155,7 @@ static int max9768_probe(struct snd_soc_component *component)
return 0;
}
-static struct snd_soc_component_driver max9768_component_driver = {
+static const struct snd_soc_component_driver max9768_component_driver = {
.probe = max9768_probe,
.controls = max9768_volume,
.num_controls = ARRAY_SIZE(max9768_volume),
@@ -170,33 +174,33 @@ static const struct regmap_config max9768_i2c_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9768_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max9768_i2c_probe(struct i2c_client *client)
{
struct max9768 *max9768;
struct max9768_pdata *pdata = client->dev.platform_data;
- int err;
max9768 = devm_kzalloc(&client->dev, sizeof(*max9768), GFP_KERNEL);
if (!max9768)
return -ENOMEM;
- if (pdata) {
- /* Mute on powerup to avoid clicks */
- err = devm_gpio_request_one(&client->dev, pdata->mute_gpio,
- GPIOF_INIT_HIGH, "MAX9768 Mute");
- max9768->mute_gpio = err ?: pdata->mute_gpio;
-
- /* Activate chip by releasing shutdown, enables I2C */
- err = devm_gpio_request_one(&client->dev, pdata->shdn_gpio,
- GPIOF_INIT_HIGH, "MAX9768 Shutdown");
- max9768->shdn_gpio = err ?: pdata->shdn_gpio;
-
+ /* Mute on powerup to avoid clicks */
+ max9768->mute = devm_gpiod_get_optional(&client->dev,
+ "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max9768->mute))
+ return PTR_ERR(max9768->mute);
+ gpiod_set_consumer_name(max9768->mute, "MAX9768 Mute");
+
+ /* Activate chip by releasing shutdown, enables I2C */
+ max9768->shdn = devm_gpiod_get_optional(&client->dev,
+ "shutdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max9768->shdn))
+ return PTR_ERR(max9768->shdn);
+ gpiod_set_consumer_name(max9768->shdn, "MAX9768 Shutdown");
+
+ if (pdata)
max9768->flags = pdata->flags;
- } else {
- max9768->shdn_gpio = -EINVAL;
- max9768->mute_gpio = -EINVAL;
- }
i2c_set_clientdata(client, max9768);
@@ -209,7 +213,7 @@ static int max9768_i2c_probe(struct i2c_client *client,
}
static const struct i2c_device_id max9768_i2c_id[] = {
- { "max9768", 0 },
+ { "max9768" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
@@ -223,6 +227,6 @@ static struct i2c_driver max9768_i2c_driver = {
};
module_i2c_driver(max9768_i2c_driver);
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
MODULE_DESCRIPTION("ASoC MAX9768 amplifier driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 20dcc496d39c..9f40ca4b60d5 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98088.c -- MAX98088 ALSA SoC Audio driver
*
* Copyright 2010 Maxim Integrated Products
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -16,6 +13,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -42,6 +40,8 @@ struct max98088_priv {
struct regmap *regmap;
enum max98088_type devtype;
struct max98088_pdata *pdata;
+ struct clk *mclk;
+ unsigned char mclk_prescaler;
unsigned int sysclk;
struct max98088_cdata dai[2];
int eq_textcnt;
@@ -307,27 +307,27 @@ static const struct regmap_config max98088_regmap = {
/*
* Load equalizer DSP coefficient configurations registers
*/
-static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+static void m98088_eq_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
- unsigned int eq_reg;
- unsigned int i;
+ unsigned int eq_reg;
+ unsigned int i;
if (WARN_ON(band > 4) ||
WARN_ON(dai > 1))
return;
- /* Load the base register address */
- eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+ /* Load the base register address */
+ eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
- /* Add the band address offset, note adjustment for word address */
- eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98088_COEFS_PER_BAND << 1);
- /* Step through the registers and coefs */
- for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
- snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
- }
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+ snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i]));
+ }
}
/*
@@ -380,12 +380,12 @@ static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum,
static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
unsigned int sel = ucontrol->value.integer.value[0];
max98088->mic1pre = sel;
- snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
(1+sel)<<M98088_MICPRE_SHIFT);
return 0;
@@ -394,8 +394,8 @@ static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98088->mic1pre;
return 0;
@@ -404,12 +404,12 @@ static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
unsigned int sel = ucontrol->value.integer.value[0];
max98088->mic2pre = sel;
- snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
(1+sel)<<M98088_MICPRE_SHIFT);
return 0;
@@ -418,8 +418,8 @@ static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98088->mic2pre;
return 0;
@@ -474,15 +474,24 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
max98088_mic2pre_get, max98088_mic2pre_set,
max98088_micboost_tlv),
+ SOC_SINGLE("Noise Gate Threshold", M98088_REG_40_MICAGC_THRESH,
+ 4, 15, 0),
+
SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+ SOC_SINGLE("DACL Volume", M98088_REG_2F_LVL_DAI1_PLAY, 0, 15, 1),
+ SOC_SINGLE("DACR Volume", M98088_REG_31_LVL_DAI2_PLAY, 0, 15, 1),
+
SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+ SOC_SINGLE("Left HP Output Mixer Switch", M98088_REG_27_MIX_HP_CNTL, 4, 1, 0),
+ SOC_SINGLE("Right HP Output Mixer Switch", M98088_REG_27_MIX_HP_CNTL, 5, 1, 0),
+
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
@@ -512,10 +521,8 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
/* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
@@ -526,10 +533,8 @@ static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
/* Right speaker mixer switch */
static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
@@ -540,10 +545,8 @@ static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
/* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
@@ -554,10 +557,8 @@ static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
/* Right headphone mixer switch */
static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
@@ -568,10 +569,8 @@ static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
/* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
@@ -582,10 +581,8 @@ static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
/* Right earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
@@ -617,21 +614,21 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
static int max98088_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (w->reg == M98088_REG_35_LVL_MIC1) {
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK,
(1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
} else {
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK,
(1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
}
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK, 0);
break;
default:
return -EINVAL;
@@ -647,8 +644,8 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
static int max98088_line_pga(struct snd_soc_dapm_widget *w,
int event, int line, u8 channel)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
u8 *state;
if (WARN_ON(!(channel == 1 || channel == 2)))
@@ -668,13 +665,13 @@ static int max98088_line_pga(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
*state |= channel;
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), (1 << w->shift));
break;
case SND_SOC_DAPM_POST_PMD:
*state &= ~channel;
if (*state == 0) {
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), 0);
}
break;
@@ -714,13 +711,9 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
- SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
- M98088_REG_4D_PWR_EN_OUT, 1, 0),
- SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
- M98088_REG_4D_PWR_EN_OUT, 0, 0),
- SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+ SND_SOC_DAPM_DAC("DACL", "HiFi Playback",
M98088_REG_4D_PWR_EN_OUT, 1, 0),
- SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+ SND_SOC_DAPM_DAC("DACR", "HiFi Playback",
M98088_REG_4D_PWR_EN_OUT, 0, 0),
SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
@@ -816,10 +809,8 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */
- {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
- {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
- {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
- {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left HP Mixer", "Left DAC Switch", "DACL"},
+ {"Left HP Mixer", "Right DAC Switch", "DACR"},
{"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left HP Mixer", "INA1 Switch", "INA1 Input"},
@@ -828,10 +819,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Left HP Mixer", "INB2 Switch", "INB2 Input"},
/* Right headphone output mixer */
- {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
- {"Right HP Mixer", "Left DAC2 Switch", "DACL2" },
- {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
- {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right HP Mixer", "Left DAC Switch", "DACL"},
+ {"Right HP Mixer", "Right DAC Switch", "DACR"},
{"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right HP Mixer", "INA1 Switch", "INA1 Input"},
@@ -840,10 +829,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Right HP Mixer", "INB2 Switch", "INB2 Input"},
/* Left speaker output mixer */
- {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
- {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
- {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
- {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left SPK Mixer", "Left DAC Switch", "DACL"},
+ {"Left SPK Mixer", "Right DAC Switch", "DACR"},
{"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
@@ -852,10 +839,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
/* Right speaker output mixer */
- {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
- {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
- {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
- {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right SPK Mixer", "Left DAC Switch", "DACL"},
+ {"Right SPK Mixer", "Right DAC Switch", "DACR"},
{"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
@@ -864,10 +849,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
/* Earpiece/Receiver output mixer */
- {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
- {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
- {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
- {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left REC Mixer", "Left DAC Switch", "DACL"},
+ {"Left REC Mixer", "Right DAC Switch", "DACR"},
{"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left REC Mixer", "INA1 Switch", "INA1 Input"},
@@ -876,10 +859,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"Left REC Mixer", "INB2 Switch", "INB2 Input"},
/* Earpiece/Receiver output mixer */
- {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
- {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
- {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
- {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right REC Mixer", "Left DAC Switch", "DACL"},
+ {"Right REC Mixer", "Right DAC Switch", "DACR"},
{"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right REC Mixer", "INA1 Switch", "INA1 Input"},
@@ -963,8 +944,8 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -976,51 +957,54 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, M98088_DAI_WS);
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE,
+ snd_soc_component_update_bits(component, M98088_REG_11_DAI1_CLKMODE,
M98088_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+ if (snd_soc_component_read(component, M98088_REG_14_DAI1_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
- do_div(ni, (unsigned long long int)max98088->sysclk);
- snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler);
+ ni = DIV_ROUND_CLOSEST_ULL(ni, pclk);
+ snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS,
M98088_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS,
M98088_DAI_DHF, M98088_DAI_DHF);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
M98088_SHDNRUN);
return 0;
@@ -1030,8 +1014,8 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -1043,51 +1027,54 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, M98088_DAI_WS);
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE,
+ snd_soc_component_update_bits(component, M98088_REG_19_DAI2_CLKMODE,
M98088_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+ if (snd_soc_component_read(component, M98088_REG_1C_DAI2_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
- do_div(ni, (unsigned long long int)max98088->sysclk);
- snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler);
+ ni = DIV_ROUND_CLOSEST_ULL(ni, pclk);
+ snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS,
M98088_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS,
M98088_DAI_DHF, M98088_DAI_DHF);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
M98088_SHDNRUN);
return 0;
@@ -1096,30 +1083,37 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
/* Requested clock frequency is already setup */
if (freq == max98088->sysclk)
return 0;
+ if (!IS_ERR(max98088->mclk)) {
+ freq = clk_round_rate(max98088->mclk, freq);
+ clk_set_rate(max98088->mclk, freq);
+ }
+
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 30MHz)..
*/
if ((freq >= 10000000) && (freq < 20000000)) {
- snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+ snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x10);
+ max98088->mclk_prescaler = 1;
} else if ((freq >= 20000000) && (freq < 30000000)) {
- snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+ snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x20);
+ max98088->mclk_prescaler = 2;
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
- if (snd_soc_read(codec, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) {
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ if (snd_soc_component_read(component, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) {
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS,
M98088_SHDNRUN, 0);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS,
M98088_SHDNRUN, M98088_SHDNRUN);
}
@@ -1132,8 +1126,8 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
u8 reg15val;
u8 reg14val = 0;
@@ -1143,22 +1137,20 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
reg14val |= M98088_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1188,14 +1180,14 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
M98088_DAI_WCI, reg14val);
reg15val = M98088_DAI_BSEL64;
if (max98088->digmic)
reg15val |= M98088_DAI_OSR64;
- snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+ snd_soc_component_write(component, M98088_REG_15_DAI1_CLOCK, reg15val);
}
return 0;
@@ -1204,8 +1196,8 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
u8 reg1Cval = 0;
@@ -1214,22 +1206,20 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
reg1Cval |= M98088_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1259,20 +1249,21 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
M98088_DAI_WCI, reg1Cval);
- snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+ snd_soc_component_write(component, M98088_REG_1D_DAI2_CLOCK,
M98088_DAI_BSEL64);
}
return 0;
}
-static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98088_dai1_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int reg;
if (mute)
@@ -1280,14 +1271,15 @@ static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ snd_soc_component_update_bits(component, M98088_REG_2F_LVL_DAI1_PLAY,
M98088_DAI_MUTE_MASK, reg);
return 0;
}
-static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98088_dai2_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int reg;
if (mute)
@@ -1295,33 +1287,51 @@ static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ snd_soc_component_update_bits(component, M98088_REG_31_LVL_DAI2_PLAY,
M98088_DAI_MUTE_MASK, reg);
return 0;
}
-static int max98088_set_bias_level(struct snd_soc_codec *codec,
+static int max98088_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (!IS_ERR(max98088->mclk)) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(max98088->mclk);
+ } else {
+ ret = clk_prepare_enable(max98088->mclk);
+ if (ret)
+ return ret;
+ }
+ }
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
regcache_sync(max98088->regmap);
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, M98088_MBEN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, 0);
regcache_mark_dirty(max98088->regmap);
break;
@@ -1336,14 +1346,16 @@ static const struct snd_soc_dai_ops max98088_dai1_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
- .digital_mute = max98088_dai1_digital_mute,
+ .mute_stream = max98088_dai1_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
- .digital_mute = max98088_dai2_digital_mute,
+ .mute_stream = max98088_dai2_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver max98088_dai[] = {
@@ -1380,22 +1392,19 @@ static struct snd_soc_dai_driver max98088_dai[] = {
static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"};
-static int max98088_get_channel(struct snd_soc_codec *codec, const char *name)
+static int max98088_get_channel(struct snd_soc_component *component, const char *name)
{
- int i;
+ int ret;
- 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;
+ ret = match_string(eq_mode_name, ARRAY_SIZE(eq_mode_name), name);
+ if (ret < 0)
+ dev_err(component->dev, "Bad EQ channel name '%s'\n", name);
+ return ret;
}
-static void max98088_setup_eq1(struct snd_soc_codec *codec)
+static void max98088_setup_eq1(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *coef_set;
int best, best_val, save, i, sel, fs;
@@ -1420,29 +1429,29 @@ static void max98088_setup_eq1(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->eq_cfg[best].name,
pdata->eq_cfg[best].rate, fs);
/* Disable EQ while configuring, and save current on/off state */
- save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+ save = snd_soc_component_read(component, M98088_REG_49_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
coef_set = &pdata->eq_cfg[sel];
- m98088_eq_band(codec, 0, 0, coef_set->band1);
- m98088_eq_band(codec, 0, 1, coef_set->band2);
- m98088_eq_band(codec, 0, 2, coef_set->band3);
- m98088_eq_band(codec, 0, 3, coef_set->band4);
- m98088_eq_band(codec, 0, 4, coef_set->band5);
+ m98088_eq_band(component, 0, 0, coef_set->band1);
+ m98088_eq_band(component, 0, 1, coef_set->band2);
+ m98088_eq_band(component, 0, 2, coef_set->band3);
+ m98088_eq_band(component, 0, 3, coef_set->band4);
+ m98088_eq_band(component, 0, 4, coef_set->band5);
/* Restore the original on/off state */
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
}
-static void max98088_setup_eq2(struct snd_soc_codec *codec)
+static void max98088_setup_eq2(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *coef_set;
int best, best_val, save, i, sel, fs;
@@ -1467,36 +1476,36 @@ static void max98088_setup_eq2(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->eq_cfg[best].name,
pdata->eq_cfg[best].rate, fs);
/* Disable EQ while configuring, and save current on/off state */
- save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+ save = snd_soc_component_read(component, M98088_REG_49_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
coef_set = &pdata->eq_cfg[sel];
- m98088_eq_band(codec, 1, 0, coef_set->band1);
- m98088_eq_band(codec, 1, 1, coef_set->band2);
- m98088_eq_band(codec, 1, 2, coef_set->band3);
- m98088_eq_band(codec, 1, 3, coef_set->band4);
- m98088_eq_band(codec, 1, 4, coef_set->band5);
+ m98088_eq_band(component, 1, 0, coef_set->band1);
+ m98088_eq_band(component, 1, 1, coef_set->band2);
+ m98088_eq_band(component, 1, 2, coef_set->band3);
+ m98088_eq_band(component, 1, 3, coef_set->band4);
+ m98088_eq_band(component, 1, 4, coef_set->band5);
/* Restore the original on/off state */
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
save);
}
static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
- int channel = max98088_get_channel(codec, kcontrol->id.name);
+ int channel = max98088_get_channel(component, kcontrol->id.name);
struct max98088_cdata *cdata;
- int sel = ucontrol->value.integer.value[0];
+ int sel = ucontrol->value.enumerated.item[0];
if (channel < 0)
return channel;
@@ -1510,10 +1519,10 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
switch (channel) {
case 0:
- max98088_setup_eq1(codec);
+ max98088_setup_eq1(component);
break;
case 1:
- max98088_setup_eq2(codec);
+ max98088_setup_eq2(component);
break;
}
@@ -1523,9 +1532,9 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
- int channel = max98088_get_channel(codec, kcontrol->id.name);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
+ int channel = max98088_get_channel(component, kcontrol->id.name);
struct max98088_cdata *cdata;
if (channel < 0)
@@ -1536,9 +1545,9 @@ static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
+static void max98088_handle_eq_pdata(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *cfg;
unsigned int cfgcnt;
@@ -1591,19 +1600,19 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
max98088->eq_enum.texts = max98088->eq_texts;
max98088->eq_enum.items = max98088->eq_textcnt;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+ dev_err(component->dev, "Failed to add EQ control: %d\n", ret);
}
-static void max98088_handle_pdata(struct snd_soc_codec *codec)
+static void max98088_handle_pdata(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
u8 regval = 0;
if (!pdata) {
- dev_dbg(codec->dev, "No platform data\n");
+ dev_dbg(component->dev, "No platform data\n");
return;
}
@@ -1616,21 +1625,21 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
max98088->digmic = (regval ? 1 : 0);
- snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+ snd_soc_component_write(component, M98088_REG_48_CFG_MIC, regval);
/* Configure receiver output */
regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
- snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+ snd_soc_component_update_bits(component, M98088_REG_2A_MIC_REC_CNTL,
M98088_REC_LINEMODE_MASK, regval);
/* Configure equalizers */
if (pdata->eq_cfgcnt)
- max98088_handle_eq_pdata(codec);
+ max98088_handle_eq_pdata(component);
}
-static int max98088_probe(struct snd_soc_codec *codec)
+static int max98088_probe(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
int ret = 0;
@@ -1658,104 +1667,109 @@ static int max98088_probe(struct snd_soc_codec *codec)
max98088->mic1pre = 0;
max98088->mic2pre = 0;
- ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+ ret = snd_soc_component_read(component, M98088_REG_FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
+ dev_err(component->dev, "Failed to read device revision: %d\n",
ret);
goto err_access;
}
- dev_info(codec->dev, "revision %c\n", ret - 0x40 + 'A');
+ dev_info(component->dev, "revision %c\n", ret - 0x40 + 'A');
- snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+ snd_soc_component_write(component, M98088_REG_51_PWR_SYS, M98088_PWRSV);
- snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+ snd_soc_component_write(component, M98088_REG_0F_IRQ_ENABLE, 0x00);
- snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+ snd_soc_component_write(component, M98088_REG_22_MIX_DAC,
M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
- snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
- snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+ snd_soc_component_write(component, M98088_REG_4E_BIAS_CNTL, 0xF0);
+ snd_soc_component_write(component, M98088_REG_50_DAC_BIAS2, 0x0F);
- snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+ snd_soc_component_write(component, M98088_REG_16_DAI1_IOCFG,
M98088_S1NORMAL|M98088_SDATA);
- snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+ snd_soc_component_write(component, M98088_REG_1E_DAI2_IOCFG,
M98088_S2NORMAL|M98088_SDATA);
- max98088_handle_pdata(codec);
+ max98088_handle_pdata(component);
err_access:
return ret;
}
-static int max98088_remove(struct snd_soc_codec *codec)
+static void max98088_remove(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
kfree(max98088->eq_texts);
-
- return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
- .probe = max98088_probe,
- .remove = max98088_remove,
- .set_bias_level = max98088_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = max98088_snd_controls,
- .num_controls = ARRAY_SIZE(max98088_snd_controls),
- .dapm_widgets = max98088_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
- .dapm_routes = max98088_audio_map,
- .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
+static const struct snd_soc_component_driver soc_component_dev_max98088 = {
+ .probe = max98088_probe,
+ .remove = max98088_remove,
+ .set_bias_level = max98088_set_bias_level,
+ .controls = max98088_snd_controls,
+ .num_controls = ARRAY_SIZE(max98088_snd_controls),
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int max98088_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id max98088_i2c_id[] = {
+ { "max98088", MAX98088 },
+ { "max98089", MAX98089 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static int max98088_i2c_probe(struct i2c_client *i2c)
{
- struct max98088_priv *max98088;
- int ret;
+ struct max98088_priv *max98088;
- max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
- GFP_KERNEL);
- if (max98088 == NULL)
- return -ENOMEM;
+ max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
+ GFP_KERNEL);
+ if (max98088 == NULL)
+ return -ENOMEM;
- max98088->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap);
- if (IS_ERR(max98088->regmap))
- return PTR_ERR(max98088->regmap);
+ max98088->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap);
+ if (IS_ERR(max98088->regmap))
+ return PTR_ERR(max98088->regmap);
- max98088->devtype = id->driver_data;
+ max98088->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(max98088->mclk))
+ if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
+ return PTR_ERR(max98088->mclk);
- i2c_set_clientdata(i2c, max98088);
- max98088->pdata = i2c->dev.platform_data;
+ max98088->devtype = (uintptr_t)i2c_get_match_data(i2c);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98088, &max98088_dai[0], 2);
- return ret;
-}
+ i2c_set_clientdata(i2c, max98088);
+ max98088->pdata = i2c->dev.platform_data;
-static int max98088_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_max98088,
+ &max98088_dai[0], 2);
}
-static const struct i2c_device_id max98088_i2c_id[] = {
- { "max98088", MAX98088 },
- { "max98089", MAX98089 },
- { }
+#if defined(CONFIG_OF)
+static const struct of_device_id max98088_of_match[] = {
+ { .compatible = "maxim,max98088" },
+ { .compatible = "maxim,max98089" },
+ { }
};
-MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+MODULE_DEVICE_TABLE(of, max98088_of_match);
+#endif
static struct i2c_driver max98088_i2c_driver = {
.driver = {
.name = "max98088",
+ .of_match_table = of_match_ptr(max98088_of_match),
},
- .probe = max98088_i2c_probe,
- .remove = max98088_i2c_remove,
+ .probe = max98088_i2c_probe,
.id_table = max98088_i2c_id,
};
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index efa39bf46742..4190e5ff38f9 100644
--- a/sound/soc/codecs/max98088.h
+++ b/sound/soc/codecs/max98088.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98088.h -- MAX98088 ALSA SoC Audio driver
*
* Copyright 2010 Maxim Integrated Products
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _MAX98088_H
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 584aab83e478..13a15459040f 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98090.c -- MAX98090 ALSA SoC Audio driver
*
* Copyright 2011-2012 Maxim Integrated Products
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
@@ -13,7 +10,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/acpi.h>
@@ -284,7 +280,7 @@ static int max98090_reset(struct max98090_priv *max98090)
ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET,
M98090_SWRESET_MASK);
if (ret < 0) {
- dev_err(max98090->codec->dev,
+ dev_err(max98090->component->dev,
"Failed to reset codec: %d\n", ret);
return ret;
}
@@ -314,9 +310,6 @@ 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);
@@ -354,12 +347,12 @@ static const DECLARE_TLV_DB_RANGE(max98090_rcv_lout_tlv,
static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mask = (1 << fls(mc->max)) - 1;
- unsigned int val = snd_soc_read(codec, mc->reg);
+ unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int *select;
switch (mc->reg) {
@@ -394,14 +387,16 @@ static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mask = (1 << fls(mc->max)) - 1;
- unsigned int sel = ucontrol->value.integer.value[0];
- unsigned int val = snd_soc_read(codec, mc->reg);
+ int sel_unchecked = ucontrol->value.integer.value[0];
+ unsigned int sel;
+ unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int *select;
+ int change;
switch (mc->reg) {
case M98090_REG_MIC1_INPUT_LEVEL:
@@ -419,6 +414,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
val = (val >> mc->shift) & mask;
+ if (sel_unchecked < 0 || sel_unchecked > mc->max)
+ return -EINVAL;
+ sel = sel_unchecked;
+
+ change = *select != sel;
*select = sel;
/* Setting a volume is only valid if it is already On */
@@ -429,11 +429,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
sel = val;
}
- snd_soc_update_bits(codec, mc->reg,
+ snd_soc_component_update_bits(component, mc->reg,
mask << mc->shift,
sel << mc->shift);
- return 0;
+ return change;
}
static const char *max98090_perf_pwr_text[] =
@@ -733,10 +733,10 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
- unsigned int val = snd_soc_read(codec, w->reg);
+ unsigned int val = snd_soc_component_read(component, w->reg);
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT;
@@ -768,10 +768,10 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
}
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
- snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA1EN_MASK,
val << M98090_MIC_PA1EN_SHIFT);
else
- snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA2EN_MASK,
val << M98090_MIC_PA2EN_SHIFT);
return 0;
@@ -780,8 +780,8 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
static int max98090_shdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
if (event & SND_SOC_DAPM_POST_PMU)
max98090->shdn_pending = true;
@@ -817,17 +817,15 @@ 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 const char * const dmic_mX_mux_text[] = { "Enable", "Disable" };
-static SOC_ENUM_SINGLE_DECL(max98090_pa1en_enum,
- M98090_REG_MIC1_INPUT_LEVEL,
- M98090_MIC_PA1EN_SHIFT,
- max98090_micpre_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(dmic_m1_enum, dmic_mX_mux_text);
+static const struct snd_kcontrol_new max98090_dmic_m1_mux =
+ SOC_DAPM_ENUM("DMIC M1 Mux", dmic_m1_enum);
-static SOC_ENUM_SINGLE_DECL(max98090_pa2en_enum,
- M98090_REG_MIC2_INPUT_LEVEL,
- M98090_MIC_PA2EN_SHIFT,
- max98090_micpre_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(dmic_m2_enum, dmic_mX_mux_text);
+static const struct snd_kcontrol_new max98090_dmic_m2_mux =
+ SOC_DAPM_ENUM("DMIC M2 Mux", dmic_m2_enum);
/* LINEA mixer switch */
static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = {
@@ -1118,6 +1116,9 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &max98090_dmic_mux),
+ SND_SOC_DAPM_MUX("DMIC M1 Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_dmic_m1_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),
@@ -1156,7 +1157,7 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
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_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM,
@@ -1209,14 +1210,14 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
&max98090_right_rcv_mixer_controls[0],
ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
- SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER,
- M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux),
+ SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_linmod_mux),
- SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL,
- M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux),
+ SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_mixhplsel_mux),
- SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL,
- M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux),
+ SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_mixhprsel_mux),
SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
M98090_HPLEN_SHIFT, 0, NULL, 0),
@@ -1246,9 +1247,21 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("DMIC4"),
SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
- M98090_DIGMIC3_SHIFT, 0, NULL, 0),
+ M98090_DIGMIC3_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
- M98090_DIGMIC4_SHIFT, 0, NULL, 0),
+ M98090_DIGMIC4_SHIFT, 0, max98090_shdn_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC34_HPF", M98090_REG_FILTER_CONFIG,
+ M98090_FLT_DMIC34HPF_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DMIC M2 Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_dmic_m2_mux),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUT2L", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFOUT2R", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
@@ -1312,10 +1325,12 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"ADCL", NULL, "SHDN"},
{"ADCR", NULL, "SHDN"},
+ {"DMIC M1 Mux", "Enable", "DMICL"},
+ {"DMIC M1 Mux", "Enable", "DMICR"},
+
{"DMIC Mux", "ADC", "ADCL"},
{"DMIC Mux", "ADC", "ADCR"},
- {"DMIC Mux", "DMIC", "DMICL"},
- {"DMIC Mux", "DMIC", "DMICR"},
+ {"DMIC Mux", "DMIC", "DMIC M1 Mux"},
{"LBENL Mux", "Normal", "DMIC Mux"},
{"LBENL Mux", "Loopback", "LTENL Mux"},
@@ -1437,20 +1452,30 @@ 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"},
+ {"DMIC3", NULL, "DMIC34_HPF"},
+ {"DMIC4", NULL, "DMIC34_HPF"},
+
+ {"DMIC M2 Mux", "Enable", "DMIC3"},
+ {"DMIC M2 Mux", "Enable", "DMIC4"},
+
+ {"AIFOUT2L", NULL, "DMIC M2 Mux"},
+ {"AIFOUT2R", NULL, "DMIC M2 Mux"},
+ {"AIFOUT2L", NULL, "SHDN"},
+ {"AIFOUT2R", NULL, "SHDN"},
+ {"AIFOUT2L", NULL, "SDOEN"},
+ {"AIFOUT2R", NULL, "SDOEN"},
};
-static int max98090_add_widgets(struct snd_soc_codec *codec)
+static int max98090_add_widgets(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- snd_soc_add_codec_controls(codec, max98090_snd_controls,
+ snd_soc_add_component_controls(component, max98090_snd_controls,
ARRAY_SIZE(max98090_snd_controls));
if (max98090->devtype == MAX98091) {
- snd_soc_add_codec_controls(codec, max98091_snd_controls,
+ snd_soc_add_component_controls(component, max98091_snd_controls,
ARRAY_SIZE(max98091_snd_controls));
}
@@ -1497,24 +1522,24 @@ static const unsigned long long mi_value[] = {
8125, 1625, 1500, 25
};
-static void max98090_configure_bclk(struct snd_soc_codec *codec)
+static void max98090_configure_bclk(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
unsigned long long ni;
int i;
if (!max98090->sysclk) {
- dev_err(codec->dev, "No SYSCLK configured\n");
+ dev_err(component->dev, "No SYSCLK configured\n");
return;
}
if (!max98090->bclk || !max98090->lrclk) {
- dev_err(codec->dev, "No audio clocks configured\n");
+ dev_err(component->dev, "No audio clocks configured\n");
return;
}
/* Skip configuration when operating as slave */
- if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) &
+ if (!(snd_soc_component_read(component, M98090_REG_MASTER_MODE) &
M98090_MAS_MASK)) {
return;
}
@@ -1523,14 +1548,14 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
if ((pclk_rates[i] == max98090->sysclk) &&
(lrclk_rates[i] == max98090->lrclk)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Found supported PCLK to LRCLK rates 0x%x\n",
i + 0x8);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK,
(i + 0x8) << M98090_FREQ_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
return;
}
@@ -1540,24 +1565,24 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) {
if ((user_pclk_rates[i] == max98090->sysclk) &&
(user_lrclk_rates[i] == max98090->lrclk)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Found user supported PCLK to LRCLK rates\n");
- dev_dbg(codec->dev, "i %d ni %lld mi %lld\n",
+ dev_dbg(component->dev, "i %d ni %lld mi %lld\n",
i, ni_value[i], mi_value[i]);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK, 0);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK,
1 << M98090_USE_M1_SHIFT);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB,
(ni_value[i] >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB,
ni_value[i] & 0xFF);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_MSB,
(mi_value[i] >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_LSB,
mi_value[i] & 0xFF);
return;
@@ -1567,9 +1592,9 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
/*
* Calculate based on MI = 65536 (not as good as either method above)
*/
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK, 0);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
/*
@@ -1580,20 +1605,20 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)max98090->lrclk;
do_div(ni, (unsigned long long int)max98090->sysclk);
- dev_info(codec->dev, "No better method found\n");
- dev_info(codec->dev, "Calculating ni %lld with mi 65536\n", ni);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ dev_info(component->dev, "No better method found\n");
+ dev_info(component->dev, "Calculating ni %lld with mi 65536\n", ni);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF);
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF);
}
static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct max98090_cdata *cdata;
- u8 regval;
+ u8 regval, tdm_regval;
max98090->dai_fmt = fmt;
cdata = &max98090->dai[0];
@@ -1602,19 +1627,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
cdata->fmt = fmt;
regval = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Set to slave mode PLL - MAS mode off */
- snd_soc_write(codec,
+ tdm_regval = 0;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Set to consumer mode PLL - MAS mode off */
+ snd_soc_component_write(component,
M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
- snd_soc_write(codec,
+ snd_soc_component_write(component,
M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
max98090->master = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
if (max98090->tdm_slots == 4) {
/* TDM */
regval |= M98090_MAS_MASK |
@@ -1630,13 +1656,11 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
}
max98090->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(component->dev, "DAI clock mode unsupported");
return -EINVAL;
}
- snd_soc_write(codec, M98090_REG_MASTER_MODE, regval);
+ snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
regval = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -1649,9 +1673,10 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_RJ_MASK;
break;
case SND_SOC_DAIFMT_DSP_A:
- /* Not supported mode */
+ tdm_regval |= M98090_TDM_MASK;
+ break;
default:
- dev_err(codec->dev, "DAI format unsupported");
+ dev_err(component->dev, "DAI format unsupported");
return -EINVAL;
}
@@ -1668,7 +1693,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_BCI_MASK|M98090_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported");
+ dev_err(component->dev, "DAI invert mode unsupported");
return -EINVAL;
}
@@ -1678,11 +1703,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
* seen for the case of TDM mode. The remaining cases have
* normal logic.
*/
- if (max98090->tdm_slots > 1)
+ if (tdm_regval)
regval ^= M98090_BCI_MASK;
- snd_soc_write(codec,
+ snd_soc_component_write(component,
M98090_REG_INTERFACE_FORMAT, regval);
+
+ regval = 0;
+ if (tdm_regval)
+ regval = max98090->tdm_lslot << M98090_TDM_SLOTL_SHIFT |
+ max98090->tdm_rslot << M98090_TDM_SLOTR_SHIFT |
+ 0 << M98090_TDM_SLOTDLY_SHIFT;
+
+ snd_soc_component_write(component, M98090_REG_TDM_FORMAT, regval);
+ snd_soc_component_write(component, M98090_REG_TDM_CONTROL, tdm_regval);
}
return 0;
@@ -1691,43 +1725,33 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct max98090_cdata *cdata;
- cdata = &max98090->dai[0];
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
if (slots < 0 || slots > 4)
return -EINVAL;
- max98090->tdm_slots = slots;
- max98090->tdm_width = slot_width;
-
- if (max98090->tdm_slots > 1) {
- /* SLOTL SLOTR SLOTDLY */
- snd_soc_write(codec, M98090_REG_TDM_FORMAT,
- 0 << M98090_TDM_SLOTL_SHIFT |
- 1 << M98090_TDM_SLOTR_SHIFT |
- 0 << M98090_TDM_SLOTDLY_SHIFT);
-
- /* FSW TDM */
- snd_soc_update_bits(codec, M98090_REG_TDM_CONTROL,
- M98090_TDM_MASK,
- M98090_TDM_MASK);
- }
+ if (slot_width != 16)
+ return -EINVAL;
- /*
- * Normally advisable to set TDM first, but this permits either order
- */
- cdata->fmt = 0;
- max98090_dai_set_fmt(codec_dai, max98090->dai_fmt);
+ if (rx_mask != tx_mask)
+ return -EINVAL;
+
+ if (!rx_mask)
+ return -EINVAL;
+
+ max98090->tdm_slots = slots;
+ max98090->tdm_lslot = ffs(rx_mask) - 1;
+ max98090->tdm_rslot = fls(rx_mask) - 1;
return 0;
}
-static int max98090_set_bias_level(struct snd_soc_codec *codec,
+static int max98090_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -1745,7 +1769,7 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
if (IS_ERR(max98090->mclk))
break;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(max98090->mclk);
} else {
ret = clk_prepare_enable(max98090->mclk);
@@ -1755,10 +1779,10 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max98090->regmap);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
@@ -1767,7 +1791,7 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
/* Set internal pull-up to lowest power mode */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, M98090_JDWK_MASK);
regcache_mark_dirty(max98090->regmap);
break;
@@ -1850,7 +1874,7 @@ static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
static int max98090_find_divisor(int target_freq, int pclk)
{
int current_diff = INT_MAX;
- int test_diff = INT_MAX;
+ int test_diff;
int divisor_index = 0;
int i;
@@ -1924,12 +1948,27 @@ static int max98090_configure_dmic(struct max98090_priv *max98090,
return 0;
}
+static int max98090_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
+ unsigned int fmt = max98090->dai_fmt;
+
+ /* Remove 24-bit format support if it is not in right justified mode. */
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_RIGHT_J) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(substream->runtime, 0, 16, 16);
+ }
+ return 0;
+}
+
static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct max98090_cdata *cdata;
cdata = &max98090->dai[0];
@@ -1941,7 +1980,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
+ snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
M98090_WS_MASK, 0);
break;
default:
@@ -1949,24 +1988,24 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
}
if (max98090->master)
- max98090_configure_bclk(codec);
+ max98090_configure_bclk(component);
cdata->rate = max98090->lrclk;
/* Update filter mode */
if (max98090->lrclk < 24000)
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_MODE_MASK, 0);
else
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_MODE_MASK, M98090_MODE_MASK);
/* Update sample rate mode */
if (max98090->lrclk < 50000)
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_DHF_MASK, 0);
else
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_DHF_MASK, M98090_DHF_MASK);
max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
@@ -1981,8 +2020,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
/* Requested clock frequency is already setup */
if (freq == max98090->sysclk)
@@ -1999,19 +2038,19 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if ((freq >= 10000000) && (freq <= 20000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV1);
max98090->pclk = freq;
} else if ((freq > 20000000) && (freq <= 40000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV2);
max98090->pclk = freq >> 1;
} else if ((freq > 40000000) && (freq <= 60000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV4);
max98090->pclk = freq >> 2;
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
@@ -2020,13 +2059,14 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98090_dai_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int regval;
regval = mute ? M98090_DVM_MASK : 0;
- snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL,
+ snd_soc_component_update_bits(component, M98090_REG_DAI_PLAYBACK_LEVEL,
M98090_DVM_MASK, regval);
return 0;
@@ -2035,14 +2075,14 @@ static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!max98090->master && dai->active == 1)
+ if (!max98090->master && snd_soc_dai_active(dai) == 1)
queue_delayed_work(system_power_efficient_wq,
&max98090->pll_det_enable_work,
msecs_to_jiffies(10));
@@ -2050,7 +2090,7 @@ static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (!max98090->master && dai->active == 1)
+ if (!max98090->master && snd_soc_dai_active(dai) == 1)
schedule_work(&max98090->pll_det_disable_work);
break;
default:
@@ -2065,7 +2105,7 @@ static void max98090_pll_det_enable_work(struct work_struct *work)
struct max98090_priv *max98090 =
container_of(work, struct max98090_priv,
pll_det_enable_work.work);
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
unsigned int status, mask;
/*
@@ -2088,7 +2128,7 @@ static void max98090_pll_det_enable_work(struct work_struct *work)
msecs_to_jiffies(100));
/* Enable PLL unlock interrupt */
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IULK_MASK,
1 << M98090_IULK_SHIFT);
}
@@ -2097,35 +2137,49 @@ static void max98090_pll_det_disable_work(struct work_struct *work)
{
struct max98090_priv *max98090 =
container_of(work, struct max98090_priv, pll_det_disable_work);
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
/* Disable PLL unlock interrupt */
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IULK_MASK, 0);
}
-static void max98090_pll_work(struct work_struct *work)
+static void max98090_pll_work(struct max98090_priv *max98090)
{
- struct max98090_priv *max98090 =
- container_of(work, struct max98090_priv, pll_work);
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
+ unsigned int pll;
+ int i;
- if (!snd_soc_codec_is_active(codec))
+ if (!snd_soc_component_active(component))
return;
- dev_info(codec->dev, "PLL unlocked\n");
+ dev_info_ratelimited(component->dev, "PLL unlocked\n");
+
+ /*
+ * As the datasheet suggested, the maximum PLL lock time should be
+ * 7 msec. The workaround resets the codec softly by toggling SHDN
+ * off and on if PLL failed to lock for 10 msec. Notably, there is
+ * no suggested hold time for SHDN off.
+ */
/* Toggle shutdown OFF then ON */
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, 0);
- msleep(10);
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, M98090_SHDNN_MASK);
- /* Give PLL time to lock */
- msleep(10);
+ for (i = 0; i < 10; ++i) {
+ /* Give PLL time to lock */
+ usleep_range(1000, 1200);
+
+ /* Check lock status */
+ pll = snd_soc_component_read(
+ component, M98090_REG_DEVICE_STATUS);
+ if (!(pll & M98090_ULK_MASK))
+ break;
+ }
}
static void max98090_jack_work(struct work_struct *work)
@@ -2133,7 +2187,7 @@ static void max98090_jack_work(struct work_struct *work)
struct max98090_priv *max98090 = container_of(work,
struct max98090_priv,
jack_work.work);
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
int status = 0;
int reg;
@@ -2141,25 +2195,23 @@ static void max98090_jack_work(struct work_struct *work)
if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) {
/* Strong pull up allows mic detection */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, 0);
msleep(50);
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+ snd_soc_component_read(component, M98090_REG_JACK_STATUS);
/* Weak pull up allows only insertion detection */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, M98090_JDWK_MASK);
- } else {
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
}
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+ reg = snd_soc_component_read(component, M98090_REG_JACK_STATUS);
switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) {
case M98090_LSNS_MASK | M98090_JKSNS_MASK:
- dev_dbg(codec->dev, "No Headset Detected\n");
+ dev_dbg(component->dev, "No Headset Detected\n");
max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
@@ -2171,7 +2223,7 @@ static void max98090_jack_work(struct work_struct *work)
if (max98090->jack_state ==
M98090_JACK_STATE_HEADSET) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Headset Button Down Detected\n");
/*
@@ -2188,7 +2240,7 @@ static void max98090_jack_work(struct work_struct *work)
/* Line is reported as Headphone */
/* Nokia Headset is reported as Headphone */
/* Mono Headphone is reported as Headphone */
- dev_dbg(codec->dev, "Headphone Detected\n");
+ dev_dbg(component->dev, "Headphone Detected\n");
max98090->jack_state = M98090_JACK_STATE_HEADPHONE;
@@ -2197,7 +2249,7 @@ static void max98090_jack_work(struct work_struct *work)
break;
case M98090_JKSNS_MASK:
- dev_dbg(codec->dev, "Headset Detected\n");
+ dev_dbg(component->dev, "Headset Detected\n");
max98090->jack_state = M98090_JACK_STATE_HEADSET;
@@ -2206,7 +2258,7 @@ static void max98090_jack_work(struct work_struct *work)
break;
default:
- dev_dbg(codec->dev, "Unrecognized Jack Status\n");
+ dev_dbg(component->dev, "Unrecognized Jack Status\n");
break;
}
@@ -2217,21 +2269,21 @@ static void max98090_jack_work(struct work_struct *work)
static irqreturn_t max98090_interrupt(int irq, void *data)
{
struct max98090_priv *max98090 = data;
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
int ret;
unsigned int mask;
unsigned int active;
/* Treat interrupt before codec is initialized as spurious */
- if (codec == NULL)
+ if (component == NULL)
return IRQ_NONE;
- dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
+ dev_dbg(component->dev, "***** max98090_interrupt *****\n");
ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to read M98090_REG_INTERRUPT_S: %d\n",
ret);
return IRQ_NONE;
@@ -2240,13 +2292,13 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to read M98090_REG_DEVICE_STATUS: %d\n",
ret);
return IRQ_NONE;
}
- dev_dbg(codec->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
+ dev_dbg(component->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
active, mask, active & mask);
active &= mask;
@@ -2255,20 +2307,20 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
return IRQ_NONE;
if (active & M98090_CLD_MASK)
- dev_err(codec->dev, "M98090_CLD_MASK\n");
+ dev_err(component->dev, "M98090_CLD_MASK\n");
if (active & M98090_SLD_MASK)
- dev_dbg(codec->dev, "M98090_SLD_MASK\n");
+ dev_dbg(component->dev, "M98090_SLD_MASK\n");
if (active & M98090_ULK_MASK) {
- dev_dbg(codec->dev, "M98090_ULK_MASK\n");
- schedule_work(&max98090->pll_work);
+ dev_dbg(component->dev, "M98090_ULK_MASK\n");
+ max98090_pll_work(max98090);
}
if (active & M98090_JDET_MASK) {
- dev_dbg(codec->dev, "M98090_JDET_MASK\n");
+ dev_dbg(component->dev, "M98090_JDET_MASK\n");
- pm_wakeup_event(codec->dev, 100);
+ pm_wakeup_event(component->dev, 100);
queue_delayed_work(system_power_efficient_wq,
&max98090->jack_work,
@@ -2276,10 +2328,10 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
}
if (active & M98090_DRCACT_MASK)
- dev_dbg(codec->dev, "M98090_DRCACT_MASK\n");
+ dev_dbg(component->dev, "M98090_DRCACT_MASK\n");
if (active & M98090_DRCCLP_MASK)
- dev_err(codec->dev, "M98090_DRCCLP_MASK\n");
+ dev_err(component->dev, "M98090_DRCCLP_MASK\n");
return IRQ_HANDLED;
}
@@ -2287,7 +2339,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
/**
* max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
*
- * @codec: MAX98090 codec
+ * @component: MAX98090 component
* @jack: jack to report detection events on
*
* Enable microphone detection via IRQ on the MAX98090. If GPIOs are
@@ -2297,20 +2349,20 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
*
* If no jack is supplied detection will be disabled.
*/
-int max98090_mic_detect(struct snd_soc_codec *codec,
+int max98090_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "max98090_mic_detect\n");
+ dev_dbg(component->dev, "max98090_mic_detect\n");
max98090->jack = jack;
if (jack) {
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IJDET_MASK,
1 << M98090_IJDET_SHIFT);
} else {
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IJDET_MASK,
0);
}
@@ -2331,16 +2383,17 @@ EXPORT_SYMBOL_GPL(max98090_mic_detect);
#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops max98090_dai_ops = {
+ .startup = max98090_dai_startup,
.set_sysclk = max98090_dai_set_sysclk,
.set_fmt = max98090_dai_set_fmt,
.set_tdm_slot = max98090_set_tdm_slot,
.hw_params = max98090_dai_hw_params,
- .digital_mute = max98090_dai_digital_mute,
+ .mute_stream = max98090_dai_mute,
.trigger = max98090_dai_trigger,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_driver max98090_dai[] = {
-{
+static struct snd_soc_dai_driver max98090_dai = {
.name = "HiFi",
.playback = {
.stream_name = "HiFi Playback",
@@ -2352,30 +2405,29 @@ static struct snd_soc_dai_driver max98090_dai[] = {
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.rates = MAX98090_RATES,
.formats = MAX98090_FORMATS,
},
- .ops = &max98090_dai_ops,
-}
+ .ops = &max98090_dai_ops,
};
-static int max98090_probe(struct snd_soc_codec *codec)
+static int max98090_probe(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct max98090_cdata *cdata;
enum max98090_type devtype;
int ret = 0;
int err;
unsigned int micbias;
- dev_dbg(codec->dev, "max98090_probe\n");
+ dev_dbg(component->dev, "max98090_probe\n");
- max98090->mclk = devm_clk_get(codec->dev, "mclk");
+ max98090->mclk = devm_clk_get(component->dev, "mclk");
if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- max98090->codec = codec;
+ max98090->component = component;
/* Reset the codec, the DSP core, and disable all interrupts */
max98090_reset(max98090);
@@ -2394,26 +2446,29 @@ static int max98090_probe(struct snd_soc_codec *codec)
max98090->pa1en = 0;
max98090->pa2en = 0;
- ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
+ max98090->tdm_lslot = 0;
+ max98090->tdm_rslot = 1;
+
+ ret = snd_soc_component_read(component, M98090_REG_REVISION_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
+ dev_err(component->dev, "Failed to read device revision: %d\n",
ret);
goto err_access;
}
if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
devtype = MAX98090;
- dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
+ dev_info(component->dev, "MAX98090 REVID=0x%02x\n", ret);
} else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
devtype = MAX98091;
- dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
+ dev_info(component->dev, "MAX98091 REVID=0x%02x\n", ret);
} else {
devtype = MAX98090;
- dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
+ dev_err(component->dev, "Unrecognized revision 0x%02x\n", ret);
}
if (max98090->devtype != devtype) {
- dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
+ dev_warn(component->dev, "Mismatch in DT specified CODEC type.\n");
max98090->devtype = devtype;
}
@@ -2424,10 +2479,9 @@ static int max98090_probe(struct snd_soc_codec *codec)
max98090_pll_det_enable_work);
INIT_WORK(&max98090->pll_det_disable_work,
max98090_pll_det_disable_work);
- INIT_WORK(&max98090->pll_work, max98090_pll_work);
/* Enable jack detection */
- snd_soc_write(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_write(component, M98090_REG_JACK_DETECT,
M98090_JDETEN_MASK | M98090_JDEB_25MS);
/*
@@ -2435,75 +2489,74 @@ static int max98090_probe(struct snd_soc_codec *codec)
* An old interrupt ocurring prior to installing the ISR
* can keep a new interrupt from generating a trigger.
*/
- snd_soc_read(codec, M98090_REG_DEVICE_STATUS);
+ snd_soc_component_read(component, M98090_REG_DEVICE_STATUS);
/* High Performance is default */
- snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
M98090_DACHP_MASK,
1 << M98090_DACHP_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
M98090_PERFMODE_MASK,
0 << M98090_PERFMODE_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_ADC_CONTROL,
M98090_ADCHP_MASK,
1 << M98090_ADCHP_SHIFT);
/* Turn on VCM bandgap reference */
- snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
+ snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
M98090_VCM_MODE_MASK);
- err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias);
+ err = device_property_read_u32(component->dev, "maxim,micbias", &micbias);
if (err) {
micbias = M98090_MBVSEL_2V8;
- dev_info(codec->dev, "use default 2.8v micbias\n");
- } else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) {
- dev_err(codec->dev, "micbias out of range 0x%x\n", micbias);
+ dev_info(component->dev, "use default 2.8v micbias\n");
+ } else if (micbias > M98090_MBVSEL_2V8) {
+ dev_err(component->dev, "micbias out of range 0x%x\n", micbias);
micbias = M98090_MBVSEL_2V8;
}
- snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
+ snd_soc_component_update_bits(component, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, micbias);
- max98090_add_widgets(codec);
+ max98090_add_widgets(component);
err_access:
return ret;
}
-static int max98090_remove(struct snd_soc_codec *codec)
+static void max98090_remove(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&max98090->jack_work);
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
cancel_work_sync(&max98090->pll_det_disable_work);
- cancel_work_sync(&max98090->pll_work);
- max98090->codec = NULL;
-
- return 0;
+ max98090->component = NULL;
}
-static void max98090_seq_notifier(struct snd_soc_dapm_context *dapm,
+static void max98090_seq_notifier(struct snd_soc_component *component,
enum snd_soc_dapm_type event, int subseq)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
if (max98090->shdn_pending) {
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, 0);
msleep(40);
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, M98090_SHDNN_MASK);
max98090->shdn_pending = false;
}
}
-static struct snd_soc_codec_driver soc_codec_dev_max98090 = {
- .probe = max98090_probe,
- .remove = max98090_remove,
- .seq_notifier = max98090_seq_notifier,
- .set_bias_level = max98090_set_bias_level,
+static const struct snd_soc_component_driver soc_component_dev_max98090 = {
+ .probe = max98090_probe,
+ .remove = max98090_remove,
+ .seq_notifier = max98090_seq_notifier,
+ .set_bias_level = max98090_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config max98090_regmap = {
@@ -2518,12 +2571,16 @@ static const struct regmap_config max98090_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98090_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static const struct i2c_device_id max98090_i2c_id[] = {
+ { "max98090", MAX98090 },
+ { "max98091", MAX98091 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
+
+static int max98090_i2c_probe(struct i2c_client *i2c)
{
struct max98090_priv *max98090;
- const struct acpi_device_id *acpi_id;
- kernel_ulong_t driver_data = 0;
int ret;
pr_debug("max98090_i2c_probe\n");
@@ -2533,19 +2590,7 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
if (max98090 == NULL)
return -ENOMEM;
- if (ACPI_HANDLE(&i2c->dev)) {
- acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
- &i2c->dev);
- if (!acpi_id) {
- dev_err(&i2c->dev, "No driver data\n");
- return -EINVAL;
- }
- driver_data = acpi_id->driver_data;
- } else if (i2c_id) {
- driver_data = i2c_id->driver_data;
- }
-
- max98090->devtype = driver_data;
+ max98090->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98090);
max98090->pdata = i2c->dev.platform_data;
@@ -2570,9 +2615,9 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98090, max98090_dai,
- ARRAY_SIZE(max98090_dai));
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98090,
+ &max98090_dai, 1);
err_enable:
return ret;
}
@@ -2592,14 +2637,11 @@ static void max98090_i2c_shutdown(struct i2c_client *i2c)
msleep(40);
}
-static int max98090_i2c_remove(struct i2c_client *client)
+static void 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);
@@ -2621,9 +2663,7 @@ static int max98090_runtime_suspend(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int max98090_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2641,31 +2681,19 @@ static int max98090_resume(struct device *dev)
return 0;
}
-static int max98090_suspend(struct device *dev)
-{
- return 0;
-}
-#endif
-
static const struct dev_pm_ops max98090_pm = {
- SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
- max98090_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(max98090_suspend, max98090_resume)
+ RUNTIME_PM_OPS(max98090_runtime_suspend, max98090_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
};
-static const struct i2c_device_id max98090_i2c_id[] = {
- { "max98090", MAX98090 },
- { "max98091", MAX98091 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
-
+#ifdef CONFIG_OF
static const struct of_device_id max98090_of_match[] = {
{ .compatible = "maxim,max98090", },
{ .compatible = "maxim,max98091", },
{ }
};
MODULE_DEVICE_TABLE(of, max98090_of_match);
+#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id max98090_acpi_match[] = {
@@ -2678,11 +2706,11 @@ MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
static struct i2c_driver max98090_i2c_driver = {
.driver = {
.name = "max98090",
- .pm = &max98090_pm,
+ .pm = pm_ptr(&max98090_pm),
.of_match_table = of_match_ptr(max98090_of_match),
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
},
- .probe = max98090_i2c_probe,
+ .probe = max98090_i2c_probe,
.shutdown = max98090_i2c_shutdown,
.remove = max98090_i2c_remove,
.id_table = max98090_i2c_id,
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index bc610d9a9ecb..6ce8dd176e48 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98090.h -- MAX98090 ALSA SoC Audio driver
*
* Copyright 2011-2012 Maxim Integrated Products
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _MAX98090_H
@@ -1519,7 +1516,7 @@ struct max98090_cdata {
struct max98090_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
enum max98090_type devtype;
struct max98090_pdata *pdata;
struct clk *mclk;
@@ -1533,11 +1530,11 @@ struct max98090_priv {
struct delayed_work jack_work;
struct delayed_work pll_det_enable_work;
struct work_struct pll_det_disable_work;
- struct work_struct pll_work;
struct snd_soc_jack *jack;
unsigned int dai_fmt;
int tdm_slots;
- int tdm_width;
+ int tdm_lslot;
+ int tdm_rslot;
u8 lin_state;
unsigned int pa1en;
unsigned int pa2en;
@@ -1546,7 +1543,7 @@ struct max98090_priv {
bool shdn_pending;
};
-int max98090_mic_detect(struct snd_soc_codec *codec,
+int max98090_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
#endif
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 1fedac50355e..aae6423156e1 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98095.c -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 Maxim Integrated Products
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -251,7 +248,7 @@ static const struct regmap_config max98095_regmap = {
/*
* Load equalizer DSP coefficient configurations registers
*/
-static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+static void m98095_eq_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int eq_reg;
@@ -269,15 +266,15 @@ static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
- snd_soc_write(codec, eq_reg++, M98095_BYTE0(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_component_write(component, eq_reg++, M98095_BYTE0(coefs[i]));
}
}
/*
* Load biquad filter coefficient configurations registers
*/
-static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
+static void m98095_biquad_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int bq_reg;
@@ -295,8 +292,8 @@ static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
- snd_soc_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE0(coefs[i]));
}
}
@@ -353,12 +350,12 @@ static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum,
static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
unsigned int sel = ucontrol->value.integer.value[0];
max98095->mic1pre = sel;
- snd_soc_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
(1+sel)<<M98095_MICPRE_SHIFT);
return 0;
@@ -367,8 +364,8 @@ static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98095->mic1pre;
return 0;
@@ -377,12 +374,12 @@ static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
unsigned int sel = ucontrol->value.integer.value[0];
max98095->mic2pre = sel;
- snd_soc_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
(1+sel)<<M98095_MICPRE_SHIFT);
return 0;
@@ -391,8 +388,8 @@ static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98095->mic2pre;
return 0;
@@ -598,21 +595,21 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
static int max98095_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (w->reg == M98095_05F_LVL_MIC1) {
- snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
(1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
} else {
- snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
(1+max98095->mic2pre)<<M98095_MICPRE_SHIFT);
}
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK, 0);
break;
default:
return -EINVAL;
@@ -628,8 +625,8 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
static int max98095_line_pga(struct snd_soc_dapm_widget *w,
int event, u8 channel)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
u8 *state;
if (WARN_ON(!(channel == 1 || channel == 2)))
@@ -640,13 +637,13 @@ static int max98095_line_pga(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
*state |= channel;
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), (1 << w->shift));
break;
case SND_SOC_DAPM_POST_PMD:
*state &= ~channel;
if (*state == 0) {
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), 0);
}
break;
@@ -676,15 +673,15 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << (w->shift+2)), (1 << (w->shift+2)));
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << (w->shift+2)), 0);
break;
default:
@@ -942,8 +939,8 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -955,11 +952,11 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -969,31 +966,31 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE,
+ snd_soc_component_update_bits(component, M98095_027_DAI1_CLKMODE,
M98095_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
if (max98095->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
do_div(ni, (unsigned long long int)max98095->sysclk);
- snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1003,8 +1000,8 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -1016,11 +1013,11 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1030,31 +1027,31 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE,
+ snd_soc_component_update_bits(component, M98095_031_DAI2_CLKMODE,
M98095_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
if (max98095->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
do_div(ni, (unsigned long long int)max98095->sysclk);
- snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1064,8 +1061,8 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
unsigned long long ni;
unsigned int rate;
@@ -1077,11 +1074,11 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1091,31 +1088,31 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE,
+ snd_soc_component_update_bits(component, M98095_03B_DAI3_CLKMODE,
M98095_CLKMODE_MASK, regval);
cdata->rate = rate;
/* Configure NI when operating as master */
- if (snd_soc_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
if (max98095->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
do_div(ni, (unsigned long long int)max98095->sysclk);
- snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1124,8 +1121,8 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
/* Requested clock frequency is already setup */
if (freq == max98095->sysclk)
@@ -1142,13 +1139,13 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if ((freq >= 10000000) && (freq < 20000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x10);
} else if ((freq >= 20000000) && (freq < 40000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x20);
} else if ((freq >= 40000000) && (freq < 60000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x30);
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
@@ -1161,8 +1158,8 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
u8 regval = 0;
@@ -1171,22 +1168,20 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1216,11 +1211,11 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
M98095_DAI_WCI, regval);
- snd_soc_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ snd_soc_component_write(component, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
}
return 0;
@@ -1229,8 +1224,8 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
u8 regval = 0;
@@ -1239,22 +1234,20 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1284,11 +1277,11 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
M98095_DAI_WCI, regval);
- snd_soc_write(codec, M98095_035_DAI2_CLOCK,
+ snd_soc_component_write(component, M98095_035_DAI2_CLOCK,
M98095_DAI_BSEL64);
}
@@ -1298,8 +1291,8 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
u8 regval = 0;
@@ -1308,22 +1301,20 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Consumer mode PLL */
+ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI,
0x80);
- snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1353,21 +1344,22 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
M98095_DAI_WCI, regval);
- snd_soc_write(codec, M98095_03F_DAI3_CLOCK,
+ snd_soc_component_write(component, M98095_03F_DAI3_CLOCK,
M98095_DAI_BSEL64);
}
return 0;
}
-static int max98095_set_bias_level(struct snd_soc_codec *codec,
+static int max98095_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -1385,7 +1377,7 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
if (IS_ERR(max98095->mclk))
break;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(max98095->mclk);
} else {
ret = clk_prepare_enable(max98095->mclk);
@@ -1395,21 +1387,21 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max98095->regmap);
if (ret != 0) {
- dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
return ret;
}
}
- snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
M98095_MBEN, M98095_MBEN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
M98095_MBEN, 0);
regcache_mark_dirty(max98095->regmap);
break;
@@ -1494,12 +1486,12 @@ static int max98095_get_eq_channel(const char *name)
static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
int channel = max98095_get_eq_channel(kcontrol->id.name);
struct max98095_cdata *cdata;
- unsigned int sel = ucontrol->value.integer.value[0];
+ unsigned int sel = ucontrol->value.enumerated.item[0];
struct max98095_eq_cfg *coef_set;
int fs, best, best_val, i;
int regmask, regsave;
@@ -1528,7 +1520,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->eq_cfg[best].name,
pdata->eq_cfg[best].rate, fs);
@@ -1537,29 +1529,29 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
/* Disable filter while configuring, and save current on/off state */
- regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+ regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0);
mutex_lock(&max98095->lock);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
- m98095_eq_band(codec, channel, 0, coef_set->band1);
- m98095_eq_band(codec, channel, 1, coef_set->band2);
- m98095_eq_band(codec, channel, 2, coef_set->band3);
- m98095_eq_band(codec, channel, 3, coef_set->band4);
- m98095_eq_band(codec, channel, 4, coef_set->band5);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_eq_band(component, channel, 0, coef_set->band1);
+ m98095_eq_band(component, channel, 1, coef_set->band2);
+ m98095_eq_band(component, channel, 2, coef_set->band3);
+ m98095_eq_band(component, channel, 3, coef_set->band4);
+ m98095_eq_band(component, channel, 4, coef_set->band5);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0);
mutex_unlock(&max98095->lock);
/* Restore the original on/off state */
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave);
return 0;
}
static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
int channel = max98095_get_eq_channel(kcontrol->id.name);
struct max98095_cdata *cdata;
@@ -1569,9 +1561,9 @@ static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_eq_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
struct max98095_eq_cfg *cfg;
unsigned int cfgcnt;
@@ -1624,36 +1616,33 @@ static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
max98095->eq_enum.texts = max98095->eq_texts;
max98095->eq_enum.items = max98095->eq_textcnt;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+ dev_err(component->dev, "Failed to add EQ control: %d\n", ret);
}
static const char *bq_mode_name[] = {"Biquad1 Mode", "Biquad2 Mode"};
-static int max98095_get_bq_channel(struct snd_soc_codec *codec,
+static int max98095_get_bq_channel(struct snd_soc_component *component,
const char *name)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bq_mode_name); i++)
- if (strcmp(name, bq_mode_name[i]) == 0)
- return i;
+ int ret;
- /* Shouldn't happen */
- dev_err(codec->dev, "Bad biquad channel name '%s'\n", name);
- return -EINVAL;
+ ret = match_string(bq_mode_name, ARRAY_SIZE(bq_mode_name), name);
+ if (ret < 0)
+ dev_err(component->dev, "Bad biquad channel name '%s'\n", name);
+ return ret;
}
static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
- int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
+ int channel = max98095_get_bq_channel(component, kcontrol->id.name);
struct max98095_cdata *cdata;
- unsigned int sel = ucontrol->value.integer.value[0];
+ unsigned int sel = ucontrol->value.enumerated.item[0];
struct max98095_biquad_cfg *coef_set;
int fs, best, best_val, i;
int regmask, regsave;
@@ -1682,7 +1671,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->bq_cfg[best].name,
pdata->bq_cfg[best].rate, fs);
@@ -1691,27 +1680,27 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
/* Disable filter while configuring, and save current on/off state */
- regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+ regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL);
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0);
mutex_lock(&max98095->lock);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
- m98095_biquad_band(codec, channel, 0, coef_set->band1);
- m98095_biquad_band(codec, channel, 1, coef_set->band2);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_biquad_band(component, channel, 0, coef_set->band1);
+ m98095_biquad_band(component, channel, 1, coef_set->band2);
+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0);
mutex_unlock(&max98095->lock);
/* Restore the original on/off state */
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave);
return 0;
}
static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ int channel = max98095_get_bq_channel(component, kcontrol->id.name);
struct max98095_cdata *cdata;
if (channel < 0)
@@ -1723,9 +1712,9 @@ static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_bq_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
struct max98095_biquad_cfg *cfg;
unsigned int cfgcnt;
@@ -1779,19 +1768,19 @@ static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
max98095->bq_enum.texts = max98095->bq_texts;
max98095->bq_enum.items = max98095->bq_textcnt;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add Biquad control: %d\n", ret);
+ dev_err(component->dev, "Failed to add Biquad control: %d\n", ret);
}
-static void max98095_handle_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
u8 regval = 0;
if (!pdata) {
- dev_dbg(codec->dev, "No platform data\n");
+ dev_dbg(component->dev, "No platform data\n");
return;
}
@@ -1802,27 +1791,27 @@ static void max98095_handle_pdata(struct snd_soc_codec *codec)
if (pdata->digmic_right_mode)
regval |= M98095_DIGMIC_R;
- snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+ snd_soc_component_write(component, M98095_087_CFG_MIC, regval);
/* Configure equalizers */
if (pdata->eq_cfgcnt)
- max98095_handle_eq_pdata(codec);
+ max98095_handle_eq_pdata(component);
/* Configure bi-quad filters */
if (pdata->bq_cfgcnt)
- max98095_handle_bq_pdata(codec);
+ max98095_handle_bq_pdata(component);
}
static irqreturn_t max98095_report_jack(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = data;
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
unsigned int value;
int hp_report = 0;
int mic_report = 0;
/* Read the Jack Status Register */
- value = snd_soc_read(codec, M98095_007_JACK_AUTO_STS);
+ value = snd_soc_component_read(component, M98095_007_JACK_AUTO_STS);
/* If ddone is not set, then detection isn't finished yet */
if ((value & M98095_DDONE) == 0)
@@ -1853,9 +1842,9 @@ static irqreturn_t max98095_report_jack(int irq, void *data)
return IRQ_HANDLED;
}
-static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+static int max98095_jack_detect_enable(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
int ret = 0;
int detect_enable = M98095_JDEN;
unsigned int slew = M98095_DEFAULT_SLEW_DELAY;
@@ -1866,41 +1855,41 @@ static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
if (max98095->pdata->jack_detect_delay)
slew = max98095->pdata->jack_detect_delay;
- ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
+ ret = snd_soc_component_write(component, M98095_08E_JACK_DC_SLEW, slew);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
/* configure auto detection to be enabled */
- ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable);
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, detect_enable);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
return ret;
}
-static int max98095_jack_detect_disable(struct snd_soc_codec *codec)
+static int max98095_jack_detect_disable(struct snd_soc_component *component)
{
int ret = 0;
/* configure auto detection to be disabled */
- ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, 0x0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
return ret;
}
-int max98095_jack_detect(struct snd_soc_codec *codec,
+int max98095_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
int ret = 0;
max98095->headphone_jack = hp_jack;
@@ -1910,44 +1899,46 @@ int max98095_jack_detect(struct snd_soc_codec *codec,
if (!hp_jack && !mic_jack)
return -EINVAL;
- max98095_jack_detect_enable(codec);
+ max98095_jack_detect_enable(component);
/* enable interrupts for headphone jack detection */
- ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
+ ret = snd_soc_component_update_bits(component, M98095_013_JACK_INT_EN,
M98095_IDDONE, M98095_IDDONE);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
+ dev_err(component->dev, "Failed to cfg jack irqs %d\n", ret);
return ret;
}
- max98095_report_jack(client->irq, codec);
+ max98095_report_jack(client->irq, component);
return 0;
}
EXPORT_SYMBOL_GPL(max98095_jack_detect);
#ifdef CONFIG_PM
-static int max98095_suspend(struct snd_soc_codec *codec)
+static int max98095_suspend(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
if (max98095->headphone_jack || max98095->mic_jack)
- max98095_jack_detect_disable(codec);
+ max98095_jack_detect_disable(component);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
return 0;
}
-static int max98095_resume(struct snd_soc_codec *codec)
+static int max98095_resume(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
if (max98095->headphone_jack || max98095->mic_jack) {
- max98095_jack_detect_enable(codec);
- max98095_report_jack(client->irq, codec);
+ max98095_jack_detect_enable(component);
+ max98095_report_jack(client->irq, component);
}
return 0;
@@ -1957,30 +1948,30 @@ static int max98095_resume(struct snd_soc_codec *codec)
#define max98095_resume NULL
#endif
-static int max98095_reset(struct snd_soc_codec *codec)
+static int max98095_reset(struct snd_soc_component *component)
{
int i, ret;
/* Gracefully reset the DSP core and the codec hardware
* in a proper sequence */
- ret = snd_soc_write(codec, M98095_00F_HOST_CFG, 0);
+ ret = snd_soc_component_write(component, M98095_00F_HOST_CFG, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ dev_err(component->dev, "Failed to reset DSP: %d\n", ret);
return ret;
}
- ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ ret = snd_soc_component_write(component, M98095_097_PWR_SYS, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ dev_err(component->dev, "Failed to reset component: %d\n", ret);
return ret;
}
/* Reset to hardware default for registers, as there is not
* a soft reset hardware control register */
for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) {
- ret = snd_soc_write(codec, i, snd_soc_read(codec, i));
+ ret = snd_soc_component_write(component, i, snd_soc_component_read(component, i));
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ dev_err(component->dev, "Failed to reset: %d\n", ret);
return ret;
}
}
@@ -1988,21 +1979,21 @@ static int max98095_reset(struct snd_soc_codec *codec)
return ret;
}
-static int max98095_probe(struct snd_soc_codec *codec)
+static int max98095_probe(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_cdata *cdata;
struct i2c_client *client;
int ret = 0;
- max98095->mclk = devm_clk_get(codec->dev, "mclk");
+ max98095->mclk = devm_clk_get(component->dev, "mclk");
if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
/* reset the codec, the DSP core, and disable all interrupts */
- max98095_reset(codec);
+ max98095_reset(component);
- client = to_i2c_client(codec->dev);
+ client = to_i2c_client(component->dev);
/* initialize private data */
@@ -2037,87 +2028,93 @@ static int max98095_probe(struct snd_soc_codec *codec)
ret = request_threaded_irq(client->irq, NULL,
max98095_report_jack,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
- IRQF_ONESHOT, "max98095", codec);
+ IRQF_ONESHOT, "max98095", component);
if (ret) {
- dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
goto err_access;
}
}
- ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ ret = snd_soc_component_read(component, M98095_0FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failure reading hardware revision: %d\n",
+ dev_err(component->dev, "Failure reading hardware revision: %d\n",
ret);
goto err_irq;
}
- dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
+ dev_info(component->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
- snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+ snd_soc_component_write(component, M98095_097_PWR_SYS, M98095_PWRSV);
- snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ snd_soc_component_write(component, M98095_048_MIX_DAC_LR,
M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
- snd_soc_write(codec, M98095_049_MIX_DAC_M,
+ snd_soc_component_write(component, M98095_049_MIX_DAC_M,
M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
- snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
- snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
- snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
+ snd_soc_component_write(component, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
+ snd_soc_component_write(component, M98095_045_CFG_DSP, M98095_DSPNORMAL);
+ snd_soc_component_write(component, M98095_04E_CFG_HP, M98095_HPNORMAL);
- snd_soc_write(codec, M98095_02C_DAI1_IOCFG,
+ snd_soc_component_write(component, M98095_02C_DAI1_IOCFG,
M98095_S1NORMAL|M98095_SDATA);
- snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ snd_soc_component_write(component, M98095_036_DAI2_IOCFG,
M98095_S2NORMAL|M98095_SDATA);
- snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ snd_soc_component_write(component, M98095_040_DAI3_IOCFG,
M98095_S3NORMAL|M98095_SDATA);
- max98095_handle_pdata(codec);
+ max98095_handle_pdata(component);
/* take the codec out of the shut down */
- snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ snd_soc_component_update_bits(component, M98095_097_PWR_SYS, M98095_SHDNRUN,
M98095_SHDNRUN);
return 0;
err_irq:
if (client->irq)
- free_irq(client->irq, codec);
+ free_irq(client->irq, component);
err_access:
return ret;
}
-static int max98095_remove(struct snd_soc_codec *codec)
+static void max98095_remove(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
if (max98095->headphone_jack || max98095->mic_jack)
- max98095_jack_detect_disable(codec);
+ max98095_jack_detect_disable(component);
if (client->irq)
- free_irq(client->irq, codec);
-
- return 0;
+ free_irq(client->irq, component);
}
-static struct snd_soc_codec_driver soc_codec_dev_max98095 = {
- .probe = max98095_probe,
- .remove = max98095_remove,
- .suspend = max98095_suspend,
- .resume = max98095_resume,
- .set_bias_level = max98095_set_bias_level,
- .controls = max98095_snd_controls,
- .num_controls = ARRAY_SIZE(max98095_snd_controls),
- .dapm_widgets = max98095_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
- .dapm_routes = max98095_audio_map,
- .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+static const struct snd_soc_component_driver soc_component_dev_max98095 = {
+ .probe = max98095_probe,
+ .remove = max98095_remove,
+ .suspend = max98095_suspend,
+ .resume = max98095_resume,
+ .set_bias_level = max98095_set_bias_level,
+ .controls = max98095_snd_controls,
+ .num_controls = ARRAY_SIZE(max98095_snd_controls),
+ .dapm_widgets = max98095_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
+ .dapm_routes = max98095_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int max98095_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id max98095_i2c_id[] = {
+ { "max98095", MAX98095 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static int max98095_i2c_probe(struct i2c_client *i2c)
{
struct max98095_priv *max98095;
int ret;
@@ -2136,40 +2133,30 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
return ret;
}
- max98095->devtype = id->driver_data;
+ max98095->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98095);
max98095->pdata = i2c->dev.platform_data;
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98095,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98095,
max98095_dai, ARRAY_SIZE(max98095_dai));
return ret;
}
-static int max98095_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id max98095_i2c_id[] = {
- { "max98095", MAX98095 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
-
+#ifdef CONFIG_OF
static const struct of_device_id max98095_of_match[] = {
{ .compatible = "maxim,max98095", },
{ }
};
MODULE_DEVICE_TABLE(of, max98095_of_match);
+#endif
static struct i2c_driver max98095_i2c_driver = {
.driver = {
.name = "max98095",
.of_match_table = of_match_ptr(max98095_of_match),
},
- .probe = max98095_i2c_probe,
- .remove = max98095_i2c_remove,
+ .probe = max98095_i2c_probe,
.id_table = max98095_i2c_id,
};
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
index 2ebbe4e894bf..2af7e77021a2 100644
--- a/sound/soc/codecs/max98095.h
+++ b/sound/soc/codecs/max98095.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98095.h -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 Maxim Integrated Products
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _MAX98095_H
@@ -315,7 +312,7 @@
/* Default Delay used in Slew Rate Calculation for Jack detection */
#define M98095_DEFAULT_SLEW_DELAY 0x18
-extern int max98095_jack_detect(struct snd_soc_codec *codec,
+extern int max98095_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
#endif
diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c
index 5b1dfb1518fb..cc811f58c9d2 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -1,21 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* max98357a.c -- MAX98357A ALSA SoC Codec driver
*/
#include <linux/acpi.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
@@ -27,61 +19,83 @@
#include <sound/soc-dai.h>
#include <sound/soc-dapm.h>
+struct max98357a_priv {
+ struct gpio_desc *sdmode;
+ unsigned int sdmode_delay;
+ int sdmode_switch;
+};
+
static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
- struct gpio_desc *sdmode = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = dai->component;
+ struct max98357a_priv *max98357a =
+ snd_soc_component_get_drvdata(component);
- if (!sdmode)
+ if (!max98357a->sdmode)
return 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- gpiod_set_value(sdmode, 1);
+ mdelay(max98357a->sdmode_delay);
+ if (max98357a->sdmode_switch) {
+ gpiod_set_value(max98357a->sdmode, 1);
+ dev_dbg(component->dev, "set sdmode to 1");
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- gpiod_set_value(sdmode, 0);
+ gpiod_set_value(max98357a->sdmode, 0);
+ dev_dbg(component->dev, "set sdmode to 0");
break;
}
return 0;
}
+static int max98357a_sdmode_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98357a_priv *max98357a =
+ snd_soc_component_get_drvdata(component);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ max98357a->sdmode_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ max98357a->sdmode_switch = 0;
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("Speaker"),
+ SND_SOC_DAPM_OUT_DRV_E("SD_MODE", SND_SOC_NOPM, 0, 0, NULL, 0,
+ max98357a_sdmode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route max98357a_dapm_routes[] = {
- {"Speaker", NULL, "HiFi Playback"},
+ {"SD_MODE", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "SD_MODE"},
};
-static int max98357a_codec_probe(struct snd_soc_codec *codec)
-{
- struct gpio_desc *sdmode;
-
- sdmode = devm_gpiod_get_optional(codec->dev, "sdmode", GPIOD_OUT_LOW);
- if (IS_ERR(sdmode))
- return PTR_ERR(sdmode);
-
- snd_soc_codec_set_drvdata(codec, sdmode);
-
- return 0;
-}
-
-static struct snd_soc_codec_driver max98357a_codec_driver = {
- .probe = max98357a_codec_probe,
+static const struct snd_soc_component_driver max98357a_component_driver = {
.dapm_widgets = max98357a_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets),
.dapm_routes = max98357a_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct snd_soc_dai_ops max98357a_dai_ops = {
- .trigger = max98357a_daiops_trigger,
+ .trigger = max98357a_daiops_trigger,
};
static struct snd_soc_dai_driver max98357a_dai_driver = {
@@ -93,7 +107,10 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
@@ -105,20 +122,38 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
static int max98357a_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver,
- &max98357a_dai_driver, 1);
-}
+ struct max98357a_priv *max98357a;
+ int ret;
+
+ max98357a = devm_kzalloc(&pdev->dev, sizeof(*max98357a), GFP_KERNEL);
+ if (!max98357a)
+ return -ENOMEM;
+
+ max98357a->sdmode = devm_gpiod_get_optional(&pdev->dev,
+ "sdmode", GPIOD_OUT_LOW);
+ if (IS_ERR(max98357a->sdmode))
+ return PTR_ERR(max98357a->sdmode);
+
+ ret = device_property_read_u32(&pdev->dev, "sdmode-delay",
+ &max98357a->sdmode_delay);
+ if (ret) {
+ max98357a->sdmode_delay = 0;
+ dev_dbg(&pdev->dev,
+ "no optional property 'sdmode-delay' found, "
+ "default: no delay\n");
+ }
-static int max98357a_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, max98357a);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &max98357a_component_driver,
+ &max98357a_dai_driver, 1);
}
#ifdef CONFIG_OF
static const struct of_device_id max98357a_device_id[] = {
{ .compatible = "maxim,max98357a" },
+ { .compatible = "maxim,max98360a" },
{}
};
MODULE_DEVICE_TABLE(of, max98357a_device_id);
@@ -127,6 +162,7 @@ MODULE_DEVICE_TABLE(of, max98357a_device_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id max98357a_acpi_match[] = {
{ "MX98357A", 0 },
+ { "MX98360A", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
@@ -139,7 +175,6 @@ static struct platform_driver max98357a_platform_driver = {
.acpi_match_table = ACPI_PTR(max98357a_acpi_match),
},
.probe = max98357a_platform_probe,
- .remove = max98357a_platform_remove,
};
module_platform_driver(max98357a_platform_driver);
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
new file mode 100644
index 000000000000..25af78ab30d5
--- /dev/null
+++ b/sound/soc/codecs/max98363.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98363.h"
+
+static const struct reg_default max98363_reg[] = {
+ {MAX98363_R2021_ERR_MON_CTRL, 0x0},
+ {MAX98363_R2022_SPK_MON_THRESH, 0x0},
+ {MAX98363_R2023_SPK_MON_DURATION, 0x0},
+ {MAX98363_R2030_TONE_GEN_CFG, 0x0},
+ {MAX98363_R203F_TONE_GEN_EN, 0x0},
+ {MAX98363_R2040_AMP_VOL, 0x0},
+ {MAX98363_R2041_AMP_GAIN, 0x5},
+ {MAX98363_R2042_DSP_CFG, 0x0},
+};
+
+static bool max98363_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98363_R2001_INTR_RAW:
+ case MAX98363_R2003_INTR_STATE:
+ case MAX98363_R2005_INTR_FALG:
+ case MAX98363_R2007_INTR_EN:
+ case MAX98363_R2009_INTR_CLR:
+ case MAX98363_R2021_ERR_MON_CTRL ... MAX98363_R2023_SPK_MON_DURATION:
+ case MAX98363_R2030_TONE_GEN_CFG:
+ case MAX98363_R203F_TONE_GEN_EN:
+ case MAX98363_R2040_AMP_VOL:
+ case MAX98363_R2041_AMP_GAIN:
+ case MAX98363_R2042_DSP_CFG:
+ case MAX98363_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98363_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98363_R2001_INTR_RAW:
+ case MAX98363_R2003_INTR_STATE:
+ case MAX98363_R2005_INTR_FALG:
+ case MAX98363_R2007_INTR_EN:
+ case MAX98363_R2009_INTR_CLR:
+ case MAX98363_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max98363_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = MAX98363_R21FF_REV_ID,
+ .reg_defaults = max98363_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98363_reg),
+ .readable_reg = max98363_readable_register,
+ .volatile_reg = max98363_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int max98363_suspend(struct device *dev)
+{
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98363->regmap, true);
+ regcache_mark_dirty(max98363->regmap);
+
+ return 0;
+}
+
+#define MAX98363_PROBE_TIMEOUT 5000
+
+static int max98363_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!max98363->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(MAX98363_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(dev, "Initialization not complete, timed out\n");
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+
+ slave->unattach_request = 0;
+ regcache_cache_only(max98363->regmap, false);
+ regcache_sync(max98363->regmap);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(max98363_pm, max98363_suspend, max98363_resume, NULL);
+
+static int max98363_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* BITMAP: 00000010 Dataport 1 is active */
+ prop->sink_ports = BIT(1);
+ prop->paging_support = true;
+ prop->clk_stop_timeout = 20;
+ prop->simple_clk_stop_capable = true;
+ prop->clock_reg_supported = true;
+
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ return 0;
+}
+
+static int max98363_io_init(struct sdw_slave *slave)
+{
+ struct device *dev = &slave->dev;
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+ int ret, reg;
+
+ regcache_cache_only(max98363->regmap, false);
+ if (max98363->first_hw_init)
+ regcache_cache_bypass(max98363->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!max98363->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(dev);
+
+ pm_runtime_get_noresume(dev);
+
+ ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, &reg);
+ if (!ret)
+ dev_info(dev, "Revision ID: %X\n", reg);
+ else
+ goto out;
+
+ if (max98363->first_hw_init) {
+ regcache_cache_bypass(max98363->regmap, false);
+ regcache_mark_dirty(max98363->regmap);
+ }
+
+ max98363->first_hw_init = true;
+ max98363->hw_init = true;
+
+out:
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+#define MAX98363_RATES SNDRV_PCM_RATE_8000_192000
+#define MAX98363_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98363_priv *max98363 =
+ snd_soc_component_get_drvdata(component);
+
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ int ret;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!stream)
+ return -EINVAL;
+
+ if (!max98363->slave)
+ return -EINVAL;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+
+ direction = SDW_DATA_DIR_RX;
+ port_config.num = 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+ stream_config.ch_count = 1;
+
+ if (stream_config.ch_count > runtime->hw.channels_max) {
+ stream_config.ch_count = runtime->hw.channels_max;
+ dev_info(dai->dev, "Number of channels: %d (requested: %d)\n",
+ stream_config.ch_count, params_channels(params));
+ }
+ port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0);
+
+ ret = sdw_stream_add_slave(max98363->slave, &stream_config,
+ &port_config, 1, stream);
+ if (ret) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return ret;
+ }
+
+ dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+ return 0;
+}
+
+static int max98363_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98363_priv *max98363 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!max98363->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(max98363->slave, stream);
+
+ return 0;
+}
+
+static int max98363_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98363_dai_sdw_ops = {
+ .hw_params = max98363_sdw_dai_hw_params,
+ .hw_free = max98363_pcm_hw_free,
+ .set_stream = max98363_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver max98363_dai[] = {
+ {
+ .name = "max98363-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98363_RATES,
+ .formats = MAX98363_FORMATS,
+ },
+ .ops = &max98363_dai_sdw_ops,
+ }
+};
+
+static int max98363_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct max98363_priv *max98363 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ max98363->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+ */
+ if (max98363->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return max98363_io_init(slave);
+}
+
+static const struct sdw_slave_ops max98363_slave_ops = {
+ .read_prop = max98363_read_prop,
+ .update_status = max98363_update_status,
+};
+
+static DECLARE_TLV_DB_SCALE(max98363_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98363_spk_tlv,
+ 0, 5, TLV_DB_SCALE_ITEM(-300, 300, 0),
+);
+
+static const char * const max98363_tone_cfg_text[] = {
+ "Reserved", "0", "+FS/2", "-FS/2", "1KHz",
+ "12KHz", "8KHz", "6KHz", "4KHz", "3KHz",
+ "2KHz", "1.5KHz", "Reserved", "500Hz", "250Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_tone_cfg_enum,
+ MAX98363_R2030_TONE_GEN_CFG, 0,
+ max98363_tone_cfg_text);
+
+static const char * const max98363_spkmon_duration_text[] = {
+ "8ms", "20ms", "40ms", "60ms",
+ "80ms", "160ms", "240ms", "320ms",
+ "400ms", "480ms", "560ms", "640ms",
+ "720ms", "800ms", "880ms", "960ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_spkmon_duration_enum,
+ MAX98363_R2023_SPK_MON_DURATION, 0,
+ max98363_spkmon_duration_text);
+
+static const struct snd_kcontrol_new max98363_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", MAX98363_R2040_AMP_VOL,
+ 0, 0x7F, 1, max98363_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98363_R2041_AMP_GAIN,
+ 0, 10, 0, max98363_spk_tlv),
+ SOC_SINGLE("Tone Generator Switch", MAX98363_R203F_TONE_GEN_EN,
+ 0, 1, 0),
+ SOC_ENUM("Tone Config", max98363_tone_cfg_enum),
+ SOC_SINGLE("Ramp Switch", MAX98363_R2042_DSP_CFG,
+ MAX98363_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("CLK Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+ MAX98363_CLOCK_MON_SHIFT, 1, 0),
+ SOC_SINGLE("SPKMON Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+ MAX98363_SPKMON_SHIFT, 1, 0),
+ SOC_SINGLE("SPKMON Thresh", MAX98363_R2022_SPK_MON_THRESH, 0, 0xFF, 0),
+ SOC_ENUM("SPKMON Duration", max98363_spkmon_duration_enum),
+};
+
+static const struct snd_soc_dapm_widget max98363_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98363_audio_map[] = {
+ /* Plabyack */
+ {"BE_OUT", NULL, "AIFIN"},
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98363 = {
+ .controls = max98363_snd_controls,
+ .num_controls = ARRAY_SIZE(max98363_snd_controls),
+ .dapm_widgets = max98363_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98363_dapm_widgets),
+ .dapm_routes = max98363_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98363_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int max98363_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+ struct max98363_priv *max98363;
+ int ret;
+ struct device *dev = &slave->dev;
+
+ /* Allocate and assign private driver data structure */
+ max98363 = devm_kzalloc(dev, sizeof(*max98363), GFP_KERNEL);
+ if (!max98363)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, max98363);
+ max98363->regmap = regmap;
+ max98363->slave = slave;
+
+ regcache_cache_only(max98363->regmap, true);
+
+ max98363->hw_init = false;
+ max98363->first_hw_init = false;
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363,
+ max98363_dai,
+ ARRAY_SIZE(max98363_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+ return 0;
+}
+
+static int max98363_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &max98363_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return max98363_init(slave, regmap);
+}
+
+static const struct sdw_device_id max98363_id[] = {
+ SDW_SLAVE_ENTRY(0x019F, 0x8363, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, max98363_id);
+
+static struct sdw_driver max98363_sdw_driver = {
+ .driver = {
+ .name = "max98363",
+ .pm = pm_ptr(&max98363_pm),
+ },
+ .probe = max98363_sdw_probe,
+ .ops = &max98363_slave_ops,
+ .id_table = max98363_id,
+};
+
+module_sdw_driver(max98363_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98363 driver SDW");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98363.h b/sound/soc/codecs/max98363.h
new file mode 100644
index 000000000000..2b6743d3a2cf
--- /dev/null
+++ b/sound/soc/codecs/max98363.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2022 Analog Devices Inc. */
+
+#ifndef _MAX98363_H
+#define _MAX98363_H
+
+#define MAX98363_R2000_SW_RESET 0x2000
+#define MAX98363_R2001_INTR_RAW 0x2001
+#define MAX98363_R2003_INTR_STATE 0x2003
+#define MAX98363_R2005_INTR_FALG 0x2005
+#define MAX98363_R2007_INTR_EN 0x2007
+#define MAX98363_R2009_INTR_CLR 0x2009
+#define MAX98363_R2021_ERR_MON_CTRL 0x2021
+#define MAX98363_R2022_SPK_MON_THRESH 0x2022
+#define MAX98363_R2023_SPK_MON_DURATION 0x2023
+#define MAX98363_R2030_TONE_GEN_CFG 0x2030
+#define MAX98363_R203F_TONE_GEN_EN 0x203F
+#define MAX98363_R2040_AMP_VOL 0x2040
+#define MAX98363_R2041_AMP_GAIN 0x2041
+#define MAX98363_R2042_DSP_CFG 0x2042
+#define MAX98363_R21FF_REV_ID 0x21FF
+
+/* MAX98363_R2021_ERR_MON_CTRL */
+#define MAX98363_SPKMON_SHIFT (3)
+#define MAX98363_CLOCK_MON_SHIFT (0)
+
+/* MAX98363_R2042_DSP_CFG */
+#define MAX98363_AMP_DSP_CFG_RMP_SHIFT (3)
+
+struct max98363_priv {
+ struct regmap *regmap;
+ struct sdw_slave *slave;
+ bool hw_init;
+ bool first_hw_init;
+};
+#endif
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
new file mode 100644
index 000000000000..852db211ba1e
--- /dev/null
+++ b/sound/soc/codecs/max98371.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98371.c -- ALSA SoC Stereo MAX98371 driver
+ *
+ * Copyright 2015-16 Maxim Integrated Products
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98371.h"
+
+static const char *const monomix_text[] = {
+ "Left", "Right", "LeftRightDiv2",
+};
+
+static const char *const hpf_cutoff_txt[] = {
+ "Disable", "DC Block", "50Hz",
+ "100Hz", "200Hz", "400Hz", "800Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98371_monomix, MAX98371_MONOMIX_CFG, 0,
+ monomix_text);
+
+static SOC_ENUM_SINGLE_DECL(max98371_hpf_cutoff, MAX98371_HPF, 0,
+ hpf_cutoff_txt);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_min_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(1699, 101, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_max_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(1699, 208, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_rot_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
+ 2, 6, TLV_DB_SCALE_ITEM(-100, -100, 0),
+ 7, 8, TLV_DB_SCALE_ITEM(-800, -200, 0),
+ 9, 11, TLV_DB_SCALE_ITEM(-1200, -300, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
+ 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+);
+
+static const struct reg_default max98371_reg[] = {
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x04, 0x00 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x00 },
+ { 0x10, 0x06 },
+ { 0x11, 0x08 },
+ { 0x14, 0x80 },
+ { 0x15, 0x00 },
+ { 0x16, 0x00 },
+ { 0x18, 0x00 },
+ { 0x19, 0x00 },
+ { 0x1C, 0x00 },
+ { 0x1D, 0x00 },
+ { 0x1E, 0x00 },
+ { 0x1F, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x00 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2A, 0x00 },
+ { 0x2B, 0x00 },
+ { 0x2C, 0x00 },
+ { 0x2D, 0x00 },
+ { 0x2E, 0x0B },
+ { 0x31, 0x00 },
+ { 0x32, 0x18 },
+ { 0x33, 0x00 },
+ { 0x34, 0x00 },
+ { 0x36, 0x00 },
+ { 0x37, 0x00 },
+ { 0x38, 0x00 },
+ { 0x39, 0x00 },
+ { 0x3A, 0x00 },
+ { 0x3B, 0x00 },
+ { 0x3C, 0x00 },
+ { 0x3D, 0x00 },
+ { 0x3E, 0x00 },
+ { 0x3F, 0x00 },
+ { 0x40, 0x00 },
+ { 0x41, 0x00 },
+ { 0x42, 0x00 },
+ { 0x43, 0x00 },
+ { 0x4A, 0x00 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x4D, 0x00 },
+ { 0x4E, 0x00 },
+ { 0x50, 0x00 },
+ { 0x51, 0x00 },
+ { 0x55, 0x00 },
+ { 0x58, 0x00 },
+ { 0x59, 0x00 },
+ { 0x5C, 0x00 },
+ { 0xFF, 0x43 },
+};
+
+static bool max98371_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98371_IRQ_CLEAR1:
+ case MAX98371_IRQ_CLEAR2:
+ case MAX98371_IRQ_CLEAR3:
+ case MAX98371_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98371_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98371_SOFT_RESET:
+ return false;
+ default:
+ return true;
+ }
+};
+
+static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(0, 50, 0),
+ 8, 10, TLV_DB_SCALE_ITEM(400, 100, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1);
+
+static const struct snd_kcontrol_new max98371_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98371_GAIN,
+ MAX98371_GAIN_SHIFT, (1<<MAX98371_GAIN_WIDTH)-1, 0,
+ max98371_gain_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98371_DIGITAL_GAIN, 0,
+ (1<<MAX98371_DIGITAL_GAIN_WIDTH)-1, 1, digital_tlv),
+ SOC_SINGLE_TLV("Speaker DHT Max Volume", MAX98371_GAIN,
+ 0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
+ max98371_dht_max_gain),
+ SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
+ max98371_dht_min_gain),
+ SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
+ max98371_dht_rot_gain),
+ SOC_SINGLE("DHT Attack Step", MAX98371_DHT, MAX98371_DHT_STEP, 3, 0),
+ SOC_SINGLE("DHT Attack Rate", MAX98371_DHT, 0, 7, 0),
+ SOC_ENUM("Monomix Select", max98371_monomix),
+ SOC_ENUM("HPF Cutoff", max98371_hpf_cutoff),
+};
+
+static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= 0;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= MAX98371_DAI_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= MAX98371_DAI_LEFT;
+ break;
+ default:
+ dev_err(component->dev, "DAI wrong mode unsupported");
+ return -EINVAL;
+ }
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MODE_MASK, val);
+ return 0;
+}
+
+static int max98371_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component);
+ int blr_clk_ratio, ch_size, channels = params_channels(params);
+ int rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
+ ch_size = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
+ ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
+ ch_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
+ ch_size = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* BCLK/LRCLK ratio calculation */
+ blr_clk_ratio = channels * ch_size;
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 32000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_32);
+ break;
+ case 44100:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_44);
+ break;
+ case 48000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_48);
+ break;
+ case 88200:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_88);
+ break;
+ case 96000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_96);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* enabling both the RX channels*/
+ regmap_update_bits(max98371->regmap, MAX98371_MONOMIX_SRC,
+ MAX98371_MONOMIX_SRC_MASK, MONOMIX_RX_0_1);
+ regmap_update_bits(max98371->regmap, MAX98371_DAI_CHANNEL,
+ MAX98371_CHANNEL_MASK, MAX98371_CHANNEL_MASK);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98371_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, MAX98371_SPK_ENABLE, 0, 0),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98371_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98371_audio_map[] = {
+ {"DAC", NULL, "HiFi Playback"},
+ {"SPK_OUT", NULL, "DAC"},
+ {"SPK_OUT", NULL, "Global Enable"},
+};
+
+#define MAX98371_RATES SNDRV_PCM_RATE_8000_48000
+#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+static const struct snd_soc_dai_ops max98371_dai_ops = {
+ .set_fmt = max98371_dai_set_fmt,
+ .hw_params = max98371_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98371_dai[] = {
+ {
+ .name = "max98371-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98371_FORMATS,
+ },
+ .ops = &max98371_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver max98371_component = {
+ .controls = max98371_snd_controls,
+ .num_controls = ARRAY_SIZE(max98371_snd_controls),
+ .dapm_routes = max98371_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98371_audio_map),
+ .dapm_widgets = max98371_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98371_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98371_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98371_VERSION,
+ .reg_defaults = max98371_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98371_reg),
+ .volatile_reg = max98371_volatile_register,
+ .readable_reg = max98371_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98371_i2c_probe(struct i2c_client *i2c)
+{
+ struct max98371_priv *max98371;
+ int ret, reg;
+
+ max98371 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98371), GFP_KERNEL);
+ if (!max98371)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98371);
+ max98371->regmap = devm_regmap_init_i2c(i2c, &max98371_regmap);
+ if (IS_ERR(max98371->regmap)) {
+ ret = PTR_ERR(max98371->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(max98371->regmap, MAX98371_VERSION, &reg);
+ if (ret < 0) {
+ dev_info(&i2c->dev, "device error %d\n", ret);
+ return ret;
+ }
+ dev_info(&i2c->dev, "device version %x\n", reg);
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &max98371_component,
+ max98371_dai, ARRAY_SIZE(max98371_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id max98371_i2c_id[] = {
+ { "max98371" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max98371_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98371_of_match[] = {
+ { .compatible = "maxim,max98371", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98371_of_match);
+#endif
+
+static struct i2c_driver max98371_i2c_driver = {
+ .driver = {
+ .name = "max98371",
+ .of_match_table = of_match_ptr(max98371_of_match),
+ },
+ .probe = max98371_i2c_probe,
+ .id_table = max98371_i2c_id,
+};
+
+module_i2c_driver(max98371_i2c_driver);
+
+MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC MAX98371 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98371.h b/sound/soc/codecs/max98371.h
new file mode 100644
index 000000000000..63d9a9de3316
--- /dev/null
+++ b/sound/soc/codecs/max98371.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98371.h -- MAX98371 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ */
+
+#ifndef _MAX98371_H
+#define _MAX98371_H
+
+#define MAX98371_IRQ_CLEAR1 0x01
+#define MAX98371_IRQ_CLEAR2 0x02
+#define MAX98371_IRQ_CLEAR3 0x03
+#define MAX98371_DAI_CLK 0x10
+#define MAX98371_DAI_BSEL_MASK 0xF
+#define MAX98371_DAI_BSEL_32 2
+#define MAX98371_DAI_BSEL_48 3
+#define MAX98371_DAI_BSEL_64 4
+#define MAX98371_SPK_SR 0x11
+#define MAX98371_SPK_SR_MASK 0xF
+#define MAX98371_SPK_SR_32 6
+#define MAX98371_SPK_SR_44 7
+#define MAX98371_SPK_SR_48 8
+#define MAX98371_SPK_SR_88 10
+#define MAX98371_SPK_SR_96 11
+#define MAX98371_DAI_CHANNEL 0x15
+#define MAX98371_CHANNEL_MASK 0x3
+#define MAX98371_MONOMIX_SRC 0x18
+#define MAX98371_MONOMIX_CFG 0x19
+#define MAX98371_HPF 0x1C
+#define MAX98371_MONOMIX_SRC_MASK 0xFF
+#define MONOMIX_RX_0_1 ((0x1)<<(4))
+#define M98371_DAI_CHANNEL_I2S 0x3
+#define MAX98371_DIGITAL_GAIN 0x2D
+#define MAX98371_DIGITAL_GAIN_WIDTH 0x7
+#define MAX98371_GAIN 0x2E
+#define MAX98371_GAIN_SHIFT 0x4
+#define MAX98371_GAIN_WIDTH 0x4
+#define MAX98371_DHT_MAX_WIDTH 4
+#define MAX98371_FMT 0x14
+#define MAX98371_CHANSZ_WIDTH 6
+#define MAX98371_FMT_MASK ((0x3)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_FMT_MODE_MASK ((0x7)<<(3))
+#define MAX98371_DAI_LEFT ((0x1)<<(3))
+#define MAX98371_DAI_RIGHT ((0x2)<<(3))
+#define MAX98371_DAI_CHANSZ_16 ((1)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DAI_CHANSZ_24 ((2)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DAI_CHANSZ_32 ((3)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DHT 0x32
+#define MAX98371_DHT_STEP 0x3
+#define MAX98371_DHT_GAIN 0x31
+#define MAX98371_DHT_GAIN_WIDTH 0x4
+#define MAX98371_DHT_ROT_WIDTH 0x4
+#define MAX98371_SPK_ENABLE 0x4A
+#define MAX98371_GLOBAL_ENABLE 0x50
+#define MAX98371_SOFT_RESET 0x51
+#define MAX98371_VERSION 0xFF
+
+
+struct max98371_priv {
+ struct regmap *regmap;
+};
+#endif
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
new file mode 100644
index 000000000000..f58b8c8625a7
--- /dev/null
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98373.h"
+
+static const u32 max98373_i2c_cache_reg[] = {
+ MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+ MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+ MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
+static const struct reg_default max98373_reg[] = {
+ {MAX98373_R2000_SW_RESET, 0x00},
+ {MAX98373_R2001_INT_RAW1, 0x00},
+ {MAX98373_R2002_INT_RAW2, 0x00},
+ {MAX98373_R2003_INT_RAW3, 0x00},
+ {MAX98373_R2004_INT_STATE1, 0x00},
+ {MAX98373_R2005_INT_STATE2, 0x00},
+ {MAX98373_R2006_INT_STATE3, 0x00},
+ {MAX98373_R2007_INT_FLAG1, 0x00},
+ {MAX98373_R2008_INT_FLAG2, 0x00},
+ {MAX98373_R2009_INT_FLAG3, 0x00},
+ {MAX98373_R200A_INT_EN1, 0x00},
+ {MAX98373_R200B_INT_EN2, 0x00},
+ {MAX98373_R200C_INT_EN3, 0x00},
+ {MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+ {MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+ {MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+ {MAX98373_R2010_IRQ_CTRL, 0x00},
+ {MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+ {MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+ {MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+ {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+ {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+ {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+ {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+ {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+ {MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+ {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+ {MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+ {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+ {MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+ {MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+ {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+ {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+ {MAX98373_R202B_PCM_RX_EN, 0x00},
+ {MAX98373_R202C_PCM_TX_EN, 0x00},
+ {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+ {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+ {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+ {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+ {MAX98373_R2034_ICC_TX_CNTL, 0x00},
+ {MAX98373_R2035_ICC_TX_EN, 0x00},
+ {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+ {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+ {MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+ {MAX98373_R203F_AMP_DSP_CFG, 0x02},
+ {MAX98373_R2040_TONE_GEN_CFG, 0x00},
+ {MAX98373_R2041_AMP_CFG, 0x03},
+ {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+ {MAX98373_R2043_AMP_EN, 0x00},
+ {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+ {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+ {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+ {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+ {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+ {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+ {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+ {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+ {MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+ {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+ {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+ {MAX98373_R2097_BDE_L1_THRESH, 0x00},
+ {MAX98373_R2098_BDE_L2_THRESH, 0x00},
+ {MAX98373_R2099_BDE_L3_THRESH, 0x00},
+ {MAX98373_R209A_BDE_L4_THRESH, 0x00},
+ {MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+ {MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+ {MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+ {MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+ {MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+ {MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+ {MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+ {MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+ {MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+ {MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+ {MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+ {MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+ {MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+ {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+ {MAX98373_R20B5_BDE_EN, 0x00},
+ {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+ {MAX98373_R20D1_DHT_CFG, 0x01},
+ {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+ {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+ {MAX98373_R20D4_DHT_EN, 0x00},
+ {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+ {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+ {MAX98373_R20E2_LIMITER_EN, 0x00},
+ {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+ {MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+ {MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static int max98373_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98373_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98373_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98373_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98373_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98373_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98373_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98373_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98373->ch_size;
+ int value;
+
+ if (!max98373->tdm_mode) {
+ /* BCLK configuration */
+ value = max98373_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98373_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_96000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2027_PCM_SR_SETUP_1,
+ MAX98373_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ if (max98373->interleave_mode &&
+ sampling_rate > MAX98373_PCM_SR_SET1_SR_16000)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+
+ return max98373_set_clock(component, params);
+err:
+ return -EINVAL;
+}
+
+static int max98373_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+ unsigned int mask;
+ int x, slot_found;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98373->tdm_mode = false;
+ else
+ max98373->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98373_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ slot_found = 0;
+ mask = rx_mask;
+ for (x = 0 ; x < 16 ; x++, mask >>= 1) {
+ if (mask & 0x1) {
+ if (slot_found == 0)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ MAX98373_PCM_TO_SPK_CH0_SRC_MASK, x);
+ else
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ x);
+ slot_found++;
+ if (slot_found > 1)
+ break;
+ }
+ }
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ ~tx_mask & 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98373_dai_ops = {
+ .set_fmt = max98373_dai_set_fmt,
+ .hw_params = max98373_dai_hw_params,
+ .set_tdm_slot = max98373_dai_tdm_slot,
+};
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2000_SW_RESET:
+ case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3:
+ case MAX98373_R2010_IRQ_CTRL:
+ case MAX98373_R2014_THERM_WARN_THRESH
+ ... MAX98373_R2018_THERM_FOLDBACK_EN:
+ case MAX98373_R201E_PIN_DRIVE_STRENGTH
+ ... MAX98373_R2036_SOUNDWIRE_CTRL:
+ case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+ case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+ ... MAX98373_R2047_IV_SENSE_ADC_EN:
+ case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+ ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+ case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+ case MAX98373_R2097_BDE_L1_THRESH
+ ... MAX98373_R209B_BDE_THRESH_HYST:
+ case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+ case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+ case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+ case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+ ... MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+ case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+ case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+ case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct snd_soc_dai_driver max98373_dai[] = {
+ {
+ .name = "max98373-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .ops = &max98373_dai_ops,
+ }
+};
+
+static int max98373_suspend(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ int i;
+
+ /* cache feedback register values before suspend */
+ for (i = 0; i < max98373->cache_num; i++)
+ regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
+
+ regcache_cache_only(max98373->regmap, true);
+ regcache_mark_dirty(max98373->regmap);
+ return 0;
+}
+
+static int max98373_resume(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98373->regmap, false);
+ max98373_reset(max98373, dev);
+ regcache_sync(max98373->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops max98373_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+};
+
+static const struct regmap_config max98373_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98373_R21FF_REV_ID,
+ .reg_defaults = max98373_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98373_reg),
+ .readable_reg = max98373_readable_register,
+ .volatile_reg = max98373_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98373_i2c_probe(struct i2c_client *i2c)
+{
+ int ret = 0;
+ int reg = 0;
+ int i;
+ struct max98373_priv *max98373 = NULL;
+
+ max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
+
+ if (!max98373) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98373);
+
+ /* update interleave mode info */
+ if (device_property_read_bool(&i2c->dev, "maxim,interleave_mode"))
+ max98373->interleave_mode = true;
+ else
+ max98373->interleave_mode = false;
+
+ /* regmap initialization */
+ max98373->regmap = devm_regmap_init_i2c(i2c, &max98373_regmap);
+ if (IS_ERR(max98373->regmap)) {
+ ret = PTR_ERR(max98373->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ max98373->cache_num = ARRAY_SIZE(max98373_i2c_cache_reg);
+ max98373->cache = devm_kcalloc(&i2c->dev, max98373->cache_num,
+ sizeof(*max98373->cache),
+ GFP_KERNEL);
+ if (!max98373->cache) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ for (i = 0; i < max98373->cache_num; i++)
+ max98373->cache[i].reg = max98373_i2c_cache_reg[i];
+
+ /* voltage/current slot & gpio configuration */
+ max98373_slot_config(&i2c->dev, max98373);
+
+ /* Check Revision ID */
+ ret = regmap_read(max98373->regmap,
+ MAX98373_R21FF_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98373_R21FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_max98373,
+ max98373_dai, ARRAY_SIZE(max98373_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98373_i2c_id[] = {
+ { "max98373"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98373_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+ { .compatible = "maxim,max98373", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+ { "MX98373", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static struct i2c_driver max98373_i2c_driver = {
+ .driver = {
+ .name = "max98373",
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = pm_ptr(&max98373_pm),
+ },
+ .probe = max98373_i2c_probe,
+ .id_table = max98373_i2c_id,
+};
+
+module_i2c_driver(max98373_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
new file mode 100644
index 000000000000..88ff215f52b3
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -0,0 +1,888 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "max98373.h"
+#include "max98373-sdw.h"
+
+static const u32 max98373_sdw_cache_reg[] = {
+ MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+ MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+ MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
+static const struct reg_default max98373_reg[] = {
+ {MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
+ {MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
+ {MAX98373_R0042_SCP_INIT_STAT_2, 0x00},
+ {MAX98373_R0044_SCP_CTRL, 0x00},
+ {MAX98373_R0045_SCP_SYSTEM_CTRL, 0x00},
+ {MAX98373_R0046_SCP_DEV_NUMBER, 0x00},
+ {MAX98373_R0050_SCP_DEV_ID_0, 0x21},
+ {MAX98373_R0051_SCP_DEV_ID_1, 0x01},
+ {MAX98373_R0052_SCP_DEV_ID_2, 0x9F},
+ {MAX98373_R0053_SCP_DEV_ID_3, 0x87},
+ {MAX98373_R0054_SCP_DEV_ID_4, 0x08},
+ {MAX98373_R0055_SCP_DEV_ID_5, 0x00},
+ {MAX98373_R0060_SCP_FRAME_CTLR, 0x00},
+ {MAX98373_R0070_SCP_FRAME_CTLR, 0x00},
+ {MAX98373_R0100_DP1_INIT_STAT, 0x00},
+ {MAX98373_R0101_DP1_INIT_MASK, 0x00},
+ {MAX98373_R0102_DP1_PORT_CTRL, 0x00},
+ {MAX98373_R0103_DP1_BLOCK_CTRL_1, 0x00},
+ {MAX98373_R0104_DP1_PREPARE_STATUS, 0x00},
+ {MAX98373_R0105_DP1_PREPARE_CTRL, 0x00},
+ {MAX98373_R0120_DP1_CHANNEL_EN, 0x00},
+ {MAX98373_R0122_DP1_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0123_DP1_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0124_DP1_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0125_DP1_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0126_DP1_HCTRL, 0x00},
+ {MAX98373_R0127_DP1_BLOCK_CTRL3, 0x00},
+ {MAX98373_R0130_DP1_CHANNEL_EN, 0x00},
+ {MAX98373_R0132_DP1_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0133_DP1_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0134_DP1_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0135_DP1_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0136_DP1_HCTRL, 0x0136},
+ {MAX98373_R0137_DP1_BLOCK_CTRL3, 0x00},
+ {MAX98373_R0300_DP3_INIT_STAT, 0x00},
+ {MAX98373_R0301_DP3_INIT_MASK, 0x00},
+ {MAX98373_R0302_DP3_PORT_CTRL, 0x00},
+ {MAX98373_R0303_DP3_BLOCK_CTRL_1, 0x00},
+ {MAX98373_R0304_DP3_PREPARE_STATUS, 0x00},
+ {MAX98373_R0305_DP3_PREPARE_CTRL, 0x00},
+ {MAX98373_R0320_DP3_CHANNEL_EN, 0x00},
+ {MAX98373_R0322_DP3_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0323_DP3_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0324_DP3_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0325_DP3_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0326_DP3_HCTRL, 0x00},
+ {MAX98373_R0327_DP3_BLOCK_CTRL3, 0x00},
+ {MAX98373_R0330_DP3_CHANNEL_EN, 0x00},
+ {MAX98373_R0332_DP3_SAMPLE_CTRL1, 0x00},
+ {MAX98373_R0333_DP3_SAMPLE_CTRL2, 0x00},
+ {MAX98373_R0334_DP3_OFFSET_CTRL1, 0x00},
+ {MAX98373_R0335_DP3_OFFSET_CTRL2, 0x00},
+ {MAX98373_R0336_DP3_HCTRL, 0x00},
+ {MAX98373_R0337_DP3_BLOCK_CTRL3, 0x00},
+ {MAX98373_R2000_SW_RESET, 0x00},
+ {MAX98373_R2001_INT_RAW1, 0x00},
+ {MAX98373_R2002_INT_RAW2, 0x00},
+ {MAX98373_R2003_INT_RAW3, 0x00},
+ {MAX98373_R2004_INT_STATE1, 0x00},
+ {MAX98373_R2005_INT_STATE2, 0x00},
+ {MAX98373_R2006_INT_STATE3, 0x00},
+ {MAX98373_R2007_INT_FLAG1, 0x00},
+ {MAX98373_R2008_INT_FLAG2, 0x00},
+ {MAX98373_R2009_INT_FLAG3, 0x00},
+ {MAX98373_R200A_INT_EN1, 0x00},
+ {MAX98373_R200B_INT_EN2, 0x00},
+ {MAX98373_R200C_INT_EN3, 0x00},
+ {MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+ {MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+ {MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+ {MAX98373_R2010_IRQ_CTRL, 0x00},
+ {MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+ {MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+ {MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+ {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+ {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+ {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+ {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+ {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+ {MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+ {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+ {MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+ {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+ {MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+ {MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+ {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+ {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+ {MAX98373_R202B_PCM_RX_EN, 0x00},
+ {MAX98373_R202C_PCM_TX_EN, 0x00},
+ {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+ {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+ {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+ {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+ {MAX98373_R2034_ICC_TX_CNTL, 0x00},
+ {MAX98373_R2035_ICC_TX_EN, 0x00},
+ {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+ {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+ {MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+ {MAX98373_R203F_AMP_DSP_CFG, 0x02},
+ {MAX98373_R2040_TONE_GEN_CFG, 0x00},
+ {MAX98373_R2041_AMP_CFG, 0x03},
+ {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+ {MAX98373_R2043_AMP_EN, 0x00},
+ {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+ {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+ {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+ {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+ {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+ {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+ {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+ {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+ {MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+ {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+ {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+ {MAX98373_R2097_BDE_L1_THRESH, 0x00},
+ {MAX98373_R2098_BDE_L2_THRESH, 0x00},
+ {MAX98373_R2099_BDE_L3_THRESH, 0x00},
+ {MAX98373_R209A_BDE_L4_THRESH, 0x00},
+ {MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+ {MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+ {MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+ {MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+ {MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+ {MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+ {MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+ {MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+ {MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+ {MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+ {MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+ {MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+ {MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+ {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+ {MAX98373_R20B5_BDE_EN, 0x00},
+ {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+ {MAX98373_R20D1_DHT_CFG, 0x01},
+ {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+ {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+ {MAX98373_R20D4_DHT_EN, 0x00},
+ {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+ {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+ {MAX98373_R20E2_LIMITER_EN, 0x00},
+ {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+ {MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+ {MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R21FF_REV_ID:
+ case MAX98373_R2010_IRQ_CTRL:
+ /* SoundWire Control Port Registers */
+ case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR:
+ /* Soundwire Data Port 1 Registers */
+ case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3:
+ /* Soundwire Data Port 3 Registers */
+ case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3:
+ case MAX98373_R2000_SW_RESET ... MAX98373_R200C_INT_EN3:
+ case MAX98373_R2014_THERM_WARN_THRESH
+ ... MAX98373_R2018_THERM_FOLDBACK_EN:
+ case MAX98373_R201E_PIN_DRIVE_STRENGTH
+ ... MAX98373_R2036_SOUNDWIRE_CTRL:
+ case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+ case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+ ... MAX98373_R2047_IV_SENSE_ADC_EN:
+ case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+ ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+ case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+ case MAX98373_R2097_BDE_L1_THRESH
+ ... MAX98373_R209B_BDE_THRESH_HYST:
+ case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+ case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+ case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+ case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+ ... MAX98373_R20FF_GLOBAL_SHDN:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+ case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+ case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ /* SoundWire Control Port Registers */
+ case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR:
+ /* Soundwire Data Port 1 Registers */
+ case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3:
+ /* Soundwire Data Port 3 Registers */
+ case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3:
+ case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max98373_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = MAX98373_R21FF_REV_ID,
+ .reg_defaults = max98373_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98373_reg),
+ .readable_reg = max98373_readable_register,
+ .volatile_reg = max98373_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+/* Power management functions and structure */
+static int max98373_suspend(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ int i;
+
+ /* cache feedback register values before suspend */
+ for (i = 0; i < max98373->cache_num; i++)
+ regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
+
+ regcache_cache_only(max98373->regmap, true);
+
+ return 0;
+}
+
+#define MAX98373_PROBE_TIMEOUT 5000
+
+static int max98373_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!max98373->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(MAX98373_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(max98373->regmap, false);
+ regcache_sync(max98373->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops max98373_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+ RUNTIME_PM_OPS(max98373_suspend, max98373_resume, NULL)
+};
+
+static int max98373_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* BITMAP: 00001000 Dataport 3 is active */
+ prop->source_ports = BIT(3);
+ /* BITMAP: 00000010 Dataport 1 is active */
+ prop->sink_ports = BIT(1);
+ prop->paging_support = true;
+ prop->clk_stop_timeout = 20;
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ return 0;
+}
+
+static int max98373_io_init(struct sdw_slave *slave)
+{
+ struct device *dev = &slave->dev;
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98373->regmap, false);
+ if (max98373->first_hw_init)
+ regcache_cache_bypass(max98373->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!max98373->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(dev);
+
+ pm_runtime_get_noresume(dev);
+
+ /* Software Reset */
+ max98373_reset(max98373, dev);
+
+ /* Set soundwire mode */
+ regmap_write(max98373->regmap, MAX98373_R2025_AUDIO_IF_MODE, 3);
+ /* Enable ADC */
+ regmap_write(max98373->regmap, MAX98373_R2047_IV_SENSE_ADC_EN, 3);
+ /* Set default Soundwire clock */
+ regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, 5);
+ /* Set default sampling rate for speaker and IVDAC */
+ regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88);
+ /* IV default slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 0xFF);
+ /* L/R mix configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ 0x80);
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ 0x1);
+ /* Enable DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R203F_AMP_DSP_CFG,
+ 0x3);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+ 0x7);
+ /* voltage, current slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2022_PCM_TX_SRC_1,
+ (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+ max98373->v_slot) & 0xFF);
+ if (max98373->v_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->v_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->v_slot - 8), 0);
+
+ if (max98373->i_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->i_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->i_slot - 8), 0);
+
+ /* speaker feedback slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2023_PCM_TX_SRC_2,
+ max98373->spkfb_slot & 0xFF);
+
+ /* Set interleave mode */
+ if (max98373->interleave_mode)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker enable */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2043_AMP_EN,
+ MAX98373_SPK_EN_MASK, 1);
+
+ regmap_write(max98373->regmap, MAX98373_R20B5_BDE_EN, 1);
+ regmap_write(max98373->regmap, MAX98373_R20E2_LIMITER_EN, 1);
+
+ if (max98373->first_hw_init) {
+ regcache_cache_bypass(max98373->regmap, false);
+ regcache_mark_dirty(max98373->regmap);
+ }
+
+ max98373->first_hw_init = true;
+ max98373->hw_init = true;
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int max98373_clock_calculate(struct sdw_slave *slave,
+ unsigned int clk_freq)
+{
+ int x, y;
+ static const int max98373_clk_family[] = {
+ 7680000, 8400000, 9600000, 11289600,
+ 12000000, 12288000, 13000000
+ };
+
+ for (x = 0; x < 4; x++)
+ for (y = 0; y < ARRAY_SIZE(max98373_clk_family); y++)
+ if (clk_freq == (max98373_clk_family[y] >> x))
+ return (x << 3) + y;
+
+ /* Set default clock (12.288 Mhz) if the value is not in the list */
+ dev_err(&slave->dev, "Requested clock not found. (clk_freq = %d)\n",
+ clk_freq);
+ return 0x5;
+}
+
+static int max98373_clock_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct device *dev = &slave->dev;
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (params->curr_dr_freq >> 1);
+
+ /*
+ * Select the proper value for the register based on the
+ * requested clock. If the value is not in the list,
+ * use reasonable default - 12.288 Mhz
+ */
+ value = max98373_clock_calculate(slave, clk_freq);
+
+ /* SWCLK */
+ regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, value);
+
+ /* The default Sampling Rate value for IV is 48KHz*/
+ regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88);
+
+ return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int ret, chan_sz, sampling_rate;
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!max98373->slave)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_config.num = 1;
+
+ if (max98373->slot) {
+ stream_config.ch_count = max98373->slot;
+ port_config.ch_mask = max98373->rx_mask;
+ }
+ } else {
+ port_config.num = 3;
+
+ /* only IV are supported by capture */
+ stream_config.ch_count = 2;
+ port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0);
+ }
+
+ ret = sdw_stream_add_slave(max98373->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return ret;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ /* Channel size configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "Channel size unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+ /* Sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_96000;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set correct sampling frequency */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+
+ return 0;
+}
+
+static int max98373_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!max98373->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(max98373->slave, sdw_stream);
+ return 0;
+}
+
+static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void max98373_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98373_priv *max98373 =
+ snd_soc_component_get_drvdata(component);
+
+ /* tx_mask is unused since it's irrelevant for I/V feedback */
+ if (tx_mask)
+ return -EINVAL;
+
+ if (!rx_mask && !slots && !slot_width)
+ max98373->tdm_mode = false;
+ else
+ max98373->tdm_mode = true;
+
+ max98373->rx_mask = rx_mask;
+ max98373->slot = slots;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98373_dai_sdw_ops = {
+ .hw_params = max98373_sdw_dai_hw_params,
+ .hw_free = max98373_pcm_hw_free,
+ .set_stream = max98373_set_sdw_stream,
+ .shutdown = max98373_shutdown,
+ .set_tdm_slot = max98373_sdw_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver max98373_sdw_dai[] = {
+ {
+ .name = "max98373-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .ops = &max98373_dai_sdw_ops,
+ }
+};
+
+static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+ struct max98373_priv *max98373;
+ int ret;
+ int i;
+ struct device *dev = &slave->dev;
+
+ /* Allocate and assign private driver data structure */
+ max98373 = devm_kzalloc(dev, sizeof(*max98373), GFP_KERNEL);
+ if (!max98373)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, max98373);
+ max98373->regmap = regmap;
+ max98373->slave = slave;
+
+ regcache_cache_only(max98373->regmap, true);
+
+ max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg);
+ max98373->cache = devm_kcalloc(dev, max98373->cache_num,
+ sizeof(*max98373->cache),
+ GFP_KERNEL);
+ if (!max98373->cache)
+ return -ENOMEM;
+
+ for (i = 0; i < max98373->cache_num; i++)
+ max98373->cache[i].reg = max98373_sdw_cache_reg[i];
+
+ /* Read voltage and slot configuration */
+ max98373_slot_config(dev, max98373);
+
+ max98373->hw_init = false;
+ max98373->first_hw_init = false;
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw,
+ max98373_sdw_dai,
+ ARRAY_SIZE(max98373_sdw_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ return 0;
+}
+
+static int max98373_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ max98373->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+ */
+ if (max98373->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return max98373_io_init(slave);
+}
+
+static int max98373_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ int ret;
+
+ ret = max98373_clock_config(slave, params);
+ if (ret < 0)
+ dev_err(&slave->dev, "Invalid clk config");
+
+ return ret;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops max98373_slave_ops = {
+ .read_prop = max98373_read_prop,
+ .update_status = max98373_update_status,
+ .bus_config = max98373_bus_config,
+};
+
+static int max98373_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &max98373_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return max98373_init(slave, regmap);
+}
+
+static int max98373_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+ { .compatible = "maxim,max98373", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+ { "MX98373", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static const struct sdw_device_id max98373_id[] = {
+ SDW_SLAVE_ENTRY(0x019F, 0x8373, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, max98373_id);
+
+static struct sdw_driver max98373_sdw_driver = {
+ .driver = {
+ .name = "max98373",
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = pm_ptr(&max98373_pm),
+ },
+ .probe = max98373_sdw_probe,
+ .remove = max98373_sdw_remove,
+ .ops = &max98373_slave_ops,
+ .id_table = max98373_id,
+};
+
+module_sdw_driver(max98373_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98373 driver SDW");
+MODULE_AUTHOR("Oleg Sherbakov <oleg.sherbakov@maximintegrated.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98373-sdw.h b/sound/soc/codecs/max98373-sdw.h
new file mode 100644
index 000000000000..2d8033515d34
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020 Maxim Integrated */
+
+#ifndef _MAX98373_SDW_H
+#define _MAX98373_SDW_H
+
+#include "max98373.h"
+
+/* SoundWire Slave Control Port (SCP) */
+#define MAX98373_R0040_SCP_INIT_STAT_1 0x0040
+#define MAX98373_R0041_SCP_INIT_MASK_1 0x0041
+#define MAX98373_R0042_SCP_INIT_STAT_2 0x0042
+#define MAX98373_R0044_SCP_CTRL 0x0044
+#define MAX98373_R0045_SCP_SYSTEM_CTRL 0x0045
+#define MAX98373_R0046_SCP_DEV_NUMBER 0x0046
+#define MAX98373_R0050_SCP_DEV_ID_0 0x0050
+#define MAX98373_R0051_SCP_DEV_ID_1 0x0051
+#define MAX98373_R0052_SCP_DEV_ID_2 0x0052
+#define MAX98373_R0053_SCP_DEV_ID_3 0x0053
+#define MAX98373_R0054_SCP_DEV_ID_4 0x0054
+#define MAX98373_R0055_SCP_DEV_ID_5 0x0055
+#define MAX98373_R0060_SCP_FRAME_CTLR 0x0060
+#define MAX98373_R0070_SCP_FRAME_CTLR 0x0070
+
+/* SoundWire Device Data Port (DP) */
+/* Data Port 1 Registers */
+#define MAX98373_R0100_DP1_INIT_STAT 0x0100
+#define MAX98373_R0101_DP1_INIT_MASK 0x0101
+#define MAX98373_R0102_DP1_PORT_CTRL 0x0102
+#define MAX98373_R0103_DP1_BLOCK_CTRL_1 0x0103
+#define MAX98373_R0104_DP1_PREPARE_STATUS 0x0104
+#define MAX98373_R0105_DP1_PREPARE_CTRL 0x0105
+/* Data Port 1 Bank 0 Registers */
+#define MAX98373_R0120_DP1_CHANNEL_EN 0x0120
+#define MAX98373_R0122_DP1_SAMPLE_CTRL1 0x0122
+#define MAX98373_R0123_DP1_SAMPLE_CTRL2 0x0123
+#define MAX98373_R0124_DP1_OFFSET_CTRL1 0x0124
+#define MAX98373_R0125_DP1_OFFSET_CTRL2 0x0125
+#define MAX98373_R0126_DP1_HCTRL 0x0126
+#define MAX98373_R0127_DP1_BLOCK_CTRL3 0x0127
+/* Data Port 1 Bank 1 Registers */
+#define MAX98373_R0130_DP1_CHANNEL_EN 0x0130
+#define MAX98373_R0132_DP1_SAMPLE_CTRL1 0x0132
+#define MAX98373_R0133_DP1_SAMPLE_CTRL2 0x0133
+#define MAX98373_R0134_DP1_OFFSET_CTRL1 0x0134
+#define MAX98373_R0135_DP1_OFFSET_CTRL2 0x0135
+#define MAX98373_R0136_DP1_HCTRL 0x0136
+#define MAX98373_R0137_DP1_BLOCK_CTRL3 0x0137
+/* Data Port 3 Registers */
+#define MAX98373_R0300_DP3_INIT_STAT 0x0300
+#define MAX98373_R0301_DP3_INIT_MASK 0x0301
+#define MAX98373_R0302_DP3_PORT_CTRL 0x0302
+#define MAX98373_R0303_DP3_BLOCK_CTRL_1 0x0303
+#define MAX98373_R0304_DP3_PREPARE_STATUS 0x0304
+#define MAX98373_R0305_DP3_PREPARE_CTRL 0x0305
+/* Data Port 3 Bank 0 Registers */
+#define MAX98373_R0320_DP3_CHANNEL_EN 0x0320
+#define MAX98373_R0322_DP3_SAMPLE_CTRL1 0x0322
+#define MAX98373_R0323_DP3_SAMPLE_CTRL2 0x0323
+#define MAX98373_R0324_DP3_OFFSET_CTRL1 0x0324
+#define MAX98373_R0325_DP3_OFFSET_CTRL2 0x0325
+#define MAX98373_R0326_DP3_HCTRL 0x0326
+#define MAX98373_R0327_DP3_BLOCK_CTRL3 0x0327
+/* Data Port 3 Bank 1 Registers */
+#define MAX98373_R0330_DP3_CHANNEL_EN 0x0330
+#define MAX98373_R0332_DP3_SAMPLE_CTRL1 0x0332
+#define MAX98373_R0333_DP3_SAMPLE_CTRL2 0x0333
+#define MAX98373_R0334_DP3_OFFSET_CTRL1 0x0334
+#define MAX98373_R0335_DP3_OFFSET_CTRL2 0x0335
+#define MAX98373_R0336_DP3_HCTRL 0x0336
+#define MAX98373_R0337_DP3_BLOCK_CTRL3 0x0337
+#endif
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
new file mode 100644
index 000000000000..cfb95fd4f85e
--- /dev/null
+++ b/sound/soc/codecs/max98373.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <sound/tlv.h>
+#include "max98373.h"
+
+static int max98373_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R20FF_GLOBAL_SHDN,
+ MAX98373_GLOBAL_EN_MASK, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R20FF_GLOBAL_SHDN,
+ MAX98373_GLOBAL_EN_MASK, 0);
+ usleep_range(30000, 31000);
+ max98373->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98373_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98373_switch_text);
+
+static const struct snd_kcontrol_new max98373_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98373_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98373_R202C_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_kcontrol_new max98373_spkfb_control =
+ SOC_DAPM_SINGLE("Switch", MAX98373_R2043_AMP_EN, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = {
+SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98373_dai_controls),
+SND_SOC_DAPM_OUTPUT("BE_OUT"),
+SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98373_R2047_IV_SENSE_ADC_EN, 0, 0),
+SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98373_R2047_IV_SENSE_ADC_EN, 1, 0),
+SND_SOC_DAPM_AIF_OUT("Speaker FB Sense", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98373_vi_control),
+SND_SOC_DAPM_SWITCH("SpkFB Sense", SND_SOC_NOPM, 0, 0,
+ &max98373_spkfb_control),
+SND_SOC_DAPM_SIGGEN("VMON"),
+SND_SOC_DAPM_SIGGEN("IMON"),
+SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv,
+ 0, 8, TLV_DB_SCALE_ITEM(0, 50, 0),
+ 9, 10, TLV_DB_SCALE_ITEM(500, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_spkgain_max_tlv,
+ 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_step_size_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(25, 25, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(100, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv,
+ 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-3000, 500, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(-2200, 200, 0),
+ 5, 6, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+ 7, 9, TLV_DB_SCALE_ITEM(-1000, 200, 0),
+ 10, 13, TLV_DB_SCALE_ITEM(-500, 100, 0),
+ 14, 15, TLV_DB_SCALE_ITEM(-100, 50, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv,
+ 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv,
+ 0, 60, TLV_DB_SCALE_ITEM(-1500, 25, 0),
+);
+
+static const char * const max98373_output_voltage_lvl_text[] = {
+ "5.43V", "6.09V", "6.83V", "7.67V", "8.60V",
+ "9.65V", "10.83V", "12.15V", "13.63V", "15.29V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_out_volt_enum,
+ MAX98373_R203E_AMP_PATH_GAIN, 0,
+ max98373_output_voltage_lvl_text);
+
+static const char * const max98373_dht_attack_rate_text[] = {
+ "17.5us", "35us", "70us", "140us",
+ "280us", "560us", "1120us", "2240us"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_attack_rate_enum,
+ MAX98373_R20D2_DHT_ATTACK_CFG, 0,
+ max98373_dht_attack_rate_text);
+
+static const char * const max98373_dht_release_rate_text[] = {
+ "45ms", "225ms", "450ms", "1150ms",
+ "2250ms", "3100ms", "4500ms", "6750ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_release_rate_enum,
+ MAX98373_R20D3_DHT_RELEASE_CFG, 0,
+ max98373_dht_release_rate_text);
+
+static const char * const max98373_limiter_attack_rate_text[] = {
+ "10us", "20us", "40us", "80us",
+ "160us", "320us", "640us", "1.28ms",
+ "2.56ms", "5.12ms", "10.24ms", "20.48ms",
+ "40.96ms", "81.92ms", "16.384ms", "32.768ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_attack_rate_enum,
+ MAX98373_R20E1_LIMITER_ATK_REL_RATES, 4,
+ max98373_limiter_attack_rate_text);
+
+static const char * const max98373_limiter_release_rate_text[] = {
+ "40us", "80us", "160us", "320us",
+ "640us", "1.28ms", "2.56ms", "5.120ms",
+ "10.24ms", "20.48ms", "40.96ms", "81.92ms",
+ "163.84ms", "327.68ms", "655.36ms", "1310.72ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_release_rate_enum,
+ MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0,
+ max98373_limiter_release_rate_text);
+
+static const char * const max98373_ADC_samplerate_text[] = {
+ "333kHz", "192kHz", "64kHz", "48kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
+ MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
+ max98373_ADC_samplerate_text);
+
+static int max98373_feedback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ /*
+ * Register values will be cached before suspend. The cached value
+ * will be a valid value and userspace will happy with that.
+ */
+ for (i = 0; i < max98373->cache_num; i++) {
+ if (mc->reg == max98373->cache[i].reg) {
+ ucontrol->value.integer.value[0] = max98373->cache[i].val;
+ return 0;
+ }
+ }
+ }
+
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new max98373_snd_controls[] = {
+SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Volume Location Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Up Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0),
+/* Speaker Amplifier Overcurrent Automatic Restart Enable */
+SOC_SINGLE("OVC Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_OVC_AUTORESTART_SHIFT, 1, 0),
+/* Thermal Shutdown Automatic Restart Enable */
+SOC_SINGLE("THERM Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_THERM_AUTORESTART_SHIFT, 1, 0),
+/* Clock Monitor Automatic Restart Enable */
+SOC_SINGLE("CMON Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_CMON_AUTORESTART_SHIFT, 1, 0),
+SOC_SINGLE("CLK Monitor Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_CLOCK_MON_SHIFT, 1, 0),
+SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_DITH_SHIFT, 1, 0),
+SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL,
+ 0, 0x7F, 1, max98373_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN,
+ MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv),
+SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN,
+ MAX98373_FS_GAIN_MAX_SHIFT, 9, 0, max98373_spkgain_max_tlv),
+SOC_ENUM("Output Voltage", max98373_out_volt_enum),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN,
+ MAX98373_DHT_EN_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Min Volume", MAX98373_R20D1_DHT_CFG,
+ MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv),
+SOC_SINGLE_TLV("DHT Rot Pnt Volume", MAX98373_R20D1_DHT_CFG,
+ MAX98373_DHT_ROT_PNT_SHIFT, 15, 1, max98373_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Attack Step Volume", MAX98373_R20D2_DHT_ATTACK_CFG,
+ MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_SINGLE_TLV("DHT Release Step Volume", MAX98373_R20D3_DHT_RELEASE_CFG,
+ MAX98373_DHT_RELEASE_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_ENUM("DHT Attack Rate", max98373_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98373_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+ MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+ MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE_EXT("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0,
+ max98373_feedback_get, NULL),
+SOC_SINGLE_EXT("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0,
+ max98373_feedback_get, NULL),
+SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+ 0, 0x3, 0),
+SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+ 0, 0x3, 0),
+SOC_ENUM("ADC SampleRate", max98373_adc_samplerate_enum),
+/* Brownout Detection Engine */
+SOC_SINGLE("BDE Switch", MAX98373_R20B5_BDE_EN, MAX98373_BDE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Mute Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+ MAX98373_LVL4_MUTE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Hold Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+ MAX98373_LVL4_HOLD_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
+SOC_SINGLE_EXT("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0,
+ max98373_feedback_get, NULL),
+SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
+SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
+SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
+SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0),
+SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0),
+SOC_SINGLE_TLV("BDE LVL1 Clip Thresh Volume", MAX98373_R20A9_BDE_L1_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3,
+ 0, 0x3C, 1, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1,
+ 0, 0xF, 1, max98373_limiter_thresh_tlv),
+/* Limiter */
+SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN,
+ MAX98373_LIMITER_EN_SHIFT, 1, 0),
+SOC_SINGLE("Limiter Src Switch", MAX98373_R20E0_LIMITER_THRESH_CFG,
+ MAX98373_LIMITER_THRESH_SRC_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Limiter Thresh Volume", MAX98373_R20E0_LIMITER_THRESH_CFG,
+ MAX98373_LIMITER_THRESH_SHIFT, 15, 0, max98373_limiter_thresh_tlv),
+SOC_ENUM("Limiter Attack Rate", max98373_limiter_attack_rate_enum),
+SOC_ENUM("Limiter Release Rate", max98373_limiter_release_rate_enum),
+};
+
+static const struct snd_soc_dapm_route max98373_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "SpkFB Sense", "Switch", "FBMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+ { "Speaker FB Sense", NULL, "SpkFB Sense" },
+};
+
+void max98373_reset(struct max98373_priv *max98373, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_update_bits(max98373->regmap,
+ MAX98373_R2000_SW_RESET,
+ MAX98373_SOFT_RESET,
+ MAX98373_SOFT_RESET);
+ if (ret)
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(10000, 11000);
+ /* Software Reset Verification */
+ ret = regmap_read(max98373->regmap,
+ MAX98373_R21FF_REV_ID, &reg);
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+ return;
+ }
+ count++;
+ }
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+EXPORT_SYMBOL_GPL(max98373_reset);
+
+static int max98373_probe(struct snd_soc_component *component)
+{
+ struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98373_reset(max98373, component->dev);
+
+ /* IV default slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 0xFF);
+ /* L/R mix configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ 0x80);
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ 0x1);
+ /* Enable DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R203F_AMP_DSP_CFG,
+ 0x3);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+ 0x7);
+ /* voltage, current slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2022_PCM_TX_SRC_1,
+ (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+ max98373->v_slot) & 0xFF);
+ if (max98373->v_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->v_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->v_slot - 8), 0);
+
+ if (max98373->i_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->i_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->i_slot - 8), 0);
+
+ /* enable auto restart function by default */
+ regmap_write(max98373->regmap,
+ MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ 0xF);
+
+ /* speaker feedback slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2023_PCM_TX_SRC_2,
+ max98373->spkfb_slot & 0xFF);
+
+ /* Set interleave mode */
+ if (max98373->interleave_mode)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker enable */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2043_AMP_EN,
+ MAX98373_SPK_EN_MASK, 1);
+
+ return 0;
+}
+
+const struct snd_soc_component_driver soc_codec_dev_max98373 = {
+ .probe = max98373_probe,
+ .controls = max98373_snd_controls,
+ .num_controls = ARRAY_SIZE(max98373_snd_controls),
+ .dapm_widgets = max98373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets),
+ .dapm_routes = max98373_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98373_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98373);
+
+static int max98373_sdw_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+const struct snd_soc_component_driver soc_codec_dev_max98373_sdw = {
+ .probe = max98373_sdw_probe,
+ .controls = max98373_snd_controls,
+ .num_controls = ARRAY_SIZE(max98373_snd_controls),
+ .dapm_widgets = max98373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets),
+ .dapm_routes = max98373_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98373_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98373_sdw);
+
+void max98373_slot_config(struct device *dev,
+ struct max98373_priv *max98373)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+ max98373->v_slot = value & 0xF;
+ else
+ max98373->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+ max98373->i_slot = value & 0xF;
+ else
+ max98373->i_slot = 1;
+
+ /* This will assert RESET */
+ max98373->reset = devm_gpiod_get_optional(dev,
+ "maxim,reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max98373->reset)) {
+ dev_err(dev, "error %ld looking up RESET GPIO line\n",
+ PTR_ERR(max98373->reset));
+ return;
+ }
+
+ /* Cycle reset */
+ if (max98373->reset) {
+ gpiod_set_consumer_name(max98373->reset ,"MAX98373_RESET");
+ gpiod_direction_output(max98373->reset, 1);
+ msleep(50);
+ gpiod_direction_output(max98373->reset, 0);
+ msleep(20);
+ }
+
+ if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
+ max98373->spkfb_slot = value & 0xF;
+ else
+ max98373->spkfb_slot = 2;
+}
+EXPORT_SYMBOL_GPL(max98373_slot_config);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
new file mode 100644
index 000000000000..af3b62217497
--- /dev/null
+++ b/sound/soc/codecs/max98373.h
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2017 Maxim Integrated */
+
+#ifndef _MAX98373_H
+#define _MAX98373_H
+
+#define MAX98373_R2000_SW_RESET 0x2000
+#define MAX98373_R2001_INT_RAW1 0x2001
+#define MAX98373_R2002_INT_RAW2 0x2002
+#define MAX98373_R2003_INT_RAW3 0x2003
+#define MAX98373_R2004_INT_STATE1 0x2004
+#define MAX98373_R2005_INT_STATE2 0x2005
+#define MAX98373_R2006_INT_STATE3 0x2006
+#define MAX98373_R2007_INT_FLAG1 0x2007
+#define MAX98373_R2008_INT_FLAG2 0x2008
+#define MAX98373_R2009_INT_FLAG3 0x2009
+#define MAX98373_R200A_INT_EN1 0x200A
+#define MAX98373_R200B_INT_EN2 0x200B
+#define MAX98373_R200C_INT_EN3 0x200C
+#define MAX98373_R200D_INT_FLAG_CLR1 0x200D
+#define MAX98373_R200E_INT_FLAG_CLR2 0x200E
+#define MAX98373_R200F_INT_FLAG_CLR3 0x200F
+#define MAX98373_R2010_IRQ_CTRL 0x2010
+#define MAX98373_R2014_THERM_WARN_THRESH 0x2014
+#define MAX98373_R2015_THERM_SHDN_THRESH 0x2015
+#define MAX98373_R2016_THERM_HYSTERESIS 0x2016
+#define MAX98373_R2017_THERM_FOLDBACK_SET 0x2017
+#define MAX98373_R2018_THERM_FOLDBACK_EN 0x2018
+#define MAX98373_R201E_PIN_DRIVE_STRENGTH 0x201E
+#define MAX98373_R2020_PCM_TX_HIZ_EN_1 0x2020
+#define MAX98373_R2021_PCM_TX_HIZ_EN_2 0x2021
+#define MAX98373_R2022_PCM_TX_SRC_1 0x2022
+#define MAX98373_R2023_PCM_TX_SRC_2 0x2023
+#define MAX98373_R2024_PCM_DATA_FMT_CFG 0x2024
+#define MAX98373_R2025_AUDIO_IF_MODE 0x2025
+#define MAX98373_R2026_PCM_CLOCK_RATIO 0x2026
+#define MAX98373_R2027_PCM_SR_SETUP_1 0x2027
+#define MAX98373_R2028_PCM_SR_SETUP_2 0x2028
+#define MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 0x2029
+#define MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2 0x202A
+#define MAX98373_R202B_PCM_RX_EN 0x202B
+#define MAX98373_R202C_PCM_TX_EN 0x202C
+#define MAX98373_R202E_ICC_RX_CH_EN_1 0x202E
+#define MAX98373_R202F_ICC_RX_CH_EN_2 0x202F
+#define MAX98373_R2030_ICC_TX_HIZ_EN_1 0x2030
+#define MAX98373_R2031_ICC_TX_HIZ_EN_2 0x2031
+#define MAX98373_R2032_ICC_LINK_EN_CFG 0x2032
+#define MAX98373_R2034_ICC_TX_CNTL 0x2034
+#define MAX98373_R2035_ICC_TX_EN 0x2035
+#define MAX98373_R2036_SOUNDWIRE_CTRL 0x2036
+#define MAX98373_R203D_AMP_DIG_VOL_CTRL 0x203D
+#define MAX98373_R203E_AMP_PATH_GAIN 0x203E
+#define MAX98373_R203F_AMP_DSP_CFG 0x203F
+#define MAX98373_R2040_TONE_GEN_CFG 0x2040
+#define MAX98373_R2041_AMP_CFG 0x2041
+#define MAX98373_R2042_AMP_EDGE_RATE_CFG 0x2042
+#define MAX98373_R2043_AMP_EN 0x2043
+#define MAX98373_R2046_IV_SENSE_ADC_DSP_CFG 0x2046
+#define MAX98373_R2047_IV_SENSE_ADC_EN 0x2047
+#define MAX98373_R2051_MEAS_ADC_SAMPLING_RATE 0x2051
+#define MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG 0x2052
+#define MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG 0x2053
+#define MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK 0x2054
+#define MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK 0x2055
+#define MAX98373_R2056_MEAS_ADC_PVDD_CH_EN 0x2056
+#define MAX98373_R2090_BDE_LVL_HOLD 0x2090
+#define MAX98373_R2091_BDE_GAIN_ATK_REL_RATE 0x2091
+#define MAX98373_R2092_BDE_CLIPPER_MODE 0x2092
+#define MAX98373_R2097_BDE_L1_THRESH 0x2097
+#define MAX98373_R2098_BDE_L2_THRESH 0x2098
+#define MAX98373_R2099_BDE_L3_THRESH 0x2099
+#define MAX98373_R209A_BDE_L4_THRESH 0x209A
+#define MAX98373_R209B_BDE_THRESH_HYST 0x209B
+#define MAX98373_R20A8_BDE_L1_CFG_1 0x20A8
+#define MAX98373_R20A9_BDE_L1_CFG_2 0x20A9
+#define MAX98373_R20AA_BDE_L1_CFG_3 0x20AA
+#define MAX98373_R20AB_BDE_L2_CFG_1 0x20AB
+#define MAX98373_R20AC_BDE_L2_CFG_2 0x20AC
+#define MAX98373_R20AD_BDE_L2_CFG_3 0x20AD
+#define MAX98373_R20AE_BDE_L3_CFG_1 0x20AE
+#define MAX98373_R20AF_BDE_L3_CFG_2 0x20AF
+#define MAX98373_R20B0_BDE_L3_CFG_3 0x20B0
+#define MAX98373_R20B1_BDE_L4_CFG_1 0x20B1
+#define MAX98373_R20B2_BDE_L4_CFG_2 0x20B2
+#define MAX98373_R20B3_BDE_L4_CFG_3 0x20B3
+#define MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE 0x20B4
+#define MAX98373_R20B5_BDE_EN 0x20B5
+#define MAX98373_R20B6_BDE_CUR_STATE_READBACK 0x20B6
+#define MAX98373_R20D1_DHT_CFG 0x20D1
+#define MAX98373_R20D2_DHT_ATTACK_CFG 0x20D2
+#define MAX98373_R20D3_DHT_RELEASE_CFG 0x20D3
+#define MAX98373_R20D4_DHT_EN 0x20D4
+#define MAX98373_R20E0_LIMITER_THRESH_CFG 0x20E0
+#define MAX98373_R20E1_LIMITER_ATK_REL_RATES 0x20E1
+#define MAX98373_R20E2_LIMITER_EN 0x20E2
+#define MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG 0x20FE
+#define MAX98373_R20FF_GLOBAL_SHDN 0x20FF
+#define MAX98373_R21FF_REV_ID 0x21FF
+
+/* MAX98373_R2022_PCM_TX_SRC_1 */
+#define MAX98373_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98373_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98373_R2024_PCM_DATA_FMT_CFG */
+#define MAX98373_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98373_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98373_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98373_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98373_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98373_R2026_PCM_CLOCK_RATIO */
+#define MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98373_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98373_R2027_PCM_SR_SETUP_1 */
+#define MAX98373_PCM_SR_SET1_SR_MASK (0xF << 0)
+#define MAX98373_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98373_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98373_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98373_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98373_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98373_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98373_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98373_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98373_PCM_SR_SET1_SR_48000 (0x8 << 0)
+#define MAX98373_PCM_SR_SET1_SR_88200 (0x9 << 0)
+#define MAX98373_PCM_SR_SET1_SR_96000 (0xA << 0)
+
+/* MAX98373_R2028_PCM_SR_SETUP_2 */
+#define MAX98373_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98373_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98373_PCM_SR_SET2_IVADC_SR_MASK (0xF << 0)
+
+/* MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 */
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+#define MAX98373_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0)
+
+/* MAX98373_R203E_AMP_PATH_GAIN */
+#define MAX98373_SPK_DIGI_GAIN_MASK (0xF << 4)
+#define MAX98373_SPK_DIGI_GAIN_SHIFT (4)
+#define MAX98373_FS_GAIN_MAX_MASK (0xF << 0)
+#define MAX98373_FS_GAIN_MAX_SHIFT (0)
+
+/* MAX98373_R203F_AMP_DSP_CFG */
+#define MAX98373_AMP_DSP_CFG_DCBLK_SHIFT (0)
+#define MAX98373_AMP_DSP_CFG_DITH_SHIFT (1)
+#define MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT (2)
+#define MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT (3)
+#define MAX98373_AMP_DSP_CFG_DAC_INV_SHIFT (5)
+#define MAX98373_AMP_VOL_SEL_SHIFT (7)
+
+/* MAX98373_R2043_AMP_EN */
+#define MAX98373_SPKFB_EN_MASK (0x1 << 1)
+#define MAX98373_SPK_EN_MASK (0x1 << 0)
+#define MAX98373_SPKFB_EN_SHIFT (1)
+
+/*MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG */
+#define MAX98373_FLT_EN_SHIFT (4)
+
+/* MAX98373_R20B2_BDE_L4_CFG_2 */
+#define MAX98373_LVL4_MUTE_EN_SHIFT (7)
+#define MAX98373_LVL4_HOLD_EN_SHIFT (6)
+
+/* MAX98373_R20B5_BDE_EN */
+#define MAX98373_BDE_EN_SHIFT (0)
+
+/* MAX98373_R20D1_DHT_CFG */
+#define MAX98373_DHT_SPK_GAIN_MIN_SHIFT (4)
+#define MAX98373_DHT_ROT_PNT_SHIFT (0)
+
+/* MAX98373_R20D2_DHT_ATTACK_CFG */
+#define MAX98373_DHT_ATTACK_STEP_SHIFT (3)
+#define MAX98373_DHT_ATTACK_RATE_SHIFT (0)
+
+/* MAX98373_R20D3_DHT_RELEASE_CFG */
+#define MAX98373_DHT_RELEASE_STEP_SHIFT (3)
+#define MAX98373_DHT_RELEASE_RATE_SHIFT (0)
+
+/* MAX98373_R20D4_DHT_EN */
+#define MAX98373_DHT_EN_SHIFT (0)
+
+/* MAX98373_R20E0_LIMITER_THRESH_CFG */
+#define MAX98373_LIMITER_THRESH_SHIFT (2)
+#define MAX98373_LIMITER_THRESH_SRC_SHIFT (0)
+
+/* MAX98373_R20E2_LIMITER_EN */
+#define MAX98373_LIMITER_EN_SHIFT (0)
+
+/* MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG */
+#define MAX98373_OVC_AUTORESTART_SHIFT (3)
+#define MAX98373_THERM_AUTORESTART_SHIFT (2)
+#define MAX98373_CMON_AUTORESTART_SHIFT (1)
+#define MAX98373_CLOCK_MON_SHIFT (0)
+
+/* MAX98373_R20FF_GLOBAL_SHDN */
+#define MAX98373_GLOBAL_EN_MASK (0x1 << 0)
+
+/* MAX98373_R2000_SW_RESET */
+#define MAX98373_SOFT_RESET (0x1 << 0)
+
+struct max98373_cache {
+ u32 reg;
+ u32 val;
+};
+
+struct max98373_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ bool tdm_mode;
+ /* cache for reading a valid fake feedback value */
+ struct max98373_cache *cache;
+ int cache_num;
+ /* variables to support soundwire */
+ struct sdw_slave *slave;
+ bool hw_init;
+ bool first_hw_init;
+ int slot;
+ unsigned int rx_mask;
+};
+
+extern const struct snd_soc_component_driver soc_codec_dev_max98373;
+extern const struct snd_soc_component_driver soc_codec_dev_max98373_sdw;
+
+void max98373_reset(struct max98373_priv *max98373, struct device *dev);
+void max98373_slot_config(struct device *dev,
+ struct max98373_priv *max98373);
+#endif
diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c
new file mode 100644
index 000000000000..076f15a9867e
--- /dev/null
+++ b/sound/soc/codecs/max98388.c
@@ -0,0 +1,1013 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98388.h"
+
+static const struct reg_default max98388_reg[] = {
+ {MAX98388_R2000_SW_RESET, 0x00},
+ {MAX98388_R2001_INT_RAW1, 0x00},
+ {MAX98388_R2002_INT_RAW2, 0x00},
+ {MAX98388_R2004_INT_STATE1, 0x00},
+ {MAX98388_R2005_INT_STATE2, 0x00},
+ {MAX98388_R2020_THERM_WARN_THRESH, 0x0A},
+ {MAX98388_R2031_SPK_MON_THRESH, 0x58},
+ {MAX98388_R2032_SPK_MON_LD_SEL, 0x08},
+ {MAX98388_R2033_SPK_MON_DURATION, 0x02},
+ {MAX98388_R2037_ERR_MON_CTRL, 0x01},
+ {MAX98388_R2040_PCM_MODE_CFG, 0xC0},
+ {MAX98388_R2041_PCM_CLK_SETUP, 0x04},
+ {MAX98388_R2042_PCM_SR_SETUP, 0x88},
+ {MAX98388_R2044_PCM_TX_CTRL1, 0x00},
+ {MAX98388_R2045_PCM_TX_CTRL2, 0x00},
+ {MAX98388_R2050_PCM_TX_HIZ_CTRL1, 0xFF},
+ {MAX98388_R2051_PCM_TX_HIZ_CTRL2, 0xFF},
+ {MAX98388_R2052_PCM_TX_HIZ_CTRL3, 0xFF},
+ {MAX98388_R2053_PCM_TX_HIZ_CTRL4, 0xFF},
+ {MAX98388_R2054_PCM_TX_HIZ_CTRL5, 0xFF},
+ {MAX98388_R2055_PCM_TX_HIZ_CTRL6, 0xFF},
+ {MAX98388_R2056_PCM_TX_HIZ_CTRL7, 0xFF},
+ {MAX98388_R2057_PCM_TX_HIZ_CTRL8, 0xFF},
+ {MAX98388_R2058_PCM_RX_SRC1, 0x00},
+ {MAX98388_R2059_PCM_RX_SRC2, 0x01},
+ {MAX98388_R205C_PCM_TX_DRIVE_STRENGTH, 0x00},
+ {MAX98388_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98388_R205E_PCM_RX_EN, 0x00},
+ {MAX98388_R205F_PCM_TX_EN, 0x00},
+ {MAX98388_R2090_SPK_CH_VOL_CTRL, 0x00},
+ {MAX98388_R2091_SPK_CH_CFG, 0x02},
+ {MAX98388_R2092_SPK_AMP_OUT_CFG, 0x03},
+ {MAX98388_R2093_SPK_AMP_SSM_CFG, 0x01},
+ {MAX98388_R2094_SPK_AMP_ER_CTRL, 0x00},
+ {MAX98388_R209E_SPK_CH_PINK_NOISE_EN, 0x00},
+ {MAX98388_R209F_SPK_CH_AMP_EN, 0x00},
+ {MAX98388_R20A0_IV_DATA_DSP_CTRL, 0x10},
+ {MAX98388_R20A7_IV_DATA_EN, 0x00},
+ {MAX98388_R20E0_BP_ALC_THRESH, 0x04},
+ {MAX98388_R20E1_BP_ALC_RATES, 0x20},
+ {MAX98388_R20E2_BP_ALC_ATTEN, 0x06},
+ {MAX98388_R20E3_BP_ALC_REL, 0x02},
+ {MAX98388_R20E4_BP_ALC_MUTE, 0x33},
+ {MAX98388_R20EE_BP_INF_HOLD_REL, 0x00},
+ {MAX98388_R20EF_BP_ALC_EN, 0x00},
+ {MAX98388_R210E_AUTO_RESTART, 0x00},
+ {MAX98388_R210F_GLOBAL_EN, 0x00},
+ {MAX98388_R22FF_REV_ID, 0x00},
+};
+
+static int max98388_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 0);
+ usleep_range(30000, 31000);
+ max98388->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98388_monomix_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98388_R2058_PCM_RX_SRC1,
+ MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98388_monomix_switch_text);
+
+static const struct snd_kcontrol_new max98388_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98388_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98388_R205F_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget max98388_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98388_R205E_PCM_RX_EN, 0, 0, max98388_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98388_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98388_R20A7_IV_DATA_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98388_R20A7_IV_DATA_EN, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Voltage", NULL,
+ MAX98388_R205D_PCM_TX_SRC_EN, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Current", NULL,
+ MAX98388_R205D_PCM_TX_SRC_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98388_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98388_digital_tlv, -6350, 50, 1);
+static DECLARE_TLV_DB_SCALE(max98388_amp_gain_tlv, -300, 300, 0);
+
+static const char * const max98388_alc_max_atten_text[] = {
+ "0dBFS", "-1dBFS", "-2dBFS", "-3dBFS", "-4dBFS", "-5dBFS",
+ "-6dBFS", "-7dBFS", "-8dBFS", "-9dBFS", "-10dBFS", "-11dBFS",
+ "-12dBFS", "-13dBFS", "-14dBFS", "-15dBFS"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_max_atten_enum,
+ MAX98388_R20E2_BP_ALC_ATTEN,
+ MAX98388_ALC_MAX_ATTEN_SHIFT,
+ max98388_alc_max_atten_text);
+
+static const char * const max98388_thermal_warn_text[] = {
+ "95C", "105C", "115C", "125C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_thermal_warning_thresh_enum,
+ MAX98388_R2020_THERM_WARN_THRESH,
+ MAX98388_THERM_WARN_THRESH_SHIFT,
+ max98388_thermal_warn_text);
+
+static const char * const max98388_thermal_shutdown_text[] = {
+ "135C", "145C", "155C", "165C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_thermal_shutdown_thresh_enum,
+ MAX98388_R2020_THERM_WARN_THRESH,
+ MAX98388_THERM_SHDN_THRESH_SHIFT,
+ max98388_thermal_shutdown_text);
+
+static const char * const max98388_alc_thresh_single_text[] = {
+ "3.625V", "3.550V", "3.475V", "3.400V", "3.325V", "3.250V",
+ "3.175V", "3.100V", "3.025V", "2.950V", "2.875V", "2.800V",
+ "2.725V", "2.650V", "2.575V", "2.500V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_thresh_single_enum,
+ MAX98388_R20E0_BP_ALC_THRESH,
+ MAX98388_ALC_THRESH_SHIFT,
+ max98388_alc_thresh_single_text);
+
+static const char * const max98388_alc_attack_rate_text[] = {
+ "0", "10us", "20us", "40us", "80us", "160us",
+ "320us", "640us", "1.28ms", "2.56ms", "5.12ms", "10.24ms",
+ "20.48ms", "40.96ms", "81.92ms", "163.84ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_attack_rate_enum,
+ MAX98388_R20E1_BP_ALC_RATES,
+ MAX98388_ALC_ATTACK_RATE_SHIFT,
+ max98388_alc_attack_rate_text);
+
+static const char * const max98388_alc_release_rate_text[] = {
+ "20us", "40us", "80us", "160us", "320us", "640us",
+ "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms",
+ "81.92ms", "163.84ms", "327.68ms", "655.36ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_release_rate_enum,
+ MAX98388_R20E1_BP_ALC_RATES,
+ MAX98388_ALC_RELEASE_RATE_SHIFT,
+ max98388_alc_release_rate_text);
+
+static const char * const max98388_alc_debounce_text[] = {
+ "0.01ms", "0.1ms", "1ms", "10ms", "100ms", "250ms", "500ms", "hold"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_debouce_enum,
+ MAX98388_R20E3_BP_ALC_REL,
+ MAX98388_ALC_DEBOUNCE_TIME_SHIFT,
+ max98388_alc_debounce_text);
+
+static const char * const max98388_alc_mute_delay_text[] = {
+ "0.01ms", "0.05ms", "0.1ms", "0.5ms", "1ms", "5ms", "25ms", "250ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_alc_mute_delay_enum,
+ MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_MUTE_DELAY_SHIFT,
+ max98388_alc_mute_delay_text);
+
+static const char * const max98388_spkmon_duration_text[] = {
+ "10ms", "25ms", "50ms", "75ms", "100ms", "200ms", "300ms", "400ms",
+ "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms", "1100ms", "1200ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_spkmon_duration_enum,
+ MAX98388_R2033_SPK_MON_DURATION,
+ MAX98388_SPKMON_DURATION_SHIFT,
+ max98388_spkmon_duration_text);
+
+static const char * const max98388_spkmon_thresh_text[] = {
+ "0.03V", "0.06V", "0.09V", "0.12V", "0.15V", "0.18V", "0.20V", "0.23V",
+ "0.26V", "0.29V", "0.32V", "0.35V", "0.38V", "0.41V", "0.44V", "0.47V",
+ "0.50V", "0.53V", "0.56V", "0.58V", "0.61V", "0.64V", "0.67V", "0.70V",
+ "0.73V", "0.76V", "0.79V", "0.82V", "0.85V", "0.88V", "0.91V", "0.94V",
+ "0.96V", "0.99V", "1.02V", "1.05V", "1.08V", "1.11V", "1.14V", "1.17V",
+ "1.20V", "1.23V", "1.26V", "1.29V", "1.32V", "1.35V", "1.37V", "1.40V",
+ "1.43V", "1.46V", "1.49V", "1.52V", "1.55V", "1.58V", "1.61V", "1.64V",
+ "1.67V", "1.70V", "1.73V", "1.75V", "1.78V", "1.81V", "1.84V", "1.87V",
+ "1.90V", "1.93V", "1.96V", "1.99V", "2.02V", "2.05V", "2.08V", "2.11V",
+ "2.13V", "2.16V", "2.19V", "2.22V", "2.25V", "2.28V", "2.31V", "2.34V",
+ "2.37V", "2.40V", "2.43V", "2.46V", "2.49V", "2.51V", "2.54V", "2.57V",
+ "2.60V", "2.63V", "2.66V", "2.69V", "2.72V", "2.75V", "2.78V", "2.81V",
+ "2.84V", "2.87V", "2.89V", "2.92V", "2.95V", "2.98V", "3.01V", "3.04V",
+ "3.07V", "3.10V", "3.13V", "3.16V", "3.19V", "3.22V", "3.25V", "3.27V",
+ "3.30V", "3.33V", "3.36V", "3.39V", "3.42V", "3.45V", "3.48V", "3.51V",
+ "3.54V", "3.57V", "3.60V", "3.63V", "3.66V", "3.68V", "3.71V", "3.74V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_spkmon_thresh_enum,
+ MAX98388_R2031_SPK_MON_THRESH,
+ MAX98388_SPKMON_THRESH_SHIFT,
+ max98388_spkmon_thresh_text);
+
+static const char * const max98388_spkmon_load_text[] = {
+ "2.00ohm", "2.25ohm", "2.50ohm", "2.75ohm", "3.00ohm", "3.25ohm",
+ "3.50ohm", "3.75ohm", "4.00ohm", "4.25ohm", "4.50ohm", "4.75ohm",
+ "5.00ohm", "5.25ohm", "5.50ohm", "5.75ohm", "6.00ohm", "6.25ohm",
+ "6.50ohm", "6.75ohm", "7.00ohm", "7.25ohm", "7.50ohm", "7.75ohm",
+ "8.00ohm", "8.25ohm", "8.50ohm", "8.75ohm", "9.00ohm", "9.25ohm",
+ "9.50ohm", "9.75ohm", "10.00ohm", "10.25ohm", "10.50ohm", "10.75ohm",
+ "11.00ohm", "11.25ohm", "11.50ohm", "11.75ohm", "12.00ohm", "12.25ohm",
+ "12.50ohm", "12.75ohm", "13.00ohm", "13.25ohm", "13.50ohm", "13.75ohm",
+ "14.00ohm", "14.25ohm", "14.50ohm", "14.75ohm", "15.00ohm", "15.25ohm",
+ "15.50ohm", "15.75ohm", "16.00ohm", "16.25ohm", "16.50ohm", "16.75ohm",
+ "17.00ohm", "17.25ohm", "17.50ohm", "17.75ohm", "18.00ohm", "18.25ohm",
+ "18.50ohm", "18.75ohm", "19.00ohm", "19.25ohm", "19.50ohm", "19.75ohm",
+ "20.00ohm", "20.25ohm", "20.50ohm", "20.75ohm", "21.00ohm", "21.25ohm",
+ "21.50ohm", "21.75ohm", "22.00ohm", "22.25ohm", "22.50ohm", "22.75ohm",
+ "23.00ohm", "23.25ohm", "23.50ohm", "23.75ohm", "24.00ohm", "24.25ohm",
+ "24.50ohm", "24.75ohm", "25.00ohm", "25.25ohm", "25.50ohm", "25.75ohm",
+ "26.00ohm", "26.25ohm", "26.50ohm", "26.75ohm", "27.00ohm", "27.25ohm",
+ "27.50ohm", "27.75ohm", "28.00ohm", "28.25ohm", "28.50ohm", "28.75ohm",
+ "29.00ohm", "29.25ohm", "29.50ohm", "29.75ohm", "30.00ohm", "30.25ohm",
+ "30.50ohm", "30.75ohm", "31.00ohm", "31.25ohm", "31.50ohm", "31.75ohm",
+ "32.00ohm", "32.25ohm", "32.50ohm", "32.75ohm", "33.00ohm", "33.25ohm",
+ "33.50ohm", "33.75ohm"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_spkmon_load_enum,
+ MAX98388_R2032_SPK_MON_LD_SEL,
+ MAX98388_SPKMON_LOAD_SHIFT,
+ max98388_spkmon_load_text);
+
+static const char * const max98388_edge_rate_text[] = {
+ "Normal", "Reduced", "Maximum", "Increased",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_falling_enum,
+ MAX98388_R2094_SPK_AMP_ER_CTRL,
+ MAX98388_EDGE_RATE_FALL_SHIFT,
+ max98388_edge_rate_text);
+
+static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_rising_enum,
+ MAX98388_R2094_SPK_AMP_ER_CTRL,
+ MAX98388_EDGE_RATE_RISE_SHIFT,
+ max98388_edge_rate_text);
+
+static const char * const max98388_ssm_mod_text[] = {
+ "1.5%", "3.0%", "4.5%", "6.0%",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98388_ssm_mod_enum,
+ MAX98388_R2093_SPK_AMP_SSM_CFG,
+ MAX98388_SPK_AMP_SSM_MOD_SHIFT,
+ max98388_ssm_mod_text);
+
+static const struct snd_kcontrol_new max98388_snd_controls[] = {
+ SOC_SINGLE("Ramp Up Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_VOL_RMPDN_SHIFT, 1, 0),
+ /* Two Cell Mode Enable */
+ SOC_SINGLE("OP Mode Switch", MAX98388_R2092_SPK_AMP_OUT_CFG,
+ MAX98388_SPK_AMP_OUT_MODE_SHIFT, 1, 0),
+ /* Speaker Amplifier Overcurrent Automatic Restart Enable */
+ SOC_SINGLE("OVC Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_OVC_AUTORESTART_SHIFT, 1, 0),
+ /* Thermal Shutdown Automatic Restart Enable */
+ SOC_SINGLE("THERM Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_THERM_AUTORESTART_SHIFT, 1, 0),
+ /* PVDD UVLO Auto Restart */
+ SOC_SINGLE("UVLO Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_PVDD_UVLO_AUTORESTART_SHIFT, 1, 0),
+ /* Clock Monitor Automatic Restart Enable */
+ SOC_SINGLE("CMON Autorestart Switch", MAX98388_R210E_AUTO_RESTART,
+ MAX98388_CMON_AUTORESTART_SHIFT, 1, 0),
+ SOC_SINGLE("CLK Monitor Switch", MAX98388_R2037_ERR_MON_CTRL,
+ MAX98388_CLOCK_MON_SHIFT, 1, 0),
+ /* Pinknoise Generator Enable */
+ SOC_SINGLE("Pinknoise Gen Switch", MAX98388_R209E_SPK_CH_PINK_NOISE_EN,
+ MAX98388_PINK_NOISE_GEN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("VI Dither Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ MAX98388_AMP_DSP_CTRL_DITH_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98388_R2091_SPK_CH_CFG,
+ MAX98388_SPK_CFG_DCBLK_SHIFT, 1, 0),
+ SOC_SINGLE("Voltage DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT, 1, 0),
+ SOC_SINGLE("Current DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT, 1, 0),
+ /* Digital Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98388_R2090_SPK_CH_VOL_CTRL,
+ 0, 0x7F, 1, max98388_digital_tlv),
+ /* Speaker Volume */
+ SOC_SINGLE_TLV("Speaker Volume", MAX98388_R2092_SPK_AMP_OUT_CFG,
+ 0, 5, 0, max98388_amp_gain_tlv),
+ SOC_ENUM("Thermal Warn Thresh", max98388_thermal_warning_thresh_enum),
+ SOC_ENUM("Thermal SHDN Thresh", max98388_thermal_shutdown_thresh_enum),
+ /* Brownout Protection Automatic Level Control */
+ SOC_SINGLE("ALC Switch", MAX98388_R20EF_BP_ALC_EN, 0, 1, 0),
+ SOC_ENUM("ALC Thresh", max98388_alc_thresh_single_enum),
+ SOC_ENUM("ALC Attack Rate", max98388_alc_attack_rate_enum),
+ SOC_ENUM("ALC Release Rate", max98388_alc_release_rate_enum),
+ SOC_ENUM("ALC Max Atten", max98388_alc_max_atten_enum),
+ SOC_ENUM("ALC Debounce Time", max98388_alc_debouce_enum),
+ SOC_SINGLE("ALC Unmute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Mute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_MUTE_RAMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Mute Switch", MAX98388_R20E4_BP_ALC_MUTE,
+ MAX98388_ALC_MUTE_EN_SHIFT, 1, 0),
+ SOC_ENUM("ALC Mute Delay", max98388_alc_mute_delay_enum),
+ /* Speaker Monitor */
+ SOC_SINGLE("SPKMON Switch", MAX98388_R2037_ERR_MON_CTRL,
+ MAX98388_SPK_MON_SHIFT, 1, 0),
+ SOC_ENUM("SPKMON Thresh", max98388_spkmon_thresh_enum),
+ SOC_ENUM("SPKMON Load", max98388_spkmon_load_enum),
+ SOC_ENUM("SPKMON Duration", max98388_spkmon_duration_enum),
+ /* General Parameters */
+ SOC_ENUM("Fall Slew Rate", max98388_edge_rate_falling_enum),
+ SOC_ENUM("Rise Slew Rate", max98388_edge_rate_rising_enum),
+ SOC_SINGLE("AMP SSM Switch", MAX98388_R2093_SPK_AMP_SSM_CFG,
+ MAX98388_SPK_AMP_SSM_EN_SHIFT, 1, 0),
+ SOC_ENUM("AMP SSM Mod", max98388_ssm_mod_enum),
+};
+
+static const struct snd_soc_dapm_route max98388_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "ADC Voltage", NULL, "VMON"},
+ { "ADC Current", NULL, "IMON"},
+ { "VI Sense", "Switch", "ADC Voltage"},
+ { "VI Sense", "Switch", "ADC Current"},
+ { "Voltage Sense", NULL, "VI Sense"},
+ { "Current Sense", NULL, "VI Sense"},
+};
+
+static void max98388_reset(struct max98388_priv *max98388, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_update_bits(max98388->regmap,
+ MAX98388_R2000_SW_RESET,
+ MAX98388_SOFT_RESET,
+ MAX98388_SOFT_RESET);
+ if (ret)
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(10000, 11000);
+ /* Software Reset Verification */
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R22FF_REV_ID, &reg);
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+ return;
+ }
+ count++;
+ }
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+
+static int max98388_probe(struct snd_soc_component *component)
+{
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98388_reset(max98388, component->dev);
+
+ /* General channel source configuration */
+ regmap_write(max98388->regmap,
+ MAX98388_R2059_PCM_RX_SRC2,
+ 0x10);
+
+ /* Enable DC blocker */
+ regmap_write(max98388->regmap,
+ MAX98388_R2091_SPK_CH_CFG,
+ 0x1);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98388->regmap,
+ MAX98388_R20A0_IV_DATA_DSP_CTRL,
+ 0x3);
+ /* TX slot configuration */
+ regmap_write(max98388->regmap,
+ MAX98388_R2044_PCM_TX_CTRL1,
+ max98388->v_slot);
+
+ regmap_write(max98388->regmap,
+ MAX98388_R2045_PCM_TX_CTRL2,
+ max98388->i_slot);
+ /* Enable Auto-restart behavior by default */
+ regmap_write(max98388->regmap,
+ MAX98388_R210E_AUTO_RESTART, 0xF);
+ /* Set interleave mode */
+ if (max98388->interleave_mode)
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98388_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker Amplifier Channel Enable */
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R209F_SPK_CH_AMP_EN,
+ MAX98388_SPK_EN_MASK, 1);
+
+ return 0;
+}
+
+static int max98388_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2041_PCM_CLK_SETUP,
+ MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98388_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98388_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98388_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98388_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98388_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98388_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98388_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98388->ch_size;
+ int value;
+
+ if (!max98388->tdm_mode) {
+ /* BCLK configuration */
+ value = max98388_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2041_PCM_CLK_SETUP,
+ MAX98388_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98388_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+ int ret, reg;
+ int status = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98388->ch_size = snd_pcm_format_width(params_format(params));
+
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ goto err;
+
+ /* GLOBAL_EN OFF prior to the channel size re-configure */
+ if (chan_sz != (reg & MAX98388_PCM_MODE_CFG_CHANSZ_MASK)) {
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ goto err;
+
+ if (status) {
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 0);
+ usleep_range(30000, 31000);
+ }
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+ }
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98388_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98388_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98388_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98388_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98388_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98388_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98388_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98388_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98388_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98388_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98388_PCM_SR_96000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2042_PCM_SR_SETUP,
+ MAX98388_PCM_SR_MASK,
+ sampling_rate);
+
+ /* set sampling rate of IV */
+ if (max98388->interleave_mode &&
+ sampling_rate > MAX98388_PCM_SR_16000)
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2042_PCM_SR_SETUP,
+ MAX98388_PCM_SR_IV_MASK,
+ (sampling_rate - 3) << MAX98388_PCM_SR_IV_SHIFT);
+ else
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2042_PCM_SR_SETUP,
+ MAX98388_PCM_SR_IV_MASK,
+ sampling_rate << MAX98388_PCM_SR_IV_SHIFT);
+
+ ret = max98388_set_clock(component, params);
+
+ if (status) {
+ regmap_write(max98388->regmap,
+ MAX98388_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ }
+
+ return ret;
+
+err:
+ return -EINVAL;
+}
+
+#define MAX_NUM_SLOTS 16
+#define MAX_NUM_CH 2
+
+static int max98388_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+ unsigned int mask;
+ int cnt, slot_found;
+ int addr, bits;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98388->tdm_mode = false;
+ else
+ max98388->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98388_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2041_PCM_CLK_SETUP,
+ MAX98388_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2040_PCM_MODE_CFG,
+ MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ slot_found = 0;
+ mask = rx_mask;
+ for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) {
+ if (mask & 0x1) {
+ if (slot_found == 0)
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2059_PCM_RX_SRC2,
+ MAX98388_RX_SRC_CH0_SHIFT,
+ cnt);
+ else
+ regmap_update_bits(max98388->regmap,
+ MAX98388_R2059_PCM_RX_SRC2,
+ MAX98388_RX_SRC_CH1_SHIFT,
+ cnt);
+ slot_found++;
+ if (slot_found >= MAX_NUM_CH)
+ break;
+ }
+ }
+
+ /* speaker feedback slot configuration */
+ slot_found = 0;
+ mask = tx_mask;
+ for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) {
+ if (mask & 0x1) {
+ addr = MAX98388_R2044_PCM_TX_CTRL1 + (cnt / 8);
+ bits = cnt % 8;
+ regmap_update_bits(max98388->regmap, addr, bits, bits);
+ slot_found++;
+ if (slot_found >= MAX_NUM_CH)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#define MAX98388_RATES SNDRV_PCM_RATE_8000_96000
+
+#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98388_dai_ops = {
+ .set_fmt = max98388_dai_set_fmt,
+ .hw_params = max98388_dai_hw_params,
+ .set_tdm_slot = max98388_dai_tdm_slot,
+};
+
+static bool max98388_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case MAX98388_R2001_INT_RAW1 ... MAX98388_R2002_INT_RAW2:
+ case MAX98388_R2004_INT_STATE1... MAX98388_R2005_INT_STATE2:
+ case MAX98388_R2020_THERM_WARN_THRESH:
+ case MAX98388_R2031_SPK_MON_THRESH
+ ... MAX98388_R2033_SPK_MON_DURATION:
+ case MAX98388_R2037_ERR_MON_CTRL:
+ case MAX98388_R2040_PCM_MODE_CFG
+ ... MAX98388_R2042_PCM_SR_SETUP:
+ case MAX98388_R2044_PCM_TX_CTRL1
+ ... MAX98388_R2045_PCM_TX_CTRL2:
+ case MAX98388_R2050_PCM_TX_HIZ_CTRL1
+ ... MAX98388_R2059_PCM_RX_SRC2:
+ case MAX98388_R205C_PCM_TX_DRIVE_STRENGTH
+ ... MAX98388_R205F_PCM_TX_EN:
+ case MAX98388_R2090_SPK_CH_VOL_CTRL
+ ... MAX98388_R2094_SPK_AMP_ER_CTRL:
+ case MAX98388_R209E_SPK_CH_PINK_NOISE_EN
+ ... MAX98388_R209F_SPK_CH_AMP_EN:
+ case MAX98388_R20A0_IV_DATA_DSP_CTRL:
+ case MAX98388_R20A7_IV_DATA_EN:
+ case MAX98388_R20E0_BP_ALC_THRESH ... MAX98388_R20E4_BP_ALC_MUTE:
+ case MAX98388_R20EE_BP_INF_HOLD_REL ... MAX98388_R20EF_BP_ALC_EN:
+ case MAX98388_R210E_AUTO_RESTART:
+ case MAX98388_R210F_GLOBAL_EN:
+ case MAX98388_R22FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98388_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2:
+ case MAX98388_R210F_GLOBAL_EN:
+ case MAX98388_R22FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct snd_soc_dai_driver max98388_dai[] = {
+ {
+ .name = "max98388-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98388_RATES,
+ .formats = MAX98388_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98388_RATES,
+ .formats = MAX98388_FORMATS,
+ },
+ .ops = &max98388_dai_ops,
+ }
+};
+
+static int max98388_suspend(struct device *dev)
+{
+ struct max98388_priv *max98388 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98388->regmap, true);
+ regcache_mark_dirty(max98388->regmap);
+
+ return 0;
+}
+
+static int max98388_resume(struct device *dev)
+{
+ struct max98388_priv *max98388 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98388->regmap, false);
+ max98388_reset(max98388, dev);
+ regcache_sync(max98388->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops max98388_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98388_suspend, max98388_resume)
+};
+
+static const struct regmap_config max98388_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98388_R22FF_REV_ID,
+ .reg_defaults = max98388_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98388_reg),
+ .readable_reg = max98388_readable_register,
+ .volatile_reg = max98388_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98388 = {
+ .probe = max98388_probe,
+ .controls = max98388_snd_controls,
+ .num_controls = ARRAY_SIZE(max98388_snd_controls),
+ .dapm_widgets = max98388_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98388_dapm_widgets),
+ .dapm_routes = max98388_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98388_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static void max98388_read_deveice_property(struct device *dev,
+ struct max98388_priv *max98388)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value))
+ max98388->v_slot = value & 0xF;
+ else
+ max98388->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "adi,imon-slot-no", &value))
+ max98388->i_slot = value & 0xF;
+ else
+ max98388->i_slot = 1;
+
+ if (device_property_read_bool(dev, "adi,interleave-mode"))
+ max98388->interleave_mode = true;
+ else
+ max98388->interleave_mode = false;
+}
+
+static int max98388_i2c_probe(struct i2c_client *i2c)
+{
+ int ret = 0;
+ int reg = 0;
+
+ struct max98388_priv *max98388 = NULL;
+
+ max98388 = devm_kzalloc(&i2c->dev, sizeof(*max98388), GFP_KERNEL);
+ if (!max98388)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98388);
+
+ /* regmap initialization */
+ max98388->regmap = devm_regmap_init_i2c(i2c, &max98388_regmap);
+ if (IS_ERR(max98388->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(max98388->regmap),
+ "Failed to allocate register map.\n");
+
+ /* voltage/current slot & gpio configuration */
+ max98388_read_deveice_property(&i2c->dev, max98388);
+
+ /* Device Reset */
+ max98388->reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98388->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(max98388->reset_gpio),
+ "Unable to request GPIO\n");
+
+ if (max98388->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(max98388->reset_gpio, 0);
+ /* Wait for the hw reset done */
+ usleep_range(5000, 6000);
+ }
+
+ /* Read Revision ID */
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R22FF_REV_ID, &reg);
+ if (ret < 0)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to read the revision ID\n");
+
+ dev_info(&i2c->dev, "MAX98388 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98388,
+ max98388_dai,
+ ARRAY_SIZE(max98388_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98388_i2c_id[] = {
+ { "max98388"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98388_i2c_id);
+
+static const struct of_device_id max98388_of_match[] = {
+ { .compatible = "adi,max98388", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98388_of_match);
+
+static const struct acpi_device_id max98388_acpi_match[] = {
+ { "ADS8388", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98388_acpi_match);
+
+static struct i2c_driver max98388_i2c_driver = {
+ .driver = {
+ .name = "max98388",
+ .of_match_table = max98388_of_match,
+ .acpi_match_table = max98388_acpi_match,
+ .pm = pm_sleep_ptr(&max98388_pm),
+ },
+ .probe = max98388_i2c_probe,
+ .id_table = max98388_i2c_id,
+};
+
+module_i2c_driver(max98388_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98388 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h
new file mode 100644
index 000000000000..77833d181913
--- /dev/null
+++ b/sound/soc/codecs/max98388.h
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * max98388.h -- MAX98388 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022, Analog Devices Inc.
+ */
+
+#ifndef _MAX98388_H
+#define _MAX98388_H
+
+/* Device Status Registers */
+#define MAX98388_R2000_SW_RESET 0x2000
+#define MAX98388_R2001_INT_RAW1 0x2001
+#define MAX98388_R2002_INT_RAW2 0x2002
+#define MAX98388_R2004_INT_STATE1 0x2004
+#define MAX98388_R2005_INT_STATE2 0x2005
+/* Thermal Protection Registers */
+#define MAX98388_R2020_THERM_WARN_THRESH 0x2020
+/* Error Monitor */
+#define MAX98388_R2031_SPK_MON_THRESH 0x2031
+#define MAX98388_R2032_SPK_MON_LD_SEL 0x2032
+#define MAX98388_R2033_SPK_MON_DURATION 0x2033
+#define MAX98388_R2037_ERR_MON_CTRL 0x2037
+/* PCM Registers */
+#define MAX98388_R2040_PCM_MODE_CFG 0x2040
+#define MAX98388_R2041_PCM_CLK_SETUP 0x2041
+#define MAX98388_R2042_PCM_SR_SETUP 0x2042
+#define MAX98388_R2044_PCM_TX_CTRL1 0x2044
+#define MAX98388_R2045_PCM_TX_CTRL2 0x2045
+#define MAX98388_R2050_PCM_TX_HIZ_CTRL1 0x2050
+#define MAX98388_R2051_PCM_TX_HIZ_CTRL2 0x2051
+#define MAX98388_R2052_PCM_TX_HIZ_CTRL3 0x2052
+#define MAX98388_R2053_PCM_TX_HIZ_CTRL4 0x2053
+#define MAX98388_R2054_PCM_TX_HIZ_CTRL5 0x2054
+#define MAX98388_R2055_PCM_TX_HIZ_CTRL6 0x2055
+#define MAX98388_R2056_PCM_TX_HIZ_CTRL7 0x2056
+#define MAX98388_R2057_PCM_TX_HIZ_CTRL8 0x2057
+#define MAX98388_R2058_PCM_RX_SRC1 0x2058
+#define MAX98388_R2059_PCM_RX_SRC2 0x2059
+#define MAX98388_R205C_PCM_TX_DRIVE_STRENGTH 0x205C
+#define MAX98388_R205D_PCM_TX_SRC_EN 0x205D
+#define MAX98388_R205E_PCM_RX_EN 0x205E
+#define MAX98388_R205F_PCM_TX_EN 0x205F
+/* Speaker Channel Control */
+#define MAX98388_R2090_SPK_CH_VOL_CTRL 0x2090
+#define MAX98388_R2091_SPK_CH_CFG 0x2091
+#define MAX98388_R2092_SPK_AMP_OUT_CFG 0x2092
+#define MAX98388_R2093_SPK_AMP_SSM_CFG 0x2093
+#define MAX98388_R2094_SPK_AMP_ER_CTRL 0x2094
+#define MAX98388_R209E_SPK_CH_PINK_NOISE_EN 0x209E
+#define MAX98388_R209F_SPK_CH_AMP_EN 0x209F
+#define MAX98388_R20A0_IV_DATA_DSP_CTRL 0x20A0
+#define MAX98388_R20A7_IV_DATA_EN 0x20A7
+#define MAX98388_R20E0_BP_ALC_THRESH 0x20E0
+#define MAX98388_R20E1_BP_ALC_RATES 0x20E1
+#define MAX98388_R20E2_BP_ALC_ATTEN 0x20E2
+#define MAX98388_R20E3_BP_ALC_REL 0x20E3
+#define MAX98388_R20E4_BP_ALC_MUTE 0x20E4
+#define MAX98388_R20EE_BP_INF_HOLD_REL 0x20EE
+#define MAX98388_R20EF_BP_ALC_EN 0x20EF
+#define MAX98388_R210E_AUTO_RESTART 0x210E
+#define MAX98388_R210F_GLOBAL_EN 0x210F
+#define MAX98388_R22FF_REV_ID 0x22FF
+
+/* MAX98388_R2000_SW_RESET */
+#define MAX98388_SOFT_RESET (0x1 << 0)
+
+/* MAX98388_R2020_THERM_WARN_THRESH */
+#define MAX98388_THERM_SHDN_THRESH_SHIFT (0)
+#define MAX98388_THERM_WARN_THRESH_SHIFT (2)
+
+/* MAX98388_R2022_PCM_TX_SRC_1 */
+#define MAX98388_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98388_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98388_R2024_PCM_DATA_FMT_CFG */
+#define MAX98388_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98388_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98388_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98388_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98388_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98388_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98388_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98388_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98388_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98388_R2031_SPK_MON_THRESH */
+#define MAX98388_SPKMON_THRESH_SHIFT (0)
+
+/* MAX98388_R2032_SPK_MON_LD_SEL */
+#define MAX98388_SPKMON_LOAD_SHIFT (0)
+
+/* MAX98388_R2033_SPK_MON_DURATION */
+#define MAX98388_SPKMON_DURATION_SHIFT (0)
+
+/* MAX98388_R2037_ERR_MON_CTRL */
+#define MAX98388_CLOCK_MON_SHIFT (0)
+#define MAX98388_SPK_MON_SHIFT (1)
+
+/* MAX98388_R203E_AMP_PATH_GAIN */
+#define MAX98388_SPK_DIGI_GAIN_MASK (0xF << 4)
+#define MAX98388_SPK_DIGI_GAIN_SHIFT (4)
+#define MAX98388_FS_GAIN_MAX_MASK (0xF << 0)
+#define MAX98388_FS_GAIN_MAX_SHIFT (0)
+
+/* MAX98388_R2041_PCM_CLK_SETUP */
+#define MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98388_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98388_R2042_PCM_SR_SETUP */
+#define MAX98388_PCM_SR_MASK (0xF << 0)
+#define MAX98388_PCM_SR_IV_MASK (0xF << 4)
+#define MAX98388_PCM_SR_IV_SHIFT (4)
+#define MAX98388_PCM_SR_8000 (0x0 << 0)
+#define MAX98388_PCM_SR_11025 (0x1 << 0)
+#define MAX98388_PCM_SR_12000 (0x2 << 0)
+#define MAX98388_PCM_SR_16000 (0x3 << 0)
+#define MAX98388_PCM_SR_22050 (0x4 << 0)
+#define MAX98388_PCM_SR_24000 (0x5 << 0)
+#define MAX98388_PCM_SR_32000 (0x6 << 0)
+#define MAX98388_PCM_SR_44100 (0x7 << 0)
+#define MAX98388_PCM_SR_48000 (0x8 << 0)
+#define MAX98388_PCM_SR_88200 (0x9 << 0)
+#define MAX98388_PCM_SR_96000 (0xA << 0)
+
+/* MAX98388_R2043_AMP_EN */
+#define MAX98388_SPK_EN_MASK (0x1 << 0)
+#define MAX98388_SPKFB_EN_MASK (0x1 << 1)
+#define MAX98388_SPKFB_EN_SHIFT (1)
+
+/* MAX98388_R2052_MEAS_ADC_PVDD_FLT_CFG */
+#define MAX98388_FLT_EN_SHIFT (4)
+
+/* MAX98388_R2058_PCM_RX_SRC1 */
+#define MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT (0)
+
+/* MAX98388_R2059_PCM_RX_SRC2 */
+#define MAX98388_RX_SRC_CH0_SHIFT (0)
+#define MAX98388_RX_SRC_CH1_SHIFT (4)
+
+/* MAX98388_R2091_SPK_CH_CFG */
+#define MAX98388_SPK_CFG_DCBLK_SHIFT (0)
+#define MAX98388_SPK_CFG_DITH_EN_SHIFT (1)
+#define MAX98388_SPK_CFG_INV_SHIFT (2)
+#define MAX98388_SPK_CFG_VOL_RMPUP_SHIFT (3)
+#define MAX98388_SPK_CFG_VOL_RMPDN_SHIFT (4)
+
+/* MAX98388_R2092_SPK_AMP_OUT_CFG */
+#define MAX98388_SPK_AMP_OUT_GAIN_SHIFT (0)
+#define MAX98388_SPK_AMP_OUT_MODE_SHIFT (3)
+
+/* MAX98388_R2093_SPK_AMP_SSM_CFG */
+#define MAX98388_SPK_AMP_SSM_EN_SHIFT (0)
+#define MAX98388_SPK_AMP_SSM_MOD_SHIFT (1)
+
+/* MAX98388_R2094_SPK_AMP_ER_CTRL */
+#define MAX98388_EDGE_RATE_RISE_SHIFT (0)
+#define MAX98388_EDGE_RATE_FALL_SHIFT (2)
+
+/* MAX98388_R209E_SPK_CH_PINK_NOISE_EN */
+#define MAX98388_PINK_NOISE_GEN_SHIFT (0)
+
+/* MAX98388_R20A0_IV_DATA_DSP_CTRL */
+#define MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT (0)
+#define MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT (1)
+#define MAX98388_AMP_DSP_CTRL_VOL_INV_SHIFT (2)
+#define MAX98388_AMP_DSP_CTRL_CUR_INV_SHIFT (3)
+#define MAX98388_AMP_DSP_CTRL_DITH_SHIFT (4)
+
+/* MAX98388_R20B2_BDE_L4_CFG_2 */
+#define MAX98388_LVL4_HOLD_EN_SHIFT (6)
+#define MAX98388_LVL4_MUTE_EN_SHIFT (7)
+
+/* MAX98388_R20B5_BDE_EN */
+#define MAX98388_BDE_EN_SHIFT (0)
+
+/* MAX98388_R20D1_DHT_CFG */
+#define MAX98388_DHT_ROT_PNT_SHIFT (0)
+#define MAX98388_DHT_SPK_GAIN_MIN_SHIFT (4)
+
+/* MAX98388_R20D2_DHT_ATTACK_CFG */
+#define MAX98388_DHT_ATTACK_RATE_SHIFT (0)
+#define MAX98388_DHT_ATTACK_STEP_SHIFT (3)
+
+/* MAX98388_R20D3_DHT_RELEASE_CFG */
+#define MAX98388_DHT_RELEASE_RATE_SHIFT (0)
+#define MAX98388_DHT_RELEASE_STEP_SHIFT (3)
+
+/* MAX98388_R20D4_DHT_EN */
+#define MAX98388_DHT_EN_SHIFT (0)
+
+/* MAX98388_R20E0_BP_ALC_THRESH */
+#define MAX98388_ALC_THRESH_SHIFT (0)
+
+/* MAX98388_R20E1_BP_ALC_RATES */
+#define MAX98388_ALC_RELEASE_RATE_SHIFT (0)
+#define MAX98388_ALC_ATTACK_RATE_SHIFT (4)
+
+/* MAX98388_R20E2_BP_ALC_ATTEN */
+#define MAX98388_ALC_MAX_ATTEN_SHIFT (0)
+
+/* MAX98388_R20E3_BP_ALC_REL */
+#define MAX98388_ALC_DEBOUNCE_TIME_SHIFT (0)
+
+/* MAX98388_R20E4_BP_ALC_MUTE */
+#define MAX98388_ALC_MUTE_EN_SHIFT (0)
+#define MAX98388_ALC_MUTE_DELAY_SHIFT (1)
+#define MAX98388_ALC_MUTE_RAMP_EN_SHIFT (4)
+#define MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT (5)
+
+/* MAX98388_R210E_AUTO_RESTART */
+#define MAX98388_PVDD_UVLO_AUTORESTART_SHIFT (0)
+#define MAX98388_THERM_AUTORESTART_SHIFT (1)
+#define MAX98388_OVC_AUTORESTART_SHIFT (2)
+#define MAX98388_CMON_AUTORESTART_SHIFT (3)
+
+/* MAX98388_R210F_GLOBAL_EN */
+#define MAX98388_GLOBAL_EN_MASK (0x1 << 0)
+
+struct max98388_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ bool tdm_mode;
+};
+
+#endif
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
new file mode 100644
index 000000000000..3dd4dd94bc37
--- /dev/null
+++ b/sound/soc/codecs/max98390.c
@@ -0,0 +1,1135 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max98390.c -- MAX98390 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2020 Maxim Integrated Products
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/cdev.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98390.h"
+
+static const struct reg_default max98390_reg_defaults[] = {
+ {MAX98390_INT_EN1, 0xf0},
+ {MAX98390_INT_EN2, 0x00},
+ {MAX98390_INT_EN3, 0x00},
+ {MAX98390_INT_FLAG_CLR1, 0x00},
+ {MAX98390_INT_FLAG_CLR2, 0x00},
+ {MAX98390_INT_FLAG_CLR3, 0x00},
+ {MAX98390_IRQ_CTRL, 0x01},
+ {MAX98390_CLK_MON, 0x6d},
+ {MAX98390_DAT_MON, 0x03},
+ {MAX98390_WDOG_CTRL, 0x00},
+ {MAX98390_WDOG_RST, 0x00},
+ {MAX98390_MEAS_ADC_THERM_WARN_THRESH, 0x75},
+ {MAX98390_MEAS_ADC_THERM_SHDN_THRESH, 0x8c},
+ {MAX98390_MEAS_ADC_THERM_HYSTERESIS, 0x08},
+ {MAX98390_PIN_CFG, 0x55},
+ {MAX98390_PCM_RX_EN_A, 0x00},
+ {MAX98390_PCM_RX_EN_B, 0x00},
+ {MAX98390_PCM_TX_EN_A, 0x00},
+ {MAX98390_PCM_TX_EN_B, 0x00},
+ {MAX98390_PCM_TX_HIZ_CTRL_A, 0xff},
+ {MAX98390_PCM_TX_HIZ_CTRL_B, 0xff},
+ {MAX98390_PCM_CH_SRC_1, 0x00},
+ {MAX98390_PCM_CH_SRC_2, 0x00},
+ {MAX98390_PCM_CH_SRC_3, 0x00},
+ {MAX98390_PCM_MODE_CFG, 0xc0},
+ {MAX98390_PCM_MASTER_MODE, 0x1c},
+ {MAX98390_PCM_CLK_SETUP, 0x44},
+ {MAX98390_PCM_SR_SETUP, 0x08},
+ {MAX98390_ICC_RX_EN_A, 0x00},
+ {MAX98390_ICC_RX_EN_B, 0x00},
+ {MAX98390_ICC_TX_EN_A, 0x00},
+ {MAX98390_ICC_TX_EN_B, 0x00},
+ {MAX98390_ICC_HIZ_MANUAL_MODE, 0x00},
+ {MAX98390_ICC_TX_HIZ_EN_A, 0x00},
+ {MAX98390_ICC_TX_HIZ_EN_B, 0x00},
+ {MAX98390_ICC_LNK_EN, 0x00},
+ {MAX98390_R2039_AMP_DSP_CFG, 0x0f},
+ {MAX98390_R203A_AMP_EN, 0x81},
+ {MAX98390_TONE_GEN_DC_CFG, 0x00},
+ {MAX98390_SPK_SRC_SEL, 0x00},
+ {MAX98390_SSM_CFG, 0x85},
+ {MAX98390_MEAS_EN, 0x03},
+ {MAX98390_MEAS_DSP_CFG, 0x0f},
+ {MAX98390_BOOST_CTRL0, 0x1c},
+ {MAX98390_BOOST_CTRL3, 0x01},
+ {MAX98390_BOOST_CTRL1, 0x40},
+ {MAX98390_MEAS_ADC_CFG, 0x07},
+ {MAX98390_MEAS_ADC_BASE_MSB, 0x00},
+ {MAX98390_MEAS_ADC_BASE_LSB, 0x23},
+ {MAX98390_ADC_CH0_DIVIDE, 0x00},
+ {MAX98390_ADC_CH1_DIVIDE, 0x00},
+ {MAX98390_ADC_CH2_DIVIDE, 0x00},
+ {MAX98390_ADC_CH0_FILT_CFG, 0x00},
+ {MAX98390_ADC_CH1_FILT_CFG, 0x00},
+ {MAX98390_ADC_CH2_FILT_CFG, 0x00},
+ {MAX98390_PWR_GATE_CTL, 0x2c},
+ {MAX98390_BROWNOUT_EN, 0x00},
+ {MAX98390_BROWNOUT_INFINITE_HOLD, 0x00},
+ {MAX98390_BROWNOUT_INFINITE_HOLD_CLR, 0x00},
+ {MAX98390_BROWNOUT_LVL_HOLD, 0x00},
+ {MAX98390_BROWNOUT_LVL1_THRESH, 0x00},
+ {MAX98390_BROWNOUT_LVL2_THRESH, 0x00},
+ {MAX98390_BROWNOUT_LVL3_THRESH, 0x00},
+ {MAX98390_BROWNOUT_LVL4_THRESH, 0x00},
+ {MAX98390_BROWNOUT_THRESH_HYSTERYSIS, 0x00},
+ {MAX98390_BROWNOUT_AMP_LIMITER_ATK_REL, 0x1f},
+ {MAX98390_BROWNOUT_AMP_GAIN_ATK_REL, 0x00},
+ {MAX98390_BROWNOUT_AMP1_CLIP_MODE, 0x00},
+ {MAX98390_BROWNOUT_LVL1_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL1_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL1_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL1_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_LVL2_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL2_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL2_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL2_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_LVL3_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL3_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL3_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL3_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_LVL4_CUR_LIMIT, 0x00},
+ {MAX98390_BROWNOUT_LVL4_AMP1_CTRL1, 0x00},
+ {MAX98390_BROWNOUT_LVL4_AMP1_CTRL2, 0x00},
+ {MAX98390_BROWNOUT_LVL4_AMP1_CTRL3, 0x00},
+ {MAX98390_BROWNOUT_ILIM_HLD, 0x00},
+ {MAX98390_BROWNOUT_LIM_HLD, 0x00},
+ {MAX98390_BROWNOUT_CLIP_HLD, 0x00},
+ {MAX98390_BROWNOUT_GAIN_HLD, 0x00},
+ {MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0f},
+ {MAX98390_ENV_TRACK_BOOST_VOUT_DELAY, 0x80},
+ {MAX98390_ENV_TRACK_REL_RATE, 0x07},
+ {MAX98390_ENV_TRACK_HOLD_RATE, 0x07},
+ {MAX98390_ENV_TRACK_CTRL, 0x01},
+ {MAX98390_BOOST_BYPASS1, 0x49},
+ {MAX98390_BOOST_BYPASS2, 0x2b},
+ {MAX98390_BOOST_BYPASS3, 0x08},
+ {MAX98390_FET_SCALING1, 0x00},
+ {MAX98390_FET_SCALING2, 0x03},
+ {MAX98390_FET_SCALING3, 0x00},
+ {MAX98390_FET_SCALING4, 0x07},
+ {MAX98390_SPK_SPEEDUP, 0x00},
+ {DSMIG_WB_DRC_RELEASE_TIME_1, 0x00},
+ {DSMIG_WB_DRC_RELEASE_TIME_2, 0x00},
+ {DSMIG_WB_DRC_ATTACK_TIME_1, 0x00},
+ {DSMIG_WB_DRC_ATTACK_TIME_2, 0x00},
+ {DSMIG_WB_DRC_COMPRESSION_RATIO, 0x00},
+ {DSMIG_WB_DRC_COMPRESSION_THRESHOLD, 0x00},
+ {DSMIG_WB_DRC_MAKEUPGAIN, 0x00},
+ {DSMIG_WB_DRC_NOISE_GATE_THRESHOLD, 0x00},
+ {DSMIG_WBDRC_HPF_ENABLE, 0x00},
+ {DSMIG_WB_DRC_TEST_SMOOTHER_OUT_EN, 0x00},
+ {DSMIG_PPR_THRESHOLD, 0x00},
+ {DSM_STEREO_BASS_CHANNEL_SELECT, 0x00},
+ {DSM_TPROT_THRESHOLD_BYTE0, 0x00},
+ {DSM_TPROT_THRESHOLD_BYTE1, 0x00},
+ {DSM_TPROT_ROOM_TEMPERATURE_BYTE0, 0x00},
+ {DSM_TPROT_ROOM_TEMPERATURE_BYTE1, 0x00},
+ {DSM_TPROT_RECIP_RDC_ROOM_BYTE0, 0x00},
+ {DSM_TPROT_RECIP_RDC_ROOM_BYTE1, 0x00},
+ {DSM_TPROT_RECIP_RDC_ROOM_BYTE2, 0x00},
+ {DSM_TPROT_RECIP_TCONST_BYTE0, 0x00},
+ {DSM_TPROT_RECIP_TCONST_BYTE1, 0x00},
+ {DSM_TPROT_RECIP_TCONST_BYTE2, 0x00},
+ {DSM_THERMAL_ATTENUATION_SETTINGS, 0x00},
+ {DSM_THERMAL_PILOT_TONE_ATTENUATION, 0x00},
+ {DSM_TPROT_PG_TEMP_THRESH_BYTE0, 0x00},
+ {DSM_TPROT_PG_TEMP_THRESH_BYTE1, 0x00},
+ {DSMIG_DEBUZZER_THRESHOLD, 0x00},
+ {DSMIG_DEBUZZER_ALPHA_COEF_TEST_ONLY, 0x08},
+ {DSM_VOL_ENA, 0x20},
+ {DSM_VOL_CTRL, 0xa0},
+ {DSMIG_EN, 0x00},
+ {MAX98390_R23E1_DSP_GLOBAL_EN, 0x00},
+ {MAX98390_R23FF_GLOBAL_EN, 0x00},
+};
+
+static int max98390_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int mode;
+ unsigned int format;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ mode = MAX98390_PCM_MASTER_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max98390->provider = true;
+ mode = MAX98390_PCM_MASTER_MODE_MASTER;
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MASTER_MODE,
+ MAX98390_PCM_MASTER_MODE_MASK,
+ mode);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98390_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98390_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98390_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98390_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98390_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+static int max98390_get_bclk_sel(int bclk)
+{
+ int i;
+ /* BCLKs per LRCLK */
+ static int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 320, 384, 512,
+ };
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98390_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+ /* codec MCLK rate in master mode */
+ static int rate_table[] = {
+ 5644800, 6000000, 6144000, 6500000,
+ 9600000, 11289600, 12000000, 12288000,
+ 13000000, 19200000,
+ };
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params)
+ * snd_pcm_format_width(params_format(params));
+ int value;
+
+ if (max98390->provider) {
+ int i;
+ /* match rate to closest value */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i] >= max98390->sysclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(rate_table)) {
+ dev_err(component->dev, "failed to find proper clock rate.\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MASTER_MODE,
+ MAX98390_PCM_MASTER_MODE_MCLK_MASK,
+ i << MAX98390_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+ }
+
+ if (!max98390->tdm_mode) {
+ /* BCLK configuration */
+ value = max98390_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_CLK_SETUP,
+ MAX98390_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98390_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component =
+ dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ unsigned int sampling_rate;
+ unsigned int chan_sz;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98390_PCM_SR_SET1_SR_48000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_SR_SETUP,
+ MAX98390_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+
+ return max98390_set_clock(component, params);
+err:
+ return -EINVAL;
+}
+
+static int max98390_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ int bsel;
+ unsigned int chan_sz;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98390->tdm_mode = false;
+ else
+ max98390->tdm_mode = true;
+
+ dev_dbg(component->dev,
+ "Tdm mode : %d\n", max98390->tdm_mode);
+
+ /* BCLK configuration */
+ bsel = max98390_get_bclk_sel(slots * slot_width);
+ if (!bsel) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_CLK_SETUP,
+ MAX98390_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_MODE_CFG,
+ MAX98390_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_RX_EN_A,
+ rx_mask & 0xFF);
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_RX_EN_B,
+ (rx_mask & 0xFF00) >> 8);
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_A,
+ ~tx_mask & 0xFF);
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_B,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+static int max98390_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ max98390->sysclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98390_dai_ops = {
+ .set_sysclk = max98390_dai_set_sysclk,
+ .set_fmt = max98390_dai_set_fmt,
+ .hw_params = max98390_dai_hw_params,
+ .set_tdm_slot = max98390_dai_tdm_slot,
+};
+
+static int max98390_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 1);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static const char * const max98390_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const char * const max98390_boost_voltage_text[] = {
+ "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+ "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+ "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+ "9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98390_boost_voltage,
+ MAX98390_BOOST_CTRL0, 0,
+ max98390_boost_voltage_text);
+
+static DECLARE_TLV_DB_SCALE(max98390_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98390_digital_tlv, -8000, 50, 0);
+
+static const char * const max98390_current_limit_text[] = {
+ "0.00A", "0.50A", "1.00A", "1.05A", "1.10A", "1.15A", "1.20A", "1.25A",
+ "1.30A", "1.35A", "1.40A", "1.45A", "1.50A", "1.55A", "1.60A", "1.65A",
+ "1.70A", "1.75A", "1.80A", "1.85A", "1.90A", "1.95A", "2.00A", "2.05A",
+ "2.10A", "2.15A", "2.20A", "2.25A", "2.30A", "2.35A", "2.40A", "2.45A",
+ "2.50A", "2.55A", "2.60A", "2.65A", "2.70A", "2.75A", "2.80A", "2.85A",
+ "2.90A", "2.95A", "3.00A", "3.05A", "3.10A", "3.15A", "3.20A", "3.25A",
+ "3.30A", "3.35A", "3.40A", "3.45A", "3.50A", "3.55A", "3.60A", "3.65A",
+ "3.70A", "3.75A", "3.80A", "3.85A", "3.90A", "3.95A", "4.00A", "4.05A",
+ "4.10A"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98390_current_limit,
+ MAX98390_BOOST_CTRL1, 0,
+ max98390_current_limit_text);
+
+static int max98390_ref_rdc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ max98390->ref_rdc_value = ucontrol->value.integer.value[0];
+
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0,
+ max98390->ref_rdc_value & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE1,
+ (max98390->ref_rdc_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE2,
+ (max98390->ref_rdc_value >> 16) & 0x000000ff);
+
+ return 0;
+}
+
+static int max98390_ref_rdc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = max98390->ref_rdc_value;
+
+ return 0;
+}
+
+static int max98390_ambient_temp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ max98390->ambient_temp_value = ucontrol->value.integer.value[0];
+
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE1,
+ (max98390->ambient_temp_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE0,
+ (max98390->ambient_temp_value) & 0x000000ff);
+
+ return 0;
+}
+
+static int max98390_ambient_temp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = max98390->ambient_temp_value;
+
+ return 0;
+}
+
+static int max98390_adaptive_rdc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+
+ dev_warn(component->dev, "Put adaptive rdc not supported\n");
+
+ return 0;
+}
+
+static int max98390_adaptive_rdc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rdc, rdc0;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE1, &rdc);
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE0, &rdc0);
+ ucontrol->value.integer.value[0] = rdc0 | rdc << 8;
+
+ return 0;
+}
+
+static int max98390_dsm_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* Do nothing */
+ return 0;
+}
+
+static int max98390_dsm_calib_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98390_priv *max98390 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int rdc, rdc_cal_result, rdc_integer, rdc_factor, temp, val;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ regmap_read(max98390->regmap, MAX98390_R23FF_GLOBAL_EN, &val);
+ if (!val) {
+ /* Enable the codec for the duration of calibration readout */
+ regmap_update_bits(max98390->regmap, MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 1);
+ regmap_update_bits(max98390->regmap, MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 1);
+ }
+
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE1, &rdc);
+ regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE0, &rdc_cal_result);
+ regmap_read(max98390->regmap, MAX98390_MEAS_ADC_CH2_READ, &temp);
+
+ if (!val) {
+ /* Disable the codec if it was disabled */
+ regmap_update_bits(max98390->regmap, MAX98390_R23FF_GLOBAL_EN,
+ MAX98390_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98390->regmap, MAX98390_R203A_AMP_EN,
+ MAX98390_AMP_EN_MASK, 0);
+ }
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ rdc_cal_result |= (rdc << 8) & 0x0000FFFF;
+ if (rdc_cal_result)
+ max98390->ref_rdc_value = 268435456U / rdc_cal_result;
+
+ max98390->ambient_temp_value = temp * 52 - 1188;
+
+ rdc_integer = rdc_cal_result * 937 / 65536;
+ rdc_factor = ((rdc_cal_result * 937 * 100) / 65536) - (rdc_integer * 100);
+
+ dev_info(component->dev,
+ "rdc resistance about %d.%02d ohm, reg=0x%X temp reg=0x%X\n",
+ rdc_integer, rdc_factor, rdc_cal_result, temp);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new max98390_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", DSM_VOL_CTRL,
+ 0, 184, 0,
+ max98390_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98390_R203D_SPK_GAIN,
+ 0, 6, 0,
+ max98390_spk_tlv),
+ SOC_SINGLE("Ramp Up Bypass Switch", MAX98390_R2039_AMP_DSP_CFG,
+ MAX98390_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Bypass Switch", MAX98390_R2039_AMP_DSP_CFG,
+ MAX98390_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0),
+ SOC_SINGLE("Boost Clock Phase", MAX98390_BOOST_CTRL3,
+ MAX98390_BOOST_CLK_PHASE_CFG_SHIFT, 3, 0),
+ SOC_ENUM("Boost Output Voltage", max98390_boost_voltage),
+ SOC_ENUM("Current Limit", max98390_current_limit),
+ SOC_SINGLE_EXT("DSM Rdc", SND_SOC_NOPM, 0, 0xffffff, 0,
+ max98390_ref_rdc_get, max98390_ref_rdc_put),
+ SOC_SINGLE_EXT("DSM Ambient Temp", SND_SOC_NOPM, 0, 0xffff, 0,
+ max98390_ambient_temp_get, max98390_ambient_temp_put),
+ SOC_SINGLE_EXT("DSM Adaptive Rdc", SND_SOC_NOPM, 0, 0xffff, 0,
+ max98390_adaptive_rdc_get, max98390_adaptive_rdc_put),
+ SOC_SINGLE_EXT("DSM Calibration", SND_SOC_NOPM, 0, 1, 0,
+ max98390_dsm_calib_get, max98390_dsm_calib_put),
+};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98390_PCM_CH_SRC_1,
+ MAX98390_PCM_RX_CH_SRC_SHIFT,
+ 3, max98390_switch_text);
+
+static const struct snd_kcontrol_new max98390_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_soc_dapm_widget max98390_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ SND_SOC_NOPM, 0, 0, max98390_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98390_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98390_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static bool max98390_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98390_SOFTWARE_RESET ... MAX98390_INT_EN3:
+ case MAX98390_IRQ_CTRL ... MAX98390_WDOG_CTRL:
+ case MAX98390_MEAS_ADC_THERM_WARN_THRESH
+ ... MAX98390_BROWNOUT_INFINITE_HOLD:
+ case MAX98390_BROWNOUT_LVL_HOLD ... DSMIG_DEBUZZER_THRESHOLD:
+ case DSM_VOL_ENA ... MAX98390_R24FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98390_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98390_SOFTWARE_RESET ... MAX98390_INT_EN3:
+ case MAX98390_MEAS_ADC_CH0_READ ... MAX98390_MEAS_ADC_CH2_READ:
+ case MAX98390_PWR_GATE_STATUS ... MAX98390_BROWNOUT_STATUS:
+ case MAX98390_BROWNOUT_LOWEST_STATUS:
+ case MAX98390_ENV_TRACK_BOOST_VOUT_READ:
+ case DSM_STBASS_HPF_B0_BYTE0 ... DSM_DEBUZZER_ATTACK_TIME_BYTE2:
+ case THERMAL_RDC_RD_BACK_BYTE1 ... DSMIG_DEBUZZER_THRESHOLD:
+ case DSM_THERMAL_GAIN ... DSM_WBDRC_GAIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define MAX98390_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98390_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver max98390_dai[] = {
+ {
+ .name = "max98390-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98390_RATES,
+ .formats = MAX98390_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98390_RATES,
+ .formats = MAX98390_FORMATS,
+ },
+ .ops = &max98390_dai_ops,
+ }
+};
+
+static int max98390_dsm_init(struct snd_soc_component *component)
+{
+ int ret;
+ int param_size, param_start_addr;
+ char filename[128];
+ const char *vendor, *product;
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+ const struct firmware *fw;
+ char *dsm_param;
+
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+ if (!strcmp(max98390->dsm_param_name, "default")) {
+ if (vendor && product) {
+ snprintf(filename, sizeof(filename),
+ "dsm_param_%s_%s.bin", vendor, product);
+ } else {
+ sprintf(filename, "dsm_param.bin");
+ }
+ } else {
+ snprintf(filename, sizeof(filename), "%s",
+ max98390->dsm_param_name);
+ }
+ ret = request_firmware(&fw, filename, component->dev);
+ if (ret) {
+ ret = request_firmware(&fw, "dsm_param.bin", component->dev);
+ if (ret) {
+ ret = request_firmware(&fw, "dsmparam.bin",
+ component->dev);
+ if (ret)
+ goto err;
+ }
+ }
+
+ dev_dbg(component->dev,
+ "max98390: param fw size %zd\n",
+ fw->size);
+ if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) {
+ dev_err(component->dev,
+ "param fw is invalid.\n");
+ ret = -EINVAL;
+ goto err_alloc;
+ }
+ dsm_param = (char *)fw->data;
+ param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8;
+ param_size = (dsm_param[2] & 0xff) | (dsm_param[3] & 0xff) << 8;
+ if (param_size > MAX98390_DSM_PARAM_MAX_SIZE ||
+ param_start_addr < MAX98390_IRQ_CTRL ||
+ fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) {
+ dev_err(component->dev,
+ "param fw is invalid.\n");
+ ret = -EINVAL;
+ goto err_alloc;
+ }
+ regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80);
+ dsm_param += MAX98390_DSM_PAYLOAD_OFFSET;
+ regmap_bulk_write(max98390->regmap, param_start_addr,
+ dsm_param, param_size);
+ regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01);
+
+err_alloc:
+ release_firmware(fw);
+err:
+ return ret;
+}
+
+static void max98390_init_regs(struct snd_soc_component *component)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_write(max98390->regmap, MAX98390_CLK_MON, 0x6f);
+ regmap_write(max98390->regmap, MAX98390_DAT_MON, 0x00);
+ regmap_write(max98390->regmap, MAX98390_PWR_GATE_CTL, 0x00);
+ regmap_write(max98390->regmap, MAX98390_PCM_RX_EN_A, 0x03);
+ regmap_write(max98390->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e);
+ regmap_write(max98390->regmap, MAX98390_BOOST_BYPASS1, 0x46);
+ regmap_write(max98390->regmap, MAX98390_FET_SCALING3, 0x03);
+
+ /* voltage, current slot configuration */
+ regmap_write(max98390->regmap,
+ MAX98390_PCM_CH_SRC_2,
+ (max98390->i_l_slot << 4 |
+ max98390->v_l_slot)&0xFF);
+
+ if (max98390->v_l_slot < 8) {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_A,
+ 1 << max98390->v_l_slot, 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_A,
+ 1 << max98390->v_l_slot,
+ 1 << max98390->v_l_slot);
+ } else {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98390->v_l_slot - 8), 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_B,
+ 1 << (max98390->v_l_slot - 8),
+ 1 << (max98390->v_l_slot - 8));
+ }
+
+ if (max98390->i_l_slot < 8) {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_A,
+ 1 << max98390->i_l_slot, 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_A,
+ 1 << max98390->i_l_slot,
+ 1 << max98390->i_l_slot);
+ } else {
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98390->i_l_slot - 8), 0);
+ regmap_update_bits(max98390->regmap,
+ MAX98390_PCM_TX_EN_B,
+ 1 << (max98390->i_l_slot - 8),
+ 1 << (max98390->i_l_slot - 8));
+ }
+}
+
+static int max98390_probe(struct snd_soc_component *component)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_write(max98390->regmap, MAX98390_SOFTWARE_RESET, 0x01);
+ /* Sleep reset settle time */
+ msleep(20);
+
+ /* Amp init setting */
+ max98390_init_regs(component);
+ /* Update dsm bin param */
+ max98390_dsm_init(component);
+
+ /* Dsm Setting */
+ if (max98390->ref_rdc_value) {
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0,
+ max98390->ref_rdc_value & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE1,
+ (max98390->ref_rdc_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE2,
+ (max98390->ref_rdc_value >> 16) & 0x000000ff);
+ }
+ if (max98390->ambient_temp_value) {
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE1,
+ (max98390->ambient_temp_value >> 8) & 0x000000ff);
+ regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE0,
+ (max98390->ambient_temp_value) & 0x000000ff);
+ }
+
+ return 0;
+}
+
+static int max98390_suspend(struct device *dev)
+{
+ struct max98390_priv *max98390 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s:Enter\n", __func__);
+
+ regcache_cache_only(max98390->regmap, true);
+ regcache_mark_dirty(max98390->regmap);
+
+ return 0;
+}
+
+static int max98390_resume(struct device *dev)
+{
+ struct max98390_priv *max98390 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s:Enter\n", __func__);
+
+ regcache_cache_only(max98390->regmap, false);
+ regcache_sync(max98390->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops max98390_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98390_suspend, max98390_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98390 = {
+ .probe = max98390_probe,
+ .controls = max98390_snd_controls,
+ .num_controls = ARRAY_SIZE(max98390_snd_controls),
+ .dapm_widgets = max98390_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98390_dapm_widgets),
+ .dapm_routes = max98390_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98390_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98390_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98390_R24FF_REV_ID,
+ .reg_defaults = max98390_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max98390_reg_defaults),
+ .readable_reg = max98390_readable_register,
+ .volatile_reg = max98390_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98390_slot_config(struct i2c_client *i2c,
+ struct max98390_priv *max98390)
+{
+ int value;
+ struct device *dev = &i2c->dev;
+
+ if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+ max98390->v_l_slot = value & 0xF;
+ else
+ max98390->v_l_slot = 0;
+
+ if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+ max98390->i_l_slot = value & 0xF;
+ else
+ max98390->i_l_slot = 1;
+}
+
+static int max98390_i2c_probe(struct i2c_client *i2c)
+{
+ int ret = 0;
+ int reg = 0;
+
+ struct max98390_priv *max98390 = NULL;
+ struct i2c_adapter *adapter = i2c->adapter;
+ struct gpio_desc *reset_gpio;
+
+ ret = i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_BYTE
+ | I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!ret) {
+ dev_err(&i2c->dev, "I2C check functionality failed\n");
+ return -ENXIO;
+ }
+
+ max98390 = devm_kzalloc(&i2c->dev, sizeof(*max98390), GFP_KERNEL);
+ if (!max98390) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98390);
+
+ ret = device_property_read_u32(&i2c->dev, "maxim,temperature_calib",
+ &max98390->ambient_temp_value);
+ if (ret) {
+ dev_info(&i2c->dev,
+ "no optional property 'temperature_calib' found, default:\n");
+ }
+ ret = device_property_read_u32(&i2c->dev, "maxim,r0_calib",
+ &max98390->ref_rdc_value);
+ if (ret) {
+ dev_info(&i2c->dev,
+ "no optional property 'r0_calib' found, default:\n");
+ }
+
+ dev_info(&i2c->dev,
+ "%s: r0_calib: 0x%x,temperature_calib: 0x%x",
+ __func__, max98390->ref_rdc_value,
+ max98390->ambient_temp_value);
+
+ ret = device_property_read_string(&i2c->dev, "maxim,dsm_param_name",
+ &max98390->dsm_param_name);
+ if (ret)
+ max98390->dsm_param_name = "default";
+
+ /* voltage/current slot configuration */
+ max98390_slot_config(i2c, max98390);
+
+ /* regmap initialization */
+ max98390->regmap = devm_regmap_init_i2c(i2c, &max98390_regmap);
+ if (IS_ERR(max98390->regmap)) {
+ ret = PTR_ERR(max98390->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+
+ /* Power on device */
+ if (reset_gpio) {
+ usleep_range(1000, 2000);
+ /* bring out of reset */
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ usleep_range(1000, 2000);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98390->regmap,
+ MAX98390_R24FF_REV_ID, &reg);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "ret=%d, Failed to read: 0x%02X\n",
+ ret, MAX98390_R24FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98390 revisionID: 0x%02X\n", reg);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98390,
+ max98390_dai, ARRAY_SIZE(max98390_dai));
+
+ return ret;
+}
+
+static const struct i2c_device_id max98390_i2c_id[] = {
+ { "max98390"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, max98390_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98390_of_match[] = {
+ { .compatible = "maxim,max98390", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max98390_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98390_acpi_match[] = {
+ { "MX98390", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98390_acpi_match);
+#endif
+
+static struct i2c_driver max98390_i2c_driver = {
+ .driver = {
+ .name = "max98390",
+ .of_match_table = of_match_ptr(max98390_of_match),
+ .acpi_match_table = ACPI_PTR(max98390_acpi_match),
+ .pm = pm_ptr(&max98390_pm),
+ },
+ .probe = max98390_i2c_probe,
+ .id_table = max98390_i2c_id,
+};
+
+module_i2c_driver(max98390_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98390 driver");
+MODULE_AUTHOR("Steve Lee <steves.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h
new file mode 100644
index 000000000000..f4d6758ab4c6
--- /dev/null
+++ b/sound/soc/codecs/max98390.h
@@ -0,0 +1,667 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020, Maxim Integrated.
+ */
+
+#ifndef _MAX98390_H
+#define _MAX98390_H
+
+/* MAX98390 Register Address */
+#define MAX98390_SOFTWARE_RESET 0x2000
+#define MAX98390_INT_RAW1 0x2002
+#define MAX98390_INT_RAW2 0x2003
+#define MAX98390_INT_RAW3 0x2004
+#define MAX98390_INT_STATE1 0x2005
+#define MAX98390_INT_STATE2 0x2006
+#define MAX98390_INT_STATE3 0x2007
+#define MAX98390_INT_FLAG1 0x2008
+#define MAX98390_INT_FLAG2 0x2009
+#define MAX98390_INT_FLAG3 0x200a
+#define MAX98390_INT_EN1 0x200b
+#define MAX98390_INT_EN2 0x200c
+#define MAX98390_INT_EN3 0x200d
+#define MAX98390_INT_FLAG_CLR1 0x200e
+#define MAX98390_INT_FLAG_CLR2 0x200f
+#define MAX98390_INT_FLAG_CLR3 0x2010
+#define MAX98390_IRQ_CTRL 0x2011
+#define MAX98390_CLK_MON 0x2012
+#define MAX98390_DAT_MON 0x2014
+#define MAX98390_WDOG_CTRL 0x2015
+#define MAX98390_WDOG_RST 0x2016
+#define MAX98390_MEAS_ADC_THERM_WARN_THRESH 0x2017
+#define MAX98390_MEAS_ADC_THERM_SHDN_THRESH 0x2018
+#define MAX98390_MEAS_ADC_THERM_HYSTERESIS 0x2019
+#define MAX98390_PIN_CFG 0x201a
+#define MAX98390_PCM_RX_EN_A 0x201b
+#define MAX98390_PCM_RX_EN_B 0x201c
+#define MAX98390_PCM_TX_EN_A 0x201d
+#define MAX98390_PCM_TX_EN_B 0x201e
+#define MAX98390_PCM_TX_HIZ_CTRL_A 0x201f
+#define MAX98390_PCM_TX_HIZ_CTRL_B 0x2020
+#define MAX98390_PCM_CH_SRC_1 0x2021
+#define MAX98390_PCM_CH_SRC_2 0x2022
+#define MAX98390_PCM_CH_SRC_3 0x2023
+#define MAX98390_PCM_MODE_CFG 0x2024
+#define MAX98390_PCM_MASTER_MODE 0x2025
+#define MAX98390_PCM_CLK_SETUP 0x2026
+#define MAX98390_PCM_SR_SETUP 0x2027
+#define MAX98390_ICC_RX_EN_A 0x202c
+#define MAX98390_ICC_RX_EN_B 0x202d
+#define MAX98390_ICC_TX_EN_A 0x202e
+#define MAX98390_ICC_TX_EN_B 0x202f
+#define MAX98390_ICC_HIZ_MANUAL_MODE 0x2030
+#define MAX98390_ICC_TX_HIZ_EN_A 0x2031
+#define MAX98390_ICC_TX_HIZ_EN_B 0x2032
+#define MAX98390_ICC_LNK_EN 0x2033
+#define MAX98390_R2039_AMP_DSP_CFG 0x2039
+#define MAX98390_R203A_AMP_EN 0x203a
+#define MAX98390_TONE_GEN_DC_CFG 0x203b
+#define MAX98390_SPK_SRC_SEL 0x203c
+#define MAX98390_R203D_SPK_GAIN 0x203d
+#define MAX98390_SSM_CFG 0x203e
+#define MAX98390_MEAS_EN 0x203f
+#define MAX98390_MEAS_DSP_CFG 0x2040
+#define MAX98390_BOOST_CTRL0 0x2041
+#define MAX98390_BOOST_CTRL3 0x2042
+#define MAX98390_BOOST_CTRL1 0x2043
+#define MAX98390_MEAS_ADC_CFG 0x2044
+#define MAX98390_MEAS_ADC_BASE_MSB 0x2045
+#define MAX98390_MEAS_ADC_BASE_LSB 0x2046
+#define MAX98390_ADC_CH0_DIVIDE 0x2047
+#define MAX98390_ADC_CH1_DIVIDE 0x2048
+#define MAX98390_ADC_CH2_DIVIDE 0x2049
+#define MAX98390_ADC_CH0_FILT_CFG 0x204a
+#define MAX98390_ADC_CH1_FILT_CFG 0x204b
+#define MAX98390_ADC_CH2_FILT_CFG 0x204c
+#define MAX98390_MEAS_ADC_CH0_READ 0x204d
+#define MAX98390_MEAS_ADC_CH1_READ 0x204e
+#define MAX98390_MEAS_ADC_CH2_READ 0x204f
+#define MAX98390_PWR_GATE_CTL 0x2050
+#define MAX98390_PWR_GATE_STATUS 0x2051
+#define MAX98390_VBAT_LOW_STATUS 0x2052
+#define MAX98390_PVDD_LOW_STATUS 0x2053
+#define MAX98390_BROWNOUT_STATUS 0x2054
+#define MAX98390_BROWNOUT_EN 0x2055
+#define MAX98390_BROWNOUT_INFINITE_HOLD 0x2056
+#define MAX98390_BROWNOUT_INFINITE_HOLD_CLR 0x2057
+#define MAX98390_BROWNOUT_LVL_HOLD 0x2058
+#define MAX98390_BROWNOUT_LVL1_THRESH 0x2059
+#define MAX98390_BROWNOUT_LVL2_THRESH 0x205a
+#define MAX98390_BROWNOUT_LVL3_THRESH 0x205b
+#define MAX98390_BROWNOUT_LVL4_THRESH 0x205c
+#define MAX98390_BROWNOUT_THRESH_HYSTERYSIS 0x205d
+#define MAX98390_BROWNOUT_AMP_LIMITER_ATK_REL 0x205e
+#define MAX98390_BROWNOUT_AMP_GAIN_ATK_REL 0x205f
+#define MAX98390_BROWNOUT_AMP1_CLIP_MODE 0x2060
+#define MAX98390_BROWNOUT_LVL1_CUR_LIMIT 0x2061
+#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL1 0x2062
+#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL2 0x2063
+#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL3 0x2064
+#define MAX98390_BROWNOUT_LVL2_CUR_LIMIT 0x2065
+#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL1 0x2066
+#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL2 0x2067
+#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL3 0x2068
+#define MAX98390_BROWNOUT_LVL3_CUR_LIMIT 0x2069
+#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL1 0x206a
+#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL2 0x206b
+#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL3 0x206c
+#define MAX98390_BROWNOUT_LVL4_CUR_LIMIT 0x206d
+#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL1 0x206e
+#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL2 0x206f
+#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL3 0x2070
+#define MAX98390_BROWNOUT_LOWEST_STATUS 0x2071
+#define MAX98390_BROWNOUT_ILIM_HLD 0x2072
+#define MAX98390_BROWNOUT_LIM_HLD 0x2073
+#define MAX98390_BROWNOUT_CLIP_HLD 0x2074
+#define MAX98390_BROWNOUT_GAIN_HLD 0x2075
+#define MAX98390_ENV_TRACK_VOUT_HEADROOM 0x2076
+#define MAX98390_ENV_TRACK_BOOST_VOUT_DELAY 0x2077
+#define MAX98390_ENV_TRACK_REL_RATE 0x2078
+#define MAX98390_ENV_TRACK_HOLD_RATE 0x2079
+#define MAX98390_ENV_TRACK_CTRL 0x207a
+#define MAX98390_ENV_TRACK_BOOST_VOUT_READ 0x207b
+#define MAX98390_BOOST_BYPASS1 0x207c
+#define MAX98390_BOOST_BYPASS2 0x207d
+#define MAX98390_BOOST_BYPASS3 0x207e
+#define MAX98390_FET_SCALING1 0x207f
+#define MAX98390_FET_SCALING2 0x2080
+#define MAX98390_FET_SCALING3 0x2081
+#define MAX98390_FET_SCALING4 0x2082
+#define MAX98390_SPK_SPEEDUP 0x2084
+
+#define DSM_STBASS_HPF_B0_BYTE0 0x2101
+#define DSM_STBASS_HPF_B0_BYTE1 0x2102
+#define DSM_STBASS_HPF_B0_BYTE2 0x2103
+#define DSM_STBASS_HPF_B1_BYTE0 0x2105
+#define DSM_STBASS_HPF_B1_BYTE1 0x2106
+#define DSM_STBASS_HPF_B1_BYTE2 0x2107
+#define DSM_STBASS_HPF_B2_BYTE0 0x2109
+#define DSM_STBASS_HPF_B2_BYTE1 0x210a
+#define DSM_STBASS_HPF_B2_BYTE2 0x210b
+#define DSM_STBASS_HPF_A1_BYTE0 0x210d
+#define DSM_STBASS_HPF_A1_BYTE1 0x210e
+#define DSM_STBASS_HPF_A1_BYTE2 0x210f
+#define DSM_STBASS_HPF_A2_BYTE0 0x2111
+#define DSM_STBASS_HPF_A2_BYTE1 0x2112
+#define DSM_STBASS_HPF_A2_BYTE2 0x2113
+#define DSM_STBASS_LPF_B0_BYTE0 0x2115
+#define DSM_STBASS_LPF_B0_BYTE1 0x2116
+#define DSM_STBASS_LPF_B0_BYTE2 0x2117
+#define DSM_STBASS_LPF_B1_BYTE0 0x2119
+#define DSM_STBASS_LPF_B1_BYTE1 0x211a
+#define DSM_STBASS_LPF_B1_BYTE2 0x211b
+#define DSM_STBASS_LPF_B2_BYTE0 0x211d
+#define DSM_STBASS_LPF_B2_BYTE1 0x211e
+#define DSM_STBASS_LPF_B2_BYTE2 0x211f
+#define DSM_STBASS_LPF_A1_BYTE0 0x2121
+#define DSM_STBASS_LPF_A1_BYTE1 0x2122
+#define DSM_STBASS_LPF_A1_BYTE2 0x2123
+#define DSM_STBASS_LPF_A2_BYTE0 0x2125
+#define DSM_STBASS_LPF_A2_BYTE1 0x2126
+#define DSM_STBASS_LPF_A2_BYTE2 0x2127
+#define DSM_EQ_BQ1_B0_BYTE0 0x2129
+#define DSM_EQ_BQ1_B0_BYTE1 0x212a
+#define DSM_EQ_BQ1_B0_BYTE2 0x212b
+#define DSM_EQ_BQ1_B1_BYTE0 0x212d
+#define DSM_EQ_BQ1_B1_BYTE1 0x212e
+#define DSM_EQ_BQ1_B1_BYTE2 0x212f
+#define DSM_EQ_BQ1_B2_BYTE0 0x2131
+#define DSM_EQ_BQ1_B2_BYTE1 0x2132
+#define DSM_EQ_BQ1_B2_BYTE2 0x2133
+#define DSM_EQ_BQ1_A1_BYTE0 0x2135
+#define DSM_EQ_BQ1_A1_BYTE1 0x2136
+#define DSM_EQ_BQ1_A1_BYTE2 0x2137
+#define DSM_EQ_BQ1_A2_BYTE0 0x2139
+#define DSM_EQ_BQ1_A2_BYTE1 0x213a
+#define DSM_EQ_BQ1_A2_BYTE2 0x213b
+#define DSM_EQ_BQ2_B0_BYTE0 0x213d
+#define DSM_EQ_BQ2_B0_BYTE1 0x213e
+#define DSM_EQ_BQ2_B0_BYTE2 0x213f
+#define DSM_EQ_BQ2_B1_BYTE0 0x2141
+#define DSM_EQ_BQ2_B1_BYTE1 0x2142
+#define DSM_EQ_BQ2_B1_BYTE2 0x2143
+#define DSM_EQ_BQ2_B2_BYTE0 0x2145
+#define DSM_EQ_BQ2_B2_BYTE1 0x2146
+#define DSM_EQ_BQ2_B2_BYTE2 0x2147
+#define DSM_EQ_BQ2_A1_BYTE0 0x2149
+#define DSM_EQ_BQ2_A1_BYTE1 0x214a
+#define DSM_EQ_BQ2_A1_BYTE2 0x214b
+#define DSM_EQ_BQ2_A2_BYTE0 0x214d
+#define DSM_EQ_BQ2_A2_BYTE1 0x214e
+#define DSM_EQ_BQ2_A2_BYTE2 0x214f
+#define DSM_EQ_BQ3_B0_BYTE0 0x2151
+#define DSM_EQ_BQ3_B0_BYTE1 0x2152
+#define DSM_EQ_BQ3_B0_BYTE2 0x2153
+#define DSM_EQ_BQ3_B1_BYTE0 0x2155
+#define DSM_EQ_BQ3_B1_BYTE1 0x2156
+#define DSM_EQ_BQ3_B1_BYTE2 0x2157
+#define DSM_EQ_BQ3_B2_BYTE0 0x2159
+#define DSM_EQ_BQ3_B2_BYTE1 0x215a
+#define DSM_EQ_BQ3_B2_BYTE2 0x215b
+#define DSM_EQ_BQ3_A1_BYTE0 0x215d
+#define DSM_EQ_BQ3_A1_BYTE1 0x215e
+#define DSM_EQ_BQ3_A1_BYTE2 0x215f
+#define DSM_EQ_BQ3_A2_BYTE0 0x2161
+#define DSM_EQ_BQ3_A2_BYTE1 0x2162
+#define DSM_EQ_BQ3_A2_BYTE2 0x2163
+#define DSM_EQ_BQ4_B0_BYTE0 0x2165
+#define DSM_EQ_BQ4_B0_BYTE1 0x2166
+#define DSM_EQ_BQ4_B0_BYTE2 0x2167
+#define DSM_EQ_BQ4_B1_BYTE0 0x2169
+#define DSM_EQ_BQ4_B1_BYTE1 0x216a
+#define DSM_EQ_BQ4_B1_BYTE2 0x216b
+#define DSM_EQ_BQ4_B2_BYTE0 0x216d
+#define DSM_EQ_BQ4_B2_BYTE1 0x216e
+#define DSM_EQ_BQ4_B2_BYTE2 0x216f
+#define DSM_EQ_BQ4_A1_BYTE0 0x2171
+#define DSM_EQ_BQ4_A1_BYTE1 0x2172
+#define DSM_EQ_BQ4_A1_BYTE2 0x2173
+#define DSM_EQ_BQ4_A2_BYTE0 0x2175
+#define DSM_EQ_BQ4_A2_BYTE1 0x2176
+#define DSM_EQ_BQ4_A2_BYTE2 0x2177
+#define DSM_EQ_BQ5_B0_BYTE0 0x2179
+#define DSM_EQ_BQ5_B0_BYTE1 0x217a
+#define DSM_EQ_BQ5_B0_BYTE2 0x217b
+#define DSM_EQ_BQ5_B1_BYTE0 0x217d
+#define DSM_EQ_BQ5_B1_BYTE1 0x217e
+#define DSM_EQ_BQ5_B1_BYTE2 0x217f
+#define DSM_EQ_BQ5_B2_BYTE0 0x2181
+#define DSM_EQ_BQ5_B2_BYTE1 0x2182
+#define DSM_EQ_BQ5_B2_BYTE2 0x2183
+#define DSM_EQ_BQ5_A1_BYTE0 0x2185
+#define DSM_EQ_BQ5_A1_BYTE1 0x2186
+#define DSM_EQ_BQ5_A1_BYTE2 0x2187
+#define DSM_EQ_BQ5_A2_BYTE0 0x2189
+#define DSM_EQ_BQ5_A2_BYTE1 0x218a
+#define DSM_EQ_BQ5_A2_BYTE2 0x218b
+#define DSM_EQ_BQ6_B0_BYTE0 0x218d
+#define DSM_EQ_BQ6_B0_BYTE1 0x218e
+#define DSM_EQ_BQ6_B0_BYTE2 0x218f
+#define DSM_EQ_BQ6_B1_BYTE0 0x2191
+#define DSM_EQ_BQ6_B1_BYTE1 0x2192
+#define DSM_EQ_BQ6_B1_BYTE2 0x2193
+#define DSM_EQ_BQ6_B2_BYTE0 0x2195
+#define DSM_EQ_BQ6_B2_BYTE1 0x2196
+#define DSM_EQ_BQ6_B2_BYTE2 0x2197
+#define DSM_EQ_BQ6_A1_BYTE0 0x2199
+#define DSM_EQ_BQ6_A1_BYTE1 0x219a
+#define DSM_EQ_BQ6_A1_BYTE2 0x219b
+#define DSM_EQ_BQ6_A2_BYTE0 0x219d
+#define DSM_EQ_BQ6_A2_BYTE1 0x219e
+#define DSM_EQ_BQ6_A2_BYTE2 0x219f
+#define DSM_EQ_BQ7_B0_BYTE0 0x21a1
+#define DSM_EQ_BQ7_B0_BYTE1 0x21a2
+#define DSM_EQ_BQ7_B0_BYTE2 0x21a3
+#define DSM_EQ_BQ7_B1_BYTE0 0x21a5
+#define DSM_EQ_BQ7_B1_BYTE1 0x21a6
+#define DSM_EQ_BQ7_B1_BYTE2 0x21a7
+#define DSM_EQ_BQ7_B2_BYTE0 0x21a9
+#define DSM_EQ_BQ7_B2_BYTE1 0x21aa
+#define DSM_EQ_BQ7_B2_BYTE2 0x21ab
+#define DSM_EQ_BQ7_A1_BYTE0 0x21ad
+#define DSM_EQ_BQ7_A1_BYTE1 0x21ae
+#define DSM_EQ_BQ7_A1_BYTE2 0x21af
+#define DSM_EQ_BQ7_A2_BYTE0 0x21b1
+#define DSM_EQ_BQ7_A2_BYTE1 0x21b2
+#define DSM_EQ_BQ7_A2_BYTE2 0x21b3
+#define DSM_EQ_BQ8_B0_BYTE0 0x21b5
+#define DSM_EQ_BQ8_B0_BYTE1 0x21b6
+#define DSM_EQ_BQ8_B0_BYTE2 0x21b7
+#define DSM_EQ_BQ8_B1_BYTE0 0x21b9
+#define DSM_EQ_BQ8_B1_BYTE1 0x21ba
+#define DSM_EQ_BQ8_B1_BYTE2 0x21bb
+#define DSM_EQ_BQ8_B2_BYTE0 0x21bd
+#define DSM_EQ_BQ8_B2_BYTE1 0x21be
+#define DSM_EQ_BQ8_B2_BYTE2 0x21bf
+#define DSM_EQ_BQ8_A1_BYTE0 0x21c1
+#define DSM_EQ_BQ8_A1_BYTE1 0x21c2
+#define DSM_EQ_BQ8_A1_BYTE2 0x21c3
+#define DSM_EQ_BQ8_A2_BYTE0 0x21c5
+#define DSM_EQ_BQ8_A2_BYTE1 0x21c6
+#define DSM_EQ_BQ8_A2_BYTE2 0x21c7
+#define DSM_LFX_BQ_B0_BYTE0 0x21c9
+#define DSM_LFX_BQ_B0_BYTE1 0x21ca
+#define DSM_LFX_BQ_B0_BYTE2 0x21cb
+#define DSM_LFX_BQ_B1_BYTE0 0x21cd
+#define DSM_LFX_BQ_B1_BYTE1 0x21ce
+#define DSM_LFX_BQ_B1_BYTE2 0x21cf
+#define DSM_LFX_BQ_B2_BYTE0 0x21d1
+#define DSM_LFX_BQ_B2_BYTE1 0x21d2
+#define DSM_LFX_BQ_B2_BYTE2 0x21d3
+#define DSM_LFX_BQ_A1_BYTE0 0x21d5
+#define DSM_LFX_BQ_A1_BYTE1 0x21d6
+#define DSM_LFX_BQ_A1_BYTE2 0x21d7
+#define DSM_LFX_BQ_A2_BYTE0 0x21d9
+#define DSM_LFX_BQ_A2_BYTE1 0x21da
+#define DSM_LFX_BQ_A2_BYTE2 0x21db
+#define DSM_PPR_HPF_B0_BYTE0 0x21dd
+#define DSM_PPR_HPF_B0_BYTE1 0x21de
+#define DSM_PPR_HPF_B0_BYTE2 0x21df
+#define DSM_PPR_HPF_B1_BYTE0 0x21e1
+#define DSM_PPR_HPF_B1_BYTE1 0x21e2
+#define DSM_PPR_HPF_B1_BYTE2 0x21e3
+#define DSM_PPR_HPF_B2_BYTE0 0x21e5
+#define DSM_PPR_HPF_B2_BYTE1 0x21e6
+#define DSM_PPR_HPF_B2_BYTE2 0x21e7
+#define DSM_PPR_HPF_A1_BYTE0 0x21e9
+#define DSM_PPR_HPF_A1_BYTE1 0x21ea
+#define DSM_PPR_HPF_A1_BYTE2 0x21eb
+#define DSM_PPR_HPF_A2_BYTE0 0x21ed
+#define DSM_PPR_HPF_A2_BYTE1 0x21ee
+#define DSM_PPR_HPF_A2_BYTE2 0x21ef
+#define DSM_PPR_LPF_B0_BYTE0 0x21f1
+#define DSM_PPR_LPF_B0_BYTE1 0x21f2
+#define DSM_PPR_LPF_B0_BYTE2 0x21f3
+#define DSM_PPR_LPF_B1_BYTE0 0x21f5
+#define DSM_PPR_LPF_B1_BYTE1 0x21f6
+#define DSM_PPR_LPF_B1_BYTE2 0x21f7
+#define DSM_PPR_LPF_B2_BYTE0 0x21f9
+#define DSM_PPR_LPF_B2_BYTE1 0x21fa
+#define DSM_PPR_LPF_B2_BYTE2 0x21fb
+#define DSM_PPR_LPF_A1_BYTE0 0x21fd
+#define DSM_PPR_LPF_A1_BYTE1 0x21fe
+#define DSM_PPR_LPF_A1_BYTE2 0x21ff
+#define DSM_PPR_LPF_A2_BYTE0 0x2201
+#define DSM_PPR_LPF_A2_BYTE1 0x2202
+#define DSM_PPR_LPF_A2_BYTE2 0x2203
+#define DSM_SPL_BQ_B0_BYTE0 0x2205
+#define DSM_SPL_BQ_B0_BYTE1 0x2206
+#define DSM_SPL_BQ_B0_BYTE2 0x2207
+#define DSM_SPL_BQ_B1_BYTE0 0x2209
+#define DSM_SPL_BQ_B1_BYTE1 0x220a
+#define DSM_SPL_BQ_B1_BYTE2 0x220b
+#define DSM_SPL_BQ_B2_BYTE0 0x220d
+#define DSM_SPL_BQ_B2_BYTE1 0x220e
+#define DSM_SPL_BQ_B2_BYTE2 0x220f
+#define DSM_SPL_BQ_A1_BYTE0 0x2211
+#define DSM_SPL_BQ_A1_BYTE1 0x2212
+#define DSM_SPL_BQ_A1_BYTE2 0x2213
+#define DSM_SPL_BQ_A2_BYTE0 0x2215
+#define DSM_SPL_BQ_A2_BYTE1 0x2216
+#define DSM_SPL_BQ_A2_BYTE2 0x2217
+#define DSM_EXCUR_BQ_B0_BYTE0 0x2219
+#define DSM_EXCUR_BQ_B0_BYTE1 0x221a
+#define DSM_EXCUR_BQ_B0_BYTE2 0x221b
+#define DSM_EXCUR_BQ_B1_BYTE0 0x221d
+#define DSM_EXCUR_BQ_B1_BYTE1 0x221e
+#define DSM_EXCUR_BQ_B1_BYTE2 0x221f
+#define DSM_EXCUR_BQ_B2_BYTE0 0x2221
+#define DSM_EXCUR_BQ_B2_BYTE1 0x2222
+#define DSM_EXCUR_BQ_B2_BYTE2 0x2223
+#define DSM_EXCUR_BQ_A1_BYTE0 0x2225
+#define DSM_EXCUR_BQ_A1_BYTE1 0x2226
+#define DSM_EXCUR_BQ_A1_BYTE2 0x2227
+#define DSM_EXCUR_BQ_A2_BYTE0 0x2229
+#define DSM_EXCUR_BQ_A2_BYTE1 0x222a
+#define DSM_EXCUR_BQ_A2_BYTE2 0x222b
+#define DSM_EXCPROT_HPF1_B0_BYTE0 0x222d
+#define DSM_EXCPROT_HPF1_B0_BYTE1 0x222e
+#define DSM_EXCPROT_HPF1_B0_BYTE2 0x222f
+#define DSM_EXCPROT_HPF1_B1_BYTE0 0x2231
+#define DSM_EXCPROT_HPF1_B1_BYTE1 0x2232
+#define DSM_EXCPROT_HPF1_B1_BYTE2 0x2233
+#define DSM_EXCPROT_HPF1_B2_BYTE0 0x2235
+#define DSM_EXCPROT_HPF1_B2_BYTE1 0x2236
+#define DSM_EXCPROT_HPF1_B2_BYTE2 0x2237
+#define DSM_EXCPROT_HPF1_A1_BYTE0 0x2239
+#define DSM_EXCPROT_HPF1_A1_BYTE1 0x223a
+#define DSM_EXCPROT_HPF1_A1_BYTE2 0x223b
+#define DSM_EXCPROT_HPF1_A2_BYTE0 0x223d
+#define DSM_EXCPROT_HPF1_A2_BYTE1 0x223e
+#define DSM_EXCPROT_HPF1_A2_BYTE2 0x223f
+#define DSM_EXCPROT_HPF2_B0_BYTE0 0x2241
+#define DSM_EXCPROT_HPF2_B0_BYTE1 0x2242
+#define DSM_EXCPROT_HPF2_B0_BYTE2 0x2243
+#define DSM_EXCPROT_HPF2_B1_BYTE0 0x2245
+#define DSM_EXCPROT_HPF2_B1_BYTE1 0x2246
+#define DSM_EXCPROT_HPF2_B1_BYTE2 0x2247
+#define DSM_EXCPROT_HPF2_B2_BYTE0 0x2249
+#define DSM_EXCPROT_HPF2_B2_BYTE1 0x224a
+#define DSM_EXCPROT_HPF2_B2_BYTE2 0x224b
+#define DSM_EXCPROT_HPF2_A1_BYTE0 0x224d
+#define DSM_EXCPROT_HPF2_A1_BYTE1 0x224e
+#define DSM_EXCPROT_HPF2_A1_BYTE2 0x224f
+#define DSM_EXCPROT_HPF2_A2_BYTE0 0x2251
+#define DSM_EXCPROT_HPF2_A2_BYTE1 0x2252
+#define DSM_EXCPROT_HPF2_A2_BYTE2 0x2253
+#define DSM_EXCPROT_HPF3_B0_BYTE0 0x2255
+#define DSM_EXCPROT_HPF3_B0_BYTE1 0x2256
+#define DSM_EXCPROT_HPF3_B0_BYTE2 0x2257
+#define DSM_EXCPROT_HPF3_B1_BYTE0 0x2259
+#define DSM_EXCPROT_HPF3_B1_BYTE1 0x225a
+#define DSM_EXCPROT_HPF3_B1_BYTE2 0x225b
+#define DSM_EXCPROT_HPF3_B2_BYTE0 0x225d
+#define DSM_EXCPROT_HPF3_B2_BYTE1 0x225e
+#define DSM_EXCPROT_HPF3_B2_BYTE2 0x225f
+#define DSM_EXCPROT_HPF3_A1_BYTE0 0x2261
+#define DSM_EXCPROT_HPF3_A1_BYTE1 0x2262
+#define DSM_EXCPROT_HPF3_A1_BYTE2 0x2263
+#define DSM_EXCPROT_HPF3_A2_BYTE0 0x2265
+#define DSM_EXCPROT_HPF3_A2_BYTE1 0x2266
+#define DSM_EXCPROT_HPF3_A2_BYTE2 0x2267
+#define DSM_EXCPROT_HPF4_B0_BYTE0 0x2269
+#define DSM_EXCPROT_HPF4_B0_BYTE1 0x226a
+#define DSM_EXCPROT_HPF4_B0_BYTE2 0x226b
+#define DSM_EXCPROT_HPF4_B1_BYTE0 0x226d
+#define DSM_EXCPROT_HPF4_B1_BYTE1 0x226e
+#define DSM_EXCPROT_HPF4_B1_BYTE2 0x226f
+#define DSM_EXCPROT_HPF4_B2_BYTE0 0x2271
+#define DSM_EXCPROT_HPF4_B2_BYTE1 0x2272
+#define DSM_EXCPROT_HPF4_B2_BYTE2 0x2273
+#define DSM_EXCPROT_HPF4_A1_BYTE0 0x2275
+#define DSM_EXCPROT_HPF4_A1_BYTE1 0x2276
+#define DSM_EXCPROT_HPF4_A1_BYTE2 0x2277
+#define DSM_EXCPROT_HPF4_A2_BYTE0 0x2279
+#define DSM_EXCPROT_HPF4_A2_BYTE1 0x227a
+#define DSM_EXCPROT_HPF4_A2_BYTE2 0x227b
+#define DSM_EXCPROT_HPF5_B0_BYTE0 0x227d
+#define DSM_EXCPROT_HPF5_B0_BYTE1 0x227e
+#define DSM_EXCPROT_HPF5_B0_BYTE2 0x227f
+#define DSM_EXCPROT_HPF5_B1_BYTE0 0x2281
+#define DSM_EXCPROT_HPF5_B1_BYTE1 0x2282
+#define DSM_EXCPROT_HPF5_B1_BYTE2 0x2283
+#define DSM_EXCPROT_HPF5_B2_BYTE0 0x2285
+#define DSM_EXCPROT_HPF5_B2_BYTE1 0x2286
+#define DSM_EXCPROT_HPF5_B2_BYTE2 0x2287
+#define DSM_EXCPROT_HPF5_A1_BYTE0 0x2289
+#define DSM_EXCPROT_HPF5_A1_BYTE1 0x228a
+#define DSM_EXCPROT_HPF5_A1_BYTE2 0x228b
+#define DSM_EXCPROT_HPF5_A2_BYTE0 0x228d
+#define DSM_EXCPROT_HPF5_A2_BYTE1 0x228e
+#define DSM_EXCPROT_HPF5_A2_BYTE2 0x228f
+#define DSM_DEBUZZ_BPF_B0_BYTE0 0x2291
+#define DSM_DEBUZZ_BPF_B0_BYTE1 0x2292
+#define DSM_DEBUZZ_BPF_B0_BYTE2 0x2293
+#define DSM_DEBUZZ_BPF_B1_BYTE0 0x2295
+#define DSM_DEBUZZ_BPF_B1_BYTE1 0x2296
+#define DSM_DEBUZZ_BPF_B1_BYTE2 0x2297
+#define DSM_DEBUZZ_BPF_B2_BYTE0 0x2299
+#define DSM_DEBUZZ_BPF_B2_BYTE1 0x229a
+#define DSM_DEBUZZ_BPF_B2_BYTE2 0x229b
+#define DSM_DEBUZZ_BPF_A1_BYTE0 0x229d
+#define DSM_DEBUZZ_BPF_A1_BYTE1 0x229e
+#define DSM_DEBUZZ_BPF_A1_BYTE2 0x229f
+#define DSM_DEBUZZ_BPF_A2_BYTE0 0x22a1
+#define DSM_DEBUZZ_BPF_A2_BYTE1 0x22a2
+#define DSM_DEBUZZ_BPF_A2_BYTE2 0x22a3
+#define DSM_DEBUZZ_PORT_B0_BYTE0 0x22a5
+#define DSM_DEBUZZ_PORT_B0_BYTE1 0x22a6
+#define DSM_DEBUZZ_PORT_B0_BYTE2 0x22a7
+#define DSM_DEBUZZ_PORT_B1_BYTE0 0x22a9
+#define DSM_DEBUZZ_PORT_B1_BYTE1 0x22aa
+#define DSM_DEBUZZ_PORT_B1_BYTE2 0x22ab
+#define DSM_DEBUZZ_PORT_B2_BYTE0 0x22ad
+#define DSM_DEBUZZ_PORT_B2_BYTE1 0x22ae
+#define DSM_DEBUZZ_PORT_B2_BYTE2 0x22af
+#define DSM_DEBUZZ_PORT_A1_BYTE0 0x22b1
+#define DSM_DEBUZZ_PORT_A1_BYTE1 0x22b2
+#define DSM_DEBUZZ_PORT_A1_BYTE2 0x22b3
+#define DSM_DEBUZZ_PORT_A2_BYTE0 0x22b5
+#define DSM_DEBUZZ_PORT_A2_BYTE1 0x22b6
+#define DSM_DEBUZZ_PORT_A2_BYTE2 0x22b7
+#define DSM_DEBUZZ_NOTCH_B0_BYTE0 0x22b9
+#define DSM_DEBUZZ_NOTCH_B0_BYTE1 0x22ba
+#define DSM_DEBUZZ_NOTCH_B0_BYTE2 0x22bb
+#define DSM_DEBUZZ_NOTCH_B1_BYTE0 0x22bd
+#define DSM_DEBUZZ_NOTCH_B1_BYTE1 0x22be
+#define DSM_DEBUZZ_NOTCH_B1_BYTE2 0x22bf
+#define DSM_DEBUZZ_NOTCH_B2_BYTE0 0x22c1
+#define DSM_DEBUZZ_NOTCH_B2_BYTE1 0x22c2
+#define DSM_DEBUZZ_NOTCH_B2_BYTE2 0x22c3
+#define DSM_DEBUZZ_NOTCH_A1_BYTE0 0x22c5
+#define DSM_DEBUZZ_NOTCH_A1_BYTE1 0x22c6
+#define DSM_DEBUZZ_NOTCH_A1_BYTE2 0x22c7
+#define DSM_DEBUZZ_NOTCH_A2_BYTE0 0x22c9
+#define DSM_DEBUZZ_NOTCH_A2_BYTE1 0x22ca
+#define DSM_DEBUZZ_NOTCH_A2_BYTE2 0x22cb
+#define DSM_THERMAL_BQ_B0_BYTE0 0x22cd
+#define DSM_THERMAL_BQ_B0_BYTE1 0x22ce
+#define DSM_THERMAL_BQ_B0_BYTE2 0x22cf
+#define DSM_THERMAL_BQ_B1_BYTE0 0x22d1
+#define DSM_THERMAL_BQ_B1_BYTE1 0x22d2
+#define DSM_THERMAL_BQ_B1_BYTE2 0x22d3
+#define DSM_THERMAL_BQ_B2_BYTE0 0x22d5
+#define DSM_THERMAL_BQ_B2_BYTE1 0x22d6
+#define DSM_THERMAL_BQ_B2_BYTE2 0x22d7
+#define DSM_THERMAL_BQ_A1_BYTE0 0x22d9
+#define DSM_THERMAL_BQ_A1_BYTE1 0x22da
+#define DSM_THERMAL_BQ_A1_BYTE2 0x22db
+#define DSM_THERMAL_BQ_A2_BYTE0 0x22dd
+#define DSM_THERMAL_BQ_A2_BYTE1 0x22de
+#define DSM_THERMAL_BQ_A2_BYTE2 0x22df
+#define DSM_WBDRC_FILT1_B0_BYTE0 0x22e1
+#define DSM_WBDRC_FILT1_B0_BYTE1 0x22e2
+#define DSM_WBDRC_FILT1_B0_BYTE2 0x22e3
+#define DSM_WBDRC_FILT1_B1_BYTE0 0x22e5
+#define DSM_WBDRC_FILT1_B1_BYTE1 0x22e6
+#define DSM_WBDRC_FILT1_B1_BYTE2 0x22e7
+#define DSM_WBDRC_FILT1_B2_BYTE0 0x22e9
+#define DSM_WBDRC_FILT1_B2_BYTE1 0x22ea
+#define DSM_WBDRC_FILT1_B2_BYTE2 0x22eb
+#define DSM_WBDRC_FILT1_A1_BYTE0 0x22ed
+#define DSM_WBDRC_FILT1_A1_BYTE1 0x22ee
+#define DSM_WBDRC_FILT1_A1_BYTE2 0x22ef
+#define DSM_WBDRC_FILT1_A2_BYTE0 0x22f1
+#define DSM_WBDRC_FILT1_A2_BYTE1 0x22f2
+#define DSM_WBDRC_FILT1_A2_BYTE2 0x22f3
+#define DSM_WBDRC_FILT2_B0_BYTE0 0x22f5
+#define DSM_WBDRC_FILT2_B0_BYTE1 0x22f6
+#define DSM_WBDRC_FILT2_B0_BYTE2 0x22f7
+#define DSM_WBDRC_FILT2_B1_BYTE0 0x22f9
+#define DSM_WBDRC_FILT2_B1_BYTE1 0x22fa
+#define DSM_WBDRC_FILT2_B1_BYTE2 0x22fb
+#define DSM_WBDRC_FILT2_B2_BYTE0 0x22fd
+#define DSM_WBDRC_FILT2_B2_BYTE1 0x22fe
+#define DSM_WBDRC_FILT2_B2_BYTE2 0x22ff
+#define DSM_WBDRC_FILT2_A1_BYTE0 0x2301
+#define DSM_WBDRC_FILT2_A1_BYTE1 0x2302
+#define DSM_WBDRC_FILT2_A1_BYTE2 0x2303
+#define DSM_WBDRC_FILT2_A2_BYTE0 0x2305
+#define DSM_WBDRC_FILT2_A2_BYTE1 0x2306
+#define DSM_WBDRC_FILT2_A2_BYTE2 0x2307
+#define DSM_PPR_RELEASE_TIME_BYTE0 0x2309
+#define DSM_PPR_RELEASE_TIME_BYTE1 0x230a
+#define DSM_PPR_RELEASE_TIME_BYTE2 0x230b
+#define DSM_PPR_ATTACK_TIME_BYTE0 0x230d
+#define DSM_PPR_ATTACK_TIME_BYTE1 0x230e
+#define DSM_PPR_ATTACK_TIME_BYTE2 0x230f
+#define DSM_DEBUZZER_RELEASE_TIME_BYTE0 0x2311
+#define DSM_DEBUZZER_RELEASE_TIME_BYTE1 0x2312
+#define DSM_DEBUZZER_RELEASE_TIME_BYTE2 0x2313
+#define DSM_DEBUZZER_ATTACK_TIME_BYTE0 0x2315
+#define DSM_DEBUZZER_ATTACK_TIME_BYTE1 0x2316
+#define DSM_DEBUZZER_ATTACK_TIME_BYTE2 0x2317
+
+#define DSMIG_WB_DRC_RELEASE_TIME_1 0x2380
+#define DSMIG_WB_DRC_RELEASE_TIME_2 0x2381
+#define DSMIG_WB_DRC_ATTACK_TIME_1 0x2382
+#define DSMIG_WB_DRC_ATTACK_TIME_2 0x2383
+#define DSMIG_WB_DRC_COMPRESSION_RATIO 0x2384
+#define DSMIG_WB_DRC_COMPRESSION_THRESHOLD 0x2385
+#define DSMIG_WB_DRC_MAKEUPGAIN 0x2386
+#define DSMIG_WB_DRC_NOISE_GATE_THRESHOLD 0x2387
+#define DSMIG_WBDRC_HPF_ENABLE 0x2388
+#define DSMIG_WB_DRC_TEST_SMOOTHER_OUT_EN 0x2389
+#define DSMIG_PPR_THRESHOLD 0x238b
+#define DSM_STEREO_BASS_CHANNEL_SELECT 0x238d
+#define DSM_TPROT_THRESHOLD_BYTE0 0x238e
+#define DSM_TPROT_THRESHOLD_BYTE1 0x238f
+#define DSM_TPROT_ROOM_TEMPERATURE_BYTE0 0x2390
+#define DSM_TPROT_ROOM_TEMPERATURE_BYTE1 0x2391
+#define DSM_TPROT_RECIP_RDC_ROOM_BYTE0 0x2392
+#define DSM_TPROT_RECIP_RDC_ROOM_BYTE1 0x2393
+#define DSM_TPROT_RECIP_RDC_ROOM_BYTE2 0x2394
+#define DSM_TPROT_RECIP_TCONST_BYTE0 0x2395
+#define DSM_TPROT_RECIP_TCONST_BYTE1 0x2396
+#define DSM_TPROT_RECIP_TCONST_BYTE2 0x2397
+#define DSM_THERMAL_ATTENUATION_SETTINGS 0x2398
+#define DSM_THERMAL_PILOT_TONE_ATTENUATION 0x2399
+#define DSM_TPROT_PG_TEMP_THRESH_BYTE0 0x239a
+#define DSM_TPROT_PG_TEMP_THRESH_BYTE1 0x239b
+
+#define THERMAL_RDC_RD_BACK_BYTE1 0x239c
+#define THERMAL_RDC_RD_BACK_BYTE0 0x239d
+#define THERMAL_COILTEMP_RD_BACK_BYTE1 0x239e
+#define THERMAL_COILTEMP_RD_BACK_BYTE0 0x239f
+
+#define DSMIG_DEBUZZER_THRESHOLD 0x23b5
+#define DSMIG_DEBUZZER_ALPHA_COEF_TEST_ONLY 0x23b6
+#define DSM_VOL_ENA 0x23b9
+#define DSM_VOL_CTRL 0x23ba
+
+#define DSMIG_EN 0x23e0
+#define MAX98390_R23E1_DSP_GLOBAL_EN 0x23e1
+
+#define DSM_THERMAL_GAIN 0x23f0
+#define DSM_PPR_GAIN 0x23f1
+#define DSM_DBZ_GAIN 0x23f2
+#define DSM_WBDRC_GAIN 0x23f3
+
+#define MAX98390_R23FF_GLOBAL_EN 0x23FF
+#define MAX98390_R24FF_REV_ID 0x24FF
+
+/* MAX98390_R2021_PCM_RX_SRC_1 */
+#define MAX98390_PCM_RX_CH_SRC_SHIFT (0)
+#define MAX98390_PCM_RX_CH_SRC_BASS_SHIFT (4)
+
+/* MAX98390_R2022_PCM_TX_SRC_1 */
+#define MAX98390_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98390_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98390_R2024_PCM_DATA_FMT_CFG */
+#define MAX98390_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98390_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98390_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98390_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98390_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98390_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98390_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98390_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98390_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98390_R2039_AMP_DSP_CFG */
+#define MAX98390_AMP_DSP_CFG_RMP_UP_SHIFT (4)
+#define MAX98390_AMP_DSP_CFG_RMP_DN_SHIFT (5)
+
+/* MAX98390_R203A_AMP_EN */
+#define MAX98390_R203A_AMP_EN_SHIFT (0)
+
+/* MAX98390_PCM_MASTER_MODE */
+#define MAX98390_PCM_MASTER_MODE_MASK (0x3 << 0)
+#define MAX98390_PCM_MASTER_MODE_SLAVE (0x0 << 0)
+#define MAX98390_PCM_MASTER_MODE_MASTER (0x3 << 0)
+
+#define MAX98390_PCM_MASTER_MODE_MCLK_MASK (0xF << 2)
+#define MAX98390_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2)
+
+/* PCM_CLK_SETUP */
+#define MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2)
+#define MAX98390_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* PCM_SR_SETUP */
+#define MAX98390_PCM_SR_SET1_SR_MASK (0xF << 0)
+#define MAX98390_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98390_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98390_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98390_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98390_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98390_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98390_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98390_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98390_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* PCM_TO_SPK_MONO_MIX_1 */
+#define MAX98390_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98390_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+#define MAX98390_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0)
+#define MAX98390_PCM_TO_SPK_CH1_SRC_MASK (0xF << 4)
+
+/* MAX98390_BOOST_CTRL3 */
+#define MAX98390_BOOST_CLK_PHASE_CFG_SHIFT (2)
+
+/* SOFT_RESET */
+#define MAX98390_SOFT_RESET_MASK (0x1 << 0)
+
+#define MAX98390_GLOBAL_EN_MASK (0x1 << 0)
+#define MAX98390_AMP_EN_MASK (0x1 << 0)
+
+/* DSM register offset */
+#define MAX98390_DSM_PAYLOAD_OFFSET 16
+#define MAX98390_DSM_PARAM_MAX_SIZE 1024
+#define MAX98390_DSM_PARAM_MIN_SIZE 670
+
+struct max98390_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ unsigned int provider;
+ unsigned int tdm_mode;
+ unsigned int v_l_slot;
+ unsigned int i_l_slot;
+ unsigned int ref_rdc_value;
+ unsigned int ambient_temp_value;
+ const char *dsm_param_name;
+};
+#endif
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
new file mode 100644
index 000000000000..18fd90227187
--- /dev/null
+++ b/sound/soc/codecs/max98396.c
@@ -0,0 +1,1914 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98396.h"
+
+static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
+ "avdd",
+ "dvdd",
+ "dvddio",
+};
+
+static const struct reg_default max98396_reg[] = {
+ {MAX98396_R2000_SW_RESET, 0x00},
+ {MAX98396_R2001_INT_RAW1, 0x00},
+ {MAX98396_R2002_INT_RAW2, 0x00},
+ {MAX98396_R2003_INT_RAW3, 0x00},
+ {MAX98396_R2004_INT_RAW4, 0x00},
+ {MAX98396_R2006_INT_STATE1, 0x00},
+ {MAX98396_R2007_INT_STATE2, 0x00},
+ {MAX98396_R2008_INT_STATE3, 0x00},
+ {MAX98396_R2009_INT_STATE4, 0x00},
+ {MAX98396_R200B_INT_FLAG1, 0x00},
+ {MAX98396_R200C_INT_FLAG2, 0x00},
+ {MAX98396_R200D_INT_FLAG3, 0x00},
+ {MAX98396_R200E_INT_FLAG4, 0x00},
+ {MAX98396_R2010_INT_EN1, 0x02},
+ {MAX98396_R2011_INT_EN2, 0x00},
+ {MAX98396_R2012_INT_EN3, 0x00},
+ {MAX98396_R2013_INT_EN4, 0x00},
+ {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+ {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+ {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+ {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+ {MAX98396_R201F_IRQ_CTRL, 0x00},
+ {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+ {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+ {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+ {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+ {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+ {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+ {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+ {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+ {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+ {MAX98396_R2040_PIN_CFG, 0x55},
+ {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+ {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+ {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+ {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+ {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+ {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+ {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+ {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+ {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+ {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+ {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+ {MAX98396_R204C_PCM_TX_HIZ_CTRL_1, 0xFF},
+ {MAX98396_R204D_PCM_TX_HIZ_CTRL_2, 0xFF},
+ {MAX98396_R204E_PCM_TX_HIZ_CTRL_3, 0xFF},
+ {MAX98396_R204F_PCM_TX_HIZ_CTRL_4, 0xFF},
+ {MAX98396_R2050_PCM_TX_HIZ_CTRL_5, 0xFF},
+ {MAX98396_R2051_PCM_TX_HIZ_CTRL_6, 0xFF},
+ {MAX98396_R2052_PCM_TX_HIZ_CTRL_7, 0xFF},
+ {MAX98396_R2053_PCM_TX_HIZ_CTRL_8, 0xFF},
+ {MAX98396_R2055_PCM_RX_SRC1, 0x00},
+ {MAX98396_R2056_PCM_RX_SRC2, 0x00},
+ {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+ {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98396_R205E_PCM_RX_EN, 0x00},
+ {MAX98396_R205F_PCM_TX_EN, 0x00},
+ {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+ {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+ {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+ {MAX98396_R207F_ICC_EN, 0x00},
+ {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+ {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+ {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+ {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+ {MAX98396_R208F_TONE_GEN_EN, 0x00},
+ {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98396_R2091_AMP_PATH_GAIN, 0x0B},
+ {MAX98396_R2092_AMP_DSP_CFG, 0x23},
+ {MAX98396_R2093_SSM_CFG, 0x0D},
+ {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+ {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+ {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+ {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+ {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+ {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+ {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+ {MAX98396_R209C_SPK_EDGE_CTRL1, 0x0A},
+ {MAX98396_R209D_SPK_EDGE_CTRL2, 0xAA},
+ {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+ {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+ {MAX98396_R20A0_AMP_SUPPLY_CTL, 0x00},
+ {MAX98396_R20AF_AMP_EN, 0x00},
+ {MAX98396_R20B0_ADC_SR, 0x30},
+ {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+ {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+ {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+ {MAX98396_R20B4_ADC_READBACK_CTRL1, 0x00},
+ {MAX98396_R20B5_ADC_READBACK_CTRL2, 0x00},
+ {MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98396_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0x00},
+ {MAX98396_R20B9_ADC_VBAT_READBACK_LSB, 0x00},
+ {MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98396_R20BB_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB, 0x00},
+ {MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB, 0x00},
+ {MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB, 0x00},
+ {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+ {MAX98396_R20C7_ADC_CFG, 0x00},
+ {MAX98396_R20D0_DHT_CFG1, 0x00},
+ {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98396_R20D3_DHT_CFG2, 0x14},
+ {MAX98396_R20D4_DHT_CFG3, 0x02},
+ {MAX98396_R20D5_DHT_CFG4, 0x04},
+ {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98396_R20DF_DHT_EN, 0x00},
+ {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+ {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+ {MAX98396_R20E5_BPE_STATE, 0x00},
+ {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+ {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+ {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+ {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+ {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+ {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+ {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+ {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+ {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+ {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+ {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+ {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+ {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+ {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+ {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+ {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+ {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+ {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+ {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+ {MAX98396_R210D_BPE_EN, 0x00},
+ {MAX98396_R210E_AUTO_RESTART, 0x00},
+ {MAX98396_R210F_GLOBAL_EN, 0x00},
+ {MAX98396_R21FF_REVISION_ID, 0x00},
+};
+
+static const struct reg_default max98397_reg[] = {
+ {MAX98396_R2000_SW_RESET, 0x00},
+ {MAX98396_R2001_INT_RAW1, 0x00},
+ {MAX98396_R2002_INT_RAW2, 0x00},
+ {MAX98396_R2003_INT_RAW3, 0x00},
+ {MAX98396_R2004_INT_RAW4, 0x00},
+ {MAX98396_R2006_INT_STATE1, 0x00},
+ {MAX98396_R2007_INT_STATE2, 0x00},
+ {MAX98396_R2008_INT_STATE3, 0x00},
+ {MAX98396_R2009_INT_STATE4, 0x00},
+ {MAX98396_R200B_INT_FLAG1, 0x00},
+ {MAX98396_R200C_INT_FLAG2, 0x00},
+ {MAX98396_R200D_INT_FLAG3, 0x00},
+ {MAX98396_R200E_INT_FLAG4, 0x00},
+ {MAX98396_R2010_INT_EN1, 0x02},
+ {MAX98396_R2011_INT_EN2, 0x00},
+ {MAX98396_R2012_INT_EN3, 0x00},
+ {MAX98396_R2013_INT_EN4, 0x00},
+ {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+ {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+ {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+ {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+ {MAX98396_R201F_IRQ_CTRL, 0x00},
+ {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+ {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+ {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+ {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+ {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+ {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+ {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+ {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+ {MAX98397_R203A_SPK_MON_THRESH, 0x03},
+ {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+ {MAX98396_R2040_PIN_CFG, 0x55},
+ {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+ {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+ {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+ {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+ {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+ {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+ {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+ {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+ {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+ {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+ {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+ {MAX98397_R204C_PCM_TX_CTRL_9, 0x00},
+ {MAX98397_R204D_PCM_TX_HIZ_CTRL_1, 0xFF},
+ {MAX98397_R204E_PCM_TX_HIZ_CTRL_2, 0xFF},
+ {MAX98397_R204F_PCM_TX_HIZ_CTRL_3, 0xFF},
+ {MAX98397_R2050_PCM_TX_HIZ_CTRL_4, 0xFF},
+ {MAX98397_R2051_PCM_TX_HIZ_CTRL_5, 0xFF},
+ {MAX98397_R2052_PCM_TX_HIZ_CTRL_6, 0xFF},
+ {MAX98397_R2053_PCM_TX_HIZ_CTRL_7, 0xFF},
+ {MAX98397_R2054_PCM_TX_HIZ_CTRL_8, 0xFF},
+ {MAX98397_R2056_PCM_RX_SRC1, 0x00},
+ {MAX98397_R2057_PCM_RX_SRC2, 0x00},
+ {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+ {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98396_R205E_PCM_RX_EN, 0x00},
+ {MAX98396_R205F_PCM_TX_EN, 0x00},
+ {MAX98397_R2060_PCM_TX_SUPPLY_SEL, 0x00},
+ {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+ {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+ {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+ {MAX98396_R207F_ICC_EN, 0x00},
+ {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+ {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+ {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+ {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+ {MAX98396_R208F_TONE_GEN_EN, 0x00},
+ {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98396_R2091_AMP_PATH_GAIN, 0x12},
+ {MAX98396_R2092_AMP_DSP_CFG, 0x22},
+ {MAX98396_R2093_SSM_CFG, 0x08},
+ {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+ {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+ {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+ {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+ {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+ {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+ {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+ {MAX98397_R209B_SPK_PATH_WB_ONLY, 0x00},
+ {MAX98396_R209C_SPK_EDGE_CTRL1, 0x03},
+ {MAX98396_R209D_SPK_EDGE_CTRL2, 0xFC},
+ {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+ {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+ {MAX98396_R20AF_AMP_EN, 0x00},
+ {MAX98396_R20B0_ADC_SR, 0x30},
+ {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+ {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+ {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+ {MAX98397_R20B4_ADC_VDDH_CFG, 0x00},
+ {MAX98397_R20B5_ADC_READBACK_CTRL1, 0x00},
+ {MAX98397_R20B6_ADC_READBACK_CTRL2, 0x00},
+ {MAX98397_R20B7_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98397_R20B8_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98397_R20B9_ADC_VBAT_READBACK_MSB, 0x00},
+ {MAX98397_R20BA_ADC_VBAT_READBACK_LSB, 0x00},
+ {MAX98397_R20BB_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98397_R20BC_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98397_R20BD_ADC_VDDH__READBACK_MSB, 0x00},
+ {MAX98397_R20BE_ADC_VDDH_READBACK_LSB, 0x00},
+ {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+ {MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB, 0x00},
+ {MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB, 0x00},
+ {MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE, 0x04},
+ {MAX98396_R20C7_ADC_CFG, 0x00},
+ {MAX98396_R20D0_DHT_CFG1, 0x00},
+ {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98396_R20D3_DHT_CFG2, 0x14},
+ {MAX98396_R20D4_DHT_CFG3, 0x02},
+ {MAX98396_R20D5_DHT_CFG4, 0x04},
+ {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98396_R20DF_DHT_EN, 0x00},
+ {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+ {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+ {MAX98396_R20E5_BPE_STATE, 0x00},
+ {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+ {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+ {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+ {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+ {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+ {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+ {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+ {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+ {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+ {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+ {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+ {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+ {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+ {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+ {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+ {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+ {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+ {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+ {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+ {MAX98396_R210D_BPE_EN, 0x00},
+ {MAX98396_R210E_AUTO_RESTART, 0x00},
+ {MAX98396_R210F_GLOBAL_EN, 0x00},
+ {MAX98397_R22FF_REVISION_ID, 0x00},
+};
+
+static void max98396_global_enable_onoff(struct regmap *regmap, bool onoff)
+{
+ regmap_write(regmap, MAX98396_R210F_GLOBAL_EN, onoff ? 1 : 0);
+ usleep_range(11000, 12000);
+}
+
+static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ unsigned int format_mask, format = 0;
+ unsigned int bclk_pol = 0;
+ int ret, status;
+ int reg;
+ bool update = false;
+
+ format_mask = MAX98396_PCM_MODE_CFG_FORMAT_MASK |
+ MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+ format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+ break;
+
+ default:
+ dev_err(component->dev, "DAI invert mode %d unsupported\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= MAX98396_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= MAX98396_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= MAX98396_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format |= MAX98396_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ dev_err(component->dev, "DAI format %d unsupported\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (format != (reg & format_mask)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (bclk_pol != (reg & MAX98396_PCM_MODE_CFG_BCLKEDGE))
+ update = true;
+ }
+ /* GLOBAL_EN OFF prior to pcm mode, clock configuration change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ format_mask, format);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_MODE_CFG_BCLKEDGE,
+ bclk_pol);
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+}
+
+#define MAX98396_BSEL_32 0x2
+#define MAX98396_BSEL_48 0x3
+#define MAX98396_BSEL_64 0x4
+#define MAX98396_BSEL_96 0x5
+#define MAX98396_BSEL_128 0x6
+#define MAX98396_BSEL_192 0x7
+#define MAX98396_BSEL_256 0x8
+#define MAX98396_BSEL_384 0x9
+#define MAX98396_BSEL_512 0xa
+#define MAX98396_BSEL_320 0xb
+#define MAX98396_BSEL_250 0xc
+#define MAX98396_BSEL_125 0xd
+
+/* Refer to table 5 in the datasheet */
+static const struct max98396_pcm_config {
+ int in, out, width, bsel, max_sr;
+} max98396_pcm_configs[] = {
+ { .in = 2, .out = 4, .width = 16, .bsel = MAX98396_BSEL_32, .max_sr = 192000 },
+ { .in = 2, .out = 6, .width = 24, .bsel = MAX98396_BSEL_48, .max_sr = 192000 },
+ { .in = 2, .out = 8, .width = 32, .bsel = MAX98396_BSEL_64, .max_sr = 192000 },
+ { .in = 3, .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+ { .in = 4, .out = 8, .width = 16, .bsel = MAX98396_BSEL_64, .max_sr = 192000 },
+ { .in = 4, .out = 12, .width = 24, .bsel = MAX98396_BSEL_96, .max_sr = 192000 },
+ { .in = 4, .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 192000 },
+ { .in = 5, .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+ { .in = 7, .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+ { .in = 2, .out = 4, .width = 16, .bsel = MAX98396_BSEL_32, .max_sr = 96000 },
+ { .in = 2, .out = 6, .width = 24, .bsel = MAX98396_BSEL_48, .max_sr = 96000 },
+ { .in = 2, .out = 8, .width = 32, .bsel = MAX98396_BSEL_64, .max_sr = 96000 },
+ { .in = 3, .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 96000 },
+ { .in = 4, .out = 8, .width = 16, .bsel = MAX98396_BSEL_64, .max_sr = 96000 },
+ { .in = 4, .out = 12, .width = 24, .bsel = MAX98396_BSEL_96, .max_sr = 96000 },
+ { .in = 4, .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 96000 },
+ { .in = 5, .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 96000 },
+ { .in = 7, .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 96000 },
+ { .in = 7, .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 96000 },
+ { .in = 8, .out = 16, .width = 16, .bsel = MAX98396_BSEL_128, .max_sr = 96000 },
+ { .in = 8, .out = 24, .width = 24, .bsel = MAX98396_BSEL_192, .max_sr = 96000 },
+ { .in = 8, .out = 32, .width = 32, .bsel = MAX98396_BSEL_256, .max_sr = 96000 },
+ { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 96000 },
+ { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 96000 },
+ { .in = 16, .out = 32, .width = 16, .bsel = MAX98396_BSEL_256, .max_sr = 96000 },
+ { .in = 7, .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 48000 },
+ { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 48000 },
+ { .in = 10, .out = 40, .width = 32, .bsel = MAX98396_BSEL_320, .max_sr = 48000 },
+ { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 48000 },
+ { .in = 16, .out = 48, .width = 24, .bsel = MAX98396_BSEL_384, .max_sr = 48000 },
+ { .in = 16, .out = 64, .width = 32, .bsel = MAX98396_BSEL_512, .max_sr = 48000 },
+};
+
+static int max98396_pcm_config_index(int in_slots, int out_slots, int width)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max98396_pcm_configs); i++) {
+ const struct max98396_pcm_config *c = &max98396_pcm_configs[i];
+
+ if (in_slots == c->in && out_slots <= c->out && width == c->width)
+ return i;
+ }
+
+ return -1;
+}
+
+static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+ int ret, reg, status, bsel = 0;
+ bool update = false;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98396_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98396_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98396_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98396_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98396_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98396_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98396_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98396_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98396_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98396_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98396_PCM_SR_96000;
+ break;
+ case 192000:
+ sampling_rate = MAX98396_PCM_SR_192000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ if (max98396->tdm_mode) {
+ if (params_rate(params) > max98396->tdm_max_samplerate) {
+ dev_err(component->dev, "TDM sample rate %d too high",
+ params_rate(params));
+ goto err;
+ }
+ } else {
+ /* BCLK configuration */
+ ret = max98396_pcm_config_index(params_channels(params),
+ params_channels(params),
+ snd_pcm_format_width(params_format(params)));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "no PCM config for %d channels, format %d\n",
+ params_channels(params), params_format(params));
+ goto err;
+ }
+
+ bsel = max98396_pcm_configs[ret].bsel;
+
+ if (params_rate(params) > max98396_pcm_configs[ret].max_sr) {
+ dev_err(component->dev, "sample rate %d too high",
+ params_rate(params));
+ goto err;
+ }
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ goto err;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ goto err;
+ if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, &reg);
+ if (ret < 0)
+ goto err;
+ if (sampling_rate != (reg & MAX98396_PCM_SR_MASK))
+ update = true;
+ }
+
+ /* GLOBAL_EN OFF prior to channel size and sampling rate change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ /* set channel size */
+ regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_PCM_SR_MASK, sampling_rate);
+
+ /* set sampling rate of IV */
+ if (max98396->interleave_mode &&
+ sampling_rate > MAX98396_PCM_SR_16000)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_IVADC_SR_MASK,
+ (sampling_rate - 3)
+ << MAX98396_IVADC_SR_SHIFT);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_IVADC_SR_MASK,
+ sampling_rate << MAX98396_IVADC_SR_SHIFT);
+
+ if (bsel)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+
+err:
+ return -EINVAL;
+}
+
+static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+ int bsel;
+ unsigned int chan_sz = 0;
+ int ret, status;
+ int reg;
+ bool update = false;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98396->tdm_mode = false;
+ else
+ max98396->tdm_mode = true;
+
+ /* BCLK configuration */
+ ret = max98396_pcm_config_index(slots, slots, slot_width);
+ if (ret < 0) {
+ dev_err(component->dev, "no TDM config for %d slots %d bits\n",
+ slots, slot_width);
+ return -EINVAL;
+ }
+
+ bsel = max98396_pcm_configs[ret].bsel;
+ max98396->tdm_max_samplerate = max98396_pcm_configs[ret].max_sr;
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "slot width %d unsupported\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (bsel != (reg & MAX98396_PCM_CLK_SETUP_BSEL_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK))
+ update = true;
+ }
+
+ /* GLOBAL_EN OFF prior to channel size and BCLK per LRCLK change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+ } else {
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+ }
+
+ /* Tx slot Hi-Z configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_write(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ ~tx_mask & 0xFF);
+ regmap_write(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ (~tx_mask & 0xFF00) >> 8);
+ } else {
+ regmap_write(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ ~tx_mask & 0xFF);
+ regmap_write(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ (~tx_mask & 0xFF00) >> 8);
+ }
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+}
+
+#define MAX98396_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98396_dai_ops = {
+ .set_fmt = max98396_dai_set_fmt,
+ .hw_params = max98396_dai_hw_params,
+ .set_tdm_slot = max98396_dai_tdm_slot,
+};
+
+static int max98396_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ max98396_global_enable_onoff(max98396->regmap, true);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ max98396_global_enable_onoff(max98396->regmap, false);
+
+ max98396->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static bool max98396_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+ case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+ case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+ case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+ case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+ case MAX98396_R2027_THERM_FOLDBACK_EN:
+ case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+ case MAX98396_R2033_NOISEGATE_MODE_EN:
+ case MAX98396_R2038_CLK_MON_CTRL ... MAX98396_R2039_DATA_MON_CTRL:
+ case MAX98396_R203F_ENABLE_CTRLS ... MAX98396_R2053_PCM_TX_HIZ_CTRL_8:
+ case MAX98396_R2055_PCM_RX_SRC1 ... MAX98396_R2056_PCM_RX_SRC2:
+ case MAX98396_R2058_PCM_BYPASS_SRC:
+ case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98396_R205F_PCM_TX_EN:
+ case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+ case MAX98396_R207F_ICC_EN:
+ case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+ case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209A_SPK_EDGE_CTRL:
+ case MAX98396_R209C_SPK_EDGE_CTRL1 ... MAX98396_R20A0_AMP_SUPPLY_CTL:
+ case MAX98396_R20AF_AMP_EN ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+ case MAX98396_R20C7_ADC_CFG:
+ case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98396_R20DF_DHT_EN:
+ case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+ case MAX98396_R20E4_IV_SENSE_PATH_EN
+ ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+ case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+ case MAX98396_R21FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98396_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2000_SW_RESET:
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2041_PCM_MODE_CFG:
+ case MAX98396_R20B6_ADC_PVDD_READBACK_MSB
+ ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+ case MAX98396_R20E5_BPE_STATE:
+ case MAX98396_R2109_BPE_LOW_STATE
+ ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210F_GLOBAL_EN:
+ case MAX98396_R21FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98397_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+ case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+ case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+ case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+ case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+ case MAX98396_R2027_THERM_FOLDBACK_EN:
+ case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+ case MAX98396_R2033_NOISEGATE_MODE_EN:
+ case MAX98396_R2038_CLK_MON_CTRL ... MAX98397_R203A_SPK_MON_THRESH:
+ case MAX98396_R203F_ENABLE_CTRLS ... MAX98397_R2054_PCM_TX_HIZ_CTRL_8:
+ case MAX98397_R2056_PCM_RX_SRC1... MAX98396_R2058_PCM_BYPASS_SRC:
+ case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98397_R2060_PCM_TX_SUPPLY_SEL:
+ case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+ case MAX98396_R207F_ICC_EN:
+ case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+ case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209F_BYPASS_PATH_CFG:
+ case MAX98396_R20AF_AMP_EN ... MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE:
+ case MAX98396_R20C7_ADC_CFG:
+ case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98396_R20DF_DHT_EN:
+ case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+ case MAX98396_R20E4_IV_SENSE_PATH_EN
+ ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+ case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+ case MAX98397_R22FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98397_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2041_PCM_MODE_CFG:
+ case MAX98397_R20B7_ADC_PVDD_READBACK_MSB
+ ... MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB:
+ case MAX98396_R20E5_BPE_STATE:
+ case MAX98396_R2109_BPE_LOW_STATE
+ ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210F_GLOBAL_EN:
+ case MAX98397_R22FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98396_op_mod_text[] = {
+ "DG", "PVDD", "VBAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_op_mod_enum,
+ MAX98396_R2098_SPK_CLS_DG_MODE,
+ 0, max98396_op_mod_text);
+
+static DECLARE_TLV_DB_SCALE(max98396_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98396_spk_tlv,
+ 0, 0x11, TLV_DB_SCALE_ITEM(400, 100, 0),
+);
+static DECLARE_TLV_DB_RANGE(max98397_digital_tlv,
+ 0, 0x4A, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ 0x4B, 0xFF, TLV_DB_SCALE_ITEM(-9000, 50, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98397_spk_tlv,
+ 0, 0x15, TLV_DB_SCALE_ITEM(600, 100, 0),
+);
+
+static int max98396_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ int reg, val;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ reg = MAX98396_R2055_PCM_RX_SRC1;
+ else
+ reg = MAX98397_R2056_PCM_RX_SRC1;
+
+ regmap_read(max98396->regmap, reg, &val);
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int max98396_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ int reg, val;
+ int change;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ reg = MAX98396_R2055_PCM_RX_SRC1;
+ else
+ reg = MAX98397_R2056_PCM_RX_SRC1;
+
+ change = snd_soc_component_test_bits(component, reg,
+ MAX98396_PCM_RX_MASK, val);
+
+ if (change)
+ regmap_update_bits(max98396->regmap, reg,
+ MAX98396_PCM_RX_MASK, val);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const max98396_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static SOC_ENUM_SINGLE_DECL(dai_sel_enum, SND_SOC_NOPM, 0,
+ max98396_switch_text);
+
+static const struct snd_kcontrol_new max98396_dai_mux =
+ SOC_DAPM_ENUM_EXT("DAI Sel Mux", dai_sel_enum,
+ max98396_mux_get, max98396_mux_put);
+
+static const struct snd_kcontrol_new max98396_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98396_R205F_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget max98396_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98396_R20AF_AMP_EN, 0, 0, max98396_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98396_dai_mux),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98396_R20E4_IV_SENSE_PATH_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98396_R20E4_IV_SENSE_PATH_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98396_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+ SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static const char * const max98396_thermal_thresh_text[] = {
+ "50C", "51C", "52C", "53C", "54C", "55C", "56C", "57C",
+ "58C", "59C", "60C", "61C", "62C", "63C", "64C", "65C",
+ "66C", "67C", "68C", "69C", "70C", "71C", "72C", "73C",
+ "74C", "75C", "76C", "77C", "78C", "79C", "80C", "81C",
+ "82C", "83C", "84C", "85C", "86C", "87C", "88C", "89C",
+ "90C", "91C", "92C", "93C", "94C", "95C", "96C", "97C",
+ "98C", "99C", "100C", "101C", "102C", "103C", "104C", "105C",
+ "106C", "107C", "108C", "109C", "110C", "111C", "112C", "113C",
+ "114C", "115C", "116C", "117C", "118C", "119C", "120C", "121C",
+ "122C", "123C", "124C", "125C", "126C", "127C", "128C", "129C",
+ "130C", "131C", "132C", "133C", "134C", "135C", "136C", "137C",
+ "138C", "139C", "140C", "141C", "142C", "143C", "144C", "145C",
+ "146C", "147C", "148C", "149C", "150C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh1_enum,
+ MAX98396_R2020_THERM_WARN_THRESH, 0,
+ max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh2_enum,
+ MAX98396_R2021_THERM_WARN_THRESH2, 0,
+ max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_shdn_thresh_enum,
+ MAX98396_R2022_THERM_SHDN_THRESH, 0,
+ max98396_thermal_thresh_text);
+
+static const char * const max98396_thermal_hyteresis_text[] = {
+ "2C", "5C", "7C", "10C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_hysteresis_enum,
+ MAX98396_R2023_THERM_HYSTERESIS, 0,
+ max98396_thermal_hyteresis_text);
+
+static const char * const max98396_foldback_slope_text[] = {
+ "0.25", "0.5", "1.0", "2.0"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope1_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_SLOPE1_SHIFT,
+ max98396_foldback_slope_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope2_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_SLOPE2_SHIFT,
+ max98396_foldback_slope_text);
+
+static const char * const max98396_foldback_reltime_text[] = {
+ "3ms", "10ms", "100ms", "300ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_reltime_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_REL_SHIFT,
+ max98396_foldback_reltime_text);
+
+static const char * const max98396_foldback_holdtime_text[] = {
+ "0ms", "20ms", "40ms", "80ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_holdtime_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_HOLD_SHIFT,
+ max98396_foldback_holdtime_text);
+
+static int max98396_adc_value_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u8 val[2];
+ int reg = mc->reg;
+
+ /* ADC value is not available if the device is powered down */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ goto exit;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98397) {
+ switch (mc->reg) {
+ case MAX98396_R20B6_ADC_PVDD_READBACK_MSB:
+ reg = MAX98397_R20B7_ADC_PVDD_READBACK_MSB;
+ break;
+ case MAX98396_R20B8_ADC_VBAT_READBACK_MSB:
+ reg = MAX98397_R20B9_ADC_VBAT_READBACK_MSB;
+ break;
+ case MAX98396_R20BA_ADC_TEMP_READBACK_MSB:
+ reg = MAX98397_R20BB_ADC_TEMP_READBACK_MSB;
+ break;
+ default:
+ goto exit;
+ }
+ }
+
+ ret = regmap_raw_read(max98396->regmap, reg, &val, 2);
+ if (ret)
+ goto exit;
+
+ /* ADC readback bits[8:0] rearrangement */
+ ucontrol->value.integer.value[0] = (val[0] << 1) | (val[1] & 1);
+ return 0;
+
+exit:
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static const struct snd_kcontrol_new max98396_snd_controls[] = {
+ /* Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+ 0, 0x7F, 1, max98396_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+ 0, 0x11, 0, max98396_spk_tlv),
+ /* Volume Ramp Up/Down Enable*/
+ SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+ /* Clock Monitor Enable */
+ SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+ /* Speaker Safe Mode Enable */
+ SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+ /* Wideband Filter Enable */
+ SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+ /* Dynamic Headroom Tracking */
+ SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+ /* Brownout Protection Engine */
+ SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+ SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+ /* Bypass Path Enable */
+ SOC_SINGLE("Bypass Path Switch",
+ MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+ /* Speaker Operation Mode */
+ SOC_ENUM("OP Mode", max98396_op_mod_enum),
+ /* Auto Restart functions */
+ SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+ SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_OVC_RESTART_SHFT, 1, 0),
+ /* Thermal Threshold */
+ SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+ SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+ SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+ SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+ SOC_SINGLE("THERM Foldback Switch",
+ MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+ SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+ SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+ SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+ SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+ /* ADC */
+ SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+};
+
+static const struct snd_kcontrol_new max98397_snd_controls[] = {
+ /* Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+ 0, 0xFF, 1, max98397_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+ 0, 0x15, 0, max98397_spk_tlv),
+ /* Volume Ramp Up/Down Enable*/
+ SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+ /* Clock Monitor Enable */
+ SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+ /* Speaker Safe Mode Enable */
+ SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+ /* Wideband Filter Enable */
+ SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+ /* Dynamic Headroom Tracking */
+ SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+ /* Brownout Protection Engine */
+ SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+ SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+ /* Bypass Path Enable */
+ SOC_SINGLE("Bypass Path Switch",
+ MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+ /* Speaker Operation Mode */
+ SOC_ENUM("OP Mode", max98396_op_mod_enum),
+ /* Auto Restart functions */
+ SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+ SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_OVC_RESTART_SHFT, 1, 0),
+ /* Thermal Threshold */
+ SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+ SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+ SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+ SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+ SOC_SINGLE("THERM Foldback Switch",
+ MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+ SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+ SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+ SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+ SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+ /* ADC */
+ SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+};
+
+static const struct snd_soc_dapm_route max98396_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+};
+
+static struct snd_soc_dai_driver max98396_dai[] = {
+ {
+ .name = "max98396-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .ops = &max98396_dai_ops,
+ }
+};
+
+static struct snd_soc_dai_driver max98397_dai[] = {
+ {
+ .name = "max98397-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .ops = &max98396_dai_ops,
+ }
+};
+
+static void max98396_reset(struct max98396_priv *max98396, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_write(max98396->regmap,
+ MAX98396_R2000_SW_RESET, 1);
+ if (ret)
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(5000, 6000);
+ /* Software Reset Verification */
+ ret = regmap_read(max98396->regmap,
+ GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+ return;
+ }
+ count++;
+ }
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+
+static int max98396_probe(struct snd_soc_component *component)
+{
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98396_reset(max98396, component->dev);
+
+ /* L/R mix configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_write(max98396->regmap,
+ MAX98396_R2055_PCM_RX_SRC1, 0x02);
+ regmap_write(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2, 0x10);
+ } else {
+ regmap_write(max98396->regmap,
+ MAX98397_R2056_PCM_RX_SRC1, 0x02);
+ regmap_write(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2, 0x10);
+ }
+ /* Supply control */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20A0_AMP_SUPPLY_CTL,
+ MAX98396_AMP_SUPPLY_NOVBAT,
+ (max98396->vbat == NULL) ?
+ MAX98396_AMP_SUPPLY_NOVBAT : 0);
+ /* Enable DC blocker */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2092_AMP_DSP_CFG, 1, 1);
+ /* Enable IV Monitor DC blocker */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_MASK,
+ MAX98396_IV_SENSE_DCBLK_EN_MASK);
+ /* Configure default data output sources */
+ regmap_write(max98396->regmap,
+ MAX98396_R205D_PCM_TX_SRC_EN, 3);
+ /* Enable Wideband Filter */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2092_AMP_DSP_CFG, 0x40, 0x40);
+ /* Enable IV Wideband Filter */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20E0_IV_SENSE_PATH_CFG, 8, 8);
+
+ /* Enable Bypass Source */
+ regmap_write(max98396->regmap,
+ MAX98396_R2058_PCM_BYPASS_SRC,
+ max98396->bypass_slot);
+ /* Voltage, current slot configuration */
+ regmap_write(max98396->regmap,
+ MAX98396_R2044_PCM_TX_CTRL_1,
+ max98396->v_slot);
+ regmap_write(max98396->regmap,
+ MAX98396_R2045_PCM_TX_CTRL_2,
+ max98396->i_slot);
+ regmap_write(max98396->regmap,
+ MAX98396_R204A_PCM_TX_CTRL_7,
+ max98396->spkfb_slot);
+
+ if (max98396->v_slot < 8)
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->v_slot, 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->v_slot, 0);
+ else
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->v_slot - 8), 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->v_slot - 8), 0);
+
+ if (max98396->i_slot < 8)
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->i_slot, 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->i_slot, 0);
+ else
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->i_slot - 8), 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->i_slot - 8), 0);
+
+ /* Set interleave mode */
+ if (max98396->interleave_mode)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98396_PCM_TX_CH_INTERLEAVE_MASK);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_MASK,
+ MAX98396_CLK_MON_AUTO_RESTART_MASK);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_DMON_STUCK_EN_MASK,
+ max98396->dmon_stuck_enable ?
+ MAX98396_CTRL_DMON_STUCK_EN_MASK : 0);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_DMON_MAG_EN_MASK,
+ max98396->dmon_mag_enable ?
+ MAX98396_CTRL_DMON_MAG_EN_MASK : 0);
+
+ switch (max98396->dmon_duration) {
+ case 64:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 0);
+ break;
+ case 256:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 1);
+ break;
+ case 1024:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 2);
+ break;
+ case 4096:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_DURATION_MASK, 3);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMON duration %d\n",
+ max98396->dmon_duration);
+ }
+
+ switch (max98396->dmon_stuck_threshold) {
+ case 15:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 0 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ case 13:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 1 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ case 22:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 2 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ case 9:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ 3 << MAX98396_DMON_STUCK_THRESH_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMON stuck threshold %d\n",
+ max98396->dmon_stuck_threshold);
+ }
+
+ switch (max98396->dmon_mag_threshold) {
+ case 2 ... 5:
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2039_DATA_MON_CTRL,
+ MAX98396_DMON_STUCK_THRESH_MASK,
+ (5 - max98396->dmon_mag_threshold)
+ << MAX98396_DMON_MAG_THRESH_SHIFT);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMON magnitude threshold %d\n",
+ max98396->dmon_mag_threshold);
+ }
+
+ /* Speaker Amplifier PCM RX Enable by default */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R205E_PCM_RX_EN,
+ MAX98396_PCM_RX_EN_MASK, 1);
+
+ return 0;
+}
+
+static int max98396_suspend(struct device *dev)
+{
+ struct max98396_priv *max98396 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98396->regmap, true);
+ regcache_mark_dirty(max98396->regmap);
+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (max98396->pvdd)
+ regulator_disable(max98396->pvdd);
+
+ if (max98396->vbat)
+ regulator_disable(max98396->vbat);
+
+ return 0;
+}
+
+static int max98396_resume(struct device *dev)
+{
+ struct max98396_priv *max98396 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0)
+ return ret;
+
+ if (max98396->pvdd) {
+ ret = regulator_enable(max98396->pvdd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (max98396->vbat) {
+ ret = regulator_enable(max98396->vbat);
+ if (ret < 0)
+ return ret;
+ }
+
+ regcache_cache_only(max98396->regmap, false);
+ max98396_reset(max98396, dev);
+ regcache_sync(max98396->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops max98396_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98396 = {
+ .probe = max98396_probe,
+ .controls = max98396_snd_controls,
+ .num_controls = ARRAY_SIZE(max98396_snd_controls),
+ .dapm_widgets = max98396_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets),
+ .dapm_routes = max98396_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98396_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98397 = {
+ .probe = max98396_probe,
+ .controls = max98397_snd_controls,
+ .num_controls = ARRAY_SIZE(max98397_snd_controls),
+ .dapm_widgets = max98396_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets),
+ .dapm_routes = max98396_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98396_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98396_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98396_R21FF_REVISION_ID,
+ .reg_defaults = max98396_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98396_reg),
+ .readable_reg = max98396_readable_register,
+ .volatile_reg = max98396_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config max98397_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98397_R22FF_REVISION_ID,
+ .reg_defaults = max98397_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98397_reg),
+ .readable_reg = max98397_readable_register,
+ .volatile_reg = max98397_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98396_read_device_property(struct device *dev,
+ struct max98396_priv *max98396)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value))
+ max98396->v_slot = value & 0xF;
+ else
+ max98396->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "adi,imon-slot-no", &value))
+ max98396->i_slot = value & 0xF;
+ else
+ max98396->i_slot = 1;
+
+ if (!device_property_read_u32(dev, "adi,spkfb-slot-no", &value))
+ max98396->spkfb_slot = value & 0xF;
+ else
+ max98396->spkfb_slot = 2;
+
+ if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value))
+ max98396->bypass_slot = value & 0xF;
+ else
+ max98396->bypass_slot = 0;
+
+ max98396->dmon_stuck_enable =
+ device_property_read_bool(dev, "adi,dmon-stuck-enable");
+
+ if (!device_property_read_u32(dev, "adi,dmon-stuck-threshold-bits", &value))
+ max98396->dmon_stuck_threshold = value;
+ else
+ max98396->dmon_stuck_threshold = 15;
+
+ max98396->dmon_mag_enable =
+ device_property_read_bool(dev, "adi,dmon-magnitude-enable");
+
+ if (!device_property_read_u32(dev, "adi,dmon-magnitude-threshold-bits", &value))
+ max98396->dmon_mag_threshold = value;
+ else
+ max98396->dmon_mag_threshold = 5;
+
+ if (!device_property_read_u32(dev, "adi,dmon-duration-ms", &value))
+ max98396->dmon_duration = value;
+ else
+ max98396->dmon_duration = 64;
+}
+
+static void max98396_core_supplies_disable(void *priv)
+{
+ struct max98396_priv *max98396 = priv;
+
+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+}
+
+static void max98396_supply_disable(void *r)
+{
+ regulator_disable((struct regulator *) r);
+}
+
+static int max98396_i2c_probe(struct i2c_client *i2c)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
+ struct max98396_priv *max98396 = NULL;
+ int i, ret, reg;
+
+ max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL);
+
+ if (!max98396) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98396);
+
+ max98396->device_id = id->driver_data;
+
+ /* regmap initialization */
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ max98396->regmap = devm_regmap_init_i2c(i2c, &max98396_regmap);
+
+ else
+ max98396->regmap = devm_regmap_init_i2c(i2c, &max98397_regmap);
+
+ if (IS_ERR(max98396->regmap)) {
+ ret = PTR_ERR(max98396->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Obtain regulator supplies */
+ for (i = 0; i < MAX98396_NUM_CORE_SUPPLIES; i++)
+ max98396->core_supplies[i].supply = max98396_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ max98396->vbat = devm_regulator_get_optional(&i2c->dev, "vbat");
+ if (IS_ERR(max98396->vbat)) {
+ if (PTR_ERR(max98396->vbat) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98396->vbat = NULL;
+ }
+
+ max98396->pvdd = devm_regulator_get_optional(&i2c->dev, "pvdd");
+ if (IS_ERR(max98396->pvdd)) {
+ if (PTR_ERR(max98396->pvdd) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98396->pvdd = NULL;
+ }
+
+ ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Unable to enable core supplies: %d", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, max98396_core_supplies_disable,
+ max98396);
+ if (ret < 0)
+ return ret;
+
+ if (max98396->pvdd) {
+ ret = regulator_enable(max98396->pvdd);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev,
+ max98396_supply_disable,
+ max98396->pvdd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (max98396->vbat) {
+ ret = regulator_enable(max98396->vbat);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev,
+ max98396_supply_disable,
+ max98396->vbat);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* update interleave mode info */
+ if (device_property_read_bool(&i2c->dev, "adi,interleave_mode"))
+ max98396->interleave_mode = true;
+ else
+ max98396->interleave_mode = false;
+
+ /* voltage/current slot & gpio configuration */
+ max98396_read_device_property(&i2c->dev, max98396);
+
+ /* Reset the Device */
+ max98396->reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98396->reset_gpio)) {
+ ret = PTR_ERR(max98396->reset_gpio);
+ dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+ return ret;
+ }
+
+ if (max98396->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(max98396->reset_gpio, 0);
+ /* Wait for the hw reset done */
+ usleep_range(5000, 6000);
+ }
+
+ ret = regmap_read(max98396->regmap,
+ GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s: failed to read revision of the device.\n", id->name);
+ return ret;
+ }
+ dev_info(&i2c->dev, "%s revision ID: 0x%02X\n", id->name, reg);
+
+ /* codec registration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98396,
+ max98396_dai,
+ ARRAY_SIZE(max98396_dai));
+ else
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98397,
+ max98397_dai,
+ ARRAY_SIZE(max98397_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98396_i2c_id[] = {
+ { "max98396", CODEC_TYPE_MAX98396},
+ { "max98397", CODEC_TYPE_MAX98397},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98396_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98396_of_match[] = {
+ { .compatible = "adi,max98396", },
+ { .compatible = "adi,max98397", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98396_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98396_acpi_match[] = {
+ { "ADS8396", 0 },
+ { "ADS8397", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98396_acpi_match);
+#endif
+
+static struct i2c_driver max98396_i2c_driver = {
+ .driver = {
+ .name = "max98396",
+ .of_match_table = of_match_ptr(max98396_of_match),
+ .acpi_match_table = ACPI_PTR(max98396_acpi_match),
+ .pm = pm_ptr(&max98396_pm),
+ },
+ .probe = max98396_i2c_probe,
+ .id_table = max98396_i2c_id,
+};
+
+module_i2c_driver(max98396_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98396 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h
new file mode 100644
index 000000000000..d396aa3e698b
--- /dev/null
+++ b/sound/soc/codecs/max98396.h
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * max98396.h -- MAX98396 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022, Analog Devices Inc.
+ */
+
+#ifndef _MAX98396_H
+#define _MAX98396_H
+
+#define MAX98396_R2000_SW_RESET 0x2000
+#define MAX98396_R2001_INT_RAW1 0x2001
+#define MAX98396_R2002_INT_RAW2 0x2002
+#define MAX98396_R2003_INT_RAW3 0x2003
+#define MAX98396_R2004_INT_RAW4 0x2004
+#define MAX98396_R2006_INT_STATE1 0x2006
+#define MAX98396_R2007_INT_STATE2 0x2007
+#define MAX98396_R2008_INT_STATE3 0x2008
+#define MAX98396_R2009_INT_STATE4 0x2009
+#define MAX98396_R200B_INT_FLAG1 0x200B
+#define MAX98396_R200C_INT_FLAG2 0x200C
+#define MAX98396_R200D_INT_FLAG3 0x200D
+#define MAX98396_R200E_INT_FLAG4 0x200E
+#define MAX98396_R2010_INT_EN1 0x2010
+#define MAX98396_R2011_INT_EN2 0x2011
+#define MAX98396_R2012_INT_EN3 0x2012
+#define MAX98396_R2013_INT_EN4 0x2013
+#define MAX98396_R2015_INT_FLAG_CLR1 0x2015
+#define MAX98396_R2016_INT_FLAG_CLR2 0x2016
+#define MAX98396_R2017_INT_FLAG_CLR3 0x2017
+#define MAX98396_R2018_INT_FLAG_CLR4 0x2018
+#define MAX98396_R201F_IRQ_CTRL 0x201F
+#define MAX98396_R2020_THERM_WARN_THRESH 0x2020
+#define MAX98396_R2021_THERM_WARN_THRESH2 0x2021
+#define MAX98396_R2022_THERM_SHDN_THRESH 0x2022
+#define MAX98396_R2023_THERM_HYSTERESIS 0x2023
+#define MAX98396_R2024_THERM_FOLDBACK_SET 0x2024
+#define MAX98396_R2027_THERM_FOLDBACK_EN 0x2027
+#define MAX98396_R2030_NOISEGATE_MODE_CTRL 0x2030
+#define MAX98396_R2033_NOISEGATE_MODE_EN 0x2033
+#define MAX98396_R2038_CLK_MON_CTRL 0x2038
+#define MAX98396_R2039_DATA_MON_CTRL 0x2039
+#define MAX98396_R203F_ENABLE_CTRLS 0x203F
+#define MAX98396_R2040_PIN_CFG 0x2040
+#define MAX98396_R2041_PCM_MODE_CFG 0x2041
+#define MAX98396_R2042_PCM_CLK_SETUP 0x2042
+#define MAX98396_R2043_PCM_SR_SETUP 0x2043
+#define MAX98396_R2044_PCM_TX_CTRL_1 0x2044
+#define MAX98396_R2045_PCM_TX_CTRL_2 0x2045
+#define MAX98396_R2046_PCM_TX_CTRL_3 0x2046
+#define MAX98396_R2047_PCM_TX_CTRL_4 0x2047
+#define MAX98396_R2048_PCM_TX_CTRL_5 0x2048
+#define MAX98396_R2049_PCM_TX_CTRL_6 0x2049
+#define MAX98396_R204A_PCM_TX_CTRL_7 0x204A
+#define MAX98396_R204B_PCM_TX_CTRL_8 0x204B
+#define MAX98396_R204C_PCM_TX_HIZ_CTRL_1 0x204C
+#define MAX98396_R204D_PCM_TX_HIZ_CTRL_2 0x204D
+#define MAX98396_R204E_PCM_TX_HIZ_CTRL_3 0x204E
+#define MAX98396_R204F_PCM_TX_HIZ_CTRL_4 0x204F
+#define MAX98396_R2050_PCM_TX_HIZ_CTRL_5 0x2050
+#define MAX98396_R2051_PCM_TX_HIZ_CTRL_6 0x2051
+#define MAX98396_R2052_PCM_TX_HIZ_CTRL_7 0x2052
+#define MAX98396_R2053_PCM_TX_HIZ_CTRL_8 0x2053
+#define MAX98396_R2055_PCM_RX_SRC1 0x2055
+#define MAX98396_R2056_PCM_RX_SRC2 0x2056
+#define MAX98396_R2058_PCM_BYPASS_SRC 0x2058
+#define MAX98396_R205D_PCM_TX_SRC_EN 0x205D
+#define MAX98396_R205E_PCM_RX_EN 0x205E
+#define MAX98396_R205F_PCM_TX_EN 0x205F
+#define MAX98396_R2070_ICC_RX_EN_A 0x2070
+#define MAX98396_R2071_ICC_RX_EN_B 0x2071
+#define MAX98396_R2072_ICC_TX_CTRL 0x2072
+#define MAX98396_R207F_ICC_EN 0x207F
+#define MAX98396_R2083_TONE_GEN_DC_CFG 0x2083
+#define MAX98396_R2084_TONE_GEN_DC_LVL1 0x2084
+#define MAX98396_R2085_TONE_GEN_DC_LVL2 0x2085
+#define MAX98396_R2086_TONE_GEN_DC_LVL3 0x2086
+#define MAX98396_R208F_TONE_GEN_EN 0x208F
+#define MAX98396_R2090_AMP_VOL_CTRL 0x2090
+#define MAX98396_R2091_AMP_PATH_GAIN 0x2091
+#define MAX98396_R2092_AMP_DSP_CFG 0x2092
+#define MAX98396_R2093_SSM_CFG 0x2093
+#define MAX98396_R2094_SPK_CLS_DG_THRESH 0x2094
+#define MAX98396_R2095_SPK_CLS_DG_HDR 0x2095
+#define MAX98396_R2096_SPK_CLS_DG_HOLD_TIME 0x2096
+#define MAX98396_R2097_SPK_CLS_DG_DELAY 0x2097
+#define MAX98396_R2098_SPK_CLS_DG_MODE 0x2098
+#define MAX98396_R2099_SPK_CLS_DG_VBAT_LVL 0x2099
+#define MAX98396_R209A_SPK_EDGE_CTRL 0x209A
+#define MAX98396_R209C_SPK_EDGE_CTRL1 0x209C
+#define MAX98396_R209D_SPK_EDGE_CTRL2 0x209D
+#define MAX98396_R209E_AMP_CLIP_GAIN 0x209E
+#define MAX98396_R209F_BYPASS_PATH_CFG 0x209F
+#define MAX98396_R20A0_AMP_SUPPLY_CTL 0x20A0
+#define MAX98396_R20AF_AMP_EN 0x20AF
+#define MAX98396_R20B0_ADC_SR 0x20B0
+#define MAX98396_R20B1_ADC_PVDD_CFG 0x20B1
+#define MAX98396_R20B2_ADC_VBAT_CFG 0x20B2
+#define MAX98396_R20B3_ADC_THERMAL_CFG 0x20B3
+#define MAX98396_R20B4_ADC_READBACK_CTRL1 0x20B4
+#define MAX98396_R20B5_ADC_READBACK_CTRL2 0x20B5
+#define MAX98396_R20B6_ADC_PVDD_READBACK_MSB 0x20B6
+#define MAX98396_R20B7_ADC_PVDD_READBACK_LSB 0x20B7
+#define MAX98396_R20B8_ADC_VBAT_READBACK_MSB 0x20B8
+#define MAX98396_R20B9_ADC_VBAT_READBACK_LSB 0x20B9
+#define MAX98396_R20BA_ADC_TEMP_READBACK_MSB 0x20BA
+#define MAX98396_R20BB_ADC_TEMP_READBACK_LSB 0x20BB
+#define MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB 0x20BC
+#define MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB 0x20BD
+#define MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB 0x20BE
+#define MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB 0x20BF
+#define MAX98396_R20C7_ADC_CFG 0x20C7
+#define MAX98396_R20D0_DHT_CFG1 0x20D0
+#define MAX98396_R20D1_LIMITER_CFG1 0x20D1
+#define MAX98396_R20D2_LIMITER_CFG2 0x20D2
+#define MAX98396_R20D3_DHT_CFG2 0x20D3
+#define MAX98396_R20D4_DHT_CFG3 0x20D4
+#define MAX98396_R20D5_DHT_CFG4 0x20D5
+#define MAX98396_R20D6_DHT_HYSTERESIS_CFG 0x20D6
+#define MAX98396_R20DF_DHT_EN 0x20DF
+#define MAX98396_R20E0_IV_SENSE_PATH_CFG 0x20E0
+#define MAX98396_R20E4_IV_SENSE_PATH_EN 0x20E4
+#define MAX98396_R20E5_BPE_STATE 0x20E5
+#define MAX98396_R20E6_BPE_L3_THRESH_MSB 0x20E6
+#define MAX98396_R20E7_BPE_L3_THRESH_LSB 0x20E7
+#define MAX98396_R20E8_BPE_L2_THRESH_MSB 0x20E8
+#define MAX98396_R20E9_BPE_L2_THRESH_LSB 0x20E9
+#define MAX98396_R20EA_BPE_L1_THRESH_MSB 0x20EA
+#define MAX98396_R20EB_BPE_L1_THRESH_LSB 0x20EB
+#define MAX98396_R20EC_BPE_L0_THRESH_MSB 0x20EC
+#define MAX98396_R20ED_BPE_L0_THRESH_LSB 0x20ED
+#define MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME 0x20EE
+#define MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME 0x20EF
+#define MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME 0x20F0
+#define MAX98396_R20F1_BPE_L0_HOLD_TIME 0x20F1
+#define MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP 0x20F2
+#define MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP 0x20F3
+#define MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP 0x20F4
+#define MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP 0x20F5
+#define MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN 0x20F6
+#define MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN 0x20F7
+#define MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN 0x20F8
+#define MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN 0x20F9
+#define MAX98396_R20FA_BPE_L3_ATT_REL_RATE 0x20FA
+#define MAX98396_R20FB_BPE_L2_ATT_REL_RATE 0x20FB
+#define MAX98396_R20FC_BPE_L1_ATT_REL_RATE 0x20FC
+#define MAX98396_R20FD_BPE_L0_ATT_REL_RATE 0x20FD
+#define MAX98396_R20FE_BPE_L3_LIMITER_CFG 0x20FE
+#define MAX98396_R20FF_BPE_L2_LIMITER_CFG 0x20FF
+#define MAX98396_R2100_BPE_L1_LIMITER_CFG 0x2100
+#define MAX98396_R2101_BPE_L0_LIMITER_CFG 0x2101
+#define MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE 0x2102
+#define MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE 0x2103
+#define MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE 0x2104
+#define MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE 0x2105
+#define MAX98396_R2106_BPE_THRESH_HYSTERESIS 0x2106
+#define MAX98396_R2107_BPE_INFINITE_HOLD_CLR 0x2107
+#define MAX98396_R2108_BPE_SUPPLY_SRC 0x2108
+#define MAX98396_R2109_BPE_LOW_STATE 0x2109
+#define MAX98396_R210A_BPE_LOW_GAIN 0x210A
+#define MAX98396_R210B_BPE_LOW_LIMITER 0x210B
+#define MAX98396_R210D_BPE_EN 0x210D
+#define MAX98396_R210E_AUTO_RESTART 0x210E
+#define MAX98396_R210F_GLOBAL_EN 0x210F
+#define MAX98396_R21FF_REVISION_ID 0x21FF
+
+/* MAX98927 Registers */
+#define MAX98397_R203A_SPK_MON_THRESH 0x203A
+#define MAX98397_R204C_PCM_TX_CTRL_9 0x204C
+#define MAX98397_R204D_PCM_TX_HIZ_CTRL_1 0x204D
+#define MAX98397_R204E_PCM_TX_HIZ_CTRL_2 0x204E
+#define MAX98397_R204F_PCM_TX_HIZ_CTRL_3 0x204F
+#define MAX98397_R2050_PCM_TX_HIZ_CTRL_4 0x2050
+#define MAX98397_R2051_PCM_TX_HIZ_CTRL_5 0x2051
+#define MAX98397_R2052_PCM_TX_HIZ_CTRL_6 0x2052
+#define MAX98397_R2053_PCM_TX_HIZ_CTRL_7 0x2053
+#define MAX98397_R2054_PCM_TX_HIZ_CTRL_8 0x2054
+#define MAX98397_R2056_PCM_RX_SRC1 0x2056
+#define MAX98397_R2057_PCM_RX_SRC2 0x2057
+#define MAX98397_R2060_PCM_TX_SUPPLY_SEL 0x2060
+#define MAX98397_R209B_SPK_PATH_WB_ONLY 0x209B
+#define MAX98397_R20B4_ADC_VDDH_CFG 0x20B4
+#define MAX98397_R20B5_ADC_READBACK_CTRL1 0x20B5
+#define MAX98397_R20B6_ADC_READBACK_CTRL2 0x20B6
+#define MAX98397_R20B7_ADC_PVDD_READBACK_MSB 0x20B7
+#define MAX98397_R20B8_ADC_PVDD_READBACK_LSB 0x20B8
+#define MAX98397_R20B9_ADC_VBAT_READBACK_MSB 0x20B9
+#define MAX98397_R20BA_ADC_VBAT_READBACK_LSB 0x20BA
+#define MAX98397_R20BB_ADC_TEMP_READBACK_MSB 0x20BB
+#define MAX98397_R20BC_ADC_TEMP_READBACK_LSB 0x20BC
+#define MAX98397_R20BD_ADC_VDDH__READBACK_MSB 0x20BD
+#define MAX98397_R20BE_ADC_VDDH_READBACK_LSB 0x20BE
+#define MAX98397_R20BF_ADC_LO_PVDD_READBACK_MSB 0x20BF
+#define MAX98397_R20C0_ADC_LO_PVDD_READBACK_LSB 0x20C0
+#define MAX98397_R20C1_ADC_LO_VBAT_READBACK_MSB 0x20C1
+#define MAX98397_R20C2_ADC_LO_VBAT_READBACK_LSB 0x20C2
+#define MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB 0x20C3
+#define MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB 0x20C4
+#define MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE 0x20C5
+#define MAX98397_R22FF_REVISION_ID 0x22FF
+
+#define GET_REG_ADDR_REV_ID(x)\
+ ((x) > 0 ? MAX98397_R22FF_REVISION_ID : MAX98396_R21FF_REVISION_ID)
+
+/* MAX98396_R2024_THERM_FOLDBACK_SET */
+#define MAX98396_THERM_FB_SLOPE1_SHIFT (0)
+#define MAX98396_THERM_FB_SLOPE2_SHIFT (2)
+#define MAX98396_THERM_FB_REL_SHIFT (4)
+#define MAX98396_THERM_FB_HOLD_SHIFT (6)
+
+/* MAX98396_R2038_CLK_MON_CTRL */
+#define MAX98396_CLK_MON_AUTO_RESTART_MASK (0x1 << 0)
+#define MAX98396_CLK_MON_AUTO_RESTART_SHIFT (0)
+
+/* MAX98396_R2039_DATA_MON_CTRL */
+#define MAX98396_DMON_MAG_THRESH_SHIFT (4)
+#define MAX98396_DMON_MAG_THRESH_MASK (0x3 << MAX98396_DMON_MAG_THRESH_SHIFT)
+#define MAX98396_DMON_STUCK_THRESH_SHIFT (2)
+#define MAX98396_DMON_STUCK_THRESH_MASK (0x3 << MAX98396_DMON_STUCK_THRESH_SHIFT)
+#define MAX98396_DMON_DURATION_MASK (0x3)
+
+/* MAX98396_R203F_ENABLE_CTRLS */
+#define MAX98396_CTRL_CMON_EN_SHIFT (0)
+#define MAX98396_CTRL_DMON_STUCK_EN_MASK (0x1 << 1)
+#define MAX98396_CTRL_DMON_MAG_EN_MASK (0x1 << 2)
+
+/* MAX98396_R2041_PCM_MODE_CFG */
+#define MAX98396_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98396_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98396_PCM_FORMAT_I2S (0x0 << 3)
+#define MAX98396_PCM_FORMAT_LJ (0x1 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE0 (0x3 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE1 (0x4 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE2 (0x5 << 3)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_LRCLKEDGE (0x1 << 1)
+
+/* MAX98396_R2042_PCM_CLK_SETUP */
+#define MAX98396_PCM_MODE_CFG_BCLKEDGE (0x1 << 4)
+#define MAX98396_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+#define MAX98396_PCM_BCLKEDGE_BSEL_MASK (0x1F)
+
+/* MAX98396_R2043_PCM_SR_SETUP */
+#define MAX98396_PCM_SR_SHIFT (0)
+#define MAX98396_IVADC_SR_SHIFT (4)
+#define MAX98396_PCM_SR_MASK (0xF << MAX98396_PCM_SR_SHIFT)
+#define MAX98396_IVADC_SR_MASK (0xF << MAX98396_IVADC_SR_SHIFT)
+#define MAX98396_PCM_SR_8000 (0)
+#define MAX98396_PCM_SR_11025 (1)
+#define MAX98396_PCM_SR_12000 (2)
+#define MAX98396_PCM_SR_16000 (3)
+#define MAX98396_PCM_SR_22050 (4)
+#define MAX98396_PCM_SR_24000 (5)
+#define MAX98396_PCM_SR_32000 (6)
+#define MAX98396_PCM_SR_44100 (7)
+#define MAX98396_PCM_SR_48000 (8)
+#define MAX98396_PCM_SR_88200 (9)
+#define MAX98396_PCM_SR_96000 (10)
+#define MAX98396_PCM_SR_176400 (11)
+#define MAX98396_PCM_SR_192000 (12)
+
+/* MAX98396_R2055_PCM_RX_SRC1 */
+#define MAX98396_PCM_RX_MASK (0x3 << 0)
+
+/* MAX98396_R2056_PCM_RX_SRC2 */
+#define MAX98396_PCM_DMIX_CH1_SHIFT (0xF << 0)
+#define MAX98396_PCM_DMIX_CH0_SRC_MASK (0xF << 0)
+#define MAX98396_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98396_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98396_R205E_PCM_RX_EN */
+#define MAX98396_PCM_RX_EN_MASK (0x1 << 0)
+#define MAX98396_PCM_RX_BYP_EN_MASK (0x1 << 1)
+
+/* MAX98396_R2092_AMP_DSP_CFG */
+#define MAX98396_DSP_SPK_DCBLK_EN_SHIFT (0)
+#define MAX98396_DSP_SPK_DITH_EN_SHIFT (1)
+#define MAX98396_DSP_SPK_INVERT_SHIFT (2)
+#define MAX98396_DSP_SPK_VOL_RMPUP_SHIFT (3)
+#define MAX98396_DSP_SPK_VOL_RMPDN_SHIFT (4)
+#define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5)
+#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6)
+
+/* MAX98396_R20A0_AMP_SUPPLY_CTL */
+#define MAX98396_AMP_SUPPLY_NOVBAT (0x1 << 0)
+
+/* MAX98396_R20E0_IV_SENSE_PATH_CFG */
+#define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0)
+#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0)
+#define MAX98396_IV_SENSE_DITH_EN_SHIFT (2)
+#define MAX98396_IV_SENSE_WB_FLT_EN_SHIFT (3)
+
+/* MAX98396_R210E_AUTO_RESTART_BEHAVIOR */
+#define MAX98396_PVDD_UVLO_RESTART_SHFT (0)
+#define MAX98396_VBAT_UVLO_RESTART_SHFT (1)
+#define MAX98396_THEM_SHDN_RESTART_SHFT (2)
+#define MAX98396_OVC_RESTART_SHFT (3)
+
+enum {
+ CODEC_TYPE_MAX98396,
+ CODEC_TYPE_MAX98397,
+};
+
+#define MAX98396_NUM_CORE_SUPPLIES 3
+
+struct max98396_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data core_supplies[MAX98396_NUM_CORE_SUPPLIES];
+ struct regulator *pvdd, *vbat;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ unsigned int bypass_slot;
+ bool dmon_stuck_enable;
+ unsigned int dmon_stuck_threshold;
+ bool dmon_mag_enable;
+ unsigned int dmon_mag_threshold;
+ unsigned int dmon_duration;
+ bool interleave_mode;
+ bool tdm_mode;
+ int tdm_max_samplerate;
+ int device_id;
+};
+#endif
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index c14a79d026a1..1fcbc64a2771 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max9850.c -- codec driver for max9850
*
@@ -6,13 +7,7 @@
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
*
* Initial development of this code was funded by
- * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.de/
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
+ * MICRONIC Computer Systeme GmbH, https://www.mcsberlin.de/
*/
#include <linux/module.h>
@@ -32,19 +27,6 @@ struct max9850_priv {
unsigned int sysclk;
};
-/* max9850 register cache */
-static const struct reg_default max9850_reg[] = {
- { 2, 0x0c },
- { 3, 0x00 },
- { 4, 0x00 },
- { 5, 0x00 },
- { 6, 0x00 },
- { 7, 0x00 },
- { 8, 0x00 },
- { 9, 0x00 },
- { 10, 0x00 },
-};
-
/* these registers are not used at the moment but provided for the sake of
* completeness */
static bool max9850_volatile_register(struct device *dev, unsigned int reg)
@@ -52,9 +34,9 @@ static bool max9850_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case MAX9850_STATUSA:
case MAX9850_STATUSB:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -130,8 +112,8 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
u64 lrclk_div;
u8 sf, da;
@@ -139,14 +121,14 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
- sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1;
+ sf = (snd_soc_component_read(component, MAX9850_CLOCK) >> 2) + 1;
lrclk_div = (1 << 22);
lrclk_div *= params_rate(params);
lrclk_div *= sf;
do_div(lrclk_div, max9850->sysclk);
- snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
- snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
+ snd_soc_component_write(component, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
+ snd_soc_component_write(component, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
switch (params_width(params)) {
case 16:
@@ -161,7 +143,7 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
+ snd_soc_component_update_bits(component, MAX9850_DIGITAL_AUDIO, 0x3, da);
return 0;
}
@@ -169,16 +151,16 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
/* calculate mclk -> iclk divider */
if (freq <= 13000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x0);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x0);
else if (freq <= 26000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x4);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x4);
else if (freq <= 40000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x8);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x8);
else
return -EINVAL;
@@ -188,15 +170,15 @@ static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 da = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set clock provider for audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
da |= MAX9850_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -234,15 +216,16 @@ static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* set da */
- snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
+ snd_soc_component_write(component, MAX9850_DIGITAL_AUDIO, da);
return 0;
}
-static int max9850_set_bias_level(struct snd_soc_codec *codec,
+static int max9850_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -251,10 +234,10 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max9850->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
@@ -289,33 +272,34 @@ static struct snd_soc_dai_driver max9850_dai = {
.ops = &max9850_dai_ops,
};
-static int max9850_probe(struct snd_soc_codec *codec)
+static int max9850_probe(struct snd_soc_component *component)
{
/* enable zero-detect */
- snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
+ snd_soc_component_update_bits(component, MAX9850_GENERAL_PURPOSE, 1, 1);
/* enable slew-rate control */
- snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
+ snd_soc_component_update_bits(component, MAX9850_VOLUME, 0x40, 0x40);
/* set slew-rate 125ms */
- snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
+ snd_soc_component_update_bits(component, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
- .probe = max9850_probe,
- .set_bias_level = max9850_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = max9850_controls,
- .num_controls = ARRAY_SIZE(max9850_controls),
- .dapm_widgets = max9850_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets),
- .dapm_routes = max9850_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_max9850 = {
+ .probe = max9850_probe,
+ .set_bias_level = max9850_set_bias_level,
+ .controls = max9850_controls,
+ .num_controls = ARRAY_SIZE(max9850_controls),
+ .dapm_widgets = max9850_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets),
+ .dapm_routes = max9850_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int max9850_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max9850_i2c_probe(struct i2c_client *i2c)
{
struct max9850_priv *max9850;
int ret;
@@ -331,19 +315,13 @@ static int max9850_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max9850);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max9850, &max9850_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max9850, &max9850_dai, 1);
return ret;
}
-static int max9850_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id max9850_i2c_id[] = {
- { "max9850", 0 },
+ { "max9850" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
@@ -353,7 +331,6 @@ static struct i2c_driver max9850_i2c_driver = {
.name = "max9850",
},
.probe = max9850_i2c_probe,
- .remove = max9850_i2c_remove,
.id_table = max9850_i2c_id,
};
diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h
index 72b1ddb04b0d..da313640b4bf 100644
--- a/sound/soc/codecs/max9850.h
+++ b/sound/soc/codecs/max9850.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* max9850.h -- codec driver for max9850
*
* Copyright (C) 2011 taskit GmbH
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _MAX9850_H
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
new file mode 100644
index 000000000000..c94142768c81
--- /dev/null
+++ b/sound/soc/codecs/max98504.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2013 - 2014 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+
+#include "max98504.h"
+
+static const char * const max98504_supply_names[] = {
+ "DVDD",
+ "DIOVDD",
+ "PVDD",
+};
+#define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names)
+
+struct max98504_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES];
+ unsigned int pcm_rx_channels;
+ bool brownout_enable;
+ unsigned int brownout_threshold;
+ unsigned int brownout_attenuation;
+ unsigned int brownout_attack_hold;
+ unsigned int brownout_timed_hold;
+ unsigned int brownout_release_rate;
+};
+
+static const struct reg_default max98504_reg_defaults[] = {
+ { 0x01, 0},
+ { 0x02, 0},
+ { 0x03, 0},
+ { 0x04, 0},
+ { 0x10, 0},
+ { 0x11, 0},
+ { 0x12, 0},
+ { 0x13, 0},
+ { 0x14, 0},
+ { 0x15, 0},
+ { 0x16, 0},
+ { 0x17, 0},
+ { 0x18, 0},
+ { 0x19, 0},
+ { 0x1A, 0},
+ { 0x20, 0},
+ { 0x21, 0},
+ { 0x22, 0},
+ { 0x23, 0},
+ { 0x24, 0},
+ { 0x25, 0},
+ { 0x26, 0},
+ { 0x27, 0},
+ { 0x28, 0},
+ { 0x30, 0},
+ { 0x31, 0},
+ { 0x32, 0},
+ { 0x33, 0},
+ { 0x34, 0},
+ { 0x35, 0},
+ { 0x36, 0},
+ { 0x37, 0},
+ { 0x38, 0},
+ { 0x39, 0},
+ { 0x40, 0},
+ { 0x41, 0},
+};
+
+static bool max98504_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_INTERRUPT_STATUS:
+ case MAX98504_INTERRUPT_FLAGS:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_GLOBAL_ENABLE:
+ case MAX98504_SOFTWARE_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98504_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_SOFTWARE_RESET:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
+ max98504->pcm_rx_channels);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int max98504_component_probe(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+ struct regmap *map = max98504->regmap;
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
+ msleep(20);
+
+ if (!max98504->brownout_enable)
+ return 0;
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
+ (max98504->brownout_threshold & 0x1f) << 3 |
+ (max98504->brownout_attenuation & 0x3));
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
+ max98504->brownout_attack_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
+ max98504->brownout_timed_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
+ max98504->brownout_release_rate & 0xff);
+
+ return 0;
+}
+
+static void max98504_component_remove(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+}
+
+static const char *spk_source_mux_text[] = {
+ "PCM Monomix", "Analog In", "PDM Left", "PDM Right"
+};
+
+static const struct soc_enum spk_source_mux_enum =
+ SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
+ 0, ARRAY_SIZE(spk_source_mux_text),
+ spk_source_mux_text);
+
+static const struct snd_kcontrol_new spk_source_mux =
+ SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
+
+static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
+ { "SPKOUT", NULL, "Global Enable" },
+ { "SPK Source", "PCM Monomix", "DAC PCM" },
+ { "SPK Source", "Analog In", "AIN" },
+ { "SPK Source", "PDM Left", "DAC PDM" },
+ { "SPK Source", "PDM Right", "DAC PDM" },
+};
+
+static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_INPUT("AIN"),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
+ max98504_pcm_rx_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
+ SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
+ SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
+ MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
+};
+
+static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
+ max98504->pcm_rx_channels = rx_mask;
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ return 0;
+}
+static int max98504_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+ unsigned int i, sources = 0;
+
+ for (i = 0; i < tx_num; i++)
+ if (tx_slot[i])
+ sources |= (1 << i);
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
+ sources);
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98504_dai_ops = {
+ .set_tdm_slot = max98504_set_tdm_slot,
+ .set_channel_map = max98504_set_channel_map,
+};
+
+#define MAX98504_FORMATS (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
+ SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
+#define MAX98504_PDM_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+ SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
+ SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
+ SNDRV_PCM_RATE_96000)
+
+static struct snd_soc_dai_driver max98504_dai[] = {
+ /* TODO: Add the PCM interface definitions */
+ {
+ .name = "max98504-aif2",
+ .id = MAX98504_DAI_ID_PDM,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .ops = &max98504_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver max98504_component_driver = {
+ .probe = max98504_component_probe,
+ .remove = max98504_component_remove,
+ .dapm_widgets = max98504_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets),
+ .dapm_routes = max98504_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct regmap_config max98504_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98504_MAX_REGISTER,
+ .reg_defaults = max98504_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max98504_reg_defaults),
+ .volatile_reg = max98504_volatile_register,
+ .readable_reg = max98504_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98504_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device_node *node = dev->of_node;
+ struct max98504_priv *max98504;
+ int i, ret;
+
+ max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL);
+ if (!max98504)
+ return -ENOMEM;
+
+ if (node) {
+ if (!of_property_read_u32(node, "maxim,brownout-threshold",
+ &max98504->brownout_threshold))
+ max98504->brownout_enable = true;
+
+ of_property_read_u32(node, "maxim,brownout-attenuation",
+ &max98504->brownout_attenuation);
+ of_property_read_u32(node, "maxim,brownout-attack-hold-ms",
+ &max98504->brownout_attack_hold);
+ of_property_read_u32(node, "maxim,brownout-timed-hold-ms",
+ &max98504->brownout_timed_hold);
+ of_property_read_u32(node, "maxim,brownout-release-rate-ms",
+ &max98504->brownout_release_rate);
+ }
+
+ max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap);
+ if (IS_ERR(max98504->regmap)) {
+ ret = PTR_ERR(max98504->regmap);
+ dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < MAX98504_NUM_SUPPLIES; i++)
+ max98504->supplies[i].supply = max98504_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES,
+ max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ i2c_set_clientdata(client, max98504);
+
+ return devm_snd_soc_register_component(dev, &max98504_component_driver,
+ max98504_dai, ARRAY_SIZE(max98504_dai));
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98504_of_match[] = {
+ { .compatible = "maxim,max98504" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max98504_of_match);
+#endif
+
+static const struct i2c_device_id max98504_i2c_id[] = {
+ { "max98504" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
+
+static struct i2c_driver max98504_i2c_driver = {
+ .driver = {
+ .name = "max98504",
+ .of_match_table = of_match_ptr(max98504_of_match),
+ },
+ .probe = max98504_i2c_probe,
+ .id_table = max98504_i2c_id,
+};
+module_i2c_driver(max98504_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98504 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98504.h b/sound/soc/codecs/max98504.h
new file mode 100644
index 000000000000..8b2a113b7118
--- /dev/null
+++ b/sound/soc/codecs/max98504.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2011 - 2012 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ */
+#ifndef MAX98504_H_
+#define MAX98504_H_
+
+/*
+ * MAX98504 Register Definitions
+ */
+#define MAX98504_INTERRUPT_STATUS 0x01
+#define MAX98504_INTERRUPT_FLAGS 0x02
+#define MAX98504_INTERRUPT_ENABLE 0x03
+#define MAX98504_INTERRUPT_FLAG_CLEARS 0x04
+#define MAX98504_GPIO_ENABLE 0x10
+#define MAX98504_GPIO_CONFIG 0x11
+#define MAX98504_WATCHDOG_ENABLE 0x12
+#define MAX98504_WATCHDOG_CONFIG 0x13
+#define MAX98504_WATCHDOG_CLEAR 0x14
+#define MAX98504_CLOCK_MONITOR_ENABLE 0x15
+#define MAX98504_PVDD_BROWNOUT_ENABLE 0x16
+#define MAX98504_PVDD_BROWNOUT_CONFIG_1 0x17
+#define MAX98504_PVDD_BROWNOUT_CONFIG_2 0x18
+#define MAX98504_PVDD_BROWNOUT_CONFIG_3 0x19
+#define MAX98504_PVDD_BROWNOUT_CONFIG_4 0x1a
+#define MAX98504_PCM_RX_ENABLE 0x20
+#define MAX98504_PCM_TX_ENABLE 0x21
+#define MAX98504_PCM_TX_HIZ_CONTROL 0x22
+#define MAX98504_PCM_TX_CHANNEL_SOURCES 0x23
+#define MAX98504_PCM_MODE_CONFIG 0x24
+#define MAX98504_PCM_DSP_CONFIG 0x25
+#define MAX98504_PCM_CLOCK_SETUP 0x26
+#define MAX98504_PCM_SAMPLE_RATE_SETUP 0x27
+#define MAX98504_PCM_TO_SPEAKER_MONOMIX 0x28
+#define MAX98504_PDM_TX_ENABLE 0x30
+#define MAX98504_PDM_TX_HIZ_CONTROL 0x31
+#define MAX98504_PDM_TX_CONTROL 0x32
+#define MAX98504_PDM_RX_ENABLE 0x33
+#define MAX98504_SPEAKER_ENABLE 0x34
+#define MAX98504_SPEAKER_SOURCE_SELECT 0x35
+#define MAX98504_MEASUREMENT_ENABLE 0x36
+#define MAX98504_ANALOGUE_INPUT_GAIN 0x37
+#define MAX98504_TEMPERATURE_LIMIT_CONFIG 0x38
+#define MAX98504_GLOBAL_ENABLE 0x40
+#define MAX98504_SOFTWARE_RESET 0x41
+#define MAX98504_REV_ID 0x7fff
+
+#define MAX98504_MAX_REGISTER 0x7fff
+
+#define MAX98504_DAI_ID_PCM 1
+#define MAX98504_DAI_ID_PDM 2
+
+#endif /* MAX98504_H_ */
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
new file mode 100644
index 000000000000..2bf8976c1828
--- /dev/null
+++ b/sound/soc/codecs/max98520.c
@@ -0,0 +1,766 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <sound/tlv.h>
+#include "max98520.h"
+
+static const struct reg_default max98520_reg[] = {
+ {MAX98520_R2000_SW_RESET, 0x00},
+ {MAX98520_R2001_STATUS_1, 0x00},
+ {MAX98520_R2002_STATUS_2, 0x00},
+ {MAX98520_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98520_R2021_THERM_SHDN_THRESH, 0x64},
+ {MAX98520_R2022_THERM_HYSTERESIS, 0x02},
+ {MAX98520_R2023_THERM_FOLDBACK_SET, 0x31},
+ {MAX98520_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98520_R2030_CLK_MON_CTRL, 0x00},
+ {MAX98520_R2037_ERR_MON_CTRL, 0x01},
+ {MAX98520_R2040_PCM_MODE_CFG, 0xC0},
+ {MAX98520_R2041_PCM_CLK_SETUP, 0x04},
+ {MAX98520_R2042_PCM_SR_SETUP, 0x08},
+ {MAX98520_R2043_PCM_RX_SRC1, 0x00},
+ {MAX98520_R2044_PCM_RX_SRC2, 0x00},
+ {MAX98520_R204F_PCM_RX_EN, 0x00},
+ {MAX98520_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98520_R2091_AMP_PATH_GAIN, 0x03},
+ {MAX98520_R2092_AMP_DSP_CFG, 0x02},
+ {MAX98520_R2094_SSM_CFG, 0x01},
+ {MAX98520_R2095_AMP_CFG, 0xF0},
+ {MAX98520_R209F_AMP_EN, 0x00},
+ {MAX98520_R20B0_ADC_SR, 0x00},
+ {MAX98520_R20B1_ADC_RESOLUTION, 0x00},
+ {MAX98520_R20B2_ADC_PVDD0_CFG, 0x02},
+ {MAX98520_R20B3_ADC_THERMAL_CFG, 0x02},
+ {MAX98520_R20B4_ADC_READBACK_CTRL, 0x00},
+ {MAX98520_R20B5_ADC_READBACK_UPDATE, 0x00},
+ {MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB, 0xFF},
+ {MAX98520_R20BB_ADC_LOW_READBACK_LSB, 0x01},
+ {MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB, 0x00},
+ {MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB, 0x00},
+ {MAX98520_R20CF_MEAS_ADC_CFG, 0x00},
+ {MAX98520_R20D0_DHT_CFG1, 0x00},
+ {MAX98520_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98520_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98520_R20D3_DHT_CFG2, 0x14},
+ {MAX98520_R20D4_DHT_CFG3, 0x02},
+ {MAX98520_R20D5_DHT_CFG4, 0x04},
+ {MAX98520_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98520_R20D8_DHT_EN, 0x00},
+ {MAX98520_R210E_AUTO_RESTART_BEHAVIOR, 0x00},
+ {MAX98520_R210F_GLOBAL_EN, 0x00},
+ {MAX98520_R21FF_REVISION_ID, 0x00},
+};
+
+static int max98520_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98520_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98520_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98520_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98520_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98520_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98520_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+
+static int max98520_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98520->ch_size;
+ int value;
+
+ if (!max98520->tdm_mode) {
+ /* BCLK configuration */
+ value = max98520_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ dev_dbg(component->dev, "%s tdm_mode:%d out\n", __func__, max98520->tdm_mode);
+ return 0;
+}
+
+static int max98520_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98520->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98520_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98520_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98520_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98520_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98520_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98520_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98520_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98520_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98520_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98520_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98520_PCM_SR_96000;
+ break;
+ case 176400:
+ sampling_rate = MAX98520_PCM_SR_176400;
+ break;
+ case 192000:
+ sampling_rate = MAX98520_PCM_SR_192000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ dev_dbg(component->dev, " %s ch_size: %d, sampling rate : %d out\n", __func__,
+ snd_pcm_format_width(params_format(params)), params_rate(params));
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2042_PCM_SR_SETUP,
+ MAX98520_PCM_SR_MASK,
+ sampling_rate);
+
+ return max98520_set_clock(component, params);
+err:
+ dev_dbg(component->dev, "%s out error", __func__);
+ return -EINVAL;
+}
+
+static int max98520_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+ int bsel;
+ unsigned int chan_sz = 0;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98520->tdm_mode = false;
+ else
+ max98520->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98520_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2041_PCM_CLK_SETUP,
+ MAX98520_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2040_PCM_MODE_CFG,
+ MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2044_PCM_RX_SRC2,
+ MAX98520_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R2044_PCM_RX_SRC2,
+ MAX98520_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98520_PCM_DMIX_CH1_SHIFT);
+
+ return 0;
+}
+
+#define MAX98520_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98520_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98520_dai_ops = {
+ .set_fmt = max98520_dai_set_fmt,
+ .hw_params = max98520_dai_hw_params,
+ .set_tdm_slot = max98520_dai_tdm_slot,
+};
+
+static int max98520_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(component->dev, " AMP ON\n");
+
+ regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 1);
+ regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 1);
+ usleep_range(30000, 31000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev, " AMP OFF\n");
+
+ regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 0);
+ regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 0);
+ usleep_range(30000, 31000);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98520_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98520_R2043_PCM_RX_SRC1,
+ 0, 3, max98520_switch_text);
+
+static const struct snd_kcontrol_new max98520_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98520_left_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 0, 0x0, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 0, 0x1, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 0, 0x2, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 0, 0x3, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 0, 0x4, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 0, 0x5, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 0, 0x6, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 0, 0x7, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 0, 0x8, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 0, 0x9, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 0, 0xa, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 0, 0xb, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 0, 0xc, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 0, 0xd, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 0, 0xe, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 0, 0xf, 0),
+};
+
+static const struct snd_kcontrol_new max98520_right_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 4, 0x0, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 4, 0x1, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 4, 0x2, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 4, 0x3, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 4, 0x4, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 4, 0x5, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 4, 0x6, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 4, 0x7, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 4, 0x8, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 4, 0x9, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 4, 0xa, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 4, 0xb, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 4, 0xc, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 4, 0xd, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 4, 0xe, 0),
+ SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 4, 0xf, 0),
+};
+
+static const struct snd_soc_dapm_widget max98520_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ SND_SOC_NOPM, 0, 0, max98520_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, &max98520_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ /* Left Input Selection */
+ SND_SOC_DAPM_MIXER("Left Input Selection", SND_SOC_NOPM, 0, 0,
+ &max98520_left_input_mixer_controls[0],
+ ARRAY_SIZE(max98520_left_input_mixer_controls)),
+ /* Right Input Selection */
+ SND_SOC_DAPM_MIXER("Right Input Selection", SND_SOC_NOPM, 0, 0,
+ &max98520_right_input_mixer_controls[0],
+ ARRAY_SIZE(max98520_right_input_mixer_controls)),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98520_digital_tlv, -6300, 50, 1);
+static const DECLARE_TLV_DB_SCALE(max98520_spk_tlv, -600, 300, 0);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_lim_thresh_tlv,
+ 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_hysteresis_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(600, 200, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_rotation_point_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(-1000, 200, 0),
+ 5, 10, TLV_DB_SCALE_ITEM(-500, 100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_supply_hr_tlv,
+ 0, 16, TLV_DB_SCALE_ITEM(-2000, 250, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98520_dht_max_atten_tlv,
+ 1, 20, TLV_DB_SCALE_ITEM(-2000, 100, 0),
+);
+
+static const char * const max98520_dht_attack_rate_text[] = {
+ "20us", "40us", "80us", "160us", "320us", "640us",
+ "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms",
+ "81.92ms", "163.84ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98520_dht_attack_rate_enum,
+ MAX98520_R20D4_DHT_CFG3, 0,
+ max98520_dht_attack_rate_text);
+
+static const char * const max98520_dht_release_rate_text[] = {
+ "2ms", "4ms", "8ms", "16ms", "32ms", "64ms", "128ms", "256ms", "512ms",
+ "1.024s", "2.048s", "4.096s", "8.192s", "16.384s"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98520_dht_release_rate_enum,
+ MAX98520_R20D5_DHT_CFG4, 0,
+ max98520_dht_release_rate_text);
+
+static bool max98520_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98520_R2000_SW_RESET:
+ case MAX98520_R2027_THERM_FOLDBACK_EN:
+ case MAX98520_R2030_CLK_MON_CTRL:
+ case MAX98520_R2037_ERR_MON_CTRL:
+ case MAX98520_R204F_PCM_RX_EN:
+ case MAX98520_R209F_AMP_EN:
+ case MAX98520_R20CF_MEAS_ADC_CFG:
+ case MAX98520_R20D8_DHT_EN:
+ case MAX98520_R21FF_REVISION_ID:
+ case MAX98520_R2001_STATUS_1... MAX98520_R2002_STATUS_2:
+ case MAX98520_R2020_THERM_WARN_THRESH... MAX98520_R2023_THERM_FOLDBACK_SET:
+ case MAX98520_R2040_PCM_MODE_CFG... MAX98520_R2044_PCM_RX_SRC2:
+ case MAX98520_R2090_AMP_VOL_CTRL... MAX98520_R2092_AMP_DSP_CFG:
+ case MAX98520_R2094_SSM_CFG... MAX98520_R2095_AMP_CFG:
+ case MAX98520_R20B0_ADC_SR... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB:
+ case MAX98520_R20D0_DHT_CFG1... MAX98520_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98520_R210E_AUTO_RESTART_BEHAVIOR... MAX98520_R210F_GLOBAL_EN:
+ case MAX98520_R2161_BOOST_TM1... MAX98520_R2163_BOOST_TM3:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98520_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98520_R210F_GLOBAL_EN:
+ case MAX98520_R21FF_REVISION_ID:
+ case MAX98520_R2000_SW_RESET:
+ case MAX98520_R2001_STATUS_1 ... MAX98520_R2002_STATUS_2:
+ case MAX98520_R20B4_ADC_READBACK_CTRL
+ ... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_kcontrol_new max98520_snd_controls[] = {
+/* Volume */
+SOC_SINGLE_TLV("Digital Volume", MAX98520_R2090_AMP_VOL_CTRL,
+ 0, 0x7F, 1, max98520_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98520_R2091_AMP_PATH_GAIN,
+ 0, 0x5, 0, max98520_spk_tlv),
+/* Volume Ramp Up/Down Enable*/
+SOC_SINGLE("Ramp Up Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+/* Clock Monitor Enable */
+SOC_SINGLE("CLK Monitor Switch", MAX98520_R2037_ERR_MON_CTRL,
+ MAX98520_CTRL_CMON_EN_SHIFT, 1, 0),
+/* Clock Monitor Config */
+SOC_SINGLE("CLKMON Autorestart Switch", MAX98520_R2030_CLK_MON_CTRL,
+ MAX98520_CMON_AUTORESTART_SHIFT, 1, 0),
+/* Dither Enable */
+SOC_SINGLE("Dither Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+/* DC Blocker Enable */
+SOC_SINGLE("DC Blocker Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+/* Speaker Safe Mode Enable */
+SOC_SINGLE("Speaker Safemode Switch", MAX98520_R2092_AMP_DSP_CFG,
+ MAX98520_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+/* AMP SSM Enable */
+SOC_SINGLE("CP Bypass Switch", MAX98520_R2094_SSM_CFG,
+ MAX98520_SSM_RCVR_MODE_SHIFT, 1, 0),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98520_R20D8_DHT_EN, 0, 1, 0),
+SOC_SINGLE("DHT Limiter Mode", MAX98520_R20D2_LIMITER_CFG2,
+ MAX98520_DHT_LIMITER_MODE_SHIFT, 1, 0),
+SOC_SINGLE("DHT Hysteresis Switch", MAX98520_R20D6_DHT_HYSTERESIS_CFG,
+ MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Rot Pnt", MAX98520_R20D0_DHT_CFG1,
+ MAX98520_DHT_VROT_PNT_SHIFT, 10, 1, max98520_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Supply Headroom", MAX98520_R20D1_LIMITER_CFG1,
+ MAX98520_DHT_SUPPLY_HR_SHIFT, 16, 0, max98520_dht_supply_hr_tlv),
+SOC_SINGLE_TLV("DHT Limiter Threshold", MAX98520_R20D2_LIMITER_CFG2,
+ MAX98520_DHT_LIMITER_THRESHOLD_SHIFT, 0xF, 1, max98520_dht_lim_thresh_tlv),
+SOC_SINGLE_TLV("DHT Max Attenuation", MAX98520_R20D3_DHT_CFG2,
+ MAX98520_DHT_MAX_ATTEN_SHIFT, 20, 1, max98520_dht_max_atten_tlv),
+SOC_SINGLE_TLV("DHT Hysteresis", MAX98520_R20D6_DHT_HYSTERESIS_CFG,
+ MAX98520_DHT_HYSTERESIS_SHIFT, 0x7, 0, max98520_dht_hysteresis_tlv),
+SOC_ENUM("DHT Attack Rate", max98520_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98520_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98520_R20CF_MEAS_ADC_CFG, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98520_R20B3_ADC_THERMAL_CFG, MAX98520_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC PVDD MSB", MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0, 0xFF, 0),
+SOC_SINGLE("ADC PVDD LSB", MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0, 0x01, 0),
+SOC_SINGLE("ADC TEMP MSB", MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0, 0xFF, 0),
+SOC_SINGLE("ADC TEMP LSB", MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0, 0x01, 0),
+};
+
+static const struct snd_soc_dapm_route max98520_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static struct snd_soc_dai_driver max98520_dai[] = {
+ {
+ .name = "max98520-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98520_RATES,
+ .formats = MAX98520_FORMATS,
+ },
+ .ops = &max98520_dai_ops,
+ }
+
+};
+
+static int max98520_probe(struct snd_soc_component *component)
+{
+ struct max98520_priv *max98520 =
+ snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1);
+
+ /* L/R mono mix configuration : "DAI Sel" for 0x2043 */
+ regmap_write(max98520->regmap, MAX98520_R2043_PCM_RX_SRC1, 0x2);
+
+ /* PCM input channles configuration : "Left Input Selection" for 0x2044 */
+ /* PCM input channles configuration : "Right Input Selection" for 0x2044 */
+ regmap_write(max98520->regmap, MAX98520_R2044_PCM_RX_SRC2, 0x10);
+
+ /* Enable DC blocker */
+ regmap_update_bits(max98520->regmap, MAX98520_R2092_AMP_DSP_CFG, 1, 1);
+ /* Enable Clock Monitor Auto-restart */
+ regmap_write(max98520->regmap, MAX98520_R2030_CLK_MON_CTRL, 0x1);
+
+ /* set Rx Enable */
+ regmap_update_bits(max98520->regmap,
+ MAX98520_R204F_PCM_RX_EN,
+ MAX98520_PCM_RX_EN_MASK,
+ 1);
+
+ return 0;
+}
+
+static int max98520_suspend(struct device *dev)
+{
+ struct max98520_priv *max98520 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98520->regmap, true);
+ regcache_mark_dirty(max98520->regmap);
+ return 0;
+}
+
+static int max98520_resume(struct device *dev)
+{
+ struct max98520_priv *max98520 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98520->regmap, false);
+ regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1);
+ regcache_sync(max98520->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops max98520_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98520_suspend, max98520_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98520 = {
+ .probe = max98520_probe,
+ .controls = max98520_snd_controls,
+ .num_controls = ARRAY_SIZE(max98520_snd_controls),
+ .dapm_widgets = max98520_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98520_dapm_widgets),
+ .dapm_routes = max98520_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98520_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98520_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98520_R21FF_REVISION_ID,
+ .reg_defaults = max98520_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98520_reg),
+ .readable_reg = max98520_readable_register,
+ .volatile_reg = max98520_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98520_power_on(struct max98520_priv *max98520, bool poweron)
+{
+ if (max98520->reset_gpio)
+ gpiod_set_value_cansleep(max98520->reset_gpio, !poweron);
+}
+
+static int max98520_i2c_probe(struct i2c_client *i2c)
+{
+ int ret;
+ int reg = 0;
+ struct max98520_priv *max98520;
+ struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
+
+ ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!ret) {
+ dev_err(&i2c->dev, "I2C check functionality failed\n");
+ return -ENXIO;
+ }
+
+ max98520 = devm_kzalloc(&i2c->dev, sizeof(*max98520), GFP_KERNEL);
+
+ if (!max98520)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98520);
+
+ /* regmap initialization */
+ max98520->regmap = devm_regmap_init_i2c(i2c, &max98520_regmap);
+ if (IS_ERR(max98520->regmap)) {
+ ret = PTR_ERR(max98520->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Power on device */
+ max98520->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (max98520->reset_gpio) {
+ if (IS_ERR(max98520->reset_gpio)) {
+ ret = PTR_ERR(max98520->reset_gpio);
+ dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+ return ret;
+ }
+
+ max98520_power_on(max98520, 1);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98520->regmap, MAX98520_R21FF_REVISION_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98520_R21FF_REVISION_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98520 revisionID: 0x%02X\n", reg);
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98520,
+ max98520_dai, ARRAY_SIZE(max98520_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98520_i2c_id[] = {
+ { "max98520"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98520_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98520_of_match[] = {
+ { .compatible = "maxim,max98520", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98520_of_match);
+#endif
+
+static struct i2c_driver max98520_i2c_driver = {
+ .driver = {
+ .name = "max98520",
+ .of_match_table = of_match_ptr(max98520_of_match),
+ .pm = pm_ptr(&max98520_pm),
+ },
+ .probe = max98520_i2c_probe,
+ .id_table = max98520_i2c_id,
+};
+
+module_i2c_driver(max98520_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98520 driver");
+MODULE_AUTHOR("George Song <george.song@maximintegrated.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/max98520.h b/sound/soc/codecs/max98520.h
new file mode 100644
index 000000000000..89a95c25afcf
--- /dev/null
+++ b/sound/soc/codecs/max98520.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021, Maxim Integrated.
+ */
+
+#ifndef _MAX98520_H
+#define _MAX98520_H
+
+#define MAX98520_R2000_SW_RESET 0x2000
+#define MAX98520_R2001_STATUS_1 0x2001
+#define MAX98520_R2002_STATUS_2 0x2002
+#define MAX98520_R2020_THERM_WARN_THRESH 0x2020
+#define MAX98520_R2021_THERM_SHDN_THRESH 0x2021
+#define MAX98520_R2022_THERM_HYSTERESIS 0x2022
+#define MAX98520_R2023_THERM_FOLDBACK_SET 0x2023
+#define MAX98520_R2027_THERM_FOLDBACK_EN 0x2027
+#define MAX98520_R2030_CLK_MON_CTRL 0x2030
+#define MAX98520_R2037_ERR_MON_CTRL 0x2037
+#define MAX98520_R2040_PCM_MODE_CFG 0x2040
+#define MAX98520_R2041_PCM_CLK_SETUP 0x2041
+#define MAX98520_R2042_PCM_SR_SETUP 0x2042
+#define MAX98520_R2043_PCM_RX_SRC1 0x2043
+#define MAX98520_R2044_PCM_RX_SRC2 0x2044
+#define MAX98520_R204F_PCM_RX_EN 0x204F
+#define MAX98520_R2090_AMP_VOL_CTRL 0x2090
+#define MAX98520_R2091_AMP_PATH_GAIN 0x2091
+#define MAX98520_R2092_AMP_DSP_CFG 0x2092
+#define MAX98520_R2094_SSM_CFG 0x2094
+#define MAX98520_R2095_AMP_CFG 0x2095
+#define MAX98520_R209F_AMP_EN 0x209F
+#define MAX98520_R20B0_ADC_SR 0x20B0
+#define MAX98520_R20B1_ADC_RESOLUTION 0x20B1
+#define MAX98520_R20B2_ADC_PVDD0_CFG 0x20B2
+#define MAX98520_R20B3_ADC_THERMAL_CFG 0x20B3
+#define MAX98520_R20B4_ADC_READBACK_CTRL 0x20B4
+#define MAX98520_R20B5_ADC_READBACK_UPDATE 0x20B5
+#define MAX98520_R20B6_ADC_PVDD_READBACK_MSB 0x20B6
+#define MAX98520_R20B7_ADC_PVDD_READBACK_LSB 0x20B7
+#define MAX98520_R20B8_ADC_TEMP_READBACK_MSB 0x20B8
+#define MAX98520_R20B9_ADC_TEMP_READBACK_LSB 0x20B9
+#define MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB 0x20BA
+#define MAX98520_R20BB_ADC_LOW_READBACK_LSB 0x20BB
+#define MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB 0x20BC
+#define MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB 0x20BD
+#define MAX98520_R20CF_MEAS_ADC_CFG 0x20CF
+#define MAX98520_R20D0_DHT_CFG1 0x20D0
+#define MAX98520_R20D1_LIMITER_CFG1 0x20D1
+#define MAX98520_R20D2_LIMITER_CFG2 0x20D2
+#define MAX98520_R20D3_DHT_CFG2 0x20D3
+#define MAX98520_R20D4_DHT_CFG3 0x20D4
+#define MAX98520_R20D5_DHT_CFG4 0x20D5
+#define MAX98520_R20D6_DHT_HYSTERESIS_CFG 0x20D6
+#define MAX98520_R20D8_DHT_EN 0x20D8
+#define MAX98520_R210E_AUTO_RESTART_BEHAVIOR 0x210E
+#define MAX98520_R210F_GLOBAL_EN 0x210F
+#define MAX98520_R2161_BOOST_TM1 0x2161
+#define MAX98520_R2162_BOOST_TM2 0x2162
+#define MAX98520_R2163_BOOST_TM3 0x2163
+#define MAX98520_R21FF_REVISION_ID 0x21FF
+
+/* MAX98520_R2030_CLK_MON_CTRL */
+#define MAX98520_CMON_AUTORESTART_SHIFT (0)
+
+/* MAX98520_R2037_ERR_MON_CTRL */
+#define MAX98520_CTRL_CMON_EN_SHIFT (0)
+
+/* MAX98520_R2040_PCM_MODE_CFG */
+#define MAX98520_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98520_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98520_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98520_PCM_FORMAT_I2S (0x0 << 3)
+#define MAX98520_PCM_FORMAT_LJ (0x1 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE0 (0x3 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE1 (0x4 << 3)
+#define MAX98520_PCM_FORMAT_TDM_MODE2 (0x5 << 3)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98520_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98520_R2041_PCM_CLK_SETUP */
+#define MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98520_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98520_R2042_PCM_SR_SETUP */
+#define MAX98520_PCM_SR_SHIFT (0)
+#define MAX98520_IVADC_SR_SHIFT (4)
+#define MAX98520_PCM_SR_MASK (0xF << MAX98520_PCM_SR_SHIFT)
+#define MAX98520_IVADC_SR_MASK (0xF << MAX98520_IVADC_SR_SHIFT)
+#define MAX98520_PCM_SR_8000 (0x0)
+#define MAX98520_PCM_SR_11025 (0x1)
+#define MAX98520_PCM_SR_12000 (0x2)
+#define MAX98520_PCM_SR_16000 (0x3)
+#define MAX98520_PCM_SR_22050 (0x4)
+#define MAX98520_PCM_SR_24000 (0x5)
+#define MAX98520_PCM_SR_32000 (0x6)
+#define MAX98520_PCM_SR_44100 (0x7)
+#define MAX98520_PCM_SR_48000 (0x8)
+#define MAX98520_PCM_SR_88200 (0x9)
+#define MAX98520_PCM_SR_96000 (0xA)
+#define MAX98520_PCM_SR_176400 (0xB)
+#define MAX98520_PCM_SR_192000 (0xC)
+
+/* MAX98520_R2044_PCM_RX_SRC2 */
+#define MAX98520_PCM_DMIX_CH1_SHIFT (0xF << 0)
+#define MAX98520_PCM_DMIX_CH0_SRC_MASK (0xF << 0)
+#define MAX98520_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98520_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98520_R204F_PCM_RX_EN */
+#define MAX98520_PCM_RX_EN_MASK (0x1 << 0)
+#define MAX98520_PCM_RX_BYP_EN_MASK (0x1 << 1)
+
+/* MAX98520_R2092_AMP_DSP_CFG */
+#define MAX98520_DSP_SPK_DCBLK_EN_SHIFT (0)
+#define MAX98520_DSP_SPK_DITH_EN_SHIFT (1)
+#define MAX98520_DSP_SPK_INVERT_SHIFT (2)
+#define MAX98520_DSP_SPK_VOL_RMPUP_SHIFT (3)
+#define MAX98520_DSP_SPK_VOL_RMPDN_SHIFT (4)
+#define MAX98520_DSP_SPK_SAFE_EN_SHIFT (5)
+
+#define MAX98520_SPK_SAFE_EN_MASK (0x1 << MAX98520_DSP_SPK_SAFE_EN_SHIFT)
+
+/* MAX98520_R2094_SSM_CFG */
+#define MAX98520_SSM_EN_SHIFT (0)
+#define MAX98520_SSM_MOD_SHIFT (1)
+#define MAX98520_SSM_RCVR_MODE_SHIFT (3)
+
+/* MAX98520_R2095_AMP_CFG */
+#define MAX98520_CFG_DYN_MODE_SHIFT (4)
+#define MAX98520_CFG_SPK_MODE_SHIFT (3)
+
+/* MAX98520_R20D0_DHT_CFG1 */
+#define MAX98520_DHT_VROT_PNT_SHIFT (0)
+
+/* MAX98520_R20D1_LIMITER_CFG1 */
+#define MAX98520_DHT_SUPPLY_HR_SHIFT (0)
+
+/* MAX98520_R20D2_DHT_CFG2 */
+#define MAX98520_DHT_LIMITER_MODE_SHIFT (0)
+#define MAX98520_DHT_LIMITER_THRESHOLD_SHIFT (1)
+
+/* MAX98520_R20D3_DHT_CFG2 */
+#define MAX98520_DHT_MAX_ATTEN_SHIFT (0)
+
+/* MAX98520_R20D6_DHT_HYSTERESIS_CFG */
+#define MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT (0)
+#define MAX98520_DHT_HYSTERESIS_SHIFT (1)
+
+/* MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_R20B3_ADC_THERMAL_CFG */
+#define MAX98520_FLT_EN_SHIFT (4)
+
+struct max98520_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int ch_size;
+ bool tdm_mode;
+};
+#endif
+
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
new file mode 100644
index 000000000000..716d16daf7d7
--- /dev/null
+++ b/sound/soc/codecs/max9860.c
@@ -0,0 +1,738 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the MAX9860 Mono Audio Voice Codec
+//
+// https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf
+//
+// The driver does not support sidetone since the DVST register field is
+// backwards with the mute near the maximum level instead of the minimum.
+//
+// Author: Peter Rosin <peda@axentia.s>
+// Copyright 2016 Axentia Technologies
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "max9860.h"
+
+struct max9860_priv {
+ struct regmap *regmap;
+ struct regulator *dvddio;
+ struct notifier_block dvddio_nb;
+ u8 psclk;
+ unsigned long pclk_rate;
+ int fmt;
+};
+
+static int max9860_dvddio_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct max9860_priv *max9860 = container_of(nb, struct max9860_priv,
+ dvddio_nb);
+ if (event & REGULATOR_EVENT_DISABLE) {
+ regcache_mark_dirty(max9860->regmap);
+ regcache_cache_only(max9860->regmap, true);
+ }
+
+ return 0;
+}
+
+static const struct reg_default max9860_reg_defaults[] = {
+ { MAX9860_PWRMAN, 0x00 },
+ { MAX9860_INTEN, 0x00 },
+ { MAX9860_SYSCLK, 0x00 },
+ { MAX9860_AUDIOCLKHIGH, 0x00 },
+ { MAX9860_AUDIOCLKLOW, 0x00 },
+ { MAX9860_IFC1A, 0x00 },
+ { MAX9860_IFC1B, 0x00 },
+ { MAX9860_VOICEFLTR, 0x00 },
+ { MAX9860_DACATTN, 0x00 },
+ { MAX9860_ADCLEVEL, 0x00 },
+ { MAX9860_DACGAIN, 0x00 },
+ { MAX9860_MICGAIN, 0x00 },
+ { MAX9860_MICADC, 0x00 },
+ { MAX9860_NOISEGATE, 0x00 },
+};
+
+static bool max9860_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS ... MAX9860_MICGAIN:
+ case MAX9860_MICADC ... MAX9860_PWRMAN:
+ case MAX9860_REVISION:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTEN ... MAX9860_MICGAIN:
+ case MAX9860_MICADC ... MAX9860_PWRMAN:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS:
+ case MAX9860_MICREADBACK:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config max9860_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = max9860_readable,
+ .writeable_reg = max9860_writeable,
+ .volatile_reg = max9860_volatile,
+ .precious_reg = max9860_precious,
+
+ .max_register = MAX9860_MAX_REGISTER,
+ .reg_defaults = max9860_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max9860_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_RANGE(pam_tlv,
+ 0, MAX9860_PAM_MAX - 1, TLV_DB_SCALE_ITEM(-2000, 2000, 1),
+ MAX9860_PAM_MAX, MAX9860_PAM_MAX, TLV_DB_SCALE_ITEM(3000, 0, 0));
+static const DECLARE_TLV_DB_SCALE(pgam_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(anth_tlv, -7600, 400, 1);
+static const DECLARE_TLV_DB_SCALE(agcth_tlv, -1800, 100, 0);
+
+static const char * const agchld_text[] = {
+ "AGC Disabled", "50ms", "100ms", "400ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agchld_enum, MAX9860_MICADC,
+ MAX9860_AGCHLD_SHIFT, agchld_text);
+
+static const char * const agcsrc_text[] = {
+ "Left ADC", "Left/Right ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcsrc_enum, MAX9860_MICADC,
+ MAX9860_AGCSRC_SHIFT, agcsrc_text);
+
+static const char * const agcatk_text[] = {
+ "3ms", "12ms", "50ms", "200ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcatk_enum, MAX9860_MICADC,
+ MAX9860_AGCATK_SHIFT, agcatk_text);
+
+static const char * const agcrls_text[] = {
+ "78ms", "156ms", "312ms", "625ms",
+ "1.25s", "2.5s", "5s", "10s"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcrls_enum, MAX9860_MICADC,
+ MAX9860_AGCRLS_SHIFT, agcrls_text);
+
+static const char * const filter_text[] = {
+ "Disabled",
+ "Elliptical HP 217Hz notch (16kHz)",
+ "Butterworth HP 500Hz (16kHz)",
+ "Elliptical HP 217Hz notch (8kHz)",
+ "Butterworth HP 500Hz (8kHz)",
+ "Butterworth HP 200Hz (48kHz)"
+};
+
+static SOC_ENUM_SINGLE_DECL(avflt_enum, MAX9860_VOICEFLTR,
+ MAX9860_AVFLT_SHIFT, filter_text);
+
+static SOC_ENUM_SINGLE_DECL(dvflt_enum, MAX9860_VOICEFLTR,
+ MAX9860_DVFLT_SHIFT, filter_text);
+
+static const struct snd_kcontrol_new max9860_controls[] = {
+SOC_SINGLE_TLV("Master Playback Volume", MAX9860_DACATTN,
+ MAX9860_DVA_SHIFT, MAX9860_DVA_MUTE, 1, dva_tlv),
+SOC_SINGLE_TLV("DAC Gain Volume", MAX9860_DACGAIN,
+ MAX9860_DVG_SHIFT, MAX9860_DVG_MAX, 0, dvg_tlv),
+SOC_DOUBLE_TLV("Line Capture Volume", MAX9860_ADCLEVEL,
+ MAX9860_ADCLL_SHIFT, MAX9860_ADCRL_SHIFT, MAX9860_ADCxL_MIN, 1,
+ adc_tlv),
+
+SOC_ENUM("AGC Hold Time", agchld_enum),
+SOC_ENUM("AGC/Noise Gate Source", agcsrc_enum),
+SOC_ENUM("AGC Attack Time", agcatk_enum),
+SOC_ENUM("AGC Release Time", agcrls_enum),
+
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MAX9860_NOISEGATE,
+ MAX9860_ANTH_SHIFT, MAX9860_ANTH_MAX, 0, anth_tlv),
+SOC_SINGLE_TLV("AGC Signal Threshold Volume", MAX9860_NOISEGATE,
+ MAX9860_AGCTH_SHIFT, MAX9860_AGCTH_MIN, 1, agcth_tlv),
+
+SOC_SINGLE_TLV("Mic PGA Volume", MAX9860_MICGAIN,
+ MAX9860_PGAM_SHIFT, MAX9860_PGAM_MIN, 1, pgam_tlv),
+SOC_SINGLE_TLV("Mic Preamp Volume", MAX9860_MICGAIN,
+ MAX9860_PAM_SHIFT, MAX9860_PAM_MAX, 0, pam_tlv),
+
+SOC_ENUM("ADC Filter", avflt_enum),
+SOC_ENUM("DAC Filter", dvflt_enum),
+};
+
+static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICL"),
+SND_SOC_DAPM_INPUT("MICR"),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, MAX9860_PWRMAN, MAX9860_ADCLEN_SHIFT, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_DAC("DAC", NULL, MAX9860_PWRMAN, MAX9860_DACEN_SHIFT, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT"),
+
+SND_SOC_DAPM_SUPPLY("Supply", SND_SOC_NOPM, 0, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DVDD", 0, 0),
+SND_SOC_DAPM_CLOCK_SUPPLY("mclk"),
+};
+
+static const struct snd_soc_dapm_route max9860_dapm_routes[] = {
+ { "ADCL", NULL, "MICL" },
+ { "ADCR", NULL, "MICR" },
+ { "AIFOUTL", NULL, "ADCL" },
+ { "AIFOUTR", NULL, "ADCR" },
+
+ { "DAC", NULL, "AIFINL" },
+ { "DAC", NULL, "AIFINR" },
+ { "OUT", NULL, "DAC" },
+
+ { "Supply", NULL, "AVDD" },
+ { "Supply", NULL, "DVDD" },
+ { "Supply", NULL, "mclk" },
+
+ { "DAC", NULL, "Supply" },
+ { "ADCL", NULL, "Supply" },
+ { "ADCR", NULL, "Supply" },
+};
+
+static int max9860_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component);
+ u8 master;
+ u8 ifc1a = 0;
+ u8 ifc1b = 0;
+ u8 sysclk = 0;
+ unsigned long n;
+ int ret;
+
+ dev_dbg(component->dev, "hw_params %u Hz, %u channels\n",
+ params_rate(params),
+ params_channels(params));
+
+ if (params_channels(params) == 2)
+ ifc1b |= MAX9860_ST;
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ master = MAX9860_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ifc1a |= master;
+
+ if (master) {
+ if (params_width(params) * params_channels(params) > 48)
+ ifc1b |= MAX9860_BSEL_64X;
+ else
+ ifc1b |= MAX9860_BSEL_48X;
+ }
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ifc1a |= MAX9860_DDLY;
+ ifc1b |= MAX9860_ADLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ifc1a |= MAX9860_WCI;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ if (params_width(params) != 16) {
+ dev_err(component->dev,
+ "DSP_A works for 16 bits per sample only.\n");
+ return -EINVAL;
+ }
+ ifc1a |= MAX9860_DDLY | MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+ ifc1b |= MAX9860_ADLY;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if (params_width(params) != 16) {
+ dev_err(component->dev,
+ "DSP_B works for 16 bits per sample only.\n");
+ return -EINVAL;
+ }
+ ifc1a |= MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ return -EINVAL;
+ }
+ ifc1a ^= MAX9860_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ return -EINVAL;
+ }
+ ifc1a ^= MAX9860_WCI;
+ fallthrough;
+ case SND_SOC_DAIFMT_IB_NF:
+ ifc1a ^= MAX9860_DBCI;
+ ifc1b ^= MAX9860_ABCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "IFC1A %02x\n", ifc1a);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a);
+ if (ret) {
+ dev_err(component->dev, "Failed to set IFC1A: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(component->dev, "IFC1B %02x\n", ifc1b);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b);
+ if (ret) {
+ dev_err(component->dev, "Failed to set IFC1B: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Check if Integer Clock Mode is possible, but avoid it in slave mode
+ * since we then do not know if lrclk is derived from pclk and the
+ * datasheet mentions that the frequencies have to match exactly in
+ * order for this to work.
+ */
+ if (params_rate(params) == 8000 || params_rate(params) == 16000) {
+ if (master) {
+ switch (max9860->pclk_rate) {
+ case 12000000:
+ sysclk = MAX9860_FREQ_12MHZ;
+ break;
+ case 13000000:
+ sysclk = MAX9860_FREQ_13MHZ;
+ break;
+ case 19200000:
+ sysclk = MAX9860_FREQ_19_2MHZ;
+ break;
+ default:
+ /*
+ * Integer Clock Mode not possible. Leave
+ * sysclk at zero and fall through to the
+ * code below for PLL mode.
+ */
+ break;
+ }
+
+ if (sysclk && params_rate(params) == 16000)
+ sysclk |= MAX9860_16KHZ;
+ }
+ }
+
+ /*
+ * Largest possible n:
+ * 65536 * 96 * 48kHz / 10MHz -> 30199
+ * Smallest possible n:
+ * 65536 * 96 * 8kHz / 20MHz -> 2517
+ * Both fit nicely in the available 15 bits, no need to apply any mask.
+ */
+ n = DIV_ROUND_CLOSEST_ULL(65536ULL * 96 * params_rate(params),
+ max9860->pclk_rate);
+
+ if (!sysclk) {
+ /* PLL mode */
+ if (params_rate(params) > 24000)
+ sysclk |= MAX9860_16KHZ;
+
+ if (!master)
+ n |= 1; /* trigger rapid pll lock mode */
+ }
+
+ sysclk |= max9860->psclk;
+ dev_dbg(component->dev, "SYSCLK %02x\n", sysclk);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_SYSCLK, sysclk);
+ if (ret) {
+ dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(component->dev, "N %lu\n", n);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKHIGH, n >> 8);
+ if (ret) {
+ dev_err(component->dev, "Failed to set NHI: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKLOW, n & 0xff);
+ if (ret) {
+ dev_err(component->dev, "Failed to set NLO: %d\n", ret);
+ return ret;
+ }
+
+ if (!master) {
+ dev_dbg(component->dev, "Enable PLL\n");
+ ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH,
+ MAX9860_PLL, MAX9860_PLL);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable PLL: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max9860->fmt = fmt;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops max9860_dai_ops = {
+ .hw_params = max9860_hw_params,
+ .set_fmt = max9860_set_fmt,
+};
+
+static struct snd_soc_dai_driver max9860_dai = {
+ .name = "max9860-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &max9860_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int max9860_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(component->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, MAX9860_SHDN);
+ if (ret) {
+ dev_err(component->dev, "Failed to remove SHDN: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, 0);
+ if (ret) {
+ dev_err(component->dev, "Failed to request SHDN: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver max9860_component_driver = {
+ .set_bias_level = max9860_set_bias_level,
+ .controls = max9860_controls,
+ .num_controls = ARRAY_SIZE(max9860_controls),
+ .dapm_widgets = max9860_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets),
+ .dapm_routes = max9860_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int max9860_suspend(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, MAX9860_PSCLK_OFF);
+ if (ret) {
+ dev_err(dev, "Failed to disable clock: %d\n", ret);
+ return ret;
+ }
+
+ regulator_disable(max9860->dvddio);
+
+ return 0;
+}
+
+static int max9860_resume(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(max9860->regmap, false);
+ ret = regcache_sync(max9860->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, max9860->psclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops max9860_pm_ops = {
+ RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
+};
+
+static int max9860_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860;
+ int ret;
+ struct clk *mclk;
+ unsigned long mclk_rate;
+ int i;
+ int intr;
+
+ max9860 = devm_kzalloc(dev, sizeof(struct max9860_priv), GFP_KERNEL);
+ if (!max9860)
+ return -ENOMEM;
+
+ max9860->dvddio = devm_regulator_get(dev, "DVDDIO");
+ if (IS_ERR(max9860->dvddio))
+ return dev_err_probe(dev, PTR_ERR(max9860->dvddio),
+ "Failed to get DVDDIO supply\n");
+
+ max9860->dvddio_nb.notifier_call = max9860_dvddio_event;
+
+ ret = devm_regulator_register_notifier(max9860->dvddio,
+ &max9860->dvddio_nb);
+ if (ret)
+ dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret);
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ max9860->regmap = devm_regmap_init_i2c(i2c, &max9860_regmap);
+ if (IS_ERR(max9860->regmap)) {
+ ret = PTR_ERR(max9860->regmap);
+ goto err_regulator;
+ }
+
+ dev_set_drvdata(dev, max9860);
+
+ /*
+ * mclk has to be in the 10MHz to 60MHz range.
+ * psclk is used to scale mclk into pclk so that
+ * pclk is in the 10MHz to 20MHz range.
+ */
+ mclk = clk_get(dev, "mclk");
+
+ if (IS_ERR(mclk)) {
+ ret = PTR_ERR(mclk);
+ dev_err_probe(dev, ret, "Failed to get MCLK\n");
+ goto err_regulator;
+ }
+
+ mclk_rate = clk_get_rate(mclk);
+ clk_put(mclk);
+
+ if (mclk_rate > 60000000 || mclk_rate < 10000000) {
+ dev_err(dev, "Bad mclk %luHz (needs 10MHz - 60MHz)\n",
+ mclk_rate);
+ ret = -EINVAL;
+ goto err_regulator;
+ }
+ if (mclk_rate >= 40000000)
+ max9860->psclk = 3;
+ else if (mclk_rate >= 20000000)
+ max9860->psclk = 2;
+ else
+ max9860->psclk = 1;
+ max9860->pclk_rate = mclk_rate >> (max9860->psclk - 1);
+ max9860->psclk <<= MAX9860_PSCLK_SHIFT;
+ dev_dbg(dev, "mclk %lu pclk %lu\n", mclk_rate, max9860->pclk_rate);
+
+ regcache_cache_bypass(max9860->regmap, true);
+ for (i = 0; i < max9860_regmap.num_reg_defaults; ++i) {
+ ret = regmap_write(max9860->regmap,
+ max9860_regmap.reg_defaults[i].reg,
+ max9860_regmap.reg_defaults[i].def);
+ if (ret) {
+ dev_err(dev, "Failed to initialize register %u: %d\n",
+ max9860_regmap.reg_defaults[i].reg, ret);
+ goto err_regulator;
+ }
+ }
+ regcache_cache_bypass(max9860->regmap, false);
+
+ ret = regmap_read(max9860->regmap, MAX9860_INTRSTATUS, &intr);
+ if (ret) {
+ dev_err(dev, "Failed to clear INTRSTATUS: %d\n", ret);
+ goto err_regulator;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ ret = devm_snd_soc_register_component(dev, &max9860_component_driver,
+ &max9860_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm;
+ }
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
+err_regulator:
+ regulator_disable(max9860->dvddio);
+ return ret;
+}
+
+static void max9860_remove(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+
+ pm_runtime_disable(dev);
+ regulator_disable(max9860->dvddio);
+}
+
+static const struct i2c_device_id max9860_i2c_id[] = {
+ { "max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9860_i2c_id);
+
+static const struct of_device_id max9860_of_match[] = {
+ { .compatible = "maxim,max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9860_of_match);
+
+static struct i2c_driver max9860_i2c_driver = {
+ .probe = max9860_probe,
+ .remove = max9860_remove,
+ .id_table = max9860_i2c_id,
+ .driver = {
+ .name = "max9860",
+ .of_match_table = max9860_of_match,
+ .pm = pm_ptr(&max9860_pm_ops),
+ },
+};
+
+module_i2c_driver(max9860_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h
new file mode 100644
index 000000000000..e07b905eaf50
--- /dev/null
+++ b/sound/soc/codecs/max9860.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ * Copyright 2016 Axentia Technologies
+ */
+
+#ifndef _SND_SOC_MAX9860
+#define _SND_SOC_MAX9860
+
+#define MAX9860_INTRSTATUS 0x00
+#define MAX9860_MICREADBACK 0x01
+#define MAX9860_INTEN 0x02
+#define MAX9860_SYSCLK 0x03
+#define MAX9860_AUDIOCLKHIGH 0x04
+#define MAX9860_AUDIOCLKLOW 0x05
+#define MAX9860_IFC1A 0x06
+#define MAX9860_IFC1B 0x07
+#define MAX9860_VOICEFLTR 0x08
+#define MAX9860_DACATTN 0x09
+#define MAX9860_ADCLEVEL 0x0a
+#define MAX9860_DACGAIN 0x0b
+#define MAX9860_MICGAIN 0x0c
+#define MAX9860_RESERVED 0x0d
+#define MAX9860_MICADC 0x0e
+#define MAX9860_NOISEGATE 0x0f
+#define MAX9860_PWRMAN 0x10
+#define MAX9860_REVISION 0xff
+
+#define MAX9860_MAX_REGISTER 0xff
+
+/* INTRSTATUS */
+#define MAX9860_CLD 0x80
+#define MAX9860_SLD 0x40
+#define MAX9860_ULK 0x20
+
+/* MICREADBACK */
+#define MAX9860_NG 0xe0
+#define MAX9860_AGC 0x1f
+
+/* INTEN */
+#define MAX9860_ICLD 0x80
+#define MAX9860_ISLD 0x40
+#define MAX9860_IULK 0x20
+
+/* SYSCLK */
+#define MAX9860_PSCLK 0x30
+#define MAX9860_PSCLK_OFF 0x00
+#define MAX9860_PSCLK_SHIFT 4
+#define MAX9860_FREQ 0x06
+#define MAX9860_FREQ_NORMAL 0x00
+#define MAX9860_FREQ_12MHZ 0x02
+#define MAX9860_FREQ_13MHZ 0x04
+#define MAX9860_FREQ_19_2MHZ 0x06
+#define MAX9860_16KHZ 0x01
+
+/* AUDIOCLKHIGH */
+#define MAX9860_PLL 0x80
+#define MAX9860_NHI 0x7f
+
+/* AUDIOCLKLOW */
+#define MAX9860_NLO 0xff
+
+/* IFC1A */
+#define MAX9860_MASTER 0x80
+#define MAX9860_WCI 0x40
+#define MAX9860_DBCI 0x20
+#define MAX9860_DDLY 0x10
+#define MAX9860_HIZ 0x08
+#define MAX9860_TDM 0x04
+
+/* IFC1B */
+#define MAX9860_ABCI 0x20
+#define MAX9860_ADLY 0x10
+#define MAX9860_ST 0x08
+#define MAX9860_BSEL 0x07
+#define MAX9860_BSEL_OFF 0x00
+#define MAX9860_BSEL_64X 0x01
+#define MAX9860_BSEL_48X 0x02
+#define MAX9860_BSEL_PCLK_2 0x04
+#define MAX9860_BSEL_PCLK_4 0x05
+#define MAX9860_BSEL_PCLK_8 0x06
+#define MAX9860_BSEL_PCLK_16 0x07
+
+/* VOICEFLTR */
+#define MAX9860_AVFLT 0xf0
+#define MAX9860_AVFLT_SHIFT 4
+#define MAX9860_AVFLT_COUNT 6
+#define MAX9860_DVFLT 0x0f
+#define MAX9860_DVFLT_SHIFT 0
+#define MAX9860_DVFLT_COUNT 6
+
+/* DACATTN */
+#define MAX9860_DVA 0xfe
+#define MAX9860_DVA_SHIFT 1
+#define MAX9860_DVA_MUTE 0x5e
+
+/* ADCLEVEL */
+#define MAX9860_ADCRL 0xf0
+#define MAX9860_ADCRL_SHIFT 4
+#define MAX9860_ADCLL 0x0f
+#define MAX9860_ADCLL_SHIFT 0
+#define MAX9860_ADCxL_MIN 15
+
+/* DACGAIN */
+#define MAX9860_DVG 0x60
+#define MAX9860_DVG_SHIFT 5
+#define MAX9860_DVG_MAX 3
+#define MAX9860_DVST 0x1f
+#define MAX9860_DVST_SHIFT 0
+#define MAX9860_DVST_MIN 31
+
+/* MICGAIN */
+#define MAX9860_PAM 0x60
+#define MAX9860_PAM_SHIFT 5
+#define MAX9860_PAM_MAX 3
+#define MAX9860_PGAM 0x1f
+#define MAX9860_PGAM_SHIFT 0
+#define MAX9860_PGAM_MIN 20
+
+/* MICADC */
+#define MAX9860_AGCSRC 0x80
+#define MAX9860_AGCSRC_SHIFT 7
+#define MAX9860_AGCSRC_COUNT 2
+#define MAX9860_AGCRLS 0x70
+#define MAX9860_AGCRLS_SHIFT 4
+#define MAX9860_AGCRLS_COUNT 8
+#define MAX9860_AGCATK 0x0c
+#define MAX9860_AGCATK_SHIFT 2
+#define MAX9860_AGCATK_COUNT 4
+#define MAX9860_AGCHLD 0x03
+#define MAX9860_AGCHLD_OFF 0x00
+#define MAX9860_AGCHLD_SHIFT 0
+#define MAX9860_AGCHLD_COUNT 4
+
+/* NOISEGATE */
+#define MAX9860_ANTH 0xf0
+#define MAX9860_ANTH_SHIFT 4
+#define MAX9860_ANTH_MAX 15
+#define MAX9860_AGCTH 0x0f
+#define MAX9860_AGCTH_SHIFT 0
+#define MAX9860_AGCTH_MIN 15
+
+/* PWRMAN */
+#define MAX9860_SHDN 0x80
+#define MAX9860_DACEN 0x08
+#define MAX9860_DACEN_SHIFT 3
+#define MAX9860_ADCLEN 0x02
+#define MAX9860_ADCLEN_SHIFT 1
+#define MAX9860_ADCREN 0x01
+#define MAX9860_ADCREN_SHIFT 0
+
+#endif /* _SND_SOC_MAX9860 */
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
new file mode 100644
index 000000000000..9cad9b698cf2
--- /dev/null
+++ b/sound/soc/codecs/max9867.c
@@ -0,0 +1,718 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MAX9867 ALSA SoC codec driver
+//
+// Copyright 2013-2015 Maxim Integrated Products
+// Copyright 2018 Ladislav Michl <ladis@linux-mips.org>
+//
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max9867.h"
+
+struct max9867_priv {
+ struct clk *mclk;
+ struct regmap *regmap;
+ const struct snd_pcm_hw_constraint_list *constraints;
+ unsigned int sysclk, pclk;
+ bool provider, dsp_a;
+ unsigned int adc_dac_active;
+};
+
+static const char *const max9867_spmode[] = {
+ "Stereo Diff", "Mono Diff",
+ "Stereo Cap", "Mono Cap",
+ "Stereo Single", "Mono Single",
+ "Stereo Single Fast", "Mono Single Fast"
+};
+static const char *const max9867_filter_text[] = {"IIR", "FIR"};
+
+static const char *const max9867_adc_dac_filter_text[] = {
+ "Disabled",
+ "Elliptical/16/256",
+ "Butterworth/16/500",
+ "Elliptical/8/256",
+ "Butterworth/8/500",
+ "Butterworth/8-24"
+};
+
+enum max9867_adc_dac {
+ MAX9867_ADC_LEFT,
+ MAX9867_ADC_RIGHT,
+ MAX9867_DAC_LEFT,
+ MAX9867_DAC_RIGHT,
+};
+
+static int max9867_adc_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ enum max9867_adc_dac adc_dac;
+
+ if (!snd_soc_dapm_widget_name_cmp(w, "ADCL"))
+ adc_dac = MAX9867_ADC_LEFT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "ADCR"))
+ adc_dac = MAX9867_ADC_RIGHT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "DACL"))
+ adc_dac = MAX9867_DAC_LEFT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "DACR"))
+ adc_dac = MAX9867_DAC_RIGHT;
+ else
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ max9867->adc_dac_active |= BIT(adc_dac);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ max9867->adc_dac_active &= ~BIT(adc_dac);
+
+ return 0;
+}
+
+static int max9867_filter_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, &reg);
+ if (ret)
+ return -EINVAL;
+
+ if (reg & MAX9867_CODECFLTR_MODE)
+ ucontrol->value.enumerated.item[0] = 1;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+
+ return 0;
+}
+
+static int max9867_filter_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, mode = ucontrol->value.enumerated.item[0];
+ int ret;
+
+ if (mode > 1)
+ return -EINVAL;
+
+ /* don't allow change if ADC/DAC active */
+ if (max9867->adc_dac_active)
+ return -EBUSY;
+
+ /* read current filter mode */
+ ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, &reg);
+ if (ret)
+ return -EINVAL;
+
+ if (mode)
+ mode = MAX9867_CODECFLTR_MODE;
+
+ /* check if change is needed */
+ if ((reg & MAX9867_CODECFLTR_MODE) == mode)
+ return 0;
+
+ /* shutdown codec before switching filter mode */
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_PWRMAN_SHDN, 0);
+
+ /* switch filter mode */
+ regmap_update_bits(max9867->regmap, MAX9867_CODECFLTR,
+ MAX9867_CODECFLTR_MODE, mode);
+
+ /* out of shutdown now */
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_PWRMAN_SHDN, MAX9867_PWRMAN_SHDN);
+
+ return 0;
+}
+
+static SOC_ENUM_SINGLE_EXT_DECL(max9867_filter, max9867_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0,
+ max9867_adc_dac_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4,
+ max9867_adc_dac_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0,
+ max9867_spmode);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1),
+ 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0),
+ 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0),
+ 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0),
+ 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0),
+);
+static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0);
+static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0);
+static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1),
+ 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
+);
+
+static const struct snd_kcontrol_new max9867_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL,
+ MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv),
+ SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL,
+ MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv),
+ SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv),
+ SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv),
+ SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1),
+ SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1,
+ max9867_dac_tlv),
+ SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0,
+ max9867_dacboost_tlv),
+ SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1,
+ max9867_adc_tlv),
+ SOC_ENUM("Speaker Mode", max9867_spkmode),
+ SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
+ SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0),
+ SOC_ENUM_EXT("DSP Filter", max9867_filter, max9867_filter_get, max9867_filter_set),
+ SOC_ENUM("ADC Filter", max9867_adc_filter),
+ SOC_ENUM("DAC Filter", max9867_dac_filter),
+ SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0),
+};
+
+/* Input mixer */
+static const struct snd_kcontrol_new max9867_input_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0),
+ SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0),
+};
+
+/* Output mixer */
+static const struct snd_kcontrol_new max9867_output_mixer_controls[] = {
+ SOC_DAPM_DOUBLE_R("Line Bypass Switch",
+ MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1),
+};
+
+/* Sidetone mixer */
+static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0),
+};
+
+/* Line out switch */
+static const struct snd_kcontrol_new max9867_line_out_control =
+ SOC_DAPM_DOUBLE_R("Switch",
+ MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1);
+
+/* DMIC mux */
+static const char *const dmic_mux_text[] = {
+ "ADC", "DMIC"
+};
+static SOC_ENUM_SINGLE_DECL(left_dmic_mux_enum,
+ MAX9867_MICCONFIG, 5, dmic_mux_text);
+static SOC_ENUM_SINGLE_DECL(right_dmic_mux_enum,
+ MAX9867_MICCONFIG, 4, dmic_mux_text);
+static const struct snd_kcontrol_new max9867_left_dmic_mux =
+ SOC_DAPM_ENUM("DMICL Mux", left_dmic_mux_enum);
+static const struct snd_kcontrol_new max9867_right_dmic_mux =
+ SOC_DAPM_ENUM("DMICR Mux", right_dmic_mux_enum);
+
+static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("DMICL"),
+ SND_SOC_DAPM_INPUT("DMICR"),
+ SND_SOC_DAPM_INPUT("LINL"),
+ SND_SOC_DAPM_INPUT("LINR"),
+
+ SND_SOC_DAPM_PGA("Left Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
+ max9867_input_mixer_controls,
+ ARRAY_SIZE(max9867_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("DMICL Mux", SND_SOC_NOPM, 0, 0,
+ &max9867_left_dmic_mux),
+ SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0,
+ &max9867_right_dmic_mux),
+ SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0,
+ max9867_sidetone_mixer_controls,
+ ARRAY_SIZE(max9867_sidetone_mixer_controls)),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0,
+ max9867_output_mixer_controls,
+ ARRAY_SIZE(max9867_output_mixer_controls)),
+ SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0,
+ max9867_adc_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0,
+ &max9867_line_out_control),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+};
+
+static const struct snd_soc_dapm_route max9867_audio_map[] = {
+ {"Left Line Input", NULL, "LINL"},
+ {"Right Line Input", NULL, "LINR"},
+ {"Input Mixer", "Mic Capture Switch", "MICL"},
+ {"Input Mixer", "Mic Capture Switch", "MICR"},
+ {"Input Mixer", "Line Capture Switch", "Left Line Input"},
+ {"Input Mixer", "Line Capture Switch", "Right Line Input"},
+ {"DMICL Mux", "DMIC", "DMICL"},
+ {"DMICR Mux", "DMIC", "DMICR"},
+ {"DMICL Mux", "ADC", "Input Mixer"},
+ {"DMICR Mux", "ADC", "Input Mixer"},
+ {"ADCL", NULL, "DMICL Mux"},
+ {"ADCR", NULL, "DMICR Mux"},
+
+ {"Digital", "Sidetone Switch", "ADCL"},
+ {"Digital", "Sidetone Switch", "ADCR"},
+ {"DACL", NULL, "Digital"},
+ {"DACR", NULL, "Digital"},
+
+ {"Output Mixer", "Line Bypass Switch", "Left Line Input"},
+ {"Output Mixer", "Line Bypass Switch", "Right Line Input"},
+ {"Output Mixer", NULL, "DACL"},
+ {"Output Mixer", NULL, "DACR"},
+ {"Master Playback", "Switch", "Output Mixer"},
+ {"LOUT", NULL, "Master Playback"},
+ {"ROUT", NULL, "Master Playback"},
+};
+
+static const unsigned int max9867_rates_44k1[] = {
+ 11025, 22050, 44100,
+};
+
+static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = {
+ .list = max9867_rates_44k1,
+ .count = ARRAY_SIZE(max9867_rates_44k1),
+};
+
+static const unsigned int max9867_rates_48k[] = {
+ 8000, 16000, 32000, 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = {
+ .list = max9867_rates_48k,
+ .count = ARRAY_SIZE(max9867_rates_48k),
+};
+
+static int max9867_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct max9867_priv *max9867 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ if (max9867->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, max9867->constraints);
+
+ return 0;
+}
+
+static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ int value, freq = 0;
+ unsigned long int rate, ratio;
+ struct snd_soc_component *component = dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params),
+ max9867->pclk);
+
+ /* set up the ni value */
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+ MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8);
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+ MAX9867_NI_LOW_MASK, 0x00FF & ni);
+ if (max9867->provider) {
+ if (max9867->dsp_a) {
+ value = MAX9867_IFC1B_48X;
+ } else {
+ rate = params_rate(params) * 2 * params_width(params);
+ ratio = max9867->pclk / rate;
+ switch (params_width(params)) {
+ case 8:
+ case 16:
+ switch (ratio) {
+ case 2:
+ value = MAX9867_IFC1B_PCLK_2;
+ break;
+ case 4:
+ value = MAX9867_IFC1B_PCLK_4;
+ break;
+ case 8:
+ value = MAX9867_IFC1B_PCLK_8;
+ break;
+ case 16:
+ value = MAX9867_IFC1B_PCLK_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 24:
+ value = MAX9867_IFC1B_48X;
+ break;
+ case 32:
+ value = MAX9867_IFC1B_64X;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
+ MAX9867_IFC1B_BCLK_MASK, value);
+
+ /* Exact integer mode available for 8kHz and 16kHz sample rates
+ * and certain PCLK (prescaled MCLK) values.
+ */
+ if (params_rate(params) == 8000 ||
+ params_rate(params) == 16000) {
+ switch (max9867->pclk) {
+ case 12000000:
+ freq = 0x08;
+ break;
+ case 13000000:
+ freq = 0x0A;
+ break;
+ case 16000000:
+ freq = 0x0C;
+ break;
+ case 19200000:
+ freq = 0x0E;
+ break;
+ }
+ }
+ if (freq && params_rate(params) == 16000)
+ freq++;
+
+ /* If exact integer mode not available, the freq value
+ * remains zero, i.e. normal mode is used.
+ */
+ regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
+ MAX9867_FREQ_MASK, freq);
+ } else {
+ /*
+ * digital pll locks on to any externally supplied LRCLK signal
+ * and also enable rapid lock mode.
+ */
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+ MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+ MAX9867_PLL, MAX9867_PLL);
+ }
+ return 0;
+}
+
+static int max9867_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+
+ return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
+ 1 << 6, !!mute << 6);
+}
+
+static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ int value = 0;
+
+ /* Set the prescaler based on the master clock frequency*/
+ if (freq >= 10000000 && freq <= 20000000) {
+ value |= MAX9867_PSCLK_10_20;
+ max9867->pclk = freq;
+ } else if (freq >= 20000000 && freq <= 40000000) {
+ value |= MAX9867_PSCLK_20_40;
+ max9867->pclk = freq / 2;
+ } else if (freq >= 40000000 && freq <= 60000000) {
+ value |= MAX9867_PSCLK_40_60;
+ max9867->pclk = freq / 4;
+ } else {
+ dev_err(component->dev,
+ "Invalid clock frequency %uHz (required 10-60MHz)\n",
+ freq);
+ return -EINVAL;
+ }
+ if (freq % 48000 == 0)
+ max9867->constraints = &max9867_constraints_48k;
+ else if (freq % 44100 == 0)
+ max9867->constraints = &max9867_constraints_44k1;
+ else
+ dev_warn(component->dev,
+ "Unable to set exact rate with %uHz clock frequency\n",
+ freq);
+ max9867->sysclk = freq;
+ value = value << MAX9867_PSCLK_SHIFT;
+ regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
+ MAX9867_PSCLK_MASK, value);
+ return 0;
+}
+
+static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ u8 iface1A, iface1B;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max9867->provider = true;
+ iface1A = MAX9867_MASTER;
+ iface1B = MAX9867_IFC1B_48X;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max9867->provider = false;
+ iface1A = iface1B = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ max9867->dsp_a = false;
+ iface1A |= MAX9867_I2S_DLY;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ max9867->dsp_a = true;
+ iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion bits, BCI and WCI */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface1A |= MAX9867_BCI_MODE;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface1A |= MAX9867_WCI_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
+ regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
+ MAX9867_IFC1B_BCLK_MASK, iface1B);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max9867_dai_ops = {
+ .set_sysclk = max9867_set_dai_sysclk,
+ .set_fmt = max9867_dai_set_fmt,
+ .mute_stream = max9867_mute,
+ .startup = max9867_startup,
+ .hw_params = max9867_dai_hw_params,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver max9867_dai[] = {
+ {
+ .name = "max9867-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &max9867_dai_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+#ifdef CONFIG_PM
+static int max9867_suspend(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max9867_resume(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define max9867_suspend NULL
+#define max9867_resume NULL
+#endif
+
+static int max9867_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int err;
+ struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ err = clk_prepare_enable(max9867->mclk);
+ if (err)
+ return err;
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ err = regcache_sync(max9867->regmap);
+ if (err)
+ return err;
+
+ err = regmap_write(max9867->regmap,
+ MAX9867_PWRMAN, 0xff);
+ if (err)
+ return err;
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ err = regmap_write(max9867->regmap, MAX9867_PWRMAN, 0);
+ if (err)
+ return err;
+
+ regcache_mark_dirty(max9867->regmap);
+ clk_disable_unprepare(max9867->mclk);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver max9867_component = {
+ .controls = max9867_snd_controls,
+ .num_controls = ARRAY_SIZE(max9867_snd_controls),
+ .dapm_routes = max9867_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
+ .dapm_widgets = max9867_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
+ .suspend = max9867_suspend,
+ .resume = max9867_resume,
+ .set_bias_level = max9867_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static bool max9867_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9867_STATUS:
+ case MAX9867_JACKSTATUS:
+ case MAX9867_AUXHIGH:
+ case MAX9867_AUXLOW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max9867_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX9867_REVISION,
+ .volatile_reg = max9867_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9867_i2c_probe(struct i2c_client *i2c)
+{
+ struct max9867_priv *max9867;
+ int ret, reg;
+
+ max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL);
+ if (!max9867)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max9867);
+ max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap);
+ if (IS_ERR(max9867->regmap)) {
+ ret = PTR_ERR(max9867->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_read(max9867->regmap, MAX9867_REVISION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: %d\n", ret);
+ return ret;
+ }
+ dev_info(&i2c->dev, "device revision: %x\n", reg);
+ ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component,
+ max9867_dai, ARRAY_SIZE(max9867_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ max9867->mclk = devm_clk_get(&i2c->dev, NULL);
+ if (IS_ERR(max9867->mclk))
+ return PTR_ERR(max9867->mclk);
+
+ return 0;
+}
+
+static const struct i2c_device_id max9867_i2c_id[] = {
+ { "max9867" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max9867_of_match[] = {
+ { .compatible = "maxim,max9867", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9867_of_match);
+#endif
+
+static struct i2c_driver max9867_i2c_driver = {
+ .driver = {
+ .name = "max9867",
+ .of_match_table = of_match_ptr(max9867_of_match),
+ },
+ .probe = max9867_i2c_probe,
+ .id_table = max9867_i2c_id,
+};
+
+module_i2c_driver(max9867_i2c_driver);
+
+MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
+MODULE_DESCRIPTION("ASoC MAX9867 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
new file mode 100644
index 000000000000..b6b880631b13
--- /dev/null
+++ b/sound/soc/codecs/max9867.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max9867.h -- MAX9867 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 Maxim Integrated Products
+ */
+
+#ifndef _MAX9867_H
+#define _MAX9867_H
+
+/* MAX9867 register space */
+
+#define MAX9867_STATUS 0x00
+#define MAX9867_JACKSTATUS 0x01
+#define MAX9867_AUXHIGH 0x02
+#define MAX9867_AUXLOW 0x03
+#define MAX9867_INTEN 0x04
+#define MAX9867_SYSCLK 0x05
+#define MAX9867_FREQ_MASK 0xF
+#define MAX9867_PSCLK_SHIFT 0x4
+#define MAX9867_PSCLK_WIDTH 0x2
+#define MAX9867_PSCLK_MASK (0x03<<MAX9867_PSCLK_SHIFT)
+#define MAX9867_PSCLK_10_20 0x1
+#define MAX9867_PSCLK_20_40 0x2
+#define MAX9867_PSCLK_40_60 0x3
+#define MAX9867_AUDIOCLKHIGH 0x06
+#define MAX9867_NI_HIGH_MASK 0x7F
+#define MAX9867_NI_LOW_MASK 0xFE
+#define MAX9867_PLL (1<<7)
+#define MAX9867_AUDIOCLKLOW 0x07
+#define MAX9867_RAPID_LOCK 0x01
+#define MAX9867_IFC1A 0x08
+#define MAX9867_MASTER (1<<7)
+#define MAX9867_I2S_DLY (1<<4)
+#define MAX9867_SDOUT_HIZ (1<<3)
+#define MAX9867_TDM_MODE (1<<2)
+#define MAX9867_WCI_MODE (1<<6)
+#define MAX9867_BCI_MODE (1<<5)
+#define MAX9867_IFC1B 0x09
+#define MAX9867_IFC1B_BCLK_MASK 7
+#define MAX9867_IFC1B_64X 0x01
+#define MAX9867_IFC1B_48X 0x02
+#define MAX9867_IFC1B_PCLK_2 0x04
+#define MAX9867_IFC1B_PCLK_4 0x05
+#define MAX9867_IFC1B_PCLK_8 0x06
+#define MAX9867_IFC1B_PCLK_16 0x07
+#define MAX9867_CODECFLTR 0x0a
+#define MAX9867_CODECFLTR_MODE (1<<7)
+#define MAX9867_SIDETONE 0x0b
+#define MAX9867_DACLEVEL 0x0c
+#define MAX9867_ADCLEVEL 0x0d
+#define MAX9867_LEFTLINELVL 0x0e
+#define MAX9867_RIGHTLINELVL 0x0f
+#define MAX9867_LEFTVOL 0x10
+#define MAX9867_RIGHTVOL 0x11
+#define MAX9867_LEFTMICGAIN 0x12
+#define MAX9867_RIGHTMICGAIN 0x13
+#define MAX9867_INPUTCONFIG 0x14
+#define MAX9867_MICCONFIG 0x15
+#define MAX9867_MODECONFIG 0x16
+#define MAX9867_PWRMAN 0x17
+#define MAX9867_PWRMAN_SHDN (1<<7)
+#define MAX9867_REVISION 0xff
+
+#define MAX9867_CACHEREGNUM 10
+
+#endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 61cc18e35efb..1bd0d4761ca6 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max9877.c -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
@@ -138,8 +133,7 @@ static const struct regmap_config max9877_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9877_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max9877_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int i;
@@ -157,7 +151,7 @@ static int max9877_i2c_probe(struct i2c_client *client,
}
static const struct i2c_device_id max9877_i2c_id[] = {
- { "max9877", 0 },
+ { "max9877" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
index 6da72290ac58..3c2788175a71 100644
--- a/sound/soc/codecs/max9877.h
+++ b/sound/soc/codecs/max9877.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* max9877.h -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _MAX9877_H
@@ -32,6 +27,4 @@
#define MAX9877_BYPASS (1 << 6)
#define MAX9877_SHDN (1 << 7)
-extern int max9877_add_controls(struct snd_soc_codec *codec);
-
#endif
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index 5990de317999..124af6408d96 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -1,9 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -99,8 +97,8 @@ static const struct snd_kcontrol_new max98925_dai_sel_mux =
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -256,7 +254,7 @@ static const struct {
},
};
-static inline int max98925_rate_value(struct snd_soc_codec *codec,
+static inline int max98925_rate_value(struct snd_soc_component *component,
int rate, int clock, int *value, int *n, int *m)
{
int ret = -EINVAL;
@@ -297,32 +295,29 @@ static void max98925_set_sense_data(struct max98925_priv *max98925)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
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 */
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
regmap_update_bits(max98925->regmap,
MAX98925_DAI_CLK_MODE2,
M98925_DAI_MAS_MASK, 0);
max98925_set_sense_data(max98925);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
/*
- * set left channel DAI to master mode,
- * right channel always slave
+ * set left channel DAI to provider mode,
+ * right channel always consumer
*/
regmap_update_bits(max98925->regmap,
MAX98925_DAI_CLK_MODE2,
M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK);
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(component->dev, "DAI clock mode unsupported");
return -EINVAL;
}
@@ -339,7 +334,7 @@ static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai,
invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported");
+ dev_err(component->dev, "DAI invert mode unsupported");
return -EINVAL;
}
@@ -352,7 +347,7 @@ 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;
+ struct snd_soc_component *component = max98925->component;
int rate = params_rate(params);
/* BCLK/LRCLK ratio calculation */
int blr_clk_ratio = params_channels(params) * max98925->ch_size;
@@ -395,12 +390,12 @@ static int max98925_set_clock(struct max98925_priv *max98925,
mdll = M98925_MDLL_MULT_MCLKx8;
break;
default:
- dev_info(max98925->codec->dev, "unsupported sysclk %d\n",
+ dev_info(max98925->component->dev, "unsupported sysclk %d\n",
max98925->sysclk);
return -EINVAL;
}
- if (max98925_rate_value(codec, rate, clock, &dai_sr, &n, &m))
+ if (max98925_rate_value(component, rate, clock, &dai_sr, &n, &m))
return -EINVAL;
/* set DAI_SR to correct LRCLK frequency */
@@ -427,8 +422,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
switch (params_width(params)) {
case 16:
@@ -454,7 +449,7 @@ static int max98925_dai_hw_params(struct snd_pcm_substream *substream,
__func__, params_format(params));
return -EINVAL;
}
- dev_dbg(codec->dev, "%s: format supported %d",
+ dev_dbg(component->dev, "%s: format supported %d",
__func__, params_format(params));
return max98925_set_clock(max98925, params);
}
@@ -462,8 +457,8 @@ static int max98925_dai_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case 0:
@@ -516,11 +511,11 @@ static struct snd_soc_dai_driver max98925_dai[] = {
}
};
-static int max98925_probe(struct snd_soc_codec *codec)
+static int max98925_probe(struct snd_soc_component *component)
{
- struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component);
- max98925->codec = codec;
+ max98925->component = component;
regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00);
/* It's not the default but we need to set DAI_DLY */
regmap_write(max98925->regmap,
@@ -538,14 +533,17 @@ static int max98925_probe(struct snd_soc_codec *codec)
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 snd_soc_component_driver soc_component_dev_max98925 = {
+ .probe = max98925_probe,
+ .controls = max98925_snd_controls,
+ .num_controls = ARRAY_SIZE(max98925_snd_controls),
+ .dapm_routes = max98925_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98925_audio_map),
+ .dapm_widgets = max98925_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config max98925_regmap = {
@@ -559,8 +557,7 @@ static const struct regmap_config max98925_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98925_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98925_i2c_probe(struct i2c_client *i2c)
{
int ret, reg;
u32 value;
@@ -577,7 +574,7 @@ static int max98925_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(max98925->regmap);
dev_err(&i2c->dev,
"Failed to allocate regmap: %d\n", ret);
- goto err_out;
+ return ret;
}
if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
@@ -594,53 +591,51 @@ static int max98925_i2c_probe(struct i2c_client *i2c,
}
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 = regmap_read(max98925->regmap, MAX98925_REV_VERSION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Read revision failed\n");
+ return ret;
+ }
+
+ if ((reg != MAX98925_VERSION) && (reg != MAX98925_VERSION1)) {
+ ret = -ENODEV;
+ dev_err(&i2c->dev, "Invalid revision (%d 0x%02X)\n",
ret, reg);
- goto err_out;
+ return ret;
}
+
dev_info(&i2c->dev, "device version 0x%02X\n", reg);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98925,
max98925_dai, ARRAY_SIZE(max98925_dai));
if (ret < 0)
dev_err(&i2c->dev,
- "Failed to register codec: %d\n", ret);
-err_out:
+ "Failed to register component: %d\n", ret);
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 },
+ { "max98925" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98925_i2c_id);
+#ifdef CONFIG_OF
static const struct of_device_id max98925_of_match[] = {
{ .compatible = "maxim,max98925", },
{ }
};
MODULE_DEVICE_TABLE(of, max98925_of_match);
+#endif
static struct i2c_driver max98925_i2c_driver = {
.driver = {
.name = "max98925",
.of_match_table = of_match_ptr(max98925_of_match),
- .pm = NULL,
},
- .probe = max98925_i2c_probe,
- .remove = max98925_i2c_remove,
+ .probe = max98925_i2c_probe,
.id_table = max98925_i2c_id,
};
diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h
index 3783248f2780..6d55ccad27f9 100644
--- a/sound/soc/codecs/max98925.h
+++ b/sound/soc/codecs/max98925.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -821,7 +818,7 @@
struct max98925_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct max98925_pdata *pdata;
unsigned int sysclk;
unsigned int v_slot;
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
new file mode 100644
index 000000000000..ae962bda163e
--- /dev/null
+++ b/sound/soc/codecs/max98926.c
@@ -0,0 +1,593 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max98926.c -- ALSA SoC MAX98926 driver
+ * Copyright 2013-15 Maxim Integrated Products
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98926.h"
+
+static const char * const max98926_boost_voltage_txt[] = {
+ "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
+ "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
+};
+
+static const char *const max98926_pdm_ch_text[] = {
+ "Current", "Voltage",
+};
+
+static const char *const max98926_hpf_cutoff_txt[] = {
+ "Disable", "DC Block", "100Hz",
+ "200Hz", "400Hz", "800Hz",
+};
+
+static const struct reg_default max98926_reg[] = {
+ { 0x0B, 0x00 }, /* IRQ Enable0 */
+ { 0x0C, 0x00 }, /* IRQ Enable1 */
+ { 0x0D, 0x00 }, /* IRQ Enable2 */
+ { 0x0E, 0x00 }, /* IRQ Clear0 */
+ { 0x0F, 0x00 }, /* IRQ Clear1 */
+ { 0x10, 0x00 }, /* IRQ Clear2 */
+ { 0x11, 0xC0 }, /* Map0 */
+ { 0x12, 0x00 }, /* Map1 */
+ { 0x13, 0x00 }, /* Map2 */
+ { 0x14, 0xF0 }, /* Map3 */
+ { 0x15, 0x00 }, /* Map4 */
+ { 0x16, 0xAB }, /* Map5 */
+ { 0x17, 0x89 }, /* Map6 */
+ { 0x18, 0x00 }, /* Map7 */
+ { 0x19, 0x00 }, /* Map8 */
+ { 0x1A, 0x04 }, /* DAI Clock Mode 1 */
+ { 0x1B, 0x00 }, /* DAI Clock Mode 2 */
+ { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
+ { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
+ { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
+ { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
+ { 0x20, 0x50 }, /* Format */
+ { 0x21, 0x00 }, /* TDM Slot Select */
+ { 0x22, 0x00 }, /* DOUT Configuration VMON */
+ { 0x23, 0x00 }, /* DOUT Configuration IMON */
+ { 0x24, 0x00 }, /* DOUT Configuration VBAT */
+ { 0x25, 0x00 }, /* DOUT Configuration VBST */
+ { 0x26, 0x00 }, /* DOUT Configuration FLAG */
+ { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
+ { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
+ { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
+ { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
+ { 0x2B, 0x02 }, /* DOUT Drive Strength */
+ { 0x2C, 0x90 }, /* Filters */
+ { 0x2D, 0x00 }, /* Gain */
+ { 0x2E, 0x02 }, /* Gain Ramping */
+ { 0x2F, 0x00 }, /* Speaker Amplifier */
+ { 0x30, 0x0A }, /* Threshold */
+ { 0x31, 0x00 }, /* ALC Attack */
+ { 0x32, 0x80 }, /* ALC Atten and Release */
+ { 0x33, 0x00 }, /* ALC Infinite Hold Release */
+ { 0x34, 0x92 }, /* ALC Configuration */
+ { 0x35, 0x01 }, /* Boost Converter */
+ { 0x36, 0x00 }, /* Block Enable */
+ { 0x37, 0x00 }, /* Configuration */
+ { 0x38, 0x00 }, /* Global Enable */
+ { 0x3A, 0x00 }, /* Boost Limiter */
+};
+
+static const struct soc_enum max98926_voltage_enum[] = {
+ SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0,
+ ARRAY_SIZE(max98926_pdm_ch_text),
+ max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_voltage_control =
+ SOC_DAPM_ENUM("Route", max98926_voltage_enum);
+
+static const struct soc_enum max98926_current_enum[] = {
+ SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_SOURCE_1_SHIFT,
+ ARRAY_SIZE(max98926_pdm_ch_text),
+ max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_current_control =
+ SOC_DAPM_ENUM("Route", max98926_current_enum);
+
+static const struct snd_kcontrol_new max98926_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 0, 0),
+ SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new max98926_dai_controls[] = {
+ SOC_DAPM_SINGLE("Left", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 0, 0),
+ SOC_DAPM_SINGLE("Right", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 2, 0),
+ SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 3, 0),
+};
+
+static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE,
+ MAX98926_SPK_EN_SHIFT, 0),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE,
+ MAX98926_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE,
+ MAX98926_ADC_IMON_EN_WIDTH |
+ MAX98926_ADC_VMON_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE,
+ MAX98926_BST_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 0,
+ &max98926_mixer_controls[0],
+ ARRAY_SIZE(max98926_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DAI Sel",
+ MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0,
+ &max98926_dai_controls[0],
+ ARRAY_SIZE(max98926_dai_controls)),
+ SND_SOC_DAPM_MUX("PDM CH1 Source",
+ MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CURRENT_SHIFT,
+ 0, &max98926_current_control),
+ SND_SOC_DAPM_MUX("PDM CH0 Source",
+ MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_VOLTAGE_SHIFT,
+ 0, &max98926_voltage_control),
+};
+
+static const struct snd_soc_dapm_route max98926_audio_map[] = {
+ {"VI Enable", NULL, "DAI_OUT"},
+ {"DAI Sel", "Left", "VI Enable"},
+ {"DAI Sel", "Right", "VI Enable"},
+ {"DAI Sel", "LeftRight", "VI Enable"},
+ {"DAI Sel", "LeftRightDiv2", "VI Enable"},
+ {"PCM Sel", "PCM", "DAI Sel"},
+
+ {"PDM CH1 Source", "Current", "DAI_OUT"},
+ {"PDM CH1 Source", "Voltage", "DAI_OUT"},
+ {"PDM CH0 Source", "Current", "DAI_OUT"},
+ {"PDM CH0 Source", "Voltage", "DAI_OUT"},
+ {"PCM Sel", "Analog", "PDM CH1 Source"},
+ {"PCM Sel", "Analog", "PDM CH0 Source"},
+ {"Amp Enable", NULL, "PCM Sel"},
+
+ {"BST Enable", NULL, "Amp Enable"},
+ {"BE_OUT", NULL, "BST Enable"},
+};
+
+static bool max98926_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98926_VBAT_DATA:
+ case MAX98926_VBST_DATA:
+ case MAX98926_LIVE_STATUS0:
+ case MAX98926_LIVE_STATUS1:
+ case MAX98926_LIVE_STATUS2:
+ case MAX98926_STATE0:
+ case MAX98926_STATE1:
+ case MAX98926_STATE2:
+ case MAX98926_FLAG0:
+ case MAX98926_FLAG1:
+ case MAX98926_FLAG2:
+ case MAX98926_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98926_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98926_IRQ_CLEAR0:
+ case MAX98926_IRQ_CLEAR1:
+ case MAX98926_IRQ_CLEAR2:
+ case MAX98926_ALC_HOLD_RLS:
+ return false;
+ default:
+ return true;
+ }
+};
+
+static DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0);
+static DECLARE_TLV_DB_RANGE(max98926_current_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0),
+ 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff,
+ MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT,
+ max98926_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage,
+ MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT,
+ max98926_boost_voltage_txt);
+
+static const struct snd_kcontrol_new max98926_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN,
+ MAX98926_SPK_GAIN_SHIFT,
+ (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0,
+ max98926_spk_tlv),
+ SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD,
+ MAX98926_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD,
+ MAX98926_ALC_TH_SHIFT,
+ (1<<MAX98926_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM("Boost Output Voltage", max98926_boost_voltage),
+ SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER,
+ MAX98926_BST_ILIM_SHIFT,
+ (1<<MAX98926_BST_ILIM_SHIFT)-1, 0,
+ max98926_current_tlv),
+ SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff),
+ SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_1_SHIFT,
+ MAX98926_PDM_CHANNEL_1_HIZ, 1, 0),
+ SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_0_SHIFT,
+ MAX98926_PDM_CHANNEL_0_HIZ, 1, 0),
+};
+
+static const struct {
+ int rate;
+ int sr;
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ },
+};
+
+static void max98926_set_sense_data(struct max98926_priv *max98926)
+{
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VMON,
+ MAX98926_DAI_VMON_EN_MASK,
+ MAX98926_DAI_VMON_EN_MASK);
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_IMON,
+ MAX98926_DAI_IMON_EN_MASK,
+ MAX98926_DAI_IMON_EN_MASK);
+
+ if (!max98926->interleave_mode) {
+ /* set VMON slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VMON,
+ MAX98926_DAI_VMON_SLOT_MASK,
+ max98926->v_slot);
+ /* set IMON slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_IMON,
+ MAX98926_DAI_IMON_SLOT_MASK,
+ max98926->i_slot);
+ } else {
+ /* enable interleave mode */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_INTERLEAVE_MASK,
+ MAX98926_DAI_INTERLEAVE_MASK);
+ /* set interleave slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VBAT,
+ MAX98926_DAI_INTERLEAVE_SLOT_MASK,
+ max98926->v_slot);
+ }
+}
+
+static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max98926_set_sense_data(max98926);
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert = MAX98926_DAI_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98926_DAI_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_write(max98926->regmap,
+ MAX98926_FORMAT, MAX98926_DAI_DLY_MASK);
+ regmap_update_bits(max98926->regmap, MAX98926_FORMAT,
+ MAX98926_DAI_BCI_MASK, invert);
+ return 0;
+}
+
+static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int dai_sr = -EINVAL;
+ int rate = params_rate(params), i;
+ struct snd_soc_component *component = dai->component;
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
+ int blr_clk_ratio;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_16);
+ max98926->ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_24);
+ max98926->ch_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_32);
+ max98926->ch_size = 32;
+ break;
+ default:
+ dev_dbg(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ /* BCLK/LRCLK ratio calculation */
+ blr_clk_ratio = params_channels(params) * max98926->ch_size;
+
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* find the closest rate */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ dai_sr = rate_table[i].sr;
+ break;
+ }
+ }
+ if (dai_sr < 0)
+ return -EINVAL;
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT);
+ return 0;
+}
+
+#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98926_dai_ops = {
+ .set_fmt = max98926_dai_set_fmt,
+ .hw_params = max98926_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98926_dai[] = {
+{
+ .name = "max98926-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98926_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98926_FORMATS,
+ },
+ .ops = &max98926_dai_ops,
+}
+};
+
+static int max98926_probe(struct snd_soc_component *component)
+{
+ struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component);
+
+ max98926->component = component;
+
+ /* Hi-Z all the slots */
+ regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0);
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_max98926 = {
+ .probe = max98926_probe,
+ .controls = max98926_snd_controls,
+ .num_controls = ARRAY_SIZE(max98926_snd_controls),
+ .dapm_routes = max98926_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98926_audio_map),
+ .dapm_widgets = max98926_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98926_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98926_VERSION,
+ .reg_defaults = max98926_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98926_reg),
+ .volatile_reg = max98926_volatile_register,
+ .readable_reg = max98926_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98926_i2c_probe(struct i2c_client *i2c)
+{
+ int ret, reg;
+ u32 value;
+ struct max98926_priv *max98926;
+
+ max98926 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98926), GFP_KERNEL);
+ if (!max98926)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98926);
+ max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap);
+ if (IS_ERR(max98926->regmap)) {
+ ret = PTR_ERR(max98926->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ goto err_out;
+ }
+ if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode") ||
+ of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
+ max98926->interleave_mode = true;
+
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
+ if (value > MAX98926_DAI_VMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "vmon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98926->v_slot = value;
+ }
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
+ if (value > MAX98926_DAI_IMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "imon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98926->i_slot = value;
+ }
+ ret = regmap_read(max98926->regmap,
+ MAX98926_VERSION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: %x\n", reg);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98926,
+ max98926_dai, ARRAY_SIZE(max98926_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register component: %d\n", ret);
+ dev_info(&i2c->dev, "device version: %x\n", reg);
+err_out:
+ return ret;
+}
+
+static const struct i2c_device_id max98926_i2c_id[] = {
+ { "max98926" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98926_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98926_of_match[] = {
+ { .compatible = "maxim,max98926", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98926_of_match);
+#endif
+
+static struct i2c_driver max98926_i2c_driver = {
+ .driver = {
+ .name = "max98926",
+ .of_match_table = of_match_ptr(max98926_of_match),
+ },
+ .probe = max98926_i2c_probe,
+ .id_table = max98926_i2c_id,
+};
+
+module_i2c_driver(max98926_i2c_driver)
+MODULE_DESCRIPTION("ALSA SoC MAX98926 driver");
+MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h
new file mode 100644
index 000000000000..d622d5f4384c
--- /dev/null
+++ b/sound/soc/codecs/max98926.h
@@ -0,0 +1,846 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * max98926.h -- MAX98926 ALSA SoC Audio driver
+ * Copyright 2013-2015 Maxim Integrated Products
+ */
+
+#ifndef _MAX98926_H
+#define _MAX98926_H
+
+#define MAX98926_CHIP_VERSION 0x40
+#define MAX98926_CHIP_VERSION1 0x50
+
+#define MAX98926_VBAT_DATA 0x00
+#define MAX98926_VBST_DATA 0x01
+#define MAX98926_LIVE_STATUS0 0x02
+#define MAX98926_LIVE_STATUS1 0x03
+#define MAX98926_LIVE_STATUS2 0x04
+#define MAX98926_STATE0 0x05
+#define MAX98926_STATE1 0x06
+#define MAX98926_STATE2 0x07
+#define MAX98926_FLAG0 0x08
+#define MAX98926_FLAG1 0x09
+#define MAX98926_FLAG2 0x0A
+#define MAX98926_IRQ_ENABLE0 0x0B
+#define MAX98926_IRQ_ENABLE1 0x0C
+#define MAX98926_IRQ_ENABLE2 0x0D
+#define MAX98926_IRQ_CLEAR0 0x0E
+#define MAX98926_IRQ_CLEAR1 0x0F
+#define MAX98926_IRQ_CLEAR2 0x10
+#define MAX98926_MAP0 0x11
+#define MAX98926_MAP1 0x12
+#define MAX98926_MAP2 0x13
+#define MAX98926_MAP3 0x14
+#define MAX98926_MAP4 0x15
+#define MAX98926_MAP5 0x16
+#define MAX98926_MAP6 0x17
+#define MAX98926_MAP7 0x18
+#define MAX98926_MAP8 0x19
+#define MAX98926_DAI_CLK_MODE1 0x1A
+#define MAX98926_DAI_CLK_MODE2 0x1B
+#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C
+#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D
+#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E
+#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F
+#define MAX98926_FORMAT 0x20
+#define MAX98926_TDM_SLOT_SELECT 0x21
+#define MAX98926_DOUT_CFG_VMON 0x22
+#define MAX98926_DOUT_CFG_IMON 0x23
+#define MAX98926_DOUT_CFG_VBAT 0x24
+#define MAX98926_DOUT_CFG_VBST 0x25
+#define MAX98926_DOUT_CFG_FLAG 0x26
+#define MAX98926_DOUT_HIZ_CFG1 0x27
+#define MAX98926_DOUT_HIZ_CFG2 0x28
+#define MAX98926_DOUT_HIZ_CFG3 0x29
+#define MAX98926_DOUT_HIZ_CFG4 0x2A
+#define MAX98926_DOUT_DRV_STRENGTH 0x2B
+#define MAX98926_FILTERS 0x2C
+#define MAX98926_GAIN 0x2D
+#define MAX98926_GAIN_RAMPING 0x2E
+#define MAX98926_SPK_AMP 0x2F
+#define MAX98926_THRESHOLD 0x30
+#define MAX98926_ALC_ATTACK 0x31
+#define MAX98926_ALC_ATTEN_RLS 0x32
+#define MAX98926_ALC_HOLD_RLS 0x33
+#define MAX98926_ALC_CONFIGURATION 0x34
+#define MAX98926_BOOST_CONVERTER 0x35
+#define MAX98926_BLOCK_ENABLE 0x36
+#define MAX98926_CONFIGURATION 0x37
+#define MAX98926_GLOBAL_ENABLE 0x38
+#define MAX98926_BOOST_LIMITER 0x3A
+#define MAX98926_VERSION 0xFF
+
+#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1)
+
+#define MAX98926_PDM_CURRENT_MASK (1<<7)
+#define MAX98926_PDM_CURRENT_SHIFT 7
+#define MAX98926_PDM_VOLTAGE_MASK (1<<3)
+#define MAX98926_PDM_VOLTAGE_SHIFT 3
+#define MAX98926_PDM_CHANNEL_0_MASK (1<<2)
+#define MAX98926_PDM_CHANNEL_0_SHIFT 2
+#define MAX98926_PDM_CHANNEL_1_MASK (1<<6)
+#define MAX98926_PDM_CHANNEL_1_SHIFT 6
+#define MAX98926_PDM_CHANNEL_1_HIZ 5
+#define MAX98926_PDM_CHANNEL_0_HIZ 1
+#define MAX98926_PDM_SOURCE_0_SHIFT 0
+#define MAX98926_PDM_SOURCE_0_MASK (1<<0)
+#define MAX98926_PDM_SOURCE_1_MASK (1<<4)
+#define MAX98926_PDM_SOURCE_1_SHIFT 4
+
+/* MAX98926 Register Bit Fields */
+
+/* MAX98926_R002_LIVE_STATUS0 */
+#define MAX98926_THERMWARN_STATUS_MASK (1<<3)
+#define MAX98926_THERMWARN_STATUS_SHIFT 3
+#define MAX98926_THERMWARN_STATUS_WIDTH 1
+#define MAX98926_THERMSHDN_STATUS_MASK (1<<1)
+#define MAX98926_THERMSHDN_STATUS_SHIFT 1
+#define MAX98926_THERMSHDN_STATUS_WIDTH 1
+
+/* MAX98926_R003_LIVE_STATUS1 */
+#define MAX98926_SPKCURNT_STATUS_MASK (1<<5)
+#define MAX98926_SPKCURNT_STATUS_SHIFT 5
+#define MAX98926_SPKCURNT_STATUS_WIDTH 1
+#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4)
+#define MAX98926_WATCHFAIL_STATUS_SHIFT 4
+#define MAX98926_WATCHFAIL_STATUS_WIDTH 1
+#define MAX98926_ALCINFH_STATUS_MASK (1<<3)
+#define MAX98926_ALCINFH_STATUS_SHIFT 3
+#define MAX98926_ALCINFH_STATUS_WIDTH 1
+#define MAX98926_ALCACT_STATUS_MASK (1<<2)
+#define MAX98926_ALCACT_STATUS_SHIFT 2
+#define MAX98926_ALCACT_STATUS_WIDTH 1
+#define MAX98926_ALCMUT_STATUS_MASK (1<<1)
+#define MAX98926_ALCMUT_STATUS_SHIFT 1
+#define MAX98926_ALCMUT_STATUS_WIDTH 1
+#define MAX98926_ACLP_STATUS_MASK (1<<0)
+#define MAX98926_ACLP_STATUS_SHIFT 0
+#define MAX98926_ACLP_STATUS_WIDTH 1
+
+/* MAX98926_R004_LIVE_STATUS2 */
+#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6)
+#define MAX98926_SLOTOVRN_STATUS_SHIFT 6
+#define MAX98926_SLOTOVRN_STATUS_WIDTH 1
+#define MAX98926_INVALSLOT_STATUS_MASK (1<<5)
+#define MAX98926_INVALSLOT_STATUS_SHIFT 5
+#define MAX98926_INVALSLOT_STATUS_WIDTH 1
+#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4
+#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1
+#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3)
+#define MAX98926_VBSTOVFL_STATUS_SHIFT 3
+#define MAX98926_VBSTOVFL_STATUS_WIDTH 1
+#define MAX98926_VBATOVFL_STATUS_MASK (1<<2)
+#define MAX98926_VBATOVFL_STATUS_SHIFT 2
+#define MAX98926_VBATOVFL_STATUS_WIDTH 1
+#define MAX98926_IMONOVFL_STATUS_MASK (1<<1)
+#define MAX98926_IMONOVFL_STATUS_SHIFT 1
+#define MAX98926_IMONOVFL_STATUS_WIDTH 1
+#define MAX98926_VMONOVFL_STATUS_MASK (1<<0)
+#define MAX98926_VMONOVFL_STATUS_SHIFT 0
+#define MAX98926_VMONOVFL_STATUS_WIDTH 1
+
+/* MAX98926_R005_STATE0 */
+#define MAX98926_THERMWARN_END_STATE_MASK (1<<3)
+#define MAX98926_THERMWARN_END_STATE_SHIFT 3
+#define MAX98926_THERMWARN_END_STATE_WIDTH 1
+#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1
+#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1
+#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_STATE_SHIFT 1
+#define MAX98926_THERMSHDN_END_STATE_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1
+
+/* MAX98926_R006_STATE1 */
+#define MAX98926_SPRCURNT_STATE_MASK (1<<5)
+#define MAX98926_SPRCURNT_STATE_SHIFT 5
+#define MAX98926_SPRCURNT_STATE_WIDTH 1
+#define MAX98926_WATCHFAIL_STATE_MASK (1<<4)
+#define MAX98926_WATCHFAIL_STATE_SHIFT 4
+#define MAX98926_WATCHFAIL_STATE_WIDTH 1
+#define MAX98926_ALCINFH_STATE_MASK (1<<3)
+#define MAX98926_ALCINFH_STATE_SHIFT 3
+#define MAX98926_ALCINFH_STATE_WIDTH 1
+#define MAX98926_ALCACT_STATE_MASK (1<<2)
+#define MAX98926_ALCACT_STATE_SHIFT 2
+#define MAX98926_ALCACT_STATE_WIDTH 1
+#define MAX98926_ALCMUT_STATE_MASK (1<<1)
+#define MAX98926_ALCMUT_STATE_SHIFT 1
+#define MAX98926_ALCMUT_STATE_WIDTH 1
+#define MAX98926_ALCP_STATE_MASK (1<<0)
+#define MAX98926_ALCP_STATE_SHIFT 0
+#define MAX98926_ALCP_STATE_WIDTH 1
+
+/* MAX98926_R007_STATE2 */
+#define MAX98926_SLOTOVRN_STATE_MASK (1<<6)
+#define MAX98926_SLOTOVRN_STATE_SHIFT 6
+#define MAX98926_SLOTOVRN_STATE_WIDTH 1
+#define MAX98926_INVALSLOT_STATE_MASK (1<<5)
+#define MAX98926_INVALSLOT_STATE_SHIFT 5
+#define MAX98926_INVALSLOT_STATE_WIDTH 1
+#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_STATE_SHIFT 4
+#define MAX98926_SLOTCNFLT_STATE_WIDTH 1
+#define MAX98926_VBSTOVFL_STATE_MASK (1<<3)
+#define MAX98926_VBSTOVFL_STATE_SHIFT 3
+#define MAX98926_VBSTOVFL_STATE_WIDTH 1
+#define MAX98926_VBATOVFL_STATE_MASK (1<<2)
+#define MAX98926_VBATOVFL_STATE_SHIFT 2
+#define MAX98926_VBATOVFL_STATE_WIDTH 1
+#define MAX98926_IMONOVFL_STATE_MASK (1<<1)
+#define MAX98926_IMONOVFL_STATE_SHIFT 1
+#define MAX98926_IMONOVFL_STATE_WIDTH 1
+#define MAX98926_VMONOVFL_STATE_MASK (1<<0)
+#define MAX98926_VMONOVFL_STATE_SHIFT 0
+#define MAX98926_VMONOVFL_STATE_WIDTH 1
+
+/* MAX98926_R008_FLAG0 */
+#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3)
+#define MAX98926_THERMWARN_END_FLAG_SHIFT 3
+#define MAX98926_THERMWARN_END_FLAG_WIDTH 1
+#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2
+#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1
+#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1
+#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1
+
+/* MAX98926_R009_FLAG1 */
+#define MAX98926_SPKCURNT_FLAG_MASK (1<<5)
+#define MAX98926_SPKCURNT_FLAG_SHIFT 5
+#define MAX98926_SPKCURNT_FLAG_WIDTH 1
+#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4)
+#define MAX98926_WATCHFAIL_FLAG_SHIFT 4
+#define MAX98926_WATCHFAIL_FLAG_WIDTH 1
+#define MAX98926_ALCINFH_FLAG_MASK (1<<3)
+#define MAX98926_ALCINFH_FLAG_SHIFT 3
+#define MAX98926_ALCINFH_FLAG_WIDTH 1
+#define MAX98926_ALCACT_FLAG_MASK (1<<2)
+#define MAX98926_ALCACT_FLAG_SHIFT 2
+#define MAX98926_ALCACT_FLAG_WIDTH 1
+#define MAX98926_ALCMUT_FLAG_MASK (1<<1)
+#define MAX98926_ALCMUT_FLAG_SHIFT 1
+#define MAX98926_ALCMUT_FLAG_WIDTH 1
+#define MAX98926_ALCP_FLAG_MASK (1<<0)
+#define MAX98926_ALCP_FLAG_SHIFT 0
+#define MAX98926_ALCP_FLAG_WIDTH 1
+
+/* MAX98926_R00A_FLAG2 */
+#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6)
+#define MAX98926_SLOTOVRN_FLAG_SHIFT 6
+#define MAX98926_SLOTOVRN_FLAG_WIDTH 1
+#define MAX98926_INVALSLOT_FLAG_MASK (1<<5)
+#define MAX98926_INVALSLOT_FLAG_SHIFT 5
+#define MAX98926_INVALSLOT_FLAG_WIDTH 1
+#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4
+#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1
+#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3)
+#define MAX98926_VBSTOVFL_FLAG_SHIFT 3
+#define MAX98926_VBSTOVFL_FLAG_WIDTH 1
+#define MAX98926_VBATOVFL_FLAG_MASK (1<<2)
+#define MAX98926_VBATOVFL_FLAG_SHIFT 2
+#define MAX98926_VBATOVFL_FLAG_WIDTH 1
+#define MAX98926_IMONOVFL_FLAG_MASK (1<<1)
+#define MAX98926_IMONOVFL_FLAG_SHIFT 1
+#define MAX98926_IMONOVFL_FLAG_WIDTH 1
+#define MAX98926_VMONOVFL_FLAG_MASK (1<<0)
+#define MAX98926_VMONOVFL_FLAG_SHIFT 0
+#define MAX98926_VMONOVFL_FLAG_WIDTH 1
+
+/* MAX98926_R00B_IRQ_ENABLE0 */
+#define MAX98926_THERMWARN_END_EN_MASK (1<<3)
+#define MAX98926_THERMWARN_END_EN_SHIFT 3
+#define MAX98926_THERMWARN_END_EN_WIDTH 1
+#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_EN_SHIFT 2
+#define MAX98926_THERMWARN_BGN_EN_WIDTH 1
+#define MAX98926_THERMSHDN_END_EN_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_EN_SHIFT 1
+#define MAX98926_THERMSHDN_END_EN_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1
+
+/* MAX98926_R00C_IRQ_ENABLE1 */
+#define MAX98926_SPKCURNT_EN_MASK (1<<5)
+#define MAX98926_SPKCURNT_EN_SHIFT 5
+#define MAX98926_SPKCURNT_EN_WIDTH 1
+#define MAX98926_WATCHFAIL_EN_MASK (1<<4)
+#define MAX98926_WATCHFAIL_EN_SHIFT 4
+#define MAX98926_WATCHFAIL_EN_WIDTH 1
+#define MAX98926_ALCINFH_EN_MASK (1<<3)
+#define MAX98926_ALCINFH_EN_SHIFT 3
+#define MAX98926_ALCINFH_EN_WIDTH 1
+#define MAX98926_ALCACT_EN_MASK (1<<2)
+#define MAX98926_ALCACT_EN_SHIFT 2
+#define MAX98926_ALCACT_EN_WIDTH 1
+#define MAX98926_ALCMUT_EN_MASK (1<<1)
+#define MAX98926_ALCMUT_EN_SHIFT 1
+#define MAX98926_ALCMUT_EN_WIDTH 1
+#define MAX98926_ALCP_EN_MASK (1<<0)
+#define MAX98926_ALCP_EN_SHIFT 0
+#define MAX98926_ALCP_EN_WIDTH 1
+
+/* MAX98926_R00D_IRQ_ENABLE2 */
+#define MAX98926_SLOTOVRN_EN_MASK (1<<6)
+#define MAX98926_SLOTOVRN_EN_SHIFT 6
+#define MAX98926_SLOTOVRN_EN_WIDTH 1
+#define MAX98926_INVALSLOT_EN_MASK (1<<5)
+#define MAX98926_INVALSLOT_EN_SHIFT 5
+#define MAX98926_INVALSLOT_EN_WIDTH 1
+#define MAX98926_SLOTCNFLT_EN_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_EN_SHIFT 4
+#define MAX98926_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_VBSTOVFL_EN_MASK (1<<3)
+#define MAX98926_VBSTOVFL_EN_SHIFT 3
+#define MAX98926_VBSTOVFL_EN_WIDTH 1
+#define MAX98926_VBATOVFL_EN_MASK (1<<2)
+#define MAX98926_VBATOVFL_EN_SHIFT 2
+#define MAX98926_VBATOVFL_EN_WIDTH 1
+#define MAX98926_IMONOVFL_EN_MASK (1<<1)
+#define MAX98926_IMONOVFL_EN_SHIFT 1
+#define MAX98926_IMONOVFL_EN_WIDTH 1
+#define MAX98926_VMONOVFL_EN_MASK (1<<0)
+#define MAX98926_VMONOVFL_EN_SHIFT 0
+#define MAX98926_VMONOVFL_EN_WIDTH 1
+
+/* MAX98926_R00E_IRQ_CLEAR0 */
+#define MAX98926_THERMWARN_END_CLR_MASK (1<<3)
+#define MAX98926_THERMWARN_END_CLR_SHIFT 3
+#define MAX98926_THERMWARN_END_CLR_WIDTH 1
+#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2
+#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1
+#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_CLR_SHIFT 1
+#define MAX98926_THERMSHDN_END_CLR_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1
+
+/* MAX98926_R00F_IRQ_CLEAR1 */
+#define MAX98926_SPKCURNT_CLR_MASK (1<<5)
+#define MAX98926_SPKCURNT_CLR_SHIFT 5
+#define MAX98926_SPKCURNT_CLR_WIDTH 1
+#define MAX98926_WATCHFAIL_CLR_MASK (1<<4)
+#define MAX98926_WATCHFAIL_CLR_SHIFT 4
+#define MAX98926_WATCHFAIL_CLR_WIDTH 1
+#define MAX98926_ALCINFH_CLR_MASK (1<<3)
+#define MAX98926_ALCINFH_CLR_SHIFT 3
+#define MAX98926_ALCINFH_CLR_WIDTH 1
+#define MAX98926_ALCACT_CLR_MASK (1<<2)
+#define MAX98926_ALCACT_CLR_SHIFT 2
+#define MAX98926_ALCACT_CLR_WIDTH 1
+#define MAX98926_ALCMUT_CLR_MASK (1<<1)
+#define MAX98926_ALCMUT_CLR_SHIFT 1
+#define MAX98926_ALCMUT_CLR_WIDTH 1
+#define MAX98926_ALCP_CLR_MASK (1<<0)
+#define MAX98926_ALCP_CLR_SHIFT 0
+#define MAX98926_ALCP_CLR_WIDTH 1
+
+/* MAX98926_R010_IRQ_CLEAR2 */
+#define MAX98926_SLOTOVRN_CLR_MASK (1<<6)
+#define MAX98926_SLOTOVRN_CLR_SHIFT 6
+#define MAX98926_SLOTOVRN_CLR_WIDTH 1
+#define MAX98926_INVALSLOT_CLR_MASK (1<<5)
+#define MAX98926_INVALSLOT_CLR_SHIFT 5
+#define MAX98926_INVALSLOT_CLR_WIDTH 1
+#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_CLR_SHIFT 4
+#define MAX98926_SLOTCNFLT_CLR_WIDTH 1
+#define MAX98926_VBSTOVFL_CLR_MASK (1<<3)
+#define MAX98926_VBSTOVFL_CLR_SHIFT 3
+#define MAX98926_VBSTOVFL_CLR_WIDTH 1
+#define MAX98926_VBATOVFL_CLR_MASK (1<<2)
+#define MAX98926_VBATOVFL_CLR_SHIFT 2
+#define MAX98926_VBATOVFL_CLR_WIDTH 1
+#define MAX98926_IMONOVFL_CLR_MASK (1<<1)
+#define MAX98926_IMONOVFL_CLR_SHIFT 1
+#define MAX98926_IMONOVFL_CLR_WIDTH 1
+#define MAX98926_VMONOVFL_CLR_MASK (1<<0)
+#define MAX98926_VMONOVFL_CLR_SHIFT 0
+#define MAX98926_VMONOVFL_CLR_WIDTH 1
+
+/* MAX98926_R011_MAP0 */
+#define MAX98926_ER_THERMWARN_EN_MASK (1<<7)
+#define MAX98926_ER_THERMWARN_EN_SHIFT 7
+#define MAX98926_ER_THERMWARN_EN_WIDTH 1
+#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4)
+#define MAX98926_ER_THERMWARN_MAP_SHIFT 4
+#define MAX98926_ER_THERMWARN_MAP_WIDTH 3
+
+/* MAX98926_R012_MAP1 */
+#define MAX98926_ER_ALCMUT_EN_MASK (1<<7)
+#define MAX98926_ER_ALCMUT_EN_SHIFT 7
+#define MAX98926_ER_ALCMUT_EN_WIDTH 1
+#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_ALCMUT_MAP_SHIFT 4
+#define MAX98926_ER_ALCMUT_MAP_WIDTH 3
+#define MAX98926_ER_ALCP_EN_MASK (1<<3)
+#define MAX98926_ER_ALCP_EN_SHIFT 3
+#define MAX98926_ER_ALCP_EN_WIDTH 1
+#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0)
+#define MAX98926_ER_ALCP_MAP_SHIFT 0
+#define MAX98926_ER_ALCP_MAP_WIDTH 3
+
+/* MAX98926_R013_MAP2 */
+#define MAX98926_ER_ALCINFH_EN_MASK (1<<7)
+#define MAX98926_ER_ALCINFH_EN_SHIFT 7
+#define MAX98926_ER_ALCINFH_EN_WIDTH 1
+#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4)
+#define MAX98926_ER_ALCINFH_MAP_SHIFT 4
+#define MAX98926_ER_ALCINFH_MAP_WIDTH 3
+#define MAX98926_ER_ALCACT_EN_MASK (1<<3)
+#define MAX98926_ER_ALCACT_EN_SHIFT 3
+#define MAX98926_ER_ALCACT_EN_WIDTH 1
+#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0)
+#define MAX98926_ER_ALCACT_MAP_SHIFT 0
+#define MAX98926_ER_ALCACT_MAP_WIDTH 3
+
+/* MAX98926_R014_MAP3 */
+#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7)
+#define MAX98926_ER_SPKCURNT_EN_SHIFT 7
+#define MAX98926_ER_SPKCURNT_EN_WIDTH 1
+#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4
+#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3
+
+/* MAX98926_R015_MAP4 */
+/* RESERVED */
+
+/* MAX98926_R016_MAP5 */
+#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7)
+#define MAX98926_ER_IMONOVFL_EN_SHIFT 7
+#define MAX98926_ER_IMONOVFL_EN_WIDTH 1
+#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4)
+#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4
+#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3
+#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3)
+#define MAX98926_ER_VMONOVFL_EN_SHIFT 3
+#define MAX98926_ER_VMONOVFL_EN_WIDTH 1
+#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0)
+#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0
+#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3
+
+/* MAX98926_R017_MAP6 */
+#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7)
+#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7
+#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1
+#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4)
+#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4
+#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3
+#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3)
+#define MAX98926_ER_VBATOVFL_EN_SHIFT 3
+#define MAX98926_ER_VBATOVFL_EN_WIDTH 1
+#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0)
+#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0
+#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3
+
+/* MAX98926_R018_MAP7 */
+#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7)
+#define MAX98926_ER_INVALSLOT_EN_SHIFT 7
+#define MAX98926_ER_INVALSLOT_EN_WIDTH 1
+#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4
+#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3
+#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3)
+#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3
+#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0)
+#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0
+#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3
+
+/* MAX98926_R019_MAP8 */
+#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3)
+#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3
+#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1
+#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0)
+#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0
+#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3
+
+/* MAX98926_R01A_DAI_CLK_MODE1 */
+#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6)
+#define MAX98926_DAI_CLK_SOURCE_SHIFT 6
+#define MAX98926_DAI_CLK_SOURCE_WIDTH 1
+#define MAX98926_MDLL_MULT_MASK (0x0F<<0)
+#define MAX98926_MDLL_MULT_SHIFT 0
+#define MAX98926_MDLL_MULT_WIDTH 4
+
+#define MAX98926_MDLL_MULT_MCLKx8 6
+#define MAX98926_MDLL_MULT_MCLKx16 8
+
+/* MAX98926_R01B_DAI_CLK_MODE2 */
+#define MAX98926_DAI_SR_MASK (0x0F<<4)
+#define MAX98926_DAI_SR_SHIFT 4
+#define MAX98926_DAI_SR_WIDTH 4
+#define MAX98926_DAI_MAS_MASK (1<<3)
+#define MAX98926_DAI_MAS_SHIFT 3
+#define MAX98926_DAI_MAS_WIDTH 1
+#define MAX98926_DAI_BSEL_MASK (0x07<<0)
+#define MAX98926_DAI_BSEL_SHIFT 0
+#define MAX98926_DAI_BSEL_WIDTH 3
+
+#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT)
+
+/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */
+#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_M_MSBS_SHIFT 0
+#define MAX98926_DAI_M_MSBS_WIDTH 8
+
+/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */
+#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_M_LSBS_SHIFT 0
+#define MAX98926_DAI_M_LSBS_WIDTH 8
+
+/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */
+#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0)
+#define MAX98926_DAI_N_MSBS_SHIFT 0
+#define MAX98926_DAI_N_MSBS_WIDTH 7
+
+/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */
+#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_N_LSBS_SHIFT 0
+#define MAX98926_DAI_N_LSBS_WIDTH 8
+
+/* MAX98926_R020_FORMAT */
+#define MAX98926_DAI_CHANSZ_MASK (0x03<<6)
+#define MAX98926_DAI_CHANSZ_SHIFT 6
+#define MAX98926_DAI_CHANSZ_WIDTH 2
+#define MAX98926_DAI_INTERLEAVE_MASK (1<<5)
+#define MAX98926_DAI_INTERLEAVE_SHIFT 5
+#define MAX98926_DAI_INTERLEAVE_WIDTH 1
+#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4)
+#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4
+#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1
+#define MAX98926_DAI_WCI_MASK (1<<3)
+#define MAX98926_DAI_WCI_SHIFT 3
+#define MAX98926_DAI_WCI_WIDTH 1
+#define MAX98926_DAI_BCI_MASK (1<<2)
+#define MAX98926_DAI_BCI_SHIFT 2
+#define MAX98926_DAI_BCI_WIDTH 1
+#define MAX98926_DAI_DLY_MASK (1<<1)
+#define MAX98926_DAI_DLY_SHIFT 1
+#define MAX98926_DAI_DLY_WIDTH 1
+#define MAX98926_DAI_TDM_MASK (1<<0)
+#define MAX98926_DAI_TDM_SHIFT 0
+#define MAX98926_DAI_TDM_WIDTH 1
+
+#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT)
+
+/* MAX98926_R021_TDM_SLOT_SELECT */
+#define MAX98926_DAI_DO_EN_MASK (1<<7)
+#define MAX98926_DAI_DO_EN_SHIFT 7
+#define MAX98926_DAI_DO_EN_WIDTH 1
+#define MAX98926_DAI_DIN_EN_MASK (1<<6)
+#define MAX98926_DAI_DIN_EN_SHIFT 6
+#define MAX98926_DAI_DIN_EN_WIDTH 1
+#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3)
+#define MAX98926_DAI_INR_SOURCE_SHIFT 3
+#define MAX98926_DAI_INR_SOURCE_WIDTH 3
+#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0)
+#define MAX98926_DAI_INL_SOURCE_SHIFT 0
+#define MAX98926_DAI_INL_SOURCE_WIDTH 3
+
+/* MAX98926_R022_DOUT_CFG_VMON */
+#define MAX98926_DAI_VMON_EN_MASK (1<<5)
+#define MAX98926_DAI_VMON_EN_SHIFT 5
+#define MAX98926_DAI_VMON_EN_WIDTH 1
+#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VMON_SLOT_SHIFT 0
+#define MAX98926_DAI_VMON_SLOT_WIDTH 5
+
+#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT)
+
+/* MAX98926_R023_DOUT_CFG_IMON */
+#define MAX98926_DAI_IMON_EN_MASK (1<<5)
+#define MAX98926_DAI_IMON_EN_SHIFT 5
+#define MAX98926_DAI_IMON_EN_WIDTH 1
+#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_IMON_SLOT_SHIFT 0
+#define MAX98926_DAI_IMON_SLOT_WIDTH 5
+
+#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT)
+
+/* MAX98926_R024_DOUT_CFG_VBAT */
+#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0
+#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5
+
+/* MAX98926_R025_DOUT_CFG_VBST */
+#define MAX98926_DAI_VBST_EN_MASK (1<<5)
+#define MAX98926_DAI_VBST_EN_SHIFT 5
+#define MAX98926_DAI_VBST_EN_WIDTH 1
+#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VBST_SLOT_SHIFT 0
+#define MAX98926_DAI_VBST_SLOT_WIDTH 5
+
+/* MAX98926_R026_DOUT_CFG_FLAG */
+#define MAX98926_DAI_FLAG_EN_MASK (1<<5)
+#define MAX98926_DAI_FLAG_EN_SHIFT 5
+#define MAX98926_DAI_FLAG_EN_WIDTH 1
+#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_FLAG_SLOT_SHIFT 0
+#define MAX98926_DAI_FLAG_SLOT_WIDTH 5
+
+/* MAX98926_R027_DOUT_HIZ_CFG1 */
+#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8
+
+/* MAX98926_R028_DOUT_HIZ_CFG2 */
+#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8
+
+/* MAX98926_R029_DOUT_HIZ_CFG3 */
+#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8
+
+/* MAX98926_R02A_DOUT_HIZ_CFG4 */
+#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8
+
+/* MAX98926_R02B_DOUT_DRV_STRENGTH */
+#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0)
+#define MAX98926_DAI_OUT_DRIVE_SHIFT 0
+#define MAX98926_DAI_OUT_DRIVE_WIDTH 2
+
+/* MAX98926_R02C_FILTERS */
+#define MAX98926_ADC_DITHER_EN_MASK (1<<7)
+#define MAX98926_ADC_DITHER_EN_SHIFT 7
+#define MAX98926_ADC_DITHER_EN_WIDTH 1
+#define MAX98926_IV_DCB_EN_MASK (1<<6)
+#define MAX98926_IV_DCB_EN_SHIFT 6
+#define MAX98926_IV_DCB_EN_WIDTH 1
+#define MAX98926_DAC_DITHER_EN_MASK (1<<4)
+#define MAX98926_DAC_DITHER_EN_SHIFT 4
+#define MAX98926_DAC_DITHER_EN_WIDTH 1
+#define MAX98926_DAC_FILTER_MODE_MASK (1<<3)
+#define MAX98926_DAC_FILTER_MODE_SHIFT 3
+#define MAX98926_DAC_FILTER_MODE_WIDTH 1
+#define MAX98926_DAC_HPF_MASK (0x07<<0)
+#define MAX98926_DAC_HPF_SHIFT 0
+#define MAX98926_DAC_HPF_WIDTH 3
+#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT)
+
+/* MAX98926_R02D_GAIN */
+#define MAX98926_DAC_IN_SEL_MASK (0x03<<5)
+#define MAX98926_DAC_IN_SEL_SHIFT 5
+#define MAX98926_DAC_IN_SEL_WIDTH 2
+#define MAX98926_SPK_GAIN_MASK (0x1F<<0)
+#define MAX98926_SPK_GAIN_SHIFT 0
+#define MAX98926_SPK_GAIN_WIDTH 5
+
+#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT)
+
+/* MAX98926_R02E_GAIN_RAMPING */
+#define MAX98926_SPK_RMP_EN_MASK (1<<1)
+#define MAX98926_SPK_RMP_EN_SHIFT 1
+#define MAX98926_SPK_RMP_EN_WIDTH 1
+#define MAX98926_SPK_ZCD_EN_MASK (1<<0)
+#define MAX98926_SPK_ZCD_EN_SHIFT 0
+#define MAX98926_SPK_ZCD_EN_WIDTH 1
+
+/* MAX98926_R02F_SPK_AMP */
+#define MAX98926_SPK_MODE_MASK (1<<0)
+#define MAX98926_SPK_MODE_SHIFT 0
+#define MAX98926_SPK_MODE_WIDTH 1
+#define MAX98926_INSELECT_MODE_MASK (1<<1)
+#define MAX98926_INSELECT_MODE_SHIFT 1
+#define MAX98926_INSELECT_MODE_WIDTH 1
+
+/* MAX98926_R030_THRESHOLD */
+#define MAX98926_ALC_EN_MASK (1<<5)
+#define MAX98926_ALC_EN_SHIFT 5
+#define MAX98926_ALC_EN_WIDTH 1
+#define MAX98926_ALC_TH_MASK (0x1F<<0)
+#define MAX98926_ALC_TH_SHIFT 0
+#define MAX98926_ALC_TH_WIDTH 5
+
+/* MAX98926_R031_ALC_ATTACK */
+#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4)
+#define MAX98926_ALC_ATK_STEP_SHIFT 4
+#define MAX98926_ALC_ATK_STEP_WIDTH 4
+#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0)
+#define MAX98926_ALC_ATK_RATE_SHIFT 0
+#define MAX98926_ALC_ATK_RATE_WIDTH 3
+
+/* MAX98926_R032_ALC_ATTEN_RLS */
+#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4)
+#define MAX98926_ALC_MAX_ATTEN_SHIFT 4
+#define MAX98926_ALC_MAX_ATTEN_WIDTH 4
+#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0)
+#define MAX98926_ALC_RLS_RATE_SHIFT 0
+#define MAX98926_ALC_RLS_RATE_WIDTH 3
+
+/* MAX98926_R033_ALC_HOLD_RLS */
+#define MAX98926_ALC_RLS_TGR_MASK (1<<0)
+#define MAX98926_ALC_RLS_TGR_SHIFT 0
+#define MAX98926_ALC_RLS_TGR_WIDTH 1
+
+/* MAX98926_R034_ALC_CONFIGURATION */
+#define MAX98926_ALC_MUTE_EN_MASK (1<<7)
+#define MAX98926_ALC_MUTE_EN_SHIFT 7
+#define MAX98926_ALC_MUTE_EN_WIDTH 1
+#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4)
+#define MAX98926_ALC_MUTE_DLY_SHIFT 4
+#define MAX98926_ALC_MUTE_DLY_WIDTH 3
+#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0)
+#define MAX98926_ALC_RLS_DBT_SHIFT 0
+#define MAX98926_ALC_RLS_DBT_WIDTH 3
+
+/* MAX98926_R035_BOOST_CONVERTER */
+#define MAX98926_BST_SYNC_MASK (1<<7)
+#define MAX98926_BST_SYNC_SHIFT 7
+#define MAX98926_BST_SYNC_WIDTH 1
+#define MAX98926_BST_PHASE_MASK (0x03<<4)
+#define MAX98926_BST_PHASE_SHIFT 4
+#define MAX98926_BST_PHASE_WIDTH 2
+#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0)
+#define MAX98926_BST_SKIP_MODE_SHIFT 0
+#define MAX98926_BST_SKIP_MODE_WIDTH 2
+
+/* MAX98926_R036_BLOCK_ENABLE */
+#define MAX98926_BST_EN_MASK (1<<7)
+#define MAX98926_BST_EN_SHIFT 7
+#define MAX98926_BST_EN_WIDTH 1
+#define MAX98926_WATCH_EN_MASK (1<<6)
+#define MAX98926_WATCH_EN_SHIFT 6
+#define MAX98926_WATCH_EN_WIDTH 1
+#define MAX98926_CLKMON_EN_MASK (1<<5)
+#define MAX98926_CLKMON_EN_SHIFT 5
+#define MAX98926_CLKMON_EN_WIDTH 1
+#define MAX98926_SPK_EN_MASK (1<<4)
+#define MAX98926_SPK_EN_SHIFT 4
+#define MAX98926_SPK_EN_WIDTH 1
+#define MAX98926_ADC_VBST_EN_MASK (1<<3)
+#define MAX98926_ADC_VBST_EN_SHIFT 3
+#define MAX98926_ADC_VBST_EN_WIDTH 1
+#define MAX98926_ADC_VBAT_EN_MASK (1<<2)
+#define MAX98926_ADC_VBAT_EN_SHIFT 2
+#define MAX98926_ADC_VBAT_EN_WIDTH 1
+#define MAX98926_ADC_IMON_EN_MASK (1<<1)
+#define MAX98926_ADC_IMON_EN_SHIFT 1
+#define MAX98926_ADC_IMON_EN_WIDTH 1
+#define MAX98926_ADC_VMON_EN_MASK (1<<0)
+#define MAX98926_ADC_VMON_EN_SHIFT 0
+#define MAX98926_ADC_VMON_EN_WIDTH 1
+
+/* MAX98926_R037_CONFIGURATION */
+#define MAX98926_BST_VOUT_MASK (0x0F<<4)
+#define MAX98926_BST_VOUT_SHIFT 4
+#define MAX98926_BST_VOUT_WIDTH 4
+#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2)
+#define MAX98926_THERMWARN_LEVEL_SHIFT 2
+#define MAX98926_THERMWARN_LEVEL_WIDTH 2
+#define MAX98926_WATCH_TIME_MASK (0x03<<0)
+#define MAX98926_WATCH_TIME_SHIFT 0
+#define MAX98926_WATCH_TIME_WIDTH 2
+
+/* MAX98926_R038_GLOBAL_ENABLE */
+#define MAX98926_EN_MASK (1<<7)
+#define MAX98926_EN_SHIFT 7
+#define MAX98926_EN_WIDTH 1
+
+/* MAX98926_R03A_BOOST_LIMITER */
+#define MAX98926_BST_ILIM_MASK (0xF<<4)
+#define MAX98926_BST_ILIM_SHIFT 4
+#define MAX98926_BST_ILIM_WIDTH 4
+
+/* MAX98926_R0FF_VERSION */
+#define MAX98926_REV_ID_MASK (0xFF<<0)
+#define MAX98926_REV_ID_SHIFT 0
+#define MAX98926_REV_ID_WIDTH 8
+
+struct max98926_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ unsigned int sysclk;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int ch_size;
+ unsigned int interleave_mode;
+};
+#endif
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
new file mode 100644
index 000000000000..0e9b8970997c
--- /dev/null
+++ b/sound/soc/codecs/max98927.c
@@ -0,0 +1,914 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max98927.c -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2016-2017 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+#include <sound/tlv.h>
+#include "max98927.h"
+
+static const struct reg_default max98927_reg[] = {
+ {MAX98927_R0001_INT_RAW1, 0x00},
+ {MAX98927_R0002_INT_RAW2, 0x00},
+ {MAX98927_R0003_INT_RAW3, 0x00},
+ {MAX98927_R0004_INT_STATE1, 0x00},
+ {MAX98927_R0005_INT_STATE2, 0x00},
+ {MAX98927_R0006_INT_STATE3, 0x00},
+ {MAX98927_R0007_INT_FLAG1, 0x00},
+ {MAX98927_R0008_INT_FLAG2, 0x00},
+ {MAX98927_R0009_INT_FLAG3, 0x00},
+ {MAX98927_R000A_INT_EN1, 0x00},
+ {MAX98927_R000B_INT_EN2, 0x00},
+ {MAX98927_R000C_INT_EN3, 0x00},
+ {MAX98927_R000D_INT_FLAG_CLR1, 0x00},
+ {MAX98927_R000E_INT_FLAG_CLR2, 0x00},
+ {MAX98927_R000F_INT_FLAG_CLR3, 0x00},
+ {MAX98927_R0010_IRQ_CTRL, 0x00},
+ {MAX98927_R0011_CLK_MON, 0x00},
+ {MAX98927_R0012_WDOG_CTRL, 0x00},
+ {MAX98927_R0013_WDOG_RST, 0x00},
+ {MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH, 0x75},
+ {MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH, 0x8c},
+ {MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS, 0x08},
+ {MAX98927_R0017_PIN_CFG, 0x55},
+ {MAX98927_R0018_PCM_RX_EN_A, 0x00},
+ {MAX98927_R0019_PCM_RX_EN_B, 0x00},
+ {MAX98927_R001A_PCM_TX_EN_A, 0x00},
+ {MAX98927_R001B_PCM_TX_EN_B, 0x00},
+ {MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0x00},
+ {MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0x00},
+ {MAX98927_R001E_PCM_TX_CH_SRC_A, 0x00},
+ {MAX98927_R001F_PCM_TX_CH_SRC_B, 0x00},
+ {MAX98927_R0020_PCM_MODE_CFG, 0x40},
+ {MAX98927_R0021_PCM_MASTER_MODE, 0x00},
+ {MAX98927_R0022_PCM_CLK_SETUP, 0x22},
+ {MAX98927_R0023_PCM_SR_SETUP1, 0x00},
+ {MAX98927_R0024_PCM_SR_SETUP2, 0x00},
+ {MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, 0x00},
+ {MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, 0x00},
+ {MAX98927_R0027_ICC_RX_EN_A, 0x00},
+ {MAX98927_R0028_ICC_RX_EN_B, 0x00},
+ {MAX98927_R002B_ICC_TX_EN_A, 0x00},
+ {MAX98927_R002C_ICC_TX_EN_B, 0x00},
+ {MAX98927_R002E_ICC_HIZ_MANUAL_MODE, 0x00},
+ {MAX98927_R002F_ICC_TX_HIZ_EN_A, 0x00},
+ {MAX98927_R0030_ICC_TX_HIZ_EN_B, 0x00},
+ {MAX98927_R0031_ICC_LNK_EN, 0x00},
+ {MAX98927_R0032_PDM_TX_EN, 0x00},
+ {MAX98927_R0033_PDM_TX_HIZ_CTRL, 0x00},
+ {MAX98927_R0034_PDM_TX_CTRL, 0x00},
+ {MAX98927_R0035_PDM_RX_CTRL, 0x00},
+ {MAX98927_R0036_AMP_VOL_CTRL, 0x00},
+ {MAX98927_R0037_AMP_DSP_CFG, 0x02},
+ {MAX98927_R0038_TONE_GEN_DC_CFG, 0x00},
+ {MAX98927_R0039_DRE_CTRL, 0x01},
+ {MAX98927_R003A_AMP_EN, 0x00},
+ {MAX98927_R003B_SPK_SRC_SEL, 0x00},
+ {MAX98927_R003C_SPK_GAIN, 0x00},
+ {MAX98927_R003D_SSM_CFG, 0x04},
+ {MAX98927_R003E_MEAS_EN, 0x00},
+ {MAX98927_R003F_MEAS_DSP_CFG, 0x04},
+ {MAX98927_R0040_BOOST_CTRL0, 0x00},
+ {MAX98927_R0041_BOOST_CTRL3, 0x00},
+ {MAX98927_R0042_BOOST_CTRL1, 0x00},
+ {MAX98927_R0043_MEAS_ADC_CFG, 0x00},
+ {MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x01},
+ {MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x00},
+ {MAX98927_R0046_ADC_CH0_DIVIDE, 0x00},
+ {MAX98927_R0047_ADC_CH1_DIVIDE, 0x00},
+ {MAX98927_R0048_ADC_CH2_DIVIDE, 0x00},
+ {MAX98927_R0049_ADC_CH0_FILT_CFG, 0x00},
+ {MAX98927_R004A_ADC_CH1_FILT_CFG, 0x00},
+ {MAX98927_R004B_ADC_CH2_FILT_CFG, 0x00},
+ {MAX98927_R004C_MEAS_ADC_CH0_READ, 0x00},
+ {MAX98927_R004D_MEAS_ADC_CH1_READ, 0x00},
+ {MAX98927_R004E_MEAS_ADC_CH2_READ, 0x00},
+ {MAX98927_R0051_BROWNOUT_STATUS, 0x00},
+ {MAX98927_R0052_BROWNOUT_EN, 0x00},
+ {MAX98927_R0053_BROWNOUT_INFINITE_HOLD, 0x00},
+ {MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR, 0x00},
+ {MAX98927_R0055_BROWNOUT_LVL_HOLD, 0x00},
+ {MAX98927_R005A_BROWNOUT_LVL1_THRESH, 0x00},
+ {MAX98927_R005B_BROWNOUT_LVL2_THRESH, 0x00},
+ {MAX98927_R005C_BROWNOUT_LVL3_THRESH, 0x00},
+ {MAX98927_R005D_BROWNOUT_LVL4_THRESH, 0x00},
+ {MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS, 0x00},
+ {MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL, 0x00},
+ {MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL, 0x00},
+ {MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE, 0x00},
+ {MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT, 0x00},
+ {MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1, 0x00},
+ {MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2, 0x00},
+ {MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3, 0x00},
+ {MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT, 0x00},
+ {MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1, 0x00},
+ {MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2, 0x00},
+ {MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3, 0x00},
+ {MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT, 0x00},
+ {MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1, 0x00},
+ {MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2, 0x00},
+ {MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3, 0x00},
+ {MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT, 0x00},
+ {MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, 0x00},
+ {MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2, 0x00},
+ {MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3, 0x00},
+ {MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, 0x00},
+ {MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY, 0x00},
+ {MAX98927_R0084_ENV_TRACK_REL_RATE, 0x00},
+ {MAX98927_R0085_ENV_TRACK_HOLD_RATE, 0x00},
+ {MAX98927_R0086_ENV_TRACK_CTRL, 0x00},
+ {MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, 0x00},
+ {MAX98927_R00FF_GLOBAL_SHDN, 0x00},
+ {MAX98927_R0100_SOFT_RESET, 0x00},
+ {MAX98927_R01FF_REV_ID, 0x40},
+};
+
+static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+ unsigned int mode = 0;
+ unsigned int format = 0;
+ bool use_pdm = false;
+ unsigned int invert = 0;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ max98927->provider = false;
+ mode = MAX98927_PCM_MASTER_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ max98927->provider = true;
+ mode = MAX98927_PCM_MASTER_MODE_MASTER;
+ break;
+ default:
+ dev_err(component->dev, "DAI clock mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MASK, mode);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98927_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98927_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98927_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98927_PCM_FORMAT_TDM_MODE0;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ use_pdm = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ max98927->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ if (!use_pdm) {
+ /* pcm channel configuration */
+ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 0);
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 0);
+ } else {
+ /* pdm channel configuration */
+ regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 1);
+
+ regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 3);
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ 0);
+ }
+ return 0;
+}
+
+/* codec MCLK rate in master mode */
+static const int rate_table[] = {
+ 5644800, 6000000, 6144000, 6500000,
+ 9600000, 11289600, 12000000, 12288000,
+ 13000000, 19200000,
+};
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512,
+};
+
+static int max98927_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+static int max98927_set_clock(struct max98927_priv *max98927,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_component *component = max98927->component;
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98927->ch_size;
+ int value;
+
+ if (max98927->provider) {
+ int i;
+ /* match rate to closest value */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i] >= max98927->sysclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(rate_table)) {
+ dev_err(component->dev, "failed to find proper clock rate.\n");
+ return -EINVAL;
+ }
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MCLK_MASK,
+ i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+ }
+
+ if (!max98927->tdm_mode) {
+ /* BCLK configuration */
+ value = max98927_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK, value);
+ }
+ return 0;
+}
+
+static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98927->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_48000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98927->regmap, MAX98927_R0023_PCM_SR_SETUP1,
+ MAX98927_PCM_SR_SET1_SR_MASK, sampling_rate);
+ regmap_update_bits(max98927->regmap, MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ if (max98927->interleave_mode &&
+ sampling_rate > MAX98927_PCM_SR_SET1_SR_16000)
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
+ else
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+ return max98927_set_clock(max98927, params);
+err:
+ return -EINVAL;
+}
+
+static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+
+ max98927->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98927_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK, bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_write(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ rx_mask & 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R0019_PCM_RX_EN_B,
+ (rx_mask & 0xFF00) >> 8);
+
+ /* Tx slot configuration */
+ regmap_write(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A,
+ tx_mask & 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B,
+ (tx_mask & 0xFF00) >> 8);
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ ~tx_mask & 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+
+ max98927->sysclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98927_dai_ops = {
+ .set_sysclk = max98927_dai_set_sysclk,
+ .set_fmt = max98927_dai_set_fmt,
+ .hw_params = max98927_dai_hw_params,
+ .set_tdm_slot = max98927_dai_tdm_slot,
+};
+
+static int max98927_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ max98927->tdm_mode = false;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 1);
+ regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 0);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98927_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, 3,
+ max98927_switch_text);
+
+static const struct snd_kcontrol_new max98927_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98927_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98927_R003F_MEAS_DSP_CFG, 2, 1, 0);
+
+static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN,
+ 0, 0, max98927_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98927_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98927_R003E_MEAS_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98927_R003E_MEAS_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98927_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0);
+
+static bool max98927_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98927_R0001_INT_RAW1 ... MAX98927_R0028_ICC_RX_EN_B:
+ case MAX98927_R002B_ICC_TX_EN_A ... MAX98927_R002C_ICC_TX_EN_B:
+ case MAX98927_R002E_ICC_HIZ_MANUAL_MODE
+ ... MAX98927_R004E_MEAS_ADC_CH2_READ:
+ case MAX98927_R0051_BROWNOUT_STATUS
+ ... MAX98927_R0055_BROWNOUT_LVL_HOLD:
+ case MAX98927_R005A_BROWNOUT_LVL1_THRESH
+ ... MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE:
+ case MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT
+ ... MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ:
+ case MAX98927_R00FF_GLOBAL_SHDN:
+ case MAX98927_R0100_SOFT_RESET:
+ case MAX98927_R01FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98927_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98927_R0001_INT_RAW1 ... MAX98927_R0009_INT_FLAG3:
+ case MAX98927_R004C_MEAS_ADC_CH0_READ:
+ case MAX98927_R004D_MEAS_ADC_CH1_READ:
+ case MAX98927_R004E_MEAS_ADC_CH2_READ:
+ case MAX98927_R0051_BROWNOUT_STATUS:
+ case MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ:
+ case MAX98927_R01FF_REV_ID:
+ case MAX98927_R0100_SOFT_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98927_boost_voltage_text[] = {
+ "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+ "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+ "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+ "9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_boost_voltage,
+ MAX98927_R0040_BOOST_CTRL0, 0,
+ max98927_boost_voltage_text);
+
+static const char * const max98927_current_limit_text[] = {
+ "1.00A", "1.10A", "1.20A", "1.30A", "1.40A", "1.50A", "1.60A", "1.70A",
+ "1.80A", "1.90A", "2.00A", "2.10A", "2.20A", "2.30A", "2.40A", "2.50A",
+ "2.60A", "2.70A", "2.80A", "2.90A", "3.00A", "3.10A", "3.20A", "3.30A",
+ "3.40A", "3.50A", "3.60A", "3.70A", "3.80A", "3.90A", "4.00A", "4.10A"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
+ MAX98927_R0042_BOOST_CTRL1, 1,
+ max98927_current_limit_text);
+
+static const struct snd_kcontrol_new max98927_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, 0, 6, 0,
+ max98927_spk_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL,
+ 0, (1 << MAX98927_AMP_VOL_WIDTH) - 1, 0,
+ max98927_digital_tlv),
+ SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN,
+ MAX98927_BROWNOUT_DSP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG,
+ MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, MAX98927_DRE_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL,
+ MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
+ SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
+ SOC_ENUM("Current Limit", max98927_current_limit),
+};
+
+static const struct snd_soc_dapm_route max98927_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+};
+
+static struct snd_soc_dai_driver max98927_dai[] = {
+ {
+ .name = "max98927-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .ops = &max98927_dai_ops,
+ }
+};
+
+static int max98927_probe(struct snd_soc_component *component)
+{
+ struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component);
+
+ max98927->component = component;
+
+ /* Software Reset */
+ regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET,
+ MAX98927_SOFT_RESET);
+
+ /* IV default slot configuration */
+ regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ 0x80);
+ regmap_write(max98927->regmap, MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
+ 0x1);
+ /* Set inital volume (+13dB) */
+ regmap_write(max98927->regmap, MAX98927_R0036_AMP_VOL_CTRL, 0x38);
+ regmap_write(max98927->regmap, MAX98927_R003C_SPK_GAIN, 0x05);
+ /* Enable DC blocker */
+ regmap_write(max98927->regmap, MAX98927_R0037_AMP_DSP_CFG, 0x03);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98927->regmap, MAX98927_R003F_MEAS_DSP_CFG, 0xF7);
+ /* Boost Output Voltage & Current limit */
+ regmap_write(max98927->regmap, MAX98927_R0040_BOOST_CTRL0, 0x1C);
+ regmap_write(max98927->regmap, MAX98927_R0042_BOOST_CTRL1, 0x3E);
+ /* Measurement ADC config */
+ regmap_write(max98927->regmap, MAX98927_R0043_MEAS_ADC_CFG, 0x04);
+ regmap_write(max98927->regmap, MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00);
+ regmap_write(max98927->regmap, MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x24);
+ /* Brownout Level */
+ regmap_write(max98927->regmap, MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,
+ 0x06);
+ /* Envelope Tracking configuration */
+ regmap_write(max98927->regmap, MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,
+ 0x08);
+ regmap_write(max98927->regmap, MAX98927_R0086_ENV_TRACK_CTRL, 0x01);
+ regmap_write(max98927->regmap, MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
+ 0x10);
+
+ /* voltage, current slot configuration */
+ regmap_write(max98927->regmap, MAX98927_R001E_PCM_TX_CH_SRC_A,
+ (max98927->i_l_slot << MAX98927_PCM_TX_CH_SRC_A_I_SHIFT | max98927->v_l_slot) & 0xFF);
+
+ if (max98927->v_l_slot < 8) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->v_l_slot, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->v_l_slot,
+ 1 << max98927->v_l_slot);
+ } else {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->v_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->v_l_slot - 8),
+ 1 << (max98927->v_l_slot - 8));
+ }
+
+ if (max98927->i_l_slot < 8) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->i_l_slot, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->i_l_slot,
+ 1 << max98927->i_l_slot);
+ } else {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->i_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->i_l_slot - 8),
+ 1 << (max98927->i_l_slot - 8));
+ }
+
+ /* Set interleave mode */
+ if (max98927->interleave_mode)
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001F_PCM_TX_CH_SRC_B,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK);
+ return 0;
+}
+
+static int max98927_suspend(struct device *dev)
+{
+ struct max98927_priv *max98927 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98927->regmap, true);
+ regcache_mark_dirty(max98927->regmap);
+ return 0;
+}
+static int max98927_resume(struct device *dev)
+{
+ struct max98927_priv *max98927 = dev_get_drvdata(dev);
+
+ regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET,
+ MAX98927_SOFT_RESET);
+ regcache_cache_only(max98927->regmap, false);
+ regcache_sync(max98927->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops max98927_pm = {
+ SYSTEM_SLEEP_PM_OPS(max98927_suspend, max98927_resume)
+};
+
+static const struct snd_soc_component_driver soc_component_dev_max98927 = {
+ .probe = max98927_probe,
+ .controls = max98927_snd_controls,
+ .num_controls = ARRAY_SIZE(max98927_snd_controls),
+ .dapm_widgets = max98927_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets),
+ .dapm_routes = max98927_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98927_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config max98927_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98927_R01FF_REV_ID,
+ .reg_defaults = max98927_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98927_reg),
+ .readable_reg = max98927_readable_register,
+ .volatile_reg = max98927_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98927_slot_config(struct i2c_client *i2c,
+ struct max98927_priv *max98927)
+{
+ int value;
+ struct device *dev = &i2c->dev;
+
+ if (!device_property_read_u32(dev, "vmon-slot-no", &value))
+ max98927->v_l_slot = value & 0xF;
+ else
+ max98927->v_l_slot = 0;
+
+ if (!device_property_read_u32(dev, "imon-slot-no", &value))
+ max98927->i_l_slot = value & 0xF;
+ else
+ max98927->i_l_slot = 1;
+}
+
+static int max98927_i2c_probe(struct i2c_client *i2c)
+{
+
+ int ret = 0, value;
+ int reg = 0;
+ struct max98927_priv *max98927 = NULL;
+
+ max98927 = devm_kzalloc(&i2c->dev, sizeof(*max98927), GFP_KERNEL);
+ if (!max98927) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98927);
+
+ /* update interleave mode info */
+ if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode")) {
+ max98927->interleave_mode = true;
+ } else {
+ if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode",
+ &value))
+ if (value > 0)
+ max98927->interleave_mode = true;
+ }
+
+ /* regmap initialization */
+ max98927->regmap
+ = devm_regmap_init_i2c(i2c, &max98927_regmap);
+ if (IS_ERR(max98927->regmap)) {
+ ret = PTR_ERR(max98927->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ max98927->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max98927->reset_gpio)) {
+ ret = PTR_ERR(max98927->reset_gpio);
+ return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin");
+ }
+
+ if (max98927->reset_gpio) {
+ gpiod_set_value_cansleep(max98927->reset_gpio, 0);
+ /* Wait for i2c port to be ready */
+ usleep_range(5000, 6000);
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98927->regmap, MAX98927_R01FF_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98927 revisionID: 0x%02X\n", reg);
+
+ /* voltage/current slot configuration */
+ max98927_slot_config(i2c, max98927);
+
+ /* codec registeration */
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98927,
+ max98927_dai, ARRAY_SIZE(max98927_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
+
+ return ret;
+}
+
+static void max98927_i2c_remove(struct i2c_client *i2c)
+{
+ struct max98927_priv *max98927 = i2c_get_clientdata(i2c);
+
+ if (max98927->reset_gpio)
+ gpiod_set_value_cansleep(max98927->reset_gpio, 1);
+}
+
+static const struct i2c_device_id max98927_i2c_id[] = {
+ { "max98927"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98927_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98927_of_match[] = {
+ { .compatible = "maxim,max98927", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98927_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98927_acpi_match[] = {
+ { "MX98927", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98927_acpi_match);
+#endif
+
+static struct i2c_driver max98927_i2c_driver = {
+ .driver = {
+ .name = "max98927",
+ .of_match_table = of_match_ptr(max98927_of_match),
+ .acpi_match_table = ACPI_PTR(max98927_acpi_match),
+ .pm = pm_ptr(&max98927_pm),
+ },
+ .probe = max98927_i2c_probe,
+ .remove = max98927_i2c_remove,
+ .id_table = max98927_i2c_id,
+};
+
+module_i2c_driver(max98927_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98927 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
new file mode 100644
index 000000000000..2353910f5f17
--- /dev/null
+++ b/sound/soc/codecs/max98927.h
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * max98927.h -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2016-2017 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ */
+#ifndef _MAX98927_H
+#define _MAX98927_H
+
+/* Register Values */
+#define MAX98927_R0001_INT_RAW1 0x0001
+#define MAX98927_R0002_INT_RAW2 0x0002
+#define MAX98927_R0003_INT_RAW3 0x0003
+#define MAX98927_R0004_INT_STATE1 0x0004
+#define MAX98927_R0005_INT_STATE2 0x0005
+#define MAX98927_R0006_INT_STATE3 0x0006
+#define MAX98927_R0007_INT_FLAG1 0x0007
+#define MAX98927_R0008_INT_FLAG2 0x0008
+#define MAX98927_R0009_INT_FLAG3 0x0009
+#define MAX98927_R000A_INT_EN1 0x000A
+#define MAX98927_R000B_INT_EN2 0x000B
+#define MAX98927_R000C_INT_EN3 0x000C
+#define MAX98927_R000D_INT_FLAG_CLR1 0x000D
+#define MAX98927_R000E_INT_FLAG_CLR2 0x000E
+#define MAX98927_R000F_INT_FLAG_CLR3 0x000F
+#define MAX98927_R0010_IRQ_CTRL 0x0010
+#define MAX98927_R0011_CLK_MON 0x0011
+#define MAX98927_R0012_WDOG_CTRL 0x0012
+#define MAX98927_R0013_WDOG_RST 0x0013
+#define MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH 0x0014
+#define MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH 0x0015
+#define MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS 0x0016
+#define MAX98927_R0017_PIN_CFG 0x0017
+#define MAX98927_R0018_PCM_RX_EN_A 0x0018
+#define MAX98927_R0019_PCM_RX_EN_B 0x0019
+#define MAX98927_R001A_PCM_TX_EN_A 0x001A
+#define MAX98927_R001B_PCM_TX_EN_B 0x001B
+#define MAX98927_R001C_PCM_TX_HIZ_CTRL_A 0x001C
+#define MAX98927_R001D_PCM_TX_HIZ_CTRL_B 0x001D
+#define MAX98927_R001E_PCM_TX_CH_SRC_A 0x001E
+#define MAX98927_R001F_PCM_TX_CH_SRC_B 0x001F
+#define MAX98927_R0020_PCM_MODE_CFG 0x0020
+#define MAX98927_R0021_PCM_MASTER_MODE 0x0021
+#define MAX98927_R0022_PCM_CLK_SETUP 0x0022
+#define MAX98927_R0023_PCM_SR_SETUP1 0x0023
+#define MAX98927_R0024_PCM_SR_SETUP2 0x0024
+#define MAX98927_R0025_PCM_TO_SPK_MONOMIX_A 0x0025
+#define MAX98927_R0026_PCM_TO_SPK_MONOMIX_B 0x0026
+#define MAX98927_R0027_ICC_RX_EN_A 0x0027
+#define MAX98927_R0028_ICC_RX_EN_B 0x0028
+#define MAX98927_R002B_ICC_TX_EN_A 0x002B
+#define MAX98927_R002C_ICC_TX_EN_B 0x002C
+#define MAX98927_R002E_ICC_HIZ_MANUAL_MODE 0x002E
+#define MAX98927_R002F_ICC_TX_HIZ_EN_A 0x002F
+#define MAX98927_R0030_ICC_TX_HIZ_EN_B 0x0030
+#define MAX98927_R0031_ICC_LNK_EN 0x0031
+#define MAX98927_R0032_PDM_TX_EN 0x0032
+#define MAX98927_R0033_PDM_TX_HIZ_CTRL 0x0033
+#define MAX98927_R0034_PDM_TX_CTRL 0x0034
+#define MAX98927_R0035_PDM_RX_CTRL 0x0035
+#define MAX98927_R0036_AMP_VOL_CTRL 0x0036
+#define MAX98927_R0037_AMP_DSP_CFG 0x0037
+#define MAX98927_R0038_TONE_GEN_DC_CFG 0x0038
+#define MAX98927_R0039_DRE_CTRL 0x0039
+#define MAX98927_R003A_AMP_EN 0x003A
+#define MAX98927_R003B_SPK_SRC_SEL 0x003B
+#define MAX98927_R003C_SPK_GAIN 0x003C
+#define MAX98927_R003D_SSM_CFG 0x003D
+#define MAX98927_R003E_MEAS_EN 0x003E
+#define MAX98927_R003F_MEAS_DSP_CFG 0x003F
+#define MAX98927_R0040_BOOST_CTRL0 0x0040
+#define MAX98927_R0041_BOOST_CTRL3 0x0041
+#define MAX98927_R0042_BOOST_CTRL1 0x0042
+#define MAX98927_R0043_MEAS_ADC_CFG 0x0043
+#define MAX98927_R0044_MEAS_ADC_BASE_MSB 0x0044
+#define MAX98927_R0045_MEAS_ADC_BASE_LSB 0x0045
+#define MAX98927_R0046_ADC_CH0_DIVIDE 0x0046
+#define MAX98927_R0047_ADC_CH1_DIVIDE 0x0047
+#define MAX98927_R0048_ADC_CH2_DIVIDE 0x0048
+#define MAX98927_R0049_ADC_CH0_FILT_CFG 0x0049
+#define MAX98927_R004A_ADC_CH1_FILT_CFG 0x004A
+#define MAX98927_R004B_ADC_CH2_FILT_CFG 0x004B
+#define MAX98927_R004C_MEAS_ADC_CH0_READ 0x004C
+#define MAX98927_R004D_MEAS_ADC_CH1_READ 0x004D
+#define MAX98927_R004E_MEAS_ADC_CH2_READ 0x004E
+#define MAX98927_R0051_BROWNOUT_STATUS 0x0051
+#define MAX98927_R0052_BROWNOUT_EN 0x0052
+#define MAX98927_R0053_BROWNOUT_INFINITE_HOLD 0x0053
+#define MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR 0x0054
+#define MAX98927_R0055_BROWNOUT_LVL_HOLD 0x0055
+#define MAX98927_R005A_BROWNOUT_LVL1_THRESH 0x005A
+#define MAX98927_R005B_BROWNOUT_LVL2_THRESH 0x005B
+#define MAX98927_R005C_BROWNOUT_LVL3_THRESH 0x005C
+#define MAX98927_R005D_BROWNOUT_LVL4_THRESH 0x005D
+#define MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS 0x005E
+#define MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL 0x005F
+#define MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL 0x0060
+#define MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE 0x0061
+#define MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT 0x0072
+#define MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1 0x0073
+#define MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2 0x0074
+#define MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3 0x0075
+#define MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT 0x0076
+#define MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1 0x0077
+#define MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2 0x0078
+#define MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3 0x0079
+#define MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT 0x007A
+#define MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1 0x007B
+#define MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2 0x007C
+#define MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3 0x007D
+#define MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT 0x007E
+#define MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1 0x007F
+#define MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2 0x0080
+#define MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3 0x0081
+#define MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM 0x0082
+#define MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY 0x0083
+#define MAX98927_R0084_ENV_TRACK_REL_RATE 0x0084
+#define MAX98927_R0085_ENV_TRACK_HOLD_RATE 0x0085
+#define MAX98927_R0086_ENV_TRACK_CTRL 0x0086
+#define MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ 0x0087
+#define MAX98927_R00FF_GLOBAL_SHDN 0x00FF
+#define MAX98927_R0100_SOFT_RESET 0x0100
+#define MAX98927_R01FF_REV_ID 0x01FF
+
+/* MAX98927_R0018_PCM_RX_EN_A */
+#define MAX98927_PCM_RX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_RX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_RX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_RX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_RX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_RX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_RX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_RX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001A_PCM_TX_EN_A */
+#define MAX98927_PCM_TX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_TX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_TX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_TX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_TX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_TX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_TX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_TX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001E_PCM_TX_CH_SRC_A */
+#define MAX98927_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98927_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98927_R001F_PCM_TX_CH_SRC_B */
+#define MAX98927_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 5)
+
+/* MAX98927_R0020_PCM_MODE_CFG */
+#define MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2)
+#define MAX98927_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98927_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98927_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98927_R0021_PCM_MASTER_MODE */
+#define MAX98927_PCM_MASTER_MODE_MASK (0x3 << 0)
+#define MAX98927_PCM_MASTER_MODE_SLAVE (0x0 << 0)
+#define MAX98927_PCM_MASTER_MODE_MASTER (0x3 << 0)
+
+#define MAX98927_PCM_MASTER_MODE_MCLK_MASK (0xF << 2)
+#define MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2)
+
+/* MAX98927_R0022_PCM_CLK_SETUP */
+#define MAX98927_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98927_R0023_PCM_SR_SETUP1 */
+#define MAX98927_PCM_SR_SET1_SR_MASK (0xF << 0)
+
+#define MAX98927_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98927_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98927_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98927_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98927_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98927_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98927_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98927_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98927_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* MAX98927_R0024_PCM_SR_SETUP2 */
+#define MAX98927_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98927_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98927_PCM_SR_SET2_IVADC_SR_MASK (0xf << 0)
+
+/* MAX98927_R0025_PCM_TO_SPK_MONOMIX_A */
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+
+/* MAX98927_R0035_PDM_RX_CTRL */
+#define MAX98927_PDM_RX_EN_MASK (0x1 << 0)
+
+/* MAX98927_R0036_AMP_VOL_CTRL */
+#define MAX98927_AMP_VOL_SEL (0x1 << 7)
+#define MAX98927_AMP_VOL_SEL_WIDTH (1)
+#define MAX98927_AMP_VOL_SEL_SHIFT (7)
+#define MAX98927_AMP_VOL_MASK (0x7f << 0)
+#define MAX98927_AMP_VOL_WIDTH (7)
+#define MAX98927_AMP_VOL_SHIFT (0)
+
+/* MAX98927_R0037_AMP_DSP_CFG */
+#define MAX98927_AMP_DSP_CFG_DCBLK_EN (0x1 << 0)
+#define MAX98927_AMP_DSP_CFG_DITH_EN (0x1 << 1)
+#define MAX98927_AMP_DSP_CFG_RMP_BYPASS (0x1 << 4)
+#define MAX98927_AMP_DSP_CFG_DAC_INV (0x1 << 5)
+#define MAX98927_AMP_DSP_CFG_RMP_SHIFT (4)
+
+/* MAX98927_R0039_DRE_CTRL */
+#define MAX98927_DRE_CTRL_DRE_EN (0x1 << 0)
+#define MAX98927_DRE_EN_SHIFT 0x1
+
+/* MAX98927_R003A_AMP_EN */
+#define MAX98927_AMP_EN_MASK (0x1 << 0)
+
+/* MAX98927_R003B_SPK_SRC_SEL */
+#define MAX98927_SPK_SRC_MASK (0x3 << 0)
+
+/* MAX98927_R003C_SPK_GAIN */
+#define MAX98927_SPK_PCM_GAIN_MASK (0x7 << 0)
+#define MAX98927_SPK_PDM_GAIN_MASK (0x7 << 4)
+#define MAX98927_SPK_GAIN_WIDTH (3)
+
+/* MAX98927_R003E_MEAS_EN */
+#define MAX98927_MEAS_V_EN (0x1 << 0)
+#define MAX98927_MEAS_I_EN (0x1 << 1)
+
+/* MAX98927_R0040_BOOST_CTRL0 */
+#define MAX98927_BOOST_CTRL0_VOUT_MASK (0x1f << 0)
+#define MAX98927_BOOST_CTRL0_PVDD_MASK (0x1 << 7)
+#define MAX98927_BOOST_CTRL0_PVDD_EN_SHIFT (7)
+
+/* MAX98927_R0052_BROWNOUT_EN */
+#define MAX98927_BROWNOUT_BDE_EN (0x1 << 0)
+#define MAX98927_BROWNOUT_AMP_EN (0x1 << 1)
+#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2)
+#define MAX98927_BROWNOUT_DSP_SHIFT (2)
+
+/* MAX98927_R0100_SOFT_RESET */
+#define MAX98927_SOFT_RESET (0x1 << 0)
+
+/* MAX98927_R00FF_GLOBAL_SHDN */
+#define MAX98927_GLOBAL_EN_MASK (0x1 << 0)
+
+struct max98927_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct max98927_pdata *pdata;
+ struct gpio_desc *reset_gpio;
+ unsigned int spk_gain;
+ unsigned int sysclk;
+ unsigned int v_l_slot;
+ unsigned int i_l_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ unsigned int rate;
+ unsigned int iface;
+ unsigned int provider;
+ unsigned int digital_gain;
+ bool tdm_mode;
+};
+#endif
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 3e770cbe7f0f..086ac97e8386 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -1,24 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
* Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de
* Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch
*
* Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
+ * Phytec Messtechnik GmbH, https://www.phytec.de
*/
#include <linux/module.h>
#include <linux/device.h>
@@ -107,13 +94,13 @@ static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int rate = params_rate(params);
int i;
for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) {
if (rate == mc13783_rates[i]) {
- snd_soc_update_bits(codec, MC13783_AUDIO_DAC,
+ snd_soc_component_update_bits(component, MC13783_AUDIO_DAC,
0xf << 17, i << 17);
return 0;
}
@@ -126,7 +113,7 @@ static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int rate = params_rate(params);
unsigned int val;
@@ -141,7 +128,7 @@ static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+ snd_soc_component_update_bits(component, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
val);
return 0;
@@ -160,7 +147,7 @@ static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
unsigned int reg)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV |
AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET;
@@ -194,21 +181,20 @@ static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
}
/* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
val |= AUDIO_C_CLK_EN;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
val |= AUDIO_CSM;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
+ default:
return -EINVAL;
}
val |= AUDIO_C_RESET;
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -230,11 +216,11 @@ static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
return ret;
/*
- * In synchronous mode force the voice codec into slave mode
+ * In synchronous mode force the voice codec into consumer mode
* so that the clock / framesync from the stereo DAC is used
*/
- fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
- fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ fmt |= SND_SOC_DAIFMT_CBC_CFC;
ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
return ret;
@@ -255,7 +241,7 @@ static int mc13783_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir,
unsigned int reg)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int clk;
unsigned int val = 0;
unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
@@ -275,7 +261,7 @@ static int mc13783_set_sysclk(struct snd_soc_dai *dai,
val |= AUDIO_CLK(clk);
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -308,7 +294,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots,
int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
SSI_NETWORK_DAC_RXSLOT_MASK;
@@ -344,7 +330,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
return 0;
}
@@ -353,7 +339,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots,
int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = 0x3f;
@@ -366,7 +352,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
val |= (0x01 << 4); /* secondary timeslot TX is 1 */
- snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
return 0;
}
@@ -606,9 +592,12 @@ static struct snd_kcontrol_new mc13783_control_list[] = {
SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0),
};
-static int mc13783_probe(struct snd_soc_codec *codec)
+static int mc13783_probe(struct snd_soc_component *component)
{
- struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component,
+ dev_get_regmap(component->dev->parent, NULL));
/* these are the reset values */
mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
@@ -635,14 +624,12 @@ static int mc13783_probe(struct snd_soc_codec *codec)
return 0;
}
-static int mc13783_remove(struct snd_soc_codec *codec)
+static void mc13783_remove(struct snd_soc_component *component)
{
- struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
/* Make sure VAUDIOON is off */
mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0);
-
- return 0;
}
#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
@@ -724,25 +711,22 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = {
.formats = MC13783_FORMATS,
},
.ops = &mc13783_ops_sync,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static struct regmap *mc13783_get_regmap(struct device *dev)
-{
- return dev_get_regmap(dev->parent, NULL);
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
- .probe = mc13783_probe,
- .remove = mc13783_remove,
- .get_regmap = mc13783_get_regmap,
- .controls = mc13783_control_list,
- .num_controls = ARRAY_SIZE(mc13783_control_list),
- .dapm_widgets = mc13783_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets),
- .dapm_routes = mc13783_routes,
- .num_dapm_routes = ARRAY_SIZE(mc13783_routes),
+static const struct snd_soc_component_driver soc_component_dev_mc13783 = {
+ .probe = mc13783_probe,
+ .remove = mc13783_remove,
+ .controls = mc13783_control_list,
+ .num_controls = ARRAY_SIZE(mc13783_control_list),
+ .dapm_widgets = mc13783_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets),
+ .dapm_routes = mc13783_routes,
+ .num_dapm_routes = ARRAY_SIZE(mc13783_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int __init mc13783_codec_probe(struct platform_device *pdev)
@@ -783,27 +767,19 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
if (priv->adc_ssi_port == priv->dac_ssi_port)
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
else
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
return ret;
}
-static int mc13783_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver mc13783_codec_driver = {
.driver = {
.name = "mc13783-codec",
},
- .remove = mc13783_codec_remove,
};
module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h
index 3a6d1993a217..8992d3ab57e6 100644
--- a/sound/soc/codecs/mc13783.h
+++ b/sound/soc/codecs/mc13783.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MC13783_MIXER_H
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index f561c78b9e0e..fad0cc902346 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
@@ -68,7 +56,6 @@ static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
-static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
"A-law"};
@@ -338,8 +325,8 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
int i = get_coeff(priv->mclk, params_rate(hw_params));
int srate;
@@ -351,23 +338,23 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
if (priv->clk_in) {
switch (priv->mclk / params_rate(hw_params)) {
case 256:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 1);
break;
case 512:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 2);
break;
case 1024:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 3);
break;
default:
- dev_err(codec->dev, "Unsupported MCLKI\n");
+ dev_err(component->dev, "Unsupported MCLKI\n");
break;
}
} else {
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 0);
}
@@ -375,35 +362,35 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
if (srate < 0)
return srate;
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate);
- 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);
+ snd_soc_component_update_bits(component, ML26124_SMPLING_RATE, 0xf, srate);
+ snd_soc_component_update_bits(component, ML26124_PLLNL, 0xff, coeff_div[i].pllnl);
+ snd_soc_component_update_bits(component, ML26124_PLLNH, 0x1, coeff_div[i].pllnh);
+ snd_soc_component_update_bits(component, ML26124_PLLML, 0xff, coeff_div[i].pllml);
+ snd_soc_component_update_bits(component, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh);
+ snd_soc_component_update_bits(component, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv);
return 0;
}
-static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+static int ml26124_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
switch (priv->substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
- snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
break;
case SNDRV_PCM_STREAM_PLAYBACK:
- snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
break;
}
if (mute)
- snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
DVOL_CTL_DVMUTE_ON);
else
- snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
DVOL_CTL_DVMUTE_OFF);
return 0;
@@ -413,20 +400,19 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
unsigned char mode;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
mode = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
mode = 0;
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
+ snd_soc_component_update_bits(component, ML26124_SAI_MODE_SEL, BIT(0), mode);
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -450,8 +436,8 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case ML26124_USE_PLLOUT:
@@ -469,17 +455,18 @@ static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+static int ml26124_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
msleep(100);
- snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
ML26124_R26_MASK,
ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
break;
@@ -487,8 +474,8 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* VMID ON */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG,
ML26124_VMID, ML26124_VMID);
msleep(500);
regcache_sync(priv->regmap);
@@ -496,7 +483,7 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
/* VMID OFF */
- snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG,
ML26124_VMID, 0);
break;
}
@@ -505,9 +492,10 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ml26124_dai_ops = {
.hw_params = ml26124_hw_params,
- .digital_mute = ml26124_mute,
+ .mute_stream = ml26124_mute,
.set_fmt = ml26124_set_dai_fmt,
.set_sysclk = ml26124_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ml26124_dai = {
@@ -525,28 +513,31 @@ static struct snd_soc_dai_driver ml26124_dai = {
.rates = ML26124_RATES,
.formats = ML26124_FORMATS,},
.ops = &ml26124_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ml26124_probe(struct snd_soc_codec *codec)
+static int ml26124_probe(struct snd_soc_component *component)
{
/* Software Reset */
- snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
- snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+ snd_soc_component_update_bits(component, ML26124_SW_RST, 0x01, 1);
+ snd_soc_component_update_bits(component, ML26124_SW_RST, 0x01, 0);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
- .probe = ml26124_probe,
- .set_bias_level = ml26124_set_bias_level,
- .suspend_bias_off = true,
- .dapm_widgets = ml26124_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
- .dapm_routes = ml26124_intercon,
- .num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
- .controls = ml26124_snd_controls,
- .num_controls = ARRAY_SIZE(ml26124_snd_controls),
+static const struct snd_soc_component_driver soc_component_dev_ml26124 = {
+ .probe = ml26124_probe,
+ .set_bias_level = ml26124_set_bias_level,
+ .controls = ml26124_snd_controls,
+ .num_controls = ARRAY_SIZE(ml26124_snd_controls),
+ .dapm_widgets = ml26124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+ .dapm_routes = ml26124_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ml26124_i2c_regmap = {
@@ -559,8 +550,7 @@ static const struct regmap_config ml26124_i2c_regmap = {
.write_flag_mask = 0x01,
};
-static int ml26124_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ml26124_i2c_probe(struct i2c_client *i2c)
{
struct ml26124_priv *priv;
int ret;
@@ -578,18 +568,12 @@ static int ml26124_i2c_probe(struct i2c_client *i2c,
return ret;
}
- return snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ml26124, &ml26124_dai, 1);
-}
-
-static int ml26124_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ml26124, &ml26124_dai, 1);
}
static const struct i2c_device_id ml26124_i2c_id[] = {
- { "ml26124", 0 },
+ { "ml26124" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
@@ -599,7 +583,6 @@ static struct i2c_driver ml26124_i2c_driver = {
.name = "ml26124",
},
.probe = ml26124_i2c_probe,
- .remove = ml26124_i2c_remove,
.id_table = ml26124_i2c_id,
};
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
index 5ea0cbb8c46c..080a6232f41f 100644
--- a/sound/soc/codecs/ml26124.h
+++ b/sound/soc/codecs/ml26124.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ML26124_H
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
new file mode 100644
index 000000000000..9ca381812975
--- /dev/null
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -0,0 +1,1280 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+
+#define CDC_D_REVISION1 (0xf000)
+#define CDC_D_PERPH_SUBTYPE (0xf005)
+#define CDC_D_INT_EN_SET (0xf015)
+#define CDC_D_INT_EN_CLR (0xf016)
+#define MBHC_SWITCH_INT BIT(7)
+#define MBHC_MIC_ELECTRICAL_INS_REM_DET BIT(6)
+#define MBHC_BUTTON_PRESS_DET BIT(5)
+#define MBHC_BUTTON_RELEASE_DET BIT(4)
+#define CDC_D_CDC_RST_CTL (0xf046)
+#define RST_CTL_DIG_SW_RST_N_MASK BIT(7)
+#define RST_CTL_DIG_SW_RST_N_RESET 0
+#define RST_CTL_DIG_SW_RST_N_REMOVE_RESET BIT(7)
+
+#define CDC_D_CDC_TOP_CLK_CTL (0xf048)
+#define TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK (BIT(2) | BIT(3))
+#define TOP_CLK_CTL_A_MCLK_EN_ENABLE BIT(2)
+#define TOP_CLK_CTL_A_MCLK2_EN_ENABLE BIT(3)
+
+#define CDC_D_CDC_ANA_CLK_CTL (0xf049)
+#define ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK BIT(0)
+#define ANA_CLK_CTL_EAR_HPHR_CLK_EN BIT(0)
+#define ANA_CLK_CTL_EAR_HPHL_CLK_EN BIT(1)
+#define ANA_CLK_CTL_SPKR_CLK_EN_MASK BIT(4)
+#define ANA_CLK_CTL_SPKR_CLK_EN BIT(4)
+#define ANA_CLK_CTL_TXA_CLK25_EN BIT(5)
+
+#define CDC_D_CDC_DIG_CLK_CTL (0xf04A)
+#define DIG_CLK_CTL_RXD1_CLK_EN BIT(0)
+#define DIG_CLK_CTL_RXD2_CLK_EN BIT(1)
+#define DIG_CLK_CTL_RXD3_CLK_EN BIT(2)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN_MASK BIT(3)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN BIT(3)
+#define DIG_CLK_CTL_TXD_CLK_EN BIT(4)
+#define DIG_CLK_CTL_NCP_CLK_EN_MASK BIT(6)
+#define DIG_CLK_CTL_NCP_CLK_EN BIT(6)
+#define DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK BIT(7)
+#define DIG_CLK_CTL_RXD_PDM_CLK_EN BIT(7)
+
+#define CDC_D_CDC_CONN_TX1_CTL (0xf050)
+#define CONN_TX1_SERIAL_TX1_MUX GENMASK(1, 0)
+#define CONN_TX1_SERIAL_TX1_ADC_1 0x0
+#define CONN_TX1_SERIAL_TX1_RX_PDM_LB 0x1
+#define CONN_TX1_SERIAL_TX1_ZERO 0x2
+
+#define CDC_D_CDC_CONN_TX2_CTL (0xf051)
+#define CONN_TX2_SERIAL_TX2_MUX GENMASK(1, 0)
+#define CONN_TX2_SERIAL_TX2_ADC_2 0x0
+#define CONN_TX2_SERIAL_TX2_RX_PDM_LB 0x1
+#define CONN_TX2_SERIAL_TX2_ZERO 0x2
+#define CDC_D_CDC_CONN_HPHR_DAC_CTL (0xf052)
+#define CDC_D_CDC_CONN_RX1_CTL (0xf053)
+#define CDC_D_CDC_CONN_RX2_CTL (0xf054)
+#define CDC_D_CDC_CONN_RX3_CTL (0xf055)
+#define CDC_D_CDC_CONN_RX_LB_CTL (0xf056)
+#define CDC_D_SEC_ACCESS (0xf0D0)
+#define CDC_D_PERPH_RESET_CTL3 (0xf0DA)
+#define CDC_D_PERPH_RESET_CTL4 (0xf0DB)
+#define CDC_A_REVISION1 (0xf100)
+#define CDC_A_REVISION2 (0xf101)
+#define CDC_A_REVISION3 (0xf102)
+#define CDC_A_REVISION4 (0xf103)
+#define CDC_A_PERPH_TYPE (0xf104)
+#define CDC_A_PERPH_SUBTYPE (0xf105)
+#define CDC_A_INT_RT_STS (0xf110)
+#define CDC_A_INT_SET_TYPE (0xf111)
+#define CDC_A_INT_POLARITY_HIGH (0xf112)
+#define CDC_A_INT_POLARITY_LOW (0xf113)
+#define CDC_A_INT_LATCHED_CLR (0xf114)
+#define CDC_A_INT_EN_SET (0xf115)
+#define CDC_A_INT_EN_CLR (0xf116)
+#define CDC_A_INT_LATCHED_STS (0xf118)
+#define CDC_A_INT_PENDING_STS (0xf119)
+#define CDC_A_INT_MID_SEL (0xf11A)
+#define CDC_A_INT_PRIORITY (0xf11B)
+#define CDC_A_MICB_1_EN (0xf140)
+#define MICB_1_EN_MICB_ENABLE BIT(7)
+#define MICB_1_EN_BYP_CAP_MASK BIT(6)
+#define MICB_1_EN_NO_EXT_BYP_CAP BIT(6)
+#define MICB_1_EN_EXT_BYP_CAP 0
+#define MICB_1_EN_PULL_DOWN_EN_MASK BIT(5)
+#define MICB_1_EN_PULL_DOWN_EN_ENABLE BIT(5)
+#define MICB_1_EN_OPA_STG2_TAIL_CURR_MASK GENMASK(3, 1)
+#define MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA (0x4)
+#define MICB_1_EN_PULL_UP_EN_MASK BIT(4)
+#define MICB_1_EN_TX3_GND_SEL_MASK BIT(0)
+#define MICB_1_EN_TX3_GND_SEL_TX_GND 0
+
+#define CDC_A_MICB_1_VAL (0xf141)
+#define MICB_MIN_VAL 1600
+#define MICB_STEP_SIZE 50
+#define MICB_VOLTAGE_REGVAL(v) (((v - MICB_MIN_VAL)/MICB_STEP_SIZE) << 3)
+#define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3)
+#define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3)
+#define MICB_1_VAL_MICB_OUT_VAL_V1P80V ((0x4) << 3)
+#define CDC_A_MICB_1_CTL (0xf142)
+
+#define MICB_1_CTL_CFILT_REF_SEL_MASK BIT(1)
+#define MICB_1_CTL_CFILT_REF_SEL_HPF_REF BIT(1)
+#define MICB_1_CTL_EXT_PRECHARG_EN_MASK BIT(5)
+#define MICB_1_CTL_EXT_PRECHARG_EN_ENABLE BIT(5)
+#define MICB_1_CTL_INT_PRECHARG_BYP_MASK BIT(6)
+#define MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL BIT(6)
+
+#define CDC_A_MICB_1_INT_RBIAS (0xf143)
+#define MICB_1_INT_TX1_INT_RBIAS_EN_MASK BIT(7)
+#define MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE BIT(7)
+#define MICB_1_INT_TX1_INT_RBIAS_EN_DISABLE 0
+
+#define MICB_1_INT_TX1_INT_PULLUP_EN_MASK BIT(6)
+#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(6)
+#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_GND 0
+
+#define MICB_1_INT_TX2_INT_RBIAS_EN_MASK BIT(4)
+#define MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE BIT(4)
+#define MICB_1_INT_TX2_INT_RBIAS_EN_DISABLE 0
+#define MICB_1_INT_TX2_INT_PULLUP_EN_MASK BIT(3)
+#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(3)
+#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_GND 0
+
+#define MICB_1_INT_TX3_INT_RBIAS_EN_MASK BIT(1)
+#define MICB_1_INT_TX3_INT_RBIAS_EN_ENABLE BIT(1)
+#define MICB_1_INT_TX3_INT_RBIAS_EN_DISABLE 0
+#define MICB_1_INT_TX3_INT_PULLUP_EN_MASK BIT(0)
+#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(0)
+#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND 0
+
+#define CDC_A_MICB_2_EN (0xf144)
+#define CDC_A_MICB_2_EN_ENABLE BIT(7)
+#define CDC_A_MICB_2_PULL_DOWN_EN_MASK BIT(5)
+#define CDC_A_MICB_2_PULL_DOWN_EN BIT(5)
+#define CDC_A_TX_1_2_ATEST_CTL_2 (0xf145)
+#define CDC_A_MASTER_BIAS_CTL (0xf146)
+#define CDC_A_MBHC_DET_CTL_1 (0xf147)
+#define CDC_A_MBHC_DET_CTL_L_DET_EN BIT(7)
+#define CDC_A_MBHC_DET_CTL_GND_DET_EN BIT(6)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_REMOVAL (0)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT (5)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO BIT(4)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MANUAL BIT(3)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MASK GENMASK(4, 3)
+#define CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN BIT(2)
+#define CDC_A_MBHC_DET_CTL_2 (0xf150)
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 (BIT(7) | BIT(6))
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD BIT(5)
+#define CDC_A_PLUG_TYPE_MASK GENMASK(4, 3)
+#define CDC_A_HPHL_PLUG_TYPE_NO BIT(4)
+#define CDC_A_GND_PLUG_TYPE_NO BIT(3)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN_MASK BIT(0)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN BIT(0)
+#define CDC_A_MBHC_FSM_CTL (0xf151)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN BIT(7)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK BIT(7)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA (0x3 << 4)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK GENMASK(6, 4)
+#define CDC_A_MBHC_DBNC_TIMER (0xf152)
+#define CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS BIT(3)
+#define CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS (0x9 << 4)
+#define CDC_A_MBHC_BTN0_ZDET_CTL_0 (0xf153)
+#define CDC_A_MBHC_BTN1_ZDET_CTL_1 (0xf154)
+#define CDC_A_MBHC_BTN2_ZDET_CTL_2 (0xf155)
+#define CDC_A_MBHC_BTN3_CTL (0xf156)
+#define CDC_A_MBHC_BTN4_CTL (0xf157)
+#define CDC_A_MBHC_BTN_VREF_FINE_SHIFT (2)
+#define CDC_A_MBHC_BTN_VREF_FINE_MASK GENMASK(4, 2)
+#define CDC_A_MBHC_BTN_VREF_COARSE_MASK GENMASK(7, 5)
+#define CDC_A_MBHC_BTN_VREF_COARSE_SHIFT (5)
+#define CDC_A_MBHC_BTN_VREF_MASK (CDC_A_MBHC_BTN_VREF_COARSE_MASK | \
+ CDC_A_MBHC_BTN_VREF_FINE_MASK)
+#define CDC_A_MBHC_RESULT_1 (0xf158)
+#define CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK GENMASK(4, 0)
+#define CDC_A_TX_1_EN (0xf160)
+#define CDC_A_TX_2_EN (0xf161)
+#define CDC_A_TX_1_2_TEST_CTL_1 (0xf162)
+#define CDC_A_TX_1_2_TEST_CTL_2 (0xf163)
+#define CDC_A_TX_1_2_ATEST_CTL (0xf164)
+#define CDC_A_TX_1_2_OPAMP_BIAS (0xf165)
+#define CDC_A_TX_3_EN (0xf167)
+#define CDC_A_NCP_EN (0xf180)
+#define CDC_A_NCP_CLK (0xf181)
+#define CDC_A_NCP_FBCTRL (0xf183)
+#define CDC_A_NCP_FBCTRL_FB_CLK_INV_MASK BIT(5)
+#define CDC_A_NCP_FBCTRL_FB_CLK_INV BIT(5)
+#define CDC_A_NCP_BIAS (0xf184)
+#define CDC_A_NCP_VCTRL (0xf185)
+#define CDC_A_NCP_TEST (0xf186)
+#define CDC_A_NCP_CLIM_ADDR (0xf187)
+#define CDC_A_RX_CLOCK_DIVIDER (0xf190)
+#define CDC_A_RX_COM_OCP_CTL (0xf191)
+#define CDC_A_RX_COM_OCP_COUNT (0xf192)
+#define CDC_A_RX_COM_BIAS_DAC (0xf193)
+#define RX_COM_BIAS_DAC_RX_BIAS_EN_MASK BIT(7)
+#define RX_COM_BIAS_DAC_RX_BIAS_EN_ENABLE BIT(7)
+#define RX_COM_BIAS_DAC_DAC_REF_EN_MASK BIT(0)
+#define RX_COM_BIAS_DAC_DAC_REF_EN_ENABLE BIT(0)
+
+#define CDC_A_RX_HPH_BIAS_PA (0xf194)
+#define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195)
+#define CDC_A_RX_HPH_BIAS_CNP (0xf196)
+#define CDC_A_RX_HPH_CNP_EN (0xf197)
+#define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B)
+#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1)
+#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1)
+#define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D)
+#define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1)
+#define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1)
+
+#define CDC_A_RX_EAR_CTL (0xf19E)
+#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0)
+#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0)
+#define RX_EAR_CTL_PA_EAR_PA_EN_MASK BIT(6)
+#define RX_EAR_CTL_PA_EAR_PA_EN_ENABLE BIT(6)
+#define RX_EAR_CTL_PA_SEL_MASK BIT(7)
+#define RX_EAR_CTL_PA_SEL BIT(7)
+
+#define CDC_A_SPKR_DAC_CTL (0xf1B0)
+#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4)
+#define SPKR_DAC_CTL_DAC_RESET_NORMAL 0
+
+#define CDC_A_SPKR_DRV_CTL (0xf1B2)
+#define SPKR_DRV_CTL_DEF_MASK 0xEF
+#define SPKR_DRV_CLASSD_PA_EN_MASK BIT(7)
+#define SPKR_DRV_CLASSD_PA_EN_ENABLE BIT(7)
+#define SPKR_DRV_CAL_EN BIT(6)
+#define SPKR_DRV_SETTLE_EN BIT(5)
+#define SPKR_DRV_FW_EN BIT(3)
+#define SPKR_DRV_BOOST_SET BIT(2)
+#define SPKR_DRV_CMFB_SET BIT(1)
+#define SPKR_DRV_GAIN_SET BIT(0)
+#define SPKR_DRV_CTL_DEF_VAL (SPKR_DRV_CLASSD_PA_EN_ENABLE | \
+ SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \
+ SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \
+ SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET)
+#define CDC_A_SPKR_OCP_CTL (0xf1B4)
+#define CDC_A_SPKR_PWRSTG_CTL (0xf1B5)
+#define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0)
+#define SPKR_PWRSTG_CTL_DAC_EN BIT(0)
+#define SPKR_PWRSTG_CTL_MASK 0xE0
+#define SPKR_PWRSTG_CTL_BBM_MASK BIT(7)
+#define SPKR_PWRSTG_CTL_BBM_EN BIT(7)
+#define SPKR_PWRSTG_CTL_HBRDGE_EN_MASK BIT(6)
+#define SPKR_PWRSTG_CTL_HBRDGE_EN BIT(6)
+#define SPKR_PWRSTG_CTL_CLAMP_EN_MASK BIT(5)
+#define SPKR_PWRSTG_CTL_CLAMP_EN BIT(5)
+
+#define CDC_A_SPKR_DRV_DBG (0xf1B7)
+#define CDC_A_CURRENT_LIMIT (0xf1C0)
+#define CDC_A_BOOST_EN_CTL (0xf1C3)
+#define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4)
+#define CDC_A_SEC_ACCESS (0xf1D0)
+#define CDC_A_PERPH_RESET_CTL3 (0xf1DA)
+#define CDC_A_PERPH_RESET_CTL4 (0xf1DB)
+
+#define MSM8916_WCD_ANALOG_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
+static int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
+
+static const char * const supply_names[] = {
+ "vdd-cdc-io",
+ "vdd-cdc-tx-rx-cx",
+};
+
+#define MBHC_MAX_BUTTONS (5)
+
+struct pm8916_wcd_analog_priv {
+ u16 pmic_rev;
+ u16 codec_version;
+ bool mbhc_btn_enabled;
+ /* special event to detect accessory type */
+ int mbhc_btn0_released;
+ bool detect_accessory_type;
+ struct clk *mclk;
+ struct snd_soc_component *component;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ struct snd_soc_jack *jack;
+ bool hphl_jack_type_normally_open;
+ bool gnd_jack_type_normally_open;
+ /* Voltage threshold when internal current source of 100uA is used */
+ u32 vref_btn_cs[MBHC_MAX_BUTTONS];
+ /* Voltage threshold when microphone bias is ON */
+ u32 vref_btn_micb[MBHC_MAX_BUTTONS];
+ unsigned int micbias1_cap_mode;
+ unsigned int micbias2_cap_mode;
+ unsigned int micbias_mv;
+};
+
+static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
+static const char *const rdac2_mux_text[] = { "RX1", "RX2" };
+static const char *const hph_text[] = { "ZERO", "Switch", };
+
+static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
+ ARRAY_SIZE(hph_text), hph_text);
+
+static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum);
+static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum);
+static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum);
+
+/* ADC2 MUX */
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+/* RDAC2 MUX */
+static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
+ CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text);
+
+static const struct snd_kcontrol_new spkr_switch[] = {
+ SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0)
+};
+
+static const struct snd_kcontrol_new rdac2_mux = SOC_DAPM_ENUM(
+ "RDAC2 MUX Mux", rdac2_mux_enum);
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM(
+ "ADC2 MUX Mux", adc2_enum);
+
+/* Analog Gain control 0 dB to +24 dB in 6 dB steps */
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 600, 0);
+
+static const struct snd_kcontrol_new pm8916_wcd_analog_snd_controls[] = {
+ SOC_SINGLE_TLV("ADC1 Volume", CDC_A_TX_1_EN, 3, 8, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", CDC_A_TX_2_EN, 3, 8, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", CDC_A_TX_3_EN, 3, 8, 0, analog_gain),
+};
+
+static void pm8916_wcd_analog_micbias_enable(struct snd_soc_component *component)
+{
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_EXT_PRECHARG_EN_MASK |
+ MICB_1_CTL_INT_PRECHARG_BYP_MASK,
+ MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL
+ | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE);
+
+ if (wcd->micbias_mv) {
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_VAL,
+ MICB_1_VAL_MICB_OUT_VAL_MASK,
+ MICB_VOLTAGE_REGVAL(wcd->micbias_mv));
+ /*
+ * Special headset needs MICBIAS as 2.7V so wait for
+ * 50 msec for the MICBIAS to reach 2.7 volts.
+ */
+ if (wcd->micbias_mv >= 2700)
+ msleep(50);
+ }
+
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_EXT_PRECHARG_EN_MASK |
+ MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0);
+
+}
+
+static int pm8916_wcd_analog_enable_micbias(struct snd_soc_component *component,
+ int event, unsigned int cap_mode)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ pm8916_wcd_analog_micbias_enable(component);
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
+ MICB_1_EN_BYP_CAP_MASK, cap_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
+ MICB_1_EN_OPA_STG2_TAIL_CURR_MASK,
+ MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_micbias1(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ return pm8916_wcd_analog_enable_micbias(component, event,
+ wcd->micbias1_cap_mode);
+}
+
+static int pm8916_wcd_analog_enable_micbias2(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ return pm8916_wcd_analog_enable_micbias(component, event,
+ wcd->micbias2_cap_mode);
+
+}
+
+static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
+ bool micbias2_enabled)
+{
+ struct snd_soc_component *component = priv->component;
+ u32 coarse, fine, reg_val, reg_addr;
+ int *vrefs, i;
+
+ if (!micbias2_enabled) { /* use internal 100uA Current source */
+ /* Enable internal 2.2k Internal Rbias Resistor */
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
+ MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
+ MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
+ /* Remove pull down on MIC BIAS2 */
+ snd_soc_component_update_bits(component, CDC_A_MICB_2_EN,
+ CDC_A_MICB_2_PULL_DOWN_EN_MASK,
+ 0);
+ /* enable 100uA internal current source */
+ snd_soc_component_update_bits(component, CDC_A_MBHC_FSM_CTL,
+ CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
+ CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
+ }
+ snd_soc_component_update_bits(component, CDC_A_MBHC_FSM_CTL,
+ CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
+ CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
+
+ if (micbias2_enabled)
+ vrefs = &priv->vref_btn_micb[0];
+ else
+ vrefs = &priv->vref_btn_cs[0];
+
+ /* program vref ranges for all the buttons */
+ reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
+ for (i = 0; i < MBHC_MAX_BUTTONS; i++) {
+ /* split mv in to coarse parts of 100mv & fine parts of 12mv */
+ coarse = (vrefs[i] / 100);
+ fine = ((vrefs[i] % 100) / 12);
+ reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
+ (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
+ snd_soc_component_update_bits(component, reg_addr,
+ CDC_A_MBHC_BTN_VREF_MASK,
+ reg_val);
+ reg_addr++;
+ }
+
+ return 0;
+}
+
+static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
+{
+ struct snd_soc_component *component = wcd->component;
+ bool micbias_enabled = false;
+ u32 plug_type = 0;
+ u32 int_en_mask;
+
+ snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_1,
+ CDC_A_MBHC_DET_CTL_L_DET_EN |
+ CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION |
+ CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO |
+ CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN);
+
+ if (wcd->hphl_jack_type_normally_open)
+ plug_type |= CDC_A_HPHL_PLUG_TYPE_NO;
+
+ if (wcd->gnd_jack_type_normally_open)
+ plug_type |= CDC_A_GND_PLUG_TYPE_NO;
+
+ snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_2,
+ CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 |
+ CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD |
+ plug_type |
+ CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN);
+
+
+ snd_soc_component_write(component, CDC_A_MBHC_DBNC_TIMER,
+ CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS |
+ CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS);
+
+ /* enable MBHC clock */
+ snd_soc_component_update_bits(component, CDC_D_CDC_DIG_CLK_CTL,
+ DIG_CLK_CTL_D_MBHC_CLK_EN_MASK,
+ DIG_CLK_CTL_D_MBHC_CLK_EN);
+
+ if (snd_soc_component_read(component, CDC_A_MICB_2_EN) & CDC_A_MICB_2_EN_ENABLE)
+ micbias_enabled = true;
+
+ pm8916_mbhc_configure_bias(wcd, micbias_enabled);
+
+ int_en_mask = MBHC_SWITCH_INT;
+ if (wcd->mbhc_btn_enabled)
+ int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET;
+
+ snd_soc_component_update_bits(component, CDC_D_INT_EN_CLR, int_en_mask, 0);
+ snd_soc_component_update_bits(component, CDC_D_INT_EN_SET, int_en_mask, int_en_mask);
+ wcd->mbhc_btn0_released = false;
+ wcd->detect_accessory_type = true;
+}
+
+static int pm8916_wcd_analog_enable_micbias_int2(struct
+ snd_soc_dapm_widget
+ *w, struct snd_kcontrol
+ *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_MICB_2_EN,
+ CDC_A_MICB_2_PULL_DOWN_EN_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ pm8916_mbhc_configure_bias(wcd, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm8916_mbhc_configure_bias(wcd, false);
+ break;
+ }
+
+ return pm8916_wcd_analog_enable_micbias_int(w, kcontrol, event);
+}
+
+static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 adc_reg = CDC_A_TX_1_2_TEST_CTL_2;
+ u8 init_bit_shift;
+
+ if (w->reg == CDC_A_TX_1_EN)
+ init_bit_shift = 5;
+ else
+ init_bit_shift = 4;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (w->reg == CDC_A_TX_2_EN)
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_CFILT_REF_SEL_MASK,
+ MICB_1_CTL_CFILT_REF_SEL_HPF_REF);
+ /*
+ * Add delay of 10 ms to give sufficient time for the voltage
+ * to shoot up and settle so that the txfe init does not
+ * happen when the input voltage is changing too much.
+ */
+ usleep_range(10000, 10010);
+ snd_soc_component_update_bits(component, adc_reg, 1 << init_bit_shift,
+ 1 << init_bit_shift);
+ switch (w->reg) {
+ case CDC_A_TX_1_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX1_CTL,
+ CONN_TX1_SERIAL_TX1_MUX,
+ CONN_TX1_SERIAL_TX1_ADC_1);
+ break;
+ case CDC_A_TX_2_EN:
+ case CDC_A_TX_3_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL,
+ CONN_TX2_SERIAL_TX2_MUX,
+ CONN_TX2_SERIAL_TX2_ADC_2);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * Add delay of 12 ms before deasserting the init
+ * to reduce the tx pop
+ */
+ usleep_range(12000, 12010);
+ snd_soc_component_update_bits(component, adc_reg, 1 << init_bit_shift, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->reg) {
+ case CDC_A_TX_1_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX1_CTL,
+ CONN_TX1_SERIAL_TX1_MUX,
+ CONN_TX1_SERIAL_TX1_ZERO);
+ break;
+ case CDC_A_TX_2_EN:
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL,
+ MICB_1_CTL_CFILT_REF_SEL_MASK, 0);
+ fallthrough;
+ case CDC_A_TX_3_EN:
+ snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL,
+ CONN_TX2_SERIAL_TX2_MUX,
+ CONN_TX2_SERIAL_TX2_ZERO);
+ break;
+ }
+
+
+ break;
+ }
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_SPKR_PWRSTG_CTL,
+ SPKR_PWRSTG_CTL_DAC_EN_MASK |
+ SPKR_PWRSTG_CTL_BBM_MASK |
+ SPKR_PWRSTG_CTL_HBRDGE_EN_MASK |
+ SPKR_PWRSTG_CTL_CLAMP_EN_MASK,
+ SPKR_PWRSTG_CTL_DAC_EN|
+ SPKR_PWRSTG_CTL_BBM_EN |
+ SPKR_PWRSTG_CTL_HBRDGE_EN |
+ SPKR_PWRSTG_CTL_CLAMP_EN);
+
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK,
+ RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, CDC_A_SPKR_DRV_CTL,
+ SPKR_DRV_CTL_DEF_MASK,
+ SPKR_DRV_CTL_DEF_VAL);
+ snd_soc_component_update_bits(component, w->reg,
+ SPKR_DRV_CLASSD_PA_EN_MASK,
+ SPKR_DRV_CLASSD_PA_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, CDC_A_SPKR_PWRSTG_CTL,
+ SPKR_PWRSTG_CTL_DAC_EN_MASK|
+ SPKR_PWRSTG_CTL_BBM_MASK |
+ SPKR_PWRSTG_CTL_HBRDGE_EN_MASK |
+ SPKR_PWRSTG_CTL_CLAMP_EN_MASK, 0);
+
+ snd_soc_component_update_bits(component, CDC_A_SPKR_DAC_CTL,
+ SPKR_DAC_CTL_DAC_RESET_MASK,
+ SPKR_DAC_CTL_DAC_RESET_NORMAL);
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_EAR_PA_EN_MASK,
+ RX_EAR_CTL_PA_EAR_PA_EN_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0);
+ /* Delay to reduce ear turn off pop */
+ usleep_range(7000, 7100);
+ snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
+ RX_EAR_CTL_PA_SEL_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static const struct reg_default wcd_reg_defaults_2_0[] = {
+ {CDC_A_RX_COM_OCP_CTL, 0xD1},
+ {CDC_A_RX_COM_OCP_COUNT, 0xFF},
+ {CDC_D_SEC_ACCESS, 0xA5},
+ {CDC_D_PERPH_RESET_CTL3, 0x0F},
+ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4F},
+ {CDC_A_NCP_FBCTRL, 0x28},
+ {CDC_A_SPKR_DRV_CTL, 0x69},
+ {CDC_A_SPKR_DRV_DBG, 0x01},
+ {CDC_A_BOOST_EN_CTL, 0x5F},
+ {CDC_A_SLOPE_COMP_IP_ZERO, 0x88},
+ {CDC_A_SEC_ACCESS, 0xA5},
+ {CDC_A_PERPH_RESET_CTL3, 0x0F},
+ {CDC_A_CURRENT_LIMIT, 0x82},
+ {CDC_A_SPKR_DAC_CTL, 0x03},
+ {CDC_A_SPKR_OCP_CTL, 0xE1},
+ {CDC_A_MASTER_BIAS_CTL, 0x30},
+};
+
+static int pm8916_wcd_analog_probe(struct snd_soc_component *component)
+{
+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev);
+ int err, reg;
+
+ err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (err != 0) {
+ dev_err(component->dev, "failed to enable regulators (%d)\n", err);
+ return err;
+ }
+
+ snd_soc_component_init_regmap(component,
+ dev_get_regmap(component->dev->parent, NULL));
+ snd_soc_component_set_drvdata(component, priv);
+ priv->pmic_rev = snd_soc_component_read(component, CDC_D_REVISION1);
+ priv->codec_version = snd_soc_component_read(component, CDC_D_PERPH_SUBTYPE);
+
+ dev_info(component->dev, "PMIC REV: %d\t CODEC Version: %d\n",
+ priv->pmic_rev, priv->codec_version);
+
+ snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01);
+ snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01);
+
+ for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++)
+ snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg,
+ wcd_reg_defaults_2_0[reg].def);
+
+ priv->component = component;
+
+ snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL,
+ RST_CTL_DIG_SW_RST_N_MASK,
+ RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
+
+ pm8916_wcd_setup_mbhc(priv);
+
+ return 0;
+}
+
+static void pm8916_wcd_analog_remove(struct snd_soc_component *component)
+{
+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev);
+
+ snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL,
+ RST_CTL_DIG_SW_RST_N_MASK, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+}
+
+static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
+
+ {"PDM_RX1", NULL, "PDM Playback"},
+ {"PDM_RX2", NULL, "PDM Playback"},
+ {"PDM_RX3", NULL, "PDM Playback"},
+ {"PDM Capture", NULL, "PDM_TX"},
+
+ /* ADC Connections */
+ {"PDM_TX", NULL, "ADC2"},
+ {"PDM_TX", NULL, "ADC3"},
+ {"ADC2", NULL, "ADC2 MUX"},
+ {"ADC3", NULL, "ADC2 MUX"},
+ {"ADC2 MUX", "INP2", "ADC2_INP2"},
+ {"ADC2 MUX", "INP3", "ADC2_INP3"},
+
+ {"PDM_TX", NULL, "ADC1"},
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2_INP2", NULL, "AMIC2"},
+ {"ADC2_INP3", NULL, "AMIC3"},
+
+ /* RDAC Connections */
+ {"HPHR DAC", NULL, "RDAC2 MUX"},
+ {"RDAC2 MUX", "RX1", "PDM_RX1"},
+ {"RDAC2 MUX", "RX2", "PDM_RX2"},
+ {"HPHL DAC", NULL, "PDM_RX1"},
+ {"PDM_RX1", NULL, "RXD1_CLK"},
+ {"PDM_RX2", NULL, "RXD2_CLK"},
+ {"PDM_RX3", NULL, "RXD3_CLK"},
+
+ {"PDM_RX1", NULL, "RXD_PDM_CLK"},
+ {"PDM_RX2", NULL, "RXD_PDM_CLK"},
+ {"PDM_RX3", NULL, "RXD_PDM_CLK"},
+
+ {"ADC1", NULL, "TXD_CLK"},
+ {"ADC2", NULL, "TXD_CLK"},
+ {"ADC3", NULL, "TXD_CLK"},
+
+ {"ADC1", NULL, "TXA_CLK25"},
+ {"ADC2", NULL, "TXA_CLK25"},
+ {"ADC3", NULL, "TXA_CLK25"},
+
+ {"PDM_RX1", NULL, "A_MCLK2"},
+ {"PDM_RX2", NULL, "A_MCLK2"},
+ {"PDM_RX3", NULL, "A_MCLK2"},
+
+ {"PDM_TX", NULL, "A_MCLK2"},
+ {"A_MCLK2", NULL, "A_MCLK"},
+
+ /* Earpiece (RX MIX1) */
+ {"EAR", NULL, "EAR_S"},
+ {"EAR_S", "Switch", "EAR PA"},
+ {"EAR PA", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "HPHL DAC"},
+ {"EAR PA", NULL, "HPHR DAC"},
+ {"EAR PA", NULL, "EAR CP"},
+
+ /* Headset (RX MIX1 and RX MIX2) */
+ {"HPH_L", NULL, "HPHL PA"},
+ {"HPH_R", NULL, "HPHR PA"},
+
+ {"HPHL DAC", NULL, "EAR_HPHL_CLK"},
+ {"HPHR DAC", NULL, "EAR_HPHR_CLK"},
+
+ {"CP", NULL, "NCP_CLK"},
+
+ {"HPHL PA", NULL, "HPHL"},
+ {"HPHR PA", NULL, "HPHR"},
+ {"HPHL PA", NULL, "CP"},
+ {"HPHL PA", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "CP"},
+ {"HPHR PA", NULL, "RX_BIAS"},
+ {"HPHL", "Switch", "HPHL DAC"},
+ {"HPHR", "Switch", "HPHR DAC"},
+
+ {"RX_BIAS", NULL, "DAC_REF"},
+
+ {"SPK_OUT", NULL, "SPK PA"},
+ {"SPK PA", NULL, "RX_BIAS"},
+ {"SPK PA", NULL, "SPKR_CLK"},
+ {"SPK PA", NULL, "SPK DAC"},
+ {"SPK DAC", "Switch", "PDM_RX3"},
+
+ {"MIC_BIAS1", NULL, "INT_LDO_H"},
+ {"MIC_BIAS2", NULL, "INT_LDO_H"},
+ {"MIC_BIAS1", NULL, "vdd-micbias"},
+ {"MIC_BIAS2", NULL, "vdd-micbias"},
+
+ {"MIC BIAS External1", NULL, "MIC_BIAS1"},
+ {"MIC BIAS Internal1", NULL, "MIC_BIAS1"},
+ {"MIC BIAS External2", NULL, "MIC_BIAS2"},
+ {"MIC BIAS Internal2", NULL, "MIC_BIAS2"},
+ {"MIC BIAS Internal3", NULL, "MIC_BIAS1"},
+};
+
+static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
+
+ SND_SOC_DAPM_AIF_IN("PDM_RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PDM_RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PDM_RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PDM_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("HPH_L"),
+ SND_SOC_DAPM_OUTPUT("HPH_R"),
+
+ /* RX stuff */
+ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
+ 0, 0, NULL, 0,
+ pm8916_wcd_analog_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux),
+ SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux),
+ SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,
+ 0),
+ SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux),
+ SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL,
+ 0),
+ SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0,
+ spkr_switch, ARRAY_SIZE(spkr_switch)),
+
+ /* Speaker */
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+ SND_SOC_DAPM_PGA_E("SPK PA", CDC_A_SPKR_DRV_CTL,
+ 6, 0, NULL, 0,
+ pm8916_wcd_analog_enable_spk_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micbias", 0, 0),
+ SND_SOC_DAPM_SUPPLY("CP", CDC_A_NCP_EN, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_REF", CDC_A_RX_COM_BIAS_DAC, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0),
+
+ /* TX */
+ SND_SOC_DAPM_SUPPLY("MIC_BIAS1", CDC_A_MICB_1_EN, 7, 0,
+ pm8916_wcd_analog_enable_micbias1,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("MIC_BIAS2", CDC_A_MICB_2_EN, 7, 0,
+ pm8916_wcd_analog_enable_micbias2,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("MIC BIAS External1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS External2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_INT_RBIAS, 7, 0,
+ pm8916_wcd_analog_enable_micbias_int,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_1_INT_RBIAS, 4, 0,
+ pm8916_wcd_analog_enable_micbias_int2,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", CDC_A_MICB_1_INT_RBIAS, 1, 0,
+ pm8916_wcd_analog_enable_micbias_int,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0,
+ pm8916_wcd_analog_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2_INP2", NULL, CDC_A_TX_2_EN, 7, 0,
+ pm8916_wcd_analog_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2_INP3", NULL, CDC_A_TX_3_EN, 7, 0,
+ pm8916_wcd_analog_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+ SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux),
+
+ /* Analog path clocks */
+ SND_SOC_DAPM_SUPPLY("EAR_HPHR_CLK", CDC_D_CDC_ANA_CLK_CTL, 0, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY("EAR_HPHL_CLK", CDC_D_CDC_ANA_CLK_CTL, 1, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY("SPKR_CLK", CDC_D_CDC_ANA_CLK_CTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TXA_CLK25", CDC_D_CDC_ANA_CLK_CTL, 5, 0, NULL, 0),
+
+ /* Digital path clocks */
+
+ SND_SOC_DAPM_SUPPLY("RXD1_CLK", CDC_D_CDC_DIG_CLK_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXD2_CLK", CDC_D_CDC_DIG_CLK_CTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXD3_CLK", CDC_D_CDC_DIG_CLK_CTL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("TXD_CLK", CDC_D_CDC_DIG_CLK_CTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("NCP_CLK", CDC_D_CDC_DIG_CLK_CTL, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXD_PDM_CLK", CDC_D_CDC_DIG_CLK_CTL, 7, 0, NULL,
+ 0),
+
+ /* System Clock source */
+ SND_SOC_DAPM_SUPPLY("A_MCLK", CDC_D_CDC_TOP_CLK_CTL, 2, 0, NULL, 0),
+ /* TX ADC and RX DAC Clock source. */
+ SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0),
+};
+
+static int pm8916_wcd_analog_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack,
+ void *data)
+{
+ struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+
+ wcd->jack = jack;
+
+ return 0;
+}
+
+static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg)
+{
+ struct pm8916_wcd_analog_priv *priv = arg;
+
+ if (priv->detect_accessory_type) {
+ struct snd_soc_component *component = priv->component;
+ u32 val = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1);
+
+ /* check if its BTN0 thats released */
+ if ((val != -1) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK))
+ priv->mbhc_btn0_released = true;
+
+ } else {
+ snd_soc_jack_report(priv->jack, 0, btn_mask);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
+{
+ struct pm8916_wcd_analog_priv *priv = arg;
+ struct snd_soc_component *component = priv->component;
+ u32 btn_result;
+
+ btn_result = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1) &
+ CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK;
+
+ switch (btn_result) {
+ case 0xf:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_4, btn_mask);
+ break;
+ case 0x7:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_3, btn_mask);
+ break;
+ case 0x3:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_2, btn_mask);
+ break;
+ case 0x1:
+ snd_soc_jack_report(priv->jack, SND_JACK_BTN_1, btn_mask);
+ break;
+ case 0x0:
+ /* handle BTN_0 specially for type detection */
+ if (!priv->detect_accessory_type)
+ snd_soc_jack_report(priv->jack,
+ SND_JACK_BTN_0, btn_mask);
+ break;
+ default:
+ dev_err(component->dev,
+ "Unexpected button press result (%x)", btn_result);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+{
+ struct pm8916_wcd_analog_priv *priv = arg;
+ struct snd_soc_component *component = priv->component;
+ bool ins = false;
+
+ if (snd_soc_component_read(component, CDC_A_MBHC_DET_CTL_1) &
+ CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
+ ins = true;
+
+ /* Set the detection type appropriately */
+ snd_soc_component_update_bits(component, CDC_A_MBHC_DET_CTL_1,
+ CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
+ (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
+
+
+ if (ins) { /* hs insertion */
+ bool micbias_enabled = false;
+
+ if (snd_soc_component_read(component, CDC_A_MICB_2_EN) &
+ CDC_A_MICB_2_EN_ENABLE)
+ micbias_enabled = true;
+
+ pm8916_mbhc_configure_bias(priv, micbias_enabled);
+
+ /*
+ * if only a btn0 press event is receive just before
+ * insert event then its a 3 pole headphone else if
+ * both press and release event received then its
+ * a headset.
+ */
+ if (priv->mbhc_btn0_released)
+ snd_soc_jack_report(priv->jack,
+ SND_JACK_HEADSET, hs_jack_mask);
+ else
+ snd_soc_jack_report(priv->jack,
+ SND_JACK_HEADPHONE, hs_jack_mask);
+
+ priv->detect_accessory_type = false;
+
+ } else { /* removal */
+ snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
+ priv->detect_accessory_type = true;
+ priv->mbhc_btn0_released = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
+ [0] = {
+ .name = "pm8916_wcd_analog_pdm_rx",
+ .id = 0,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .rates = MSM8916_WCD_ANALOG_RATES,
+ .formats = MSM8916_WCD_ANALOG_FORMATS,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ },
+ [1] = {
+ .name = "pm8916_wcd_analog_pdm_tx",
+ .id = 1,
+ .capture = {
+ .stream_name = "PDM Capture",
+ .rates = MSM8916_WCD_ANALOG_RATES,
+ .formats = MSM8916_WCD_ANALOG_FORMATS,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ },
+};
+
+static const struct snd_soc_component_driver pm8916_wcd_analog = {
+ .probe = pm8916_wcd_analog_probe,
+ .remove = pm8916_wcd_analog_remove,
+ .set_jack = pm8916_wcd_analog_set_jack,
+ .controls = pm8916_wcd_analog_snd_controls,
+ .num_controls = ARRAY_SIZE(pm8916_wcd_analog_snd_controls),
+ .dapm_widgets = pm8916_wcd_analog_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm8916_wcd_analog_dapm_widgets),
+ .dapm_routes = pm8916_wcd_analog_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pm8916_wcd_analog_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pm8916_wcd_analog_parse_dt(struct device *dev,
+ struct pm8916_wcd_analog_priv *priv)
+{
+ int rval;
+
+ if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
+ priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
+ else
+ priv->micbias1_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP;
+
+ if (of_property_read_bool(dev->of_node, "qcom,micbias2-ext-cap"))
+ priv->micbias2_cap_mode = MICB_1_EN_EXT_BYP_CAP;
+ else
+ priv->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP;
+
+ of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
+ &priv->micbias_mv);
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,hphl-jack-type-normally-open"))
+ priv->hphl_jack_type_normally_open = true;
+ else
+ priv->hphl_jack_type_normally_open = false;
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,gnd-jack-type-normally-open"))
+ priv->gnd_jack_type_normally_open = true;
+ else
+ priv->gnd_jack_type_normally_open = false;
+
+ priv->mbhc_btn_enabled = true;
+ rval = of_property_read_u32_array(dev->of_node,
+ "qcom,mbhc-vthreshold-low",
+ &priv->vref_btn_cs[0],
+ MBHC_MAX_BUTTONS);
+ if (rval < 0) {
+ priv->mbhc_btn_enabled = false;
+ } else {
+ rval = of_property_read_u32_array(dev->of_node,
+ "qcom,mbhc-vthreshold-high",
+ &priv->vref_btn_micb[0],
+ MBHC_MAX_BUTTONS);
+ if (rval < 0)
+ priv->mbhc_btn_enabled = false;
+ }
+
+ if (!priv->mbhc_btn_enabled)
+ dev_err(dev,
+ "DT property missing, MBHC btn detection disabled\n");
+
+
+ return 0;
+}
+
+static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
+{
+ struct pm8916_wcd_analog_priv *priv;
+ struct device *dev = &pdev->dev;
+ int ret, i, irq;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = pm8916_wcd_analog_parse_dt(dev, priv);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to get regulator supplies %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ pm8916_mbhc_switch_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "mbhc switch irq", priv);
+ if (ret) {
+ dev_err(dev, "cannot request mbhc switch irq\n");
+ return ret;
+ }
+
+ if (priv->mbhc_btn_enabled) {
+ irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ mbhc_btn_press_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "mbhc btn press irq", priv);
+ if (ret) {
+ dev_err(dev, "cannot request mbhc button press irq\n");
+ return ret;
+ }
+
+ irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ mbhc_btn_release_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "mbhc btn release irq", priv);
+ if (ret) {
+ dev_err(dev, "cannot request mbhc button release irq\n");
+ return ret;
+ }
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ return devm_snd_soc_register_component(dev, &pm8916_wcd_analog,
+ pm8916_wcd_analog_dai,
+ ARRAY_SIZE(pm8916_wcd_analog_dai));
+}
+
+static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = {
+ { .compatible = "qcom,pm8916-wcd-analog-codec", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pm8916_wcd_analog_spmi_match_table);
+
+static struct platform_driver pm8916_wcd_analog_spmi_driver = {
+ .driver = {
+ .name = "qcom,pm8916-wcd-spmi-codec",
+ .of_match_table = pm8916_wcd_analog_spmi_match_table,
+ },
+ .probe = pm8916_wcd_analog_spmi_probe,
+};
+
+module_platform_driver(pm8916_wcd_analog_spmi_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+MODULE_DESCRIPTION("PMIC PM8916 WCD Analog Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c
new file mode 100644
index 000000000000..cfadea2aa1f7
--- /dev/null
+++ b/sound/soc/codecs/msm8916-wcd-digital.c
@@ -0,0 +1,1249 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define LPASS_CDC_CLK_RX_RESET_CTL (0x000)
+#define LPASS_CDC_CLK_TX_RESET_B1_CTL (0x004)
+#define CLK_RX_RESET_B1_CTL_TX1_RESET_MASK BIT(0)
+#define CLK_RX_RESET_B1_CTL_TX2_RESET_MASK BIT(1)
+#define LPASS_CDC_CLK_DMIC_B1_CTL (0x008)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_MASK GENMASK(3, 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV2 (0x0 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3 (0x1 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV4 (0x2 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV6 (0x3 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV16 (0x4 << 1)
+#define DMIC_B1_CTL_DMIC0_CLK_EN_MASK BIT(0)
+#define DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE BIT(0)
+
+#define LPASS_CDC_CLK_RX_I2S_CTL (0x00C)
+#define RX_I2S_CTL_RX_I2S_MODE_MASK BIT(5)
+#define RX_I2S_CTL_RX_I2S_MODE_16 BIT(5)
+#define RX_I2S_CTL_RX_I2S_MODE_32 0
+#define RX_I2S_CTL_RX_I2S_FS_RATE_MASK GENMASK(2, 0)
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ 0x0
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ 0x1
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ 0x2
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ 0x3
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_96_KHZ 0x4
+#define RX_I2S_CTL_RX_I2S_FS_RATE_F_192_KHZ 0x5
+#define LPASS_CDC_CLK_TX_I2S_CTL (0x010)
+#define TX_I2S_CTL_TX_I2S_MODE_MASK BIT(5)
+#define TX_I2S_CTL_TX_I2S_MODE_16 BIT(5)
+#define TX_I2S_CTL_TX_I2S_MODE_32 0
+#define TX_I2S_CTL_TX_I2S_FS_RATE_MASK GENMASK(2, 0)
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ 0x0
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ 0x1
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ 0x2
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ 0x3
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ 0x4
+#define TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ 0x5
+
+#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x014)
+#define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL (0x018)
+#define LPASS_CDC_CLK_OTHR_CTL (0x01C)
+#define LPASS_CDC_CLK_RX_B1_CTL (0x020)
+#define LPASS_CDC_CLK_MCLK_CTL (0x024)
+#define MCLK_CTL_MCLK_EN_MASK BIT(0)
+#define MCLK_CTL_MCLK_EN_ENABLE BIT(0)
+#define MCLK_CTL_MCLK_EN_DISABLE 0
+#define LPASS_CDC_CLK_PDM_CTL (0x028)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK BIT(0)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_EN BIT(0)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK BIT(1)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB BIT(1)
+#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_PDM_CLK 0
+
+#define LPASS_CDC_CLK_SD_CTL (0x02C)
+#define LPASS_CDC_RX1_B1_CTL (0x040)
+#define LPASS_CDC_RX2_B1_CTL (0x060)
+#define LPASS_CDC_RX3_B1_CTL (0x080)
+#define LPASS_CDC_RX1_B2_CTL (0x044)
+#define LPASS_CDC_RX2_B2_CTL (0x064)
+#define LPASS_CDC_RX3_B2_CTL (0x084)
+#define LPASS_CDC_RX1_B3_CTL (0x048)
+#define LPASS_CDC_RX2_B3_CTL (0x068)
+#define LPASS_CDC_RX3_B3_CTL (0x088)
+#define LPASS_CDC_RX1_B4_CTL (0x04C)
+#define LPASS_CDC_RX2_B4_CTL (0x06C)
+#define LPASS_CDC_RX3_B4_CTL (0x08C)
+#define LPASS_CDC_RX1_B5_CTL (0x050)
+#define LPASS_CDC_RX2_B5_CTL (0x070)
+#define LPASS_CDC_RX3_B5_CTL (0x090)
+#define LPASS_CDC_RX1_B6_CTL (0x054)
+#define RXn_B6_CTL_MUTE_MASK BIT(0)
+#define RXn_B6_CTL_MUTE_ENABLE BIT(0)
+#define RXn_B6_CTL_MUTE_DISABLE 0
+#define LPASS_CDC_RX2_B6_CTL (0x074)
+#define LPASS_CDC_RX3_B6_CTL (0x094)
+#define LPASS_CDC_RX1_VOL_CTL_B1_CTL (0x058)
+#define LPASS_CDC_RX2_VOL_CTL_B1_CTL (0x078)
+#define LPASS_CDC_RX3_VOL_CTL_B1_CTL (0x098)
+#define LPASS_CDC_RX1_VOL_CTL_B2_CTL (0x05C)
+#define LPASS_CDC_RX2_VOL_CTL_B2_CTL (0x07C)
+#define LPASS_CDC_RX3_VOL_CTL_B2_CTL (0x09C)
+#define LPASS_CDC_TOP_GAIN_UPDATE (0x0A0)
+#define LPASS_CDC_TOP_CTL (0x0A4)
+#define TOP_CTL_DIG_MCLK_FREQ_MASK BIT(0)
+#define TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ 0
+#define TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ BIT(0)
+
+#define LPASS_CDC_DEBUG_DESER1_CTL (0x0E0)
+#define LPASS_CDC_DEBUG_DESER2_CTL (0x0E4)
+#define LPASS_CDC_DEBUG_B1_CTL_CFG (0x0E8)
+#define LPASS_CDC_DEBUG_B2_CTL_CFG (0x0EC)
+#define LPASS_CDC_DEBUG_B3_CTL_CFG (0x0F0)
+#define LPASS_CDC_IIR1_GAIN_B1_CTL (0x100)
+#define LPASS_CDC_IIR2_GAIN_B1_CTL (0x140)
+#define LPASS_CDC_IIR1_GAIN_B2_CTL (0x104)
+#define LPASS_CDC_IIR2_GAIN_B2_CTL (0x144)
+#define LPASS_CDC_IIR1_GAIN_B3_CTL (0x108)
+#define LPASS_CDC_IIR2_GAIN_B3_CTL (0x148)
+#define LPASS_CDC_IIR1_GAIN_B4_CTL (0x10C)
+#define LPASS_CDC_IIR2_GAIN_B4_CTL (0x14C)
+#define LPASS_CDC_IIR1_GAIN_B5_CTL (0x110)
+#define LPASS_CDC_IIR2_GAIN_B5_CTL (0x150)
+#define LPASS_CDC_IIR1_GAIN_B6_CTL (0x114)
+#define LPASS_CDC_IIR2_GAIN_B6_CTL (0x154)
+#define LPASS_CDC_IIR1_GAIN_B7_CTL (0x118)
+#define LPASS_CDC_IIR2_GAIN_B7_CTL (0x158)
+#define LPASS_CDC_IIR1_GAIN_B8_CTL (0x11C)
+#define LPASS_CDC_IIR2_GAIN_B8_CTL (0x15C)
+#define LPASS_CDC_IIR1_CTL (0x120)
+#define LPASS_CDC_IIR2_CTL (0x160)
+#define LPASS_CDC_IIR1_GAIN_TIMER_CTL (0x124)
+#define LPASS_CDC_IIR2_GAIN_TIMER_CTL (0x164)
+#define LPASS_CDC_IIR1_COEF_B1_CTL (0x128)
+#define LPASS_CDC_IIR2_COEF_B1_CTL (0x168)
+#define LPASS_CDC_IIR1_COEF_B2_CTL (0x12C)
+#define LPASS_CDC_IIR2_COEF_B2_CTL (0x16C)
+#define LPASS_CDC_CONN_RX1_B1_CTL (0x180)
+#define LPASS_CDC_CONN_RX1_B2_CTL (0x184)
+#define LPASS_CDC_CONN_RX1_B3_CTL (0x188)
+#define LPASS_CDC_CONN_RX2_B1_CTL (0x18C)
+#define LPASS_CDC_CONN_RX2_B2_CTL (0x190)
+#define LPASS_CDC_CONN_RX2_B3_CTL (0x194)
+#define LPASS_CDC_CONN_RX3_B1_CTL (0x198)
+#define LPASS_CDC_CONN_RX3_B2_CTL (0x19C)
+#define LPASS_CDC_CONN_TX_B1_CTL (0x1A0)
+#define LPASS_CDC_CONN_EQ1_B1_CTL (0x1A8)
+#define LPASS_CDC_CONN_EQ1_B2_CTL (0x1AC)
+#define LPASS_CDC_CONN_EQ1_B3_CTL (0x1B0)
+#define LPASS_CDC_CONN_EQ1_B4_CTL (0x1B4)
+#define LPASS_CDC_CONN_EQ2_B1_CTL (0x1B8)
+#define LPASS_CDC_CONN_EQ2_B2_CTL (0x1BC)
+#define LPASS_CDC_CONN_EQ2_B3_CTL (0x1C0)
+#define LPASS_CDC_CONN_EQ2_B4_CTL (0x1C4)
+#define LPASS_CDC_CONN_TX_I2S_SD1_CTL (0x1C8)
+#define LPASS_CDC_TX1_VOL_CTL_TIMER (0x280)
+#define LPASS_CDC_TX2_VOL_CTL_TIMER (0x2A0)
+#define LPASS_CDC_TX1_VOL_CTL_GAIN (0x284)
+#define LPASS_CDC_TX2_VOL_CTL_GAIN (0x2A4)
+#define LPASS_CDC_TX1_VOL_CTL_CFG (0x288)
+#define TX_VOL_CTL_CFG_MUTE_EN_MASK BIT(0)
+#define TX_VOL_CTL_CFG_MUTE_EN_ENABLE BIT(0)
+
+#define LPASS_CDC_TX2_VOL_CTL_CFG (0x2A8)
+#define LPASS_CDC_TX1_MUX_CTL (0x28C)
+#define TX_MUX_CTL_CUT_OFF_FREQ_MASK GENMASK(5, 4)
+#define TX_MUX_CTL_CUT_OFF_FREQ_SHIFT 4
+#define TX_MUX_CTL_CF_NEG_3DB_4HZ (0x0 << 4)
+#define TX_MUX_CTL_CF_NEG_3DB_75HZ (0x1 << 4)
+#define TX_MUX_CTL_CF_NEG_3DB_150HZ (0x2 << 4)
+#define TX_MUX_CTL_HPF_BP_SEL_MASK BIT(3)
+#define TX_MUX_CTL_HPF_BP_SEL_BYPASS BIT(3)
+#define TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS 0
+
+#define LPASS_CDC_TX2_MUX_CTL (0x2AC)
+#define LPASS_CDC_TX1_CLK_FS_CTL (0x290)
+#define LPASS_CDC_TX2_CLK_FS_CTL (0x2B0)
+#define LPASS_CDC_TX1_DMIC_CTL (0x294)
+#define LPASS_CDC_TX2_DMIC_CTL (0x2B4)
+#define TXN_DMIC_CTL_CLK_SEL_MASK GENMASK(2, 0)
+#define TXN_DMIC_CTL_CLK_SEL_DIV2 0x0
+#define TXN_DMIC_CTL_CLK_SEL_DIV3 0x1
+#define TXN_DMIC_CTL_CLK_SEL_DIV4 0x2
+#define TXN_DMIC_CTL_CLK_SEL_DIV6 0x3
+#define TXN_DMIC_CTL_CLK_SEL_DIV16 0x4
+
+#define MSM8916_WCD_DIGITAL_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_48000)
+#define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR1 = 0,
+ IIR2,
+ IIR_MAX,
+};
+
+/* Codec supports 5 bands */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+#define WCD_IIR_FILTER_SIZE (sizeof(u32)*BAND_MAX)
+
+#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = wcd_iir_filter_info, \
+ .get = msm8x16_wcd_get_iir_band_audio_mixer, \
+ .put = msm8x16_wcd_put_iir_band_audio_mixer, \
+ .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \
+ .iir_idx = iidx, \
+ .band_idx = bidx, \
+ .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \
+ } \
+}
+
+struct wcd_iir_filter_ctl {
+ unsigned int iir_idx;
+ unsigned int band_idx;
+ struct soc_bytes_ext bytes_ext;
+};
+
+struct msm8916_wcd_digital_priv {
+ struct clk *ahbclk, *mclk;
+};
+
+static const unsigned long rx_gain_reg[] = {
+ LPASS_CDC_RX1_VOL_CTL_B2_CTL,
+ LPASS_CDC_RX2_VOL_CTL_B2_CTL,
+ LPASS_CDC_RX3_VOL_CTL_B2_CTL,
+};
+
+static const unsigned long tx_gain_reg[] = {
+ LPASS_CDC_TX1_VOL_CTL_GAIN,
+ LPASS_CDC_TX2_VOL_CTL_GAIN,
+};
+
+static const char *const rx_mix1_text[] = {
+ "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3"
+};
+
+static const char * const rx_mix2_text[] = {
+ "ZERO", "IIR1", "IIR2"
+};
+
+static const char *const dec_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2"
+};
+
+static const char *const cic_mux_text[] = { "AMIC", "DMIC" };
+
+/* RX1 MIX1 */
+static const struct soc_enum rx_mix1_inp_enum[] = {
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 0, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 3, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text),
+};
+
+/* RX2 MIX1 */
+static const struct soc_enum rx2_mix1_inp_enum[] = {
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_text),
+};
+
+/* RX3 MIX1 */
+static const struct soc_enum rx3_mix1_inp_enum[] = {
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text),
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B2_CTL, 0, 6, rx_mix1_text),
+};
+
+/* RX1 MIX2 */
+static const struct soc_enum rx_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* RX2 MIX2 */
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* DEC */
+static const struct soc_enum dec1_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text);
+static const struct soc_enum dec2_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_CONN_TX_B1_CTL, 3, 6, dec_mux_text);
+
+/* CIC */
+static const struct soc_enum cic1_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_TX1_MUX_CTL, 0, 2, cic_mux_text);
+static const struct soc_enum cic2_mux_enum = SOC_ENUM_SINGLE(
+ LPASS_CDC_TX2_MUX_CTL, 0, 2, cic_mux_text);
+
+/* RDAC2 MUX */
+static const struct snd_kcontrol_new dec1_mux = SOC_DAPM_ENUM(
+ "DEC1 MUX Mux", dec1_mux_enum);
+static const struct snd_kcontrol_new dec2_mux = SOC_DAPM_ENUM(
+ "DEC2 MUX Mux", dec2_mux_enum);
+static const struct snd_kcontrol_new cic1_mux = SOC_DAPM_ENUM(
+ "CIC1 MUX Mux", cic1_mux_enum);
+static const struct snd_kcontrol_new cic2_mux = SOC_DAPM_ENUM(
+ "CIC2 MUX Mux", cic2_mux_enum);
+static const struct snd_kcontrol_new rx_mix1_inp1_mux = SOC_DAPM_ENUM(
+ "RX1 MIX1 INP1 Mux", rx_mix1_inp_enum[0]);
+static const struct snd_kcontrol_new rx_mix1_inp2_mux = SOC_DAPM_ENUM(
+ "RX1 MIX1 INP2 Mux", rx_mix1_inp_enum[1]);
+static const struct snd_kcontrol_new rx_mix1_inp3_mux = SOC_DAPM_ENUM(
+ "RX1 MIX1 INP3 Mux", rx_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux = SOC_DAPM_ENUM(
+ "RX2 MIX1 INP1 Mux", rx2_mix1_inp_enum[0]);
+static const struct snd_kcontrol_new rx2_mix1_inp2_mux = SOC_DAPM_ENUM(
+ "RX2 MIX1 INP2 Mux", rx2_mix1_inp_enum[1]);
+static const struct snd_kcontrol_new rx2_mix1_inp3_mux = SOC_DAPM_ENUM(
+ "RX2 MIX1 INP3 Mux", rx2_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux = SOC_DAPM_ENUM(
+ "RX3 MIX1 INP1 Mux", rx3_mix1_inp_enum[0]);
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux = SOC_DAPM_ENUM(
+ "RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]);
+static const struct snd_kcontrol_new rx3_mix1_inp3_mux = SOC_DAPM_ENUM(
+ "RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux = SOC_DAPM_ENUM(
+ "RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum);
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux = SOC_DAPM_ENUM(
+ "RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
+
+/* Digital Gain control -84 dB to +40 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+/* Cutoff Freq for High Pass Filter at -3dB */
+static const char * const hpf_cutoff_text[] = {
+ "4Hz", "75Hz", "150Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(tx1_hpf_cutoff_enum, LPASS_CDC_TX1_MUX_CTL, 4,
+ hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(tx2_hpf_cutoff_enum, LPASS_CDC_TX2_MUX_CTL, 4,
+ hpf_cutoff_text);
+
+/* cut off for dc blocker inside rx chain */
+static const char * const dc_blocker_cutoff_text[] = {
+ "4Hz", "75Hz", "150Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(rx1_dcb_cutoff_enum, LPASS_CDC_RX1_B4_CTL, 0,
+ dc_blocker_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0,
+ dc_blocker_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rx3_dcb_cutoff_enum, LPASS_CDC_RX3_B4_CTL, 0,
+ dc_blocker_cutoff_text);
+
+static int msm8x16_wcd_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ int value = 0, reg = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->shift == 0)
+ reg = LPASS_CDC_IIR1_GAIN_B1_CTL;
+ else if (w->shift == 1)
+ reg = LPASS_CDC_IIR2_GAIN_B1_CTL;
+ value = snd_soc_component_read(component, reg);
+ snd_soc_component_write(component, reg, value);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ uint32_t value = 0;
+
+ /* Address does not automatically update if reading */
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx));
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8);
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16);
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= ((snd_soc_component_read(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) & 0x3f) << 24);
+ return value;
+
+}
+
+static int msm8x16_wcd_get_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0);
+ coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1);
+ coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2);
+ coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3);
+ coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4);
+
+ memcpy(ucontrol->value.bytes.data, &coeff[0], params->max);
+
+ return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx,
+ uint32_t value)
+{
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 16) & 0xFF);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 24) & 0x3F);
+}
+
+static int msm8x16_wcd_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ memcpy(&coeff[0], ucontrol->value.bytes.data, params->max);
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_component_write(component,
+ (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]);
+
+ return 0;
+}
+
+static int wcd_iir_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new msm8916_wcd_digital_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("RX1 Digital Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX1 Digital Volume", LPASS_CDC_TX1_VOL_CTL_GAIN,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("TX2 Digital Volume", LPASS_CDC_TX2_VOL_CTL_GAIN,
+ -84, 40, digital_gain),
+ SOC_ENUM("TX1 HPF Cutoff", tx1_hpf_cutoff_enum),
+ SOC_ENUM("TX2 HPF Cutoff", tx2_hpf_cutoff_enum),
+ SOC_SINGLE("TX1 HPF Switch", LPASS_CDC_TX1_MUX_CTL, 3, 1, 0),
+ SOC_SINGLE("TX2 HPF Switch", LPASS_CDC_TX2_MUX_CTL, 3, 1, 0),
+ SOC_ENUM("RX1 DCB Cutoff", rx1_dcb_cutoff_enum),
+ SOC_ENUM("RX2 DCB Cutoff", rx2_dcb_cutoff_enum),
+ SOC_ENUM("RX3 DCB Cutoff", rx3_dcb_cutoff_enum),
+ SOC_SINGLE("RX1 DCB Switch", LPASS_CDC_RX1_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX2 DCB Switch", LPASS_CDC_RX2_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX3 DCB Switch", LPASS_CDC_RX3_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX1 Mute Switch", LPASS_CDC_RX1_B6_CTL, 0, 1, 0),
+ SOC_SINGLE("RX2 Mute Switch", LPASS_CDC_RX2_B6_CTL, 0, 1, 0),
+ SOC_SINGLE("RX3 Mute Switch", LPASS_CDC_RX3_B6_CTL, 0, 1, 0),
+
+ SOC_SINGLE("IIR1 Band1 Switch", LPASS_CDC_IIR1_CTL, 0, 1, 0),
+ SOC_SINGLE("IIR1 Band2 Switch", LPASS_CDC_IIR1_CTL, 1, 1, 0),
+ SOC_SINGLE("IIR1 Band3 Switch", LPASS_CDC_IIR1_CTL, 2, 1, 0),
+ SOC_SINGLE("IIR1 Band4 Switch", LPASS_CDC_IIR1_CTL, 3, 1, 0),
+ SOC_SINGLE("IIR1 Band5 Switch", LPASS_CDC_IIR1_CTL, 4, 1, 0),
+ SOC_SINGLE("IIR2 Band1 Switch", LPASS_CDC_IIR2_CTL, 0, 1, 0),
+ SOC_SINGLE("IIR2 Band2 Switch", LPASS_CDC_IIR2_CTL, 1, 1, 0),
+ SOC_SINGLE("IIR2 Band3 Switch", LPASS_CDC_IIR2_CTL, 2, 1, 0),
+ SOC_SINGLE("IIR2 Band4 Switch", LPASS_CDC_IIR2_CTL, 3, 1, 0),
+ SOC_SINGLE("IIR2 Band5 Switch", LPASS_CDC_IIR2_CTL, 4, 1, 0),
+ WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1),
+ WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2),
+ WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3),
+ WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4),
+ WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5),
+ WCD_IIR_FILTER_CTL("IIR2 Band1", IIR2, BAND1),
+ WCD_IIR_FILTER_CTL("IIR2 Band2", IIR2, BAND2),
+ WCD_IIR_FILTER_CTL("IIR2 Band3", IIR2, BAND3),
+ WCD_IIR_FILTER_CTL("IIR2 Band4", IIR2, BAND4),
+ WCD_IIR_FILTER_CTL("IIR2 Band5", IIR2, BAND5),
+ SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", LPASS_CDC_IIR1_GAIN_B1_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP2 Volume", LPASS_CDC_IIR1_GAIN_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP3 Volume", LPASS_CDC_IIR1_GAIN_B3_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP4 Volume", LPASS_CDC_IIR1_GAIN_B4_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP1 Volume", LPASS_CDC_IIR2_GAIN_B1_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP2 Volume", LPASS_CDC_IIR2_GAIN_B2_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP3 Volume", LPASS_CDC_IIR2_GAIN_B3_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("IIR2 INP4 Volume", LPASS_CDC_IIR2_GAIN_B4_CTL,
+ -84, 40, digital_gain),
+
+};
+
+static int msm8916_wcd_digital_enable_interpolator(
+ struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* apply the digital gain after the interpolator is enabled */
+ usleep_range(10000, 10100);
+ snd_soc_component_write(component, rx_gain_reg[w->shift],
+ snd_soc_component_read(component, rx_gain_reg[w->shift]));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 1 << w->shift);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 0x0);
+ break;
+ }
+ return 0;
+}
+
+static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator = w->shift + 1;
+ u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
+ u8 dec_hpf_cut_of_freq;
+
+ dec_reset_reg = LPASS_CDC_CLK_TX_RESET_B1_CTL;
+ tx_vol_ctl_reg = LPASS_CDC_TX1_VOL_CTL_CFG + 32 * (decimator - 1);
+ tx_mux_ctl_reg = LPASS_CDC_TX1_MUX_CTL + 32 * (decimator - 1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable TX digital mute */
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK,
+ TX_VOL_CTL_CFG_MUTE_EN_ENABLE);
+ dec_hpf_cut_of_freq = snd_soc_component_read(component, tx_mux_ctl_reg) &
+ TX_MUX_CTL_CUT_OFF_FREQ_MASK;
+ dec_hpf_cut_of_freq >>= TX_MUX_CTL_CUT_OFF_FREQ_SHIFT;
+ if (dec_hpf_cut_of_freq != TX_MUX_CTL_CF_NEG_3DB_150HZ) {
+ /* set cut of freq to CF_MIN_3DB_150HZ (0x1) */
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_CUT_OFF_FREQ_MASK,
+ TX_MUX_CTL_CF_NEG_3DB_150HZ);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* enable HPF */
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_HPF_BP_SEL_MASK,
+ TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS);
+ /* apply the digital gain after the decimator is enabled */
+ snd_soc_component_write(component, tx_gain_reg[w->shift],
+ snd_soc_component_read(component, tx_gain_reg[w->shift]));
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK,
+ TX_VOL_CTL_CFG_MUTE_EN_ENABLE);
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_HPF_BP_SEL_MASK,
+ TX_MUX_CTL_HPF_BP_SEL_BYPASS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift, 0x0);
+ snd_soc_component_update_bits(component, tx_mux_ctl_reg,
+ TX_MUX_CTL_HPF_BP_SEL_MASK,
+ TX_MUX_CTL_HPF_BP_SEL_BYPASS);
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ TX_VOL_CTL_CFG_MUTE_EN_MASK, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int dmic;
+ int ret;
+ /* get dmic number out of widget name */
+ char *dmic_num = strpbrk(w->name, "12");
+
+ if (dmic_num == NULL) {
+ dev_err(component->dev, "Invalid DMIC\n");
+ return -EINVAL;
+ }
+ ret = kstrtouint(dmic_num, 10, &dmic);
+ if (ret < 0 || dmic > 2) {
+ dev_err(component->dev, "Invalid DMIC line on the component\n");
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_DMIC_B1_CTL,
+ DMIC_B1_CTL_DMIC0_CLK_SEL_MASK,
+ DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3);
+ switch (dmic) {
+ case 1:
+ snd_soc_component_update_bits(component, LPASS_CDC_TX1_DMIC_CTL,
+ TXN_DMIC_CTL_CLK_SEL_MASK,
+ TXN_DMIC_CTL_CLK_SEL_DIV3);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component, LPASS_CDC_TX2_DMIC_CTL,
+ TXN_DMIC_CTL_CLK_SEL_MASK,
+ TXN_DMIC_CTL_CLK_SEL_DIV3);
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const iir_inp1_text[] = {
+ "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3"
+};
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ1_B1_CTL,
+ 0, 6, iir_inp1_text);
+
+static const struct soc_enum iir2_inp1_mux_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ2_B1_CTL,
+ 0, 6, iir_inp1_text);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp1_mux =
+ SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum);
+
+static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = {
+ /*RX stuff */
+ SND_SOC_DAPM_AIF_IN("I2S RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("PDM_RX1"),
+ SND_SOC_DAPM_OUTPUT("PDM_RX2"),
+ SND_SOC_DAPM_OUTPUT("PDM_RX3"),
+
+ SND_SOC_DAPM_INPUT("LPASS_PDM_TX"),
+
+ SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Interpolator */
+ SND_SOC_DAPM_MIXER_E("RX1 INT", LPASS_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
+ 0, msm8916_wcd_digital_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX2 INT", LPASS_CDC_CLK_RX_B1_CTL, 1, 0, NULL,
+ 0, msm8916_wcd_digital_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX3 INT", LPASS_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+ 0, msm8916_wcd_digital_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx1_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix2_inp1_mux),
+
+ SND_SOC_DAPM_MUX("CIC1 MUX", SND_SOC_NOPM, 0, 0, &cic1_mux),
+ SND_SOC_DAPM_MUX("CIC2 MUX", SND_SOC_NOPM, 0, 0, &cic2_mux),
+ /* TX */
+ SND_SOC_DAPM_MIXER("ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("DEC1 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+ &dec1_mux, msm8916_wcd_digital_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("DEC2 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0,
+ &dec2_mux, msm8916_wcd_digital_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("I2S TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ msm8916_wcd_digital_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ msm8916_wcd_digital_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DMIC_CLK", LPASS_CDC_CLK_DMIC_B1_CTL, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", LPASS_CDC_CLK_RX_I2S_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", LPASS_CDC_CLK_TX_I2S_CTL, 4, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDM_CLK", LPASS_CDC_CLK_PDM_CTL, 0, 0, NULL, 0),
+ /* Connectivity Clock */
+ SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, LPASS_CDC_CLK_OTHR_CTL, 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+
+ /* Sidetone */
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_PGA_E("IIR1", LPASS_CDC_CLK_SD_CTL, 0, 0, NULL, 0,
+ msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
+ SND_SOC_DAPM_PGA_E("IIR2", LPASS_CDC_CLK_SD_CTL, 1, 0, NULL, 0,
+ msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+};
+
+static int msm8916_wcd_digital_get_clks(struct platform_device *pdev,
+ struct msm8916_wcd_digital_priv *priv)
+{
+ struct device *dev = &pdev->dev;
+
+ priv->ahbclk = devm_clk_get(dev, "ahbix-clk");
+ if (IS_ERR(priv->ahbclk)) {
+ dev_err(dev, "failed to get ahbix clk\n");
+ return PTR_ERR(priv->ahbclk);
+ }
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ dev_err(dev, "failed to get mclk\n");
+ return PTR_ERR(priv->mclk);
+ }
+
+ return 0;
+}
+
+static int msm8916_wcd_digital_component_probe(struct snd_soc_component *component)
+{
+ struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(component->dev);
+
+ snd_soc_component_set_drvdata(component, priv);
+
+ return 0;
+}
+
+static int msm8916_wcd_digital_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct msm8916_wcd_digital_priv *p = dev_get_drvdata(component->dev);
+
+ return clk_set_rate(p->mclk, freq);
+}
+
+static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u8 tx_fs_rate;
+ u8 rx_fs_rate;
+
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ;
+ break;
+ case 16000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ;
+ break;
+ case 32000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ;
+ break;
+ case 48000:
+ tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ;
+ rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ;
+ break;
+ default:
+ dev_err(dai->component->dev, "Invalid sampling rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL,
+ TX_I2S_CTL_TX_I2S_FS_RATE_MASK, tx_fs_rate);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL,
+ RX_I2S_CTL_RX_I2S_FS_RATE_MASK, rx_fs_rate);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL,
+ TX_I2S_CTL_TX_I2S_MODE_MASK,
+ TX_I2S_CTL_TX_I2S_MODE_16);
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL,
+ RX_I2S_CTL_RX_I2S_MODE_MASK,
+ RX_I2S_CTL_RX_I2S_MODE_16);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL,
+ TX_I2S_CTL_TX_I2S_MODE_MASK,
+ TX_I2S_CTL_TX_I2S_MODE_32);
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL,
+ RX_I2S_CTL_RX_I2S_MODE_MASK,
+ RX_I2S_CTL_RX_I2S_MODE_32);
+ break;
+ default:
+ dev_err(dai->dev, "%s: wrong format selected\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = {
+
+ {"I2S RX1", NULL, "AIF1 Playback"},
+ {"I2S RX2", NULL, "AIF1 Playback"},
+ {"I2S RX3", NULL, "AIF1 Playback"},
+
+ {"AIF1 Capture", NULL, "I2S TX1"},
+ {"AIF1 Capture", NULL, "I2S TX2"},
+ {"AIF1 Capture", NULL, "I2S TX3"},
+
+ {"CIC1 MUX", "DMIC", "DEC1 MUX"},
+ {"CIC1 MUX", "AMIC", "DEC1 MUX"},
+ {"CIC2 MUX", "DMIC", "DEC2 MUX"},
+ {"CIC2 MUX", "AMIC", "DEC2 MUX"},
+
+ /* Decimator Inputs */
+ {"DEC1 MUX", "DMIC1", "DMIC1"},
+ {"DEC1 MUX", "DMIC2", "DMIC2"},
+ {"DEC1 MUX", "ADC1", "ADC1"},
+ {"DEC1 MUX", "ADC2", "ADC2"},
+ {"DEC1 MUX", "ADC3", "ADC3"},
+ {"DEC1 MUX", NULL, "CDC_CONN"},
+
+ {"DEC2 MUX", "DMIC1", "DMIC1"},
+ {"DEC2 MUX", "DMIC2", "DMIC2"},
+ {"DEC2 MUX", "ADC1", "ADC1"},
+ {"DEC2 MUX", "ADC2", "ADC2"},
+ {"DEC2 MUX", "ADC3", "ADC3"},
+ {"DEC2 MUX", NULL, "CDC_CONN"},
+
+ {"DMIC1", NULL, "DMIC_CLK"},
+ {"DMIC2", NULL, "DMIC_CLK"},
+
+ {"I2S TX1", NULL, "CIC1 MUX"},
+ {"I2S TX2", NULL, "CIC2 MUX"},
+
+ {"I2S TX1", NULL, "TX_I2S_CLK"},
+ {"I2S TX2", NULL, "TX_I2S_CLK"},
+
+ {"TX_I2S_CLK", NULL, "MCLK"},
+ {"TX_I2S_CLK", NULL, "PDM_CLK"},
+
+ {"ADC1", NULL, "LPASS_PDM_TX"},
+ {"ADC2", NULL, "LPASS_PDM_TX"},
+ {"ADC3", NULL, "LPASS_PDM_TX"},
+
+ {"I2S RX1", NULL, "RX_I2S_CLK"},
+ {"I2S RX2", NULL, "RX_I2S_CLK"},
+ {"I2S RX3", NULL, "RX_I2S_CLK"},
+
+ {"RX_I2S_CLK", NULL, "PDM_CLK"},
+ {"RX_I2S_CLK", NULL, "MCLK"},
+ {"RX_I2S_CLK", NULL, "CDC_CONN"},
+
+ /* RX1 PATH.. */
+ {"PDM_RX1", NULL, "RX1 INT"},
+ {"RX1 INT", NULL, "RX1 MIX1"},
+
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
+
+ {"RX1 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX1 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP2", "IIR2", "IIR2"},
+
+ {"RX1 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP3", "RX3", "I2S RX3"},
+
+ /* RX2 PATH */
+ {"PDM_RX2", NULL, "RX2 INT"},
+ {"RX2 INT", NULL, "RX2 MIX1"},
+
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP3"},
+
+ {"RX2 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX2 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX2 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX2 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX2 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP3", "RX3", "I2S RX3"},
+
+ /* RX3 PATH */
+ {"PDM_RX3", NULL, "RX3 INT"},
+ {"RX3 INT", NULL, "RX3 MIX1"},
+
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP3"},
+
+ {"RX3 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX3 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP1", "IIR2", "IIR2"},
+
+ {"RX3 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX3 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP2", "IIR2", "IIR2"},
+
+ {"RX1 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX2 INP1", "IIR2", "IIR2"},
+ {"RX2 MIX2 INP1", "IIR2", "IIR2"},
+
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
+
+ {"IIR2", NULL, "IIR2 INP1 MUX"},
+ {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"},
+
+ {"RX3 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP3", "RX3", "I2S RX3"},
+
+};
+
+static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct msm8916_wcd_digital_priv *msm8916_wcd;
+ unsigned long mclk_rate;
+
+ msm8916_wcd = snd_soc_component_get_drvdata(component);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_MCLK_CTL,
+ MCLK_CTL_MCLK_EN_MASK,
+ MCLK_CTL_MCLK_EN_ENABLE);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_PDM_CTL,
+ LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK,
+ LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB);
+
+ mclk_rate = clk_get_rate(msm8916_wcd->mclk);
+ switch (mclk_rate) {
+ case 12288000:
+ snd_soc_component_update_bits(component, LPASS_CDC_TOP_CTL,
+ TOP_CTL_DIG_MCLK_FREQ_MASK,
+ TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ);
+ break;
+ case 9600000:
+ snd_soc_component_update_bits(component, LPASS_CDC_TOP_CTL,
+ TOP_CTL_DIG_MCLK_FREQ_MASK,
+ TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ);
+ break;
+ default:
+ dev_err(component->dev, "Invalid mclk rate %ld\n", mclk_rate);
+ break;
+ }
+ return 0;
+}
+
+static void msm8916_wcd_digital_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_PDM_CTL,
+ LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0);
+}
+
+static const struct snd_soc_dai_ops msm8916_wcd_digital_dai_ops = {
+ .startup = msm8916_wcd_digital_startup,
+ .shutdown = msm8916_wcd_digital_shutdown,
+ .hw_params = msm8916_wcd_digital_hw_params,
+};
+
+static struct snd_soc_dai_driver msm8916_wcd_digital_dai[] = {
+ [0] = {
+ .name = "msm8916_wcd_digital_i2s_rx1",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = MSM8916_WCD_DIGITAL_RATES,
+ .formats = MSM8916_WCD_DIGITAL_FORMATS,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ .ops = &msm8916_wcd_digital_dai_ops,
+ },
+ [1] = {
+ .name = "msm8916_wcd_digital_i2s_tx1",
+ .id = 1,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = MSM8916_WCD_DIGITAL_RATES,
+ .formats = MSM8916_WCD_DIGITAL_FORMATS,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &msm8916_wcd_digital_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver msm8916_wcd_digital = {
+ .probe = msm8916_wcd_digital_component_probe,
+ .set_sysclk = msm8916_wcd_digital_component_set_sysclk,
+ .controls = msm8916_wcd_digital_snd_controls,
+ .num_controls = ARRAY_SIZE(msm8916_wcd_digital_snd_controls),
+ .dapm_widgets = msm8916_wcd_digital_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm8916_wcd_digital_dapm_widgets),
+ .dapm_routes = msm8916_wcd_digital_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(msm8916_wcd_digital_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config msm8916_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LPASS_CDC_TX2_DMIC_CTL,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int msm8916_wcd_digital_probe(struct platform_device *pdev)
+{
+ struct msm8916_wcd_digital_priv *priv;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ struct regmap *digital_map;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ digital_map =
+ devm_regmap_init_mmio(&pdev->dev, base,
+ &msm8916_codec_regmap_config);
+ if (IS_ERR(digital_map))
+ return PTR_ERR(digital_map);
+
+ ret = msm8916_wcd_digital_get_clks(pdev, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(priv->ahbclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable ahbclk %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable mclk %d\n", ret);
+ goto err_clk;
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_snd_soc_register_component(dev, &msm8916_wcd_digital,
+ msm8916_wcd_digital_dai,
+ ARRAY_SIZE(msm8916_wcd_digital_dai));
+ if (ret)
+ goto err_mclk;
+
+ return 0;
+
+err_mclk:
+ clk_disable_unprepare(priv->mclk);
+err_clk:
+ clk_disable_unprepare(priv->ahbclk);
+ return ret;
+}
+
+static void msm8916_wcd_digital_remove(struct platform_device *pdev)
+{
+ struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(priv->mclk);
+ clk_disable_unprepare(priv->ahbclk);
+}
+
+static const struct of_device_id msm8916_wcd_digital_match_table[] = {
+ { .compatible = "qcom,msm8916-wcd-digital-codec" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm8916_wcd_digital_match_table);
+
+static struct platform_driver msm8916_wcd_digital_driver = {
+ .driver = {
+ .name = "msm8916-wcd-digital-codec",
+ .of_match_table = msm8916_wcd_digital_match_table,
+ },
+ .probe = msm8916_wcd_digital_probe,
+ .remove = msm8916_wcd_digital_remove,
+};
+
+module_platform_driver(msm8916_wcd_digital_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+MODULE_DESCRIPTION("MSM8916 WCD Digital Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c
new file mode 100644
index 000000000000..2a5e963fb2b5
--- /dev/null
+++ b/sound/soc/codecs/mt6351.c
@@ -0,0 +1,1496 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6351.c -- mt6351 ALSA SoC audio codec driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "mt6351.h"
+
+/* MT6351_TOP_CLKSQ */
+#define RG_CLKSQ_EN_AUD_BIT (0)
+
+/* MT6351_TOP_CKPDN_CON0 */
+#define RG_AUDNCP_CK_PDN_BIT (12)
+#define RG_AUDIF_CK_PDN_BIT (13)
+#define RG_AUD_CK_PDN_BIT (14)
+#define RG_ZCD13M_CK_PDN_BIT (15)
+
+/* MT6351_AUDDEC_ANA_CON0 */
+#define RG_AUDDACLPWRUP_VAUDP32_BIT (0)
+#define RG_AUDDACRPWRUP_VAUDP32_BIT (1)
+#define RG_AUD_DAC_PWR_UP_VA32_BIT (2)
+#define RG_AUD_DAC_PWL_UP_VA32_BIT (3)
+
+#define RG_AUDHSPWRUP_VAUDP32_BIT (4)
+
+#define RG_AUDHPLPWRUP_VAUDP32_BIT (5)
+#define RG_AUDHPRPWRUP_VAUDP32_BIT (6)
+
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT (7)
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT (9)
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT (11)
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDHSSCDISABLE_VAUDP32 (13)
+#define RG_AUDHPLSCDISABLE_VAUDP32_BIT (14)
+#define RG_AUDHPRSCDISABLE_VAUDP32_BIT (15)
+
+/* MT6351_AUDDEC_ANA_CON1 */
+#define RG_HSOUTPUTSTBENH_VAUDP32_BIT (8)
+
+/* MT6351_AUDDEC_ANA_CON3 */
+#define RG_AUDLOLPWRUP_VAUDP32_BIT (2)
+
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT (3)
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK (0x3)
+
+#define RG_AUDLOLSCDISABLE_VAUDP32_BIT (5)
+#define RG_LOOUTPUTSTBENH_VAUDP32_BIT (9)
+
+/* MT6351_AUDDEC_ANA_CON6 */
+#define RG_ABIDEC_RSVD0_VAUDP32_HPL_BIT (8)
+#define RG_ABIDEC_RSVD0_VAUDP32_HPR_BIT (9)
+#define RG_ABIDEC_RSVD0_VAUDP32_HS_BIT (10)
+#define RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT (11)
+
+/* MT6351_AUDDEC_ANA_CON9 */
+#define RG_AUDIBIASPWRDN_VAUDP32_BIT (8)
+#define RG_RSTB_DECODER_VA32_BIT (9)
+#define RG_AUDGLB_PWRDN_VA32_BIT (12)
+
+#define RG_LCLDO_DEC_EN_VA32_BIT (13)
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT (15)
+/* MT6351_AUDDEC_ANA_CON10 */
+#define RG_NVREG_EN_VAUDP32_BIT (8)
+
+#define RG_AUDGLB_LP2_VOW_EN_VA32 10
+
+/* MT6351_AFE_UL_DL_CON0 */
+#define RG_AFE_ON_BIT (0)
+
+/* MT6351_AFE_DL_SRC2_CON0_L */
+#define RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT (0)
+
+/* MT6351_AFE_UL_SRC_CON0_L */
+#define UL_SRC_ON_TMP_CTL (0)
+
+/* MT6351_AFE_TOP_CON0 */
+#define RG_DL_SINE_ON_SFT (0)
+#define RG_DL_SINE_ON_MASK (0x1)
+
+#define RG_UL_SINE_ON_SFT (1)
+#define RG_UL_SINE_ON_MASK (0x1)
+
+/* MT6351_AUDIO_TOP_CON0 */
+#define AUD_TOP_PDN_RESERVED_BIT 0
+#define AUD_TOP_PWR_CLK_DIS_CTL_BIT 2
+#define AUD_TOP_PDN_ADC_CTL_BIT 5
+#define AUD_TOP_PDN_DAC_CTL_BIT 6
+#define AUD_TOP_PDN_AFE_CTL_BIT 7
+
+/* MT6351_AFE_SGEN_CFG0 */
+#define SGEN_C_MUTE_SW_CTL_BIT 6
+#define SGEN_C_DAC_EN_CTL_BIT 7
+
+/* MT6351_AFE_NCP_CFG0 */
+#define RG_NCP_ON_BIT 0
+
+/* MT6351_LDO_VUSB33_CON0 */
+#define RG_VUSB33_EN 1
+#define RG_VUSB33_ON_CTRL 3
+
+/* MT6351_LDO_VA18_CON0 */
+#define RG_VA18_EN 1
+#define RG_VA18_ON_CTRL 3
+
+/* MT6351_AUDENC_ANA_CON0 */
+#define RG_AUDPREAMPLON 0
+#define RG_AUDPREAMPLDCCEN 1
+#define RG_AUDPREAMPLDCPRECHARGE 2
+
+#define RG_AUDPREAMPLINPUTSEL_SFT (4)
+#define RG_AUDPREAMPLINPUTSEL_MASK (0x3)
+
+#define RG_AUDADCLPWRUP 12
+
+#define RG_AUDADCLINPUTSEL_SFT (13)
+#define RG_AUDADCLINPUTSEL_MASK (0x3)
+
+/* MT6351_AUDENC_ANA_CON1 */
+#define RG_AUDPREAMPRON 0
+#define RG_AUDPREAMPRDCCEN 1
+#define RG_AUDPREAMPRDCPRECHARGE 2
+
+#define RG_AUDPREAMPRINPUTSEL_SFT (4)
+#define RG_AUDPREAMPRINPUTSEL_MASK (0x3)
+
+#define RG_AUDADCRPWRUP 12
+
+#define RG_AUDADCRINPUTSEL_SFT (13)
+#define RG_AUDADCRINPUTSEL_MASK (0x3)
+
+/* MT6351_AUDENC_ANA_CON3 */
+#define RG_AUDADCCLKRSTB 6
+
+/* MT6351_AUDENC_ANA_CON9 */
+#define RG_AUDPWDBMICBIAS0 0
+#define RG_AUDMICBIAS0VREF 4
+#define RG_AUDMICBIAS0LOWPEN 7
+
+#define RG_AUDPWDBMICBIAS2 8
+#define RG_AUDMICBIAS2VREF 12
+#define RG_AUDMICBIAS2LOWPEN 15
+
+/* MT6351_AUDENC_ANA_CON10 */
+#define RG_AUDPWDBMICBIAS1 0
+#define RG_AUDMICBIAS1DCSW1NEN 2
+#define RG_AUDMICBIAS1VREF 4
+#define RG_AUDMICBIAS1LOWPEN 7
+
+enum {
+ AUDIO_ANALOG_VOLUME_HSOUTL,
+ AUDIO_ANALOG_VOLUME_HSOUTR,
+ AUDIO_ANALOG_VOLUME_HPOUTL,
+ AUDIO_ANALOG_VOLUME_HPOUTR,
+ AUDIO_ANALOG_VOLUME_LINEOUTL,
+ AUDIO_ANALOG_VOLUME_LINEOUTR,
+ AUDIO_ANALOG_VOLUME_MICAMP1,
+ AUDIO_ANALOG_VOLUME_MICAMP2,
+ AUDIO_ANALOG_VOLUME_TYPE_MAX
+};
+
+/* Supply subseq */
+enum {
+ SUPPLY_SUBSEQ_SETTING,
+ SUPPLY_SUBSEQ_ENABLE,
+ SUPPLY_SUBSEQ_MICBIAS,
+};
+
+#define REG_STRIDE 2
+
+struct mt6351_priv {
+ struct device *dev;
+ struct regmap *regmap;
+
+ unsigned int dl_rate;
+ unsigned int ul_rate;
+
+ int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX];
+
+ int hp_en_counter;
+};
+
+static void set_hp_gain_zero(struct snd_soc_component *cmpnt)
+{
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2,
+ 0x1f << 7, 0x8 << 7);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2,
+ 0x1f << 0, 0x8 << 0);
+}
+
+static unsigned int get_cap_reg_val(struct snd_soc_component *cmpnt,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0;
+ case 16000:
+ return 1;
+ case 32000:
+ return 2;
+ case 48000:
+ return 3;
+ case 96000:
+ return 4;
+ case 192000:
+ return 5;
+ default:
+ dev_warn(cmpnt->dev, "%s(), error rate %d, return 3",
+ __func__, rate);
+ return 3;
+ }
+}
+
+static unsigned int get_play_reg_val(struct snd_soc_component *cmpnt,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0;
+ case 11025:
+ return 1;
+ case 12000:
+ return 2;
+ case 16000:
+ return 3;
+ case 22050:
+ return 4;
+ case 24000:
+ return 5;
+ case 32000:
+ return 6;
+ case 44100:
+ return 7;
+ case 48000:
+ case 96000:
+ case 192000:
+ return 8;
+ default:
+ dev_warn(cmpnt->dev, "%s(), error rate %d, return 8",
+ __func__, rate);
+ return 8;
+ }
+}
+
+static int mt6351_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int rate = params_rate(params);
+
+ dev_dbg(priv->dev, "%s(), substream->stream %d, rate %d\n",
+ __func__, substream->stream, rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ priv->dl_rate = rate;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->ul_rate = rate;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6351_codec_dai_ops = {
+ .hw_params = mt6351_codec_dai_hw_params,
+};
+
+#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6351_dai_driver[] = {
+ {
+ .name = "mt6351-snd-codec-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6351_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6351_FORMATS,
+ },
+ .ops = &mt6351_codec_dai_ops,
+ },
+};
+
+enum {
+ HP_GAIN_SET_ZERO,
+ HP_GAIN_RESTORE,
+};
+
+static void hp_gain_ramp_set(struct snd_soc_component *cmpnt, int hp_gain_ctl)
+{
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int idx, old_idx, offset, reg_idx;
+
+ if (hp_gain_ctl == HP_GAIN_SET_ZERO) {
+ idx = 8; /* 0dB */
+ old_idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ } else {
+ idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ old_idx = 8; /* 0dB */
+ }
+ dev_dbg(priv->dev, "%s(), idx %d, old_idx %d\n",
+ __func__, idx, old_idx);
+
+ if (idx > old_idx)
+ offset = idx - old_idx;
+ else
+ offset = old_idx - idx;
+
+ reg_idx = old_idx;
+
+ while (offset > 0) {
+ reg_idx = idx > old_idx ? reg_idx + 1 : reg_idx - 1;
+
+ /* check valid range, and set value */
+ if ((reg_idx >= 0 && reg_idx <= 0x12) || reg_idx == 0x1f) {
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_ZCD_CON2,
+ 0xf9f,
+ (reg_idx << 7) | reg_idx);
+ usleep_range(100, 120);
+ }
+ offset--;
+ }
+}
+
+static void hp_zcd_enable(struct snd_soc_component *cmpnt)
+{
+ /* Enable ZCD, for minimize pop noise */
+ /* when adjust gain during HP buffer on */
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 8, 0x1 << 8);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 7, 0x0 << 7);
+
+ /* timeout, 1=5ms, 0=30ms */
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 6, 0x1 << 6);
+
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x3 << 4, 0x0 << 4);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 1, 0x5 << 1);
+ regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 0, 0x1 << 0);
+}
+
+static void hp_zcd_disable(struct snd_soc_component *cmpnt)
+{
+ regmap_write(cmpnt->regmap, MT6351_ZCD_CON0, 0x0000);
+}
+
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new mt6351_snd_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_TLV("Headphone Volume",
+ MT6351_ZCD_CON2, 0, 7, 0x12, 1,
+ playback_tlv),
+ SOC_DOUBLE_TLV("Lineout Volume",
+ MT6351_ZCD_CON1, 0, 7, 0x12, 1,
+ playback_tlv),
+ SOC_SINGLE_TLV("Handset Volume",
+ MT6351_ZCD_CON3, 0, 0x12, 1,
+ playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_TLV("PGA Volume",
+ MT6351_AUDENC_ANA_CON0, MT6351_AUDENC_ANA_CON1,
+ 8, 4, 0,
+ pga_tlv),
+};
+
+/* MUX */
+
+/* LOL MUX */
+static const char *const lo_in_mux_map[] = {
+ "Open", "Mute", "Playback", "Test Mode",
+};
+
+static int lo_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON3,
+ RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK,
+ lo_in_mux_map,
+ lo_in_mux_map_value);
+
+static const struct snd_kcontrol_new lo_in_mux_control =
+ SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum);
+
+/*HP MUX */
+static const char *const hp_in_mux_map[] = {
+ "Open", "LoudSPK Playback", "Audio Playback", "Test Mode",
+};
+
+static int hp_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpl_in_mux_control =
+ SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpr_in_mux_control =
+ SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum);
+
+/* RCV MUX */
+static const char *const rcv_in_mux_map[] = {
+ "Open", "Mute", "Voice Playback", "Test Mode",
+};
+
+static int rcv_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum,
+ MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHSMUXINPUTSEL_VAUDP32_SFT,
+ RG_AUDHSMUXINPUTSEL_VAUDP32_MASK,
+ rcv_in_mux_map,
+ rcv_in_mux_map_value);
+
+static const struct snd_kcontrol_new rcv_in_mux_control =
+ SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum);
+
+/* DAC In MUX */
+static const char *const dac_in_mux_map[] = {
+ "Normal Path", "Sgen",
+};
+
+static int dac_in_mux_map_value[] = {
+ 0x0, 0x1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum,
+ MT6351_AFE_TOP_CON0,
+ RG_DL_SINE_ON_SFT,
+ RG_DL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new dac_in_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum);
+
+/* AIF Out MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum,
+ MT6351_AFE_TOP_CON0,
+ RG_UL_SINE_ON_SFT,
+ RG_UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum);
+
+/* ADC L MUX */
+static const char *const adc_left_mux_map[] = {
+ "Idle", "AIN0", "Left Preamplifier", "Idle_1",
+};
+
+static int adc_left_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum,
+ MT6351_AUDENC_ANA_CON0,
+ RG_AUDADCLINPUTSEL_SFT,
+ RG_AUDADCLINPUTSEL_MASK,
+ adc_left_mux_map,
+ adc_left_mux_map_value);
+
+static const struct snd_kcontrol_new adc_left_mux_control =
+ SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum);
+
+/* ADC R MUX */
+static const char *const adc_right_mux_map[] = {
+ "Idle", "AIN0", "Right Preamplifier", "Idle_1",
+};
+
+static int adc_right_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum,
+ MT6351_AUDENC_ANA_CON1,
+ RG_AUDADCRINPUTSEL_SFT,
+ RG_AUDADCRINPUTSEL_MASK,
+ adc_right_mux_map,
+ adc_right_mux_map_value);
+
+static const struct snd_kcontrol_new adc_right_mux_control =
+ SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum);
+
+/* PGA L MUX */
+static const char *const pga_left_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2",
+};
+
+static int pga_left_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6351_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLINPUTSEL_SFT,
+ RG_AUDPREAMPLINPUTSEL_MASK,
+ pga_left_mux_map,
+ pga_left_mux_map_value);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+/* PGA R MUX */
+static const char *const pga_right_mux_map[] = {
+ "None", "AIN0", "AIN3", "AIN2",
+};
+
+static int pga_right_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6351_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRINPUTSEL_SFT,
+ RG_AUDPREAMPRINPUTSEL_MASK,
+ pga_right_mux_map,
+ pga_right_mux_map_value);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+static int mt_reg_set_clr_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->on_val) {
+ /* SET REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ } else {
+ /* CLR REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE * 2,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (w->off_val) {
+ /* SET REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ } else {
+ /* CLR REG */
+ regmap_update_bits(cmpnt->regmap,
+ w->reg + REG_STRIDE * 2,
+ 0x1 << w->shift,
+ 0x1 << w->shift);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ncp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG1,
+ 0xffff, 0x1515);
+ /* NCP: ck1 and ck2 clock frequecy adjust configure */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG0,
+ 0xfffe, 0x8C00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG0,
+ 0xffef, 0x0008);
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG1,
+ 0xffff, 0x0101);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->dl_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2,
+ 0xffff, 0x0006);
+ /* scrambler clock on enable */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON0,
+ 0xffff, 0xC3A1);
+ /* sdm power on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2,
+ 0xffff, 0x0003);
+ /* sdm fifo enable */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2,
+ 0xffff, 0x000B);
+ /* set attenuation gain */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DL_SDM_CON1,
+ 0xffff, 0x001E);
+
+ regmap_write(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG0,
+ (get_play_reg_val(cmpnt, priv->dl_rate) << 12) |
+ 0x330);
+ regmap_write(cmpnt->regmap, MT6351_AFE_DL_SRC2_CON0_H,
+ (get_play_reg_val(cmpnt, priv->dl_rate) << 12) |
+ 0x300);
+
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2,
+ 0x8000, 0x8000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int reg;
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, hp_en_counter %d\n",
+ __func__, event, priv->hp_en_counter);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->hp_en_counter++;
+ if (priv->hp_en_counter > 1)
+ break; /* already enabled, do nothing */
+ else if (priv->hp_en_counter <= 0)
+ dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n",
+ __func__,
+ priv->hp_en_counter);
+
+ hp_zcd_disable(cmpnt);
+
+ /* from yoyo HQA script */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6,
+ 0x0700, 0x0700);
+
+ /* save target gain to restore after hardware open complete */
+ regmap_read(cmpnt->regmap, MT6351_ZCD_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = reg & 0x1f;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> 7) & 0x1f;
+
+ /* Set HPR/HPL gain as minimum (~ -40dB) */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_ZCD_CON2, 0xffff, 0x0F9F);
+ /* Set HS gain as minimum (~ -40dB) */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_ZCD_CON3, 0xffff, 0x001F);
+ /* De_OSC of HP */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON2,
+ 0x0001, 0x0001);
+ /* enable output STBENH */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2000);
+ /* De_OSC of voice, enable output STBENH */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2100);
+ /* Enable voice driver */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0,
+ 0x0010, 0xE090);
+ /* Enable pre-charge buffer */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2140);
+
+ usleep_range(50, 60);
+
+ /* Apply digital DC compensation value to DAC */
+ set_hp_gain_zero(cmpnt);
+
+ /* Enable HPR/HPL */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2100);
+ /* Disable pre-charge buffer */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1,
+ 0xffff, 0x2000);
+ /* Disable De_OSC of voice */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0,
+ 0x0010, 0xF4EF);
+ /* Disable voice buffer */
+
+ /* from yoyo HQ */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6,
+ 0x0700, 0x0300);
+
+ /* Enable ZCD, for minimize pop noise */
+ /* when adjust gain during HP buffer on */
+ hp_zcd_enable(cmpnt);
+
+ /* apply volume setting */
+ hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_en_counter--;
+ if (priv->hp_en_counter > 0)
+ break; /* still being used, don't close */
+ else if (priv->hp_en_counter < 0)
+ dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n",
+ __func__,
+ priv->hp_en_counter);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(cmpnt);
+
+ /* Set HPR/HPL gain as -1dB, step by step */
+ hp_gain_ramp_set(cmpnt, HP_GAIN_SET_ZERO);
+
+ set_hp_gain_zero(cmpnt);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->hp_en_counter > 0)
+ break; /* still being used, don't close */
+ else if (priv->hp_en_counter < 0)
+ dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n",
+ __func__,
+ priv->hp_en_counter);
+
+ /* reset*/
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AUDDEC_ANA_CON6,
+ 0x0700,
+ 0x0000);
+ /* De_OSC of HP */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AUDDEC_ANA_CON2,
+ 0x0001,
+ 0x0000);
+
+ /* apply volume setting */
+ hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->ul_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* dcclk_div=11'b00100000011, dcclk_ref_ck_sel=2'b00 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0,
+ 0xffff, 0x2062);
+ /* dcclk_pdn=1'b0 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0,
+ 0xffff, 0x2060);
+ /* dcclk_gen_on=1'b1 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0,
+ 0xffff, 0x2061);
+
+ /* UL sample rate and mode configure */
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_UL_SRC_CON0_H,
+ 0x000E,
+ get_cap_reg_val(cmpnt, priv->ul_rate) << 1);
+
+ /* fixed 260k path for 8/16/32/48 */
+ if (priv->ul_rate <= 48000) {
+ /* anc ul path src on */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 1,
+ 0x1 << 1);
+ /* ANC clk pdn release */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* fixed 260k path for 8/16/32/48 */
+ if (priv->ul_rate <= 48000) {
+ /* anc ul path src on */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 1,
+ 0x0 << 1);
+ /* ANC clk pdn release */
+ regmap_update_bits(cmpnt->regmap,
+ MT6351_AFE_HPANC_CFG0,
+ 0x1 << 0,
+ 0x1 << 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_clkgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Audio ADC clock gen. mode: 00_divided by 2 (Normal) */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3,
+ 0x3 << 4, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* ADC CLK from: 00_13MHz from CLKSQ (Default) */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3,
+ 0x3 << 2, 0x0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_pga_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Audio L PGA precharge on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0,
+ 0x3 << RG_AUDPREAMPLDCPRECHARGE,
+ 0x1 << RG_AUDPREAMPLDCPRECHARGE);
+ /* Audio L PGA mode: 1_DCC */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0,
+ 0x3 << RG_AUDPREAMPLDCCEN,
+ 0x1 << RG_AUDPREAMPLDCCEN);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio L PGA precharge off */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0,
+ 0x3 << RG_AUDPREAMPLDCPRECHARGE,
+ 0x0 << RG_AUDPREAMPLDCPRECHARGE);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_pga_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Audio R PGA precharge on */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1,
+ 0x3 << RG_AUDPREAMPRDCPRECHARGE,
+ 0x1 << RG_AUDPREAMPRDCPRECHARGE);
+ /* Audio R PGA mode: 1_DCC */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1,
+ 0x3 << RG_AUDPREAMPRDCCEN,
+ 0x1 << RG_AUDPREAMPRDCCEN);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio R PGA precharge off */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1,
+ 0x3 << RG_AUDPREAMPRDCPRECHARGE,
+ 0x0 << RG_AUDPREAMPRDCPRECHARGE);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MIC Bias 0 LowPower: 0_Normal */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x3 << RG_AUDMICBIAS0LOWPEN, 0x0);
+ /* MISBIAS0 = 1P9V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS0VREF,
+ 0x2 << RG_AUDMICBIAS0VREF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* MISBIAS0 = 1P97 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS0VREF,
+ 0x0 << RG_AUDMICBIAS0VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MIC Bias 1 LowPower: 0_Normal */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10,
+ 0x3 << RG_AUDMICBIAS1LOWPEN, 0x0);
+ /* MISBIAS1 = 2P7V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10,
+ 0x7 << RG_AUDMICBIAS1VREF,
+ 0x7 << RG_AUDMICBIAS1VREF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* MISBIAS1 = 1P7V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10,
+ 0x7 << RG_AUDMICBIAS1VREF,
+ 0x0 << RG_AUDMICBIAS1VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MIC Bias 2 LowPower: 0_Normal */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x3 << RG_AUDMICBIAS2LOWPEN, 0x0);
+ /* MISBIAS2 = 1P9V */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS2VREF,
+ 0x2 << RG_AUDMICBIAS2VREF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* MISBIAS2 = 1P97 */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9,
+ 0x7 << RG_AUDMICBIAS2VREF,
+ 0x0 << RG_AUDMICBIAS2VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6351_dapm_widgets[] = {
+ /* Digital Clock */
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_AFE_CTL", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_AFE_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_DAC_CTL", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_DAC_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_ADC_CTL", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_ADC_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PWR_CLK", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PWR_CLK_DIS_CTL_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PDN_RESERVED", MT6351_AUDIO_TOP_CON0,
+ AUD_TOP_PDN_RESERVED_BIT, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("NCP", MT6351_AFE_NCP_CFG0,
+ RG_NCP_ON_BIT, 0,
+ mt_ncp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* Global Supply*/
+ SND_SOC_DAPM_SUPPLY("AUDGLB", MT6351_AUDDEC_ANA_CON9,
+ RG_AUDGLB_PWRDN_VA32_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKSQ Audio", MT6351_TOP_CLKSQ,
+ RG_CLKSQ_EN_AUD_BIT, 0,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("ZCD13M_CK", MT6351_TOP_CKPDN_CON0,
+ RG_ZCD13M_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("AUD_CK", MT6351_TOP_CKPDN_CON0,
+ RG_AUD_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("AUDIF_CK", MT6351_TOP_CKPDN_CON0,
+ RG_AUDIF_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("AUDNCP_CK", MT6351_TOP_CKPDN_CON0,
+ RG_AUDNCP_CK_PDN_BIT, 1,
+ mt_reg_set_clr_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("AFE_ON", MT6351_AFE_UL_DL_CON0, RG_AFE_ON_BIT, 0,
+ NULL, 0),
+
+ /* AIF Rx*/
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0,
+ MT6351_AFE_DL_SRC2_CON0_L,
+ RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0,
+ mt_aif_in_event, SND_SOC_DAPM_PRE_PMU),
+
+ /* DL Supply */
+ SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("NV Regulator", MT6351_AUDDEC_ANA_CON10,
+ RG_NVREG_EN_VAUDP32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AUD_CLK", MT6351_AUDDEC_ANA_CON9,
+ RG_RSTB_DECODER_VA32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBIST", MT6351_AUDDEC_ANA_CON9,
+ RG_AUDIBIASPWRDN_VAUDP32_BIT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO", MT6351_AUDDEC_ANA_CON9,
+ RG_LCLDO_DEC_EN_VA32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO_REMOTE_SENSE", MT6351_AUDDEC_ANA_CON9,
+ RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT, 0, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, MT6351_AUDDEC_ANA_CON0,
+ RG_AUDDACLPWRUP_VAUDP32_BIT, 0),
+ SND_SOC_DAPM_SUPPLY("DACL_BIASGEN", MT6351_AUDDEC_ANA_CON0,
+ RG_AUD_DAC_PWL_UP_VA32_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DACR", NULL, MT6351_AUDDEC_ANA_CON0,
+ RG_AUDDACRPWRUP_VAUDP32_BIT, 0),
+ SND_SOC_DAPM_SUPPLY("DACR_BIASGEN", MT6351_AUDDEC_ANA_CON0,
+ RG_AUD_DAC_PWR_UP_VA32_BIT, 0, NULL, 0),
+ /* LOL */
+ SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6351_AUDDEC_ANA_CON3,
+ RG_LOOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LOL Bias Gen", MT6351_AUDDEC_ANA_CON6,
+ RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6351_AUDDEC_ANA_CON3,
+ RG_AUDLOLPWRUP_VAUDP32_BIT, 0, NULL, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_in_mux_control),
+ SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_in_mux_control),
+
+ SND_SOC_DAPM_OUT_DRV_E("HPL Power", MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPLPWRUP_VAUDP32_BIT, 0, NULL, 0,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("HPR Power", MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHPRPWRUP_VAUDP32_BIT, 0, NULL, 0,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Receiver */
+ SND_SOC_DAPM_MUX("RCV Mux", SND_SOC_NOPM, 0, 0, &rcv_in_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("RCV Stability Enh", MT6351_AUDDEC_ANA_CON1,
+ RG_HSOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RCV Bias Gen", MT6351_AUDDEC_ANA_CON6,
+ RG_ABIDEC_RSVD0_VAUDP32_HS_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("RCV Buffer", MT6351_AUDDEC_ANA_CON0,
+ RG_AUDHSPWRUP_VAUDP32_BIT, 0, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("Receiver"),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L"),
+
+ /* SGEN */
+ SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6351_AFE_SGEN_CFG0,
+ SGEN_C_DAC_EN_CTL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6351_AFE_SGEN_CFG0,
+ SGEN_C_MUTE_SW_CTL_BIT, 1,
+ mt_sgen_event, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6351_AFE_DL_SRC2_CON0_L,
+ RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0,
+ MT6351_AFE_UL_SRC_CON0_L,
+ UL_SRC_ON_TMP_CTL, 0,
+ mt_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO", SUPPLY_SUBSEQ_ENABLE,
+ MT6351_LDO_VUSB33_CON0, RG_VUSB33_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO_CTRL", SUPPLY_SUBSEQ_SETTING,
+ MT6351_LDO_VUSB33_CON0, RG_VUSB33_ON_CTRL, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("VA18_LDO", SUPPLY_SUBSEQ_ENABLE,
+ MT6351_LDO_VA18_CON0, RG_VA18_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VA18_LDO_CTRL", SUPPLY_SUBSEQ_SETTING,
+ MT6351_LDO_VA18_CON0, RG_VA18_ON_CTRL, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC CLKGEN", SUPPLY_SUBSEQ_ENABLE,
+ MT6351_AUDENC_ANA_CON3, RG_AUDADCCLKRSTB, 0,
+ mt_adc_clkgen_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* Uplinks MUX */
+ SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif_out_mux_control),
+
+ SND_SOC_DAPM_MUX("ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &adc_left_mux_control),
+ SND_SOC_DAPM_MUX("ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &adc_right_mux_control),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL,
+ MT6351_AUDENC_ANA_CON0, RG_AUDADCLPWRUP, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL,
+ MT6351_AUDENC_ANA_CON1, RG_AUDADCRPWRUP, 0),
+
+ SND_SOC_DAPM_MUX("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control),
+ SND_SOC_DAPM_MUX("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control),
+
+ SND_SOC_DAPM_PGA_E("PGA L", MT6351_AUDENC_ANA_CON0, RG_AUDPREAMPLON, 0,
+ NULL, 0,
+ mt_pga_left_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PGA R", MT6351_AUDENC_ANA_CON1, RG_AUDPREAMPRON, 0,
+ NULL, 0,
+ mt_pga_right_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* main mic mic bias */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 0", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS0, 0,
+ mt_mic_bias_0_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* ref mic mic bias */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 2", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS2, 0,
+ mt_mic_bias_2_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* headset mic1/2 mic bias */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 1", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON10, RG_AUDPWDBMICBIAS1, 0,
+ mt_mic_bias_1_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias 1 DCC pull high", SUPPLY_SUBSEQ_MICBIAS,
+ MT6351_AUDENC_ANA_CON10,
+ RG_AUDMICBIAS1DCSW1NEN, 0,
+ NULL, 0),
+
+ /* UL input */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+};
+
+static const struct snd_soc_dapm_route mt6351_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "AIF Out Mux"},
+ {"AIF1TX", NULL, "VUSB33_LDO"},
+ {"VUSB33_LDO", NULL, "VUSB33_LDO_CTRL"},
+ {"AIF1TX", NULL, "VA18_LDO"},
+ {"VA18_LDO", NULL, "VA18_LDO_CTRL"},
+
+ {"AIF1TX", NULL, "AUDGLB"},
+ {"AIF1TX", NULL, "CLKSQ Audio"},
+
+ {"AIF1TX", NULL, "AFE_ON"},
+
+ {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"},
+
+ {"AIF Out Mux", "Normal Path", "ADC L"},
+ {"AIF Out Mux", "Normal Path", "ADC R"},
+
+ {"ADC L", NULL, "ADC L Mux"},
+ {"ADC L", NULL, "AUD_CK"},
+ {"ADC L", NULL, "AUDIF_CK"},
+ {"ADC L", NULL, "ADC CLKGEN"},
+ {"ADC R", NULL, "ADC R Mux"},
+ {"ADC R", NULL, "AUD_CK"},
+ {"ADC R", NULL, "AUDIF_CK"},
+ {"ADC R", NULL, "ADC CLKGEN"},
+
+ {"ADC L Mux", "AIN0", "AIN0"},
+ {"ADC L Mux", "Left Preamplifier", "PGA L"},
+
+ {"ADC R Mux", "AIN0", "AIN0"},
+ {"ADC R Mux", "Right Preamplifier", "PGA R"},
+
+ {"PGA L", NULL, "PGA L Mux"},
+ {"PGA R", NULL, "PGA R Mux"},
+
+ {"PGA L Mux", "AIN0", "AIN0"},
+ {"PGA L Mux", "AIN1", "AIN1"},
+ {"PGA L Mux", "AIN2", "AIN2"},
+
+ {"PGA R Mux", "AIN0", "AIN0"},
+ {"PGA R Mux", "AIN3", "AIN3"},
+ {"PGA R Mux", "AIN2", "AIN2"},
+
+ {"AIN0", NULL, "Mic Bias 0"},
+ {"AIN2", NULL, "Mic Bias 2"},
+
+ {"AIN1", NULL, "Mic Bias 1"},
+ {"AIN1", NULL, "Mic Bias 1 DCC pull high"},
+
+ /* DL Supply */
+ {"DL Power Supply", NULL, "AUDGLB"},
+ {"DL Power Supply", NULL, "CLKSQ Audio"},
+ {"DL Power Supply", NULL, "ZCD13M_CK"},
+ {"DL Power Supply", NULL, "AUD_CK"},
+ {"DL Power Supply", NULL, "AUDIF_CK"},
+ {"DL Power Supply", NULL, "AUDNCP_CK"},
+
+ {"DL Power Supply", NULL, "NV Regulator"},
+ {"DL Power Supply", NULL, "AUD_CLK"},
+ {"DL Power Supply", NULL, "IBIST"},
+ {"DL Power Supply", NULL, "LDO"},
+ {"LDO", NULL, "LDO_REMOTE_SENSE"},
+
+ /* DL Digital Supply */
+ {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"DL Digital Clock", NULL, "NCP"},
+ {"DL Digital Clock", NULL, "AFE_ON"},
+
+ {"AIF_RX", NULL, "DL Digital Clock"},
+
+ /* DL Path */
+ {"DAC In Mux", "Normal Path", "AIF_RX"},
+
+ {"DAC In Mux", "Sgen", "SGEN DL"},
+ {"SGEN DL", NULL, "SGEN DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN DL Enable"},
+ {"SGEN DL", NULL, "DL Digital Clock"},
+
+ {"DACL", NULL, "DAC In Mux"},
+ {"DACL", NULL, "DL Power Supply"},
+ {"DACL", NULL, "DACL_BIASGEN"},
+
+ {"DACR", NULL, "DAC In Mux"},
+ {"DACR", NULL, "DL Power Supply"},
+ {"DACR", NULL, "DACR_BIASGEN"},
+
+ {"LOL Mux", "Playback", "DACL"},
+
+ {"LOL Buffer", NULL, "LOL Mux"},
+ {"LOL Buffer", NULL, "LO Stability Enh"},
+ {"LOL Buffer", NULL, "LOL Bias Gen"},
+
+ {"LINEOUT L", NULL, "LOL Buffer"},
+
+ /* Headphone Path */
+ {"HPL Mux", "Audio Playback", "DACL"},
+ {"HPR Mux", "Audio Playback", "DACR"},
+
+ {"HPL Mux", "LoudSPK Playback", "DACL"},
+ {"HPR Mux", "LoudSPK Playback", "DACR"},
+
+ {"HPL Power", NULL, "HPL Mux"},
+ {"HPR Power", NULL, "HPR Mux"},
+
+ {"Headphone L", NULL, "HPL Power"},
+ {"Headphone R", NULL, "HPR Power"},
+
+ /* Receiver Path */
+ {"RCV Mux", "Voice Playback", "DACL"},
+
+ {"RCV Buffer", NULL, "RCV Mux"},
+ {"RCV Buffer", NULL, "RCV Stability Enh"},
+ {"RCV Buffer", NULL, "RCV Bias Gen"},
+
+ {"Receiver", NULL, "RCV Buffer"},
+};
+
+static int mt6351_codec_init_reg(struct snd_soc_component *cmpnt)
+{
+ /* Disable CLKSQ 26MHz */
+ regmap_update_bits(cmpnt->regmap, MT6351_TOP_CLKSQ, 0x0001, 0x0);
+ /* disable AUDGLB */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON9,
+ 0x1000, 0x1000);
+ /* Turn off AUDNCP_CLKDIV engine clock,Turn off AUD 26M */
+ regmap_update_bits(cmpnt->regmap, MT6351_TOP_CKPDN_CON0_SET,
+ 0x3800, 0x3800);
+ /* Disable HeadphoneL/HeadphoneR/voice short circuit protection */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0,
+ 0xe000, 0xe000);
+ /* [5] = 1, disable LO buffer left short circuit protection */
+ regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON3,
+ 0x20, 0x20);
+ /* Reverse the PMIC clock*/
+ regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2,
+ 0x8000, 0x8000);
+ return 0;
+}
+
+static int mt6351_codec_probe(struct snd_soc_component *cmpnt)
+{
+ struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ snd_soc_component_init_regmap(cmpnt, priv->regmap);
+
+ mt6351_codec_init_reg(cmpnt);
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6351_soc_component_driver = {
+ .probe = mt6351_codec_probe,
+ .controls = mt6351_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6351_snd_controls),
+ .dapm_widgets = mt6351_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets),
+ .dapm_routes = mt6351_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes),
+ .endianness = 1,
+};
+
+static int mt6351_codec_driver_probe(struct platform_device *pdev)
+{
+ struct mt6351_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct mt6351_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ priv->dev = &pdev->dev;
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap)
+ return -ENODEV;
+
+ dev_dbg(priv->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6351_soc_component_driver,
+ mt6351_dai_driver,
+ ARRAY_SIZE(mt6351_dai_driver));
+}
+
+static const struct of_device_id mt6351_of_match[] = {
+ {.compatible = "mediatek,mt6351-sound",},
+ {}
+};
+
+static struct platform_driver mt6351_codec_driver = {
+ .driver = {
+ .name = "mt6351-sound",
+ .of_match_table = mt6351_of_match,
+ },
+ .probe = mt6351_codec_driver_probe,
+};
+
+module_platform_driver(mt6351_codec_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6351 ALSA SoC codec driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6351.h b/sound/soc/codecs/mt6351.h
new file mode 100644
index 000000000000..04b2ab694ec7
--- /dev/null
+++ b/sound/soc/codecs/mt6351.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6351.h -- mt6351 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef __MT6351_H__
+#define __MT6351_H__
+
+#define MT6351_AFE_UL_DL_CON0 (0x2000 + 0x0000)
+#define MT6351_AFE_DL_SRC2_CON0_H (0x2000 + 0x0002)
+#define MT6351_AFE_DL_SRC2_CON0_L (0x2000 + 0x0004)
+#define MT6351_AFE_DL_SDM_CON0 (0x2000 + 0x0006)
+#define MT6351_AFE_DL_SDM_CON1 (0x2000 + 0x0008)
+#define MT6351_AFE_UL_SRC_CON0_H (0x2000 + 0x000a)
+#define MT6351_AFE_UL_SRC_CON0_L (0x2000 + 0x000c)
+#define MT6351_AFE_UL_SRC_CON1_H (0x2000 + 0x000e)
+#define MT6351_AFE_UL_SRC_CON1_L (0x2000 + 0x0010)
+#define MT6351_AFE_TOP_CON0 (0x2000 + 0x0012)
+#define MT6351_AUDIO_TOP_CON0 (0x2000 + 0x0014)
+#define MT6351_AFE_DL_SRC_MON0 (0x2000 + 0x0016)
+#define MT6351_AFE_DL_SDM_TEST0 (0x2000 + 0x0018)
+#define MT6351_AFE_MON_DEBUG0 (0x2000 + 0x001a)
+#define MT6351_AFUNC_AUD_CON0 (0x2000 + 0x001c)
+#define MT6351_AFUNC_AUD_CON1 (0x2000 + 0x001e)
+#define MT6351_AFUNC_AUD_CON2 (0x2000 + 0x0020)
+#define MT6351_AFUNC_AUD_CON3 (0x2000 + 0x0022)
+#define MT6351_AFUNC_AUD_CON4 (0x2000 + 0x0024)
+#define MT6351_AFUNC_AUD_MON0 (0x2000 + 0x0026)
+#define MT6351_AFUNC_AUD_MON1 (0x2000 + 0x0028)
+#define MT6351_AFE_UP8X_FIFO_CFG0 (0x2000 + 0x002c)
+#define MT6351_AFE_UP8X_FIFO_LOG_MON0 (0x2000 + 0x002e)
+#define MT6351_AFE_UP8X_FIFO_LOG_MON1 (0x2000 + 0x0030)
+#define MT6351_AFE_DL_DC_COMP_CFG0 (0x2000 + 0x0032)
+#define MT6351_AFE_DL_DC_COMP_CFG1 (0x2000 + 0x0034)
+#define MT6351_AFE_DL_DC_COMP_CFG2 (0x2000 + 0x0036)
+#define MT6351_AFE_PMIC_NEWIF_CFG0 (0x2000 + 0x0038)
+#define MT6351_AFE_PMIC_NEWIF_CFG1 (0x2000 + 0x003a)
+#define MT6351_AFE_PMIC_NEWIF_CFG2 (0x2000 + 0x003c)
+#define MT6351_AFE_PMIC_NEWIF_CFG3 (0x2000 + 0x003e)
+#define MT6351_AFE_SGEN_CFG0 (0x2000 + 0x0040)
+#define MT6351_AFE_SGEN_CFG1 (0x2000 + 0x0042)
+#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON0 (0x2000 + 0x004c)
+#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON1 (0x2000 + 0x004e)
+#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG0 (0x2000 + 0x0050)
+#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG1 (0x2000 + 0x0052)
+#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG2 (0x2000 + 0x0054)
+#define MT6351_AFE_DCCLK_CFG0 (0x2000 + 0x0090)
+#define MT6351_AFE_DCCLK_CFG1 (0x2000 + 0x0092)
+#define MT6351_AFE_HPANC_CFG0 (0x2000 + 0x0094)
+#define MT6351_AFE_NCP_CFG0 (0x2000 + 0x0096)
+#define MT6351_AFE_NCP_CFG1 (0x2000 + 0x0098)
+
+#define MT6351_TOP_CKPDN_CON0 0x023A
+#define MT6351_TOP_CKPDN_CON0_SET 0x023C
+#define MT6351_TOP_CKPDN_CON0_CLR 0x023E
+
+#define MT6351_TOP_CLKSQ 0x029A
+#define MT6351_TOP_CLKSQ_SET 0x029C
+#define MT6351_TOP_CLKSQ_CLR 0x029E
+
+#define MT6351_ZCD_CON0 0x0800
+#define MT6351_ZCD_CON1 0x0802
+#define MT6351_ZCD_CON2 0x0804
+#define MT6351_ZCD_CON3 0x0806
+#define MT6351_ZCD_CON4 0x0808
+#define MT6351_ZCD_CON5 0x080A
+
+#define MT6351_LDO_VA18_CON0 0x0A00
+#define MT6351_LDO_VA18_CON1 0x0A02
+#define MT6351_LDO_VUSB33_CON0 0x0A16
+#define MT6351_LDO_VUSB33_CON1 0x0A18
+
+#define MT6351_AUDDEC_ANA_CON0 0x0CF2
+#define MT6351_AUDDEC_ANA_CON1 0x0CF4
+#define MT6351_AUDDEC_ANA_CON2 0x0CF6
+#define MT6351_AUDDEC_ANA_CON3 0x0CF8
+#define MT6351_AUDDEC_ANA_CON4 0x0CFA
+#define MT6351_AUDDEC_ANA_CON5 0x0CFC
+#define MT6351_AUDDEC_ANA_CON6 0x0CFE
+#define MT6351_AUDDEC_ANA_CON7 0x0D00
+#define MT6351_AUDDEC_ANA_CON8 0x0D02
+#define MT6351_AUDDEC_ANA_CON9 0x0D04
+#define MT6351_AUDDEC_ANA_CON10 0x0D06
+
+#define MT6351_AUDENC_ANA_CON0 0x0D08
+#define MT6351_AUDENC_ANA_CON1 0x0D0A
+#define MT6351_AUDENC_ANA_CON2 0x0D0C
+#define MT6351_AUDENC_ANA_CON3 0x0D0E
+#define MT6351_AUDENC_ANA_CON4 0x0D10
+#define MT6351_AUDENC_ANA_CON5 0x0D12
+#define MT6351_AUDENC_ANA_CON6 0x0D14
+#define MT6351_AUDENC_ANA_CON7 0x0D16
+#define MT6351_AUDENC_ANA_CON8 0x0D18
+#define MT6351_AUDENC_ANA_CON9 0x0D1A
+#define MT6351_AUDENC_ANA_CON10 0x0D1C
+#define MT6351_AUDENC_ANA_CON11 0x0D1E
+#define MT6351_AUDENC_ANA_CON12 0x0D20
+#define MT6351_AUDENC_ANA_CON13 0x0D22
+#define MT6351_AUDENC_ANA_CON14 0x0D24
+#define MT6351_AUDENC_ANA_CON15 0x0D26
+#define MT6351_AUDENC_ANA_CON16 0x0D28
+#endif
diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c
new file mode 100644
index 000000000000..674cf7df9df4
--- /dev/null
+++ b/sound/soc/codecs/mt6357.c
@@ -0,0 +1,1855 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MT6357 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2024 Baylibre
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/regulator/consumer.h>
+
+#include "mt6357.h"
+
+static void set_playback_gpio(struct mt6357_priv *priv, bool enable)
+{
+ regmap_write(priv->regmap, MT6357_GPIO_MODE2_CLR, MT6357_GPIO_MODE2_CLEAR_ALL);
+ if (enable) {
+ /* set gpio mosi mode */
+ regmap_write(priv->regmap, MT6357_GPIO_MODE2_SET,
+ MT6357_GPIO8_MODE_SET_AUD_CLK_MOSI |
+ MT6357_GPIO9_MODE_SET_AUD_DAT_MOSI0 |
+ MT6357_GPIO10_MODE_SET_AUD_DAT_MOSI1 |
+ MT6357_GPIO11_MODE_SET_AUD_SYNC_MOSI);
+ } else {
+ /* pad_aud_*_mosi are GPIO mode after clear and set them to dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ */
+ regmap_update_bits(priv->regmap, MT6357_GPIO_DIR0,
+ MT6357_GPIO8_DIR_MASK |
+ MT6357_GPIO9_DIR_MASK |
+ MT6357_GPIO10_DIR_MASK |
+ MT6357_GPIO11_DIR_MASK,
+ MT6357_GPIO8_DIR_INPUT |
+ MT6357_GPIO9_DIR_INPUT |
+ MT6357_GPIO10_DIR_INPUT |
+ MT6357_GPIO11_DIR_INPUT);
+ }
+}
+
+static void set_capture_gpio(struct mt6357_priv *priv, bool enable)
+{
+ regmap_write(priv->regmap, MT6357_GPIO_MODE3_CLR, MT6357_GPIO_MODE3_CLEAR_ALL);
+ if (enable) {
+ /* set gpio miso mode */
+ regmap_write(priv->regmap, MT6357_GPIO_MODE3_SET,
+ MT6357_GPIO12_MODE_SET_AUD_CLK_MISO |
+ MT6357_GPIO13_MODE_SET_AUD_DAT_MISO0 |
+ MT6357_GPIO14_MODE_SET_AUD_DAT_MISO1 |
+ MT6357_GPIO15_MODE_SET_AUD_SYNC_MISO);
+ } else {
+ /* pad_aud_*_mosi are GPIO mode after clear and set them to dir input
+ * reason:
+ * pad_aud_clk_miso, because when playback only the miso_clk
+ * will also have 26m, so will have power leak
+ * pad_aud_dat_miso*, because the pin is used as boot strap
+ */
+ regmap_update_bits(priv->regmap, MT6357_GPIO_DIR0,
+ MT6357_GPIO12_DIR_MASK |
+ MT6357_GPIO13_DIR_MASK |
+ MT6357_GPIO14_DIR_MASK |
+ MT6357_GPIO15_DIR_MASK,
+ MT6357_GPIO12_DIR_INPUT |
+ MT6357_GPIO13_DIR_INPUT |
+ MT6357_GPIO14_DIR_INPUT |
+ MT6357_GPIO15_DIR_INPUT);
+ }
+}
+
+static void hp_main_output_ramp(struct mt6357_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX; i++) {
+ stage = up ? i : MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX - i;
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPLOUT_STG_CTRL_VAUDP15_MASK,
+ stage << MT6357_HPLOUT_STG_CTRL_VAUDP15_SFT);
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_STG_CTRL_VAUDP15_MASK,
+ stage << MT6357_HPROUT_STG_CTRL_VAUDP15_SFT);
+ usleep_range(600, 700);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6357_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= MT6357_HP_AUX_LOOP_GAIN_MAX; i++) {
+ stage = up ? i : MT6357_HP_AUX_LOOP_GAIN_MAX - i;
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_AUX_LOOP_GAIN_MASK,
+ stage << MT6357_HP_AUX_LOOP_GAIN_SFT);
+ usleep_range(600, 700);
+ }
+}
+
+static void hp_pull_down(struct mt6357_priv *priv, bool enable)
+{
+ if (enable)
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_MASK,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_ENABLE);
+ else
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_MASK,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_DISABLE);
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_12DB) || reg_idx == DL_GAIN_N_40DB;
+}
+
+static void volume_ramp(struct mt6357_priv *priv, int lfrom, int lto,
+ int rfrom, int rto, unsigned int reg_addr)
+{
+ int lcount, rcount, sleep = 0;
+
+ if (!is_valid_hp_pga_idx(lfrom) || !is_valid_hp_pga_idx(lto))
+ pr_debug("%s(), invalid left volume index, from %d, to %d\n",
+ __func__, lfrom, lto);
+
+ if (!is_valid_hp_pga_idx(rfrom) || !is_valid_hp_pga_idx(rto))
+ pr_debug("%s(), invalid right volume index, from %d, to %d\n",
+ __func__, rfrom, rto);
+
+ if (lto > lfrom)
+ lcount = 1;
+ else
+ lcount = -1;
+
+ if (rto > rfrom)
+ rcount = 1;
+ else
+ rcount = -1;
+
+ while ((lto != lfrom) || (rto != rfrom)) {
+ if (lto != lfrom) {
+ lfrom += lcount;
+ if (is_valid_hp_pga_idx(lfrom)) {
+ regmap_update_bits(priv->regmap, reg_addr,
+ MT6357_DL_GAIN_REG_LEFT_MASK,
+ lfrom << MT6357_DL_GAIN_REG_LEFT_SHIFT);
+ sleep = 1;
+ }
+ }
+ if (rto != rfrom) {
+ rfrom += rcount;
+ if (is_valid_hp_pga_idx(rfrom)) {
+ regmap_update_bits(priv->regmap, reg_addr,
+ MT6357_DL_GAIN_REG_RIGHT_MASK,
+ rfrom << MT6357_DL_GAIN_REG_RIGHT_SHIFT);
+ sleep = 1;
+ }
+ }
+ if (sleep)
+ usleep_range(200, 300);
+ }
+}
+
+static void lo_volume_ramp(struct mt6357_priv *priv, int lfrom, int lto, int rfrom, int rto)
+{
+ volume_ramp(priv, lfrom, lto, rfrom, rto, MT6357_ZCD_CON1);
+}
+
+static void hp_volume_ramp(struct mt6357_priv *priv, int lfrom, int lto, int rfrom, int rto)
+{
+ volume_ramp(priv, lfrom, lto, rfrom, rto, MT6357_ZCD_CON2);
+}
+
+static void hs_volume_ramp(struct mt6357_priv *priv, int from, int to)
+{
+ volume_ramp(priv, from, to, 0, 0, MT6357_ZCD_CON3);
+}
+
+/* Volume and channel swap controls */
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(hp_degain_tlv, -1200, 1200, 0);
+
+static const struct snd_kcontrol_new mt6357_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_TLV("Headphone Volume",
+ MT6357_ZCD_CON2, MT6357_AUD_HPL_GAIN_SFT,
+ MT6357_AUD_HPR_GAIN_SFT, MT6357_AUD_HP_GAIN_MAX,
+ 1, playback_tlv),
+ SOC_SINGLE_TLV("Headphone Vin Volume",
+ MT6357_AUDDEC_ANA_CON7, MT6357_HP_IVBUF_DEGAIN_SFT,
+ MT6357_HP_IVBUF_DEGAIN_MAX, 1, hp_degain_tlv),
+ SOC_DOUBLE_TLV("Lineout Volume",
+ MT6357_ZCD_CON1, MT6357_AUD_LOL_GAIN_SFT,
+ MT6357_AUD_LOR_GAIN_SFT, MT6357_AUD_LO_GAIN_MAX,
+ 1, playback_tlv),
+ SOC_SINGLE_TLV("Handset Volume",
+ MT6357_ZCD_CON3, MT6357_AUD_HS_GAIN_SFT,
+ MT6357_AUD_HS_GAIN_MAX, 1, playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_TLV("Mic Volume",
+ MT6357_AUDENC_ANA_CON0, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPLGAIN_SFT, MT6357_AUDPREAMPLGAIN_MAX,
+ 0, capture_tlv),
+};
+
+/* Uplink controls */
+
+enum {
+ MIC_TYPE_MUX_IDLE,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+ MIC_TYPE_MUX_LPBK,
+ MIC_TYPE_MUX_SGEN,
+};
+
+#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+
+static const char * const mic_type_mux_map[] = {
+ "Idle",
+ "ACC",
+ "DMIC",
+ "DCC",
+ "DCC_ECM_DIFF",
+ "DCC_ECM_SINGLE",
+ "Loopback",
+ "Sine Generator",
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_type_mux_map_enum, SND_SOC_NOPM,
+ 0, mic_type_mux_map);
+
+static const struct snd_kcontrol_new mic_type_mux_control =
+ SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum);
+
+static const char * const pga_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2"
+};
+
+static SOC_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLINPUTSEL_SFT,
+ pga_mux_map);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRINPUTSEL_SFT,
+ pga_mux_map);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+/* Downlink controls */
+static const char * const hslo_mux_map[] = {
+ "Open", "DACR", "Playback", "Test mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(lo_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_MUX_INPUT_VAUDP15_SFT,
+ hslo_mux_map);
+
+static const struct snd_kcontrol_new lo_mux_control =
+ SOC_DAPM_ENUM("Line out source", lo_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(hs_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_MUX_INPUT_VAUDP15_SFT,
+ hslo_mux_map);
+
+static const struct snd_kcontrol_new hs_mux_control =
+ SOC_DAPM_ENUM("Handset source", hs_mux_map_enum);
+
+static const char * const hplr_mux_map[] = {
+ "Open", "Line Out", "DAC", "Handset"
+};
+
+static SOC_ENUM_SINGLE_DECL(hpr_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_MUX_INPUT_VAUDP15_SFT,
+ hplr_mux_map);
+
+static const struct snd_kcontrol_new hpr_mux_control =
+ SOC_DAPM_ENUM("Headphone Right source", hpr_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(hpl_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPL_MUX_INPUT_VAUDP15_SFT,
+ hplr_mux_map);
+
+static const struct snd_kcontrol_new hpl_mux_control =
+ SOC_DAPM_ENUM("Headphone Left source", hpl_mux_map_enum);
+
+static const char * const dac_mux_map[] = {
+ "Normal Path", "Sine Generator"
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_mux_map_enum,
+ MT6357_AFE_TOP_CON0,
+ MT6357_DL_SINE_ON_SFT,
+ dac_mux_map);
+
+static const struct snd_kcontrol_new dac_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_mux_map_enum);
+
+static int mt6357_set_dmic(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* DMIC enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON7,
+ MT6357_AUDDIGMICBIAS_MASK | MT6357_AUDDIGMICEN_MASK,
+ MT6357_AUDDIGMICBIAS_DEFAULT_VALUE | MT6357_AUDDIGMICEN_ENABLE);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL dmic setting: dual mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_H,
+ MT6357_C_TWO_DIGITAL_MIC_CTL_MASK,
+ MT6357_C_TWO_DIGITAL_MIC_ENABLE);
+ /* UL turn on SDM 3 level mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SDM_3_LEVEL_CTL_MASK,
+ MT6357_UL_SDM_3_LEVEL_SELECT);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ /* Wait to avoid any pop noises */
+ msleep(100);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* UL turn on SDM 3 level mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SDM_3_LEVEL_CTL_MASK,
+ MT6357_UL_SDM_3_LEVEL_DESELECT);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ /* UL dmic setting: dual mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_H,
+ MT6357_C_TWO_DIGITAL_MIC_CTL_MASK,
+ MT6357_C_TWO_DIGITAL_MIC_DISABLE);
+ /* DMIC disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON7,
+ MT6357_AUDDIGMICBIAS_MASK | MT6357_AUDDIGMICEN_MASK,
+ MT6357_AUDDIGMICBIAS_OFF | MT6357_AUDDIGMICEN_DISABLE);
+ }
+ return 0;
+}
+
+static int mt6357_set_amic(struct mt6357_priv *priv, bool enable, unsigned int mic_type)
+{
+ if (enable) {
+ if (IS_DCC_BASE(mic_type)) {
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_DIV_MASK, MT6357_DCCLK_DIV_RUN_VALUE);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_PDN_MASK, MT6357_DCCLK_OUTPUT);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_GEN_ON_MASK, MT6357_DCCLK_GEN_ON);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG1,
+ MT6357_DCCLK_RESYNC_BYPASS_MASK,
+ MT6357_DCCLK_RESYNC_BYPASS);
+
+ /* mic bias 0: set the correct DC couple*/
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_ENABLE_ALL);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_ENABLE_P1);
+ break;
+ default:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_DISABLE_ALL);
+ break;
+ }
+
+ /* mic bias 1: set the correct DC couple */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK,
+ MT6357_AUD_MICBIAS1_DCSW1P_ENABLE);
+
+ /* Audio L/R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_ENABLE);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_ENABLE);
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_DC);
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK,
+ MT6357_AUDPREAMPRDCCEN_DC);
+ } else {
+ /* Audio L preamplifier DCC precharge disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ /* L preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_AC);
+ /* Audio R preamplifier DCC precharge disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ /* R preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK,
+ MT6357_AUDPREAMPRDCCEN_AC);
+ }
+ } else {
+ /* disable any Mic Bias 0 DC couple */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_DISABLE_ALL);
+ /* disable any Mic Bias 1 DC couple */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK,
+ MT6357_AUD_MICBIAS1_DCSW1P_DISABLE);
+ if (IS_DCC_BASE(mic_type)) {
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_GEN_ON_MASK, MT6357_DCCLK_GEN_OFF);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_PDN_MASK, MT6357_DCCLK_PDN);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_DIV_MASK, MT6357_DCCLK_DIV_STOP_VALUE);
+ }
+ }
+
+ return 0;
+}
+
+static int mt6357_set_loopback(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* enable aud_pad lpk TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_MASK,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_ENABLE);
+ /* Set UL Part: enable new lpbk 2 */
+ regmap_update_bits(priv->regmap, MT6357_AFE_ADDA_MTKAIF_CFG0,
+ MT6357_ADDA_MTKAIF_LPBK_CTL_MASK,
+ MT6357_ADDA_MTKAIF_LPBK_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* disable new lpbk 2 */
+ regmap_update_bits(priv->regmap, MT6357_AFE_ADDA_MTKAIF_CFG0,
+ MT6357_ADDA_MTKAIF_LPBK_CTL_MASK,
+ MT6357_ADDA_MTKAIF_LPBK_DISABLE);
+ /* disable aud_pad lpbk TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_MASK,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ }
+
+ return 0;
+}
+
+static int mt6357_set_ul_sine_gen(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ }
+
+ return 0;
+}
+
+static int mt_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_capture_gpio(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ set_capture_gpio(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_ENCODER_VA28_MASK, MT6357_RSTB_ENCODER_VA28_ENABLE);
+ /* Enable LCLDO_ENC 2P8V */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_LCLDO_ENC_EN_VA28_MASK, MT6357_LCLDO_ENC_EN_VA28_ENABLE);
+ /* LCLDO_ENC remote sense */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA28REFGEN_EN_VA28_MASK |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK,
+ MT6357_VA28REFGEN_EN_VA28_ENABLE |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* LCLDO_ENC remote sense off */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA28REFGEN_EN_VA28_MASK |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK,
+ MT6357_VA28REFGEN_EN_VA28_DISABLE |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_DISABLE);
+ /* disable LCLDO_ENC 2P8V */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_LCLDO_ENC_EN_VA28_MASK,
+ MT6357_LCLDO_ENC_EN_VA28_DISABLE);
+ /* disable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_ENCODER_VA28_MASK,
+ MT6357_RSTB_ENCODER_VA28_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_type_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6357_set_dmic(priv, true);
+ break;
+ case MIC_TYPE_MUX_LPBK:
+ mt6357_set_loopback(priv, true);
+ break;
+ case MIC_TYPE_MUX_SGEN:
+ mt6357_set_ul_sine_gen(priv, true);
+ break;
+ default:
+ mt6357_set_amic(priv, true, mic_type);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6357_set_dmic(priv, false);
+ break;
+ case MIC_TYPE_MUX_LPBK:
+ mt6357_set_loopback(priv, false);
+ break;
+ case MIC_TYPE_MUX_SGEN:
+ mt6357_set_ul_sine_gen(priv, false);
+ break;
+ default:
+ mt6357_set_amic(priv, false, mic_type);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* L preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLON_MASK,
+ MT6357_AUDPREAMPLON_ENABLE);
+ /* L ADC input sel : L PGA. Enable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLINPUTSEL_MASK,
+ MT6357_AUDADCLINPUTSEL_PREAMPLIFIER);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLPWRUP_MASK,
+ MT6357_AUDADCLPWRUP);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Audio L ADC input sel : off, disable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLPWRUP_MASK,
+ MT6357_AUDADCLPWRDOWN);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLINPUTSEL_MASK,
+ MT6357_AUDADCLINPUTSEL_IDLE);
+ /* L preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_AC);
+ /* L preamplifier disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLON_MASK,
+ MT6357_AUDPREAMPLON_DISABLE);
+ /* disable Audio L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* R preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRON_MASK, MT6357_AUDPREAMPRON_ENABLE);
+ /* R ADC input sel : R PGA. Enable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRINPUTSEL_MASK,
+ MT6357_AUDADCRINPUTSEL_PREAMPLIFIER);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRPWRUP_MASK, MT6357_AUDADCRPWRUP);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Audio R ADC input sel : off, disable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRPWRUP_MASK, MT6357_AUDADCRPWRDOWN);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCRINPUTSEL_MASK, MT6357_AUDADCRINPUTSEL_IDLE);
+ /* R preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK, MT6357_AUDPREAMPRDCCEN_AC);
+ /* R preamplifier disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRON_MASK, MT6357_AUDPREAMPRON_DISABLE);
+ /* disable Audio R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int adc_enable_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_read(priv->regmap, MT6357_AUDENC_ANA_CON0, &lgain);
+ regmap_read(priv->regmap, MT6357_AUDENC_ANA_CON1, &rgain);
+ /* L PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLGAIN_MASK,
+ UL_GAIN_0DB << MT6357_AUDPREAMPLGAIN_SFT);
+ /* R PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRGAIN_MASK,
+ UL_GAIN_0DB << MT6357_AUDPREAMPRGAIN_SFT);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK, MT6357_UL_SRC_ENABLE);
+ /* Wait to avoid any pop noises */
+ msleep(100);
+ /* set the mic gains to the stored values */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLGAIN_MASK, lgain);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRGAIN_MASK, rgain);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK, MT6357_UL_SRC_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void configure_downlinks(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ELR_0,
+ MT6357_AUD_HP_TRIM_EN_VAUDP15_MASK,
+ MT6357_AUD_HP_TRIM_EN_VAUDP15_ENABLE);
+ /* Disable headphone short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK | MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_DISABLE);
+ /* Disable handset short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_DISABLE);
+ /* Disable lineout short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_DISABLE);
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_AUD_REFN_DERES_VAUDP15_MASK,
+ MT6357_AUD_REFN_DERES_VAUDP15_ENABLE);
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON1, MT6357_DIVCKS_ON);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON0, MT6357_DIVCKS_CHG);
+ /* Set NCP soft start mode as default mode: 150us */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON4,
+ MT6357_DIVCKS_PWD_NCP_ST_150US);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON3,
+ MT6357_DIVCKS_PWD_NCP_ENABLE);
+ usleep_range(250, 270);
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA33REFGEN_EN_VA18_MASK |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_LCLDO_EN_VA18_MASK |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_HCLDO_EN_VA18_MASK,
+ MT6357_VA33REFGEN_EN_VA18_ENABLE |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_ENABLE |
+ MT6357_LCLDO_EN_VA18_ENABLE |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_ENABLE |
+ MT6357_HCLDO_EN_VA18_ENABLE);
+ /* Enable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON13,
+ MT6357_NVREG_EN_VAUDP15_MASK, MT6357_NVREG_EN_VAUDP15_ENABLE);
+ usleep_range(100, 120);
+ /* Enable IBIST */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON10,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_ENABLE);
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_DECODER_VA28_MASK,
+ MT6357_RSTB_DECODER_VA28_ENABLE);
+ /* Enable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_DAC_LOW_NOISE_MODE_MASK,
+ MT6357_DAC_LOW_NOISE_MODE_ENABLE);
+ usleep_range(100, 120);
+ } else {
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_DAC_LOW_NOISE_MODE_MASK,
+ MT6357_DAC_LOW_NOISE_MODE_DISABLE);
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_DECODER_VA28_MASK,
+ MT6357_RSTB_DECODER_VA28_DISABLE);
+ /* Enable linout short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_ENABLE);
+ /* Enable handset short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_ENABLE);
+ /* Enable headphone short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK |
+ MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_ENABLE);
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON10,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_DISABLE);
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON13,
+ MT6357_NVREG_EN_VAUDP15_MASK,
+ MT6357_NVREG_EN_VAUDP15_DISABLE);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA33REFGEN_EN_VA18_MASK |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_LCLDO_EN_VA18_MASK |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_HCLDO_EN_VA18_MASK,
+ MT6357_VA33REFGEN_EN_VA18_DISABLE |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_DISABLE |
+ MT6357_LCLDO_EN_VA18_DISABLE |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_DISABLE |
+ MT6357_HCLDO_EN_VA18_DISABLE);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6357_AUDNCP_CLKDIV_CON3,
+ MT6357_DIVCKS_PWD_NCP_MASK, MT6357_DIVCKS_PWD_NCP_DISABLE);
+ }
+}
+
+static int mt_audio_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_playback_gpio(priv, true);
+
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+
+ /* Disable HP main CMFB Switch */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE);
+ /* Audio system digital clock power down release */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_NORMAL_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RESET);
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON0,
+ MT6357_CCI_AUD_ANACK_INVERT |
+ (4 << MT6357_CCI_AUDIO_FIFO_WPTR_SFT) |
+ MT6357_CCI_SCRAMBLER_CG_ENABLE |
+ MT6357_CCI_RAND_ENABLE |
+ MT6357_CCI_SPLT_SCRMB_CLK_ON |
+ MT6357_CCI_SPLT_SCRMB_ON |
+ MT6357_CCI_ZERO_PADDING_DISABLE |
+ MT6357_CCI_SCRAMBLER_ENABLE);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RELEASE);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_ENABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RELEASE);
+
+ configure_downlinks(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ configure_downlinks(priv, false);
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_DOWN |
+ MT6357_CCI_ACD_FUNC_RSTB_RESET);
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON0,
+ MT6357_CCI_AUD_ANACK_INVERT |
+ (4 << MT6357_CCI_AUDIO_FIFO_WPTR_SFT) |
+ MT6357_CCI_SCRAMBLER_CG_ENABLE |
+ MT6357_CCI_RAND_ENABLE |
+ MT6357_CCI_SPLT_SCRMB_CLK_ON |
+ MT6357_CCI_SPLT_SCRMB_ON |
+ MT6357_CCI_ZERO_PADDING_DISABLE |
+ MT6357_CCI_SCRAMBLER_DISABLE);
+
+ set_playback_gpio(priv, false);
+
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_delay_250_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(250, 270);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int lo_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON1, &lgain);
+ rgain = (lgain & MT6357_AUD_LOR_GAIN_MASK) >> MT6357_AUD_LOR_GAIN_SFT;
+ lgain = lgain & MT6357_AUD_LOL_GAIN_MASK;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON1,
+ MT6357_AUD_LOL_GAIN_MASK |
+ MT6357_AUD_LOR_GAIN_MASK,
+ MT6357_DL_GAIN_N_40DB_REG);
+ /* Set LO STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_ENABLE);
+ /* Enable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Enable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_ENABLE);
+ /* Set LOL gain to normal gain step by step */
+ lo_volume_ramp(priv, DL_GAIN_N_40DB, lgain,
+ DL_GAIN_N_40DB, rgain);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* decrease LOL gain to minimum gain step by step */
+
+ lo_volume_ramp(priv, lgain, DL_GAIN_N_40DB,
+ rgain, DL_GAIN_N_40DB);
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_DISABLE);
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_DISABLE);
+ /* Clear LO STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON1,
+ MT6357_AUD_LOL_GAIN_MASK |
+ MT6357_AUD_LOR_GAIN_MASK,
+ lgain << MT6357_AUD_LOL_GAIN_SFT |
+ rgain << MT6357_AUD_LOR_GAIN_SFT);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int hs_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int gain; /* HS register has only one gain slot */
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON3, &gain);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON3,
+ MT6357_AUD_HS_GAIN_MASK,
+ DL_GAIN_N_40DB);
+
+ /* Set HS STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_ENABLE);
+ /* Enable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Enable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_VAUDP15_ENABLE);
+ /* Set HS gain to normal gain step by step */
+ hs_volume_ramp(priv, DL_GAIN_N_40DB, gain);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* decrease HS gain to minimum gain step by step */
+ hs_volume_ramp(priv, gain, DL_GAIN_N_40DB);
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_VAUDP15_DISABLE);
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Clear HS STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON3,
+ MT6357_AUD_HS_GAIN_MASK, gain);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int hp_main_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON2, &lgain);
+ rgain = (lgain & MT6357_AUD_HPR_GAIN_MASK) >> MT6357_AUD_HPR_GAIN_SFT;
+ lgain = lgain & MT6357_AUD_HPL_GAIN_MASK;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ priv->hp_channel_number++;
+ if (priv->hp_channel_number > 1)
+ break;
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON2,
+ MT6357_AUD_HPL_GAIN_MASK |
+ MT6357_AUD_HPR_GAIN_MASK,
+ MT6357_DL_GAIN_N_40DB_REG);
+ /* Set HPP/N STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPROUT_STB_ENH_VAUDP15_MASK |
+ MT6357_HPLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_HPROUT_STB_ENH_VAUDP15_N470_P250 |
+ MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P250);
+ /* Enable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE);
+ /* Enable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE);
+ /* Enable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_CMFB_RST_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK,
+ MT6357_HP_CMFB_RST_NORMAL |
+ MT6357_HPL_AUX_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_ENABLE);
+ /* Enable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_BIAS_VAUDP15_MASK |
+ MT6357_AUD_HPL_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HPR_BIAS_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_BIAS_VAUDP15_ENABLE);
+ /* Enable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_MASK |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_ENABLE);
+ /* Short HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE);
+ /* Enable HP main CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE);
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPR_AUX_CMFB_LOOP_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK,
+ MT6357_HPR_AUX_CMFB_LOOP_DISABLE |
+ MT6357_HPL_AUX_CMFB_LOOP_DISABLE);
+ /* Enable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_PWRUP_VAUDP15_ENABLE);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+ usleep_range(1000, 1200);
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE);
+ /* apply volume setting */
+ hp_volume_ramp(priv, DL_GAIN_N_40DB, lgain,
+ DL_GAIN_N_40DB, rgain);
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE);
+ usleep_range(100, 120);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_channel_number--;
+ if (priv->hp_channel_number > 0)
+ break;
+ /* Short HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE);
+ /* Enable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE);
+ /* decrease HPL/R gain to normal gain step by step */
+ hp_volume_ramp(priv, lgain, DL_GAIN_N_40DB,
+ rgain, DL_GAIN_N_40DB);
+ /* Enable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE);
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_PWRUP_VAUDP15_DISABLE);
+ /* Enable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_CMFB_RST_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK,
+ MT6357_HP_CMFB_RST_RESET |
+ MT6357_HPL_AUX_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_ENABLE);
+ /* Disable HP main CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE);
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_MASK |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_DISABLE);
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_BIAS_VAUDP15_MASK |
+ MT6357_AUD_HPL_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HPR_BIAS_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_BIAS_VAUDP15_DISABLE);
+ /* Disable HP aux CMFB loop,
+ * Enable HP main CMFB for HP off state
+ */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_DISABLE |
+ MT6357_HPL_AUX_CMFB_LOOP_DISABLE);
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE);
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON2,
+ MT6357_AUD_HPL_GAIN_MASK |
+ MT6357_AUD_HPR_GAIN_MASK,
+ lgain << MT6357_AUD_HPL_GAIN_SFT |
+ rgain << MT6357_AUD_HPR_GAIN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int right_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACR_PWRUP_VA28_MASK |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACR_PWRUP_VA28_ENABLE |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+ /* Disable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACR_PWRUP_VA28_MASK |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACR_PWRUP_VA28_DISABLE |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int left_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACL_PWRUP_VA28_MASK |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACL_PWRUP_VA28_ENABLE |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+ /* Disable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACL_PWRUP_VA28_MASK |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACL_PWRUP_VA28_DISABLE |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Supply widgets subsequence */
+enum {
+ /* common */
+ SUPPLY_SEQ_CLK_BUF,
+ SUPPLY_SEQ_AUD_GLB,
+ SUPPLY_SEQ_CLKSQ,
+ SUPPLY_SEQ_VOW_AUD_LPW,
+ SUPPLY_SEQ_AUD_VOW,
+ SUPPLY_SEQ_VOW_CLK,
+ SUPPLY_SEQ_VOW_LDO,
+ SUPPLY_SEQ_TOP_CK,
+ SUPPLY_SEQ_TOP_CK_LAST,
+ SUPPLY_SEQ_AUD_TOP,
+ SUPPLY_SEQ_AUD_TOP_LAST,
+ SUPPLY_SEQ_AFE,
+ /* capture */
+ SUPPLY_SEQ_ADC_SUPPLY,
+};
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6357_dapm_widgets[] = {
+ /* Analog Clocks */
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6357_AUDDEC_ANA_CON11,
+ MT6357_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6357_AUDENC_ANA_CON6,
+ MT6357_CLKSQ_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUD_CK_PDN_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+
+ /* Digital Clocks */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_AFE_CTL_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_RESERVED_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_LPBK", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_LPBK_CTL_SFT, 1, NULL, 0),
+
+ /* General */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6357_AFE_UL_DL_CON0,
+ MT6357_AFE_ON_SFT, 0, NULL, 0),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "MT6357 Capture", 0,
+ SND_SOC_NOPM, 0, 0,
+ mt_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ADC Supply", SUPPLY_SEQ_ADC_SUPPLY,
+ SND_SOC_NOPM, 0, 0,
+ mt_adc_supply_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, adc_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_left_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_right_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("PGA L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX_E("Mic Type Mux", SND_SOC_NOPM, 0, 0,
+ &mic_type_mux_control,
+ mt_mic_type_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MICBIAS0", MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_PWD_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_PWD_SFT, 0, NULL, 0),
+
+ /* UL inputs */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("LPBK"),
+ SND_SOC_DAPM_INPUT("SGEN UL"),
+
+ /* Downlinks */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "MT6357 Playback", 0,
+ SND_SOC_NOPM, 0, 0,
+ mt_audio_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, &dac_mux_control),
+
+ SND_SOC_DAPM_DAC_E("DACR", NULL, SND_SOC_NOPM, 0, 0, right_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, SND_SOC_NOPM, 0, 0, left_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Analog Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL SRC", MT6357_AFE_DL_SRC2_CON0_L,
+ MT6357_DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("Line Out Source", SND_SOC_NOPM, 0, 0, &lo_mux_control,
+ lo_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Handset Source", SND_SOC_NOPM, 0, 0, &hs_mux_control,
+ hs_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Headphone Right Source", SND_SOC_NOPM, 0, 0, &hpr_mux_control,
+ hp_main_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Headphone Left Source", SND_SOC_NOPM, 0, 0, &hpl_mux_control,
+ hp_main_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ /* DL outputs */
+ SND_SOC_DAPM_OUTPUT("Headphones"),
+ SND_SOC_DAPM_OUTPUT("Hansdet"),
+ SND_SOC_DAPM_OUTPUT("Line out"),
+
+ /* Sine generator */
+ SND_SOC_DAPM_SUPPLY("SGEN UL Enable",
+ MT6357_AFE_TOP_CON0, MT6357_UL_SINE_ON_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN Enable",
+ MT6357_AFE_SGEN_CFG0,
+ MT6357_SGEN_DAC_EN_CTL_SFT, 0, mt_audio_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE",
+ MT6357_AFE_SGEN_CFG0,
+ MT6357_SGEN_MUTE_SW_CTL_SFT, 1, NULL, 0)
+};
+
+static const struct snd_soc_dapm_route mt6357_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "Mic Type Mux"},
+ {"AIF1TX", NULL, "CLK_BUF"},
+ {"AIF1TX", NULL, "AUDGLB"},
+ {"AIF1TX", NULL, "CLKSQ Audio"},
+ {"AIF1TX", NULL, "AUD_CK"},
+ {"AIF1TX", NULL, "AUDIF_CK"},
+
+ {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"AIF1TX", NULL, "AUDIO_TOP_I2S_DL"},
+ {"AIF1TX", NULL, "AFE_ON"},
+
+ {"Mic Type Mux", "ACC", "ADC"},
+ {"Mic Type Mux", "DCC", "ADC"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC"},
+ {"Mic Type Mux", "DMIC", "AIN0"},
+ {"Mic Type Mux", "DMIC", "AIN2"},
+ {"Mic Type Mux", "Loopback", "LPBK"},
+ {"Mic Type Mux", "Sine Generator", "SGEN UL"},
+
+ {"SGEN UL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+ {"SGEN UL", NULL, "SGEN UL Enable"},
+ {"SGEN UL", NULL, "SGEN MUTE"},
+ {"SGEN UL", NULL, "SGEN Enable"},
+
+ {"ADC", NULL, "PGA L Mux"},
+ {"ADC", NULL, "PGA R Mux"},
+ {"ADC", NULL, "ADC Supply"},
+
+ {"PGA L Mux", "AIN0", "AIN0"},
+ {"PGA L Mux", "AIN1", "AIN1"},
+ {"PGA L Mux", "AIN2", "AIN2"},
+
+ {"PGA R Mux", "AIN0", "AIN0"},
+ {"PGA R Mux", "AIN1", "AIN1"},
+ {"PGA R Mux", "AIN2", "AIN2"},
+
+ {"AIN0", NULL, "MICBIAS0"},
+ {"AIN1", NULL, "MICBIAS1"},
+ {"AIN2", NULL, "MICBIAS0"},
+ {"LPBK", NULL, "AUDIO_TOP_LPBK"},
+
+ /* Playback */
+ {"DAC Mux", "Normal Path", "AIF_RX"},
+ {"DAC Mux", "Sine Generator", "SGEN DL"},
+
+ {"AIF_RX", NULL, "DL SRC"},
+
+ {"SGEN DL", NULL, "DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN Enable"},
+ {"SGEN DL", NULL, "DL Digital Supply"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC Mux"},
+ {"DACR", NULL, "DAC Mux"},
+
+ {"DL Analog Supply", NULL, "CLK_BUF"},
+ {"DL Analog Supply", NULL, "AUDGLB"},
+ {"DL Analog Supply", NULL, "CLKSQ Audio"},
+ {"DL Analog Supply", NULL, "AUDNCP_CK"},
+ {"DL Analog Supply", NULL, "ZCD13M_CK"},
+ {"DL Analog Supply", NULL, "AUD_CK"},
+ {"DL Analog Supply", NULL, "AUDIF_CK"},
+
+ {"DL Digital Supply", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Supply", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Supply", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Supply", NULL, "AFE_ON"},
+
+ {"DACR", NULL, "DL Digital Supply"},
+ {"DACR", NULL, "DL Analog Supply"},
+ {"DACL", NULL, "DL Digital Supply"},
+ {"DACL", NULL, "DL Analog Supply"},
+
+ {"Line Out Source", "DACR", "DACR"},
+ {"Line Out Source", "Playback", "DACL"},
+ {"Line Out Source", "Test mode", "DACL"},
+
+ {"Handset Source", "DACR", "DACR"},
+ {"Handset Source", "Playback", "DACL"},
+ {"Handset Source", "Test mode", "DACL"},
+
+ {"Headphone Right Source", "DAC", "DACR"},
+ {"Headphone Right Source", "Line Out", "Line Out Source"},
+ {"Headphone Right Source", "Handset", "Handset Source"},
+
+ {"Headphone Left Source", "DAC", "DACL"},
+ {"Headphone Left Source", "Line Out", "Line Out Source"},
+ {"Headphone Left Source", "Handset", "Handset Source"},
+
+ {"Line out", NULL, "Line Out Source"},
+ {"Hansdet", NULL, "Handset Source"},
+
+ {"Headphones", NULL, "Headphone Right Source"},
+ {"Headphones", NULL, "Headphone Left Source"},
+};
+
+static struct snd_soc_dai_driver mtk_6357_dai_codecs[] = {
+ {
+ .name = "mt6357-snd-codec-aif1",
+ .playback = {
+ .stream_name = "MT6357 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = MT6357_SND_SOC_ADV_MT_FMTS,
+ },
+ .capture = {
+ .stream_name = "MT6357 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MT6357_SOC_HIGH_USE_RATE,
+ .formats = MT6357_SND_SOC_ADV_MT_FMTS,
+ },
+ },
+};
+
+static int mt6357_codec_probe(struct snd_soc_component *codec)
+{
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(codec);
+
+ snd_soc_component_init_regmap(codec, priv->regmap);
+
+ /* Enable audio part */
+ regmap_update_bits(priv->regmap, MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_MASK, MT6357_XO_AUDIO_EN_M_ENABLE);
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK |
+ MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_DISABLE);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_DISABLE);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_DISABLE);
+ /* set gpio */
+ set_playback_gpio(priv, false);
+ set_capture_gpio(priv, false);
+ /* Disable audio part */
+ regmap_update_bits(priv->regmap, MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_MASK,
+ MT6357_XO_AUDIO_EN_M_DISABLE);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6357_soc_component_driver = {
+ .probe = mt6357_codec_probe,
+ .read = snd_soc_component_read,
+ .write = snd_soc_component_write,
+ .controls = mt6357_controls,
+ .num_controls = ARRAY_SIZE(mt6357_controls),
+ .dapm_widgets = mt6357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6357_dapm_widgets),
+ .dapm_routes = mt6357_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6357_dapm_routes),
+};
+
+static const u32 micbias_values[] = {
+ 1700000, 1800000, 1900000, 2000000,
+ 2100000, 2500000, 2600000, 2700000
+};
+
+static u32 mt6357_get_micbias_idx(struct device_node *np, const char *micbias)
+{
+ int err;
+ u32 idx, val;
+
+ err = of_property_read_u32(np, micbias, &val);
+ if (err)
+ return 0;
+
+ for (idx = 0; idx < ARRAY_SIZE(micbias_values); idx++) {
+ if (val == micbias_values[idx])
+ return idx;
+ }
+ return 0;
+}
+
+static int mt6357_parse_dt(struct mt6357_priv *priv)
+{
+ u32 micbias_voltage_index = 0;
+ struct device_node *np = priv->dev->parent->of_node;
+
+ if (!np)
+ return -EINVAL;
+
+ priv->pull_down_needed = false;
+ if (of_property_read_bool(np, "mediatek,hp-pull-down"))
+ priv->pull_down_needed = true;
+
+ micbias_voltage_index = mt6357_get_micbias_idx(np, "mediatek,micbias0-microvolt");
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_VREF_MASK,
+ micbias_voltage_index << MT6357_AUD_MICBIAS0_VREF_SFT);
+
+ micbias_voltage_index = mt6357_get_micbias_idx(np, "mediatek,micbias1-microvolt");
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_VREF_MASK,
+ micbias_voltage_index << MT6357_AUD_MICBIAS1_VREF_SFT);
+
+ return 0;
+}
+
+static int mt6357_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ struct mt6357_priv *priv;
+ int ret;
+
+ ret = devm_regulator_get_enable(&pdev->dev, "vaud28");
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to enable vaud28 regulator\n");
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+ priv->dev = &pdev->dev;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ ret = mt6357_parse_dt(priv);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to parse dts\n");
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6357_soc_component_driver,
+ mtk_6357_dai_codecs,
+ ARRAY_SIZE(mtk_6357_dai_codecs));
+}
+
+static const struct platform_device_id mt6357_platform_ids[] = {
+ {"mt6357-sound", 0},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6357_platform_ids);
+
+static struct platform_driver mt6357_platform_driver = {
+ .driver = {
+ .name = "mt6357-sound",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = mt6357_platform_driver_probe,
+ .id_table = mt6357_platform_ids,
+};
+
+module_platform_driver(mt6357_platform_driver)
+
+MODULE_DESCRIPTION("MT6357 ALSA SoC codec driver");
+MODULE_AUTHOR("Nicolas Belin <nbelin@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/mt6357.h b/sound/soc/codecs/mt6357.h
new file mode 100644
index 000000000000..7f6fccada6a2
--- /dev/null
+++ b/sound/soc/codecs/mt6357.h
@@ -0,0 +1,660 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6357.h -- mt6357 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2024 Baylibre
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#ifndef __MT6357_H__
+#define __MT6357_H__
+
+#include <linux/types.h>
+
+/* Reg bit defines */
+/* MT6357_GPIO_DIR0 */
+#define MT6357_GPIO8_DIR_MASK BIT(8)
+#define MT6357_GPIO8_DIR_INPUT 0
+#define MT6357_GPIO8_DIR_OUTPUT BIT(8)
+#define MT6357_GPIO9_DIR_MASK BIT(9)
+#define MT6357_GPIO9_DIR_INPUT 0
+#define MT6357_GPIO9_DIR_OUTPUT BIT(9)
+#define MT6357_GPIO10_DIR_MASK BIT(10)
+#define MT6357_GPIO10_DIR_INPUT 0
+#define MT6357_GPIO10_DIR_OUTPUT BIT(10)
+#define MT6357_GPIO11_DIR_MASK BIT(11)
+#define MT6357_GPIO11_DIR_INPUT 0
+#define MT6357_GPIO11_DIR_OUTPUT BIT(11)
+#define MT6357_GPIO12_DIR_MASK BIT(12)
+#define MT6357_GPIO12_DIR_INPUT 0
+#define MT6357_GPIO12_DIR_OUTPUT BIT(12)
+#define MT6357_GPIO13_DIR_MASK BIT(13)
+#define MT6357_GPIO13_DIR_INPUT 0
+#define MT6357_GPIO13_DIR_OUTPUT BIT(13)
+#define MT6357_GPIO14_DIR_MASK BIT(14)
+#define MT6357_GPIO14_DIR_INPUT 0
+#define MT6357_GPIO14_DIR_OUTPUT BIT(14)
+#define MT6357_GPIO15_DIR_MASK BIT(15)
+#define MT6357_GPIO15_DIR_INPUT 0
+#define MT6357_GPIO15_DIR_OUTPUT BIT(15)
+
+/* MT6357_GPIO_MODE2 */
+#define MT6357_GPIO8_MODE_MASK GENMASK(2, 0)
+#define MT6357_GPIO8_MODE_AUD_CLK_MOSI BIT(0)
+#define MT6357_GPIO8_MODE_GPIO 0
+#define MT6357_GPIO9_MODE_MASK GENMASK(5, 3)
+#define MT6357_GPIO9_MODE_AUD_DAT_MOSI0 BIT(3)
+#define MT6357_GPIO9_MODE_GPIO 0
+#define MT6357_GPIO10_MODE_MASK GENMASK(8, 6)
+#define MT6357_GPIO10_MODE_AUD_DAT_MOSI1 BIT(6)
+#define MT6357_GPIO10_MODE_GPIO 0
+#define MT6357_GPIO11_MODE_MASK GENMASK(11, 9)
+#define MT6357_GPIO11_MODE_AUD_SYNC_MOSI BIT(9)
+#define MT6357_GPIO11_MODE_GPIO 0
+
+/* MT6357_GPIO_MODE2_SET */
+#define MT6357_GPIO8_MODE_SET_MASK GENMASK(2, 0)
+#define MT6357_GPIO8_MODE_SET_AUD_CLK_MOSI BIT(0)
+#define MT6357_GPIO9_MODE_SET_MASK GENMASK(5, 3)
+#define MT6357_GPIO9_MODE_SET_AUD_DAT_MOSI0 BIT(3)
+#define MT6357_GPIO10_MODE_SET_MASK GENMASK(8, 6)
+#define MT6357_GPIO10_MODE_SET_AUD_DAT_MOSI1 BIT(6)
+#define MT6357_GPIO11_MODE_SET_MASK GENMASK(11, 9)
+#define MT6357_GPIO11_MODE_SET_AUD_SYNC_MOSI BIT(9)
+
+/* MT6357_GPIO_MODE2_CLR */
+#define MT6357_GPIO_MODE2_CLEAR_ALL GENMASK(15, 0)
+
+/* MT6357_GPIO_MODE3 */
+#define MT6357_GPIO12_MODE_MASK GENMASK(2, 0)
+#define MT6357_GPIO12_MODE_AUD_CLK_MISO BIT(0)
+#define MT6357_GPIO12_MODE_GPIO 0
+#define MT6357_GPIO13_MODE_MASK GENMASK(5, 3)
+#define MT6357_GPIO13_MODE_AUD_DAT_MISO0 BIT(3)
+#define MT6357_GPIO13_MODE_GPIO 0
+#define MT6357_GPIO14_MODE_MASK GENMASK(8, 6)
+#define MT6357_GPIO14_MODE_AUD_DAT_MISO1 BIT(6)
+#define MT6357_GPIO14_MODE_GPIO 0
+#define MT6357_GPIO15_MODE_MASK GENMASK(11, 9)
+#define MT6357_GPIO15_MODE_AUD_SYNC_MISO BIT(9)
+#define MT6357_GPIO15_MODE_GPIO 0
+
+/* MT6357_GPIO_MODE3_SET */
+#define MT6357_GPIO12_MODE_SET_MASK GENMASK(2, 0)
+#define MT6357_GPIO12_MODE_SET_AUD_CLK_MISO BIT(0)
+#define MT6357_GPIO13_MODE_SET_MASK GENMASK(5, 3)
+#define MT6357_GPIO13_MODE_SET_AUD_DAT_MISO0 BIT(3)
+#define MT6357_GPIO14_MODE_SET_MASK GENMASK(8, 6)
+#define MT6357_GPIO14_MODE_SET_AUD_DAT_MISO1 BIT(6)
+#define MT6357_GPIO15_MODE_SET_MASK GENMASK(11, 9)
+#define MT6357_GPIO15_MODE_SET_AUD_SYNC_MISO BIT(9)
+
+/* MT6357_GPIO_MODE3_CLR */
+#define MT6357_GPIO_MODE3_CLEAR_ALL GENMASK(15, 0)
+
+/* MT6357_DCXO_CW14 */
+#define MT6357_XO_AUDIO_EN_M_SFT 13
+#define MT6357_XO_AUDIO_EN_M_MASK BIT(13)
+#define MT6357_XO_AUDIO_EN_M_ENABLE BIT(13)
+#define MT6357_XO_AUDIO_EN_M_DISABLE 0
+
+/* MT6357_AUD_TOP_CKPDN_CON0 */
+#define MT6357_AUDNCP_CK_PDN_SFT 6
+#define MT6357_ZCD13M_CK_PDN_SFT 5
+#define MT6357_AUDIF_CK_PDN_SFT 2
+#define MT6357_AUD_CK_PDN_SFT 1
+
+/* MT6357_AUDNCP_CLKDIV_CON0 */
+#define MT6357_DIVCKS_CHG BIT(0)
+
+/* MT6357_AUDNCP_CLKDIV_CON1 */
+#define MT6357_DIVCKS_ON BIT(0)
+
+/* MT6357_AUDNCP_CLKDIV_CON3 */
+#define MT6357_DIVCKS_PWD_NCP_MASK BIT(0)
+#define MT6357_DIVCKS_PWD_NCP_DISABLE BIT(0)
+#define MT6357_DIVCKS_PWD_NCP_ENABLE 0
+
+/* MT6357_AUDNCP_CLKDIV_CON4 */
+#define MT6357_DIVCKS_PWD_NCP_ST_SEL_MASK GENMASK(1, 0)
+#define MT6357_DIVCKS_PWD_NCP_ST_50US 0
+#define MT6357_DIVCKS_PWD_NCP_ST_100US 1
+#define MT6357_DIVCKS_PWD_NCP_ST_150US 2
+#define MT6357_DIVCKS_PWD_NCP_ST_200US 3
+
+/* MT6357_AFE_UL_DL_CON0 */
+#define MT6357_AFE_UL_LR_SWAP_SFT 15
+#define MT6357_AFE_ON_SFT 0
+
+/* MT6357_AFE_DL_SRC2_CON0_L */
+#define MT6357_DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+
+/* MT6357_AFE_UL_SRC_CON0_H */
+#define MT6357_C_TWO_DIGITAL_MIC_CTL_MASK BIT(7)
+#define MT6357_C_TWO_DIGITAL_MIC_ENABLE BIT(7)
+#define MT6357_C_TWO_DIGITAL_MIC_DISABLE 0
+
+/* MT6357_AFE_UL_SRC_CON0_L */
+#define MT6357_UL_SDM_3_LEVEL_CTL_MASK BIT(1)
+#define MT6357_UL_SDM_3_LEVEL_SELECT BIT(1)
+#define MT6357_UL_SDM_3_LEVEL_DESELECT 0
+#define MT6357_UL_SRC_ON_TMP_CTL_MASK BIT(0)
+#define MT6357_UL_SRC_ENABLE BIT(0)
+#define MT6357_UL_SRC_DISABLE 0
+
+/* MT6357_AFE_TOP_CON0 */
+#define MT6357_UL_SINE_ON_SFT 1
+#define MT6357_UL_SINE_ON_MASK BIT(1)
+#define MT6357_DL_SINE_ON_SFT 0
+#define MT6357_DL_SINE_ON_MASK BIT(0)
+
+/* MT6357_AUDIO_TOP_CON0 */
+#define MT6357_PDN_LPBK_CTL_SFT 15
+#define MT6357_PDN_AFE_CTL_SFT 7
+#define MT6357_PDN_DAC_CTL_SFT 6
+#define MT6357_PDN_ADC_CTL_SFT 5
+#define MT6357_PDN_I2S_DL_CTL_SFT 3
+#define MT6357_PWR_CLK_DIS_CTL_SFT 2
+#define MT6357_PDN_AFE_TESTMODEL_CTL_SFT 1
+#define MT6357_PDN_RESERVED_SFT 0
+
+/* MT6357_AFUNC_AUD_CON0 */
+#define MT6357_CCI_AUD_ANACK_INVERT BIT(15)
+#define MT6357_CCI_AUD_ANACK_NORMAL 0
+#define MT6357_CCI_AUDIO_FIFO_WPTR_SFT 12
+#define MT6357_CCI_SCRAMBLER_CG_ENABLE BIT(11)
+#define MT6357_CCI_SCRAMBLER_CG_DISABLE 0
+#define MT6357_CCI_LCK_INV_OUT_OF_PHASE BIT(10)
+#define MT6357_CCI_LCK_INV_IN_PHASE 0
+#define MT6357_CCI_RAND_ENABLE BIT(9)
+#define MT6357_CCI_RAND_DISABLE 0
+#define MT6357_CCI_SPLT_SCRMB_CLK_ON BIT(8)
+#define MT6357_CCI_SPLT_SCRMB_CLK_OFF 0
+#define MT6357_CCI_SPLT_SCRMB_ON BIT(7)
+#define MT6357_CCI_SPLT_SCRMB_OFF 0
+#define MT6357_CCI_AUD_IDAC_TEST_EN_FROM_TEST_IN BIT(6)
+#define MT6357_CCI_AUD_IDAC_TEST_EN_NORMAL_PATH 0
+#define MT6357_CCI_ZERO_PADDING_DISABLE BIT(5)
+#define MT6357_CCI_ZERO_PADDING_ENABLE 0
+#define MT6357_CCI_AUD_SPLIT_TEST_EN_FROM_TEST_IN BIT(4)
+#define MT6357_CCI_AUD_SPLIT_TEST_EN_NORMAL_PATH 0
+#define MT6357_CCI_AUD_SDM_MUTE_L_REG_CTL BIT(3)
+#define MT6357_CCI_AUD_SDM_MUTE_L_NO_CTL 0
+#define MT6357_CCI_AUD_SDM_MUTE_R_REG_CTL BIT(2)
+#define MT6357_CCI_AUD_SDM_MUTE_R_NO_CTL 0
+#define MT6357_CCI_AUD_SDM_7BIT_FROM_SPLITTER3 BIT(1)
+#define MT6357_CCI_AUD_SDM_7BIT_FROM_SPLITTER1 0
+#define MT6357_CCI_SCRAMBLER_ENABLE BIT(0)
+#define MT6357_CCI_SCRAMBLER_DISABLE 0
+
+/* MT6357_AFUNC_AUD_CON2 */
+#define MT6357_CCI_AUDIO_FIFO_ENABLE BIT(3)
+#define MT6357_CCI_AUDIO_FIFO_DISABLE 0
+#define MT6357_CCI_ACD_MODE_NORMAL_PATH BIT(2)
+#define MT6357_CCI_ACD_MODE_TEST_PATH 0
+#define MT6357_CCI_AFIFO_CLK_PWDB_ON BIT(1)
+#define MT6357_CCI_AFIFO_CLK_PWDB_DOWN 0
+#define MT6357_CCI_ACD_FUNC_RSTB_RELEASE BIT(0)
+#define MT6357_CCI_ACD_FUNC_RSTB_RESET 0
+
+/* MT6357_AFE_ADDA_MTKAIF_CFG0 */
+#define MT6357_ADDA_MTKAIF_LPBK_CTL_MASK BIT(1)
+#define MT6357_ADDA_MTKAIF_LPBK_ENABLE BIT(1)
+#define MT6357_ADDA_MTKAIF_LPBK_DISABLE 0
+
+/* MT6357_AFE_SGEN_CFG0 */
+#define MT6357_SGEN_DAC_EN_CTL_SFT 7
+#define MT6357_SGEN_DAC_ENABLE BIT(7)
+#define MT6357_SGEN_MUTE_SW_CTL_SFT 6
+#define MT6357_SGEN_MUTE_SW_DISABLE 0
+
+/* MT6357_AFE_DCCLK_CFG0 */
+#define MT6357_DCCLK_DIV_MASK GENMASK(15, 5)
+#define MT6357_DCCLK_DIV_SFT 5
+#define MT6357_DCCLK_DIV_RUN_VALUE (32 << MT6357_DCCLK_DIV_SFT)
+#define MT6357_DCCLK_DIV_STOP_VALUE (259 << MT6357_DCCLK_DIV_SFT)
+#define MT6357_DCCLK_PDN_MASK BIT(1)
+#define MT6357_DCCLK_PDN BIT(1)
+#define MT6357_DCCLK_OUTPUT 0
+#define MT6357_DCCLK_GEN_ON_MASK BIT(0)
+#define MT6357_DCCLK_GEN_ON BIT(0)
+#define MT6357_DCCLK_GEN_OFF 0
+
+/* MT6357_AFE_DCCLK_CFG1 */
+#define MT6357_DCCLK_RESYNC_BYPASS_MASK BIT(8)
+#define MT6357_DCCLK_RESYNC_BYPASS BIT(8)
+
+/* MT6357_AFE_AUD_PAD_TOP */
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK GENMASK(15, 8)
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE (BIT(13) | BIT(12) | BIT(8))
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE (BIT(13) | BIT(12))
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_MASK GENMASK(7, 0)
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_ENABLE (BIT(5) | BIT(4) | BIT(0))
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON0 */
+#define MT6357_AUDADCLINPUTSEL_MASK GENMASK(14, 13)
+#define MT6357_AUDADCLINPUTSEL_PREAMPLIFIER BIT(14)
+#define MT6357_AUDADCLINPUTSEL_IDLE 0
+#define MT6357_AUDADCLPWRUP_SFT 12
+#define MT6357_AUDADCLPWRUP_MASK BIT(12)
+#define MT6357_AUDADCLPWRUP BIT(12)
+#define MT6357_AUDADCLPWRDOWN 0
+#define MT6357_AUDPREAMPLGAIN_SFT 8
+#define MT6357_AUDPREAMPLGAIN_MASK GENMASK(10, 8)
+#define MT6357_AUDPREAMPLGAIN_MAX 4
+#define MT6357_AUDPREAMPLINPUTSEL_SFT 6
+#define MT6357_AUDPREAMPLINPUTSEL_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUDPREAMPLDCPRECHARGE_MASK BIT(2)
+#define MT6357_AUDPREAMPLDCPRECHARGE_ENABLE BIT(2)
+#define MT6357_AUDPREAMPLDCPRECHARGE_DISABLE 0
+#define MT6357_AUDPREAMPLDCCEN_MASK BIT(1)
+#define MT6357_AUDPREAMPLDCCEN_DC BIT(1)
+#define MT6357_AUDPREAMPLDCCEN_AC 0
+#define MT6357_AUDPREAMPLON_MASK BIT(0)
+#define MT6357_AUDPREAMPLON_ENABLE BIT(0)
+#define MT6357_AUDPREAMPLON_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON1 */
+#define MT6357_AUDADCRINPUTSEL_MASK GENMASK(14, 13)
+#define MT6357_AUDADCRINPUTSEL_PREAMPLIFIER BIT(14)
+#define MT6357_AUDADCRINPUTSEL_IDLE 0
+#define MT6357_AUDADCRPWRUP_SFT 12
+#define MT6357_AUDADCRPWRUP_MASK BIT(12)
+#define MT6357_AUDADCRPWRUP BIT(12)
+#define MT6357_AUDADCRPWRDOWN 0
+#define MT6357_AUDPREAMPRGAIN_SFT 8
+#define MT6357_AUDPREAMPRGAIN_MASK GENMASK(10, 8)
+#define MT6357_AUDPREAMPRGAIN_MAX 4
+#define MT6357_AUDPREAMPRINPUTSEL_SFT 6
+#define MT6357_AUDPREAMPRINPUTSEL_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUDPREAMPRDCPRECHARGE_MASK BIT(2)
+#define MT6357_AUDPREAMPRDCPRECHARGE_ENABLE BIT(2)
+#define MT6357_AUDPREAMPRDCPRECHARGE_DISABLE 0
+#define MT6357_AUDPREAMPRDCCEN_MASK BIT(1)
+#define MT6357_AUDPREAMPRDCCEN_DC BIT(1)
+#define MT6357_AUDPREAMPRDCCEN_AC 0
+#define MT6357_AUDPREAMPRON_MASK BIT(0)
+#define MT6357_AUDPREAMPRON_ENABLE BIT(0)
+#define MT6357_AUDPREAMPRON_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON6 */
+#define MT6357_CLKSQ_EN_SFT 0
+
+/* MT6357_AUDENC_ANA_CON7 */
+#define MT6357_AUDDIGMICBIAS_MASK GENMASK(2, 1)
+#define MT6357_AUDDIGMICBIAS_DEFAULT_VALUE BIT(2)
+#define MT6357_AUDDIGMICBIAS_OFF 0
+#define MT6357_AUDDIGMICEN_MASK BIT(0)
+#define MT6357_AUDDIGMICEN_ENABLE BIT(0)
+#define MT6357_AUDDIGMICEN_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON8 */
+#define MT6357_AUD_MICBIAS0_DCSW2N_EN_MASK BIT(14)
+#define MT6357_AUD_MICBIAS0_DCSW2N_ENABLE BIT(14)
+#define MT6357_AUD_MICBIAS0_DCSW2N_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW2P2_EN_MASK BIT(13)
+#define MT6357_AUD_MICBIAS0_DCSW2P2_ENABLE BIT(13)
+#define MT6357_AUD_MICBIAS0_DCSW2P2_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW2P1_EN_MASK BIT(12)
+#define MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE BIT(12)
+#define MT6357_AUD_MICBIAS0_DCSW2P1_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0N_EN_MASK BIT(10)
+#define MT6357_AUD_MICBIAS0_DCSW0N_ENABLE BIT(10)
+#define MT6357_AUD_MICBIAS0_DCSWN_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0P2_EN_MASK BIT(9)
+#define MT6357_AUD_MICBIAS0_DCSW0P2_ENABLE BIT(9)
+#define MT6357_AUD_MICBIAS0_DCSW0P2_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0P1_EN_MASK BIT(8)
+#define MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE BIT(8)
+#define MT6357_AUD_MICBIAS0_DCSW0P1_DISABLE 0
+#define MT6357_AUD_MICBIAS0_VREF_MASK GENMASK(6, 4)
+#define MT6357_AUD_MICBIAS0_VREF_SFT 4
+#define MT6357_AUD_MICBIAS0_PWD_SFT 0
+
+#define MT6357_AUD_MICBIAS0_DC_MASK (MT6357_AUD_MICBIAS0_DCSW2N_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW2P2_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW2P1_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0N_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0P2_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_EN_MASK)
+
+#define MT6357_AUD_MICBIAS0_DC_ENABLE_ALL (MT6357_AUD_MICBIAS0_DCSW2N_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW2P2_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0N_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P2_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE)
+
+#define MT6357_AUD_MICBIAS0_DC_ENABLE_P1 (MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE)
+
+#define MT6357_AUD_MICBIAS0_DC_DISABLE_ALL 0
+
+/* MT6357_AUDENC_ANA_CON9 */
+#define MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK BIT(8)
+#define MT6357_AUD_MICBIAS1_DCSW1P_ENABLE BIT(8)
+#define MT6357_AUD_MICBIAS1_DCSW1P_DISABLE 0
+#define MT6357_AUD_MICBIAS1_VREF_MASK GENMASK(6, 4)
+#define MT6357_AUD_MICBIAS1_VREF_SFT 4
+#define MT6357_AUD_MICBIAS1_PWD_SFT 0
+
+/* MT6357_AUDDEC_ANA_CON0 */
+#define MT6357_AUD_HPR_SC_VAUDP15_MASK BIT(13)
+#define MT6357_AUD_HPR_SC_VAUDP15_DISABLE BIT(13)
+#define MT6357_AUD_HPR_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HPL_SC_VAUDP15_MASK BIT(12)
+#define MT6357_AUD_HPL_SC_VAUDP15_DISABLE BIT(12)
+#define MT6357_AUD_HPL_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HPR_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HPR_MUX_INPUT_VAUDP15_SFT 10
+#define MT6357_AUD_HPL_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HPL_MUX_INPUT_VAUDP15_SFT 8
+#define MT6357_AUD_HPR_BIAS_VAUDP15_MASK BIT(7)
+#define MT6357_AUD_HPR_BIAS_VAUDP15_ENABLE BIT(7)
+#define MT6357_AUD_HPR_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPL_BIAS_VAUDP15_MASK BIT(6)
+#define MT6357_AUD_HPL_BIAS_VAUDP15_ENABLE BIT(6)
+#define MT6357_AUD_HPL_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_MASK BIT(5)
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_ENABLE BIT(5)
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_ENABLE BIT(4)
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_DACL_PWRUP_VA28_MASK BIT(3)
+#define MT6357_AUD_DACL_PWRUP_VA28_ENABLE BIT(3)
+#define MT6357_AUD_DACL_PWRUP_VA28_DISABLE 0
+#define MT6357_AUD_DACR_PWRUP_VA28_MASK BIT(2)
+#define MT6357_AUD_DACR_PWRUP_VA28_ENABLE BIT(2)
+#define MT6357_AUD_DACR_PWRUP_VA28_DISABLE 0
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON1 */
+#define MT6357_HPROUT_STG_CTRL_VAUDP15_MASK GENMASK(14, 12)
+#define MT6357_HPROUT_STG_CTRL_VAUDP15_SFT 12
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_MASK GENMASK(10, 8)
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_SFT 8
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX 7
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK BIT(7)
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE BIT(7)
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE 0
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK BIT(6)
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE BIT(6)
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE 0
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_MASK BIT(5)
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE BIT(5)
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE 0
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_MASK BIT(4)
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE BIT(4)
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK BIT(3)
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE BIT(3)
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK BIT(2)
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE BIT(2)
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_PWRUP_VAUDP15_MASK BIT(1)
+#define MT6357_HPROUT_PWRUP_VAUDP15_ENABLE BIT(1)
+#define MT6357_HPROUT_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPLOUT_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_HPLOUT_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_HPLOUT_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON2 */
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_MASK BIT(10)
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_ENABLE BIT(10)
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_DISABLE 0
+#define MT6357_AUD_REFN_DERES_VAUDP15_MASK BIT(9)
+#define MT6357_AUD_REFN_DERES_VAUDP15_ENABLE BIT(9)
+#define MT6357_AUD_REFN_DERES_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_STB_ENH_VAUDP15_MASK GENMASK(6, 4)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_OPEN 0
+#define MT6357_HPROUT_STB_ENH_VAUDP15_NOPEN_P250 BIT(4)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_POPEN BIT(5)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_P250 (BIT(4) | BIT(5))
+#define MT6357_HPROUT_STB_ENH_VAUDP15_NOPEN_P470 (BIT(4) | BIT(6))
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_P470 (BIT(4) | BIT(5) | BIT(6))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_MASK GENMASK(2, 0)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_OPEN 0
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_NOPEN_P250 BIT(0)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_POPEN BIT(1)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P250 (BIT(0) | BIT(1))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_NOPEN_P470 (BIT(0) | BIT(2))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P470 (BIT(0) | BIT(1) | BIT(2))
+
+/* MT6357_AUDDEC_ANA_CON3 */
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK BIT(7)
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_ENABLE BIT(7)
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_DISABLE 0
+#define MT6357_AUD_HS_SC_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_HS_SC_VAUDP15_DISABLE BIT(4)
+#define MT6357_AUD_HS_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HS_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HS_MUX_INPUT_VAUDP15_SFT 2
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HS_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_HS_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_HS_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON4 */
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK BIT(8)
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_ENABLE BIT(8)
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_DISABLE 0
+#define MT6357_AUD_LOL_SC_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_LOL_SC_VAUDP15_DISABLE BIT(4)
+#define MT6357_AUD_LOL_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_LOL_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_LOL_MUX_INPUT_VAUDP15_SFT 2
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON6 */
+#define MT6357_HP_AUX_LOOP_GAIN_MASK GENMASK(15, 12)
+#define MT6357_HP_AUX_LOOP_GAIN_SFT 12
+#define MT6357_HP_AUX_LOOP_GAIN_MAX 0x0f
+#define MT6357_HPR_AUX_CMFB_LOOP_MASK BIT(11)
+#define MT6357_HPR_AUX_CMFB_LOOP_ENABLE BIT(11)
+#define MT6357_HPR_AUX_CMFB_LOOP_DISABLE 0
+#define MT6357_HPL_AUX_CMFB_LOOP_MASK BIT(10)
+#define MT6357_HPL_AUX_CMFB_LOOP_ENABLE BIT(10)
+#define MT6357_HPL_AUX_CMFB_LOOP_DISABLE 0
+#define MT6357_HPRL_MAIN_CMFB_LOOP_MASK BIT(9)
+#define MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE BIT(9)
+#define MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE 0
+#define MT6357_HP_CMFB_RST_MASK BIT(7)
+#define MT6357_HP_CMFB_RST_NORMAL BIT(7)
+#define MT6357_HP_CMFB_RST_RESET 0
+#define MT6357_DAC_LOW_NOISE_MODE_MASK BIT(0)
+#define MT6357_DAC_LOW_NOISE_MODE_ENABLE BIT(0)
+#define MT6357_DAC_LOW_NOISE_MODE_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON7 */
+#define MT6357_HP_IVBUF_DEGAIN_SFT 2
+#define MT6357_HP_IVBUF_DEGAIN_MAX 1
+
+/* MT6357_AUDDEC_ANA_CON10 */
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK BIT(8)
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_DISABLE BIT(8)
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_ENABLE 0
+
+/* MT6357_AUDDEC_ANA_CON11 */
+#define MT6357_RSTB_ENCODER_VA28_MASK BIT(5)
+#define MT6357_RSTB_ENCODER_VA28_ENABLE BIT(5)
+#define MT6357_RSTB_ENCODER_VA28_DISABLE 0
+#define MT6357_AUDGLB_PWRDN_VA28_SFT 4
+#define MT6357_RSTB_DECODER_VA28_MASK BIT(0)
+#define MT6357_RSTB_DECODER_VA28_ENABLE BIT(0)
+#define MT6357_RSTB_DECODER_VA28_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON12 */
+#define MT6357_VA28REFGEN_EN_VA28_MASK BIT(13)
+#define MT6357_VA28REFGEN_EN_VA28_ENABLE BIT(13)
+#define MT6357_VA28REFGEN_EN_VA28_DISABLE 0
+#define MT6357_VA33REFGEN_EN_VA18_MASK BIT(12)
+#define MT6357_VA33REFGEN_EN_VA18_ENABLE BIT(12)
+#define MT6357_VA33REFGEN_EN_VA18_DISABLE 0
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK BIT(10)
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_ENABLE BIT(10)
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_DISABLE 0
+#define MT6357_LCLDO_ENC_EN_VA28_MASK BIT(8)
+#define MT6357_LCLDO_ENC_EN_VA28_ENABLE BIT(8)
+#define MT6357_LCLDO_ENC_EN_VA28_DISABLE 0
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_MASK BIT(6)
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_ENABLE BIT(6)
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_DISABLE 0
+#define MT6357_LCLDO_EN_VA18_MASK BIT(4)
+#define MT6357_LCLDO_EN_VA18_ENABLE BIT(4)
+#define MT6357_LCLDO_EN_VA18_DISABLE 0
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_MASK BIT(2)
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_ENABLE BIT(2)
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_DISABLE 0
+#define MT6357_HCLDO_EN_VA18_MASK BIT(0)
+#define MT6357_HCLDO_EN_VA18_ENABLE BIT(0)
+#define MT6357_HCLDO_EN_VA18_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON13 */
+#define MT6357_NVREG_EN_VAUDP15_MASK BIT(0)
+#define MT6357_NVREG_EN_VAUDP15_ENABLE BIT(0)
+#define MT6357_NVREG_EN_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ELR_0 */
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_MASK BIT(12)
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_ENABLE BIT(12)
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_DISABLE 0
+
+/* MT6357_ZCD_CON1 */
+#define MT6357_AUD_LOL_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_LOL_GAIN_SFT 0
+#define MT6357_AUD_LOR_GAIN_MASK GENMASK(11, 7)
+#define MT6357_AUD_LOR_GAIN_SFT 7
+#define MT6357_AUD_LO_GAIN_MAX 0x12
+
+/* MT6357_ZCD_CON2 */
+#define MT6357_AUD_HPL_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_HPL_GAIN_SFT 0
+#define MT6357_AUD_HPR_GAIN_MASK GENMASK(11, 7)
+#define MT6357_AUD_HPR_GAIN_SFT 7
+#define MT6357_AUD_HP_GAIN_MAX 0x12
+
+/* MT6357_ZCD_CON3 */
+#define MT6357_AUD_HS_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_HS_GAIN_SFT 0
+#define MT6357_AUD_HS_GAIN_MAX 0x12
+
+/* Registers list */
+/* gpio direction */
+#define MT6357_GPIO_DIR0 0x0088
+/* mosi */
+#define MT6357_GPIO_MODE2 0x00B6
+#define MT6357_GPIO_MODE2_SET 0x00B8
+#define MT6357_GPIO_MODE2_CLR 0x00BA
+/* miso */
+#define MT6357_GPIO_MODE3 0x00BC
+#define MT6357_GPIO_MODE3_SET 0x00BE
+#define MT6357_GPIO_MODE3_CLR 0x00C0
+
+#define MT6357_DCXO_CW14 0x07AC
+
+#define MT6357_AUD_TOP_CKPDN_CON0 0x208C
+#define MT6357_AUDNCP_CLKDIV_CON0 0x20B4
+#define MT6357_AUDNCP_CLKDIV_CON1 0x20B6
+#define MT6357_AUDNCP_CLKDIV_CON2 0x20B8
+#define MT6357_AUDNCP_CLKDIV_CON3 0x20BA
+#define MT6357_AUDNCP_CLKDIV_CON4 0x20BC
+#define MT6357_AFE_UL_DL_CON0 0x2108
+#define MT6357_AFE_DL_SRC2_CON0_L 0x210A
+#define MT6357_AFE_UL_SRC_CON0_H 0x210C
+#define MT6357_AFE_UL_SRC_CON0_L 0x210E
+#define MT6357_AFE_TOP_CON0 0x2110
+#define MT6357_AUDIO_TOP_CON0 0x2112
+#define MT6357_AFUNC_AUD_CON0 0x2116
+#define MT6357_AFUNC_AUD_CON2 0x211A
+#define MT6357_AFE_ADDA_MTKAIF_CFG0 0x2134
+#define MT6357_AFE_SGEN_CFG0 0x2140
+#define MT6357_AFE_DCCLK_CFG0 0x2146
+#define MT6357_AFE_DCCLK_CFG1 0x2148
+#define MT6357_AFE_AUD_PAD_TOP 0x214C
+#define MT6357_AUDENC_ANA_CON0 0x2188
+#define MT6357_AUDENC_ANA_CON1 0x218A
+#define MT6357_AUDENC_ANA_CON6 0x2194
+#define MT6357_AUDENC_ANA_CON7 0x2196
+#define MT6357_AUDENC_ANA_CON8 0x2198
+#define MT6357_AUDENC_ANA_CON9 0x219A
+#define MT6357_AUDDEC_ANA_CON0 0x2208
+#define MT6357_AUDDEC_ANA_CON1 0x220A
+#define MT6357_AUDDEC_ANA_CON2 0x220C
+#define MT6357_AUDDEC_ANA_CON3 0x220E
+#define MT6357_AUDDEC_ANA_CON4 0x2210
+#define MT6357_AUDDEC_ANA_CON6 0x2214
+#define MT6357_AUDDEC_ANA_CON7 0x2216
+#define MT6357_AUDDEC_ANA_CON10 0x221C
+#define MT6357_AUDDEC_ANA_CON11 0x221E
+#define MT6357_AUDDEC_ANA_CON12 0x2220
+#define MT6357_AUDDEC_ANA_CON13 0x2222
+#define MT6357_AUDDEC_ELR_0 0x2226
+#define MT6357_ZCD_CON1 0x228A
+#define MT6357_ZCD_CON2 0x228C
+#define MT6357_ZCD_CON3 0x228E
+
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_12DB = 20,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+enum {
+ UL_GAIN_0DB = 0,
+ UL_GAIN_6DB,
+ UL_GAIN_12DB,
+ UL_GAIN_18DB,
+ UL_GAIN_24DB,
+};
+
+#define MT6357_DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define MT6357_DL_GAIN_REG_LEFT_MASK 0x001f
+#define MT6357_DL_GAIN_REG_LEFT_SHIFT 0
+#define MT6357_DL_GAIN_REG_RIGHT_MASK 0x0f80
+#define MT6357_DL_GAIN_REG_RIGHT_SHIFT 7
+#define MT6357_DL_GAIN_REG_MASK 0x0f9f
+
+#define MT6357_SND_SOC_ADV_MT_FMTS (\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_U16_BE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_BE |\
+ SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_U24_BE |\
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_S32_BE |\
+ SNDRV_PCM_FMTBIT_U32_LE |\
+ SNDRV_PCM_FMTBIT_U32_BE)
+
+#define MT6357_SOC_HIGH_USE_RATE (\
+ SNDRV_PCM_RATE_CONTINUOUS |\
+ SNDRV_PCM_RATE_8000_192000)
+
+/* codec private structure */
+struct mt6357_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ bool pull_down_needed;
+ int hp_channel_number;
+};
+#endif
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
new file mode 100644
index 000000000000..a787accb88e8
--- /dev/null
+++ b/sound/soc/codecs/mt6358.c
@@ -0,0 +1,2430 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6358.c -- mt6358 ALSA SoC audio codec driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "mt6358.h"
+
+enum {
+ AUDIO_ANALOG_VOLUME_HSOUTL,
+ AUDIO_ANALOG_VOLUME_HSOUTR,
+ AUDIO_ANALOG_VOLUME_HPOUTL,
+ AUDIO_ANALOG_VOLUME_HPOUTR,
+ AUDIO_ANALOG_VOLUME_LINEOUTL,
+ AUDIO_ANALOG_VOLUME_LINEOUTR,
+ AUDIO_ANALOG_VOLUME_MICAMP1,
+ AUDIO_ANALOG_VOLUME_MICAMP2,
+ AUDIO_ANALOG_VOLUME_TYPE_MAX
+};
+
+enum {
+ MUX_ADC_L,
+ MUX_ADC_R,
+ MUX_PGA_L,
+ MUX_PGA_R,
+ MUX_MIC_TYPE,
+ MUX_HP_L,
+ MUX_HP_R,
+ MUX_NUM,
+};
+
+enum {
+ DEVICE_HP,
+ DEVICE_LO,
+ DEVICE_RCV,
+ DEVICE_MIC1,
+ DEVICE_MIC2,
+ DEVICE_NUM
+};
+
+/* Supply widget subseq */
+enum {
+ /* common */
+ SUPPLY_SEQ_CLK_BUF,
+ SUPPLY_SEQ_AUD_GLB,
+ SUPPLY_SEQ_CLKSQ,
+ SUPPLY_SEQ_VOW_AUD_LPW,
+ SUPPLY_SEQ_AUD_VOW,
+ SUPPLY_SEQ_VOW_CLK,
+ SUPPLY_SEQ_VOW_LDO,
+ SUPPLY_SEQ_TOP_CK,
+ SUPPLY_SEQ_TOP_CK_LAST,
+ SUPPLY_SEQ_AUD_TOP,
+ SUPPLY_SEQ_AUD_TOP_LAST,
+ SUPPLY_SEQ_AFE,
+ /* capture */
+ SUPPLY_SEQ_ADC_SUPPLY,
+};
+
+enum {
+ CH_L = 0,
+ CH_R,
+ NUM_CH,
+};
+
+#define REG_STRIDE 2
+
+struct mt6358_priv {
+ struct device *dev;
+ struct regmap *regmap;
+
+ unsigned int dl_rate;
+ unsigned int ul_rate;
+
+ int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX];
+ unsigned int mux_select[MUX_NUM];
+
+ int dev_counter[DEVICE_NUM];
+
+ int mtkaif_protocol;
+
+ struct regulator *avdd_reg;
+
+ int wov_enabled;
+
+ int dmic_one_wire_mode;
+};
+
+int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ priv->mtkaif_protocol = mtkaif_protocol;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_protocol);
+
+static void playback_gpio_set(struct mt6358_priv *priv)
+{
+ /* set gpio mosi mode */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR,
+ 0x01f8, 0x01f8);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_SET,
+ 0xffff, 0x0249);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2,
+ 0xffff, 0x0249);
+}
+
+static void playback_gpio_reset(struct mt6358_priv *priv)
+{
+ /* set pad_aud_*_mosi to GPIO mode and dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ * don't clean clk/sync, for mtkaif protocol 2
+ */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR,
+ 0x01f8, 0x01f8);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2,
+ 0x01f8, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0,
+ 0xf << 8, 0x0);
+}
+
+static void capture_gpio_set(struct mt6358_priv *priv)
+{
+ /* set gpio miso mode */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR,
+ 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_SET,
+ 0xffff, 0x0249);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3,
+ 0xffff, 0x0249);
+}
+
+static void capture_gpio_reset(struct mt6358_priv *priv)
+{
+ /* set pad_aud_*_miso to GPIO mode and dir input
+ * reason:
+ * pad_aud_clk_miso, because when playback only the miso_clk
+ * will also have 26m, so will have power leak
+ * pad_aud_dat_miso*, because the pin is used as boot strap
+ */
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR,
+ 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0,
+ 0xf << 12, 0x0);
+}
+
+static int mt6358_mtkaif_tx_enable(struct mt6358_priv *priv)
+{
+ switch (priv->mtkaif_protocol) {
+ case MT6358_MTKAIF_PROTOCOL_2_CLK_P2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0010);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3800);
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3900);
+ break;
+ case MT6358_MTKAIF_PROTOCOL_2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0010);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ case MT6358_MTKAIF_PROTOCOL_1:
+ default:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0000);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ }
+ return 0;
+}
+
+static int mt6358_mtkaif_tx_disable(struct mt6358_priv *priv)
+{
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6358_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3000);
+ return 0;
+}
+
+/* dl pga gain */
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB)
+#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define DL_GAIN_REG_MASK 0x0f9f
+
+static void hp_zcd_disable(struct mt6358_priv *priv)
+{
+ regmap_write(priv->regmap, MT6358_ZCD_CON0, 0x0000);
+}
+
+static void hp_main_output_ramp(struct mt6358_priv *priv, bool up)
+{
+ int i, stage;
+ int target = 7;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= target; i++) {
+ stage = up ? i : target - i;
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x7 << 8, stage << 8);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x7 << 11, stage << 11);
+ usleep_range(100, 150);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6358_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= 0xf; i++) {
+ stage = up ? i : 0xf - i;
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xf << 12, stage << 12);
+ usleep_range(100, 150);
+ }
+}
+
+static void hp_pull_down(struct mt6358_priv *priv, bool enable)
+{
+ int i;
+
+ if (enable) {
+ for (i = 0x0; i <= 0x6; i++) {
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x7, i);
+ usleep_range(600, 700);
+ }
+ } else {
+ for (i = 0x6; i >= 0x1; i--) {
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x7, i);
+ usleep_range(600, 700);
+ }
+ }
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_10DB) ||
+ reg_idx == DL_GAIN_N_40DB;
+}
+
+static void headset_volume_ramp(struct mt6358_priv *priv, int from, int to)
+{
+ int offset = 0, count = 0, reg_idx;
+
+ if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to))
+ dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n",
+ __func__, from, to);
+
+ dev_info(priv->dev, "%s(), from %d, to %d\n",
+ __func__, from, to);
+
+ if (to > from)
+ offset = to - from;
+ else
+ offset = from - to;
+
+ while (offset >= 0) {
+ if (to > from)
+ reg_idx = from + count;
+ else
+ reg_idx = from - count;
+
+ if (is_valid_hp_pga_idx(reg_idx)) {
+ regmap_update_bits(priv->regmap,
+ MT6358_ZCD_CON2,
+ DL_GAIN_REG_MASK,
+ (reg_idx << 7) | reg_idx);
+ usleep_range(200, 300);
+ }
+ offset--;
+ count++;
+ }
+}
+
+static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = 0;
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ switch (mc->reg) {
+ case MT6358_ZCD_CON2:
+ regmap_read(priv->regmap, MT6358_ZCD_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] =
+ (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] =
+ (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK;
+ break;
+ case MT6358_ZCD_CON1:
+ regmap_read(priv->regmap, MT6358_ZCD_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] =
+ (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] =
+ (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK;
+ break;
+ case MT6358_ZCD_CON3:
+ regmap_read(priv->regmap, MT6358_ZCD_CON3, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] =
+ (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTR] =
+ (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
+ break;
+ case MT6358_AUDENC_ANA_CON0:
+ case MT6358_AUDENC_ANA_CON1:
+ regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON0, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] =
+ (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK;
+ regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] =
+ (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK;
+ break;
+ }
+
+ return ret;
+}
+
+static void mt6358_restore_pga(struct mt6358_priv *priv);
+
+static int mt6358_enable_wov_phase2(struct mt6358_priv *priv)
+{
+ /* analog */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xffff, 0x0800);
+ mt6358_restore_pga(priv);
+
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xffff, 0x0025);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
+ 0xffff, 0x0005);
+
+ /* digital */
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
+ 0xffff, 0x0451);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1);
+
+ return 0;
+}
+
+static int mt6358_disable_wov_phase2(struct mt6358_priv *priv)
+{
+ /* digital */
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
+ 0xffff, 0x0450);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
+ 0xffff, 0x0c00);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400);
+ regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8);
+ regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
+ 0xffff, 0x0000);
+
+ /* analog */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
+ 0xffff, 0x0004);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xffff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xffff, 0x0000);
+ mt6358_restore_pga(priv);
+ regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0xffff, 0x0010);
+
+ return 0;
+}
+
+static int mt6358_get_wov(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->wov_enabled;
+ return 0;
+}
+
+static int mt6358_put_wov(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+
+ if (enabled < 0 || enabled > 1)
+ return -EINVAL;
+
+ if (priv->wov_enabled != enabled) {
+ if (enabled)
+ mt6358_enable_wov_phase2(priv);
+ else
+ mt6358_disable_wov_phase2(priv);
+
+ priv->wov_enabled = enabled;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mt6358_dmic_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->dmic_one_wire_mode;
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 0;
+}
+
+static int mt6358_dmic_mode_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
+ int enabled = ucontrol->value.integer.value[0];
+
+ if (enabled < 0 || enabled > 1)
+ return -EINVAL;
+
+ if (priv->dmic_one_wire_mode != enabled) {
+ priv->dmic_one_wire_mode = enabled;
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 1;
+ }
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new mt6358_snd_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_EXT_TLV("Headphone Volume",
+ MT6358_ZCD_CON2, 0, 7, 0x12, 1,
+ snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
+ SOC_DOUBLE_EXT_TLV("Lineout Volume",
+ MT6358_ZCD_CON1, 0, 7, 0x12, 1,
+ snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
+ SOC_SINGLE_EXT_TLV("Handset Volume",
+ MT6358_ZCD_CON3, 0, 0x12, 1,
+ snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_EXT_TLV("PGA Volume",
+ MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1,
+ 8, 4, 0,
+ snd_soc_get_volsw, mt6358_put_volsw, pga_tlv),
+
+ SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0,
+ mt6358_get_wov, mt6358_put_wov),
+
+ SOC_SINGLE_BOOL_EXT("Dmic Mode Switch", 0,
+ mt6358_dmic_mode_get, mt6358_dmic_mode_set),
+};
+
+/* MUX */
+/* LOL MUX */
+static const char * const lo_in_mux_map[] = {
+ "Open", "Mute", "Playback", "Test Mode"
+};
+
+static int lo_in_mux_map_value[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum,
+ MT6358_AUDDEC_ANA_CON7,
+ RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT,
+ RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK,
+ lo_in_mux_map,
+ lo_in_mux_map_value);
+
+static const struct snd_kcontrol_new lo_in_mux_control =
+ SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum);
+
+/*HP MUX */
+enum {
+ HP_MUX_OPEN = 0,
+ HP_MUX_HPSPK,
+ HP_MUX_HP,
+ HP_MUX_TEST_MODE,
+ HP_MUX_HP_IMPEDANCE,
+ HP_MUX_MASK = 0x7,
+};
+
+static const char * const hp_in_mux_map[] = {
+ "Open",
+ "LoudSPK Playback",
+ "Audio Playback",
+ "Test Mode",
+ "HP Impedance",
+};
+
+static int hp_in_mux_map_value[] = {
+ HP_MUX_OPEN,
+ HP_MUX_HPSPK,
+ HP_MUX_HP,
+ HP_MUX_TEST_MODE,
+ HP_MUX_HP_IMPEDANCE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ HP_MUX_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpl_in_mux_control =
+ SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ HP_MUX_MASK,
+ hp_in_mux_map,
+ hp_in_mux_map_value);
+
+static const struct snd_kcontrol_new hpr_in_mux_control =
+ SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum);
+
+/* RCV MUX */
+enum {
+ RCV_MUX_OPEN = 0,
+ RCV_MUX_MUTE,
+ RCV_MUX_VOICE_PLAYBACK,
+ RCV_MUX_TEST_MODE,
+ RCV_MUX_MASK = 0x3,
+};
+
+static const char * const rcv_in_mux_map[] = {
+ "Open", "Mute", "Voice Playback", "Test Mode"
+};
+
+static int rcv_in_mux_map_value[] = {
+ RCV_MUX_OPEN,
+ RCV_MUX_MUTE,
+ RCV_MUX_VOICE_PLAYBACK,
+ RCV_MUX_TEST_MODE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ RCV_MUX_MASK,
+ rcv_in_mux_map,
+ rcv_in_mux_map_value);
+
+static const struct snd_kcontrol_new rcv_in_mux_control =
+ SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum);
+
+/* DAC In MUX */
+static const char * const dac_in_mux_map[] = {
+ "Normal Path", "Sgen"
+};
+
+static int dac_in_mux_map_value[] = {
+ 0x0, 0x1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum,
+ MT6358_AFE_TOP_CON0,
+ DL_SINE_ON_SFT,
+ DL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new dac_in_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum);
+
+/* AIF Out MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum,
+ MT6358_AFE_TOP_CON0,
+ UL_SINE_ON_SFT,
+ UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum);
+
+/* Mic Type MUX */
+enum {
+ MIC_TYPE_MUX_IDLE = 0,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+ MIC_TYPE_MUX_MASK = 0x7,
+};
+
+#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+
+static const char * const mic_type_mux_map[] = {
+ "Idle",
+ "ACC",
+ "DMIC",
+ "DCC",
+ "DCC_ECM_DIFF",
+ "DCC_ECM_SINGLE",
+};
+
+static int mic_type_mux_map_value[] = {
+ MIC_TYPE_MUX_IDLE,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(mic_type_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ MIC_TYPE_MUX_MASK,
+ mic_type_mux_map,
+ mic_type_mux_map_value);
+
+static const struct snd_kcontrol_new mic_type_mux_control =
+ SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum);
+
+/* ADC L MUX */
+enum {
+ ADC_MUX_IDLE = 0,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+ ADC_MUX_MASK = 0x3,
+};
+
+static const char * const adc_left_mux_map[] = {
+ "Idle", "AIN0", "Left Preamplifier", "Idle_1"
+};
+
+static int adc_mux_map_value[] = {
+ ADC_MUX_IDLE,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADC_MUX_MASK,
+ adc_left_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_left_mux_control =
+ SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum);
+
+/* ADC R MUX */
+static const char * const adc_right_mux_map[] = {
+ "Idle", "AIN0", "Right Preamplifier", "Idle_1"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADC_MUX_MASK,
+ adc_right_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_right_mux_control =
+ SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum);
+
+/* PGA L MUX */
+enum {
+ PGA_MUX_NONE = 0,
+ PGA_MUX_AIN0,
+ PGA_MUX_AIN1,
+ PGA_MUX_AIN2,
+ PGA_MUX_MASK = 0x3,
+};
+
+static const char * const pga_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2"
+};
+
+static int pga_mux_map_value[] = {
+ PGA_MUX_NONE,
+ PGA_MUX_AIN0,
+ PGA_MUX_AIN1,
+ PGA_MUX_AIN2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ PGA_MUX_MASK,
+ pga_mux_map,
+ pga_mux_map_value);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+/* PGA R MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ PGA_MUX_MASK,
+ pga_mux_map,
+ pga_mux_map_value);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+static int mt_clksq_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* audio clk source from internal dcxo */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6,
+ RG_CLKSQ_IN_SEL_TEST_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B);
+
+ regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG0,
+ 0xff3f,
+ 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG1,
+ 0xffff,
+ 0x0001);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000);
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_info(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->dl_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ playback_gpio_set(priv);
+
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000);
+ regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0);
+
+ playback_gpio_reset(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_hp_enable(struct mt6358_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+ /* release HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x1 << 6);
+
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
+
+ /* Set HPR/HPL gain as minimum (~ -40dB) */
+ regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_40DB_REG);
+
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle RG_DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001);
+ /* Set NCP soft start mode as default mode: 100us */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000);
+ usleep_range(250, 270);
+
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x1055);
+ /* Enable NV regulator (-1.2V) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001);
+ usleep_range(100, 120);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(priv);
+
+ /* Disable headphone short-circuit protection */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000);
+
+ /* Enable IBIST */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HPP/N STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033);
+
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x000c);
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x003c);
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00);
+ /* Enable HP driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0);
+ /* Enable HP driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0);
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00fc);
+
+ /* Enable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00);
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200);
+
+ /* Select CMFB resistor bulk to AC mode */
+ /* Selec HS/LO cap size (6.5pF default) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000);
+
+ /* Enable HP main output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00ff);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf);
+
+ /* apply volume setting */
+ headset_volume_ramp(priv,
+ DL_GAIN_N_10DB,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]);
+
+ /* Disable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3f03);
+ usleep_range(100, 120);
+
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1);
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30ff);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0xf201);
+ usleep_range(100, 120);
+
+ /* Switch HPL MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x32ff);
+ /* Switch HPR MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3aff);
+
+ /* Disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ return 0;
+}
+
+static int mtk_hp_disable(struct mt6358_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+
+ /* HPR/HPL mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x0f00, 0x0000);
+
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0x0001, 0x0000);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0);
+
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3);
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf);
+
+ /* decrease HPL/R gain to normal gain step by step */
+ headset_volume_ramp(priv,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL],
+ DL_GAIN_N_40DB);
+
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0);
+
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00);
+
+ /* Disable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00);
+
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000);
+
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1,
+ 0x3 << 2, 0x0);
+
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12,
+ 0x1 << 8, 0x1 << 8);
+
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x0);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3,
+ 0x1, 0x1);
+
+ /* Increase ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON2,
+ 0x1 << 14, 0x0);
+
+ /* Set HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x0);
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ return 0;
+}
+
+static int mtk_hp_spk_enable(struct mt6358_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+ /* release HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x1 << 6);
+
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
+
+ /* Set HPR/HPL gain to -10dB */
+ regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_10DB_REG);
+
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle RG_DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001);
+ /* Set NCP soft start mode as default mode: 100us */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000);
+ usleep_range(250, 270);
+
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x1055);
+ /* Enable NV regulator (-1.2V) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001);
+ usleep_range(100, 120);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(priv);
+
+ /* Disable headphone short-circuit protection */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000);
+
+ /* Enable IBIST */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HPP/N STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033);
+
+ /* Disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ /* Enable HP driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0);
+ /* Enable HP driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0);
+ /* Enable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200);
+
+ /* Select CMFB resistor bulk to AC mode */
+ /* Selec HS/LO cap size (6.5pF default) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000);
+
+ /* Enable HP main output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x0003);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+
+ /* Set LO gain as minimum (~ -40dB) */
+ regmap_write(priv->regmap, MT6358_ZCD_CON1, DL_GAIN_N_40DB_REG);
+ /* apply volume setting */
+ headset_volume_ramp(priv,
+ DL_GAIN_N_10DB,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]);
+
+ /* Set LO STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0110);
+ /* Enable LO driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0112);
+ /* Enable LO driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0113);
+
+ /* Set LOL gain to normal gain step by step */
+ regmap_update_bits(priv->regmap, MT6358_ZCD_CON1,
+ RG_AUDLOLGAIN_MASK_SFT,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] <<
+ RG_AUDLOLGAIN_SFT);
+ regmap_update_bits(priv->regmap, MT6358_ZCD_CON1,
+ RG_AUDLORGAIN_MASK_SFT,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] <<
+ RG_AUDLORGAIN_SFT);
+
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1);
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f9);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0201);
+ /* Switch LOL MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x011b);
+ /* Switch HPL/R MUX to Line-out */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x35f9);
+
+ return 0;
+}
+
+static int mtk_hp_spk_disable(struct mt6358_priv *priv)
+{
+ /* HPR/HPL mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x0f00, 0x0000);
+ /* LOL mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ 0x3 << 2, 0x0000);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0);
+
+ /* decrease HPL/R gain to normal gain step by step */
+ headset_volume_ramp(priv,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL],
+ DL_GAIN_N_40DB);
+
+ /* decrease LOL gain to minimum gain step by step */
+ regmap_update_bits(priv->regmap, MT6358_ZCD_CON1,
+ DL_GAIN_REG_MASK, DL_GAIN_N_40DB_REG);
+
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0);
+
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3);
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf);
+
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ 0x1, 0x0);
+
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ 0x1 << 1, 0x0);
+
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xff << 8, 0x0000);
+
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12,
+ 0x1 << 8, 0x1 << 8);
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, 0x1055, 0x0);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x1, 0x1);
+
+ /* Set HP CMFB gate rstb */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4,
+ 0x1 << 6, 0x0);
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+
+ return 0;
+}
+
+static int mt_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+ int device = DEVICE_HP;
+
+ dev_info(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n",
+ __func__,
+ event,
+ priv->dev_counter[device],
+ mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->dev_counter[device]++;
+ if (priv->dev_counter[device] > 1)
+ break; /* already enabled, do nothing */
+ else if (priv->dev_counter[device] <= 0)
+ dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d <= 0\n",
+ __func__,
+ priv->dev_counter[device]);
+
+ priv->mux_select[MUX_HP_L] = mux;
+
+ if (mux == HP_MUX_HP)
+ mtk_hp_enable(priv);
+ else if (mux == HP_MUX_HPSPK)
+ mtk_hp_spk_enable(priv);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->dev_counter[device]--;
+ if (priv->dev_counter[device] > 0) {
+ break; /* still being used, don't close */
+ } else if (priv->dev_counter[device] < 0) {
+ dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d < 0\n",
+ __func__,
+ priv->dev_counter[device]);
+ priv->dev_counter[device] = 0;
+ break;
+ }
+
+ if (priv->mux_select[MUX_HP_L] == HP_MUX_HP)
+ mtk_hp_disable(priv);
+ else if (priv->mux_select[MUX_HP_L] == HP_MUX_HPSPK)
+ mtk_hp_spk_disable(priv);
+
+ priv->mux_select[MUX_HP_L] = mux;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_rcv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_info(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__,
+ event,
+ snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
+
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle RG_DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001);
+ /* Set NCP soft start mode as default mode: 100us */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000);
+ usleep_range(250, 270);
+
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x1055);
+ /* Enable NV regulator (-1.2V) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001);
+ usleep_range(100, 120);
+
+ /* Disable AUD_ZCD */
+ hp_zcd_disable(priv);
+
+ /* Disable handset short-circuit protection */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0010);
+
+ /* Enable IBIST */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055);
+ /* Set HS STB enhance circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0090);
+
+ /* Disable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000);
+ /* Select CMFB resistor bulk to AC mode */
+ /* Selec HS/LO cap size (6.5pF default) */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000);
+
+ /* Enable HS driver bias circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0092);
+ /* Enable HS driver core circuits */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0093);
+
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1, 0x1);
+
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x0009);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0001);
+ /* Switch HS MUX to audio DAC */
+ regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x009b);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* HS mux to open */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT,
+ RCV_MUX_OPEN);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1, 0x0);
+
+ /* decrease HS gain to minimum gain step by step */
+ regmap_write(priv->regmap, MT6358_ZCD_CON3, DL_GAIN_N_40DB);
+
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ 0x1, 0x0);
+
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ 0x1 << 1, 0x0000);
+
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xff << 8, 0x0);
+
+ /* Enable HP main CMFB Switch */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9,
+ 0xff << 8, 0x2 << 8);
+
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12,
+ 0x1 << 8, 0x1 << 8);
+
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15,
+ 0x1, 0x0);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x1055, 0x0);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3,
+ 0x1, 0x1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n",
+ __func__, event, priv->ul_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ capture_gpio_set(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ capture_gpio_reset(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x\n",
+ __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1 << 5, 0x1 << 5);
+ /* ADC CLK from CLKGEN (13MHz) */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3,
+ 0x0000);
+ /* Enable LCLDO_ENC 1P8V */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x0100);
+ /* LCLDO_ENC remote sense */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x2500);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* LCLDO_ENC remote sense off */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x0100);
+ /* disable LCLDO_ENC 1P8V */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14,
+ 0x2500, 0x0000);
+
+ /* ADC CLK from CLKGEN (13MHz) */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3, 0x0000);
+ /* disable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
+ 0x1 << 5, 0x0 << 5);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt6358_amic_enable(struct mt6358_priv *priv)
+{
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE];
+ unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L];
+ unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R];
+
+ dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n",
+ __func__, mic_type, mux_pga_l, mux_pga_r);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* DCC 50k CLK (from 26M) */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2061);
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG1, 0x0100);
+ }
+
+ /* mic bias 0 */
+ if (mux_pga_l == PGA_MUX_AIN0 || mux_pga_l == PGA_MUX_AIN2 ||
+ mux_pga_r == PGA_MUX_AIN0 || mux_pga_r == PGA_MUX_AIN2) {
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff00, 0x7700);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff00, 0x1100);
+ break;
+ default:
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff00, 0x0000);
+ break;
+ }
+ /* Enable MICBIAS0, MISBIAS0 = 1P9V */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
+ 0xff, 0x21);
+ }
+
+ /* mic bias 1 */
+ if (mux_pga_l == PGA_MUX_AIN1 || mux_pga_r == PGA_MUX_AIN1) {
+ /* Enable MICBIAS1, MISBIAS1 = 2P6V */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_write(priv->regmap,
+ MT6358_AUDENC_ANA_CON10, 0x0161);
+ else
+ regmap_write(priv->regmap,
+ MT6358_AUDENC_ANA_CON10, 0x0061);
+ }
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio L/R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xf8ff, 0x0004);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xf8ff, 0x0004);
+ } else {
+ /* reset reg */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xf8ff, 0x0000);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xf8ff, 0x0000);
+ }
+
+ if (mux_pga_l != PGA_MUX_NONE) {
+ /* L preamplifier input sel */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLINPUTSEL_MASK_SFT,
+ mux_pga_l << RG_AUDPREAMPLINPUTSEL_SFT);
+
+ /* L preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLON_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLON_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLDCCEN_SFT);
+ }
+
+ /* L ADC input sel : L PGA. Enable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDADCLINPUTSEL_MASK_SFT,
+ ADC_MUX_PREAMPLIFIER <<
+ RG_AUDADCLINPUTSEL_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDADCLPWRUP_MASK_SFT,
+ 0x1 << RG_AUDADCLPWRUP_SFT);
+ }
+
+ if (mux_pga_r != PGA_MUX_NONE) {
+ /* R preamplifier input sel */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRINPUTSEL_MASK_SFT,
+ mux_pga_r << RG_AUDPREAMPRINPUTSEL_SFT);
+
+ /* R preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRON_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRON_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRDCCEN_SFT);
+ }
+
+ /* R ADC input sel : R PGA. Enable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDADCRINPUTSEL_MASK_SFT,
+ ADC_MUX_PREAMPLIFIER <<
+ RG_AUDADCRINPUTSEL_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDADCRPWRUP_MASK_SFT,
+ 0x1 << RG_AUDADCRPWRUP_SFT);
+ }
+
+ if (IS_DCC_BASE(mic_type)) {
+ usleep_range(100, 150);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, 0x0);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, 0x0);
+
+ /* Short body to ground in PGA */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON3,
+ 0x1 << 12, 0x0);
+ }
+
+ /* here to set digital part */
+ mt6358_mtkaif_tx_enable(priv);
+
+ /* UL dmic setting off */
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0000);
+
+ /* UL turn on */
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0001);
+
+ return 0;
+}
+
+static void mt6358_amic_disable(struct mt6358_priv *priv)
+{
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE];
+ unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L];
+ unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R];
+
+ dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n",
+ __func__, mic_type, mux_pga_l, mux_pga_r);
+
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L,
+ 0x0001, 0x0000);
+
+ /* disable aud_pad TX fifos */
+ mt6358_mtkaif_tx_disable(priv);
+
+ /* L ADC input sel : off, disable L ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xf000, 0x0000);
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0x1 << 1, 0x0);
+ /* L preamplifier input sel : off, L PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0xfffb, 0x0000);
+
+ /* disable L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ 0x1 << 2, 0x0);
+
+ /* R ADC input sel : off, disable R ADC */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0xf000, 0x0000);
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0x1 << 1, 0x0);
+ /* R preamplifier input sel : off, R PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0x0ffb, 0x0000);
+
+ /* disable R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ 0x1 << 2, 0x0);
+
+ /* mic bias */
+ /* Disable MICBIAS0, MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000);
+
+ /* Disable MICBIAS1 */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10,
+ 0x0001, 0x0000);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* dcclk_gen_on=1'b0 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060);
+ /* dcclk_pdn=1'b1 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ /* dcclk_ref_ck_sel=2'b00 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ /* dcclk_div=11'b00100000011 */
+ regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062);
+ }
+}
+
+static int mt6358_dmic_enable(struct mt6358_priv *priv)
+{
+ dev_info(priv->dev, "%s()\n", __func__);
+
+ /* mic bias */
+ /* Enable MICBIAS0, MISBIAS0 = 1P9V */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0021);
+
+ /* RG_BANDGAPGEN=1'b0 */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10,
+ 0x1 << 12, 0x0);
+
+ /* DMIC enable */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0005);
+
+ /* here to set digital part */
+ mt6358_mtkaif_tx_enable(priv);
+
+ /* UL dmic setting */
+ if (priv->dmic_one_wire_mode)
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0400);
+ else
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0080);
+
+ /* UL turn on */
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0003);
+
+ /* Prevent pop noise form dmic hw */
+ msleep(100);
+
+ return 0;
+}
+
+static void mt6358_dmic_disable(struct mt6358_priv *priv)
+{
+ dev_info(priv->dev, "%s()\n", __func__);
+
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L,
+ 0x0003, 0x0000);
+
+ /* disable aud_pad TX fifos */
+ mt6358_mtkaif_tx_disable(priv);
+
+ /* DMIC disable */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0000);
+
+ /* mic bias */
+ /* MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0001);
+
+ /* RG_BANDGAPGEN=1'b0 */
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10,
+ 0x1 << 12, 0x0);
+
+ /* MICBIA0 disable */
+ regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000);
+}
+
+static void mt6358_restore_pga(struct mt6358_priv *priv)
+{
+ unsigned int gain_l, gain_r;
+
+ gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLGAIN_MASK_SFT,
+ gain_l << RG_AUDPREAMPLGAIN_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRGAIN_MASK_SFT,
+ gain_r << RG_AUDPREAMPRGAIN_SFT);
+}
+
+static int mt_mic_type_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_WILL_PMU:
+ priv->mux_select[MUX_MIC_TYPE] = mux;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6358_dmic_enable(priv);
+ break;
+ default:
+ mt6358_amic_enable(priv);
+ break;
+ }
+ mt6358_restore_pga(priv);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (priv->mux_select[MUX_MIC_TYPE]) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6358_dmic_disable(priv);
+ break;
+ default:
+ mt6358_amic_disable(priv);
+ break;
+ }
+
+ priv->mux_select[MUX_MIC_TYPE] = mux;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_l_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_ADC_L] = mux;
+
+ return 0;
+}
+
+static int mt_adc_r_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_ADC_R] = mux;
+
+ return 0;
+}
+
+static int mt_pga_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_PGA_L] = mux;
+
+ return 0;
+}
+
+static int mt_pga_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ priv->mux_select[MUX_PGA_R] = mux;
+
+ return 0;
+}
+
+static int mt_delay_250_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(250, 270);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6358_dapm_widgets[] = {
+ /* Global Supply*/
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6358_DCXO_CW14,
+ RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6358_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6358_AUDENC_ANA_CON6,
+ RG_CLKSQ_EN_SFT, 0,
+ mt_clksq_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_AUD_CK_PDN_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK,
+ MT6358_AUD_TOP_CKPDN_CON0,
+ RG_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+
+ /* Digital Clock */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_AFE_CTL_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6358_AUDIO_TOP_CON0,
+ PDN_RESERVED_SFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* AFE ON */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6358_AFE_UL_DL_CON0, AFE_ON_SFT, 0,
+ NULL, 0),
+
+ /* AIF Rx*/
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0,
+ MT6358_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ mt_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DL Supply */
+ SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* LOL */
+ SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6358_AUDDEC_ANA_CON7,
+ RG_LOOUTPUTSTBENH_VAUDP15_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6358_AUDDEC_ANA_CON7,
+ RG_AUDLOLPWRUP_VAUDP15_SFT, 0, NULL, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_MUX_E("HPL Mux", SND_SOC_NOPM, 0, 0,
+ &hpl_in_mux_control,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("HPR Mux", SND_SOC_NOPM, 0, 0,
+ &hpr_in_mux_control,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* Receiver */
+ SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0,
+ &rcv_in_mux_control,
+ mt_rcv_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("Receiver"),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+ SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L HSSPK"),
+
+ /* SGEN */
+ SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6358_AFE_SGEN_CFG0,
+ SGEN_DAC_EN_CTL_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6358_AFE_SGEN_CFG0,
+ SGEN_MUTE_SW_CTL_SFT, 1,
+ mt_sgen_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6358_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0,
+ SND_SOC_NOPM, 0, 0,
+ mt_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC Supply", SUPPLY_SEQ_ADC_SUPPLY,
+ SND_SOC_NOPM, 0, 0,
+ mt_adc_supply_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Uplinks MUX */
+ SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif_out_mux_control),
+
+ SND_SOC_DAPM_MUX_E("Mic Type Mux", SND_SOC_NOPM, 0, 0,
+ &mic_type_mux_control,
+ mt_mic_type_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &adc_left_mux_control,
+ mt_adc_l_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &adc_right_mux_control,
+ mt_adc_r_event,
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_left_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_right_event,
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_PGA("PGA L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* UL input */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+};
+
+static const struct snd_soc_dapm_route mt6358_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "AIF Out Mux"},
+ {"AIF1TX", NULL, "CLK_BUF"},
+ {"AIF1TX", NULL, "AUDGLB"},
+ {"AIF1TX", NULL, "CLKSQ Audio"},
+
+ {"AIF1TX", NULL, "AUD_CK"},
+ {"AIF1TX", NULL, "AUDIF_CK"},
+
+ {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"AIF1TX", NULL, "AUDIO_TOP_I2S_DL"},
+
+ {"AIF1TX", NULL, "AFE_ON"},
+
+ {"AIF Out Mux", NULL, "Mic Type Mux"},
+
+ {"Mic Type Mux", "ACC", "ADC L"},
+ {"Mic Type Mux", "ACC", "ADC R"},
+ {"Mic Type Mux", "DCC", "ADC L"},
+ {"Mic Type Mux", "DCC", "ADC R"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC L"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC R"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC L"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC R"},
+ {"Mic Type Mux", "DMIC", "AIN0"},
+ {"Mic Type Mux", "DMIC", "AIN2"},
+
+ {"ADC L", NULL, "ADC L Mux"},
+ {"ADC L", NULL, "ADC Supply"},
+ {"ADC R", NULL, "ADC R Mux"},
+ {"ADC R", NULL, "ADC Supply"},
+
+ {"ADC L Mux", "Left Preamplifier", "PGA L"},
+
+ {"ADC R Mux", "Right Preamplifier", "PGA R"},
+
+ {"PGA L", NULL, "PGA L Mux"},
+ {"PGA R", NULL, "PGA R Mux"},
+
+ {"PGA L Mux", "AIN0", "AIN0"},
+ {"PGA L Mux", "AIN1", "AIN1"},
+ {"PGA L Mux", "AIN2", "AIN2"},
+
+ {"PGA R Mux", "AIN0", "AIN0"},
+ {"PGA R Mux", "AIN1", "AIN1"},
+ {"PGA R Mux", "AIN2", "AIN2"},
+
+ /* DL Supply */
+ {"DL Power Supply", NULL, "CLK_BUF"},
+ {"DL Power Supply", NULL, "AUDGLB"},
+ {"DL Power Supply", NULL, "CLKSQ Audio"},
+
+ {"DL Power Supply", NULL, "AUDNCP_CK"},
+ {"DL Power Supply", NULL, "ZCD13M_CK"},
+ {"DL Power Supply", NULL, "AUD_CK"},
+ {"DL Power Supply", NULL, "AUDIF_CK"},
+
+ /* DL Digital Supply */
+ {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"},
+
+ {"DL Digital Clock", NULL, "AFE_ON"},
+
+ {"AIF_RX", NULL, "DL Digital Clock"},
+
+ /* DL Path */
+ {"DAC In Mux", "Normal Path", "AIF_RX"},
+
+ {"DAC In Mux", "Sgen", "SGEN DL"},
+ {"SGEN DL", NULL, "SGEN DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN DL Enable"},
+ {"SGEN DL", NULL, "DL Digital Clock"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC In Mux"},
+ {"DACL", NULL, "DL Power Supply"},
+
+ {"DACR", NULL, "DAC In Mux"},
+ {"DACR", NULL, "DL Power Supply"},
+
+ /* Lineout Path */
+ {"LOL Mux", "Playback", "DACL"},
+
+ {"LOL Buffer", NULL, "LOL Mux"},
+ {"LOL Buffer", NULL, "LO Stability Enh"},
+
+ {"LINEOUT L", NULL, "LOL Buffer"},
+
+ /* Headphone Path */
+ {"HPL Mux", "Audio Playback", "DACL"},
+ {"HPR Mux", "Audio Playback", "DACR"},
+ {"HPL Mux", "HP Impedance", "DACL"},
+ {"HPR Mux", "HP Impedance", "DACR"},
+ {"HPL Mux", "LoudSPK Playback", "DACL"},
+ {"HPR Mux", "LoudSPK Playback", "DACR"},
+
+ {"Headphone L", NULL, "HPL Mux"},
+ {"Headphone R", NULL, "HPR Mux"},
+ {"Headphone L Ext Spk Amp", NULL, "HPL Mux"},
+ {"Headphone R Ext Spk Amp", NULL, "HPR Mux"},
+ {"LINEOUT L HSSPK", NULL, "HPL Mux"},
+
+ /* Receiver Path */
+ {"RCV Mux", "Voice Playback", "DACL"},
+ {"Receiver", NULL, "RCV Mux"},
+};
+
+static int mt6358_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int rate = params_rate(params);
+
+ dev_info(priv->dev, "%s(), substream->stream %d, rate %d, number %d\n",
+ __func__,
+ substream->stream,
+ rate,
+ substream->number);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ priv->dl_rate = rate;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->ul_rate = rate;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6358_codec_dai_ops = {
+ .hw_params = mt6358_codec_dai_hw_params,
+};
+
+#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6358_dai_driver[] = {
+ {
+ .name = "mt6358-snd-codec-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6358_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = MT6358_FORMATS,
+ },
+ .ops = &mt6358_codec_dai_ops,
+ },
+};
+
+static void mt6358_codec_init_reg(struct mt6358_priv *priv)
+{
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDHPLSCDISABLE_VAUDP15_SFT);
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0,
+ RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDHPRSCDISABLE_VAUDP15_SFT);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6,
+ RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDHSSCDISABLE_VAUDP15_SFT);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7,
+ RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT,
+ 0x1 << RG_AUDLOLSCDISABLE_VAUDP15_SFT);
+
+ /* accdet s/w enable */
+ regmap_update_bits(priv->regmap, MT6358_ACCDET_CON13,
+ 0xFFFF, 0x700E);
+
+ /* gpio miso driving set to 4mA */
+ regmap_write(priv->regmap, MT6358_DRV_CON3, 0x8888);
+
+ /* set gpio */
+ playback_gpio_reset(priv);
+ capture_gpio_reset(priv);
+}
+
+static int mt6358_codec_probe(struct snd_soc_component *cmpnt)
+{
+ struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int ret;
+
+ snd_soc_component_init_regmap(cmpnt, priv->regmap);
+
+ mt6358_codec_init_reg(priv);
+
+ priv->avdd_reg = devm_regulator_get(priv->dev, "Avdd");
+ if (IS_ERR(priv->avdd_reg)) {
+ dev_err(priv->dev, "%s() have no Avdd supply", __func__);
+ return PTR_ERR(priv->avdd_reg);
+ }
+
+ ret = regulator_enable(priv->avdd_reg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6358_soc_component_driver = {
+ .probe = mt6358_codec_probe,
+ .controls = mt6358_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6358_snd_controls),
+ .dapm_widgets = mt6358_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets),
+ .dapm_routes = mt6358_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes),
+ .endianness = 1,
+};
+
+static void mt6358_parse_dt(struct mt6358_priv *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,dmic-mode",
+ &priv->dmic_one_wire_mode);
+ if (ret) {
+ dev_warn(priv->dev, "%s() failed to read dmic-mode\n",
+ __func__);
+ priv->dmic_one_wire_mode = 0;
+ }
+}
+
+static int mt6358_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6358_priv *priv;
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct mt6358_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ priv->dev = &pdev->dev;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ mt6358_parse_dt(priv);
+
+ dev_info(priv->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6358_soc_component_driver,
+ mt6358_dai_driver,
+ ARRAY_SIZE(mt6358_dai_driver));
+}
+
+static const struct of_device_id mt6358_of_match[] = {
+ {.compatible = "mediatek,mt6358-sound",},
+ {.compatible = "mediatek,mt6366-sound",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt6358_of_match);
+
+static struct platform_driver mt6358_platform_driver = {
+ .driver = {
+ .name = "mt6358-sound",
+ .of_match_table = mt6358_of_match,
+ },
+ .probe = mt6358_platform_driver_probe,
+};
+
+module_platform_driver(mt6358_platform_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6358 ALSA SoC codec driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h
new file mode 100644
index 000000000000..b729c3899b7e
--- /dev/null
+++ b/sound/soc/codecs/mt6358.h
@@ -0,0 +1,2310 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6358.h -- mt6358 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef __MT6358_H__
+#define __MT6358_H__
+
+/* Reg bit define */
+/* MT6358_DCXO_CW14 */
+#define RG_XO_AUDIO_EN_M_SFT 13
+
+/* MT6358_DCXO_CW13 */
+#define RG_XO_VOW_EN_SFT 8
+
+/* MT6358_AUD_TOP_CKPDN_CON0 */
+#define RG_VOW13M_CK_PDN_SFT 13
+#define RG_VOW13M_CK_PDN_MASK 0x1
+#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13)
+#define RG_VOW32K_CK_PDN_SFT 12
+#define RG_VOW32K_CK_PDN_MASK 0x1
+#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12)
+#define RG_AUD_INTRP_CK_PDN_SFT 8
+#define RG_AUD_INTRP_CK_PDN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8)
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7)
+#define RG_AUDNCP_CK_PDN_SFT 6
+#define RG_AUDNCP_CK_PDN_MASK 0x1
+#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6)
+#define RG_ZCD13M_CK_PDN_SFT 5
+#define RG_ZCD13M_CK_PDN_MASK 0x1
+#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5)
+#define RG_AUDIF_CK_PDN_SFT 2
+#define RG_AUDIF_CK_PDN_MASK 0x1
+#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2)
+#define RG_AUD_CK_PDN_SFT 1
+#define RG_AUD_CK_PDN_MASK 0x1
+#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1)
+#define RG_ACCDET_CK_PDN_SFT 0
+#define RG_ACCDET_CK_PDN_MASK 0x1
+#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_CKPDN_CON0_SET */
+#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AUD_TOP_CKPDN_CON0_CLR */
+#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AUD_TOP_CKSEL_CON0 */
+#define RG_AUDIF_CK_CKSEL_SFT 3
+#define RG_AUDIF_CK_CKSEL_MASK 0x1
+#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_CKSEL_SFT 2
+#define RG_AUD_CK_CKSEL_MASK 0x1
+#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2)
+
+/* MT6358_AUD_TOP_CKSEL_CON0_SET */
+#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_CKSEL_CON0_CLR */
+#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_CKTST_CON0 */
+#define RG_VOW13M_CK_TSTSEL_SFT 9
+#define RG_VOW13M_CK_TSTSEL_MASK 0x1
+#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9)
+#define RG_VOW13M_CK_TST_DIS_SFT 8
+#define RG_VOW13M_CK_TST_DIS_MASK 0x1
+#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8)
+#define RG_AUD26M_CK_TSTSEL_SFT 4
+#define RG_AUD26M_CK_TSTSEL_MASK 0x1
+#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4)
+#define RG_AUDIF_CK_TSTSEL_SFT 3
+#define RG_AUDIF_CK_TSTSEL_MASK 0x1
+#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_TSTSEL_SFT 2
+#define RG_AUD_CK_TSTSEL_MASK 0x1
+#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2)
+#define RG_AUD26M_CK_TST_DIS_SFT 0
+#define RG_AUD26M_CK_TST_DIS_MASK 0x1
+#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_CLK_HWEN_CON0 */
+#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_CLK_HWEN_CON0_SET */
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_CLK_HWEN_CON0_CLR */
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_RST_CON0 */
+#define RG_AUDNCP_RST_SFT 3
+#define RG_AUDNCP_RST_MASK 0x1
+#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3)
+#define RG_ZCD_RST_SFT 2
+#define RG_ZCD_RST_MASK 0x1
+#define RG_ZCD_RST_MASK_SFT (0x1 << 2)
+#define RG_ACCDET_RST_SFT 1
+#define RG_ACCDET_RST_MASK 0x1
+#define RG_ACCDET_RST_MASK_SFT (0x1 << 1)
+#define RG_AUDIO_RST_SFT 0
+#define RG_AUDIO_RST_MASK 0x1
+#define RG_AUDIO_RST_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_RST_CON0_SET */
+#define RG_AUD_TOP_RST_CON0_SET_SFT 0
+#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_RST_CON0_CLR */
+#define RG_AUD_TOP_RST_CON0_CLR_SFT 0
+#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* MT6358_AUD_TOP_RST_BANK_CON0 */
+#define BANK_AUDZCD_SWRST_SFT 2
+#define BANK_AUDZCD_SWRST_MASK 0x1
+#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2)
+#define BANK_AUDIO_SWRST_SFT 1
+#define BANK_AUDIO_SWRST_MASK 0x1
+#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1)
+#define BANK_ACCDET_SWRST_SFT 0
+#define BANK_ACCDET_SWRST_MASK 0x1
+#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUD_TOP_INT_CON0 */
+#define RG_INT_EN_AUDIO_SFT 0
+#define RG_INT_EN_AUDIO_MASK 0x1
+#define RG_INT_EN_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_EN_ACCDET_SFT 5
+#define RG_INT_EN_ACCDET_MASK 0x1
+#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_EN_ACCDET_EINT0_SFT 6
+#define RG_INT_EN_ACCDET_EINT0_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_EN_ACCDET_EINT1_SFT 7
+#define RG_INT_EN_ACCDET_EINT1_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_CON0_SET */
+#define RG_AUD_INT_CON0_SET_SFT 0
+#define RG_AUD_INT_CON0_SET_MASK 0xffff
+#define RG_AUD_INT_CON0_SET_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_INT_CON0_CLR */
+#define RG_AUD_INT_CON0_CLR_SFT 0
+#define RG_AUD_INT_CON0_CLR_MASK 0xffff
+#define RG_AUD_INT_CON0_CLR_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUD_TOP_INT_MASK_CON0 */
+#define RG_INT_MASK_AUDIO_SFT 0
+#define RG_INT_MASK_AUDIO_MASK 0x1
+#define RG_INT_MASK_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_MASK_ACCDET_SFT 5
+#define RG_INT_MASK_ACCDET_MASK 0x1
+#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_MASK_ACCDET_EINT0_SFT 6
+#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_MASK_ACCDET_EINT1_SFT 7
+#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_MASK_CON0_SET */
+#define RG_AUD_INT_MASK_CON0_SET_SFT 0
+#define RG_AUD_INT_MASK_CON0_SET_MASK 0xff
+#define RG_AUD_INT_MASK_CON0_SET_MASK_SFT (0xff << 0)
+
+/* MT6358_AUD_TOP_INT_MASK_CON0_CLR */
+#define RG_AUD_INT_MASK_CON0_CLR_SFT 0
+#define RG_AUD_INT_MASK_CON0_CLR_MASK 0xff
+#define RG_AUD_INT_MASK_CON0_CLR_MASK_SFT (0xff << 0)
+
+/* MT6358_AUD_TOP_INT_STATUS0 */
+#define RG_INT_STATUS_AUDIO_SFT 0
+#define RG_INT_STATUS_AUDIO_MASK 0x1
+#define RG_INT_STATUS_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_STATUS_ACCDET_SFT 5
+#define RG_INT_STATUS_ACCDET_MASK 0x1
+#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_RAW_STATUS0 */
+#define RG_INT_RAW_STATUS_AUDIO_SFT 0
+#define RG_INT_RAW_STATUS_AUDIO_MASK 0x1
+#define RG_INT_RAW_STATUS_AUDIO_MASK_SFT (0x1 << 0)
+#define RG_INT_RAW_STATUS_ACCDET_SFT 5
+#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+/* MT6358_AUD_TOP_INT_MISC_CON0 */
+#define RG_AUD_TOP_INT_POLARITY_SFT 0
+#define RG_AUD_TOP_INT_POLARITY_MASK 0x1
+#define RG_AUD_TOP_INT_POLARITY_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON0 */
+#define RG_DIVCKS_CHG_SFT 0
+#define RG_DIVCKS_CHG_MASK 0x1
+#define RG_DIVCKS_CHG_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON1 */
+#define RG_DIVCKS_ON_SFT 0
+#define RG_DIVCKS_ON_MASK 0x1
+#define RG_DIVCKS_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON2 */
+#define RG_DIVCKS_PRG_SFT 0
+#define RG_DIVCKS_PRG_MASK 0x1ff
+#define RG_DIVCKS_PRG_MASK_SFT (0x1ff << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON3 */
+#define RG_DIVCKS_PWD_NCP_SFT 0
+#define RG_DIVCKS_PWD_NCP_MASK 0x1
+#define RG_DIVCKS_PWD_NCP_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDNCP_CLKDIV_CON4 */
+#define RG_DIVCKS_PWD_NCP_ST_SEL_SFT 0
+#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK 0x3
+#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK_SFT (0x3 << 0)
+
+/* MT6358_AUD_TOP_MON_CON0 */
+#define RG_AUD_TOP_MON_SEL_SFT 0
+#define RG_AUD_TOP_MON_SEL_MASK 0x7
+#define RG_AUD_TOP_MON_SEL_MASK_SFT (0x7 << 0)
+#define RG_AUD_CLK_INT_MON_FLAG_SEL_SFT 3
+#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK 0xff
+#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK_SFT (0xff << 3)
+#define RG_AUD_CLK_INT_MON_FLAG_EN_SFT 11
+#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK 0x1
+#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK_SFT (0x1 << 11)
+
+/* MT6358_AUDIO_DIG_DSN_ID */
+#define AUDIO_DIG_ANA_ID_SFT 0
+#define AUDIO_DIG_ANA_ID_MASK 0xff
+#define AUDIO_DIG_ANA_ID_MASK_SFT (0xff << 0)
+#define AUDIO_DIG_DIG_ID_SFT 8
+#define AUDIO_DIG_DIG_ID_MASK 0xff
+#define AUDIO_DIG_DIG_ID_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDIO_DIG_DSN_REV0 */
+#define AUDIO_DIG_ANA_MINOR_REV_SFT 0
+#define AUDIO_DIG_ANA_MINOR_REV_MASK 0xf
+#define AUDIO_DIG_ANA_MINOR_REV_MASK_SFT (0xf << 0)
+#define AUDIO_DIG_ANA_MAJOR_REV_SFT 4
+#define AUDIO_DIG_ANA_MAJOR_REV_MASK 0xf
+#define AUDIO_DIG_ANA_MAJOR_REV_MASK_SFT (0xf << 4)
+#define AUDIO_DIG_DIG_MINOR_REV_SFT 8
+#define AUDIO_DIG_DIG_MINOR_REV_MASK 0xf
+#define AUDIO_DIG_DIG_MINOR_REV_MASK_SFT (0xf << 8)
+#define AUDIO_DIG_DIG_MAJOR_REV_SFT 12
+#define AUDIO_DIG_DIG_MAJOR_REV_MASK 0xf
+#define AUDIO_DIG_DIG_MAJOR_REV_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDIO_DIG_DSN_DBI */
+#define AUDIO_DIG_DSN_CBS_SFT 0
+#define AUDIO_DIG_DSN_CBS_MASK 0x3
+#define AUDIO_DIG_DSN_CBS_MASK_SFT (0x3 << 0)
+#define AUDIO_DIG_DSN_BIX_SFT 2
+#define AUDIO_DIG_DSN_BIX_MASK 0x3
+#define AUDIO_DIG_DSN_BIX_MASK_SFT (0x3 << 2)
+#define AUDIO_DIG_ESP_SFT 8
+#define AUDIO_DIG_ESP_MASK 0xff
+#define AUDIO_DIG_ESP_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDIO_DIG_DSN_DXI */
+#define AUDIO_DIG_DSN_FPI_SFT 0
+#define AUDIO_DIG_DSN_FPI_MASK 0xff
+#define AUDIO_DIG_DSN_FPI_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_UL_DL_CON0 */
+#define AFE_UL_LR_SWAP_SFT 15
+#define AFE_UL_LR_SWAP_MASK 0x1
+#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15)
+#define AFE_DL_LR_SWAP_SFT 14
+#define AFE_DL_LR_SWAP_MASK 0x1
+#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_DL_SRC2_CON0_L */
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_UL_SRC_CON0_H */
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11)
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8)
+#define C_TWO_DIGITAL_MIC_CTL_SFT 7
+#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7)
+
+/* MT6358_AFE_UL_SRC_CON0_L */
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_TOP_CON0 */
+#define MTKAIF_SINE_ON_SFT 2
+#define MTKAIF_SINE_ON_MASK 0x1
+#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2)
+#define UL_SINE_ON_SFT 1
+#define UL_SINE_ON_MASK 0x1
+#define UL_SINE_ON_MASK_SFT (0x1 << 1)
+#define DL_SINE_ON_SFT 0
+#define DL_SINE_ON_MASK 0x1
+#define DL_SINE_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AUDIO_TOP_CON0 */
+#define PDN_AFE_CTL_SFT 7
+#define PDN_AFE_CTL_MASK 0x1
+#define PDN_AFE_CTL_MASK_SFT (0x1 << 7)
+#define PDN_DAC_CTL_SFT 6
+#define PDN_DAC_CTL_MASK 0x1
+#define PDN_DAC_CTL_MASK_SFT (0x1 << 6)
+#define PDN_ADC_CTL_SFT 5
+#define PDN_ADC_CTL_MASK 0x1
+#define PDN_ADC_CTL_MASK_SFT (0x1 << 5)
+#define PDN_I2S_DL_CTL_SFT 3
+#define PDN_I2S_DL_CTL_MASK 0x1
+#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3)
+#define PWR_CLK_DIS_CTL_SFT 2
+#define PWR_CLK_DIS_CTL_MASK 0x1
+#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2)
+#define PDN_AFE_TESTMODEL_CTL_SFT 1
+#define PDN_AFE_TESTMODEL_CTL_MASK 0x1
+#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1)
+#define PDN_RESERVED_SFT 0
+#define PDN_RESERVED_MASK 0x1
+#define PDN_RESERVED_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_MON_DEBUG0 */
+#define AUDIO_SYS_TOP_MON_SWAP_SFT 14
+#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3
+#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14)
+#define AUDIO_SYS_TOP_MON_SEL_SFT 8
+#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f
+#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8)
+#define AFE_MON_SEL_SFT 0
+#define AFE_MON_SEL_MASK 0xff
+#define AFE_MON_SEL_MASK_SFT (0xff << 0)
+
+/* MT6358_AFUNC_AUD_CON0 */
+#define CCI_AUD_ANACK_SEL_SFT 15
+#define CCI_AUD_ANACK_SEL_MASK 0x1
+#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15)
+#define CCI_AUDIO_FIFO_WPTR_SFT 12
+#define CCI_AUDIO_FIFO_WPTR_MASK 0x7
+#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12)
+#define CCI_SCRAMBLER_CG_EN_SFT 11
+#define CCI_SCRAMBLER_CG_EN_MASK 0x1
+#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11)
+#define CCI_LCH_INV_SFT 10
+#define CCI_LCH_INV_MASK 0x1
+#define CCI_LCH_INV_MASK_SFT (0x1 << 10)
+#define CCI_RAND_EN_SFT 9
+#define CCI_RAND_EN_MASK 0x1
+#define CCI_RAND_EN_MASK_SFT (0x1 << 9)
+#define CCI_SPLT_SCRMB_CLK_ON_SFT 8
+#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8)
+#define CCI_SPLT_SCRMB_ON_SFT 7
+#define CCI_SPLT_SCRMB_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7)
+#define CCI_AUD_IDAC_TEST_EN_SFT 6
+#define CCI_AUD_IDAC_TEST_EN_MASK 0x1
+#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6)
+#define CCI_ZERO_PAD_DISABLE_SFT 5
+#define CCI_ZERO_PAD_DISABLE_MASK 0x1
+#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5)
+#define CCI_AUD_SPLIT_TEST_EN_SFT 4
+#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1
+#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4)
+#define CCI_AUD_SDM_MUTEL_SFT 3
+#define CCI_AUD_SDM_MUTEL_MASK 0x1
+#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3)
+#define CCI_AUD_SDM_MUTER_SFT 2
+#define CCI_AUD_SDM_MUTER_MASK 0x1
+#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2)
+#define CCI_AUD_SDM_7BIT_SEL_SFT 1
+#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1
+#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1)
+#define CCI_SCRAMBLER_EN_SFT 0
+#define CCI_SCRAMBLER_EN_MASK 0x1
+#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_CON1 */
+#define AUD_SDM_TEST_L_SFT 8
+#define AUD_SDM_TEST_L_MASK 0xff
+#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8)
+#define AUD_SDM_TEST_R_SFT 0
+#define AUD_SDM_TEST_R_MASK 0xff
+#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0)
+
+/* MT6358_AFUNC_AUD_CON2 */
+#define CCI_AUD_DAC_ANA_MUTE_SFT 7
+#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1
+#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7)
+#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6)
+#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4)
+#define CCI_AUDIO_FIFO_ENABLE_SFT 3
+#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1
+#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3)
+#define CCI_ACD_MODE_SFT 2
+#define CCI_ACD_MODE_MASK 0x1
+#define CCI_ACD_MODE_MASK_SFT (0x1 << 2)
+#define CCI_AFIFO_CLK_PWDB_SFT 1
+#define CCI_AFIFO_CLK_PWDB_MASK 0x1
+#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1)
+#define CCI_ACD_FUNC_RSTB_SFT 0
+#define CCI_ACD_FUNC_RSTB_MASK 0x1
+#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_CON3 */
+#define SDM_ANA13M_TESTCK_SEL_SFT 15
+#define SDM_ANA13M_TESTCK_SEL_MASK 0x1
+#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15)
+#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12)
+#define SDM_TESTCK_SRC_SEL_SFT 8
+#define SDM_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8)
+#define DIGMIC_TESTCK_SRC_SEL_SFT 4
+#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7
+#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4)
+#define DIGMIC_TESTCK_SEL_SFT 0
+#define DIGMIC_TESTCK_SEL_MASK 0x1
+#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_CON4 */
+#define UL_FIFO_WCLK_INV_SFT 8
+#define UL_FIFO_WCLK_INV_MASK 0x1
+#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8)
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6)
+#define UL_FIFO_WDATA_TESTEN_SFT 5
+#define UL_FIFO_WDATA_TESTEN_MASK 0x1
+#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5)
+#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0)
+
+/* MT6358_AFUNC_AUD_CON5 */
+#define R_AUD_DAC_POS_LARGE_MONO_SFT 8
+#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8)
+#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0)
+
+/* MT6358_AFUNC_AUD_CON6 */
+#define R_AUD_DAC_POS_SMALL_MONO_SFT 12
+#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12)
+#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8)
+#define R_AUD_DAC_POS_TINY_MONO_SFT 6
+#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6)
+#define R_AUD_DAC_NEG_TINY_MONO_SFT 4
+#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4)
+#define R_AUD_DAC_MONO_SEL_SFT 3
+#define R_AUD_DAC_MONO_SEL_MASK 0x1
+#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3)
+#define R_AUD_DAC_SW_RSTB_SFT 0
+#define R_AUD_DAC_SW_RSTB_MASK 0x1
+#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFUNC_AUD_MON0 */
+#define AUD_SCR_OUT_L_SFT 8
+#define AUD_SCR_OUT_L_MASK 0xff
+#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8)
+#define AUD_SCR_OUT_R_SFT 0
+#define AUD_SCR_OUT_R_MASK 0xff
+#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDRC_TUNE_MON0 */
+#define ASYNC_TEST_OUT_BCK_SFT 15
+#define ASYNC_TEST_OUT_BCK_MASK 0x1
+#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15)
+#define RGS_AUDRCTUNE1READ_SFT 8
+#define RGS_AUDRCTUNE1READ_MASK 0x1f
+#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8)
+#define RGS_AUDRCTUNE0READ_SFT 0
+#define RGS_AUDRCTUNE0READ_MASK 0x1f
+#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 */
+#define AFE_RESERVED_SFT 1
+#define AFE_RESERVED_MASK 0x7fff
+#define AFE_RESERVED_MASK_SFT (0x7fff << 1)
+#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */
+#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1)
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON0 */
+#define MTKAIFTX_V3_SYNC_OUT_SFT 14
+#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1
+#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 14)
+#define MTKAIFTX_V3_SDATA_OUT2_SFT 13
+#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13)
+#define MTKAIFTX_V3_SDATA_OUT1_SFT 12
+#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_FIFO_STATUS_SFT 0
+#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff
+#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON1 */
+#define MTKAIFRX_V3_SYNC_IN_SFT 14
+#define MTKAIFRX_V3_SYNC_IN_MASK 0x1
+#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 14)
+#define MTKAIFRX_V3_SDATA_IN2_SFT 13
+#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13)
+#define MTKAIFRX_V3_SDATA_IN1_SFT 12
+#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11)
+#define MTKAIF_RXIF_INVALID_FLAG_SFT 8
+#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1
+#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8)
+#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON2 */
+#define MTKAIF_TXIF_IN_CH2_SFT 8
+#define MTKAIF_TXIF_IN_CH2_MASK 0xff
+#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_TXIF_IN_CH1_SFT 0
+#define MTKAIF_TXIF_IN_CH1_MASK 0xff
+#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_MON3 */
+#define MTKAIF_RXIF_OUT_CH2_SFT 8
+#define MTKAIF_RXIF_OUT_CH2_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_RXIF_OUT_CH1_SFT 0
+#define MTKAIF_RXIF_OUT_CH1_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_CFG0 */
+#define RG_MTKAIF_RXIF_CLKINV_SFT 15
+#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1
+#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15)
+#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8)
+#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6)
+#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5)
+#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1)
+#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG0 */
+#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3)
+#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG1 */
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG2 */
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12)
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0)
+
+/* MT6358_AFE_ADDA_MTKAIF_RX_CFG3 */
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7)
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3)
+
+/* MT6358_AFE_ADDA_MTKAIF_TX_CFG1 */
+#define RG_MTKAIF_SYNC_WORD2_SFT 4
+#define RG_MTKAIF_SYNC_WORD2_MASK 0x7
+#define RG_MTKAIF_SYNC_WORD2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_SYNC_WORD1_SFT 0
+#define RG_MTKAIF_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF_SYNC_WORD1_MASK_SFT (0x7 << 0)
+
+/* MT6358_AFE_SGEN_CFG0 */
+#define SGEN_AMP_DIV_CH1_CTL_SFT 12
+#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf
+#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12)
+#define SGEN_DAC_EN_CTL_SFT 7
+#define SGEN_DAC_EN_CTL_MASK 0x1
+#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7)
+#define SGEN_MUTE_SW_CTL_SFT 6
+#define SGEN_MUTE_SW_CTL_MASK 0x1
+#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6)
+#define R_AUD_SDM_MUTE_L_SFT 5
+#define R_AUD_SDM_MUTE_L_MASK 0x1
+#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5)
+#define R_AUD_SDM_MUTE_R_SFT 4
+#define R_AUD_SDM_MUTE_R_MASK 0x1
+#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4)
+
+/* MT6358_AFE_SGEN_CFG1 */
+#define C_SGEN_RCH_INV_5BIT_SFT 15
+#define C_SGEN_RCH_INV_5BIT_MASK 0x1
+#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15)
+#define C_SGEN_RCH_INV_8BIT_SFT 14
+#define C_SGEN_RCH_INV_8BIT_MASK 0x1
+#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14)
+#define SGEN_FREQ_DIV_CH1_CTL_SFT 0
+#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0)
+
+/* MT6358_AFE_ADC_ASYNC_FIFO_CFG */
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5)
+#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1)
+
+/* MT6358_AFE_DCCLK_CFG0 */
+#define DCCLK_DIV_SFT 5
+#define DCCLK_DIV_MASK 0x7ff
+#define DCCLK_DIV_MASK_SFT (0x7ff << 5)
+#define DCCLK_INV_SFT 4
+#define DCCLK_INV_MASK 0x1
+#define DCCLK_INV_MASK_SFT (0x1 << 4)
+#define DCCLK_PDN_SFT 1
+#define DCCLK_PDN_MASK 0x1
+#define DCCLK_PDN_MASK_SFT (0x1 << 1)
+#define DCCLK_GEN_ON_SFT 0
+#define DCCLK_GEN_ON_MASK 0x1
+#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_DCCLK_CFG1 */
+#define RESYNC_SRC_SEL_SFT 10
+#define RESYNC_SRC_SEL_MASK 0x3
+#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10)
+#define RESYNC_SRC_CK_INV_SFT 9
+#define RESYNC_SRC_CK_INV_MASK 0x1
+#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9)
+#define DCCLK_RESYNC_BYPASS_SFT 8
+#define DCCLK_RESYNC_BYPASS_MASK 0x1
+#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8)
+#define DCCLK_PHASE_SEL_SFT 4
+#define DCCLK_PHASE_SEL_MASK 0xf
+#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4)
+
+/* MT6358_AUDIO_DIG_CFG */
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15)
+#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8)
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7)
+#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0)
+
+/* MT6358_AFE_AUD_PAD_TOP */
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12)
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11)
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8)
+
+/* MT6358_AFE_AUD_PAD_TOP_MON */
+#define ADDA_AUD_PAD_TOP_MON_SFT 0
+#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_AUD_PAD_TOP_MON1 */
+#define ADDA_AUD_PAD_TOP_MON1_SFT 0
+#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_DL_NLE_CFG */
+#define NLE_RCH_HPGAIN_SEL_SFT 10
+#define NLE_RCH_HPGAIN_SEL_MASK 0x1
+#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10)
+#define NLE_RCH_CH_SEL_SFT 9
+#define NLE_RCH_CH_SEL_MASK 0x1
+#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9)
+#define NLE_RCH_ON_SFT 8
+#define NLE_RCH_ON_MASK 0x1
+#define NLE_RCH_ON_MASK_SFT (0x1 << 8)
+#define NLE_LCH_HPGAIN_SEL_SFT 2
+#define NLE_LCH_HPGAIN_SEL_MASK 0x1
+#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2)
+#define NLE_LCH_CH_SEL_SFT 1
+#define NLE_LCH_CH_SEL_MASK 0x1
+#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1)
+#define NLE_LCH_ON_SFT 0
+#define NLE_LCH_ON_MASK 0x1
+#define NLE_LCH_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_DL_NLE_MON */
+#define NLE_MONITOR_SFT 0
+#define NLE_MONITOR_MASK 0x3fff
+#define NLE_MONITOR_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_CG_EN_MON */
+#define CK_CG_EN_MON_SFT 0
+#define CK_CG_EN_MON_MASK 0x3f
+#define CK_CG_EN_MON_MASK_SFT (0x3f << 0)
+
+/* MT6358_AFE_VOW_TOP */
+#define PDN_VOW_SFT 15
+#define PDN_VOW_MASK 0x1
+#define PDN_VOW_MASK_SFT (0x1 << 15)
+#define VOW_1P6M_800K_SEL_SFT 14
+#define VOW_1P6M_800K_SEL_MASK 0x1
+#define VOW_1P6M_800K_SEL_MASK_SFT (0x1 << 14)
+#define VOW_DIGMIC_ON_SFT 13
+#define VOW_DIGMIC_ON_MASK 0x1
+#define VOW_DIGMIC_ON_MASK_SFT (0x1 << 13)
+#define VOW_CK_DIV_RST_SFT 12
+#define VOW_CK_DIV_RST_MASK 0x1
+#define VOW_CK_DIV_RST_MASK_SFT (0x1 << 12)
+#define VOW_ON_SFT 11
+#define VOW_ON_MASK 0x1
+#define VOW_ON_MASK_SFT (0x1 << 11)
+#define VOW_DIGMIC_CK_PHASE_SEL_SFT 8
+#define VOW_DIGMIC_CK_PHASE_SEL_MASK 0x7
+#define VOW_DIGMIC_CK_PHASE_SEL_MASK_SFT (0x7 << 8)
+#define MAIN_DMIC_CK_VOW_SEL_SFT 7
+#define MAIN_DMIC_CK_VOW_SEL_MASK 0x1
+#define MAIN_DMIC_CK_VOW_SEL_MASK_SFT (0x1 << 7)
+#define VOW_SDM_3_LEVEL_SFT 6
+#define VOW_SDM_3_LEVEL_MASK 0x1
+#define VOW_SDM_3_LEVEL_MASK_SFT (0x1 << 6)
+#define VOW_LOOP_BACK_MODE_SFT 5
+#define VOW_LOOP_BACK_MODE_MASK 0x1
+#define VOW_LOOP_BACK_MODE_MASK_SFT (0x1 << 5)
+#define VOW_INTR_SOURCE_SEL_SFT 4
+#define VOW_INTR_SOURCE_SEL_MASK 0x1
+#define VOW_INTR_SOURCE_SEL_MASK_SFT (0x1 << 4)
+#define VOW_INTR_CLR_SFT 3
+#define VOW_INTR_CLR_MASK 0x1
+#define VOW_INTR_CLR_MASK_SFT (0x1 << 3)
+#define S_N_VALUE_RST_SFT 2
+#define S_N_VALUE_RST_MASK 0x1
+#define S_N_VALUE_RST_MASK_SFT (0x1 << 2)
+#define SAMPLE_BASE_MODE_SFT 1
+#define SAMPLE_BASE_MODE_MASK 0x1
+#define SAMPLE_BASE_MODE_MASK_SFT (0x1 << 1)
+#define VOW_INTR_FLAG_SFT 0
+#define VOW_INTR_FLAG_MASK 0x1
+#define VOW_INTR_FLAG_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_VOW_CFG0 */
+#define AMPREF_SFT 0
+#define AMPREF_MASK 0xffff
+#define AMPREF_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_CFG1 */
+#define TIMERINI_SFT 0
+#define TIMERINI_MASK 0xffff
+#define TIMERINI_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_CFG2 */
+#define B_DEFAULT_SFT 12
+#define B_DEFAULT_MASK 0x7
+#define B_DEFAULT_MASK_SFT (0x7 << 12)
+#define A_DEFAULT_SFT 8
+#define A_DEFAULT_MASK 0x7
+#define A_DEFAULT_MASK_SFT (0x7 << 8)
+#define B_INI_SFT 4
+#define B_INI_MASK 0x7
+#define B_INI_MASK_SFT (0x7 << 4)
+#define A_INI_SFT 0
+#define A_INI_MASK 0x7
+#define A_INI_MASK_SFT (0x7 << 0)
+
+/* MT6358_AFE_VOW_CFG3 */
+#define K_BETA_RISE_SFT 12
+#define K_BETA_RISE_MASK 0xf
+#define K_BETA_RISE_MASK_SFT (0xf << 12)
+#define K_BETA_FALL_SFT 8
+#define K_BETA_FALL_MASK 0xf
+#define K_BETA_FALL_MASK_SFT (0xf << 8)
+#define K_ALPHA_RISE_SFT 4
+#define K_ALPHA_RISE_MASK 0xf
+#define K_ALPHA_RISE_MASK_SFT (0xf << 4)
+#define K_ALPHA_FALL_SFT 0
+#define K_ALPHA_FALL_MASK 0xf
+#define K_ALPHA_FALL_MASK_SFT (0xf << 0)
+
+/* MT6358_AFE_VOW_CFG4 */
+#define VOW_TXIF_SCK_INV_SFT 15
+#define VOW_TXIF_SCK_INV_MASK 0x1
+#define VOW_TXIF_SCK_INV_MASK_SFT (0x1 << 15)
+#define VOW_ADC_TESTCK_SRC_SEL_SFT 12
+#define VOW_ADC_TESTCK_SRC_SEL_MASK 0x7
+#define VOW_ADC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12)
+#define VOW_ADC_TESTCK_SEL_SFT 11
+#define VOW_ADC_TESTCK_SEL_MASK 0x1
+#define VOW_ADC_TESTCK_SEL_MASK_SFT (0x1 << 11)
+#define VOW_ADC_CLK_INV_SFT 10
+#define VOW_ADC_CLK_INV_MASK 0x1
+#define VOW_ADC_CLK_INV_MASK_SFT (0x1 << 10)
+#define VOW_TXIF_MONO_SFT 9
+#define VOW_TXIF_MONO_MASK 0x1
+#define VOW_TXIF_MONO_MASK_SFT (0x1 << 9)
+#define VOW_TXIF_SCK_DIV_SFT 4
+#define VOW_TXIF_SCK_DIV_MASK 0x1f
+#define VOW_TXIF_SCK_DIV_MASK_SFT (0x1f << 4)
+#define K_GAMMA_SFT 0
+#define K_GAMMA_MASK 0xf
+#define K_GAMMA_MASK_SFT (0xf << 0)
+
+/* MT6358_AFE_VOW_CFG5 */
+#define N_MIN_SFT 0
+#define N_MIN_MASK 0xffff
+#define N_MIN_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_CFG6 */
+#define RG_WINDOW_SIZE_SEL_SFT 12
+#define RG_WINDOW_SIZE_SEL_MASK 0x1
+#define RG_WINDOW_SIZE_SEL_MASK_SFT (0x1 << 12)
+#define RG_FLR_BYPASS_SFT 11
+#define RG_FLR_BYPASS_MASK 0x1
+#define RG_FLR_BYPASS_MASK_SFT (0x1 << 11)
+#define RG_FLR_RATIO_SFT 8
+#define RG_FLR_RATIO_MASK 0x7
+#define RG_FLR_RATIO_MASK_SFT (0x7 << 8)
+#define RG_BUCK_DVFS_DONE_SW_CTL_SFT 7
+#define RG_BUCK_DVFS_DONE_SW_CTL_MASK 0x1
+#define RG_BUCK_DVFS_DONE_SW_CTL_MASK_SFT (0x1 << 7)
+#define RG_BUCK_DVFS_DONE_HW_MODE_SFT 6
+#define RG_BUCK_DVFS_DONE_HW_MODE_MASK 0x1
+#define RG_BUCK_DVFS_DONE_HW_MODE_MASK_SFT (0x1 << 6)
+#define RG_BUCK_DVFS_HW_CNT_THR_SFT 0
+#define RG_BUCK_DVFS_HW_CNT_THR_MASK 0x3f
+#define RG_BUCK_DVFS_HW_CNT_THR_MASK_SFT (0x3f << 0)
+
+/* MT6358_AFE_VOW_MON0 */
+#define VOW_DOWNCNT_SFT 0
+#define VOW_DOWNCNT_MASK 0xffff
+#define VOW_DOWNCNT_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON1 */
+#define K_TMP_MON_SFT 10
+#define K_TMP_MON_MASK 0xf
+#define K_TMP_MON_MASK_SFT (0xf << 10)
+#define SLT_COUNTER_MON_SFT 7
+#define SLT_COUNTER_MON_MASK 0x7
+#define SLT_COUNTER_MON_MASK_SFT (0x7 << 7)
+#define VOW_B_SFT 4
+#define VOW_B_MASK 0x7
+#define VOW_B_MASK_SFT (0x7 << 4)
+#define VOW_A_SFT 1
+#define VOW_A_MASK 0x7
+#define VOW_A_MASK_SFT (0x7 << 1)
+#define SECOND_CNT_START_SFT 0
+#define SECOND_CNT_START_MASK 0x1
+#define SECOND_CNT_START_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_VOW_MON2 */
+#define VOW_S_L_SFT 0
+#define VOW_S_L_MASK 0xffff
+#define VOW_S_L_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON3 */
+#define VOW_S_H_SFT 0
+#define VOW_S_H_MASK 0xffff
+#define VOW_S_H_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON4 */
+#define VOW_N_L_SFT 0
+#define VOW_N_L_MASK 0xffff
+#define VOW_N_L_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_MON5 */
+#define VOW_N_H_SFT 0
+#define VOW_N_H_MASK 0xffff
+#define VOW_N_H_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_SN_INI_CFG */
+#define VOW_SN_INI_CFG_EN_SFT 15
+#define VOW_SN_INI_CFG_EN_MASK 0x1
+#define VOW_SN_INI_CFG_EN_MASK_SFT (0x1 << 15)
+#define VOW_SN_INI_CFG_VAL_SFT 0
+#define VOW_SN_INI_CFG_VAL_MASK 0x7fff
+#define VOW_SN_INI_CFG_VAL_MASK_SFT (0x7fff << 0)
+
+/* MT6358_AFE_VOW_TGEN_CFG0 */
+#define VOW_TGEN_EN_SFT 15
+#define VOW_TGEN_EN_MASK 0x1
+#define VOW_TGEN_EN_MASK_SFT (0x1 << 15)
+#define VOW_TGEN_MUTE_SW_SFT 14
+#define VOW_TGEN_MUTE_SW_MASK 0x1
+#define VOW_TGEN_MUTE_SW_MASK_SFT (0x1 << 14)
+#define VOW_TGEN_FREQ_DIV_SFT 0
+#define VOW_TGEN_FREQ_DIV_MASK 0x3fff
+#define VOW_TGEN_FREQ_DIV_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_POSDIV_CFG0 */
+#define BUCK_DVFS_DONE_SFT 15
+#define BUCK_DVFS_DONE_MASK 0x1
+#define BUCK_DVFS_DONE_MASK_SFT (0x1 << 15)
+#define VOW_32K_MODE_SFT 13
+#define VOW_32K_MODE_MASK 0x1
+#define VOW_32K_MODE_MASK_SFT (0x1 << 13)
+#define RG_BUCK_CLK_DIV_SFT 8
+#define RG_BUCK_CLK_DIV_MASK 0x1f
+#define RG_BUCK_CLK_DIV_MASK_SFT (0x1f << 8)
+#define RG_A1P6M_EN_SEL_SFT 7
+#define RG_A1P6M_EN_SEL_MASK 0x1
+#define RG_A1P6M_EN_SEL_MASK_SFT (0x1 << 7)
+#define VOW_CLK_SEL_SFT 6
+#define VOW_CLK_SEL_MASK 0x1
+#define VOW_CLK_SEL_MASK_SFT (0x1 << 6)
+#define VOW_INTR_SW_MODE_SFT 5
+#define VOW_INTR_SW_MODE_MASK 0x1
+#define VOW_INTR_SW_MODE_MASK_SFT (0x1 << 5)
+#define VOW_INTR_SW_VAL_SFT 4
+#define VOW_INTR_SW_VAL_MASK 0x1
+#define VOW_INTR_SW_VAL_MASK_SFT (0x1 << 4)
+#define VOW_CIC_MODE_SEL_SFT 2
+#define VOW_CIC_MODE_SEL_MASK 0x3
+#define VOW_CIC_MODE_SEL_MASK_SFT (0x3 << 2)
+#define RG_VOW_POSDIV_SFT 0
+#define RG_VOW_POSDIV_MASK 0x3
+#define RG_VOW_POSDIV_MASK_SFT (0x3 << 0)
+
+/* MT6358_AFE_VOW_HPF_CFG0 */
+#define VOW_HPF_DC_TEST_SFT 12
+#define VOW_HPF_DC_TEST_MASK 0xf
+#define VOW_HPF_DC_TEST_MASK_SFT (0xf << 12)
+#define VOW_IRQ_LATCH_SNR_EN_SFT 10
+#define VOW_IRQ_LATCH_SNR_EN_MASK 0x1
+#define VOW_IRQ_LATCH_SNR_EN_MASK_SFT (0x1 << 10)
+#define VOW_DMICCLK_PDN_SFT 9
+#define VOW_DMICCLK_PDN_MASK 0x1
+#define VOW_DMICCLK_PDN_MASK_SFT (0x1 << 9)
+#define VOW_POSDIVCLK_PDN_SFT 8
+#define VOW_POSDIVCLK_PDN_MASK 0x1
+#define VOW_POSDIVCLK_PDN_MASK_SFT (0x1 << 8)
+#define RG_BASELINE_ALPHA_ORDER_SFT 4
+#define RG_BASELINE_ALPHA_ORDER_MASK 0xf
+#define RG_BASELINE_ALPHA_ORDER_MASK_SFT (0xf << 4)
+#define RG_MTKAIF_HPF_BYPASS_SFT 2
+#define RG_MTKAIF_HPF_BYPASS_MASK 0x1
+#define RG_MTKAIF_HPF_BYPASS_MASK_SFT (0x1 << 2)
+#define RG_SNRDET_HPF_BYPASS_SFT 1
+#define RG_SNRDET_HPF_BYPASS_MASK 0x1
+#define RG_SNRDET_HPF_BYPASS_MASK_SFT (0x1 << 1)
+#define RG_HPF_ON_SFT 0
+#define RG_HPF_ON_MASK 0x1
+#define RG_HPF_ON_MASK_SFT (0x1 << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG0 */
+#define RG_PERIODIC_EN_SFT 15
+#define RG_PERIODIC_EN_MASK 0x1
+#define RG_PERIODIC_EN_MASK_SFT (0x1 << 15)
+#define RG_PERIODIC_CNT_CLR_SFT 14
+#define RG_PERIODIC_CNT_CLR_MASK 0x1
+#define RG_PERIODIC_CNT_CLR_MASK_SFT (0x1 << 14)
+#define RG_PERIODIC_CNT_PERIOD_SFT 0
+#define RG_PERIODIC_CNT_PERIOD_MASK 0x3fff
+#define RG_PERIODIC_CNT_PERIOD_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG1 */
+#define RG_PERIODIC_CNT_SET_SFT 15
+#define RG_PERIODIC_CNT_SET_MASK 0x1
+#define RG_PERIODIC_CNT_SET_MASK_SFT (0x1 << 15)
+#define RG_PERIODIC_CNT_PAUSE_SFT 14
+#define RG_PERIODIC_CNT_PAUSE_MASK 0x1
+#define RG_PERIODIC_CNT_PAUSE_MASK_SFT (0x1 << 14)
+#define RG_PERIODIC_CNT_SET_VALUE_SFT 0
+#define RG_PERIODIC_CNT_SET_VALUE_MASK 0x3fff
+#define RG_PERIODIC_CNT_SET_VALUE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG2 */
+#define AUDPREAMPLON_PERIODIC_MODE_SFT 15
+#define AUDPREAMPLON_PERIODIC_MODE_MASK 0x1
+#define AUDPREAMPLON_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLON_PERIODIC_INVERSE_SFT 14
+#define AUDPREAMPLON_PERIODIC_INVERSE_MASK 0x1
+#define AUDPREAMPLON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPREAMPLON_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG3 */
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_SFT 15
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK 0x1
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_SFT 14
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK 0x1
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG4 */
+#define AUDADCLPWRUP_PERIODIC_MODE_SFT 15
+#define AUDADCLPWRUP_PERIODIC_MODE_MASK 0x1
+#define AUDADCLPWRUP_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDADCLPWRUP_PERIODIC_INVERSE_SFT 14
+#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK 0x1
+#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_SFT 0
+#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG5 */
+#define AUDGLBVOWLPWEN_PERIODIC_MODE_SFT 15
+#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK 0x1
+#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_SFT 14
+#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK 0x1
+#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_SFT 0
+#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG6 */
+#define AUDDIGMICEN_PERIODIC_MODE_SFT 15
+#define AUDDIGMICEN_PERIODIC_MODE_MASK 0x1
+#define AUDDIGMICEN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDDIGMICEN_PERIODIC_INVERSE_SFT 14
+#define AUDDIGMICEN_PERIODIC_INVERSE_MASK 0x1
+#define AUDDIGMICEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDDIGMICEN_PERIODIC_ON_CYCLE_SFT 0
+#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG7 */
+#define AUDPWDBMICBIAS0_PERIODIC_MODE_SFT 15
+#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK 0x1
+#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_SFT 14
+#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK 0x1
+#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG8 */
+#define AUDPWDBMICBIAS1_PERIODIC_MODE_SFT 15
+#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK 0x1
+#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_SFT 14
+#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK 0x1
+#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_SFT 0
+#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG9 */
+#define XO_VOW_CK_EN_PERIODIC_MODE_SFT 15
+#define XO_VOW_CK_EN_PERIODIC_MODE_MASK 0x1
+#define XO_VOW_CK_EN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define XO_VOW_CK_EN_PERIODIC_INVERSE_SFT 14
+#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK 0x1
+#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_SFT 0
+#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG10 */
+#define AUDGLB_PWRDN_PERIODIC_MODE_SFT 15
+#define AUDGLB_PWRDN_PERIODIC_MODE_MASK 0x1
+#define AUDGLB_PWRDN_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define AUDGLB_PWRDN_PERIODIC_INVERSE_SFT 14
+#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK 0x1
+#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_SFT 0
+#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG11 */
+#define VOW_ON_PERIODIC_MODE_SFT 15
+#define VOW_ON_PERIODIC_MODE_MASK 0x1
+#define VOW_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define VOW_ON_PERIODIC_INVERSE_SFT 14
+#define VOW_ON_PERIODIC_INVERSE_MASK 0x1
+#define VOW_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define VOW_ON_PERIODIC_ON_CYCLE_SFT 0
+#define VOW_ON_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define VOW_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG12 */
+#define DMIC_ON_PERIODIC_MODE_SFT 15
+#define DMIC_ON_PERIODIC_MODE_MASK 0x1
+#define DMIC_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define DMIC_ON_PERIODIC_INVERSE_SFT 14
+#define DMIC_ON_PERIODIC_INVERSE_MASK 0x1
+#define DMIC_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14)
+#define DMIC_ON_PERIODIC_ON_CYCLE_SFT 0
+#define DMIC_ON_PERIODIC_ON_CYCLE_MASK 0x3fff
+#define DMIC_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG13 */
+#define PDN_VOW_F32K_CK_SFT 15
+#define PDN_VOW_F32K_CK_MASK 0x1
+#define PDN_VOW_F32K_CK_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG14 */
+#define VOW_SNRDET_PERIODIC_CFG_SFT 15
+#define VOW_SNRDET_PERIODIC_CFG_MASK 0x1
+#define VOW_SNRDET_PERIODIC_CFG_MASK_SFT (0x1 << 15)
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG15 */
+#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG16 */
+#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG17 */
+#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG18 */
+#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG19 */
+#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG20 */
+#define CLKSQ_EN_VOW_PERIODIC_MODE_SFT 15
+#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK 0x1
+#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK_SFT (0x1 << 15)
+#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_SFT 0
+#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG21 */
+#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_SFT 0
+#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG22 */
+#define VOW_ON_PERIODIC_OFF_CYCLE_SFT 0
+#define VOW_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define VOW_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_CFG23 */
+#define DMIC_ON_PERIODIC_OFF_CYCLE_SFT 0
+#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff
+#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_MON0 */
+#define VOW_PERIODIC_MON_SFT 0
+#define VOW_PERIODIC_MON_MASK 0xffff
+#define VOW_PERIODIC_MON_MASK_SFT (0xffff << 0)
+
+/* MT6358_AFE_VOW_PERIODIC_MON1 */
+#define VOW_PERIODIC_COUNT_MON_SFT 0
+#define VOW_PERIODIC_COUNT_MON_MASK 0xffff
+#define VOW_PERIODIC_COUNT_MON_MASK_SFT (0xffff << 0)
+
+/* MT6358_AUDENC_DSN_ID */
+#define AUDENC_ANA_ID_SFT 0
+#define AUDENC_ANA_ID_MASK 0xff
+#define AUDENC_ANA_ID_MASK_SFT (0xff << 0)
+#define AUDENC_DIG_ID_SFT 8
+#define AUDENC_DIG_ID_MASK 0xff
+#define AUDENC_DIG_ID_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDENC_DSN_REV0 */
+#define AUDENC_ANA_MINOR_REV_SFT 0
+#define AUDENC_ANA_MINOR_REV_MASK 0xf
+#define AUDENC_ANA_MINOR_REV_MASK_SFT (0xf << 0)
+#define AUDENC_ANA_MAJOR_REV_SFT 4
+#define AUDENC_ANA_MAJOR_REV_MASK 0xf
+#define AUDENC_ANA_MAJOR_REV_MASK_SFT (0xf << 4)
+#define AUDENC_DIG_MINOR_REV_SFT 8
+#define AUDENC_DIG_MINOR_REV_MASK 0xf
+#define AUDENC_DIG_MINOR_REV_MASK_SFT (0xf << 8)
+#define AUDENC_DIG_MAJOR_REV_SFT 12
+#define AUDENC_DIG_MAJOR_REV_MASK 0xf
+#define AUDENC_DIG_MAJOR_REV_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDENC_DSN_DBI */
+#define AUDENC_DSN_CBS_SFT 0
+#define AUDENC_DSN_CBS_MASK 0x3
+#define AUDENC_DSN_CBS_MASK_SFT (0x3 << 0)
+#define AUDENC_DSN_BIX_SFT 2
+#define AUDENC_DSN_BIX_MASK 0x3
+#define AUDENC_DSN_BIX_MASK_SFT (0x3 << 2)
+#define AUDENC_DSN_ESP_SFT 8
+#define AUDENC_DSN_ESP_MASK 0xff
+#define AUDENC_DSN_ESP_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDENC_DSN_FPI */
+#define AUDENC_DSN_FPI_SFT 0
+#define AUDENC_DSN_FPI_MASK 0xff
+#define AUDENC_DSN_FPI_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDENC_ANA_CON0 */
+#define RG_AUDPREAMPLON_SFT 0
+#define RG_AUDPREAMPLON_MASK 0x1
+#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPLDCCEN_SFT 1
+#define RG_AUDPREAMPLDCCEN_MASK 0x1
+#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPLPGATEST_SFT 3
+#define RG_AUDPREAMPLPGATEST_MASK 0x1
+#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPLVSCALE_SFT 4
+#define RG_AUDPREAMPLVSCALE_MASK 0x3
+#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPLINPUTSEL_SFT 6
+#define RG_AUDPREAMPLINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPLGAIN_SFT 8
+#define RG_AUDPREAMPLGAIN_MASK 0x7
+#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8)
+#define RG_AUDADCLPWRUP_SFT 12
+#define RG_AUDADCLPWRUP_MASK 0x1
+#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCLINPUTSEL_SFT 13
+#define RG_AUDADCLINPUTSEL_MASK 0x3
+#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13)
+
+/* MT6358_AUDENC_ANA_CON1 */
+#define RG_AUDPREAMPRON_SFT 0
+#define RG_AUDPREAMPRON_MASK 0x1
+#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPRDCCEN_SFT 1
+#define RG_AUDPREAMPRDCCEN_MASK 0x1
+#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPRDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPRPGATEST_SFT 3
+#define RG_AUDPREAMPRPGATEST_MASK 0x1
+#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPRVSCALE_SFT 4
+#define RG_AUDPREAMPRVSCALE_MASK 0x3
+#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPRINPUTSEL_SFT 6
+#define RG_AUDPREAMPRINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPRGAIN_SFT 8
+#define RG_AUDPREAMPRGAIN_MASK 0x7
+#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8)
+#define RG_AUDIO_VOW_EN_SFT 11
+#define RG_AUDIO_VOW_EN_MASK 0x1
+#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADCRPWRUP_SFT 12
+#define RG_AUDADCRPWRUP_MASK 0x1
+#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCRINPUTSEL_SFT 13
+#define RG_AUDADCRINPUTSEL_MASK 0x3
+#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13)
+#define RG_CLKSQ_EN_VOW_SFT 15
+#define RG_CLKSQ_EN_VOW_MASK 0x1
+#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDENC_ANA_CON2 */
+#define RG_AUDULHALFBIAS_SFT 0
+#define RG_AUDULHALFBIAS_MASK 0x1
+#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0)
+#define RG_AUDGLBVOWLPWEN_SFT 1
+#define RG_AUDGLBVOWLPWEN_MASK 0x1
+#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLPEN_SFT 2
+#define RG_AUDPREAMPLPEN_MASK 0x1
+#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDADC1STSTAGELPEN_SFT 3
+#define RG_AUDADC1STSTAGELPEN_MASK 0x1
+#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3)
+#define RG_AUDADC2NDSTAGELPEN_SFT 4
+#define RG_AUDADC2NDSTAGELPEN_MASK 0x1
+#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4)
+#define RG_AUDADCFLASHLPEN_SFT 5
+#define RG_AUDADCFLASHLPEN_MASK 0x1
+#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5)
+#define RG_AUDPREAMPIDDTEST_SFT 6
+#define RG_AUDPREAMPIDDTEST_MASK 0x3
+#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6)
+#define RG_AUDADC1STSTAGEIDDTEST_SFT 8
+#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8)
+#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10)
+#define RG_AUDADCREFBUFIDDTEST_SFT 12
+#define RG_AUDADCREFBUFIDDTEST_MASK 0x3
+#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12)
+#define RG_AUDADCFLASHIDDTEST_SFT 14
+#define RG_AUDADCFLASHIDDTEST_MASK 0x3
+#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14)
+
+/* MT6358_AUDENC_ANA_CON3 */
+#define RG_AUDADCDAC0P25FS_SFT 0
+#define RG_AUDADCDAC0P25FS_MASK 0x1
+#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 0)
+#define RG_AUDADCCLKSEL_SFT 1
+#define RG_AUDADCCLKSEL_MASK 0x1
+#define RG_AUDADCCLKSEL_MASK_SFT (0x1 << 1)
+#define RG_AUDADCCLKSOURCE_SFT 2
+#define RG_AUDADCCLKSOURCE_MASK 0x3
+#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 2)
+#define RG_AUDPREAMPAAFEN_SFT 8
+#define RG_AUDPREAMPAAFEN_MASK 0x1
+#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8)
+#define RG_DCCVCMBUFLPMODSEL_SFT 9
+#define RG_DCCVCMBUFLPMODSEL_MASK 0x1
+#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9)
+#define RG_DCCVCMBUFLPSWEN_SFT 10
+#define RG_DCCVCMBUFLPSWEN_MASK 0x1
+#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10)
+#define RG_CMSTBENH_SFT 11
+#define RG_CMSTBENH_MASK 0x1
+#define RG_CMSTBENH_MASK_SFT (0x1 << 11)
+#define RG_PGABODYSW_SFT 12
+#define RG_PGABODYSW_MASK 0x1
+#define RG_PGABODYSW_MASK_SFT (0x1 << 12)
+
+/* MT6358_AUDENC_ANA_CON4 */
+#define RG_AUDADC1STSTAGESDENB_SFT 0
+#define RG_AUDADC1STSTAGESDENB_MASK 0x1
+#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0)
+#define RG_AUDADC2NDSTAGERESET_SFT 1
+#define RG_AUDADC2NDSTAGERESET_MASK 0x1
+#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1)
+#define RG_AUDADC3RDSTAGERESET_SFT 2
+#define RG_AUDADC3RDSTAGERESET_MASK 0x1
+#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2)
+#define RG_AUDADCFSRESET_SFT 3
+#define RG_AUDADCFSRESET_MASK 0x1
+#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3)
+#define RG_AUDADCWIDECM_SFT 4
+#define RG_AUDADCWIDECM_MASK 0x1
+#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4)
+#define RG_AUDADCNOPATEST_SFT 5
+#define RG_AUDADCNOPATEST_MASK 0x1
+#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5)
+#define RG_AUDADCBYPASS_SFT 6
+#define RG_AUDADCBYPASS_MASK 0x1
+#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6)
+#define RG_AUDADCFFBYPASS_SFT 7
+#define RG_AUDADCFFBYPASS_MASK 0x1
+#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7)
+#define RG_AUDADCDACFBCURRENT_SFT 8
+#define RG_AUDADCDACFBCURRENT_MASK 0x1
+#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8)
+#define RG_AUDADCDACIDDTEST_SFT 9
+#define RG_AUDADCDACIDDTEST_MASK 0x3
+#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9)
+#define RG_AUDADCDACNRZ_SFT 11
+#define RG_AUDADCDACNRZ_MASK 0x1
+#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11)
+#define RG_AUDADCNODEM_SFT 12
+#define RG_AUDADCNODEM_MASK 0x1
+#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12)
+#define RG_AUDADCDACTEST_SFT 13
+#define RG_AUDADCDACTEST_MASK 0x1
+#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13)
+
+/* MT6358_AUDENC_ANA_CON5 */
+#define RG_AUDRCTUNEL_SFT 0
+#define RG_AUDRCTUNEL_MASK 0x1f
+#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0)
+#define RG_AUDRCTUNELSEL_SFT 5
+#define RG_AUDRCTUNELSEL_MASK 0x1
+#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5)
+#define RG_AUDRCTUNER_SFT 8
+#define RG_AUDRCTUNER_MASK 0x1f
+#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8)
+#define RG_AUDRCTUNERSEL_SFT 13
+#define RG_AUDRCTUNERSEL_MASK 0x1
+#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13)
+
+/* MT6358_AUDENC_ANA_CON6 */
+#define RG_CLKSQ_EN_SFT 0
+#define RG_CLKSQ_EN_MASK 0x1
+#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0)
+#define RG_CLKSQ_IN_SEL_TEST_SFT 1
+#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1
+#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1)
+#define RG_CM_REFGENSEL_SFT 2
+#define RG_CM_REFGENSEL_MASK 0x1
+#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2)
+#define RG_AUDSPARE_SFT 4
+#define RG_AUDSPARE_MASK 0xf
+#define RG_AUDSPARE_MASK_SFT (0xf << 4)
+#define RG_AUDENCSPARE_SFT 8
+#define RG_AUDENCSPARE_MASK 0x3f
+#define RG_AUDENCSPARE_MASK_SFT (0x3f << 8)
+
+/* MT6358_AUDENC_ANA_CON7 */
+#define RG_AUDENCSPARE2_SFT 0
+#define RG_AUDENCSPARE2_MASK 0xff
+#define RG_AUDENCSPARE2_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDENC_ANA_CON8 */
+#define RG_AUDDIGMICEN_SFT 0
+#define RG_AUDDIGMICEN_MASK 0x1
+#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0)
+#define RG_AUDDIGMICBIAS_SFT 1
+#define RG_AUDDIGMICBIAS_MASK 0x3
+#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1)
+#define RG_DMICHPCLKEN_SFT 3
+#define RG_DMICHPCLKEN_MASK 0x1
+#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3)
+#define RG_AUDDIGMICPDUTY_SFT 4
+#define RG_AUDDIGMICPDUTY_MASK 0x3
+#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4)
+#define RG_AUDDIGMICNDUTY_SFT 6
+#define RG_AUDDIGMICNDUTY_MASK 0x3
+#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6)
+#define RG_DMICMONEN_SFT 8
+#define RG_DMICMONEN_MASK 0x1
+#define RG_DMICMONEN_MASK_SFT (0x1 << 8)
+#define RG_DMICMONSEL_SFT 9
+#define RG_DMICMONSEL_MASK 0x7
+#define RG_DMICMONSEL_MASK_SFT (0x7 << 9)
+#define RG_AUDSPAREVMIC_SFT 12
+#define RG_AUDSPAREVMIC_MASK 0xf
+#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDENC_ANA_CON9 */
+#define RG_AUDPWDBMICBIAS0_SFT 0
+#define RG_AUDPWDBMICBIAS0_MASK 0x1
+#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS0BYPASSEN_SFT 1
+#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS0LOWPEN_SFT 2
+#define RG_AUDMICBIAS0LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS0VREF_SFT 4
+#define RG_AUDMICBIAS0VREF_MASK 0x7
+#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9)
+#define RG_AUDMICBIAS0DCSW0NEN_SFT 10
+#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13)
+#define RG_AUDMICBIAS0DCSW2NEN_SFT 14
+#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14)
+
+/* MT6358_AUDENC_ANA_CON10 */
+#define RG_AUDPWDBMICBIAS1_SFT 0
+#define RG_AUDPWDBMICBIAS1_MASK 0x1
+#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS1BYPASSEN_SFT 1
+#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS1LOWPEN_SFT 2
+#define RG_AUDMICBIAS1LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS1VREF_SFT 4
+#define RG_AUDMICBIAS1VREF_MASK 0x7
+#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS1DCSW1PEN_SFT 8
+#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS1DCSW1NEN_SFT 9
+#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9)
+#define RG_BANDGAPGEN_SFT 12
+#define RG_BANDGAPGEN_MASK 0x1
+#define RG_BANDGAPGEN_MASK_SFT (0x1 << 12)
+#define RG_MTEST_EN_SFT 13
+#define RG_MTEST_EN_MASK 0x1
+#define RG_MTEST_EN_MASK_SFT (0x1 << 13)
+#define RG_MTEST_SEL_SFT 14
+#define RG_MTEST_SEL_MASK 0x1
+#define RG_MTEST_SEL_MASK_SFT (0x1 << 14)
+#define RG_MTEST_CURRENT_SFT 15
+#define RG_MTEST_CURRENT_MASK 0x1
+#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDENC_ANA_CON11 */
+#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0)
+#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1)
+#define RG_AUDACCDETVIN1PULLLOW_SFT 2
+#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1
+#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 2)
+#define RG_AUDACCDETVTHACAL_SFT 4
+#define RG_AUDACCDETVTHACAL_MASK 0x1
+#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4)
+#define RG_AUDACCDETVTHBCAL_SFT 5
+#define RG_AUDACCDETVTHBCAL_MASK 0x1
+#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5)
+#define RG_AUDACCDETTVDET_SFT 6
+#define RG_AUDACCDETTVDET_MASK 0x1
+#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6)
+#define RG_ACCDETSEL_SFT 7
+#define RG_ACCDETSEL_MASK 0x1
+#define RG_ACCDETSEL_MASK_SFT (0x1 << 7)
+#define RG_SWBUFMODSEL_SFT 8
+#define RG_SWBUFMODSEL_MASK 0x1
+#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8)
+#define RG_SWBUFSWEN_SFT 9
+#define RG_SWBUFSWEN_MASK 0x1
+#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9)
+#define RG_EINTCOMPVTH_SFT 10
+#define RG_EINTCOMPVTH_MASK 0x1
+#define RG_EINTCOMPVTH_MASK_SFT (0x1 << 10)
+#define RG_EINTCONFIGACCDET_SFT 11
+#define RG_EINTCONFIGACCDET_MASK 0x1
+#define RG_EINTCONFIGACCDET_MASK_SFT (0x1 << 11)
+#define RG_EINTHIRENB_SFT 12
+#define RG_EINTHIRENB_MASK 0x1
+#define RG_EINTHIRENB_MASK_SFT (0x1 << 12)
+#define RG_ACCDET2AUXRESBYPASS_SFT 13
+#define RG_ACCDET2AUXRESBYPASS_MASK 0x1
+#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13)
+#define RG_ACCDET2AUXBUFFERBYPASS_SFT 14
+#define RG_ACCDET2AUXBUFFERBYPASS_MASK 0x1
+#define RG_ACCDET2AUXBUFFERBYPASS_MASK_SFT (0x1 << 14)
+#define RG_ACCDET2AUXSWEN_SFT 15
+#define RG_ACCDET2AUXSWEN_MASK 0x1
+#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDENC_ANA_CON12 */
+#define RGS_AUDRCTUNELREAD_SFT 0
+#define RGS_AUDRCTUNELREAD_MASK 0x1f
+#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0)
+#define RGS_AUDRCTUNERREAD_SFT 8
+#define RGS_AUDRCTUNERREAD_MASK 0x1f
+#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8)
+
+/* MT6358_AUDDEC_DSN_ID */
+#define AUDDEC_ANA_ID_SFT 0
+#define AUDDEC_ANA_ID_MASK 0xff
+#define AUDDEC_ANA_ID_MASK_SFT (0xff << 0)
+#define AUDDEC_DIG_ID_SFT 8
+#define AUDDEC_DIG_ID_MASK 0xff
+#define AUDDEC_DIG_ID_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_DSN_REV0 */
+#define AUDDEC_ANA_MINOR_REV_SFT 0
+#define AUDDEC_ANA_MINOR_REV_MASK 0xf
+#define AUDDEC_ANA_MINOR_REV_MASK_SFT (0xf << 0)
+#define AUDDEC_ANA_MAJOR_REV_SFT 4
+#define AUDDEC_ANA_MAJOR_REV_MASK 0xf
+#define AUDDEC_ANA_MAJOR_REV_MASK_SFT (0xf << 4)
+#define AUDDEC_DIG_MINOR_REV_SFT 8
+#define AUDDEC_DIG_MINOR_REV_MASK 0xf
+#define AUDDEC_DIG_MINOR_REV_MASK_SFT (0xf << 8)
+#define AUDDEC_DIG_MAJOR_REV_SFT 12
+#define AUDDEC_DIG_MAJOR_REV_MASK 0xf
+#define AUDDEC_DIG_MAJOR_REV_MASK_SFT (0xf << 12)
+
+/* MT6358_AUDDEC_DSN_DBI */
+#define AUDDEC_DSN_CBS_SFT 0
+#define AUDDEC_DSN_CBS_MASK 0x3
+#define AUDDEC_DSN_CBS_MASK_SFT (0x3 << 0)
+#define AUDDEC_DSN_BIX_SFT 2
+#define AUDDEC_DSN_BIX_MASK 0x3
+#define AUDDEC_DSN_BIX_MASK_SFT (0x3 << 2)
+#define AUDDEC_DSN_ESP_SFT 8
+#define AUDDEC_DSN_ESP_MASK 0xff
+#define AUDDEC_DSN_ESP_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_DSN_FPI */
+#define AUDDEC_DSN_FPI_SFT 0
+#define AUDDEC_DSN_FPI_MASK 0xff
+#define AUDDEC_DSN_FPI_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDDEC_ANA_CON0 */
+#define RG_AUDDACLPWRUP_VAUDP15_SFT 0
+#define RG_AUDDACLPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDDACLPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDDACRPWRUP_VAUDP15_SFT 1
+#define RG_AUDDACRPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDDACRPWRUP_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUD_DAC_PWR_UP_VA28_SFT 2
+#define RG_AUD_DAC_PWR_UP_VA28_MASK 0x1
+#define RG_AUD_DAC_PWR_UP_VA28_MASK_SFT (0x1 << 2)
+#define RG_AUD_DAC_PWL_UP_VA28_SFT 3
+#define RG_AUD_DAC_PWL_UP_VA28_MASK 0x1
+#define RG_AUD_DAC_PWL_UP_VA28_MASK_SFT (0x1 << 3)
+#define RG_AUDHPLPWRUP_VAUDP15_SFT 4
+#define RG_AUDHPLPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPLPWRUP_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_AUDHPRPWRUP_VAUDP15_SFT 5
+#define RG_AUDHPRPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPRPWRUP_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_SFT 6
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_SFT 7
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_AUDHPLMUXINPUTSEL_VAUDP15_SFT 8
+#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 8)
+#define RG_AUDHPRMUXINPUTSEL_VAUDP15_SFT 10
+#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 10)
+#define RG_AUDHPLSCDISABLE_VAUDP15_SFT 12
+#define RG_AUDHPLSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 12)
+#define RG_AUDHPRSCDISABLE_VAUDP15_SFT 13
+#define RG_AUDHPRSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT (0x1 << 13)
+#define RG_AUDHPLBSCCURRENT_VAUDP15_SFT 14
+#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 14)
+#define RG_AUDHPRBSCCURRENT_VAUDP15_SFT 15
+#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON1 */
+#define RG_AUDHPLOUTPWRUP_VAUDP15_SFT 0
+#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDHPROUTPWRUP_VAUDP15_SFT 1
+#define RG_AUDHPROUTPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPROUTPWRUP_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_SFT 2
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 2)
+#define RG_AUDHPROUTAUXPWRUP_VAUDP15_SFT 3
+#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 3)
+#define RG_HPLAUXFBRSW_EN_VAUDP15_SFT 4
+#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK 0x1
+#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_HPRAUXFBRSW_EN_VAUDP15_SFT 5
+#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK 0x1
+#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_SFT 6
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK 0x1
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_SFT 7
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK 0x1
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_HPLOUTSTGCTRL_VAUDP15_SFT 8
+#define RG_HPLOUTSTGCTRL_VAUDP15_MASK 0x7
+#define RG_HPLOUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 8)
+#define RG_HPROUTSTGCTRL_VAUDP15_SFT 11
+#define RG_HPROUTSTGCTRL_VAUDP15_MASK 0x7
+#define RG_HPROUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 11)
+
+/* MT6358_AUDDEC_ANA_CON2 */
+#define RG_HPLOUTPUTSTBENH_VAUDP15_SFT 0
+#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK 0x7
+#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 0)
+#define RG_HPROUTPUTSTBENH_VAUDP15_SFT 4
+#define RG_HPROUTPUTSTBENH_VAUDP15_MASK 0x7
+#define RG_HPROUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 4)
+#define RG_AUDHPSTARTUP_VAUDP15_SFT 13
+#define RG_AUDHPSTARTUP_VAUDP15_MASK 0x1
+#define RG_AUDHPSTARTUP_VAUDP15_MASK_SFT (0x1 << 13)
+#define RG_AUDREFN_DERES_EN_VAUDP15_SFT 14
+#define RG_AUDREFN_DERES_EN_VAUDP15_MASK 0x1
+#define RG_AUDREFN_DERES_EN_VAUDP15_MASK_SFT (0x1 << 14)
+#define RG_HPPSHORT2VCM_VAUDP15_SFT 15
+#define RG_HPPSHORT2VCM_VAUDP15_MASK 0x1
+#define RG_HPPSHORT2VCM_VAUDP15_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON3 */
+#define RG_HPINPUTSTBENH_VAUDP15_SFT 13
+#define RG_HPINPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_HPINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 13)
+#define RG_HPINPUTRESET0_VAUDP15_SFT 14
+#define RG_HPINPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HPINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 14)
+#define RG_HPOUTPUTRESET0_VAUDP15_SFT 15
+#define RG_HPOUTPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HPOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON4 */
+#define RG_ABIDEC_RSVD0_VAUDP28_SFT 0
+#define RG_ABIDEC_RSVD0_VAUDP28_MASK 0xff
+#define RG_ABIDEC_RSVD0_VAUDP28_MASK_SFT (0xff << 0)
+
+/* MT6358_AUDDEC_ANA_CON5 */
+#define RG_AUDHPDECMGAINADJ_VAUDP15_SFT 0
+#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK 0x7
+#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK_SFT (0x7 << 0)
+#define RG_AUDHPDEDMGAINADJ_VAUDP15_SFT 4
+#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK 0x7
+#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK_SFT (0x7 << 4)
+
+/* MT6358_AUDDEC_ANA_CON6 */
+#define RG_AUDHSPWRUP_VAUDP15_SFT 0
+#define RG_AUDHSPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDHSPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDHSPWRUP_IBIAS_VAUDP15_SFT 1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDHSMUXINPUTSEL_VAUDP15_SFT 2
+#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2)
+#define RG_AUDHSSCDISABLE_VAUDP15_SFT 4
+#define RG_AUDHSSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_AUDHSBSCCURRENT_VAUDP15_SFT 5
+#define RG_AUDHSBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDHSBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_AUDHSSTARTUP_VAUDP15_SFT 6
+#define RG_AUDHSSTARTUP_VAUDP15_MASK 0x1
+#define RG_AUDHSSTARTUP_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_HSOUTPUTSTBENH_VAUDP15_SFT 7
+#define RG_HSOUTPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_HSOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_HSINPUTSTBENH_VAUDP15_SFT 8
+#define RG_HSINPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_HSINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8)
+#define RG_HSINPUTRESET0_VAUDP15_SFT 9
+#define RG_HSINPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HSINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9)
+#define RG_HSOUTPUTRESET0_VAUDP15_SFT 10
+#define RG_HSOUTPUTRESET0_VAUDP15_MASK 0x1
+#define RG_HSOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10)
+#define RG_HSOUT_SHORTVCM_VAUDP15_SFT 11
+#define RG_HSOUT_SHORTVCM_VAUDP15_MASK 0x1
+#define RG_HSOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11)
+
+/* MT6358_AUDDEC_ANA_CON7 */
+#define RG_AUDLOLPWRUP_VAUDP15_SFT 0
+#define RG_AUDLOLPWRUP_VAUDP15_MASK 0x1
+#define RG_AUDLOLPWRUP_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_SFT 1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK 0x1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT 2
+#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK 0x3
+#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2)
+#define RG_AUDLOLSCDISABLE_VAUDP15_SFT 4
+#define RG_AUDLOLSCDISABLE_VAUDP15_MASK 0x1
+#define RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4)
+#define RG_AUDLOLBSCCURRENT_VAUDP15_SFT 5
+#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK 0x1
+#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5)
+#define RG_AUDLOSTARTUP_VAUDP15_SFT 6
+#define RG_AUDLOSTARTUP_VAUDP15_MASK 0x1
+#define RG_AUDLOSTARTUP_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_LOINPUTSTBENH_VAUDP15_SFT 7
+#define RG_LOINPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_LOINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7)
+#define RG_LOOUTPUTSTBENH_VAUDP15_SFT 8
+#define RG_LOOUTPUTSTBENH_VAUDP15_MASK 0x1
+#define RG_LOOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8)
+#define RG_LOINPUTRESET0_VAUDP15_SFT 9
+#define RG_LOINPUTRESET0_VAUDP15_MASK 0x1
+#define RG_LOINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9)
+#define RG_LOOUTPUTRESET0_VAUDP15_SFT 10
+#define RG_LOOUTPUTRESET0_VAUDP15_MASK 0x1
+#define RG_LOOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10)
+#define RG_LOOUT_SHORTVCM_VAUDP15_SFT 11
+#define RG_LOOUT_SHORTVCM_VAUDP15_MASK 0x1
+#define RG_LOOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11)
+
+/* MT6358_AUDDEC_ANA_CON8 */
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_SFT 0
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK 0xf
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK_SFT (0xf << 0)
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_SFT 4
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK 0x3
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK_SFT (0x3 << 4)
+#define RG_AUDTRIMBUF_EN_VAUDP15_SFT 6
+#define RG_AUDTRIMBUF_EN_VAUDP15_MASK 0x1
+#define RG_AUDTRIMBUF_EN_VAUDP15_MASK_SFT (0x1 << 6)
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_SFT 8
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 8)
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_SFT 10
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK 0x3
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 10)
+#define RG_AUDHPSPKDET_EN_VAUDP15_SFT 12
+#define RG_AUDHPSPKDET_EN_VAUDP15_MASK 0x1
+#define RG_AUDHPSPKDET_EN_VAUDP15_MASK_SFT (0x1 << 12)
+
+/* MT6358_AUDDEC_ANA_CON9 */
+#define RG_ABIDEC_RSVD0_VA28_SFT 0
+#define RG_ABIDEC_RSVD0_VA28_MASK 0xff
+#define RG_ABIDEC_RSVD0_VA28_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD0_VAUDP15_SFT 8
+#define RG_ABIDEC_RSVD0_VAUDP15_MASK 0xff
+#define RG_ABIDEC_RSVD0_VAUDP15_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_ANA_CON10 */
+#define RG_ABIDEC_RSVD1_VAUDP15_SFT 0
+#define RG_ABIDEC_RSVD1_VAUDP15_MASK 0xff
+#define RG_ABIDEC_RSVD1_VAUDP15_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD2_VAUDP15_SFT 8
+#define RG_ABIDEC_RSVD2_VAUDP15_MASK 0xff
+#define RG_ABIDEC_RSVD2_VAUDP15_MASK_SFT (0xff << 8)
+
+/* MT6358_AUDDEC_ANA_CON11 */
+#define RG_AUDZCDMUXSEL_VAUDP15_SFT 0
+#define RG_AUDZCDMUXSEL_VAUDP15_MASK 0x7
+#define RG_AUDZCDMUXSEL_VAUDP15_MASK_SFT (0x7 << 0)
+#define RG_AUDZCDCLKSEL_VAUDP15_SFT 3
+#define RG_AUDZCDCLKSEL_VAUDP15_MASK 0x1
+#define RG_AUDZCDCLKSEL_VAUDP15_MASK_SFT (0x1 << 3)
+#define RG_AUDBIASADJ_0_VAUDP15_SFT 7
+#define RG_AUDBIASADJ_0_VAUDP15_MASK 0x1ff
+#define RG_AUDBIASADJ_0_VAUDP15_MASK_SFT (0x1ff << 7)
+
+/* MT6358_AUDDEC_ANA_CON12 */
+#define RG_AUDBIASADJ_1_VAUDP15_SFT 0
+#define RG_AUDBIASADJ_1_VAUDP15_MASK 0xff
+#define RG_AUDBIASADJ_1_VAUDP15_MASK_SFT (0xff << 0)
+#define RG_AUDIBIASPWRDN_VAUDP15_SFT 8
+#define RG_AUDIBIASPWRDN_VAUDP15_MASK 0x1
+#define RG_AUDIBIASPWRDN_VAUDP15_MASK_SFT (0x1 << 8)
+
+/* MT6358_AUDDEC_ANA_CON13 */
+#define RG_RSTB_DECODER_VA28_SFT 0
+#define RG_RSTB_DECODER_VA28_MASK 0x1
+#define RG_RSTB_DECODER_VA28_MASK_SFT (0x1 << 0)
+#define RG_SEL_DECODER_96K_VA28_SFT 1
+#define RG_SEL_DECODER_96K_VA28_MASK 0x1
+#define RG_SEL_DECODER_96K_VA28_MASK_SFT (0x1 << 1)
+#define RG_SEL_DELAY_VCORE_SFT 2
+#define RG_SEL_DELAY_VCORE_MASK 0x1
+#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2)
+#define RG_AUDGLB_PWRDN_VA28_SFT 4
+#define RG_AUDGLB_PWRDN_VA28_MASK 0x1
+#define RG_AUDGLB_PWRDN_VA28_MASK_SFT (0x1 << 4)
+#define RG_RSTB_ENCODER_VA28_SFT 5
+#define RG_RSTB_ENCODER_VA28_MASK 0x1
+#define RG_RSTB_ENCODER_VA28_MASK_SFT (0x1 << 5)
+#define RG_SEL_ENCODER_96K_VA28_SFT 6
+#define RG_SEL_ENCODER_96K_VA28_MASK 0x1
+#define RG_SEL_ENCODER_96K_VA28_MASK_SFT (0x1 << 6)
+
+/* MT6358_AUDDEC_ANA_CON14 */
+#define RG_HCLDO_EN_VA18_SFT 0
+#define RG_HCLDO_EN_VA18_MASK 0x1
+#define RG_HCLDO_EN_VA18_MASK_SFT (0x1 << 0)
+#define RG_HCLDO_PDDIS_EN_VA18_SFT 1
+#define RG_HCLDO_PDDIS_EN_VA18_MASK 0x1
+#define RG_HCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 1)
+#define RG_HCLDO_REMOTE_SENSE_VA18_SFT 2
+#define RG_HCLDO_REMOTE_SENSE_VA18_MASK 0x1
+#define RG_HCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2)
+#define RG_LCLDO_EN_VA18_SFT 4
+#define RG_LCLDO_EN_VA18_MASK 0x1
+#define RG_LCLDO_EN_VA18_MASK_SFT (0x1 << 4)
+#define RG_LCLDO_PDDIS_EN_VA18_SFT 5
+#define RG_LCLDO_PDDIS_EN_VA18_MASK 0x1
+#define RG_LCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 5)
+#define RG_LCLDO_REMOTE_SENSE_VA18_SFT 6
+#define RG_LCLDO_REMOTE_SENSE_VA18_MASK 0x1
+#define RG_LCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 6)
+#define RG_LCLDO_ENC_EN_VA28_SFT 8
+#define RG_LCLDO_ENC_EN_VA28_MASK 0x1
+#define RG_LCLDO_ENC_EN_VA28_MASK_SFT (0x1 << 8)
+#define RG_LCLDO_ENC_PDDIS_EN_VA28_SFT 9
+#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK 0x1
+#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK_SFT (0x1 << 9)
+#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_SFT 10
+#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK 0x1
+#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK_SFT (0x1 << 10)
+#define RG_VA33REFGEN_EN_VA18_SFT 12
+#define RG_VA33REFGEN_EN_VA18_MASK 0x1
+#define RG_VA33REFGEN_EN_VA18_MASK_SFT (0x1 << 12)
+#define RG_VA28REFGEN_EN_VA28_SFT 13
+#define RG_VA28REFGEN_EN_VA28_MASK 0x1
+#define RG_VA28REFGEN_EN_VA28_MASK_SFT (0x1 << 13)
+#define RG_HCLDO_VOSEL_VA18_SFT 14
+#define RG_HCLDO_VOSEL_VA18_MASK 0x1
+#define RG_HCLDO_VOSEL_VA18_MASK_SFT (0x1 << 14)
+#define RG_LCLDO_VOSEL_VA18_SFT 15
+#define RG_LCLDO_VOSEL_VA18_MASK 0x1
+#define RG_LCLDO_VOSEL_VA18_MASK_SFT (0x1 << 15)
+
+/* MT6358_AUDDEC_ANA_CON15 */
+#define RG_NVREG_EN_VAUDP15_SFT 0
+#define RG_NVREG_EN_VAUDP15_MASK 0x1
+#define RG_NVREG_EN_VAUDP15_MASK_SFT (0x1 << 0)
+#define RG_NVREG_PULL0V_VAUDP15_SFT 1
+#define RG_NVREG_PULL0V_VAUDP15_MASK 0x1
+#define RG_NVREG_PULL0V_VAUDP15_MASK_SFT (0x1 << 1)
+#define RG_AUDPMU_RSD0_VAUDP15_SFT 4
+#define RG_AUDPMU_RSD0_VAUDP15_MASK 0xf
+#define RG_AUDPMU_RSD0_VAUDP15_MASK_SFT (0xf << 4)
+#define RG_AUDPMU_RSD0_VA18_SFT 8
+#define RG_AUDPMU_RSD0_VA18_MASK 0xf
+#define RG_AUDPMU_RSD0_VA18_MASK_SFT (0xf << 8)
+#define RG_AUDPMU_RSD0_VA28_SFT 12
+#define RG_AUDPMU_RSD0_VA28_MASK 0xf
+#define RG_AUDPMU_RSD0_VA28_MASK_SFT (0xf << 12)
+
+/* MT6358_ZCD_CON0 */
+#define RG_AUDZCDENABLE_SFT 0
+#define RG_AUDZCDENABLE_MASK 0x1
+#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0)
+#define RG_AUDZCDGAINSTEPTIME_SFT 1
+#define RG_AUDZCDGAINSTEPTIME_MASK 0x7
+#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1)
+#define RG_AUDZCDGAINSTEPSIZE_SFT 4
+#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3
+#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4)
+#define RG_AUDZCDTIMEOUTMODESEL_SFT 6
+#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1
+#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6)
+
+/* MT6358_ZCD_CON1 */
+#define RG_AUDLOLGAIN_SFT 0
+#define RG_AUDLOLGAIN_MASK 0x1f
+#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDLORGAIN_SFT 7
+#define RG_AUDLORGAIN_MASK 0x1f
+#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6358_ZCD_CON2 */
+#define RG_AUDHPLGAIN_SFT 0
+#define RG_AUDHPLGAIN_MASK 0x1f
+#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDHPRGAIN_SFT 7
+#define RG_AUDHPRGAIN_MASK 0x1f
+#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6358_ZCD_CON3 */
+#define RG_AUDHSGAIN_SFT 0
+#define RG_AUDHSGAIN_MASK 0x1f
+#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0)
+
+/* MT6358_ZCD_CON4 */
+#define RG_AUDIVLGAIN_SFT 0
+#define RG_AUDIVLGAIN_MASK 0x7
+#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0)
+#define RG_AUDIVRGAIN_SFT 8
+#define RG_AUDIVRGAIN_MASK 0x7
+#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8)
+
+/* MT6358_ZCD_CON5 */
+#define RG_AUDINTGAIN1_SFT 0
+#define RG_AUDINTGAIN1_MASK 0x3f
+#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0)
+#define RG_AUDINTGAIN2_SFT 8
+#define RG_AUDINTGAIN2_MASK 0x3f
+#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8)
+
+/* audio register */
+#define MT6358_DRV_CON3 0x3c
+#define MT6358_GPIO_DIR0 0x88
+
+#define MT6358_GPIO_MODE2 0xd8 /* mosi */
+#define MT6358_GPIO_MODE2_SET 0xda
+#define MT6358_GPIO_MODE2_CLR 0xdc
+
+#define MT6358_GPIO_MODE3 0xde /* miso */
+#define MT6358_GPIO_MODE3_SET 0xe0
+#define MT6358_GPIO_MODE3_CLR 0xe2
+
+#define MT6358_TOP_CKPDN_CON0 0x10c
+#define MT6358_TOP_CKPDN_CON0_SET 0x10e
+#define MT6358_TOP_CKPDN_CON0_CLR 0x110
+
+#define MT6358_TOP_CKHWEN_CON0 0x12a
+#define MT6358_TOP_CKHWEN_CON0_SET 0x12c
+#define MT6358_TOP_CKHWEN_CON0_CLR 0x12e
+
+#define MT6358_OTP_CON0 0x38a
+#define MT6358_OTP_CON8 0x39a
+#define MT6358_OTP_CON11 0x3a0
+#define MT6358_OTP_CON12 0x3a2
+#define MT6358_OTP_CON13 0x3a4
+
+#define MT6358_DCXO_CW13 0x7aa
+#define MT6358_DCXO_CW14 0x7ac
+
+#define MT6358_AUXADC_CON10 0x11a0
+
+/* audio register */
+#define MT6358_AUD_TOP_ID 0x2200
+#define MT6358_AUD_TOP_REV0 0x2202
+#define MT6358_AUD_TOP_DBI 0x2204
+#define MT6358_AUD_TOP_DXI 0x2206
+#define MT6358_AUD_TOP_CKPDN_TPM0 0x2208
+#define MT6358_AUD_TOP_CKPDN_TPM1 0x220a
+#define MT6358_AUD_TOP_CKPDN_CON0 0x220c
+#define MT6358_AUD_TOP_CKPDN_CON0_SET 0x220e
+#define MT6358_AUD_TOP_CKPDN_CON0_CLR 0x2210
+#define MT6358_AUD_TOP_CKSEL_CON0 0x2212
+#define MT6358_AUD_TOP_CKSEL_CON0_SET 0x2214
+#define MT6358_AUD_TOP_CKSEL_CON0_CLR 0x2216
+#define MT6358_AUD_TOP_CKTST_CON0 0x2218
+#define MT6358_AUD_TOP_CLK_HWEN_CON0 0x221a
+#define MT6358_AUD_TOP_CLK_HWEN_CON0_SET 0x221c
+#define MT6358_AUD_TOP_CLK_HWEN_CON0_CLR 0x221e
+#define MT6358_AUD_TOP_RST_CON0 0x2220
+#define MT6358_AUD_TOP_RST_CON0_SET 0x2222
+#define MT6358_AUD_TOP_RST_CON0_CLR 0x2224
+#define MT6358_AUD_TOP_RST_BANK_CON0 0x2226
+#define MT6358_AUD_TOP_INT_CON0 0x2228
+#define MT6358_AUD_TOP_INT_CON0_SET 0x222a
+#define MT6358_AUD_TOP_INT_CON0_CLR 0x222c
+#define MT6358_AUD_TOP_INT_MASK_CON0 0x222e
+#define MT6358_AUD_TOP_INT_MASK_CON0_SET 0x2230
+#define MT6358_AUD_TOP_INT_MASK_CON0_CLR 0x2232
+#define MT6358_AUD_TOP_INT_STATUS0 0x2234
+#define MT6358_AUD_TOP_INT_RAW_STATUS0 0x2236
+#define MT6358_AUD_TOP_INT_MISC_CON0 0x2238
+#define MT6358_AUDNCP_CLKDIV_CON0 0x223a
+#define MT6358_AUDNCP_CLKDIV_CON1 0x223c
+#define MT6358_AUDNCP_CLKDIV_CON2 0x223e
+#define MT6358_AUDNCP_CLKDIV_CON3 0x2240
+#define MT6358_AUDNCP_CLKDIV_CON4 0x2242
+#define MT6358_AUD_TOP_MON_CON0 0x2244
+#define MT6358_AUDIO_DIG_DSN_ID 0x2280
+#define MT6358_AUDIO_DIG_DSN_REV0 0x2282
+#define MT6358_AUDIO_DIG_DSN_DBI 0x2284
+#define MT6358_AUDIO_DIG_DSN_DXI 0x2286
+#define MT6358_AFE_UL_DL_CON0 0x2288
+#define MT6358_AFE_DL_SRC2_CON0_L 0x228a
+#define MT6358_AFE_UL_SRC_CON0_H 0x228c
+#define MT6358_AFE_UL_SRC_CON0_L 0x228e
+#define MT6358_AFE_TOP_CON0 0x2290
+#define MT6358_AUDIO_TOP_CON0 0x2292
+#define MT6358_AFE_MON_DEBUG0 0x2294
+#define MT6358_AFUNC_AUD_CON0 0x2296
+#define MT6358_AFUNC_AUD_CON1 0x2298
+#define MT6358_AFUNC_AUD_CON2 0x229a
+#define MT6358_AFUNC_AUD_CON3 0x229c
+#define MT6358_AFUNC_AUD_CON4 0x229e
+#define MT6358_AFUNC_AUD_CON5 0x22a0
+#define MT6358_AFUNC_AUD_CON6 0x22a2
+#define MT6358_AFUNC_AUD_MON0 0x22a4
+#define MT6358_AUDRC_TUNE_MON0 0x22a6
+#define MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 0x22a8
+#define MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x22aa
+#define MT6358_AFE_ADDA_MTKAIF_MON0 0x22ac
+#define MT6358_AFE_ADDA_MTKAIF_MON1 0x22ae
+#define MT6358_AFE_ADDA_MTKAIF_MON2 0x22b0
+#define MT6358_AFE_ADDA_MTKAIF_MON3 0x22b2
+#define MT6358_AFE_ADDA_MTKAIF_CFG0 0x22b4
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG0 0x22b6
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG1 0x22b8
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG2 0x22ba
+#define MT6358_AFE_ADDA_MTKAIF_RX_CFG3 0x22bc
+#define MT6358_AFE_ADDA_MTKAIF_TX_CFG1 0x22be
+#define MT6358_AFE_SGEN_CFG0 0x22c0
+#define MT6358_AFE_SGEN_CFG1 0x22c2
+#define MT6358_AFE_ADC_ASYNC_FIFO_CFG 0x22c4
+#define MT6358_AFE_DCCLK_CFG0 0x22c6
+#define MT6358_AFE_DCCLK_CFG1 0x22c8
+#define MT6358_AUDIO_DIG_CFG 0x22ca
+#define MT6358_AFE_AUD_PAD_TOP 0x22cc
+#define MT6358_AFE_AUD_PAD_TOP_MON 0x22ce
+#define MT6358_AFE_AUD_PAD_TOP_MON1 0x22d0
+#define MT6358_AFE_DL_NLE_CFG 0x22d2
+#define MT6358_AFE_DL_NLE_MON 0x22d4
+#define MT6358_AFE_CG_EN_MON 0x22d6
+#define MT6358_AUDIO_DIG_2ND_DSN_ID 0x2300
+#define MT6358_AUDIO_DIG_2ND_DSN_REV0 0x2302
+#define MT6358_AUDIO_DIG_2ND_DSN_DBI 0x2304
+#define MT6358_AUDIO_DIG_2ND_DSN_DXI 0x2306
+#define MT6358_AFE_PMIC_NEWIF_CFG3 0x2308
+#define MT6358_AFE_VOW_TOP 0x230a
+#define MT6358_AFE_VOW_CFG0 0x230c
+#define MT6358_AFE_VOW_CFG1 0x230e
+#define MT6358_AFE_VOW_CFG2 0x2310
+#define MT6358_AFE_VOW_CFG3 0x2312
+#define MT6358_AFE_VOW_CFG4 0x2314
+#define MT6358_AFE_VOW_CFG5 0x2316
+#define MT6358_AFE_VOW_CFG6 0x2318
+#define MT6358_AFE_VOW_MON0 0x231a
+#define MT6358_AFE_VOW_MON1 0x231c
+#define MT6358_AFE_VOW_MON2 0x231e
+#define MT6358_AFE_VOW_MON3 0x2320
+#define MT6358_AFE_VOW_MON4 0x2322
+#define MT6358_AFE_VOW_MON5 0x2324
+#define MT6358_AFE_VOW_SN_INI_CFG 0x2326
+#define MT6358_AFE_VOW_TGEN_CFG0 0x2328
+#define MT6358_AFE_VOW_POSDIV_CFG0 0x232a
+#define MT6358_AFE_VOW_HPF_CFG0 0x232c
+#define MT6358_AFE_VOW_PERIODIC_CFG0 0x232e
+#define MT6358_AFE_VOW_PERIODIC_CFG1 0x2330
+#define MT6358_AFE_VOW_PERIODIC_CFG2 0x2332
+#define MT6358_AFE_VOW_PERIODIC_CFG3 0x2334
+#define MT6358_AFE_VOW_PERIODIC_CFG4 0x2336
+#define MT6358_AFE_VOW_PERIODIC_CFG5 0x2338
+#define MT6358_AFE_VOW_PERIODIC_CFG6 0x233a
+#define MT6358_AFE_VOW_PERIODIC_CFG7 0x233c
+#define MT6358_AFE_VOW_PERIODIC_CFG8 0x233e
+#define MT6358_AFE_VOW_PERIODIC_CFG9 0x2340
+#define MT6358_AFE_VOW_PERIODIC_CFG10 0x2342
+#define MT6358_AFE_VOW_PERIODIC_CFG11 0x2344
+#define MT6358_AFE_VOW_PERIODIC_CFG12 0x2346
+#define MT6358_AFE_VOW_PERIODIC_CFG13 0x2348
+#define MT6358_AFE_VOW_PERIODIC_CFG14 0x234a
+#define MT6358_AFE_VOW_PERIODIC_CFG15 0x234c
+#define MT6358_AFE_VOW_PERIODIC_CFG16 0x234e
+#define MT6358_AFE_VOW_PERIODIC_CFG17 0x2350
+#define MT6358_AFE_VOW_PERIODIC_CFG18 0x2352
+#define MT6358_AFE_VOW_PERIODIC_CFG19 0x2354
+#define MT6358_AFE_VOW_PERIODIC_CFG20 0x2356
+#define MT6358_AFE_VOW_PERIODIC_CFG21 0x2358
+#define MT6358_AFE_VOW_PERIODIC_CFG22 0x235a
+#define MT6358_AFE_VOW_PERIODIC_CFG23 0x235c
+#define MT6358_AFE_VOW_PERIODIC_MON0 0x235e
+#define MT6358_AFE_VOW_PERIODIC_MON1 0x2360
+#define MT6358_AUDENC_DSN_ID 0x2380
+#define MT6358_AUDENC_DSN_REV0 0x2382
+#define MT6358_AUDENC_DSN_DBI 0x2384
+#define MT6358_AUDENC_DSN_FPI 0x2386
+#define MT6358_AUDENC_ANA_CON0 0x2388
+#define MT6358_AUDENC_ANA_CON1 0x238a
+#define MT6358_AUDENC_ANA_CON2 0x238c
+#define MT6358_AUDENC_ANA_CON3 0x238e
+#define MT6358_AUDENC_ANA_CON4 0x2390
+#define MT6358_AUDENC_ANA_CON5 0x2392
+#define MT6358_AUDENC_ANA_CON6 0x2394
+#define MT6358_AUDENC_ANA_CON7 0x2396
+#define MT6358_AUDENC_ANA_CON8 0x2398
+#define MT6358_AUDENC_ANA_CON9 0x239a
+#define MT6358_AUDENC_ANA_CON10 0x239c
+#define MT6358_AUDENC_ANA_CON11 0x239e
+#define MT6358_AUDENC_ANA_CON12 0x23a0
+#define MT6358_AUDDEC_DSN_ID 0x2400
+#define MT6358_AUDDEC_DSN_REV0 0x2402
+#define MT6358_AUDDEC_DSN_DBI 0x2404
+#define MT6358_AUDDEC_DSN_FPI 0x2406
+#define MT6358_AUDDEC_ANA_CON0 0x2408
+#define MT6358_AUDDEC_ANA_CON1 0x240a
+#define MT6358_AUDDEC_ANA_CON2 0x240c
+#define MT6358_AUDDEC_ANA_CON3 0x240e
+#define MT6358_AUDDEC_ANA_CON4 0x2410
+#define MT6358_AUDDEC_ANA_CON5 0x2412
+#define MT6358_AUDDEC_ANA_CON6 0x2414
+#define MT6358_AUDDEC_ANA_CON7 0x2416
+#define MT6358_AUDDEC_ANA_CON8 0x2418
+#define MT6358_AUDDEC_ANA_CON9 0x241a
+#define MT6358_AUDDEC_ANA_CON10 0x241c
+#define MT6358_AUDDEC_ANA_CON11 0x241e
+#define MT6358_AUDDEC_ANA_CON12 0x2420
+#define MT6358_AUDDEC_ANA_CON13 0x2422
+#define MT6358_AUDDEC_ANA_CON14 0x2424
+#define MT6358_AUDDEC_ANA_CON15 0x2426
+#define MT6358_AUDDEC_ELR_NUM 0x2428
+#define MT6358_AUDDEC_ELR_0 0x242a
+#define MT6358_AUDZCD_DSN_ID 0x2480
+#define MT6358_AUDZCD_DSN_REV0 0x2482
+#define MT6358_AUDZCD_DSN_DBI 0x2484
+#define MT6358_AUDZCD_DSN_FPI 0x2486
+#define MT6358_ZCD_CON0 0x2488
+#define MT6358_ZCD_CON1 0x248a
+#define MT6358_ZCD_CON2 0x248c
+#define MT6358_ZCD_CON3 0x248e
+#define MT6358_ZCD_CON4 0x2490
+#define MT6358_ZCD_CON5 0x2492
+#define MT6358_ACCDET_CON13 0x2522
+
+#define MT6358_MAX_REGISTER MT6358_ZCD_CON5
+
+enum {
+ MT6358_MTKAIF_PROTOCOL_1 = 0,
+ MT6358_MTKAIF_PROTOCOL_2,
+ MT6358_MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+/* set only during init */
+int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol);
+#endif /* __MT6358_H__ */
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c
new file mode 100644
index 000000000000..ed34cc15b80e
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.c
@@ -0,0 +1,1062 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359-accdet.c -- ALSA SoC mt6359 accdet driver
+//
+// Copyright (C) 2021 MediaTek Inc.
+// Author: Argus Lin <argus.lin@mediatek.com>
+//
+
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/sched/clock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/mfd/mt6397/core.h>
+
+#include "mt6359-accdet.h"
+#include "mt6359.h"
+
+/* global variable definitions */
+#define REGISTER_VAL(x) ((x) - 1)
+
+/* mt6359 accdet capability */
+#define ACCDET_PMIC_EINT_IRQ BIT(0)
+#define ACCDET_AP_GPIO_EINT BIT(1)
+
+#define ACCDET_PMIC_EINT0 BIT(2)
+#define ACCDET_PMIC_EINT1 BIT(3)
+#define ACCDET_PMIC_BI_EINT BIT(4)
+
+#define ACCDET_PMIC_GPIO_TRIG_EINT BIT(5)
+#define ACCDET_PMIC_INVERTER_TRIG_EINT BIT(6)
+#define ACCDET_PMIC_RSV_EINT BIT(7)
+
+#define ACCDET_THREE_KEY BIT(8)
+#define ACCDET_FOUR_KEY BIT(9)
+#define ACCDET_TRI_KEY_CDD BIT(10)
+#define ACCDET_RSV_KEY BIT(11)
+
+#define ACCDET_ANALOG_FASTDISCHARGE BIT(12)
+#define ACCDET_DIGITAL_FASTDISCHARGE BIT(13)
+#define ACCDET_AD_FASTDISCHRAGE BIT(14)
+
+static struct platform_driver mt6359_accdet_driver;
+static const struct snd_soc_component_driver mt6359_accdet_soc_driver;
+
+/* local function declaration */
+static void accdet_set_debounce(struct mt6359_accdet *priv, int state,
+ unsigned int debounce);
+static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv);
+static void config_digital_init_by_mode(struct mt6359_accdet *priv);
+static void config_eint_init_by_mode(struct mt6359_accdet *priv);
+static inline void mt6359_accdet_init(struct mt6359_accdet *priv);
+static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv);
+static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv);
+static void mt6359_accdet_jack_report(struct mt6359_accdet *priv);
+static void recover_eint_analog_setting(struct mt6359_accdet *priv);
+static void recover_eint_digital_setting(struct mt6359_accdet *priv);
+static void recover_eint_setting(struct mt6359_accdet *priv);
+
+static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv)
+{
+ if (priv->data->eint_detect_mode == 0x3 ||
+ priv->data->eint_detect_mode == 0x4) {
+ /* ESD switches off */
+ regmap_update_bits(priv->regmap,
+ RG_ACCDETSPARE_ADDR, 1 << 8, 0);
+ }
+ if (priv->data->eint_detect_mode == 0x4) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* enable RG_EINT0CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT0CONFIGACCDET_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* enable RG_EINT1CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT1CONFIGACCDET_SFT));
+ }
+ if (priv->data->eint_use_ext_res == 0x3 ||
+ priv->data->eint_use_ext_res == 0x4) {
+ /*select 500k, use internal resistor */
+ regmap_update_bits(priv->regmap,
+ RG_EINT0HIRENB_ADDR,
+ RG_EINT0HIRENB_MASK_SFT,
+ BIT(RG_EINT0HIRENB_SFT));
+ }
+ }
+ return 0;
+}
+
+static unsigned int adjust_eint_digital_setting(struct mt6359_accdet *priv)
+{
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* disable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* disable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, 0);
+ }
+
+ if (priv->data->eint_detect_mode == 0x4) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* set DA stable signal */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT0_CEN_STABLE_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* set DA stable signal */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT1_CEN_STABLE_MASK_SFT, 0);
+ }
+ }
+ return 0;
+}
+
+static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv)
+{
+ if (priv->jd_sts == M_PLUG_IN) {
+ /* adjust digital setting */
+ adjust_eint_digital_setting(priv);
+ /* adjust analog setting */
+ adjust_eint_analog_setting(priv);
+ } else if (priv->jd_sts == M_PLUG_OUT) {
+ /* set debounce to 1ms */
+ accdet_set_debounce(priv, eint_state000,
+ priv->data->pwm_deb->eint_debounce0);
+ } else {
+ dev_dbg(priv->dev, "should not be here %s()\n", __func__);
+ }
+
+ return 0;
+}
+
+static void recover_eint_analog_setting(struct mt6359_accdet *priv)
+{
+ if (priv->data->eint_detect_mode == 0x3 ||
+ priv->data->eint_detect_mode == 0x4) {
+ /* ESD switches on */
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 1 << 8, 1 << 8);
+ }
+ if (priv->data->eint_detect_mode == 0x4) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* disable RG_EINT0CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* disable RG_EINT1CONFIGACCDET */
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT, 0);
+ }
+ regmap_update_bits(priv->regmap, RG_EINT0HIRENB_ADDR,
+ RG_EINT0HIRENB_MASK_SFT, 0);
+ }
+}
+
+static void recover_eint_digital_setting(struct mt6359_accdet *priv)
+{
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_M_SW_EN_ADDR,
+ ACCDET_EINT0_M_SW_EN_MASK_SFT, 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_M_SW_EN_ADDR,
+ ACCDET_EINT1_M_SW_EN_MASK_SFT, 0);
+ }
+ if (priv->data->eint_detect_mode == 0x4) {
+ /* enable eint0cen */
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* enable eint0cen */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT0_CEN_STABLE_MASK_SFT,
+ BIT(ACCDET_EINT0_CEN_STABLE_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* enable eint1cen */
+ regmap_update_bits(priv->regmap,
+ ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT1_CEN_STABLE_MASK_SFT,
+ BIT(ACCDET_EINT1_CEN_STABLE_SFT));
+ }
+ }
+
+ if (priv->data->eint_detect_mode != 0x1) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ /* enable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ /* enable inverter */
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT));
+ }
+ }
+}
+
+static void recover_eint_setting(struct mt6359_accdet *priv)
+{
+ if (priv->jd_sts == M_PLUG_OUT) {
+ recover_eint_analog_setting(priv);
+ recover_eint_digital_setting(priv);
+ }
+}
+
+static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv)
+{
+ int ret;
+ unsigned int value = 0;
+
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT, BIT(ACCDET_IRQ_CLR_SFT));
+ usleep_range(200, 300);
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret)
+ dev_warn(priv->dev, "%s(), ret %d\n", __func__, ret);
+ /* clear accdet int, modify for fix interrupt trigger twice error */
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_SFT));
+
+ /* recover accdet debounce0,3 */
+ accdet_set_debounce(priv, accdet_state000,
+ priv->data->pwm_deb->debounce0);
+ accdet_set_debounce(priv, accdet_state001,
+ priv->data->pwm_deb->debounce1);
+ accdet_set_debounce(priv, accdet_state011,
+ priv->data->pwm_deb->debounce3);
+
+ priv->jack_type = 0;
+ priv->btn_type = 0;
+ priv->accdet_status = 0x3;
+ mt6359_accdet_jack_report(priv);
+}
+
+static void accdet_set_debounce(struct mt6359_accdet *priv, int state,
+ unsigned int debounce)
+{
+ switch (state) {
+ case accdet_state000:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE0_ADDR, debounce);
+ break;
+ case accdet_state001:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE1_ADDR, debounce);
+ break;
+ case accdet_state010:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE2_ADDR, debounce);
+ break;
+ case accdet_state011:
+ regmap_write(priv->regmap, ACCDET_DEBOUNCE3_ADDR, debounce);
+ break;
+ case accdet_auxadc:
+ regmap_write(priv->regmap,
+ ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR, debounce);
+ break;
+ case eint_state000:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE0_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE0_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE0_SFT);
+ break;
+ case eint_state001:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE1_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE1_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE1_SFT);
+ break;
+ case eint_state010:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE2_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE2_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE2_SFT);
+ break;
+ case eint_state011:
+ regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE3_ADDR,
+ 0xF << ACCDET_EINT_DEBOUNCE3_SFT,
+ debounce << ACCDET_EINT_DEBOUNCE3_SFT);
+ break;
+ case eint_inverter_state000:
+ regmap_write(priv->regmap, ACCDET_EINT_INVERTER_DEBOUNCE_ADDR,
+ debounce);
+ break;
+ default:
+ dev_warn(priv->dev, "Error: %s error state (%d)\n", __func__,
+ state);
+ break;
+ }
+}
+
+static void mt6359_accdet_jack_report(struct mt6359_accdet *priv)
+{
+ int report = 0;
+
+ if (!priv->jack)
+ return;
+
+ report = priv->jack_type | priv->btn_type;
+ snd_soc_jack_report(priv->jack, report, MT6359_ACCDET_JACK_MASK);
+}
+
+static unsigned int check_button(struct mt6359_accdet *priv, unsigned int v)
+{
+ if (priv->caps & ACCDET_FOUR_KEY) {
+ if (v < priv->data->four_key.down &&
+ v >= priv->data->four_key.up)
+ priv->btn_type = SND_JACK_BTN_1;
+ if (v < priv->data->four_key.up &&
+ v >= priv->data->four_key.voice)
+ priv->btn_type = SND_JACK_BTN_2;
+ if (v < priv->data->four_key.voice &&
+ v >= priv->data->four_key.mid)
+ priv->btn_type = SND_JACK_BTN_3;
+ if (v < priv->data->four_key.mid)
+ priv->btn_type = SND_JACK_BTN_0;
+ } else {
+ if (v < priv->data->three_key.down &&
+ v >= priv->data->three_key.up)
+ priv->btn_type = SND_JACK_BTN_1;
+ if (v < priv->data->three_key.up &&
+ v >= priv->data->three_key.mid)
+ priv->btn_type = SND_JACK_BTN_2;
+ if (v < priv->data->three_key.mid)
+ priv->btn_type = SND_JACK_BTN_0;
+ }
+ return 0;
+}
+
+static void is_key_pressed(struct mt6359_accdet *priv, bool pressed)
+{
+ priv->btn_type = priv->jack_type & ~MT6359_ACCDET_BTN_MASK;
+
+ if (pressed)
+ check_button(priv, priv->cali_voltage);
+}
+
+static inline void check_jack_btn_type(struct mt6359_accdet *priv)
+{
+ unsigned int val = 0;
+
+ regmap_read(priv->regmap, ACCDET_MEM_IN_ADDR, &val);
+
+ priv->accdet_status =
+ (val >> ACCDET_STATE_MEM_IN_OFFSET) & ACCDET_STATE_AB_MASK;
+
+ switch (priv->accdet_status) {
+ case 0:
+ if (priv->jack_type == SND_JACK_HEADSET)
+ is_key_pressed(priv, true);
+ else
+ priv->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 1:
+ if (priv->jack_type == SND_JACK_HEADSET) {
+ is_key_pressed(priv, false);
+ } else {
+ priv->jack_type = SND_JACK_HEADSET;
+ accdet_set_debounce(priv, eint_state011, 0x1);
+ }
+ break;
+ case 3:
+ default:
+ priv->jack_type = 0;
+ break;
+ }
+}
+
+static void mt6359_accdet_work(struct work_struct *work)
+{
+ struct mt6359_accdet *priv =
+ container_of(work, struct mt6359_accdet, accdet_work);
+
+ mutex_lock(&priv->res_lock);
+ priv->pre_accdet_status = priv->accdet_status;
+ check_jack_btn_type(priv);
+
+ if (priv->jack_plugged &&
+ priv->pre_accdet_status != priv->accdet_status)
+ mt6359_accdet_jack_report(priv);
+ mutex_unlock(&priv->res_lock);
+}
+
+static void mt6359_accdet_jd_work(struct work_struct *work)
+{
+ int ret;
+ unsigned int value = 0;
+
+ struct mt6359_accdet *priv =
+ container_of(work, struct mt6359_accdet, jd_work);
+
+ mutex_lock(&priv->res_lock);
+ if (priv->jd_sts == M_PLUG_IN) {
+ priv->jack_plugged = true;
+
+ /* set and clear initial bit every eint interrupt */
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT,
+ BIT(ACCDET_SEQ_INIT_SFT));
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT, 0);
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_SEQ_INIT_ADDR,
+ value,
+ (value & ACCDET_SEQ_INIT_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret)
+ dev_err(priv->dev, "%s(), ret %d\n", __func__, ret);
+
+ /* enable ACCDET unit */
+ regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR,
+ ACCDET_SW_EN_MASK_SFT, BIT(ACCDET_SW_EN_SFT));
+ } else if (priv->jd_sts == M_PLUG_OUT) {
+ priv->jack_plugged = false;
+
+ accdet_set_debounce(priv, accdet_state011,
+ priv->data->pwm_deb->debounce3);
+ regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR,
+ ACCDET_SW_EN_MASK_SFT, 0);
+ mt6359_accdet_recover_jd_setting(priv);
+ }
+
+ if (priv->caps & ACCDET_PMIC_EINT_IRQ)
+ recover_eint_setting(priv);
+ mutex_unlock(&priv->res_lock);
+}
+
+static irqreturn_t mt6359_accdet_irq(int irq, void *data)
+{
+ struct mt6359_accdet *priv = data;
+ unsigned int irq_val = 0, val = 0, value = 0;
+ int ret;
+
+ mutex_lock(&priv->res_lock);
+ regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val);
+
+ if (irq_val & ACCDET_IRQ_MASK_SFT) {
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT,
+ BIT(ACCDET_IRQ_CLR_SFT));
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret) {
+ dev_err(priv->dev, "%s(), ret %d\n", __func__, ret);
+ mutex_unlock(&priv->res_lock);
+ return IRQ_NONE;
+ }
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_SFT));
+
+ queue_work(priv->accdet_workqueue, &priv->accdet_work);
+ } else {
+ if (irq_val & ACCDET_EINT0_IRQ_MASK_SFT) {
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT0_IRQ_CLR_MASK_SFT,
+ BIT(ACCDET_EINT0_IRQ_CLR_SFT));
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_EINT0_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret) {
+ dev_err(priv->dev, "%s(), ret %d\n", __func__,
+ ret);
+ mutex_unlock(&priv->res_lock);
+ return IRQ_NONE;
+ }
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT0_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap,
+ RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_EINT0_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_EINT0_SFT));
+ }
+ if (irq_val & ACCDET_EINT1_IRQ_MASK_SFT) {
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT1_IRQ_CLR_MASK_SFT,
+ BIT(ACCDET_EINT1_IRQ_CLR_SFT));
+ ret = regmap_read_poll_timeout(priv->regmap,
+ ACCDET_IRQ_ADDR,
+ value,
+ (value & ACCDET_EINT1_IRQ_MASK_SFT) == 0,
+ 0,
+ 1000);
+ if (ret) {
+ dev_err(priv->dev, "%s(), ret %d\n", __func__,
+ ret);
+ mutex_unlock(&priv->res_lock);
+ return IRQ_NONE;
+ }
+ regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+ ACCDET_EINT1_IRQ_CLR_MASK_SFT, 0);
+ regmap_update_bits(priv->regmap,
+ RG_INT_STATUS_ACCDET_ADDR,
+ RG_INT_STATUS_ACCDET_EINT1_MASK_SFT,
+ BIT(RG_INT_STATUS_ACCDET_EINT1_SFT));
+ }
+ /* get jack detection status */
+ regmap_read(priv->regmap, ACCDET_EINT0_MEM_IN_ADDR, &val);
+ priv->jd_sts = ((val >> ACCDET_EINT0_MEM_IN_SFT) &
+ ACCDET_EINT0_MEM_IN_MASK);
+ /* adjust eint digital/analog setting */
+ mt6359_accdet_jd_setting(priv);
+
+ queue_work(priv->jd_workqueue, &priv->jd_work);
+ }
+ mutex_unlock(&priv->res_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int mt6359_accdet_parse_dt(struct mt6359_accdet *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+ struct device_node *node = NULL;
+ int pwm_deb[15] = {0};
+ unsigned int tmp = 0;
+
+ node = of_get_child_by_name(dev->parent->of_node, "accdet");
+ if (!node)
+ return -EINVAL;
+
+ ret = of_property_read_u32(node, "mediatek,mic-vol",
+ &priv->data->mic_vol);
+ if (ret)
+ priv->data->mic_vol = 8;
+
+ ret = of_property_read_u32(node, "mediatek,plugout-debounce",
+ &priv->data->plugout_deb);
+ if (ret)
+ priv->data->plugout_deb = 1;
+
+ ret = of_property_read_u32(node, "mediatek,mic-mode",
+ &priv->data->mic_mode);
+ if (ret)
+ priv->data->mic_mode = 2;
+
+ ret = of_property_read_u32_array(node, "mediatek,pwm-deb-setting",
+ pwm_deb, ARRAY_SIZE(pwm_deb));
+ /* debounce8(auxadc debounce) is default, needn't get from dts */
+ if (!ret)
+ memcpy(priv->data->pwm_deb, pwm_deb, sizeof(pwm_deb));
+
+ ret = of_property_read_u32(node, "mediatek,eint-level-pol",
+ &priv->data->eint_pol);
+ if (ret)
+ priv->data->eint_pol = 8;
+
+ ret = of_property_read_u32(node, "mediatek,eint-use-ap", &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0)
+ priv->caps |= ACCDET_PMIC_EINT_IRQ;
+ else if (tmp == 1)
+ priv->caps |= ACCDET_AP_GPIO_EINT;
+
+ ret = of_property_read_u32(node, "mediatek,eint-detect-mode",
+ &priv->data->eint_detect_mode);
+ if (ret) {
+ /* eint detection mode equals to EINT HW Mode */
+ priv->data->eint_detect_mode = 0x4;
+ }
+
+ ret = of_property_read_u32(node, "mediatek,eint-num", &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0)
+ priv->caps |= ACCDET_PMIC_EINT0;
+ else if (tmp == 1)
+ priv->caps |= ACCDET_PMIC_EINT1;
+ else if (tmp == 2)
+ priv->caps |= ACCDET_PMIC_BI_EINT;
+
+ ret = of_property_read_u32(node, "mediatek,eint-trig-mode",
+ &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0)
+ priv->caps |= ACCDET_PMIC_GPIO_TRIG_EINT;
+ else if (tmp == 1)
+ priv->caps |= ACCDET_PMIC_INVERTER_TRIG_EINT;
+
+ ret = of_property_read_u32(node, "mediatek,eint-use-ext-res",
+ &priv->data->eint_use_ext_res);
+ if (ret) {
+ /* eint use internal resister */
+ priv->data->eint_use_ext_res = 0x0;
+ }
+
+ ret = of_property_read_u32(node, "mediatek,eint-comp-vth",
+ &priv->data->eint_comp_vth);
+ if (ret)
+ priv->data->eint_comp_vth = 0x0;
+
+ ret = of_property_read_u32(node, "mediatek,key-mode", &tmp);
+ if (ret)
+ tmp = 0;
+ if (tmp == 0) {
+ int three_key[4];
+
+ priv->caps |= ACCDET_THREE_KEY;
+ ret = of_property_read_u32_array(node,
+ "mediatek,three-key-thr",
+ three_key,
+ ARRAY_SIZE(three_key));
+ if (!ret)
+ memcpy(&priv->data->three_key, three_key + 1,
+ sizeof(struct three_key_threshold));
+ } else if (tmp == 1) {
+ int four_key[5];
+
+ priv->caps |= ACCDET_FOUR_KEY;
+ ret = of_property_read_u32_array(node,
+ "mediatek,four-key-thr",
+ four_key,
+ ARRAY_SIZE(four_key));
+ if (!ret) {
+ memcpy(&priv->data->four_key, four_key + 1,
+ sizeof(struct four_key_threshold));
+ } else {
+ dev_warn(priv->dev,
+ "accdet no 4-key-thrsh dts, use efuse\n");
+ }
+ } else if (tmp == 2) {
+ int three_key[4];
+
+ priv->caps |= ACCDET_TRI_KEY_CDD;
+ ret = of_property_read_u32_array(node,
+ "mediatek,tri-key-cdd-thr",
+ three_key,
+ ARRAY_SIZE(three_key));
+ if (!ret)
+ memcpy(&priv->data->three_key, three_key + 1,
+ sizeof(struct three_key_threshold));
+ }
+
+ of_node_put(node);
+ dev_warn(priv->dev, "accdet caps=%x\n", priv->caps);
+
+ return 0;
+}
+
+static void config_digital_init_by_mode(struct mt6359_accdet *priv)
+{
+ /* enable eint cmpmem pwm */
+ regmap_write(priv->regmap, ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR,
+ (priv->data->pwm_deb->eint_pwm_width << 4 |
+ priv->data->pwm_deb->eint_pwm_thresh));
+ /* DA signal stable */
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT0_STABLE_VAL);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR,
+ ACCDET_EINT1_STABLE_VAL);
+ }
+ /* after receive n+1 number, interrupt issued. */
+ regmap_update_bits(priv->regmap, ACCDET_EINT_M_PLUG_IN_NUM_ADDR,
+ ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT,
+ BIT(ACCDET_EINT_M_PLUG_IN_NUM_SFT));
+ /* setting HW mode, enable digital fast discharge
+ * if use EINT0 & EINT1 detection, please modify
+ * ACCDET_HWMODE_EN_ADDR[2:1]
+ */
+ regmap_write(priv->regmap, ACCDET_HWMODE_EN_ADDR, 0x100);
+
+ regmap_update_bits(priv->regmap, ACCDET_EINT_M_DETECT_EN_ADDR,
+ ACCDET_EINT_M_DETECT_EN_MASK_SFT, 0);
+
+ /* enable PWM */
+ regmap_write(priv->regmap, ACCDET_CMP_PWM_EN_ADDR, 0x67);
+ /* enable inverter detection */
+ if (priv->data->eint_detect_mode == 0x1) {
+ /* disable inverter detection */
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+ 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+ 0);
+ }
+ } else {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+ ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+ BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT));
+ }
+ }
+}
+
+static void config_eint_init_by_mode(struct mt6359_accdet *priv)
+{
+ unsigned int val = 0;
+
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap, RG_EINT0EN_ADDR,
+ RG_EINT0EN_MASK_SFT, BIT(RG_EINT0EN_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap, RG_EINT1EN_ADDR,
+ RG_EINT1EN_MASK_SFT, BIT(RG_EINT1EN_SFT));
+ }
+ /* ESD switches on */
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 1 << 8, 1 << 8);
+ /* before playback, set NCP pull low before nagative voltage */
+ regmap_update_bits(priv->regmap, RG_NCP_PDDIS_EN_ADDR,
+ RG_NCP_PDDIS_EN_MASK_SFT, BIT(RG_NCP_PDDIS_EN_SFT));
+
+ if (priv->data->eint_detect_mode == 0x1 ||
+ priv->data->eint_detect_mode == 0x2 ||
+ priv->data->eint_detect_mode == 0x3) {
+ if (priv->data->eint_use_ext_res == 0x1) {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT,
+ 0);
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT,
+ 0);
+ }
+ } else {
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT0CONFIGACCDET_ADDR,
+ RG_EINT0CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT0CONFIGACCDET_SFT));
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ regmap_update_bits(priv->regmap,
+ RG_EINT1CONFIGACCDET_ADDR,
+ RG_EINT1CONFIGACCDET_MASK_SFT,
+ BIT(RG_EINT1CONFIGACCDET_SFT));
+ }
+ }
+ }
+
+ if (priv->data->eint_detect_mode != 0x1) {
+ /* current detect set 0.25uA */
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 0x3 << RG_ACCDETSPARE_SFT,
+ 0x3 << RG_ACCDETSPARE_SFT);
+ }
+ regmap_write(priv->regmap, RG_EINTCOMPVTH_ADDR,
+ val | priv->data->eint_comp_vth << RG_EINTCOMPVTH_SFT);
+}
+
+static void mt6359_accdet_init(struct mt6359_accdet *priv)
+{
+ unsigned int reg = 0;
+
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT, BIT(ACCDET_SEQ_INIT_SFT));
+ mdelay(2);
+ regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+ ACCDET_SEQ_INIT_MASK_SFT, 0);
+ mdelay(1);
+ /* init the debounce time (debounce/32768)sec */
+ accdet_set_debounce(priv, accdet_state000,
+ priv->data->pwm_deb->debounce0);
+ accdet_set_debounce(priv, accdet_state001,
+ priv->data->pwm_deb->debounce1);
+ accdet_set_debounce(priv, accdet_state011,
+ priv->data->pwm_deb->debounce3);
+ accdet_set_debounce(priv, accdet_auxadc,
+ priv->data->pwm_deb->debounce4);
+
+ accdet_set_debounce(priv, eint_state000,
+ priv->data->pwm_deb->eint_debounce0);
+ accdet_set_debounce(priv, eint_state001,
+ priv->data->pwm_deb->eint_debounce1);
+ accdet_set_debounce(priv, eint_state011,
+ priv->data->pwm_deb->eint_debounce3);
+ accdet_set_debounce(priv, eint_inverter_state000,
+ priv->data->pwm_deb->eint_inverter_debounce);
+
+ regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR,
+ RG_ACCDET_RST_MASK_SFT, BIT(RG_ACCDET_RST_SFT));
+ regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR,
+ RG_ACCDET_RST_MASK_SFT, 0);
+
+ /* clear high micbias1 voltage setting */
+ regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ 0x3 << RG_AUDMICBIAS1HVEN_SFT, 0);
+ regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ 0x7 << RG_AUDMICBIAS1VREF_SFT, 0);
+
+ /* init pwm frequency, duty & rise/falling delay */
+ regmap_write(priv->regmap, ACCDET_PWM_WIDTH_ADDR,
+ REGISTER_VAL(priv->data->pwm_deb->pwm_width));
+ regmap_write(priv->regmap, ACCDET_PWM_THRESH_ADDR,
+ REGISTER_VAL(priv->data->pwm_deb->pwm_thresh));
+ regmap_write(priv->regmap, ACCDET_RISE_DELAY_ADDR,
+ (priv->data->pwm_deb->fall_delay << 15 |
+ priv->data->pwm_deb->rise_delay));
+
+ regmap_read(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, &reg);
+ if (priv->data->mic_vol <= 7) {
+ /* micbias1 <= 2.7V */
+ regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ reg | (priv->data->mic_vol << RG_AUDMICBIAS1VREF_SFT) |
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+ } else if (priv->data->mic_vol == 8) {
+ /* micbias1 = 2.8v */
+ regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ reg | (3 << RG_AUDMICBIAS1HVEN_SFT) |
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+ } else if (priv->data->mic_vol == 9) {
+ /* micbias1 = 2.85v */
+ regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ reg | (1 << RG_AUDMICBIAS1HVEN_SFT) |
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+ }
+ /* mic mode setting */
+ regmap_read(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, &reg);
+ if (priv->data->mic_mode == HEADSET_MODE_1) {
+ /* ACC mode*/
+ regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+ reg | RG_ACCDET_MODE_ANA11_MODE1);
+ /* enable analog fast discharge */
+ regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+ RG_ANALOGFDEN_MASK_SFT,
+ BIT(RG_ANALOGFDEN_SFT));
+ regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+ 0x3 << 11, 0x3 << 11);
+ } else if (priv->data->mic_mode == HEADSET_MODE_2) {
+ /* DCC mode Low cost mode without internal bias */
+ regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+ reg | RG_ACCDET_MODE_ANA11_MODE2);
+ /* enable analog fast discharge */
+ regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+ 0x3 << RG_ANALOGFDEN_SFT,
+ 0x3 << RG_ANALOGFDEN_SFT);
+ } else if (priv->data->mic_mode == HEADSET_MODE_6) {
+ /* DCC mode Low cost mode with internal bias,
+ * bit8 = 1 to use internal bias
+ */
+ regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+ reg | RG_ACCDET_MODE_ANA11_MODE6);
+ regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+ RG_AUDMICBIAS1DCSW1PEN_MASK_SFT,
+ BIT(RG_AUDMICBIAS1DCSW1PEN_SFT));
+ /* enable analog fast discharge */
+ regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+ 0x3 << RG_ANALOGFDEN_SFT,
+ 0x3 << RG_ANALOGFDEN_SFT);
+ }
+
+ if (priv->caps & ACCDET_PMIC_EINT_IRQ) {
+ config_eint_init_by_mode(priv);
+ config_digital_init_by_mode(priv);
+ }
+}
+
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct mt6359_accdet *priv =
+ snd_soc_component_get_drvdata(component);
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ priv->jack = jack;
+
+ mt6359_accdet_jack_report(priv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt6359_accdet_enable_jack_detect);
+
+static int mt6359_accdet_probe(struct platform_device *pdev)
+{
+ struct mt6359_accdet *priv;
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct mt6359_accdet),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = devm_kzalloc(&pdev->dev, sizeof(struct dts_data),
+ GFP_KERNEL);
+ if (!priv->data)
+ return -ENOMEM;
+
+ priv->data->pwm_deb = devm_kzalloc(&pdev->dev,
+ sizeof(struct pwm_deb_settings),
+ GFP_KERNEL);
+ if (!priv->data->pwm_deb)
+ return -ENOMEM;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&pdev->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ priv->dev = &pdev->dev;
+
+ ret = mt6359_accdet_parse_dt(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to parse dts\n");
+ return ret;
+ }
+ mutex_init(&priv->res_lock);
+
+ priv->accdet_irq = platform_get_irq(pdev, 0);
+ if (priv->accdet_irq >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, priv->accdet_irq,
+ NULL, mt6359_accdet_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ACCDET_IRQ", priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request IRQ: (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (priv->caps & ACCDET_PMIC_EINT0) {
+ priv->accdet_eint0 = platform_get_irq(pdev, 1);
+ if (priv->accdet_eint0 >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ priv->accdet_eint0,
+ NULL, mt6359_accdet_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ACCDET_EINT0", priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request eint0 IRQ (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+ } else if (priv->caps & ACCDET_PMIC_EINT1) {
+ priv->accdet_eint1 = platform_get_irq(pdev, 2);
+ if (priv->accdet_eint1 >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ priv->accdet_eint1,
+ NULL, mt6359_accdet_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ACCDET_EINT1", priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request eint1 IRQ (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+
+ priv->accdet_workqueue = create_singlethread_workqueue("accdet");
+ INIT_WORK(&priv->accdet_work, mt6359_accdet_work);
+ if (!priv->accdet_workqueue) {
+ dev_err(&pdev->dev, "Failed to create accdet workqueue\n");
+ ret = -1;
+ goto err_accdet_wq;
+ }
+
+ priv->jd_workqueue = create_singlethread_workqueue("mt6359_accdet_jd");
+ INIT_WORK(&priv->jd_work, mt6359_accdet_jd_work);
+ if (!priv->jd_workqueue) {
+ dev_err(&pdev->dev, "Failed to create jack detect workqueue\n");
+ ret = -1;
+ goto err_eint_wq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mt6359_accdet_soc_driver,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register component\n");
+ return ret;
+ }
+
+ priv->jd_sts = M_PLUG_OUT;
+ priv->jack_type = 0;
+ priv->btn_type = 0;
+ priv->accdet_status = 0x3;
+ mt6359_accdet_init(priv);
+
+ mt6359_accdet_jack_report(priv);
+
+ return 0;
+
+err_eint_wq:
+ destroy_workqueue(priv->accdet_workqueue);
+err_accdet_wq:
+ dev_err(&pdev->dev, "%s error. now exit.!\n", __func__);
+ return ret;
+}
+
+static struct platform_driver mt6359_accdet_driver = {
+ .driver = {
+ .name = "pmic-codec-accdet",
+ },
+ .probe = mt6359_accdet_probe,
+};
+
+module_platform_driver(mt6359_accdet_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6359 ALSA SoC codec jack driver");
+MODULE_AUTHOR("Argus Lin <argus.lin@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6359-accdet.h b/sound/soc/codecs/mt6359-accdet.h
new file mode 100644
index 000000000000..78ada3a5bfae
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 MediaTek Inc.
+ * Author: Argus Lin <argus.lin@mediatek.com>
+ */
+
+#ifndef _ACCDET_H_
+#define _ACCDET_H_
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#define ACCDET_DEVNAME "accdet"
+
+#define HEADSET_MODE_1 (1)
+#define HEADSET_MODE_2 (2)
+#define HEADSET_MODE_6 (6)
+
+#define MT6359_ACCDET_NUM_BUTTONS 4
+#define MT6359_ACCDET_JACK_MASK (SND_JACK_HEADPHONE | \
+ SND_JACK_HEADSET | \
+ SND_JACK_BTN_0 | \
+ SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3)
+#define MT6359_ACCDET_BTN_MASK (SND_JACK_BTN_0 | \
+ SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3)
+
+enum eint_moisture_status {
+ M_PLUG_IN = 0,
+ M_WATER_IN = 1,
+ M_HP_PLUG_IN = 2,
+ M_PLUG_OUT = 3,
+ M_NO_ACT = 4,
+ M_UNKNOWN = 5,
+};
+
+enum {
+ accdet_state000 = 0,
+ accdet_state001,
+ accdet_state010,
+ accdet_state011,
+ accdet_auxadc,
+ eint_state000,
+ eint_state001,
+ eint_state010,
+ eint_state011,
+ eint_inverter_state000,
+};
+
+struct three_key_threshold {
+ unsigned int mid;
+ unsigned int up;
+ unsigned int down;
+};
+
+struct four_key_threshold {
+ unsigned int mid;
+ unsigned int voice;
+ unsigned int up;
+ unsigned int down;
+};
+
+struct pwm_deb_settings {
+ unsigned int pwm_width;
+ unsigned int pwm_thresh;
+ unsigned int fall_delay;
+ unsigned int rise_delay;
+ unsigned int debounce0;
+ unsigned int debounce1;
+ unsigned int debounce3;
+ unsigned int debounce4;
+ unsigned int eint_pwm_width;
+ unsigned int eint_pwm_thresh;
+ unsigned int eint_debounce0;
+ unsigned int eint_debounce1;
+ unsigned int eint_debounce2;
+ unsigned int eint_debounce3;
+ unsigned int eint_inverter_debounce;
+
+};
+
+struct dts_data {
+ unsigned int mic_vol;
+ unsigned int mic_mode;
+ unsigned int plugout_deb;
+ unsigned int eint_pol;
+ struct pwm_deb_settings *pwm_deb;
+ struct three_key_threshold three_key;
+ struct four_key_threshold four_key;
+ unsigned int moisture_detect_enable;
+ unsigned int eint_detect_mode;
+ unsigned int eint_use_ext_res;
+ unsigned int eint_comp_vth;
+ unsigned int moisture_detect_mode;
+ unsigned int moisture_comp_vth;
+ unsigned int moisture_comp_vref2;
+ unsigned int moisture_use_ext_res;
+};
+
+struct mt6359_accdet {
+ struct snd_soc_jack *jack;
+ struct device *dev;
+ struct regmap *regmap;
+ struct dts_data *data;
+ unsigned int caps;
+ int accdet_irq;
+ int accdet_eint0;
+ int accdet_eint1;
+ struct mutex res_lock; /* lock protection */
+ bool jack_plugged;
+ unsigned int jack_type;
+ unsigned int btn_type;
+ unsigned int accdet_status;
+ unsigned int pre_accdet_status;
+ unsigned int cali_voltage;
+ unsigned int jd_sts;
+ struct work_struct accdet_work;
+ struct workqueue_struct *accdet_workqueue;
+ struct work_struct jd_work;
+ struct workqueue_struct *jd_workqueue;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_MT6359_ACCDET)
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+#else
+static inline int
+mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+#endif
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
new file mode 100644
index 000000000000..b15bdb15dbb0
--- /dev/null
+++ b/sound/soc/codecs/mt6359.c
@@ -0,0 +1,2960 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359.c -- mt6359 ALSA SoC audio codec driver
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "mt6359.h"
+
+static void mt6359_set_gpio_smt(struct mt6359_priv *priv)
+{
+ /* set gpio SMT mode */
+ regmap_update_bits(priv->regmap, MT6359_SMT_CON1, 0x3ff0, 0x3ff0);
+}
+
+static void mt6359_set_gpio_driving(struct mt6359_priv *priv)
+{
+ /* 8:4mA(default), a:8mA, c:12mA, e:16mA */
+ regmap_update_bits(priv->regmap, MT6359_DRV_CON2, 0xffff, 0x8888);
+ regmap_update_bits(priv->regmap, MT6359_DRV_CON3, 0xffff, 0x8888);
+ regmap_update_bits(priv->regmap, MT6359_DRV_CON4, 0x00ff, 0x88);
+}
+
+static void mt6359_set_playback_gpio(struct mt6359_priv *priv)
+{
+ /* set gpio mosi mode, clk / data mosi */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ffe);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE2_SET, 0x0249);
+
+ /* sync mosi */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x6);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x1);
+}
+
+static void mt6359_reset_playback_gpio(struct mt6359_priv *priv)
+{
+ /* set pad_aud_*_mosi to GPIO mode and dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ * don't clean clk/sync, for mtkaif protocol 2
+ */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ff8);
+ regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0, 0x7 << 9, 0x0);
+}
+
+static void mt6359_set_capture_gpio(struct mt6359_priv *priv)
+{
+ /* set gpio miso mode */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x0200);
+
+ regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f);
+ regmap_write(priv->regmap, MT6359_GPIO_MODE4_SET, 0x0009);
+}
+
+static void mt6359_reset_capture_gpio(struct mt6359_priv *priv)
+{
+ /* set pad_aud_*_miso to GPIO mode and dir input
+ * reason:
+ * pad_aud_clk_miso, because when playback only the miso_clk
+ * will also have 26m, so will have power leak
+ * pad_aud_dat_miso*, because the pin is used as boot strap
+ */
+ regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00);
+
+ regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f);
+
+ regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0,
+ 0x7 << 13, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_GPIO_DIR1,
+ 0x3 << 0, 0x0);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_dcxo(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_clksq(struct mt6359_priv *priv, bool enable)
+{
+ /* Enable/disable CLKSQ 26MHz */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23,
+ RG_CLKSQ_EN_MASK_SFT,
+ (enable ? 1 : 0) << RG_CLKSQ_EN_SFT);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_aud_global_bias(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA32_MASK_SFT,
+ (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA32_SFT);
+}
+
+/* use only when doing mtkaif calibraiton at the boot time */
+static void mt6359_set_topck(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_AUD_TOP_CKPDN_CON0,
+ 0x0066, enable ? 0x0 : 0x66);
+}
+
+static void mt6359_set_decoder_clk(struct mt6359_priv *priv, bool enable)
+{
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13,
+ RG_RSTB_DECODER_VA32_MASK_SFT,
+ (enable ? 1 : 0) << RG_RSTB_DECODER_VA32_SFT);
+}
+
+static void mt6359_mtkaif_tx_enable(struct mt6359_priv *priv)
+{
+ switch (priv->mtkaif_protocol) {
+ case MT6359_MTKAIF_PROTOCOL_2_CLK_P2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0210);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3800);
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3900);
+ break;
+ case MT6359_MTKAIF_PROTOCOL_2:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0210);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ case MT6359_MTKAIF_PROTOCOL_1:
+ default:
+ /* MTKAIF TX format setting */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_ADDA_MTKAIF_CFG0,
+ 0xffff, 0x0000);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap,
+ MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3100);
+ break;
+ }
+}
+
+static void mt6359_mtkaif_tx_disable(struct mt6359_priv *priv)
+{
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6359_AFE_AUD_PAD_TOP,
+ 0xff00, 0x3000);
+}
+
+void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ priv->mtkaif_protocol = mtkaif_protocol;
+}
+EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_protocol);
+
+void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ mt6359_set_playback_gpio(priv);
+ mt6359_set_capture_gpio(priv);
+ mt6359_mtkaif_tx_enable(priv);
+
+ mt6359_set_dcxo(priv, true);
+ mt6359_set_aud_global_bias(priv, true);
+ mt6359_set_clksq(priv, true);
+ mt6359_set_topck(priv, true);
+
+ /* set dat_miso_loopback on */
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1,
+ RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT,
+ 1 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT);
+}
+EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_enable);
+
+void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ /* set dat_miso_loopback off */
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1,
+ RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT,
+ 0 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT);
+
+ mt6359_set_topck(priv, false);
+ mt6359_set_clksq(priv, false);
+ mt6359_set_aud_global_bias(priv, false);
+ mt6359_set_dcxo(priv, false);
+
+ mt6359_mtkaif_tx_disable(priv);
+ mt6359_reset_playback_gpio(priv);
+ mt6359_reset_capture_gpio(priv);
+}
+EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_disable);
+
+void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
+ int phase_1, int phase_2, int phase_3)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT,
+ phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG,
+ RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT,
+ phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1,
+ RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT,
+ phase_3 << RG_AUD_PAD_TOP_PHASE_MODE3_SFT);
+}
+EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_calibration_phase);
+
+static void zcd_disable(struct mt6359_priv *priv)
+{
+ regmap_write(priv->regmap, MT6359_ZCD_CON0, 0x0000);
+}
+
+static void hp_main_output_ramp(struct mt6359_priv *priv, bool up)
+{
+ int i, stage;
+ int target = 7;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= target; i++) {
+ stage = up ? i : target - i;
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT,
+ stage << RG_HPLOUTSTGCTRL_VAUDP32_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT,
+ stage << RG_HPROUTSTGCTRL_VAUDP32_SFT);
+ usleep_range(600, 650);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6359_priv *priv, bool up)
+{
+ int i, stage;
+ int target = 0xf;
+
+ /* Enable/Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= target; i++) {
+ stage = up ? i : target - i;
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9,
+ 0xf << 12, stage << 12);
+ usleep_range(600, 650);
+ }
+}
+
+static void hp_in_pair_current(struct mt6359_priv *priv, bool increase)
+{
+ int i, stage;
+ int target = 0x3;
+
+ /* Set input diff pair bias select (Hi-Fi mode) */
+ if (priv->hp_hifi_mode) {
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= target; i++) {
+ stage = increase ? i : target - i;
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDDEC_ANA_CON10,
+ 0x3 << 3, stage << 3);
+ usleep_range(100, 150);
+ }
+ }
+}
+
+static void hp_pull_down(struct mt6359_priv *priv, bool enable)
+{
+ int i;
+
+ if (enable) {
+ for (i = 0x0; i <= 0x7; i++) {
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_HPPSHORT2VCM_VAUDP32_MASK_SFT,
+ i << RG_HPPSHORT2VCM_VAUDP32_SFT);
+ usleep_range(100, 150);
+ }
+ } else {
+ for (i = 0x7; i >= 0x0; i--) {
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_HPPSHORT2VCM_VAUDP32_MASK_SFT,
+ i << RG_HPPSHORT2VCM_VAUDP32_SFT);
+ usleep_range(100, 150);
+ }
+ }
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_22DB) ||
+ reg_idx == DL_GAIN_N_40DB;
+}
+
+static void headset_volume_ramp(struct mt6359_priv *priv,
+ int from, int to)
+{
+ int offset = 0, count = 1, reg_idx;
+
+ if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to)) {
+ dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n",
+ __func__, from, to);
+ return;
+ }
+
+ dev_dbg(priv->dev, "%s(), from %d, to %d\n", __func__, from, to);
+
+ if (to > from)
+ offset = to - from;
+ else
+ offset = from - to;
+
+ while (offset > 0) {
+ if (to > from)
+ reg_idx = from + count;
+ else
+ reg_idx = from - count;
+
+ if (is_valid_hp_pga_idx(reg_idx)) {
+ regmap_update_bits(priv->regmap,
+ MT6359_ZCD_CON2,
+ DL_GAIN_REG_MASK,
+ (reg_idx << 7) | reg_idx);
+ usleep_range(600, 650);
+ }
+ offset--;
+ count++;
+ }
+}
+
+static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = 0;
+ int index = ucontrol->value.integer.value[0];
+ int orig_gain[2], new_gain[2];
+ int ret;
+
+ switch (mc->reg) {
+ case MT6359_ZCD_CON2:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR];
+ break;
+ case MT6359_ZCD_CON1:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL];
+ orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR];
+ break;
+ case MT6359_ZCD_CON3:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL];
+ break;
+ case MT6359_AUDENC_ANA_CON0:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ break;
+ case MT6359_AUDENC_ANA_CON1:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+ break;
+ case MT6359_AUDENC_ANA_CON2:
+ orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ switch (mc->reg) {
+ case MT6359_ZCD_CON2:
+ regmap_read(priv->regmap, MT6359_ZCD_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] =
+ (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] =
+ (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR];
+ break;
+ case MT6359_ZCD_CON1:
+ regmap_read(priv->regmap, MT6359_ZCD_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] =
+ (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK;
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] =
+ (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL];
+ new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR];
+ break;
+ case MT6359_ZCD_CON3:
+ regmap_read(priv->regmap, MT6359_ZCD_CON3, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] =
+ (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL];
+ break;
+ case MT6359_AUDENC_ANA_CON0:
+ regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] =
+ (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ break;
+ case MT6359_AUDENC_ANA_CON1:
+ regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] =
+ (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+ break;
+ case MT6359_AUDENC_ANA_CON2:
+ regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] =
+ (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK;
+ new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3];
+ break;
+ }
+
+ ret = 0;
+ if (orig_gain[0] != new_gain[0]) {
+ ret = 1;
+ } else if (snd_soc_volsw_is_stereo(mc)) {
+ if (orig_gain[1] != new_gain[1])
+ ret = 1;
+ }
+
+ dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n",
+ __func__, kcontrol->id.name, mc->reg, reg, index);
+
+ return ret;
+}
+
+static int mt6359_get_playback_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ switch (mc->reg) {
+ case MT6359_ZCD_CON2:
+ ucontrol->value.integer.value[0] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL];
+ ucontrol->value.integer.value[1] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR];
+ break;
+ case MT6359_ZCD_CON1:
+ ucontrol->value.integer.value[0] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL];
+ ucontrol->value.integer.value[1] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR];
+ break;
+ case MT6359_ZCD_CON3:
+ ucontrol->value.integer.value[0] =
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* MUX */
+
+/* LOL MUX */
+static const char * const lo_in_mux_map[] = {
+ "Open", "Playback_L_DAC", "Playback", "Test Mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(lo_in_mux_map_enum, SND_SOC_NOPM, 0, lo_in_mux_map);
+
+static const struct snd_kcontrol_new lo_in_mux_control =
+ SOC_DAPM_ENUM("LO Select", lo_in_mux_map_enum);
+
+/*HP MUX */
+static const char * const hp_in_mux_map[] = {
+ "Open",
+ "LoudSPK Playback",
+ "Audio Playback",
+ "Test Mode",
+ "HP Impedance",
+};
+
+static SOC_ENUM_SINGLE_DECL(hp_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ hp_in_mux_map);
+
+static const struct snd_kcontrol_new hp_in_mux_control =
+ SOC_DAPM_ENUM("HP Select", hp_in_mux_map_enum);
+
+/* RCV MUX */
+static const char * const rcv_in_mux_map[] = {
+ "Open", "Mute", "Voice Playback", "Test Mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(rcv_in_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ rcv_in_mux_map);
+
+static const struct snd_kcontrol_new rcv_in_mux_control =
+ SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum);
+
+/* DAC In MUX */
+static const char * const dac_in_mux_map[] = {
+ "Normal Path", "Sgen"
+};
+
+static int dac_in_mux_map_value[] = {
+ 0x0, 0x1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum,
+ MT6359_AFE_TOP_CON0,
+ DL_SINE_ON_SFT,
+ DL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new dac_in_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum);
+
+/* AIF Out MUX */
+static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum,
+ MT6359_AFE_TOP_CON0,
+ UL_SINE_ON_SFT,
+ UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(aif2_out_mux_map_enum,
+ MT6359_AFE_TOP_CON0,
+ ADDA6_UL_SINE_ON_SFT,
+ ADDA6_UL_SINE_ON_MASK,
+ dac_in_mux_map,
+ dac_in_mux_map_value);
+
+static const struct snd_kcontrol_new aif2_out_mux_control =
+ SOC_DAPM_ENUM("AIF Out Select", aif2_out_mux_map_enum);
+
+static const char * const ul_src_mux_map[] = {
+ "AMIC",
+ "DMIC",
+};
+
+static int ul_src_mux_map_value[] = {
+ UL_SRC_MUX_AMIC,
+ UL_SRC_MUX_DMIC,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul_src_mux_map_enum,
+ MT6359_AFE_UL_SRC_CON0_L,
+ UL_SDM_3_LEVEL_CTL_SFT,
+ UL_SDM_3_LEVEL_CTL_MASK,
+ ul_src_mux_map,
+ ul_src_mux_map_value);
+
+static const struct snd_kcontrol_new ul_src_mux_control =
+ SOC_DAPM_ENUM("UL_SRC_MUX Select", ul_src_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_src_mux_map_enum,
+ MT6359_AFE_ADDA6_UL_SRC_CON0_L,
+ ADDA6_UL_SDM_3_LEVEL_CTL_SFT,
+ ADDA6_UL_SDM_3_LEVEL_CTL_MASK,
+ ul_src_mux_map,
+ ul_src_mux_map_value);
+
+static const struct snd_kcontrol_new ul2_src_mux_control =
+ SOC_DAPM_ENUM("UL_SRC_MUX Select", ul2_src_mux_map_enum);
+
+static const char * const miso_mux_map[] = {
+ "UL1_CH1",
+ "UL1_CH2",
+ "UL2_CH1",
+ "UL2_CH2",
+};
+
+static int miso_mux_map_value[] = {
+ MISO_MUX_UL1_CH1,
+ MISO_MUX_UL1_CH2,
+ MISO_MUX_UL2_CH1,
+ MISO_MUX_UL2_CH2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(miso0_mux_map_enum,
+ MT6359_AFE_MTKAIF_MUX_CFG,
+ RG_ADDA_CH1_SEL_SFT,
+ RG_ADDA_CH1_SEL_MASK,
+ miso_mux_map,
+ miso_mux_map_value);
+
+static const struct snd_kcontrol_new miso0_mux_control =
+ SOC_DAPM_ENUM("MISO_MUX Select", miso0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(miso1_mux_map_enum,
+ MT6359_AFE_MTKAIF_MUX_CFG,
+ RG_ADDA_CH2_SEL_SFT,
+ RG_ADDA_CH2_SEL_MASK,
+ miso_mux_map,
+ miso_mux_map_value);
+
+static const struct snd_kcontrol_new miso1_mux_control =
+ SOC_DAPM_ENUM("MISO_MUX Select", miso1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(miso2_mux_map_enum,
+ MT6359_AFE_MTKAIF_MUX_CFG,
+ RG_ADDA6_CH1_SEL_SFT,
+ RG_ADDA6_CH1_SEL_MASK,
+ miso_mux_map,
+ miso_mux_map_value);
+
+static const struct snd_kcontrol_new miso2_mux_control =
+ SOC_DAPM_ENUM("MISO_MUX Select", miso2_mux_map_enum);
+
+static const char * const dmic_mux_map[] = {
+ "DMIC_DATA0",
+ "DMIC_DATA1_L",
+ "DMIC_DATA1_L_1",
+ "DMIC_DATA1_R",
+};
+
+static int dmic_mux_map_value[] = {
+ DMIC_MUX_DMIC_DATA0,
+ DMIC_MUX_DMIC_DATA1_L,
+ DMIC_MUX_DMIC_DATA1_L_1,
+ DMIC_MUX_DMIC_DATA1_R,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dmic0_mux_map_enum,
+ MT6359_AFE_MIC_ARRAY_CFG,
+ RG_DMIC_ADC1_SOURCE_SEL_SFT,
+ RG_DMIC_ADC1_SOURCE_SEL_MASK,
+ dmic_mux_map,
+ dmic_mux_map_value);
+
+static const struct snd_kcontrol_new dmic0_mux_control =
+ SOC_DAPM_ENUM("DMIC_MUX Select", dmic0_mux_map_enum);
+
+/* ul1 ch2 use RG_DMIC_ADC3_SOURCE_SEL */
+static SOC_VALUE_ENUM_SINGLE_DECL(dmic1_mux_map_enum,
+ MT6359_AFE_MIC_ARRAY_CFG,
+ RG_DMIC_ADC3_SOURCE_SEL_SFT,
+ RG_DMIC_ADC3_SOURCE_SEL_MASK,
+ dmic_mux_map,
+ dmic_mux_map_value);
+
+static const struct snd_kcontrol_new dmic1_mux_control =
+ SOC_DAPM_ENUM("DMIC_MUX Select", dmic1_mux_map_enum);
+
+/* ul2 ch1 use RG_DMIC_ADC2_SOURCE_SEL */
+static SOC_VALUE_ENUM_SINGLE_DECL(dmic2_mux_map_enum,
+ MT6359_AFE_MIC_ARRAY_CFG,
+ RG_DMIC_ADC2_SOURCE_SEL_SFT,
+ RG_DMIC_ADC2_SOURCE_SEL_MASK,
+ dmic_mux_map,
+ dmic_mux_map_value);
+
+static const struct snd_kcontrol_new dmic2_mux_control =
+ SOC_DAPM_ENUM("DMIC_MUX Select", dmic2_mux_map_enum);
+
+/* ADC L MUX */
+static const char * const adc_left_mux_map[] = {
+ "Idle", "AIN0", "Left Preamplifier", "Idle_1"
+};
+
+static int adc_mux_map_value[] = {
+ ADC_MUX_IDLE,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDADCLINPUTSEL_SFT,
+ RG_AUDADCLINPUTSEL_MASK,
+ adc_left_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_left_mux_control =
+ SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum);
+
+/* ADC R MUX */
+static const char * const adc_right_mux_map[] = {
+ "Idle", "AIN0", "Right Preamplifier", "Idle_1"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDADCRINPUTSEL_SFT,
+ RG_AUDADCRINPUTSEL_MASK,
+ adc_right_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_right_mux_control =
+ SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum);
+
+/* ADC 3 MUX */
+static const char * const adc_3_mux_map[] = {
+ "Idle", "AIN0", "Preamplifier", "Idle_1"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adc_3_mux_map_enum,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDADC3INPUTSEL_SFT,
+ RG_AUDADC3INPUTSEL_MASK,
+ adc_3_mux_map,
+ adc_mux_map_value);
+
+static const struct snd_kcontrol_new adc_3_mux_control =
+ SOC_DAPM_ENUM("ADC 3 Select", adc_3_mux_map_enum);
+
+static const char * const pga_l_mux_map[] = {
+ "None", "AIN0", "AIN1"
+};
+
+static int pga_l_mux_map_value[] = {
+ PGA_L_MUX_NONE,
+ PGA_L_MUX_AIN0,
+ PGA_L_MUX_AIN1
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLINPUTSEL_SFT,
+ RG_AUDPREAMPLINPUTSEL_MASK,
+ pga_l_mux_map,
+ pga_l_mux_map_value);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+static const char * const pga_r_mux_map[] = {
+ "None", "AIN2", "AIN3", "AIN0"
+};
+
+static int pga_r_mux_map_value[] = {
+ PGA_R_MUX_NONE,
+ PGA_R_MUX_AIN2,
+ PGA_R_MUX_AIN3,
+ PGA_R_MUX_AIN0
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRINPUTSEL_SFT,
+ RG_AUDPREAMPRINPUTSEL_MASK,
+ pga_r_mux_map,
+ pga_r_mux_map_value);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+static const char * const pga_3_mux_map[] = {
+ "None", "AIN3", "AIN2"
+};
+
+static int pga_3_mux_map_value[] = {
+ PGA_3_MUX_NONE,
+ PGA_3_MUX_AIN3,
+ PGA_3_MUX_AIN2
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pga_3_mux_map_enum,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3INPUTSEL_SFT,
+ RG_AUDPREAMP3INPUTSEL_MASK,
+ pga_3_mux_map,
+ pga_3_mux_map_value);
+
+static const struct snd_kcontrol_new pga_3_mux_control =
+ SOC_DAPM_ENUM("PGA 3 Select", pga_3_mux_map_enum);
+
+static int mt_sgen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x000b);
+
+ regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG0,
+ 0xff3f,
+ 0x0000);
+ regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG1,
+ 0xffff,
+ 0x0001);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0000);
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void mtk_hp_enable(struct mt6359_priv *priv)
+{
+ if (priv->hp_hifi_mode) {
+ /* Set HP DR bias current optimization, 010: 6uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_HP_MASK_SFT,
+ DRBIAS_6UA << DRBIAS_HP_SFT);
+ /* Set HP & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_4UA << IBIAS_ZCD_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_HP_MASK_SFT,
+ IBIAS_5UA << IBIAS_HP_SFT);
+ } else {
+ /* Set HP DR bias current optimization, 001: 5uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_HP_MASK_SFT,
+ DRBIAS_5UA << DRBIAS_HP_SFT);
+ /* Set HP & ZCD bias current optimization */
+ /* 00: ZCD: 3uA, HP/HS/LO: 4uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_3UA << IBIAS_ZCD_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_HP_MASK_SFT,
+ IBIAS_4UA << IBIAS_HP_SFT);
+ }
+
+ /* HP damp circuit enable */
+ /* Enable HPRN/HPLN output 4K to VCM */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0087);
+
+ /* HP Feedback Cap select 2'b00: 15pF */
+ /* for >= 96KHz sampling rate: 2'b01: 10.5pF */
+ if (priv->dl_rate[MT6359_AIF_1] >= 96000)
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDDEC_ANA_CON4,
+ RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT);
+ else
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON4, 0x0000);
+
+ /* Set HPP/N STB enhance circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON2, 0xf133);
+
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x000c);
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x003c);
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c00);
+ /* Enable HP driver bias circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30c0);
+ /* Enable HP driver core circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30f0);
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00fc);
+
+ /* Increase HP input pair current to HPM step by step */
+ hp_in_pair_current(priv, true);
+
+ /* Enable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e00);
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0200);
+
+ /* Enable HP main output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00ff);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf);
+
+ /* apply volume setting */
+ headset_volume_ramp(priv,
+ DL_GAIN_N_22DB,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]);
+
+ /* Disable HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x7703);
+ usleep_range(100, 120);
+
+ /* Enable AUD_CLK */
+ mt6359_set_decoder_clk(priv, true);
+
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30ff);
+ if (priv->hp_hifi_mode) {
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf201);
+ } else {
+ /* Disable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200);
+ }
+ usleep_range(100, 120);
+
+ /* Switch HPL MUX to audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x32ff);
+ /* Switch HPR MUX to audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3aff);
+
+ /* Disable Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, false);
+}
+
+static void mtk_hp_disable(struct mt6359_priv *priv)
+{
+ /* Pull-down HPL/R to AVSS28_AUD */
+ hp_pull_down(priv, true);
+
+ /* HPR/HPL mux to open */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x0f00, 0x0000);
+
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9,
+ 0x0001, 0x0000);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ mt6359_set_decoder_clk(priv, false);
+
+ /* Short HP main output to HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3);
+ /* Enable HP aux output stage */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf);
+
+ /* decrease HPL/R gain to normal gain step by step */
+ headset_volume_ramp(priv,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL],
+ DL_GAIN_N_22DB);
+
+ /* Enable HP aux feedback loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77ff);
+
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, false);
+
+ /* decrease HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, false);
+
+ /* Disable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x3, 0x0);
+
+ /* Enable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e01);
+
+ /* Disable HP main CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c01);
+
+ /* Decrease HP input pair current to 2'b00 step by step */
+ hp_in_pair_current(priv, false);
+
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+
+ /* Disable HP aux CMFB loop */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x201);
+
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ 0x3 << 4, 0x0);
+
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1,
+ 0x3 << 2, 0x0);
+}
+
+static int mt_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+ int device = DEVICE_HP;
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n",
+ __func__, event, priv->dev_counter[device], mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->dev_counter[device]++;
+ if (mux == HP_MUX_HP)
+ mtk_hp_enable(priv);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->dev_counter[device]--;
+ if (mux == HP_MUX_HP)
+ mtk_hp_disable(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_rcv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__, event, snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disable handset short-circuit protection */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0010);
+
+ /* Set RCV DR bias current optimization, 010: 6uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_HS_MASK_SFT,
+ DRBIAS_6UA << DRBIAS_HS_SFT);
+ /* Set RCV & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_4UA << IBIAS_ZCD_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_HS_MASK_SFT,
+ IBIAS_5UA << IBIAS_HS_SFT);
+
+ /* Set HS STB enhance circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0090);
+
+ /* Set HS output stage (3'b111 = 8x) */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x7000);
+
+ /* Enable HS driver bias circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0092);
+ /* Enable HS driver core circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0093);
+
+ /* Set HS gain to normal gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON3,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]);
+
+ /* Enable AUD_CLK */
+ mt6359_set_decoder_clk(priv, true);
+
+ /* Enable Audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x0009);
+ /* Enable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001);
+ /* Switch HS MUX to audio DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x009b);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* HS mux to open */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT,
+ RCV_MUX_OPEN);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ /* Disable AUD_CLK */
+ mt6359_set_decoder_clk(priv, false);
+
+ /* decrease HS gain to minimum gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON3, DL_GAIN_N_40DB);
+
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSPWRUP_VAUDP32_MASK_SFT, 0x0);
+
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_lo_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n",
+ __func__, event, mux);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disable handset short-circuit protection */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0010);
+
+ /* Set LO DR bias current optimization, 010: 6uA */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11,
+ DRBIAS_LO_MASK_SFT,
+ DRBIAS_6UA << DRBIAS_LO_SFT);
+ /* Set LO & ZCD bias current optimization */
+ /* 01: ZCD: 4uA, HP/HS/LO: 5uA */
+ if (priv->dev_counter[DEVICE_HP] == 0)
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDDEC_ANA_CON12,
+ IBIAS_ZCD_MASK_SFT,
+ IBIAS_ZCD_4UA << IBIAS_ZCD_SFT);
+
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12,
+ IBIAS_LO_MASK_SFT,
+ IBIAS_5UA << IBIAS_LO_SFT);
+
+ /* Set LO STB enhance circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0110);
+
+ /* Enable LO driver bias circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0112);
+ /* Enable LO driver core circuits */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0113);
+
+ /* Set LO gain to normal gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON1,
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]);
+
+ /* Enable AUD_CLK */
+ mt6359_set_decoder_clk(priv, true);
+
+ /* Switch LOL MUX to audio DAC */
+ if (mux == LO_MUX_L_DAC) {
+ if (priv->dev_counter[DEVICE_HP] > 0) {
+ dev_info(priv->dev, "%s(), can not enable DAC, hp count %d\n",
+ __func__, priv->dev_counter[DEVICE_HP]);
+ break;
+ }
+ /* Enable DACL and switch HP MUX to open*/
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3009);
+ /* Disable low-noise mode of DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200);
+ usleep_range(100, 120);
+ /* Switch LOL MUX to DACL */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0117);
+ } else if (mux == LO_MUX_3RD_DAC) {
+ /* Enable Audio DAC (3rd DAC) */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113);
+ /* Enable low-noise mode of DAC */
+ if (priv->dev_counter[DEVICE_HP] == 0)
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001);
+ /* Switch LOL MUX to audio 3rd DAC */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Switch LOL MUX to open */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT,
+ LO_MUX_OPEN);
+
+ /* Disable Audio DAC */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x000f, 0x0000);
+
+ if (mux == LO_MUX_L_DAC) {
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x3 << 4, 0x0);
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ 0x3 << 6, 0x0);
+ }
+
+ /* Disable AUD_CLK */
+ mt6359_set_decoder_clk(priv, false);
+
+ /* decrease LO gain to minimum gain step by step */
+ regmap_write(priv->regmap, MT6359_ZCD_CON1, DL_GAIN_N_40DB);
+
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLPWRUP_VAUDP32_MASK_SFT, 0x0);
+
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_clk_gen_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* ADC CLK from CLKGEN (6.5MHz) */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKRSTB_MASK_SFT,
+ 0x1 << RG_AUDADCCLKRSTB_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSOURCE_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSEL_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKGENMODE_MASK_SFT,
+ 0x1 << RG_AUDADCCLKGENMODE_SFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSOURCE_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKSEL_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKGENMODE_MASK_SFT, 0x0);
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5,
+ RG_AUDADCCLKRSTB_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_dcc_clk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* DCC 50k CLK (from 26M) */
+ /* MT6359_AFE_DCCLK_CFG0, bit 3 for dm ck swap */
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2062);
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2060);
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2061);
+
+ regmap_write(priv->regmap, MT6359_AFE_DCCLK_CFG1, 0x0100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2060);
+ regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0,
+ 0xfff7, 0x2062);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_0];
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n",
+ __func__, event, mic_type);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON15,
+ 0xff00, 0x7700);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON15,
+ 0xff00, 0x1100);
+ break;
+ default:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON15,
+ 0xff00, 0x0000);
+ break;
+ }
+
+ /* DMIC enable */
+ regmap_write(priv->regmap,
+ MT6359_AUDENC_ANA_CON14, 0x0004);
+ /* MISBIAS0 = 1P9V */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15,
+ RG_AUDMICBIAS0VREF_MASK_SFT,
+ MIC_BIAS_1P9 << RG_AUDMICBIAS0VREF_SFT);
+ /* normal power select */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15,
+ RG_AUDMICBIAS0LOWPEN_MASK_SFT,
+ 0 << RG_AUDMICBIAS0LOWPEN_SFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable MICBIAS0, MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON15, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_1];
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n",
+ __func__, event, mic_type);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* MISBIAS1 = 2P6V */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_write(priv->regmap,
+ MT6359_AUDENC_ANA_CON16, 0x0160);
+ else
+ regmap_write(priv->regmap,
+ MT6359_AUDENC_ANA_CON16, 0x0060);
+
+ /* normal power select */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON16,
+ RG_AUDMICBIAS1LOWPEN_MASK_SFT,
+ 0 << RG_AUDMICBIAS1LOWPEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_2];
+
+ dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n",
+ __func__, event, mic_type);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON17,
+ 0xff00, 0x7700);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON17,
+ 0xff00, 0x1100);
+ break;
+ default:
+ regmap_update_bits(priv->regmap,
+ MT6359_AUDENC_ANA_CON17,
+ 0xff00, 0x0000);
+ break;
+ }
+
+ /* MISBIAS2 = 1P9V */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17,
+ RG_AUDMICBIAS2VREF_MASK_SFT,
+ MIC_BIAS_1P9 << RG_AUDMICBIAS2VREF_SFT);
+ /* normal power select */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17,
+ RG_AUDMICBIAS2LOWPEN_MASK_SFT,
+ 0 << RG_AUDMICBIAS2LOWPEN_SFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable MICBIAS2, MISBIAS0 = 1P7V */
+ regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON17, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_mtkaif_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt6359_mtkaif_tx_enable(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt6359_mtkaif_tx_disable(priv);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ul_src_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* UL dmic setting */
+ if (priv->dmic_one_wire_mode)
+ regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H,
+ 0x0400);
+ else
+ regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H,
+ 0x0080);
+ /* default one wire, 3.25M */
+ regmap_update_bits(priv->regmap, MT6359_AFE_UL_SRC_CON0_L,
+ 0xfffc, 0x0000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(priv->regmap,
+ MT6359_AFE_UL_SRC_CON0_H, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ul_src_34_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* default two wire, 3.25M */
+ regmap_write(priv->regmap,
+ MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0080);
+ regmap_update_bits(priv->regmap, MT6359_AFE_ADDA6_UL_SRC_CON0_L,
+ 0xfffc, 0x0000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(priv->regmap,
+ MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_l_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCPRECHARGE_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_r_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCPRECHARGE_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_adc_3_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(100, 120);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCPRECHARGE_MASK_SFT,
+ 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_l_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux);
+ priv->mux_select[MUX_PGA_L] = mux >> RG_AUDPREAMPLINPUTSEL_SFT;
+ return 0;
+}
+
+static int mt_pga_r_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux);
+ priv->mux_select[MUX_PGA_R] = mux >> RG_AUDPREAMPRINPUTSEL_SFT;
+ return 0;
+}
+
+static int mt_pga_3_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux);
+ priv->mux_select[MUX_PGA_3] = mux >> RG_AUDPREAMP3INPUTSEL_SFT;
+ return 0;
+}
+
+static int mt_pga_l_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int mic_gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
+ unsigned int mux_pga = priv->mux_select[MUX_PGA_L];
+ unsigned int mic_type;
+
+ switch (mux_pga) {
+ case PGA_L_MUX_AIN0:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_0];
+ break;
+ case PGA_L_MUX_AIN1:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_1];
+ break;
+ default:
+ dev_err(priv->dev, "%s(), invalid pga mux %d\n",
+ __func__, mux_pga);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCPRECHARGE_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLDCPRECHARGE_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* set mic pga gain */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLGAIN_MASK_SFT,
+ mic_gain_l << RG_AUDPREAMPLGAIN_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPLDCCEN_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLDCCEN_MASK_SFT,
+ 0x0 << RG_AUDPREAMPLDCCEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_r_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int mic_gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
+ unsigned int mux_pga = priv->mux_select[MUX_PGA_R];
+ unsigned int mic_type;
+
+ switch (mux_pga) {
+ case PGA_R_MUX_AIN0:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_0];
+ break;
+ case PGA_R_MUX_AIN2:
+ case PGA_R_MUX_AIN3:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_2];
+ break;
+ default:
+ dev_err(priv->dev, "%s(), invalid pga mux %d\n",
+ __func__, mux_pga);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCPRECHARGE_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRDCPRECHARGE_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* set mic pga gain */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRGAIN_MASK_SFT,
+ mic_gain_r << RG_AUDPREAMPRGAIN_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMPRDCCEN_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRDCCEN_MASK_SFT,
+ 0x0 << RG_AUDPREAMPRDCCEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_pga_3_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int mic_gain_3 = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3];
+ unsigned int mux_pga = priv->mux_select[MUX_PGA_3];
+ unsigned int mic_type;
+
+ switch (mux_pga) {
+ case PGA_3_MUX_AIN2:
+ case PGA_3_MUX_AIN3:
+ mic_type = priv->mux_select[MUX_MIC_TYPE_2];
+ break;
+ default:
+ dev_err(priv->dev, "%s(), invalid pga mux %d\n",
+ __func__, mux_pga);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (IS_DCC_BASE(mic_type)) {
+ /* Audio 3 preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCPRECHARGE_MASK_SFT,
+ 0x1 << RG_AUDPREAMP3DCPRECHARGE_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* set mic pga gain */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3GAIN_MASK_SFT,
+ mic_gain_3 << RG_AUDPREAMP3GAIN_SFT);
+
+ if (IS_DCC_BASE(mic_type)) {
+ /* 3 preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCCEN_MASK_SFT,
+ 0x1 << RG_AUDPREAMP3DCCEN_SFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 3 preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3DCCEN_MASK_SFT,
+ 0x0 << RG_AUDPREAMP3DCCEN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* It is based on hw's control sequenece to add some delay when PMU/PMD */
+static int mt_delay_250_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(250, 270);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_delay_100_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(100, 120);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_pull_down_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hp_pull_down(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ hp_pull_down(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_mute_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Set HPR/HPL gain to -22dB */
+ regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_22DB_REG);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Set HPL/HPR gain to mute */
+ regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_40DB_REG);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_hp_damp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable HP damping circuit & HPN 4K load */
+ /* reset CMFB PW level */
+ regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0000);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_esd_resist_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDREFN_DERES_EN_VAUDP32_SFT);
+ usleep_range(250, 270);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Increase ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2,
+ RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sdm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1);
+ /* sdm power on */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x0003);
+ /* sdm fifo enable */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x000B);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2,
+ 0xfffd, 0x0000);
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_sdm_3rd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0006);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba1);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0003);
+ /* sdm fifo enable */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x000b);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0000);
+ regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_ncp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_write(priv->regmap, MT6359_AFE_NCP_CFG0, 0xc800);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6359_dapm_widgets[] = {
+ /* Global Supply*/
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6359_DCXO_CW12,
+ RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6359_AUDDEC_ANA_CON13,
+ RG_AUDGLB_PWRDN_VA32_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6359_AUDENC_ANA_CON23,
+ RG_CLKSQ_EN_SFT, 0, NULL, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_AUD_CK_PDN_SFT, 1, mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK,
+ MT6359_AUD_TOP_CKPDN_CON0,
+ RG_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vaud18", 0, 0),
+
+ /* Digital Clock */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_AFE_CTL_SFT, 1,
+ mt_delay_250_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADDA6_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_ADDA6_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6359_AUDIO_TOP_CON0,
+ PDN_RESERVED_SFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("SDM", SUPPLY_SEQ_DL_SDM,
+ SND_SOC_NOPM, 0, 0,
+ mt_sdm_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("SDM_3RD", SUPPLY_SEQ_DL_SDM,
+ SND_SOC_NOPM, 0, 0,
+ mt_sdm_3rd_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* ch123 share SDM FIFO CLK */
+ SND_SOC_DAPM_SUPPLY_S("SDM_FIFO_CLK", SUPPLY_SEQ_DL_SDM_FIFO_CLK,
+ MT6359_AFUNC_AUD_CON2,
+ CCI_AFIFO_CLK_PWDB_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("NCP", SUPPLY_SEQ_DL_NCP,
+ MT6359_AFE_NCP_CFG0,
+ RG_NCP_ON_SFT, 0,
+ mt_ncp_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_1_2", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_3", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* AFE ON */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6359_AFE_UL_DL_CON0, AFE_ON_SFT, 0,
+ NULL, 0),
+
+ /* AIF Rx*/
+ SND_SOC_DAPM_AIF_IN("AIF_RX", "AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIF2_RX", "AIF2 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("AFE_DL_SRC", SUPPLY_SEQ_DL_SRC,
+ MT6359_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ NULL, 0),
+
+ /* DL Supply */
+ SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ESD_RESIST", SUPPLY_SEQ_DL_ESD_RESIST,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_esd_resist_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("LDO", SUPPLY_SEQ_DL_LDO,
+ MT6359_AUDDEC_ANA_CON14,
+ RG_LCLDO_DEC_EN_VA32_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO_REMOTE", SUPPLY_SEQ_DL_LDO_REMOTE_SENSE,
+ MT6359_AUDDEC_ANA_CON14,
+ RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("NV_REGULATOR", SUPPLY_SEQ_DL_NV,
+ MT6359_AUDDEC_ANA_CON14,
+ RG_NVREG_EN_VAUDP32_SFT, 0,
+ mt_delay_100_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY_S("IBIST", SUPPLY_SEQ_DL_IBIST,
+ MT6359_AUDDEC_ANA_CON12,
+ RG_AUDIBIASPWRDN_VAUDP32_SFT, 1,
+ NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC_3RD", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_MUX_E("HP Mux", SND_SOC_NOPM, 0, 0,
+ &hp_in_mux_control,
+ mt_hp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("HP_Supply", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("HP_PULL_DOWN", SUPPLY_SEQ_HP_PULL_DOWN,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_hp_pull_down_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("HP_MUTE", SUPPLY_SEQ_HP_MUTE,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_hp_mute_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("HP_DAMP", SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB,
+ SND_SOC_NOPM,
+ 0, 0,
+ mt_hp_damp_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Receiver */
+ SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0,
+ &rcv_in_mux_control,
+ mt_rcv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* LOL */
+ SND_SOC_DAPM_MUX_E("LOL Mux", SND_SOC_NOPM, 0, 0,
+ &lo_in_mux_control,
+ mt_lo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("Receiver"),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+ SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT L"),
+
+ /* SGEN */
+ SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6359_AFE_SGEN_CFG0,
+ SGEN_DAC_EN_CTL_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6359_AFE_SGEN_CFG0,
+ SGEN_MUTE_SW_CTL_SFT, 1,
+ mt_sgen_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6359_AFE_DL_SRC2_CON0_L,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC_CLKGEN", SUPPLY_SEQ_ADC_CLKGEN,
+ SND_SOC_NOPM, 0, 0,
+ mt_adc_clk_gen_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("DCC_CLK", SUPPLY_SEQ_DCC_CLK,
+ SND_SOC_NOPM, 0, 0,
+ mt_dcc_clk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Uplinks MUX */
+ SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif_out_mux_control),
+
+ SND_SOC_DAPM_MUX("AIF2 Out Mux", SND_SOC_NOPM, 0, 0,
+ &aif2_out_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("AIFTX_Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("MTKAIF_TX", SUPPLY_SEQ_UL_MTKAIF,
+ SND_SOC_NOPM, 0, 0,
+ mt_mtkaif_tx_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC", SUPPLY_SEQ_UL_SRC,
+ MT6359_AFE_UL_SRC_CON0_L,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC_DMIC", SUPPLY_SEQ_UL_SRC_DMIC,
+ SND_SOC_NOPM, 0, 0,
+ mt_ul_src_dmic_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC_34", SUPPLY_SEQ_UL_SRC,
+ MT6359_AFE_ADDA6_UL_SRC_CON0_L,
+ ADDA6_UL_SRC_ON_TMP_CTL_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("UL_SRC_34_DMIC", SUPPLY_SEQ_UL_SRC_DMIC,
+ SND_SOC_NOPM, 0, 0,
+ mt_ul_src_34_dmic_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("MISO0_MUX", SND_SOC_NOPM, 0, 0, &miso0_mux_control),
+ SND_SOC_DAPM_MUX("MISO1_MUX", SND_SOC_NOPM, 0, 0, &miso1_mux_control),
+ SND_SOC_DAPM_MUX("MISO2_MUX", SND_SOC_NOPM, 0, 0, &miso2_mux_control),
+
+ SND_SOC_DAPM_MUX("UL_SRC_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_src_mux_control),
+ SND_SOC_DAPM_MUX("UL2_SRC_MUX", SND_SOC_NOPM, 0, 0,
+ &ul2_src_mux_control),
+
+ SND_SOC_DAPM_MUX("DMIC0_MUX", SND_SOC_NOPM, 0, 0, &dmic0_mux_control),
+ SND_SOC_DAPM_MUX("DMIC1_MUX", SND_SOC_NOPM, 0, 0, &dmic1_mux_control),
+ SND_SOC_DAPM_MUX("DMIC2_MUX", SND_SOC_NOPM, 0, 0, &dmic2_mux_control),
+
+ SND_SOC_DAPM_MUX_E("ADC_L_Mux", SND_SOC_NOPM, 0, 0,
+ &adc_left_mux_control, NULL, 0),
+ SND_SOC_DAPM_MUX_E("ADC_R_Mux", SND_SOC_NOPM, 0, 0,
+ &adc_right_mux_control, NULL, 0),
+ SND_SOC_DAPM_MUX_E("ADC_3_Mux", SND_SOC_NOPM, 0, 0,
+ &adc_3_mux_control, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC_L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC_R", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC_3", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADC_L_EN", SUPPLY_SEQ_UL_ADC,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDADCLPWRUP_SFT, 0,
+ mt_adc_l_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADC_R_EN", SUPPLY_SEQ_UL_ADC,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDADCRPWRUP_SFT, 0,
+ mt_adc_r_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADC_3_EN", SUPPLY_SEQ_UL_ADC,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDADC3PWRUP_SFT, 0,
+ mt_adc_3_event,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("PGA_L_Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_l_mux_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("PGA_R_Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_r_mux_event,
+ SND_SOC_DAPM_WILL_PMU),
+ SND_SOC_DAPM_MUX_E("PGA_3_Mux", SND_SOC_NOPM, 0, 0,
+ &pga_3_mux_control,
+ mt_pga_3_mux_event,
+ SND_SOC_DAPM_WILL_PMU),
+
+ SND_SOC_DAPM_PGA("PGA_L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA_R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA_3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("PGA_L_EN", SUPPLY_SEQ_UL_PGA,
+ MT6359_AUDENC_ANA_CON0,
+ RG_AUDPREAMPLON_SFT, 0,
+ mt_pga_l_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("PGA_R_EN", SUPPLY_SEQ_UL_PGA,
+ MT6359_AUDENC_ANA_CON1,
+ RG_AUDPREAMPRON_SFT, 0,
+ mt_pga_r_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("PGA_3_EN", SUPPLY_SEQ_UL_PGA,
+ MT6359_AUDENC_ANA_CON2,
+ RG_AUDPREAMP3ON_SFT, 0,
+ mt_pga_3_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* UL input */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+
+ SND_SOC_DAPM_INPUT("AIN0_DMIC"),
+ SND_SOC_DAPM_INPUT("AIN2_DMIC"),
+ SND_SOC_DAPM_INPUT("AIN3_DMIC"),
+
+ /* mic bias */
+ SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_0", SUPPLY_SEQ_MIC_BIAS,
+ MT6359_AUDENC_ANA_CON15,
+ RG_AUDPWDBMICBIAS0_SFT, 0,
+ mt_mic_bias_0_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_1", SUPPLY_SEQ_MIC_BIAS,
+ MT6359_AUDENC_ANA_CON16,
+ RG_AUDPWDBMICBIAS1_SFT, 0,
+ mt_mic_bias_1_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_2", SUPPLY_SEQ_MIC_BIAS,
+ MT6359_AUDENC_ANA_CON17,
+ RG_AUDPWDBMICBIAS2_SFT, 0,
+ mt_mic_bias_2_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* dmic */
+ SND_SOC_DAPM_SUPPLY_S("DMIC_0", SUPPLY_SEQ_DMIC,
+ MT6359_AUDENC_ANA_CON13,
+ RG_AUDDIGMICEN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC_1", SUPPLY_SEQ_DMIC,
+ MT6359_AUDENC_ANA_CON14,
+ RG_AUDDIGMIC1EN_SFT, 0,
+ NULL, 0),
+};
+
+static int mt_dcc_clk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ if (IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_0]) ||
+ IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_1]) ||
+ IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_2]))
+ return 1;
+ else
+ return 0;
+}
+
+static const struct snd_soc_dapm_route mt6359_dapm_routes[] = {
+ /* Capture */
+ {"AIFTX_Supply", NULL, "CLK_BUF"},
+ {"AIFTX_Supply", NULL, "vaud18"},
+ {"AIFTX_Supply", NULL, "AUDGLB"},
+ {"AIFTX_Supply", NULL, "CLKSQ Audio"},
+ {"AIFTX_Supply", NULL, "AUD_CK"},
+ {"AIFTX_Supply", NULL, "AUDIF_CK"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_I2S_DL"},
+ /*
+ * *_ADC_CTL should enable only if UL_SRC in use,
+ * but dm ck may be needed even UL_SRC_x not in use
+ */
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_ADC_CTL"},
+ {"AIFTX_Supply", NULL, "AUDIO_TOP_ADDA6_ADC_CTL"},
+ {"AIFTX_Supply", NULL, "AFE_ON"},
+
+ /* ul ch 12 */
+ {"AIF1TX", NULL, "AIF Out Mux"},
+ {"AIF1TX", NULL, "AIFTX_Supply"},
+ {"AIF1TX", NULL, "MTKAIF_TX"},
+
+ {"AIF2TX", NULL, "AIF2 Out Mux"},
+ {"AIF2TX", NULL, "AIFTX_Supply"},
+ {"AIF2TX", NULL, "MTKAIF_TX"},
+
+ {"AIF Out Mux", "Normal Path", "MISO0_MUX"},
+ {"AIF Out Mux", "Normal Path", "MISO1_MUX"},
+ {"AIF2 Out Mux", "Normal Path", "MISO2_MUX"},
+
+ {"MISO0_MUX", "UL1_CH1", "UL_SRC_MUX"},
+ {"MISO0_MUX", "UL1_CH2", "UL_SRC_MUX"},
+ {"MISO0_MUX", "UL2_CH1", "UL2_SRC_MUX"},
+ {"MISO0_MUX", "UL2_CH2", "UL2_SRC_MUX"},
+
+ {"MISO1_MUX", "UL1_CH1", "UL_SRC_MUX"},
+ {"MISO1_MUX", "UL1_CH2", "UL_SRC_MUX"},
+ {"MISO1_MUX", "UL2_CH1", "UL2_SRC_MUX"},
+ {"MISO1_MUX", "UL2_CH2", "UL2_SRC_MUX"},
+
+ {"MISO2_MUX", "UL1_CH1", "UL_SRC_MUX"},
+ {"MISO2_MUX", "UL1_CH2", "UL_SRC_MUX"},
+ {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"},
+ {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"},
+
+ {"MISO0_MUX", NULL, "UL_SRC"},
+ {"MISO1_MUX", NULL, "UL_SRC"},
+ {"MISO2_MUX", NULL, "UL_SRC_34"},
+
+ {"UL_SRC_MUX", "AMIC", "ADC_L"},
+ {"UL_SRC_MUX", "AMIC", "ADC_R"},
+ {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"},
+ {"UL_SRC_MUX", "DMIC", "DMIC1_MUX"},
+ {"UL_SRC_MUX", NULL, "UL_SRC"},
+
+ {"UL2_SRC_MUX", "AMIC", "ADC_3"},
+ {"UL2_SRC_MUX", "DMIC", "DMIC2_MUX"},
+ {"UL2_SRC_MUX", NULL, "UL_SRC_34"},
+
+ {"DMIC0_MUX", "DMIC_DATA0", "AIN0_DMIC"},
+ {"DMIC0_MUX", "DMIC_DATA1_L", "AIN2_DMIC"},
+ {"DMIC0_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"},
+ {"DMIC0_MUX", "DMIC_DATA1_R", "AIN3_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA0", "AIN0_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA1_L", "AIN2_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"},
+ {"DMIC1_MUX", "DMIC_DATA1_R", "AIN3_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA0", "AIN0_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA1_L", "AIN2_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"},
+ {"DMIC2_MUX", "DMIC_DATA1_R", "AIN3_DMIC"},
+
+ {"DMIC0_MUX", NULL, "UL_SRC_DMIC"},
+ {"DMIC1_MUX", NULL, "UL_SRC_DMIC"},
+ {"DMIC2_MUX", NULL, "UL_SRC_34_DMIC"},
+
+ {"AIN0_DMIC", NULL, "DMIC_0"},
+ {"AIN2_DMIC", NULL, "DMIC_1"},
+ {"AIN3_DMIC", NULL, "DMIC_1"},
+ {"AIN0_DMIC", NULL, "MIC_BIAS_0"},
+ {"AIN2_DMIC", NULL, "MIC_BIAS_2"},
+ {"AIN3_DMIC", NULL, "MIC_BIAS_2"},
+
+ /* adc */
+ {"ADC_L", NULL, "ADC_L_Mux"},
+ {"ADC_L", NULL, "ADC_CLKGEN"},
+ {"ADC_L", NULL, "ADC_L_EN"},
+ {"ADC_R", NULL, "ADC_R_Mux"},
+ {"ADC_R", NULL, "ADC_CLKGEN"},
+ {"ADC_R", NULL, "ADC_R_EN"},
+ /*
+ * amic fifo ch1/2 clk from ADC_L,
+ * enable ADC_L even use ADC_R only
+ */
+ {"ADC_R", NULL, "ADC_L_EN"},
+ {"ADC_3", NULL, "ADC_3_Mux"},
+ {"ADC_3", NULL, "ADC_CLKGEN"},
+ {"ADC_3", NULL, "ADC_3_EN"},
+
+ {"ADC_L_Mux", "Left Preamplifier", "PGA_L"},
+ {"ADC_R_Mux", "Right Preamplifier", "PGA_R"},
+ {"ADC_3_Mux", "Preamplifier", "PGA_3"},
+
+ {"PGA_L", NULL, "PGA_L_Mux"},
+ {"PGA_L", NULL, "PGA_L_EN"},
+ {"PGA_R", NULL, "PGA_R_Mux"},
+ {"PGA_R", NULL, "PGA_R_EN"},
+ {"PGA_3", NULL, "PGA_3_Mux"},
+ {"PGA_3", NULL, "PGA_3_EN"},
+
+ {"PGA_L", NULL, "DCC_CLK", mt_dcc_clk_connect},
+ {"PGA_R", NULL, "DCC_CLK", mt_dcc_clk_connect},
+ {"PGA_3", NULL, "DCC_CLK", mt_dcc_clk_connect},
+
+ {"PGA_L_Mux", "AIN0", "AIN0"},
+ {"PGA_L_Mux", "AIN1", "AIN1"},
+
+ {"PGA_R_Mux", "AIN0", "AIN0"},
+ {"PGA_R_Mux", "AIN2", "AIN2"},
+ {"PGA_R_Mux", "AIN3", "AIN3"},
+
+ {"PGA_3_Mux", "AIN2", "AIN2"},
+ {"PGA_3_Mux", "AIN3", "AIN3"},
+
+ {"AIN0", NULL, "MIC_BIAS_0"},
+ {"AIN1", NULL, "MIC_BIAS_1"},
+ {"AIN2", NULL, "MIC_BIAS_0"},
+ {"AIN2", NULL, "MIC_BIAS_2"},
+ {"AIN3", NULL, "MIC_BIAS_2"},
+
+ /* DL Supply */
+ {"DL Power Supply", NULL, "CLK_BUF"},
+ {"DL Power Supply", NULL, "vaud18"},
+ {"DL Power Supply", NULL, "AUDGLB"},
+ {"DL Power Supply", NULL, "CLKSQ Audio"},
+ {"DL Power Supply", NULL, "AUDNCP_CK"},
+ {"DL Power Supply", NULL, "ZCD13M_CK"},
+ {"DL Power Supply", NULL, "AUD_CK"},
+ {"DL Power Supply", NULL, "AUDIF_CK"},
+ {"DL Power Supply", NULL, "ESD_RESIST"},
+ {"DL Power Supply", NULL, "LDO"},
+ {"DL Power Supply", NULL, "LDO_REMOTE"},
+ {"DL Power Supply", NULL, "NV_REGULATOR"},
+ {"DL Power Supply", NULL, "IBIST"},
+
+ /* DL Digital Supply */
+ {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"},
+ {"DL Digital Clock", NULL, "SDM_FIFO_CLK"},
+ {"DL Digital Clock", NULL, "NCP"},
+ {"DL Digital Clock", NULL, "AFE_ON"},
+ {"DL Digital Clock", NULL, "AFE_DL_SRC"},
+
+ {"DL Digital Clock CH_1_2", NULL, "DL Digital Clock"},
+ {"DL Digital Clock CH_1_2", NULL, "SDM"},
+
+ {"DL Digital Clock CH_3", NULL, "DL Digital Clock"},
+ {"DL Digital Clock CH_3", NULL, "SDM_3RD"},
+
+ {"AIF_RX", NULL, "DL Digital Clock CH_1_2"},
+
+ {"AIF2_RX", NULL, "DL Digital Clock CH_3"},
+
+ /* DL Path */
+ {"DAC In Mux", "Normal Path", "AIF_RX"},
+ {"DAC In Mux", "Sgen", "SGEN DL"},
+ {"SGEN DL", NULL, "SGEN DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN DL Enable"},
+ {"SGEN DL", NULL, "DL Digital Clock CH_1_2"},
+ {"SGEN DL", NULL, "DL Digital Clock CH_3"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC In Mux"},
+ {"DACL", NULL, "DL Power Supply"},
+
+ {"DACR", NULL, "DAC In Mux"},
+ {"DACR", NULL, "DL Power Supply"},
+
+ /* DAC 3RD */
+ {"DAC In Mux", "Normal Path", "AIF2_RX"},
+ {"DAC_3RD", NULL, "DAC In Mux"},
+ {"DAC_3RD", NULL, "DL Power Supply"},
+
+ /* Lineout Path */
+ {"LOL Mux", "Playback", "DAC_3RD"},
+ {"LOL Mux", "Playback_L_DAC", "DACL"},
+ {"LINEOUT L", NULL, "LOL Mux"},
+
+ /* Headphone Path */
+ {"HP_Supply", NULL, "HP_PULL_DOWN"},
+ {"HP_Supply", NULL, "HP_MUTE"},
+ {"HP_Supply", NULL, "HP_DAMP"},
+ {"HP Mux", NULL, "HP_Supply"},
+
+ {"HP Mux", "Audio Playback", "DACL"},
+ {"HP Mux", "Audio Playback", "DACR"},
+ {"HP Mux", "HP Impedance", "DACL"},
+ {"HP Mux", "HP Impedance", "DACR"},
+ {"HP Mux", "LoudSPK Playback", "DACL"},
+ {"HP Mux", "LoudSPK Playback", "DACR"},
+
+ {"Headphone L", NULL, "HP Mux"},
+ {"Headphone R", NULL, "HP Mux"},
+ {"Headphone L Ext Spk Amp", NULL, "HP Mux"},
+ {"Headphone R Ext Spk Amp", NULL, "HP Mux"},
+
+ /* Receiver Path */
+ {"RCV Mux", "Voice Playback", "DACL"},
+ {"Receiver", NULL, "RCV Mux"},
+};
+
+static int mt6359_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int rate = params_rate(params);
+ int id = dai->id;
+
+ dev_dbg(priv->dev, "%s(), id %d, substream->stream %d, rate %d, number %d\n",
+ __func__, id, substream->stream, rate, substream->number);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ priv->dl_rate[id] = rate;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->ul_rate[id] = rate;
+
+ return 0;
+}
+
+static int mt6359_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mt6359_set_playback_gpio(priv);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt6359_set_capture_gpio(priv);
+
+ return 0;
+}
+
+static void mt6359_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *cmpnt = dai->component;
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mt6359_reset_playback_gpio(priv);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt6359_reset_capture_gpio(priv);
+}
+
+static const struct snd_soc_dai_ops mt6359_codec_dai_ops = {
+ .hw_params = mt6359_codec_dai_hw_params,
+ .startup = mt6359_codec_dai_startup,
+ .shutdown = mt6359_codec_dai_shutdown,
+};
+
+#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6359_dai_driver[] = {
+ {
+ .id = MT6359_AIF_1,
+ .name = "mt6359-snd-codec-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6359_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6359_FORMATS,
+ },
+ .ops = &mt6359_codec_dai_ops,
+ },
+ {
+ .id = MT6359_AIF_2,
+ .name = "mt6359-snd-codec-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = MT6359_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = MT6359_FORMATS,
+ },
+ .ops = &mt6359_codec_dai_ops,
+ },
+};
+
+static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ /* enable clk buf */
+ regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT);
+
+ /* set those not controlled by dapm widget */
+
+ /* audio clk source from internal dcxo */
+ regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23,
+ RG_CLKSQ_IN_SEL_TEST_MASK_SFT,
+ 0x0);
+
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHPLSCDISABLE_VAUDP32_SFT);
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0,
+ RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHPRSCDISABLE_VAUDP32_SFT);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6,
+ RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDHSSCDISABLE_VAUDP32_SFT);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7,
+ RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT,
+ 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT);
+
+ /* set gpio */
+ mt6359_set_gpio_smt(priv);
+ mt6359_set_gpio_driving(priv);
+ mt6359_reset_playback_gpio(priv);
+ mt6359_reset_capture_gpio(priv);
+
+ /* hp hifi mode, default normal mode */
+ priv->hp_hifi_mode = 0;
+
+ /* Disable AUD_ZCD */
+ zcd_disable(priv);
+
+ /* disable clk buf */
+ regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
+ 0x1 << RG_XO_AUDIO_EN_M_SFT,
+ 0x0 << RG_XO_AUDIO_EN_M_SFT);
+
+ return 0;
+}
+
+static int mt6359_codec_probe(struct snd_soc_component *cmpnt)
+{
+ struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ snd_soc_component_init_regmap(cmpnt, priv->regmap);
+
+ return mt6359_codec_init_reg(cmpnt);
+}
+
+static void mt6359_codec_remove(struct snd_soc_component *cmpnt)
+{
+ cmpnt->regmap = NULL;
+}
+
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new mt6359_snd_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_EXT_TLV("Headset Volume",
+ MT6359_ZCD_CON2, 0, 7, 0x12, 0,
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
+ SOC_DOUBLE_EXT_TLV("Lineout Volume",
+ MT6359_ZCD_CON1, 0, 7, 0x12, 0,
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
+ SOC_SINGLE_EXT_TLV("Handset Volume",
+ MT6359_ZCD_CON3, 0, 0x12, 0,
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
+
+ /* ul pga gain */
+ SOC_SINGLE_EXT_TLV("PGA1 Volume",
+ MT6359_AUDENC_ANA_CON0, RG_AUDPREAMPLGAIN_SFT, 4, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, capture_tlv),
+ SOC_SINGLE_EXT_TLV("PGA2 Volume",
+ MT6359_AUDENC_ANA_CON1, RG_AUDPREAMPRGAIN_SFT, 4, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, capture_tlv),
+ SOC_SINGLE_EXT_TLV("PGA3 Volume",
+ MT6359_AUDENC_ANA_CON2, RG_AUDPREAMP3GAIN_SFT, 4, 0,
+ snd_soc_get_volsw, mt6359_put_volsw, capture_tlv),
+};
+
+static const struct snd_soc_component_driver mt6359_soc_component_driver = {
+ .name = CODEC_MT6359_NAME,
+ .probe = mt6359_codec_probe,
+ .remove = mt6359_codec_remove,
+ .controls = mt6359_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6359_snd_controls),
+ .dapm_widgets = mt6359_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets),
+ .dapm_routes = mt6359_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes),
+ .endianness = 1,
+};
+
+static int mt6359_parse_dt(struct mt6359_priv *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+ struct device_node *np;
+
+ np = of_get_child_by_name(dev->parent->of_node, "audio-codec");
+ if (!np) {
+ np = of_get_child_by_name(dev->parent->of_node, "mt6359codec");
+ if (!np)
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,dmic-mode",
+ &priv->dmic_one_wire_mode);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read dmic-mode, use default (0)\n",
+ __func__);
+ priv->dmic_one_wire_mode = 0;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,mic-type-0",
+ &priv->mux_select[MUX_MIC_TYPE_0]);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read mic-type-0, use default (%d)\n",
+ __func__, MIC_TYPE_MUX_IDLE);
+ priv->mux_select[MUX_MIC_TYPE_0] = MIC_TYPE_MUX_IDLE;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,mic-type-1",
+ &priv->mux_select[MUX_MIC_TYPE_1]);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read mic-type-1, use default (%d)\n",
+ __func__, MIC_TYPE_MUX_IDLE);
+ priv->mux_select[MUX_MIC_TYPE_1] = MIC_TYPE_MUX_IDLE;
+ }
+
+ ret = of_property_read_u32(np, "mediatek,mic-type-2",
+ &priv->mux_select[MUX_MIC_TYPE_2]);
+ of_node_put(np);
+ if (ret) {
+ dev_info(priv->dev,
+ "%s() failed to read mic-type-2, use default (%d)\n",
+ __func__, MIC_TYPE_MUX_IDLE);
+ priv->mux_select[MUX_MIC_TYPE_2] = MIC_TYPE_MUX_IDLE;
+ }
+
+ return 0;
+}
+
+static int mt6359_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6359_priv *priv;
+ int ret;
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+
+ dev_dbg(&pdev->dev, "%s(), dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = mt6397->regmap;
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ dev_set_drvdata(&pdev->dev, priv);
+ priv->dev = &pdev->dev;
+
+ ret = mt6359_parse_dt(priv);
+ if (ret) {
+ dev_warn(&pdev->dev, "%s() failed to parse dts\n", __func__);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6359_soc_component_driver,
+ mt6359_dai_driver,
+ ARRAY_SIZE(mt6359_dai_driver));
+}
+
+static struct platform_driver mt6359_platform_driver = {
+ .driver = {
+ .name = "mt6359-sound",
+ },
+ .probe = mt6359_platform_driver_probe,
+};
+
+module_platform_driver(mt6359_platform_driver)
+
+/* Module information */
+MODULE_DESCRIPTION("MT6359 ALSA SoC codec driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_AUTHOR("Eason Yen <eason.yen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6359.h b/sound/soc/codecs/mt6359.h
new file mode 100644
index 000000000000..296ffa7f50b5
--- /dev/null
+++ b/sound/soc/codecs/mt6359.h
@@ -0,0 +1,4289 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ * Author: Argus Lin <argus.lin@mediatek.com>
+ */
+
+#ifndef _MT6359_H_
+#define _MT6359_H_
+
+/*************Register Bit Define*************/
+#define MT6359_TOP0_ID 0x0
+#define MT6359_SMT_CON1 0x32
+#define MT6359_DRV_CON2 0x3c
+#define MT6359_DRV_CON3 0x3e
+#define MT6359_DRV_CON4 0x40
+#define MT6359_TOP_CKPDN_CON0 0x10c
+#define MT6359_TOP_CKPDN_CON0_SET 0x10e
+#define MT6359_TOP_CKPDN_CON0_CLR 0x110
+#define MT6359_AUXADC_RQST0 0x1108
+#define MT6359_AUXADC_CON10 0x11a0
+#define MT6359_AUXADC_ACCDET 0x11ba
+#define MT6359_LDO_VUSB_OP_EN 0x1d0c
+#define MT6359_LDO_VUSB_OP_EN_SET 0x1d0e
+#define MT6359_LDO_VUSB_OP_EN_CLR 0x1d10
+#define MT6359_AUD_TOP_CKPDN_CON0 0x230c
+#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e
+#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310
+#define MT6359_AUD_TOP_RST_CON0 0x2320
+#define MT6359_AUD_TOP_RST_CON0_SET 0x2322
+#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324
+#define MT6359_AUD_TOP_INT_CON0 0x2328
+#define MT6359_AUD_TOP_INT_CON0_SET 0x232a
+#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c
+#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e
+#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330
+#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332
+#define MT6359_AUD_TOP_INT_STATUS0 0x2334
+#define MT6359_AFE_NCP_CFG2 0x24e2
+#define MT6359_AUDENC_DSN_ID 0x2500
+#define MT6359_AUDENC_DSN_REV0 0x2502
+#define MT6359_AUDENC_DSN_DBI 0x2504
+#define MT6359_AUDENC_DSN_FPI 0x2506
+#define MT6359_AUDENC_ANA_CON0 0x2508
+#define MT6359_AUDENC_ANA_CON1 0x250a
+#define MT6359_AUDENC_ANA_CON2 0x250c
+#define MT6359_AUDENC_ANA_CON3 0x250e
+#define MT6359_AUDENC_ANA_CON4 0x2510
+#define MT6359_AUDENC_ANA_CON5 0x2512
+#define MT6359_AUDENC_ANA_CON6 0x2514
+#define MT6359_AUDENC_ANA_CON7 0x2516
+#define MT6359_AUDENC_ANA_CON8 0x2518
+#define MT6359_AUDENC_ANA_CON9 0x251a
+#define MT6359_AUDENC_ANA_CON10 0x251c
+#define MT6359_AUDENC_ANA_CON11 0x251e
+#define MT6359_AUDENC_ANA_CON12 0x2520
+#define MT6359_AUDENC_ANA_CON13 0x2522
+#define MT6359_AUDENC_ANA_CON14 0x2524
+#define MT6359_AUDENC_ANA_CON15 0x2526
+#define MT6359_AUDENC_ANA_CON16 0x2528
+#define MT6359_AUDENC_ANA_CON17 0x252a
+#define MT6359_AUDENC_ANA_CON18 0x252c
+#define MT6359_AUDENC_ANA_CON19 0x252e
+#define MT6359_AUDENC_ANA_CON20 0x2530
+#define MT6359_AUDENC_ANA_CON21 0x2532
+#define MT6359_AUDENC_ANA_CON22 0x2534
+#define MT6359_AUDENC_ANA_CON23 0x2536
+#define MT6359_AUDDEC_DSN_ID 0x2580
+#define MT6359_AUDDEC_DSN_REV0 0x2582
+#define MT6359_AUDDEC_DSN_DBI 0x2584
+#define MT6359_AUDDEC_DSN_FPI 0x2586
+#define MT6359_AUDDEC_ANA_CON0 0x2588
+#define MT6359_AUDDEC_ANA_CON1 0x258a
+#define MT6359_AUDDEC_ANA_CON2 0x258c
+#define MT6359_AUDDEC_ANA_CON3 0x258e
+#define MT6359_AUDDEC_ANA_CON4 0x2590
+#define MT6359_AUDDEC_ANA_CON5 0x2592
+#define MT6359_AUDDEC_ANA_CON6 0x2594
+#define MT6359_AUDDEC_ANA_CON7 0x2596
+#define MT6359_AUDDEC_ANA_CON8 0x2598
+#define MT6359_AUDDEC_ANA_CON9 0x259a
+#define MT6359_AUDDEC_ANA_CON10 0x259c
+#define MT6359_AUDDEC_ANA_CON11 0x259e
+#define MT6359_AUDDEC_ANA_CON12 0x25a0
+#define MT6359_AUDDEC_ANA_CON13 0x25a2
+#define MT6359_AUDDEC_ANA_CON14 0x25a4
+#define MT6359_ACCDET_DSN_DIG_ID 0x2680
+#define MT6359_ACCDET_DSN_DIG_REV0 0x2682
+#define MT6359_ACCDET_DSN_DBI 0x2684
+#define MT6359_ACCDET_DSN_FPI 0x2686
+#define MT6359_ACCDET_CON0 0x2688
+#define MT6359_ACCDET_CON1 0x268a
+#define MT6359_ACCDET_CON2 0x268c
+#define MT6359_ACCDET_CON3 0x268e
+#define MT6359_ACCDET_CON4 0x2690
+#define MT6359_ACCDET_CON5 0x2692
+#define MT6359_ACCDET_CON6 0x2694
+#define MT6359_ACCDET_CON7 0x2696
+#define MT6359_ACCDET_CON8 0x2698
+#define MT6359_ACCDET_CON9 0x269a
+#define MT6359_ACCDET_CON10 0x269c
+#define MT6359_ACCDET_CON11 0x269e
+#define MT6359_ACCDET_CON12 0x26a0
+#define MT6359_ACCDET_CON13 0x26a2
+#define MT6359_ACCDET_CON14 0x26a4
+#define MT6359_ACCDET_CON15 0x26a6
+#define MT6359_ACCDET_CON16 0x26a8
+#define MT6359_ACCDET_CON17 0x26aa
+#define MT6359_ACCDET_CON18 0x26ac
+#define MT6359_ACCDET_CON19 0x26ae
+#define MT6359_ACCDET_CON20 0x26b0
+#define MT6359_ACCDET_CON21 0x26b2
+#define MT6359_ACCDET_CON22 0x26b4
+#define MT6359_ACCDET_CON23 0x26b6
+#define MT6359_ACCDET_CON24 0x26b8
+#define MT6359_ACCDET_CON25 0x26ba
+#define MT6359_ACCDET_CON26 0x26bc
+#define MT6359_ACCDET_CON27 0x26be
+#define MT6359_ACCDET_CON28 0x26c0
+#define MT6359_ACCDET_CON29 0x26c2
+#define MT6359_ACCDET_CON30 0x26c4
+#define MT6359_ACCDET_CON31 0x26c6
+#define MT6359_ACCDET_CON32 0x26c8
+#define MT6359_ACCDET_CON33 0x26ca
+#define MT6359_ACCDET_CON34 0x26cc
+#define MT6359_ACCDET_CON35 0x26ce
+#define MT6359_ACCDET_CON36 0x26d0
+#define MT6359_ACCDET_CON37 0x26d2
+#define MT6359_ACCDET_CON38 0x26d4
+#define MT6359_ACCDET_CON39 0x26d6
+#define MT6359_ACCDET_CON40 0x26d8
+
+#define TOP0_ANA_ID_ADDR \
+ MT6359_TOP0_ID
+#define TOP0_ANA_ID_SFT 0
+#define TOP0_ANA_ID_MASK 0xFF
+#define TOP0_ANA_ID_MASK_SFT (0xFF << 0)
+#define AUXADC_RQST_CH0_ADDR \
+ MT6359_AUXADC_RQST0
+#define AUXADC_RQST_CH0_SFT 0
+#define AUXADC_RQST_CH0_MASK 0x1
+#define AUXADC_RQST_CH0_MASK_SFT (0x1 << 0)
+#define AUXADC_ACCDET_ANASWCTRL_EN_ADDR \
+ MT6359_AUXADC_CON15
+#define AUXADC_ACCDET_ANASWCTRL_EN_SFT 6
+#define AUXADC_ACCDET_ANASWCTRL_EN_MASK 0x1
+#define AUXADC_ACCDET_ANASWCTRL_EN_MASK_SFT (0x1 << 6)
+
+#define AUXADC_ACCDET_AUTO_SPL_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_AUTO_SPL_SFT 0
+#define AUXADC_ACCDET_AUTO_SPL_MASK 0x1
+#define AUXADC_ACCDET_AUTO_SPL_MASK_SFT (0x1 << 0)
+#define AUXADC_ACCDET_AUTO_RQST_CLR_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_AUTO_RQST_CLR_SFT 1
+#define AUXADC_ACCDET_AUTO_RQST_CLR_MASK 0x1
+#define AUXADC_ACCDET_AUTO_RQST_CLR_MASK_SFT (0x1 << 1)
+#define AUXADC_ACCDET_DIG1_RSV0_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_DIG1_RSV0_SFT 2
+#define AUXADC_ACCDET_DIG1_RSV0_MASK 0x3F
+#define AUXADC_ACCDET_DIG1_RSV0_MASK_SFT (0x3F << 2)
+#define AUXADC_ACCDET_DIG0_RSV0_ADDR \
+ MT6359_AUXADC_ACCDET
+#define AUXADC_ACCDET_DIG0_RSV0_SFT 8
+#define AUXADC_ACCDET_DIG0_RSV0_MASK 0xFF
+#define AUXADC_ACCDET_DIG0_RSV0_MASK_SFT (0xFF << 8)
+
+#define RG_ACCDET_CK_PDN_ADDR \
+ MT6359_AUD_TOP_CKPDN_CON0
+#define RG_ACCDET_CK_PDN_SFT 0
+#define RG_ACCDET_CK_PDN_MASK 0x1
+#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0)
+
+#define RG_ACCDET_RST_ADDR \
+ MT6359_AUD_TOP_RST_CON0
+#define RG_ACCDET_RST_SFT 1
+#define RG_ACCDET_RST_MASK 0x1
+#define RG_ACCDET_RST_MASK_SFT (0x1 << 1)
+#define BANK_ACCDET_SWRST_ADDR \
+ MT6359_AUD_TOP_RST_BANK_CON0
+#define BANK_ACCDET_SWRST_SFT 0
+#define BANK_ACCDET_SWRST_MASK 0x1
+#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0)
+
+#define RG_INT_EN_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_CON0
+#define RG_INT_EN_ACCDET_SFT 5
+#define RG_INT_EN_ACCDET_MASK 0x1
+#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_EN_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_CON0
+#define RG_INT_EN_ACCDET_EINT0_SFT 6
+#define RG_INT_EN_ACCDET_EINT0_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_EN_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_CON0
+#define RG_INT_EN_ACCDET_EINT1_SFT 7
+#define RG_INT_EN_ACCDET_EINT1_MASK 0x1
+#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_INT_MASK_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_MASK_CON0
+#define RG_INT_MASK_ACCDET_SFT 5
+#define RG_INT_MASK_ACCDET_MASK 0x1
+#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_MASK_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_MASK_CON0
+#define RG_INT_MASK_ACCDET_EINT0_SFT 6
+#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_MASK_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_MASK_CON0
+#define RG_INT_MASK_ACCDET_EINT1_SFT 7
+#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1
+#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_INT_STATUS_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_STATUS0
+#define RG_INT_STATUS_ACCDET_SFT 5
+#define RG_INT_STATUS_ACCDET_MASK 0x1
+#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_STATUS_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_STATUS0
+#define RG_INT_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_STATUS_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_STATUS0
+#define RG_INT_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_INT_RAW_STATUS_ACCDET_ADDR \
+ MT6359_AUD_TOP_INT_RAW_STATUS0
+#define RG_INT_RAW_STATUS_ACCDET_SFT 5
+#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5)
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_ADDR \
+ MT6359_AUD_TOP_INT_RAW_STATUS0
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6)
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_ADDR \
+ MT6359_AUD_TOP_INT_RAW_STATUS0
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1
+#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7)
+
+#define RG_AUDACCDETMICBIAS0PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0)
+#define RG_AUDACCDETMICBIAS1PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1)
+#define RG_AUDACCDETMICBIAS2PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2)
+#define RG_AUDACCDETVIN1PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETVIN1PULLLOW_SFT 3
+#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1
+#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3)
+#define RG_AUDACCDETVTHACAL_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETVTHACAL_SFT 4
+#define RG_AUDACCDETVTHACAL_MASK 0x1
+#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4)
+#define RG_AUDACCDETVTHBCAL_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETVTHBCAL_SFT 5
+#define RG_AUDACCDETVTHBCAL_MASK 0x1
+#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5)
+#define RG_AUDACCDETTVDET_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETTVDET_SFT 6
+#define RG_AUDACCDETTVDET_MASK 0x1
+#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6)
+#define RG_ACCDETSEL_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_ACCDETSEL_SFT 7
+#define RG_ACCDETSEL_MASK 0x1
+#define RG_ACCDETSEL_MASK_SFT (0x1 << 7)
+
+#define RG_AUDPWDBMICBIAS1_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDPWDBMICBIAS1_SFT 0
+#define RG_AUDPWDBMICBIAS1_MASK 0x1
+#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS1BYPASSEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1BYPASSEN_SFT 1
+#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS1LOWPEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1LOWPEN_SFT 2
+#define RG_AUDMICBIAS1LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS1VREF_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1VREF_SFT 4
+#define RG_AUDMICBIAS1VREF_MASK 0x7
+#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS1DCSW1PEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1DCSW1PEN_SFT 8
+#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS1DCSW1NEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1DCSW1NEN_SFT 9
+#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9)
+#define RG_BANDGAPGEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_BANDGAPGEN_SFT 10
+#define RG_BANDGAPGEN_MASK 0x1
+#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS1HVEN_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1HVEN_SFT 12
+#define RG_AUDMICBIAS1HVEN_MASK 0x1
+#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS1HVVREF_ADDR \
+ MT6359_AUDENC_ANA_CON16
+#define RG_AUDMICBIAS1HVVREF_SFT 13
+#define RG_AUDMICBIAS1HVVREF_MASK 0x1
+#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13)
+
+#define RG_EINT0NOHYS_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_EINT0NOHYS_SFT 10
+#define RG_EINT0NOHYS_MASK 0x1
+#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10)
+#define RG_EINT0CONFIGACCDET_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_EINT0CONFIGACCDET_SFT 11
+#define RG_EINT0CONFIGACCDET_MASK 0x1
+#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11)
+#define RG_EINT0HIRENB_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_EINT0HIRENB_SFT 12
+#define RG_EINT0HIRENB_MASK 0x1
+#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12)
+#define RG_ACCDET2AUXRESBYPASS_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_ACCDET2AUXRESBYPASS_SFT 13
+#define RG_ACCDET2AUXRESBYPASS_MASK 0x1
+#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13)
+#define RG_ACCDET2AUXSWEN_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_ACCDET2AUXSWEN_SFT 14
+#define RG_ACCDET2AUXSWEN_MASK 0x1
+#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14)
+#define RG_AUDACCDETMICBIAS3PULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON18
+#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15)
+#define RG_EINT1CONFIGACCDET_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_EINT1CONFIGACCDET_SFT 0
+#define RG_EINT1CONFIGACCDET_MASK 0x1
+#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0)
+#define RG_EINT1HIRENB_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_EINT1HIRENB_SFT 1
+#define RG_EINT1HIRENB_MASK 0x1
+#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1)
+#define RG_EINT1NOHYS_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_EINT1NOHYS_SFT 2
+#define RG_EINT1NOHYS_MASK 0x1
+#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2)
+#define RG_EINTCOMPVTH_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_EN_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_EN_SFT 8
+#define RG_MTEST_EN_MASK 0x1
+#define RG_MTEST_EN_MASK_SFT (0x1 << 8)
+#define RG_MTEST_SEL_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_SEL_SFT 9
+#define RG_MTEST_SEL_MASK 0x1
+#define RG_MTEST_SEL_MASK_SFT (0x1 << 9)
+#define RG_MTEST_CURRENT_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_MTEST_CURRENT_SFT 10
+#define RG_MTEST_CURRENT_MASK 0x1
+#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10)
+#define RG_ANALOGFDEN_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_ANALOGFDEN_SFT 12
+#define RG_ANALOGFDEN_MASK 0x1
+#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12)
+#define RG_FDVIN1PPULLLOW_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_FDVIN1PPULLLOW_SFT 13
+#define RG_FDVIN1PPULLLOW_MASK 0x1
+#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13)
+#define RG_FDEINT0TYPE_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_FDEINT0TYPE_SFT 14
+#define RG_FDEINT0TYPE_MASK 0x1
+#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14)
+#define RG_FDEINT1TYPE_ADDR \
+ MT6359_AUDENC_ANA_CON19
+#define RG_FDEINT1TYPE_SFT 15
+#define RG_FDEINT1TYPE_MASK 0x1
+#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15)
+#define RG_EINT0CMPEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CMPEN_SFT 0
+#define RG_EINT0CMPEN_MASK 0x1
+#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0)
+#define RG_EINT0CMPMEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CMPMEN_SFT 1
+#define RG_EINT0CMPMEN_MASK 0x1
+#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1)
+#define RG_EINT0EN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0EN_SFT 2
+#define RG_EINT0EN_MASK 0x1
+#define RG_EINT0EN_MASK_SFT (0x1 << 2)
+#define RG_EINT0CEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CEN_SFT 3
+#define RG_EINT0CEN_MASK 0x1
+#define RG_EINT0CEN_MASK_SFT (0x1 << 3)
+#define RG_EINT0INVEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0INVEN_SFT 4
+#define RG_EINT0INVEN_MASK 0x1
+#define RG_EINT0INVEN_MASK_SFT (0x1 << 4)
+#define RG_EINT0CTURBO_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT0CTURBO_SFT 5
+#define RG_EINT0CTURBO_MASK 0x7
+#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5)
+#define RG_EINT1CMPEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CMPEN_SFT 8
+#define RG_EINT1CMPEN_MASK 0x1
+#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8)
+#define RG_EINT1CMPMEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CMPMEN_SFT 9
+#define RG_EINT1CMPMEN_MASK 0x1
+#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9)
+#define RG_EINT1EN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1EN_SFT 10
+#define RG_EINT1EN_MASK 0x1
+#define RG_EINT1EN_MASK_SFT (0x1 << 10)
+#define RG_EINT1CEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CEN_SFT 11
+#define RG_EINT1CEN_MASK 0x1
+#define RG_EINT1CEN_MASK_SFT (0x1 << 11)
+#define RG_EINT1INVEN_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1INVEN_SFT 12
+#define RG_EINT1INVEN_MASK 0x1
+#define RG_EINT1INVEN_MASK_SFT (0x1 << 12)
+#define RG_EINT1CTURBO_ADDR \
+ MT6359_AUDENC_ANA_CON20
+#define RG_EINT1CTURBO_SFT 13
+#define RG_EINT1CTURBO_MASK 0x7
+#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13)
+#define RG_ACCDETSPARE_ADDR \
+ MT6359_AUDENC_ANA_CON21
+
+#define ACCDET_ANA_ID_ADDR \
+ MT6359_ACCDET_DSN_DIG_ID
+#define ACCDET_ANA_ID_SFT 0
+#define ACCDET_ANA_ID_MASK 0xFF
+#define ACCDET_ANA_ID_MASK_SFT (0xFF << 0)
+#define ACCDET_DIG_ID_ADDR \
+ MT6359_ACCDET_DSN_DIG_ID
+#define ACCDET_DIG_ID_SFT 8
+#define ACCDET_DIG_ID_MASK 0xFF
+#define ACCDET_DIG_ID_MASK_SFT (0xFF << 8)
+#define ACCDET_ANA_MINOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_ANA_MINOR_REV_SFT 0
+#define ACCDET_ANA_MINOR_REV_MASK 0xF
+#define ACCDET_ANA_MINOR_REV_MASK_SFT (0xF << 0)
+#define ACCDET_ANA_MAJOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_ANA_MAJOR_REV_SFT 4
+#define ACCDET_ANA_MAJOR_REV_MASK 0xF
+#define ACCDET_ANA_MAJOR_REV_MASK_SFT (0xF << 4)
+#define ACCDET_DIG_MINOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_DIG_MINOR_REV_SFT 8
+#define ACCDET_DIG_MINOR_REV_MASK 0xF
+#define ACCDET_DIG_MINOR_REV_MASK_SFT (0xF << 8)
+#define ACCDET_DIG_MAJOR_REV_ADDR \
+ MT6359_ACCDET_DSN_DIG_REV0
+#define ACCDET_DIG_MAJOR_REV_SFT 12
+#define ACCDET_DIG_MAJOR_REV_MASK 0xF
+#define ACCDET_DIG_MAJOR_REV_MASK_SFT (0xF << 12)
+#define ACCDET_DSN_CBS_ADDR \
+ MT6359_ACCDET_DSN_DBI
+#define ACCDET_DSN_CBS_SFT 0
+#define ACCDET_DSN_CBS_MASK 0x3
+#define ACCDET_DSN_CBS_MASK_SFT (0x3 << 0)
+#define ACCDET_DSN_BIX_ADDR \
+ MT6359_ACCDET_DSN_DBI
+#define ACCDET_DSN_BIX_SFT 2
+#define ACCDET_DSN_BIX_MASK 0x3
+#define ACCDET_DSN_BIX_MASK_SFT (0x3 << 2)
+#define ACCDET_ESP_ADDR \
+ MT6359_ACCDET_DSN_DBI
+#define ACCDET_ESP_SFT 8
+#define ACCDET_ESP_MASK 0xFF
+#define ACCDET_ESP_MASK_SFT (0xFF << 8)
+#define ACCDET_DSN_FPI_ADDR \
+ MT6359_ACCDET_DSN_FPI
+#define ACCDET_DSN_FPI_SFT 0
+#define ACCDET_DSN_FPI_MASK 0xFF
+#define ACCDET_DSN_FPI_MASK_SFT (0xFF << 0)
+#define ACCDET_AUXADC_SEL_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_AUXADC_SEL_SFT 0
+#define ACCDET_AUXADC_SEL_MASK 0x1
+#define ACCDET_AUXADC_SEL_MASK_SFT (0x1 << 0)
+#define ACCDET_AUXADC_SW_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_AUXADC_SW_SFT 1
+#define ACCDET_AUXADC_SW_MASK 0x1
+#define ACCDET_AUXADC_SW_MASK_SFT (0x1 << 1)
+#define ACCDET_TEST_AUXADC_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_TEST_AUXADC_SFT 2
+#define ACCDET_TEST_AUXADC_MASK 0x1
+#define ACCDET_TEST_AUXADC_MASK_SFT (0x1 << 2)
+#define ACCDET_AUXADC_ANASWCTRL_SEL_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_AUXADC_ANASWCTRL_SEL_SFT 8
+#define ACCDET_AUXADC_ANASWCTRL_SEL_MASK 0x1
+#define ACCDET_AUXADC_ANASWCTRL_SEL_MASK_SFT (0x1 << 8)
+#define AUDACCDETAUXADCSWCTRL_SEL_ADDR \
+ MT6359_ACCDET_CON0
+#define AUDACCDETAUXADCSWCTRL_SEL_SFT 9
+#define AUDACCDETAUXADCSWCTRL_SEL_MASK 0x1
+#define AUDACCDETAUXADCSWCTRL_SEL_MASK_SFT (0x1 << 9)
+#define AUDACCDETAUXADCSWCTRL_SW_ADDR \
+ MT6359_ACCDET_CON0
+#define AUDACCDETAUXADCSWCTRL_SW_SFT 10
+#define AUDACCDETAUXADCSWCTRL_SW_MASK 0x1
+#define AUDACCDETAUXADCSWCTRL_SW_MASK_SFT (0x1 << 10)
+#define ACCDET_TEST_ANA_ADDR \
+ MT6359_ACCDET_CON0
+#define ACCDET_TEST_ANA_SFT 11
+#define ACCDET_TEST_ANA_MASK 0x1
+#define ACCDET_TEST_ANA_MASK_SFT (0x1 << 11)
+#define RG_AUDACCDETRSV_ADDR \
+ MT6359_ACCDET_CON0
+#define RG_AUDACCDETRSV_SFT 13
+#define RG_AUDACCDETRSV_MASK 0x3
+#define RG_AUDACCDETRSV_MASK_SFT (0x3 << 13)
+#define ACCDET_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_SW_EN_SFT 0
+#define ACCDET_SW_EN_MASK 0x1
+#define ACCDET_SW_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_SEQ_INIT_SFT 1
+#define ACCDET_SEQ_INIT_MASK 0x1
+#define ACCDET_SEQ_INIT_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_SW_EN_SFT 2
+#define ACCDET_EINT0_SW_EN_MASK 0x1
+#define ACCDET_EINT0_SW_EN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_SEQ_INIT_SFT 3
+#define ACCDET_EINT0_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT0_SEQ_INIT_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT1_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_SW_EN_SFT 4
+#define ACCDET_EINT1_SW_EN_MASK 0x1
+#define ACCDET_EINT1_SW_EN_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT1_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_SEQ_INIT_SFT 5
+#define ACCDET_EINT1_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT1_SEQ_INIT_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT0_INVERTER_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_INVERTER_SW_EN_SFT 6
+#define ACCDET_EINT0_INVERTER_SW_EN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_SFT 7
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT0_INVERTER_SEQ_INIT_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_INVERTER_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_INVERTER_SW_EN_SFT 8
+#define ACCDET_EINT1_INVERTER_SW_EN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_SFT 9
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_MASK 0x1
+#define ACCDET_EINT1_INVERTER_SEQ_INIT_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT0_M_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT0_M_SW_EN_SFT 10
+#define ACCDET_EINT0_M_SW_EN_MASK 0x1
+#define ACCDET_EINT0_M_SW_EN_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT1_M_SW_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT1_M_SW_EN_SFT 11
+#define ACCDET_EINT1_M_SW_EN_MASK 0x1
+#define ACCDET_EINT1_M_SW_EN_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_M_DETECT_EN_ADDR \
+ MT6359_ACCDET_CON1
+#define ACCDET_EINT_M_DETECT_EN_SFT 12
+#define ACCDET_EINT_M_DETECT_EN_MASK 0x1
+#define ACCDET_EINT_M_DETECT_EN_MASK_SFT (0x1 << 12)
+#define ACCDET_CMP_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_CMP_PWM_EN_SFT 0
+#define ACCDET_CMP_PWM_EN_MASK 0x1
+#define ACCDET_CMP_PWM_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_VTH_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_VTH_PWM_EN_SFT 1
+#define ACCDET_VTH_PWM_EN_MASK 0x1
+#define ACCDET_VTH_PWM_EN_MASK_SFT (0x1 << 1)
+#define ACCDET_MBIAS_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_MBIAS_PWM_EN_SFT 2
+#define ACCDET_MBIAS_PWM_EN_MASK 0x1
+#define ACCDET_MBIAS_PWM_EN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_EN_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_EN_PWM_EN_SFT 3
+#define ACCDET_EINT_EN_PWM_EN_MASK 0x1
+#define ACCDET_EINT_EN_PWM_EN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_CMPEN_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_CMPEN_PWM_EN_SFT 4
+#define ACCDET_EINT_CMPEN_PWM_EN_MASK 0x1
+#define ACCDET_EINT_CMPEN_PWM_EN_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT_CMPMEN_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_CMPMEN_PWM_EN_SFT 5
+#define ACCDET_EINT_CMPMEN_PWM_EN_MASK 0x1
+#define ACCDET_EINT_CMPMEN_PWM_EN_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_CTURBO_PWM_EN_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT_CTURBO_PWM_EN_SFT 6
+#define ACCDET_EINT_CTURBO_PWM_EN_MASK 0x1
+#define ACCDET_EINT_CTURBO_PWM_EN_MASK_SFT (0x1 << 6)
+#define ACCDET_CMP_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_CMP_PWM_IDLE_SFT 8
+#define ACCDET_CMP_PWM_IDLE_MASK 0x1
+#define ACCDET_CMP_PWM_IDLE_MASK_SFT (0x1 << 8)
+#define ACCDET_VTH_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_VTH_PWM_IDLE_SFT 9
+#define ACCDET_VTH_PWM_IDLE_MASK 0x1
+#define ACCDET_VTH_PWM_IDLE_MASK_SFT (0x1 << 9)
+#define ACCDET_MBIAS_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_MBIAS_PWM_IDLE_SFT 10
+#define ACCDET_MBIAS_PWM_IDLE_MASK 0x1
+#define ACCDET_MBIAS_PWM_IDLE_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_SFT 11
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_MASK 0x1
+#define ACCDET_EINT0_CMPEN_PWM_IDLE_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_SFT 12
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_MASK 0x1
+#define ACCDET_EINT1_CMPEN_PWM_IDLE_MASK_SFT (0x1 << 12)
+#define ACCDET_PWM_EN_SW_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_PWM_EN_SW_SFT 13
+#define ACCDET_PWM_EN_SW_MASK 0x1
+#define ACCDET_PWM_EN_SW_MASK_SFT (0x1 << 13)
+#define ACCDET_PWM_EN_SEL_ADDR \
+ MT6359_ACCDET_CON2
+#define ACCDET_PWM_EN_SEL_SFT 14
+#define ACCDET_PWM_EN_SEL_MASK 0x3
+#define ACCDET_PWM_EN_SEL_MASK_SFT (0x3 << 14)
+#define ACCDET_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON3
+#define ACCDET_PWM_WIDTH_SFT 0
+#define ACCDET_PWM_WIDTH_MASK 0xFFFF
+#define ACCDET_PWM_WIDTH_MASK_SFT (0xFFFF << 0)
+#define ACCDET_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON4
+#define ACCDET_PWM_THRESH_SFT 0
+#define ACCDET_PWM_THRESH_MASK 0xFFFF
+#define ACCDET_PWM_THRESH_MASK_SFT (0xFFFF << 0)
+#define ACCDET_RISE_DELAY_ADDR \
+ MT6359_ACCDET_CON5
+#define ACCDET_RISE_DELAY_SFT 0
+#define ACCDET_RISE_DELAY_MASK 0x7FFF
+#define ACCDET_RISE_DELAY_MASK_SFT (0x7FFF << 0)
+#define ACCDET_FALL_DELAY_ADDR \
+ MT6359_ACCDET_CON5
+#define ACCDET_FALL_DELAY_SFT 15
+#define ACCDET_FALL_DELAY_MASK 0x1
+#define ACCDET_FALL_DELAY_MASK_SFT (0x1 << 15)
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON6
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_SFT 0
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_MASK 0x7
+#define ACCDET_EINT_CMPMEN_PWM_THRESH_MASK_SFT (0x7 << 0)
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON6
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_SFT 4
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_MASK 0x7
+#define ACCDET_EINT_CMPMEN_PWM_WIDTH_MASK_SFT (0x7 << 4)
+#define ACCDET_EINT_EN_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_EN_PWM_THRESH_SFT 0
+#define ACCDET_EINT_EN_PWM_THRESH_MASK 0x7
+#define ACCDET_EINT_EN_PWM_THRESH_MASK_SFT (0x7 << 0)
+#define ACCDET_EINT_EN_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_EN_PWM_WIDTH_SFT 4
+#define ACCDET_EINT_EN_PWM_WIDTH_MASK 0x3
+#define ACCDET_EINT_EN_PWM_WIDTH_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT_CMPEN_PWM_THRESH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_CMPEN_PWM_THRESH_SFT 8
+#define ACCDET_EINT_CMPEN_PWM_THRESH_MASK 0x7
+#define ACCDET_EINT_CMPEN_PWM_THRESH_MASK_SFT (0x7 << 8)
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_ADDR \
+ MT6359_ACCDET_CON7
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_SFT 12
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_MASK 0x3
+#define ACCDET_EINT_CMPEN_PWM_WIDTH_MASK_SFT (0x3 << 12)
+#define ACCDET_DEBOUNCE0_ADDR \
+ MT6359_ACCDET_CON8
+#define ACCDET_DEBOUNCE0_SFT 0
+#define ACCDET_DEBOUNCE0_MASK 0xFFFF
+#define ACCDET_DEBOUNCE0_MASK_SFT (0xFFFF << 0)
+#define ACCDET_DEBOUNCE1_ADDR \
+ MT6359_ACCDET_CON9
+#define ACCDET_DEBOUNCE1_SFT 0
+#define ACCDET_DEBOUNCE1_MASK 0xFFFF
+#define ACCDET_DEBOUNCE1_MASK_SFT (0xFFFF << 0)
+#define ACCDET_DEBOUNCE2_ADDR \
+ MT6359_ACCDET_CON10
+#define ACCDET_DEBOUNCE2_SFT 0
+#define ACCDET_DEBOUNCE2_MASK 0xFFFF
+#define ACCDET_DEBOUNCE2_MASK_SFT (0xFFFF << 0)
+#define ACCDET_DEBOUNCE3_ADDR \
+ MT6359_ACCDET_CON11
+#define ACCDET_DEBOUNCE3_SFT 0
+#define ACCDET_DEBOUNCE3_MASK 0xFFFF
+#define ACCDET_DEBOUNCE3_MASK_SFT (0xFFFF << 0)
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR \
+ MT6359_ACCDET_CON12
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_SFT 0
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_MASK 0xFFFF
+#define ACCDET_CONNECT_AUXADC_TIME_DIG_MASK_SFT (0xFFFF << 0)
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_ADDR \
+ MT6359_ACCDET_CON13
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_SFT 0
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_MASK 0xFFFF
+#define ACCDET_CONNECT_AUXADC_TIME_ANA_MASK_SFT (0xFFFF << 0)
+#define ACCDET_EINT_DEBOUNCE0_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE0_SFT 0
+#define ACCDET_EINT_DEBOUNCE0_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE0_MASK_SFT (0xF << 0)
+#define ACCDET_EINT_DEBOUNCE1_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE1_SFT 4
+#define ACCDET_EINT_DEBOUNCE1_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE1_MASK_SFT (0xF << 4)
+#define ACCDET_EINT_DEBOUNCE2_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE2_SFT 8
+#define ACCDET_EINT_DEBOUNCE2_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE2_MASK_SFT (0xF << 8)
+#define ACCDET_EINT_DEBOUNCE3_ADDR \
+ MT6359_ACCDET_CON14
+#define ACCDET_EINT_DEBOUNCE3_SFT 12
+#define ACCDET_EINT_DEBOUNCE3_MASK 0xF
+#define ACCDET_EINT_DEBOUNCE3_MASK_SFT (0xF << 12)
+#define ACCDET_EINT_INVERTER_DEBOUNCE_ADDR \
+ MT6359_ACCDET_CON15
+#define ACCDET_EINT_INVERTER_DEBOUNCE_SFT 0
+#define ACCDET_EINT_INVERTER_DEBOUNCE_MASK 0xF
+#define ACCDET_EINT_INVERTER_DEBOUNCE_MASK_SFT (0xF << 0)
+#define ACCDET_IVAL_CUR_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_CUR_IN_SFT 0
+#define ACCDET_IVAL_CUR_IN_MASK 0x3
+#define ACCDET_IVAL_CUR_IN_MASK_SFT (0x3 << 0)
+#define ACCDET_IVAL_SAM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_SAM_IN_SFT 2
+#define ACCDET_IVAL_SAM_IN_MASK 0x3
+#define ACCDET_IVAL_SAM_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_IVAL_MEM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_MEM_IN_SFT 4
+#define ACCDET_IVAL_MEM_IN_MASK 0x3
+#define ACCDET_IVAL_MEM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT_IVAL_CUR_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_CUR_IN_SFT 6
+#define ACCDET_EINT_IVAL_CUR_IN_MASK 0x3
+#define ACCDET_EINT_IVAL_CUR_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_EINT_IVAL_SAM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_SAM_IN_SFT 8
+#define ACCDET_EINT_IVAL_SAM_IN_MASK 0x3
+#define ACCDET_EINT_IVAL_SAM_IN_MASK_SFT (0x3 << 8)
+#define ACCDET_EINT_IVAL_MEM_IN_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_MEM_IN_SFT 10
+#define ACCDET_EINT_IVAL_MEM_IN_MASK 0x3
+#define ACCDET_EINT_IVAL_MEM_IN_MASK_SFT (0x3 << 10)
+#define ACCDET_IVAL_SEL_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_IVAL_SEL_SFT 12
+#define ACCDET_IVAL_SEL_MASK 0x1
+#define ACCDET_IVAL_SEL_MASK_SFT (0x1 << 12)
+#define ACCDET_EINT_IVAL_SEL_ADDR \
+ MT6359_ACCDET_CON16
+#define ACCDET_EINT_IVAL_SEL_SFT 13
+#define ACCDET_EINT_IVAL_SEL_MASK 0x1
+#define ACCDET_EINT_IVAL_SEL_MASK_SFT (0x1 << 13)
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_SFT 0
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_SFT 1
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_SFT 2
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_INVERTER_IVAL_SEL_ADDR \
+ MT6359_ACCDET_CON17
+#define ACCDET_EINT_INVERTER_IVAL_SEL_SFT 3
+#define ACCDET_EINT_INVERTER_IVAL_SEL_MASK 0x1
+#define ACCDET_EINT_INVERTER_IVAL_SEL_MASK_SFT (0x1 << 3)
+#define ACCDET_IRQ_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_IRQ_SFT 0
+#define ACCDET_IRQ_MASK 0x1
+#define ACCDET_IRQ_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_IRQ_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT0_IRQ_SFT 2
+#define ACCDET_EINT0_IRQ_MASK 0x1
+#define ACCDET_EINT0_IRQ_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT1_IRQ_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT1_IRQ_SFT 3
+#define ACCDET_EINT1_IRQ_MASK 0x1
+#define ACCDET_EINT1_IRQ_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_IN_INVERSE_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT_IN_INVERSE_SFT 4
+#define ACCDET_EINT_IN_INVERSE_MASK 0x1
+#define ACCDET_EINT_IN_INVERSE_MASK_SFT (0x1 << 4)
+#define ACCDET_IRQ_CLR_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_IRQ_CLR_SFT 8
+#define ACCDET_IRQ_CLR_MASK 0x1
+#define ACCDET_IRQ_CLR_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT0_IRQ_CLR_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT0_IRQ_CLR_SFT 10
+#define ACCDET_EINT0_IRQ_CLR_MASK 0x1
+#define ACCDET_EINT0_IRQ_CLR_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT1_IRQ_CLR_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT1_IRQ_CLR_SFT 11
+#define ACCDET_EINT1_IRQ_CLR_MASK 0x1
+#define ACCDET_EINT1_IRQ_CLR_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_M_PLUG_IN_NUM_ADDR \
+ MT6359_ACCDET_CON18
+#define ACCDET_EINT_M_PLUG_IN_NUM_SFT 12
+#define ACCDET_EINT_M_PLUG_IN_NUM_MASK 0x7
+#define ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT (0x7 << 12)
+#define ACCDET_DA_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_DA_STABLE_SFT 0
+#define ACCDET_DA_STABLE_MASK 0x1
+#define ACCDET_DA_STABLE_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_EN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_EN_STABLE_SFT 1
+#define ACCDET_EINT0_EN_STABLE_MASK 0x1
+#define ACCDET_EINT0_EN_STABLE_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_CMPEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CMPEN_STABLE_SFT 2
+#define ACCDET_EINT0_CMPEN_STABLE_MASK 0x1
+#define ACCDET_EINT0_CMPEN_STABLE_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_CMPMEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CMPMEN_STABLE_SFT 3
+#define ACCDET_EINT0_CMPMEN_STABLE_MASK 0x1
+#define ACCDET_EINT0_CMPMEN_STABLE_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_CTURBO_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CTURBO_STABLE_SFT 4
+#define ACCDET_EINT0_CTURBO_STABLE_MASK 0x1
+#define ACCDET_EINT0_CTURBO_STABLE_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT0_CEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT0_CEN_STABLE_SFT 5
+#define ACCDET_EINT0_CEN_STABLE_MASK 0x1
+#define ACCDET_EINT0_CEN_STABLE_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT1_EN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_EN_STABLE_SFT 6
+#define ACCDET_EINT1_EN_STABLE_MASK 0x1
+#define ACCDET_EINT1_EN_STABLE_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT1_CMPEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CMPEN_STABLE_SFT 7
+#define ACCDET_EINT1_CMPEN_STABLE_MASK 0x1
+#define ACCDET_EINT1_CMPEN_STABLE_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_CMPMEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CMPMEN_STABLE_SFT 8
+#define ACCDET_EINT1_CMPMEN_STABLE_MASK 0x1
+#define ACCDET_EINT1_CMPMEN_STABLE_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_CTURBO_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CTURBO_STABLE_SFT 9
+#define ACCDET_EINT1_CTURBO_STABLE_MASK 0x1
+#define ACCDET_EINT1_CTURBO_STABLE_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT1_CEN_STABLE_ADDR \
+ MT6359_ACCDET_CON19
+#define ACCDET_EINT1_CEN_STABLE_SFT 10
+#define ACCDET_EINT1_CEN_STABLE_MASK 0x1
+#define ACCDET_EINT1_CEN_STABLE_MASK_SFT (0x1 << 10)
+#define ACCDET_HWMODE_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_HWMODE_EN_SFT 0
+#define ACCDET_HWMODE_EN_MASK 0x1
+#define ACCDET_HWMODE_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_HWMODE_SEL_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_HWMODE_SEL_SFT 1
+#define ACCDET_HWMODE_SEL_MASK 0x3
+#define ACCDET_HWMODE_SEL_MASK_SFT (0x3 << 1)
+#define ACCDET_PLUG_OUT_DETECT_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_PLUG_OUT_DETECT_SFT 3
+#define ACCDET_PLUG_OUT_DETECT_MASK 0x1
+#define ACCDET_PLUG_OUT_DETECT_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_REVERSE_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT0_REVERSE_SFT 4
+#define ACCDET_EINT0_REVERSE_MASK 0x1
+#define ACCDET_EINT0_REVERSE_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT1_REVERSE_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT1_REVERSE_SFT 5
+#define ACCDET_EINT1_REVERSE_MASK 0x1
+#define ACCDET_EINT1_REVERSE_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_HWMODE_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_HWMODE_EN_SFT 8
+#define ACCDET_EINT_HWMODE_EN_MASK 0x1
+#define ACCDET_EINT_HWMODE_EN_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_SFT 9
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_MASK 0x1
+#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT_M_PLUG_IN_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_M_PLUG_IN_EN_SFT 10
+#define ACCDET_EINT_M_PLUG_IN_EN_MASK 0x1
+#define ACCDET_EINT_M_PLUG_IN_EN_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT_M_HWMODE_EN_ADDR \
+ MT6359_ACCDET_CON20
+#define ACCDET_EINT_M_HWMODE_EN_SFT 11
+#define ACCDET_EINT_M_HWMODE_EN_MASK 0x1
+#define ACCDET_EINT_M_HWMODE_EN_MASK_SFT (0x1 << 11)
+#define ACCDET_TEST_CMPEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_CMPEN_SFT 0
+#define ACCDET_TEST_CMPEN_MASK 0x1
+#define ACCDET_TEST_CMPEN_MASK_SFT (0x1 << 0)
+#define ACCDET_TEST_VTHEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_VTHEN_SFT 1
+#define ACCDET_TEST_VTHEN_MASK 0x1
+#define ACCDET_TEST_VTHEN_MASK_SFT (0x1 << 1)
+#define ACCDET_TEST_MBIASEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_MBIASEN_SFT 2
+#define ACCDET_TEST_MBIASEN_MASK 0x1
+#define ACCDET_TEST_MBIASEN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_TEST_EN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_EN_SFT 3
+#define ACCDET_EINT_TEST_EN_MASK 0x1
+#define ACCDET_EINT_TEST_EN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_TEST_INVEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_INVEN_SFT 4
+#define ACCDET_EINT_TEST_INVEN_MASK 0x1
+#define ACCDET_EINT_TEST_INVEN_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT_TEST_CMPEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPEN_SFT 5
+#define ACCDET_EINT_TEST_CMPEN_MASK 0x1
+#define ACCDET_EINT_TEST_CMPEN_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_TEST_CMPMEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPMEN_SFT 6
+#define ACCDET_EINT_TEST_CMPMEN_MASK 0x1
+#define ACCDET_EINT_TEST_CMPMEN_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT_TEST_CTURBO_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CTURBO_SFT 7
+#define ACCDET_EINT_TEST_CTURBO_MASK 0x1
+#define ACCDET_EINT_TEST_CTURBO_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT_TEST_CEN_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CEN_SFT 8
+#define ACCDET_EINT_TEST_CEN_MASK 0x1
+#define ACCDET_EINT_TEST_CEN_MASK_SFT (0x1 << 8)
+#define ACCDET_TEST_B_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_B_SFT 9
+#define ACCDET_TEST_B_MASK 0x1
+#define ACCDET_TEST_B_MASK_SFT (0x1 << 9)
+#define ACCDET_TEST_A_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_TEST_A_SFT 10
+#define ACCDET_TEST_A_MASK 0x1
+#define ACCDET_TEST_A_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT_TEST_CMPOUT_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPOUT_SFT 11
+#define ACCDET_EINT_TEST_CMPOUT_MASK 0x1
+#define ACCDET_EINT_TEST_CMPOUT_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_TEST_CMPMOUT_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_CMPMOUT_SFT 12
+#define ACCDET_EINT_TEST_CMPMOUT_MASK 0x1
+#define ACCDET_EINT_TEST_CMPMOUT_MASK_SFT (0x1 << 12)
+#define ACCDET_EINT_TEST_INVOUT_ADDR \
+ MT6359_ACCDET_CON21
+#define ACCDET_EINT_TEST_INVOUT_SFT 13
+#define ACCDET_EINT_TEST_INVOUT_MASK 0x1
+#define ACCDET_EINT_TEST_INVOUT_MASK_SFT (0x1 << 13)
+#define ACCDET_CMPEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_CMPEN_SEL_SFT 0
+#define ACCDET_CMPEN_SEL_MASK 0x1
+#define ACCDET_CMPEN_SEL_MASK_SFT (0x1 << 0)
+#define ACCDET_VTHEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_VTHEN_SEL_SFT 1
+#define ACCDET_VTHEN_SEL_MASK 0x1
+#define ACCDET_VTHEN_SEL_MASK_SFT (0x1 << 1)
+#define ACCDET_MBIASEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_MBIASEN_SEL_SFT 2
+#define ACCDET_MBIASEN_SEL_MASK 0x1
+#define ACCDET_MBIASEN_SEL_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT_EN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_EN_SEL_SFT 3
+#define ACCDET_EINT_EN_SEL_MASK 0x1
+#define ACCDET_EINT_EN_SEL_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT_INVEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_INVEN_SEL_SFT 4
+#define ACCDET_EINT_INVEN_SEL_MASK 0x1
+#define ACCDET_EINT_INVEN_SEL_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT_CMPEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPEN_SEL_SFT 5
+#define ACCDET_EINT_CMPEN_SEL_MASK 0x1
+#define ACCDET_EINT_CMPEN_SEL_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT_CMPMEN_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPMEN_SEL_SFT 6
+#define ACCDET_EINT_CMPMEN_SEL_MASK 0x1
+#define ACCDET_EINT_CMPMEN_SEL_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT_CTURBO_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CTURBO_SEL_SFT 7
+#define ACCDET_EINT_CTURBO_SEL_MASK 0x1
+#define ACCDET_EINT_CTURBO_SEL_MASK_SFT (0x1 << 7)
+#define ACCDET_B_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_B_SEL_SFT 9
+#define ACCDET_B_SEL_MASK 0x1
+#define ACCDET_B_SEL_MASK_SFT (0x1 << 9)
+#define ACCDET_A_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_A_SEL_SFT 10
+#define ACCDET_A_SEL_MASK 0x1
+#define ACCDET_A_SEL_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT_CMPOUT_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPOUT_SEL_SFT 11
+#define ACCDET_EINT_CMPOUT_SEL_MASK 0x1
+#define ACCDET_EINT_CMPOUT_SEL_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT_CMPMOUT_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_CMPMOUT_SEL_SFT 12
+#define ACCDET_EINT_CMPMOUT_SEL_MASK 0x1
+#define ACCDET_EINT_CMPMOUT_SEL_MASK_SFT (0x1 << 12)
+#define ACCDET_EINT_INVOUT_SEL_ADDR \
+ MT6359_ACCDET_CON22
+#define ACCDET_EINT_INVOUT_SEL_SFT 13
+#define ACCDET_EINT_INVOUT_SEL_MASK 0x1
+#define ACCDET_EINT_INVOUT_SEL_MASK_SFT (0x1 << 13)
+#define ACCDET_CMPEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_CMPEN_SW_SFT 0
+#define ACCDET_CMPEN_SW_MASK 0x1
+#define ACCDET_CMPEN_SW_MASK_SFT (0x1 << 0)
+#define ACCDET_VTHEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_VTHEN_SW_SFT 1
+#define ACCDET_VTHEN_SW_MASK 0x1
+#define ACCDET_VTHEN_SW_MASK_SFT (0x1 << 1)
+#define ACCDET_MBIASEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_MBIASEN_SW_SFT 2
+#define ACCDET_MBIASEN_SW_MASK 0x1
+#define ACCDET_MBIASEN_SW_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_EN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_EN_SW_SFT 3
+#define ACCDET_EINT0_EN_SW_MASK 0x1
+#define ACCDET_EINT0_EN_SW_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_INVEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_INVEN_SW_SFT 4
+#define ACCDET_EINT0_INVEN_SW_MASK 0x1
+#define ACCDET_EINT0_INVEN_SW_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT0_CMPEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_CMPEN_SW_SFT 5
+#define ACCDET_EINT0_CMPEN_SW_MASK 0x1
+#define ACCDET_EINT0_CMPEN_SW_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT0_CMPMEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_CMPMEN_SW_SFT 6
+#define ACCDET_EINT0_CMPMEN_SW_MASK 0x1
+#define ACCDET_EINT0_CMPMEN_SW_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT0_CTURBO_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT0_CTURBO_SW_SFT 7
+#define ACCDET_EINT0_CTURBO_SW_MASK 0x1
+#define ACCDET_EINT0_CTURBO_SW_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_EN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_EN_SW_SFT 8
+#define ACCDET_EINT1_EN_SW_MASK 0x1
+#define ACCDET_EINT1_EN_SW_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_INVEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_INVEN_SW_SFT 9
+#define ACCDET_EINT1_INVEN_SW_MASK 0x1
+#define ACCDET_EINT1_INVEN_SW_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT1_CMPEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_CMPEN_SW_SFT 10
+#define ACCDET_EINT1_CMPEN_SW_MASK 0x1
+#define ACCDET_EINT1_CMPEN_SW_MASK_SFT (0x1 << 10)
+#define ACCDET_EINT1_CMPMEN_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_CMPMEN_SW_SFT 11
+#define ACCDET_EINT1_CMPMEN_SW_MASK 0x1
+#define ACCDET_EINT1_CMPMEN_SW_MASK_SFT (0x1 << 11)
+#define ACCDET_EINT1_CTURBO_SW_ADDR \
+ MT6359_ACCDET_CON23
+#define ACCDET_EINT1_CTURBO_SW_SFT 12
+#define ACCDET_EINT1_CTURBO_SW_MASK 0x1
+#define ACCDET_EINT1_CTURBO_SW_MASK_SFT (0x1 << 12)
+#define ACCDET_B_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_B_SW_SFT 0
+#define ACCDET_B_SW_MASK 0x1
+#define ACCDET_B_SW_MASK_SFT (0x1 << 0)
+#define ACCDET_A_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_A_SW_SFT 1
+#define ACCDET_A_SW_MASK 0x1
+#define ACCDET_A_SW_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_CMPOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT0_CMPOUT_SW_SFT 2
+#define ACCDET_EINT0_CMPOUT_SW_MASK 0x1
+#define ACCDET_EINT0_CMPOUT_SW_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_CMPMOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT0_CMPMOUT_SW_SFT 3
+#define ACCDET_EINT0_CMPMOUT_SW_MASK 0x1
+#define ACCDET_EINT0_CMPMOUT_SW_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_INVOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT0_INVOUT_SW_SFT 4
+#define ACCDET_EINT0_INVOUT_SW_MASK 0x1
+#define ACCDET_EINT0_INVOUT_SW_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT1_CMPOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT1_CMPOUT_SW_SFT 5
+#define ACCDET_EINT1_CMPOUT_SW_MASK 0x1
+#define ACCDET_EINT1_CMPOUT_SW_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT1_CMPMOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT1_CMPMOUT_SW_SFT 6
+#define ACCDET_EINT1_CMPMOUT_SW_MASK 0x1
+#define ACCDET_EINT1_CMPMOUT_SW_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT1_INVOUT_SW_ADDR \
+ MT6359_ACCDET_CON24
+#define ACCDET_EINT1_INVOUT_SW_SFT 7
+#define ACCDET_EINT1_INVOUT_SW_MASK 0x1
+#define ACCDET_EINT1_INVOUT_SW_MASK_SFT (0x1 << 7)
+#define AD_AUDACCDETCMPOB_ADDR \
+ MT6359_ACCDET_CON25
+#define AD_AUDACCDETCMPOB_SFT 0
+#define AD_AUDACCDETCMPOB_MASK 0x1
+#define AD_AUDACCDETCMPOB_MASK_SFT (0x1 << 0)
+#define AD_AUDACCDETCMPOA_ADDR \
+ MT6359_ACCDET_CON25
+#define AD_AUDACCDETCMPOA_SFT 1
+#define AD_AUDACCDETCMPOA_MASK 0x1
+#define AD_AUDACCDETCMPOA_MASK_SFT (0x1 << 1)
+#define ACCDET_CUR_IN_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_CUR_IN_SFT 2
+#define ACCDET_CUR_IN_MASK 0x3
+#define ACCDET_CUR_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_SAM_IN_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_SAM_IN_SFT 4
+#define ACCDET_SAM_IN_MASK 0x3
+#define ACCDET_SAM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_MEM_IN_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_MEM_IN_SFT 6
+#define ACCDET_MEM_IN_MASK 0x3
+#define ACCDET_MEM_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_STATE_ADDR \
+ MT6359_ACCDET_CON25
+#define ACCDET_STATE_SFT 8
+#define ACCDET_STATE_MASK 0x7
+#define ACCDET_STATE_MASK_SFT (0x7 << 8)
+#define DA_AUDACCDETMBIASCLK_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETMBIASCLK_SFT 12
+#define DA_AUDACCDETMBIASCLK_MASK 0x1
+#define DA_AUDACCDETMBIASCLK_MASK_SFT (0x1 << 12)
+#define DA_AUDACCDETVTHCLK_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETVTHCLK_SFT 13
+#define DA_AUDACCDETVTHCLK_MASK 0x1
+#define DA_AUDACCDETVTHCLK_MASK_SFT (0x1 << 13)
+#define DA_AUDACCDETCMPCLK_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETCMPCLK_SFT 14
+#define DA_AUDACCDETCMPCLK_MASK 0x1
+#define DA_AUDACCDETCMPCLK_MASK_SFT (0x1 << 14)
+#define DA_AUDACCDETAUXADCSWCTRL_ADDR \
+ MT6359_ACCDET_CON25
+#define DA_AUDACCDETAUXADCSWCTRL_SFT 15
+#define DA_AUDACCDETAUXADCSWCTRL_MASK 0x1
+#define DA_AUDACCDETAUXADCSWCTRL_MASK_SFT (0x1 << 15)
+#define AD_EINT0CMPMOUT_ADDR \
+ MT6359_ACCDET_CON26
+#define AD_EINT0CMPMOUT_SFT 0
+#define AD_EINT0CMPMOUT_MASK 0x1
+#define AD_EINT0CMPMOUT_MASK_SFT (0x1 << 0)
+#define AD_EINT0CMPOUT_ADDR \
+ MT6359_ACCDET_CON26
+#define AD_EINT0CMPOUT_SFT 1
+#define AD_EINT0CMPOUT_MASK 0x1
+#define AD_EINT0CMPOUT_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_CUR_IN_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_CUR_IN_SFT 2
+#define ACCDET_EINT0_CUR_IN_MASK 0x3
+#define ACCDET_EINT0_CUR_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_EINT0_SAM_IN_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_SAM_IN_SFT 4
+#define ACCDET_EINT0_SAM_IN_MASK 0x3
+#define ACCDET_EINT0_SAM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT0_MEM_IN_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_MEM_IN_SFT 6
+#define ACCDET_EINT0_MEM_IN_MASK 0x3
+#define ACCDET_EINT0_MEM_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_EINT0_STATE_ADDR \
+ MT6359_ACCDET_CON26
+#define ACCDET_EINT0_STATE_SFT 8
+#define ACCDET_EINT0_STATE_MASK 0x7
+#define ACCDET_EINT0_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT0CMPEN_ADDR \
+ MT6359_ACCDET_CON26
+#define DA_EINT0CMPEN_SFT 13
+#define DA_EINT0CMPEN_MASK 0x1
+#define DA_EINT0CMPEN_MASK_SFT (0x1 << 13)
+#define DA_EINT0CMPMEN_ADDR \
+ MT6359_ACCDET_CON26
+#define DA_EINT0CMPMEN_SFT 14
+#define DA_EINT0CMPMEN_MASK 0x1
+#define DA_EINT0CMPMEN_MASK_SFT (0x1 << 14)
+#define DA_EINT0CTURBO_ADDR \
+ MT6359_ACCDET_CON26
+#define DA_EINT0CTURBO_SFT 15
+#define DA_EINT0CTURBO_MASK 0x1
+#define DA_EINT0CTURBO_MASK_SFT (0x1 << 15)
+#define AD_EINT1CMPMOUT_ADDR \
+ MT6359_ACCDET_CON27
+#define AD_EINT1CMPMOUT_SFT 0
+#define AD_EINT1CMPMOUT_MASK 0x1
+#define AD_EINT1CMPMOUT_MASK_SFT (0x1 << 0)
+#define AD_EINT1CMPOUT_ADDR \
+ MT6359_ACCDET_CON27
+#define AD_EINT1CMPOUT_SFT 1
+#define AD_EINT1CMPOUT_MASK 0x1
+#define AD_EINT1CMPOUT_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT1_CUR_IN_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_CUR_IN_SFT 2
+#define ACCDET_EINT1_CUR_IN_MASK 0x3
+#define ACCDET_EINT1_CUR_IN_MASK_SFT (0x3 << 2)
+#define ACCDET_EINT1_SAM_IN_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_SAM_IN_SFT 4
+#define ACCDET_EINT1_SAM_IN_MASK 0x3
+#define ACCDET_EINT1_SAM_IN_MASK_SFT (0x3 << 4)
+#define ACCDET_EINT1_MEM_IN_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_MEM_IN_SFT 6
+#define ACCDET_EINT1_MEM_IN_MASK 0x3
+#define ACCDET_EINT1_MEM_IN_MASK_SFT (0x3 << 6)
+#define ACCDET_EINT1_STATE_ADDR \
+ MT6359_ACCDET_CON27
+#define ACCDET_EINT1_STATE_SFT 8
+#define ACCDET_EINT1_STATE_MASK 0x7
+#define ACCDET_EINT1_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT1CMPEN_ADDR \
+ MT6359_ACCDET_CON27
+#define DA_EINT1CMPEN_SFT 13
+#define DA_EINT1CMPEN_MASK 0x1
+#define DA_EINT1CMPEN_MASK_SFT (0x1 << 13)
+#define DA_EINT1CMPMEN_ADDR \
+ MT6359_ACCDET_CON27
+#define DA_EINT1CMPMEN_SFT 14
+#define DA_EINT1CMPMEN_MASK 0x1
+#define DA_EINT1CMPMEN_MASK_SFT (0x1 << 14)
+#define DA_EINT1CTURBO_ADDR \
+ MT6359_ACCDET_CON27
+#define DA_EINT1CTURBO_SFT 15
+#define DA_EINT1CTURBO_MASK 0x1
+#define DA_EINT1CTURBO_MASK_SFT (0x1 << 15)
+#define AD_EINT0INVOUT_ADDR \
+ MT6359_ACCDET_CON28
+#define AD_EINT0INVOUT_SFT 0
+#define AD_EINT0INVOUT_MASK 0x1
+#define AD_EINT0INVOUT_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_INVERTER_CUR_IN_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_CUR_IN_SFT 1
+#define ACCDET_EINT0_INVERTER_CUR_IN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_CUR_IN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT0_INVERTER_SAM_IN_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_SAM_IN_SFT 2
+#define ACCDET_EINT0_INVERTER_SAM_IN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_SAM_IN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_INVERTER_MEM_IN_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_MEM_IN_SFT 3
+#define ACCDET_EINT0_INVERTER_MEM_IN_MASK 0x1
+#define ACCDET_EINT0_INVERTER_MEM_IN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_INVERTER_STATE_ADDR \
+ MT6359_ACCDET_CON28
+#define ACCDET_EINT0_INVERTER_STATE_SFT 8
+#define ACCDET_EINT0_INVERTER_STATE_MASK 0x7
+#define ACCDET_EINT0_INVERTER_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT0EN_ADDR \
+ MT6359_ACCDET_CON28
+#define DA_EINT0EN_SFT 12
+#define DA_EINT0EN_MASK 0x1
+#define DA_EINT0EN_MASK_SFT (0x1 << 12)
+#define DA_EINT0INVEN_ADDR \
+ MT6359_ACCDET_CON28
+#define DA_EINT0INVEN_SFT 13
+#define DA_EINT0INVEN_MASK 0x1
+#define DA_EINT0INVEN_MASK_SFT (0x1 << 13)
+#define DA_EINT0CEN_ADDR \
+ MT6359_ACCDET_CON28
+#define DA_EINT0CEN_SFT 14
+#define DA_EINT0CEN_MASK 0x1
+#define DA_EINT0CEN_MASK_SFT (0x1 << 14)
+#define AD_EINT1INVOUT_ADDR \
+ MT6359_ACCDET_CON29
+#define AD_EINT1INVOUT_SFT 0
+#define AD_EINT1INVOUT_MASK 0x1
+#define AD_EINT1INVOUT_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT1_INVERTER_CUR_IN_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_CUR_IN_SFT 1
+#define ACCDET_EINT1_INVERTER_CUR_IN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_CUR_IN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT1_INVERTER_SAM_IN_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_SAM_IN_SFT 2
+#define ACCDET_EINT1_INVERTER_SAM_IN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_SAM_IN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT1_INVERTER_MEM_IN_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_MEM_IN_SFT 3
+#define ACCDET_EINT1_INVERTER_MEM_IN_MASK 0x1
+#define ACCDET_EINT1_INVERTER_MEM_IN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT1_INVERTER_STATE_ADDR \
+ MT6359_ACCDET_CON29
+#define ACCDET_EINT1_INVERTER_STATE_SFT 8
+#define ACCDET_EINT1_INVERTER_STATE_MASK 0x7
+#define ACCDET_EINT1_INVERTER_STATE_MASK_SFT (0x7 << 8)
+#define DA_EINT1EN_ADDR \
+ MT6359_ACCDET_CON29
+#define DA_EINT1EN_SFT 12
+#define DA_EINT1EN_MASK 0x1
+#define DA_EINT1EN_MASK_SFT (0x1 << 12)
+#define DA_EINT1INVEN_ADDR \
+ MT6359_ACCDET_CON29
+#define DA_EINT1INVEN_SFT 13
+#define DA_EINT1INVEN_MASK 0x1
+#define DA_EINT1INVEN_MASK_SFT (0x1 << 13)
+#define DA_EINT1CEN_ADDR \
+ MT6359_ACCDET_CON29
+#define DA_EINT1CEN_SFT 14
+#define DA_EINT1CEN_MASK 0x1
+#define DA_EINT1CEN_MASK_SFT (0x1 << 14)
+#define ACCDET_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EN_SFT 0
+#define ACCDET_EN_MASK 0x1
+#define ACCDET_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_EINT0_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_EN_SFT 1
+#define ACCDET_EINT0_EN_MASK 0x1
+#define ACCDET_EINT0_EN_MASK_SFT (0x1 << 1)
+#define ACCDET_EINT1_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_EN_SFT 2
+#define ACCDET_EINT1_EN_MASK 0x1
+#define ACCDET_EINT1_EN_MASK_SFT (0x1 << 2)
+#define ACCDET_EINT0_M_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_M_EN_SFT 3
+#define ACCDET_EINT0_M_EN_MASK 0x1
+#define ACCDET_EINT0_M_EN_MASK_SFT (0x1 << 3)
+#define ACCDET_EINT0_DETECT_MOISTURE_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_DETECT_MOISTURE_SFT 4
+#define ACCDET_EINT0_DETECT_MOISTURE_MASK 0x1
+#define ACCDET_EINT0_DETECT_MOISTURE_MASK_SFT (0x1 << 4)
+#define ACCDET_EINT0_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_PLUG_IN_SFT 5
+#define ACCDET_EINT0_PLUG_IN_MASK 0x1
+#define ACCDET_EINT0_PLUG_IN_MASK_SFT (0x1 << 5)
+#define ACCDET_EINT0_M_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT0_M_PLUG_IN_SFT 6
+#define ACCDET_EINT0_M_PLUG_IN_MASK 0x1
+#define ACCDET_EINT0_M_PLUG_IN_MASK_SFT (0x1 << 6)
+#define ACCDET_EINT1_M_EN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_M_EN_SFT 7
+#define ACCDET_EINT1_M_EN_MASK 0x1
+#define ACCDET_EINT1_M_EN_MASK_SFT (0x1 << 7)
+#define ACCDET_EINT1_DETECT_MOISTURE_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_DETECT_MOISTURE_SFT 8
+#define ACCDET_EINT1_DETECT_MOISTURE_MASK 0x1
+#define ACCDET_EINT1_DETECT_MOISTURE_MASK_SFT (0x1 << 8)
+#define ACCDET_EINT1_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_PLUG_IN_SFT 9
+#define ACCDET_EINT1_PLUG_IN_MASK 0x1
+#define ACCDET_EINT1_PLUG_IN_MASK_SFT (0x1 << 9)
+#define ACCDET_EINT1_M_PLUG_IN_ADDR \
+ MT6359_ACCDET_CON30
+#define ACCDET_EINT1_M_PLUG_IN_SFT 10
+#define ACCDET_EINT1_M_PLUG_IN_MASK 0x1
+#define ACCDET_EINT1_M_PLUG_IN_MASK_SFT (0x1 << 10)
+#define ACCDET_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON31
+#define ACCDET_CUR_DEB_SFT 0
+#define ACCDET_CUR_DEB_MASK 0xFFFF
+#define ACCDET_CUR_DEB_MASK_SFT (0xFFFF << 0)
+#define ACCDET_EINT0_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON32
+#define ACCDET_EINT0_CUR_DEB_SFT 0
+#define ACCDET_EINT0_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT0_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define ACCDET_EINT1_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON33
+#define ACCDET_EINT1_CUR_DEB_SFT 0
+#define ACCDET_EINT1_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT1_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define ACCDET_EINT0_INVERTER_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON34
+#define ACCDET_EINT0_INVERTER_CUR_DEB_SFT 0
+#define ACCDET_EINT0_INVERTER_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT0_INVERTER_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define ACCDET_EINT1_INVERTER_CUR_DEB_ADDR \
+ MT6359_ACCDET_CON35
+#define ACCDET_EINT1_INVERTER_CUR_DEB_SFT 0
+#define ACCDET_EINT1_INVERTER_CUR_DEB_MASK 0x7FFF
+#define ACCDET_EINT1_INVERTER_CUR_DEB_MASK_SFT (0x7FFF << 0)
+#define AD_AUDACCDETCMPOB_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_AUDACCDETCMPOB_MON_SFT 0
+#define AD_AUDACCDETCMPOB_MON_MASK 0x1
+#define AD_AUDACCDETCMPOB_MON_MASK_SFT (0x1 << 0)
+#define AD_AUDACCDETCMPOA_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_AUDACCDETCMPOA_MON_SFT 1
+#define AD_AUDACCDETCMPOA_MON_MASK 0x1
+#define AD_AUDACCDETCMPOA_MON_MASK_SFT (0x1 << 1)
+#define AD_EINT0CMPMOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT0CMPMOUT_MON_SFT 2
+#define AD_EINT0CMPMOUT_MON_MASK 0x1
+#define AD_EINT0CMPMOUT_MON_MASK_SFT (0x1 << 2)
+#define AD_EINT0CMPOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT0CMPOUT_MON_SFT 3
+#define AD_EINT0CMPOUT_MON_MASK 0x1
+#define AD_EINT0CMPOUT_MON_MASK_SFT (0x1 << 3)
+#define AD_EINT0INVOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT0INVOUT_MON_SFT 4
+#define AD_EINT0INVOUT_MON_MASK 0x1
+#define AD_EINT0INVOUT_MON_MASK_SFT (0x1 << 4)
+#define AD_EINT1CMPMOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT1CMPMOUT_MON_SFT 5
+#define AD_EINT1CMPMOUT_MON_MASK 0x1
+#define AD_EINT1CMPMOUT_MON_MASK_SFT (0x1 << 5)
+#define AD_EINT1CMPOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT1CMPOUT_MON_SFT 6
+#define AD_EINT1CMPOUT_MON_MASK 0x1
+#define AD_EINT1CMPOUT_MON_MASK_SFT (0x1 << 6)
+#define AD_EINT1INVOUT_MON_ADDR \
+ MT6359_ACCDET_CON36
+#define AD_EINT1INVOUT_MON_SFT 7
+#define AD_EINT1INVOUT_MON_MASK 0x1
+#define AD_EINT1INVOUT_MON_MASK_SFT (0x1 << 7)
+#define DA_AUDACCDETCMPCLK_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETCMPCLK_MON_SFT 0
+#define DA_AUDACCDETCMPCLK_MON_MASK 0x1
+#define DA_AUDACCDETCMPCLK_MON_MASK_SFT (0x1 << 0)
+#define DA_AUDACCDETVTHCLK_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETVTHCLK_MON_SFT 1
+#define DA_AUDACCDETVTHCLK_MON_MASK 0x1
+#define DA_AUDACCDETVTHCLK_MON_MASK_SFT (0x1 << 1)
+#define DA_AUDACCDETMBIASCLK_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETMBIASCLK_MON_SFT 2
+#define DA_AUDACCDETMBIASCLK_MON_MASK 0x1
+#define DA_AUDACCDETMBIASCLK_MON_MASK_SFT (0x1 << 2)
+#define DA_AUDACCDETAUXADCSWCTRL_MON_ADDR \
+ MT6359_ACCDET_CON37
+#define DA_AUDACCDETAUXADCSWCTRL_MON_SFT 3
+#define DA_AUDACCDETAUXADCSWCTRL_MON_MASK 0x1
+#define DA_AUDACCDETAUXADCSWCTRL_MON_MASK_SFT (0x1 << 3)
+#define DA_EINT0CTURBO_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CTURBO_MON_SFT 0
+#define DA_EINT0CTURBO_MON_MASK 0x1
+#define DA_EINT0CTURBO_MON_MASK_SFT (0x1 << 0)
+#define DA_EINT0CMPMEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CMPMEN_MON_SFT 1
+#define DA_EINT0CMPMEN_MON_MASK 0x1
+#define DA_EINT0CMPMEN_MON_MASK_SFT (0x1 << 1)
+#define DA_EINT0CMPEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CMPEN_MON_SFT 2
+#define DA_EINT0CMPEN_MON_MASK 0x1
+#define DA_EINT0CMPEN_MON_MASK_SFT (0x1 << 2)
+#define DA_EINT0INVEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0INVEN_MON_SFT 3
+#define DA_EINT0INVEN_MON_MASK 0x1
+#define DA_EINT0INVEN_MON_MASK_SFT (0x1 << 3)
+#define DA_EINT0CEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0CEN_MON_SFT 4
+#define DA_EINT0CEN_MON_MASK 0x1
+#define DA_EINT0CEN_MON_MASK_SFT (0x1 << 4)
+#define DA_EINT0EN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT0EN_MON_SFT 5
+#define DA_EINT0EN_MON_MASK 0x1
+#define DA_EINT0EN_MON_MASK_SFT (0x1 << 5)
+#define DA_EINT1CTURBO_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CTURBO_MON_SFT 8
+#define DA_EINT1CTURBO_MON_MASK 0x1
+#define DA_EINT1CTURBO_MON_MASK_SFT (0x1 << 8)
+#define DA_EINT1CMPMEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CMPMEN_MON_SFT 9
+#define DA_EINT1CMPMEN_MON_MASK 0x1
+#define DA_EINT1CMPMEN_MON_MASK_SFT (0x1 << 9)
+#define DA_EINT1CMPEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CMPEN_MON_SFT 10
+#define DA_EINT1CMPEN_MON_MASK 0x1
+#define DA_EINT1CMPEN_MON_MASK_SFT (0x1 << 10)
+#define DA_EINT1INVEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1INVEN_MON_SFT 11
+#define DA_EINT1INVEN_MON_MASK 0x1
+#define DA_EINT1INVEN_MON_MASK_SFT (0x1 << 11)
+#define DA_EINT1CEN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1CEN_MON_SFT 12
+#define DA_EINT1CEN_MON_MASK 0x1
+#define DA_EINT1CEN_MON_MASK_SFT (0x1 << 12)
+#define DA_EINT1EN_MON_ADDR \
+ MT6359_ACCDET_CON38
+#define DA_EINT1EN_MON_SFT 13
+#define DA_EINT1EN_MON_MASK 0x1
+#define DA_EINT1EN_MON_MASK_SFT (0x1 << 13)
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_ADDR \
+ MT6359_ACCDET_CON39
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_SFT 0
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_MASK 0x7
+#define ACCDET_EINT0_M_PLUG_IN_COUNT_MASK_SFT (0x7 << 0)
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_ADDR \
+ MT6359_ACCDET_CON39
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_SFT 4
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_MASK 0x7
+#define ACCDET_EINT1_M_PLUG_IN_COUNT_MASK_SFT (0x7 << 4)
+#define ACCDET_MON_FLAG_EN_ADDR \
+ MT6359_ACCDET_CON40
+#define ACCDET_MON_FLAG_EN_SFT 0
+#define ACCDET_MON_FLAG_EN_MASK 0x1
+#define ACCDET_MON_FLAG_EN_MASK_SFT (0x1 << 0)
+#define ACCDET_MON_FLAG_SEL_ADDR \
+ MT6359_ACCDET_CON40
+#define ACCDET_MON_FLAG_SEL_SFT 4
+#define ACCDET_MON_FLAG_SEL_MASK 0xF
+#define ACCDET_MON_FLAG_SEL_MASK_SFT (0xF << 4)
+
+#define RG_AUDPWDBMICBIAS0_ADDR \
+ MT6359_AUDENC_ANA_CON15
+#define RG_AUDPWDBMICBIAS0_SFT 0
+#define RG_AUDPWDBMICBIAS0_MASK 0x1
+#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPLON_ADDR \
+ MT6359_AUDENC_ANA_CON0
+#define RG_AUDPREAMPLON_SFT 0
+#define RG_AUDPREAMPLON_MASK 0x1
+#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0)
+#define RG_CLKSQ_EN_ADDR \
+ MT6359_AUDENC_ANA_CON23
+#define RG_CLKSQ_EN_SFT 0
+#define RG_CLKSQ_EN_MASK 0x1
+#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0)
+#define RG_RTC32K_CK_PDN_ADDR \
+ MT6359_TOP_CKPDN_CON0
+#define RG_RTC32K_CK_PDN_SFT 15
+#define RG_RTC32K_CK_PDN_MASK 0x1
+#define RG_RTC32K_CK_PDN_MASK_SFT (0x1 << 15)
+#define RG_HPLOUTPUTSTBENH_VAUDP32_ADDR \
+ MT6359_AUDDEC_ANA_CON2
+#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0)
+#define AUXADC_RQST_CH5_ADDR \
+ MT6359_AUXADC_RQST0
+#define AUXADC_RQST_CH5_SFT 5
+#define AUXADC_RQST_CH5_MASK 0x1
+#define AUXADC_RQST_CH5_MASK_SFT (0x1 << 5)
+#define RG_LDO_VUSB_HW0_OP_EN_ADDR \
+ MT6359_LDO_VUSB_OP_EN
+#define RG_LDO_VUSB_HW0_OP_EN_SFT 0
+#define RG_LDO_VUSB_HW0_OP_EN_MASK 0x1
+#define RG_LDO_VUSB_HW0_OP_EN_MASK_SFT (0x1 << 0)
+#define RG_HPROUTPUTSTBENH_VAUDP32_ADDR \
+ MT6359_AUDDEC_ANA_CON2
+#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4)
+#define RG_NCP_PDDIS_EN_ADDR \
+ MT6359_AFE_NCP_CFG2
+#define RG_NCP_PDDIS_EN_SFT 0
+#define RG_NCP_PDDIS_EN_MASK 0x1
+#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0)
+#define RG_SCK32K_CK_PDN_ADDR \
+ MT6359_TOP_CKPDN_CON0
+#define RG_SCK32K_CK_PDN_SFT 0
+#define RG_SCK32K_CK_PDN_MASK 0x1
+#define RG_SCK32K_CK_PDN_MASK_SFT (0x1 << 0)
+/* AUDENC_ANA_CON18: */
+#define RG_ACCDET_MODE_ANA11_MODE1 (0x000F)
+#define RG_ACCDET_MODE_ANA11_MODE2 (0x008F)
+#define RG_ACCDET_MODE_ANA11_MODE6 (0x008F)
+
+/* AUXADC_ADC5: Auxadc CH5 read data */
+#define AUXADC_DATA_RDY_CH5 BIT(15)
+#define AUXADC_DATA_PROCEED_CH5 BIT(15)
+#define AUXADC_DATA_MASK (0x0FFF)
+
+/* AUXADC_RQST0_SET: Auxadc CH5 request, relevant 0x07EC */
+#define AUXADC_RQST_CH5_SET BIT(5)
+/* AUXADC_RQST0_CLR: Auxadc CH5 request, relevant 0x07EC */
+#define AUXADC_RQST_CH5_CLR BIT(5)
+
+#define ACCDET_CALI_MASK0 (0xFF)
+#define ACCDET_CALI_MASK1 (0xFF << 8)
+#define ACCDET_CALI_MASK2 (0xFF)
+#define ACCDET_CALI_MASK3 (0xFF << 8)
+#define ACCDET_CALI_MASK4 (0xFF)
+
+#define ACCDET_EINT_IRQ_B2_B3 (0x03 << ACCDET_EINT0_IRQ_SFT)
+
+/* ACCDET_CON25: RO, accdet FSM state,etc.*/
+#define ACCDET_STATE_MEM_IN_OFFSET (ACCDET_MEM_IN_SFT)
+#define ACCDET_STATE_AB_MASK (0x03)
+#define ACCDET_STATE_AB_00 (0x00)
+#define ACCDET_STATE_AB_01 (0x01)
+#define ACCDET_STATE_AB_10 (0x02)
+#define ACCDET_STATE_AB_11 (0x03)
+
+/* ACCDET_CON19 */
+#define ACCDET_EINT0_STABLE_VAL ((ACCDET_DA_STABLE_MASK_SFT) | \
+ (ACCDET_EINT0_EN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT0_CMPEN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT0_CEN_STABLE_MASK_SFT))
+
+#define ACCDET_EINT1_STABLE_VAL ((ACCDET_DA_STABLE_MASK_SFT) | \
+ (ACCDET_EINT1_EN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT1_CMPEN_STABLE_MASK_SFT) | \
+ (ACCDET_EINT1_CEN_STABLE_MASK_SFT))
+/* The following are used for mt6359.c */
+/* MT6359_DCXO_CW12 */
+#define RG_XO_AUDIO_EN_M_SFT 13
+
+/* AUD_TOP_CKPDN_CON0 */
+#define RG_VOW13M_CK_PDN_SFT 13
+#define RG_VOW13M_CK_PDN_MASK 0x1
+#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13)
+#define RG_VOW32K_CK_PDN_SFT 12
+#define RG_VOW32K_CK_PDN_MASK 0x1
+#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12)
+#define RG_AUD_INTRP_CK_PDN_SFT 8
+#define RG_AUD_INTRP_CK_PDN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8)
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1
+#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7)
+#define RG_AUDNCP_CK_PDN_SFT 6
+#define RG_AUDNCP_CK_PDN_MASK 0x1
+#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6)
+#define RG_ZCD13M_CK_PDN_SFT 5
+#define RG_ZCD13M_CK_PDN_MASK 0x1
+#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5)
+#define RG_AUDIF_CK_PDN_SFT 2
+#define RG_AUDIF_CK_PDN_MASK 0x1
+#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2)
+#define RG_AUD_CK_PDN_SFT 1
+#define RG_AUD_CK_PDN_MASK 0x1
+#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1)
+#define RG_ACCDET_CK_PDN_SFT 0
+#define RG_ACCDET_CK_PDN_MASK 0x1
+#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_CKPDN_CON0_SET */
+#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0)
+
+/* AUD_TOP_CKPDN_CON0_CLR */
+#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff
+#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0)
+
+/* AUD_TOP_CKSEL_CON0 */
+#define RG_AUDIF_CK_CKSEL_SFT 3
+#define RG_AUDIF_CK_CKSEL_MASK 0x1
+#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_CKSEL_SFT 2
+#define RG_AUD_CK_CKSEL_MASK 0x1
+#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2)
+
+/* AUD_TOP_CKSEL_CON0_SET */
+#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_CKSEL_CON0_CLR */
+#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_CKTST_CON0 */
+#define RG_VOW13M_CK_TSTSEL_SFT 9
+#define RG_VOW13M_CK_TSTSEL_MASK 0x1
+#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9)
+#define RG_VOW13M_CK_TST_DIS_SFT 8
+#define RG_VOW13M_CK_TST_DIS_MASK 0x1
+#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8)
+#define RG_AUD26M_CK_TSTSEL_SFT 4
+#define RG_AUD26M_CK_TSTSEL_MASK 0x1
+#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4)
+#define RG_AUDIF_CK_TSTSEL_SFT 3
+#define RG_AUDIF_CK_TSTSEL_MASK 0x1
+#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3)
+#define RG_AUD_CK_TSTSEL_SFT 2
+#define RG_AUD_CK_TSTSEL_MASK 0x1
+#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2)
+#define RG_AUD26M_CK_TST_DIS_SFT 0
+#define RG_AUD26M_CK_TST_DIS_MASK 0x1
+#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_CLK_HWEN_CON0 */
+#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1
+#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_CLK_HWEN_CON0_SET */
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff
+#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0)
+
+/* AUD_TOP_CLK_HWEN_CON0_CLR */
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff
+#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0)
+
+/* AUD_TOP_RST_CON0 */
+#define RG_AUDNCP_RST_SFT 3
+#define RG_AUDNCP_RST_MASK 0x1
+#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3)
+#define RG_ZCD_RST_SFT 2
+#define RG_ZCD_RST_MASK 0x1
+#define RG_ZCD_RST_MASK_SFT (0x1 << 2)
+#define RG_ACCDET_RST_SFT 1
+#define RG_ACCDET_RST_MASK 0x1
+#define RG_ACCDET_RST_MASK_SFT (0x1 << 1)
+#define RG_AUDIO_RST_SFT 0
+#define RG_AUDIO_RST_MASK 0x1
+#define RG_AUDIO_RST_MASK_SFT (0x1 << 0)
+
+/* AUD_TOP_RST_CON0_SET */
+#define RG_AUD_TOP_RST_CON0_SET_SFT 0
+#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_RST_CON0_CLR */
+#define RG_AUD_TOP_RST_CON0_CLR_SFT 0
+#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf
+#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0)
+
+/* AUD_TOP_RST_BANK_CON0 */
+#define BANK_AUDZCD_SWRST_SFT 2
+#define BANK_AUDZCD_SWRST_MASK 0x1
+#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2)
+#define BANK_AUDIO_SWRST_SFT 1
+#define BANK_AUDIO_SWRST_MASK 0x1
+#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1)
+#define BANK_ACCDET_SWRST_SFT 0
+#define BANK_ACCDET_SWRST_MASK 0x1
+#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0)
+
+/* AFE_UL_DL_CON0 */
+#define AFE_UL_LR_SWAP_SFT 15
+#define AFE_UL_LR_SWAP_MASK 0x1
+#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15)
+#define AFE_DL_LR_SWAP_SFT 14
+#define AFE_DL_LR_SWAP_MASK 0x1
+#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DL_SRC2_CON0_L */
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* AFE_UL_SRC_CON0_H */
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11)
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8)
+#define C_TWO_DIGITAL_MIC_CTL_SFT 7
+#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7)
+
+/* AFE_UL_SRC_CON0_L */
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define DIGMIC_4P33M_SEL_CTL_SFT 6
+#define DIGMIC_4P33M_SEL_CTL_MASK 0x1
+#define DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA6_L_SRC_CON0_H */
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11)
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8)
+#define ADDA6_C_TWO_DIGITAL_MIC_CTL_SFT 7
+#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7)
+
+/* AFE_ADDA6_UL_SRC_CON0_L */
+#define ADDA6_DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define ADDA6_DIGMIC_4P33M_SEL_CTL_SFT 6
+#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK 0x1
+#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6)
+#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define ADDA6_UL_LOOP_BACK_MODE_CTL_SFT 2
+#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define ADDA6_UL_SDM_3_LEVEL_CTL_SFT 1
+#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define ADDA6_UL_SRC_ON_TMP_CTL_SFT 0
+#define ADDA6_UL_SRC_ON_TMP_CTL_MASK 0x1
+#define ADDA6_UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_TOP_CON0 */
+#define ADDA6_MTKAIF_SINE_ON_SFT 4
+#define ADDA6_MTKAIF_SINE_ON_MASK 0x1
+#define ADDA6_MTKAIF_SINE_ON_MASK_SFT (0x1 << 4)
+#define ADDA6_UL_SINE_ON_SFT 3
+#define ADDA6_UL_SINE_ON_MASK 0x1
+#define ADDA6_UL_SINE_ON_MASK_SFT (0x1 << 3)
+#define MTKAIF_SINE_ON_SFT 2
+#define MTKAIF_SINE_ON_MASK 0x1
+#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2)
+#define UL_SINE_ON_SFT 1
+#define UL_SINE_ON_MASK 0x1
+#define UL_SINE_ON_MASK_SFT (0x1 << 1)
+#define DL_SINE_ON_SFT 0
+#define DL_SINE_ON_MASK 0x1
+#define DL_SINE_ON_MASK_SFT (0x1 << 0)
+
+/* AUDIO_TOP_CON0 */
+#define PDN_AFE_CTL_SFT 7
+#define PDN_AFE_CTL_MASK 0x1
+#define PDN_AFE_CTL_MASK_SFT (0x1 << 7)
+#define PDN_DAC_CTL_SFT 6
+#define PDN_DAC_CTL_MASK 0x1
+#define PDN_DAC_CTL_MASK_SFT (0x1 << 6)
+#define PDN_ADC_CTL_SFT 5
+#define PDN_ADC_CTL_MASK 0x1
+#define PDN_ADC_CTL_MASK_SFT (0x1 << 5)
+#define PDN_ADDA6_ADC_CTL_SFT 4
+#define PDN_ADDA6_ADC_CTL_MASK 0x1
+#define PDN_ADDA6_ADC_CTL_MASK_SFT (0x1 << 4)
+#define PDN_I2S_DL_CTL_SFT 3
+#define PDN_I2S_DL_CTL_MASK 0x1
+#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3)
+#define PWR_CLK_DIS_CTL_SFT 2
+#define PWR_CLK_DIS_CTL_MASK 0x1
+#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2)
+#define PDN_AFE_TESTMODEL_CTL_SFT 1
+#define PDN_AFE_TESTMODEL_CTL_MASK 0x1
+#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1)
+#define PDN_RESERVED_SFT 0
+#define PDN_RESERVED_MASK 0x1
+#define PDN_RESERVED_MASK_SFT (0x1 << 0)
+
+/* AFE_MON_DEBUG0 */
+#define AUDIO_SYS_TOP_MON_SWAP_SFT 14
+#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3
+#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14)
+#define AUDIO_SYS_TOP_MON_SEL_SFT 8
+#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f
+#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8)
+#define AFE_MON_SEL_SFT 0
+#define AFE_MON_SEL_MASK 0xff
+#define AFE_MON_SEL_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON0 */
+#define CCI_AUD_ANACK_SEL_SFT 15
+#define CCI_AUD_ANACK_SEL_MASK 0x1
+#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15)
+#define CCI_AUDIO_FIFO_WPTR_SFT 12
+#define CCI_AUDIO_FIFO_WPTR_MASK 0x7
+#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12)
+#define CCI_SCRAMBLER_CG_EN_SFT 11
+#define CCI_SCRAMBLER_CG_EN_MASK 0x1
+#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11)
+#define CCI_LCH_INV_SFT 10
+#define CCI_LCH_INV_MASK 0x1
+#define CCI_LCH_INV_MASK_SFT (0x1 << 10)
+#define CCI_RAND_EN_SFT 9
+#define CCI_RAND_EN_MASK 0x1
+#define CCI_RAND_EN_MASK_SFT (0x1 << 9)
+#define CCI_SPLT_SCRMB_CLK_ON_SFT 8
+#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8)
+#define CCI_SPLT_SCRMB_ON_SFT 7
+#define CCI_SPLT_SCRMB_ON_MASK 0x1
+#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7)
+#define CCI_AUD_IDAC_TEST_EN_SFT 6
+#define CCI_AUD_IDAC_TEST_EN_MASK 0x1
+#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6)
+#define CCI_ZERO_PAD_DISABLE_SFT 5
+#define CCI_ZERO_PAD_DISABLE_MASK 0x1
+#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5)
+#define CCI_AUD_SPLIT_TEST_EN_SFT 4
+#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1
+#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4)
+#define CCI_AUD_SDM_MUTEL_SFT 3
+#define CCI_AUD_SDM_MUTEL_MASK 0x1
+#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3)
+#define CCI_AUD_SDM_MUTER_SFT 2
+#define CCI_AUD_SDM_MUTER_MASK 0x1
+#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2)
+#define CCI_AUD_SDM_7BIT_SEL_SFT 1
+#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1
+#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1)
+#define CCI_SCRAMBLER_EN_SFT 0
+#define CCI_SCRAMBLER_EN_MASK 0x1
+#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON1 */
+#define AUD_SDM_TEST_L_SFT 8
+#define AUD_SDM_TEST_L_MASK 0xff
+#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8)
+#define AUD_SDM_TEST_R_SFT 0
+#define AUD_SDM_TEST_R_MASK 0xff
+#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON2 */
+#define CCI_AUD_DAC_ANA_MUTE_SFT 7
+#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1
+#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7)
+#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1
+#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6)
+#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1
+#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4)
+#define CCI_AUDIO_FIFO_ENABLE_SFT 3
+#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1
+#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3)
+#define CCI_ACD_MODE_SFT 2
+#define CCI_ACD_MODE_MASK 0x1
+#define CCI_ACD_MODE_MASK_SFT (0x1 << 2)
+#define CCI_AFIFO_CLK_PWDB_SFT 1
+#define CCI_AFIFO_CLK_PWDB_MASK 0x1
+#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1)
+#define CCI_ACD_FUNC_RSTB_SFT 0
+#define CCI_ACD_FUNC_RSTB_MASK 0x1
+#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON3 */
+#define SDM_ANA13M_TESTCK_SEL_SFT 15
+#define SDM_ANA13M_TESTCK_SEL_MASK 0x1
+#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15)
+#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12)
+#define SDM_TESTCK_SRC_SEL_SFT 8
+#define SDM_TESTCK_SRC_SEL_MASK 0x7
+#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8)
+#define DIGMIC_TESTCK_SRC_SEL_SFT 4
+#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7
+#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4)
+#define DIGMIC_TESTCK_SEL_SFT 0
+#define DIGMIC_TESTCK_SEL_MASK 0x1
+#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON4 */
+#define UL_FIFO_WCLK_INV_SFT 8
+#define UL_FIFO_WCLK_INV_MASK 0x1
+#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8)
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6)
+#define UL_FIFO_WDATA_TESTEN_SFT 5
+#define UL_FIFO_WDATA_TESTEN_MASK 0x1
+#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5)
+#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1
+#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3)
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7
+#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0)
+
+/* AFUNC_AUD_CON5 */
+#define R_AUD_DAC_POS_LARGE_MONO_SFT 8
+#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8)
+#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff
+#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON6 */
+#define R_AUD_DAC_POS_SMALL_MONO_SFT 12
+#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12)
+#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf
+#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8)
+#define R_AUD_DAC_POS_TINY_MONO_SFT 6
+#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6)
+#define R_AUD_DAC_NEG_TINY_MONO_SFT 4
+#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3
+#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4)
+#define R_AUD_DAC_MONO_SEL_SFT 3
+#define R_AUD_DAC_MONO_SEL_MASK 0x1
+#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3)
+#define R_AUD_DAC_3TH_SEL_SFT 1
+#define R_AUD_DAC_3TH_SEL_MASK 0x1
+#define R_AUD_DAC_3TH_SEL_MASK_SFT (0x1 << 1)
+#define R_AUD_DAC_SW_RSTB_SFT 0
+#define R_AUD_DAC_SW_RSTB_MASK 0x1
+#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON7 */
+#define UL2_DIGMIC_TESTCK_SRC_SEL_SFT 10
+#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK 0x7
+#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 10)
+#define UL2_DIGMIC_TESTCK_SEL_SFT 9
+#define UL2_DIGMIC_TESTCK_SEL_MASK 0x1
+#define UL2_DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 9)
+#define UL2_FIFO_WCLK_INV_SFT 8
+#define UL2_FIFO_WCLK_INV_MASK 0x1
+#define UL2_FIFO_WCLK_INV_MASK_SFT (0x1 << 8)
+#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6
+#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6)
+#define UL2_FIFO_WDATA_TESTEN_SFT 5
+#define UL2_FIFO_WDATA_TESTEN_MASK 0x1
+#define UL2_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5)
+#define UL2_FIFO_WDATA_TESTSRC_SEL_SFT 4
+#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK 0x1
+#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4)
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3)
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7
+#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0)
+
+/* AFUNC_AUD_CON8 */
+#define SPLITTER2_DITHER_EN_SFT 9
+#define SPLITTER2_DITHER_EN_MASK 0x1
+#define SPLITTER2_DITHER_EN_MASK_SFT (0x1 << 9)
+#define SPLITTER1_DITHER_EN_SFT 8
+#define SPLITTER1_DITHER_EN_MASK 0x1
+#define SPLITTER1_DITHER_EN_MASK_SFT (0x1 << 8)
+#define SPLITTER2_DITHER_GAIN_SFT 4
+#define SPLITTER2_DITHER_GAIN_MASK 0xf
+#define SPLITTER2_DITHER_GAIN_MASK_SFT (0xf << 4)
+#define SPLITTER1_DITHER_GAIN_SFT 0
+#define SPLITTER1_DITHER_GAIN_MASK 0xf
+#define SPLITTER1_DITHER_GAIN_MASK_SFT (0xf << 0)
+
+/* AFUNC_AUD_CON9 */
+#define CCI_AUD_ANACK_SEL_2ND_SFT 15
+#define CCI_AUD_ANACK_SEL_2ND_MASK 0x1
+#define CCI_AUD_ANACK_SEL_2ND_MASK_SFT (0x1 << 15)
+#define CCI_AUDIO_FIFO_WPTR_2ND_SFT 12
+#define CCI_AUDIO_FIFO_WPTR_2ND_MASK 0x7
+#define CCI_AUDIO_FIFO_WPTR_2ND_MASK_SFT (0x7 << 12)
+#define CCI_SCRAMBLER_CG_EN_2ND_SFT 11
+#define CCI_SCRAMBLER_CG_EN_2ND_MASK 0x1
+#define CCI_SCRAMBLER_CG_EN_2ND_MASK_SFT (0x1 << 11)
+#define CCI_LCH_INV_2ND_SFT 10
+#define CCI_LCH_INV_2ND_MASK 0x1
+#define CCI_LCH_INV_2ND_MASK_SFT (0x1 << 10)
+#define CCI_RAND_EN_2ND_SFT 9
+#define CCI_RAND_EN_2ND_MASK 0x1
+#define CCI_RAND_EN_2ND_MASK_SFT (0x1 << 9)
+#define CCI_SPLT_SCRMB_CLK_ON_2ND_SFT 8
+#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK 0x1
+#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK_SFT (0x1 << 8)
+#define CCI_SPLT_SCRMB_ON_2ND_SFT 7
+#define CCI_SPLT_SCRMB_ON_2ND_MASK 0x1
+#define CCI_SPLT_SCRMB_ON_2ND_MASK_SFT (0x1 << 7)
+#define CCI_AUD_IDAC_TEST_EN_2ND_SFT 6
+#define CCI_AUD_IDAC_TEST_EN_2ND_MASK 0x1
+#define CCI_AUD_IDAC_TEST_EN_2ND_MASK_SFT (0x1 << 6)
+#define CCI_ZERO_PAD_DISABLE_2ND_SFT 5
+#define CCI_ZERO_PAD_DISABLE_2ND_MASK 0x1
+#define CCI_ZERO_PAD_DISABLE_2ND_MASK_SFT (0x1 << 5)
+#define CCI_AUD_SPLIT_TEST_EN_2ND_SFT 4
+#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK 0x1
+#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK_SFT (0x1 << 4)
+#define CCI_AUD_SDM_MUTEL_2ND_SFT 3
+#define CCI_AUD_SDM_MUTEL_2ND_MASK 0x1
+#define CCI_AUD_SDM_MUTEL_2ND_MASK_SFT (0x1 << 3)
+#define CCI_AUD_SDM_MUTER_2ND_SFT 2
+#define CCI_AUD_SDM_MUTER_2ND_MASK 0x1
+#define CCI_AUD_SDM_MUTER_2ND_MASK_SFT (0x1 << 2)
+#define CCI_AUD_SDM_7BIT_SEL_2ND_SFT 1
+#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK 0x1
+#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK_SFT (0x1 << 1)
+#define CCI_SCRAMBLER_EN_2ND_SFT 0
+#define CCI_SCRAMBLER_EN_2ND_MASK 0x1
+#define CCI_SCRAMBLER_EN_2ND_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON10 */
+#define AUD_SDM_TEST_L_2ND_SFT 8
+#define AUD_SDM_TEST_L_2ND_MASK 0xff
+#define AUD_SDM_TEST_L_2ND_MASK_SFT (0xff << 8)
+#define AUD_SDM_TEST_R_2ND_SFT 0
+#define AUD_SDM_TEST_R_2ND_MASK 0xff
+#define AUD_SDM_TEST_R_2ND_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_CON11 */
+#define CCI_AUD_DAC_ANA_MUTE_2ND_SFT 7
+#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK 0x1
+#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK_SFT (0x1 << 7)
+#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_SFT 6
+#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK 0x1
+#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK_SFT (0x1 << 6)
+#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_SFT 4
+#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK 0x1
+#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK_SFT (0x1 << 4)
+#define CCI_AUDIO_FIFO_ENABLE_2ND_SFT 3
+#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK 0x1
+#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK_SFT (0x1 << 3)
+#define CCI_ACD_MODE_2ND_SFT 2
+#define CCI_ACD_MODE_2ND_MASK 0x1
+#define CCI_ACD_MODE_2ND_MASK_SFT (0x1 << 2)
+#define CCI_AFIFO_CLK_PWDB_2ND_SFT 1
+#define CCI_AFIFO_CLK_PWDB_2ND_MASK 0x1
+#define CCI_AFIFO_CLK_PWDB_2ND_MASK_SFT (0x1 << 1)
+#define CCI_ACD_FUNC_RSTB_2ND_SFT 0
+#define CCI_ACD_FUNC_RSTB_2ND_MASK 0x1
+#define CCI_ACD_FUNC_RSTB_2ND_MASK_SFT (0x1 << 0)
+
+/* AFUNC_AUD_CON12 */
+#define SPLITTER2_DITHER_EN_2ND_SFT 9
+#define SPLITTER2_DITHER_EN_2ND_MASK 0x1
+#define SPLITTER2_DITHER_EN_2ND_MASK_SFT (0x1 << 9)
+#define SPLITTER1_DITHER_EN_2ND_SFT 8
+#define SPLITTER1_DITHER_EN_2ND_MASK 0x1
+#define SPLITTER1_DITHER_EN_2ND_MASK_SFT (0x1 << 8)
+#define SPLITTER2_DITHER_GAIN_2ND_SFT 4
+#define SPLITTER2_DITHER_GAIN_2ND_MASK 0xf
+#define SPLITTER2_DITHER_GAIN_2ND_MASK_SFT (0xf << 4)
+#define SPLITTER1_DITHER_GAIN_2ND_SFT 0
+#define SPLITTER1_DITHER_GAIN_2ND_MASK 0xf
+#define SPLITTER1_DITHER_GAIN_2ND_MASK_SFT (0xf << 0)
+
+/* AFUNC_AUD_MON0 */
+#define AUD_SCR_OUT_L_SFT 8
+#define AUD_SCR_OUT_L_MASK 0xff
+#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8)
+#define AUD_SCR_OUT_R_SFT 0
+#define AUD_SCR_OUT_R_MASK 0xff
+#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0)
+
+/* AFUNC_AUD_MON1 */
+#define AUD_SCR_OUT_L_2ND_SFT 8
+#define AUD_SCR_OUT_L_2ND_MASK 0xff
+#define AUD_SCR_OUT_L_2ND_MASK_SFT (0xff << 8)
+#define AUD_SCR_OUT_R_2ND_SFT 0
+#define AUD_SCR_OUT_R_2ND_MASK 0xff
+#define AUD_SCR_OUT_R_2ND_MASK_SFT (0xff << 0)
+
+/* AUDRC_TUNE_MON0 */
+#define ASYNC_TEST_OUT_BCK_SFT 15
+#define ASYNC_TEST_OUT_BCK_MASK 0x1
+#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15)
+#define RGS_AUDRCTUNE1READ_SFT 8
+#define RGS_AUDRCTUNE1READ_MASK 0x1f
+#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8)
+#define RGS_AUDRCTUNE0READ_SFT 0
+#define RGS_AUDRCTUNE0READ_MASK 0x1f
+#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0)
+
+/* AFE_ADDA_MTKAIF_FIFO_CFG0 */
+#define AFE_RESERVED_SFT 1
+#define AFE_RESERVED_MASK 0x7fff
+#define AFE_RESERVED_MASK_SFT (0x7fff << 1)
+#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1
+#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */
+#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1
+#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1)
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1
+#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_MON0 */
+#define MTKAIFTX_V3_SYNC_OUT_SFT 15
+#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1
+#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 15)
+#define MTKAIFTX_V3_SDATA_OUT3_SFT 14
+#define MTKAIFTX_V3_SDATA_OUT3_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT3_MASK_SFT (0x1 << 14)
+#define MTKAIFTX_V3_SDATA_OUT2_SFT 13
+#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13)
+#define MTKAIFTX_V3_SDATA_OUT1_SFT 12
+#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1
+#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_FIFO_STATUS_SFT 0
+#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff
+#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_MTKAIF_MON1 */
+#define MTKAIFRX_V3_SYNC_IN_SFT 15
+#define MTKAIFRX_V3_SYNC_IN_MASK 0x1
+#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 15)
+#define MTKAIFRX_V3_SDATA_IN3_SFT 14
+#define MTKAIFRX_V3_SDATA_IN3_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN3_MASK_SFT (0x1 << 14)
+#define MTKAIFRX_V3_SDATA_IN2_SFT 13
+#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13)
+#define MTKAIFRX_V3_SDATA_IN1_SFT 12
+#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1
+#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12)
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1
+#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11)
+#define MTKAIF_RXIF_INVALID_FLAG_SFT 8
+#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1
+#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8)
+#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff
+#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_MON2 */
+#define MTKAIF_TXIF_IN_CH2_SFT 8
+#define MTKAIF_TXIF_IN_CH2_MASK 0xff
+#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_TXIF_IN_CH1_SFT 0
+#define MTKAIF_TXIF_IN_CH1_MASK 0xff
+#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA6_MTKAIF_MON3 */
+#define ADDA6_MTKAIF_TXIF_IN_CH2_SFT 8
+#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK 0xff
+#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8)
+#define ADDA6_MTKAIF_TXIF_IN_CH1_SFT 0
+#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK 0xff
+#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_MON4 */
+#define MTKAIF_RXIF_OUT_CH2_SFT 8
+#define MTKAIF_RXIF_OUT_CH2_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8)
+#define MTKAIF_RXIF_OUT_CH1_SFT 0
+#define MTKAIF_RXIF_OUT_CH1_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_MON5 */
+#define MTKAIF_RXIF_OUT_CH3_SFT 0
+#define MTKAIF_RXIF_OUT_CH3_MASK 0xff
+#define MTKAIF_RXIF_OUT_CH3_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define RG_MTKAIF_RXIF_CLKINV_SFT 15
+#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1
+#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15)
+#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_SFT 9
+#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 9)
+#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8)
+#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3
+#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6)
+#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1
+#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5)
+#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_SFT 3
+#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1
+#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 3)
+#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1
+#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1)
+#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1
+#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG0 */
+#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7
+#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3)
+#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1
+#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG1 */
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf
+#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define RG_MTKAIF_RXIF_P2_INPUT_SEL_SFT 15
+#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK 0x1
+#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK_SFT (0x1 << 15)
+#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_SFT 14
+#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK 0x1
+#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK_SFT (0x1 << 14)
+#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_SFT 13
+#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK 0x1
+#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK_SFT (0x1 << 13)
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1
+#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12)
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG3 */
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1
+#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7)
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7
+#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG0 */
+#define RG_MTKAIF_RX_SYNC_WORD2_SFT 4
+#define RG_MTKAIF_RX_SYNC_WORD2_MASK 0x7
+#define RG_MTKAIF_RX_SYNC_WORD2_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF_RX_SYNC_WORD1_SFT 0
+#define RG_MTKAIF_RX_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF_RX_SYNC_WORD1_MASK_SFT (0x7 << 0)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG1 */
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_SFT 12
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK 0x7
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 12)
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_SFT 8
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK 0x7
+#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 8)
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_SFT 4
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK 0x7
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 4)
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_SFT 0
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK 0x7
+#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 0)
+
+/* AFE_SGEN_CFG0 */
+#define SGEN_AMP_DIV_CH1_CTL_SFT 12
+#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf
+#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12)
+#define SGEN_DAC_EN_CTL_SFT 7
+#define SGEN_DAC_EN_CTL_MASK 0x1
+#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7)
+#define SGEN_MUTE_SW_CTL_SFT 6
+#define SGEN_MUTE_SW_CTL_MASK 0x1
+#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6)
+#define R_AUD_SDM_MUTE_L_SFT 5
+#define R_AUD_SDM_MUTE_L_MASK 0x1
+#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5)
+#define R_AUD_SDM_MUTE_R_SFT 4
+#define R_AUD_SDM_MUTE_R_MASK 0x1
+#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4)
+#define R_AUD_SDM_MUTE_L_2ND_SFT 3
+#define R_AUD_SDM_MUTE_L_2ND_MASK 0x1
+#define R_AUD_SDM_MUTE_L_2ND_MASK_SFT (0x1 << 3)
+#define R_AUD_SDM_MUTE_R_2ND_SFT 2
+#define R_AUD_SDM_MUTE_R_2ND_MASK 0x1
+#define R_AUD_SDM_MUTE_R_2ND_MASK_SFT (0x1 << 2)
+
+/* AFE_SGEN_CFG1 */
+#define C_SGEN_RCH_INV_5BIT_SFT 15
+#define C_SGEN_RCH_INV_5BIT_MASK 0x1
+#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15)
+#define C_SGEN_RCH_INV_8BIT_SFT 14
+#define C_SGEN_RCH_INV_8BIT_MASK 0x1
+#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14)
+#define SGEN_FREQ_DIV_CH1_CTL_SFT 0
+#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0)
+
+/* AFE_ADC_ASYNC_FIFO_CFG */
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5)
+#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1
+#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1)
+
+/* AFE_ADC_ASYNC_FIFO_CFG1 */
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_SFT 5
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5)
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_SFT 4
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+
+/* AFE_DCCLK_CFG0 */
+#define DCCLK_DIV_SFT 5
+#define DCCLK_DIV_MASK 0x7ff
+#define DCCLK_DIV_MASK_SFT (0x7ff << 5)
+#define DCCLK_INV_SFT 4
+#define DCCLK_INV_MASK 0x1
+#define DCCLK_INV_MASK_SFT (0x1 << 4)
+#define DCCLK_REF_CK_SEL_SFT 2
+#define DCCLK_REF_CK_SEL_MASK 0x3
+#define DCCLK_REF_CK_SEL_MASK_SFT (0x3 << 2)
+#define DCCLK_PDN_SFT 1
+#define DCCLK_PDN_MASK 0x1
+#define DCCLK_PDN_MASK_SFT (0x1 << 1)
+#define DCCLK_GEN_ON_SFT 0
+#define DCCLK_GEN_ON_MASK 0x1
+#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DCCLK_CFG1 */
+#define RESYNC_SRC_SEL_SFT 10
+#define RESYNC_SRC_SEL_MASK 0x3
+#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10)
+#define RESYNC_SRC_CK_INV_SFT 9
+#define RESYNC_SRC_CK_INV_MASK 0x1
+#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9)
+#define DCCLK_RESYNC_BYPASS_SFT 8
+#define DCCLK_RESYNC_BYPASS_MASK 0x1
+#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8)
+#define DCCLK_PHASE_SEL_SFT 4
+#define DCCLK_PHASE_SEL_MASK 0xf
+#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4)
+
+/* AUDIO_DIG_CFG */
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15)
+#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8)
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7)
+#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0)
+
+/* AUDIO_DIG_CFG1 */
+#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT 7
+#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK 0x1
+#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT (0x1 << 7)
+#define RG_AUD_PAD_TOP_PHASE_MODE3_SFT 0
+#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK 0x7f
+#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT (0x7f << 0)
+
+/* AFE_AUD_PAD_TOP */
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7
+#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12)
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1
+#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11)
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1
+#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8)
+
+/* AFE_AUD_PAD_TOP_MON */
+#define ADDA_AUD_PAD_TOP_MON_SFT 0
+#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0)
+
+/* AFE_AUD_PAD_TOP_MON1 */
+#define ADDA_AUD_PAD_TOP_MON1_SFT 0
+#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0)
+
+/* AFE_AUD_PAD_TOP_MON2 */
+#define ADDA_AUD_PAD_TOP_MON2_SFT 0
+#define ADDA_AUD_PAD_TOP_MON2_MASK 0xffff
+#define ADDA_AUD_PAD_TOP_MON2_MASK_SFT (0xffff << 0)
+
+/* AFE_DL_NLE_CFG */
+#define NLE_RCH_HPGAIN_SEL_SFT 10
+#define NLE_RCH_HPGAIN_SEL_MASK 0x1
+#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10)
+#define NLE_RCH_CH_SEL_SFT 9
+#define NLE_RCH_CH_SEL_MASK 0x1
+#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9)
+#define NLE_RCH_ON_SFT 8
+#define NLE_RCH_ON_MASK 0x1
+#define NLE_RCH_ON_MASK_SFT (0x1 << 8)
+#define NLE_LCH_HPGAIN_SEL_SFT 2
+#define NLE_LCH_HPGAIN_SEL_MASK 0x1
+#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2)
+#define NLE_LCH_CH_SEL_SFT 1
+#define NLE_LCH_CH_SEL_MASK 0x1
+#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1)
+#define NLE_LCH_ON_SFT 0
+#define NLE_LCH_ON_MASK 0x1
+#define NLE_LCH_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DL_NLE_MON */
+#define NLE_MONITOR_SFT 0
+#define NLE_MONITOR_MASK 0x3fff
+#define NLE_MONITOR_MASK_SFT (0x3fff << 0)
+
+/* AFE_CG_EN_MON */
+#define CK_CG_EN_MON_SFT 0
+#define CK_CG_EN_MON_MASK 0x3f
+#define CK_CG_EN_MON_MASK_SFT (0x3f << 0)
+
+/* AFE_MIC_ARRAY_CFG */
+#define RG_AMIC_ADC1_SOURCE_SEL_SFT 10
+#define RG_AMIC_ADC1_SOURCE_SEL_MASK 0x3
+#define RG_AMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 10)
+#define RG_AMIC_ADC2_SOURCE_SEL_SFT 8
+#define RG_AMIC_ADC2_SOURCE_SEL_MASK 0x3
+#define RG_AMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 8)
+#define RG_AMIC_ADC3_SOURCE_SEL_SFT 6
+#define RG_AMIC_ADC3_SOURCE_SEL_MASK 0x3
+#define RG_AMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 6)
+#define RG_DMIC_ADC1_SOURCE_SEL_SFT 4
+#define RG_DMIC_ADC1_SOURCE_SEL_MASK 0x3
+#define RG_DMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 4)
+#define RG_DMIC_ADC2_SOURCE_SEL_SFT 2
+#define RG_DMIC_ADC2_SOURCE_SEL_MASK 0x3
+#define RG_DMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 2)
+#define RG_DMIC_ADC3_SOURCE_SEL_SFT 0
+#define RG_DMIC_ADC3_SOURCE_SEL_MASK 0x3
+#define RG_DMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 0)
+
+/* AFE_CHOP_CFG0 */
+#define RG_CHOP_DIV_SEL_SFT 4
+#define RG_CHOP_DIV_SEL_MASK 0x1f
+#define RG_CHOP_DIV_SEL_MASK_SFT (0x1f << 4)
+#define RG_CHOP_DIV_EN_SFT 0
+#define RG_CHOP_DIV_EN_MASK 0x1
+#define RG_CHOP_DIV_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF_MUX_CFG */
+#define RG_ADDA6_EN_SEL_SFT 12
+#define RG_ADDA6_EN_SEL_MASK 0x1
+#define RG_ADDA6_EN_SEL_MASK_SFT (0x1 << 12)
+#define RG_ADDA6_CH2_SEL_SFT 10
+#define RG_ADDA6_CH2_SEL_MASK 0x3
+#define RG_ADDA6_CH2_SEL_MASK_SFT (0x3 << 10)
+#define RG_ADDA6_CH1_SEL_SFT 8
+#define RG_ADDA6_CH1_SEL_MASK 0x3
+#define RG_ADDA6_CH1_SEL_MASK_SFT (0x3 << 8)
+#define RG_ADDA_EN_SEL_SFT 4
+#define RG_ADDA_EN_SEL_MASK 0x1
+#define RG_ADDA_EN_SEL_MASK_SFT (0x1 << 4)
+#define RG_ADDA_CH2_SEL_SFT 2
+#define RG_ADDA_CH2_SEL_MASK 0x3
+#define RG_ADDA_CH2_SEL_MASK_SFT (0x3 << 2)
+#define RG_ADDA_CH1_SEL_SFT 0
+#define RG_ADDA_CH1_SEL_MASK 0x3
+#define RG_ADDA_CH1_SEL_MASK_SFT (0x3 << 0)
+
+/* AFE_PMIC_NEWIF_CFG3 */
+#define RG_UP8X_SYNC_WORD_SFT 0
+#define RG_UP8X_SYNC_WORD_MASK 0xffff
+#define RG_UP8X_SYNC_WORD_MASK_SFT (0xffff << 0)
+
+/* AFE_NCP_CFG0 */
+#define RG_NCP_CK1_VALID_CNT_SFT 9
+#define RG_NCP_CK1_VALID_CNT_MASK 0x7f
+#define RG_NCP_CK1_VALID_CNT_MASK_SFT (0x7f << 9)
+#define RG_NCP_ADITH_SFT 8
+#define RG_NCP_ADITH_MASK 0x1
+#define RG_NCP_ADITH_MASK_SFT (0x1 << 8)
+#define RG_NCP_DITHER_EN_SFT 7
+#define RG_NCP_DITHER_EN_MASK 0x1
+#define RG_NCP_DITHER_EN_MASK_SFT (0x1 << 7)
+#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_SFT 4
+#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK 0x7
+#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK_SFT (0x7 << 4)
+#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_SFT 1
+#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK 0x7
+#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK_SFT (0x7 << 1)
+#define RG_NCP_ON_SFT 0
+#define RG_NCP_ON_MASK 0x1
+#define RG_NCP_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_NCP_CFG1 */
+#define RG_XY_VAL_CFG_EN_SFT 15
+#define RG_XY_VAL_CFG_EN_MASK 0x1
+#define RG_XY_VAL_CFG_EN_MASK_SFT (0x1 << 15)
+#define RG_X_VAL_CFG_SFT 8
+#define RG_X_VAL_CFG_MASK 0x7f
+#define RG_X_VAL_CFG_MASK_SFT (0x7f << 8)
+#define RG_Y_VAL_CFG_SFT 0
+#define RG_Y_VAL_CFG_MASK 0x7f
+#define RG_Y_VAL_CFG_MASK_SFT (0x7f << 0)
+
+/* AFE_NCP_CFG2 */
+#define RG_NCP_NONCLK_SET_SFT 1
+#define RG_NCP_NONCLK_SET_MASK 0x1
+#define RG_NCP_NONCLK_SET_MASK_SFT (0x1 << 1)
+#define RG_NCP_PDDIS_EN_SFT 0
+#define RG_NCP_PDDIS_EN_MASK 0x1
+#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0)
+
+/* AUDENC_ANA_CON0 */
+#define RG_AUDPREAMPLON_SFT 0
+#define RG_AUDPREAMPLON_MASK 0x1
+#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPLDCCEN_SFT 1
+#define RG_AUDPREAMPLDCCEN_MASK 0x1
+#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPLPGATEST_SFT 3
+#define RG_AUDPREAMPLPGATEST_MASK 0x1
+#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPLVSCALE_SFT 4
+#define RG_AUDPREAMPLVSCALE_MASK 0x3
+#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPLINPUTSEL_SFT 6
+#define RG_AUDPREAMPLINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPLGAIN_SFT 8
+#define RG_AUDPREAMPLGAIN_MASK 0x7
+#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8)
+#define RG_BULKL_VCM_EN_SFT 11
+#define RG_BULKL_VCM_EN_MASK 0x1
+#define RG_BULKL_VCM_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADCLPWRUP_SFT 12
+#define RG_AUDADCLPWRUP_MASK 0x1
+#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCLINPUTSEL_SFT 13
+#define RG_AUDADCLINPUTSEL_MASK 0x3
+#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13)
+
+/* AUDENC_ANA_CON1 */
+#define RG_AUDPREAMPRON_SFT 0
+#define RG_AUDPREAMPRON_MASK 0x1
+#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMPRDCCEN_SFT 1
+#define RG_AUDPREAMPRDCCEN_MASK 0x1
+#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPRDCPRECHARGE_SFT 2
+#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMPRPGATEST_SFT 3
+#define RG_AUDPREAMPRPGATEST_MASK 0x1
+#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMPRVSCALE_SFT 4
+#define RG_AUDPREAMPRVSCALE_MASK 0x3
+#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMPRINPUTSEL_SFT 6
+#define RG_AUDPREAMPRINPUTSEL_MASK 0x3
+#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMPRGAIN_SFT 8
+#define RG_AUDPREAMPRGAIN_MASK 0x7
+#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8)
+#define RG_BULKR_VCM_EN_SFT 11
+#define RG_BULKR_VCM_EN_MASK 0x1
+#define RG_BULKR_VCM_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADCRPWRUP_SFT 12
+#define RG_AUDADCRPWRUP_MASK 0x1
+#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADCRINPUTSEL_SFT 13
+#define RG_AUDADCRINPUTSEL_MASK 0x3
+#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13)
+
+/* AUDENC_ANA_CON2 */
+#define RG_AUDPREAMP3ON_SFT 0
+#define RG_AUDPREAMP3ON_MASK 0x1
+#define RG_AUDPREAMP3ON_MASK_SFT (0x1 << 0)
+#define RG_AUDPREAMP3DCCEN_SFT 1
+#define RG_AUDPREAMP3DCCEN_MASK 0x1
+#define RG_AUDPREAMP3DCCEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMP3DCPRECHARGE_SFT 2
+#define RG_AUDPREAMP3DCPRECHARGE_MASK 0x1
+#define RG_AUDPREAMP3DCPRECHARGE_MASK_SFT (0x1 << 2)
+#define RG_AUDPREAMP3PGATEST_SFT 3
+#define RG_AUDPREAMP3PGATEST_MASK 0x1
+#define RG_AUDPREAMP3PGATEST_MASK_SFT (0x1 << 3)
+#define RG_AUDPREAMP3VSCALE_SFT 4
+#define RG_AUDPREAMP3VSCALE_MASK 0x3
+#define RG_AUDPREAMP3VSCALE_MASK_SFT (0x3 << 4)
+#define RG_AUDPREAMP3INPUTSEL_SFT 6
+#define RG_AUDPREAMP3INPUTSEL_MASK 0x3
+#define RG_AUDPREAMP3INPUTSEL_MASK_SFT (0x3 << 6)
+#define RG_AUDPREAMP3GAIN_SFT 8
+#define RG_AUDPREAMP3GAIN_MASK 0x7
+#define RG_AUDPREAMP3GAIN_MASK_SFT (0x7 << 8)
+#define RG_BULK3_VCM_EN_SFT 11
+#define RG_BULK3_VCM_EN_MASK 0x1
+#define RG_BULK3_VCM_EN_MASK_SFT (0x1 << 11)
+#define RG_AUDADC3PWRUP_SFT 12
+#define RG_AUDADC3PWRUP_MASK 0x1
+#define RG_AUDADC3PWRUP_MASK_SFT (0x1 << 12)
+#define RG_AUDADC3INPUTSEL_SFT 13
+#define RG_AUDADC3INPUTSEL_MASK 0x3
+#define RG_AUDADC3INPUTSEL_MASK_SFT (0x3 << 13)
+
+/* AUDENC_ANA_CON3 */
+#define RG_AUDULHALFBIAS_SFT 0
+#define RG_AUDULHALFBIAS_MASK 0x1
+#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0)
+#define RG_AUDGLBVOWLPWEN_SFT 1
+#define RG_AUDGLBVOWLPWEN_MASK 0x1
+#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1)
+#define RG_AUDPREAMPLPEN_SFT 2
+#define RG_AUDPREAMPLPEN_MASK 0x1
+#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDADC1STSTAGELPEN_SFT 3
+#define RG_AUDADC1STSTAGELPEN_MASK 0x1
+#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3)
+#define RG_AUDADC2NDSTAGELPEN_SFT 4
+#define RG_AUDADC2NDSTAGELPEN_MASK 0x1
+#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4)
+#define RG_AUDADCFLASHLPEN_SFT 5
+#define RG_AUDADCFLASHLPEN_MASK 0x1
+#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5)
+#define RG_AUDPREAMPIDDTEST_SFT 6
+#define RG_AUDPREAMPIDDTEST_MASK 0x3
+#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6)
+#define RG_AUDADC1STSTAGEIDDTEST_SFT 8
+#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8)
+#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3
+#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10)
+#define RG_AUDADCREFBUFIDDTEST_SFT 12
+#define RG_AUDADCREFBUFIDDTEST_MASK 0x3
+#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12)
+#define RG_AUDADCFLASHIDDTEST_SFT 14
+#define RG_AUDADCFLASHIDDTEST_MASK 0x3
+#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14)
+
+/* AUDENC_ANA_CON4 */
+#define RG_AUDRULHALFBIAS_SFT 0
+#define RG_AUDRULHALFBIAS_MASK 0x1
+#define RG_AUDRULHALFBIAS_MASK_SFT (0x1 << 0)
+#define RG_AUDGLBRVOWLPWEN_SFT 1
+#define RG_AUDGLBRVOWLPWEN_MASK 0x1
+#define RG_AUDGLBRVOWLPWEN_MASK_SFT (0x1 << 1)
+#define RG_AUDRPREAMPLPEN_SFT 2
+#define RG_AUDRPREAMPLPEN_MASK 0x1
+#define RG_AUDRPREAMPLPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDRADC1STSTAGELPEN_SFT 3
+#define RG_AUDRADC1STSTAGELPEN_MASK 0x1
+#define RG_AUDRADC1STSTAGELPEN_MASK_SFT (0x1 << 3)
+#define RG_AUDRADC2NDSTAGELPEN_SFT 4
+#define RG_AUDRADC2NDSTAGELPEN_MASK 0x1
+#define RG_AUDRADC2NDSTAGELPEN_MASK_SFT (0x1 << 4)
+#define RG_AUDRADCFLASHLPEN_SFT 5
+#define RG_AUDRADCFLASHLPEN_MASK 0x1
+#define RG_AUDRADCFLASHLPEN_MASK_SFT (0x1 << 5)
+#define RG_AUDRPREAMPIDDTEST_SFT 6
+#define RG_AUDRPREAMPIDDTEST_MASK 0x3
+#define RG_AUDRPREAMPIDDTEST_MASK_SFT (0x3 << 6)
+#define RG_AUDRADC1STSTAGEIDDTEST_SFT 8
+#define RG_AUDRADC1STSTAGEIDDTEST_MASK 0x3
+#define RG_AUDRADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8)
+#define RG_AUDRADC2NDSTAGEIDDTEST_SFT 10
+#define RG_AUDRADC2NDSTAGEIDDTEST_MASK 0x3
+#define RG_AUDRADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10)
+#define RG_AUDRADCREFBUFIDDTEST_SFT 12
+#define RG_AUDRADCREFBUFIDDTEST_MASK 0x3
+#define RG_AUDRADCREFBUFIDDTEST_MASK_SFT (0x3 << 12)
+#define RG_AUDRADCFLASHIDDTEST_SFT 14
+#define RG_AUDRADCFLASHIDDTEST_MASK 0x3
+#define RG_AUDRADCFLASHIDDTEST_MASK_SFT (0x3 << 14)
+
+/* AUDENC_ANA_CON5 */
+#define RG_AUDADCCLKRSTB_SFT 0
+#define RG_AUDADCCLKRSTB_MASK 0x1
+#define RG_AUDADCCLKRSTB_MASK_SFT (0x1 << 0)
+#define RG_AUDADCCLKSEL_SFT 1
+#define RG_AUDADCCLKSEL_MASK 0x3
+#define RG_AUDADCCLKSEL_MASK_SFT (0x3 << 1)
+#define RG_AUDADCCLKSOURCE_SFT 3
+#define RG_AUDADCCLKSOURCE_MASK 0x3
+#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 3)
+#define RG_AUDADCCLKGENMODE_SFT 5
+#define RG_AUDADCCLKGENMODE_MASK 0x3
+#define RG_AUDADCCLKGENMODE_MASK_SFT (0x3 << 5)
+#define RG_AUDPREAMP_ACCFS_SFT 7
+#define RG_AUDPREAMP_ACCFS_MASK 0x1
+#define RG_AUDPREAMP_ACCFS_MASK_SFT (0x1 << 7)
+#define RG_AUDPREAMPAAFEN_SFT 8
+#define RG_AUDPREAMPAAFEN_MASK 0x1
+#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8)
+#define RG_DCCVCMBUFLPMODSEL_SFT 9
+#define RG_DCCVCMBUFLPMODSEL_MASK 0x1
+#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9)
+#define RG_DCCVCMBUFLPSWEN_SFT 10
+#define RG_DCCVCMBUFLPSWEN_MASK 0x1
+#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10)
+#define RG_AUDSPAREPGA_SFT 11
+#define RG_AUDSPAREPGA_MASK 0x1f
+#define RG_AUDSPAREPGA_MASK_SFT (0x1f << 11)
+
+/* AUDENC_ANA_CON6 */
+#define RG_AUDADC1STSTAGESDENB_SFT 0
+#define RG_AUDADC1STSTAGESDENB_MASK 0x1
+#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0)
+#define RG_AUDADC2NDSTAGERESET_SFT 1
+#define RG_AUDADC2NDSTAGERESET_MASK 0x1
+#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1)
+#define RG_AUDADC3RDSTAGERESET_SFT 2
+#define RG_AUDADC3RDSTAGERESET_MASK 0x1
+#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2)
+#define RG_AUDADCFSRESET_SFT 3
+#define RG_AUDADCFSRESET_MASK 0x1
+#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3)
+#define RG_AUDADCWIDECM_SFT 4
+#define RG_AUDADCWIDECM_MASK 0x1
+#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4)
+#define RG_AUDADCNOPATEST_SFT 5
+#define RG_AUDADCNOPATEST_MASK 0x1
+#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5)
+#define RG_AUDADCBYPASS_SFT 6
+#define RG_AUDADCBYPASS_MASK 0x1
+#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6)
+#define RG_AUDADCFFBYPASS_SFT 7
+#define RG_AUDADCFFBYPASS_MASK 0x1
+#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7)
+#define RG_AUDADCDACFBCURRENT_SFT 8
+#define RG_AUDADCDACFBCURRENT_MASK 0x1
+#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8)
+#define RG_AUDADCDACIDDTEST_SFT 9
+#define RG_AUDADCDACIDDTEST_MASK 0x3
+#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9)
+#define RG_AUDADCDACNRZ_SFT 11
+#define RG_AUDADCDACNRZ_MASK 0x1
+#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11)
+#define RG_AUDADCNODEM_SFT 12
+#define RG_AUDADCNODEM_MASK 0x1
+#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12)
+#define RG_AUDADCDACTEST_SFT 13
+#define RG_AUDADCDACTEST_MASK 0x1
+#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13)
+#define RG_AUDADCDAC0P25FS_SFT 14
+#define RG_AUDADCDAC0P25FS_MASK 0x1
+#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 14)
+#define RG_AUDADCRDAC0P25FS_SFT 15
+#define RG_AUDADCRDAC0P25FS_MASK 0x1
+#define RG_AUDADCRDAC0P25FS_MASK_SFT (0x1 << 15)
+
+/* AUDENC_ANA_CON7 */
+#define RG_AUDADCTESTDATA_SFT 0
+#define RG_AUDADCTESTDATA_MASK 0xffff
+#define RG_AUDADCTESTDATA_MASK_SFT (0xffff << 0)
+
+/* AUDENC_ANA_CON8 */
+#define RG_AUDRCTUNEL_SFT 0
+#define RG_AUDRCTUNEL_MASK 0x1f
+#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0)
+#define RG_AUDRCTUNELSEL_SFT 5
+#define RG_AUDRCTUNELSEL_MASK 0x1
+#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5)
+#define RG_AUDRCTUNER_SFT 8
+#define RG_AUDRCTUNER_MASK 0x1f
+#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8)
+#define RG_AUDRCTUNERSEL_SFT 13
+#define RG_AUDRCTUNERSEL_MASK 0x1
+#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13)
+
+/* AUDENC_ANA_CON9 */
+#define RG_AUD3CTUNEL_SFT 0
+#define RG_AUD3CTUNEL_MASK 0x1f
+#define RG_AUD3CTUNEL_MASK_SFT (0x1f << 0)
+#define RG_AUD3CTUNELSEL_SFT 5
+#define RG_AUD3CTUNELSEL_MASK 0x1
+#define RG_AUD3CTUNELSEL_MASK_SFT (0x1 << 5)
+#define RGS_AUDRCTUNE3READ_SFT 6
+#define RGS_AUDRCTUNE3READ_MASK 0x1f
+#define RGS_AUDRCTUNE3READ_MASK_SFT (0x1f << 6)
+#define RG_AUD3SPARE_SFT 11
+#define RG_AUD3SPARE_MASK 0x1f
+#define RG_AUD3SPARE_MASK_SFT (0x1f << 11)
+
+/* AUDENC_ANA_CON10 */
+#define RGS_AUDRCTUNELREAD_SFT 0
+#define RGS_AUDRCTUNELREAD_MASK 0x1f
+#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0)
+#define RGS_AUDRCTUNERREAD_SFT 8
+#define RGS_AUDRCTUNERREAD_MASK 0x1f
+#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8)
+
+/* AUDENC_ANA_CON11 */
+#define RG_AUDSPAREVA30_SFT 0
+#define RG_AUDSPAREVA30_MASK 0xff
+#define RG_AUDSPAREVA30_MASK_SFT (0xff << 0)
+#define RG_AUDSPAREVA18_SFT 8
+#define RG_AUDSPAREVA18_MASK 0xff
+#define RG_AUDSPAREVA18_MASK_SFT (0xff << 8)
+
+/* AUDENC_ANA_CON12 */
+#define RG_AUDPGA_DECAP_SFT 0
+#define RG_AUDPGA_DECAP_MASK 0x1
+#define RG_AUDPGA_DECAP_MASK_SFT (0x1 << 0)
+#define RG_AUDPGA_CAPRA_SFT 1
+#define RG_AUDPGA_CAPRA_MASK 0x1
+#define RG_AUDPGA_CAPRA_MASK_SFT (0x1 << 1)
+#define RG_AUDPGA_ACCCMP_SFT 2
+#define RG_AUDPGA_ACCCMP_MASK 0x1
+#define RG_AUDPGA_ACCCMP_MASK_SFT (0x1 << 2)
+#define RG_AUDENC_SPARE2_SFT 3
+#define RG_AUDENC_SPARE2_MASK 0x1fff
+#define RG_AUDENC_SPARE2_MASK_SFT (0x1fff << 3)
+
+/* AUDENC_ANA_CON13 */
+#define RG_AUDDIGMICEN_SFT 0
+#define RG_AUDDIGMICEN_MASK 0x1
+#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0)
+#define RG_AUDDIGMICBIAS_SFT 1
+#define RG_AUDDIGMICBIAS_MASK 0x3
+#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1)
+#define RG_DMICHPCLKEN_SFT 3
+#define RG_DMICHPCLKEN_MASK 0x1
+#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3)
+#define RG_AUDDIGMICPDUTY_SFT 4
+#define RG_AUDDIGMICPDUTY_MASK 0x3
+#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4)
+#define RG_AUDDIGMICNDUTY_SFT 6
+#define RG_AUDDIGMICNDUTY_MASK 0x3
+#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6)
+#define RG_DMICMONEN_SFT 8
+#define RG_DMICMONEN_MASK 0x1
+#define RG_DMICMONEN_MASK_SFT (0x1 << 8)
+#define RG_DMICMONSEL_SFT 9
+#define RG_DMICMONSEL_MASK 0x7
+#define RG_DMICMONSEL_MASK_SFT (0x7 << 9)
+
+/* AUDENC_ANA_CON14 */
+#define RG_AUDDIGMIC1EN_SFT 0
+#define RG_AUDDIGMIC1EN_MASK 0x1
+#define RG_AUDDIGMIC1EN_MASK_SFT (0x1 << 0)
+#define RG_AUDDIGMICBIAS1_SFT 1
+#define RG_AUDDIGMICBIAS1_MASK 0x3
+#define RG_AUDDIGMICBIAS1_MASK_SFT (0x3 << 1)
+#define RG_DMIC1HPCLKEN_SFT 3
+#define RG_DMIC1HPCLKEN_MASK 0x1
+#define RG_DMIC1HPCLKEN_MASK_SFT (0x1 << 3)
+#define RG_AUDDIGMIC1PDUTY_SFT 4
+#define RG_AUDDIGMIC1PDUTY_MASK 0x3
+#define RG_AUDDIGMIC1PDUTY_MASK_SFT (0x3 << 4)
+#define RG_AUDDIGMIC1NDUTY_SFT 6
+#define RG_AUDDIGMIC1NDUTY_MASK 0x3
+#define RG_AUDDIGMIC1NDUTY_MASK_SFT (0x3 << 6)
+#define RG_DMIC1MONEN_SFT 8
+#define RG_DMIC1MONEN_MASK 0x1
+#define RG_DMIC1MONEN_MASK_SFT (0x1 << 8)
+#define RG_DMIC1MONSEL_SFT 9
+#define RG_DMIC1MONSEL_MASK 0x7
+#define RG_DMIC1MONSEL_MASK_SFT (0x7 << 9)
+#define RG_AUDSPAREVMIC_SFT 12
+#define RG_AUDSPAREVMIC_MASK 0xf
+#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12)
+
+/* AUDENC_ANA_CON15 */
+#define RG_AUDPWDBMICBIAS0_SFT 0
+#define RG_AUDPWDBMICBIAS0_MASK 0x1
+#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS0BYPASSEN_SFT 1
+#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS0LOWPEN_SFT 2
+#define RG_AUDMICBIAS0LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDPWDBMICBIAS3_SFT 3
+#define RG_AUDPWDBMICBIAS3_MASK 0x1
+#define RG_AUDPWDBMICBIAS3_MASK_SFT (0x1 << 3)
+#define RG_AUDMICBIAS0VREF_SFT 4
+#define RG_AUDMICBIAS0VREF_MASK 0x7
+#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9)
+#define RG_AUDMICBIAS0DCSW0NEN_SFT 10
+#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13)
+#define RG_AUDMICBIAS0DCSW2NEN_SFT 14
+#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1
+#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14)
+
+/* AUDENC_ANA_CON16 */
+#define RG_AUDPWDBMICBIAS1_SFT 0
+#define RG_AUDPWDBMICBIAS1_MASK 0x1
+#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS1BYPASSEN_SFT 1
+#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS1LOWPEN_SFT 2
+#define RG_AUDMICBIAS1LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS1VREF_SFT 4
+#define RG_AUDMICBIAS1VREF_MASK 0x7
+#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS1DCSW1PEN_SFT 8
+#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS1DCSW1NEN_SFT 9
+#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1
+#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9)
+#define RG_BANDGAPGEN_SFT 10
+#define RG_BANDGAPGEN_MASK 0x1
+#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIAS1HVEN_SFT 12
+#define RG_AUDMICBIAS1HVEN_MASK 0x1
+#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12)
+#define RG_AUDMICBIAS1HVVREF_SFT 13
+#define RG_AUDMICBIAS1HVVREF_MASK 0x1
+#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13)
+
+/* AUDENC_ANA_CON17 */
+#define RG_AUDPWDBMICBIAS2_SFT 0
+#define RG_AUDPWDBMICBIAS2_MASK 0x1
+#define RG_AUDPWDBMICBIAS2_MASK_SFT (0x1 << 0)
+#define RG_AUDMICBIAS2BYPASSEN_SFT 1
+#define RG_AUDMICBIAS2BYPASSEN_MASK 0x1
+#define RG_AUDMICBIAS2BYPASSEN_MASK_SFT (0x1 << 1)
+#define RG_AUDMICBIAS2LOWPEN_SFT 2
+#define RG_AUDMICBIAS2LOWPEN_MASK 0x1
+#define RG_AUDMICBIAS2LOWPEN_MASK_SFT (0x1 << 2)
+#define RG_AUDMICBIAS2VREF_SFT 4
+#define RG_AUDMICBIAS2VREF_MASK 0x7
+#define RG_AUDMICBIAS2VREF_MASK_SFT (0x7 << 4)
+#define RG_AUDMICBIAS2DCSW3P1EN_SFT 8
+#define RG_AUDMICBIAS2DCSW3P1EN_MASK 0x1
+#define RG_AUDMICBIAS2DCSW3P1EN_MASK_SFT (0x1 << 8)
+#define RG_AUDMICBIAS2DCSW3P2EN_SFT 9
+#define RG_AUDMICBIAS2DCSW3P2EN_MASK 0x1
+#define RG_AUDMICBIAS2DCSW3P2EN_MASK_SFT (0x1 << 9)
+#define RG_AUDMICBIAS2DCSW3NEN_SFT 10
+#define RG_AUDMICBIAS2DCSW3NEN_MASK 0x1
+#define RG_AUDMICBIAS2DCSW3NEN_MASK_SFT (0x1 << 10)
+#define RG_AUDMICBIASSPARE_SFT 12
+#define RG_AUDMICBIASSPARE_MASK 0xf
+#define RG_AUDMICBIASSPARE_MASK_SFT (0xf << 12)
+
+/* AUDENC_ANA_CON18 */
+#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0)
+#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1)
+#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2)
+#define RG_AUDACCDETVIN1PULLLOW_SFT 3
+#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1
+#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3)
+#define RG_AUDACCDETVTHACAL_SFT 4
+#define RG_AUDACCDETVTHACAL_MASK 0x1
+#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4)
+#define RG_AUDACCDETVTHBCAL_SFT 5
+#define RG_AUDACCDETVTHBCAL_MASK 0x1
+#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5)
+#define RG_AUDACCDETTVDET_SFT 6
+#define RG_AUDACCDETTVDET_MASK 0x1
+#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6)
+#define RG_ACCDETSEL_SFT 7
+#define RG_ACCDETSEL_MASK 0x1
+#define RG_ACCDETSEL_MASK_SFT (0x1 << 7)
+#define RG_SWBUFMODSEL_SFT 8
+#define RG_SWBUFMODSEL_MASK 0x1
+#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8)
+#define RG_SWBUFSWEN_SFT 9
+#define RG_SWBUFSWEN_MASK 0x1
+#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9)
+#define RG_EINT0NOHYS_SFT 10
+#define RG_EINT0NOHYS_MASK 0x1
+#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10)
+#define RG_EINT0CONFIGACCDET_SFT 11
+#define RG_EINT0CONFIGACCDET_MASK 0x1
+#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11)
+#define RG_EINT0HIRENB_SFT 12
+#define RG_EINT0HIRENB_MASK 0x1
+#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12)
+#define RG_ACCDET2AUXRESBYPASS_SFT 13
+#define RG_ACCDET2AUXRESBYPASS_MASK 0x1
+#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13)
+#define RG_ACCDET2AUXSWEN_SFT 14
+#define RG_ACCDET2AUXSWEN_MASK 0x1
+#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14)
+#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1
+#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15)
+
+/* AUDENC_ANA_CON19 */
+#define RG_EINT1CONFIGACCDET_SFT 0
+#define RG_EINT1CONFIGACCDET_MASK 0x1
+#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0)
+#define RG_EINT1HIRENB_SFT 1
+#define RG_EINT1HIRENB_MASK 0x1
+#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1)
+#define RG_EINT1NOHYS_SFT 2
+#define RG_EINT1NOHYS_MASK 0x1
+#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2)
+#define RG_EINTCOMPVTH_SFT 4
+#define RG_EINTCOMPVTH_MASK 0xf
+#define RG_EINTCOMPVTH_MASK_SFT (0xf << 4)
+#define RG_MTEST_EN_SFT 8
+#define RG_MTEST_EN_MASK 0x1
+#define RG_MTEST_EN_MASK_SFT (0x1 << 8)
+#define RG_MTEST_SEL_SFT 9
+#define RG_MTEST_SEL_MASK 0x1
+#define RG_MTEST_SEL_MASK_SFT (0x1 << 9)
+#define RG_MTEST_CURRENT_SFT 10
+#define RG_MTEST_CURRENT_MASK 0x1
+#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10)
+#define RG_ANALOGFDEN_SFT 12
+#define RG_ANALOGFDEN_MASK 0x1
+#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12)
+#define RG_FDVIN1PPULLLOW_SFT 13
+#define RG_FDVIN1PPULLLOW_MASK 0x1
+#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13)
+#define RG_FDEINT0TYPE_SFT 14
+#define RG_FDEINT0TYPE_MASK 0x1
+#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14)
+#define RG_FDEINT1TYPE_SFT 15
+#define RG_FDEINT1TYPE_MASK 0x1
+#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15)
+
+/* AUDENC_ANA_CON20 */
+#define RG_EINT0CMPEN_SFT 0
+#define RG_EINT0CMPEN_MASK 0x1
+#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0)
+#define RG_EINT0CMPMEN_SFT 1
+#define RG_EINT0CMPMEN_MASK 0x1
+#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1)
+#define RG_EINT0EN_SFT 2
+#define RG_EINT0EN_MASK 0x1
+#define RG_EINT0EN_MASK_SFT (0x1 << 2)
+#define RG_EINT0CEN_SFT 3
+#define RG_EINT0CEN_MASK 0x1
+#define RG_EINT0CEN_MASK_SFT (0x1 << 3)
+#define RG_EINT0INVEN_SFT 4
+#define RG_EINT0INVEN_MASK 0x1
+#define RG_EINT0INVEN_MASK_SFT (0x1 << 4)
+#define RG_EINT0CTURBO_SFT 5
+#define RG_EINT0CTURBO_MASK 0x7
+#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5)
+#define RG_EINT1CMPEN_SFT 8
+#define RG_EINT1CMPEN_MASK 0x1
+#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8)
+#define RG_EINT1CMPMEN_SFT 9
+#define RG_EINT1CMPMEN_MASK 0x1
+#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9)
+#define RG_EINT1EN_SFT 10
+#define RG_EINT1EN_MASK 0x1
+#define RG_EINT1EN_MASK_SFT (0x1 << 10)
+#define RG_EINT1CEN_SFT 11
+#define RG_EINT1CEN_MASK 0x1
+#define RG_EINT1CEN_MASK_SFT (0x1 << 11)
+#define RG_EINT1INVEN_SFT 12
+#define RG_EINT1INVEN_MASK 0x1
+#define RG_EINT1INVEN_MASK_SFT (0x1 << 12)
+#define RG_EINT1CTURBO_SFT 13
+#define RG_EINT1CTURBO_MASK 0x7
+#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13)
+
+/* AUDENC_ANA_CON21 */
+#define RG_ACCDETSPARE_SFT 0
+#define RG_ACCDETSPARE_MASK 0xffff
+#define RG_ACCDETSPARE_MASK_SFT (0xffff << 0)
+
+/* AUDENC_ANA_CON22 */
+#define RG_AUDENCSPAREVA30_SFT 0
+#define RG_AUDENCSPAREVA30_MASK 0xff
+#define RG_AUDENCSPAREVA30_MASK_SFT (0xff << 0)
+#define RG_AUDENCSPAREVA18_SFT 8
+#define RG_AUDENCSPAREVA18_MASK 0xff
+#define RG_AUDENCSPAREVA18_MASK_SFT (0xff << 8)
+
+/* AUDENC_ANA_CON23 */
+#define RG_CLKSQ_EN_SFT 0
+#define RG_CLKSQ_EN_MASK 0x1
+#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0)
+#define RG_CLKSQ_IN_SEL_TEST_SFT 1
+#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1
+#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1)
+#define RG_CM_REFGENSEL_SFT 2
+#define RG_CM_REFGENSEL_MASK 0x1
+#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2)
+#define RG_AUDIO_VOW_EN_SFT 3
+#define RG_AUDIO_VOW_EN_MASK 0x1
+#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 3)
+#define RG_CLKSQ_EN_VOW_SFT 4
+#define RG_CLKSQ_EN_VOW_MASK 0x1
+#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 4)
+#define RG_CLKAND_EN_VOW_SFT 5
+#define RG_CLKAND_EN_VOW_MASK 0x1
+#define RG_CLKAND_EN_VOW_MASK_SFT (0x1 << 5)
+#define RG_VOWCLK_SEL_EN_VOW_SFT 6
+#define RG_VOWCLK_SEL_EN_VOW_MASK 0x1
+#define RG_VOWCLK_SEL_EN_VOW_MASK_SFT (0x1 << 6)
+#define RG_SPARE_VOW_SFT 7
+#define RG_SPARE_VOW_MASK 0x7
+#define RG_SPARE_VOW_MASK_SFT (0x7 << 7)
+
+/* AUDDEC_ANA_CON0 */
+#define RG_AUDDACLPWRUP_VAUDP32_SFT 0
+#define RG_AUDDACLPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDDACLPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDDACRPWRUP_VAUDP32_SFT 1
+#define RG_AUDDACRPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDDACRPWRUP_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUD_DAC_PWR_UP_VA32_SFT 2
+#define RG_AUD_DAC_PWR_UP_VA32_MASK 0x1
+#define RG_AUD_DAC_PWR_UP_VA32_MASK_SFT (0x1 << 2)
+#define RG_AUD_DAC_PWL_UP_VA32_SFT 3
+#define RG_AUD_DAC_PWL_UP_VA32_MASK 0x1
+#define RG_AUD_DAC_PWL_UP_VA32_MASK_SFT (0x1 << 3)
+#define RG_AUDHPLPWRUP_VAUDP32_SFT 4
+#define RG_AUDHPLPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPLPWRUP_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_AUDHPRPWRUP_VAUDP32_SFT 5
+#define RG_AUDHPRPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPRPWRUP_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_SFT 6
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_SFT 7
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT 8
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 8)
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT 10
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 10)
+#define RG_AUDHPLSCDISABLE_VAUDP32_SFT 12
+#define RG_AUDHPLSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 12)
+#define RG_AUDHPRSCDISABLE_VAUDP32_SFT 13
+#define RG_AUDHPRSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT (0x1 << 13)
+#define RG_AUDHPLBSCCURRENT_VAUDP32_SFT 14
+#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 14)
+#define RG_AUDHPRBSCCURRENT_VAUDP32_SFT 15
+#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 15)
+
+/* AUDDEC_ANA_CON1 */
+#define RG_AUDHPLOUTPWRUP_VAUDP32_SFT 0
+#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDHPROUTPWRUP_VAUDP32_SFT 1
+#define RG_AUDHPROUTPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPROUTPWRUP_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_SFT 2
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 2)
+#define RG_AUDHPROUTAUXPWRUP_VAUDP32_SFT 3
+#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 3)
+#define RG_HPLAUXFBRSW_EN_VAUDP32_SFT 4
+#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK 0x1
+#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_HPRAUXFBRSW_EN_VAUDP32_SFT 5
+#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK 0x1
+#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_SFT 6
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK 0x1
+#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_SFT 7
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK 0x1
+#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_HPLOUTSTGCTRL_VAUDP32_SFT 8
+#define RG_HPLOUTSTGCTRL_VAUDP32_MASK 0x7
+#define RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 8)
+#define RG_HPROUTSTGCTRL_VAUDP32_SFT 12
+#define RG_HPROUTSTGCTRL_VAUDP32_MASK 0x7
+#define RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 12)
+
+/* AUDDEC_ANA_CON2 */
+#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7
+#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4)
+#define RG_AUDHPSTARTUP_VAUDP32_SFT 7
+#define RG_AUDHPSTARTUP_VAUDP32_MASK 0x1
+#define RG_AUDHPSTARTUP_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_AUDREFN_DERES_EN_VAUDP32_SFT 8
+#define RG_AUDREFN_DERES_EN_VAUDP32_MASK 0x1
+#define RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT (0x1 << 8)
+#define RG_HPINPUTSTBENH_VAUDP32_SFT 9
+#define RG_HPINPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_HPINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 9)
+#define RG_HPINPUTRESET0_VAUDP32_SFT 10
+#define RG_HPINPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HPINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10)
+#define RG_HPOUTPUTRESET0_VAUDP32_SFT 11
+#define RG_HPOUTPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HPOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 11)
+#define RG_HPPSHORT2VCM_VAUDP32_SFT 12
+#define RG_HPPSHORT2VCM_VAUDP32_MASK 0x7
+#define RG_HPPSHORT2VCM_VAUDP32_MASK_SFT (0x7 << 12)
+#define RG_AUDHPTRIM_EN_VAUDP32_SFT 15
+#define RG_AUDHPTRIM_EN_VAUDP32_MASK 0x1
+#define RG_AUDHPTRIM_EN_VAUDP32_MASK_SFT (0x1 << 15)
+
+/* AUDDEC_ANA_CON3 */
+#define RG_AUDHPLTRIM_VAUDP32_SFT 0
+#define RG_AUDHPLTRIM_VAUDP32_MASK 0x1f
+#define RG_AUDHPLTRIM_VAUDP32_MASK_SFT (0x1f << 0)
+#define RG_AUDHPLFINETRIM_VAUDP32_SFT 5
+#define RG_AUDHPLFINETRIM_VAUDP32_MASK 0x7
+#define RG_AUDHPLFINETRIM_VAUDP32_MASK_SFT (0x7 << 5)
+#define RG_AUDHPRTRIM_VAUDP32_SFT 8
+#define RG_AUDHPRTRIM_VAUDP32_MASK 0x1f
+#define RG_AUDHPRTRIM_VAUDP32_MASK_SFT (0x1f << 8)
+#define RG_AUDHPRFINETRIM_VAUDP32_SFT 13
+#define RG_AUDHPRFINETRIM_VAUDP32_MASK 0x7
+#define RG_AUDHPRFINETRIM_VAUDP32_MASK_SFT (0x7 << 13)
+
+/* AUDDEC_ANA_CON4 */
+#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_SFT 0
+#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK 0x7
+#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_AUDHPLFCOMPRESSEL_VAUDP32_SFT 4
+#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK 0x7
+#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 4)
+#define RG_AUDHPHFCOMPRESSEL_VAUDP32_SFT 8
+#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK 0x7
+#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 8)
+#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT 12
+#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT (0x3 << 12)
+#define RG_AUDHPCOMP_EN_VAUDP32_SFT 15
+#define RG_AUDHPCOMP_EN_VAUDP32_MASK 0x1
+#define RG_AUDHPCOMP_EN_VAUDP32_MASK_SFT (0x1 << 15)
+
+/* AUDDEC_ANA_CON5 */
+#define RG_AUDHPDECMGAINADJ_VAUDP32_SFT 0
+#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK 0x7
+#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_AUDHPDEDMGAINADJ_VAUDP32_SFT 4
+#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK 0x7
+#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK_SFT (0x7 << 4)
+
+/* AUDDEC_ANA_CON6 */
+#define RG_AUDHSPWRUP_VAUDP32_SFT 0
+#define RG_AUDHSPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDHSPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDHSPWRUP_IBIAS_VAUDP32_SFT 1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT 2
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2)
+#define RG_AUDHSSCDISABLE_VAUDP32_SFT 4
+#define RG_AUDHSSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_AUDHSBSCCURRENT_VAUDP32_SFT 5
+#define RG_AUDHSBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDHSBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDHSSTARTUP_VAUDP32_SFT 6
+#define RG_AUDHSSTARTUP_VAUDP32_MASK 0x1
+#define RG_AUDHSSTARTUP_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_HSOUTPUTSTBENH_VAUDP32_SFT 7
+#define RG_HSOUTPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_HSOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_HSINPUTSTBENH_VAUDP32_SFT 8
+#define RG_HSINPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_HSINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8)
+#define RG_HSINPUTRESET0_VAUDP32_SFT 9
+#define RG_HSINPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HSINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9)
+#define RG_HSOUTPUTRESET0_VAUDP32_SFT 10
+#define RG_HSOUTPUTRESET0_VAUDP32_MASK 0x1
+#define RG_HSOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10)
+#define RG_HSOUT_SHORTVCM_VAUDP32_SFT 11
+#define RG_HSOUT_SHORTVCM_VAUDP32_MASK 0x1
+#define RG_HSOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11)
+
+/* AUDDEC_ANA_CON7 */
+#define RG_AUDLOLPWRUP_VAUDP32_SFT 0
+#define RG_AUDLOLPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDLOLPWRUP_VAUDP32_MASK_SFT (0x1 << 0)
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_SFT 1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK 0x1
+#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1)
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT 2
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK 0x3
+#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2)
+#define RG_AUDLOLSCDISABLE_VAUDP32_SFT 4
+#define RG_AUDLOLSCDISABLE_VAUDP32_MASK 0x1
+#define RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_AUDLOLBSCCURRENT_VAUDP32_SFT 5
+#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK 0x1
+#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDLOSTARTUP_VAUDP32_SFT 6
+#define RG_AUDLOSTARTUP_VAUDP32_MASK 0x1
+#define RG_AUDLOSTARTUP_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_LOINPUTSTBENH_VAUDP32_SFT 7
+#define RG_LOINPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_LOINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7)
+#define RG_LOOUTPUTSTBENH_VAUDP32_SFT 8
+#define RG_LOOUTPUTSTBENH_VAUDP32_MASK 0x1
+#define RG_LOOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8)
+#define RG_LOINPUTRESET0_VAUDP32_SFT 9
+#define RG_LOINPUTRESET0_VAUDP32_MASK 0x1
+#define RG_LOINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9)
+#define RG_LOOUTPUTRESET0_VAUDP32_SFT 10
+#define RG_LOOUTPUTRESET0_VAUDP32_MASK 0x1
+#define RG_LOOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10)
+#define RG_LOOUT_SHORTVCM_VAUDP32_SFT 11
+#define RG_LOOUT_SHORTVCM_VAUDP32_MASK 0x1
+#define RG_LOOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11)
+#define RG_AUDDACTPWRUP_VAUDP32_SFT 12
+#define RG_AUDDACTPWRUP_VAUDP32_MASK 0x1
+#define RG_AUDDACTPWRUP_VAUDP32_MASK_SFT (0x1 << 12)
+#define RG_AUD_DAC_PWT_UP_VA32_SFT 13
+#define RG_AUD_DAC_PWT_UP_VA32_MASK 0x1
+#define RG_AUD_DAC_PWT_UP_VA32_MASK_SFT (0x1 << 13)
+
+/* AUDDEC_ANA_CON8 */
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_SFT 0
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK 0xf
+#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK_SFT (0xf << 0)
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_SFT 4
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK 0x3
+#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK_SFT (0x3 << 4)
+#define RG_AUDTRIMBUF_EN_VAUDP32_SFT 6
+#define RG_AUDTRIMBUF_EN_VAUDP32_MASK 0x1
+#define RG_AUDTRIMBUF_EN_VAUDP32_MASK_SFT (0x1 << 6)
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_SFT 8
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 8)
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_SFT 10
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK 0x3
+#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 10)
+#define RG_AUDHPSPKDET_EN_VAUDP32_SFT 12
+#define RG_AUDHPSPKDET_EN_VAUDP32_MASK 0x1
+#define RG_AUDHPSPKDET_EN_VAUDP32_MASK_SFT (0x1 << 12)
+
+/* AUDDEC_ANA_CON9 */
+#define RG_ABIDEC_RSVD0_VA32_SFT 0
+#define RG_ABIDEC_RSVD0_VA32_MASK 0xff
+#define RG_ABIDEC_RSVD0_VA32_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD0_VAUDP32_SFT 8
+#define RG_ABIDEC_RSVD0_VAUDP32_MASK 0xff
+#define RG_ABIDEC_RSVD0_VAUDP32_MASK_SFT (0xff << 8)
+
+/* AUDDEC_ANA_CON10 */
+#define RG_ABIDEC_RSVD1_VAUDP32_SFT 0
+#define RG_ABIDEC_RSVD1_VAUDP32_MASK 0xff
+#define RG_ABIDEC_RSVD1_VAUDP32_MASK_SFT (0xff << 0)
+#define RG_ABIDEC_RSVD2_VAUDP32_SFT 8
+#define RG_ABIDEC_RSVD2_VAUDP32_MASK 0xff
+#define RG_ABIDEC_RSVD2_VAUDP32_MASK_SFT (0xff << 8)
+
+/* AUDDEC_ANA_CON11 */
+#define RG_AUDZCDMUXSEL_VAUDP32_SFT 0
+#define RG_AUDZCDMUXSEL_VAUDP32_MASK 0x7
+#define RG_AUDZCDMUXSEL_VAUDP32_MASK_SFT (0x7 << 0)
+#define RG_AUDZCDCLKSEL_VAUDP32_SFT 3
+#define RG_AUDZCDCLKSEL_VAUDP32_MASK 0x1
+#define RG_AUDZCDCLKSEL_VAUDP32_MASK_SFT (0x1 << 3)
+#define RG_AUDBIASADJ_0_VAUDP32_SFT 7
+#define RG_AUDBIASADJ_0_VAUDP32_MASK 0x1ff
+#define RG_AUDBIASADJ_0_VAUDP32_MASK_SFT (0x1ff << 7)
+
+/* AUDDEC_ANA_CON12 */
+#define RG_AUDBIASADJ_1_VAUDP32_SFT 0
+#define RG_AUDBIASADJ_1_VAUDP32_MASK 0xff
+#define RG_AUDBIASADJ_1_VAUDP32_MASK_SFT (0xff << 0)
+#define RG_AUDIBIASPWRDN_VAUDP32_SFT 8
+#define RG_AUDIBIASPWRDN_VAUDP32_MASK 0x1
+#define RG_AUDIBIASPWRDN_VAUDP32_MASK_SFT (0x1 << 8)
+
+/* AUDDEC_ANA_CON13 */
+#define RG_RSTB_DECODER_VA32_SFT 0
+#define RG_RSTB_DECODER_VA32_MASK 0x1
+#define RG_RSTB_DECODER_VA32_MASK_SFT (0x1 << 0)
+#define RG_SEL_DECODER_96K_VA32_SFT 1
+#define RG_SEL_DECODER_96K_VA32_MASK 0x1
+#define RG_SEL_DECODER_96K_VA32_MASK_SFT (0x1 << 1)
+#define RG_SEL_DELAY_VCORE_SFT 2
+#define RG_SEL_DELAY_VCORE_MASK 0x1
+#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2)
+#define RG_AUDGLB_PWRDN_VA32_SFT 4
+#define RG_AUDGLB_PWRDN_VA32_MASK 0x1
+#define RG_AUDGLB_PWRDN_VA32_MASK_SFT (0x1 << 4)
+#define RG_AUDGLB_LP_VOW_EN_VA32_SFT 5
+#define RG_AUDGLB_LP_VOW_EN_VA32_MASK 0x1
+#define RG_AUDGLB_LP_VOW_EN_VA32_MASK_SFT (0x1 << 5)
+#define RG_AUDGLB_LP2_VOW_EN_VA32_SFT 6
+#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK 0x1
+#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK_SFT (0x1 << 6)
+
+/* AUDDEC_ANA_CON14 */
+#define RG_LCLDO_DEC_EN_VA32_SFT 0
+#define RG_LCLDO_DEC_EN_VA32_MASK 0x1
+#define RG_LCLDO_DEC_EN_VA32_MASK_SFT (0x1 << 0)
+#define RG_LCLDO_DEC_PDDIS_EN_VA18_SFT 1
+#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK 0x1
+#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK_SFT (0x1 << 1)
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT 2
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK 0x1
+#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2)
+#define RG_NVREG_EN_VAUDP32_SFT 4
+#define RG_NVREG_EN_VAUDP32_MASK 0x1
+#define RG_NVREG_EN_VAUDP32_MASK_SFT (0x1 << 4)
+#define RG_NVREG_PULL0V_VAUDP32_SFT 5
+#define RG_NVREG_PULL0V_VAUDP32_MASK 0x1
+#define RG_NVREG_PULL0V_VAUDP32_MASK_SFT (0x1 << 5)
+#define RG_AUDPMU_RSVD_VA18_SFT 8
+#define RG_AUDPMU_RSVD_VA18_MASK 0xff
+#define RG_AUDPMU_RSVD_VA18_MASK_SFT (0xff << 8)
+
+/* MT6359_ZCD_CON0 */
+#define RG_AUDZCDENABLE_SFT 0
+#define RG_AUDZCDENABLE_MASK 0x1
+#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0)
+#define RG_AUDZCDGAINSTEPTIME_SFT 1
+#define RG_AUDZCDGAINSTEPTIME_MASK 0x7
+#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1)
+#define RG_AUDZCDGAINSTEPSIZE_SFT 4
+#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3
+#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4)
+#define RG_AUDZCDTIMEOUTMODESEL_SFT 6
+#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1
+#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6)
+
+/* MT6359_ZCD_CON1 */
+#define RG_AUDLOLGAIN_SFT 0
+#define RG_AUDLOLGAIN_MASK 0x1f
+#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDLORGAIN_SFT 7
+#define RG_AUDLORGAIN_MASK 0x1f
+#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6359_ZCD_CON2 */
+#define RG_AUDHPLGAIN_SFT 0
+#define RG_AUDHPLGAIN_MASK 0x1f
+#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0)
+#define RG_AUDHPRGAIN_SFT 7
+#define RG_AUDHPRGAIN_MASK 0x1f
+#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7)
+
+/* MT6359_ZCD_CON3 */
+#define RG_AUDHSGAIN_SFT 0
+#define RG_AUDHSGAIN_MASK 0x1f
+#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0)
+
+/* MT6359_ZCD_CON4 */
+#define RG_AUDIVLGAIN_SFT 0
+#define RG_AUDIVLGAIN_MASK 0x7
+#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0)
+#define RG_AUDIVRGAIN_SFT 8
+#define RG_AUDIVRGAIN_MASK 0x7
+#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8)
+
+/* MT6359_ZCD_CON5 */
+#define RG_AUDINTGAIN1_SFT 0
+#define RG_AUDINTGAIN1_MASK 0x3f
+#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0)
+#define RG_AUDINTGAIN2_SFT 8
+#define RG_AUDINTGAIN2_MASK 0x3f
+#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8)
+
+/* audio register */
+#define MT6359_GPIO_DIR0 0x88
+#define MT6359_GPIO_DIR0_SET 0x8a
+#define MT6359_GPIO_DIR0_CLR 0x8c
+#define MT6359_GPIO_DIR1 0x8e
+#define MT6359_GPIO_DIR1_SET 0x90
+#define MT6359_GPIO_DIR1_CLR 0x92
+
+#define MT6359_DCXO_CW11 0x7a6
+#define MT6359_DCXO_CW12 0x7a8
+
+#define MT6359_GPIO_MODE0 0xcc
+#define MT6359_GPIO_MODE0_SET 0xce
+#define MT6359_GPIO_MODE0_CLR 0xd0
+#define MT6359_GPIO_MODE1 0xd2
+#define MT6359_GPIO_MODE1_SET 0xd4
+#define MT6359_GPIO_MODE1_CLR 0xd6
+#define MT6359_GPIO_MODE2 0xd8
+#define MT6359_GPIO_MODE2_SET 0xda
+#define MT6359_GPIO_MODE2_CLR 0xdc
+#define MT6359_GPIO_MODE3 0xde
+#define MT6359_GPIO_MODE3_SET 0xe0
+#define MT6359_GPIO_MODE3_CLR 0xe2
+#define MT6359_GPIO_MODE4 0xe4
+#define MT6359_GPIO_MODE4_SET 0xe6
+#define MT6359_GPIO_MODE4_CLR 0xe8
+
+#define MT6359_AUD_TOP_ID 0x2300
+#define MT6359_AUD_TOP_REV0 0x2302
+#define MT6359_AUD_TOP_DBI 0x2304
+#define MT6359_AUD_TOP_DXI 0x2306
+#define MT6359_AUD_TOP_CKPDN_TPM0 0x2308
+#define MT6359_AUD_TOP_CKPDN_TPM1 0x230a
+#define MT6359_AUD_TOP_CKPDN_CON0 0x230c
+#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e
+#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310
+#define MT6359_AUD_TOP_CKSEL_CON0 0x2312
+#define MT6359_AUD_TOP_CKSEL_CON0_SET 0x2314
+#define MT6359_AUD_TOP_CKSEL_CON0_CLR 0x2316
+#define MT6359_AUD_TOP_CKTST_CON0 0x2318
+#define MT6359_AUD_TOP_CLK_HWEN_CON0 0x231a
+#define MT6359_AUD_TOP_CLK_HWEN_CON0_SET 0x231c
+#define MT6359_AUD_TOP_CLK_HWEN_CON0_CLR 0x231e
+#define MT6359_AUD_TOP_RST_CON0 0x2320
+#define MT6359_AUD_TOP_RST_CON0_SET 0x2322
+#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324
+#define MT6359_AUD_TOP_RST_BANK_CON0 0x2326
+#define MT6359_AUD_TOP_INT_CON0 0x2328
+#define MT6359_AUD_TOP_INT_CON0_SET 0x232a
+#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c
+#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e
+#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330
+#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332
+#define MT6359_AUD_TOP_INT_STATUS0 0x2334
+#define MT6359_AUD_TOP_INT_RAW_STATUS0 0x2336
+#define MT6359_AUD_TOP_INT_MISC_CON0 0x2338
+#define MT6359_AUD_TOP_MON_CON0 0x233a
+#define MT6359_AUDIO_DIG_DSN_ID 0x2380
+#define MT6359_AUDIO_DIG_DSN_REV0 0x2382
+#define MT6359_AUDIO_DIG_DSN_DBI 0x2384
+#define MT6359_AUDIO_DIG_DSN_DXI 0x2386
+#define MT6359_AFE_UL_DL_CON0 0x2388
+#define MT6359_AFE_DL_SRC2_CON0_L 0x238a
+#define MT6359_AFE_UL_SRC_CON0_H 0x238c
+#define MT6359_AFE_UL_SRC_CON0_L 0x238e
+#define MT6359_AFE_ADDA6_L_SRC_CON0_H 0x2390
+#define MT6359_AFE_ADDA6_UL_SRC_CON0_L 0x2392
+#define MT6359_AFE_TOP_CON0 0x2394
+#define MT6359_AUDIO_TOP_CON0 0x2396
+#define MT6359_AFE_MON_DEBUG0 0x2398
+#define MT6359_AFUNC_AUD_CON0 0x239a
+#define MT6359_AFUNC_AUD_CON1 0x239c
+#define MT6359_AFUNC_AUD_CON2 0x239e
+#define MT6359_AFUNC_AUD_CON3 0x23a0
+#define MT6359_AFUNC_AUD_CON4 0x23a2
+#define MT6359_AFUNC_AUD_CON5 0x23a4
+#define MT6359_AFUNC_AUD_CON6 0x23a6
+#define MT6359_AFUNC_AUD_CON7 0x23a8
+#define MT6359_AFUNC_AUD_CON8 0x23aa
+#define MT6359_AFUNC_AUD_CON9 0x23ac
+#define MT6359_AFUNC_AUD_CON10 0x23ae
+#define MT6359_AFUNC_AUD_CON11 0x23b0
+#define MT6359_AFUNC_AUD_CON12 0x23b2
+#define MT6359_AFUNC_AUD_MON0 0x23b4
+#define MT6359_AFUNC_AUD_MON1 0x23b6
+#define MT6359_AUDRC_TUNE_MON0 0x23b8
+#define MT6359_AFE_ADDA_MTKAIF_FIFO_CFG0 0x23ba
+#define MT6359_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x23bc
+#define MT6359_AFE_ADDA_MTKAIF_MON0 0x23be
+#define MT6359_AFE_ADDA_MTKAIF_MON1 0x23c0
+#define MT6359_AFE_ADDA_MTKAIF_MON2 0x23c2
+#define MT6359_AFE_ADDA6_MTKAIF_MON3 0x23c4
+#define MT6359_AFE_ADDA_MTKAIF_MON4 0x23c6
+#define MT6359_AFE_ADDA_MTKAIF_MON5 0x23c8
+#define MT6359_AFE_ADDA_MTKAIF_CFG0 0x23ca
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG0 0x23cc
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG1 0x23ce
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG2 0x23d0
+#define MT6359_AFE_ADDA_MTKAIF_RX_CFG3 0x23d2
+#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG0 0x23d4
+#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG1 0x23d6
+#define MT6359_AFE_SGEN_CFG0 0x23d8
+#define MT6359_AFE_SGEN_CFG1 0x23da
+#define MT6359_AFE_ADC_ASYNC_FIFO_CFG 0x23dc
+#define MT6359_AFE_ADC_ASYNC_FIFO_CFG1 0x23de
+#define MT6359_AFE_DCCLK_CFG0 0x23e0
+#define MT6359_AFE_DCCLK_CFG1 0x23e2
+#define MT6359_AUDIO_DIG_CFG 0x23e4
+#define MT6359_AUDIO_DIG_CFG1 0x23e6
+#define MT6359_AFE_AUD_PAD_TOP 0x23e8
+#define MT6359_AFE_AUD_PAD_TOP_MON 0x23ea
+#define MT6359_AFE_AUD_PAD_TOP_MON1 0x23ec
+#define MT6359_AFE_AUD_PAD_TOP_MON2 0x23ee
+#define MT6359_AFE_DL_NLE_CFG 0x23f0
+#define MT6359_AFE_DL_NLE_MON 0x23f2
+#define MT6359_AFE_CG_EN_MON 0x23f4
+#define MT6359_AFE_MIC_ARRAY_CFG 0x23f6
+#define MT6359_AFE_CHOP_CFG0 0x23f8
+#define MT6359_AFE_MTKAIF_MUX_CFG 0x23fa
+#define MT6359_AUDIO_DIG_2ND_DSN_ID 0x2400
+#define MT6359_AUDIO_DIG_2ND_DSN_REV0 0x2402
+#define MT6359_AUDIO_DIG_2ND_DSN_DBI 0x2404
+#define MT6359_AUDIO_DIG_2ND_DSN_DXI 0x2406
+#define MT6359_AFE_PMIC_NEWIF_CFG3 0x2408
+#define MT6359_AUDIO_DIG_3RD_DSN_ID 0x2480
+#define MT6359_AUDIO_DIG_3RD_DSN_REV0 0x2482
+#define MT6359_AUDIO_DIG_3RD_DSN_DBI 0x2484
+#define MT6359_AUDIO_DIG_3RD_DSN_DXI 0x2486
+#define MT6359_AFE_NCP_CFG0 0x24de
+#define MT6359_AFE_NCP_CFG1 0x24e0
+#define MT6359_AFE_NCP_CFG2 0x24e2
+#define MT6359_AUDENC_DSN_ID 0x2500
+#define MT6359_AUDENC_DSN_REV0 0x2502
+#define MT6359_AUDENC_DSN_DBI 0x2504
+#define MT6359_AUDENC_DSN_FPI 0x2506
+#define MT6359_AUDENC_ANA_CON0 0x2508
+#define MT6359_AUDENC_ANA_CON1 0x250a
+#define MT6359_AUDENC_ANA_CON2 0x250c
+#define MT6359_AUDENC_ANA_CON3 0x250e
+#define MT6359_AUDENC_ANA_CON4 0x2510
+#define MT6359_AUDENC_ANA_CON5 0x2512
+#define MT6359_AUDENC_ANA_CON6 0x2514
+#define MT6359_AUDENC_ANA_CON7 0x2516
+#define MT6359_AUDENC_ANA_CON8 0x2518
+#define MT6359_AUDENC_ANA_CON9 0x251a
+#define MT6359_AUDENC_ANA_CON10 0x251c
+#define MT6359_AUDENC_ANA_CON11 0x251e
+#define MT6359_AUDENC_ANA_CON12 0x2520
+#define MT6359_AUDENC_ANA_CON13 0x2522
+#define MT6359_AUDENC_ANA_CON14 0x2524
+#define MT6359_AUDENC_ANA_CON15 0x2526
+#define MT6359_AUDENC_ANA_CON16 0x2528
+#define MT6359_AUDENC_ANA_CON17 0x252a
+#define MT6359_AUDENC_ANA_CON18 0x252c
+#define MT6359_AUDENC_ANA_CON19 0x252e
+#define MT6359_AUDENC_ANA_CON20 0x2530
+#define MT6359_AUDENC_ANA_CON21 0x2532
+#define MT6359_AUDENC_ANA_CON22 0x2534
+#define MT6359_AUDENC_ANA_CON23 0x2536
+#define MT6359_AUDDEC_DSN_ID 0x2580
+#define MT6359_AUDDEC_DSN_REV0 0x2582
+#define MT6359_AUDDEC_DSN_DBI 0x2584
+#define MT6359_AUDDEC_DSN_FPI 0x2586
+#define MT6359_AUDDEC_ANA_CON0 0x2588
+#define MT6359_AUDDEC_ANA_CON1 0x258a
+#define MT6359_AUDDEC_ANA_CON2 0x258c
+#define MT6359_AUDDEC_ANA_CON3 0x258e
+#define MT6359_AUDDEC_ANA_CON4 0x2590
+#define MT6359_AUDDEC_ANA_CON5 0x2592
+#define MT6359_AUDDEC_ANA_CON6 0x2594
+#define MT6359_AUDDEC_ANA_CON7 0x2596
+#define MT6359_AUDDEC_ANA_CON8 0x2598
+#define MT6359_AUDDEC_ANA_CON9 0x259a
+#define MT6359_AUDDEC_ANA_CON10 0x259c
+#define MT6359_AUDDEC_ANA_CON11 0x259e
+#define MT6359_AUDDEC_ANA_CON12 0x25a0
+#define MT6359_AUDDEC_ANA_CON13 0x25a2
+#define MT6359_AUDDEC_ANA_CON14 0x25a4
+#define MT6359_AUDZCD_DSN_ID 0x2600
+#define MT6359_AUDZCD_DSN_REV0 0x2602
+#define MT6359_AUDZCD_DSN_DBI 0x2604
+#define MT6359_AUDZCD_DSN_FPI 0x2606
+#define MT6359_ZCD_CON0 0x2608
+#define MT6359_ZCD_CON1 0x260a
+#define MT6359_ZCD_CON2 0x260c
+#define MT6359_ZCD_CON3 0x260e
+#define MT6359_ZCD_CON4 0x2610
+#define MT6359_ZCD_CON5 0x2612
+#define MT6359_ACCDET_DSN_DIG_ID 0x2680
+#define MT6359_ACCDET_DSN_DIG_REV0 0x2682
+#define MT6359_ACCDET_DSN_DBI 0x2684
+#define MT6359_ACCDET_DSN_FPI 0x2686
+#define MT6359_ACCDET_CON0 0x2688
+#define MT6359_ACCDET_CON1 0x268a
+#define MT6359_ACCDET_CON2 0x268c
+#define MT6359_ACCDET_CON3 0x268e
+#define MT6359_ACCDET_CON4 0x2690
+#define MT6359_ACCDET_CON5 0x2692
+#define MT6359_ACCDET_CON6 0x2694
+#define MT6359_ACCDET_CON7 0x2696
+#define MT6359_ACCDET_CON8 0x2698
+#define MT6359_ACCDET_CON9 0x269a
+#define MT6359_ACCDET_CON10 0x269c
+#define MT6359_ACCDET_CON11 0x269e
+#define MT6359_ACCDET_CON12 0x26a0
+#define MT6359_ACCDET_CON13 0x26a2
+#define MT6359_ACCDET_CON14 0x26a4
+#define MT6359_ACCDET_CON15 0x26a6
+#define MT6359_ACCDET_CON16 0x26a8
+#define MT6359_ACCDET_CON17 0x26aa
+#define MT6359_ACCDET_CON18 0x26ac
+#define MT6359_ACCDET_CON19 0x26ae
+#define MT6359_ACCDET_CON20 0x26b0
+#define MT6359_ACCDET_CON21 0x26b2
+#define MT6359_ACCDET_CON22 0x26b4
+#define MT6359_ACCDET_CON23 0x26b6
+#define MT6359_ACCDET_CON24 0x26b8
+#define MT6359_ACCDET_CON25 0x26ba
+#define MT6359_ACCDET_CON26 0x26bc
+#define MT6359_ACCDET_CON27 0x26be
+#define MT6359_ACCDET_CON28 0x26c0
+#define MT6359_ACCDET_CON29 0x26c2
+#define MT6359_ACCDET_CON30 0x26c4
+#define MT6359_ACCDET_CON31 0x26c6
+#define MT6359_ACCDET_CON32 0x26c8
+#define MT6359_ACCDET_CON33 0x26ca
+#define MT6359_ACCDET_CON34 0x26cc
+#define MT6359_ACCDET_CON35 0x26ce
+#define MT6359_ACCDET_CON36 0x26d0
+#define MT6359_ACCDET_CON37 0x26d2
+#define MT6359_ACCDET_CON38 0x26d4
+#define MT6359_ACCDET_CON39 0x26d6
+#define MT6359_ACCDET_CON40 0x26d8
+#define MT6359_MAX_REGISTER MT6359_ZCD_CON5
+
+/* dl bias */
+#define DRBIAS_MASK 0x7
+#define DRBIAS_HP_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 0)
+#define DRBIAS_HP_MASK_SFT (DRBIAS_MASK << DRBIAS_HP_SFT)
+#define DRBIAS_HS_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 3)
+#define DRBIAS_HS_MASK_SFT (DRBIAS_MASK << DRBIAS_HS_SFT)
+#define DRBIAS_LO_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 6)
+#define DRBIAS_LO_MASK_SFT (DRBIAS_MASK << DRBIAS_LO_SFT)
+#define IBIAS_MASK 0x3
+#define IBIAS_HP_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 0)
+#define IBIAS_HP_MASK_SFT (IBIAS_MASK << IBIAS_HP_SFT)
+#define IBIAS_HS_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 2)
+#define IBIAS_HS_MASK_SFT (IBIAS_MASK << IBIAS_HS_SFT)
+#define IBIAS_LO_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 4)
+#define IBIAS_LO_MASK_SFT (IBIAS_MASK << IBIAS_LO_SFT)
+#define IBIAS_ZCD_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 6)
+#define IBIAS_ZCD_MASK_SFT (IBIAS_MASK << IBIAS_ZCD_SFT)
+
+/* dl gain */
+#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB)
+#define DL_GAIN_N_22DB_REG (DL_GAIN_N_22DB << 7 | DL_GAIN_N_22DB)
+#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define DL_GAIN_REG_MASK 0x0f9f
+
+/* mic type mux */
+#define MT_SOC_ENUM_EXT_ID(xname, xenum, xhandler_get, xhandler_put, id) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .device = id,\
+ .info = snd_soc_info_enum_double, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(xenum) }
+
+enum {
+ MT6359_MTKAIF_PROTOCOL_1 = 0,
+ MT6359_MTKAIF_PROTOCOL_2,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+enum {
+ MT6359_AIF_1 = 0, /* dl: hp, rcv, hp+lo */
+ MT6359_AIF_2, /* dl: lo only */
+ MT6359_AIF_NUM,
+};
+
+enum {
+ AUDIO_ANALOG_VOLUME_HSOUTL,
+ AUDIO_ANALOG_VOLUME_HSOUTR,
+ AUDIO_ANALOG_VOLUME_HPOUTL,
+ AUDIO_ANALOG_VOLUME_HPOUTR,
+ AUDIO_ANALOG_VOLUME_LINEOUTL,
+ AUDIO_ANALOG_VOLUME_LINEOUTR,
+ AUDIO_ANALOG_VOLUME_MICAMP1,
+ AUDIO_ANALOG_VOLUME_MICAMP2,
+ AUDIO_ANALOG_VOLUME_MICAMP3,
+ AUDIO_ANALOG_VOLUME_TYPE_MAX
+};
+
+enum {
+ MUX_MIC_TYPE_0, /* ain0, micbias 0 */
+ MUX_MIC_TYPE_1, /* ain1, micbias 1 */
+ MUX_MIC_TYPE_2, /* ain2/3, micbias 2 */
+ MUX_PGA_L,
+ MUX_PGA_R,
+ MUX_PGA_3,
+ MUX_HP,
+ MUX_NUM,
+};
+
+enum {
+ DEVICE_HP,
+ DEVICE_LO,
+ DEVICE_RCV,
+ DEVICE_MIC1,
+ DEVICE_MIC2,
+ DEVICE_NUM
+};
+
+enum {
+ HP_GAIN_CTL_ZCD = 0,
+ HP_GAIN_CTL_NLE,
+ HP_GAIN_CTL_NUM,
+};
+
+enum {
+ HP_MUX_OPEN = 0,
+ HP_MUX_HPSPK,
+ HP_MUX_HP,
+ HP_MUX_TEST_MODE,
+ HP_MUX_HP_IMPEDANCE,
+ HP_MUX_MASK = 0x7,
+};
+
+enum {
+ RCV_MUX_OPEN = 0,
+ RCV_MUX_MUTE,
+ RCV_MUX_VOICE_PLAYBACK,
+ RCV_MUX_TEST_MODE,
+ RCV_MUX_MASK = 0x3,
+};
+
+enum {
+ LO_MUX_OPEN = 0,
+ LO_MUX_L_DAC,
+ LO_MUX_3RD_DAC,
+ LO_MUX_TEST_MODE,
+ LO_MUX_MASK = 0x3,
+};
+
+/* Supply widget subseq */
+enum {
+ /* common */
+ SUPPLY_SEQ_CLK_BUF,
+ SUPPLY_SEQ_AUD_GLB,
+ SUPPLY_SEQ_HP_PULL_DOWN,
+ SUPPLY_SEQ_CLKSQ,
+ SUPPLY_SEQ_ADC_CLKGEN,
+ SUPPLY_SEQ_TOP_CK,
+ SUPPLY_SEQ_TOP_CK_LAST,
+ SUPPLY_SEQ_DCC_CLK,
+ SUPPLY_SEQ_MIC_BIAS,
+ SUPPLY_SEQ_DMIC,
+ SUPPLY_SEQ_AUD_TOP,
+ SUPPLY_SEQ_AUD_TOP_LAST,
+ SUPPLY_SEQ_DL_SDM_FIFO_CLK,
+ SUPPLY_SEQ_DL_SDM,
+ SUPPLY_SEQ_DL_NCP,
+ SUPPLY_SEQ_AFE,
+ /* playback */
+ SUPPLY_SEQ_DL_SRC,
+ SUPPLY_SEQ_DL_ESD_RESIST,
+ SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB,
+ SUPPLY_SEQ_HP_MUTE,
+ SUPPLY_SEQ_DL_LDO_REMOTE_SENSE,
+ SUPPLY_SEQ_DL_LDO,
+ SUPPLY_SEQ_DL_NV,
+ SUPPLY_SEQ_HP_ANA_TRIM,
+ SUPPLY_SEQ_DL_IBIST,
+ /* capture */
+ SUPPLY_SEQ_UL_PGA,
+ SUPPLY_SEQ_UL_ADC,
+ SUPPLY_SEQ_UL_MTKAIF,
+ SUPPLY_SEQ_UL_SRC_DMIC,
+ SUPPLY_SEQ_UL_SRC,
+};
+
+enum {
+ CH_L = 0,
+ CH_R,
+ NUM_CH,
+};
+
+enum {
+ DRBIAS_4UA = 0,
+ DRBIAS_5UA,
+ DRBIAS_6UA,
+ DRBIAS_7UA,
+ DRBIAS_8UA,
+ DRBIAS_9UA,
+ DRBIAS_10UA,
+ DRBIAS_11UA,
+};
+
+enum {
+ IBIAS_4UA = 0,
+ IBIAS_5UA,
+ IBIAS_6UA,
+ IBIAS_7UA,
+};
+
+enum {
+ IBIAS_ZCD_3UA = 0,
+ IBIAS_ZCD_4UA,
+ IBIAS_ZCD_5UA,
+ IBIAS_ZCD_6UA,
+};
+
+enum {
+ MIC_BIAS_1P7 = 0,
+ MIC_BIAS_1P8,
+ MIC_BIAS_1P9,
+ MIC_BIAS_2P0,
+ MIC_BIAS_2P1,
+ MIC_BIAS_2P5,
+ MIC_BIAS_2P6,
+ MIC_BIAS_2P7,
+};
+
+/* dl pga gain */
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_22DB = 30,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+/* Mic Type MUX */
+enum {
+ MIC_TYPE_MUX_IDLE = 0,
+ MIC_TYPE_MUX_ACC,
+ MIC_TYPE_MUX_DMIC,
+ MIC_TYPE_MUX_DCC,
+ MIC_TYPE_MUX_DCC_ECM_DIFF,
+ MIC_TYPE_MUX_DCC_ECM_SINGLE,
+};
+
+/* UL SRC MUX */
+enum {
+ UL_SRC_MUX_AMIC = 0,
+ UL_SRC_MUX_DMIC,
+};
+
+/* MISO MUX */
+enum {
+ MISO_MUX_UL1_CH1 = 0,
+ MISO_MUX_UL1_CH2,
+ MISO_MUX_UL2_CH1,
+ MISO_MUX_UL2_CH2,
+};
+
+/* DMIC MUX */
+enum {
+ DMIC_MUX_DMIC_DATA0 = 0,
+ DMIC_MUX_DMIC_DATA1_L,
+ DMIC_MUX_DMIC_DATA1_L_1,
+ DMIC_MUX_DMIC_DATA1_R,
+};
+
+/* ADC L MUX */
+enum {
+ ADC_MUX_IDLE = 0,
+ ADC_MUX_AIN0,
+ ADC_MUX_PREAMPLIFIER,
+ ADC_MUX_IDLE1,
+};
+
+/* PGA L MUX */
+enum {
+ PGA_L_MUX_NONE = 0,
+ PGA_L_MUX_AIN0,
+ PGA_L_MUX_AIN1,
+};
+
+/* PGA R MUX */
+enum {
+ PGA_R_MUX_NONE = 0,
+ PGA_R_MUX_AIN2,
+ PGA_R_MUX_AIN3,
+ PGA_R_MUX_AIN0,
+};
+
+/* PGA 3 MUX */
+enum {
+ PGA_3_MUX_NONE = 0,
+ PGA_3_MUX_AIN3,
+ PGA_3_MUX_AIN2,
+};
+
+struct mt6359_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int dl_rate[MT6359_AIF_NUM];
+ unsigned int ul_rate[MT6359_AIF_NUM];
+ int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX];
+ unsigned int mux_select[MUX_NUM];
+ unsigned int dmic_one_wire_mode;
+ int dev_counter[DEVICE_NUM];
+ int hp_gain_ctl;
+ int hp_hifi_mode;
+ int mtkaif_protocol;
+};
+
+#define CODEC_MT6359_NAME "mtk-codec-mt6359"
+#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \
+ (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+
+void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
+ int mtkaif_protocol);
+void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt);
+void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt);
+void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
+ int phase_1, int phase_2, int phase_3);
+
+#endif/* end _MT6359_H_ */
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
new file mode 100644
index 000000000000..ef63fd113cb7
--- /dev/null
+++ b/sound/soc/codecs/mt6660.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (c) 2019 MediaTek Inc.
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "mt6660.h"
+
+struct reg_size_table {
+ u32 addr;
+ u8 size;
+};
+
+static const struct reg_size_table mt6660_reg_size_table[] = {
+ { MT6660_REG_HPF1_COEF, 4 },
+ { MT6660_REG_HPF2_COEF, 4 },
+ { MT6660_REG_TDM_CFG3, 2 },
+ { MT6660_REG_RESV17, 2 },
+ { MT6660_REG_RESV23, 2 },
+ { MT6660_REG_SIGMAX, 2 },
+ { MT6660_REG_DEVID, 2 },
+ { MT6660_REG_HCLIP_CTRL, 2 },
+ { MT6660_REG_DA_GAIN, 2 },
+};
+
+static int mt6660_get_reg_size(uint32_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt6660_reg_size_table); i++) {
+ if (mt6660_reg_size_table[i].addr == addr)
+ return mt6660_reg_size_table[i].size;
+ }
+ return 1;
+}
+
+static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct mt6660_chip *chip = context;
+ int size = mt6660_get_reg_size(reg);
+ u8 reg_data[4];
+ int i;
+
+ for (i = 0; i < size; i++)
+ reg_data[size - i - 1] = (val >> (8 * i)) & 0xff;
+
+ return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data);
+}
+
+static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct mt6660_chip *chip = context;
+ int size = mt6660_get_reg_size(reg);
+ int i, ret;
+ u8 data[4];
+ u32 reg_data = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, size, data);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < size; i++) {
+ reg_data <<= 8;
+ reg_data |= data[i];
+ }
+ *val = reg_data;
+ return 0;
+}
+
+static const struct regmap_config mt6660_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_write = mt6660_reg_write,
+ .reg_read = mt6660_reg_read,
+};
+
+static int mt6660_codec_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event == SND_SOC_DAPM_POST_PMU)
+ usleep_range(1000, 1100);
+ return 0;
+}
+
+static int mt6660_codec_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(component->dev,
+ "%s: before classd turn on\n", __func__);
+ /* config to adaptive mode */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_BST_CTRL, 0x03, 0x03);
+ if (ret < 0) {
+ dev_err(component->dev, "config mode adaptive fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* voltage sensing enable */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV7, 0x04, 0x04);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "enable voltage sensing fail\n");
+ return ret;
+ }
+ dev_dbg(component->dev, "Amp on\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(component->dev, "Amp off\n");
+ /* voltage sensing disable */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV7, 0x04, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "disable voltage sensing fail\n");
+ return ret;
+ }
+ /* pop-noise improvement 1 */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV10, 0x10, 0x10);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "pop-noise improvement 1 fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev,
+ "%s: after classd turn off\n", __func__);
+ /* pop-noise improvement 2 */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV10, 0x10, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "pop-noise improvement 2 fail\n");
+ return ret;
+ }
+ /* config to off mode */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_BST_CTRL, 0x03, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev, "config mode off fail\n");
+ return ret;
+ }
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mt6660_component_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC", NULL, MT6660_REG_PLL_CFG1,
+ 0, 1, mt6660_codec_dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("VI ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("ClassD", MT6660_REG_SYSTEM_CTRL, 2, 0,
+ NULL, 0, mt6660_codec_classd_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUTP"),
+ SND_SOC_DAPM_OUTPUT("OUTN"),
+};
+
+static const struct snd_soc_dapm_route mt6660_component_dapm_routes[] = {
+ { "DAC", NULL, "aif_playback" },
+ { "PGA", NULL, "DAC" },
+ { "ClassD", NULL, "PGA" },
+ { "OUTP", NULL, "ClassD" },
+ { "OUTN", NULL, "ClassD" },
+ { "VI ADC", NULL, "ClassD" },
+ { "aif_capture", NULL, "VI ADC" },
+};
+
+static int mt6660_component_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mt6660_chip *chip = (struct mt6660_chip *)
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = chip->chip_rev & 0x0f;
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(vol_ctl_tlv, -1155, 5, 0);
+
+static const struct snd_kcontrol_new mt6660_component_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", MT6660_REG_VOL_CTRL, 0, 255,
+ 1, vol_ctl_tlv),
+ SOC_SINGLE("Hard Clip Switch", MT6660_REG_HCLIP_CTRL, 8, 1, 0),
+ SOC_SINGLE("Clip Switch", MT6660_REG_SPS_CTRL, 0, 1, 0),
+ SOC_SINGLE("Boost Mode", MT6660_REG_BST_CTRL, 0, 3, 0),
+ SOC_SINGLE("DRE Switch", MT6660_REG_DRE_CTRL, 0, 1, 0),
+ SOC_SINGLE("DC Protect Switch", MT6660_REG_DC_PROTECT_CTRL, 3, 1, 0),
+ SOC_SINGLE("Data Output Left Channel Selection",
+ MT6660_REG_DATAO_SEL, 3, 7, 0),
+ SOC_SINGLE("Data Output Right Channel Selection",
+ MT6660_REG_DATAO_SEL, 0, 7, 0),
+ SOC_SINGLE_EXT("T0 SEL", MT6660_REG_CALI_T0, 0, 7, 0,
+ snd_soc_get_volsw, NULL),
+ SOC_SINGLE_EXT("Chip Rev", MT6660_REG_DEVID, 8, 15, 0,
+ mt6660_component_get_volsw, NULL),
+};
+
+static int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off)
+{
+ return regmap_write_bits(chip->regmap, MT6660_REG_SYSTEM_CTRL,
+ 0x01, on_off ? 0x00 : 0x01);
+}
+
+struct reg_table {
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t val;
+};
+
+static const struct reg_table mt6660_setting_table[] = {
+ { 0x20, 0x80, 0x00 },
+ { 0x30, 0x01, 0x00 },
+ { 0x50, 0x1c, 0x04 },
+ { 0xB1, 0x0c, 0x00 },
+ { 0xD3, 0x03, 0x03 },
+ { 0xE0, 0x01, 0x00 },
+ { 0x98, 0x44, 0x04 },
+ { 0xB9, 0xff, 0x82 },
+ { 0xB7, 0x7777, 0x7273 },
+ { 0xB6, 0x07, 0x03 },
+ { 0x6B, 0xe0, 0x20 },
+ { 0x07, 0xff, 0x70 },
+ { 0xBB, 0xff, 0x20 },
+ { 0x69, 0xff, 0x40 },
+ { 0xBD, 0xffff, 0x17f8 },
+ { 0x70, 0xff, 0x15 },
+ { 0x7C, 0xff, 0x00 },
+ { 0x46, 0xff, 0x1d },
+ { 0x1A, 0xffffffff, 0x7fdb7ffe },
+ { 0x1B, 0xffffffff, 0x7fdb7ffe },
+ { 0x51, 0xff, 0x58 },
+ { 0xA2, 0xff, 0xce },
+ { 0x33, 0xffff, 0x7fff },
+ { 0x4C, 0xffff, 0x0116 },
+ { 0x16, 0x1800, 0x0800 },
+ { 0x68, 0x1f, 0x07 },
+};
+
+static int mt6660_component_setting(struct snd_soc_component *component)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ size_t i = 0;
+
+ ret = _mt6660_chip_power_on(chip, 1);
+ if (ret < 0) {
+ dev_err(component->dev, "%s chip power on failed\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mt6660_setting_table); i++) {
+ ret = snd_soc_component_update_bits(component,
+ mt6660_setting_table[i].addr,
+ mt6660_setting_table[i].mask,
+ mt6660_setting_table[i].val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s update 0x%02x failed\n",
+ __func__, mt6660_setting_table[i].addr);
+ return ret;
+ }
+ }
+
+ ret = _mt6660_chip_power_on(chip, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "%s chip power off failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt6660_component_probe(struct snd_soc_component *component)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ dev_dbg(component->dev, "%s\n", __func__);
+ snd_soc_component_init_regmap(component, chip->regmap);
+
+ ret = mt6660_component_setting(component);
+ if (ret < 0)
+ dev_err(chip->dev, "mt6660 component setting failed\n");
+
+ return ret;
+}
+
+static void mt6660_component_remove(struct snd_soc_component *component)
+{
+ dev_dbg(component->dev, "%s\n", __func__);
+ snd_soc_component_exit_regmap(component);
+}
+
+static const struct snd_soc_component_driver mt6660_component_driver = {
+ .probe = mt6660_component_probe,
+ .remove = mt6660_component_remove,
+
+ .controls = mt6660_component_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6660_component_snd_controls),
+ .dapm_widgets = mt6660_component_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6660_component_dapm_widgets),
+ .dapm_routes = mt6660_component_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes),
+
+ .idle_bias_on = false, /* idle_bias_off = true */
+ .endianness = 1,
+};
+
+static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ int word_len = params_physical_width(hw_params);
+ int aud_bit = params_width(hw_params);
+ u16 reg_data = 0;
+ int ret;
+
+ dev_dbg(dai->dev, "%s: ++\n", __func__);
+ dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params));
+ dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params));
+ dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit);
+ if (word_len > 32 || word_len < 16) {
+ dev_err(dai->dev, "not supported word length\n");
+ return -ENOTSUPP;
+ }
+ switch (aud_bit) {
+ case 16:
+ reg_data = 3;
+ break;
+ case 18:
+ reg_data = 2;
+ break;
+ case 20:
+ reg_data = 1;
+ break;
+ case 24:
+ case 32:
+ reg_data = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ ret = snd_soc_component_update_bits(dai->component,
+ MT6660_REG_SERIAL_CFG1, 0xc0, (reg_data << 6));
+ if (ret < 0) {
+ dev_err(dai->dev, "config aud bit fail\n");
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(dai->component,
+ MT6660_REG_TDM_CFG3, 0x3f0, word_len << 4);
+ if (ret < 0) {
+ dev_err(dai->dev, "config word len fail\n");
+ return ret;
+ }
+ dev_dbg(dai->dev, "%s: --\n", __func__);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6660_component_aif_ops = {
+ .hw_params = mt6660_component_aif_hw_params,
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6660_codec_dai = {
+ .name = "mt6660-aif",
+ .playback = {
+ .stream_name = "aif_playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ .capture = {
+ .stream_name = "aif_capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ /* dai properties */
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ /* dai operations */
+ .ops = &mt6660_component_aif_ops,
+};
+
+static int _mt6660_chip_id_check(struct mt6660_chip *chip)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val);
+ if (ret < 0)
+ return ret;
+ val &= 0x0ff0;
+ if (val != 0x00e0 && val != 0x01e0) {
+ dev_err(chip->dev, "%s id(%x) not match\n", __func__, val);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int _mt6660_chip_sw_reset(struct mt6660_chip *chip)
+{
+ int ret;
+
+ /* turn on main pll first, then trigger reset */
+ ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x00);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x80);
+ if (ret < 0)
+ return ret;
+ msleep(30);
+ return 0;
+}
+
+static int _mt6660_read_chip_revision(struct mt6660_chip *chip)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "get chip revision fail\n");
+ return ret;
+ }
+ chip->chip_rev = val&0xff;
+ dev_info(chip->dev, "%s chip_rev = %x\n", __func__, chip->chip_rev);
+ return 0;
+}
+
+static int mt6660_i2c_probe(struct i2c_client *client)
+{
+ struct mt6660_chip *chip = NULL;
+ int ret;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ chip->i2c = client;
+ chip->dev = &client->dev;
+ mutex_init(&chip->io_lock);
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init(&client->dev,
+ NULL, chip, &mt6660_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(&client->dev, "failed to initialise regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* chip reset first */
+ ret = _mt6660_chip_sw_reset(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip reset fail\n");
+ goto probe_fail;
+ }
+ /* chip power on */
+ ret = _mt6660_chip_power_on(chip, 1);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip power on 2 fail\n");
+ goto probe_fail;
+ }
+ /* chip devid check */
+ ret = _mt6660_chip_id_check(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip id check fail\n");
+ goto probe_fail;
+ }
+ /* chip revision get */
+ ret = _mt6660_read_chip_revision(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "read chip revision fail\n");
+ goto probe_fail;
+ }
+ pm_runtime_set_active(chip->dev);
+ pm_runtime_enable(chip->dev);
+
+ ret = devm_snd_soc_register_component(chip->dev,
+ &mt6660_component_driver,
+ &mt6660_codec_dai, 1);
+ if (ret)
+ pm_runtime_disable(chip->dev);
+
+ return ret;
+
+probe_fail:
+ _mt6660_chip_power_on(chip, 0);
+ mutex_destroy(&chip->io_lock);
+ return ret;
+}
+
+static void mt6660_i2c_remove(struct i2c_client *client)
+{
+ struct mt6660_chip *chip = i2c_get_clientdata(client);
+
+ pm_runtime_disable(chip->dev);
+ pm_runtime_set_suspended(chip->dev);
+ mutex_destroy(&chip->io_lock);
+}
+
+static int mt6660_i2c_runtime_suspend(struct device *dev)
+{
+ struct mt6660_chip *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "enter low power mode\n");
+ return regmap_update_bits(chip->regmap,
+ MT6660_REG_SYSTEM_CTRL, 0x01, 0x01);
+}
+
+static int mt6660_i2c_runtime_resume(struct device *dev)
+{
+ struct mt6660_chip *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "exit low power mode\n");
+ return regmap_update_bits(chip->regmap,
+ MT6660_REG_SYSTEM_CTRL, 0x01, 0x00);
+}
+
+static const struct dev_pm_ops mt6660_dev_pm_ops = {
+ RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend, mt6660_i2c_runtime_resume, NULL)
+};
+
+static const struct of_device_id __maybe_unused mt6660_of_id[] = {
+ { .compatible = "mediatek,mt6660",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6660_of_id);
+
+static const struct i2c_device_id mt6660_i2c_id[] = {
+ {"mt6660" },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id);
+
+static struct i2c_driver mt6660_i2c_driver = {
+ .driver = {
+ .name = "mt6660",
+ .of_match_table = of_match_ptr(mt6660_of_id),
+ .pm = pm_ptr(&mt6660_dev_pm_ops),
+ },
+ .probe = mt6660_i2c_probe,
+ .remove = mt6660_i2c_remove,
+ .id_table = mt6660_i2c_id,
+};
+module_i2c_driver(mt6660_i2c_driver);
+
+MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
+MODULE_DESCRIPTION("MT6660 SPKAMP Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.8_G");
diff --git a/sound/soc/codecs/mt6660.h b/sound/soc/codecs/mt6660.h
new file mode 100644
index 000000000000..054a3c56ec1f
--- /dev/null
+++ b/sound/soc/codecs/mt6660.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#ifndef __SND_SOC_MT6660_H
+#define __SND_SOC_MT6660_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#pragma pack(push, 1)
+struct mt6660_platform_data {
+ u8 init_setting_num;
+ u32 *init_setting_addr;
+ u32 *init_setting_mask;
+ u32 *init_setting_val;
+};
+
+struct mt6660_chip {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct platform_device *param_dev;
+ struct mt6660_platform_data plat_data;
+ struct mutex io_lock;
+ struct regmap *regmap;
+ u16 chip_rev;
+};
+#pragma pack(pop)
+
+#define MT6660_REG_DEVID (0x00)
+#define MT6660_REG_SYSTEM_CTRL (0x03)
+#define MT6660_REG_IRQ_STATUS1 (0x05)
+#define MT6660_REG_ADDA_CLOCK (0x07)
+#define MT6660_REG_SERIAL_CFG1 (0x10)
+#define MT6660_REG_DATAO_SEL (0x12)
+#define MT6660_REG_TDM_CFG3 (0x15)
+#define MT6660_REG_HPF_CTRL (0x18)
+#define MT6660_REG_HPF1_COEF (0x1A)
+#define MT6660_REG_HPF2_COEF (0x1B)
+#define MT6660_REG_PATH_BYPASS (0x1E)
+#define MT6660_REG_WDT_CTRL (0x20)
+#define MT6660_REG_HCLIP_CTRL (0x24)
+#define MT6660_REG_VOL_CTRL (0x29)
+#define MT6660_REG_SPS_CTRL (0x30)
+#define MT6660_REG_SIGMAX (0x33)
+#define MT6660_REG_CALI_T0 (0x3F)
+#define MT6660_REG_BST_CTRL (0x40)
+#define MT6660_REG_PROTECTION_CFG (0x46)
+#define MT6660_REG_DA_GAIN (0x4c)
+#define MT6660_REG_AUDIO_IN2_SEL (0x50)
+#define MT6660_REG_SIG_GAIN (0x51)
+#define MT6660_REG_PLL_CFG1 (0x60)
+#define MT6660_REG_DRE_CTRL (0x68)
+#define MT6660_REG_DRE_THDMODE (0x69)
+#define MT6660_REG_DRE_CORASE (0x6B)
+#define MT6660_REG_PWM_CTRL (0x70)
+#define MT6660_REG_DC_PROTECT_CTRL (0x74)
+#define MT6660_REG_ADC_USB_MODE (0x7c)
+#define MT6660_REG_INTERNAL_CFG (0x88)
+#define MT6660_REG_RESV0 (0x98)
+#define MT6660_REG_RESV1 (0x99)
+#define MT6660_REG_RESV2 (0x9A)
+#define MT6660_REG_RESV3 (0x9B)
+#define MT6660_REG_RESV6 (0xA2)
+#define MT6660_REG_RESV7 (0xA3)
+#define MT6660_REG_RESV10 (0xB0)
+#define MT6660_REG_RESV11 (0xB1)
+#define MT6660_REG_RESV16 (0xB6)
+#define MT6660_REG_RESV17 (0xB7)
+#define MT6660_REG_RESV19 (0xB9)
+#define MT6660_REG_RESV21 (0xBB)
+#define MT6660_REG_RESV23 (0xBD)
+#define MT6660_REG_RESV31 (0xD3)
+#define MT6660_REG_RESV40 (0xE0)
+
+#endif /* __SND_SOC_MT6660_H */
diff --git a/sound/soc/codecs/nau8315.c b/sound/soc/codecs/nau8315.c
new file mode 100644
index 000000000000..125742601f88
--- /dev/null
+++ b/sound/soc/codecs/nau8315.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8315.c -- NAU8315 ALSA SoC Audio Amplifier Driver
+//
+// Copyright 2020 Nuvoton Technology Crop.
+//
+// Author: David Lin <ctlin0@nuvoton.com>
+//
+// Based on MAX98357A.c
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct nau8315_priv {
+ struct gpio_desc *enable;
+ int enpin_switch;
+};
+
+static int nau8315_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8315_priv *nau8315 =
+ snd_soc_component_get_drvdata(component);
+
+ if (!nau8315->enable)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (nau8315->enpin_switch) {
+ gpiod_set_value(nau8315->enable, 1);
+ dev_dbg(component->dev, "set enable to 1");
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ gpiod_set_value(nau8315->enable, 0);
+ dev_dbg(component->dev, "set enable to 0");
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8315_enpin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8315_priv *nau8315 =
+ snd_soc_component_get_drvdata(component);
+
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ nau8315->enpin_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ nau8315->enpin_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8315_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("Speaker"),
+ SND_SOC_DAPM_OUT_DRV_E("EN_Pin", SND_SOC_NOPM, 0, 0, NULL, 0,
+ nau8315_enpin_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8315_dapm_routes[] = {
+ {"EN_Pin", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "EN_Pin"},
+};
+
+static const struct snd_soc_component_driver nau8315_component_driver = {
+ .dapm_widgets = nau8315_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8315_dapm_widgets),
+ .dapm_routes = nau8315_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8315_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops nau8315_dai_ops = {
+ .trigger = nau8315_daiops_trigger,
+};
+
+#define NAU8315_RATES SNDRV_PCM_RATE_8000_96000
+#define NAU8315_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver nau8315_dai_driver = {
+ .name = "nau8315-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = NAU8315_FORMATS,
+ .rates = NAU8315_RATES,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &nau8315_dai_ops,
+};
+
+static int nau8315_platform_probe(struct platform_device *pdev)
+{
+ struct nau8315_priv *nau8315;
+
+ nau8315 = devm_kzalloc(&pdev->dev, sizeof(*nau8315), GFP_KERNEL);
+ if (!nau8315)
+ return -ENOMEM;
+
+ nau8315->enable = devm_gpiod_get_optional(&pdev->dev,
+ "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(nau8315->enable))
+ return PTR_ERR(nau8315->enable);
+
+ dev_set_drvdata(&pdev->dev, nau8315);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &nau8315_component_driver,
+ &nau8315_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8315_device_id[] = {
+ { .compatible = "nuvoton,nau8315" },
+ { .compatible = "nuvoton,nau8318" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8315_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8315_acpi_match[] = {
+ { "NVTN2010", 0 },
+ { "NVTN2012", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8315_acpi_match);
+#endif
+
+static struct platform_driver nau8315_platform_driver = {
+ .driver = {
+ .name = "nau8315",
+ .of_match_table = of_match_ptr(nau8315_device_id),
+ .acpi_match_table = ACPI_PTR(nau8315_acpi_match),
+ },
+ .probe = nau8315_platform_probe,
+};
+module_platform_driver(nau8315_platform_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8315 Mono Class-D Amplifier Driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c
new file mode 100644
index 000000000000..3bfdb448f8bd
--- /dev/null
+++ b/sound/soc/codecs/nau8325.c
@@ -0,0 +1,899 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8325.c -- Nuvoton NAU8325 audio codec driver
+//
+// Copyright 2023 Nuvoton Technology Crop.
+// Author: Seven Lee <WTLI@nuvoton.com>
+// David Lin <CTLIN0@nuvoton.com>
+//
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "nau8325.h"
+
+/* Range of Master Clock MCLK (Hz) */
+#define MASTER_CLK_MAX 49152000
+#define MASTER_CLK_MIN 2048000
+
+/* scaling for MCLK source */
+#define CLK_PROC_BYPASS (-1)
+
+/* the maximum CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* from MCLK input */
+#define MCLK_SRC 4
+
+static const struct nau8325_src_attr mclk_n1_div[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 3, 0x2 },
+};
+
+/* over sampling rate */
+static const struct nau8325_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8325_src_attr mclk_n2_div[] = {
+ { 0, 0x0 },
+ { 1, 0x1 },
+ { 2, 0x2 },
+ { 3, 0x3 },
+ { 4, 0x4 },
+};
+
+static const struct nau8325_src_attr mclk_n3_mult[] = {
+ { 0, 0x1 },
+ { 1, 0x2 },
+ { 2, 0x3 },
+ { 3, 0x4 },
+};
+
+/* Sample Rate and MCLK_SRC selections */
+static const struct nau8325_srate_attr target_srate_table[] = {
+ /* { FS, range, max, { MCLK source }} */
+ { 48000, 2, true, { 12288000, 19200000, 24000000 } },
+ { 16000, 1, false, { 4096000, 6400000, 8000000 } },
+ { 8000, 0, false, { 2048000, 3200000, 4000000 }},
+ { 44100, 2, true, { 11289600, 17640000, 22050000 }},
+ { 64000, 3, false, { 16384000, 25600000, 32000000 } },
+ { 96000, 3, true, { 24576000, 38400000, 48000000 } },
+ { 12000, 0, true, { 3072000, 4800000, 6000000 } },
+ { 24000, 1, true, { 6144000, 9600000, 12000000 } },
+ { 32000, 2, false, { 8192000, 12800000, 16000000 } },
+};
+
+static const struct reg_default nau8325_reg_defaults[] = {
+ { NAU8325_R00_HARDWARE_RST, 0x0000 },
+ { NAU8325_R01_SOFTWARE_RST, 0x0000 },
+ { NAU8325_R03_CLK_CTRL, 0x0000 },
+ { NAU8325_R04_ENA_CTRL, 0x0000 },
+ { NAU8325_R05_INTERRUPT_CTRL, 0x007f },
+ { NAU8325_R09_IRQOUT, 0x0000 },
+ { NAU8325_R0A_IO_CTRL, 0x0000 },
+ { NAU8325_R0B_PDM_CTRL, 0x0000 },
+ { NAU8325_R0C_TDM_CTRL, 0x0000 },
+ { NAU8325_R0D_I2S_PCM_CTRL1, 0x000a },
+ { NAU8325_R0E_I2S_PCM_CTRL2, 0x0000 },
+ { NAU8325_R0F_L_TIME_SLOT, 0x0000 },
+ { NAU8325_R10_R_TIME_SLOT, 0x0000 },
+ { NAU8325_R11_HPF_CTRL, 0x0000 },
+ { NAU8325_R12_MUTE_CTRL, 0x0000 },
+ { NAU8325_R13_DAC_VOLUME, 0xf3f3 },
+ { NAU8325_R29_DAC_CTRL1, 0x0081 },
+ { NAU8325_R2A_DAC_CTRL2, 0x0000 },
+ { NAU8325_R2C_ALC_CTRL1, 0x000e },
+ { NAU8325_R2D_ALC_CTRL2, 0x8400 },
+ { NAU8325_R2E_ALC_CTRL3, 0x0000 },
+ { NAU8325_R2F_ALC_CTRL4, 0x003f },
+ { NAU8325_R40_CLK_DET_CTRL, 0xa801 },
+ { NAU8325_R50_MIXER_CTRL, 0x0000 },
+ { NAU8325_R55_MISC_CTRL, 0x0000 },
+ { NAU8325_R60_BIAS_ADJ, 0x0000 },
+ { NAU8325_R61_ANALOG_CONTROL_1, 0x0000 },
+ { NAU8325_R62_ANALOG_CONTROL_2, 0x0000 },
+ { NAU8325_R63_ANALOG_CONTROL_3, 0x0000 },
+ { NAU8325_R64_ANALOG_CONTROL_4, 0x0000 },
+ { NAU8325_R65_ANALOG_CONTROL_5, 0x0000 },
+ { NAU8325_R66_ANALOG_CONTROL_6, 0x0000 },
+ { NAU8325_R69_CLIP_CTRL, 0x0000 },
+ { NAU8325_R73_RDAC, 0x0008 },
+};
+
+static bool nau8325_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R02_DEVICE_ID ... NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME:
+ case NAU8325_R1D_DEBUG_READ1:
+ case NAU8325_R1F_DEBUG_READ2:
+ case NAU8325_R22_DEBUG_READ3:
+ case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2:
+ case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4:
+ case NAU8325_R40_CLK_DET_CTRL:
+ case NAU8325_R49_TEST_STATUS ... NAU8325_R4A_ANALOG_READ:
+ case NAU8325_R50_MIXER_CTRL:
+ case NAU8325_R55_MISC_CTRL:
+ case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6:
+ case NAU8325_R69_CLIP_CTRL:
+ case NAU8325_R73_RDAC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8325_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R00_HARDWARE_RST:
+ case NAU8325_R03_CLK_CTRL ... NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME:
+ case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2:
+ case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4:
+ case NAU8325_R40_CLK_DET_CTRL:
+ case NAU8325_R50_MIXER_CTRL:
+ case NAU8325_R55_MISC_CTRL:
+ case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6:
+ case NAU8325_R69_CLIP_CTRL:
+ case NAU8325_R73_RDAC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8325_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R00_HARDWARE_RST ... NAU8325_R02_DEVICE_ID:
+ case NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R1D_DEBUG_READ1:
+ case NAU8325_R1F_DEBUG_READ2:
+ case NAU8325_R22_DEBUG_READ3:
+ case NAU8325_R4A_ANALOG_READ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const nau8325_dac_oversampl_texts[] = {
+ "64", "256", "128", "32",
+};
+
+static const unsigned int nau8325_dac_oversampl_values[] = {
+ 0, 1, 2, 4,
+};
+
+static const struct soc_enum nau8325_dac_oversampl_enum =
+ SOC_VALUE_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1,
+ NAU8325_DAC_OVERSAMPLE_SFT, 0x7,
+ ARRAY_SIZE(nau8325_dac_oversampl_texts),
+ nau8325_dac_oversampl_texts,
+ nau8325_dac_oversampl_values);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -8000, 600);
+
+static const struct snd_kcontrol_new nau8325_snd_controls[] = {
+ SOC_ENUM("DAC Oversampling Rate", nau8325_dac_oversampl_enum),
+ SOC_DOUBLE_TLV("Speaker Volume", NAU8325_R13_DAC_VOLUME,
+ NAU8325_DAC_VOLUME_L_SFT, NAU8325_DAC_VOLUME_R_SFT,
+ NAU8325_DAC_VOLUME_R_EN, 0, dac_vol_tlv),
+ SOC_SINGLE("ALC Max Gain", NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MAXGAIN_SFT, NAU8325_ALC_MAXGAIN_MAX, 0),
+ SOC_SINGLE("ALC Min Gain", NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MINGAIN_SFT, NAU8325_ALC_MINGAIN_MAX, 0),
+ SOC_SINGLE("ALC Decay Timer", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_DCY_SFT, NAU8325_ALC_DCY_MAX, 0),
+ SOC_SINGLE("ALC Attack Timer", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_ATK_SFT, NAU8325_ALC_ATK_MAX, 0),
+ SOC_SINGLE("ALC Hold Time", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_HLD_SFT, NAU8325_ALC_HLD_MAX, 0),
+ SOC_SINGLE("ALC Target Level", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_LVL_SFT, NAU8325_ALC_LVL_MAX, 0),
+ SOC_SINGLE("ALC Enable Switch", NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN_SFT, 1, 0),
+};
+
+static int nau8325_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL,
+ NAU8325_SOFT_MUTE, 0);
+ msleep(30);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Soft mute the output to prevent the pop noise. */
+ regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL,
+ NAU8325_SOFT_MUTE, NAU8325_SOFT_MUTE);
+ msleep(30);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8325_powerup_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ if (nau8325->clock_detection)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_PWRUP_DFT, NAU8325_PWRUP_DFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_PWRUP_DFT, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8325_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Power Up", SND_SOC_NOPM, 0, 0,
+ nau8325_powerup_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, NAU8325_R04_ENA_CTRL,
+ NAU8325_DAC_LEFT_CH_EN_SFT, 0, nau8325_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, NAU8325_R04_ENA_CTRL,
+ NAU8325_DAC_RIGHT_CH_EN_SFT, 0, nau8325_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route nau8325_dapm_routes[] = {
+ { "DACL", NULL, "Power Up" },
+ { "DACR", NULL, "Power Up" },
+
+ { "DACL", NULL, "AIFRX" },
+ { "DACR", NULL, "AIFRX" },
+ { "SPKL", NULL, "DACL" },
+ { "SPKR", NULL, "DACR" },
+};
+
+static int nau8325_srate_clk_apply(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr *srate_table,
+ int n1_sel, int mclk_mult_sel, int n2_sel)
+{
+ if (!srate_table || n2_sel < 0 || n2_sel >= ARRAY_SIZE(mclk_n2_div) ||
+ n1_sel < 0 || n1_sel >= ARRAY_SIZE(mclk_n1_div)) {
+ dev_dbg(nau8325->dev, "The CLK isn't supported.");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_REG_SRATE_MASK | NAU8325_REG_DIV_MAX,
+ (srate_table->range << NAU8325_REG_SRATE_SFT) |
+ (srate_table->max ? NAU8325_REG_DIV_MAX : 0));
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SRC_MASK, mclk_n2_div[n2_sel].val);
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_CLK_MUL_SRC_MASK,
+ mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT);
+
+ if (mclk_mult_sel != CLK_PROC_BYPASS) {
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SEL_MASK,
+ mclk_n3_mult[mclk_mult_sel].val <<
+ NAU8325_MCLK_SEL_SFT);
+ } else {
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SEL_MASK, 0);
+ }
+
+ switch (mclk_mult_sel) {
+ case 2:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN, NAU8325_MCLK4XEN_EN);
+ break;
+ case 3:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN);
+ break;
+ default:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8325_clksrc_n2(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr *srate_table,
+ int mclk, int *n2_sel)
+{
+ int i, mclk_src, ratio;
+
+ ratio = NAU8325_MCLK_FS_RATIO_NUM;
+ for (i = 0; i < ARRAY_SIZE(mclk_n2_div); i++) {
+ mclk_src = mclk >> mclk_n2_div[i].param;
+ if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_256;
+ break;
+ } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_400;
+ break;
+ } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_500;
+ break;
+ }
+ }
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM)
+ *n2_sel = i;
+
+ return ratio;
+}
+
+static const struct nau8325_srate_attr *target_srate_attribute(int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(target_srate_table); i++)
+ if (target_srate_table[i].fs == srate)
+ break;
+
+ if (i == ARRAY_SIZE(target_srate_table))
+ goto proc_err;
+
+ return &target_srate_table[i];
+
+proc_err:
+ return NULL;
+}
+
+static int nau8325_clksrc_choose(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr **srate_table,
+ int *n1_sel, int *mult_sel, int *n2_sel)
+{
+ int i, j, mclk, mclk_max, ratio, ratio_sel, n2_max;
+
+ if (!nau8325->mclk || !nau8325->fs)
+ goto proc_err;
+
+ /* select sampling rate and MCLK_SRC */
+ *srate_table = target_srate_attribute(nau8325->fs);
+ if (!*srate_table)
+ goto proc_err;
+
+ /* First check clock from MCLK directly, decide N2 for MCLK_SRC.
+ * If not good, consider 1/N1 and Multiplier.
+ */
+ ratio = nau8325_clksrc_n2(nau8325, *srate_table, nau8325->mclk, n2_sel);
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM) {
+ *n1_sel = 0;
+ *mult_sel = CLK_PROC_BYPASS;
+ *n2_sel = MCLK_SRC;
+ goto proc_done;
+ }
+
+ /* Get MCLK_SRC through 1/N, Multiplier, and then 1/N2. */
+ mclk_max = 0;
+ for (i = 0; i < ARRAY_SIZE(mclk_n1_div); i++) {
+ for (j = 0; j < ARRAY_SIZE(mclk_n3_mult); j++) {
+ mclk = nau8325->mclk << mclk_n3_mult[j].param;
+ mclk = mclk / mclk_n1_div[i].param;
+ ratio = nau8325_clksrc_n2(nau8325,
+ *srate_table, mclk, n2_sel);
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM &&
+ (mclk_max < mclk || i > *n1_sel)) {
+ mclk_max = mclk;
+ n2_max = *n2_sel;
+ *n1_sel = i;
+ *mult_sel = j;
+ ratio_sel = ratio;
+ goto proc_done;
+ }
+ }
+ }
+ if (mclk_max) {
+ *n2_sel = n2_max;
+ ratio = ratio_sel;
+ goto proc_done;
+ }
+
+proc_err:
+ dev_dbg(nau8325->dev, "The MCLK %d is invalid. It can't get MCLK_SRC of 256/400/500 FS (%d)",
+ nau8325->mclk, nau8325->fs);
+ return -EINVAL;
+proc_done:
+ dev_dbg(nau8325->dev, "nau8325->fs=%d,range=0x%x, %s, (n1,mu,n2,dmu):(%d,%d,%d), MCLK_SRC=%uHz (%d)",
+ nau8325->fs, (*srate_table)->range,
+ (*srate_table)->max ? "MAX" : "MIN",
+ *n1_sel == CLK_PROC_BYPASS ?
+ CLK_PROC_BYPASS : mclk_n1_div[*n1_sel].param,
+ *mult_sel == CLK_PROC_BYPASS ?
+ CLK_PROC_BYPASS : 1 << mclk_n3_mult[*mult_sel].param,
+ 1 << mclk_n2_div[*n2_sel].param,
+ (*srate_table)->mclk_src[ratio],
+ (*srate_table)->mclk_src[ratio] / nau8325->fs);
+
+ return 0;
+}
+
+static int nau8325_clock_config(struct nau8325 *nau8325)
+{
+ const struct nau8325_srate_attr *srate_table;
+ int ret, n1_sel, mult_sel, n2_sel;
+
+ ret = nau8325_clksrc_choose(nau8325, &srate_table,
+ &n1_sel, &mult_sel, &n2_sel);
+ if (ret)
+ goto err;
+
+ ret = nau8325_srate_clk_apply(nau8325, srate_table,
+ n1_sel, mult_sel, n2_sel);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct nau8325_osr_attr *nau8325_get_osr(struct nau8325 *nau8325)
+{
+ unsigned int osr;
+
+ regmap_read(nau8325->regmap, NAU8325_R29_DAC_CTRL1, &osr);
+ osr &= NAU8325_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+
+ return &osr_dac_sel[osr];
+}
+
+static int nau8325_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ const struct nau8325_osr_attr *osr;
+
+ osr = nau8325_get_osr(nau8325);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
+static int nau8325_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0;
+ const struct nau8325_osr_attr *osr;
+ int ret;
+
+ nau8325->fs = params_rate(params);
+ osr = nau8325_get_osr(nau8325);
+ if (!osr || !osr->osr || nau8325->fs * osr->osr > CLK_DA_AD_MAX) {
+ ret = -EINVAL;
+ goto err;
+ }
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8325_CLK_DAC_SRC_SFT);
+
+ ret = nau8325_clock_config(nau8325);
+ if (ret)
+ goto err;
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8325_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8325_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8325_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8325_I2S_DL_32;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1,
+ NAU8325_I2S_DL_MASK, val_len);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int nau8325_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8325_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8325_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8325_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8325_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8325_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8325_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8325_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1,
+ NAU8325_I2S_DF_MASK | NAU8325_I2S_BP_MASK |
+ NAU8325_I2S_PCMB_EN, ctrl1_val);
+
+ return 0;
+}
+
+static int nau8325_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) {
+ dev_dbg(nau8325->dev, "MCLK exceeds the range, MCLK:%d", freq);
+ return -EINVAL;
+ }
+
+ nau8325->mclk = freq;
+ dev_dbg(nau8325->dev, "MCLK %dHz", nau8325->mclk);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8325_component_driver = {
+ .set_sysclk = nau8325_set_sysclk,
+ .suspend_bias_off = true,
+ .controls = nau8325_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8325_snd_controls),
+ .dapm_widgets = nau8325_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8325_dapm_widgets),
+ .dapm_routes = nau8325_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8325_dapm_routes),
+};
+
+static const struct snd_soc_dai_ops nau8325_dai_ops = {
+ .startup = nau8325_dai_startup,
+ .hw_params = nau8325_hw_params,
+ .set_fmt = nau8325_set_fmt,
+};
+
+#define NAU8325_RATES SNDRV_PCM_RATE_8000_96000
+#define NAU8325_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver nau8325_dai = {
+ .name = NAU8325_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8325_RATES,
+ .formats = NAU8325_FORMATS,
+ },
+ .ops = &nau8325_dai_ops,
+};
+
+static const struct regmap_config nau8325_regmap_config = {
+ .reg_bits = NAU8325_REG_ADDR_LEN,
+ .val_bits = NAU8325_REG_DATA_LEN,
+
+ .max_register = NAU8325_REG_MAX,
+ .readable_reg = nau8325_readable_reg,
+ .writeable_reg = nau8325_writeable_reg,
+ .volatile_reg = nau8325_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8325_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8325_reg_defaults),
+};
+
+static void nau8325_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0001);
+ regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0000);
+}
+
+static void nau8325_init_regs(struct nau8325 *nau8325)
+{
+ struct regmap *regmap = nau8325->regmap;
+ struct device *dev = nau8325->dev;
+
+ /* set ALC parameters */
+ regmap_update_bits(regmap, NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MAXGAIN_MASK,
+ 0x7 << NAU8325_ALC_MAXGAIN_SFT);
+ regmap_update_bits(regmap, NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_DCY_MASK | NAU8325_ALC_ATK_MASK |
+ NAU8325_ALC_HLD_MASK, (0x5 << NAU8325_ALC_DCY_SFT) |
+ (0x3 << NAU8325_ALC_ATK_SFT) |
+ (0x5 << NAU8325_ALC_HLD_SFT));
+ /* Enable ALC to avoid signal distortion when battery low. */
+ if (nau8325->alc_enable)
+ regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN, NAU8325_ALC_EN);
+ if (nau8325->clock_detection)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS |
+ NAU8325_PWRUP_DFT, 0);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT,
+ NAU8325_CLKPWRUP_DIS);
+ if (nau8325->clock_det_data)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, NAU8325_APWRUP_EN);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, 0);
+
+ /* DAC Reference Voltage Setting */
+ switch (nau8325->dac_vref_microvolt) {
+ case 1800000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 0 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 2700000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 1 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 2880000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 2 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 3060000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 3 << NAU8325_DACVREFSEL_SFT);
+ break;
+ default:
+ dev_dbg(dev, "Invalid dac-vref-microvolt %d", nau8325->dac_vref_microvolt);
+
+ }
+
+ /* DAC Reference Voltage Decoupling Capacitors. */
+ regmap_update_bits(regmap, NAU8325_R63_ANALOG_CONTROL_3,
+ NAU8325_CLASSD_COARSE_GAIN_MASK, 0x4);
+ /* Auto-Att Min Gain 0dB, Class-D N Driver Slew Rate -25%. */
+ regmap_update_bits(regmap, NAU8325_R64_ANALOG_CONTROL_4,
+ NAU8325_CLASSD_SLEWN_MASK, 0x7);
+
+ /* VMID Tieoff (VMID Resistor Selection) */
+ switch (nau8325->vref_impedance_ohms) {
+ case 0:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 0 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 25000:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 1 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 125000:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 2 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 2500:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 3 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ default:
+ dev_dbg(dev, "Invalid vref-impedance-ohms %d", nau8325->vref_impedance_ohms);
+ }
+
+
+ /* enable VMID, BIAS, DAC, DCA CLOCK, Voltage/Current Amps
+ */
+ regmap_update_bits(regmap, NAU8325_R61_ANALOG_CONTROL_1,
+ NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK |
+ NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK |
+ NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK |
+ NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK,
+ (0x1 << NAU8325_DACEN_SFT) |
+ (0x1 << NAU8325_DACCLKEN_SFT) |
+ (0x1 << NAU8325_DACEN_R_SFT) |
+ (0x1 << NAU8325_DACCLKEN_R_SFT) |
+ (0x1 << NAU8325_CLASSDEN_SFT) |
+ (0x1 << NAU8325_VMDFSTENB_SFT) |
+ (0x1 << NAU8325_BIASEN_SFT) | 0x3);
+
+ /* Enable ALC to avoid signal distortion when battery low. */
+ if (nau8325->alc_enable)
+ regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN, NAU8325_ALC_EN);
+ if (nau8325->clock_det_data)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, NAU8325_APWRUP_EN);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, 0);
+ if (nau8325->clock_detection)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS |
+ NAU8325_PWRUP_DFT, 0);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT,
+ NAU8325_CLKPWRUP_DIS);
+ regmap_update_bits(regmap, NAU8325_R29_DAC_CTRL1,
+ NAU8325_DAC_OVERSAMPLE_MASK,
+ NAU8325_DAC_OVERSAMPLE_128);
+}
+
+static void nau8325_print_device_properties(struct nau8325 *nau8325)
+{
+ struct device *dev = nau8325->dev;
+
+ dev_dbg(dev, "vref-impedance-ohms: %d", nau8325->vref_impedance_ohms);
+ dev_dbg(dev, "dac-vref-microvolt: %d", nau8325->dac_vref_microvolt);
+ dev_dbg(dev, "alc-enable: %d", nau8325->alc_enable);
+ dev_dbg(dev, "clock-det-data: %d", nau8325->clock_det_data);
+ dev_dbg(dev, "clock-detection-disable: %d", nau8325->clock_detection);
+}
+
+static int nau8325_read_device_properties(struct device *dev,
+ struct nau8325 *nau8325)
+{
+ int ret;
+
+ nau8325->alc_enable =
+ device_property_read_bool(dev, "nuvoton,alc-enable");
+ nau8325->clock_det_data =
+ device_property_read_bool(dev, "nuvoton,clock-det-data");
+ nau8325->clock_detection =
+ !device_property_read_bool(dev, "nuvoton,clock-detection-disable");
+
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance-ohms",
+ &nau8325->vref_impedance_ohms);
+ if (ret)
+ nau8325->vref_impedance_ohms = 125000;
+ ret = device_property_read_u32(dev, "nuvoton,dac-vref-microvolt",
+ &nau8325->dac_vref_microvolt);
+ if (ret)
+ nau8325->dac_vref_microvolt = 2880000;
+
+ return 0;
+}
+
+static int nau8325_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8325 *nau8325 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8325) {
+ nau8325 = devm_kzalloc(dev, sizeof(*nau8325), GFP_KERNEL);
+ if (!nau8325) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ ret = nau8325_read_device_properties(dev, nau8325);
+ if (ret)
+ goto err;
+ }
+ i2c_set_clientdata(i2c, nau8325);
+
+ nau8325->regmap = devm_regmap_init_i2c(i2c, &nau8325_regmap_config);
+ if (IS_ERR(nau8325->regmap)) {
+ ret = PTR_ERR(nau8325->regmap);
+ goto err;
+ }
+ nau8325->dev = dev;
+ nau8325_print_device_properties(nau8325);
+
+ nau8325_reset_chip(nau8325->regmap);
+ ret = regmap_read(nau8325->regmap, NAU8325_R02_DEVICE_ID, &value);
+ if (ret) {
+ dev_dbg(dev, "Failed to read device id (%d)", ret);
+ goto err;
+ }
+ nau8325_init_regs(nau8325);
+
+ ret = devm_snd_soc_register_component(dev, &nau8325_component_driver,
+ &nau8325_dai, 1);
+err:
+ return ret;
+}
+
+static const struct i2c_device_id nau8325_i2c_ids[] = {
+ { "nau8325" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8325_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8325_of_ids[] = {
+ { .compatible = "nuvoton,nau8325", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8325_of_ids);
+#endif
+
+static struct i2c_driver nau8325_i2c_driver = {
+ .driver = {
+ .name = "nau8325",
+ .of_match_table = of_match_ptr(nau8325_of_ids),
+ },
+ .probe = nau8325_i2c_probe,
+ .id_table = nau8325_i2c_ids,
+};
+module_i2c_driver(nau8325_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8325 driver");
+MODULE_AUTHOR("Seven Lee <WTLI@nuvoton.com>");
+MODULE_AUTHOR("David Lin <CTLIN0@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8325.h b/sound/soc/codecs/nau8325.h
new file mode 100644
index 000000000000..0d173b66a4d4
--- /dev/null
+++ b/sound/soc/codecs/nau8325.h
@@ -0,0 +1,391 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * nau8325.h -- Nuvoton NAU8325 audio codec driver
+ *
+ * Copyright 2023 Nuvoton Technology Crop.
+ * Author: Seven Lee <WTLI@nuvoton.com>
+ * David Lin <CTLIN0@nuvoton.com>
+ */
+
+#ifndef __NAU8325_H__
+#define __NAU8325_H__
+
+#define NAU8325_R00_HARDWARE_RST 0x00
+#define NAU8325_R01_SOFTWARE_RST 0x01
+#define NAU8325_R02_DEVICE_ID 0x02
+#define NAU8325_R03_CLK_CTRL 0x03
+#define NAU8325_R04_ENA_CTRL 0x04
+#define NAU8325_R05_INTERRUPT_CTRL 0x05
+#define NAU8325_R06_INT_CLR_STATUS 0x06
+#define NAU8325_R09_IRQOUT 0x09
+#define NAU8325_R0A_IO_CTRL 0x0a
+#define NAU8325_R0B_PDM_CTRL 0x0b
+#define NAU8325_R0C_TDM_CTRL 0x0c
+#define NAU8325_R0D_I2S_PCM_CTRL1 0x0d
+#define NAU8325_R0E_I2S_PCM_CTRL2 0x0e
+#define NAU8325_R0F_L_TIME_SLOT 0x0f
+#define NAU8325_R10_R_TIME_SLOT 0x10
+#define NAU8325_R11_HPF_CTRL 0x11
+#define NAU8325_R12_MUTE_CTRL 0x12
+#define NAU8325_R13_DAC_VOLUME 0x13
+#define NAU8325_R1D_DEBUG_READ1 0x1d
+#define NAU8325_R1F_DEBUG_READ2 0x1f
+#define NAU8325_R22_DEBUG_READ3 0x22
+#define NAU8325_R29_DAC_CTRL1 0x29
+#define NAU8325_R2A_DAC_CTRL2 0x2a
+#define NAU8325_R2C_ALC_CTRL1 0x2c
+#define NAU8325_R2D_ALC_CTRL2 0x2d
+#define NAU8325_R2E_ALC_CTRL3 0x2e
+#define NAU8325_R2F_ALC_CTRL4 0x2f
+#define NAU8325_R40_CLK_DET_CTRL 0x40
+#define NAU8325_R49_TEST_STATUS 0x49
+#define NAU8325_R4A_ANALOG_READ 0x4a
+#define NAU8325_R50_MIXER_CTRL 0x50
+#define NAU8325_R55_MISC_CTRL 0x55
+#define NAU8325_R60_BIAS_ADJ 0x60
+#define NAU8325_R61_ANALOG_CONTROL_1 0x61
+#define NAU8325_R62_ANALOG_CONTROL_2 0x62
+#define NAU8325_R63_ANALOG_CONTROL_3 0x63
+#define NAU8325_R64_ANALOG_CONTROL_4 0x64
+#define NAU8325_R65_ANALOG_CONTROL_5 0x65
+#define NAU8325_R66_ANALOG_CONTROL_6 0x66
+#define NAU8325_R69_CLIP_CTRL 0x69
+#define NAU8325_R73_RDAC 0x73
+#define NAU8325_REG_MAX NAU8325_R73_RDAC
+
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8325_REG_ADDR_LEN 16
+#define NAU8325_REG_DATA_LEN 16
+
+/* CLK_CTRL (0x03) */
+#define NAU8325_CLK_DAC_SRC_SFT 12
+#define NAU8325_CLK_DAC_SRC_MASK (0x3 << NAU8325_CLK_DAC_SRC_SFT)
+#define NAU8325_CLK_MUL_SRC_SFT 6
+#define NAU8325_CLK_MUL_SRC_MASK (0x3 << NAU8325_CLK_MUL_SRC_SFT)
+#define NAU8325_MCLK_SEL_SFT 3
+#define NAU8325_MCLK_SEL_MASK (0x7 << NAU8325_MCLK_SEL_SFT)
+#define NAU8325_MCLK_SRC_MASK 0x7
+
+/* ENA_CTRL (0x04) */
+#define NAU8325_DAC_LEFT_CH_EN_SFT 3
+#define NAU8325_DAC_LEFT_CH_EN (0x1 << NAU8325_DAC_LEFT_CH_EN_SFT)
+#define NAU8325_DAC_RIGHT_CH_EN_SFT 2
+#define NAU8325_DAC_RIGHT_CH_EN (0x1 << NAU8325_DAC_RIGHT_CH_EN_SFT)
+
+/* INTERRUPT_CTRL (0x05) */
+#define NAU8325_ARP_DWN_INT_SFT 12
+#define NAU8325_ARP_DWN_INT_MASK (0x1 << NAU8325_ARP_DWN_INT_SFT)
+#define NAU8325_CLIP_INT_SFT 11
+#define NAU8325_CLIP_INT_MASK (0x1 << NAU8325_CLIP_INT_SFT)
+#define NAU8325_LVD_INT_SFT 10
+#define NAU8325_LVD_INT_MASK (0x1 << NAU8325_LVD_INT_SFT)
+#define NAU8325_PWR_INT_DIS_SFT 8
+#define NAU8325_PWR_INT_DIS (0x1 << NAU8325_PWR_INT_DIS_SFT)
+#define NAU8325_OCP_OTP_SHTDWN_INT_SFT 4
+#define NAU8325_OCP_OTP_SHTDWN_INT_MASK (0x1 << NAU8325_OCP_OTP_SHTDWN_INT_SFT)
+#define NAU8325_CLIP_INT_DIS_SFT 3
+#define NAU8325_CLIP_INT_DIS (0x1 << NAU8325_CLIP_INT_DIS_SFT)
+#define NAU8325_LVD_INT_DIS_SFT 2
+#define NAU8325_LVD_INT_DIS (0x1 << NAU8325_LVD_INT_DIS_SFT)
+#define NAU8325_PWR_INT_MASK 0x1
+
+/* INT_CLR_STATUS (0x06) */
+#define NAU8325_INT_STATUS_MASK 0x7f
+
+/* IRQOUT (0x9) */
+#define NAU8325_IRQOUT_SEL_SEF 12
+#define NAU8325_IRQOUT_SEL_MASK (0xf << NAU8325_IRQOUT_SEL_SEF)
+#define NAU8325_DEM_DITH_SFT 7
+#define NAU8325_DEM_DITH_EN (0x1 << NAU8325_DEM_DITH_SFT)
+#define NAU8325_GAINZI3_SFT 5
+#define NAU8325_GAINZI3_MASK (0x1 << NAU8325_GAINZI3_SFT)
+#define NAU8325_GAINZI2_MASK 0x1f
+
+/* IO_CTRL (0x0a) */
+#define NAU8325_IRQ_PL_SFT 15
+#define NAU8325_IRQ_PL_ACT_HIGH (0x1 << NAU8325_IRQ_PL_SFT)
+#define NAU8325_IRQ_PS_SFT 14
+#define NAU8325_IRQ_PS_UP (0x1 << NAU8325_IRQ_PS_SFT)
+#define NAU8325_IRQ_PE_SFT 13
+#define NAU8325_IRQ_PE_EN (0x1 << NAU8325_IRQ_PE_SFT)
+#define NAU8325_IRQ_DS_SFT 12
+#define NAU8325_IRQ_DS_HIGH (0x1 << NAU8325_IRQ_DS_SFT)
+#define NAU8325_IRQ_OUTPUT_SFT 11
+#define NAU8325_IRQ_OUTPUT_EN (0x1 << NAU8325_IRQ_OUTPUT_SFT)
+#define NAU8325_IRQ_PIN_DEBUG_SFT 10
+#define NAU8325_IRQ_PIN_DEBUG_EN (0x1 << NAU8325_IRQ_PIN_DEBUG_SFT)
+
+/* PDM_CTRL (0x0b) */
+#define NAU8325_PDM_LCH_EDGE_SFT 1
+#define NAU8325_PDM_LCH_EDGE__MASK (0x1 << NAU8325_PDM_LCH_EDGE_SFT)
+#define NAU8325_PDM_MODE_EN 0x1
+
+/* TDM_CTRL (0x0c) */
+#define NAU8325_TDM_SFT 15
+#define NAU8325_TDM_EN (0x1 << NAU8325_TDM_SFT)
+#define NAU8325_PCM_OFFSET_CTRL_SFT 14
+#define NAU8325_PCM_OFFSET_CTRL_EN (0x1 << NAU8325_PCM_OFFSET_CTRL_SFT)
+#define NAU8325_DAC_LEFT_SFT 6
+#define NAU8325_NAU8325_DAC_LEFT_MASK (0x7 << NAU8325_DAC_LEFT_SFT)
+#define NAU8325_DAC_RIGHT_SFT 3
+#define NAU8325_DAC_RIGHT_MASK (0x7 << NAU8325_DAC_RIGHT_SFT)
+
+/* I2S_PCM_CTRL1 (0x0d) */
+#define NAU8325_DACCM_CTL_SFT 14
+#define NAU8325_DACCM_CTL_MASK (0x3 << NAU8325_DACCM_CTL_SFT)
+#define NAU8325_CMB8_0_SFT 10
+#define NAU8325_CMB8_0_MASK (0x1 << NAU8325_CMB8_0_SFT)
+#define NAU8325_UA_OFFSET_SFT 9
+#define NAU8325_UA_OFFSET_MASK (0x1 << NAU8325_UA_OFFSET_SFT)
+#define NAU8325_I2S_BP_SFT 7
+#define NAU8325_I2S_BP_MASK (0x1 << NAU8325_I2S_BP_SFT)
+#define NAU8325_I2S_BP_INV (0x1 << NAU8325_I2S_BP_SFT)
+#define NAU8325_I2S_PCMB_SFT 6
+#define NAU8325_I2S_PCMB_EN (0x1 << NAU8325_I2S_PCMB_SFT)
+#define NAU8325_I2S_DACPSHS0_SFT 5
+#define NAU8325_I2S_DACPSHS0_MASK (0x1 << NAU8325_I2S_DACPSHS0_SFT)
+#define NAU8325_I2S_DL_SFT 2
+#define NAU8325_I2S_DL_MASK (0x3 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_32 (0x3 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_24 (0x2 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_20 (0x1 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_16 (0x0 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DF_MASK 0x3
+#define NAU8325_I2S_DF_RIGTH 0x0
+#define NAU8325_I2S_DF_LEFT 0x1
+#define NAU8325_I2S_DF_I2S 0x2
+#define NAU8325_I2S_DF_PCM_AB 0x3
+
+/* I2S_PCM_CTRL2 (0x0e) */
+#define NAU8325_PCM_TS_SFT 10
+#define NAU8325_PCM_TS_EN (0x1 << NAU8325_PCM_TS_SFT)
+#define NAU8325_PCM8BIT0_SFT 8
+#define NAU8325_PCM8BIT0_MASK (0x1 << NAU8325_PCM8BIT0_SFT)
+
+/* L_TIME_SLOT (0x0f)*/
+#define NAU8325_SHORT_FS_DET_SFT 13
+#define NAU8325_SHORT_FS_DET_DIS (0x1 << NAU8325_SHORT_FS_DET_SFT)
+#define NAU8325_TSLOT_L0_MASK 0x3ff
+
+/* R_TIME_SLOT (0x10)*/
+#define NAU8325_TSLOT_R0_MASK 0x3ff
+
+/* HPF_CTRL (0x11)*/
+#define NAU8325_DAC_HPF_SFT 15
+#define NAU8325_DAC_HPF_EN (0x1 << NAU8325_DAC_HPF_SFT)
+#define NAU8325_DAC_HPF_APP_SFT 14
+#define NAU8325_DAC_HPF_APP_MASK (0x1 << NAU8325_DAC_HPF_APP_SFT)
+#define NAU8325_DAC_HPF_FCUT_SFT 11
+#define NAU8325_DAC_HPF_FCUT_MASK (0x7 << NAU8325_DAC_HPF_FCUT_SFT)
+
+/* MUTE_CTRL (0x12)*/
+#define NAU8325_SOFT_MUTE_SFT 15
+#define NAU8325_SOFT_MUTE (0x1 << NAU8325_SOFT_MUTE_SFT)
+#define NAU8325_DAC_ZC_SFT 8
+#define NAU8325_DAC_ZC_EN (0x1 << NAU8325_DAC_ZC_SFT)
+#define NAU8325_UNMUTE_CTL_SFT 6
+#define NAU8325_UNMUTE_CTL_MASK (0x3 << NAU8325_UNMUTE_CTL_SFT)
+#define NAU8325_ANA_MUTE_SFT 4
+#define NAU8325_ANA_MUTE_MASK (0x3 << NAU8325_ANA_MUTE_SFT)
+#define NAU8325_AUTO_MUTE_SFT 3
+#define NAU8325_AUTO_MUTE_DIS (0x1 << NAU8325_AUTO_MUTE_SFT)
+
+/* DAC_VOLUME (0x13) */
+#define NAU8325_DAC_VOLUME_L_SFT 8
+#define NAU8325_DAC_VOLUME_L_EN (0xff << NAU8325_DAC_VOLUME_L_SFT)
+#define NAU8325_DAC_VOLUME_R_SFT 0
+#define NAU8325_DAC_VOLUME_R_EN (0xff << NAU8325_DAC_VOLUME_R_SFT)
+#define NAU8325_DAC_VOL_MAX 0xff
+
+/* DEBUG_READ1 (0x1d)*/
+#define NAU8325_OSR100_MASK (0x1 << 6)
+#define NAU8325_MIPS500_MASK (0x1 << 5)
+#define NAU8325_SHUTDWNDRVR_R_MASK (0x1 << 4)
+#define NAU8325_SHUTDWNDRVR_L_MASK (0x1 << 3)
+#define NAU8325_MUTEB_MASK (0x1 << 2)
+#define NAU8325_PDOSCB_MASK (0x1 << 1)
+#define NAU8325_POWERDOWN1B_D_MASK 0x1
+
+/* DEBUG_READ2 (0x1f)*/
+#define NAU8325_R_CHANNEL_Vol_SFT 8
+#define NAU8325_R_CHANNEL_Vol_MASK (0xff << NAU8325_R_CHANNEL_Vol_SFT)
+#define NAU8325_L_CHANNEL_Vol_MASK 0xff
+
+/* DEBUG_READ3(0x22)*/
+#define NAU8325_PGAL_GAIN_MASK (0x3f << 7)
+#define NAU8325_CLIP_MASK (0x1 << 6)
+#define NAU8325_SCAN_MODE_MASK (0x1 << 5)
+#define NAU8325_SDB_MASK (0x1 << 4)
+#define NAU8325_TALARM_MASK (0x1 << 3)
+#define NAU8325_SHORTR_MASK (0x1 << 2)
+#define NAU8325_SHORTL_MASK (0x1 << 1)
+#define NAU8325_TMDET_MASK 0x1
+
+/* DAC_CTRL1 (0x29) */
+#define NAU8325_DAC_OVERSAMPLE_SFT 0
+#define NAU8325_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8325_DAC_OVERSAMPLE_256 1
+#define NAU8325_DAC_OVERSAMPLE_128 2
+#define NAU8325_DAC_OVERSAMPLE_64 0
+#define NAU8325_DAC_OVERSAMPLE_32 4
+
+/* ALC_CTRL1 (0x2c) */
+#define NAU8325_ALC_MAXGAIN_SFT 5
+#define NAU8325_ALC_MAXGAIN_MAX 0x7
+#define NAU8325_ALC_MAXGAIN_MASK (0x7 << NAU8325_ALC_MAXGAIN_SFT)
+#define NAU8325_ALC_MINGAIN_MAX 4
+#define NAU8325_ALC_MINGAIN_SFT 1
+#define NAU8325_ALC_MINGAIN_MASK (0x7 << NAU8325_ALC_MINGAIN_SFT)
+
+/* ALC_CTRL2 (0x2d) */
+#define NAU8325_ALC_DCY_SFT 12
+#define NAU8325_ALC_DCY_MAX 0xb
+#define NAU8325_ALC_DCY_MASK (0xf << NAU8325_ALC_DCY_SFT)
+#define NAU8325_ALC_ATK_SFT 8
+#define NAU8325_ALC_ATK_MAX 0xb
+#define NAU8325_ALC_ATK_MASK (0xf << NAU8325_ALC_ATK_SFT)
+#define NAU8325_ALC_HLD_SFT 4
+#define NAU8325_ALC_HLD_MAX 0xa
+#define NAU8325_ALC_HLD_MASK (0xf << NAU8325_ALC_HLD_SFT)
+#define NAU8325_ALC_LVL_SFT 0
+#define NAU8325_ALC_LVL_MAX 0xf
+#define NAU8325_ALC_LVL_MASK 0xf
+
+/* ALC_CTRL3 (0x2e) */
+#define NAU8325_ALC_EN_SFT 15
+#define NAU8325_ALC_EN (0x1 << NAU8325_ALC_EN_SFT)
+
+/* TEMP_COMP_CTRL (0x30) */
+#define NAU8325_TEMP_COMP_ACT2_MASK 0xff
+
+/* LPF_CTRL (0x33) */
+#define NAU8325_LPF_IN1_EN_SFT 15
+#define NAU8325_LPF_IN1_EN (0x1 << NAU8325_LPF_IN1_EN_SFT)
+#define NAU8325_LPF_IN1_TC_SFT 11
+#define NAU8325_LPF_IN1_TC_MASK (0xf << NAU8325_LPF_IN1_TC_SFT)
+#define NAU8325_LPF_IN2_EN_SFT 10
+#define NAU8325_LPF_IN2_EN (0x1 << NAU8325_LPF_IN2_EN_SFT)
+#define NAU8325_LPF_IN2_TC_SFT 6
+#define NAU8325_LPF_IN2_TC_MASK (0xf << NAU8325_LPF_IN2_TC_SFT)
+
+/* CLK_DET_CTRL (0x40) */
+#define NAU8325_APWRUP_SFT 15
+#define NAU8325_APWRUP_EN (0x1 << NAU8325_APWRUP_SFT)
+#define NAU8325_CLKPWRUP_SFT 14
+#define NAU8325_CLKPWRUP_DIS (0x1 << NAU8325_CLKPWRUP_SFT)
+#define NAU8325_PWRUP_DFT_SFT 13
+#define NAU8325_PWRUP_DFT (0x1 << NAU8325_PWRUP_DFT_SFT)
+#define NAU8325_REG_SRATE_SFT 10
+#define NAU8325_REG_SRATE_MASK (0x7 << NAU8325_REG_SRATE_SFT)
+#define NAU8325_REG_ALT_SRATE_SFT 9
+#define NAU8325_REG_ALT_SRATE_EN (0x1 << NAU8325_REG_ALT_SRATE_SFT)
+#define NAU8325_REG_DIV_MAX 0x1
+
+/* BIAS_ADJ (0x60) */
+#define NAU8325_BIAS_VMID_SEL_SFT 4
+#define NAU8325_BIAS_VMID_SEL_MASK (0x3 << NAU8325_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x61) */
+#define NAU8325_VMDFSTENB_SFT 14
+#define NAU8325_VMDFSTENB_MASK (0x3 << NAU8325_VMDFSTENB_SFT)
+#define NAU8325_CLASSDEN_SFT 12
+#define NAU8325_CLASSDEN_MASK (0x3 << NAU8325_CLASSDEN_SFT)
+#define NAU8325_DACCLKEN_R_SFT 10
+#define NAU8325_DACCLKEN_R_MASK (0x3 << NAU8325_DACCLKEN_R_SFT)
+#define NAU8325_DACEN_R_SFT 8
+#define NAU8325_DACEN_R_MASK (0x3 << NAU8325_DACEN_R_SFT)
+#define NAU8325_DACCLKEN_SFT 6
+#define NAU8325_DACCLKEN_MASK (0x3 << NAU8325_DACCLKEN_SFT)
+#define NAU8325_DACEN_SFT 4
+#define NAU8325_DACEN_MASK (0x3 << NAU8325_DACEN_SFT)
+#define NAU8325_BIASEN_SFT 2
+#define NAU8325_BIASEN_MASK (0x3 << NAU8325_BIASEN_SFT)
+#define NAU8325_VMIDEN_MASK 0x3
+
+/* ANALOG_CONTROL_2 (0x62) */
+#define NAU8325_PWMMOD_SFT 14
+#define NAU8325_PWMMOD_MASK (0x1 << NAU8325_PWMMOD_SFT)
+#define NAU8325_DACTEST_SFT 6
+#define NAU8325_DACTEST_MASK (0x3 << NAU8325_DACTEST_SFT)
+#define NAU8325_DACREFCAP_SFT 4
+#define NAU8325_DACREFCAP_MASK (0x3 << NAU8325_DACREFCAP_SFT)
+
+/* ANALOG_CONTROL_3 (0x63) */
+#define NAU8325_POWER_DOWN_L_SFT 12
+#define NAU8325_POWER_DOWN_L_MASK (0x3 << NAU8325_POWER_DOWN_L_SFT)
+#define NAU8325_POWER_DOWN_R_SFT 11
+#define NAU8325_POWER_DOWN_R_MASK (0x3 << NAU8325_DACREFCAP_SFT)
+#define NAU8325_CLASSD_FINE_SFT 5
+#define NAU8325_CLASSD_FINE_MASK (0x3 << NAU8325_CLASSD_FINE_SFT)
+#define NAU8325_CLASSD_COARSE_GAIN_MASK 0xf
+
+/* ANALOG_CONTROL_4 (0x64) */
+#define NAU8325_CLASSD_OCPN_SFT 12
+#define NAU8325_CLASSD_OCPN_MASK (0xf << NAU8325_CLASSD_OCPN_SFT)
+#define NAU8325_CLASSD_OCPP_SFT 8
+#define NAU8325_CLASSD_OCPP_MASK (0xf << NAU8325_CLASSD_OCPP_SFT)
+#define NAU8325_CLASSD_SLEWN_MASK 0xff
+
+/* ANALOG_CONTROL_5 (0x65) */
+#define NAU8325_MCLK_RANGE_SFT 2
+#define NAU8325_MCLK_RANGE_EN (0x1 << NAU8325_MCLK_RANGE_SFT)
+#define NAU8325_MCLK8XEN_SFT 1
+#define NAU8325_MCLK8XEN_EN (0x1 << NAU8325_MCLK8XEN_SFT)
+#define NAU8325_MCLK4XEN_EN 0x1
+
+/* ANALOG_CONTROL_6 (0x66) */
+#define NAU8325_VBATLOW_SFT 4
+#define NAU8325_VBATLOW_MASK (0x1 << NAU8325_VBATLOW_SFT)
+#define NAU8325_VDDSPK_LIM_SFT 3
+#define NAU8325_VDDSPK_LIM_EN (0x1 << NAU8325_VDDSPK_LIM_SFT)
+#define NAU8325_VDDSPK_LIM_MASK 0x7
+
+/* CLIP_CTRL (0x69)*/
+#define NAU8325_ANTI_CLIP_SFT 4
+#define NAU8325_ANTI_CLIP_EN (0x1 << NAU8325_ANTI_CLIP_SFT)
+
+/* RDAC (0x73) */
+#define NAU8325_CLK_DAC_DELAY_SFT 4
+#define NAU8325_CLK_DAC_DELAY_EN (0x7 << NAU8325_CLK_DAC_DELAY_SFT)
+#define NAU8325_DACVREFSEL_SFT 2
+#define NAU8325_DACVREFSEL_MASK (0x3 << NAU8325_DACVREFSEL_SFT)
+
+#define NAU8325_CODEC_DAI "nau8325-hifi"
+
+struct nau8325 {
+ struct device *dev;
+ struct regmap *regmap;
+ int mclk;
+ int fs;
+ int vref_impedance_ohms;
+ int dac_vref_microvolt;
+ int clock_detection;
+ int clock_det_data;
+ int alc_enable;
+};
+
+struct nau8325_src_attr {
+ int param;
+ unsigned int val;
+};
+
+enum {
+ NAU8325_MCLK_FS_RATIO_256,
+ NAU8325_MCLK_FS_RATIO_400,
+ NAU8325_MCLK_FS_RATIO_500,
+ NAU8325_MCLK_FS_RATIO_NUM,
+};
+
+struct nau8325_srate_attr {
+ int fs;
+ int range;
+ bool max;
+ unsigned int mclk_src[NAU8325_MCLK_FS_RATIO_NUM];
+};
+
+struct nau8325_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+#endif /* __NAU8325_H__ */
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
new file mode 100644
index 000000000000..caf2edb23088
--- /dev/null
+++ b/sound/soc/codecs/nau8540.c
@@ -0,0 +1,993 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "nau8540.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+/* the maximum frequency of CLK_ADC */
+#define CLK_ADC_MAX 6144000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8540_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8540_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8540_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+/* over sampling rate */
+static const struct nau8540_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8540_reg_defaults[] = {
+ {NAU8540_REG_POWER_MANAGEMENT, 0x0000},
+ {NAU8540_REG_CLOCK_CTRL, 0x0000},
+ {NAU8540_REG_CLOCK_SRC, 0x0000},
+ {NAU8540_REG_FLL1, 0x0001},
+ {NAU8540_REG_FLL2, 0x3126},
+ {NAU8540_REG_FLL3, 0x0008},
+ {NAU8540_REG_FLL4, 0x0010},
+ {NAU8540_REG_FLL5, 0xC000},
+ {NAU8540_REG_FLL6, 0x6000},
+ {NAU8540_REG_FLL_VCO_RSV, 0xF13C},
+ {NAU8540_REG_PCM_CTRL0, 0x000B},
+ {NAU8540_REG_PCM_CTRL1, 0x3010},
+ {NAU8540_REG_PCM_CTRL2, 0x0800},
+ {NAU8540_REG_PCM_CTRL3, 0x0000},
+ {NAU8540_REG_PCM_CTRL4, 0x000F},
+ {NAU8540_REG_ALC_CONTROL_1, 0x0000},
+ {NAU8540_REG_ALC_CONTROL_2, 0x700B},
+ {NAU8540_REG_ALC_CONTROL_3, 0x0022},
+ {NAU8540_REG_ALC_CONTROL_4, 0x1010},
+ {NAU8540_REG_ALC_CONTROL_5, 0x1010},
+ {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
+ {NAU8540_REG_HPF_FILTER_CH12, 0x0000},
+ {NAU8540_REG_HPF_FILTER_CH34, 0x0000},
+ {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
+ {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
+ {NAU8540_REG_DIGITAL_MUX, 0x00E4},
+ {NAU8540_REG_GPIO_CTRL, 0x0000},
+ {NAU8540_REG_MISC_CTRL, 0x0000},
+ {NAU8540_REG_I2C_CTRL, 0xEFFF},
+ {NAU8540_REG_VMID_CTRL, 0x0000},
+ {NAU8540_REG_MUTE, 0x0000},
+ {NAU8540_REG_ANALOG_ADC1, 0x0011},
+ {NAU8540_REG_ANALOG_ADC2, 0x0020},
+ {NAU8540_REG_ANALOG_PWR, 0x0000},
+ {NAU8540_REG_MIC_BIAS, 0x0004},
+ {NAU8540_REG_REFERENCE, 0x0000},
+ {NAU8540_REG_FEPGA1, 0x0000},
+ {NAU8540_REG_FEPGA2, 0x0000},
+ {NAU8540_REG_FEPGA3, 0x0101},
+ {NAU8540_REG_FEPGA4, 0x0101},
+ {NAU8540_REG_PWR, 0x0000},
+};
+
+static bool nau8540_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
+ case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+ case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+ case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
+ case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+ case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
+ case NAU8540_REG_I2C_DEVICE_ID:
+ case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+ case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8540_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
+ case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+ case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+ case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
+ case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+ case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
+ case NAU8540_REG_RST:
+ case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+ case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8540_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8540_REG_SW_RESET:
+ case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
+ case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
+ case NAU8540_REG_I2C_DEVICE_ID:
+ case NAU8540_REG_RST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+
+static const struct snd_kcontrol_new nau8540_snd_controls[] = {
+ SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
+ 0, 0x520, 0, adc_vol_tlv),
+
+ SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
+ 0, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
+ 8, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
+ 0, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
+ 8, 0x25, 0, fepga_gain_tlv),
+};
+
+static const char * const adc_channel[] = {
+ "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
+};
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch4_mux =
+ SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch3_mux =
+ SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch2_mux =
+ SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch1_mux =
+ SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
+
+static int nau8540_fepga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+ NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF |
+ NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF |
+ NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF |
+ NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF |
+ NAU8540_ACDC_CTL_MIC4N_VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int nau8540_precharge_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+ NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN);
+ msleep(40);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+ NAU8540_DISCHRG_EN, 0);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+ NAU8540_ACDC_CTL_MASK, 0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int adc_power_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ msleep(160);
+ /* DO12 and DO34 pad output enable */
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+ NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_TRI, 0);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_TRI, 0);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+ NAU8540_ADC_ALL_EN, 0);
+ }
+ return 0;
+}
+
+static int aiftx_power_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0001);
+ regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0000);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+
+ SND_SOC_DAPM_PGA_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0,
+ nau8540_precharge_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("Digital CH4 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
+ SND_SOC_DAPM_MUX("Digital CH3 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
+ SND_SOC_DAPM_MUX("Digital CH2 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
+ SND_SOC_DAPM_MUX("Digital CH1 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0,
+ aiftx_power_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
+ {"Frontend PGA1", NULL, "MIC1"},
+ {"Frontend PGA2", NULL, "MIC2"},
+ {"Frontend PGA3", NULL, "MIC3"},
+ {"Frontend PGA4", NULL, "MIC4"},
+
+ {"Precharge", NULL, "Frontend PGA1"},
+ {"Precharge", NULL, "Frontend PGA2"},
+ {"Precharge", NULL, "Frontend PGA3"},
+ {"Precharge", NULL, "Frontend PGA4"},
+
+ {"ADC CH1", NULL, "Precharge"},
+ {"ADC CH2", NULL, "Precharge"},
+ {"ADC CH3", NULL, "Precharge"},
+ {"ADC CH4", NULL, "Precharge"},
+
+ {"ADC CH1", NULL, "MICBIAS1"},
+ {"ADC CH2", NULL, "MICBIAS1"},
+ {"ADC CH3", NULL, "MICBIAS2"},
+ {"ADC CH4", NULL, "MICBIAS2"},
+
+ {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"AIFTX", NULL, "Digital CH1 Mux"},
+ {"AIFTX", NULL, "Digital CH2 Mux"},
+ {"AIFTX", NULL, "Digital CH3 Mux"},
+ {"AIFTX", NULL, "Digital CH4 Mux"},
+};
+
+static const struct nau8540_osr_attr *
+nau8540_get_osr(struct nau8540 *nau8540)
+{
+ unsigned int osr;
+
+ regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
+ osr &= NAU8540_ADC_OSR_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+}
+
+static int nau8540_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ const struct nau8540_osr_attr *osr;
+
+ osr = nau8540_get_osr(nau8540);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_ADC_MAX / osr->osr);
+}
+
+static int nau8540_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0;
+ const struct nau8540_osr_attr *osr;
+
+ /* CLK_ADC = OSR * FS
+ * ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ osr = nau8540_get_osr(nau8540);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+ if (params_rate(params) * osr->osr > CLK_ADC_MAX)
+ return -EINVAL;
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8540_CLK_ADC_SRC_SFT);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8540_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8540_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8540_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8540_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+ NAU8540_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= NAU8540_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8540_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8540_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8540_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8540_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8540_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+ NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
+ NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_OE, 0);
+
+ return 0;
+}
+
+/**
+ * nau8540_set_tdm_slot - configure DAI TX TDM.
+ * @dai: DAI
+ * @tx_mask: bitmask representing active TX slots. Ex.
+ * 0xf for normal 4 channel TDM.
+ * 0xf0 for shifted 4 channel TDM
+ * @rx_mask: no used.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Only support 4 slots TDM.
+ */
+static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl2_val = 0, ctrl4_val = 0;
+
+ if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
+ return -EINVAL;
+
+ ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
+ if (tx_mask & 0xf0) {
+ ctrl2_val = 4 * slot_width;
+ ctrl4_val |= (tx_mask >> 4);
+ } else {
+ ctrl4_val |= tx_mask;
+ }
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
+ NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
+ NAU8540_TDM_TX_MASK, ctrl4_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
+ NAU8540_I2S_DO34_OE | ctrl2_val);
+
+ return 0;
+}
+
+static int nau8540_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8540->regmap;
+ unsigned int val;
+ int ret = 0;
+
+ /* Reading the peak data to detect abnormal data in the ADC channel.
+ * If abnormal data happens, the driver takes recovery actions to
+ * refresh the ADC channel.
+ */
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_AGC_EN, NAU8540_CLK_AGC_EN);
+ regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3,
+ NAU8540_ALC_CH_ALL_EN, NAU8540_ALC_CH_ALL_EN);
+
+ regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val);
+ dev_dbg(nau8540->dev, "1.ADC CH1 peak data %x", val);
+ if (!val) {
+ regmap_update_bits(regmap, NAU8540_REG_MUTE,
+ NAU8540_PGA_CH_ALL_MUTE, NAU8540_PGA_CH_ALL_MUTE);
+ regmap_update_bits(regmap, NAU8540_REG_MUTE,
+ NAU8540_PGA_CH_ALL_MUTE, 0);
+ regmap_write(regmap, NAU8540_REG_RST, 0x1);
+ regmap_write(regmap, NAU8540_REG_RST, 0);
+ regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val);
+ dev_dbg(nau8540->dev, "2.ADC CH1 peak data %x", val);
+ if (!val) {
+ dev_err(nau8540->dev, "Channel recovery failed!!");
+ ret = -EIO;
+ }
+ }
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_AGC_EN, 0);
+ regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3,
+ NAU8540_ALC_CH_ALL_EN, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops nau8540_dai_ops = {
+ .startup = nau8540_dai_startup,
+ .hw_params = nau8540_hw_params,
+ .set_fmt = nau8540_set_fmt,
+ .set_tdm_slot = nau8540_set_tdm_slot,
+ .trigger = nau8540_dai_trigger,
+};
+
+#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
+#define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8540_dai = {
+ .name = "nau8540-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = NAU8540_RATES,
+ .formats = NAU8540_FORMATS,
+ },
+ .ops = &nau8540_dai_ops,
+};
+
+/**
+ * nau8540_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8540_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8540_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8540_fll_apply(struct regmap *regmap,
+ struct nau8540_fll *fll_param)
+{
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
+ NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
+ regmap_update_bits(regmap, NAU8540_REG_FLL1,
+ NAU8540_FLL_RATIO_MASK | NAU8540_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8540_ICTRL_LATCH_SFT));
+ /* FLL 16-bit fractional input */
+ regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8540_REG_FLL4,
+ NAU8540_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
+ regmap_update_bits(regmap,
+ NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_MASK,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8540_REG_FLL6,
+ NAU8540_SDM_EN | NAU8540_CUTOFF500,
+ NAU8540_SDM_EN | NAU8540_CUTOFF500);
+ } else {
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap, NAU8540_REG_FLL6,
+ NAU8540_SDM_EN | NAU8540_CUTOFF500, 0);
+ }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8540_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ struct nau8540_fll fll_param;
+ int ret, fs;
+
+ switch (pll_id) {
+ case NAU8540_CLK_FLL_MCLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK,
+ NAU8540_FLL_CLK_SRC_MCLK | 0);
+ break;
+
+ case NAU8540_CLK_FLL_BLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK,
+ NAU8540_FLL_CLK_SRC_BLK |
+ (0xf << NAU8540_GAIN_ERR_SFT));
+ break;
+
+ case NAU8540_CLK_FLL_FS:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK,
+ NAU8540_FLL_CLK_SRC_FS |
+ (0xf << NAU8540_GAIN_ERR_SFT));
+ break;
+
+ default:
+ dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
+ return -EINVAL;
+ }
+ dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq_out, pll_id);
+
+ fs = freq_out / 256;
+ ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8540_fll_apply(nau8540->regmap, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static int nau8540_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case NAU8540_CLK_DIS:
+ case NAU8540_CLK_MCLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+ NAU8540_DCO_EN, 0);
+ break;
+
+ case NAU8540_CLK_INTERNAL:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+ NAU8540_DCO_EN, NAU8540_DCO_EN);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+ break;
+
+ default:
+ dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static void nau8540_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+ regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+}
+
+static void nau8540_init_regs(struct nau8540 *nau8540)
+{
+ struct regmap *regmap = nau8540->regmap;
+
+ /* Enable Bias/VMID/VMID Tieoff */
+ regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
+ NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
+ NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
+ regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
+ NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
+ NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
+ mdelay(2);
+ regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
+ NAU8540_PU_PRE, NAU8540_PU_PRE);
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
+ NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
+ /* ADC OSR selection, CLK_ADC = Fs * OSR;
+ * Channel time alignment enable.
+ */
+ regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
+ NAU8540_CH_SYNC | NAU8540_ADC_OSR_MASK,
+ NAU8540_CH_SYNC | NAU8540_ADC_OSR_64);
+ /* PGA input mode selection */
+ regmap_update_bits(regmap, NAU8540_REG_FEPGA1,
+ NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT,
+ NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT);
+ regmap_update_bits(regmap, NAU8540_REG_FEPGA2,
+ NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT,
+ NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT);
+ /* DO12 and DO34 pad output disable */
+ regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI);
+ regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI);
+}
+
+static int __maybe_unused nau8540_suspend(struct snd_soc_component *component)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8540->regmap, true);
+ regcache_mark_dirty(nau8540->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8540_resume(struct snd_soc_component *component)
+{
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8540->regmap, false);
+ regcache_sync(nau8540->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8540_component_driver = {
+ .set_sysclk = nau8540_set_sysclk,
+ .set_pll = nau8540_set_pll,
+ .suspend = nau8540_suspend,
+ .resume = nau8540_resume,
+ .controls = nau8540_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8540_snd_controls),
+ .dapm_widgets = nau8540_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
+ .dapm_routes = nau8540_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config nau8540_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 16,
+
+ .max_register = NAU8540_REG_MAX,
+ .readable_reg = nau8540_readable_reg,
+ .writeable_reg = nau8540_writeable_reg,
+ .volatile_reg = nau8540_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8540_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
+};
+
+static int nau8540_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8540 *nau8540 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8540) {
+ nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
+ if (!nau8540)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8540);
+
+ nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
+ if (IS_ERR(nau8540->regmap))
+ return PTR_ERR(nau8540->regmap);
+ ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
+ ret);
+ return ret;
+ }
+
+ nau8540->dev = dev;
+ nau8540_reset_chip(nau8540->regmap);
+ nau8540_init_regs(nau8540);
+
+ return devm_snd_soc_register_component(dev,
+ &nau8540_component_driver, &nau8540_dai, 1);
+}
+
+static const struct i2c_device_id nau8540_i2c_ids[] = {
+ { "nau8540" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8540_of_ids[] = {
+ { .compatible = "nuvoton,nau8540", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8540_of_ids);
+#endif
+
+static struct i2c_driver nau8540_i2c_driver = {
+ .driver = {
+ .name = "nau8540",
+ .of_match_table = of_match_ptr(nau8540_of_ids),
+ },
+ .probe = nau8540_i2c_probe,
+ .id_table = nau8540_i2c_ids,
+};
+module_i2c_driver(nau8540_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU85L40 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
new file mode 100644
index 000000000000..762bb93b06fd
--- /dev/null
+++ b/sound/soc/codecs/nau8540.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#ifndef __NAU8540_H__
+#define __NAU8540_H__
+
+#define NAU8540_REG_SW_RESET 0x00
+#define NAU8540_REG_POWER_MANAGEMENT 0x01
+#define NAU8540_REG_CLOCK_CTRL 0x02
+#define NAU8540_REG_CLOCK_SRC 0x03
+#define NAU8540_REG_FLL1 0x04
+#define NAU8540_REG_FLL2 0x05
+#define NAU8540_REG_FLL3 0x06
+#define NAU8540_REG_FLL4 0x07
+#define NAU8540_REG_FLL5 0x08
+#define NAU8540_REG_FLL6 0x09
+#define NAU8540_REG_FLL_VCO_RSV 0x0A
+#define NAU8540_REG_PCM_CTRL0 0x10
+#define NAU8540_REG_PCM_CTRL1 0x11
+#define NAU8540_REG_PCM_CTRL2 0x12
+#define NAU8540_REG_PCM_CTRL3 0x13
+#define NAU8540_REG_PCM_CTRL4 0x14
+#define NAU8540_REG_ALC_CONTROL_1 0x20
+#define NAU8540_REG_ALC_CONTROL_2 0x21
+#define NAU8540_REG_ALC_CONTROL_3 0x22
+#define NAU8540_REG_ALC_CONTROL_4 0x23
+#define NAU8540_REG_ALC_CONTROL_5 0x24
+#define NAU8540_REG_ALC_GAIN_CH12 0x2D
+#define NAU8540_REG_ALC_GAIN_CH34 0x2E
+#define NAU8540_REG_ALC_STATUS 0x2F
+#define NAU8540_REG_NOTCH_FIL1_CH1 0x30
+#define NAU8540_REG_NOTCH_FIL2_CH1 0x31
+#define NAU8540_REG_NOTCH_FIL1_CH2 0x32
+#define NAU8540_REG_NOTCH_FIL2_CH2 0x33
+#define NAU8540_REG_NOTCH_FIL1_CH3 0x34
+#define NAU8540_REG_NOTCH_FIL2_CH3 0x35
+#define NAU8540_REG_NOTCH_FIL1_CH4 0x36
+#define NAU8540_REG_NOTCH_FIL2_CH4 0x37
+#define NAU8540_REG_HPF_FILTER_CH12 0x38
+#define NAU8540_REG_HPF_FILTER_CH34 0x39
+#define NAU8540_REG_ADC_SAMPLE_RATE 0x3A
+#define NAU8540_REG_DIGITAL_GAIN_CH1 0x40
+#define NAU8540_REG_DIGITAL_GAIN_CH2 0x41
+#define NAU8540_REG_DIGITAL_GAIN_CH3 0x42
+#define NAU8540_REG_DIGITAL_GAIN_CH4 0x43
+#define NAU8540_REG_DIGITAL_MUX 0x44
+#define NAU8540_REG_P2P_CH1 0x48
+#define NAU8540_REG_P2P_CH2 0x49
+#define NAU8540_REG_P2P_CH3 0x4A
+#define NAU8540_REG_P2P_CH4 0x4B
+#define NAU8540_REG_PEAK_CH1 0x4C
+#define NAU8540_REG_PEAK_CH2 0x4D
+#define NAU8540_REG_PEAK_CH3 0x4E
+#define NAU8540_REG_PEAK_CH4 0x4F
+#define NAU8540_REG_GPIO_CTRL 0x50
+#define NAU8540_REG_MISC_CTRL 0x51
+#define NAU8540_REG_I2C_CTRL 0x52
+#define NAU8540_REG_I2C_DEVICE_ID 0x58
+#define NAU8540_REG_RST 0x5A
+#define NAU8540_REG_VMID_CTRL 0x60
+#define NAU8540_REG_MUTE 0x61
+#define NAU8540_REG_ANALOG_ADC1 0x64
+#define NAU8540_REG_ANALOG_ADC2 0x65
+#define NAU8540_REG_ANALOG_PWR 0x66
+#define NAU8540_REG_MIC_BIAS 0x67
+#define NAU8540_REG_REFERENCE 0x68
+#define NAU8540_REG_FEPGA1 0x69
+#define NAU8540_REG_FEPGA2 0x6A
+#define NAU8540_REG_FEPGA3 0x6B
+#define NAU8540_REG_FEPGA4 0x6C
+#define NAU8540_REG_PWR 0x6D
+#define NAU8540_REG_MAX NAU8540_REG_PWR
+
+
+/* POWER_MANAGEMENT (0x01) */
+#define NAU8540_ADC_ALL_EN 0xf
+#define NAU8540_ADC4_EN (0x1 << 3)
+#define NAU8540_ADC3_EN (0x1 << 2)
+#define NAU8540_ADC2_EN (0x1 << 1)
+#define NAU8540_ADC1_EN 0x1
+
+/* CLOCK_CTRL (0x02) */
+#define NAU8540_CLK_ADC_EN (0x1 << 15)
+#define NAU8540_CLK_AGC_EN (0x1 << 3)
+#define NAU8540_CLK_I2S_EN (0x1 << 1)
+
+/* CLOCK_SRC (0x03) */
+#define NAU8540_CLK_SRC_SFT 15
+#define NAU8540_CLK_SRC_MASK (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_VCO (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_MCLK (0 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_ADC_SRC_SFT 6
+#define NAU8540_CLK_ADC_SRC_MASK (0x3 << NAU8540_CLK_ADC_SRC_SFT)
+#define NAU8540_CLK_MCLK_SRC_MASK 0xf
+
+/* FLL1 (0x04) */
+#define NAU8540_ICTRL_LATCH_SFT 10
+#define NAU8540_ICTRL_LATCH_MASK (0x7 << NAU8540_ICTRL_LATCH_SFT)
+#define NAU8540_FLL_RATIO_MASK 0x7f
+
+/* FLL3 (0x06) */
+#define NAU8540_GAIN_ERR_SFT 12
+#define NAU8540_GAIN_ERR_MASK (0xf << NAU8540_GAIN_ERR_SFT)
+#define NAU8540_FLL_CLK_SRC_SFT 10
+#define NAU8540_FLL_CLK_SRC_MASK (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_MCLK (0 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_BLK (0x2 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_FS (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_INTEGER_MASK 0x3ff
+
+/* FLL4 (0x07) */
+#define NAU8540_FLL_REF_DIV_SFT 10
+#define NAU8540_FLL_REF_DIV_MASK (0x3 << NAU8540_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8540_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8540_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8540_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_FILTER (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8540_DCO_EN (0x1 << 15)
+#define NAU8540_SDM_EN (0x1 << 14)
+#define NAU8540_CUTOFF500 (0x1 << 13)
+
+/* PCM_CTRL0 (0x10) */
+#define NAU8540_I2S_BP_SFT 7
+#define NAU8540_I2S_BP_INV (0x1 << NAU8540_I2S_BP_SFT)
+#define NAU8540_I2S_PCMB_SFT 6
+#define NAU8540_I2S_PCMB_EN (0x1 << NAU8540_I2S_PCMB_SFT)
+#define NAU8540_I2S_DL_SFT 2
+#define NAU8540_I2S_DL_MASK (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_16 (0 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_20 (0x1 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_24 (0x2 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_32 (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DF_MASK 0x3
+#define NAU8540_I2S_DF_RIGTH 0
+#define NAU8540_I2S_DF_LEFT 0x1
+#define NAU8540_I2S_DF_I2S 0x2
+#define NAU8540_I2S_DF_PCM_AB 0x3
+
+/* PCM_CTRL1 (0x11) */
+#define NAU8540_I2S_DO12_TRI (0x1 << 15)
+#define NAU8540_I2S_LRC_DIV_SFT 12
+#define NAU8540_I2S_LRC_DIV_MASK (0x3 << NAU8540_I2S_LRC_DIV_SFT)
+#define NAU8540_I2S_DO12_OE (0x1 << 4)
+#define NAU8540_I2S_MS_SFT 3
+#define NAU8540_I2S_MS_MASK (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_MASTER (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_SLAVE (0x0 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_BLK_DIV_MASK 0x7
+
+/* PCM_CTRL1 (0x12) */
+#define NAU8540_I2S_DO34_TRI (0x1 << 15)
+#define NAU8540_I2S_DO34_OE (0x1 << 11)
+#define NAU8540_I2S_TSLOT_L_MASK 0x3ff
+
+/* PCM_CTRL4 (0x14) */
+#define NAU8540_TDM_MODE (0x1 << 15)
+#define NAU8540_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8540_TDM_TX_MASK 0xf
+
+/* ALC_CONTROL_3 (0x22) */
+#define NAU8540_ALC_CH1_EN (0x1 << 12)
+#define NAU8540_ALC_CH2_EN (0x1 << 13)
+#define NAU8540_ALC_CH3_EN (0x1 << 14)
+#define NAU8540_ALC_CH4_EN (0x1 << 15)
+#define NAU8540_ALC_CH_ALL_EN (0xf << 12)
+
+/* ADC_SAMPLE_RATE (0x3A) */
+#define NAU8540_CH_SYNC (0x1 << 14)
+#define NAU8540_ADC_OSR_MASK 0x3
+#define NAU8540_ADC_OSR_256 0x3
+#define NAU8540_ADC_OSR_128 0x2
+#define NAU8540_ADC_OSR_64 0x1
+#define NAU8540_ADC_OSR_32 0x0
+
+/* VMID_CTRL (0x60) */
+#define NAU8540_VMID_EN (1 << 6)
+#define NAU8540_VMID_SEL_SFT 4
+#define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT)
+
+/* MUTE (0x61) */
+#define NAU8540_PGA_CH1_MUTE 0x1
+#define NAU8540_PGA_CH2_MUTE 0x2
+#define NAU8540_PGA_CH3_MUTE 0x4
+#define NAU8540_PGA_CH4_MUTE 0x8
+#define NAU8540_PGA_CH_ALL_MUTE 0xf
+
+/* MIC_BIAS (0x67) */
+#define NAU8540_PU_PRE (0x1 << 8)
+
+/* REFERENCE (0x68) */
+#define NAU8540_PRECHARGE_DIS (0x1 << 13)
+#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8540_DISCHRG_EN (0x1 << 11)
+
+/* FEPGA1 (0x69) */
+#define NAU8540_FEPGA1_MODCH2_SHT_SFT 7
+#define NAU8540_FEPGA1_MODCH2_SHT (0x1 << NAU8540_FEPGA1_MODCH2_SHT_SFT)
+#define NAU8540_FEPGA1_MODCH1_SHT_SFT 3
+#define NAU8540_FEPGA1_MODCH1_SHT (0x1 << NAU8540_FEPGA1_MODCH1_SHT_SFT)
+
+/* FEPGA2 (0x6A) */
+#define NAU8540_FEPGA2_MODCH4_SHT_SFT 7
+#define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT)
+#define NAU8540_FEPGA2_MODCH3_SHT_SFT 3
+#define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT)
+#define NAU8540_ACDC_CTL_SFT 8
+#define NAU8540_ACDC_CTL_MASK (0xff << NAU8540_ACDC_CTL_SFT)
+#define NAU8540_ACDC_CTL_MIC4N_VREF (0x1 << 15)
+#define NAU8540_ACDC_CTL_MIC4P_VREF (0x1 << 14)
+#define NAU8540_ACDC_CTL_MIC3N_VREF (0x1 << 13)
+#define NAU8540_ACDC_CTL_MIC3P_VREF (0x1 << 12)
+#define NAU8540_ACDC_CTL_MIC2N_VREF (0x1 << 11)
+#define NAU8540_ACDC_CTL_MIC2P_VREF (0x1 << 10)
+#define NAU8540_ACDC_CTL_MIC1N_VREF (0x1 << 9)
+#define NAU8540_ACDC_CTL_MIC1P_VREF (0x1 << 8)
+
+/* System Clock Source */
+enum {
+ NAU8540_CLK_DIS,
+ NAU8540_CLK_MCLK,
+ NAU8540_CLK_INTERNAL,
+ NAU8540_CLK_FLL_MCLK,
+ NAU8540_CLK_FLL_BLK,
+ NAU8540_CLK_FLL_FS,
+};
+
+struct nau8540 {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+struct nau8540_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8540_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* over sampling rate */
+struct nau8540_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+
+#endif /* __NAU8540_H__ */
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
new file mode 100644
index 000000000000..9870e62d372e
--- /dev/null
+++ b/sound/soc/codecs/nau8810.c
@@ -0,0 +1,929 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * nau8810.c -- NAU8810 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *
+ * Based on WM8974.c
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "nau8810.h"
+
+#define NAU_PLL_FREQ_MAX 100000000
+#define NAU_PLL_FREQ_MIN 90000000
+#define NAU_PLL_REF_MAX 33000000
+#define NAU_PLL_REF_MIN 8000000
+#define NAU_PLL_OPTOP_MIN 6
+
+
+static const int nau8810_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 };
+
+static const struct reg_default nau8810_reg_defaults[] = {
+ { NAU8810_REG_POWER1, 0x0000 },
+ { NAU8810_REG_POWER2, 0x0000 },
+ { NAU8810_REG_POWER3, 0x0000 },
+ { NAU8810_REG_IFACE, 0x0050 },
+ { NAU8810_REG_COMP, 0x0000 },
+ { NAU8810_REG_CLOCK, 0x0140 },
+ { NAU8810_REG_SMPLR, 0x0000 },
+ { NAU8810_REG_DAC, 0x0000 },
+ { NAU8810_REG_DACGAIN, 0x00FF },
+ { NAU8810_REG_ADC, 0x0100 },
+ { NAU8810_REG_ADCGAIN, 0x00FF },
+ { NAU8810_REG_EQ1, 0x012C },
+ { NAU8810_REG_EQ2, 0x002C },
+ { NAU8810_REG_EQ3, 0x002C },
+ { NAU8810_REG_EQ4, 0x002C },
+ { NAU8810_REG_EQ5, 0x002C },
+ { NAU8810_REG_DACLIM1, 0x0032 },
+ { NAU8810_REG_DACLIM2, 0x0000 },
+ { NAU8810_REG_NOTCH1, 0x0000 },
+ { NAU8810_REG_NOTCH2, 0x0000 },
+ { NAU8810_REG_NOTCH3, 0x0000 },
+ { NAU8810_REG_NOTCH4, 0x0000 },
+ { NAU8810_REG_ALC1, 0x0038 },
+ { NAU8810_REG_ALC2, 0x000B },
+ { NAU8810_REG_ALC3, 0x0032 },
+ { NAU8810_REG_NOISEGATE, 0x0000 },
+ { NAU8810_REG_PLLN, 0x0008 },
+ { NAU8810_REG_PLLK1, 0x000C },
+ { NAU8810_REG_PLLK2, 0x0093 },
+ { NAU8810_REG_PLLK3, 0x00E9 },
+ { NAU8810_REG_ATTEN, 0x0000 },
+ { NAU8810_REG_INPUT_SIGNAL, 0x0003 },
+ { NAU8810_REG_PGAGAIN, 0x0010 },
+ { NAU8810_REG_ADCBOOST, 0x0100 },
+ { NAU8810_REG_OUTPUT, 0x0002 },
+ { NAU8810_REG_SPKMIX, 0x0001 },
+ { NAU8810_REG_SPKGAIN, 0x0039 },
+ { NAU8810_REG_MONOMIX, 0x0001 },
+ { NAU8810_REG_POWER4, 0x0000 },
+ { NAU8810_REG_TSLOTCTL1, 0x0000 },
+ { NAU8810_REG_TSLOTCTL2, 0x0020 },
+ { NAU8810_REG_DEVICE_REVID, 0x0000 },
+ { NAU8810_REG_I2C_DEVICEID, 0x001A },
+ { NAU8810_REG_ADDITIONID, 0x00CA },
+ { NAU8810_REG_RESERVE, 0x0124 },
+ { NAU8810_REG_OUTCTL, 0x0001 },
+ { NAU8810_REG_ALC1ENHAN1, 0x0010 },
+ { NAU8810_REG_ALC1ENHAN2, 0x0000 },
+ { NAU8810_REG_MISCCTL, 0x0000 },
+ { NAU8810_REG_OUTTIEOFF, 0x0000 },
+ { NAU8810_REG_AGCP2POUT, 0x0000 },
+ { NAU8810_REG_AGCPOUT, 0x0000 },
+ { NAU8810_REG_AMTCTL, 0x0000 },
+ { NAU8810_REG_OUTTIEOFFMAN, 0x0000 },
+};
+
+static bool nau8810_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8810_REG_RESET ... NAU8810_REG_SMPLR:
+ case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN:
+ case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN:
+ case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5:
+ case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2:
+ case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4:
+ case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN:
+ case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN:
+ case NAU8810_REG_ADCBOOST:
+ case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX:
+ case NAU8810_REG_SPKGAIN:
+ case NAU8810_REG_MONOMIX:
+ case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2:
+ case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE:
+ case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2:
+ case NAU8810_REG_MISCCTL:
+ case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8810_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8810_REG_RESET ... NAU8810_REG_SMPLR:
+ case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN:
+ case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN:
+ case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5:
+ case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2:
+ case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4:
+ case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN:
+ case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN:
+ case NAU8810_REG_ADCBOOST:
+ case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX:
+ case NAU8810_REG_SPKGAIN:
+ case NAU8810_REG_MONOMIX:
+ case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2:
+ case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2:
+ case NAU8810_REG_MISCCTL:
+ case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8810_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8810_REG_RESET:
+ case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* The EQ parameters get function is to get the 5 band equalizer control.
+ * The regmap raw read can't work here because regmap doesn't provide
+ * value format for value width of 9 bits. Therefore, the driver reads data
+ * from cache and makes value format according to the endianness of
+ * bytes type control element.
+ */
+static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, reg, reg_val;
+ u16 *val;
+ __be16 tmp;
+
+ val = (u16 *)ucontrol->value.bytes.data;
+ reg = NAU8810_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ regmap_read(nau8810->regmap, reg + i, &reg_val);
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ tmp = cpu_to_be16(reg_val);
+ memcpy(val + i, &tmp, sizeof(tmp));
+ }
+
+ return 0;
+}
+
+/* The EQ parameters put function is to make configuration of 5 band equalizer
+ * control. These configuration includes central frequency, equalizer gain,
+ * cut-off frequency, bandwidth control, and equalizer path.
+ * The regmap raw write can't work here because regmap doesn't provide
+ * register and value format for register with address 7 bits and value 9 bits.
+ * Therefore, the driver makes value format according to the endianness of
+ * bytes type control element and writes data to codec.
+ */
+static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+ u16 *val, value;
+ int i, reg, ret;
+ __be16 *tmp;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ val = (u16 *)data;
+ reg = NAU8810_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ tmp = (__be16 *)(val + i);
+ value = be16_to_cpup(tmp);
+ ret = regmap_write(nau8810->regmap, reg + i, value);
+ if (ret) {
+ dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n",
+ reg + i, ret);
+ kfree(data);
+ return ret;
+ }
+ }
+ kfree(data);
+
+ return 0;
+}
+
+static const char * const nau8810_companding[] = {
+ "Off", "NC", "u-law", "A-law" };
+
+static const struct soc_enum nau8810_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_ADCCM_SFT,
+ ARRAY_SIZE(nau8810_companding), nau8810_companding);
+
+static const struct soc_enum nau8810_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_DACCM_SFT,
+ ARRAY_SIZE(nau8810_companding), nau8810_companding);
+
+static const char * const nau8810_deemp[] = {
+ "None", "32kHz", "44.1kHz", "48kHz" };
+
+static const struct soc_enum nau8810_deemp_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_DAC, NAU8810_DEEMP_SFT,
+ ARRAY_SIZE(nau8810_deemp), nau8810_deemp);
+
+static const char * const nau8810_eqmode[] = {"Capture", "Playback" };
+
+static const struct soc_enum nau8810_eqmode_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_EQ1, NAU8810_EQM_SFT,
+ ARRAY_SIZE(nau8810_eqmode), nau8810_eqmode);
+
+static const char * const nau8810_alc[] = {"Normal", "Limiter" };
+
+static const struct soc_enum nau8810_alc_enum =
+ SOC_ENUM_SINGLE(NAU8810_REG_ALC3, NAU8810_ALCM_SFT,
+ ARRAY_SIZE(nau8810_alc), nau8810_alc);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+
+static const struct snd_kcontrol_new nau8810_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8810_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8810_companding_dac_enum),
+ SOC_ENUM("DAC De-emphasis", nau8810_deemp_enum),
+
+ SOC_ENUM("EQ Function", nau8810_eqmode_enum),
+ SND_SOC_BYTES_EXT("EQ Parameters", 10,
+ nau8810_eq_get, nau8810_eq_put),
+
+ SOC_SINGLE("DAC Inversion Switch", NAU8810_REG_DAC,
+ NAU8810_DACPL_SFT, 1, 0),
+ SOC_SINGLE_TLV("Playback Volume", NAU8810_REG_DACGAIN,
+ NAU8810_DACGAIN_SFT, 0xff, 0, digital_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch", NAU8810_REG_ADC,
+ NAU8810_HPFEN_SFT, 1, 0),
+ SOC_SINGLE("High Pass Cut Off", NAU8810_REG_ADC,
+ NAU8810_HPF_SFT, 0x7, 0),
+
+ SOC_SINGLE("ADC Inversion Switch", NAU8810_REG_ADC,
+ NAU8810_ADCPL_SFT, 1, 0),
+ SOC_SINGLE_TLV("Capture Volume", NAU8810_REG_ADCGAIN,
+ NAU8810_ADCGAIN_SFT, 0xff, 0, digital_tlv),
+
+ SOC_SINGLE_TLV("EQ1 Volume", NAU8810_REG_EQ1,
+ NAU8810_EQ1GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 Volume", NAU8810_REG_EQ2,
+ NAU8810_EQ2GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 Volume", NAU8810_REG_EQ3,
+ NAU8810_EQ3GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 Volume", NAU8810_REG_EQ4,
+ NAU8810_EQ4GC_SFT, 0x18, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 Volume", NAU8810_REG_EQ5,
+ NAU8810_EQ5GC_SFT, 0x18, 1, eq_tlv),
+
+ SOC_SINGLE("DAC Limiter Switch", NAU8810_REG_DACLIM1,
+ NAU8810_DACLIMEN_SFT, 1, 0),
+ SOC_SINGLE("DAC Limiter Decay", NAU8810_REG_DACLIM1,
+ NAU8810_DACLIMDCY_SFT, 0xf, 0),
+ SOC_SINGLE("DAC Limiter Attack", NAU8810_REG_DACLIM1,
+ NAU8810_DACLIMATK_SFT, 0xf, 0),
+ SOC_SINGLE("DAC Limiter Threshold", NAU8810_REG_DACLIM2,
+ NAU8810_DACLIMTHL_SFT, 0x7, 0),
+ SOC_SINGLE("DAC Limiter Boost", NAU8810_REG_DACLIM2,
+ NAU8810_DACLIMBST_SFT, 0xf, 0),
+
+ SOC_ENUM("ALC Mode", nau8810_alc_enum),
+ SOC_SINGLE("ALC Enable Switch", NAU8810_REG_ALC1,
+ NAU8810_ALCEN_SFT, 1, 0),
+ SOC_SINGLE("ALC Max Volume", NAU8810_REG_ALC1,
+ NAU8810_ALCMXGAIN_SFT, 0x7, 0),
+ SOC_SINGLE("ALC Min Volume", NAU8810_REG_ALC1,
+ NAU8810_ALCMINGAIN_SFT, 0x7, 0),
+ SOC_SINGLE("ALC ZC Switch", NAU8810_REG_ALC2,
+ NAU8810_ALCZC_SFT, 1, 0),
+ SOC_SINGLE("ALC Hold", NAU8810_REG_ALC2,
+ NAU8810_ALCHT_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Target", NAU8810_REG_ALC2,
+ NAU8810_ALCSL_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Decay", NAU8810_REG_ALC3,
+ NAU8810_ALCDCY_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Attack", NAU8810_REG_ALC3,
+ NAU8810_ALCATK_SFT, 0xf, 0),
+ SOC_SINGLE("ALC Noise Gate Switch", NAU8810_REG_NOISEGATE,
+ NAU8810_ALCNEN_SFT, 1, 0),
+ SOC_SINGLE("ALC Noise Gate Threshold", NAU8810_REG_NOISEGATE,
+ NAU8810_ALCNTH_SFT, 0x7, 0),
+
+ SOC_SINGLE("PGA ZC Switch", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAZC_SFT, 1, 0),
+ SOC_SINGLE_TLV("PGA Volume", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAGAIN_SFT, 0x3f, 0, inpga_tlv),
+
+ SOC_SINGLE("Speaker ZC Switch", NAU8810_REG_SPKGAIN,
+ NAU8810_SPKZC_SFT, 1, 0),
+ SOC_SINGLE("Speaker Mute Switch", NAU8810_REG_SPKGAIN,
+ NAU8810_SPKMT_SFT, 1, 0),
+ SOC_SINGLE_TLV("Speaker Volume", NAU8810_REG_SPKGAIN,
+ NAU8810_SPKGAIN_SFT, 0x3f, 0, spk_tlv),
+
+ SOC_SINGLE("Capture Boost(+20dB)", NAU8810_REG_ADCBOOST,
+ NAU8810_PGABST_SFT, 1, 0),
+ SOC_SINGLE("Mono Mute Switch", NAU8810_REG_MONOMIX,
+ NAU8810_MOUTMXMT_SFT, 1, 0),
+
+ SOC_SINGLE("DAC Oversampling Rate(128x) Switch", NAU8810_REG_DAC,
+ NAU8810_DACOS_SFT, 1, 0),
+ SOC_SINGLE("ADC Oversampling Rate(128x) Switch", NAU8810_REG_ADC,
+ NAU8810_ADCOS_SFT, 1, 0),
+};
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_SPKMIX,
+ NAU8810_AUXSPK_SFT, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_SPKMIX,
+ NAU8810_BYPSPK_SFT, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_SPKMIX,
+ NAU8810_DACSPK_SFT, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_MONOMIX,
+ NAU8810_AUXMOUT_SFT, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_MONOMIX,
+ NAU8810_BYPMOUT_SFT, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_MONOMIX,
+ NAU8810_DACMOUT_SFT, 1, 0),
+};
+
+/* PGA Mute */
+static const struct snd_kcontrol_new nau8810_pgaboost_mixer_controls[] = {
+ SOC_DAPM_SINGLE("AUX PGA Switch", NAU8810_REG_ADCBOOST,
+ NAU8810_AUXBSTGAIN_SFT, 0x7, 0),
+ SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAMT_SFT, 1, 1),
+ SOC_DAPM_SINGLE("PMIC PGA Switch", NAU8810_REG_ADCBOOST,
+ NAU8810_PMICBSTGAIN_SFT, 0x7, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new nau8810_inpga[] = {
+ SOC_DAPM_SINGLE("AUX Switch", NAU8810_REG_INPUT_SIGNAL,
+ NAU8810_AUXPGA_SFT, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", NAU8810_REG_INPUT_SIGNAL,
+ NAU8810_NMICPGA_SFT, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", NAU8810_REG_INPUT_SIGNAL,
+ NAU8810_PMICPGA_SFT, 1, 0),
+};
+
+/* Loopback Switch */
+static const struct snd_kcontrol_new nau8810_loopback =
+ SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP,
+ NAU8810_ADDAP_SFT, 1, 0);
+
+static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+
+ regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &value);
+ return (value & NAU8810_CLKM_MASK);
+}
+
+static int check_mic_enabled(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+
+ regmap_read(nau8810->regmap, NAU8810_REG_INPUT_SIGNAL, &value);
+ if (value & NAU8810_PMICPGA_EN || value & NAU8810_NMICPGA_EN)
+ return 1;
+ regmap_read(nau8810->regmap, NAU8810_REG_ADCBOOST, &value);
+ if (value & NAU8810_PMICBSTGAIN_MASK)
+ return 1;
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Speaker Mixer", NAU8810_REG_POWER3,
+ NAU8810_SPKMX_EN_SFT, 0, &nau8810_speaker_mixer_controls[0],
+ ARRAY_SIZE(nau8810_speaker_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", NAU8810_REG_POWER3,
+ NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0],
+ ARRAY_SIZE(nau8810_mono_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "Playback", NAU8810_REG_POWER3,
+ NAU8810_DAC_EN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", NAU8810_REG_POWER2,
+ NAU8810_ADC_EN_SFT, 0),
+ SND_SOC_DAPM_PGA("SpkN Out", NAU8810_REG_POWER3,
+ NAU8810_NSPK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SpkP Out", NAU8810_REG_POWER3,
+ NAU8810_PSPK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", NAU8810_REG_POWER3,
+ NAU8810_MOUT_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Input PGA", NAU8810_REG_POWER2,
+ NAU8810_PGA_EN_SFT, 0, nau8810_inpga,
+ ARRAY_SIZE(nau8810_inpga)),
+ SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2,
+ NAU8810_BST_EN_SFT, 0, nau8810_pgaboost_mixer_controls,
+ ARRAY_SIZE(nau8810_pgaboost_mixer_controls)),
+ SND_SOC_DAPM_PGA("AUX Input", NAU8810_REG_POWER1,
+ NAU8810_AUX_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1,
+ NAU8810_MICBIAS_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL", NAU8810_REG_POWER1,
+ NAU8810_PLL_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &nau8810_loopback),
+
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("MICN"),
+ SND_SOC_DAPM_INPUT("MICP"),
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route nau8810_dapm_routes[] = {
+ {"DAC", NULL, "PLL", check_mclk_select_pll},
+
+ /* Mono output mixer */
+ {"Mono Mixer", "AUX Bypass Switch", "AUX Input"},
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "AUX Bypass Switch", "AUX Input"},
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Line Bypass Switch", "Input Boost Stage"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Input Boost Stage */
+ {"ADC", NULL, "Input Boost Stage"},
+ {"ADC", NULL, "PLL", check_mclk_select_pll},
+ {"Input Boost Stage", "AUX PGA Switch", "AUX Input"},
+ {"Input Boost Stage", "PGA Mute Switch", "Input PGA"},
+ {"Input Boost Stage", "PMIC PGA Switch", "MICP"},
+
+ /* Input PGA */
+ {"Input PGA", NULL, "Mic Bias", check_mic_enabled},
+ {"Input PGA", "AUX Switch", "AUX Input"},
+ {"Input PGA", "MicN Switch", "MICN"},
+ {"Input PGA", "MicP Switch", "MICP"},
+ {"AUX Input", NULL, "AUX"},
+
+ /* Digital Looptack */
+ {"Digital Loopback", "Switch", "ADC"},
+ {"DAC", NULL, "Digital Loopback"},
+};
+
+static int nau8810_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+
+ nau8810->clk_id = clk_id;
+ nau8810->sysclk = freq;
+ dev_dbg(nau8810->dev, "master sysclk %dHz, source %s\n",
+ freq, clk_id == NAU8810_SCLK_PLL ? "PLL" : "MCLK");
+
+ return 0;
+}
+
+static int nau8810_calc_pll(unsigned int pll_in,
+ unsigned int fs, struct nau8810_pll *pll_param)
+{
+ u64 f2, f2_max, pll_ratio;
+ int i, scal_sel;
+
+ if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN)
+ return -EINVAL;
+
+ f2_max = 0;
+ scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
+ for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
+ f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i];
+ f2 = div_u64(f2, 10);
+ if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
+ f2_max < f2) {
+ f2_max = f2;
+ scal_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(nau8810_mclk_scaler) == scal_sel)
+ return -EINVAL;
+ pll_param->mclk_scaler = scal_sel;
+ f2 = f2_max;
+
+ /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional
+ * input; round up the 24+4bit.
+ */
+ pll_ratio = div_u64(f2 << 28, pll_in);
+ pll_param->pre_factor = 0;
+ if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) {
+ pll_ratio <<= 1;
+ pll_param->pre_factor = 1;
+ }
+ pll_param->pll_int = (pll_ratio >> 28) & 0xF;
+ pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4);
+
+ return 0;
+}
+
+static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct regmap *map = nau8810->regmap;
+ struct nau8810_pll *pll_param = &nau8810->pll;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8810_calc_pll(freq_in, fs, pll_param);
+ if (ret < 0) {
+ dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_info(nau8810->dev, "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n",
+ pll_param->pll_int, pll_param->pll_frac, pll_param->mclk_scaler,
+ pll_param->pre_factor);
+
+ regmap_update_bits(map, NAU8810_REG_PLLN,
+ NAU8810_PLLMCLK_DIV2 | NAU8810_PLLN_MASK,
+ (pll_param->pre_factor ? NAU8810_PLLMCLK_DIV2 : 0) |
+ pll_param->pll_int);
+ regmap_write(map, NAU8810_REG_PLLK1,
+ (pll_param->pll_frac >> NAU8810_PLLK1_SFT) &
+ NAU8810_PLLK1_MASK);
+ regmap_write(map, NAU8810_REG_PLLK2,
+ (pll_param->pll_frac >> NAU8810_PLLK2_SFT) &
+ NAU8810_PLLK2_MASK);
+ regmap_write(map, NAU8810_REG_PLLK3,
+ pll_param->pll_frac & NAU8810_PLLK3_MASK);
+ regmap_update_bits(map, NAU8810_REG_CLOCK, NAU8810_MCLKSEL_MASK,
+ pll_param->mclk_scaler << NAU8810_MCLKSEL_SFT);
+ regmap_update_bits(map, NAU8810_REG_CLOCK,
+ NAU8810_CLKM_MASK, NAU8810_CLKM_PLL);
+
+ return 0;
+}
+
+static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ u16 ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= NAU8810_CLKIO_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8810_AIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8810_AIFMT_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8810_AIFMT_PCM_A;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8810_BCLKP_IB;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl1_val |= NAU8810_FSP_IF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE,
+ NAU8810_AIFMT_MASK | NAU8810_FSP_IF |
+ NAU8810_BCLKP_IB, ctrl1_val);
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_CLKIO_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate)
+{
+ int i, sclk, imclk = rate * 256, div = 0;
+
+ if (!nau8810->sysclk) {
+ dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n");
+ return -EINVAL;
+ }
+
+ /* Configure the master clock prescaler div to make system
+ * clock to approximate the internal master clock (IMCLK);
+ * and large or equal to IMCLK.
+ */
+ for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
+ sclk = (nau8810->sysclk * 10) /
+ nau8810_mclk_scaler[i];
+ if (sclk < imclk)
+ break;
+ div = i;
+ }
+ dev_dbg(nau8810->dev,
+ "master clock prescaler %x for fs %d\n", div, rate);
+
+ /* master clock from MCLK and disable PLL */
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_MCLKSEL_MASK, (div << NAU8810_MCLKSEL_SFT));
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK);
+
+ return 0;
+}
+
+static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ int val_len = 0, val_rate = 0, ret = 0;
+ unsigned int ctrl_val, bclk_fs, bclk_div;
+
+ /* Select BCLK configuration if the codec as master. */
+ regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val);
+ if (ctrl_val & NAU8810_CLKIO_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = NAU8810_BCLKDIV_8;
+ else if (bclk_fs <= 64)
+ bclk_div = NAU8810_BCLKDIV_4;
+ else if (bclk_fs <= 128)
+ bclk_div = NAU8810_BCLKDIV_2;
+ else
+ return -EINVAL;
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_BCLKSEL_MASK, bclk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= NAU8810_WLEN_20;
+ break;
+ case 24:
+ val_len |= NAU8810_WLEN_24;
+ break;
+ case 32:
+ val_len |= NAU8810_WLEN_32;
+ break;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val_rate |= NAU8810_SMPLR_8K;
+ break;
+ case 11025:
+ val_rate |= NAU8810_SMPLR_12K;
+ break;
+ case 16000:
+ val_rate |= NAU8810_SMPLR_16K;
+ break;
+ case 22050:
+ val_rate |= NAU8810_SMPLR_24K;
+ break;
+ case 32000:
+ val_rate |= NAU8810_SMPLR_32K;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE,
+ NAU8810_WLEN_MASK, val_len);
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_SMPLR,
+ NAU8810_SMPLR_MASK, val_rate);
+
+ /* If the master clock is from MCLK, provide the runtime FS for driver
+ * to get the master clock prescaler configuration.
+ */
+ if (nau8810->clk_id == NAU8810_SCLK_MCLK) {
+ ret = nau8810_mclk_clkdiv(nau8810, params_rate(params));
+ if (ret < 0)
+ dev_err(nau8810->dev, "MCLK div configuration fail\n");
+ }
+
+ return ret;
+}
+
+static int nau8810_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
+ struct regmap *map = nau8810->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_REFIMP_MASK, NAU8810_REFIMP_80K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_IOBUF_EN | NAU8810_ABIAS_EN,
+ NAU8810_IOBUF_EN | NAU8810_ABIAS_EN);
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ regcache_sync(map);
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_REFIMP_MASK, NAU8810_REFIMP_3K);
+ mdelay(100);
+ }
+ regmap_update_bits(map, NAU8810_REG_POWER1,
+ NAU8810_REFIMP_MASK, NAU8810_REFIMP_300K);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regmap_write(map, NAU8810_REG_POWER1, 0);
+ regmap_write(map, NAU8810_REG_POWER2, 0);
+ regmap_write(map, NAU8810_REG_POWER3, 0);
+ break;
+ }
+
+ return 0;
+}
+
+
+#define NAU8810_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops nau8810_ops = {
+ .hw_params = nau8810_pcm_hw_params,
+ .set_fmt = nau8810_set_dai_fmt,
+ .set_sysclk = nau8810_set_sysclk,
+ .set_pll = nau8810_set_pll,
+};
+
+static struct snd_soc_dai_driver nau8810_dai = {
+ .name = "nau8810-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = NAU8810_RATES,
+ .formats = NAU8810_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = NAU8810_RATES,
+ .formats = NAU8810_FORMATS,
+ },
+ .ops = &nau8810_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct regmap_config nau8810_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = NAU8810_REG_MAX,
+ .readable_reg = nau8810_readable_reg,
+ .writeable_reg = nau8810_writeable_reg,
+ .volatile_reg = nau8810_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8810_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults),
+};
+
+static const struct snd_soc_component_driver nau8810_component_driver = {
+ .set_bias_level = nau8810_set_bias_level,
+ .controls = nau8810_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8810_snd_controls),
+ .dapm_widgets = nau8810_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8810_dapm_widgets),
+ .dapm_routes = nau8810_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8810_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int nau8810_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8810 *nau8810 = dev_get_platdata(dev);
+
+ if (!nau8810) {
+ nau8810 = devm_kzalloc(dev, sizeof(*nau8810), GFP_KERNEL);
+ if (!nau8810)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8810);
+
+ nau8810->regmap = devm_regmap_init_i2c(i2c, &nau8810_regmap_config);
+ if (IS_ERR(nau8810->regmap))
+ return PTR_ERR(nau8810->regmap);
+ nau8810->dev = dev;
+
+ regmap_write(nau8810->regmap, NAU8810_REG_RESET, 0x00);
+
+ return devm_snd_soc_register_component(dev,
+ &nau8810_component_driver, &nau8810_dai, 1);
+}
+
+static const struct i2c_device_id nau8810_i2c_id[] = {
+ { "nau8810" },
+ { "nau8812" },
+ { "nau8814" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8810_of_match[] = {
+ { .compatible = "nuvoton,nau8810", },
+ { .compatible = "nuvoton,nau8812", },
+ { .compatible = "nuvoton,nau8814", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nau8810_of_match);
+#endif
+
+static struct i2c_driver nau8810_i2c_driver = {
+ .driver = {
+ .name = "nau8810",
+ .of_match_table = of_match_ptr(nau8810_of_match),
+ },
+ .probe = nau8810_i2c_probe,
+ .id_table = nau8810_i2c_id,
+};
+
+module_i2c_driver(nau8810_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8810 driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8810.h b/sound/soc/codecs/nau8810.h
new file mode 100644
index 000000000000..6a7cacbe044a
--- /dev/null
+++ b/sound/soc/codecs/nau8810.h
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU8810 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ */
+
+#ifndef __NAU8810_H__
+#define __NAU8810_H__
+
+#define NAU8810_REG_RESET 0x00
+#define NAU8810_REG_POWER1 0x01
+#define NAU8810_REG_POWER2 0x02
+#define NAU8810_REG_POWER3 0x03
+#define NAU8810_REG_IFACE 0x04
+#define NAU8810_REG_COMP 0x05
+#define NAU8810_REG_CLOCK 0x06
+#define NAU8810_REG_SMPLR 0x07
+#define NAU8810_REG_DAC 0x0A
+#define NAU8810_REG_DACGAIN 0x0B
+#define NAU8810_REG_ADC 0x0E
+#define NAU8810_REG_ADCGAIN 0x0F
+#define NAU8810_REG_EQ1 0x12
+#define NAU8810_REG_EQ2 0x13
+#define NAU8810_REG_EQ3 0x14
+#define NAU8810_REG_EQ4 0x15
+#define NAU8810_REG_EQ5 0x16
+#define NAU8810_REG_DACLIM1 0x18
+#define NAU8810_REG_DACLIM2 0x19
+#define NAU8810_REG_NOTCH1 0x1B
+#define NAU8810_REG_NOTCH2 0x1C
+#define NAU8810_REG_NOTCH3 0x1D
+#define NAU8810_REG_NOTCH4 0x1E
+#define NAU8810_REG_ALC1 0x20
+#define NAU8810_REG_ALC2 0x21
+#define NAU8810_REG_ALC3 0x22
+#define NAU8810_REG_NOISEGATE 0x23
+#define NAU8810_REG_PLLN 0x24
+#define NAU8810_REG_PLLK1 0x25
+#define NAU8810_REG_PLLK2 0x26
+#define NAU8810_REG_PLLK3 0x27
+#define NAU8810_REG_ATTEN 0x28
+#define NAU8810_REG_INPUT_SIGNAL 0x2C
+#define NAU8810_REG_PGAGAIN 0x2D
+#define NAU8810_REG_ADCBOOST 0x2F
+#define NAU8810_REG_OUTPUT 0x31
+#define NAU8810_REG_SPKMIX 0x32
+#define NAU8810_REG_SPKGAIN 0x36
+#define NAU8810_REG_MONOMIX 0x38
+#define NAU8810_REG_POWER4 0x3A
+#define NAU8810_REG_TSLOTCTL1 0x3B
+#define NAU8810_REG_TSLOTCTL2 0x3C
+#define NAU8810_REG_DEVICE_REVID 0x3E
+#define NAU8810_REG_I2C_DEVICEID 0x3F
+#define NAU8810_REG_ADDITIONID 0x40
+#define NAU8810_REG_RESERVE 0x41
+#define NAU8810_REG_OUTCTL 0x45
+#define NAU8810_REG_ALC1ENHAN1 0x46
+#define NAU8810_REG_ALC1ENHAN2 0x47
+#define NAU8810_REG_MISCCTL 0x49
+#define NAU8810_REG_OUTTIEOFF 0x4B
+#define NAU8810_REG_AGCP2POUT 0x4C
+#define NAU8810_REG_AGCPOUT 0x4D
+#define NAU8810_REG_AMTCTL 0x4E
+#define NAU8810_REG_OUTTIEOFFMAN 0x4F
+#define NAU8810_REG_MAX NAU8810_REG_OUTTIEOFFMAN
+
+
+/* NAU8810_REG_POWER1 (0x1) */
+#define NAU8810_DCBUF_EN (0x1 << 8)
+#define NAU8810_AUX_EN_SFT 6
+#define NAU8810_PLL_EN_SFT 5
+#define NAU8810_MICBIAS_EN_SFT 4
+#define NAU8810_ABIAS_EN (0x1 << 3)
+#define NAU8810_IOBUF_EN (0x1 << 2)
+#define NAU8810_REFIMP_MASK 0x3
+#define NAU8810_REFIMP_DIS 0x0
+#define NAU8810_REFIMP_80K 0x1
+#define NAU8810_REFIMP_300K 0x2
+#define NAU8810_REFIMP_3K 0x3
+
+/* NAU8810_REG_POWER2 (0x2) */
+#define NAU8810_BST_EN_SFT 4
+#define NAU8810_PGA_EN_SFT 2
+#define NAU8810_ADC_EN_SFT 0
+
+/* NAU8810_REG_POWER3 (0x3) */
+#define NAU8810_DAC_EN_SFT 0
+#define NAU8810_SPKMX_EN_SFT 2
+#define NAU8810_MOUTMX_EN_SFT 3
+#define NAU8810_PSPK_EN_SFT 5
+#define NAU8810_NSPK_EN_SFT 6
+#define NAU8810_MOUT_EN_SFT 7
+
+/* NAU8810_REG_IFACE (0x4) */
+#define NAU8810_AIFMT_SFT 3
+#define NAU8810_AIFMT_MASK (0x3 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_RIGHT (0x0 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_LEFT (0x1 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_I2S (0x2 << NAU8810_AIFMT_SFT)
+#define NAU8810_AIFMT_PCM_A (0x3 << NAU8810_AIFMT_SFT)
+#define NAU8810_WLEN_SFT 5
+#define NAU8810_WLEN_MASK (0x3 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_16 (0x0 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_20 (0x1 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_24 (0x2 << NAU8810_WLEN_SFT)
+#define NAU8810_WLEN_32 (0x3 << NAU8810_WLEN_SFT)
+#define NAU8810_FSP_IF (0x1 << 7)
+#define NAU8810_BCLKP_IB (0x1 << 8)
+
+/* NAU8810_REG_COMP (0x5) */
+#define NAU8810_ADDAP_SFT 0
+#define NAU8810_ADCCM_SFT 1
+#define NAU8810_DACCM_SFT 3
+
+/* NAU8810_REG_CLOCK (0x6) */
+#define NAU8810_CLKIO_MASK 0x1
+#define NAU8810_CLKIO_SLAVE 0x0
+#define NAU8810_CLKIO_MASTER 0x1
+#define NAU8810_BCLKSEL_SFT 2
+#define NAU8810_BCLKSEL_MASK (0x7 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_1 (0x0 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_2 (0x1 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_4 (0x2 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_8 (0x3 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_16 (0x4 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_BCLKDIV_32 (0x5 << NAU8810_BCLKSEL_SFT)
+#define NAU8810_MCLKSEL_SFT 5
+#define NAU8810_MCLKSEL_MASK (0x7 << NAU8810_MCLKSEL_SFT)
+#define NAU8810_CLKM_SFT 8
+#define NAU8810_CLKM_MASK (0x1 << NAU8810_CLKM_SFT)
+#define NAU8810_CLKM_MCLK (0x0 << NAU8810_CLKM_SFT)
+#define NAU8810_CLKM_PLL (0x1 << NAU8810_CLKM_SFT)
+
+/* NAU8810_REG_SMPLR (0x7) */
+#define NAU8810_SMPLR_SFT 1
+#define NAU8810_SMPLR_MASK (0x7 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_48K (0x0 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_32K (0x1 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_24K (0x2 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_16K (0x3 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_12K (0x4 << NAU8810_SMPLR_SFT)
+#define NAU8810_SMPLR_8K (0x5 << NAU8810_SMPLR_SFT)
+
+/* NAU8810_REG_DAC (0xA) */
+#define NAU8810_DACPL_SFT 0
+#define NAU8810_DACOS_SFT 3
+#define NAU8810_DEEMP_SFT 4
+
+/* NAU8810_REG_DACGAIN (0xB) */
+#define NAU8810_DACGAIN_SFT 0
+
+/* NAU8810_REG_ADC (0xE) */
+#define NAU8810_ADCPL_SFT 0
+#define NAU8810_ADCOS_SFT 3
+#define NAU8810_HPF_SFT 4
+#define NAU8810_HPFEN_SFT 8
+
+/* NAU8810_REG_ADCGAIN (0xF) */
+#define NAU8810_ADCGAIN_SFT 0
+
+/* NAU8810_REG_EQ1 (0x12) */
+#define NAU8810_EQ1GC_SFT 0
+#define NAU8810_EQ1CF_SFT 5
+#define NAU8810_EQM_SFT 8
+
+/* NAU8810_REG_EQ2 (0x13) */
+#define NAU8810_EQ2GC_SFT 0
+#define NAU8810_EQ2CF_SFT 5
+#define NAU8810_EQ2BW_SFT 8
+
+/* NAU8810_REG_EQ3 (0x14) */
+#define NAU8810_EQ3GC_SFT 0
+#define NAU8810_EQ3CF_SFT 5
+#define NAU8810_EQ3BW_SFT 8
+
+/* NAU8810_REG_EQ4 (0x15) */
+#define NAU8810_EQ4GC_SFT 0
+#define NAU8810_EQ4CF_SFT 5
+#define NAU8810_EQ4BW_SFT 8
+
+/* NAU8810_REG_EQ5 (0x16) */
+#define NAU8810_EQ5GC_SFT 0
+#define NAU8810_EQ5CF_SFT 5
+
+/* NAU8810_REG_DACLIM1 (0x18) */
+#define NAU8810_DACLIMATK_SFT 0
+#define NAU8810_DACLIMDCY_SFT 4
+#define NAU8810_DACLIMEN_SFT 8
+
+/* NAU8810_REG_DACLIM2 (0x19) */
+#define NAU8810_DACLIMBST_SFT 0
+#define NAU8810_DACLIMTHL_SFT 4
+
+/* NAU8810_REG_ALC1 (0x20) */
+#define NAU8810_ALCMINGAIN_SFT 0
+#define NAU8810_ALCMXGAIN_SFT 3
+#define NAU8810_ALCEN_SFT 8
+
+/* NAU8810_REG_ALC2 (0x21) */
+#define NAU8810_ALCSL_SFT 0
+#define NAU8810_ALCHT_SFT 4
+#define NAU8810_ALCZC_SFT 8
+
+/* NAU8810_REG_ALC3 (0x22) */
+#define NAU8810_ALCATK_SFT 0
+#define NAU8810_ALCDCY_SFT 4
+#define NAU8810_ALCM_SFT 8
+
+/* NAU8810_REG_NOISEGATE (0x23) */
+#define NAU8810_ALCNTH_SFT 0
+#define NAU8810_ALCNEN_SFT 3
+
+/* NAU8810_REG_PLLN (0x24) */
+#define NAU8810_PLLN_MASK 0xF
+#define NAU8810_PLLMCLK_DIV2 (0x1 << 4)
+
+/* NAU8810_REG_PLLK1 (0x25) */
+#define NAU8810_PLLK1_SFT 18
+#define NAU8810_PLLK1_MASK 0x3F
+
+/* NAU8810_REG_PLLK2 (0x26) */
+#define NAU8810_PLLK2_SFT 9
+#define NAU8810_PLLK2_MASK 0x1FF
+
+/* NAU8810_REG_PLLK3 (0x27) */
+#define NAU8810_PLLK3_MASK 0x1FF
+
+/* NAU8810_REG_INPUT_SIGNAL (0x2C) */
+#define NAU8810_PMICPGA_SFT 0
+#define NAU8810_PMICPGA_EN (0x1 << NAU8810_PMICPGA_SFT)
+#define NAU8810_NMICPGA_SFT 1
+#define NAU8810_NMICPGA_EN (0x1 << NAU8810_NMICPGA_SFT)
+#define NAU8810_AUXPGA_SFT 2
+
+/* NAU8810_REG_PGAGAIN (0x2D) */
+#define NAU8810_PGAGAIN_SFT 0
+#define NAU8810_PGAMT_SFT 6
+#define NAU8810_PGAZC_SFT 7
+
+/* NAU8810_REG_ADCBOOST (0x2F) */
+#define NAU8810_AUXBSTGAIN_SFT 0
+#define NAU8810_PMICBSTGAIN_SFT 4
+#define NAU8810_PMICBSTGAIN_MASK (0x7 << NAU8810_PMICBSTGAIN_SFT)
+#define NAU8810_PGABST_SFT 8
+
+/* NAU8810_REG_SPKMIX (0x32) */
+#define NAU8810_DACSPK_SFT 0
+#define NAU8810_BYPSPK_SFT 1
+#define NAU8810_AUXSPK_SFT 5
+
+/* NAU8810_REG_SPKGAIN (0x36) */
+#define NAU8810_SPKGAIN_SFT 0
+#define NAU8810_SPKMT_SFT 6
+#define NAU8810_SPKZC_SFT 7
+
+/* NAU8810_REG_MONOMIX (0x38) */
+#define NAU8810_DACMOUT_SFT 0
+#define NAU8810_BYPMOUT_SFT 1
+#define NAU8810_AUXMOUT_SFT 2
+#define NAU8810_MOUTMXMT_SFT 6
+
+
+/* System Clock Source */
+enum {
+ NAU8810_SCLK_MCLK,
+ NAU8810_SCLK_PLL,
+};
+
+struct nau8810_pll {
+ int pre_factor;
+ int mclk_scaler;
+ int pll_frac;
+ int pll_int;
+};
+
+struct nau8810 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct nau8810_pll pll;
+ int sysclk;
+ int clk_id;
+};
+
+#endif
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
new file mode 100644
index 000000000000..3beb3c44dc2c
--- /dev/null
+++ b/sound/soc/codecs/nau8821.c
@@ -0,0 +1,1984 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8821.c -- Nuvoton NAU88L21 audio codec driver
+//
+// Copyright 2021 Nuvoton Technology Corp.
+// Author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Lee <wtli@nuvoton.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "nau8821.h"
+
+#define NAU8821_QUIRK_JD_ACTIVE_HIGH BIT(0)
+#define NAU8821_QUIRK_JD_DB_BYPASS BIT(1)
+
+static int nau8821_quirk;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+#define NAU8821_BUTTON SND_JACK_BTN_0
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+static int nau8821_configure_sysclk(struct nau8821 *nau8821,
+ int clk_id, unsigned int freq);
+static bool nau8821_is_jack_inserted(struct regmap *regmap);
+
+struct nau8821_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8821_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8821_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8821_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8821_fll_attr fll_pre_scalar[] = {
+ { 0, 0x0 },
+ { 1, 0x1 },
+ { 2, 0x2 },
+ { 3, 0x3 },
+};
+
+/* over sampling rate */
+struct nau8821_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+static const struct nau8821_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8821_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+struct nau8821_dmic_speed {
+ unsigned int param;
+ unsigned int val;
+};
+
+static const struct nau8821_dmic_speed dmic_speed_sel[] = {
+ { 0, 0x0 }, /*SPEED 1, SRC 1 */
+ { 1, 0x1 }, /*SPEED 2, SRC 1/2 */
+ { 2, 0x2 }, /*SPEED 4, SRC 1/4 */
+ { 3, 0x3 }, /*SPEED 8, SRC 1/8 */
+};
+
+static const struct reg_default nau8821_reg_defaults[] = {
+ { NAU8821_R01_ENA_CTRL, 0x00ff },
+ { NAU8821_R03_CLK_DIVIDER, 0x0050 },
+ { NAU8821_R04_FLL1, 0x0 },
+ { NAU8821_R05_FLL2, 0x00bc },
+ { NAU8821_R06_FLL3, 0x0008 },
+ { NAU8821_R07_FLL4, 0x0010 },
+ { NAU8821_R08_FLL5, 0x4000 },
+ { NAU8821_R09_FLL6, 0x6900 },
+ { NAU8821_R0A_FLL7, 0x0031 },
+ { NAU8821_R0B_FLL8, 0x26e9 },
+ { NAU8821_R0D_JACK_DET_CTRL, 0x0 },
+ { NAU8821_R0F_INTERRUPT_MASK, 0x0 },
+ { NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8821_R13_DMIC_CTRL, 0x0 },
+ { NAU8821_R1A_GPIO12_CTRL, 0x0 },
+ { NAU8821_R1B_TDM_CTRL, 0x0 },
+ { NAU8821_R1C_I2S_PCM_CTRL1, 0x000a },
+ { NAU8821_R1D_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8821_R1E_LEFT_TIME_SLOT, 0x0 },
+ { NAU8821_R1F_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8821_R21_BIQ0_COF1, 0x0 },
+ { NAU8821_R22_BIQ0_COF2, 0x0 },
+ { NAU8821_R23_BIQ0_COF3, 0x0 },
+ { NAU8821_R24_BIQ0_COF4, 0x0 },
+ { NAU8821_R25_BIQ0_COF5, 0x0 },
+ { NAU8821_R26_BIQ0_COF6, 0x0 },
+ { NAU8821_R27_BIQ0_COF7, 0x0 },
+ { NAU8821_R28_BIQ0_COF8, 0x0 },
+ { NAU8821_R29_BIQ0_COF9, 0x0 },
+ { NAU8821_R2A_BIQ0_COF10, 0x0 },
+ { NAU8821_R2B_ADC_RATE, 0x0002 },
+ { NAU8821_R2C_DAC_CTRL1, 0x0082 },
+ { NAU8821_R2D_DAC_CTRL2, 0x0 },
+ { NAU8821_R2F_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8821_R30_ADC_DGAIN_CTRL, 0x0 },
+ { NAU8821_R31_MUTE_CTRL, 0x0 },
+ { NAU8821_R32_HSVOL_CTRL, 0x0 },
+ { NAU8821_R34_DACR_CTRL, 0xcfcf },
+ { NAU8821_R35_ADC_DGAIN_CTRL1, 0xcfcf },
+ { NAU8821_R36_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8821_R37_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8821_R38_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8821_R39_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8821_R3A_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8821_R3B_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8821_R3C_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8821_R3D_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8821_R41_BIQ1_COF1, 0x0 },
+ { NAU8821_R42_BIQ1_COF2, 0x0 },
+ { NAU8821_R43_BIQ1_COF3, 0x0 },
+ { NAU8821_R44_BIQ1_COF4, 0x0 },
+ { NAU8821_R45_BIQ1_COF5, 0x0 },
+ { NAU8821_R46_BIQ1_COF6, 0x0 },
+ { NAU8821_R47_BIQ1_COF7, 0x0 },
+ { NAU8821_R48_BIQ1_COF8, 0x0 },
+ { NAU8821_R49_BIQ1_COF9, 0x0 },
+ { NAU8821_R4A_BIQ1_COF10, 0x0 },
+ { NAU8821_R4B_CLASSG_CTRL, 0x0 },
+ { NAU8821_R4C_IMM_MODE_CTRL, 0x0 },
+ { NAU8821_R4D_IMM_RMS_L, 0x0 },
+ { NAU8821_R53_OTPDOUT_1, 0xaad8 },
+ { NAU8821_R54_OTPDOUT_2, 0x0002 },
+ { NAU8821_R55_MISC_CTRL, 0x0 },
+ { NAU8821_R66_BIAS_ADJ, 0x0 },
+ { NAU8821_R68_TRIM_SETTINGS, 0x0 },
+ { NAU8821_R69_ANALOG_CONTROL_1, 0x0 },
+ { NAU8821_R6A_ANALOG_CONTROL_2, 0x0 },
+ { NAU8821_R6B_PGA_MUTE, 0x0 },
+ { NAU8821_R71_ANALOG_ADC_1, 0x0011 },
+ { NAU8821_R72_ANALOG_ADC_2, 0x0020 },
+ { NAU8821_R73_RDAC, 0x0008 },
+ { NAU8821_R74_MIC_BIAS, 0x0006 },
+ { NAU8821_R76_BOOST, 0x0 },
+ { NAU8821_R77_FEPGA, 0x0 },
+ { NAU8821_R7E_PGA_GAIN, 0x0 },
+ { NAU8821_R7F_POWER_UP_CONTROL, 0x0 },
+ { NAU8821_R80_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8821_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL:
+ case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8:
+ case NAU8821_R0D_JACK_DET_CTRL:
+ case NAU8821_R0F_INTERRUPT_MASK ... NAU8821_R13_DMIC_CTRL:
+ case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2:
+ case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL:
+ case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4F_FUSE_CTRL3:
+ case NAU8821_R51_FUSE_CTRL1:
+ case NAU8821_R53_OTPDOUT_1 ... NAU8821_R55_MISC_CTRL:
+ case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R66_BIAS_ADJ:
+ case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE:
+ case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS:
+ case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA:
+ case NAU8821_R7E_PGA_GAIN ... NAU8821_R82_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8821_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL:
+ case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8:
+ case NAU8821_R0D_JACK_DET_CTRL:
+ case NAU8821_R0F_INTERRUPT_MASK:
+ case NAU8821_R11_INT_CLR_KEY_STATUS ... NAU8821_R13_DMIC_CTRL:
+ case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2:
+ case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL:
+ case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4C_IMM_MODE_CTRL:
+ case NAU8821_R4E_FUSE_CTRL2 ... NAU8821_R4F_FUSE_CTRL3:
+ case NAU8821_R51_FUSE_CTRL1:
+ case NAU8821_R55_MISC_CTRL:
+ case NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R66_BIAS_ADJ:
+ case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE:
+ case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS:
+ case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA:
+ case NAU8821_R7E_PGA_GAIN ... NAU8821_R80_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8821_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8821_R00_RESET:
+ case NAU8821_R10_IRQ_STATUS ... NAU8821_R11_INT_CLR_KEY_STATUS:
+ case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2A_BIQ0_COF10:
+ case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4A_BIQ1_COF10:
+ case NAU8821_R4D_IMM_RMS_L:
+ case NAU8821_R53_OTPDOUT_1 ... NAU8821_R54_OTPDOUT_2:
+ case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST:
+ case NAU8821_R81_CHARGE_PUMP_INPUT_READ ... NAU8821_R82_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8821_biq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ return regmap_raw_read(component->regmap, NAU8821_R21_BIQ0_COF1,
+ ucontrol->value.bytes.data, params->max);
+}
+
+static int nau8821_biq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+ int ret;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(component->regmap, NAU8821_R21_BIQ0_COF1,
+ data, params->max);
+
+ kfree(data);
+
+ return ret;
+}
+
+static const char * const nau8821_adc_decimation[] = {
+ "32", "64", "128", "256" };
+
+static const struct soc_enum nau8821_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8821_R2B_ADC_RATE, NAU8821_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8821_adc_decimation), nau8821_adc_decimation);
+
+static const char * const nau8821_dac_oversampl[] = {
+ "64", "256", "128", "", "32" };
+
+static const struct soc_enum nau8821_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_SFT,
+ ARRAY_SIZE(nau8821_dac_oversampl), nau8821_dac_oversampl);
+
+static const char * const nau8821_adc_drc_noise_gate[] = {
+ "1:1", "2:1", "4:1", "8:1" };
+
+static const struct soc_enum nau8821_adc_drc_noise_gate_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_NG_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_noise_gate),
+ nau8821_adc_drc_noise_gate);
+
+static const char * const nau8821_adc_drc_expansion_slope[] = {
+ "1:1", "2:1", "4:1" };
+
+static const struct soc_enum nau8821_adc_drc_expansion_slope_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_EXP_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_expansion_slope),
+ nau8821_adc_drc_expansion_slope);
+
+static const char * const nau8821_adc_drc_lower_region[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_adc_drc_lower_region_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_CMP2_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_lower_region),
+ nau8821_adc_drc_lower_region);
+
+static const char * const nau8821_higher_region[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_higher_region_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_CMP1_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_higher_region),
+ nau8821_higher_region);
+
+static const char * const nau8821_limiter_slope[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "1:32", "1:64", "1:1" };
+
+static const struct soc_enum nau8821_limiter_slope_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_LMT_SLP_ADC_SFT, ARRAY_SIZE(nau8821_limiter_slope),
+ nau8821_limiter_slope);
+
+static const char * const nau8821_detection_attack_time[] = {
+ "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+ "", "511Ts" };
+
+static const struct soc_enum nau8821_detection_attack_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+ NAU8821_DRC_PK_COEF1_ADC_SFT,
+ ARRAY_SIZE(nau8821_detection_attack_time),
+ nau8821_detection_attack_time);
+
+static const char * const nau8821_detection_release_time[] = {
+ "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+ "8191Ts", "", "16383Ts" };
+
+static const struct soc_enum nau8821_detection_release_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+ NAU8821_DRC_PK_COEF2_ADC_SFT,
+ ARRAY_SIZE(nau8821_detection_release_time),
+ nau8821_detection_release_time);
+
+static const char * const nau8821_attack_time[] = {
+ "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+ "511Ts", "1023Ts", "2047Ts", "4095Ts", "8191Ts" };
+
+static const struct soc_enum nau8821_attack_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_ATK_ADC_SFT,
+ ARRAY_SIZE(nau8821_attack_time), nau8821_attack_time);
+
+static const char * const nau8821_decay_time[] = {
+ "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+ "8191Ts", "16383Ts", "32757Ts", "65535Ts" };
+
+static const struct soc_enum nau8821_decay_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_DCY_ADC_SFT,
+ ARRAY_SIZE(nau8821_decay_time), nau8821_decay_time);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -6600, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -900, 0);
+static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -6600, 50, 1);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -7000, 2400);
+static const DECLARE_TLV_DB_MINMAX(drc_knee4_tlv, -9800, -3500);
+static const DECLARE_TLV_DB_MINMAX(drc_knee3_tlv, -8100, -1800);
+
+static const struct snd_kcontrol_new nau8821_controls[] = {
+ SOC_DOUBLE_TLV("Mic Volume", NAU8821_R35_ADC_DGAIN_CTRL1,
+ NAU8821_ADCL_CH_VOL_SFT, NAU8821_ADCR_CH_VOL_SFT,
+ 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8821_R30_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8821_R32_HSVOL_CTRL,
+ NAU8821_HPL_VOL_SFT, NAU8821_HPR_VOL_SFT, 0x3, 1, hp_vol_tlv),
+ SOC_DOUBLE_TLV("Digital Playback Volume", NAU8821_R34_DACR_CTRL,
+ NAU8821_DACL_CH_VOL_SFT, NAU8821_DACR_CH_VOL_SFT,
+ 0xcf, 0, playback_vol_tlv),
+ SOC_DOUBLE_TLV("Frontend PGA Volume", NAU8821_R7E_PGA_GAIN,
+ NAU8821_PGA_GAIN_L_SFT, NAU8821_PGA_GAIN_R_SFT,
+ 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume",
+ NAU8821_R2F_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+ SOC_SINGLE_TLV("ADC DRC KNEE4", NAU8821_R37_ADC_DRC_KNEE_IP34,
+ NAU8821_DRC_KNEE4_IP_ADC_SFT, 0x3f, 1, drc_knee4_tlv),
+ SOC_SINGLE_TLV("ADC DRC KNEE3", NAU8821_R37_ADC_DRC_KNEE_IP34,
+ NAU8821_DRC_KNEE3_IP_ADC_SFT, 0x3f, 1, drc_knee3_tlv),
+
+ SOC_ENUM("ADC DRC Noise Gate", nau8821_adc_drc_noise_gate_enum),
+ SOC_ENUM("ADC DRC Expansion Slope", nau8821_adc_drc_expansion_slope_enum),
+ SOC_ENUM("ADC DRC Lower Region", nau8821_adc_drc_lower_region_enum),
+ SOC_ENUM("ADC DRC Higher Region", nau8821_higher_region_enum),
+ SOC_ENUM("ADC DRC Limiter Slope", nau8821_limiter_slope_enum),
+ SOC_ENUM("ADC DRC Peak Detection Attack Time", nau8821_detection_attack_time_enum),
+ SOC_ENUM("ADC DRC Peak Detection Release Time", nau8821_detection_release_time_enum),
+ SOC_ENUM("ADC DRC Attack Time", nau8821_attack_time_enum),
+ SOC_ENUM("ADC DRC Decay Time", nau8821_decay_time_enum),
+ SOC_SINGLE("DRC Enable Switch", NAU8821_R36_ADC_DRC_KNEE_IP12,
+ NAU8821_DRC_ENA_ADC_SFT, 1, 0),
+
+ SOC_ENUM("ADC Decimation Rate", nau8821_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8821_dac_oversampl_enum),
+ SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+ nau8821_biq_coeff_get, nau8821_biq_coeff_put),
+ SOC_SINGLE("ADC Phase Switch", NAU8821_R1B_TDM_CTRL,
+ NAU8821_ADCPHS_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8821_dmic_mode_switch =
+ SOC_DAPM_SINGLE("Switch", NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_EN_SFT, 1, 0);
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ int i, speed_selection = -1, clk_adc_src, clk_adc;
+ unsigned int clk_divider_r03;
+
+ /* The DMIC clock is gotten from adc clock divided by
+ * CLK_DMIC_SRC (1, 2, 4, 8). The clock has to be equal or
+ * less than nau8821->dmic_clk_threshold.
+ */
+ regmap_read(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ &clk_divider_r03);
+ clk_adc_src = (clk_divider_r03 & NAU8821_CLK_ADC_SRC_MASK)
+ >> NAU8821_CLK_ADC_SRC_SFT;
+ clk_adc = (nau8821->fs * 256) >> clk_adc_src;
+
+ for (i = 0 ; i < 4 ; i++)
+ if ((clk_adc >> dmic_speed_sel[i].param) <=
+ nau8821->dmic_clk_threshold) {
+ speed_selection = dmic_speed_sel[i].val;
+ break;
+ }
+ if (i == 4)
+ return -EINVAL;
+
+ dev_dbg(nau8821->dev,
+ "clk_adc=%d, dmic_clk_threshold = %d, param=%d, val = %d\n",
+ clk_adc, nau8821->dmic_clk_threshold,
+ dmic_speed_sel[i].param, dmic_speed_sel[i].val);
+ regmap_update_bits(nau8821->regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SRC_MASK,
+ (speed_selection << NAU8821_DMIC_SRC_SFT));
+
+ return 0;
+}
+
+static int nau8821_left_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(nau8821->adc_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_right_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(nau8821->adc_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(20);
+ regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_JAMNODCLOW, NAU8821_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8821_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_TESTDAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_TESTDAC_EN, NAU8821_BIAS_TESTDAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8821->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8821_is_jack_inserted(nau8821->regmap)) {
+ nau8821_configure_sysclk(nau8821,
+ NAU8821_CLK_INTERNAL, 0);
+ } else {
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+ }
+ }
+ return 0;
+}
+
+static int nau8821_left_fepga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (!nau8821->left_input_single_end)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA,
+ NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK,
+ NAU8821_ACDC_VREF_MICN | NAU8821_FEPGA_MODEL_AAF);
+ regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST,
+ NAU8821_HP_BOOST_DISCHRG_EN, NAU8821_HP_BOOST_DISCHRG_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA,
+ NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, 0);
+ regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST,
+ NAU8821_HP_BOOST_DISCHRG_EN, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+ dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("ADCL Power", NULL, NAU8821_R72_ANALOG_ADC_2,
+ NAU8821_POWERUP_ADCL_SFT, 0),
+ SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2,
+ NAU8821_POWERUP_ADCR_SFT, 0),
+ /* single-ended design only on the left */
+ SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_L_SFT, 0, nau8821_left_fepga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_PGA_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCL_SFT, 0, nau8821_left_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCR Digital path", 0, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_ADCR_SFT, 0, nau8821_right_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("DMIC Enable", SND_SOC_NOPM,
+ 0, 0, &nau8821_dmic_mode_switch),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_TRISTATE_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8821_R73_RDAC,
+ NAU8821_DACL_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8821_R73_RDAC,
+ NAU8821_DACR_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8821_R73_RDAC,
+ NAU8821_DACL_CLK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8821_R73_RDAC,
+ NAU8821_DACR_CLK_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8821_R01_ENA_CTRL,
+ NAU8821_EN_DACL_SFT, 0),
+ SND_SOC_DAPM_PGA_S("HP amp L", 0, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_LDAC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP amp R", 0, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_RDAC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_CHANRGE_PUMP_EN_SFT, 0, nau8821_pump_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_INTEG_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_INTEG_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_DRV_INSTG_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_DRV_INSTG_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_MAIN_DRV_R_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
+ NAU8821_R7F_POWER_UP_CONTROL,
+ NAU8821_PUP_MAIN_DRV_L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACL", 7,
+ NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACL_SFT,
+ 0, nau8821_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 7,
+ NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACR_SFT,
+ 0, nau8821_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+ NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1L_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+ NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R_SFT, 0, NULL, 0),
+
+ /* High current HPOL/R boost driver */
+ SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+ NAU8821_R76_BOOST, NAU8821_HP_BOOST_DIS_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Class G", NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8821_dapm_routes[] = {
+ {"DMIC Enable", "Switch", "DMIC"},
+ {"DMIC Enable", NULL, "DMIC Clock"},
+
+ {"Frontend PGA L", NULL, "MICL"},
+ {"Frontend PGA R", NULL, "MICR"},
+ {"Frontend PGA L", NULL, "MICBIAS"},
+ {"Frontend PGA R", NULL, "MICBIAS"},
+
+ {"ADCL Power", NULL, "Frontend PGA L"},
+ {"ADCR Power", NULL, "Frontend PGA R"},
+
+ {"ADCL Digital path", NULL, "ADCL Power"},
+ {"ADCR Digital path", NULL, "ADCR Power"},
+ {"ADCL Digital path", NULL, "DMIC Enable"},
+ {"ADCR Digital path", NULL, "DMIC Enable"},
+
+ {"AIFTX", NULL, "ADCL Digital path"},
+ {"AIFTX", NULL, "ADCR Digital path"},
+
+ {"AIFTX", NULL, "System Clock"},
+ {"AIFRX", NULL, "System Clock"},
+
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
+
+ {"HP amp L", NULL, "DDACL"},
+ {"HP amp R", NULL, "DDACR"},
+
+ {"Charge Pump", NULL, "HP amp L"},
+ {"Charge Pump", NULL, "HP amp R"},
+
+ {"ADACL", NULL, "Charge Pump"},
+ {"ADACR", NULL, "Charge Pump"},
+ {"ADACL Clock", NULL, "ADACL"},
+ {"ADACR Clock", NULL, "ADACR"},
+
+ {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+ {"Output Driver R Stage 1", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+
+ {"HPOL Pulldown", NULL, "Output DACL"},
+ {"HPOR Pulldown", NULL, "Output DACR"},
+ {"HP Boost Driver", NULL, "HPOL Pulldown"},
+ {"HP Boost Driver", NULL, "HPOR Pulldown"},
+
+ {"Class G", NULL, "HP Boost Driver"},
+ {"HPOL", NULL, "Class G"},
+ {"HPOR", NULL, "Class G"},
+};
+
+static const struct nau8821_osr_attr *
+nau8821_get_osr(struct nau8821 *nau8821, int stream)
+{
+ unsigned int osr;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
+ osr &= NAU8821_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+ return &osr_dac_sel[osr];
+ } else {
+ regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
+ osr &= NAU8821_ADC_SYNC_DOWN_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+ }
+}
+
+static int nau8821_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ const struct nau8821_osr_attr *osr;
+
+ osr = nau8821_get_osr(nau8821, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
+static int nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ctrl_val, bclk_fs, clk_div;
+ const struct nau8821_osr_attr *osr;
+
+ nau8821->fs = params_rate(params);
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ osr = nau8821_get_osr(nau8821, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+ if (nau8821->fs * osr->osr > CLK_DA_AD_MAX)
+ return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8821_CLK_DAC_SRC_SFT);
+ else
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8821_CLK_ADC_SRC_SFT);
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, &ctrl_val);
+ if (ctrl_val & NAU8821_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs;
+ if (bclk_fs <= 32)
+ clk_div = 3;
+ else if (bclk_fs <= 64)
+ clk_div = 2;
+ else if (bclk_fs <= 128)
+ clk_div = 1;
+ else {
+ return -EINVAL;
+ }
+ regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK,
+ (clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8821_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8821_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8821_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8821_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1,
+ NAU8821_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8821_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= NAU8821_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8821_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8821_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8821_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8821_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8821_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8821_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8821_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1,
+ NAU8821_I2S_DL_MASK | NAU8821_I2S_DF_MASK |
+ NAU8821_I2S_BP_MASK | NAU8821_I2S_PCMB_MASK, ctrl1_val);
+ regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8821_digital_mute(struct snd_soc_dai *dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+
+ if (mute)
+ val = NAU8821_DAC_SOFT_MUTE;
+
+ return regmap_update_bits(nau8821->regmap,
+ NAU8821_R31_MUTE_CTRL, NAU8821_DAC_SOFT_MUTE, val);
+}
+
+static const struct snd_soc_dai_ops nau8821_dai_ops = {
+ .startup = nau8821_dai_startup,
+ .hw_params = nau8821_hw_params,
+ .set_fmt = nau8821_set_dai_fmt,
+ .mute_stream = nau8821_digital_mute,
+ .no_capture_mute = 1,
+};
+
+#define NAU8821_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8821_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8821_dai = {
+ .name = NUVOTON_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8821_RATES,
+ .formats = NAU8821_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8821_RATES,
+ .formats = NAU8821_FORMATS,
+ },
+ .ops = &nau8821_dai_ops,
+};
+
+
+static bool nau8821_is_jack_inserted(struct regmap *regmap)
+{
+ bool active_high, is_high;
+ int status, jkdet;
+
+ regmap_read(regmap, NAU8821_R0D_JACK_DET_CTRL, &jkdet);
+ active_high = jkdet & NAU8821_JACK_POLARITY;
+ regmap_read(regmap, NAU8821_R82_GENERAL_STATUS, &status);
+ is_high = status & NAU8821_GPIO2_IN;
+ /* return jack connection status according to jack insertion logic
+ * active high or active low.
+ */
+ return active_high == is_high;
+}
+
+static void nau8821_irq_status_clear(struct regmap *regmap, int active_irq)
+{
+ int clear_irq, i;
+
+ if (active_irq) {
+ regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, active_irq);
+ return;
+ }
+
+ /* Reset the interruption status from rightmost bit if the
+ * corresponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq);
+ for (i = 0; i < NAU8821_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq);
+ }
+}
+
+static void nau8821_eject_jack(struct nau8821 *nau8821)
+{
+ struct snd_soc_dapm_context *dapm = nau8821->dapm;
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, 0);
+ /* HPL/HPR short to ground */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0);
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ /* Disable & mask both insertion & ejection IRQs */
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS,
+ NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_INSERT_EN | NAU8821_IRQ_EJECT_EN,
+ NAU8821_IRQ_INSERT_EN | NAU8821_IRQ_EJECT_EN);
+
+ /* Clear all interruption status */
+ nau8821_irq_status_clear(regmap, 0);
+
+ /* Enable & unmask the insertion IRQ */
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS, 0);
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_INSERT_EN, 0);
+
+ /* Bypass de-bounce circuit */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS, NAU8821_JACK_DET_DB_BYPASS);
+
+ /* Close clock for jack type detection at manual mode */
+ if (snd_soc_dapm_get_bias_level(dapm) < SND_SOC_BIAS_PREPARE)
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+
+ /* Recover to normal channel input */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_R_SRC_EN, 0);
+ if (nau8821->key_enable) {
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN);
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS);
+ }
+}
+
+static void nau8821_jdet_work(struct work_struct *work)
+{
+ struct nau8821 *nau8821 =
+ container_of(work, struct nau8821, jdet_work.work);
+ struct snd_soc_dapm_context *dapm = nau8821->dapm;
+ struct regmap *regmap = nau8821->regmap;
+ int jack_status_reg, mic_detected, event = 0, event_mask = 0;
+
+ regmap_read(regmap, NAU8821_R58_I2C_DEVICE_ID, &jack_status_reg);
+ mic_detected = !(jack_status_reg & NAU8821_KEYDET);
+ if (mic_detected) {
+ dev_dbg(nau8821->dev, "Headset connected\n");
+ event |= SND_JACK_HEADSET;
+
+ /* 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, NAU8821_MICBIAS_JKR2);
+ /* Latch Right Channel Analog data
+ * input into the Right Channel Filter
+ */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_R_SRC_EN, NAU8821_ADC_R_SRC_EN);
+ if (nau8821->key_enable) {
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN, 0);
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS, 0);
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ }
+ } else {
+ dev_dbg(nau8821->dev, "Headphone connected\n");
+ event |= SND_JACK_HEADPHONE;
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ }
+
+ event_mask |= SND_JACK_HEADSET;
+ snd_soc_jack_report(nau8821->jack, event, event_mask);
+}
+
+/* Enable interruptions with internal clock. */
+static void nau8821_setup_inserted_irq(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Disable & mask insertion IRQ */
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS, NAU8821_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_INSERT_EN, NAU8821_IRQ_INSERT_EN);
+
+ /* Clear insert IRQ status */
+ nau8821_irq_status_clear(regmap, NAU8821_JACK_INSERT_DETECTED);
+
+ /* Enable internal VCO needed for interruptions */
+ if (snd_soc_dapm_get_bias_level(nau8821->dapm) < SND_SOC_BIAS_PREPARE)
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_INTERNAL, 0);
+
+ /* Chip needs one FSCLK cycle in order to generate interruptions,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
+ NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_SLAVE);
+
+ /* Do not bypass de-bounce circuit */
+ if (!(nau8821_quirk & NAU8821_QUIRK_JD_DB_BYPASS))
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS, 0);
+
+ /* Unmask & enable the ejection IRQs */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN, 0);
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_EJECT_DIS, 0);
+}
+
+static irqreturn_t nau8821_interrupt(int irq, void *data)
+{
+ struct nau8821 *nau8821 = (struct nau8821 *)data;
+ struct regmap *regmap = nau8821->regmap;
+ int active_irq, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq)) {
+ dev_err(nau8821->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+
+ dev_dbg(nau8821->dev, "IRQ %d\n", active_irq);
+
+ if ((active_irq & NAU8821_JACK_EJECT_IRQ_MASK) ==
+ NAU8821_JACK_EJECT_DETECTED) {
+ cancel_delayed_work_sync(&nau8821->jdet_work);
+ regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
+ NAU8821_MICDET_MASK, NAU8821_MICDET_DIS);
+ nau8821_eject_jack(nau8821);
+ event_mask |= SND_JACK_HEADSET;
+ } else if (active_irq & NAU8821_KEY_SHORT_PRESS_IRQ) {
+ event |= NAU8821_BUTTON;
+ event_mask |= NAU8821_BUTTON;
+ nau8821_irq_status_clear(regmap, NAU8821_KEY_SHORT_PRESS_IRQ);
+ } else if (active_irq & NAU8821_KEY_RELEASE_IRQ) {
+ event_mask = NAU8821_BUTTON;
+ nau8821_irq_status_clear(regmap, NAU8821_KEY_RELEASE_IRQ);
+ } else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) ==
+ NAU8821_JACK_INSERT_DETECTED) {
+ cancel_delayed_work_sync(&nau8821->jdet_work);
+ regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
+ NAU8821_MICDET_MASK, NAU8821_MICDET_EN);
+ if (nau8821_is_jack_inserted(regmap)) {
+ /* Detect microphone and jack type */
+ snd_soc_dapm_force_enable_pin(nau8821->dapm, "MICBIAS");
+ snd_soc_dapm_sync(nau8821->dapm);
+ schedule_delayed_work(&nau8821->jdet_work, msecs_to_jiffies(20));
+ /* Turn off insertion interruption at manual mode */
+ nau8821_setup_inserted_irq(nau8821);
+ } else {
+ dev_warn(nau8821->dev,
+ "Inserted IRQ fired but not connected\n");
+ nau8821_eject_jack(nau8821);
+ }
+ } else {
+ /* Clear the rightmost interrupt */
+ nau8821_irq_status_clear(regmap, active_irq);
+ }
+
+ if (event_mask)
+ snd_soc_jack_report(nau8821->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config nau8821_regmap_config = {
+ .val_bits = NAU8821_REG_DATA_LEN,
+ .reg_bits = NAU8821_REG_ADDR_LEN,
+
+ .max_register = NAU8821_REG_MAX,
+ .readable_reg = nau8821_readable_reg,
+ .writeable_reg = nau8821_writeable_reg,
+ .volatile_reg = nau8821_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8821_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8821_reg_defaults),
+};
+
+static int nau8821_component_probe(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ nau8821->dapm = dapm;
+
+ return 0;
+}
+
+/**
+ * nau8821_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8821_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8821_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by
+ * dividing freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8821_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in >> fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 24-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 24, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 24) & 0x3ff;
+ fll_param->fll_frac = fvco & 0xffffff;
+
+ return 0;
+}
+
+static void nau8821_fll_apply(struct nau8821 *nau8821,
+ struct nau8821_fll *fll_param)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK | NAU8821_CLK_MCLK_SRC_MASK,
+ NAU8821_CLK_SRC_MCLK | fll_param->mclk_src);
+ /* Make DSP operate at high speed for better performance. */
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_FLL_RATIO_MASK | NAU8821_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8821_ICTRL_LATCH_SFT));
+ /* FLL 24-bit fractional input */
+ regmap_write(regmap, NAU8821_R0A_FLL7,
+ (fll_param->fll_frac >> 16) & 0xff);
+ regmap_write(regmap, NAU8821_R0B_FLL8, fll_param->fll_frac & 0xffff);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8821_R07_FLL4,
+ NAU8821_HIGHBW_EN | NAU8821_FLL_REF_DIV_MASK,
+ NAU8821_HIGHBW_EN |
+ (fll_param->clk_ref_div << NAU8821_FLL_REF_DIV_SFT));
+ /* select divided VCO input */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_CLK_SW_MASK, NAU8821_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(regmap,
+ NAU8821_R09_FLL6, NAU8821_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ /* set FLL loop filter enable and cutoff frequency at 500Khz */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_MASK,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500);
+ } else {
+ /* disable FLL loop filter and cutoff frequency */
+ regmap_update_bits(regmap, NAU8821_R08_FLL5,
+ NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN |
+ NAU8821_FLL_FTR_SW_MASK, NAU8821_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN | NAU8821_CUTOFF500, 0);
+ }
+}
+
+/**
+ * nau8821_set_fll - FLL configuration of nau8821
+ * @component: codec component
+ * @pll_id: PLL requested
+ * @source: clock source
+ * @freq_in: frequency of input clock source
+ * @freq_out: must be 256*Fs in order to achieve the best performance
+ *
+ * The FLL function can select BCLK or MCLK as the input clock source.
+ *
+ * Returns 0 if the parameters have been applied successfully
+ * or negative error code.
+ */
+static int nau8821_set_fll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct nau8821_fll fll_set_param, *fll_param = &fll_set_param;
+ int ret, fs;
+
+ fs = freq_out >> 8;
+ ret = nau8821_calc_fll_param(freq_in, fs, fll_param);
+ if (ret) {
+ dev_err(nau8821->dev,
+ "Unsupported input clock %d to output clock %d\n",
+ freq_in, freq_out);
+ return ret;
+ }
+ dev_dbg(nau8821->dev,
+ "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param->mclk_src, fll_param->ratio, fll_param->fll_frac,
+ fll_param->fll_int, fll_param->clk_ref_div);
+
+ nau8821_fll_apply(nau8821, fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static void nau8821_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_DCO_EN, 0);
+ /* Make DSP operate as default setting for power saving. */
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_ICTRL_LATCH_MASK, 0);
+}
+
+static int nau8821_configure_sysclk(struct nau8821 *nau8821,
+ int clk_id, unsigned int freq)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ switch (clk_id) {
+ case NAU8821_CLK_DIS:
+ /* Clock provided externally and disable internal VCO clock */
+ nau8821_configure_mclk_as_sysclk(regmap);
+ break;
+ case NAU8821_CLK_MCLK:
+ nau8821_configure_mclk_as_sysclk(regmap);
+ /* MCLK not changed by clock tree */
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_MCLK_SRC_MASK, 0);
+ break;
+ case NAU8821_CLK_INTERNAL:
+ if (nau8821_is_jack_inserted(regmap)) {
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_DCO_EN, NAU8821_DCO_EN);
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO);
+ /* Decrease the VCO frequency and make DSP operate
+ * as default setting for power saving.
+ */
+ regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER,
+ NAU8821_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(regmap, NAU8821_R04_FLL1,
+ NAU8821_ICTRL_LATCH_MASK |
+ NAU8821_FLL_RATIO_MASK, 0x10);
+ regmap_update_bits(regmap, NAU8821_R09_FLL6,
+ NAU8821_SDM_EN, NAU8821_SDM_EN);
+ }
+ break;
+ case NAU8821_CLK_FLL_MCLK:
+ /* Higher FLL reference input frequency can only set lower
+ * gain error, such as 0000 for input reference from MCLK
+ * 12.288Mhz.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_MCLK | 0);
+ break;
+ case NAU8821_CLK_FLL_BLK:
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_BLK |
+ (0xf << NAU8821_GAIN_ERR_SFT));
+ break;
+ case NAU8821_CLK_FLL_FS:
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8821_R06_FLL3,
+ NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK,
+ NAU8821_FLL_CLK_SRC_FS |
+ (0xf << NAU8821_GAIN_ERR_SFT));
+ break;
+ default:
+ dev_err(nau8821->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ nau8821->clk_id = clk_id;
+ dev_dbg(nau8821->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ nau8821->clk_id);
+
+ return 0;
+}
+
+static int nau8821_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ return nau8821_configure_sysclk(nau8821, clk_id, freq);
+}
+
+static int nau8821_resume_setup(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
+ if (nau8821->irq) {
+ /* Clear all interruption status */
+ nau8821_irq_status_clear(regmap, 0);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN, 0);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_DET_DB_BYPASS,
+ NAU8821_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS, 0);
+ }
+
+ return 0;
+}
+
+static int nau8821_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8821->regmap;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* Setup codec configuration after resume */
+ if (snd_soc_dapm_get_bias_level(nau8821->dapm) == SND_SOC_BIAS_OFF)
+ nau8821_resume_setup(nau8821);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* HPL/HPR short to ground */
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0);
+ if (nau8821->irq) {
+ /* Reset the configuration of jack type for detection.
+ * Detach 2kOhm Resistors from MICBIAS to MICGND1/2.
+ */
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_JKR2, 0);
+ /* Turn off all interruptions before system shutdown.
+ * Keep theinterruption quiet before resume
+ * setup completes.
+ */
+ regmap_write(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff);
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN,
+ NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused nau8821_suspend(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (nau8821->irq)
+ disable_irq(nau8821->irq);
+ snd_soc_dapm_force_bias_level(nau8821->dapm, SND_SOC_BIAS_OFF);
+ /* Power down codec power; don't support button wakeup */
+ snd_soc_dapm_disable_pin(nau8821->dapm, "MICBIAS");
+ snd_soc_dapm_sync(nau8821->dapm);
+ regcache_cache_only(nau8821->regmap, true);
+ regcache_mark_dirty(nau8821->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8821_resume(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(nau8821->regmap, false);
+ regcache_sync(nau8821->regmap);
+ if (nau8821->irq)
+ enable_irq(nau8821->irq);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8821_component_driver = {
+ .probe = nau8821_component_probe,
+ .set_sysclk = nau8821_set_sysclk,
+ .set_pll = nau8821_set_fll,
+ .set_bias_level = nau8821_set_bias_level,
+ .suspend = nau8821_suspend,
+ .resume = nau8821_resume,
+ .controls = nau8821_controls,
+ .num_controls = ARRAY_SIZE(nau8821_controls),
+ .dapm_widgets = nau8821_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8821_dapm_widgets),
+ .dapm_routes = nau8821_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8821_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/**
+ * nau8821_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8821_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ nau8821->jack = jack;
+ /* Initiate jack detection work queue */
+ INIT_DELAYED_WORK(&nau8821->jdet_work, nau8821_jdet_work);
+
+ ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL,
+ nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8821", nau8821);
+ if (ret) {
+ dev_err(nau8821->dev, "Cannot request irq %d (%d)\n",
+ nau8821->irq, ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nau8821_enable_jack_detect);
+
+static void nau8821_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8821_R00_RESET, 0xffff);
+ regmap_write(regmap, NAU8821_R00_RESET, 0xffff);
+}
+
+static void nau8821_print_device_properties(struct nau8821 *nau8821)
+{
+ struct device *dev = nau8821->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8821->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8821->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8821->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8821->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8821->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8821->vref_impedance);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8821->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8821->jack_eject_debounce);
+ dev_dbg(dev, "dmic-clk-threshold: %d\n",
+ nau8821->dmic_clk_threshold);
+ dev_dbg(dev, "key_enable: %d\n", nau8821->key_enable);
+ dev_dbg(dev, "adc-delay-ms: %d\n", nau8821->adc_delay);
+}
+
+static int nau8821_read_device_properties(struct device *dev,
+ struct nau8821 *nau8821)
+{
+ int ret;
+
+ nau8821->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8821->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8821->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ nau8821->key_enable = device_property_read_bool(dev,
+ "nuvoton,key-enable");
+ nau8821->left_input_single_end = device_property_read_bool(dev,
+ "nuvoton,left-input-single-end");
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8821->jkdet_polarity);
+ if (ret)
+ nau8821->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8821->micbias_voltage);
+ if (ret)
+ nau8821->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8821->vref_impedance);
+ if (ret)
+ nau8821->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8821->jack_insert_debounce);
+ if (ret)
+ nau8821->jack_insert_debounce = 7;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8821->jack_eject_debounce);
+ if (ret)
+ nau8821->jack_eject_debounce = 0;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-clk-threshold",
+ &nau8821->dmic_clk_threshold);
+ if (ret)
+ nau8821->dmic_clk_threshold = 3072000;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-slew-rate",
+ &nau8821->dmic_slew_rate);
+ if (ret)
+ nau8821->dmic_slew_rate = 0;
+ ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms",
+ &nau8821->adc_delay);
+ if (ret)
+ nau8821->adc_delay = 125;
+ if (nau8821->adc_delay < 125 || nau8821->adc_delay > 500)
+ dev_warn(dev, "Please set the suitable delay time!\n");
+
+ return 0;
+}
+
+static void nau8821_init_regs(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Enable Bias/Vmid */
+ regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_VMID, NAU8821_BIAS_VMID);
+ regmap_update_bits(regmap, NAU8821_R76_BOOST,
+ NAU8821_GLOBAL_BIAS_EN, NAU8821_GLOBAL_BIAS_EN);
+ /* VMID Tieoff setting and enable TESTDAC.
+ * This sets the analog DAC inputs to a '0' input signal to avoid
+ * any glitches due to power up transients in both the analog and
+ * digital DAC circuit.
+ */
+ regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ,
+ NAU8821_BIAS_VMID_SEL_MASK | NAU8821_BIAS_TESTDAC_EN,
+ (nau8821->vref_impedance << NAU8821_BIAS_VMID_SEL_SFT) |
+ NAU8821_BIAS_TESTDAC_EN);
+ /* Disable short Frame Sync detection logic */
+ regmap_update_bits(regmap, NAU8821_R1E_LEFT_TIME_SLOT,
+ NAU8821_DIS_FS_SHORT_DET, NAU8821_DIS_FS_SHORT_DET);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8821_R76_BOOST,
+ NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS |
+ NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN,
+ NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS |
+ NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8821_R4B_CLASSG_CTRL,
+ NAU8821_CLASSG_TIMER_MASK,
+ 0x20 << NAU8821_CLASSG_TIMER_SFT);
+ /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+ regmap_update_bits(regmap, NAU8821_R6A_ANALOG_CONTROL_2,
+ NAU8821_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB,
+ NAU8821_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8821_R80_CHARGE_PUMP,
+ NAU8821_POWER_DOWN_DACR | NAU8821_POWER_DOWN_DACL, 0);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8821_R73_RDAC,
+ NAU8821_DAC_CLK_DELAY_MASK | NAU8821_DAC_VREF_MASK,
+ (0x2 << NAU8821_DAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8821_DAC_VREF_SFT));
+
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_VOLTAGE_MASK, nau8821->micbias_voltage);
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
+ NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1,
+ NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64);
+ regmap_update_bits(regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SLEW_MASK, nau8821->dmic_slew_rate <<
+ NAU8821_DMIC_SLEW_SFT);
+ if (nau8821->left_input_single_end) {
+ regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE,
+ NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN);
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_LOWNOISE_EN, NAU8821_MICBIAS_LOWNOISE_EN);
+ }
+}
+
+static int nau8821_setup_irq(struct nau8821 *nau8821)
+{
+ struct regmap *regmap = nau8821->regmap;
+
+ /* Jack detection */
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_OUTPUT_EN,
+ nau8821->jkdet_enable ? 0 : NAU8821_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_PULL_EN,
+ nau8821->jkdet_pull_enable ? 0 : NAU8821_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL,
+ NAU8821_JKDET_PULL_UP,
+ nau8821->jkdet_pull_up ? NAU8821_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8821->jkdet_polarity ? 0 : NAU8821_JACK_POLARITY);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_INSERT_DEBOUNCE_MASK,
+ nau8821->jack_insert_debounce <<
+ NAU8821_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
+ NAU8821_JACK_EJECT_DEBOUNCE_MASK,
+ nau8821->jack_eject_debounce <<
+ NAU8821_JACK_EJECT_DEBOUNCE_SFT);
+ /* Pull up IRQ pin */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_PIN_PULL_UP | NAU8821_IRQ_PIN_PULL_EN |
+ NAU8821_IRQ_OUTPUT_EN, NAU8821_IRQ_PIN_PULL_UP |
+ NAU8821_IRQ_PIN_PULL_EN | NAU8821_IRQ_OUTPUT_EN);
+ /* Disable interruption before codec initiation done */
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, 0x3f5, 0x3f5);
+
+ return 0;
+}
+
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id nau8821_quirk_table[] = {
+ {
+ /* Positivo CW14Q01P-V2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P-V2"),
+ },
+ .driver_data = (void *)(NAU8821_QUIRK_JD_ACTIVE_HIGH),
+ },
+ {
+ /* Valve Steam Deck LCD */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ .driver_data = (void *)(NAU8821_QUIRK_JD_DB_BYPASS),
+ },
+ {
+ /* Valve Steam Deck OLED */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ .driver_data = (void *)(NAU8821_QUIRK_JD_DB_BYPASS),
+ },
+ {}
+};
+
+static void nau8821_check_quirks(void)
+{
+ const struct dmi_system_id *dmi_id;
+
+ if (quirk_override != -1) {
+ nau8821_quirk = quirk_override;
+ return;
+ }
+
+ dmi_id = dmi_first_match(nau8821_quirk_table);
+ if (dmi_id)
+ nau8821_quirk = (unsigned long)dmi_id->driver_data;
+}
+
+static int nau8821_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8821) {
+ nau8821 = devm_kzalloc(dev, sizeof(*nau8821), GFP_KERNEL);
+ if (!nau8821)
+ return -ENOMEM;
+ nau8821_read_device_properties(dev, nau8821);
+ }
+ i2c_set_clientdata(i2c, nau8821);
+
+ nau8821->regmap = devm_regmap_init_i2c(i2c, &nau8821_regmap_config);
+ if (IS_ERR(nau8821->regmap))
+ return PTR_ERR(nau8821->regmap);
+
+ nau8821->dev = dev;
+ nau8821->irq = i2c->irq;
+
+ nau8821_check_quirks();
+
+ if (nau8821_quirk & NAU8821_QUIRK_JD_ACTIVE_HIGH)
+ nau8821->jkdet_polarity = 0;
+
+ if (nau8821_quirk & NAU8821_QUIRK_JD_DB_BYPASS)
+ dev_dbg(dev, "Force bypassing jack detection debounce circuit\n");
+
+ nau8821_print_device_properties(nau8821);
+
+ nau8821_reset_chip(nau8821->regmap);
+ ret = regmap_read(nau8821->regmap, NAU8821_R58_I2C_DEVICE_ID, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read device id (%d)\n", ret);
+ return ret;
+ }
+ nau8821_init_regs(nau8821);
+
+ if (i2c->irq)
+ nau8821_setup_irq(nau8821);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &nau8821_component_driver, &nau8821_dai, 1);
+
+ return ret;
+}
+
+static const struct i2c_device_id nau8821_i2c_ids[] = {
+ { "nau8821" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8821_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8821_of_ids[] = {
+ { .compatible = "nuvoton,nau8821", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8821_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8821_acpi_match[] = {
+ { "NVTN2020", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8821_acpi_match);
+#endif
+
+static struct i2c_driver nau8821_driver = {
+ .driver = {
+ .name = "nau8821",
+ .of_match_table = of_match_ptr(nau8821_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8821_acpi_match),
+ },
+ .probe = nau8821_i2c_probe,
+ .id_table = nau8821_i2c_ids,
+};
+module_i2c_driver(nau8821_driver);
+
+MODULE_DESCRIPTION("ASoC nau8821 driver");
+MODULE_AUTHOR("John Hsu <kchsu0@nuvoton.com>");
+MODULE_AUTHOR("Seven Lee <wtli@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
new file mode 100644
index 000000000000..88602923780d
--- /dev/null
+++ b/sound/soc/codecs/nau8821.h
@@ -0,0 +1,586 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L21 ALSA SoC audio driver
+ *
+ * Copyright 2021 Nuvoton Technology Corp.
+ * Author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Lee <wtli@nuvoton.com>
+ */
+
+#ifndef __NAU8821_H__
+#define __NAU8821_H__
+
+#define NAU8821_R00_RESET 0x00
+#define NAU8821_R01_ENA_CTRL 0x01
+#define NAU8821_R03_CLK_DIVIDER 0x03
+#define NAU8821_R04_FLL1 0x04
+#define NAU8821_R05_FLL2 0x05
+#define NAU8821_R06_FLL3 0x06
+#define NAU8821_R07_FLL4 0x07
+#define NAU8821_R08_FLL5 0x08
+#define NAU8821_R09_FLL6 0x09
+#define NAU8821_R0A_FLL7 0x0a
+#define NAU8821_R0B_FLL8 0x0b
+#define NAU8821_R0D_JACK_DET_CTRL 0x0d
+#define NAU8821_R0F_INTERRUPT_MASK 0x0f
+#define NAU8821_R10_IRQ_STATUS 0x10
+#define NAU8821_R11_INT_CLR_KEY_STATUS 0x11
+#define NAU8821_R12_INTERRUPT_DIS_CTRL 0x12
+#define NAU8821_R13_DMIC_CTRL 0x13
+#define NAU8821_R1A_GPIO12_CTRL 0x1a
+#define NAU8821_R1B_TDM_CTRL 0x1b
+#define NAU8821_R1C_I2S_PCM_CTRL1 0x1c
+#define NAU8821_R1D_I2S_PCM_CTRL2 0x1d
+#define NAU8821_R1E_LEFT_TIME_SLOT 0x1e
+#define NAU8821_R1F_RIGHT_TIME_SLOT 0x1f
+#define NAU8821_R21_BIQ0_COF1 0x21
+#define NAU8821_R22_BIQ0_COF2 0x22
+#define NAU8821_R23_BIQ0_COF3 0x23
+#define NAU8821_R24_BIQ0_COF4 0x24
+#define NAU8821_R25_BIQ0_COF5 0x25
+#define NAU8821_R26_BIQ0_COF6 0x26
+#define NAU8821_R27_BIQ0_COF7 0x27
+#define NAU8821_R28_BIQ0_COF8 0x28
+#define NAU8821_R29_BIQ0_COF9 0x29
+#define NAU8821_R2A_BIQ0_COF10 0x2a
+#define NAU8821_R2B_ADC_RATE 0x2b
+#define NAU8821_R2C_DAC_CTRL1 0x2c
+#define NAU8821_R2D_DAC_CTRL2 0x2d
+#define NAU8821_R2F_DAC_DGAIN_CTRL 0x2f
+#define NAU8821_R30_ADC_DGAIN_CTRL 0x30
+#define NAU8821_R31_MUTE_CTRL 0x31
+#define NAU8821_R32_HSVOL_CTRL 0x32
+#define NAU8821_R34_DACR_CTRL 0x34
+#define NAU8821_R35_ADC_DGAIN_CTRL1 0x35
+#define NAU8821_R36_ADC_DRC_KNEE_IP12 0x36
+#define NAU8821_R37_ADC_DRC_KNEE_IP34 0x37
+#define NAU8821_R38_ADC_DRC_SLOPES 0x38
+#define NAU8821_R39_ADC_DRC_ATKDCY 0x39
+#define NAU8821_R3A_DAC_DRC_KNEE_IP12 0x3a
+#define NAU8821_R3B_DAC_DRC_KNEE_IP34 0x3b
+#define NAU8821_R3C_DAC_DRC_SLOPES 0x3c
+#define NAU8821_R3D_DAC_DRC_ATKDCY 0x3d
+#define NAU8821_R41_BIQ1_COF1 0x41
+#define NAU8821_R42_BIQ1_COF2 0x42
+#define NAU8821_R43_BIQ1_COF3 0x43
+#define NAU8821_R44_BIQ1_COF4 0x44
+#define NAU8821_R45_BIQ1_COF5 0x45
+#define NAU8821_R46_BIQ1_COF6 0x46
+#define NAU8821_R47_BIQ1_COF7 0x47
+#define NAU8821_R48_BIQ1_COF8 0x48
+#define NAU8821_R49_BIQ1_COF9 0x49
+#define NAU8821_R4A_BIQ1_COF10 0x4a
+#define NAU8821_R4B_CLASSG_CTRL 0x4b
+#define NAU8821_R4C_IMM_MODE_CTRL 0x4c
+#define NAU8821_R4D_IMM_RMS_L 0x4d
+#define NAU8821_R4E_FUSE_CTRL2 0x4e
+#define NAU8821_R4F_FUSE_CTRL3 0x4f
+#define NAU8821_R51_FUSE_CTRL1 0x51
+#define NAU8821_R53_OTPDOUT_1 0x53
+#define NAU8821_R54_OTPDOUT_2 0x54
+#define NAU8821_R55_MISC_CTRL 0x55
+#define NAU8821_R58_I2C_DEVICE_ID 0x58
+#define NAU8821_R59_SARDOUT_RAM_STATUS 0x59
+#define NAU8821_R5A_SOFTWARE_RST 0x5a
+#define NAU8821_R66_BIAS_ADJ 0x66
+#define NAU8821_R68_TRIM_SETTINGS 0x68
+#define NAU8821_R69_ANALOG_CONTROL_1 0x69
+#define NAU8821_R6A_ANALOG_CONTROL_2 0x6a
+#define NAU8821_R6B_PGA_MUTE 0x6b
+#define NAU8821_R71_ANALOG_ADC_1 0x71
+#define NAU8821_R72_ANALOG_ADC_2 0x72
+#define NAU8821_R73_RDAC 0x73
+#define NAU8821_R74_MIC_BIAS 0x74
+#define NAU8821_R76_BOOST 0x76
+#define NAU8821_R77_FEPGA 0x77
+#define NAU8821_R7E_PGA_GAIN 0x7e
+#define NAU8821_R7F_POWER_UP_CONTROL 0x7f
+#define NAU8821_R80_CHARGE_PUMP 0x80
+#define NAU8821_R81_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8821_R82_GENERAL_STATUS 0x82
+#define NAU8821_REG_MAX NAU8821_R82_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8821_REG_ADDR_LEN 16
+#define NAU8821_REG_DATA_LEN 16
+
+/* ENA_CTRL (0x01) */
+#define NAU8821_CLK_DAC_INV_SFT 14
+#define NAU8821_CLK_DAC_INV (0x1 << NAU8821_CLK_DAC_INV)
+#define NAU8821_EN_DACR_SFT 11
+#define NAU8821_EN_DACR (0x1 << NAU8821_EN_DACR_SFT)
+#define NAU8821_EN_DACL_SFT 10
+#define NAU8821_EN_DACL (0x1 << NAU8821_EN_DACL_SFT)
+#define NAU8821_EN_ADCR_SFT 9
+#define NAU8821_EN_ADCR (0x1 << NAU8821_EN_ADCR_SFT)
+#define NAU8821_EN_ADCL_SFT 8
+#define NAU8821_EN_ADCL (0x1 << NAU8821_EN_ADCL_SFT)
+#define NAU8821_EN_ADC_CLK_SFT 7
+#define NAU8821_EN_ADC_CLK (0x1 << NAU8821_EN_ADC_CLK_SFT)
+#define NAU8821_EN_DAC_CLK_SFT 6
+#define NAU8821_EN_DAC_CLK (0x1 << NAU8821_EN_DAC_CLK_SFT)
+#define NAU8821_EN_I2S_CLK_SFT 4
+#define NAU8821_EN_I2S_CLK (0x1 << NAU8821_EN_I2S_CLK_SFT)
+#define NAU8821_EN_DRC_CLK_SFT 0
+#define NAU8821_EN_DRC_CLK (0x1 << NAU8821_EN_DRC_CLK_SFT)
+
+/* CLK_DIVIDER (0x03) */
+#define NAU8821_CLK_SRC_SFT 15
+#define NAU8821_CLK_SRC_MASK (0x1 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_SRC_VCO (0x1 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_SRC_MCLK (0x0 << NAU8821_CLK_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_SFT 13
+#define NAU8821_CLK_CODEC_SRC_MASK (0x1 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_VCO (0x1 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_CODEC_SRC_MCLK (0x0 << NAU8821_CLK_CODEC_SRC_SFT)
+#define NAU8821_CLK_ADC_SRC_SFT 6
+#define NAU8821_CLK_ADC_SRC_MASK (0x3 << NAU8821_CLK_ADC_SRC_SFT)
+#define NAU8821_CLK_DAC_SRC_SFT 4
+#define NAU8821_CLK_DAC_SRC_MASK (0x3 << NAU8821_CLK_DAC_SRC_SFT)
+#define NAU8821_CLK_MCLK_SRC_MASK 0xf
+
+/* FLL1 (0x04) */
+#define NAU8821_ICTRL_LATCH_SFT 10
+#define NAU8821_ICTRL_LATCH_MASK (0x7 << NAU8821_ICTRL_LATCH_SFT)
+#define NAU8821_FLL_RATIO_MASK 0x7f
+
+/* FLL3 (0x06) */
+#define NAU8821_GAIN_ERR_SFT 12
+#define NAU8821_GAIN_ERR_MASK (0xf << NAU8821_GAIN_ERR_SFT)
+#define NAU8821_FLL_CLK_SRC_SFT 10
+#define NAU8821_FLL_CLK_SRC_MASK (0x3 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_FS (0x3 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_BLK (0x2 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_CLK_SRC_MCLK (0x0 << NAU8821_FLL_CLK_SRC_SFT)
+#define NAU8821_FLL_INTEGER_MASK 0x3ff
+
+/* FLL4 (0x07) */
+#define NAU8821_HIGHBW_EN_SFT 15
+#define NAU8821_HIGHBW_EN (0x1 << NAU8821_HIGHBW_EN_SFT)
+#define NAU8821_FLL_REF_DIV_SFT 10
+#define NAU8821_FLL_REF_DIV_MASK (0x3 << NAU8821_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8821_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8821_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8821_FLL_CLK_SW_SFT 13
+#define NAU8821_FLL_CLK_SW_MASK (0x1 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_CLK_SW_N2 (0x1 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_CLK_SW_REF (0x0 << NAU8821_FLL_CLK_SW_SFT)
+#define NAU8821_FLL_FTR_SW_SFT 12
+#define NAU8821_FLL_FTR_SW_MASK (0x1 << NAU8821_FLL_FTR_SW_SFT)
+#define NAU8821_FLL_FTR_SW_ACCU (0x1 << NAU8821_FLL_FTR_SW_SFT)
+#define NAU8821_FLL_FTR_SW_FILTER (0x0 << NAU8821_FLL_FTR_SW_SFT)
+
+/* FLL6 (0x09) */
+#define NAU8821_DCO_EN (0x1 << 15)
+#define NAU8821_SDM_EN (0x1 << 14)
+#define NAU8821_CUTOFF500 (0x1 << 13)
+
+/* FLL7 (0x0a) */
+#define NAU8821_FLL_FRACH_MASK 0xff
+
+/* FLL8 (0x0b) */
+#define NAU8821_FLL_FRACL_MASK 0xffff
+
+/* JACK_DET_CTRL (0x0d) */
+/* 0 - open, 1 - short to GND */
+#define NAU8821_SPKR_DWN1R_SFT 15
+#define NAU8821_SPKR_DWN1R (0x1 << NAU8821_SPKR_DWN1R_SFT)
+#define NAU8821_SPKR_DWN1L_SFT 14
+#define NAU8821_SPKR_DWN1L (0x1 << NAU8821_SPKR_DWN1L_SFT)
+#define NAU8821_JACK_DET_RESTART (0x1 << 9)
+#define NAU8821_JACK_DET_DB_BYPASS (0x1 << 8)
+#define NAU8821_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8821_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8821_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8821_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8821_JACK_POLARITY (0x1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0x0f) */
+#define NAU8821_IRQ_PIN_PULL_UP (0x1 << 14)
+#define NAU8821_IRQ_PIN_PULL_EN (0x1 << 13)
+#define NAU8821_IRQ_OUTPUT_EN (0x1 << 11)
+#define NAU8821_IRQ_RMS_EN (0x1 << 8)
+#define NAU8821_IRQ_KEY_RELEASE_EN (0x1 << 7)
+#define NAU8821_IRQ_KEY_PRESS_EN (0x1 << 6)
+#define NAU8821_IRQ_MIC_DET_EN (0x1 << 4)
+#define NAU8821_IRQ_EJECT_EN (0x1 << 2)
+#define NAU8821_IRQ_INSERT_EN 0x1
+
+/* IRQ_STATUS (0x10) */
+#define NAU8821_SHORT_CIRCUIT_IRQ (0x1 << 9)
+#define NAU8821_IMPEDANCE_MEAS_IRQ (0x1 << 8)
+#define NAU8821_KEY_IRQ_SFT 6
+#define NAU8821_KEY_IRQ_MASK (0x3 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_KEY_RELEASE_IRQ (0x2 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_KEY_SHORT_PRESS_IRQ (0x1 << NAU8821_KEY_IRQ_SFT)
+#define NAU8821_MIC_DETECT_IRQ (0x1 << 4)
+#define NAU8821_JACK_EJECT_IRQ_MASK (0x3 << 2)
+#define NAU8821_JACK_EJECT_DETECTED (0x1 << 2)
+#define NAU8821_JACK_INSERT_IRQ_MASK 0x3
+#define NAU8821_JACK_INSERT_DETECTED 0x1
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8821_IRQ_KEY_RELEASE_DIS (0x1 << 7)
+#define NAU8821_IRQ_KEY_PRESS_DIS (0x1 << 6)
+#define NAU8821_IRQ_MIC_DIS (0x1 << 4)
+#define NAU8821_IRQ_EJECT_DIS (0x1 << 2)
+#define NAU8821_IRQ_INSERT_DIS 0x1
+
+/* DMIC_CTRL (0x13) */
+#define NAU8821_DMIC_DS_SFT 7
+#define NAU8821_DMIC_DS_MASK (0x1 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_DS_HIGH (0x1 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_DS_LOW (0x0 << NAU8821_DMIC_DS_SFT)
+#define NAU8821_DMIC_SRC_SFT 1
+#define NAU8821_DMIC_SRC_MASK (0x3 << NAU8821_DMIC_SRC_SFT)
+#define NAU8821_CLK_DMIC_SRC (0x2 << NAU8821_DMIC_SRC_SFT)
+#define NAU8821_DMIC_EN_SFT 0
+#define NAU8821_DMIC_SLEW_SFT 8
+#define NAU8821_DMIC_SLEW_MASK (0x7 << NAU8821_DMIC_SLEW_SFT)
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8821_JKDET_PULL_UP (0x1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8821_JKDET_PULL_EN (0x1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8821_JKDET_OUTPUT_EN (0x1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* TDM_CTRL (0x1b) */
+#define NAU8821_TDM_EN_SFT 15
+#define NAU8821_TDM_EN (0x1 << NAU8821_TDM_EN_SFT)
+#define NAU8821_ADCPHS_SFT 13
+#define NAU8821_DACL_CH_SFT 7
+#define NAU8821_DACL_CH_MASK (0x7 << NAU8821_DACL_CH_SFT)
+#define NAU8821_DACR_CH_SFT 4
+#define NAU8821_DACR_CH_MASK (0x7 << NAU8821_DACR_CH_SFT)
+#define NAU8821_ADCL_CH_SFT 2
+#define NAU8821_ADCL_CH_MASK (0x3 << NAU8821_ADCL_CH_SFT)
+#define NAU8821_ADCR_CH_SFT 0
+#define NAU8821_ADCR_CH_MASK 0x3
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8821_I2S_BP_SFT 7
+#define NAU8821_I2S_BP_MASK (0x1 << NAU8821_I2S_BP_SFT)
+#define NAU8821_I2S_BP_INV (0x1 << NAU8821_I2S_BP_SFT)
+#define NAU8821_I2S_PCMB_SFT 6
+#define NAU8821_I2S_PCMB_MASK (0x1 << NAU8821_I2S_PCMB_SFT)
+#define NAU8821_I2S_PCMB_EN (0x1 << NAU8821_I2S_PCMB_SFT)
+#define NAU8821_I2S_DL_SFT 2
+#define NAU8821_I2S_DL_MASK (0x3 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_32 (0x3 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_24 (0x2 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_20 (0x1 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DL_16 (0x0 << NAU8821_I2S_DL_SFT)
+#define NAU8821_I2S_DF_MASK 0x3
+#define NAU8821_I2S_DF_PCM_AB 0x3
+#define NAU8821_I2S_DF_I2S 0x2
+#define NAU8821_I2S_DF_LEFT 0x1
+#define NAU8821_I2S_DF_RIGTH 0x0
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8821_I2S_TRISTATE_SFT 15
+#define NAU8821_I2S_TRISTATE (0x1 << NAU8821_I2S_TRISTATE_SFT)
+#define NAU8821_I2S_LRC_DIV_SFT 12
+#define NAU8821_I2S_LRC_DIV_MASK (0x3 << NAU8821_I2S_LRC_DIV_SFT)
+#define NAU8821_I2S_MS_SFT 3
+#define NAU8821_I2S_MS_MASK (0x1 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_MS_MASTER (0x1 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_MS_SLAVE (0x0 << NAU8821_I2S_MS_SFT)
+#define NAU8821_I2S_BLK_DIV_MASK 0x7
+
+/* LEFT_TIME_SLOT (0x1e) */
+#define NAU8821_TSLOT_L_OFFSET_MASK 0x3ff
+#define NAU8821_DIS_FS_SHORT_DET (0x1 << 13)
+
+/* RIGHT_TIME_SLOT (0x1f) */
+#define NAU8821_TSLOT_R_OFFSET_MASK 0x3ff
+
+/* BIQ0_COF10 (0x2a) */
+#define NAU8821_BIQ0_ADC_EN_SFT 3
+#define NAU8821_BIQ0_ADC_EN_EN (0x1 << NAU8821_BIQ0_ADC_EN_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8821_ADC_SYNC_DOWN_SFT 0
+#define NAU8821_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8821_ADC_SYNC_DOWN_256 0x3
+#define NAU8821_ADC_SYNC_DOWN_128 0x2
+#define NAU8821_ADC_SYNC_DOWN_64 0x1
+#define NAU8821_ADC_SYNC_DOWN_32 0x0
+#define NAU8821_ADC_L_SRC_SFT 15
+#define NAU8821_ADC_L_SRC_EN (0x1 << NAU8821_ADC_L_SRC_SFT)
+#define NAU8821_ADC_R_SRC_SFT 14
+#define NAU8821_ADC_R_SRC_EN (0x1 << NAU8821_ADC_R_SRC_SFT)
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8821_DAC_OVERSAMPLE_SFT 0
+#define NAU8821_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8821_DAC_OVERSAMPLE_32 0x4
+#define NAU8821_DAC_OVERSAMPLE_128 0x2
+#define NAU8821_DAC_OVERSAMPLE_256 0x1
+#define NAU8821_DAC_OVERSAMPLE_64 0x0
+
+/* DAC_DGAIN_CTRL (0x2f) */
+#define NAU8821_DAC1_TO_DAC0_ST_SFT 8
+#define NAU8821_DAC1_TO_DAC0_ST_MASK (0xff << NAU8821_DAC1_TO_DAC0_ST_SFT)
+#define NAU8821_DAC0_TO_DAC1_ST_SFT 0
+#define NAU8821_DAC0_TO_DAC1_ST_MASK 0xff
+
+/* MUTE_CTRL (0x31) */
+#define NAU8821_DAC_ZC_EN (0x1 << 12)
+#define NAU8821_DAC_SOFT_MUTE (0x1 << 9)
+#define NAU8821_ADC_ZC_EN (0x1 << 2)
+#define NAU8821_ADC_SOFT_MUTE (0x1 << 1)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8821_HP_MUTE (0x1 << 15)
+#define NAU8821_HP_MUTE_AUTO (0x1 << 14)
+#define NAU8821_HPL_MUTE (0x1 << 13)
+#define NAU8821_HPR_MUTE (0x1 << 12)
+#define NAU8821_HPL_VOL_SFT 4
+#define NAU8821_HPL_VOL_MASK (0x3 << NAU8821_HPL_VOL_SFT)
+#define NAU8821_HPR_VOL_SFT 0
+#define NAU8821_HPR_VOL_MASK (0x3 << NAU8821_HPR_VOL_SFT)
+
+/* DACR_CTRL (0x34) */
+#define NAU8821_DACR_CH_VOL_SFT 8
+#define NAU8821_DACR_CH_VOL_MASK (0xff << NAU8821_DACR_CH_VOL_SFT)
+#define NAU8821_DACL_CH_VOL_SFT 0
+#define NAU8821_DACL_CH_VOL_MASK 0xff
+
+/* ADC_DGAIN_CTRL1 (0x35) */
+#define NAU8821_ADCR_CH_VOL_SFT 8
+#define NAU8821_ADCR_CH_VOL_MASK (0xff << NAU8821_ADCR_CH_VOL_SFT)
+#define NAU8821_ADCL_CH_VOL_SFT 0
+#define NAU8821_ADCL_CH_VOL_MASK 0xff
+
+/* ADC_DRC_KNEE_IP12 (0x36) */
+#define NAU8821_DRC_ENA_ADC_SFT 15
+#define NAU8821_DRC_ENA_ADC_EN (0x1 << NAU8821_DRC_ENA_ADC_SFT)
+
+/* ADC_DRC_KNEE_IP34 (0x37) */
+#define NAU8821_DRC_KNEE4_IP_ADC_SFT 8
+#define NAU8821_DRC_KNEE4_IP_ADC_MASK (0xff << NAU8821_DRC_KNEE4_IP_ADC_SFT)
+#define NAU8821_DRC_KNEE3_IP_ADC_SFT 0
+#define NAU8821_DRC_KNEE3_IP_ADC_MASK 0xff
+
+/* ADC_DRC_SLOPES (0x38) */
+#define NAU8821_DRC_NG_SLP_ADC_SFT 12
+#define NAU8821_DRC_EXP_SLP_ADC_SFT 9
+#define NAU8821_DRC_CMP2_SLP_ADC_SFT 6
+#define NAU8821_DRC_CMP1_SLP_ADC_SFT 3
+#define NAU8821_DRC_LMT_SLP_ADC_SFT 0
+
+/* ADC_DRC_ATKDCY (0x39) */
+#define NAU8821_DRC_PK_COEF1_ADC_SFT 12
+#define NAU8821_DRC_PK_COEF2_ADC_SFT 8
+#define NAU8821_DRC_ATK_ADC_SFT 4
+#define NAU8821_DRC_DCY_ADC_SFT 0
+
+/* BIQ1_COF10 (0x4a) */
+#define NAU8821_BIQ1_DAC_EN_SFT 3
+#define NAU8821_BIQ1_DAC_EN_EN (0x1 << NAU8821_BIQ1_DAC_EN_SFT)
+
+/* CLASSG_CTRL (0x4b) */
+#define NAU8821_CLASSG_TIMER_SFT 8
+#define NAU8821_CLASSG_TIMER_MASK (0x3f << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_64MS (0x20 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_32MS (0x10 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_16MS (0x8 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_8MS (0x4 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_2MS (0x2 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_TIMER_1MS (0x1 << NAU8821_CLASSG_TIMER_SFT)
+#define NAU8821_CLASSG_RDAC_EN_SFT 2
+#define NAU8821_CLASSG_RDAC_EN (0x1 << NAU8821_CLASSG_RDAC_EN_SFT)
+#define NAU8821_CLASSG_LDAC_EN_SFT 1
+#define NAU8821_CLASSG_LDAC_EN (0x1 << NAU8821_CLASSG_LDAC_EN_SFT)
+#define NAU8821_CLASSG_EN_SFT 0
+#define NAU8821_CLASSG_EN 0x1
+
+/* IMM_MODE_CTRL (0x4c) */
+#define NAU8821_IMM_THD_SFT 8
+#define NAU8821_IMM_THD_MASK (0x3f << NAU8821_IMM_THD_SFT)
+#define NAU8821_IMM_GEN_VOL_SFT 6
+#define NAU8821_IMM_GEN_VOL_MASK (0x3 << NAU8821_IMM_GEN_VOL_SFT)
+#define NAU8821_IMM_CYC_SFT 4
+#define NAU8821_IMM_CYC_MASK (0x3 << NAU8821_IMM_CYC_SFT)
+#define NAU8821_IMM_EN (0x1 << 3)
+#define NAU8821_IMM_DAC_SRC_MASK 0x3
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8821_KEYDET (0x1 << 7)
+#define NAU8821_MICDET (0x1 << 6)
+#define NAU8821_SOFTWARE_ID_MASK 0x3
+
+/* BIAS_ADJ (0x66) */
+#define NAU8821_BIAS_HP_IMP (0x1 << 15)
+#define NAU8821_BIAS_TESTDAC_SFT 8
+#define NAU8821_BIAS_TESTDAC_EN (0x3 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_TESTDACR_EN (0x2 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_TESTDACL_EN (0x1 << NAU8821_BIAS_TESTDAC_SFT)
+#define NAU8821_BIAS_VMID (0x1 << 6)
+#define NAU8821_BIAS_VMID_SEL_SFT 4
+#define NAU8821_BIAS_VMID_SEL_MASK (0x3 << NAU8821_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8821_JD_POL_SFT 2
+#define NAU8821_JD_POL_MASK (0x1 << NAU8821_JD_POL_SFT)
+#define NAU8821_JD_POL_INV (0x1 << NAU8821_JD_POL_SFT)
+#define NAU8821_JD_OUT_POL_SFT 1
+#define NAU8821_JD_OUT_POL_MASK (0x1 << NAU8821_JD_OUT_POL_SFT)
+#define NAU8821_JD_OUT_POL_INV (0x1 << NAU8821_JD_OUT_POL_SFT)
+#define NAU8821_JD_EN_SFT 0
+#define NAU8821_JD_EN 0x1
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8821_HP_NON_CLASSG_CURRENT_2xADJ (0x1 << 12)
+#define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1)
+#define NAU8821_DAC_CAPACITOR_LSB 0x1
+
+/* MUTE_MIC_L_N (0x6b) */
+#define NAU8821_MUTE_MICNL_SFT 5
+#define NAU8821_MUTE_MICNL_EN (0x1 << NAU8821_MUTE_MICNL_SFT)
+#define NAU8821_MUTE_MICNR_SFT 4
+#define NAU8821_MUTE_MICNR_EN (0x1 << NAU8821_MUTE_MICNR_SFT)
+#define NAU8821_MUTE_MICRP_SFT 2
+#define NAU8821_MUTE_MICRP_EN (0x1 << NAU8821_MUTE_MICRP_SFT)
+
+/* ANALOG_ADC_1 (0x71) */
+#define NAU8821_MICDET_EN_SFT 0
+#define NAU8821_MICDET_MASK 0x1
+#define NAU8821_MICDET_DIS 0x1
+#define NAU8821_MICDET_EN 0x0
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8821_ADC_VREFSEL_SFT 8
+#define NAU8821_ADC_VREFSEL_MASK (0x3 << NAU8821_ADC_VREFSEL_SFT)
+#define NAU8821_POWERUP_ADCL_SFT 6
+#define NAU8821_POWERUP_ADCL (0x1 << NAU8821_POWERUP_ADCL_SFT)
+#define NAU8821_POWERUP_ADCR_SFT 4
+#define NAU8821_POWERUP_ADCR (0x1 << NAU8821_POWERUP_ADCR_SFT)
+
+/* RDAC (0x73) */
+#define NAU8821_DACR_EN_SFT 13
+#define NAU8821_DACR_EN (0x3 << NAU8821_DACR_EN_SFT)
+#define NAU8821_DACL_EN_SFT 12
+#define NAU8821_DACL_EN (0x3 << NAU8821_DACL_EN_SFT)
+#define NAU8821_DACR_CLK_EN_SFT 9
+#define NAU8821_DACR_CLK_EN (0x3 << NAU8821_DACR_CLK_EN_SFT)
+#define NAU8821_DACL_CLK_EN_SFT 8
+#define NAU8821_DACL_CLK_EN (0x3 << NAU8821_DACL_CLK_EN_SFT)
+#define NAU8821_DAC_CLK_DELAY_SFT 4
+#define NAU8821_DAC_CLK_DELAY_MASK (0x7 << NAU8821_DAC_CLK_DELAY_SFT)
+#define NAU8821_DAC_VREF_SFT 2
+#define NAU8821_DAC_VREF_MASK (0x3 << NAU8821_DAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8821_MICBIAS_JKR2 (0x1 << 12)
+#define NAU8821_MICBIAS_LOWNOISE_SFT 10
+#define NAU8821_MICBIAS_LOWNOISE_EN (0x1 << NAU8821_MICBIAS_LOWNOISE_SFT)
+#define NAU8821_MICBIAS_POWERUP_SFT 8
+#define NAU8821_MICBIAS_POWERUP_EN (0x1 << NAU8821_MICBIAS_POWERUP_SFT)
+#define NAU8821_MICBIAS_VOLTAGE_SFT 0
+#define NAU8821_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8821_PRECHARGE_DIS (0x1 << 13)
+#define NAU8821_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8821_HP_BOOST_DISCHRG_SFT 11
+#define NAU8821_HP_BOOST_DISCHRG_EN (0x1 << NAU8821_HP_BOOST_DISCHRG_SFT)
+#define NAU8821_HP_BOOST_DIS_SFT 9
+#define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT)
+#define NAU8821_HP_BOOST_G_DIS (0x1 << 8)
+#define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8821_ACDC_CTRL_SFT 14
+#define NAU8821_ACDC_CTRL_MASK (0x3 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_ACDC_VREF_MICP (0x1 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_ACDC_VREF_MICN (0x2 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_FEPGA_MODEL_SFT 4
+#define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_AAF (0x1 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_DIS (0x2 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_IMP12K (0x8 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODER_SFT 0
+#define NAU8821_FEPGA_MODER_MASK 0xf
+#define NAU8821_FEPGA_MODER_AAF 0x1
+#define NAU8821_FEPGA_MODER_DIS 0x2
+#define NAU8821_FEPGA_MODER_IMP12K 0x8
+
+
+/* PGA_GAIN (0x7e) */
+#define NAU8821_PGA_GAIN_L_SFT 8
+#define NAU8821_PGA_GAIN_L_MASK (0x3f << NAU8821_PGA_GAIN_L_SFT)
+#define NAU8821_PGA_GAIN_R_SFT 0
+#define NAU8821_PGA_GAIN_R_MASK 0x3f
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8821_PUP_PGA_L_SFT 15
+#define NAU8821_PUP_PGA_L (0x1 << NAU8821_PUP_PGA_L_SFT)
+#define NAU8821_PUP_PGA_R_SFT 14
+#define NAU8821_PUP_PGA_R (0x1 << NAU8821_PUP_PGA_R_SFT)
+#define NAU8821_PUP_INTEG_R_SFT 5
+#define NAU8821_PUP_INTEG_R (0x1 << NAU8821_PUP_INTEG_R_SFT)
+#define NAU8821_PUP_INTEG_L_SFT 4
+#define NAU8821_PUP_INTEG_L (0x1 << NAU8821_PUP_INTEG_L_SFT)
+#define NAU8821_PUP_DRV_INSTG_R_SFT 3
+#define NAU8821_PUP_DRV_INSTG_R (0x1 << NAU8821_PUP_DRV_INSTG_R_SFT)
+#define NAU8821_PUP_DRV_INSTG_L_SFT 2
+#define NAU8821_PUP_DRV_INSTG_L (0x1 << NAU8821_PUP_DRV_INSTG_L_SFT)
+#define NAU8821_PUP_MAIN_DRV_R_SFT 1
+#define NAU8821_PUP_MAIN_DRV_R (0x1 << NAU8821_PUP_MAIN_DRV_R_SFT)
+#define NAU8821_PUP_MAIN_DRV_L_SFT 0
+#define NAU8821_PUP_MAIN_DRV_L 0x1
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8821_JAMNODCLOW (0x1 << 10)
+#define NAU8821_POWER_DOWN_DACR_SFT 9
+#define NAU8821_POWER_DOWN_DACR (0x1 << NAU8821_POWER_DOWN_DACR_SFT)
+#define NAU8821_POWER_DOWN_DACL_SFT 8
+#define NAU8821_POWER_DOWN_DACL (0x1 << NAU8821_POWER_DOWN_DACL_SFT)
+#define NAU8821_CHANRGE_PUMP_EN_SFT 5
+#define NAU8821_CHANRGE_PUMP_EN (0x1 << NAU8821_CHANRGE_PUMP_EN_SFT)
+
+/* GENERAL_STATUS (0x82) */
+#define NAU8821_GPIO2_IN_SFT 1
+#define NAU8821_GPIO2_IN (0x1 << NAU8821_GPIO2_IN_SFT)
+
+#define NUVOTON_CODEC_DAI "nau8821-hifi"
+
+/* System Clock Source */
+enum {
+ NAU8821_CLK_DIS,
+ NAU8821_CLK_MCLK,
+ NAU8821_CLK_INTERNAL,
+ NAU8821_CLK_FLL_MCLK,
+ NAU8821_CLK_FLL_BLK,
+ NAU8821_CLK_FLL_FS,
+};
+
+struct nau8821 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct delayed_work jdet_work;
+ int irq;
+ int clk_id;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ bool left_input_single_end;
+ int jkdet_polarity;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+ int fs;
+ int dmic_clk_threshold;
+ int dmic_slew_rate;
+ int key_enable;
+ int adc_delay;
+};
+
+int nau8821_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+
+#endif /* __NAU8821_H__ */
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
new file mode 100644
index 000000000000..a11759f85eac
--- /dev/null
+++ b/sound/soc/codecs/nau8822.c
@@ -0,0 +1,1221 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// nau8822.c -- NAU8822 ALSA Soc Audio driver
+//
+// Copyright 2017 Nuvoton Technology Crop.
+//
+// Author: David Lin <ctlin0@nuvoton.com>
+// Co-author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Li <wtli@nuvoton.com>
+//
+// Based on WM8974.c
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+#include "nau8822.h"
+
+#define NAU_PLL_FREQ_MAX 100000000
+#define NAU_PLL_FREQ_MIN 90000000
+#define NAU_PLL_REF_MAX 33000000
+#define NAU_PLL_REF_MIN 8000000
+#define NAU_PLL_OPTOP_MIN 6
+
+static const int nau8822_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 };
+
+static const struct reg_default nau8822_reg_defaults[] = {
+ { NAU8822_REG_POWER_MANAGEMENT_1, 0x0000 },
+ { NAU8822_REG_POWER_MANAGEMENT_2, 0x0000 },
+ { NAU8822_REG_POWER_MANAGEMENT_3, 0x0000 },
+ { NAU8822_REG_AUDIO_INTERFACE, 0x0050 },
+ { NAU8822_REG_COMPANDING_CONTROL, 0x0000 },
+ { NAU8822_REG_CLOCKING, 0x0140 },
+ { NAU8822_REG_ADDITIONAL_CONTROL, 0x0000 },
+ { NAU8822_REG_GPIO_CONTROL, 0x0000 },
+ { NAU8822_REG_JACK_DETECT_CONTROL_1, 0x0000 },
+ { NAU8822_REG_DAC_CONTROL, 0x0000 },
+ { NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_JACK_DETECT_CONTROL_2, 0x0000 },
+ { NAU8822_REG_ADC_CONTROL, 0x0100 },
+ { NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0x00ff },
+ { NAU8822_REG_EQ1, 0x012c },
+ { NAU8822_REG_EQ2, 0x002c },
+ { NAU8822_REG_EQ3, 0x002c },
+ { NAU8822_REG_EQ4, 0x002c },
+ { NAU8822_REG_EQ5, 0x002c },
+ { NAU8822_REG_DAC_LIMITER_1, 0x0032 },
+ { NAU8822_REG_DAC_LIMITER_2, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_1, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_2, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_3, 0x0000 },
+ { NAU8822_REG_NOTCH_FILTER_4, 0x0000 },
+ { NAU8822_REG_ALC_CONTROL_1, 0x0038 },
+ { NAU8822_REG_ALC_CONTROL_2, 0x000b },
+ { NAU8822_REG_ALC_CONTROL_3, 0x0032 },
+ { NAU8822_REG_NOISE_GATE, 0x0010 },
+ { NAU8822_REG_PLL_N, 0x0008 },
+ { NAU8822_REG_PLL_K1, 0x000c },
+ { NAU8822_REG_PLL_K2, 0x0093 },
+ { NAU8822_REG_PLL_K3, 0x00e9 },
+ { NAU8822_REG_3D_CONTROL, 0x0000 },
+ { NAU8822_REG_RIGHT_SPEAKER_CONTROL, 0x0000 },
+ { NAU8822_REG_INPUT_CONTROL, 0x0033 },
+ { NAU8822_REG_LEFT_INP_PGA_CONTROL, 0x0010 },
+ { NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0x0010 },
+ { NAU8822_REG_LEFT_ADC_BOOST_CONTROL, 0x0100 },
+ { NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0x0100 },
+ { NAU8822_REG_OUTPUT_CONTROL, 0x0002 },
+ { NAU8822_REG_LEFT_MIXER_CONTROL, 0x0001 },
+ { NAU8822_REG_RIGHT_MIXER_CONTROL, 0x0001 },
+ { NAU8822_REG_LHP_VOLUME, 0x0039 },
+ { NAU8822_REG_RHP_VOLUME, 0x0039 },
+ { NAU8822_REG_LSPKOUT_VOLUME, 0x0039 },
+ { NAU8822_REG_RSPKOUT_VOLUME, 0x0039 },
+ { NAU8822_REG_AUX2_MIXER, 0x0001 },
+ { NAU8822_REG_AUX1_MIXER, 0x0001 },
+ { NAU8822_REG_POWER_MANAGEMENT_4, 0x0000 },
+ { NAU8822_REG_LEFT_TIME_SLOT, 0x0000 },
+ { NAU8822_REG_MISC, 0x0020 },
+ { NAU8822_REG_RIGHT_TIME_SLOT, 0x0000 },
+ { NAU8822_REG_DEVICE_REVISION, 0x007f },
+ { NAU8822_REG_DEVICE_ID, 0x001a },
+ { NAU8822_REG_DAC_DITHER, 0x0114 },
+ { NAU8822_REG_ALC_ENHANCE_1, 0x0000 },
+ { NAU8822_REG_ALC_ENHANCE_2, 0x0000 },
+ { NAU8822_REG_192KHZ_SAMPLING, 0x0008 },
+ { NAU8822_REG_MISC_CONTROL, 0x0000 },
+ { NAU8822_REG_INPUT_TIEOFF, 0x0000 },
+ { NAU8822_REG_POWER_REDUCTION, 0x0000 },
+ { NAU8822_REG_AGC_PEAK2PEAK, 0x0000 },
+ { NAU8822_REG_AGC_PEAK_DETECT, 0x0000 },
+ { NAU8822_REG_AUTOMUTE_CONTROL, 0x0000 },
+ { NAU8822_REG_OUTPUT_TIEOFF, 0x0000 },
+};
+
+static bool nau8822_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1:
+ case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5:
+ case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2:
+ case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4:
+ case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3:
+ case NAU8822_REG_3D_CONTROL:
+ case NAU8822_REG_RIGHT_SPEAKER_CONTROL:
+ case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL:
+ case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER:
+ case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID:
+ case NAU8822_REG_DAC_DITHER:
+ case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL:
+ case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8822_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1:
+ case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME:
+ case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5:
+ case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2:
+ case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4:
+ case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3:
+ case NAU8822_REG_3D_CONTROL:
+ case NAU8822_REG_RIGHT_SPEAKER_CONTROL:
+ case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL:
+ case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER:
+ case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID:
+ case NAU8822_REG_DAC_DITHER:
+ case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL:
+ case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8822_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8822_REG_RESET:
+ case NAU8822_REG_DEVICE_REVISION:
+ case NAU8822_REG_DEVICE_ID:
+ case NAU8822_REG_AGC_PEAK2PEAK:
+ case NAU8822_REG_AGC_PEAK_DETECT:
+ case NAU8822_REG_AUTOMUTE_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* The EQ parameters get function is to get the 5 band equalizer control.
+ * The regmap raw read can't work here because regmap doesn't provide
+ * value format for value width of 9 bits. Therefore, the driver reads data
+ * from cache and makes value format according to the endianness of
+ * bytes type control element.
+ */
+static int nau8822_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, reg;
+ u16 reg_val, *val;
+ __be16 tmp;
+
+ val = (u16 *)ucontrol->value.bytes.data;
+ reg = NAU8822_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ reg_val = snd_soc_component_read(component, reg + i);
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ tmp = cpu_to_be16(reg_val);
+ memcpy(val + i, &tmp, sizeof(tmp));
+ }
+
+ return 0;
+}
+
+/* The EQ parameters put function is to make configuration of 5 band equalizer
+ * control. These configuration includes central frequency, equalizer gain,
+ * cut-off frequency, bandwidth control, and equalizer path.
+ * The regmap raw write can't work here because regmap doesn't provide
+ * register and value format for register with address 7 bits and value 9 bits.
+ * Therefore, the driver makes value format according to the endianness of
+ * bytes type control element and writes data to codec.
+ */
+static int nau8822_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+ u16 *val, value;
+ int i, reg, ret;
+ __be16 *tmp;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ val = (u16 *)data;
+ reg = NAU8822_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ tmp = (__be16 *)(val + i);
+ value = be16_to_cpup(tmp);
+ ret = snd_soc_component_write(component, reg + i, value);
+ if (ret) {
+ dev_err(component->dev,
+ "EQ configuration fail, register: %x ret: %d\n",
+ reg + i, ret);
+ kfree(data);
+ return ret;
+ }
+ }
+ kfree(data);
+
+ return 0;
+}
+
+static const char * const nau8822_companding[] = {
+ "Off", "NC", "u-law", "A-law"};
+
+static const struct soc_enum nau8822_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_ADCCM_SFT,
+ ARRAY_SIZE(nau8822_companding), nau8822_companding);
+
+static const struct soc_enum nau8822_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_DACCM_SFT,
+ ARRAY_SIZE(nau8822_companding), nau8822_companding);
+
+static const char * const nau8822_eqmode[] = {"Capture", "Playback"};
+
+static const struct soc_enum nau8822_eqmode_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_EQ1, NAU8822_EQM_SFT,
+ ARRAY_SIZE(nau8822_eqmode), nau8822_eqmode);
+
+static const char * const nau8822_alc1[] = {"Off", "Right", "Left", "Both"};
+static const char * const nau8822_alc3[] = {"Normal", "Limiter"};
+
+static const struct soc_enum nau8822_alc_enable_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_1, NAU8822_ALCEN_SFT,
+ ARRAY_SIZE(nau8822_alc1), nau8822_alc1);
+
+static const struct soc_enum nau8822_alc_mode_enum =
+ SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_3, NAU8822_ALCM_SFT,
+ ARRAY_SIZE(nau8822_alc3), nau8822_alc3);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
+
+static const struct snd_kcontrol_new nau8822_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8822_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8822_companding_dac_enum),
+
+ SOC_ENUM("EQ Function", nau8822_eqmode_enum),
+ SND_SOC_BYTES_EXT("EQ Parameters", 10,
+ nau8822_eq_get, nau8822_eq_put),
+
+ SOC_DOUBLE("DAC Inversion Switch",
+ NAU8822_REG_DAC_CONTROL, 0, 1, 1, 0),
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch",
+ NAU8822_REG_ADC_CONTROL, 8, 1, 0),
+ SOC_SINGLE("High Pass Cut Off",
+ NAU8822_REG_ADC_CONTROL, 4, 7, 0),
+
+ SOC_DOUBLE("ADC Inversion Switch",
+ NAU8822_REG_ADC_CONTROL, 0, 1, 1, 0),
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv),
+
+ SOC_SINGLE("DAC Limiter Switch",
+ NAU8822_REG_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Limiter Decay",
+ NAU8822_REG_DAC_LIMITER_1, 4, 15, 0),
+ SOC_SINGLE("DAC Limiter Attack",
+ NAU8822_REG_DAC_LIMITER_1, 0, 15, 0),
+ SOC_SINGLE("DAC Limiter Threshold",
+ NAU8822_REG_DAC_LIMITER_2, 4, 7, 0),
+ SOC_SINGLE_TLV("DAC Limiter Volume",
+ NAU8822_REG_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
+
+ SOC_ENUM("ALC Mode", nau8822_alc_mode_enum),
+ SOC_ENUM("ALC Enable Switch", nau8822_alc_enable_enum),
+ SOC_SINGLE("ALC Min Gain",
+ NAU8822_REG_ALC_CONTROL_1, 0, 7, 0),
+ SOC_SINGLE("ALC Max Gain",
+ NAU8822_REG_ALC_CONTROL_1, 3, 7, 0),
+ SOC_SINGLE("ALC Hold",
+ NAU8822_REG_ALC_CONTROL_2, 4, 10, 0),
+ SOC_SINGLE("ALC Target",
+ NAU8822_REG_ALC_CONTROL_2, 0, 15, 0),
+ SOC_SINGLE("ALC Decay",
+ NAU8822_REG_ALC_CONTROL_3, 4, 10, 0),
+ SOC_SINGLE("ALC Attack",
+ NAU8822_REG_ALC_CONTROL_3, 0, 10, 0),
+ SOC_SINGLE("ALC Noise Gate Switch",
+ NAU8822_REG_NOISE_GATE, 3, 1, 0),
+ SOC_SINGLE("ALC Noise Gate Threshold",
+ NAU8822_REG_NOISE_GATE, 0, 7, 0),
+
+ SOC_DOUBLE_R("PGA ZC Switch",
+ NAU8822_REG_LEFT_INP_PGA_CONTROL,
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL,
+ 7, 1, 0),
+ SOC_DOUBLE_R_TLV("PGA Volume",
+ NAU8822_REG_LEFT_INP_PGA_CONTROL,
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0, 63, 0, inpga_tlv),
+
+ SOC_DOUBLE_R("Headphone ZC Switch",
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME, 7, 1, 0),
+ SOC_DOUBLE_R("Headphone Playback Switch",
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME, 6, 1, 1),
+ SOC_DOUBLE_R_TLV("Headphone Volume",
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME, 0, 63, 0, spk_tlv),
+
+ SOC_DOUBLE_R("Speaker ZC Switch",
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Playback Switch",
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME, 6, 1, 1),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME, 0, 63, 0, spk_tlv),
+
+ SOC_DOUBLE_R("AUXOUT Playback Switch",
+ NAU8822_REG_AUX2_MIXER,
+ NAU8822_REG_AUX1_MIXER, 6, 1, 1),
+
+ SOC_DOUBLE_R_TLV("PGA Boost Volume",
+ NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 8, 1, 0, pga_boost_tlv),
+ SOC_DOUBLE_R_TLV("L2/R2 Boost Volume",
+ NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 4, 7, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("Aux Boost Volume",
+ NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv),
+
+ SOC_SINGLE("DAC 128x Oversampling Switch",
+ NAU8822_REG_DAC_CONTROL, 5, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch",
+ NAU8822_REG_ADC_CONTROL, 5, 1, 0),
+};
+
+/* LMAIN and RMAIN Mixer */
+static const struct snd_kcontrol_new nau8822_left_out_mixer[] = {
+ SOC_DAPM_SINGLE("LINMIX Switch",
+ NAU8822_REG_LEFT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("LAUX Switch",
+ NAU8822_REG_LEFT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("LDAC Switch",
+ NAU8822_REG_LEFT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("RDAC Switch",
+ NAU8822_REG_OUTPUT_CONTROL, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8822_right_out_mixer[] = {
+ SOC_DAPM_SINGLE("RINMIX Switch",
+ NAU8822_REG_RIGHT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("RAUX Switch",
+ NAU8822_REG_RIGHT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("RDAC Switch",
+ NAU8822_REG_RIGHT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("LDAC Switch",
+ NAU8822_REG_OUTPUT_CONTROL, 6, 1, 0),
+};
+
+/* AUX1 and AUX2 Mixer */
+static const struct snd_kcontrol_new nau8822_auxout1_mixer[] = {
+ SOC_DAPM_SINGLE("RDAC Switch", NAU8822_REG_AUX1_MIXER, 0, 1, 0),
+ SOC_DAPM_SINGLE("RMIX Switch", NAU8822_REG_AUX1_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINMIX Switch", NAU8822_REG_AUX1_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX1_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX1_MIXER, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8822_auxout2_mixer[] = {
+ SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX2_MIXER, 0, 1, 0),
+ SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX2_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINMIX Switch", NAU8822_REG_AUX2_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("AUX1MIX Output Switch",
+ NAU8822_REG_AUX2_MIXER, 3, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new nau8822_left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", NAU8822_REG_INPUT_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 0, 1, 0),
+};
+static const struct snd_kcontrol_new nau8822_right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", NAU8822_REG_INPUT_CONTROL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 4, 1, 0),
+};
+
+/* Loopback Switch */
+static const struct snd_kcontrol_new nau8822_loopback =
+ SOC_DAPM_SINGLE("Switch", NAU8822_REG_COMPANDING_CONTROL,
+ NAU8822_ADDAP_SFT, 1, 0);
+
+static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ unsigned int value;
+
+ value = snd_soc_component_read(component, NAU8822_REG_CLOCKING);
+
+ return (value & NAU8822_CLKM_MASK);
+}
+
+static const struct snd_soc_dapm_widget nau8822_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ NAU8822_REG_POWER_MANAGEMENT_3, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ NAU8822_REG_POWER_MANAGEMENT_3, 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+ NAU8822_REG_POWER_MANAGEMENT_2, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+ NAU8822_REG_POWER_MANAGEMENT_2, 1, 0),
+
+ SOC_MIXER_ARRAY("Left Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_3, 2, 0, nau8822_left_out_mixer),
+ SOC_MIXER_ARRAY("Right Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_3, 3, 0, nau8822_right_out_mixer),
+ SOC_MIXER_ARRAY("AUX1 Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_1, 7, 0, nau8822_auxout1_mixer),
+ SOC_MIXER_ARRAY("AUX2 Output Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_1, 6, 0, nau8822_auxout2_mixer),
+
+ SOC_MIXER_ARRAY("Left Input Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2,
+ 2, 0, nau8822_left_input_mixer),
+ SOC_MIXER_ARRAY("Right Input Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2,
+ 3, 0, nau8822_right_input_mixer),
+
+ SND_SOC_DAPM_PGA("Left Boost Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Boost Mixer",
+ NAU8822_REG_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA",
+ NAU8822_REG_LEFT_INP_PGA_CONTROL, 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA",
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL, 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out",
+ NAU8822_REG_POWER_MANAGEMENT_2, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out",
+ NAU8822_REG_POWER_MANAGEMENT_2, 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("AUX1 Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 8, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX2 Out",
+ NAU8822_REG_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias",
+ NAU8822_REG_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL",
+ NAU8822_REG_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &nau8822_loopback),
+
+ SND_SOC_DAPM_INPUT("LMICN"),
+ SND_SOC_DAPM_INPUT("LMICP"),
+ SND_SOC_DAPM_INPUT("RMICN"),
+ SND_SOC_DAPM_INPUT("RMICP"),
+ SND_SOC_DAPM_INPUT("LAUX"),
+ SND_SOC_DAPM_INPUT("RAUX"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("LHP"),
+ SND_SOC_DAPM_OUTPUT("RHP"),
+ SND_SOC_DAPM_OUTPUT("LSPK"),
+ SND_SOC_DAPM_OUTPUT("RSPK"),
+ SND_SOC_DAPM_OUTPUT("AUXOUT1"),
+ SND_SOC_DAPM_OUTPUT("AUXOUT2"),
+};
+
+static const struct snd_soc_dapm_route nau8822_dapm_routes[] = {
+ {"Right DAC", NULL, "PLL", check_mclk_select_pll},
+ {"Left DAC", NULL, "PLL", check_mclk_select_pll},
+
+ /* LMAIN and RMAIN Mixer */
+ {"Right Output Mixer", "LDAC Switch", "Left DAC"},
+ {"Right Output Mixer", "RDAC Switch", "Right DAC"},
+ {"Right Output Mixer", "RAUX Switch", "RAUX"},
+ {"Right Output Mixer", "RINMIX Switch", "Right Boost Mixer"},
+
+ {"Left Output Mixer", "LDAC Switch", "Left DAC"},
+ {"Left Output Mixer", "RDAC Switch", "Right DAC"},
+ {"Left Output Mixer", "LAUX Switch", "LAUX"},
+ {"Left Output Mixer", "LINMIX Switch", "Left Boost Mixer"},
+
+ /* AUX1 and AUX2 Mixer */
+ {"AUX1 Output Mixer", "RDAC Switch", "Right DAC"},
+ {"AUX1 Output Mixer", "RMIX Switch", "Right Output Mixer"},
+ {"AUX1 Output Mixer", "RINMIX Switch", "Right Boost Mixer"},
+ {"AUX1 Output Mixer", "LDAC Switch", "Left DAC"},
+ {"AUX1 Output Mixer", "LMIX Switch", "Left Output Mixer"},
+
+ {"AUX2 Output Mixer", "LDAC Switch", "Left DAC"},
+ {"AUX2 Output Mixer", "LMIX Switch", "Left Output Mixer"},
+ {"AUX2 Output Mixer", "LINMIX Switch", "Left Boost Mixer"},
+ {"AUX2 Output Mixer", "AUX1MIX Output Switch", "AUX1 Output Mixer"},
+
+ /* Outputs */
+ {"Right Headphone Out", NULL, "Right Output Mixer"},
+ {"RHP", NULL, "Right Headphone Out"},
+
+ {"Left Headphone Out", NULL, "Left Output Mixer"},
+ {"LHP", NULL, "Left Headphone Out"},
+
+ {"Right Speaker Out", NULL, "Right Output Mixer"},
+ {"RSPK", NULL, "Right Speaker Out"},
+
+ {"Left Speaker Out", NULL, "Left Output Mixer"},
+ {"LSPK", NULL, "Left Speaker Out"},
+
+ {"AUX1 Out", NULL, "AUX1 Output Mixer"},
+ {"AUX2 Out", NULL, "AUX2 Output Mixer"},
+ {"AUXOUT1", NULL, "AUX1 Out"},
+ {"AUXOUT2", NULL, "AUX2 Out"},
+
+ /* Boost Mixer */
+ {"Right ADC", NULL, "PLL", check_mclk_select_pll},
+ {"Left ADC", NULL, "PLL", check_mclk_select_pll},
+
+ {"Right ADC", NULL, "Right Boost Mixer"},
+
+ {"Right Boost Mixer", NULL, "RAUX"},
+ {"Right Boost Mixer", NULL, "Right Capture PGA"},
+ {"Right Boost Mixer", NULL, "R2"},
+
+ {"Left ADC", NULL, "Left Boost Mixer"},
+
+ {"Left Boost Mixer", NULL, "LAUX"},
+ {"Left Boost Mixer", NULL, "Left Capture PGA"},
+ {"Left Boost Mixer", NULL, "L2"},
+
+ /* Input PGA */
+ {"Right Capture PGA", NULL, "Right Input Mixer"},
+ {"Left Capture PGA", NULL, "Left Input Mixer"},
+
+ /* Enable Microphone Power */
+ {"Right Capture PGA", NULL, "Mic Bias"},
+ {"Left Capture PGA", NULL, "Mic Bias"},
+
+ {"Right Input Mixer", "R2 Switch", "R2"},
+ {"Right Input Mixer", "MicN Switch", "RMICN"},
+ {"Right Input Mixer", "MicP Switch", "RMICP"},
+
+ {"Left Input Mixer", "L2 Switch", "L2"},
+ {"Left Input Mixer", "MicN Switch", "LMICN"},
+ {"Left Input Mixer", "MicP Switch", "LMICP"},
+
+ /* Digital Loopback */
+ {"Digital Loopback", "Switch", "Left ADC"},
+ {"Digital Loopback", "Switch", "Right ADC"},
+ {"Left DAC", NULL, "Digital Loopback"},
+ {"Right DAC", NULL, "Digital Loopback"},
+};
+
+static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs,
+ struct nau8822_pll *pll_param)
+{
+ u64 f2, f2_max, pll_ratio;
+ int i, scal_sel;
+
+ if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN)
+ return -EINVAL;
+ f2_max = 0;
+ scal_sel = ARRAY_SIZE(nau8822_mclk_scaler);
+
+ for (i = 0; i < scal_sel; i++) {
+ f2 = 256 * fs * 4 * nau8822_mclk_scaler[i] / 10;
+ if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
+ f2_max < f2) {
+ f2_max = f2;
+ scal_sel = i;
+ }
+ }
+
+ if (ARRAY_SIZE(nau8822_mclk_scaler) == scal_sel)
+ return -EINVAL;
+ pll_param->mclk_scaler = scal_sel;
+ f2 = f2_max;
+
+ /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional
+ * input; round up the 24+4bit.
+ */
+ pll_ratio = div_u64(f2 << 28, pll_in);
+ pll_param->pre_factor = 0;
+ if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) {
+ pll_ratio <<= 1;
+ pll_param->pre_factor = 1;
+ }
+ pll_param->pll_int = (pll_ratio >> 28) & 0xF;
+ pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4);
+
+ return 0;
+}
+
+static int nau8822_config_clkdiv(struct snd_soc_dai *dai, int div, int rate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct nau8822_pll *pll = &nau8822->pll;
+ int i, sclk, imclk;
+
+ switch (nau8822->div_id) {
+ case NAU8822_CLK_MCLK:
+ /* Configure the master clock prescaler div to make system
+ * clock to approximate the internal master clock (IMCLK);
+ * and large or equal to IMCLK.
+ */
+ div = 0;
+ imclk = rate * 256;
+ for (i = 1; i < ARRAY_SIZE(nau8822_mclk_scaler); i++) {
+ sclk = (nau8822->sysclk * 10) / nau8822_mclk_scaler[i];
+ if (sclk < imclk)
+ break;
+ div = i;
+ }
+ dev_dbg(component->dev, "master clock prescaler %x for fs %d\n",
+ div, rate);
+
+ /* master clock from MCLK and disable PLL */
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+ (div << NAU8822_MCLKSEL_SFT));
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK,
+ NAU8822_CLKM_MCLK);
+ break;
+
+ case NAU8822_CLK_PLL:
+ /* master clock from PLL and enable PLL */
+ if (pll->mclk_scaler != div) {
+ dev_err(component->dev,
+ "master clock prescaler not meet PLL parameters\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+ (div << NAU8822_MCLKSEL_SFT));
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK,
+ NAU8822_CLKM_PLL);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct nau8822_pll *pll_param = &nau8822->pll;
+ int ret, fs;
+
+ if (freq_in == pll_param->freq_in &&
+ freq_out == pll_param->freq_out)
+ return 0;
+
+ if (freq_out == 0) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_OFF);
+ return 0;
+ }
+
+ fs = freq_out / 256;
+
+ ret = nau8822_calc_pll(freq_in, fs, pll_param);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev,
+ "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n",
+ pll_param->pll_int, pll_param->pll_frac,
+ pll_param->mclk_scaler, pll_param->pre_factor);
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_OFF);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_PLL_N, NAU8822_PLLMCLK_DIV2 | NAU8822_PLLN_MASK,
+ (pll_param->pre_factor ? NAU8822_PLLMCLK_DIV2 : 0) |
+ pll_param->pll_int);
+ snd_soc_component_write(component,
+ NAU8822_REG_PLL_K1, (pll_param->pll_frac >> NAU8822_PLLK1_SFT) &
+ NAU8822_PLLK1_MASK);
+ snd_soc_component_write(component,
+ NAU8822_REG_PLL_K2, (pll_param->pll_frac >> NAU8822_PLLK2_SFT) &
+ NAU8822_PLLK2_MASK);
+ snd_soc_component_write(component,
+ NAU8822_REG_PLL_K3, pll_param->pll_frac & NAU8822_PLLK3_MASK);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+ pll_param->mclk_scaler << NAU8822_MCLKSEL_SFT);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, NAU8822_CLKM_PLL);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_ON);
+
+ pll_param->freq_in = freq_in;
+ pll_param->freq_out = freq_out;
+
+ return 0;
+}
+
+static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ unsigned long mclk_freq;
+
+ nau8822->div_id = clk_id;
+ nau8822->sysclk = freq;
+
+ if (nau8822->mclk) {
+ mclk_freq = clk_get_rate(nau8822->mclk);
+ if (mclk_freq != freq) {
+ int ret = nau8822_set_pll(dai, NAU8822_CLK_MCLK,
+ NAU8822_CLK_MCLK, mclk_freq, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set PLL\n");
+ return ret;
+ }
+ nau8822->div_id = NAU8822_CLK_PLL;
+ }
+ }
+
+ dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq,
+ nau8822->div_id == NAU8822_CLK_PLL ? "PLL" : "MCLK");
+
+ return 0;
+}
+
+static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u16 ctrl1_val = 0, ctrl2_val = 0;
+
+ dev_dbg(component->dev, "%s\n", __func__);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ ctrl2_val &= ~1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl1_val |= 0x180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= 0x100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl1_val |= 0x80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_AUDIO_INTERFACE,
+ NAU8822_AIFMT_MASK | NAU8822_LRP_MASK | NAU8822_BCLKP_MASK,
+ ctrl1_val);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_CLOCKING, NAU8822_CLKIOEN_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8822_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ int div = 0, val_len = 0, val_rate = 0;
+ unsigned int ctrl_val, bclk_fs, bclk_div;
+
+ /* make BCLK and LRC divide configuration if the codec as master. */
+ ctrl_val = snd_soc_component_read(component, NAU8822_REG_CLOCKING);
+ if (ctrl_val & NAU8822_CLK_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = NAU8822_BCLKDIV_8;
+ else if (bclk_fs <= 64)
+ bclk_div = NAU8822_BCLKDIV_4;
+ else if (bclk_fs <= 128)
+ bclk_div = NAU8822_BCLKDIV_2;
+ else
+ return -EINVAL;
+ snd_soc_component_update_bits(component, NAU8822_REG_CLOCKING,
+ NAU8822_BCLKSEL_MASK, bclk_div);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len |= NAU8822_WLEN_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len |= NAU8822_WLEN_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val_len |= NAU8822_WLEN_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val_rate |= NAU8822_SMPLR_8K;
+ break;
+ case 11025:
+ val_rate |= NAU8822_SMPLR_12K;
+ break;
+ case 16000:
+ val_rate |= NAU8822_SMPLR_16K;
+ break;
+ case 22050:
+ val_rate |= NAU8822_SMPLR_24K;
+ break;
+ case 32000:
+ val_rate |= NAU8822_SMPLR_32K;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_AUDIO_INTERFACE, NAU8822_WLEN_MASK, val_len);
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_ADDITIONAL_CONTROL, NAU8822_SMPLR_MASK, val_rate);
+
+ /* If the master clock is from MCLK, provide the runtime FS for driver
+ * to get the master clock prescaler configuration.
+ */
+ if (nau8822->div_id != NAU8822_CLK_MCLK)
+ div = nau8822->pll.mclk_scaler;
+
+ nau8822_config_clkdiv(dai, div, params_rate(params));
+
+ return 0;
+}
+
+static int nau8822_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ dev_dbg(component->dev, "%s: %d\n", __func__, mute);
+
+ if (mute)
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_DAC_CONTROL, 0x40, 0x40);
+ else
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_DAC_CONTROL, 0x40, 0);
+
+ return 0;
+}
+
+static int nau8822_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (nau8822->mclk &&
+ snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON) {
+ int ret = clk_prepare_enable(nau8822->mclk);
+
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (nau8822->mclk &&
+ snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_OFF)
+ clk_disable_unprepare(nau8822->mclk);
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_IOBUF_EN | NAU8822_ABIAS_EN,
+ NAU8822_IOBUF_EN | NAU8822_ABIAS_EN);
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_3K);
+ mdelay(100);
+ }
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_300K);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component,
+ NAU8822_REG_POWER_MANAGEMENT_1, 0);
+ snd_soc_component_write(component,
+ NAU8822_REG_POWER_MANAGEMENT_2, 0);
+ snd_soc_component_write(component,
+ NAU8822_REG_POWER_MANAGEMENT_3, 0);
+ break;
+ }
+
+ dev_dbg(component->dev, "%s: %d\n", __func__, level);
+
+ return 0;
+}
+
+#define NAU8822_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define NAU8822_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops nau8822_dai_ops = {
+ .hw_params = nau8822_hw_params,
+ .mute_stream = nau8822_mute,
+ .set_fmt = nau8822_set_dai_fmt,
+ .set_sysclk = nau8822_set_dai_sysclk,
+ .set_pll = nau8822_set_pll,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver nau8822_dai = {
+ .name = "nau8822-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8822_RATES,
+ .formats = NAU8822_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8822_RATES,
+ .formats = NAU8822_FORMATS,
+ },
+ .ops = &nau8822_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int nau8822_suspend(struct snd_soc_component *component)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+
+ regcache_mark_dirty(nau8822->regmap);
+
+ return 0;
+}
+
+static int nau8822_resume(struct snd_soc_component *component)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ regcache_sync(nau8822->regmap);
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+/*
+ * These registers contain an "update" bit - bit 8. This means, for example,
+ * that one can write new DAC digital volume for both channels, but only when
+ * the update bit is set, will also the volume be updated - simultaneously for
+ * both channels.
+ */
+static const int update_reg[] = {
+ NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME,
+ NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME,
+ NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME,
+ NAU8822_REG_LEFT_INP_PGA_CONTROL,
+ NAU8822_REG_RIGHT_INP_PGA_CONTROL,
+ NAU8822_REG_LHP_VOLUME,
+ NAU8822_REG_RHP_VOLUME,
+ NAU8822_REG_LSPKOUT_VOLUME,
+ NAU8822_REG_RSPKOUT_VOLUME,
+};
+
+static int nau8822_probe(struct snd_soc_component *component)
+{
+ int i;
+ struct device_node *of_node = component->dev->of_node;
+
+ /*
+ * Set the update bit in all registers, that have one. This way all
+ * writes to those registers will also cause the update bit to be
+ * written.
+ */
+ for (i = 0; i < ARRAY_SIZE(update_reg); i++)
+ snd_soc_component_update_bits(component,
+ update_reg[i], 0x100, 0x100);
+
+ /* Check property to configure the two loudspeaker outputs as
+ * a single Bridge Tied Load output
+ */
+ if (of_property_read_bool(of_node, "nuvoton,spk-btl"))
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_RIGHT_SPEAKER_CONTROL,
+ NAU8822_RSUBBYP, NAU8822_RSUBBYP);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_nau8822 = {
+ .probe = nau8822_probe,
+ .suspend = nau8822_suspend,
+ .resume = nau8822_resume,
+ .set_bias_level = nau8822_set_bias_level,
+ .controls = nau8822_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8822_snd_controls),
+ .dapm_widgets = nau8822_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8822_dapm_widgets),
+ .dapm_routes = nau8822_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8822_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config nau8822_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = NAU8822_REG_MAX_REGISTER,
+ .volatile_reg = nau8822_volatile,
+
+ .readable_reg = nau8822_readable_reg,
+ .writeable_reg = nau8822_writeable_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8822_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults),
+};
+
+static int nau8822_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8822 *nau8822 = dev_get_platdata(dev);
+ int ret;
+
+ if (!nau8822) {
+ nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL);
+ if (nau8822 == NULL)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8822);
+
+ nau8822->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(nau8822->mclk))
+ return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk),
+ "Error getting mclk\n");
+
+ nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config);
+ if (IS_ERR(nau8822->regmap)) {
+ ret = PTR_ERR(nau8822->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+ nau8822->dev = dev;
+
+ /* Reset the codec */
+ ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822,
+ &nau8822_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id nau8822_i2c_id[] = {
+ { "nau8822" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8822_of_match[] = {
+ { .compatible = "nuvoton,nau8822", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nau8822_of_match);
+#endif
+
+static struct i2c_driver nau8822_i2c_driver = {
+ .driver = {
+ .name = "nau8822",
+ .of_match_table = of_match_ptr(nau8822_of_match),
+ },
+ .probe = nau8822_i2c_probe,
+ .id_table = nau8822_i2c_id,
+};
+module_i2c_driver(nau8822_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8822 codec driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h
new file mode 100644
index 000000000000..13fe0a091e9e
--- /dev/null
+++ b/sound/soc/codecs/nau8822.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * nau8822.h -- NAU8822 ALSA SoC Audio driver
+ *
+ * Copyright 2017 Nuvoton Technology Crop.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ * Co-author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Li <wtli@nuvoton.com>
+ */
+
+#ifndef __NAU8822_H__
+#define __NAU8822_H__
+
+#define NAU8822_REG_RESET 0x00
+#define NAU8822_REG_POWER_MANAGEMENT_1 0x01
+#define NAU8822_REG_POWER_MANAGEMENT_2 0x02
+#define NAU8822_REG_POWER_MANAGEMENT_3 0x03
+#define NAU8822_REG_AUDIO_INTERFACE 0x04
+#define NAU8822_REG_COMPANDING_CONTROL 0x05
+#define NAU8822_REG_CLOCKING 0x06
+#define NAU8822_REG_ADDITIONAL_CONTROL 0x07
+#define NAU8822_REG_GPIO_CONTROL 0x08
+#define NAU8822_REG_JACK_DETECT_CONTROL_1 0x09
+#define NAU8822_REG_DAC_CONTROL 0x0A
+#define NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define NAU8822_REG_JACK_DETECT_CONTROL_2 0x0D
+#define NAU8822_REG_ADC_CONTROL 0x0E
+#define NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define NAU8822_REG_EQ1 0x12
+#define NAU8822_REG_EQ2 0x13
+#define NAU8822_REG_EQ3 0x14
+#define NAU8822_REG_EQ4 0x15
+#define NAU8822_REG_EQ5 0x16
+#define NAU8822_REG_DAC_LIMITER_1 0x18
+#define NAU8822_REG_DAC_LIMITER_2 0x19
+#define NAU8822_REG_NOTCH_FILTER_1 0x1B
+#define NAU8822_REG_NOTCH_FILTER_2 0x1C
+#define NAU8822_REG_NOTCH_FILTER_3 0x1D
+#define NAU8822_REG_NOTCH_FILTER_4 0x1E
+#define NAU8822_REG_ALC_CONTROL_1 0x20
+#define NAU8822_REG_ALC_CONTROL_2 0x21
+#define NAU8822_REG_ALC_CONTROL_3 0x22
+#define NAU8822_REG_NOISE_GATE 0x23
+#define NAU8822_REG_PLL_N 0x24
+#define NAU8822_REG_PLL_K1 0x25
+#define NAU8822_REG_PLL_K2 0x26
+#define NAU8822_REG_PLL_K3 0x27
+#define NAU8822_REG_3D_CONTROL 0x29
+#define NAU8822_REG_RIGHT_SPEAKER_CONTROL 0x2B
+#define NAU8822_REG_INPUT_CONTROL 0x2C
+#define NAU8822_REG_LEFT_INP_PGA_CONTROL 0x2D
+#define NAU8822_REG_RIGHT_INP_PGA_CONTROL 0x2E
+#define NAU8822_REG_LEFT_ADC_BOOST_CONTROL 0x2F
+#define NAU8822_REG_RIGHT_ADC_BOOST_CONTROL 0x30
+#define NAU8822_REG_OUTPUT_CONTROL 0x31
+#define NAU8822_REG_LEFT_MIXER_CONTROL 0x32
+#define NAU8822_REG_RIGHT_MIXER_CONTROL 0x33
+#define NAU8822_REG_LHP_VOLUME 0x34
+#define NAU8822_REG_RHP_VOLUME 0x35
+#define NAU8822_REG_LSPKOUT_VOLUME 0x36
+#define NAU8822_REG_RSPKOUT_VOLUME 0x37
+#define NAU8822_REG_AUX2_MIXER 0x38
+#define NAU8822_REG_AUX1_MIXER 0x39
+#define NAU8822_REG_POWER_MANAGEMENT_4 0x3A
+#define NAU8822_REG_LEFT_TIME_SLOT 0x3B
+#define NAU8822_REG_MISC 0x3C
+#define NAU8822_REG_RIGHT_TIME_SLOT 0x3D
+#define NAU8822_REG_DEVICE_REVISION 0x3E
+#define NAU8822_REG_DEVICE_ID 0x3F
+#define NAU8822_REG_DAC_DITHER 0x41
+#define NAU8822_REG_ALC_ENHANCE_1 0x46
+#define NAU8822_REG_ALC_ENHANCE_2 0x47
+#define NAU8822_REG_192KHZ_SAMPLING 0x48
+#define NAU8822_REG_MISC_CONTROL 0x49
+#define NAU8822_REG_INPUT_TIEOFF 0x4A
+#define NAU8822_REG_POWER_REDUCTION 0x4B
+#define NAU8822_REG_AGC_PEAK2PEAK 0x4C
+#define NAU8822_REG_AGC_PEAK_DETECT 0x4D
+#define NAU8822_REG_AUTOMUTE_CONTROL 0x4E
+#define NAU8822_REG_OUTPUT_TIEOFF 0x4F
+#define NAU8822_REG_MAX_REGISTER NAU8822_REG_OUTPUT_TIEOFF
+
+/* NAU8822_REG_POWER_MANAGEMENT_1 (0x1) */
+#define NAU8822_REFIMP_MASK 0x3
+#define NAU8822_REFIMP_80K 0x1
+#define NAU8822_REFIMP_300K 0x2
+#define NAU8822_REFIMP_3K 0x3
+#define NAU8822_IOBUF_EN (0x1 << 2)
+#define NAU8822_ABIAS_EN (0x1 << 3)
+#define NAU8822_PLL_EN_MASK (0x1 << 5)
+#define NAU8822_PLL_ON (0x1 << 5)
+#define NAU8822_PLL_OFF (0x0 << 5)
+
+/* NAU8822_REG_AUDIO_INTERFACE (0x4) */
+#define NAU8822_AIFMT_MASK (0x3 << 3)
+#define NAU8822_WLEN_MASK (0x3 << 5)
+#define NAU8822_WLEN_20 (0x1 << 5)
+#define NAU8822_WLEN_24 (0x2 << 5)
+#define NAU8822_WLEN_32 (0x3 << 5)
+#define NAU8822_LRP_MASK (0x1 << 7)
+#define NAU8822_BCLKP_MASK (0x1 << 8)
+
+/* NAU8822_REG_COMPANDING_CONTROL (0x5) */
+#define NAU8822_ADDAP_SFT 0
+#define NAU8822_ADCCM_SFT 1
+#define NAU8822_DACCM_SFT 3
+
+/* NAU8822_REG_CLOCKING (0x6) */
+#define NAU8822_CLKIOEN_MASK 0x1
+#define NAU8822_CLK_MASTER 0x1
+#define NAU8822_CLK_SLAVE 0x0
+#define NAU8822_MCLKSEL_SFT 5
+#define NAU8822_MCLKSEL_MASK (0x7 << 5)
+#define NAU8822_BCLKSEL_SFT 2
+#define NAU8822_BCLKSEL_MASK (0x7 << 2)
+#define NAU8822_BCLKDIV_1 (0x0 << 2)
+#define NAU8822_BCLKDIV_2 (0x1 << 2)
+#define NAU8822_BCLKDIV_4 (0x2 << 2)
+#define NAU8822_BCLKDIV_8 (0x3 << 2)
+#define NAU8822_BCLKDIV_16 (0x4 << 2)
+#define NAU8822_CLKM_MASK (0x1 << 8)
+#define NAU8822_CLKM_MCLK (0x0 << 8)
+#define NAU8822_CLKM_PLL (0x1 << 8)
+
+/* NAU8822_REG_ADDITIONAL_CONTROL (0x08) */
+#define NAU8822_SMPLR_SFT 1
+#define NAU8822_SMPLR_MASK (0x7 << 1)
+#define NAU8822_SMPLR_48K (0x0 << 1)
+#define NAU8822_SMPLR_32K (0x1 << 1)
+#define NAU8822_SMPLR_24K (0x2 << 1)
+#define NAU8822_SMPLR_16K (0x3 << 1)
+#define NAU8822_SMPLR_12K (0x4 << 1)
+#define NAU8822_SMPLR_8K (0x5 << 1)
+
+/* NAU8822_REG_EQ1 (0x12) */
+#define NAU8822_EQ1GC_SFT 0
+#define NAU8822_EQ1CF_SFT 5
+#define NAU8822_EQM_SFT 8
+
+/* NAU8822_REG_EQ2 (0x13) */
+#define NAU8822_EQ2GC_SFT 0
+#define NAU8822_EQ2CF_SFT 5
+#define NAU8822_EQ2BW_SFT 8
+
+/* NAU8822_REG_EQ3 (0x14) */
+#define NAU8822_EQ3GC_SFT 0
+#define NAU8822_EQ3CF_SFT 5
+#define NAU8822_EQ3BW_SFT 8
+
+/* NAU8822_REG_EQ4 (0x15) */
+#define NAU8822_EQ4GC_SFT 0
+#define NAU8822_EQ4CF_SFT 5
+#define NAU8822_EQ4BW_SFT 8
+
+/* NAU8822_REG_EQ5 (0x16) */
+#define NAU8822_EQ5GC_SFT 0
+#define NAU8822_EQ5CF_SFT 5
+
+/* NAU8822_REG_ALC_CONTROL_1 (0x20) */
+#define NAU8822_ALCMINGAIN_SFT 0
+#define NAU8822_ALCMXGAIN_SFT 3
+#define NAU8822_ALCEN_SFT 7
+
+/* NAU8822_REG_ALC_CONTROL_2 (0x21) */
+#define NAU8822_ALCSL_SFT 0
+#define NAU8822_ALCHT_SFT 4
+
+/* NAU8822_REG_ALC_CONTROL_3 (0x22) */
+#define NAU8822_ALCATK_SFT 0
+#define NAU8822_ALCDCY_SFT 4
+#define NAU8822_ALCM_SFT 8
+
+/* NAU8822_REG_PLL_N (0x24) */
+#define NAU8822_PLLMCLK_DIV2 (0x1 << 4)
+#define NAU8822_PLLN_MASK 0xF
+
+#define NAU8822_PLLK1_SFT 18
+#define NAU8822_PLLK1_MASK 0x3F
+
+/* NAU8822_REG_PLL_K2 (0x26) */
+#define NAU8822_PLLK2_SFT 9
+#define NAU8822_PLLK2_MASK 0x1FF
+
+/* NAU8822_REG_PLL_K3 (0x27) */
+#define NAU8822_PLLK3_MASK 0x1FF
+
+/* NAU8822_REG_RIGHT_SPEAKER_CONTROL (0x2B) */
+#define NAU8822_RMIXMUT 0x20
+#define NAU8822_RSUBBYP 0x10
+
+#define NAU8822_RAUXRSUBG_SFT 1
+#define NAU8822_RAUXRSUBG_MASK 0x0E
+
+#define NAU8822_RAUXSMUT 0x01
+
+/* System Clock Source */
+enum {
+ NAU8822_CLK_MCLK,
+ NAU8822_CLK_PLL,
+};
+
+struct nau8822_pll {
+ int pre_factor;
+ int mclk_scaler;
+ int pll_frac;
+ int pll_int;
+ int freq_in;
+ int freq_out;
+};
+
+/* Codec Private Data */
+struct nau8822 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct nau8822_pll pll;
+ int sysclk;
+ int div_id;
+};
+
+#endif /* __NAU8822_H__ */
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
new file mode 100644
index 000000000000..6ce763762443
--- /dev/null
+++ b/sound/soc/codecs/nau8824.c
@@ -0,0 +1,2058 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "nau8824.h"
+
+#define NAU8824_JD_ACTIVE_HIGH BIT(0)
+#define NAU8824_MONO_SPEAKER BIT(1)
+
+static int nau8824_quirk;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+ int clk_id, unsigned int freq);
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824);
+
+/* the ADC threshold of headset */
+#define DMIC_CLK 3072000
+
+/* the ADC threshold of headset */
+#define HEADSET_SARADC_THD 0x80
+
+/* the parameter threshold of FLL */
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8824_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8824_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8824_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* over sampling rate */
+static const struct nau8824_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8824_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8824_reg_defaults[] = {
+ { NAU8824_REG_ENA_CTRL, 0x0000 },
+ { NAU8824_REG_CLK_GATING_ENA, 0x0000 },
+ { NAU8824_REG_CLK_DIVIDER, 0x0000 },
+ { NAU8824_REG_FLL1, 0x0000 },
+ { NAU8824_REG_FLL2, 0x3126 },
+ { NAU8824_REG_FLL3, 0x0008 },
+ { NAU8824_REG_FLL4, 0x0010 },
+ { NAU8824_REG_FLL5, 0xC000 },
+ { NAU8824_REG_FLL6, 0x6000 },
+ { NAU8824_REG_FLL_VCO_RSV, 0xF13C },
+ { NAU8824_REG_JACK_DET_CTRL, 0x0000 },
+ { NAU8824_REG_INTERRUPT_SETTING_1, 0x0000 },
+ { NAU8824_REG_IRQ, 0x0000 },
+ { NAU8824_REG_CLEAR_INT_REG, 0x0000 },
+ { NAU8824_REG_INTERRUPT_SETTING, 0x1000 },
+ { NAU8824_REG_SAR_ADC, 0x0015 },
+ { NAU8824_REG_VDET_COEFFICIENT, 0x0110 },
+ { NAU8824_REG_VDET_THRESHOLD_1, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_2, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_3, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_4, 0x0000 },
+ { NAU8824_REG_GPIO_SEL, 0x0000 },
+ { NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 0x000B },
+ { NAU8824_REG_PORT0_I2S_PCM_CTRL_2, 0x0010 },
+ { NAU8824_REG_PORT0_LEFT_TIME_SLOT, 0x0000 },
+ { NAU8824_REG_PORT0_RIGHT_TIME_SLOT, 0x0000 },
+ { NAU8824_REG_TDM_CTRL, 0x0000 },
+ { NAU8824_REG_ADC_HPF_FILTER, 0x0000 },
+ { NAU8824_REG_ADC_FILTER_CTRL, 0x0002 },
+ { NAU8824_REG_DAC_FILTER_CTRL_1, 0x0000 },
+ { NAU8824_REG_DAC_FILTER_CTRL_2, 0x0000 },
+ { NAU8824_REG_NOTCH_FILTER_1, 0x0000 },
+ { NAU8824_REG_NOTCH_FILTER_2, 0x0000 },
+ { NAU8824_REG_EQ1_LOW, 0x112C },
+ { NAU8824_REG_EQ2_EQ3, 0x2C2C },
+ { NAU8824_REG_EQ4_EQ5, 0x2C2C },
+ { NAU8824_REG_ADC_CH0_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH1_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH2_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH3_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_DAC_MUTE_CTRL, 0x0000 },
+ { NAU8824_REG_DAC_CH0_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_DAC_CH1_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_TO_DAC_ST, 0x0000 },
+ { NAU8824_REG_DRC_KNEE_IP12_ADC_CH01, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_ADC_CH01, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_ADC_CH01, 0x25FF },
+ { NAU8824_REG_DRC_ATKDCY_ADC_CH01, 0x3457 },
+ { NAU8824_REG_DRC_KNEE_IP12_ADC_CH23, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_ADC_CH23, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_ADC_CH23, 0x25FF },
+ { NAU8824_REG_DRC_ATKDCY_ADC_CH23, 0x3457 },
+ { NAU8824_REG_DRC_GAINL_ADC0, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC1, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC2, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC3, 0x0200 },
+ { NAU8824_REG_DRC_KNEE_IP12_DAC, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_DAC, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_DAC, 0x25F9 },
+ { NAU8824_REG_DRC_ATKDCY_DAC, 0x3457 },
+ { NAU8824_REG_DRC_GAIN_DAC_CH0, 0x0200 },
+ { NAU8824_REG_DRC_GAIN_DAC_CH1, 0x0200 },
+ { NAU8824_REG_MODE, 0x0000 },
+ { NAU8824_REG_MODE1, 0x0000 },
+ { NAU8824_REG_MODE2, 0x0000 },
+ { NAU8824_REG_CLASSG, 0x0000 },
+ { NAU8824_REG_OTP_EFUSE, 0x0000 },
+ { NAU8824_REG_OTPDOUT_1, 0x0000 },
+ { NAU8824_REG_OTPDOUT_2, 0x0000 },
+ { NAU8824_REG_MISC_CTRL, 0x0000 },
+ { NAU8824_REG_I2C_TIMEOUT, 0xEFFF },
+ { NAU8824_REG_TEST_MODE, 0x0000 },
+ { NAU8824_REG_I2C_DEVICE_ID, 0x1AF1 },
+ { NAU8824_REG_SAR_ADC_DATA_OUT, 0x00FF },
+ { NAU8824_REG_BIAS_ADJ, 0x0000 },
+ { NAU8824_REG_PGA_GAIN, 0x0000 },
+ { NAU8824_REG_TRIM_SETTINGS, 0x0000 },
+ { NAU8824_REG_ANALOG_CONTROL_1, 0x0000 },
+ { NAU8824_REG_ANALOG_CONTROL_2, 0x0000 },
+ { NAU8824_REG_ENABLE_LO, 0x0000 },
+ { NAU8824_REG_GAIN_LO, 0x0000 },
+ { NAU8824_REG_CLASSD_GAIN_1, 0x0000 },
+ { NAU8824_REG_CLASSD_GAIN_2, 0x0000 },
+ { NAU8824_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8824_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8824_REG_RDAC, 0x0008 },
+ { NAU8824_REG_MIC_BIAS, 0x0006 },
+ { NAU8824_REG_HS_VOLUME_CONTROL, 0x0000 },
+ { NAU8824_REG_BOOST, 0x0000 },
+ { NAU8824_REG_FEPGA, 0x0000 },
+ { NAU8824_REG_FEPGA_II, 0x0000 },
+ { NAU8824_REG_FEPGA_SE, 0x0000 },
+ { NAU8824_REG_FEPGA_ATTENUATION, 0x0000 },
+ { NAU8824_REG_ATT_PORT0, 0x0000 },
+ { NAU8824_REG_ATT_PORT1, 0x0000 },
+ { NAU8824_REG_POWER_UP_CONTROL, 0x0000 },
+ { NAU8824_REG_CHARGE_PUMP_CONTROL, 0x0300 },
+ { NAU8824_REG_CHARGE_PUMP_INPUT, 0x0013 },
+};
+
+static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout)
+{
+ int ret;
+
+ if (timeout) {
+ ret = down_timeout(&nau8824->jd_sem, timeout);
+ if (ret < 0)
+ dev_warn(nau8824->dev, "Acquire semaphore timeout\n");
+ } else {
+ ret = down_interruptible(&nau8824->jd_sem);
+ if (ret < 0)
+ dev_warn(nau8824->dev, "Acquire semaphore fail\n");
+ }
+
+ return ret;
+}
+
+static inline void nau8824_sema_release(struct nau8824 *nau8824)
+{
+ up(&nau8824->jd_sem);
+}
+
+static bool nau8824_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_ENA_CTRL ... NAU8824_REG_FLL_VCO_RSV:
+ case NAU8824_REG_JACK_DET_CTRL:
+ case NAU8824_REG_INTERRUPT_SETTING_1:
+ case NAU8824_REG_IRQ:
+ case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+ case NAU8824_REG_GPIO_SEL:
+ case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+ case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+ case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 ... NAU8824_REG_DRC_GAINL_ADC3:
+ case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+ case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+ case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+ case NAU8824_REG_I2C_TIMEOUT:
+ case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+ case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+ case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+ case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_INPUT:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8824_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_RESET ... NAU8824_REG_FLL_VCO_RSV:
+ case NAU8824_REG_JACK_DET_CTRL:
+ case NAU8824_REG_INTERRUPT_SETTING_1:
+ case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+ case NAU8824_REG_GPIO_SEL:
+ case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+ case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+ case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01:
+ case NAU8824_REG_DRC_KNEE_IP34_ADC_CH01:
+ case NAU8824_REG_DRC_SLOPE_ADC_CH01:
+ case NAU8824_REG_DRC_ATKDCY_ADC_CH01:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH23:
+ case NAU8824_REG_DRC_KNEE_IP34_ADC_CH23:
+ case NAU8824_REG_DRC_SLOPE_ADC_CH23:
+ case NAU8824_REG_DRC_ATKDCY_ADC_CH23:
+ case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_ATKDCY_DAC:
+ case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+ case NAU8824_REG_I2C_TIMEOUT:
+ case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+ case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+ case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8824_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_RESET:
+ case NAU8824_REG_IRQ ... NAU8824_REG_CLEAR_INT_REG:
+ case NAU8824_REG_DRC_GAINL_ADC0 ... NAU8824_REG_DRC_GAINL_ADC3:
+ case NAU8824_REG_DRC_GAIN_DAC_CH0 ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+ case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+ case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+ case NAU8824_REG_CHARGE_PUMP_INPUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const nau8824_companding[] = {
+ "Off", "NC", "u-law", "A-law" };
+
+static const struct soc_enum nau8824_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 12,
+ ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const struct soc_enum nau8824_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 14,
+ ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const char * const nau8824_adc_decimation[] = {
+ "32", "64", "128", "256" };
+
+static const struct soc_enum nau8824_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_FILTER_CTRL, 0,
+ ARRAY_SIZE(nau8824_adc_decimation), nau8824_adc_decimation);
+
+static const char * const nau8824_dac_oversampl[] = {
+ "64", "256", "128", "", "32" };
+
+static const struct soc_enum nau8824_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_DAC_FILTER_CTRL_1, 0,
+ ARRAY_SIZE(nau8824_dac_oversampl), nau8824_dac_oversampl);
+
+static const char * const nau8824_input_channel[] = {
+ "Input CH0", "Input CH1", "Input CH2", "Input CH3" };
+
+static const struct soc_enum nau8824_adc_ch0_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH0_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch1_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH1_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch2_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH2_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch3_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH3_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const char * const nau8824_tdm_slot[] = {
+ "Slot 0", "Slot 1", "Slot 2", "Slot 3" };
+
+static const struct soc_enum nau8824_dac_left_sel_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 6,
+ ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const struct soc_enum nau8824_dac_right_sel_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 4,
+ ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(spk_vol_tlv, 0, 2400);
+static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -3000, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, -12800, 50, 0);
+
+static const struct snd_kcontrol_new nau8824_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8824_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8824_companding_dac_enum),
+
+ SOC_ENUM("ADC Decimation Rate", nau8824_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum),
+
+ SOC_SINGLE_TLV("Speaker Right DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 8, 0x19, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 0, 0x19, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 0, 0x19, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Right DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 8, 0x19, 0, spk_vol_tlv),
+
+ SOC_SINGLE_TLV("Headphone Right DACR Volume",
+ NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Left DACL Volume",
+ NAU8824_REG_ATT_PORT0, 0, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Right DACL Volume",
+ NAU8824_REG_ATT_PORT1, 8, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Left DACR Volume",
+ NAU8824_REG_ATT_PORT1, 0, 0x1f, 0, hp_vol_tlv),
+
+ SOC_SINGLE_TLV("MIC1 Volume", NAU8824_REG_FEPGA_II,
+ NAU8824_FEPGA_GAINL_SFT, 0x12, 0, mic_vol_tlv),
+ SOC_SINGLE_TLV("MIC2 Volume", NAU8824_REG_FEPGA_II,
+ NAU8824_FEPGA_GAINR_SFT, 0x12, 0, mic_vol_tlv),
+
+ SOC_SINGLE_TLV("DMIC1 Volume", NAU8824_REG_ADC_CH0_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC2 Volume", NAU8824_REG_ADC_CH1_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC3 Volume", NAU8824_REG_ADC_CH2_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC4 Volume", NAU8824_REG_ADC_CH3_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+
+ SOC_ENUM("ADC CH0 Select", nau8824_adc_ch0_enum),
+ SOC_ENUM("ADC CH1 Select", nau8824_adc_ch1_enum),
+ SOC_ENUM("ADC CH2 Select", nau8824_adc_ch2_enum),
+ SOC_ENUM("ADC CH3 Select", nau8824_adc_ch3_enum),
+
+ SOC_SINGLE("ADC CH0 TX Switch", NAU8824_REG_TDM_CTRL, 0, 1, 0),
+ SOC_SINGLE("ADC CH1 TX Switch", NAU8824_REG_TDM_CTRL, 1, 1, 0),
+ SOC_SINGLE("ADC CH2 TX Switch", NAU8824_REG_TDM_CTRL, 2, 1, 0),
+ SOC_SINGLE("ADC CH3 TX Switch", NAU8824_REG_TDM_CTRL, 3, 1, 0),
+
+ SOC_ENUM("DACL Channel Source", nau8824_dac_left_sel_enum),
+ SOC_ENUM("DACR Channel Source", nau8824_dac_right_sel_enum),
+
+ SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0),
+ SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0),
+
+ SOC_SINGLE("THD for key media",
+ NAU8824_REG_VDET_THRESHOLD_1, 8, 0xff, 0),
+ SOC_SINGLE("THD for key voice command",
+ NAU8824_REG_VDET_THRESHOLD_1, 0, 0xff, 0),
+ SOC_SINGLE("THD for key volume up",
+ NAU8824_REG_VDET_THRESHOLD_2, 8, 0xff, 0),
+ SOC_SINGLE("THD for key volume down",
+ NAU8824_REG_VDET_THRESHOLD_2, 0, 0xff, 0),
+};
+
+static int nau8824_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_ANALOG_CONTROL_2,
+ NAU8824_CLASSD_CLAMP_DIS, NAU8824_CLASSD_CLAMP_DIS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_ANALOG_CONTROL_2,
+ NAU8824_CLASSD_CLAMP_DIS, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_JAMNODCLOW, NAU8824_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8824->regmap;
+ unsigned int value;
+ bool clk_fll, error;
+ int ret;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8824->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8824_is_jack_inserted(nau8824)) {
+ nau8824_config_sysclk(nau8824,
+ NAU8824_CLK_INTERNAL, 0);
+ } else {
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+ }
+
+ clk_disable_unprepare(nau8824->mclk);
+ } else {
+ dev_dbg(nau8824->dev, "system clock control : POWER ON\n");
+
+ ret = clk_prepare_enable(nau8824->mclk);
+ if (ret)
+ return ret;
+
+ /* Check the clock source setting is proper or not
+ * no matter the source is from FLL or MCLK.
+ */
+ regmap_read(regmap, NAU8824_REG_FLL1, &value);
+ clk_fll = value & NAU8824_FLL_RATIO_MASK;
+ /* It's error to use internal clock when playback */
+ regmap_read(regmap, NAU8824_REG_FLL6, &value);
+ error = value & NAU8824_DCO_EN;
+ if (!error) {
+ /* Check error depending on source is FLL or MCLK. */
+ regmap_read(regmap, NAU8824_REG_CLK_DIVIDER, &value);
+ if (clk_fll)
+ error = !(value & NAU8824_CLK_SRC_VCO);
+ else
+ error = value & NAU8824_CLK_SRC_VCO;
+ }
+ /* Recover the clock source setting if error. */
+ if (error) {
+ if (clk_fll) {
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_DCO_EN, 0);
+ regmap_update_bits(regmap,
+ NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK,
+ NAU8824_CLK_SRC_VCO);
+ } else {
+ nau8824_config_sysclk(nau8824,
+ NAU8824_CLK_MCLK, 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int src;
+ unsigned int freq;
+
+ freq = clk_get_rate(nau8824->mclk);
+ if (!freq)
+ freq = nau8824->fs * 256;
+
+ /* The DMIC clock is gotten from system clock (256fs) divided by
+ * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or
+ * less than 3.072 MHz.
+ */
+ for (src = 0; src < 5; src++) {
+ if (freq / (0x1 << src) <= DMIC_CLK)
+ break;
+ }
+ dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, freq);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT));
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new nau8824_adc_ch0_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH0_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch1_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH1_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch2_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH2_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch3_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH3_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_left_mixer[] = {
+ SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_MIC1_SFT, 1, 0),
+ SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_adc_right_mixer[] = {
+ SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODER_MIC2_SFT, 1, 0),
+ SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODER_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_left_mixer[] = {
+ SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPL_EN_SFT, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACL_HPL_EN_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_right_mixer[] = {
+ SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACL_HPR_EN_SFT, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPR_EN_SFT, 1, 0),
+};
+
+static const char * const nau8824_dac_src[] = { "DACL", "DACR" };
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8824_dacl_enum, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+ NAU8824_DAC_CH0_SEL_SFT, nau8824_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8824_dacr_enum, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+ NAU8824_DAC_CH1_SEL_SFT, nau8824_dac_src);
+
+static const struct snd_kcontrol_new nau8824_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8824_dacl_enum);
+
+static const struct snd_kcontrol_new nau8824_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8824_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_INPUT("HSMIC1"),
+ SND_SOC_DAPM_INPUT("HSMIC2"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+
+ SND_SOC_DAPM_SUPPLY("SAR", NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_ADC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8824_REG_MIC_BIAS,
+ NAU8824_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC12 Power", NAU8824_REG_BIAS_ADJ,
+ NAU8824_DMIC1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC34 Power", NAU8824_REG_BIAS_ADJ,
+ NAU8824_DMIC2_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+ dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SWITCH("DMIC1 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch0_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC2 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch1_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC3 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch2_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC4 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch3_dmic),
+
+ SND_SOC_DAPM_MIXER("Left ADC", NAU8824_REG_POWER_UP_CONTROL,
+ 12, 0, nau8824_adc_left_mixer,
+ ARRAY_SIZE(nau8824_adc_left_mixer)),
+ SND_SOC_DAPM_MIXER("Right ADC", NAU8824_REG_POWER_UP_CONTROL,
+ 13, 0, nau8824_adc_right_mixer,
+ ARRAY_SIZE(nau8824_adc_right_mixer)),
+
+ SND_SOC_DAPM_ADC("ADCL", NULL, NAU8824_REG_ANALOG_ADC_2,
+ NAU8824_ADCL_EN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2,
+ NAU8824_ADCR_EN_SFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC,
+ NAU8824_DACL_EN_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DACL Clock", NAU8824_REG_RDAC,
+ NAU8824_DACL_CLK_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, NAU8824_REG_RDAC,
+ NAU8824_DACR_EN_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DACR Clock", NAU8824_REG_RDAC,
+ NAU8824_DACR_CLK_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacr_mux),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ 8, 1, nau8824_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ 9, 1, nau8824_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("ClassD", 0, NAU8824_REG_CLASSD_GAIN_1,
+ NAU8824_CLASSD_EN_SFT, 0, nau8824_spk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("Left Headphone", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_LDAC_EN_SFT, 0, nau8824_hp_left_mixer,
+ ARRAY_SIZE(nau8824_hp_left_mixer)),
+ SND_SOC_DAPM_MIXER("Right Headphone", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_RDAC_EN_SFT, 0, nau8824_hp_right_mixer,
+ ARRAY_SIZE(nau8824_hp_right_mixer)),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_CHARGE_PUMP_EN_SFT, 0, nau8824_pump_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("Output Driver L",
+ NAU8824_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver R",
+ NAU8824_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Main Driver L",
+ NAU8824_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Main Driver R",
+ NAU8824_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Boost Driver", NAU8824_REG_BOOST,
+ NAU8824_HP_BOOST_DIS_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Class G", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPKOUTL"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8824_dapm_routes[] = {
+ {"DMIC1 Enable", "Switch", "DMIC1"},
+ {"DMIC2 Enable", "Switch", "DMIC2"},
+ {"DMIC3 Enable", "Switch", "DMIC3"},
+ {"DMIC4 Enable", "Switch", "DMIC4"},
+
+ {"DMIC1", NULL, "DMIC12 Power"},
+ {"DMIC2", NULL, "DMIC12 Power"},
+ {"DMIC3", NULL, "DMIC34 Power"},
+ {"DMIC4", NULL, "DMIC34 Power"},
+ {"DMIC12 Power", NULL, "DMIC Clock"},
+ {"DMIC34 Power", NULL, "DMIC Clock"},
+
+ {"Left ADC", "MIC Switch", "MIC1"},
+ {"Left ADC", "HSMIC Switch", "HSMIC1"},
+ {"Right ADC", "MIC Switch", "MIC2"},
+ {"Right ADC", "HSMIC Switch", "HSMIC2"},
+
+ {"ADCL", NULL, "Left ADC"},
+ {"ADCR", NULL, "Right ADC"},
+
+ {"AIFTX", NULL, "MICBIAS"},
+ {"AIFTX", NULL, "ADCL"},
+ {"AIFTX", NULL, "ADCR"},
+ {"AIFTX", NULL, "DMIC1 Enable"},
+ {"AIFTX", NULL, "DMIC2 Enable"},
+ {"AIFTX", NULL, "DMIC3 Enable"},
+ {"AIFTX", NULL, "DMIC4 Enable"},
+
+ {"AIFTX", NULL, "System Clock"},
+ {"AIFRX", NULL, "System Clock"},
+
+ {"DACL", NULL, "AIFRX"},
+ {"DACL", NULL, "DACL Clock"},
+ {"DACR", NULL, "AIFRX"},
+ {"DACR", NULL, "DACR Clock"},
+
+ {"DACL Mux", "DACL", "DACL"},
+ {"DACL Mux", "DACR", "DACR"},
+ {"DACR Mux", "DACL", "DACL"},
+ {"DACR Mux", "DACR", "DACR"},
+
+ {"Output DACL", NULL, "DACL Mux"},
+ {"Output DACR", NULL, "DACR Mux"},
+
+ {"ClassD", NULL, "Output DACL"},
+ {"ClassD", NULL, "Output DACR"},
+
+ {"Left Headphone", "DAC Left Switch", "Output DACL"},
+ {"Left Headphone", "DAC Right Switch", "Output DACR"},
+ {"Right Headphone", "DAC Left Switch", "Output DACL"},
+ {"Right Headphone", "DAC Right Switch", "Output DACR"},
+
+ {"Charge Pump", NULL, "Left Headphone"},
+ {"Charge Pump", NULL, "Right Headphone"},
+ {"Output Driver L", NULL, "Charge Pump"},
+ {"Output Driver R", NULL, "Charge Pump"},
+ {"Main Driver L", NULL, "Output Driver L"},
+ {"Main Driver R", NULL, "Output Driver R"},
+ {"Class G", NULL, "Main Driver L"},
+ {"Class G", NULL, "Main Driver R"},
+ {"HP Boost Driver", NULL, "Class G"},
+
+ {"SPKOUTL", NULL, "ClassD"},
+ {"SPKOUTR", NULL, "ClassD"},
+ {"HPOL", NULL, "HP Boost Driver"},
+ {"HPOR", NULL, "HP Boost Driver"},
+};
+
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824)
+{
+ struct snd_soc_jack *jack = nau8824->jack;
+ bool insert = false;
+
+ if (nau8824->irq && jack)
+ insert = jack->status & SND_JACK_HEADPHONE;
+
+ return insert;
+}
+
+static void nau8824_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8824_REG_IRQ, &active_irq);
+ for (i = 0; i < NAU8824_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8824_REG_CLEAR_INT_REG, clear_irq);
+ }
+}
+
+static void nau8824_eject_jack(struct nau8824 *nau8824)
+{
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Clear all interruption status */
+ nau8824_int_status_clear_all(regmap);
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ /* Enable the insertion interruption, disable the ejection
+ * interruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS,
+ NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+ NAU8824_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+ NAU8824_IRQ_INSERT_EN);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+
+ /* Close clock for jack type detection at manual mode */
+ if (snd_soc_dapm_get_bias_level(dapm) < SND_SOC_BIAS_PREPARE)
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+}
+
+static void nau8824_jdet_work(struct work_struct *work)
+{
+ struct nau8824 *nau8824 = container_of(
+ work, struct nau8824, jdet_work);
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ struct regmap *regmap = nau8824->regmap;
+ int adc_value, event = 0, event_mask = 0;
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+
+ msleep(100);
+
+ regmap_read(regmap, NAU8824_REG_SAR_ADC_DATA_OUT, &adc_value);
+ adc_value = adc_value & NAU8824_SAR_ADC_DATA_MASK;
+ dev_dbg(nau8824->dev, "SAR ADC data 0x%02x\n", adc_value);
+ if (adc_value < HEADSET_SARADC_THD) {
+ event |= SND_JACK_HEADPHONE;
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ } else {
+ event |= SND_JACK_HEADSET;
+ }
+ event_mask |= SND_JACK_HEADSET;
+ snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+ /* Enable short key press and release interruption. */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_KEY_RELEASE_DIS |
+ NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0);
+
+ if (nau8824->resume_lock) {
+ nau8824_sema_release(nau8824);
+ nau8824->resume_lock = false;
+ }
+}
+
+static void nau8824_setup_auto_irq(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Enable jack ejection interruption. */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+ NAU8824_IRQ_EJECT_EN);
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_EJECT_DIS, 0);
+ /* Enable internal VCO needed for interruptions */
+ if (snd_soc_dapm_get_bias_level(nau8824->dapm) < SND_SOC_BIAS_PREPARE)
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, 0);
+}
+
+static int nau8824_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines
+ * only 6 buttons.
+ */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+#define NAU8824_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8824_interrupt(int irq, void *data)
+{
+ struct nau8824 *nau8824 = (struct nau8824 *)data;
+ struct regmap *regmap = nau8824->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8824_REG_IRQ, &active_irq)) {
+ dev_err(nau8824->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+ dev_dbg(nau8824->dev, "IRQ %x\n", active_irq);
+
+ if (active_irq & NAU8824_JACK_EJECTION_DETECTED) {
+ nau8824_eject_jack(nau8824);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8824_JACK_EJECTION_DETECTED;
+ /* release semaphore held after resume,
+ * and cancel jack detection
+ */
+ if (nau8824->resume_lock) {
+ nau8824_sema_release(nau8824);
+ nau8824->resume_lock = false;
+ }
+ cancel_work_sync(&nau8824->jdet_work);
+ } else if (active_irq & NAU8824_KEY_SHORT_PRESS_IRQ) {
+ int key_status, button_pressed;
+
+ regmap_read(regmap, NAU8824_REG_CLEAR_INT_REG,
+ &key_status);
+
+ /* lower 8 bits of the register are for pressed keys */
+ button_pressed = nau8824_button_decode(key_status);
+
+ event |= button_pressed;
+ dev_dbg(nau8824->dev, "button %x pressed\n", event);
+ event_mask |= NAU8824_BUTTONS;
+ clear_irq = NAU8824_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8824_KEY_RELEASE_IRQ) {
+ event_mask = NAU8824_BUTTONS;
+ clear_irq = NAU8824_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8824_JACK_INSERTION_DETECTED) {
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_INSERT_DIS,
+ NAU8824_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN, 0);
+ /* detect microphone and jack type */
+ cancel_work_sync(&nau8824->jdet_work);
+ schedule_work(&nau8824->jdet_work);
+
+ /* Enable interruption for jack type detection at audo
+ * mode which can detect microphone and jack type.
+ */
+ nau8824_setup_auto_irq(nau8824);
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8824_REG_CLEAR_INT_REG, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static const struct nau8824_osr_attr *
+nau8824_get_osr(struct nau8824 *nau8824, int stream)
+{
+ unsigned int osr;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
+ osr &= NAU8824_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+ return &osr_dac_sel[osr];
+ } else {
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_ADC_FILTER_CTRL, &osr);
+ osr &= NAU8824_ADC_SYNC_DOWN_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+ }
+}
+
+static int nau8824_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ const struct nau8824_osr_attr *osr;
+
+ osr = nau8824_get_osr(nau8824, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
+static int nau8824_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div;
+ const struct nau8824_osr_attr *osr;
+ int err = -EINVAL;
+
+ nau8824_sema_acquire(nau8824, HZ);
+
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ nau8824->fs = params_rate(params);
+ osr = nau8824_get_osr(nau8824, substream->stream);
+ if (!osr || !osr->osr)
+ goto error;
+ if (nau8824->fs * osr->osr > CLK_DA_AD_MAX)
+ goto error;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8824_CLK_DAC_SRC_SFT);
+ else
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8824_CLK_ADC_SRC_SFT);
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_PORT0_I2S_PCM_CTRL_2, &ctrl_val);
+ if (ctrl_val & NAU8824_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / nau8824->fs;
+ if (bclk_fs <= 32)
+ bclk_div = 0x3;
+ else if (bclk_fs <= 64)
+ bclk_div = 0x2;
+ else if (bclk_fs <= 128)
+ bclk_div = 0x1;
+ else if (bclk_fs <= 256)
+ bclk_div = 0;
+ else
+ goto error;
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+ NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK,
+ (bclk_div << NAU8824_I2S_LRC_DIV_SFT) | bclk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8824_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8824_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8824_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8824_I2S_DL_32;
+ break;
+ default:
+ goto error;
+ }
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+ NAU8824_I2S_DL_MASK, val_len);
+ err = 0;
+
+ error:
+ nau8824_sema_release(nau8824);
+
+ return err;
+}
+
+static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl2_val |= NAU8824_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8824_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8824_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8824_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8824_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8824_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ nau8824_sema_acquire(nau8824, HZ);
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+ NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK |
+ NAU8824_I2S_PCMB_EN, ctrl1_val);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+ NAU8824_I2S_MS_MASK, ctrl2_val);
+
+ nau8824_sema_release(nau8824);
+
+ return 0;
+}
+
+/**
+ * nau8824_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @tx_mask: Bitmask representing active TX slots. Ex.
+ * 0xf for normal 4 channel TDM.
+ * 0xf0 for shifted 4 channel TDM
+ * @rx_mask: Bitmask [0:1] representing active DACR RX slots.
+ * Bitmask [2:3] representing active DACL RX slots.
+ * 00=CH0,01=CH1,10=CH2,11=CH3. Ex.
+ * 0xf for DACL/R selecting TDM CH3.
+ * 0xf0 for DACL/R selecting shifted TDM CH3.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Only support 4 slots TDM.
+ */
+static int nau8824_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ unsigned int tslot_l = 0, ctrl_val = 0;
+
+ if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)) ||
+ ((rx_mask & 0xf0) && (rx_mask & 0xf)) ||
+ ((rx_mask & 0xf0) && (tx_mask & 0xf)) ||
+ ((rx_mask & 0xf) && (tx_mask & 0xf0)))
+ return -EINVAL;
+
+ ctrl_val |= (NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN);
+ if (tx_mask & 0xf0) {
+ tslot_l = 4 * slot_width;
+ ctrl_val |= (tx_mask >> 4);
+ } else {
+ ctrl_val |= tx_mask;
+ }
+ if (rx_mask & 0xf0)
+ ctrl_val |= ((rx_mask >> 4) << NAU8824_TDM_DACR_RX_SFT);
+ else
+ ctrl_val |= (rx_mask << NAU8824_TDM_DACR_RX_SFT);
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_TDM_CTRL,
+ NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN |
+ NAU8824_TDM_DACL_RX_MASK | NAU8824_TDM_DACR_RX_MASK |
+ NAU8824_TDM_TX_MASK, ctrl_val);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_LEFT_TIME_SLOT,
+ NAU8824_TSLOT_L_MASK, tslot_l);
+
+ return 0;
+}
+
+/**
+ * nau8824_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8824_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8824_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8824_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8824_fll_apply(struct regmap *regmap,
+ struct nau8824_fll *fll_param)
+{
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK | NAU8824_CLK_MCLK_SRC_MASK,
+ NAU8824_CLK_SRC_MCLK | fll_param->mclk_src);
+ regmap_update_bits(regmap, NAU8824_REG_FLL1,
+ NAU8824_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(regmap, NAU8824_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8824_REG_FLL4,
+ NAU8824_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8824_FLL_REF_DIV_SFT);
+ /* select divided VCO input */
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_CLK_SW_MASK, NAU8824_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_MASK,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_SDM_EN, NAU8824_SDM_EN);
+ } else {
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_MASK, NAU8824_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_SDM_EN, 0);
+ }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8824_set_pll(struct snd_soc_component *component, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ struct nau8824_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8824_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(nau8824->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(nau8824->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8824_fll_apply(nau8824->regmap, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+ int clk_id, unsigned int freq)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ switch (clk_id) {
+ case NAU8824_CLK_DIS:
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, 0);
+ break;
+
+ case NAU8824_CLK_MCLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, 0);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_INTERNAL:
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, NAU8824_DCO_EN);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+ break;
+
+ case NAU8824_CLK_FLL_MCLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_MCLK);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_FLL_BLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_BLK);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_FLL_FS:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_FS);
+ nau8824_sema_release(nau8824);
+ break;
+
+ default:
+ dev_err(nau8824->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8824->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+
+ return 0;
+}
+
+static int nau8824_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ return nau8824_config_sysclk(nau8824, clk_id, freq);
+}
+
+static void nau8824_resume_setup(struct nau8824 *nau8824)
+{
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+ if (nau8824->irq) {
+ /* Clear all interruption status */
+ nau8824_int_status_clear_all(nau8824->regmap);
+ /* Enable jack detection at sleep mode, insertion detection,
+ * and ejection detection.
+ */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, 0);
+ }
+}
+
+static int nau8824_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(nau8824->dapm) == SND_SOC_BIAS_OFF) {
+ /* Setup codec configuration after resume */
+ nau8824_resume_setup(nau8824);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8824_component_probe(struct snd_soc_component *component)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ nau8824->dapm = dapm;
+
+ return 0;
+}
+
+static int __maybe_unused nau8824_suspend(struct snd_soc_component *component)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+
+ if (nau8824->irq) {
+ disable_irq(nau8824->irq);
+ snd_soc_dapm_force_bias_level(nau8824->dapm, SND_SOC_BIAS_OFF);
+ }
+ regcache_cache_only(nau8824->regmap, true);
+ regcache_mark_dirty(nau8824->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8824_resume(struct snd_soc_component *component)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ regcache_cache_only(nau8824->regmap, false);
+ regcache_sync(nau8824->regmap);
+ if (nau8824->irq) {
+ /* Hold semaphore to postpone playback happening
+ * until jack detection done.
+ */
+ nau8824->resume_lock = true;
+ ret = nau8824_sema_acquire(nau8824, 0);
+ if (ret)
+ nau8824->resume_lock = false;
+ enable_irq(nau8824->irq);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8824_component_driver = {
+ .probe = nau8824_component_probe,
+ .set_sysclk = nau8824_set_sysclk,
+ .set_pll = nau8824_set_pll,
+ .set_bias_level = nau8824_set_bias_level,
+ .suspend = nau8824_suspend,
+ .resume = nau8824_resume,
+ .controls = nau8824_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8824_snd_controls),
+ .dapm_widgets = nau8824_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8824_dapm_widgets),
+ .dapm_routes = nau8824_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8824_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops nau8824_dai_ops = {
+ .startup = nau8824_dai_startup,
+ .hw_params = nau8824_hw_params,
+ .set_fmt = nau8824_set_fmt,
+ .set_tdm_slot = nau8824_set_tdm_slot,
+};
+
+#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8824_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8824_dai = {
+ .name = NAU8824_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8824_RATES,
+ .formats = NAU8824_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8824_RATES,
+ .formats = NAU8824_FORMATS,
+ },
+ .ops = &nau8824_dai_ops,
+};
+
+static const struct regmap_config nau8824_regmap_config = {
+ .val_bits = NAU8824_REG_ADDR_LEN,
+ .reg_bits = NAU8824_REG_DATA_LEN,
+
+ .max_register = NAU8824_REG_MAX,
+ .readable_reg = nau8824_readable_reg,
+ .writeable_reg = nau8824_writeable_reg,
+ .volatile_reg = nau8824_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8824_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8824_reg_defaults),
+};
+
+/**
+ * nau8824_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8824_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ nau8824->jack = jack;
+ /* Initiate jack detection work queue */
+ INIT_WORK(&nau8824->jdet_work, nau8824_jdet_work);
+ ret = devm_request_threaded_irq(nau8824->dev, nau8824->irq, NULL,
+ nau8824_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8824", nau8824);
+ if (ret) {
+ dev_err(nau8824->dev, "Cannot request irq %d (%d)\n",
+ nau8824->irq, ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nau8824_enable_jack_detect);
+
+static void nau8824_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+}
+
+static void nau8824_setup_buttons(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_TRACKING_GAIN_MASK,
+ nau8824->sar_voltage << NAU8824_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_COMPARE_TIME_MASK,
+ nau8824->sar_compare_time << NAU8824_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_SAMPLING_TIME_MASK,
+ nau8824->sar_sampling_time << NAU8824_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_LEVELS_NR_MASK,
+ (nau8824->sar_threshold_num - 1) << NAU8824_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_HYSTERESIS_MASK,
+ nau8824->sar_hysteresis << NAU8824_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_SHORTKEY_DEBOUNCE_MASK,
+ nau8824->key_debounce << NAU8824_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_1,
+ (nau8824->sar_threshold[0] << 8) | nau8824->sar_threshold[1]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_2,
+ (nau8824->sar_threshold[2] << 8) | nau8824->sar_threshold[3]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_3,
+ (nau8824->sar_threshold[4] << 8) | nau8824->sar_threshold[5]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_4,
+ (nau8824->sar_threshold[6] << 8) | nau8824->sar_threshold[7]);
+}
+
+static void nau8824_init_regs(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Enable Bias/VMID/VMID Tieoff */
+ regmap_update_bits(regmap, NAU8824_REG_BIAS_ADJ,
+ NAU8824_VMID | NAU8824_VMID_SEL_MASK, NAU8824_VMID |
+ (nau8824->vref_impedance << NAU8824_VMID_SEL_SFT));
+ regmap_update_bits(regmap, NAU8824_REG_BOOST,
+ NAU8824_GLOBAL_BIAS_EN, NAU8824_GLOBAL_BIAS_EN);
+ mdelay(2);
+ regmap_update_bits(regmap, NAU8824_REG_MIC_BIAS,
+ NAU8824_MICBIAS_VOLTAGE_MASK, nau8824->micbias_voltage);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8824_REG_BOOST,
+ NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+ NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN,
+ NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+ NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN);
+ /* Scaling for ADC and DAC clock */
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_ADC_SRC_MASK | NAU8824_CLK_DAC_SRC_MASK,
+ (0x1 << NAU8824_CLK_ADC_SRC_SFT) |
+ (0x1 << NAU8824_CLK_DAC_SRC_SFT));
+ regmap_update_bits(regmap, NAU8824_REG_DAC_MUTE_CTRL,
+ NAU8824_DAC_ZC_EN, NAU8824_DAC_ZC_EN);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+ NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+ NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN,
+ NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+ NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+ NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_GATING_ENA,
+ NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+ NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+ NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+ NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN,
+ NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+ NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+ NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+ NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_TIMER_MASK,
+ 0x20 << NAU8824_CLASSG_TIMER_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_TRIM_SETTINGS,
+ NAU8824_DRV_CURR_INC, NAU8824_DRV_CURR_INC);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+ NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL,
+ NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+ NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL);
+ /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+ * signal to avoid any glitches due to power up transients in both
+ * the analog and digital DAC circuit.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+ /* Config L/R channel */
+ regmap_update_bits(regmap, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+ NAU8824_DAC_CH0_SEL_MASK, NAU8824_DAC_CH0_SEL_I2S0);
+ regmap_update_bits(regmap, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+ NAU8824_DAC_CH1_SEL_MASK, NAU8824_DAC_CH1_SEL_I2S1);
+ regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN,
+ NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN);
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_ADC_FILTER_CTRL,
+ NAU8824_ADC_SYNC_DOWN_MASK, NAU8824_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8824_REG_DAC_FILTER_CTRL_1,
+ NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_MASK,
+ NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_64);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8824_REG_RDAC,
+ NAU8824_RDAC_CLK_DELAY_MASK | NAU8824_RDAC_VREF_MASK,
+ (0x2 << NAU8824_RDAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8824_RDAC_VREF_SFT));
+ /* PGA input mode selection */
+ regmap_update_bits(regmap, NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN,
+ NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN);
+ /* Digital microphone control */
+ regmap_update_bits(regmap, NAU8824_REG_ANALOG_CONTROL_1,
+ NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST,
+ NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST);
+ regmap_update_bits(regmap, NAU8824_REG_JACK_DET_CTRL,
+ NAU8824_JACK_LOGIC,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8824->jkdet_polarity ? 0 : NAU8824_JACK_LOGIC);
+ regmap_update_bits(regmap,
+ NAU8824_REG_JACK_DET_CTRL, NAU8824_JACK_EJECT_DT_MASK,
+ (nau8824->jack_eject_debounce << NAU8824_JACK_EJECT_DT_SFT));
+ if (nau8824->sar_threshold_num)
+ nau8824_setup_buttons(nau8824);
+}
+
+static int nau8824_setup_irq(struct nau8824 *nau8824)
+{
+ /* Disable interruption before codec initiation done */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+
+ return 0;
+}
+
+static void nau8824_print_device_properties(struct nau8824 *nau8824)
+{
+ struct device *dev = nau8824->dev;
+ int i;
+
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8824->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8824->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8824->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8824->sar_threshold_num);
+ for (i = 0; i < nau8824->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%x\n", i,
+ nau8824->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8824->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8824->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8824->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8824->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8824->key_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8824->jack_eject_debounce);
+}
+
+static int nau8824_read_device_properties(struct device *dev,
+ struct nau8824 *nau8824) {
+ int ret;
+
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8824->jkdet_polarity);
+ if (ret)
+ nau8824->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8824->micbias_voltage);
+ if (ret)
+ nau8824->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8824->vref_impedance);
+ if (ret)
+ nau8824->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8824->sar_threshold_num);
+ if (ret)
+ nau8824->sar_threshold_num = 4;
+ ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8824->sar_threshold, nau8824->sar_threshold_num);
+ if (ret) {
+ nau8824->sar_threshold[0] = 0x0a;
+ nau8824->sar_threshold[1] = 0x14;
+ nau8824->sar_threshold[2] = 0x26;
+ nau8824->sar_threshold[3] = 0x73;
+ }
+ ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8824->sar_hysteresis);
+ if (ret)
+ nau8824->sar_hysteresis = 0;
+ ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8824->sar_voltage);
+ if (ret)
+ nau8824->sar_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8824->sar_compare_time);
+ if (ret)
+ nau8824->sar_compare_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8824->sar_sampling_time);
+ if (ret)
+ nau8824->sar_sampling_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8824->key_debounce);
+ if (ret)
+ nau8824->key_debounce = 0;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8824->jack_eject_debounce);
+ if (ret)
+ nau8824->jack_eject_debounce = 1;
+
+ nau8824->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(nau8824->mclk))
+ return PTR_ERR(nau8824->mclk);
+
+ return 0;
+}
+
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id nau8824_quirk_table[] = {
+ {
+ /* Cyberbook T116 rugged tablet */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH |
+ NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* CUBE iwork8 Air */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ },
+ .driver_data = (void *)(NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* Pipo W2S */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PIPO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "W2S"),
+ },
+ .driver_data = (void *)(NAU8824_MONO_SPEAKER),
+ },
+ {
+ /* Positivo CW14Q01P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH),
+ },
+ {
+ /* Positivo K1424G */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "K1424G"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH),
+ },
+ {
+ /* Positivo N14ZP74G */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
+ DMI_MATCH(DMI_BOARD_NAME, "N14ZP74G"),
+ },
+ .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH),
+ },
+ {}
+};
+
+static void nau8824_check_quirks(void)
+{
+ const struct dmi_system_id *dmi_id;
+
+ if (quirk_override != -1) {
+ nau8824_quirk = quirk_override;
+ return;
+ }
+
+ dmi_id = dmi_first_match(nau8824_quirk_table);
+ if (dmi_id)
+ nau8824_quirk = (unsigned long)dmi_id->driver_data;
+}
+
+const char *nau8824_components(void)
+{
+ nau8824_check_quirks();
+
+ if (nau8824_quirk & NAU8824_MONO_SPEAKER)
+ return "cfg-spk:1";
+ else
+ return "cfg-spk:2";
+}
+EXPORT_SYMBOL_GPL(nau8824_components);
+
+static int nau8824_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8824 *nau8824 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8824) {
+ nau8824 = devm_kzalloc(dev, sizeof(*nau8824), GFP_KERNEL);
+ if (!nau8824)
+ return -ENOMEM;
+ ret = nau8824_read_device_properties(dev, nau8824);
+ if (ret)
+ return ret;
+ }
+ i2c_set_clientdata(i2c, nau8824);
+
+ nau8824->regmap = devm_regmap_init_i2c(i2c, &nau8824_regmap_config);
+ if (IS_ERR(nau8824->regmap))
+ return PTR_ERR(nau8824->regmap);
+ nau8824->resume_lock = false;
+ nau8824->dev = dev;
+ nau8824->irq = i2c->irq;
+ sema_init(&nau8824->jd_sem, 1);
+
+ nau8824_check_quirks();
+
+ if (nau8824_quirk & NAU8824_JD_ACTIVE_HIGH)
+ nau8824->jkdet_polarity = 0;
+
+ nau8824_print_device_properties(nau8824);
+
+ ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8824: %d\n",
+ ret);
+ return ret;
+ }
+ nau8824_reset_chip(nau8824->regmap);
+ nau8824_init_regs(nau8824);
+
+ if (i2c->irq)
+ nau8824_setup_irq(nau8824);
+
+ return devm_snd_soc_register_component(dev,
+ &nau8824_component_driver, &nau8824_dai, 1);
+}
+
+static const struct i2c_device_id nau8824_i2c_ids[] = {
+ { "nau8824" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8824_of_ids[] = {
+ { .compatible = "nuvoton,nau8824", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8824_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8824_acpi_match[] = {
+ { "10508824", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8824_acpi_match);
+#endif
+
+static struct i2c_driver nau8824_i2c_driver = {
+ .driver = {
+ .name = "nau8824",
+ .of_match_table = of_match_ptr(nau8824_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8824_acpi_match),
+ },
+ .probe = nau8824_i2c_probe,
+ .id_table = nau8824_i2c_ids,
+};
+module_i2c_driver(nau8824_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC NAU88L24 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
new file mode 100644
index 000000000000..d8e19515133c
--- /dev/null
+++ b/sound/soc/codecs/nau8824.h
@@ -0,0 +1,478 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#ifndef __NAU8824_H__
+#define __NAU8824_H__
+
+#define NAU8824_REG_RESET 0x00
+#define NAU8824_REG_ENA_CTRL 0x01
+#define NAU8824_REG_CLK_GATING_ENA 0x02
+#define NAU8824_REG_CLK_DIVIDER 0x03
+#define NAU8824_REG_FLL1 0x04
+#define NAU8824_REG_FLL2 0x05
+#define NAU8824_REG_FLL3 0x06
+#define NAU8824_REG_FLL4 0x07
+#define NAU8824_REG_FLL5 0x08
+#define NAU8824_REG_FLL6 0x09
+#define NAU8824_REG_FLL_VCO_RSV 0x0A
+#define NAU8824_REG_JACK_DET_CTRL 0x0D
+#define NAU8824_REG_INTERRUPT_SETTING_1 0x0F
+#define NAU8824_REG_IRQ 0x10
+#define NAU8824_REG_CLEAR_INT_REG 0x11
+#define NAU8824_REG_INTERRUPT_SETTING 0x12
+#define NAU8824_REG_SAR_ADC 0x13
+#define NAU8824_REG_VDET_COEFFICIENT 0x14
+#define NAU8824_REG_VDET_THRESHOLD_1 0x15
+#define NAU8824_REG_VDET_THRESHOLD_2 0x16
+#define NAU8824_REG_VDET_THRESHOLD_3 0x17
+#define NAU8824_REG_VDET_THRESHOLD_4 0x18
+#define NAU8824_REG_GPIO_SEL 0x1A
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_1 0x1C
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_2 0x1D
+#define NAU8824_REG_PORT0_LEFT_TIME_SLOT 0x1E
+#define NAU8824_REG_PORT0_RIGHT_TIME_SLOT 0x1F
+#define NAU8824_REG_TDM_CTRL 0x20
+#define NAU8824_REG_ADC_HPF_FILTER 0x23
+#define NAU8824_REG_ADC_FILTER_CTRL 0x24
+#define NAU8824_REG_DAC_FILTER_CTRL_1 0x25
+#define NAU8824_REG_DAC_FILTER_CTRL_2 0x26
+#define NAU8824_REG_NOTCH_FILTER_1 0x27
+#define NAU8824_REG_NOTCH_FILTER_2 0x28
+#define NAU8824_REG_EQ1_LOW 0x29
+#define NAU8824_REG_EQ2_EQ3 0x2A
+#define NAU8824_REG_EQ4_EQ5 0x2B
+#define NAU8824_REG_ADC_CH0_DGAIN_CTRL 0x2D
+#define NAU8824_REG_ADC_CH1_DGAIN_CTRL 0x2E
+#define NAU8824_REG_ADC_CH2_DGAIN_CTRL 0x2F
+#define NAU8824_REG_ADC_CH3_DGAIN_CTRL 0x30
+#define NAU8824_REG_DAC_MUTE_CTRL 0x31
+#define NAU8824_REG_DAC_CH0_DGAIN_CTRL 0x32
+#define NAU8824_REG_DAC_CH1_DGAIN_CTRL 0x33
+#define NAU8824_REG_ADC_TO_DAC_ST 0x34
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 0x38
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH01 0x39
+#define NAU8824_REG_DRC_SLOPE_ADC_CH01 0x3A
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH01 0x3B
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH23 0x3C
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH23 0x3D
+#define NAU8824_REG_DRC_SLOPE_ADC_CH23 0x3E
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH23 0x3F
+#define NAU8824_REG_DRC_GAINL_ADC0 0x40
+#define NAU8824_REG_DRC_GAINL_ADC1 0x41
+#define NAU8824_REG_DRC_GAINL_ADC2 0x42
+#define NAU8824_REG_DRC_GAINL_ADC3 0x43
+#define NAU8824_REG_DRC_KNEE_IP12_DAC 0x45
+#define NAU8824_REG_DRC_KNEE_IP34_DAC 0x46
+#define NAU8824_REG_DRC_SLOPE_DAC 0x47
+#define NAU8824_REG_DRC_ATKDCY_DAC 0x48
+#define NAU8824_REG_DRC_GAIN_DAC_CH0 0x49
+#define NAU8824_REG_DRC_GAIN_DAC_CH1 0x4A
+#define NAU8824_REG_MODE 0x4C
+#define NAU8824_REG_MODE1 0x4D
+#define NAU8824_REG_MODE2 0x4E
+#define NAU8824_REG_CLASSG 0x50
+#define NAU8824_REG_OTP_EFUSE 0x51
+#define NAU8824_REG_OTPDOUT_1 0x53
+#define NAU8824_REG_OTPDOUT_2 0x54
+#define NAU8824_REG_MISC_CTRL 0x55
+#define NAU8824_REG_I2C_TIMEOUT 0x56
+#define NAU8824_REG_TEST_MODE 0x57
+#define NAU8824_REG_I2C_DEVICE_ID 0x58
+#define NAU8824_REG_SAR_ADC_DATA_OUT 0x59
+#define NAU8824_REG_BIAS_ADJ 0x66
+#define NAU8824_REG_PGA_GAIN 0x67
+#define NAU8824_REG_TRIM_SETTINGS 0x68
+#define NAU8824_REG_ANALOG_CONTROL_1 0x69
+#define NAU8824_REG_ANALOG_CONTROL_2 0x6A
+#define NAU8824_REG_ENABLE_LO 0x6B
+#define NAU8824_REG_GAIN_LO 0x6C
+#define NAU8824_REG_CLASSD_GAIN_1 0x6D
+#define NAU8824_REG_CLASSD_GAIN_2 0x6E
+#define NAU8824_REG_ANALOG_ADC_1 0x71
+#define NAU8824_REG_ANALOG_ADC_2 0x72
+#define NAU8824_REG_RDAC 0x73
+#define NAU8824_REG_MIC_BIAS 0x74
+#define NAU8824_REG_HS_VOLUME_CONTROL 0x75
+#define NAU8824_REG_BOOST 0x76
+#define NAU8824_REG_FEPGA 0x77
+#define NAU8824_REG_FEPGA_II 0x78
+#define NAU8824_REG_FEPGA_SE 0x79
+#define NAU8824_REG_FEPGA_ATTENUATION 0x7A
+#define NAU8824_REG_ATT_PORT0 0x7B
+#define NAU8824_REG_ATT_PORT1 0x7C
+#define NAU8824_REG_POWER_UP_CONTROL 0x7F
+#define NAU8824_REG_CHARGE_PUMP_CONTROL 0x80
+#define NAU8824_REG_CHARGE_PUMP_INPUT 0x81
+#define NAU8824_REG_MAX NAU8824_REG_CHARGE_PUMP_INPUT
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8824_REG_ADDR_LEN 16
+#define NAU8824_REG_DATA_LEN 16
+
+
+/* ENA_CTRL (0x1) */
+#define NAU8824_DMIC_LCH_EDGE_CH23 (0x1 << 12)
+#define NAU8824_DMIC_LCH_EDGE_CH01 (0x1 << 11)
+#define NAU8824_JD_SLEEP_MODE (0x1 << 10)
+#define NAU8824_ADC_CH3_DMIC_SFT 9
+#define NAU8824_ADC_CH3_DMIC_EN (0x1 << NAU8824_ADC_CH3_DMIC_SFT)
+#define NAU8824_ADC_CH2_DMIC_SFT 8
+#define NAU8824_ADC_CH2_DMIC_EN (0x1 << NAU8824_ADC_CH2_DMIC_SFT)
+#define NAU8824_ADC_CH1_DMIC_SFT 7
+#define NAU8824_ADC_CH1_DMIC_EN (0x1 << NAU8824_ADC_CH1_DMIC_SFT)
+#define NAU8824_ADC_CH0_DMIC_SFT 6
+#define NAU8824_ADC_CH0_DMIC_EN (0x1 << NAU8824_ADC_CH0_DMIC_SFT)
+#define NAU8824_DAC_CH1_EN (0x1 << 5)
+#define NAU8824_DAC_CH0_EN (0x1 << 4)
+#define NAU8824_ADC_CH3_EN (0x1 << 3)
+#define NAU8824_ADC_CH2_EN (0x1 << 2)
+#define NAU8824_ADC_CH1_EN (0x1 << 1)
+#define NAU8824_ADC_CH0_EN 0x1
+
+/* CLK_GATING_ENA (0x02) */
+#define NAU8824_CLK_ADC_CH23_EN (0x1 << 15)
+#define NAU8824_CLK_ADC_CH01_EN (0x1 << 14)
+#define NAU8824_CLK_DAC_CH1_EN (0x1 << 13)
+#define NAU8824_CLK_DAC_CH0_EN (0x1 << 12)
+#define NAU8824_CLK_I2S_EN (0x1 << 7)
+#define NAU8824_CLK_GAIN_EN (0x1 << 5)
+#define NAU8824_CLK_SAR_EN (0x1 << 3)
+#define NAU8824_CLK_DMIC_CH23_EN (0x1 << 1)
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8824_CLK_SRC_SFT 15
+#define NAU8824_CLK_SRC_MASK (1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_VCO (1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_MCLK (0 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_MCLK_SRC_MASK (0xf << 0)
+#define NAU8824_CLK_DMIC_SRC_SFT 10
+#define NAU8824_CLK_DMIC_SRC_MASK (0x7 << NAU8824_CLK_DMIC_SRC_SFT)
+#define NAU8824_CLK_ADC_SRC_SFT 6
+#define NAU8824_CLK_ADC_SRC_MASK (0x3 << NAU8824_CLK_ADC_SRC_SFT)
+#define NAU8824_CLK_DAC_SRC_SFT 4
+#define NAU8824_CLK_DAC_SRC_MASK (0x3 << NAU8824_CLK_DAC_SRC_SFT)
+
+/* FLL1 (0x04) */
+#define NAU8824_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8824_FLL_INTEGER_MASK (0x3ff << 0)
+#define NAU8824_FLL_CLK_SRC_SFT 10
+#define NAU8824_FLL_CLK_SRC_MASK (0x3 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_MCLK (0 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_BLK (0x2 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_FS (0x3 << NAU8824_FLL_CLK_SRC_SFT)
+
+/* FLL4 (0x07) */
+#define NAU8824_FLL_REF_DIV_SFT 10
+#define NAU8824_FLL_REF_DIV_MASK (0x3 << NAU8824_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8824_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8824_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8824_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8824_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8824_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8824_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8824_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8824_FLL_FTR_SW_FILTER (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8824_DCO_EN (0x1 << 15)
+#define NAU8824_SDM_EN (0x1 << 14)
+
+/* IRQ (0x10) */
+#define NAU8824_SHORT_CIRCUIT_IRQ (0x1 << 7)
+#define NAU8824_IMPEDANCE_MEAS_IRQ (0x1 << 6)
+#define NAU8824_KEY_RELEASE_IRQ (0x1 << 5)
+#define NAU8824_KEY_LONG_PRESS_IRQ (0x1 << 4)
+#define NAU8824_KEY_SHORT_PRESS_IRQ (0x1 << 3)
+#define NAU8824_JACK_EJECTION_DETECTED (0x1 << 1)
+#define NAU8824_JACK_INSERTION_DETECTED 0x1
+
+/* JACK_DET_CTRL (0x0D) */
+#define NAU8824_JACK_EJECT_DT_SFT 2
+#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT)
+#define NAU8824_JACK_LOGIC (0x1 << 1)
+
+
+/* INTERRUPT_SETTING_1 (0x0F) */
+#define NAU8824_IRQ_EJECT_EN (0x1 << 9)
+#define NAU8824_IRQ_INSERT_EN (0x1 << 8)
+
+/* INTERRUPT_SETTING (0x12) */
+#define NAU8824_IRQ_KEY_RELEASE_DIS (0x1 << 5)
+#define NAU8824_IRQ_KEY_SHORT_PRESS_DIS (0x1 << 3)
+#define NAU8824_IRQ_EJECT_DIS (0x1 << 1)
+#define NAU8824_IRQ_INSERT_DIS 0x1
+
+/* SAR_ADC (0x13) */
+#define NAU8824_SAR_ADC_EN_SFT 12
+#define NAU8824_SAR_TRACKING_GAIN_SFT 8
+#define NAU8824_SAR_TRACKING_GAIN_MASK (0x7 << NAU8824_SAR_TRACKING_GAIN_SFT)
+#define NAU8824_SAR_COMPARE_TIME_SFT 2
+#define NAU8824_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8824_SAR_SAMPLING_TIME_SFT 0
+#define NAU8824_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* VDET_COEFFICIENT (0x14) */
+#define NAU8824_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8824_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8824_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8824_LEVELS_NR_SFT 8
+#define NAU8824_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8824_HYSTERESIS_SFT 0
+#define NAU8824_HYSTERESIS_MASK 0xf
+
+/* PORT0_I2S_PCM_CTRL_1 (0x1C) */
+#define NAU8824_I2S_BP_SFT 7
+#define NAU8824_I2S_BP_MASK (1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_BP_INV (1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_PCMB_SFT 6
+#define NAU8824_I2S_PCMB_EN (1 << NAU8824_I2S_PCMB_SFT)
+#define NAU8824_I2S_DL_SFT 2
+#define NAU8824_I2S_DL_MASK (0x3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_16 (0 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_20 (1 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_24 (2 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_32 (3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DF_MASK 0x3
+#define NAU8824_I2S_DF_RIGTH 0
+#define NAU8824_I2S_DF_LEFT 1
+#define NAU8824_I2S_DF_I2S 2
+#define NAU8824_I2S_DF_PCM_AB 3
+
+
+/* PORT0_I2S_PCM_CTRL_2 (0x1D) */
+#define NAU8824_I2S_LRC_DIV_SFT 12
+#define NAU8824_I2S_LRC_DIV_MASK (0x3 << NAU8824_I2S_LRC_DIV_SFT)
+#define NAU8824_I2S_MS_SFT 3
+#define NAU8824_I2S_MS_MASK (1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_MASTER (1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_BLK_DIV_MASK 0x7
+
+/* PORT0_LEFT_TIME_SLOT (0x1E) */
+#define NAU8824_TSLOT_L_MASK 0x3ff
+
+/* TDM_CTRL (0x20) */
+#define NAU8824_TDM_MODE (0x1 << 15)
+#define NAU8824_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8824_TDM_DACL_RX_SFT 6
+#define NAU8824_TDM_DACL_RX_MASK (0x3 << NAU8824_TDM_DACL_RX_SFT)
+#define NAU8824_TDM_DACR_RX_SFT 4
+#define NAU8824_TDM_DACR_RX_MASK (0x3 << NAU8824_TDM_DACR_RX_SFT)
+#define NAU8824_TDM_TX_MASK 0xf
+
+/* ADC_FILTER_CTRL (0x24) */
+#define NAU8824_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8824_ADC_SYNC_DOWN_32 0
+#define NAU8824_ADC_SYNC_DOWN_64 1
+#define NAU8824_ADC_SYNC_DOWN_128 2
+#define NAU8824_ADC_SYNC_DOWN_256 3
+
+/* DAC_FILTER_CTRL_1 (0x25) */
+#define NAU8824_DAC_CICCLP_OFF (0x1 << 7)
+#define NAU8824_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8824_DAC_OVERSAMPLE_64 0
+#define NAU8824_DAC_OVERSAMPLE_256 1
+#define NAU8824_DAC_OVERSAMPLE_128 2
+#define NAU8824_DAC_OVERSAMPLE_32 4
+
+/* DAC_MUTE_CTRL (0x31) */
+#define NAU8824_DAC_CH01_MIX 0x3
+#define NAU8824_DAC_ZC_EN (0x1 << 11)
+
+/* DAC_CH0_DGAIN_CTRL (0x32) */
+#define NAU8824_DAC_CH0_SEL_SFT 9
+#define NAU8824_DAC_CH0_SEL_MASK (0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S0 (0x0 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S1 (0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_VOL_MASK 0x1ff
+
+/* DAC_CH1_DGAIN_CTRL (0x33) */
+#define NAU8824_DAC_CH1_SEL_SFT 9
+#define NAU8824_DAC_CH1_SEL_MASK (0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S0 (0x0 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S1 (0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_VOL_MASK 0x1ff
+
+/* CLASSG (0x50) */
+#define NAU8824_CLASSG_TIMER_SFT 8
+#define NAU8824_CLASSG_TIMER_MASK (0x3f << NAU8824_CLASSG_TIMER_SFT)
+#define NAU8824_CLASSG_LDAC_EN_SFT 2
+#define NAU8824_CLASSG_RDAC_EN_SFT 1
+#define NAU8824_CLASSG_EN_SFT 0
+
+/* SAR_ADC_DATA_OUT (0x59) */
+#define NAU8824_SAR_ADC_DATA_MASK 0xff
+
+/* BIAS_ADJ (0x66) */
+#define NAU8824_VMID (1 << 6)
+#define NAU8824_VMID_SEL_SFT 4
+#define NAU8824_VMID_SEL_MASK (3 << NAU8824_VMID_SEL_SFT)
+#define NAU8824_DMIC2_EN_SFT 3
+#define NAU8824_DMIC1_EN_SFT 2
+
+/* TRIM_SETTINGS (0x68) */
+#define NAU8824_DRV_CURR_INC (1 << 15)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8824_DMIC_CLK_DRV_STRG (1 << 3)
+#define NAU8824_DMIC_CLK_SLEW_FAST (0x7)
+
+/* ANALOG_CONTROL_2 (0x6A) */
+#define NAU8824_CLASSD_CLAMP_DIS_SFT 3
+#define NAU8824_CLASSD_CLAMP_DIS (0x1 << NAU8824_CLASSD_CLAMP_DIS_SFT)
+
+/* ENABLE_LO (0x6B) */
+#define NAU8824_TEST_DAC_SFT 14
+#define NAU8824_TEST_DAC_EN (0x3 << NAU8824_TEST_DAC_SFT)
+#define NAU8824_DACL_HPR_EN_SFT 3
+#define NAU8824_DACL_HPR_EN (0x1 << NAU8824_DACL_HPR_EN_SFT)
+#define NAU8824_DACR_HPR_EN_SFT 2
+#define NAU8824_DACR_HPR_EN (0x1 << NAU8824_DACR_HPR_EN_SFT)
+#define NAU8824_DACR_HPL_EN_SFT 1
+#define NAU8824_DACR_HPL_EN (0x1 << NAU8824_DACR_HPL_EN_SFT)
+#define NAU8824_DACL_HPL_EN_SFT 0
+#define NAU8824_DACL_HPL_EN 0x1
+
+/* CLASSD_GAIN_1 (0x6D) */
+#define NAU8824_CLASSD_GAIN_1R_SFT 8
+#define NAU8824_CLASSD_GAIN_1R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT 7
+#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_1L_MASK 0x1f
+
+/* CLASSD_GAIN_2 (0x6E) */
+#define NAU8824_CLASSD_GAIN_2R_SFT 8
+#define NAU8824_CLASSD_GAIN_2R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT 7
+#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_2L_MASK 0x1f
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8824_ADCR_EN_SFT 7
+#define NAU8824_ADCL_EN_SFT 6
+
+/* RDAC (0x73) */
+#define NAU8824_DACR_EN_SFT 13
+#define NAU8824_DACL_EN_SFT 12
+#define NAU8824_DACR_CLK_SFT 9
+#define NAU8824_DACL_CLK_SFT 8
+#define NAU8824_RDAC_CLK_DELAY_SFT 4
+#define NAU8824_RDAC_CLK_DELAY_MASK (0x7 << NAU8824_RDAC_CLK_DELAY_SFT)
+#define NAU8824_RDAC_VREF_SFT 2
+#define NAU8824_RDAC_VREF_MASK (0x3 << NAU8824_RDAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8824_MICBIAS_JKSLV (1 << 14)
+#define NAU8824_MICBIAS_JKR2 (1 << 12)
+#define NAU8824_MICBIAS_POWERUP_SFT 8
+#define NAU8824_MICBIAS_VOLTAGE_SFT 0
+#define NAU8824_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8824_PRECHARGE_DIS (0x1 << 13)
+#define NAU8824_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8824_HP_BOOST_DIS_SFT 9
+#define NAU8824_HP_BOOST_DIS (0x1 << NAU8824_HP_BOOST_DIS_SFT)
+#define NAU8824_HP_BOOST_G_DIS_SFT 8
+#define NAU8824_HP_BOOST_G_DIS (0x1 << NAU8824_HP_BOOST_G_DIS_SFT)
+#define NAU8824_SHORT_SHUTDOWN_DIG_EN (1 << 7)
+#define NAU8824_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8824_FEPGA_MODER_SHORT_SFT 7
+#define NAU8824_FEPGA_MODER_SHORT_EN (0x1 << NAU8824_FEPGA_MODER_SHORT_SFT)
+#define NAU8824_FEPGA_MODER_MIC2_SFT 5
+#define NAU8824_FEPGA_MODER_MIC2_EN (0x1 << NAU8824_FEPGA_MODER_MIC2_SFT)
+#define NAU8824_FEPGA_MODER_HSMIC_SFT 4
+#define NAU8824_FEPGA_MODER_HSMIC_EN (0x1 << NAU8824_FEPGA_MODER_HSMIC_SFT)
+#define NAU8824_FEPGA_MODEL_SHORT_SFT 3
+#define NAU8824_FEPGA_MODEL_SHORT_EN (0x1 << NAU8824_FEPGA_MODEL_SHORT_SFT)
+#define NAU8824_FEPGA_MODEL_MIC1_SFT 1
+#define NAU8824_FEPGA_MODEL_MIC1_EN (0x1 << NAU8824_FEPGA_MODEL_MIC1_SFT)
+#define NAU8824_FEPGA_MODEL_HSMIC_SFT 0
+#define NAU8824_FEPGA_MODEL_HSMIC_EN (0x1 << NAU8824_FEPGA_MODEL_HSMIC_SFT)
+
+/* FEPGA_II (0x78) */
+#define NAU8824_FEPGA_GAINR_SFT 5
+#define NAU8824_FEPGA_GAINR_MASK (0x1f << NAU8824_FEPGA_GAINR_SFT)
+#define NAU8824_FEPGA_GAINL_SFT 0
+#define NAU8824_FEPGA_GAINL_MASK 0x1f
+
+/* CHARGE_PUMP_CONTROL (0x80) */
+#define NAU8824_JAMNODCLOW (0x1 << 15)
+#define NAU8824_SPKR_PULL_DOWN (0x1 << 13)
+#define NAU8824_SPKL_PULL_DOWN (0x1 << 12)
+#define NAU8824_POWER_DOWN_DACR (0x1 << 9)
+#define NAU8824_POWER_DOWN_DACL (0x1 << 8)
+#define NAU8824_CHARGE_PUMP_EN_SFT 5
+#define NAU8824_CHARGE_PUMP_EN (0x1 << NAU8824_CHARGE_PUMP_EN_SFT)
+
+
+#define NAU8824_CODEC_DAI "nau8824-hifi"
+
+/* System Clock Source */
+enum {
+ NAU8824_CLK_DIS,
+ NAU8824_CLK_MCLK,
+ NAU8824_CLK_INTERNAL,
+ NAU8824_CLK_FLL_MCLK,
+ NAU8824_CLK_FLL_BLK,
+ NAU8824_CLK_FLL_FS,
+};
+
+struct nau8824 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct work_struct jdet_work;
+ struct semaphore jd_sem;
+ struct clk *mclk;
+ int fs;
+ int irq;
+ int resume_lock;
+ int micbias_voltage;
+ int vref_impedance;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_eject_debounce;
+};
+
+struct nau8824_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8824_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+struct nau8824_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+
+int nau8824_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+const char *nau8824_components(void);
+
+#endif /* _NAU8824_H */
+
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index c1b87c5800b1..dd3528537ae4 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Nuvoton NAU8825 audio codec driver
*
@@ -5,19 +6,19 @@
* Author: Anatol Pomozov <anatol@chromium.org>
* Copyright 2015 Nuvoton Technology Corp.
* Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/int_log.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/acpi.h>
#include <linux/math64.h>
+#include <linux/semaphore.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -30,14 +31,29 @@
#include "nau8825.h"
+
+#define NUVOTON_CODEC_DAI "nau8825-hifi"
+
#define NAU_FREF_MAX 13500000
-#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MAX 124000000
#define NAU_FVCO_MIN 90000000
+/* cross talk suppression detection */
+#define GAIN_AUGMENT 22500
+#define SIDETONE_BASE 207000
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825,
+ int clk_id, unsigned int freq);
+static bool nau8825_is_jack_inserted(struct regmap *regmap);
+
struct nau8825_fll {
int mclk_src;
int ratio;
int fll_frac;
+ int fll_frac_num;
int fll_int;
int clk_ref_div;
};
@@ -82,8 +98,30 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = {
{ 8, 0x3 },
};
+/* over sampling rate */
+struct nau8825_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+static const struct nau8825_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8825_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_ENA_CTRL, 0x00ff },
+ { NAU8825_REG_IIC_ADDR_SET, 0x0 },
{ NAU8825_REG_CLK_DIVIDER, 0x0050 },
{ NAU8825_REG_FLL1, 0x0 },
{ NAU8825_REG_FLL2, 0x3126 },
@@ -141,6 +179,8 @@ static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_CLASSG_CTRL, 0x0 },
{ NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
{ NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_FLL2_LOWER, 0x0 },
+ { NAU8825_REG_FLL2_UPPER, 0x0 },
{ NAU8825_REG_BIAS_ADJ, 0x0 },
{ NAU8825_REG_TRIM_SETTINGS, 0x0 },
{ NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
@@ -155,11 +195,601 @@ static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_CHARGE_PUMP, 0x0 },
};
+/* register backup table when cross talk detection */
+static struct reg_default nau8825_xtalk_baktab[] = {
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+ { NAU8825_REG_HSVOL_CTRL, 0 },
+ { NAU8825_REG_DACL_CTRL, 0x00cf },
+ { NAU8825_REG_DACR_CTRL, 0x02cf },
+};
+
+/* The regmap patch for Rev C */
+static const struct reg_sequence nau8825_regmap_patch[] = {
+ { NAU8825_REG_FLL2, 0x0000 },
+ { NAU8825_REG_FLL4, 0x8010 },
+ { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0800 },
+ { NAU8825_REG_DACL_CTRL, 0x00cf },
+ { NAU8825_REG_DACR_CTRL, 0x02cf },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 },
+ { NAU8825_REG_FLL2_LOWER, 0x26e9 },
+ { NAU8825_REG_FLL2_UPPER, 0x0031 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0220 },
+ { NAU8825_REG_MIC_BIAS, 0x0046 },
+};
+
+/**
+ * nau8825_sema_acquire - acquire the semaphore of nau88l25
+ * @nau8825: component to register the codec private data with
+ * @timeout: how long in jiffies to wait before failure or zero to wait
+ * until release
+ *
+ * Attempts to acquire the semaphore with number of jiffies. If no more
+ * tasks are allowed to acquire the semaphore, calling this function will
+ * put the task to sleep. If the semaphore is not released within the
+ * specified number of jiffies, this function returns.
+ * If the semaphore is not released within the specified number of jiffies,
+ * this function returns -ETIME. If the sleep is interrupted by a signal,
+ * this function will return -EINTR. It returns 0 if the semaphore was
+ * acquired successfully.
+ *
+ * Acquires the semaphore without jiffies. Try to acquire the semaphore
+ * atomically. Returns 0 if the semaphore has been acquired successfully
+ * or 1 if it cannot be acquired.
+ */
+static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout)
+{
+ int ret;
+
+ if (timeout) {
+ ret = down_timeout(&nau8825->xtalk_sem, timeout);
+ if (ret < 0)
+ dev_warn(nau8825->dev, "Acquire semaphore timeout\n");
+ } else {
+ ret = down_trylock(&nau8825->xtalk_sem);
+ if (ret)
+ dev_warn(nau8825->dev, "Acquire semaphore fail\n");
+ }
+
+ return ret;
+}
+
+/**
+ * nau8825_sema_release - release the semaphore of nau88l25
+ * @nau8825: component to register the codec private data with
+ *
+ * Release the semaphore which may be called from any context and
+ * even by tasks which have never called down().
+ */
+static inline void nau8825_sema_release(struct nau8825 *nau8825)
+{
+ up(&nau8825->xtalk_sem);
+}
+
+/**
+ * nau8825_sema_reset - reset the semaphore for nau88l25
+ * @nau8825: component to register the codec private data with
+ *
+ * Reset the counter of the semaphore. Call this function to restart
+ * a new round task management.
+ */
+static inline void nau8825_sema_reset(struct nau8825 *nau8825)
+{
+ nau8825->xtalk_sem.count = 1;
+}
+
+/**
+ * nau8825_hpvol_ramp - Ramp up the headphone volume change gradually to target level.
+ *
+ * @nau8825: component to register the codec private data with
+ * @vol_from: the volume to start up
+ * @vol_to: the target volume
+ * @step: the volume span to move on
+ *
+ * The headphone volume is from 0dB to minimum -54dB and -1dB per step.
+ * If the volume changes sharp, there is a pop noise heard in headphone. We
+ * provide the function to ramp up the volume up or down by delaying 10ms
+ * per step.
+ */
+static void nau8825_hpvol_ramp(struct nau8825 *nau8825,
+ unsigned int vol_from, unsigned int vol_to, unsigned int step)
+{
+ unsigned int value, volume, ramp_up, from, to;
+
+ if (vol_from == vol_to || step == 0) {
+ return;
+ } else if (vol_from < vol_to) {
+ ramp_up = true;
+ from = vol_from;
+ to = vol_to;
+ } else {
+ ramp_up = false;
+ from = vol_to;
+ to = vol_from;
+ }
+ /* only handle volume from 0dB to minimum -54dB */
+ if (to > NAU8825_HP_VOL_MIN)
+ to = NAU8825_HP_VOL_MIN;
+
+ for (volume = from; volume < to; volume += step) {
+ if (ramp_up)
+ value = volume;
+ else
+ value = to - volume + from;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+ NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+ (value << NAU8825_HPL_VOL_SFT) | value);
+ usleep_range(10000, 10500);
+ }
+ if (ramp_up)
+ value = to;
+ else
+ value = from;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+ NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+ (value << NAU8825_HPL_VOL_SFT) | value);
+}
+
+/**
+ * nau8825_intlog10_dec3 - Computes log10 of a value, rounding the result to 3 decimal places.
+ * @value: input for log10
+ *
+ * return log10(value) * 1000
+ */
+static u32 nau8825_intlog10_dec3(u32 value)
+{
+ return intlog10(value) / ((1 << 24) / 1000);
+}
+
+/**
+ * nau8825_xtalk_sidetone - computes cross talk suppression sidetone gain.
+ *
+ * @sig_org: orignal signal level
+ * @sig_cros: cross talk signal level
+ *
+ * The orignal and cross talk signal vlues need to be characterized.
+ * Once these values have been characterized, this sidetone value
+ * can be converted to decibel with the equation below.
+ * sidetone = 20 * log (original signal level / crosstalk signal level)
+ *
+ * return cross talk sidetone gain
+ */
+static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros)
+{
+ u32 gain, sidetone;
+
+ if (WARN_ON(sig_org == 0 || sig_cros == 0))
+ return 0;
+
+ sig_org = nau8825_intlog10_dec3(sig_org);
+ sig_cros = nau8825_intlog10_dec3(sig_cros);
+ if (sig_org >= sig_cros)
+ gain = (sig_org - sig_cros) * 20 + GAIN_AUGMENT;
+ else
+ gain = (sig_cros - sig_org) * 20 + GAIN_AUGMENT;
+ sidetone = SIDETONE_BASE - gain * 2;
+ sidetone /= 1000;
+
+ return sidetone;
+}
+
+static int nau8825_xtalk_baktab_index_by_reg(unsigned int reg)
+{
+ int index;
+
+ for (index = 0; index < ARRAY_SIZE(nau8825_xtalk_baktab); index++)
+ if (nau8825_xtalk_baktab[index].reg == reg)
+ return index;
+ return -EINVAL;
+}
+
+static void nau8825_xtalk_backup(struct nau8825 *nau8825)
+{
+ int i;
+
+ if (nau8825->xtalk_baktab_initialized)
+ return;
+
+ /* Backup some register values to backup table */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++)
+ regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+ &nau8825_xtalk_baktab[i].def);
+
+ nau8825->xtalk_baktab_initialized = true;
+}
+
+static void nau8825_xtalk_restore(struct nau8825 *nau8825, bool cause_cancel)
+{
+ int i, volume;
+
+ if (!nau8825->xtalk_baktab_initialized)
+ return;
+
+ /* Restore register values from backup table; When the driver restores
+ * the headphone volume in XTALK_DONE state, it needs recover to
+ * original level gradually with 3dB per step for less pop noise.
+ * Otherwise, the restore should do ASAP.
+ */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) {
+ if (!cause_cancel && nau8825_xtalk_baktab[i].reg ==
+ NAU8825_REG_HSVOL_CTRL) {
+ /* Ramping up the volume change to reduce pop noise */
+ volume = nau8825_xtalk_baktab[i].def &
+ NAU8825_HPR_VOL_MASK;
+ nau8825_hpvol_ramp(nau8825, 0, volume, 3);
+ continue;
+ }
+ regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+ nau8825_xtalk_baktab[i].def);
+ }
+
+ nau8825->xtalk_baktab_initialized = false;
+}
+
+static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825)
+{
+ /* Enable power of DAC path */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+ NAU8825_ENABLE_ADC | NAU8825_ENABLE_ADC_CLK |
+ NAU8825_ENABLE_DAC_CLK, NAU8825_ENABLE_DACR |
+ NAU8825_ENABLE_DACL | NAU8825_ENABLE_ADC |
+ NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK);
+ /* Prevent startup click by letting charge pump to ramp up and
+ * change bump enable
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN);
+ /* Enable clock sync of DAC and DAC clock */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN |
+ NAU8825_RDAC_FS_BCLK_ENB,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN);
+ /* Power up output driver with 2 stage */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L);
+ /* HP outputs not shouted to ground */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 0);
+ /* Enable HP boost driver */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_HP_BOOST_DIS, NAU8825_HP_BOOST_DIS);
+ /* Enable class G compare path to supply 1.8V or 0.9V. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLASSG_CTRL,
+ NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN,
+ NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN);
+}
+
+static void nau8825_xtalk_prepare_adc(struct nau8825 *nau8825)
+{
+ /* Power up left ADC and raise 5dB than Vmid for Vref */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB);
+}
+
+static void nau8825_xtalk_clock(struct nau8825 *nau8825)
+{
+ /* Recover FLL default value */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL1, 0x0);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, 0x3126);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL3, 0x0008);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL4, 0x0010);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL5, 0x0);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL6, 0x6000);
+ /* Enable internal VCO clock for detection signal generated */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+ NAU8825_DCO_EN);
+ /* Given specific clock frequency of internal clock to
+ * generate signal.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, 0x10);
+}
+
+static void nau8825_xtalk_prepare(struct nau8825 *nau8825)
+{
+ int volume, index;
+
+ /* Backup those registers changed by cross talk detection */
+ nau8825_xtalk_backup(nau8825);
+ /* Config IIS as master to output signal by codec */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER |
+ (0x2 << NAU8825_I2S_LRC_DIV_SFT) | 0x1);
+ /* Ramp up headphone volume to 0dB to get better performance and
+ * avoid pop noise in headphone.
+ */
+ index = nau8825_xtalk_baktab_index_by_reg(NAU8825_REG_HSVOL_CTRL);
+ if (index != -EINVAL) {
+ volume = nau8825_xtalk_baktab[index].def &
+ NAU8825_HPR_VOL_MASK;
+ nau8825_hpvol_ramp(nau8825, volume, 0, 3);
+ }
+ nau8825_xtalk_clock(nau8825);
+ nau8825_xtalk_prepare_dac(nau8825);
+ nau8825_xtalk_prepare_adc(nau8825);
+ /* Config channel path and digital gain */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_MASK | NAU8825_DACL_CH_VOL_MASK,
+ NAU8825_DACL_CH_SEL_L | 0xab);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_MASK | NAU8825_DACR_CH_VOL_MASK,
+ NAU8825_DACR_CH_SEL_R | 0xab);
+ /* Config cross talk parameters and generate the 23Hz sine wave with
+ * 1/16 full scale of signal level for impedance measurement.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+ NAU8825_IMM_THD_MASK | NAU8825_IMM_GEN_VOL_MASK |
+ NAU8825_IMM_CYC_MASK | NAU8825_IMM_DAC_SRC_MASK,
+ (0x9 << NAU8825_IMM_THD_SFT) | NAU8825_IMM_GEN_VOL_1_16th |
+ NAU8825_IMM_CYC_8192 | NAU8825_IMM_DAC_SRC_SIN);
+ /* RMS intrruption enable */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0);
+ /* Power up left and right DAC */
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+}
+
+static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
+{
+ /* Disable HP boost driver */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_HP_BOOST_DIS, 0);
+ /* HP outputs shouted to ground */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ /* Power down left and right DAC */
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+
+ /* Enable the TESTDAC and disable L/R HP impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP |
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ /* Power down output driver with 2 stage */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 0);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L, 0);
+ /* Disable clock sync of DAC and DAC clock */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, 0);
+ /* Disable charge pump ramp up function and change bump */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 0);
+ /* Disable power of DAC path */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+ NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK, 0);
+ if (!nau8825->irq)
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+}
+
+static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825)
+{
+ /* Power down left ADC and restore voltage to Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0);
+}
+
+static void nau8825_xtalk_clean(struct nau8825 *nau8825, bool cause_cancel)
+{
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+ nau8825_xtalk_clean_dac(nau8825);
+ nau8825_xtalk_clean_adc(nau8825);
+ /* Clear cross talk parameters and disable */
+ regmap_write(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, 0);
+ /* RMS intrruption disable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_RMS_EN, NAU8825_IRQ_RMS_EN);
+ /* Recover default value for IIS */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE);
+ /* Restore value of specific register for cross talk */
+ nau8825_xtalk_restore(nau8825, cause_cancel);
+}
+
+static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol)
+{
+ /* Apply ADC volume for better cross talk performance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ADC_DGAIN_CTRL,
+ NAU8825_ADC_DIG_VOL_MASK, vol);
+ /* Disables JKTIP(HPL) DAC channel for right to left measurement.
+ * Do it before sending signal in order to erase pop noise.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDACR_EN | NAU8825_BIAS_TESTDACL_EN,
+ NAU8825_BIAS_TESTDACL_EN);
+ switch (nau8825->xtalk_state) {
+ case NAU8825_XTALK_HPR_R2L:
+ /* Enable right headphone impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+ NAU8825_BIAS_HPR_IMP);
+ break;
+ case NAU8825_XTALK_HPL_R2L:
+ /* Enable left headphone impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+ NAU8825_BIAS_HPL_IMP);
+ break;
+ default:
+ break;
+ }
+ msleep(100);
+ /* Impedance measurement mode enable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+ NAU8825_IMM_EN, NAU8825_IMM_EN);
+}
+
+static void nau8825_xtalk_imm_stop(struct nau8825 *nau8825)
+{
+ /* Impedance measurement mode disable */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_IMM_MODE_CTRL, NAU8825_IMM_EN, 0);
+}
+
+/* The cross talk measurement function can reduce cross talk across the
+ * JKTIP(HPL) and JKR1(HPR) outputs which measures the cross talk signal
+ * level to determine what cross talk reduction gain is. This system works by
+ * sending a 23Hz -24dBV sine wave into the headset output DAC and through
+ * the PGA. The output of the PGA is then connected to an internal current
+ * sense which measures the attenuated 23Hz signal and passing the output to
+ * an ADC which converts the measurement to a binary code. With two separated
+ * measurement, one for JKR1(HPR) and the other JKTIP(HPL), measurement data
+ * can be separated read in IMM_RMS_L for HSR and HSL after each measurement.
+ * Thus, the measurement function has four states to complete whole sequence.
+ * 1. Prepare state : Prepare the resource for detection and transfer to HPR
+ * IMM stat to make JKR1(HPR) impedance measure.
+ * 2. HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer
+ * to HPL IMM state to make JKTIP(HPL) impedance measure.
+ * 3. HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and
+ * transfer to IMM state to determine suppression sidetone gain.
+ * 4. IMM state : Computes cross talk suppression sidetone gain with orignal
+ * and cross talk signal level. Apply this gain and then restore codec
+ * configuration. Then transfer to Done state for ending.
+ */
+static void nau8825_xtalk_measure(struct nau8825 *nau8825)
+{
+ u32 sidetone;
+
+ switch (nau8825->xtalk_state) {
+ case NAU8825_XTALK_PREPARE:
+ /* In prepare state, set up clock, intrruption, DAC path, ADC
+ * path and cross talk detection parameters for preparation.
+ */
+ nau8825_xtalk_prepare(nau8825);
+ msleep(280);
+ /* Trigger right headphone impedance detection */
+ nau8825->xtalk_state = NAU8825_XTALK_HPR_R2L;
+ nau8825_xtalk_imm_start(nau8825, 0x00d2);
+ break;
+ case NAU8825_XTALK_HPR_R2L:
+ /* In right headphone IMM state, read out right headphone
+ * impedance measure result, and then start up left side.
+ */
+ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+ &nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+ dev_dbg(nau8825->dev, "HPR_R2L imm: %x\n",
+ nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+ /* Disable then re-enable IMM mode to update */
+ nau8825_xtalk_imm_stop(nau8825);
+ /* Trigger left headphone impedance detection */
+ nau8825->xtalk_state = NAU8825_XTALK_HPL_R2L;
+ nau8825_xtalk_imm_start(nau8825, 0x00ff);
+ break;
+ case NAU8825_XTALK_HPL_R2L:
+ /* In left headphone IMM state, read out left headphone
+ * impedance measure result, and delay some time to wait
+ * detection sine wave output finish. Then, we can calculate
+ * the cross talk suppresstion side tone according to the L/R
+ * headphone imedance.
+ */
+ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+ &nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ dev_dbg(nau8825->dev, "HPL_R2L imm: %x\n",
+ nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ nau8825_xtalk_imm_stop(nau8825);
+ msleep(150);
+ nau8825->xtalk_state = NAU8825_XTALK_IMM;
+ break;
+ case NAU8825_XTALK_IMM:
+ /* In impedance measure state, the orignal and cross talk
+ * signal level vlues are ready. The side tone gain is deter-
+ * mined with these signal level. After all, restore codec
+ * configuration.
+ */
+ sidetone = nau8825_xtalk_sidetone(
+ nau8825->imp_rms[NAU8825_XTALK_HPR_R2L],
+ nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone);
+ regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL,
+ (sidetone << 8) | sidetone);
+ nau8825_xtalk_clean(nau8825, false);
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ break;
+ default:
+ break;
+ }
+}
+
+static void nau8825_xtalk_work(struct work_struct *work)
+{
+ struct nau8825 *nau8825 = container_of(
+ work, struct nau8825, xtalk_work);
+
+ nau8825_xtalk_measure(nau8825);
+ /* To determine the cross talk side tone gain when reach
+ * the impedance measure state.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_IMM)
+ nau8825_xtalk_measure(nau8825);
+
+ /* Delay jack report until cross talk detection process
+ * completed. It can avoid application to do playback
+ * preparation before cross talk detection is still working.
+ * Meanwhile, the protection of the cross talk detection
+ * is released.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_DONE) {
+ snd_soc_jack_report(nau8825->jack, nau8825->xtalk_event,
+ nau8825->xtalk_event_mask);
+ nau8825_sema_release(nau8825);
+ nau8825->xtalk_protect = false;
+ }
+}
+
+static void nau8825_xtalk_cancel(struct nau8825 *nau8825)
+{
+ /* If the crosstalk is eanbled and the process is on going,
+ * the driver forces to cancel the crosstalk task and
+ * restores the configuration to original status.
+ */
+ if (nau8825->xtalk_enable && nau8825->xtalk_state !=
+ NAU8825_XTALK_DONE) {
+ cancel_work_sync(&nau8825->xtalk_work);
+ nau8825_xtalk_clean(nau8825, true);
+ }
+ /* Reset parameters for cross talk suppression function */
+ nau8825_sema_reset(nau8825);
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+}
+
static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case NAU8825_REG_ENA_CTRL:
- case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
@@ -168,7 +798,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
case NAU8825_REG_MISC_CTRL:
- case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER:
case NAU8825_REG_BIAS_ADJ:
case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
@@ -184,8 +814,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
- case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
case NAU8825_REG_INTERRUPT_MASK:
case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
@@ -195,6 +824,7 @@ static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_IMM_MODE_CTRL:
case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER:
case NAU8825_REG_BIAS_ADJ:
case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
@@ -218,19 +848,79 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_SARDOUT_RAM_STATUS:
case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
case NAU8825_REG_GENERAL_STATUS:
+ case NAU8825_REG_BIQ_CTRL ... NAU8825_REG_BIQ_COF10:
return true;
default:
return false;
}
}
+static int nau8825_fepga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA,
+ NAU8825_ACDC_CTRL_MASK,
+ NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN);
+ msleep(40);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_DISCHRG_EN, 0);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA,
+ NAU8825_ACDC_CTRL_MASK, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(nau8825->adc_delay);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!nau8825->irq)
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Prevent startup click by letting charge pump to ramp up */
msleep(10);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW, 0);
break;
default:
return -EINVAL;
@@ -239,6 +929,117 @@ static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, 0);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8825->regmap;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ dev_dbg(nau8825->dev, "system clock control : POWER OFF\n");
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8825_is_jack_inserted(regmap)) {
+ nau8825_configure_sysclk(nau8825,
+ NAU8825_CLK_INTERNAL, 0);
+ } else {
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+ }
+ }
+
+ return 0;
+}
+
+static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ regmap_raw_read(component->regmap, NAU8825_REG_BIQ_COF1,
+ ucontrol->value.bytes.data, params->max);
+ return 0;
+}
+
+static int nau8825_biq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+ NAU8825_BIQ_WRT_EN, 0);
+ regmap_raw_write(component->regmap, NAU8825_REG_BIQ_COF1,
+ data, params->max);
+ regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+ NAU8825_BIQ_WRT_EN, NAU8825_BIQ_WRT_EN);
+
+ kfree(data);
+ return 0;
+}
+
+static const char * const nau8825_biq_path[] = {
+ "ADC", "DAC"
+};
+
+static const struct soc_enum nau8825_biq_path_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_BIQ_CTRL, NAU8825_BIQ_PATH_SFT,
+ ARRAY_SIZE(nau8825_biq_path), nau8825_biq_path);
+
static const char * const nau8825_adc_decimation[] = {
"32", "64", "128", "256"
};
@@ -275,6 +1076,10 @@ static const struct snd_kcontrol_new nau8825_controls[] = {
SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+ /* programmable biquad filter */
+ SOC_ENUM("BIQ Path Select", nau8825_biq_path_enum),
+ SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+ nau8825_biq_coeff_get, nau8825_biq_coeff_put),
};
/* DAC Mux 0x33[9] and 0x34[9] */
@@ -300,26 +1105,34 @@ static const struct snd_kcontrol_new nau8825_dacr_mux =
static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
15, 1),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("MIC"),
SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
- SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
- NULL, 0),
+ SND_SOC_DAPM_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0,
+ nau8825_adc_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
0),
- /* ADC for button press detection */
- SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
- NAU8825_SAR_ADC_EN_SFT, 0),
+ /* ADC for button press detection. A dapm supply widget is used to
+ * prevent dapm_power_widgets keeping the codec at SND_SOC_BIAS_ON
+ * during suspend.
+ */
+ SND_SOC_DAPM_SUPPLY("SAR", NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_ADC_EN_SFT, 0, NULL, 0),
- SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
- SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
- SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0),
SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_DACR_SFT, 0),
@@ -330,29 +1143,49 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
- SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
- 0),
+ SND_SOC_DAPM_PGA_S("HP amp L", 0,
+ NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP amp R", 0,
+ NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
- nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0,
+ nau8825_pump_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACL", 7,
+ SND_SOC_NOPM, 0, 0, nau8825_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 7,
+ SND_SOC_NOPM, 0, 0, nau8825_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+
+ /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+ NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+ NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0),
+
+ /* High current HPOL/R boost driver */
+ SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+ NAU8825_REG_BOOST, 9, 1, NULL, 0),
+
+ /* Class G operation control*/
+ SND_SOC_DAPM_PGA_S("Class G", 10,
+ NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HPOL"),
SND_SOC_DAPM_OUTPUT("HPOR"),
@@ -364,9 +1197,11 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"ADC", NULL, "ADC Clock"},
{"ADC", NULL, "ADC Power"},
{"AIFTX", NULL, "ADC"},
+ {"AIFTX", NULL, "System Clock"},
- {"DDACL", NULL, "Playback"},
- {"DDACR", NULL, "Playback"},
+ {"AIFRX", NULL, "System Clock"},
+ {"DDACL", NULL, "AIFRX"},
+ {"DDACR", NULL, "AIFRX"},
{"DDACL", NULL, "DDAC Clock"},
{"DDACR", NULL, "DDAC Clock"},
{"DACL Mux", "DACL", "DDACL"},
@@ -375,33 +1210,116 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"DACR Mux", "DACR", "DDACR"},
{"HP amp L", NULL, "DACL Mux"},
{"HP amp R", NULL, "DACR Mux"},
- {"HP amp L", NULL, "HP amp power"},
- {"HP amp R", NULL, "HP amp power"},
- {"ADACL", NULL, "HP amp L"},
- {"ADACR", NULL, "HP amp R"},
- {"ADACL", NULL, "ADACL Clock"},
- {"ADACR", NULL, "ADACR Clock"},
- {"Output Driver L Stage 1", NULL, "ADACL"},
- {"Output Driver R Stage 1", NULL, "ADACR"},
+ {"Charge Pump", NULL, "HP amp L"},
+ {"Charge Pump", NULL, "HP amp R"},
+ {"ADACL", NULL, "Charge Pump"},
+ {"ADACR", NULL, "Charge Pump"},
+ {"ADACL Clock", NULL, "ADACL"},
+ {"ADACR Clock", NULL, "ADACR"},
+ {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+ {"Output Driver R Stage 1", NULL, "ADACR Clock"},
{"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
{"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
{"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
{"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
{"Output DACL", NULL, "Output Driver L Stage 3"},
{"Output DACR", NULL, "Output Driver R Stage 3"},
- {"HPOL", NULL, "Output DACL"},
- {"HPOR", NULL, "Output DACR"},
- {"HPOL", NULL, "Charge Pump"},
- {"HPOR", NULL, "Charge Pump"},
+ {"HPOL Pulldown", NULL, "Output DACL"},
+ {"HPOR Pulldown", NULL, "Output DACR"},
+ {"HP Boost Driver", NULL, "HPOL Pulldown"},
+ {"HP Boost Driver", NULL, "HPOR Pulldown"},
+ {"Class G", NULL, "HP Boost Driver"},
+ {"HPOL", NULL, "Class G"},
+ {"HPOR", NULL, "Class G"},
};
+static const struct nau8825_osr_attr *
+nau8825_get_osr(struct nau8825 *nau8825, int stream)
+{
+ unsigned int osr;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8825->regmap,
+ NAU8825_REG_DAC_CTRL1, &osr);
+ osr &= NAU8825_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+ return &osr_dac_sel[osr];
+ } else {
+ regmap_read(nau8825->regmap,
+ NAU8825_REG_ADC_RATE, &osr);
+ osr &= NAU8825_ADC_SYNC_DOWN_MASK;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+ return &osr_adc_sel[osr];
+ }
+}
+
+static int nau8825_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ const struct nau8825_osr_attr *osr;
+
+ osr = nau8825_get_osr(nau8825, substream->stream);
+ if (!osr || !osr->osr)
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, CLK_DA_AD_MAX / osr->osr);
+}
+
static int nau8825_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0;
+ struct snd_soc_component *component = dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div;
+ const struct nau8825_osr_attr *osr;
+ int err = -EINVAL;
+
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ osr = nau8825_get_osr(nau8825, substream->stream);
+ if (!osr || !osr->osr)
+ goto error;
+ if (params_rate(params) * osr->osr > CLK_DA_AD_MAX)
+ goto error;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8825_CLK_DAC_SRC_SFT);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_ADC_SRC_MASK,
+ osr->clk_src << NAU8825_CLK_ADC_SRC_SFT);
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, &ctrl_val);
+ if (ctrl_val & NAU8825_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = 2;
+ else if (bclk_fs <= 64)
+ bclk_div = 1;
+ else if (bclk_fs <= 128)
+ bclk_div = 0;
+ else
+ goto error;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK,
+ ((bclk_div + 1) << NAU8825_I2S_LRC_DIV_SFT) | bclk_div);
+ }
switch (params_width(params)) {
case 16:
@@ -417,26 +1335,31 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
val_len |= NAU8825_I2S_DL_32;
break;
default:
- return -EINVAL;
+ goto error;
}
regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
NAU8825_I2S_DL_MASK, val_len);
+ err = 0;
- return 0;
+ error:
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ return err;
}
static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
unsigned int ctrl1_val = 0, ctrl2_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl2_val |= NAU8825_I2S_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -473,6 +1396,8 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+
regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK |
NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
@@ -480,12 +1405,113 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
NAU8825_I2S_MS_MASK, ctrl2_val);
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ return 0;
+}
+
+/**
+ * nau8825_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Support TDM 4/8 slots.
+ * The limitation is DAC and ADC need shift 4 slots at 8 slots mode.
+ */
+static int nau8825_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl_val = 0, ctrl_offset = 0, value = 0, dac_s, adc_s;
+
+ if (slots != 4 && slots != 8) {
+ dev_err(nau8825->dev, "Only support 4 or 8 slots!\n");
+ return -EINVAL;
+ }
+
+ /* The driver is limited to 1-channel for ADC, and 2-channel for DAC on TDM mode */
+ if (hweight_long((unsigned long) tx_mask) != 1 ||
+ hweight_long((unsigned long) rx_mask) != 2) {
+ dev_err(nau8825->dev,
+ "The limitation is 1-channel for ADC, and 2-channel for DAC on TDM mode.\n");
+ return -EINVAL;
+ }
+
+ if (((tx_mask & 0xf) && (tx_mask & 0xf0)) ||
+ ((rx_mask & 0xf) && (rx_mask & 0xf0)) ||
+ ((tx_mask & 0xf) && (rx_mask & 0xf0)) ||
+ ((rx_mask & 0xf) && (tx_mask & 0xf0))) {
+ dev_err(nau8825->dev,
+ "Slot assignment of DAC and ADC need to set same interval.\n");
+ return -EINVAL;
+ }
+
+ /* The offset of fixed 4 slots for 8 slots support */
+ if (rx_mask & 0xf0) {
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_PCM_TS_EN_MASK, NAU8825_I2S_PCM_TS_EN);
+ regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, &value);
+ ctrl_val |= NAU8825_TDM_OFFSET_EN;
+ ctrl_offset = 4 * slot_width;
+ if (!(value & NAU8825_I2S_PCMB_MASK))
+ ctrl_offset += 1;
+ dac_s = (rx_mask & 0xf0) >> 4;
+ adc_s = fls((tx_mask & 0xf0) >> 4);
+ } else {
+ dac_s = rx_mask & 0xf;
+ adc_s = fls(tx_mask & 0xf);
+ }
+
+ ctrl_val |= NAU8825_TDM_MODE;
+
+ switch (dac_s) {
+ case 0x3:
+ ctrl_val |= 1 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0x5:
+ ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0x6:
+ ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT;
+ ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0x9:
+ ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0xa:
+ ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT;
+ ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ case 0xc:
+ ctrl_val |= 2 << NAU8825_TDM_DACL_RX_SFT;
+ ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ctrl_val |= adc_s - 1;
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_TDM_CTRL,
+ NAU8825_TDM_MODE | NAU8825_TDM_OFFSET_EN |
+ NAU8825_TDM_DACL_RX_MASK | NAU8825_TDM_DACR_RX_MASK |
+ NAU8825_TDM_TX_MASK, ctrl_val);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_LEFT_TIME_SLOT,
+ NAU8825_TSLOT_L0_MASK, ctrl_offset);
+
return 0;
}
static const struct snd_soc_dai_ops nau8825_dai_ops = {
+ .startup = nau8825_dai_startup,
.hw_params = nau8825_hw_params,
.set_fmt = nau8825_set_dai_fmt,
+ .set_tdm_slot = nau8825_set_tdm_slot,
};
#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
@@ -504,7 +1530,7 @@ static struct snd_soc_dai_driver nau8825_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 1,
- .channels_max = 1,
+ .channels_max = 2, /* Only 1 channel of data */
.rates = NAU8825_RATES,
.formats = NAU8825_FORMATS,
},
@@ -521,14 +1547,20 @@ static struct snd_soc_dai_driver nau8825_dai = {
* events will be routed to the given jack. Jack can be null to stop
* reporting.
*/
-int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+int nau8825_enable_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
struct regmap *regmap = nau8825->regmap;
nau8825->jack = jack;
+ if (!nau8825->jack) {
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, 0);
+ return 0;
+ }
/* Ground HP Outputs[1:0], needed for headset auto detection
* Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
*/
@@ -536,9 +1568,6 @@ int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
- regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
- NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
-
return 0;
}
EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
@@ -546,10 +1575,17 @@ EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
static bool nau8825_is_jack_inserted(struct regmap *regmap)
{
- int status;
+ bool active_high, is_high;
+ int status, jkdet;
+ regmap_read(regmap, NAU8825_REG_JACK_DET_CTRL, &jkdet);
+ active_high = jkdet & NAU8825_JACK_POLARITY;
regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
- return !(status & NAU8825_GPIO2JD1);
+ is_high = status & NAU8825_GPIO2JD1;
+ /* return jack connection status according to jack insertion logic
+ * active high or active low.
+ */
+ return active_high == is_high;
}
static void nau8825_restart_jack_detection(struct regmap *regmap)
@@ -564,11 +1600,30 @@ static void nau8825_restart_jack_detection(struct regmap *regmap)
NAU8825_JACK_DET_RESTART, 0);
}
+static void nau8825_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+ for (i = 0; i < NAU8825_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+ }
+}
+
static void nau8825_eject_jack(struct nau8825 *nau8825)
{
struct snd_soc_dapm_context *dapm = nau8825->dapm;
struct regmap *regmap = nau8825->regmap;
+ /* Force to cancel the cross talk detection process */
+ nau8825_xtalk_cancel(nau8825);
+
snd_soc_dapm_disable_pin(dapm, "SAR");
snd_soc_dapm_disable_pin(dapm, "MICBIAS");
/* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
@@ -578,6 +1633,76 @@ static void nau8825_eject_jack(struct nau8825 *nau8825)
regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
snd_soc_dapm_sync(dapm);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable the insertion interruption, disable the ejection inter-
+ * ruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS,
+ NAU8825_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+ NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
+
+ /* Close clock for jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+}
+
+/* Enable audo mode interruptions with internal clock. */
+static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Enable HSD function */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE);
+
+ /* Enable headset jack type detection complete interruption and
+ * jack ejection interruption.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+ /* Raise up the internal clock for jack detection */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0);
+
+ /* Enable ADC needed for interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+
+ /* Chip needs one FSCLK cycle in order to generate interruptions,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+ /* Not bypass de-bounce circuit */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, 0);
+
+ /* Unmask all interruptions */
+ regmap_write(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+
+ /* Restart the jack detection process at auto mode */
+ nau8825_restart_jack_detection(regmap);
}
static int nau8825_button_decode(int value)
@@ -601,6 +1726,121 @@ static int nau8825_button_decode(int value)
return buttons;
}
+static int nau8825_high_imped_detection(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ unsigned int adc_mg1, adc_mg2;
+
+ /* Initial phase */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2);
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_1,
+ NAU8825_TESTDACIN_MASK, NAU8825_TESTDACIN_GND);
+ regmap_write(regmap, NAU8825_REG_TRIM_SETTINGS, 0x6);
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_LOWNOISE_MASK | NAU8825_MICBIAS_VOLTAGE_MASK,
+ NAU8825_MICBIAS_LOWNOISE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK | NAU8825_SAR_TRACKING_GAIN_MASK |
+ NAU8825_SAR_HV_SEL_MASK | NAU8825_SAR_RES_SEL_MASK |
+ NAU8825_SAR_COMPARE_TIME_MASK | NAU8825_SAR_SAMPLING_TIME_MASK,
+ NAU8825_SAR_HV_SEL_VDDMIC | NAU8825_SAR_RES_SEL_70K);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+
+ /* Configure settings for first reading of SARADC */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND2);
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ regmap_read(regmap, NAU8825_REG_SARDOUT_RAM_STATUS, &adc_mg1);
+
+ /* Configure settings for second reading of SARADC */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 |
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1);
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK, NAU8825_SAR_INPUT_JKSLV);
+ regmap_read(regmap, NAU8825_REG_SARDOUT_RAM_STATUS, &adc_mg2);
+
+ /* Disable phase */
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_LOWNOISE_MASK |
+ NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R |
+ NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 |
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_1,
+ NAU8825_TESTDACIN_MASK, NAU8825_TESTDACIN_GND);
+ regmap_write(regmap, NAU8825_REG_TRIM_SETTINGS, 0);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_TRACKING_GAIN_MASK | NAU8825_SAR_HV_SEL_MASK,
+ nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_COMPARE_TIME_MASK | NAU8825_SAR_SAMPLING_TIME_MASK,
+ (nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT) |
+ (nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT));
+ dev_dbg(nau8825->dev, "adc_mg1:%x, adc_mg2:%x\n", adc_mg1, adc_mg2);
+
+ /* Confirmation phase */
+ if (adc_mg1 > adc_mg2) {
+ dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+
+ /* Unground MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2,
+ NAU8825_SPKR_ENGND2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ /* Attach SARADC to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKR2);
+ } else if (adc_mg1 < adc_mg2) {
+ dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+
+ /* Unground MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2,
+ NAU8825_SPKR_ENGND1);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ /* Attach SARADC to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKSLV);
+ } else {
+ dev_err(nau8825->dev, "Jack broken.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int nau8825_jack_insert(struct nau8825 *nau8825)
{
struct regmap *regmap = nau8825->regmap;
@@ -610,6 +1850,11 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
mic_detected = (jack_status_reg >> 10) & 3;
+ /* The JKSLV and JKR2 all detected in high impedance headset */
+ if (mic_detected == 0x3)
+ nau8825->high_imped = true;
+ else
+ nau8825->high_imped = false;
switch (mic_detected) {
case 0:
@@ -637,7 +1882,6 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
snd_soc_dapm_sync(dapm);
break;
case 2:
- case 3:
dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
type = SND_JACK_HEADSET;
@@ -657,13 +1901,31 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
snd_soc_dapm_force_enable_pin(dapm, "SAR");
snd_soc_dapm_sync(dapm);
break;
+ case 3:
+ /* Detection failure case */
+ dev_warn(nau8825->dev,
+ "Detection failure. Try the manually mechanism for jack type checking.\n");
+ if (!nau8825_high_imped_detection(nau8825)) {
+ type = SND_JACK_HEADSET;
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ } else
+ type = SND_JACK_HEADPHONE;
+ break;
}
- if (type & SND_JACK_HEADPHONE) {
- /* Unground HPL/R */
- regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
- }
+ /* Update to the default divider of internal clock for power saving */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ /* Disable HSD function */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0);
+
+ /* Leaving HPOL/R grounded after jack insert by default. They will be
+ * ungrounded as part of the widget power up sequence at the beginning
+ * of playback to reduce pop.
+ */
return type;
}
@@ -676,7 +1938,10 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
struct regmap *regmap = nau8825->regmap;
int active_irq, clear_irq = 0, event = 0, event_mask = 0;
- regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+ if (regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq)) {
+ dev_err(nau8825->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) ==
NAU8825_JACK_EJECTION_DETECTED) {
@@ -705,6 +1970,39 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
} else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
if (nau8825_is_jack_inserted(regmap)) {
event |= nau8825_jack_insert(nau8825);
+ if (nau8825->xtalk_enable && !nau8825->high_imped) {
+ /* Apply the cross talk suppression in the
+ * headset without high impedance.
+ */
+ if (!nau8825->xtalk_protect) {
+ /* Raise protection for cross talk de-
+ * tection if no protection before.
+ * The driver has to cancel the pro-
+ * cess and restore changes if process
+ * is ongoing when ejection.
+ */
+ int ret;
+ nau8825->xtalk_protect = true;
+ ret = nau8825_sema_acquire(nau8825, 0);
+ if (ret)
+ nau8825->xtalk_protect = false;
+ }
+ /* Startup cross talk detection process */
+ if (nau8825->xtalk_protect) {
+ nau8825->xtalk_state =
+ NAU8825_XTALK_PREPARE;
+ schedule_work(&nau8825->xtalk_work);
+ }
+ } else {
+ /* The cross talk suppression shouldn't apply
+ * in the headset with high impedance. Thus,
+ * relieve the protection raised before.
+ */
+ if (nau8825->xtalk_protect) {
+ nau8825_sema_release(nau8825);
+ nau8825->xtalk_protect = false;
+ }
+ }
} else {
dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
nau8825_eject_jack(nau8825);
@@ -712,6 +2010,39 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
event_mask |= SND_JACK_HEADSET;
clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+ /* Record the interruption report event for driver to report
+ * the event later. The jack report will delay until cross
+ * talk detection process is done.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_PREPARE) {
+ nau8825->xtalk_event = event;
+ nau8825->xtalk_event_mask = event_mask;
+ }
+ } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) {
+ /* crosstalk detection enable and process on going */
+ if (nau8825->xtalk_enable && nau8825->xtalk_protect)
+ schedule_work(&nau8825->xtalk_work);
+ clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ;
+ } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) ==
+ NAU8825_JACK_INSERTION_DETECTED) {
+ /* One more step to check GPIO status directly. Thus, the
+ * driver can confirm the real insertion interruption because
+ * the intrruption at manual mode has bypassed debounce
+ * circuit which can get rid of unstable status.
+ */
+ if (nau8825_is_jack_inserted(regmap)) {
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS,
+ NAU8825_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN);
+ /* Enable interruption for jack type detection at audo
+ * mode which can detect microphone and jack type.
+ */
+ nau8825_setup_auto_irq(nau8825);
+ }
}
if (!clear_irq)
@@ -719,7 +2050,12 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
/* clears the rightmost interruption */
regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
- if (event_mask)
+ /* Delay jack report until cross talk detection is done. It can avoid
+ * application to do playback preparation when cross talk detection
+ * process is still working. Otherwise, the resource like clock and
+ * power will be issued by them at the same time and conflict happens.
+ */
+ if (event_mask && nau8825->xtalk_state == NAU8825_XTALK_DONE)
snd_soc_jack_report(nau8825->jack, event, event_mask);
return IRQ_HANDLED;
@@ -768,6 +2104,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
{
struct regmap *regmap = nau8825->regmap;
+ /* Latch IIC LSB value */
+ regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001);
/* Enable Bias/Vmid */
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
@@ -780,10 +2118,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
/* Disable Boost Driver, Automatic Short circuit protection enable */
regmap_update_bits(regmap, NAU8825_REG_BOOST,
- NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
- NAU8825_SHORT_SHUTDOWN_EN,
- NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
- NAU8825_SHORT_SHUTDOWN_EN);
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+ NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+ NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN);
regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
NAU8825_JKDET_OUTPUT_EN,
@@ -806,6 +2144,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
NAU8825_JACK_EJECT_DEBOUNCE_MASK,
nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT);
+ /* Pull up IRQ pin */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN,
+ NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN);
/* Mask unneeded IRQs: 1 - disable, 0 - enable */
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff);
@@ -819,14 +2161,57 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
* (audible hiss). Set it to something better.
*/
regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
- NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
+ NAU8825_ADC_SYNC_DOWN_MASK | NAU8825_ADC_SINC4_EN,
+ NAU8825_ADC_SYNC_DOWN_64);
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
- NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+ NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64);
+ /* Disable DACR/L power */
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+ * signal to avoid any glitches due to power up transients in both
+ * the analog and digital DAC circuit.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ /* CICCLP off */
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF);
+
+ /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2,
+ NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB,
+ NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL,
+ NAU8825_CLASSG_TIMER_MASK,
+ 0x20 << NAU8825_CLASSG_TIMER_SFT);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
+ (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8825_RDAC_VREF_SFT));
+ /* Config L/R channel */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R);
+ /* Disable short Frame Sync detection logic */
+ regmap_update_bits(regmap, NAU8825_REG_LEFT_TIME_SLOT,
+ NAU8825_DIS_FS_SHORT_DET, NAU8825_DIS_FS_SHORT_DET);
+ /* ADCDAT IO drive strength control */
+ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_ADCOUT_DS_MASK,
+ nau8825->adcout_ds << NAU8825_ADCOUT_DS_SFT);
}
static const struct regmap_config nau8825_regmap_config = {
- .val_bits = 16,
- .reg_bits = 16,
+ .val_bits = NAU8825_REG_DATA_LEN,
+ .reg_bits = NAU8825_REG_ADDR_LEN,
.max_register = NAU8825_REG_MAX,
.readable_reg = nau8825_readable_reg,
@@ -838,27 +2223,22 @@ static const struct regmap_config nau8825_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
};
-static int nau8825_codec_probe(struct snd_soc_codec *codec)
+static int nau8825_component_probe(struct snd_soc_component *component)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
nau8825->dapm = dapm;
- /* The interrupt clock is gated by x1[10:8],
- * one of them needs to be enabled all the time for
- * interrupts to happen.
- */
- snd_soc_dapm_force_enable_pin(dapm, "DDACR");
- snd_soc_dapm_sync(dapm);
+ return 0;
+}
- /* Unmask interruptions. Handler uses dapm object so we can enable
- * interruptions only after dapm is fully initialized.
- */
- regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
- nau8825_restart_jack_detection(nau8825->regmap);
+static void nau8825_component_remove(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
- return 0;
+ /* Cancel and reset cross tak suppresstion detection funciton */
+ nau8825_xtalk_cancel(nau8825);
}
/**
@@ -874,8 +2254,8 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
struct nau8825_fll *fll_param)
{
- u64 fvco;
- unsigned int fref, i;
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
/* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
* freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
@@ -900,25 +2280,33 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
fll_param->ratio = fll_ratio[i].val;
/* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
- * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
* guaranteed across the full range of operation.
* FDCO = freq_out * 2 * mclk_src_scaling
*/
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
- fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
- if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
- break;
+ fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
}
- if (i == ARRAY_SIZE(mclk_src_scaling))
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
return -EINVAL;
- fll_param->mclk_src = mclk_src_scaling[i].val;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
/* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
* input based on FDCO, FREF and FLL ratio.
*/
- fvco = div_u64(fvco << 16, fref * fll_param->ratio);
- fll_param->fll_int = (fvco >> 16) & 0x3FF;
- fll_param->fll_frac = fvco & 0xFFFF;
+ fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF;
+ if (fll_param->fll_frac_num == 16)
+ fll_param->fll_frac = fvco & 0xFFFF;
+ else
+ fll_param->fll_frac = fvco & 0xFFFFFF;
return 0;
}
@@ -926,40 +2314,75 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
struct nau8825_fll *fll_param)
{
regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
- NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+ NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK,
+ NAU8825_CLK_SRC_MCLK | fll_param->mclk_src);
+ /* Make DSP operate at high speed for better performance. */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
- NAU8825_FLL_RATIO_MASK, fll_param->ratio);
- /* FLL 16-bit fractional input */
- regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
+ NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK,
+ fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT));
+ /* FLL 16/24 bit fractional input */
+ if (fll_param->fll_frac_num == 16)
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2,
+ fll_param->fll_frac);
+ else {
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER,
+ fll_param->fll_frac & 0xffff);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER,
+ (fll_param->fll_frac >> 16) & 0xff);
+ }
/* FLL 10-bit integer input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
/* FLL pre-scaler */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
- NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+ NAU8825_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT);
/* select divided VCO input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
- NAU8825_FLL_FILTER_SW_MASK, 0x0000);
- /* FLL sigma delta modulator enable */
- regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
- NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+ NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ /* set FLL loop filter enable and cutoff frequency at 500Khz */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_MASK,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_FILTER);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN | NAU8825_CUTOFF500,
+ NAU8825_SDM_EN | NAU8825_CUTOFF500);
+ } else {
+ /* disable FLL loop filter and cutoff frequency */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN | NAU8825_CUTOFF500, 0);
+ }
}
/* freq_out must be 256*Fs in order to achieve the best performance */
-static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
struct nau8825_fll fll_param;
int ret, fs;
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ fll_param.fll_frac_num = 16;
+ else
+ fll_param.fll_frac_num = 24;
+
fs = freq_out / 256;
ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
if (ret < 0) {
- dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ dev_dbg(component->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
fll_param.fll_int, fll_param.clk_ref_div);
@@ -970,6 +2393,48 @@ static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
return 0;
}
+static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq)
+{
+ int ret;
+
+ nau8825->mclk = devm_clk_get(nau8825->dev, "mclk");
+ if (IS_ERR(nau8825->mclk)) {
+ dev_info(nau8825->dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ return 0;
+ }
+
+ if (!nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ if (nau8825->mclk_freq != freq) {
+ freq = clk_round_rate(nau8825->mclk, freq);
+ ret = clk_set_rate(nau8825->mclk, freq);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to set mclk rate\n");
+ return ret;
+ }
+ nau8825->mclk_freq = freq;
+ }
+
+ return 0;
+}
+
+static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_DCO_EN, 0);
+ /* Make DSP operate as default setting for power saving. */
+ regmap_update_bits(regmap, NAU8825_REG_FLL1,
+ NAU8825_ICTRL_LATCH_MASK, 0);
+}
+
static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
unsigned int freq)
{
@@ -977,40 +2442,130 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
int ret;
switch (clk_id) {
+ case NAU8825_CLK_DIS:
+ /* Clock provided externally and disable internal VCO clock */
+ nau8825_configure_mclk_as_sysclk(regmap);
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
case NAU8825_CLK_MCLK:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ nau8825_configure_mclk_as_sysclk(regmap);
+ /* MCLK not changed by clock tree */
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
- NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
- regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+ NAU8825_CLK_MCLK_SRC_MASK, 0);
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
- /* We selected MCLK source but the clock itself managed externally */
- if (!nau8825->mclk)
- break;
+ ret = nau8825_mclk_prepare(nau8825, freq);
+ if (ret)
+ return ret;
- if (!nau8825->mclk_freq) {
- ret = clk_prepare_enable(nau8825->mclk);
- if (ret) {
- dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
- return ret;
- }
+ break;
+ case NAU8825_CLK_INTERNAL:
+ if (nau8825_is_jack_inserted(nau8825->regmap)) {
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_DCO_EN, NAU8825_DCO_EN);
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ /* Decrease the VCO frequency and make DSP operate
+ * as default setting for power saving.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(regmap, NAU8825_REG_FLL1,
+ NAU8825_ICTRL_LATCH_MASK |
+ NAU8825_FLL_RATIO_MASK, 0x10);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN, NAU8825_SDM_EN);
+ } else {
+ /* The clock turns off intentionally for power saving
+ * when no headset connected.
+ */
+ nau8825_configure_mclk_as_sysclk(regmap);
+ dev_warn(nau8825->dev, "Disable clock for power saving when no headset connected\n");
+ }
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
}
- if (nau8825->mclk_freq != freq) {
- nau8825->mclk_freq = freq;
+ break;
+ case NAU8825_CLK_FLL_MCLK:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ /* Higher FLL reference input frequency can only set lower
+ * gain error, such as 0000 for input reference from MCLK
+ * 12.288Mhz.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
+ NAU8825_FLL_CLK_SRC_MCLK | 0);
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
- freq = clk_round_rate(nau8825->mclk, freq);
- ret = clk_set_rate(nau8825->mclk, freq);
- if (ret) {
- dev_err(nau8825->dev, "Unable to set mclk rate\n");
- return ret;
- }
+ ret = nau8825_mclk_prepare(nau8825, freq);
+ if (ret)
+ return ret;
+
+ break;
+ case NAU8825_CLK_FLL_BLK:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
+ NAU8825_FLL_CLK_SRC_BLK |
+ (0xf << NAU8825_GAIN_ERR_SFT));
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
}
break;
- case NAU8825_CLK_INTERNAL:
- regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
- NAU8825_DCO_EN);
- regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
- NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ case NAU8825_CLK_FLL_FS:
+ /* Acquire the semaphore to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 3 * HZ);
+ /* If FLL reference input is from low frequency source,
+ * higher error gain can apply such as 0xf which has
+ * the most sensitive gain error correction threshold,
+ * Therefore, FLL has the most accurate DCO to
+ * target frequency.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
+ NAU8825_FLL_CLK_SRC_FS |
+ (0xf << NAU8825_GAIN_ERR_SFT));
+ /* Release the semaphore. */
+ nau8825_sema_release(nau8825);
if (nau8825->mclk_freq) {
clk_disable_unprepare(nau8825->mclk);
@@ -1028,18 +2583,43 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
return 0;
}
-static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int nau8825_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
return nau8825_configure_sysclk(nau8825, clk_id, freq);
}
-static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+static int nau8825_resume_setup(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN |
+ NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0);
+
+ return 0;
+}
+
+static int nau8825_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1050,48 +2630,101 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(nau8825->dapm) == SND_SOC_BIAS_OFF) {
if (nau8825->mclk_freq) {
ret = clk_prepare_enable(nau8825->mclk);
if (ret) {
- dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ dev_err(nau8825->dev, "Unable to prepare component mclk\n");
return ret;
}
}
-
- ret = regcache_sync(nau8825->regmap);
- if (ret) {
- dev_err(codec->dev,
- "Failed to sync cache: %d\n", ret);
- return ret;
- }
+ /* Setup codec configuration after resume */
+ nau8825_resume_setup(nau8825);
}
-
break;
case SND_SOC_BIAS_OFF:
+ /* Reset the configuration of jack type for detection */
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ /* ground HPL/HPR, MICGRND1/2 */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+ /* Cancel and reset cross talk detection funciton */
+ nau8825_xtalk_cancel(nau8825);
+ /* Turn off all interruptions before system shutdown. Keep the
+ * interruption quiet before resume setup completes.
+ */
+ regmap_write(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff);
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
if (nau8825->mclk_freq)
clk_disable_unprepare(nau8825->mclk);
-
- regcache_mark_dirty(nau8825->regmap);
break;
}
return 0;
}
-static struct snd_soc_codec_driver nau8825_codec_driver = {
- .probe = nau8825_codec_probe,
- .set_sysclk = nau8825_set_sysclk,
- .set_pll = nau8825_set_pll,
- .set_bias_level = nau8825_set_bias_level,
- .suspend_bias_off = true,
+static int __maybe_unused nau8825_suspend(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ disable_irq(nau8825->irq);
+ snd_soc_dapm_force_bias_level(nau8825->dapm, SND_SOC_BIAS_OFF);
+ /* Power down codec power; don't suppoet button wakeup */
+ snd_soc_dapm_disable_pin(nau8825->dapm, "SAR");
+ snd_soc_dapm_disable_pin(nau8825->dapm, "MICBIAS");
+ snd_soc_dapm_sync(nau8825->dapm);
+ regcache_cache_only(nau8825->regmap, true);
+ regcache_mark_dirty(nau8825->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8825_resume(struct snd_soc_component *component)
+{
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ regcache_cache_only(nau8825->regmap, false);
+ regcache_sync(nau8825->regmap);
+ nau8825->xtalk_protect = true;
+ ret = nau8825_sema_acquire(nau8825, 0);
+ if (ret)
+ nau8825->xtalk_protect = false;
+ enable_irq(nau8825->irq);
+
+ return 0;
+}
+
+static int nau8825_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ return nau8825_enable_jack_detect(component, jack);
+}
- .controls = nau8825_controls,
- .num_controls = ARRAY_SIZE(nau8825_controls),
- .dapm_widgets = nau8825_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
- .dapm_routes = nau8825_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+static const struct snd_soc_component_driver nau8825_component_driver = {
+ .probe = nau8825_component_probe,
+ .remove = nau8825_component_remove,
+ .set_sysclk = nau8825_set_sysclk,
+ .set_pll = nau8825_set_pll,
+ .set_bias_level = nau8825_set_bias_level,
+ .suspend = nau8825_suspend,
+ .resume = nau8825_resume,
+ .controls = nau8825_controls,
+ .num_controls = ARRAY_SIZE(nau8825_controls),
+ .dapm_widgets = nau8825_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+ .dapm_routes = nau8825_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+ .set_jack = nau8825_set_jack,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static void nau8825_reset_chip(struct regmap *regmap)
@@ -1126,10 +2759,15 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825)
nau8825->jack_insert_debounce);
dev_dbg(dev, "jack-eject-debounce: %d\n",
nau8825->jack_eject_debounce);
+ dev_dbg(dev, "crosstalk-enable: %d\n",
+ nau8825->xtalk_enable);
+ dev_dbg(dev, "adcout-drive-strong: %d\n", nau8825->adcout_ds);
+ dev_dbg(dev, "adc-delay-ms: %d\n", nau8825->adc_delay);
}
static int nau8825_read_device_properties(struct device *dev,
struct nau8825 *nau8825) {
+ int ret;
nau8825->jkdet_enable = device_property_read_bool(dev,
"nuvoton,jkdet-enable");
@@ -1137,73 +2775,81 @@ static int nau8825_read_device_properties(struct device *dev,
"nuvoton,jkdet-pull-enable");
nau8825->jkdet_pull_up = device_property_read_bool(dev,
"nuvoton,jkdet-pull-up");
- device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
&nau8825->jkdet_polarity);
- device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ if (ret)
+ nau8825->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
&nau8825->micbias_voltage);
- device_property_read_u32(dev, "nuvoton,vref-impedance",
+ if (ret)
+ nau8825->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
&nau8825->vref_impedance);
- device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ if (ret)
+ nau8825->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
&nau8825->sar_threshold_num);
- device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ if (ret)
+ nau8825->sar_threshold_num = 4;
+ ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
nau8825->sar_threshold, nau8825->sar_threshold_num);
- device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ if (ret) {
+ nau8825->sar_threshold[0] = 0x08;
+ nau8825->sar_threshold[1] = 0x12;
+ nau8825->sar_threshold[2] = 0x26;
+ nau8825->sar_threshold[3] = 0x73;
+ }
+ ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
&nau8825->sar_hysteresis);
- device_property_read_u32(dev, "nuvoton,sar-voltage",
+ if (ret)
+ nau8825->sar_hysteresis = 0;
+ ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
&nau8825->sar_voltage);
- device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ if (ret)
+ nau8825->sar_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
&nau8825->sar_compare_time);
- device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ if (ret)
+ nau8825->sar_compare_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
&nau8825->sar_sampling_time);
- device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ if (ret)
+ nau8825->sar_sampling_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
&nau8825->key_debounce);
- device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ if (ret)
+ nau8825->key_debounce = 3;
+ ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
&nau8825->jack_insert_debounce);
- device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ if (ret)
+ nau8825->jack_insert_debounce = 7;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
&nau8825->jack_eject_debounce);
-
- nau8825->mclk = devm_clk_get(dev, "mclk");
- if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+ if (ret)
+ nau8825->jack_eject_debounce = 0;
+ nau8825->xtalk_enable = device_property_read_bool(dev,
+ "nuvoton,crosstalk-enable");
+ nau8825->adcout_ds = device_property_read_bool(dev, "nuvoton,adcout-drive-strong");
+ ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms", &nau8825->adc_delay);
+ if (ret)
+ nau8825->adc_delay = 125;
+ if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500)
+ dev_warn(dev, "Please set the suitable delay time!\n");
+
+ nau8825->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(nau8825->mclk))
+ return PTR_ERR(nau8825->mclk);
+ if (!nau8825->mclk)
/* The MCLK is managed externally or not used at all */
- nau8825->mclk = NULL;
dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
- } else if (IS_ERR(nau8825->mclk)) {
- return -EINVAL;
- }
return 0;
}
static int nau8825_setup_irq(struct nau8825 *nau8825)
{
- struct regmap *regmap = nau8825->regmap;
int ret;
- /* IRQ Output Enable */
- regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
- NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
-
- /* Enable internal VCO needed for interruptions */
- nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
-
- /* Enable DDACR needed for interrupts
- * It is the same as force_enable_pin("DDACR") we do later
- */
- regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
- NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
-
- /* Chip needs one FSCLK cycle in order to generate interrupts,
- * as we cannot guarantee one will be provided by the system. Turning
- * master mode on then off enables us to generate that FSCLK cycle
- * with a minimum of contention on the clock bus.
- */
- regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
- NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
- regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
- NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
-
ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"nau8825", nau8825);
@@ -1217,8 +2863,7 @@ static int nau8825_setup_irq(struct nau8825 *nau8825)
return 0;
}
-static int nau8825_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8825_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
@@ -1240,6 +2885,14 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
return PTR_ERR(nau8825->regmap);
nau8825->dev = dev;
nau8825->irq = i2c->irq;
+ /* Initiate parameters, semaphore and work queue which are needed in
+ * cross talk suppression measurment function.
+ */
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+ nau8825->xtalk_baktab_initialized = false;
+ sema_init(&nau8825->xtalk_sem, 1);
+ INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work);
nau8825_print_device_properties(nau8825);
@@ -1250,8 +2903,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
ret);
return ret;
}
- if ((value & NAU8825_SOFTWARE_ID_MASK) !=
- NAU8825_SOFTWARE_ID_NAU8825) {
+ nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK;
+ switch (nau8825->sw_id) {
+ case NAU8825_SOFTWARE_ID_NAU8825:
+ break;
+ case NAU8825_SOFTWARE_ID_NAU8825C:
+ ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch,
+ ARRAY_SIZE(nau8825_regmap_patch));
+ if (ret) {
+ dev_err(dev, "Failed to register Rev C patch: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
dev_err(dev, "Not a NAU8825 chip\n");
return -ENODEV;
}
@@ -1261,50 +2925,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
if (i2c->irq)
nau8825_setup_irq(nau8825);
- return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &nau8825_component_driver,
&nau8825_dai, 1);
}
-static int nau8825_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int nau8825_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct nau8825 *nau8825 = dev_get_drvdata(dev);
-
- disable_irq(client->irq);
- regcache_cache_only(nau8825->regmap, true);
- regcache_mark_dirty(nau8825->regmap);
-
- return 0;
-}
-
-static int nau8825_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct nau8825 *nau8825 = dev_get_drvdata(dev);
-
- regcache_cache_only(nau8825->regmap, false);
- regcache_sync(nau8825->regmap);
- enable_irq(client->irq);
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops nau8825_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume)
-};
+static void nau8825_i2c_remove(struct i2c_client *client)
+{}
static const struct i2c_device_id nau8825_i2c_ids[] = {
- { "nau8825", 0 },
+ { "nau8825" },
{ }
};
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id nau8825_of_ids[] = {
@@ -1327,7 +2960,6 @@ static struct i2c_driver nau8825_driver = {
.name = "nau8825",
.of_match_table = of_match_ptr(nau8825_of_ids),
.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
- .pm = &nau8825_pm,
},
.probe = nau8825_i2c_probe,
.remove = nau8825_i2c_remove,
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index dff8edb83bfd..2abfbb5184da 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NAU8825 ALSA SoC audio driver
*
* Copyright 2015 Google Inc.
* Author: Anatol Pomozov <anatol.pomozov@chrominium.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 __NAU8825_H__
@@ -14,6 +11,7 @@
#define NAU8825_REG_RESET 0x00
#define NAU8825_REG_ENA_CTRL 0x01
+#define NAU8825_REG_IIC_ADDR_SET 0x02
#define NAU8825_REG_CLK_DIVIDER 0x03
#define NAU8825_REG_FLL1 0x04
#define NAU8825_REG_FLL2 0x05
@@ -77,6 +75,8 @@
#define NAU8825_REG_MISC_CTRL 0x55
#define NAU8825_REG_I2C_DEVICE_ID 0x58
#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
+#define NAU8825_REG_FLL2_LOWER 0x5a
+#define NAU8825_REG_FLL2_UPPER 0x5b
#define NAU8825_REG_BIAS_ADJ 0x66
#define NAU8825_REG_TRIM_SETTINGS 0x68
#define NAU8825_REG_ANALOG_CONTROL_1 0x69
@@ -92,12 +92,21 @@
#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
#define NAU8825_REG_GENERAL_STATUS 0x82
#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8825_REG_ADDR_LEN 16
+#define NAU8825_REG_DATA_LEN 16
/* ENA_CTRL (0x1) */
#define NAU8825_ENABLE_DACR_SFT 10
#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
#define NAU8825_ENABLE_DACL_SFT 9
+#define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT)
#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT)
+#define NAU8825_ENABLE_ADC_CLK_SFT 7
+#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT)
+#define NAU8825_ENABLE_DAC_CLK_SFT 6
+#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT)
#define NAU8825_ENABLE_SAR_SFT 1
/* CLK_DIVIDER (0x3) */
@@ -105,36 +114,57 @@
#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT)
#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT)
#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_ADC_SRC_SFT 6
+#define NAU8825_CLK_ADC_SRC_MASK (0x3 << NAU8825_CLK_ADC_SRC_SFT)
+#define NAU8825_CLK_DAC_SRC_SFT 4
+#define NAU8825_CLK_DAC_SRC_MASK (0x3 << NAU8825_CLK_DAC_SRC_SFT)
#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0)
/* FLL1 (0x04) */
+#define NAU8825_ICTRL_LATCH_SFT 10
+#define NAU8825_ICTRL_LATCH_MASK (0x7 << NAU8825_ICTRL_LATCH_SFT)
#define NAU8825_FLL_RATIO_MASK (0x7f << 0)
/* FLL3 (0x06) */
+#define NAU8825_GAIN_ERR_SFT 12
+#define NAU8825_GAIN_ERR_MASK (0xf << NAU8825_GAIN_ERR_SFT)
#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
+#define NAU8825_FLL_CLK_SRC_SFT 10
+#define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT)
/* FLL4 (0x07) */
-#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
+#define NAU8825_FLL_REF_DIV_SFT 10
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT)
/* FLL5 (0x08) */
-#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
+#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8825_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8825_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12)
/* FLL6 (0x9) */
-#define NAU8825_DCO_EN_MASK (0x1 << 15)
#define NAU8825_DCO_EN (0x1 << 15)
-#define NAU8825_DCO_DIS (0x0 << 15)
-#define NAU8825_SDM_EN_MASK (0x1 << 14)
#define NAU8825_SDM_EN (0x1 << 14)
-#define NAU8825_SDM_DIS (0x0 << 14)
+#define NAU8825_CUTOFF500 (0x1 << 13)
/* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6)
-/* 0 - short to GND, 1 - open */
+/* 0 - open, 1 - short to GND */
+#define NAU8825_SPKR_ENGND1 (1 << 3)
+#define NAU8825_SPKR_ENGND2 (1 << 2)
#define NAU8825_SPKR_DWN1R (1 << 1)
#define NAU8825_SPKR_DWN1L (1 << 0)
/* JACK_DET_CTRL (0xd) */
#define NAU8825_JACK_DET_RESTART (1 << 9)
+#define NAU8825_JACK_DET_DB_BYPASS (1 << 8)
#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
@@ -142,11 +172,15 @@
#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */
/* INTERRUPT_MASK (0xf) */
+#define NAU8825_IRQ_PIN_PULLUP (1 << 14)
+#define NAU8825_IRQ_PIN_PULL_EN (1 << 13)
#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_RMS_EN (1 << 8)
#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
#define NAU8825_IRQ_EJECT_EN (1 << 2)
+#define NAU8825_IRQ_INSERT_EN (1 << 0)
/* IRQ_STATUS (0x10) */
#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
@@ -167,6 +201,7 @@
#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+#define NAU8825_IRQ_INSERT_DIS (1 << 0)
/* SAR_CTRL (0x13) */
#define NAU8825_SAR_ADC_EN_SFT 12
@@ -176,6 +211,17 @@
#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
#define NAU8825_SAR_TRACKING_GAIN_SFT 8
#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
+#define NAU8825_SAR_HV_SEL_SFT 7
+#define NAU8825_SAR_HV_SEL_MASK (1 << NAU8825_SAR_HV_SEL_SFT)
+#define NAU8825_SAR_HV_SEL_MICBIAS (0 << NAU8825_SAR_HV_SEL_SFT)
+#define NAU8825_SAR_HV_SEL_VDDMIC (1 << NAU8825_SAR_HV_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_SFT 4
+#define NAU8825_SAR_RES_SEL_MASK (0x7 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_35K (0 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_70K (1 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_170K (2 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_360K (3 << NAU8825_SAR_RES_SEL_SFT)
+#define NAU8825_SAR_RES_SEL_SHORTED (4 << NAU8825_SAR_RES_SEL_SFT)
#define NAU8825_SAR_COMPARE_TIME_SFT 2
#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2)
#define NAU8825_SAR_SAMPLING_TIME_SFT 0
@@ -194,6 +240,15 @@
#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */
#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */
+/* TDM_CTRL (0x1b) */
+#define NAU8825_TDM_MODE (0x1 << 15)
+#define NAU8825_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8825_TDM_DACL_RX_SFT 6
+#define NAU8825_TDM_DACL_RX_MASK (0x3 << NAU8825_TDM_DACL_RX_SFT)
+#define NAU8825_TDM_DACR_RX_SFT 4
+#define NAU8825_TDM_DACR_RX_MASK (0x3 << NAU8825_TDM_DACR_RX_SFT)
+#define NAU8825_TDM_TX_MASK 0x3
+
/* I2S_PCM_CTRL1 (0x1c) */
#define NAU8825_I2S_BP_SFT 7
#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT)
@@ -216,12 +271,35 @@
/* I2S_PCM_CTRL2 (0x1d) */
#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_LRC_DIV_SFT 12
+#define NAU8825_I2S_LRC_DIV_MASK (0x3 << NAU8825_I2S_LRC_DIV_SFT)
+#define NAU8825_I2S_PCM_TS_EN_SFT 10
+#define NAU8825_I2S_PCM_TS_EN_MASK (1 << NAU8825_I2S_PCM_TS_EN_SFT)
+#define NAU8825_I2S_PCM_TS_EN (1 << NAU8825_I2S_PCM_TS_EN_SFT)
#define NAU8825_I2S_MS_SFT 3
#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_BLK_DIV_MASK 0x7
+
+/* LEFT_TIME_SLOT (0x1e) */
+#define NAU8825_FS_ERR_CMP_SEL_SFT 14
+#define NAU8825_FS_ERR_CMP_SEL_MASK (0x3 << NAU8825_FS_ERR_CMP_SEL_SFT)
+#define NAU8825_DIS_FS_SHORT_DET (1 << 13)
+#define NAU8825_TSLOT_L0_MASK 0x3ff
+#define NAU8825_TSLOT_R0_MASK 0x3ff
+
+/* BIQ_CTRL (0x20) */
+#define NAU8825_BIQ_WRT_SFT 4
+#define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT)
+#define NAU8825_BIQ_PATH_SFT 0
+#define NAU8825_BIQ_PATH_MASK (1 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_ADC (0 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT)
/* ADC_RATE (0x2b) */
+#define NAU8825_ADC_SINC4_SFT 4
+#define NAU8825_ADC_SINC4_EN (1 << NAU8825_ADC_SINC4_SFT)
#define NAU8825_ADC_SYNC_DOWN_SFT 0
#define NAU8825_ADC_SYNC_DOWN_MASK 0x3
#define NAU8825_ADC_SYNC_DOWN_32 0
@@ -238,29 +316,98 @@
#define NAU8825_DAC_OVERSAMPLE_128 2
#define NAU8825_DAC_OVERSAMPLE_32 4
+/* ADC_DGAIN_CTRL (0x30) */
+#define NAU8825_ADC_DIG_VOL_MASK 0xff
+
/* MUTE_CTRL (0x31) */
#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
#define NAU8825_DAC_SOFT_MUTE (1 << 9)
/* HSVOL_CTRL (0x32) */
#define NAU8825_HP_MUTE (1 << 15)
+#define NAU8825_HP_MUTE_AUTO (1 << 14)
+#define NAU8825_HPL_MUTE (1 << 13)
+#define NAU8825_HPR_MUTE (1 << 12)
+#define NAU8825_HPL_VOL_SFT 6
+#define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT)
+#define NAU8825_HPR_VOL_SFT 0
+#define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT)
+#define NAU8825_HP_VOL_MIN 0x36
/* DACL_CTRL (0x33) */
#define NAU8825_DACL_CH_SEL_SFT 9
+#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_VOL_MASK 0xff
/* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9
+#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_VOL_MASK 0xff
+
+/* IMM_MODE_CTRL (0x4C) */
+#define NAU8825_IMM_THD_SFT 8
+#define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT)
+#define NAU8825_IMM_GEN_VOL_SFT 6
+#define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+
+#define NAU8825_IMM_CYC_SFT 4
+#define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_EN (1 << 3)
+#define NAU8825_IMM_DAC_SRC_MASK 0x7
+#define NAU8825_IMM_DAC_SRC_BIQ 0x0
+#define NAU8825_IMM_DAC_SRC_DRC 0x1
+#define NAU8825_IMM_DAC_SRC_MIX 0x2
+#define NAU8825_IMM_DAC_SRC_SIN 0x3
+
+/* CLASSG_CTRL (0x50) */
+#define NAU8825_CLASSG_TIMER_SFT 8
+#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_LDAC_EN (0x1 << 2)
+#define NAU8825_CLASSG_RDAC_EN (0x1 << 1)
+#define NAU8825_CLASSG_EN (1 << 0)
/* I2C_DEVICE_ID (0x58) */
#define NAU8825_GPIO2JD1 (1 << 7)
#define NAU8825_SOFTWARE_ID_MASK 0x3
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
+#define NAU8825_SOFTWARE_ID_NAU8825C 0x1
/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_HPR_IMP (1 << 15)
+#define NAU8825_BIAS_HPL_IMP (1 << 14)
+#define NAU8825_BIAS_TESTDAC_SFT 8
+#define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_VMID (1 << 6)
#define NAU8825_BIAS_VMID_SEL_SFT 4
#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8825_TESTDACIN_SFT 14
+#define NAU8825_TESTDACIN_MASK (0x3 << NAU8825_TESTDACIN_SFT)
+#define NAU8825_TESTDACIN_HIGH (1 << NAU8825_TESTDACIN_SFT)
+#define NAU8825_TESTDACIN_LOW (2 << NAU8825_TESTDACIN_SFT)
+#define NAU8825_TESTDACIN_GND (3 << NAU8825_TESTDACIN_SFT)
+
/* ANALOG_CONTROL_2 (0x6a) */
#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
@@ -274,9 +421,23 @@
#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
#define NAU8825_POWERUP_ADCL (1 << 6)
+/* RDAC (0x73) */
+#define NAU8825_RDAC_FS_BCLK_ENB (1 << 15)
+#define NAU8825_RDAC_EN_SFT 12
+#define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT)
+#define NAU8825_RDAC_CLK_EN_SFT 8
+#define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT)
+#define NAU8825_RDAC_CLK_DELAY_SFT 4
+#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
+#define NAU8825_RDAC_VREF_SFT 2
+#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT)
+
/* MIC_BIAS (0x74) */
#define NAU8825_MICBIAS_JKSLV (1 << 14)
#define NAU8825_MICBIAS_JKR2 (1 << 12)
+#define NAU8825_MICBIAS_LOWNOISE_SFT 10
+#define NAU8825_MICBIAS_LOWNOISE_MASK (0x1 << NAU8825_MICBIAS_LOWNOISE_SFT)
+#define NAU8825_MICBIAS_LOWNOISE_EN (0x1 << NAU8825_MICBIAS_LOWNOISE_SFT)
#define NAU8825_MICBIAS_POWERUP_SFT 8
#define NAU8825_MICBIAS_VOLTAGE_SFT 0
#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7
@@ -284,9 +445,17 @@
/* BOOST (0x76) */
#define NAU8825_PRECHARGE_DIS (1 << 13)
#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_DISCHRG_EN (1 << 11)
+#define NAU8825_HP_BOOST_DIS (1 << 9)
#define NAU8825_HP_BOOST_G_DIS (1 << 8)
#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
+/* FEPGA (0x77) */
+#define NAU8825_ACDC_CTRL_SFT 14
+#define NAU8825_ACDC_CTRL_MASK (0x3 << NAU8825_ACDC_CTRL_SFT)
+#define NAU8825_ACDC_VREF_MICP (0x1 << NAU8825_ACDC_CTRL_SFT)
+#define NAU8825_ACDC_VREF_MICN (0x2 << NAU8825_ACDC_CTRL_SFT)
+
/* POWER_UP_CONTROL (0x7f) */
#define NAU8825_POWERUP_INTEGR_R (1 << 5)
#define NAU8825_POWERUP_INTEGR_L (1 << 4)
@@ -296,6 +465,8 @@
#define NAU8825_POWERUP_HP_DRV_L (1 << 0)
/* CHARGE_PUMP (0x80) */
+#define NAU8825_ADCOUT_DS_SFT 12
+#define NAU8825_ADCOUT_DS_MASK (1 << NAU8825_ADCOUT_DS_SFT)
#define NAU8825_JAMNODCLOW (1 << 10)
#define NAU8825_POWER_DOWN_DACR (1 << 9)
#define NAU8825_POWER_DOWN_DACL (1 << 8)
@@ -304,8 +475,21 @@
/* System Clock Source */
enum {
- NAU8825_CLK_MCLK = 0,
+ NAU8825_CLK_DIS = 0,
+ NAU8825_CLK_MCLK,
NAU8825_CLK_INTERNAL,
+ NAU8825_CLK_FLL_MCLK,
+ NAU8825_CLK_FLL_BLK,
+ NAU8825_CLK_FLL_FS,
+};
+
+/* Cross talk detection state */
+enum {
+ NAU8825_XTALK_PREPARE = 0,
+ NAU8825_XTALK_HPR_R2L,
+ NAU8825_XTALK_HPL_R2L,
+ NAU8825_XTALK_IMM,
+ NAU8825_XTALK_DONE,
};
struct nau8825 {
@@ -314,6 +498,9 @@ struct nau8825 {
struct snd_soc_dapm_context *dapm;
struct snd_soc_jack *jack;
struct clk *mclk;
+ struct work_struct xtalk_work;
+ struct semaphore xtalk_sem;
+ int sw_id;
int irq;
int mclk_freq; /* 0 - mclk is disabled */
int button_pressed;
@@ -332,9 +519,19 @@ struct nau8825 {
int key_debounce;
int jack_insert_debounce;
int jack_eject_debounce;
+ int high_imped;
+ int xtalk_state;
+ int xtalk_event;
+ int xtalk_event_mask;
+ bool xtalk_protect;
+ int imp_rms[NAU8825_XTALK_IMM];
+ int xtalk_enable;
+ bool xtalk_baktab_initialized; /* True if initialized. */
+ bool adcout_ds;
+ int adc_delay;
};
-int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+int nau8825_enable_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
diff --git a/sound/soc/codecs/ntp8835.c b/sound/soc/codecs/ntp8835.c
new file mode 100644
index 000000000000..2b93bea11752
--- /dev/null
+++ b/sound/soc/codecs/ntp8835.c
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the NTP8835/NTP8835C Audio Amplifiers
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Igor Prusov <ivprusov@salutedevices.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/bits.h>
+#include <linux/reset.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/initval.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/tlv.h>
+
+#include "ntpfw.h"
+
+#define NTP8835_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define NTP8835_INPUT_FMT 0x0
+#define NTP8835_INPUT_FMT_MASTER_MODE BIT(0)
+#define NTP8835_INPUT_FMT_GSA_MODE BIT(1)
+#define NTP8835_GSA_FMT 0x1
+#define NTP8835_GSA_BS_MASK GENMASK(3, 2)
+#define NTP8835_GSA_BS(x) ((x) << 2)
+#define NTP8835_GSA_RIGHT_J BIT(0)
+#define NTP8835_GSA_LSB BIT(1)
+#define NTP8835_MCLK_FREQ_CTRL 0x2
+#define NTP8835_MCLK_FREQ_MCF GENMASK(1, 0)
+#define NTP8835_SOFT_MUTE 0x26
+#define NTP8835_SOFT_MUTE_SM1 BIT(0)
+#define NTP8835_SOFT_MUTE_SM2 BIT(1)
+#define NTP8835_SOFT_MUTE_SM3 BIT(2)
+#define NTP8835_PWM_SWITCH 0x27
+#define NTP8835_PWM_SWITCH_POF1 BIT(0)
+#define NTP8835_PWM_SWITCH_POF2 BIT(1)
+#define NTP8835_PWM_SWITCH_POF3 BIT(2)
+#define NTP8835_PWM_MASK_CTRL0 0x28
+#define NTP8835_PWM_MASK_CTRL0_OUT_LOW BIT(1)
+#define NTP8835_PWM_MASK_CTRL0_FPMLD BIT(2)
+#define NTP8835_MASTER_VOL 0x2e
+#define NTP8835_CHNL_A_VOL 0x2f
+#define NTP8835_CHNL_B_VOL 0x30
+#define NTP8835_CHNL_C_VOL 0x31
+#define REG_MAX NTP8835_CHNL_C_VOL
+
+#define NTP8835_FW_NAME "eq_8835.bin"
+#define NTP8835_FW_MAGIC 0x38383335 /* "8835" */
+
+struct ntp8835_priv {
+ struct i2c_client *i2c;
+ struct reset_control *reset;
+ unsigned int format;
+ struct clk *mclk;
+ unsigned int mclk_rate;
+};
+
+static const DECLARE_TLV_DB_RANGE(ntp8835_vol_scale,
+ 0, 1, TLV_DB_SCALE_ITEM(-15000, 0, 0),
+ 2, 6, TLV_DB_SCALE_ITEM(-15000, 1000, 0),
+ 7, 0xff, TLV_DB_SCALE_ITEM(-10000, 50, 0),
+);
+
+static int ntp8835_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->access =
+ (SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE);
+ uinfo->count = 1;
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ntp8835_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, NTP8835_SOFT_MUTE);
+
+ ucontrol->value.integer.value[0] = val ? 0 : 1;
+ return 0;
+}
+
+static int ntp8835_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+
+ val = ucontrol->value.integer.value[0] ? 0 : 7;
+
+ snd_soc_component_write(component, NTP8835_SOFT_MUTE, val);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new ntp8835_vol_control[] = {
+ SOC_SINGLE_TLV("Playback Volume", NTP8835_MASTER_VOL, 0,
+ 0xff, 0, ntp8835_vol_scale),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Playback Switch",
+ .info = ntp8835_mute_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = ntp8835_mute_get,
+ .put = ntp8835_mute_put,
+ },
+};
+
+static void ntp8835_reset_gpio(struct ntp8835_priv *ntp8835)
+{
+ /*
+ * Proper initialization sequence for NTP835 amplifier requires driving
+ * /RESET signal low during power up for at least 0.1us. The sequence is,
+ * according to NTP8835 datasheet, 6.2 Timing Sequence (recommended):
+ * Deassert for T2 >= 1ms...
+ */
+ reset_control_deassert(ntp8835->reset);
+ fsleep(1000);
+
+ /* ...Assert for T3 >= 0.1us... */
+ reset_control_assert(ntp8835->reset);
+ fsleep(1);
+
+ /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
+ reset_control_deassert(ntp8835->reset);
+ fsleep(500);
+}
+
+static const struct reg_sequence ntp8835_sound_on[] = {
+ { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_FPMLD },
+ { NTP8835_PWM_SWITCH, 0x00 },
+ { NTP8835_SOFT_MUTE, 0x00 },
+};
+
+static const struct reg_sequence ntp8835_sound_off[] = {
+ { NTP8835_SOFT_MUTE, NTP8835_SOFT_MUTE_SM1 |
+ NTP8835_SOFT_MUTE_SM2 |
+ NTP8835_SOFT_MUTE_SM3 },
+
+ { NTP8835_PWM_SWITCH, NTP8835_PWM_SWITCH_POF1 |
+ NTP8835_PWM_SWITCH_POF2 |
+ NTP8835_PWM_SWITCH_POF3 },
+
+ { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_OUT_LOW |
+ NTP8835_PWM_MASK_CTRL0_FPMLD },
+};
+
+static int ntp8835_load_firmware(struct ntp8835_priv *ntp8835)
+{
+ int ret;
+
+ ret = ntpfw_load(ntp8835->i2c, NTP8835_FW_NAME, NTP8835_FW_MAGIC);
+ if (ret == -ENOENT) {
+ dev_warn_once(&ntp8835->i2c->dev,
+ "Could not find firmware %s\n", NTP8835_FW_NAME);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int ntp8835_snd_suspend(struct snd_soc_component *component)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(component->regmap, true);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8835_sound_off,
+ ARRAY_SIZE(ntp8835_sound_off));
+
+ /*
+ * According to NTP8835 datasheet, 6.2 Timing Sequence (recommended):
+ * wait after sound off for T6 >= 0.5ms
+ */
+ fsleep(500);
+ reset_control_assert(ntp8835->reset);
+
+ regcache_mark_dirty(component->regmap);
+ clk_disable_unprepare(ntp8835->mclk);
+
+ return 0;
+}
+
+static int ntp8835_snd_resume(struct snd_soc_component *component)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ntp8835_reset_gpio(ntp8835);
+ ret = clk_prepare_enable(ntp8835->mclk);
+ if (ret)
+ return ret;
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8835_sound_on,
+ ARRAY_SIZE(ntp8835_sound_on));
+
+ ret = ntp8835_load_firmware(ntp8835);
+ if (ret) {
+ dev_err(&ntp8835->i2c->dev, "Failed to load firmware\n");
+ return ret;
+ }
+
+ regcache_cache_only(component->regmap, false);
+ snd_soc_component_cache_sync(component);
+
+ return 0;
+}
+
+static int ntp8835_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ ret = snd_soc_add_component_controls(component, ntp8835_vol_control,
+ ARRAY_SIZE(ntp8835_vol_control));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add controls\n");
+
+ ret = ntp8835_load_firmware(ntp8835);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to load firmware\n");
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ntp8835_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route ntp8835_dapm_routes[] = {
+ { "OUT1", NULL, "AIFIN" },
+ { "OUT2", NULL, "AIFIN" },
+ { "OUT3", NULL, "AIFIN" },
+};
+
+static int ntp8835_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 12288000:
+ case 24576000:
+ case 18432000:
+ ntp8835->mclk_rate = freq;
+ break;
+ default:
+ ntp8835->mclk_rate = 0;
+ dev_err(component->dev, "Unsupported MCLK value: %u", freq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_ntp8835 = {
+ .probe = ntp8835_probe,
+ .suspend = ntp8835_snd_suspend,
+ .resume = ntp8835_snd_resume,
+ .dapm_widgets = ntp8835_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ntp8835_dapm_widgets),
+ .dapm_routes = ntp8835_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ntp8835_dapm_routes),
+ .set_sysclk = ntp8835_set_component_sysclk,
+};
+
+static int ntp8835_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ unsigned int input_fmt = 0;
+ unsigned int gsa_fmt = 0;
+ unsigned int gsa_fmt_mask;
+ unsigned int mcf;
+ int ret;
+
+ switch (ntp8835->mclk_rate) {
+ case 12288000:
+ mcf = 0;
+ break;
+ case 24576000:
+ mcf = 1;
+ break;
+ case 18432000:
+ mcf = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8835_MCLK_FREQ_CTRL,
+ NTP8835_MCLK_FREQ_MCF, mcf);
+ if (ret)
+ return ret;
+
+ switch (ntp8835->format) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ input_fmt |= NTP8835_INPUT_FMT_GSA_MODE;
+ gsa_fmt |= NTP8835_GSA_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ input_fmt |= NTP8835_INPUT_FMT_GSA_MODE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8835_INPUT_FMT,
+ NTP8835_INPUT_FMT_MASTER_MODE |
+ NTP8835_INPUT_FMT_GSA_MODE,
+ input_fmt);
+
+ if (!(input_fmt & NTP8835_INPUT_FMT_GSA_MODE) || ret < 0)
+ return ret;
+
+ switch (params_width(params)) {
+ case 24:
+ gsa_fmt |= NTP8835_GSA_BS(0);
+ break;
+ case 20:
+ gsa_fmt |= NTP8835_GSA_BS(1);
+ break;
+ case 18:
+ gsa_fmt |= NTP8835_GSA_BS(2);
+ break;
+ case 16:
+ gsa_fmt |= NTP8835_GSA_BS(3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ gsa_fmt_mask = NTP8835_GSA_BS_MASK |
+ NTP8835_GSA_RIGHT_J |
+ NTP8835_GSA_LSB;
+ return snd_soc_component_update_bits(component, NTP8835_GSA_FMT,
+ gsa_fmt_mask, gsa_fmt);
+}
+
+static int ntp8835_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ ntp8835->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+};
+
+static const struct snd_soc_dai_ops ntp8835_dai_ops = {
+ .hw_params = ntp8835_hw_params,
+ .set_fmt = ntp8835_set_fmt,
+};
+
+static struct snd_soc_dai_driver ntp8835_dai = {
+ .name = "ntp8835-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 3,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = NTP8835_FORMATS,
+ },
+ .ops = &ntp8835_dai_ops,
+};
+
+static const struct regmap_config ntp8835_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ntp8835_i2c_probe(struct i2c_client *i2c)
+{
+ struct ntp8835_priv *ntp8835;
+ struct regmap *regmap;
+ int ret;
+
+ ntp8835 = devm_kzalloc(&i2c->dev, sizeof(*ntp8835), GFP_KERNEL);
+ if (!ntp8835)
+ return -ENOMEM;
+
+ ntp8835->i2c = i2c;
+
+ ntp8835->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
+ if (IS_ERR(ntp8835->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->reset),
+ "Failed to get reset\n");
+
+ ret = reset_control_deassert(ntp8835->reset);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to deassert reset\n");
+
+ dev_set_drvdata(&i2c->dev, ntp8835);
+
+ ntp8835_reset_gpio(ntp8835);
+
+ regmap = devm_regmap_init_i2c(i2c, &ntp8835_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to allocate regmap\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8835,
+ &ntp8835_dai, 1);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to register component\n");
+
+ ntp8835->mclk = devm_clk_get_enabled(&i2c->dev, "mclk");
+ if (IS_ERR(ntp8835->mclk))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->mclk), "failed to get mclk\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ntp8835_i2c_id[] = {
+ { "ntp8835" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id);
+
+static const struct of_device_id ntp8835_of_match[] = {
+ {.compatible = "neofidelity,ntp8835",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ntp8835_of_match);
+
+static struct i2c_driver ntp8835_i2c_driver = {
+ .probe = ntp8835_i2c_probe,
+ .id_table = ntp8835_i2c_id,
+ .driver = {
+ .name = "ntp8835",
+ .of_match_table = ntp8835_of_match,
+ },
+};
+module_i2c_driver(ntp8835_i2c_driver);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("NTP8835 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c
new file mode 100644
index 000000000000..5593d48ef696
--- /dev/null
+++ b/sound/soc/codecs/ntp8918.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the NTP8918 Audio Amplifier
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Igor Prusov <ivprusov@salutedevices.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#include <sound/initval.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/tlv.h>
+
+#include "ntpfw.h"
+
+#define NTP8918_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define NTP8918_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define NTP8918_INPUT_FMT 0x0
+#define NTP8918_INPUT_FMT_MASTER_MODE BIT(0)
+#define NTP8918_INPUT_FMT_GSA_MODE BIT(1)
+#define NTP8918_GSA_FMT 0x1
+#define NTP8918_GSA_BS_MASK GENMASK(3, 2)
+#define NTP8918_GSA_BS(x) ((x) << 2)
+#define NTP8918_GSA_RIGHT_J BIT(0)
+#define NTP8918_GSA_LSB BIT(1)
+#define NTP8918_MCLK_FREQ_CTRL 0x2
+#define NTP8918_MCLK_FREQ_MCF GENMASK(1, 0)
+#define NTP8918_MASTER_VOL 0x0C
+#define NTP8918_CHNL_A_VOL 0x17
+#define NTP8918_CHNL_B_VOL 0x18
+#define NTP8918_SOFT_MUTE 0x33
+#define NTP8918_SOFT_MUTE_SM1 BIT(0)
+#define NTP8918_SOFT_MUTE_SM2 BIT(1)
+#define NTP8918_PWM_SWITCH 0x34
+#define NTP8918_PWM_MASK_CTRL0 0x35
+#define REG_MAX NTP8918_PWM_MASK_CTRL0
+
+#define NTP8918_FW_NAME "eq_8918.bin"
+#define NTP8918_FW_MAGIC 0x38393138 /* "8918" */
+
+struct ntp8918_priv {
+ struct i2c_client *i2c;
+ struct clk *bck;
+ struct reset_control *reset;
+ unsigned int format;
+};
+
+static const DECLARE_TLV_DB_SCALE(ntp8918_master_vol_scale, -12550, 50, 0);
+
+static const struct snd_kcontrol_new ntp8918_vol_control[] = {
+ SOC_SINGLE_RANGE_TLV("Playback Volume", NTP8918_MASTER_VOL, 0,
+ 0x04, 0xff, 0, ntp8918_master_vol_scale),
+ SOC_SINGLE("Playback Switch", NTP8918_PWM_MASK_CTRL0, 1, 1, 1),
+};
+
+static void ntp8918_reset_gpio(struct ntp8918_priv *ntp8918)
+{
+ /*
+ * Proper initialization sequence for NTP8918 amplifier requires driving
+ * /RESET signal low during power up for at least 0.1us. The sequence is,
+ * according to NTP8918 datasheet, 6.2 Timing Sequence 1:
+ * Deassert for T2 >= 1ms...
+ */
+ reset_control_deassert(ntp8918->reset);
+ fsleep(1000);
+
+ /* ...Assert for T3 >= 0.1us... */
+ reset_control_assert(ntp8918->reset);
+ fsleep(1);
+
+ /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
+ reset_control_deassert(ntp8918->reset);
+ fsleep(500);
+}
+
+static const struct reg_sequence ntp8918_sound_off[] = {
+ { NTP8918_MASTER_VOL, 0 },
+};
+
+static const struct reg_sequence ntp8918_sound_on[] = {
+ { NTP8918_MASTER_VOL, 0b11 },
+};
+
+static int ntp8918_load_firmware(struct ntp8918_priv *ntp8918)
+{
+ int ret;
+
+ ret = ntpfw_load(ntp8918->i2c, NTP8918_FW_NAME, NTP8918_FW_MAGIC);
+ if (ret == -ENOENT) {
+ dev_warn_once(&ntp8918->i2c->dev, "Could not find firmware %s\n",
+ NTP8918_FW_NAME);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int ntp8918_snd_suspend(struct snd_soc_component *component)
+{
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(component->regmap, true);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8918_sound_off,
+ ARRAY_SIZE(ntp8918_sound_off));
+
+ /*
+ * According to NTP8918 datasheet, 6.2 Timing Sequence 1:
+ * wait after sound off for T6 >= 0.5ms
+ */
+ fsleep(500);
+ reset_control_assert(ntp8918->reset);
+
+ regcache_mark_dirty(component->regmap);
+ clk_disable_unprepare(ntp8918->bck);
+
+ return 0;
+}
+
+static int ntp8918_snd_resume(struct snd_soc_component *component)
+{
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = clk_prepare_enable(ntp8918->bck);
+ if (ret)
+ return ret;
+
+ ntp8918_reset_gpio(ntp8918);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8918_sound_on,
+ ARRAY_SIZE(ntp8918_sound_on));
+
+ ret = ntp8918_load_firmware(ntp8918);
+ if (ret) {
+ dev_err(&ntp8918->i2c->dev, "Failed to load firmware\n");
+ return ret;
+ }
+
+ regcache_cache_only(component->regmap, false);
+ snd_soc_component_cache_sync(component);
+
+ return 0;
+}
+
+static int ntp8918_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ ret = snd_soc_add_component_controls(component, ntp8918_vol_control,
+ ARRAY_SIZE(ntp8918_vol_control));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add controls\n");
+
+ ret = ntp8918_load_firmware(ntp8918);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to load firmware\n");
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ntp8918_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+};
+
+static const struct snd_soc_dapm_route ntp8918_dapm_routes[] = {
+ { "OUT1", NULL, "AIFIN" },
+ { "OUT2", NULL, "AIFIN" },
+};
+
+static const struct snd_soc_component_driver soc_component_ntp8918 = {
+ .probe = ntp8918_probe,
+ .suspend = ntp8918_snd_suspend,
+ .resume = ntp8918_snd_resume,
+ .dapm_widgets = ntp8918_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ntp8918_dapm_widgets),
+ .dapm_routes = ntp8918_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ntp8918_dapm_routes),
+};
+
+static int ntp8918_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ unsigned int input_fmt = 0;
+ unsigned int gsa_fmt = 0;
+ unsigned int gsa_fmt_mask;
+ unsigned int mcf;
+ int bclk;
+ int ret;
+
+ bclk = snd_soc_params_to_bclk(params);
+ switch (bclk) {
+ case 3072000:
+ case 2822400:
+ mcf = 0;
+ break;
+ case 6144000:
+ mcf = 1;
+ break;
+ case 2048000:
+ mcf = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8918_MCLK_FREQ_CTRL,
+ NTP8918_MCLK_FREQ_MCF, mcf);
+ if (ret)
+ return ret;
+
+ switch (ntp8918->format) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
+ gsa_fmt |= NTP8918_GSA_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8918_INPUT_FMT,
+ NTP8918_INPUT_FMT_MASTER_MODE |
+ NTP8918_INPUT_FMT_GSA_MODE,
+ input_fmt);
+
+ if (!(input_fmt & NTP8918_INPUT_FMT_GSA_MODE) || ret < 0)
+ return ret;
+
+ switch (params_width(params)) {
+ case 24:
+ gsa_fmt |= NTP8918_GSA_BS(0);
+ break;
+ case 20:
+ gsa_fmt |= NTP8918_GSA_BS(1);
+ break;
+ case 18:
+ gsa_fmt |= NTP8918_GSA_BS(2);
+ break;
+ case 16:
+ gsa_fmt |= NTP8918_GSA_BS(3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ gsa_fmt_mask = NTP8918_GSA_BS_MASK |
+ NTP8918_GSA_RIGHT_J |
+ NTP8918_GSA_LSB;
+ return snd_soc_component_update_bits(component, NTP8918_GSA_FMT,
+ gsa_fmt_mask, gsa_fmt);
+}
+
+static int ntp8918_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ ntp8918->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ntp8918_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ unsigned int mute_mask = NTP8918_SOFT_MUTE_SM1 |
+ NTP8918_SOFT_MUTE_SM2;
+
+ return snd_soc_component_update_bits(dai->component, NTP8918_SOFT_MUTE,
+ mute_mask, mute ? mute_mask : 0);
+}
+
+static const struct snd_soc_dai_ops ntp8918_dai_ops = {
+ .hw_params = ntp8918_hw_params,
+ .set_fmt = ntp8918_set_fmt,
+ .mute_stream = ntp8918_digital_mute,
+};
+
+static struct snd_soc_dai_driver ntp8918_dai = {
+ .name = "ntp8918-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NTP8918_RATES,
+ .formats = NTP8918_FORMATS,
+ },
+ .ops = &ntp8918_dai_ops,
+};
+
+static const struct regmap_config ntp8918_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ntp8918_i2c_probe(struct i2c_client *i2c)
+{
+ struct ntp8918_priv *ntp8918;
+ int ret;
+ struct regmap *regmap;
+
+ ntp8918 = devm_kzalloc(&i2c->dev, sizeof(*ntp8918), GFP_KERNEL);
+ if (!ntp8918)
+ return -ENOMEM;
+
+ ntp8918->i2c = i2c;
+
+ ntp8918->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
+ if (IS_ERR(ntp8918->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->reset), "Failed to get reset\n");
+
+ dev_set_drvdata(&i2c->dev, ntp8918);
+
+ ntp8918_reset_gpio(ntp8918);
+
+ regmap = devm_regmap_init_i2c(i2c, &ntp8918_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to allocate regmap\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8918,
+ &ntp8918_dai, 1);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to register component\n");
+
+ ntp8918->bck = devm_clk_get_enabled(&i2c->dev, "bck");
+ if (IS_ERR(ntp8918->bck))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->bck), "failed to get bck clock\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ntp8918_i2c_id[] = {
+ { "ntp8918" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);
+
+static const struct of_device_id ntp8918_of_match[] = {
+ {.compatible = "neofidelity,ntp8918"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ntp8918_of_match);
+
+static struct i2c_driver ntp8918_i2c_driver = {
+ .probe = ntp8918_i2c_probe,
+ .id_table = ntp8918_i2c_id,
+ .driver = {
+ .name = "ntp8918",
+ .of_match_table = ntp8918_of_match,
+ },
+};
+module_i2c_driver(ntp8918_i2c_driver);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("NTP8918 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c
new file mode 100644
index 000000000000..5ced2e966ab7
--- /dev/null
+++ b/sound/soc/codecs/ntpfw.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ntpfw.c - Firmware helper functions for Neofidelity codecs
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ */
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "ntpfw.h"
+
+struct ntpfw_chunk {
+ __be16 length;
+ u8 step;
+ u8 data[];
+} __packed;
+
+struct ntpfw_header {
+ __be32 magic;
+} __packed;
+
+static bool ntpfw_verify(struct device *dev, const u8 *buf, size_t buf_size, u32 magic)
+{
+ const struct ntpfw_header *header = (struct ntpfw_header *)buf;
+ u32 buf_magic;
+
+ if (buf_size <= sizeof(*header)) {
+ dev_err(dev, "Failed to load firmware: image too small\n");
+ return false;
+ }
+
+ buf_magic = be32_to_cpu(header->magic);
+ if (buf_magic != magic) {
+ dev_err(dev, "Failed to load firmware: invalid magic 0x%x:\n", buf_magic);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ntpfw_verify_chunk(struct device *dev, const struct ntpfw_chunk *chunk, size_t buf_size)
+{
+ size_t chunk_size;
+
+ if (buf_size <= sizeof(*chunk)) {
+ dev_err(dev, "Failed to load firmware: chunk size too big\n");
+ return false;
+ }
+
+ if (chunk->step != 2 && chunk->step != 5) {
+ dev_err(dev, "Failed to load firmware: invalid chunk step: %d\n", chunk->step);
+ return false;
+ }
+
+ chunk_size = be16_to_cpu(chunk->length);
+ if (chunk_size > buf_size) {
+ dev_err(dev, "Failed to load firmware: invalid chunk length\n");
+ return false;
+ }
+
+ if (chunk_size % chunk->step) {
+ dev_err(dev, "Failed to load firmware: chunk length and step mismatch\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int ntpfw_send_chunk(struct i2c_client *i2c, const struct ntpfw_chunk *chunk)
+{
+ int ret;
+ size_t i;
+ size_t length = be16_to_cpu(chunk->length);
+
+ for (i = 0; i < length; i += chunk->step) {
+ ret = i2c_master_send(i2c, &chunk->data[i], chunk->step);
+ if (ret != chunk->step) {
+ dev_err(&i2c->dev, "I2C send failed: %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic)
+{
+ struct device *dev = &i2c->dev;
+ const struct ntpfw_chunk *chunk;
+ const struct firmware *fw;
+ const u8 *data;
+ size_t leftover;
+ int ret;
+
+ ret = request_firmware(&fw, name, dev);
+ if (ret) {
+ dev_warn(dev, "request_firmware '%s' failed with %d\n",
+ name, ret);
+ return ret;
+ }
+
+ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ data = fw->data + sizeof(struct ntpfw_header);
+ leftover = fw->size - sizeof(struct ntpfw_header);
+
+ while (leftover) {
+ chunk = (struct ntpfw_chunk *)data;
+
+ if (!ntpfw_verify_chunk(dev, chunk, leftover)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = ntpfw_send_chunk(i2c, chunk);
+ if (ret)
+ goto done;
+
+ data += be16_to_cpu(chunk->length) + sizeof(*chunk);
+ leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk);
+ }
+
+done:
+ release_firmware(fw);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ntpfw_load);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("Helper for loading Neofidelity amplifiers firmware");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h
new file mode 100644
index 000000000000..1cf10d5480ee
--- /dev/null
+++ b/sound/soc/codecs/ntpfw.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/**
+ * ntpfw.h - Firmware helper functions for Neofidelity codecs
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ */
+
+#ifndef __NTPFW_H__
+#define __NTPFW_H__
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+
+/**
+ * ntpfw_load - load firmware to amplifier over i2c interface.
+ *
+ * @i2c Pointer to amplifier's I2C client.
+ * @name Firmware file name.
+ * @magic Magic number to validate firmware.
+ * @return 0 or error code upon error.
+ */
+int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic);
+
+#endif /* __NTPFW_H__ */
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 58325234285c..f4e5f3133f2b 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -1,29 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM1681 ASoC codec driver
*
* Copyright (c) StreamUnlimited GmbH 2013
* Marek Belisko <marek.belisko@streamunlimited.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -73,7 +61,7 @@ static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
return !((reg == 0x00) || (reg == 0x0f));
}
-static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
{
return pcm1681_accessible_reg(dev, reg) &&
(reg != PCM1681_ZERO_DETECT_STATUS);
@@ -90,10 +78,10 @@ struct pcm1681_private {
static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
-static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+static int pcm1681_set_deemph(struct snd_soc_component *component)
{
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
- int i = 0, val = -1, enable = 0;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
+ int i, val = -1, enable = 0;
if (priv->deemph) {
for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) {
@@ -120,8 +108,8 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec)
static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = priv->deemph;
@@ -131,23 +119,23 @@ static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
priv->deemph = ucontrol->value.integer.value[0];
- return pcm1681_set_deemph(codec);
+ return pcm1681_set_deemph(component);
}
static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
- /* The PCM1681 can only be slave to all clocks */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(codec->dev, "Invalid clocking mode\n");
+ /* The PCM1681 can only be consumer to all clocks */
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_err(component->dev, "Invalid clocking mode\n");
return -EINVAL;
}
@@ -156,10 +144,10 @@ static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm1681_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
int val;
if (mute)
@@ -174,8 +162,8 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
int val = 0, ret;
priv->rate = params_rate(params);
@@ -200,7 +188,7 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
val = 0x05;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -208,13 +196,14 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return pcm1681_set_deemph(codec);
+ return pcm1681_set_deemph(component);
}
static const struct snd_soc_dai_ops pcm1681_dai_ops = {
.set_fmt = pcm1681_set_dai_fmt,
.hw_params = pcm1681_hw_params,
- .digital_mute = pcm1681_digital_mute,
+ .mute_stream = pcm1681_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
@@ -288,23 +277,25 @@ static const struct regmap_config pcm1681_regmap = {
.readable_reg = pcm1681_accessible_reg,
};
-static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
+static const struct snd_soc_component_driver soc_component_dev_pcm1681 = {
.controls = pcm1681_controls,
.num_controls = ARRAY_SIZE(pcm1681_controls),
.dapm_widgets = pcm1681_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets),
.dapm_routes = pcm1681_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct i2c_device_id pcm1681_i2c_id[] = {
- {"pcm1681", 0},
+ {"pcm1681"},
{}
};
MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
-static int pcm1681_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm1681_i2c_probe(struct i2c_client *client)
{
int ret;
struct pcm1681_private *priv;
@@ -322,16 +313,11 @@ static int pcm1681_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, priv);
- return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681,
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_pcm1681,
&pcm1681_dai, 1);
}
-static int pcm1681_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver pcm1681_i2c_driver = {
.driver = {
.name = "pcm1681",
@@ -339,7 +325,6 @@ static struct i2c_driver pcm1681_i2c_driver = {
},
.id_table = pcm1681_i2c_id,
.probe = pcm1681_i2c_probe,
- .remove = pcm1681_i2c_remove,
};
module_i2c_driver(pcm1681_i2c_driver);
diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c
new file mode 100644
index 000000000000..b68a528000be
--- /dev/null
+++ b/sound/soc/codecs/pcm1754.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM1754 DAC ASoC codec driver
+ *
+ * Copyright (c) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
+ * Copyright (c) 2025 Stefan Kerkmann <s.kerkmann@pengutronix.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct pcm1754_priv {
+ unsigned int format;
+ struct gpio_desc *gpiod_mute;
+ struct gpio_desc *gpiod_format;
+};
+
+static int pcm1754_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1754_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+ int format;
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ format = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 16:
+ fallthrough;
+ case 24:
+ format = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_format, format);
+
+ return 0;
+}
+
+static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+ gpiod_set_value_cansleep(priv->gpiod_mute, mute);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1754_dai_ops = {
+ .set_fmt = pcm1754_set_dai_fmt,
+ .hw_params = pcm1754_hw_params,
+ .mute_stream = pcm1754_mute_stream,
+};
+
+static const struct snd_soc_dai_driver pcm1754_dai = {
+ .name = "pcm1754",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5000,
+ .rate_max = 200000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &pcm1754_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget pcm1754_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC1", "Channel 1 Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", "Channel 2 Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm1754_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+
+ { "DAC1", NULL, "VCC" },
+ { "DAC2", NULL, "VCC" },
+
+ { "VOUTL", NULL, "DAC1" },
+ { "VOUTR", NULL, "DAC2" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1754 = {
+ .dapm_widgets = pcm1754_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1754_dapm_widgets),
+ .dapm_routes = pcm1754_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1754_dapm_routes),
+};
+
+static int pcm1754_probe(struct platform_device *pdev)
+{
+ struct pcm1754_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_driver *dai_drv;
+ int ret;
+
+ dai_drv = devm_kmemdup(dev, &pcm1754_dai, sizeof(*dai_drv), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->gpiod_mute = devm_gpiod_get_optional(dev, "mute", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_mute))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute),
+ "failed to get mute gpio");
+
+ priv->gpiod_format = devm_gpiod_get_optional(dev, "format", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_format))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_format),
+ "failed to get format gpio");
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_snd_soc_register_component(
+ &pdev->dev, &soc_component_dev_pcm1754, dai_drv, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1754_of_match[] = {
+ { .compatible = "ti,pcm1754" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1754_of_match);
+#endif
+
+static struct platform_driver pcm1754_codec_driver = {
+ .driver = {
+ .name = "pcm1754-codec",
+ .of_match_table = of_match_ptr(pcm1754_of_match),
+ },
+ .probe = pcm1754_probe,
+};
+
+module_platform_driver(pcm1754_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1754 driver");
+MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+MODULE_AUTHOR("Stefan Kerkmann <s.kerkmann@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c
new file mode 100644
index 000000000000..abadf4f8ed5e
--- /dev/null
+++ b/sound/soc/codecs/pcm1789-i2c.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+// Audio driver for PCM1789 I2C
+// Copyright (C) 2018 Bootlin
+// Mylène Josserand <mylene.josserand@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "pcm1789.h"
+
+static int pcm1789_i2c_probe(struct i2c_client *client)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &pcm1789_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm1789_common_init(&client->dev, regmap);
+}
+
+static void pcm1789_i2c_remove(struct i2c_client *client)
+{
+ pcm1789_common_exit(&client->dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1789_of_match[] = {
+ { .compatible = "ti,pcm1789", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1789_of_match);
+#endif
+
+static const struct i2c_device_id pcm1789_i2c_ids[] = {
+ { "pcm1789" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm1789_i2c_ids);
+
+static struct i2c_driver pcm1789_i2c_driver = {
+ .driver = {
+ .name = "pcm1789",
+ .of_match_table = of_match_ptr(pcm1789_of_match),
+ },
+ .id_table = pcm1789_i2c_ids,
+ .probe = pcm1789_i2c_probe,
+ .remove = pcm1789_i2c_remove,
+};
+
+module_i2c_driver(pcm1789_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1789 I2C driver");
+MODULE_AUTHOR("Mylène Josserand <mylene.josserand@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c
new file mode 100644
index 000000000000..3ab381e9a856
--- /dev/null
+++ b/sound/soc/codecs/pcm1789.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+// Audio driver for PCM1789
+// Copyright (C) 2018 Bootlin
+// Mylène Josserand <mylene.josserand@bootlin.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm1789.h"
+
+#define PCM1789_MUTE_CONTROL 0x10
+#define PCM1789_FMT_CONTROL 0x11
+#define PCM1789_SOFT_MUTE 0x14
+#define PCM1789_DAC_VOL_LEFT 0x18
+#define PCM1789_DAC_VOL_RIGHT 0x19
+
+#define PCM1789_FMT_MASK 0x07
+#define PCM1789_MUTE_MASK 0x03
+#define PCM1789_MUTE_SRET 0x06
+
+struct pcm1789_private {
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int rate;
+ struct gpio_desc *reset;
+ struct work_struct work;
+ struct device *dev;
+};
+
+static const struct reg_default pcm1789_reg_defaults[] = {
+ { PCM1789_FMT_CONTROL, 0x00 },
+ { PCM1789_SOFT_MUTE, 0x00 },
+ { PCM1789_DAC_VOL_LEFT, 0xff },
+ { PCM1789_DAC_VOL_RIGHT, 0xff },
+};
+
+static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT;
+}
+
+static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return pcm1789_accessible_reg(dev, reg);
+}
+
+static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+
+ return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE,
+ PCM1789_MUTE_MASK,
+ mute ? 0 : PCM1789_MUTE_MASK);
+}
+
+static int pcm1789_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+ int val = 0, ret;
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 24:
+ val = 2;
+ break;
+ case 16:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL,
+ PCM1789_FMT_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void pcm1789_work_queue(struct work_struct *work)
+{
+ struct pcm1789_private *priv = container_of(work,
+ struct pcm1789_private,
+ work);
+
+ /* Perform a software reset to remove codec from desynchronized state */
+ if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL,
+ 0x3 << PCM1789_MUTE_SRET, 0) < 0)
+ dev_err(priv->dev, "Error while setting SRET");
+}
+
+static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ schedule_work(&priv->work);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops pcm1789_dai_ops = {
+ .set_fmt = pcm1789_set_dai_fmt,
+ .hw_params = pcm1789_hw_params,
+ .mute_stream = pcm1789_mute,
+ .trigger = pcm1789_trigger,
+ .no_capture_mute = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm1789_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT,
+ PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+ pcm1789_dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("IOUTL+"),
+ SND_SOC_DAPM_OUTPUT("IOUTL-"),
+ SND_SOC_DAPM_OUTPUT("IOUTR+"),
+ SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = {
+ { "IOUTL+", NULL, "Playback" },
+ { "IOUTL-", NULL, "Playback" },
+ { "IOUTR+", NULL, "Playback" },
+ { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm1789_dai = {
+ .name = "pcm1789-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 10000,
+ .rate_max = 200000,
+ .formats = PCM1789_FORMATS,
+ },
+ .ops = &pcm1789_dai_ops,
+};
+
+const struct regmap_config pcm1789_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PCM1789_DAC_VOL_RIGHT,
+ .reg_defaults = pcm1789_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1789_reg_defaults),
+ .writeable_reg = pcm1789_writeable_reg,
+ .readable_reg = pcm1789_accessible_reg,
+};
+EXPORT_SYMBOL_GPL(pcm1789_regmap_config);
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1789 = {
+ .controls = pcm1789_controls,
+ .num_controls = ARRAY_SIZE(pcm1789_controls),
+ .dapm_widgets = pcm1789_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1789_dapm_widgets),
+ .dapm_routes = pcm1789_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1789_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+int pcm1789_common_init(struct device *dev, struct regmap *regmap)
+{
+ struct pcm1789_private *pcm1789;
+
+ pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private),
+ GFP_KERNEL);
+ if (!pcm1789)
+ return -ENOMEM;
+
+ pcm1789->regmap = regmap;
+ pcm1789->dev = dev;
+ dev_set_drvdata(dev, pcm1789);
+
+ pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(pcm1789->reset))
+ return PTR_ERR(pcm1789->reset);
+
+ gpiod_set_value_cansleep(pcm1789->reset, 0);
+ msleep(300);
+
+ INIT_WORK(&pcm1789->work, pcm1789_work_queue);
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789,
+ &pcm1789_dai, 1);
+}
+EXPORT_SYMBOL_GPL(pcm1789_common_init);
+
+void pcm1789_common_exit(struct device *dev)
+{
+ struct pcm1789_private *priv = dev_get_drvdata(dev);
+
+ flush_work(&priv->work);
+}
+EXPORT_SYMBOL_GPL(pcm1789_common_exit);
+
+MODULE_DESCRIPTION("ASoC PCM1789 driver");
+MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789.h b/sound/soc/codecs/pcm1789.h
new file mode 100644
index 000000000000..79439c8322b3
--- /dev/null
+++ b/sound/soc/codecs/pcm1789.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+// Definitions for PCM1789 audio driver
+// Copyright (C) 2018 Bootlin
+// Mylène Josserand <mylene.josserand@bootlin.com>
+
+#ifndef __PCM1789_H__
+#define __PCM1789_H__
+
+#define PCM1789_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+extern const struct regmap_config pcm1789_regmap_config;
+
+int pcm1789_common_init(struct device *dev, struct regmap *regmap);
+void pcm1789_common_exit(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
new file mode 100644
index 000000000000..effc1dd6df22
--- /dev/null
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM179X ASoC I2C driver
+ *
+ * Copyright (c) Teenage Engineering AB 2016
+ *
+ * Jacob Siverskog <jacob@teenage.engineering>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_i2c_probe(struct i2c_client *client)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm179x_common_init(&client->dev, regmap);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm179x_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+#endif
+
+static const struct i2c_device_id pcm179x_i2c_ids[] = {
+ { "pcm179x" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
+
+static struct i2c_driver pcm179x_i2c_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_i2c_ids,
+ .probe = pcm179x_i2c_probe,
+};
+
+module_i2c_driver(pcm179x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X I2C driver");
+MODULE_AUTHOR("Jacob Siverskog <jacob@teenage.engineering>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
new file mode 100644
index 000000000000..192fee90c971
--- /dev/null
+++ b/sound/soc/codecs/pcm179x-spi.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM179X ASoC SPI driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm179x_common_init(&spi->dev, regmap);
+}
+
+static const struct of_device_id pcm179x_of_match[] __maybe_unused = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct spi_device_id pcm179x_spi_ids[] = {
+ { "pcm1792a", 0 },
+ { "pcm179x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
+
+static struct spi_driver pcm179x_spi_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_spi_ids,
+ .probe = pcm179x_spi_probe,
+};
+
+module_spi_driver(pcm179x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X SPI driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index a56c7b767d90..f52ff66b6e64 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -1,26 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM179X 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>
@@ -29,7 +19,6 @@
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include "pcm179x.h"
@@ -61,7 +50,7 @@ static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
return reg >= 0x10 && reg <= 0x17;
}
-static bool pcm179x_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
{
bool accessible;
@@ -79,18 +68,18 @@ struct pcm179x_private {
static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
priv->format = format;
return 0;
}
-static int pcm179x_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm179x_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
int ret;
ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
@@ -105,8 +94,8 @@ static int pcm179x_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 pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
int val = 0, ret;
priv->rate = params_rate(params);
@@ -139,7 +128,7 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream,
}
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -156,7 +145,8 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops pcm179x_dai_ops = {
.set_fmt = pcm179x_set_dai_fmt,
.hw_params = pcm179x_hw_params,
- .digital_mute = pcm179x_digital_mute,
+ .mute_stream = pcm179x_mute,
+ .no_capture_mute = 1,
};
static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
@@ -189,18 +179,14 @@ static struct snd_soc_dai_driver pcm179x_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = PCM1792A_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 10000,
+ .rate_max = 200000,
.formats = PCM1792A_FORMATS, },
.ops = &pcm179x_dai_ops,
};
-static const struct of_device_id pcm179x_of_match[] = {
- { .compatible = "ti,pcm1792a", },
- { }
-};
-MODULE_DEVICE_TABLE(of, pcm179x_of_match);
-
-static const struct regmap_config pcm179x_regmap = {
+const struct regmap_config pcm179x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 23,
@@ -209,62 +195,36 @@ static const struct regmap_config pcm179x_regmap = {
.writeable_reg = pcm179x_writeable_reg,
.readable_reg = pcm179x_accessible_reg,
};
+EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
-static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
+static const struct snd_soc_component_driver soc_component_dev_pcm179x = {
.controls = pcm179x_controls,
.num_controls = ARRAY_SIZE(pcm179x_controls),
.dapm_widgets = pcm179x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets),
.dapm_routes = pcm179x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int pcm179x_spi_probe(struct spi_device *spi)
+int pcm179x_common_init(struct device *dev, struct regmap *regmap)
{
struct pcm179x_private *pcm179x;
- int ret;
- pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private),
+ pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
GFP_KERNEL);
if (!pcm179x)
return -ENOMEM;
- spi_set_drvdata(spi, pcm179x);
-
- pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap);
- if (IS_ERR(pcm179x->regmap)) {
- ret = PTR_ERR(pcm179x->regmap);
- dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
- return ret;
- }
-
- return snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_pcm179x, &pcm179x_dai, 1);
-}
+ pcm179x->regmap = regmap;
+ dev_set_drvdata(dev, pcm179x);
-static int pcm179x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_pcm179x, &pcm179x_dai, 1);
}
-
-static const struct spi_device_id pcm179x_spi_ids[] = {
- { "pcm179x", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
-
-static struct spi_driver pcm179x_codec_driver = {
- .driver = {
- .name = "pcm179x",
- .of_match_table = of_match_ptr(pcm179x_of_match),
- },
- .id_table = pcm179x_spi_ids,
- .probe = pcm179x_spi_probe,
- .remove = pcm179x_spi_remove,
-};
-
-module_spi_driver(pcm179x_codec_driver);
+EXPORT_SYMBOL_GPL(pcm179x_common_init);
MODULE_DESCRIPTION("ASoC PCM179X driver");
MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h
index c6fdc062a497..0039ca8ee742 100644
--- a/sound/soc/codecs/pcm179x.h
+++ b/sound/soc/codecs/pcm179x.h
@@ -1,27 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* definitions for PCM179X
*
* 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 __PCM179X_H__
#define __PCM179X_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)
+extern const struct regmap_config pcm179x_regmap_config;
+
+int pcm179x_common_init(struct device *dev, struct regmap *regmap);
+
#endif
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c
new file mode 100644
index 000000000000..a50f9f6e39c1
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC - I2C
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "pcm186x.h"
+
+static const struct of_device_id pcm186x_of_match[] = {
+ { .compatible = "ti,pcm1862", .data = (void *)PCM1862 },
+ { .compatible = "ti,pcm1863", .data = (void *)PCM1863 },
+ { .compatible = "ti,pcm1864", .data = (void *)PCM1864 },
+ { .compatible = "ti,pcm1865", .data = (void *)PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm186x_of_match);
+
+static const struct i2c_device_id pcm186x_i2c_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
+
+static int pcm186x_i2c_probe(struct i2c_client *i2c)
+{
+ const enum pcm186x_type type = (uintptr_t)i2c_get_match_data(i2c);
+ int irq = i2c->irq;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm186x_probe(&i2c->dev, type, irq, regmap);
+}
+
+static struct i2c_driver pcm186x_i2c_driver = {
+ .probe = pcm186x_i2c_probe,
+ .id_table = pcm186x_i2c_id,
+ .driver = {
+ .name = "pcm186x",
+ .of_match_table = pcm186x_of_match,
+ },
+};
+module_i2c_driver(pcm186x_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC I2C Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c
new file mode 100644
index 000000000000..bc1b0f0698ed
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-spi.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC - SPI
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "pcm186x.h"
+
+static const struct of_device_id pcm186x_of_match[] = {
+ { .compatible = "ti,pcm1862", .data = (void *)PCM1862 },
+ { .compatible = "ti,pcm1863", .data = (void *)PCM1863 },
+ { .compatible = "ti,pcm1864", .data = (void *)PCM1864 },
+ { .compatible = "ti,pcm1865", .data = (void *)PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm186x_of_match);
+
+static int pcm186x_spi_probe(struct spi_device *spi)
+{
+ const enum pcm186x_type type =
+ (enum pcm186x_type)spi_get_device_id(spi)->driver_data;
+ int irq = spi->irq;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &pcm186x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm186x_probe(&spi->dev, type, irq, regmap);
+}
+
+static const struct spi_device_id pcm186x_spi_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, pcm186x_spi_id);
+
+static struct spi_driver pcm186x_spi_driver = {
+ .probe = pcm186x_spi_probe,
+ .id_table = pcm186x_spi_id,
+ .driver = {
+ .name = "pcm186x",
+ .of_match_table = pcm186x_of_match,
+ },
+};
+module_spi_driver(pcm186x_spi_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC SPI Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c
new file mode 100644
index 000000000000..0d1103fe4e04
--- /dev/null
+++ b/sound/soc/codecs/pcm186x.c
@@ -0,0 +1,706 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "pcm186x.h"
+
+static const char * const pcm186x_supply_names[] = {
+ "avdd", /* Analog power supply. Connect to 3.3-V supply. */
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "iovdd", /* I/O power supply. Connect to 3.3-V or 1.8-V. */
+};
+#define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names)
+
+struct pcm186x_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES];
+ unsigned int sysclk;
+ unsigned int tdm_offset;
+ bool is_tdm_mode;
+ bool is_provider_mode;
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0);
+
+static const struct snd_kcontrol_new pcm1863_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L,
+ PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+};
+
+static const struct snd_kcontrol_new pcm1865_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L,
+ PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L,
+ PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+};
+
+static const unsigned int pcm186x_adc_input_channel_sel_value[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x20, 0x30
+};
+
+static const char * const pcm186x_adcl_input_channel_sel_text[] = {
+ "No Select",
+ "VINL1[SE]", /* Default for ADC1L */
+ "VINL2[SE]", /* Default for ADC2L */
+ "VINL2[SE] + VINL1[SE]",
+ "VINL3[SE]",
+ "VINL3[SE] + VINL1[SE]",
+ "VINL3[SE] + VINL2[SE]",
+ "VINL3[SE] + VINL2[SE] + VINL1[SE]",
+ "VINL4[SE]",
+ "VINL4[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL2[SE]",
+ "VINL4[SE] + VINL2[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL3[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL2[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]",
+ "{VIN1P, VIN1M}[DIFF]",
+ "{VIN4P, VIN4M}[DIFF]",
+ "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]"
+};
+
+static const char * const pcm186x_adcr_input_channel_sel_text[] = {
+ "No Select",
+ "VINR1[SE]", /* Default for ADC1R */
+ "VINR2[SE]", /* Default for ADC2R */
+ "VINR2[SE] + VINR1[SE]",
+ "VINR3[SE]",
+ "VINR3[SE] + VINR1[SE]",
+ "VINR3[SE] + VINR2[SE]",
+ "VINR3[SE] + VINR2[SE] + VINR1[SE]",
+ "VINR4[SE]",
+ "VINR4[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR2[SE]",
+ "VINR4[SE] + VINR2[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR3[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR2[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]",
+ "{VIN2P, VIN2M}[DIFF]",
+ "{VIN3P, VIN3M}[DIFF]",
+ "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]"
+};
+
+static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+ pcm186x_adcl_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+ pcm186x_adcr_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+ pcm186x_adcl_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+ pcm186x_adcr_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+};
+
+static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = {
+ SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]),
+ SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]),
+ SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]),
+ SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]),
+};
+
+static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_INPUT("VINL3"),
+ SND_SOC_DAPM_INPUT("VINR3"),
+ SND_SOC_DAPM_INPUT("VINL4"),
+ SND_SOC_DAPM_INPUT("VINR4"),
+
+ SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[0]),
+ SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[1]),
+
+ /*
+ * Put the codec into SLEEP mode when not in use, allowing the
+ * Energysense mechanism to operate.
+ */
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_INPUT("VINL3"),
+ SND_SOC_DAPM_INPUT("VINR3"),
+ SND_SOC_DAPM_INPUT("VINL4"),
+ SND_SOC_DAPM_INPUT("VINR4"),
+
+ SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[0]),
+ SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[1]),
+ SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[2]),
+ SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[3]),
+
+ /*
+ * Put the codec into SLEEP mode when not in use, allowing the
+ * Energysense mechanism to operate.
+ */
+ SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 1),
+ SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 1),
+};
+
+static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = {
+ { "ADC Left Capture Source", NULL, "VINL1" },
+ { "ADC Left Capture Source", NULL, "VINR1" },
+ { "ADC Left Capture Source", NULL, "VINL2" },
+ { "ADC Left Capture Source", NULL, "VINR2" },
+ { "ADC Left Capture Source", NULL, "VINL3" },
+ { "ADC Left Capture Source", NULL, "VINR3" },
+ { "ADC Left Capture Source", NULL, "VINL4" },
+ { "ADC Left Capture Source", NULL, "VINR4" },
+
+ { "ADC", NULL, "ADC Left Capture Source" },
+
+ { "ADC Right Capture Source", NULL, "VINL1" },
+ { "ADC Right Capture Source", NULL, "VINR1" },
+ { "ADC Right Capture Source", NULL, "VINL2" },
+ { "ADC Right Capture Source", NULL, "VINR2" },
+ { "ADC Right Capture Source", NULL, "VINL3" },
+ { "ADC Right Capture Source", NULL, "VINR3" },
+ { "ADC Right Capture Source", NULL, "VINL4" },
+ { "ADC Right Capture Source", NULL, "VINR4" },
+
+ { "ADC", NULL, "ADC Right Capture Source" },
+};
+
+static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = {
+ { "ADC1 Left Capture Source", NULL, "VINL1" },
+ { "ADC1 Left Capture Source", NULL, "VINR1" },
+ { "ADC1 Left Capture Source", NULL, "VINL2" },
+ { "ADC1 Left Capture Source", NULL, "VINR2" },
+ { "ADC1 Left Capture Source", NULL, "VINL3" },
+ { "ADC1 Left Capture Source", NULL, "VINR3" },
+ { "ADC1 Left Capture Source", NULL, "VINL4" },
+ { "ADC1 Left Capture Source", NULL, "VINR4" },
+
+ { "ADC1", NULL, "ADC1 Left Capture Source" },
+
+ { "ADC1 Right Capture Source", NULL, "VINL1" },
+ { "ADC1 Right Capture Source", NULL, "VINR1" },
+ { "ADC1 Right Capture Source", NULL, "VINL2" },
+ { "ADC1 Right Capture Source", NULL, "VINR2" },
+ { "ADC1 Right Capture Source", NULL, "VINL3" },
+ { "ADC1 Right Capture Source", NULL, "VINR3" },
+ { "ADC1 Right Capture Source", NULL, "VINL4" },
+ { "ADC1 Right Capture Source", NULL, "VINR4" },
+
+ { "ADC1", NULL, "ADC1 Right Capture Source" },
+
+ { "ADC2 Left Capture Source", NULL, "VINL1" },
+ { "ADC2 Left Capture Source", NULL, "VINR1" },
+ { "ADC2 Left Capture Source", NULL, "VINL2" },
+ { "ADC2 Left Capture Source", NULL, "VINR2" },
+ { "ADC2 Left Capture Source", NULL, "VINL3" },
+ { "ADC2 Left Capture Source", NULL, "VINR3" },
+ { "ADC2 Left Capture Source", NULL, "VINL4" },
+ { "ADC2 Left Capture Source", NULL, "VINR4" },
+
+ { "ADC2", NULL, "ADC2 Left Capture Source" },
+
+ { "ADC2 Right Capture Source", NULL, "VINL1" },
+ { "ADC2 Right Capture Source", NULL, "VINR1" },
+ { "ADC2 Right Capture Source", NULL, "VINL2" },
+ { "ADC2 Right Capture Source", NULL, "VINR2" },
+ { "ADC2 Right Capture Source", NULL, "VINL3" },
+ { "ADC2 Right Capture Source", NULL, "VINR3" },
+ { "ADC2 Right Capture Source", NULL, "VINL4" },
+ { "ADC2 Right Capture Source", NULL, "VINR4" },
+
+ { "ADC2", NULL, "ADC2 Right Capture Source" },
+};
+
+static int pcm186x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int rate = params_rate(params);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int width = params_width(params);
+ unsigned int channels = params_channels(params);
+ unsigned int div_lrck;
+ unsigned int div_bck;
+ u8 tdm_tx_sel = 0;
+ u8 pcm_cfg = 0;
+
+ dev_dbg(component->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
+ __func__, rate, format, width, channels);
+
+ switch (width) {
+ case 16:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_16 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 20:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_20 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 24:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_24 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 32:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_32 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_RX_WLEN_MASK |
+ PCM186X_PCM_CFG_TX_WLEN_MASK,
+ pcm_cfg);
+
+ div_lrck = width * channels;
+
+ if (priv->is_tdm_mode) {
+ /* Select TDM transmission data */
+ switch (channels) {
+ case 2:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH;
+ break;
+ case 4:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH;
+ break;
+ case 6:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_TDM_TX_SEL,
+ PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel);
+
+ /* In DSP/TDM mode, the LRCLK divider must be 256 */
+ div_lrck = 256;
+
+ /* Configure 1/256 duty cycle for LRCK */
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_TDM_LRCK_MODE,
+ PCM186X_PCM_CFG_TDM_LRCK_MODE);
+ }
+
+ /* Only configure clock dividers in provider mode. */
+ if (priv->is_provider_mode) {
+ div_bck = priv->sysclk / (div_lrck * rate);
+
+ dev_dbg(component->dev,
+ "%s() master_clk=%u div_bck=%u div_lrck=%u\n",
+ __func__, priv->sysclk, div_bck, div_lrck);
+
+ snd_soc_component_write(component, PCM186X_BCK_DIV, div_bck - 1);
+ snd_soc_component_write(component, PCM186X_LRK_DIV, div_lrck - 1);
+ }
+
+ return 0;
+}
+
+static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ u8 clk_ctrl = 0;
+ u8 pcm_cfg = 0;
+
+ dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format);
+
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (!priv->sysclk) {
+ dev_err(component->dev, "operating in provider mode requires sysclock to be configured\n");
+ return -EINVAL;
+ }
+ clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE;
+ priv->is_provider_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->is_provider_mode = false;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ /* set interface polarity */
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(component->dev, "Inverted DAI clocks not supported\n");
+ return -EINVAL;
+ }
+
+ /* set interface format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcm_cfg = PCM186X_PCM_CFG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ priv->tdm_offset += 1;
+ fallthrough;
+ /* DSP_A uses the same basic config as DSP_B
+ * except we need to shift the TDM output by one BCK cycle
+ */
+ case SND_SOC_DAIFMT_DSP_B:
+ priv->is_tdm_mode = true;
+ pcm_cfg = PCM186X_PCM_CFG_FMT_TDM;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_CLK_CTRL,
+ PCM186X_CLK_CTRL_MST_MODE, clk_ctrl);
+
+ snd_soc_component_write(component, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);
+
+ snd_soc_component_update_bits(component, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_FMT_MASK, pcm_cfg);
+
+ return 0;
+}
+
+static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int first_slot, last_slot, tdm_offset;
+
+ dev_dbg(component->dev,
+ "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (!tx_mask) {
+ dev_err(component->dev, "tdm tx mask must not be 0\n");
+ return -EINVAL;
+ }
+
+ first_slot = __ffs(tx_mask);
+ last_slot = __fls(tx_mask);
+
+ if (last_slot - first_slot != hweight32(tx_mask) - 1) {
+ dev_err(component->dev, "tdm tx mask must be contiguous\n");
+ return -EINVAL;
+ }
+
+ tdm_offset = first_slot * slot_width;
+
+ if (tdm_offset > 255) {
+ dev_err(component->dev, "tdm tx slot selection out of bounds\n");
+ return -EINVAL;
+ }
+
+ priv->tdm_offset = tdm_offset;
+
+ return 0;
+}
+
+static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ __func__, clk_id, freq, dir);
+
+ priv->sysclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm186x_dai_ops = {
+ .set_sysclk = pcm186x_set_dai_sysclk,
+ .set_tdm_slot = pcm186x_set_tdm_slot,
+ .set_fmt = pcm186x_set_fmt,
+ .hw_params = pcm186x_hw_params,
+};
+
+static struct snd_soc_dai_driver pcm1863_dai = {
+ .name = "pcm1863-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM186X_RATES,
+ .formats = PCM186X_FORMATS,
+ },
+ .ops = &pcm186x_dai_ops,
+};
+
+static struct snd_soc_dai_driver pcm1865_dai = {
+ .name = "pcm1865-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = PCM186X_RATES,
+ .formats = PCM186X_FORMATS,
+ },
+ .ops = &pcm186x_dai_ops,
+};
+
+static int pcm186x_power_on(struct snd_soc_component *component)
+{
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(priv->regmap, false);
+ ret = regcache_sync(priv->regmap);
+ if (ret) {
+ dev_err(component->dev, "Failed to restore cache\n");
+ regcache_cache_only(priv->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ return ret;
+ }
+
+ snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
+ PCM186X_PWR_CTRL_PWRDN, 0);
+
+ return 0;
+}
+
+static int pcm186x_power_off(struct snd_soc_component *component)
+{
+ struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
+ PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
+
+ regcache_cache_only(priv->regmap, true);
+
+ return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+}
+
+static int pcm186x_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ dev_dbg(component->dev, "## %s: %d -> %d\n", __func__,
+ snd_soc_dapm_get_bias_level(dapm), level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ pcm186x_power_on(component);
+ break;
+ case SND_SOC_BIAS_OFF:
+ pcm186x_power_off(component);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_pcm1863 = {
+ .set_bias_level = pcm186x_set_bias_level,
+ .controls = pcm1863_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm1863_snd_controls),
+ .dapm_widgets = pcm1863_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1863_dapm_widgets),
+ .dapm_routes = pcm1863_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1863_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_pcm1865 = {
+ .set_bias_level = pcm186x_set_bias_level,
+ .controls = pcm1865_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm1865_snd_controls),
+ .dapm_widgets = pcm1865_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1865_dapm_widgets),
+ .dapm_routes = pcm1865_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1865_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static bool pcm186x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM186X_PAGE:
+ case PCM186X_DEVICE_STATUS:
+ case PCM186X_FSAMPLE_STATUS:
+ case PCM186X_DIV_STATUS:
+ case PCM186X_CLK_STATUS:
+ case PCM186X_SUPPLY_STATUS:
+ case PCM186X_MMAP_STAT_CTRL:
+ case PCM186X_MMAP_ADDRESS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_range_cfg pcm186x_range = {
+ .name = "Pages",
+ .range_max = PCM186X_MAX_REGISTER,
+ .selector_reg = PCM186X_PAGE,
+ .selector_mask = 0xff,
+ .window_len = PCM186X_PAGE_LEN,
+};
+
+const struct regmap_config pcm186x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .volatile_reg = pcm186x_volatile,
+
+ .ranges = &pcm186x_range,
+ .num_ranges = 1,
+
+ .max_register = PCM186X_MAX_REGISTER,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(pcm186x_regmap);
+
+int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
+ struct regmap *regmap)
+{
+ struct pcm186x_priv *priv;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(priv->supplies); i++)
+ priv->supplies[i].supply = pcm186x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset device registers for a consistent power-on like state */
+ ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET);
+ if (ret) {
+ dev_err(dev, "failed to write device: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ switch (type) {
+ case PCM1865:
+ case PCM1864:
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1865,
+ &pcm1865_dai, 1);
+ break;
+ case PCM1863:
+ case PCM1862:
+ default:
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1863,
+ &pcm1863_dai, 1);
+ }
+ if (ret) {
+ dev_err(dev, "failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcm186x_probe);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h
new file mode 100644
index 000000000000..4d493754a3e2
--- /dev/null
+++ b/sound/soc/codecs/pcm186x.h
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef _PCM186X_H_
+#define _PCM186X_H_
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+enum pcm186x_type {
+ PCM1862,
+ PCM1863,
+ PCM1864,
+ PCM1865,
+};
+
+#define PCM186X_RATES SNDRV_PCM_RATE_8000_192000
+#define PCM186X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM186X_PAGE_LEN 0x0100
+#define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n)
+
+/* The page selection register address is the same on all pages */
+#define PCM186X_PAGE 0
+
+/* Register Definitions - Page 0 */
+#define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1)
+#define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2)
+#define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3)
+#define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4)
+#define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5)
+#define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6)
+#define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7)
+#define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8)
+#define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9)
+#define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10)
+#define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11)
+#define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12)
+#define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13)
+#define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14)
+#define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15)
+#define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16)
+#define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17)
+#define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18)
+#define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19)
+#define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20)
+#define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21)
+#define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22)
+#define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23)
+#define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24)
+#define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25)
+#define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26)
+#define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27)
+#define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32)
+#define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33)
+#define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34)
+#define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35)
+#define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37)
+#define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38)
+#define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39)
+#define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40)
+#define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41)
+#define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42)
+#define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43)
+#define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44)
+#define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45)
+#define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48)
+#define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49)
+#define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50)
+#define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52)
+#define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53)
+#define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54)
+#define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64)
+#define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65)
+#define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66)
+#define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67)
+#define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68)
+#define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69)
+#define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70)
+#define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71)
+#define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72)
+#define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73)
+#define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74)
+#define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75)
+#define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76)
+#define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77)
+#define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78)
+#define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79)
+#define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80)
+#define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81)
+#define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82)
+#define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83)
+#define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84)
+#define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85)
+#define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86)
+#define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87)
+#define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88)
+#define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89)
+#define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90)
+#define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96)
+#define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97)
+#define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98)
+#define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112)
+#define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113)
+#define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114)
+#define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115)
+#define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116)
+#define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117)
+#define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120)
+
+/* Register Definitions - Page 1 */
+#define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1)
+#define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2)
+#define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4)
+#define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5)
+#define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6)
+#define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7)
+#define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8)
+#define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9)
+#define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10)
+#define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11)
+
+/* Register Definitions - Page 3 */
+#define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18)
+#define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21)
+
+/* Register Definitions - Page 253 */
+#define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20)
+
+#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL
+
+/* PCM186X_PAGE */
+#define PCM186X_RESET 0xfe
+
+/* PCM186X_ADCX_INPUT_SEL_X */
+#define PCM186X_ADC_INPUT_SEL_POL BIT(7)
+#define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0)
+
+/* PCM186X_PCM_CFG */
+#define PCM186X_PCM_CFG_RX_WLEN_MASK GENMASK(7, 6)
+#define PCM186X_PCM_CFG_RX_WLEN_SHIFT 6
+#define PCM186X_PCM_CFG_RX_WLEN_32 0x00
+#define PCM186X_PCM_CFG_RX_WLEN_24 0x01
+#define PCM186X_PCM_CFG_RX_WLEN_20 0x02
+#define PCM186X_PCM_CFG_RX_WLEN_16 0x03
+#define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4)
+#define PCM186X_PCM_CFG_TX_WLEN_MASK GENMASK(3, 2)
+#define PCM186X_PCM_CFG_TX_WLEN_SHIFT 2
+#define PCM186X_PCM_CFG_TX_WLEN_32 0x00
+#define PCM186X_PCM_CFG_TX_WLEN_24 0x01
+#define PCM186X_PCM_CFG_TX_WLEN_20 0x02
+#define PCM186X_PCM_CFG_TX_WLEN_16 0x03
+#define PCM186X_PCM_CFG_FMT_MASK GENMASK(1, 0)
+#define PCM186X_PCM_CFG_FMT_SHIFT 0
+#define PCM186X_PCM_CFG_FMT_I2S 0x00
+#define PCM186X_PCM_CFG_FMT_LEFTJ 0x01
+#define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02
+#define PCM186X_PCM_CFG_FMT_TDM 0x03
+
+/* PCM186X_TDM_TX_SEL */
+#define PCM186X_TDM_TX_SEL_2CH 0x00
+#define PCM186X_TDM_TX_SEL_4CH 0x01
+#define PCM186X_TDM_TX_SEL_6CH 0x02
+#define PCM186X_TDM_TX_SEL_MASK 0x03
+
+/* PCM186X_CLK_CTRL */
+#define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7)
+#define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6)
+#define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5)
+#define PCM186X_CLK_CTRL_MST_MODE BIT(4)
+#define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3)
+#define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2)
+#define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1)
+#define PCM186X_CLK_CTRL_CLKDET_EN BIT(0)
+
+/* PCM186X_PLL_CTRL */
+#define PCM186X_PLL_CTRL_LOCK BIT(4)
+#define PCM186X_PLL_CTRL_REF_SEL BIT(1)
+#define PCM186X_PLL_CTRL_EN BIT(0)
+
+/* PCM186X_POWER_CTRL */
+#define PCM186X_PWR_CTRL_PWRDN BIT(2)
+#define PCM186X_PWR_CTRL_SLEEP BIT(1)
+#define PCM186X_PWR_CTRL_STBY BIT(0)
+
+/* PCM186X_CLK_STATUS */
+#define PCM186X_CLK_STATUS_LRCKHLT BIT(6)
+#define PCM186X_CLK_STATUS_BCKHLT BIT(5)
+#define PCM186X_CLK_STATUS_SCKHLT BIT(4)
+#define PCM186X_CLK_STATUS_LRCKERR BIT(2)
+#define PCM186X_CLK_STATUS_BCKERR BIT(1)
+#define PCM186X_CLK_STATUS_SCKERR BIT(0)
+
+/* PCM186X_SUPPLY_STATUS */
+#define PCM186X_SUPPLY_STATUS_DVDD BIT(2)
+#define PCM186X_SUPPLY_STATUS_AVDD BIT(1)
+#define PCM186X_SUPPLY_STATUS_LDO BIT(0)
+
+/* PCM186X_MMAP_STAT_CTRL */
+#define PCM186X_MMAP_STAT_DONE BIT(4)
+#define PCM186X_MMAP_STAT_BUSY BIT(2)
+#define PCM186X_MMAP_STAT_R_REQ BIT(1)
+#define PCM186X_MMAP_STAT_W_REQ BIT(0)
+
+extern const struct regmap_config pcm186x_regmap;
+
+int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
+ struct regmap *regmap);
+
+#endif /* _PCM186X_H_ */
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index 8fb445f33f6f..d3d2e7f40170 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Soc PCM3008 codec support
*
@@ -7,18 +8,13 @@
* Based on AC97 Soc codec, original copyright follow:
* Copyright 2005 Wolfson Microelectronics PLC.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Generic PCM3008 support.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -26,17 +22,22 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include "pcm3008.h"
+struct pcm3008 {
+ struct gpio_desc *dem0_pin;
+ struct gpio_desc *dem1_pin;
+ struct gpio_desc *pdad_pin;
+ struct gpio_desc *pdda_pin;
+};
static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008 *pcm = component->dev->platform_data;
- gpio_set_value_cansleep(setup->pdda_pin,
- SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(pcm->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -45,11 +46,11 @@ static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008 *pcm = component->dev->platform_data;
- gpio_set_value_cansleep(setup->pdad_pin,
- SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(pcm->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -98,20 +99,25 @@ static struct snd_soc_dai_driver pcm3008_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
- .dapm_widgets = pcm3008_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
- .dapm_routes = pcm3008_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_pcm3008 = {
+ .dapm_widgets = pcm3008_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
+ .dapm_routes = pcm3008_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int pcm3008_codec_probe(struct platform_device *pdev)
{
- struct pcm3008_setup_data *setup = pdev->dev.platform_data;
- int ret;
+ struct device *dev = &pdev->dev;
+ struct pcm3008 *pcm;
- if (!setup)
- return -EINVAL;
+ pcm = devm_kzalloc(dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, pcm);
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
@@ -121,45 +127,33 @@ static int pcm3008_codec_probe(struct platform_device *pdev)
*/
/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
- ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
- GPIOF_OUT_INIT_HIGH, "codec_dem0");
- if (ret != 0)
- return ret;
+ pcm->dem0_pin = devm_gpiod_get(dev, "dem0", GPIOD_OUT_HIGH);
+ if (IS_ERR(pcm->dem0_pin))
+ return PTR_ERR(pcm->dem0_pin);
/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
- ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
- GPIOF_OUT_INIT_LOW, "codec_dem1");
- if (ret != 0)
- return ret;
+ pcm->dem1_pin = devm_gpiod_get(dev, "dem1", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->dem1_pin))
+ return PTR_ERR(pcm->dem1_pin);
/* Configure PDAD GPIO. */
- ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
- GPIOF_OUT_INIT_LOW, "codec_pdad");
- if (ret != 0)
- return ret;
+ pcm->pdad_pin = devm_gpiod_get(dev, "pdad", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->pdad_pin))
+ return PTR_ERR(pcm->pdad_pin);
/* Configure PDDA GPIO. */
- ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
- GPIOF_OUT_INIT_LOW, "codec_pdda");
- if (ret != 0)
- return ret;
-
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_pcm3008, &pcm3008_dai, 1);
-}
+ pcm->pdda_pin = devm_gpiod_get(dev, "pdda", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->pdda_pin))
+ return PTR_ERR(pcm->pdda_pin);
-static int pcm3008_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_pcm3008, &pcm3008_dai, 1);
}
MODULE_ALIAS("platform:pcm3008-codec");
static struct platform_driver pcm3008_codec_driver = {
.probe = pcm3008_codec_probe,
- .remove = pcm3008_codec_remove,
.driver = {
.name = "pcm3008-codec",
},
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
deleted file mode 100644
index 7e5489ab4812..000000000000
--- a/sound/soc/codecs/pcm3008.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * PCM3008 ALSA SoC Layer
- *
- * Author: Hugo Villeneuve
- * Copyright (C) 2008 Lyrtech inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __LINUX_SND_SOC_PCM3008_H
-#define __LINUX_SND_SOC_PCM3008_H
-
-struct pcm3008_setup_data {
- unsigned dem0_pin;
- unsigned dem1_pin;
- unsigned pdad_pin;
- unsigned pdda_pin;
-};
-
-#endif
diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c
new file mode 100644
index 000000000000..3816b25a8ead
--- /dev/null
+++ b/sound/soc/codecs/pcm3060-i2c.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 I2C driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <k.marinushkin@gmail.com>
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "pcm3060.h"
+
+static int pcm3060_i2c_probe(struct i2c_client *i2c)
+{
+ struct pcm3060_priv *priv;
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
+ priv->regmap = devm_regmap_init_i2c(i2c, &pcm3060_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ return pcm3060_probe(&i2c->dev);
+}
+
+static const struct i2c_device_id pcm3060_i2c_id[] = {
+ { .name = "pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, pcm3060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm3060_of_match[] = {
+ { .compatible = "ti,pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pcm3060_of_match);
+#endif /* CONFIG_OF */
+
+static struct i2c_driver pcm3060_i2c_driver = {
+ .driver = {
+ .name = "pcm3060",
+#ifdef CONFIG_OF
+ .of_match_table = pcm3060_of_match,
+#endif /* CONFIG_OF */
+ },
+ .id_table = pcm3060_i2c_id,
+ .probe = pcm3060_i2c_probe,
+};
+
+module_i2c_driver(pcm3060_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3060 I2C driver");
+MODULE_AUTHOR("Kirill Marinushkin <k.marinushkin@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060-spi.c b/sound/soc/codecs/pcm3060-spi.c
new file mode 100644
index 000000000000..6095841f2f56
--- /dev/null
+++ b/sound/soc/codecs/pcm3060-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 SPI driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <k.marinushkin@gmail.com>
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "pcm3060.h"
+
+static int pcm3060_spi_probe(struct spi_device *spi)
+{
+ struct pcm3060_priv *priv;
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, priv);
+
+ priv->regmap = devm_regmap_init_spi(spi, &pcm3060_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ return pcm3060_probe(&spi->dev);
+}
+
+static const struct spi_device_id pcm3060_spi_id[] = {
+ { .name = "pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm3060_spi_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm3060_of_match[] = {
+ { .compatible = "ti,pcm3060" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pcm3060_of_match);
+#endif /* CONFIG_OF */
+
+static struct spi_driver pcm3060_spi_driver = {
+ .driver = {
+ .name = "pcm3060",
+#ifdef CONFIG_OF
+ .of_match_table = pcm3060_of_match,
+#endif /* CONFIG_OF */
+ },
+ .id_table = pcm3060_spi_id,
+ .probe = pcm3060_spi_probe,
+};
+
+module_spi_driver(pcm3060_spi_driver);
+
+MODULE_DESCRIPTION("PCM3060 SPI driver");
+MODULE_AUTHOR("Kirill Marinushkin <k.marinushkin@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c
new file mode 100644
index 000000000000..8974200652e7
--- /dev/null
+++ b/sound/soc/codecs/pcm3060.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 codec driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <k.marinushkin@gmail.com>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm3060.h"
+
+/* dai */
+
+static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned int reg;
+ unsigned int val;
+
+ if (dir != SND_SOC_CLOCK_IN) {
+ dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case PCM3060_CLK_DEF:
+ val = 0;
+ break;
+
+ case PCM3060_CLK1:
+ val = (dai->id == PCM3060_DAI_ID_DAC ? PCM3060_REG_CSEL : 0);
+ break;
+
+ case PCM3060_CLK2:
+ val = (dai->id == PCM3060_DAI_ID_DAC ? 0 : PCM3060_REG_CSEL);
+ break;
+
+ default:
+ dev_err(comp->dev, "unsupported sysclock id: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ if (dai->id == PCM3060_DAI_ID_DAC)
+ reg = PCM3060_REG67;
+ else
+ reg = PCM3060_REG72;
+
+ regmap_update_bits(priv->regmap, reg, PCM3060_REG_CSEL, val);
+
+ priv->dai[dai->id].sclk_freq = freq;
+
+ return 0;
+}
+
+static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned int reg;
+ unsigned int val;
+
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ dev_err(comp->dev, "unsupported DAI polarity: 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ priv->dai[dai->id].is_provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->dai[dai->id].is_provider = false;
+ break;
+ default:
+ dev_err(comp->dev, "unsupported DAI mode: 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val = PCM3060_REG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = PCM3060_REG_FMT_RJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = PCM3060_REG_FMT_LJ;
+ break;
+ default:
+ dev_err(comp->dev, "unsupported DAI format: 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ if (dai->id == PCM3060_DAI_ID_DAC)
+ reg = PCM3060_REG67;
+ else
+ reg = PCM3060_REG72;
+
+ regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_FMT, val);
+
+ return 0;
+}
+
+static int pcm3060_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned int rate;
+ unsigned int ratio;
+ unsigned int reg;
+ unsigned int val;
+
+ if (!priv->dai[dai->id].is_provider) {
+ val = PCM3060_REG_MS_S;
+ goto val_ready;
+ }
+
+ rate = params_rate(params);
+ if (!rate) {
+ dev_err(comp->dev, "rate is not configured\n");
+ return -EINVAL;
+ }
+
+ ratio = priv->dai[dai->id].sclk_freq / rate;
+
+ switch (ratio) {
+ case 768:
+ val = PCM3060_REG_MS_M768;
+ break;
+ case 512:
+ val = PCM3060_REG_MS_M512;
+ break;
+ case 384:
+ val = PCM3060_REG_MS_M384;
+ break;
+ case 256:
+ val = PCM3060_REG_MS_M256;
+ break;
+ case 192:
+ val = PCM3060_REG_MS_M192;
+ break;
+ case 128:
+ val = PCM3060_REG_MS_M128;
+ break;
+ default:
+ dev_err(comp->dev, "unsupported ratio: %d\n", ratio);
+ return -EINVAL;
+ }
+
+val_ready:
+ if (dai->id == PCM3060_DAI_ID_DAC)
+ reg = PCM3060_REG67;
+ else
+ reg = PCM3060_REG72;
+
+ regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_MS, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm3060_dai_ops = {
+ .set_sysclk = pcm3060_set_sysclk,
+ .set_fmt = pcm3060_set_fmt,
+ .hw_params = pcm3060_hw_params,
+};
+
+#define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PCM3060_DAI_RATES_DAC (PCM3060_DAI_RATES_ADC | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver pcm3060_dai[] = {
+ {
+ .name = "pcm3060-dac",
+ .id = PCM3060_DAI_ID_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM3060_DAI_RATES_DAC,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &pcm3060_dai_ops,
+ },
+ {
+ .name = "pcm3060-adc",
+ .id = PCM3060_DAI_ID_ADC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM3060_DAI_RATES_ADC,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &pcm3060_dai_ops,
+ },
+};
+
+/* dapm */
+
+static DECLARE_TLV_DB_SCALE(pcm3060_dapm_tlv, -10050, 50, 1);
+
+static const struct snd_kcontrol_new pcm3060_dapm_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume",
+ PCM3060_REG65, PCM3060_REG66, 0,
+ PCM3060_REG_AT2_MIN, PCM3060_REG_AT2_MAX,
+ 0, pcm3060_dapm_tlv),
+ SOC_DOUBLE("Master Playback Switch", PCM3060_REG68,
+ PCM3060_REG_SHIFT_MUT21, PCM3060_REG_SHIFT_MUT22, 1, 1),
+
+ SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume",
+ PCM3060_REG70, PCM3060_REG71, 0,
+ PCM3060_REG_AT1_MIN, PCM3060_REG_AT1_MAX,
+ 0, pcm3060_dapm_tlv),
+ SOC_DOUBLE("Master Capture Switch", PCM3060_REG73,
+ PCM3060_REG_SHIFT_MUT11, PCM3060_REG_SHIFT_MUT12, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", PCM3060_REG64,
+ PCM3060_REG_SHIFT_DAPSV, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", PCM3060_REG64,
+ PCM3060_REG_SHIFT_ADPSV, 1),
+};
+
+static const struct snd_soc_dapm_route pcm3060_dapm_map[] = {
+ { "OUTL", NULL, "DAC" },
+ { "OUTR", NULL, "DAC" },
+
+ { "ADC", NULL, "INL" },
+ { "ADC", NULL, "INR" },
+};
+
+/* soc component */
+
+static const struct snd_soc_component_driver pcm3060_soc_comp_driver = {
+ .controls = pcm3060_dapm_controls,
+ .num_controls = ARRAY_SIZE(pcm3060_dapm_controls),
+ .dapm_widgets = pcm3060_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets),
+ .dapm_routes = pcm3060_dapm_map,
+ .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map),
+ .endianness = 1,
+};
+
+/* regmap */
+
+static bool pcm3060_reg_writeable(struct device *dev, unsigned int reg)
+{
+ return (reg >= PCM3060_REG64);
+}
+
+static bool pcm3060_reg_readable(struct device *dev, unsigned int reg)
+{
+ return (reg >= PCM3060_REG64);
+}
+
+static bool pcm3060_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* PCM3060_REG64 is volatile */
+ return (reg == PCM3060_REG64);
+}
+
+static const struct reg_default pcm3060_reg_defaults[] = {
+ { PCM3060_REG64, 0xF0 },
+ { PCM3060_REG65, 0xFF },
+ { PCM3060_REG66, 0xFF },
+ { PCM3060_REG67, 0x00 },
+ { PCM3060_REG68, 0x00 },
+ { PCM3060_REG69, 0x00 },
+ { PCM3060_REG70, 0xD7 },
+ { PCM3060_REG71, 0xD7 },
+ { PCM3060_REG72, 0x00 },
+ { PCM3060_REG73, 0x00 },
+};
+
+const struct regmap_config pcm3060_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = pcm3060_reg_writeable,
+ .readable_reg = pcm3060_reg_readable,
+ .volatile_reg = pcm3060_reg_volatile,
+ .max_register = PCM3060_REG73,
+ .reg_defaults = pcm3060_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm3060_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL(pcm3060_regmap);
+
+/* device */
+
+static void pcm3060_parse_dt(const struct device_node *np,
+ struct pcm3060_priv *priv)
+{
+ priv->out_se = of_property_read_bool(np, "ti,out-single-ended");
+}
+
+int pcm3060_probe(struct device *dev)
+{
+ int rc;
+ struct pcm3060_priv *priv = dev_get_drvdata(dev);
+
+ /* soft reset */
+ rc = regmap_update_bits(priv->regmap, PCM3060_REG64,
+ PCM3060_REG_MRST, 0);
+ if (rc) {
+ dev_err(dev, "failed to reset component, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (dev->of_node)
+ pcm3060_parse_dt(dev->of_node, priv);
+
+ if (priv->out_se)
+ regmap_update_bits(priv->regmap, PCM3060_REG64,
+ PCM3060_REG_SE, PCM3060_REG_SE);
+
+ rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver,
+ pcm3060_dai,
+ ARRAY_SIZE(pcm3060_dai));
+ if (rc) {
+ dev_err(dev, "failed to register component, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcm3060_probe);
+
+MODULE_DESCRIPTION("PCM3060 codec driver");
+MODULE_AUTHOR("Kirill Marinushkin <k.marinushkin@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h
new file mode 100644
index 000000000000..1b96835600b4
--- /dev/null
+++ b/sound/soc/codecs/pcm3060.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCM3060 codec driver
+ *
+ * Copyright (C) 2018 Kirill Marinushkin <k.marinushkin@gmail.com>
+ */
+
+#ifndef _SND_SOC_PCM3060_H
+#define _SND_SOC_PCM3060_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+extern const struct regmap_config pcm3060_regmap;
+
+#define PCM3060_DAI_ID_DAC 0
+#define PCM3060_DAI_ID_ADC 1
+#define PCM3060_DAI_IDS_NUM 2
+
+/* ADC and DAC can be clocked from separate or same sources CLK1 and CLK2 */
+#define PCM3060_CLK_DEF 0 /* default: CLK1->ADC, CLK2->DAC */
+#define PCM3060_CLK1 1
+#define PCM3060_CLK2 2
+
+struct pcm3060_priv_dai {
+ bool is_provider;
+ unsigned int sclk_freq;
+};
+
+struct pcm3060_priv {
+ struct regmap *regmap;
+ struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM];
+ u8 out_se: 1;
+};
+
+int pcm3060_probe(struct device *dev);
+int pcm3060_remove(struct device *dev);
+
+/* registers */
+
+#define PCM3060_REG64 0x40
+#define PCM3060_REG_MRST 0x80
+#define PCM3060_REG_SRST 0x40
+#define PCM3060_REG_ADPSV 0x20
+#define PCM3060_REG_SHIFT_ADPSV 0x05
+#define PCM3060_REG_DAPSV 0x10
+#define PCM3060_REG_SHIFT_DAPSV 0x04
+#define PCM3060_REG_SE 0x01
+
+#define PCM3060_REG65 0x41
+#define PCM3060_REG66 0x42
+#define PCM3060_REG_AT2_MIN 0x36
+#define PCM3060_REG_AT2_MAX 0xFF
+
+#define PCM3060_REG67 0x43
+#define PCM3060_REG72 0x48
+#define PCM3060_REG_CSEL 0x80
+#define PCM3060_REG_MASK_MS 0x70
+#define PCM3060_REG_MS_S 0x00
+#define PCM3060_REG_MS_M768 (0x01 << 4)
+#define PCM3060_REG_MS_M512 (0x02 << 4)
+#define PCM3060_REG_MS_M384 (0x03 << 4)
+#define PCM3060_REG_MS_M256 (0x04 << 4)
+#define PCM3060_REG_MS_M192 (0x05 << 4)
+#define PCM3060_REG_MS_M128 (0x06 << 4)
+#define PCM3060_REG_MASK_FMT 0x03
+#define PCM3060_REG_FMT_I2S 0x00
+#define PCM3060_REG_FMT_LJ 0x01
+#define PCM3060_REG_FMT_RJ 0x02
+
+#define PCM3060_REG68 0x44
+#define PCM3060_REG_OVER 0x40
+#define PCM3060_REG_DREV2 0x04
+#define PCM3060_REG_SHIFT_MUT21 0x00
+#define PCM3060_REG_SHIFT_MUT22 0x01
+
+#define PCM3060_REG69 0x45
+#define PCM3060_REG_FLT 0x80
+#define PCM3060_REG_MASK_DMF 0x60
+#define PCM3060_REG_DMC 0x10
+#define PCM3060_REG_ZREV 0x02
+#define PCM3060_REG_AZRO 0x01
+
+#define PCM3060_REG70 0x46
+#define PCM3060_REG71 0x47
+#define PCM3060_REG_AT1_MIN 0x0E
+#define PCM3060_REG_AT1_MAX 0xFF
+
+#define PCM3060_REG73 0x49
+#define PCM3060_REG_ZCDD 0x10
+#define PCM3060_REG_BYP 0x08
+#define PCM3060_REG_DREV1 0x04
+#define PCM3060_REG_SHIFT_MUT11 0x00
+#define PCM3060_REG_SHIFT_MUT12 0x01
+
+#endif /* _SND_SOC_PCM3060_H */
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
index 6feb0901dfeb..ff18c74b616c 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -1,25 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM3168A codec i2c driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <sound/soc.h>
#include "pcm3168a.h"
-static int pcm3168a_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm3168a_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -30,11 +27,9 @@ static int pcm3168a_i2c_probe(struct i2c_client *i2c,
return pcm3168a_probe(&i2c->dev, regmap);
}
-static int pcm3168a_i2c_remove(struct i2c_client *i2c)
+static void pcm3168a_i2c_remove(struct i2c_client *i2c)
{
pcm3168a_remove(&i2c->dev);
-
- return 0;
}
static const struct i2c_device_id pcm3168a_i2c_id[] = {
@@ -43,6 +38,13 @@ static const struct i2c_device_id pcm3168a_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+static const struct acpi_device_id pcm3168a_acpi_match[] = {
+ { "PCM3168A" },
+ { "104C3168" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match);
+
static const struct of_device_id pcm3168a_of_match[] = {
{ .compatible = "ti,pcm3168a", },
{ }
@@ -55,8 +57,9 @@ static struct i2c_driver pcm3168a_i2c_driver = {
.id_table = pcm3168a_i2c_id,
.driver = {
.name = "pcm3168a",
+ .acpi_match_table = pcm3168a_acpi_match,
.of_match_table = pcm3168a_of_match,
- .pm = &pcm3168a_pm_ops,
+ .pm = pm_ptr(&pcm3168a_pm_ops),
},
};
module_i2c_driver(pcm3168a_i2c_driver);
diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c
index 03945a27ae40..0871338eacba 100644
--- a/sound/soc/codecs/pcm3168a-spi.c
+++ b/sound/soc/codecs/pcm3168a-spi.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM3168A codec spi driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/init.h>
@@ -29,11 +26,9 @@ static int pcm3168a_spi_probe(struct spi_device *spi)
return pcm3168a_probe(&spi->dev, regmap);
}
-static int pcm3168a_spi_remove(struct spi_device *spi)
+static void pcm3168a_spi_remove(struct spi_device *spi)
{
pcm3168a_remove(&spi->dev);
-
- return 0;
}
static const struct spi_device_id pcm3168a_spi_id[] = {
@@ -55,7 +50,7 @@ static struct spi_driver pcm3168a_spi_driver = {
.driver = {
.name = "pcm3168a",
.of_match_table = pcm3168a_of_match,
- .pm = &pcm3168a_pm_ops,
+ .pm = pm_ptr(&pcm3168a_pm_ops),
},
};
module_spi_driver(pcm3168a_spi_driver);
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 44b268aa4dd8..c8617a488b11 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM3168A codec driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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/gpio/consumer.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -24,8 +22,7 @@
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S24_LE)
#define PCM3168A_FMT_I2S 0x0
#define PCM3168A_FMT_LEFT_J 0x1
@@ -33,10 +30,10 @@
#define PCM3168A_FMT_RIGHT_J_16 0x3
#define PCM3168A_FMT_DSP_A 0x4
#define PCM3168A_FMT_DSP_B 0x5
-#define PCM3168A_FMT_DSP_MASK 0x4
+#define PCM3168A_FMT_I2S_TDM 0x6
+#define PCM3168A_FMT_LEFT_J_TDM 0x7
-#define PCM3168A_NUM_SUPPLIES 6
-static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+static const char *const pcm3168a_supply_names[] = {
"VDD1",
"VDD2",
"VCCAD1",
@@ -45,15 +42,27 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
"VCCDA2"
};
+#define PCM3168A_DAI_DAC 0
+#define PCM3168A_DAI_ADC 1
+
+/* ADC/DAC side parameters */
+struct pcm3168a_io_params {
+ bool provider_mode;
+ unsigned int format;
+ int tdm_slots;
+ u32 tdm_mask;
+ int slot_width;
+};
+
struct pcm3168a_priv {
- struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
+ struct regulator_bulk_data supplies[ARRAY_SIZE(pcm3168a_supply_names)];
struct regmap *regmap;
struct clk *scki;
- bool adc_master_mode;
- bool dac_master_mode;
+ struct gpio_desc *gpio_rst;
unsigned long sysclk;
- unsigned int adc_fmt;
- unsigned int dac_fmt;
+
+ struct pcm3168a_io_params io_params[2];
+ struct snd_soc_dai_driver dai_drv[2];
};
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
@@ -131,10 +140,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
- SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
- SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
- SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
- SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
@@ -174,9 +179,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
- SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
- SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
- SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
@@ -268,7 +270,7 @@ static unsigned int pcm3168a_scki_ratios[] = {
#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
-#define PCM1368A_MAX_SYSCLK 36864000
+#define PCM3168A_MAX_SYSCLK 36864000
static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
{
@@ -285,10 +287,10 @@ static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
}
-static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm3168a_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
@@ -298,54 +300,85 @@ static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(dai->component);
+ int ret;
- if (freq > PCM1368A_MAX_SYSCLK)
+ /*
+ * Some sound card sets 0 Hz as reset,
+ * but it is impossible to set. Ignore it here
+ */
+ if (freq == 0)
+ return 0;
+
+ if (freq > PCM3168A_MAX_SYSCLK)
return -EINVAL;
+ ret = clk_set_rate(pcm3168a->scki, freq);
+ if (ret)
+ return ret;
+
pcm3168a->sysclk = freq;
return 0;
}
-static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
- unsigned int format, bool dac)
+static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE;
+ unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6;
+
+ if (io_params->format == SND_SOC_DAIFMT_RIGHT_J) {
+ /* S16_LE is only supported in RIGHT_J mode */
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+
+ /*
+ * If multi DIN/DOUT is not selected, RIGHT_J can only support
+ * two channels (no TDM support)
+ */
+ if (io_params->tdm_slots != 2)
+ channel_max = 2;
+ }
+
+ if (dai->id == PCM3168A_DAI_DAC) {
+ dai->driver->playback.channels_max = channel_max;
+ dai->driver->playback.formats = formats;
+ } else {
+ dai->driver->capture.channels_max = channel_max;
+ dai->driver->capture.formats = formats;
+ }
+}
+
+static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
- u32 fmt, reg, mask, shift;
- bool master_mode;
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ bool provider_mode;
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
- fmt = PCM3168A_FMT_LEFT_J;
- break;
case SND_SOC_DAIFMT_I2S:
- fmt = PCM3168A_FMT_I2S;
- break;
case SND_SOC_DAIFMT_RIGHT_J:
- fmt = PCM3168A_FMT_RIGHT_J;
- break;
case SND_SOC_DAIFMT_DSP_A:
- fmt = PCM3168A_FMT_DSP_A;
- break;
case SND_SOC_DAIFMT_DSP_B:
- fmt = PCM3168A_FMT_DSP_B;
break;
default:
- dev_err(codec->dev, "unsupported dai format\n");
+ dev_err(component->dev, "unsupported dai format\n");
return -EINVAL;
}
- switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- master_mode = false;
+ switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ provider_mode = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- master_mode = true;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ provider_mode = true;
break;
default:
- dev_err(codec->dev, "unsupported master/slave mode\n");
+ dev_err(component->dev, "unsupported provider mode\n");
return -EINVAL;
}
@@ -356,140 +389,218 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- if (dac) {
- reg = PCM3168A_DAC_PWR_MST_FMT;
- mask = PCM3168A_DAC_FMT_MASK;
- shift = PCM3168A_DAC_FMT_SHIFT;
- pcm3168a->dac_master_mode = master_mode;
- pcm3168a->dac_fmt = fmt;
- } else {
- reg = PCM3168A_ADC_MST_FMT;
- mask = PCM3168A_ADC_FMTAD_MASK;
- shift = PCM3168A_ADC_FMTAD_SHIFT;
- pcm3168a->adc_master_mode = master_mode;
- pcm3168a->adc_fmt = fmt;
- }
+ io_params->provider_mode = provider_mode;
+ io_params->format = format & SND_SOC_DAIFMT_FORMAT_MASK;
- regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+ pcm3168a_update_fixup_pcm_stream(dai);
return 0;
}
-static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
- unsigned int format)
+static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
{
- return pcm3168a_set_dai_fmt(dai, format, true);
-}
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+
+ if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(component->dev,
+ "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+ tx_mask, rx_mask, slots);
+ return -EINVAL;
+ }
-static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
- unsigned int format)
-{
- return pcm3168a_set_dai_fmt(dai, format, false);
+ if (slot_width &&
+ (slot_width != 16 && slot_width != 24 && slot_width != 32 )) {
+ dev_err(component->dev, "Unsupported slot_width %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ io_params->tdm_slots = slots;
+ io_params->slot_width = slot_width;
+ /* Ignore the not relevant mask for the DAI/direction */
+ if (dai->id == PCM3168A_DAI_DAC)
+ io_params->tdm_mask = tx_mask;
+ else
+ io_params->tdm_mask = rx_mask;
+
+ pcm3168a_update_fixup_pcm_stream(dai);
+
+ return 0;
}
static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
- bool tx, master_mode;
- u32 val, mask, shift, reg;
- unsigned int rate, channels, fmt, ratio, max_ratio;
- int i, min_frame_size;
- snd_pcm_format_t format;
-
- rate = params_rate(params);
- format = params_format(params);
- channels = params_channels(params);
-
- ratio = pcm3168a->sysclk / rate;
-
- tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- if (tx) {
- max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ bool provider_mode, tdm_mode;
+ unsigned int format;
+ unsigned int reg, mask, ms, ms_shift, fmt, fmt_shift, ratio, tdm_slots;
+ int i, num_scki_ratios, slot_width;
+
+ if (dai->id == PCM3168A_DAI_DAC) {
+ num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_DAC;
reg = PCM3168A_DAC_PWR_MST_FMT;
- mask = PCM3168A_DAC_MSDA_MASK;
- shift = PCM3168A_DAC_MSDA_SHIFT;
- master_mode = pcm3168a->dac_master_mode;
- fmt = pcm3168a->dac_fmt;
+ mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK;
+ ms_shift = PCM3168A_DAC_MSDA_SHIFT;
+ fmt_shift = PCM3168A_DAC_FMT_SHIFT;
} else {
- max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
+ num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_ADC;
reg = PCM3168A_ADC_MST_FMT;
- mask = PCM3168A_ADC_MSAD_MASK;
- shift = PCM3168A_ADC_MSAD_SHIFT;
- master_mode = pcm3168a->adc_master_mode;
- fmt = pcm3168a->adc_fmt;
+ mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK;
+ ms_shift = PCM3168A_ADC_MSAD_SHIFT;
+ fmt_shift = PCM3168A_ADC_FMTAD_SHIFT;
}
- for (i = 0; i < max_ratio; i++) {
- if (pcm3168a_scki_ratios[i] == ratio)
- break;
- }
+ provider_mode = io_params->provider_mode;
- if (i == max_ratio) {
- dev_err(codec->dev, "unsupported sysclk ratio\n");
- return -EINVAL;
+ if (provider_mode) {
+ ratio = pcm3168a->sysclk / params_rate(params);
+
+ for (i = 0; i < num_scki_ratios; i++) {
+ if (pcm3168a_scki_ratios[i] == ratio)
+ break;
+ }
+
+ if (i == num_scki_ratios) {
+ dev_err(component->dev, "unsupported sysclk ratio\n");
+ return -EINVAL;
+ }
+
+ ms = (i + 1);
+ } else {
+ ms = 0;
}
- min_frame_size = params_width(params) * 2;
- switch (min_frame_size) {
- case 32:
- if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
- dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
+ format = io_params->format;
+
+ if (io_params->slot_width)
+ slot_width = io_params->slot_width;
+ else
+ slot_width = params_width(params);
+
+ switch (slot_width) {
+ case 16:
+ if (provider_mode || (format != SND_SOC_DAIFMT_RIGHT_J)) {
+ dev_err(component->dev, "16-bit slots are supported only for consumer mode using right justified\n");
return -EINVAL;
}
- fmt = PCM3168A_FMT_RIGHT_J_16;
break;
- case 48:
- if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
- dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
+ case 24:
+ if (!provider_mode && ((format == SND_SOC_DAIFMT_DSP_A) ||
+ (format == SND_SOC_DAIFMT_DSP_B))) {
+ dev_err(component->dev, "24-bit slots not supported in consumer mode using DSP\n");
return -EINVAL;
}
break;
- case 64:
+ case 32:
break;
default:
- dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+ dev_err(component->dev, "unsupported frame size: %d\n", slot_width);
return -EINVAL;
}
- if (master_mode)
- val = ((i + 1) << shift);
+ if (io_params->tdm_slots)
+ tdm_slots = io_params->tdm_slots;
else
- val = 0;
-
- regmap_update_bits(pcm3168a->regmap, reg, mask, val);
+ tdm_slots = params_channels(params);
+
+ /*
+ * Switch the codec to TDM mode when more than 2 TDM slots are needed
+ * for the stream.
+ * If pcm3168a->tdm_slots is not set or set to more than 2 (8/6 usually)
+ * then DIN1/DOUT1 is used in TDM mode.
+ * If pcm3168a->tdm_slots is set to 2 then DIN1/2/3/4 and DOUT1/2/3 is
+ * used in normal mode, no need to switch to TDM modes.
+ */
+ tdm_mode = (tdm_slots > 2);
+
+ if (tdm_mode) {
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+ default:
+ dev_err(component->dev,
+ "TDM is supported under DSP/I2S/Left_J only\n");
+ return -EINVAL;
+ }
+ }
- if (tx) {
- mask = PCM3168A_DAC_FMT_MASK;
- shift = PCM3168A_DAC_FMT_SHIFT;
- } else {
- mask = PCM3168A_ADC_FMTAD_MASK;
- shift = PCM3168A_ADC_FMTAD_SHIFT;
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ fmt = (slot_width == 16) ? PCM3168A_FMT_RIGHT_J_16 :
+ PCM3168A_FMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_DSP_B;
+ break;
+ default:
+ return -EINVAL;
}
- regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+ regmap_update_bits(pcm3168a->regmap, reg, mask,
+ (ms << ms_shift) | (fmt << fmt_shift));
return 0;
}
-static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
- .set_fmt = pcm3168a_set_dai_fmt_dac,
- .set_sysclk = pcm3168a_set_dai_sysclk,
- .hw_params = pcm3168a_hw_params,
- .digital_mute = pcm3168a_digital_mute
+static const u64 pcm3168a_dai_formats[] = {
+ /*
+ * Select below from Sound Card, not here
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+
+ /*
+ * First Priority
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J,
+ /*
+ * Second Priority
+ *
+ * These have picky limitation.
+ * see
+ * pcm3168a_hw_params()
+ */
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
-static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
- .set_fmt = pcm3168a_set_dai_fmt_adc,
+static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt,
.set_sysclk = pcm3168a_set_dai_sysclk,
- .hw_params = pcm3168a_hw_params
+ .hw_params = pcm3168a_hw_params,
+ .mute_stream = pcm3168a_mute,
+ .set_tdm_slot = pcm3168a_set_tdm_slot,
+ .no_capture_mute = 1,
+ .auto_selectable_formats = pcm3168a_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(pcm3168a_dai_formats),
};
static struct snd_soc_dai_driver pcm3168a_dais[] = {
{
.name = "pcm3168a-dac",
+ .id = PCM3168A_DAI_DAC,
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -497,10 +608,11 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = PCM3168A_FORMATS
},
- .ops = &pcm3168a_dac_dai_ops
+ .ops = &pcm3168a_dai_ops
},
{
.name = "pcm3168a-adc",
+ .id = PCM3168A_DAI_ADC,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -508,7 +620,7 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = PCM3168A_FORMATS
},
- .ops = &pcm3168a_adc_dai_ops
+ .ops = &pcm3168a_dai_ops
},
};
@@ -557,6 +669,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case PCM3168A_RST_SMODE:
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
@@ -593,14 +706,15 @@ const struct regmap_config pcm3168a_regmap = {
};
EXPORT_SYMBOL_GPL(pcm3168a_regmap);
-static const struct snd_soc_codec_driver pcm3168a_driver = {
- .idle_bias_off = true,
- .controls = pcm3168a_snd_controls,
- .num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
- .dapm_widgets = pcm3168a_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
- .dapm_routes = pcm3168a_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
+static const struct snd_soc_component_driver pcm3168a_driver = {
+ .controls = pcm3168a_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
+ .dapm_widgets = pcm3168a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
+ .dapm_routes = pcm3168a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
int pcm3168a_probe(struct device *dev, struct regmap *regmap)
@@ -614,13 +728,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
- pcm3168a->scki = devm_clk_get(dev, "scki");
- if (IS_ERR(pcm3168a->scki)) {
- ret = PTR_ERR(pcm3168a->scki);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
- return ret;
- }
+ /*
+ * Request the reset (connected to RST pin) gpio line as non exclusive
+ * as the same reset line might be connected to multiple pcm3168a codec
+ *
+ * The RST is low active, we want the GPIO line to be high initially, so
+ * request the initial level to LOW which in practice means DEASSERTED:
+ * The deasserted level of GPIO_ACTIVE_LOW is HIGH.
+ */
+ pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(pcm3168a->gpio_rst))
+ return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst),
+ "failed to acquire RST gpio\n");
+
+ pcm3168a->scki = devm_clk_get_optional(dev, "scki");
+ if (IS_ERR(pcm3168a->scki))
+ return dev_err_probe(dev, PTR_ERR(pcm3168a->scki),
+ "failed to acquire clock 'scki'\n");
ret = clk_prepare_enable(pcm3168a->scki);
if (ret) {
@@ -629,6 +755,9 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
}
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+ /* Fallback to the default if no clk entry available. */
+ if (!pcm3168a->sysclk)
+ pcm3168a->sysclk = 24576000;
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
@@ -636,8 +765,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
ret = devm_regulator_bulk_get(dev,
ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to request supplies: %d\n", ret);
+ dev_err_probe(dev, ret, "failed to request supplies\n");
goto err_clk;
}
@@ -655,20 +783,30 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
- ret = pcm3168a_reset(pcm3168a);
- if (ret) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
- goto err_regulator;
+ if (pcm3168a->gpio_rst) {
+ /*
+ * The device is taken out from reset via GPIO line, wait for
+ * 3846 SCKI clock cycles for the internal reset de-assertion
+ */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+ } else {
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
- ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
- ARRAY_SIZE(pcm3168a_dais));
+ memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
+ ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
+ pcm3168a->dai_drv,
+ ARRAY_SIZE(pcm3168a->dai_drv));
if (ret) {
- dev_err(dev, "failed to register codec: %d\n", ret);
+ dev_err(dev, "failed to register component: %d\n", ret);
goto err_regulator;
}
@@ -684,19 +822,33 @@ err_clk:
}
EXPORT_SYMBOL_GPL(pcm3168a_probe);
-void pcm3168a_remove(struct device *dev)
+static void pcm3168a_disable(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
- pm_runtime_disable(dev);
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
- pcm3168a->supplies);
+ pcm3168a->supplies);
clk_disable_unprepare(pcm3168a->scki);
}
+
+void pcm3168a_remove(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ /*
+ * The RST is low active, we want the GPIO line to be low when the
+ * driver is removed, so set level to 1 which in practice means
+ * ASSERTED:
+ * The asserted level of GPIO_ACTIVE_LOW is LOW.
+ */
+ gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
+ pm_runtime_disable(dev);
+#ifndef CONFIG_PM
+ pcm3168a_disable(dev);
+#endif
+}
EXPORT_SYMBOL_GPL(pcm3168a_remove);
-#ifdef CONFIG_PM
static int pcm3168a_rt_resume(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
@@ -748,19 +900,14 @@ static int pcm3168a_rt_suspend(struct device *dev)
regcache_cache_only(pcm3168a->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
- pcm3168a->supplies);
-
- clk_disable_unprepare(pcm3168a->scki);
+ pcm3168a_disable(dev);
return 0;
}
-#endif
-const struct dev_pm_ops pcm3168a_pm_ops = {
- SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(pcm3168a_pm_ops) = {
+ RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
};
-EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
MODULE_DESCRIPTION("PCM3168A codec driver");
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
index 56c8332d82fb..c4b7140dce39 100644
--- a/sound/soc/codecs/pcm3168a.h
+++ b/sound/soc/codecs/pcm3168a.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* PCM3168A codec driver header
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#ifndef __PCM3168A_H__
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
new file mode 100644
index 000000000000..9bca53de2475
--- /dev/null
+++ b/sound/soc/codecs/pcm5102a.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the PCM5102A codec
+ *
+ * Author: Florian Meier <florian.meier@koalo.de>
+ * Copyright 2013
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver pcm5102a_dai = {
+ .name = "pcm5102a-hifi",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm5102a = {
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pcm5102a_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm5102a,
+ &pcm5102a_dai, 1);
+}
+
+static const struct of_device_id pcm5102a_of_match[] = {
+ { .compatible = "ti,pcm5102a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm5102a_of_match);
+
+static struct platform_driver pcm5102a_codec_driver = {
+ .probe = pcm5102a_probe,
+ .driver = {
+ .name = "pcm5102a-codec",
+ .of_match_table = pcm5102a_of_match,
+ },
+};
+
+module_platform_driver(pcm5102a_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM5102A codec driver");
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
index dbff416e38be..a1d849b0c50f 100644
--- a/sound/soc/codecs/pcm512x-i2c.c
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -1,27 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include "pcm512x.h"
-static int pcm512x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm512x_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config = pcm512x_regmap;
@@ -37,10 +29,9 @@ static int pcm512x_i2c_probe(struct i2c_client *i2c,
return pcm512x_probe(&i2c->dev, regmap);
}
-static int pcm512x_i2c_remove(struct i2c_client *i2c)
+static void pcm512x_i2c_remove(struct i2c_client *i2c)
{
pcm512x_remove(&i2c->dev);
- return 0;
}
static const struct i2c_device_id pcm512x_i2c_id[] = {
@@ -48,32 +39,52 @@ static const struct i2c_device_id pcm512x_i2c_id[] = {
{ "pcm5122", },
{ "pcm5141", },
{ "pcm5142", },
+ { "pcm5242", },
+ { "tas5754", },
+ { "tas5756", },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+#if defined(CONFIG_OF)
static const struct of_device_id pcm512x_of_match[] = {
{ .compatible = "ti,pcm5121", },
{ .compatible = "ti,pcm5122", },
{ .compatible = "ti,pcm5141", },
{ .compatible = "ti,pcm5142", },
+ { .compatible = "ti,pcm5242", },
+ { .compatible = "ti,tas5754", },
+ { .compatible = "ti,tas5756", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id pcm512x_acpi_match[] = {
+ { "104C5121", 0 },
+ { "104C5122", 0 },
+ { "104C5141", 0 },
+ { "104C5142", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match);
+#endif
static struct i2c_driver pcm512x_i2c_driver = {
- .probe = pcm512x_i2c_probe,
+ .probe = pcm512x_i2c_probe,
.remove = pcm512x_i2c_remove,
.id_table = pcm512x_i2c_id,
.driver = {
.name = "pcm512x",
- .of_match_table = pcm512x_of_match,
- .pm = &pcm512x_pm_ops,
+ .of_match_table = of_match_ptr(pcm512x_of_match),
+ .acpi_match_table = ACPI_PTR(pcm512x_acpi_match),
+ .pm = pm_ptr(&pcm512x_pm_ops),
},
};
module_i2c_driver(pcm512x_i2c_driver);
MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C");
-MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
index 712ed6598c48..92f7f78a4e20 100644
--- a/sound/soc/codecs/pcm512x-spi.c
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/init.h>
@@ -34,10 +26,9 @@ static int pcm512x_spi_probe(struct spi_device *spi)
return pcm512x_probe(&spi->dev, regmap);
}
-static int pcm512x_spi_remove(struct spi_device *spi)
+static void pcm512x_spi_remove(struct spi_device *spi)
{
pcm512x_remove(&spi->dev);
- return 0;
}
static const struct spi_device_id pcm512x_spi_id[] = {
@@ -45,6 +36,7 @@ static const struct spi_device_id pcm512x_spi_id[] = {
{ "pcm5122", },
{ "pcm5141", },
{ "pcm5142", },
+ { "pcm5242", },
{ },
};
MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
@@ -54,6 +46,7 @@ static const struct of_device_id pcm512x_of_match[] = {
{ .compatible = "ti,pcm5122", },
{ .compatible = "ti,pcm5141", },
{ .compatible = "ti,pcm5142", },
+ { .compatible = "ti,pcm5242", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm512x_of_match);
@@ -65,8 +58,12 @@ static struct spi_driver pcm512x_spi_driver = {
.driver = {
.name = "pcm512x",
.of_match_table = pcm512x_of_match,
- .pm = &pcm512x_pm_ops,
+ .pm = pm_ptr(&pcm512x_pm_ops),
},
};
module_spi_driver(pcm512x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index 047c48953a20..a70e8ea166dc 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
@@ -30,9 +22,6 @@
#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",
@@ -56,6 +45,10 @@ struct pcm512x_priv {
unsigned long overclock_pll;
unsigned long overclock_dac;
unsigned long overclock_dsp;
+ int mute;
+ struct mutex mutex;
+ unsigned int bclk_ratio;
+ int force_pll_on;
};
/*
@@ -124,6 +117,8 @@ static const struct reg_default pcm512x_reg_defaults[] = {
{ PCM512x_FS_SPEED_MODE, 0x00 },
{ PCM512x_IDAC_1, 0x01 },
{ PCM512x_IDAC_2, 0x00 },
+ { PCM512x_I2S_1, 0x02 },
+ { PCM512x_I2S_2, 0x00 },
};
static bool pcm512x_readable(struct device *dev, unsigned int reg)
@@ -229,8 +224,8 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg)
static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = pcm512x->overclock_pll;
return 0;
@@ -239,10 +234,11 @@ static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -257,8 +253,8 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = pcm512x->overclock_dsp;
return 0;
@@ -267,10 +263,11 @@ static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -285,8 +282,8 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = pcm512x->overclock_dac;
return 0;
@@ -295,10 +292,11 @@ static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
break;
@@ -387,6 +385,61 @@ static const struct soc_enum pcm512x_veds =
SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
pcm512x_ramp_step_text);
+static int pcm512x_update_mute(struct pcm512x_priv *pcm512x)
+{
+ return regmap_update_bits(
+ pcm512x->regmap, PCM512x_MUTE, PCM512x_RQML | PCM512x_RQMR,
+ (!!(pcm512x->mute & 0x5) << PCM512x_RQML_SHIFT)
+ | (!!(pcm512x->mute & 0x3) << PCM512x_RQMR_SHIFT));
+}
+
+static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&pcm512x->mutex);
+ ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4);
+ ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2);
+ mutex_unlock(&pcm512x->mutex);
+
+ return 0;
+}
+
+static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int ret, changed = 0;
+
+ mutex_lock(&pcm512x->mutex);
+
+ if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) {
+ pcm512x->mute ^= 0x4;
+ changed = 1;
+ }
+ if ((pcm512x->mute & 0x2) == (ucontrol->value.integer.value[1] << 1)) {
+ pcm512x->mute ^= 0x2;
+ changed = 1;
+ }
+
+ if (changed) {
+ ret = pcm512x_update_mute(pcm512x);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to update digital mute: %d\n", ret);
+ mutex_unlock(&pcm512x->mutex);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&pcm512x->mutex);
+
+ return changed;
+}
+
static const struct snd_kcontrol_new pcm512x_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
@@ -394,8 +447,15 @@ 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),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Playback Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_stereo_info,
+ .get = pcm512x_digital_playback_switch_get,
+ .put = pcm512x_digital_playback_switch_put
+},
SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
SOC_ENUM("DSP Program", pcm512x_dsp_program),
@@ -525,8 +585,8 @@ static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
struct device *dev = dai->dev;
struct snd_pcm_hw_constraint_ratnums *constraints_no_pll;
struct snd_ratnum *rats_no_pll;
@@ -567,8 +627,8 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
struct device *dev = dai->dev;
struct regmap *regmap = pcm512x->regmap;
@@ -593,15 +653,15 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ switch (pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ case SND_SOC_DAIFMT_CBP_CFC:
return pcm512x_dai_startup_master(substream, dai);
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
return pcm512x_dai_startup_slave(substream, dai);
default:
@@ -609,10 +669,10 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
}
}
-static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
+static int pcm512x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(component->dev);
int ret;
switch (level) {
@@ -624,7 +684,7 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
PCM512x_RQST, 0);
if (ret != 0) {
- dev_err(codec->dev, "Failed to remove standby: %d\n",
+ dev_err(component->dev, "Failed to remove standby: %d\n",
ret);
return ret;
}
@@ -634,7 +694,7 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
PCM512x_RQST, PCM512x_RQST);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request standby: %d\n",
+ dev_err(component->dev, "Failed to request standby: %d\n",
ret);
return ret;
}
@@ -648,8 +708,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
unsigned long sck_rate;
int pow2;
@@ -694,8 +754,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai,
unsigned long pll_rate)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
unsigned long common;
int R, J, D, P;
unsigned long K; /* 10000 * J.D */
@@ -801,8 +861,8 @@ static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
unsigned long osr_rate,
unsigned long pllin_rate)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
unsigned long dac_rate;
if (!pcm512x->pll_out)
@@ -832,8 +892,8 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
struct snd_pcm_hw_params *params)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
unsigned long pllin_rate = 0;
unsigned long pll_rate;
unsigned long sck_rate;
@@ -854,16 +914,21 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
int fssp;
int gpio;
- lrclk_div = snd_soc_params_to_frame_size(params);
- if (lrclk_div == 0) {
- dev_err(dev, "No LRCLK?\n");
- return -EINVAL;
+ if (pcm512x->bclk_ratio > 0) {
+ lrclk_div = pcm512x->bclk_ratio;
+ } else {
+ lrclk_div = snd_soc_params_to_frame_size(params);
+
+ if (lrclk_div == 0) {
+ dev_err(dev, "No LRCLK?\n");
+ return -EINVAL;
+ }
}
if (!pcm512x->pll_out) {
sck_rate = clk_get_rate(pcm512x->sclk);
- bclk_div = params->rate_den * 64 / lrclk_div;
- bclk_rate = DIV_ROUND_CLOSEST(sck_rate, bclk_div);
+ bclk_rate = params_rate(params) * lrclk_div;
+ bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate);
mck_rate = sck_rate;
} else {
@@ -952,7 +1017,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
PCM512x_SDAC, PCM512x_SDAC_GPIO);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio as dacref: %d\n", ret);
return ret;
}
@@ -961,7 +1026,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN,
PCM512x_GREF, gpio);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio %d as dacin: %d\n",
pcm512x->pll_in, ret);
return ret;
@@ -990,7 +1055,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
PCM512x_SDAC, PCM512x_SDAC_SCK);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set sck as dacref: %d\n", ret);
return ret;
}
@@ -1085,18 +1150,18 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE,
PCM512x_FSSP, fssp);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set fs speed: %d\n", ret);
+ dev_err(component->dev, "Failed to set fs speed: %d\n", ret);
return ret;
}
- dev_dbg(codec->dev, "DSP divider %d\n", dsp_div);
- dev_dbg(codec->dev, "DAC divider %d\n", dac_div);
- dev_dbg(codec->dev, "NCP divider %d\n", ncp_div);
- dev_dbg(codec->dev, "OSR divider %d\n", osr_div);
- dev_dbg(codec->dev, "BCK divider %d\n", bclk_div);
- dev_dbg(codec->dev, "LRCK divider %d\n", lrclk_div);
- dev_dbg(codec->dev, "IDAC %d\n", idac);
- dev_dbg(codec->dev, "1<<FSSP %d\n", 1 << fssp);
+ dev_dbg(component->dev, "DSP divider %d\n", dsp_div);
+ dev_dbg(component->dev, "DAC divider %d\n", dac_div);
+ dev_dbg(component->dev, "NCP divider %d\n", ncp_div);
+ dev_dbg(component->dev, "OSR divider %d\n", osr_div);
+ dev_dbg(component->dev, "BCK divider %d\n", bclk_div);
+ dev_dbg(component->dev, "LRCK divider %d\n", lrclk_div);
+ dev_dbg(component->dev, "IDAC %d\n", idac);
+ dev_dbg(component->dev, "1<<FSSP %d\n", 1 << fssp);
return 0;
}
@@ -1105,15 +1170,13 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
int alen;
int gpio;
- int clock_output;
- int master_mode;
int ret;
- dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+ dev_dbg(component->dev, "hw_params %u Hz, %u channels\n",
params_rate(params),
params_channels(params));
@@ -1131,62 +1194,41 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
alen = PCM512x_ALEN_32;
break;
default:
- dev_err(codec->dev, "Bad frame size: %d\n",
+ dev_err(component->dev, "Bad frame size: %d\n",
params_width(params));
return -EINVAL;
}
- switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- ret = regmap_update_bits(pcm512x->regmap,
- PCM512x_BCLK_LRCLK_CFG,
- PCM512x_BCKP
- | PCM512x_BCKO | PCM512x_LRKO,
- 0);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to enable slave mode: %d\n", ret);
- return ret;
- }
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
+ PCM512x_ALEN, alen);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set frame size: %d\n", ret);
+ return ret;
+ }
+ if ((pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_CBC_CFC) {
ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT,
PCM512x_DCAS, 0);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable clock divider autoset: %d\n",
ret);
return ret;
}
- return 0;
- case SND_SOC_DAIFMT_CBM_CFM:
- clock_output = PCM512x_BCKO | PCM512x_LRKO;
- master_mode = PCM512x_RLRK | PCM512x_RBCK;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- clock_output = PCM512x_BCKO;
- master_mode = PCM512x_RBCK;
- break;
- default:
- return -EINVAL;
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
- PCM512x_ALEN, alen);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to set frame size: %d\n", ret);
- return ret;
+ goto skip_pll;
}
if (pcm512x->pll_out) {
ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_A, 0x11);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set FLEX_A: %d\n", ret);
+ dev_err(component->dev, "Failed to set FLEX_A: %d\n", ret);
return ret;
}
ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_B, 0xff);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set FLEX_B: %d\n", ret);
+ dev_err(component->dev, "Failed to set FLEX_B: %d\n", ret);
return ret;
}
@@ -1199,7 +1241,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
| PCM512x_IDSK | PCM512x_IDCH
| PCM512x_DCAS);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to ignore auto-clock failures: %d\n",
ret);
return ret;
@@ -1214,16 +1256,40 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
| PCM512x_IDSK | PCM512x_IDCH
| PCM512x_DCAS | PCM512x_IPLK);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to ignore auto-clock failures: %d\n",
ret);
return ret;
}
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
- PCM512x_PLLE, 0);
+ if (!pcm512x->force_pll_on) {
+ ret = regmap_update_bits(pcm512x->regmap,
+ PCM512x_PLL_EN, PCM512x_PLLE, 0);
+ } else {
+ /* provide minimum PLL config for TAS575x clocking
+ * and leave PLL enabled
+ */
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_0, 0x01);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set pll coefficient: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_1, 0x04);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set pll coefficient: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_EN, 0x01);
+ dev_dbg(component->dev, "Enabling PLL for TAS575x\n");
+ }
+
if (ret != 0) {
- dev_err(codec->dev, "Failed to disable pll: %d\n", ret);
+ dev_err(component->dev, "Failed to set pll mode: %d\n", ret);
return ret;
}
}
@@ -1236,7 +1302,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF,
PCM512x_SREF, PCM512x_SREF_GPIO);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio as pllref: %d\n", ret);
return ret;
}
@@ -1245,7 +1311,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN,
PCM512x_GREF, gpio);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio %d as pllin: %d\n",
pcm512x->pll_in, ret);
return ret;
@@ -1254,33 +1320,15 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
PCM512x_PLLE, PCM512x_PLLE);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable pll: %d\n", ret);
+ dev_err(component->dev, "Failed to enable pll: %d\n", ret);
return ret;
}
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG,
- PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO,
- clock_output);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable clock output: %d\n", ret);
- return ret;
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE,
- PCM512x_RLRK | PCM512x_RBCK,
- master_mode);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable master mode: %d\n", ret);
- return ret;
- }
- if (pcm512x->pll_out) {
gpio = PCM512x_G1OE << (pcm512x->pll_out - 1);
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN,
gpio, gpio);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable gpio %d: %d\n",
+ dev_err(component->dev, "Failed to enable gpio %d: %d\n",
pcm512x->pll_out, ret);
return ret;
}
@@ -1289,7 +1337,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, gpio,
PCM512x_GxSL, PCM512x_GxSL_PLLCK);
if (ret != 0) {
- dev_err(codec->dev, "Failed to output pll on %d: %d\n",
+ dev_err(component->dev, "Failed to output pll on %d: %d\n",
ret, pcm512x->pll_out);
return ret;
}
@@ -1298,34 +1346,172 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
PCM512x_RQSY, PCM512x_RQSY_HALT);
if (ret != 0) {
- dev_err(codec->dev, "Failed to halt clocks: %d\n", ret);
+ dev_err(component->dev, "Failed to halt clocks: %d\n", ret);
return ret;
}
ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
PCM512x_RQSY, PCM512x_RQSY_RESUME);
if (ret != 0) {
- dev_err(codec->dev, "Failed to resume clocks: %d\n", ret);
+ dev_err(component->dev, "Failed to resume clocks: %d\n", ret);
return ret;
}
+skip_pll:
return 0;
}
static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int afmt;
+ int offset = 0;
+ int clock_output;
+ int provider_mode;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ clock_output = 0;
+ provider_mode = 0;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ clock_output = PCM512x_BCKO | PCM512x_LRKO;
+ provider_mode = PCM512x_RLRK | PCM512x_RBCK;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ clock_output = PCM512x_BCKO;
+ provider_mode = PCM512x_RBCK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG,
+ PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO,
+ clock_output);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to enable clock output: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE,
+ PCM512x_RLRK | PCM512x_RBCK,
+ provider_mode);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to enable provider mode: %d\n", ret);
+ return ret;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ afmt = PCM512x_AFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ afmt = PCM512x_AFMT_RTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ afmt = PCM512x_AFMT_LTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ offset = 1;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_B:
+ afmt = PCM512x_AFMT_DSP;
+ break;
+ default:
+ dev_err(component->dev, "unsupported DAI format: 0x%x\n",
+ pcm512x->fmt);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
+ PCM512x_AFMT, afmt);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set data format: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_2,
+ 0xFF, offset);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to set data offset: %d\n", ret);
+ return ret;
+ }
pcm512x->fmt = fmt;
return 0;
}
+static int pcm512x_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+ if (ratio > 256)
+ return -EINVAL;
+
+ pcm512x->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+ int ret;
+ unsigned int mute_det;
+
+ mutex_lock(&pcm512x->mutex);
+
+ if (mute) {
+ pcm512x->mute |= 0x1;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_MUTE,
+ PCM512x_RQML | PCM512x_RQMR,
+ PCM512x_RQML | PCM512x_RQMR);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set digital mute: %d\n", ret);
+ goto unlock;
+ }
+
+ regmap_read_poll_timeout(pcm512x->regmap,
+ PCM512x_ANALOG_MUTE_DET,
+ mute_det, (mute_det & 0x3) == 0,
+ 200, 10000);
+ } else {
+ pcm512x->mute &= ~0x1;
+ ret = pcm512x_update_mute(pcm512x);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to update digital mute: %d\n", ret);
+ goto unlock;
+ }
+
+ regmap_read_poll_timeout(pcm512x->regmap,
+ PCM512x_ANALOG_MUTE_DET,
+ mute_det,
+ (mute_det & 0x3)
+ == ((~pcm512x->mute >> 1) & 0x3),
+ 200, 10000);
+ }
+
+unlock:
+ mutex_unlock(&pcm512x->mutex);
+
+ return ret;
+}
+
static const struct snd_soc_dai_ops pcm512x_dai_ops = {
.startup = pcm512x_dai_startup,
.hw_params = pcm512x_hw_params,
.set_fmt = pcm512x_set_fmt,
+ .mute_stream = pcm512x_mute,
+ .set_bclk_ratio = pcm512x_set_bclk_ratio,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver pcm512x_dai = {
@@ -1344,16 +1530,16 @@ static struct snd_soc_dai_driver pcm512x_dai = {
.ops = &pcm512x_dai_ops,
};
-static struct snd_soc_codec_driver pcm512x_codec_driver = {
- .set_bias_level = pcm512x_set_bias_level,
- .idle_bias_off = true,
-
- .controls = pcm512x_controls,
- .num_controls = ARRAY_SIZE(pcm512x_controls),
- .dapm_widgets = pcm512x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
- .dapm_routes = pcm512x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
+static const struct snd_soc_component_driver pcm512x_component_driver = {
+ .set_bias_level = pcm512x_set_bias_level,
+ .controls = pcm512x_controls,
+ .num_controls = ARRAY_SIZE(pcm512x_controls),
+ .dapm_widgets = pcm512x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
+ .dapm_routes = pcm512x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_range_cfg pcm512x_range = {
@@ -1390,6 +1576,8 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (!pcm512x)
return -ENOMEM;
+ mutex_init(&pcm512x->mutex);
+
dev_set_drvdata(dev, pcm512x);
pcm512x->regmap = regmap;
@@ -1408,8 +1596,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
- ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
- &pcm512x->supply_nb[i]);
+ ret = devm_regulator_register_notifier(
+ pcm512x->supplies[i].consumer,
+ &pcm512x->supply_nb[i]);
if (ret != 0) {
dev_err(dev,
"Failed to register regulator notifier: %d\n",
@@ -1439,13 +1628,15 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
}
pcm512x->sclk = devm_clk_get(dev, NULL);
- if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
if (!IS_ERR(pcm512x->sclk)) {
ret = clk_prepare_enable(pcm512x->sclk);
if (ret != 0) {
dev_err(dev, "Failed to enable SCLK: %d\n", ret);
- return ret;
+ goto err;
}
}
@@ -1471,7 +1662,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (val > 6) {
dev_err(dev, "Invalid pll-in\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
pcm512x->pll_in = val;
}
@@ -1480,7 +1671,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (val > 6) {
dev_err(dev, "Invalid pll-out\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
pcm512x->pll_out = val;
}
@@ -1489,17 +1680,22 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
dev_err(dev,
"Error: both pll-in and pll-out, or none\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
if (pcm512x->pll_in && pcm512x->pll_in == pcm512x->pll_out) {
dev_err(dev, "Error: pll-in == pll-out\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
+
+ if (!strcmp(np->name, "tas5756") ||
+ !strcmp(np->name, "tas5754"))
+ pcm512x->force_pll_on = 1;
+ dev_dbg(dev, "Device ID: %s\n", np->name);
}
#endif
- ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
+ ret = devm_snd_soc_register_component(dev, &pcm512x_component_driver,
&pcm512x_dai, 1);
if (ret != 0) {
dev_err(dev, "Failed to register CODEC: %d\n", ret);
@@ -1524,7 +1720,6 @@ void pcm512x_remove(struct device *dev)
{
struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
pm_runtime_disable(dev);
if (!IS_ERR(pcm512x->sclk))
clk_disable_unprepare(pcm512x->sclk);
@@ -1533,7 +1728,6 @@ void pcm512x_remove(struct device *dev)
}
EXPORT_SYMBOL_GPL(pcm512x_remove);
-#ifdef CONFIG_PM
static int pcm512x_suspend(struct device *dev)
{
struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
@@ -1595,13 +1789,11 @@ static int pcm512x_resume(struct device *dev)
return 0;
}
-#endif
-const struct dev_pm_ops pcm512x_pm_ops = {
- SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(pcm512x_pm_ops) = {
+ RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
};
-EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
MODULE_DESCRIPTION("ASoC PCM512x codec driver");
-MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
index b7c310207223..08d04f539805 100644
--- a/sound/soc/codecs/pcm512x.h
+++ b/sound/soc/codecs/pcm512x.h
@@ -1,17 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#ifndef _SND_SOC_PCM512X
@@ -112,7 +104,9 @@
#define PCM512x_RQST_SHIFT 4
/* Page 0, Register 3 - mute */
+#define PCM512x_RQMR (1 << 0)
#define PCM512x_RQMR_SHIFT 0
+#define PCM512x_RQML (1 << 4)
#define PCM512x_RQML_SHIFT 4
/* Page 0, Register 4 - PLL */
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
new file mode 100644
index 000000000000..bde190a659b1
--- /dev/null
+++ b/sound/soc/codecs/pcm6240.c
@@ -0,0 +1,2167 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC Device
+//
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The PCM6240 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// PCM6240 Family chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#include <linux/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm6240.h"
+
+static const struct i2c_device_id pcmdevice_i2c_id[] = {
+ { "adc3120", ADC3120 },
+ { "adc5120", ADC5120 },
+ { "adc6120", ADC6120 },
+ { "dix4192", DIX4192 },
+ { "pcm1690", PCM1690 },
+ { "pcm3120", PCM3120 },
+ { "pcm3140", PCM3140 },
+ { "pcm5120", PCM5120 },
+ { "pcm5140", PCM5140 },
+ { "pcm6120", PCM6120 },
+ { "pcm6140", PCM6140 },
+ { "pcm6240", PCM6240 },
+ { "pcm6260", PCM6260 },
+ { "pcm9211", PCM9211 },
+ { "pcmd3140", PCMD3140 },
+ { "pcmd3180", PCMD3180 },
+ { "pcmd512x", PCMD512X },
+ { "taa5212", TAA5212 },
+ { "taa5412", TAA5412 },
+ { "tad5212", TAD5212 },
+ { "tad5412", TAD5412 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id);
+
+static const char *const pcmdev_ctrl_name[] = {
+ "%s i2c%d Dev%d Ch%d Ana Volume",
+ "%s i2c%d Dev%d Ch%d Digi Volume",
+ "%s i2c%d Dev%d Ch%d Fine Volume",
+};
+
+static const struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = {
+ {
+ .shift = 1,
+ .reg = ADC5120_REG_CH1_ANALOG_GAIN,
+ .max = 0x54,
+ .invert = 0,
+ },
+ {
+ .shift = 1,
+ .reg = ADC5120_REG_CH2_ANALOG_GAIN,
+ .max = 0x54,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control adc5120_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = ADC5120_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = ADC5120_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm1690_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH7_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH8_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6240_analog_gain_ctl[] = {
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH1_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH2_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH3_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH4_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6240_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6260_analog_gain_ctl[] = {
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH1_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH2_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH3_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH4_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH5_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH6_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6260_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm9211_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM9211_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM9211_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3140_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3140_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3180_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH7_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH8_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3180_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH5_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH6_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH7_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH8_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control taa5412_digi_vol_ctl[] = {
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH1_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH2_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH3_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH4_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control taa5412_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 4,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 4,
+ }
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3140_dig_gain_tlv,
+ -10000, 2700);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_fine_dig_gain_tlv,
+ -12750, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_dig_gain_tlv,
+ -25500, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm9211_dig_gain_tlv,
+ -11450, 2000);
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc5120_fgain_tlv,
+ -10050, 2700);
+static const DECLARE_TLV_DB_LINEAR(adc5120_chgain_tlv, 0, 4200);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm6260_fgain_tlv,
+ -10000, 2700);
+static const DECLARE_TLV_DB_LINEAR(pcm6260_chgain_tlv, 0, 4200);
+static const DECLARE_TLV_DB_MINMAX_MUTE(taa5412_dig_vol_tlv,
+ -8050, 4700);
+static const DECLARE_TLV_DB_LINEAR(taa5412_fine_gain_tlv,
+ -80, 70);
+
+static int pcmdev_change_dev(struct pcmdevice_priv *pcm_priv,
+ unsigned short dev_no)
+{
+ struct i2c_client *client = (struct i2c_client *)pcm_priv->client;
+ struct regmap *map = pcm_priv->regmap;
+ int ret;
+
+ if (client->addr == pcm_priv->addr[dev_no])
+ return 0;
+
+ client->addr = pcm_priv->addr[dev_no];
+ /* All pcmdevices share the same regmap, clear the page
+ * inside regmap once switching to another pcmdevice.
+ * Register 0 at any pages inside pcmdevice is the same
+ * one for page-switching.
+ */
+ ret = regmap_write(map, PCMDEVICE_PAGE_SELECT, 0);
+ if (ret < 0)
+ dev_err(pcm_priv->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_read(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int *val)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(map, reg, val);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_update_bits(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int mask,
+ unsigned int value)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(map, reg, mask, value);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: update_bits err=%d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(component);
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+ int max = mc->max, ret;
+ unsigned int mask = BIT(fls(max)) - 1;
+ unsigned int dev_no = mc->dev_no;
+ unsigned int shift = mc->shift;
+ unsigned int reg = mc->reg;
+ unsigned int val;
+
+ mutex_lock(&pcm_dev->codec_lock);
+
+ if (pcm_dev->chip_id == PCM1690) {
+ ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL,
+ &val);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: read mode err=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ val &= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ /* Set to wide-range mode, before using vol ctrl. */
+ if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) {
+ ucontrol->value.integer.value[0] = -25500;
+ goto out;
+ }
+ /* Set to fine mode, before using fine vol ctrl. */
+ if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) {
+ ucontrol->value.integer.value[0] = -12750;
+ goto out;
+ }
+ }
+
+ ret = pcmdev_dev_read(pcm_dev, dev_no, reg, &val);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: read err=%d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ val = (val >> shift) & mask;
+ val = (val > max) ? max : val;
+ val = mc->invert ? max - val : val;
+ ucontrol->value.integer.value[0] = val;
+out:
+ mutex_unlock(&pcm_dev->codec_lock);
+ return ret;
+}
+
+static int pcmdevice_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL);
+}
+
+static int pcm1690_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL);
+}
+
+static int pcm1690_get_finevolsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol,
+ PCMDEV_PCM1690_FINE_VOL_CTRL);
+}
+
+static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(component);
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+ int max = mc->max, rc;
+ unsigned int mask = BIT(fls(max)) - 1;
+ unsigned int dev_no = mc->dev_no;
+ unsigned int shift = mc->shift;
+ unsigned int val, val_mask;
+ unsigned int reg = mc->reg;
+
+ mutex_lock(&pcm_dev->codec_lock);
+ val = ucontrol->value.integer.value[0] & mask;
+ val = (val > max) ? max : val;
+ val = mc->invert ? max - val : val;
+ val_mask = mask << shift;
+ val = val << shift;
+
+ switch (vol_ctrl_type) {
+ case PCMDEV_PCM1690_VOL_CTRL:
+ val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ val |= PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE;
+ break;
+ case PCMDEV_PCM1690_FINE_VOL_CTRL:
+ val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ val |= PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP;
+ break;
+ }
+
+ rc = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, val_mask, val);
+ if (rc < 0)
+ dev_err(pcm_dev->dev, "%s: update_bits err = %d\n",
+ __func__, rc);
+ else
+ rc = 1;
+ mutex_unlock(&pcm_dev->codec_lock);
+ return rc;
+}
+
+static int pcmdevice_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL);
+}
+
+static int pcm1690_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL);
+}
+
+static int pcm1690_put_finevolsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol,
+ PCMDEV_PCM1690_FINE_VOL_CTRL);
+}
+
+static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = {
+ // ADC3120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // ADC5120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // ADC6120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // DIX4192
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // PCM1690
+ {
+ {
+ .gain = pcm1690_fine_dig_gain_tlv,
+ .pcmdev_ctrl = pcm1690_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl),
+ .get = pcm1690_get_volsw,
+ .put = pcm1690_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ {
+ .gain = pcm1690_dig_gain_tlv,
+ .pcmdev_ctrl = pcm1690_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl),
+ .get = pcm1690_get_finevolsw,
+ .put = pcm1690_put_finevolsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ },
+ // PCM3120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM3140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM5120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM5140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6240
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6260
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6260_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6260_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6260_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6260_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM9211
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .gain = pcm9211_dig_gain_tlv,
+ .pcmdev_ctrl = pcm9211_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm9211_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+
+ },
+ // PCMD3140
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = pcmd3140_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3140_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = pcmd3140_dig_gain_tlv,
+ .pcmdev_ctrl = pcmd3140_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3140_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCMD3180
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = pcmd3180_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3180_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = pcmd3140_dig_gain_tlv,
+ .pcmdev_ctrl = pcmd3180_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3180_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCMD512X
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // TAA5212
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = taa5412_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = taa5412_dig_vol_tlv,
+ .pcmdev_ctrl = taa5412_digi_vol_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // TAA5412
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = taa5412_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = taa5412_dig_vol_tlv,
+ .pcmdev_ctrl = taa5412_digi_vol_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // TAD5212
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // TAD5412
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+};
+
+static int pcmdev_dev_bulk_write(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(map, reg, data, len);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: bulk_write err = %d\n", __func__,
+ ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_write(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int value)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_write(map, reg, value);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdevice_info_profile(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = max(0, pcm_dev->regbin.ncfgs - 1);
+
+ return 0;
+}
+
+static int pcmdevice_get_profile_id(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = pcm_dev->cur_conf;
+
+ return 0;
+}
+
+static int pcmdevice_set_profile_id(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+ int nr_profile = ucontrol->value.integer.value[0];
+ int max = pcm_dev->regbin.ncfgs - 1;
+ int ret = 0;
+
+ nr_profile = clamp(nr_profile, 0, max);
+
+ if (pcm_dev->cur_conf != nr_profile) {
+ pcm_dev->cur_conf = nr_profile;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int pcmdevice_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mc->max;
+ return 0;
+}
+
+static void pcm9211_sw_rst(struct pcmdevice_priv *pcm_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ ret = pcmdev_dev_update_bits(pcm_dev, i,
+ PCM9211_REG_SW_CTRL, PCM9211_REG_SW_CTRL_MRST_MSK,
+ PCM9211_REG_SW_CTRL_MRST);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n",
+ __func__, i, ret);
+ }
+}
+
+static void pcmdevice_sw_rst(struct pcmdevice_priv *pcm_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ ret = pcmdev_dev_write(pcm_dev, i, PCMDEVICE_REG_SWRESET,
+ PCMDEVICE_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n",
+ __func__, i, ret);
+ }
+}
+
+static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt,
+ const unsigned char *config_data, unsigned int config_size,
+ int *status)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ struct pcmdevice_config_info *cfg_info;
+ struct pcmdevice_block_data **bk_da;
+ unsigned int config_offset = 0, i;
+
+ cfg_info = kzalloc(sizeof(struct pcmdevice_config_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ goto out;
+ }
+
+ if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: cfg_name out of boundary\n", __func__);
+ goto out;
+ }
+ memcpy(cfg_info->cfg_name, &config_data[config_offset], 64);
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev, "%s: nblocks out of boundary\n",
+ __func__);
+ goto out;
+ }
+ cfg_info->nblocks =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(struct pcmdevice_block_data *), GFP_KERNEL);
+ if (!bk_da) {
+ *status = -ENOMEM;
+ goto out;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: out of boundary i = %d nblocks = %u\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ bk_da[i] = kzalloc(sizeof(struct pcmdevice_block_data),
+ GFP_KERNEL);
+ if (!bk_da[i]) {
+ *status = -ENOMEM;
+ break;
+ }
+ bk_da[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ if (bk_da[i]->block_type == PCMDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (bk_da[i]->dev_idx == 0)
+ cfg_info->active_dev =
+ (1 << pcm_dev->ndev) - 1;
+ else
+ cfg_info->active_dev =
+ 1 << (bk_da[i]->dev_idx - 1);
+ }
+
+ bk_da[i]->yram_checksum =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_be32(&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: out of boundary: i = %d blks = %u\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+
+ bk_da[i]->regdata = kmemdup(&config_data[config_offset],
+ bk_da[i]->block_size, GFP_KERNEL);
+ if (!bk_da[i]->regdata) {
+ *status = -ENOMEM;
+ goto out;
+ }
+ config_offset += bk_da[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+out:
+ return cfg_info;
+}
+
+static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev,
+ int dev_no, int ctl_id)
+{
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ struct snd_soc_component *comp = pcm_dev->component;
+ struct pcmdevice_mixer_control *pcmdev_ctrl;
+ struct snd_kcontrol_new *pcmdev_controls;
+ int ret, mix_index = 0, name_id, chn;
+ unsigned int id = pcm_dev->chip_id;
+ const int nr_chn =
+ pcmdev_gain_ctl_info[id][ctl_id].ctrl_array_size;
+ const char *ctrl_name;
+ char *name;
+
+ if (!nr_chn) {
+ dev_dbg(pcm_dev->dev, "%s: no gain ctrl for %s\n", __func__,
+ pcm_dev->dev_name);
+ return 0;
+ }
+
+ pcmdev_controls = devm_kcalloc(pcm_dev->dev, nr_chn,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ if (!pcmdev_controls)
+ return -ENOMEM;
+
+ name_id = pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl_name_id;
+
+ ctrl_name = pcmdev_ctrl_name[name_id];
+
+ for (chn = 1; chn <= nr_chn; chn++) {
+ name = devm_kzalloc(pcm_dev->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ ctrl_name, pcm_dev->upper_dev_name, adap->nr,
+ dev_no, chn);
+ pcmdev_controls[mix_index].tlv.p =
+ pcmdev_gain_ctl_info[id][ctl_id].gain;
+ pcmdev_ctrl = devm_kmemdup(pcm_dev->dev,
+ &pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl[chn - 1],
+ sizeof(*pcmdev_ctrl), GFP_KERNEL);
+ if (!pcmdev_ctrl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pcmdev_ctrl->dev_no = dev_no;
+ pcmdev_controls[mix_index].private_value =
+ (unsigned long)pcmdev_ctrl;
+ pcmdev_controls[mix_index].name = name;
+ pcmdev_controls[mix_index].access =
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ pcmdev_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ pcmdev_controls[mix_index].info = pcmdevice_info_volsw;
+ pcmdev_controls[mix_index].get =
+ pcmdev_gain_ctl_info[id][ctl_id].get;
+ pcmdev_controls[mix_index].put =
+ pcmdev_gain_ctl_info[id][ctl_id].put;
+ mix_index++;
+ }
+
+ ret = snd_soc_add_component_controls(comp, pcmdev_controls, mix_index);
+ if (ret)
+ dev_err(pcm_dev->dev, "%s: add_controls err = %d\n",
+ __func__, ret);
+out:
+ return ret;
+}
+
+static int pcmdev_profile_ctrl_add(struct pcmdevice_priv *pcm_dev)
+{
+ struct snd_soc_component *comp = pcm_dev->component;
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ struct snd_kcontrol_new *pcmdev_ctrl;
+ char *name;
+ int ret;
+
+ pcmdev_ctrl = devm_kzalloc(pcm_dev->dev,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ if (!pcmdev_ctrl)
+ return -ENOMEM;
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(pcm_dev->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s i2c%d Profile id", pcm_dev->upper_dev_name, adap->nr);
+ pcmdev_ctrl->name = name;
+ pcmdev_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ pcmdev_ctrl->info = pcmdevice_info_profile;
+ pcmdev_ctrl->get = pcmdevice_get_profile_id;
+ pcmdev_ctrl->put = pcmdevice_set_profile_id;
+
+ ret = snd_soc_add_component_controls(comp, pcmdev_ctrl, 1);
+ if (ret)
+ dev_err(pcm_dev->dev, "%s: add_controls err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static void pcmdevice_config_info_remove(void *ctxt)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *) ctxt;
+ struct pcmdevice_regbin *regbin = &(pcm_dev->regbin);
+ struct pcmdevice_config_info **cfg_info = regbin->cfg_info;
+ int i, j;
+
+ if (!cfg_info)
+ return;
+ for (i = 0; i < regbin->ncfgs; i++) {
+ if (!cfg_info[i])
+ continue;
+ if (cfg_info[i]->blk_data) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
+ if (!cfg_info[i]->blk_data[j])
+ continue;
+ kfree(cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ kfree(cfg_info[i]->blk_data);
+ }
+ kfree(cfg_info[i]);
+ }
+ kfree(cfg_info);
+}
+
+static int pcmdev_regbin_ready(const struct firmware *fmw, void *ctxt)
+{
+ struct pcmdevice_config_info **cfg_info;
+ struct pcmdevice_priv *pcm_dev = ctxt;
+ struct pcmdevice_regbin_hdr *fw_hdr;
+ struct pcmdevice_regbin *regbin;
+ unsigned int total_config_sz = 0;
+ int offset = 0, ret = 0, i;
+ unsigned char *buf;
+
+ regbin = &(pcm_dev->regbin);
+ fw_hdr = &(regbin->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(pcm_dev->dev, "%s: failed to read %s\n",
+ __func__, pcm_dev->bin_name);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(pcm_dev->dev, "%s: file size(%d) not match %u",
+ __func__, (int)fmw->size, fw_hdr->img_sz);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(pcm_dev->dev, "%s: bin version 0x%04x is out of date",
+ __func__, fw_hdr->binary_version_num);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+ if (fw_hdr->ndev != pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: invalid ndev(%u)\n", __func__,
+ fw_hdr->ndev);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (offset + PCMDEVICE_MAX_REGBIN_DEVICES > fw_hdr->img_sz) {
+ dev_err(pcm_dev->dev, "%s: devs out of boundary!\n", __func__);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < PCMDEVICE_MAX_REGBIN_DEVICES; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < PCMDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(pcm_dev->dev, "%s: bin file error!\n", __func__);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -ENOMEM;
+ goto out;
+ }
+ regbin->cfg_info = cfg_info;
+ regbin->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = pcmdevice_add_config(ctxt, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ /* In case the bin file is partially destroyed. */
+ if (regbin->ncfgs == 0)
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ regbin->ncfgs += 1;
+ }
+
+out:
+ if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) {
+ dev_err(pcm_dev->dev,
+ "%s: remove config due to fw load error!\n", __func__);
+ pcmdevice_config_info_remove(pcm_dev);
+ }
+
+ return ret;
+}
+
+static int pcmdevice_comp_probe(struct snd_soc_component *comp)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp);
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ const struct firmware *fw_entry = NULL;
+ int ret, i, j;
+
+ mutex_lock(&pcm_dev->codec_lock);
+
+ pcm_dev->component = comp;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ for (j = 0; j < 2; j++) {
+ ret = pcmdev_gain_ctrl_add(pcm_dev, i, j);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ if (comp->name_prefix) {
+ /* There's name_prefix defined in DTS. Bin file name will be
+ * name_prefix.bin stores the firmware including register
+ * setting and params for different filters inside chips, it
+ * must be copied into firmware folder. The same types of
+ * pcmdevices sitting on the same i2c bus will be aggregated as
+ * one single codec, all of them share the same bin file.
+ */
+ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN,
+ "%s.bin", comp->name_prefix);
+ } else {
+ /* There's NO name_prefix defined in DTS. Bin file name will be
+ * device-name[defined in pcmdevice_i2c_id]-i2c-bus_id
+ * [0,1,...,N]-sum[1,...,4]dev.bin stores the firmware
+ * including register setting and params for different filters
+ * inside chips, it must be copied into firmware folder. The
+ * same types of pcmdevices sitting on the same i2c bus will be
+ * aggregated as one single codec, all of them share the same
+ * bin file.
+ */
+ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN,
+ "%s-i2c-%d-%udev.bin", pcm_dev->dev_name, adap->nr,
+ pcm_dev->ndev);
+ }
+
+ ret = request_firmware(&fw_entry, pcm_dev->bin_name, pcm_dev->dev);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__,
+ pcm_dev->bin_name, ret);
+ goto out;
+ }
+
+ ret = pcmdev_regbin_ready(fw_entry, pcm_dev);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__,
+ pcm_dev->bin_name, ret);
+ goto out;
+ }
+ ret = pcmdev_profile_ctrl_add(pcm_dev);
+out:
+ release_firmware(fw_entry);
+
+ mutex_unlock(&pcm_dev->codec_lock);
+ return ret;
+}
+
+
+static void pcmdevice_comp_remove(struct snd_soc_component *codec)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec);
+
+ if (!pcm_dev)
+ return;
+ mutex_lock(&pcm_dev->codec_lock);
+ pcmdevice_config_info_remove(pcm_dev);
+ mutex_unlock(&pcm_dev->codec_lock);
+}
+
+static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI1 OUT", "ASI1 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("MIC"),
+};
+
+static const struct snd_soc_dapm_route pcmdevice_audio_map[] = {
+ {"OUT", NULL, "ASI"},
+ {"ASI1 OUT", NULL, "MIC"},
+};
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_pcmdevice = {
+ .probe = pcmdevice_comp_probe,
+ .remove = pcmdevice_comp_remove,
+ .dapm_widgets = pcmdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcmdevice_dapm_widgets),
+ .dapm_routes = pcmdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pcmdevice_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pcmdev_single_byte_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int offset = 2;
+ int i, ret;
+
+ offset += 2;
+ if (offset + 4 * len > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d byt wr out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = pcmdev_dev_write(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 1], data[offset + 2]),
+ data[offset + 3]);
+ /* skip this error for next operation or next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d single write err\n",
+ __func__, devn);
+
+ offset += 4;
+ }
+
+ return offset;
+}
+
+static int pcmdev_burst_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int offset = 2;
+ int ret;
+
+ offset += 2;
+ if (offset + 4 + len > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d burst Out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ if (len % 4) {
+ dev_err(pcm_dev->dev, "%s: dev-%d bst-len(%u) not div by 4\n",
+ __func__, devn, len);
+ return -EINVAL;
+ }
+ ret = pcmdev_dev_bulk_write(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 1], data[offset + 2]),
+ &(data[offset + 4]), len);
+ /* skip this error for next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d bulk_write err = %d\n",
+ __func__, devn, ret);
+
+ offset += (len + 4);
+
+ return offset;
+}
+
+static int pcmdev_delay(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned int delay_time = 0;
+ int offset = 2;
+
+ if (offset + 2 > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d delay out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ delay_time = get_unaligned_be16(&data[2]) * 1000;
+ usleep_range(delay_time, delay_time + 50);
+ offset += 2;
+
+ return offset;
+}
+
+static int pcmdev_bits_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ int offset = 2;
+ int ret;
+
+ if (offset + 6 > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d bit write out of memory\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ ret = pcmdev_dev_update_bits(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 3], data[offset + 4]),
+ data[offset + 1], data[offset + 5]);
+ /* skip this error for next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d update_bits err = %d\n",
+ __func__, devn, ret);
+
+ offset += 6;
+
+ return offset;
+}
+
+static int pcmdevice_process_block(void *ctxt, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ int devn, dev_end, ret = 0;
+ unsigned char subblk_typ = data[1];
+
+ if (dev_idx) {
+ devn = dev_idx - 1;
+ dev_end = dev_idx;
+ } else {
+ devn = 0;
+ dev_end = pcm_dev->ndev;
+ }
+
+ /* loop in case of several devices sharing the same sub-block */
+ for (; devn < dev_end; devn++) {
+ switch (subblk_typ) {
+ case PCMDEVICE_CMD_SING_W:
+ ret = pcmdev_single_byte_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_BURST:
+ ret = pcmdev_burst_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_DELAY:
+ ret = pcmdev_delay(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_FIELD_W:
+ ret = pcmdev_bits_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ default:
+ break;
+ }
+ /*
+ * In case of sub-block error, break the loop for the rest of
+ * devices.
+ */
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static void pcmdevice_select_cfg_blk(void *ctxt, int conf_no,
+ unsigned char block_type)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ struct pcmdevice_regbin *regbin = &(pcm_dev->regbin);
+ struct pcmdevice_config_info **cfg_info = regbin->cfg_info;
+ struct pcmdevice_block_data **blk_data;
+ int j, k;
+
+ if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(pcm_dev->dev, "%s: conf_no should be less than %u\n",
+ __func__, regbin->ncfgs);
+ goto out;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, ret;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(pcm_dev->dev,
+ "%s: block_type should be out of range\n",
+ __func__);
+ goto out;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < (int)blk_data[j]->n_subblks; k++) {
+ ret = pcmdevice_process_block(pcm_dev,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += ret;
+ if (blk_data[j]->block_size < length) {
+ dev_err(pcm_dev->dev,
+ "%s: %u %u out of boundary\n",
+ __func__, length,
+ blk_data[j]->block_size);
+ break;
+ }
+ }
+ if (length != blk_data[j]->block_size)
+ dev_err(pcm_dev->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+
+out:
+ return;
+}
+
+static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec);
+ unsigned char block_type;
+
+ if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) {
+ dev_err(pcm_dev->dev, "%s: bin file not loaded\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mute)
+ block_type = PCMDEVICE_BIN_BLK_PRE_SHUTDOWN;
+ else
+ block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP;
+
+ mutex_lock(&pcm_dev->codec_lock);
+ pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type);
+ mutex_unlock(&pcm_dev->codec_lock);
+ return 0;
+}
+
+static int pcmdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int fsrate;
+ unsigned int slot_width;
+ int bclk_rate;
+ int ret = 0;
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ break;
+ case 44100:
+ break;
+ default:
+ dev_err(pcm_dev->dev, "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ break;
+ case 24:
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(pcm_dev->dev, "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(pcm_dev->dev, "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ ret = bclk_rate;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_soc_dai_ops pcmdevice_dai_ops = {
+ .mute_stream = pcmdevice_mute,
+ .hw_params = pcmdevice_hw_params,
+};
+
+static struct snd_soc_dai_driver pcmdevice_dai_driver[] = {
+ {
+ .name = "pcmdevice-codec",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = PCMDEVICE_MAX_CHANNELS,
+ .rates = PCMDEVICE_RATES,
+ .formats = PCMDEVICE_FORMATS,
+ },
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = PCMDEVICE_MAX_CHANNELS,
+ .rates = PCMDEVICE_RATES,
+ .formats = PCMDEVICE_FORMATS,
+ },
+ .ops = &pcmdevice_dai_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcmdevice_of_match[] = {
+ { .compatible = "ti,adc3120" },
+ { .compatible = "ti,adc5120" },
+ { .compatible = "ti,adc6120" },
+ { .compatible = "ti,dix4192" },
+ { .compatible = "ti,pcm1690" },
+ { .compatible = "ti,pcm3120" },
+ { .compatible = "ti,pcm3140" },
+ { .compatible = "ti,pcm5120" },
+ { .compatible = "ti,pcm5140" },
+ { .compatible = "ti,pcm6120" },
+ { .compatible = "ti,pcm6140" },
+ { .compatible = "ti,pcm6240" },
+ { .compatible = "ti,pcm6260" },
+ { .compatible = "ti,pcm9211" },
+ { .compatible = "ti,pcmd3140" },
+ { .compatible = "ti,pcmd3180" },
+ { .compatible = "ti,pcmd512x" },
+ { .compatible = "ti,taa5212" },
+ { .compatible = "ti,taa5412" },
+ { .compatible = "ti,tad5212" },
+ { .compatible = "ti,tad5412" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pcmdevice_of_match);
+#endif
+
+static const struct regmap_range_cfg pcmdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = PCMDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config pcmdevice_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .ranges = pcmdevice_ranges,
+ .num_ranges = ARRAY_SIZE(pcmdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev)
+{
+ if (pcm_dev->irq)
+ free_irq(pcm_dev->irq, pcm_dev);
+ mutex_destroy(&pcm_dev->codec_lock);
+}
+
+static char *str_to_upper(char *str)
+{
+ char *orig = str;
+
+ if (!str)
+ return NULL;
+
+ while (*str) {
+ *str = toupper(*str);
+ str++;
+ }
+
+ return orig;
+}
+
+static int pcmdevice_i2c_probe(struct i2c_client *i2c)
+{
+ struct pcmdevice_priv *pcm_dev;
+ struct device_node *np;
+ unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES];
+ int ret = 0, i = 0, ndev = 0;
+
+ pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL);
+ if (!pcm_dev)
+ return -ENOMEM;
+
+ pcm_dev->chip_id = (uintptr_t)i2c_get_match_data(i2c);
+
+ pcm_dev->dev = &i2c->dev;
+ pcm_dev->client = i2c;
+
+ if (pcm_dev->chip_id >= MAX_DEVICE)
+ pcm_dev->chip_id = 0;
+
+ strscpy(pcm_dev->dev_name, pcmdevice_i2c_id[pcm_dev->chip_id].name,
+ sizeof(pcm_dev->dev_name));
+
+ strscpy(pcm_dev->upper_dev_name,
+ pcmdevice_i2c_id[pcm_dev->chip_id].name,
+ sizeof(pcm_dev->upper_dev_name));
+
+ str_to_upper(pcm_dev->upper_dev_name);
+
+ pcm_dev->regmap = devm_regmap_init_i2c(i2c, &pcmdevice_i2c_regmap);
+ if (IS_ERR(pcm_dev->regmap)) {
+ ret = PTR_ERR(pcm_dev->regmap);
+ dev_err(&i2c->dev, "%s: failed to allocate register map: %d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ i2c_set_clientdata(i2c, pcm_dev);
+ mutex_init(&pcm_dev->codec_lock);
+ np = pcm_dev->dev->of_node;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ u64 addr;
+
+ for (i = 0; i < PCMDEVICE_MAX_I2C_DEVICES; i++) {
+ if (of_property_read_reg(np, i, &addr, NULL))
+ break;
+ dev_addrs[ndev++] = addr;
+ }
+ } else {
+ ndev = 1;
+ dev_addrs[0] = i2c->addr;
+ }
+ pcm_dev->irq = of_irq_get(np, 0);
+
+ for (i = 0; i < ndev; i++)
+ pcm_dev->addr[i] = dev_addrs[i];
+
+ pcm_dev->ndev = ndev;
+
+ pcm_dev->hw_rst = devm_gpiod_get_optional(&i2c->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ /* No reset GPIO, no side-effect */
+ if (IS_ERR(pcm_dev->hw_rst)) {
+ if (pcm_dev->chip_id == PCM9211 || pcm_dev->chip_id == PCM1690)
+ pcm9211_sw_rst(pcm_dev);
+ else
+ pcmdevice_sw_rst(pcm_dev);
+ } else {
+ gpiod_set_value_cansleep(pcm_dev->hw_rst, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(pcm_dev->hw_rst, 1);
+ }
+
+ if (pcm_dev->chip_id == PCM1690)
+ goto skip_interrupt;
+ if (pcm_dev->irq) {
+ dev_dbg(pcm_dev->dev, "irq = %d", pcm_dev->irq);
+ } else
+ dev_err(pcm_dev->dev, "No irq provided\n");
+
+skip_interrupt:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_driver_pcmdevice, pcmdevice_dai_driver,
+ ARRAY_SIZE(pcmdevice_dai_driver));
+ if (ret < 0)
+ dev_err(&i2c->dev, "probe register comp failed %d\n", ret);
+
+out:
+ if (ret < 0)
+ pcmdevice_remove(pcm_dev);
+ return ret;
+}
+
+static void pcmdevice_i2c_remove(struct i2c_client *i2c)
+{
+ struct pcmdevice_priv *pcm_dev = i2c_get_clientdata(i2c);
+
+ pcmdevice_remove(pcm_dev);
+}
+
+static struct i2c_driver pcmdevice_i2c_driver = {
+ .driver = {
+ .name = "pcmdevice-codec",
+ .of_match_table = of_match_ptr(pcmdevice_of_match),
+ },
+ .probe = pcmdevice_i2c_probe,
+ .remove = pcmdevice_i2c_remove,
+ .id_table = pcmdevice_i2c_id,
+};
+module_i2c_driver(pcmdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>");
+MODULE_DESCRIPTION("ASoC PCM6240 Family Audio ADC/DAC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h
new file mode 100644
index 000000000000..2d8f9e798139
--- /dev/null
+++ b/sound/soc/codecs/pcm6240.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC/Router
+//
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The PCM6240 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// PCM6240 Family Audio chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#ifndef __PCM6240_H__
+#define __PCM6240_H__
+
+enum pcm_device {
+ ADC3120,
+ ADC5120,
+ ADC6120,
+ DIX4192,
+ PCM1690,
+ PCM3120,
+ PCM3140,
+ PCM5120,
+ PCM5140,
+ PCM6120,
+ PCM6140,
+ PCM6240,
+ PCM6260,
+ PCM9211,
+ PCMD3140,
+ PCMD3180,
+ PCMD512X,
+ TAA5212,
+ TAA5412,
+ TAD5212,
+ TAD5412,
+ MAX_DEVICE,
+};
+
+#define PCMDEV_GENERIC_VOL_CTRL 0x0
+#define PCMDEV_PCM1690_VOL_CTRL 0x1
+#define PCMDEV_PCM1690_FINE_VOL_CTRL 0x2
+
+/* Maximum number of I2C addresses */
+#define PCMDEVICE_MAX_I2C_DEVICES 4
+/* Maximum number defined in REGBIN protocol */
+#define PCMDEVICE_MAX_REGBIN_DEVICES 8
+#define PCMDEVICE_CONFIG_SUM 64
+#define PCMDEVICE_BIN_FILENAME_LEN 64
+
+#define PCMDEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+#define PCMDEVICE_MAX_CHANNELS 8
+#define PCMDEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* PAGE Control Register (available in page0 of each book) */
+#define PCMDEVICE_PAGE_SELECT 0x00
+#define PCMDEVICE_REG(page, reg) ((page * 128) + reg)
+#define PCMDEVICE_REG_SWRESET PCMDEVICE_REG(0X0, 0x01)
+#define PCMDEVICE_REG_SWRESET_RESET BIT(0)
+
+#define ADC5120_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define ADC5120_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define ADC5120_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define ADC5120_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+
+#define PCM1690_REG_MODE_CTRL PCMDEVICE_REG(0X0, 0x46)
+#define PCM1690_REG_MODE_CTRL_DAMS_MSK BIT(7)
+#define PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP 0x0
+#define PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE 0x80
+
+#define PCM1690_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM1690_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCM1690_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4a)
+#define PCM1690_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4b)
+#define PCM1690_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM1690_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+#define PCM1690_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4e)
+#define PCM1690_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4f)
+
+#define PCM6240_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define PCM6240_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define PCM6240_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define PCM6240_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCM6240_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47)
+#define PCM6240_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM6240_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM6240_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+
+#define PCM6260_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define PCM6260_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define PCM6260_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define PCM6260_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCM6260_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47)
+#define PCM6260_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM6260_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM6260_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+#define PCM6260_REG_CH5_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x51)
+#define PCM6260_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52)
+#define PCM6260_REG_CH6_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x56)
+#define PCM6260_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57)
+
+#define PCM9211_REG_SW_CTRL PCMDEVICE_REG(0X0, 0x40)
+#define PCM9211_REG_SW_CTRL_MRST_MSK BIT(7)
+#define PCM9211_REG_SW_CTRL_MRST 0x0
+
+#define PCM9211_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x46)
+#define PCM9211_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x47)
+
+#define PCMD3140_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E)
+#define PCMD3140_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCMD3140_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCMD3140_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D)
+
+#define PCMD3140_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x3F)
+#define PCMD3140_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x44)
+#define PCMD3140_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCMD3140_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x4E)
+
+#define PCMD3180_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E)
+#define PCMD3180_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCMD3180_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCMD3180_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D)
+#define PCMD3180_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52)
+#define PCMD3180_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57)
+#define PCMD3180_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x5C)
+#define PCMD3180_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x61)
+
+#define PCMD3180_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x3F)
+#define PCMD3180_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x44)
+#define PCMD3180_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCMD3180_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x4E)
+#define PCMD3180_REG_CH5_FINE_GAIN PCMDEVICE_REG(0X0, 0x53)
+#define PCMD3180_REG_CH6_FINE_GAIN PCMDEVICE_REG(0X0, 0x58)
+#define PCMD3180_REG_CH7_FINE_GAIN PCMDEVICE_REG(0X0, 0x5D)
+#define PCMD3180_REG_CH8_FINE_GAIN PCMDEVICE_REG(0X0, 0x62)
+
+#define TAA5412_REG_CH1_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x52)
+#define TAA5412_REG_CH2_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x57)
+#define TAA5412_REG_CH3_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5B)
+#define TAA5412_REG_CH4_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5F)
+
+#define TAA5412_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x53)
+#define TAA5412_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x58)
+#define TAA5412_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x5C)
+#define TAA5412_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x60)
+
+#define PCMDEVICE_CMD_SING_W 0x1
+#define PCMDEVICE_CMD_BURST 0x2
+#define PCMDEVICE_CMD_DELAY 0x3
+#define PCMDEVICE_CMD_FIELD_W 0x4
+
+enum pcmdevice_bin_blk_type {
+ PCMDEVICE_BIN_BLK_COEFF = 1,
+ PCMDEVICE_BIN_BLK_POST_POWER_UP,
+ PCMDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ PCMDEVICE_BIN_BLK_PRE_POWER_UP,
+ PCMDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+enum pcmdevice_fw_state {
+ PCMDEVICE_FW_LOAD_OK = 0,
+ PCMDEVICE_FW_LOAD_FAILED
+};
+
+struct pcmdevice_regbin_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned int timestamp;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[PCMDEVICE_MAX_REGBIN_DEVICES];
+ unsigned int nconfig;
+ unsigned int config_size[PCMDEVICE_CONFIG_SUM];
+};
+
+struct pcmdevice_block_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int n_subblks;
+ unsigned char *regdata;
+};
+
+struct pcmdevice_config_info {
+ char cfg_name[64];
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct pcmdevice_block_data **blk_data;
+};
+
+struct pcmdevice_regbin {
+ struct pcmdevice_regbin_hdr fw_hdr;
+ int ncfgs;
+ struct pcmdevice_config_info **cfg_info;
+};
+
+struct pcmdevice_priv {
+ struct snd_soc_component *component;
+ struct i2c_client *client;
+ struct device *dev;
+ struct mutex codec_lock;
+ struct gpio_desc *hw_rst;
+ struct regmap *regmap;
+ struct pcmdevice_regbin regbin;
+ int irq;
+ unsigned int addr[PCMDEVICE_MAX_I2C_DEVICES];
+ unsigned int chip_id;
+ int cur_conf;
+ int fw_state;
+ int ndev;
+ unsigned char bin_name[PCMDEVICE_BIN_FILENAME_LEN];
+ /* used for kcontrol name */
+ unsigned char upper_dev_name[I2C_NAME_SIZE];
+ unsigned char dev_name[I2C_NAME_SIZE];
+};
+
+/* mixer control */
+struct pcmdevice_mixer_control {
+ int max;
+ int reg;
+ unsigned int dev_no;
+ unsigned int shift;
+ unsigned int invert;
+};
+struct pcmdev_ctrl_info {
+ const unsigned int *gain;
+ const struct pcmdevice_mixer_control *pcmdev_ctrl;
+ unsigned int ctrl_array_size;
+ snd_kcontrol_get_t *get;
+ snd_kcontrol_put_t *put;
+ int pcmdev_ctrl_name_id;
+};
+#endif /* __PCM6240_H__ */
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
new file mode 100644
index 000000000000..f1ee42af264b
--- /dev/null
+++ b/sound/soc/codecs/peb2466.c
@@ -0,0 +1,2064 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// peb2466.c -- Infineon PEB2466 ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/unaligned.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PEB2466_NB_CHANNEL 4
+
+struct peb2466_lookup {
+ u8 (*table)[4];
+ unsigned int count;
+};
+
+#define PEB2466_TLV_SIZE ARRAY_SIZE(((unsigned int[]){TLV_DB_SCALE_ITEM(0, 0, 0)}))
+
+struct peb2466_lkup_ctrl {
+ int reg;
+ unsigned int index;
+ const struct peb2466_lookup *lookup;
+ unsigned int tlv_array[PEB2466_TLV_SIZE];
+};
+
+struct peb2466 {
+ struct spi_device *spi;
+ struct clk *mclk;
+ struct gpio_desc *reset_gpio;
+ u8 spi_tx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */
+ u8 spi_rx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */
+ struct regmap *regmap;
+ struct {
+ struct peb2466_lookup ax_lookup;
+ struct peb2466_lookup ar_lookup;
+ struct peb2466_lkup_ctrl ax_lkup_ctrl;
+ struct peb2466_lkup_ctrl ar_lkup_ctrl;
+ unsigned int tg1_freq_item;
+ unsigned int tg2_freq_item;
+ } ch[PEB2466_NB_CHANNEL];
+ int max_chan_playback;
+ int max_chan_capture;
+ struct {
+ struct gpio_chip gpio_chip;
+ struct mutex lock;
+ struct {
+ unsigned int xr0;
+ unsigned int xr1;
+ unsigned int xr2;
+ unsigned int xr3;
+ } cache;
+ } gpio;
+};
+
+#define PEB2466_CMD_R (1 << 5)
+#define PEB2466_CMD_W (0 << 5)
+
+#define PEB2466_CMD_MASK 0x18
+#define PEB2466_CMD_XOP 0x18 /* XOP is 0bxxx11xxx */
+#define PEB2466_CMD_SOP 0x10 /* SOP is 0bxxx10xxx */
+#define PEB2466_CMD_COP 0x00 /* COP is 0bxxx0xxxx, handle 0bxxx00xxx */
+#define PEB2466_CMD_COP1 0x08 /* COP is 0bxxx0xxxx, handle 0bxxx01xxx */
+
+#define PEB2466_MAKE_XOP(_lsel) (PEB2466_CMD_XOP | (_lsel))
+#define PEB2466_MAKE_SOP(_ad, _lsel) (PEB2466_CMD_SOP | ((_ad) << 6) | (_lsel))
+#define PEB2466_MAKE_COP(_ad, _code) (PEB2466_CMD_COP | ((_ad) << 6) | (_code))
+
+#define PEB2466_CR0(_ch) PEB2466_MAKE_SOP(_ch, 0x0)
+#define PEB2466_CR0_TH (1 << 7)
+#define PEB2466_CR0_IMR1 (1 << 6)
+#define PEB2466_CR0_FRX (1 << 5)
+#define PEB2466_CR0_FRR (1 << 4)
+#define PEB2466_CR0_AX (1 << 3)
+#define PEB2466_CR0_AR (1 << 2)
+#define PEB2466_CR0_THSEL_MASK (0x3 << 0)
+#define PEB2466_CR0_THSEL(_set) ((_set) << 0)
+
+#define PEB2466_CR1(_ch) PEB2466_MAKE_SOP(_ch, 0x1)
+#define PEB2466_CR1_ETG2 (1 << 7)
+#define PEB2466_CR1_ETG1 (1 << 6)
+#define PEB2466_CR1_PTG2 (1 << 5)
+#define PEB2466_CR1_PTG1 (1 << 4)
+#define PEB2466_CR1_LAW_MASK (1 << 3)
+#define PEB2466_CR1_LAW_ALAW (0 << 3)
+#define PEB2466_CR1_LAW_MULAW (1 << 3)
+#define PEB2466_CR1_PU (1 << 0)
+
+#define PEB2466_CR2(_ch) PEB2466_MAKE_SOP(_ch, 0x2)
+#define PEB2466_CR3(_ch) PEB2466_MAKE_SOP(_ch, 0x3)
+#define PEB2466_CR4(_ch) PEB2466_MAKE_SOP(_ch, 0x4)
+#define PEB2466_CR5(_ch) PEB2466_MAKE_SOP(_ch, 0x5)
+
+#define PEB2466_XR0 PEB2466_MAKE_XOP(0x0)
+#define PEB2466_XR1 PEB2466_MAKE_XOP(0x1)
+#define PEB2466_XR2 PEB2466_MAKE_XOP(0x2)
+#define PEB2466_XR3 PEB2466_MAKE_XOP(0x3)
+#define PEB2466_XR4 PEB2466_MAKE_XOP(0x4)
+#define PEB2466_XR5 PEB2466_MAKE_XOP(0x5)
+#define PEB2466_XR5_MCLK_1536 (0x0 << 6)
+#define PEB2466_XR5_MCLK_2048 (0x1 << 6)
+#define PEB2466_XR5_MCLK_4096 (0x2 << 6)
+#define PEB2466_XR5_MCLK_8192 (0x3 << 6)
+
+#define PEB2466_XR6 PEB2466_MAKE_XOP(0x6)
+#define PEB2466_XR6_PCM_OFFSET(_off) ((_off) << 0)
+
+#define PEB2466_XR7 PEB2466_MAKE_XOP(0x7)
+
+#define PEB2466_TH_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x0)
+#define PEB2466_TH_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x1)
+#define PEB2466_TH_FILTER_P3(_ch) PEB2466_MAKE_COP(_ch, 0x2)
+#define PEB2466_IMR1_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x4)
+#define PEB2466_IMR1_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x5)
+#define PEB2466_FRX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x6)
+#define PEB2466_FRR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x7)
+#define PEB2466_AX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x8)
+#define PEB2466_AR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x9)
+#define PEB2466_TG1(_ch) PEB2466_MAKE_COP(_ch, 0xc)
+#define PEB2466_TG2(_ch) PEB2466_MAKE_COP(_ch, 0xd)
+
+static int peb2466_write_byte(struct peb2466 *peb2466, u8 cmd, u8 val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .len = 2,
+ };
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W;
+ peb2466->spi_tx_buf[1] = val;
+
+ dev_dbg(&peb2466->spi->dev, "write byte (cmd %02x) %02x\n",
+ peb2466->spi_tx_buf[0], peb2466->spi_tx_buf[1]);
+
+ return spi_sync_transfer(peb2466->spi, &xfer, 1);
+}
+
+static int peb2466_read_byte(struct peb2466 *peb2466, u8 cmd, u8 *val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .rx_buf = &peb2466->spi_rx_buf,
+ .len = 3,
+ };
+ int ret;
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_R;
+
+ ret = spi_sync_transfer(peb2466->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ if (peb2466->spi_rx_buf[1] != 0x81) {
+ dev_err(&peb2466->spi->dev,
+ "spi xfer rd (cmd %02x) invalid ident byte (0x%02x)\n",
+ peb2466->spi_tx_buf[0], peb2466->spi_rx_buf[1]);
+ return -EILSEQ;
+ }
+
+ *val = peb2466->spi_rx_buf[2];
+
+ dev_dbg(&peb2466->spi->dev, "read byte (cmd %02x) %02x\n",
+ peb2466->spi_tx_buf[0], *val);
+
+ return 0;
+}
+
+static int peb2466_write_buf(struct peb2466 *peb2466, u8 cmd, const u8 *buf, unsigned int len)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .len = len + 1,
+ };
+
+ if (len > 8)
+ return -EINVAL;
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W;
+ memcpy(&peb2466->spi_tx_buf[1], buf, len);
+
+ dev_dbg(&peb2466->spi->dev, "write buf (cmd %02x, %u) %*ph\n",
+ peb2466->spi_tx_buf[0], len, len, &peb2466->spi_tx_buf[1]);
+
+ return spi_sync_transfer(peb2466->spi, &xfer, 1);
+}
+
+static int peb2466_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct peb2466 *peb2466 = context;
+ int ret;
+
+ /*
+ * Only XOP and SOP commands can be handled as registers.
+ * COP commands are handled using direct peb2466_write_buf() calls.
+ */
+ switch (reg & PEB2466_CMD_MASK) {
+ case PEB2466_CMD_XOP:
+ case PEB2466_CMD_SOP:
+ ret = peb2466_write_byte(peb2466, reg, val);
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int peb2466_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct peb2466 *peb2466 = context;
+ int ret;
+ u8 tmp;
+
+ /* Only XOP and SOP commands can be handled as registers */
+ switch (reg & PEB2466_CMD_MASK) {
+ case PEB2466_CMD_XOP:
+ case PEB2466_CMD_SOP:
+ ret = peb2466_read_byte(peb2466, reg, &tmp);
+ if (!ret)
+ *val = tmp;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct regmap_config peb2466_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+ .reg_write = peb2466_reg_write,
+ .reg_read = peb2466_reg_read,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int peb2466_lkup_ctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = lkup_ctrl->lookup->count - 1;
+ return 0;
+}
+
+static int peb2466_lkup_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = lkup_ctrl->index;
+ return 0;
+}
+
+static int peb2466_lkup_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ unsigned int index;
+ int ret;
+
+ index = ucontrol->value.integer.value[0];
+ if (index >= lkup_ctrl->lookup->count)
+ return -EINVAL;
+
+ if (index == lkup_ctrl->index)
+ return 0;
+
+ ret = peb2466_write_buf(peb2466, lkup_ctrl->reg,
+ lkup_ctrl->lookup->table[index], 4);
+ if (ret)
+ return ret;
+
+ lkup_ctrl->index = index;
+ return 1; /* The value changed */
+}
+
+static int peb2466_add_lkup_ctrl(struct snd_soc_component *component,
+ struct peb2466_lkup_ctrl *lkup_ctrl,
+ const char *name, int min_val, int step)
+{
+ DECLARE_TLV_DB_SCALE(tlv_array, min_val, step, 0);
+ struct snd_kcontrol_new control = {0};
+
+ BUILD_BUG_ON(sizeof(lkup_ctrl->tlv_array) < sizeof(tlv_array));
+ memcpy(lkup_ctrl->tlv_array, tlv_array, sizeof(tlv_array));
+
+ control.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ control.name = name;
+ control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ control.tlv.p = lkup_ctrl->tlv_array;
+ control.info = peb2466_lkup_ctrl_info;
+ control.get = peb2466_lkup_ctrl_get;
+ control.put = peb2466_lkup_ctrl_put;
+ control.private_value = (unsigned long)lkup_ctrl;
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+enum peb2466_tone_freq {
+ PEB2466_TONE_697HZ,
+ PEB2466_TONE_800HZ,
+ PEB2466_TONE_950HZ,
+ PEB2466_TONE_1000HZ,
+ PEB2466_TONE_1008HZ,
+ PEB2466_TONE_2000HZ,
+};
+
+static const u8 peb2466_tone_lookup[][4] = {
+ [PEB2466_TONE_697HZ] = {0x0a, 0x33, 0x5a, 0x2c},
+ [PEB2466_TONE_800HZ] = {0x12, 0xD6, 0x5a, 0xc0},
+ [PEB2466_TONE_950HZ] = {0x1c, 0xf0, 0x5c, 0xc0},
+ [PEB2466_TONE_1000HZ] = {0}, /* lookup value not used for 1000Hz */
+ [PEB2466_TONE_1008HZ] = {0x1a, 0xae, 0x57, 0x70},
+ [PEB2466_TONE_2000HZ] = {0x00, 0x80, 0x50, 0x09},
+};
+
+static const char * const peb2466_tone_freq_txt[] = {
+ [PEB2466_TONE_697HZ] = "697Hz",
+ [PEB2466_TONE_800HZ] = "800Hz",
+ [PEB2466_TONE_950HZ] = "950Hz",
+ [PEB2466_TONE_1000HZ] = "1000Hz",
+ [PEB2466_TONE_1008HZ] = "1008Hz",
+ [PEB2466_TONE_2000HZ] = "2000Hz"
+};
+
+static const struct soc_enum peb2466_tg_freq[][2] = {
+ [0] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [1] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [2] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [3] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ }
+};
+
+static int peb2466_tg_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ switch (e->reg) {
+ case PEB2466_TG1(0):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg1_freq_item;
+ break;
+ case PEB2466_TG2(0):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg2_freq_item;
+ break;
+ case PEB2466_TG1(1):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg1_freq_item;
+ break;
+ case PEB2466_TG2(1):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg2_freq_item;
+ break;
+ case PEB2466_TG1(2):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg1_freq_item;
+ break;
+ case PEB2466_TG2(2):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg2_freq_item;
+ break;
+ case PEB2466_TG1(3):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg1_freq_item;
+ break;
+ case PEB2466_TG2(3):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg2_freq_item;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int peb2466_tg_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *tg_freq_item;
+ u8 cr1_reg, cr1_mask;
+ unsigned int index;
+ int ret;
+
+ index = ucontrol->value.enumerated.item[0];
+
+ if (index >= ARRAY_SIZE(peb2466_tone_lookup))
+ return -EINVAL;
+
+ switch (e->reg) {
+ case PEB2466_TG1(0):
+ tg_freq_item = &peb2466->ch[0].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(0);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(0):
+ tg_freq_item = &peb2466->ch[0].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(0);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(1):
+ tg_freq_item = &peb2466->ch[1].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(1);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(1):
+ tg_freq_item = &peb2466->ch[1].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(1);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(2):
+ tg_freq_item = &peb2466->ch[2].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(2);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(2):
+ tg_freq_item = &peb2466->ch[2].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(2);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(3):
+ tg_freq_item = &peb2466->ch[3].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(3);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(3):
+ tg_freq_item = &peb2466->ch[3].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(3);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (index == *tg_freq_item)
+ return 0;
+
+ if (index == PEB2466_TONE_1000HZ) {
+ ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, 0);
+ if (ret)
+ return ret;
+ } else {
+ ret = peb2466_write_buf(peb2466, e->reg, peb2466_tone_lookup[index], 4);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, cr1_mask);
+ if (ret)
+ return ret;
+ }
+
+ *tg_freq_item = index;
+ return 1; /* The value changed */
+}
+
+static const struct snd_kcontrol_new peb2466_ch0_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(0), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(0), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(0), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch1_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(1), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(1), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(1), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch2_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(2), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(2), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(2), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch3_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(3), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(3), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(3), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_controls[] = {
+ /* Attenuators */
+ SOC_SINGLE("DAC0 -6dB Playback Switch", PEB2466_CR3(0), 2, 1, 0),
+ SOC_SINGLE("DAC1 -6dB Playback Switch", PEB2466_CR3(1), 2, 1, 0),
+ SOC_SINGLE("DAC2 -6dB Playback Switch", PEB2466_CR3(2), 2, 1, 0),
+ SOC_SINGLE("DAC3 -6dB Playback Switch", PEB2466_CR3(3), 2, 1, 0),
+
+ /* Amplifiers */
+ SOC_SINGLE("ADC0 +6dB Capture Switch", PEB2466_CR3(0), 3, 1, 0),
+ SOC_SINGLE("ADC1 +6dB Capture Switch", PEB2466_CR3(1), 3, 1, 0),
+ SOC_SINGLE("ADC2 +6dB Capture Switch", PEB2466_CR3(2), 3, 1, 0),
+ SOC_SINGLE("ADC3 +6dB Capture Switch", PEB2466_CR3(3), 3, 1, 0),
+
+ /* Tone generators */
+ SOC_ENUM_EXT("DAC0 TG1 Freq", peb2466_tg_freq[0][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC1 TG1 Freq", peb2466_tg_freq[1][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC2 TG1 Freq", peb2466_tg_freq[2][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC3 TG1 Freq", peb2466_tg_freq[3][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+
+ SOC_ENUM_EXT("DAC0 TG2 Freq", peb2466_tg_freq[0][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC1 TG2 Freq", peb2466_tg_freq[1][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC2 TG2 Freq", peb2466_tg_freq[2][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC3 TG2 Freq", peb2466_tg_freq[3][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+};
+
+static const struct snd_soc_dapm_widget peb2466_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("CH0 PWR", PEB2466_CR1(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH1 PWR", PEB2466_CR1(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH2 PWR", PEB2466_CR1(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH3 PWR", PEB2466_CR1(3), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("CH0 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH1 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH2 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH3 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SIGGEN("CH0 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH1 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH2 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH3 TG1"),
+
+ SND_SOC_DAPM_SIGGEN("CH0 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH1 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH2 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH3 TG2"),
+
+ SND_SOC_DAPM_MIXER("DAC0 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch0_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch0_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch1_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch1_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch2_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch2_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC3 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch3_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch3_out_mix_controls)),
+
+ SND_SOC_DAPM_PGA("DAC0 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC1 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC2 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC3 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+
+ SND_SOC_DAPM_DAC("ADC0", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC1", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC2", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC3", "Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route peb2466_dapm_routes[] = {
+ { "CH0 DIN", NULL, "CH0 PWR" },
+ { "CH1 DIN", NULL, "CH1 PWR" },
+ { "CH2 DIN", NULL, "CH2 PWR" },
+ { "CH3 DIN", NULL, "CH3 PWR" },
+
+ { "CH0 TG1", NULL, "CH0 PWR" },
+ { "CH1 TG1", NULL, "CH1 PWR" },
+ { "CH2 TG1", NULL, "CH2 PWR" },
+ { "CH3 TG1", NULL, "CH3 PWR" },
+
+ { "CH0 TG2", NULL, "CH0 PWR" },
+ { "CH1 TG2", NULL, "CH1 PWR" },
+ { "CH2 TG2", NULL, "CH2 PWR" },
+ { "CH3 TG2", NULL, "CH3 PWR" },
+
+ { "DAC0 Mixer", "TG1 Switch", "CH0 TG1" },
+ { "DAC0 Mixer", "TG2 Switch", "CH0 TG2" },
+ { "DAC0 Mixer", "Voice Switch", "CH0 DIN" },
+ { "DAC0 Mixer", NULL, "CH0 DIN" },
+
+ { "DAC1 Mixer", "TG1 Switch", "CH1 TG1" },
+ { "DAC1 Mixer", "TG2 Switch", "CH1 TG2" },
+ { "DAC1 Mixer", "Voice Switch", "CH1 DIN" },
+ { "DAC1 Mixer", NULL, "CH1 DIN" },
+
+ { "DAC2 Mixer", "TG1 Switch", "CH2 TG1" },
+ { "DAC2 Mixer", "TG2 Switch", "CH2 TG2" },
+ { "DAC2 Mixer", "Voice Switch", "CH2 DIN" },
+ { "DAC2 Mixer", NULL, "CH2 DIN" },
+
+ { "DAC3 Mixer", "TG1 Switch", "CH3 TG1" },
+ { "DAC3 Mixer", "TG2 Switch", "CH3 TG2" },
+ { "DAC3 Mixer", "Voice Switch", "CH3 DIN" },
+ { "DAC3 Mixer", NULL, "CH3 DIN" },
+
+ { "DAC0 PGA", NULL, "DAC0 Mixer" },
+ { "DAC1 PGA", NULL, "DAC1 Mixer" },
+ { "DAC2 PGA", NULL, "DAC2 Mixer" },
+ { "DAC3 PGA", NULL, "DAC3 Mixer" },
+
+ { "OUT0", NULL, "DAC0 PGA" },
+ { "OUT1", NULL, "DAC1 PGA" },
+ { "OUT2", NULL, "DAC2 PGA" },
+ { "OUT3", NULL, "DAC3 PGA" },
+
+ { "ADC0", NULL, "IN0" },
+ { "ADC1", NULL, "IN1" },
+ { "ADC2", NULL, "IN2" },
+ { "ADC3", NULL, "IN3" },
+
+ { "ADC0", NULL, "CH0 PWR" },
+ { "ADC1", NULL, "CH1 PWR" },
+ { "ADC2", NULL, "CH2 PWR" },
+ { "ADC3", NULL, "CH3 PWR" },
+};
+
+static int peb2466_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int chan;
+ unsigned int mask;
+ u8 slot;
+ int ret;
+
+ switch (width) {
+ case 0:
+ /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ mask = tx_mask;
+ slot = 0;
+ chan = 0;
+ while (mask && chan < PEB2466_NB_CHANNEL) {
+ if (mask & 0x1) {
+ ret = regmap_write(peb2466->regmap, PEB2466_CR5(chan), slot);
+ if (ret) {
+ dev_err(dai->dev, "chan %d set tx tdm slot failed (%d)\n",
+ chan, ret);
+ return ret;
+ }
+ chan++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
+ tx_mask, PEB2466_NB_CHANNEL);
+ return -EINVAL;
+ }
+ peb2466->max_chan_playback = chan;
+
+ mask = rx_mask;
+ slot = 0;
+ chan = 0;
+ while (mask && chan < PEB2466_NB_CHANNEL) {
+ if (mask & 0x1) {
+ ret = regmap_write(peb2466->regmap, PEB2466_CR4(chan), slot);
+ if (ret) {
+ dev_err(dai->dev, "chan %d set rx tdm slot failed (%d)\n",
+ chan, ret);
+ return ret;
+ }
+ chan++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
+ rx_mask, PEB2466_NB_CHANNEL);
+ return -EINVAL;
+ }
+ peb2466->max_chan_capture = chan;
+
+ return 0;
+}
+
+static int peb2466_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ u8 xr6;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ xr6 = PEB2466_XR6_PCM_OFFSET(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ xr6 = PEB2466_XR6_PCM_OFFSET(0);
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+ return regmap_write(peb2466->regmap, PEB2466_XR6, xr6);
+}
+
+static int peb2466_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ch;
+ int ret;
+ u8 cr1;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ cr1 = PEB2466_CR1_LAW_MULAW;
+ break;
+ case SNDRV_PCM_FORMAT_A_LAW:
+ cr1 = PEB2466_CR1_LAW_ALAW;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Unsupported format 0x%x\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ for (ch = 0; ch < PEB2466_NB_CHANNEL; ch++) {
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR1(ch),
+ PEB2466_CR1_LAW_MASK, cr1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const unsigned int peb2466_sample_bits[] = {8};
+
+static struct snd_pcm_hw_constraint_list peb2466_sample_bits_constr = {
+ .list = peb2466_sample_bits,
+ .count = ARRAY_SIZE(peb2466_sample_bits),
+};
+
+static int peb2466_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_ch;
+ int ret;
+
+ max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ peb2466->max_chan_playback : peb2466->max_chan_capture;
+
+ /*
+ * Disable stream support (min = 0, max = 0) if no timeslots were
+ * configured.
+ */
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ max_ch ? 1 : 0, max_ch);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &peb2466_sample_bits_constr);
+}
+
+static const u64 peb2466_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops peb2466_dai_ops = {
+ .startup = peb2466_dai_startup,
+ .hw_params = peb2466_dai_hw_params,
+ .set_tdm_slot = peb2466_dai_set_tdm_slot,
+ .set_fmt = peb2466_dai_set_fmt,
+ .auto_selectable_formats = peb2466_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats),
+};
+
+static struct snd_soc_dai_driver peb2466_dai_driver = {
+ .name = "peb2466",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = PEB2466_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = PEB2466_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .ops = &peb2466_dai_ops,
+};
+
+static int peb2466_reset_audio(struct peb2466 *peb2466)
+{
+ static const struct reg_sequence reg_reset[] = {
+ { .reg = PEB2466_XR6, .def = 0x00 },
+
+ { .reg = PEB2466_CR5(0), .def = 0x00 },
+ { .reg = PEB2466_CR4(0), .def = 0x00 },
+ { .reg = PEB2466_CR3(0), .def = 0x00 },
+ { .reg = PEB2466_CR2(0), .def = 0x00 },
+ { .reg = PEB2466_CR1(0), .def = 0x00 },
+ { .reg = PEB2466_CR0(0), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(1), .def = 0x00 },
+ { .reg = PEB2466_CR4(1), .def = 0x00 },
+ { .reg = PEB2466_CR3(1), .def = 0x00 },
+ { .reg = PEB2466_CR2(1), .def = 0x00 },
+ { .reg = PEB2466_CR1(1), .def = 0x00 },
+ { .reg = PEB2466_CR0(1), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(2), .def = 0x00 },
+ { .reg = PEB2466_CR4(2), .def = 0x00 },
+ { .reg = PEB2466_CR3(2), .def = 0x00 },
+ { .reg = PEB2466_CR2(2), .def = 0x00 },
+ { .reg = PEB2466_CR1(2), .def = 0x00 },
+ { .reg = PEB2466_CR0(2), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(3), .def = 0x00 },
+ { .reg = PEB2466_CR4(3), .def = 0x00 },
+ { .reg = PEB2466_CR3(3), .def = 0x00 },
+ { .reg = PEB2466_CR2(3), .def = 0x00 },
+ { .reg = PEB2466_CR1(3), .def = 0x00 },
+ { .reg = PEB2466_CR0(3), .def = PEB2466_CR0_IMR1 },
+ };
+ static const u8 imr1_p1[8] = {0x00, 0x90, 0x09, 0x00, 0x90, 0x09, 0x00, 0x00};
+ static const u8 imr1_p2[8] = {0x7F, 0xFF, 0x00, 0x00, 0x90, 0x14, 0x40, 0x08};
+ static const u8 zero[8] = {0};
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ peb2466->ch[i].tg1_freq_item = PEB2466_TONE_1000HZ;
+ peb2466->ch[i].tg2_freq_item = PEB2466_TONE_1000HZ;
+
+ /*
+ * Even if not used, disabling IM/R1 filter is not recommended.
+ * Instead, we must configure it with default coefficients and
+ * enable it.
+ * The filter will be enabled right after (in the following
+ * regmap_multi_reg_write() call).
+ */
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), imr1_p1, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), imr1_p2, 8);
+ if (ret)
+ return ret;
+
+ /* Set all other filters coefficients to zero */
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), zero, 4);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), zero, 4);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset));
+}
+
+static int peb2466_fw_parse_thfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw TH filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * TH_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: TH-Filter coefficients part1
+ * - @9 8 bytes: TH-Filter coefficients part2
+ * - @17 8 bytes: TH-Filter coefficients part3
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_TH, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), data + 9, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), data + 17, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_TH | PEB2466_CR0_THSEL_MASK,
+ PEB2466_CR0_TH | PEB2466_CR0_THSEL(i));
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_imr1filter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw IM/R1 filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * IMR1_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: IM/R1-Filter coefficients part1
+ * - @9 8 bytes: IM/R1-Filter coefficients part2
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_IMR1, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), data + 9, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_IMR1, PEB2466_CR0_IMR1);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_frxfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw FRX filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * FRX_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: FRX-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRX, PEB2466_CR0_FRX);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_frrfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw FRR filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * FRR_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: FRR-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRR, PEB2466_CR0_FRR);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_axfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw AX filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * AX_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 4 bytes: AX-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), data + 1, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, PEB2466_CR0_AX);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_arfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw AR filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * AR_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 4 bytes: AR-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), data + 1, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, PEB2466_CR0_AR);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char * const peb2466_ax_ctrl_names[] = {
+ "ADC0 Capture Volume",
+ "ADC1 Capture Volume",
+ "ADC2 Capture Volume",
+ "ADC3 Capture Volume",
+};
+
+static int peb2466_fw_parse_axtable(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct peb2466_lkup_ctrl *lkup_ctrl;
+ struct peb2466_lookup *lookup;
+ u8 (*table)[4];
+ u32 table_size;
+ u32 init_index;
+ s32 min_val;
+ s32 step;
+ u8 mask;
+ int ret;
+ int i;
+
+ /*
+ * AX_TABLE TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 32bits signed: Min table value in centi dB (MinVal)
+ * ie -300 means -3.0 dB
+ * - @5 32bits signed: Step from on item to other item in centi dB (Step)
+ * ie 25 means 0.25 dB)
+ * - @9 32bits unsigned: Item index in the table to use for the initial
+ * value
+ * - @13 N*4 bytes: Table composed of 4 bytes items.
+ * Each item correspond to an AX filter value.
+ *
+ * The conversion from raw value item in the table to/from the value in
+ * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB.
+ */
+
+ /* Check Lng and extract the table size. */
+ if (lng < 13 || ((lng - 13) % 4)) {
+ dev_err(component->dev, "fw AX table lng %u invalid\n", lng);
+ return -EINVAL;
+ }
+ table_size = lng - 13;
+
+ min_val = get_unaligned_be32(data + 1);
+ step = get_unaligned_be32(data + 5);
+ init_index = get_unaligned_be32(data + 9);
+ if (init_index >= (table_size / 4)) {
+ dev_err(component->dev, "fw AX table index %u out of table[%u]\n",
+ init_index, table_size / 4);
+ return -EINVAL;
+ }
+
+ dev_info(component->dev,
+ "fw AX table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n",
+ *data, min_val, step, table_size / 4, init_index,
+ 4, data + 13 + (init_index * 4));
+
+ BUILD_BUG_ON(sizeof(*table) != 4);
+ table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ memcpy(table, data + 13, table_size);
+
+ mask = *data;
+ BUILD_BUG_ON(ARRAY_SIZE(peb2466_ax_ctrl_names) != ARRAY_SIZE(peb2466->ch));
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ lookup = &peb2466->ch[i].ax_lookup;
+ lookup->table = table;
+ lookup->count = table_size / 4;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i),
+ lookup->table[init_index], 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, PEB2466_CR0_AX);
+ if (ret)
+ return ret;
+
+ lkup_ctrl = &peb2466->ch[i].ax_lkup_ctrl;
+ lkup_ctrl->lookup = lookup;
+ lkup_ctrl->reg = PEB2466_AX_FILTER(i);
+ lkup_ctrl->index = init_index;
+
+ ret = peb2466_add_lkup_ctrl(component, lkup_ctrl,
+ peb2466_ax_ctrl_names[i],
+ min_val, step);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char * const peb2466_ar_ctrl_names[] = {
+ "DAC0 Playback Volume",
+ "DAC1 Playback Volume",
+ "DAC2 Playback Volume",
+ "DAC3 Playback Volume",
+};
+
+static int peb2466_fw_parse_artable(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct peb2466_lkup_ctrl *lkup_ctrl;
+ struct peb2466_lookup *lookup;
+ u8 (*table)[4];
+ u32 table_size;
+ u32 init_index;
+ s32 min_val;
+ s32 step;
+ u8 mask;
+ int ret;
+ int i;
+
+ /*
+ * AR_TABLE TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 32bits signed: Min table value in centi dB (MinVal)
+ * ie -300 means -3.0 dB
+ * - @5 32bits signed: Step from on item to other item in centi dB (Step)
+ * ie 25 means 0.25 dB)
+ * - @9 32bits unsigned: Item index in the table to use for the initial
+ * value
+ * - @13 N*4 bytes: Table composed of 4 bytes items.
+ * Each item correspond to an AR filter value.
+ *
+ * The conversion from raw value item in the table to/from the value in
+ * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB.
+ */
+
+ /* Check Lng and extract the table size. */
+ if (lng < 13 || ((lng - 13) % 4)) {
+ dev_err(component->dev, "fw AR table lng %u invalid\n", lng);
+ return -EINVAL;
+ }
+ table_size = lng - 13;
+
+ min_val = get_unaligned_be32(data + 1);
+ step = get_unaligned_be32(data + 5);
+ init_index = get_unaligned_be32(data + 9);
+ if (init_index >= (table_size / 4)) {
+ dev_err(component->dev, "fw AR table index %u out of table[%u]\n",
+ init_index, table_size / 4);
+ return -EINVAL;
+ }
+
+ dev_info(component->dev,
+ "fw AR table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n",
+ *data, min_val, step, table_size / 4, init_index,
+ 4, data + 13 + (init_index * 4));
+
+ BUILD_BUG_ON(sizeof(*table) != 4);
+ table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ memcpy(table, data + 13, table_size);
+
+ mask = *data;
+ BUILD_BUG_ON(ARRAY_SIZE(peb2466_ar_ctrl_names) != ARRAY_SIZE(peb2466->ch));
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ lookup = &peb2466->ch[i].ar_lookup;
+ lookup->table = table;
+ lookup->count = table_size / 4;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i),
+ lookup->table[init_index], 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, PEB2466_CR0_AR);
+ if (ret)
+ return ret;
+
+ lkup_ctrl = &peb2466->ch[i].ar_lkup_ctrl;
+ lkup_ctrl->lookup = lookup;
+ lkup_ctrl->reg = PEB2466_AR_FILTER(i);
+ lkup_ctrl->index = init_index;
+
+ ret = peb2466_add_lkup_ctrl(component, lkup_ctrl,
+ peb2466_ar_ctrl_names[i],
+ min_val, step);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+struct peb2466_fw_tag_def {
+ u16 tag;
+ u32 lng_min;
+ u32 lng_max;
+ int (*parse)(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data);
+};
+
+#define PEB2466_TAG_DEF_LNG_EQ(__tag, __lng, __parse) { \
+ .tag = __tag, \
+ .lng_min = __lng, \
+ .lng_max = __lng, \
+ .parse = __parse, \
+}
+
+#define PEB2466_TAG_DEF_LNG_MIN(__tag, __lng_min, __parse) { \
+ .tag = __tag, \
+ .lng_min = __lng_min, \
+ .lng_max = U32_MAX, \
+ .parse = __parse, \
+}
+
+static const struct peb2466_fw_tag_def peb2466_fw_tag_defs[] = {
+ /* TH FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0001, 1 + 3 * 8, peb2466_fw_parse_thfilter),
+ /* IMR1 FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0002, 1 + 2 * 8, peb2466_fw_parse_imr1filter),
+ /* FRX FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0003, 1 + 8, peb2466_fw_parse_frxfilter),
+ /* FRR FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0004, 1 + 8, peb2466_fw_parse_frrfilter),
+ /* AX FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0005, 1 + 4, peb2466_fw_parse_axfilter),
+ /* AR FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0006, 1 + 4, peb2466_fw_parse_arfilter),
+ /* AX TABLE */
+ PEB2466_TAG_DEF_LNG_MIN(0x0105, 1 + 3 * 4, peb2466_fw_parse_axtable),
+ /* AR TABLE */
+ PEB2466_TAG_DEF_LNG_MIN(0x0106, 1 + 3 * 4, peb2466_fw_parse_artable),
+};
+
+static const struct peb2466_fw_tag_def *peb2466_fw_get_tag_def(u16 tag)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(peb2466_fw_tag_defs); i++) {
+ if (peb2466_fw_tag_defs[i].tag == tag)
+ return &peb2466_fw_tag_defs[i];
+ }
+ return NULL;
+}
+
+static int peb2466_fw_parse(struct snd_soc_component *component,
+ const u8 *data, size_t size)
+{
+ const struct peb2466_fw_tag_def *tag_def;
+ size_t left;
+ const u8 *buf;
+ u16 val16;
+ u16 tag;
+ u32 lng;
+ int ret;
+
+ /*
+ * Coefficients firmware binary structure (16bits and 32bits are
+ * big-endian values).
+ *
+ * @0, 16bits: Magic (0x2466)
+ * @2, 16bits: Version (0x0100 for version 1.0)
+ * @4, 2+4+N bytes: TLV block
+ * @4+(2+4+N) bytes: Next TLV block
+ * ...
+ *
+ * Detail of a TLV block:
+ * @0, 16bits: Tag
+ * @2, 32bits: Lng
+ * @6, lng bytes: Data
+ *
+ * The detail the Data for a given TLV Tag is provided in the related
+ * parser.
+ */
+
+ left = size;
+ buf = data;
+
+ if (left < 4) {
+ dev_err(component->dev, "fw size %zu, exp at least 4\n", left);
+ return -EINVAL;
+ }
+
+ /* Check magic */
+ val16 = get_unaligned_be16(buf);
+ if (val16 != 0x2466) {
+ dev_err(component->dev, "fw magic 0x%04x exp 0x2466\n", val16);
+ return -EINVAL;
+ }
+ buf += 2;
+ left -= 2;
+
+ /* Check version */
+ val16 = get_unaligned_be16(buf);
+ if (val16 != 0x0100) {
+ dev_err(component->dev, "fw magic 0x%04x exp 0x0100\n", val16);
+ return -EINVAL;
+ }
+ buf += 2;
+ left -= 2;
+
+ while (left) {
+ if (left < 6) {
+ dev_err(component->dev, "fw %td/%zu left %zu, exp at least 6\n",
+ buf - data, size, left);
+ return -EINVAL;
+ }
+ /* Check tag and lng */
+ tag = get_unaligned_be16(buf);
+ lng = get_unaligned_be32(buf + 2);
+ tag_def = peb2466_fw_get_tag_def(tag);
+ if (!tag_def) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x unknown\n",
+ buf - data, size, tag);
+ return -EINVAL;
+ }
+ if (lng < tag_def->lng_min || lng > tag_def->lng_max) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, exp [%u;%u]\n",
+ buf - data, size, tag, lng, tag_def->lng_min, tag_def->lng_max);
+ return -EINVAL;
+ }
+ buf += 6;
+ left -= 6;
+ if (left < lng) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, left %zu\n",
+ buf - data, size, tag, lng, left);
+ return -EINVAL;
+ }
+
+ /* TLV block is valid -> parse the data part */
+ ret = tag_def->parse(component, tag, lng, buf);
+ if (ret) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u parse failed\n",
+ buf - data, size, tag, lng);
+ return ret;
+ }
+
+ buf += lng;
+ left -= lng;
+ }
+ return 0;
+}
+
+static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, fw_name, component->dev);
+ if (ret)
+ return ret;
+
+ ret = peb2466_fw_parse(component, fw->data, fw->size);
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int peb2466_component_probe(struct snd_soc_component *component)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ const char *firmware_name;
+ int ret;
+
+ /* reset peb2466 audio part */
+ ret = peb2466_reset_audio(peb2466);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_string(peb2466->spi->dev.of_node,
+ "firmware-name", &firmware_name);
+ if (ret)
+ return (ret == -EINVAL) ? 0 : ret;
+
+ return peb2466_load_coeffs(component, firmware_name);
+}
+
+static const struct snd_soc_component_driver peb2466_component_driver = {
+ .probe = peb2466_component_probe,
+ .controls = peb2466_controls,
+ .num_controls = ARRAY_SIZE(peb2466_controls),
+ .dapm_widgets = peb2466_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(peb2466_dapm_widgets),
+ .dapm_routes = peb2466_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(peb2466_dapm_routes),
+ .endianness = 1,
+};
+
+/*
+ * The mapping used for the relationship between the gpio offset and the
+ * physical pin is the following:
+ *
+ * offset pin
+ * 0 SI1_0
+ * 1 SI1_1
+ * 2 SI2_0
+ * 3 SI2_1
+ * 4 SI3_0
+ * 5 SI3_1
+ * 6 SI4_0
+ * 7 SI4_1
+ * 8 SO1_0
+ * 9 SO1_1
+ * 10 SO2_0
+ * 11 SO2_1
+ * 12 SO3_0
+ * 13 SO3_1
+ * 14 SO4_0
+ * 15 SO4_1
+ * 16 SB1_0
+ * 17 SB1_1
+ * 18 SB2_0
+ * 19 SB2_1
+ * 20 SB3_0
+ * 21 SB3_1
+ * 22 SB4_0
+ * 23 SB4_1
+ * 24 SB1_2
+ * 25 SB2_2
+ * 26 SB3_2
+ * 27 SB4_2
+ */
+
+static int peb2466_chip_gpio_offset_to_data_regmask(unsigned int offset,
+ unsigned int *xr_reg,
+ unsigned int *mask)
+{
+ if (offset < 16) {
+ /*
+ * SIx_{0,1} and SOx_{0,1}
+ * Read accesses read SIx_{0,1} values
+ * Write accesses write SOx_{0,1} values
+ */
+ *xr_reg = PEB2466_XR0;
+ *mask = (1 << (offset % 8));
+ return 0;
+ }
+ if (offset < 24) {
+ /* SBx_{0,1} */
+ *xr_reg = PEB2466_XR1;
+ *mask = (1 << (offset - 16));
+ return 0;
+ }
+ if (offset < 28) {
+ /* SBx_2 */
+ *xr_reg = PEB2466_XR3;
+ *mask = (1 << (offset - 24 + 4));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int peb2466_chip_gpio_offset_to_dir_regmask(unsigned int offset,
+ unsigned int *xr_reg,
+ unsigned int *mask)
+{
+ if (offset < 16) {
+ /* Direction cannot be changed for these GPIOs */
+ return -EINVAL;
+ }
+ if (offset < 24) {
+ *xr_reg = PEB2466_XR2;
+ *mask = (1 << (offset - 16));
+ return 0;
+ }
+ if (offset < 28) {
+ *xr_reg = PEB2466_XR3;
+ *mask = (1 << (offset - 24));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static unsigned int *peb2466_chip_gpio_get_cache(struct peb2466 *peb2466,
+ unsigned int xr_reg)
+{
+ unsigned int *cache;
+
+ switch (xr_reg) {
+ case PEB2466_XR0:
+ cache = &peb2466->gpio.cache.xr0;
+ break;
+ case PEB2466_XR1:
+ cache = &peb2466->gpio.cache.xr1;
+ break;
+ case PEB2466_XR2:
+ cache = &peb2466->gpio.cache.xr2;
+ break;
+ case PEB2466_XR3:
+ cache = &peb2466->gpio.cache.xr3;
+ break;
+ default:
+ cache = NULL;
+ break;
+ }
+ return cache;
+}
+
+static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int xr_reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ unsigned int *cache;
+ int ret;
+
+ /*
+ * Read and write accesses use different peb2466 internal signals (input
+ * signals on reads and output signals on writes). regmap_update_bits
+ * cannot be used to read/modify/write the value.
+ * So, a specific cache value is used.
+ */
+
+ mutex_lock(&peb2466->gpio.lock);
+
+ cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg);
+ if (!cache) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ tmp = *cache;
+ tmp &= ~mask;
+ tmp |= val;
+
+ ret = regmap_write(peb2466->regmap, xr_reg, tmp);
+ if (ret)
+ goto end;
+
+ *cache = tmp;
+ ret = 0;
+
+end:
+ mutex_unlock(&peb2466->gpio.lock);
+ return ret;
+}
+
+static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset,
+ int val)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /*
+ * SIx_{0,1} signals cannot be set and writing the related
+ * register will change the SOx_{0,1} signals
+ */
+ dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n",
+ offset);
+ return -EINVAL;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ }
+
+ return ret;
+}
+
+static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ bool use_cache = false;
+ unsigned int *cache;
+ unsigned int xr_reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ if (offset >= 8 && offset < 16) {
+ /*
+ * SOx_{0,1} signals cannot be read. Reading the related
+ * register will read the SIx_{0,1} signals.
+ * Use the cache to get value;
+ */
+ use_cache = true;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot get gpio %d (%d)\n",
+ offset, ret);
+ return -EINVAL;
+ }
+
+ if (use_cache) {
+ cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg);
+ if (!cache)
+ return -EINVAL;
+ val = *cache;
+ } else {
+ ret = regmap_read(peb2466->regmap, xr_reg, &val);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "get gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+ }
+
+ return !!(val & mask);
+}
+
+static int peb2466_chip_get_direction(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return GPIO_LINE_DIRECTION_IN;
+ }
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return GPIO_LINE_DIRECTION_OUT;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot get gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = regmap_read(peb2466->regmap, xr_reg, &val);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "get dir gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return val & mask ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int peb2466_chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return 0;
+ }
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return -EINVAL;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, 0);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return -EINVAL;
+ }
+
+ ret = peb2466_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return 0;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peb2466_reset_gpio(struct peb2466 *peb2466)
+{
+ static const struct reg_sequence reg_reset[] = {
+ /* Output pins at 0, input/output pins as input */
+ { .reg = PEB2466_XR0, .def = 0 },
+ { .reg = PEB2466_XR1, .def = 0 },
+ { .reg = PEB2466_XR2, .def = 0 },
+ { .reg = PEB2466_XR3, .def = 0 },
+ };
+
+ peb2466->gpio.cache.xr0 = 0;
+ peb2466->gpio.cache.xr1 = 0;
+ peb2466->gpio.cache.xr2 = 0;
+ peb2466->gpio.cache.xr3 = 0;
+
+ return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset));
+}
+
+static int peb2466_gpio_init(struct peb2466 *peb2466)
+{
+ int ret;
+
+ mutex_init(&peb2466->gpio.lock);
+
+ ret = peb2466_reset_gpio(peb2466);
+ if (ret)
+ return ret;
+
+ peb2466->gpio.gpio_chip.owner = THIS_MODULE;
+ peb2466->gpio.gpio_chip.label = dev_name(&peb2466->spi->dev);
+ peb2466->gpio.gpio_chip.parent = &peb2466->spi->dev;
+ peb2466->gpio.gpio_chip.base = -1;
+ peb2466->gpio.gpio_chip.ngpio = 28;
+ peb2466->gpio.gpio_chip.get_direction = peb2466_chip_get_direction;
+ peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input;
+ peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output;
+ peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get;
+ peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set;
+ peb2466->gpio.gpio_chip.can_sleep = true;
+
+ return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip,
+ peb2466);
+}
+
+static int peb2466_spi_probe(struct spi_device *spi)
+{
+ struct peb2466 *peb2466;
+ unsigned long mclk_rate;
+ int ret;
+ u8 xr5;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ peb2466 = devm_kzalloc(&spi->dev, sizeof(*peb2466), GFP_KERNEL);
+ if (!peb2466)
+ return -ENOMEM;
+
+ peb2466->spi = spi;
+
+ peb2466->regmap = devm_regmap_init(&peb2466->spi->dev, NULL, peb2466,
+ &peb2466_regmap_config);
+ if (IS_ERR(peb2466->regmap))
+ return PTR_ERR(peb2466->regmap);
+
+ peb2466->reset_gpio = devm_gpiod_get_optional(&peb2466->spi->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(peb2466->reset_gpio))
+ return PTR_ERR(peb2466->reset_gpio);
+
+ peb2466->mclk = devm_clk_get_enabled(&peb2466->spi->dev, "mclk");
+ if (IS_ERR(peb2466->mclk))
+ return PTR_ERR(peb2466->mclk);
+
+ if (peb2466->reset_gpio) {
+ gpiod_set_value_cansleep(peb2466->reset_gpio, 1);
+ udelay(4);
+ gpiod_set_value_cansleep(peb2466->reset_gpio, 0);
+ udelay(4);
+ }
+
+ spi_set_drvdata(spi, peb2466);
+
+ mclk_rate = clk_get_rate(peb2466->mclk);
+ switch (mclk_rate) {
+ case 1536000:
+ xr5 = PEB2466_XR5_MCLK_1536;
+ break;
+ case 2048000:
+ xr5 = PEB2466_XR5_MCLK_2048;
+ break;
+ case 4096000:
+ xr5 = PEB2466_XR5_MCLK_4096;
+ break;
+ case 8192000:
+ xr5 = PEB2466_XR5_MCLK_8192;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Unsupported clock rate %lu\n",
+ mclk_rate);
+ ret = -EINVAL;
+ goto failed;
+ }
+ ret = regmap_write(peb2466->regmap, PEB2466_XR5, xr5);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Setting MCLK failed (%d)\n", ret);
+ goto failed;
+ }
+
+ ret = devm_snd_soc_register_component(&spi->dev, &peb2466_component_driver,
+ &peb2466_dai_driver, 1);
+ if (ret)
+ goto failed;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB)) {
+ ret = peb2466_gpio_init(peb2466);
+ if (ret)
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ return ret;
+}
+
+static const struct of_device_id peb2466_of_match[] = {
+ { .compatible = "infineon,peb2466", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, peb2466_of_match);
+
+static const struct spi_device_id peb2466_id_table[] = {
+ { "peb2466", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, peb2466_id_table);
+
+static struct spi_driver peb2466_spi_driver = {
+ .driver = {
+ .name = "peb2466",
+ .of_match_table = peb2466_of_match,
+ },
+ .id_table = peb2466_id_table,
+ .probe = peb2466_spi_probe,
+};
+
+module_spi_driver(peb2466_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("PEB2466 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
new file mode 100644
index 000000000000..3167b38e2876
--- /dev/null
+++ b/sound/soc/codecs/pm4125-sdw.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright, 2025 Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include "pm4125.h"
+
+static struct wcd_sdw_ch_info pm4125_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(PM4125_HPH_L, PM4125_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_HPH_R, PM4125_HPH_PORT, BIT(1)),
+};
+
+static struct wcd_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_ADC2, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(1)),
+};
+
+static struct sdw_dpn_prop pm4125_dpn_prop[PM4125_MAX_SWR_PORTS] = {
+ {
+ .num = 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 8,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 2,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *priv, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ priv->sconfig.ch_count = 1;
+ priv->active_ports = 0;
+ for (i = 0; i < PM4125_MAX_SWR_PORTS; i++) {
+ ch_mask = priv->port_config[i].ch_mask;
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ priv->sconfig.ch_count++;
+
+ port_config[priv->active_ports] = priv->port_config[i];
+ priv->active_ports++;
+ }
+
+ priv->sconfig.bps = 1;
+ priv->sconfig.frame_rate = params_rate(params);
+ priv->sconfig.direction = priv->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
+ priv->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(priv->sdev, &priv->sconfig, &port_config[0], priv->active_ports,
+ priv->sruntime);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_hw_params);
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering the first irq of the slave_irq
+ * irq domain, which then will be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int pm4125_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+
+ return wcd_interrupt_callback(slave, priv->slave_irq, PM4125_DIG_SWR_INTR_STATUS_0,
+ PM4125_DIG_SWR_INTR_STATUS_1, PM4125_DIG_SWR_INTR_STATUS_2);
+}
+
+static const struct reg_default pm4125_defaults[] = {
+ { PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01 },
+ { PM4125_ANA_MICBIAS_MICB_3_EN, 0x00 },
+ { PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x21 },
+ { PM4125_ANA_MICBIAS_LDO_1_CTRL, 0x01 },
+ { PM4125_ANA_TX_AMIC1, 0x00 },
+ { PM4125_ANA_TX_AMIC2, 0x00 },
+ { PM4125_ANA_MBHC_MECH, 0x39 },
+ { PM4125_ANA_MBHC_ELECT, 0x08 },
+ { PM4125_ANA_MBHC_ZDET, 0x10 },
+ { PM4125_ANA_MBHC_RESULT_1, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_2, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_3, 0x00 },
+ { PM4125_ANA_MBHC_BTN0_ZDET_VREF1, 0x00 },
+ { PM4125_ANA_MBHC_BTN1_ZDET_VREF2, 0x10 },
+ { PM4125_ANA_MBHC_BTN2_ZDET_VREF3, 0x20 },
+ { PM4125_ANA_MBHC_BTN3_ZDET_DBG_400, 0x30 },
+ { PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400, 0x40 },
+ { PM4125_ANA_MBHC_MICB2_RAMP, 0x00 },
+ { PM4125_ANA_MBHC_CTL_1, 0x02 },
+ { PM4125_ANA_MBHC_CTL_2, 0x05 },
+ { PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0xE9 },
+ { PM4125_ANA_MBHC_ZDET_ANA_CTL, 0x0F },
+ { PM4125_ANA_MBHC_ZDET_RAMP_CTL, 0x00 },
+ { PM4125_ANA_MBHC_FSM_STATUS, 0x00 },
+ { PM4125_ANA_MBHC_ADC_RESULT, 0x00 },
+ { PM4125_ANA_MBHC_CTL_CLK, 0x30 },
+ { PM4125_ANA_MBHC_ZDET_CALIB_RESULT, 0x00 },
+ { PM4125_ANA_NCP_EN, 0x00 },
+ { PM4125_ANA_NCP_VCTRL, 0xA7 },
+ { PM4125_ANA_HPHPA_CNP_CTL_1, 0x54 },
+ { PM4125_ANA_HPHPA_CNP_CTL_2, 0x2B },
+ { PM4125_ANA_HPHPA_PA_STATUS, 0x00 },
+ { PM4125_ANA_HPHPA_FSM_CLK, 0x12 },
+ { PM4125_ANA_HPHPA_L_GAIN, 0x00 },
+ { PM4125_ANA_HPHPA_R_GAIN, 0x00 },
+ { PM4125_SWR_HPHPA_HD2, 0x1B },
+ { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 },
+ { PM4125_ANA_SURGE_EN, 0x38 },
+ { PM4125_ANA_COMBOPA_CTL, 0x35 },
+ { PM4125_ANA_COMBOPA_CTL_4, 0x84 },
+ { PM4125_ANA_COMBOPA_CTL_5, 0x05 },
+ { PM4125_ANA_RXLDO_CTL, 0x86 },
+ { PM4125_ANA_MBIAS_EN, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID0, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID1, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID2, 0x0C },
+ { PM4125_DIG_SWR_CHIP_ID3, 0x01 },
+ { PM4125_DIG_SWR_SWR_TX_CLK_RATE, 0x00 },
+ { PM4125_DIG_SWR_CDC_RST_CTL, 0x03 },
+ { PM4125_DIG_SWR_TOP_CLK_CFG, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_CLK_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX_CLK_CTL, 0x33 },
+ { PM4125_DIG_SWR_SWR_RST_EN, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX0_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_RX1_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1, 0x00 },
+ { PM4125_DIG_SWR_CDC_COMP_CTL_0, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_DELAY_CTL, 0x66 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_0, 0x55 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_1, 0xA9 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX0_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX1_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_REQ0_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_REQ1_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_AMIC_CTL, 0x02 },
+ { PM4125_DIG_SWR_CDC_DMIC_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_RATE, 0x01 },
+ { PM4125_DIG_SWR_PDM_WD_CTL0, 0x00 },
+ { PM4125_DIG_SWR_PDM_WD_CTL1, 0x00 },
+ { PM4125_DIG_SWR_INTR_MODE, 0x00 },
+ { PM4125_DIG_SWR_INTR_MASK_0, 0xFF },
+ { PM4125_DIG_SWR_INTR_MASK_1, 0x7F },
+ { PM4125_DIG_SWR_INTR_MASK_2, 0x0C },
+ { PM4125_DIG_SWR_INTR_STATUS_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_1, 0x2A },
+ { PM4125_DIG_SWR_INTR_LEVEL_2, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX0_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX1_CTL, 0x00 },
+ { PM4125_DIG_SWR_LOOP_BACK_MODE, 0x00 },
+ { PM4125_DIG_SWR_DRIVE_STRENGTH_0, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_CTL, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_EN, 0x00 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA0, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA1, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA2, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA3, 0x01 },
+};
+
+static bool pm4125_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MICBIAS_MICB_1_2_EN:
+ case PM4125_ANA_MICBIAS_MICB_3_EN:
+ case PM4125_ANA_MICBIAS_LDO_1_SETTING:
+ case PM4125_ANA_MICBIAS_LDO_1_CTRL:
+ case PM4125_ANA_TX_AMIC1:
+ case PM4125_ANA_TX_AMIC2:
+ case PM4125_ANA_MBHC_MECH:
+ case PM4125_ANA_MBHC_ELECT:
+ case PM4125_ANA_MBHC_ZDET:
+ case PM4125_ANA_MBHC_BTN0_ZDET_VREF1:
+ case PM4125_ANA_MBHC_BTN1_ZDET_VREF2:
+ case PM4125_ANA_MBHC_BTN2_ZDET_VREF3:
+ case PM4125_ANA_MBHC_BTN3_ZDET_DBG_400:
+ case PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400:
+ case PM4125_ANA_MBHC_MICB2_RAMP:
+ case PM4125_ANA_MBHC_CTL_1:
+ case PM4125_ANA_MBHC_CTL_2:
+ case PM4125_ANA_MBHC_PLUG_DETECT_CTL:
+ case PM4125_ANA_MBHC_ZDET_ANA_CTL:
+ case PM4125_ANA_MBHC_ZDET_RAMP_CTL:
+ case PM4125_ANA_MBHC_CTL_CLK:
+ case PM4125_ANA_NCP_EN:
+ case PM4125_ANA_NCP_VCTRL:
+ case PM4125_ANA_HPHPA_CNP_CTL_1:
+ case PM4125_ANA_HPHPA_CNP_CTL_2:
+ case PM4125_ANA_HPHPA_FSM_CLK:
+ case PM4125_ANA_HPHPA_L_GAIN:
+ case PM4125_ANA_HPHPA_R_GAIN:
+ case PM4125_ANA_HPHPA_SPARE_CTL:
+ case PM4125_SWR_HPHPA_HD2:
+ case PM4125_ANA_SURGE_EN:
+ case PM4125_ANA_COMBOPA_CTL:
+ case PM4125_ANA_COMBOPA_CTL_4:
+ case PM4125_ANA_COMBOPA_CTL_5:
+ case PM4125_ANA_RXLDO_CTL:
+ case PM4125_ANA_MBIAS_EN:
+ case PM4125_DIG_SWR_SWR_TX_CLK_RATE:
+ case PM4125_DIG_SWR_CDC_RST_CTL:
+ case PM4125_DIG_SWR_TOP_CLK_CFG:
+ case PM4125_DIG_SWR_CDC_RX_CLK_CTL:
+ case PM4125_DIG_SWR_CDC_TX_CLK_CTL:
+ case PM4125_DIG_SWR_SWR_RST_EN:
+ case PM4125_DIG_SWR_CDC_RX_RST:
+ case PM4125_DIG_SWR_CDC_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_RX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1:
+ case PM4125_DIG_SWR_CDC_COMP_CTL_0:
+ case PM4125_DIG_SWR_CDC_RX_DELAY_CTL:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_0:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_1:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_CTL:
+ case PM4125_DIG_SWR_CDC_TX0_CTL:
+ case PM4125_DIG_SWR_CDC_TX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_RST:
+ case PM4125_DIG_SWR_CDC_REQ0_CTL:
+ case PM4125_DIG_SWR_CDC_REQ1_CTL:
+ case PM4125_DIG_SWR_CDC_RST:
+ case PM4125_DIG_SWR_CDC_AMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_RATE:
+ case PM4125_DIG_SWR_PDM_WD_CTL0:
+ case PM4125_DIG_SWR_PDM_WD_CTL1:
+ case PM4125_DIG_SWR_INTR_MODE:
+ case PM4125_DIG_SWR_INTR_MASK_0:
+ case PM4125_DIG_SWR_INTR_MASK_1:
+ case PM4125_DIG_SWR_INTR_MASK_2:
+ case PM4125_DIG_SWR_INTR_CLEAR_0:
+ case PM4125_DIG_SWR_INTR_CLEAR_1:
+ case PM4125_DIG_SWR_INTR_CLEAR_2:
+ case PM4125_DIG_SWR_INTR_LEVEL_0:
+ case PM4125_DIG_SWR_INTR_LEVEL_1:
+ case PM4125_DIG_SWR_INTR_LEVEL_2:
+ case PM4125_DIG_SWR_CDC_CONN_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_CONN_RX1_CTL:
+ case PM4125_DIG_SWR_LOOP_BACK_MODE:
+ case PM4125_DIG_SWR_DRIVE_STRENGTH_0:
+ case PM4125_DIG_SWR_DIG_DEBUG_CTL:
+ case PM4125_DIG_SWR_DIG_DEBUG_EN:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA0:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA1:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA2:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA3:
+ return true;
+ }
+
+ return false;
+}
+
+static bool pm4125_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+ return pm4125_rdwr_register(dev, reg);
+}
+
+static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config pm4125_regmap_config = {
+ .name = "pm4125_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = pm4125_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pm4125_defaults),
+ .max_register = PM4125_MAX_REGISTER,
+ .readable_reg = pm4125_readable_register,
+ .writeable_reg = pm4125_rdwr_register,
+ .volatile_reg = pm4125_volatile_register,
+};
+
+static const struct sdw_slave_ops pm4125_slave_ops = {
+ .update_status = wcd_update_status,
+ .interrupt_callback = pm4125_interrupt_callback,
+};
+
+static int pm4125_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct pm4125_sdw_priv *priv;
+ u8 master_ch_mask[PM4125_MAX_SWR_CH_IDS];
+ int master_ch_mask_size = 0;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Port map index starts at 0, however the data port for this codec starts at index 1 */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ priv->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_SWR_PORTS);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Error getting static port mapping for %s (%d)\n",
+ priv->is_tx ? "TX" : "RX", ret);
+
+ priv->sdev = pdev;
+ dev_set_drvdata(dev, priv);
+
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+ SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
+
+ memset(master_ch_mask, 0, PM4125_MAX_SWR_CH_IDS);
+
+ if (priv->is_tx) {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,tx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ } else {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,rx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+ if (priv->is_tx) {
+ pdev->prop.source_ports = GENMASK(PM4125_MAX_TX_SWR_PORTS, 0);
+ pdev->prop.src_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_tx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+
+ pdev->prop.wake_capable = true;
+
+ priv->regmap = devm_regmap_init_sdw(pdev, &pm4125_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap), "regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(priv->regmap, true);
+ } else {
+ pdev->prop.sink_ports = GENMASK(PM4125_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_rx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+ }
+
+ ret = component_add(dev, &wcd_sdw_component_ops);
+ if (ret)
+ return ret;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+}
+
+static int pm4125_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &wcd_sdw_component_ops);
+
+ return 0;
+}
+
+static const struct sdw_device_id pm4125_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10c, 0), /* Soundwire pm4125 RX/TX Device ID */
+ { }
+};
+MODULE_DEVICE_TABLE(sdw, pm4125_slave_id);
+
+static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, true);
+ regcache_mark_dirty(priv->regmap);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, false);
+ regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops pm4125_sdw_pm_ops = {
+ SET_RUNTIME_PM_OPS(pm4125_sdw_runtime_suspend, pm4125_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .ops = &pm4125_slave_ops,
+ .id_table = pm4125_slave_id,
+ .driver = {
+ .name = "pm4125-codec",
+ .pm = &pm4125_sdw_pm_ops,
+ }
+};
+module_sdw_driver(pm4125_codec_driver);
+
+MODULE_DESCRIPTION("PM4125 SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
new file mode 100644
index 000000000000..8bc3b9994019
--- /dev/null
+++ b/sound/soc/codecs/pm4125.c
@@ -0,0 +1,1761 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright (c) 2025, Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "pm4125.h"
+#include "wcd-mbhc-v2.h"
+
+#define WCD_MBHC_HS_V_MAX 1600
+#define PM4125_MBHC_MAX_BUTTONS 8
+
+#define PM4125_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+
+/* Fractional Rates */
+#define PM4125_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define PM4125_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Registers in SPMI addr space */
+#define PM4125_CODEC_RESET_REG 0xF3DB
+#define PM4125_CODEC_OFF 0x1
+#define PM4125_CODEC_ON 0x0
+#define PM4125_CODEC_FOUNDRY_ID_REG 0x7
+
+enum {
+ HPH_COMP_DELAY,
+ HPH_PA_DELAY,
+ AMIC2_BCS_ENABLE,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct pm4125_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct pm4125_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode;
+ struct device_node *txnode;
+ struct regmap *regmap;
+ struct regmap *spmi_regmap;
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct wcd_common common;
+ struct irq_domain *virq;
+ const struct regmap_irq_chip *chip_desc;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[PM4125_MAX_MICBIAS];
+ s32 pullup_ref[PM4125_MAX_MICBIAS];
+
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+
+ atomic_t gloal_mbias_cnt;
+};
+
+static const char * const pm4125_power_supplies[] = {
+ "vdd-io", "vdd-cp", "vdd-mic-bias", "vdd-pa-vpos",
+};
+
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static const struct wcd_mbhc_field pm4125_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, PM4125_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, PM4125_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, PM4125_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, PM4125_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, PM4125_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, PM4125_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, PM4125_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, PM4125_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, PM4125_ANA_MBHC_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, PM4125_ANA_MBHC_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, PM4125_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, PM4125_ANA_MBHC_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, PM4125_ANA_MBHC_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, PM4125_ANA_MBHC_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, PM4125_ANA_MBHC_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, PM4125_ANA_MBHC_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, PM4125_ANA_MBHC_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, PM4125_ANA_MBHC_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, PM4125_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq pm4125_irqs[PM4125_NUM_IRQS] = {
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_SW_DET, 0, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_OCP_INT, 0, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_CNP_INT, 0, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_OCP_INT, 0, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_CNP_INT, 1, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_CNP_INT, 1, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_SCD_INT, 1, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_CNP_INT, 1, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_SCD_INT, 1, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_PDM_WD_INT, 1, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_LDORT_SCD_INT, 2, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)),
+};
+
+static int pm4125_handle_post_irq(void *data)
+{
+ struct pm4125_priv *pm4125 = (struct pm4125_priv *)data;
+
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_0, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_1, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_2, 0);
+
+ return IRQ_HANDLED;
+}
+
+static const u32 pm4125_config_regs[] = {
+ PM4125_DIG_SWR_INTR_LEVEL_0,
+};
+
+static const struct regmap_irq_chip pm4125_regmap_irq_chip = {
+ .name = "pm4125",
+ .irqs = pm4125_irqs,
+ .num_irqs = ARRAY_SIZE(pm4125_irqs),
+ .num_regs = 3,
+ .status_base = PM4125_DIG_SWR_INTR_STATUS_0,
+ .mask_base = PM4125_DIG_SWR_INTR_MASK_0,
+ .ack_base = PM4125_DIG_SWR_INTR_CLEAR_0,
+ .use_ack = 1,
+ .clear_ack = 1,
+ .config_base = pm4125_config_regs,
+ .num_config_bases = ARRAY_SIZE(pm4125_config_regs),
+ .num_config_regs = 1,
+ .runtime_pm = true,
+ .handle_post_irq = pm4125_handle_post_irq,
+};
+
+static void pm4125_reset(struct pm4125_priv *pm4125)
+{
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_OFF);
+ usleep_range(20, 30);
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_ON);
+ usleep_range(5000, 5010);
+}
+
+static void pm4125_io_init(struct regmap *regmap)
+{
+ /* Disable HPH OCP */
+ regmap_update_bits(regmap, PM4125_ANA_HPHPA_CNP_CTL_2,
+ PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK | PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK,
+ PM4125_ANA_HPHPA_CNP_OCP_DISABLE);
+
+ /* Enable surge protection */
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+
+ /* Disable mic bias 2 pull down */
+ regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN,
+ PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ FIELD_PREP(PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ PM4125_ANA_MICBIAS_MICB_PULL_DISABLE));
+}
+
+static int pm4125_global_mbias_disable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_dec_and_test(&pm4125->gloal_mbias_cnt)) {
+
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ }
+
+ return 0;
+}
+
+static int pm4125_global_mbias_enable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_inc_return(&pm4125->gloal_mbias_cnt) == 1) {
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
+static int pm4125_rx_clk_enable(struct snd_soc_component *component)
+{
+ pm4125_global_mbias_enable(component);
+
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ usleep_range(5000, 5100);
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_68);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE);
+ snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL, 0x07, 0x06);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_ENABLE);
+ usleep_range(500, 510);
+
+ return 0;
+}
+
+static int pm4125_rx_clk_disable(struct snd_soc_component *component)
+{
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ 0x00);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+
+ pm4125_global_mbias_disable(component);
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_rxclk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pm4125_rx_clk_enable(component);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_rx_clk_disable(component);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_LEFT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp1_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_RIGHT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp2_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled
+ * as per HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_ear_lo_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_hphl_wdt_irq(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_wdt_irq(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX1_M | PM4125_WDT_ENABLE_RX1_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_lo_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_LO);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(2000, 2010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable BCS for Headset mic */
+ if (w->shift == 1 &&
+ !(snd_soc_component_read(component, PM4125_ANA_TX_AMIC2) & 0x10)) {
+ set_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+ }
+ pm4125_global_mbias_enable(component);
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask))
+ clear_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ 0x00);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ 0x00);
+ pm4125_global_mbias_disable(component);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 dmic_clk_reg = w->reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_DMIC1);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_AMIC3);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_micbias_control(struct snd_soc_component *component, int micb_num, int req,
+ bool is_dapm)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+ u8 pullup_mask = 0, enable_mask = 0;
+
+ if ((micb_index < 0) || (micb_index > PM4125_MAX_MICBIAS - 1)) {
+ dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK;
+ enable_mask = 0x40;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK;
+ enable_mask = 0x04;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_3_EN;
+ pullup_mask = 0x02;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ pm4125->pullup_ref[micb_index]++;
+ if ((pm4125->pullup_ref[micb_index] == 1) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (pm4125->pullup_ref[micb_index] > 0)
+ pm4125->pullup_ref[micb_index]--;
+ if ((pm4125->pullup_ref[micb_index] == 0) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, 0x00);
+ break;
+ case MICB_ENABLE:
+ pm4125->micb_ref[micb_index]++;
+ if (pm4125->micb_ref[micb_index] == 1) {
+ pm4125_global_mbias_enable(component);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, enable_mask);
+ }
+ break;
+ case MICB_DISABLE:
+ if (pm4125->micb_ref[micb_index] > 0)
+ pm4125->micb_ref[micb_index]--;
+ if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] > 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ } else if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] == 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_connect_port(struct pm4125_sdw_priv *sdw_priv, u8 port_idx, u8 ch_id, bool enable)
+{
+ struct sdw_port_config *port_config = &sdw_priv->port_config[port_idx - 1];
+ const struct wcd_sdw_ch_info *ch_info = &sdw_priv->ch_info[ch_id];
+ struct sdw_slave *sdev = sdw_priv->sdev;
+ u8 port_num = ch_info->port_num;
+ u8 ch_mask = ch_info->ch_mask;
+ u8 mstr_port_num, mstr_ch_mask;
+
+ port_config->num = port_num;
+
+ mstr_port_num = sdev->m_port_map[port_num];
+ mstr_ch_mask = ch_info->master_ch_mask;
+
+ if (enable) {
+ port_config->ch_mask |= ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+ } else {
+ port_config->ch_mask &= ~ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ ucontrol->value.integer.value[0] = hphr ? pm4125->comp2_enable : pm4125->comp1_enable;
+ return 0;
+}
+
+static int pm4125_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[AIF1_PB];
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc;
+ int portidx;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr) {
+ if (value == pm4125->comp2_enable)
+ return 0;
+
+ pm4125->comp2_enable = value;
+ } else {
+ if (value == pm4125->comp1_enable)
+ return 0;
+
+ pm4125->comp1_enable = value;
+ }
+
+ portidx = sdw_priv->ch_info[mc->reg].port_num;
+
+ pm4125_connect_port(sdw_priv, portidx, mc->reg, value ? true : false);
+
+ return 1;
+}
+
+static int pm4125_get_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ ucontrol->value.integer.value[0] = sdw_priv->port_enable[portidx];
+
+ return 0;
+}
+
+static int pm4125_set_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+ bool enable;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ enable = ucontrol->value.integer.value[0];
+
+ if (enable == sdw_priv->port_enable[portidx]) {
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+ return 0;
+ }
+
+ sdw_priv->port_enable[portidx] = enable;
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+
+ return 1;
+}
+
+static void pm4125_mbhc_bias_control(struct snd_soc_component *component, bool enable)
+{
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_ELECT,
+ PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK,
+ enable ? PM4125_ANA_MBHC_ELECT_BIAS_ENABLE :
+ PM4125_ANA_MBHC_ELECT_BIAS_DISABLE);
+}
+
+static void pm4125_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_BTN0_ZDET_VREF1 + i,
+ PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK, vth << 2);
+ }
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .mbhc_bias = pm4125_mbhc_bias_control,
+ .set_btn_thr = pm4125_mbhc_program_btn_thr,
+};
+
+static int pm4125_mbhc_init(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &pm4125->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_MBHC_SW_DET);
+
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET);
+
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET);
+
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET);
+
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_OCP_INT);
+
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_OCP_INT);
+
+ pm4125->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, pm4125_mbhc_fields, false);
+ if (IS_ERR(pm4125->wcd_mbhc))
+ return PTR_ERR(pm4125->wcd_mbhc);
+
+ return 0;
+}
+
+static void pm4125_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(pm4125->wcd_mbhc);
+}
+
+static const struct snd_kcontrol_new pm4125_snd_controls[] = {
+ SOC_SINGLE_EXT("HPHL_COMP Switch", PM4125_COMP_L, 0, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", PM4125_COMP_R, 1, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+
+ SOC_SINGLE_TLV("HPHL Volume", PM4125_ANA_HPHPA_L_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", PM4125_ANA_HPHPA_R_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", PM4125_ANA_TX_AMIC1, 0, 8, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", PM4125_ANA_TX_AMIC2, 0, 8, 0,
+ analog_gain),
+
+ SOC_SINGLE_EXT("HPHL Switch", PM4125_HPH_L, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", PM4125_HPH_R, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+
+ SOC_SINGLE_EXT("ADC1 Switch", PM4125_ADC1, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", PM4125_ADC2, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new lo_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc2_mux_text[] = {
+ "INP2", "INP3"
+};
+
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE(PM4125_ANA_TX_AMIC2, 4,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_soc_dapm_widget pm4125_dapm_widgets[] = {
+ /* Input widgets */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+ /* TX mixers */
+ SND_SOC_DAPM_MIXER("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch)),
+ SND_SOC_DAPM_MIXER("ADC2_MIXER", SND_SOC_NOPM, 1, 0, adc2_switch, ARRAY_SIZE(adc2_switch)),
+
+ /* MIC_BIAS widgets */
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* RX widgets */
+ SND_SOC_DAPM_PGA_E("EAR PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LO PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_lo_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL, 0,
+ pm4125_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL, 0,
+ pm4125_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_ear_lo_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+
+ SND_SOC_DAPM_SUPPLY("HPHL_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphl_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("HPHR_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphr_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_rxclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+ /* RX mixer widgets */
+ SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, ear_rdac_switch,
+ ARRAY_SIZE(ear_rdac_switch)),
+ SND_SOC_DAPM_MIXER("LO_RDAC", SND_SOC_NOPM, 0, 0, lo_rdac_switch,
+ ARRAY_SIZE(lo_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, hphl_rdac_switch,
+ ARRAY_SIZE(hphl_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, hphr_rdac_switch,
+ ARRAY_SIZE(hphr_rdac_switch)),
+
+ /* TX output widgets */
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+
+ /* RX output widgets */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("LO"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+
+ /* MIC_BIAS pull up widgets */
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 0, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 1, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX mixer widgets */
+ SND_SOC_DAPM_MIXER("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch,
+ ARRAY_SIZE(dmic1_switch)),
+ SND_SOC_DAPM_MIXER("DMIC2_MIXER", SND_SOC_NOPM, 1, 0, dmic2_switch,
+ ARRAY_SIZE(dmic2_switch)),
+
+ /* Output widgets */
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route pm4125_audio_map[] = {
+ { "ADC1_OUTPUT", NULL, "ADC1_MIXER" },
+ { "ADC1_MIXER", "Switch", "ADC1" },
+ { "ADC1", NULL, "AMIC1" },
+
+ { "ADC2_OUTPUT", NULL, "ADC2_MIXER" },
+ { "ADC2_MIXER", "Switch", "ADC2" },
+ { "ADC2", NULL, "ADC2 MUX" },
+ { "ADC2 MUX", "INP3", "AMIC3" },
+ { "ADC2 MUX", "INP2", "AMIC2" },
+
+ { "IN1_HPHL", NULL, "PA_VPOS" },
+ { "RX1", NULL, "IN1_HPHL" },
+ { "RX1", NULL, "RXCLK" },
+ { "RX1", NULL, "HPHL_WDT_IRQ" },
+ { "RDAC1", NULL, "RX1" },
+ { "HPHL_RDAC", "Switch", "RDAC1" },
+ { "HPHL PGA", NULL, "HPHL_RDAC" },
+ { "HPHL", NULL, "HPHL PGA" },
+
+ { "IN2_HPHR", NULL, "PA_VPOS" },
+ { "RX2", NULL, "IN2_HPHR" },
+ { "RX2", NULL, "RXCLK" },
+ { "RX2", NULL, "HPHR_WDT_IRQ" },
+ { "RDAC2", NULL, "RX2" },
+ { "HPHR_RDAC", "Switch", "RDAC2" },
+ { "HPHR PGA", NULL, "HPHR_RDAC" },
+ { "HPHR", NULL, "HPHR PGA" },
+
+ { "RDAC3", NULL, "RX1" },
+ { "EAR_RDAC", "Switch", "RDAC3" },
+ { "EAR PGA", NULL, "EAR_RDAC" },
+ { "EAR", NULL, "EAR PGA" },
+
+ { "LO_RDAC", "Switch", "RDAC3" },
+ { "LO PGA", NULL, "LO_RDAC" },
+ { "LO", NULL, "LO PGA" },
+
+ { "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" },
+ { "DMIC1_MIXER", "Switch", "DMIC1" },
+
+ { "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" },
+ { "DMIC2_MIXER", "Switch", "DMIC2" },
+};
+
+static int pm4125_set_micbias_data(struct device *dev, struct pm4125_priv *pm4125)
+{
+ regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
+ PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK, pm4125->common.micb_vout[0]);
+ return 0;
+}
+
+static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
+{
+ /*
+ * HPHR/HPHL Watchdog interrupt threaded handler
+ * Watchdog interrupts are expected to be enabled when switching on the HPHL/R
+ * in order to make sure the interrupts are acked by the regmap_irq handler
+ * io allow PDM sync. We could leave those interrupts masked but we would
+ * not haveany valid way to enable/disable them without violating irq layers.
+ *
+ * The HPHR/HPHL Watchdog interrupts are handled by regmap_irq, so requesting
+ * a threaded handler is the safest way to be able to ack those interrupts
+ * without colliding with the regmap_irq setup.
+ */
+ return IRQ_HANDLED;
+}
+
+static const struct irq_chip pm4125_codec_irq_chip = {
+ .name = "pm4125_codec",
+};
+
+static int pm4125_codec_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &pm4125_codec_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pm4125_domain_ops = {
+ .map = pm4125_codec_irq_chip_map,
+};
+
+static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev)
+{
+ pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL);
+ if (!(pm4125->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ return devm_regmap_add_irq_chip(dev, pm4125->regmap, irq_create_mapping(pm4125->virq, 0),
+ IRQF_ONESHOT, 0, pm4125->chip_desc,
+ &pm4125->irq_chip);
+}
+
+static int pm4125_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ int i, ret;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(5000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, pm4125->regmap);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ pm4125_io_init(pm4125->regmap);
+
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < pm4125_regmap_irq_chip.num_regs; i++)
+ regmap_write(pm4125->regmap, (PM4125_DIG_SWR_INTR_LEVEL_0 + i), 0);
+
+ pm_runtime_put(dev);
+
+ pm4125->hphr_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_PDM_WD_INT);
+ pm4125->hphl_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_PDM_WD_INT);
+
+ /* Request for watchdog interrupts */
+ ret = devm_request_threaded_irq(dev, pm4125->hphr_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHR wdt interrupt: %d\n", ret);
+
+ ret = devm_request_threaded_irq(dev, pm4125->hphl_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHL wdt interrupt: %d\n", ret);
+
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+
+ ret = pm4125_mbhc_init(component);
+ if (ret)
+ dev_err(component->dev, "mbhc initialization failed\n");
+
+ return ret;
+}
+
+static void pm4125_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ pm4125_mbhc_deinit(component);
+ free_irq(pm4125->hphl_pdm_wd_int, pm4125);
+ free_irq(pm4125->hphr_pdm_wd_int, pm4125);
+}
+
+static int pm4125_codec_set_jack(struct snd_soc_component *comp, struct snd_soc_jack *jack,
+ void *data)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (jack)
+ ret = wcd_mbhc_start(pm4125->wcd_mbhc, &pm4125->mbhc_cfg, jack);
+ else
+ wcd_mbhc_stop(pm4125->wcd_mbhc);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_pm4125 = {
+ .name = "pm4125_codec",
+ .probe = pm4125_soc_codec_probe,
+ .remove = pm4125_soc_codec_remove,
+ .controls = pm4125_snd_controls,
+ .num_controls = ARRAY_SIZE(pm4125_snd_controls),
+ .dapm_widgets = pm4125_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm4125_dapm_widgets),
+ .dapm_routes = pm4125_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pm4125_audio_map),
+ .set_jack = pm4125_codec_set_jack,
+ .endianness = 1,
+};
+
+static int pm4125_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return pm4125_sdw_hw_params(sdw_priv, substream, params, dai);
+}
+
+static int pm4125_codec_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return sdw_stream_remove_slave(sdw_priv->sdev, sdw_priv->sruntime);
+}
+
+static int pm4125_codec_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ sdw_priv->sruntime = stream;
+
+ return 0;
+}
+
+static int pm4125_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ rx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ tx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *tx_num = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pm4125_sdw_dai_ops = {
+ .hw_params = pm4125_codec_hw_params,
+ .hw_free = pm4125_codec_free,
+ .set_stream = pm4125_codec_set_sdw_stream,
+ .get_channel_map = pm4125_get_channel_map,
+};
+
+static struct snd_soc_dai_driver pm4125_dais[] = {
+ [0] = {
+ .name = "pm4125-sdw-rx",
+ .playback = {
+ .stream_name = "PM4125 AIF Playback",
+ .rates = PM4125_RATES | PM4125_FRAC_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+ [1] = {
+ .name = "pm4125-sdw-tx",
+ .capture = {
+ .stream_name = "PM4125 AIF Capture",
+ .rates = PM4125_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+};
+
+static int pm4125_bind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+ struct device_link *devlink;
+ int ret;
+
+ /* Initialize device pointers to NULL for safe cleanup */
+ pm4125->rxdev = NULL;
+ pm4125->txdev = NULL;
+
+ /* Give the soundwire subdevices some more time to settle */
+ usleep_range(15000, 15010);
+
+ ret = component_bind_all(dev, pm4125);
+ if (ret) {
+ dev_err(dev, "Slave bind failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ pm4125->rxdev = of_sdw_find_device_by_node(pm4125->rxnode);
+ if (!pm4125->rxdev) {
+ dev_err(dev, "could not find rxslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_PB] = dev_get_drvdata(pm4125->rxdev);
+ pm4125->sdw_priv[AIF1_PB]->pm4125 = pm4125;
+
+ pm4125->txdev = of_sdw_find_device_by_node(pm4125->txnode);
+ if (!pm4125->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_put_rx;
+ }
+
+ pm4125->sdw_priv[AIF1_CAP] = dev_get_drvdata(pm4125->txdev);
+ pm4125->sdw_priv[AIF1_CAP]->pm4125 = pm4125;
+
+ pm4125->tx_sdw_dev = dev_to_sdw_dev(pm4125->txdev);
+ if (!pm4125->tx_sdw_dev) {
+ dev_err(dev, "could not get txslave with matching of dev\n");
+ ret = -EINVAL;
+ goto error_put_tx;
+ }
+
+ /*
+ * As TX is the main CSR reg interface, which should not be suspended first.
+ * expicilty add the dependency link
+ */
+ devlink = device_link_add(pm4125->rxdev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink TX and RX\n");
+ ret = -EINVAL;
+ goto error_put_tx;
+ }
+
+ devlink = device_link_add(dev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and TX\n");
+ ret = -EINVAL;
+ goto link_remove_rx_tx;
+ }
+
+ devlink = device_link_add(dev, pm4125->rxdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and RX\n");
+ ret = -EINVAL;
+ goto link_remove_dev_tx;
+ }
+
+ pm4125->regmap = pm4125->sdw_priv[AIF1_CAP]->regmap;
+ if (!pm4125->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -EINVAL;
+ goto link_remove_dev_rx;
+ }
+
+ ret = pm4125_irq_init(pm4125, dev);
+ if (ret) {
+ dev_err(dev, "IRQ init failed: %d\n", ret);
+ goto link_remove_dev_rx;
+ }
+
+ pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
+ pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
+
+ pm4125_set_micbias_data(dev, pm4125);
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
+ pm4125_dais, ARRAY_SIZE(pm4125_dais));
+ if (!ret)
+ return ret;
+
+ dev_err(dev, "Codec registration failed\n");
+
+link_remove_dev_rx:
+ device_link_remove(dev, pm4125->rxdev);
+link_remove_dev_tx:
+ device_link_remove(dev, pm4125->txdev);
+link_remove_rx_tx:
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+error_put_tx:
+ put_device(pm4125->txdev);
+error_put_rx:
+ put_device(pm4125->rxdev);
+error_unbind_all:
+ component_unbind_all(dev, pm4125);
+ return ret;
+}
+
+static void pm4125_unbind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ devm_regmap_del_irq_chip(dev, irq_find_mapping(pm4125->virq, 0),
+ pm4125->irq_chip);
+ device_link_remove(dev, pm4125->txdev);
+ device_link_remove(dev, pm4125->rxdev);
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+
+ /* Release device references acquired in bind */
+ if (pm4125->txdev)
+ put_device(pm4125->txdev);
+ if (pm4125->rxdev)
+ put_device(pm4125->rxdev);
+
+ component_unbind_all(dev, pm4125);
+}
+
+static const struct component_master_ops pm4125_comp_ops = {
+ .bind = pm4125_bind,
+ .unbind = pm4125_unbind,
+};
+
+static int pm4125_add_slave_components(struct pm4125_priv *pm4125, struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np = dev->of_node;
+
+ pm4125->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!pm4125->rxnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,rx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->rxnode);
+
+ pm4125->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!pm4125->txnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,tx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->txnode);
+
+ return 0;
+}
+
+static int pm4125_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct device *dev = &pdev->dev;
+ struct regmap_irq_chip *chip_desc;
+ struct pm4125_priv *pm4125;
+ struct wcd_mbhc_config *cfg;
+ int ret;
+
+ pm4125 = devm_kzalloc(dev, sizeof(*pm4125), GFP_KERNEL);
+ if (!pm4125)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pm4125);
+
+ chip_desc = devm_kmemdup(dev, &pm4125_regmap_irq_chip,
+ sizeof(pm4125_regmap_irq_chip),
+ GFP_KERNEL);
+ if (!chip_desc)
+ return -ENOMEM;
+ chip_desc->irq_drv_data = pm4125;
+ pm4125->chip_desc = chip_desc;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(pm4125_power_supplies),
+ pm4125_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pm4125->spmi_regmap)
+ return -ENXIO;
+
+ pm4125_reset(pm4125);
+
+ pm4125->common.dev = dev;
+ pm4125->common.max_bias = 3;
+ ret = wcd_dt_parse_micbias_info(&pm4125->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get micbias\n");
+
+ atomic_set(&pm4125->gloal_mbias_cnt, 0);
+
+ cfg = &pm4125->mbhc_cfg;
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = PM4125_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = pm4125->common.micb_mv[1];
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
+
+ ret = pm4125_add_slave_components(pm4125, dev, &match);
+ if (ret)
+ return ret;
+
+ ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void pm4125_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_master_del(&pdev->dev, &pm4125_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct of_device_id pm4125_of_match[] = {
+ { .compatible = "qcom,pm4125-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm4125_of_match);
+
+static struct platform_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .driver = {
+ .name = "pm4125_codec",
+ .of_match_table = pm4125_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(pm4125_codec_driver);
+MODULE_DESCRIPTION("PM4125 audio codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.h b/sound/soc/codecs/pm4125.h
new file mode 100644
index 000000000000..25fd3106f44f
--- /dev/null
+++ b/sound/soc/codecs/pm4125.h
@@ -0,0 +1,293 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _PM4125_REGISTERS_H
+#define _PM4125_REGISTERS_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include "wcd-common.h"
+
+#define PM4125_ANA_BASE_ADDR 0x3000
+#define PM4125_DIG_BASE_ADDR 0x3400
+
+#define PM4125_ANA_MICBIAS_MICB_1_2_EN (PM4125_ANA_BASE_ADDR+0x040)
+#define PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK BIT(5)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK BIT(1)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK BIT(0)
+#define PM4125_ANA_MICBIAS_MICB_PULL_ENABLE 1
+#define PM4125_ANA_MICBIAS_MICB_PULL_DISABLE 0
+#define PM4125_ANA_MICBIAS_MICB_3_EN (PM4125_ANA_BASE_ADDR+0x041)
+#define PM4125_ANA_MICBIAS_LDO_1_SETTING (PM4125_ANA_BASE_ADDR+0x042)
+#define PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK GENMASK(7, 3)
+#define PM4125_ANA_MICBIAS_LDO_1_CTRL (PM4125_ANA_BASE_ADDR+0x043)
+#define PM4125_ANA_TX_AMIC1 (PM4125_ANA_BASE_ADDR+0x047)
+#define PM4125_ANA_TX_AMIC2 (PM4125_ANA_BASE_ADDR+0x048)
+#define PM4125_ANA_MBHC_MECH (PM4125_ANA_BASE_ADDR+0x05A)
+#define PM4125_ANA_MBHC_ELECT (PM4125_ANA_BASE_ADDR+0x05B)
+#define PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK BIT(0)
+#define PM4125_ANA_MBHC_ELECT_BIAS_ENABLE 1
+#define PM4125_ANA_MBHC_ELECT_BIAS_DISABLE 0
+#define PM4125_ANA_MBHC_ZDET (PM4125_ANA_BASE_ADDR+0x05C)
+#define PM4125_ANA_MBHC_RESULT_1 (PM4125_ANA_BASE_ADDR+0x05D)
+#define PM4125_ANA_MBHC_RESULT_2 (PM4125_ANA_BASE_ADDR+0x05E)
+#define PM4125_ANA_MBHC_RESULT_3 (PM4125_ANA_BASE_ADDR+0x05F)
+#define PM4125_ANA_MBHC_BTN0_ZDET_VREF1 (PM4125_ANA_BASE_ADDR+0x060)
+#define PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK GENMASK(7, 2)
+#define PM4125_ANA_MBHC_BTN1_ZDET_VREF2 (PM4125_ANA_BASE_ADDR+0x061)
+#define PM4125_ANA_MBHC_BTN2_ZDET_VREF3 (PM4125_ANA_BASE_ADDR+0x062)
+#define PM4125_ANA_MBHC_BTN3_ZDET_DBG_400 (PM4125_ANA_BASE_ADDR+0x063)
+#define PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400 (PM4125_ANA_BASE_ADDR+0x064)
+#define PM4125_ANA_MBHC_MICB2_RAMP (PM4125_ANA_BASE_ADDR+0x065)
+#define PM4125_ANA_MBHC_CTL_1 (PM4125_ANA_BASE_ADDR+0x066)
+#define PM4125_ANA_MBHC_CTL_2 (PM4125_ANA_BASE_ADDR+0x067)
+#define PM4125_ANA_MBHC_PLUG_DETECT_CTL (PM4125_ANA_BASE_ADDR+0x068)
+#define PM4125_ANA_MBHC_ZDET_ANA_CTL (PM4125_ANA_BASE_ADDR+0x069)
+#define PM4125_ANA_MBHC_ZDET_RAMP_CTL (PM4125_ANA_BASE_ADDR+0x06A)
+#define PM4125_ANA_MBHC_FSM_STATUS (PM4125_ANA_BASE_ADDR+0x06B)
+#define PM4125_ANA_MBHC_ADC_RESULT (PM4125_ANA_BASE_ADDR+0x06C)
+#define PM4125_ANA_MBHC_CTL_CLK (PM4125_ANA_BASE_ADDR+0x06D)
+#define PM4125_ANA_MBHC_ZDET_CALIB_RESULT (PM4125_ANA_BASE_ADDR+0x072)
+#define PM4125_ANA_NCP_EN (PM4125_ANA_BASE_ADDR+0x077)
+#define PM4125_ANA_NCP_ENABLE_MASK BIT(0)
+#define PM4125_ANA_NCP_ENABLE 1
+#define PM4125_ANA_NCP_DISABLE 0
+#define PM4125_ANA_NCP_VCTRL (PM4125_ANA_BASE_ADDR+0x07C)
+#define PM4125_ANA_HPHPA_CNP_CTL_1 (PM4125_ANA_BASE_ADDR+0x083)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN 1
+#define PM4125_ANA_HPHPA_CNP_CTL_2 (PM4125_ANA_BASE_ADDR+0x084)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK BIT(0)
+#define PM4125_ANA_HPHPA_CNP_OCP_ENABLE 1
+#define PM4125_ANA_HPHPA_CNP_OCP_DISABLE 0
+#define PM4125_ANA_HPHPA_PA_STATUS (PM4125_ANA_BASE_ADDR+0x087)
+#define PM4125_ANA_HPHPA_FSM_CLK (PM4125_ANA_BASE_ADDR+0x088)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK BIT(7)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE 1
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE 0
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK GENMASK(6, 0)
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_68 (0x11)
+#define PM4125_ANA_HPHPA_L_GAIN (PM4125_ANA_BASE_ADDR+0x08B)
+#define PM4125_ANA_HPHPA_R_GAIN (PM4125_ANA_BASE_ADDR+0x08C)
+#define PM4125_ANA_HPHPA_SPARE_CTL (PM4125_ANA_BASE_ADDR+0x08E)
+#define PM4125_SWR_HPHPA_HD2 (PM4125_ANA_BASE_ADDR+0x090)
+#define PM4125_SWR_HPHPA_HD2_LEFT_MASK GENMASK(5, 3)
+#define PM4125_SWR_HPHPA_HD2_RIGHT_MASK GENMASK(2, 0)
+#define PM4125_SWR_HPHPA_HD2_ENABLE (BIT(2) | BIT(1) | BIT(0))
+#define PM4125_ANA_SURGE_EN (PM4125_ANA_BASE_ADDR+0x097)
+#define PM4125_ANA_SURGE_PROTECTION_HPHL_MASK BIT(7)
+#define PM4125_ANA_SURGE_PROTECTION_HPHR_MASK BIT(6)
+#define PM4125_ANA_SURGE_PROTECTION_ENABLE 1
+#define PM4125_ANA_SURGE_PROTECTION_DISABLE 0
+#define PM4125_ANA_COMBOPA_CTL (PM4125_ANA_BASE_ADDR+0x09B)
+#define PM4125_ANA_COMBO_PA_SELECT_MASK BIT(6)
+#define PM4125_ANA_COMBO_PA_SELECT_EAR 0
+#define PM4125_ANA_COMBO_PA_SELECT_LO 1
+#define PM4125_ANA_COMBOPA_CTL_4 (PM4125_ANA_BASE_ADDR+0x09F)
+#define PM4125_ANA_COMBOPA_CTL_5 (PM4125_ANA_BASE_ADDR+0x0A0)
+#define PM4125_ANA_RXLDO_CTL (PM4125_ANA_BASE_ADDR+0x0B2)
+#define PM4125_ANA_MBIAS_EN (PM4125_ANA_BASE_ADDR+0x0B4)
+#define PM4125_ANA_MBIAS_EN_GLOBAL_MASK BIT(5)
+#define PM4125_ANA_MBIAS_EN_V2I_MASK BIT(4)
+#define PM4125_ANA_MBIAS_EN_ENABLE 1
+#define PM4125_ANA_MBIAS_EN_DISABLE 0
+
+#define PM4125_DIG_SWR_CHIP_ID0 (PM4125_DIG_BASE_ADDR+0x001)
+#define PM4125_DIG_SWR_CHIP_ID1 (PM4125_DIG_BASE_ADDR+0x002)
+#define PM4125_DIG_SWR_CHIP_ID2 (PM4125_DIG_BASE_ADDR+0x003)
+#define PM4125_DIG_SWR_CHIP_ID3 (PM4125_DIG_BASE_ADDR+0x004)
+#define PM4125_DIG_SWR_SWR_TX_CLK_RATE (PM4125_DIG_BASE_ADDR+0x040)
+#define PM4125_DIG_SWR_CDC_RST_CTL (PM4125_DIG_BASE_ADDR+0x041)
+#define PM4125_DIG_SWR_TOP_CLK_CFG (PM4125_DIG_BASE_ADDR+0x042)
+#define PM4125_DIG_SWR_CDC_RX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x043)
+#define PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK BIT(5)
+#define PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK BIT(4)
+#define PM4125_DIG_SWR_RX1_CLK_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_RX0_CLK_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_RX_CLK_ENABLE 1
+#define PM4125_DIG_SWR_RX_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_TX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x044)
+#define PM4125_DIG_SWR_SWR_RST_EN (PM4125_DIG_BASE_ADDR+0x045)
+#define PM4125_DIG_SWR_CDC_RX_RST (PM4125_DIG_BASE_ADDR+0x047)
+#define PM4125_DIG_SWR_CDC_RX0_CTL (PM4125_DIG_BASE_ADDR+0x048)
+#define PM4125_DIG_SWR_DSM_DITHER_EN_MASK BIT(7)
+#define PM4125_DIG_SWR_DSM_DITHER_DISABLE 0
+#define PM4125_DIG_SWR_DSM_DITHER_ENABLE 1
+#define PM4125_DIG_SWR_CDC_RX1_CTL (PM4125_DIG_BASE_ADDR+0x049)
+#define PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1 (PM4125_DIG_BASE_ADDR+0x04B)
+#define PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK GENMASK(7, 4)
+#define PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK GENMASK(3, 0)
+#define PM4125_DIG_SWR_TXD_MODE_ULPI (0x9)
+#define PM4125_DIG_SWR_TXD_MODE_NORMAL (0x3)
+#define PM4125_DIG_SWR_CDC_COMP_CTL_0 (PM4125_DIG_BASE_ADDR+0x04F)
+#define PM4125_DIG_SWR_COMP_HPHL_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_COMP_HPHR_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_COMP_ENABLE 1
+#define PM4125_DIG_SWR_COMP_DISABLE 0
+#define PM4125_DIG_SWR_CDC_RX_DELAY_CTL (PM4125_DIG_BASE_ADDR+0x052)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_0 (PM4125_DIG_BASE_ADDR+0x053)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_1 (PM4125_DIG_BASE_ADDR+0x054)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_CTL (PM4125_DIG_BASE_ADDR+0x057)
+#define PM4125_DIG_SWR_RX1_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_RX0_EN_MASK BIT(2)
+#define PM4125_DIG_SWR_RX_INPUT_DISABLE 0
+#define PM4125_DIG_SWR_RX_INPUT_ENABLE 1
+#define PM4125_DIG_SWR_CDC_TX0_CTL (PM4125_DIG_BASE_ADDR+0x060)
+#define PM4125_DIG_SWR_CDC_TX1_CTL (PM4125_DIG_BASE_ADDR+0x061)
+#define PM4125_DIG_SWR_CDC_TX_RST (PM4125_DIG_BASE_ADDR+0x063)
+#define PM4125_DIG_SWR_CDC_REQ0_CTL (PM4125_DIG_BASE_ADDR+0x064)
+#define PM4125_DIG_SWR_CDC_REQ1_CTL (PM4125_DIG_BASE_ADDR+0x065)
+#define PM4125_DIG_SWR_CDC_RST (PM4125_DIG_BASE_ADDR+0x067)
+#define PM4125_DIG_SWR_CDC_AMIC_CTL (PM4125_DIG_BASE_ADDR+0x06A)
+#define PM4125_DIG_SWR_AMIC_SELECT_MASK BIT(1)
+#define PM4125_DIG_SWR_AMIC_SELECT_DMIC1 0
+#define PM4125_DIG_SWR_AMIC_SELECT_AMIC3 1
+#define PM4125_DIG_SWR_CDC_DMIC_CTL (PM4125_DIG_BASE_ADDR+0x06B)
+#define PM4125_DIG_SWR_CDC_DMIC1_CTL (PM4125_DIG_BASE_ADDR+0x06C)
+#define PM4125_DIG_SWR_DMIC1_CLK_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_DMIC1_CLK_ENABLE 1
+#define PM4125_DIG_SWR_DMIC1_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_DMIC1_RATE (PM4125_DIG_BASE_ADDR+0x06D)
+#define PM4125_DIG_SWR_PDM_WD_CTL0 (PM4125_DIG_BASE_ADDR+0x070)
+#define PM4125_WDT_ENABLE_MASK GENMASK(1, 0)
+#define PM4125_WDT_ENABLE_RX0_L BIT(0)
+#define PM4125_WDT_ENABLE_RX0_M BIT(1)
+#define PM4125_DIG_SWR_PDM_WD_CTL1 (PM4125_DIG_BASE_ADDR+0x071)
+#define PM4125_WDT_ENABLE_RX1_L BIT(0)
+#define PM4125_WDT_ENABLE_RX1_M BIT(1)
+#define PM4125_DIG_SWR_INTR_MODE (PM4125_DIG_BASE_ADDR+0x080)
+#define PM4125_DIG_SWR_INTR_MASK_0 (PM4125_DIG_BASE_ADDR+0x081)
+#define PM4125_DIG_SWR_INTR_MASK_1 (PM4125_DIG_BASE_ADDR+0x082)
+#define PM4125_DIG_SWR_INTR_MASK_2 (PM4125_DIG_BASE_ADDR+0x083)
+#define PM4125_DIG_SWR_INTR_STATUS_0 (PM4125_DIG_BASE_ADDR+0x084)
+#define PM4125_DIG_SWR_INTR_STATUS_1 (PM4125_DIG_BASE_ADDR+0x085)
+#define PM4125_DIG_SWR_INTR_STATUS_2 (PM4125_DIG_BASE_ADDR+0x086)
+#define PM4125_DIG_SWR_INTR_CLEAR_0 (PM4125_DIG_BASE_ADDR+0x087)
+#define PM4125_DIG_SWR_INTR_CLEAR_1 (PM4125_DIG_BASE_ADDR+0x088)
+#define PM4125_DIG_SWR_INTR_CLEAR_2 (PM4125_DIG_BASE_ADDR+0x089)
+#define PM4125_DIG_SWR_INTR_LEVEL_0 (PM4125_DIG_BASE_ADDR+0x08A)
+#define PM4125_DIG_SWR_INTR_LEVEL_1 (PM4125_DIG_BASE_ADDR+0x08B)
+#define PM4125_DIG_SWR_INTR_LEVEL_2 (PM4125_DIG_BASE_ADDR+0x08C)
+#define PM4125_DIG_SWR_CDC_CONN_RX0_CTL (PM4125_DIG_BASE_ADDR+0x093)
+#define PM4125_DIG_SWR_CDC_CONN_RX1_CTL (PM4125_DIG_BASE_ADDR+0x094)
+#define PM4125_DIG_SWR_LOOP_BACK_MODE (PM4125_DIG_BASE_ADDR+0x097)
+#define PM4125_DIG_SWR_DRIVE_STRENGTH_0 (PM4125_DIG_BASE_ADDR+0x0A0)
+#define PM4125_DIG_SWR_DIG_DEBUG_CTL (PM4125_DIG_BASE_ADDR+0x0AB)
+#define PM4125_DIG_SWR_DIG_DEBUG_EN (PM4125_DIG_BASE_ADDR+0x0AC)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA0 (PM4125_DIG_BASE_ADDR+0x0B0)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA1 (PM4125_DIG_BASE_ADDR+0x0B1)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA2 (PM4125_DIG_BASE_ADDR+0x0B2)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA3 (PM4125_DIG_BASE_ADDR+0x0B3)
+
+#define PM4125_ANALOG_REGISTERS_MAX_SIZE (PM4125_ANA_BASE_ADDR+0x0B5)
+#define PM4125_DIGITAL_REGISTERS_MAX_SIZE (PM4125_DIG_BASE_ADDR+0x0B4)
+#define PM4125_ANALOG_MAX_REGISTER (PM4125_ANALOG_REGISTERS_MAX_SIZE - 1)
+#define PM4125_DIGITAL_MAX_REGISTER (PM4125_DIGITAL_REGISTERS_MAX_SIZE - 1)
+#define PM4125_MAX_REGISTER PM4125_DIGITAL_MAX_REGISTER
+
+#define PM4125_MAX_MICBIAS 3
+#define PM4125_MAX_SWR_CH_IDS 15
+#define PM4125_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
+
+enum pm4125_tx_sdw_ports {
+ PM4125_ADC_1_2_DMIC1L_BCS_PORT = 1,
+ PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+ PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+};
+
+enum pm4125_rx_sdw_ports {
+ PM4125_HPH_PORT = 1,
+ PM4125_COMP_PORT,
+ PM4125_MAX_SWR_PORTS = PM4125_COMP_PORT,
+};
+
+struct pm4125_priv;
+struct pm4125_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ struct wcd_sdw_ch_info *ch_info;
+ bool port_enable[PM4125_MAX_SWR_CH_IDS];
+ unsigned int master_channel_map[SDW_MAX_PORTS];
+ int active_ports;
+ int num_ports;
+ bool is_tx;
+ struct pm4125_priv *pm4125;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_PM4125_SDW)
+int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125, struct snd_soc_dai *dai, void *stream,
+ int direction);
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
+
+#else
+static inline int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
+ struct snd_soc_dai *dai, void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ PM4125_IRQ_MBHC_SW_DET,
+ PM4125_IRQ_HPHR_OCP_INT,
+ PM4125_IRQ_HPHR_CNP_INT,
+ PM4125_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ PM4125_IRQ_HPHL_CNP_INT,
+ PM4125_IRQ_EAR_CNP_INT,
+ PM4125_IRQ_EAR_SCD_INT,
+ PM4125_IRQ_AUX_CNP_INT,
+ PM4125_IRQ_AUX_SCD_INT,
+ PM4125_IRQ_HPHL_PDM_WD_INT,
+ PM4125_IRQ_HPHR_PDM_WD_INT,
+ PM4125_IRQ_AUX_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ PM4125_IRQ_LDORT_SCD_INT,
+ PM4125_IRQ_MBHC_MOISTURE_INT,
+ PM4125_IRQ_HPHL_SURGE_DET_INT,
+ PM4125_IRQ_HPHR_SURGE_DET_INT,
+ PM4125_NUM_IRQS,
+};
+
+enum pm4125_tx_sdw_channels {
+ PM4125_ADC1,
+ PM4125_ADC2,
+};
+
+enum pm4125_rx_sdw_channels {
+ PM4125_HPH_L,
+ PM4125_HPH_R,
+ PM4125_COMP_L,
+ PM4125_COMP_R,
+};
+
+#endif /* _PM4125_REGISTERS_H */
diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c
new file mode 100644
index 000000000000..60befe9d37f0
--- /dev/null
+++ b/sound/soc/codecs/rk3308_codec.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip RK3308 internal audio codec driver
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ * Copyright (c) 2024, Vivax-Metrotech Ltd
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/util_macros.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rk3308_codec.h"
+
+#define ADC_LR_GROUP_MAX 4
+
+#define GRF_CHIP_ID 0x800
+
+enum {
+ ACODEC_VERSION_A = 'A',
+ ACODEC_VERSION_B,
+ ACODEC_VERSION_C,
+};
+
+struct rk3308_codec_priv {
+ const struct device *dev;
+ struct regmap *regmap;
+ struct regmap *grf;
+ struct reset_control *reset;
+ struct clk *hclk;
+ struct clk *mclk_rx;
+ struct clk *mclk_tx;
+ struct snd_soc_component *component;
+ unsigned char codec_ver;
+};
+
+static struct clk_bulk_data rk3308_codec_clocks[] = {
+ { .id = "hclk" },
+ { .id = "mclk_rx" },
+ { .id = "mclk_tx" },
+};
+
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, -1800, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, -3900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, -600, 600, 0);
+
+static const DECLARE_TLV_DB_RANGE(rk3308_codec_dac_lineout_gain_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-300, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-150, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0),
+);
+
+static const char * const rk3308_codec_hpf_cutoff_text[] = {
+ "20 Hz", "245 Hz", "612 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum12, RK3308_ADC_DIG_CON04(0), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum34, RK3308_ADC_DIG_CON04(1), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum56, RK3308_ADC_DIG_CON04(2), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum78, RK3308_ADC_DIG_CON04(3), 0,
+ rk3308_codec_hpf_cutoff_text);
+
+static const struct snd_kcontrol_new rk3308_codec_controls[] = {
+ /* Despite the register names, these set the gain when AGC is OFF */
+ SOC_SINGLE_RANGE_TLV("MIC1 Capture Volume",
+ RK3308_ADC_ANA_CON03(0),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC2 Capture Volume",
+ RK3308_ADC_ANA_CON04(0),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC3 Capture Volume",
+ RK3308_ADC_ANA_CON03(1),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC4 Capture Volume",
+ RK3308_ADC_ANA_CON04(1),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC5 Capture Volume",
+ RK3308_ADC_ANA_CON03(2),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC6 Capture Volume",
+ RK3308_ADC_ANA_CON04(2),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC7 Capture Volume",
+ RK3308_ADC_ANA_CON03(3),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC8 Capture Volume",
+ RK3308_ADC_ANA_CON04(3),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+
+ SOC_SINGLE("MIC1 Capture Switch", RK3308_ADC_ANA_CON00(0), 3, 1, 0),
+ SOC_SINGLE("MIC2 Capture Switch", RK3308_ADC_ANA_CON00(0), 7, 1, 0),
+ SOC_SINGLE("MIC3 Capture Switch", RK3308_ADC_ANA_CON00(1), 3, 1, 0),
+ SOC_SINGLE("MIC4 Capture Switch", RK3308_ADC_ANA_CON00(1), 7, 1, 0),
+ SOC_SINGLE("MIC5 Capture Switch", RK3308_ADC_ANA_CON00(2), 3, 1, 0),
+ SOC_SINGLE("MIC6 Capture Switch", RK3308_ADC_ANA_CON00(2), 7, 1, 0),
+ SOC_SINGLE("MIC7 Capture Switch", RK3308_ADC_ANA_CON00(3), 3, 1, 0),
+ SOC_SINGLE("MIC8 Capture Switch", RK3308_ADC_ANA_CON00(3), 7, 1, 0),
+
+ SOC_SINGLE("MIC12 HPF Capture Switch", RK3308_ADC_DIG_CON04(0), 2, 1, 1),
+ SOC_SINGLE("MIC34 HPF Capture Switch", RK3308_ADC_DIG_CON04(1), 2, 1, 1),
+ SOC_SINGLE("MIC56 HPF Capture Switch", RK3308_ADC_DIG_CON04(2), 2, 1, 1),
+ SOC_SINGLE("MIC78 HPF Capture Switch", RK3308_ADC_DIG_CON04(3), 2, 1, 1),
+
+ SOC_ENUM("MIC12 HPF Cutoff", rk3308_codec_hpf_cutoff_enum12),
+ SOC_ENUM("MIC34 HPF Cutoff", rk3308_codec_hpf_cutoff_enum34),
+ SOC_ENUM("MIC56 HPF Cutoff", rk3308_codec_hpf_cutoff_enum56),
+ SOC_ENUM("MIC78 HPF Cutoff", rk3308_codec_hpf_cutoff_enum78),
+
+ SOC_DOUBLE_TLV("Line Out Playback Volume",
+ RK3308_DAC_ANA_CON04,
+ RK3308_DAC_L_LINEOUT_GAIN_SFT,
+ RK3308_DAC_R_LINEOUT_GAIN_SFT,
+ RK3308_DAC_x_LINEOUT_GAIN_MAX,
+ 0, rk3308_codec_dac_lineout_gain_tlv),
+ SOC_DOUBLE("Line Out Playback Switch",
+ RK3308_DAC_ANA_CON04,
+ RK3308_DAC_L_LINEOUT_MUTE_SFT,
+ RK3308_DAC_R_LINEOUT_MUTE_SFT, 1, 0),
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+ RK3308_DAC_ANA_CON05,
+ RK3308_DAC_ANA_CON06,
+ RK3308_DAC_x_HPOUT_GAIN_SFT,
+ RK3308_DAC_x_HPOUT_GAIN_MAX,
+ 0, rk3308_codec_dac_hpout_gain_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ RK3308_DAC_ANA_CON03,
+ RK3308_DAC_L_HPOUT_MUTE_SFT,
+ RK3308_DAC_R_HPOUT_MUTE_SFT, 1, 0),
+ SOC_DOUBLE_RANGE_TLV("DAC HPMIX Playback Volume",
+ RK3308_DAC_ANA_CON12,
+ RK3308_DAC_L_HPMIX_GAIN_SFT,
+ RK3308_DAC_R_HPMIX_GAIN_SFT,
+ 1, 2, 0, rk3308_codec_dac_hpmix_gain_tlv),
+};
+
+static int rk3308_codec_pop_sound_set(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ unsigned int val = (event == SND_SOC_DAPM_POST_PMU) ?
+ RK3308_DAC_HPOUT_POP_SOUND_x_WORK :
+ RK3308_DAC_HPOUT_POP_SOUND_x_INIT;
+ unsigned int mask = RK3308_DAC_HPOUT_POP_SOUND_x_MSK;
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01,
+ mask << w->shift, val << w->shift);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rk3308_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+ SND_SOC_DAPM_INPUT("MIC5"),
+ SND_SOC_DAPM_INPUT("MIC6"),
+ SND_SOC_DAPM_INPUT("MIC7"),
+ SND_SOC_DAPM_INPUT("MIC8"),
+
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN12", RK3308_ADC_ANA_CON06(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN34", RK3308_ADC_ANA_CON06(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN56", RK3308_ADC_ANA_CON06(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN78", RK3308_ADC_ANA_CON06(3), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC1_EN", RK3308_ADC_ANA_CON00(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC2_EN", RK3308_ADC_ANA_CON00(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC3_EN", RK3308_ADC_ANA_CON00(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC4_EN", RK3308_ADC_ANA_CON00(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC5_EN", RK3308_ADC_ANA_CON00(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC6_EN", RK3308_ADC_ANA_CON00(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC7_EN", RK3308_ADC_ANA_CON00(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC8_EN", RK3308_ADC_ANA_CON00(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC1_WORK", RK3308_ADC_ANA_CON00(0), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC2_WORK", RK3308_ADC_ANA_CON00(0), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC3_WORK", RK3308_ADC_ANA_CON00(1), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC4_WORK", RK3308_ADC_ANA_CON00(1), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC5_WORK", RK3308_ADC_ANA_CON00(2), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC6_WORK", RK3308_ADC_ANA_CON00(2), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC7_WORK", RK3308_ADC_ANA_CON00(3), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC8_WORK", RK3308_ADC_ANA_CON00(3), 6, 1, 1, 0),
+
+ /*
+ * In theory MIC1 and MIC2 can switch to LINE IN, but this is not
+ * supported so all we can do is enabling the MIC input.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "CH1_IN_SEL", RK3308_ADC_ANA_CON07(0), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "CH2_IN_SEL", RK3308_ADC_ANA_CON07(0), 6, 1, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1_BUF_REF_EN", RK3308_ADC_ANA_CON00(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2_BUF_REF_EN", RK3308_ADC_ANA_CON00(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3_BUF_REF_EN", RK3308_ADC_ANA_CON00(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC4_BUF_REF_EN", RK3308_ADC_ANA_CON00(1), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC5_BUF_REF_EN", RK3308_ADC_ANA_CON00(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC6_BUF_REF_EN", RK3308_ADC_ANA_CON00(2), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC7_BUF_REF_EN", RK3308_ADC_ANA_CON00(3), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC8_BUF_REF_EN", RK3308_ADC_ANA_CON00(3), 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC_MCLK_GATE", RK3308_GLB_CON, 5, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1_CLK_EN", RK3308_ADC_ANA_CON05(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2_CLK_EN", RK3308_ADC_ANA_CON05(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3_CLK_EN", RK3308_ADC_ANA_CON05(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC4_CLK_EN", RK3308_ADC_ANA_CON05(1), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC5_CLK_EN", RK3308_ADC_ANA_CON05(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC6_CLK_EN", RK3308_ADC_ANA_CON05(2), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC7_CLK_EN", RK3308_ADC_ANA_CON05(3), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC8_CLK_EN", RK3308_ADC_ANA_CON05(3), 4, 0, NULL, 0),
+
+ /* The "ALC" name from the TRM is misleading, these are needed even without ALC/AGC */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC1_EN", RK3308_ADC_ANA_CON02(0), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC2_EN", RK3308_ADC_ANA_CON02(0), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC3_EN", RK3308_ADC_ANA_CON02(1), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC4_EN", RK3308_ADC_ANA_CON02(1), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC5_EN", RK3308_ADC_ANA_CON02(2), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC6_EN", RK3308_ADC_ANA_CON02(2), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC7_EN", RK3308_ADC_ANA_CON02(3), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC8_EN", RK3308_ADC_ANA_CON02(3), 4, 1, 1, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1_EN", RK3308_ADC_ANA_CON05(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2_EN", RK3308_ADC_ANA_CON05(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC3_EN", RK3308_ADC_ANA_CON05(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC4_EN", RK3308_ADC_ANA_CON05(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC5_EN", RK3308_ADC_ANA_CON05(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC6_EN", RK3308_ADC_ANA_CON05(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC7_EN", RK3308_ADC_ANA_CON05(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC8_EN", RK3308_ADC_ANA_CON05(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_ADC("ADC1_WORK", "Capture", RK3308_ADC_ANA_CON05(0), 2, 0),
+ SND_SOC_DAPM_ADC("ADC2_WORK", "Capture", RK3308_ADC_ANA_CON05(0), 6, 0),
+ SND_SOC_DAPM_ADC("ADC3_WORK", "Capture", RK3308_ADC_ANA_CON05(1), 2, 0),
+ SND_SOC_DAPM_ADC("ADC4_WORK", "Capture", RK3308_ADC_ANA_CON05(1), 6, 0),
+ SND_SOC_DAPM_ADC("ADC5_WORK", "Capture", RK3308_ADC_ANA_CON05(2), 2, 0),
+ SND_SOC_DAPM_ADC("ADC6_WORK", "Capture", RK3308_ADC_ANA_CON05(2), 6, 0),
+ SND_SOC_DAPM_ADC("ADC7_WORK", "Capture", RK3308_ADC_ANA_CON05(3), 2, 0),
+ SND_SOC_DAPM_ADC("ADC8_WORK", "Capture", RK3308_ADC_ANA_CON05(3), 6, 0),
+
+ /* The "ALC" name from the TRM is misleading, these are needed even without ALC/AGC */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC1_WORK", RK3308_ADC_ANA_CON02(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC2_WORK", RK3308_ADC_ANA_CON02(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC3_WORK", RK3308_ADC_ANA_CON02(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC4_WORK", RK3308_ADC_ANA_CON02(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC5_WORK", RK3308_ADC_ANA_CON02(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC6_WORK", RK3308_ADC_ANA_CON02(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC7_WORK", RK3308_ADC_ANA_CON02(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC8_WORK", RK3308_ADC_ANA_CON02(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS Current", RK3308_ADC_ANA_CON08(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RK3308_ADC_ANA_CON07(1), 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RK3308_ADC_ANA_CON07(2), 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_EN", RK3308_DAC_ANA_CON13, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_EN", RK3308_DAC_ANA_CON13, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_WORK", RK3308_DAC_ANA_CON13, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_WORK", RK3308_DAC_ANA_CON13, 5, 0, NULL, 0),
+ /* HPMIX is not actually acting as a mixer as the only supported input is I2S */
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_SEL", RK3308_DAC_ANA_CON12, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_SEL", RK3308_DAC_ANA_CON12, 6, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DAC HPMIX Left", RK3308_DAC_ANA_CON13, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DAC HPMIX Right", RK3308_DAC_ANA_CON13, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_MCLK_GATE", RK3308_GLB_CON, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_CURRENT_EN", RK3308_DAC_ANA_CON00, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_L_REF_EN", RK3308_DAC_ANA_CON02, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_R_REF_EN", RK3308_DAC_ANA_CON02, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_L_CLK_EN", RK3308_DAC_ANA_CON02, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_R_CLK_EN", RK3308_DAC_ANA_CON02, 5, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC_L_DAC_WORK", NULL, RK3308_DAC_ANA_CON02, 3, 0),
+ SND_SOC_DAPM_DAC("DAC_R_DAC_WORK", NULL, RK3308_DAC_ANA_CON02, 7, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_BUF_REF_L", RK3308_DAC_ANA_CON01, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_BUF_REF_R", RK3308_DAC_ANA_CON01, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("HPOUT_POP_SOUND_L", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rk3308_codec_pop_sound_set,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("HPOUT_POP_SOUND_R", SND_SOC_NOPM, 4, 0, NULL, 0,
+ rk3308_codec_pop_sound_set,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV("L_HPOUT_EN", RK3308_DAC_ANA_CON03, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_HPOUT_EN", RK3308_DAC_ANA_CON03, 5, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("L_HPOUT_WORK", RK3308_DAC_ANA_CON03, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_HPOUT_WORK", RK3308_DAC_ANA_CON03, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HPOUT_L"),
+ SND_SOC_DAPM_OUTPUT("HPOUT_R"),
+
+ SND_SOC_DAPM_OUT_DRV("L_LINEOUT_EN", RK3308_DAC_ANA_CON04, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_LINEOUT_EN", RK3308_DAC_ANA_CON04, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LINEOUT_L"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT_R"),
+};
+
+static const struct snd_soc_dapm_route rk3308_codec_dapm_routes[] = {
+ { "MICBIAS1", NULL, "MICBIAS Current" },
+ { "MICBIAS2", NULL, "MICBIAS Current" },
+
+ { "MIC1_EN", NULL, "MIC1" },
+ { "MIC2_EN", NULL, "MIC2" },
+ { "MIC3_EN", NULL, "MIC3" },
+ { "MIC4_EN", NULL, "MIC4" },
+ { "MIC5_EN", NULL, "MIC5" },
+ { "MIC6_EN", NULL, "MIC6" },
+ { "MIC7_EN", NULL, "MIC7" },
+ { "MIC8_EN", NULL, "MIC8" },
+
+ { "MIC1_WORK", NULL, "MIC1_EN" },
+ { "MIC2_WORK", NULL, "MIC2_EN" },
+ { "MIC3_WORK", NULL, "MIC3_EN" },
+ { "MIC4_WORK", NULL, "MIC4_EN" },
+ { "MIC5_WORK", NULL, "MIC5_EN" },
+ { "MIC6_WORK", NULL, "MIC6_EN" },
+ { "MIC7_WORK", NULL, "MIC7_EN" },
+ { "MIC8_WORK", NULL, "MIC8_EN" },
+
+ { "CH1_IN_SEL", NULL, "MIC1_WORK" },
+ { "CH2_IN_SEL", NULL, "MIC2_WORK" },
+
+ { "ALC1_EN", NULL, "CH1_IN_SEL" },
+ { "ALC2_EN", NULL, "CH2_IN_SEL" },
+ { "ALC3_EN", NULL, "MIC3_WORK" },
+ { "ALC4_EN", NULL, "MIC4_WORK" },
+ { "ALC5_EN", NULL, "MIC5_WORK" },
+ { "ALC6_EN", NULL, "MIC6_WORK" },
+ { "ALC7_EN", NULL, "MIC7_WORK" },
+ { "ALC8_EN", NULL, "MIC8_WORK" },
+
+ { "ADC1_EN", NULL, "ALC1_EN" },
+ { "ADC2_EN", NULL, "ALC2_EN" },
+ { "ADC3_EN", NULL, "ALC3_EN" },
+ { "ADC4_EN", NULL, "ALC4_EN" },
+ { "ADC5_EN", NULL, "ALC5_EN" },
+ { "ADC6_EN", NULL, "ALC6_EN" },
+ { "ADC7_EN", NULL, "ALC7_EN" },
+ { "ADC8_EN", NULL, "ALC8_EN" },
+
+ { "ADC1_WORK", NULL, "ADC1_EN" },
+ { "ADC2_WORK", NULL, "ADC2_EN" },
+ { "ADC3_WORK", NULL, "ADC3_EN" },
+ { "ADC4_WORK", NULL, "ADC4_EN" },
+ { "ADC5_WORK", NULL, "ADC5_EN" },
+ { "ADC6_WORK", NULL, "ADC6_EN" },
+ { "ADC7_WORK", NULL, "ADC7_EN" },
+ { "ADC8_WORK", NULL, "ADC8_EN" },
+
+ { "ADC1_BUF_REF_EN", NULL, "ADC_CURRENT_EN12" },
+ { "ADC2_BUF_REF_EN", NULL, "ADC_CURRENT_EN12" },
+ { "ADC3_BUF_REF_EN", NULL, "ADC_CURRENT_EN34" },
+ { "ADC4_BUF_REF_EN", NULL, "ADC_CURRENT_EN34" },
+ { "ADC5_BUF_REF_EN", NULL, "ADC_CURRENT_EN56" },
+ { "ADC6_BUF_REF_EN", NULL, "ADC_CURRENT_EN56" },
+ { "ADC7_BUF_REF_EN", NULL, "ADC_CURRENT_EN78" },
+ { "ADC8_BUF_REF_EN", NULL, "ADC_CURRENT_EN78" },
+
+ { "ADC1_WORK", NULL, "ADC1_BUF_REF_EN" },
+ { "ADC2_WORK", NULL, "ADC2_BUF_REF_EN" },
+ { "ADC3_WORK", NULL, "ADC3_BUF_REF_EN" },
+ { "ADC4_WORK", NULL, "ADC4_BUF_REF_EN" },
+ { "ADC5_WORK", NULL, "ADC5_BUF_REF_EN" },
+ { "ADC6_WORK", NULL, "ADC6_BUF_REF_EN" },
+ { "ADC7_WORK", NULL, "ADC7_BUF_REF_EN" },
+ { "ADC8_WORK", NULL, "ADC8_BUF_REF_EN" },
+
+ { "ADC1_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC2_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC3_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC4_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC5_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC6_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC7_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC8_CLK_EN", NULL, "ADC_MCLK_GATE" },
+
+ { "ADC1_WORK", NULL, "ADC1_CLK_EN" },
+ { "ADC2_WORK", NULL, "ADC2_CLK_EN" },
+ { "ADC3_WORK", NULL, "ADC3_CLK_EN" },
+ { "ADC4_WORK", NULL, "ADC4_CLK_EN" },
+ { "ADC5_WORK", NULL, "ADC5_CLK_EN" },
+ { "ADC6_WORK", NULL, "ADC6_CLK_EN" },
+ { "ADC7_WORK", NULL, "ADC7_CLK_EN" },
+ { "ADC8_WORK", NULL, "ADC8_CLK_EN" },
+
+ { "ALC1_WORK", NULL, "ADC1_WORK" },
+ { "ALC2_WORK", NULL, "ADC2_WORK" },
+ { "ALC3_WORK", NULL, "ADC3_WORK" },
+ { "ALC4_WORK", NULL, "ADC4_WORK" },
+ { "ALC5_WORK", NULL, "ADC5_WORK" },
+ { "ALC6_WORK", NULL, "ADC6_WORK" },
+ { "ALC7_WORK", NULL, "ADC7_WORK" },
+ { "ALC8_WORK", NULL, "ADC8_WORK" },
+
+ { "HiFi Capture", NULL, "ALC1_WORK" },
+ { "HiFi Capture", NULL, "ALC2_WORK" },
+ { "HiFi Capture", NULL, "ALC3_WORK" },
+ { "HiFi Capture", NULL, "ALC4_WORK" },
+ { "HiFi Capture", NULL, "ALC5_WORK" },
+ { "HiFi Capture", NULL, "ALC6_WORK" },
+ { "HiFi Capture", NULL, "ALC7_WORK" },
+ { "HiFi Capture", NULL, "ALC8_WORK" },
+
+ { "DAC_L_HPMIX_EN", NULL, "HiFi Playback" },
+ { "DAC_R_HPMIX_EN", NULL, "HiFi Playback" },
+ { "DAC_L_HPMIX_WORK", NULL, "DAC_L_HPMIX_EN" },
+ { "DAC_R_HPMIX_WORK", NULL, "DAC_R_HPMIX_EN" },
+ { "DAC HPMIX Left", NULL, "DAC_L_HPMIX_WORK" },
+ { "DAC HPMIX Right", NULL, "DAC_R_HPMIX_WORK" },
+
+ { "DAC_L_DAC_WORK", NULL, "DAC HPMIX Left" },
+ { "DAC_R_DAC_WORK", NULL, "DAC HPMIX Right" },
+
+ { "DAC_L_REF_EN", NULL, "DAC_CURRENT_EN" },
+ { "DAC_R_REF_EN", NULL, "DAC_CURRENT_EN" },
+ { "DAC_L_CLK_EN", NULL, "DAC_L_REF_EN" },
+ { "DAC_R_CLK_EN", NULL, "DAC_R_REF_EN" },
+ { "DAC_L_CLK_EN", NULL, "DAC_MCLK_GATE" },
+ { "DAC_R_CLK_EN", NULL, "DAC_MCLK_GATE" },
+ { "DAC_L_DAC_WORK", NULL, "DAC_L_CLK_EN" },
+ { "DAC_R_DAC_WORK", NULL, "DAC_R_CLK_EN" },
+ { "DAC_L_HPMIX_SEL", NULL, "DAC_L_DAC_WORK" },
+ { "DAC_R_HPMIX_SEL", NULL, "DAC_R_DAC_WORK" },
+
+ { "HPOUT_L", NULL, "DAC_BUF_REF_L" },
+ { "HPOUT_R", NULL, "DAC_BUF_REF_R" },
+ { "L_HPOUT_EN", NULL, "DAC_L_HPMIX_SEL" },
+ { "R_HPOUT_EN", NULL, "DAC_R_HPMIX_SEL" },
+ { "L_HPOUT_WORK", NULL, "L_HPOUT_EN" },
+ { "R_HPOUT_WORK", NULL, "R_HPOUT_EN" },
+ { "HPOUT_POP_SOUND_L", NULL, "L_HPOUT_WORK" },
+ { "HPOUT_POP_SOUND_R", NULL, "R_HPOUT_WORK" },
+ { "HPOUT_L", NULL, "HPOUT_POP_SOUND_L" },
+ { "HPOUT_R", NULL, "HPOUT_POP_SOUND_R" },
+
+ { "L_LINEOUT_EN", NULL, "DAC_L_HPMIX_SEL" },
+ { "R_LINEOUT_EN", NULL, "DAC_R_HPMIX_SEL" },
+ { "LINEOUT_L", NULL, "L_LINEOUT_EN" },
+ { "LINEOUT_R", NULL, "R_LINEOUT_EN" },
+};
+
+static int rk3308_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ const unsigned int inv_bits = fmt & SND_SOC_DAIFMT_INV_MASK;
+ const bool inv_bitclk =
+ (inv_bits & SND_SOC_DAIFMT_IB_IF) ||
+ (inv_bits & SND_SOC_DAIFMT_IB_NF);
+ const bool inv_frmclk =
+ (inv_bits & SND_SOC_DAIFMT_IB_IF) ||
+ (inv_bits & SND_SOC_DAIFMT_NB_IF);
+ const unsigned int dac_master_bits = rk3308->codec_ver < ACODEC_VERSION_C ?
+ RK3308_DAC_IO_MODE_MASTER | RK3308_DAC_MODE_MASTER :
+ RK3308BS_DAC_IO_MODE_MASTER | RK3308BS_DAC_MODE_MASTER;
+ unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0;
+ bool is_master = false;
+ int grp;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adc_aif2 |= RK3308_ADC_IO_MODE_MASTER;
+ adc_aif2 |= RK3308_ADC_MODE_MASTER;
+ dac_aif2 |= dac_master_bits;
+ is_master = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_PCM;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_I2S;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_RJ;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_RJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_LJ;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_LJ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (inv_bitclk) {
+ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL;
+ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL;
+ }
+
+ if (inv_frmclk) {
+ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL;
+ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL;
+ }
+
+ /*
+ * Hold ADC Digital registers start at master mode
+ *
+ * There are 8 ADCs which use the same internal SCLK and LRCK for
+ * master mode. We need to make sure that they are in effect at the
+ * same time, otherwise they will cause abnormal clocks.
+ */
+ if (is_master)
+ regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK);
+
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) {
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp),
+ RK3308_ADC_I2S_LRC_POL_REVERSAL |
+ RK3308_ADC_I2S_MODE_MSK,
+ adc_aif1);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp),
+ RK3308_ADC_IO_MODE_MASTER |
+ RK3308_ADC_MODE_MASTER |
+ RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL,
+ adc_aif2);
+ }
+
+ /* Hold ADC Digital registers end at master mode */
+ if (is_master)
+ regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK);
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01,
+ RK3308_DAC_I2S_LRC_POL_REVERSAL |
+ RK3308_DAC_I2S_MODE_MSK,
+ dac_aif1);
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02,
+ dac_master_bits | RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL,
+ dac_aif2);
+
+ return 0;
+}
+
+static int rk3308_codec_dac_dig_config(struct rk3308_codec_priv *rk3308,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int dac_aif1 = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01,
+ RK3308_DAC_I2S_VALID_LEN_MSK, dac_aif1);
+ regmap_set_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, RK3308_DAC_I2S_WORK);
+
+ return 0;
+}
+
+static int rk3308_codec_adc_dig_config(struct rk3308_codec_priv *rk3308,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int adc_aif1 = 0;
+ /*
+ * grp 0 = ADC1 and ADC2
+ * grp 1 = ADC3 and ADC4
+ * grp 2 = ADC5 and ADC6
+ * grp 3 = ADC7 and ADC8
+ */
+ u32 used_adc_grps;
+ int grp;
+
+ switch (params_channels(params)) {
+ case 1:
+ adc_aif1 |= RK3308_ADC_I2S_MONO;
+ used_adc_grps = 1;
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ used_adc_grps = params_channels(params) / 2;
+ break;
+ default:
+ dev_err(rk3308->dev, "Invalid channel number %d\n", params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (grp = 0; grp < used_adc_grps; grp++) {
+ regmap_update_bits(rk3308->regmap,
+ RK3308_ADC_DIG_CON03(grp),
+ RK3308_ADC_L_CH_BIST_MSK | RK3308_ADC_R_CH_BIST_MSK,
+ RK3308_ADC_L_CH_NORMAL_LEFT | RK3308_ADC_R_CH_NORMAL_RIGHT);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp),
+ RK3308_ADC_I2S_VALID_LEN_MSK | RK3308_ADC_I2S_MONO, adc_aif1);
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), RK3308_ADC_I2S_WORK);
+ }
+
+ return 0;
+}
+
+static int rk3308_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ return (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ rk3308_codec_dac_dig_config(rk3308, params) :
+ rk3308_codec_adc_dig_config(rk3308, params);
+}
+
+static const struct snd_soc_dai_ops rk3308_codec_dai_ops = {
+ .hw_params = rk3308_codec_hw_params,
+ .set_fmt = rk3308_codec_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rk3308_codec_dai_driver = {
+ .name = "rk3308-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .ops = &rk3308_codec_dai_ops,
+};
+
+static void rk3308_codec_reset(struct snd_soc_component *component)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ reset_control_assert(rk3308->reset);
+ usleep_range(10000, 11000); /* estimated value */
+ reset_control_deassert(rk3308->reset);
+
+ regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00);
+ usleep_range(10000, 11000); /* estimated value */
+ regmap_write(rk3308->regmap, RK3308_GLB_CON,
+ RK3308_SYS_WORK |
+ RK3308_DAC_DIG_WORK |
+ RK3308_ADC_DIG_WORK);
+}
+
+/*
+ * Initialize register whose default after HW reset is problematic or which
+ * are never modified.
+ */
+static int rk3308_codec_initialize(struct rk3308_codec_priv *rk3308)
+{
+ int grp;
+
+ /*
+ * Init ADC digital vol to 0 dB (reset value is 0xff, undocumented).
+ * Range: -97dB ~ +32dB.
+ */
+ if (rk3308->codec_ver == ACODEC_VERSION_C) {
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) {
+ regmap_write(rk3308->regmap, RK3308_ADC_DIG_CON05(grp),
+ RK3308_ADC_DIG_VOL_CON_x_0DB);
+ regmap_write(rk3308->regmap, RK3308_ADC_DIG_CON06(grp),
+ RK3308_ADC_DIG_VOL_CON_x_0DB);
+ }
+ }
+
+ /* set HPMIX default gains (reset value is 0, which is illegal) */
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12,
+ RK3308_DAC_L_HPMIX_GAIN_MSK |
+ RK3308_DAC_R_HPMIX_GAIN_MSK,
+ RK3308_DAC_L_HPMIX_GAIN_NDB_6 |
+ RK3308_DAC_R_HPMIX_GAIN_NDB_6);
+
+ /* recover DAC digital gain to 0 dB (reset value is 0xff, undocumented) */
+ if (rk3308->codec_ver == ACODEC_VERSION_C)
+ regmap_write(rk3308->regmap, RK3308_DAC_DIG_CON04,
+ RK3308BS_DAC_DIG_GAIN_0DB);
+
+ /*
+ * Unconditionally enable zero-cross detection (needed for AGC,
+ * harmless without AGC)
+ */
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++)
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp),
+ RK3308_ADC_CH1_ZEROCROSS_DET_EN |
+ RK3308_ADC_CH2_ZEROCROSS_DET_EN);
+
+ return 0;
+}
+
+static int rk3308_codec_probe(struct snd_soc_component *component)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ rk3308->component = component;
+
+ rk3308_codec_reset(component);
+ rk3308_codec_initialize(rk3308);
+
+ return 0;
+}
+
+static int rk3308_codec_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_OFF)
+ break;
+
+ /* Sequence from TRM Section 8.6.3 "Power Up" */
+ regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02,
+ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 1);
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_REF_EN);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 0x7f);
+ msleep(20); /* estimated value */
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Sequence from TRM Section 8.6.4 "Power Down" */
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 1);
+ regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_REF_EN);
+ regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02,
+ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN);
+ msleep(20); /* estimated value */
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_component_driver rk3308_codec_component_driver = {
+ .probe = rk3308_codec_probe,
+ .set_bias_level = rk3308_codec_set_bias_level,
+ .controls = rk3308_codec_controls,
+ .num_controls = ARRAY_SIZE(rk3308_codec_controls),
+ .dapm_widgets = rk3308_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk3308_codec_dapm_widgets),
+ .dapm_routes = rk3308_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk3308_codec_dapm_routes),
+};
+
+static const struct regmap_config rk3308_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = RK3308_DAC_ANA_CON15,
+};
+
+static int rk3308_codec_get_version(struct rk3308_codec_priv *rk3308)
+{
+ unsigned int chip_id;
+ int err;
+
+ err = regmap_read(rk3308->grf, GRF_CHIP_ID, &chip_id);
+ if (err)
+ return err;
+
+ switch (chip_id) {
+ case 3306:
+ rk3308->codec_ver = ACODEC_VERSION_A;
+ break;
+ case 0x3308:
+ rk3308->codec_ver = ACODEC_VERSION_B;
+ return dev_err_probe(rk3308->dev, -EINVAL, "Chip version B not supported\n");
+ case 0x3308c:
+ rk3308->codec_ver = ACODEC_VERSION_C;
+ break;
+ default:
+ return dev_err_probe(rk3308->dev, -EINVAL, "Unknown chip_id: 0x%x\n", chip_id);
+ }
+
+ dev_info(rk3308->dev, "Found codec version %c\n", rk3308->codec_ver);
+ return 0;
+}
+
+static int rk3308_codec_set_micbias_level(struct rk3308_codec_priv *rk3308)
+{
+ struct device_node *np = rk3308->dev->of_node;
+ u32 percent;
+ u32 mult;
+ int err;
+
+ err = of_property_read_u32(np, "rockchip,micbias-avdd-percent", &percent);
+ if (err == -EINVAL)
+ return 0;
+ if (err)
+ return dev_err_probe(rk3308->dev, err,
+ "Error reading 'rockchip,micbias-avdd-percent'\n");
+
+ /* Convert percent to register value, linerarly (50% -> 0, 5% step = +1) */
+ mult = (percent - 50) / 5;
+
+ /* Check range and that the percent was an exact value allowed */
+ if (mult > RK3308_ADC_LEVEL_RANGE_MICBIAS_MAX || mult * 5 + 50 != percent)
+ return dev_err_probe(rk3308->dev, -EINVAL,
+ "Invalid value %u for 'rockchip,micbias-avdd-percent'\n",
+ percent);
+
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0),
+ RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK,
+ mult << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT);
+
+ return 0;
+}
+
+static int rk3308_codec_platform_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct rk3308_codec_priv *rk3308;
+ void __iomem *base;
+ int err;
+
+ rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL);
+ if (!rk3308)
+ return -ENOMEM;
+
+ rk3308->dev = dev;
+
+ rk3308->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(rk3308->grf))
+ return dev_err_probe(dev, PTR_ERR(rk3308->grf), "Error getting GRF\n");
+
+ rk3308->reset = devm_reset_control_get_optional_exclusive(dev, "codec");
+ if (IS_ERR(rk3308->reset))
+ return dev_err_probe(dev, PTR_ERR(rk3308->reset), "Failed to get reset control\n");
+
+ err = devm_clk_bulk_get(dev, ARRAY_SIZE(rk3308_codec_clocks), rk3308_codec_clocks);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to get clocks\n");
+
+ err = clk_bulk_prepare_enable(ARRAY_SIZE(rk3308_codec_clocks), rk3308_codec_clocks);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to enable clocks\n");
+
+ err = rk3308_codec_get_version(rk3308);
+ if (err)
+ return err;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rk3308->regmap = devm_regmap_init_mmio(dev, base, &rk3308_codec_regmap_config);
+ if (IS_ERR(rk3308->regmap))
+ return dev_err_probe(dev, PTR_ERR(rk3308->regmap),
+ "Failed to init regmap\n");
+
+ platform_set_drvdata(pdev, rk3308);
+
+ err = rk3308_codec_set_micbias_level(rk3308);
+ if (err)
+ return err;
+
+ err = devm_snd_soc_register_component(dev, &rk3308_codec_component_driver,
+ &rk3308_codec_dai_driver, 1);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to register codec\n");
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused rk3308_codec_of_match[] = {
+ { .compatible = "rockchip,rk3308-codec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk3308_codec_of_match);
+
+static struct platform_driver rk3308_codec_driver = {
+ .driver = {
+ .name = "rk3308-acodec",
+ .of_match_table = rk3308_codec_of_match,
+ },
+ .probe = rk3308_codec_platform_probe,
+};
+module_platform_driver(rk3308_codec_driver);
+
+MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_DESCRIPTION("ASoC RK3308 Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h
new file mode 100644
index 000000000000..a4226b235ab7
--- /dev/null
+++ b/sound/soc/codecs/rk3308_codec.h
@@ -0,0 +1,579 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Rockchip RK3308 internal audio codec driver -- register definitions
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ * Copyright (c) 2022, Vivax-Metrotech Ltd
+ */
+
+#ifndef __RK3308_CODEC_H__
+#define __RK3308_CODEC_H__
+
+#define RK3308_GLB_CON 0x00
+
+/* ADC DIGITAL REGISTERS */
+
+/*
+ * The ADC group are 0 ~ 3, that control:
+ *
+ * CH0: left_0(ADC1) and right_0(ADC2)
+ * CH1: left_1(ADC3) and right_1(ADC4)
+ * CH2: left_2(ADC5) and right_2(ADC6)
+ * CH3: left_3(ADC7) and right_3(ADC8)
+ */
+#define RK3308_ADC_DIG_OFFSET(ch) (((ch) & 0x3) * 0xc0 + 0x0)
+
+#define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x04)
+#define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x08)
+#define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x0c)
+#define RK3308_ADC_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x10)
+#define RK3308_ADC_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x14) // ver.C only
+#define RK3308_ADC_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x18) // ver.C only
+#define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x1c)
+
+#define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x40)
+#define RK3308_ALC_L_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x44)
+#define RK3308_ALC_L_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x48)
+#define RK3308_ALC_L_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x4c)
+#define RK3308_ALC_L_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x50)
+#define RK3308_ALC_L_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x54)
+#define RK3308_ALC_L_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x58)
+#define RK3308_ALC_L_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x5c)
+#define RK3308_ALC_L_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x60)
+#define RK3308_ALC_L_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x64)
+#define RK3308_ALC_L_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x70)
+
+#define RK3308_ALC_R_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x80)
+#define RK3308_ALC_R_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x84)
+#define RK3308_ALC_R_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x88)
+#define RK3308_ALC_R_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x8c)
+#define RK3308_ALC_R_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x90)
+#define RK3308_ALC_R_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x94)
+#define RK3308_ALC_R_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x98)
+#define RK3308_ALC_R_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x9c)
+#define RK3308_ALC_R_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa0)
+#define RK3308_ALC_R_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa4)
+#define RK3308_ALC_R_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xb0)
+
+/* DAC DIGITAL REGISTERS */
+#define RK3308_DAC_DIG_OFFSET 0x300
+#define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + 0x04)
+#define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + 0x08)
+#define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + 0x0c)
+#define RK3308_DAC_DIG_CON04 (RK3308_DAC_DIG_OFFSET + 0x10)
+#define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + 0x14)
+#define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + 0x28)
+#define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + 0x2c)
+#define RK3308_DAC_DIG_CON13 (RK3308_DAC_DIG_OFFSET + 0x34)
+#define RK3308_DAC_DIG_CON14 (RK3308_DAC_DIG_OFFSET + 0x38)
+
+/* ADC ANALOG REGISTERS */
+/*
+ * The ADC group are 0 ~ 3, that control:
+ *
+ * CH0: left_0(ADC1) and right_0(ADC2)
+ * CH1: left_1(ADC3) and right_1(ADC4)
+ * CH2: left_2(ADC5) and right_2(ADC6)
+ * CH3: left_3(ADC7) and right_3(ADC8)
+ */
+#define RK3308_ADC_ANA_OFFSET(ch) (((ch) & 0x3) * 0x40 + 0x340)
+#define RK3308_ADC_ANA_CON00(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x00)
+#define RK3308_ADC_ANA_CON01(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x04)
+#define RK3308_ADC_ANA_CON02(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x08)
+#define RK3308_ADC_ANA_CON03(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x0c)
+#define RK3308_ADC_ANA_CON04(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x10)
+#define RK3308_ADC_ANA_CON05(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x14)
+#define RK3308_ADC_ANA_CON06(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x18)
+#define RK3308_ADC_ANA_CON07(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x1c)
+#define RK3308_ADC_ANA_CON08(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x20)
+#define RK3308_ADC_ANA_CON10(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x28)
+#define RK3308_ADC_ANA_CON11(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x2c)
+
+/* DAC ANALOG REGISTERS */
+#define RK3308_DAC_ANA_OFFSET 0x440
+#define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + 0x00)
+#define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + 0x04)
+#define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + 0x08)
+#define RK3308_DAC_ANA_CON03 (RK3308_DAC_ANA_OFFSET + 0x0c)
+#define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + 0x10)
+#define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + 0x14)
+#define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + 0x18)
+#define RK3308_DAC_ANA_CON07 (RK3308_DAC_ANA_OFFSET + 0x1c)
+#define RK3308_DAC_ANA_CON08 (RK3308_DAC_ANA_OFFSET + 0x20)
+#define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + 0x30)
+#define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + 0x34)
+#define RK3308_DAC_ANA_CON14 (RK3308_DAC_ANA_OFFSET + 0x38)
+#define RK3308_DAC_ANA_CON15 (RK3308_DAC_ANA_OFFSET + 0x3c)
+
+/*
+ * These are the bits for registers
+ */
+
+/* RK3308_GLB_CON - REG: 0x0000 */
+#define RK3308_ADC_BIST_WORK BIT(7)
+#define RK3308_DAC_BIST_WORK BIT(6)
+#define RK3308_ADC_MCLK_GATING BIT(5)
+#define RK3308_DAC_MCLK_GATING BIT(4)
+#define RK3308_ADC_DIG_WORK BIT(2)
+#define RK3308_DAC_DIG_WORK BIT(1)
+#define RK3308_SYS_WORK BIT(0)
+
+/* RK3308_ADC_DIG_CON01 - REG: 0x0004 */
+#define RK3308_ADC_I2S_LRC_POL_REVERSAL BIT(7)
+#define RK3308_ADC_I2S_VALID_LEN_SFT 5
+#define RK3308_ADC_I2S_VALID_LEN_MSK (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_32BITS (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_24BITS (0x2 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_20BITS (0x1 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_16BITS (0x0 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_MODE_SFT 3
+#define RK3308_ADC_I2S_MODE_MSK (0x3 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_PCM (0x3 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_I2S (0x2 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_LJ (0x1 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_RJ (0x0 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_LR_SWAP BIT(1)
+#define RK3308_ADC_I2S_MONO BIT(0)
+
+/* RK3308_ADC_DIG_CON02 - REG: 0x0008 */
+#define RK3308_ADC_IO_MODE_MASTER BIT(5)
+#define RK3308_ADC_MODE_MASTER BIT(4)
+#define RK3308_ADC_I2S_FRAME_LEN_SFT 2
+#define RK3308_ADC_I2S_FRAME_LEN_MSK (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_32BITS (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_24BITS (0x2 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_20BITS (0x1 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_16BITS (0x0 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_WORK BIT(1)
+#define RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL BIT(0)
+
+/* RK3308_ADC_DIG_CON03 - REG: 0x000c */
+#define RK3308_ADC_L_CH_BIST_SFT 2
+#define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_NORMAL_RIGHT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_L_CH_BIST_CUBE (0x2 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_BIST_SINE (0x1 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_NORMAL_LEFT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_R_CH_BIST_SFT 0
+#define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_NORMAL_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_R_CH_BIST_CUBE (0x2 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_BIST_SINE (0x1 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_NORMAL_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */
+
+/* RK3308_ADC_DIG_CON04 - REG: 0x0010 */
+#define RK3308_ADC_HPF_PATH_DIS BIT(2)
+#define RK3308_ADC_HPF_CUTOFF_SFT 0
+#define RK3308_ADC_HPF_CUTOFF_MSK (0x3 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_612HZ (0x2 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_245HZ (0x1 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_20HZ (0x0 << RK3308_ADC_HPF_CUTOFF_SFT)
+
+/* RK3308_ADC_DIG_CON07 - REG: 0x001c */
+#define RK3308_ADCL_DATA_SFT 4
+#define RK3308_ADCR_DATA_SFT 2
+#define RK3308_ADCL_DATA_SEL_ADCL BIT(1)
+#define RK3308_ADCR_DATA_SEL_ADCR BIT(0)
+
+/*
+ * RK3308_ALC_L_DIG_CON00 - REG: 0x0040 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON00 - REG: 0x0080 + ch * 0xc0
+ */
+#define RK3308_GAIN_ATTACK_JACK BIT(6)
+#define RK3308_CTRL_GEN_SFT 4
+#define RK3308_CTRL_GEN_MSK (0x3 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK3 (0x3 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK2 (0x2 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK1 (0x1 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_NORMAL (0x0 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_AGC_HOLD_TIME_SFT 0
+#define RK3308_AGC_HOLD_TIME_MSK (0xf << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_1S (0xa << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_512MS (0x9 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_256MS (0x8 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_128MS (0x7 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_64MS (0x6 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_32MS (0x5 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_16MS (0x4 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_8MS (0x3 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_4MS (0x2 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_2MS (0x1 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_0MS (0x0 << RK3308_AGC_HOLD_TIME_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON01 - REG: 0x0044 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON01 - REG: 0x0084 + ch * 0xc0
+ */
+#define RK3308_AGC_DECAY_TIME_SFT 4
+#define RK3308_AGC_ATTACK_TIME_SFT 0
+
+/*
+ * RK3308_ALC_L_DIG_CON02 - REG: 0x0048 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON02 - REG: 0x0088 + ch * 0xc0
+ */
+#define RK3308_AGC_MODE_LIMITER BIT(7)
+#define RK3308_AGC_ZERO_CRO_EN BIT(6)
+#define RK3308_AGC_AMP_RECOVER_GAIN BIT(5)
+#define RK3308_AGC_FAST_DEC_EN BIT(4)
+#define RK3308_AGC_NOISE_GATE_EN BIT(3)
+#define RK3308_AGC_NOISE_GATE_THRESH_SFT 0
+#define RK3308_AGC_NOISE_GATE_THRESH_MSK (0x7 << RK3308_AGC_NOISE_GATE_THRESH_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON03 - REG: 0x004c + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON03 - REG: 0x008c + ch * 0xc0
+ */
+#define RK3308_AGC_PGA_ZERO_CRO_EN BIT(5)
+#define RK3308_AGC_PGA_GAIN_MAX 0x1f
+#define RK3308_AGC_PGA_GAIN_MIN 0
+#define RK3308_AGC_PGA_GAIN_SFT 0
+
+/*
+ * RK3308_ALC_L_DIG_CON04 - REG: 0x0050 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON04 - REG: 0x0090 + ch * 0xc0
+ */
+#define RK3308_AGC_SLOW_CLK_EN BIT(3)
+#define RK3308_AGC_APPROX_RATE_SFT 0
+#define RK3308_AGC_APPROX_RATE_MSK (0x7 << RK3308_AGC_APPROX_RATE_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON05 - REG: 0x0054 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON05 - REG: 0x0094 + ch * 0xc0
+ */
+#define RK3308_AGC_LO_8BITS_AGC_MAX_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON06 - REG: 0x0058 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON06 - REG: 0x0098 + ch * 0xc0
+ */
+#define RK3308_AGC_HI_8BITS_AGC_MAX_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON07 - REG: 0x005c + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON07 - REG: 0x009c + ch * 0xc0
+ */
+#define RK3308_AGC_LO_8BITS_AGC_MIN_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON08 - REG: 0x0060 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON08 - REG: 0x00a0 + ch * 0xc0
+ */
+#define RK3308_AGC_HI_8BITS_AGC_MIN_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON09 - REG: 0x0064 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON09 - REG: 0x00a4 + ch * 0xc0
+ */
+#define RK3308_AGC_FUNC_SEL BIT(6)
+#define RK3308_AGC_MAX_GAIN_PGA_MAX 0x7
+#define RK3308_AGC_MAX_GAIN_PGA_MIN 0
+#define RK3308_AGC_MAX_GAIN_PGA_SFT 3
+#define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT)
+#define RK3308_AGC_MIN_GAIN_PGA_MAX 0x7
+#define RK3308_AGC_MIN_GAIN_PGA_MIN 0
+#define RK3308_AGC_MIN_GAIN_PGA_SFT 0
+#define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON12 - REG: 0x0068 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON12 - REG: 0x00a8 + ch * 0xc0
+ */
+#define RK3308_AGC_GAIN_MSK 0x1f
+
+/* RK3308_DAC_DIG_CON01 - REG: 0x0304 */
+#define RK3308_DAC_I2S_LRC_POL_REVERSAL BIT(7)
+#define RK3308_DAC_I2S_VALID_LEN_SFT 5
+#define RK3308_DAC_I2S_VALID_LEN_MSK (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_32BITS (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_24BITS (0x2 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_20BITS (0x1 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_16BITS (0x0 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_MODE_SFT 3
+#define RK3308_DAC_I2S_MODE_MSK (0x3 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_PCM (0x3 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_I2S (0x2 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_LJ (0x1 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_RJ (0x0 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_LR_SWAP BIT(2)
+
+/* RK3308_DAC_DIG_CON02 - REG: 0x0308 */
+#define RK3308BS_DAC_IO_MODE_MASTER BIT(7)
+#define RK3308BS_DAC_MODE_MASTER BIT(6)
+#define RK3308_DAC_IO_MODE_MASTER BIT(5)
+#define RK3308_DAC_MODE_MASTER BIT(4)
+#define RK3308_DAC_I2S_FRAME_LEN_SFT 2
+#define RK3308_DAC_I2S_FRAME_LEN_MSK (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_32BITS (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_24BITS (0x2 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_20BITS (0x1 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_16BITS (0x0 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_WORK BIT(1)
+#define RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL BIT(0)
+
+/* RK3308_DAC_DIG_CON03 - REG: 0x030C */
+#define RK3308_DAC_L_CH_BIST_SFT 2
+#define RK3308_DAC_L_CH_BIST_MSK (0x3 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_LEFT (0x3 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_L_CH_BIST_CUBE (0x2 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_SINE (0x1 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_RIGHT (0x0 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_R_CH_BIST_SFT 0
+#define RK3308_DAC_R_CH_BIST_MSK (0x3 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_LEFT (0x3 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_R_CH_BIST_CUBE (0x2 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */
+
+/* RK3308_DAC_DIG_CON04 - REG: 0x0310 */
+/* Versions up to B: */
+#define RK3308_DAC_MODULATOR_GAIN_SFT 4
+#define RK3308_DAC_MODULATOR_GAIN_MSK (0x7 << RK3308_DAC_MODULATOR_GAIN_SFT)
+#define RK3308_DAC_CIC_IF_GAIN_SFT 0
+#define RK3308_DAC_CIC_IF_GAIN_MSK (0x7 << RK3308_DAC_CIC_IF_GAIN_SFT)
+/* Version C: */
+#define RK3308BS_DAC_DIG_GAIN_SFT 0
+#define RK3308BS_DAC_DIG_GAIN_MSK (0xff << RK3308BS_DAC_DIG_GAIN_SFT)
+#define RK3308BS_DAC_DIG_GAIN_0DB (0xed << RK3308BS_DAC_DIG_GAIN_SFT)
+
+/* RK3308BS_ADC_DIG_CON05..06 (Version C only) */
+#define RK3308_ADC_DIG_VOL_CON_x_SFT 0
+#define RK3308_ADC_DIG_VOL_CON_x_MSK (0xff << RK3308_ADC_DIG_VOL_CON_x_SFT)
+#define RK3308_ADC_DIG_VOL_CON_x_0DB (0xc2 << RK3308_ADC_DIG_VOL_CON_x_SFT)
+
+/* RK3308_DAC_DIG_CON05 - REG: 0x0314 */
+#define RK3308_DAC_L_REG_CTL_INDATA BIT(2)
+#define RK3308_DAC_R_REG_CTL_INDATA BIT(1)
+
+/* RK3308_DAC_DIG_CON10 - REG: 0x0328 */
+#define RK3308_DAC_DATA_HI4(x) ((x) & 0xf)
+
+/* RK3308_DAC_DIG_CON11 - REG: 0x032c */
+#define RK3308_DAC_DATA_LO8(x) ((x) & 0xff)
+
+/* RK3308_ADC_ANA_CON00 - REG: 0x0340 */
+#define RK3308_ADC_CH1_CH2_MIC_ALL_MSK (0xff << 0)
+#define RK3308_ADC_CH1_CH2_MIC_ALL 0xff
+#define RK3308_ADC_CH2_MIC_UNMUTE BIT(7)
+#define RK3308_ADC_CH2_MIC_WORK BIT(6)
+#define RK3308_ADC_CH2_MIC_EN BIT(5)
+#define RK3308_ADC_CH2_BUF_REF_EN BIT(4)
+#define RK3308_ADC_CH1_MIC_UNMUTE BIT(3)
+#define RK3308_ADC_CH1_MIC_WORK BIT(2)
+#define RK3308_ADC_CH1_MIC_EN BIT(1)
+#define RK3308_ADC_CH1_BUF_REF_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON01 - REG: 0x0344
+ *
+ * The PGA of MIC-INs:
+ * - HW version A:
+ * 0x0 - MIC1~MIC8 0 dB (recommended when ADC used as loopback)
+ * 0x3 - MIC1~MIC8 20 dB (recommended when ADC used as MIC input)
+ * - HW version B:
+ * 0x0 - MIC1~MIC8 0 dB
+ * 0x1 - MIC1~MIC8 6.6 dB
+ * 0x2 - MIC1~MIC8 13 dB
+ * 0x3 - MIC1~MIC8 20 dB
+ */
+#define RK3308_ADC_CH2_MIC_GAIN_MAX 0x3
+#define RK3308_ADC_CH2_MIC_GAIN_MIN 0
+#define RK3308_ADC_CH2_MIC_GAIN_SFT 4
+#define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+
+#define RK3308_ADC_CH1_MIC_GAIN_MAX 0x3
+#define RK3308_ADC_CH1_MIC_GAIN_MIN 0
+#define RK3308_ADC_CH1_MIC_GAIN_SFT 0
+#define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON02 - REG: 0x0348 */
+#define RK3308_ADC_CH2_ZEROCROSS_DET_EN BIT(6)
+#define RK3308_ADC_CH2_ALC_WORK BIT(5)
+#define RK3308_ADC_CH2_ALC_EN BIT(4)
+#define RK3308_ADC_CH1_ZEROCROSS_DET_EN BIT(2)
+#define RK3308_ADC_CH1_ALC_WORK BIT(1)
+#define RK3308_ADC_CH1_ALC_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON03 - REG: 0x034c */
+#define RK3308_ADC_CH1_ALC_GAIN_MAX 0x1f
+#define RK3308_ADC_CH1_ALC_GAIN_MIN 0
+#define RK3308_ADC_CH1_ALC_GAIN_SFT 0
+#define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT)
+#define RK3308_ADC_CH1_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH1_ALC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON04 - REG: 0x0350 */
+#define RK3308_ADC_CH2_ALC_GAIN_MAX 0x1f
+#define RK3308_ADC_CH2_ALC_GAIN_MIN 0
+#define RK3308_ADC_CH2_ALC_GAIN_SFT 0
+#define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT)
+#define RK3308_ADC_CH2_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH2_ALC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON05 - REG: 0x0354 */
+#define RK3308_ADC_CH2_ADC_WORK BIT(6)
+#define RK3308_ADC_CH2_ADC_EN BIT(5)
+#define RK3308_ADC_CH2_CLK_EN BIT(4)
+#define RK3308_ADC_CH1_ADC_WORK BIT(2)
+#define RK3308_ADC_CH1_ADC_EN BIT(1)
+#define RK3308_ADC_CH1_CLK_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON06 - REG: 0x0358 */
+#define RK3308_ADC_CURRENT_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON07 - REG: 0x035c */
+/* Note: The register configuration is only valid for ADC2 */
+#define RK3308_ADC_CH2_IN_SEL_SFT 6
+#define RK3308_ADC_CH2_IN_SEL_MSK (0x3 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_LINEIN (0x2 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_MIC (0x1 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_NONE (0x0 << RK3308_ADC_CH2_IN_SEL_SFT)
+/* Note: The register configuration is only valid for ADC1 */
+#define RK3308_ADC_CH1_IN_SEL_SFT 4
+#define RK3308_ADC_CH1_IN_SEL_MSK (0x3 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_LINEIN (0x2 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_MIC_BIAS_BUF_EN BIT(3)
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MAX 7
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT)
+
+/* RK3308_ADC_ANA_CON08 - REG: 0x0360 */
+#define RK3308_ADC_MICBIAS_CURRENT_EN BIT(4)
+
+/* RK3308_ADC_ANA_CON10 - REG: 0x0368 */
+#define RK3308_ADC_REF_EN BIT(7)
+#define RK3308_ADC_CURRENT_CHARGE_SFT 0
+#define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT)
+
+/* RK3308_ADC_ANA_CON11 - REG: 0x036c */
+#define RK3308_ADC_ALCR_CON_GAIN_PGAR_EN BIT(1)
+#define RK3308_ADC_ALCL_CON_GAIN_PGAL_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON00 - REG: 0x0440 */
+#define RK3308_DAC_HEADPHONE_DET_EN BIT(1)
+#define RK3308_DAC_CURRENT_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON01 - REG: 0x0444 */
+#define RK3308_DAC_BUF_REF_R_EN BIT(6)
+#define RK3308_DAC_BUF_REF_L_EN BIT(2)
+#define RK3308_DAC_HPOUT_POP_SOUND_R_SFT 4
+#define RK3308_DAC_HPOUT_POP_SOUND_L_SFT 0
+// unshifted values for both L and R:
+#define RK3308_DAC_HPOUT_POP_SOUND_x_MSK 0x3
+#define RK3308_DAC_HPOUT_POP_SOUND_x_WORK 0x2
+#define RK3308_DAC_HPOUT_POP_SOUND_x_INIT 0x1
+
+/* RK3308_DAC_ANA_CON02 - REG: 0x0448 */
+#define RK3308_DAC_R_DAC_WORK BIT(7)
+#define RK3308_DAC_R_DAC_EN BIT(6)
+#define RK3308_DAC_R_CLK_EN BIT(5)
+#define RK3308_DAC_R_REF_EN BIT(4)
+#define RK3308_DAC_L_DAC_WORK BIT(3)
+#define RK3308_DAC_L_DAC_EN BIT(2)
+#define RK3308_DAC_L_CLK_EN BIT(1)
+#define RK3308_DAC_L_REF_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON03 - REG: 0x044c */
+#define RK3308_DAC_R_HPOUT_WORK BIT(6)
+#define RK3308_DAC_R_HPOUT_EN BIT(5)
+#define RK3308_DAC_R_HPOUT_MUTE_SFT 4
+#define RK3308_DAC_L_HPOUT_WORK BIT(2)
+#define RK3308_DAC_L_HPOUT_EN BIT(1)
+#define RK3308_DAC_L_HPOUT_MUTE_SFT 0
+
+/* RK3308_DAC_ANA_CON04 - REG: 0x0450 */
+#define RK3308_DAC_x_LINEOUT_GAIN_MAX 0x3
+#define RK3308_DAC_R_LINEOUT_GAIN_SFT 6
+#define RK3308_DAC_R_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_MUTE_SFT 5
+#define RK3308_DAC_R_LINEOUT_EN BIT(4)
+#define RK3308_DAC_L_LINEOUT_GAIN_SFT 2
+#define RK3308_DAC_L_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_MUTE_SFT 1
+#define RK3308_DAC_L_LINEOUT_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */
+/* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */
+#define RK3308_DAC_x_HPOUT_GAIN_MAX 0x1e
+#define RK3308_DAC_x_HPOUT_GAIN_SFT 0
+#define RK3308_DAC_x_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_x_HPOUT_GAIN_SFT)
+#define RK3308_DAC_x_HPOUT_GAIN_MIN (0x00 << RK3308_DAC_x_HPOUT_GAIN_SFT)
+
+/* RK3308_DAC_ANA_CON07 - REG: 0x045c */
+#define RK3308_DAC_R_HPOUT_DRV_SFT 4
+#define RK3308_DAC_R_HPOUT_DRV_MSK (0xf << RK3308_DAC_R_HPOUT_DRV_SFT)
+#define RK3308_DAC_L_HPOUT_DRV_SFT 0
+#define RK3308_DAC_L_HPOUT_DRV_MSK (0xf << RK3308_DAC_L_HPOUT_DRV_SFT)
+
+/* RK3308_DAC_ANA_CON08 - REG: 0x0460 */
+#define RK3308_DAC_R_LINEOUT_DRV_SFT 4
+#define RK3308_DAC_R_LINEOUT_DRV_MSK (0xf << RK3308_DAC_R_LINEOUT_DRV_SFT)
+#define RK3308_DAC_L_LINEOUT_DRV_SFT 0
+#define RK3308_DAC_L_LINEOUT_DRV_MSK (0xf << RK3308_DAC_L_LINEOUT_DRV_SFT)
+
+/* RK3308_DAC_ANA_CON12 - REG: 0x0470 */
+#define RK3308_DAC_R_HPMIX_SEL_SFT 6
+#define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_SEL_SFT 2
+#define RK3308_DAC_L_HPMIX_SEL_MSK (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_x_HPMIX_GAIN_MIN 0x1 /* 0x0 and 0x3 are reserved */
+#define RK3308_DAC_x_HPMIX_GAIN_MAX 0x2
+#define RK3308_DAC_R_HPMIX_GAIN_SFT 4
+#define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_R_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_SFT 0
+#define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+
+/* RK3308_DAC_ANA_CON13 - REG: 0x0474 */
+#define RK3308_DAC_R_HPMIX_UNMUTE BIT(6)
+#define RK3308_DAC_R_HPMIX_WORK BIT(5)
+#define RK3308_DAC_R_HPMIX_EN BIT(4)
+#define RK3308_DAC_L_HPMIX_UNMUTE BIT(2)
+#define RK3308_DAC_L_HPMIX_WORK BIT(1)
+#define RK3308_DAC_L_HPMIX_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON14 - REG: 0x0478 */
+#define RK3308_DAC_VCM_LINEOUT_EN (0x1 << 4)
+#define RK3308_DAC_CURRENT_CHARGE_SFT 0
+#define RK3308_DAC_CURRENT_CHARGE_MSK (0xf << RK3308_DAC_CURRENT_CHARGE_SFT)
+
+/* RK3308_DAC_ANA_CON15 - REG: 0x047C */
+#define RK3308_DAC_LINEOUT_POP_SOUND_R_SFT 4
+#define RK3308_DAC_LINEOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_LINEOUT_POP_SOUND_L_SFT 0
+#define RK3308_DAC_LINEOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+
+#endif /* __RK3308_CODEC_H__ */
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
new file mode 100644
index 000000000000..9697aefc6e03
--- /dev/null
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk3328 ALSA SoC Audio driver
+//
+// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include "rk3328_codec.h"
+
+/*
+ * volume setting
+ * 0: -39dB
+ * 26: 0dB
+ * 31: 6dB
+ * Step: 1.5dB
+ */
+#define OUT_VOLUME (0x18)
+#define RK3328_GRF_SOC_CON2 (0x0408)
+#define RK3328_GRF_SOC_CON10 (0x0428)
+#define INITIAL_FREQ (11289600)
+
+struct rk3328_codec_priv {
+ struct regmap *regmap;
+ struct gpio_desc *mute;
+ struct clk *mclk;
+ struct clk *pclk;
+ unsigned int sclk;
+ int spk_depop_time; /* msec */
+};
+
+static const struct reg_default rk3328_codec_reg_defaults[] = {
+ { CODEC_RESET, 0x03 },
+ { DAC_INIT_CTRL1, 0x00 },
+ { DAC_INIT_CTRL2, 0x50 },
+ { DAC_INIT_CTRL3, 0x0e },
+ { DAC_PRECHARGE_CTRL, 0x01 },
+ { DAC_PWR_CTRL, 0x00 },
+ { DAC_CLK_CTRL, 0x00 },
+ { HPMIX_CTRL, 0x00 },
+ { HPOUT_CTRL, 0x00 },
+ { HPOUTL_GAIN_CTRL, 0x00 },
+ { HPOUTR_GAIN_CTRL, 0x00 },
+ { HPOUT_POP_CTRL, 0x11 },
+};
+
+static int rk3328_codec_reset(struct rk3328_codec_priv *rk3328)
+{
+ regmap_write(rk3328->regmap, CODEC_RESET, 0x00);
+ mdelay(10);
+ regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
+
+ return 0;
+}
+
+static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
+ PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ val = DAC_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = DAC_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = DAC_MODE_RJM;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = DAC_MODE_LJM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2,
+ DAC_MODE_MASK, val);
+
+ return 0;
+}
+
+static int rk3328_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int val;
+
+ if (mute)
+ val = HPOUTL_MUTE | HPOUTR_MUTE;
+ else
+ val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
+
+ regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
+ HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
+
+ return 0;
+}
+
+static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms)
+{
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
+ mdelay(10);
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_ALL_ON);
+ mdelay(wait_ms);
+
+ return 0;
+}
+
+static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms)
+{
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
+ mdelay(10);
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_ALL_ON);
+ mdelay(wait_ms);
+
+ return 0;
+}
+
+static const struct rk3328_reg_msk_val playback_open_list[] = {
+ { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
+ { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+ DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
+ { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK,
+ HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
+ { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+ HPOUTR_POP_WORK | HPOUTL_POP_WORK },
+ { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
+ { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+ HPMIXL_INIT_EN | HPMIXR_INIT_EN },
+ { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
+ { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+ HPOUTL_INIT_EN | HPOUTR_INIT_EN },
+ { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+ DACL_REFV_ON | DACR_REFV_ON },
+ { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+ DACL_CLK_ON | DACR_CLK_ON },
+ { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
+ { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+ DACL_INIT_ON | DACR_INIT_ON },
+ { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+ DACL_SELECT | DACR_SELECT },
+ { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+ HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
+ { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+ HPOUTL_UNMUTE | HPOUTR_UNMUTE },
+};
+
+static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328)
+{
+ int i;
+
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_I);
+
+ for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) {
+ regmap_update_bits(rk3328->regmap,
+ playback_open_list[i].reg,
+ playback_open_list[i].msk,
+ playback_open_list[i].val);
+ mdelay(1);
+ }
+
+ msleep(rk3328->spk_depop_time);
+ gpiod_set_value(rk3328->mute, 0);
+
+ regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+ HPOUTL_GAIN_MASK, OUT_VOLUME);
+ regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+ HPOUTR_GAIN_MASK, OUT_VOLUME);
+
+ return 0;
+}
+
+static const struct rk3328_reg_msk_val playback_close_list[] = {
+ { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+ HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
+ { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+ DACL_UNSELECT | DACR_UNSELECT },
+ { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+ HPOUTL_MUTE | HPOUTR_MUTE },
+ { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+ HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
+ { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
+ { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
+ { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
+ { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+ DACL_CLK_OFF | DACR_CLK_OFF },
+ { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+ DACL_REFV_OFF | DACR_REFV_OFF },
+ { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+ HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
+ { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+ DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
+ { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
+ { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+ HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
+ { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+ DACL_INIT_OFF | DACR_INIT_OFF },
+};
+
+static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328)
+{
+ size_t i;
+
+ gpiod_set_value(rk3328->mute, 1);
+
+ regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+ HPOUTL_GAIN_MASK, 0);
+ regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+ HPOUTR_GAIN_MASK, 0);
+
+ for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) {
+ regmap_update_bits(rk3328->regmap,
+ playback_close_list[i].reg,
+ playback_close_list[i].msk,
+ playback_close_list[i].val);
+ mdelay(1);
+ }
+
+ /* Workaround for silence when changed Fs 48 -> 44.1kHz */
+ rk3328_codec_reset(rk3328);
+
+ regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+ DAC_CHARGE_CURRENT_ALL_MASK,
+ DAC_CHARGE_CURRENT_ALL_ON);
+
+ return 0;
+}
+
+static int rk3328_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+ unsigned int val = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = DAC_VDL_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = DAC_VDL_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = DAC_VDL_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = DAC_VDL_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
+
+ val = DAC_WL_32BITS | DAC_RST_DIS;
+ regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
+ DAC_WL_MASK | DAC_RST_MASK, val);
+
+ return 0;
+}
+
+static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ return rk3328_codec_open_playback(rk3328);
+}
+
+static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ rk3328_codec_close_playback(rk3328);
+}
+
+static const struct snd_soc_dai_ops rk3328_dai_ops = {
+ .hw_params = rk3328_hw_params,
+ .set_fmt = rk3328_set_dai_fmt,
+ .mute_stream = rk3328_mute_stream,
+ .startup = rk3328_pcm_startup,
+ .shutdown = rk3328_pcm_shutdown,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver rk3328_dai[] = {
+ {
+ .name = "rk3328-hifi",
+ .id = RK3328_HIFI,
+ .playback = {
+ .stream_name = "HIFI Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .capture = {
+ .stream_name = "HIFI Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .ops = &rk3328_dai_ops,
+ },
+};
+
+static int rk3328_codec_probe(struct snd_soc_component *component)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(component);
+
+ rk3328_codec_reset(rk3328);
+ rk3328_codec_power_on(rk3328, 0);
+
+ return 0;
+}
+
+static void rk3328_codec_remove(struct snd_soc_component *component)
+{
+ struct rk3328_codec_priv *rk3328 =
+ snd_soc_component_get_drvdata(component);
+
+ rk3328_codec_close_playback(rk3328);
+ rk3328_codec_power_off(rk3328, 0);
+}
+
+static const struct snd_soc_component_driver soc_codec_rk3328 = {
+ .probe = rk3328_codec_probe,
+ .remove = rk3328_codec_remove,
+};
+
+static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CODEC_RESET:
+ case DAC_INIT_CTRL1:
+ case DAC_INIT_CTRL2:
+ case DAC_INIT_CTRL3:
+ case DAC_PRECHARGE_CTRL:
+ case DAC_PWR_CTRL:
+ case DAC_CLK_CTRL:
+ case HPMIX_CTRL:
+ case DAC_SELECT:
+ case HPOUT_CTRL:
+ case HPOUTL_GAIN_CTRL:
+ case HPOUTR_GAIN_CTRL:
+ case HPOUT_POP_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CODEC_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rk3328_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = HPOUT_POP_CTRL,
+ .writeable_reg = rk3328_codec_write_read_reg,
+ .readable_reg = rk3328_codec_write_read_reg,
+ .volatile_reg = rk3328_codec_volatile_reg,
+ .reg_defaults = rk3328_codec_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rk3328_platform_probe(struct platform_device *pdev)
+{
+ struct device_node *rk3328_np = pdev->dev.of_node;
+ struct rk3328_codec_priv *rk3328;
+ struct regmap *grf;
+ void __iomem *base;
+ int ret = 0;
+
+ rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
+ if (!rk3328)
+ return -ENOMEM;
+
+ grf = syscon_regmap_lookup_by_phandle(rk3328_np,
+ "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
+ return PTR_ERR(grf);
+ }
+ /* enable i2s_acodec_en */
+ regmap_write(grf, RK3328_GRF_SOC_CON2,
+ (BIT(14) << 16 | BIT(14)));
+
+ ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
+ &rk3328->spk_depop_time);
+ if (ret < 0) {
+ dev_info(&pdev->dev, "spk_depop_time use default value.\n");
+ rk3328->spk_depop_time = 200;
+ }
+
+ rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH);
+ if (IS_ERR(rk3328->mute))
+ return PTR_ERR(rk3328->mute);
+ /*
+ * Rock64 is the only supported platform to have widely relied on
+ * this; if we do happen to come across an old DTB, just leave the
+ * external mute forced off.
+ */
+ if (!rk3328->mute && of_machine_is_compatible("pine64,rock64")) {
+ dev_warn(&pdev->dev, "assuming implicit control of GPIO_MUTE; update devicetree if possible\n");
+ regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1));
+ }
+
+ rk3328->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(rk3328->mclk))
+ return PTR_ERR(rk3328->mclk);
+
+ ret = clk_prepare_enable(rk3328->mclk);
+ if (ret)
+ return ret;
+ clk_set_rate(rk3328->mclk, INITIAL_FREQ);
+
+ rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(rk3328->pclk)) {
+ dev_err(&pdev->dev, "can't get acodec pclk\n");
+ ret = PTR_ERR(rk3328->pclk);
+ goto err_unprepare_mclk;
+ }
+
+ ret = clk_prepare_enable(rk3328->pclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable acodec pclk\n");
+ goto err_unprepare_mclk;
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err_unprepare_pclk;
+ }
+
+ rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &rk3328_codec_regmap_config);
+ if (IS_ERR(rk3328->regmap)) {
+ ret = PTR_ERR(rk3328->regmap);
+ goto err_unprepare_pclk;
+ }
+
+ platform_set_drvdata(pdev, rk3328);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
+ rk3328_dai,
+ ARRAY_SIZE(rk3328_dai));
+ if (ret)
+ goto err_unprepare_pclk;
+
+ return 0;
+
+err_unprepare_pclk:
+ clk_disable_unprepare(rk3328->pclk);
+
+err_unprepare_mclk:
+ clk_disable_unprepare(rk3328->mclk);
+ return ret;
+}
+
+static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = {
+ { .compatible = "rockchip,rk3328-codec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk3328_codec_of_match);
+
+static struct platform_driver rk3328_codec_driver = {
+ .driver = {
+ .name = "rk3328-codec",
+ .of_match_table = of_match_ptr(rk3328_codec_of_match),
+ },
+ .probe = rk3328_platform_probe,
+};
+module_platform_driver(rk3328_codec_driver);
+
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("ASoC rk3328 codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rk3328_codec.h b/sound/soc/codecs/rk3328_codec.h
new file mode 100644
index 000000000000..655103586241
--- /dev/null
+++ b/sound/soc/codecs/rk3328_codec.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rk3328 ALSA SoC Audio driver
+ *
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ */
+
+#ifndef _RK3328_CODEC_H
+#define _RK3328_CODEC_H
+
+#include <linux/bitfield.h>
+
+/* codec register */
+#define CODEC_RESET (0x00 << 2)
+#define DAC_INIT_CTRL1 (0x03 << 2)
+#define DAC_INIT_CTRL2 (0x04 << 2)
+#define DAC_INIT_CTRL3 (0x05 << 2)
+#define DAC_PRECHARGE_CTRL (0x22 << 2)
+#define DAC_PWR_CTRL (0x23 << 2)
+#define DAC_CLK_CTRL (0x24 << 2)
+#define HPMIX_CTRL (0x25 << 2)
+#define DAC_SELECT (0x26 << 2)
+#define HPOUT_CTRL (0x27 << 2)
+#define HPOUTL_GAIN_CTRL (0x28 << 2)
+#define HPOUTR_GAIN_CTRL (0x29 << 2)
+#define HPOUT_POP_CTRL (0x2a << 2)
+
+/* REG00: CODEC_RESET */
+#define PWR_RST_BYPASS_DIS (0x0 << 6)
+#define PWR_RST_BYPASS_EN (0x1 << 6)
+#define DIG_CORE_RST (0x0 << 1)
+#define DIG_CORE_WORK (0x1 << 1)
+#define SYS_RST (0x0 << 0)
+#define SYS_WORK (0x1 << 0)
+
+/* REG03: DAC_INIT_CTRL1 */
+#define PIN_DIRECTION_MASK BIT(5)
+#define PIN_DIRECTION_IN (0x0 << 5)
+#define PIN_DIRECTION_OUT (0x1 << 5)
+#define DAC_I2S_MODE_MASK BIT(4)
+#define DAC_I2S_MODE_SLAVE (0x0 << 4)
+#define DAC_I2S_MODE_MASTER (0x1 << 4)
+
+/* REG04: DAC_INIT_CTRL2 */
+#define DAC_I2S_LRP_MASK BIT(7)
+#define DAC_I2S_LRP_NORMAL (0x0 << 7)
+#define DAC_I2S_LRP_REVERSAL (0x1 << 7)
+#define DAC_VDL_MASK GENMASK(6, 5)
+#define DAC_VDL_16BITS (0x0 << 5)
+#define DAC_VDL_20BITS (0x1 << 5)
+#define DAC_VDL_24BITS (0x2 << 5)
+#define DAC_VDL_32BITS (0x3 << 5)
+#define DAC_MODE_MASK GENMASK(4, 3)
+#define DAC_MODE_RJM (0x0 << 3)
+#define DAC_MODE_LJM (0x1 << 3)
+#define DAC_MODE_I2S (0x2 << 3)
+#define DAC_MODE_PCM (0x3 << 3)
+#define DAC_LR_SWAP_MASK BIT(2)
+#define DAC_LR_SWAP_DIS (0x0 << 2)
+#define DAC_LR_SWAP_EN (0x1 << 2)
+
+/* REG05: DAC_INIT_CTRL3 */
+#define DAC_WL_MASK GENMASK(3, 2)
+#define DAC_WL_16BITS (0x0 << 2)
+#define DAC_WL_20BITS (0x1 << 2)
+#define DAC_WL_24BITS (0x2 << 2)
+#define DAC_WL_32BITS (0x3 << 2)
+#define DAC_RST_MASK BIT(1)
+#define DAC_RST_EN (0x0 << 1)
+#define DAC_RST_DIS (0x1 << 1)
+#define DAC_BCP_MASK BIT(0)
+#define DAC_BCP_NORMAL (0x0 << 0)
+#define DAC_BCP_REVERSAL (0x1 << 0)
+
+/* REG22: DAC_PRECHARGE_CTRL */
+#define DAC_CHARGE_XCHARGE_MASK BIT(7)
+#define DAC_CHARGE_DISCHARGE (0x0 << 7)
+#define DAC_CHARGE_PRECHARGE (0x1 << 7)
+#define DAC_CHARGE_CURRENT_64I_MASK BIT(6)
+#define DAC_CHARGE_CURRENT_64I (0x1 << 6)
+#define DAC_CHARGE_CURRENT_32I_MASK BIT(5)
+#define DAC_CHARGE_CURRENT_32I (0x1 << 5)
+#define DAC_CHARGE_CURRENT_16I_MASK BIT(4)
+#define DAC_CHARGE_CURRENT_16I (0x1 << 4)
+#define DAC_CHARGE_CURRENT_08I_MASK BIT(3)
+#define DAC_CHARGE_CURRENT_08I (0x1 << 3)
+#define DAC_CHARGE_CURRENT_04I_MASK BIT(2)
+#define DAC_CHARGE_CURRENT_04I (0x1 << 2)
+#define DAC_CHARGE_CURRENT_02I_MASK BIT(1)
+#define DAC_CHARGE_CURRENT_02I (0x1 << 1)
+#define DAC_CHARGE_CURRENT_I_MASK BIT(0)
+#define DAC_CHARGE_CURRENT_I (0x1 << 0)
+#define DAC_CHARGE_CURRENT_ALL_MASK GENMASK(6, 0)
+#define DAC_CHARGE_CURRENT_ALL_OFF 0x00
+#define DAC_CHARGE_CURRENT_ALL_ON 0x7f
+
+/* REG23: DAC_PWR_CTRL */
+#define DAC_PWR_MASK BIT(6)
+#define DAC_PWR_OFF (0x0 << 6)
+#define DAC_PWR_ON (0x1 << 6)
+#define DACL_PATH_REFV_MASK BIT(5)
+#define DACL_PATH_REFV_OFF (0x0 << 5)
+#define DACL_PATH_REFV_ON (0x1 << 5)
+#define HPOUTL_ZERO_CROSSING_MASK BIT(4)
+#define HPOUTL_ZERO_CROSSING_OFF (0x0 << 4)
+#define HPOUTL_ZERO_CROSSING_ON (0x1 << 4)
+#define DACR_PATH_REFV_MASK BIT(1)
+#define DACR_PATH_REFV_OFF (0x0 << 1)
+#define DACR_PATH_REFV_ON (0x1 << 1)
+#define HPOUTR_ZERO_CROSSING_MASK BIT(0)
+#define HPOUTR_ZERO_CROSSING_OFF (0x0 << 0)
+#define HPOUTR_ZERO_CROSSING_ON (0x1 << 0)
+
+/* REG24: DAC_CLK_CTRL */
+#define DACL_REFV_MASK BIT(7)
+#define DACL_REFV_OFF (0x0 << 7)
+#define DACL_REFV_ON (0x1 << 7)
+#define DACL_CLK_MASK BIT(6)
+#define DACL_CLK_OFF (0x0 << 6)
+#define DACL_CLK_ON (0x1 << 6)
+#define DACL_MASK BIT(5)
+#define DACL_OFF (0x0 << 5)
+#define DACL_ON (0x1 << 5)
+#define DACL_INIT_MASK BIT(4)
+#define DACL_INIT_OFF (0x0 << 4)
+#define DACL_INIT_ON (0x1 << 4)
+#define DACR_REFV_MASK BIT(3)
+#define DACR_REFV_OFF (0x0 << 3)
+#define DACR_REFV_ON (0x1 << 3)
+#define DACR_CLK_MASK BIT(2)
+#define DACR_CLK_OFF (0x0 << 2)
+#define DACR_CLK_ON (0x1 << 2)
+#define DACR_MASK BIT(1)
+#define DACR_OFF (0x0 << 1)
+#define DACR_ON (0x1 << 1)
+#define DACR_INIT_MASK BIT(0)
+#define DACR_INIT_OFF (0x0 << 0)
+#define DACR_INIT_ON (0x1 << 0)
+
+/* REG25: HPMIX_CTRL*/
+#define HPMIXL_MASK BIT(6)
+#define HPMIXL_DIS (0x0 << 6)
+#define HPMIXL_EN (0x1 << 6)
+#define HPMIXL_INIT_MASK BIT(5)
+#define HPMIXL_INIT_DIS (0x0 << 5)
+#define HPMIXL_INIT_EN (0x1 << 5)
+#define HPMIXL_INIT2_MASK BIT(4)
+#define HPMIXL_INIT2_DIS (0x0 << 4)
+#define HPMIXL_INIT2_EN (0x1 << 4)
+#define HPMIXR_MASK BIT(2)
+#define HPMIXR_DIS (0x0 << 2)
+#define HPMIXR_EN (0x1 << 2)
+#define HPMIXR_INIT_MASK BIT(1)
+#define HPMIXR_INIT_DIS (0x0 << 1)
+#define HPMIXR_INIT_EN (0x1 << 1)
+#define HPMIXR_INIT2_MASK BIT(0)
+#define HPMIXR_INIT2_DIS (0x0 << 0)
+#define HPMIXR_INIT2_EN (0x1 << 0)
+
+/* REG26: DAC_SELECT */
+#define DACL_SELECT_MASK BIT(4)
+#define DACL_UNSELECT (0x0 << 4)
+#define DACL_SELECT (0x1 << 4)
+#define DACR_SELECT_MASK BIT(0)
+#define DACR_UNSELECT (0x0 << 0)
+#define DACR_SELECT (0x1 << 0)
+
+/* REG27: HPOUT_CTRL */
+#define HPOUTL_MASK BIT(7)
+#define HPOUTL_DIS (0x0 << 7)
+#define HPOUTL_EN (0x1 << 7)
+#define HPOUTL_INIT_MASK BIT(6)
+#define HPOUTL_INIT_DIS (0x0 << 6)
+#define HPOUTL_INIT_EN (0x1 << 6)
+#define HPOUTL_MUTE_MASK BIT(5)
+#define HPOUTL_MUTE (0x0 << 5)
+#define HPOUTL_UNMUTE (0x1 << 5)
+#define HPOUTR_MASK BIT(4)
+#define HPOUTR_DIS (0x0 << 4)
+#define HPOUTR_EN (0x1 << 4)
+#define HPOUTR_INIT_MASK BIT(3)
+#define HPOUTR_INIT_DIS (0x0 << 3)
+#define HPOUTR_INIT_EN (0x1 << 3)
+#define HPOUTR_MUTE_MASK BIT(2)
+#define HPOUTR_MUTE (0x0 << 2)
+#define HPOUTR_UNMUTE (0x1 << 2)
+
+/* REG28: HPOUTL_GAIN_CTRL */
+#define HPOUTL_GAIN_MASK GENMASK(4, 0)
+
+/* REG29: HPOUTR_GAIN_CTRL */
+#define HPOUTR_GAIN_MASK GENMASK(4, 0)
+
+/* REG2a: HPOUT_POP_CTRL */
+#define HPOUTR_POP_MASK GENMASK(5, 4)
+#define HPOUTR_POP_XCHARGE (0x1 << 4)
+#define HPOUTR_POP_WORK (0x2 << 4)
+#define HPOUTL_POP_MASK GENMASK(1, 0)
+#define HPOUTL_POP_XCHARGE (0x1 << 0)
+#define HPOUTL_POP_WORK (0x2 << 0)
+
+#define RK3328_HIFI 0
+
+struct rk3328_reg_msk_val {
+ unsigned int reg;
+ unsigned int msk;
+ unsigned int val;
+};
+
+#endif
diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c
new file mode 100644
index 000000000000..3c9957b00881
--- /dev/null
+++ b/sound/soc/codecs/rk817_codec.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk817 ALSA SoC Audio driver
+//
+// Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mfd/rk808.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct rk817_codec_priv {
+ struct snd_soc_component *component;
+ struct rk808 *rk808;
+ struct clk *mclk;
+ unsigned int stereo_sysclk;
+ bool mic_in_differential;
+};
+
+/*
+ * This sets the codec up with the values defined in the default implementation including the APLL
+ * from the Rockchip vendor kernel. I do not know if these values are universal despite differing
+ * from the default values defined above and taken from the datasheet, or implementation specific.
+ * I don't have another implementation to compare from the Rockchip sources. Hard-coding for now.
+ * Additionally, I do not know according to the documentation the units accepted for the clock
+ * values, so for the moment those are left unvalidated.
+ */
+
+static int rk817_init(struct snd_soc_component *component)
+{
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_write(component, RK817_CODEC_DDAC_POPD_DACST, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DDAC_SR_LMT0, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DADC_SR_ACL0, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DTOP_VUCTIME, 0xf4);
+ if (rk817->mic_in_differential) {
+ snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, MIC_DIFF_MASK,
+ MIC_DIFF_EN);
+ }
+
+ return 0;
+}
+
+static int rk817_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ /* Set resistor value and charge pump current for PLL. */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG1, 0x58);
+ /* Set the PLL feedback clock divide value (values not documented). */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG2, 0x2d);
+ /* Set the PLL pre-divide value (values not documented). */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG3, 0x0c);
+ /* Set the PLL VCO output clock divide and PLL divided ratio of PLL High Clk (values not
+ * documented).
+ */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5);
+
+ return 0;
+}
+
+/*
+ * DDAC/DADC L/R volume setting
+ * 0db~-95db, 0.375db/step, for example:
+ * 0x00: 0dB
+ * 0xff: -95dB
+ */
+
+static const DECLARE_TLV_DB_MINMAX(rk817_vol_tlv, -9500, 0);
+
+/*
+ * PGA GAIN L/R volume setting
+ * 27db~-18db, 3db/step, for example:
+ * 0x0: -18dB
+ * 0xf: 27dB
+ */
+
+static const DECLARE_TLV_DB_MINMAX(rk817_gain_tlv, -1800, 2700);
+
+static const struct snd_kcontrol_new rk817_volume_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", RK817_CODEC_DDAC_VOLL,
+ RK817_CODEC_DDAC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", RK817_CODEC_DADC_VOLL,
+ RK817_CODEC_DADC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv),
+ SOC_DOUBLE_TLV("Mic Capture Gain", RK817_CODEC_DMIC_PGA_GAIN, 4, 0, 0xf, 0,
+ rk817_gain_tlv),
+};
+
+/* Since the speaker output and L headphone pin are internally the same, make audio path mutually
+ * exclusive with a mux.
+ */
+
+static const char *dac_mux_text[] = {
+ "HP",
+ "SPK",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(dac_enum, dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+ SOC_DAPM_ENUM("Playback Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget rk817_dapm_widgets[] = {
+
+ /* capture/playback common */
+ SND_SOC_DAPM_SUPPLY("LDO Regulator", RK817_CODEC_AREF_RTCFG1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBIAS Block", RK817_CODEC_AREF_RTCFG1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VAvg Buffer", RK817_CODEC_AREF_RTCFG1, 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL Power", RK817_CODEC_APLL_CFG5, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX1 Transfer Start", RK817_CODEC_DI2S_RXCMD_TSD, 5, 0, NULL, 0),
+
+ /* capture path common */
+ SND_SOC_DAPM_SUPPLY("ADC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC Power On", RK817_CODEC_AMIC_CFG0, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX3 Transfer Start", RK817_CODEC_DI2S_TXCR3_TXCMD, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX3 Right Justified", RK817_CODEC_DI2S_TXCR3_TXCMD, 3, 0, NULL, 0),
+
+ /* capture path L */
+ SND_SOC_DAPM_ADC("ADC L", "Capture", RK817_CODEC_AADC_CFG0, 7, 1),
+ SND_SOC_DAPM_SUPPLY("PGA L Power On", RK817_CODEC_AMIC_CFG0, 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost L1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost L2", RK817_CODEC_AMIC_CFG0, 2, 0, NULL, 0),
+
+ /* capture path R */
+ SND_SOC_DAPM_ADC("ADC R", "Capture", RK817_CODEC_AADC_CFG0, 6, 1),
+ SND_SOC_DAPM_SUPPLY("PGA R Power On", RK817_CODEC_AMIC_CFG0, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost R1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost R2", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+
+ /* playback path common */
+ SND_SOC_DAPM_SUPPLY("DAC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S RX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S RX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Bias", RK817_CODEC_ADAC_CFG1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mute Off", RK817_CODEC_DDAC_MUTE_MIXCTL, 0, 1, NULL, 0),
+
+ /* playback path speaker */
+ SND_SOC_DAPM_SUPPLY("Class D Mode", RK817_CODEC_DDAC_MUTE_MIXCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("High Pass Filter", RK817_CODEC_DDAC_MUTE_MIXCTL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("SPK DAC", "Playback", RK817_CODEC_ADAC_CFG1, 2, 1),
+ SND_SOC_DAPM_SUPPLY("Enable Class D", RK817_CODEC_ACLASSD_CFG1, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Disable Class D Mute Ramp", RK817_CODEC_ACLASSD_CFG1, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D Mute Rate 1", RK817_CODEC_ACLASSD_CFG1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D Mute Rate 2", RK817_CODEC_ACLASSD_CFG1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPP 2", RK817_CODEC_ACLASSD_CFG2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPP 3", RK817_CODEC_ACLASSD_CFG2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPN 2", RK817_CODEC_ACLASSD_CFG2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPN 3", RK817_CODEC_ACLASSD_CFG2, 0, 0, NULL, 0),
+
+ /* playback path headphones */
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", RK817_CODEC_AHP_CP, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone CP Discharge LDO", RK817_CODEC_AHP_CP, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone OStage", RK817_CODEC_AHP_CFG0, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Pre Amp", RK817_CODEC_AHP_CFG0, 5, 1, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC L", "Playback", RK817_CODEC_ADAC_CFG1, 1, 1),
+ SND_SOC_DAPM_DAC("DAC R", "Playback", RK817_CODEC_ADAC_CFG1, 0, 1),
+
+ /* Mux for input/output path selection */
+ SND_SOC_DAPM_MUX("Playback Mux", SND_SOC_NOPM, 1, 0, &dac_mux),
+
+ /* Pins for Simple Card Bindings */
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("SPKO"),
+};
+
+static const struct snd_soc_dapm_route rk817_dapm_routes[] = {
+
+ /* capture path */
+ /* left mic */
+ {"ADC L", NULL, "LDO Regulator"},
+ {"ADC L", NULL, "IBIAS Block"},
+ {"ADC L", NULL, "VAvg Buffer"},
+ {"ADC L", NULL, "PLL Power"},
+ {"ADC L", NULL, "ADC Clock"},
+ {"ADC L", NULL, "I2S TX Clock"},
+ {"ADC L", NULL, "ADC Channel Enable"},
+ {"ADC L", NULL, "I2S TX Channel Enable"},
+ {"ADC L", NULL, "I2S TX1 Transfer Start"},
+ {"MICL", NULL, "MIC Power On"},
+ {"MICL", NULL, "PGA L Power On"},
+ {"MICL", NULL, "Mic Boost L1"},
+ {"MICL", NULL, "Mic Boost L2"},
+ {"MICL", NULL, "I2S TX3 Transfer Start"},
+ {"MICL", NULL, "I2S TX3 Right Justified"},
+ {"ADC L", NULL, "MICL"},
+
+ /* right mic */
+ {"ADC R", NULL, "LDO Regulator"},
+ {"ADC R", NULL, "IBIAS Block"},
+ {"ADC R", NULL, "VAvg Buffer"},
+ {"ADC R", NULL, "PLL Power"},
+ {"ADC R", NULL, "ADC Clock"},
+ {"ADC R", NULL, "I2S TX Clock"},
+ {"ADC R", NULL, "ADC Channel Enable"},
+ {"ADC R", NULL, "I2S TX Channel Enable"},
+ {"ADC R", NULL, "I2S TX1 Transfer Start"},
+ {"MICR", NULL, "MIC Power On"},
+ {"MICR", NULL, "PGA R Power On"},
+ {"MICR", NULL, "Mic Boost R1"},
+ {"MICR", NULL, "Mic Boost R2"},
+ {"MICR", NULL, "I2S TX3 Transfer Start"},
+ {"MICR", NULL, "I2S TX3 Right Justified"},
+ {"ADC R", NULL, "MICR"},
+
+ /* playback path */
+ /* speaker path */
+ {"SPK DAC", NULL, "LDO Regulator"},
+ {"SPK DAC", NULL, "IBIAS Block"},
+ {"SPK DAC", NULL, "VAvg Buffer"},
+ {"SPK DAC", NULL, "PLL Power"},
+ {"SPK DAC", NULL, "I2S TX1 Transfer Start"},
+ {"SPK DAC", NULL, "DAC Clock"},
+ {"SPK DAC", NULL, "I2S RX Clock"},
+ {"SPK DAC", NULL, "DAC Channel Enable"},
+ {"SPK DAC", NULL, "I2S RX Channel Enable"},
+ {"SPK DAC", NULL, "Class D Mode"},
+ {"SPK DAC", NULL, "DAC Bias"},
+ {"SPK DAC", NULL, "DAC Mute Off"},
+ {"SPK DAC", NULL, "Enable Class D"},
+ {"SPK DAC", NULL, "Disable Class D Mute Ramp"},
+ {"SPK DAC", NULL, "Class D Mute Rate 1"},
+ {"SPK DAC", NULL, "Class D Mute Rate 2"},
+ {"SPK DAC", NULL, "Class D OCPP 2"},
+ {"SPK DAC", NULL, "Class D OCPP 3"},
+ {"SPK DAC", NULL, "Class D OCPN 2"},
+ {"SPK DAC", NULL, "Class D OCPN 3"},
+ {"SPK DAC", NULL, "High Pass Filter"},
+
+ /* headphone path L */
+ {"DAC L", NULL, "LDO Regulator"},
+ {"DAC L", NULL, "IBIAS Block"},
+ {"DAC L", NULL, "VAvg Buffer"},
+ {"DAC L", NULL, "PLL Power"},
+ {"DAC L", NULL, "I2S TX1 Transfer Start"},
+ {"DAC L", NULL, "DAC Clock"},
+ {"DAC L", NULL, "I2S RX Clock"},
+ {"DAC L", NULL, "DAC Channel Enable"},
+ {"DAC L", NULL, "I2S RX Channel Enable"},
+ {"DAC L", NULL, "DAC Bias"},
+ {"DAC L", NULL, "DAC Mute Off"},
+ {"DAC L", NULL, "Headphone Charge Pump"},
+ {"DAC L", NULL, "Headphone CP Discharge LDO"},
+ {"DAC L", NULL, "Headphone OStage"},
+ {"DAC L", NULL, "Headphone Pre Amp"},
+
+ /* headphone path R */
+ {"DAC R", NULL, "LDO Regulator"},
+ {"DAC R", NULL, "IBIAS Block"},
+ {"DAC R", NULL, "VAvg Buffer"},
+ {"DAC R", NULL, "PLL Power"},
+ {"DAC R", NULL, "I2S TX1 Transfer Start"},
+ {"DAC R", NULL, "DAC Clock"},
+ {"DAC R", NULL, "I2S RX Clock"},
+ {"DAC R", NULL, "DAC Channel Enable"},
+ {"DAC R", NULL, "I2S RX Channel Enable"},
+ {"DAC R", NULL, "DAC Bias"},
+ {"DAC R", NULL, "DAC Mute Off"},
+ {"DAC R", NULL, "Headphone Charge Pump"},
+ {"DAC R", NULL, "Headphone CP Discharge LDO"},
+ {"DAC R", NULL, "Headphone OStage"},
+ {"DAC R", NULL, "Headphone Pre Amp"},
+
+ /* mux path for output selection */
+ {"Playback Mux", "HP", "DAC L"},
+ {"Playback Mux", "HP", "DAC R"},
+ {"Playback Mux", "SPK", "SPK DAC"},
+ {"SPKO", NULL, "Playback Mux"},
+ {"HPOL", NULL, "Playback Mux"},
+ {"HPOR", NULL, "Playback Mux"},
+};
+
+static int rk817_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+
+ rk817->stereo_sysclk = freq;
+
+ return 0;
+}
+
+static int rk817_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ unsigned int i2s_mst = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ i2s_mst |= RK817_I2S_MODE_SLV;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ i2s_mst |= RK817_I2S_MODE_MST;
+ break;
+ default:
+ dev_err(component->dev, "%s : set master mask failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RK817_CODEC_DI2S_CKM,
+ RK817_I2S_MODE_MASK, i2s_mst);
+
+ return 0;
+}
+
+static int rk817_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2,
+ VDW_RX_16BITS);
+ snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2,
+ VDW_TX_16BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2,
+ VDW_RX_24BITS);
+ snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2,
+ VDW_TX_24BITS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rk817_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute)
+ snd_soc_component_update_bits(component,
+ RK817_CODEC_DDAC_MUTE_MIXCTL,
+ DACMT_MASK, DACMT_ENABLE);
+ else
+ snd_soc_component_update_bits(component,
+ RK817_CODEC_DDAC_MUTE_MIXCTL,
+ DACMT_MASK, DACMT_DISABLE);
+
+ return 0;
+}
+
+#define RK817_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK817_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK817_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops rk817_dai_ops = {
+ .hw_params = rk817_hw_params,
+ .set_fmt = rk817_set_dai_fmt,
+ .set_sysclk = rk817_set_dai_sysclk,
+ .mute_stream = rk817_digital_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver rk817_dai[] = {
+ {
+ .name = "rk817-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = RK817_PLAYBACK_RATES,
+ .formats = RK817_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RK817_CAPTURE_RATES,
+ .formats = RK817_FORMATS,
+ },
+ .ops = &rk817_dai_ops,
+ },
+};
+
+static int rk817_probe(struct snd_soc_component *component)
+{
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+ struct rk808 *rk808 = dev_get_drvdata(component->dev->parent);
+
+ snd_soc_component_init_regmap(component, rk808->regmap);
+ rk817->component = component;
+
+ snd_soc_component_write(component, RK817_CODEC_DTOP_LPT_SRST, 0x40);
+
+ rk817_init(component);
+
+ /* setting initial pll values so that we can continue to leverage simple-audio-card.
+ * The values aren't important since no parameters are used.
+ */
+
+ snd_soc_component_set_pll(component, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static void rk817_remove(struct snd_soc_component *component)
+{
+ snd_soc_component_exit_regmap(component);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rk817 = {
+ .probe = rk817_probe,
+ .remove = rk817_remove,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .controls = rk817_volume_controls,
+ .num_controls = ARRAY_SIZE(rk817_volume_controls),
+ .dapm_routes = rk817_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk817_dapm_routes),
+ .dapm_widgets = rk817_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk817_dapm_widgets),
+ .set_pll = rk817_set_component_pll,
+};
+
+static void rk817_codec_parse_dt_property(struct device *dev,
+ struct rk817_codec_priv *rk817)
+{
+ struct device_node *node;
+
+ node = of_get_child_by_name(dev->parent->of_node, "codec");
+ if (!node) {
+ dev_dbg(dev, "%s() Can not get child: codec\n",
+ __func__);
+ }
+
+ rk817->mic_in_differential =
+ of_property_read_bool(node, "rockchip,mic-in-differential");
+
+ of_node_put(node);
+}
+
+static int rk817_platform_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct rk817_codec_priv *rk817_codec_data;
+ int ret;
+
+ rk817_codec_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct rk817_codec_priv),
+ GFP_KERNEL);
+ if (!rk817_codec_data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rk817_codec_data);
+
+ rk817_codec_data->rk808 = rk808;
+
+ rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data);
+
+ rk817_codec_data->mclk = devm_clk_get(pdev->dev.parent, "mclk");
+ if (IS_ERR(rk817_codec_data->mclk)) {
+ dev_dbg(&pdev->dev, "Unable to get mclk\n");
+ ret = -ENXIO;
+ goto err_;
+ }
+
+ ret = clk_prepare_enable(rk817_codec_data->mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s() clock prepare error %d\n",
+ __func__, ret);
+ goto err_;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817,
+ rk817_dai, ARRAY_SIZE(rk817_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s() register codec error %d\n",
+ __func__, ret);
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ clk_disable_unprepare(rk817_codec_data->mclk);
+err_:
+ return ret;
+}
+
+static void rk817_platform_remove(struct platform_device *pdev)
+{
+ struct rk817_codec_priv *rk817 = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(rk817->mclk);
+}
+
+static struct platform_driver rk817_codec_driver = {
+ .driver = {
+ .name = "rk817-codec",
+ },
+ .probe = rk817_platform_probe,
+ .remove = rk817_platform_remove,
+};
+
+module_platform_driver(rk817_codec_driver);
+
+MODULE_DESCRIPTION("ASoC RK817 codec driver");
+MODULE_AUTHOR("binyuan <kevan.lan@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:rk817-codec");
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
index 1dc68ab08a17..d1fc1706422f 100644
--- a/sound/soc/codecs/rl6231.c
+++ b/sound/soc/codecs/rl6231.c
@@ -1,18 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rl6231.c - RL6231 class device shared support
*
* Copyright 2014 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/gcd.h>
#include "rl6231.h"
/**
@@ -71,7 +69,7 @@ EXPORT_SYMBOL_GPL(rl6231_get_pre_div);
*/
int rl6231_calc_dmic_clk(int rate)
{
- int div[] = {2, 3, 4, 6, 8, 12};
+ static const int div[] = {2, 3, 4, 6, 8, 12};
int i;
if (rate < 1000000 * div[0]) {
@@ -82,8 +80,8 @@ int rl6231_calc_dmic_clk(int rate)
for (i = 0; i < ARRAY_SIZE(div); i++) {
if ((div[i] % 3) == 0)
continue;
- /* find divider that gives DMIC frequency below 3.072MHz */
- if (3072000 * div[i] >= rate)
+ /* find divider that gives DMIC frequency below 1.536MHz */
+ if (1536000 * div[i] >= rate)
return i;
}
@@ -99,17 +97,41 @@ struct pll_calc_map {
int n;
int m;
bool m_bp;
+ bool k_bp;
};
static const struct pll_calc_map pll_preset_table[] = {
- {19200000, 24576000, 3, 30, 3, false},
+ {19200000, 4096000, 23, 14, 1, false, false},
+ {19200000, 24576000, 3, 30, 3, false, false},
+ {48000000, 3840000, 23, 2, 0, false, false},
+ {3840000, 24576000, 3, 30, 0, true, false},
+ {3840000, 22579200, 3, 5, 0, true, false},
};
+static unsigned int find_best_div(unsigned int in,
+ unsigned int max, unsigned int div)
+{
+ unsigned int d;
+
+ if (in <= max)
+ return 1;
+
+ d = in / max;
+ if (in % max)
+ d++;
+
+ while (div % d != 0)
+ d++;
+
+
+ return d;
+}
+
/**
* rl6231_pll_calc - Calcualte PLL M/N/K code.
* @freq_in: external clock provided to codec.
* @freq_out: target clock which codec works on.
- * @pll_code: Pointer to structure with M, N, K and bypass flag.
+ * @pll_code: Pointer to structure with M, N, K, m_bypass and k_bypass flag.
*
* Calcualte M/N/K code to configure PLL for codec.
*
@@ -119,10 +141,12 @@ int rl6231_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rl6231_pll_code *pll_code)
{
int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
- int i, k, red, n_t, pll_out, in_t, out_t;
- int n = 0, m = 0, m_t = 0;
- int red_t = abs(freq_out - freq_in);
- bool bypass = false;
+ int i, k, n_t;
+ int k_t, min_k, max_k, n = 0, m = 0, m_t = 0;
+ unsigned int red, pll_out, in_t, out_t, div, div_t;
+ unsigned int red_t = abs(freq_out - freq_in);
+ unsigned int f_in, f_out, f_max;
+ bool m_bypass = false, k_bypass = false;
if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
return -EINVAL;
@@ -133,52 +157,73 @@ int rl6231_pll_calc(const unsigned int freq_in,
k = pll_preset_table[i].k;
m = pll_preset_table[i].m;
n = pll_preset_table[i].n;
- bypass = pll_preset_table[i].m_bp;
+ m_bypass = pll_preset_table[i].m_bp;
+ k_bypass = pll_preset_table[i].k_bp;
pr_debug("Use preset PLL parameter table\n");
goto code_find;
}
}
- 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)
+ min_k = 80000000 / freq_out - 2;
+ max_k = 150000000 / freq_out - 2;
+ if (max_k > RL6231_PLL_K_MAX)
+ max_k = RL6231_PLL_K_MAX;
+ if (min_k > RL6231_PLL_K_MAX)
+ min_k = max_k = RL6231_PLL_K_MAX;
+ div_t = gcd(freq_in, freq_out);
+ f_max = 0xffffffff / RL6231_PLL_N_MAX;
+ div = find_best_div(freq_in, f_max, div_t);
+ f_in = freq_in / div;
+ f_out = freq_out / div;
+ k = min_k;
+ if (min_k < -1)
+ min_k = -1;
+ for (k_t = min_k; k_t <= max_k; k_t++) {
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = f_in * (n_t + 2);
+ pll_out = f_out * (k_t + 2);
+ if (in_t == pll_out) {
+ m_bypass = true;
+ n = n_t;
+ k = k_t;
goto code_find;
- red_t = red;
- }
- for (m_t = 0; m_t <= max_m; m_t++) {
- out_t = in_t / (m_t + 2);
- red = abs(out_t - pll_out);
+ }
+ out_t = in_t / (k_t + 2);
+ red = abs(f_out - out_t);
if (red < red_t) {
- bypass = false;
+ m_bypass = true;
n = n_t;
- m = m_t;
+ m = 0;
+ k = k_t;
if (red == 0)
goto code_find;
red_t = red;
}
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / ((m_t + 2) * (k_t + 2));
+ red = abs(f_out - out_t);
+ if (red < red_t) {
+ m_bypass = false;
+ n = n_t;
+ m = m_t;
+ k = k_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
}
}
pr_debug("Only get approximation about PLL\n");
code_find:
+ if (k == -1) {
+ k_bypass = true;
+ k = 0;
+ }
- pll_code->m_bp = bypass;
+ pll_code->m_bp = m_bypass;
+ pll_code->k_bp = k_bypass;
pll_code->m_code = m;
pll_code->n_code = n;
pll_code->k_code = k;
@@ -188,7 +233,8 @@ EXPORT_SYMBOL_GPL(rl6231_pll_calc);
int rl6231_get_clk_info(int sclk, int rate)
{
- int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
if (sclk <= 0 || rate <= 0)
return -EINVAL;
diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h
index 4c77b441fba2..928082750860 100644
--- a/sound/soc/codecs/rl6231.h
+++ b/sound/soc/codecs/rl6231.h
@@ -1,19 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rl6231.h - RL6231 class device shared support
*
* Copyright 2014 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RL6231_H__
#define __RL6231_H__
-#define RL6231_PLL_INP_MAX 40000000
+#define RL6231_PLL_INP_MAX 50000000
#define RL6231_PLL_INP_MIN 256000
#define RL6231_PLL_N_MAX 0x1ff
#define RL6231_PLL_K_MAX 0x1f
@@ -21,6 +18,7 @@
struct rl6231_pll_code {
bool m_bp; /* Indicates bypass m code or not. */
+ bool k_bp; /* Indicates bypass k code or not. */
int m_code;
int n_code;
int k_code;
diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c
index a4b910efbd45..fa8ac34549eb 100644
--- a/sound/soc/codecs/rl6347a.c
+++ b/sound/soc/codecs/rl6347a.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rl6347a.c - RL6347A class device shared support
*
* Copyright 2015 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can 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>
@@ -51,7 +48,7 @@ int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
if (ret == 4)
return 0;
else
- pr_err("ret=%d\n", ret);
+ dev_err(&client->dev, "I2C error %d\n", ret);
if (ret < 0)
return ret;
else
@@ -64,8 +61,8 @@ int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
struct i2c_client *client = context;
struct i2c_msg xfer[2];
int ret;
- __be32 be_reg;
- unsigned int index, vid, buf = 0x0;
+ __be32 be_reg, buf = 0x0;
+ unsigned int index, vid;
/* handle index registers */
if (reg <= 0xff) {
diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h
index e127919cb36b..761455a2fa38 100644
--- a/sound/soc/codecs/rl6347a.h
+++ b/sound/soc/codecs/rl6347a.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rl6347a.h - RL6347A class device shared support
*
* Copyright 2015 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can 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 __RL6347A_H__
#define __RL6347A_H__
diff --git a/sound/soc/codecs/rt-sdw-common.c b/sound/soc/codecs/rt-sdw-common.c
new file mode 100644
index 000000000000..ad61943ce75f
--- /dev/null
+++ b/sound/soc/codecs/rt-sdw-common.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt-sdw-common.c
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+
+/*
+ * This file defines common functions used with Realtek soundwire codecs.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/jack.h>
+
+#include "rt-sdw-common.h"
+
+/**
+ * rt_sdca_index_write - Write a value to Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @value: value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_index_write(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int value)
+{
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_write(map, addr, value);
+ if (ret < 0)
+ pr_err("Failed to set value: %06x <= %04x ret=%d\n",
+ addr, value, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_write);
+
+/**
+ * rt_sdca_index_read - Read value from Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @value: value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_index_read(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int *value)
+{
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_read(map, addr, value);
+ if (ret < 0)
+ pr_err("Failed to get value: %06x => %04x ret=%d\n",
+ addr, *value, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_read);
+
+/**
+ * rt_sdca_index_update_bits - Update value on Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+
+int rt_sdca_index_update_bits(struct regmap *map,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt_sdca_index_read(map, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt_sdca_index_write(map, nid, reg, tmp);
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_update_bits);
+
+/**
+ * rt_sdca_btn_type - Decision of button type.
+ *
+ * @buffer: UMP message buffer.
+ *
+ * A button type will be returned regarding to buffer,
+ * it returns zero if buffer cannot be recognized.
+ */
+int rt_sdca_btn_type(unsigned char *buffer)
+{
+ u8 btn_type = 0;
+ int ret = 0;
+
+ btn_type |= buffer[0] & 0xf;
+ btn_type |= (buffer[0] >> 4) & 0xf;
+ btn_type |= buffer[1] & 0xf;
+ btn_type |= (buffer[1] >> 4) & 0xf;
+
+ if (btn_type & BIT(0))
+ ret |= SND_JACK_BTN_2;
+ if (btn_type & BIT(1))
+ ret |= SND_JACK_BTN_3;
+ if (btn_type & BIT(2))
+ ret |= SND_JACK_BTN_0;
+ if (btn_type & BIT(3))
+ ret |= SND_JACK_BTN_1;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_btn_type);
+
+/**
+ * rt_sdca_headset_detect - Headset jack type detection.
+ *
+ * @map: map for setting.
+ * @entity_id: SDCA entity ID.
+ *
+ * A headset jack type will be returned, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id)
+{
+ unsigned int det_mode, jack_type;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id,
+ RT_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x03:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ jack_type = SND_JACK_HEADSET;
+ break;
+ default:
+ jack_type = 0;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id,
+ RT_SDCA_CTL_SELECTED_MODE, 0), det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ return jack_type;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_headset_detect);
+
+/**
+ * rt_sdca_button_detect - Read UMP message and decide button type.
+ *
+ * @map: map for setting.
+ * @entity_id: SDCA entity ID.
+ * @hid_buf_addr: HID buffer address.
+ * @hid_id: Report ID for HID.
+ *
+ * A button type will be returned regarding to buffer,
+ * it returns zero if buffer cannot be recognized.
+ */
+int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id,
+ unsigned int hid_buf_addr, unsigned int hid_id)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ unsigned char buf[3];
+ int ret;
+
+ /* get current UMP message owner */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(map, hid_buf_addr + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+ /* Report ID for HID */
+ if (buf[0] == hid_id)
+ btn_type = rt_sdca_btn_type(&buf[1]);
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(map,
+ SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return btn_type;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_button_detect);
+
+MODULE_DESCRIPTION("Realtek soundwire common functions");
+MODULE_AUTHOR("jack yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt-sdw-common.h b/sound/soc/codecs/rt-sdw-common.h
new file mode 100644
index 000000000000..4759516feb38
--- /dev/null
+++ b/sound/soc/codecs/rt-sdw-common.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+//
+// rt-sdw-common.h
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+
+/*
+ * This file defines common functions used with Realtek soundwire codecs.
+ */
+
+#ifndef __RT_SDW_COMMON_H__
+#define __RT_SDW_COMMON_H__
+
+#define SDCA_NUM_JACK_CODEC 0x01
+#define SDCA_NUM_MIC_ARRAY 0x02
+#define SDCA_NUM_HID 0x03
+#define SDCA_NUM_AMP 0x04
+#define RT_SDCA_CTL_SELECTED_MODE 0x01
+#define RT_SDCA_CTL_DETECTED_MODE 0x02
+#define RT_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+
+struct rt_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+#define RT_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount, \
+ xinfo, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = xinfo, \
+ .get = xget, \
+ .put = xput, \
+ .private_value = RT_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array, xinfo) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = xinfo, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+
+int rt_sdca_index_write(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int value);
+int rt_sdca_index_read(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int *value);
+int rt_sdca_index_update_bits(struct regmap *map,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val);
+int rt_sdca_btn_type(unsigned char *buffer);
+int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id);
+int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id,
+ unsigned int hid_buf_addr, unsigned int hid_id);
+
+#endif /* __RT_SDW_COMMON_H__ */
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
new file mode 100644
index 000000000000..9f34a6a35487
--- /dev/null
+++ b/sound/soc/codecs/rt1011.c
@@ -0,0 +1,2486 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rt1011.c -- rt1011 ALSA SoC amplifier component driver
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ *
+ * Author: Shuming Fan <shumingf@realtek.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1011.h"
+
+static int rt1011_calibrate(struct rt1011_priv *rt1011,
+ unsigned char cali_flag);
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1011_POWER_9, 0xa840 },
+
+ { RT1011_ADC_SET_5, 0x0a20 },
+ { RT1011_DAC_SET_2, 0xa032 },
+
+ { RT1011_SPK_PRO_DC_DET_1, 0xb00c },
+ { RT1011_SPK_PRO_DC_DET_2, 0xcccc },
+
+ { RT1011_A_TIMING_1, 0x6054 },
+
+ { RT1011_POWER_7, 0x3e55 },
+ { RT1011_POWER_8, 0x0520 },
+ { RT1011_BOOST_CON_1, 0xe188 },
+ { RT1011_POWER_4, 0x16f2 },
+
+ { RT1011_CROSS_BQ_SET_1, 0x0004 },
+ { RT1011_SIL_DET, 0xc313 },
+ { RT1011_SINE_GEN_REG_1, 0x0707 },
+
+ { RT1011_DC_CALIB_CLASSD_3, 0xcb00 },
+
+ { RT1011_DAC_SET_1, 0xe702 },
+ { RT1011_DAC_SET_3, 0x2004 },
+};
+
+static const struct reg_default rt1011_reg[] = {
+ {0x0000, 0x0000},
+ {0x0002, 0x0000},
+ {0x0004, 0xa000},
+ {0x0006, 0x0000},
+ {0x0008, 0x0003},
+ {0x000a, 0x087e},
+ {0x000c, 0x0020},
+ {0x000e, 0x9002},
+ {0x0010, 0x0000},
+ {0x0012, 0x0000},
+ {0x0020, 0x0c40},
+ {0x0022, 0x4313},
+ {0x0076, 0x0000},
+ {0x0078, 0x0000},
+ {0x007a, 0x0000},
+ {0x007c, 0x10ec},
+ {0x007d, 0x1011},
+ {0x00f0, 0x5000},
+ {0x00f2, 0x0374},
+ {0x00f3, 0x0000},
+ {0x00f4, 0x0000},
+ {0x0100, 0x0038},
+ {0x0102, 0xff02},
+ {0x0104, 0x0232},
+ {0x0106, 0x200c},
+ {0x0107, 0x0000},
+ {0x0108, 0x2f2f},
+ {0x010a, 0x2f2f},
+ {0x010c, 0x002f},
+ {0x010e, 0xe000},
+ {0x0110, 0x0820},
+ {0x0111, 0x4010},
+ {0x0112, 0x0000},
+ {0x0114, 0x0000},
+ {0x0116, 0x0000},
+ {0x0118, 0x0000},
+ {0x011a, 0x0101},
+ {0x011c, 0x4567},
+ {0x011e, 0x0000},
+ {0x0120, 0x0000},
+ {0x0122, 0x0000},
+ {0x0124, 0x0123},
+ {0x0126, 0x4567},
+ {0x0200, 0x0000},
+ {0x0300, 0xffdd},
+ {0x0302, 0x001e},
+ {0x0311, 0x0000},
+ {0x0313, 0x5254},
+ {0x0314, 0x0062},
+ {0x0316, 0x7f40},
+ {0x0319, 0x000f},
+ {0x031a, 0xffff},
+ {0x031b, 0x0000},
+ {0x031c, 0x009f},
+ {0x031d, 0xffff},
+ {0x031e, 0x0000},
+ {0x031f, 0x0000},
+ {0x0320, 0xe31c},
+ {0x0321, 0x0000},
+ {0x0322, 0x0000},
+ {0x0324, 0x0000},
+ {0x0326, 0x0002},
+ {0x0328, 0x20b2},
+ {0x0329, 0x0175},
+ {0x032a, 0x32ad},
+ {0x032b, 0x3455},
+ {0x032c, 0x0528},
+ {0x032d, 0xa800},
+ {0x032e, 0x030e},
+ {0x0330, 0x2080},
+ {0x0332, 0x0034},
+ {0x0334, 0x0000},
+ {0x0508, 0x0010},
+ {0x050a, 0x0018},
+ {0x050c, 0x0000},
+ {0x050d, 0xffff},
+ {0x050e, 0x1f1f},
+ {0x050f, 0x04ff},
+ {0x0510, 0x4020},
+ {0x0511, 0x01f0},
+ {0x0512, 0x0702},
+ {0x0516, 0xbb80},
+ {0x0517, 0xffff},
+ {0x0518, 0xffff},
+ {0x0519, 0x307f},
+ {0x051a, 0xffff},
+ {0x051b, 0x0000},
+ {0x051c, 0x0000},
+ {0x051d, 0x2000},
+ {0x051e, 0x0000},
+ {0x051f, 0x0000},
+ {0x0520, 0x0000},
+ {0x0521, 0x1001},
+ {0x0522, 0x7fff},
+ {0x0524, 0x7fff},
+ {0x0526, 0x0000},
+ {0x0528, 0x0000},
+ {0x052a, 0x0000},
+ {0x0530, 0x0401},
+ {0x0532, 0x3000},
+ {0x0534, 0x0000},
+ {0x0535, 0xffff},
+ {0x0536, 0x101c},
+ {0x0538, 0x1814},
+ {0x053a, 0x100c},
+ {0x053c, 0x0804},
+ {0x053d, 0x0000},
+ {0x053e, 0x0000},
+ {0x053f, 0x0000},
+ {0x0540, 0x0000},
+ {0x0541, 0x0000},
+ {0x0542, 0x0000},
+ {0x0543, 0x0000},
+ {0x0544, 0x001c},
+ {0x0545, 0x1814},
+ {0x0546, 0x100c},
+ {0x0547, 0x0804},
+ {0x0548, 0x0000},
+ {0x0549, 0x0000},
+ {0x054a, 0x0000},
+ {0x054b, 0x0000},
+ {0x054c, 0x0000},
+ {0x054d, 0x0000},
+ {0x054e, 0x0000},
+ {0x054f, 0x0000},
+ {0x0566, 0x0000},
+ {0x0568, 0x20f1},
+ {0x056a, 0x0007},
+ {0x0600, 0x9d00},
+ {0x0611, 0x2000},
+ {0x0612, 0x505f},
+ {0x0613, 0x0444},
+ {0x0614, 0x4000},
+ {0x0615, 0x4004},
+ {0x0616, 0x0606},
+ {0x0617, 0x8904},
+ {0x0618, 0xe021},
+ {0x0621, 0x2000},
+ {0x0622, 0x505f},
+ {0x0623, 0x0444},
+ {0x0624, 0x4000},
+ {0x0625, 0x4004},
+ {0x0626, 0x0606},
+ {0x0627, 0x8704},
+ {0x0628, 0xe021},
+ {0x0631, 0x2000},
+ {0x0632, 0x517f},
+ {0x0633, 0x0440},
+ {0x0634, 0x4000},
+ {0x0635, 0x4104},
+ {0x0636, 0x0306},
+ {0x0637, 0x8904},
+ {0x0638, 0xe021},
+ {0x0702, 0x0014},
+ {0x0704, 0x0000},
+ {0x0706, 0x0014},
+ {0x0708, 0x0000},
+ {0x070a, 0x0000},
+ {0x0710, 0x0200},
+ {0x0711, 0x0000},
+ {0x0712, 0x0200},
+ {0x0713, 0x0000},
+ {0x0720, 0x0200},
+ {0x0721, 0x0000},
+ {0x0722, 0x0000},
+ {0x0723, 0x0000},
+ {0x0724, 0x0000},
+ {0x0725, 0x0000},
+ {0x0726, 0x0000},
+ {0x0727, 0x0000},
+ {0x0728, 0x0000},
+ {0x0729, 0x0000},
+ {0x0730, 0x0200},
+ {0x0731, 0x0000},
+ {0x0732, 0x0000},
+ {0x0733, 0x0000},
+ {0x0734, 0x0000},
+ {0x0735, 0x0000},
+ {0x0736, 0x0000},
+ {0x0737, 0x0000},
+ {0x0738, 0x0000},
+ {0x0739, 0x0000},
+ {0x0740, 0x0200},
+ {0x0741, 0x0000},
+ {0x0742, 0x0000},
+ {0x0743, 0x0000},
+ {0x0744, 0x0000},
+ {0x0745, 0x0000},
+ {0x0746, 0x0000},
+ {0x0747, 0x0000},
+ {0x0748, 0x0000},
+ {0x0749, 0x0000},
+ {0x0750, 0x0200},
+ {0x0751, 0x0000},
+ {0x0752, 0x0000},
+ {0x0753, 0x0000},
+ {0x0754, 0x0000},
+ {0x0755, 0x0000},
+ {0x0756, 0x0000},
+ {0x0757, 0x0000},
+ {0x0758, 0x0000},
+ {0x0759, 0x0000},
+ {0x0760, 0x0200},
+ {0x0761, 0x0000},
+ {0x0762, 0x0000},
+ {0x0763, 0x0000},
+ {0x0764, 0x0000},
+ {0x0765, 0x0000},
+ {0x0766, 0x0000},
+ {0x0767, 0x0000},
+ {0x0768, 0x0000},
+ {0x0769, 0x0000},
+ {0x0770, 0x0200},
+ {0x0771, 0x0000},
+ {0x0772, 0x0000},
+ {0x0773, 0x0000},
+ {0x0774, 0x0000},
+ {0x0775, 0x0000},
+ {0x0776, 0x0000},
+ {0x0777, 0x0000},
+ {0x0778, 0x0000},
+ {0x0779, 0x0000},
+ {0x0780, 0x0200},
+ {0x0781, 0x0000},
+ {0x0782, 0x0000},
+ {0x0783, 0x0000},
+ {0x0784, 0x0000},
+ {0x0785, 0x0000},
+ {0x0786, 0x0000},
+ {0x0787, 0x0000},
+ {0x0788, 0x0000},
+ {0x0789, 0x0000},
+ {0x0790, 0x0200},
+ {0x0791, 0x0000},
+ {0x0792, 0x0000},
+ {0x0793, 0x0000},
+ {0x0794, 0x0000},
+ {0x0795, 0x0000},
+ {0x0796, 0x0000},
+ {0x0797, 0x0000},
+ {0x0798, 0x0000},
+ {0x0799, 0x0000},
+ {0x07a0, 0x0200},
+ {0x07a1, 0x0000},
+ {0x07a2, 0x0000},
+ {0x07a3, 0x0000},
+ {0x07a4, 0x0000},
+ {0x07a5, 0x0000},
+ {0x07a6, 0x0000},
+ {0x07a7, 0x0000},
+ {0x07a8, 0x0000},
+ {0x07a9, 0x0000},
+ {0x07b0, 0x0200},
+ {0x07b1, 0x0000},
+ {0x07b2, 0x0000},
+ {0x07b3, 0x0000},
+ {0x07b4, 0x0000},
+ {0x07b5, 0x0000},
+ {0x07b6, 0x0000},
+ {0x07b7, 0x0000},
+ {0x07b8, 0x0000},
+ {0x07b9, 0x0000},
+ {0x07c0, 0x0200},
+ {0x07c1, 0x0000},
+ {0x07c2, 0x0000},
+ {0x07c3, 0x0000},
+ {0x07c4, 0x0000},
+ {0x07c5, 0x0000},
+ {0x07c6, 0x0000},
+ {0x07c7, 0x0000},
+ {0x07c8, 0x0000},
+ {0x07c9, 0x0000},
+ {0x1000, 0x4040},
+ {0x1002, 0x6505},
+ {0x1004, 0x5405},
+ {0x1006, 0x5555},
+ {0x1007, 0x003f},
+ {0x1008, 0x7fd7},
+ {0x1009, 0x770f},
+ {0x100a, 0xfffe},
+ {0x100b, 0xe000},
+ {0x100c, 0x0000},
+ {0x100d, 0x0007},
+ {0x1010, 0xa433},
+ {0x1020, 0x0000},
+ {0x1022, 0x0000},
+ {0x1024, 0x0000},
+ {0x1200, 0x5a01},
+ {0x1202, 0x6324},
+ {0x1204, 0x0b00},
+ {0x1206, 0x0000},
+ {0x1208, 0x0000},
+ {0x120a, 0x0024},
+ {0x120c, 0x0000},
+ {0x120e, 0x000e},
+ {0x1210, 0x0000},
+ {0x1212, 0x0000},
+ {0x1300, 0x0701},
+ {0x1302, 0x12f9},
+ {0x1304, 0x3405},
+ {0x1305, 0x0844},
+ {0x1306, 0x5611},
+ {0x1308, 0x555e},
+ {0x130a, 0xa605},
+ {0x130c, 0x2000},
+ {0x130e, 0x0000},
+ {0x130f, 0x0001},
+ {0x1310, 0xaa48},
+ {0x1312, 0x0285},
+ {0x1314, 0xaaaa},
+ {0x1316, 0xaaa0},
+ {0x1318, 0x2aaa},
+ {0x131a, 0xaa07},
+ {0x1322, 0x0029},
+ {0x1323, 0x4a52},
+ {0x1324, 0x002c},
+ {0x1325, 0x0b02},
+ {0x1326, 0x002d},
+ {0x1327, 0x6b5a},
+ {0x1328, 0x002e},
+ {0x1329, 0xcbb2},
+ {0x132a, 0x0030},
+ {0x132b, 0x2c0b},
+ {0x1330, 0x0031},
+ {0x1331, 0x8c63},
+ {0x1332, 0x0032},
+ {0x1333, 0xecbb},
+ {0x1334, 0x0034},
+ {0x1335, 0x4d13},
+ {0x1336, 0x0037},
+ {0x1337, 0x0dc3},
+ {0x1338, 0x003d},
+ {0x1339, 0xef7b},
+ {0x133a, 0x0044},
+ {0x133b, 0xd134},
+ {0x133c, 0x0047},
+ {0x133d, 0x91e4},
+ {0x133e, 0x004d},
+ {0x133f, 0xc370},
+ {0x1340, 0x0053},
+ {0x1341, 0xf4fd},
+ {0x1342, 0x0060},
+ {0x1343, 0x5816},
+ {0x1344, 0x006c},
+ {0x1345, 0xbb2e},
+ {0x1346, 0x0072},
+ {0x1347, 0xecbb},
+ {0x1348, 0x0076},
+ {0x1349, 0x5d97},
+ {0x1500, 0x0702},
+ {0x1502, 0x002f},
+ {0x1504, 0x0000},
+ {0x1510, 0x0064},
+ {0x1512, 0x0000},
+ {0x1514, 0xdf47},
+ {0x1516, 0x079c},
+ {0x1518, 0xfbf5},
+ {0x151a, 0x00bc},
+ {0x151c, 0x3b85},
+ {0x151e, 0x02b3},
+ {0x1520, 0x3333},
+ {0x1522, 0x0000},
+ {0x1524, 0x4000},
+ {0x1528, 0x0064},
+ {0x152a, 0x0000},
+ {0x152c, 0x0000},
+ {0x152e, 0x0000},
+ {0x1530, 0x0000},
+ {0x1532, 0x0000},
+ {0x1534, 0x0000},
+ {0x1536, 0x0000},
+ {0x1538, 0x0040},
+ {0x1539, 0x0000},
+ {0x153a, 0x0040},
+ {0x153b, 0x0000},
+ {0x153c, 0x0064},
+ {0x153e, 0x0bf9},
+ {0x1540, 0xb2a9},
+ {0x1544, 0x0200},
+ {0x1546, 0x0000},
+ {0x1548, 0x00ca},
+ {0x1552, 0x03ff},
+ {0x1554, 0x017f},
+ {0x1556, 0x017f},
+ {0x155a, 0x0000},
+ {0x155c, 0x0000},
+ {0x1560, 0x0040},
+ {0x1562, 0x0000},
+ {0x1570, 0x03ff},
+ {0x1571, 0xdcff},
+ {0x1572, 0x1e00},
+ {0x1573, 0x224f},
+ {0x1574, 0x0000},
+ {0x1575, 0x0000},
+ {0x1576, 0x1e00},
+ {0x1577, 0x0000},
+ {0x1578, 0x0000},
+ {0x1579, 0x1128},
+ {0x157a, 0x03ff},
+ {0x157b, 0xdcff},
+ {0x157c, 0x1e00},
+ {0x157d, 0x224f},
+ {0x157e, 0x0000},
+ {0x157f, 0x0000},
+ {0x1580, 0x1e00},
+ {0x1581, 0x0000},
+ {0x1582, 0x0000},
+ {0x1583, 0x1128},
+ {0x1590, 0x03ff},
+ {0x1591, 0xdcff},
+ {0x1592, 0x1e00},
+ {0x1593, 0x224f},
+ {0x1594, 0x0000},
+ {0x1595, 0x0000},
+ {0x1596, 0x1e00},
+ {0x1597, 0x0000},
+ {0x1598, 0x0000},
+ {0x1599, 0x1128},
+ {0x159a, 0x03ff},
+ {0x159b, 0xdcff},
+ {0x159c, 0x1e00},
+ {0x159d, 0x224f},
+ {0x159e, 0x0000},
+ {0x159f, 0x0000},
+ {0x15a0, 0x1e00},
+ {0x15a1, 0x0000},
+ {0x15a2, 0x0000},
+ {0x15a3, 0x1128},
+ {0x15b0, 0x007f},
+ {0x15b1, 0xffff},
+ {0x15b2, 0x007f},
+ {0x15b3, 0xffff},
+ {0x15b4, 0x007f},
+ {0x15b5, 0xffff},
+ {0x15b8, 0x007f},
+ {0x15b9, 0xffff},
+ {0x15bc, 0x0000},
+ {0x15bd, 0x0000},
+ {0x15be, 0xff00},
+ {0x15bf, 0x0000},
+ {0x15c0, 0xff00},
+ {0x15c1, 0x0000},
+ {0x15c3, 0xfc00},
+ {0x15c4, 0xbb80},
+ {0x15d0, 0x0000},
+ {0x15d1, 0x0000},
+ {0x15d2, 0x0000},
+ {0x15d3, 0x0000},
+ {0x15d4, 0x0000},
+ {0x15d5, 0x0000},
+ {0x15d6, 0x0000},
+ {0x15d7, 0x0000},
+ {0x15d8, 0x0200},
+ {0x15d9, 0x0000},
+ {0x15da, 0x0000},
+ {0x15db, 0x0000},
+ {0x15dc, 0x0000},
+ {0x15dd, 0x0000},
+ {0x15de, 0x0000},
+ {0x15df, 0x0000},
+ {0x15e0, 0x0000},
+ {0x15e1, 0x0000},
+ {0x15e2, 0x0200},
+ {0x15e3, 0x0000},
+ {0x15e4, 0x0000},
+ {0x15e5, 0x0000},
+ {0x15e6, 0x0000},
+ {0x15e7, 0x0000},
+ {0x15e8, 0x0000},
+ {0x15e9, 0x0000},
+ {0x15ea, 0x0000},
+ {0x15eb, 0x0000},
+ {0x15ec, 0x0200},
+ {0x15ed, 0x0000},
+ {0x15ee, 0x0000},
+ {0x15ef, 0x0000},
+ {0x15f0, 0x0000},
+ {0x15f1, 0x0000},
+ {0x15f2, 0x0000},
+ {0x15f3, 0x0000},
+ {0x15f4, 0x0000},
+ {0x15f5, 0x0000},
+ {0x15f6, 0x0200},
+ {0x15f7, 0x0200},
+ {0x15f8, 0x8200},
+ {0x15f9, 0x0000},
+ {0x1600, 0x007d},
+ {0x1601, 0xa178},
+ {0x1602, 0x00c2},
+ {0x1603, 0x5383},
+ {0x1604, 0x0000},
+ {0x1605, 0x02c1},
+ {0x1606, 0x007d},
+ {0x1607, 0xa178},
+ {0x1608, 0x00c2},
+ {0x1609, 0x5383},
+ {0x160a, 0x003e},
+ {0x160b, 0xd37d},
+ {0x1611, 0x3210},
+ {0x1612, 0x7418},
+ {0x1613, 0xc0ff},
+ {0x1614, 0x0000},
+ {0x1615, 0x00ff},
+ {0x1616, 0x0000},
+ {0x1617, 0x0000},
+ {0x1621, 0x6210},
+ {0x1622, 0x7418},
+ {0x1623, 0xc0ff},
+ {0x1624, 0x0000},
+ {0x1625, 0x00ff},
+ {0x1626, 0x0000},
+ {0x1627, 0x0000},
+ {0x1631, 0x3a14},
+ {0x1632, 0x7418},
+ {0x1633, 0xc3ff},
+ {0x1634, 0x0000},
+ {0x1635, 0x00ff},
+ {0x1636, 0x0000},
+ {0x1637, 0x0000},
+ {0x1638, 0x0000},
+ {0x163a, 0x0000},
+ {0x163c, 0x0000},
+ {0x163e, 0x0000},
+ {0x1640, 0x0000},
+ {0x1642, 0x0000},
+ {0x1644, 0x0000},
+ {0x1646, 0x0000},
+ {0x1648, 0x0000},
+ {0x1650, 0x0000},
+ {0x1652, 0x0000},
+ {0x1654, 0x0000},
+ {0x1656, 0x0000},
+ {0x1658, 0x0000},
+ {0x1660, 0x0000},
+ {0x1662, 0x0000},
+ {0x1664, 0x0000},
+ {0x1666, 0x0000},
+ {0x1668, 0x0000},
+ {0x1670, 0x0000},
+ {0x1672, 0x0000},
+ {0x1674, 0x0000},
+ {0x1676, 0x0000},
+ {0x1678, 0x0000},
+ {0x1680, 0x0000},
+ {0x1682, 0x0000},
+ {0x1684, 0x0000},
+ {0x1686, 0x0000},
+ {0x1688, 0x0000},
+ {0x1690, 0x0000},
+ {0x1692, 0x0000},
+ {0x1694, 0x0000},
+ {0x1696, 0x0000},
+ {0x1698, 0x0000},
+ {0x1700, 0x0000},
+ {0x1702, 0x0000},
+ {0x1704, 0x0000},
+ {0x1706, 0x0000},
+ {0x1708, 0x0000},
+ {0x1710, 0x0000},
+ {0x1712, 0x0000},
+ {0x1714, 0x0000},
+ {0x1716, 0x0000},
+ {0x1718, 0x0000},
+ {0x1720, 0x0000},
+ {0x1722, 0x0000},
+ {0x1724, 0x0000},
+ {0x1726, 0x0000},
+ {0x1728, 0x0000},
+ {0x1730, 0x0000},
+ {0x1732, 0x0000},
+ {0x1734, 0x0000},
+ {0x1736, 0x0000},
+ {0x1738, 0x0000},
+ {0x173a, 0x0000},
+ {0x173c, 0x0000},
+ {0x173e, 0x0000},
+ {0x17bb, 0x0500},
+ {0x17bd, 0x0004},
+ {0x17bf, 0x0004},
+ {0x17c1, 0x0004},
+ {0x17c2, 0x7fff},
+ {0x17c3, 0x0000},
+ {0x17c5, 0x0000},
+ {0x17c7, 0x0000},
+ {0x17c9, 0x0000},
+ {0x17cb, 0x2010},
+ {0x17cd, 0x0000},
+ {0x17cf, 0x0000},
+ {0x17d1, 0x0000},
+ {0x17d3, 0x0000},
+ {0x17d5, 0x0000},
+ {0x17d7, 0x0000},
+ {0x17d9, 0x0000},
+ {0x17db, 0x0000},
+ {0x17dd, 0x0000},
+ {0x17df, 0x0000},
+ {0x17e1, 0x0000},
+ {0x17e3, 0x0000},
+ {0x17e5, 0x0000},
+ {0x17e7, 0x0000},
+ {0x17e9, 0x0000},
+ {0x17eb, 0x0000},
+ {0x17ed, 0x0000},
+ {0x17ef, 0x0000},
+ {0x17f1, 0x0000},
+ {0x17f3, 0x0000},
+ {0x17f5, 0x0000},
+ {0x17f7, 0x0000},
+ {0x17f9, 0x0000},
+ {0x17fb, 0x0000},
+ {0x17fd, 0x0000},
+ {0x17ff, 0x0000},
+ {0x1801, 0x0000},
+ {0x1803, 0x0000},
+};
+
+static int rt1011_reg_init(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ regmap_multi_reg_write(rt1011->regmap,
+ init_list, ARRAY_SIZE(init_list));
+ return 0;
+}
+
+static bool rt1011_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1011_RESET:
+ case RT1011_SRC_2:
+ case RT1011_CLK_DET:
+ case RT1011_SIL_DET:
+ case RT1011_VERSION_ID:
+ case RT1011_VENDOR_ID:
+ case RT1011_DEVICE_ID:
+ case RT1011_DUM_RO:
+ case RT1011_DAC_SET_3:
+ case RT1011_PWM_CAL:
+ case RT1011_SPK_VOL_TEST_OUT:
+ case RT1011_VBAT_VOL_DET_1:
+ case RT1011_VBAT_TEST_OUT_1:
+ case RT1011_VBAT_TEST_OUT_2:
+ case RT1011_VBAT_PROTECTION:
+ case RT1011_VBAT_DET:
+ case RT1011_BOOST_CON_1:
+ case RT1011_SHORT_CIRCUIT_DET_1:
+ case RT1011_SPK_TEMP_PROTECT_3:
+ case RT1011_SPK_TEMP_PROTECT_6:
+ case RT1011_SPK_PRO_DC_DET_3:
+ case RT1011_SPK_PRO_DC_DET_7:
+ case RT1011_SPK_PRO_DC_DET_8:
+ case RT1011_SPL_1:
+ case RT1011_SPL_4:
+ case RT1011_EXCUR_PROTECT_1:
+ case RT1011_CROSS_BQ_SET_1:
+ case RT1011_CROSS_BQ_SET_2:
+ case RT1011_BQ_SET_0:
+ case RT1011_BQ_SET_1:
+ case RT1011_BQ_SET_2:
+ case RT1011_TEST_PAD_STATUS:
+ case RT1011_DC_CALIB_CLASSD_1:
+ case RT1011_DC_CALIB_CLASSD_5:
+ case RT1011_DC_CALIB_CLASSD_6:
+ case RT1011_DC_CALIB_CLASSD_7:
+ case RT1011_DC_CALIB_CLASSD_8:
+ case RT1011_SINE_GEN_REG_2:
+ case RT1011_STP_CALIB_RS_TEMP:
+ case RT1011_SPK_RESISTANCE_1:
+ case RT1011_SPK_RESISTANCE_2:
+ case RT1011_SPK_THERMAL:
+ case RT1011_ALC_BK_GAIN_O:
+ case RT1011_ALC_BK_GAIN_O_PRE:
+ case RT1011_SPK_DC_O_23_16:
+ case RT1011_SPK_DC_O_15_0:
+ case RT1011_INIT_RECIPROCAL_SYN_24_16:
+ case RT1011_INIT_RECIPROCAL_SYN_15_0:
+ case RT1011_SPK_EXCURSION_23_16:
+ case RT1011_SPK_EXCURSION_15_0:
+ case RT1011_SEP_MAIN_OUT_23_16:
+ case RT1011_SEP_MAIN_OUT_15_0:
+ case RT1011_ALC_DRC_HB_INTERNAL_5:
+ case RT1011_ALC_DRC_HB_INTERNAL_6:
+ case RT1011_ALC_DRC_HB_INTERNAL_7:
+ case RT1011_ALC_DRC_BB_INTERNAL_5:
+ case RT1011_ALC_DRC_BB_INTERNAL_6:
+ case RT1011_ALC_DRC_BB_INTERNAL_7:
+ case RT1011_ALC_DRC_POS_INTERNAL_5:
+ case RT1011_ALC_DRC_POS_INTERNAL_6:
+ case RT1011_ALC_DRC_POS_INTERNAL_7:
+ case RT1011_ALC_DRC_POS_INTERNAL_8:
+ case RT1011_ALC_DRC_POS_INTERNAL_9:
+ case RT1011_ALC_DRC_POS_INTERNAL_10:
+ case RT1011_ALC_DRC_POS_INTERNAL_11:
+ case RT1011_IRQ_1:
+ case RT1011_EFUSE_CONTROL_1:
+ case RT1011_EFUSE_CONTROL_2:
+ case RT1011_EFUSE_MATCH_DONE ... RT1011_EFUSE_READ_R0_3_15_0:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1011_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1011_RESET:
+ case RT1011_CLK_1:
+ case RT1011_CLK_2:
+ case RT1011_CLK_3:
+ case RT1011_CLK_4:
+ case RT1011_PLL_1:
+ case RT1011_PLL_2:
+ case RT1011_SRC_1:
+ case RT1011_SRC_2:
+ case RT1011_SRC_3:
+ case RT1011_CLK_DET:
+ case RT1011_SIL_DET:
+ case RT1011_PRIV_INDEX:
+ case RT1011_PRIV_DATA:
+ case RT1011_CUSTOMER_ID:
+ case RT1011_FM_VER:
+ case RT1011_VERSION_ID:
+ case RT1011_VENDOR_ID:
+ case RT1011_DEVICE_ID:
+ case RT1011_DUM_RW_0:
+ case RT1011_DUM_YUN:
+ case RT1011_DUM_RW_1:
+ case RT1011_DUM_RO:
+ case RT1011_MAN_I2C_DEV:
+ case RT1011_DAC_SET_1:
+ case RT1011_DAC_SET_2:
+ case RT1011_DAC_SET_3:
+ case RT1011_ADC_SET:
+ case RT1011_ADC_SET_1:
+ case RT1011_ADC_SET_2:
+ case RT1011_ADC_SET_3:
+ case RT1011_ADC_SET_4:
+ case RT1011_ADC_SET_5:
+ case RT1011_TDM_TOTAL_SET:
+ case RT1011_TDM1_SET_TCON:
+ case RT1011_TDM1_SET_1:
+ case RT1011_TDM1_SET_2:
+ case RT1011_TDM1_SET_3:
+ case RT1011_TDM1_SET_4:
+ case RT1011_TDM1_SET_5:
+ case RT1011_TDM2_SET_1:
+ case RT1011_TDM2_SET_2:
+ case RT1011_TDM2_SET_3:
+ case RT1011_TDM2_SET_4:
+ case RT1011_TDM2_SET_5:
+ case RT1011_PWM_CAL:
+ case RT1011_MIXER_1:
+ case RT1011_MIXER_2:
+ case RT1011_ADRC_LIMIT:
+ case RT1011_A_PRO:
+ case RT1011_A_TIMING_1:
+ case RT1011_A_TIMING_2:
+ case RT1011_A_TEMP_SEN:
+ case RT1011_SPK_VOL_DET_1:
+ case RT1011_SPK_VOL_DET_2:
+ case RT1011_SPK_VOL_TEST_OUT:
+ case RT1011_VBAT_VOL_DET_1:
+ case RT1011_VBAT_VOL_DET_2:
+ case RT1011_VBAT_TEST_OUT_1:
+ case RT1011_VBAT_TEST_OUT_2:
+ case RT1011_VBAT_PROTECTION:
+ case RT1011_VBAT_DET:
+ case RT1011_POWER_1:
+ case RT1011_POWER_2:
+ case RT1011_POWER_3:
+ case RT1011_POWER_4:
+ case RT1011_POWER_5:
+ case RT1011_POWER_6:
+ case RT1011_POWER_7:
+ case RT1011_POWER_8:
+ case RT1011_POWER_9:
+ case RT1011_CLASS_D_POS:
+ case RT1011_BOOST_CON_1:
+ case RT1011_BOOST_CON_2:
+ case RT1011_ANALOG_CTRL:
+ case RT1011_POWER_SEQ:
+ case RT1011_SHORT_CIRCUIT_DET_1:
+ case RT1011_SHORT_CIRCUIT_DET_2:
+ case RT1011_SPK_TEMP_PROTECT_0:
+ case RT1011_SPK_TEMP_PROTECT_1:
+ case RT1011_SPK_TEMP_PROTECT_2:
+ case RT1011_SPK_TEMP_PROTECT_3:
+ case RT1011_SPK_TEMP_PROTECT_4:
+ case RT1011_SPK_TEMP_PROTECT_5:
+ case RT1011_SPK_TEMP_PROTECT_6:
+ case RT1011_SPK_TEMP_PROTECT_7:
+ case RT1011_SPK_TEMP_PROTECT_8:
+ case RT1011_SPK_TEMP_PROTECT_9:
+ case RT1011_SPK_PRO_DC_DET_1:
+ case RT1011_SPK_PRO_DC_DET_2:
+ case RT1011_SPK_PRO_DC_DET_3:
+ case RT1011_SPK_PRO_DC_DET_4:
+ case RT1011_SPK_PRO_DC_DET_5:
+ case RT1011_SPK_PRO_DC_DET_6:
+ case RT1011_SPK_PRO_DC_DET_7:
+ case RT1011_SPK_PRO_DC_DET_8:
+ case RT1011_SPL_1:
+ case RT1011_SPL_2:
+ case RT1011_SPL_3:
+ case RT1011_SPL_4:
+ case RT1011_THER_FOLD_BACK_1:
+ case RT1011_THER_FOLD_BACK_2:
+ case RT1011_EXCUR_PROTECT_1:
+ case RT1011_EXCUR_PROTECT_2:
+ case RT1011_EXCUR_PROTECT_3:
+ case RT1011_EXCUR_PROTECT_4:
+ case RT1011_BAT_GAIN_1:
+ case RT1011_BAT_GAIN_2:
+ case RT1011_BAT_GAIN_3:
+ case RT1011_BAT_GAIN_4:
+ case RT1011_BAT_GAIN_5:
+ case RT1011_BAT_GAIN_6:
+ case RT1011_BAT_GAIN_7:
+ case RT1011_BAT_GAIN_8:
+ case RT1011_BAT_GAIN_9:
+ case RT1011_BAT_GAIN_10:
+ case RT1011_BAT_GAIN_11:
+ case RT1011_BAT_RT_THMAX_1:
+ case RT1011_BAT_RT_THMAX_2:
+ case RT1011_BAT_RT_THMAX_3:
+ case RT1011_BAT_RT_THMAX_4:
+ case RT1011_BAT_RT_THMAX_5:
+ case RT1011_BAT_RT_THMAX_6:
+ case RT1011_BAT_RT_THMAX_7:
+ case RT1011_BAT_RT_THMAX_8:
+ case RT1011_BAT_RT_THMAX_9:
+ case RT1011_BAT_RT_THMAX_10:
+ case RT1011_BAT_RT_THMAX_11:
+ case RT1011_BAT_RT_THMAX_12:
+ case RT1011_SPREAD_SPECTURM:
+ case RT1011_PRO_GAIN_MODE:
+ case RT1011_RT_DRC_CROSS:
+ case RT1011_RT_DRC_HB_1:
+ case RT1011_RT_DRC_HB_2:
+ case RT1011_RT_DRC_HB_3:
+ case RT1011_RT_DRC_HB_4:
+ case RT1011_RT_DRC_HB_5:
+ case RT1011_RT_DRC_HB_6:
+ case RT1011_RT_DRC_HB_7:
+ case RT1011_RT_DRC_HB_8:
+ case RT1011_RT_DRC_BB_1:
+ case RT1011_RT_DRC_BB_2:
+ case RT1011_RT_DRC_BB_3:
+ case RT1011_RT_DRC_BB_4:
+ case RT1011_RT_DRC_BB_5:
+ case RT1011_RT_DRC_BB_6:
+ case RT1011_RT_DRC_BB_7:
+ case RT1011_RT_DRC_BB_8:
+ case RT1011_RT_DRC_POS_1:
+ case RT1011_RT_DRC_POS_2:
+ case RT1011_RT_DRC_POS_3:
+ case RT1011_RT_DRC_POS_4:
+ case RT1011_RT_DRC_POS_5:
+ case RT1011_RT_DRC_POS_6:
+ case RT1011_RT_DRC_POS_7:
+ case RT1011_RT_DRC_POS_8:
+ case RT1011_CROSS_BQ_SET_1:
+ case RT1011_CROSS_BQ_SET_2:
+ case RT1011_BQ_SET_0:
+ case RT1011_BQ_SET_1:
+ case RT1011_BQ_SET_2:
+ case RT1011_BQ_PRE_GAIN_28_16:
+ case RT1011_BQ_PRE_GAIN_15_0:
+ case RT1011_BQ_POST_GAIN_28_16:
+ case RT1011_BQ_POST_GAIN_15_0:
+ case RT1011_BQ_H0_28_16 ... RT1011_BQ_A2_15_0:
+ case RT1011_BQ_1_H0_28_16 ... RT1011_BQ_1_A2_15_0:
+ case RT1011_BQ_2_H0_28_16 ... RT1011_BQ_2_A2_15_0:
+ case RT1011_BQ_3_H0_28_16 ... RT1011_BQ_3_A2_15_0:
+ case RT1011_BQ_4_H0_28_16 ... RT1011_BQ_4_A2_15_0:
+ case RT1011_BQ_5_H0_28_16 ... RT1011_BQ_5_A2_15_0:
+ case RT1011_BQ_6_H0_28_16 ... RT1011_BQ_6_A2_15_0:
+ case RT1011_BQ_7_H0_28_16 ... RT1011_BQ_7_A2_15_0:
+ case RT1011_BQ_8_H0_28_16 ... RT1011_BQ_8_A2_15_0:
+ case RT1011_BQ_9_H0_28_16 ... RT1011_BQ_9_A2_15_0:
+ case RT1011_BQ_10_H0_28_16 ... RT1011_BQ_10_A2_15_0:
+ case RT1011_TEST_PAD_STATUS ... RT1011_PLL_INTERNAL_SET:
+ case RT1011_TEST_OUT_1 ... RT1011_TEST_OUT_3:
+ case RT1011_DC_CALIB_CLASSD_1 ... RT1011_DC_CALIB_CLASSD_10:
+ case RT1011_CLASSD_INTERNAL_SET_1 ... RT1011_VREF_LV_1:
+ case RT1011_SMART_BOOST_TIMING_1 ... RT1011_SMART_BOOST_TIMING_36:
+ case RT1011_SINE_GEN_REG_1 ... RT1011_SINE_GEN_REG_3:
+ case RT1011_STP_INITIAL_RS_TEMP ... RT1011_SPK_THERMAL:
+ case RT1011_STP_OTP_TH ... RT1011_INIT_RECIPROCAL_SYN_15_0:
+ case RT1011_STP_BQ_1_A1_L_28_16 ... RT1011_STP_BQ_1_H0_R_15_0:
+ case RT1011_STP_BQ_2_A1_L_28_16 ... RT1011_SEP_RE_REG_15_0:
+ case RT1011_DRC_CF_PARAMS_1 ... RT1011_DRC_CF_PARAMS_12:
+ case RT1011_ALC_DRC_HB_INTERNAL_1 ... RT1011_ALC_DRC_HB_INTERNAL_7:
+ case RT1011_ALC_DRC_BB_INTERNAL_1 ... RT1011_ALC_DRC_BB_INTERNAL_7:
+ case RT1011_ALC_DRC_POS_INTERNAL_1 ... RT1011_ALC_DRC_POS_INTERNAL_8:
+ case RT1011_ALC_DRC_POS_INTERNAL_9 ... RT1011_BQ_1_PARAMS_CHECK_5:
+ case RT1011_BQ_2_PARAMS_CHECK_1 ... RT1011_BQ_2_PARAMS_CHECK_5:
+ case RT1011_BQ_3_PARAMS_CHECK_1 ... RT1011_BQ_3_PARAMS_CHECK_5:
+ case RT1011_BQ_4_PARAMS_CHECK_1 ... RT1011_BQ_4_PARAMS_CHECK_5:
+ case RT1011_BQ_5_PARAMS_CHECK_1 ... RT1011_BQ_5_PARAMS_CHECK_5:
+ case RT1011_BQ_6_PARAMS_CHECK_1 ... RT1011_BQ_6_PARAMS_CHECK_5:
+ case RT1011_BQ_7_PARAMS_CHECK_1 ... RT1011_BQ_7_PARAMS_CHECK_5:
+ case RT1011_BQ_8_PARAMS_CHECK_1 ... RT1011_BQ_8_PARAMS_CHECK_5:
+ case RT1011_BQ_9_PARAMS_CHECK_1 ... RT1011_BQ_9_PARAMS_CHECK_5:
+ case RT1011_BQ_10_PARAMS_CHECK_1 ... RT1011_BQ_10_PARAMS_CHECK_5:
+ case RT1011_IRQ_1 ... RT1011_PART_NUMBER_EFUSE:
+ case RT1011_EFUSE_CONTROL_1 ... RT1011_EFUSE_READ_R0_3_15_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const rt1011_din_source_select[] = {
+ "Left",
+ "Right",
+ "Left + Right average",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_din_source_enum, RT1011_CROSS_BQ_SET_1, 5,
+ rt1011_din_source_select);
+
+static const char * const rt1011_tdm_data_out_select[] = {
+ "TDM_O_LR", "BQ1", "DVOL", "BQ10", "ALC", "DMIX", "ADC_SRC_LR",
+ "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS",
+ "SEP_O_GAIN", "ALC_BK_GAIN", "STP_V_C", "DMIX_ABST"
+};
+
+static const char * const rt1011_tdm_l_ch_data_select[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7"
+};
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_l_dac1_enum, RT1011_TDM1_SET_4, 12,
+ rt1011_tdm_l_ch_data_select);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_l_dac1_enum, RT1011_TDM2_SET_4, 12,
+ rt1011_tdm_l_ch_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_dat_enum,
+ RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_loc_enum, RT1011_TDM1_SET_2, 0,
+ rt1011_tdm_l_ch_data_select);
+
+static const char * const rt1011_adc_data_mode_select[] = {
+ "Stereo", "Mono"
+};
+static SOC_ENUM_SINGLE_DECL(rt1011_adc_dout_mode_enum, RT1011_TDM1_SET_1, 12,
+ rt1011_adc_data_mode_select);
+
+static const char * const rt1011_tdm_adc_data_len_control[] = {
+ "1CH", "2CH", "3CH", "4CH", "5CH", "6CH", "7CH", "8CH"
+};
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_dout_len_enum, RT1011_TDM1_SET_2, 13,
+ rt1011_tdm_adc_data_len_control);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_dout_len_enum, RT1011_TDM2_SET_2, 13,
+ rt1011_tdm_adc_data_len_control);
+
+static const char * const rt1011_tdm_adc_swap_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6,
+ rt1011_tdm_adc_swap_select);
+static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4,
+ rt1011_tdm_adc_swap_select);
+
+static void rt1011_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT1011_RESET, 0);
+}
+
+static int rt1011_recv_spk_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1011->recv_spk_mode;
+
+ return 0;
+}
+
+static int rt1011_recv_spk_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ if (ucontrol->value.integer.value[0] == rt1011->recv_spk_mode)
+ return 0;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ rt1011->recv_spk_mode = ucontrol->value.integer.value[0];
+
+ if (rt1011->recv_spk_mode) {
+
+ /* 1: recevier mode on */
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_3,
+ RT1011_REG_GAIN_CLASSD_RI_SPK_MASK,
+ RT1011_REG_GAIN_CLASSD_RI_410K);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_RECV_MODE_SPK_MASK,
+ RT1011_RECV_MODE);
+ } else {
+ /* 0: speaker mode on */
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_3,
+ RT1011_REG_GAIN_CLASSD_RI_SPK_MASK,
+ RT1011_REG_GAIN_CLASSD_RI_72P5K);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_RECV_MODE_SPK_MASK,
+ RT1011_SPK_MODE);
+ }
+ }
+
+ return 0;
+}
+
+static bool rt1011_validate_bq_drc_coeff(unsigned short reg)
+{
+ if ((reg == RT1011_DAC_SET_1) ||
+ (reg >= RT1011_ADC_SET && reg <= RT1011_ADC_SET_1) ||
+ (reg == RT1011_ADC_SET_4) || (reg == RT1011_ADC_SET_5) ||
+ (reg == RT1011_MIXER_1) ||
+ (reg == RT1011_A_TIMING_1) ||
+ (reg >= RT1011_POWER_7 && reg <= RT1011_POWER_8) ||
+ (reg == RT1011_CLASS_D_POS) || (reg == RT1011_ANALOG_CTRL) ||
+ (reg >= RT1011_SPK_TEMP_PROTECT_0 && reg <= RT1011_SPK_TEMP_PROTECT_6) ||
+ (reg >= RT1011_SPK_PRO_DC_DET_5 && reg <= RT1011_BAT_GAIN_1) ||
+ (reg >= RT1011_RT_DRC_CROSS && reg <= RT1011_RT_DRC_POS_8) ||
+ (reg >= RT1011_CROSS_BQ_SET_1 && reg <= RT1011_BQ_10_A2_15_0) ||
+ (reg >= RT1011_SMART_BOOST_TIMING_1 && reg <= RT1011_SMART_BOOST_TIMING_36) ||
+ (reg == RT1011_SINE_GEN_REG_1) ||
+ (reg >= RT1011_STP_ALPHA_RECIPROCAL_MSB && reg <= RT1011_BQ_6_PARAMS_CHECK_5) ||
+ (reg >= RT1011_BQ_7_PARAMS_CHECK_1 && reg <= RT1011_BQ_10_PARAMS_CHECK_5))
+ return true;
+
+ return false;
+}
+
+static int rt1011_bq_drc_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+ struct rt1011_bq_drc_params *bq_drc_info;
+ struct rt1011_bq_drc_params *params =
+ (struct rt1011_bq_drc_params *)ucontrol->value.integer.value;
+ unsigned int i, mode_idx = 0;
+
+ if (strstr(ucontrol->id.name, "AdvanceMode Initial Set"))
+ mode_idx = RT1011_ADVMODE_INITIAL_SET;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_SEP_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode EQ BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_EQ_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode BQ UI Coeff"))
+ mode_idx = RT1011_ADVMODE_BQ_UI_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SmartBoost Coeff"))
+ mode_idx = RT1011_ADVMODE_SMARTBOOST_COEFF;
+ else
+ return -EINVAL;
+
+ pr_info("%s, id.name=%s, mode_idx=%d\n", __func__,
+ ucontrol->id.name, mode_idx);
+ bq_drc_info = rt1011->bq_drc_params[mode_idx];
+
+ for (i = 0; i < RT1011_BQ_DRC_NUM; i++) {
+ params[i].reg = bq_drc_info[i].reg;
+ params[i].val = bq_drc_info[i].val;
+ }
+
+ return 0;
+}
+
+static int rt1011_bq_drc_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+ struct rt1011_bq_drc_params *bq_drc_info;
+ struct rt1011_bq_drc_params *params =
+ (struct rt1011_bq_drc_params *)ucontrol->value.integer.value;
+ unsigned int i, mode_idx = 0;
+
+ if (strstr(ucontrol->id.name, "AdvanceMode Initial Set"))
+ mode_idx = RT1011_ADVMODE_INITIAL_SET;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_SEP_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode EQ BQ Coeff"))
+ mode_idx = RT1011_ADVMODE_EQ_BQ_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode BQ UI Coeff"))
+ mode_idx = RT1011_ADVMODE_BQ_UI_COEFF;
+ else if (strstr(ucontrol->id.name, "AdvanceMode SmartBoost Coeff"))
+ mode_idx = RT1011_ADVMODE_SMARTBOOST_COEFF;
+ else
+ return -EINVAL;
+
+ bq_drc_info = rt1011->bq_drc_params[mode_idx];
+ memset(bq_drc_info, 0,
+ sizeof(struct rt1011_bq_drc_params) * RT1011_BQ_DRC_NUM);
+
+ pr_info("%s, id.name=%s, mode_idx=%d\n", __func__,
+ ucontrol->id.name, mode_idx);
+ for (i = 0; i < RT1011_BQ_DRC_NUM; i++) {
+ bq_drc_info[i].reg = params[i].reg;
+ bq_drc_info[i].val = params[i].val;
+ }
+
+ for (i = 0; i < RT1011_BQ_DRC_NUM; i++) {
+ if (bq_drc_info[i].reg == 0)
+ break;
+ else if (rt1011_validate_bq_drc_coeff(bq_drc_info[i].reg)) {
+ snd_soc_component_write(component, bq_drc_info[i].reg,
+ bq_drc_info[i].val);
+ }
+ }
+
+ return 0;
+}
+
+static int rt1011_bq_drc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.max = 0x17ffffff;
+
+ return 0;
+}
+
+#define RT1011_BQ_DRC(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt1011_bq_drc_info, \
+ .get = rt1011_bq_drc_coeff_get, \
+ .put = rt1011_bq_drc_coeff_put \
+}
+
+static int rt1011_r0_cali_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1011->cali_done;
+
+ return 0;
+}
+
+static int rt1011_r0_cali_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ rt1011->cali_done = 0;
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0])
+ rt1011_calibrate(rt1011, 1);
+
+ return 0;
+}
+
+static int rt1011_r0_load(struct rt1011_priv *rt1011)
+{
+ if (!rt1011->r0_reg)
+ return -EINVAL;
+
+ /* write R0 to register */
+ regmap_write(rt1011->regmap, RT1011_INIT_RECIPROCAL_REG_24_16,
+ ((rt1011->r0_reg>>16) & 0x1ff));
+ regmap_write(rt1011->regmap, RT1011_INIT_RECIPROCAL_REG_15_0,
+ (rt1011->r0_reg & 0xffff));
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_4, 0x4080);
+
+ return 0;
+}
+
+static int rt1011_r0_load_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1011->r0_reg;
+
+ return 0;
+}
+
+static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ struct device *dev;
+ unsigned int r0_integer, r0_factor, format;
+
+ if (ucontrol->value.integer.value[0] == rt1011->r0_reg)
+ return 0;
+
+ if (ucontrol->value.integer.value[0] == 0)
+ return -EINVAL;
+
+ dev = regmap_get_device(rt1011->regmap);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ rt1011->r0_reg = ucontrol->value.integer.value[0];
+
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / rt1011->r0_reg / 128;
+ r0_factor = ((format / rt1011->r0_reg * 100) / 128)
+ - (r0_integer * 100);
+ dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, rt1011->r0_reg);
+
+ if (rt1011->r0_reg)
+ rt1011_r0_load(rt1011);
+ }
+
+ return 0;
+}
+
+static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.max = 0x1ffffff;
+
+ return 0;
+}
+
+#define RT1011_R0_LOAD(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt1011_r0_load_info, \
+ .get = rt1011_r0_load_mode_get, \
+ .put = rt1011_r0_load_mode_put \
+}
+
+static const char * const rt1011_i2s_ref[] = {
+ "None", "Left Channel", "Right Channel"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum, 0, 0,
+ rt1011_i2s_ref);
+
+static int rt1011_i2s_ref_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ rt1011->i2s_ref = ucontrol->value.enumerated.item[0];
+ switch (rt1011->i2s_ref) {
+ case RT1011_I2S_REF_LEFT_CH:
+ regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x1022);
+ regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4);
+ break;
+ case RT1011_I2S_REF_RIGHT_CH:
+ regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8);
+ regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x10a2);
+ regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4);
+ break;
+ default:
+ dev_info(component->dev, "I2S Reference: Do nothing\n");
+ }
+
+ return 0;
+}
+
+static int rt1011_i2s_ref_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1011_priv *rt1011 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = rt1011->i2s_ref;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1011_snd_controls[] = {
+ /* I2S Data In Selection */
+ SOC_ENUM("DIN Source", rt1011_din_source_enum),
+
+ /* TDM Data In Selection */
+ SOC_ENUM("TDM1 DIN Source", rt1011_tdm1_l_dac1_enum),
+ SOC_ENUM("TDM2 DIN Source", rt1011_tdm2_l_dac1_enum),
+
+ /* TDM1 Data Out Selection */
+ SOC_ENUM("TDM1 DOUT Source", rt1011_tdm1_adc1_dat_enum),
+ SOC_ENUM("TDM1 DOUT Location", rt1011_tdm1_adc1_loc_enum),
+ SOC_ENUM("TDM1 ADC1DAT Swap Select", rt1011_tdm_adc1_1_enum),
+ SOC_ENUM("TDM1 ADC2DAT Swap Select", rt1011_tdm_adc2_1_enum),
+
+ /* Data Out Mode */
+ SOC_ENUM("I2S ADC DOUT Mode", rt1011_adc_dout_mode_enum),
+ SOC_ENUM("TDM1 DOUT Length", rt1011_tdm1_dout_len_enum),
+ SOC_ENUM("TDM2 DOUT Length", rt1011_tdm2_dout_len_enum),
+
+ /* Speaker/Receiver Mode */
+ SOC_SINGLE_EXT("RECV SPK Mode", SND_SOC_NOPM, 0, 1, 0,
+ rt1011_recv_spk_mode_get, rt1011_recv_spk_mode_put),
+
+ /* BiQuad/DRC/SmartBoost Settings */
+ RT1011_BQ_DRC("AdvanceMode Initial Set"),
+ RT1011_BQ_DRC("AdvanceMode SEP BQ Coeff"),
+ RT1011_BQ_DRC("AdvanceMode EQ BQ Coeff"),
+ RT1011_BQ_DRC("AdvanceMode BQ UI Coeff"),
+ RT1011_BQ_DRC("AdvanceMode SmartBoost Coeff"),
+
+ /* R0 */
+ SOC_SINGLE_EXT("R0 Calibration", SND_SOC_NOPM, 0, 1, 0,
+ rt1011_r0_cali_get, rt1011_r0_cali_put),
+ RT1011_R0_LOAD("R0 Load Mode"),
+
+ /* R0 temperature */
+ SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP,
+ 2, 255, 0),
+ /* I2S Reference */
+ SOC_ENUM_EXT("I2S Reference", rt1011_i2s_ref_enum,
+ rt1011_i2s_ref_get, rt1011_i2s_ref_put),
+};
+
+static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ if (rt1011->sysclk_src == RT1011_FS_SYS_PRE_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt1011_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component,
+ RT1011_SPK_TEMP_PROTECT_0,
+ RT1011_STP_EN_MASK | RT1011_STP_RS_CLB_EN_MASK,
+ RT1011_STP_EN | RT1011_STP_RS_CLB_EN);
+ snd_soc_component_update_bits(component, RT1011_POWER_9,
+ RT1011_POW_MNL_SDB_MASK, RT1011_POW_MNL_SDB);
+ msleep(50);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_DRIVER_READY_SPK, RT1011_DRIVER_READY_SPK);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1011_POWER_9,
+ RT1011_POW_MNL_SDB_MASK, 0);
+ snd_soc_component_update_bits(component,
+ RT1011_SPK_TEMP_PROTECT_0,
+ RT1011_STP_EN_MASK | RT1011_STP_RS_CLB_EN_MASK, 0);
+ msleep(200);
+ snd_soc_component_update_bits(component,
+ RT1011_CLASSD_INTERNAL_SET_1,
+ RT1011_DRIVER_READY_SPK, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static const struct snd_soc_dapm_widget rt1011_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT1011_POWER_1,
+ RT1011_POW_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISENSE SPK", RT1011_POWER_1,
+ RT1011_POW_ISENSE_SPK_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VSENSE SPK", RT1011_POWER_1,
+ RT1011_POW_VSENSE_SPK_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL", RT1011_POWER_2,
+ RT1011_PLLEN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG", RT1011_POWER_2,
+ RT1011_POW_BG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1011_POWER_2,
+ RT1011_POW_BG_MBIAS_LV_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DET VBAT", RT1011_POWER_3,
+ RT1011_POW_DET_VBAT_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT1011_POWER_3,
+ RT1011_POW_MBIAS_LV_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC I", RT1011_POWER_3,
+ RT1011_POW_ADC_I_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC V", RT1011_POWER_3,
+ RT1011_POW_ADC_V_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC T", RT1011_POWER_3,
+ RT1011_POW_ADC_T_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DITHER ADC T", RT1011_POWER_3,
+ RT1011_POWD_ADC_T_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIX I", RT1011_POWER_3,
+ RT1011_POW_MIX_I_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIX V", RT1011_POWER_3,
+ RT1011_POW_MIX_V_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SUM I", RT1011_POWER_3,
+ RT1011_POW_SUM_I_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SUM V", RT1011_POWER_3,
+ RT1011_POW_SUM_V_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIX T", RT1011_POWER_3,
+ RT1011_POW_MIX_T_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1011_POWER_3,
+ RT1011_POW_VREF_LV_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("BOOST SWR", RT1011_POWER_4,
+ RT1011_POW_EN_SWR_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BGOK SWR", RT1011_POWER_4,
+ RT1011_POW_EN_PASS_BGOK_SWR_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VPOK SWR", RT1011_POWER_4,
+ RT1011_POW_EN_PASS_VPOK_SWR_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("TEMP REG", RT1011_A_TEMP_SEN,
+ RT1011_POW_TEMP_REG_BIT, 0, NULL, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC Power", RT1011_POWER_1,
+ RT1011_POW_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK12M", RT1011_POWER_1,
+ RT1011_POW_CLK12M_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, RT1011_DAC_SET_3,
+ RT1011_DA_MUTE_EN_SFT, 1, rt1011_dac_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1011_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+ { "DAC", NULL, "DAC Power" },
+ { "DAC", NULL, "LDO2" },
+ { "DAC", NULL, "ISENSE SPK" },
+ { "DAC", NULL, "VSENSE SPK" },
+ { "DAC", NULL, "CLK12M" },
+
+ { "DAC", NULL, "PLL", rt1011_is_sys_clk_from_pll },
+ { "DAC", NULL, "BG" },
+ { "DAC", NULL, "BG MBIAS" },
+
+ { "DAC", NULL, "BOOST SWR" },
+ { "DAC", NULL, "BGOK SWR" },
+ { "DAC", NULL, "VPOK SWR" },
+
+ { "DAC", NULL, "DET VBAT" },
+ { "DAC", NULL, "MBIAS" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "ADC I" },
+ { "DAC", NULL, "ADC V" },
+ { "DAC", NULL, "ADC T" },
+ { "DAC", NULL, "DITHER ADC T" },
+ { "DAC", NULL, "MIX I" },
+ { "DAC", NULL, "MIX V" },
+ { "DAC", NULL, "SUM I" },
+ { "DAC", NULL, "SUM V" },
+ { "DAC", NULL, "MIX T" },
+
+ { "DAC", NULL, "TEMP REG" },
+
+ { "SPO", NULL, "DAC" },
+};
+
+static int rt1011_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1011_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, ch_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1011->lrck = params_rate(params);
+ pre_div = rt1011_get_clk_info(rt1011->sysclk, rt1011->lrck);
+ if (pre_div < 0) {
+ dev_warn(component->dev, "Force using PLL ");
+ snd_soc_dai_set_pll(dai, 0, RT1011_PLL1_S_BCLK,
+ rt1011->lrck * 64, rt1011->lrck * 256);
+ snd_soc_dai_set_sysclk(dai, RT1011_FS_SYS_PRE_S_PLL1,
+ rt1011->lrck * 256, SND_SOC_CLOCK_IN);
+ pre_div = 0;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1011->bclk = rt1011->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1011->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1011_I2S_TX_DL_16B;
+ val_len |= RT1011_I2S_RX_DL_16B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_16B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_16B;
+ break;
+ case 20:
+ val_len |= RT1011_I2S_TX_DL_20B;
+ val_len |= RT1011_I2S_RX_DL_20B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_20B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_20B;
+ break;
+ case 24:
+ val_len |= RT1011_I2S_TX_DL_24B;
+ val_len |= RT1011_I2S_RX_DL_24B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_24B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_24B;
+ break;
+ case 32:
+ val_len |= RT1011_I2S_TX_DL_32B;
+ val_len |= RT1011_I2S_RX_DL_32B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_32B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_32B;
+ break;
+ case 8:
+ val_len |= RT1011_I2S_TX_DL_8B;
+ val_len |= RT1011_I2S_RX_DL_8B;
+ ch_len |= RT1011_I2S_CH_TX_LEN_8B;
+ ch_len |= RT1011_I2S_CH_RX_LEN_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1011_AIF1:
+ mask_clk = RT1011_FS_SYS_DIV_MASK;
+ val_clk = pre_div << RT1011_FS_SYS_DIV_SFT;
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_I2S_TX_DL_MASK | RT1011_I2S_RX_DL_MASK,
+ val_len);
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
+ RT1011_I2S_CH_TX_LEN_MASK |
+ RT1011_I2S_CH_RX_LEN_MASK,
+ ch_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ RT1011_CLK_2, mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int reg_val = 0, reg_bclk_inv = 0;
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT1011_I2S_TDM_MS_S;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_fmt_err_;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_bclk_inv |= RT1011_TDM_INV_BCLK;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_fmt_err_;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1011_I2S_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1011_I2S_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1011_I2S_TDM_DF_PCM_B;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_fmt_err_;
+ }
+
+ switch (dai->id) {
+ case RT1011_AIF1:
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_I2S_TDM_MS_MASK | RT1011_I2S_TDM_DF_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
+ RT1011_TDM_INV_BCLK_MASK, reg_bclk_inv);
+ snd_soc_component_update_bits(component, RT1011_TDM2_SET_1,
+ RT1011_TDM_INV_BCLK_MASK, reg_bclk_inv);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ ret = -EINVAL;
+ }
+
+_set_fmt_err_:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static int rt1011_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1011->sysclk && clk_id == rt1011->sysclk_src)
+ return 0;
+
+ /* disable MCLK detect in default */
+ snd_soc_component_update_bits(component, RT1011_CLK_DET,
+ RT1011_EN_MCLK_DET_MASK, 0);
+
+ switch (clk_id) {
+ case RT1011_FS_SYS_PRE_S_MCLK:
+ reg_val |= RT1011_FS_SYS_PRE_MCLK;
+ snd_soc_component_update_bits(component, RT1011_CLK_DET,
+ RT1011_EN_MCLK_DET_MASK, RT1011_EN_MCLK_DET);
+ break;
+ case RT1011_FS_SYS_PRE_S_BCLK:
+ reg_val |= RT1011_FS_SYS_PRE_BCLK;
+ break;
+ case RT1011_FS_SYS_PRE_S_PLL1:
+ reg_val |= RT1011_FS_SYS_PRE_PLL1;
+ break;
+ case RT1011_FS_SYS_PRE_S_RCCLK:
+ reg_val |= RT1011_FS_SYS_PRE_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_FS_SYS_PRE_MASK, reg_val);
+ rt1011->sysclk = freq;
+ rt1011->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1011_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1011->pll_src && freq_in == rt1011->pll_in &&
+ freq_out == rt1011->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1011->pll_in = 0;
+ rt1011->pll_out = 0;
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_FS_SYS_PRE_MASK, RT1011_FS_SYS_PRE_BCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1011_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL2_SRC_MASK, RT1011_PLL2_SRC_MCLK);
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_PLL2);
+ snd_soc_component_update_bits(component, RT1011_CLK_DET,
+ RT1011_EN_MCLK_DET_MASK, RT1011_EN_MCLK_DET);
+ break;
+ case RT1011_PLL1_S_BCLK:
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_BCLK);
+ break;
+ case RT1011_PLL2_S_RCCLK:
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL2_SRC_MASK, RT1011_PLL2_SRC_RCCLK);
+ snd_soc_component_update_bits(component, RT1011_CLK_2,
+ RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_PLL2);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1011_PLL_1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1011_PLL1_QM_SFT) |
+ (pll_code.m_bp << RT1011_PLL1_BPM_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1011_PLL_2,
+ pll_code.k_code);
+
+ rt1011->pll_in = freq_in;
+ rt1011->pll_out = freq_out;
+ rt1011->pll_src = source;
+
+ return 0;
+}
+
+static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int val = 0, tdm_en = 0, rx_slotnum, tx_slotnum;
+ int ret = 0, first_bit, last_bit;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (rx_mask || tx_mask)
+ tdm_en = RT1011_TDM_I2S_DOCK_EN_1;
+
+ switch (slots) {
+ case 4:
+ val |= RT1011_I2S_TX_4CH;
+ val |= RT1011_I2S_RX_4CH;
+ break;
+ case 6:
+ val |= RT1011_I2S_TX_6CH;
+ val |= RT1011_I2S_RX_6CH;
+ break;
+ case 8:
+ val |= RT1011_I2S_TX_8CH;
+ val |= RT1011_I2S_RX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT1011_I2S_CH_TX_LEN_20B;
+ val |= RT1011_I2S_CH_RX_LEN_20B;
+ break;
+ case 24:
+ val |= RT1011_I2S_CH_TX_LEN_24B;
+ val |= RT1011_I2S_CH_RX_LEN_24B;
+ break;
+ case 32:
+ val |= RT1011_I2S_CH_TX_LEN_32B;
+ val |= RT1011_I2S_CH_RX_LEN_32B;
+ break;
+ case 16:
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum > 1 || !rx_slotnum) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
+ RT1011_MONO_L_CHANNEL);
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_4,
+ RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit+1) << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
+ RT1011_MONO_R_CHANNEL);
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_4,
+ RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit-1) << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ if (tx_slotnum > 2 || !tx_slotnum) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many tx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ first_bit = __ffs(tx_mask);
+ last_bit = __fls(tx_mask);
+ if (last_bit - first_bit > 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "tx slot location error\n");
+ goto _set_tdm_err_;
+ }
+
+ if (tx_slotnum == 1) {
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
+ RT1011_TDM_ADCDAT1_DATA_LOCATION, first_bit);
+ switch (first_bit) {
+ case 1:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC1_1_MASK,
+ RT1011_TDM_I2S_RX_ADC1_1_LL);
+ break;
+ case 3:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC2_1_MASK,
+ RT1011_TDM_I2S_RX_ADC2_1_LL);
+ break;
+ case 5:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC3_1_MASK,
+ RT1011_TDM_I2S_RX_ADC3_1_LL);
+ break;
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC4_1_MASK,
+ RT1011_TDM_I2S_RX_ADC4_1_LL);
+ break;
+ case 0:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC1_1_MASK, 0);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC2_1_MASK, 0);
+ break;
+ case 4:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC3_1_MASK, 0);
+ break;
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_3,
+ RT1011_TDM_I2S_RX_ADC4_1_MASK, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_dbg(component->dev,
+ "tx slot location error\n");
+ goto _set_tdm_err_;
+ }
+ } else if (tx_slotnum == 2) {
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
+ RT1011_TDM_ADCDAT1_DATA_LOCATION,
+ RT1011_TDM_I2S_DOCK_ADCDAT_2CH | first_bit);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_dbg(component->dev,
+ "tx slot location should be paired and start from slot0/2/4/6\n");
+ goto _set_tdm_err_;
+ }
+ }
+
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
+ RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
+ RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
+ snd_soc_component_update_bits(component, RT1011_TDM2_SET_1,
+ RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
+ RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
+ snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
+ RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en);
+ snd_soc_component_update_bits(component, RT1011_TDM2_SET_2,
+ RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en);
+
+ snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
+ RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG,
+ RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT);
+
+_set_tdm_err_:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static int rt1011_probe(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ rt1011->component = component;
+
+ schedule_work(&rt1011->cali_work);
+
+ rt1011->i2s_ref = 0;
+ rt1011->bq_drc_params = devm_kcalloc(component->dev,
+ RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *),
+ GFP_KERNEL);
+ if (!rt1011->bq_drc_params)
+ return -ENOMEM;
+
+ for (i = 0; i < RT1011_ADVMODE_NUM; i++) {
+ rt1011->bq_drc_params[i] = devm_kcalloc(component->dev,
+ RT1011_BQ_DRC_NUM, sizeof(struct rt1011_bq_drc_params),
+ GFP_KERNEL);
+ if (!rt1011->bq_drc_params[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void rt1011_remove(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&rt1011->cali_work);
+ rt1011_reset(rt1011->regmap);
+}
+
+#ifdef CONFIG_PM
+static int rt1011_suspend(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1011->regmap, true);
+ regcache_mark_dirty(rt1011->regmap);
+
+ return 0;
+}
+
+static int rt1011_resume(struct snd_soc_component *component)
+{
+ struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1011->regmap, false);
+ regcache_sync(rt1011->regmap);
+
+ return 0;
+}
+#else
+#define rt1011_suspend NULL
+#define rt1011_resume NULL
+#endif
+
+static int rt1011_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_1, 0x0000);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_2, 0x0000);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_3, 0x0001);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_1, 0x003f);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_2, 0x7fd7);
+ snd_soc_component_write(component,
+ RT1011_SYSTEM_RESET_3, 0x770f);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#define RT1011_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1011_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops rt1011_aif_dai_ops = {
+ .hw_params = rt1011_hw_params,
+ .set_fmt = rt1011_set_dai_fmt,
+ .set_tdm_slot = rt1011_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1011_dai[] = {
+ {
+ .name = "rt1011-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1011_STEREO_RATES,
+ .formats = RT1011_FORMATS,
+ },
+ .ops = &rt1011_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1011 = {
+ .probe = rt1011_probe,
+ .remove = rt1011_remove,
+ .suspend = rt1011_suspend,
+ .resume = rt1011_resume,
+ .set_bias_level = rt1011_set_bias_level,
+ .controls = rt1011_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1011_snd_controls),
+ .dapm_widgets = rt1011_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1011_dapm_widgets),
+ .dapm_routes = rt1011_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1011_dapm_routes),
+ .set_sysclk = rt1011_set_component_sysclk,
+ .set_pll = rt1011_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1011_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT1011_MAX_REG + 1,
+ .volatile_reg = rt1011_volatile_register,
+ .readable_reg = rt1011_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt1011_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1011_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1011_of_match[] = {
+ { .compatible = "realtek,rt1011", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1011_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1011_acpi_match[] = {
+ { "10EC1011" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1011_i2c_id[] = {
+ { "rt1011" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1011_i2c_id);
+
+static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
+{
+ unsigned int value, count = 0, r0[3];
+ unsigned int chk_cnt = 50; /* DONT change this */
+ unsigned int dc_offset;
+ unsigned int r0_integer, r0_factor, format;
+ struct device *dev = regmap_get_device(rt1011->regmap);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1011->component);
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ regcache_cache_bypass(rt1011->regmap, true);
+
+ regmap_write(rt1011->regmap, RT1011_RESET, 0x0000);
+ regmap_write(rt1011->regmap, RT1011_SYSTEM_RESET_3, 0x740f);
+ regmap_write(rt1011->regmap, RT1011_SYSTEM_RESET_3, 0x770f);
+
+ /* RC clock */
+ regmap_write(rt1011->regmap, RT1011_CLK_2, 0x9400);
+ regmap_write(rt1011->regmap, RT1011_PLL_1, 0x0800);
+ regmap_write(rt1011->regmap, RT1011_PLL_2, 0x0020);
+ regmap_write(rt1011->regmap, RT1011_CLK_DET, 0x0800);
+
+ /* ADC/DAC setting */
+ regmap_write(rt1011->regmap, RT1011_ADC_SET_5, 0x0a20);
+ regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xe232);
+ regmap_write(rt1011->regmap, RT1011_ADC_SET_4, 0xc000);
+
+ /* DC detection */
+ regmap_write(rt1011->regmap, RT1011_SPK_PRO_DC_DET_1, 0xb00c);
+ regmap_write(rt1011->regmap, RT1011_SPK_PRO_DC_DET_2, 0xcccc);
+
+ /* Power */
+ regmap_write(rt1011->regmap, RT1011_POWER_1, 0xe0e0);
+ regmap_write(rt1011->regmap, RT1011_POWER_3, 0x5003);
+ regmap_write(rt1011->regmap, RT1011_POWER_9, 0xa860);
+ regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xa032);
+
+ /* POW_PLL / POW_BG / POW_BG_MBIAS_LV / POW_V/I */
+ regmap_write(rt1011->regmap, RT1011_POWER_2, 0x0007);
+ regmap_write(rt1011->regmap, RT1011_POWER_3, 0x5ff7);
+ regmap_write(rt1011->regmap, RT1011_A_TEMP_SEN, 0x7f44);
+ regmap_write(rt1011->regmap, RT1011_A_TIMING_1, 0x4054);
+ regmap_write(rt1011->regmap, RT1011_BAT_GAIN_1, 0x309c);
+
+ /* DC offset from EFUSE */
+ regmap_write(rt1011->regmap, RT1011_DC_CALIB_CLASSD_3, 0xcb00);
+ regmap_write(rt1011->regmap, RT1011_BOOST_CON_1, 0xe080);
+ regmap_write(rt1011->regmap, RT1011_POWER_4, 0x16f2);
+ regmap_write(rt1011->regmap, RT1011_POWER_6, 0x36ad);
+
+ /* mixer */
+ regmap_write(rt1011->regmap, RT1011_MIXER_1, 0x3f1d);
+
+ /* EFUSE read */
+ regmap_write(rt1011->regmap, RT1011_EFUSE_CONTROL_1, 0x0d0a);
+ msleep(30);
+
+ regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_18_16, &value);
+ dc_offset = value << 16;
+ regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_15_0, &value);
+ dc_offset |= (value & 0xffff);
+ dev_info(dev, "ADC offset=0x%x\n", dc_offset);
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_20_16, &value);
+ dc_offset = value << 16;
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_15_0, &value);
+ dc_offset |= (value & 0xffff);
+ dev_info(dev, "Gain0 offset=0x%x\n", dc_offset);
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_20_16, &value);
+ dc_offset = value << 16;
+ regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_15_0, &value);
+ dc_offset |= (value & 0xffff);
+ dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
+
+ if (cali_flag) {
+
+ regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925);
+ /* Class D on */
+ regmap_write(rt1011->regmap, RT1011_CLASS_D_POS, 0x010e);
+ regmap_write(rt1011->regmap,
+ RT1011_CLASSD_INTERNAL_SET_1, 0x1701);
+
+ /* STP enable */
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0x8000);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_7, 0xf000);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_4, 0x4040);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0xc000);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_6, 0x07c2);
+
+ r0[0] = r0[1] = r0[2] = count = 0;
+ while (count < chk_cnt) {
+ msleep(100);
+ regmap_read(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_SYN_24_16, &value);
+ r0[count%3] = value << 16;
+ regmap_read(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_SYN_15_0, &value);
+ r0[count%3] |= value;
+
+ if (r0[count%3] == 0)
+ continue;
+
+ count++;
+
+ if (r0[0] == r0[1] && r0[1] == r0[2])
+ break;
+ }
+ if (count > chk_cnt) {
+ dev_err(dev, "Calibrate R0 Failure\n");
+ ret = -EAGAIN;
+ } else {
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / r0[0] / 128;
+ r0_factor = ((format / r0[0] * 100) / 128)
+ - (r0_integer * 100);
+ rt1011->r0_reg = r0[0];
+ rt1011->cali_done = 1;
+ dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, r0[0]);
+ }
+ }
+
+ /* depop */
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0x0000);
+ msleep(400);
+ regmap_write(rt1011->regmap, RT1011_POWER_9, 0xa840);
+ regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_6, 0x0702);
+ regmap_write(rt1011->regmap, RT1011_MIXER_1, 0xffdd);
+ regmap_write(rt1011->regmap, RT1011_CLASSD_INTERNAL_SET_1, 0x0701);
+ regmap_write(rt1011->regmap, RT1011_DAC_SET_3, 0xe004);
+ regmap_write(rt1011->regmap, RT1011_A_TEMP_SEN, 0x7f40);
+ regmap_write(rt1011->regmap, RT1011_POWER_1, 0x0000);
+ regmap_write(rt1011->regmap, RT1011_POWER_2, 0x0000);
+ regmap_write(rt1011->regmap, RT1011_POWER_3, 0x0002);
+ regmap_write(rt1011->regmap, RT1011_POWER_4, 0x00f2);
+
+ regmap_write(rt1011->regmap, RT1011_RESET, 0x0000);
+
+ if (cali_flag) {
+ if (count <= chk_cnt) {
+ regmap_write(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_REG_24_16,
+ ((r0[0]>>16) & 0x1ff));
+ regmap_write(rt1011->regmap,
+ RT1011_INIT_RECIPROCAL_REG_15_0,
+ (r0[0] & 0xffff));
+ regmap_write(rt1011->regmap,
+ RT1011_SPK_TEMP_PROTECT_4, 0x4080);
+ }
+ }
+
+ regcache_cache_bypass(rt1011->regmap, false);
+ regcache_mark_dirty(rt1011->regmap);
+ regcache_sync(rt1011->regmap);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static void rt1011_calibration_work(struct work_struct *work)
+{
+ struct rt1011_priv *rt1011 =
+ container_of(work, struct rt1011_priv, cali_work);
+ struct snd_soc_component *component = rt1011->component;
+ unsigned int r0_integer, r0_factor, format;
+
+ if (rt1011->r0_calib)
+ rt1011_calibrate(rt1011, 0);
+ else
+ rt1011_calibrate(rt1011, 1);
+
+ /*
+ * This flag should reset after booting.
+ * The factory test will do calibration again and use this flag to check
+ * whether the calibration completed
+ */
+ rt1011->cali_done = 0;
+
+ /* initial */
+ rt1011_reg_init(component);
+
+ /* Apply temperature and calibration data from device property */
+ if (rt1011->temperature_calib <= 0xff &&
+ rt1011->temperature_calib > 0) {
+ snd_soc_component_update_bits(component,
+ RT1011_STP_INITIAL_RESISTANCE_TEMP, 0x3ff,
+ (rt1011->temperature_calib << 2));
+ }
+
+ if (rt1011->r0_calib) {
+ rt1011->r0_reg = rt1011->r0_calib;
+
+ format = 2147483648U; /* 2^24 * 128 */
+ r0_integer = format / rt1011->r0_reg / 128;
+ r0_factor = ((format / rt1011->r0_reg * 100) / 128)
+ - (r0_integer * 100);
+ dev_info(component->dev, "DP r0 resistance about %d.%02d ohm, reg=0x%X\n",
+ r0_integer, r0_factor, rt1011->r0_reg);
+
+ rt1011_r0_load(rt1011);
+ }
+
+ snd_soc_component_write(component, RT1011_ADC_SET_1, 0x2925);
+}
+
+static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,temperature_calib",
+ &rt1011->temperature_calib);
+ device_property_read_u32(dev, "realtek,r0_calib",
+ &rt1011->r0_calib);
+
+ dev_dbg(dev, "%s: r0_calib: 0x%x, temperature_calib: 0x%x",
+ __func__, rt1011->r0_calib, rt1011->temperature_calib);
+
+ return 0;
+}
+
+static int rt1011_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1011_priv *rt1011;
+ int ret;
+ unsigned int val;
+
+ rt1011 = devm_kzalloc(&i2c->dev, sizeof(struct rt1011_priv),
+ GFP_KERNEL);
+ if (!rt1011)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1011);
+
+ rt1011_parse_dp(rt1011, &i2c->dev);
+
+ rt1011->regmap = devm_regmap_init_i2c(i2c, &rt1011_regmap);
+ if (IS_ERR(rt1011->regmap)) {
+ ret = PTR_ERR(rt1011->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1011->regmap, RT1011_DEVICE_ID, &val);
+ if (val != RT1011_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1011\n", val);
+ return -ENODEV;
+ }
+
+ INIT_WORK(&rt1011->cali_work, rt1011_calibration_work);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1011,
+ rt1011_dai, ARRAY_SIZE(rt1011_dai));
+
+}
+
+static void rt1011_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1011_priv *rt1011 = i2c_get_clientdata(client);
+
+ rt1011_reset(rt1011->regmap);
+}
+
+static struct i2c_driver rt1011_i2c_driver = {
+ .driver = {
+ .name = "rt1011",
+ .of_match_table = of_match_ptr(rt1011_of_match),
+ .acpi_match_table = ACPI_PTR(rt1011_acpi_match)
+ },
+ .probe = rt1011_i2c_probe,
+ .shutdown = rt1011_i2c_shutdown,
+ .id_table = rt1011_i2c_id,
+};
+module_i2c_driver(rt1011_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1011 amplifier driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h
new file mode 100644
index 000000000000..4d6e7492d99c
--- /dev/null
+++ b/sound/soc/codecs/rt1011.h
@@ -0,0 +1,704 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1011.h -- RT1011 ALSA SoC amplifier component driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef _RT1011_H_
+#define _RT1011_H_
+
+#define RT1011_DEVICE_ID_NUM 0x1011
+
+#define RT1011_RESET 0x0000
+#define RT1011_CLK_1 0x0002
+#define RT1011_CLK_2 0x0004
+#define RT1011_CLK_3 0x0006
+#define RT1011_CLK_4 0x0008
+#define RT1011_PLL_1 0x000a
+#define RT1011_PLL_2 0x000c
+#define RT1011_SRC_1 0x000e
+#define RT1011_SRC_2 0x0010
+#define RT1011_SRC_3 0x0012
+#define RT1011_CLK_DET 0x0020
+#define RT1011_SIL_DET 0x0022
+#define RT1011_PRIV_INDEX 0x006a
+#define RT1011_PRIV_DATA 0x006c
+#define RT1011_CUSTOMER_ID 0x0076
+#define RT1011_FM_VER 0x0078
+#define RT1011_VERSION_ID 0x007a
+#define RT1011_VENDOR_ID 0x007c
+#define RT1011_DEVICE_ID 0x007d
+#define RT1011_DUM_RW_0 0x00f0
+#define RT1011_DUM_YUN 0x00f2
+#define RT1011_DUM_RW_1 0x00f3
+#define RT1011_DUM_RO 0x00f4
+#define RT1011_MAN_I2C_DEV 0x0100
+#define RT1011_DAC_SET_1 0x0102
+#define RT1011_DAC_SET_2 0x0104
+#define RT1011_DAC_SET_3 0x0106
+#define RT1011_ADC_SET 0x0107
+#define RT1011_ADC_SET_1 0x0108
+#define RT1011_ADC_SET_2 0x010a
+#define RT1011_ADC_SET_3 0x010c
+#define RT1011_ADC_SET_4 0x010e
+#define RT1011_ADC_SET_5 0x0110
+#define RT1011_TDM_TOTAL_SET 0x0111
+#define RT1011_TDM1_SET_TCON 0x0112
+#define RT1011_TDM1_SET_1 0x0114
+#define RT1011_TDM1_SET_2 0x0116
+#define RT1011_TDM1_SET_3 0x0118
+#define RT1011_TDM1_SET_4 0x011a
+#define RT1011_TDM1_SET_5 0x011c
+#define RT1011_TDM2_SET_1 0x011e
+#define RT1011_TDM2_SET_2 0x0120
+#define RT1011_TDM2_SET_3 0x0122
+#define RT1011_TDM2_SET_4 0x0124
+#define RT1011_TDM2_SET_5 0x0126
+#define RT1011_PWM_CAL 0x0200
+#define RT1011_MIXER_1 0x0300
+#define RT1011_MIXER_2 0x0302
+#define RT1011_ADRC_LIMIT 0x0310
+#define RT1011_A_PRO 0x0311
+#define RT1011_A_TIMING_1 0x0313
+#define RT1011_A_TIMING_2 0x0314
+#define RT1011_A_TEMP_SEN 0x0316
+#define RT1011_SPK_VOL_DET_1 0x0319
+#define RT1011_SPK_VOL_DET_2 0x031a
+#define RT1011_SPK_VOL_TEST_OUT 0x031b
+#define RT1011_VBAT_VOL_DET_1 0x031c
+#define RT1011_VBAT_VOL_DET_2 0x031d
+#define RT1011_VBAT_TEST_OUT_1 0x031e
+#define RT1011_VBAT_TEST_OUT_2 0x031f
+#define RT1011_VBAT_PROTECTION 0x0320
+#define RT1011_VBAT_DET 0x0321
+#define RT1011_POWER_1 0x0322
+#define RT1011_POWER_2 0x0324
+#define RT1011_POWER_3 0x0326
+#define RT1011_POWER_4 0x0328
+#define RT1011_POWER_5 0x0329
+#define RT1011_POWER_6 0x032a
+#define RT1011_POWER_7 0x032b
+#define RT1011_POWER_8 0x032c
+#define RT1011_POWER_9 0x032d
+#define RT1011_CLASS_D_POS 0x032e
+#define RT1011_BOOST_CON_1 0x0330
+#define RT1011_BOOST_CON_2 0x0332
+#define RT1011_ANALOG_CTRL 0x0334
+#define RT1011_POWER_SEQ 0x0340
+#define RT1011_SHORT_CIRCUIT_DET_1 0x0508
+#define RT1011_SHORT_CIRCUIT_DET_2 0x050a
+#define RT1011_SPK_TEMP_PROTECT_0 0x050c
+#define RT1011_SPK_TEMP_PROTECT_1 0x050d
+#define RT1011_SPK_TEMP_PROTECT_2 0x050e
+#define RT1011_SPK_TEMP_PROTECT_3 0x050f
+#define RT1011_SPK_TEMP_PROTECT_4 0x0510
+#define RT1011_SPK_TEMP_PROTECT_5 0x0511
+#define RT1011_SPK_TEMP_PROTECT_6 0x0512
+#define RT1011_SPK_TEMP_PROTECT_7 0x0516
+#define RT1011_SPK_TEMP_PROTECT_8 0x0517
+#define RT1011_SPK_TEMP_PROTECT_9 0x0518
+#define RT1011_SPK_PRO_DC_DET_1 0x0519
+#define RT1011_SPK_PRO_DC_DET_2 0x051a
+#define RT1011_SPK_PRO_DC_DET_3 0x051b
+#define RT1011_SPK_PRO_DC_DET_4 0x051c
+#define RT1011_SPK_PRO_DC_DET_5 0x051d
+#define RT1011_SPK_PRO_DC_DET_6 0x051e
+#define RT1011_SPK_PRO_DC_DET_7 0x051f
+#define RT1011_SPK_PRO_DC_DET_8 0x0520
+#define RT1011_SPL_1 0x0521
+#define RT1011_SPL_2 0x0522
+#define RT1011_SPL_3 0x0524
+#define RT1011_SPL_4 0x0526
+#define RT1011_THER_FOLD_BACK_1 0x0528
+#define RT1011_THER_FOLD_BACK_2 0x052a
+#define RT1011_EXCUR_PROTECT_1 0x0530
+#define RT1011_EXCUR_PROTECT_2 0x0532
+#define RT1011_EXCUR_PROTECT_3 0x0534
+#define RT1011_EXCUR_PROTECT_4 0x0535
+#define RT1011_BAT_GAIN_1 0x0536
+#define RT1011_BAT_GAIN_2 0x0538
+#define RT1011_BAT_GAIN_3 0x053a
+#define RT1011_BAT_GAIN_4 0x053c
+#define RT1011_BAT_GAIN_5 0x053d
+#define RT1011_BAT_GAIN_6 0x053e
+#define RT1011_BAT_GAIN_7 0x053f
+#define RT1011_BAT_GAIN_8 0x0540
+#define RT1011_BAT_GAIN_9 0x0541
+#define RT1011_BAT_GAIN_10 0x0542
+#define RT1011_BAT_GAIN_11 0x0543
+#define RT1011_BAT_RT_THMAX_1 0x0544
+#define RT1011_BAT_RT_THMAX_2 0x0545
+#define RT1011_BAT_RT_THMAX_3 0x0546
+#define RT1011_BAT_RT_THMAX_4 0x0547
+#define RT1011_BAT_RT_THMAX_5 0x0548
+#define RT1011_BAT_RT_THMAX_6 0x0549
+#define RT1011_BAT_RT_THMAX_7 0x054a
+#define RT1011_BAT_RT_THMAX_8 0x054b
+#define RT1011_BAT_RT_THMAX_9 0x054c
+#define RT1011_BAT_RT_THMAX_10 0x054d
+#define RT1011_BAT_RT_THMAX_11 0x054e
+#define RT1011_BAT_RT_THMAX_12 0x054f
+#define RT1011_SPREAD_SPECTURM 0x0568
+#define RT1011_PRO_GAIN_MODE 0x056a
+#define RT1011_RT_DRC_CROSS 0x0600
+#define RT1011_RT_DRC_HB_1 0x0611
+#define RT1011_RT_DRC_HB_2 0x0612
+#define RT1011_RT_DRC_HB_3 0x0613
+#define RT1011_RT_DRC_HB_4 0x0614
+#define RT1011_RT_DRC_HB_5 0x0615
+#define RT1011_RT_DRC_HB_6 0x0616
+#define RT1011_RT_DRC_HB_7 0x0617
+#define RT1011_RT_DRC_HB_8 0x0618
+#define RT1011_RT_DRC_BB_1 0x0621
+#define RT1011_RT_DRC_BB_2 0x0622
+#define RT1011_RT_DRC_BB_3 0x0623
+#define RT1011_RT_DRC_BB_4 0x0624
+#define RT1011_RT_DRC_BB_5 0x0625
+#define RT1011_RT_DRC_BB_6 0x0626
+#define RT1011_RT_DRC_BB_7 0x0627
+#define RT1011_RT_DRC_BB_8 0x0628
+#define RT1011_RT_DRC_POS_1 0x0631
+#define RT1011_RT_DRC_POS_2 0x0632
+#define RT1011_RT_DRC_POS_3 0x0633
+#define RT1011_RT_DRC_POS_4 0x0634
+#define RT1011_RT_DRC_POS_5 0x0635
+#define RT1011_RT_DRC_POS_6 0x0636
+#define RT1011_RT_DRC_POS_7 0x0637
+#define RT1011_RT_DRC_POS_8 0x0638
+#define RT1011_CROSS_BQ_SET_1 0x0702
+#define RT1011_CROSS_BQ_SET_2 0x0704
+#define RT1011_BQ_SET_0 0x0706
+#define RT1011_BQ_SET_1 0x0708
+#define RT1011_BQ_SET_2 0x070a
+#define RT1011_BQ_PRE_GAIN_28_16 0x0710
+#define RT1011_BQ_PRE_GAIN_15_0 0x0711
+#define RT1011_BQ_POST_GAIN_28_16 0x0712
+#define RT1011_BQ_POST_GAIN_15_0 0x0713
+
+#define RT1011_BQ_H0_28_16 0x0720
+#define RT1011_BQ_A2_15_0 0x0729
+#define RT1011_BQ_1_H0_28_16 0x0730
+#define RT1011_BQ_1_A2_15_0 0x0739
+#define RT1011_BQ_2_H0_28_16 0x0740
+#define RT1011_BQ_2_A2_15_0 0x0749
+#define RT1011_BQ_3_H0_28_16 0x0750
+#define RT1011_BQ_3_A2_15_0 0x0759
+#define RT1011_BQ_4_H0_28_16 0x0760
+#define RT1011_BQ_4_A2_15_0 0x0769
+#define RT1011_BQ_5_H0_28_16 0x0770
+#define RT1011_BQ_5_A2_15_0 0x0779
+#define RT1011_BQ_6_H0_28_16 0x0780
+#define RT1011_BQ_6_A2_15_0 0x0789
+#define RT1011_BQ_7_H0_28_16 0x0790
+#define RT1011_BQ_7_A2_15_0 0x0799
+#define RT1011_BQ_8_H0_28_16 0x07a0
+#define RT1011_BQ_8_A2_15_0 0x07a9
+#define RT1011_BQ_9_H0_28_16 0x07b0
+#define RT1011_BQ_9_A2_15_0 0x07b9
+#define RT1011_BQ_10_H0_28_16 0x07c0
+#define RT1011_BQ_10_A2_15_0 0x07c9
+#define RT1011_TEST_PAD_STATUS 0x1000
+#define RT1011_SYSTEM_RESET_1 0x1007
+#define RT1011_SYSTEM_RESET_2 0x1008
+#define RT1011_SYSTEM_RESET_3 0x1009
+#define RT1011_ADCDAT_OUT_SOURCE 0x100D
+#define RT1011_PLL_INTERNAL_SET 0x1010
+#define RT1011_TEST_OUT_1 0x1020
+#define RT1011_TEST_OUT_3 0x1024
+#define RT1011_DC_CALIB_CLASSD_1 0x1200
+#define RT1011_DC_CALIB_CLASSD_2 0x1202
+#define RT1011_DC_CALIB_CLASSD_3 0x1204
+#define RT1011_DC_CALIB_CLASSD_5 0x1208
+#define RT1011_DC_CALIB_CLASSD_6 0x120a
+#define RT1011_DC_CALIB_CLASSD_7 0x120c
+#define RT1011_DC_CALIB_CLASSD_8 0x120e
+#define RT1011_DC_CALIB_CLASSD_10 0x1212
+#define RT1011_CLASSD_INTERNAL_SET_1 0x1300
+#define RT1011_CLASSD_INTERNAL_SET_3 0x1304
+#define RT1011_CLASSD_INTERNAL_SET_8 0x130c
+#define RT1011_VREF_LV_1 0x131a
+#define RT1011_SMART_BOOST_TIMING_1 0x1322
+#define RT1011_SMART_BOOST_TIMING_36 0x1349
+#define RT1011_SINE_GEN_REG_1 0x1500
+#define RT1011_SINE_GEN_REG_2 0x1502
+#define RT1011_SINE_GEN_REG_3 0x1504
+#define RT1011_STP_INITIAL_RS_TEMP 0x1510
+#define RT1011_STP_CALIB_RS_TEMP 0x152a
+#define RT1011_INIT_RECIPROCAL_REG_24_16 0x1538
+#define RT1011_INIT_RECIPROCAL_REG_15_0 0x1539
+#define RT1011_STP_INITIAL_RESISTANCE_TEMP 0x153c
+#define RT1011_STP_ALPHA_RECIPROCAL_MSB 0x153e
+#define RT1011_SPK_RESISTANCE_1 0x1544
+#define RT1011_SPK_RESISTANCE_2 0x1546
+#define RT1011_SPK_THERMAL 0x1548
+#define RT1011_STP_OTP_TH 0x1552
+#define RT1011_ALC_BK_GAIN_O 0x1554
+#define RT1011_ALC_BK_GAIN_O_PRE 0x1556
+#define RT1011_SPK_DC_O_23_16 0x155a
+#define RT1011_SPK_DC_O_15_0 0x155c
+#define RT1011_INIT_RECIPROCAL_SYN_24_16 0x1560
+#define RT1011_INIT_RECIPROCAL_SYN_15_0 0x1562
+#define RT1011_STP_BQ_1_A1_L_28_16 0x1570
+#define RT1011_STP_BQ_1_H0_R_15_0 0x1583
+#define RT1011_STP_BQ_2_A1_L_28_16 0x1590
+#define RT1011_SPK_EXCURSION_23_16 0x15be
+#define RT1011_SPK_EXCURSION_15_0 0x15bf
+#define RT1011_SEP_MAIN_OUT_23_16 0x15c0
+#define RT1011_SEP_MAIN_OUT_15_0 0x15c1
+#define RT1011_SEP_RE_REG_15_0 0x15f9
+#define RT1011_DRC_CF_PARAMS_1 0x1600
+#define RT1011_DRC_CF_PARAMS_12 0x160b
+#define RT1011_ALC_DRC_HB_INTERNAL_1 0x1611
+#define RT1011_ALC_DRC_HB_INTERNAL_5 0x1615
+#define RT1011_ALC_DRC_HB_INTERNAL_6 0x1616
+#define RT1011_ALC_DRC_HB_INTERNAL_7 0x1617
+#define RT1011_ALC_DRC_BB_INTERNAL_1 0x1621
+#define RT1011_ALC_DRC_BB_INTERNAL_5 0x1625
+#define RT1011_ALC_DRC_BB_INTERNAL_6 0x1626
+#define RT1011_ALC_DRC_BB_INTERNAL_7 0x1627
+#define RT1011_ALC_DRC_POS_INTERNAL_1 0x1631
+#define RT1011_ALC_DRC_POS_INTERNAL_5 0x1635
+#define RT1011_ALC_DRC_POS_INTERNAL_6 0x1636
+#define RT1011_ALC_DRC_POS_INTERNAL_7 0x1637
+#define RT1011_ALC_DRC_POS_INTERNAL_8 0x1638
+#define RT1011_ALC_DRC_POS_INTERNAL_9 0x163a
+#define RT1011_ALC_DRC_POS_INTERNAL_10 0x163c
+#define RT1011_ALC_DRC_POS_INTERNAL_11 0x163e
+#define RT1011_BQ_1_PARAMS_CHECK_5 0x1648
+#define RT1011_BQ_2_PARAMS_CHECK_1 0x1650
+#define RT1011_BQ_2_PARAMS_CHECK_5 0x1658
+#define RT1011_BQ_3_PARAMS_CHECK_1 0x1660
+#define RT1011_BQ_3_PARAMS_CHECK_5 0x1668
+#define RT1011_BQ_4_PARAMS_CHECK_1 0x1670
+#define RT1011_BQ_4_PARAMS_CHECK_5 0x1678
+#define RT1011_BQ_5_PARAMS_CHECK_1 0x1680
+#define RT1011_BQ_5_PARAMS_CHECK_5 0x1688
+#define RT1011_BQ_6_PARAMS_CHECK_1 0x1690
+#define RT1011_BQ_6_PARAMS_CHECK_5 0x1698
+#define RT1011_BQ_7_PARAMS_CHECK_1 0x1700
+#define RT1011_BQ_7_PARAMS_CHECK_5 0x1708
+#define RT1011_BQ_8_PARAMS_CHECK_1 0x1710
+#define RT1011_BQ_8_PARAMS_CHECK_5 0x1718
+#define RT1011_BQ_9_PARAMS_CHECK_1 0x1720
+#define RT1011_BQ_9_PARAMS_CHECK_5 0x1728
+#define RT1011_BQ_10_PARAMS_CHECK_1 0x1730
+#define RT1011_BQ_10_PARAMS_CHECK_5 0x1738
+#define RT1011_IRQ_1 0x173a
+#define RT1011_PART_NUMBER_EFUSE 0x173e
+#define RT1011_EFUSE_CONTROL_1 0x17bb
+#define RT1011_EFUSE_CONTROL_2 0x17bd
+#define RT1011_EFUSE_MATCH_DONE 0x17cb
+#define RT1011_EFUSE_ADC_OFFSET_18_16 0x17e5
+#define RT1011_EFUSE_ADC_OFFSET_15_0 0x17e7
+#define RT1011_EFUSE_DAC_OFFSET_G0_20_16 0x17e9
+#define RT1011_EFUSE_DAC_OFFSET_G0_15_0 0x17eb
+#define RT1011_EFUSE_DAC_OFFSET_G1_20_16 0x17ed
+#define RT1011_EFUSE_DAC_OFFSET_G1_15_0 0x17ef
+#define RT1011_EFUSE_READ_R0_3_15_0 0x1803
+#define RT1011_MAX_REG 0x1803
+#define RT1011_REG_DISP_LEN 23
+
+
+/* CLOCK-2 (0x0004) */
+#define RT1011_FS_SYS_PRE_MASK (0x3 << 14)
+#define RT1011_FS_SYS_PRE_SFT 14
+#define RT1011_FS_SYS_PRE_MCLK (0x0 << 14)
+#define RT1011_FS_SYS_PRE_BCLK (0x1 << 14)
+#define RT1011_FS_SYS_PRE_PLL1 (0x2 << 14)
+#define RT1011_FS_SYS_PRE_RCCLK (0x3 << 14)
+#define RT1011_PLL1_SRC_MASK (0x1 << 13)
+#define RT1011_PLL1_SRC_SFT 13
+#define RT1011_PLL1_SRC_PLL2 (0x0 << 13)
+#define RT1011_PLL1_SRC_BCLK (0x1 << 13)
+#define RT1011_PLL2_SRC_MASK (0x1 << 12)
+#define RT1011_PLL2_SRC_SFT 12
+#define RT1011_PLL2_SRC_MCLK (0x0 << 12)
+#define RT1011_PLL2_SRC_RCCLK (0x1 << 12)
+#define RT1011_PLL2_SRC_DIV_MASK (0x3 << 10)
+#define RT1011_PLL2_SRC_DIV_SFT 10
+#define RT1011_SRCIN_DIV_MASK (0x3 << 8)
+#define RT1011_SRCIN_DIV_SFT 8
+#define RT1011_FS_SYS_DIV_MASK (0x7 << 4)
+#define RT1011_FS_SYS_DIV_SFT 4
+
+/* PLL-1 (0x000a) */
+#define RT1011_PLL1_QM_MASK (0xf << 12)
+#define RT1011_PLL1_QM_SFT 12
+#define RT1011_PLL1_BPM_MASK (0x1 << 11)
+#define RT1011_PLL1_BPM_SFT 11
+#define RT1011_PLL1_BPM (0x1 << 11)
+#define RT1011_PLL1_QN_MASK (0x1ff << 0)
+#define RT1011_PLL1_QN_SFT 0
+
+/* PLL-2 (0x000c) */
+#define RT1011_PLL2_BPK_MASK (0x1 << 5)
+#define RT1011_PLL2_BPK_SFT 5
+#define RT1011_PLL2_BPK (0x1 << 5)
+#define RT1011_PLL2_QK_MASK (0x1f << 0)
+#define RT1011_PLL2_QK_SFT 0
+
+/* Clock Detect (0x0020) */
+#define RT1011_EN_MCLK_DET_MASK (0x1 << 15)
+#define RT1011_EN_MCLK_DET_SFT 15
+#define RT1011_EN_MCLK_DET (0x1 << 15)
+
+/* DAC Setting-2 (0x0104) */
+#define RT1011_EN_CKGEN_DAC_MASK (0x1 << 13)
+#define RT1011_EN_CKGEN_DAC_SFT 13
+#define RT1011_EN_CKGEN_DAC (0x1 << 13)
+
+/* DAC Setting-3 (0x0106) */
+#define RT1011_DA_MUTE_EN_MASK (0x1 << 15)
+#define RT1011_DA_MUTE_EN_SFT 15
+
+/* ADC Setting-5 (0x0110) */
+#define RT1011_AD_EN_CKGEN_ADC_MASK (0x1 << 9)
+#define RT1011_AD_EN_CKGEN_ADC_SFT 9
+#define RT1011_AD_EN_CKGEN_ADC (0x1 << 9)
+
+/* TDM Total Setting (0x0111) */
+#define RT1011_I2S_TDM_MS_MASK (0x1 << 14)
+#define RT1011_I2S_TDM_MS_SFT 14
+#define RT1011_I2S_TDM_MS_S (0x0 << 14)
+#define RT1011_I2S_TDM_MS_M (0x1 << 14)
+#define RT1011_I2S_TX_DL_MASK (0x7 << 8)
+#define RT1011_I2S_TX_DL_SFT 8
+#define RT1011_I2S_TX_DL_16B (0x0 << 8)
+#define RT1011_I2S_TX_DL_20B (0x1 << 8)
+#define RT1011_I2S_TX_DL_24B (0x2 << 8)
+#define RT1011_I2S_TX_DL_32B (0x3 << 8)
+#define RT1011_I2S_TX_DL_8B (0x4 << 8)
+#define RT1011_I2S_RX_DL_MASK (0x7 << 5)
+#define RT1011_I2S_RX_DL_SFT 5
+#define RT1011_I2S_RX_DL_16B (0x0 << 5)
+#define RT1011_I2S_RX_DL_20B (0x1 << 5)
+#define RT1011_I2S_RX_DL_24B (0x2 << 5)
+#define RT1011_I2S_RX_DL_32B (0x3 << 5)
+#define RT1011_I2S_RX_DL_8B (0x4 << 5)
+#define RT1011_ADCDAT1_PIN_CONFIG (0x1 << 4)
+#define RT1011_ADCDAT1_OUTPUT (0x0 << 4)
+#define RT1011_ADCDAT1_INPUT (0x1 << 4)
+#define RT1011_ADCDAT2_PIN_CONFIG (0x1 << 3)
+#define RT1011_ADCDAT2_OUTPUT (0x0 << 3)
+#define RT1011_ADCDAT2_INPUT (0x1 << 3)
+#define RT1011_I2S_TDM_DF_MASK (0x7 << 0)
+#define RT1011_I2S_TDM_DF_SFT 0
+#define RT1011_I2S_TDM_DF_I2S (0x0)
+#define RT1011_I2S_TDM_DF_LEFT (0x1)
+#define RT1011_I2S_TDM_DF_PCM_A (0x2)
+#define RT1011_I2S_TDM_DF_PCM_B (0x3)
+#define RT1011_I2S_TDM_DF_PCM_A_N (0x6)
+#define RT1011_I2S_TDM_DF_PCM_B_N (0x7)
+
+/* TDM_tcon Setting (0x0112) */
+#define RT1011_TCON_DF_MASK (0x7 << 13)
+#define RT1011_TCON_DF_SFT 13
+#define RT1011_TCON_DF_I2S (0x0 << 13)
+#define RT1011_TCON_DF_LEFT (0x1 << 13)
+#define RT1011_TCON_DF_PCM_A (0x2 << 13)
+#define RT1011_TCON_DF_PCM_B (0x3 << 13)
+#define RT1011_TCON_DF_PCM_A_N (0x6 << 13)
+#define RT1011_TCON_DF_PCM_B_N (0x7 << 13)
+#define RT1011_TCON_BCLK_SEL_MASK (0x3 << 10)
+#define RT1011_TCON_BCLK_SEL_SFT 10
+#define RT1011_TCON_BCLK_SEL_32FS (0x0 << 10)
+#define RT1011_TCON_BCLK_SEL_64FS (0x1 << 10)
+#define RT1011_TCON_BCLK_SEL_128FS (0x2 << 10)
+#define RT1011_TCON_BCLK_SEL_256FS (0x3 << 10)
+#define RT1011_TCON_CH_LEN_MASK (0x3 << 5)
+#define RT1011_TCON_CH_LEN_SFT 5
+#define RT1011_TCON_CH_LEN_16B (0x0 << 5)
+#define RT1011_TCON_CH_LEN_20B (0x1 << 5)
+#define RT1011_TCON_CH_LEN_24B (0x2 << 5)
+#define RT1011_TCON_CH_LEN_32B (0x3 << 5)
+#define RT1011_TCON_BCLK_MST_MASK (0x1 << 4)
+#define RT1011_TCON_BCLK_MST_SFT 4
+#define RT1011_TCON_BCLK_MST_INV (0x1 << 4)
+
+/* TDM1 Setting-1 (0x0114) */
+#define RT1011_TDM_INV_BCLK_MASK (0x1 << 15)
+#define RT1011_TDM_INV_BCLK_SFT 15
+#define RT1011_TDM_INV_BCLK (0x1 << 15)
+#define RT1011_I2S_CH_TX_MASK (0x3 << 10)
+#define RT1011_I2S_CH_TX_SFT 10
+#define RT1011_I2S_TX_2CH (0x0 << 10)
+#define RT1011_I2S_TX_4CH (0x1 << 10)
+#define RT1011_I2S_TX_6CH (0x2 << 10)
+#define RT1011_I2S_TX_8CH (0x3 << 10)
+#define RT1011_I2S_CH_RX_MASK (0x3 << 8)
+#define RT1011_I2S_CH_RX_SFT 8
+#define RT1011_I2S_RX_2CH (0x0 << 8)
+#define RT1011_I2S_RX_4CH (0x1 << 8)
+#define RT1011_I2S_RX_6CH (0x2 << 8)
+#define RT1011_I2S_RX_8CH (0x3 << 8)
+#define RT1011_I2S_LR_CH_SEL_MASK (0x1 << 7)
+#define RT1011_I2S_LR_CH_SEL_SFT 7
+#define RT1011_I2S_LEFT_CH_SEL (0x0 << 7)
+#define RT1011_I2S_RIGHT_CH_SEL (0x1 << 7)
+#define RT1011_I2S_CH_TX_LEN_MASK (0x7 << 4)
+#define RT1011_I2S_CH_TX_LEN_SFT 4
+#define RT1011_I2S_CH_TX_LEN_16B (0x0 << 4)
+#define RT1011_I2S_CH_TX_LEN_20B (0x1 << 4)
+#define RT1011_I2S_CH_TX_LEN_24B (0x2 << 4)
+#define RT1011_I2S_CH_TX_LEN_32B (0x3 << 4)
+#define RT1011_I2S_CH_TX_LEN_8B (0x4 << 4)
+#define RT1011_I2S_CH_RX_LEN_MASK (0x7 << 0)
+#define RT1011_I2S_CH_RX_LEN_SFT 0
+#define RT1011_I2S_CH_RX_LEN_16B (0x0 << 0)
+#define RT1011_I2S_CH_RX_LEN_20B (0x1 << 0)
+#define RT1011_I2S_CH_RX_LEN_24B (0x2 << 0)
+#define RT1011_I2S_CH_RX_LEN_32B (0x3 << 0)
+#define RT1011_I2S_CH_RX_LEN_8B (0x4 << 0)
+
+/* TDM1 Setting-2 (0x0116) */
+#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK (0x7 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_2CH (0x1 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_4CH (0x3 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_6CH (0x5 << 13)
+#define RT1011_TDM_I2S_DOCK_ADCDAT_8CH (0x7 << 13)
+#define RT1011_TDM_I2S_DOCK_EN_1_MASK (0x1 << 3)
+#define RT1011_TDM_I2S_DOCK_EN_1_SFT 3
+#define RT1011_TDM_I2S_DOCK_EN_1 (0x1 << 3)
+#define RT1011_TDM_ADCDAT1_DATA_LOCATION (0x7 << 0)
+
+/* TDM1 Setting-3 (0x0118) */
+#define RT1011_TDM_I2S_RX_ADC1_1_MASK (0x3 << 6)
+#define RT1011_TDM_I2S_RX_ADC2_1_MASK (0x3 << 4)
+#define RT1011_TDM_I2S_RX_ADC3_1_MASK (0x3 << 2)
+#define RT1011_TDM_I2S_RX_ADC4_1_MASK (0x3 << 0)
+#define RT1011_TDM_I2S_RX_ADC1_1_LL (0x2 << 6)
+#define RT1011_TDM_I2S_RX_ADC2_1_LL (0x2 << 4)
+#define RT1011_TDM_I2S_RX_ADC3_1_LL (0x2 << 2)
+#define RT1011_TDM_I2S_RX_ADC4_1_LL (0x2 << 0)
+
+/* TDM1 Setting-4 (0x011a) */
+#define RT1011_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12)
+#define RT1011_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8)
+#define RT1011_TDM_I2S_TX_L_DAC1_1_SFT 12
+#define RT1011_TDM_I2S_TX_R_DAC1_1_SFT 8
+
+/* TDM2 Setting-2 (0x0120) */
+#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_2_MASK (0x7 << 13)
+#define RT1011_TDM_I2S_DOCK_EN_2_MASK (0x1 << 3)
+#define RT1011_TDM_I2S_DOCK_EN_2_SFT 3
+#define RT1011_TDM_I2S_DOCK_EN_2 (0x1 << 3)
+
+/* MIXER 1 (0x0300) */
+#define RT1011_MIXER_MUTE_MIX_I_MASK (0x1 << 15)
+#define RT1011_MIXER_MUTE_MIX_I_SFT 15
+#define RT1011_MIXER_MUTE_MIX_I (0x1 << 15)
+#define RT1011_MIXER_MUTE_SUM_I_MASK (0x1 << 14)
+#define RT1011_MIXER_MUTE_SUM_I_SFT 14
+#define RT1011_MIXER_MUTE_SUM_I (0x1 << 14)
+#define RT1011_MIXER_MUTE_MIX_V_MASK (0x1 << 7)
+#define RT1011_MIXER_MUTE_MIX_V_SFT 7
+#define RT1011_MIXER_MUTE_MIX_V (0x1 << 7)
+#define RT1011_MIXER_MUTE_SUM_V_MASK (0x1 << 6)
+#define RT1011_MIXER_MUTE_SUM_V_SFT 6
+#define RT1011_MIXER_MUTE_SUM_V (0x1 << 6)
+
+/* Analog Temperature Sensor (0x0316) */
+#define RT1011_POW_TEMP_REG (0x1 << 2)
+#define RT1011_POW_TEMP_REG_BIT 2
+
+/* POWER-1 (0x0322) */
+#define RT1011_POW_LDO2 (0x1 << 15)
+#define RT1011_POW_LDO2_BIT 15
+#define RT1011_POW_DAC (0x1 << 14)
+#define RT1011_POW_DAC_BIT 14
+#define RT1011_POW_CLK12M (0x1 << 13)
+#define RT1011_POW_CLK12M_BIT 13
+#define RT1011_POW_TEMP (0x1 << 12)
+#define RT1011_POW_TEMP_BIT 12
+#define RT1011_POW_ISENSE_SPK (0x1 << 7)
+#define RT1011_POW_ISENSE_SPK_BIT 7
+#define RT1011_POW_LPF_SPK (0x1 << 6)
+#define RT1011_POW_LPF_SPK_BIT 6
+#define RT1011_POW_VSENSE_SPK (0x1 << 5)
+#define RT1011_POW_VSENSE_SPK_BIT 5
+#define RT1011_POW_TWO_BATTERY_SPK (0x1 << 4)
+#define RT1011_POW_TWO_BATTERY_SPK_BIT 4
+
+/* POWER-2 (0x0324) */
+#define RT1011_PLLEN (0x1 << 2)
+#define RT1011_PLLEN_BIT 2
+#define RT1011_POW_BG (0x1 << 1)
+#define RT1011_POW_BG_BIT 1
+#define RT1011_POW_BG_MBIAS_LV (0x1 << 0)
+#define RT1011_POW_BG_MBIAS_LV_BIT 0
+
+/* POWER-3 (0x0326) */
+#define RT1011_POW_DET_SPKVDD (0x1 << 15)
+#define RT1011_POW_DET_SPKVDD_BIT 15
+#define RT1011_POW_DET_VBAT (0x1 << 14)
+#define RT1011_POW_DET_VBAT_BIT 14
+#define RT1011_POW_FC (0x1 << 13)
+#define RT1011_POW_FC_BIT 13
+#define RT1011_POW_MBIAS_LV (0x1 << 12)
+#define RT1011_POW_MBIAS_LV_BIT 12
+#define RT1011_POW_ADC_I (0x1 << 11)
+#define RT1011_POW_ADC_I_BIT 11
+#define RT1011_POW_ADC_V (0x1 << 10)
+#define RT1011_POW_ADC_V_BIT 10
+#define RT1011_POW_ADC_T (0x1 << 9)
+#define RT1011_POW_ADC_T_BIT 9
+#define RT1011_POWD_ADC_T (0x1 << 8)
+#define RT1011_POWD_ADC_T_BIT 8
+#define RT1011_POW_MIX_I (0x1 << 7)
+#define RT1011_POW_MIX_I_BIT 7
+#define RT1011_POW_MIX_V (0x1 << 6)
+#define RT1011_POW_MIX_V_BIT 6
+#define RT1011_POW_SUM_I (0x1 << 5)
+#define RT1011_POW_SUM_I_BIT 5
+#define RT1011_POW_SUM_V (0x1 << 4)
+#define RT1011_POW_SUM_V_BIT 4
+#define RT1011_POW_MIX_T (0x1 << 2)
+#define RT1011_POW_MIX_T_BIT 2
+#define RT1011_BYPASS_MIX_T (0x1 << 1)
+#define RT1011_BYPASS_MIX_T_BIT 1
+#define RT1011_POW_VREF_LV (0x1 << 0)
+#define RT1011_POW_VREF_LV_BIT 0
+
+/* POWER-4 (0x0328) */
+#define RT1011_POW_EN_SWR (0x1 << 12)
+#define RT1011_POW_EN_SWR_BIT 12
+#define RT1011_POW_EN_PASS_BGOK_SWR (0x1 << 10)
+#define RT1011_POW_EN_PASS_BGOK_SWR_BIT 10
+#define RT1011_POW_EN_PASS_VPOK_SWR (0x1 << 9)
+#define RT1011_POW_EN_PASS_VPOK_SWR_BIT 9
+
+/* POWER-9 (0x032d) */
+#define RT1011_POW_SDB_REG_MASK (0x1 << 9)
+#define RT1011_POW_SDB_REG_BIT 9
+#define RT1011_POW_SDB_REG (0x1 << 9)
+#define RT1011_POW_SEL_SDB_MODE_MASK (0x1 << 6)
+#define RT1011_POW_SEL_SDB_MODE_BIT 6
+#define RT1011_POW_SEL_SDB_MODE (0x1 << 6)
+#define RT1011_POW_MNL_SDB_MASK (0x1 << 5)
+#define RT1011_POW_MNL_SDB_BIT 5
+#define RT1011_POW_MNL_SDB (0x1 << 5)
+
+/* SPK Protection-Temperature Protection (0x050c) */
+#define RT1011_STP_EN_MASK (0x1 << 15)
+#define RT1011_STP_EN_BIT 15
+#define RT1011_STP_EN (0x1 << 15)
+#define RT1011_STP_RS_CLB_EN_MASK (0x1 << 14)
+#define RT1011_STP_RS_CLB_EN_BIT 14
+#define RT1011_STP_RS_CLB_EN (0x1 << 14)
+
+/* SPK Protection-Temperature Protection-4 (0x0510) */
+#define RT1011_STP_R0_SELECT_MASK (0x3 << 6)
+#define RT1011_STP_R0_SELECT_EFUSE (0x0 << 6)
+#define RT1011_STP_R0_SELECT_START_VAL (0x1 << 6)
+#define RT1011_STP_R0_SELECT_REG (0x2 << 6)
+#define RT1011_STP_R0_SELECT_FORCE_ZERO (0x3 << 6)
+
+/* SPK Protection-Temperature Protection-6 (0x0512) */
+#define RT1011_STP_R0_EN_MASK (0x1 << 7)
+#define RT1011_STP_R0_EN_BIT 7
+#define RT1011_STP_R0_EN (0x1 << 7)
+#define RT1011_STP_T0_EN_MASK (0x1 << 6)
+#define RT1011_STP_T0_EN_BIT 6
+#define RT1011_STP_T0_EN (0x1 << 6)
+
+/* Cross Biquad Setting-1 (0x0702) */
+#define RT1011_MONO_LR_SEL_MASK (0x3 << 5)
+#define RT1011_MONO_L_CHANNEL (0x0 << 5)
+#define RT1011_MONO_R_CHANNEL (0x1 << 5)
+#define RT1011_MONO_LR_MIX_CHANNEL (0x2 << 5)
+
+/* ClassD Internal Setting-1 (0x1300) */
+#define RT1011_DRIVER_READY_SPK (0x1 << 12)
+#define RT1011_DRIVER_READY_SPK_BIT 12
+#define RT1011_RECV_MODE_SPK_MASK (0x1 << 5)
+#define RT1011_SPK_MODE (0x0 << 5)
+#define RT1011_RECV_MODE (0x1 << 5)
+#define RT1011_RECV_MODE_SPK_BIT 5
+
+/* ClassD Internal Setting-3 (0x1304) */
+#define RT1011_REG_GAIN_CLASSD_RI_SPK_MASK (0x7 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_410K (0x0 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_95K (0x1 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_82P5K (0x2 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_72P5K (0x3 << 12)
+#define RT1011_REG_GAIN_CLASSD_RI_62P5K (0x4 << 12)
+
+/* ClassD Internal Setting-8 (0x130c) */
+#define RT1011_TM_PORPVDD_SPK (0x1 << 1)
+#define RT1011_TM_PORPVDD_SPK_BIT 1
+
+/* SPK Protection-Temperature Protection-SINE_GEN_REG-1 (0x1500) */
+#define RT1011_STP_SIN_GEN_EN_MASK (0x1 << 13)
+#define RT1011_STP_SIN_GEN_EN (0x1 << 13)
+#define RT1011_STP_SIN_GEN_EN_BIT 13
+
+
+/* System Clock Source */
+enum {
+ RT1011_FS_SYS_PRE_S_MCLK,
+ RT1011_FS_SYS_PRE_S_BCLK,
+ RT1011_FS_SYS_PRE_S_PLL1,
+ RT1011_FS_SYS_PRE_S_RCCLK, /* 12M Hz */
+};
+
+/* PLL Source 1/2 */
+enum {
+ RT1011_PLL1_S_BCLK,
+ RT1011_PLL2_S_MCLK,
+ RT1011_PLL2_S_RCCLK, /* 12M Hz */
+};
+
+enum {
+ RT1011_AIF1,
+ RT1011_AIFS
+};
+
+enum {
+ RT1011_I2S_REF_NONE,
+ RT1011_I2S_REF_LEFT_CH,
+ RT1011_I2S_REF_RIGHT_CH,
+};
+
+/* BiQual & DRC related settings */
+#define RT1011_BQ_DRC_NUM 128
+struct rt1011_bq_drc_params {
+ unsigned short val;
+ unsigned short reg;
+#ifdef CONFIG_64BIT
+ unsigned int reserved;
+#endif
+};
+enum {
+ RT1011_ADVMODE_INITIAL_SET,
+ RT1011_ADVMODE_SEP_BQ_COEFF,
+ RT1011_ADVMODE_EQ_BQ_COEFF,
+ RT1011_ADVMODE_BQ_UI_COEFF,
+ RT1011_ADVMODE_SMARTBOOST_COEFF,
+ RT1011_ADVMODE_NUM,
+};
+
+struct rt1011_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct work_struct cali_work;
+ struct rt1011_bq_drc_params **bq_drc_params;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int id;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int bq_drc_set;
+ unsigned int r0_reg, cali_done;
+ unsigned int r0_calib, temperature_calib;
+ int recv_spk_mode;
+ int i2s_ref;
+};
+
+#endif /* end of _RT1011_H_ */
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
new file mode 100644
index 000000000000..ca1ed9d5a24e
--- /dev/null
+++ b/sound/soc/codecs/rt1015.c
@@ -0,0 +1,1190 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1015.c -- RT1015 ALSA SoC audio amplifier driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+//
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rt1015.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1015.h"
+
+static const struct rt1015_platform_data i2s_default_platform_data = {
+ .power_up_delay_ms = 50,
+};
+
+static const struct reg_default rt1015_reg[] = {
+ { 0x0000, 0x0000 },
+ { 0x0004, 0xa000 },
+ { 0x0006, 0x0003 },
+ { 0x000a, 0x081e },
+ { 0x000c, 0x0006 },
+ { 0x000e, 0x0000 },
+ { 0x0010, 0x0000 },
+ { 0x0012, 0x0000 },
+ { 0x0014, 0x0000 },
+ { 0x0016, 0x0000 },
+ { 0x0018, 0x0000 },
+ { 0x0020, 0x8000 },
+ { 0x0022, 0x8043 },
+ { 0x0076, 0x0000 },
+ { 0x0078, 0x0000 },
+ { 0x007a, 0x0002 },
+ { 0x007c, 0x10ec },
+ { 0x007d, 0x1015 },
+ { 0x00f0, 0x5000 },
+ { 0x00f2, 0x004c },
+ { 0x00f3, 0xecfe },
+ { 0x00f4, 0x0000 },
+ { 0x00f6, 0x0400 },
+ { 0x0100, 0x0028 },
+ { 0x0102, 0xff02 },
+ { 0x0104, 0xa213 },
+ { 0x0106, 0x200c },
+ { 0x010c, 0x0000 },
+ { 0x010e, 0x0058 },
+ { 0x0111, 0x0200 },
+ { 0x0112, 0x0400 },
+ { 0x0114, 0x0022 },
+ { 0x0116, 0x0000 },
+ { 0x0118, 0x0000 },
+ { 0x011a, 0x0123 },
+ { 0x011c, 0x4567 },
+ { 0x0300, 0x203d },
+ { 0x0302, 0x001e },
+ { 0x0311, 0x0000 },
+ { 0x0313, 0x6014 },
+ { 0x0314, 0x00a2 },
+ { 0x031a, 0x00a0 },
+ { 0x031c, 0x001f },
+ { 0x031d, 0xffff },
+ { 0x031e, 0x0000 },
+ { 0x031f, 0x0000 },
+ { 0x0320, 0x0000 },
+ { 0x0321, 0x0000 },
+ { 0x0322, 0xd7df },
+ { 0x0328, 0x10b2 },
+ { 0x0329, 0x0175 },
+ { 0x032a, 0x36ad },
+ { 0x032b, 0x7e55 },
+ { 0x032c, 0x0520 },
+ { 0x032d, 0xaa00 },
+ { 0x032e, 0x570e },
+ { 0x0330, 0xe180 },
+ { 0x0332, 0x0034 },
+ { 0x0334, 0x0001 },
+ { 0x0336, 0x0010 },
+ { 0x0338, 0x0000 },
+ { 0x04fa, 0x0030 },
+ { 0x04fc, 0x35c8 },
+ { 0x04fe, 0x0800 },
+ { 0x0500, 0x0400 },
+ { 0x0502, 0x1000 },
+ { 0x0504, 0x0000 },
+ { 0x0506, 0x04ff },
+ { 0x0508, 0x0010 },
+ { 0x050a, 0x001a },
+ { 0x0519, 0x1c68 },
+ { 0x051a, 0x0ccc },
+ { 0x051b, 0x0666 },
+ { 0x051d, 0x0000 },
+ { 0x051f, 0x0000 },
+ { 0x0536, 0x061c },
+ { 0x0538, 0x0000 },
+ { 0x053a, 0x0000 },
+ { 0x053c, 0x0000 },
+ { 0x053d, 0x0000 },
+ { 0x053e, 0x0000 },
+ { 0x053f, 0x0000 },
+ { 0x0540, 0x0000 },
+ { 0x0541, 0x0000 },
+ { 0x0542, 0x0000 },
+ { 0x0543, 0x0000 },
+ { 0x0544, 0x0000 },
+ { 0x0568, 0x0000 },
+ { 0x056a, 0x0000 },
+ { 0x1000, 0x0040 },
+ { 0x1002, 0x5405 },
+ { 0x1006, 0x5515 },
+ { 0x1007, 0x05f7 },
+ { 0x1009, 0x0b0a },
+ { 0x100a, 0x00ef },
+ { 0x100d, 0x0003 },
+ { 0x1010, 0xa433 },
+ { 0x1020, 0x0000 },
+ { 0x1200, 0x5a01 },
+ { 0x1202, 0x6524 },
+ { 0x1204, 0x1f00 },
+ { 0x1206, 0x0000 },
+ { 0x1208, 0x0000 },
+ { 0x120a, 0x0000 },
+ { 0x120c, 0x0000 },
+ { 0x120e, 0x0000 },
+ { 0x1210, 0x0000 },
+ { 0x1212, 0x0000 },
+ { 0x1300, 0x10a1 },
+ { 0x1302, 0x12ff },
+ { 0x1304, 0x0400 },
+ { 0x1305, 0x0844 },
+ { 0x1306, 0x4611 },
+ { 0x1308, 0x555e },
+ { 0x130a, 0x0000 },
+ { 0x130c, 0x2000 },
+ { 0x130e, 0x0100 },
+ { 0x130f, 0x0001 },
+ { 0x1310, 0x0000 },
+ { 0x1312, 0x0000 },
+ { 0x1314, 0x0000 },
+ { 0x1316, 0x0000 },
+ { 0x1318, 0x0000 },
+ { 0x131a, 0x0000 },
+ { 0x1322, 0x0029 },
+ { 0x1323, 0x4a52 },
+ { 0x1324, 0x002c },
+ { 0x1325, 0x0b02 },
+ { 0x1326, 0x002d },
+ { 0x1327, 0x6b5a },
+ { 0x1328, 0x002e },
+ { 0x1329, 0xcbb2 },
+ { 0x132a, 0x0030 },
+ { 0x132b, 0x2c0b },
+ { 0x1330, 0x0031 },
+ { 0x1331, 0x8c63 },
+ { 0x1332, 0x0032 },
+ { 0x1333, 0xecbb },
+ { 0x1334, 0x0034 },
+ { 0x1335, 0x4d13 },
+ { 0x1336, 0x0037 },
+ { 0x1337, 0x0dc3 },
+ { 0x1338, 0x003d },
+ { 0x1339, 0xef7b },
+ { 0x133a, 0x0044 },
+ { 0x133b, 0xd134 },
+ { 0x133c, 0x0047 },
+ { 0x133d, 0x91e4 },
+ { 0x133e, 0x004d },
+ { 0x133f, 0xc370 },
+ { 0x1340, 0x0053 },
+ { 0x1341, 0xf4fd },
+ { 0x1342, 0x0060 },
+ { 0x1343, 0x5816 },
+ { 0x1344, 0x006c },
+ { 0x1345, 0xbb2e },
+ { 0x1346, 0x0072 },
+ { 0x1347, 0xecbb },
+ { 0x1348, 0x0076 },
+ { 0x1349, 0x5d97 },
+};
+
+static bool rt1015_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1015_RESET:
+ case RT1015_CLK_DET:
+ case RT1015_SIL_DET:
+ case RT1015_VER_ID:
+ case RT1015_VENDOR_ID:
+ case RT1015_DEVICE_ID:
+ case RT1015_PRO_ALT:
+ case RT1015_MAN_I2C:
+ case RT1015_DAC3:
+ case RT1015_VBAT_TEST_OUT1:
+ case RT1015_VBAT_TEST_OUT2:
+ case RT1015_VBAT_PROT_ATT:
+ case RT1015_VBAT_DET_CODE:
+ case RT1015_SMART_BST_CTRL1:
+ case RT1015_SPK_DC_DETECT1:
+ case RT1015_SPK_DC_DETECT4:
+ case RT1015_SPK_DC_DETECT5:
+ case RT1015_DC_CALIB_CLSD1:
+ case RT1015_DC_CALIB_CLSD5:
+ case RT1015_DC_CALIB_CLSD6:
+ case RT1015_DC_CALIB_CLSD7:
+ case RT1015_DC_CALIB_CLSD8:
+ case RT1015_S_BST_TIMING_INTER1:
+ case RT1015_OSCK_STA:
+ case RT1015_MONO_DYNA_CTRL1:
+ case RT1015_MONO_DYNA_CTRL5:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1015_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1015_RESET:
+ case RT1015_CLK2:
+ case RT1015_CLK3:
+ case RT1015_PLL1:
+ case RT1015_PLL2:
+ case RT1015_DUM_RW1:
+ case RT1015_DUM_RW2:
+ case RT1015_DUM_RW3:
+ case RT1015_DUM_RW4:
+ case RT1015_DUM_RW5:
+ case RT1015_DUM_RW6:
+ case RT1015_CLK_DET:
+ case RT1015_SIL_DET:
+ case RT1015_CUSTOMER_ID:
+ case RT1015_PCODE_FWVER:
+ case RT1015_VER_ID:
+ case RT1015_VENDOR_ID:
+ case RT1015_DEVICE_ID:
+ case RT1015_PAD_DRV1:
+ case RT1015_PAD_DRV2:
+ case RT1015_GAT_BOOST:
+ case RT1015_PRO_ALT:
+ case RT1015_OSCK_STA:
+ case RT1015_MAN_I2C:
+ case RT1015_DAC1:
+ case RT1015_DAC2:
+ case RT1015_DAC3:
+ case RT1015_ADC1:
+ case RT1015_ADC2:
+ case RT1015_TDM_MASTER:
+ case RT1015_TDM_TCON:
+ case RT1015_TDM1_1:
+ case RT1015_TDM1_2:
+ case RT1015_TDM1_3:
+ case RT1015_TDM1_4:
+ case RT1015_TDM1_5:
+ case RT1015_MIXER1:
+ case RT1015_MIXER2:
+ case RT1015_ANA_PROTECT1:
+ case RT1015_ANA_CTRL_SEQ1:
+ case RT1015_ANA_CTRL_SEQ2:
+ case RT1015_VBAT_DET_DEB:
+ case RT1015_VBAT_VOLT_DET1:
+ case RT1015_VBAT_VOLT_DET2:
+ case RT1015_VBAT_TEST_OUT1:
+ case RT1015_VBAT_TEST_OUT2:
+ case RT1015_VBAT_PROT_ATT:
+ case RT1015_VBAT_DET_CODE:
+ case RT1015_PWR1:
+ case RT1015_PWR4:
+ case RT1015_PWR5:
+ case RT1015_PWR6:
+ case RT1015_PWR7:
+ case RT1015_PWR8:
+ case RT1015_PWR9:
+ case RT1015_CLASSD_SEQ:
+ case RT1015_SMART_BST_CTRL1:
+ case RT1015_SMART_BST_CTRL2:
+ case RT1015_ANA_CTRL1:
+ case RT1015_ANA_CTRL2:
+ case RT1015_PWR_STATE_CTRL:
+ case RT1015_MONO_DYNA_CTRL:
+ case RT1015_MONO_DYNA_CTRL1:
+ case RT1015_MONO_DYNA_CTRL2:
+ case RT1015_MONO_DYNA_CTRL3:
+ case RT1015_MONO_DYNA_CTRL4:
+ case RT1015_MONO_DYNA_CTRL5:
+ case RT1015_SPK_VOL:
+ case RT1015_SHORT_DETTOP1:
+ case RT1015_SHORT_DETTOP2:
+ case RT1015_SPK_DC_DETECT1:
+ case RT1015_SPK_DC_DETECT2:
+ case RT1015_SPK_DC_DETECT3:
+ case RT1015_SPK_DC_DETECT4:
+ case RT1015_SPK_DC_DETECT5:
+ case RT1015_BAT_RPO_STEP1:
+ case RT1015_BAT_RPO_STEP2:
+ case RT1015_BAT_RPO_STEP3:
+ case RT1015_BAT_RPO_STEP4:
+ case RT1015_BAT_RPO_STEP5:
+ case RT1015_BAT_RPO_STEP6:
+ case RT1015_BAT_RPO_STEP7:
+ case RT1015_BAT_RPO_STEP8:
+ case RT1015_BAT_RPO_STEP9:
+ case RT1015_BAT_RPO_STEP10:
+ case RT1015_BAT_RPO_STEP11:
+ case RT1015_BAT_RPO_STEP12:
+ case RT1015_SPREAD_SPEC1:
+ case RT1015_SPREAD_SPEC2:
+ case RT1015_PAD_STATUS:
+ case RT1015_PADS_PULLING_CTRL1:
+ case RT1015_PADS_DRIVING:
+ case RT1015_SYS_RST1:
+ case RT1015_SYS_RST2:
+ case RT1015_SYS_GATING1:
+ case RT1015_TEST_MODE1:
+ case RT1015_TEST_MODE2:
+ case RT1015_TIMING_CTRL1:
+ case RT1015_PLL_INT:
+ case RT1015_TEST_OUT1:
+ case RT1015_DC_CALIB_CLSD1:
+ case RT1015_DC_CALIB_CLSD2:
+ case RT1015_DC_CALIB_CLSD3:
+ case RT1015_DC_CALIB_CLSD4:
+ case RT1015_DC_CALIB_CLSD5:
+ case RT1015_DC_CALIB_CLSD6:
+ case RT1015_DC_CALIB_CLSD7:
+ case RT1015_DC_CALIB_CLSD8:
+ case RT1015_DC_CALIB_CLSD9:
+ case RT1015_DC_CALIB_CLSD10:
+ case RT1015_CLSD_INTERNAL1:
+ case RT1015_CLSD_INTERNAL2:
+ case RT1015_CLSD_INTERNAL3:
+ case RT1015_CLSD_INTERNAL4:
+ case RT1015_CLSD_INTERNAL5:
+ case RT1015_CLSD_INTERNAL6:
+ case RT1015_CLSD_INTERNAL7:
+ case RT1015_CLSD_INTERNAL8:
+ case RT1015_CLSD_INTERNAL9:
+ case RT1015_CLSD_OCP_CTRL:
+ case RT1015_VREF_LV:
+ case RT1015_MBIAS1:
+ case RT1015_MBIAS2:
+ case RT1015_MBIAS3:
+ case RT1015_MBIAS4:
+ case RT1015_VREF_LV1:
+ case RT1015_S_BST_TIMING_INTER1:
+ case RT1015_S_BST_TIMING_INTER2:
+ case RT1015_S_BST_TIMING_INTER3:
+ case RT1015_S_BST_TIMING_INTER4:
+ case RT1015_S_BST_TIMING_INTER5:
+ case RT1015_S_BST_TIMING_INTER6:
+ case RT1015_S_BST_TIMING_INTER7:
+ case RT1015_S_BST_TIMING_INTER8:
+ case RT1015_S_BST_TIMING_INTER9:
+ case RT1015_S_BST_TIMING_INTER10:
+ case RT1015_S_BST_TIMING_INTER11:
+ case RT1015_S_BST_TIMING_INTER12:
+ case RT1015_S_BST_TIMING_INTER13:
+ case RT1015_S_BST_TIMING_INTER14:
+ case RT1015_S_BST_TIMING_INTER15:
+ case RT1015_S_BST_TIMING_INTER16:
+ case RT1015_S_BST_TIMING_INTER17:
+ case RT1015_S_BST_TIMING_INTER18:
+ case RT1015_S_BST_TIMING_INTER19:
+ case RT1015_S_BST_TIMING_INTER20:
+ case RT1015_S_BST_TIMING_INTER21:
+ case RT1015_S_BST_TIMING_INTER22:
+ case RT1015_S_BST_TIMING_INTER23:
+ case RT1015_S_BST_TIMING_INTER24:
+ case RT1015_S_BST_TIMING_INTER25:
+ case RT1015_S_BST_TIMING_INTER26:
+ case RT1015_S_BST_TIMING_INTER27:
+ case RT1015_S_BST_TIMING_INTER28:
+ case RT1015_S_BST_TIMING_INTER29:
+ case RT1015_S_BST_TIMING_INTER30:
+ case RT1015_S_BST_TIMING_INTER31:
+ case RT1015_S_BST_TIMING_INTER32:
+ case RT1015_S_BST_TIMING_INTER33:
+ case RT1015_S_BST_TIMING_INTER34:
+ case RT1015_S_BST_TIMING_INTER35:
+ case RT1015_S_BST_TIMING_INTER36:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0);
+
+static const char * const rt1015_din_source_select[] = {
+ "Left",
+ "Right",
+ "Left + Right average",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_mono_lr_sel, RT1015_PAD_DRV2, 4,
+ rt1015_din_source_select);
+
+static const char * const rt1015_boost_mode[] = {
+ "Bypass", "Adaptive", "Fixed Adaptive"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_boost_mode_enum, 0, 0,
+ rt1015_boost_mode);
+
+static int rt1015_boost_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1015->boost_mode;
+
+ return 0;
+}
+
+static int rt1015_boost_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+ int boost_mode = ucontrol->value.integer.value[0];
+
+ switch (boost_mode) {
+ case BYPASS:
+ snd_soc_component_update_bits(component,
+ RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
+ RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
+ RT1015_ABST_REG_MODE | RT1015_ABST_FIX_TGT_DIS |
+ RT1015_BYPASS_SWRREG_BYPASS);
+ break;
+ case ADAPTIVE:
+ snd_soc_component_update_bits(component,
+ RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
+ RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
+ RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_DIS |
+ RT1015_BYPASS_SWRREG_PASS);
+ break;
+ case FIXED_ADAPTIVE:
+ snd_soc_component_update_bits(component,
+ RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK |
+ RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK,
+ RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_EN |
+ RT1015_BYPASS_SWRREG_PASS);
+ break;
+ default:
+ dev_err(component->dev, "Unknown boost control.\n");
+ return -EINVAL;
+ }
+
+ rt1015->boost_mode = boost_mode;
+
+ return 0;
+}
+
+static int rt1015_bypass_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1015->bypass_boost;
+
+ return 0;
+}
+
+static void rt1015_calibrate(struct rt1015_priv *rt1015)
+{
+ struct snd_soc_component *component = rt1015->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct regmap *regmap = rt1015->regmap;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ regcache_cache_bypass(regmap, true);
+
+ regmap_write(regmap, RT1015_CLK_DET, 0x0000);
+ regmap_write(regmap, RT1015_PWR4, 0x00B2);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0009);
+ msleep(100);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000A);
+ msleep(100);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000C);
+ msleep(100);
+ regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2028);
+ regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000D);
+ msleep(300);
+ regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0008);
+ regmap_write(regmap, RT1015_SYS_RST1, 0x05F5);
+ regmap_write(regmap, RT1015_CLK_DET, 0x8000);
+
+ regcache_cache_bypass(regmap, false);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ if (rt1015->dac_is_used) {
+ dev_err(component->dev, "DAC is being used!\n");
+ return -EBUSY;
+ }
+
+ rt1015->bypass_boost = ucontrol->value.integer.value[0];
+ if (rt1015->bypass_boost == RT1015_Bypass_Boost &&
+ !rt1015->cali_done) {
+ rt1015_calibrate(rt1015);
+ rt1015->cali_done = 1;
+
+ regmap_write(rt1015->regmap, RT1015_MONO_DYNA_CTRL, 0x0010);
+ }
+
+ return 0;
+}
+
+static const char * const rt1015_dac_output_vol_select[] = {
+ "immediate",
+ "zero detection + immediate change",
+ "zero detection + inc/dec change",
+ "zero detection + soft inc/dec change",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_dac_vol_ctl_enum,
+ RT1015_DAC3, 2, rt1015_dac_output_vol_select);
+
+static const struct snd_kcontrol_new rt1015_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT,
+ 127, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC Playback Switch", RT1015_DAC3,
+ RT1015_DA_MUTE_SFT, RT1015_DVOL_MUTE_FLAG_SFT, 1, 1),
+ SOC_ENUM_EXT("Boost Mode", rt1015_boost_mode_enum,
+ rt1015_boost_mode_get, rt1015_boost_mode_put),
+ SOC_ENUM("Mono LR Select", rt1015_mono_lr_sel),
+ SOC_SINGLE_EXT("Bypass Boost", SND_SOC_NOPM, 0, 1, 0,
+ rt1015_bypass_boost_get, rt1015_bypass_boost_put),
+
+ /* DAC Output Volume Control */
+ SOC_ENUM("DAC Output Control", rt1015_dac_vol_ctl_enum),
+};
+
+static int rt1015_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ if (rt1015->sysclk_src == RT1015_SCLK_S_PLL)
+ return 1;
+ else
+ return 0;
+}
+
+static int r1015_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rt1015->dac_is_used = 1;
+ if (rt1015->bypass_boost == RT1015_Enable_Boost) {
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f7);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b0a);
+ snd_soc_component_write(component,
+ RT1015_GAT_BOOST, 0xacfe);
+ snd_soc_component_write(component,
+ RT1015_PWR9, 0xaa00);
+ snd_soc_component_write(component,
+ RT1015_GAT_BOOST, 0xecfe);
+ } else {
+ snd_soc_component_write(component,
+ 0x032d, 0xaa60);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f7);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b0a);
+ snd_soc_component_write(component,
+ RT1015_PWR_STATE_CTRL, 0x008e);
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (rt1015->bypass_boost == RT1015_Enable_Boost) {
+ snd_soc_component_write(component,
+ RT1015_PWR9, 0xa800);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f5);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b9a);
+ } else {
+ snd_soc_component_write(component,
+ 0x032d, 0xaa60);
+ snd_soc_component_write(component,
+ RT1015_PWR_STATE_CTRL, 0x0088);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST1, 0x05f5);
+ snd_soc_component_write(component,
+ RT1015_SYS_RST2, 0x0b9a);
+ }
+ rt1015->dac_is_used = 0;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ unsigned int ret, ret2;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = snd_soc_component_read(component, RT1015_CLK_DET);
+ ret2 = snd_soc_component_read(component, RT1015_SPK_DC_DETECT1);
+ if (!((ret >> 15) & 0x1)) {
+ snd_soc_component_update_bits(component, RT1015_CLK_DET,
+ RT1015_EN_BCLK_DET_MASK, RT1015_EN_BCLK_DET);
+ dev_dbg(component->dev, "BCLK Detection Enabled.\n");
+ }
+ if (!((ret2 >> 12) & 0x1)) {
+ snd_soc_component_update_bits(component, RT1015_SPK_DC_DETECT1,
+ RT1015_EN_CLA_D_DC_DET_MASK, RT1015_EN_CLA_D_DC_DET);
+ dev_dbg(component->dev, "Class-D DC Detection Enabled.\n");
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(rt1015->pdata.power_up_delay_ms);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", RT1015_PWR1, RT1015_PWR_PLL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ r1015_dac_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1015_amp_drv_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1015_dapm_routes[] = {
+ { "DAC", NULL, "AIFRX" },
+ { "DAC", NULL, "PLL", rt1015_is_sys_clk_from_pll},
+ { "Amp Drv", NULL, "DAC" },
+ { "SPO", NULL, "Amp Drv" },
+};
+
+static int rt1015_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ int pre_div, frame_size, lrck;
+ unsigned int val_len = 0;
+
+ lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt1015->sysclk, lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock rate\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "pre_div is %d for iis %d\n", pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len = RT1015_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT1015_I2S_DL_24;
+ break;
+ case 8:
+ val_len = RT1015_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1015_TDM_MASTER,
+ RT1015_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_FS_PD_MASK, pre_div << RT1015_FS_PD_SFT);
+
+ return 0;
+}
+
+static int rt1015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg_val = 0, reg_val2 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ reg_val |= RT1015_TCON_TDM_MS_M;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT1015_TCON_TDM_MS_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val2 |= RT1015_TDM_INV_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1015_I2S_M_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1015_I2S_M_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1015_I2S_M_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1015_TDM_MASTER,
+ RT1015_TCON_TDM_MS_MASK | RT1015_I2S_M_DF_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component, RT1015_TDM1_1,
+ RT1015_TDM_INV_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1015_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1015->sysclk && clk_id == rt1015->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1015_SCLK_S_MCLK:
+ reg_val |= RT1015_CLK_SYS_PRE_SEL_MCLK;
+ break;
+
+ case RT1015_SCLK_S_PLL:
+ reg_val |= RT1015_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1015->sysclk = freq;
+ rt1015->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1015_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1015->pll_in = 0;
+ rt1015->pll_out = 0;
+
+ return 0;
+ }
+
+ if (source == rt1015->pll_src && freq_in == rt1015->pll_in &&
+ freq_out == rt1015->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1015_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_PLL_SRC2);
+ break;
+
+ case RT1015_PLL_S_BCLK:
+ snd_soc_component_update_bits(component, RT1015_CLK2,
+ RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_BCLK);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1015_PLL1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1015_PLL_M_SFT) |
+ (pll_code.m_bp << RT1015_PLL_M_BP_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1015_PLL2,
+ pll_code.k_code);
+
+ rt1015->pll_in = freq_in;
+ rt1015->pll_out = freq_out;
+ rt1015->pll_src = source;
+
+ return 0;
+}
+
+static int rt1015_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0, rx_slotnum, tx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 2:
+ val |= RT1015_I2S_TX_2CH;
+ break;
+ case 4:
+ val |= RT1015_I2S_TX_4CH;
+ break;
+ case 6:
+ val |= RT1015_I2S_TX_6CH;
+ break;
+ case 8:
+ val |= RT1015_I2S_TX_8CH;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ switch (slot_width) {
+ case 16:
+ val |= RT1015_I2S_CH_TX_LEN_16B;
+ break;
+ case 20:
+ val |= RT1015_I2S_CH_TX_LEN_20B;
+ break;
+ case 24:
+ val |= RT1015_I2S_CH_TX_LEN_24B;
+ break;
+ case 32:
+ val |= RT1015_I2S_CH_TX_LEN_32B;
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum != 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ /* This is an assumption that the system sends stereo audio to the amplifier typically.
+ * And the stereo audio is placed in slot 0/2/4/6 as the starting slot.
+ * The users could select the channel from L/R/L+R by "Mono LR Select" control.
+ */
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1015_TDM1_4,
+ RT1015_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1015_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit+1) << RT1015_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1015_TDM1_4,
+ RT1015_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1015_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit-1) << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1015_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ if (tx_slotnum) {
+ ret = -EINVAL;
+ dev_err(component->dev, "doesn't need to support tx slots\n");
+ goto _set_tdm_err_;
+ }
+
+ snd_soc_component_update_bits(component, RT1015_TDM1_1,
+ RT1015_I2S_CH_TX_MASK | RT1015_I2S_CH_RX_MASK |
+ RT1015_I2S_CH_TX_LEN_MASK | RT1015_I2S_CH_RX_LEN_MASK, val);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1015_probe(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 =
+ snd_soc_component_get_drvdata(component);
+
+ rt1015->component = component;
+
+ return 0;
+}
+
+static void rt1015_remove(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1015->regmap, RT1015_RESET, 0);
+}
+
+#define RT1015_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1015_aif_dai_ops = {
+ .hw_params = rt1015_hw_params,
+ .set_fmt = rt1015_set_dai_fmt,
+ .set_tdm_slot = rt1015_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1015_dai[] = {
+ {
+ .name = "rt1015-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT1015_STEREO_RATES,
+ .formats = RT1015_FORMATS,
+ },
+ .ops = &rt1015_aif_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM
+static int rt1015_suspend(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1015->regmap, true);
+ regcache_mark_dirty(rt1015->regmap);
+
+ return 0;
+}
+
+static int rt1015_resume(struct snd_soc_component *component)
+{
+ struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1015->regmap, false);
+ regcache_sync(rt1015->regmap);
+
+ if (rt1015->cali_done)
+ rt1015_calibrate(rt1015);
+
+ return 0;
+}
+#else
+#define rt1015_suspend NULL
+#define rt1015_resume NULL
+#endif
+
+static const struct snd_soc_component_driver soc_component_dev_rt1015 = {
+ .probe = rt1015_probe,
+ .remove = rt1015_remove,
+ .suspend = rt1015_suspend,
+ .resume = rt1015_resume,
+ .controls = rt1015_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1015_snd_controls),
+ .dapm_widgets = rt1015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1015_dapm_widgets),
+ .dapm_routes = rt1015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1015_dapm_routes),
+ .set_sysclk = rt1015_set_component_sysclk,
+ .set_pll = rt1015_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1015_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT1015_S_BST_TIMING_INTER36,
+ .volatile_reg = rt1015_volatile_register,
+ .readable_reg = rt1015_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1015_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1015_reg),
+};
+
+static const struct i2c_device_id rt1015_i2c_id[] = {
+ { "rt1015" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1015_of_match[] = {
+ { .compatible = "realtek,rt1015", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1015_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1015_acpi_match[] = {
+ { "10EC1015" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match);
+#endif
+
+static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,power-up-delay-ms",
+ &rt1015->pdata.power_up_delay_ms);
+}
+
+static int rt1015_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt1015_priv *rt1015;
+ int ret;
+ unsigned int val;
+
+ rt1015 = devm_kzalloc(&i2c->dev, sizeof(*rt1015), GFP_KERNEL);
+ if (!rt1015)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1015);
+
+ rt1015->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt1015->pdata = *pdata;
+ else
+ rt1015_parse_dt(rt1015, &i2c->dev);
+
+ rt1015->regmap = devm_regmap_init_i2c(i2c, &rt1015_regmap);
+ if (IS_ERR(rt1015->regmap)) {
+ ret = PTR_ERR(rt1015->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read device register: %d\n", ret);
+ return ret;
+ } else if ((val != RT1015_DEVICE_ID_VAL) &&
+ (val != RT1015_DEVICE_ID_VAL2)) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1015\n", val);
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1015,
+ rt1015_dai, ARRAY_SIZE(rt1015_dai));
+}
+
+static void rt1015_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1015_priv *rt1015 = i2c_get_clientdata(client);
+
+ regmap_write(rt1015->regmap, RT1015_RESET, 0);
+}
+
+static struct i2c_driver rt1015_i2c_driver = {
+ .driver = {
+ .name = "rt1015",
+ .of_match_table = of_match_ptr(rt1015_of_match),
+ .acpi_match_table = ACPI_PTR(rt1015_acpi_match),
+ },
+ .probe = rt1015_i2c_probe,
+ .shutdown = rt1015_i2c_shutdown,
+ .id_table = rt1015_i2c_id,
+};
+module_i2c_driver(rt1015_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1015 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h
new file mode 100644
index 000000000000..c9f636af7fd1
--- /dev/null
+++ b/sound/soc/codecs/rt1015.h
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1015.h -- RT1015 ALSA SoC audio amplifier driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+
+#ifndef __RT1015_H__
+#define __RT1015_H__
+#include <sound/rt1015.h>
+
+#define RT1015_DEVICE_ID_VAL 0x1011
+#define RT1015_DEVICE_ID_VAL2 0x1015
+
+#define RT1015_RESET 0x0000
+#define RT1015_CLK2 0x0004
+#define RT1015_CLK3 0x0006
+#define RT1015_PLL1 0x000a
+#define RT1015_PLL2 0x000c
+#define RT1015_DUM_RW1 0x000e
+#define RT1015_DUM_RW2 0x0010
+#define RT1015_DUM_RW3 0x0012
+#define RT1015_DUM_RW4 0x0014
+#define RT1015_DUM_RW5 0x0016
+#define RT1015_DUM_RW6 0x0018
+#define RT1015_CLK_DET 0x0020
+#define RT1015_SIL_DET 0x0022
+#define RT1015_CUSTOMER_ID 0x0076
+#define RT1015_PCODE_FWVER 0x0078
+#define RT1015_VER_ID 0x007a
+#define RT1015_VENDOR_ID 0x007c
+#define RT1015_DEVICE_ID 0x007d
+#define RT1015_PAD_DRV1 0x00f0
+#define RT1015_PAD_DRV2 0x00f2
+#define RT1015_GAT_BOOST 0x00f3
+#define RT1015_PRO_ALT 0x00f4
+#define RT1015_OSCK_STA 0x00f6
+#define RT1015_MAN_I2C 0x0100
+#define RT1015_DAC1 0x0102
+#define RT1015_DAC2 0x0104
+#define RT1015_DAC3 0x0106
+#define RT1015_ADC1 0x010c
+#define RT1015_ADC2 0x010e
+#define RT1015_TDM_MASTER 0x0111
+#define RT1015_TDM_TCON 0x0112
+#define RT1015_TDM1_1 0x0114
+#define RT1015_TDM1_2 0x0116
+#define RT1015_TDM1_3 0x0118
+#define RT1015_TDM1_4 0x011a
+#define RT1015_TDM1_5 0x011c
+#define RT1015_MIXER1 0x0300
+#define RT1015_MIXER2 0x0302
+#define RT1015_ANA_PROTECT1 0x0311
+#define RT1015_ANA_CTRL_SEQ1 0x0313
+#define RT1015_ANA_CTRL_SEQ2 0x0314
+#define RT1015_VBAT_DET_DEB 0x031a
+#define RT1015_VBAT_VOLT_DET1 0x031c
+#define RT1015_VBAT_VOLT_DET2 0x031d
+#define RT1015_VBAT_TEST_OUT1 0x031e
+#define RT1015_VBAT_TEST_OUT2 0x031f
+#define RT1015_VBAT_PROT_ATT 0x0320
+#define RT1015_VBAT_DET_CODE 0x0321
+#define RT1015_PWR1 0x0322
+#define RT1015_PWR4 0x0328
+#define RT1015_PWR5 0x0329
+#define RT1015_PWR6 0x032a
+#define RT1015_PWR7 0x032b
+#define RT1015_PWR8 0x032c
+#define RT1015_PWR9 0x032d
+#define RT1015_CLASSD_SEQ 0x032e
+#define RT1015_SMART_BST_CTRL1 0x0330
+#define RT1015_SMART_BST_CTRL2 0x0332
+#define RT1015_ANA_CTRL1 0x0334
+#define RT1015_ANA_CTRL2 0x0336
+#define RT1015_PWR_STATE_CTRL 0x0338
+#define RT1015_MONO_DYNA_CTRL 0x04fa
+#define RT1015_MONO_DYNA_CTRL1 0x04fc
+#define RT1015_MONO_DYNA_CTRL2 0x04fe
+#define RT1015_MONO_DYNA_CTRL3 0x0500
+#define RT1015_MONO_DYNA_CTRL4 0x0502
+#define RT1015_MONO_DYNA_CTRL5 0x0504
+#define RT1015_SPK_VOL 0x0506
+#define RT1015_SHORT_DETTOP1 0x0508
+#define RT1015_SHORT_DETTOP2 0x050a
+#define RT1015_SPK_DC_DETECT1 0x0519
+#define RT1015_SPK_DC_DETECT2 0x051a
+#define RT1015_SPK_DC_DETECT3 0x051b
+#define RT1015_SPK_DC_DETECT4 0x051d
+#define RT1015_SPK_DC_DETECT5 0x051f
+#define RT1015_BAT_RPO_STEP1 0x0536
+#define RT1015_BAT_RPO_STEP2 0x0538
+#define RT1015_BAT_RPO_STEP3 0x053a
+#define RT1015_BAT_RPO_STEP4 0x053c
+#define RT1015_BAT_RPO_STEP5 0x053d
+#define RT1015_BAT_RPO_STEP6 0x053e
+#define RT1015_BAT_RPO_STEP7 0x053f
+#define RT1015_BAT_RPO_STEP8 0x0540
+#define RT1015_BAT_RPO_STEP9 0x0541
+#define RT1015_BAT_RPO_STEP10 0x0542
+#define RT1015_BAT_RPO_STEP11 0x0543
+#define RT1015_BAT_RPO_STEP12 0x0544
+#define RT1015_SPREAD_SPEC1 0x0568
+#define RT1015_SPREAD_SPEC2 0x056a
+#define RT1015_PAD_STATUS 0x1000
+#define RT1015_PADS_PULLING_CTRL1 0x1002
+#define RT1015_PADS_DRIVING 0x1006
+#define RT1015_SYS_RST1 0x1007
+#define RT1015_SYS_RST2 0x1009
+#define RT1015_SYS_GATING1 0x100a
+#define RT1015_TEST_MODE1 0x100c
+#define RT1015_TEST_MODE2 0x100d
+#define RT1015_TIMING_CTRL1 0x100e
+#define RT1015_PLL_INT 0x1010
+#define RT1015_TEST_OUT1 0x1020
+#define RT1015_DC_CALIB_CLSD1 0x1200
+#define RT1015_DC_CALIB_CLSD2 0x1202
+#define RT1015_DC_CALIB_CLSD3 0x1204
+#define RT1015_DC_CALIB_CLSD4 0x1206
+#define RT1015_DC_CALIB_CLSD5 0x1208
+#define RT1015_DC_CALIB_CLSD6 0x120a
+#define RT1015_DC_CALIB_CLSD7 0x120c
+#define RT1015_DC_CALIB_CLSD8 0x120e
+#define RT1015_DC_CALIB_CLSD9 0x1210
+#define RT1015_DC_CALIB_CLSD10 0x1212
+#define RT1015_CLSD_INTERNAL1 0x1300
+#define RT1015_CLSD_INTERNAL2 0x1302
+#define RT1015_CLSD_INTERNAL3 0x1304
+#define RT1015_CLSD_INTERNAL4 0x1305
+#define RT1015_CLSD_INTERNAL5 0x1306
+#define RT1015_CLSD_INTERNAL6 0x1308
+#define RT1015_CLSD_INTERNAL7 0x130a
+#define RT1015_CLSD_INTERNAL8 0x130c
+#define RT1015_CLSD_INTERNAL9 0x130e
+#define RT1015_CLSD_OCP_CTRL 0x130f
+#define RT1015_VREF_LV 0x1310
+#define RT1015_MBIAS1 0x1312
+#define RT1015_MBIAS2 0x1314
+#define RT1015_MBIAS3 0x1316
+#define RT1015_MBIAS4 0x1318
+#define RT1015_VREF_LV1 0x131a
+#define RT1015_S_BST_TIMING_INTER1 0x1322
+#define RT1015_S_BST_TIMING_INTER2 0x1323
+#define RT1015_S_BST_TIMING_INTER3 0x1324
+#define RT1015_S_BST_TIMING_INTER4 0x1325
+#define RT1015_S_BST_TIMING_INTER5 0x1326
+#define RT1015_S_BST_TIMING_INTER6 0x1327
+#define RT1015_S_BST_TIMING_INTER7 0x1328
+#define RT1015_S_BST_TIMING_INTER8 0x1329
+#define RT1015_S_BST_TIMING_INTER9 0x132a
+#define RT1015_S_BST_TIMING_INTER10 0x132b
+#define RT1015_S_BST_TIMING_INTER11 0x1330
+#define RT1015_S_BST_TIMING_INTER12 0x1331
+#define RT1015_S_BST_TIMING_INTER13 0x1332
+#define RT1015_S_BST_TIMING_INTER14 0x1333
+#define RT1015_S_BST_TIMING_INTER15 0x1334
+#define RT1015_S_BST_TIMING_INTER16 0x1335
+#define RT1015_S_BST_TIMING_INTER17 0x1336
+#define RT1015_S_BST_TIMING_INTER18 0x1337
+#define RT1015_S_BST_TIMING_INTER19 0x1338
+#define RT1015_S_BST_TIMING_INTER20 0x1339
+#define RT1015_S_BST_TIMING_INTER21 0x133a
+#define RT1015_S_BST_TIMING_INTER22 0x133b
+#define RT1015_S_BST_TIMING_INTER23 0x133c
+#define RT1015_S_BST_TIMING_INTER24 0x133d
+#define RT1015_S_BST_TIMING_INTER25 0x133e
+#define RT1015_S_BST_TIMING_INTER26 0x133f
+#define RT1015_S_BST_TIMING_INTER27 0x1340
+#define RT1015_S_BST_TIMING_INTER28 0x1341
+#define RT1015_S_BST_TIMING_INTER29 0x1342
+#define RT1015_S_BST_TIMING_INTER30 0x1343
+#define RT1015_S_BST_TIMING_INTER31 0x1344
+#define RT1015_S_BST_TIMING_INTER32 0x1345
+#define RT1015_S_BST_TIMING_INTER33 0x1346
+#define RT1015_S_BST_TIMING_INTER34 0x1347
+#define RT1015_S_BST_TIMING_INTER35 0x1348
+#define RT1015_S_BST_TIMING_INTER36 0x1349
+
+/* 0x0004 */
+#define RT1015_CLK_SYS_PRE_SEL_MASK (0x3 << 14)
+#define RT1015_CLK_SYS_PRE_SEL_SFT 14
+#define RT1015_CLK_SYS_PRE_SEL_MCLK (0x0 << 14)
+#define RT1015_CLK_SYS_PRE_SEL_PLL (0x2 << 14)
+#define RT1015_PLL_SEL_MASK (0x1 << 13)
+#define RT1015_PLL_SEL_SFT 13
+#define RT1015_PLL_SEL_PLL_SRC2 (0x0 << 13)
+#define RT1015_PLL_SEL_BCLK (0x1 << 13)
+#define RT1015_FS_PD_MASK (0x7 << 4)
+#define RT1015_FS_PD_SFT 4
+
+/* 0x000a */
+#define RT1015_PLL_M_MAX 0xf
+#define RT1015_PLL_M_MASK (RT1015_PLL_M_MAX << 12)
+#define RT1015_PLL_M_SFT 12
+#define RT1015_PLL_M_BP (0x1 << 11)
+#define RT1015_PLL_M_BP_SFT 11
+#define RT1015_PLL_N_MAX 0x1ff
+#define RT1015_PLL_N_MASK (RT1015_PLL_N_MAX << 0)
+#define RT1015_PLL_N_SFT 0
+
+/* 0x000c */
+#define RT1015_PLL_BPK_MASK (0x1 << 5)
+#define RT1015_PLL_BPK (0x0 << 5)
+#define RT1015_PLL_K_MAX 0x1f
+#define RT1015_PLL_K_MASK (RT1015_PLL_K_MAX)
+#define RT1015_PLL_K_SFT 0
+
+/* 0x0020 */
+#define RT1015_EN_BCLK_DET_MASK (0x1 << 15)
+#define RT1015_EN_BCLK_DET (0x1 << 15)
+#define RT1015_DIS_BCLK_DET (0x0 << 15)
+
+/* 0x007a */
+#define RT1015_ID_MASK 0xff
+#define RT1015_ID_VERA 0x0
+#define RT1015_ID_VERB 0x1
+
+/* 0x00f2 */
+#define RT1015_MONO_LR_SEL_MASK (0x3 << 4)
+#define RT1015_MONO_L_CHANNEL (0x0 << 4)
+#define RT1015_MONO_R_CHANNEL (0x1 << 4)
+#define RT1015_MONO_LR_MIX_CHANNEL (0x2 << 4)
+
+/* 0x0102 */
+#define RT1015_DAC_VOL_MASK (0x7f << 9)
+#define RT1015_DAC_VOL_SFT 9
+
+/* 0x0104 */
+#define RT1015_DAC_CLK (0x1 << 13)
+#define RT1015_DAC_CLK_BIT 13
+
+/* 0x0106 */
+#define RT1015_DAC_MUTE_MASK (0x1 << 15)
+#define RT1015_DA_MUTE_SFT 15
+#define RT1015_DVOL_MUTE_FLAG_SFT 12
+
+/* 0x0111 */
+#define RT1015_TCON_TDM_MS_MASK (0x1 << 14)
+#define RT1015_TCON_TDM_MS_SFT 14
+#define RT1015_TCON_TDM_MS_S (0x0 << 14)
+#define RT1015_TCON_TDM_MS_M (0x1 << 14)
+#define RT1015_I2S_DL_MASK (0x7 << 8)
+#define RT1015_I2S_DL_SFT 8
+#define RT1015_I2S_DL_16 (0x0 << 8)
+#define RT1015_I2S_DL_20 (0x1 << 8)
+#define RT1015_I2S_DL_24 (0x2 << 8)
+#define RT1015_I2S_DL_8 (0x3 << 8)
+#define RT1015_I2S_M_DF_MASK (0x7 << 0)
+#define RT1015_I2S_M_DF_SFT 0
+#define RT1015_I2S_M_DF_I2S (0x0)
+#define RT1015_I2S_M_DF_LEFT (0x1)
+#define RT1015_I2S_M_DF_PCM_A (0x2)
+#define RT1015_I2S_M_DF_PCM_B (0x3)
+#define RT1015_I2S_M_DF_PCM_A_N (0x6)
+#define RT1015_I2S_M_DF_PCM_B_N (0x7)
+
+/* TDM_tcon Setting (0x0112) */
+#define RT1015_I2S_TCON_DF_MASK (0x7 << 13)
+#define RT1015_I2S_TCON_DF_SFT 13
+#define RT1015_I2S_TCON_DF_I2S (0x0 << 13)
+#define RT1015_I2S_TCON_DF_LEFT (0x1 << 13)
+#define RT1015_I2S_TCON_DF_PCM_A (0x2 << 13)
+#define RT1015_I2S_TCON_DF_PCM_B (0x3 << 13)
+#define RT1015_I2S_TCON_DF_PCM_A_N (0x6 << 13)
+#define RT1015_I2S_TCON_DF_PCM_B_N (0x7 << 13)
+#define RT1015_TCON_BCLK_SEL_MASK (0x3 << 10)
+#define RT1015_TCON_BCLK_SEL_SFT 10
+#define RT1015_TCON_BCLK_SEL_32FS (0x0 << 10)
+#define RT1015_TCON_BCLK_SEL_64FS (0x1 << 10)
+#define RT1015_TCON_BCLK_SEL_128FS (0x2 << 10)
+#define RT1015_TCON_BCLK_SEL_256FS (0x3 << 10)
+#define RT1015_TCON_CH_LEN_MASK (0x3 << 5)
+#define RT1015_TCON_CH_LEN_SFT 5
+#define RT1015_TCON_CH_LEN_16B (0x0 << 5)
+#define RT1015_TCON_CH_LEN_20B (0x1 << 5)
+#define RT1015_TCON_CH_LEN_24B (0x2 << 5)
+#define RT1015_TCON_CH_LEN_32B (0x3 << 5)
+#define RT1015_TCON_BCLK_MST_MASK (0x1 << 4)
+#define RT1015_TCON_BCLK_MST_SFT 4
+#define RT1015_TCON_BCLK_MST_INV (0x1 << 4)
+
+/* TDM1 Setting-1 (0x0114) */
+#define RT1015_TDM_INV_BCLK_MASK (0x1 << 15)
+#define RT1015_TDM_INV_BCLK_SFT 15
+#define RT1015_TDM_INV_BCLK (0x1 << 15)
+#define RT1015_I2S_CH_TX_MASK (0x3 << 10)
+#define RT1015_I2S_CH_TX_SFT 10
+#define RT1015_I2S_TX_2CH (0x0 << 10)
+#define RT1015_I2S_TX_4CH (0x1 << 10)
+#define RT1015_I2S_TX_6CH (0x2 << 10)
+#define RT1015_I2S_TX_8CH (0x3 << 10)
+#define RT1015_I2S_CH_RX_MASK (0x3 << 8)
+#define RT1015_I2S_CH_RX_SFT 8
+#define RT1015_I2S_RX_2CH (0x0 << 8)
+#define RT1015_I2S_RX_4CH (0x1 << 8)
+#define RT1015_I2S_RX_6CH (0x2 << 8)
+#define RT1015_I2S_RX_8CH (0x3 << 8)
+#define RT1015_I2S_LR_CH_SEL_MASK (0x1 << 7)
+#define RT1015_I2S_LR_CH_SEL_SFT 7
+#define RT1015_I2S_LEFT_CH_SEL (0x0 << 7)
+#define RT1015_I2S_RIGHT_CH_SEL (0x1 << 7)
+#define RT1015_I2S_CH_TX_LEN_MASK (0x7 << 4)
+#define RT1015_I2S_CH_TX_LEN_SFT 4
+#define RT1015_I2S_CH_TX_LEN_16B (0x0 << 4)
+#define RT1015_I2S_CH_TX_LEN_20B (0x1 << 4)
+#define RT1015_I2S_CH_TX_LEN_24B (0x2 << 4)
+#define RT1015_I2S_CH_TX_LEN_32B (0x3 << 4)
+#define RT1015_I2S_CH_TX_LEN_8B (0x4 << 4)
+#define RT1015_I2S_CH_RX_LEN_MASK (0x7 << 0)
+#define RT1015_I2S_CH_RX_LEN_SFT 0
+#define RT1015_I2S_CH_RX_LEN_16B (0x0 << 0)
+#define RT1015_I2S_CH_RX_LEN_20B (0x1 << 0)
+#define RT1015_I2S_CH_RX_LEN_24B (0x2 << 0)
+#define RT1015_I2S_CH_RX_LEN_32B (0x3 << 0)
+#define RT1015_I2S_CH_RX_LEN_8B (0x4 << 0)
+
+/* TDM1 Setting-4 (0x011a) */
+#define RT1015_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12)
+#define RT1015_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8)
+#define RT1015_TDM_I2S_TX_L_DAC1_1_SFT 12
+#define RT1015_TDM_I2S_TX_R_DAC1_1_SFT 8
+
+/* 0x0330 */
+#define RT1015_ABST_AUTO_EN_MASK (0x1 << 13)
+#define RT1015_ABST_AUTO_MODE (0x1 << 13)
+#define RT1015_ABST_REG_MODE (0x0 << 13)
+#define RT1015_ABST_FIX_TGT_MASK (0x1 << 12)
+#define RT1015_ABST_FIX_TGT_EN (0x1 << 12)
+#define RT1015_ABST_FIX_TGT_DIS (0x0 << 12)
+#define RT1015_BYPASS_SWR_REG_MASK (0x1 << 7)
+#define RT1015_BYPASS_SWRREG_BYPASS (0x1 << 7)
+#define RT1015_BYPASS_SWRREG_PASS (0x0 << 7)
+
+/* 0x0322 */
+#define RT1015_PWR_LDO2 (0x1 << 15)
+#define RT1015_PWR_LDO2_BIT 15
+#define RT1015_PWR_DAC (0x1 << 14)
+#define RT1015_PWR_DAC_BIT 14
+#define RT1015_PWR_INTCLK (0x1 << 13)
+#define RT1015_PWR_INTCLK_BIT 13
+#define RT1015_PWR_ISENSE (0x1 << 12)
+#define RT1015_PWR_ISENSE_BIT 12
+#define RT1015_PWR_VSENSE (0x1 << 10)
+#define RT1015_PWR_VSENSE_BIT 10
+#define RT1015_PWR_PLL (0x1 << 9)
+#define RT1015_PWR_PLL_BIT 9
+#define RT1015_PWR_BG_1_2 (0x1 << 8)
+#define RT1015_PWR_BG_1_2_BIT 8
+#define RT1015_PWR_MBIAS_BG (0x1 << 7)
+#define RT1015_PWR_MBIAS_BG_BIT 7
+#define RT1015_PWR_VBAT (0x1 << 6)
+#define RT1015_PWR_VBAT_BIT 6
+#define RT1015_PWR_MBIAS (0x1 << 4)
+#define RT1015_PWR_MBIAS_BIT 4
+#define RT1015_PWR_ADCV (0x1 << 3)
+#define RT1015_PWR_ADCV_BIT 3
+#define RT1015_PWR_MIXERV (0x1 << 2)
+#define RT1015_PWR_MIXERV_BIT 2
+#define RT1015_PWR_SUMV (0x1 << 1)
+#define RT1015_PWR_SUMV_BIT 1
+#define RT1015_PWR_VREFLV (0x1 << 0)
+#define RT1015_PWR_VREFLV_BIT 0
+
+/* 0x0324 */
+#define RT1015_PWR_BASIC (0x1 << 15)
+#define RT1015_PWR_BASIC_BIT 15
+#define RT1015_PWR_SD (0x1 << 14)
+#define RT1015_PWR_SD_BIT 14
+#define RT1015_PWR_IBIAS (0x1 << 13)
+#define RT1015_PWR_IBIAS_BIT 13
+#define RT1015_PWR_VCM (0x1 << 11)
+#define RT1015_PWR_VCM_BIT 11
+
+/* 0x0328 */
+#define RT1015_PWR_SWR (0x1 << 12)
+#define RT1015_PWR_SWR_BIT 12
+
+/* 0x0519 */
+#define RT1015_EN_CLA_D_DC_DET_MASK (0x1 << 12)
+#define RT1015_EN_CLA_D_DC_DET (0x1 << 12)
+#define RT1015_DIS_CLA_D_DC_DET (0x0 << 12)
+
+/* 0x1300 */
+#define RT1015_PWR_CLSD (0x1 << 12)
+#define RT1015_PWR_CLSD_BIT 12
+
+/* 0x007a */
+#define RT1015_ID_MASK 0xff
+#define RT1015_ID_VERA 0x0
+#define RT1015_ID_VERB 0x1
+
+/* System Clock Source */
+enum {
+ RT1015_SCLK_S_MCLK,
+ RT1015_SCLK_S_PLL,
+};
+
+/* PLL1 Source */
+enum {
+ RT1015_PLL_S_MCLK,
+ RT1015_PLL_S_BCLK,
+};
+
+enum {
+ RT1015_AIF1,
+ RT1015_AIFS,
+};
+
+enum {
+ RT1015_VERA,
+ RT1015_VERB,
+};
+
+enum {
+ BYPASS,
+ ADAPTIVE,
+ FIXED_ADAPTIVE,
+};
+
+enum {
+ RT1015_Enable_Boost = 0,
+ RT1015_Bypass_Boost,
+};
+
+enum {
+ RT1015_HW_28 = 0,
+ RT1015_HW_29,
+};
+
+struct rt1015_priv {
+ struct snd_soc_component *component;
+ struct rt1015_platform_data pdata;
+ struct regmap *regmap;
+ int sysclk;
+ int sysclk_src;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ int boost_mode;
+ int bypass_boost;
+ int dac_is_used;
+ int cali_done;
+};
+
+#endif /* __RT1015_H__ */
diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c
new file mode 100644
index 000000000000..44e7fe3c32da
--- /dev/null
+++ b/sound/soc/codecs/rt1015p.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1015p.c -- RT1015P ALSA SoC audio amplifier driver
+//
+// Copyright 2020 The Linux Foundation. All rights reserved.
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct rt1015p_priv {
+ struct gpio_desc *sdb;
+ bool calib_done;
+};
+
+static int rt1015p_sdb_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1015p_priv *rt1015p =
+ snd_soc_component_get_drvdata(component);
+
+ if (!rt1015p->sdb)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(rt1015p->sdb, 1);
+ dev_dbg(component->dev, "set sdb to 1");
+
+ if (!rt1015p->calib_done) {
+ msleep(300);
+ rt1015p->calib_done = true;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(rt1015p->sdb, 0);
+ dev_dbg(component->dev, "set sdb to 0");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("Speaker"),
+ SND_SOC_DAPM_OUT_DRV_E("SDB", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1015p_sdb_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = {
+ {"SDB", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "SDB"},
+};
+
+#ifdef CONFIG_PM
+static int rt1015p_suspend(struct snd_soc_component *component)
+{
+ struct rt1015p_priv *rt1015p = snd_soc_component_get_drvdata(component);
+
+ rt1015p->calib_done = false;
+ return 0;
+}
+#else
+#define rt1015p_suspend NULL
+#endif
+
+static const struct snd_soc_component_driver rt1015p_component_driver = {
+ .suspend = rt1015p_suspend,
+ .dapm_widgets = rt1015p_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets),
+ .dapm_routes = rt1015p_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1015p_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver rt1015p_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+};
+
+static int rt1015p_platform_probe(struct platform_device *pdev)
+{
+ struct rt1015p_priv *rt1015p;
+
+ rt1015p = devm_kzalloc(&pdev->dev, sizeof(*rt1015p), GFP_KERNEL);
+ if (!rt1015p)
+ return -ENOMEM;
+
+ rt1015p->sdb = devm_gpiod_get_optional(&pdev->dev,
+ "sdb", GPIOD_OUT_LOW);
+ if (IS_ERR(rt1015p->sdb))
+ return PTR_ERR(rt1015p->sdb);
+
+ dev_set_drvdata(&pdev->dev, rt1015p);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &rt1015p_component_driver,
+ &rt1015p_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt1015p_device_id[] = {
+ { .compatible = "realtek,rt1015p" },
+ { .compatible = "realtek,rt1019p" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt1015p_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1015p_acpi_match[] = {
+ { "RTL1015", 0},
+ { "RTL1019", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt1015p_acpi_match);
+#endif
+
+static struct platform_driver rt1015p_platform_driver = {
+ .driver = {
+ .name = "rt1015p",
+ .of_match_table = of_match_ptr(rt1015p_device_id),
+ .acpi_match_table = ACPI_PTR(rt1015p_acpi_match),
+ },
+ .probe = rt1015p_platform_probe,
+};
+module_platform_driver(rt1015p_platform_driver);
+
+MODULE_DESCRIPTION("ASoC RT1015P driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
new file mode 100644
index 000000000000..9f86f071fca8
--- /dev/null
+++ b/sound/soc/codecs/rt1016.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1016.c -- RT1016 ALSA SoC audio amplifier driver
+//
+// Copyright 2020 Realtek Semiconductor Corp.
+// Author: Oder Chiou <oder_chiou@realtek.com>
+//
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1016.h"
+
+static const struct reg_sequence rt1016_patch[] = {
+ {RT1016_VOL_CTRL_3, 0x8900},
+ {RT1016_ANA_CTRL_1, 0xa002},
+ {RT1016_ANA_CTRL_2, 0x0002},
+ {RT1016_CLOCK_4, 0x6700},
+ {RT1016_CLASSD_3, 0xdc55},
+ {RT1016_CLASSD_4, 0x376a},
+ {RT1016_CLASSD_5, 0x009f},
+};
+
+static const struct reg_default rt1016_reg[] = {
+ {0x00, 0x0000},
+ {0x01, 0x5400},
+ {0x02, 0x5506},
+ {0x03, 0xf800},
+ {0x04, 0x0000},
+ {0x05, 0xbfbf},
+ {0x06, 0x8900},
+ {0x07, 0xa002},
+ {0x08, 0x0000},
+ {0x09, 0x0000},
+ {0x0a, 0x0000},
+ {0x0c, 0x0000},
+ {0x0d, 0x0000},
+ {0x0e, 0x10ec},
+ {0x0f, 0x6595},
+ {0x11, 0x0002},
+ {0x1c, 0x0000},
+ {0x1d, 0x0000},
+ {0x1e, 0x0000},
+ {0x1f, 0xf000},
+ {0x20, 0x0000},
+ {0x21, 0x6000},
+ {0x22, 0x0000},
+ {0x23, 0x6700},
+ {0x24, 0x0000},
+ {0x25, 0x0000},
+ {0x26, 0x0000},
+ {0x40, 0x0018},
+ {0x60, 0x00a5},
+ {0x80, 0x0010},
+ {0x81, 0x0009},
+ {0x82, 0x0000},
+ {0x83, 0x0000},
+ {0xa0, 0x0700},
+ {0xc0, 0x0080},
+ {0xc1, 0x02a0},
+ {0xc2, 0x1400},
+ {0xc3, 0x0a4a},
+ {0xc4, 0x552a},
+ {0xc5, 0x087e},
+ {0xc6, 0x0020},
+ {0xc7, 0xa833},
+ {0xc8, 0x0433},
+ {0xc9, 0x8040},
+ {0xca, 0xdc55},
+ {0xcb, 0x376a},
+ {0xcc, 0x009f},
+ {0xcf, 0x0020},
+};
+
+static bool rt1016_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1016_ANA_FLAG:
+ case RT1016_VERSION2_ID:
+ case RT1016_VERSION1_ID:
+ case RT1016_VENDER_ID:
+ case RT1016_DEVICE_ID:
+ case RT1016_TEST_SIGNAL:
+ case RT1016_SC_CTRL_1:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1016_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1016_RESET:
+ case RT1016_PADS_CTRL_1:
+ case RT1016_PADS_CTRL_2:
+ case RT1016_I2C_CTRL:
+ case RT1016_VOL_CTRL_1:
+ case RT1016_VOL_CTRL_2:
+ case RT1016_VOL_CTRL_3:
+ case RT1016_ANA_CTRL_1:
+ case RT1016_MUX_SEL:
+ case RT1016_RX_I2S_CTRL:
+ case RT1016_ANA_FLAG:
+ case RT1016_VERSION2_ID:
+ case RT1016_VERSION1_ID:
+ case RT1016_VENDER_ID:
+ case RT1016_DEVICE_ID:
+ case RT1016_ANA_CTRL_2:
+ case RT1016_TEST_SIGNAL:
+ case RT1016_TEST_CTRL_1:
+ case RT1016_TEST_CTRL_2:
+ case RT1016_TEST_CTRL_3:
+ case RT1016_CLOCK_1:
+ case RT1016_CLOCK_2:
+ case RT1016_CLOCK_3:
+ case RT1016_CLOCK_4:
+ case RT1016_CLOCK_5:
+ case RT1016_CLOCK_6:
+ case RT1016_CLOCK_7:
+ case RT1016_I2S_CTRL:
+ case RT1016_DAC_CTRL_1:
+ case RT1016_SC_CTRL_1:
+ case RT1016_SC_CTRL_2:
+ case RT1016_SC_CTRL_3:
+ case RT1016_SC_CTRL_4:
+ case RT1016_SIL_DET:
+ case RT1016_SYS_CLK:
+ case RT1016_BIAS_CUR:
+ case RT1016_DAC_CTRL_2:
+ case RT1016_LDO_CTRL:
+ case RT1016_CLASSD_1:
+ case RT1016_PLL1:
+ case RT1016_PLL2:
+ case RT1016_PLL3:
+ case RT1016_CLASSD_2:
+ case RT1016_CLASSD_OUT:
+ case RT1016_CLASSD_3:
+ case RT1016_CLASSD_4:
+ case RT1016_CLASSD_5:
+ case RT1016_PWR_CTRL:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+
+static const struct snd_kcontrol_new rt1016_snd_controls[] = {
+ SOC_DOUBLE_TLV("DAC Playback Volume", RT1016_VOL_CTRL_2,
+ RT1016_L_VOL_SFT, RT1016_R_VOL_SFT, 191, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC Playback Switch", RT1016_VOL_CTRL_1,
+ RT1016_DA_MUTE_L_SFT, RT1016_DA_MUTE_R_SFT, 1, 1),
+};
+
+static int rt1016_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ if (rt1016->sysclk_src == RT1016_SCLK_S_PLL)
+ return 1;
+ else
+ return 0;
+}
+
+/* Interface data select */
+static const char * const rt1016_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1016_if_data_swap_enum,
+ RT1016_I2S_CTRL, RT1016_I2S_DATA_SWAP_SFT, rt1016_data_select);
+
+static const struct snd_kcontrol_new rt1016_if_data_swap_mux =
+ SOC_DAPM_ENUM("Data Swap Mux", rt1016_if_data_swap_enum);
+
+static const struct snd_soc_dapm_widget rt1016_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Data Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt1016_if_data_swap_mux),
+
+ SND_SOC_DAPM_SUPPLY("DAC Filter", RT1016_CLOCK_3,
+ RT1016_PWR_DAC_FILTER_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAMOD", RT1016_CLOCK_3, RT1016_PWR_DACMOD_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("FIFO", RT1016_CLOCK_3, RT1016_PWR_CLK_FIFO_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Pure DC", RT1016_CLOCK_3,
+ RT1016_PWR_CLK_PUREDC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK Silence Det", RT1016_CLOCK_3,
+ RT1016_PWR_SIL_DET_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RC 25M", RT1016_CLOCK_3, RT1016_PWR_RC_25M_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT1016_CLOCK_3, RT1016_PWR_PLL1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ANA CTRL", RT1016_CLOCK_3, RT1016_PWR_ANA_CTRL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK SYS", RT1016_CLOCK_3, RT1016_PWR_CLK_SYS_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LRCK Det", RT1016_CLOCK_4, RT1016_PWR_LRCK_DET_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BCLK Det", RT1016_CLOCK_4, RT1016_PWR_BCLK_DET_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1016_DAC_CTRL_2,
+ RT1016_CKGEN_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VCM SLOW", RT1016_CLASSD_1, RT1016_VCM_SLOW_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Silence Det", RT1016_SIL_DET,
+ RT1016_SIL_DET_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2", RT1016_PLL2, RT1016_PLL2_EN_BIT, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY_S("BG1 BG2", 1, RT1016_PWR_CTRL,
+ RT1016_PWR_BG_1_2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("MBIAS BG", 1, RT1016_PWR_CTRL,
+ RT1016_PWR_MBIAS_BG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLL", 1, RT1016_PWR_CTRL, RT1016_PWR_PLL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("BASIC", 1, RT1016_PWR_CTRL, RT1016_PWR_BASIC_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLASS D", 1, RT1016_PWR_CTRL,
+ RT1016_PWR_CLSD_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("25M", 1, RT1016_PWR_CTRL, RT1016_PWR_25M_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACL", 1, RT1016_PWR_CTRL, RT1016_PWR_DACL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DACR", 1, RT1016_PWR_CTRL, RT1016_PWR_DACR_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT1016_PWR_CTRL, RT1016_PWR_LDO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VREF", 1, RT1016_PWR_CTRL, RT1016_PWR_VREF_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("MBIAS", 1, RT1016_PWR_CTRL, RT1016_PWR_MBIAS_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1016_dapm_routes[] = {
+ { "Data Swap Mux", "L/R", "AIFRX" },
+ { "Data Swap Mux", "R/L", "AIFRX" },
+ { "Data Swap Mux", "L/L", "AIFRX" },
+ { "Data Swap Mux", "R/R", "AIFRX" },
+
+ { "DAC", NULL, "DAC Filter" },
+ { "DAC", NULL, "DAMOD" },
+ { "DAC", NULL, "FIFO" },
+ { "DAC", NULL, "Pure DC" },
+ { "DAC", NULL, "Silence Det" },
+ { "DAC", NULL, "ANA CTRL" },
+ { "DAC", NULL, "CLK SYS" },
+ { "DAC", NULL, "LRCK Det" },
+ { "DAC", NULL, "BCLK Det" },
+ { "DAC", NULL, "CKGEN DAC" },
+ { "DAC", NULL, "VCM SLOW" },
+
+ { "PLL", NULL, "PLL1" },
+ { "PLL", NULL, "PLL2" },
+ { "25M", NULL, "RC 25M" },
+ { "Silence Det", NULL, "CLK Silence Det" },
+
+ { "DAC", NULL, "Data Swap Mux" },
+ { "DAC", NULL, "BG1 BG2" },
+ { "DAC", NULL, "MBIAS BG" },
+ { "DAC", NULL, "PLL", rt1016_is_sys_clk_from_pll},
+ { "DAC", NULL, "BASIC" },
+ { "DAC", NULL, "CLASS D" },
+ { "DAC", NULL, "25M" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "DACR" },
+ { "DAC", NULL, "LDO2" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MBIAS" },
+
+ { "SPO", NULL, "DAC" },
+};
+
+static int rt1016_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0;
+
+ rt1016->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt1016->sysclk, rt1016->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock rate\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1016->bclk = rt1016->lrck * (32 << bclk_ms);
+
+ if (bclk_ms && rt1016->master)
+ snd_soc_component_update_bits(component, RT1016_I2S_CTRL,
+ RT1016_I2S_BCLK_MS_MASK, RT1016_I2S_BCLK_MS_64);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1016->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len = RT1016_I2S_DL_16;
+ break;
+ case 20:
+ val_len = RT1016_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT1016_I2S_DL_24;
+ break;
+ case 32:
+ val_len = RT1016_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1016_I2S_CTRL,
+ RT1016_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT1016_CLOCK_2,
+ RT1016_FS_PD_MASK | RT1016_OSR_PD_MASK,
+ ((pre_div + 3) << RT1016_FS_PD_SFT) |
+ (pre_div << RT1016_OSR_PD_SFT));
+
+ return 0;
+}
+
+static int rt1016_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ reg_val |= RT1016_I2S_MS_M;
+ rt1016->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT1016_I2S_MS_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT1016_I2S_BCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1016_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1016_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1016_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1016_I2S_CTRL,
+ RT1016_I2S_MS_MASK | RT1016_I2S_BCLK_POL_MASK |
+ RT1016_I2S_DF_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1016_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1016->sysclk && clk_id == rt1016->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1016_SCLK_S_MCLK:
+ reg_val |= RT1016_CLK_SYS_SEL_MCLK;
+ break;
+
+ case RT1016_SCLK_S_PLL:
+ reg_val |= RT1016_CLK_SYS_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1016->sysclk = freq;
+ rt1016->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ snd_soc_component_update_bits(component, RT1016_CLOCK_1,
+ RT1016_CLK_SYS_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1016_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1016->pll_in = 0;
+ rt1016->pll_out = 0;
+
+ return 0;
+ }
+
+ if (source == rt1016->pll_src && freq_in == rt1016->pll_in &&
+ freq_out == rt1016->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1016_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT1016_CLOCK_1,
+ RT1016_PLL_SEL_MASK, RT1016_PLL_SEL_MCLK);
+ break;
+
+ case RT1016_PLL_S_BCLK:
+ snd_soc_component_update_bits(component, RT1016_CLOCK_1,
+ RT1016_PLL_SEL_MASK, RT1016_PLL_SEL_BCLK);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out * 4, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "mbypass=%d m=%d n=%d kbypass=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_bp,
+ (pll_code.k_bp ? 0 : pll_code.k_code));
+
+ snd_soc_component_write(component, RT1016_PLL1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1016_PLL_M_SFT) |
+ (pll_code.m_bp << RT1016_PLL_M_BP_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1016_PLL2,
+ (pll_code.k_bp << RT1016_PLL_K_BP_SFT) |
+ (pll_code.k_bp ? 0 : pll_code.k_code));
+
+ rt1016->pll_in = freq_in;
+ rt1016->pll_out = freq_out;
+ rt1016->pll_src = source;
+
+ return 0;
+}
+
+static int rt1016_probe(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 =
+ snd_soc_component_get_drvdata(component);
+
+ rt1016->component = component;
+
+ return 0;
+}
+
+static void rt1016_remove(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1016->regmap, RT1016_RESET, 0);
+}
+
+#define RT1016_STEREO_RATES SNDRV_PCM_RATE_8000_48000
+#define RT1016_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1016_aif_dai_ops = {
+ .hw_params = rt1016_hw_params,
+ .set_fmt = rt1016_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1016_dai[] = {
+ {
+ .name = "rt1016-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1016_STEREO_RATES,
+ .formats = RT1016_FORMATS,
+ },
+ .ops = &rt1016_aif_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM
+static int rt1016_suspend(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1016->regmap, true);
+ regcache_mark_dirty(rt1016->regmap);
+
+ return 0;
+}
+
+static int rt1016_resume(struct snd_soc_component *component)
+{
+ struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1016->regmap, false);
+ regcache_sync(rt1016->regmap);
+
+ return 0;
+}
+#else
+#define rt1016_suspend NULL
+#define rt1016_resume NULL
+#endif
+
+static const struct snd_soc_component_driver soc_component_dev_rt1016 = {
+ .probe = rt1016_probe,
+ .remove = rt1016_remove,
+ .suspend = rt1016_suspend,
+ .resume = rt1016_resume,
+ .controls = rt1016_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1016_snd_controls),
+ .dapm_widgets = rt1016_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1016_dapm_widgets),
+ .dapm_routes = rt1016_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1016_dapm_routes),
+ .set_sysclk = rt1016_set_component_sysclk,
+ .set_pll = rt1016_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1016_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = RT1016_PWR_CTRL,
+ .volatile_reg = rt1016_volatile_register,
+ .readable_reg = rt1016_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1016_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1016_reg),
+};
+
+static const struct i2c_device_id rt1016_i2c_id[] = {
+ { "rt1016" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1016_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1016_of_match[] = {
+ { .compatible = "realtek,rt1016", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1016_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1016_acpi_match[] = {
+ { "10EC1016" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match);
+#endif
+
+static int rt1016_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1016_priv *rt1016;
+ int ret;
+ unsigned int val;
+
+ rt1016 = devm_kzalloc(&i2c->dev, sizeof(struct rt1016_priv),
+ GFP_KERNEL);
+ if (rt1016 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1016);
+
+ rt1016->regmap = devm_regmap_init_i2c(i2c, &rt1016_regmap);
+ if (IS_ERR(rt1016->regmap)) {
+ ret = PTR_ERR(rt1016->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1016->regmap, RT1016_DEVICE_ID, &val);
+ if (val != RT1016_DEVICE_ID_VAL) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1016\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt1016->regmap, RT1016_RESET, 0);
+
+ ret = regmap_register_patch(rt1016->regmap, rt1016_patch,
+ ARRAY_SIZE(rt1016_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1016,
+ rt1016_dai, ARRAY_SIZE(rt1016_dai));
+}
+
+static void rt1016_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1016_priv *rt1016 = i2c_get_clientdata(client);
+
+ regmap_write(rt1016->regmap, RT1016_RESET, 0);
+}
+
+static struct i2c_driver rt1016_i2c_driver = {
+ .driver = {
+ .name = "rt1016",
+ .of_match_table = of_match_ptr(rt1016_of_match),
+ .acpi_match_table = ACPI_PTR(rt1016_acpi_match),
+ },
+ .probe = rt1016_i2c_probe,
+ .shutdown = rt1016_i2c_shutdown,
+ .id_table = rt1016_i2c_id,
+};
+module_i2c_driver(rt1016_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1016 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1016.h b/sound/soc/codecs/rt1016.h
new file mode 100644
index 000000000000..041d6a5a6f46
--- /dev/null
+++ b/sound/soc/codecs/rt1016.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1016.h -- RT1016 ALSA SoC audio amplifier driver
+ *
+ * Copyright 2020 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT1016_H__
+#define __RT1016_H__
+
+#define RT1016_DEVICE_ID_VAL 0x6595
+
+#define RT1016_RESET 0x00
+#define RT1016_PADS_CTRL_1 0x01
+#define RT1016_PADS_CTRL_2 0x02
+#define RT1016_I2C_CTRL 0x03
+#define RT1016_VOL_CTRL_1 0x04
+#define RT1016_VOL_CTRL_2 0x05
+#define RT1016_VOL_CTRL_3 0x06
+#define RT1016_ANA_CTRL_1 0x07
+#define RT1016_MUX_SEL 0x08
+#define RT1016_RX_I2S_CTRL 0x09
+#define RT1016_ANA_FLAG 0x0a
+#define RT1016_VERSION2_ID 0x0c
+#define RT1016_VERSION1_ID 0x0d
+#define RT1016_VENDER_ID 0x0e
+#define RT1016_DEVICE_ID 0x0f
+#define RT1016_ANA_CTRL_2 0x11
+#define RT1016_TEST_SIGNAL 0x1c
+#define RT1016_TEST_CTRL_1 0x1d
+#define RT1016_TEST_CTRL_2 0x1e
+#define RT1016_TEST_CTRL_3 0x1f
+#define RT1016_CLOCK_1 0x20
+#define RT1016_CLOCK_2 0x21
+#define RT1016_CLOCK_3 0x22
+#define RT1016_CLOCK_4 0x23
+#define RT1016_CLOCK_5 0x24
+#define RT1016_CLOCK_6 0x25
+#define RT1016_CLOCK_7 0x26
+#define RT1016_I2S_CTRL 0x40
+#define RT1016_DAC_CTRL_1 0x60
+#define RT1016_SC_CTRL_1 0x80
+#define RT1016_SC_CTRL_2 0x81
+#define RT1016_SC_CTRL_3 0x82
+#define RT1016_SC_CTRL_4 0x83
+#define RT1016_SIL_DET 0xa0
+#define RT1016_SYS_CLK 0xc0
+#define RT1016_BIAS_CUR 0xc1
+#define RT1016_DAC_CTRL_2 0xc2
+#define RT1016_LDO_CTRL 0xc3
+#define RT1016_CLASSD_1 0xc4
+#define RT1016_PLL1 0xc5
+#define RT1016_PLL2 0xc6
+#define RT1016_PLL3 0xc7
+#define RT1016_CLASSD_2 0xc8
+#define RT1016_CLASSD_OUT 0xc9
+#define RT1016_CLASSD_3 0xca
+#define RT1016_CLASSD_4 0xcb
+#define RT1016_CLASSD_5 0xcc
+#define RT1016_PWR_CTRL 0xcf
+
+/* global definition */
+#define RT1016_L_VOL_MASK (0xff << 8)
+#define RT1016_L_VOL_SFT 8
+#define RT1016_R_VOL_MASK (0xff)
+#define RT1016_R_VOL_SFT 0
+
+/* 0x04 */
+#define RT1016_DA_MUTE_L_SFT 7
+#define RT1016_DA_MUTE_R_SFT 6
+
+/* 0x20 */
+#define RT1016_CLK_SYS_SEL_MASK (0x1 << 15)
+#define RT1016_CLK_SYS_SEL_SFT 15
+#define RT1016_CLK_SYS_SEL_MCLK (0x0 << 15)
+#define RT1016_CLK_SYS_SEL_PLL (0x1 << 15)
+#define RT1016_PLL_SEL_MASK (0x1 << 13)
+#define RT1016_PLL_SEL_SFT 13
+#define RT1016_PLL_SEL_MCLK (0x0 << 13)
+#define RT1016_PLL_SEL_BCLK (0x1 << 13)
+
+/* 0x21 */
+#define RT1016_FS_PD_MASK (0x7 << 13)
+#define RT1016_FS_PD_SFT 13
+#define RT1016_OSR_PD_MASK (0x3 << 10)
+#define RT1016_OSR_PD_SFT 10
+
+/* 0x22 */
+#define RT1016_PWR_DAC_FILTER (0x1 << 11)
+#define RT1016_PWR_DAC_FILTER_BIT 11
+#define RT1016_PWR_DACMOD (0x1 << 10)
+#define RT1016_PWR_DACMOD_BIT 10
+#define RT1016_PWR_CLK_FIFO (0x1 << 9)
+#define RT1016_PWR_CLK_FIFO_BIT 9
+#define RT1016_PWR_CLK_PUREDC (0x1 << 8)
+#define RT1016_PWR_CLK_PUREDC_BIT 8
+#define RT1016_PWR_SIL_DET (0x1 << 7)
+#define RT1016_PWR_SIL_DET_BIT 7
+#define RT1016_PWR_RC_25M (0x1 << 6)
+#define RT1016_PWR_RC_25M_BIT 6
+#define RT1016_PWR_PLL1 (0x1 << 5)
+#define RT1016_PWR_PLL1_BIT 5
+#define RT1016_PWR_ANA_CTRL (0x1 << 4)
+#define RT1016_PWR_ANA_CTRL_BIT 4
+#define RT1016_PWR_CLK_SYS (0x1 << 3)
+#define RT1016_PWR_CLK_SYS_BIT 3
+
+/* 0x23 */
+#define RT1016_PWR_LRCK_DET (0x1 << 15)
+#define RT1016_PWR_LRCK_DET_BIT 15
+#define RT1016_PWR_BCLK_DET (0x1 << 11)
+#define RT1016_PWR_BCLK_DET_BIT 11
+
+/* 0x40 */
+#define RT1016_I2S_BCLK_MS_MASK (0x1 << 15)
+#define RT1016_I2S_BCLK_MS_SFT 15
+#define RT1016_I2S_BCLK_MS_32 (0x0 << 15)
+#define RT1016_I2S_BCLK_MS_64 (0x1 << 15)
+#define RT1016_I2S_BCLK_POL_MASK (0x1 << 13)
+#define RT1016_I2S_BCLK_POL_SFT 13
+#define RT1016_I2S_BCLK_POL_NOR (0x0 << 13)
+#define RT1016_I2S_BCLK_POL_INV (0x1 << 13)
+#define RT1016_I2S_DATA_SWAP_MASK (0x1 << 10)
+#define RT1016_I2S_DATA_SWAP_SFT 10
+#define RT1016_I2S_DL_MASK (0x7 << 4)
+#define RT1016_I2S_DL_SFT 4
+#define RT1016_I2S_DL_16 (0x1 << 4)
+#define RT1016_I2S_DL_20 (0x2 << 4)
+#define RT1016_I2S_DL_24 (0x3 << 4)
+#define RT1016_I2S_DL_32 (0x4 << 4)
+#define RT1016_I2S_MS_MASK (0x1 << 3)
+#define RT1016_I2S_MS_SFT 3
+#define RT1016_I2S_MS_M (0x0 << 3)
+#define RT1016_I2S_MS_S (0x1 << 3)
+#define RT1016_I2S_DF_MASK (0x7 << 0)
+#define RT1016_I2S_DF_SFT 0
+#define RT1016_I2S_DF_I2S (0x0)
+#define RT1016_I2S_DF_LEFT (0x1)
+#define RT1016_I2S_DF_PCM_A (0x2)
+#define RT1016_I2S_DF_PCM_B (0x3)
+
+/* 0xa0 */
+#define RT1016_SIL_DET_EN (0x1 << 15)
+#define RT1016_SIL_DET_EN_BIT 15
+
+/* 0xc2 */
+#define RT1016_CKGEN_DAC (0x1 << 13)
+#define RT1016_CKGEN_DAC_BIT 13
+
+/* 0xc4 */
+#define RT1016_VCM_SLOW (0x1 << 6)
+#define RT1016_VCM_SLOW_BIT 6
+
+/* 0xc5 */
+#define RT1016_PLL_M_MAX 0xf
+#define RT1016_PLL_M_MASK (RT1016_PLL_M_MAX << 12)
+#define RT1016_PLL_M_SFT 12
+#define RT1016_PLL_M_BP (0x1 << 11)
+#define RT1016_PLL_M_BP_SFT 11
+#define RT1016_PLL_N_MAX 0x1ff
+#define RT1016_PLL_N_MASK (RT1016_PLL_N_MAX << 0)
+#define RT1016_PLL_N_SFT 0
+
+/* 0xc6 */
+#define RT1016_PLL2_EN (0x1 << 15)
+#define RT1016_PLL2_EN_BIT 15
+#define RT1016_PLL_K_BP (0x1 << 5)
+#define RT1016_PLL_K_BP_SFT 5
+#define RT1016_PLL_K_MAX 0x1f
+#define RT1016_PLL_K_MASK (RT1016_PLL_K_MAX)
+#define RT1016_PLL_K_SFT 0
+
+/* 0xcf */
+#define RT1016_PWR_BG_1_2 (0x1 << 12)
+#define RT1016_PWR_BG_1_2_BIT 12
+#define RT1016_PWR_MBIAS_BG (0x1 << 11)
+#define RT1016_PWR_MBIAS_BG_BIT 11
+#define RT1016_PWR_PLL (0x1 << 9)
+#define RT1016_PWR_PLL_BIT 9
+#define RT1016_PWR_BASIC (0x1 << 8)
+#define RT1016_PWR_BASIC_BIT 8
+#define RT1016_PWR_CLSD (0x1 << 7)
+#define RT1016_PWR_CLSD_BIT 7
+#define RT1016_PWR_25M (0x1 << 6)
+#define RT1016_PWR_25M_BIT 6
+#define RT1016_PWR_DACL (0x1 << 4)
+#define RT1016_PWR_DACL_BIT 4
+#define RT1016_PWR_DACR (0x1 << 3)
+#define RT1016_PWR_DACR_BIT 3
+#define RT1016_PWR_LDO2 (0x1 << 2)
+#define RT1016_PWR_LDO2_BIT 2
+#define RT1016_PWR_VREF (0x1 << 1)
+#define RT1016_PWR_VREF_BIT 1
+#define RT1016_PWR_MBIAS (0x1 << 0)
+#define RT1016_PWR_MBIAS_BIT 0
+
+/* System Clock Source */
+enum {
+ RT1016_SCLK_S_MCLK,
+ RT1016_SCLK_S_PLL,
+};
+
+/* PLL1 Source */
+enum {
+ RT1016_PLL_S_MCLK,
+ RT1016_PLL_S_BCLK,
+};
+
+enum {
+ RT1016_AIF1,
+ RT1016_AIFS,
+};
+
+struct rt1016_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#endif /* __RT1016_H__ */
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
new file mode 100644
index 000000000000..a9c000876be8
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1017-sdca-sdw.c -- rt1017 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt1017-sdca-sdw.h"
+
+static bool rt1017_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3206:
+ case 0xc000:
+ case 0xc001:
+ case 0xc022:
+ case 0xc030:
+ case 0xc104:
+ case 0xc10b:
+ case 0xc10c:
+ case 0xc110:
+ case 0xc112:
+ case 0xc300:
+ case 0xc301:
+ case 0xc318:
+ case 0xc325 ... 0xc328:
+ case 0xc331:
+ case 0xc340:
+ case 0xc350 ... 0xc351:
+ case 0xc500:
+ case 0xc502:
+ case 0xc504:
+ case 0xc507:
+ case 0xc509:
+ case 0xc510:
+ case 0xc512:
+ case 0xc518:
+ case 0xc51b:
+ case 0xc51d:
+ case 0xc520:
+ case 0xc540 ... 0xc542:
+ case 0xc550 ... 0xc552:
+ case 0xc600:
+ case 0xc602:
+ case 0xc612:
+ case 0xc622:
+ case 0xc632:
+ case 0xc642:
+ case 0xc651:
+ case 0xca00:
+ case 0xca09 ... 0xca0c:
+ case 0xca0e ... 0xca0f:
+ case 0xca10 ... 0xca11:
+ case 0xca16 ... 0xca17:
+ case 0xcb00:
+ case 0xcc00:
+ case 0xcc02:
+ case 0xd017:
+ case 0xd01a ... 0xd01c:
+ case 0xd101:
+ case 0xd20c:
+ case 0xd300:
+ case 0xd370:
+ case 0xd500:
+ case 0xd545 ... 0xd548:
+ case 0xd5a5 ... 0xd5a8:
+ case 0xd5aa ... 0xd5ad:
+ case 0xda04 ... 0xda07:
+ case 0xda09 ... 0xda0a:
+ case 0xda0c ... 0xda0f:
+ case 0xda11 ... 0xda14:
+ case 0xda16 ... 0xda19:
+ case 0xdab6 ... 0xdabb:
+ case 0xdb09 ... 0xdb0a:
+ case 0xdb14:
+
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU,
+ RT1017_SDCA_CTL_FU_MUTE, 0x01):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22,
+ RT1017_SDCA_CTL_BYPASS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29,
+ RT1017_SDCA_CTL_PROT_STAT, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1017_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0xc000:
+ case 0xc022:
+ case 0xc351:
+ case 0xc518:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29,
+ RT1017_SDCA_CTL_PROT_STAT, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_sequence rt1017_blind_write[] = {
+ { 0xc001, 0x43 },
+ { 0x2f55, 0x02 },
+ { 0x3206, 0x80 },
+ { 0x005f, 0x7f },
+ { 0xd101, 0xa0 },
+ { 0xc112, 0xc0 },
+ { 0xc104, 0xaa },
+ { 0xc110, 0x59 },
+ { 0xc112, 0xc0 },
+ { 0xc340, 0x80 },
+ { 0xd017, 0x2c },
+ { 0xd01a, 0xc8 },
+ { 0xd01b, 0xcf },
+ { 0xd01c, 0x0c },
+ { 0xd20c, 0x14 },
+ { 0xdb09, 0x0f },
+ { 0xdb0a, 0x7f },
+ { 0xdb14, 0x03 },
+ { 0xcb00, 0x31 },
+ { 0xc318, 0x44 },
+ { 0xc325, 0xce },
+ { 0xc326, 0x13 },
+ { 0xc327, 0x5f },
+ { 0xc328, 0xf3 },
+ { 0xc350, 0xe1 },
+ { 0xc351, 0x88 },
+ { 0xc030, 0x14 },
+ { 0xc331, 0xf2 },
+ { 0xc551, 0x0f },
+ { 0xc552, 0xff },
+ { 0xc651, 0xc0 },
+ { 0xc550, 0xd0 },
+ { 0xc612, 0x00 },
+ { 0xc622, 0x00 },
+ { 0xc632, 0x00 },
+ { 0xc642, 0x00 },
+ { 0xc602, 0xf0 },
+ { 0xc600, 0xd0 },
+ { 0xcc02, 0x78 },
+ { 0xcc00, 0x90 },
+ { 0xc300, 0x3f },
+ { 0xc301, 0x1d },
+ { 0xc10b, 0x2e },
+ { 0xc10c, 0x36 },
+
+ { 0xd5a5, 0x00 },
+ { 0xd5a6, 0x6a },
+ { 0xd5a7, 0xaa },
+ { 0xd5a8, 0xaa },
+ { 0xd5aa, 0x00 },
+ { 0xd5ab, 0x16 },
+ { 0xd5ac, 0xdb },
+ { 0xd5ad, 0x6d },
+ { 0xd545, 0x09 },
+ { 0xd546, 0x30 },
+ { 0xd547, 0xf0 },
+ { 0xd548, 0xf0 },
+ { 0xd500, 0x20 },
+ { 0xc504, 0x3f },
+ { 0xc540, 0x00 },
+ { 0xc541, 0x0a },
+ { 0xc542, 0x1a },
+ { 0xc512, 0x00 },
+ { 0xc520, 0x40 },
+ { 0xc51b, 0x7f },
+ { 0xc51d, 0x0f },
+ { 0xc500, 0x40 },
+ { 0xc502, 0xde },
+ { 0xc507, 0x05 },
+ { 0xc509, 0x05 },
+ { 0xc510, 0x40 },
+ { 0xc518, 0xc0 },
+ { 0xc500, 0xc0 },
+
+ { 0xda0c, 0x00 },
+ { 0xda0d, 0x0b },
+ { 0xda0e, 0x55 },
+ { 0xda0f, 0x55 },
+ { 0xda04, 0x00 },
+ { 0xda05, 0x51 },
+ { 0xda06, 0xeb },
+ { 0xda07, 0x85 },
+ { 0xca16, 0x0f },
+ { 0xca17, 0x00 },
+ { 0xda09, 0x5d },
+ { 0xda0a, 0xc0 },
+ { 0xda11, 0x26 },
+ { 0xda12, 0x66 },
+ { 0xda13, 0x66 },
+ { 0xda14, 0x66 },
+ { 0xda16, 0x79 },
+ { 0xda17, 0x99 },
+ { 0xda18, 0x99 },
+ { 0xda19, 0x99 },
+ { 0xca09, 0x00 },
+ { 0xca0a, 0x07 },
+ { 0xca0b, 0x89 },
+ { 0xca0c, 0x61 },
+ { 0xca0e, 0x00 },
+ { 0xca0f, 0x03 },
+ { 0xca10, 0xc4 },
+ { 0xca11, 0xb0 },
+ { 0xdab6, 0x00 },
+ { 0xdab7, 0x01 },
+ { 0xdab8, 0x00 },
+ { 0xdab9, 0x00 },
+ { 0xdaba, 0x00 },
+ { 0xdabb, 0x00 },
+ { 0xd017, 0x0e },
+ { 0xca00, 0xcd },
+ { 0xc022, 0x84 },
+};
+
+#define RT1017_MAX_REG_NUM 0x4108ffff
+
+static const struct regmap_config rt1017_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1017_sdca_readable_register,
+ .volatile_reg = rt1017_sdca_volatile_register,
+ .max_register = RT1017_MAX_REG_NUM,
+ .reg_defaults = rt1017_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1017_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1017_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists
+ * port = 1 for AMP playback
+ * port = 2 for IV capture
+ */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = BIT(1); /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 64;
+
+ return 0;
+}
+
+static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+
+ if (rt1017->hw_init)
+ return 0;
+
+ if (rt1017->first_hw_init) {
+ regcache_cache_only(rt1017->regmap, false);
+ regcache_cache_bypass(rt1017->regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* sw reset */
+ regmap_write(rt1017->regmap, 0xc000, 0x02);
+
+ /* initial settings - blind write */
+ regmap_multi_reg_write(rt1017->regmap, rt1017_blind_write,
+ ARRAY_SIZE(rt1017_blind_write));
+
+ if (rt1017->first_hw_init) {
+ regcache_cache_bypass(rt1017->regmap, false);
+ regcache_mark_dirty(rt1017->regmap);
+ } else
+ rt1017->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1017->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "hw_init complete\n");
+ return 0;
+}
+
+static int rt1017_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1017->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1017->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1017_sdca_io_init(&slave->dev, slave);
+}
+
+static const char * const rt1017_rx_data_ch_select[] = {
+ "Bypass",
+ "CN1",
+ "CN2",
+ "CN3",
+ "CN4",
+ "(1+2)/2",
+ "(1+3)/2",
+ "(1+4)/2",
+ "(2+3)/2",
+ "(2+4)/2",
+ "(3+4)/2",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1017_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0),
+ 0, rt1017_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1017_sdca_controls[] = {
+ /* UDMPU Cluster Selection */
+ SOC_ENUM("RX Channel Select", rt1017_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1017_sto_dac =
+ SOC_DAPM_SINGLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x1),
+ 0, 1, 1);
+
+static int rt1017_sdca_pde23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1017_sdca_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt1017->regmap, RT1017_PWM_TRIM_1,
+ RT1017_PWM_FREQ_CTL_SRC_SEL_MASK, RT1017_PWM_FREQ_CTL_SRC_SEL_REG);
+ regmap_write(rt1017->regmap, RT1017_CLASSD_INT_1, 0x10);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1017_sdca_feedback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x09);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1017_sdca_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0,
+ rt1017_sdca_feedback_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1017_sto_dac),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1017_sdca_classd_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+
+ SND_SOC_DAPM_SUPPLY("PDE23", SND_SOC_NOPM, 0, 0,
+ rt1017_sdca_pde23_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("I Gen"),
+ SND_SOC_DAPM_SIGGEN("V Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = {
+
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "CLASS D", NULL, "PDE23" },
+ { "SPO", NULL, "CLASS D" },
+
+ { "I Sense", NULL, "I Gen" },
+ { "V Sense", NULL, "V Gen" },
+ { "I Sense", NULL, "PDE23" },
+ { "V Sense", NULL, "PDE23" },
+ { "DP2TX", NULL, "I Sense" },
+ { "DP2TX", NULL, "V Sense" },
+};
+
+static const struct sdw_slave_ops rt1017_sdca_slave_ops = {
+ .read_prop = rt1017_sdca_read_prop,
+ .update_status = rt1017_sdca_update_status,
+};
+
+static int rt1017_sdca_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static void rt1017_sdca_component_remove(struct snd_soc_component *component)
+{
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1017->regmap, true);
+}
+
+static const struct snd_soc_component_driver soc_sdca_component_rt1017 = {
+ .probe = rt1017_sdca_component_probe,
+ .remove = rt1017_sdca_component_remove,
+ .controls = rt1017_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt1017_sdca_controls),
+ .dapm_widgets = rt1017_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1017_sdca_dapm_widgets),
+ .dapm_routes = rt1017_sdca_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1017_sdca_dapm_routes),
+ .endianness = 1,
+};
+
+static int rt1017_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1017_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels, ch_mask;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1017->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ port = 1;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ port = 2;
+ }
+
+ num_channels = params_channels(params);
+ ch_mask = (1 << num_channels) - 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = num_channels;
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ port_config.ch_mask = ch_mask;
+ port_config.num = port;
+
+ dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n",
+ params_rate(params), num_channels, snd_pcm_format_width(params_format(params)),
+ direction, ch_mask, port);
+
+ retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT1017_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1017_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1017_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1017_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt1017_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1017->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1017->sdw_slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt1017_sdca_ops = {
+ .hw_params = rt1017_sdca_pcm_hw_params,
+ .hw_free = rt1017_sdca_pcm_hw_free,
+ .set_stream = rt1017_sdca_set_sdw_stream,
+ .shutdown = rt1017_sdca_shutdown,
+};
+
+#define RT1017_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT1017_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1017_sdca_dai[] = {
+ {
+ .name = "rt1017-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = RT1017_STEREO_RATES,
+ .formats = RT1017_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = RT1017_STEREO_RATES,
+ .formats = RT1017_FORMATS,
+ },
+ .ops = &rt1017_sdca_ops,
+ },
+};
+
+static int rt1017_sdca_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017;
+ int ret;
+
+ rt1017 = devm_kzalloc(dev, sizeof(*rt1017), GFP_KERNEL);
+ if (!rt1017)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1017);
+ rt1017->sdw_slave = slave;
+ rt1017->regmap = regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1017->hw_init = false;
+ rt1017->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_component_rt1017,
+ rt1017_sdca_dai,
+ ARRAY_SIZE(rt1017_sdca_dai));
+
+ return ret;
+}
+
+static int rt1017_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1017_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1017_sdca_init(&slave->dev, regmap, slave);
+}
+
+static int rt1017_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev);
+
+ if (rt1017->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1017_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1017, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id);
+
+static int rt1017_sdca_dev_suspend(struct device *dev)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+
+ if (!rt1017->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1017->regmap, true);
+
+ return 0;
+}
+
+#define RT1017_PROBE_TIMEOUT 5000
+
+static int rt1017_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1017->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1017_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1017->regmap, false);
+ regcache_sync(rt1017->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1017_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1017_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt1017-sdca",
+ .pm = pm_ptr(&rt1017_sdca_pm),
+ },
+ .probe = rt1017_sdca_sdw_probe,
+ .remove = rt1017_sdca_sdw_remove,
+ .ops = &rt1017_sdca_slave_ops,
+ .id_table = rt1017_sdca_id,
+};
+module_sdw_driver(rt1017_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1017 driver SDCA SDW");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h
new file mode 100644
index 000000000000..4932b5dbe3c0
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1017-sdca-sdw.h -- RT1017 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1017_SDW_H__
+#define __RT1017_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* RT1017 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1017 SDCA entity */
+#define RT1017_SDCA_ENT_PDE23 0x31
+#define RT1017_SDCA_ENT_PDE22 0x33
+#define RT1017_SDCA_ENT_CS21 0x21
+#define RT1017_SDCA_ENT_SAPU29 0x29
+#define RT1017_SDCA_ENT_XU22 0x22
+#define RT1017_SDCA_ENT_FU 0x03
+#define RT1017_SDCA_ENT_UDMPU21 0x02
+
+/* RT1017 SDCA control */
+#define RT1017_SDCA_CTL_FS_INDEX 0x10
+#define RT1017_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1017_SDCA_CTL_PROT_STAT 0x11
+#define RT1017_SDCA_CTL_BYPASS 0x01
+#define RT1017_SDCA_CTL_FU_MUTE 0x01
+#define RT1017_SDCA_CTL_FU_VOLUME 0x02
+#define RT1017_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+
+#define RT1017_CLASSD_INT_1 0xd300
+#define RT1017_PWM_TRIM_1 0xd370
+
+
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_MASK (0x3 << 2)
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_EFUSE (0x2 << 2)
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_REG (0x0 << 2)
+
+enum {
+ RT1017_SDCA_RATE_44100HZ = 0x8,
+ RT1017_SDCA_RATE_48000HZ = 0x9,
+ RT1017_SDCA_RATE_96000HZ = 0xb,
+ RT1017_SDCA_RATE_192000HZ = 0xd,
+};
+
+struct rt1017_sdca_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+static const struct reg_default rt1017_sdca_reg_defaults[] = {
+ { 0x3206, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc030, 0x54 },
+ { 0xc104, 0x8a },
+ { 0xc10b, 0x2f },
+ { 0xc10c, 0x2f },
+ { 0xc110, 0x49 },
+ { 0xc112, 0x10 },
+ { 0xc300, 0xff },
+ { 0xc301, 0xdd },
+ { 0xc318, 0x40 },
+ { 0xc325, 0x00 },
+ { 0xc326, 0x00 },
+ { 0xc327, 0x00 },
+ { 0xc328, 0x02 },
+ { 0xc331, 0xb2 },
+ { 0xc340, 0x02 },
+ { 0xc350, 0x21 },
+ { 0xc500, 0x00 },
+ { 0xc502, 0x00 },
+ { 0xc504, 0x3f },
+ { 0xc507, 0x1f },
+ { 0xc509, 0x1f },
+ { 0xc510, 0x40 },
+ { 0xc512, 0x00 },
+ { 0xc518, 0x02 },
+ { 0xc51b, 0x7f },
+ { 0xc51d, 0x0f },
+ { 0xc520, 0x00 },
+ { 0xc540, 0x80 },
+ { 0xc541, 0x00 },
+ { 0xc542, 0x0a },
+ { 0xc550, 0x80 },
+ { 0xc551, 0x0f },
+ { 0xc552, 0xff },
+ { 0xc600, 0x10 },
+ { 0xc602, 0x83 },
+ { 0xc612, 0x40 },
+ { 0xc622, 0x40 },
+ { 0xc632, 0x40 },
+ { 0xc642, 0x40 },
+ { 0xc651, 0x00 },
+ { 0xca00, 0xc1 },
+ { 0xca09, 0x00 },
+ { 0xca0a, 0x51 },
+ { 0xca0b, 0xeb },
+ { 0xca0c, 0x85 },
+ { 0xca0e, 0x00 },
+ { 0xca0f, 0x10 },
+ { 0xca10, 0x62 },
+ { 0xca11, 0x4d },
+ { 0xca16, 0x0f },
+ { 0xca17, 0x00 },
+ { 0xcb00, 0x10 },
+ { 0xcc00, 0x10 },
+ { 0xcc02, 0x0b },
+ { 0xd017, 0x09 },
+ { 0xd01a, 0x00 },
+ { 0xd01b, 0x00 },
+ { 0xd01c, 0x00 },
+ { 0xd101, 0xa0 },
+ { 0xd20c, 0x14 },
+ { 0xd300, 0x0f },
+ { 0xd370, 0x18 },
+ { 0xd500, 0x00 },
+ { 0xd545, 0x0b },
+ { 0xd546, 0xf9 },
+ { 0xd547, 0xb2 },
+ { 0xd548, 0xa9 },
+ { 0xd5a5, 0x00 },
+ { 0xd5a6, 0x00 },
+ { 0xd5a7, 0x00 },
+ { 0xd5a8, 0x00 },
+ { 0xd5aa, 0x00 },
+ { 0xd5ab, 0x00 },
+ { 0xd5ac, 0x00 },
+ { 0xd5ad, 0x00 },
+ { 0xda04, 0x03 },
+ { 0xda05, 0x33 },
+ { 0xda06, 0x33 },
+ { 0xda07, 0x33 },
+ { 0xda09, 0x5d },
+ { 0xda0a, 0xc0 },
+ { 0xda0c, 0x00 },
+ { 0xda0d, 0x01 },
+ { 0xda0e, 0x5d },
+ { 0xda0f, 0x86 },
+ { 0xda11, 0x20 },
+ { 0xda12, 0x00 },
+ { 0xda13, 0x00 },
+ { 0xda14, 0x00 },
+ { 0xda16, 0x7f },
+ { 0xda17, 0xff },
+ { 0xda18, 0xff },
+ { 0xda19, 0xff },
+ { 0xdab6, 0x00 },
+ { 0xdab7, 0x01 },
+ { 0xdab8, 0x00 },
+ { 0xdab9, 0x01 },
+ { 0xdaba, 0x00 },
+ { 0xdabb, 0x01 },
+ { 0xdb09, 0x0f },
+ { 0xdb0a, 0xff },
+ { 0xdb14, 0x00 },
+
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU,
+ RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22,
+ RT1017_SDCA_CTL_BYPASS, 0), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+#endif /* __RT1017_SDW_H__ */
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
new file mode 100644
index 000000000000..86539c6f6cc1
--- /dev/null
+++ b/sound/soc/codecs/rt1019.c
@@ -0,0 +1,609 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1019.c -- RT1019 ALSA SoC audio amplifier driver
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1019.h"
+
+static const struct reg_default rt1019_reg[] = {
+ { 0x0000, 0x00 },
+ { 0x0011, 0x04 },
+ { 0x0013, 0x00 },
+ { 0x0019, 0x30 },
+ { 0x001b, 0x01 },
+ { 0x005c, 0x00 },
+ { 0x005e, 0x10 },
+ { 0x005f, 0xec },
+ { 0x0061, 0x10 },
+ { 0x0062, 0x19 },
+ { 0x0066, 0x08 },
+ { 0x0100, 0x80 },
+ { 0x0100, 0x51 },
+ { 0x0102, 0x23 },
+ { 0x0311, 0x00 },
+ { 0x0312, 0x3e },
+ { 0x0313, 0x86 },
+ { 0x0400, 0x03 },
+ { 0x0401, 0x02 },
+ { 0x0402, 0x01 },
+ { 0x0504, 0xff },
+ { 0x0505, 0x24 },
+ { 0x0b00, 0x50 },
+ { 0x0b01, 0xc3 },
+};
+
+static bool rt1019_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1019_PWR_STRP_2:
+ case RT1019_VER_ID:
+ case RT1019_VEND_ID_1:
+ case RT1019_VEND_ID_2:
+ case RT1019_DEV_ID_1:
+ case RT1019_DEV_ID_2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1019_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1019_RESET:
+ case RT1019_IDS_CTRL:
+ case RT1019_ASEL_CTRL:
+ case RT1019_PWR_STRP_2:
+ case RT1019_BEEP_TONE:
+ case RT1019_VER_ID:
+ case RT1019_VEND_ID_1:
+ case RT1019_VEND_ID_2:
+ case RT1019_DEV_ID_1:
+ case RT1019_DEV_ID_2:
+ case RT1019_SDB_CTRL:
+ case RT1019_CLK_TREE_1:
+ case RT1019_CLK_TREE_2:
+ case RT1019_CLK_TREE_3:
+ case RT1019_PLL_1:
+ case RT1019_PLL_2:
+ case RT1019_PLL_3:
+ case RT1019_TDM_1:
+ case RT1019_TDM_2:
+ case RT1019_TDM_3:
+ case RT1019_DMIX_MONO_1:
+ case RT1019_DMIX_MONO_2:
+ case RT1019_BEEP_1:
+ case RT1019_BEEP_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0);
+
+static const char * const rt1019_din_source_select[] = {
+ "Left",
+ "Right",
+ "Left + Right average",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1019_mono_lr_sel, RT1019_IDS_CTRL, 0,
+ rt1019_din_source_select);
+
+static const struct snd_kcontrol_new rt1019_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", RT1019_DMIX_MONO_1, 0,
+ 127, 0, dac_vol_tlv),
+ SOC_ENUM("Mono LR Select", rt1019_mono_lr_sel),
+};
+
+static int r1019_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(component, RT1019_SDB_CTRL, 0xb);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write(component, RT1019_SDB_CTRL, 0xa);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1019_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ r1019_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt1019_dapm_routes[] = {
+ { "DAC", NULL, "AIFRX" },
+ { "SPO", NULL, "DAC" },
+};
+
+static int rt1019_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0, sys_div_da_filter = 0;
+ unsigned int sys_dac_osr = 0, sys_fifo_clk = 0;
+ unsigned int sys_clk_cal = 0, sys_asrc_in = 0;
+
+ rt1019->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt1019->sysclk, rt1019->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1019->bclk = rt1019->lrck * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt1019->bclk, rt1019->lrck);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (pre_div) {
+ case 0:
+ sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV1;
+ sys_dac_osr = RT1019_SYS_DA_OSR_DIV1;
+ sys_asrc_in = RT1019_ASRC_256FS_DIV1;
+ sys_fifo_clk = RT1019_SEL_FIFO_DIV1;
+ sys_clk_cal = RT1019_SEL_CLK_CAL_DIV1;
+ break;
+ case 1:
+ sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV2;
+ sys_dac_osr = RT1019_SYS_DA_OSR_DIV2;
+ sys_asrc_in = RT1019_ASRC_256FS_DIV2;
+ sys_fifo_clk = RT1019_SEL_FIFO_DIV2;
+ sys_clk_cal = RT1019_SEL_CLK_CAL_DIV2;
+ break;
+ case 3:
+ sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV4;
+ sys_dac_osr = RT1019_SYS_DA_OSR_DIV4;
+ sys_asrc_in = RT1019_ASRC_256FS_DIV4;
+ sys_fifo_clk = RT1019_SEL_FIFO_DIV4;
+ sys_clk_cal = RT1019_SEL_CLK_CAL_DIV4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len = RT1019_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT1019_I2S_DL_24;
+ break;
+ case 32:
+ val_len = RT1019_I2S_DL_32;
+ break;
+ case 8:
+ val_len = RT1019_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1019_TDM_2, RT1019_I2S_DL_MASK,
+ val_len);
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_SEL_FIFO_MASK, sys_fifo_clk);
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_2,
+ RT1019_SYS_DIV_DA_FIL_MASK | RT1019_SYS_DA_OSR_MASK |
+ RT1019_ASRC_256FS_MASK, sys_div_da_filter | sys_dac_osr |
+ sys_asrc_in);
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_3,
+ RT1019_SEL_CLK_CAL_MASK, sys_clk_cal);
+
+ return 0;
+}
+
+static int rt1019_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg_val = 0, reg_val2 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val2 |= RT1019_TDM_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1019_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1019_I2S_DF_PCM_A_R;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1019_I2S_DF_PCM_B_R;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1019_TDM_2,
+ RT1019_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT1019_TDM_1,
+ RT1019_TDM_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1019_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1019->sysclk && clk_id == rt1019->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1019_SCLK_S_BCLK:
+ reg_val |= RT1019_CLK_SYS_PRE_SEL_BCLK;
+ break;
+
+ case RT1019_SCLK_S_PLL:
+ reg_val |= RT1019_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1019->sysclk = freq;
+ rt1019->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt1019_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt1019->pll_in = 0;
+ rt1019->pll_out = 0;
+ return 0;
+ }
+
+ if (source == rt1019->pll_src && freq_in == rt1019->pll_in &&
+ freq_out == rt1019->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1019_PLL_S_BCLK:
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_PLL_SRC_MASK, RT1019_PLL_SRC_SEL_BCLK);
+ break;
+
+ case RT1019_PLL_S_RC25M:
+ snd_soc_component_update_bits(component, RT1019_CLK_TREE_1,
+ RT1019_PLL_SRC_MASK, RT1019_PLL_SRC_SEL_RC);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_update_bits(component, RT1019_PWR_STRP_2,
+ RT1019_AUTO_BITS_SEL_MASK | RT1019_AUTO_CLK_SEL_MASK,
+ RT1019_AUTO_BITS_SEL_MANU | RT1019_AUTO_CLK_SEL_MANU);
+ snd_soc_component_update_bits(component, RT1019_PLL_1,
+ RT1019_PLL_M_MASK | RT1019_PLL_M_BP_MASK | RT1019_PLL_Q_8_8_MASK,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1019_PLL_M_SFT) |
+ (pll_code.m_bp << RT1019_PLL_M_BP_SFT) |
+ ((pll_code.n_code >> 8) & RT1019_PLL_Q_8_8_MASK));
+ snd_soc_component_update_bits(component, RT1019_PLL_2,
+ RT1019_PLL_Q_7_0_MASK, pll_code.n_code & RT1019_PLL_Q_7_0_MASK);
+ snd_soc_component_update_bits(component, RT1019_PLL_3,
+ RT1019_PLL_K_MASK, pll_code.k_code);
+
+ rt1019->pll_in = freq_in;
+ rt1019->pll_out = freq_out;
+ rt1019->pll_src = source;
+
+ return 0;
+}
+
+static int rt1019_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int cn = 0, cl = 0, rx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 4:
+ cn = RT1019_I2S_TX_4CH;
+ break;
+ case 6:
+ cn = RT1019_I2S_TX_6CH;
+ break;
+ case 8:
+ cn = RT1019_I2S_TX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ cl = RT1019_TDM_CL_20;
+ break;
+ case 24:
+ cl = RT1019_TDM_CL_24;
+ break;
+ case 32:
+ cl = RT1019_TDM_CL_32;
+ break;
+ case 8:
+ cl = RT1019_TDM_CL_8;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum != 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+ /* This is an assumption that the system sends stereo audio to the
+ * amplifier typically. And the stereo audio is placed in slot 0/2/4/6
+ * as the starting slot. The users could select the channel from
+ * L/R/L+R by "Mono LR Select" control.
+ */
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ snd_soc_component_update_bits(component,
+ RT1019_TDM_3,
+ RT1019_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1019_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1019_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit + 1) << RT1019_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ snd_soc_component_update_bits(component,
+ RT1019_TDM_3,
+ RT1019_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1019_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit - 1) << RT1019_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1019_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ snd_soc_component_update_bits(component, RT1019_TDM_1,
+ RT1019_TDM_CL_MASK, cl);
+ snd_soc_component_update_bits(component, RT1019_TDM_2,
+ RT1019_I2S_CH_TX_MASK, cn);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1019_probe(struct snd_soc_component *component)
+{
+ struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component);
+
+ rt1019->component = component;
+ snd_soc_component_write(component, RT1019_SDB_CTRL, 0xa);
+
+ return 0;
+}
+
+#define RT1019_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1019_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1019_aif_dai_ops = {
+ .hw_params = rt1019_hw_params,
+ .set_fmt = rt1019_set_dai_fmt,
+ .set_sysclk = rt1019_set_dai_sysclk,
+ .set_pll = rt1019_set_dai_pll,
+ .set_tdm_slot = rt1019_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1019_dai[] = {
+ {
+ .name = "rt1019-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1019_STEREO_RATES,
+ .formats = RT1019_FORMATS,
+ },
+ .ops = &rt1019_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
+ .probe = rt1019_probe,
+ .controls = rt1019_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1019_snd_controls),
+ .dapm_widgets = rt1019_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1019_dapm_widgets),
+ .dapm_routes = rt1019_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1019_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1019_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = RT1019_BEEP_2,
+ .volatile_reg = rt1019_volatile_register,
+ .readable_reg = rt1019_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt1019_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1019_reg),
+};
+
+static const struct i2c_device_id rt1019_i2c_id[] = {
+ { "rt1019" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id);
+
+static const struct of_device_id rt1019_of_match[] __maybe_unused = {
+ { .compatible = "realtek,rt1019", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1019_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1019_acpi_match[] = {
+ { "10EC1019" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match);
+#endif
+
+static int rt1019_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1019_priv *rt1019;
+ int ret;
+ unsigned int val, val2, dev_id;
+
+ rt1019 = devm_kzalloc(&i2c->dev, sizeof(struct rt1019_priv),
+ GFP_KERNEL);
+ if (!rt1019)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1019);
+
+ rt1019->regmap = devm_regmap_init_i2c(i2c, &rt1019_regmap);
+ if (IS_ERR(rt1019->regmap)) {
+ ret = PTR_ERR(rt1019->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1019->regmap, RT1019_DEV_ID_1, &val);
+ regmap_read(rt1019->regmap, RT1019_DEV_ID_2, &val2);
+ dev_id = val << 8 | val2;
+ if (dev_id != RT1019_DEVICE_ID_VAL && dev_id != RT1019_DEVICE_ID_VAL2) {
+ dev_err(&i2c->dev,
+ "Device with ID register 0x%x is not rt1019\n", dev_id);
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1019, rt1019_dai, ARRAY_SIZE(rt1019_dai));
+}
+
+static struct i2c_driver rt1019_i2c_driver = {
+ .driver = {
+ .name = "rt1019",
+ .of_match_table = of_match_ptr(rt1019_of_match),
+ .acpi_match_table = ACPI_PTR(rt1019_acpi_match),
+ },
+ .probe = rt1019_i2c_probe,
+ .id_table = rt1019_i2c_id,
+};
+module_i2c_driver(rt1019_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1019 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1019.h b/sound/soc/codecs/rt1019.h
new file mode 100644
index 000000000000..48ba15efb48d
--- /dev/null
+++ b/sound/soc/codecs/rt1019.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1019.h -- RT1019 ALSA SoC audio amplifier driver
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1019_H__
+#define __RT1019_H__
+
+#define RT1019_DEVICE_ID_VAL 0x1019
+#define RT1019_DEVICE_ID_VAL2 0x6731
+
+#define RT1019_RESET 0x0000
+#define RT1019_IDS_CTRL 0x0011
+#define RT1019_ASEL_CTRL 0x0013
+#define RT1019_PWR_STRP_2 0x0019
+#define RT1019_BEEP_TONE 0x001b
+#define RT1019_VER_ID 0x005c
+#define RT1019_VEND_ID_1 0x005e
+#define RT1019_VEND_ID_2 0x005f
+#define RT1019_DEV_ID_1 0x0061
+#define RT1019_DEV_ID_2 0x0062
+#define RT1019_SDB_CTRL 0x0066
+#define RT1019_CLK_TREE_1 0x0100
+#define RT1019_CLK_TREE_2 0x0101
+#define RT1019_CLK_TREE_3 0x0102
+#define RT1019_PLL_1 0x0311
+#define RT1019_PLL_2 0x0312
+#define RT1019_PLL_3 0x0313
+#define RT1019_TDM_1 0x0400
+#define RT1019_TDM_2 0x0401
+#define RT1019_TDM_3 0x0402
+#define RT1019_DMIX_MONO_1 0x0504
+#define RT1019_DMIX_MONO_2 0x0505
+#define RT1019_BEEP_1 0x0b00
+#define RT1019_BEEP_2 0x0b01
+
+/* 0x0019 Power On Strap Control-2 */
+#define RT1019_AUTO_BITS_SEL_MASK (0x1 << 5)
+#define RT1019_AUTO_BITS_SEL_AUTO (0x1 << 5)
+#define RT1019_AUTO_BITS_SEL_MANU (0x0 << 5)
+#define RT1019_AUTO_CLK_SEL_MASK (0x1 << 4)
+#define RT1019_AUTO_CLK_SEL_AUTO (0x1 << 4)
+#define RT1019_AUTO_CLK_SEL_MANU (0x0 << 4)
+
+/* 0x0100 Clock Tree Control-1 */
+#define RT1019_CLK_SYS_PRE_SEL_MASK (0x1 << 7)
+#define RT1019_CLK_SYS_PRE_SEL_SFT 7
+#define RT1019_CLK_SYS_PRE_SEL_BCLK (0x0 << 7)
+#define RT1019_CLK_SYS_PRE_SEL_PLL (0x1 << 7)
+#define RT1019_PLL_SRC_MASK (0x1 << 4)
+#define RT1019_PLL_SRC_SFT 4
+#define RT1019_PLL_SRC_SEL_BCLK (0x0 << 4)
+#define RT1019_PLL_SRC_SEL_RC (0x1 << 4)
+#define RT1019_SEL_FIFO_MASK (0x3 << 2)
+#define RT1019_SEL_FIFO_DIV1 (0x0 << 2)
+#define RT1019_SEL_FIFO_DIV2 (0x1 << 2)
+#define RT1019_SEL_FIFO_DIV4 (0x2 << 2)
+
+/* 0x0101 clock tree control-2 */
+#define RT1019_SYS_DIV_DA_FIL_MASK (0x7 << 5)
+#define RT1019_SYS_DIV_DA_FIL_DIV1 (0x2 << 5)
+#define RT1019_SYS_DIV_DA_FIL_DIV2 (0x3 << 5)
+#define RT1019_SYS_DIV_DA_FIL_DIV4 (0x4 << 5)
+#define RT1019_SYS_DA_OSR_MASK (0x3 << 2)
+#define RT1019_SYS_DA_OSR_DIV1 (0x0 << 2)
+#define RT1019_SYS_DA_OSR_DIV2 (0x1 << 2)
+#define RT1019_SYS_DA_OSR_DIV4 (0x2 << 2)
+#define RT1019_ASRC_256FS_MASK 0x3
+#define RT1019_ASRC_256FS_DIV1 0x0
+#define RT1019_ASRC_256FS_DIV2 0x1
+#define RT1019_ASRC_256FS_DIV4 0x2
+
+/* 0x0102 clock tree control-3 */
+#define RT1019_SEL_CLK_CAL_MASK (0x3 << 6)
+#define RT1019_SEL_CLK_CAL_DIV1 (0x0 << 6)
+#define RT1019_SEL_CLK_CAL_DIV2 (0x1 << 6)
+#define RT1019_SEL_CLK_CAL_DIV4 (0x2 << 6)
+
+/* 0x0311 PLL-1 */
+#define RT1019_PLL_M_MASK (0xf << 4)
+#define RT1019_PLL_M_SFT 4
+#define RT1019_PLL_M_BP_MASK (0x1 << 1)
+#define RT1019_PLL_M_BP_SFT 1
+#define RT1019_PLL_Q_8_8_MASK (0x1)
+
+/* 0x0312 PLL-2 */
+#define RT1019_PLL_Q_7_0_MASK 0xff
+
+/* 0x0313 PLL-3 */
+#define RT1019_PLL_K_MASK 0x1f
+
+/* 0x0400 TDM Control-1 */
+#define RT1019_TDM_BCLK_MASK (0x1 << 6)
+#define RT1019_TDM_BCLK_NORM (0x0 << 6)
+#define RT1019_TDM_BCLK_INV (0x1 << 6)
+#define RT1019_TDM_CL_MASK (0x7)
+#define RT1019_TDM_CL_8 (0x4)
+#define RT1019_TDM_CL_32 (0x3)
+#define RT1019_TDM_CL_24 (0x2)
+#define RT1019_TDM_CL_20 (0x1)
+#define RT1019_TDM_CL_16 (0x0)
+
+/* 0x0401 TDM Control-2 */
+#define RT1019_I2S_CH_TX_MASK (0x3 << 6)
+#define RT1019_I2S_CH_TX_SFT 6
+#define RT1019_I2S_TX_2CH (0x0 << 6)
+#define RT1019_I2S_TX_4CH (0x1 << 6)
+#define RT1019_I2S_TX_6CH (0x2 << 6)
+#define RT1019_I2S_TX_8CH (0x3 << 6)
+#define RT1019_I2S_DF_MASK (0x7 << 3)
+#define RT1019_I2S_DF_SFT 3
+#define RT1019_I2S_DF_I2S (0x0 << 3)
+#define RT1019_I2S_DF_LEFT (0x1 << 3)
+#define RT1019_I2S_DF_PCM_A_R (0x2 << 3)
+#define RT1019_I2S_DF_PCM_B_R (0x3 << 3)
+#define RT1019_I2S_DF_PCM_A_F (0x6 << 3)
+#define RT1019_I2S_DF_PCM_B_F (0x7 << 3)
+#define RT1019_I2S_DL_MASK 0x7
+#define RT1019_I2S_DL_SFT 0
+#define RT1019_I2S_DL_16 0x0
+#define RT1019_I2S_DL_20 0x1
+#define RT1019_I2S_DL_24 0x2
+#define RT1019_I2S_DL_32 0x3
+#define RT1019_I2S_DL_8 0x4
+
+/* TDM1 Control-3 (0x0402) */
+#define RT1019_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4)
+#define RT1019_TDM_I2S_TX_R_DAC1_1_MASK 0x7
+#define RT1019_TDM_I2S_TX_L_DAC1_1_SFT 4
+#define RT1019_TDM_I2S_TX_R_DAC1_1_SFT 0
+
+/* System Clock Source */
+enum {
+ RT1019_SCLK_S_BCLK,
+ RT1019_SCLK_S_PLL,
+};
+
+/* PLL1 Source */
+enum {
+ RT1019_PLL_S_BCLK,
+ RT1019_PLL_S_RC25M,
+};
+
+enum {
+ RT1019_AIF1,
+ RT1019_AIFS
+};
+
+struct rt1019_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ unsigned int bclk_ratio;
+};
+
+#endif /* __RT1019_H__ */
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
new file mode 100644
index 000000000000..26b7382f97ef
--- /dev/null
+++ b/sound/soc/codecs/rt1305.c
@@ -0,0 +1,1179 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt1305.c -- RT1305 ALSA SoC amplifier component driver
+ *
+ * Copyright 2018 Realtek Semiconductor Corp.
+ * Author: Shuming Fan <shumingf@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1305.h"
+
+
+#define RT1305_PR_RANGE_BASE (0xff + 1)
+#define RT1305_PR_SPACING 0x100
+
+#define RT1305_PR_BASE (RT1305_PR_RANGE_BASE + (0 * RT1305_PR_SPACING))
+
+
+static const struct regmap_range_cfg rt1305_ranges[] = {
+ {
+ .name = "PR",
+ .range_min = RT1305_PR_BASE,
+ .range_max = RT1305_PR_BASE + 0xff,
+ .selector_reg = RT1305_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT1305_PRIV_DATA,
+ .window_len = 0x1,
+ },
+};
+
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1305_PR_BASE + 0xcf, 0x5548 },
+ { RT1305_PR_BASE + 0x5d, 0x0442 },
+ { RT1305_PR_BASE + 0xc1, 0x0320 },
+
+ { RT1305_POWER_STATUS, 0x0000 },
+
+ { RT1305_SPK_TEMP_PROTECTION_1, 0xd6de },
+ { RT1305_SPK_TEMP_PROTECTION_2, 0x0707 },
+ { RT1305_SPK_TEMP_PROTECTION_3, 0x4090 },
+
+ { RT1305_DAC_SET_1, 0xdfdf }, /* 4 ohm 2W */
+ { RT1305_ADC_SET_3, 0x0219 },
+ { RT1305_ADC_SET_1, 0x170f }, /* 0.2 ohm RSense*/
+
+};
+#define RT1305_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+struct rt1305_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+static const struct reg_default rt1305_reg[] = {
+
+ { 0x04, 0x0400 },
+ { 0x05, 0x0880 },
+ { 0x06, 0x0000 },
+ { 0x07, 0x3100 },
+ { 0x08, 0x8000 },
+ { 0x09, 0x0000 },
+ { 0x0a, 0x087e },
+ { 0x0b, 0x0020 },
+ { 0x0c, 0x0802 },
+ { 0x0d, 0x0020 },
+ { 0x10, 0x1d1d },
+ { 0x11, 0x1d1d },
+ { 0x12, 0xffff },
+ { 0x14, 0x000c },
+ { 0x16, 0x1717 },
+ { 0x17, 0x4000 },
+ { 0x18, 0x0019 },
+ { 0x20, 0x0000 },
+ { 0x22, 0x0000 },
+ { 0x24, 0x0000 },
+ { 0x26, 0x0000 },
+ { 0x28, 0x0000 },
+ { 0x2a, 0x4000 },
+ { 0x2b, 0x3000 },
+ { 0x2d, 0x6000 },
+ { 0x2e, 0x0000 },
+ { 0x2f, 0x8000 },
+ { 0x32, 0x0000 },
+ { 0x39, 0x0001 },
+ { 0x3a, 0x0000 },
+ { 0x3b, 0x1020 },
+ { 0x3c, 0x0000 },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x4c00 },
+ { 0x3f, 0x3000 },
+ { 0x40, 0x000c },
+ { 0x42, 0x0400 },
+ { 0x46, 0xc22c },
+ { 0x47, 0x0000 },
+ { 0x4b, 0x0000 },
+ { 0x4c, 0x0300 },
+ { 0x4f, 0xf000 },
+ { 0x50, 0xc200 },
+ { 0x51, 0x1f1f },
+ { 0x52, 0x01f0 },
+ { 0x53, 0x407f },
+ { 0x54, 0xffff },
+ { 0x58, 0x4005 },
+ { 0x5e, 0x0000 },
+ { 0x5f, 0x0000 },
+ { 0x60, 0xee13 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x5f5f },
+ { 0x64, 0x0040 },
+ { 0x65, 0x4000 },
+ { 0x66, 0x4004 },
+ { 0x67, 0x0306 },
+ { 0x68, 0x8c04 },
+ { 0x69, 0xe021 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0xaaaa },
+ { 0x70, 0x0333 },
+ { 0x71, 0x3330 },
+ { 0x72, 0x3333 },
+ { 0x73, 0x3300 },
+ { 0x74, 0x0000 },
+ { 0x75, 0x0000 },
+ { 0x76, 0x0000 },
+ { 0x7a, 0x0003 },
+ { 0x7c, 0x10ec },
+ { 0x7e, 0x6251 },
+ { 0x80, 0x0800 },
+ { 0x81, 0x4000 },
+ { 0x82, 0x0000 },
+ { 0x90, 0x7a01 },
+ { 0x91, 0x8431 },
+ { 0x92, 0x0180 },
+ { 0x93, 0x0000 },
+ { 0x94, 0x0000 },
+ { 0x95, 0x0000 },
+ { 0x96, 0x0000 },
+ { 0x97, 0x0000 },
+ { 0x98, 0x0000 },
+ { 0x99, 0x0000 },
+ { 0x9a, 0x0000 },
+ { 0x9b, 0x0000 },
+ { 0x9c, 0x0000 },
+ { 0x9d, 0x0000 },
+ { 0x9e, 0x0000 },
+ { 0x9f, 0x0000 },
+ { 0xa0, 0x0000 },
+ { 0xb0, 0x8200 },
+ { 0xb1, 0x00ff },
+ { 0xb2, 0x0008 },
+ { 0xc0, 0x0200 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xc3, 0x0000 },
+ { 0xc4, 0x0000 },
+ { 0xc5, 0x0000 },
+ { 0xc6, 0x0000 },
+ { 0xc7, 0x0000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0200 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x0000 },
+ { 0xd0, 0x0000 },
+ { 0xd1, 0x0000 },
+ { 0xd2, 0x0000 },
+ { 0xd3, 0x0000 },
+ { 0xd4, 0x0200 },
+ { 0xd5, 0x0000 },
+ { 0xd6, 0x0000 },
+ { 0xd7, 0x0000 },
+ { 0xd8, 0x0000 },
+ { 0xd9, 0x0000 },
+ { 0xda, 0x0000 },
+ { 0xdb, 0x0000 },
+ { 0xdc, 0x0000 },
+ { 0xdd, 0x0000 },
+ { 0xde, 0x0200 },
+ { 0xdf, 0x0000 },
+ { 0xe0, 0x0000 },
+ { 0xe1, 0x0000 },
+ { 0xe2, 0x0000 },
+ { 0xe3, 0x0000 },
+ { 0xe4, 0x0000 },
+ { 0xe5, 0x0000 },
+ { 0xe6, 0x0000 },
+ { 0xe7, 0x0000 },
+ { 0xe8, 0x0200 },
+ { 0xe9, 0x0000 },
+ { 0xea, 0x0000 },
+ { 0xeb, 0x0000 },
+ { 0xec, 0x0000 },
+ { 0xed, 0x0000 },
+ { 0xee, 0x0000 },
+ { 0xef, 0x0000 },
+ { 0xf0, 0x0000 },
+ { 0xf1, 0x0000 },
+ { 0xf2, 0x0200 },
+ { 0xf3, 0x0000 },
+ { 0xf4, 0x0000 },
+ { 0xf5, 0x0000 },
+ { 0xf6, 0x0000 },
+ { 0xf7, 0x0000 },
+ { 0xf8, 0x0000 },
+ { 0xf9, 0x0000 },
+ { 0xfa, 0x0000 },
+ { 0xfb, 0x0000 },
+};
+
+static int rt1305_reg_init(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ regmap_multi_reg_write(rt1305->regmap, init_list, RT1305_INIT_REG_LEN);
+ return 0;
+}
+
+static bool rt1305_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) {
+ if (reg >= rt1305_ranges[i].range_min &&
+ reg <= rt1305_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT1305_RESET:
+ case RT1305_SPDIF_IN_SET_1:
+ case RT1305_SPDIF_IN_SET_2:
+ case RT1305_SPDIF_IN_SET_3:
+ case RT1305_POWER_CTRL_2:
+ case RT1305_CLOCK_DETECT:
+ case RT1305_BIQUAD_SET_1:
+ case RT1305_BIQUAD_SET_2:
+ case RT1305_EQ_SET_2:
+ case RT1305_SPK_TEMP_PROTECTION_0:
+ case RT1305_SPK_TEMP_PROTECTION_2:
+ case RT1305_SPK_DC_DETECT_1:
+ case RT1305_SILENCE_DETECT:
+ case RT1305_VERSION_ID:
+ case RT1305_VENDOR_ID:
+ case RT1305_DEVICE_ID:
+ case RT1305_EFUSE_1:
+ case RT1305_EFUSE_3:
+ case RT1305_DC_CALIB_1:
+ case RT1305_DC_CALIB_3:
+ case RT1305_DAC_OFFSET_1:
+ case RT1305_DAC_OFFSET_2:
+ case RT1305_DAC_OFFSET_3:
+ case RT1305_DAC_OFFSET_4:
+ case RT1305_DAC_OFFSET_5:
+ case RT1305_DAC_OFFSET_6:
+ case RT1305_DAC_OFFSET_7:
+ case RT1305_DAC_OFFSET_8:
+ case RT1305_DAC_OFFSET_9:
+ case RT1305_DAC_OFFSET_10:
+ case RT1305_DAC_OFFSET_11:
+ case RT1305_TRIM_1:
+ case RT1305_TRIM_2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1305_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) {
+ if (reg >= rt1305_ranges[i].range_min &&
+ reg <= rt1305_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT1305_RESET:
+ case RT1305_CLK_1 ... RT1305_CAL_EFUSE_CLOCK:
+ case RT1305_PLL0_1 ... RT1305_PLL1_2:
+ case RT1305_MIXER_CTRL_1:
+ case RT1305_MIXER_CTRL_2:
+ case RT1305_DAC_SET_1:
+ case RT1305_DAC_SET_2:
+ case RT1305_ADC_SET_1:
+ case RT1305_ADC_SET_2:
+ case RT1305_ADC_SET_3:
+ case RT1305_PATH_SET:
+ case RT1305_SPDIF_IN_SET_1:
+ case RT1305_SPDIF_IN_SET_2:
+ case RT1305_SPDIF_IN_SET_3:
+ case RT1305_SPDIF_OUT_SET_1:
+ case RT1305_SPDIF_OUT_SET_2:
+ case RT1305_SPDIF_OUT_SET_3:
+ case RT1305_I2S_SET_1:
+ case RT1305_I2S_SET_2:
+ case RT1305_PBTL_MONO_MODE_SRC:
+ case RT1305_MANUALLY_I2C_DEVICE:
+ case RT1305_POWER_STATUS:
+ case RT1305_POWER_CTRL_1:
+ case RT1305_POWER_CTRL_2:
+ case RT1305_POWER_CTRL_3:
+ case RT1305_POWER_CTRL_4:
+ case RT1305_POWER_CTRL_5:
+ case RT1305_CLOCK_DETECT:
+ case RT1305_BIQUAD_SET_1:
+ case RT1305_BIQUAD_SET_2:
+ case RT1305_ADJUSTED_HPF_1:
+ case RT1305_ADJUSTED_HPF_2:
+ case RT1305_EQ_SET_1:
+ case RT1305_EQ_SET_2:
+ case RT1305_SPK_TEMP_PROTECTION_0:
+ case RT1305_SPK_TEMP_PROTECTION_1:
+ case RT1305_SPK_TEMP_PROTECTION_2:
+ case RT1305_SPK_TEMP_PROTECTION_3:
+ case RT1305_SPK_DC_DETECT_1:
+ case RT1305_SPK_DC_DETECT_2:
+ case RT1305_LOUDNESS:
+ case RT1305_THERMAL_FOLD_BACK_1:
+ case RT1305_THERMAL_FOLD_BACK_2:
+ case RT1305_SILENCE_DETECT ... RT1305_SPK_EXCURSION_LIMITER_7:
+ case RT1305_VERSION_ID:
+ case RT1305_VENDOR_ID:
+ case RT1305_DEVICE_ID:
+ case RT1305_EFUSE_1:
+ case RT1305_EFUSE_2:
+ case RT1305_EFUSE_3:
+ case RT1305_DC_CALIB_1:
+ case RT1305_DC_CALIB_2:
+ case RT1305_DC_CALIB_3:
+ case RT1305_DAC_OFFSET_1 ... RT1305_DAC_OFFSET_14:
+ case RT1305_TRIM_1:
+ case RT1305_TRIM_2:
+ case RT1305_TUNE_INTERNAL_OSC:
+ case RT1305_BIQUAD1_H0_L_28_16 ... RT1305_BIQUAD3_A2_R_15_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9435, 37, 0);
+
+static const char * const rt1305_rx_data_ch_select[] = {
+ "LR",
+ "RL",
+ "Copy L",
+ "Copy R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1305_rx_data_ch_enum, RT1305_I2S_SET_2, 2,
+ rt1305_rx_data_ch_select);
+
+static void rt1305_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT1305_RESET, 0);
+}
+
+static const struct snd_kcontrol_new rt1305_snd_controls[] = {
+ SOC_DOUBLE_TLV("DAC Playback Volume", RT1305_DAC_SET_1,
+ 8, 0, 0xff, 0, dac_vol_tlv),
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1305_rx_data_ch_enum),
+};
+
+static int rt1305_is_rc_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, RT1305_CLK_1);
+
+ if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1 &&
+ (val & RT1305_SEL_PLL_SRC_2_RCCLK))
+ return 1;
+ else
+ return 0;
+}
+
+static int rt1305_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt1305_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1,
+ RT1305_POW_PDB_JD_MASK, RT1305_POW_PDB_JD);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1,
+ RT1305_POW_PDB_JD_MASK, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1305_sto_dac_l =
+ SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2,
+ RT1305_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1305_sto_dac_r =
+ SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2,
+ RT1305_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1305_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL0", RT1305_POWER_CTRL_1,
+ RT1305_POW_PLL0_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT1305_POWER_CTRL_1,
+ RT1305_POW_PLL1_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT1305_POWER_CTRL_1,
+ RT1305_POW_MBIAS_LV_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1305_POWER_CTRL_1,
+ RT1305_POW_BG_MBIAS_LV_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO2", RT1305_POWER_CTRL_1,
+ RT1305_POW_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG2", RT1305_POWER_CTRL_1,
+ RT1305_POW_BG2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO2 IB2", RT1305_POWER_CTRL_1,
+ RT1305_POW_LDO2_IB2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1305_POWER_CTRL_1,
+ RT1305_POW_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF1", RT1305_POWER_CTRL_1,
+ RT1305_POW_VREF1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF2", RT1305_POWER_CTRL_1,
+ RT1305_POW_VREF2_BIT, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("DISC VREF", RT1305_POWER_CTRL_2,
+ RT1305_POW_DISC_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("FASTB VREF", RT1305_POWER_CTRL_2,
+ RT1305_POW_FASTB_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ULTRA FAST VREF", RT1305_POWER_CTRL_2,
+ RT1305_POW_ULTRA_FAST_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CHOP DAC", RT1305_POWER_CTRL_2,
+ RT1305_POW_CKXEN_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1305_POWER_CTRL_2,
+ RT1305_POW_EN_CKGEN_DAC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLAMP", RT1305_POWER_CTRL_2,
+ RT1305_POW_CLAMP_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BUFL", RT1305_POWER_CTRL_2,
+ RT1305_POW_BUFL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BUFR", RT1305_POWER_CTRL_2,
+ RT1305_POW_BUFR_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CKGEN ADC", RT1305_POWER_CTRL_2,
+ RT1305_POW_EN_CKGEN_ADC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3 L", RT1305_POWER_CTRL_2,
+ RT1305_POW_ADC3_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3 R", RT1305_POWER_CTRL_2,
+ RT1305_POW_ADC3_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TRIOSC", RT1305_POWER_CTRL_2,
+ RT1305_POW_TRIOSC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AVDD1", RT1305_POWER_CTRL_2,
+ RT1305_POR_AVDD1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AVDD2", RT1305_POWER_CTRL_2,
+ RT1305_POR_AVDD2_BIT, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("VSENSE R", RT1305_POWER_CTRL_3,
+ RT1305_POW_VSENSE_RCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VSENSE L", RT1305_POWER_CTRL_3,
+ RT1305_POW_VSENSE_LCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISENSE R", RT1305_POWER_CTRL_3,
+ RT1305_POW_ISENSE_RCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISENSE L", RT1305_POWER_CTRL_3,
+ RT1305_POW_ISENSE_LCH_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("POR AVDD1", RT1305_POWER_CTRL_3,
+ RT1305_POW_POR_AVDD1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("POR AVDD2", RT1305_POWER_CTRL_3,
+ RT1305_POW_POR_AVDD2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VCM 6172", RT1305_POWER_CTRL_3,
+ RT1305_EN_VCM_6172_BIT, 0, NULL, 0),
+
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC L Power", RT1305_POWER_CTRL_2,
+ RT1305_POW_DAC1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R Power", RT1305_POWER_CTRL_2,
+ RT1305_POW_DAC1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1305_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1305_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "PLL0", rt1305_is_rc_clk_from_pll },
+ { "DAC", NULL, "PLL1", rt1305_is_sys_clk_from_pll },
+
+ { "DAC", NULL, "MBIAS" },
+ { "DAC", NULL, "BG MBIAS" },
+ { "DAC", NULL, "LDO2" },
+ { "DAC", NULL, "BG2" },
+ { "DAC", NULL, "LDO2 IB2" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "VREF1" },
+ { "DAC", NULL, "VREF2" },
+
+ { "DAC", NULL, "DISC VREF" },
+ { "DAC", NULL, "FASTB VREF" },
+ { "DAC", NULL, "ULTRA FAST VREF" },
+ { "DAC", NULL, "CHOP DAC" },
+ { "DAC", NULL, "CKGEN DAC" },
+ { "DAC", NULL, "CLAMP" },
+ { "DAC", NULL, "CKGEN ADC" },
+ { "DAC", NULL, "TRIOSC" },
+ { "DAC", NULL, "AVDD1" },
+ { "DAC", NULL, "AVDD2" },
+
+ { "DAC", NULL, "POR AVDD1" },
+ { "DAC", NULL, "POR AVDD2" },
+ { "DAC", NULL, "VCM 6172" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+
+ { "DAC R", NULL, "VSENSE R" },
+ { "DAC L", NULL, "VSENSE L" },
+ { "DAC R", NULL, "ISENSE R" },
+ { "DAC L", NULL, "ISENSE L" },
+ { "DAC L", NULL, "ADC3 L" },
+ { "DAC R", NULL, "ADC3 R" },
+ { "DAC L", NULL, "BUFL" },
+ { "DAC R", NULL, "BUFR" },
+ { "DAC L", NULL, "DAC L Power" },
+ { "DAC R", NULL, "DAC R Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1305_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1305_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1305->lrck = params_rate(params);
+ pre_div = rt1305_get_clk_info(rt1305->sysclk, rt1305->lrck);
+ if (pre_div < 0) {
+ dev_warn(component->dev, "Force using PLL ");
+ snd_soc_dai_set_pll(dai, 0, RT1305_PLL1_S_BCLK,
+ rt1305->lrck * 64, rt1305->lrck * 256);
+ snd_soc_dai_set_sysclk(dai, RT1305_FS_SYS_PRE_S_PLL1,
+ rt1305->lrck * 256, SND_SOC_CLOCK_IN);
+ pre_div = 0;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1305->bclk = rt1305->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1305->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1305_I2S_DL_SEL_16B;
+ break;
+ case 20:
+ val_len |= RT1305_I2S_DL_SEL_20B;
+ break;
+ case 24:
+ val_len |= RT1305_I2S_DL_SEL_24B;
+ break;
+ case 8:
+ val_len |= RT1305_I2S_DL_SEL_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1305_AIF1:
+ mask_clk = RT1305_DIV_FS_SYS_MASK;
+ val_clk = pre_div << RT1305_DIV_FS_SYS_SFT;
+ snd_soc_component_update_bits(component, RT1305_I2S_SET_2,
+ RT1305_I2S_DL_SEL_MASK,
+ val_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1305_CLK_2,
+ mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1305_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ reg_val |= RT1305_SEL_I2S_OUT_MODE_M;
+ rt1305->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT1305_SEL_I2S_OUT_MODE_S;
+ rt1305->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1_val |= RT1305_I2S_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg1_val |= RT1305_I2S_DF_SEL_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg1_val |= RT1305_I2S_DF_SEL_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg1_val |= RT1305_I2S_DF_SEL_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1305_AIF1:
+ snd_soc_component_update_bits(component, RT1305_I2S_SET_1,
+ RT1305_SEL_I2S_OUT_MODE_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT1305_I2S_SET_2,
+ RT1305_I2S_DF_SEL_MASK | RT1305_I2S_BCLK_MASK,
+ reg1_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt1305_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1305->sysclk && clk_id == rt1305->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1305_FS_SYS_PRE_S_MCLK:
+ reg_val |= RT1305_SEL_FS_SYS_PRE_MCLK;
+ snd_soc_component_update_bits(component,
+ RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK,
+ RT1305_SEL_CLK_DET_SRC_MCLK);
+ break;
+ case RT1305_FS_SYS_PRE_S_PLL1:
+ reg_val |= RT1305_SEL_FS_SYS_PRE_PLL;
+ break;
+ case RT1305_FS_SYS_PRE_S_RCCLK:
+ reg_val |= RT1305_SEL_FS_SYS_PRE_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_FS_SYS_PRE_MASK, reg_val);
+ rt1305->sysclk = freq;
+ rt1305->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1305_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1305->pll_src && freq_in == rt1305->pll_in &&
+ freq_out == rt1305->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1305->pll_in = 0;
+ rt1305->pll_out = 0;
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_FS_SYS_PRE_MASK | RT1305_SEL_PLL_SRC_1_MASK,
+ RT1305_SEL_FS_SYS_PRE_PLL | RT1305_SEL_PLL_SRC_1_BCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1305_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK |
+ RT1305_DIV_PLL_SRC_2_MASK,
+ RT1305_SEL_PLL_SRC_2_MCLK | RT1305_SEL_PLL_SRC_1_PLL2);
+ snd_soc_component_update_bits(component,
+ RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK,
+ RT1305_SEL_CLK_DET_SRC_MCLK);
+ break;
+ case RT1305_PLL1_S_BCLK:
+ snd_soc_component_update_bits(component,
+ RT1305_CLK_1, RT1305_SEL_PLL_SRC_1_MASK,
+ RT1305_SEL_PLL_SRC_1_BCLK);
+ break;
+ case RT1305_PLL2_S_RCCLK:
+ snd_soc_component_update_bits(component, RT1305_CLK_1,
+ RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK |
+ RT1305_DIV_PLL_SRC_2_MASK,
+ RT1305_SEL_PLL_SRC_2_RCCLK | RT1305_SEL_PLL_SRC_1_PLL2);
+ freq_in = 98304000;
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1305_PLL1_1,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1305_PLL_1_M_SFT) |
+ (pll_code.m_bp << RT1305_PLL_1_M_BYPASS_SFT) |
+ pll_code.n_code);
+ snd_soc_component_write(component, RT1305_PLL1_2,
+ pll_code.k_code);
+
+ rt1305->pll_in = freq_in;
+ rt1305->pll_out = freq_out;
+ rt1305->pll_src = source;
+
+ return 0;
+}
+
+static int rt1305_probe(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ rt1305->component = component;
+
+ /* initial settings */
+ rt1305_reg_init(component);
+
+ return 0;
+}
+
+static void rt1305_remove(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ rt1305_reset(rt1305->regmap);
+}
+
+#ifdef CONFIG_PM
+static int rt1305_suspend(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1305->regmap, true);
+ regcache_mark_dirty(rt1305->regmap);
+
+ return 0;
+}
+
+static int rt1305_resume(struct snd_soc_component *component)
+{
+ struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1305->regmap, false);
+ regcache_sync(rt1305->regmap);
+
+ return 0;
+}
+#else
+#define rt1305_suspend NULL
+#define rt1305_resume NULL
+#endif
+
+#define RT1305_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1305_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt1305_aif_dai_ops = {
+ .hw_params = rt1305_hw_params,
+ .set_fmt = rt1305_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1305_dai[] = {
+ {
+ .name = "rt1305-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1305_STEREO_RATES,
+ .formats = RT1305_FORMATS,
+ },
+ .ops = &rt1305_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1305 = {
+ .probe = rt1305_probe,
+ .remove = rt1305_remove,
+ .suspend = rt1305_suspend,
+ .resume = rt1305_resume,
+ .controls = rt1305_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1305_snd_controls),
+ .dapm_widgets = rt1305_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1305_dapm_widgets),
+ .dapm_routes = rt1305_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1305_dapm_routes),
+ .set_sysclk = rt1305_set_component_sysclk,
+ .set_pll = rt1305_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1305_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = RT1305_MAX_REG + 1 + (ARRAY_SIZE(rt1305_ranges) *
+ RT1305_PR_SPACING),
+ .volatile_reg = rt1305_volatile_register,
+ .readable_reg = rt1305_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt1305_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1305_reg),
+ .ranges = rt1305_ranges,
+ .num_ranges = ARRAY_SIZE(rt1305_ranges),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt1305_of_match[] = {
+ { .compatible = "realtek,rt1305", },
+ { .compatible = "realtek,rt1306", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1305_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1305_acpi_match[] = {
+ { "10EC1305" },
+ { "10EC1306" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1305_i2c_id[] = {
+ { "rt1305" },
+ { "rt1306" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1305_i2c_id);
+
+static void rt1305_calibrate(struct rt1305_priv *rt1305)
+{
+ unsigned int valmsb, vallsb, offsetl, offsetr;
+ unsigned int rh, rl, rhl, r0ohm;
+ u64 r0l, r0r;
+
+ regcache_cache_bypass(rt1305->regmap, true);
+
+ rt1305_reset(rt1305->regmap);
+ regmap_write(rt1305->regmap, RT1305_ADC_SET_3, 0x0219);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcf, 0x5548);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320);
+ regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x1000);
+ regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0600);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xffd0);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe);
+
+ /* Sin Gen */
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442);
+
+ regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0xb000);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc3, 0xd4a0);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcc, 0x00cc);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320);
+ regmap_write(rt1305->regmap, RT1305_POWER_STATUS, 0x0000);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x00c0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0);
+
+ /* EFUSE read */
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x0000);
+ regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0000);
+
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_5, &valmsb);
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_6, &vallsb);
+ offsetl = valmsb << 16 | vallsb;
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_7, &valmsb);
+ regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_8, &vallsb);
+ offsetr = valmsb << 16 | vallsb;
+ pr_info("DC offsetl=0x%x, offsetr=0x%x\n", offsetl, offsetr);
+
+ /* R0 calibration */
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x9542);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x1dfe);
+ regmap_write(rt1305->regmap, RT1305_SILENCE_DETECT, 0x0e13);
+ regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0650);
+
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x50, 0x0064);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x51, 0x0770);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x52, 0xc30c);
+ regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x8200);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80);
+ msleep(2000);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl);
+ rhl = (rh << 16) | rl;
+ r0ohm = (rhl*10) / 33554432;
+
+ pr_debug("Left_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl);
+ pr_info("Left channel %d.%dohm\n", (r0ohm/10), (r0ohm%10));
+
+ r0l = 562949953421312ULL;
+ if (rhl != 0)
+ do_div(r0l, rhl);
+ pr_debug("Left_r0 = 0x%llx\n", r0l);
+
+ regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x9200);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80);
+ msleep(2000);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh);
+ regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl);
+ rhl = (rh << 16) | rl;
+ r0ohm = (rhl*10) / 33554432;
+
+ pr_debug("Right_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl);
+ pr_info("Right channel %d.%dohm\n", (r0ohm/10), (r0ohm%10));
+
+ r0r = 562949953421312ULL;
+ if (rhl != 0)
+ do_div(r0r, rhl);
+ pr_debug("Right_r0 = 0x%llx\n", r0r);
+
+ regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0xc2ec);
+
+ if ((r0l > R0_UPPER) && (r0l < R0_LOWER) &&
+ (r0r > R0_UPPER) && (r0r < R0_LOWER)) {
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4e,
+ (r0l >> 16) & 0xffff);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4f,
+ r0l & 0xffff);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfe,
+ ((r0r >> 16) & 0xffff) | 0xf800);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfd,
+ r0r & 0xffff);
+ } else {
+ pr_err("R0 calibration failed\n");
+ }
+
+ /* restore some registers */
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe);
+ usleep_range(200000, 400000);
+ regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442);
+ regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x3000);
+ regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0400);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0000);
+ regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0x8000);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0x1020);
+ regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0x0000);
+
+ regcache_cache_bypass(rt1305->regmap, false);
+}
+
+static int rt1305_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1305_priv *rt1305;
+ int ret;
+ unsigned int val;
+
+ rt1305 = devm_kzalloc(&i2c->dev, sizeof(struct rt1305_priv),
+ GFP_KERNEL);
+ if (rt1305 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1305);
+
+ rt1305->regmap = devm_regmap_init_i2c(i2c, &rt1305_regmap);
+ if (IS_ERR(rt1305->regmap)) {
+ ret = PTR_ERR(rt1305->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1305->regmap, RT1305_DEVICE_ID, &val);
+ if (val != RT1305_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1305\n", val);
+ return -ENODEV;
+ }
+
+ rt1305_reset(rt1305->regmap);
+ rt1305_calibrate(rt1305);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1305,
+ rt1305_dai, ARRAY_SIZE(rt1305_dai));
+}
+
+static void rt1305_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1305_priv *rt1305 = i2c_get_clientdata(client);
+
+ rt1305_reset(rt1305->regmap);
+}
+
+
+static struct i2c_driver rt1305_i2c_driver = {
+ .driver = {
+ .name = "rt1305",
+#if defined(CONFIG_OF)
+ .of_match_table = rt1305_of_match,
+#endif
+#if defined(CONFIG_ACPI)
+ .acpi_match_table = ACPI_PTR(rt1305_acpi_match)
+#endif
+ },
+ .probe = rt1305_i2c_probe,
+ .shutdown = rt1305_i2c_shutdown,
+ .id_table = rt1305_i2c_id,
+};
+module_i2c_driver(rt1305_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1305 amplifier driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1305.h b/sound/soc/codecs/rt1305.h
new file mode 100644
index 000000000000..026f74eb6815
--- /dev/null
+++ b/sound/soc/codecs/rt1305.h
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * RT1305.h -- RT1305 ALSA SoC amplifier component driver
+ *
+ * Copyright 2018 Realtek Semiconductor Corp.
+ * Author: Shuming Fan <shumingf@realtek.com>
+ */
+
+#ifndef _RT1305_H_
+#define _RT1305_H_
+
+#define RT1305_DEVICE_ID_NUM 0x6251
+
+#define RT1305_RESET 0x00
+#define RT1305_CLK_1 0x04
+#define RT1305_CLK_2 0x05
+#define RT1305_CLK_3 0x06
+#define RT1305_DFLL_REG 0x07
+#define RT1305_CAL_EFUSE_CLOCK 0x08
+#define RT1305_PLL0_1 0x0a
+#define RT1305_PLL0_2 0x0b
+#define RT1305_PLL1_1 0x0c
+#define RT1305_PLL1_2 0x0d
+#define RT1305_MIXER_CTRL_1 0x10
+#define RT1305_MIXER_CTRL_2 0x11
+#define RT1305_DAC_SET_1 0x12
+#define RT1305_DAC_SET_2 0x14
+#define RT1305_ADC_SET_1 0x16
+#define RT1305_ADC_SET_2 0x17
+#define RT1305_ADC_SET_3 0x18
+#define RT1305_PATH_SET 0x20
+#define RT1305_SPDIF_IN_SET_1 0x22
+#define RT1305_SPDIF_IN_SET_2 0x24
+#define RT1305_SPDIF_IN_SET_3 0x26
+#define RT1305_SPDIF_OUT_SET_1 0x28
+#define RT1305_SPDIF_OUT_SET_2 0x2a
+#define RT1305_SPDIF_OUT_SET_3 0x2b
+#define RT1305_I2S_SET_1 0x2d
+#define RT1305_I2S_SET_2 0x2e
+#define RT1305_PBTL_MONO_MODE_SRC 0x2f
+#define RT1305_MANUALLY_I2C_DEVICE 0x32
+#define RT1305_POWER_STATUS 0x39
+#define RT1305_POWER_CTRL_1 0x3a
+#define RT1305_POWER_CTRL_2 0x3b
+#define RT1305_POWER_CTRL_3 0x3c
+#define RT1305_POWER_CTRL_4 0x3d
+#define RT1305_POWER_CTRL_5 0x3e
+#define RT1305_CLOCK_DETECT 0x3f
+#define RT1305_BIQUAD_SET_1 0x40
+#define RT1305_BIQUAD_SET_2 0x42
+#define RT1305_ADJUSTED_HPF_1 0x46
+#define RT1305_ADJUSTED_HPF_2 0x47
+#define RT1305_EQ_SET_1 0x4b
+#define RT1305_EQ_SET_2 0x4c
+#define RT1305_SPK_TEMP_PROTECTION_0 0x4f
+#define RT1305_SPK_TEMP_PROTECTION_1 0x50
+#define RT1305_SPK_TEMP_PROTECTION_2 0x51
+#define RT1305_SPK_TEMP_PROTECTION_3 0x52
+#define RT1305_SPK_DC_DETECT_1 0x53
+#define RT1305_SPK_DC_DETECT_2 0x54
+#define RT1305_LOUDNESS 0x58
+#define RT1305_THERMAL_FOLD_BACK_1 0x5e
+#define RT1305_THERMAL_FOLD_BACK_2 0x5f
+#define RT1305_SILENCE_DETECT 0x60
+#define RT1305_ALC_DRC_1 0x62
+#define RT1305_ALC_DRC_2 0x63
+#define RT1305_ALC_DRC_3 0x64
+#define RT1305_ALC_DRC_4 0x65
+#define RT1305_PRIV_INDEX 0x6a
+#define RT1305_PRIV_DATA 0x6c
+#define RT1305_SPK_EXCURSION_LIMITER_7 0x76
+#define RT1305_VERSION_ID 0x7a
+#define RT1305_VENDOR_ID 0x7c
+#define RT1305_DEVICE_ID 0x7e
+#define RT1305_EFUSE_1 0x80
+#define RT1305_EFUSE_2 0x81
+#define RT1305_EFUSE_3 0x82
+#define RT1305_DC_CALIB_1 0x90
+#define RT1305_DC_CALIB_2 0x91
+#define RT1305_DC_CALIB_3 0x92
+#define RT1305_DAC_OFFSET_1 0x93
+#define RT1305_DAC_OFFSET_2 0x94
+#define RT1305_DAC_OFFSET_3 0x95
+#define RT1305_DAC_OFFSET_4 0x96
+#define RT1305_DAC_OFFSET_5 0x97
+#define RT1305_DAC_OFFSET_6 0x98
+#define RT1305_DAC_OFFSET_7 0x99
+#define RT1305_DAC_OFFSET_8 0x9a
+#define RT1305_DAC_OFFSET_9 0x9b
+#define RT1305_DAC_OFFSET_10 0x9c
+#define RT1305_DAC_OFFSET_11 0x9d
+#define RT1305_DAC_OFFSET_12 0x9e
+#define RT1305_DAC_OFFSET_13 0x9f
+#define RT1305_DAC_OFFSET_14 0xa0
+#define RT1305_TRIM_1 0xb0
+#define RT1305_TRIM_2 0xb1
+#define RT1305_TUNE_INTERNAL_OSC 0xb2
+#define RT1305_BIQUAD1_H0_L_28_16 0xc0
+#define RT1305_BIQUAD3_A2_R_15_0 0xfb
+#define RT1305_MAX_REG 0xff
+
+/* CLOCK-1 (0x04) */
+#define RT1305_SEL_PLL_SRC_2_MASK (0x1 << 15)
+#define RT1305_SEL_PLL_SRC_2_SFT 15
+#define RT1305_SEL_PLL_SRC_2_MCLK (0x0 << 15)
+#define RT1305_SEL_PLL_SRC_2_RCCLK (0x1 << 15)
+#define RT1305_DIV_PLL_SRC_2_MASK (0x3 << 13)
+#define RT1305_DIV_PLL_SRC_2_SFT 13
+#define RT1305_SEL_PLL_SRC_1_MASK (0x3 << 10)
+#define RT1305_SEL_PLL_SRC_1_SFT 10
+#define RT1305_SEL_PLL_SRC_1_PLL2 (0x0 << 10)
+#define RT1305_SEL_PLL_SRC_1_BCLK (0x1 << 10)
+#define RT1305_SEL_PLL_SRC_1_DFLL (0x2 << 10)
+#define RT1305_SEL_FS_SYS_PRE_MASK (0x3 << 8)
+#define RT1305_SEL_FS_SYS_PRE_SFT 8
+#define RT1305_SEL_FS_SYS_PRE_MCLK (0x0 << 8)
+#define RT1305_SEL_FS_SYS_PRE_PLL (0x1 << 8)
+#define RT1305_SEL_FS_SYS_PRE_RCCLK (0x2 << 8)
+#define RT1305_DIV_FS_SYS_MASK (0x7 << 4)
+#define RT1305_DIV_FS_SYS_SFT 4
+
+/* PLL1M/N/K Code-1 (0x0c) */
+#define RT1305_PLL_1_M_SFT 12
+#define RT1305_PLL_1_M_BYPASS_MASK (0x1 << 11)
+#define RT1305_PLL_1_M_BYPASS_SFT 11
+#define RT1305_PLL_1_M_BYPASS (0x1 << 11)
+#define RT1305_PLL_1_N_MASK (0x1ff << 0)
+
+/* DAC Setting (0x14) */
+#define RT1305_DVOL_MUTE_L_EN_SFT 15
+#define RT1305_DVOL_MUTE_R_EN_SFT 14
+
+/* I2S Setting-1 (0x2d) */
+#define RT1305_SEL_I2S_OUT_MODE_MASK (0x1 << 15)
+#define RT1305_SEL_I2S_OUT_MODE_SFT 15
+#define RT1305_SEL_I2S_OUT_MODE_S (0x0 << 15)
+#define RT1305_SEL_I2S_OUT_MODE_M (0x1 << 15)
+
+/* I2S Setting-2 (0x2e) */
+#define RT1305_I2S_DF_SEL_MASK (0x3 << 12)
+#define RT1305_I2S_DF_SEL_SFT 12
+#define RT1305_I2S_DF_SEL_I2S (0x0 << 12)
+#define RT1305_I2S_DF_SEL_LEFT (0x1 << 12)
+#define RT1305_I2S_DF_SEL_PCM_A (0x2 << 12)
+#define RT1305_I2S_DF_SEL_PCM_B (0x3 << 12)
+#define RT1305_I2S_DL_SEL_MASK (0x3 << 10)
+#define RT1305_I2S_DL_SEL_SFT 10
+#define RT1305_I2S_DL_SEL_16B (0x0 << 10)
+#define RT1305_I2S_DL_SEL_20B (0x1 << 10)
+#define RT1305_I2S_DL_SEL_24B (0x2 << 10)
+#define RT1305_I2S_DL_SEL_8B (0x3 << 10)
+#define RT1305_I2S_BCLK_MASK (0x1 << 9)
+#define RT1305_I2S_BCLK_SFT 9
+#define RT1305_I2S_BCLK_NORMAL (0x0 << 9)
+#define RT1305_I2S_BCLK_INV (0x1 << 9)
+
+/* Power Control-1 (0x3a) */
+#define RT1305_POW_PDB_JD_MASK (0x1 << 12)
+#define RT1305_POW_PDB_JD (0x1 << 12)
+#define RT1305_POW_PDB_JD_BIT 12
+#define RT1305_POW_PLL0_EN (0x1 << 11)
+#define RT1305_POW_PLL0_EN_BIT 11
+#define RT1305_POW_PLL1_EN (0x1 << 10)
+#define RT1305_POW_PLL1_EN_BIT 10
+#define RT1305_POW_PDB_JD_POLARITY (0x1 << 9)
+#define RT1305_POW_PDB_JD_POLARITY_BIT 9
+#define RT1305_POW_MBIAS_LV (0x1 << 8)
+#define RT1305_POW_MBIAS_LV_BIT 8
+#define RT1305_POW_BG_MBIAS_LV (0x1 << 7)
+#define RT1305_POW_BG_MBIAS_LV_BIT 7
+#define RT1305_POW_LDO2 (0x1 << 6)
+#define RT1305_POW_LDO2_BIT 6
+#define RT1305_POW_BG2 (0x1 << 5)
+#define RT1305_POW_BG2_BIT 5
+#define RT1305_POW_LDO2_IB2 (0x1 << 4)
+#define RT1305_POW_LDO2_IB2_BIT 4
+#define RT1305_POW_VREF (0x1 << 3)
+#define RT1305_POW_VREF_BIT 3
+#define RT1305_POW_VREF1 (0x1 << 2)
+#define RT1305_POW_VREF1_BIT 2
+#define RT1305_POW_VREF2 (0x1 << 1)
+#define RT1305_POW_VREF2_BIT 1
+
+/* Power Control-2 (0x3b) */
+#define RT1305_POW_DISC_VREF (1 << 15)
+#define RT1305_POW_DISC_VREF_BIT 15
+#define RT1305_POW_FASTB_VREF (1 << 14)
+#define RT1305_POW_FASTB_VREF_BIT 14
+#define RT1305_POW_ULTRA_FAST_VREF (1 << 13)
+#define RT1305_POW_ULTRA_FAST_VREF_BIT 13
+#define RT1305_POW_CKXEN_DAC (1 << 12)
+#define RT1305_POW_CKXEN_DAC_BIT 12
+#define RT1305_POW_EN_CKGEN_DAC (1 << 11)
+#define RT1305_POW_EN_CKGEN_DAC_BIT 11
+#define RT1305_POW_DAC1_L (1 << 10)
+#define RT1305_POW_DAC1_L_BIT 10
+#define RT1305_POW_DAC1_R (1 << 9)
+#define RT1305_POW_DAC1_R_BIT 9
+#define RT1305_POW_CLAMP (1 << 8)
+#define RT1305_POW_CLAMP_BIT 8
+#define RT1305_POW_BUFL (1 << 7)
+#define RT1305_POW_BUFL_BIT 7
+#define RT1305_POW_BUFR (1 << 6)
+#define RT1305_POW_BUFR_BIT 6
+#define RT1305_POW_EN_CKGEN_ADC (1 << 5)
+#define RT1305_POW_EN_CKGEN_ADC_BIT 5
+#define RT1305_POW_ADC3_L (1 << 4)
+#define RT1305_POW_ADC3_L_BIT 4
+#define RT1305_POW_ADC3_R (1 << 3)
+#define RT1305_POW_ADC3_R_BIT 3
+#define RT1305_POW_TRIOSC (1 << 2)
+#define RT1305_POW_TRIOSC_BIT 2
+#define RT1305_POR_AVDD1 (1 << 1)
+#define RT1305_POR_AVDD1_BIT 1
+#define RT1305_POR_AVDD2 (1 << 0)
+#define RT1305_POR_AVDD2_BIT 0
+
+/* Power Control-3 (0x3c) */
+#define RT1305_POW_VSENSE_RCH (1 << 15)
+#define RT1305_POW_VSENSE_RCH_BIT 15
+#define RT1305_POW_VSENSE_LCH (1 << 14)
+#define RT1305_POW_VSENSE_LCH_BIT 14
+#define RT1305_POW_ISENSE_RCH (1 << 13)
+#define RT1305_POW_ISENSE_RCH_BIT 13
+#define RT1305_POW_ISENSE_LCH (1 << 12)
+#define RT1305_POW_ISENSE_LCH_BIT 12
+#define RT1305_POW_POR_AVDD1 (1 << 11)
+#define RT1305_POW_POR_AVDD1_BIT 11
+#define RT1305_POW_POR_AVDD2 (1 << 10)
+#define RT1305_POW_POR_AVDD2_BIT 10
+#define RT1305_EN_K_HV (1 << 9)
+#define RT1305_EN_K_HV_BIT 9
+#define RT1305_EN_PRE_K_HV (1 << 8)
+#define RT1305_EN_PRE_K_HV_BIT 8
+#define RT1305_EN_EFUSE_1P8V (1 << 7)
+#define RT1305_EN_EFUSE_1P8V_BIT 7
+#define RT1305_EN_EFUSE_5V (1 << 6)
+#define RT1305_EN_EFUSE_5V_BIT 6
+#define RT1305_EN_VCM_6172 (1 << 5)
+#define RT1305_EN_VCM_6172_BIT 5
+#define RT1305_POR_EFUSE (1 << 4)
+#define RT1305_POR_EFUSE_BIT 4
+
+/* Clock Detect (0x3f) */
+#define RT1305_SEL_CLK_DET_SRC_MASK (0x1 << 12)
+#define RT1305_SEL_CLK_DET_SRC_SFT 12
+#define RT1305_SEL_CLK_DET_SRC_MCLK (0x0 << 12)
+#define RT1305_SEL_CLK_DET_SRC_BCLK (0x1 << 12)
+
+
+/* System Clock Source */
+enum {
+ RT1305_FS_SYS_PRE_S_MCLK,
+ RT1305_FS_SYS_PRE_S_PLL1,
+ RT1305_FS_SYS_PRE_S_RCCLK, /* 98.304M Hz */
+};
+
+/* PLL Source 1/2 */
+enum {
+ RT1305_PLL1_S_BCLK,
+ RT1305_PLL2_S_MCLK,
+ RT1305_PLL2_S_RCCLK, /* 98.304M Hz */
+};
+
+enum {
+ RT1305_AIF1,
+ RT1305_AIFS
+};
+
+#define R0_UPPER 0x2E8BA2 //5.5 ohm
+#define R0_LOWER 0x666666 //2.5 ohm
+
+#endif /* end of _RT1305_H_ */
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
new file mode 100644
index 000000000000..b6c224832a43
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1308-sdw.c -- rt1308 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "rt1308.h"
+#include "rt1308-sdw.h"
+
+static bool rt1308_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x2f01 ... 0x2f07:
+ case 0x3000 ... 0x3001:
+ case 0x3004 ... 0x3005:
+ case 0x3008:
+ case 0x300a:
+ case 0xc000 ... 0xcff3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1308_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f07:
+ case 0x3000 ... 0x3001:
+ case 0x3004 ... 0x3005:
+ case 0x3008:
+ case 0x300a:
+ case 0xc000:
+ case 0xc710:
+ case 0xcf01:
+ case 0xc860 ... 0xc863:
+ case 0xc870 ... 0xc873:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1308_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1308_readable_register,
+ .volatile_reg = rt1308_volatile_register,
+ .max_register = 0xcfff,
+ .reg_defaults = rt1308_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+/* Bus clock frequency */
+#define RT1308_CLK_FREQ_9600000HZ 9600000
+#define RT1308_CLK_FREQ_12000000HZ 12000000
+#define RT1308_CLK_FREQ_6000000HZ 6000000
+#define RT1308_CLK_FREQ_4800000HZ 4800000
+#define RT1308_CLK_FREQ_2400000HZ 2400000
+#define RT1308_CLK_FREQ_12288000HZ 12288000
+
+static int rt1308_clock_config(struct device *dev)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt1308->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT1308_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT1308_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT1308_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT1308_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT1308_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT1308_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt1308->regmap, 0xe0, value);
+ regmap_write(rt1308->regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+static int rt1308_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */
+ prop->sink_ports = 0x2; /* BITMAP: 00000010 */
+
+ /* for sink */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt1308_apply_calib_params(struct rt1308_sdw_priv *rt1308)
+{
+ unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp;
+ unsigned int efuse_c_btl_l, efuse_c_btl_r;
+
+ /* read efuse to apply calibration parameters */
+ regmap_write(rt1308->regmap, 0xc7f0, 0x04);
+ regmap_write(rt1308->regmap, 0xc7f1, 0xfe);
+ msleep(100);
+ regmap_write(rt1308->regmap, 0xc7f0, 0x44);
+ msleep(20);
+ regmap_write(rt1308->regmap, 0xc240, 0x10);
+
+ regmap_read(rt1308->regmap, 0xc861, &tmp);
+ efuse_m_btl_l = tmp;
+ regmap_read(rt1308->regmap, 0xc860, &tmp);
+ efuse_m_btl_l = efuse_m_btl_l | (tmp << 8);
+ regmap_read(rt1308->regmap, 0xc863, &tmp);
+ efuse_c_btl_l = tmp;
+ regmap_read(rt1308->regmap, 0xc862, &tmp);
+ efuse_c_btl_l = efuse_c_btl_l | (tmp << 8);
+ regmap_read(rt1308->regmap, 0xc871, &tmp);
+ efuse_m_btl_r = tmp;
+ regmap_read(rt1308->regmap, 0xc870, &tmp);
+ efuse_m_btl_r = efuse_m_btl_r | (tmp << 8);
+ regmap_read(rt1308->regmap, 0xc873, &tmp);
+ efuse_c_btl_r = tmp;
+ regmap_read(rt1308->regmap, 0xc872, &tmp);
+ efuse_c_btl_r = efuse_c_btl_r | (tmp << 8);
+ dev_dbg(&rt1308->sdw_slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__,
+ efuse_m_btl_l, efuse_m_btl_r);
+ dev_dbg(&rt1308->sdw_slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__,
+ efuse_c_btl_l, efuse_c_btl_r);
+}
+
+static void rt1308_apply_bq_params(struct rt1308_sdw_priv *rt1308)
+{
+ unsigned int i, reg, data;
+
+ for (i = 0; i < rt1308->bq_params_cnt; i += 3) {
+ reg = rt1308->bq_params[i] | (rt1308->bq_params[i + 1] << 8);
+ data = rt1308->bq_params[i + 2];
+ regmap_write(rt1308->regmap, reg, data);
+ }
+}
+
+static int rt1308_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned int tmp, hibernation_flag;
+
+ if (rt1308->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1308->regmap, false);
+ if (rt1308->first_hw_init)
+ regcache_cache_bypass(rt1308->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt1308->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag);
+ if ((hibernation_flag != 0x00) && rt1308->first_hw_init)
+ goto _preset_ready_;
+
+ /* sw reset */
+ regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0);
+
+ regmap_read(rt1308->regmap, 0xc710, &tmp);
+ rt1308->hw_ver = tmp;
+ dev_dbg(dev, "%s, hw_ver=0x%x\n", __func__, rt1308->hw_ver);
+
+ /* initial settings */
+ regmap_write(rt1308->regmap, 0xc103, 0xc0);
+ regmap_write(rt1308->regmap, 0xc030, 0x17);
+ regmap_write(rt1308->regmap, 0xc031, 0x81);
+ regmap_write(rt1308->regmap, 0xc032, 0x26);
+ regmap_write(rt1308->regmap, 0xc040, 0x80);
+ regmap_write(rt1308->regmap, 0xc041, 0x80);
+ regmap_write(rt1308->regmap, 0xc042, 0x06);
+ regmap_write(rt1308->regmap, 0xc052, 0x0a);
+ regmap_write(rt1308->regmap, 0xc080, 0x0a);
+ regmap_write(rt1308->regmap, 0xc060, 0x02);
+ regmap_write(rt1308->regmap, 0xc061, 0x75);
+ regmap_write(rt1308->regmap, 0xc062, 0x05);
+ regmap_write(rt1308->regmap, 0xc171, 0x07);
+ regmap_write(rt1308->regmap, 0xc173, 0x0d);
+ if (rt1308->hw_ver == RT1308_VER_C) {
+ regmap_write(rt1308->regmap, 0xc311, 0x7f);
+ regmap_write(rt1308->regmap, 0xc300, 0x09);
+ } else {
+ regmap_write(rt1308->regmap, 0xc311, 0x4f);
+ regmap_write(rt1308->regmap, 0xc300, 0x0b);
+ }
+ regmap_write(rt1308->regmap, 0xc900, 0x5a);
+ regmap_write(rt1308->regmap, 0xc1a0, 0x84);
+ regmap_write(rt1308->regmap, 0xc1a1, 0x01);
+ regmap_write(rt1308->regmap, 0xc360, 0x78);
+ regmap_write(rt1308->regmap, 0xc361, 0x87);
+ regmap_write(rt1308->regmap, 0xc0a1, 0x71);
+ regmap_write(rt1308->regmap, 0xc210, 0x00);
+ regmap_write(rt1308->regmap, 0xc070, 0x00);
+ regmap_write(rt1308->regmap, 0xc100, 0xd7);
+ regmap_write(rt1308->regmap, 0xc101, 0xd7);
+
+ /* apply BQ params */
+ rt1308_apply_bq_params(rt1308);
+
+ regmap_write(rt1308->regmap, 0xcf01, 0x01);
+
+_preset_ready_:
+ if (rt1308->first_hw_init) {
+ regcache_cache_bypass(rt1308->regmap, false);
+ regcache_mark_dirty(rt1308->regmap);
+ } else
+ rt1308->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1308->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+ return ret;
+}
+
+static int rt1308_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1308->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1308_io_init(&slave->dev, slave);
+}
+
+static int rt1308_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt1308->params, params, sizeof(*params));
+
+ ret = rt1308_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "Invalid clk config");
+
+ return ret;
+}
+
+static int rt1308_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ return 0;
+}
+
+static int rt1308_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(30);
+ snd_soc_component_update_bits(component,
+ RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4),
+ 0x3, 0x3);
+ msleep(40);
+ rt1308_apply_calib_params(rt1308);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component,
+ RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4),
+ 0x3, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1308_rx_data_ch_select[] = {
+ "LR",
+ "LL",
+ "RL",
+ "RR",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum,
+ RT1308_SDW_OFFSET | (RT1308_DATA_PATH << 4), 0,
+ rt1308_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1308_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1308_sto_dac_l =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4),
+ RT1308_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1308_sto_dac_r =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4),
+ RT1308_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Supply Widgets */
+ SND_SOC_DAPM_SUPPLY("MBIAS20U",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALDO",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DBG",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DACL",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK25M",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_R",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_L",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Power",
+ RT1308_SDW_OFFSET | (RT1308_POWER << 4), 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DLDO",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_R",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_L",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS4U",
+ RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL2_LDO",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F2",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B2",
+ RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1308_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "MBIAS20U" },
+ { "DAC", NULL, "ALDO" },
+ { "DAC", NULL, "DBG" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "CLK25M" },
+ { "DAC", NULL, "ADC_R" },
+ { "DAC", NULL, "ADC_L" },
+ { "DAC", NULL, "DLDO" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MIXER_R" },
+ { "DAC", NULL, "MIXER_L" },
+ { "DAC", NULL, "MBIAS4U" },
+ { "DAC", NULL, "PLL2_LDO" },
+ { "DAC", NULL, "PLL2B" },
+ { "DAC", NULL, "PLL2F" },
+ { "DAC", NULL, "PLL2F2" },
+ { "DAC", NULL, "PLL2B2" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+ { "DAC L", NULL, "DAC Power" },
+ { "DAC R", NULL, "DAC Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+
+ if (tx_mask)
+ return -EINVAL;
+
+ if (slots > 2)
+ return -EINVAL;
+
+ rt1308->rx_mask = rx_mask;
+ rt1308->slots = slots;
+ /* slot_width is not used since it's irrelevant for SoundWire */
+
+ return 0;
+}
+
+static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1308->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ return -EINVAL;
+
+ if (rt1308->slots) {
+ stream_config.ch_count = rt1308->slots;
+ port_config.ch_mask = rt1308->rx_mask;
+ }
+
+ retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ return retval;
+}
+
+static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_sdw_priv *rt1308 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1308->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1308->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1308_slave_ops = {
+ .read_prop = rt1308_read_prop,
+ .interrupt_callback = rt1308_interrupt_callback,
+ .update_status = rt1308_update_status,
+ .bus_config = rt1308_bus_config,
+};
+
+static int rt1308_sdw_parse_dt(struct rt1308_sdw_priv *rt1308, struct device *dev)
+{
+ int ret = 0;
+
+ device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1308->bq_params_cnt);
+ if (rt1308->bq_params_cnt) {
+ rt1308->bq_params = devm_kzalloc(dev, rt1308->bq_params_cnt, GFP_KERNEL);
+ if (!rt1308->bq_params) {
+ dev_err(dev, "Could not allocate bq_params memory\n");
+ ret = -ENOMEM;
+ } else {
+ ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1308->bq_params, rt1308->bq_params_cnt);
+ if (ret < 0)
+ dev_err(dev, "Could not read list of realtek,bq-params\n");
+ }
+ }
+
+ dev_dbg(dev, "bq_params_cnt=%d\n", rt1308->bq_params_cnt);
+ return ret;
+}
+
+static int rt1308_sdw_component_probe(struct snd_soc_component *component)
+{
+ struct rt1308_sdw_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt1308->component = component;
+ rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev);
+
+ if (!rt1308->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ /* apply BQ params */
+ rt1308_apply_bq_params(rt1308);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1308 = {
+ .probe = rt1308_sdw_component_probe,
+ .controls = rt1308_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1308_snd_controls),
+ .dapm_widgets = rt1308_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
+ .dapm_routes = rt1308_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
+ .hw_params = rt1308_sdw_hw_params,
+ .hw_free = rt1308_sdw_pcm_hw_free,
+ .set_stream = rt1308_set_sdw_stream,
+ .shutdown = rt1308_sdw_shutdown,
+ .set_tdm_slot = rt1308_sdw_set_tdm_slot,
+};
+
+#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1308_sdw_dai[] = {
+ {
+ .name = "rt1308-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1308_STEREO_RATES,
+ .formats = RT1308_FORMATS,
+ },
+ .ops = &rt1308_aif_dai_ops,
+ },
+};
+
+static int rt1308_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1308_sdw_priv *rt1308;
+ int ret;
+
+ rt1308 = devm_kzalloc(dev, sizeof(*rt1308), GFP_KERNEL);
+ if (!rt1308)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1308);
+ rt1308->sdw_slave = slave;
+ rt1308->regmap = regmap;
+
+ regcache_cache_only(rt1308->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1308->hw_init = false;
+ rt1308->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1308,
+ rt1308_sdw_dai,
+ ARRAY_SIZE(rt1308_sdw_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static int rt1308_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1308_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1308_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt1308_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1308_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1308, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1308_id);
+
+static int rt1308_dev_suspend(struct device *dev)
+{
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+
+ if (!rt1308->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1308->regmap, true);
+
+ return 0;
+}
+
+#define RT1308_PROBE_TIMEOUT 5000
+
+static int rt1308_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1308->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1308_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1308->regmap, false);
+ regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1308_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume)
+ RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1308_sdw_driver = {
+ .driver = {
+ .name = "rt1308",
+ .pm = pm_ptr(&rt1308_pm),
+ },
+ .probe = rt1308_sdw_probe,
+ .remove = rt1308_sdw_remove,
+ .ops = &rt1308_slave_ops,
+ .id_table = rt1308_id,
+};
+module_sdw_driver(rt1308_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1308 driver SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h
new file mode 100644
index 000000000000..f816c73e247e
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1308-sdw.h -- RT1308 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1308_SDW_H__
+#define __RT1308_SDW_H__
+
+static const struct reg_default rt1308_reg_defaults[] = {
+ { 0x0000, 0x00 },
+ { 0x0001, 0x00 },
+ { 0x0002, 0x00 },
+ { 0x0003, 0x00 },
+ { 0x0004, 0x00 },
+ { 0x0005, 0x01 },
+ { 0x0020, 0x00 },
+ { 0x0022, 0x00 },
+ { 0x0023, 0x00 },
+ { 0x0024, 0x00 },
+ { 0x0025, 0x00 },
+ { 0x0026, 0x00 },
+ { 0x0030, 0x00 },
+ { 0x0032, 0x00 },
+ { 0x0033, 0x00 },
+ { 0x0034, 0x00 },
+ { 0x0035, 0x00 },
+ { 0x0036, 0x00 },
+ { 0x0040, 0x00 },
+ { 0x0041, 0x00 },
+ { 0x0042, 0x00 },
+ { 0x0043, 0x00 },
+ { 0x0044, 0x20 },
+ { 0x0045, 0x01 },
+ { 0x0046, 0x01 },
+ { 0x0048, 0x00 },
+ { 0x0049, 0x00 },
+ { 0x0050, 0x20 },
+ { 0x0051, 0x02 },
+ { 0x0052, 0x5D },
+ { 0x0053, 0x13 },
+ { 0x0054, 0x08 },
+ { 0x0055, 0x00 },
+ { 0x0060, 0x00 },
+ { 0x0070, 0x00 },
+ { 0x00E0, 0x00 },
+ { 0x00F0, 0x00 },
+ { 0x0100, 0x00 },
+ { 0x0101, 0x00 },
+ { 0x0102, 0x20 },
+ { 0x0103, 0x00 },
+ { 0x0104, 0x00 },
+ { 0x0105, 0x03 },
+ { 0x0120, 0x00 },
+ { 0x0122, 0x00 },
+ { 0x0123, 0x00 },
+ { 0x0124, 0x00 },
+ { 0x0125, 0x00 },
+ { 0x0126, 0x00 },
+ { 0x0127, 0x00 },
+ { 0x0130, 0x00 },
+ { 0x0132, 0x00 },
+ { 0x0133, 0x00 },
+ { 0x0134, 0x00 },
+ { 0x0135, 0x00 },
+ { 0x0136, 0x00 },
+ { 0x0137, 0x00 },
+ { 0x0200, 0x00 },
+ { 0x0201, 0x00 },
+ { 0x0202, 0x00 },
+ { 0x0203, 0x00 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x03 },
+ { 0x0220, 0x00 },
+ { 0x0222, 0x00 },
+ { 0x0223, 0x00 },
+ { 0x0224, 0x00 },
+ { 0x0225, 0x00 },
+ { 0x0226, 0x00 },
+ { 0x0227, 0x00 },
+ { 0x0230, 0x00 },
+ { 0x0232, 0x00 },
+ { 0x0233, 0x00 },
+ { 0x0234, 0x00 },
+ { 0x0235, 0x00 },
+ { 0x0236, 0x00 },
+ { 0x0237, 0x00 },
+ { 0x0400, 0x00 },
+ { 0x0401, 0x00 },
+ { 0x0402, 0x00 },
+ { 0x0403, 0x00 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x03 },
+ { 0x0420, 0x00 },
+ { 0x0422, 0x00 },
+ { 0x0423, 0x00 },
+ { 0x0424, 0x00 },
+ { 0x0425, 0x00 },
+ { 0x0426, 0x00 },
+ { 0x0427, 0x00 },
+ { 0x0430, 0x00 },
+ { 0x0432, 0x00 },
+ { 0x0433, 0x00 },
+ { 0x0434, 0x00 },
+ { 0x0435, 0x00 },
+ { 0x0436, 0x00 },
+ { 0x0437, 0x00 },
+ { 0x0f00, 0x00 },
+ { 0x0f01, 0x00 },
+ { 0x0f02, 0x00 },
+ { 0x0f03, 0x00 },
+ { 0x0f04, 0x00 },
+ { 0x0f05, 0x00 },
+ { 0x0f20, 0x00 },
+ { 0x0f22, 0x00 },
+ { 0x0f23, 0x00 },
+ { 0x0f24, 0x00 },
+ { 0x0f25, 0x00 },
+ { 0x0f26, 0x00 },
+ { 0x0f27, 0x00 },
+ { 0x0f30, 0x00 },
+ { 0x0f32, 0x00 },
+ { 0x0f33, 0x00 },
+ { 0x0f34, 0x00 },
+ { 0x0f35, 0x00 },
+ { 0x0f36, 0x00 },
+ { 0x0f37, 0x00 },
+ { 0x2f01, 0x01 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x0f },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f07, 0x8e },
+ { 0x3000, 0x00 },
+ { 0x3001, 0x00 },
+ { 0x3004, 0x01 },
+ { 0x3005, 0x23 },
+ { 0x3008, 0x02 },
+ { 0x300a, 0x00 },
+ { 0xc000 | (RT1308_DATA_PATH << 4), 0x00 },
+ { 0xc003 | (RT1308_DAC_SET << 4), 0x00 },
+ { 0xc000 | (RT1308_POWER << 4), 0x00 },
+ { 0xc001 | (RT1308_POWER << 4), 0x00 },
+ { 0xc002 | (RT1308_POWER << 4), 0x00 },
+ { 0xc000 | (RT1308_POWER_STATUS << 4), 0x00 },
+};
+
+#define RT1308_SDW_OFFSET 0xc000
+#define RT1308_SDW_OFFSET_BYTE0 0xc000
+#define RT1308_SDW_OFFSET_BYTE1 0xc001
+#define RT1308_SDW_OFFSET_BYTE2 0xc002
+#define RT1308_SDW_OFFSET_BYTE3 0xc003
+
+#define RT1308_SDW_RESET (RT1308_SDW_OFFSET | (RT1308_RESET << 4))
+
+struct rt1308_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int rx_mask;
+ int slots;
+ int hw_ver;
+ unsigned char *bq_params;
+ unsigned int bq_params_cnt;
+};
+
+#endif /* __RT1308_SDW_H__ */
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
new file mode 100644
index 000000000000..df50b38c24b9
--- /dev/null
+++ b/sound/soc/codecs/rt1308.c
@@ -0,0 +1,871 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1308.c -- RT1308 ALSA SoC amplifier component driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Derek Fang <derek.fang@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1308.h"
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1308_I2C_I2S_SDW_SET, 0x01014005 },
+ { RT1308_CLASS_D_SET_2, 0x227f5501 },
+ { RT1308_PADS_1, 0x50150505 },
+ { RT1308_VREF, 0x18100000 },
+ { RT1308_IV_SENSE, 0x87010000 },
+ { RT1308_DUMMY_REG, 0x00000200 },
+ { RT1308_SIL_DET, 0xe1c30000 },
+ { RT1308_DC_CAL_2, 0x00ffff00 },
+ { RT1308_CLK_DET, 0x01000000 },
+ { RT1308_POWER_STATUS, 0x08800000 },
+ { RT1308_DAC_SET, 0xafaf0700 },
+
+};
+#define RT1308_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+struct rt1308_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+static const struct reg_default rt1308_reg[] = {
+
+ { 0x01, 0x1f3f5f00 },
+ { 0x02, 0x07000000 },
+ { 0x03, 0x80003e00 },
+ { 0x04, 0x80800600 },
+ { 0x05, 0x0aaa1a0a },
+ { 0x06, 0x52000000 },
+ { 0x07, 0x00000000 },
+ { 0x08, 0x00600000 },
+ { 0x09, 0xe1030000 },
+ { 0x0a, 0x00000000 },
+ { 0x0b, 0x30000000 },
+ { 0x0c, 0x7fff7000 },
+ { 0x10, 0xffff0700 },
+ { 0x11, 0x0a000000 },
+ { 0x12, 0x60040000 },
+ { 0x13, 0x00000000 },
+ { 0x14, 0x0f300000 },
+ { 0x15, 0x00000022 },
+ { 0x16, 0x02000000 },
+ { 0x17, 0x01004045 },
+ { 0x18, 0x00000000 },
+ { 0x19, 0x00000000 },
+ { 0x1a, 0x80000000 },
+ { 0x1b, 0x10325476 },
+ { 0x1c, 0x1d1d0000 },
+ { 0x20, 0xd2101300 },
+ { 0x21, 0xf3ffff00 },
+ { 0x22, 0x00000000 },
+ { 0x23, 0x00000000 },
+ { 0x24, 0x00000000 },
+ { 0x25, 0x00000000 },
+ { 0x26, 0x00000000 },
+ { 0x27, 0x00000000 },
+ { 0x28, 0x00000000 },
+ { 0x29, 0x00000000 },
+ { 0x2a, 0x00000000 },
+ { 0x2b, 0x00000000 },
+ { 0x2c, 0x00000000 },
+ { 0x2d, 0x00000000 },
+ { 0x2e, 0x00000000 },
+ { 0x2f, 0x00000000 },
+ { 0x30, 0x01000000 },
+ { 0x31, 0x20025501 },
+ { 0x32, 0x00000000 },
+ { 0x33, 0x105a0000 },
+ { 0x34, 0x10100000 },
+ { 0x35, 0x2aaa52aa },
+ { 0x36, 0x00c00000 },
+ { 0x37, 0x20046100 },
+ { 0x50, 0x10022f00 },
+ { 0x51, 0x003c0000 },
+ { 0x54, 0x04000000 },
+ { 0x55, 0x01000000 },
+ { 0x56, 0x02000000 },
+ { 0x57, 0x02000000 },
+ { 0x58, 0x02000000 },
+ { 0x59, 0x02000000 },
+ { 0x5b, 0x02000000 },
+ { 0x5c, 0x00000000 },
+ { 0x5d, 0x00000000 },
+ { 0x5e, 0x00000000 },
+ { 0x5f, 0x00000000 },
+ { 0x60, 0x02000000 },
+ { 0x61, 0x00000000 },
+ { 0x62, 0x00000000 },
+ { 0x63, 0x00000000 },
+ { 0x64, 0x00000000 },
+ { 0x65, 0x02000000 },
+ { 0x66, 0x00000000 },
+ { 0x67, 0x00000000 },
+ { 0x68, 0x00000000 },
+ { 0x69, 0x00000000 },
+ { 0x6a, 0x02000000 },
+ { 0x6c, 0x00000000 },
+ { 0x6d, 0x00000000 },
+ { 0x6e, 0x00000000 },
+ { 0x70, 0x10EC1308 },
+ { 0x71, 0x00000000 },
+ { 0x72, 0x00000000 },
+ { 0x73, 0x00000000 },
+ { 0x74, 0x00000000 },
+ { 0x75, 0x00000000 },
+ { 0x76, 0x00000000 },
+ { 0x77, 0x00000000 },
+ { 0x78, 0x00000000 },
+ { 0x79, 0x00000000 },
+ { 0x7a, 0x00000000 },
+ { 0x7b, 0x00000000 },
+ { 0x7c, 0x00000000 },
+ { 0x7d, 0x00000000 },
+ { 0x7e, 0x00000000 },
+ { 0x7f, 0x00020f00 },
+ { 0x80, 0x00000000 },
+ { 0x81, 0x00000000 },
+ { 0x82, 0x00000000 },
+ { 0x83, 0x00000000 },
+ { 0x84, 0x00000000 },
+ { 0x85, 0x00000000 },
+ { 0x86, 0x00000000 },
+ { 0x87, 0x00000000 },
+ { 0x88, 0x00000000 },
+ { 0x89, 0x00000000 },
+ { 0x8a, 0x00000000 },
+ { 0x8b, 0x00000000 },
+ { 0x8c, 0x00000000 },
+ { 0x8d, 0x00000000 },
+ { 0x8e, 0x00000000 },
+ { 0x90, 0x50250905 },
+ { 0x91, 0x15050000 },
+ { 0xa0, 0x00000000 },
+ { 0xa1, 0x00000000 },
+ { 0xa2, 0x00000000 },
+ { 0xa3, 0x00000000 },
+ { 0xa4, 0x00000000 },
+ { 0xb0, 0x00000000 },
+ { 0xb1, 0x00000000 },
+ { 0xb2, 0x00000000 },
+ { 0xb3, 0x00000000 },
+ { 0xb4, 0x00000000 },
+ { 0xb5, 0x00000000 },
+ { 0xb6, 0x00000000 },
+ { 0xb7, 0x00000000 },
+ { 0xb8, 0x00000000 },
+ { 0xb9, 0x00000000 },
+ { 0xba, 0x00000000 },
+ { 0xbb, 0x00000000 },
+ { 0xc0, 0x01000000 },
+ { 0xc1, 0x00000000 },
+ { 0xf0, 0x00000000 },
+};
+
+static int rt1308_reg_init(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ return regmap_multi_reg_write(rt1308->regmap, init_list,
+ RT1308_INIT_REG_LEN);
+}
+
+static bool rt1308_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1308_RESET:
+ case RT1308_RESET_N:
+ case RT1308_CLK_2:
+ case RT1308_SIL_DET:
+ case RT1308_CLK_DET:
+ case RT1308_DC_DET:
+ case RT1308_DAC_SET:
+ case RT1308_DAC_BUF:
+ case RT1308_SDW_REG_RDATA:
+ case RT1308_DC_CAL_1:
+ case RT1308_PVDD_OFFSET_CTL:
+ case RT1308_CAL_OFFSET_DAC_PBTL:
+ case RT1308_CAL_OFFSET_DAC_L:
+ case RT1308_CAL_OFFSET_DAC_R:
+ case RT1308_CAL_OFFSET_PWM_L:
+ case RT1308_CAL_OFFSET_PWM_R:
+ case RT1308_CAL_PWM_VOS_ADC_L:
+ case RT1308_CAL_PWM_VOS_ADC_R:
+ case RT1308_MBIAS:
+ case RT1308_POWER_STATUS:
+ case RT1308_POWER_INT:
+ case RT1308_SINE_TONE_GEN_2:
+ case RT1308_BQ_SET:
+ case RT1308_BQ_PARA_UPDATE:
+ case RT1308_VEN_DEV_ID:
+ case RT1308_VERSION_ID:
+ case RT1308_EFUSE_1:
+ case RT1308_EFUSE_READ_PVDD_L:
+ case RT1308_EFUSE_READ_PVDD_R:
+ case RT1308_EFUSE_READ_PVDD_PTBL:
+ case RT1308_EFUSE_READ_DEV:
+ case RT1308_EFUSE_READ_R0:
+ case RT1308_EFUSE_READ_ADC_L:
+ case RT1308_EFUSE_READ_ADC_R:
+ case RT1308_EFUSE_READ_ADC_PBTL:
+ case RT1308_EFUSE_RESERVE:
+ case RT1308_EFUSE_DATA_0_MSB:
+ case RT1308_EFUSE_DATA_0_LSB:
+ case RT1308_EFUSE_DATA_1_MSB:
+ case RT1308_EFUSE_DATA_1_LSB:
+ case RT1308_EFUSE_DATA_2_MSB:
+ case RT1308_EFUSE_DATA_2_LSB:
+ case RT1308_EFUSE_DATA_3_MSB:
+ case RT1308_EFUSE_DATA_3_LSB:
+ case RT1308_EFUSE_STATUS_1:
+ case RT1308_EFUSE_STATUS_2:
+ case RT1308_DUMMY_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1308_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1308_RESET:
+ case RT1308_RESET_N:
+ case RT1308_CLK_GATING ... RT1308_DC_DET_THRES:
+ case RT1308_DAC_SET ... RT1308_AD_FILTER_SET:
+ case RT1308_DC_CAL_1 ... RT1308_POWER_INT:
+ case RT1308_SINE_TONE_GEN_1:
+ case RT1308_SINE_TONE_GEN_2:
+ case RT1308_BQ_SET:
+ case RT1308_BQ_PARA_UPDATE:
+ case RT1308_BQ_PRE_VOL_L ... RT1308_BQ_POST_VOL_R:
+ case RT1308_BQ1_L_H0 ... RT1308_BQ2_R_A2:
+ case RT1308_VEN_DEV_ID:
+ case RT1308_VERSION_ID:
+ case RT1308_SPK_BOUND:
+ case RT1308_BQ1_EQ_L_1 ... RT1308_BQ2_EQ_R_3:
+ case RT1308_EFUSE_1 ... RT1308_EFUSE_RESERVE:
+ case RT1308_PADS_1:
+ case RT1308_PADS_2:
+ case RT1308_TEST_MODE:
+ case RT1308_TEST_1:
+ case RT1308_TEST_2:
+ case RT1308_TEST_3:
+ case RT1308_TEST_4:
+ case RT1308_EFUSE_DATA_0_MSB ... RT1308_EFUSE_STATUS_2:
+ case RT1308_TCON_1:
+ case RT1308_TCON_2:
+ case RT1308_DUMMY_REG:
+ case RT1308_MAX_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1308_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(30);
+ snd_soc_component_update_bits(component, RT1308_POWER_STATUS,
+ RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT,
+ RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT);
+ msleep(40);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1308_POWER_STATUS,
+ RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1308_rx_data_ch_select[] = {
+ "LR",
+ "LL",
+ "RL",
+ "RR",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, RT1308_DATA_PATH, 24,
+ rt1308_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1308_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1308_sto_dac_l =
+ SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET,
+ RT1308_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1308_sto_dac_r =
+ SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET,
+ RT1308_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Supply Widgets */
+ SND_SOC_DAPM_SUPPLY("MBIAS20U", RT1308_POWER,
+ RT1308_POW_MBIAS20U_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALDO", RT1308_POWER,
+ RT1308_POW_ALDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DBG", RT1308_POWER,
+ RT1308_POW_DBG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DACL", RT1308_POWER,
+ RT1308_POW_DACL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK25M", RT1308_POWER,
+ RT1308_POW_CLK25M_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_R", RT1308_POWER,
+ RT1308_POW_ADC_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_L", RT1308_POWER,
+ RT1308_POW_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DLDO", RT1308_POWER,
+ RT1308_POW_DLDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1308_POWER,
+ RT1308_POW_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_R", RT1308_POWER,
+ RT1308_POW_MIXER_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_L", RT1308_POWER,
+ RT1308_POW_MIXER_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS4U", RT1308_POWER,
+ RT1308_POW_MBIAS4U_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2_LDO", RT1308_POWER,
+ RT1308_POW_PLL2_LDO_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT1308_POWER,
+ RT1308_POW_PLL2B_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT1308_POWER,
+ RT1308_POW_PLL2F_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F2", RT1308_POWER,
+ RT1308_POW_PLL2F2_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B2", RT1308_POWER,
+ RT1308_POW_PLL2B2_EN_BIT, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC Power", RT1308_POWER,
+ RT1308_POW_DAC1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1308_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "MBIAS20U" },
+ { "DAC", NULL, "ALDO" },
+ { "DAC", NULL, "DBG" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "CLK25M" },
+ { "DAC", NULL, "ADC_R" },
+ { "DAC", NULL, "ADC_L" },
+ { "DAC", NULL, "DLDO" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MIXER_R" },
+ { "DAC", NULL, "MIXER_L" },
+ { "DAC", NULL, "MBIAS4U" },
+ { "DAC", NULL, "PLL2_LDO" },
+ { "DAC", NULL, "PLL2B" },
+ { "DAC", NULL, "PLL2F" },
+ { "DAC", NULL, "PLL2F2" },
+ { "DAC", NULL, "PLL2B2" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+ { "DAC L", NULL, "DAC Power" },
+ { "DAC R", NULL, "DAC Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1308_get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1308_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1308->lrck = params_rate(params);
+ pre_div = rt1308_get_clk_info(rt1308->sysclk, rt1308->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev,
+ "Unsupported clock setting %d\n", rt1308->lrck);
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1308->bclk = rt1308->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1308->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1308_I2S_DL_SEL_16B;
+ break;
+ case 20:
+ val_len |= RT1308_I2S_DL_SEL_20B;
+ break;
+ case 24:
+ val_len |= RT1308_I2S_DL_SEL_24B;
+ break;
+ case 8:
+ val_len |= RT1308_I2S_DL_SEL_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1308_AIF1:
+ mask_clk = RT1308_DIV_FS_SYS_MASK;
+ val_clk = pre_div << RT1308_DIV_FS_SYS_SFT;
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_2, RT1308_I2S_DL_SEL_MASK,
+ val_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1308_CLK_1,
+ mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1308_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt1308->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1308_I2S_DF_SEL_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1308_I2S_DF_SEL_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1308_I2S_DF_SEL_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1_val |= RT1308_I2S_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1308_AIF1:
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_1, RT1308_I2S_DF_SEL_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_2, RT1308_I2S_BCLK_MASK,
+ reg1_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt1308_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1308->sysclk && clk_id == rt1308->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1308_FS_SYS_S_MCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_MCLK;
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK,
+ RT1308_MCLK_DET_EN);
+ break;
+ case RT1308_FS_SYS_S_BCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_BCLK;
+ break;
+ case RT1308_FS_SYS_S_PLL:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_PLL;
+ break;
+ case RT1308_FS_SYS_S_RCCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1308_CLK_1,
+ RT1308_SEL_FS_SYS_MASK, reg_val);
+ rt1308->sysclk = freq;
+ rt1308->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1308_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1308->pll_src && freq_in == rt1308->pll_in &&
+ freq_out == rt1308->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1308->pll_in = 0;
+ rt1308->pll_out = 0;
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_1, RT1308_SEL_FS_SYS_MASK,
+ RT1308_SEL_FS_SYS_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1308_PLL_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_MCLK);
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK,
+ RT1308_MCLK_DET_EN);
+ break;
+ case RT1308_PLL_S_BCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_BCLK);
+ break;
+ case RT1308_PLL_S_RCCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_RCCLK);
+ freq_in = 25000000;
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1308_PLL_1,
+ (pll_code.k_code << RT1308_PLL1_K_SFT) |
+ (pll_code.m_bp << RT1308_PLL1_M_BYPASS_SFT) |
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1308_PLL1_M_SFT) |
+ (pll_code.n_code << RT1308_PLL1_N_SFT));
+
+ rt1308->pll_in = freq_in;
+ rt1308->pll_out = freq_out;
+ rt1308->pll_src = source;
+
+ return 0;
+}
+
+static int rt1308_probe(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ rt1308->component = component;
+
+ return rt1308_reg_init(component);
+}
+
+static void rt1308_remove(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt1308_suspend(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1308->regmap, true);
+ regcache_mark_dirty(rt1308->regmap);
+
+ return 0;
+}
+
+static int rt1308_resume(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1308->regmap, false);
+ regcache_sync(rt1308->regmap);
+
+ return 0;
+}
+#else
+#define rt1308_suspend NULL
+#define rt1308_resume NULL
+#endif
+
+#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
+ .hw_params = rt1308_hw_params,
+ .set_fmt = rt1308_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1308_dai[] = {
+ {
+ .name = "rt1308-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1308_STEREO_RATES,
+ .formats = RT1308_FORMATS,
+ },
+ .ops = &rt1308_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1308 = {
+ .probe = rt1308_probe,
+ .remove = rt1308_remove,
+ .suspend = rt1308_suspend,
+ .resume = rt1308_resume,
+ .controls = rt1308_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1308_snd_controls),
+ .dapm_widgets = rt1308_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
+ .dapm_routes = rt1308_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+ .set_sysclk = rt1308_set_component_sysclk,
+ .set_pll = rt1308_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1308_regmap = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = RT1308_MAX_REG,
+ .volatile_reg = rt1308_volatile_register,
+ .readable_reg = rt1308_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt1308_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1308_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt1308_of_match[] = {
+ { .compatible = "realtek,rt1308", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1308_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1308_acpi_match[] = {
+ { "10EC1308" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1308_i2c_id[] = {
+ { "rt1308" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1308_i2c_id);
+
+static void rt1308_efuse(struct rt1308_priv *rt1308)
+{
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+
+ regmap_write(rt1308->regmap, RT1308_POWER_STATUS, 0x01800000);
+ msleep(100);
+ regmap_write(rt1308->regmap, RT1308_EFUSE_1, 0x44fe0f00);
+ msleep(20);
+ regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000);
+}
+
+static int rt1308_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1308_priv *rt1308;
+ int ret;
+ unsigned int val;
+
+ rt1308 = devm_kzalloc(&i2c->dev, sizeof(struct rt1308_priv),
+ GFP_KERNEL);
+ if (rt1308 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1308);
+
+ rt1308->regmap = devm_regmap_init_i2c(i2c, &rt1308_regmap);
+ if (IS_ERR(rt1308->regmap)) {
+ ret = PTR_ERR(rt1308->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1308->regmap, RT1308_VEN_DEV_ID, &val);
+ /* ignore last byte difference */
+ if ((val & 0xFFFFFF00) != RT1308_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1308\n", val);
+ return -ENODEV;
+ }
+
+ rt1308_efuse(rt1308);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1308,
+ rt1308_dai, ARRAY_SIZE(rt1308_dai));
+}
+
+static void rt1308_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1308_priv *rt1308 = i2c_get_clientdata(client);
+
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+}
+
+static struct i2c_driver rt1308_i2c_driver = {
+ .driver = {
+ .name = "rt1308",
+ .of_match_table = of_match_ptr(rt1308_of_match),
+ .acpi_match_table = ACPI_PTR(rt1308_acpi_match),
+ },
+ .probe = rt1308_i2c_probe,
+ .shutdown = rt1308_i2c_shutdown,
+ .id_table = rt1308_i2c_id,
+};
+module_i2c_driver(rt1308_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1308 amplifier driver");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1308.h b/sound/soc/codecs/rt1308.h
new file mode 100644
index 000000000000..d3a0f91630ca
--- /dev/null
+++ b/sound/soc/codecs/rt1308.h
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt1308.h -- RT1308 ALSA SoC amplifier component driver
+ *
+ * Copyright 2019 Realtek Semiconductor Corp.
+ * Author: Derek Fang <derek.fang@realtek.com>
+ *
+ */
+
+#ifndef _RT1308_H_
+#define _RT1308_H_
+
+#define RT1308_DEVICE_ID_NUM 0x10ec1300
+
+#define RT1308_RESET 0x00
+#define RT1308_RESET_N 0x01
+#define RT1308_CLK_GATING 0x02
+#define RT1308_PLL_1 0x03
+#define RT1308_PLL_2 0x04
+#define RT1308_PLL_INT 0x05
+#define RT1308_CLK_1 0x06
+#define RT1308_DATA_PATH 0x07
+#define RT1308_CLK_2 0x08
+#define RT1308_SIL_DET 0x09
+#define RT1308_CLK_DET 0x0a
+#define RT1308_DC_DET 0x0b
+#define RT1308_DC_DET_THRES 0x0c
+#define RT1308_DAC_SET 0x10
+#define RT1308_SRC_SET 0x11
+#define RT1308_DAC_BUF 0x12
+#define RT1308_ADC_SET 0x13
+#define RT1308_ADC_SET_INT 0x14
+#define RT1308_I2S_SET_1 0x15
+#define RT1308_I2S_SET_2 0x16
+#define RT1308_I2C_I2S_SDW_SET 0x17
+#define RT1308_SDW_REG_RW 0x18
+#define RT1308_SDW_REG_RDATA 0x19
+#define RT1308_IV_SENSE 0x1a
+#define RT1308_I2S_TX_DAC_SET 0x1b
+#define RT1308_AD_FILTER_SET 0x1c
+#define RT1308_DC_CAL_1 0x20
+#define RT1308_DC_CAL_2 0x21
+#define RT1308_DC_CAL_L_OFFSET 0x22
+#define RT1308_DC_CAL_R_OFFSET 0x23
+#define RT1308_PVDD_OFFSET_CTL 0x24
+#define RT1308_PVDD_OFFSET_L 0x25
+#define RT1308_PVDD_OFFSET_R 0x26
+#define RT1308_PVDD_OFFSET_PBTL 0x27
+#define RT1308_PVDD_OFFSET_PVDD 0x28
+#define RT1308_CAL_OFFSET_DAC_PBTL 0x29
+#define RT1308_CAL_OFFSET_DAC_L 0x2a
+#define RT1308_CAL_OFFSET_DAC_R 0x2b
+#define RT1308_CAL_OFFSET_PWM_L 0x2c
+#define RT1308_CAL_OFFSET_PWM_R 0x2d
+#define RT1308_CAL_PWM_VOS_ADC_L 0x2e
+#define RT1308_CAL_PWM_VOS_ADC_R 0x2f
+#define RT1308_CLASS_D_SET_1 0x30
+#define RT1308_CLASS_D_SET_2 0x31
+#define RT1308_POWER 0x32
+#define RT1308_LDO 0x33
+#define RT1308_VREF 0x34
+#define RT1308_MBIAS 0x35
+#define RT1308_POWER_STATUS 0x36
+#define RT1308_POWER_INT 0x37
+#define RT1308_SINE_TONE_GEN_1 0x50
+#define RT1308_SINE_TONE_GEN_2 0x51
+#define RT1308_BQ_SET 0x54
+#define RT1308_BQ_PARA_UPDATE 0x55
+#define RT1308_BQ_PRE_VOL_L 0x56
+#define RT1308_BQ_PRE_VOL_R 0x57
+#define RT1308_BQ_POST_VOL_L 0x58
+#define RT1308_BQ_POST_VOL_R 0x59
+#define RT1308_BQ1_L_H0 0x5b
+#define RT1308_BQ1_L_B1 0x5c
+#define RT1308_BQ1_L_B2 0x5d
+#define RT1308_BQ1_L_A1 0x5e
+#define RT1308_BQ1_L_A2 0x5f
+#define RT1308_BQ1_R_H0 0x60
+#define RT1308_BQ1_R_B1 0x61
+#define RT1308_BQ1_R_B2 0x62
+#define RT1308_BQ1_R_A1 0x63
+#define RT1308_BQ1_R_A2 0x64
+#define RT1308_BQ2_L_H0 0x65
+#define RT1308_BQ2_L_B1 0x66
+#define RT1308_BQ2_L_B2 0x67
+#define RT1308_BQ2_L_A1 0x68
+#define RT1308_BQ2_L_A2 0x69
+#define RT1308_BQ2_R_H0 0x6a
+#define RT1308_BQ2_R_B1 0x6b
+#define RT1308_BQ2_R_B2 0x6c
+#define RT1308_BQ2_R_A1 0x6d
+#define RT1308_BQ2_R_A2 0x6e
+#define RT1308_VEN_DEV_ID 0x70
+#define RT1308_VERSION_ID 0x71
+#define RT1308_SPK_BOUND 0x72
+#define RT1308_BQ1_EQ_L_1 0x73
+#define RT1308_BQ1_EQ_L_2 0x74
+#define RT1308_BQ1_EQ_L_3 0x75
+#define RT1308_BQ1_EQ_R_1 0x76
+#define RT1308_BQ1_EQ_R_2 0x77
+#define RT1308_BQ1_EQ_R_3 0x78
+#define RT1308_BQ2_EQ_L_1 0x79
+#define RT1308_BQ2_EQ_L_2 0x7a
+#define RT1308_BQ2_EQ_L_3 0x7b
+#define RT1308_BQ2_EQ_R_1 0x7c
+#define RT1308_BQ2_EQ_R_2 0x7d
+#define RT1308_BQ2_EQ_R_3 0x7e
+#define RT1308_EFUSE_1 0x7f
+#define RT1308_EFUSE_2 0x80
+#define RT1308_EFUSE_PROG_PVDD_L 0x81
+#define RT1308_EFUSE_PROG_PVDD_R 0x82
+#define RT1308_EFUSE_PROG_R0_L 0x83
+#define RT1308_EFUSE_PROG_R0_R 0x84
+#define RT1308_EFUSE_PROG_DEV 0x85
+#define RT1308_EFUSE_READ_PVDD_L 0x86
+#define RT1308_EFUSE_READ_PVDD_R 0x87
+#define RT1308_EFUSE_READ_PVDD_PTBL 0x88
+#define RT1308_EFUSE_READ_DEV 0x89
+#define RT1308_EFUSE_READ_R0 0x8a
+#define RT1308_EFUSE_READ_ADC_L 0x8b
+#define RT1308_EFUSE_READ_ADC_R 0x8c
+#define RT1308_EFUSE_READ_ADC_PBTL 0x8d
+#define RT1308_EFUSE_RESERVE 0x8e
+#define RT1308_PADS_1 0x90
+#define RT1308_PADS_2 0x91
+#define RT1308_TEST_MODE 0xa0
+#define RT1308_TEST_1 0xa1
+#define RT1308_TEST_2 0xa2
+#define RT1308_TEST_3 0xa3
+#define RT1308_TEST_4 0xa4
+#define RT1308_EFUSE_DATA_0_MSB 0xb0
+#define RT1308_EFUSE_DATA_0_LSB 0xb1
+#define RT1308_EFUSE_DATA_1_MSB 0xb2
+#define RT1308_EFUSE_DATA_1_LSB 0xb3
+#define RT1308_EFUSE_DATA_2_MSB 0xb4
+#define RT1308_EFUSE_DATA_2_LSB 0xb5
+#define RT1308_EFUSE_DATA_3_MSB 0xb6
+#define RT1308_EFUSE_DATA_3_LSB 0xb7
+#define RT1308_EFUSE_DATA_TEST_MSB 0xb8
+#define RT1308_EFUSE_DATA_TEST_LSB 0xb9
+#define RT1308_EFUSE_STATUS_1 0xba
+#define RT1308_EFUSE_STATUS_2 0xbb
+#define RT1308_TCON_1 0xc0
+#define RT1308_TCON_2 0xc1
+#define RT1308_DUMMY_REG 0xf0
+#define RT1308_MAX_REG 0xff
+
+/* PLL1 M/N/K Code-1 (0x03) */
+#define RT1308_PLL1_K_SFT 24
+#define RT1308_PLL1_K_MASK (0x1f << 24)
+#define RT1308_PLL1_M_BYPASS_MASK (0x1 << 23)
+#define RT1308_PLL1_M_BYPASS_SFT 23
+#define RT1308_PLL1_M_BYPASS (0x1 << 23)
+#define RT1308_PLL1_M_MASK (0x3f << 16)
+#define RT1308_PLL1_M_SFT 16
+#define RT1308_PLL1_N_MASK (0x7f << 8)
+#define RT1308_PLL1_N_SFT 8
+
+/* CLOCK-1 (0x06) */
+#define RT1308_DIV_FS_SYS_MASK (0xf << 28)
+#define RT1308_DIV_FS_SYS_SFT 28
+#define RT1308_SEL_FS_SYS_MASK (0x7 << 24)
+#define RT1308_SEL_FS_SYS_SFT 24
+#define RT1308_SEL_FS_SYS_SRC_MCLK (0x0 << 24)
+#define RT1308_SEL_FS_SYS_SRC_BCLK (0x1 << 24)
+#define RT1308_SEL_FS_SYS_SRC_PLL (0x2 << 24)
+#define RT1308_SEL_FS_SYS_SRC_RCCLK (0x4 << 24)
+
+/* CLOCK-2 (0x08) */
+#define RT1308_DIV_PRE_PLL_MASK (0xf << 28)
+#define RT1308_DIV_PRE_PLL_SFT 28
+#define RT1308_SEL_PLL_SRC_MASK (0x7 << 24)
+#define RT1308_SEL_PLL_SRC_SFT 24
+#define RT1308_SEL_PLL_SRC_MCLK (0x0 << 24)
+#define RT1308_SEL_PLL_SRC_BCLK (0x1 << 24)
+#define RT1308_SEL_PLL_SRC_RCCLK (0x4 << 24)
+
+/* Clock Detect (0x0a) */
+#define RT1308_MCLK_DET_EN_MASK (0x1 << 25)
+#define RT1308_MCLK_DET_EN_SFT 25
+#define RT1308_MCLK_DET_EN (0x1 << 25)
+#define RT1308_BCLK_DET_EN_MASK (0x1 << 24)
+#define RT1308_BCLK_DET_EN_SFT 24
+#define RT1308_BCLK_DET_EN (0x1 << 24)
+
+/* DAC Setting (0x10) */
+#define RT1308_DVOL_MUTE_R_EN_SFT 7
+#define RT1308_DVOL_MUTE_L_EN_SFT 6
+
+/* I2S Setting-1 (0x15) */
+#define RT1308_I2S_DF_SEL_MASK (0x3 << 12)
+#define RT1308_I2S_DF_SEL_SFT 12
+#define RT1308_I2S_DF_SEL_I2S (0x0 << 12)
+#define RT1308_I2S_DF_SEL_LEFT (0x1 << 12)
+#define RT1308_I2S_DF_SEL_PCM_A (0x2 << 12)
+#define RT1308_I2S_DF_SEL_PCM_B (0x3 << 12)
+#define RT1308_I2S_DL_RX_SEL_MASK (0x7 << 4)
+#define RT1308_I2S_DL_RX_SEL_SFT 4
+#define RT1308_I2S_DL_RX_SEL_16B (0x0 << 4)
+#define RT1308_I2S_DL_RX_SEL_20B (0x1 << 4)
+#define RT1308_I2S_DL_RX_SEL_24B (0x2 << 4)
+#define RT1308_I2S_DL_RX_SEL_32B (0x3 << 4)
+#define RT1308_I2S_DL_RX_SEL_8B (0x4 << 4)
+#define RT1308_I2S_DL_TX_SEL_MASK (0x7 << 0)
+#define RT1308_I2S_DL_TX_SEL_SFT 0
+#define RT1308_I2S_DL_TX_SEL_16B (0x0 << 0)
+#define RT1308_I2S_DL_TX_SEL_20B (0x1 << 0)
+#define RT1308_I2S_DL_TX_SEL_24B (0x2 << 0)
+#define RT1308_I2S_DL_TX_SEL_32B (0x3 << 0)
+#define RT1308_I2S_DL_TX_SEL_8B (0x4 << 0)
+
+/* I2S Setting-2 (0x16) */
+#define RT1308_I2S_DL_SEL_MASK (0x7 << 24)
+#define RT1308_I2S_DL_SEL_SFT 24
+#define RT1308_I2S_DL_SEL_16B (0x0 << 24)
+#define RT1308_I2S_DL_SEL_20B (0x1 << 24)
+#define RT1308_I2S_DL_SEL_24B (0x2 << 24)
+#define RT1308_I2S_DL_SEL_32B (0x3 << 24)
+#define RT1308_I2S_DL_SEL_8B (0x4 << 24)
+#define RT1308_I2S_BCLK_MASK (0x1 << 14)
+#define RT1308_I2S_BCLK_SFT 14
+#define RT1308_I2S_BCLK_NORMAL (0x0 << 14)
+#define RT1308_I2S_BCLK_INV (0x1 << 14)
+
+/* Power Control-1 (0x32) */
+#define RT1308_POW_MBIAS20U (0x1 << 31)
+#define RT1308_POW_MBIAS20U_BIT 31
+#define RT1308_POW_ALDO (0x1 << 30)
+#define RT1308_POW_ALDO_BIT 30
+#define RT1308_POW_DBG (0x1 << 29)
+#define RT1308_POW_DBG_BIT 29
+#define RT1308_POW_DACL (0x1 << 28)
+#define RT1308_POW_DACL_BIT 28
+#define RT1308_POW_DAC1 (0x1 << 27)
+#define RT1308_POW_DAC1_BIT 27
+#define RT1308_POW_CLK25M (0x1 << 26)
+#define RT1308_POW_CLK25M_BIT 26
+#define RT1308_POW_ADC_R (0x1 << 25)
+#define RT1308_POW_ADC_R_BIT 25
+#define RT1308_POW_ADC_L (0x1 << 24)
+#define RT1308_POW_ADC_L_BIT 24
+#define RT1308_POW_DLDO (0x1 << 21)
+#define RT1308_POW_DLDO_BIT 21
+#define RT1308_POW_VREF (0x1 << 20)
+#define RT1308_POW_VREF_BIT 20
+#define RT1308_POW_MIXER_R (0x1 << 18)
+#define RT1308_POW_MIXER_R_BIT 18
+#define RT1308_POW_MIXER_L (0x1 << 17)
+#define RT1308_POW_MIXER_L_BIT 17
+#define RT1308_POW_MBIAS4U (0x1 << 16)
+#define RT1308_POW_MBIAS4U_BIT 16
+#define RT1308_POW_PLL2_LDO_EN (0x1 << 12)
+#define RT1308_POW_PLL2_LDO_EN_BIT 12
+#define RT1308_POW_PLL2B_EN (0x1 << 11)
+#define RT1308_POW_PLL2B_EN_BIT 11
+#define RT1308_POW_PLL2F_EN (0x1 << 10)
+#define RT1308_POW_PLL2F_EN_BIT 10
+#define RT1308_POW_PLL2F2_EN (0x1 << 9)
+#define RT1308_POW_PLL2F2_EN_BIT 9
+#define RT1308_POW_PLL2B2_EN (0x1 << 8)
+#define RT1308_POW_PLL2B2_EN_BIT 8
+
+/* Power Control-2 (0x36) */
+#define RT1308_POW_PDB_SRC_BIT (0x1 << 27)
+#define RT1308_POW_PDB_MN_BIT (0x1 << 25)
+#define RT1308_POW_PDB_REG_BIT (0x1 << 24)
+
+
+/* System Clock Source */
+enum {
+ RT1308_FS_SYS_S_MCLK,
+ RT1308_FS_SYS_S_BCLK,
+ RT1308_FS_SYS_S_PLL,
+ RT1308_FS_SYS_S_RCCLK, /* 25.0 MHz */
+};
+
+/* PLL Source */
+enum {
+ RT1308_PLL_S_MCLK,
+ RT1308_PLL_S_BCLK,
+ RT1308_PLL_S_RCCLK,
+};
+
+enum {
+ RT1308_AIF1,
+ RT1308_AIFS
+};
+
+enum rt1308_hw_ver {
+ RT1308_VER_C = 2,
+ RT1308_VER_D
+};
+
+#endif /* end of _RT1308_H_ */
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
new file mode 100644
index 000000000000..01a977398864
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1316-sdw.c -- rt1316 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "rt1316-sdw.h"
+
+static const struct reg_default rt1316_reg_defaults[] = {
+ { 0x3004, 0x00 },
+ { 0x3005, 0x00 },
+ { 0x3206, 0x00 },
+ { 0xc001, 0x00 },
+ { 0xc002, 0x00 },
+ { 0xc003, 0x00 },
+ { 0xc004, 0x00 },
+ { 0xc005, 0x00 },
+ { 0xc006, 0x00 },
+ { 0xc007, 0x00 },
+ { 0xc008, 0x00 },
+ { 0xc009, 0x00 },
+ { 0xc00a, 0x00 },
+ { 0xc00b, 0x00 },
+ { 0xc00c, 0x00 },
+ { 0xc00d, 0x00 },
+ { 0xc00e, 0x00 },
+ { 0xc00f, 0x00 },
+ { 0xc010, 0xa5 },
+ { 0xc011, 0x00 },
+ { 0xc012, 0xff },
+ { 0xc013, 0xff },
+ { 0xc014, 0x40 },
+ { 0xc015, 0x00 },
+ { 0xc016, 0x00 },
+ { 0xc017, 0x00 },
+ { 0xc605, 0x30 },
+ { 0xc700, 0x0a },
+ { 0xc701, 0xaa },
+ { 0xc702, 0x1a },
+ { 0xc703, 0x0a },
+ { 0xc710, 0x80 },
+ { 0xc711, 0x00 },
+ { 0xc712, 0x3e },
+ { 0xc713, 0x80 },
+ { 0xc714, 0x80 },
+ { 0xc715, 0x06 },
+ { 0xd101, 0x00 },
+ { 0xd102, 0x30 },
+ { 0xd103, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_sequence rt1316_blind_write[] = {
+ { 0xc710, 0x17 },
+ { 0xc711, 0x80 },
+ { 0xc712, 0x26 },
+ { 0xc713, 0x06 },
+ { 0xc714, 0x80 },
+ { 0xc715, 0x06 },
+ { 0xc702, 0x0a },
+ { 0xc703, 0x0a },
+ { 0xc001, 0x45 },
+ { 0xc003, 0x00 },
+ { 0xc004, 0x11 },
+ { 0xc005, 0x00 },
+ { 0xc006, 0x00 },
+ { 0xc106, 0x00 },
+ { 0xc007, 0x11 },
+ { 0xc008, 0x11 },
+ { 0xc009, 0x00 },
+
+ { 0x2f0a, 0x00 },
+ { 0xd101, 0xf0 },
+ { 0xd103, 0x9b },
+ { 0x2f36, 0x8e },
+ { 0x3206, 0x80 },
+ { 0x3211, 0x0b },
+ { 0x3216, 0x06 },
+ { 0xc614, 0x20 },
+ { 0xc615, 0x0a },
+ { 0xc616, 0x02 },
+ { 0xc617, 0x00 },
+ { 0xc60b, 0x10 },
+ { 0xc60e, 0x05 },
+ { 0xc102, 0x00 },
+ { 0xc090, 0xb0 },
+ { 0xc00f, 0x01 },
+ { 0xc09c, 0x7b },
+
+ { 0xc602, 0x07 },
+ { 0xc603, 0x07 },
+ { 0xc0a3, 0x71 },
+ { 0xc00b, 0x30 },
+ { 0xc093, 0x80 },
+ { 0xc09d, 0x80 },
+ { 0xc0b0, 0x77 },
+ { 0xc010, 0xa5 },
+ { 0xc050, 0x83 },
+ { 0x2f55, 0x03 },
+ { 0x3217, 0xb5 },
+ { 0x3202, 0x02 },
+
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x00 },
+
+ /* for IV sense */
+ { 0x2232, 0x80 },
+ { 0xc0b0, 0x77 },
+ { 0xc011, 0x00 },
+ { 0xc020, 0x00 },
+ { 0xc023, 0x00 },
+ { 0x3101, 0x00 },
+ { 0x3004, 0xa0 },
+ { 0x3005, 0xb1 },
+ { 0xc007, 0x11 },
+ { 0xc008, 0x11 },
+ { 0xc009, 0x00 },
+ { 0xc022, 0xd6 },
+ { 0xc025, 0xd6 },
+
+ { 0xd001, 0x03 },
+ { 0xd002, 0xbf },
+ { 0xd003, 0x03 },
+ { 0xd004, 0xbf },
+};
+
+static bool rt1316_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f0a:
+ case 0x2f36:
+ case 0x3203 ... 0x320e:
+ case 0xc000 ... 0xc7b4:
+ case 0xcf00 ... 0xcf03:
+ case 0xd101 ... 0xd103:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1316_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc093:
+ case 0xc09d:
+ case 0xc0a3:
+ case 0xc201:
+ case 0xc427 ... 0xc428:
+ case 0xd102:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1316_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1316_readable_register,
+ .volatile_reg = rt1316_volatile_register,
+ .max_register = 0x4108ffff,
+ .reg_defaults = rt1316_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1316_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x04; /* BITMAP: 00000100 */
+ prop->sink_ports = 0x2; /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt1316_apply_bq_params(struct rt1316_sdw_priv *rt1316)
+{
+ unsigned int i, reg, data;
+
+ for (i = 0; i < rt1316->bq_params_cnt; i += 3) {
+ reg = rt1316->bq_params[i] | (rt1316->bq_params[i + 1] << 8);
+ data = rt1316->bq_params[i + 2];
+ regmap_write(rt1316->regmap, reg, data);
+ }
+}
+
+static int rt1316_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+
+ if (rt1316->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1316->regmap, false);
+ if (rt1316->first_hw_init) {
+ regcache_cache_bypass(rt1316->regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* sw reset */
+ regmap_write(rt1316->regmap, 0xc000, 0x02);
+
+ /* initial settings - blind write */
+ regmap_multi_reg_write(rt1316->regmap, rt1316_blind_write,
+ ARRAY_SIZE(rt1316_blind_write));
+
+ if (rt1316->first_hw_init) {
+ regcache_cache_bypass(rt1316->regmap, false);
+ regcache_mark_dirty(rt1316->regmap);
+ } else
+ rt1316->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1316->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1316_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1316->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1316->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1316_io_init(&slave->dev, slave);
+}
+
+static int rt1316_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1316_pde24_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1316->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24,
+ RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const char * const rt1316_rx_data_ch_select[] = {
+ "L,R",
+ "L,L",
+ "L,R",
+ "L,L+R",
+ "R,L",
+ "R,R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0,
+ rt1316_rx_data_ch_select);
+
+static const char * const rt1316_dac_output_vol_select[] = {
+ "immediately",
+ "zero crossing",
+ "zero crossing with soft ramp",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_dac_vol_ctl_enum,
+ 0xc010, 6, rt1316_dac_output_vol_select);
+
+static const struct snd_kcontrol_new rt1316_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1316_rx_data_ch_enum),
+
+ /* XU24 Bypass Control */
+ SOC_SINGLE("XU24 Bypass Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0, 1, 0),
+
+ /* Left/Right IV tag */
+ SOC_SINGLE("Left V Tag Select", 0x3004, 0, 7, 0),
+ SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),
+ SOC_SINGLE("Right V Tag Select", 0x3005, 0, 7, 0),
+ SOC_SINGLE("Right I Tag Select", 0x3005, 4, 7, 0),
+
+ /* IV mixer Control */
+ SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1),
+ SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1),
+
+ /* DAC Output Volume Control */
+ SOC_ENUM("DAC Output Vol Control", rt1316_dac_vol_ctl_enum),
+};
+
+static const struct snd_kcontrol_new rt1316_sto_dac =
+ SOC_DAPM_DOUBLE_R("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1316_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1316_sto_dac),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1316_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 24", SND_SOC_NOPM, 0, 0,
+ rt1316_pde24_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("I Gen"),
+ SND_SOC_DAPM_SIGGEN("V Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1316_dapm_routes[] = {
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+
+ { "I Sense", NULL, "I Gen" },
+ { "V Sense", NULL, "V Gen" },
+ { "I Sense", NULL, "PDE 24" },
+ { "V Sense", NULL, "PDE 24" },
+ { "DP2TX", NULL, "I Sense" },
+ { "DP2TX", NULL, "V Sense" },
+};
+
+static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1316_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1316_sdw_priv *rt1316 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1316->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1316_sdw_priv *rt1316 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1316->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1316->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1316_slave_ops = {
+ .read_prop = rt1316_read_prop,
+ .update_status = rt1316_update_status,
+};
+
+static int rt1316_sdw_parse_dt(struct rt1316_sdw_priv *rt1316, struct device *dev)
+{
+ int ret = 0;
+
+ device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1316->bq_params_cnt);
+ if (rt1316->bq_params_cnt) {
+ rt1316->bq_params = devm_kzalloc(dev, rt1316->bq_params_cnt, GFP_KERNEL);
+ if (!rt1316->bq_params) {
+ dev_err(dev, "%s: Could not allocate bq_params memory\n", __func__);
+ ret = -ENOMEM;
+ } else {
+ ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1316->bq_params, rt1316->bq_params_cnt);
+ if (ret < 0)
+ dev_err(dev, "%s: Could not read list of realtek,bq-params\n", __func__);
+ }
+ }
+
+ dev_dbg(dev, "bq_params_cnt=%d\n", rt1316->bq_params_cnt);
+ return ret;
+}
+
+static int rt1316_sdw_component_probe(struct snd_soc_component *component)
+{
+ struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt1316->component = component;
+ rt1316_sdw_parse_dt(rt1316, &rt1316->sdw_slave->dev);
+
+ if (!rt1316->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ /* apply BQ params */
+ rt1316_apply_bq_params(rt1316);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1316 = {
+ .probe = rt1316_sdw_component_probe,
+ .controls = rt1316_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1316_snd_controls),
+ .dapm_widgets = rt1316_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets),
+ .dapm_routes = rt1316_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1316_aif_dai_ops = {
+ .hw_params = rt1316_sdw_hw_params,
+ .hw_free = rt1316_sdw_pcm_hw_free,
+ .set_stream = rt1316_set_sdw_stream,
+ .shutdown = rt1316_sdw_shutdown,
+};
+
+#define RT1316_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1316_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1316_sdw_dai[] = {
+ {
+ .name = "rt1316-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1316_STEREO_RATES,
+ .formats = RT1316_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1316_STEREO_RATES,
+ .formats = RT1316_FORMATS,
+ },
+ .ops = &rt1316_aif_dai_ops,
+ },
+};
+
+static int rt1316_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1316_sdw_priv *rt1316;
+ int ret;
+
+ rt1316 = devm_kzalloc(dev, sizeof(*rt1316), GFP_KERNEL);
+ if (!rt1316)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1316);
+ rt1316->sdw_slave = slave;
+ rt1316->regmap = regmap;
+
+ regcache_cache_only(rt1316->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1316->hw_init = false;
+ rt1316->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1316,
+ rt1316_sdw_dai,
+ ARRAY_SIZE(rt1316_sdw_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static int rt1316_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1316_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1316_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt1316_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1316_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1316, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1316_id);
+
+static int rt1316_dev_suspend(struct device *dev)
+{
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+
+ if (!rt1316->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1316->regmap, true);
+
+ return 0;
+}
+
+#define RT1316_PROBE_TIMEOUT 5000
+
+static int rt1316_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1316->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1316_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1316->regmap, false);
+ regcache_sync(rt1316->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1316_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume)
+ RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1316_sdw_driver = {
+ .driver = {
+ .name = "rt1316-sdca",
+ .pm = pm_ptr(&rt1316_pm),
+ },
+ .probe = rt1316_sdw_probe,
+ .remove = rt1316_sdw_remove,
+ .ops = &rt1316_slave_ops,
+ .id_table = rt1316_id,
+};
+module_sdw_driver(rt1316_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1316 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h
new file mode 100644
index 000000000000..dc1bfe40edd3
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1316-sdw.h -- RT1316 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1316_SDW_H__
+#define __RT1316_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* RT1316 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1316 SDCA entity */
+#define RT1316_SDCA_ENT_PDE23 0x31
+#define RT1316_SDCA_ENT_PDE27 0x32
+#define RT1316_SDCA_ENT_PDE22 0x33
+#define RT1316_SDCA_ENT_PDE24 0x34
+#define RT1316_SDCA_ENT_XU24 0x24
+#define RT1316_SDCA_ENT_FU21 0x03
+#define RT1316_SDCA_ENT_UDMPU21 0x02
+
+/* RT1316 SDCA control */
+#define RT1316_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1316_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1316_SDCA_CTL_BYPASS 0x01
+#define RT1316_SDCA_CTL_FU_MUTE 0x01
+#define RT1316_SDCA_CTL_FU_VOLUME 0x02
+#define RT1316_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+/* RT1316 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+struct rt1316_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ unsigned char *bq_params;
+ unsigned int bq_params_cnt;
+};
+
+#endif /* __RT1316_SDW_H__ */
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
new file mode 100644
index 000000000000..70db5450d6d2
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318-sdw.c -- rt1318 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2022 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "rt1318-sdw.h"
+
+static const struct reg_sequence rt1318_blind_write[] = {
+ { 0xc001, 0x43 },
+ { 0xc003, 0xa2 },
+ { 0xc004, 0x44 },
+ { 0xc005, 0x44 },
+ { 0xc006, 0x33 },
+ { 0xc007, 0x64 },
+ { 0xc320, 0x20 },
+ { 0xf203, 0x18 },
+ { 0xf211, 0x00 },
+ { 0xf212, 0x26 },
+ { 0xf20d, 0x17 },
+ { 0xf214, 0x06 },
+ { 0xf20e, 0x00 },
+ { 0xf223, 0x7f },
+ { 0xf224, 0xdb },
+ { 0xf225, 0xee },
+ { 0xf226, 0x3f },
+ { 0xf227, 0x0f },
+ { 0xf21a, 0x78 },
+ { 0xf242, 0x3c },
+ { 0xc321, 0x0b },
+ { 0xc200, 0xd8 },
+ { 0xc201, 0x27 },
+ { 0xc202, 0x0f },
+ { 0xf800, 0x20 },
+ { 0xdf00, 0x10 },
+ { 0xdf5f, 0x01 },
+ { 0xdf60, 0xa7 },
+ { 0xc400, 0x0e },
+ { 0xc401, 0x43 },
+ { 0xc402, 0xe0 },
+ { 0xc403, 0x00 },
+ { 0xc404, 0x4c },
+ { 0xc407, 0x02 },
+ { 0xc408, 0x3f },
+ { 0xc300, 0x01 },
+ { 0xc206, 0x78 },
+ { 0xc203, 0x84 },
+ { 0xc120, 0xc0 },
+ { 0xc121, 0x03 },
+ { 0xe000, 0x88 },
+ { 0xc321, 0x09 },
+ { 0xc322, 0x01 },
+ { 0xe706, 0x0f },
+ { 0xe707, 0x30 },
+ { 0xe806, 0x0f },
+ { 0xe807, 0x30 },
+ { 0xed00, 0xb0 },
+ { 0xce04, 0x02 },
+ { 0xce05, 0x63 },
+ { 0xce06, 0x68 },
+ { 0xce07, 0x07 },
+ { 0xcf04, 0x02 },
+ { 0xcf05, 0x63 },
+ { 0xcf06, 0x68 },
+ { 0xcf07, 0x07 },
+ { 0xce60, 0xe3 },
+ { 0xc130, 0x51 },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0xf5 },
+ { 0xf105, 0x06 },
+ { 0xf109, 0x9b },
+ { 0xf10a, 0x0b },
+ { 0xf10b, 0x4c },
+ { 0xf10b, 0x5c },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0xf5 },
+ { 0xf105, 0x0b },
+ { 0xf109, 0x03 },
+ { 0xf10a, 0x0b },
+ { 0xf10b, 0x4c },
+ { 0xf10b, 0x5c },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0xf5 },
+ { 0xf105, 0x0c },
+ { 0xf109, 0x7f },
+ { 0xf10a, 0x0b },
+ { 0xf10b, 0x4c },
+ { 0xf10b, 0x5c },
+
+ { 0xe604, 0x00 },
+ { 0xdb00, 0x0c },
+ { 0xdd00, 0x0c },
+ { 0xdc19, 0x00 },
+ { 0xdc1a, 0xff },
+ { 0xdc1b, 0xff },
+ { 0xdc1c, 0xff },
+ { 0xdc1d, 0x00 },
+ { 0xdc1e, 0x00 },
+ { 0xdc1f, 0x00 },
+ { 0xdc20, 0xff },
+ { 0xde19, 0x00 },
+ { 0xde1a, 0xff },
+ { 0xde1b, 0xff },
+ { 0xde1c, 0xff },
+ { 0xde1d, 0x00 },
+ { 0xde1e, 0x00 },
+ { 0xde1f, 0x00 },
+ { 0xde20, 0xff },
+ { 0xdb32, 0x00 },
+ { 0xdd32, 0x00 },
+ { 0xdb33, 0x0a },
+ { 0xdd33, 0x0a },
+ { 0xdb34, 0x1a },
+ { 0xdd34, 0x1a },
+ { 0xdb17, 0xef },
+ { 0xdd17, 0xef },
+ { 0xdba7, 0x00 },
+ { 0xdba8, 0x64 },
+ { 0xdda7, 0x00 },
+ { 0xdda8, 0x64 },
+ { 0xdb19, 0x40 },
+ { 0xdd19, 0x40 },
+ { 0xdb00, 0x4c },
+ { 0xdb01, 0x79 },
+ { 0xdd01, 0x79 },
+ { 0xdb04, 0x05 },
+ { 0xdb05, 0x03 },
+ { 0xdd04, 0x05 },
+ { 0xdd05, 0x03 },
+ { 0xdbbb, 0x09 },
+ { 0xdbbc, 0x30 },
+ { 0xdbbd, 0xf0 },
+ { 0xdbbe, 0xf1 },
+ { 0xddbb, 0x09 },
+ { 0xddbc, 0x30 },
+ { 0xddbd, 0xf0 },
+ { 0xddbe, 0xf1 },
+ { 0xdb01, 0x79 },
+ { 0xdd01, 0x79 },
+ { 0xdc52, 0xef },
+ { 0xde52, 0xef },
+ { 0x2f55, 0x22 },
+};
+
+static const struct reg_default rt1318_reg_defaults[] = {
+ { 0x3000, 0x00 },
+ { 0x3004, 0x01 },
+ { 0x3005, 0x23 },
+ { 0x3202, 0x00 },
+ { 0x3203, 0x01 },
+ { 0x3206, 0x00 },
+ { 0xc000, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc003, 0x22 },
+ { 0xc004, 0x44 },
+ { 0xc005, 0x44 },
+ { 0xc006, 0x33 },
+ { 0xc007, 0x64 },
+ { 0xc008, 0x05 },
+ { 0xc00a, 0xfc },
+ { 0xc00b, 0x0f },
+ { 0xc00c, 0x0e },
+ { 0xc00d, 0xef },
+ { 0xc00e, 0xe5 },
+ { 0xc00f, 0xff },
+ { 0xc120, 0xc0 },
+ { 0xc121, 0x00 },
+ { 0xc122, 0x00 },
+ { 0xc123, 0x14 },
+ { 0xc125, 0x00 },
+ { 0xc200, 0x00 },
+ { 0xc201, 0x00 },
+ { 0xc202, 0x00 },
+ { 0xc203, 0x04 },
+ { 0xc204, 0x00 },
+ { 0xc205, 0x00 },
+ { 0xc206, 0x68 },
+ { 0xc207, 0x70 },
+ { 0xc208, 0x00 },
+ { 0xc20a, 0x00 },
+ { 0xc20b, 0x01 },
+ { 0xc20c, 0x7f },
+ { 0xc20d, 0x01 },
+ { 0xc20e, 0x7f },
+ { 0xc300, 0x00 },
+ { 0xc301, 0x00 },
+ { 0xc303, 0x80 },
+ { 0xc320, 0x00 },
+ { 0xc321, 0x09 },
+ { 0xc322, 0x02 },
+ { 0xc410, 0x04 },
+ { 0xc430, 0x00 },
+ { 0xc431, 0x00 },
+ { 0xca00, 0x10 },
+ { 0xca01, 0x00 },
+ { 0xca02, 0x0b },
+ { 0xca10, 0x10 },
+ { 0xca11, 0x00 },
+ { 0xca12, 0x0b },
+ { 0xdd93, 0x00 },
+ { 0xdd94, 0x64 },
+ { 0xe300, 0xa0 },
+ { 0xed00, 0x80 },
+ { 0xed01, 0x0f },
+ { 0xed02, 0xff },
+ { 0xed03, 0x00 },
+ { 0xed04, 0x00 },
+ { 0xed05, 0x0f },
+ { 0xed06, 0xff },
+ { 0xf010, 0x10 },
+ { 0xf011, 0xec },
+ { 0xf012, 0x68 },
+ { 0xf013, 0x21 },
+ { 0xf800, 0x00 },
+ { 0xf801, 0x12 },
+ { 0xf802, 0xe0 },
+ { 0xf803, 0x2f },
+ { 0xf804, 0x00 },
+ { 0xf805, 0x00 },
+ { 0xf806, 0x07 },
+ { 0xf807, 0xff },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+};
+
+static bool rt1318_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3000:
+ case 0x3004 ... 0x3005:
+ case 0x3202 ... 0x3203:
+ case 0x3206:
+ case 0xc000 ... 0xc00f:
+ case 0xc120 ... 0xc125:
+ case 0xc200 ... 0xc20e:
+ case 0xc300 ... 0xc303:
+ case 0xc320 ... 0xc322:
+ case 0xc410:
+ case 0xc430 ... 0xc431:
+ case 0xca00 ... 0xca02:
+ case 0xca10 ... 0xca12:
+ case 0xcb00 ... 0xcb0b:
+ case 0xcc00 ... 0xcce5:
+ case 0xcd00 ... 0xcde5:
+ case 0xce00 ... 0xce6a:
+ case 0xcf00 ... 0xcf53:
+ case 0xd000 ... 0xd0cc:
+ case 0xd100 ... 0xd1b9:
+ case 0xdb00 ... 0xdc53:
+ case 0xdd00 ... 0xde53:
+ case 0xdf00 ... 0xdf6b:
+ case 0xe300:
+ case 0xeb00 ... 0xebcc:
+ case 0xec00 ... 0xecb9:
+ case 0xed00 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case 0xf800 ... 0xf807:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1318_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3000 ... 0x3001:
+ case 0xc000:
+ case 0xc301:
+ case 0xc410:
+ case 0xc430 ... 0xc431:
+ case 0xdb06:
+ case 0xdb12:
+ case 0xdb1d ... 0xdb1f:
+ case 0xdb35:
+ case 0xdb37:
+ case 0xdb8a ... 0xdb92:
+ case 0xdbc5 ... 0xdbc8:
+ case 0xdc2b ... 0xdc49:
+ case 0xdd0b:
+ case 0xdd12:
+ case 0xdd1d ... 0xdd1f:
+ case 0xdd35:
+ case 0xdd8a ... 0xdd92:
+ case 0xddc5 ... 0xddc8:
+ case 0xde2b ... 0xde44:
+ case 0xdf4a ... 0xdf55:
+ case 0xe224 ... 0xe23b:
+ case 0xea01:
+ case 0xebc5:
+ case 0xebc8:
+ case 0xebcb ... 0xebcc:
+ case 0xed03 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1318_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1318_readable_register,
+ .volatile_reg = rt1318_volatile_register,
+ .max_register = 0x41081488,
+ .reg_defaults = rt1318_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1318_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(2);
+ prop->sink_ports = BIT(1);
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ return 0;
+}
+
+static int rt1318_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
+
+ if (rt1318->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1318->regmap, false);
+ if (rt1318->first_hw_init) {
+ regcache_cache_bypass(rt1318->regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* blind write */
+ regmap_multi_reg_write(rt1318->regmap, rt1318_blind_write,
+ ARRAY_SIZE(rt1318_blind_write));
+
+ if (rt1318->first_hw_init) {
+ regcache_cache_bypass(rt1318->regmap, false);
+ regcache_mark_dirty(rt1318->regmap);
+ }
+
+ /* Mark Slave initialization complete */
+ rt1318->first_hw_init = true;
+ rt1318->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1318_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1318->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1318->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1318_io_init(&slave->dev, slave);
+}
+
+static int rt1318_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1318->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23,
+ RT1318_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1318->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23,
+ RT1318_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1318_rx_data_ch_select[] = {
+ "L,R",
+ "L,L",
+ "L,R",
+ "L,L+R",
+ "R,L",
+ "R,R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1318_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0,
+ rt1318_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1318_snd_controls[] = {
+
+ /* UDMPU Cluster Selection */
+ SOC_ENUM("RX Channel Select", rt1318_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1318_sto_dac =
+ SOC_DAPM_DOUBLE_R("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1318_sto_dac),
+
+ /* Output */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1318_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ /* Input */
+ SND_SOC_DAPM_PGA("FB Data", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("FB Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+
+ { "FB Data", NULL, "FB Gen" },
+ { "DP2TX", NULL, "FB Data" },
+};
+
+static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1318_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_sdw_priv *rt1318 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels, ch_mask;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1318->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ port = 1;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ port = 2;
+ }
+
+ num_channels = params_channels(params);
+ ch_mask = (1 << num_channels) - 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = num_channels;
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ port_config.ch_mask = ch_mask;
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT1318_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT1318_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT1318_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1318_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1318_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1318_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt1318->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_sdw_priv *rt1318 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1318->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1318->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1318_slave_ops = {
+ .read_prop = rt1318_read_prop,
+ .update_status = rt1318_update_status,
+};
+
+static int rt1318_sdw_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->component = component;
+
+ if (!rt1318->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ dev_dbg(&rt1318->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1318 = {
+ .probe = rt1318_sdw_component_probe,
+ .controls = rt1318_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1318_snd_controls),
+ .dapm_widgets = rt1318_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets),
+ .dapm_routes = rt1318_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1318_aif_dai_ops = {
+ .hw_params = rt1318_sdw_hw_params,
+ .hw_free = rt1318_sdw_pcm_hw_free,
+ .set_stream = rt1318_set_sdw_stream,
+ .shutdown = rt1318_sdw_shutdown,
+};
+
+#define RT1318_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt1318_sdw_dai[] = {
+ {
+ .name = "rt1318-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .ops = &rt1318_aif_dai_ops,
+ },
+};
+
+static int rt1318_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1318_sdw_priv *rt1318;
+ int ret;
+
+ rt1318 = devm_kzalloc(dev, sizeof(*rt1318), GFP_KERNEL);
+ if (!rt1318)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1318);
+ rt1318->sdw_slave = slave;
+ rt1318->regmap = regmap;
+
+ regcache_cache_only(rt1318->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1318->hw_init = false;
+ rt1318->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1318,
+ rt1318_sdw_dai,
+ ARRAY_SIZE(rt1318_sdw_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt1318_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1318_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1318_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt1318_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1318_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1318, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1318_id);
+
+static int rt1318_dev_suspend(struct device *dev)
+{
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
+
+ if (!rt1318->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1318->regmap, true);
+ return 0;
+}
+
+#define RT1318_PROBE_TIMEOUT 5000
+
+static int rt1318_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1318->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1318_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_sync(rt1318->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1318_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume)
+ RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1318_sdw_driver = {
+ .driver = {
+ .name = "rt1318-sdca",
+ .pm = pm_ptr(&rt1318_pm),
+ },
+ .probe = rt1318_sdw_probe,
+ .remove = rt1318_sdw_remove,
+ .ops = &rt1318_slave_ops,
+ .id_table = rt1318_id,
+};
+module_sdw_driver(rt1318_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1318 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h
new file mode 100644
index 000000000000..86e83d63a017
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1318-sdw.h -- RT1318 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1318_SDW_H__
+#define __RT1318_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* imp-defined registers */
+#define RT1318_SAPU_SM 0x3203
+
+#define R1318_TCON 0xc203
+#define R1318_TCON_RELATED_1 0xc206
+
+#define R1318_SPK_TEMPERATRUE_PROTECTION_0 0xdb00
+#define R1318_SPK_TEMPERATRUE_PROTECTION_L_4 0xdb08
+#define R1318_SPK_TEMPERATRUE_PROTECTION_R_4 0xdd08
+
+#define R1318_SPK_TEMPERATRUE_PROTECTION_L_6 0xdb12
+#define R1318_SPK_TEMPERATRUE_PROTECTION_R_6 0xdd12
+
+#define RT1318_INIT_RECIPROCAL_REG_L_24 0xdbb5
+#define RT1318_INIT_RECIPROCAL_REG_L_23_16 0xdbb6
+#define RT1318_INIT_RECIPROCAL_REG_L_15_8 0xdbb7
+#define RT1318_INIT_RECIPROCAL_REG_L_7_0 0xdbb8
+#define RT1318_INIT_RECIPROCAL_REG_R_24 0xddb5
+#define RT1318_INIT_RECIPROCAL_REG_R_23_16 0xddb6
+#define RT1318_INIT_RECIPROCAL_REG_R_15_8 0xddb7
+#define RT1318_INIT_RECIPROCAL_REG_R_7_0 0xddb8
+
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_24 0xdbc5
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_23_16 0xdbc6
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_15_8 0xdbc7
+#define RT1318_INIT_R0_RECIPROCAL_SYN_L_7_0 0xdbc8
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_24 0xddc5
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_23_16 0xddc6
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_15_8 0xddc7
+#define RT1318_INIT_R0_RECIPROCAL_SYN_R_7_0 0xddc8
+
+#define RT1318_R0_COMPARE_FLAG_L 0xdb35
+#define RT1318_R0_COMPARE_FLAG_R 0xdd35
+
+#define RT1318_STP_INITIAL_RS_TEMP_H 0xdd93
+#define RT1318_STP_INITIAL_RS_TEMP_L 0xdd94
+
+/* RT1318 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1318 SDCA entity */
+#define RT1318_SDCA_ENT_PDE23 0x31
+#define RT1318_SDCA_ENT_XU24 0x24
+#define RT1318_SDCA_ENT_FU21 0x03
+#define RT1318_SDCA_ENT_UDMPU21 0x02
+#define RT1318_SDCA_ENT_CS21 0x21
+#define RT1318_SDCA_ENT_SAPU 0x29
+
+/* RT1318 SDCA control */
+#define RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1318_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1318_SDCA_CTL_FU_MUTE 0x01
+#define RT1318_SDCA_CTL_FU_VOLUME 0x02
+#define RT1318_SDCA_CTL_UDMPU_CLUSTER 0x10
+#define RT1318_SDCA_CTL_SAPU_PROTECTION_MODE 0x10
+#define RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11
+
+/* RT1318 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* sample frequency index */
+#define RT1318_SDCA_RATE_16000HZ 0x04
+#define RT1318_SDCA_RATE_32000HZ 0x07
+#define RT1318_SDCA_RATE_44100HZ 0x08
+#define RT1318_SDCA_RATE_48000HZ 0x09
+#define RT1318_SDCA_RATE_96000HZ 0x0b
+#define RT1318_SDCA_RATE_192000HZ 0x0d
+
+
+struct rt1318_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+#endif /* __RT1318_SDW_H__ */
diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c
new file mode 100644
index 000000000000..01c58b15fd91
--- /dev/null
+++ b/sound/soc/codecs/rt1318.c
@@ -0,0 +1,1353 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318.c -- RT1318 ALSA SoC audio amplifier driver
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt1318.h>
+
+#include "rt1318.h"
+
+static const struct reg_sequence init_list[] = {
+ { 0x0000C000, 0x01},
+ { 0x0000F20D, 0x00},
+ { 0x0000F212, 0x3E},
+ { 0x0000C001, 0x02},
+ { 0x0000C003, 0x22},
+ { 0x0000C004, 0x44},
+ { 0x0000C005, 0x44},
+ { 0x0000C007, 0x64},
+ { 0x0000C00E, 0xE7},
+ { 0x0000F223, 0x7F},
+ { 0x0000F224, 0xDB},
+ { 0x0000F225, 0xEE},
+ { 0x0000F226, 0x3F},
+ { 0x0000F227, 0x0F},
+ { 0x0000F21A, 0x78},
+ { 0x0000F242, 0x3C},
+ { 0x0000C120, 0x40},
+ { 0x0000C125, 0x03},
+ { 0x0000C321, 0x0A},
+ { 0x0000C200, 0xD8},
+ { 0x0000C201, 0x27},
+ { 0x0000C202, 0x0F},
+ { 0x0000C400, 0x0E},
+ { 0x0000C401, 0x43},
+ { 0x0000C402, 0xE0},
+ { 0x0000C403, 0x00},
+ { 0x0000C404, 0x4C},
+ { 0x0000C406, 0x40},
+ { 0x0000C407, 0x02},
+ { 0x0000C408, 0x3F},
+ { 0x0000C300, 0x01},
+ { 0x0000C125, 0x03},
+ { 0x0000DF00, 0x10},
+ { 0x0000F20B, 0x2A},
+ { 0x0000DF5F, 0x01},
+ { 0x0000DF60, 0xA7},
+ { 0x0000C203, 0x84},
+ { 0x0000C206, 0x78},
+ { 0x0000F10A, 0x09},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x03},
+ { 0x0000F109, 0xE0},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x04},
+ { 0x0000F109, 0x65},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x02},
+ { 0x0000F109, 0x30},
+ { 0x0000F10B, 0x5C},
+ { 0x0000E706, 0x0F},
+ { 0x0000E707, 0x30},
+ { 0x0000E806, 0x0F},
+ { 0x0000E807, 0x30},
+ { 0x0000CE04, 0x03},
+ { 0x0000CE05, 0x5F},
+ { 0x0000CE06, 0xA2},
+ { 0x0000CE07, 0x6B},
+ { 0x0000CF04, 0x03},
+ { 0x0000CF05, 0x5F},
+ { 0x0000CF06, 0xA2},
+ { 0x0000CF07, 0x6B},
+ { 0x0000CE60, 0xE3},
+ { 0x0000C130, 0x51},
+ { 0x0000E000, 0xA8},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x04},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x41001888, 0x00},
+ { 0x0000C121, 0x0B},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x00},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F800, 0x20},
+ { 0x0000CA00, 0x80},
+ { 0x0000CA10, 0x00},
+ { 0x0000CA02, 0x78},
+ { 0x0000CA12, 0x78},
+ { 0x0000ED00, 0x90},
+ { 0x0000E604, 0x00},
+ { 0x0000DB00, 0x0C},
+ { 0x0000DD00, 0x0C},
+ { 0x0000DC19, 0x00},
+ { 0x0000DC1A, 0x6A},
+ { 0x0000DC1B, 0xAA},
+ { 0x0000DC1C, 0xAB},
+ { 0x0000DC1D, 0x00},
+ { 0x0000DC1E, 0x16},
+ { 0x0000DC1F, 0xDB},
+ { 0x0000DC20, 0x6D},
+ { 0x0000DE19, 0x00},
+ { 0x0000DE1A, 0x6A},
+ { 0x0000DE1B, 0xAA},
+ { 0x0000DE1C, 0xAB},
+ { 0x0000DE1D, 0x00},
+ { 0x0000DE1E, 0x16},
+ { 0x0000DE1F, 0xDB},
+ { 0x0000DE20, 0x6D},
+ { 0x0000DB32, 0x00},
+ { 0x0000DD32, 0x00},
+ { 0x0000DB33, 0x0A},
+ { 0x0000DD33, 0x0A},
+ { 0x0000DB34, 0x1A},
+ { 0x0000DD34, 0x1A},
+ { 0x0000DB15, 0xEF},
+ { 0x0000DD15, 0xEF},
+ { 0x0000DB17, 0xEF},
+ { 0x0000DD17, 0xEF},
+ { 0x0000DB94, 0x70},
+ { 0x0000DD94, 0x70},
+ { 0x0000DB19, 0x40},
+ { 0x0000DD19, 0x40},
+ { 0x0000DB12, 0xC0},
+ { 0x0000DD12, 0xC0},
+ { 0x0000DB00, 0x4C},
+ { 0x0000DB04, 0x05},
+ { 0x0000DB05, 0x03},
+ { 0x0000DD04, 0x05},
+ { 0x0000DD05, 0x03},
+ { 0x0000DBBB, 0x09},
+ { 0x0000DBBC, 0x30},
+ { 0x0000DBBD, 0xF0},
+ { 0x0000DBBE, 0xF1},
+ { 0x0000DDBB, 0x09},
+ { 0x0000DDBC, 0x30},
+ { 0x0000DDBD, 0xF0},
+ { 0x0000DDBE, 0xF1},
+ { 0x0000DB01, 0x79},
+ { 0x0000DD01, 0x79},
+ { 0x0000DB08, 0x40},
+ { 0x0000DD08, 0x40},
+ { 0x0000DC52, 0xEF},
+ { 0x0000DE52, 0xEF},
+ { 0x0000DB00, 0xCC},
+ { 0x0000CC2C, 0x00},
+ { 0x0000CC2D, 0x2A},
+ { 0x0000CC2E, 0x83},
+ { 0x0000CC2F, 0xA8},
+ { 0x0000CD2C, 0x00},
+ { 0x0000CD2D, 0x2A},
+ { 0x0000CD2E, 0x83},
+ { 0x0000CD2F, 0xA8},
+ { 0x0000CC24, 0x00},
+ { 0x0000CC25, 0x51},
+ { 0x0000CC26, 0xEB},
+ { 0x0000CC27, 0x85},
+ { 0x0000CD24, 0x00},
+ { 0x0000CD25, 0x51},
+ { 0x0000CD26, 0xEB},
+ { 0x0000CD27, 0x85},
+ { 0x0000CC20, 0x00},
+ { 0x0000CC21, 0x00},
+ { 0x0000CC22, 0x43},
+ { 0x0000CD20, 0x00},
+ { 0x0000CD21, 0x00},
+ { 0x0000CD22, 0x43},
+ { 0x0000CC16, 0x0F},
+ { 0x0000CC17, 0x00},
+ { 0x0000CD16, 0x0F},
+ { 0x0000CD17, 0x00},
+ { 0x0000CC29, 0x5D},
+ { 0x0000CC2A, 0xC0},
+ { 0x0000CD29, 0x5D},
+ { 0x0000CD2A, 0xC0},
+ { 0x0000CC31, 0x20},
+ { 0x0000CC32, 0x00},
+ { 0x0000CC33, 0x00},
+ { 0x0000CC34, 0x00},
+ { 0x0000CD31, 0x20},
+ { 0x0000CD32, 0x00},
+ { 0x0000CD33, 0x00},
+ { 0x0000CD34, 0x00},
+ { 0x0000CC36, 0x79},
+ { 0x0000CC37, 0x99},
+ { 0x0000CC38, 0x99},
+ { 0x0000CC39, 0x99},
+ { 0x0000CD36, 0x79},
+ { 0x0000CD37, 0x99},
+ { 0x0000CD38, 0x99},
+ { 0x0000CD39, 0x99},
+ { 0x0000CC09, 0x00},
+ { 0x0000CC0A, 0x07},
+ { 0x0000CC0B, 0x5F},
+ { 0x0000CC0C, 0x6F},
+ { 0x0000CD09, 0x00},
+ { 0x0000CD0A, 0x07},
+ { 0x0000CD0B, 0x5F},
+ { 0x0000CD0C, 0x6F},
+ { 0x0000CC0E, 0x00},
+ { 0x0000CC0F, 0x03},
+ { 0x0000CC10, 0xAF},
+ { 0x0000CC11, 0xB7},
+ { 0x0000CD0E, 0x00},
+ { 0x0000CD0F, 0x03},
+ { 0x0000CD10, 0xAF},
+ { 0x0000CD11, 0xB7},
+ { 0x0000CCD6, 0x00},
+ { 0x0000CCD7, 0x03},
+ { 0x0000CDD6, 0x00},
+ { 0x0000CDD7, 0x03},
+ { 0x0000CCD8, 0x00},
+ { 0x0000CCD9, 0x03},
+ { 0x0000CDD8, 0x00},
+ { 0x0000CDD9, 0x03},
+ { 0x0000CCDA, 0x00},
+ { 0x0000CCDB, 0x03},
+ { 0x0000CDDA, 0x00},
+ { 0x0000CDDB, 0x03},
+ { 0x0000C320, 0x20},
+ { 0x0000C203, 0x9C},
+};
+
+static const struct reg_default rt1318_reg[] = {
+ { 0xc000, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc003, 0x22 },
+ { 0xc004, 0x44 },
+ { 0xc005, 0x44 },
+ { 0xc006, 0x33 },
+ { 0xc007, 0x64 },
+ { 0xc008, 0x05 },
+ { 0xc00a, 0xfc },
+ { 0xc00b, 0x0f },
+ { 0xc00c, 0x0e },
+ { 0xc00d, 0xef },
+ { 0xc00e, 0xe5 },
+ { 0xc00f, 0xff },
+ { 0xc120, 0xc0 },
+ { 0xc121, 0x00 },
+ { 0xc122, 0x00 },
+ { 0xc123, 0x14 },
+ { 0xc125, 0x00 },
+ { 0xc130, 0x59 },
+ { 0xc200, 0x00 },
+ { 0xc201, 0x00 },
+ { 0xc202, 0x00 },
+ { 0xc203, 0x04 },
+ { 0xc204, 0x00 },
+ { 0xc205, 0x00 },
+ { 0xc206, 0x68 },
+ { 0xc207, 0x70 },
+ { 0xc208, 0x00 },
+ { 0xc20a, 0x00 },
+ { 0xc20b, 0x01 },
+ { 0xc20c, 0x7f },
+ { 0xc20d, 0x01 },
+ { 0xc20e, 0x7f },
+ { 0xc300, 0x00 },
+ { 0xc301, 0x00 },
+ { 0xc303, 0x80 },
+ { 0xc320, 0x00 },
+ { 0xc321, 0x09 },
+ { 0xc322, 0x02 },
+ { 0xc400, 0x00 },
+ { 0xc401, 0x00 },
+ { 0xc402, 0x00 },
+ { 0xc403, 0x00 },
+ { 0xc404, 0x00 },
+ { 0xc405, 0x00 },
+ { 0xc406, 0x00 },
+ { 0xc407, 0x00 },
+ { 0xc408, 0x00 },
+ { 0xc410, 0x04 },
+ { 0xc430, 0x00 },
+ { 0xc431, 0x00 },
+ { 0xca00, 0x10 },
+ { 0xca01, 0x00 },
+ { 0xca02, 0x0b },
+ { 0xca10, 0x10 },
+ { 0xca11, 0x00 },
+ { 0xca12, 0x0b },
+ { 0xce04, 0x08 },
+ { 0xce05, 0x00 },
+ { 0xce06, 0x00 },
+ { 0xce07, 0x00 },
+ { 0xce60, 0x63 },
+ { 0xcf04, 0x08 },
+ { 0xcf05, 0x00 },
+ { 0xcf06, 0x00 },
+ { 0xcf07, 0x00 },
+ { 0xdb00, 0x00 },
+ { 0xdb08, 0x40 },
+ { 0xdb12, 0x00 },
+ { 0xdb35, 0x00 },
+ { 0xdbb5, 0x00 },
+ { 0xdbb6, 0x40 },
+ { 0xdbb7, 0x00 },
+ { 0xdbb8, 0x00 },
+ { 0xdbc5, 0x00 },
+ { 0xdbc6, 0x00 },
+ { 0xdbc7, 0x00 },
+ { 0xdbc8, 0x00 },
+ { 0xdd08, 0x40 },
+ { 0xdd12, 0x00 },
+ { 0xdd35, 0x00 },
+ { 0xddb5, 0x00 },
+ { 0xddb6, 0x40 },
+ { 0xddb7, 0x00 },
+ { 0xddb8, 0x00 },
+ { 0xddc5, 0x00 },
+ { 0xddc6, 0x00 },
+ { 0xddc7, 0x00 },
+ { 0xddc8, 0x00 },
+ { 0xdd93, 0x00 },
+ { 0xdd94, 0x64 },
+ { 0xdf00, 0x00 },
+ { 0xdf5f, 0x00 },
+ { 0xdf60, 0x00 },
+ { 0xe000, 0x08 },
+ { 0xe300, 0xa0 },
+ { 0xe400, 0x22 },
+ { 0xe706, 0x2f },
+ { 0xe707, 0x2f },
+ { 0xe806, 0x2f },
+ { 0xe807, 0x2f },
+ { 0xea00, 0x43 },
+ { 0xed00, 0x80 },
+ { 0xed01, 0x0f },
+ { 0xed02, 0xff },
+ { 0xed03, 0x00 },
+ { 0xed04, 0x00 },
+ { 0xed05, 0x0f },
+ { 0xed06, 0xff },
+ { 0xf010, 0x10 },
+ { 0xf011, 0xec },
+ { 0xf012, 0x68 },
+ { 0xf013, 0x21 },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0x00 },
+ { 0xf105, 0x00 },
+ { 0xf106, 0x00 },
+ { 0xf107, 0x00 },
+ { 0xf108, 0x00 },
+ { 0xf109, 0x00 },
+ { 0xf10a, 0x03 },
+ { 0xf10b, 0x40 },
+ { 0xf20b, 0x28 },
+ { 0xf20d, 0x00 },
+ { 0xf212, 0x00 },
+ { 0xf21a, 0x00 },
+ { 0xf223, 0x40 },
+ { 0xf224, 0x00 },
+ { 0xf225, 0x00 },
+ { 0xf226, 0x00 },
+ { 0xf227, 0x00 },
+ { 0xf242, 0x0c },
+ { 0xf800, 0x00 },
+ { 0xf801, 0x12 },
+ { 0xf802, 0xe0 },
+ { 0xf803, 0x2f },
+ { 0xf804, 0x00 },
+ { 0xf805, 0x00 },
+ { 0xf806, 0x07 },
+ { 0xf807, 0xff },
+};
+
+static bool rt1318_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc301:
+ case 0xc410:
+ case 0xc430 ... 0xc431:
+ case 0xdb06:
+ case 0xdb12:
+ case 0xdb1d ... 0xdb1f:
+ case 0xdb35:
+ case 0xdb37:
+ case 0xdb8a ... 0xdb92:
+ case 0xdbc5 ... 0xdbc8:
+ case 0xdc2b ... 0xdc49:
+ case 0xdd0b:
+ case 0xdd12:
+ case 0xdd1d ... 0xdd1f:
+ case 0xdd35:
+ case 0xdd8a ... 0xdd92:
+ case 0xddc5 ... 0xddc8:
+ case 0xde2b ... 0xde44:
+ case 0xdf4a ... 0xdf55:
+ case 0xe224 ... 0xe23b:
+ case 0xea01:
+ case 0xebc5:
+ case 0xebc8:
+ case 0xebcb ... 0xebcc:
+ case 0xed03 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1318_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc00f:
+ case 0xc120 ... 0xc130:
+ case 0xc200 ... 0xc20e:
+ case 0xc300 ... 0xc303:
+ case 0xc320 ... 0xc322:
+ case 0xc400 ... 0xc408:
+ case 0xc430 ... 0xc431:
+ case 0xca00 ... 0xca02:
+ case 0xca10 ... 0xca12:
+ case 0xcb00 ... 0xcb0b:
+ case 0xcc00 ... 0xcce5:
+ case 0xcd00 ... 0xcde5:
+ case 0xce00 ... 0xce6a:
+ case 0xcf00 ... 0xcf53:
+ case 0xd000 ... 0xd0cc:
+ case 0xd100 ... 0xd1b9:
+ case 0xdb00 ... 0xdc53:
+ case 0xdd00 ... 0xde53:
+ case 0xdf00 ... 0xdf6b:
+ case 0xe000:
+ case 0xe300:
+ case 0xe400:
+ case 0xe706 ... 0xe707:
+ case 0xe806 ... 0xe807:
+ case 0xea00:
+ case 0xeb00 ... 0xebcc:
+ case 0xec00 ... 0xecb9:
+ case 0xed00 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case 0xf102 ... 0xf10b:
+ case 0xf20b:
+ case 0xf20d ... 0xf242:
+ case 0xf800 ... 0xf807:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1318_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1318_dvol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->rt1318_dvol = ucontrol->value.integer.value[0];
+
+ if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) {
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rt1318_dvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1318->rt1318_dvol;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1318_snd_controls[] = {
+ SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0,
+ rt1318_dvol_get, rt1318_dvol_put),
+};
+
+static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ /* DACs */
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ rt1318_dac_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("Amp"),
+};
+
+static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
+ {"DAC", NULL, "AIF1RX"},
+ {"Amp", NULL, "DAC"},
+};
+
+static int rt1318_get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 4, 8, 16, 24};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (lrclk) {
+ case RT1318_LRCLK_48000:
+ case RT1318_LRCLK_44100:
+ case RT1318_LRCLK_16000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4);
+ break;
+ case RT1318_LRCLK_96000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2);
+ break;
+ case RT1318_LRCLK_192000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock rate.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt1318_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int data_len = 0, ch_len = 0;
+ int pre_div, ret;
+
+ rt1318->lrck = params_rate(params);
+ pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ ret = rt1318_clk_ip_info(component, rt1318->lrck);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data_len = RT1318_I2S_DL_20;
+ ch_len = RT1318_I2S_DL_20;
+ break;
+ case 24:
+ data_len = RT1318_I2S_DL_24;
+ ch_len = RT1318_I2S_DL_24;
+ break;
+ case 32:
+ data_len = RT1318_I2S_DL_32;
+ ch_len = RT1318_I2S_DL_32;
+ break;
+ case 8:
+ data_len = RT1318_I2S_DL_8;
+ ch_len = RT1318_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_CLK2,
+ RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK,
+ pre_div << RT1318_DIV_AP_SFT |
+ pre_div << RT1318_DIV_DAMOD_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK3,
+ RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK,
+ pre_div << RT1318_AD_STO1_SFT |
+ pre_div << RT1318_AD_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK4,
+ RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK,
+ pre_div << RT1318_AD_ANA_STO1_SFT |
+ pre_div << RT1318_AD_ANA_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK5,
+ RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK,
+ pre_div << RT1318_DIV_FIFO_IN_SFT |
+ pre_div << RT1318_DIV_FIFO_OUT_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK6,
+ RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK |
+ RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT |
+ pre_div << RT1318_DIV_AD_MONO_SFT |
+ pre_div << RT1318_DIV_POST_G_SFT);
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK,
+ ch_len << RT1318_I2S_TX_CHL_SFT |
+ ch_len << RT1318_I2S_RX_CHL_SFT);
+
+ return 0;
+}
+
+static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg_val2 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val2 |= RT1318_TDM_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1318_FMT_LEFT_J;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1318_FMT_PCM_A_R;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1318_FMT_PCM_B_R;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_I2S_FMT_MASK, reg_val);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_TDM_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int reg_val = 0;
+
+ if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1318_SCLK_S_BCLK:
+ reg_val |= RT1318_SYSCLK_BCLK;
+ break;
+ case RT1318_SCLK_S_SDW:
+ reg_val |= RT1318_SYSCLK_SDW;
+ break;
+ case RT1318_SCLK_S_PLL2F:
+ reg_val |= RT1318_SYSCLK_PLL2F;
+ break;
+ case RT1318_SCLK_S_PLL2B:
+ reg_val |= RT1318_SYSCLK_PLL2B;
+ break;
+ case RT1318_SCLK_S_MCLK:
+ reg_val |= RT1318_SYSCLK_MCLK;
+ break;
+ case RT1318_SCLK_S_RC0:
+ reg_val |= RT1318_SYSCLK_RC1;
+ break;
+ case RT1318_SCLK_S_RC1:
+ reg_val |= RT1318_SYSCLK_RC2;
+ break;
+ case RT1318_SCLK_S_RC2:
+ reg_val |= RT1318_SYSCLK_RC3;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1318->sysclk = freq;
+ rt1318->sysclk_src = clk_id;
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_SYSCLK_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static const struct pll_calc_map pll_preset_table[] = {
+ {512000, 4096000, 22, 190, 0, true, false},
+ {1024000, 4096000, 22, 94, 0, true, false},
+ {1024000, 16384000, 4, 190, 0, true, false},
+ {1411200, 11289600, 6, 62, 0, true, false},
+ {1536000, 12288000, 6, 62, 0, true, false},
+ {2822400, 11289600, 6, 62, 0, true, false},
+ {2822400, 45158400, 0, 62, 0, true, false},
+ {2822400, 49152000, 0, 62, 0, true, false},
+ {3072000, 12288000, 6, 62, 0, true, false},
+ {3072000, 24576000, 2, 62, 0, true, false},
+ {3072000, 49152000, 0, 62, 0, true, false},
+ {6144000, 24576000, 2, 94, 4, false, false},
+ {6144000, 49152000, 0, 30, 0, true, false},
+ {6144000, 98304000, 0, 94, 4, false, true},
+ {12288000, 49152000, 0, 62, 6, false, false},
+};
+
+static int rt1318_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rt1318_pll_code *pll_code)
+{
+ int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX;
+ int i, k, red, n_t, pll_out, in_t, out_t;
+ int n = 0, m = 0, m_t = 0;
+ int red_t = abs(freq_out - freq_in);
+ bool m_bypass = false, k_bypass = false;
+
+ if (RT1318_PLL_INP_MAX < freq_in || RT1318_PLL_INP_MIN > freq_in)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) {
+ if (freq_in == pll_preset_table[i].pll_in &&
+ freq_out == pll_preset_table[i].pll_out) {
+ k = pll_preset_table[i].k;
+ m = pll_preset_table[i].m;
+ n = pll_preset_table[i].n;
+ m_bypass = pll_preset_table[i].m_bp;
+ k_bypass = pll_preset_table[i].k_bp;
+ goto code_find;
+ }
+ }
+
+ k = 100000000 / freq_out - 2;
+ if (k > RT1318_PLL_K_MAX)
+ k = RT1318_PLL_K_MAX;
+ if (k < 0) {
+ k = 0;
+ k_bypass = true;
+ }
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = freq_in / (k_bypass ? 1 : (k + 2));
+ pll_out = freq_out / (n_t + 2);
+ if (in_t < 0)
+ continue;
+ if (in_t == pll_out) {
+ m_bypass = true;
+ n = n_t;
+ goto code_find;
+ }
+ red = abs(in_t - pll_out);
+ if (red < red_t) {
+ m_bypass = true;
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / (m_t + 2);
+ red = abs(out_t - pll_out);
+ if (red < red_t) {
+ m_bypass = false;
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+
+ pll_code->m_bp = m_bypass;
+ pll_code->k_bp = k_bypass;
+ pll_code->m_code = m;
+ pll_code->n_code = n;
+ pll_code->k_code = k;
+ return 0;
+}
+
+static int rt1318_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ struct rt1318_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt1318->pll_in = 0;
+ rt1318->pll_out = 0;
+ return 0;
+ }
+
+ if (source == rt1318->pll_src && freq_in == rt1318->pll_in &&
+ freq_out == rt1318->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1318_PLL_S_BCLK0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0);
+ break;
+ case RT1318_PLL_S_BCLK1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1);
+ break;
+ case RT1318_PLL_S_RC:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_RC);
+ break;
+ case RT1318_PLL_S_MCLK:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK);
+ break;
+ case RT1318_PLL_S_SDW_IN_PLL:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1);
+ break;
+ case RT1318_PLL_S_SDW_0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2);
+ break;
+ case RT1318_PLL_S_SDW_1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3);
+ break;
+ case RT1318_PLL_S_SDW_2:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rt1318_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_K,
+ RT1318_K_PLL1_MASK, pll_code.k_code);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_M,
+ RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code));
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8,
+ RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0,
+ RT1318_N_7_0_PLL1_MASK, pll_code.n_code);
+
+ rt1318->pll_in = freq_in;
+ rt1318->pll_out = freq_out;
+ rt1318->pll_src = source;
+
+ return 0;
+}
+
+static int rt1318_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned int cn = 0, cl = 0, rx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 4:
+ cn |= RT1318_I2S_CH_TX_4CH;
+ cn |= RT1318_I2S_CH_RX_4CH;
+ break;
+ case 6:
+ cn |= RT1318_I2S_CH_TX_6CH;
+ cn |= RT1318_I2S_CH_RX_6CH;
+ break;
+ case 8:
+ cn |= RT1318_I2S_CH_TX_8CH;
+ cn |= RT1318_I2S_CH_RX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ cl |= RT1318_I2S_TX_CHL_20;
+ cl |= RT1318_I2S_RX_CHL_20;
+ break;
+ case 24:
+ cl |= RT1318_I2S_TX_CHL_24;
+ cl |= RT1318_I2S_RX_CHL_24;
+ break;
+ case 32:
+ cl |= RT1318_I2S_TX_CHL_32;
+ cl |= RT1318_I2S_RX_CHL_32;
+ break;
+ case 8:
+ cl |= RT1318_I2S_TX_CHL_8;
+ cl |= RT1318_I2S_RX_CHL_8;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum != 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1318_probe(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->component = component;
+
+ schedule_work(&rt1318->cali_work);
+ rt1318->rt1318_dvol = RT1318_DVOL_STEP;
+
+ return 0;
+}
+
+static void rt1318_remove(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&rt1318->cali_work);
+}
+
+#ifdef CONFIG_PM
+static int rt1318_suspend(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, true);
+ regcache_mark_dirty(rt1318->regmap);
+ return 0;
+}
+
+static int rt1318_resume(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_sync(rt1318->regmap);
+ return 0;
+}
+#else
+#define rt1318_suspend NULL
+#define rt1318_resume NULL
+#endif
+
+#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1318_aif_dai_ops = {
+ .hw_params = rt1318_hw_params,
+ .set_fmt = rt1318_set_dai_fmt,
+ .set_sysclk = rt1318_set_dai_sysclk,
+ .set_pll = rt1318_set_dai_pll,
+ .set_tdm_slot = rt1318_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1318_dai[] = {
+ {
+ .name = "rt1318-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .ops = &rt1318_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1318 = {
+ .probe = rt1318_probe,
+ .remove = rt1318_remove,
+ .suspend = rt1318_suspend,
+ .resume = rt1318_resume,
+ .controls = rt1318_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1318_snd_controls),
+ .dapm_widgets = rt1318_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets),
+ .dapm_routes = rt1318_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1318_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1318_readable_register,
+ .volatile_reg = rt1318_volatile_register,
+ .max_register = 0x41001888,
+ .reg_defaults = rt1318_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1318_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt1318_i2c_id[] = {
+ { "rt1318" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id);
+
+static const struct of_device_id rt1318_of_match[] = {
+ { .compatible = "realtek,rt1318", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1318_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1318_acpi_match[] = {
+ { "10EC1318" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match);
+#endif
+
+static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,r0_l",
+ &rt1318->pdata.init_r0_l);
+ device_property_read_u32(dev, "realtek,r0_r",
+ &rt1318->pdata.init_r0_r);
+
+ return 0;
+}
+
+static void rt1318_calibration_sequence(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_CLK1, 0x22);
+ regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40);
+ regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20);
+ regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00);
+ regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x1C);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2);
+}
+
+static void rt1318_r0_calculate(struct rt1318_priv *rt1318)
+{
+ unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3;
+ unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3;
+ unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor;
+ unsigned int format = 16777216; /* 2^24 */
+
+ regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3);
+ r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3;
+ r0_l_integer = format / r0_l;
+ r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10;
+
+ regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3);
+ r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3;
+ r0_r_integer = format / r0_r;
+ r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10;
+
+ dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor);
+ dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor);
+}
+
+static void rt1318_r0_restore(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24,
+ (rt1318->pdata.init_r0_l >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16,
+ (rt1318->pdata.init_r0_l >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8,
+ (rt1318->pdata.init_r0_l >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0,
+ (rt1318->pdata.init_r0_l >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24,
+ (rt1318->pdata.init_r0_r >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16,
+ (rt1318->pdata.init_r0_r >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8,
+ (rt1318->pdata.init_r0_r >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0,
+ (rt1318->pdata.init_r0_r >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x9c);
+}
+
+static int rt1318_calibrate(struct rt1318_priv *rt1318)
+{
+ int chk_cnt = 30, count = 0;
+ int val, val2;
+
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1);
+ usleep_range(0, 10000);
+ rt1318_calibration_sequence(rt1318);
+
+ while (count < chk_cnt) {
+ msleep(100);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ val = (val >> 1) & 0x1;
+ val2 = (val2 >> 1) & 0x1;
+ if (val & val2) {
+ dev_dbg(rt1318->component->dev, "Calibration done.\n");
+ break;
+ }
+ count++;
+ if (count == chk_cnt) {
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ return RT1318_R0_CALIB_NOT_DONE;
+ }
+ }
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ if ((val & 0x1) & (val2 & 0x1))
+ return RT1318_R0_IN_RANGE;
+ else
+ return RT1318_R0_OUT_OF_RANGE;
+}
+
+static void rt1318_calibration_work(struct work_struct *work)
+{
+ struct rt1318_priv *rt1318 =
+ container_of(work, struct rt1318_priv, cali_work);
+ int ret;
+
+ if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r)
+ rt1318_r0_restore(rt1318);
+ else {
+ ret = rt1318_calibrate(rt1318);
+ if (ret == RT1318_R0_IN_RANGE)
+ rt1318_r0_calculate(rt1318);
+ dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret);
+ }
+}
+
+static int rt1318_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt1318_priv *rt1318;
+ int ret, val, val2, dev_id;
+
+ rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv),
+ GFP_KERNEL);
+ if (!rt1318)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1318);
+
+ if (pdata)
+ rt1318->pdata = *pdata;
+ else
+ rt1318_parse_dt(rt1318, &i2c->dev);
+
+ rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap);
+ if (IS_ERR(rt1318->regmap)) {
+ ret = PTR_ERR(rt1318->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val);
+ regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2);
+ dev_id = (val << 8) | val2;
+ if (dev_id != 0x6821) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt1318\n",
+ dev_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_register_patch(rt1318->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ INIT_WORK(&rt1318->cali_work, rt1318_calibration_work);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai));
+}
+
+static struct i2c_driver rt1318_i2c_driver = {
+ .driver = {
+ .name = "rt1318",
+ .of_match_table = of_match_ptr(rt1318_of_match),
+ .acpi_match_table = ACPI_PTR(rt1318_acpi_match),
+ },
+ .probe = rt1318_i2c_probe,
+ .id_table = rt1318_i2c_id,
+};
+module_i2c_driver(rt1318_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1318 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h
new file mode 100644
index 000000000000..cec40b484216
--- /dev/null
+++ b/sound/soc/codecs/rt1318.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1318.h -- Platform data for RT1318
+ *
+ * Copyright 2024 Realtek Semiconductor Corp.
+ */
+#include <sound/rt1318.h>
+
+#ifndef __RT1318_H__
+#define __RT1318_H__
+
+struct rt1318_priv {
+ struct snd_soc_component *component;
+ struct rt1318_platform_data pdata;
+ struct work_struct cali_work;
+ struct regmap *regmap;
+
+ unsigned int r0_l_integer;
+ unsigned int r0_l_factor;
+ unsigned int r0_r_integer;
+ unsigned int r0_r_factor;
+ int rt1318_init;
+ int rt1318_dvol;
+ int sysclk_src;
+ int sysclk;
+ int lrck;
+ int bclk;
+ int master;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#define RT1318_PLL_INP_MAX 40000000
+#define RT1318_PLL_INP_MIN 256000
+#define RT1318_PLL_N_MAX 0x1ff
+#define RT1318_PLL_K_MAX 0x1f
+#define RT1318_PLL_M_MAX 0x1f
+
+#define RT1318_LRCLK_192000 192000
+#define RT1318_LRCLK_96000 96000
+#define RT1318_LRCLK_48000 48000
+#define RT1318_LRCLK_44100 44100
+#define RT1318_LRCLK_16000 16000
+#define RT1318_DVOL_STEP 383
+
+#define RT1318_CLK1 0xc001
+#define RT1318_CLK2 0xc003
+#define RT1318_CLK3 0xc004
+#define RT1318_CLK4 0xc005
+#define RT1318_CLK5 0xc006
+#define RT1318_CLK6 0xc007
+#define RT1318_CLK7 0xc008
+#define RT1318_PWR_STA1 0xc121
+#define RT1318_SPK_VOL_TH 0xc130
+#define RT1318_TCON 0xc203
+#define RT1318_SRC_TCON 0xc204
+#define RT1318_TCON_RELATE 0xc206
+#define RT1318_DA_VOL_L_8 0xc20b
+#define RT1318_DA_VOL_L_1_7 0xc20c
+#define RT1318_DA_VOL_R_8 0xc20d
+#define RT1318_DA_VOL_R_1_7 0xc20e
+#define RT1318_FEEDBACK_PATH 0xc321
+#define RT1318_STP_TEMP_L 0xdb00
+#define RT1318_STP_SEL_L 0xdb08
+#define RT1318_STP_R0_EN_L 0xdb12
+#define RT1318_R0_CMP_L_FLAG 0xdb35
+#define RT1318_PRE_R0_L_24 0xdbb5
+#define RT1318_PRE_R0_L_23_16 0xdbb6
+#define RT1318_PRE_R0_L_15_8 0xdbb7
+#define RT1318_PRE_R0_L_7_0 0xdbb8
+#define RT1318_R0_L_24 0xdbc5
+#define RT1318_R0_L_23_16 0xdbc6
+#define RT1318_R0_L_15_8 0xdbc7
+#define RT1318_R0_L_7_0 0xdbc8
+#define RT1318_STP_SEL_R 0xdd08
+#define RT1318_STP_R0_EN_R 0xdd12
+#define RT1318_R0_CMP_R_FLAG 0xdd35
+#define RT1318_PRE_R0_R_24 0xddb5
+#define RT1318_PRE_R0_R_23_16 0xddb6
+#define RT1318_PRE_R0_R_15_8 0xddb7
+#define RT1318_PRE_R0_R_7_0 0xddb8
+#define RT1318_R0_R_24 0xddc5
+#define RT1318_R0_R_23_16 0xddc6
+#define RT1318_R0_R_15_8 0xddc7
+#define RT1318_R0_R_7_0 0xddc8
+#define RT1318_DEV_ID1 0xf012
+#define RT1318_DEV_ID2 0xf013
+#define RT1318_PLL1_K 0xf20d
+#define RT1318_PLL1_M 0xf20f
+#define RT1318_PLL1_N_8 0xf211
+#define RT1318_PLL1_N_7_0 0xf212
+#define RT1318_SINE_GEN0 0xf800
+#define RT1318_TDM_CTRL1 0xf900
+#define RT1318_TDM_CTRL2 0xf901
+#define RT1318_TDM_CTRL3 0xf902
+#define RT1318_TDM_CTRL9 0xf908
+
+
+/* Clock-1 (0xC001) */
+#define RT1318_PLLIN_MASK (0x7 << 4)
+#define RT1318_PLLIN_BCLK0 (0x0 << 4)
+#define RT1318_PLLIN_BCLK1 (0x1 << 4)
+#define RT1318_PLLIN_RC (0x2 << 4)
+#define RT1318_PLLIN_MCLK (0x3 << 4)
+#define RT1318_PLLIN_SDW1 (0x4 << 4)
+#define RT1318_PLLIN_SDW2 (0x5 << 4)
+#define RT1318_PLLIN_SDW3 (0x6 << 4)
+#define RT1318_PLLIN_SDW4 (0x7 << 4)
+#define RT1318_SYSCLK_SEL_MASK (0x7 << 0)
+#define RT1318_SYSCLK_BCLK (0x0 << 0)
+#define RT1318_SYSCLK_SDW (0x1 << 0)
+#define RT1318_SYSCLK_PLL2F (0x2 << 0)
+#define RT1318_SYSCLK_PLL2B (0x3 << 0)
+#define RT1318_SYSCLK_MCLK (0x4 << 0)
+#define RT1318_SYSCLK_RC1 (0x5 << 0)
+#define RT1318_SYSCLK_RC2 (0x6 << 0)
+#define RT1318_SYSCLK_RC3 (0x7 << 0)
+/* Clock-2 (0xC003) */
+#define RT1318_DIV_AP_MASK (0x3 << 4)
+#define RT1318_DIV_AP_SFT 4
+#define RT1318_DIV_AP_DIV1 (0x0 << 4)
+#define RT1318_DIV_AP_DIV2 (0x1 << 4)
+#define RT1318_DIV_AP_DIV4 (0x2 << 4)
+#define RT1318_DIV_AP_DIV8 (0x3 << 4)
+#define RT1318_DIV_DAMOD_MASK (0x3 << 0)
+#define RT1318_DIV_DAMOD_SFT 0
+#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0)
+#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0)
+#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0)
+#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0)
+/* Clock-3 (0xC004) */
+#define RT1318_AD_STO1_MASK (0x7 << 4)
+#define RT1318_AD_STO1_SFT 4
+#define RT1318_AD_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_STO2_MASK (0x7 << 0)
+#define RT1318_AD_STO2_SFT 0
+#define RT1318_AD_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_STO2_SFT 0
+/* Clock-4 (0xC005) */
+#define RT1318_AD_ANA_STO1_MASK (0x7 << 4)
+#define RT1318_AD_ANA_STO1_SFT 4
+#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_ANA_STO2_MASK (0x7 << 0)
+#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_ANA_STO2_SFT 0
+/* Clock-5 (0xC006) */
+#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4)
+#define RT1318_DIV_FIFO_IN_SFT 4
+#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4)
+#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4)
+#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4)
+#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4)
+#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_SFT 0
+/* Clock-6 (0xC007) */
+#define RT1318_DIV_NLMS_MASK (0x3 << 6)
+#define RT1318_DIV_NLMS_SFT 6
+#define RT1318_DIV_NLMS_DIV1 (0x0 << 6)
+#define RT1318_DIV_NLMS_DIV2 (0x1 << 6)
+#define RT1318_DIV_NLMS_DIV4 (0x2 << 6)
+#define RT1318_DIV_NLMS_DIV8 (0x3 << 6)
+#define RT1318_DIV_AD_MONO_MASK (0x7 << 3)
+#define RT1318_DIV_AD_MONO_SFT 3
+#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3)
+#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3)
+#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3)
+#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3)
+#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3)
+#define RT1318_DIV_POST_G_MASK (0x7 << 0)
+#define RT1318_DIV_POST_G_SFT 0
+#define RT1318_DIV_POST_G_DIV1 (0x0 << 0)
+#define RT1318_DIV_POST_G_DIV2 (0x1 << 0)
+#define RT1318_DIV_POST_G_DIV4 (0x2 << 0)
+#define RT1318_DIV_POST_G_DIV8 (0x3 << 0)
+#define RT1318_DIV_POST_G_DIV16 (0x4 << 0)
+/* Power Status 1 (0xC121) */
+#define RT1318_PDB_CTRL_MASK (0x1)
+#define RT1318_PDB_CTRL_LOW (0x0)
+#define RT1318_PDB_CTRL_HIGH (0x1)
+#define RT1318_PDB_CTRL_SFT 0
+/* SRC Tcon(0xc204) */
+#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6)
+#define RT1318_SRCIN_IN_48K (0x0 << 6)
+#define RT1318_SRCIN_IN_44P1 (0x1 << 6)
+#define RT1318_SRCIN_IN_32K (0x2 << 6)
+#define RT1318_SRCIN_IN_16K (0x3 << 6)
+#define RT1318_SRCIN_F12288_MASK (0x3 << 4)
+#define RT1318_SRCIN_TCON1 (0x0 << 4)
+#define RT1318_SRCIN_TCON2 (0x1 << 4)
+#define RT1318_SRCIN_TCON4 (0x2 << 4)
+#define RT1318_SRCIN_TCON8 (0x3 << 4)
+#define RT1318_SRCIN_DACLK_MASK (0x3 << 2)
+#define RT1318_DACLK_TCON1 (0x0 << 2)
+#define RT1318_DACLK_TCON2 (0x1 << 2)
+#define RT1318_DACLK_TCON4 (0x2 << 2)
+#define RT1318_DACLK_TCON8 (0x3 << 2)
+/* R0 Compare Flag (0xDB35) */
+#define RT1318_R0_RANGE_MASK (0x1)
+#define RT1318_R0_OUTOFRANGE (0x0)
+#define RT1318_R0_INRANGE (0x1)
+/* PLL internal setting (0xF20D), K value */
+#define RT1318_K_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF20F), M value */
+#define RT1318_M_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF211), N_8 value */
+#define RT1318_N_8_PLL1_MASK (0x1 << 0)
+/* PLL internal setting (0xF212), N_7_0 value */
+#define RT1318_N_7_0_PLL1_MASK (0xff << 0)
+/* TDM CTRL 1 (0xf900) */
+#define RT1318_TDM_BCLK_MASK (0x1 << 7)
+#define RT1318_TDM_BCLK_NORM (0x0 << 7)
+#define RT1318_TDM_BCLK_INV (0x1 << 7)
+#define RT1318_I2S_FMT_MASK (0x7 << 0)
+#define RT1318_FMT_I2S (0x0 << 0)
+#define RT1318_FMT_LEFT_J (0x1 << 0)
+#define RT1318_FMT_PCM_A_R (0x2 << 0)
+#define RT1318_FMT_PCM_B_R (0x3 << 0)
+#define RT1318_FMT_PCM_A_F (0x6 << 0)
+#define RT1318_FMT_PCM_B_F (0x7 << 0)
+#define RT1318_I2S_FMT_SFT 0
+/* TDM CTRL 2 (0xf901) */
+#define RT1318_I2S_CH_TX_MASK (0x3 << 6)
+#define RT1318_I2S_CH_TX_2CH (0x0 << 6)
+#define RT1318_I2S_CH_TX_4CH (0x1 << 6)
+#define RT1318_I2S_CH_TX_6CH (0x2 << 6)
+#define RT1318_I2S_CH_TX_8CH (0x3 << 6)
+#define RT1318_I2S_CH_RX_MASK (0x3 << 4)
+#define RT1318_I2S_CH_RX_2CH (0x0 << 4)
+#define RT1318_I2S_CH_RX_4CH (0x1 << 4)
+#define RT1318_I2S_CH_RX_6CH (0x2 << 4)
+#define RT1318_I2S_CH_RX_8CH (0x3 << 4)
+#define RT1318_I2S_DL_MASK 0x7
+#define RT1318_I2S_DL_SFT 0
+#define RT1318_I2S_DL_16 0x0
+#define RT1318_I2S_DL_20 0x1
+#define RT1318_I2S_DL_24 0x2
+#define RT1318_I2S_DL_32 0x3
+#define RT1318_I2S_DL_8 0x4
+/* TDM CTRL 3 (0xf902) */
+#define RT1318_I2S_TX_CHL_MASK (0x7 << 4)
+#define RT1318_I2S_TX_CHL_SFT 4
+#define RT1318_I2S_TX_CHL_16 (0x0 << 4)
+#define RT1318_I2S_TX_CHL_20 (0x1 << 4)
+#define RT1318_I2S_TX_CHL_24 (0x2 << 4)
+#define RT1318_I2S_TX_CHL_32 (0x3 << 4)
+#define RT1318_I2S_TX_CHL_8 (0x4 << 4)
+#define RT1318_I2S_RX_CHL_MASK (0x7 << 0)
+#define RT1318_I2S_RX_CHL_SFT 0
+#define RT1318_I2S_RX_CHL_16 (0x0 << 0)
+#define RT1318_I2S_RX_CHL_20 (0x1 << 0)
+#define RT1318_I2S_RX_CHL_24 (0x2 << 0)
+#define RT1318_I2S_RX_CHL_32 (0x3 << 0)
+#define RT1318_I2S_RX_CHL_8 (0x4 << 0)
+/* TDM CTRL 9 (0xf908) */
+#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4)
+#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7
+#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4
+#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0
+
+#define RT1318_REG_DISP_LEN 23
+
+/* System Clock Source */
+enum {
+ RT1318_SCLK_S_BCLK,
+ RT1318_SCLK_S_SDW,
+ RT1318_SCLK_S_PLL2F,
+ RT1318_SCLK_S_PLL2B,
+ RT1318_SCLK_S_MCLK,
+ RT1318_SCLK_S_RC0,
+ RT1318_SCLK_S_RC1,
+ RT1318_SCLK_S_RC2,
+};
+
+/* PLL Source */
+enum {
+ RT1318_PLL_S_BCLK0,
+ RT1318_PLL_S_BCLK1,
+ RT1318_PLL_S_RC,
+ RT1318_PLL_S_MCLK,
+ RT1318_PLL_S_SDW_IN_PLL,
+ RT1318_PLL_S_SDW_0,
+ RT1318_PLL_S_SDW_1,
+ RT1318_PLL_S_SDW_2,
+};
+
+/* TDM channel */
+enum {
+ RT1318_2CH,
+ RT1318_4CH,
+ RT1318_6CH,
+ RT1318_8CH,
+};
+
+/* R0 calibration result */
+enum {
+ RT1318_R0_OUT_OF_RANGE,
+ RT1318_R0_IN_RANGE,
+ RT1318_R0_CALIB_NOT_DONE,
+};
+
+/* PLL pre-defined M/N/K */
+
+struct pll_calc_map {
+ unsigned int pll_in;
+ unsigned int pll_out;
+ int k;
+ int n;
+ int m;
+ bool m_bp;
+ bool k_bp;
+};
+
+struct rt1318_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ bool k_bp; /* Indicates bypass k code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+#endif /* __RT1318_H__ */
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
new file mode 100644
index 000000000000..e3f9b03df3aa
--- /dev/null
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -0,0 +1,1823 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1320-sdw.c -- rt1320 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/sdw.h>
+#include "rt1320-sdw.h"
+#include "rt-sdw-common.h"
+
+/*
+ * The 'blind writes' is an SDCA term to deal with platform-specific initialization.
+ * It might include vendor-specific or SDCA control registers.
+ */
+static const struct reg_sequence rt1320_blind_write[] = {
+ { 0xc003, 0xe0 },
+ { 0xc01b, 0xfc },
+ { 0xc5c3, 0xf2 },
+ { 0xc5c2, 0x00 },
+ { 0xc5c6, 0x10 },
+ { 0xc5c4, 0x12 },
+ { 0xc5c8, 0x03 },
+ { 0xc5d8, 0x0a },
+ { 0xc5f7, 0x22 },
+ { 0xc5f6, 0x22 },
+ { 0xc5d0, 0x0f },
+ { 0xc5d1, 0x89 },
+ { 0xc057, 0x51 },
+ { 0xc054, 0x35 },
+ { 0xc053, 0x55 },
+ { 0xc052, 0x55 },
+ { 0xc051, 0x13 },
+ { 0xc050, 0x15 },
+ { 0xc060, 0x77 },
+ { 0xc061, 0x55 },
+ { 0xc063, 0x55 },
+ { 0xc065, 0xa5 },
+ { 0xc06b, 0x0a },
+ { 0xca05, 0xd6 },
+ { 0xca25, 0xd6 },
+ { 0xcd00, 0x05 },
+ { 0xc604, 0x40 },
+ { 0xc609, 0x40 },
+ { 0xc046, 0xff },
+ { 0xc045, 0xff },
+ { 0xc044, 0xff },
+ { 0xc043, 0xff },
+ { 0xc042, 0xff },
+ { 0xc041, 0xff },
+ { 0xc040, 0xff },
+ { 0xcc10, 0x01 },
+ { 0xc700, 0xf0 },
+ { 0xc701, 0x13 },
+ { 0xc901, 0x04 },
+ { 0xc900, 0x73 },
+ { 0xde03, 0x05 },
+ { 0xdd0b, 0x0d },
+ { 0xdd0a, 0xff },
+ { 0xdd09, 0x0d },
+ { 0xdd08, 0xff },
+ { 0xc570, 0x08 },
+ { 0xe803, 0xbe },
+ { 0xc003, 0xc0 },
+ { 0xc081, 0xfe },
+ { 0xce31, 0x0d },
+ { 0xce30, 0xae },
+ { 0xce37, 0x0b },
+ { 0xce36, 0xd2 },
+ { 0xce39, 0x04 },
+ { 0xce38, 0x80 },
+ { 0xce3f, 0x00 },
+ { 0xce3e, 0x00 },
+ { 0xd470, 0x8b },
+ { 0xd471, 0x18 },
+ { 0xc019, 0x10 },
+ { 0xd487, 0x3f },
+ { 0xd486, 0xc3 },
+ { 0x3fc2bfc7, 0x00 },
+ { 0x3fc2bfc6, 0x00 },
+ { 0x3fc2bfc5, 0x00 },
+ { 0x3fc2bfc4, 0x01 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x02 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x11 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x82 },
+ { 0x1000db06, 0x04 },
+ { 0x1000db07, 0xf1 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x40 },
+ { 0x0000d540, 0x01 },
+ { 0xd172, 0x2a },
+ { 0xc5d6, 0x01 },
+ { 0xd478, 0xff },
+};
+
+static const struct reg_sequence rt1320_vc_blind_write[] = {
+ { 0xc003, 0xe0 },
+ { 0xe80a, 0x01 },
+ { 0xc5c3, 0xf3 },
+ { 0xc057, 0x51 },
+ { 0xc054, 0x35 },
+ { 0xca05, 0xd6 },
+ { 0xca07, 0x07 },
+ { 0xca25, 0xd6 },
+ { 0xca27, 0x07 },
+ { 0xc604, 0x40 },
+ { 0xc609, 0x40 },
+ { 0xc046, 0xff },
+ { 0xc045, 0xff },
+ { 0xda81, 0x14 },
+ { 0xda8d, 0x14 },
+ { 0xc044, 0xff },
+ { 0xc043, 0xff },
+ { 0xc042, 0xff },
+ { 0xc041, 0x7f },
+ { 0xc040, 0xff },
+ { 0xcc10, 0x01 },
+ { 0xc700, 0xf0 },
+ { 0xc701, 0x13 },
+ { 0xc901, 0x09 },
+ { 0xc900, 0xd0 },
+ { 0xde03, 0x05 },
+ { 0xdd0b, 0x0d },
+ { 0xdd0a, 0xff },
+ { 0xdd09, 0x0d },
+ { 0xdd08, 0xff },
+ { 0xc570, 0x08 },
+ { 0xc086, 0x02 },
+ { 0xc085, 0x7f },
+ { 0xc084, 0x00 },
+ { 0xc081, 0xfe },
+ { 0xf084, 0x0f },
+ { 0xf083, 0xff },
+ { 0xf082, 0xff },
+ { 0xf081, 0xff },
+ { 0xf080, 0xff },
+ { 0xe802, 0xf8 },
+ { 0xe803, 0xbe },
+ { 0xc003, 0xc0 },
+ { 0xd470, 0xec },
+ { 0xd471, 0x3a },
+ { 0xd474, 0x11 },
+ { 0xd475, 0x32 },
+ { 0xd478, 0xff },
+ { 0xd479, 0x20 },
+ { 0xd47a, 0x10 },
+ { 0xd47c, 0xff },
+ { 0xc019, 0x10 },
+ { 0xd487, 0x0b },
+ { 0xd487, 0x3b },
+ { 0xd486, 0xc3 },
+ { 0xc598, 0x04 },
+ { 0xdb03, 0xf0 },
+ { 0xdb09, 0x00 },
+ { 0xdb08, 0x7a },
+ { 0xdb19, 0x02 },
+ { 0xdb07, 0x5a },
+ { 0xdb05, 0x45 },
+ { 0xd500, 0x00 },
+ { 0xd500, 0x17 },
+ { 0xd600, 0x01 },
+ { 0xd601, 0x02 },
+ { 0xd602, 0x03 },
+ { 0xd603, 0x04 },
+ { 0xd64c, 0x03 },
+ { 0xd64d, 0x03 },
+ { 0xd64e, 0x03 },
+ { 0xd64f, 0x03 },
+ { 0xd650, 0x03 },
+ { 0xd651, 0x03 },
+ { 0xd652, 0x03 },
+ { 0xd610, 0x01 },
+ { 0xd608, 0x03 },
+ { 0xd609, 0x00 },
+ { 0x3fc2bf83, 0x00 },
+ { 0x3fc2bf82, 0x00 },
+ { 0x3fc2bf81, 0x00 },
+ { 0x3fc2bf80, 0x00 },
+ { 0x3fc2bfc7, 0x00 },
+ { 0x3fc2bfc6, 0x00 },
+ { 0x3fc2bfc5, 0x00 },
+ { 0x3fc2bfc4, 0x00 },
+ { 0x3fc2bfc3, 0x00 },
+ { 0x3fc2bfc2, 0x00 },
+ { 0x3fc2bfc1, 0x00 },
+ { 0x3fc2bfc0, 0x03 },
+ { 0x0000d486, 0x43 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00 },
+ { 0x1000db00, 0x07 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x11 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x82 },
+ { 0x1000db06, 0x04 },
+ { 0x1000db07, 0xf1 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x40 },
+ { 0x1000db0b, 0x02 },
+ { 0x1000db0c, 0xf2 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0xe0 },
+ { 0x1000db10, 0x00 },
+ { 0x1000db11, 0x10 },
+ { 0x1000db12, 0x00 },
+ { 0x1000db13, 0x00 },
+ { 0x1000db14, 0x45 },
+ { 0x1000db15, 0x0d },
+ { 0x1000db16, 0x01 },
+ { 0x1000db17, 0x00 },
+ { 0x1000db18, 0x00 },
+ { 0x1000db19, 0xbf },
+ { 0x1000db1a, 0x13 },
+ { 0x1000db1b, 0x09 },
+ { 0x1000db1c, 0x00 },
+ { 0x1000db1d, 0x00 },
+ { 0x1000db1e, 0x00 },
+ { 0x1000db1f, 0x12 },
+ { 0x1000db20, 0x09 },
+ { 0x1000db21, 0x00 },
+ { 0x1000db22, 0x00 },
+ { 0x1000db23, 0x00 },
+ { 0x0000d540, 0x01 },
+ { 0x0000c081, 0xfc },
+ { 0x0000f01e, 0x80 },
+ { 0xc01b, 0xfc },
+ { 0xc5d1, 0x89 },
+ { 0xc5d8, 0x0a },
+ { 0xc5f7, 0x22 },
+ { 0xc5f6, 0x22 },
+ { 0xc065, 0xa5 },
+ { 0xc06b, 0x0a },
+ { 0xd172, 0x2a },
+ { 0xc5d6, 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_sequence rt1321_blind_write[] = {
+ { 0x0000c003, 0xf0 },
+ { 0x0000c01b, 0xfc },
+ { 0x0000c5c3, 0xf2 },
+ { 0x0000c5c2, 0x00 },
+ { 0x0000c5c1, 0x10 },
+ { 0x0000c5c0, 0x04 },
+ { 0x0000c5c7, 0x03 },
+ { 0x0000c5c6, 0x10 },
+ { 0x0000c526, 0x47 },
+ { 0x0000c5c4, 0x12 },
+ { 0x0000c5c5, 0x60 },
+ { 0x0000c520, 0x10 },
+ { 0x0000c521, 0x32 },
+ { 0x0000c5c7, 0x00 },
+ { 0x0000c5c8, 0x03 },
+ { 0x0000c5d3, 0x08 },
+ { 0x0000c5d2, 0x0a },
+ { 0x0000c5d1, 0x49 },
+ { 0x0000c5d0, 0x0f },
+ { 0x0000c580, 0x10 },
+ { 0x0000c581, 0x32 },
+ { 0x0000c582, 0x01 },
+ { 0x0000cb00, 0x03 },
+ { 0x0000cb02, 0x52 },
+ { 0x0000cb04, 0x80 },
+ { 0x0000cb0b, 0x01 },
+ { 0x0000c682, 0x60 },
+ { 0x0000c019, 0x10 },
+ { 0x0000c5f0, 0x01 },
+ { 0x0000c5f7, 0x22 },
+ { 0x0000c5f6, 0x22 },
+ { 0x0000c057, 0x51 },
+ { 0x0000c054, 0x55 },
+ { 0x0000c053, 0x55 },
+ { 0x0000c052, 0x55 },
+ { 0x0000c051, 0x01 },
+ { 0x0000c050, 0x15 },
+ { 0x0000c060, 0x99 },
+ { 0x0000c030, 0x55 },
+ { 0x0000c061, 0x55 },
+ { 0x0000c063, 0x55 },
+ { 0x0000c065, 0xa5 },
+ { 0x0000c06b, 0x0a },
+ { 0x0000ca05, 0xd6 },
+ { 0x0000ca07, 0x07 },
+ { 0x0000ca25, 0xd6 },
+ { 0x0000ca27, 0x07 },
+ { 0x0000cd00, 0x05 },
+ { 0x0000c604, 0x40 },
+ { 0x0000c609, 0x40 },
+ { 0x0000c046, 0xf7 },
+ { 0x0000c045, 0xff },
+ { 0x0000c044, 0xff },
+ { 0x0000c043, 0xff },
+ { 0x0000c042, 0xff },
+ { 0x0000c041, 0xff },
+ { 0x0000c040, 0xff },
+ { 0x0000c049, 0xff },
+ { 0x0000c028, 0x3f },
+ { 0x0000c020, 0x3f },
+ { 0x0000c032, 0x13 },
+ { 0x0000c033, 0x01 },
+ { 0x0000cc10, 0x01 },
+ { 0x0000dc20, 0x03 },
+ { 0x0000de03, 0x05 },
+ { 0x0000dc00, 0x00 },
+ { 0x0000c700, 0xf0 },
+ { 0x0000c701, 0x13 },
+ { 0x0000c900, 0xc3 },
+ { 0x0000c570, 0x08 },
+ { 0x0000c086, 0x02 },
+ { 0x0000c085, 0x7f },
+ { 0x0000c084, 0x00 },
+ { 0x0000c081, 0xff },
+ { 0x0000f084, 0x0f },
+ { 0x0000f083, 0xff },
+ { 0x0000f082, 0xff },
+ { 0x0000f081, 0xff },
+ { 0x0000f080, 0xff },
+ { 0x20003003, 0x3f },
+ { 0x20005818, 0x81 },
+ { 0x20009018, 0x81 },
+ { 0x2000301c, 0x81 },
+ { 0x0000c003, 0xc0 },
+ { 0x0000c047, 0x80 },
+ { 0x0000d541, 0x80 },
+ { 0x0000d487, 0x0b },
+ { 0x0000d487, 0x3b },
+ { 0x0000d486, 0xc3 },
+ { 0x0000d470, 0x89 },
+ { 0x0000d471, 0x3a },
+ { 0x0000d472, 0x1d },
+ { 0x0000d478, 0xff },
+ { 0x0000d479, 0x20 },
+ { 0x0000d47a, 0x10 },
+ { 0x0000d73c, 0xb7 },
+ { 0x0000d73d, 0xd7 },
+ { 0x0000d73e, 0x00 },
+ { 0x0000d73f, 0x10 },
+ { 0x3fc2dfc3, 0x00 },
+ { 0x3fc2dfc2, 0x00 },
+ { 0x3fc2dfc1, 0x00 },
+ { 0x3fc2dfc0, 0x07 },
+ { 0x3fc2dfc7, 0x00 },
+ { 0x3fc2dfc6, 0x00 },
+ { 0x3fc2dfc5, 0x00 },
+ { 0x3fc2dfc4, 0x01 },
+ { 0x3fc2df83, 0x00 },
+ { 0x3fc2df82, 0x00 },
+ { 0x3fc2df81, 0x00 },
+ { 0x3fc2df80, 0x00 },
+ { 0x0000d541, 0x40 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x03 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x10 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x45 },
+ { 0x1000db06, 0x12 },
+ { 0x1000db07, 0x09 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x00 },
+ { 0x1000db0b, 0x13 },
+ { 0x1000db0c, 0x09 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0x00 },
+ { 0x0000d540, 0x21 },
+ { 0x41000189, 0x00 },
+ { 0x4100018a, 0x00 },
+ { 0x41001988, 0x00 },
+ { 0x41081400, 0x09 },
+ { 0x40801508, 0x03 },
+ { 0x40801588, 0x03 },
+ { 0x40801809, 0x00 },
+ { 0x4080180a, 0x00 },
+ { 0x4080180b, 0x00 },
+ { 0x4080180c, 0x00 },
+ { 0x40801b09, 0x00 },
+ { 0x40801b0a, 0x00 },
+ { 0x40801b0b, 0x00 },
+ { 0x40801b0c, 0x00 },
+ { 0x0000d714, 0x17 },
+ { 0x20009012, 0x00 },
+ { 0x0000dd0b, 0x0d },
+ { 0x0000dd0a, 0xff },
+ { 0x0000dd09, 0x0d },
+ { 0x0000dd08, 0xff },
+ { 0x0000d172, 0x2a },
+ { 0x41001988, 0x03 },
+};
+
+static const struct reg_default rt1320_reg_defaults[] = {
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x0b },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_default rt1320_mbq_defaults[] = {
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+};
+
+static bool rt1320_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc086:
+ case 0xc400 ... 0xc409:
+ case 0xc480 ... 0xc48f:
+ case 0xc4c0 ... 0xc4c4:
+ case 0xc4e0 ... 0xc4e7:
+ case 0xc500:
+ case 0xc560 ... 0xc56b:
+ case 0xc570:
+ case 0xc580 ... 0xc59a:
+ case 0xc5b0 ... 0xc60f:
+ case 0xc640 ... 0xc64f:
+ case 0xc670:
+ case 0xc680 ... 0xc683:
+ case 0xc700 ... 0xc76f:
+ case 0xc800 ... 0xc801:
+ case 0xc820:
+ case 0xc900 ... 0xc901:
+ case 0xc920 ... 0xc921:
+ case 0xca00 ... 0xca07:
+ case 0xca20 ... 0xca27:
+ case 0xca40 ... 0xca4b:
+ case 0xca60 ... 0xca68:
+ case 0xca80 ... 0xca88:
+ case 0xcb00 ... 0xcb0c:
+ case 0xcc00 ... 0xcc12:
+ case 0xcc80 ... 0xcc81:
+ case 0xcd00:
+ case 0xcd80 ... 0xcd82:
+ case 0xce00 ... 0xce4d:
+ case 0xcf00 ... 0xcf25:
+ case 0xd000 ... 0xd0ff:
+ case 0xd100 ... 0xd1ff:
+ case 0xd200 ... 0xd2ff:
+ case 0xd300 ... 0xd3ff:
+ case 0xd400 ... 0xd403:
+ case 0xd410 ... 0xd417:
+ case 0xd470 ... 0xd497:
+ case 0xd4dc ... 0xd50f:
+ case 0xd520 ... 0xd543:
+ case 0xd560 ... 0xd5ef:
+ case 0xd600 ... 0xd663:
+ case 0xda00 ... 0xda6e:
+ case 0xda80 ... 0xda9e:
+ case 0xdb00 ... 0xdb7f:
+ case 0xdc00:
+ case 0xdc20 ... 0xdc21:
+ case 0xdd00 ... 0xdd17:
+ case 0xde00 ... 0xde09:
+ case 0xdf00 ... 0xdf1b:
+ case 0xe000 ... 0xe847:
+ case 0xf01e:
+ case 0xf717 ... 0xf719:
+ case 0xf720 ... 0xf723:
+ case 0x1000cd91 ... 0x1000cd96:
+ case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER:
+ case 0x1000f008:
+ case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc29d80 ... 0x3fc29d83:
+ case 0x3fe2e000 ... 0x3fe2e003:
+ case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ /* 0x40880900/0x40880980 */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x40881500 */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ /* 0x41000189/0x4100018a */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ /* 0x41001388 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ /* 0x41001988 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ /* 0x41080000 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
+ /* 0x41080200 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0):
+ /* 0x41080900 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41080980 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41081080 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41081480/0x41081488 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ /* 0x41081980 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc003:
+ case 0xc081:
+ case 0xc402 ... 0xc406:
+ case 0xc48c ... 0xc48f:
+ case 0xc560:
+ case 0xc5b5 ... 0xc5b7:
+ case 0xc5fc ... 0xc5ff:
+ case 0xc680 ... 0xc683:
+ case 0xc820:
+ case 0xc900:
+ case 0xc920:
+ case 0xca42:
+ case 0xca62:
+ case 0xca82:
+ case 0xcd00:
+ case 0xce03:
+ case 0xce10:
+ case 0xce14 ... 0xce17:
+ case 0xce44 ... 0xce49:
+ case 0xce4c ... 0xce4d:
+ case 0xcf0c:
+ case 0xcf10 ... 0xcf25:
+ case 0xd486 ... 0xd487:
+ case 0xd4e5 ... 0xd4e6:
+ case 0xd4e8 ... 0xd4ff:
+ case 0xd530:
+ case 0xd540 ... 0xd541:
+ case 0xd543:
+ case 0xdb58 ... 0xdb5f:
+ case 0xdb60 ... 0xdb63:
+ case 0xdb68 ... 0xdb69:
+ case 0xdb6d:
+ case 0xdb70 ... 0xdb71:
+ case 0xdb76:
+ case 0xdb7a:
+ case 0xdb7c ... 0xdb7f:
+ case 0xdd0c ... 0xdd13:
+ case 0xde02:
+ case 0xdf14 ... 0xdf1b:
+ case 0xe83c ... 0xe847:
+ case 0xf01e:
+ case 0xf717 ... 0xf719:
+ case 0xf720 ... 0xf723:
+ case 0x10000000 ... 0x10008fff:
+ case 0x1000c000 ... 0x1000dfff:
+ case 0x1000f008:
+ case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2b780:
+ case 0x3fc2bf80 ... 0x3fc2bf83:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ case 0x3fe2e000 ... 0x3fe2e003:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1320_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1320_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1320_readable_register,
+ .volatile_reg = rt1320_volatile_register,
+ .max_register = 0x41081980,
+ .reg_defaults = rt1320_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1320_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt1320_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt1320_mbq_readable_register,
+ .max_register = 0x41000192,
+ .reg_defaults = rt1320_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1320_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1320_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ /*
+ * Due to support the multi-lane, we call 'sdw_slave_read_prop' to get the lane mapping
+ */
+ sdw_slave_read_prop(slave);
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+ prop->lane_control_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(4) | BIT(8) | BIT(10);
+ prop->sink_ports = BIT(1);
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 64;
+
+ /* BIOS may set wake_capable. Make sure it is 0 as wake events are disabled. */
+ prop->wake_capable = 0;
+
+ return 0;
+}
+
+static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 2000, val;
+
+ pm_runtime_mark_last_busy(&rt1320->sdw_slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt1320->regmap,
+ SDW_SDCA_CTL(func, entity, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt1320->sdw_slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * The 'patch code' is written to the patch code area.
+ * The patch code area is used for SDCA register expansion flexibility.
+ */
+static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
+{
+ struct sdw_slave *slave = rt1320->sdw_slave;
+ const struct firmware *patch;
+ const char *filename;
+ unsigned int addr, val, min_addr, max_addr;
+ const unsigned char *ptr;
+ int ret, i;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id <= RT1320_VB)
+ filename = RT1320_VAB_MCU_PATCH;
+ else
+ filename = RT1320_VC_MCU_PATCH;
+ min_addr = 0x10007000;
+ max_addr = 0x10007fff;
+ break;
+ case RT1321_DEV_ID:
+ filename = RT1321_VA_MCU_PATCH;
+ min_addr = 0x10008000;
+ max_addr = 0x10008fff;
+ break;
+ default:
+ dev_err(&slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
+
+ /* load the patch code here */
+ ret = request_firmware(&patch, filename, &slave->dev);
+ if (ret) {
+ dev_err(&slave->dev, "%s: Failed to load %s firmware", __func__, filename);
+ regmap_write(rt1320->regmap, 0xc598, 0x00);
+ regmap_write(rt1320->regmap, min_addr, 0x67);
+ regmap_write(rt1320->regmap, min_addr + 0x1, 0x80);
+ regmap_write(rt1320->regmap, min_addr + 0x2, 0x00);
+ regmap_write(rt1320->regmap, min_addr + 0x3, 0x00);
+ if (rt1320->dev_id == RT1321_DEV_ID) {
+ regmap_write(rt1320->regmap, 0xd73c, 0x67);
+ regmap_write(rt1320->regmap, 0xd73d, 0x80);
+ regmap_write(rt1320->regmap, 0xd73e, 0x00);
+ regmap_write(rt1320->regmap, 0xd73f, 0x00);
+ }
+ } else {
+ ptr = (const unsigned char *)patch->data;
+ if ((patch->size % 8) == 0) {
+ for (i = 0; i < patch->size; i += 8) {
+ addr = (ptr[i] & 0xff) | (ptr[i + 1] & 0xff) << 8 |
+ (ptr[i + 2] & 0xff) << 16 | (ptr[i + 3] & 0xff) << 24;
+ val = (ptr[i + 4] & 0xff) | (ptr[i + 5] & 0xff) << 8 |
+ (ptr[i + 6] & 0xff) << 16 | (ptr[i + 7] & 0xff) << 24;
+
+ if (addr > max_addr || addr < min_addr) {
+ dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr);
+ goto _exit_;
+ }
+ if (val > 0xff) {
+ dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val);
+ goto _exit_;
+ }
+ regmap_write(rt1320->regmap, addr, val);
+ }
+ }
+_exit_:
+ release_firmware(patch);
+ }
+}
+
+static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320_blind_write); i++) {
+ reg = rt1320_blind_write[i].reg;
+ val = rt1320_blind_write[i].def;
+ delay = rt1320_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2bfc7)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+ if (delay)
+ usleep_range(delay, delay + 1000);
+ }
+}
+
+static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
+{
+ struct sdw_slave *slave = rt1320->sdw_slave;
+ unsigned int i, reg, val, delay, retry, tmp;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320_vc_blind_write); i++) {
+ reg = rt1320_vc_blind_write[i].reg;
+ val = rt1320_vc_blind_write[i].def;
+ delay = rt1320_vc_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2bf83)
+ rt1320_load_mcu_patch(rt1320);
+
+ if ((reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0)) &&
+ (val == 0x00)) {
+ retry = 200;
+ while (retry) {
+ regmap_read(rt1320->regmap, RT1320_KR0_INT_READY, &tmp);
+ dev_dbg(&slave->dev, "%s, RT1320_KR0_INT_READY=0x%x\n", __func__, tmp);
+ if (tmp == 0x1f)
+ break;
+ usleep_range(1000, 1500);
+ retry--;
+ }
+ if (!retry)
+ dev_warn(&slave->dev, "%s MCU is NOT ready!", __func__);
+ }
+ regmap_write(rt1320->regmap, reg, val);
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
+static void rt1321_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) {
+ reg = rt1321_blind_write[i].reg;
+ val = rt1321_blind_write[i].def;
+ delay = rt1321_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2dfc3)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
+static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+ unsigned int amp_func_status, val, tmp;
+
+ if (rt1320->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1320->regmap, false);
+ regcache_cache_only(rt1320->mbq_regmap, false);
+ if (rt1320->first_hw_init) {
+ regcache_cache_bypass(rt1320->regmap, true);
+ regcache_cache_bypass(rt1320->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ if (rt1320->version_id < 0) {
+ regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val);
+ rt1320->version_id = val;
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val);
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp);
+ rt1320->dev_id = (val << 8) | tmp;
+ }
+
+ regmap_read(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ /* initialization write */
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id < RT1320_VC)
+ rt1320_vab_preset(rt1320);
+ else
+ rt1320_vc_preset(rt1320);
+ break;
+ case RT1321_DEV_ID:
+ rt1321_preset(rt1320);
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ }
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+ if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
+ regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0);
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val);
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_1, &tmp);
+ val = (tmp << 8) | val;
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_2, &tmp);
+ val = (tmp << 16) | val;
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_3, &tmp);
+ val = (tmp << 24) | val;
+ dev_dbg(dev, "%s ROM version=0x%x\n", __func__, val);
+ /*
+ * We call the version b which has the new DSP ROM code against version a.
+ * Therefore, we read the DSP address to check the ID.
+ */
+ if (val == RT1320_VER_B_ID)
+ rt1320->version_id = RT1320_VB;
+ regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3);
+ }
+ dev_dbg(dev, "%s version_id=%d, dev_id=0x%x\n", __func__, rt1320->version_id, rt1320->dev_id);
+
+ if (rt1320->first_hw_init) {
+ regcache_cache_bypass(rt1320->regmap, false);
+ regcache_cache_bypass(rt1320->mbq_regmap, false);
+ regcache_mark_dirty(rt1320->regmap);
+ regcache_mark_dirty(rt1320->mbq_regmap);
+ }
+
+ /* Mark Slave initialization complete */
+ rt1320->first_hw_init = true;
+ rt1320->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1320_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1320->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1320->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1320_io_init(&slave->dev, slave);
+}
+
+static int rt1320_pde11_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, ps3);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1320_pde23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, ps3);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_l_val, gain_r_val;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ unsigned int changed = 0, reg_base;
+ struct rt_sdca_dmic_kctrl_priv *p;
+ unsigned int regvalue[4], gain_val[4], i;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU Capture Volume"))
+ goto _dmic_vol_;
+
+ regmap_read(rt1320->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt1320->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+
+ if (lvalue == gain_l_val && rvalue == gain_r_val)
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt1320->mbq_regmap, mc->reg, gain_l_val);
+ /* Rch */
+ regmap_write(rt1320->mbq_regmap, mc->rreg, gain_r_val);
+ goto _done_;
+
+_dmic_vol_:
+ p = (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+ break;
+ }
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ break;
+ }
+
+ if (err < 0)
+ dev_err(&rt1320->sdw_slave->dev, "0x%08x can't be set\n", reg_base + i);
+ }
+
+_done_:
+ return 1;
+}
+
+static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ const unsigned int interval_offset = 0xc0;
+ unsigned int reg_base, regvalue, ctl, i;
+ struct rt_sdca_dmic_kctrl_priv *p;
+
+ if (strstr(ucontrol->id.name, "FU Capture Volume"))
+ goto _dmic_vol_;
+
+ regmap_read(rt1320->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r);
+
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+
+ if (read_l != read_r)
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+ goto _done_;
+
+_dmic_vol_:
+ p = (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+ break;
+ }
+
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+ ucontrol->value.integer.value[i] = ctl;
+ }
+_done_:
+ return 0;
+}
+
+static int rt1320_set_fu_capture_ctl(struct rt1320_sdw_priv *rt1320)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320->fu_mixer_mute); i++) {
+ ch_mute = (rt1320->fu_dapm_mute || rt1320->fu_mixer_mute[i]) ? 0x01 : 0x00;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2)
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ else
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+ break;
+ case RT1321_DEV_ID:
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ break;
+ default:
+ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt1320_dmic_fu_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt1320->fu_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt1320_dmic_fu_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt1320->fu_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt1320->fu_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt1320_set_fu_capture_ctl(rt1320);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt1320_dmic_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int rt1320_dmic_fu_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt1320->fu_dapm_mute = false;
+ rt1320_set_fu_capture_ctl(rt1320);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt1320->fu_dapm_mute = true;
+ rt1320_set_fu_capture_ctl(rt1320);
+ break;
+ }
+ return 0;
+}
+
+static const char * const rt1320_rx_data_ch_select[] = {
+ "L,R",
+ "R,L",
+ "L,L",
+ "R,R",
+ "L,L+R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0,
+ rt1320_rx_data_ch_select);
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+
+static const struct snd_kcontrol_new rt1320_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0, rt1320_set_gain_get, rt1320_set_gain_put, out_vol_tlv),
+ SOC_ENUM("RX Channel Select", rt1320_rx_data_ch_enum),
+
+ RT_SDCA_FU_CTRL("FU Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4, rt1320_dmic_fu_info, rt1320_dmic_fu_capture_get, rt1320_dmic_fu_capture_put),
+ RT_SDCA_EXT_TLV("FU Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
+ rt1320_set_gain_get, rt1320_set_gain_put, 4, 0x3f, in_vol_tlv, rt1320_dmic_fu_info),
+};
+
+static const struct snd_kcontrol_new rt1320_spk_l_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01),
+ 0, 1, 1);
+static const struct snd_kcontrol_new rt1320_spk_r_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1320_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP8-10TX", "DP8-10 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("FU21", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt1320_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt1320_pde11_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC("FU 113", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("FU 14", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA_E("FU", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1320_dmic_fu_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output */
+ SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt1320_spk_l_dac),
+ SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt1320_spk_r_dac),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+
+ /* Input */
+ SND_SOC_DAPM_PGA("AEC Data", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("AEC Gen"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+};
+
+static const struct snd_soc_dapm_route rt1320_dapm_routes[] = {
+ { "FU21", NULL, "DP1RX" },
+ { "FU21", NULL, "PDE 23" },
+ { "OT23 L", "Switch", "FU21" },
+ { "OT23 R", "Switch", "FU21" },
+ { "SPOL", NULL, "OT23 L" },
+ { "SPOR", NULL, "OT23 R" },
+
+ { "AEC Data", NULL, "AEC Gen" },
+ { "DP4TX", NULL, "AEC Data" },
+
+ {"DP8-10TX", NULL, "FU"},
+ {"FU", NULL, "PDE 11"},
+ {"FU", NULL, "FU 113"},
+ {"FU", NULL, "FU 14"},
+ {"FU 113", NULL, "DMIC1"},
+ {"FU 14", NULL, "DMIC2"},
+};
+
+static int rt1320_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+ return 0;
+}
+
+static void rt1320_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1320_sdw_priv *rt1320 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ struct sdw_port_config dmic_port_config[2];
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt1320->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (dai->id == RT1320_AIF1)
+ port_config.num = 1;
+ else
+ return -EINVAL;
+ } else {
+ if (dai->id == RT1320_AIF1)
+ port_config.num = 4;
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[1].num = 10;
+ break;
+ case RT1321_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ }
+
+ if (dai->id == RT1320_AIF1)
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 2, sdw_stream);
+ break;
+ case RT1321_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 1, sdw_stream);
+ break;
+ default:
+ dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT1320_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT1320_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT1320_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1320_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1320_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1320_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT1320_AIF1)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ else {
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ if (rt1320->dev_id == RT1320_DEV_ID)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ }
+
+ return 0;
+}
+
+static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1320_sdw_priv *rt1320 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1320->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1320->sdw_slave, sdw_stream);
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt1320_slave_ops = {
+ .read_prop = rt1320_read_prop,
+ .update_status = rt1320_update_status,
+};
+
+static int rt1320_sdw_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ rt1320->component = component;
+
+ if (!rt1320->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ dev_dbg(&rt1320->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1320 = {
+ .probe = rt1320_sdw_component_probe,
+ .controls = rt1320_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1320_snd_controls),
+ .dapm_widgets = rt1320_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1320_dapm_widgets),
+ .dapm_routes = rt1320_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1320_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1320_aif_dai_ops = {
+ .hw_params = rt1320_sdw_hw_params,
+ .hw_free = rt1320_sdw_pcm_hw_free,
+ .set_stream = rt1320_set_sdw_stream,
+ .shutdown = rt1320_sdw_shutdown,
+};
+
+#define RT1320_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT1320_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt1320_sdw_dai[] = {
+ {
+ .name = "rt1320-aif1",
+ .id = RT1320_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .ops = &rt1320_aif_dai_ops,
+ },
+ /* DMIC: DP8 2ch + DP10 2ch */
+ {
+ .name = "rt1320-aif2",
+ .id = RT1320_AIF2,
+ .capture = {
+ .stream_name = "DP8-10 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .ops = &rt1320_aif_dai_ops,
+ },
+};
+
+static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt1320_sdw_priv *rt1320;
+ int ret;
+
+ rt1320 = devm_kzalloc(dev, sizeof(*rt1320), GFP_KERNEL);
+ if (!rt1320)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1320);
+ rt1320->sdw_slave = slave;
+ rt1320->mbq_regmap = mbq_regmap;
+ rt1320->regmap = regmap;
+
+ regcache_cache_only(rt1320->regmap, true);
+ regcache_cache_only(rt1320->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1320->hw_init = false;
+ rt1320->first_hw_init = false;
+ rt1320->version_id = -1;
+ rt1320->fu_dapm_mute = true;
+ rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] =
+ rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1320,
+ rt1320_sdw_dai,
+ ARRAY_SIZE(rt1320_sdw_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt1320_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt1320_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt1320_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1320_sdw_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt1320_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+/*
+ * Version A/B will use the class id 0
+ * The newer version than A/B will use the class id 1, so add it in advance
+ */
+static const struct sdw_device_id rt1320_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1321, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1320_id);
+
+static int rt1320_dev_suspend(struct device *dev)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1320->regmap, true);
+ regcache_cache_only(rt1320->mbq_regmap, true);
+ return 0;
+}
+
+#define RT1320_PROBE_TIMEOUT 5000
+
+static int rt1320_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1320->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1320_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1320->regmap, false);
+ regcache_sync(rt1320->regmap);
+ regcache_cache_only(rt1320->mbq_regmap, false);
+ regcache_sync(rt1320->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt1320_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume)
+ RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1320_sdw_driver = {
+ .driver = {
+ .name = "rt1320-sdca",
+ .pm = pm_ptr(&rt1320_pm),
+ },
+ .probe = rt1320_sdw_probe,
+ .remove = rt1320_sdw_remove,
+ .ops = &rt1320_slave_ops,
+ .id_table = rt1320_id,
+};
+module_sdw_driver(rt1320_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1320 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
new file mode 100644
index 000000000000..a6d90e259dc9
--- /dev/null
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1320-sdw.h -- RT1320 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1320_SDW_H__
+#define __RT1320_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+#define RT1320_DEV_ID 0x6981
+#define RT1321_DEV_ID 0x7045
+
+/* imp-defined registers */
+#define RT1320_DEV_VERSION_ID_1 0xc404
+#define RT1320_DEV_ID_1 0xc405
+#define RT1320_DEV_ID_0 0xc406
+
+#define RT1321_PATCH_MAIN_VER 0x1000cffe
+#define RT1321_PATCH_BETA_VER 0x1000cfff
+
+#define RT1320_KR0_STATUS_CNT 0x1000f008
+#define RT1320_KR0_INT_READY 0x1000f021
+#define RT1320_HIFI_VER_0 0x3fe2e000
+#define RT1320_HIFI_VER_1 0x3fe2e001
+#define RT1320_HIFI_VER_2 0x3fe2e002
+#define RT1320_HIFI_VER_3 0x3fe2e003
+
+/* RT1320 SDCA Control - function number */
+#define FUNC_NUM_AMP 0x04
+#define FUNC_NUM_MIC 0x02
+
+/* RT1320 SDCA entity */
+#define RT1320_SDCA_ENT0 0x00
+#define RT1320_SDCA_ENT_PDE11 0x2a
+#define RT1320_SDCA_ENT_PDE23 0x33
+#define RT1320_SDCA_ENT_PDE27 0x27
+#define RT1320_SDCA_ENT_FU14 0x32
+#define RT1320_SDCA_ENT_FU21 0x03
+#define RT1320_SDCA_ENT_FU113 0x30
+#define RT1320_SDCA_ENT_CS14 0x13
+#define RT1320_SDCA_ENT_CS21 0x21
+#define RT1320_SDCA_ENT_CS113 0x12
+#define RT1320_SDCA_ENT_SAPU 0x29
+#define RT1320_SDCA_ENT_PPU21 0x04
+
+/* RT1320 SDCA control */
+#define RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1320_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1320_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+#define RT1320_SDCA_CTL_FU_MUTE 0x01
+#define RT1320_SDCA_CTL_FU_VOLUME 0x02
+#define RT1320_SDCA_CTL_SAPU_PROTECTION_MODE 0x10
+#define RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11
+#define RT1320_SDCA_CTL_POSTURE_NUMBER 0x10
+#define RT1320_SDCA_CTL_FUNC_STATUS 0x10
+
+/* RT1320 SDCA channel */
+#define CH_01 0x01
+#define CH_02 0x02
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
+/* Sample Frequency Index */
+#define RT1320_SDCA_RATE_16000HZ 0x04
+#define RT1320_SDCA_RATE_32000HZ 0x07
+#define RT1320_SDCA_RATE_44100HZ 0x08
+#define RT1320_SDCA_RATE_48000HZ 0x09
+#define RT1320_SDCA_RATE_96000HZ 0x0b
+#define RT1320_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT1320_AIF1,
+ RT1320_AIF2,
+};
+
+/*
+ * The version id will be useful to distinguish the capability between the different IC versions.
+ * Currently, VA and VB have different DSP FW versions.
+ */
+enum rt1320_version_id {
+ RT1320_VA,
+ RT1320_VB,
+ RT1320_VC,
+};
+
+#define RT1320_VER_B_ID 0x07392238
+#define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin"
+#define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
+#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
+
+struct rt1320_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int version_id;
+ unsigned int dev_id;
+ bool fu_dapm_mute;
+ bool fu_mixer_mute[4];
+};
+
+#endif /* __RT1320_SDW_H__ */
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
new file mode 100644
index 000000000000..5c33aeaced2f
--- /dev/null
+++ b/sound/soc/codecs/rt274.c
@@ -0,0 +1,1234 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt274.c -- RT274 ALSA SoC audio codec driver
+ *
+ * Copyright 2017 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+
+#include "rl6347a.h"
+#include "rt274.h"
+
+#define RT274_VENDOR_ID 0x10ec0274
+
+struct rt274_priv {
+ struct reg_default *index_cache;
+ int index_cache_size;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct i2c_client *i2c;
+ struct snd_soc_jack *jack;
+ struct delayed_work jack_detect_work;
+ int sys_clk;
+ int clk_id;
+ int fs;
+ bool master;
+};
+
+static const struct reg_default rt274_index_def[] = {
+ { 0x00, 0x1004 },
+ { 0x01, 0xaaaa },
+ { 0x02, 0x88aa },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaa09 },
+ { 0x05, 0x0700 },
+ { 0x06, 0x6110 },
+ { 0x07, 0x0200 },
+ { 0x08, 0xa807 },
+ { 0x09, 0x0021 },
+ { 0x0a, 0x7770 },
+ { 0x0b, 0x7770 },
+ { 0x0c, 0x002b },
+ { 0x0d, 0x2420 },
+ { 0x0e, 0x65c0 },
+ { 0x0f, 0x7770 },
+ { 0x10, 0x0420 },
+ { 0x11, 0x7418 },
+ { 0x12, 0x6bd0 },
+ { 0x13, 0x645f },
+ { 0x14, 0x0400 },
+ { 0x15, 0x8ccc },
+ { 0x16, 0x4c50 },
+ { 0x17, 0xff00 },
+ { 0x18, 0x0003 },
+ { 0x19, 0x2c11 },
+ { 0x1a, 0x830b },
+ { 0x1b, 0x4e4b },
+ { 0x1c, 0x0000 },
+ { 0x1d, 0x0000 },
+ { 0x1e, 0x0000 },
+ { 0x1f, 0x0000 },
+ { 0x20, 0x51ff },
+ { 0x21, 0x8000 },
+ { 0x22, 0x8f00 },
+ { 0x23, 0x88f4 },
+ { 0x24, 0x0000 },
+ { 0x25, 0x0000 },
+ { 0x26, 0x0000 },
+ { 0x27, 0x0000 },
+ { 0x28, 0x0000 },
+ { 0x29, 0x3000 },
+ { 0x2a, 0x0000 },
+ { 0x2b, 0x0000 },
+ { 0x2c, 0x0f00 },
+ { 0x2d, 0x100f },
+ { 0x2e, 0x2902 },
+ { 0x2f, 0xe280 },
+ { 0x30, 0x1000 },
+ { 0x31, 0x8400 },
+ { 0x32, 0x5aaa },
+ { 0x33, 0x8420 },
+ { 0x34, 0xa20c },
+ { 0x35, 0x096a },
+ { 0x36, 0x5757 },
+ { 0x37, 0xfe05 },
+ { 0x38, 0x4901 },
+ { 0x39, 0x110a },
+ { 0x3a, 0x0010 },
+ { 0x3b, 0x60d9 },
+ { 0x3c, 0xf214 },
+ { 0x3d, 0xc2ba },
+ { 0x3e, 0xa928 },
+ { 0x3f, 0x0000 },
+ { 0x40, 0x9800 },
+ { 0x41, 0x0000 },
+ { 0x42, 0x2000 },
+ { 0x43, 0x3d90 },
+ { 0x44, 0x4900 },
+ { 0x45, 0x5289 },
+ { 0x46, 0x0004 },
+ { 0x47, 0xa47a },
+ { 0x48, 0xd049 },
+ { 0x49, 0x0049 },
+ { 0x4a, 0xa83b },
+ { 0x4b, 0x0777 },
+ { 0x4c, 0x065c },
+ { 0x4d, 0x7fff },
+ { 0x4e, 0x7fff },
+ { 0x4f, 0x0000 },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0xbf5f },
+ { 0x53, 0x3320 },
+ { 0x54, 0xcc00 },
+ { 0x55, 0x0000 },
+ { 0x56, 0x3f00 },
+ { 0x57, 0x0000 },
+ { 0x58, 0x0000 },
+ { 0x59, 0x0000 },
+ { 0x5a, 0x1300 },
+ { 0x5b, 0x005f },
+ { 0x5c, 0x0000 },
+ { 0x5d, 0x1001 },
+ { 0x5e, 0x1000 },
+ { 0x5f, 0x0000 },
+ { 0x60, 0x5554 },
+ { 0x61, 0xffc0 },
+ { 0x62, 0xa000 },
+ { 0x63, 0xd010 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x3fb1 },
+ { 0x66, 0x1881 },
+ { 0x67, 0xc810 },
+ { 0x68, 0x2000 },
+ { 0x69, 0xfff0 },
+ { 0x6a, 0x0300 },
+ { 0x6b, 0x5060 },
+ { 0x6c, 0x0000 },
+ { 0x6d, 0x0000 },
+ { 0x6e, 0x0c25 },
+ { 0x6f, 0x0c0b },
+ { 0x70, 0x8000 },
+ { 0x71, 0x4008 },
+ { 0x72, 0x0000 },
+ { 0x73, 0x0800 },
+ { 0x74, 0xa28f },
+ { 0x75, 0xa050 },
+ { 0x76, 0x7fe8 },
+ { 0x77, 0xdb8c },
+ { 0x78, 0x0000 },
+ { 0x79, 0x0000 },
+ { 0x7a, 0x2a96 },
+ { 0x7b, 0x800f },
+ { 0x7c, 0x0200 },
+ { 0x7d, 0x1600 },
+ { 0x7e, 0x0000 },
+ { 0x7f, 0x0000 },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt274_index_def)
+
+static const struct reg_default rt274_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x00000057 },
+ { 0x0023a000, 0x00000057 },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x00000097 },
+ { 0x00936000, 0x00000097 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000400 },
+ { 0x00b37200, 0x00000400 },
+ { 0x00b37300, 0x00000400 },
+ { 0x00c37000, 0x00000400 },
+ { 0x00c37100, 0x00000400 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01937000, 0x00000000 },
+ { 0x01970500, 0x00000400 },
+ { 0x02050000, 0x0000001b },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000001 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x01970700, 0x00000020 },
+ { 0x00830000, 0x00000097 },
+ { 0x00930000, 0x00000097 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt274_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT274_GET_HP_SENSE:
+ case RT274_GET_MIC_SENSE:
+ case RT274_PROC_COEF:
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_INLINE_CMD, 0):
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt274_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT274_GET_HP_SENSE:
+ case RT274_GET_MIC_SENSE:
+ case RT274_SET_AUDIO_POWER:
+ case RT274_SET_HPO_POWER:
+ case RT274_SET_DMIC1_POWER:
+ case RT274_LOUT_MUX:
+ case RT274_HPO_MUX:
+ case RT274_ADC0_MUX:
+ case RT274_ADC1_MUX:
+ case RT274_SET_MIC:
+ case RT274_SET_PIN_HPO:
+ case RT274_SET_PIN_LOUT3:
+ case RT274_SET_PIN_DMIC1:
+ case RT274_SET_AMP_GAIN_HPO:
+ case RT274_SET_DMIC2_DEFAULT:
+ case RT274_DAC0L_GAIN:
+ case RT274_DAC0R_GAIN:
+ case RT274_DAC1L_GAIN:
+ case RT274_DAC1R_GAIN:
+ case RT274_ADCL_GAIN:
+ case RT274_ADCR_GAIN:
+ case RT274_MIC_GAIN:
+ case RT274_HPOL_GAIN:
+ case RT274_HPOR_GAIN:
+ case RT274_LOUTL_GAIN:
+ case RT274_LOUTR_GAIN:
+ case RT274_DAC_FORMAT:
+ case RT274_ADC_FORMAT:
+ case RT274_COEF_INDEX:
+ case RT274_PROC_COEF:
+ case RT274_SET_AMP_GAIN_ADC_IN1:
+ case RT274_SET_AMP_GAIN_ADC_IN2:
+ case RT274_SET_POWER(RT274_DAC_OUT0):
+ case RT274_SET_POWER(RT274_DAC_OUT1):
+ case RT274_SET_POWER(RT274_ADC_IN1):
+ case RT274_SET_POWER(RT274_ADC_IN2):
+ case RT274_SET_POWER(RT274_DMIC2):
+ case RT274_SET_POWER(RT274_MIC):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN1, 0):
+ case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE1, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE2, 0):
+ case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_HP_OUT, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_MIC, 0):
+ case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_INLINE_CMD, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifdef CONFIG_PM
+static void rt274_index_sync(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_component_write(component, rt274->index_cache[i].reg,
+ rt274->index_cache[i].def);
+ }
+}
+#endif
+
+static int rt274_jack_detect(struct rt274_priv *rt274, bool *hp, bool *mic)
+{
+ unsigned int buf;
+ int ret;
+
+ *hp = false;
+ *mic = false;
+
+ if (!rt274->component)
+ return -EINVAL;
+
+ ret = regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf);
+ if (ret)
+ return ret;
+
+ *hp = buf & 0x80000000;
+ ret = regmap_read(rt274->regmap, RT274_GET_MIC_SENSE, &buf);
+ if (ret)
+ return ret;
+
+ *mic = buf & 0x80000000;
+
+ pr_debug("*hp = %d *mic = %d\n", *hp, *mic);
+
+ return 0;
+}
+
+static void rt274_jack_detect_work(struct work_struct *work)
+{
+ struct rt274_priv *rt274 =
+ container_of(work, struct rt274_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ if (rt274_jack_detect(rt274, &hp, &mic) < 0)
+ return;
+
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt274->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+static irqreturn_t rt274_irq(int irq, void *data);
+
+static int rt274_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ rt274->jack = jack;
+
+ if (jack == NULL) {
+ /* Disable jack detection */
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_IRQ_EN, RT274_IRQ_DIS);
+
+ return 0;
+ }
+
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_IRQ_EN, RT274_IRQ_EN);
+
+ /* Send an initial report */
+ rt274_irq(0, rt274);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt274_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT274_DAC0L_GAIN,
+ RT274_DAC0R_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", RT274_DAC1L_GAIN,
+ RT274_DAC1R_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT274_ADCL_GAIN,
+ RT274_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R("ADC0 Capture Switch", RT274_ADCL_GAIN,
+ RT274_ADCR_GAIN, RT274_MUTE_SFT, 1, 1),
+ SOC_SINGLE_TLV("AMIC Volume", RT274_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+};
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_HPOL_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_HPOR_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new loutl_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_LOUTL_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new loutr_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_LOUTR_GAIN,
+ RT274_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt274_adc_src[] = {
+ "Mic", "Line1", "Line2", "Dmic"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt274_adc0_enum, RT274_ADC0_MUX, RT274_ADC_SEL_SFT,
+ rt274_adc_src);
+
+static const struct snd_kcontrol_new rt274_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt274_adc0_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt274_adc1_enum, RT274_ADC1_MUX, RT274_ADC_SEL_SFT,
+ rt274_adc_src);
+
+static const struct snd_kcontrol_new rt274_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt274_adc1_enum);
+
+static const char * const rt274_dac_src[] = {
+ "DAC OUT0", "DAC OUT1"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt274_hpo_enum, RT274_HPO_MUX,
+ 0, rt274_dac_src);
+
+static const struct snd_kcontrol_new rt274_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt274_hpo_enum);
+
+/* Line out source */
+static SOC_ENUM_SINGLE_DECL(rt274_lout_enum, RT274_LOUT_MUX,
+ 0, rt274_dac_src);
+
+static const struct snd_kcontrol_new rt274_lout_mux =
+SOC_DAPM_ENUM("LOUT source", rt274_lout_enum);
+
+static const struct snd_soc_dapm_widget rt274_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, RT274_SET_STREAMID_ADC1, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, RT274_SET_STREAMID_ADC2, 4, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("ADC 0 Mux", SND_SOC_NOPM, 0, 0,
+ &rt274_adc0_mux),
+ SND_SOC_DAPM_MUX("ADC 1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt274_adc1_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RXL", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1RXR", "AIF1 Playback", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TXL", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TXR", "AIF1 Capture", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RXL", "AIF1 Playback", 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RXR", "AIF1 Playback", 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TXL", "AIF1 Capture", 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TXR", "AIF1 Capture", 3, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, RT274_SET_STREAMID_DAC0, 4, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, RT274_SET_STREAMID_DAC1, 4, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt274_hpo_mux),
+ SND_SOC_DAPM_MUX("LOUT Mux", SND_SOC_NOPM, 0, 0, &rt274_lout_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT274_SET_PIN_HPO,
+ RT274_SET_PIN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LOUT Power", RT274_SET_PIN_LOUT3,
+ RT274_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_PGA("DAC OUT0", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("DAC OUT1", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH("LOUT L", SND_SOC_NOPM, 0, 0,
+ &loutl_enable_control),
+ SND_SOC_DAPM_SWITCH("LOUT R", SND_SOC_NOPM, 0, 0,
+ &loutr_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+ SND_SOC_DAPM_OUTPUT("LINE3"),
+};
+
+static const struct snd_soc_dapm_route rt274_dapm_routes[] = {
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+
+ {"ADC 0 Mux", "Mic", "MIC"},
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "Line1", "LINE1"},
+ {"ADC 0 Mux", "Line2", "LINE2"},
+ {"ADC 1 Mux", "Mic", "MIC"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "Line1", "LINE1"},
+ {"ADC 1 Mux", "Line2", "LINE2"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TXL", NULL, "ADC 0"},
+ {"AIF1TXR", NULL, "ADC 0"},
+ {"AIF2TXL", NULL, "ADC 1"},
+ {"AIF2TXR", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RXL"},
+ {"DAC 0", NULL, "AIF1RXR"},
+ {"DAC 1", NULL, "AIF2RXL"},
+ {"DAC 1", NULL, "AIF2RXR"},
+
+ {"DAC OUT0", NULL, "DAC 0"},
+
+ {"DAC OUT1", NULL, "DAC 1"},
+
+ {"LOUT Mux", "DAC OUT0", "DAC OUT0"},
+ {"LOUT Mux", "DAC OUT1", "DAC OUT1"},
+
+ {"LOUT L", "Switch", "LOUT Mux"},
+ {"LOUT R", "Switch", "LOUT Mux"},
+ {"LOUT L", NULL, "LOUT Power"},
+ {"LOUT R", NULL, "LOUT Power"},
+
+ {"LINE3", NULL, "LOUT L"},
+ {"LINE3", NULL, "LOUT R"},
+
+ {"HPO Mux", "DAC OUT0", "DAC OUT0"},
+ {"HPO Mux", "DAC OUT1", "DAC OUT1"},
+
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt274_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+ int d_len_code = 0, c_len_code = 0;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt274->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt274->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt274->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ c_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ c_len_code = 3;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ c_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ c_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ c_len_code = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rt274->master)
+ c_len_code = 0x3;
+
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, 0xc018, d_len_code << 3 | c_len_code << 14);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
+
+ snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x407f, val);
+
+ return 0;
+}
+
+static int rt274_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_M);
+ rt274->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_S);
+ rt274->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_I2S);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_LJ);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_PCMA);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_component_update_bits(component, RT274_I2S_CTRL1,
+ RT274_I2S_FMT_MASK, RT274_I2S_FMT_PCMB);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+ snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x8000, 0);
+
+ return 0;
+}
+
+static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ switch (source) {
+ case RT274_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component, RT274_PLL2_CTRL,
+ RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_MCLK);
+ break;
+ default:
+ dev_warn(component->dev, "invalid pll source, use BCLK\n");
+ fallthrough;
+ case RT274_PLL2_S_BCLK:
+ snd_soc_component_update_bits(component, RT274_PLL2_CTRL,
+ RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_BCLK);
+ break;
+ }
+
+ if (source == RT274_PLL2_S_BCLK) {
+ snd_soc_component_update_bits(component, RT274_MCLK_CTRL,
+ (0x3 << 12), (0x3 << 12));
+ switch (rt274->fs) {
+ case 50:
+ snd_soc_component_write(component, 0x7a, 0xaab6);
+ snd_soc_component_write(component, 0x7b, 0x0301);
+ snd_soc_component_write(component, 0x7c, 0x04fe);
+ break;
+ case 64:
+ snd_soc_component_write(component, 0x7a, 0xaa96);
+ snd_soc_component_write(component, 0x7b, 0x8003);
+ snd_soc_component_write(component, 0x7c, 0x081e);
+ break;
+ case 128:
+ snd_soc_component_write(component, 0x7a, 0xaa96);
+ snd_soc_component_write(component, 0x7b, 0x8003);
+ snd_soc_component_write(component, 0x7c, 0x080e);
+ break;
+ default:
+ dev_warn(component->dev, "invalid freq_in, assume 4.8M\n");
+ fallthrough;
+ case 100:
+ snd_soc_component_write(component, 0x7a, 0xaab6);
+ snd_soc_component_write(component, 0x7b, 0x0301);
+ snd_soc_component_write(component, 0x7c, 0x047e);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rt274_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+ unsigned int clk_src, mclk_en;
+
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
+
+ switch (clk_id) {
+ case RT274_SCLK_S_MCLK:
+ mclk_en = RT274_MCLK_MODE_EN;
+ clk_src = RT274_CLK_SRC_MCLK;
+ break;
+ case RT274_SCLK_S_PLL1:
+ mclk_en = RT274_MCLK_MODE_DIS;
+ clk_src = RT274_CLK_SRC_MCLK;
+ break;
+ case RT274_SCLK_S_PLL2:
+ mclk_en = RT274_MCLK_MODE_EN;
+ clk_src = RT274_CLK_SRC_PLL2;
+ break;
+ default:
+ mclk_en = RT274_MCLK_MODE_DIS;
+ clk_src = RT274_CLK_SRC_MCLK;
+ dev_warn(component->dev, "invalid sysclk source, use PLL1\n");
+ break;
+ }
+ snd_soc_component_update_bits(component, RT274_MCLK_CTRL,
+ RT274_MCLK_MODE_MASK, mclk_en);
+ snd_soc_component_update_bits(component, RT274_CLK_CTRL,
+ RT274_CLK_SRC_MASK, clk_src);
+
+ switch (freq) {
+ case 19200000:
+ if (clk_id == RT274_SCLK_S_MCLK) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (clk_id == RT274_SCLK_S_MCLK) {
+ dev_err(component->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_component_update_bits(component,
+ RT274_MCLK_CTRL, 0x1fcf, 0x0008);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_component_update_bits(component,
+ RT274_MCLK_CTRL, 0x1fcf, 0x1543);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt274->sys_clk = freq;
+ rt274->clk_id = clk_id;
+
+ return 0;
+}
+
+static int rt274_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
+ rt274->fs = ratio;
+ if ((ratio / 50) == 0)
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt274_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (rx_mask || tx_mask) {
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_EN);
+ } else {
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_DIS);
+ return 0;
+ }
+
+ switch (slots) {
+ case 4:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_4CH);
+ break;
+ case 2:
+ snd_soc_component_update_bits(component,
+ RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_2CH);
+ break;
+ default:
+ dev_err(component->dev,
+ "Support 2 or 4 slots TDM only\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt274_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_write(component,
+ RT274_SET_AUDIO_POWER, AC_PWRST_D0);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component,
+ RT274_SET_AUDIO_POWER, AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rt274_irq(int irq, void *data)
+{
+ struct rt274_priv *rt274 = data;
+ bool hp = false;
+ bool mic = false;
+ int ret, status = 0;
+
+ /* Clear IRQ */
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_IRQ_CLR, RT274_IRQ_CLR);
+
+ ret = rt274_jack_detect(rt274, &hp, &mic);
+
+ if (ret == 0) {
+ if (hp)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt274->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt274->i2c->dev, 300);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rt274_probe(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ rt274->component = component;
+ INIT_DELAYED_WORK(&rt274->jack_detect_work, rt274_jack_detect_work);
+
+ if (rt274->i2c->irq)
+ schedule_delayed_work(&rt274->jack_detect_work,
+ msecs_to_jiffies(1250));
+ return 0;
+}
+
+static void rt274_remove(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt274->jack_detect_work);
+ rt274->component = NULL;
+}
+
+#ifdef CONFIG_PM
+static int rt274_suspend(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt274->regmap, true);
+ regcache_mark_dirty(rt274->regmap);
+
+ return 0;
+}
+
+static int rt274_resume(struct snd_soc_component *component)
+{
+ struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt274->regmap, false);
+ rt274_index_sync(component);
+ regcache_sync(rt274->regmap);
+
+ return 0;
+}
+#else
+#define rt274_suspend NULL
+#define rt274_resume NULL
+#endif
+
+#define RT274_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT274_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt274_aif_dai_ops = {
+ .hw_params = rt274_hw_params,
+ .set_fmt = rt274_set_dai_fmt,
+ .set_sysclk = rt274_set_dai_sysclk,
+ .set_pll = rt274_set_dai_pll,
+ .set_bclk_ratio = rt274_set_bclk_ratio,
+ .set_tdm_slot = rt274_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt274_dai[] = {
+ {
+ .name = "rt274-aif1",
+ .id = RT274_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT274_STEREO_RATES,
+ .formats = RT274_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT274_STEREO_RATES,
+ .formats = RT274_FORMATS,
+ },
+ .ops = &rt274_aif_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt274 = {
+ .probe = rt274_probe,
+ .remove = rt274_remove,
+ .suspend = rt274_suspend,
+ .resume = rt274_resume,
+ .set_bias_level = rt274_set_bias_level,
+ .set_jack = rt274_mic_detect,
+ .controls = rt274_snd_controls,
+ .num_controls = ARRAY_SIZE(rt274_snd_controls),
+ .dapm_widgets = rt274_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt274_dapm_widgets),
+ .dapm_routes = rt274_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt274_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt274_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x05bfffff,
+ .volatile_reg = rt274_volatile_register,
+ .readable_reg = rt274_readable_register,
+ .reg_write = rl6347a_hw_write,
+ .reg_read = rl6347a_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt274_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt274_reg),
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt274_of_match[] = {
+ {.compatible = "realtek,rt274"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt274_of_match);
+#endif
+
+static const struct i2c_device_id rt274_i2c_id[] = {
+ {"rt274"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt274_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt274_acpi_match[] = {
+ { "10EC0274" },
+ { "INT34C2" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt274_acpi_match);
+#endif
+
+static int rt274_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt274_priv *rt274;
+
+ int ret;
+ unsigned int val;
+
+ rt274 = devm_kzalloc(&i2c->dev, sizeof(*rt274),
+ GFP_KERNEL);
+ if (rt274 == NULL)
+ return -ENOMEM;
+
+ rt274->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt274_regmap);
+ if (IS_ERR(rt274->regmap)) {
+ ret = PTR_ERR(rt274->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(rt274->regmap,
+ RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+ if (ret)
+ return ret;
+
+ if (val != RT274_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt274\n", val);
+ return -ENODEV;
+ }
+
+ rt274->index_cache = devm_kmemdup(&i2c->dev, rt274_index_def,
+ sizeof(rt274_index_def), GFP_KERNEL);
+ if (!rt274->index_cache)
+ return -ENOMEM;
+
+ rt274->index_cache_size = INDEX_CACHE_SIZE;
+ rt274->i2c = i2c;
+ i2c_set_clientdata(i2c, rt274);
+
+ /* reset codec */
+ regmap_write(rt274->regmap, RT274_RESET, 0);
+ regmap_update_bits(rt274->regmap, 0x1a, 0x4000, 0x4000);
+
+ /* Set Pad PDB is floating */
+ regmap_update_bits(rt274->regmap, RT274_PAD_CTRL12, 0x3, 0x0);
+ regmap_write(rt274->regmap, RT274_COEF5b_INDEX, 0x01);
+ regmap_write(rt274->regmap, RT274_COEF5b_COEF, 0x8540);
+ regmap_update_bits(rt274->regmap, 0x6f, 0x0100, 0x0100);
+ /* Combo jack auto detect */
+ regmap_write(rt274->regmap, 0x4a, 0x201b);
+ /* Aux mode off */
+ regmap_update_bits(rt274->regmap, 0x6f, 0x3000, 0x2000);
+ /* HP DC Calibration */
+ regmap_update_bits(rt274->regmap, 0x6f, 0xf, 0x0);
+ /* Set NID=58h.Index 00h [15]= 1b; */
+ regmap_write(rt274->regmap, RT274_COEF58_INDEX, 0x00);
+ regmap_write(rt274->regmap, RT274_COEF58_COEF, 0xb888);
+ msleep(500);
+ regmap_update_bits(rt274->regmap, 0x6f, 0xf, 0xb);
+ regmap_write(rt274->regmap, RT274_COEF58_INDEX, 0x00);
+ regmap_write(rt274->regmap, RT274_COEF58_COEF, 0x3888);
+ /* Set pin widget */
+ regmap_write(rt274->regmap, RT274_SET_PIN_HPO, 0x40);
+ regmap_write(rt274->regmap, RT274_SET_PIN_LOUT3, 0x40);
+ regmap_write(rt274->regmap, RT274_SET_MIC, 0x20);
+ regmap_write(rt274->regmap, RT274_SET_PIN_DMIC1, 0x20);
+
+ regmap_update_bits(rt274->regmap, RT274_I2S_CTRL2, 0xc004, 0x4004);
+ regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL,
+ RT274_GPI2_SEL_MASK, RT274_GPI2_SEL_DMIC_CLK);
+
+ /* jack detection */
+ regmap_write(rt274->regmap, RT274_UNSOLICITED_HP_OUT, 0x81);
+ regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82);
+
+ if (rt274->i2c->irq) {
+ ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt274,
+ rt274_dai, ARRAY_SIZE(rt274_dai));
+
+ return ret;
+}
+
+static void rt274_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt274_priv *rt274 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt274);
+}
+
+
+static struct i2c_driver rt274_i2c_driver = {
+ .driver = {
+ .name = "rt274",
+ .acpi_match_table = ACPI_PTR(rt274_acpi_match),
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(rt274_of_match),
+#endif
+ },
+ .probe = rt274_i2c_probe,
+ .remove = rt274_i2c_remove,
+ .id_table = rt274_i2c_id,
+};
+
+module_i2c_driver(rt274_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT274 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt274.h b/sound/soc/codecs/rt274.h
new file mode 100644
index 000000000000..0fcf942fa183
--- /dev/null
+++ b/sound/soc/codecs/rt274.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt274.h -- RT274 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT274_H__
+#define __RT274_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT274_AUDIO_FUNCTION_GROUP 0x01
+#define RT274_DAC_OUT0 0x02
+#define RT274_DAC_OUT1 0x03
+#define RT274_ADC_IN2 0x08
+#define RT274_ADC_IN1 0x09
+#define RT274_DIG_CVT 0x0a
+#define RT274_DMIC1 0x12
+#define RT274_DMIC2 0x13
+#define RT274_MIC 0x19
+#define RT274_LINE1 0x1a
+#define RT274_LINE2 0x1b
+#define RT274_LINE3 0x16
+#define RT274_SPDIF 0x1e
+#define RT274_VENDOR_REGISTERS 0x20
+#define RT274_HP_OUT 0x21
+#define RT274_MIXER_IN1 0x22
+#define RT274_MIXER_IN2 0x23
+#define RT274_INLINE_CMD 0x55
+
+#define RT274_SET_PIN_SFT 6
+#define RT274_SET_PIN_ENABLE 0x40
+#define RT274_SET_PIN_DISABLE 0
+#define RT274_SET_EAPD_HIGH 0x2
+#define RT274_SET_EAPD_LOW 0
+
+#define RT274_MUTE_SFT 7
+
+/* Verb commands */
+#define RT274_RESET\
+ VERB_CMD(AC_VERB_SET_CODEC_RESET, RT274_AUDIO_FUNCTION_GROUP, 0)
+#define RT274_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT274_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT274_SET_AUDIO_POWER RT274_SET_POWER(RT274_AUDIO_FUNCTION_GROUP)
+#define RT274_SET_HPO_POWER RT274_SET_POWER(RT274_HP_OUT)
+#define RT274_SET_DMIC1_POWER RT274_SET_POWER(RT274_DMIC1)
+#define RT274_LOUT_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_LINE3, 0)
+#define RT274_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_HP_OUT, 0)
+#define RT274_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_MIXER_IN1, 0)
+#define RT274_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_MIXER_IN2, 0)
+#define RT274_SET_MIC\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_MIC, 0)
+#define RT274_SET_PIN_LOUT3\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_LINE3, 0)
+#define RT274_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0)
+#define RT274_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0)
+#define RT274_SET_PIN_SPDIF\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_SPDIF, 0)
+#define RT274_SET_PIN_DIG_CVT\
+ VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT274_DIG_CVT, 0)
+#define RT274_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0)
+#define RT274_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0)
+#define RT274_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0)
+#define RT274_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT274_HP_OUT, 0)
+#define RT274_GET_MIC_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT274_MIC, 0)
+#define RT274_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT274_DMIC2, 0)
+#define RT274_SET_SPDIF_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT274_SPDIF, 0)
+#define RT274_DAC0L_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0xa000)
+#define RT274_DAC0R_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0x9000)
+#define RT274_DAC1L_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0xa000)
+#define RT274_DAC1R_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0x9000)
+#define RT274_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0x6000)
+#define RT274_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0x5000)
+#define RT274_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_MIC, 0x7000)
+#define RT274_LOUTL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_LINE3, 0xa000)
+#define RT274_LOUTR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_LINE3, 0x9000)
+#define RT274_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0xa000)
+#define RT274_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0x9000)
+#define RT274_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT274_DAC_OUT0, 0)
+#define RT274_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT274_ADC_IN1, 0)
+#define RT274_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT274_VENDOR_REGISTERS, 0)
+#define RT274_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT274_VENDOR_REGISTERS, 0)
+#define RT274_UNSOLICITED_INLINE_CMD\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_INLINE_CMD, 0)
+#define RT274_UNSOLICITED_HP_OUT\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_HP_OUT, 0)
+#define RT274_UNSOLICITED_MIC\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_MIC, 0)
+#define RT274_COEF58_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, 0x58, 0)
+#define RT274_COEF58_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, 0x58, 0)
+#define RT274_COEF5b_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, 0x5b, 0)
+#define RT274_COEF5b_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, 0x5b, 0)
+#define RT274_SET_STREAMID_DAC0\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_DAC_OUT0, 0)
+#define RT274_SET_STREAMID_DAC1\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_DAC_OUT1, 0)
+#define RT274_SET_STREAMID_ADC1\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_ADC_IN1, 0)
+#define RT274_SET_STREAMID_ADC2\
+ VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_ADC_IN2, 0)
+
+/* Index registers */
+#define RT274_EAPD_GPIO_IRQ_CTRL 0x10
+#define RT274_PAD_CTRL12 0x35
+#define RT274_I2S_CTRL1 0x63
+#define RT274_I2S_CTRL2 0x64
+#define RT274_MCLK_CTRL 0x71
+#define RT274_CLK_CTRL 0x72
+#define RT274_PLL2_CTRL 0x7b
+
+
+/* EAPD GPIO IRQ control (Index 0x10) */
+#define RT274_IRQ_DIS (0x0 << 13)
+#define RT274_IRQ_EN (0x1 << 13)
+#define RT274_IRQ_CLR (0x1 << 12)
+#define RT274_GPI2_SEL_MASK (0x3 << 7)
+#define RT274_GPI2_SEL_GPIO2 (0x0 << 7)
+#define RT274_GPI2_SEL_I2S (0x1 << 7)
+#define RT274_GPI2_SEL_DMIC_CLK (0x2 << 7)
+#define RT274_GPI2_SEL_CBJ (0x3 << 7)
+
+/* Front I2S_Interface control 1 (Index 0x63) */
+#define RT274_I2S_MODE_MASK (0x1 << 11)
+#define RT274_I2S_MODE_S (0x0 << 11)
+#define RT274_I2S_MODE_M (0x1 << 11)
+#define RT274_TDM_DIS (0x0 << 10)
+#define RT274_TDM_EN (0x1 << 10)
+#define RT274_TDM_CH_NUM (0x1 << 7)
+#define RT274_TDM_2CH (0x0 << 7)
+#define RT274_TDM_4CH (0x1 << 7)
+#define RT274_I2S_FMT_MASK (0x3 << 8)
+#define RT274_I2S_FMT_I2S (0x0 << 8)
+#define RT274_I2S_FMT_LJ (0x1 << 8)
+#define RT274_I2S_FMT_PCMA (0x2 << 8)
+#define RT274_I2S_FMT_PCMB (0x3 << 8)
+
+/* MCLK clock domain control (Index 0x71) */
+#define RT274_MCLK_MODE_MASK (0x1 << 14)
+#define RT274_MCLK_MODE_DIS (0x0 << 14)
+#define RT274_MCLK_MODE_EN (0x1 << 14)
+
+/* Clock control (Index 0x72) */
+#define RT274_CLK_SRC_MASK (0x7 << 3)
+#define RT274_CLK_SRC_MCLK (0x0 << 3)
+#define RT274_CLK_SRC_PLL2 (0x3 << 3)
+
+/* PLL2 control (Index 0x7b) */
+#define RT274_PLL2_SRC_MASK (0x1 << 13)
+#define RT274_PLL2_SRC_MCLK (0x0 << 13)
+#define RT274_PLL2_SRC_BCLK (0x1 << 13)
+
+/* HP-OUT (0x21) */
+#define RT274_M_HP_MUX_SFT 14
+#define RT274_HP_SEL_MASK 0x1
+#define RT274_HP_SEL_SFT 0
+#define RT274_HP_SEL_F 0
+#define RT274_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT274_ADC_SEL_MASK 0x7
+#define RT274_ADC_SEL_SFT 0
+#define RT274_ADC_SEL_MIC 0
+#define RT274_ADC_SEL_LINE1 1
+#define RT274_ADC_SEL_LINE2 2
+#define RT274_ADC_SEL_DMIC 3
+
+#define RT274_SCLK_S_MCLK 0
+#define RT274_SCLK_S_PLL1 1
+#define RT274_SCLK_S_PLL2 2
+
+#define RT274_PLL2_S_MCLK 0
+#define RT274_PLL2_S_BCLK 1
+
+enum {
+ RT274_AIF1,
+ RT274_AIFS,
+};
+
+#endif /* __RT274_H__ */
+
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index bc08f0c5a5f6..2fbb5860c421 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt286.c -- RT286 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -40,7 +37,7 @@ struct rt286_priv {
struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt286_platform_data pdata;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
@@ -174,6 +171,9 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
case RT286_PROC_COEF:
case RT286_SET_AMP_GAIN_ADC_IN1:
case RT286_SET_AMP_GAIN_ADC_IN2:
+ case RT286_SET_GPIO_MASK:
+ case RT286_SET_GPIO_DIRECTION:
+ case RT286_SET_GPIO_DATA:
case RT286_SET_POWER(RT286_DAC_OUT1):
case RT286_SET_POWER(RT286_DAC_OUT2):
case RT286_SET_POWER(RT286_ADC_IN1):
@@ -187,13 +187,13 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
}
#ifdef CONFIG_PM
-static void rt286_index_sync(struct snd_soc_codec *codec)
+static void rt286_index_sync(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- snd_soc_write(codec, rt286->index_cache[i].reg,
+ snd_soc_component_write(component, rt286->index_cache[i].reg,
rt286->index_cache[i].def);
}
}
@@ -220,10 +220,10 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*hp = false;
*mic = false;
- if (!rt286->codec)
+ if (!rt286->component)
return -EINVAL;
- dapm = snd_soc_codec_get_dapm(rt286->codec);
+ dapm = snd_soc_component_to_dapm(rt286->component);
if (rt286->pdata.cbj_en) {
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -255,17 +255,24 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
msleep(300);
regmap_read(rt286->regmap,
RT286_CBJ_CTRL2, &val);
- if (0x0070 == (val & 0x0070))
+ if (0x0070 == (val & 0x0070)) {
*mic = true;
- else
+ } else {
*mic = false;
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1,
+ 0xfcc0, 0xc400);
+ }
}
+
regmap_update_bits(rt286->regmap,
RT286_DC_GAIN, 0x200, 0x0);
} else {
*mic = false;
regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0x0400, 0x0000);
}
} else {
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -274,11 +281,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*mic = buf & 0x80000000;
}
- snd_soc_dapm_disable_pin(dapm, "HV");
- snd_soc_dapm_disable_pin(dapm, "VREF");
- if (!*hp)
+ if (!*hp) {
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
snd_soc_dapm_disable_pin(dapm, "LDO1");
- snd_soc_dapm_sync(dapm);
+ snd_soc_dapm_sync(dapm);
+ }
return 0;
}
@@ -293,20 +301,21 @@ static void rt286_jack_detect_work(struct work_struct *work)
rt286_jack_detect(rt286, &hp, &mic);
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
}
-int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+static int rt286_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
rt286->jack = jack;
@@ -327,13 +336,12 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
return 0;
}
-EXPORT_SYMBOL_GPL(rt286_mic_detect);
static int is_mclk_mode(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
if (rt286->clk_id == RT286_SCLK_S_MCLK)
return 1;
@@ -432,15 +440,15 @@ SOC_DAPM_ENUM("SPO source", rt286_spo_enum);
static int rt286_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
break;
@@ -454,32 +462,14 @@ static int rt286_spk_event(struct snd_soc_dapm_widget *w,
static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20);
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0x20);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0);
- break;
- default:
- return 0;
- }
-
- return 0;
-}
-
-static int rt286_vref_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0000);
- mdelay(50);
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0);
break;
default:
return 0;
@@ -491,14 +481,14 @@ static int rt286_vref_event(struct snd_soc_dapm_widget *w,
static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x08);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x30);
break;
default:
return 0;
@@ -510,19 +500,19 @@ static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
break;
default:
@@ -536,7 +526,7 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
12, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
- 0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+ 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
@@ -690,8 +680,8 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
unsigned int val = 0;
int d_len_code;
@@ -703,7 +693,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "Unsupported sample rate %d\n",
+ dev_err(component->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -711,7 +701,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
@@ -719,7 +709,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
@@ -730,12 +720,11 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
- dev_err(codec->dev, "Unsupported channels %d\n",
+ dev_err(component->dev, "Unsupported channels %d\n",
params_channels(params));
return -EINVAL;
}
- d_len_code = 0;
switch (params_width(params)) {
/* bit 6:4 Bits per Sample */
case 16:
@@ -761,27 +750,27 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
- dev_dbg(codec->dev, "format val = 0x%x\n", val);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
- snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val);
- snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x407f, val);
return 0;
}
static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x800, 0x800);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x800, 0x0);
break;
default:
@@ -790,27 +779,27 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x0);
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x1 << 8);
break;
case SND_SOC_DAIFMT_DSP_A:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x2 << 8);
break;
case SND_SOC_DAIFMT_DSP_B:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x3 << 8);
break;
default:
return -EINVAL;
}
/* bit 15 Stream Type 0:PCM 1:Non-PCM */
- snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0);
- snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x8000, 0);
return 0;
}
@@ -818,58 +807,58 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
if (RT286_SCLK_S_MCLK == clk_id) {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x0100, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL1, 0x20, 0x20);
} else {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x0100, 0x0100);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL, 0x4, 0x4);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL1, 0x20, 0x0);
}
switch (freq) {
case 19200000:
if (RT286_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (RT286_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x8, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_CLK_DIV, 0xfc1e, 0x0004);
break;
case 24576000:
case 22579200:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x8, 0x8);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_CLK_DIV, 0xfc1e, 0x5406);
break;
default:
- dev_err(codec->dev, "Unsupported system clock\n");
+ dev_err(component->dev, "Unsupported system clock\n");
return -EINVAL;
}
@@ -881,47 +870,45 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
if (50 == ratio)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x1000, 0x1000);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x1000, 0x0);
return 0;
}
-static int rt286_set_bias_level(struct snd_soc_codec *codec,
+static int rt286_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_write(codec,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_write(component,
RT286_SET_AUDIO_POWER, AC_PWRST_D0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_DC_GAIN, 0x200, 0x200);
}
break;
case SND_SOC_BIAS_ON:
mdelay(10);
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0400);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_DC_GAIN, 0x200, 0x0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SET_AUDIO_POWER, AC_PWRST_D3);
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0000);
break;
default:
@@ -943,10 +930,10 @@ static irqreturn_t rt286_irq(int irq, void *data)
/* Clear IRQ */
regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1);
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
@@ -957,38 +944,31 @@ static irqreturn_t rt286_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt286_probe(struct snd_soc_codec *codec)
+static int rt286_probe(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
- rt286->codec = codec;
+ rt286->component = component;
+ INIT_DELAYED_WORK(&rt286->jack_detect_work, rt286_jack_detect_work);
- if (rt286->i2c->irq) {
- regmap_update_bits(rt286->regmap,
- RT286_IRQ_CTRL, 0x2, 0x2);
-
- INIT_DELAYED_WORK(&rt286->jack_detect_work,
- rt286_jack_detect_work);
+ if (rt286->i2c->irq)
schedule_delayed_work(&rt286->jack_detect_work,
- msecs_to_jiffies(1250));
- }
-
+ msecs_to_jiffies(50));
return 0;
}
-static int rt286_remove(struct snd_soc_codec *codec)
+static void rt286_remove(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt286->jack_detect_work);
-
- return 0;
+ rt286->component = NULL;
}
#ifdef CONFIG_PM
-static int rt286_suspend(struct snd_soc_codec *codec)
+static int rt286_suspend(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt286->regmap, true);
regcache_mark_dirty(rt286->regmap);
@@ -996,12 +976,12 @@ static int rt286_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt286_resume(struct snd_soc_codec *codec)
+static int rt286_resume(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt286->regmap, false);
- rt286_index_sync(codec);
+ rt286_index_sync(component);
regcache_sync(rt286->regmap);
return 0;
@@ -1041,7 +1021,7 @@ static struct snd_soc_dai_driver rt286_dai[] = {
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "rt286-aif2",
@@ -1061,24 +1041,26 @@ static struct snd_soc_dai_driver rt286_dai[] = {
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt286 = {
- .probe = rt286_probe,
- .remove = rt286_remove,
- .suspend = rt286_suspend,
- .resume = rt286_resume,
- .set_bias_level = rt286_set_bias_level,
- .idle_bias_off = true,
- .controls = rt286_snd_controls,
- .num_controls = ARRAY_SIZE(rt286_snd_controls),
- .dapm_widgets = rt286_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
- .dapm_routes = rt286_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt286 = {
+ .probe = rt286_probe,
+ .remove = rt286_remove,
+ .suspend = rt286_suspend,
+ .resume = rt286_resume,
+ .set_bias_level = rt286_set_bias_level,
+ .set_jack = rt286_mic_detect,
+ .controls = rt286_snd_controls,
+ .num_controls = ARRAY_SIZE(rt286_snd_controls),
+ .dapm_widgets = rt286_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
+ .dapm_routes = rt286_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt286_regmap = {
@@ -1095,17 +1077,20 @@ static const struct regmap_config rt286_regmap = {
};
static const struct i2c_device_id rt286_i2c_id[] = {
- {"rt286", 0},
- {"rt288", 0},
+ {"rt286"},
+ {"rt288"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt286_acpi_match[] = {
- { "INT343A", 0 },
- {},
+ { "10EC0286" },
+ { "INT343A" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+#endif
static const struct dmi_system_id force_combo_jack_table[] = {
{
@@ -1120,26 +1105,38 @@ static const struct dmi_system_id force_combo_jack_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
}
},
+ {
+ .ident = "Intel Kabylake RVP",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+ }
+ },
+ {
+ .ident = "Thinkpad Helix 2nd",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix 2nd")
+ }
+ },
+
{ }
};
-static const struct dmi_system_id dmi_dell_dino[] = {
+static const struct dmi_system_id dmi_dell[] = {
{
- .ident = "Dell Dino",
+ .ident = "Dell",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343")
}
},
{ }
};
-static int rt286_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt286_i2c_probe(struct i2c_client *i2c)
{
struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt286_priv *rt286;
- int i, ret, val;
+ int i, ret, vendor_id;
rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
GFP_KERNEL);
@@ -1155,14 +1152,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
}
ret = regmap_read(rt286->regmap,
- RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+ RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &vendor_id);
if (ret != 0) {
dev_err(&i2c->dev, "I2C error %d\n", ret);
return ret;
}
- if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) {
+ if (vendor_id != RT286_VENDOR_ID && vendor_id != RT288_VENDOR_ID) {
dev_err(&i2c->dev,
- "Device with ID register %#x is not rt286\n", val);
+ "Device with ID register %#x is not rt286\n",
+ vendor_id);
return -ENODEV;
}
@@ -1186,8 +1184,8 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt286->pdata = *pdata;
- if (dmi_check_system(force_combo_jack_table) ||
- dmi_check_system(dmi_dell_dino))
+ if ((vendor_id == RT288_VENDOR_ID && dmi_check_system(dmi_dell)) ||
+ dmi_check_system(force_combo_jack_table))
rt286->pdata.cbj_en = true;
regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
@@ -1210,7 +1208,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
mdelay(10);
if (!rt286->pdata.gpio2_en)
- regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x40);
else
regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
@@ -1226,7 +1224,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
- if (dmi_check_system(dmi_dell_dino)) {
+ if (vendor_id == RT288_VENDOR_ID && dmi_check_system(dmi_dell)) {
regmap_update_bits(rt286->regmap,
RT286_SET_GPIO_MASK, 0x40, 0x40);
regmap_update_bits(rt286->regmap,
@@ -1242,26 +1240,24 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
if (ret != 0) {
dev_err(&i2c->dev,
- "Failed to reguest IRQ: %d\n", ret);
+ "Failed to request IRQ: %d\n", ret);
return ret;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt286,
rt286_dai, ARRAY_SIZE(rt286_dai));
return ret;
}
-static int rt286_i2c_remove(struct i2c_client *i2c)
+static void rt286_i2c_remove(struct i2c_client *i2c)
{
struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
if (i2c->irq)
free_irq(i2c->irq, rt286);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h
index 7130edb152ef..4b7a3bd6043d 100644
--- a/sound/soc/codecs/rt286.h
+++ b/sound/soc/codecs/rt286.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt286.h -- RT286 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT286_H__
@@ -199,7 +196,5 @@ enum {
RT286_AIFS,
};
-int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
-
#endif /* __RT286_H__ */
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 30c6de62ae6c..02247593513a 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt298.c -- RT298 ALSA SoC audio codec driver
*
* Copyright 2015 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -17,6 +14,7 @@
#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>
@@ -38,7 +36,7 @@ struct rt298_priv {
struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt298_platform_data pdata;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
@@ -193,13 +191,13 @@ static bool rt298_readable_register(struct device *dev, unsigned int reg)
}
#ifdef CONFIG_PM
-static void rt298_index_sync(struct snd_soc_codec *codec)
+static void rt298_index_sync(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- snd_soc_write(codec, rt298->index_cache[i].reg,
+ snd_soc_component_write(component, rt298->index_cache[i].reg,
rt298->index_cache[i].def);
}
}
@@ -226,10 +224,10 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
*hp = false;
*mic = false;
- if (!rt298->codec)
+ if (!rt298->component)
return -EINVAL;
- dapm = snd_soc_codec_get_dapm(rt298->codec);
+ dapm = snd_soc_component_to_dapm(rt298->component);
if (rt298->pdata.cbj_en) {
regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
@@ -248,6 +246,11 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
snd_soc_dapm_force_enable_pin(dapm, "LDO1");
snd_soc_dapm_sync(dapm);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL1, 0x1001, 0);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL2, 0x4, 0x4);
+
regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24);
msleep(50);
@@ -264,17 +267,24 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
msleep(300);
regmap_read(rt298->regmap,
RT298_CBJ_CTRL2, &val);
- if (0x0070 == (val & 0x0070))
+ if (0x0070 == (val & 0x0070)) {
*mic = true;
- else
+ } else {
*mic = false;
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1,
+ 0xfcc0, 0xc400);
+ }
}
+
regmap_update_bits(rt298->regmap,
RT298_DC_GAIN, 0x200, 0x0);
} else {
*mic = false;
regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20);
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0x0400, 0x0000);
}
} else {
regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
@@ -282,9 +292,10 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
regmap_read(rt298->regmap, RT298_GET_MIC1_SENSE, &buf);
*mic = buf & 0x80000000;
}
-
- snd_soc_dapm_disable_pin(dapm, "HV");
- snd_soc_dapm_disable_pin(dapm, "VREF");
+ if (!*mic) {
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
+ }
if (!*hp)
snd_soc_dapm_disable_pin(dapm, "LDO1");
snd_soc_dapm_sync(dapm);
@@ -305,35 +316,53 @@ static void rt298_jack_detect_work(struct work_struct *work)
if (rt298_jack_detect(rt298, &hp, &mic) < 0)
return;
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt298->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
}
-int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+static int rt298_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
rt298->jack = jack;
- /* Send an initial empty report */
- snd_soc_jack_report(rt298->jack, 0,
- SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ if (jack) {
+ /* Enable IRQ */
+ if (rt298->jack->status & SND_JACK_HEADPHONE)
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ if (rt298->jack->status & SND_JACK_MICROPHONE) {
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
+ }
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt298->jack, rt298->jack->status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ } else {
+ /* Disable IRQ */
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x0);
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
+ snd_soc_dapm_disable_pin(dapm, "LDO1");
+ }
+ snd_soc_dapm_sync(dapm);
return 0;
}
-EXPORT_SYMBOL_GPL(rt298_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 rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
if (rt298->clk_id == RT298_SCLK_S_MCLK)
return 1;
@@ -430,15 +459,15 @@ SOC_DAPM_ENUM("SPO source", rt298_spo_enum);
static int rt298_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT298_SPK_EAPD, RT298_SET_EAPD_HIGH);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT298_SPK_EAPD, RT298_SET_EAPD_LOW);
break;
@@ -452,14 +481,14 @@ static int rt298_spk_event(struct snd_soc_dapm_widget *w,
static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0x20);
+ snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0x20);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT298_SET_PIN_DMIC1, 0);
+ snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0);
break;
default:
return 0;
@@ -471,19 +500,39 @@ static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w,
static int rt298_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int nid;
nid = (w->reg >> 20) & 0xff;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
0x7080, 0x7000);
+ /* If MCLK doesn't exist, reset AD filter */
+ if (!(snd_soc_component_read(component, RT298_VAD_CTRL) & 0x200)) {
+ pr_info("NO MCLK\n");
+ switch (nid) {
+ case RT298_ADC_IN1:
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x2, 0x2);
+ mdelay(10);
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x2, 0x0);
+ break;
+ case RT298_ADC_IN2:
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x4, 0x4);
+ mdelay(10);
+ snd_soc_component_update_bits(component,
+ RT298_D_FILTER_CTRL, 0x4, 0x0);
+ break;
+ }
+ }
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
0x7080, 0x7080);
break;
@@ -497,19 +546,19 @@ static int rt298_adc_event(struct snd_soc_dapm_widget *w,
static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL3, 0xc000, 0x8000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL2, 0xc000, 0x8000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL3, 0xc000, 0x0000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_A_BIAS_CTRL2, 0xc000, 0x0000);
break;
default:
@@ -519,30 +568,12 @@ static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rt298_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,
- RT298_CBJ_CTRL1, 0x0400, 0x0000);
- mdelay(50);
- break;
- default:
- return 0;
- }
-
- return 0;
-}
-
static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1,
12, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1,
- 0, 1, rt298_vref_event, SND_SOC_DAPM_PRE_PMU),
+ 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2,
@@ -715,8 +746,8 @@ static int rt298_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 rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
unsigned int val = 0;
int d_len_code;
@@ -726,7 +757,7 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "Unsupported sample rate %d\n",
+ dev_err(component->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -734,7 +765,7 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt298->sys_clk);
return -EINVAL;
}
@@ -742,7 +773,7 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt298->sys_clk);
return -EINVAL;
}
@@ -753,12 +784,11 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
- dev_err(codec->dev, "Unsupported channels %d\n",
+ dev_err(component->dev, "Unsupported channels %d\n",
params_channels(params));
return -EINVAL;
}
- d_len_code = 0;
switch (params_width(params)) {
/* bit 6:4 Bits per Sample */
case 16:
@@ -784,27 +814,27 @@ static int rt298_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x0018, d_len_code << 3);
- dev_dbg(codec->dev, "format val = 0x%x\n", val);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
- snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x407f, val);
- snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x407f, val);
+ snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x407f, val);
return 0;
}
static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x800, 0x800);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x800, 0x0);
break;
default:
@@ -813,27 +843,27 @@ static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x0);
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x1 << 8);
break;
case SND_SOC_DAIFMT_DSP_A:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x2 << 8);
break;
case SND_SOC_DAIFMT_DSP_B:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x300, 0x3 << 8);
break;
default:
return -EINVAL;
}
/* bit 15 Stream Type 0:PCM 1:Non-PCM */
- snd_soc_update_bits(codec, RT298_DAC_FORMAT, 0x8000, 0);
- snd_soc_update_bits(codec, RT298_ADC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x8000, 0);
return 0;
}
@@ -841,56 +871,56 @@ static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
if (RT298_SCLK_S_MCLK == clk_id) {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x0100, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_PLL_CTRL1, 0x20, 0x20);
} else {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x0100, 0x0100);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_PLL_CTRL1, 0x20, 0x0);
}
switch (freq) {
case 19200000:
if (RT298_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (RT298_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x8, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_CLK_DIV, 0xfc1e, 0x0004);
break;
case 24576000:
case 22579200:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL2, 0x8, 0x8);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_CLK_DIV, 0xfc1e, 0x5406);
break;
default:
- dev_err(codec->dev, "Unsupported system clock\n");
+ dev_err(component->dev, "Unsupported system clock\n");
return -EINVAL;
}
@@ -902,49 +932,41 @@ static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt298_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
if (50 == ratio)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x1000, 0x1000);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT298_I2S_CTRL1, 0x1000, 0x0);
return 0;
}
-static int rt298_set_bias_level(struct snd_soc_codec *codec,
+static int rt298_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY ==
- snd_soc_codec_get_bias_level(codec)) {
- snd_soc_write(codec,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_write(component,
RT298_SET_AUDIO_POWER, AC_PWRST_D0);
- snd_soc_update_bits(codec, 0x0d, 0x200, 0x200);
- snd_soc_update_bits(codec, 0x52, 0x80, 0x0);
+ snd_soc_component_update_bits(component, 0x0d, 0x200, 0x200);
+ snd_soc_component_update_bits(component, 0x52, 0x80, 0x0);
mdelay(20);
- snd_soc_update_bits(codec, 0x0d, 0x200, 0x0);
- snd_soc_update_bits(codec, 0x52, 0x80, 0x80);
+ snd_soc_component_update_bits(component, 0x0d, 0x200, 0x0);
+ snd_soc_component_update_bits(component, 0x52, 0x80, 0x80);
}
break;
- case SND_SOC_BIAS_ON:
- mdelay(30);
- snd_soc_update_bits(codec,
- RT298_CBJ_CTRL1, 0x0400, 0x0400);
-
- break;
-
case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT298_SET_AUDIO_POWER, AC_PWRST_D3);
- snd_soc_update_bits(codec,
- RT298_CBJ_CTRL1, 0x0400, 0x0000);
break;
default:
@@ -967,10 +989,10 @@ static irqreturn_t rt298_irq(int irq, void *data)
regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1);
if (ret == 0) {
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt298->jack, status,
@@ -982,38 +1004,31 @@ static irqreturn_t rt298_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt298_probe(struct snd_soc_codec *codec)
+static int rt298_probe(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
-
- rt298->codec = codec;
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
- if (rt298->i2c->irq) {
- regmap_update_bits(rt298->regmap,
- RT298_IRQ_CTRL, 0x2, 0x2);
+ rt298->component = component;
+ INIT_DELAYED_WORK(&rt298->jack_detect_work, rt298_jack_detect_work);
- INIT_DELAYED_WORK(&rt298->jack_detect_work,
- rt298_jack_detect_work);
+ if (rt298->i2c->irq)
schedule_delayed_work(&rt298->jack_detect_work,
- msecs_to_jiffies(1250));
- }
-
+ msecs_to_jiffies(1250));
return 0;
}
-static int rt298_remove(struct snd_soc_codec *codec)
+static void rt298_remove(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt298->jack_detect_work);
-
- return 0;
+ rt298->component = NULL;
}
#ifdef CONFIG_PM
-static int rt298_suspend(struct snd_soc_codec *codec)
+static int rt298_suspend(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
rt298->is_hp_in = -1;
regcache_cache_only(rt298->regmap, true);
@@ -1022,12 +1037,12 @@ static int rt298_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt298_resume(struct snd_soc_codec *codec)
+static int rt298_resume(struct snd_soc_component *component)
{
- struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec);
+ struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt298->regmap, false);
- rt298_index_sync(codec);
+ rt298_index_sync(component);
regcache_sync(rt298->regmap);
return 0;
@@ -1067,7 +1082,7 @@ static struct snd_soc_dai_driver rt298_dai[] = {
.formats = RT298_FORMATS,
},
.ops = &rt298_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "rt298-aif2",
@@ -1087,24 +1102,26 @@ static struct snd_soc_dai_driver rt298_dai[] = {
.formats = RT298_FORMATS,
},
.ops = &rt298_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt298 = {
- .probe = rt298_probe,
- .remove = rt298_remove,
- .suspend = rt298_suspend,
- .resume = rt298_resume,
- .set_bias_level = rt298_set_bias_level,
- .idle_bias_off = true,
- .controls = rt298_snd_controls,
- .num_controls = ARRAY_SIZE(rt298_snd_controls),
- .dapm_widgets = rt298_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets),
- .dapm_routes = rt298_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt298 = {
+ .probe = rt298_probe,
+ .remove = rt298_remove,
+ .suspend = rt298_suspend,
+ .resume = rt298_resume,
+ .set_bias_level = rt298_set_bias_level,
+ .set_jack = rt298_mic_detect,
+ .controls = rt298_snd_controls,
+ .num_controls = ARRAY_SIZE(rt298_snd_controls),
+ .dapm_widgets = rt298_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets),
+ .dapm_routes = rt298_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt298_regmap = {
@@ -1121,19 +1138,46 @@ static const struct regmap_config rt298_regmap = {
};
static const struct i2c_device_id rt298_i2c_id[] = {
- {"rt298", 0},
+ {"rt298"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt298_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt298_acpi_match[] = {
- { "INT343A", 0 },
- {},
+ { "10EC0298" },
+ { "INT343A" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt298_acpi_match);
+#endif
+
+static const struct dmi_system_id force_combo_jack_table[] = {
+ {
+ .ident = "Intel Broxton P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P")
+ }
+ },
+ {
+ .ident = "Intel Gemini Lake",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake")
+ }
+ },
+ {
+ .ident = "Intel Kabylake R RVP",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+ }
+ },
+ { }
+};
-static int rt298_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt298_i2c_probe(struct i2c_client *i2c)
{
struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt298_priv *rt298;
@@ -1184,11 +1228,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
/* enable jack combo mode on supported devices */
acpiid = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (acpiid) {
+ if (acpiid && acpiid->driver_data) {
rt298->pdata = *(struct rt298_platform_data *)
acpiid->driver_data;
}
+ if (dmi_check_system(force_combo_jack_table)) {
+ rt298->pdata.cbj_en = true;
+ rt298->pdata.gpio2_en = false;
+ }
+
/* VREF Charging */
regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80);
regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860);
@@ -1224,7 +1273,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000);
regmap_update_bits(rt298->regmap,
RT298_WIND_FILTER_CTRL, 0x0082, 0x0082);
- regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81);
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82);
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84);
+ regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2);
+
rt298->is_hp_in = -1;
if (rt298->i2c->irq) {
@@ -1232,26 +1286,24 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
if (ret != 0) {
dev_err(&i2c->dev,
- "Failed to reguest IRQ: %d\n", ret);
+ "Failed to request IRQ: %d\n", ret);
return ret;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt298,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt298,
rt298_dai, ARRAY_SIZE(rt298_dai));
return ret;
}
-static int rt298_i2c_remove(struct i2c_client *i2c)
+static void rt298_i2c_remove(struct i2c_client *i2c)
{
struct rt298_priv *rt298 = i2c_get_clientdata(i2c);
if (i2c->irq)
free_irq(i2c->irq, rt298);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h
index 31da16265f2b..f1be9c135401 100644
--- a/sound/soc/codecs/rt298.h
+++ b/sound/soc/codecs/rt298.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt298.h -- RT298 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 __RT298_H__
@@ -34,6 +31,7 @@
#define RT298_HP_OUT 0x21
#define RT298_MIXER_IN1 0x22
#define RT298_MIXER_IN2 0x23
+#define RT298_INLINE_CMD 0x55
#define RT298_SET_PIN_SFT 6
#define RT298_SET_PIN_ENABLE 0x40
@@ -124,12 +122,19 @@
VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0)
#define RT298_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0)
+#define RT298_UNSOLICITED_INLINE_CMD\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0)
+#define RT298_UNSOLICITED_HP_OUT\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0)
+#define RT298_UNSOLICITED_MIC1\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0)
/* Index registers */
#define RT298_A_BIAS_CTRL1 0x01
#define RT298_A_BIAS_CTRL2 0x02
#define RT298_POWER_CTRL1 0x03
#define RT298_A_BIAS_CTRL3 0x04
+#define RT298_D_FILTER_CTRL 0x05
#define RT298_POWER_CTRL2 0x08
#define RT298_I2S_CTRL1 0x09
#define RT298_I2S_CTRL2 0x0a
@@ -141,6 +146,7 @@
#define RT298_IRQ_CTRL 0x33
#define RT298_WIND_FILTER_CTRL 0x46
#define RT298_PLL_CTRL1 0x49
+#define RT298_VAD_CTRL 0x4e
#define RT298_CBJ_CTRL1 0x4f
#define RT298_CBJ_CTRL2 0x50
#define RT298_PLL_CTRL 0x63
@@ -148,6 +154,7 @@
#define RT298_DEPOP_CTRL2 0x67
#define RT298_DEPOP_CTRL3 0x68
#define RT298_DEPOP_CTRL4 0x69
+#define RT298_IRQ_FLAG_CTRL 0x7c
/* SPDIF (0x06) */
#define RT298_SPDIF_SEL_SFT 0
@@ -200,7 +207,5 @@ enum {
RT298_AIFS,
};
-int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
-
#endif /* __RT298_H__ */
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
new file mode 100644
index 000000000000..54d84581ec47
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5514-spi.c -- RT5514 SPI driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5514-spi.h"
+
+#define DRV_NAME "rt5514-spi"
+
+static struct spi_device *rt5514_spi;
+
+struct rt5514_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ unsigned int buf_base, buf_limit, buf_rp;
+ size_t buf_size, get_size, dma_offset;
+};
+
+static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 0x20000,
+};
+
+static struct snd_soc_dai_driver rt5514_spi_dai = {
+ .name = "rt5514-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static void rt5514_spi_copy_work(struct work_struct *work)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ container_of(work, struct rt5514_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime;
+ size_t period_bytes, truncated_bytes = 0;
+ unsigned int cur_wp, remain_data;
+ u8 buf[8];
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ if (!rt5514_dsp->substream) {
+ dev_err(rt5514_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ runtime = rt5514_dsp->substream->runtime;
+ period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
+ if (!period_bytes) {
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+ goto done;
+ }
+
+ if (rt5514_dsp->buf_size % period_bytes)
+ rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) *
+ period_bytes;
+
+ if (rt5514_dsp->get_size >= rt5514_dsp->buf_size) {
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
+ sizeof(buf));
+ cur_wp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ if (cur_wp >= rt5514_dsp->buf_rp)
+ remain_data = (cur_wp - rt5514_dsp->buf_rp);
+ else
+ remain_data =
+ (rt5514_dsp->buf_limit - rt5514_dsp->buf_rp) +
+ (cur_wp - rt5514_dsp->buf_base);
+
+ if (remain_data < period_bytes) {
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+ goto done;
+ }
+ }
+
+ if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ period_bytes);
+
+ if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit)
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base;
+ else
+ rt5514_dsp->buf_rp += period_bytes;
+ } else {
+ truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp;
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ truncated_bytes);
+
+ rt5514_spi_burst_read(rt5514_dsp->buf_base,
+ runtime->dma_area + rt5514_dsp->dma_offset +
+ truncated_bytes, period_bytes - truncated_bytes);
+
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base + period_bytes -
+ truncated_bytes;
+ }
+
+ rt5514_dsp->get_size += period_bytes;
+ rt5514_dsp->dma_offset += period_bytes;
+ if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+ rt5514_dsp->dma_offset = 0;
+
+ snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+
+done:
+ mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp)
+{
+ u8 buf[8];
+
+ if (!rt5514_dsp->substream)
+ return;
+
+ rt5514_dsp->get_size = 0;
+
+ /**
+ * The address area x1800XXXX is the register address, and it cannot
+ * support spi burst read perfectly. So we use the spi burst read
+ * individually to make sure the data correctly.
+ */
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ if (rt5514_dsp->buf_rp % 8)
+ rt5514_dsp->buf_rp = (rt5514_dsp->buf_rp / 8) * 8;
+
+ rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base;
+
+ if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
+ rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+}
+
+static irqreturn_t rt5514_spi_irq(int irq, void *data)
+{
+ struct rt5514_dsp *rt5514_dsp = data;
+
+ rt5514_schedule_copy(rt5514_dsp);
+
+ return IRQ_HANDLED;
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+ u8 buf[8];
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ rt5514_dsp->substream = substream;
+ rt5514_dsp->dma_offset = 0;
+
+ /* Read IRQ status and schedule copy accordingly. */
+ rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf));
+ if (buf[0] & RT5514_IRQ_STATUS_BIT)
+ rt5514_schedule_copy(rt5514_dsp);
+
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ rt5514_dsp->substream = NULL;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ cancel_delayed_work_sync(&rt5514_dsp->copy_work);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+
+static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
+{
+ struct rt5514_dsp *rt5514_dsp;
+ int ret;
+
+ rt5514_dsp = devm_kzalloc(component->dev, sizeof(*rt5514_dsp),
+ GFP_KERNEL);
+ if (!rt5514_dsp)
+ return -ENOMEM;
+
+ rt5514_dsp->dev = &rt5514_spi->dev;
+ mutex_init(&rt5514_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
+ snd_soc_component_set_drvdata(component, rt5514_dsp);
+
+ if (rt5514_spi->irq) {
+ ret = devm_request_threaded_irq(&rt5514_spi->dev,
+ rt5514_spi->irq, NULL, rt5514_spi_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5514-spi",
+ rt5514_dsp);
+ if (ret)
+ dev_err(&rt5514_spi->dev,
+ "%s Failed to request IRQ: %d\n", __func__,
+ ret);
+ else
+ device_init_wakeup(rt5514_dsp->dev, true);
+ }
+
+ return 0;
+}
+
+static int rt5514_spi_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ return 0;
+}
+
+static const struct snd_soc_component_driver rt5514_spi_component = {
+ .name = DRV_NAME,
+ .probe = rt5514_spi_pcm_probe,
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .pointer = rt5514_spi_pcm_pointer,
+ .pcm_construct = rt5514_spi_pcm_new,
+ .legacy_dai_naming = 1,
+};
+
+/**
+ * rt5514_spi_burst_read - Read data from SPI by rt5514 address.
+ * @addr: Start address.
+ * @rxbuf: Data Buffer for reading.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_READ;
+ int status;
+ u8 write_buf[8];
+ unsigned int i, end, offset = 0;
+
+ struct spi_message message;
+ struct spi_transfer x[3];
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 5;
+ x[0].tx_buf = write_buf;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = 4;
+ x[1].tx_buf = write_buf;
+ spi_message_add_tail(&x[1], &message);
+
+ x[2].len = end;
+ x[2].rx_buf = rxbuf + offset;
+ spi_message_add_tail(&x[2], &message);
+
+ status = spi_sync(rt5514_spi, &message);
+
+ if (status)
+ return false;
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ for (i = 0; i < len; i += 8) {
+ write_buf[0] = rxbuf[i + 0];
+ write_buf[1] = rxbuf[i + 1];
+ write_buf[2] = rxbuf[i + 2];
+ write_buf[3] = rxbuf[i + 3];
+ write_buf[4] = rxbuf[i + 4];
+ write_buf[5] = rxbuf[i + 5];
+ write_buf[6] = rxbuf[i + 6];
+ write_buf[7] = rxbuf[i + 7];
+
+ rxbuf[i + 0] = write_buf[7];
+ rxbuf[i + 1] = write_buf[6];
+ rxbuf[i + 2] = write_buf[5];
+ rxbuf[i + 3] = write_buf[4];
+ rxbuf[i + 4] = write_buf[3];
+ rxbuf[i + 5] = write_buf[2];
+ rxbuf[i + 6] = write_buf[1];
+ rxbuf[i + 7] = write_buf[0];
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_read);
+
+/**
+ * rt5514_spi_burst_write - Write data to SPI by rt5514 address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE;
+ u8 *write_buf;
+ unsigned int i, end, offset = 0;
+
+ write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+ if (write_buf == NULL)
+ return -ENOMEM;
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ for (i = 0; i < end; i += 8) {
+ write_buf[i + 12] = txbuf[offset + i + 0];
+ write_buf[i + 11] = txbuf[offset + i + 1];
+ write_buf[i + 10] = txbuf[offset + i + 2];
+ write_buf[i + 9] = txbuf[offset + i + 3];
+ write_buf[i + 8] = txbuf[offset + i + 4];
+ write_buf[i + 7] = txbuf[offset + i + 5];
+ write_buf[i + 6] = txbuf[offset + i + 6];
+ write_buf[i + 5] = txbuf[offset + i + 7];
+ }
+
+ write_buf[end + 5] = spi_cmd;
+
+ spi_write(rt5514_spi, write_buf, end + 6);
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ kfree(write_buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_write);
+
+static int rt5514_spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ rt5514_spi = spi;
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &rt5514_spi_component,
+ &rt5514_spi_dai, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register component.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rt5514_suspend(struct device *dev)
+{
+ int irq = to_spi_device(dev)->irq;
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(irq);
+
+ return 0;
+}
+
+static int rt5514_resume(struct device *dev)
+{
+ struct rt5514_dsp *rt5514_dsp = dev_get_drvdata(dev);
+ int irq = to_spi_device(dev)->irq;
+ u8 buf[8];
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(irq);
+
+ if (rt5514_dsp) {
+ if (rt5514_dsp->substream) {
+ rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf,
+ sizeof(buf));
+ if (buf[0] & RT5514_IRQ_STATUS_BIT)
+ rt5514_schedule_copy(rt5514_dsp);
+ }
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt5514_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume)
+};
+
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+
+static struct spi_driver rt5514_spi_driver = {
+ .driver = {
+ .name = "rt5514",
+ .pm = pm_ptr(&rt5514_pm_ops),
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ },
+ .probe = rt5514_spi_probe,
+};
+module_spi_driver(rt5514_spi_driver);
+
+MODULE_DESCRIPTION("RT5514 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h
new file mode 100644
index 000000000000..cedb19709c9a
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5514-spi.h -- RT5514 driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#ifndef __RT5514_SPI_H__
+#define __RT5514_SPI_H__
+
+/**
+ * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller.
+*/
+#define RT5514_SPI_BUF_LEN 240
+
+#define RT5514_BUFFER_VOICE_BASE 0x18000200
+#define RT5514_BUFFER_VOICE_LIMIT 0x18000204
+#define RT5514_BUFFER_VOICE_WP 0x1800020c
+#define RT5514_IRQ_CTRL 0x18002094
+
+#define RT5514_IRQ_STATUS_BIT (0x1 << 5)
+
+/* SPI Command */
+enum {
+ RT5514_SPI_CMD_16_READ = 0,
+ RT5514_SPI_CMD_16_WRITE,
+ RT5514_SPI_CMD_32_READ,
+ RT5514_SPI_CMD_32_WRITE,
+ RT5514_SPI_CMD_BURST_READ,
+ RT5514_SPI_CMD_BURST_WRITE,
+};
+
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
+
+#endif /* __RT5514_SPI_H__ */
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
new file mode 100644
index 000000000000..649b44b790b0
--- /dev/null
+++ b/sound/soc/codecs/rt5514.c
@@ -0,0 +1,1335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5514.c -- RT5514 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5514.h"
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
+
+static const struct reg_sequence rt5514_i2c_patch[] = {
+ {0x1800101c, 0x00000000},
+ {0x18001100, 0x0000031f},
+ {0x18001104, 0x00000007},
+ {0x18001108, 0x00000000},
+ {0x1800110c, 0x00000000},
+ {0x18001110, 0x00000000},
+ {0x18001114, 0x00000001},
+ {0x18001118, 0x00000000},
+ {0x18002f08, 0x00000006},
+ {0x18002f00, 0x00055149},
+ {0x18002f00, 0x0005514b},
+ {0x18002f00, 0x00055149},
+ {0xfafafafa, 0x00000001},
+ {0x18002f10, 0x00000001},
+ {0x18002f10, 0x00000000},
+ {0x18002f10, 0x00000001},
+ {0xfafafafa, 0x00000001},
+ {0x18002000, 0x000010ec},
+ {0xfafafafa, 0x00000000},
+};
+
+static const struct reg_sequence rt5514_patch[] = {
+ {RT5514_DIG_IO_CTRL, 0x00000040},
+ {RT5514_CLK_CTRL1, 0x38020041},
+ {RT5514_SRC_CTRL, 0x44000eee},
+ {RT5514_ANA_CTRL_LDO10, 0x00028604},
+ {RT5514_ANA_CTRL_ADCFED, 0x00000800},
+ {RT5514_ASRC_IN_CTRL1, 0x00000003},
+ {RT5514_DOWNFILTER0_CTRL3, 0x10000342},
+ {RT5514_DOWNFILTER1_CTRL3, 0x10000342},
+};
+
+static const struct reg_default rt5514_reg[] = {
+ {RT5514_RESET, 0x00000000},
+ {RT5514_PWR_ANA1, 0x00808880},
+ {RT5514_PWR_ANA2, 0x00220000},
+ {RT5514_I2S_CTRL1, 0x00000330},
+ {RT5514_I2S_CTRL2, 0x20000000},
+ {RT5514_VAD_CTRL6, 0xc00007d2},
+ {RT5514_EXT_VAD_CTRL, 0x80000080},
+ {RT5514_DIG_IO_CTRL, 0x00000040},
+ {RT5514_PAD_CTRL1, 0x00804000},
+ {RT5514_DMIC_DATA_CTRL, 0x00000005},
+ {RT5514_DIG_SOURCE_CTRL, 0x00000002},
+ {RT5514_SRC_CTRL, 0x44000eee},
+ {RT5514_DOWNFILTER2_CTRL1, 0x0000882f},
+ {RT5514_PLL_SOURCE_CTRL, 0x00000004},
+ {RT5514_CLK_CTRL1, 0x38020041},
+ {RT5514_CLK_CTRL2, 0x00000000},
+ {RT5514_PLL3_CALIB_CTRL1, 0x00400200},
+ {RT5514_PLL3_CALIB_CTRL5, 0x40220012},
+ {RT5514_DELAY_BUF_CTRL1, 0x7fff006a},
+ {RT5514_DELAY_BUF_CTRL3, 0x00000000},
+ {RT5514_ASRC_IN_CTRL1, 0x00000003},
+ {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f},
+ {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f},
+ {RT5514_DOWNFILTER0_CTRL3, 0x10000342},
+ {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f},
+ {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f},
+ {RT5514_DOWNFILTER1_CTRL3, 0x10000342},
+ {RT5514_ANA_CTRL_LDO10, 0x00028604},
+ {RT5514_ANA_CTRL_LDO18_16, 0x02000345},
+ {RT5514_ANA_CTRL_ADC12, 0x0000a2a8},
+ {RT5514_ANA_CTRL_ADC21, 0x00001180},
+ {RT5514_ANA_CTRL_ADC22, 0x0000aaa8},
+ {RT5514_ANA_CTRL_ADC23, 0x00151427},
+ {RT5514_ANA_CTRL_MICBST, 0x00002000},
+ {RT5514_ANA_CTRL_ADCFED, 0x00000800},
+ {RT5514_ANA_CTRL_INBUF, 0x00000143},
+ {RT5514_ANA_CTRL_VREF, 0x00008d50},
+ {RT5514_ANA_CTRL_PLL3, 0x0000000e},
+ {RT5514_ANA_CTRL_PLL1_1, 0x00000000},
+ {RT5514_ANA_CTRL_PLL1_2, 0x00030220},
+ {RT5514_DMIC_LP_CTRL, 0x00000000},
+ {RT5514_MISC_CTRL_DSP, 0x00000000},
+ {RT5514_DSP_CTRL1, 0x00055149},
+ {RT5514_DSP_CTRL3, 0x00000006},
+ {RT5514_DSP_CTRL4, 0x00000001},
+ {RT5514_VENDOR_ID1, 0x00000001},
+ {RT5514_VENDOR_ID2, 0x10ec5514},
+};
+
+static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514)
+{
+ /* Reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
+ /* LDO_I_limit */
+ regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
+ /* I2C bypass enable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001);
+ /* mini-core reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+ /* I2C bypass disable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000);
+ /* PIN config */
+ regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
+ /* PLL3(QN)=RCOSC*(10+2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
+ /* PLL3 source=RCOSC, fsi=rt_clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
+ /* Power on RCOSC, pll3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+ /* DSP clk source = pll3, ENABLE DSP clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+ /* Enable DSP clk auto switch */
+ regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
+ /* Reduce DSP power */
+ regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
+}
+
+static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_VENDOR_ID1:
+ case RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt5514_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_RESET:
+ case RT5514_PWR_ANA1:
+ case RT5514_PWR_ANA2:
+ case RT5514_I2S_CTRL1:
+ case RT5514_I2S_CTRL2:
+ case RT5514_VAD_CTRL6:
+ case RT5514_EXT_VAD_CTRL:
+ case RT5514_DIG_IO_CTRL:
+ case RT5514_PAD_CTRL1:
+ case RT5514_DMIC_DATA_CTRL:
+ case RT5514_DIG_SOURCE_CTRL:
+ case RT5514_SRC_CTRL:
+ case RT5514_DOWNFILTER2_CTRL1:
+ case RT5514_PLL_SOURCE_CTRL:
+ case RT5514_CLK_CTRL1:
+ case RT5514_CLK_CTRL2:
+ case RT5514_PLL3_CALIB_CTRL1:
+ case RT5514_PLL3_CALIB_CTRL5:
+ case RT5514_DELAY_BUF_CTRL1:
+ case RT5514_DELAY_BUF_CTRL3:
+ case RT5514_ASRC_IN_CTRL1:
+ case RT5514_DOWNFILTER0_CTRL1:
+ case RT5514_DOWNFILTER0_CTRL2:
+ case RT5514_DOWNFILTER0_CTRL3:
+ case RT5514_DOWNFILTER1_CTRL1:
+ case RT5514_DOWNFILTER1_CTRL2:
+ case RT5514_DOWNFILTER1_CTRL3:
+ case RT5514_ANA_CTRL_LDO10:
+ case RT5514_ANA_CTRL_LDO18_16:
+ case RT5514_ANA_CTRL_ADC12:
+ case RT5514_ANA_CTRL_ADC21:
+ case RT5514_ANA_CTRL_ADC22:
+ case RT5514_ANA_CTRL_ADC23:
+ case RT5514_ANA_CTRL_MICBST:
+ case RT5514_ANA_CTRL_ADCFED:
+ case RT5514_ANA_CTRL_INBUF:
+ case RT5514_ANA_CTRL_VREF:
+ case RT5514_ANA_CTRL_PLL3:
+ case RT5514_ANA_CTRL_PLL1_1:
+ case RT5514_ANA_CTRL_PLL1_2:
+ case RT5514_DMIC_LP_CTRL:
+ case RT5514_MISC_CTRL_DSP:
+ case RT5514_DSP_CTRL1:
+ case RT5514_DSP_CTRL3:
+ case RT5514_DSP_CTRL4:
+ case RT5514_VENDOR_ID1:
+ case RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt5514_i2c_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_DSP_MAPPING | RT5514_RESET:
+ case RT5514_DSP_MAPPING | RT5514_PWR_ANA1:
+ case RT5514_DSP_MAPPING | RT5514_PWR_ANA2:
+ case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6:
+ case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_SRC_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5:
+ case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_ASRC_IN_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2:
+ case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4:
+ case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1:
+ case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+
+static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5514->dsp_enabled;
+
+ return 0;
+}
+
+static int rt5514_calibration(struct rt5514_priv *rt5514, bool on)
+{
+ if (on) {
+ regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL3, 0x0000000a);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf,
+ 0xa);
+ regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301,
+ 0x301);
+ regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL4,
+ 0x80000000 | rt5514->pll3_cal_value);
+ regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL1,
+ 0x8bb80800);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0x80000000);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0xc0000000);
+ } else {
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0x40000000);
+ regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, 0);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf,
+ 0x4);
+ }
+
+ return 0;
+}
+
+static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ const struct firmware *fw = NULL;
+ u8 buf[8];
+
+ if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
+ return 0;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ rt5514->dsp_enabled = ucontrol->value.integer.value[0];
+
+ if (rt5514->dsp_enabled) {
+ if (rt5514->pdata.dsp_calib_clk_name &&
+ !IS_ERR(rt5514->dsp_calib_clk)) {
+ if (clk_set_rate(rt5514->dsp_calib_clk,
+ rt5514->pdata.dsp_calib_clk_rate))
+ dev_err(component->dev,
+ "Can't set rate for mclk");
+
+ if (clk_prepare_enable(rt5514->dsp_calib_clk))
+ dev_err(component->dev,
+ "Can't enable dsp_calib_clk");
+
+ rt5514_calibration(rt5514, true);
+
+ msleep(20);
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_read(RT5514_PLL3_CALIB_CTRL6 |
+ RT5514_DSP_MAPPING, buf, sizeof(buf));
+#else
+ dev_err(component->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+ memset(buf, 0, sizeof(buf));
+#endif
+ rt5514->pll3_cal_value = buf[0] | buf[1] << 8 |
+ buf[2] << 16 | buf[3] << 24;
+
+ rt5514_calibration(rt5514, false);
+ clk_disable_unprepare(rt5514->dsp_calib_clk);
+ }
+
+ rt5514_enable_dsp_prepare(rt5514);
+
+ request_firmware(&fw, RT5514_FIRMWARE1, component->dev);
+ if (fw) {
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ff60000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(component->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ request_firmware(&fw, RT5514_FIRMWARE2, component->dev);
+ if (fw) {
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ffc0000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(component->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ /* DSP run */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00,
+ 0x00055148);
+
+ if (rt5514->pdata.dsp_calib_clk_name &&
+ !IS_ERR(rt5514->dsp_calib_clk)) {
+ msleep(20);
+
+ regmap_write(rt5514->i2c_regmap, 0x1800211c,
+ rt5514->pll3_cal_value);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0x00220012);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0x80220042);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0xe0220042);
+ }
+ } else {
+ regmap_multi_reg_write(rt5514->i2c_regmap,
+ rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new rt5514_snd_controls[] = {
+ SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
+ RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
+ SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0,
+ rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put),
+};
+
+/* ADC Mixer*/
+static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+/* DMIC Source */
+static const char * const rt5514_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+ RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+ RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto2_dmic_mux =
+ SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum);
+
+/**
+ * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
+ *
+ * @component: only used for dev_warn
+ * @rate: base clock rate.
+ *
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
+ */
+static int rt5514_calc_dmic_clk(struct snd_soc_component *component, int rate)
+{
+ static const int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
+ int i;
+
+ if (rate < 1000000 * div[0]) {
+ pr_warn("Base clock rate %d is too low\n", rate);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ /* find divider that gives DMIC frequency below 3.072MHz */
+ if (3072000 * div[i] >= rate)
+ return i;
+ }
+
+ dev_warn(component->dev, "Base clock rate %d is too high\n", rate);
+ return -EINVAL;
+}
+
+static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ int idx;
+
+ idx = rt5514_calc_dmic_clk(component, rt5514->sysclk);
+ if (idx < 0)
+ dev_err(component->dev, "Failed to set DMIC clock\n");
+ else
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
+ RT5514_CLK_DMIC_OUT_SEL_MASK,
+ idx << RT5514_CLK_DMIC_OUT_SEL_SFT);
+
+ if (rt5514->pdata.dmic_init_delay)
+ msleep(rt5514->pdata.dmic_init_delay);
+
+ return idx;
+}
+
+static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt5514_i2s_use_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ return (rt5514->sysclk > rt5514->lrck * 384);
+}
+
+static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1L"),
+ SND_SOC_DAPM_INPUT("DMIC1R"),
+ SND_SOC_DAPM_INPUT("DMIC2L"),
+ SND_SOC_DAPM_INPUT("DMIC2R"),
+
+ SND_SOC_DAPM_INPUT("AMICL"),
+ SND_SOC_DAPM_INPUT("AMICR"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("DMIC CLK", 1, SND_SOC_NOPM, 0, 0,
+ rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1,
+ RT5514_POW_LDO18_IN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1,
+ RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1,
+ RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1,
+ RT5514_POW_BG_LDO21_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2,
+ RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2,
+ RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2,
+ RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ASRC AD1", 1, RT5514_CLK_CTRL2,
+ RT5514_CLK_AD0_ASRC_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ASRC AD2", 1, RT5514_CLK_CTRL2,
+ RT5514_CLK_AD1_ASRC_EN_BIT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5514_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5514_sto2_dmic_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD0_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD1_EN_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)),
+
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_AD_MUTE_BIT, 1),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
+ { "DMIC1", NULL, "DMIC1L" },
+ { "DMIC1", NULL, "DMIC1R" },
+ { "DMIC2", NULL, "DMIC2L" },
+ { "DMIC2", NULL, "DMIC2R" },
+
+ { "DMIC1L", NULL, "DMIC CLK" },
+ { "DMIC1R", NULL, "DMIC CLK" },
+ { "DMIC2L", NULL, "DMIC CLK" },
+ { "DMIC2R", NULL, "DMIC CLK" },
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+
+ { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" },
+ { "Sto1 ADC MIXL", "ADC Switch", "AMICL" },
+ { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" },
+ { "Sto1 ADC MIXR", "ADC Switch", "AMICR" },
+
+ { "ADC Power", NULL, "LDO18 IN" },
+ { "ADC Power", NULL, "LDO18 ADC" },
+ { "ADC Power", NULL, "LDO21" },
+ { "ADC Power", NULL, "BG LDO18 IN" },
+ { "ADC Power", NULL, "BG LDO21" },
+ { "ADC Power", NULL, "BG MBIAS" },
+ { "ADC Power", NULL, "MBIAS" },
+ { "ADC Power", NULL, "VREF2" },
+ { "ADC Power", NULL, "VREF1" },
+
+ { "ADCL Power", NULL, "LDO16L" },
+ { "ADCL Power", NULL, "ADC1L" },
+ { "ADCL Power", NULL, "BSTL2" },
+ { "ADCL Power", NULL, "BSTL" },
+ { "ADCL Power", NULL, "ADCFEDL" },
+
+ { "ADCR Power", NULL, "LDO16R" },
+ { "ADCR Power", NULL, "ADC1R" },
+ { "ADCR Power", NULL, "BSTR2" },
+ { "ADCR Power", NULL, "BSTR" },
+ { "ADCR Power", NULL, "ADCFEDR" },
+
+ { "AMICL", NULL, "ADC CLK" },
+ { "AMICL", NULL, "ADC Power" },
+ { "AMICL", NULL, "ADCL Power" },
+ { "AMICR", NULL, "ADC CLK" },
+ { "AMICR", NULL, "ADC Power" },
+ { "AMICR", NULL, "ADCR Power" },
+
+ { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" },
+ { "PLL1", NULL, "PLL1 LDO" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+ { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+ { "adc stereo1 filter", NULL, "ASRC AD1", rt5514_i2s_use_asrc },
+
+ { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+
+ { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" },
+ { "Sto2 ADC MIXL", "ADC Switch", "AMICL" },
+ { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" },
+ { "Sto2 ADC MIXR", "ADC Switch", "AMICR" },
+
+ { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" },
+ { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
+ { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" },
+ { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+ { "adc stereo2 filter", NULL, "ASRC AD2", rt5514_i2s_use_asrc },
+
+ { "AIF1TX", NULL, "Stereo1 ADC MIX"},
+ { "AIF1TX", NULL, "Stereo2 ADC MIX"},
+};
+
+static int rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0;
+
+ rt5514->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt5514->bclk = rt5514->lrck * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5514->bclk, rt5514->lrck);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len = RT5514_I2S_DL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len = RT5514_I2S_DL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ val_len = RT5514_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK,
+ val_len);
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
+ RT5514_CLK_AD_ANA1_SEL_MASK,
+ (pre_div + 1) << RT5514_CLK_AD_ANA1_SEL_SFT);
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK,
+ pre_div << RT5514_CLK_SYS_DIV_OUT_SFT |
+ pre_div << RT5514_SEL_ADC_OSR_SFT);
+
+ return 0;
+}
+
+static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ reg_val |= RT5514_I2S_LR_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5514_I2S_BP_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5514_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5514_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5514_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1,
+ RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK,
+ reg_val);
+
+ return 0;
+}
+
+static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5514_SCLK_S_MCLK:
+ reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK;
+ break;
+
+ case RT5514_SCLK_S_PLL1:
+ reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ rt5514->sysclk = freq;
+ rt5514->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5514->pll_in = 0;
+ rt5514->pll_out = 0;
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_PRE_SEL_MASK,
+ RT5514_CLK_SYS_PRE_SEL_MCLK);
+
+ return 0;
+ }
+
+ if (source == rt5514->pll_src && freq_in == rt5514->pll_in &&
+ freq_out == rt5514->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT5514_PLL1_S_MCLK:
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+ RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK);
+ break;
+
+ case RT5514_PLL1_S_BCLK:
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+ RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1,
+ pll_code.k_code << RT5514_PLL_K_SFT |
+ pll_code.n_code << RT5514_PLL_N_SFT |
+ (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT);
+ regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2,
+ RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT);
+
+ rt5514->pll_in = freq_in;
+ rt5514->pll_out = freq_out;
+ rt5514->pll_src = source;
+
+ return 0;
+}
+
+static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, val2 = 0;
+
+ if (rx_mask || tx_mask)
+ val |= RT5514_TDM_MODE;
+
+ switch (tx_mask) {
+ case 0x3:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 |
+ RT5514_TDM_DOCKING_START_SLOT0;
+ break;
+
+ case 0x30:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 |
+ RT5514_TDM_DOCKING_START_SLOT4;
+ break;
+
+ case 0xf:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 |
+ RT5514_TDM_DOCKING_START_SLOT0;
+ break;
+
+ case 0xf0:
+ val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 |
+ RT5514_TDM_DOCKING_START_SLOT4;
+ break;
+
+ default:
+ break;
+ }
+
+
+
+ switch (slots) {
+ case 4:
+ val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
+ break;
+
+ case 6:
+ val |= RT5514_TDMSLOT_SEL_RX_6CH | RT5514_TDMSLOT_SEL_TX_6CH;
+ break;
+
+ case 8:
+ val |= RT5514_TDMSLOT_SEL_RX_8CH | RT5514_TDMSLOT_SEL_TX_8CH;
+ break;
+
+ case 2:
+ default:
+ break;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20;
+ break;
+
+ case 24:
+ val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24;
+ break;
+
+ case 25:
+ val |= RT5514_TDM_MODE2;
+ break;
+
+ case 32:
+ val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32;
+ break;
+
+ case 16:
+ default:
+ break;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE |
+ RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK |
+ RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK |
+ RT5514_TDM_MODE2, val);
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL2,
+ RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH_MASK |
+ RT5514_TDM_DOCKING_START_MASK, val2);
+
+ return 0;
+}
+
+static int rt5514_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5514->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5514->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ /*
+ * If the DSP is enabled in start of recording, the DSP
+ * should be disabled, and sync back to normal recording
+ * settings to make sure recording properly.
+ */
+ if (rt5514->dsp_enabled) {
+ rt5514->dsp_enabled = 0;
+ regmap_multi_reg_write(rt5514->i2c_regmap,
+ rt5514_i2c_patch,
+ ARRAY_SIZE(rt5514_i2c_patch));
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5514_probe(struct snd_soc_component *component)
+{
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct platform_device *pdev = to_platform_device(component->dev);
+
+ rt5514->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5514->mclk))
+ return PTR_ERR(rt5514->mclk);
+
+ if (rt5514->pdata.dsp_calib_clk_name) {
+ rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev,
+ rt5514->pdata.dsp_calib_clk_name);
+ if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
+ rt5514->component = component;
+ rt5514->pll3_cal_value = 0x0078b000;
+
+ return 0;
+}
+
+static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+ regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+ return 0;
+}
+
+static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct i2c_client *client = context;
+ struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+ regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+ return 0;
+}
+
+#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5514_aif_dai_ops = {
+ .hw_params = rt5514_hw_params,
+ .set_fmt = rt5514_set_dai_fmt,
+ .set_sysclk = rt5514_set_dai_sysclk,
+ .set_pll = rt5514_set_dai_pll,
+ .set_tdm_slot = rt5514_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt5514_dai[] = {
+ {
+ .name = "rt5514-aif1",
+ .id = 0,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT5514_STEREO_RATES,
+ .formats = RT5514_FORMATS,
+ },
+ .ops = &rt5514_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5514 = {
+ .probe = rt5514_probe,
+ .set_bias_level = rt5514_set_bias_level,
+ .controls = rt5514_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5514_snd_controls),
+ .dapm_widgets = rt5514_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets),
+ .dapm_routes = rt5514_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5514_i2c_regmap = {
+ .name = "i2c",
+ .reg_bits = 32,
+ .val_bits = 32,
+
+ .readable_reg = rt5514_i2c_readable_register,
+
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config rt5514_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+
+ .max_register = RT5514_VENDOR_ID2,
+ .volatile_reg = rt5514_volatile_register,
+ .readable_reg = rt5514_readable_register,
+ .reg_read = rt5514_i2c_read,
+ .reg_write = rt5514_i2c_write,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5514_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5514_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt5514_i2c_id[] = {
+ { "rt5514" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5514_acpi_match[] = {
+ { "10EC5514" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match);
+#endif
+
+static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,dmic-init-delay-ms",
+ &rt5514->pdata.dmic_init_delay);
+ device_property_read_string(dev, "realtek,dsp-calib-clk-name",
+ &rt5514->pdata.dsp_calib_clk_name);
+ device_property_read_u32(dev, "realtek,dsp-calib-clk-rate",
+ &rt5514->pdata.dsp_calib_clk_rate);
+
+ return 0;
+}
+
+static int rt5514_i2c_resume(struct device *dev)
+{
+ struct rt5514_priv *rt5514 = dev_get_drvdata(dev);
+ unsigned int val;
+
+ /*
+ * Add a bogus read to avoid rt5514's confusion after s2r in case it
+ * saw glitches on the i2c lines and thought the other side sent a
+ * start bit.
+ */
+ regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+
+ return 0;
+}
+
+static int rt5514_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5514_priv *rt5514;
+ int ret;
+ unsigned int val = ~0;
+
+ rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
+ GFP_KERNEL);
+ if (rt5514 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5514);
+
+ if (pdata)
+ rt5514->pdata = *pdata;
+ else
+ rt5514_parse_dp(rt5514, &i2c->dev);
+
+ rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap);
+ if (IS_ERR(rt5514->i2c_regmap)) {
+ ret = PTR_ERR(rt5514->i2c_regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap);
+ if (IS_ERR(rt5514->regmap)) {
+ ret = PTR_ERR(rt5514->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ /*
+ * The rt5514 can get confused if the i2c lines glitch together, as
+ * can happen at bootup as regulators are turned off and on. If it's
+ * in this glitched state the first i2c read will fail, so we'll give
+ * it one change to retry.
+ */
+ ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (ret || val != RT5514_DEVICE_ID)
+ ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (ret || val != RT5514_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5514\n", val);
+ return -ENODEV;
+ }
+
+ ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch,
+ ARRAY_SIZE(rt5514_i2c_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
+ ret);
+
+ ret = regmap_register_patch(rt5514->regmap, rt5514_patch,
+ ARRAY_SIZE(rt5514_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5514,
+ rt5514_dai, ARRAY_SIZE(rt5514_dai));
+}
+
+static const struct dev_pm_ops rt5514_i2_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
+};
+
+static struct i2c_driver rt5514_i2c_driver = {
+ .driver = {
+ .name = "rt5514",
+ .acpi_match_table = ACPI_PTR(rt5514_acpi_match),
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ .pm = pm_ptr(&rt5514_i2_pm_ops),
+ },
+ .probe = rt5514_i2c_probe,
+ .id_table = rt5514_i2c_id,
+};
+module_i2c_driver(rt5514_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5514 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
new file mode 100644
index 000000000000..75755599f940
--- /dev/null
+++ b/sound/soc/codecs/rt5514.h
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5514.h -- RT5514 ALSA SoC audio driver
+ *
+ * Copyright 2015 Realtek Microelectronics
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#ifndef __RT5514_H__
+#define __RT5514_H__
+
+#include <linux/clk.h>
+#include <sound/rt5514.h>
+
+#define RT5514_DEVICE_ID 0x10ec5514
+
+#define RT5514_RESET 0x2000
+#define RT5514_PWR_ANA1 0x2004
+#define RT5514_PWR_ANA2 0x2008
+#define RT5514_I2S_CTRL1 0x2010
+#define RT5514_I2S_CTRL2 0x2014
+#define RT5514_VAD_CTRL6 0x2030
+#define RT5514_EXT_VAD_CTRL 0x206c
+#define RT5514_DIG_IO_CTRL 0x2070
+#define RT5514_PAD_CTRL1 0x2080
+#define RT5514_DMIC_DATA_CTRL 0x20a0
+#define RT5514_DIG_SOURCE_CTRL 0x20a4
+#define RT5514_SRC_CTRL 0x20ac
+#define RT5514_DOWNFILTER2_CTRL1 0x20d0
+#define RT5514_PLL_SOURCE_CTRL 0x2100
+#define RT5514_CLK_CTRL1 0x2104
+#define RT5514_CLK_CTRL2 0x2108
+#define RT5514_PLL3_CALIB_CTRL1 0x2110
+#define RT5514_PLL3_CALIB_CTRL4 0x2120
+#define RT5514_PLL3_CALIB_CTRL5 0x2124
+#define RT5514_PLL3_CALIB_CTRL6 0x2128
+#define RT5514_DELAY_BUF_CTRL1 0x2140
+#define RT5514_DELAY_BUF_CTRL3 0x2148
+#define RT5514_ASRC_IN_CTRL1 0x2180
+#define RT5514_DOWNFILTER0_CTRL1 0x2190
+#define RT5514_DOWNFILTER0_CTRL2 0x2194
+#define RT5514_DOWNFILTER0_CTRL3 0x2198
+#define RT5514_DOWNFILTER1_CTRL1 0x21a0
+#define RT5514_DOWNFILTER1_CTRL2 0x21a4
+#define RT5514_DOWNFILTER1_CTRL3 0x21a8
+#define RT5514_ANA_CTRL_LDO10 0x2200
+#define RT5514_ANA_CTRL_LDO18_16 0x2204
+#define RT5514_ANA_CTRL_ADC12 0x2210
+#define RT5514_ANA_CTRL_ADC21 0x2214
+#define RT5514_ANA_CTRL_ADC22 0x2218
+#define RT5514_ANA_CTRL_ADC23 0x221c
+#define RT5514_ANA_CTRL_MICBST 0x2220
+#define RT5514_ANA_CTRL_ADCFED 0x2224
+#define RT5514_ANA_CTRL_INBUF 0x2228
+#define RT5514_ANA_CTRL_VREF 0x222c
+#define RT5514_ANA_CTRL_PLL3 0x2240
+#define RT5514_ANA_CTRL_PLL1_1 0x2260
+#define RT5514_ANA_CTRL_PLL1_2 0x2264
+#define RT5514_DMIC_LP_CTRL 0x2e00
+#define RT5514_MISC_CTRL_DSP 0x2e04
+#define RT5514_DSP_CTRL1 0x2f00
+#define RT5514_DSP_CTRL3 0x2f08
+#define RT5514_DSP_CTRL4 0x2f10
+#define RT5514_VENDOR_ID1 0x2ff0
+#define RT5514_VENDOR_ID2 0x2ff4
+
+#define RT5514_DSP_MAPPING 0x18000000
+
+/* RT5514_PWR_ANA1 (0x2004) */
+#define RT5514_POW_LDO18_IN (0x1 << 5)
+#define RT5514_POW_LDO18_IN_BIT 5
+#define RT5514_POW_LDO18_ADC (0x1 << 4)
+#define RT5514_POW_LDO18_ADC_BIT 4
+#define RT5514_POW_LDO21 (0x1 << 3)
+#define RT5514_POW_LDO21_BIT 3
+#define RT5514_POW_BG_LDO18_IN (0x1 << 2)
+#define RT5514_POW_BG_LDO18_IN_BIT 2
+#define RT5514_POW_BG_LDO21 (0x1 << 1)
+#define RT5514_POW_BG_LDO21_BIT 1
+
+/* RT5514_PWR_ANA2 (0x2008) */
+#define RT5514_POW_PLL1 (0x1 << 18)
+#define RT5514_POW_PLL1_BIT 18
+#define RT5514_POW_PLL1_LDO (0x1 << 16)
+#define RT5514_POW_PLL1_LDO_BIT 16
+#define RT5514_POW_BG_MBIAS (0x1 << 15)
+#define RT5514_POW_BG_MBIAS_BIT 15
+#define RT5514_POW_MBIAS (0x1 << 14)
+#define RT5514_POW_MBIAS_BIT 14
+#define RT5514_POW_VREF2 (0x1 << 13)
+#define RT5514_POW_VREF2_BIT 13
+#define RT5514_POW_VREF1 (0x1 << 12)
+#define RT5514_POW_VREF1_BIT 12
+#define RT5514_POWR_LDO16 (0x1 << 11)
+#define RT5514_POWR_LDO16_BIT 11
+#define RT5514_POWL_LDO16 (0x1 << 10)
+#define RT5514_POWL_LDO16_BIT 10
+#define RT5514_POW_ADC2 (0x1 << 9)
+#define RT5514_POW_ADC2_BIT 9
+#define RT5514_POW_INPUT_BUF (0x1 << 8)
+#define RT5514_POW_INPUT_BUF_BIT 8
+#define RT5514_POW_ADC1_R (0x1 << 7)
+#define RT5514_POW_ADC1_R_BIT 7
+#define RT5514_POW_ADC1_L (0x1 << 6)
+#define RT5514_POW_ADC1_L_BIT 6
+#define RT5514_POW2_BSTR (0x1 << 5)
+#define RT5514_POW2_BSTR_BIT 5
+#define RT5514_POW2_BSTL (0x1 << 4)
+#define RT5514_POW2_BSTL_BIT 4
+#define RT5514_POW_BSTR (0x1 << 3)
+#define RT5514_POW_BSTR_BIT 3
+#define RT5514_POW_BSTL (0x1 << 2)
+#define RT5514_POW_BSTL_BIT 2
+#define RT5514_POW_ADCFEDR (0x1 << 1)
+#define RT5514_POW_ADCFEDR_BIT 1
+#define RT5514_POW_ADCFEDL (0x1 << 0)
+#define RT5514_POW_ADCFEDL_BIT 0
+
+/* RT5514_I2S_CTRL1 (0x2010) */
+#define RT5514_TDM_MODE2 (0x1 << 30)
+#define RT5514_TDM_MODE2_SFT 30
+#define RT5514_TDM_MODE (0x1 << 28)
+#define RT5514_TDM_MODE_SFT 28
+#define RT5514_I2S_LR_MASK (0x1 << 26)
+#define RT5514_I2S_LR_SFT 26
+#define RT5514_I2S_LR_NOR (0x0 << 26)
+#define RT5514_I2S_LR_INV (0x1 << 26)
+#define RT5514_I2S_BP_MASK (0x1 << 25)
+#define RT5514_I2S_BP_SFT 25
+#define RT5514_I2S_BP_NOR (0x0 << 25)
+#define RT5514_I2S_BP_INV (0x1 << 25)
+#define RT5514_I2S_DF_MASK (0x7 << 16)
+#define RT5514_I2S_DF_SFT 16
+#define RT5514_I2S_DF_I2S (0x0 << 16)
+#define RT5514_I2S_DF_LEFT (0x1 << 16)
+#define RT5514_I2S_DF_PCM_A (0x2 << 16)
+#define RT5514_I2S_DF_PCM_B (0x3 << 16)
+#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10)
+#define RT5514_TDMSLOT_SEL_RX_SFT 10
+#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10)
+#define RT5514_TDMSLOT_SEL_RX_6CH (0x2 << 10)
+#define RT5514_TDMSLOT_SEL_RX_8CH (0x3 << 10)
+#define RT5514_CH_LEN_RX_MASK (0x3 << 8)
+#define RT5514_CH_LEN_RX_SFT 8
+#define RT5514_CH_LEN_RX_16 (0x0 << 8)
+#define RT5514_CH_LEN_RX_20 (0x1 << 8)
+#define RT5514_CH_LEN_RX_24 (0x2 << 8)
+#define RT5514_CH_LEN_RX_32 (0x3 << 8)
+#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6)
+#define RT5514_TDMSLOT_SEL_TX_SFT 6
+#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6)
+#define RT5514_TDMSLOT_SEL_TX_6CH (0x2 << 6)
+#define RT5514_TDMSLOT_SEL_TX_8CH (0x3 << 6)
+#define RT5514_CH_LEN_TX_MASK (0x3 << 4)
+#define RT5514_CH_LEN_TX_SFT 4
+#define RT5514_CH_LEN_TX_16 (0x0 << 4)
+#define RT5514_CH_LEN_TX_20 (0x1 << 4)
+#define RT5514_CH_LEN_TX_24 (0x2 << 4)
+#define RT5514_CH_LEN_TX_32 (0x3 << 4)
+#define RT5514_I2S_DL_MASK (0x3 << 0)
+#define RT5514_I2S_DL_SFT 0
+#define RT5514_I2S_DL_16 (0x0 << 0)
+#define RT5514_I2S_DL_20 (0x1 << 0)
+#define RT5514_I2S_DL_24 (0x2 << 0)
+#define RT5514_I2S_DL_8 (0x3 << 0)
+
+/* RT5514_I2S_CTRL2 (0x2014) */
+#define RT5514_TDM_DOCKING_MODE (0x1 << 31)
+#define RT5514_TDM_DOCKING_MODE_SFT 31
+#define RT5514_TDM_DOCKING_VALID_CH_MASK (0x1 << 29)
+#define RT5514_TDM_DOCKING_VALID_CH_SFT 29
+#define RT5514_TDM_DOCKING_VALID_CH2 (0x0 << 29)
+#define RT5514_TDM_DOCKING_VALID_CH4 (0x1 << 29)
+#define RT5514_TDM_DOCKING_START_MASK (0x1 << 28)
+#define RT5514_TDM_DOCKING_START_SFT 28
+#define RT5514_TDM_DOCKING_START_SLOT0 (0x0 << 28)
+#define RT5514_TDM_DOCKING_START_SLOT4 (0x1 << 28)
+
+/* RT5514_DIG_SOURCE_CTRL (0x20a4) */
+#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1)
+#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1
+#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0)
+#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0
+
+/* RT5514_PLL_SOURCE_CTRL (0x2100) */
+#define RT5514_PLL_1_SEL_MASK (0x7 << 12)
+#define RT5514_PLL_1_SEL_SFT 12
+#define RT5514_PLL_1_SEL_SCLK (0x3 << 12)
+#define RT5514_PLL_1_SEL_MCLK (0x4 << 12)
+
+/* RT5514_CLK_CTRL1 (0x2104) */
+#define RT5514_CLK_AD_ANA1_EN (0x1 << 31)
+#define RT5514_CLK_AD_ANA1_EN_BIT 31
+#define RT5514_CLK_AD1_EN (0x1 << 24)
+#define RT5514_CLK_AD1_EN_BIT 24
+#define RT5514_CLK_AD0_EN (0x1 << 23)
+#define RT5514_CLK_AD0_EN_BIT 23
+#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8)
+#define RT5514_CLK_DMIC_OUT_SEL_SFT 8
+#define RT5514_CLK_AD_ANA1_SEL_MASK (0xf << 0)
+#define RT5514_CLK_AD_ANA1_SEL_SFT 0
+
+/* RT5514_CLK_CTRL2 (0x2108) */
+#define RT5514_CLK_AD1_ASRC_EN (0x1 << 17)
+#define RT5514_CLK_AD1_ASRC_EN_BIT 17
+#define RT5514_CLK_AD0_ASRC_EN (0x1 << 16)
+#define RT5514_CLK_AD0_ASRC_EN_BIT 16
+#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8)
+#define RT5514_CLK_SYS_DIV_OUT_SFT 8
+#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4)
+#define RT5514_SEL_ADC_OSR_SFT 4
+#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_SFT 0
+#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0)
+
+/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */
+#define RT5514_AD_DMIC_MIX (0x1 << 11)
+#define RT5514_AD_DMIC_MIX_BIT 11
+#define RT5514_AD_AD_MIX (0x1 << 10)
+#define RT5514_AD_AD_MIX_BIT 10
+#define RT5514_AD_AD_MUTE (0x1 << 7)
+#define RT5514_AD_AD_MUTE_BIT 7
+#define RT5514_AD_GAIN_MASK (0x3f << 1)
+#define RT5514_AD_GAIN_SFT 1
+
+/* RT5514_ANA_CTRL_MICBST (0x2220) */
+#define RT5514_SEL_BSTL_MASK (0xf << 4)
+#define RT5514_SEL_BSTL_SFT 4
+#define RT5514_SEL_BSTR_MASK (0xf << 0)
+#define RT5514_SEL_BSTR_SFT 0
+
+/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */
+#define RT5514_PLL_K_MAX 0x1f
+#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16)
+#define RT5514_PLL_K_SFT 16
+#define RT5514_PLL_N_MAX 0x1ff
+#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7)
+#define RT5514_PLL_N_SFT 4
+#define RT5514_PLL_M_MAX 0xf
+#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0)
+#define RT5514_PLL_M_SFT 0
+
+/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */
+#define RT5514_PLL_M_BP (0x1 << 2)
+#define RT5514_PLL_M_BP_SFT 2
+#define RT5514_PLL_K_BP (0x1 << 1)
+#define RT5514_PLL_K_BP_SFT 1
+#define RT5514_EN_LDO_PLL1 (0x1 << 0)
+#define RT5514_EN_LDO_PLL1_BIT 0
+
+#define RT5514_PLL_INP_MAX 40000000
+#define RT5514_PLL_INP_MIN 256000
+
+#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin"
+#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin"
+
+/* System Clock Source */
+enum {
+ RT5514_SCLK_S_MCLK,
+ RT5514_SCLK_S_PLL1,
+};
+
+/* PLL1 Source */
+enum {
+ RT5514_PLL1_S_MCLK,
+ RT5514_PLL1_S_BCLK,
+};
+
+struct rt5514_priv {
+ struct rt5514_platform_data pdata;
+ struct snd_soc_component *component;
+ struct regmap *i2c_regmap, *regmap;
+ struct clk *mclk, *dsp_calib_clk;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ int dsp_enabled;
+ unsigned int pll3_cal_value;
+};
+
+#endif /* __RT5514_H__ */
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 1c10d8ed39d2..fb9cf127e3ff 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5616.c -- RT5616 ALSA SoC audio codec driver
*
* Copyright 2015 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can 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/i2c.h>
@@ -53,6 +51,7 @@ static const struct reg_sequence init_list[] = {
{RT5616_PR_BASE + 0x21, 0x4040},
{RT5616_PR_BASE + 0x23, 0x0004},
};
+
#define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5616_reg[] = {
@@ -96,7 +95,7 @@ static const struct reg_default rt5616_reg[] = {
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0000 },
- { 0x91, 0x0000 },
+ { 0x91, 0x0c00 },
{ 0x92, 0x0000 },
{ 0x93, 0x2000 },
{ 0x94, 0x0200 },
@@ -140,9 +139,10 @@ static const struct reg_default rt5616_reg[] = {
};
struct rt5616_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct delayed_work patch_work;
struct regmap *regmap;
+ struct clk *mclk;
int sysclk;
int sysclk_src;
@@ -162,9 +162,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg)
for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
if (reg >= rt5616_ranges[i].range_min &&
- reg <= rt5616_ranges[i].range_max) {
+ reg <= rt5616_ranges[i].range_max)
return true;
- }
}
switch (reg) {
@@ -190,9 +189,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg)
for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
if (reg >= rt5616_ranges[i].range_min &&
- reg <= rt5616_ranges[i].range_max) {
+ reg <= rt5616_ranges[i].range_max)
return true;
- }
}
switch (reg) {
@@ -293,8 +291,7 @@ 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),
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
@@ -302,54 +299,56 @@ static unsigned int bst_tlv[] = {
6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
-};
+);
static const struct snd_kcontrol_new rt5616_snd_controls[] = {
/* Headphone Output Volume */
SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL,
- RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL,
+ RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
/* OUTPUT Control */
SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1,
- RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1,
- RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
+ RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
- 175, 0, dac_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2,
- RT5616_BST_SFT1, 8, 0, bst_tlv),
+ RT5616_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2,
- RT5616_BST_SFT2, 8, 0, bst_tlv),
+ RT5616_BST_SFT2, 8, 0, bst_tlv),
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL,
- RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
- 31, 1, in_vol_tlv),
+ RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
/* ADC Digital Volume Control */
SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL,
- RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
- 127, 0, adc_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
/* ADC Boost Volume Control */
SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL,
- RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
- 3, 0, adc_bst_tlv),
+ RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
};
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
+ struct snd_soc_dapm_widget *sink)
{
unsigned int val;
- val = snd_soc_read(snd_soc_dapm_to_codec(source->dapm), RT5616_GLB_CLK);
+ val = snd_soc_component_read(snd_soc_dapm_to_component(source->dapm), RT5616_GLB_CLK);
val &= RT5616_SCLK_SRC_MASK;
if (val == RT5616_SCLK_SRC_PLL1)
return 1;
@@ -462,20 +461,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = {
};
static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE,
- RT5616_L_MUTE | RT5616_R_MUTE);
+ snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
break;
default:
@@ -486,62 +485,63 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* depop parameters */
- snd_soc_update_bits(codec, RT5616_DEPOP_M2,
- RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
- RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
- RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
- snd_soc_write(codec, RT5616_PR_BASE +
- RT5616_HP_DCC_INT1, 0x9f00);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M2,
+ RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+ RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
+ RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
+ snd_soc_component_write(component, RT5616_PR_BASE +
+ RT5616_HP_DCC_INT1, 0x9f00);
/* headphone amp power on */
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5616_PWR_VOL,
- RT5616_PWR_HV_L | RT5616_PWR_HV_R,
- RT5616_PWR_HV_L | RT5616_PWR_HV_R);
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_HP_L | RT5616_PWR_HP_R |
- RT5616_PWR_HA, RT5616_PWR_HP_L |
- RT5616_PWR_HP_R | RT5616_PWR_HA);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
+ snd_soc_component_update_bits(component, RT5616_PWR_VOL,
+ RT5616_PWR_HV_L | RT5616_PWR_HV_R,
+ RT5616_PWR_HV_L | RT5616_PWR_HV_R);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+ RT5616_PWR_HA, RT5616_PWR_HP_L |
+ RT5616_PWR_HP_R | RT5616_PWR_HA);
msleep(50);
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2,
- RT5616_PWR_FV1 | RT5616_PWR_FV2);
-
- snd_soc_update_bits(codec, RT5616_CHARGE_PUMP,
- RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
- snd_soc_update_bits(codec, RT5616_PR_BASE +
- RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
- RT5616_HP_CO_EN | RT5616_HP_SG_EN);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
+
+ snd_soc_component_update_bits(component, RT5616_CHARGE_PUMP,
+ RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
+ snd_soc_component_update_bits(component, RT5616_PR_BASE +
+ RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
+ RT5616_HP_CO_EN | RT5616_HP_SG_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5616_PR_BASE +
- RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
- RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+ snd_soc_component_update_bits(component, RT5616_PR_BASE +
+ RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+ RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
/* headphone amp power down */
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK |
- RT5616_HP_CO_MASK | RT5616_HP_CP_MASK |
- RT5616_HP_SG_MASK | RT5616_HP_CB_MASK,
- RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
- RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
- RT5616_HP_SG_EN | RT5616_HP_CB_PD);
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_HP_L | RT5616_PWR_HP_R |
- RT5616_PWR_HA, 0);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_SMT_TRIG_MASK |
+ RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK |
+ RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+ RT5616_HP_CB_MASK,
+ RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
+ RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
+ RT5616_HP_SG_EN | RT5616_HP_CB_PD);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+ RT5616_PWR_HA, 0);
break;
default:
return 0;
@@ -551,65 +551,65 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* headphone unmute sequence */
- snd_soc_update_bits(codec, RT5616_DEPOP_M3,
- RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
- RT5616_CP_FQ3_MASK,
- (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) |
- (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
- (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT));
- snd_soc_write(codec, RT5616_PR_BASE +
- RT5616_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTN_MASK, RT5616_RSTN_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
- RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
- snd_soc_update_bits(codec, RT5616_HP_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M3,
+ RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+ RT5616_CP_FQ3_MASK,
+ RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT |
+ RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+ RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT);
+ snd_soc_component_write(component, RT5616_PR_BASE +
+ RT5616_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTN_MASK, RT5616_RSTN_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
+ RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+ snd_soc_component_update_bits(component, RT5616_HP_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
msleep(100);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
- RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+ RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
msleep(20);
- snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
- RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
+ snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET,
+ RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
/* headphone mute sequence */
- snd_soc_update_bits(codec, RT5616_DEPOP_M3,
- RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
- RT5616_CP_FQ3_MASK,
- (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) |
- (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
- (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT));
- snd_soc_write(codec, RT5616_PR_BASE +
- RT5616_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTP_MASK, RT5616_RSTP_EN);
- snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
- RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
- snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
- RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M3,
+ RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+ RT5616_CP_FQ3_MASK,
+ RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT |
+ RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+ RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT);
+ snd_soc_component_write(component, RT5616_PR_BASE +
+ RT5616_MAMP_INT_REG2, 0xfc00);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTP_MASK, RT5616_RSTP_EN);
+ snd_soc_component_update_bits(component, RT5616_DEPOP_M1,
+ RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
+ RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+ snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET,
+ RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
msleep(90);
- snd_soc_update_bits(codec, RT5616_HP_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE,
- RT5616_L_MUTE | RT5616_R_MUTE);
+ snd_soc_component_update_bits(component, RT5616_HP_VOL,
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
msleep(30);
break;
@@ -621,24 +621,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_LM, RT5616_PWR_LM);
- snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
- RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_LM, RT5616_PWR_LM);
+ snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1,
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
- RT5616_L_MUTE | RT5616_R_MUTE,
- RT5616_L_MUTE | RT5616_R_MUTE);
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_LM, 0);
+ snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1,
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_LM, 0);
break;
default:
@@ -649,19 +649,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST1_OP2, 0);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST1_OP2, 0);
break;
default:
@@ -672,19 +672,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST2_OP2, 0);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG2,
+ RT5616_PWR_BST2_OP2, 0);
break;
default:
@@ -696,13 +696,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2,
- RT5616_PWR_PLL_BIT, 0, NULL, 0),
+ RT5616_PWR_PLL_BIT, 0, NULL, 0),
/* Input Side */
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1,
- RT5616_PWR_LDO_BIT, 0, NULL, 0),
+ RT5616_PWR_LDO_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2,
- RT5616_PWR_MB1_BIT, 0, NULL, 0),
+ RT5616_PWR_MB1_BIT, 0, NULL, 0),
/* Input Lines */
SND_SOC_DAPM_INPUT("MIC1"),
@@ -714,45 +714,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
/* Boost */
SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2,
- RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2,
- RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
/* Input Volume */
SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0,
- rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
+ rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0,
- rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
+ rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
/* ADCs */
SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
- SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
- SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
/* ADC Mixer */
SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
- RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+ RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
- rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
+ rt5616_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
- rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
+ rt5616_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
/* Digital Interface */
SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1,
- RT5616_PWR_I2S1_BIT, 0, NULL, 0),
+ RT5616_PWR_I2S1_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -770,68 +772,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
/* Output Side */
/* DAC mixer before sound effect */
SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
- rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
+ rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
- rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
+ rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2,
- RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+ RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
/* DAC Mixer */
SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
- rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)),
+ rt5616_sto_dac_l_mix,
+ ARRAY_SIZE(rt5616_sto_dac_l_mix)),
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
- rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)),
+ rt5616_sto_dac_r_mix,
+ ARRAY_SIZE(rt5616_sto_dac_r_mix)),
/* DACs */
SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_DAC_L1_BIT, 0),
+ RT5616_PWR_DAC_L1_BIT, 0),
SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_DAC_R1_BIT, 0),
+ RT5616_PWR_DAC_R1_BIT, 0),
/* OUT Mixer */
SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT,
- 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
+ 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT,
- 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
+ 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
/* Output Volume */
SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL,
- RT5616_PWR_OV_L_BIT, 0, NULL, 0),
+ RT5616_PWR_OV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL,
- RT5616_PWR_OV_R_BIT, 0, NULL, 0),
+ RT5616_PWR_OV_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL,
- RT5616_PWR_HV_L_BIT, 0, NULL, 0),
+ RT5616_PWR_HV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL,
- RT5616_PWR_HV_R_BIT, 0, NULL, 0),
+ RT5616_PWR_HV_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
- 0, 0, NULL, 0),
+ 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM,
- 0, 0, NULL, 0),
+ 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM,
- 0, 0, NULL, 0),
+ 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL,
- RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL,
- RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL,
- RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL,
- RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
/* HPO/LOUT/Mono Mixer */
SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
- rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
+ rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
- rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
+ rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0,
- rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
- SND_SOC_DAPM_POST_PMU),
+ rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
- rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
- SND_SOC_DAPM_POST_PMU),
+ rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0,
- rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_PRE_PMD),
+ rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("HPOL"),
@@ -950,11 +954,11 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = {
};
static int rt5616_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
@@ -963,12 +967,12 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
pre_div = rl6231_get_clk_info(rt5616->sysclk, rt5616->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ dev_err(component->dev, "Unsupported clock setting\n");
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32 ? 1 : 0;
@@ -977,7 +981,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
rt5616->bclk[dai->id], rt5616->lrck[dai->id]);
dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
- bclk_ms, pre_div, dai->id);
+ bclk_ms, pre_div, dai->id);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -997,25 +1001,24 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5616_I2S_PD1_MASK;
val_clk = pre_div << RT5616_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5616_I2S1_SDP,
- RT5616_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
-
+ snd_soc_component_update_bits(component, RT5616_I2S1_SDP,
+ RT5616_I2S_DL_MASK, val_len);
+ snd_soc_component_update_bits(component, RT5616_ADDA_CLK1, mask_clk, val_clk);
return 0;
}
static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5616->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5616_I2S_MS_S;
rt5616->master[dai->id] = 0;
break;
@@ -1049,19 +1052,18 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5616_I2S1_SDP,
- RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
- RT5616_I2S_DF_MASK, reg_val);
-
+ snd_soc_component_update_bits(component, RT5616_I2S1_SDP,
+ RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
+ RT5616_I2S_DF_MASK, reg_val);
return 0;
}
static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+ int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5616->sysclk && clk_id == rt5616->sysclk_src)
@@ -1075,11 +1077,12 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5616_SCLK_SRC_PLL1;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_SCLK_SRC_MASK, reg_val);
+
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_SCLK_SRC_MASK, reg_val);
rt5616->sysclk = freq;
rt5616->sysclk_src = clk_id;
@@ -1089,10 +1092,10 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
}
static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
- unsigned int freq_in, unsigned int freq_out)
+ unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -1101,45 +1104,49 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5616->pll_in = 0;
rt5616->pll_out = 0;
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK);
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_SCLK_SRC_MASK,
+ RT5616_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5616_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK);
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_PLL1_SRC_MASK,
+ RT5616_PLL1_SRC_MCLK);
break;
case RT5616_PLL1_S_BCLK1:
case RT5616_PLL1_S_BCLK2:
- snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1);
+ snd_soc_component_update_bits(component, RT5616_GLB_CLK,
+ RT5616_PLL1_SRC_MASK,
+ RT5616_PLL1_SRC_BCLK1);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
- snd_soc_write(codec, RT5616_PLL_CTRL1,
- pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5616_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT |
- pll_code.m_bp << RT5616_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5616_PLL_CTRL1,
+ pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5616_PLL_CTRL2,
+ (pll_code.m_bp ? 0 : pll_code.m_code) <<
+ RT5616_PLL_M_SFT |
+ pll_code.m_bp << RT5616_PLL_M_BP_SFT);
rt5616->pll_in = freq_in;
rt5616->pll_out = freq_out;
@@ -1148,34 +1155,60 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5616_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
+static int rt5616_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
{
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
switch (level) {
+
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5616->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5616->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2);
mdelay(10);
- snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2,
- RT5616_PWR_FV1 | RT5616_PWR_FV2);
- snd_soc_update_bits(codec, RT5616_D_MISC,
- RT5616_D_GATE_EN, RT5616_D_GATE_EN);
+ snd_soc_component_update_bits(component, RT5616_PWR_ANLG1,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5616_D_MISC,
+ RT5616_D_GATE_EN,
+ RT5616_D_GATE_EN);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, RT5616_D_MISC, RT5616_D_GATE_EN, 0);
- snd_soc_write(codec, RT5616_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5616_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5616_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5616_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5616_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5616_PWR_ANLG2, 0x0000);
+ snd_soc_component_update_bits(component, RT5616_D_MISC, RT5616_D_GATE_EN, 0);
+ snd_soc_component_write(component, RT5616_PWR_DIG1, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_DIG2, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_VOL, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_MIXER, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_ANLG1, 0x0000);
+ snd_soc_component_write(component, RT5616_PWR_ANLG2, 0x0000);
break;
default:
@@ -1185,19 +1218,24 @@ static int rt5616_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5616_probe(struct snd_soc_codec *codec)
+static int rt5616_probe(struct snd_soc_component *component)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
+
+ /* Check if MCLK provided */
+ rt5616->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5616->mclk))
+ return PTR_ERR(rt5616->mclk);
- rt5616->codec = codec;
+ rt5616->component = component;
return 0;
}
#ifdef CONFIG_PM
-static int rt5616_suspend(struct snd_soc_codec *codec)
+static int rt5616_suspend(struct snd_soc_component *component)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5616->regmap, true);
regcache_mark_dirty(rt5616->regmap);
@@ -1205,9 +1243,9 @@ static int rt5616_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5616_resume(struct snd_soc_codec *codec)
+static int rt5616_resume(struct snd_soc_component *component)
{
- struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5616->regmap, false);
regcache_sync(rt5616->regmap);
@@ -1218,19 +1256,18 @@ static int rt5616_resume(struct snd_soc_codec *codec)
#define rt5616_resume NULL
#endif
-#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000
#define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-
-struct snd_soc_dai_ops rt5616_aif_dai_ops = {
+static const struct snd_soc_dai_ops rt5616_aif_dai_ops = {
.hw_params = rt5616_hw_params,
.set_fmt = rt5616_set_dai_fmt,
.set_sysclk = rt5616_set_dai_sysclk,
.set_pll = rt5616_set_dai_pll,
};
-struct snd_soc_dai_driver rt5616_dai[] = {
+static struct snd_soc_dai_driver rt5616_dai[] = {
{
.name = "rt5616-aif1",
.id = RT5616_AIF1,
@@ -1252,29 +1289,31 @@ struct snd_soc_dai_driver rt5616_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5616 = {
- .probe = rt5616_probe,
- .suspend = rt5616_suspend,
- .resume = rt5616_resume,
- .set_bias_level = rt5616_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5616_snd_controls,
- .num_controls = ARRAY_SIZE(rt5616_snd_controls),
- .dapm_widgets = rt5616_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5616_dapm_widgets),
- .dapm_routes = rt5616_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5616_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5616 = {
+ .probe = rt5616_probe,
+ .suspend = rt5616_suspend,
+ .resume = rt5616_resume,
+ .set_bias_level = rt5616_set_bias_level,
+ .controls = rt5616_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5616_snd_controls),
+ .dapm_widgets = rt5616_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5616_dapm_widgets),
+ .dapm_routes = rt5616_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5616_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5616_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5616_DEVICE_ID + 1 + (ARRAY_SIZE(rt5616_ranges) *
RT5616_PR_SPACING),
.volatile_reg = rt5616_volatile_register,
.readable_reg = rt5616_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5616_reg,
.num_reg_defaults = ARRAY_SIZE(rt5616_reg),
.ranges = rt5616_ranges,
@@ -1282,7 +1321,7 @@ static const struct regmap_config rt5616_regmap = {
};
static const struct i2c_device_id rt5616_i2c_id[] = {
- { "rt5616", 0 },
+ { "rt5616" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5616_i2c_id);
@@ -1295,16 +1334,15 @@ static const struct of_device_id rt5616_of_match[] = {
MODULE_DEVICE_TABLE(of, rt5616_of_match);
#endif
-static int rt5616_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5616_i2c_probe(struct i2c_client *i2c)
{
struct rt5616_priv *rt5616;
unsigned int val;
int ret;
rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
- GFP_KERNEL);
- if (rt5616 == NULL)
+ GFP_KERNEL);
+ if (!rt5616)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5616);
@@ -1326,14 +1364,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
}
regmap_write(rt5616->regmap, RT5616_RESET, 0);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2);
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2);
mdelay(10);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2,
- RT5616_PWR_FV1 | RT5616_PWR_FV2);
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
ret = regmap_register_patch(rt5616->regmap, init_list,
ARRAY_SIZE(init_list));
@@ -1341,19 +1379,15 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
- RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
-
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
- rt5616_dai, ARRAY_SIZE(rt5616_dai));
+ RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5616,
+ rt5616_dai, ARRAY_SIZE(rt5616_dai));
}
-static int rt5616_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
+static void rt5616_i2c_remove(struct i2c_client *i2c)
+{}
static void rt5616_i2c_shutdown(struct i2c_client *client)
{
@@ -1361,7 +1395,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client)
regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8);
regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8);
-
}
static struct i2c_driver rt5616_i2c_driver = {
diff --git a/sound/soc/codecs/rt5616.h b/sound/soc/codecs/rt5616.h
index f88cdddbc34a..ad9c5de9052d 100644
--- a/sound/soc/codecs/rt5616.h
+++ b/sound/soc/codecs/rt5616.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5616.h -- RT5616 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 __RT5616_H__
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 1be2bab7dee3..19c6d8f760d9 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5631.c -- RT5631 ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
* Author: flove <flove@realtek.com>
*
* Based on WM8753.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -68,33 +64,33 @@ static const struct reg_default rt5631_reg[] = {
{ RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
};
-/**
+/*
* rt5631_write_index - write index register of 2nd layer
*/
-static void rt5631_write_index(struct snd_soc_codec *codec,
+static void rt5631_write_index(struct snd_soc_component *component,
unsigned int reg, unsigned int value)
{
- snd_soc_write(codec, RT5631_INDEX_ADD, reg);
- snd_soc_write(codec, RT5631_INDEX_DATA, value);
+ snd_soc_component_write(component, RT5631_INDEX_ADD, reg);
+ snd_soc_component_write(component, RT5631_INDEX_DATA, value);
}
-/**
+/*
* rt5631_read_index - read index register of 2nd layer
*/
-static unsigned int rt5631_read_index(struct snd_soc_codec *codec,
+static unsigned int rt5631_read_index(struct snd_soc_component *component,
unsigned int reg)
{
unsigned int value;
- snd_soc_write(codec, RT5631_INDEX_ADD, reg);
- value = snd_soc_read(codec, RT5631_INDEX_DATA);
+ snd_soc_component_write(component, RT5631_INDEX_ADD, reg);
+ value = snd_soc_component_read(component, RT5631_INDEX_DATA);
return value;
}
-static int rt5631_reset(struct snd_soc_codec *codec)
+static int rt5631_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5631_RESET, 0);
+ return snd_soc_component_write(component, RT5631_RESET, 0);
}
static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
@@ -105,9 +101,9 @@ static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
case RT5631_INDEX_ADD:
case RT5631_INDEX_DATA:
case RT5631_EQ_CTRL:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -164,9 +160,9 @@ static bool rt5631_readable_register(struct device *dev, unsigned int reg)
case RT5631_VENDOR_ID:
case RT5631_VENDOR_ID1:
case RT5631_VENDOR_ID2:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -187,8 +183,8 @@ static const DECLARE_TLV_DB_RANGE(mic_bst_tlv,
static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = rt5631->dmic_used_flag;
@@ -198,8 +194,8 @@ static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
rt5631->dmic_used_flag = ucontrol->value.integer.value[0];
return 0;
@@ -229,10 +225,10 @@ static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
static const struct snd_kcontrol_new rt5631_snd_controls[] = {
/* MIC */
SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum),
- SOC_SINGLE_TLV("MIC1 Boost", RT5631_MIC_CTRL_2,
+ SOC_SINGLE_TLV("MIC1 Boost Volume", RT5631_MIC_CTRL_2,
RT5631_MIC1_BOOST_SHIFT, 8, 0, mic_bst_tlv),
SOC_ENUM("MIC2 Mode Control", rt5631_mic2_mode_enum),
- SOC_SINGLE_TLV("MIC2 Boost", RT5631_MIC_CTRL_2,
+ SOC_SINGLE_TLV("MIC2 Boost Volume", RT5631_MIC_CTRL_2,
RT5631_MIC2_BOOST_SHIFT, 8, 0, mic_bst_tlv),
/* MONO IN */
SOC_ENUM("MONOIN Mode Control", rt5631_monoin_mode_enum),
@@ -286,304 +282,308 @@ static const struct snd_kcontrol_new rt5631_snd_controls[] = {
static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_GLOBAL_CLK_CTRL);
+ reg = snd_soc_component_read(component, RT5631_GLOBAL_CLK_CTRL);
return reg & RT5631_SYSCLK_SOUR_SEL_PLL;
}
static int check_dmic_used(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
return rt5631->dmic_used_flag;
}
static int check_dacl_to_outmixl(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_OUTMIXER_L_CTRL);
+ reg = snd_soc_component_read(component, RT5631_OUTMIXER_L_CTRL);
return !(reg & RT5631_M_DAC_L_TO_OUTMIXER_L);
}
static int check_dacr_to_outmixr(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_OUTMIXER_R_CTRL);
+ reg = snd_soc_component_read(component, RT5631_OUTMIXER_R_CTRL);
return !(reg & RT5631_M_DAC_R_TO_OUTMIXER_R);
}
static int check_dacl_to_spkmixl(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_SPK_MIXER_CTRL);
+ reg = snd_soc_component_read(component, RT5631_SPK_MIXER_CTRL);
return !(reg & RT5631_M_DAC_L_TO_SPKMIXER_L);
}
static int check_dacr_to_spkmixr(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_SPK_MIXER_CTRL);
+ reg = snd_soc_component_read(component, RT5631_SPK_MIXER_CTRL);
return !(reg & RT5631_M_DAC_R_TO_SPKMIXER_R);
}
static int check_adcl_select(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ reg = snd_soc_component_read(component, RT5631_ADC_REC_MIXER);
return !(reg & RT5631_M_MIC1_TO_RECMIXER_L);
}
static int check_adcr_select(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ reg = snd_soc_component_read(component, RT5631_ADC_REC_MIXER);
return !(reg & RT5631_M_MIC2_TO_RECMIXER_R);
}
/**
* onebit_depop_power_stage - auto depop in power stage.
+ * @component: ASoC component
* @enable: power on/off
*
* When power on/off headphone, the depop sequence is done by hardware.
*/
-static void onebit_depop_power_stage(struct snd_soc_codec *codec, int enable)
+static void onebit_depop_power_stage(struct snd_soc_component *component, int enable)
{
unsigned int soft_vol, hp_zc;
/* enable one-bit depop function */
- snd_soc_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_ONE_BIT_DEPOP, 0);
/* keep soft volume and zero crossing setting */
- soft_vol = snd_soc_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
if (enable) {
/* config one-bit depop parameter */
- rt5631_write_index(codec, RT5631_TEST_MODE_CTRL, 0x84c0);
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x309f);
- rt5631_write_index(codec, RT5631_CP_INTL_REG2, 0x6530);
+ rt5631_write_index(component, RT5631_TEST_MODE_CTRL, 0x84c0);
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x309f);
+ rt5631_write_index(component, RT5631_CP_INTL_REG2, 0x6530);
/* power on capless block */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_CAP_FREE_DEPOP);
} else {
/* power off capless block */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_2, 0);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2, 0);
msleep(100);
}
/* recover soft volume and zero crossing setting */
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
}
/**
* onebit_depop_mute_stage - auto depop in mute stage.
+ * @component: ASoC component
* @enable: mute/unmute
*
* When mute/unmute headphone, the depop sequence is done by hardware.
*/
-static void onebit_depop_mute_stage(struct snd_soc_codec *codec, int enable)
+static void onebit_depop_mute_stage(struct snd_soc_component *component, int enable)
{
unsigned int soft_vol, hp_zc;
/* enable one-bit depop function */
- snd_soc_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_ONE_BIT_DEPOP, 0);
/* keep soft volume and zero crossing setting */
- soft_vol = snd_soc_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
if (enable) {
schedule_timeout_uninterruptible(msecs_to_jiffies(10));
/* config one-bit depop parameter */
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x307f);
- snd_soc_update_bits(codec, RT5631_HP_OUT_VOL,
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x307f);
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
RT5631_L_MUTE | RT5631_R_MUTE, 0);
msleep(300);
} else {
- snd_soc_update_bits(codec, RT5631_HP_OUT_VOL,
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
RT5631_L_MUTE | RT5631_R_MUTE,
RT5631_L_MUTE | RT5631_R_MUTE);
msleep(100);
}
/* recover soft volume and zero crossing setting */
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
}
/**
- * onebit_depop_power_stage - step by step depop sequence in power stage.
+ * depop_seq_power_stage - step by step depop sequence in power stage.
+ * @component: ASoC component
* @enable: power on/off
*
* When power on/off headphone, the depop sequence is done in step by step.
*/
-static void depop_seq_power_stage(struct snd_soc_codec *codec, int enable)
+static void depop_seq_power_stage(struct snd_soc_component *component, int enable)
{
unsigned int soft_vol, hp_zc;
/* depop control by register */
- snd_soc_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_ONE_BIT_DEPOP, RT5631_EN_ONE_BIT_DEPOP);
/* keep soft volume and zero crossing setting */
- soft_vol = snd_soc_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
if (enable) {
/* config depop sequence parameter */
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x303e);
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303e);
/* power on headphone and charge pump */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP |
RT5631_PWR_HP_R_AMP,
RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP |
RT5631_PWR_HP_R_AMP);
/* power on soft generator and depop mode2 */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP);
msleep(100);
/* stop depop mode */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_HP_DEPOP_DIS, RT5631_PWR_HP_DEPOP_DIS);
} else {
/* config depop sequence parameter */
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x303F);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303F);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP |
RT5631_PD_HPAMP_L_ST_UP | RT5631_PD_HPAMP_R_ST_UP);
msleep(75);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN | RT5631_PD_HPAMP_L_ST_UP |
RT5631_PD_HPAMP_R_ST_UP);
/* start depop mode */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_HP_DEPOP_DIS, 0);
/* config depop sequence parameter */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP |
RT5631_PD_HPAMP_L_ST_UP | RT5631_PD_HPAMP_R_ST_UP);
msleep(80);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN);
/* power down headphone and charge pump */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP |
RT5631_PWR_HP_R_AMP, 0);
}
/* recover soft volume and zero crossing setting */
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
}
/**
* depop_seq_mute_stage - step by step depop sequence in mute stage.
+ * @component: ASoC component
* @enable: mute/unmute
*
* When mute/unmute headphone, the depop sequence is done in step by step.
*/
-static void depop_seq_mute_stage(struct snd_soc_codec *codec, int enable)
+static void depop_seq_mute_stage(struct snd_soc_component *component, int enable)
{
unsigned int soft_vol, hp_zc;
/* depop control by register */
- snd_soc_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_ONE_BIT_DEPOP, RT5631_EN_ONE_BIT_DEPOP);
/* keep soft volume and zero crossing setting */
- soft_vol = snd_soc_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0);
+ hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
if (enable) {
schedule_timeout_uninterruptible(msecs_to_jiffies(10));
/* config depop sequence parameter */
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x302f);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x302f);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP |
RT5631_EN_HP_R_M_UN_MUTE_DEPOP |
RT5631_EN_HP_L_M_UN_MUTE_DEPOP);
- snd_soc_update_bits(codec, RT5631_HP_OUT_VOL,
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
RT5631_L_MUTE | RT5631_R_MUTE, 0);
msleep(160);
} else {
/* config depop sequence parameter */
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x302f);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x302f);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP |
RT5631_EN_HP_R_M_UN_MUTE_DEPOP |
RT5631_EN_HP_L_M_UN_MUTE_DEPOP);
- snd_soc_update_bits(codec, RT5631_HP_OUT_VOL,
+ snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL,
RT5631_L_MUTE | RT5631_R_MUTE,
RT5631_L_MUTE | RT5631_R_MUTE);
msleep(150);
}
/* recover soft volume and zero crossing setting */
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
}
static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
if (rt5631->codec_version) {
- onebit_depop_mute_stage(codec, 0);
- onebit_depop_power_stage(codec, 0);
+ onebit_depop_mute_stage(component, 0);
+ onebit_depop_power_stage(component, 0);
} else {
- depop_seq_mute_stage(codec, 0);
- depop_seq_power_stage(codec, 0);
+ depop_seq_mute_stage(component, 0);
+ depop_seq_power_stage(component, 0);
}
break;
case SND_SOC_DAPM_POST_PMU:
if (rt5631->codec_version) {
- onebit_depop_power_stage(codec, 1);
- onebit_depop_mute_stage(codec, 1);
+ onebit_depop_power_stage(component, 1);
+ onebit_depop_mute_stage(component, 1);
} else {
- depop_seq_power_stage(codec, 1);
- depop_seq_mute_stage(codec, 1);
+ depop_seq_power_stage(component, 1);
+ depop_seq_mute_stage(component, 1);
}
break;
@@ -597,20 +597,20 @@ static int hp_event(struct snd_soc_dapm_widget *w,
static int set_dmic_params(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
switch (rt5631->rx_rate) {
case 44100:
case 48000:
- snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_32FS);
break;
case 32000:
case 22050:
- snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_64FS);
break;
@@ -618,7 +618,7 @@ static int set_dmic_params(struct snd_soc_dapm_widget *w,
case 16000:
case 11025:
case 8000:
- snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_128FS);
break;
@@ -1283,7 +1283,7 @@ static const struct pll_div codec_slave_pll_div[] = {
{3072000, 12288000, 0x0a90},
};
-static struct coeff_clk_div coeff_div[] = {
+static const struct coeff_clk_div coeff_div[] = {
/* sysclk is 256fs */
{2048000, 8000 * 32, 8000, 0x1000},
{2048000, 8000 * 64, 8000, 0x0000},
@@ -1352,16 +1352,16 @@ static int get_coeff(int mclk, int rate, int timesofbclk)
static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
int timesofbclk = 32, coeff;
unsigned int iface = 0;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
rt5631->bclk_rate = snd_soc_params_to_bclk(params);
if (rt5631->bclk_rate < 0) {
- dev_err(codec->dev, "Fail to get BCLK rate\n");
+ dev_err(component->dev, "Fail to get BCLK rate\n");
return rt5631->bclk_rate;
}
rt5631->rx_rate = params_rate(params);
@@ -1373,7 +1373,7 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
timesofbclk);
if (coeff < 0) {
- dev_err(codec->dev, "Fail to get coeff\n");
+ dev_err(component->dev, "Fail to get coeff\n");
return coeff;
}
@@ -1393,9 +1393,9 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5631_SDP_CTRL,
+ snd_soc_component_update_bits(component, RT5631_SDP_CTRL,
RT5631_SDP_I2S_DL_MASK, iface);
- snd_soc_write(codec, RT5631_STEREO_AD_DA_CLK_CTRL,
+ snd_soc_component_write(component, RT5631_STEREO_AD_DA_CLK_CTRL,
coeff_div[coeff].reg_val);
return 0;
@@ -1404,17 +1404,17 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
unsigned int iface = 0;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5631->master = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface |= RT5631_SDP_MODE_SEL_SLAVE;
rt5631->master = 0;
break;
@@ -1448,7 +1448,7 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, RT5631_SDP_CTRL, iface);
+ snd_soc_component_write(component, RT5631_SDP_CTRL, iface);
return 0;
}
@@ -1456,10 +1456,10 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "enter %s, syclk=%d\n", __func__, freq);
+ dev_dbg(component->dev, "enter %s, syclk=%d\n", __func__, freq);
if ((freq >= (256 * 8000)) && (freq <= (512 * 96000))) {
rt5631->sysclk = freq;
@@ -1472,16 +1472,16 @@ static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
int i, ret = -EINVAL;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
- snd_soc_update_bits(codec, RT5631_GLOBAL_CLK_CTRL,
+ snd_soc_component_update_bits(component, RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK,
RT5631_SYSCLK_SOUR_SEL_MCLK);
@@ -1492,13 +1492,13 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++)
if (freq_in == codec_master_pll_div[i].pll_in &&
freq_out == codec_master_pll_div[i].pll_out) {
- dev_info(codec->dev,
+ dev_info(component->dev,
"change PLL in master mode\n");
- snd_soc_write(codec, RT5631_PLL_CTRL,
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
codec_master_pll_div[i].reg_val);
schedule_timeout_uninterruptible(
msecs_to_jiffies(20));
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK |
RT5631_PLLCLK_SOUR_SEL_MASK,
@@ -1511,13 +1511,13 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++)
if (freq_in == codec_slave_pll_div[i].pll_in &&
freq_out == codec_slave_pll_div[i].pll_out) {
- dev_info(codec->dev,
+ dev_info(component->dev,
"change PLL in slave mode\n");
- snd_soc_write(codec, RT5631_PLL_CTRL,
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
codec_slave_pll_div[i].reg_val);
schedule_timeout_uninterruptible(
msecs_to_jiffies(20));
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK |
RT5631_PLLCLK_SOUR_SEL_MASK,
@@ -1531,26 +1531,27 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return ret;
}
-static int rt5631_set_bias_level(struct snd_soc_codec *codec,
+static int rt5631_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD2,
RT5631_PWR_MICBIAS1_VOL | RT5631_PWR_MICBIAS2_VOL,
RT5631_PWR_MICBIAS1_VOL | RT5631_PWR_MICBIAS2_VOL);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS,
RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
msleep(80);
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_FAST_VREF_CTRL,
RT5631_PWR_FAST_VREF_CTRL);
regcache_cache_only(rt5631->regmap, false);
@@ -1559,10 +1560,10 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD1, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD2, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD3, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD4, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD1, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD2, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD3, 0x0000);
+ snd_soc_component_write(component, RT5631_PWR_MANAG_ADD4, 0x0000);
break;
default:
@@ -1572,48 +1573,49 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5631_probe(struct snd_soc_codec *codec)
+static int rt5631_probe(struct snd_soc_component *component)
{
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
unsigned int val;
- val = rt5631_read_index(codec, RT5631_ADDA_MIXER_INTL_REG3);
+ val = rt5631_read_index(component, RT5631_ADDA_MIXER_INTL_REG3);
if (val & 0x0002)
rt5631->codec_version = 1;
else
rt5631->codec_version = 0;
- rt5631_reset(codec);
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ rt5631_reset(component);
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS,
RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
msleep(80);
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_FAST_VREF_CTRL, RT5631_PWR_FAST_VREF_CTRL);
/* enable HP zero cross */
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
/* power off ClassD auto Recovery */
if (rt5631->codec_version)
- snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
0x2000, 0x2000);
else
- snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
0x2000, 0);
/* DMIC */
if (rt5631->dmic_used_flag) {
- snd_soc_update_bits(codec, RT5631_GPIO_CTRL,
+ snd_soc_component_update_bits(component, RT5631_GPIO_CTRL,
RT5631_GPIO_PIN_FUN_SEL_MASK |
RT5631_GPIO_DMIC_FUN_SEL_MASK,
RT5631_GPIO_PIN_FUN_SEL_GPIO_DIMC |
RT5631_GPIO_DMIC_FUN_SEL_DIMC);
- snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_L_CH_LATCH_MASK |
RT5631_DMIC_R_CH_LATCH_MASK,
RT5631_DMIC_L_CH_LATCH_FALLING |
RT5631_DMIC_R_CH_LATCH_RISING);
}
- snd_soc_codec_init_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_init_bias_level(dapm, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -1653,21 +1655,24 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
- .probe = rt5631_probe,
- .set_bias_level = rt5631_set_bias_level,
- .suspend_bias_off = true,
- .controls = rt5631_snd_controls,
- .num_controls = ARRAY_SIZE(rt5631_snd_controls),
- .dapm_widgets = rt5631_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets),
- .dapm_routes = rt5631_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5631 = {
+ .probe = rt5631_probe,
+ .set_bias_level = rt5631_set_bias_level,
+ .controls = rt5631_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5631_snd_controls),
+ .dapm_widgets = rt5631_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets),
+ .dapm_routes = rt5631_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct i2c_device_id rt5631_i2c_id[] = {
- { "rt5631", 0 },
- { "alc5631", 0 },
+ { "rt5631" },
+ { "alc5631" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
@@ -1690,11 +1695,12 @@ static const struct regmap_config rt5631_regmap_config = {
.max_register = RT5631_VENDOR_ID2,
.reg_defaults = rt5631_reg,
.num_reg_defaults = ARRAY_SIZE(rt5631_reg),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int rt5631_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5631_i2c_probe(struct i2c_client *i2c)
{
struct rt5631_priv *rt5631;
int ret;
@@ -1710,23 +1716,21 @@ static int rt5631_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(rt5631->regmap))
return PTR_ERR(rt5631->regmap);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5631,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5631,
rt5631_dai, ARRAY_SIZE(rt5631_dai));
return ret;
}
-static int rt5631_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+static void rt5631_i2c_remove(struct i2c_client *client)
+{}
static struct i2c_driver rt5631_i2c_driver = {
.driver = {
.name = "rt5631",
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
},
- .probe = rt5631_i2c_probe,
+ .probe = rt5631_i2c_probe,
.remove = rt5631_i2c_remove,
.id_table = rt5631_i2c_id,
};
diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h
index 13401581b0df..8a6b99a48c7c 100644
--- a/sound/soc/codecs/rt5631.h
+++ b/sound/soc/codecs/rt5631.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __RTCODEC5631_H__
#define __RTCODEC5631_H__
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 11d032cdc658..4c08c274f50e 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5640.c -- RT5640/RT5639 ALSA SoC audio codec driver
*
* Copyright 2011 Realtek Semiconductor Corp.
* Author: Johnny Hsu <johnnyhsu@realtek.com>
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -15,15 +12,15 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -55,7 +52,6 @@ static const struct reg_sequence init_list[] = {
{RT5640_PR_BASE + 0x3d, 0x3600},
{RT5640_PR_BASE + 0x12, 0x0aa8},
{RT5640_PR_BASE + 0x14, 0x0aaa},
- {RT5640_PR_BASE + 0x20, 0x6110},
{RT5640_PR_BASE + 0x21, 0xe0e0},
{RT5640_PR_BASE + 0x23, 0x1804},
};
@@ -166,9 +162,9 @@ static const struct reg_default rt5640_reg[] = {
{ 0xff, 0x6231 },
};
-static int rt5640_reset(struct snd_soc_codec *codec)
+static int rt5640_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5640_RESET, 0);
+ return snd_soc_component_write(component, RT5640_RESET, 0);
}
static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
@@ -197,6 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
+ case RT5640_GCTL2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@@ -328,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
case RT5640_HP_CALIB2:
case RT5640_SV_ZCD1:
case RT5640_SV_ZCD2:
- case RT5640_DUMMY1:
- case RT5640_DUMMY2:
+ case RT5640_GCTL1:
+ case RT5640_GCTL2:
case RT5640_DUMMY3:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
@@ -341,9 +338,9 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
}
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
@@ -359,7 +356,7 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
/* Interface data select */
static const char * const rt5640_data_select[] = {
- "Normal", "left copy to right", "right copy to left", "Swap"};
+ "Normal", "Swap", "left copy to right", "right copy to left"};
static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
@@ -402,6 +399,9 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
/* DAC Digital Volume */
SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5640_DAC2_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
175, 0, dac_vol_tlv),
@@ -423,6 +423,8 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
127, 0, adc_vol_tlv),
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5640_GCTL1,
+ RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1),
SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
127, 0, adc_vol_tlv),
@@ -443,9 +445,6 @@ static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
/* MONO Output Control */
SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT, RT5640_L_MUTE_SFT,
1, 1),
-
- SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
- RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv),
};
/**
@@ -459,40 +458,26 @@ static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5640->sysclk / rl6231_get_pre_div(rt5640->regmap,
RT5640_ADDA_CLK1, RT5640_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
idx << RT5640_DMIC_CLK_SFT);
return idx;
}
-static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
- 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;
-}
-
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 rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
if (!rt5640->asrc_en)
return 0;
@@ -928,9 +913,9 @@ static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
static const struct snd_kcontrol_new rt5640_sdi_mux =
SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
-static void hp_amp_power_on(struct snd_soc_codec *codec)
+static void hp_amp_power_on(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
/* depop parameters */
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
@@ -954,9 +939,9 @@ static void hp_amp_power_on(struct snd_soc_codec *codec)
RT5640_PWR_FV1 | RT5640_PWR_FV2);
}
-static void rt5640_pmu_depop(struct snd_soc_codec *codec)
+static void rt5640_pmu_depop(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK,
@@ -982,18 +967,18 @@ static void rt5640_pmu_depop(struct snd_soc_codec *codec)
static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- rt5640_pmu_depop(codec);
- rt5640->hp_mute = 0;
+ rt5640_pmu_depop(component);
+ rt5640->hp_mute = false;
break;
case SND_SOC_DAPM_PRE_PMD:
- rt5640->hp_mute = 1;
- usleep_range(70000, 75000);
+ rt5640->hp_mute = true;
+ msleep(70);
break;
default:
@@ -1006,22 +991,22 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
static int rt5640_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power_on(codec);
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ hp_amp_power_on(component);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_LM, RT5640_PWR_LM);
- snd_soc_update_bits(codec, RT5640_OUTPUT,
+ snd_soc_component_update_bits(component, RT5640_OUTPUT,
RT5640_L_MUTE | RT5640_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5640_OUTPUT,
+ snd_soc_component_update_bits(component, RT5640_OUTPUT,
RT5640_L_MUTE | RT5640_R_MUTE,
RT5640_L_MUTE | RT5640_R_MUTE);
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_LM, 0);
break;
@@ -1035,11 +1020,11 @@ static int rt5640_lout_event(struct snd_soc_dapm_widget *w,
static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power_on(codec);
+ hp_amp_power_on(component);
break;
default:
return 0;
@@ -1051,13 +1036,13 @@ static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (!rt5640->hp_mute)
- usleep_range(80000, 85000);
+ msleep(80);
break;
@@ -1069,9 +1054,6 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
}
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
- SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
- RT5640_PWR_PLL_BIT, 0, NULL, 0),
-
/* ASRC */
SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
15, 0, NULL, 0),
@@ -1217,11 +1199,18 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
/* DACs */
- SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1,
- RT5640_PWR_DAC_L1_BIT, 0),
- SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
- RT5640_PWR_DAC_R1_BIT, 0),
-
+ SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
/* SPK/OUT Mixer */
SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
@@ -1298,9 +1287,9 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
- SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT,
+ SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0,
0),
- SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT,
+ SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0,
0),
SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
@@ -1328,11 +1317,6 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)),
- SND_SOC_DAPM_SUPPLY("DAC L2 Filter", RT5640_PWR_DIG1,
- RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("DAC R2 Filter", RT5640_PWR_DIG1,
- RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
-
SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
@@ -1423,22 +1407,18 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"},
{"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"},
{"Stereo ADC MIXL", NULL, "Stereo Filter"},
- {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
{"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
{"Stereo ADC MIXR", NULL, "Stereo Filter"},
- {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"},
{"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"},
{"Mono ADC MIXL", NULL, "Mono Left Filter"},
- {"Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"},
{"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"},
{"Mono ADC MIXR", NULL, "Mono Right Filter"},
- {"Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"IF2 ADC L", NULL, "Mono ADC MIXL"},
{"IF2 ADC R", NULL, "Mono ADC MIXR"},
@@ -1493,8 +1473,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
{"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+ {"DAC MIXL", NULL, "DAC L1 Power"},
{"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
{"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+ {"DAC MIXR", NULL, "DAC R1 Power"},
{"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
{"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
@@ -1506,9 +1488,9 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
{"DAC L1", NULL, "Stereo DAC MIXL"},
- {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
- {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R1", NULL, "DAC R1 Power"},
{"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
{"SPK MIXL", "INL Switch", "INL VOL"},
@@ -1595,8 +1577,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"DAC L2 Mux", "IF2", "IF2 DAC L"},
{"DAC L2 Mux", "Base L/R", "Audio DSP"},
-
+ {"DAC L2 Mux", NULL, "DAC L2 Power"},
{"DAC R2 Mux", "IF2", "IF2 DAC R"},
+ {"DAC R2 Mux", NULL, "DAC R2 Power"},
{"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
{"Stereo DAC MIXL", "ANC Switch", "ANC"},
@@ -1613,9 +1596,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
{"DAC L2", NULL, "Mono DAC MIXL"},
- {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC L2", NULL, "DAC L2 Power"},
{"DAC R2", NULL, "Mono DAC MIXR"},
- {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R2", NULL, "DAC R2 Power"},
{"SPK MIXL", "DAC L2 Switch", "DAC L2"},
{"SPK MIXR", "DAC R2 Switch", "DAC R2"},
@@ -1656,18 +1639,18 @@ static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
{"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
{"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
- {"IF2 DAC L", NULL, "DAC L2 Filter"},
- {"IF2 DAC R", NULL, "DAC R2 Filter"},
+ {"IF2 DAC L", NULL, "DAC L2 Power"},
+ {"IF2 DAC R", NULL, "DAC R2 Power"},
};
-static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
+static int get_sdp_info(struct snd_soc_component *component, int dai_id)
{
int ret = 0, val;
- if (codec == NULL)
+ if (component == NULL)
return -EINVAL;
- val = snd_soc_read(codec, RT5640_I2S1_SDP);
+ val = snd_soc_component_read(component, RT5640_I2S1_SDP);
val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT;
switch (dai_id) {
case RT5640_AIF1:
@@ -1678,6 +1661,7 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
break;
case RT5640_IF_113:
ret |= RT5640_U_IF1;
+ fallthrough;
case RT5640_IF_312:
case RT5640_IF_213:
ret |= RT5640_U_IF2;
@@ -1693,6 +1677,7 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
break;
case RT5640_IF_223:
ret |= RT5640_U_IF1;
+ fallthrough;
case RT5640_IF_123:
case RT5640_IF_321:
ret |= RT5640_U_IF2;
@@ -1711,21 +1696,21 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
static int rt5640_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int dai_sel, pre_div, bclk_ms, frame_size;
rt5640->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5640->lrck[dai->id], dai->id);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return frame_size;
}
if (frame_size > 32)
@@ -1755,26 +1740,26 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- dai_sel = get_sdp_info(codec, dai->id);
+ dai_sel = get_sdp_info(component, dai->id);
if (dai_sel < 0) {
- dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
mask_clk = RT5640_I2S_BCLK_MS1_MASK | RT5640_I2S_PD1_MASK;
val_clk = bclk_ms << RT5640_I2S_BCLK_MS1_SFT |
pre_div << RT5640_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S1_SDP,
RT5640_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk);
}
if (dai_sel & RT5640_U_IF2) {
mask_clk = RT5640_I2S_BCLK_MS2_MASK | RT5640_I2S_PD2_MASK;
val_clk = bclk_ms << RT5640_I2S_BCLK_MS2_SFT |
pre_div << RT5640_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5640_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
RT5640_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk);
}
return 0;
@@ -1782,16 +1767,16 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
int dai_sel;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5640->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5640_I2S_MS_S;
rt5640->master[dai->id] = 0;
break;
@@ -1825,18 +1810,18 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- dai_sel = get_sdp_info(codec, dai->id);
+ dai_sel = get_sdp_info(component, dai->id);
if (dai_sel < 0) {
- dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
- snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S1_SDP,
RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
RT5640_I2S_DF_MASK, reg_val);
}
if (dai_sel & RT5640_U_IF2) {
- snd_soc_update_bits(codec, RT5640_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
RT5640_I2S_DF_MASK, reg_val);
}
@@ -1847,25 +1832,34 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
-
- if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
- return 0;
+ unsigned int pll_bit = 0;
+ int ret;
switch (clk_id) {
case RT5640_SCLK_S_MCLK:
+ ret = clk_set_rate(rt5640->mclk, freq);
+ if (ret)
+ return ret;
+
reg_val |= RT5640_SCLK_SRC_MCLK;
break;
case RT5640_SCLK_S_PLL1:
reg_val |= RT5640_SCLK_SRC_PLL1;
+ pll_bit |= RT5640_PWR_PLL;
+ break;
+ case RT5640_SCLK_S_RCCLK:
+ reg_val |= RT5640_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG2,
+ RT5640_PWR_PLL, pll_bit);
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
RT5640_SCLK_SRC_MASK, reg_val);
rt5640->sysclk = freq;
rt5640->sysclk_src = clk_id;
@@ -1877,67 +1871,58 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
- int ret, dai_sel;
+ int ret;
if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
freq_out == rt5640->pll_out)
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5640->pll_in = 0;
rt5640->pll_out = 0;
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5640_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
break;
case RT5640_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
+ break;
case RT5640_PLL1_S_BCLK2:
- dai_sel = get_sdp_info(codec, dai->id);
- if (dai_sel < 0) {
- dev_err(codec->dev,
- "Failed to get sdp info: %d\n", dai_sel);
- return -EINVAL;
- }
- if (dai_sel & RT5640_U_IF1) {
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
- RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
- }
- if (dai_sel & RT5640_U_IF2) {
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
- RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
- }
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
- snd_soc_write(codec, RT5640_PLL_CTRL1,
- pll_code.n_code << RT5640_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5640_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5640_PLL_M_SFT |
- pll_code.m_bp << RT5640_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5640_PLL_CTRL1,
+ (pll_code.n_code << RT5640_PLL_N_SFT) | pll_code.k_code);
+ snd_soc_component_write(component, RT5640_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5640_PLL_M_SFT) |
+ (pll_code.m_bp << RT5640_PLL_M_BP_SFT));
rt5640->pll_in = freq_in;
rt5640->pll_out = freq_out;
@@ -1946,38 +1931,67 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5640_set_bias_level(struct snd_soc_codec *codec,
+static int rt5640_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5640->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5640->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
case SND_SOC_BIAS_STANDBY:
- if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ if (SND_SOC_BIAS_OFF == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_VREF1 | RT5640_PWR_MB |
RT5640_PWR_BG | RT5640_PWR_VREF2,
RT5640_PWR_VREF1 | RT5640_PWR_MB |
RT5640_PWR_BG | RT5640_PWR_VREF2);
usleep_range(10000, 15000);
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
- snd_soc_update_bits(codec, RT5640_DUMMY1,
- 0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_MICBIAS,
+ snd_soc_component_update_bits(component, RT5640_GCTL1,
+ 0x1, 0x1);
+ snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004);
- snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100);
- snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0);
- snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5640_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5640_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5640_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0000);
+ snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004);
+ snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100);
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0);
+ snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_MIXER, 0x0000);
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x2818);
+ else
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x0000);
+ snd_soc_component_write(component, RT5640_PWR_ANLG2, 0x0000);
break;
default:
@@ -1987,10 +2001,10 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-int rt5640_dmic_enable(struct snd_soc_codec *codec,
+int rt5640_dmic_enable(struct snd_soc_component *component,
bool dmic1_data_pin, bool dmic2_data_pin)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
@@ -2013,10 +2027,10 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
-int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5640_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int asrc2_mask = 0;
unsigned int asrc2_value = 0;
@@ -2068,38 +2082,600 @@ int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
| (clk_src << RT5640_MAD_R_M_SFT);
}
- snd_soc_update_bits(codec, RT5640_ASRC_2,
+ snd_soc_component_update_bits(component, RT5640_ASRC_2,
asrc2_mask, asrc2_value);
- if (snd_soc_read(codec, RT5640_ASRC_2)) {
+ if (snd_soc_component_read(component, RT5640_ASRC_2)) {
rt5640->asrc_en = true;
- snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x3);
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x3);
} else {
rt5640->asrc_en = false;
- snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x0);
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x0);
}
return 0;
}
EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
-static int rt5640_probe(struct snd_soc_codec *codec)
+void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1");
+ /* OVCD is unreliable when used with RCCLK as sysclk-source */
+ if (rt5640->use_platform_clock)
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+EXPORT_SYMBOL_GPL(rt5640_enable_micbias1_for_ovcd);
+
+void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (rt5640->use_platform_clock)
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "LDO2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+EXPORT_SYMBOL_GPL(rt5640_disable_micbias1_for_ovcd);
+
+static void rt5640_enable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_NOR);
+ rt5640->ovcd_irq_enabled = true;
+}
+
+static void rt5640_disable_micbias1_ovcd_irq(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
- rt5640->codec = codec;
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_BP);
+ rt5640->ovcd_irq_enabled = false;
+}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+static void rt5640_clear_micbias1_ovcd(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_MB1_OC_STATUS, 0);
+}
+
+static bool rt5640_micbias1_ovcd(struct snd_soc_component *component)
+{
+ int val;
- 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);
+ val = snd_soc_component_read(component, RT5640_IRQ_CTRL2);
+ dev_dbg(component->dev, "irq ctrl2 %#04x\n", val);
+
+ return (val & RT5640_MB1_OC_STATUS);
+}
+
+static bool rt5640_jack_inserted(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int val;
- switch (snd_soc_read(codec, RT5640_RESET) & RT5640_ID_MASK) {
+ if (rt5640->jd_gpio)
+ val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0;
+ else
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+
+ dev_dbg(component->dev, "irq status %#04x\n", val);
+
+ if (rt5640->jd_inverted)
+ return !(val & RT5640_JD_STATUS);
+ else
+ return (val & RT5640_JD_STATUS);
+}
+
+/* Jack detect and button-press timings */
+#define JACK_SETTLE_TIME 100 /* milli seconds */
+#define JACK_DETECT_COUNT 5
+#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */
+#define JACK_UNPLUG_TIME 80 /* milli seconds */
+#define BP_POLL_TIME 10 /* milli seconds */
+#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */
+#define BP_THRESHOLD 3
+
+static void rt5640_start_button_press_work(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ rt5640->poll_count = 0;
+ rt5640->press_count = 0;
+ rt5640->release_count = 0;
+ rt5640->pressed = false;
+ rt5640->press_reported = false;
+ rt5640_clear_micbias1_ovcd(component);
+ schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+static void rt5640_button_press_work(struct work_struct *work)
+{
+ struct rt5640_priv *rt5640 =
+ container_of(work, struct rt5640_priv, bp_work.work);
+ struct snd_soc_component *component = rt5640->component;
+
+ /* Check the jack was not removed underneath us */
+ if (!rt5640_jack_inserted(component))
+ return;
+
+ if (rt5640_micbias1_ovcd(component)) {
+ rt5640->release_count = 0;
+ rt5640->press_count++;
+ /* Remember till after JACK_UNPLUG_TIME wait */
+ if (rt5640->press_count >= BP_THRESHOLD)
+ rt5640->pressed = true;
+ rt5640_clear_micbias1_ovcd(component);
+ } else {
+ rt5640->press_count = 0;
+ rt5640->release_count++;
+ }
+
+ /*
+ * The pins get temporarily shorted on jack unplug, so we poll for
+ * at least JACK_UNPLUG_TIME milli-seconds before reporting a press.
+ */
+ rt5640->poll_count++;
+ if (rt5640->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) {
+ schedule_delayed_work(&rt5640->bp_work,
+ msecs_to_jiffies(BP_POLL_TIME));
+ return;
+ }
+
+ if (rt5640->pressed && !rt5640->press_reported) {
+ dev_dbg(component->dev, "headset button press\n");
+ snd_soc_jack_report(rt5640->jack, SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ rt5640->press_reported = true;
+ }
+
+ if (rt5640->release_count >= BP_THRESHOLD) {
+ if (rt5640->press_reported) {
+ dev_dbg(component->dev, "headset button release\n");
+ snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0);
+ }
+ /* Re-enable OVCD IRQ to detect next press */
+ rt5640_enable_micbias1_ovcd_irq(component);
+ return; /* Stop polling */
+ }
+
+ schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio)
+{
+ int i, headset_count = 0, headphone_count = 0;
+
+ /*
+ * We get the insertion event before the jack is fully inserted at which
+ * point the second ring on a TRRS connector may short the 2nd ring and
+ * sleeve contacts, also the overcurrent detection is not entirely
+ * reliable. So we try several times with a wait in between until we
+ * detect the same type JACK_DETECT_COUNT times in a row.
+ */
+ for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) {
+ /* Clear any previous over-current status flag */
+ rt5640_clear_micbias1_ovcd(component);
+
+ msleep(JACK_SETTLE_TIME);
+
+ /* Check the jack is still connected before checking ovcd */
+ if (hp_det_gpio) {
+ if (gpiod_get_value_cansleep(hp_det_gpio))
+ return 0;
+ } else {
+ if (!rt5640_jack_inserted(component))
+ return 0;
+ }
+
+ if (rt5640_micbias1_ovcd(component)) {
+ /*
+ * Over current detected, there is a short between the
+ * 2nd ring contact and the ground, so a TRS connector
+ * without a mic contact and thus plain headphones.
+ */
+ dev_dbg(component->dev, "jack mic-gnd shorted\n");
+ headset_count = 0;
+ headphone_count++;
+ if (headphone_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADPHONE;
+ } else {
+ dev_dbg(component->dev, "jack mic-gnd open\n");
+ headphone_count = 0;
+ headset_count++;
+ if (headset_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADSET;
+ }
+ }
+
+ dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n");
+ return SND_JACK_HEADPHONE;
+}
+EXPORT_SYMBOL_GPL(rt5640_detect_headset);
+
+static void rt5640_jack_work(struct work_struct *work)
+{
+ struct rt5640_priv *rt5640 =
+ container_of(work, struct rt5640_priv, jack_work.work);
+ struct snd_soc_component *component = rt5640->component;
+ int status;
+
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged;
+
+ /* mic jack */
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+ hda_mic_plugged = !(val & RT5640_JD_STATUS);
+ dev_dbg(component->dev, "mic jack status %d\n",
+ hda_mic_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1,
+ RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT);
+
+ if (hda_mic_plugged)
+ jack_type |= SND_JACK_MICROPHONE;
+
+ /* headphone jack */
+ val = snd_soc_component_read(component, RT5640_GCTL2);
+ hda_hp_plugged = !(val & (0x1 << 11));
+ dev_dbg(component->dev, "headphone jack status %d\n",
+ hda_hp_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_GCTL2,
+ (0x1 << 10), !hda_hp_plugged << 10);
+
+ if (hda_hp_plugged)
+ jack_type |= SND_JACK_HEADPHONE;
+
+ snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET);
+
+ return;
+ }
+
+ if (!rt5640_jack_inserted(component)) {
+ /* Jack removed, or spurious IRQ? */
+ if (rt5640->jack->status & SND_JACK_HEADPHONE) {
+ if (rt5640->jack->status & SND_JACK_MICROPHONE) {
+ cancel_delayed_work_sync(&rt5640->bp_work);
+ rt5640_disable_micbias1_ovcd_irq(component);
+ rt5640_disable_micbias1_for_ovcd(component);
+ }
+ snd_soc_jack_report(rt5640->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ dev_dbg(component->dev, "jack unplugged\n");
+ }
+ } else if (!(rt5640->jack->status & SND_JACK_HEADPHONE)) {
+ /* Jack inserted */
+ WARN_ON(rt5640->ovcd_irq_enabled);
+ rt5640_enable_micbias1_for_ovcd(component);
+ status = rt5640_detect_headset(component, NULL);
+ if (status == SND_JACK_HEADSET) {
+ /* Enable ovcd IRQ for button press detect. */
+ rt5640_enable_micbias1_ovcd_irq(component);
+ } else {
+ /* No more need for overcurrent detect. */
+ rt5640_disable_micbias1_for_ovcd(component);
+ }
+ dev_dbg(component->dev, "detect status %#02x\n", status);
+ snd_soc_jack_report(rt5640->jack, status, SND_JACK_HEADSET);
+ } else if (rt5640->ovcd_irq_enabled && rt5640_micbias1_ovcd(component)) {
+ dev_dbg(component->dev, "OVCD IRQ\n");
+
+ /*
+ * The ovcd IRQ keeps firing while the button is pressed, so
+ * we disable it and start polling the button until released.
+ *
+ * The disable will make the IRQ pin 0 again and since we get
+ * IRQs on both edges (so as to detect both jack plugin and
+ * unplug) this means we will immediately get another IRQ.
+ * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP.
+ */
+ rt5640_disable_micbias1_ovcd_irq(component);
+ rt5640_start_button_press_work(component);
+
+ /*
+ * If the jack-detect IRQ flag goes high (unplug) after our
+ * above rt5640_jack_inserted() check and before we have
+ * disabled the OVCD IRQ, the IRQ pin will stay high and as
+ * we react to edges, we miss the unplug event -> recheck.
+ */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+}
+
+static irqreturn_t rt5640_irq(int irq, void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+ int delay = 0;
+
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ delay = 100;
+
+ if (rt5640->jack)
+ mod_delayed_work(system_long_wq, &rt5640->jack_work, delay);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work,
+ msecs_to_jiffies(JACK_SETTLE_TIME));
+
+ return IRQ_HANDLED;
+}
+
+static void rt5640_disable_irq_and_cancel_work(void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ if (rt5640->jd_gpio_irq_requested) {
+ free_irq(rt5640->jd_gpio_irq, rt5640);
+ rt5640->jd_gpio_irq_requested = false;
+ }
+
+ if (rt5640->irq_requested) {
+ free_irq(rt5640->irq, rt5640);
+ rt5640->irq_requested = false;
+ }
+
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->bp_work);
+}
+
+void rt5640_set_ovcd_params(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4,
+ 0xa800 | rt5640->ovcd_sf);
+
+ snd_soc_component_update_bits(component, RT5640_MICBIAS,
+ RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK,
+ rt5640->ovcd_th | RT5640_MIC1_OVCD_EN);
+
+ /*
+ * The over-current-detect is only reliable in detecting the absence
+ * of over-current, when the mic-contact in the jack is short-circuited,
+ * the hardware periodically retries if it can apply the bias-current
+ * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about
+ * 10% of the time, as we poll the ovcd status bit we might hit that
+ * 10%, so we enable sticky mode and when checking OVCD we clear the
+ * status, msleep() a bit and then check to get a reliable reading.
+ */
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2,
+ RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN);
+}
+EXPORT_SYMBOL_GPL(rt5640_set_ovcd_params);
+
+static void rt5640_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * soc_remove_component() force-disables jack and thus rt5640->jack
+ * could be NULL at the time of driver's module unloading.
+ */
+ if (!rt5640->jack)
+ return;
+
+ rt5640_disable_irq_and_cancel_work(rt5640);
+
+ if (rt5640->jack->status & SND_JACK_MICROPHONE) {
+ rt5640_disable_micbias1_ovcd_irq(component);
+ rt5640_disable_micbias1_for_ovcd(component);
+ snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0);
+ }
+
+ rt5640->jd_gpio = NULL;
+ rt5640->jack = NULL;
+}
+
+static void rt5640_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack,
+ struct rt5640_set_jack_data *jack_data)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Select JD-source */
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL,
+ RT5640_JD_MASK, rt5640->jd_src << RT5640_JD_SFT);
+
+ /* Selecting GPIO01 as an interrupt */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
+ RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ);
+
+ /* Set GPIO1 output */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
+ RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
+
+ snd_soc_component_write(component, RT5640_GCTL1, 0x3f41);
+
+ rt5640_set_ovcd_params(component);
+
+ /*
+ * All IRQs get or-ed together, so we need the jack IRQ to report 0
+ * when a jack is inserted so that the OVCD IRQ then toggles the IRQ
+ * pin 0/1 instead of it being stuck to 1. So we invert the JD polarity
+ * on systems where the hardware does not already do this.
+ */
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P)
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1,
+ RT5640_IRQ_JD_NOR);
+ else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(component, RT5640_GCTL2,
+ RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR | RT5640_JD2_EN);
+ } else {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P)
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1,
+ RT5640_IRQ_JD_NOR | RT5640_JD_P_INV);
+ else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(component, RT5640_GCTL2,
+ RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
+
+ rt5640->jack = jack;
+ if (rt5640->jack->status & SND_JACK_MICROPHONE) {
+ rt5640_enable_micbias1_for_ovcd(component);
+ rt5640_enable_micbias1_ovcd_irq(component);
+ }
+
+ if (jack_data && jack_data->codec_irq_override)
+ rt5640->irq = jack_data->codec_irq_override;
+
+ if (jack_data && jack_data->jd_gpio) {
+ rt5640->jd_gpio = jack_data->jd_gpio;
+ rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio);
+
+ ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5640-jd-gpio", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n",
+ rt5640->jd_gpio_irq, ret);
+ rt5640_disable_jack_detect(component);
+ return;
+ }
+ rt5640->jd_gpio_irq_requested = true;
+ }
+
+ if (jack_data && jack_data->use_platform_clock)
+ rt5640->use_platform_clock = jack_data->use_platform_clock;
+
+ ret = request_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "rt5640", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640_disable_jack_detect(component);
+ return;
+ }
+ rt5640->irq_requested = true;
+
+ /* sync initial jack state */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+}
+
+static const struct snd_soc_dapm_route rt5640_hda_jack_dapm_routes[] = {
+ {"IN1P", NULL, "MICBIAS1"},
+ {"IN2P", NULL, "MICBIAS1"},
+ {"IN3P", NULL, "MICBIAS1"},
+};
+
+static void rt5640_enable_hda_jack_detect(
+ struct snd_soc_component *component, struct snd_soc_jack *jack)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ /* Select JD1 for Mic */
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL,
+ RT5640_JD_MASK, RT5640_JD_JD1_IN4P);
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR);
+
+ /* Select JD2 for Headphone */
+ snd_soc_component_update_bits(component, RT5640_GCTL2, 0x1100, 0x1100);
+
+ /* Selecting GPIO01 as an interrupt */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
+ RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ);
+
+ /* Set GPIO1 output */
+ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
+ RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
+
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 0x400, 0x0);
+
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV2, RT5640_PWR_FV2);
+
+ rt5640->jack = jack;
+
+ ret = request_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640->jack = NULL;
+ return;
+ }
+ rt5640->irq_requested = true;
+
+ /* sync initial jack state */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+
+ snd_soc_dapm_add_routes(dapm, rt5640_hda_jack_dapm_routes,
+ ARRAY_SIZE(rt5640_hda_jack_dapm_routes));
+}
+
+static int rt5640_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ rt5640_enable_hda_jack_detect(component, jack);
+ else
+ rt5640_enable_jack_detect(component, jack, data);
+ } else {
+ rt5640_disable_jack_detect(component);
+ }
+
+ return 0;
+}
+
+static int rt5640_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ u32 dmic1_data_pin = 0;
+ u32 dmic2_data_pin = 0;
+ bool dmic_en = false;
+ u32 val;
+
+ /* Check if MCLK provided */
+ rt5640->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5640->mclk))
+ return PTR_ERR(rt5640->mclk);
+
+ rt5640->component = component;
+
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 0x0301, 0x0301);
+ snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030);
+ snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
+
+ switch (snd_soc_component_read(component, RT5640_RESET) & RT5640_ID_MASK) {
case RT5640_ID_5640:
case RT5640_ID_5642:
- snd_soc_add_codec_controls(codec,
+ snd_soc_add_component_controls(component,
rt5640_specific_snd_controls,
ARRAY_SIZE(rt5640_specific_snd_controls));
snd_soc_dapm_new_controls(dapm,
@@ -2118,52 +2694,170 @@ static int rt5640_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5639_specific_dapm_routes));
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"The driver is for RT5639 RT5640 or RT5642 only\n");
return -ENODEV;
}
- if (rt5640->pdata.dmic_en)
- rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin,
- rt5640->pdata.dmic2_data_pin);
+ /*
+ * Note on some platforms the platform code may need to add device-props
+ * rather then relying only on properties set by the firmware.
+ * Therefor the property parsing MUST be done here, rather then from
+ * rt5640_i2c_probe(), so that the platform-code can attach extra
+ * properties before calling snd_soc_register_card().
+ */
+ if (device_property_read_bool(component->dev, "realtek,in1-differential"))
+ snd_soc_component_update_bits(component, RT5640_IN1_IN2,
+ RT5640_IN_DF1, RT5640_IN_DF1);
+
+ if (device_property_read_bool(component->dev, "realtek,in2-differential"))
+ snd_soc_component_update_bits(component, RT5640_IN3_IN4,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ if (device_property_read_bool(component->dev, "realtek,in3-differential"))
+ snd_soc_component_update_bits(component, RT5640_IN1_IN2,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ if (device_property_read_bool(component->dev, "realtek,lout-differential"))
+ snd_soc_component_update_bits(component, RT5640_GCTL1,
+ RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF);
+
+ if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin",
+ &val) == 0 && val) {
+ dmic1_data_pin = val - 1;
+ dmic_en = true;
+ }
+
+ if (device_property_read_u32(component->dev, "realtek,dmic2-data-pin",
+ &val) == 0 && val) {
+ dmic2_data_pin = val - 1;
+ dmic_en = true;
+ }
+
+ if (dmic_en)
+ rt5640_dmic_enable(component, dmic1_data_pin, dmic2_data_pin);
+
+ if (device_property_read_u32(component->dev,
+ "realtek,jack-detect-source", &val) == 0) {
+ if (val <= RT5640_JD_SRC_HDA_HEADER)
+ rt5640->jd_src = val;
+ else
+ dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n",
+ val);
+ }
+
+ if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
+ rt5640->jd_inverted = true;
+
+ /*
+ * Testing on various boards has shown that good defaults for the OVCD
+ * threshold and scale-factor are 2000µA and 0.75. For an effective
+ * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0.
+ */
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA;
+ rt5640->ovcd_sf = RT5640_MIC_OVCD_SF_0P75;
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-threshold-microamp", &val) == 0) {
+ switch (val) {
+ case 600:
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_600UA;
+ break;
+ case 1500:
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_1500UA;
+ break;
+ case 2000:
+ rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA;
+ break;
+ default:
+ dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n",
+ val);
+ }
+ }
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-scale-factor", &val) == 0) {
+ if (val <= RT5640_OVCD_SF_1P5)
+ rt5640->ovcd_sf = val << RT5640_MIC_OVCD_SF_SFT;
+ else
+ dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n",
+ val);
+ }
return 0;
}
-static int rt5640_remove(struct snd_soc_codec *codec)
+static void rt5640_remove(struct snd_soc_component *component)
{
- rt5640_reset(codec);
-
- return 0;
+ rt5640_reset(component);
}
#ifdef CONFIG_PM
-static int rt5640_suspend(struct snd_soc_codec *codec)
+static int rt5640_suspend(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ if (rt5640->jack) {
+ /* disable jack interrupts during system suspend */
+ disable_irq(rt5640->irq);
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->bp_work);
+ }
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
- rt5640_reset(codec);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+ rt5640_reset(component);
regcache_cache_only(rt5640->regmap, true);
regcache_mark_dirty(rt5640->regmap);
- if (gpio_is_valid(rt5640->pdata.ldo1_en))
- gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 0);
+ if (rt5640->ldo1_en)
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 0);
return 0;
}
-static int rt5640_resume(struct snd_soc_codec *codec)
+static int rt5640_resume(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
- gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1);
+ if (rt5640->ldo1_en) {
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 1);
msleep(400);
}
regcache_cache_only(rt5640->regmap, false);
regcache_sync(rt5640->regmap);
+ if (rt5640->jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ snd_soc_component_update_bits(component,
+ RT5640_GCTL2, 0x1100, 0x1100);
+ } else {
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_GCTL2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_EN);
+
+ } else {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_GCTL2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
+ }
+
+ enable_irq(rt5640->irq);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+
return 0;
}
#else
@@ -2223,32 +2917,35 @@ static struct snd_soc_dai_driver rt5640_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
- .probe = rt5640_probe,
- .remove = rt5640_remove,
- .suspend = rt5640_suspend,
- .resume = rt5640_resume,
- .set_bias_level = rt5640_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5640_snd_controls,
- .num_controls = ARRAY_SIZE(rt5640_snd_controls),
- .dapm_widgets = rt5640_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
- .dapm_routes = rt5640_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5640 = {
+ .probe = rt5640_probe,
+ .remove = rt5640_remove,
+ .suspend = rt5640_suspend,
+ .resume = rt5640_resume,
+ .set_bias_level = rt5640_set_bias_level,
+ .set_jack = rt5640_set_jack,
+ .controls = rt5640_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5640_snd_controls),
+ .dapm_widgets = rt5640_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
+ .dapm_routes = rt5640_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5640_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5640_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5640_ranges) *
RT5640_PR_SPACING),
.volatile_reg = rt5640_volatile_register,
.readable_reg = rt5640_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5640_reg,
.num_reg_defaults = ARRAY_SIZE(rt5640_reg),
.ranges = rt5640_ranges,
@@ -2256,9 +2953,9 @@ static const struct regmap_config rt5640_regmap = {
};
static const struct i2c_device_id rt5640_i2c_id[] = {
- { "rt5640", 0 },
- { "rt5639", 0 },
- { "rt5642", 0 },
+ { "rt5640" },
+ { "rt5639" },
+ { "rt5642" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
@@ -2267,48 +2964,25 @@ MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
static const struct of_device_id rt5640_of_match[] = {
{ .compatible = "realtek,rt5639", },
{ .compatible = "realtek,rt5640", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rt5640_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5640_acpi_match[] = {
- { "INT33CA", 0 },
- { "10EC5640", 0 },
- { "10EC5642", 0 },
- { "INTCCFFD", 0 },
- { },
+ { "10EC3276" },
+ { "10EC5640" },
+ { "10EC5642" },
+ { "INT33CA" },
+ { "INTCCFFD" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
#endif
-static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
+static int rt5640_i2c_probe(struct i2c_client *i2c)
{
- 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;
@@ -2320,22 +2994,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
- if (pdata) {
- rt5640->pdata = *pdata;
- /*
- * Translate zero'd out (default) pdata value to an invalid
- * GPIO ID. This makes the pdata and DT paths consistent in
- * terms of the value left in this field when no GPIO is
- * specified, but means we can't actually use GPIO 0.
- */
- if (!rt5640->pdata.ldo1_en)
- rt5640->pdata.ldo1_en = -EINVAL;
- } else if (i2c->dev.of_node) {
- ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
- if (ret)
- return ret;
- } else
- rt5640->pdata.ldo1_en = -EINVAL;
+ rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5640->ldo1_en))
+ return PTR_ERR(rt5640->ldo1_en);
+
+ if (rt5640->ldo1_en) {
+ gpiod_set_consumer_name(rt5640->ldo1_en, "RT5640 LDO1_EN");
+ msleep(400);
+ }
rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
if (IS_ERR(rt5640->regmap)) {
@@ -2345,19 +3013,12 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return ret;
}
- if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
- ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH,
- "RT5640 LDO1_EN");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n",
- rt5640->pdata.ldo1_en, ret);
- return ret;
- }
- msleep(400);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ if (val != RT5640_DEVICE_ID) {
+ usleep_range(60000, 100000);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
}
- regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
if (val != RT5640_DEVICE_ID) {
dev_err(&i2c->dev,
"Device with ID register %#x is not rt5640/39\n", val);
@@ -2371,31 +3032,24 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- if (rt5640->pdata.in1_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
- RT5640_IN_DF1, RT5640_IN_DF1);
-
- if (rt5640->pdata.in2_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
- RT5640_IN_DF2, RT5640_IN_DF2);
+ regmap_update_bits(rt5640->regmap, RT5640_GCTL1,
+ RT5640_MCLK_DET, RT5640_MCLK_DET);
- if (rt5640->pdata.in3_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
- RT5640_IN_DF2, RT5640_IN_DF2);
+ rt5640->hp_mute = true;
+ rt5640->irq = i2c->irq;
+ INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work);
+ INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work);
- rt5640->hp_mute = 1;
+ /* Make sure work is stopped on probe-error / remove */
+ ret = devm_add_action_or_reset(&i2c->dev, rt5640_disable_irq_and_cancel_work, rt5640);
+ if (ret)
+ return ret;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai));
}
-static int rt5640_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5640_i2c_driver = {
.driver = {
.name = "rt5640",
@@ -2403,7 +3057,6 @@ static struct i2c_driver rt5640_i2c_driver = {
.of_match_table = of_match_ptr(rt5640_of_match),
},
.probe = rt5640_i2c_probe,
- .remove = rt5640_i2c_remove,
.id_table = rt5640_i2c_id,
};
module_i2c_driver(rt5640_i2c_driver);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 83a7150ddc24..8a12cee76bdc 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -1,18 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5640.h -- RT5640 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _RT5640_H
#define _RT5640_H
-#include <sound/rt5640.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/workqueue.h>
+#include <dt-bindings/sound/rt5640.h>
/* Info */
#define RT5640_RESET 0x00
@@ -139,12 +139,13 @@
#define RT5640_SV_ZCD1 0xd9
#define RT5640_SV_ZCD2 0xda
/* Dummy Register */
-#define RT5640_DUMMY1 0xfa
-#define RT5640_DUMMY2 0xfb
+#define RT5640_GCTL1 0xfa
+#define RT5640_GCTL2 0xfb
#define RT5640_DUMMY3 0xfc
/* Index of Codec Private Register definition */
+#define RT5640_BIAS_CUR4 0x15
#define RT5640_CHPUMP_INT_REG1 0x24
#define RT5640_MAMP_INT_REG2 0x37
#define RT5640_3D_SPK 0x63
@@ -442,39 +443,39 @@
#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14)
#define RT5640_IF1_DAC_SEL_SFT 14
#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14)
-#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14)
-#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14)
-#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 14)
+#define RT5640_IF1_DAC_SEL_SWAP (0x1 << 14)
+#define RT5640_IF1_DAC_SEL_L2R (0x2 << 14)
+#define RT5640_IF1_DAC_SEL_R2L (0x3 << 14)
#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12)
#define RT5640_IF1_ADC_SEL_SFT 12
#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12)
-#define RT5640_IF1_ADC_SEL_L2R (0x1 << 12)
-#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12)
-#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 12)
+#define RT5640_IF1_ADC_SEL_SWAP (0x1 << 12)
+#define RT5640_IF1_ADC_SEL_L2R (0x2 << 12)
+#define RT5640_IF1_ADC_SEL_R2L (0x3 << 12)
#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10)
#define RT5640_IF2_DAC_SEL_SFT 10
#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10)
-#define RT5640_IF2_DAC_SEL_L2R (0x1 << 10)
-#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10)
-#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 10)
+#define RT5640_IF2_DAC_SEL_SWAP (0x1 << 10)
+#define RT5640_IF2_DAC_SEL_L2R (0x2 << 10)
+#define RT5640_IF2_DAC_SEL_R2L (0x3 << 10)
#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8)
#define RT5640_IF2_ADC_SEL_SFT 8
#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8)
-#define RT5640_IF2_ADC_SEL_L2R (0x1 << 8)
-#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8)
-#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 8)
+#define RT5640_IF2_ADC_SEL_SWAP (0x1 << 8)
+#define RT5640_IF2_ADC_SEL_L2R (0x2 << 8)
+#define RT5640_IF2_ADC_SEL_R2L (0x3 << 8)
#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6)
#define RT5640_IF3_DAC_SEL_SFT 6
#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6)
-#define RT5640_IF3_DAC_SEL_L2R (0x1 << 6)
-#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6)
-#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 6)
+#define RT5640_IF3_DAC_SEL_SWAP (0x1 << 6)
+#define RT5640_IF3_DAC_SEL_L2R (0x2 << 6)
+#define RT5640_IF3_DAC_SEL_R2L (0x3 << 6)
#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4)
#define RT5640_IF3_ADC_SEL_SFT 4
#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4)
-#define RT5640_IF3_ADC_SEL_L2R (0x1 << 4)
-#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4)
-#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 4)
+#define RT5640_IF3_ADC_SEL_SWAP (0x1 << 4)
+#define RT5640_IF3_ADC_SEL_L2R (0x2 << 4)
+#define RT5640_IF3_ADC_SEL_R2L (0x3 << 4)
/* REC Left Mixer Control 1 (0x3b) */
#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13)
@@ -983,6 +984,7 @@
#define RT5640_SCLK_SRC_SFT 14
#define RT5640_SCLK_SRC_MCLK (0x0 << 14)
#define RT5640_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5640_SCLK_SRC_RCCLK (0x2 << 14)
#define RT5640_PLL1_SRC_MASK (0x3 << 12)
#define RT5640_PLL1_SRC_SFT 12
#define RT5640_PLL1_SRC_MCLK (0x0 << 12)
@@ -1605,10 +1607,17 @@
#define RT5640_MB2_OC_P_SFT 6
#define RT5640_MB2_OC_P_NOR (0x0 << 6)
#define RT5640_MB2_OC_P_INV (0x1 << 6)
-#define RT5640_MB1_OC_CLR (0x1 << 3)
-#define RT5640_MB1_OC_CLR_SFT 3
-#define RT5640_MB2_OC_CLR (0x1 << 2)
-#define RT5640_MB2_OC_CLR_SFT 2
+#define RT5640_MB1_OC_STATUS (0x1 << 3)
+#define RT5640_MB1_OC_STATUS_SFT 3
+#define RT5640_MB2_OC_STATUS (0x1 << 2)
+#define RT5640_MB2_OC_STATUS_SFT 2
+
+/* GPIO and Internal Status (0xbf) */
+#define RT5640_GPIO1_STATUS (0x1 << 8)
+#define RT5640_GPIO2_STATUS (0x1 << 7)
+#define RT5640_JD_STATUS (0x1 << 4)
+#define RT5640_OVT_STATUS (0x1 << 3)
+#define RT5640_CLS_D_OVCD_STATUS (0x1 << 0)
/* GPIO Control 1 (0xc0) */
#define RT5640_GP1_PIN_MASK (0x1 << 15)
@@ -1968,8 +1977,39 @@
#define RT5640_ZCD_HP_DIS (0x0 << 15)
#define RT5640_ZCD_HP_EN (0x1 << 15)
+/* General Control 1 (0xfa) */
+#define RT5640_EN_LOUT_DF (0x1 << 14)
+#define RT5640_EN_LOUT_DF_SFT 14
+#define RT5640_M_MONO_ADC_L (0x1 << 13)
+#define RT5640_M_MONO_ADC_L_SFT 13
+#define RT5640_M_MONO_ADC_R (0x1 << 12)
+#define RT5640_M_MONO_ADC_R_SFT 12
+#define RT5640_MCLK_DET (0x1 << 11)
+
+/* General Control 2 (0xfb) */
+#define RT5640_IRQ_JD2_MASK (0x1 << 12)
+#define RT5640_IRQ_JD2_SFT 12
+#define RT5640_IRQ_JD2_BP (0x0 << 12)
+#define RT5640_IRQ_JD2_NOR (0x1 << 12)
+#define RT5640_JD2_P_MASK (0x1 << 10)
+#define RT5640_JD2_P_SFT 10
+#define RT5640_JD2_P_NOR (0x0 << 10)
+#define RT5640_JD2_P_INV (0x1 << 10)
+#define RT5640_JD2_MASK (0x1 << 8)
+#define RT5640_JD2_SFT 8
+#define RT5640_JD2_DIS (0x0 << 8)
+#define RT5640_JD2_EN (0x1 << 8)
/* Codec Private Register definition */
+
+/* MIC Over current threshold scale factor (0x15) */
+#define RT5640_MIC_OVCD_SF_MASK (0x3 << 8)
+#define RT5640_MIC_OVCD_SF_SFT 8
+#define RT5640_MIC_OVCD_SF_0P5 (0x0 << 8)
+#define RT5640_MIC_OVCD_SF_0P75 (0x1 << 8)
+#define RT5640_MIC_OVCD_SF_1P0 (0x2 << 8)
+#define RT5640_MIC_OVCD_SF_1P5 (0x3 << 8)
+
/* 3D Speaker Control (0x63) */
#define RT5640_3D_SPK_MASK (0x1 << 15)
#define RT5640_3D_SPK_SFT 15
@@ -2094,10 +2134,13 @@ enum {
};
struct rt5640_priv {
- struct snd_soc_codec *codec;
- struct rt5640_platform_data pdata;
+ struct snd_soc_component *component;
struct regmap *regmap;
+ struct clk *mclk;
+ struct gpio_desc *ldo1_en; /* GPIO for LDO1_EN */
+ int irq;
+ int jd_gpio_irq;
int sysclk;
int sysclk_src;
int lrck[RT5640_AIFS];
@@ -2110,11 +2153,41 @@ struct rt5640_priv {
bool hp_mute;
bool asrc_en;
+ bool irq_requested;
+ bool jd_gpio_irq_requested;
+
+ /* Jack and button detect data */
+ bool ovcd_irq_enabled;
+ bool pressed;
+ bool press_reported;
+ int press_count;
+ int release_count;
+ int poll_count;
+ struct delayed_work bp_work;
+ struct delayed_work jack_work;
+ struct snd_soc_jack *jack;
+ struct gpio_desc *jd_gpio;
+ unsigned int jd_src;
+ bool jd_inverted;
+ unsigned int ovcd_th;
+ unsigned int ovcd_sf;
+ bool use_platform_clock;
};
-int rt5640_dmic_enable(struct snd_soc_codec *codec,
+struct rt5640_set_jack_data {
+ int codec_irq_override;
+ struct gpio_desc *jd_gpio;
+ bool use_platform_clock;
+};
+
+int rt5640_dmic_enable(struct snd_soc_component *component,
bool dmic1_data_pin, bool dmic2_data_pin);
-int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5640_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
+void rt5640_set_ovcd_params(struct snd_soc_component *component);
+void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component);
+void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component);
+int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio);
+
#endif
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index c61d38b585fb..f7701b8d0d3c 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5645.c -- RT5645 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -17,7 +14,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -34,6 +30,20 @@
#include "rl6231.h"
#include "rt5645.h"
+#define QUIRK_INV_JD1_1(q) ((q) & 1)
+#define QUIRK_LEVEL_IRQ(q) (((q) >> 1) & 1)
+#define QUIRK_IN2_DIFF(q) (((q) >> 2) & 1)
+#define QUIRK_INV_HP_POL(q) (((q) >> 3) & 1)
+#define QUIRK_JD_MODE(q) (((q) >> 4) & 7)
+#define QUIRK_DMIC1_DATA_PIN(q) (((q) >> 8) & 3)
+#define QUIRK_DMIC2_DATA_PIN(q) (((q) >> 12) & 3)
+
+static unsigned int quirk = -1;
+module_param(quirk, uint, 0444);
+MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override");
+
+static const struct acpi_gpio_mapping *cht_rt5645_gpios;
+
#define RT5645_DEVICE_ID 0x6308
#define RT5650_DEVICE_ID 0x6419
@@ -44,6 +54,8 @@
#define RT5645_HWEQ_NUM 57
+#define TIME_TO_POWER_MS 400
+
static const struct regmap_range_cfg rt5645_ranges[] = {
{
.name = "PR",
@@ -59,14 +71,18 @@ static const struct regmap_range_cfg rt5645_ranges[] = {
static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x3d, 0x3600},
- {RT5645_PR_BASE + 0x1c, 0xfd20},
+ {RT5645_PR_BASE + 0x1c, 0xfd70},
{RT5645_PR_BASE + 0x20, 0x611f},
{RT5645_PR_BASE + 0x21, 0x4040},
{RT5645_PR_BASE + 0x23, 0x0004},
+ {RT5645_ASRC_4, 0x0120},
};
static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
+ {RT5645_PWR_ANLG1, 0x02},
+ {RT5645_IL_CMD3, 0x6728},
+ {RT5645_PR_BASE + 0x3a, 0x0000},
};
static const struct reg_default rt5645_reg[] = {
@@ -157,7 +173,7 @@ static const struct reg_default rt5645_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -253,7 +269,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x2b, 0x5454 },
{ 0x2c, 0xaaa0 },
{ 0x2d, 0x0000 },
- { 0x2f, 0x1002 },
+ { 0x2f, 0x5002 },
{ 0x31, 0x5000 },
{ 0x32, 0x0000 },
{ 0x33, 0x0000 },
@@ -314,7 +330,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -387,17 +403,50 @@ struct rt5645_eq_param_s {
unsigned short val;
};
+struct rt5645_eq_param_s_be16 {
+ __be16 reg;
+ __be16 val;
+};
+
static const char *const rt5645_supply_names[] = {
"avdd",
"cpvdd",
};
+struct rt5645_platform_data {
+ /* IN2 can optionally be differential */
+ bool in2_diff;
+
+ unsigned int dmic1_data_pin;
+ /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
+ unsigned int dmic2_data_pin;
+ /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
+
+ unsigned int jd_mode;
+ /* Use level triggered irq */
+ bool level_trigger_irq;
+ /* Invert JD1_1 status polarity */
+ bool inv_jd1_1;
+ /* Invert HP detect status polarity */
+ bool inv_hp_pol;
+
+ /* Only 1 speaker connected */
+ bool mono_speaker;
+
+ /* Value to assign to snd_soc_card.long_name */
+ const char *long_name;
+
+ /* Some (package) variants have the headset-mic pin not-connected */
+ bool no_headset_mic;
+};
+
struct rt5645_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt5645_platform_data pdata;
struct regmap *regmap;
struct i2c_client *i2c;
struct gpio_desc *gpiod_hp_det;
+ struct gpio_desc *gpiod_cbj_sleeve;
struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack;
struct snd_soc_jack *btn_jack;
@@ -405,6 +454,7 @@ struct rt5645_priv {
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
struct rt5645_eq_param_s *eq_param;
struct timer_list btn_check_timer;
+ struct mutex jd_mutex;
int codec_type;
int sysclk;
@@ -419,12 +469,12 @@ struct rt5645_priv {
int jack_type;
bool en_button_func;
- bool hp_on;
+ int v_id;
};
-static int rt5645_reset(struct snd_soc_codec *codec)
+static int rt5645_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5645_RESET, 0);
+ return snd_soc_component_write(component, RT5645_RESET, 0);
}
static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
@@ -440,6 +490,7 @@ static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case RT5645_RESET:
+ case RT5645_PRIV_INDEX:
case RT5645_PRIV_DATA:
case RT5645_IN1_CTRL1:
case RT5645_IN1_CTRL2:
@@ -656,8 +707,8 @@ static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
- struct rt5645_eq_param_s *eq_param =
- (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ struct rt5645_eq_param_s_be16 *eq_param =
+ (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data;
int i;
for (i = 0; i < RT5645_HWEQ_NUM; i++) {
@@ -670,7 +721,7 @@ static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
static bool rt5645_validate_hweq(unsigned short reg)
{
- if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+ if ((reg >= 0x1a4 && reg <= 0x1cd) || (reg >= 0x1e5 && reg <= 0x1f8) ||
(reg == RT5645_EQ_CTRL2))
return true;
@@ -682,36 +733,33 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
- struct rt5645_eq_param_s *eq_param =
- (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ struct rt5645_eq_param_s_be16 *eq_param =
+ (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data;
int i;
for (i = 0; i < RT5645_HWEQ_NUM; i++) {
- eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
- eq_param[i].val = be16_to_cpu(eq_param[i].val);
+ rt5645->eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+ rt5645->eq_param[i].val = be16_to_cpu(eq_param[i].val);
}
/* The final setting of the table should be RT5645_EQ_CTRL2 */
for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
- if (eq_param[i].reg == 0)
+ if (rt5645->eq_param[i].reg == 0)
continue;
- else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+ else if (rt5645->eq_param[i].reg != RT5645_EQ_CTRL2)
return 0;
else
break;
}
for (i = 0; i < RT5645_HWEQ_NUM; i++) {
- if (!rt5645_validate_hweq(eq_param[i].reg) &&
- eq_param[i].reg != 0)
+ if (!rt5645_validate_hweq(rt5645->eq_param[i].reg) &&
+ rt5645->eq_param[i].reg != 0)
return 0;
- else if (eq_param[i].reg == 0)
+ else if (rt5645->eq_param[i].reg == 0)
break;
}
- memcpy(rt5645->eq_param, eq_param,
- RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
-
return 0;
}
@@ -740,6 +788,14 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
return ret;
}
+static const char * const rt5645_dac1_vol_ctrl_mode_text[] = {
+ "immediately", "zero crossing", "soft ramp"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1_vol_ctrl_mode, RT5645_PR_BASE,
+ RT5645_DA1_ZDET_SFT, rt5645_dac1_vol_ctrl_mode_text);
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -776,7 +832,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
- RT5645_BST_SFT1, 8, 0, bst_tlv),
+ RT5645_BST_SFT1, 12, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
RT5645_BST_SFT2, 8, 0, bst_tlv),
@@ -806,6 +862,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
1, 1),
RT5645_HWEQ("Speaker HWEQ"),
+
+ /* Digital Soft Volume Control */
+ SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode),
};
/**
@@ -819,17 +878,17 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5645->sysclk / rl6231_get_pre_div(rt5645->regmap,
RT5645_ADDA_CLK1, RT5645_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5645_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_DMIC_CTRL1,
RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT);
return idx;
}
@@ -837,10 +896,10 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int val;
- val = snd_soc_read(codec, RT5645_GLB_CLK);
+ val = snd_soc_component_read(component, RT5645_GLB_CLK);
val &= RT5645_SCLK_SRC_MASK;
if (val == RT5645_SCLK_SRC_PLL1)
return 1;
@@ -851,7 +910,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg, shift, val;
switch (source->shift) {
@@ -883,7 +942,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
@@ -896,9 +955,9 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
-static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+static int rt5645_enable_hweq(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < RT5645_HWEQ_NUM; i++) {
@@ -914,7 +973,7 @@ static int rt5645_enable_hweq(struct snd_soc_codec *codec)
/**
* rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -926,7 +985,7 @@ static int rt5645_enable_hweq(struct snd_soc_codec *codec)
* set of filters specified by the mask. And the codec driver will turn on ASRC
* for these filters if ASRC is selected as their clock source.
*/
-int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
unsigned int asrc2_mask = 0;
@@ -982,11 +1041,11 @@ int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5645_ASRC_2,
+ snd_soc_component_update_bits(component, RT5645_ASRC_2,
asrc2_mask, asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5645_ASRC_3,
+ snd_soc_component_update_bits(component, RT5645_ASRC_3,
asrc3_mask, asrc3_value);
return 0;
@@ -1261,30 +1320,6 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_dac_r2_mux =
SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum);
-
-/* INL/R source */
-static const char * const rt5645_inl_src[] = {
- "IN2P", "MonoP"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_inl_enum, RT5645_INL1_INR1_VOL,
- RT5645_INL_SEL_SFT, rt5645_inl_src);
-
-static const struct snd_kcontrol_new rt5645_inl_mux =
- SOC_DAPM_ENUM("INL source", rt5645_inl_enum);
-
-static const char * const rt5645_inr_src[] = {
- "IN2N", "MonoN"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_inr_enum, RT5645_INL1_INR1_VOL,
- RT5645_INR_SEL_SFT, rt5645_inr_src);
-
-static const struct snd_kcontrol_new rt5645_inr_mux =
- SOC_DAPM_ENUM("INR source", rt5645_inr_enum);
-
/* Stereo1 ADC source */
/* MX-27 [12] */
static const char * const rt5645_stereo_adc1_src[] = {
@@ -1584,18 +1619,6 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_if2_adc_in_mux =
SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum);
-/* MX-2F [1:0] */
-static const char * const rt5645_if3_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "VAD_ADC"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_if3_adc_in_enum, RT5645_DIG_INF1_DATA,
- RT5645_IF3_ADC_IN_SFT, rt5645_if3_adc_in_src);
-
-static const struct snd_kcontrol_new rt5645_if3_adc_in_mux =
- SOC_DAPM_ENUM("IF3 ADC IN source", rt5645_if3_adc_in_enum);
-
/* MX-31 [15] [13] [11] [9] */
static const char * const rt5645_pdm_src[] = {
"Mono DAC", "Stereo DAC"
@@ -1651,56 +1674,65 @@ static const struct snd_kcontrol_new pdm1_r_vol_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
RT5645_M_PDM1_R, 1, 1);
-static void hp_amp_power(struct snd_soc_codec *codec, int on)
+static void hp_amp_power(struct snd_soc_component *component, int on)
{
static int hp_amp_power_count;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int i, val;
if (on) {
if (hp_amp_power_count <= 0) {
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x3100);
- snd_soc_write(codec, RT5645_CHARGE_PUMP,
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x3100);
+ snd_soc_component_write(component, RT5645_CHARGE_PUMP,
0x0e06);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_HP_DCC_INT1, 0x9f01);
- msleep(20);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ for (i = 0; i < 20; i++) {
+ usleep_range(1000, 1500);
+ regmap_read(rt5645->regmap, RT5645_PR_BASE +
+ RT5645_HP_DCC_INT1, &val);
+ if (!(val & 0x8000))
+ break;
+ }
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_CO_MASK, RT5645_HP_CO_EN);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
0x3e, 0x7400);
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
+ snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
- msleep(70);
- rt5645->hp_on = true;
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R);
+ msleep(90);
} else {
/* depop parameters */
- snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M2,
RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_HP_DCC_INT1, 0x9f01);
mdelay(150);
/* headphone amp power on */
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5645_PWR_VOL,
+ snd_soc_component_update_bits(component, RT5645_PWR_VOL,
RT5645_PWR_HV_L | RT5645_PWR_HV_R,
RT5645_PWR_HV_L | RT5645_PWR_HV_R);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_HP_L | RT5645_PWR_HP_R |
RT5645_PWR_HA,
RT5645_PWR_HP_L | RT5645_PWR_HP_R |
RT5645_PWR_HA);
mdelay(5);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
RT5645_HP_CO_EN | RT5645_HP_SG_EN);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
@@ -1716,15 +1748,16 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
regmap_write(rt5645->regmap, RT5645_PR_BASE +
0x3e, 0x7400);
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
+ snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737);
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
msleep(100);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001);
-
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R, 0);
} else {
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_SG_MASK |
RT5645_HP_L_SMT_MASK |
RT5645_HP_R_SMT_MASK,
@@ -1732,11 +1765,11 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
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,
+ snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0000);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_HP_L | RT5645_PWR_HP_R |
RT5645_PWR_HA, 0);
- snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M2,
RT5645_DEPOP_MASK, 0);
}
}
@@ -1746,15 +1779,15 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power(codec, 1);
+ hp_amp_power(component, 1);
/* headphone unmute sequence */
if (rt5645->codec_type == CODEC_TYPE_RT5645) {
- snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M3,
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
RT5645_CP_FQ3_MASK,
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
@@ -1762,16 +1795,16 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
(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,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_RSTN_MASK, RT5645_RSTN_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
msleep(40);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
@@ -1781,7 +1814,7 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMD:
/* headphone mute sequence */
if (rt5645->codec_type == CODEC_TYPE_RT5645) {
- snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M3,
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
RT5645_CP_FQ3_MASK,
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
@@ -1789,17 +1822,17 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
(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,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_RSTP_MASK, RT5645_RSTP_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+ snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
msleep(30);
}
- hp_amp_power(codec, 0);
+ hp_amp_power(component, 0);
break;
default:
@@ -1812,25 +1845,25 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- rt5645_enable_hweq(codec);
- snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+ rt5645_enable_hweq(component);
+ snd_soc_component_update_bits(component, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L);
- snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
- snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
- snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+ snd_soc_component_write(component, RT5645_EQ_CTRL2, 0);
+ snd_soc_component_update_bits(component, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L, 0);
break;
@@ -1845,24 +1878,24 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power(codec, 1);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ hp_amp_power(component, 1);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_LM, RT5645_PWR_LM);
- snd_soc_update_bits(codec, RT5645_LOUT1,
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
RT5645_L_MUTE | RT5645_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5645_LOUT1,
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
RT5645_L_MUTE | RT5645_R_MUTE,
RT5645_L_MUTE | RT5645_R_MUTE);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_LM, 0);
- hp_amp_power(codec, 0);
+ hp_amp_power(component, 0);
break;
default:
@@ -1875,16 +1908,16 @@ static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG2,
RT5645_PWR_BST2_P, RT5645_PWR_BST2_P);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG2,
RT5645_PWR_BST2_P, 0);
break;
@@ -1895,18 +1928,47 @@ static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rt5650_hp_event(struct snd_soc_dapm_widget *w,
+static int rt5645_set_micbias1_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);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- if (rt5645->hp_on) {
- msleep(100);
- rt5645->hp_on = false;
- }
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS1_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS1_POW_CTRL_SEL_M);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS1_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS1_POW_CTRL_SEL_A);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_set_micbias2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS2_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS2_POW_CTRL_SEL_M);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL2,
+ RT5645_MICBIAS2_POW_CTRL_SEL_MASK,
+ RT5645_MICBIAS2_POW_CTRL_SEL_A);
break;
default:
@@ -1953,10 +2015,12 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
/* Input Side */
/* micbias */
- SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
- RT5645_PWR_MB1_BIT, 0),
- SND_SOC_DAPM_MICBIAS("micbias2", RT5645_PWR_ANLG2,
- RT5645_PWR_MB2_BIT, 0),
+ SND_SOC_DAPM_SUPPLY("micbias1", RT5645_PWR_ANLG2,
+ RT5645_PWR_MB1_BIT, 0, rt5645_set_micbias1_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("micbias2", RT5645_PWR_ANLG2,
+ RT5645_PWR_MB2_BIT, 0, rt5645_set_micbias2_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC L1"),
SND_SOC_DAPM_INPUT("DMIC R1"),
@@ -2200,7 +2264,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("PDM1R"),
SND_SOC_DAPM_OUTPUT("SPOL"),
SND_SOC_DAPM_OUTPUT("SPOR"),
- SND_SOC_DAPM_POST("DAPM_POST", rt5650_hp_event),
};
static const struct snd_soc_dapm_widget rt5645_specific_dapm_widgets[] = {
@@ -2492,9 +2555,7 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "SPKVOL L", "Switch", "SPK MIXL" },
{ "SPKVOL R", "Switch", "SPK MIXR" },
- { "SPOL MIX", "DAC R1 Switch", "DAC R1" },
{ "SPOL MIX", "DAC L1 Switch", "DAC L1" },
- { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" },
{ "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" },
{ "SPOR MIX", "DAC R1 Switch", "DAC R1" },
{ "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" },
@@ -2683,23 +2744,28 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
{ "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" },
};
+static const struct snd_soc_dapm_route rt5645_old_dapm_routes[] = {
+ { "SPOL MIX", "DAC R1 Switch", "DAC R1" },
+ { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" },
+};
+
static int rt5645_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk, dl_sft;
int pre_div, bclk_ms, frame_size;
rt5645->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ dev_err(component->dev, "Unsupported clock setting\n");
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
@@ -2740,20 +2806,20 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
case RT5645_AIF1:
mask_clk = RT5645_I2S_PD1_MASK;
val_clk = pre_div << RT5645_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5645_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S1_SDP,
(0x3 << dl_sft), (val_len << dl_sft));
- snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5645_ADDA_CLK1, mask_clk, val_clk);
break;
case RT5645_AIF2:
mask_clk = RT5645_I2S_BCLK_MS2_MASK | RT5645_I2S_PD2_MASK;
val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT |
pre_div << RT5645_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5645_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S2_SDP,
(0x3 << dl_sft), (val_len << dl_sft));
- snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5645_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -2762,8 +2828,8 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0, pol_sft;
switch (rt5645->codec_type) {
@@ -2776,10 +2842,10 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5645->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5645_I2S_MS_S;
rt5645->master[dai->id] = 0;
break;
@@ -2814,17 +2880,17 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (dai->id) {
case RT5645_AIF1:
- snd_soc_update_bits(codec, RT5645_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S1_SDP,
RT5645_I2S_MS_MASK | (1 << pol_sft) |
RT5645_I2S_DF_MASK, reg_val);
break;
case RT5645_AIF2:
- snd_soc_update_bits(codec, RT5645_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5645_I2S2_SDP,
RT5645_I2S_MS_MASK | (1 << pol_sft) |
RT5645_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
@@ -2833,8 +2899,8 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5645->sysclk && clk_id == rt5645->sysclk_src)
@@ -2851,10 +2917,10 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5645_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_SCLK_SRC_MASK, reg_val);
rt5645->sysclk = freq;
rt5645->sysclk_src = clk_id;
@@ -2867,8 +2933,8 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -2877,56 +2943,56 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5645->pll_in = 0;
rt5645->pll_out = 0;
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_SCLK_SRC_MASK, RT5645_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5645_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_MCLK);
break;
case RT5645_PLL1_S_BCLK1:
case RT5645_PLL1_S_BCLK2:
switch (dai->id) {
case RT5645_AIF1:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1);
break;
case RT5645_AIF2:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
- snd_soc_write(codec, RT5645_PLL_CTRL1,
+ snd_soc_component_write(component, RT5645_PLL_CTRL1,
pll_code.n_code << RT5645_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5645_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT |
- pll_code.m_bp << RT5645_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5645_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT) |
+ (pll_code.m_bp << RT5645_PLL_M_BP_SFT));
rt5645->pll_in = freq_in;
rt5645->pll_out = freq_out;
@@ -2938,8 +3004,8 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int i_slot_sft, o_slot_sft, i_width_sht, o_width_sht, en_sft;
unsigned int mask, val = 0;
@@ -2962,7 +3028,7 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (rx_mask || tx_mask) {
val |= (1 << en_sft);
if (rt5645->codec_type == CODEC_TYPE_RT5645)
- snd_soc_update_bits(codec, RT5645_BASS_BACK,
+ snd_soc_component_update_bits(component, RT5645_BASS_BACK,
RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
}
@@ -2996,54 +3062,60 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
}
- snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, mask, val);
+ snd_soc_component_update_bits(component, RT5645_TDM_CTRL_1, mask, val);
return 0;
}
-static int rt5645_set_bias_level(struct snd_soc_codec *codec,
+static int rt5645_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2);
mdelay(10);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
}
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ mdelay(10);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- if (rt5645->en_button_func &&
- snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
- queue_delayed_work(system_power_efficient_wq,
- &rt5645->jack_detect_work, msecs_to_jiffies(0));
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
+ msleep(40);
+ if (rt5645->en_button_func)
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work,
+ msecs_to_jiffies(0));
+ }
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
+ snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1100);
if (!rt5645->en_button_func)
- snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, 0);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2 |
RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
@@ -3056,27 +3128,39 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
+static void rt5645_enable_push_button_irq(struct snd_soc_component *component,
bool enable)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
if (enable) {
snd_soc_dapm_force_enable_pin(dapm, "ADC L power");
snd_soc_dapm_force_enable_pin(dapm, "ADC R power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
- snd_soc_update_bits(codec,
- RT5645_INT_IRQ_ST, 0x8, 0x8);
- snd_soc_update_bits(codec,
- RT5650_4BTN_IL_CMD2, 0x8000, 0x8000);
- snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2,
+ RT5645_EN_4BTN_IL_MASK | RT5645_RST_4BTN_IL_MASK,
+ RT5645_EN_4BTN_IL_EN | RT5645_RST_4BTN_IL_RST);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2,
+ RT5645_EN_4BTN_IL_MASK | RT5645_RST_4BTN_IL_MASK,
+ RT5645_EN_4BTN_IL_EN | RT5645_RST_4BTN_IL_NORM);
+ msleep(50);
+ ret = snd_soc_component_read(component, RT5645_INT_IRQ_ST);
+ pr_debug("%s read %x = %x\n", __func__, RT5645_INT_IRQ_ST,
+ snd_soc_component_read(component, RT5645_INT_IRQ_ST));
+ snd_soc_component_write(component, RT5645_INT_IRQ_ST, ret);
+ ret = snd_soc_component_read(component, RT5650_4BTN_IL_CMD1);
pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1,
- snd_soc_read(codec, RT5650_4BTN_IL_CMD1));
+ snd_soc_component_read(component, RT5650_4BTN_IL_CMD1));
+ snd_soc_component_write(component, RT5650_4BTN_IL_CMD1, ret);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
+ snd_soc_component_update_bits(component,
+ RT5645_INT_IRQ_ST, 0x8, 0x8);
} else {
- snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
- snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
+ snd_soc_component_update_bits(component, RT5645_INT_IRQ_ST, 0x8, 0x0);
snd_soc_dapm_disable_pin(dapm, "ADC L power");
snd_soc_dapm_disable_pin(dapm, "ADC R power");
@@ -3084,20 +3168,20 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
}
}
-static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5645_jack_detect(struct snd_soc_component *component, int jack_insert)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (jack_insert) {
- regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006);
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0206);
/* for jack type detect */
snd_soc_dapm_force_enable_pin(dapm, "LDO2");
snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
- if (!dapm->card->instantiated) {
+ if (!snd_soc_card_is_instantiated(component->card)) {
/* Power up necessary bits for JD if dapm is
not ready yet */
regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1,
@@ -3118,24 +3202,33 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2,
RT5645_CBJ_MN_JD, 0);
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 1);
+
msleep(600);
regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val);
val &= 0x7;
- dev_dbg(codec->dev, "val = %d\n", val);
+ dev_dbg(component->dev, "val = %d\n", val);
- if (val == 1 || val == 2) {
+ if ((val == 1 || val == 2) && !rt5645->pdata.no_headset_mic) {
rt5645->jack_type = SND_JACK_HEADSET;
if (rt5645->en_button_func) {
- rt5645_enable_push_button_irq(codec, true);
+ rt5645_enable_push_button_irq(component, true);
}
} else {
+ if (rt5645->en_button_func)
+ rt5645_enable_push_button_irq(component, false);
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
rt5645->jack_type = SND_JACK_HEADPHONE;
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
}
- if (rt5645->pdata.jd_invert)
+ if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
+
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);
} else { /* jack out */
rt5645->jack_type = 0;
@@ -3148,39 +3241,42 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
RT5645_CBJ_BST1_EN, 0);
if (rt5645->en_button_func)
- rt5645_enable_push_button_irq(codec, false);
+ rt5645_enable_push_button_irq(component, false);
if (rt5645->pdata.jd_mode == 0)
snd_soc_dapm_disable_pin(dapm, "LDO2");
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
- if (rt5645->pdata.jd_invert)
+ if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
}
return rt5645->jack_type;
}
-static int rt5645_button_detect(struct snd_soc_codec *codec)
+static int rt5645_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+ val = snd_soc_component_read(component, RT5650_4BTN_IL_CMD1);
pr_debug("val=0x%x\n", val);
btn_type = val & 0xfff0;
- snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val);
+ snd_soc_component_write(component, RT5650_4BTN_IL_CMD1, val);
return btn_type;
}
static irqreturn_t rt5645_irq(int irq, void *data);
-int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+int rt5645_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
rt5645->hp_jack = hp_jack;
rt5645->mic_jack = mic_jack;
@@ -3191,6 +3287,8 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec,
RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
+ regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1,
+ RT5645_HP_CB_MASK, RT5645_HP_CB_PU);
}
rt5645_irq(0, rt5645);
@@ -3198,51 +3296,71 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
+static int rt5645_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct snd_soc_jack *mic_jack = NULL;
+ struct snd_soc_jack *btn_jack = NULL;
+ int type;
+
+ if (hs_jack) {
+ type = *(int *)data;
+
+ if (type & SND_JACK_MICROPHONE)
+ mic_jack = hs_jack;
+ if (type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ btn_jack = hs_jack;
+ }
+
+ return rt5645_set_jack_detect(component, hs_jack, mic_jack, btn_jack);
+}
+
static void rt5645_jack_detect_work(struct work_struct *work)
{
struct rt5645_priv *rt5645 =
container_of(work, struct rt5645_priv, jack_detect_work.work);
int val, btn_type, gpio_state = 0, report = 0;
- if (!rt5645->codec)
+ if (!rt5645->component)
return;
+ mutex_lock(&rt5645->jd_mutex);
+
switch (rt5645->pdata.jd_mode) {
case 0: /* Not using rt5645 JD */
if (rt5645->gpiod_hp_det) {
gpio_state = gpiod_get_value(rt5645->gpiod_hp_det);
- dev_dbg(rt5645->codec->dev, "gpio_state = %d\n",
+ if (rt5645->pdata.inv_hp_pol)
+ gpio_state ^= 1;
+ dev_dbg(rt5645->component->dev, "gpio_state = %d\n",
gpio_state);
- report = rt5645_jack_detect(rt5645->codec, gpio_state);
+ report = rt5645_jack_detect(rt5645->component, gpio_state);
}
snd_soc_jack_report(rt5645->hp_jack,
report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
+ mutex_unlock(&rt5645->jd_mutex);
return;
- case 1: /* 2 port */
- val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070;
+ case 4:
+ val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020;
break;
- default: /* 1 port */
- val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020;
+ default: /* read rt5645 jd1_1 status */
+ val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000;
break;
}
- switch (val) {
- /* jack in */
- case 0x30: /* 2 port */
- case 0x0: /* 1 port or 2 port */
- if (rt5645->jack_type == 0) {
- report = rt5645_jack_detect(rt5645->codec, 1);
- /* for push button and jack out */
- break;
- }
+ if (!val && (rt5645->jack_type == 0)) { /* jack in */
+ report = rt5645_jack_detect(rt5645->component, 1);
+ } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) {
+ /* for push button and jack out */
btn_type = 0;
- if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) {
+ if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) {
/* button pressed */
report = SND_JACK_HEADSET;
- btn_type = rt5645_button_detect(rt5645->codec);
+ btn_type = rt5645_button_detect(rt5645->component);
/* rt5650 can report three kinds of button behavior,
one click, double click and hold. However,
currently we will report button pressed/released
@@ -3272,7 +3390,7 @@ static void rt5645_jack_detect_work(struct work_struct *work)
case 0x0000: /* unpressed */
break;
default:
- dev_err(rt5645->codec->dev,
+ dev_err(rt5645->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -3281,26 +3399,19 @@ static void rt5645_jack_detect_work(struct work_struct *work)
if (btn_type == 0)/* button release */
report = rt5645->jack_type;
else {
- if (rt5645->pdata.jd_invert) {
- mod_timer(&rt5645->btn_check_timer,
- msecs_to_jiffies(100));
- }
+ mod_timer(&rt5645->btn_check_timer,
+ msecs_to_jiffies(100));
}
-
- break;
- /* jack out */
- case 0x70: /* 2 port */
- case 0x10: /* 2 port */
- case 0x20: /* 1 port */
+ } else {
+ /* jack out */
report = 0;
- snd_soc_update_bits(rt5645->codec,
+ snd_soc_component_update_bits(rt5645->component,
RT5645_INT_IRQ_ST, 0x1, 0x0);
- rt5645_jack_detect(rt5645->codec, 0);
- break;
- default:
- break;
+ rt5645_jack_detect(rt5645->component, 0);
}
+ mutex_unlock(&rt5645->jd_mutex);
+
snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE);
if (rt5645->en_button_func)
@@ -3328,20 +3439,21 @@ static irqreturn_t rt5645_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static void rt5645_btn_check_callback(unsigned long data)
+static void rt5645_btn_check_callback(struct timer_list *t)
{
- struct rt5645_priv *rt5645 = (struct rt5645_priv *)data;
+ struct rt5645_priv *rt5645 = timer_container_of(rt5645, t,
+ btn_check_timer);
queue_delayed_work(system_power_efficient_wq,
&rt5645->jack_detect_work, msecs_to_jiffies(5));
}
-static int rt5645_probe(struct snd_soc_codec *codec)
+static int rt5645_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
- rt5645->codec = codec;
+ rt5645->component = component;
switch (rt5645->codec_type) {
case CODEC_TYPE_RT5645:
@@ -3351,6 +3463,11 @@ static int rt5645_probe(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm,
rt5645_specific_dapm_routes,
ARRAY_SIZE(rt5645_specific_dapm_routes));
+ if (rt5645->v_id < 3) {
+ snd_soc_dapm_add_routes(dapm,
+ rt5645_old_dapm_routes,
+ ARRAY_SIZE(rt5645_old_dapm_routes));
+ }
break;
case CODEC_TYPE_RT5650:
snd_soc_dapm_new_controls(dapm,
@@ -3362,7 +3479,7 @@ static int rt5645_probe(struct snd_soc_codec *codec)
break;
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
/* for JD function */
if (rt5645->pdata.jd_mode) {
@@ -3371,22 +3488,28 @@ static int rt5645_probe(struct snd_soc_codec *codec)
snd_soc_dapm_sync(dapm);
}
- rt5645->eq_param = devm_kzalloc(codec->dev,
- RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+ if (rt5645->pdata.long_name)
+ component->card->long_name = rt5645->pdata.long_name;
+
+ rt5645->eq_param = devm_kcalloc(component->dev,
+ RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s),
+ GFP_KERNEL);
+
+ if (!rt5645->eq_param)
+ return -ENOMEM;
return 0;
}
-static int rt5645_remove(struct snd_soc_codec *codec)
+static void rt5645_remove(struct snd_soc_component *component)
{
- rt5645_reset(codec);
- return 0;
+ rt5645_reset(component);
}
#ifdef CONFIG_PM
-static int rt5645_suspend(struct snd_soc_codec *codec)
+static int rt5645_suspend(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5645->regmap, true);
regcache_mark_dirty(rt5645->regmap);
@@ -3394,9 +3517,9 @@ static int rt5645_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5645_resume(struct snd_soc_codec *codec)
+static int rt5645_resume(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5645->regmap, false);
regcache_sync(rt5645->regmap);
@@ -3461,31 +3584,34 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5645 = {
- .probe = rt5645_probe,
- .remove = rt5645_remove,
- .suspend = rt5645_suspend,
- .resume = rt5645_resume,
- .set_bias_level = rt5645_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5645_snd_controls,
- .num_controls = ARRAY_SIZE(rt5645_snd_controls),
- .dapm_widgets = rt5645_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
- .dapm_routes = rt5645_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5645 = {
+ .probe = rt5645_probe,
+ .remove = rt5645_remove,
+ .suspend = rt5645_suspend,
+ .resume = rt5645_resume,
+ .set_bias_level = rt5645_set_bias_level,
+ .controls = rt5645_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5645_snd_controls),
+ .dapm_widgets = rt5645_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
+ .dapm_routes = rt5645_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
+ .set_jack = rt5645_component_set_jack,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5645_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
RT5645_PR_SPACING),
.volatile_reg = rt5645_volatile_register,
.readable_reg = rt5645_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5645_reg,
.num_reg_defaults = ARRAY_SIZE(rt5645_reg),
.ranges = rt5645_ranges,
@@ -3495,13 +3621,14 @@ static const struct regmap_config rt5645_regmap = {
static const struct regmap_config rt5650_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
RT5645_PR_SPACING),
.volatile_reg = rt5645_volatile_register,
.readable_reg = rt5645_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5650_reg,
.num_reg_defaults = ARRAY_SIZE(rt5650_reg),
.ranges = rt5645_ranges,
@@ -3512,62 +3639,288 @@ static const struct regmap_config temp_regmap = {
.name="nocache",
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5645_VENDOR_ID2 + 1,
.cache_type = REGCACHE_NONE,
};
static const struct i2c_device_id rt5645_i2c_id[] = {
- { "rt5645", 0 },
- { "rt5650", 0 },
+ { "rt5645" },
+ { "rt5650" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
+#ifdef CONFIG_OF
+static const struct of_device_id rt5645_of_match[] = {
+ { .compatible = "realtek,rt5645", },
+ { .compatible = "realtek,rt5650", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5645_of_match);
+#endif
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5645_acpi_match[] = {
- { "10EC5645", 0 },
- { "10EC5650", 0 },
- {},
+ { "10EC3270" },
+ { "10EC5640" },
+ { "10EC5645" },
+ { "10EC5648" },
+ { "10EC5650" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
#endif
-static struct rt5645_platform_data general_platform_data = {
+static const struct rt5645_platform_data intel_braswell_platform_data = {
.dmic1_data_pin = RT5645_DMIC1_DISABLE,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
.jd_mode = 3,
};
-static const struct dmi_system_id dmi_platform_intel_braswell[] = {
+static const struct rt5645_platform_data buddy_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 4,
+ .level_trigger_irq = true,
+};
+
+static const struct rt5645_platform_data gpd_win_platform_data = {
+ .jd_mode = 3,
+ .inv_jd1_1 = true,
+ .mono_speaker = true,
+ .long_name = "gpd-win-pocket-rt5645",
+ /* The GPD pocket has a diff. mic, for the win this does not matter. */
+ .in2_diff = true,
+};
+
+static const struct rt5645_platform_data asus_t100ha_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_IN2N,
+ .dmic2_data_pin = RT5645_DMIC2_DISABLE,
+ .jd_mode = 3,
+ .inv_jd1_1 = true,
+};
+
+static const struct rt5645_platform_data asus_t101ha_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_IN2N,
+ .dmic2_data_pin = RT5645_DMIC2_DISABLE,
+ .jd_mode = 3,
+};
+
+static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = {
+ .jd_mode = 3,
+ .in2_diff = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_monospk_platform_data = {
+ .jd_mode = 3,
+ .mono_speaker = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_inv_data = {
+ .jd_mode = 3,
+ .inv_jd1_1 = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_platform_data = {
+ .jd_mode = 3,
+};
+
+static const struct rt5645_platform_data lattepanda_board_platform_data = {
+ .jd_mode = 2,
+ .inv_jd1_1 = true
+};
+
+static const struct rt5645_platform_data kahlee_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+};
+
+static const struct rt5645_platform_data ecs_ef20_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC1_DISABLE,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .inv_hp_pol = 1,
+};
+
+static const struct acpi_gpio_params ef20_hp_detect = { 1, 0, false };
+
+static const struct acpi_gpio_mapping cht_rt5645_ef20_gpios[] = {
+ { "hp-detect-gpios", &ef20_hp_detect, 1 },
+ { },
+};
+
+static int cht_rt5645_ef20_quirk_cb(const struct dmi_system_id *id)
+{
+ cht_rt5645_gpios = cht_rt5645_ef20_gpios;
+ return 1;
+}
+
+static const struct dmi_system_id dmi_platform_data[] = {
+ {
+ .ident = "Chrome Buddy",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+ },
+ .driver_data = (void *)&buddy_platform_data,
+ },
{
.ident = "Intel Strago",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
},
+ .driver_data = (void *)&intel_braswell_platform_data,
},
{
.ident = "Google Chrome",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
},
+ .driver_data = (void *)&intel_braswell_platform_data,
},
- { }
-};
-
-static struct rt5645_platform_data buddy_platform_data = {
- .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
- .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
- .jd_mode = 3,
- .jd_invert = true,
-};
-
-static struct dmi_system_id dmi_platform_intel_broadwell[] = {
{
- .ident = "Chrome Buddy",
+ .ident = "Google Setzer",
.matches = {
- DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
},
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "Microsoft Surface 3",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ /*
+ * Match for the GPDwin which unfortunately uses somewhat
+ * generic dmi strings, which is why we test for 4 strings.
+ * Comparing against 23 other byt/cht boards, board_vendor
+ * and board_name are unique to the GPDwin, where as only one
+ * other board has the same board_serial and 3 others have
+ * the same default product_name. Also the GPDwin is the
+ * only device to have both board_ and product_name not set.
+ */
+ .ident = "GPD Win / Pocket",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Default string"),
+ DMI_MATCH(DMI_BOARD_SERIAL, "Default string"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ },
+ .driver_data = (void *)&gpd_win_platform_data,
+ },
+ {
+ .ident = "ASUS T100HAN",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
+ },
+ .driver_data = (void *)&asus_t100ha_platform_data,
+ },
+ {
+ .ident = "ASUS T101HA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T101HA"),
+ },
+ .driver_data = (void *)&asus_t101ha_platform_data,
+ },
+ {
+ .ident = "MINIX Z83-4",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
+ },
+ .driver_data = (void *)&jd_mode3_platform_data,
+ },
+ {
+ .ident = "Teclast X80 Pro",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X80 Pro"),
+ },
+ .driver_data = (void *)&jd_mode3_monospk_platform_data,
+ },
+ {
+ .ident = "Lenovo Ideapad Miix 310",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
+ },
+ .driver_data = (void *)&lenovo_ideapad_miix_310_pdata,
+ },
+ {
+ .ident = "Lenovo Ideapad Miix 320",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "LattePanda board",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"),
+ /*
+ * Above strings are too generic, LattePanda BIOS versions for
+ * all 4 hw revisions are:
+ * DF-BI-7-S70CR100-*
+ * DF-BI-7-S70CR110-*
+ * DF-BI-7-S70CR200-*
+ * LP-BS-7-S70CR700-*
+ * Do a partial match for S70CR to avoid false positive matches.
+ */
+ DMI_MATCH(DMI_BIOS_VERSION, "S70CR"),
+ },
+ .driver_data = (void *)&lattepanda_board_platform_data,
+ },
+ {
+ .ident = "Chrome Kahlee",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"),
+ },
+ .driver_data = (void *)&kahlee_platform_data,
+ },
+ {
+ .ident = "Medion E1239T",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1239T MD60568"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "EF20",
+ .callback = cht_rt5645_ef20_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "EF20"),
+ },
+ .driver_data = (void *)&ecs_ef20_platform_data,
+ },
+ {
+ .ident = "Acer Switch V 10 (SW5-017)",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "Meegopad T08",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+ DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+ },
+ .driver_data = (void *)&jd_mode3_inv_data,
},
{ }
};
@@ -3575,32 +3928,76 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = {
static bool rt5645_check_dp(struct device *dev)
{
if (device_property_present(dev, "realtek,in2-differential") ||
- device_property_present(dev, "realtek,dmic1-data-pin") ||
- device_property_present(dev, "realtek,dmic2-data-pin") ||
- device_property_present(dev, "realtek,jd-mode"))
+ device_property_present(dev, "realtek,dmic1-data-pin") ||
+ device_property_present(dev, "realtek,dmic2-data-pin") ||
+ device_property_present(dev, "realtek,jd-mode"))
return true;
return false;
}
-static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
+static void rt5645_parse_dt(struct device *dev, struct rt5645_platform_data *pdata)
{
- rt5645->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- device_property_read_u32(dev,
- "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin);
- device_property_read_u32(dev,
- "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin);
- device_property_read_u32(dev,
- "realtek,jd-mode", &rt5645->pdata.jd_mode);
+ pdata->in2_diff = device_property_read_bool(dev, "realtek,in2-differential");
+ device_property_read_u32(dev, "realtek,dmic1-data-pin", &pdata->dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic2-data-pin", &pdata->dmic2_data_pin);
+ device_property_read_u32(dev, "realtek,jd-mode", &pdata->jd_mode);
+}
- return 0;
+static void rt5645_get_pdata(struct device *codec_dev, struct rt5645_platform_data *pdata)
+{
+ const struct dmi_system_id *dmi_data;
+
+ dmi_data = dmi_first_match(dmi_platform_data);
+ if (dmi_data) {
+ dev_info(codec_dev, "Detected %s platform\n", dmi_data->ident);
+ *pdata = *((struct rt5645_platform_data *)dmi_data->driver_data);
+ } else if (rt5645_check_dp(codec_dev)) {
+ rt5645_parse_dt(codec_dev, pdata);
+ } else {
+ *pdata = jd_mode3_platform_data;
+ }
+
+ if (quirk != -1) {
+ pdata->in2_diff = QUIRK_IN2_DIFF(quirk);
+ pdata->level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
+ pdata->inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
+ pdata->inv_hp_pol = QUIRK_INV_HP_POL(quirk);
+ pdata->jd_mode = QUIRK_JD_MODE(quirk);
+ pdata->dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
+ pdata->dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
+ }
}
-static int rt5645_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+const char *rt5645_components(struct device *codec_dev)
+{
+ struct rt5645_platform_data pdata = { };
+ static char buf[32];
+ const char *mic;
+ int spk = 2;
+
+ rt5645_get_pdata(codec_dev, &pdata);
+
+ if (pdata.mono_speaker)
+ spk = 1;
+
+ if (pdata.dmic1_data_pin && pdata.dmic2_data_pin)
+ mic = "dmics12";
+ else if (pdata.dmic1_data_pin)
+ mic = "dmic1";
+ else if (pdata.dmic2_data_pin)
+ mic = "dmic2";
+ else
+ mic = "in2";
+
+ snprintf(buf, sizeof(buf), "cfg-spk:%d cfg-mic:%s", spk, mic);
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(rt5645_components);
+
+static int rt5645_i2c_probe(struct i2c_client *i2c)
{
- struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5645_priv *rt5645;
int ret, i;
unsigned int val;
@@ -3613,22 +4010,41 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
rt5645->i2c = i2c;
i2c_set_clientdata(i2c, rt5645);
+ rt5645_get_pdata(&i2c->dev, &rt5645->pdata);
+
+ if (has_acpi_companion(&i2c->dev)) {
+ if (cht_rt5645_gpios) {
+ if (devm_acpi_dev_add_driver_gpios(&i2c->dev, cht_rt5645_gpios))
+ dev_dbg(&i2c->dev, "Failed to add driver gpios\n");
+ }
- if (pdata)
- rt5645->pdata = *pdata;
- else if (dmi_check_system(dmi_platform_intel_broadwell))
- rt5645->pdata = buddy_platform_data;
- else if (rt5645_check_dp(&i2c->dev))
- rt5645_parse_dt(rt5645, &i2c->dev);
- else if (dmi_check_system(dmi_platform_intel_braswell))
- rt5645->pdata = general_platform_data;
+ /* The ALC3270 package has the headset-mic pin not-connected */
+ if (acpi_dev_hid_uid_match(ACPI_COMPANION(&i2c->dev), "10EC3270", NULL))
+ rt5645->pdata.no_headset_mic = true;
+ }
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
GPIOD_IN);
if (IS_ERR(rt5645->gpiod_hp_det)) {
- dev_err(&i2c->dev, "failed to initialize gpiod\n");
- return PTR_ERR(rt5645->gpiod_hp_det);
+ dev_info(&i2c->dev, "failed to initialize gpiod\n");
+ ret = PTR_ERR(rt5645->gpiod_hp_det);
+ /*
+ * Continue if optional gpiod is missing, bail for all other
+ * errors, including -EPROBE_DEFER
+ */
+ if (ret != -ENOENT)
+ return ret;
+ }
+
+ rt5645->gpiod_cbj_sleeve = devm_gpiod_get_optional(&i2c->dev, "cbj-sleeve",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(rt5645->gpiod_cbj_sleeve)) {
+ ret = PTR_ERR(rt5645->gpiod_cbj_sleeve);
+ dev_info(&i2c->dev, "failed to initialize gpiod, ret=%d\n", ret);
+ if (ret != -ENOENT)
+ return ret;
}
for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
@@ -3654,9 +4070,19 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
ret);
- return ret;
+ goto err_enable;
+ }
+
+ /*
+ * Read after 400msec, as it is the interval required between
+ * read and power On.
+ */
+ msleep(TIME_TO_POWER_MS);
+ ret = regmap_read(regmap, RT5645_VENDOR_ID2, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: 0x%02X\n, ret = %d", RT5645_VENDOR_ID2, ret);
+ goto err_enable;
}
- regmap_read(regmap, RT5645_VENDOR_ID2, &val);
switch (val) {
case RT5645_DEVICE_ID:
@@ -3679,24 +4105,31 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(rt5645->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
regmap_write(rt5645->regmap, RT5645_RESET, 0);
- ret = regmap_register_patch(rt5645->regmap, init_list,
+ regmap_read(regmap, RT5645_VENDOR_ID, &val);
+ rt5645->v_id = val & 0xff;
+
+ regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080);
+
+ ret = regmap_multi_reg_write(rt5645->regmap, init_list,
ARRAY_SIZE(init_list));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- ret = regmap_register_patch(rt5645->regmap, rt5650_init_list,
+ ret = regmap_multi_reg_write(rt5645->regmap, rt5650_init_list,
ARRAY_SIZE(rt5650_init_list));
if (ret != 0)
dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n",
ret);
}
+ regmap_update_bits(rt5645->regmap, RT5645_CLSD_OUT_CTRL, 0xc0, 0xc0);
+
if (rt5645->pdata.in2_diff)
regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
RT5645_IN_DF2, RT5645_IN_DF2);
@@ -3793,6 +4226,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
RT5645_JD1_MODE_1);
break;
case 3:
+ case 4:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_2);
@@ -3800,15 +4234,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
default:
break;
}
+ if (rt5645->pdata.inv_jd1_1) {
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
+ }
}
- if (rt5645->pdata.jd_invert) {
+ regmap_update_bits(rt5645->regmap, RT5645_ADDA_CLK1,
+ RT5645_I2S_PD1_MASK, RT5645_I2S_PD1_2);
+
+ if (rt5645->pdata.level_trigger_irq) {
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
- setup_timer(&rt5645->btn_check_timer,
- rt5645_btn_check_callback, (unsigned long)rt5645);
}
+ timer_setup(&rt5645->btn_check_timer, rt5645_btn_check_callback, 0);
+ mutex_init(&rt5645->jd_mutex);
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
@@ -3817,12 +4258,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5645", rt5645);
if (ret) {
- dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
goto err_enable;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5645,
rt5645_dai, ARRAY_SIZE(rt5645_dai));
if (ret)
goto err_irq;
@@ -3837,20 +4278,26 @@ err_enable:
return ret;
}
-static int rt5645_i2c_remove(struct i2c_client *i2c)
+static void rt5645_i2c_remove(struct i2c_client *i2c)
{
struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c);
if (i2c->irq)
free_irq(i2c->irq, rt5645);
+ /*
+ * Since the rt5645_btn_check_callback() can queue jack_detect_work,
+ * the timer need to be delted first
+ */
+ timer_delete_sync(&rt5645->btn_check_timer);
+
cancel_delayed_work_sync(&rt5645->jack_detect_work);
cancel_delayed_work_sync(&rt5645->rcclock_work);
- snd_soc_unregister_codec(&i2c->dev);
- regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
- return 0;
+ regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
}
static void rt5645_i2c_shutdown(struct i2c_client *i2c)
@@ -3865,12 +4312,48 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
0);
msleep(20);
regmap_write(rt5645->regmap, RT5645_RESET, 0);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
+}
+
+static int rt5645_sys_suspend(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ timer_delete_sync(&rt5645->btn_check_timer);
+ cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
+
+ regcache_cache_only(rt5645->regmap, true);
+ regcache_mark_dirty(rt5645->regmap);
+ return 0;
+}
+
+static int rt5645_sys_resume(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ regcache_cache_only(rt5645->regmap, false);
+ regcache_sync(rt5645->regmap);
+
+ if (rt5645->hp_jack) {
+ rt5645->jack_type = 0;
+ rt5645_jack_detect_work(&rt5645->jack_detect_work.work);
+ }
+ return 0;
}
+static const struct dev_pm_ops rt5645_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume)
+};
+
static struct i2c_driver rt5645_i2c_driver = {
.driver = {
.name = "rt5645",
+ .of_match_table = of_match_ptr(rt5645_of_match),
.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
+ .pm = pm_ptr(&rt5645_pm),
},
.probe = rt5645_i2c_probe,
.remove = rt5645_i2c_remove,
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 205e0715c99a..bef74b29fd54 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -1,19 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5645.h -- RT5645 ALSA SoC audio driver
*
* Copyright 2013 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT5645_H__
#define __RT5645_H__
-#include <sound/rt5645.h>
-
/* Info */
#define RT5645_RESET 0x00
#define RT5645_VENDOR_ID 0xfd
@@ -2016,8 +2011,17 @@
#define RT5645_ZCD_HP_DIS (0x0 << 15)
#define RT5645_ZCD_HP_EN (0x1 << 15)
+/* Buttons Inline Command Function 2 (0xe0) */
+#define RT5645_EN_4BTN_IL_MASK (0x1 << 15)
+#define RT5645_EN_4BTN_IL_EN (0x1 << 15)
+#define RT5645_RST_4BTN_IL_MASK (0x1 << 14)
+#define RT5645_RST_4BTN_IL_RST (0x0 << 14)
+#define RT5645_RST_4BTN_IL_NORM (0x1 << 14)
/* Codec Private Register definition */
+/* DAC ADC Digital Volume (0x00) */
+#define RT5645_DA1_ZDET_SFT 6
+
/* 3D Speaker Control (0x63) */
#define RT5645_3D_SPK_MASK (0x1 << 15)
#define RT5645_3D_SPK_SFT 15
@@ -2114,6 +2118,12 @@ enum {
#define RT5645_RXDC_SRC_STO (0x0 << 7)
#define RT5645_RXDC_SRC_MONO (0x1 << 7)
#define RT5645_RXDC_SRC_SFT (7)
+#define RT5645_MICBIAS1_POW_CTRL_SEL_MASK (0x1 << 5)
+#define RT5645_MICBIAS1_POW_CTRL_SEL_A (0x0 << 5)
+#define RT5645_MICBIAS1_POW_CTRL_SEL_M (0x1 << 5)
+#define RT5645_MICBIAS2_POW_CTRL_SEL_MASK (0x1 << 4)
+#define RT5645_MICBIAS2_POW_CTRL_SEL_A (0x0 << 4)
+#define RT5645_MICBIAS2_POW_CTRL_SEL_M (0x1 << 4)
#define RT5645_RXDP2_SEL_MASK (0x1 << 3)
#define RT5645_RXDP2_SEL_IF2 (0x0 << 3)
#define RT5645_RXDP2_SEL_ADC (0x1 << 3)
@@ -2191,10 +2201,13 @@ enum {
RT5645_AD_MONO_R_FILTER = (0x1 << 5),
};
-int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
-int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+int rt5645_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack);
+
+const char *rt5645_components(struct device *codec_dev);
+
#endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 7a6197042423..9af65a38f0ee 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1,19 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5651.c -- RT5651 ALSA SoC audio codec driver
*
* Copyright 2014 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
@@ -26,6 +23,7 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
+#include <sound/jack.h>
#include "rl6231.h"
#include "rt5651.h"
@@ -287,9 +285,9 @@ static bool rt5651_readable_register(struct device *dev, unsigned int reg)
}
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
@@ -330,11 +328,13 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL,
RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
175, 0, dac_vol_tlv),
- /* IN1/IN2 Control */
+ /* IN1/IN2/IN3 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2,
RT5651_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2,
RT5651_BST_SFT2, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost", RT5651_IN3,
+ RT5651_BST_SFT1, 8, 0, bst_tlv),
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL,
RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT,
@@ -376,36 +376,22 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5651->sysclk / rl6231_get_pre_div(rt5651->regmap,
RT5651_ADDA_CLK1, RT5651_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
idx << RT5651_DMIC_CLK_SFT);
return idx;
}
-static int is_sysclk_from_pll(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
-
- val = snd_soc_read(codec, RT5651_GLB_CLK);
- val &= RT5651_SCLK_SRC_MASK;
- if (val == RT5651_SCLK_SRC_PLL1)
- return 1;
- else
- return 0;
-}
-
/* Digital Mixer */
static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
@@ -586,44 +572,6 @@ static const struct snd_kcontrol_new hpo_r_mute_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
RT5651_R_MUTE_SFT, 1, 1);
-/* INL/R source */
-static const char * const rt5651_inl_src[] = {"IN2P", "HPOVOLLP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inl_enum, RT5651_INL1_INR1_VOL,
- RT5651_INL_SEL_SFT, rt5651_inl_src);
-
-static const struct snd_kcontrol_new rt5651_inl1_mux =
- SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum);
-
-static const char * const rt5651_inr1_src[] = {"IN2N", "HPOVOLRP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inr1_enum, RT5651_INL1_INR1_VOL,
- RT5651_INR_SEL_SFT, rt5651_inr1_src);
-
-static const struct snd_kcontrol_new rt5651_inr1_mux =
- SOC_DAPM_ENUM("INR1 source", rt5651_inr1_enum);
-
-static const char * const rt5651_inl2_src[] = {"IN3P", "OUTVOLLP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inl2_enum, RT5651_INL2_INR2_VOL,
- RT5651_INL_SEL_SFT, rt5651_inl2_src);
-
-static const struct snd_kcontrol_new rt5651_inl2_mux =
- SOC_DAPM_ENUM("INL2 source", rt5651_inl2_enum);
-
-static const char * const rt5651_inr2_src[] = {"IN3N", "OUTVOLRP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inr2_enum, RT5651_INL2_INR2_VOL,
- RT5651_INR_SEL_SFT, rt5651_inr2_src);
-
-static const struct snd_kcontrol_new rt5651_inr2_mux =
- SOC_DAPM_ENUM("INR2 source", rt5651_inr2_enum);
-
-
/* Stereo ADC source */
static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"};
@@ -733,8 +681,8 @@ static const struct snd_kcontrol_new rt5651_pdm_r_mux =
static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -771,8 +719,8 @@ static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -797,11 +745,11 @@ static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
RT5651_HP_CP_PD | RT5651_HP_SG_EN);
regmap_update_bits(rt5651->regmap, RT5651_PR_BASE +
RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400);
- rt5651->hp_mute = 0;
+ rt5651->hp_mute = false;
break;
case SND_SOC_DAPM_PRE_PMD:
- rt5651->hp_mute = 1;
+ rt5651->hp_mute = true;
usleep_range(70000, 75000);
break;
@@ -816,8 +764,8 @@ static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -836,16 +784,16 @@ static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST1_OP2, RT5651_PWR_BST1_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST1_OP2, 0);
break;
@@ -859,16 +807,16 @@ static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST2_OP2, RT5651_PWR_BST2_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST2_OP2, 0);
break;
@@ -882,16 +830,16 @@ static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst3_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST3_OP2, RT5651_PWR_BST3_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST3_OP2, 0);
break;
@@ -915,14 +863,11 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2,
11, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2,
- RT5651_PWR_PLL_BIT, 0, NULL, 0),
- /* Input Side */
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1,
RT5651_PWR_LDO_BIT, 0, NULL, 0),
- SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2,
- RT5651_PWR_MB1_BIT, 0),
+ SND_SOC_DAPM_SUPPLY("micbias1", RT5651_PWR_ANLG2,
+ RT5651_PWR_MB1_BIT, 0, NULL, 0),
/* Input Lines */
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
@@ -955,11 +900,7 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
RT5651_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL,
RT5651_PWR_IN2_R_BIT, 0, NULL, 0),
- /* IN Mux */
- SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux),
- SND_SOC_DAPM_MUX("INR1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr1_mux),
- SND_SOC_DAPM_MUX("INL2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl2_mux),
- SND_SOC_DAPM_MUX("INR2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr2_mux),
+
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIXL", RT5651_PWR_MIXER, RT5651_PWR_RM_L_BIT, 0,
rt5651_rec_l_mix, ARRAY_SIZE(rt5651_rec_l_mix)),
@@ -1200,7 +1141,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
{"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
{"Stereo1 ADC MIXL", NULL, "Stereo1 Filter"},
- {"Stereo1 Filter", NULL, "PLL1", is_sysclk_from_pll},
{"Stereo1 Filter", NULL, "ADC ASRC"},
{"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
@@ -1210,7 +1150,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"},
{"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"},
{"Stereo2 ADC MIXL", NULL, "Stereo2 Filter"},
- {"Stereo2 Filter", NULL, "PLL1", is_sysclk_from_pll},
{"Stereo2 Filter", NULL, "ADC ASRC"},
{"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"},
@@ -1277,10 +1216,8 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"PDM R Mux", "DD MIX", "DAC MIXR"},
{"DAC L1", NULL, "Stereo DAC MIXL"},
- {"DAC L1", NULL, "PLL1", is_sysclk_from_pll},
{"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
- {"DAC R1", NULL, "PLL1", is_sysclk_from_pll},
{"DAC R1", NULL, "DAC R1 Power"},
{"DD MIXL", "DAC L1 Switch", "DAC MIXL"},
@@ -1344,8 +1281,8 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
static int rt5651_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
@@ -1353,12 +1290,12 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ dev_err(component->dev, "Unsupported clock setting\n");
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32 ? 1 : 0;
@@ -1389,19 +1326,19 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
case RT5651_AIF1:
mask_clk = RT5651_I2S_PD1_MASK;
val_clk = pre_div << RT5651_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S1_SDP,
RT5651_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk);
break;
case RT5651_AIF2:
mask_clk = RT5651_I2S_BCLK_MS2_MASK | RT5651_I2S_PD2_MASK;
val_clk = pre_div << RT5651_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5651_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S2_SDP,
RT5651_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Wrong dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -1410,15 +1347,15 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5651->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5651_I2S_MS_S;
rt5651->master[dai->id] = 0;
break;
@@ -1454,17 +1391,17 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5651_AIF1:
- snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S1_SDP,
RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK |
RT5651_I2S_DF_MASK, reg_val);
break;
case RT5651_AIF2:
- snd_soc_update_bits(codec, RT5651_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S2_SDP,
RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK |
RT5651_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Wrong dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
@@ -1473,9 +1410,10 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
+ unsigned int pll_bit = 0;
if (freq == rt5651->sysclk && clk_id == rt5651->sysclk_src)
return 0;
@@ -1486,15 +1424,18 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
break;
case RT5651_SCLK_S_PLL1:
reg_val |= RT5651_SCLK_SRC_PLL1;
+ pll_bit |= RT5651_PWR_PLL;
break;
case RT5651_SCLK_S_RCCLK:
reg_val |= RT5651_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+ RT5651_PWR_PLL, pll_bit);
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_SCLK_SRC_MASK, reg_val);
rt5651->sysclk = freq;
rt5651->sysclk_src = clk_id;
@@ -1507,8 +1448,8 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -1517,48 +1458,48 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5651->pll_in = 0;
rt5651->pll_out = 0;
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5651_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_MCLK);
break;
case RT5651_PLL1_S_BCLK1:
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK1);
break;
case RT5651_PLL1_S_BCLK2:
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
- snd_soc_write(codec, RT5651_PLL_CTRL1,
+ snd_soc_component_write(component, RT5651_PLL_CTRL1,
pll_code.n_code << RT5651_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5651_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT |
- pll_code.m_bp << RT5651_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5651_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT) |
+ (pll_code.m_bp << RT5651_PLL_M_BP_SFT));
rt5651->pll_in = freq_in;
rt5651->pll_out = freq_out;
@@ -1567,39 +1508,46 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5651_set_bias_level(struct snd_soc_codec *codec,
+static int rt5651_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_dapm_get_bias_level(dapm)) {
+ if (snd_soc_component_read(component, RT5651_PLL_MODE_1) & 0x9200)
+ snd_soc_component_update_bits(component, RT5651_D_MISC,
+ 0xc00, 0xc00);
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (SND_SOC_BIAS_OFF == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
RT5651_PWR_VREF1 | RT5651_PWR_MB |
RT5651_PWR_BG | RT5651_PWR_VREF2,
RT5651_PWR_VREF1 | RT5651_PWR_MB |
RT5651_PWR_BG | RT5651_PWR_VREF2);
usleep_range(10000, 15000);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
RT5651_PWR_FV1 | RT5651_PWR_FV2,
RT5651_PWR_FV1 | RT5651_PWR_FV2);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_LDO_DVO_MASK,
- RT5651_PWR_LDO_DVO_1_2V);
- snd_soc_update_bits(codec, RT5651_D_MISC, 0x1, 0x1);
- if (snd_soc_read(codec, RT5651_PLL_MODE_1) & 0x9200)
- snd_soc_update_bits(codec, RT5651_D_MISC,
- 0xc00, 0xc00);
+ snd_soc_component_update_bits(component, RT5651_D_MISC, 0x1, 0x1);
}
break;
- case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec, RT5651_D_MISC, 0x0010);
- snd_soc_write(codec, RT5651_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5651_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000);
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_write(component, RT5651_D_MISC, 0x0010);
+ snd_soc_component_write(component, RT5651_PWR_DIG1, 0x0000);
+ snd_soc_component_write(component, RT5651_PWR_DIG2, 0x0000);
+ snd_soc_component_write(component, RT5651_PWR_VOL, 0x0000);
+ snd_soc_component_write(component, RT5651_PWR_MIXER, 0x0000);
+ /* Do not touch the LDO voltage select bits on bias-off */
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
+ ~RT5651_PWR_LDO_DVO_MASK, 0);
+ /* Leave PLL1 and jack-detect power as is, all others off */
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+ ~(RT5651_PWR_PLL | RT5651_PWR_JD_M), 0);
break;
default:
@@ -1609,43 +1557,539 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5651_probe(struct snd_soc_codec *codec)
+static void rt5651_enable_micbias1_for_ovcd(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "micbias1");
+ /* OVCD is unreliable when used with RCCLK as sysclk-source */
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void rt5651_disable_micbias1_for_ovcd(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "micbias1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "LDO");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void rt5651_enable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_NOR);
+ rt5651->ovcd_irq_enabled = true;
+}
+
+static void rt5651_disable_micbias1_ovcd_irq(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_BP);
+ rt5651->ovcd_irq_enabled = false;
+}
+
+static void rt5651_clear_micbias1_ovcd(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_MB1_OC_CLR, 0);
+}
+
+static bool rt5651_micbias1_ovcd(struct snd_soc_component *component)
+{
+ int val;
+
+ val = snd_soc_component_read(component, RT5651_IRQ_CTRL2);
+ dev_dbg(component->dev, "irq ctrl2 %#04x\n", val);
+
+ return (val & RT5651_MB1_OC_CLR);
+}
+
+static bool rt5651_jack_inserted(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ int val;
+
+ if (rt5651->gpiod_hp_det) {
+ val = gpiod_get_value_cansleep(rt5651->gpiod_hp_det);
+ dev_dbg(component->dev, "jack-detect gpio %d\n", val);
+ return val;
+ }
+
+ val = snd_soc_component_read(component, RT5651_INT_IRQ_ST);
+ dev_dbg(component->dev, "irq status %#04x\n", val);
+
+ switch (rt5651->jd_src) {
+ case RT5651_JD1_1:
+ val &= 0x1000;
+ break;
+ case RT5651_JD1_2:
+ val &= 0x2000;
+ break;
+ case RT5651_JD2:
+ val &= 0x4000;
+ break;
+ default:
+ break;
+ }
+
+ if (rt5651->jd_active_high)
+ return val != 0;
+ else
+ return val == 0;
+}
+
+/* Jack detect and button-press timings */
+#define JACK_SETTLE_TIME 100 /* milli seconds */
+#define JACK_DETECT_COUNT 5
+#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */
+#define JACK_UNPLUG_TIME 80 /* milli seconds */
+#define BP_POLL_TIME 10 /* milli seconds */
+#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */
+#define BP_THRESHOLD 3
+
+static void rt5651_start_button_press_work(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ rt5651->poll_count = 0;
+ rt5651->press_count = 0;
+ rt5651->release_count = 0;
+ rt5651->pressed = false;
+ rt5651->press_reported = false;
+ rt5651_clear_micbias1_ovcd(component);
+ schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+static void rt5651_button_press_work(struct work_struct *work)
+{
+ struct rt5651_priv *rt5651 =
+ container_of(work, struct rt5651_priv, bp_work.work);
+ struct snd_soc_component *component = rt5651->component;
+
+ /* Check the jack was not removed underneath us */
+ if (!rt5651_jack_inserted(component))
+ return;
+
+ if (rt5651_micbias1_ovcd(component)) {
+ rt5651->release_count = 0;
+ rt5651->press_count++;
+ /* Remember till after JACK_UNPLUG_TIME wait */
+ if (rt5651->press_count >= BP_THRESHOLD)
+ rt5651->pressed = true;
+ rt5651_clear_micbias1_ovcd(component);
+ } else {
+ rt5651->press_count = 0;
+ rt5651->release_count++;
+ }
+
+ /*
+ * The pins get temporarily shorted on jack unplug, so we poll for
+ * at least JACK_UNPLUG_TIME milli-seconds before reporting a press.
+ */
+ rt5651->poll_count++;
+ if (rt5651->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) {
+ schedule_delayed_work(&rt5651->bp_work,
+ msecs_to_jiffies(BP_POLL_TIME));
+ return;
+ }
+
+ if (rt5651->pressed && !rt5651->press_reported) {
+ dev_dbg(component->dev, "headset button press\n");
+ snd_soc_jack_report(rt5651->hp_jack, SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ rt5651->press_reported = true;
+ }
+
+ if (rt5651->release_count >= BP_THRESHOLD) {
+ if (rt5651->press_reported) {
+ dev_dbg(component->dev, "headset button release\n");
+ snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0);
+ }
+ /* Re-enable OVCD IRQ to detect next press */
+ rt5651_enable_micbias1_ovcd_irq(component);
+ return; /* Stop polling */
+ }
+
+ schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME));
+}
+
+static int rt5651_detect_headset(struct snd_soc_component *component)
+{
+ int i, headset_count = 0, headphone_count = 0;
+
+ /*
+ * We get the insertion event before the jack is fully inserted at which
+ * point the second ring on a TRRS connector may short the 2nd ring and
+ * sleeve contacts, also the overcurrent detection is not entirely
+ * reliable. So we try several times with a wait in between until we
+ * detect the same type JACK_DETECT_COUNT times in a row.
+ */
+ for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) {
+ /* Clear any previous over-current status flag */
+ rt5651_clear_micbias1_ovcd(component);
+
+ msleep(JACK_SETTLE_TIME);
+
+ /* Check the jack is still connected before checking ovcd */
+ if (!rt5651_jack_inserted(component))
+ return 0;
+
+ if (rt5651_micbias1_ovcd(component)) {
+ /*
+ * Over current detected, there is a short between the
+ * 2nd ring contact and the ground, so a TRS connector
+ * without a mic contact and thus plain headphones.
+ */
+ dev_dbg(component->dev, "mic-gnd shorted\n");
+ headset_count = 0;
+ headphone_count++;
+ if (headphone_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADPHONE;
+ } else {
+ dev_dbg(component->dev, "mic-gnd open\n");
+ headphone_count = 0;
+ headset_count++;
+ if (headset_count == JACK_DETECT_COUNT)
+ return SND_JACK_HEADSET;
+ }
+ }
+
+ dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n");
+ return SND_JACK_HEADPHONE;
+}
+
+static bool rt5651_support_button_press(struct rt5651_priv *rt5651)
+{
+ if (!rt5651->hp_jack)
+ return false;
+
+ /* Button press support only works with internal jack-detection */
+ return (rt5651->hp_jack->status & SND_JACK_MICROPHONE) &&
+ rt5651->gpiod_hp_det == NULL;
+}
+
+static void rt5651_jack_detect_work(struct work_struct *work)
+{
+ struct rt5651_priv *rt5651 =
+ container_of(work, struct rt5651_priv, jack_detect_work);
+ struct snd_soc_component *component = rt5651->component;
+ int report;
+
+ if (!rt5651_jack_inserted(component)) {
+ /* Jack removed, or spurious IRQ? */
+ if (rt5651->hp_jack->status & SND_JACK_HEADPHONE) {
+ if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) {
+ cancel_delayed_work_sync(&rt5651->bp_work);
+ rt5651_disable_micbias1_ovcd_irq(component);
+ rt5651_disable_micbias1_for_ovcd(component);
+ }
+ snd_soc_jack_report(rt5651->hp_jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ dev_dbg(component->dev, "jack unplugged\n");
+ }
+ } else if (!(rt5651->hp_jack->status & SND_JACK_HEADPHONE)) {
+ /* Jack inserted */
+ WARN_ON(rt5651->ovcd_irq_enabled);
+ rt5651_enable_micbias1_for_ovcd(component);
+ report = rt5651_detect_headset(component);
+ dev_dbg(component->dev, "detect report %#02x\n", report);
+ snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET);
+ if (rt5651_support_button_press(rt5651)) {
+ /* Enable ovcd IRQ for button press detect. */
+ rt5651_enable_micbias1_ovcd_irq(component);
+ } else {
+ /* No more need for overcurrent detect. */
+ rt5651_disable_micbias1_for_ovcd(component);
+ }
+ } else if (rt5651->ovcd_irq_enabled && rt5651_micbias1_ovcd(component)) {
+ dev_dbg(component->dev, "OVCD IRQ\n");
+
+ /*
+ * The ovcd IRQ keeps firing while the button is pressed, so
+ * we disable it and start polling the button until released.
+ *
+ * The disable will make the IRQ pin 0 again and since we get
+ * IRQs on both edges (so as to detect both jack plugin and
+ * unplug) this means we will immediately get another IRQ.
+ * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP.
+ */
+ rt5651_disable_micbias1_ovcd_irq(component);
+ rt5651_start_button_press_work(component);
+
+ /*
+ * If the jack-detect IRQ flag goes high (unplug) after our
+ * above rt5651_jack_inserted() check and before we have
+ * disabled the OVCD IRQ, the IRQ pin will stay high and as
+ * we react to edges, we miss the unplug event -> recheck.
+ */
+ queue_work(system_long_wq, &rt5651->jack_detect_work);
+ }
+}
+
+static irqreturn_t rt5651_irq(int irq, void *data)
+{
+ struct rt5651_priv *rt5651 = data;
+
+ queue_work(system_power_efficient_wq, &rt5651->jack_detect_work);
+
+ return IRQ_HANDLED;
+}
+
+static void rt5651_cancel_work(void *data)
+{
+ struct rt5651_priv *rt5651 = data;
+
+ cancel_work_sync(&rt5651->jack_detect_work);
+ cancel_delayed_work_sync(&rt5651->bp_work);
+}
+
+static void rt5651_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hp_jack,
+ struct gpio_desc *gpiod_hp_det)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ bool using_internal_jack_detect = true;
+
+ /* Select jack detect source */
+ switch (rt5651->jd_src) {
+ case RT5651_JD_NULL:
+ rt5651->gpiod_hp_det = gpiod_hp_det;
+ if (!rt5651->gpiod_hp_det)
+ return; /* No jack detect */
+ using_internal_jack_detect = false;
+ break;
+ case RT5651_JD1_1:
+ snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
+ RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
+ RT5651_JD1_1_IRQ_EN);
+ break;
+ case RT5651_JD1_2:
+ snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
+ RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_2);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
+ RT5651_JD1_2_IRQ_EN);
+ break;
+ case RT5651_JD2:
+ snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
+ RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD2);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
+ RT5651_JD2_IRQ_EN);
+ break;
+ default:
+ dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
+ return;
+ }
+
+ if (using_internal_jack_detect) {
+ /* IRQ output on GPIO1 */
+ snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1,
+ RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ);
+
+ /* Enable jack detect power */
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
+ RT5651_PWR_JD_M, RT5651_PWR_JD_M);
+ }
+
+ /* Set OVCD threshold current and scale-factor */
+ snd_soc_component_write(component, RT5651_PR_BASE + RT5651_BIAS_CUR4,
+ 0xa800 | rt5651->ovcd_sf);
+
+ snd_soc_component_update_bits(component, RT5651_MICBIAS,
+ RT5651_MIC1_OVCD_MASK |
+ RT5651_MIC1_OVTH_MASK |
+ RT5651_PWR_CLK12M_MASK |
+ RT5651_PWR_MB_MASK,
+ RT5651_MIC1_OVCD_EN |
+ rt5651->ovcd_th |
+ RT5651_PWR_MB_PU |
+ RT5651_PWR_CLK12M_PU);
+
+ /*
+ * The over-current-detect is only reliable in detecting the absence
+ * of over-current, when the mic-contact in the jack is short-circuited,
+ * the hardware periodically retries if it can apply the bias-current
+ * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about
+ * 10% of the time, as we poll the ovcd status bit we might hit that
+ * 10%, so we enable sticky mode and when checking OVCD we clear the
+ * status, msleep() a bit and then check to get a reliable reading.
+ */
+ snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2,
+ RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN);
+
+ rt5651->hp_jack = hp_jack;
+ if (rt5651_support_button_press(rt5651)) {
+ rt5651_enable_micbias1_for_ovcd(component);
+ rt5651_enable_micbias1_ovcd_irq(component);
+ }
+
+ enable_irq(rt5651->irq);
+ /* sync initial jack state */
+ queue_work(system_power_efficient_wq, &rt5651->jack_detect_work);
+}
+
+static void rt5651_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+
+ disable_irq(rt5651->irq);
+ rt5651_cancel_work(rt5651);
+
+ if (rt5651_support_button_press(rt5651)) {
+ rt5651_disable_micbias1_ovcd_irq(component);
+ rt5651_disable_micbias1_for_ovcd(component);
+ snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0);
+ }
+
+ rt5651->hp_jack = NULL;
+}
+
+static int rt5651_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ rt5651_enable_jack_detect(component, jack, data);
+ else
+ rt5651_disable_jack_detect(component);
+
+ return 0;
+}
+
+/*
+ * Note on some platforms the platform code may need to add device-properties,
+ * rather then relying only on properties set by the firmware. Therefor the
+ * property parsing MUST be done from the component driver's probe function,
+ * rather then from the i2c driver's probe function, so that the platform-code
+ * can attach extra properties before calling snd_soc_register_card().
+ */
+static void rt5651_apply_properties(struct snd_soc_component *component)
+{
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ u32 val;
+
+ if (device_property_read_bool(component->dev, "realtek,in2-differential"))
+ snd_soc_component_update_bits(component, RT5651_IN1_IN2,
+ RT5651_IN_DF2, RT5651_IN_DF2);
+
+ if (device_property_read_bool(component->dev, "realtek,dmic-en"))
+ snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1,
+ RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
+
+ if (device_property_read_u32(component->dev,
+ "realtek,jack-detect-source", &val) == 0)
+ rt5651->jd_src = val;
+
+ if (device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
+ rt5651->jd_active_high = true;
+
+ /*
+ * Testing on various boards has shown that good defaults for the OVCD
+ * threshold and scale-factor are 2000µA and 0.75. For an effective
+ * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0.
+ */
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_2000UA;
+ rt5651->ovcd_sf = RT5651_MIC_OVCD_SF_0P75;
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-threshold-microamp", &val) == 0) {
+ switch (val) {
+ case 600:
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_600UA;
+ break;
+ case 1500:
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_1500UA;
+ break;
+ case 2000:
+ rt5651->ovcd_th = RT5651_MIC1_OVTH_2000UA;
+ break;
+ default:
+ dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n",
+ val);
+ }
+ }
+
+ if (device_property_read_u32(component->dev,
+ "realtek,over-current-scale-factor", &val) == 0) {
+ if (val <= RT5651_OVCD_SF_1P5)
+ rt5651->ovcd_sf = val << RT5651_MIC_OVCD_SF_SFT;
+ else
+ dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n",
+ val);
+ }
+}
+
+static int rt5651_probe(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- rt5651->codec = codec;
+ rt5651->component = component;
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_VREF1 | RT5651_PWR_MB |
- RT5651_PWR_BG | RT5651_PWR_VREF2,
- RT5651_PWR_VREF1 | RT5651_PWR_MB |
- RT5651_PWR_BG | RT5651_PWR_VREF2);
- usleep_range(10000, 15000);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_FV1 | RT5651_PWR_FV2,
- RT5651_PWR_FV1 | RT5651_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
+ RT5651_PWR_LDO_DVO_MASK, RT5651_PWR_LDO_DVO_1_2V);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+
+ rt5651_apply_properties(component);
return 0;
}
#ifdef CONFIG_PM
-static int rt5651_suspend(struct snd_soc_codec *codec)
+static int rt5651_suspend(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5651->regmap, true);
regcache_mark_dirty(rt5651->regmap);
return 0;
}
-static int rt5651_resume(struct snd_soc_codec *codec)
+static int rt5651_resume(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5651->regmap, false);
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -1706,18 +2150,20 @@ static struct snd_soc_dai_driver rt5651_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
- .probe = rt5651_probe,
- .suspend = rt5651_suspend,
- .resume = rt5651_resume,
- .set_bias_level = rt5651_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5651_snd_controls,
- .num_controls = ARRAY_SIZE(rt5651_snd_controls),
- .dapm_widgets = rt5651_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets),
- .dapm_routes = rt5651_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5651 = {
+ .probe = rt5651_probe,
+ .suspend = rt5651_suspend,
+ .resume = rt5651_resume,
+ .set_bias_level = rt5651_set_bias_level,
+ .set_jack = rt5651_set_jack,
+ .controls = rt5651_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5651_snd_controls),
+ .dapm_widgets = rt5651_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets),
+ .dapm_routes = rt5651_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5651_regmap = {
@@ -1729,51 +2175,47 @@ static const struct regmap_config rt5651_regmap = {
.volatile_reg = rt5651_volatile_register,
.readable_reg = rt5651_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5651_reg,
.num_reg_defaults = ARRAY_SIZE(rt5651_reg),
.ranges = rt5651_ranges,
.num_ranges = ARRAY_SIZE(rt5651_ranges),
+ .use_single_read = true,
+ .use_single_write = true,
};
#if defined(CONFIG_OF)
static const struct of_device_id rt5651_of_match[] = {
{ .compatible = "realtek,rt5651", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rt5651_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5651_acpi_match[] = {
- { "10EC5651", 0 },
- { },
+ { "10EC5640" },
+ { "10EC5651" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match);
#endif
static const struct i2c_device_id rt5651_i2c_id[] = {
- { "rt5651", 0 },
+ { "rt5651" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
-static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np)
-{
- rt5651->pdata.in2_diff = of_property_read_bool(np,
- "realtek,in2-differential");
- rt5651->pdata.dmic_en = of_property_read_bool(np,
- "realtek,dmic-en");
-
- return 0;
-}
-
-static int rt5651_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+/*
+ * Note this function MUST not look at device-properties, see the comment
+ * above rt5651_apply_properties().
+ */
+static int rt5651_i2c_probe(struct i2c_client *i2c)
{
- struct rt5651_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5651_priv *rt5651;
int ret;
+ int err;
rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651),
GFP_KERNEL);
@@ -1782,11 +2224,6 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5651);
- if (pdata)
- rt5651->pdata = *pdata;
- else if (i2c->dev.of_node)
- rt5651_parse_dt(rt5651, i2c->dev.of_node);
-
rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
if (IS_ERR(rt5651->regmap)) {
ret = PTR_ERR(rt5651->regmap);
@@ -1795,7 +2232,10 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
+ err = regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
+ if (err)
+ return err;
+
if (ret != RT5651_DEVICE_ID_VALUE) {
dev_err(&i2c->dev,
"Device with ID register %#x is not rt5651\n", ret);
@@ -1809,29 +2249,33 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- if (rt5651->pdata.in2_diff)
- regmap_update_bits(rt5651->regmap, RT5651_IN1_IN2,
- RT5651_IN_DF2, RT5651_IN_DF2);
+ rt5651->irq = i2c->irq;
+ rt5651->hp_mute = true;
- if (rt5651->pdata.dmic_en)
- regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1,
- RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
+ INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work);
+ INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work);
- rt5651->hp_mute = 1;
+ /* Make sure work is stopped on probe-error / remove */
+ ret = devm_add_action_or_reset(&i2c->dev, rt5651_cancel_work, rt5651);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651);
+ if (ret) {
+ dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n",
+ rt5651->irq, ret);
+ rt5651->irq = -ENXIO;
+ }
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5651,
rt5651_dai, ARRAY_SIZE(rt5651_dai));
return ret;
}
-static int rt5651_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5651_i2c_driver = {
.driver = {
.name = "rt5651",
@@ -1839,7 +2283,6 @@ static struct i2c_driver rt5651_i2c_driver = {
.of_match_table = of_match_ptr(rt5651_of_match),
},
.probe = rt5651_i2c_probe,
- .remove = rt5651_i2c_remove,
.id_table = rt5651_i2c_id,
};
module_i2c_driver(rt5651_i2c_driver);
diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h
index 1bd33cfa6411..20c33a3ece37 100644
--- a/sound/soc/codecs/rt5651.h
+++ b/sound/soc/codecs/rt5651.h
@@ -1,18 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5651.h -- RT5651 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT5651_H__
#define __RT5651_H__
-#include <sound/rt5651.h>
+#include <dt-bindings/sound/rt5651.h>
/* Info */
#define RT5651_RESET 0x00
@@ -138,6 +135,7 @@
/* Index of Codec Private Register definition */
#define RT5651_BIAS_CUR1 0x12
#define RT5651_BIAS_CUR3 0x14
+#define RT5651_BIAS_CUR4 0x15
#define RT5651_CLSD_INT_REG1 0x1c
#define RT5651_CHPUMP_INT_REG1 0x24
#define RT5651_MAMP_INT_REG2 0x37
@@ -1966,6 +1964,15 @@
#define RT5651_D_GATE_EN_SFT 0
/* Codec Private Register definition */
+
+/* MIC Over current threshold scale factor (0x15) */
+#define RT5651_MIC_OVCD_SF_MASK (0x3 << 8)
+#define RT5651_MIC_OVCD_SF_SFT 8
+#define RT5651_MIC_OVCD_SF_0P5 (0x0 << 8)
+#define RT5651_MIC_OVCD_SF_0P75 (0x1 << 8)
+#define RT5651_MIC_OVCD_SF_1P0 (0x2 << 8)
+#define RT5651_MIC_OVCD_SF_1P5 (0x3 << 8)
+
/* 3D Speaker Control (0x63) */
#define RT5651_3D_SPK_MASK (0x1 << 15)
#define RT5651_3D_SPK_SFT 15
@@ -2059,10 +2066,25 @@ struct rt5651_pll_code {
};
struct rt5651_priv {
- struct snd_soc_codec *codec;
- struct rt5651_platform_data pdata;
+ struct snd_soc_component *component;
struct regmap *regmap;
-
+ /* Jack and button detect data */
+ struct snd_soc_jack *hp_jack;
+ struct gpio_desc *gpiod_hp_det;
+ struct work_struct jack_detect_work;
+ struct delayed_work bp_work;
+ bool ovcd_irq_enabled;
+ bool pressed;
+ bool press_reported;
+ int press_count;
+ int release_count;
+ int poll_count;
+ unsigned int jd_src;
+ bool jd_active_high;
+ unsigned int ovcd_th;
+ unsigned int ovcd_sf;
+
+ int irq;
int sysclk;
int sysclk_src;
int lrck[RT5651_AIFS];
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 820d8fa62b5e..f5957470652c 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -1,14 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5659.c -- RT5659/RT5658 ALSA SoC audio codec driver
*
* Copyright 2015 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can 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>
@@ -18,7 +16,6 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -1149,28 +1146,28 @@ static const char * const rt5659_data_select[] = {
"L/R", "R/L", "L/L", "R/R"
};
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT01_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT23_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT45_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT67_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,
RT5659_DIG_INF23_DATA, RT5659_IF2_DAC_SEL_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,
RT5659_DIG_INF23_DATA, RT5659_IF2_ADC_SEL_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,
RT5659_DIG_INF23_DATA, RT5659_IF3_DAC_SEL_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,
RT5659_DIG_INF23_DATA, RT5659_IF3_ADC_SEL_SFT, rt5659_data_select);
static const struct snd_kcontrol_new rt5659_if1_01_adc_swap_mux =
@@ -1197,66 +1194,29 @@ static const struct snd_kcontrol_new rt5659_if3_dac_swap_mux =
static const struct snd_kcontrol_new rt5659_if3_adc_swap_mux =
SOC_DAPM_ENUM("IF3 ADC Swap Source", rt5659_if3_adc_enum);
-static const char * const rt5659_asrc_clk_src[] = {
- "clk_sysy_div_out", "clk_i2s1_track", "clk_i2s2_track",
- "clk_i2s3_track", "clk_sys2", "clk_sys3"
-};
-
-static unsigned int rt5659_asrc_clk_map_values[] = {
- 0, 1, 2, 3, 5, 6,
-};
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_da_sto_asrc_enum, RT5659_ASRC_2, RT5659_DA_STO_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_da_monol_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_L_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_da_monor_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_R_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_sto1_asrc_enum, RT5659_ASRC_2, RT5659_AD_STO1_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_sto2_asrc_enum, RT5659_ASRC_3, RT5659_AD_STO2_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_monol_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_L_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
-static const SOC_VALUE_ENUM_SINGLE_DECL(
- rt5659_ad_monor_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_R_T_SFT, 0x7,
- rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-
static int rt5659_hp_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int ret = snd_soc_put_volsw(kcontrol, ucontrol);
- if (snd_soc_read(codec, RT5659_STO_NG2_CTRL_1) & RT5659_NG2_EN) {
- snd_soc_update_bits(codec, RT5659_STO_NG2_CTRL_1,
+ if (snd_soc_component_read(component, RT5659_STO_NG2_CTRL_1) & RT5659_NG2_EN) {
+ snd_soc_component_update_bits(component, RT5659_STO_NG2_CTRL_1,
RT5659_NG2_EN_MASK, RT5659_NG2_DIS);
- snd_soc_update_bits(codec, RT5659_STO_NG2_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_STO_NG2_CTRL_1,
RT5659_NG2_EN_MASK, RT5659_NG2_EN);
}
return ret;
}
-static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
+static void rt5659_enable_push_button_irq(struct snd_soc_component *component,
bool enable)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
if (enable) {
- snd_soc_write(codec, RT5659_4BTN_IL_CMD_1, 0x000b);
+ snd_soc_component_write(component, RT5659_4BTN_IL_CMD_1, 0x000b);
/* MICBIAS1 and Mic Det Power for button detect*/
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
@@ -1264,19 +1224,19 @@ static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
"Mic Det Power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5659_PWR_ANLG_2,
+ snd_soc_component_update_bits(component, RT5659_PWR_ANLG_2,
RT5659_PWR_MB1, RT5659_PWR_MB1);
- snd_soc_update_bits(codec, RT5659_PWR_VOL,
+ snd_soc_component_update_bits(component, RT5659_PWR_VOL,
RT5659_PWR_MIC_DET, RT5659_PWR_MIC_DET);
- snd_soc_update_bits(codec, RT5659_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5659_IRQ_CTRL_2,
RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN);
- snd_soc_update_bits(codec, RT5659_4BTN_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5659_4BTN_IL_CMD_2,
RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_EN);
} else {
- snd_soc_update_bits(codec, RT5659_4BTN_IL_CMD_2,
+ snd_soc_component_update_bits(component, RT5659_4BTN_IL_CMD_2,
RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_DIS);
- snd_soc_update_bits(codec, RT5659_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5659_IRQ_CTRL_2,
RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_DIS);
/* MICBIAS1 and Mic Det Power for button detect*/
snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
@@ -1287,7 +1247,7 @@ static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
/**
* rt5659_headset_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
@@ -1295,37 +1255,37 @@ static void rt5659_enable_push_button_irq(struct snd_soc_codec *codec,
* Returns detect status.
*/
-static int rt5659_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5659_headset_detect(struct snd_soc_component *component, int jack_insert)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30};
int reg_63;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
if (jack_insert) {
snd_soc_dapm_force_enable_pin(dapm,
"Mic Det Power");
snd_soc_dapm_sync(dapm);
- reg_63 = snd_soc_read(codec, RT5659_PWR_ANLG_1);
+ reg_63 = snd_soc_component_read(component, RT5659_PWR_ANLG_1);
- snd_soc_update_bits(codec, RT5659_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5659_PWR_ANLG_1,
RT5659_PWR_VREF2 | RT5659_PWR_MB,
RT5659_PWR_VREF2 | RT5659_PWR_MB);
msleep(20);
- snd_soc_update_bits(codec, RT5659_PWR_ANLG_1,
+ snd_soc_component_update_bits(component, RT5659_PWR_ANLG_1,
RT5659_PWR_FV2, RT5659_PWR_FV2);
- snd_soc_write(codec, RT5659_EJD_CTRL_2, 0x4160);
- snd_soc_update_bits(codec, RT5659_EJD_CTRL_1,
+ snd_soc_component_write(component, RT5659_EJD_CTRL_2, 0x4160);
+ snd_soc_component_update_bits(component, RT5659_EJD_CTRL_1,
0x20, 0x0);
msleep(20);
- snd_soc_update_bits(codec, RT5659_EJD_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_EJD_CTRL_1,
0x20, 0x20);
while (i < 5) {
msleep(sleep_time[i]);
- val = snd_soc_read(codec, RT5659_EJD_CTRL_2) & 0x0003;
+ val = snd_soc_component_read(component, RT5659_EJD_CTRL_2) & 0x0003;
i++;
if (val == 0x1 || val == 0x2 || val == 0x3)
break;
@@ -1334,10 +1294,10 @@ static int rt5659_headset_detect(struct snd_soc_codec *codec, int jack_insert)
switch (val) {
case 1:
rt5659->jack_type = SND_JACK_HEADSET;
- rt5659_enable_push_button_irq(codec, true);
+ rt5659_enable_push_button_irq(component, true);
break;
default:
- snd_soc_write(codec, RT5659_PWR_ANLG_1, reg_63);
+ snd_soc_component_write(component, RT5659_PWR_ANLG_1, reg_63);
rt5659->jack_type = SND_JACK_HEADPHONE;
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
@@ -1347,21 +1307,21 @@ static int rt5659_headset_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
if (rt5659->jack_type == SND_JACK_HEADSET)
- rt5659_enable_push_button_irq(codec, false);
+ rt5659_enable_push_button_irq(component, false);
rt5659->jack_type = 0;
}
- dev_dbg(codec->dev, "jack_type = %d\n", rt5659->jack_type);
+ dev_dbg(component->dev, "jack_type = %d\n", rt5659->jack_type);
return rt5659->jack_type;
}
-static int rt5659_button_detect(struct snd_soc_codec *codec)
+static int rt5659_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5659_4BTN_IL_CMD_1);
+ val = snd_soc_component_read(component, RT5659_4BTN_IL_CMD_1);
btn_type = val & 0xfff0;
- snd_soc_write(codec, RT5659_4BTN_IL_CMD_1, val);
+ snd_soc_component_write(component, RT5659_4BTN_IL_CMD_1, val);
return btn_type;
}
@@ -1376,10 +1336,10 @@ static irqreturn_t rt5659_irq(int irq, void *data)
return IRQ_HANDLED;
}
-int rt5659_set_jack_detect(struct snd_soc_codec *codec,
+int rt5659_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
rt5659->hs_jack = hs_jack;
@@ -1395,19 +1355,19 @@ static void rt5659_jack_detect_work(struct work_struct *work)
container_of(work, struct rt5659_priv, jack_detect_work.work);
int val, btn_type, report = 0;
- if (!rt5659->codec)
+ if (!rt5659->component)
return;
- val = snd_soc_read(rt5659->codec, RT5659_INT_ST_1) & 0x0080;
+ val = snd_soc_component_read(rt5659->component, RT5659_INT_ST_1) & 0x0080;
if (!val) {
/* jack in */
if (rt5659->jack_type == 0) {
/* jack was out, report jack type */
- report = rt5659_headset_detect(rt5659->codec, 1);
+ report = rt5659_headset_detect(rt5659->component, 1);
} else {
/* jack is already in, report button event */
report = SND_JACK_HEADSET;
- btn_type = rt5659_button_detect(rt5659->codec);
+ btn_type = rt5659_button_detect(rt5659->component);
/**
* rt5659 can report three kinds of button behavior,
* one click, double click and hold. However,
@@ -1440,7 +1400,7 @@ static void rt5659_jack_detect_work(struct work_struct *work)
break;
default:
btn_type = 0;
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -1452,7 +1412,7 @@ static void rt5659_jack_detect_work(struct work_struct *work)
}
} else {
/* jack out */
- report = rt5659_headset_detect(rt5659->codec, 0);
+ report = rt5659_headset_detect(rt5659->component, 0);
}
snd_soc_jack_report(rt5659->hs_jack, report, SND_JACK_HEADSET |
@@ -1460,6 +1420,61 @@ static void rt5659_jack_detect_work(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3);
}
+static void rt5659_jack_detect_intel_hd_header(struct work_struct *work)
+{
+ struct rt5659_priv *rt5659 =
+ container_of(work, struct rt5659_priv, jack_detect_work.work);
+ unsigned int value;
+ bool hp_flag, mic_flag;
+
+ if (!rt5659->hs_jack)
+ return;
+
+ /* headphone jack */
+ regmap_read(rt5659->regmap, RT5659_GPIO_STA, &value);
+ hp_flag = (!(value & 0x8)) ? true : false;
+
+ if (hp_flag != rt5659->hda_hp_plugged) {
+ rt5659->hda_hp_plugged = hp_flag;
+
+ if (hp_flag) {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x0);
+ rt5659->jack_type |= SND_JACK_HEADPHONE;
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x10);
+ rt5659->jack_type = rt5659->jack_type &
+ (~SND_JACK_HEADPHONE);
+ }
+
+ snd_soc_jack_report(rt5659->hs_jack, rt5659->jack_type,
+ SND_JACK_HEADPHONE);
+ }
+
+ /* mic jack */
+ regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value);
+ regmap_write(rt5659->regmap, RT5659_4BTN_IL_CMD_1, value);
+ mic_flag = (value & 0x2000) ? true : false;
+
+ if (mic_flag != rt5659->hda_mic_plugged) {
+ rt5659->hda_mic_plugged = mic_flag;
+ if (mic_flag) {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x2);
+ rt5659->jack_type |= SND_JACK_MICROPHONE;
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x0);
+ rt5659->jack_type = rt5659->jack_type
+ & (~SND_JACK_MICROPHONE);
+ }
+
+ snd_soc_jack_report(rt5659->hs_jack, rt5659->jack_type,
+ SND_JACK_MICROPHONE);
+ }
+}
+
static const struct snd_kcontrol_new rt5659_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5659_SPO_VOL,
@@ -1549,38 +1564,64 @@ static const struct snd_kcontrol_new rt5659_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
- int pd, idx = -EINVAL;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+ int pd, idx;
pd = rl6231_get_pre_div(rt5659->regmap,
RT5659_ADDA_CLK_1, RT5659_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rt5659->sysclk / pd);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else {
- snd_soc_update_bits(codec, RT5659_DMIC_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_DMIC_CTRL_1,
RT5659_DMIC_CLK_MASK, idx << RT5659_DMIC_CLK_SFT);
}
return idx;
}
-static int set_adc_clk(struct snd_soc_dapm_widget *w,
+static int set_adc1_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK,
+ RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int set_adc2_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5659_CHOP_ADC,
- RT5659_CKXEN_ADCC_MASK | RT5659_CKGEN_ADCC_MASK,
- RT5659_CKXEN_ADCC_MASK | RT5659_CKGEN_ADCC_MASK);
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK,
+ RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5659_CHOP_ADC,
- RT5659_CKXEN_ADCC_MASK | RT5659_CKGEN_ADCC_MASK, 0);
+ snd_soc_component_update_bits(component, RT5659_CHOP_ADC,
+ RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK, 0);
break;
default:
@@ -1594,15 +1635,15 @@ static int set_adc_clk(struct snd_soc_dapm_widget *w,
static int rt5659_charge_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Depop */
- snd_soc_write(codec, RT5659_DEPOP_1, 0x0009);
+ snd_soc_component_write(component, RT5659_DEPOP_1, 0x0009);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5659_HP_CHARGE_PUMP_1, 0x0c16);
+ snd_soc_component_write(component, RT5659_HP_CHARGE_PUMP_1, 0x0c16);
break;
default:
return 0;
@@ -1615,9 +1656,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
- val = snd_soc_read(codec, RT5659_GLB_CLK);
+ val = snd_soc_component_read(component, RT5659_GLB_CLK);
val &= RT5659_SCLK_SRC_MASK;
if (val == RT5659_SCLK_SRC_PLL1)
return 1;
@@ -1629,7 +1670,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
unsigned int reg, shift, val;
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (w->shift) {
case RT5659_ADC_MONO_R_ASRC_SFT:
@@ -1660,13 +1701,13 @@ static int is_using_asrc(struct snd_soc_dapm_widget *w,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
case 3:
/* I2S_Pre_Div1 should be 1 in asrc mode */
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_I2S_PD1_MASK, RT5659_I2S_PD1_2);
return 1;
default:
@@ -1929,14 +1970,14 @@ static const char * const rt5659_dac2_src[] = {
"IF1 DAC2", "IF2 DAC", "IF3 DAC", "Mono ADC MIX"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_l2_enum, RT5659_DAC_CTRL,
RT5659_DAC_L2_SEL_SFT, rt5659_dac2_src);
static const struct snd_kcontrol_new rt5659_dac_l2_mux =
SOC_DAPM_ENUM("DAC L2 Source", rt5659_dac_l2_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_r2_enum, RT5659_DAC_CTRL,
RT5659_DAC_R2_SEL_SFT, rt5659_dac2_src);
@@ -1950,7 +1991,7 @@ static const char * const rt5659_sto1_adc1_src[] = {
"DAC MIX", "ADC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_adc1_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_ADC1_SRC_SFT, rt5659_sto1_adc1_src);
@@ -1963,7 +2004,7 @@ static const char * const rt5659_sto1_adc_src[] = {
"ADC1", "ADC2"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_adc_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_ADC_SRC_SFT, rt5659_sto1_adc_src);
@@ -1976,7 +2017,7 @@ static const char * const rt5659_sto1_adc2_src[] = {
"DAC MIX", "DMIC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_adc2_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_ADC2_SRC_SFT, rt5659_sto1_adc2_src);
@@ -1989,7 +2030,7 @@ static const char * const rt5659_sto1_dmic_src[] = {
"DMIC1", "DMIC2"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_dmic_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_DMIC_SRC_SFT, rt5659_sto1_dmic_src);
@@ -2003,7 +2044,7 @@ static const char * const rt5659_mono_adc_l2_src[] = {
"Mono DAC MIXL", "DMIC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_l2_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_L2_SRC_SFT, rt5659_mono_adc_l2_src);
@@ -2017,7 +2058,7 @@ static const char * const rt5659_mono_adc_l1_src[] = {
"Mono DAC MIXL", "ADC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_l1_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_L1_SRC_SFT, rt5659_mono_adc_l1_src);
@@ -2030,14 +2071,14 @@ static const char * const rt5659_mono_adc_src[] = {
"ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_l_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_L_SRC_SFT, rt5659_mono_adc_src);
static const struct snd_kcontrol_new rt5659_mono_adc_l_mux =
SOC_DAPM_ENUM("Mono ADC L Source", rt5659_mono_adc_l_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adcr_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_R_SRC_SFT, rt5659_mono_adc_src);
@@ -2050,7 +2091,7 @@ static const char * const rt5659_mono_dmic_l_src[] = {
"DMIC1 L", "DMIC2 L"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_dmic_l_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_DMIC_L_SRC_SFT, rt5659_mono_dmic_l_src);
@@ -2063,7 +2104,7 @@ static const char * const rt5659_mono_adc_r2_src[] = {
"Mono DAC MIXR", "DMIC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_r2_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_R2_SRC_SFT, rt5659_mono_adc_r2_src);
@@ -2076,7 +2117,7 @@ static const char * const rt5659_mono_adc_r1_src[] = {
"Mono DAC MIXR", "ADC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_r1_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_R1_SRC_SFT, rt5659_mono_adc_r1_src);
@@ -2089,7 +2130,7 @@ static const char * const rt5659_mono_dmic_r_src[] = {
"DMIC1 R", "DMIC2 R"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_dmic_r_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_DMIC_R_SRC_SFT, rt5659_mono_dmic_r_src);
@@ -2103,14 +2144,14 @@ static const char * const rt5659_dac1_src[] = {
"IF1 DAC1", "IF2 DAC", "IF3 DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_r1_enum, RT5659_AD_DA_MIXER,
RT5659_DAC1_R_SEL_SFT, rt5659_dac1_src);
static const struct snd_kcontrol_new rt5659_dac_r1_mux =
SOC_DAPM_ENUM("DAC R1 Source", rt5659_dac_r1_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_l1_enum, RT5659_AD_DA_MIXER,
RT5659_DAC1_L_SEL_SFT, rt5659_dac1_src);
@@ -2123,14 +2164,14 @@ static const char * const rt5659_dig_dac_mix_src[] = {
"Stereo DAC Mixer", "Mono DAC Mixer"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dig_dac_mixl_enum, RT5659_DIG_MIXER,
RT5659_DAC_MIX_L_SFT, rt5659_dig_dac_mix_src);
static const struct snd_kcontrol_new rt5659_dig_dac_mixl_mux =
SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5659_dig_dac_mixl_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dig_dac_mixr_enum, RT5659_DIG_MIXER,
RT5659_DAC_MIX_R_SFT, rt5659_dig_dac_mix_src);
@@ -2143,14 +2184,14 @@ static const char * const rt5659_alg_dac1_src[] = {
"DAC", "Stereo DAC Mixer"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_l1_enum, RT5659_A_DAC_MUX,
RT5659_A_DACL1_SFT, rt5659_alg_dac1_src);
static const struct snd_kcontrol_new rt5659_alg_dac_l1_mux =
SOC_DAPM_ENUM("Analog DACL1 Source", rt5659_alg_dac_l1_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_r1_enum, RT5659_A_DAC_MUX,
RT5659_A_DACR1_SFT, rt5659_alg_dac1_src);
@@ -2163,14 +2204,14 @@ static const char * const rt5659_alg_dac2_src[] = {
"Stereo DAC Mixer", "Mono DAC Mixer"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_l2_enum, RT5659_A_DAC_MUX,
RT5659_A_DACL2_SFT, rt5659_alg_dac2_src);
static const struct snd_kcontrol_new rt5659_alg_dac_l2_mux =
SOC_DAPM_ENUM("Analog DAC L2 Source", rt5659_alg_dac_l2_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_r2_enum, RT5659_A_DAC_MUX,
RT5659_A_DACR2_SFT, rt5659_alg_dac2_src);
@@ -2183,7 +2224,7 @@ static const char * const rt5659_if2_adc_in_src[] = {
"IF_ADC1", "IF_ADC2", "DAC_REF", "IF_ADC3"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_if2_adc_in_enum, RT5659_DIG_INF23_DATA,
RT5659_IF2_ADC_IN_SFT, rt5659_if2_adc_in_src);
@@ -2196,7 +2237,7 @@ static const char * const rt5659_if3_adc_in_src[] = {
"IF_ADC1", "IF_ADC2", "DAC_REF", "Stereo2_ADC_L/R"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_if3_adc_in_enum, RT5659_DIG_INF23_DATA,
RT5659_IF3_ADC_IN_SFT, rt5659_if3_adc_in_src);
@@ -2209,14 +2250,14 @@ static const char * const rt5659_pdm_src[] = {
"Mono DAC", "Stereo DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_pdm_l_enum, RT5659_PDM_OUT_CTRL,
RT5659_PDM1_L_SFT, rt5659_pdm_src);
static const struct snd_kcontrol_new rt5659_pdm_l_mux =
SOC_DAPM_ENUM("PDM L Source", rt5659_pdm_l_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_pdm_r_enum, RT5659_PDM_OUT_CTRL,
RT5659_PDM1_R_SFT, rt5659_pdm_src);
@@ -2229,7 +2270,7 @@ static const char * const rt5659_spdif_src[] = {
"IF1_DAC1", "IF1_DAC2", "IF2_DAC", "IF3_DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_spdif_enum, RT5659_SPDIF_CTRL,
RT5659_SPDIF_SEL_SFT, rt5659_spdif_src);
@@ -2249,7 +2290,7 @@ static const char * const rt5659_rx_adc_data_src[] = {
"NUL:AD2:DAC:AD1", "NUL:DAC:DAC:AD2", "NUL:DAC:AD2:DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_rx_adc_data_enum, RT5659_TDM_CTRL_2,
RT5659_ADCDAT_SRC_SFT, rt5659_rx_adc_data_src);
@@ -2302,24 +2343,24 @@ static const struct snd_kcontrol_new pdm_r_switch =
static int rt5659_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, RT5659_CLASSD_CTRL_1,
+ snd_soc_component_update_bits(component, RT5659_CLASSD_CTRL_1,
RT5659_POW_CLSD_DB_MASK, RT5659_POW_CLSD_DB_EN);
- snd_soc_update_bits(codec, RT5659_CLASSD_2,
+ snd_soc_component_update_bits(component, RT5659_CLASSD_2,
RT5659_M_RI_DIG, RT5659_M_RI_DIG);
- snd_soc_write(codec, RT5659_CLASSD_1, 0x0803);
- snd_soc_write(codec, RT5659_SPK_DC_CAILB_CTRL_3, 0x0000);
+ snd_soc_component_write(component, RT5659_CLASSD_1, 0x0803);
+ snd_soc_component_write(component, RT5659_SPK_DC_CAILB_CTRL_3, 0x0000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5659_CLASSD_1, 0x0011);
- snd_soc_update_bits(codec, RT5659_CLASSD_2,
+ snd_soc_component_write(component, RT5659_CLASSD_1, 0x0011);
+ snd_soc_component_update_bits(component, RT5659_CLASSD_2,
RT5659_M_RI_DIG, 0x0);
- snd_soc_write(codec, RT5659_SPK_DC_CAILB_CTRL_3, 0x0003);
- snd_soc_update_bits(codec, RT5659_CLASSD_CTRL_1,
+ snd_soc_component_write(component, RT5659_SPK_DC_CAILB_CTRL_3, 0x0003);
+ snd_soc_component_update_bits(component, RT5659_CLASSD_CTRL_1,
RT5659_POW_CLSD_DB_MASK, RT5659_POW_CLSD_DB_DIS);
break;
@@ -2334,15 +2375,15 @@ static int rt5659_spk_event(struct snd_soc_dapm_widget *w,
static int rt5659_mono_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e00);
+ snd_soc_component_write(component, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e00);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e04);
+ snd_soc_component_write(component, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e04);
break;
default:
@@ -2356,16 +2397,16 @@ static int rt5659_mono_event(struct snd_soc_dapm_widget *w,
static int rt5659_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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT5659_HP_CHARGE_PUMP_1, 0x0e1e);
- snd_soc_update_bits(codec, RT5659_DEPOP_1, 0x0010, 0x0010);
+ snd_soc_component_write(component, RT5659_HP_CHARGE_PUMP_1, 0x0e1e);
+ snd_soc_component_update_bits(component, RT5659_DEPOP_1, 0x0010, 0x0010);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT5659_DEPOP_1, 0x0000);
+ snd_soc_component_write(component, RT5659_DEPOP_1, 0x0000);
break;
default:
@@ -2391,13 +2432,18 @@ static int set_dmic_power(struct snd_soc_dapm_widget *w,
return 0;
}
-static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget rt5659_particular_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("LDO2", RT5659_PWR_ANLG_3, RT5659_PWR_LDO2_BIT, 0,
NULL, 0),
- SND_SOC_DAPM_SUPPLY("PLL", RT5659_PWR_ANLG_3, RT5659_PWR_PLL_BIT, 0,
- NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5659_PWR_ANLG_2, RT5659_PWR_MB1_BIT,
+ 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5659_PWR_VOL,
RT5659_PWR_MIC_DET_BIT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", RT5659_PWR_ANLG_3, RT5659_PWR_PLL_BIT, 0,
+ NULL, 0),
SND_SOC_DAPM_SUPPLY("Mono Vref", RT5659_PWR_ANLG_1,
RT5659_PWR_VREF3_BIT, 0, NULL, 0),
@@ -2422,8 +2468,6 @@ static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
RT5659_ADC_MONO_R_ASRC_SFT, 0, NULL, 0),
/* Input Side */
- SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5659_PWR_ANLG_2, RT5659_PWR_MB1_BIT,
- 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5659_PWR_ANLG_2, RT5659_PWR_MB2_BIT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS3", RT5659_PWR_ANLG_2, RT5659_PWR_MB3_BIT,
@@ -2499,13 +2543,13 @@ static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
RT5659_PWR_ADC_L1_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5659_PWR_DIG_1,
RT5659_PWR_ADC_R1_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5659_PWR_DIG_2,
+ SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5659_PWR_DIG_1,
RT5659_PWR_ADC_L2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5659_PWR_DIG_2,
+ SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5659_PWR_DIG_1,
RT5659_PWR_ADC_R2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC1 clock", SND_SOC_NOPM, 0, 0, set_adc_clk,
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", SND_SOC_NOPM, 0, 0, set_adc1_clk,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_SUPPLY("ADC2 clock", SND_SOC_NOPM, 0, 0, set_adc_clk,
+ SND_SOC_DAPM_SUPPLY("ADC2 clock", SND_SOC_NOPM, 0, 0, set_adc2_clk,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
/* ADC Mux */
@@ -2743,7 +2787,8 @@ static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = {
SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5659_hp_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_PGA("LOUT Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("LOUT Amp", 1, RT5659_PWR_ANLG_1, RT5659_PWR_LM_BIT,
+ 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0,
rt5659_charge_pump_event, SND_SOC_DAPM_PRE_PMU |
@@ -2816,11 +2861,6 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = {
{ "I2S2", NULL, "I2S2 ASRC" },
{ "I2S3", NULL, "I2S3 ASRC" },
- { "IN1P", NULL, "LDO2" },
- { "IN2P", NULL, "LDO2" },
- { "IN3P", NULL, "LDO2" },
- { "IN4P", NULL, "LDO2" },
-
{ "DMIC1", NULL, "DMIC L1" },
{ "DMIC1", NULL, "DMIC R1" },
{ "DMIC2", NULL, "DMIC L2" },
@@ -3207,6 +3247,7 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = {
{ "LOUT R MIX", "OUTVOL R Switch", "OUTVOL R" },
{ "LOUT Amp", NULL, "LOUT L MIX" },
{ "LOUT Amp", NULL, "LOUT R MIX" },
+ { "LOUT Amp", NULL, "Charge Pump" },
{ "LOUT Amp", NULL, "SYS CLK DET" },
{ "LOUT L Playback", "Switch", "LOUT Amp" },
{ "LOUT R Playback", "Switch", "LOUT Amp" },
@@ -3234,21 +3275,21 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = {
static int rt5659_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 rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, frame_size;
rt5659->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5659->sysclk, rt5659->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5659->lrck[dai->id], dai->id);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
@@ -3275,39 +3316,39 @@ static int rt5659_hw_params(struct snd_pcm_substream *substream,
case RT5659_AIF1:
mask_clk = RT5659_I2S_PD1_MASK;
val_clk = pre_div << RT5659_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5659_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S1_SDP,
RT5659_I2S_DL_MASK, val_len);
break;
case RT5659_AIF2:
mask_clk = RT5659_I2S_PD2_MASK;
val_clk = pre_div << RT5659_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5659_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S2_SDP,
RT5659_I2S_DL_MASK, val_len);
break;
case RT5659_AIF3:
mask_clk = RT5659_I2S_PD3_MASK;
val_clk = pre_div << RT5659_I2S_PD3_SFT;
- snd_soc_update_bits(codec, RT5659_I2S3_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S3_SDP,
RT5659_I2S_DL_MASK, val_len);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, mask_clk, val_clk);
switch (rt5659->lrck[dai->id]) {
case 192000:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_32);
break;
case 96000:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_64);
break;
default:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_128);
break;
}
@@ -3317,15 +3358,15 @@ static int rt5659_hw_params(struct snd_pcm_substream *substream,
static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5659->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5659_I2S_MS_S;
rt5659->master[dai->id] = 0;
break;
@@ -3361,39 +3402,43 @@ static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5659_AIF1:
- snd_soc_update_bits(codec, RT5659_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S1_SDP,
RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK |
RT5659_I2S_DF_MASK, reg_val);
break;
case RT5659_AIF2:
- snd_soc_update_bits(codec, RT5659_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S2_SDP,
RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK |
RT5659_I2S_DF_MASK, reg_val);
break;
case RT5659_AIF3:
- snd_soc_update_bits(codec, RT5659_I2S3_SDP,
+ snd_soc_component_update_bits(component, RT5659_I2S3_SDP,
RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK |
RT5659_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
}
-static int rt5659_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+static int rt5659_set_component_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
+ int ret;
if (freq == rt5659->sysclk && clk_id == rt5659->sysclk_src)
return 0;
switch (clk_id) {
case RT5659_SCLK_S_MCLK:
+ ret = clk_set_rate(rt5659->mclk, freq);
+ if (ret)
+ return ret;
+
reg_val |= RT5659_SCLK_SRC_MCLK;
break;
case RT5659_SCLK_S_PLL1:
@@ -3403,82 +3448,83 @@ static int rt5659_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5659_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_SCLK_SRC_MASK, reg_val);
rt5659->sysclk = freq;
rt5659->sysclk_src = clk_id;
- dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
return 0;
}
-static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
- unsigned int freq_in, unsigned int freq_out)
+static int rt5659_set_component_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
- if (Source == rt5659->pll_src && freq_in == rt5659->pll_in &&
+ if (source == rt5659->pll_src && freq_in == rt5659->pll_in &&
freq_out == rt5659->pll_out)
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5659->pll_in = 0;
rt5659->pll_out = 0;
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_SCLK_SRC_MASK, RT5659_SCLK_SRC_MCLK);
return 0;
}
- switch (Source) {
+ switch (source) {
case RT5659_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_MCLK);
break;
case RT5659_PLL1_S_BCLK1:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK1);
break;
case RT5659_PLL1_S_BCLK2:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK2);
break;
case RT5659_PLL1_S_BCLK3:
- snd_soc_update_bits(codec, RT5659_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5659_GLB_CLK,
RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK3);
break;
default:
- dev_err(codec->dev, "Unknown PLL Source %d\n", Source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
- snd_soc_write(codec, RT5659_PLL_CTRL_1,
+ snd_soc_component_write(component, RT5659_PLL_CTRL_1,
pll_code.n_code << RT5659_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5659_PLL_CTRL_2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5659_PLL_M_SFT |
- pll_code.m_bp << RT5659_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5659_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5659_PLL_M_SFT) |
+ (pll_code.m_bp << RT5659_PLL_M_BP_SFT));
rt5659->pll_in = freq_in;
rt5659->pll_out = freq_out;
- rt5659->pll_src = Source;
+ rt5659->pll_src = source;
return 0;
}
@@ -3486,7 +3532,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
static int rt5659_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
if (rx_mask || tx_mask)
@@ -3530,29 +3576,29 @@ static int rt5659_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5659_TDM_CTRL_1, 0x8ff0, val);
+ snd_soc_component_update_bits(component, RT5659_TDM_CTRL_1, 0x8ff0, val);
return 0;
}
static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
rt5659->bclk[dai->id] = ratio;
if (ratio == 64) {
switch (dai->id) {
case RT5659_AIF2:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_I2S_BCLK_MS2_MASK,
RT5659_I2S_BCLK_MS2_64);
break;
case RT5659_AIF3:
- snd_soc_update_bits(codec, RT5659_ADDA_CLK_1,
+ snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1,
RT5659_I2S_BCLK_MS3_MASK,
RT5659_I2S_BCLK_MS3_64);
break;
@@ -3562,10 +3608,12 @@ static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
return 0;
}
-static int rt5659_set_bias_level(struct snd_soc_codec *codec,
+static int rt5659_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+ int ret;
switch (level) {
case SND_SOC_BIAS_PREPARE:
@@ -3582,6 +3630,17 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec,
RT5659_PWR_FV1 | RT5659_PWR_FV2);
break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ ret = clk_prepare_enable(rt5659->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
case SND_SOC_BIAS_OFF:
regmap_update_bits(rt5659->regmap, RT5659_PWR_DIG_1,
RT5659_PWR_LDO, 0);
@@ -3591,6 +3650,7 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec,
RT5659_PWR_MB | RT5659_PWR_VREF2);
regmap_update_bits(rt5659->regmap, RT5659_DIG_MISC,
RT5659_DIG_GATE_CTRL, 0);
+ clk_disable_unprepare(rt5659->mclk);
break;
default:
@@ -3600,37 +3660,47 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5659_probe(struct snd_soc_codec *codec)
+static int rt5659_probe(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
- rt5659->codec = codec;
+ rt5659->component = component;
+
+ switch (rt5659->pdata.jd_src) {
+ case RT5659_JD_HDA_HEADER:
+ break;
+
+ default:
+ snd_soc_dapm_new_controls(dapm,
+ rt5659_particular_dapm_widgets,
+ ARRAY_SIZE(rt5659_particular_dapm_widgets));
+ break;
+ }
return 0;
}
-static int rt5659_remove(struct snd_soc_codec *codec)
+static void rt5659_remove(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regmap_write(rt5659->regmap, RT5659_RESET, 0);
-
- return 0;
}
#ifdef CONFIG_PM
-static int rt5659_suspend(struct snd_soc_codec *codec)
+static int rt5659_suspend(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5659->regmap, true);
regcache_mark_dirty(rt5659->regmap);
return 0;
}
-static int rt5659_resume(struct snd_soc_codec *codec)
+static int rt5659_resume(struct snd_soc_component *component)
{
- struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5659->regmap, false);
regcache_sync(rt5659->regmap);
@@ -3649,9 +3719,7 @@ static int rt5659_resume(struct snd_soc_codec *codec)
static const struct snd_soc_dai_ops rt5659_aif_dai_ops = {
.hw_params = rt5659_hw_params,
.set_fmt = rt5659_set_dai_fmt,
- .set_sysclk = rt5659_set_dai_sysclk,
.set_tdm_slot = rt5659_set_tdm_slot,
- .set_pll = rt5659_set_dai_pll,
.set_bclk_ratio = rt5659_set_bclk_ratio,
};
@@ -3715,19 +3783,22 @@ static struct snd_soc_dai_driver rt5659_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5659 = {
- .probe = rt5659_probe,
- .remove = rt5659_remove,
- .suspend = rt5659_suspend,
- .resume = rt5659_resume,
- .set_bias_level = rt5659_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5659_snd_controls,
- .num_controls = ARRAY_SIZE(rt5659_snd_controls),
- .dapm_widgets = rt5659_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets),
- .dapm_routes = rt5659_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5659 = {
+ .probe = rt5659_probe,
+ .remove = rt5659_remove,
+ .suspend = rt5659_suspend,
+ .resume = rt5659_resume,
+ .set_bias_level = rt5659_set_bias_level,
+ .controls = rt5659_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5659_snd_controls),
+ .dapm_widgets = rt5659_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets),
+ .dapm_routes = rt5659_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes),
+ .set_sysclk = rt5659_set_component_sysclk,
+ .set_pll = rt5659_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
@@ -3743,8 +3814,8 @@ static const struct regmap_config rt5659_regmap = {
};
static const struct i2c_device_id rt5659_i2c_id[] = {
- { "rt5658", 0 },
- { "rt5659", 0 },
+ { "rt5658" },
+ { "rt5659" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5659_i2c_id);
@@ -3830,7 +3901,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 30) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"HP Calibration 1 Failure\n");
return;
}
@@ -3855,7 +3926,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 85) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"HP Calibration 2 Failure\n");
return;
}
@@ -3903,7 +3974,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 10) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"SPK Calibration Failure\n");
return;
}
@@ -3936,7 +4007,7 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
break;
if (count > 35) {
- dev_err(rt5659->codec->dev,
+ dev_err(rt5659->component->dev,
"Mono Calibration Failure\n");
return;
}
@@ -3971,8 +4042,55 @@ static void rt5659_calibrate(struct rt5659_priv *rt5659)
regmap_write(rt5659->regmap, RT5659_HP_CHARGE_PUMP_1, 0x0c16);
}
-static int rt5659_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static void rt5659_intel_hd_header_probe_setup(struct rt5659_priv *rt5659)
+{
+ int value;
+
+ regmap_read(rt5659->regmap, RT5659_GPIO_STA, &value);
+ if (!(value & 0x8)) {
+ rt5659->hda_hp_plugged = true;
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x0);
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x10);
+ }
+
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1,
+ RT5659_PWR_VREF2 | RT5659_PWR_MB,
+ RT5659_PWR_VREF2 | RT5659_PWR_MB);
+ msleep(20);
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1,
+ RT5659_PWR_FV2, RT5659_PWR_FV2);
+
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_3, RT5659_PWR_LDO2,
+ RT5659_PWR_LDO2);
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_2, RT5659_PWR_MB1,
+ RT5659_PWR_MB1);
+ regmap_update_bits(rt5659->regmap, RT5659_PWR_VOL, RT5659_PWR_MIC_DET,
+ RT5659_PWR_MIC_DET);
+ msleep(20);
+
+ regmap_update_bits(rt5659->regmap, RT5659_4BTN_IL_CMD_2,
+ RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_EN);
+ regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value);
+ regmap_write(rt5659->regmap, RT5659_4BTN_IL_CMD_1, value);
+ regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value);
+
+ if (value & 0x2000) {
+ rt5659->hda_mic_plugged = true;
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x2);
+ } else {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x0);
+ }
+
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN);
+}
+
+static int rt5659_i2c_probe(struct i2c_client *i2c)
{
struct rt5659_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5659_priv *rt5659;
@@ -3985,7 +4103,6 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
if (rt5659 == NULL)
return -ENOMEM;
- rt5659->i2c = i2c;
i2c_set_clientdata(i2c, rt5659);
if (pdata)
@@ -4002,7 +4119,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
GPIOD_OUT_HIGH);
/* Sleep for 300 ms miniumum */
- usleep_range(300000, 350000);
+ msleep(300);
rt5659->regmap = devm_regmap_init_i2c(i2c, &rt5659_regmap);
if (IS_ERR(rt5659->regmap)) {
@@ -4021,6 +4138,11 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
regmap_write(rt5659->regmap, RT5659_RESET, 0);
+ /* Check if MCLK provided */
+ rt5659->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5659->mclk))
+ return PTR_ERR(rt5659->mclk);
+
rt5659_calibrate(rt5659);
/* line in diff mode*/
@@ -4147,72 +4269,72 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
RT5659_PWR_MB, RT5659_PWR_MB);
regmap_write(rt5659->regmap, RT5659_PWR_ANLG_2, 0x0001);
regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_2, 0x0040);
+ INIT_DELAYED_WORK(&rt5659->jack_detect_work,
+ rt5659_jack_detect_work);
break;
- case RT5659_JD_NULL:
+ case RT5659_JD_HDA_HEADER:
+ regmap_write(rt5659->regmap, RT5659_GPIO_CTRL_3, 0x8000);
+ regmap_write(rt5659->regmap, RT5659_RC_CLK_CTRL, 0x0900);
+ regmap_write(rt5659->regmap, RT5659_EJD_CTRL_1, 0x70c0);
+ regmap_write(rt5659->regmap, RT5659_JD_CTRL_1, 0x2000);
+ regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_1, 0x0040);
+ INIT_DELAYED_WORK(&rt5659->jack_detect_work,
+ rt5659_jack_detect_intel_hd_header);
+ rt5659_intel_hd_header_probe_setup(rt5659);
break;
default:
- dev_warn(&i2c->dev, "Currently, support JD3 only\n");
break;
}
- INIT_DELAYED_WORK(&rt5659->jack_detect_work, rt5659_jack_detect_work);
-
- if (rt5659->i2c->irq) {
- ret = request_threaded_irq(rt5659->i2c->irq, NULL, rt5659_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5659", rt5659);
if (ret)
- dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ /* Enable IRQ output for GPIO1 pin any way */
+ regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1,
+ RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ);
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5659,
rt5659_dai, ARRAY_SIZE(rt5659_dai));
-
- if (ret) {
- if (rt5659->i2c->irq)
- free_irq(rt5659->i2c->irq, rt5659);
- }
-
- return 0;
}
-static int rt5659_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
-void rt5659_i2c_shutdown(struct i2c_client *client)
+static void rt5659_i2c_shutdown(struct i2c_client *client)
{
struct rt5659_priv *rt5659 = i2c_get_clientdata(client);
regmap_write(rt5659->regmap, RT5659_RESET, 0);
}
+#ifdef CONFIG_OF
static const struct of_device_id rt5659_of_match[] = {
{ .compatible = "realtek,rt5658", },
{ .compatible = "realtek,rt5659", },
- {},
+ { }
};
+MODULE_DEVICE_TABLE(of, rt5659_of_match);
+#endif
-static struct acpi_device_id rt5659_acpi_match[] = {
- { "10EC5658", 0},
- { "10EC5659", 0},
- { },
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5659_acpi_match[] = {
+ { "10EC5658" },
+ { "10EC5659" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match);
+#endif
-struct i2c_driver rt5659_i2c_driver = {
+static struct i2c_driver rt5659_i2c_driver = {
.driver = {
.name = "rt5659",
- .owner = THIS_MODULE,
- .of_match_table = rt5659_of_match,
+ .of_match_table = of_match_ptr(rt5659_of_match),
.acpi_match_table = ACPI_PTR(rt5659_acpi_match),
},
.probe = rt5659_i2c_probe,
- .remove = rt5659_i2c_remove,
.shutdown = rt5659_i2c_shutdown,
.id_table = rt5659_i2c_id,
};
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index 8f07ee903eaa..b49fd8baf4e7 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5659.h -- RT5659/RT5658 ALSA SoC audio driver
*
* Copyright 2015 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can 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 __RT5659_H__
@@ -180,9 +177,9 @@
#define RT5659_IRQ_CTRL_1 0x00b6
#define RT5659_IRQ_CTRL_2 0x00b7
#define RT5659_IRQ_CTRL_3 0x00b8
-#define RT5659_IRQ_CTRL_4 0x00b9
-#define RT5659_IRQ_CTRL_5 0x00ba
-#define RT5659_IRQ_CTRL_6 0x00bb
+#define RT5659_IRQ_CTRL_4 0x00ba
+#define RT5659_IRQ_CTRL_5 0x00bb
+#define RT5659_IRQ_CTRL_6 0x00bc
#define RT5659_INT_ST_1 0x00be
#define RT5659_INT_ST_2 0x00bf
#define RT5659_GPIO_CTRL_1 0x00c0
@@ -1008,7 +1005,7 @@
#define RT5659_PWR_ADC_R1 (0x1 << 3)
#define RT5659_PWR_ADC_R1_BIT 3
#define RT5659_PWR_ADC_L2 (0x1 << 2)
-#define RT5659_PWR_ADC_L2_BIT 4
+#define RT5659_PWR_ADC_L2_BIT 2
#define RT5659_PWR_ADC_R2 (0x1 << 1)
#define RT5659_PWR_ADC_R2_BIT 1
#define RT5659_PWR_CLS_D (0x1)
@@ -1743,10 +1740,14 @@
#define RT5659_CKGEN_DAC2_SFT 4
/* Chopper and Clock control for ADC (0x013b)*/
-#define RT5659_CKXEN_ADCC_MASK (0x1 << 13)
-#define RT5659_CKXEN_ADCC_SFT 13
-#define RT5659_CKGEN_ADCC_MASK (0x1 << 12)
-#define RT5659_CKGEN_ADCC_SFT 12
+#define RT5659_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5659_CKXEN_ADC1_SFT 13
+#define RT5659_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5659_CKGEN_ADC1_SFT 12
+#define RT5659_CKXEN_ADC2_MASK (0x1 << 5)
+#define RT5659_CKXEN_ADC2_SFT 5
+#define RT5659_CKGEN_ADC2_MASK (0x1 << 4)
+#define RT5659_CKGEN_ADC2_SFT 4
/* Test Mode Control 1 (0x0145) */
#define RT5659_AD2DA_LB_MASK (0x1 << 9)
@@ -1789,14 +1790,14 @@ struct rt5659_pll_code {
};
struct rt5659_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct rt5659_platform_data pdata;
struct regmap *regmap;
- struct i2c_client *i2c;
struct gpio_desc *gpiod_ldo1_en;
struct gpio_desc *gpiod_reset;
struct snd_soc_jack *hs_jack;
struct delayed_work jack_detect_work;
+ struct clk *mclk;
int sysclk;
int sysclk_src;
@@ -1810,10 +1811,11 @@ struct rt5659_priv {
int pll_out;
int jack_type;
-
+ bool hda_hp_plugged;
+ bool hda_mic_plugged;
};
-int rt5659_set_jack_detect(struct snd_soc_codec *codec,
+int rt5659_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack);
#endif /* __RT5659_H__ */
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
new file mode 100644
index 000000000000..84cdfb810c66
--- /dev/null
+++ b/sound/soc/codecs/rt5660.c
@@ -0,0 +1,1350 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5660.c -- RT5660 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5660.h"
+
+#define RT5660_DEVICE_ID 0x6338
+
+#define RT5660_PR_RANGE_BASE (0xff + 1)
+#define RT5660_PR_SPACING 0x100
+
+#define RT5660_PR_BASE (RT5660_PR_RANGE_BASE + (0 * RT5660_PR_SPACING))
+
+static const struct regmap_range_cfg rt5660_ranges[] = {
+ { .name = "PR", .range_min = RT5660_PR_BASE,
+ .range_max = RT5660_PR_BASE + 0xf3,
+ .selector_reg = RT5660_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5660_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static const struct reg_sequence rt5660_patch[] = {
+ { RT5660_ALC_PGA_CTRL2, 0x44c3 },
+ { RT5660_PR_BASE + 0x3d, 0x2600 },
+};
+
+static const struct reg_default rt5660_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x01, 0xc800 },
+ { 0x02, 0xc8c8 },
+ { 0x0d, 0x1010 },
+ { 0x0e, 0x1010 },
+ { 0x19, 0xafaf },
+ { 0x1c, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x6060 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x4242 },
+ { 0x2f, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x45, 0xe000 },
+ { 0x46, 0x003e },
+ { 0x48, 0xf800 },
+ { 0x4a, 0x0004 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x73, 0x7000 },
+ { 0x74, 0x3c00 },
+ { 0x75, 0x2800 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x8c, 0x0228 },
+ { 0x8d, 0xa000 },
+ { 0x8e, 0x0000 },
+ { 0x92, 0x0000 },
+ { 0x93, 0x3000 },
+ { 0xa1, 0x0059 },
+ { 0xa2, 0x0001 },
+ { 0xa3, 0x5c80 },
+ { 0xa4, 0x0146 },
+ { 0xa5, 0x1f1f },
+ { 0xa6, 0x78c6 },
+ { 0xa7, 0xe5ec },
+ { 0xa8, 0xba61 },
+ { 0xa9, 0x3c78 },
+ { 0xaa, 0x8ae2 },
+ { 0xab, 0xe5ec },
+ { 0xac, 0xc600 },
+ { 0xad, 0xba61 },
+ { 0xae, 0x17ed },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb3, 0x001f },
+ { 0xb4, 0x020c },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb7, 0x4000 },
+ { 0xbb, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0100 },
+ { 0xc0, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xd3, 0xa220 },
+ { 0xd9, 0x0809 },
+ { 0xda, 0x0000 },
+ { 0xe0, 0x8000 },
+ { 0xe1, 0x0200 },
+ { 0xe2, 0x8000 },
+ { 0xe3, 0x0200 },
+ { 0xe4, 0x0f20 },
+ { 0xe5, 0x001f },
+ { 0xe6, 0x020c },
+ { 0xe7, 0x1f00 },
+ { 0xe8, 0x0000 },
+ { 0xe9, 0x4000 },
+ { 0xea, 0x00a6 },
+ { 0xeb, 0x04c3 },
+ { 0xec, 0x27c8 },
+ { 0xed, 0x7418 },
+ { 0xee, 0xbf50 },
+ { 0xef, 0x0045 },
+ { 0xf0, 0x0007 },
+ { 0xfa, 0x0000 },
+ { 0xfd, 0x0000 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6338 },
+};
+
+static bool rt5660_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5660_ranges); i++)
+ if ((reg >= rt5660_ranges[i].window_start &&
+ reg <= rt5660_ranges[i].window_start +
+ rt5660_ranges[i].window_len) ||
+ (reg >= rt5660_ranges[i].range_min &&
+ reg <= rt5660_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5660_RESET:
+ case RT5660_PRIV_DATA:
+ case RT5660_EQ_CTRL1:
+ case RT5660_IRQ_CTRL2:
+ case RT5660_INT_IRQ_ST:
+ case RT5660_VENDOR_ID:
+ case RT5660_VENDOR_ID1:
+ case RT5660_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5660_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5660_ranges); i++)
+ if ((reg >= rt5660_ranges[i].window_start &&
+ reg <= rt5660_ranges[i].window_start +
+ rt5660_ranges[i].window_len) ||
+ (reg >= rt5660_ranges[i].range_min &&
+ reg <= rt5660_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5660_RESET:
+ case RT5660_SPK_VOL:
+ case RT5660_LOUT_VOL:
+ case RT5660_IN1_IN2:
+ case RT5660_IN3_IN4:
+ case RT5660_DAC1_DIG_VOL:
+ case RT5660_STO1_ADC_DIG_VOL:
+ case RT5660_ADC_BST_VOL1:
+ case RT5660_STO1_ADC_MIXER:
+ case RT5660_AD_DA_MIXER:
+ case RT5660_STO_DAC_MIXER:
+ case RT5660_DIG_INF1_DATA:
+ case RT5660_REC_L1_MIXER:
+ case RT5660_REC_L2_MIXER:
+ case RT5660_REC_R1_MIXER:
+ case RT5660_REC_R2_MIXER:
+ case RT5660_LOUT_MIXER:
+ case RT5660_SPK_MIXER:
+ case RT5660_SPO_MIXER:
+ case RT5660_SPO_CLSD_RATIO:
+ case RT5660_OUT_L_GAIN1:
+ case RT5660_OUT_L_GAIN2:
+ case RT5660_OUT_L1_MIXER:
+ case RT5660_OUT_R_GAIN1:
+ case RT5660_OUT_R_GAIN2:
+ case RT5660_OUT_R1_MIXER:
+ case RT5660_PWR_DIG1:
+ case RT5660_PWR_DIG2:
+ case RT5660_PWR_ANLG1:
+ case RT5660_PWR_ANLG2:
+ case RT5660_PWR_MIXER:
+ case RT5660_PWR_VOL:
+ case RT5660_PRIV_INDEX:
+ case RT5660_PRIV_DATA:
+ case RT5660_I2S1_SDP:
+ case RT5660_ADDA_CLK1:
+ case RT5660_ADDA_CLK2:
+ case RT5660_DMIC_CTRL1:
+ case RT5660_GLB_CLK:
+ case RT5660_PLL_CTRL1:
+ case RT5660_PLL_CTRL2:
+ case RT5660_CLSD_AMP_OC_CTRL:
+ case RT5660_CLSD_AMP_CTRL:
+ case RT5660_LOUT_AMP_CTRL:
+ case RT5660_SPK_AMP_SPKVDD:
+ case RT5660_MICBIAS:
+ case RT5660_CLSD_OUT_CTRL1:
+ case RT5660_CLSD_OUT_CTRL2:
+ case RT5660_DIPOLE_MIC_CTRL1:
+ case RT5660_DIPOLE_MIC_CTRL2:
+ case RT5660_DIPOLE_MIC_CTRL3:
+ case RT5660_DIPOLE_MIC_CTRL4:
+ case RT5660_DIPOLE_MIC_CTRL5:
+ case RT5660_DIPOLE_MIC_CTRL6:
+ case RT5660_DIPOLE_MIC_CTRL7:
+ case RT5660_DIPOLE_MIC_CTRL8:
+ case RT5660_DIPOLE_MIC_CTRL9:
+ case RT5660_DIPOLE_MIC_CTRL10:
+ case RT5660_DIPOLE_MIC_CTRL11:
+ case RT5660_DIPOLE_MIC_CTRL12:
+ case RT5660_EQ_CTRL1:
+ case RT5660_EQ_CTRL2:
+ case RT5660_DRC_AGC_CTRL1:
+ case RT5660_DRC_AGC_CTRL2:
+ case RT5660_DRC_AGC_CTRL3:
+ case RT5660_DRC_AGC_CTRL4:
+ case RT5660_DRC_AGC_CTRL5:
+ case RT5660_JD_CTRL:
+ case RT5660_IRQ_CTRL1:
+ case RT5660_IRQ_CTRL2:
+ case RT5660_INT_IRQ_ST:
+ case RT5660_GPIO_CTRL1:
+ case RT5660_GPIO_CTRL2:
+ case RT5660_WIND_FILTER_CTRL1:
+ case RT5660_SV_ZCD1:
+ case RT5660_SV_ZCD2:
+ case RT5660_DRC1_LM_CTRL1:
+ case RT5660_DRC1_LM_CTRL2:
+ case RT5660_DRC2_LM_CTRL1:
+ case RT5660_DRC2_LM_CTRL2:
+ case RT5660_MULTI_DRC_CTRL:
+ case RT5660_DRC2_CTRL1:
+ case RT5660_DRC2_CTRL2:
+ case RT5660_DRC2_CTRL3:
+ case RT5660_DRC2_CTRL4:
+ case RT5660_DRC2_CTRL5:
+ case RT5660_ALC_PGA_CTRL1:
+ case RT5660_ALC_PGA_CTRL2:
+ case RT5660_ALC_PGA_CTRL3:
+ case RT5660_ALC_PGA_CTRL4:
+ case RT5660_ALC_PGA_CTRL5:
+ case RT5660_ALC_PGA_CTRL6:
+ case RT5660_ALC_PGA_CTRL7:
+ case RT5660_GEN_CTRL1:
+ case RT5660_GEN_CTRL2:
+ case RT5660_GEN_CTRL3:
+ case RT5660_VENDOR_ID:
+ case RT5660_VENDOR_ID1:
+ case RT5660_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(rt5660_out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rt5660_dac_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(rt5660_adc_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(rt5660_adc_bst_tlv, 0, 1200, 0);
+static const DECLARE_TLV_DB_SCALE(rt5660_bst_tlv, -1200, 75, 0);
+
+static const struct snd_kcontrol_new rt5660_snd_controls[] = {
+ /* Speaker Output Volume */
+ SOC_SINGLE("Speaker Playback Switch", RT5660_SPK_VOL, RT5660_L_MUTE_SFT,
+ 1, 1),
+ SOC_SINGLE_TLV("Speaker Playback Volume", RT5660_SPK_VOL,
+ RT5660_L_VOL_SFT, 39, 1, rt5660_out_vol_tlv),
+
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5660_LOUT_VOL, RT5660_L_MUTE_SFT,
+ RT5660_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5660_LOUT_VOL, RT5660_L_VOL_SFT,
+ RT5660_R_VOL_SFT, 39, 1, rt5660_out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5660_DAC1_DIG_VOL,
+ RT5660_DAC_L1_VOL_SFT, RT5660_DAC_R1_VOL_SFT, 87, 0,
+ rt5660_dac_vol_tlv),
+
+ /* IN1/IN2/IN3 Control */
+ SOC_SINGLE_TLV("IN1 Boost Volume", RT5660_IN1_IN2, RT5660_BST_SFT1, 69,
+ 0, rt5660_bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost Volume", RT5660_IN1_IN2, RT5660_BST_SFT2, 69,
+ 0, rt5660_bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost Volume", RT5660_IN3_IN4, RT5660_BST_SFT3, 69,
+ 0, rt5660_bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5660_STO1_ADC_DIG_VOL,
+ RT5660_L_MUTE_SFT, RT5660_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5660_STO1_ADC_DIG_VOL,
+ RT5660_ADC_L_VOL_SFT, RT5660_ADC_R_VOL_SFT, 63, 0,
+ rt5660_adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5660_ADC_BST_VOL1,
+ RT5660_STO1_ADC_L_BST_SFT, RT5660_STO1_ADC_R_BST_SFT, 3, 0,
+ rt5660_adc_bst_tlv),
+};
+
+/**
+ * rt5660_set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int rt5660_set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+ int idx, rate;
+
+ rate = rt5660->sysclk / rl6231_get_pre_div(rt5660->regmap,
+ RT5660_ADDA_CLK1, RT5660_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rate);
+ if (idx < 0)
+ dev_err(component->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_component_update_bits(component, RT5660_DMIC_CTRL1,
+ RT5660_DMIC_CLK_MASK, idx << RT5660_DMIC_CLK_SFT);
+
+ return idx;
+}
+
+static int rt5660_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, RT5660_GLB_CLK);
+ val &= RT5660_SCLK_SRC_MASK;
+ if (val == RT5660_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5660_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5660_STO1_ADC_MIXER,
+ RT5660_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5660_STO1_ADC_MIXER,
+ RT5660_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5660_STO1_ADC_MIXER,
+ RT5660_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5660_STO1_ADC_MIXER,
+ RT5660_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5660_AD_DA_MIXER,
+ RT5660_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5660_AD_DA_MIXER,
+ RT5660_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5660_AD_DA_MIXER,
+ RT5660_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5660_AD_DA_MIXER,
+ RT5660_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5660_STO_DAC_MIXER,
+ RT5660_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5660_STO_DAC_MIXER,
+ RT5660_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5660_STO_DAC_MIXER,
+ RT5660_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5660_STO_DAC_MIXER,
+ RT5660_M_DAC_L1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5660_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("BST3 Switch", RT5660_REC_L2_MIXER,
+ RT5660_M_BST3_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5660_REC_L2_MIXER,
+ RT5660_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5660_REC_L2_MIXER,
+ RT5660_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5660_REC_L2_MIXER,
+ RT5660_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("BST3 Switch", RT5660_REC_R2_MIXER,
+ RT5660_M_BST3_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5660_REC_R2_MIXER,
+ RT5660_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5660_REC_R2_MIXER,
+ RT5660_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5660_REC_R2_MIXER,
+ RT5660_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_spk_mix[] = {
+ SOC_DAPM_SINGLE("BST3 Switch", RT5660_SPK_MIXER,
+ RT5660_M_BST3_SM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5660_SPK_MIXER,
+ RT5660_M_BST1_SM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Switch", RT5660_SPK_MIXER,
+ RT5660_M_DACL_SM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Switch", RT5660_SPK_MIXER,
+ RT5660_M_DACR_SM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIXL Switch", RT5660_SPK_MIXER,
+ RT5660_M_OM_L_SM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST3 Switch", RT5660_OUT_L1_MIXER,
+ RT5660_M_BST3_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5660_OUT_L1_MIXER,
+ RT5660_M_BST2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5660_OUT_L1_MIXER,
+ RT5660_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXL Switch", RT5660_OUT_L1_MIXER,
+ RT5660_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Switch", RT5660_OUT_L1_MIXER,
+ RT5660_M_DAC_R_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Switch", RT5660_OUT_L1_MIXER,
+ RT5660_M_DAC_L_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5660_OUT_R1_MIXER,
+ RT5660_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5660_OUT_R1_MIXER,
+ RT5660_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Switch", RT5660_OUT_R1_MIXER,
+ RT5660_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Switch", RT5660_OUT_R1_MIXER,
+ RT5660_M_DAC_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Switch", RT5660_OUT_R1_MIXER,
+ RT5660_M_DAC_L_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_spo_mix[] = {
+ SOC_DAPM_SINGLE("DACR Switch", RT5660_SPO_MIXER,
+ RT5660_M_DAC_R_SPM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Switch", RT5660_SPO_MIXER,
+ RT5660_M_DAC_L_SPM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL Switch", RT5660_SPO_MIXER,
+ RT5660_M_SV_SPM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5660_SPO_MIXER,
+ RT5660_M_BST1_SPM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5660_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC Switch", RT5660_LOUT_MIXER,
+ RT5660_M_DAC1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX Switch", RT5660_LOUT_MIXER,
+ RT5660_M_LOVOL_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spk_vol_control =
+ SOC_DAPM_SINGLE("Switch", RT5660_SPK_VOL,
+ RT5660_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_l_vol_control =
+ SOC_DAPM_SINGLE("Switch", RT5660_LOUT_VOL,
+ RT5660_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_r_vol_control =
+ SOC_DAPM_SINGLE("Switch", RT5660_LOUT_VOL,
+ RT5660_VOL_R_SFT, 1, 1);
+
+/* Interface data select */
+static const char * const rt5660_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,
+ RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum,
+ RT5660_DIG_INF1_DATA, RT5660_IF1_ADC_IN_SFT, rt5660_data_select);
+
+static const struct snd_kcontrol_new rt5660_if1_dac_swap_mux =
+ SOC_DAPM_ENUM("IF1 DAC Swap Source", rt5660_if1_dac_enum);
+
+static const struct snd_kcontrol_new rt5660_if1_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC Swap Source", rt5660_if1_adc_enum);
+
+static int rt5660_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5660_LOUT_AMP_CTRL,
+ RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK,
+ RT5660_LOUT_CO_EN | RT5660_LOUT_CB_PU);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5660_LOUT_AMP_CTRL,
+ RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK,
+ RT5660_LOUT_CO_DIS | RT5660_LOUT_CB_PD);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5660_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5660_PWR_ANLG1,
+ RT5660_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5660_PWR_ANLG2,
+ RT5660_PWR_PLL_BIT, 0, NULL, 0),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5660_PWR_ANLG2,
+ RT5660_PWR_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5660_PWR_ANLG2,
+ RT5660_PWR_MB2_BIT, 0, NULL, 0),
+
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN3P"),
+ SND_SOC_DAPM_INPUT("IN3N"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ rt5660_set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC Power", RT5660_DMIC_CTRL1,
+ RT5660_DMIC_1_EN_SFT, 0, NULL, 0),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5660_PWR_ANLG2, RT5660_PWR_BST1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", RT5660_PWR_ANLG2, RT5660_PWR_BST2_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("BST3", RT5660_PWR_ANLG2, RT5660_PWR_BST3_BIT, 0,
+ NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5660_PWR_MIXER, RT5660_PWR_RM_L_BIT,
+ 0, rt5660_rec_l_mix, ARRAY_SIZE(rt5660_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5660_PWR_MIXER, RT5660_PWR_RM_R_BIT,
+ 0, rt5660_rec_r_mix, ARRAY_SIZE(rt5660_rec_r_mix)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC L power", RT5660_PWR_DIG1,
+ RT5660_PWR_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC R power", RT5660_PWR_DIG1,
+ RT5660_PWR_ADC_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC clock", RT5660_PR_BASE + RT5660_CHOP_DAC_ADC,
+ 12, 0, NULL, 0),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5660_PWR_DIG2,
+ RT5660_PWR_ADC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5660_sto1_adc_l_mix, ARRAY_SIZE(rt5660_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5660_sto1_adc_r_mix, ARRAY_SIZE(rt5660_sto1_adc_r_mix)),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5660_STO1_ADC_DIG_VOL,
+ RT5660_L_MUTE_SFT, 1),
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5660_STO1_ADC_DIG_VOL,
+ RT5660_R_MUTE_SFT, 1),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5660_PWR_DIG1, RT5660_PWR_I2S1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("IF1 DAC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5660_if1_dac_swap_mux),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("IF1 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5660_if1_adc_swap_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, rt5660_dac_l_mix,
+ ARRAY_SIZE(rt5660_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, rt5660_dac_r_mix,
+ ARRAY_SIZE(rt5660_dac_r_mix)),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5660_PWR_DIG2,
+ RT5660_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5660_sto_dac_l_mix, ARRAY_SIZE(rt5660_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5660_sto_dac_r_mix, ARRAY_SIZE(rt5660_sto_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5660_PWR_DIG1,
+ RT5660_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5660_PWR_DIG1,
+ RT5660_PWR_DAC_R1_BIT, 0),
+
+ /* OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIX", RT5660_PWR_MIXER, RT5660_PWR_SM_BIT,
+ 0, rt5660_spk_mix, ARRAY_SIZE(rt5660_spk_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5660_PWR_MIXER, RT5660_PWR_OM_L_BIT,
+ 0, rt5660_out_l_mix, ARRAY_SIZE(rt5660_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5660_PWR_MIXER, RT5660_PWR_OM_R_BIT,
+ 0, rt5660_out_r_mix, ARRAY_SIZE(rt5660_out_r_mix)),
+
+ /* Output Volume */
+ SND_SOC_DAPM_SWITCH("SPKVOL", RT5660_PWR_VOL,
+ RT5660_PWR_SV_BIT, 0, &spk_vol_control),
+ SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LOUTVOL", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("LOUTVOL L", SND_SOC_NOPM,
+ RT5660_PWR_LV_L_BIT, 0, &lout_l_vol_control),
+ SND_SOC_DAPM_SWITCH("LOUTVOL R", SND_SOC_NOPM,
+ RT5660_PWR_LV_R_BIT, 0, &lout_r_vol_control),
+
+ /* HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPO MIX", SND_SOC_NOPM, 0,
+ 0, rt5660_spo_mix, ARRAY_SIZE(rt5660_spo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
+ rt5660_lout_mix, ARRAY_SIZE(rt5660_lout_mix)),
+ SND_SOC_DAPM_SUPPLY("VREF HP", RT5660_GEN_CTRL1,
+ RT5660_PWR_VREF_HP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("LOUT amp", 1, RT5660_PWR_ANLG1,
+ RT5660_PWR_HA_BIT, 0, rt5660_lout_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("SPK amp", 1, RT5660_PWR_DIG1,
+ RT5660_PWR_CLS_D_BIT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+};
+
+static const struct snd_soc_dapm_route rt5660_dapm_routes[] = {
+ { "MICBIAS1", NULL, "LDO2" },
+ { "MICBIAS2", NULL, "LDO2" },
+
+ { "BST1", NULL, "IN1P" },
+ { "BST1", NULL, "IN1N" },
+ { "BST2", NULL, "IN2P" },
+ { "BST3", NULL, "IN3P" },
+ { "BST3", NULL, "IN3N" },
+
+ { "RECMIXL", "BST3 Switch", "BST3" },
+ { "RECMIXL", "BST2 Switch", "BST2" },
+ { "RECMIXL", "BST1 Switch", "BST1" },
+ { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" },
+
+ { "RECMIXR", "BST3 Switch", "BST3" },
+ { "RECMIXR", "BST2 Switch", "BST2" },
+ { "RECMIXR", "BST1 Switch", "BST1" },
+ { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" },
+
+ { "ADC L", NULL, "RECMIXL" },
+ { "ADC L", NULL, "ADC L power" },
+ { "ADC L", NULL, "ADC clock" },
+ { "ADC R", NULL, "RECMIXR" },
+ { "ADC R", NULL, "ADC R power" },
+ { "ADC R", NULL, "ADC clock" },
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC Power"},
+
+ { "Sto1 ADC MIXL", "ADC1 Switch", "ADC L" },
+ { "Sto1 ADC MIXL", "ADC2 Switch", "DMIC L1" },
+ { "Sto1 ADC MIXR", "ADC1 Switch", "ADC R" },
+ { "Sto1 ADC MIXR", "ADC2 Switch", "DMIC R1" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll },
+
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+ { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll },
+
+ { "IF1 ADC", NULL, "Stereo1 ADC MIXL" },
+ { "IF1 ADC", NULL, "Stereo1 ADC MIXR" },
+ { "IF1 ADC", NULL, "I2S1" },
+
+ { "IF1 ADC Swap Mux", "L/R", "IF1 ADC" },
+ { "IF1 ADC Swap Mux", "R/L", "IF1 ADC" },
+ { "IF1 ADC Swap Mux", "L/L", "IF1 ADC" },
+ { "IF1 ADC Swap Mux", "R/R", "IF1 ADC" },
+ { "AIF1TX", NULL, "IF1 ADC Swap Mux" },
+
+ { "IF1 DAC", NULL, "AIF1RX" },
+ { "IF1 DAC", NULL, "I2S1" },
+
+ { "IF1 DAC Swap Mux", "L/R", "IF1 DAC" },
+ { "IF1 DAC Swap Mux", "R/L", "IF1 DAC" },
+ { "IF1 DAC Swap Mux", "L/L", "IF1 DAC" },
+ { "IF1 DAC Swap Mux", "R/R", "IF1 DAC" },
+
+ { "IF1 DAC L", NULL, "IF1 DAC Swap Mux" },
+ { "IF1 DAC R", NULL, "IF1 DAC Swap Mux" },
+
+ { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
+ { "DAC1 MIXL", "DAC1 Switch", "IF1 DAC L" },
+ { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" },
+ { "DAC1 MIXR", "DAC1 Switch", "IF1 DAC R" },
+
+ { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXL", NULL, "dac stereo1 filter" },
+ { "dac stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll },
+ { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXR", NULL, "dac stereo1 filter" },
+ { "dac stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll },
+
+ { "DAC L1", NULL, "Stereo DAC MIXL" },
+ { "DAC R1", NULL, "Stereo DAC MIXR" },
+
+ { "SPK MIX", "BST3 Switch", "BST3" },
+ { "SPK MIX", "BST1 Switch", "BST1" },
+ { "SPK MIX", "DACL Switch", "DAC L1" },
+ { "SPK MIX", "DACR Switch", "DAC R1" },
+ { "SPK MIX", "OUTMIXL Switch", "OUT MIXL" },
+
+ { "OUT MIXL", "BST3 Switch", "BST3" },
+ { "OUT MIXL", "BST2 Switch", "BST2" },
+ { "OUT MIXL", "BST1 Switch", "BST1" },
+ { "OUT MIXL", "RECMIXL Switch", "RECMIXL" },
+ { "OUT MIXL", "DACR Switch", "DAC R1" },
+ { "OUT MIXL", "DACL Switch", "DAC L1" },
+
+ { "OUT MIXR", "BST2 Switch", "BST2" },
+ { "OUT MIXR", "BST1 Switch", "BST1" },
+ { "OUT MIXR", "RECMIXR Switch", "RECMIXR" },
+ { "OUT MIXR", "DACR Switch", "DAC R1" },
+ { "OUT MIXR", "DACL Switch", "DAC L1" },
+
+ { "SPO MIX", "DACR Switch", "DAC R1" },
+ { "SPO MIX", "DACL Switch", "DAC L1" },
+ { "SPO MIX", "SPKVOL Switch", "SPKVOL" },
+ { "SPO MIX", "BST1 Switch", "BST1" },
+
+ { "SPKVOL", "Switch", "SPK MIX" },
+ { "LOUTVOL L", "Switch", "OUT MIXL" },
+ { "LOUTVOL R", "Switch", "OUT MIXR" },
+
+ { "LOUTVOL", NULL, "LOUTVOL L" },
+ { "LOUTVOL", NULL, "LOUTVOL R" },
+
+ { "DAC 1", NULL, "DAC L1" },
+ { "DAC 1", NULL, "DAC R1" },
+
+ { "LOUT MIX", "DAC Switch", "DAC 1" },
+ { "LOUT MIX", "OUTMIX Switch", "LOUTVOL" },
+
+ { "LOUT amp", NULL, "LOUT MIX" },
+ { "LOUT amp", NULL, "VREF HP" },
+ { "LOUTL", NULL, "LOUT amp" },
+ { "LOUTR", NULL, "LOUT amp" },
+
+ { "SPK amp", NULL, "SPO MIX" },
+ { "SPO", NULL, "SPK amp" },
+};
+
+static int rt5660_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt5660->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5660->sysclk, rt5660->lrck[dai->id]);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
+ rt5660->lrck[dai->id], dai->id);
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return frame_size;
+ }
+
+ if (frame_size > 32)
+ bclk_ms = 1;
+ else
+ bclk_ms = 0;
+
+ rt5660->bclk[dai->id] = rt5660->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5660->bclk[dai->id], rt5660->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= RT5660_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= RT5660_I2S_DL_24;
+ break;
+ case 8:
+ val_len |= RT5660_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5660_AIF1:
+ mask_clk = RT5660_I2S_BCLK_MS1_MASK | RT5660_I2S_PD1_MASK;
+ val_clk = bclk_ms << RT5660_I2S_BCLK_MS1_SFT |
+ pre_div << RT5660_I2S_PD1_SFT;
+ snd_soc_component_update_bits(component, RT5660_I2S1_SDP, RT5660_I2S_DL_MASK,
+ val_len);
+ snd_soc_component_update_bits(component, RT5660_ADDA_CLK1, mask_clk, val_clk);
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5660->master[dai->id] = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT5660_I2S_MS_S;
+ rt5660->master[dai->id] = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5660_I2S_BP_INV;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5660_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5660_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5660_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5660_AIF1:
+ snd_soc_component_update_bits(component, RT5660_I2S1_SDP,
+ RT5660_I2S_MS_MASK | RT5660_I2S_BP_MASK |
+ RT5660_I2S_DF_MASK, reg_val);
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5660_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5660->sysclk && clk_id == rt5660->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5660_SCLK_S_MCLK:
+ reg_val |= RT5660_SCLK_SRC_MCLK;
+ break;
+
+ case RT5660_SCLK_S_PLL1:
+ reg_val |= RT5660_SCLK_SRC_PLL1;
+ break;
+
+ case RT5660_SCLK_S_RCCLK:
+ reg_val |= RT5660_SCLK_SRC_RCCLK;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK, RT5660_SCLK_SRC_MASK,
+ reg_val);
+
+ rt5660->sysclk = freq;
+ rt5660->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5660->pll_src && freq_in == rt5660->pll_in &&
+ freq_out == rt5660->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5660->pll_in = 0;
+ rt5660->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK,
+ RT5660_SCLK_SRC_MASK, RT5660_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5660_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK,
+ RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_MCLK);
+ break;
+
+ case RT5660_PLL1_S_BCLK:
+ snd_soc_component_update_bits(component, RT5660_GLB_CLK,
+ RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_BCLK1);
+ break;
+
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT5660_PLL_CTRL1,
+ pll_code.n_code << RT5660_PLL_N_SFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5660_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5660_PLL_M_SFT) |
+ (pll_code.m_bp << RT5660_PLL_M_BP_SFT));
+
+ rt5660->pll_in = freq_in;
+ rt5660->pll_out = freq_out;
+ rt5660->pll_src = source;
+
+ return 0;
+}
+
+static int rt5660_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, RT5660_GEN_CTRL1,
+ RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL);
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5660->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5660->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, RT5660_PWR_ANLG1,
+ RT5660_PWR_VREF1 | RT5660_PWR_MB |
+ RT5660_PWR_BG | RT5660_PWR_VREF2,
+ RT5660_PWR_VREF1 | RT5660_PWR_MB |
+ RT5660_PWR_BG | RT5660_PWR_VREF2);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5660_PWR_ANLG1,
+ RT5660_PWR_FV1 | RT5660_PWR_FV2,
+ RT5660_PWR_FV1 | RT5660_PWR_FV2);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, RT5660_GEN_CTRL1,
+ RT5660_DIG_GATE_CTRL, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5660_probe(struct snd_soc_component *component)
+{
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+
+ rt5660->component = component;
+
+ return 0;
+}
+
+static void rt5660_remove(struct snd_soc_component *component)
+{
+ snd_soc_component_write(component, RT5660_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt5660_suspend(struct snd_soc_component *component)
+{
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5660->regmap, true);
+ regcache_mark_dirty(rt5660->regmap);
+
+ return 0;
+}
+
+static int rt5660_resume(struct snd_soc_component *component)
+{
+ struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component);
+
+ if (rt5660->pdata.poweroff_codec_in_suspend)
+ msleep(350);
+
+ regcache_cache_only(rt5660->regmap, false);
+ regcache_sync(rt5660->regmap);
+
+ return 0;
+}
+#else
+#define rt5660_suspend NULL
+#define rt5660_resume NULL
+#endif
+
+#define RT5660_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5660_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5660_aif_dai_ops = {
+ .hw_params = rt5660_hw_params,
+ .set_fmt = rt5660_set_dai_fmt,
+ .set_sysclk = rt5660_set_dai_sysclk,
+ .set_pll = rt5660_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5660_dai[] = {
+ {
+ .name = "rt5660-aif1",
+ .id = RT5660_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5660_STEREO_RATES,
+ .formats = RT5660_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5660_STEREO_RATES,
+ .formats = RT5660_FORMATS,
+ },
+ .ops = &rt5660_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5660 = {
+ .probe = rt5660_probe,
+ .remove = rt5660_remove,
+ .suspend = rt5660_suspend,
+ .resume = rt5660_resume,
+ .set_bias_level = rt5660_set_bias_level,
+ .controls = rt5660_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5660_snd_controls),
+ .dapm_widgets = rt5660_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5660_dapm_widgets),
+ .dapm_routes = rt5660_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5660_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5660_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_read = true,
+ .use_single_write = true,
+
+ .max_register = RT5660_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5660_ranges) *
+ RT5660_PR_SPACING),
+ .volatile_reg = rt5660_volatile_register,
+ .readable_reg = rt5660_readable_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5660_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5660_reg),
+ .ranges = rt5660_ranges,
+ .num_ranges = ARRAY_SIZE(rt5660_ranges),
+};
+
+static const struct i2c_device_id rt5660_i2c_id[] = {
+ { "rt5660" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt5660_of_match[] = {
+ { .compatible = "realtek,rt5660", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5660_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5660_acpi_match[] = {
+ { "10EC3277" },
+ { "10EC5660" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match);
+#endif
+
+static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev)
+{
+ rt5660->pdata.in1_diff = device_property_read_bool(dev,
+ "realtek,in1-differential");
+ rt5660->pdata.in3_diff = device_property_read_bool(dev,
+ "realtek,in3-differential");
+ rt5660->pdata.poweroff_codec_in_suspend = device_property_read_bool(dev,
+ "realtek,poweroff-in-suspend");
+ device_property_read_u32(dev, "realtek,dmic1-data-pin",
+ &rt5660->pdata.dmic1_data_pin);
+
+ return 0;
+}
+
+static int rt5660_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5660_priv *rt5660;
+ int ret;
+ unsigned int val;
+
+ rt5660 = devm_kzalloc(&i2c->dev, sizeof(struct rt5660_priv),
+ GFP_KERNEL);
+
+ if (rt5660 == NULL)
+ return -ENOMEM;
+
+ /* Check if MCLK provided */
+ rt5660->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5660->mclk))
+ return PTR_ERR(rt5660->mclk);
+
+ i2c_set_clientdata(i2c, rt5660);
+
+ if (pdata)
+ rt5660->pdata = *pdata;
+ else if (i2c->dev.of_node)
+ rt5660_parse_dt(rt5660, &i2c->dev);
+
+ rt5660->regmap = devm_regmap_init_i2c(i2c, &rt5660_regmap);
+ if (IS_ERR(rt5660->regmap)) {
+ ret = PTR_ERR(rt5660->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5660->regmap, RT5660_VENDOR_ID2, &val);
+ if (val != RT5660_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5660\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5660->regmap, RT5660_RESET, 0);
+
+ ret = regmap_register_patch(rt5660->regmap, rt5660_patch,
+ ARRAY_SIZE(rt5660_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ regmap_update_bits(rt5660->regmap, RT5660_GEN_CTRL1,
+ RT5660_AUTO_DIS_AMP | RT5660_MCLK_DET | RT5660_POW_CLKDET,
+ RT5660_AUTO_DIS_AMP | RT5660_MCLK_DET | RT5660_POW_CLKDET);
+
+ if (rt5660->pdata.dmic1_data_pin) {
+ regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
+ RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL);
+
+ if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) {
+ regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
+ RT5660_SEL_DMIC_DATA_MASK,
+ RT5660_SEL_DMIC_DATA_GPIO2);
+ regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
+ RT5660_GP2_PIN_MASK, RT5660_GP2_PIN_DMIC1_SDA);
+ } else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) {
+ regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
+ RT5660_SEL_DMIC_DATA_MASK,
+ RT5660_SEL_DMIC_DATA_IN1P);
+ }
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5660,
+ rt5660_dai, ARRAY_SIZE(rt5660_dai));
+}
+
+static struct i2c_driver rt5660_i2c_driver = {
+ .driver = {
+ .name = "rt5660",
+ .acpi_match_table = ACPI_PTR(rt5660_acpi_match),
+ .of_match_table = of_match_ptr(rt5660_of_match),
+ },
+ .probe = rt5660_i2c_probe,
+ .id_table = rt5660_i2c_id,
+};
+module_i2c_driver(rt5660_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5660 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5660.h b/sound/soc/codecs/rt5660.h
new file mode 100644
index 000000000000..a33025c920e1
--- /dev/null
+++ b/sound/soc/codecs/rt5660.h
@@ -0,0 +1,847 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5660.h -- RT5660 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#ifndef _RT5660_H
+#define _RT5660_H
+
+#include <linux/clk.h>
+#include <sound/rt5660.h>
+
+/* Info */
+#define RT5660_RESET 0x00
+#define RT5660_VENDOR_ID 0xfd
+#define RT5660_VENDOR_ID1 0xfe
+#define RT5660_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5660_SPK_VOL 0x01
+#define RT5660_LOUT_VOL 0x02
+/* I/O - Input */
+#define RT5660_IN1_IN2 0x0d
+#define RT5660_IN3_IN4 0x0e
+/* I/O - ADC/DAC/DMIC */
+#define RT5660_DAC1_DIG_VOL 0x19
+#define RT5660_STO1_ADC_DIG_VOL 0x1c
+#define RT5660_ADC_BST_VOL1 0x1e
+/* Mixer - D-D */
+#define RT5660_STO1_ADC_MIXER 0x27
+#define RT5660_AD_DA_MIXER 0x29
+#define RT5660_STO_DAC_MIXER 0x2a
+#define RT5660_DIG_INF1_DATA 0x2f
+/* Mixer - ADC */
+#define RT5660_REC_L1_MIXER 0x3b
+#define RT5660_REC_L2_MIXER 0x3c
+#define RT5660_REC_R1_MIXER 0x3d
+#define RT5660_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5660_LOUT_MIXER 0x45
+#define RT5660_SPK_MIXER 0x46
+#define RT5660_SPO_MIXER 0x48
+#define RT5660_SPO_CLSD_RATIO 0x4a
+#define RT5660_OUT_L_GAIN1 0x4d
+#define RT5660_OUT_L_GAIN2 0x4e
+#define RT5660_OUT_L1_MIXER 0x4f
+#define RT5660_OUT_R_GAIN1 0x50
+#define RT5660_OUT_R_GAIN2 0x51
+#define RT5660_OUT_R1_MIXER 0x52
+/* Power */
+#define RT5660_PWR_DIG1 0x61
+#define RT5660_PWR_DIG2 0x62
+#define RT5660_PWR_ANLG1 0x63
+#define RT5660_PWR_ANLG2 0x64
+#define RT5660_PWR_MIXER 0x65
+#define RT5660_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5660_PRIV_INDEX 0x6a
+#define RT5660_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5660_I2S1_SDP 0x70
+#define RT5660_ADDA_CLK1 0x73
+#define RT5660_ADDA_CLK2 0x74
+#define RT5660_DMIC_CTRL1 0x75
+/* Function - Analog */
+#define RT5660_GLB_CLK 0x80
+#define RT5660_PLL_CTRL1 0x81
+#define RT5660_PLL_CTRL2 0x82
+#define RT5660_CLSD_AMP_OC_CTRL 0x8c
+#define RT5660_CLSD_AMP_CTRL 0x8d
+#define RT5660_LOUT_AMP_CTRL 0x8e
+#define RT5660_SPK_AMP_SPKVDD 0x92
+#define RT5660_MICBIAS 0x93
+#define RT5660_CLSD_OUT_CTRL1 0xa1
+#define RT5660_CLSD_OUT_CTRL2 0xa2
+#define RT5660_DIPOLE_MIC_CTRL1 0xa3
+#define RT5660_DIPOLE_MIC_CTRL2 0xa4
+#define RT5660_DIPOLE_MIC_CTRL3 0xa5
+#define RT5660_DIPOLE_MIC_CTRL4 0xa6
+#define RT5660_DIPOLE_MIC_CTRL5 0xa7
+#define RT5660_DIPOLE_MIC_CTRL6 0xa8
+#define RT5660_DIPOLE_MIC_CTRL7 0xa9
+#define RT5660_DIPOLE_MIC_CTRL8 0xaa
+#define RT5660_DIPOLE_MIC_CTRL9 0xab
+#define RT5660_DIPOLE_MIC_CTRL10 0xac
+#define RT5660_DIPOLE_MIC_CTRL11 0xad
+#define RT5660_DIPOLE_MIC_CTRL12 0xae
+/* Function - Digital */
+#define RT5660_EQ_CTRL1 0xb0
+#define RT5660_EQ_CTRL2 0xb1
+#define RT5660_DRC_AGC_CTRL1 0xb3
+#define RT5660_DRC_AGC_CTRL2 0xb4
+#define RT5660_DRC_AGC_CTRL3 0xb5
+#define RT5660_DRC_AGC_CTRL4 0xb6
+#define RT5660_DRC_AGC_CTRL5 0xb7
+#define RT5660_JD_CTRL 0xbb
+#define RT5660_IRQ_CTRL1 0xbd
+#define RT5660_IRQ_CTRL2 0xbe
+#define RT5660_INT_IRQ_ST 0xbf
+#define RT5660_GPIO_CTRL1 0xc0
+#define RT5660_GPIO_CTRL2 0xc2
+#define RT5660_WIND_FILTER_CTRL1 0xd3
+#define RT5660_SV_ZCD1 0xd9
+#define RT5660_SV_ZCD2 0xda
+#define RT5660_DRC1_LM_CTRL1 0xe0
+#define RT5660_DRC1_LM_CTRL2 0xe1
+#define RT5660_DRC2_LM_CTRL1 0xe2
+#define RT5660_DRC2_LM_CTRL2 0xe3
+#define RT5660_MULTI_DRC_CTRL 0xe4
+#define RT5660_DRC2_CTRL1 0xe5
+#define RT5660_DRC2_CTRL2 0xe6
+#define RT5660_DRC2_CTRL3 0xe7
+#define RT5660_DRC2_CTRL4 0xe8
+#define RT5660_DRC2_CTRL5 0xe9
+#define RT5660_ALC_PGA_CTRL1 0xea
+#define RT5660_ALC_PGA_CTRL2 0xeb
+#define RT5660_ALC_PGA_CTRL3 0xec
+#define RT5660_ALC_PGA_CTRL4 0xed
+#define RT5660_ALC_PGA_CTRL5 0xee
+#define RT5660_ALC_PGA_CTRL6 0xef
+#define RT5660_ALC_PGA_CTRL7 0xf0
+
+/* General Control */
+#define RT5660_GEN_CTRL1 0xfa
+#define RT5660_GEN_CTRL2 0xfb
+#define RT5660_GEN_CTRL3 0xfc
+
+/* Index of Codec Private Register definition */
+#define RT5660_CHOP_DAC_ADC 0x3d
+
+/* Global Definition */
+#define RT5660_L_MUTE (0x1 << 15)
+#define RT5660_L_MUTE_SFT 15
+#define RT5660_VOL_L_MUTE (0x1 << 14)
+#define RT5660_VOL_L_SFT 14
+#define RT5660_R_MUTE (0x1 << 7)
+#define RT5660_R_MUTE_SFT 7
+#define RT5660_VOL_R_MUTE (0x1 << 6)
+#define RT5660_VOL_R_SFT 6
+#define RT5660_L_VOL_MASK (0x3f << 8)
+#define RT5660_L_VOL_SFT 8
+#define RT5660_R_VOL_MASK (0x3f)
+#define RT5660_R_VOL_SFT 0
+
+/* IN1 and IN2 Control (0x0d) */
+#define RT5660_IN_DF1 (0x1 << 15)
+#define RT5660_IN_SFT1 15
+#define RT5660_BST_MASK1 (0x7f << 8)
+#define RT5660_BST_SFT1 8
+#define RT5660_IN_DF2 (0x1 << 7)
+#define RT5660_IN_SFT2 7
+#define RT5660_BST_MASK2 (0x7f << 0)
+#define RT5660_BST_SFT2 0
+
+/* IN3 and IN4 Control (0x0e) */
+#define RT5660_IN_DF3 (0x1 << 15)
+#define RT5660_IN_SFT3 15
+#define RT5660_BST_MASK3 (0x7f << 8)
+#define RT5660_BST_SFT3 8
+#define RT5660_IN_DF4 (0x1 << 7)
+#define RT5660_IN_SFT4 7
+#define RT5660_BST_MASK4 (0x7f << 0)
+#define RT5660_BST_SFT4 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5660_DAC_L1_VOL_MASK (0x7f << 9)
+#define RT5660_DAC_L1_VOL_SFT 9
+#define RT5660_DAC_R1_VOL_MASK (0x7f << 1)
+#define RT5660_DAC_R1_VOL_SFT 1
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5660_ADC_L_VOL_MASK (0x3f << 9)
+#define RT5660_ADC_L_VOL_SFT 9
+#define RT5660_ADC_R_VOL_MASK (0x3f << 1)
+#define RT5660_ADC_R_VOL_SFT 1
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5660_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5660_STO1_ADC_L_BST_SFT 14
+#define RT5660_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5660_STO1_ADC_R_BST_SFT 12
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5660_M_ADC_L1 (0x1 << 14)
+#define RT5660_M_ADC_L1_SFT 14
+#define RT5660_M_ADC_L2 (0x1 << 13)
+#define RT5660_M_ADC_L2_SFT 13
+#define RT5660_M_ADC_R1 (0x1 << 6)
+#define RT5660_M_ADC_R1_SFT 6
+#define RT5660_M_ADC_R2 (0x1 << 5)
+#define RT5660_M_ADC_R2_SFT 5
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5660_M_ADCMIX_L (0x1 << 15)
+#define RT5660_M_ADCMIX_L_SFT 15
+#define RT5660_M_DAC1_L (0x1 << 14)
+#define RT5660_M_DAC1_L_SFT 14
+#define RT5660_M_ADCMIX_R (0x1 << 7)
+#define RT5660_M_ADCMIX_R_SFT 7
+#define RT5660_M_DAC1_R (0x1 << 6)
+#define RT5660_M_DAC1_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5660_M_DAC_L1 (0x1 << 14)
+#define RT5660_M_DAC_L1_SFT 14
+#define RT5660_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5660_DAC_L1_STO_L_VOL_SFT 13
+#define RT5660_M_DAC_R1_STO_L (0x1 << 9)
+#define RT5660_M_DAC_R1_STO_L_SFT 9
+#define RT5660_DAC_R1_STO_L_VOL_MASK (0x1 << 8)
+#define RT5660_DAC_R1_STO_L_VOL_SFT 8
+#define RT5660_M_DAC_R1 (0x1 << 6)
+#define RT5660_M_DAC_R1_SFT 6
+#define RT5660_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5660_DAC_R1_STO_R_VOL_SFT 5
+#define RT5660_M_DAC_L1_STO_R (0x1 << 1)
+#define RT5660_M_DAC_L1_STO_R_SFT 1
+#define RT5660_DAC_L1_STO_R_VOL_MASK (0x1)
+#define RT5660_DAC_L1_STO_R_VOL_SFT 0
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5660_IF1_DAC_IN_SEL (0x3 << 14)
+#define RT5660_IF1_DAC_IN_SFT 14
+#define RT5660_IF1_ADC_IN_SEL (0x3 << 12)
+#define RT5660_IF1_ADC_IN_SFT 12
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5660_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5660_G_BST3_RM_L_SFT 4
+#define RT5660_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5660_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5660_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5660_G_BST1_RM_L_SFT 13
+#define RT5660_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5660_G_OM_L_RM_L_SFT 10
+#define RT5660_M_BST3_RM_L (0x1 << 3)
+#define RT5660_M_BST3_RM_L_SFT 3
+#define RT5660_M_BST2_RM_L (0x1 << 2)
+#define RT5660_M_BST2_RM_L_SFT 2
+#define RT5660_M_BST1_RM_L (0x1 << 1)
+#define RT5660_M_BST1_RM_L_SFT 1
+#define RT5660_M_OM_L_RM_L (0x1)
+#define RT5660_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5660_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5660_G_BST3_RM_R_SFT 4
+#define RT5660_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5660_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5660_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5660_G_BST1_RM_R_SFT 13
+#define RT5660_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5660_G_OM_R_RM_R_SFT 10
+#define RT5660_M_BST3_RM_R (0x1 << 3)
+#define RT5660_M_BST3_RM_R_SFT 3
+#define RT5660_M_BST2_RM_R (0x1 << 2)
+#define RT5660_M_BST2_RM_R_SFT 2
+#define RT5660_M_BST1_RM_R (0x1 << 1)
+#define RT5660_M_BST1_RM_R_SFT 1
+#define RT5660_M_OM_R_RM_R (0x1)
+#define RT5660_M_OM_R_RM_R_SFT 0
+
+/* LOUTMIX Control (0x45) */
+#define RT5660_M_DAC1_LM (0x1 << 14)
+#define RT5660_M_DAC1_LM_SFT 14
+#define RT5660_M_LOVOL_M (0x1 << 13)
+#define RT5660_M_LOVOL_LM_SFT 13
+
+/* SPK Mixer Control (0x46) */
+#define RT5660_G_BST3_SM_MASK (0x3 << 14)
+#define RT5660_G_BST3_SM_SFT 14
+#define RT5660_G_BST1_SM_MASK (0x3 << 12)
+#define RT5660_G_BST1_SM_SFT 12
+#define RT5660_G_DACl_SM_MASK (0x3 << 10)
+#define RT5660_G_DACl_SM_SFT 10
+#define RT5660_G_DACR_SM_MASK (0x3 << 8)
+#define RT5660_G_DACR_SM_SFT 8
+#define RT5660_G_OM_L_SM_MASK (0x3 << 6)
+#define RT5660_G_OM_L_SM_SFT 6
+#define RT5660_M_DACR_SM (0x1 << 5)
+#define RT5660_M_DACR_SM_SFT 5
+#define RT5660_M_BST1_SM (0x1 << 4)
+#define RT5660_M_BST1_SM_SFT 4
+#define RT5660_M_BST3_SM (0x1 << 3)
+#define RT5660_M_BST3_SM_SFT 3
+#define RT5660_M_DACL_SM (0x1 << 2)
+#define RT5660_M_DACL_SM_SFT 2
+#define RT5660_M_OM_L_SM (0x1 << 1)
+#define RT5660_M_OM_L_SM_SFT 1
+
+/* SPOMIX Control (0x48) */
+#define RT5660_M_DAC_R_SPM (0x1 << 14)
+#define RT5660_M_DAC_R_SPM_SFT 14
+#define RT5660_M_DAC_L_SPM (0x1 << 13)
+#define RT5660_M_DAC_L_SPM_SFT 13
+#define RT5660_M_SV_SPM (0x1 << 12)
+#define RT5660_M_SV_SPM_SFT 12
+#define RT5660_M_BST1_SPM (0x1 << 11)
+#define RT5660_M_BST1_SPM_SFT 11
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5660_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5660_G_BST3_OM_L_SFT 13
+#define RT5660_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5660_G_BST2_OM_L_SFT 10
+#define RT5660_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5660_G_BST1_OM_L_SFT 7
+#define RT5660_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5660_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5660_G_DAC_R1_OM_L_MASK (0x7 << 10)
+#define RT5660_G_DAC_R1_OM_L_SFT 10
+#define RT5660_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5660_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5660_M_BST3_OM_L (0x1 << 5)
+#define RT5660_M_BST3_OM_L_SFT 5
+#define RT5660_M_BST2_OM_L (0x1 << 4)
+#define RT5660_M_BST2_OM_L_SFT 4
+#define RT5660_M_BST1_OM_L (0x1 << 3)
+#define RT5660_M_BST1_OM_L_SFT 3
+#define RT5660_M_RM_L_OM_L (0x1 << 2)
+#define RT5660_M_RM_L_OM_L_SFT 2
+#define RT5660_M_DAC_R_OM_L (0x1 << 1)
+#define RT5660_M_DAC_R_OM_L_SFT 1
+#define RT5660_M_DAC_L_OM_L (0x1)
+#define RT5660_M_DAC_L_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5660_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5660_G_BST2_OM_R_SFT 10
+#define RT5660_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5660_G_BST1_OM_R_SFT 7
+#define RT5660_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5660_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5660_G_DAC_L_OM_R_MASK (0x7 << 10)
+#define RT5660_G_DAC_L_OM_R_SFT 10
+#define RT5660_G_DAC_R_OM_R_MASK (0x7 << 7)
+#define RT5660_G_DAC_R_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5660_M_BST2_OM_R (0x1 << 4)
+#define RT5660_M_BST2_OM_R_SFT 4
+#define RT5660_M_BST1_OM_R (0x1 << 3)
+#define RT5660_M_BST1_OM_R_SFT 3
+#define RT5660_M_RM_R_OM_R (0x1 << 2)
+#define RT5660_M_RM_R_OM_R_SFT 2
+#define RT5660_M_DAC_L_OM_R (0x1 << 1)
+#define RT5660_M_DAC_L_OM_R_SFT 1
+#define RT5660_M_DAC_R_OM_R (0x1)
+#define RT5660_M_DAC_R_OM_R_SFT 0
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5660_PWR_I2S1 (0x1 << 15)
+#define RT5660_PWR_I2S1_BIT 15
+#define RT5660_PWR_DAC_L1 (0x1 << 12)
+#define RT5660_PWR_DAC_L1_BIT 12
+#define RT5660_PWR_DAC_R1 (0x1 << 11)
+#define RT5660_PWR_DAC_R1_BIT 11
+#define RT5660_PWR_ADC_L (0x1 << 2)
+#define RT5660_PWR_ADC_L_BIT 2
+#define RT5660_PWR_ADC_R (0x1 << 1)
+#define RT5660_PWR_ADC_R_BIT 1
+#define RT5660_PWR_CLS_D (0x1)
+#define RT5660_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5660_PWR_ADC_S1F (0x1 << 15)
+#define RT5660_PWR_ADC_S1F_BIT 15
+#define RT5660_PWR_DAC_S1F (0x1 << 11)
+#define RT5660_PWR_DAC_S1F_BIT 11
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5660_PWR_VREF1 (0x1 << 15)
+#define RT5660_PWR_VREF1_BIT 15
+#define RT5660_PWR_FV1 (0x1 << 14)
+#define RT5660_PWR_FV1_BIT 14
+#define RT5660_PWR_MB (0x1 << 13)
+#define RT5660_PWR_MB_BIT 13
+#define RT5660_PWR_BG (0x1 << 11)
+#define RT5660_PWR_BG_BIT 11
+#define RT5660_PWR_HP_L (0x1 << 7)
+#define RT5660_PWR_HP_L_BIT 7
+#define RT5660_PWR_HP_R (0x1 << 6)
+#define RT5660_PWR_HP_R_BIT 6
+#define RT5660_PWR_HA (0x1 << 5)
+#define RT5660_PWR_HA_BIT 5
+#define RT5660_PWR_VREF2 (0x1 << 4)
+#define RT5660_PWR_VREF2_BIT 4
+#define RT5660_PWR_FV2 (0x1 << 3)
+#define RT5660_PWR_FV2_BIT 3
+#define RT5660_PWR_LDO2 (0x1 << 2)
+#define RT5660_PWR_LDO2_BIT 2
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5660_PWR_BST1 (0x1 << 15)
+#define RT5660_PWR_BST1_BIT 15
+#define RT5660_PWR_BST2 (0x1 << 14)
+#define RT5660_PWR_BST2_BIT 14
+#define RT5660_PWR_BST3 (0x1 << 13)
+#define RT5660_PWR_BST3_BIT 13
+#define RT5660_PWR_MB1 (0x1 << 11)
+#define RT5660_PWR_MB1_BIT 11
+#define RT5660_PWR_MB2 (0x1 << 10)
+#define RT5660_PWR_MB2_BIT 10
+#define RT5660_PWR_PLL (0x1 << 9)
+#define RT5660_PWR_PLL_BIT 9
+
+/* Power Management for Mixer (0x65) */
+#define RT5660_PWR_OM_L (0x1 << 15)
+#define RT5660_PWR_OM_L_BIT 15
+#define RT5660_PWR_OM_R (0x1 << 14)
+#define RT5660_PWR_OM_R_BIT 14
+#define RT5660_PWR_SM (0x1 << 13)
+#define RT5660_PWR_SM_BIT 13
+#define RT5660_PWR_RM_L (0x1 << 11)
+#define RT5660_PWR_RM_L_BIT 11
+#define RT5660_PWR_RM_R (0x1 << 10)
+#define RT5660_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5660_PWR_SV (0x1 << 15)
+#define RT5660_PWR_SV_BIT 15
+#define RT5660_PWR_LV_L (0x1 << 11)
+#define RT5660_PWR_LV_L_BIT 11
+#define RT5660_PWR_LV_R (0x1 << 10)
+#define RT5660_PWR_LV_R_BIT 10
+
+/* I2S1 Audio Serial Data Port Control (0x70) */
+#define RT5660_I2S_MS_MASK (0x1 << 15)
+#define RT5660_I2S_MS_SFT 15
+#define RT5660_I2S_MS_M (0x0 << 15)
+#define RT5660_I2S_MS_S (0x1 << 15)
+#define RT5660_I2S_O_CP_MASK (0x3 << 10)
+#define RT5660_I2S_O_CP_SFT 10
+#define RT5660_I2S_O_CP_OFF (0x0 << 10)
+#define RT5660_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5660_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5660_I2S_I_CP_MASK (0x3 << 8)
+#define RT5660_I2S_I_CP_SFT 8
+#define RT5660_I2S_I_CP_OFF (0x0 << 8)
+#define RT5660_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5660_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5660_I2S_BP_MASK (0x1 << 7)
+#define RT5660_I2S_BP_SFT 7
+#define RT5660_I2S_BP_NOR (0x0 << 7)
+#define RT5660_I2S_BP_INV (0x1 << 7)
+#define RT5660_I2S_DL_MASK (0x3 << 2)
+#define RT5660_I2S_DL_SFT 2
+#define RT5660_I2S_DL_16 (0x0 << 2)
+#define RT5660_I2S_DL_20 (0x1 << 2)
+#define RT5660_I2S_DL_24 (0x2 << 2)
+#define RT5660_I2S_DL_8 (0x3 << 2)
+#define RT5660_I2S_DF_MASK (0x3)
+#define RT5660_I2S_DF_SFT 0
+#define RT5660_I2S_DF_I2S (0x0)
+#define RT5660_I2S_DF_LEFT (0x1)
+#define RT5660_I2S_DF_PCM_A (0x2)
+#define RT5660_I2S_DF_PCM_B (0x3)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5660_I2S_BCLK_MS1_MASK (0x1 << 15)
+#define RT5660_I2S_BCLK_MS1_SFT 15
+#define RT5660_I2S_BCLK_MS1_32 (0x0 << 15)
+#define RT5660_I2S_BCLK_MS1_64 (0x1 << 15)
+#define RT5660_I2S_PD1_MASK (0x7 << 12)
+#define RT5660_I2S_PD1_SFT 12
+#define RT5660_I2S_PD1_1 (0x0 << 12)
+#define RT5660_I2S_PD1_2 (0x1 << 12)
+#define RT5660_I2S_PD1_3 (0x2 << 12)
+#define RT5660_I2S_PD1_4 (0x3 << 12)
+#define RT5660_I2S_PD1_6 (0x4 << 12)
+#define RT5660_I2S_PD1_8 (0x5 << 12)
+#define RT5660_I2S_PD1_12 (0x6 << 12)
+#define RT5660_I2S_PD1_16 (0x7 << 12)
+#define RT5660_DAC_OSR_MASK (0x3 << 2)
+#define RT5660_DAC_OSR_SFT 2
+#define RT5660_DAC_OSR_128 (0x0 << 2)
+#define RT5660_DAC_OSR_64 (0x1 << 2)
+#define RT5660_DAC_OSR_32 (0x2 << 2)
+#define RT5660_DAC_OSR_16 (0x3 << 2)
+#define RT5660_ADC_OSR_MASK (0x3)
+#define RT5660_ADC_OSR_SFT 0
+#define RT5660_ADC_OSR_128 (0x0)
+#define RT5660_ADC_OSR_64 (0x1)
+#define RT5660_ADC_OSR_32 (0x2)
+#define RT5660_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5660_RESET_ADF (0x1 << 13)
+#define RT5660_RESET_ADF_SFT 13
+#define RT5660_RESET_DAF (0x1 << 12)
+#define RT5660_RESET_DAF_SFT 12
+#define RT5660_DAHPF_EN (0x1 << 11)
+#define RT5660_DAHPF_EN_SFT 11
+#define RT5660_ADHPF_EN (0x1 << 10)
+#define RT5660_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5660_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5660_DMIC_1_EN_SFT 15
+#define RT5660_DMIC_1_DIS (0x0 << 15)
+#define RT5660_DMIC_1_EN (0x1 << 15)
+#define RT5660_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5660_DMIC_1L_LH_SFT 13
+#define RT5660_DMIC_1L_LH_RISING (0x0 << 13)
+#define RT5660_DMIC_1L_LH_FALLING (0x1 << 13)
+#define RT5660_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5660_DMIC_1R_LH_SFT 12
+#define RT5660_DMIC_1R_LH_RISING (0x0 << 12)
+#define RT5660_DMIC_1R_LH_FALLING (0x1 << 12)
+#define RT5660_SEL_DMIC_DATA_MASK (0x1 << 11)
+#define RT5660_SEL_DMIC_DATA_SFT 11
+#define RT5660_SEL_DMIC_DATA_GPIO2 (0x0 << 11)
+#define RT5660_SEL_DMIC_DATA_IN1P (0x1 << 11)
+#define RT5660_DMIC_CLK_MASK (0x7 << 5)
+#define RT5660_DMIC_CLK_SFT 5
+
+/* Global Clock Control (0x80) */
+#define RT5660_SCLK_SRC_MASK (0x3 << 14)
+#define RT5660_SCLK_SRC_SFT 14
+#define RT5660_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5660_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5660_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5660_PLL1_SRC_MASK (0x3 << 12)
+#define RT5660_PLL1_SRC_SFT 12
+#define RT5660_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5660_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5660_PLL1_SRC_RCCLK (0x2 << 12)
+#define RT5660_PLL1_PD_MASK (0x1 << 3)
+#define RT5660_PLL1_PD_SFT 3
+#define RT5660_PLL1_PD_1 (0x0 << 3)
+#define RT5660_PLL1_PD_2 (0x1 << 3)
+
+#define RT5660_PLL_INP_MAX 40000000
+#define RT5660_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5660_PLL_N_MAX 0x1ff
+#define RT5660_PLL_N_MASK (RT5660_PLL_N_MAX << 7)
+#define RT5660_PLL_N_SFT 7
+#define RT5660_PLL_K_MAX 0x1f
+#define RT5660_PLL_K_MASK (RT5660_PLL_K_MAX)
+#define RT5660_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5660_PLL_M_MAX 0xf
+#define RT5660_PLL_M_MASK (RT5660_PLL_M_MAX << 12)
+#define RT5660_PLL_M_SFT 12
+#define RT5660_PLL_M_BP (0x1 << 11)
+#define RT5660_PLL_M_BP_SFT 11
+
+/* Class D Over Current Control (0x8c) */
+#define RT5660_CLSD_OC_MASK (0x1 << 9)
+#define RT5660_CLSD_OC_SFT 9
+#define RT5660_CLSD_OC_PU (0x0 << 9)
+#define RT5660_CLSD_OC_PD (0x1 << 9)
+#define RT5660_AUTO_PD_MASK (0x1 << 8)
+#define RT5660_AUTO_PD_SFT 8
+#define RT5660_AUTO_PD_DIS (0x0 << 8)
+#define RT5660_AUTO_PD_EN (0x1 << 8)
+#define RT5660_CLSD_OC_TH_MASK (0x3f)
+#define RT5660_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5660_CLSD_RATIO_MASK (0xf << 12)
+#define RT5660_CLSD_RATIO_SFT 12
+
+/* Lout Amp Control 1 (0x8e) */
+#define RT5660_LOUT_CO_MASK (0x1 << 4)
+#define RT5660_LOUT_CO_SFT 4
+#define RT5660_LOUT_CO_DIS (0x0 << 4)
+#define RT5660_LOUT_CO_EN (0x1 << 4)
+#define RT5660_LOUT_CB_MASK (0x1)
+#define RT5660_LOUT_CB_SFT 0
+#define RT5660_LOUT_CB_PD (0x0)
+#define RT5660_LOUT_CB_PU (0x1)
+
+/* SPKVDD detection control (0x92) */
+#define RT5660_SPKVDD_DET_MASK (0x1 << 15)
+#define RT5660_SPKVDD_DET_SFT 15
+#define RT5660_SPKVDD_DET_DIS (0x0 << 15)
+#define RT5660_SPKVDD_DET_EN (0x1 << 15)
+#define RT5660_SPK_AG_MASK (0x1 << 14)
+#define RT5660_SPK_AG_SFT 14
+#define RT5660_SPK_AG_DIS (0x0 << 14)
+#define RT5660_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5660_MIC1_BS_MASK (0x1 << 15)
+#define RT5660_MIC1_BS_SFT 15
+#define RT5660_MIC1_BS_9AV (0x0 << 15)
+#define RT5660_MIC1_BS_75AV (0x1 << 15)
+#define RT5660_MIC2_BS_MASK (0x1 << 14)
+#define RT5660_MIC2_BS_SFT 14
+#define RT5660_MIC2_BS_9AV (0x0 << 14)
+#define RT5660_MIC2_BS_75AV (0x1 << 14)
+#define RT5660_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5660_MIC1_OVCD_SFT 11
+#define RT5660_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5660_MIC1_OVCD_EN (0x1 << 11)
+#define RT5660_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5660_MIC1_OVTH_SFT 9
+#define RT5660_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5660_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5660_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5660_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5660_MIC2_OVCD_SFT 8
+#define RT5660_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5660_MIC2_OVCD_EN (0x1 << 8)
+#define RT5660_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5660_MIC2_OVTH_SFT 6
+#define RT5660_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5660_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5660_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5660_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5660_PWR_CLK25M_SFT 4
+#define RT5660_PWR_CLK25M_PD (0x0 << 4)
+#define RT5660_PWR_CLK25M_PU (0x1 << 4)
+
+/* EQ Control 1 (0xb0) */
+#define RT5660_EQ_SRC_MASK (0x1 << 15)
+#define RT5660_EQ_SRC_SFT 15
+#define RT5660_EQ_SRC_DAC (0x0 << 15)
+#define RT5660_EQ_SRC_ADC (0x1 << 15)
+#define RT5660_EQ_UPD (0x1 << 14)
+#define RT5660_EQ_UPD_BIT 14
+
+/* Jack Detect Control (0xbb) */
+#define RT5660_JD_MASK (0x3 << 14)
+#define RT5660_JD_SFT 14
+#define RT5660_JD_DIS (0x0 << 14)
+#define RT5660_JD_GPIO1 (0x1 << 14)
+#define RT5660_JD_GPIO2 (0x2 << 14)
+#define RT5660_JD_LOUT_MASK (0x1 << 11)
+#define RT5660_JD_LOUT_SFT 11
+#define RT5660_JD_LOUT_DIS (0x0 << 11)
+#define RT5660_JD_LOUT_EN (0x1 << 11)
+#define RT5660_JD_LOUT_TRG_MASK (0x1 << 10)
+#define RT5660_JD_LOUT_TRG_SFT 10
+#define RT5660_JD_LOUT_TRG_LO (0x0 << 10)
+#define RT5660_JD_LOUT_TRG_HI (0x1 << 10)
+#define RT5660_JD_SPO_MASK (0x1 << 9)
+#define RT5660_JD_SPO_SFT 9
+#define RT5660_JD_SPO_DIS (0x0 << 9)
+#define RT5660_JD_SPO_EN (0x1 << 9)
+#define RT5660_JD_SPO_TRG_MASK (0x1 << 8)
+#define RT5660_JD_SPO_TRG_SFT 8
+#define RT5660_JD_SPO_TRG_LO (0x0 << 8)
+#define RT5660_JD_SPO_TRG_HI (0x1 << 8)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5660_IRQ_JD_MASK (0x1 << 15)
+#define RT5660_IRQ_JD_SFT 15
+#define RT5660_IRQ_JD_BP (0x0 << 15)
+#define RT5660_IRQ_JD_NOR (0x1 << 15)
+#define RT5660_IRQ_OT_MASK (0x1 << 14)
+#define RT5660_IRQ_OT_SFT 14
+#define RT5660_IRQ_OT_BP (0x0 << 14)
+#define RT5660_IRQ_OT_NOR (0x1 << 14)
+#define RT5660_JD_STKY_MASK (0x1 << 13)
+#define RT5660_JD_STKY_SFT 13
+#define RT5660_JD_STKY_DIS (0x0 << 13)
+#define RT5660_JD_STKY_EN (0x1 << 13)
+#define RT5660_OT_STKY_MASK (0x1 << 12)
+#define RT5660_OT_STKY_SFT 12
+#define RT5660_OT_STKY_DIS (0x0 << 12)
+#define RT5660_OT_STKY_EN (0x1 << 12)
+#define RT5660_JD_P_MASK (0x1 << 11)
+#define RT5660_JD_P_SFT 11
+#define RT5660_JD_P_NOR (0x0 << 11)
+#define RT5660_JD_P_INV (0x1 << 11)
+#define RT5660_OT_P_MASK (0x1 << 10)
+#define RT5660_OT_P_SFT 10
+#define RT5660_OT_P_NOR (0x0 << 10)
+#define RT5660_OT_P_INV (0x1 << 10)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5660_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5660_IRQ_MB1_OC_SFT 15
+#define RT5660_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5660_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5660_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5660_IRQ_MB2_OC_SFT 14
+#define RT5660_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5660_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5660_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5660_MB1_OC_STKY_SFT 11
+#define RT5660_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5660_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5660_MB2_OC_STKY_MASK (0x1 << 10)
+#define RT5660_MB2_OC_STKY_SFT 10
+#define RT5660_MB2_OC_STKY_DIS (0x0 << 10)
+#define RT5660_MB2_OC_STKY_EN (0x1 << 10)
+#define RT5660_MB1_OC_P_MASK (0x1 << 7)
+#define RT5660_MB1_OC_P_SFT 7
+#define RT5660_MB1_OC_P_NOR (0x0 << 7)
+#define RT5660_MB1_OC_P_INV (0x1 << 7)
+#define RT5660_MB2_OC_P_MASK (0x1 << 6)
+#define RT5660_MB2_OC_P_SFT 6
+#define RT5660_MB2_OC_P_NOR (0x0 << 6)
+#define RT5660_MB2_OC_P_INV (0x1 << 6)
+#define RT5660_MB1_OC_CLR (0x1 << 3)
+#define RT5660_MB1_OC_CLR_SFT 3
+#define RT5660_MB2_OC_CLR (0x1 << 2)
+#define RT5660_MB2_OC_CLR_SFT 2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5660_GP2_PIN_MASK (0x1 << 14)
+#define RT5660_GP2_PIN_SFT 14
+#define RT5660_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5660_GP2_PIN_DMIC1_SDA (0x1 << 14)
+#define RT5660_GP1_PIN_MASK (0x3 << 12)
+#define RT5660_GP1_PIN_SFT 12
+#define RT5660_GP1_PIN_GPIO1 (0x0 << 12)
+#define RT5660_GP1_PIN_DMIC1_SCL (0x1 << 12)
+#define RT5660_GP1_PIN_IRQ (0x2 << 12)
+#define RT5660_GPIO_M_MASK (0x1 << 9)
+#define RT5660_GPIO_M_SFT 9
+#define RT5660_GPIO_M_FLT (0x0 << 9)
+#define RT5660_GPIO_M_PH (0x1 << 9)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5660_GP2_PF_MASK (0x1 << 5)
+#define RT5660_GP2_PF_SFT 5
+#define RT5660_GP2_PF_IN (0x0 << 5)
+#define RT5660_GP2_PF_OUT (0x1 << 5)
+#define RT5660_GP2_OUT_MASK (0x1 << 4)
+#define RT5660_GP2_OUT_SFT 4
+#define RT5660_GP2_OUT_LO (0x0 << 4)
+#define RT5660_GP2_OUT_HI (0x1 << 4)
+#define RT5660_GP2_P_MASK (0x1 << 3)
+#define RT5660_GP2_P_SFT 3
+#define RT5660_GP2_P_NOR (0x0 << 3)
+#define RT5660_GP2_P_INV (0x1 << 3)
+#define RT5660_GP1_PF_MASK (0x1 << 2)
+#define RT5660_GP1_PF_SFT 2
+#define RT5660_GP1_PF_IN (0x0 << 2)
+#define RT5660_GP1_PF_OUT (0x1 << 2)
+#define RT5660_GP1_OUT_MASK (0x1 << 1)
+#define RT5660_GP1_OUT_SFT 1
+#define RT5660_GP1_OUT_LO (0x0 << 1)
+#define RT5660_GP1_OUT_HI (0x1 << 1)
+#define RT5660_GP1_P_MASK (0x1)
+#define RT5660_GP1_P_SFT 0
+#define RT5660_GP1_P_NOR (0x0)
+#define RT5660_GP1_P_INV (0x1)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5660_SV_MASK (0x1 << 15)
+#define RT5660_SV_SFT 15
+#define RT5660_SV_DIS (0x0 << 15)
+#define RT5660_SV_EN (0x1 << 15)
+#define RT5660_SPO_SV_MASK (0x1 << 14)
+#define RT5660_SPO_SV_SFT 14
+#define RT5660_SPO_SV_DIS (0x0 << 14)
+#define RT5660_SPO_SV_EN (0x1 << 14)
+#define RT5660_OUT_SV_MASK (0x1 << 12)
+#define RT5660_OUT_SV_SFT 12
+#define RT5660_OUT_SV_DIS (0x0 << 12)
+#define RT5660_OUT_SV_EN (0x1 << 12)
+#define RT5660_ZCD_DIG_MASK (0x1 << 11)
+#define RT5660_ZCD_DIG_SFT 11
+#define RT5660_ZCD_DIG_DIS (0x0 << 11)
+#define RT5660_ZCD_DIG_EN (0x1 << 11)
+#define RT5660_ZCD_MASK (0x1 << 10)
+#define RT5660_ZCD_SFT 10
+#define RT5660_ZCD_PD (0x0 << 10)
+#define RT5660_ZCD_PU (0x1 << 10)
+#define RT5660_SV_DLY_MASK (0xf)
+#define RT5660_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5660_ZCD_SPO_MASK (0x1 << 15)
+#define RT5660_ZCD_SPO_SFT 15
+#define RT5660_ZCD_SPO_DIS (0x0 << 15)
+#define RT5660_ZCD_SPO_EN (0x1 << 15)
+#define RT5660_ZCD_OMR_MASK (0x1 << 8)
+#define RT5660_ZCD_OMR_SFT 8
+#define RT5660_ZCD_OMR_DIS (0x0 << 8)
+#define RT5660_ZCD_OMR_EN (0x1 << 8)
+#define RT5660_ZCD_OML_MASK (0x1 << 7)
+#define RT5660_ZCD_OML_SFT 7
+#define RT5660_ZCD_OML_DIS (0x0 << 7)
+#define RT5660_ZCD_OML_EN (0x1 << 7)
+#define RT5660_ZCD_SPM_MASK (0x1 << 6)
+#define RT5660_ZCD_SPM_SFT 6
+#define RT5660_ZCD_SPM_DIS (0x0 << 6)
+#define RT5660_ZCD_SPM_EN (0x1 << 6)
+#define RT5660_ZCD_RMR_MASK (0x1 << 5)
+#define RT5660_ZCD_RMR_SFT 5
+#define RT5660_ZCD_RMR_DIS (0x0 << 5)
+#define RT5660_ZCD_RMR_EN (0x1 << 5)
+#define RT5660_ZCD_RML_MASK (0x1 << 4)
+#define RT5660_ZCD_RML_SFT 4
+#define RT5660_ZCD_RML_DIS (0x0 << 4)
+#define RT5660_ZCD_RML_EN (0x1 << 4)
+
+/* General Control 1 (0xfa) */
+#define RT5660_PWR_VREF_HP (0x1 << 11)
+#define RT5660_PWR_VREF_HP_SFT 11
+#define RT5660_AUTO_DIS_AMP (0x1 << 6)
+#define RT5660_MCLK_DET (0x1 << 5)
+#define RT5660_POW_CLKDET (0x1 << 1)
+#define RT5660_DIG_GATE_CTRL (0x1)
+#define RT5660_DIG_GATE_CTRL_SFT 0
+
+/* System Clock Source */
+#define RT5660_SCLK_S_MCLK 0
+#define RT5660_SCLK_S_PLL1 1
+#define RT5660_SCLK_S_RCCLK 2
+
+/* PLL1 Source */
+#define RT5660_PLL1_S_MCLK 0
+#define RT5660_PLL1_S_BCLK 1
+
+enum {
+ RT5660_AIF1,
+ RT5660_AIFS,
+};
+
+struct rt5660_priv {
+ struct snd_soc_component *component;
+ struct rt5660_platform_data pdata;
+ struct regmap *regmap;
+ struct clk *mclk;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5660_AIFS];
+ int bclk[RT5660_AIFS];
+ int master[RT5660_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#endif
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
new file mode 100644
index 000000000000..e4d8785e64c1
--- /dev/null
+++ b/sound/soc/codecs/rt5663.c
@@ -0,0 +1,3756 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5663.c -- RT5663 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 Realtek Semiconductor Corp.
+ * Author: Jack Yu <jack.yu@realtek.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5663.h"
+#include "rl6231.h"
+
+#define RT5663_DEVICE_ID_2 0x6451
+#define RT5663_DEVICE_ID_1 0x6406
+
+#define RT5663_POWER_ON_DELAY_MS 300
+#define RT5663_SUPPLY_CURRENT_UA 500000
+
+enum {
+ CODEC_VER_1,
+ CODEC_VER_0,
+};
+
+struct impedance_mapping_table {
+ unsigned int imp_min;
+ unsigned int imp_max;
+ unsigned int vol;
+ unsigned int dc_offset_l_manual;
+ unsigned int dc_offset_r_manual;
+ unsigned int dc_offset_l_manual_mic;
+ unsigned int dc_offset_r_manual_mic;
+};
+
+static const char *const rt5663_supply_names[] = {
+ "avdd",
+ "cpvdd",
+};
+
+struct rt5663_priv {
+ struct snd_soc_component *component;
+ struct rt5663_platform_data pdata;
+ struct regmap *regmap;
+ struct delayed_work jack_detect_work, jd_unplug_work;
+ struct snd_soc_jack *hs_jack;
+ struct timer_list btn_check_timer;
+ struct impedance_mapping_table *imp_table;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(rt5663_supply_names)];
+
+ int codec_ver;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+ unsigned int irq;
+};
+
+static const struct reg_sequence rt5663_patch_list[] = {
+ { 0x002a, 0x8020 },
+ { 0x0086, 0x0028 },
+ { 0x0100, 0xa020 },
+ { 0x0117, 0x0f28 },
+ { 0x02fb, 0x8089 },
+};
+
+static const struct reg_default rt5663_v2_reg[] = {
+ { 0x0000, 0x0000 },
+ { 0x0001, 0xc8c8 },
+ { 0x0002, 0x8080 },
+ { 0x0003, 0x8000 },
+ { 0x0004, 0xc80a },
+ { 0x0005, 0x0000 },
+ { 0x0006, 0x0000 },
+ { 0x0007, 0x0000 },
+ { 0x000a, 0x0000 },
+ { 0x000b, 0x0000 },
+ { 0x000c, 0x0000 },
+ { 0x000d, 0x0000 },
+ { 0x000f, 0x0808 },
+ { 0x0010, 0x4000 },
+ { 0x0011, 0x0000 },
+ { 0x0012, 0x1404 },
+ { 0x0013, 0x1000 },
+ { 0x0014, 0xa00a },
+ { 0x0015, 0x0404 },
+ { 0x0016, 0x0404 },
+ { 0x0017, 0x0011 },
+ { 0x0018, 0xafaf },
+ { 0x0019, 0xafaf },
+ { 0x001a, 0xafaf },
+ { 0x001b, 0x0011 },
+ { 0x001c, 0x2f2f },
+ { 0x001d, 0x2f2f },
+ { 0x001e, 0x2f2f },
+ { 0x001f, 0x0000 },
+ { 0x0020, 0x0000 },
+ { 0x0021, 0x0000 },
+ { 0x0022, 0x5757 },
+ { 0x0023, 0x0039 },
+ { 0x0024, 0x000b },
+ { 0x0026, 0xc0c0 },
+ { 0x0027, 0xc0c0 },
+ { 0x0028, 0xc0c0 },
+ { 0x0029, 0x8080 },
+ { 0x002a, 0xaaaa },
+ { 0x002b, 0xaaaa },
+ { 0x002c, 0xaba8 },
+ { 0x002d, 0x0000 },
+ { 0x002e, 0x0000 },
+ { 0x002f, 0x0000 },
+ { 0x0030, 0x0000 },
+ { 0x0031, 0x5000 },
+ { 0x0032, 0x0000 },
+ { 0x0033, 0x0000 },
+ { 0x0034, 0x0000 },
+ { 0x0035, 0x0000 },
+ { 0x003a, 0x0000 },
+ { 0x003b, 0x0000 },
+ { 0x003c, 0x00ff },
+ { 0x003d, 0x0000 },
+ { 0x003e, 0x00ff },
+ { 0x003f, 0x0000 },
+ { 0x0040, 0x0000 },
+ { 0x0041, 0x00ff },
+ { 0x0042, 0x0000 },
+ { 0x0043, 0x00ff },
+ { 0x0044, 0x0c0c },
+ { 0x0049, 0xc00b },
+ { 0x004a, 0x0000 },
+ { 0x004b, 0x031f },
+ { 0x004d, 0x0000 },
+ { 0x004e, 0x001f },
+ { 0x004f, 0x0000 },
+ { 0x0050, 0x001f },
+ { 0x0052, 0xf000 },
+ { 0x0061, 0x0000 },
+ { 0x0062, 0x0000 },
+ { 0x0063, 0x003e },
+ { 0x0064, 0x0000 },
+ { 0x0065, 0x0000 },
+ { 0x0066, 0x003f },
+ { 0x0067, 0x0000 },
+ { 0x006b, 0x0000 },
+ { 0x006d, 0xff00 },
+ { 0x006e, 0x2808 },
+ { 0x006f, 0x000a },
+ { 0x0070, 0x8000 },
+ { 0x0071, 0x8000 },
+ { 0x0072, 0x8000 },
+ { 0x0073, 0x7000 },
+ { 0x0074, 0x7770 },
+ { 0x0075, 0x0002 },
+ { 0x0076, 0x0001 },
+ { 0x0078, 0x00f0 },
+ { 0x0079, 0x0000 },
+ { 0x007a, 0x0000 },
+ { 0x007b, 0x0000 },
+ { 0x007c, 0x0000 },
+ { 0x007d, 0x0123 },
+ { 0x007e, 0x4500 },
+ { 0x007f, 0x8003 },
+ { 0x0080, 0x0000 },
+ { 0x0081, 0x0000 },
+ { 0x0082, 0x0000 },
+ { 0x0083, 0x0000 },
+ { 0x0084, 0x0000 },
+ { 0x0085, 0x0000 },
+ { 0x0086, 0x0008 },
+ { 0x0087, 0x0000 },
+ { 0x0088, 0x0000 },
+ { 0x0089, 0x0000 },
+ { 0x008a, 0x0000 },
+ { 0x008b, 0x0000 },
+ { 0x008c, 0x0003 },
+ { 0x008e, 0x0060 },
+ { 0x008f, 0x1000 },
+ { 0x0091, 0x0c26 },
+ { 0x0092, 0x0073 },
+ { 0x0093, 0x0000 },
+ { 0x0094, 0x0080 },
+ { 0x0098, 0x0000 },
+ { 0x0099, 0x0000 },
+ { 0x009a, 0x0007 },
+ { 0x009f, 0x0000 },
+ { 0x00a0, 0x0000 },
+ { 0x00a1, 0x0002 },
+ { 0x00a2, 0x0001 },
+ { 0x00a3, 0x0002 },
+ { 0x00a4, 0x0001 },
+ { 0x00ae, 0x2040 },
+ { 0x00af, 0x0000 },
+ { 0x00b6, 0x0000 },
+ { 0x00b7, 0x0000 },
+ { 0x00b8, 0x0000 },
+ { 0x00b9, 0x0000 },
+ { 0x00ba, 0x0002 },
+ { 0x00bb, 0x0000 },
+ { 0x00be, 0x0000 },
+ { 0x00c0, 0x0000 },
+ { 0x00c1, 0x0aaa },
+ { 0x00c2, 0xaa80 },
+ { 0x00c3, 0x0003 },
+ { 0x00c4, 0x0000 },
+ { 0x00d0, 0x0000 },
+ { 0x00d1, 0x2244 },
+ { 0x00d2, 0x0000 },
+ { 0x00d3, 0x3300 },
+ { 0x00d4, 0x2200 },
+ { 0x00d9, 0x0809 },
+ { 0x00da, 0x0000 },
+ { 0x00db, 0x0008 },
+ { 0x00dc, 0x00c0 },
+ { 0x00dd, 0x6724 },
+ { 0x00de, 0x3131 },
+ { 0x00df, 0x0008 },
+ { 0x00e0, 0x4000 },
+ { 0x00e1, 0x3131 },
+ { 0x00e2, 0x600c },
+ { 0x00ea, 0xb320 },
+ { 0x00eb, 0x0000 },
+ { 0x00ec, 0xb300 },
+ { 0x00ed, 0x0000 },
+ { 0x00ee, 0xb320 },
+ { 0x00ef, 0x0000 },
+ { 0x00f0, 0x0201 },
+ { 0x00f1, 0x0ddd },
+ { 0x00f2, 0x0ddd },
+ { 0x00f6, 0x0000 },
+ { 0x00f7, 0x0000 },
+ { 0x00f8, 0x0000 },
+ { 0x00fa, 0x0000 },
+ { 0x00fb, 0x0000 },
+ { 0x00fc, 0x0000 },
+ { 0x00fd, 0x0000 },
+ { 0x00fe, 0x10ec },
+ { 0x00ff, 0x6451 },
+ { 0x0100, 0xaaaa },
+ { 0x0101, 0x000a },
+ { 0x010a, 0xaaaa },
+ { 0x010b, 0xa0a0 },
+ { 0x010c, 0xaeae },
+ { 0x010d, 0xaaaa },
+ { 0x010e, 0xaaaa },
+ { 0x010f, 0xaaaa },
+ { 0x0110, 0xe002 },
+ { 0x0111, 0xa602 },
+ { 0x0112, 0xaaaa },
+ { 0x0113, 0x2000 },
+ { 0x0117, 0x0f00 },
+ { 0x0125, 0x0420 },
+ { 0x0132, 0x0000 },
+ { 0x0133, 0x0000 },
+ { 0x0136, 0x5555 },
+ { 0x0137, 0x5540 },
+ { 0x0138, 0x3700 },
+ { 0x0139, 0x79a1 },
+ { 0x013a, 0x2020 },
+ { 0x013b, 0x2020 },
+ { 0x013c, 0x2005 },
+ { 0x013f, 0x0000 },
+ { 0x0145, 0x0002 },
+ { 0x0146, 0x0000 },
+ { 0x0147, 0x0000 },
+ { 0x0148, 0x0000 },
+ { 0x0160, 0x4ec0 },
+ { 0x0161, 0x0080 },
+ { 0x0162, 0x0200 },
+ { 0x0163, 0x0800 },
+ { 0x0164, 0x0000 },
+ { 0x0165, 0x0000 },
+ { 0x0166, 0x0000 },
+ { 0x0167, 0x000f },
+ { 0x0168, 0x000f },
+ { 0x0170, 0x4e80 },
+ { 0x0171, 0x0080 },
+ { 0x0172, 0x0200 },
+ { 0x0173, 0x0800 },
+ { 0x0174, 0x00ff },
+ { 0x0175, 0x0000 },
+ { 0x0190, 0x4131 },
+ { 0x0191, 0x4131 },
+ { 0x0192, 0x4131 },
+ { 0x0193, 0x4131 },
+ { 0x0194, 0x0000 },
+ { 0x0195, 0x0000 },
+ { 0x0196, 0x0000 },
+ { 0x0197, 0x0000 },
+ { 0x0198, 0x0000 },
+ { 0x0199, 0x0000 },
+ { 0x01a0, 0x1e64 },
+ { 0x01a1, 0x06a3 },
+ { 0x01a2, 0x0000 },
+ { 0x01a3, 0x0000 },
+ { 0x01a4, 0x0000 },
+ { 0x01a5, 0x0000 },
+ { 0x01a6, 0x0000 },
+ { 0x01a7, 0x0000 },
+ { 0x01a8, 0x0000 },
+ { 0x01a9, 0x0000 },
+ { 0x01aa, 0x0000 },
+ { 0x01ab, 0x0000 },
+ { 0x01b5, 0x0000 },
+ { 0x01b6, 0x01c3 },
+ { 0x01b7, 0x02a0 },
+ { 0x01b8, 0x03e9 },
+ { 0x01b9, 0x1389 },
+ { 0x01ba, 0xc351 },
+ { 0x01bb, 0x0009 },
+ { 0x01bc, 0x0018 },
+ { 0x01bd, 0x002a },
+ { 0x01be, 0x004c },
+ { 0x01bf, 0x0097 },
+ { 0x01c0, 0x433d },
+ { 0x01c1, 0x0000 },
+ { 0x01c2, 0x0000 },
+ { 0x01c3, 0x0000 },
+ { 0x01c4, 0x0000 },
+ { 0x01c5, 0x0000 },
+ { 0x01c6, 0x0000 },
+ { 0x01c7, 0x0000 },
+ { 0x01c8, 0x40af },
+ { 0x01c9, 0x0702 },
+ { 0x01ca, 0x0000 },
+ { 0x01cb, 0x0000 },
+ { 0x01cc, 0x5757 },
+ { 0x01cd, 0x5757 },
+ { 0x01ce, 0x5757 },
+ { 0x01cf, 0x5757 },
+ { 0x01d0, 0x5757 },
+ { 0x01d1, 0x5757 },
+ { 0x01d2, 0x5757 },
+ { 0x01d3, 0x5757 },
+ { 0x01d4, 0x5757 },
+ { 0x01d5, 0x5757 },
+ { 0x01d6, 0x003c },
+ { 0x01da, 0x0000 },
+ { 0x01db, 0x0000 },
+ { 0x01dc, 0x0000 },
+ { 0x01de, 0x7c00 },
+ { 0x01df, 0x0320 },
+ { 0x01e0, 0x06a1 },
+ { 0x01e1, 0x0000 },
+ { 0x01e2, 0x0000 },
+ { 0x01e3, 0x0000 },
+ { 0x01e4, 0x0000 },
+ { 0x01e5, 0x0000 },
+ { 0x01e6, 0x0001 },
+ { 0x01e7, 0x0000 },
+ { 0x01e8, 0x0000 },
+ { 0x01ea, 0x0000 },
+ { 0x01eb, 0x0000 },
+ { 0x01ec, 0x0000 },
+ { 0x01ed, 0x0000 },
+ { 0x01ee, 0x0000 },
+ { 0x01ef, 0x0000 },
+ { 0x01f0, 0x0000 },
+ { 0x01f1, 0x0000 },
+ { 0x01f2, 0x0000 },
+ { 0x01f3, 0x0000 },
+ { 0x01f4, 0x0000 },
+ { 0x0200, 0x0000 },
+ { 0x0201, 0x0000 },
+ { 0x0202, 0x0000 },
+ { 0x0203, 0x0000 },
+ { 0x0204, 0x0000 },
+ { 0x0205, 0x0000 },
+ { 0x0206, 0x0000 },
+ { 0x0207, 0x0000 },
+ { 0x0208, 0x0000 },
+ { 0x0210, 0x60b1 },
+ { 0x0211, 0xa000 },
+ { 0x0212, 0x024c },
+ { 0x0213, 0xf7ff },
+ { 0x0214, 0x024c },
+ { 0x0215, 0x0102 },
+ { 0x0216, 0x00a3 },
+ { 0x0217, 0x0048 },
+ { 0x0218, 0x92c0 },
+ { 0x0219, 0x0000 },
+ { 0x021a, 0x00c8 },
+ { 0x021b, 0x0020 },
+ { 0x02fa, 0x0000 },
+ { 0x02fb, 0x0000 },
+ { 0x02fc, 0x0000 },
+ { 0x02ff, 0x0110 },
+ { 0x0300, 0x001f },
+ { 0x0301, 0x032c },
+ { 0x0302, 0x5f21 },
+ { 0x0303, 0x4000 },
+ { 0x0304, 0x4000 },
+ { 0x0305, 0x06d5 },
+ { 0x0306, 0x8000 },
+ { 0x0307, 0x0700 },
+ { 0x0310, 0x4560 },
+ { 0x0311, 0xa4a8 },
+ { 0x0312, 0x7418 },
+ { 0x0313, 0x0000 },
+ { 0x0314, 0x0006 },
+ { 0x0315, 0xffff },
+ { 0x0316, 0xc400 },
+ { 0x0317, 0x0000 },
+ { 0x0330, 0x00a6 },
+ { 0x0331, 0x04c3 },
+ { 0x0332, 0x27c8 },
+ { 0x0333, 0xbf50 },
+ { 0x0334, 0x0045 },
+ { 0x0335, 0x0007 },
+ { 0x0336, 0x7418 },
+ { 0x0337, 0x0501 },
+ { 0x0338, 0x0000 },
+ { 0x0339, 0x0010 },
+ { 0x033a, 0x1010 },
+ { 0x03c0, 0x7e00 },
+ { 0x03c1, 0x8000 },
+ { 0x03c2, 0x8000 },
+ { 0x03c3, 0x8000 },
+ { 0x03c4, 0x8000 },
+ { 0x03c5, 0x8000 },
+ { 0x03c6, 0x8000 },
+ { 0x03c7, 0x8000 },
+ { 0x03c8, 0x8000 },
+ { 0x03c9, 0x8000 },
+ { 0x03ca, 0x8000 },
+ { 0x03cb, 0x8000 },
+ { 0x03cc, 0x8000 },
+ { 0x03d0, 0x0000 },
+ { 0x03d1, 0x0000 },
+ { 0x03d2, 0x0000 },
+ { 0x03d3, 0x0000 },
+ { 0x03d4, 0x2000 },
+ { 0x03d5, 0x2000 },
+ { 0x03d6, 0x0000 },
+ { 0x03d7, 0x0000 },
+ { 0x03d8, 0x2000 },
+ { 0x03d9, 0x2000 },
+ { 0x03da, 0x2000 },
+ { 0x03db, 0x2000 },
+ { 0x03dc, 0x0000 },
+ { 0x03dd, 0x0000 },
+ { 0x03de, 0x0000 },
+ { 0x03df, 0x2000 },
+ { 0x03e0, 0x0000 },
+ { 0x03e1, 0x0000 },
+ { 0x03e2, 0x0000 },
+ { 0x03e3, 0x0000 },
+ { 0x03e4, 0x0000 },
+ { 0x03e5, 0x0000 },
+ { 0x03e6, 0x0000 },
+ { 0x03e7, 0x0000 },
+ { 0x03e8, 0x0000 },
+ { 0x03e9, 0x0000 },
+ { 0x03ea, 0x0000 },
+ { 0x03eb, 0x0000 },
+ { 0x03ec, 0x0000 },
+ { 0x03ed, 0x0000 },
+ { 0x03ee, 0x0000 },
+ { 0x03ef, 0x0000 },
+ { 0x03f0, 0x0800 },
+ { 0x03f1, 0x0800 },
+ { 0x03f2, 0x0800 },
+ { 0x03f3, 0x0800 },
+ { 0x03fe, 0x0000 },
+ { 0x03ff, 0x0000 },
+ { 0x07f0, 0x0000 },
+ { 0x07fa, 0x0000 },
+};
+
+static const struct reg_default rt5663_reg[] = {
+ { 0x0000, 0x0000 },
+ { 0x0002, 0x0008 },
+ { 0x0005, 0x1000 },
+ { 0x0006, 0x1000 },
+ { 0x000a, 0x0000 },
+ { 0x0010, 0x000f },
+ { 0x0015, 0x42f1 },
+ { 0x0016, 0x0000 },
+ { 0x0018, 0x000b },
+ { 0x0019, 0xafaf },
+ { 0x001c, 0x2f2f },
+ { 0x001f, 0x0000 },
+ { 0x0022, 0x5757 },
+ { 0x0023, 0x0039 },
+ { 0x0026, 0xc0c0 },
+ { 0x0029, 0x8080 },
+ { 0x002a, 0x8020 },
+ { 0x002c, 0x000c },
+ { 0x002d, 0x0000 },
+ { 0x0040, 0x0808 },
+ { 0x0061, 0x0000 },
+ { 0x0062, 0x0000 },
+ { 0x0063, 0x003e },
+ { 0x0064, 0x0000 },
+ { 0x0065, 0x0000 },
+ { 0x0066, 0x0000 },
+ { 0x006b, 0x0000 },
+ { 0x006e, 0x0000 },
+ { 0x006f, 0x0000 },
+ { 0x0070, 0x8020 },
+ { 0x0073, 0x1000 },
+ { 0x0074, 0xe400 },
+ { 0x0075, 0x0002 },
+ { 0x0076, 0x0001 },
+ { 0x0077, 0x00f0 },
+ { 0x0078, 0x0000 },
+ { 0x0079, 0x0000 },
+ { 0x007a, 0x0123 },
+ { 0x007b, 0x8003 },
+ { 0x0080, 0x0000 },
+ { 0x0081, 0x0000 },
+ { 0x0082, 0x0000 },
+ { 0x0083, 0x0000 },
+ { 0x0084, 0x0000 },
+ { 0x0086, 0x0028 },
+ { 0x0087, 0x0000 },
+ { 0x008a, 0x0000 },
+ { 0x008b, 0x0000 },
+ { 0x008c, 0x0003 },
+ { 0x008e, 0x0008 },
+ { 0x008f, 0x1000 },
+ { 0x0090, 0x0646 },
+ { 0x0091, 0x0e3e },
+ { 0x0092, 0x1071 },
+ { 0x0093, 0x0000 },
+ { 0x0094, 0x0080 },
+ { 0x0097, 0x0000 },
+ { 0x0098, 0x0000 },
+ { 0x009a, 0x0000 },
+ { 0x009f, 0x0000 },
+ { 0x00ae, 0x6000 },
+ { 0x00af, 0x0000 },
+ { 0x00b6, 0x0000 },
+ { 0x00b7, 0x0000 },
+ { 0x00b8, 0x0000 },
+ { 0x00ba, 0x0000 },
+ { 0x00bb, 0x0000 },
+ { 0x00be, 0x0000 },
+ { 0x00bf, 0x0000 },
+ { 0x00c0, 0x0000 },
+ { 0x00c1, 0x0000 },
+ { 0x00c5, 0x0000 },
+ { 0x00cb, 0xa02f },
+ { 0x00cc, 0x0000 },
+ { 0x00cd, 0x0e02 },
+ { 0x00d9, 0x08f9 },
+ { 0x00db, 0x0008 },
+ { 0x00dc, 0x00c0 },
+ { 0x00dd, 0x6729 },
+ { 0x00de, 0x3131 },
+ { 0x00df, 0x0008 },
+ { 0x00e0, 0x4000 },
+ { 0x00e1, 0x3131 },
+ { 0x00e2, 0x0043 },
+ { 0x00e4, 0x400b },
+ { 0x00e5, 0x8031 },
+ { 0x00e6, 0x3080 },
+ { 0x00e7, 0x4100 },
+ { 0x00e8, 0x1400 },
+ { 0x00e9, 0xe00a },
+ { 0x00ea, 0x0404 },
+ { 0x00eb, 0x0404 },
+ { 0x00ec, 0xb320 },
+ { 0x00ed, 0x0000 },
+ { 0x00f4, 0x0000 },
+ { 0x00f6, 0x0000 },
+ { 0x00f8, 0x0000 },
+ { 0x00fa, 0x8000 },
+ { 0x00fd, 0x0001 },
+ { 0x00fe, 0x10ec },
+ { 0x00ff, 0x6406 },
+ { 0x0100, 0xa020 },
+ { 0x0108, 0x4444 },
+ { 0x0109, 0x4444 },
+ { 0x010a, 0xaaaa },
+ { 0x010b, 0x00a0 },
+ { 0x010c, 0x8aaa },
+ { 0x010d, 0xaaaa },
+ { 0x010e, 0x2aaa },
+ { 0x010f, 0x002a },
+ { 0x0110, 0xa0a4 },
+ { 0x0111, 0x4602 },
+ { 0x0112, 0x0101 },
+ { 0x0113, 0x2000 },
+ { 0x0114, 0x0000 },
+ { 0x0116, 0x0000 },
+ { 0x0117, 0x0f28 },
+ { 0x0118, 0x0006 },
+ { 0x0125, 0x2424 },
+ { 0x0126, 0x5550 },
+ { 0x0127, 0x0400 },
+ { 0x0128, 0x7711 },
+ { 0x0132, 0x0004 },
+ { 0x0137, 0x5441 },
+ { 0x0139, 0x79a1 },
+ { 0x013a, 0x30c0 },
+ { 0x013b, 0x2000 },
+ { 0x013c, 0x2005 },
+ { 0x013d, 0x30c0 },
+ { 0x013e, 0x0000 },
+ { 0x0140, 0x3700 },
+ { 0x0141, 0x1f00 },
+ { 0x0144, 0x0000 },
+ { 0x0145, 0x0002 },
+ { 0x0146, 0x0000 },
+ { 0x0160, 0x0e80 },
+ { 0x0161, 0x0080 },
+ { 0x0162, 0x0200 },
+ { 0x0163, 0x0800 },
+ { 0x0164, 0x0000 },
+ { 0x0165, 0x0000 },
+ { 0x0166, 0x0000 },
+ { 0x0167, 0x1417 },
+ { 0x0168, 0x0017 },
+ { 0x0169, 0x0017 },
+ { 0x0180, 0x2000 },
+ { 0x0181, 0x0000 },
+ { 0x0182, 0x0000 },
+ { 0x0183, 0x2000 },
+ { 0x0184, 0x0000 },
+ { 0x0185, 0x0000 },
+ { 0x01b0, 0x4b30 },
+ { 0x01b1, 0x0000 },
+ { 0x01b2, 0xd870 },
+ { 0x01b3, 0x0000 },
+ { 0x01b4, 0x0030 },
+ { 0x01b5, 0x5757 },
+ { 0x01b6, 0x5757 },
+ { 0x01b7, 0x5757 },
+ { 0x01b8, 0x5757 },
+ { 0x01c0, 0x433d },
+ { 0x01c1, 0x0540 },
+ { 0x01c2, 0x0000 },
+ { 0x01c3, 0x0000 },
+ { 0x01c4, 0x0000 },
+ { 0x01c5, 0x0009 },
+ { 0x01c6, 0x0018 },
+ { 0x01c7, 0x002a },
+ { 0x01c8, 0x004c },
+ { 0x01c9, 0x0097 },
+ { 0x01ca, 0x01c3 },
+ { 0x01cb, 0x03e9 },
+ { 0x01cc, 0x1389 },
+ { 0x01cd, 0xc351 },
+ { 0x01ce, 0x0000 },
+ { 0x01cf, 0x0000 },
+ { 0x01d0, 0x0000 },
+ { 0x01d1, 0x0000 },
+ { 0x01d2, 0x0000 },
+ { 0x01d3, 0x003c },
+ { 0x01d4, 0x5757 },
+ { 0x01d5, 0x5757 },
+ { 0x01d6, 0x5757 },
+ { 0x01d7, 0x5757 },
+ { 0x01d8, 0x5757 },
+ { 0x01d9, 0x5757 },
+ { 0x01da, 0x0000 },
+ { 0x01db, 0x0000 },
+ { 0x01dd, 0x0009 },
+ { 0x01de, 0x7f00 },
+ { 0x01df, 0x00c8 },
+ { 0x01e0, 0x0691 },
+ { 0x01e1, 0x0000 },
+ { 0x01e2, 0x0000 },
+ { 0x01e3, 0x0000 },
+ { 0x01e4, 0x0000 },
+ { 0x01e5, 0x0040 },
+ { 0x01e6, 0x0000 },
+ { 0x01e7, 0x0000 },
+ { 0x01e8, 0x0000 },
+ { 0x01ea, 0x0000 },
+ { 0x01eb, 0x0000 },
+ { 0x01ec, 0x0000 },
+ { 0x01ed, 0x0000 },
+ { 0x01ee, 0x0000 },
+ { 0x01ef, 0x0000 },
+ { 0x01f0, 0x0000 },
+ { 0x01f1, 0x0000 },
+ { 0x01f2, 0x0000 },
+ { 0x0200, 0x0000 },
+ { 0x0201, 0x2244 },
+ { 0x0202, 0xaaaa },
+ { 0x0250, 0x8010 },
+ { 0x0251, 0x0000 },
+ { 0x0252, 0x028a },
+ { 0x02fa, 0x0000 },
+ { 0x02fb, 0x8089 },
+ { 0x02fc, 0x0300 },
+ { 0x0300, 0x0000 },
+ { 0x03d0, 0x0000 },
+ { 0x03d1, 0x0000 },
+ { 0x03d2, 0x0000 },
+ { 0x03d3, 0x0000 },
+ { 0x03d4, 0x2000 },
+ { 0x03d5, 0x2000 },
+ { 0x03d6, 0x0000 },
+ { 0x03d7, 0x0000 },
+ { 0x03d8, 0x2000 },
+ { 0x03d9, 0x2000 },
+ { 0x03da, 0x2000 },
+ { 0x03db, 0x2000 },
+ { 0x03dc, 0x0000 },
+ { 0x03dd, 0x0000 },
+ { 0x03de, 0x0000 },
+ { 0x03df, 0x2000 },
+ { 0x03e0, 0x0000 },
+ { 0x03e1, 0x0000 },
+ { 0x03e2, 0x0000 },
+ { 0x03e3, 0x0000 },
+ { 0x03e4, 0x0000 },
+ { 0x03e5, 0x0000 },
+ { 0x03e6, 0x0000 },
+ { 0x03e7, 0x0000 },
+ { 0x03e8, 0x0000 },
+ { 0x03e9, 0x0000 },
+ { 0x03ea, 0x0000 },
+ { 0x03eb, 0x0000 },
+ { 0x03ec, 0x0000 },
+ { 0x03ed, 0x0000 },
+ { 0x03ee, 0x0000 },
+ { 0x03ef, 0x0000 },
+ { 0x03f0, 0x0800 },
+ { 0x03f1, 0x0800 },
+ { 0x03f2, 0x0800 },
+ { 0x03f3, 0x0800 },
+};
+
+static bool rt5663_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5663_RESET:
+ case RT5663_SIL_DET_CTL:
+ case RT5663_HP_IMP_GAIN_2:
+ case RT5663_AD_DA_MIXER:
+ case RT5663_FRAC_DIV_2:
+ case RT5663_MICBIAS_1:
+ case RT5663_ASRC_11_2:
+ case RT5663_ADC_EQ_1:
+ case RT5663_INT_ST_1:
+ case RT5663_INT_ST_2:
+ case RT5663_GPIO_STA1:
+ case RT5663_SIN_GEN_1:
+ case RT5663_IL_CMD_1:
+ case RT5663_IL_CMD_5:
+ case RT5663_IL_CMD_PWRSAV1:
+ case RT5663_EM_JACK_TYPE_1:
+ case RT5663_EM_JACK_TYPE_2:
+ case RT5663_EM_JACK_TYPE_3:
+ case RT5663_JD_CTRL2:
+ case RT5663_VENDOR_ID:
+ case RT5663_VENDOR_ID_1:
+ case RT5663_VENDOR_ID_2:
+ case RT5663_PLL_INT_REG:
+ case RT5663_SOFT_RAMP:
+ case RT5663_STO_DRE_1:
+ case RT5663_STO_DRE_5:
+ case RT5663_STO_DRE_6:
+ case RT5663_STO_DRE_7:
+ case RT5663_MIC_DECRO_1:
+ case RT5663_MIC_DECRO_4:
+ case RT5663_HP_IMP_SEN_1:
+ case RT5663_HP_IMP_SEN_3:
+ case RT5663_HP_IMP_SEN_4:
+ case RT5663_HP_IMP_SEN_5:
+ case RT5663_HP_CALIB_1_1:
+ case RT5663_HP_CALIB_9:
+ case RT5663_HP_CALIB_ST1:
+ case RT5663_HP_CALIB_ST2:
+ case RT5663_HP_CALIB_ST3:
+ case RT5663_HP_CALIB_ST4:
+ case RT5663_HP_CALIB_ST5:
+ case RT5663_HP_CALIB_ST6:
+ case RT5663_HP_CALIB_ST7:
+ case RT5663_HP_CALIB_ST8:
+ case RT5663_HP_CALIB_ST9:
+ case RT5663_ANA_JD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5663_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5663_RESET:
+ case RT5663_HP_OUT_EN:
+ case RT5663_HP_LCH_DRE:
+ case RT5663_HP_RCH_DRE:
+ case RT5663_CALIB_BST:
+ case RT5663_RECMIX:
+ case RT5663_SIL_DET_CTL:
+ case RT5663_PWR_SAV_SILDET:
+ case RT5663_SIDETONE_CTL:
+ case RT5663_STO1_DAC_DIG_VOL:
+ case RT5663_STO1_ADC_DIG_VOL:
+ case RT5663_STO1_BOOST:
+ case RT5663_HP_IMP_GAIN_1:
+ case RT5663_HP_IMP_GAIN_2:
+ case RT5663_STO1_ADC_MIXER:
+ case RT5663_AD_DA_MIXER:
+ case RT5663_STO_DAC_MIXER:
+ case RT5663_DIG_SIDE_MIXER:
+ case RT5663_BYPASS_STO_DAC:
+ case RT5663_CALIB_REC_MIX:
+ case RT5663_PWR_DIG_1:
+ case RT5663_PWR_DIG_2:
+ case RT5663_PWR_ANLG_1:
+ case RT5663_PWR_ANLG_2:
+ case RT5663_PWR_ANLG_3:
+ case RT5663_PWR_MIXER:
+ case RT5663_SIG_CLK_DET:
+ case RT5663_PRE_DIV_GATING_1:
+ case RT5663_PRE_DIV_GATING_2:
+ case RT5663_I2S1_SDP:
+ case RT5663_ADDA_CLK_1:
+ case RT5663_ADDA_RST:
+ case RT5663_FRAC_DIV_1:
+ case RT5663_FRAC_DIV_2:
+ case RT5663_TDM_1:
+ case RT5663_TDM_2:
+ case RT5663_TDM_3:
+ case RT5663_TDM_4:
+ case RT5663_TDM_5:
+ case RT5663_GLB_CLK:
+ case RT5663_PLL_1:
+ case RT5663_PLL_2:
+ case RT5663_ASRC_1:
+ case RT5663_ASRC_2:
+ case RT5663_ASRC_4:
+ case RT5663_DUMMY_REG:
+ case RT5663_ASRC_8:
+ case RT5663_ASRC_9:
+ case RT5663_ASRC_11:
+ case RT5663_DEPOP_1:
+ case RT5663_DEPOP_2:
+ case RT5663_DEPOP_3:
+ case RT5663_HP_CHARGE_PUMP_1:
+ case RT5663_HP_CHARGE_PUMP_2:
+ case RT5663_MICBIAS_1:
+ case RT5663_RC_CLK:
+ case RT5663_ASRC_11_2:
+ case RT5663_DUMMY_REG_2:
+ case RT5663_REC_PATH_GAIN:
+ case RT5663_AUTO_1MRC_CLK:
+ case RT5663_ADC_EQ_1:
+ case RT5663_ADC_EQ_2:
+ case RT5663_IRQ_1:
+ case RT5663_IRQ_2:
+ case RT5663_IRQ_3:
+ case RT5663_IRQ_4:
+ case RT5663_IRQ_5:
+ case RT5663_INT_ST_1:
+ case RT5663_INT_ST_2:
+ case RT5663_GPIO_1:
+ case RT5663_GPIO_2:
+ case RT5663_GPIO_STA1:
+ case RT5663_SIN_GEN_1:
+ case RT5663_SIN_GEN_2:
+ case RT5663_SIN_GEN_3:
+ case RT5663_SOF_VOL_ZC1:
+ case RT5663_IL_CMD_1:
+ case RT5663_IL_CMD_2:
+ case RT5663_IL_CMD_3:
+ case RT5663_IL_CMD_4:
+ case RT5663_IL_CMD_5:
+ case RT5663_IL_CMD_6:
+ case RT5663_IL_CMD_7:
+ case RT5663_IL_CMD_8:
+ case RT5663_IL_CMD_PWRSAV1:
+ case RT5663_IL_CMD_PWRSAV2:
+ case RT5663_EM_JACK_TYPE_1:
+ case RT5663_EM_JACK_TYPE_2:
+ case RT5663_EM_JACK_TYPE_3:
+ case RT5663_EM_JACK_TYPE_4:
+ case RT5663_EM_JACK_TYPE_5:
+ case RT5663_EM_JACK_TYPE_6:
+ case RT5663_STO1_HPF_ADJ1:
+ case RT5663_STO1_HPF_ADJ2:
+ case RT5663_FAST_OFF_MICBIAS:
+ case RT5663_JD_CTRL1:
+ case RT5663_JD_CTRL2:
+ case RT5663_DIG_MISC:
+ case RT5663_VENDOR_ID:
+ case RT5663_VENDOR_ID_1:
+ case RT5663_VENDOR_ID_2:
+ case RT5663_DIG_VOL_ZCD:
+ case RT5663_ANA_BIAS_CUR_1:
+ case RT5663_ANA_BIAS_CUR_2:
+ case RT5663_ANA_BIAS_CUR_3:
+ case RT5663_ANA_BIAS_CUR_4:
+ case RT5663_ANA_BIAS_CUR_5:
+ case RT5663_ANA_BIAS_CUR_6:
+ case RT5663_BIAS_CUR_5:
+ case RT5663_BIAS_CUR_6:
+ case RT5663_BIAS_CUR_7:
+ case RT5663_BIAS_CUR_8:
+ case RT5663_DACREF_LDO:
+ case RT5663_DUMMY_REG_3:
+ case RT5663_BIAS_CUR_9:
+ case RT5663_DUMMY_REG_4:
+ case RT5663_VREFADJ_OP:
+ case RT5663_VREF_RECMIX:
+ case RT5663_CHARGE_PUMP_1:
+ case RT5663_CHARGE_PUMP_1_2:
+ case RT5663_CHARGE_PUMP_1_3:
+ case RT5663_CHARGE_PUMP_2:
+ case RT5663_DIG_IN_PIN1:
+ case RT5663_PAD_DRV_CTL:
+ case RT5663_PLL_INT_REG:
+ case RT5663_CHOP_DAC_L:
+ case RT5663_CHOP_ADC:
+ case RT5663_CALIB_ADC:
+ case RT5663_CHOP_DAC_R:
+ case RT5663_DUMMY_CTL_DACLR:
+ case RT5663_DUMMY_REG_5:
+ case RT5663_SOFT_RAMP:
+ case RT5663_TEST_MODE_1:
+ case RT5663_TEST_MODE_2:
+ case RT5663_TEST_MODE_3:
+ case RT5663_STO_DRE_1:
+ case RT5663_STO_DRE_2:
+ case RT5663_STO_DRE_3:
+ case RT5663_STO_DRE_4:
+ case RT5663_STO_DRE_5:
+ case RT5663_STO_DRE_6:
+ case RT5663_STO_DRE_7:
+ case RT5663_STO_DRE_8:
+ case RT5663_STO_DRE_9:
+ case RT5663_STO_DRE_10:
+ case RT5663_MIC_DECRO_1:
+ case RT5663_MIC_DECRO_2:
+ case RT5663_MIC_DECRO_3:
+ case RT5663_MIC_DECRO_4:
+ case RT5663_MIC_DECRO_5:
+ case RT5663_MIC_DECRO_6:
+ case RT5663_HP_DECRO_1:
+ case RT5663_HP_DECRO_2:
+ case RT5663_HP_DECRO_3:
+ case RT5663_HP_DECRO_4:
+ case RT5663_HP_DECOUP:
+ case RT5663_HP_IMP_SEN_MAP8:
+ case RT5663_HP_IMP_SEN_MAP9:
+ case RT5663_HP_IMP_SEN_MAP10:
+ case RT5663_HP_IMP_SEN_MAP11:
+ case RT5663_HP_IMP_SEN_1:
+ case RT5663_HP_IMP_SEN_2:
+ case RT5663_HP_IMP_SEN_3:
+ case RT5663_HP_IMP_SEN_4:
+ case RT5663_HP_IMP_SEN_5:
+ case RT5663_HP_IMP_SEN_6:
+ case RT5663_HP_IMP_SEN_7:
+ case RT5663_HP_IMP_SEN_8:
+ case RT5663_HP_IMP_SEN_9:
+ case RT5663_HP_IMP_SEN_10:
+ case RT5663_HP_IMP_SEN_11:
+ case RT5663_HP_IMP_SEN_12:
+ case RT5663_HP_IMP_SEN_13:
+ case RT5663_HP_IMP_SEN_14:
+ case RT5663_HP_IMP_SEN_15:
+ case RT5663_HP_IMP_SEN_16:
+ case RT5663_HP_IMP_SEN_17:
+ case RT5663_HP_IMP_SEN_18:
+ case RT5663_HP_IMP_SEN_19:
+ case RT5663_HP_IMPSEN_DIG5:
+ case RT5663_HP_IMPSEN_MAP1:
+ case RT5663_HP_IMPSEN_MAP2:
+ case RT5663_HP_IMPSEN_MAP3:
+ case RT5663_HP_IMPSEN_MAP4:
+ case RT5663_HP_IMPSEN_MAP5:
+ case RT5663_HP_IMPSEN_MAP7:
+ case RT5663_HP_LOGIC_1:
+ case RT5663_HP_LOGIC_2:
+ case RT5663_HP_CALIB_1:
+ case RT5663_HP_CALIB_1_1:
+ case RT5663_HP_CALIB_2:
+ case RT5663_HP_CALIB_3:
+ case RT5663_HP_CALIB_4:
+ case RT5663_HP_CALIB_5:
+ case RT5663_HP_CALIB_5_1:
+ case RT5663_HP_CALIB_6:
+ case RT5663_HP_CALIB_7:
+ case RT5663_HP_CALIB_9:
+ case RT5663_HP_CALIB_10:
+ case RT5663_HP_CALIB_11:
+ case RT5663_HP_CALIB_ST1:
+ case RT5663_HP_CALIB_ST2:
+ case RT5663_HP_CALIB_ST3:
+ case RT5663_HP_CALIB_ST4:
+ case RT5663_HP_CALIB_ST5:
+ case RT5663_HP_CALIB_ST6:
+ case RT5663_HP_CALIB_ST7:
+ case RT5663_HP_CALIB_ST8:
+ case RT5663_HP_CALIB_ST9:
+ case RT5663_HP_AMP_DET:
+ case RT5663_DUMMY_REG_6:
+ case RT5663_HP_BIAS:
+ case RT5663_CBJ_1:
+ case RT5663_CBJ_2:
+ case RT5663_CBJ_3:
+ case RT5663_DUMMY_1:
+ case RT5663_DUMMY_2:
+ case RT5663_DUMMY_3:
+ case RT5663_ANA_JD:
+ case RT5663_ADC_LCH_LPF1_A1:
+ case RT5663_ADC_RCH_LPF1_A1:
+ case RT5663_ADC_LCH_LPF1_H0:
+ case RT5663_ADC_RCH_LPF1_H0:
+ case RT5663_ADC_LCH_BPF1_A1:
+ case RT5663_ADC_RCH_BPF1_A1:
+ case RT5663_ADC_LCH_BPF1_A2:
+ case RT5663_ADC_RCH_BPF1_A2:
+ case RT5663_ADC_LCH_BPF1_H0:
+ case RT5663_ADC_RCH_BPF1_H0:
+ case RT5663_ADC_LCH_BPF2_A1:
+ case RT5663_ADC_RCH_BPF2_A1:
+ case RT5663_ADC_LCH_BPF2_A2:
+ case RT5663_ADC_RCH_BPF2_A2:
+ case RT5663_ADC_LCH_BPF2_H0:
+ case RT5663_ADC_RCH_BPF2_H0:
+ case RT5663_ADC_LCH_BPF3_A1:
+ case RT5663_ADC_RCH_BPF3_A1:
+ case RT5663_ADC_LCH_BPF3_A2:
+ case RT5663_ADC_RCH_BPF3_A2:
+ case RT5663_ADC_LCH_BPF3_H0:
+ case RT5663_ADC_RCH_BPF3_H0:
+ case RT5663_ADC_LCH_BPF4_A1:
+ case RT5663_ADC_RCH_BPF4_A1:
+ case RT5663_ADC_LCH_BPF4_A2:
+ case RT5663_ADC_RCH_BPF4_A2:
+ case RT5663_ADC_LCH_BPF4_H0:
+ case RT5663_ADC_RCH_BPF4_H0:
+ case RT5663_ADC_LCH_HPF1_A1:
+ case RT5663_ADC_RCH_HPF1_A1:
+ case RT5663_ADC_LCH_HPF1_H0:
+ case RT5663_ADC_RCH_HPF1_H0:
+ case RT5663_ADC_EQ_PRE_VOL_L:
+ case RT5663_ADC_EQ_PRE_VOL_R:
+ case RT5663_ADC_EQ_POST_VOL_L:
+ case RT5663_ADC_EQ_POST_VOL_R:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5663_v2_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5663_RESET:
+ case RT5663_CBJ_TYPE_2:
+ case RT5663_PDM_OUT_CTL:
+ case RT5663_PDM_I2C_DATA_CTL1:
+ case RT5663_PDM_I2C_DATA_CTL4:
+ case RT5663_ALC_BK_GAIN:
+ case RT5663_PLL_2:
+ case RT5663_MICBIAS_1:
+ case RT5663_ADC_EQ_1:
+ case RT5663_INT_ST_1:
+ case RT5663_GPIO_STA2:
+ case RT5663_IL_CMD_1:
+ case RT5663_IL_CMD_5:
+ case RT5663_A_JD_CTRL:
+ case RT5663_JD_CTRL2:
+ case RT5663_VENDOR_ID:
+ case RT5663_VENDOR_ID_1:
+ case RT5663_VENDOR_ID_2:
+ case RT5663_STO_DRE_1:
+ case RT5663_STO_DRE_5:
+ case RT5663_STO_DRE_6:
+ case RT5663_STO_DRE_7:
+ case RT5663_MONO_DYNA_6:
+ case RT5663_STO1_SIL_DET:
+ case RT5663_MONOL_SIL_DET:
+ case RT5663_MONOR_SIL_DET:
+ case RT5663_STO2_DAC_SIL:
+ case RT5663_MONO_AMP_CAL_ST1:
+ case RT5663_MONO_AMP_CAL_ST2:
+ case RT5663_MONO_AMP_CAL_ST3:
+ case RT5663_MONO_AMP_CAL_ST4:
+ case RT5663_HP_IMP_SEN_2:
+ case RT5663_HP_IMP_SEN_3:
+ case RT5663_HP_IMP_SEN_4:
+ case RT5663_HP_IMP_SEN_10:
+ case RT5663_HP_CALIB_1:
+ case RT5663_HP_CALIB_10:
+ case RT5663_HP_CALIB_ST1:
+ case RT5663_HP_CALIB_ST4:
+ case RT5663_HP_CALIB_ST5:
+ case RT5663_HP_CALIB_ST6:
+ case RT5663_HP_CALIB_ST7:
+ case RT5663_HP_CALIB_ST8:
+ case RT5663_HP_CALIB_ST9:
+ case RT5663_HP_CALIB_ST10:
+ case RT5663_HP_CALIB_ST11:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5663_v2_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5663_LOUT_CTRL:
+ case RT5663_HP_AMP_2:
+ case RT5663_MONO_OUT:
+ case RT5663_MONO_GAIN:
+ case RT5663_AEC_BST:
+ case RT5663_IN1_IN2:
+ case RT5663_IN3_IN4:
+ case RT5663_INL1_INR1:
+ case RT5663_CBJ_TYPE_2:
+ case RT5663_CBJ_TYPE_3:
+ case RT5663_CBJ_TYPE_4:
+ case RT5663_CBJ_TYPE_5:
+ case RT5663_CBJ_TYPE_8:
+ case RT5663_DAC3_DIG_VOL:
+ case RT5663_DAC3_CTRL:
+ case RT5663_MONO_ADC_DIG_VOL:
+ case RT5663_STO2_ADC_DIG_VOL:
+ case RT5663_MONO_ADC_BST_GAIN:
+ case RT5663_STO2_ADC_BST_GAIN:
+ case RT5663_SIDETONE_CTRL:
+ case RT5663_MONO1_ADC_MIXER:
+ case RT5663_STO2_ADC_MIXER:
+ case RT5663_MONO_DAC_MIXER:
+ case RT5663_DAC2_SRC_CTRL:
+ case RT5663_IF_3_4_DATA_CTL:
+ case RT5663_IF_5_DATA_CTL:
+ case RT5663_PDM_OUT_CTL:
+ case RT5663_PDM_I2C_DATA_CTL1:
+ case RT5663_PDM_I2C_DATA_CTL2:
+ case RT5663_PDM_I2C_DATA_CTL3:
+ case RT5663_PDM_I2C_DATA_CTL4:
+ case RT5663_RECMIX1_NEW:
+ case RT5663_RECMIX1L_0:
+ case RT5663_RECMIX1L:
+ case RT5663_RECMIX1R_0:
+ case RT5663_RECMIX1R:
+ case RT5663_RECMIX2_NEW:
+ case RT5663_RECMIX2_L_2:
+ case RT5663_RECMIX2_R:
+ case RT5663_RECMIX2_R_2:
+ case RT5663_CALIB_REC_LR:
+ case RT5663_ALC_BK_GAIN:
+ case RT5663_MONOMIX_GAIN:
+ case RT5663_MONOMIX_IN_GAIN:
+ case RT5663_OUT_MIXL_GAIN:
+ case RT5663_OUT_LMIX_IN_GAIN:
+ case RT5663_OUT_RMIX_IN_GAIN:
+ case RT5663_OUT_RMIX_IN_GAIN1:
+ case RT5663_LOUT_MIXER_CTRL:
+ case RT5663_PWR_VOL:
+ case RT5663_ADCDAC_RST:
+ case RT5663_I2S34_SDP:
+ case RT5663_I2S5_SDP:
+ case RT5663_TDM_6:
+ case RT5663_TDM_7:
+ case RT5663_TDM_8:
+ case RT5663_TDM_9:
+ case RT5663_ASRC_3:
+ case RT5663_ASRC_6:
+ case RT5663_ASRC_7:
+ case RT5663_PLL_TRK_13:
+ case RT5663_I2S_M_CLK_CTL:
+ case RT5663_FDIV_I2S34_M_CLK:
+ case RT5663_FDIV_I2S34_M_CLK2:
+ case RT5663_FDIV_I2S5_M_CLK:
+ case RT5663_FDIV_I2S5_M_CLK2:
+ case RT5663_V2_IRQ_4:
+ case RT5663_GPIO_3:
+ case RT5663_GPIO_4:
+ case RT5663_GPIO_STA2:
+ case RT5663_HP_AMP_DET1:
+ case RT5663_HP_AMP_DET2:
+ case RT5663_HP_AMP_DET3:
+ case RT5663_MID_BD_HP_AMP:
+ case RT5663_LOW_BD_HP_AMP:
+ case RT5663_SOF_VOL_ZC2:
+ case RT5663_ADC_STO2_ADJ1:
+ case RT5663_ADC_STO2_ADJ2:
+ case RT5663_A_JD_CTRL:
+ case RT5663_JD1_TRES_CTRL:
+ case RT5663_JD2_TRES_CTRL:
+ case RT5663_V2_JD_CTRL2:
+ case RT5663_DUM_REG_2:
+ case RT5663_DUM_REG_3:
+ case RT5663_VENDOR_ID:
+ case RT5663_VENDOR_ID_1:
+ case RT5663_VENDOR_ID_2:
+ case RT5663_DACADC_DIG_VOL2:
+ case RT5663_DIG_IN_PIN2:
+ case RT5663_PAD_DRV_CTL1:
+ case RT5663_SOF_RAM_DEPOP:
+ case RT5663_VOL_TEST:
+ case RT5663_TEST_MODE_4:
+ case RT5663_TEST_MODE_5:
+ case RT5663_STO_DRE_9:
+ case RT5663_MONO_DYNA_1:
+ case RT5663_MONO_DYNA_2:
+ case RT5663_MONO_DYNA_3:
+ case RT5663_MONO_DYNA_4:
+ case RT5663_MONO_DYNA_5:
+ case RT5663_MONO_DYNA_6:
+ case RT5663_STO1_SIL_DET:
+ case RT5663_MONOL_SIL_DET:
+ case RT5663_MONOR_SIL_DET:
+ case RT5663_STO2_DAC_SIL:
+ case RT5663_PWR_SAV_CTL1:
+ case RT5663_PWR_SAV_CTL2:
+ case RT5663_PWR_SAV_CTL3:
+ case RT5663_PWR_SAV_CTL4:
+ case RT5663_PWR_SAV_CTL5:
+ case RT5663_PWR_SAV_CTL6:
+ case RT5663_MONO_AMP_CAL1:
+ case RT5663_MONO_AMP_CAL2:
+ case RT5663_MONO_AMP_CAL3:
+ case RT5663_MONO_AMP_CAL4:
+ case RT5663_MONO_AMP_CAL5:
+ case RT5663_MONO_AMP_CAL6:
+ case RT5663_MONO_AMP_CAL7:
+ case RT5663_MONO_AMP_CAL_ST1:
+ case RT5663_MONO_AMP_CAL_ST2:
+ case RT5663_MONO_AMP_CAL_ST3:
+ case RT5663_MONO_AMP_CAL_ST4:
+ case RT5663_MONO_AMP_CAL_ST5:
+ case RT5663_V2_HP_IMP_SEN_13:
+ case RT5663_V2_HP_IMP_SEN_14:
+ case RT5663_V2_HP_IMP_SEN_6:
+ case RT5663_V2_HP_IMP_SEN_7:
+ case RT5663_V2_HP_IMP_SEN_8:
+ case RT5663_V2_HP_IMP_SEN_9:
+ case RT5663_V2_HP_IMP_SEN_10:
+ case RT5663_HP_LOGIC_3:
+ case RT5663_HP_CALIB_ST10:
+ case RT5663_HP_CALIB_ST11:
+ case RT5663_PRO_REG_TBL_4:
+ case RT5663_PRO_REG_TBL_5:
+ case RT5663_PRO_REG_TBL_6:
+ case RT5663_PRO_REG_TBL_7:
+ case RT5663_PRO_REG_TBL_8:
+ case RT5663_PRO_REG_TBL_9:
+ case RT5663_SAR_ADC_INL_1:
+ case RT5663_SAR_ADC_INL_2:
+ case RT5663_SAR_ADC_INL_3:
+ case RT5663_SAR_ADC_INL_4:
+ case RT5663_SAR_ADC_INL_5:
+ case RT5663_SAR_ADC_INL_6:
+ case RT5663_SAR_ADC_INL_7:
+ case RT5663_SAR_ADC_INL_8:
+ case RT5663_SAR_ADC_INL_9:
+ case RT5663_SAR_ADC_INL_10:
+ case RT5663_SAR_ADC_INL_11:
+ case RT5663_SAR_ADC_INL_12:
+ case RT5663_DRC_CTRL_1:
+ case RT5663_DRC1_CTRL_2:
+ case RT5663_DRC1_CTRL_3:
+ case RT5663_DRC1_CTRL_4:
+ case RT5663_DRC1_CTRL_5:
+ case RT5663_DRC1_CTRL_6:
+ case RT5663_DRC1_HD_CTRL_1:
+ case RT5663_DRC1_HD_CTRL_2:
+ case RT5663_DRC1_PRI_REG_1:
+ case RT5663_DRC1_PRI_REG_2:
+ case RT5663_DRC1_PRI_REG_3:
+ case RT5663_DRC1_PRI_REG_4:
+ case RT5663_DRC1_PRI_REG_5:
+ case RT5663_DRC1_PRI_REG_6:
+ case RT5663_DRC1_PRI_REG_7:
+ case RT5663_DRC1_PRI_REG_8:
+ case RT5663_ALC_PGA_CTL_1:
+ case RT5663_ALC_PGA_CTL_2:
+ case RT5663_ALC_PGA_CTL_3:
+ case RT5663_ALC_PGA_CTL_4:
+ case RT5663_ALC_PGA_CTL_5:
+ case RT5663_ALC_PGA_CTL_6:
+ case RT5663_ALC_PGA_CTL_7:
+ case RT5663_ALC_PGA_CTL_8:
+ case RT5663_ALC_PGA_REG_1:
+ case RT5663_ALC_PGA_REG_2:
+ case RT5663_ALC_PGA_REG_3:
+ case RT5663_ADC_EQ_RECOV_1:
+ case RT5663_ADC_EQ_RECOV_2:
+ case RT5663_ADC_EQ_RECOV_3:
+ case RT5663_ADC_EQ_RECOV_4:
+ case RT5663_ADC_EQ_RECOV_5:
+ case RT5663_ADC_EQ_RECOV_6:
+ case RT5663_ADC_EQ_RECOV_7:
+ case RT5663_ADC_EQ_RECOV_8:
+ case RT5663_ADC_EQ_RECOV_9:
+ case RT5663_ADC_EQ_RECOV_10:
+ case RT5663_ADC_EQ_RECOV_11:
+ case RT5663_ADC_EQ_RECOV_12:
+ case RT5663_ADC_EQ_RECOV_13:
+ case RT5663_VID_HIDDEN:
+ case RT5663_VID_CUSTOMER:
+ case RT5663_SCAN_MODE:
+ case RT5663_I2C_BYPA:
+ return true;
+ case RT5663_TDM_1:
+ case RT5663_DEPOP_3:
+ case RT5663_ASRC_11_2:
+ case RT5663_INT_ST_2:
+ case RT5663_GPIO_STA1:
+ case RT5663_SIN_GEN_1:
+ case RT5663_SIN_GEN_2:
+ case RT5663_SIN_GEN_3:
+ case RT5663_IL_CMD_PWRSAV1:
+ case RT5663_IL_CMD_PWRSAV2:
+ case RT5663_EM_JACK_TYPE_1:
+ case RT5663_EM_JACK_TYPE_2:
+ case RT5663_EM_JACK_TYPE_3:
+ case RT5663_EM_JACK_TYPE_4:
+ case RT5663_FAST_OFF_MICBIAS:
+ case RT5663_ANA_BIAS_CUR_1:
+ case RT5663_ANA_BIAS_CUR_2:
+ case RT5663_BIAS_CUR_9:
+ case RT5663_DUMMY_REG_4:
+ case RT5663_VREF_RECMIX:
+ case RT5663_CHARGE_PUMP_1_2:
+ case RT5663_CHARGE_PUMP_1_3:
+ case RT5663_CHARGE_PUMP_2:
+ case RT5663_CHOP_DAC_R:
+ case RT5663_DUMMY_CTL_DACLR:
+ case RT5663_DUMMY_REG_5:
+ case RT5663_SOFT_RAMP:
+ case RT5663_TEST_MODE_1:
+ case RT5663_STO_DRE_10:
+ case RT5663_MIC_DECRO_1:
+ case RT5663_MIC_DECRO_2:
+ case RT5663_MIC_DECRO_3:
+ case RT5663_MIC_DECRO_4:
+ case RT5663_MIC_DECRO_5:
+ case RT5663_MIC_DECRO_6:
+ case RT5663_HP_DECRO_1:
+ case RT5663_HP_DECRO_2:
+ case RT5663_HP_DECRO_3:
+ case RT5663_HP_DECRO_4:
+ case RT5663_HP_DECOUP:
+ case RT5663_HP_IMPSEN_MAP4:
+ case RT5663_HP_IMPSEN_MAP5:
+ case RT5663_HP_IMPSEN_MAP7:
+ case RT5663_HP_CALIB_1:
+ case RT5663_CBJ_1:
+ case RT5663_CBJ_2:
+ case RT5663_CBJ_3:
+ return false;
+ default:
+ return rt5663_readable_register(dev, reg);
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(rt5663_hp_vol_tlv, -2400, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rt5663_v2_hp_vol_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const DECLARE_TLV_DB_RANGE(in_bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* Interface data select */
+static const char * const rt5663_if1_adc_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5663_if1_adc_enum, RT5663_TDM_2,
+ RT5663_DATA_SWAP_ADCDAT1_SHIFT, rt5663_if1_adc_data_select);
+
+static void rt5663_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
+ RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_EN);
+ /* reset in-line command */
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
+ RT5663_RESET_4BTN_INL_MASK,
+ RT5663_RESET_4BTN_INL_RESET);
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
+ RT5663_RESET_4BTN_INL_MASK,
+ RT5663_RESET_4BTN_INL_NOR);
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ snd_soc_component_update_bits(component, RT5663_IRQ_3,
+ RT5663_V2_EN_IRQ_INLINE_MASK,
+ RT5663_V2_EN_IRQ_INLINE_NOR);
+ break;
+ case CODEC_VER_0:
+ snd_soc_component_update_bits(component, RT5663_IRQ_2,
+ RT5663_EN_IRQ_INLINE_MASK,
+ RT5663_EN_IRQ_INLINE_NOR);
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+ } else {
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ snd_soc_component_update_bits(component, RT5663_IRQ_3,
+ RT5663_V2_EN_IRQ_INLINE_MASK,
+ RT5663_V2_EN_IRQ_INLINE_BYP);
+ break;
+ case CODEC_VER_0:
+ snd_soc_component_update_bits(component, RT5663_IRQ_2,
+ RT5663_EN_IRQ_INLINE_MASK,
+ RT5663_EN_IRQ_INLINE_BYP);
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
+ RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_DIS);
+ /* reset in-line command */
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
+ RT5663_RESET_4BTN_INL_MASK,
+ RT5663_RESET_4BTN_INL_RESET);
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_6,
+ RT5663_RESET_4BTN_INL_MASK,
+ RT5663_RESET_4BTN_INL_NOR);
+ }
+}
+
+/**
+ * rt5663_v2_jack_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+
+static int rt5663_v2_jack_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30};
+
+ dev_dbg(component->dev, "%s jack_insert:%d\n", __func__, jack_insert);
+ if (jack_insert) {
+ snd_soc_component_write(component, RT5663_CBJ_TYPE_2, 0x8040);
+ snd_soc_component_write(component, RT5663_CBJ_TYPE_3, 0x1484);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2");
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_force_enable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+ snd_soc_component_update_bits(component, RT5663_RC_CLK,
+ RT5663_DIG_1M_CLK_MASK, RT5663_DIG_1M_CLK_EN);
+ snd_soc_component_update_bits(component, RT5663_RECMIX, 0x8, 0x8);
+
+ while (i < 5) {
+ msleep(sleep_time[i]);
+ val = snd_soc_component_read(component, RT5663_CBJ_TYPE_2) & 0x0003;
+ if (val == 0x1 || val == 0x2 || val == 0x3)
+ break;
+ dev_dbg(component->dev, "%s: MX-0011 val=%x sleep %d\n",
+ __func__, val, sleep_time[i]);
+ i++;
+ }
+ dev_dbg(component->dev, "%s val = %d\n", __func__, val);
+ switch (val) {
+ case 1:
+ case 2:
+ rt5663->jack_type = SND_JACK_HEADSET;
+ rt5663_enable_push_button_irq(component, true);
+ break;
+ default:
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
+ snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_disable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+ rt5663->jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+ } else {
+ snd_soc_component_update_bits(component, RT5663_RECMIX, 0x8, 0x0);
+
+ if (rt5663->jack_type == SND_JACK_HEADSET) {
+ rt5663_enable_push_button_irq(component, false);
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
+ snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+ snd_soc_dapm_disable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+ }
+ rt5663->jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type);
+ return rt5663->jack_type;
+}
+
+/**
+ * rt5663_jack_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5663_jack_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ int val, i = 0;
+
+ dev_dbg(component->dev, "%s jack_insert:%d\n", __func__, jack_insert);
+
+ if (jack_insert) {
+ snd_soc_component_update_bits(component, RT5663_DIG_MISC,
+ RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN);
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
+ RT5663_SI_HP_MASK | RT5663_OSW_HP_L_MASK |
+ RT5663_OSW_HP_R_MASK, RT5663_SI_HP_EN |
+ RT5663_OSW_HP_L_DIS | RT5663_OSW_HP_R_DIS);
+ snd_soc_component_update_bits(component, RT5663_DUMMY_1,
+ RT5663_EMB_CLK_MASK | RT5663_HPA_CPL_BIAS_MASK |
+ RT5663_HPA_CPR_BIAS_MASK, RT5663_EMB_CLK_EN |
+ RT5663_HPA_CPL_BIAS_1 | RT5663_HPA_CPR_BIAS_1);
+ snd_soc_component_update_bits(component, RT5663_CBJ_1,
+ RT5663_INBUF_CBJ_BST1_MASK | RT5663_CBJ_SENSE_BST1_MASK,
+ RT5663_INBUF_CBJ_BST1_ON | RT5663_CBJ_SENSE_BST1_L);
+ snd_soc_component_update_bits(component, RT5663_IL_CMD_2,
+ RT5663_PWR_MIC_DET_MASK, RT5663_PWR_MIC_DET_ON);
+ /* BST1 power on for JD */
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
+ RT5663_PWR_BST1_MASK, RT5663_PWR_BST1_ON);
+ snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1,
+ RT5663_CBJ_DET_MASK | RT5663_EXT_JD_MASK |
+ RT5663_POL_EXT_JD_MASK, RT5663_CBJ_DET_EN |
+ RT5663_EXT_JD_EN | RT5663_POL_EXT_JD_EN);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_MB_MASK | RT5663_LDO1_DVO_MASK |
+ RT5663_AMP_HP_MASK, RT5663_PWR_MB |
+ RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_VREF1 | RT5663_PWR_VREF2);
+ msleep(20);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5663_AUTO_1MRC_CLK,
+ RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN);
+ snd_soc_component_update_bits(component, RT5663_IRQ_1,
+ RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN);
+ snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1,
+ RT5663_EM_JD_MASK, RT5663_EM_JD_RST);
+ snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1,
+ RT5663_EM_JD_MASK, RT5663_EM_JD_NOR);
+
+ while (true) {
+ regmap_read(rt5663->regmap, RT5663_INT_ST_2, &val);
+ if (!(val & 0x80))
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (i > 200)
+ break;
+ i++;
+ }
+
+ val = snd_soc_component_read(component, RT5663_EM_JACK_TYPE_2) & 0x0003;
+ dev_dbg(component->dev, "%s val = %d\n", __func__, val);
+
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
+ RT5663_OSW_HP_L_MASK | RT5663_OSW_HP_R_MASK,
+ RT5663_OSW_HP_L_EN | RT5663_OSW_HP_R_EN);
+
+ switch (val) {
+ case 1:
+ case 2:
+ rt5663->jack_type = SND_JACK_HEADSET;
+ rt5663_enable_push_button_irq(component, true);
+
+ if (rt5663->pdata.impedance_sensing_num)
+ break;
+
+ if (rt5663->pdata.dc_offset_l_manual_mic) {
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2,
+ rt5663->pdata.dc_offset_l_manual_mic >>
+ 16);
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_3,
+ rt5663->pdata.dc_offset_l_manual_mic &
+ 0xffff);
+ }
+
+ if (rt5663->pdata.dc_offset_r_manual_mic) {
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_5,
+ rt5663->pdata.dc_offset_r_manual_mic >>
+ 16);
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_6,
+ rt5663->pdata.dc_offset_r_manual_mic &
+ 0xffff);
+ }
+ break;
+ default:
+ rt5663->jack_type = SND_JACK_HEADPHONE;
+ snd_soc_component_update_bits(component,
+ RT5663_PWR_ANLG_1,
+ RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK |
+ RT5663_PWR_VREF2_MASK, 0);
+ if (rt5663->pdata.impedance_sensing_num)
+ break;
+
+ if (rt5663->pdata.dc_offset_l_manual) {
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2,
+ rt5663->pdata.dc_offset_l_manual >> 16);
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_3,
+ rt5663->pdata.dc_offset_l_manual &
+ 0xffff);
+ }
+
+ if (rt5663->pdata.dc_offset_r_manual) {
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_5,
+ rt5663->pdata.dc_offset_r_manual >> 16);
+ regmap_write(rt5663->regmap, RT5663_MIC_DECRO_6,
+ rt5663->pdata.dc_offset_r_manual &
+ 0xffff);
+ }
+ break;
+ }
+ } else {
+ if (rt5663->jack_type == SND_JACK_HEADSET)
+ rt5663_enable_push_button_irq(component, false);
+ rt5663->jack_type = 0;
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK |
+ RT5663_PWR_VREF2_MASK, 0);
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type);
+ return rt5663->jack_type;
+}
+
+static int rt5663_impedance_sensing(struct snd_soc_component *component)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80;
+
+ for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) {
+ if (rt5663->imp_table[i].vol == 7)
+ break;
+ }
+
+ if (rt5663->jack_type == SND_JACK_HEADSET) {
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual_mic >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual_mic >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff);
+ } else {
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual & 0xffff);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual & 0xffff);
+ }
+
+ reg84 = snd_soc_component_read(component, RT5663_ASRC_2);
+ reg26 = snd_soc_component_read(component, RT5663_STO1_ADC_MIXER);
+ reg2fa = snd_soc_component_read(component, RT5663_DUMMY_1);
+ reg91 = snd_soc_component_read(component, RT5663_HP_CHARGE_PUMP_1);
+ reg10 = snd_soc_component_read(component, RT5663_RECMIX);
+ reg80 = snd_soc_component_read(component, RT5663_GLB_CLK);
+
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_1, 0x8000, 0);
+ snd_soc_component_write(component, RT5663_ASRC_2, 0);
+ snd_soc_component_write(component, RT5663_STO1_ADC_MIXER, 0x4040);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_VREF1 | RT5663_PWR_VREF2);
+ usleep_range(10000, 10005);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
+ RT5663_SCLK_SRC_RCCLK);
+ snd_soc_component_update_bits(component, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
+ RT5663_DIG_25M_CLK_EN);
+ snd_soc_component_update_bits(component, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0xff00);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0xfffc);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_1, 0x1232);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_2, 0x0005);
+ snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0x0030);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0x0003);
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_2,
+ RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F,
+ RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F);
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_1,
+ RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
+ RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 |
+ RT5663_PWR_ADC_R1,
+ RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
+ RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 |
+ RT5663_PWR_ADC_R1);
+ msleep(40);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
+ RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2,
+ RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2);
+ msleep(30);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ snd_soc_component_write(component, RT5663_STO_DAC_MIXER, 0);
+ snd_soc_component_write(component, RT5663_BYPASS_STO_DAC, 0x000c);
+ snd_soc_component_write(component, RT5663_HP_BIAS, 0xafaa);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_1, 0x2224);
+ snd_soc_component_write(component, RT5663_HP_OUT_EN, 0x8088);
+ snd_soc_component_write(component, RT5663_CHOP_ADC, 0x3000);
+ snd_soc_component_write(component, RT5663_ADDA_RST, 0xc000);
+ snd_soc_component_write(component, RT5663_STO1_HPF_ADJ1, 0x3320);
+ snd_soc_component_write(component, RT5663_HP_CALIB_2, 0x00c9);
+ snd_soc_component_write(component, RT5663_DUMMY_1, 0x004c);
+ snd_soc_component_write(component, RT5663_ANA_BIAS_CUR_1, 0x7733);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_2, 0x7777);
+ snd_soc_component_write(component, RT5663_STO_DRE_9, 0x0007);
+ snd_soc_component_write(component, RT5663_STO_DRE_10, 0x0007);
+ snd_soc_component_write(component, RT5663_DUMMY_2, 0x02a4);
+ snd_soc_component_write(component, RT5663_RECMIX, 0x0005);
+ snd_soc_component_write(component, RT5663_HP_IMP_SEN_1, 0x4334);
+ snd_soc_component_update_bits(component, RT5663_IRQ_3, 0x0004, 0x0004);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0x2200);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x3000);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0x6200);
+
+ for (i = 0; i < 100; i++) {
+ msleep(20);
+ if (snd_soc_component_read(component, RT5663_INT_ST_1) & 0x2)
+ break;
+ }
+
+ value = snd_soc_component_read(component, RT5663_HP_IMP_SEN_4);
+
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0);
+ snd_soc_component_write(component, RT5663_INT_ST_1, 0);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0);
+ snd_soc_component_update_bits(component, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
+ RT5663_DIG_25M_CLK_DIS);
+ snd_soc_component_write(component, RT5663_GLB_CLK, reg80);
+ snd_soc_component_write(component, RT5663_RECMIX, reg10);
+ snd_soc_component_write(component, RT5663_DUMMY_2, 0x00a4);
+ snd_soc_component_write(component, RT5663_DUMMY_1, reg2fa);
+ snd_soc_component_write(component, RT5663_HP_CALIB_2, 0x00c8);
+ snd_soc_component_write(component, RT5663_STO1_HPF_ADJ1, 0xb320);
+ snd_soc_component_write(component, RT5663_ADDA_RST, 0xe400);
+ snd_soc_component_write(component, RT5663_CHOP_ADC, 0x2000);
+ snd_soc_component_write(component, RT5663_HP_OUT_EN, 0x0008);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
+ RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0);
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_1,
+ RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
+ RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 |
+ RT5663_PWR_ADC_R1, 0);
+ snd_soc_component_update_bits(component, RT5663_PWR_DIG_2,
+ RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0);
+ snd_soc_component_write(component, RT5663_HP_LOGIC_2, 0);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_1, reg91);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0);
+ snd_soc_component_write(component, RT5663_STO1_ADC_MIXER, reg26);
+ snd_soc_component_write(component, RT5663_ASRC_2, reg84);
+
+ for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) {
+ if (value >= rt5663->imp_table[i].imp_min &&
+ value <= rt5663->imp_table[i].imp_max)
+ break;
+ }
+
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK,
+ rt5663->imp_table[i].vol);
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK,
+ rt5663->imp_table[i].vol);
+
+ if (rt5663->jack_type == SND_JACK_HEADSET) {
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual_mic >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual_mic >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff);
+ } else {
+ snd_soc_component_write(component, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual & 0xffff);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual >> 16);
+ snd_soc_component_write(component, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual & 0xffff);
+ }
+
+ return 0;
+}
+
+static int rt5663_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5663_IL_CMD_5);
+ dev_dbg(component->dev, "%s: val=0x%x\n", __func__, val);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5663_IL_CMD_5, val);
+
+ return btn_type;
+}
+
+static irqreturn_t rt5663_irq(int irq, void *data)
+{
+ struct rt5663_priv *rt5663 = data;
+
+ dev_dbg(regmap_get_device(rt5663->regmap), "%s IRQ queue work\n",
+ __func__);
+
+ queue_delayed_work(system_dfl_wq, &rt5663->jack_detect_work,
+ msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+static int rt5663_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ rt5663->hs_jack = hs_jack;
+
+ rt5663_irq(0, rt5663);
+
+ return 0;
+}
+
+static bool rt5663_check_jd_status(struct snd_soc_component *component)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ int val = snd_soc_component_read(component, RT5663_INT_ST_1);
+
+ dev_dbg(component->dev, "%s val=%x\n", __func__, val);
+
+ /* JD1 */
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ return !(val & 0x2000);
+ case CODEC_VER_0:
+ return !(val & 0x1000);
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+
+ return false;
+}
+
+static void rt5663_jack_detect_work(struct work_struct *work)
+{
+ struct rt5663_priv *rt5663 =
+ container_of(work, struct rt5663_priv, jack_detect_work.work);
+ struct snd_soc_component *component = rt5663->component;
+ int btn_type, report = 0;
+
+ if (!component)
+ return;
+
+ if (rt5663_check_jd_status(component)) {
+ /* jack in */
+ if (rt5663->jack_type == 0) {
+ /* jack was out, report jack type */
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ report = rt5663_v2_jack_detect(
+ rt5663->component, 1);
+ break;
+ case CODEC_VER_0:
+ report = rt5663_jack_detect(rt5663->component, 1);
+ if (rt5663->pdata.impedance_sensing_num)
+ rt5663_impedance_sensing(rt5663->component);
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+
+ /* Delay the jack insert report to avoid pop noise */
+ msleep(30);
+ } else {
+ /* jack is already in, report button event */
+ report = SND_JACK_HEADSET;
+ btn_type = rt5663_button_detect(rt5663->component);
+ /**
+ * rt5663 can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ report |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ report |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ report |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ report |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ btn_type = 0;
+ dev_err(rt5663->component->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ /* button release or spurious interrput*/
+ if (btn_type == 0) {
+ report = rt5663->jack_type;
+ cancel_delayed_work_sync(
+ &rt5663->jd_unplug_work);
+ } else {
+ queue_delayed_work(system_dfl_wq,
+ &rt5663->jd_unplug_work,
+ msecs_to_jiffies(500));
+ }
+ }
+ } else {
+ /* jack out */
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ report = rt5663_v2_jack_detect(rt5663->component, 0);
+ break;
+ case CODEC_VER_0:
+ report = rt5663_jack_detect(rt5663->component, 0);
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+ }
+ dev_dbg(component->dev, "%s jack report: 0x%04x\n", __func__, report);
+ snd_soc_jack_report(rt5663->hs_jack, report, SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+}
+
+static void rt5663_jd_unplug_work(struct work_struct *work)
+{
+ struct rt5663_priv *rt5663 =
+ container_of(work, struct rt5663_priv, jd_unplug_work.work);
+ struct snd_soc_component *component = rt5663->component;
+
+ if (!component)
+ return;
+
+ if (!rt5663_check_jd_status(component)) {
+ /* jack out */
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ rt5663_v2_jack_detect(rt5663->component, 0);
+ break;
+ case CODEC_VER_0:
+ rt5663_jack_detect(rt5663->component, 0);
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+
+ snd_soc_jack_report(rt5663->hs_jack, 0, SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else {
+ queue_delayed_work(system_dfl_wq, &rt5663->jd_unplug_work,
+ msecs_to_jiffies(500));
+ }
+}
+
+static const struct snd_kcontrol_new rt5663_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL,
+ RT5663_DAC_L1_VOL_SHIFT + 1, RT5663_DAC_R1_VOL_SHIFT + 1,
+ 87, 0, dac_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5663_STO1_ADC_DIG_VOL,
+ RT5663_ADC_L_MUTE_SHIFT, RT5663_ADC_R_MUTE_SHIFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5663_STO1_ADC_DIG_VOL,
+ RT5663_ADC_L_VOL_SHIFT + 1, RT5663_ADC_R_VOL_SHIFT + 1,
+ 63, 0, adc_vol_tlv),
+};
+
+static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_HP_LCH_DRE,
+ RT5663_HP_RCH_DRE, RT5663_GAIN_HP_SHIFT, 15, 1,
+ rt5663_v2_hp_vol_tlv),
+ /* Mic Boost Volume */
+ SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_AEC_BST,
+ RT5663_GAIN_CBJ_SHIFT, 8, 0, in_bst_tlv),
+};
+
+static const struct snd_kcontrol_new rt5663_specific_controls[] = {
+ /* Mic Boost Volume*/
+ SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2,
+ RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv),
+ /* Data Swap for Slot0/1 in ADCDAT1 */
+ SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum),
+};
+
+static const struct snd_kcontrol_new rt5663_hpvol_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9,
+ RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1,
+ rt5663_hp_vol_tlv),
+};
+
+static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5663_GLB_CLK);
+ val &= RT5663_SCLK_SRC_MASK;
+ if (val == RT5663_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int rt5663_is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, shift, val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ if (rt5663->codec_ver == CODEC_VER_1) {
+ switch (w->shift) {
+ case RT5663_ADC_STO1_ASRC_SHIFT:
+ reg = RT5663_ASRC_3;
+ shift = RT5663_V2_AD_STO1_TRACK_SHIFT;
+ break;
+ case RT5663_DAC_STO1_ASRC_SHIFT:
+ reg = RT5663_ASRC_2;
+ shift = RT5663_DA_STO1_TRACK_SHIFT;
+ break;
+ default:
+ return 0;
+ }
+ } else {
+ switch (w->shift) {
+ case RT5663_ADC_STO1_ASRC_SHIFT:
+ reg = RT5663_ASRC_2;
+ shift = RT5663_AD_STO1_TRACK_SHIFT;
+ break;
+ case RT5663_DAC_STO1_ASRC_SHIFT:
+ reg = RT5663_ASRC_2;
+ shift = RT5663_DA_STO1_TRACK_SHIFT;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0x7;
+
+ if (val)
+ return 1;
+
+ return 0;
+}
+
+static int rt5663_i2s_use_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ int da_asrc_en, ad_asrc_en;
+
+ da_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_2) &
+ RT5663_DA_STO1_TRACK_MASK) ? 1 : 0;
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ ad_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_3) &
+ RT5663_V2_AD_STO1_TRACK_MASK) ? 1 : 0;
+ break;
+ case CODEC_VER_0:
+ ad_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_2) &
+ RT5663_AD_STO1_TRACK_MASK) ? 1 : 0;
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ return 1;
+ }
+
+ if (da_asrc_en || ad_asrc_en)
+ if (rt5663->sysclk > rt5663->lrck * 384)
+ return 1;
+
+ dev_err(component->dev, "sysclk < 384 x fs, disable i2s asrc\n");
+
+ return 0;
+}
+
+/**
+ * rt5663_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @component: SoC audio component device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5663 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the codec driver will turn on ASRC
+ * for these filters if ASRC is selected as their clock source.
+ */
+int rt5663_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ unsigned int asrc2_mask = 0;
+ unsigned int asrc2_value = 0;
+ unsigned int asrc3_mask = 0;
+ unsigned int asrc3_value = 0;
+
+ switch (clk_src) {
+ case RT5663_CLK_SEL_SYS:
+ case RT5663_CLK_SEL_I2S1_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5663_DA_STEREO_FILTER) {
+ asrc2_mask |= RT5663_DA_STO1_TRACK_MASK;
+ asrc2_value |= clk_src << RT5663_DA_STO1_TRACK_SHIFT;
+ }
+
+ if (filter_mask & RT5663_AD_STEREO_FILTER) {
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ asrc3_mask |= RT5663_V2_AD_STO1_TRACK_MASK;
+ asrc3_value |= clk_src << RT5663_V2_AD_STO1_TRACK_SHIFT;
+ break;
+ case CODEC_VER_0:
+ asrc2_mask |= RT5663_AD_STO1_TRACK_MASK;
+ asrc2_value |= clk_src << RT5663_AD_STO1_TRACK_SHIFT;
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ }
+ }
+
+ if (asrc2_mask)
+ snd_soc_component_update_bits(component, RT5663_ASRC_2, asrc2_mask,
+ asrc2_value);
+
+ if (asrc3_mask)
+ snd_soc_component_update_bits(component, RT5663_ASRC_3, asrc3_mask,
+ asrc3_value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5663_sel_asrc_clk_src);
+
+/* Analog Mixer */
+static const struct snd_kcontrol_new rt5663_recmix1l[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5663_RECMIX1L,
+ RT5663_RECMIX1L_BST2_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 CBJ Switch", RT5663_RECMIX1L,
+ RT5663_RECMIX1L_BST1_CBJ_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_recmix1r[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5663_RECMIX1R,
+ RT5663_RECMIX1R_BST2_SHIFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5663_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER,
+ RT5663_M_STO1_ADC_L1_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER,
+ RT5663_M_STO1_ADC_L2_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER,
+ RT5663_M_STO1_ADC_R1_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER,
+ RT5663_M_STO1_ADC_R2_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_adda_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC L Switch", RT5663_AD_DA_MIXER,
+ RT5663_M_ADCMIX_L_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L Switch", RT5663_AD_DA_MIXER,
+ RT5663_M_DAC1_L_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_adda_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC R Switch", RT5663_AD_DA_MIXER,
+ RT5663_M_ADCMIX_R_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R Switch", RT5663_AD_DA_MIXER,
+ RT5663_M_DAC1_R_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L Switch", RT5663_STO_DAC_MIXER,
+ RT5663_M_DAC_L1_STO_L_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R Switch", RT5663_STO_DAC_MIXER,
+ RT5663_M_DAC_R1_STO_R_SHIFT, 1, 1),
+};
+
+/* Out Switch */
+static const struct snd_kcontrol_new rt5663_hpo_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5663_HP_AMP_2,
+ RT5663_EN_DAC_HPO_SHIFT, 1, 0);
+
+/* Stereo ADC source */
+static const char * const rt5663_sto1_adc_src[] = {
+ "ADC L", "ADC R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5663_sto1_adcl_enum, RT5663_STO1_ADC_MIXER,
+ RT5663_STO1_ADC_L_SRC_SHIFT, rt5663_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5663_sto1_adcl_mux =
+ SOC_DAPM_ENUM("STO1 ADC L Mux", rt5663_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5663_sto1_adcr_enum, RT5663_STO1_ADC_MIXER,
+ RT5663_STO1_ADC_R_SRC_SHIFT, rt5663_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5663_sto1_adcr_mux =
+ SOC_DAPM_ENUM("STO1 ADC R Mux", rt5663_sto1_adcr_enum);
+
+/* RT5663: Analog DACL1 input source */
+static const char * const rt5663_alg_dacl_src[] = {
+ "DAC L", "STO DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5663_alg_dacl_enum, RT5663_BYPASS_STO_DAC,
+ RT5663_DACL1_SRC_SHIFT, rt5663_alg_dacl_src);
+
+static const struct snd_kcontrol_new rt5663_alg_dacl_mux =
+ SOC_DAPM_ENUM("DAC L Mux", rt5663_alg_dacl_enum);
+
+/* RT5663: Analog DACR1 input source */
+static const char * const rt5663_alg_dacr_src[] = {
+ "DAC R", "STO DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5663_alg_dacr_enum, RT5663_BYPASS_STO_DAC,
+ RT5663_DACR1_SRC_SHIFT, rt5663_alg_dacr_src);
+
+static const struct snd_kcontrol_new rt5663_alg_dacr_mux =
+ SOC_DAPM_ENUM("DAC R Mux", rt5663_alg_dacr_enum);
+
+static int rt5663_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (rt5663->codec_ver == CODEC_VER_1) {
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
+ RT5663_SEL_PM_HP_SHIFT, RT5663_SEL_PM_HP_HIGH);
+ snd_soc_component_update_bits(component, RT5663_HP_LOGIC_2,
+ RT5663_HP_SIG_SRC1_MASK,
+ RT5663_HP_SIG_SRC1_SILENCE);
+ } else {
+ snd_soc_component_update_bits(component,
+ RT5663_DACREF_LDO, 0x3e0e, 0x3a0a);
+ snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003);
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
+ RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_DIS);
+ snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ snd_soc_component_write(component, RT5663_HP_BIAS, 0xabba);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_1, 0x2224);
+ snd_soc_component_write(component, RT5663_ANA_BIAS_CUR_1, 0x7766);
+ snd_soc_component_write(component, RT5663_HP_BIAS, 0xafaa);
+ snd_soc_component_write(component, RT5663_CHARGE_PUMP_2, 0x7777);
+ snd_soc_component_update_bits(component, RT5663_STO_DRE_1, 0x8000,
+ 0x8000);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000,
+ 0x3000);
+ snd_soc_component_update_bits(component,
+ RT5663_DIG_VOL_ZCD, 0x00c0, 0x0080);
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ if (rt5663->codec_ver == CODEC_VER_1) {
+ snd_soc_component_update_bits(component, RT5663_HP_LOGIC_2,
+ RT5663_HP_SIG_SRC1_MASK,
+ RT5663_HP_SIG_SRC1_REG);
+ } else {
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x0);
+ snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1,
+ RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN);
+ snd_soc_component_update_bits(component,
+ RT5663_DACREF_LDO, 0x3e0e, 0);
+ snd_soc_component_update_bits(component,
+ RT5663_DIG_VOL_ZCD, 0x00c0, 0);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5663_charge_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (rt5663->codec_ver == CODEC_VER_0) {
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030,
+ 0x0030);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003,
+ 0x0003);
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (rt5663->codec_ver == CODEC_VER_0) {
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0);
+ snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5663_bst2_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
+ RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK,
+ RT5663_PWR_BST2 | RT5663_PWR_BST2_OP);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2,
+ RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5663_pre_div_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0xff00);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0xfffc);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0x0000);
+ snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0x0000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5663_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", RT5663_PWR_ANLG_3, RT5663_PWR_PLL_SHIFT, 0,
+ NULL, 0),
+
+ /* micbias */
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", RT5663_PWR_ANLG_2,
+ RT5663_PWR_MB1_SHIFT, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", RT5663_PWR_ANLG_2,
+ RT5663_PWR_MB2_SHIFT, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+
+ /* REC Mixer Power */
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5663_PWR_ANLG_2,
+ RT5663_PWR_RECMIX1_SHIFT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC L Power", RT5663_PWR_DIG_1,
+ RT5663_PWR_ADC_L1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", RT5663_CHOP_ADC,
+ RT5663_CKGEN_ADCC_SHIFT, 0, NULL, 0),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_MIXER("STO1 ADC MIXL", SND_SOC_NOPM,
+ 0, 0, rt5663_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5663_sto1_adc_l_mix)),
+
+ /* ADC Filter Power */
+ SND_SOC_DAPM_SUPPLY("STO1 ADC Filter", RT5663_PWR_DIG_2,
+ RT5663_PWR_ADC_S1F_SHIFT, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S", RT5663_PWR_DIG_1, RT5663_PWR_I2S1_SHIFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "AIF Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("ADDA MIXL", SND_SOC_NOPM, 0, 0, rt5663_adda_l_mix,
+ ARRAY_SIZE(rt5663_adda_l_mix)),
+ SND_SOC_DAPM_MIXER("ADDA MIXR", SND_SOC_NOPM, 0, 0, rt5663_adda_r_mix,
+ ARRAY_SIZE(rt5663_adda_r_mix)),
+ SND_SOC_DAPM_PGA("DAC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("STO1 DAC Filter", RT5663_PWR_DIG_2,
+ RT5663_PWR_DAC_S1F_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("STO1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5663_sto1_dac_l_mix, ARRAY_SIZE(rt5663_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("STO1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5663_sto1_dac_r_mix, ARRAY_SIZE(rt5663_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_SUPPLY("STO1 DAC L Power", RT5663_PWR_DIG_1,
+ RT5663_PWR_DAC_L1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("STO1 DAC R Power", RT5663_PWR_DIG_1,
+ RT5663_PWR_DAC_R1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Headphone*/
+ SND_SOC_DAPM_SUPPLY("HP Charge Pump", SND_SOC_NOPM, 0, 0,
+ rt5663_charge_pump_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5663_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_widget rt5663_v2_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5663_PWR_ANLG_3,
+ RT5663_PWR_LDO2_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5663_PWR_VOL,
+ RT5663_V2_PWR_MIC_DET_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO DAC", RT5663_PWR_DIG_1,
+ RT5663_PWR_LDO_DACREF_SHIFT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1,
+ RT5663_I2S1_ASRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1,
+ RT5663_DAC_STO1_ASRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1,
+ RT5663_ADC_STO1_ASRC_SHIFT, 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CBJ Power", RT5663_PWR_ANLG_3,
+ RT5663_PWR_CBJ_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST2 Power", SND_SOC_NOPM, 0, 0,
+ rt5663_bst2_power, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5663_recmix1l,
+ ARRAY_SIZE(rt5663_recmix1l)),
+ SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5663_recmix1r,
+ ARRAY_SIZE(rt5663_recmix1r)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5663_PWR_ANLG_2,
+ RT5663_PWR_RECMIX2_SHIFT, 0, NULL, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC R Power", RT5663_PWR_DIG_1,
+ RT5663_PWR_ADC_R1_SHIFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_PGA("STO1 ADC L1", RT5663_STO1_ADC_MIXER,
+ RT5663_STO1_ADC_L1_SRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC R1", RT5663_STO1_ADC_MIXER,
+ RT5663_STO1_ADC_R1_SRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC L2", RT5663_STO1_ADC_MIXER,
+ RT5663_STO1_ADC_L2_SRC_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC R2", RT5663_STO1_ADC_MIXER,
+ RT5663_STO1_ADC_R2_SRC_SHIFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("STO1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5663_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("STO1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5663_sto1_adcr_mux),
+
+ /* ADC Mix */
+ SND_SOC_DAPM_MIXER("STO1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5663_sto1_adc_r_mix, ARRAY_SIZE(rt5663_sto1_adc_r_mix)),
+
+ /* Analog DAC Clock */
+ SND_SOC_DAPM_SUPPLY("DAC Clock", RT5663_CHOP_DAC_L,
+ RT5663_CKGEN_DAC1_SHIFT, 0, NULL, 0),
+
+ /* Headphone out */
+ SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0,
+ &rt5663_hpo_switch),
+};
+
+static const struct snd_soc_dapm_widget rt5663_specific_dapm_widgets[] = {
+ /* System Clock Pre Divider Gating */
+ SND_SOC_DAPM_SUPPLY("Pre Div Power", SND_SOC_NOPM, 0, 0,
+ rt5663_pre_div_power, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* LDO */
+ SND_SOC_DAPM_SUPPLY("LDO ADC", RT5663_PWR_DIG_1,
+ RT5663_PWR_LDO_DACREF_SHIFT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1,
+ RT5663_I2S1_ASRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1,
+ RT5663_DAC_STO1_ASRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1,
+ RT5663_ADC_STO1_ASRC_SHIFT, 0, NULL, 0),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* STO ADC */
+ SND_SOC_DAPM_PGA("STO1 ADC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog DAC source */
+ SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0, &rt5663_alg_dacl_mux),
+ SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0, &rt5663_alg_dacr_mux),
+};
+
+static const struct snd_soc_dapm_route rt5663_dapm_routes[] = {
+ /* PLL */
+ { "I2S", NULL, "PLL", rt5663_is_sys_clk_from_pll },
+
+ /* ASRC */
+ { "STO1 ADC Filter", NULL, "ADC ASRC", rt5663_is_using_asrc },
+ { "STO1 DAC Filter", NULL, "DAC ASRC", rt5663_is_using_asrc },
+ { "I2S", NULL, "I2S ASRC", rt5663_i2s_use_asrc },
+
+ { "ADC L", NULL, "ADC L Power" },
+ { "ADC L", NULL, "ADC Clock" },
+
+ { "STO1 ADC L2", NULL, "STO1 DAC MIXL" },
+
+ { "STO1 ADC MIXL", "ADC1 Switch", "STO1 ADC L1" },
+ { "STO1 ADC MIXL", "ADC2 Switch", "STO1 ADC L2" },
+ { "STO1 ADC MIXL", NULL, "STO1 ADC Filter" },
+
+ { "IF1 ADC1", NULL, "STO1 ADC MIXL" },
+ { "IF ADC", NULL, "IF1 ADC1" },
+ { "AIFTX", NULL, "IF ADC" },
+ { "AIFTX", NULL, "I2S" },
+
+ { "AIFRX", NULL, "I2S" },
+ { "IF DAC", NULL, "AIFRX" },
+ { "IF1 DAC1 L", NULL, "IF DAC" },
+ { "IF1 DAC1 R", NULL, "IF DAC" },
+
+ { "ADDA MIXL", "ADC L Switch", "STO1 ADC MIXL" },
+ { "ADDA MIXL", "DAC L Switch", "IF1 DAC1 L" },
+ { "ADDA MIXL", NULL, "STO1 DAC Filter" },
+ { "ADDA MIXL", NULL, "STO1 DAC L Power" },
+ { "ADDA MIXR", "DAC R Switch", "IF1 DAC1 R" },
+ { "ADDA MIXR", NULL, "STO1 DAC Filter" },
+ { "ADDA MIXR", NULL, "STO1 DAC R Power" },
+
+ { "DAC L1", NULL, "ADDA MIXL" },
+ { "DAC R1", NULL, "ADDA MIXR" },
+
+ { "STO1 DAC MIXL", "DAC L Switch", "DAC L1" },
+ { "STO1 DAC MIXL", NULL, "STO1 DAC L Power" },
+ { "STO1 DAC MIXL", NULL, "STO1 DAC Filter" },
+ { "STO1 DAC MIXR", "DAC R Switch", "DAC R1" },
+ { "STO1 DAC MIXR", NULL, "STO1 DAC R Power" },
+ { "STO1 DAC MIXR", NULL, "STO1 DAC Filter" },
+
+ { "HP Amp", NULL, "HP Charge Pump" },
+ { "HP Amp", NULL, "DAC L" },
+ { "HP Amp", NULL, "DAC R" },
+};
+
+static const struct snd_soc_dapm_route rt5663_v2_specific_dapm_routes[] = {
+ { "MICBIAS1", NULL, "LDO2" },
+ { "MICBIAS2", NULL, "LDO2" },
+
+ { "BST1 CBJ", NULL, "IN1P" },
+ { "BST1 CBJ", NULL, "IN1N" },
+ { "BST1 CBJ", NULL, "CBJ Power" },
+
+ { "BST2", NULL, "IN2P" },
+ { "BST2", NULL, "IN2N" },
+ { "BST2", NULL, "BST2 Power" },
+
+ { "RECMIX1L", "BST2 Switch", "BST2" },
+ { "RECMIX1L", "BST1 CBJ Switch", "BST1 CBJ" },
+ { "RECMIX1L", NULL, "RECMIX1L Power" },
+ { "RECMIX1R", "BST2 Switch", "BST2" },
+ { "RECMIX1R", NULL, "RECMIX1R Power" },
+
+ { "ADC L", NULL, "RECMIX1L" },
+ { "ADC R", NULL, "RECMIX1R" },
+ { "ADC R", NULL, "ADC R Power" },
+ { "ADC R", NULL, "ADC Clock" },
+
+ { "STO1 ADC L Mux", "ADC L", "ADC L" },
+ { "STO1 ADC L Mux", "ADC R", "ADC R" },
+ { "STO1 ADC L1", NULL, "STO1 ADC L Mux" },
+
+ { "STO1 ADC R Mux", "ADC L", "ADC L" },
+ { "STO1 ADC R Mux", "ADC R", "ADC R" },
+ { "STO1 ADC R1", NULL, "STO1 ADC R Mux" },
+ { "STO1 ADC R2", NULL, "STO1 DAC MIXR" },
+
+ { "STO1 ADC MIXR", "ADC1 Switch", "STO1 ADC R1" },
+ { "STO1 ADC MIXR", "ADC2 Switch", "STO1 ADC R2" },
+ { "STO1 ADC MIXR", NULL, "STO1 ADC Filter" },
+
+ { "IF1 ADC1", NULL, "STO1 ADC MIXR" },
+
+ { "ADDA MIXR", "ADC R Switch", "STO1 ADC MIXR" },
+
+ { "DAC L", NULL, "STO1 DAC MIXL" },
+ { "DAC L", NULL, "LDO DAC" },
+ { "DAC L", NULL, "DAC Clock" },
+ { "DAC R", NULL, "STO1 DAC MIXR" },
+ { "DAC R", NULL, "LDO DAC" },
+ { "DAC R", NULL, "DAC Clock" },
+
+ { "HPO Playback", "Switch", "HP Amp" },
+ { "HPOL", NULL, "HPO Playback" },
+ { "HPOR", NULL, "HPO Playback" },
+};
+
+static const struct snd_soc_dapm_route rt5663_specific_dapm_routes[] = {
+ { "I2S", NULL, "Pre Div Power" },
+
+ { "BST1", NULL, "IN1P" },
+ { "BST1", NULL, "IN1N" },
+ { "BST1", NULL, "RECMIX1L Power" },
+
+ { "ADC L", NULL, "BST1" },
+
+ { "STO1 ADC L1", NULL, "ADC L" },
+
+ { "DAC L Mux", "DAC L", "DAC L1" },
+ { "DAC L Mux", "STO DAC MIXL", "STO1 DAC MIXL" },
+ { "DAC R Mux", "DAC R", "DAC R1"},
+ { "DAC R Mux", "STO DAC MIXR", "STO1 DAC MIXR" },
+
+ { "DAC L", NULL, "DAC L Mux" },
+ { "DAC R", NULL, "DAC R Mux" },
+
+ { "HPOL", NULL, "HP Amp" },
+ { "HPOR", NULL, "HP Amp" },
+};
+
+static int rt5663_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0;
+ int pre_div;
+
+ rt5663->lrck = params_rate(params);
+
+ dev_dbg(dai->dev, "bclk is %dHz and sysclk is %dHz\n",
+ rt5663->lrck, rt5663->sysclk);
+
+ pre_div = rl6231_get_clk_info(rt5663->sysclk, rt5663->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
+ rt5663->lrck, dai->id);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "pre_div is %d for iis %d\n", pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 8:
+ val_len = RT5663_I2S_DL_8;
+ break;
+ case 16:
+ val_len = RT5663_I2S_DL_16;
+ break;
+ case 20:
+ val_len = RT5663_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT5663_I2S_DL_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5663_I2S1_SDP,
+ RT5663_I2S_DL_MASK, val_len);
+
+ snd_soc_component_update_bits(component, RT5663_ADDA_CLK_1,
+ RT5663_I2S_PD1_MASK, pre_div << RT5663_I2S_PD1_SHIFT);
+
+ return 0;
+}
+
+static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT5663_I2S_MS_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5663_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5663_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5663_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5663_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5663_I2S1_SDP, RT5663_I2S_MS_MASK |
+ RT5663_I2S_BP_MASK | RT5663_I2S_DF_MASK, reg_val);
+
+ return 0;
+}
+
+static int rt5663_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5663->sysclk && clk_id == rt5663->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5663_SCLK_S_MCLK:
+ reg_val |= RT5663_SCLK_SRC_MCLK;
+ break;
+ case RT5663_SCLK_S_PLL1:
+ reg_val |= RT5663_SCLK_SRC_PLL1;
+ break;
+ case RT5663_SCLK_S_RCCLK:
+ reg_val |= RT5663_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
+ reg_val);
+ rt5663->sysclk = freq;
+ rt5663->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+ int mask, shift, val;
+
+ if (source == rt5663->pll_src && freq_in == rt5663->pll_in &&
+ freq_out == rt5663->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5663->pll_in = 0;
+ rt5663->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK,
+ RT5663_SCLK_SRC_MASK, RT5663_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ mask = RT5663_V2_PLL1_SRC_MASK;
+ shift = RT5663_V2_PLL1_SRC_SHIFT;
+ break;
+ case CODEC_VER_0:
+ mask = RT5663_PLL1_SRC_MASK;
+ shift = RT5663_PLL1_SRC_SHIFT;
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ return -EINVAL;
+ }
+
+ switch (source) {
+ case RT5663_PLL1_S_MCLK:
+ val = 0x0;
+ break;
+ case RT5663_PLL1_S_BCLK1:
+ val = 0x1;
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5663_GLB_CLK, mask, (val << shift));
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", pll_code.m_bp,
+ (pll_code.m_bp ? 0 : pll_code.m_code), pll_code.n_code,
+ pll_code.k_code);
+
+ snd_soc_component_write(component, RT5663_PLL_1,
+ pll_code.n_code << RT5663_PLL_N_SHIFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5663_PLL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5663_PLL_M_SHIFT) |
+ (pll_code.m_bp << RT5663_PLL_M_BP_SHIFT));
+
+ rt5663->pll_in = freq_in;
+ rt5663->pll_out = freq_out;
+ rt5663->pll_src = source;
+
+ return 0;
+}
+
+static int rt5663_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, reg;
+
+ if (rx_mask || tx_mask)
+ val |= RT5663_TDM_MODE_TDM;
+
+ switch (slots) {
+ case 4:
+ val |= RT5663_TDM_IN_CH_4;
+ val |= RT5663_TDM_OUT_CH_4;
+ break;
+ case 6:
+ val |= RT5663_TDM_IN_CH_6;
+ val |= RT5663_TDM_OUT_CH_6;
+ break;
+ case 8:
+ val |= RT5663_TDM_IN_CH_8;
+ val |= RT5663_TDM_OUT_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5663_TDM_IN_LEN_20;
+ val |= RT5663_TDM_OUT_LEN_20;
+ break;
+ case 24:
+ val |= RT5663_TDM_IN_LEN_24;
+ val |= RT5663_TDM_OUT_LEN_24;
+ break;
+ case 32:
+ val |= RT5663_TDM_IN_LEN_32;
+ val |= RT5663_TDM_OUT_LEN_32;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ reg = RT5663_TDM_2;
+ break;
+ case CODEC_VER_0:
+ reg = RT5663_TDM_1;
+ break;
+ default:
+ dev_err(component->dev, "Unknown CODEC Version\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, reg, RT5663_TDM_MODE_MASK |
+ RT5663_TDM_IN_CH_MASK | RT5663_TDM_OUT_CH_MASK |
+ RT5663_TDM_IN_LEN_MASK | RT5663_TDM_OUT_LEN_MASK, val);
+
+ return 0;
+}
+
+static int rt5663_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ dev_dbg(component->dev, "%s ratio = %d\n", __func__, ratio);
+
+ if (rt5663->codec_ver == CODEC_VER_1)
+ reg = RT5663_TDM_9;
+ else
+ reg = RT5663_TDM_5;
+
+ switch (ratio) {
+ case 32:
+ snd_soc_component_update_bits(component, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_16);
+ break;
+ case 40:
+ snd_soc_component_update_bits(component, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_20);
+ break;
+ case 48:
+ snd_soc_component_update_bits(component, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_24);
+ break;
+ case 64:
+ snd_soc_component_update_bits(component, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_32);
+ break;
+ default:
+ dev_err(component->dev, "Invalid ratio!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5663_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (rt5663->codec_ver == CODEC_VER_1) {
+ snd_soc_component_update_bits(component, RT5663_DIG_MISC,
+ RT5663_DIG_GATE_CTRL_MASK,
+ RT5663_DIG_GATE_CTRL_EN);
+ snd_soc_component_update_bits(component, RT5663_SIG_CLK_DET,
+ RT5663_EN_ANA_CLK_DET_MASK |
+ RT5663_PWR_CLK_DET_MASK,
+ RT5663_EN_ANA_CLK_DET_AUTO |
+ RT5663_PWR_CLK_DET_EN);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (rt5663->codec_ver == CODEC_VER_1)
+ snd_soc_component_update_bits(component, RT5663_DIG_MISC,
+ RT5663_DIG_GATE_CTRL_MASK,
+ RT5663_DIG_GATE_CTRL_DIS);
+ snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK |
+ RT5663_PWR_MB_MASK, RT5663_PWR_VREF1 |
+ RT5663_PWR_VREF2 | RT5663_PWR_MB);
+ usleep_range(10000, 10005);
+ if (rt5663->codec_ver == CODEC_VER_1) {
+ snd_soc_component_update_bits(component, RT5663_SIG_CLK_DET,
+ RT5663_EN_ANA_CLK_DET_MASK |
+ RT5663_PWR_CLK_DET_MASK,
+ RT5663_EN_ANA_CLK_DET_DIS |
+ RT5663_PWR_CLK_DET_DIS);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (rt5663->jack_type != SND_JACK_HEADSET)
+ snd_soc_component_update_bits(component,
+ RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1 | RT5663_PWR_FV2 |
+ RT5663_PWR_MB_MASK, 0);
+ else
+ snd_soc_component_update_bits(component,
+ RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5663_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ rt5663->component = component;
+
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ snd_soc_dapm_new_controls(dapm,
+ rt5663_v2_specific_dapm_widgets,
+ ARRAY_SIZE(rt5663_v2_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt5663_v2_specific_dapm_routes,
+ ARRAY_SIZE(rt5663_v2_specific_dapm_routes));
+ snd_soc_add_component_controls(component, rt5663_v2_specific_controls,
+ ARRAY_SIZE(rt5663_v2_specific_controls));
+ break;
+ case CODEC_VER_0:
+ snd_soc_dapm_new_controls(dapm,
+ rt5663_specific_dapm_widgets,
+ ARRAY_SIZE(rt5663_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt5663_specific_dapm_routes,
+ ARRAY_SIZE(rt5663_specific_dapm_routes));
+ snd_soc_add_component_controls(component, rt5663_specific_controls,
+ ARRAY_SIZE(rt5663_specific_controls));
+
+ if (!rt5663->imp_table)
+ snd_soc_add_component_controls(component, rt5663_hpvol_controls,
+ ARRAY_SIZE(rt5663_hpvol_controls));
+ break;
+ }
+
+ return 0;
+}
+
+static void rt5663_remove(struct snd_soc_component *component)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt5663->regmap, RT5663_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt5663_suspend(struct snd_soc_component *component)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ if (rt5663->irq)
+ disable_irq(rt5663->irq);
+
+ cancel_delayed_work_sync(&rt5663->jack_detect_work);
+ cancel_delayed_work_sync(&rt5663->jd_unplug_work);
+
+ regcache_cache_only(rt5663->regmap, true);
+ regcache_mark_dirty(rt5663->regmap);
+
+ return 0;
+}
+
+static int rt5663_resume(struct snd_soc_component *component)
+{
+ struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5663->regmap, false);
+ regcache_sync(rt5663->regmap);
+
+ rt5663_irq(0, rt5663);
+
+ if (rt5663->irq)
+ enable_irq(rt5663->irq);
+
+ return 0;
+}
+#else
+#define rt5663_suspend NULL
+#define rt5663_resume NULL
+#endif
+
+#define RT5663_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5663_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5663_aif_dai_ops = {
+ .hw_params = rt5663_hw_params,
+ .set_fmt = rt5663_set_dai_fmt,
+ .set_sysclk = rt5663_set_dai_sysclk,
+ .set_pll = rt5663_set_dai_pll,
+ .set_tdm_slot = rt5663_set_tdm_slot,
+ .set_bclk_ratio = rt5663_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt5663_dai[] = {
+ {
+ .name = "rt5663-aif",
+ .id = RT5663_AIF,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5663_STEREO_RATES,
+ .formats = RT5663_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5663_STEREO_RATES,
+ .formats = RT5663_FORMATS,
+ },
+ .ops = &rt5663_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5663 = {
+ .probe = rt5663_probe,
+ .remove = rt5663_remove,
+ .suspend = rt5663_suspend,
+ .resume = rt5663_resume,
+ .set_bias_level = rt5663_set_bias_level,
+ .controls = rt5663_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5663_snd_controls),
+ .dapm_widgets = rt5663_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5663_dapm_widgets),
+ .dapm_routes = rt5663_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5663_dapm_routes),
+ .set_jack = rt5663_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5663_v2_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x07fa,
+ .volatile_reg = rt5663_v2_volatile_register,
+ .readable_reg = rt5663_v2_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5663_v2_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg),
+};
+
+static const struct regmap_config rt5663_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x03f3,
+ .volatile_reg = rt5663_volatile_register,
+ .readable_reg = rt5663_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5663_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5663_reg),
+};
+
+static const struct regmap_config temp_regmap = {
+ .name = "nocache",
+ .reg_bits = 16,
+ .val_bits = 16,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x03f3,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct i2c_device_id rt5663_i2c_id[] = {
+ { "rt5663" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5663_of_match[] = {
+ { .compatible = "realtek,rt5663", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5663_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5663_acpi_match[] = {
+ { "10EC5663" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5663_acpi_match);
+#endif
+
+static void rt5663_v2_calibrate(struct rt5663_priv *rt5663)
+{
+ regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402);
+ regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x0100);
+ regmap_write(rt5663->regmap, RT5663_RECMIX, 0x4040);
+ regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x0001);
+ regmap_write(rt5663->regmap, RT5663_RC_CLK, 0x0380);
+ regmap_write(rt5663->regmap, RT5663_GLB_CLK, 0x8000);
+ regmap_write(rt5663->regmap, RT5663_ADDA_CLK_1, 0x1000);
+ regmap_write(rt5663->regmap, RT5663_CHOP_DAC_L, 0x3030);
+ regmap_write(rt5663->regmap, RT5663_CALIB_ADC, 0x3c05);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23e);
+ msleep(40);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23e);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_2, 0x0321);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_1, 0xfc00);
+ msleep(500);
+}
+
+static void rt5663_calibrate(struct rt5663_priv *rt5663)
+{
+ int value, count;
+
+ regmap_write(rt5663->regmap, RT5663_RESET, 0x0000);
+ msleep(20);
+ regmap_write(rt5663->regmap, RT5663_ANA_BIAS_CUR_4, 0x00a1);
+ regmap_write(rt5663->regmap, RT5663_RC_CLK, 0x0380);
+ regmap_write(rt5663->regmap, RT5663_GLB_CLK, 0x8000);
+ regmap_write(rt5663->regmap, RT5663_ADDA_CLK_1, 0x1000);
+ regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032);
+ regmap_write(rt5663->regmap, RT5663_HP_IMP_SEN_19, 0x000c);
+ regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x0324);
+ regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x8001);
+ regmap_write(rt5663->regmap, RT5663_VREFADJ_OP, 0x0f28);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23b);
+ msleep(30);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23b);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x8000);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x0008);
+ regmap_write(rt5663->regmap, RT5663_PRE_DIV_GATING_1, 0xffff);
+ regmap_write(rt5663->regmap, RT5663_PRE_DIV_GATING_2, 0xffff);
+ regmap_write(rt5663->regmap, RT5663_CBJ_1, 0x8c10);
+ regmap_write(rt5663->regmap, RT5663_IL_CMD_2, 0x00c1);
+ regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_1, 0xb880);
+ regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_2, 0x4110);
+ regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_2, 0x4118);
+
+ count = 0;
+ while (true) {
+ regmap_read(rt5663->regmap, RT5663_INT_ST_2, &value);
+ if (!(value & 0x80))
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (++count > 200)
+ break;
+ }
+
+ regmap_write(rt5663->regmap, RT5663_HP_IMP_SEN_19, 0x0000);
+ regmap_write(rt5663->regmap, RT5663_DEPOP_2, 0x3003);
+ regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x0038);
+ regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x003b);
+ regmap_write(rt5663->regmap, RT5663_PWR_DIG_2, 0x8400);
+ regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x8df8);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x8003);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x018c);
+ regmap_write(rt5663->regmap, RT5663_HP_CHARGE_PUMP_1, 0x1e32);
+ regmap_write(rt5663->regmap, RT5663_DUMMY_2, 0x8089);
+ regmap_write(rt5663->regmap, RT5663_DACREF_LDO, 0x3b0b);
+ msleep(40);
+ regmap_write(rt5663->regmap, RT5663_STO_DAC_MIXER, 0x0000);
+ regmap_write(rt5663->regmap, RT5663_BYPASS_STO_DAC, 0x000c);
+ regmap_write(rt5663->regmap, RT5663_HP_BIAS, 0xafaa);
+ regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_1, 0x2224);
+ regmap_write(rt5663->regmap, RT5663_HP_OUT_EN, 0x8088);
+ regmap_write(rt5663->regmap, RT5663_STO_DRE_9, 0x0017);
+ regmap_write(rt5663->regmap, RT5663_STO_DRE_10, 0x0017);
+ regmap_write(rt5663->regmap, RT5663_STO1_ADC_MIXER, 0x4040);
+ regmap_write(rt5663->regmap, RT5663_CHOP_ADC, 0x3000);
+ regmap_write(rt5663->regmap, RT5663_RECMIX, 0x0005);
+ regmap_write(rt5663->regmap, RT5663_ADDA_RST, 0xc000);
+ regmap_write(rt5663->regmap, RT5663_STO1_HPF_ADJ1, 0x3320);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_2, 0x00c9);
+ regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x004c);
+ regmap_write(rt5663->regmap, RT5663_ANA_BIAS_CUR_1, 0x1111);
+ regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0x4402);
+ regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_2, 0x3311);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_1, 0x0069);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_3, 0x06ce);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0x6800);
+ regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_2, 0x1100);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_7, 0x0057);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0xe800);
+
+ count = 0;
+ while (true) {
+ regmap_read(rt5663->regmap, RT5663_HP_CALIB_1_1, &value);
+ if (value & 0x8000)
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (count > 200)
+ return;
+ count++;
+ }
+
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0x6200);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_7, 0x0059);
+ regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0xe200);
+
+ count = 0;
+ while (true) {
+ regmap_read(rt5663->regmap, RT5663_HP_CALIB_1_1, &value);
+ if (value & 0x8000)
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (count > 200)
+ return;
+ count++;
+ }
+
+ regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_1, 0xb8e0);
+ usleep_range(10000, 10005);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0x003b);
+ usleep_range(10000, 10005);
+ regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x0000);
+ usleep_range(10000, 10005);
+ regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x000b);
+ usleep_range(10000, 10005);
+ regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x0008);
+ usleep_range(10000, 10005);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x0000);
+ usleep_range(10000, 10005);
+}
+
+static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
+{
+ int table_size;
+ int ret;
+
+ device_property_read_u32(dev, "realtek,dc_offset_l_manual",
+ &rt5663->pdata.dc_offset_l_manual);
+ device_property_read_u32(dev, "realtek,dc_offset_r_manual",
+ &rt5663->pdata.dc_offset_r_manual);
+ device_property_read_u32(dev, "realtek,dc_offset_l_manual_mic",
+ &rt5663->pdata.dc_offset_l_manual_mic);
+ device_property_read_u32(dev, "realtek,dc_offset_r_manual_mic",
+ &rt5663->pdata.dc_offset_r_manual_mic);
+ device_property_read_u32(dev, "realtek,impedance_sensing_num",
+ &rt5663->pdata.impedance_sensing_num);
+
+ if (rt5663->pdata.impedance_sensing_num) {
+ table_size = sizeof(struct impedance_mapping_table) *
+ rt5663->pdata.impedance_sensing_num;
+ rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL);
+ if (!rt5663->imp_table)
+ return -ENOMEM;
+ ret = device_property_read_u32_array(dev,
+ "realtek,impedance_sensing_table",
+ (u32 *)rt5663->imp_table, table_size);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rt5663_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5663_priv *rt5663;
+ int ret, i;
+ unsigned int val;
+ struct regmap *regmap;
+
+ rt5663 = devm_kzalloc(&i2c->dev, sizeof(struct rt5663_priv),
+ GFP_KERNEL);
+
+ if (rt5663 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5663);
+
+ if (pdata)
+ rt5663->pdata = *pdata;
+ else {
+ ret = rt5663_parse_dp(rt5663, &i2c->dev);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++)
+ rt5663->supplies[i].supply = rt5663_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(rt5663->supplies),
+ rt5663->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Set load for regulator. */
+ for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) {
+ ret = regulator_set_load(rt5663->supplies[i].consumer,
+ RT5663_SUPPLY_CURRENT_UA);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to set regulator load on %s, ret: %d\n",
+ rt5663->supplies[i].supply, ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5663->supplies),
+ rt5663->supplies);
+
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+ msleep(RT5663_POWER_ON_DELAY_MS);
+
+ regmap = devm_regmap_init_i2c(i2c, &temp_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
+ if (ret || (val != RT5663_DEVICE_ID_2 && val != RT5663_DEVICE_ID_1)) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5663, retry one time.\n",
+ val);
+ msleep(100);
+ regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
+ }
+
+ switch (val) {
+ case RT5663_DEVICE_ID_2:
+ rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_v2_regmap);
+ rt5663->codec_ver = CODEC_VER_1;
+ break;
+ case RT5663_DEVICE_ID_1:
+ rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_regmap);
+ rt5663->codec_ver = CODEC_VER_0;
+ break;
+ default:
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5663\n",
+ val);
+ ret = -ENODEV;
+ goto err_enable;
+ }
+
+ if (IS_ERR(rt5663->regmap)) {
+ ret = PTR_ERR(rt5663->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ /* reset and calibrate */
+ regmap_write(rt5663->regmap, RT5663_RESET, 0);
+ regcache_cache_bypass(rt5663->regmap, true);
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ rt5663_v2_calibrate(rt5663);
+ break;
+ case CODEC_VER_0:
+ rt5663_calibrate(rt5663);
+ break;
+ default:
+ dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__);
+ }
+ regcache_cache_bypass(rt5663->regmap, false);
+ regmap_write(rt5663->regmap, RT5663_RESET, 0);
+ dev_dbg(&i2c->dev, "calibrate done\n");
+
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ break;
+ case CODEC_VER_0:
+ ret = regmap_register_patch(rt5663->regmap, rt5663_patch_list,
+ ARRAY_SIZE(rt5663_patch_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev,
+ "Failed to apply regmap patch: %d\n", ret);
+ break;
+ default:
+ dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__);
+ }
+
+ /* GPIO1 as IRQ */
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, RT5663_GP1_PIN_MASK,
+ RT5663_GP1_PIN_IRQ);
+ /* 4btn inline command debounce */
+ regmap_update_bits(rt5663->regmap, RT5663_IL_CMD_5,
+ RT5663_4BTN_CLK_DEB_MASK, RT5663_4BTN_CLK_DEB_65MS);
+
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402);
+ /* JD1 */
+ regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK,
+ RT5663_IRQ_POW_SAV_MASK | RT5663_IRQ_POW_SAV_JD1_MASK,
+ RT5663_IRQ_POW_SAV_EN | RT5663_IRQ_POW_SAV_JD1_EN);
+ regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_2,
+ RT5663_PWR_JD1_MASK, RT5663_PWR_JD1);
+ regmap_update_bits(rt5663->regmap, RT5663_IRQ_1,
+ RT5663_EN_CB_JD_MASK, RT5663_EN_CB_JD_EN);
+
+ regmap_update_bits(rt5663->regmap, RT5663_HP_LOGIC_2,
+ RT5663_HP_SIG_SRC1_MASK, RT5663_HP_SIG_SRC1_REG);
+ regmap_update_bits(rt5663->regmap, RT5663_RECMIX,
+ RT5663_VREF_BIAS_MASK | RT5663_CBJ_DET_MASK |
+ RT5663_DET_TYPE_MASK, RT5663_VREF_BIAS_REG |
+ RT5663_CBJ_DET_EN | RT5663_DET_TYPE_QFN);
+ /* Set GPIO4 and GPIO8 as input for combo jack */
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_2,
+ RT5663_GP4_PIN_CONF_MASK, RT5663_GP4_PIN_CONF_INPUT);
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_3,
+ RT5663_GP8_PIN_CONF_MASK, RT5663_GP8_PIN_CONF_INPUT);
+ regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1,
+ RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK,
+ RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X);
+ break;
+ case CODEC_VER_0:
+ regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC,
+ RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN);
+ regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK,
+ RT5663_IRQ_MANUAL_MASK, RT5663_IRQ_MANUAL_EN);
+ regmap_update_bits(rt5663->regmap, RT5663_IRQ_1,
+ RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN);
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_1,
+ RT5663_GPIO1_TYPE_MASK, RT5663_GPIO1_TYPE_EN);
+ regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032);
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_2,
+ RT5663_GP1_PIN_CONF_MASK | RT5663_SEL_GPIO1_MASK,
+ RT5663_GP1_PIN_CONF_OUTPUT | RT5663_SEL_GPIO1_EN);
+ regmap_update_bits(rt5663->regmap, RT5663_RECMIX,
+ RT5663_RECMIX1_BST1_MASK, RT5663_RECMIX1_BST1_ON);
+ regmap_update_bits(rt5663->regmap, RT5663_TDM_2,
+ RT5663_DATA_SWAP_ADCDAT1_MASK,
+ RT5663_DATA_SWAP_ADCDAT1_LL);
+ break;
+ default:
+ dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__);
+ }
+
+ INIT_DELAYED_WORK(&rt5663->jack_detect_work, rt5663_jack_detect_work);
+ INIT_DELAYED_WORK(&rt5663->jd_unplug_work, rt5663_jd_unplug_work);
+
+ if (i2c->irq) {
+ ret = request_irq(i2c->irq, rt5663_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5663", rt5663);
+ if (ret) {
+ dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n",
+ __func__, ret);
+ goto err_enable;
+ }
+ rt5663->irq = i2c->irq;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5663,
+ rt5663_dai, ARRAY_SIZE(rt5663_dai));
+
+ if (ret)
+ goto err_enable;
+
+ return 0;
+
+
+ /*
+ * Error after enabling regulators should goto err_enable
+ * to disable regulators.
+ */
+err_enable:
+ if (i2c->irq)
+ free_irq(i2c->irq, rt5663);
+
+ regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies);
+ return ret;
+}
+
+static void rt5663_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt5663_priv *rt5663 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt5663);
+
+ regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies);
+}
+
+static void rt5663_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5663_priv *rt5663 = i2c_get_clientdata(client);
+
+ regmap_write(rt5663->regmap, RT5663_RESET, 0);
+}
+
+static struct i2c_driver rt5663_i2c_driver = {
+ .driver = {
+ .name = "rt5663",
+ .acpi_match_table = ACPI_PTR(rt5663_acpi_match),
+ .of_match_table = of_match_ptr(rt5663_of_match),
+ },
+ .probe = rt5663_i2c_probe,
+ .remove = rt5663_i2c_remove,
+ .shutdown = rt5663_i2c_shutdown,
+ .id_table = rt5663_i2c_id,
+};
+module_i2c_driver(rt5663_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5663 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h
new file mode 100644
index 000000000000..2c485d0655b5
--- /dev/null
+++ b/sound/soc/codecs/rt5663.h
@@ -0,0 +1,1128 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5663.h -- RT5663 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Microelectronics
+ * Author: Jack Yu <jack.yu@realtek.com>
+ */
+
+#ifndef __RT5663_H__
+#define __RT5663_H__
+
+#include <sound/rt5663.h>
+
+/* Info */
+#define RT5663_RESET 0x0000
+#define RT5663_VENDOR_ID 0x00fd
+#define RT5663_VENDOR_ID_1 0x00fe
+#define RT5663_VENDOR_ID_2 0x00ff
+
+#define RT5663_LOUT_CTRL 0x0001
+#define RT5663_HP_AMP_2 0x0003
+#define RT5663_MONO_OUT 0x0004
+#define RT5663_MONO_GAIN 0x0007
+
+#define RT5663_AEC_BST 0x000b
+#define RT5663_IN1_IN2 0x000c
+#define RT5663_IN3_IN4 0x000d
+#define RT5663_INL1_INR1 0x000f
+#define RT5663_CBJ_TYPE_2 0x0011
+#define RT5663_CBJ_TYPE_3 0x0012
+#define RT5663_CBJ_TYPE_4 0x0013
+#define RT5663_CBJ_TYPE_5 0x0014
+#define RT5663_CBJ_TYPE_8 0x0017
+
+/* I/O - ADC/DAC/DMIC */
+#define RT5663_DAC3_DIG_VOL 0x001a
+#define RT5663_DAC3_CTRL 0x001b
+#define RT5663_MONO_ADC_DIG_VOL 0x001d
+#define RT5663_STO2_ADC_DIG_VOL 0x001e
+#define RT5663_MONO_ADC_BST_GAIN 0x0020
+#define RT5663_STO2_ADC_BST_GAIN 0x0021
+#define RT5663_SIDETONE_CTRL 0x0024
+/* Mixer - D-D */
+#define RT5663_MONO1_ADC_MIXER 0x0027
+#define RT5663_STO2_ADC_MIXER 0x0028
+#define RT5663_MONO_DAC_MIXER 0x002b
+#define RT5663_DAC2_SRC_CTRL 0x002e
+#define RT5663_IF_3_4_DATA_CTL 0x002f
+#define RT5663_IF_5_DATA_CTL 0x0030
+#define RT5663_PDM_OUT_CTL 0x0031
+#define RT5663_PDM_I2C_DATA_CTL1 0x0032
+#define RT5663_PDM_I2C_DATA_CTL2 0x0033
+#define RT5663_PDM_I2C_DATA_CTL3 0x0034
+#define RT5663_PDM_I2C_DATA_CTL4 0x0035
+
+/*Mixer - Analog*/
+#define RT5663_RECMIX1_NEW 0x003a
+#define RT5663_RECMIX1L_0 0x003b
+#define RT5663_RECMIX1L 0x003c
+#define RT5663_RECMIX1R_0 0x003d
+#define RT5663_RECMIX1R 0x003e
+#define RT5663_RECMIX2_NEW 0x003f
+#define RT5663_RECMIX2_L_2 0x0041
+#define RT5663_RECMIX2_R 0x0042
+#define RT5663_RECMIX2_R_2 0x0043
+#define RT5663_CALIB_REC_LR 0x0044
+#define RT5663_ALC_BK_GAIN 0x0049
+#define RT5663_MONOMIX_GAIN 0x004a
+#define RT5663_MONOMIX_IN_GAIN 0x004b
+#define RT5663_OUT_MIXL_GAIN 0x004d
+#define RT5663_OUT_LMIX_IN_GAIN 0x004e
+#define RT5663_OUT_RMIX_IN_GAIN 0x004f
+#define RT5663_OUT_RMIX_IN_GAIN1 0x0050
+#define RT5663_LOUT_MIXER_CTRL 0x0052
+/* Power */
+#define RT5663_PWR_VOL 0x0067
+
+#define RT5663_ADCDAC_RST 0x006d
+/* Format - ADC/DAC */
+#define RT5663_I2S34_SDP 0x0071
+#define RT5663_I2S5_SDP 0x0072
+
+/* Function - Analog */
+#define RT5663_ASRC_3 0x0085
+#define RT5663_ASRC_6 0x0088
+#define RT5663_ASRC_7 0x0089
+#define RT5663_PLL_TRK_13 0x0099
+#define RT5663_I2S_M_CLK_CTL 0x00a0
+#define RT5663_FDIV_I2S34_M_CLK 0x00a1
+#define RT5663_FDIV_I2S34_M_CLK2 0x00a2
+#define RT5663_FDIV_I2S5_M_CLK 0x00a3
+#define RT5663_FDIV_I2S5_M_CLK2 0x00a4
+
+/* Function - Digital */
+#define RT5663_V2_IRQ_4 0x00b9
+#define RT5663_GPIO_3 0x00c2
+#define RT5663_GPIO_4 0x00c3
+#define RT5663_GPIO_STA2 0x00c4
+#define RT5663_HP_AMP_DET1 0x00d0
+#define RT5663_HP_AMP_DET2 0x00d1
+#define RT5663_HP_AMP_DET3 0x00d2
+#define RT5663_MID_BD_HP_AMP 0x00d3
+#define RT5663_LOW_BD_HP_AMP 0x00d4
+#define RT5663_SOF_VOL_ZC2 0x00da
+#define RT5663_ADC_STO2_ADJ1 0x00ee
+#define RT5663_ADC_STO2_ADJ2 0x00ef
+/* General Control */
+#define RT5663_A_JD_CTRL 0x00f0
+#define RT5663_JD1_TRES_CTRL 0x00f1
+#define RT5663_JD2_TRES_CTRL 0x00f2
+#define RT5663_V2_JD_CTRL2 0x00f7
+#define RT5663_DUM_REG_2 0x00fb
+#define RT5663_DUM_REG_3 0x00fc
+
+
+#define RT5663_DACADC_DIG_VOL2 0x0101
+#define RT5663_DIG_IN_PIN2 0x0133
+#define RT5663_PAD_DRV_CTL1 0x0136
+#define RT5663_SOF_RAM_DEPOP 0x0138
+#define RT5663_VOL_TEST 0x013f
+#define RT5663_MONO_DYNA_1 0x0170
+#define RT5663_MONO_DYNA_2 0x0171
+#define RT5663_MONO_DYNA_3 0x0172
+#define RT5663_MONO_DYNA_4 0x0173
+#define RT5663_MONO_DYNA_5 0x0174
+#define RT5663_MONO_DYNA_6 0x0175
+#define RT5663_STO1_SIL_DET 0x0190
+#define RT5663_MONOL_SIL_DET 0x0191
+#define RT5663_MONOR_SIL_DET 0x0192
+#define RT5663_STO2_DAC_SIL 0x0193
+#define RT5663_PWR_SAV_CTL1 0x0194
+#define RT5663_PWR_SAV_CTL2 0x0195
+#define RT5663_PWR_SAV_CTL3 0x0196
+#define RT5663_PWR_SAV_CTL4 0x0197
+#define RT5663_PWR_SAV_CTL5 0x0198
+#define RT5663_PWR_SAV_CTL6 0x0199
+#define RT5663_MONO_AMP_CAL1 0x01a0
+#define RT5663_MONO_AMP_CAL2 0x01a1
+#define RT5663_MONO_AMP_CAL3 0x01a2
+#define RT5663_MONO_AMP_CAL4 0x01a3
+#define RT5663_MONO_AMP_CAL5 0x01a4
+#define RT5663_MONO_AMP_CAL6 0x01a5
+#define RT5663_MONO_AMP_CAL7 0x01a6
+#define RT5663_MONO_AMP_CAL_ST1 0x01a7
+#define RT5663_MONO_AMP_CAL_ST2 0x01a8
+#define RT5663_MONO_AMP_CAL_ST3 0x01a9
+#define RT5663_MONO_AMP_CAL_ST4 0x01aa
+#define RT5663_MONO_AMP_CAL_ST5 0x01ab
+#define RT5663_V2_HP_IMP_SEN_13 0x01b9
+#define RT5663_V2_HP_IMP_SEN_14 0x01ba
+#define RT5663_V2_HP_IMP_SEN_6 0x01bb
+#define RT5663_V2_HP_IMP_SEN_7 0x01bc
+#define RT5663_V2_HP_IMP_SEN_8 0x01bd
+#define RT5663_V2_HP_IMP_SEN_9 0x01be
+#define RT5663_V2_HP_IMP_SEN_10 0x01bf
+#define RT5663_HP_LOGIC_3 0x01dc
+#define RT5663_HP_CALIB_ST10 0x01f3
+#define RT5663_HP_CALIB_ST11 0x01f4
+#define RT5663_PRO_REG_TBL_4 0x0203
+#define RT5663_PRO_REG_TBL_5 0x0204
+#define RT5663_PRO_REG_TBL_6 0x0205
+#define RT5663_PRO_REG_TBL_7 0x0206
+#define RT5663_PRO_REG_TBL_8 0x0207
+#define RT5663_PRO_REG_TBL_9 0x0208
+#define RT5663_SAR_ADC_INL_1 0x0210
+#define RT5663_SAR_ADC_INL_2 0x0211
+#define RT5663_SAR_ADC_INL_3 0x0212
+#define RT5663_SAR_ADC_INL_4 0x0213
+#define RT5663_SAR_ADC_INL_5 0x0214
+#define RT5663_SAR_ADC_INL_6 0x0215
+#define RT5663_SAR_ADC_INL_7 0x0216
+#define RT5663_SAR_ADC_INL_8 0x0217
+#define RT5663_SAR_ADC_INL_9 0x0218
+#define RT5663_SAR_ADC_INL_10 0x0219
+#define RT5663_SAR_ADC_INL_11 0x021a
+#define RT5663_SAR_ADC_INL_12 0x021b
+#define RT5663_DRC_CTRL_1 0x02ff
+#define RT5663_DRC1_CTRL_2 0x0301
+#define RT5663_DRC1_CTRL_3 0x0302
+#define RT5663_DRC1_CTRL_4 0x0303
+#define RT5663_DRC1_CTRL_5 0x0304
+#define RT5663_DRC1_CTRL_6 0x0305
+#define RT5663_DRC1_HD_CTRL_1 0x0306
+#define RT5663_DRC1_HD_CTRL_2 0x0307
+#define RT5663_DRC1_PRI_REG_1 0x0310
+#define RT5663_DRC1_PRI_REG_2 0x0311
+#define RT5663_DRC1_PRI_REG_3 0x0312
+#define RT5663_DRC1_PRI_REG_4 0x0313
+#define RT5663_DRC1_PRI_REG_5 0x0314
+#define RT5663_DRC1_PRI_REG_6 0x0315
+#define RT5663_DRC1_PRI_REG_7 0x0316
+#define RT5663_DRC1_PRI_REG_8 0x0317
+#define RT5663_ALC_PGA_CTL_1 0x0330
+#define RT5663_ALC_PGA_CTL_2 0x0331
+#define RT5663_ALC_PGA_CTL_3 0x0332
+#define RT5663_ALC_PGA_CTL_4 0x0333
+#define RT5663_ALC_PGA_CTL_5 0x0334
+#define RT5663_ALC_PGA_CTL_6 0x0335
+#define RT5663_ALC_PGA_CTL_7 0x0336
+#define RT5663_ALC_PGA_CTL_8 0x0337
+#define RT5663_ALC_PGA_REG_1 0x0338
+#define RT5663_ALC_PGA_REG_2 0x0339
+#define RT5663_ALC_PGA_REG_3 0x033a
+#define RT5663_ADC_EQ_RECOV_1 0x03c0
+#define RT5663_ADC_EQ_RECOV_2 0x03c1
+#define RT5663_ADC_EQ_RECOV_3 0x03c2
+#define RT5663_ADC_EQ_RECOV_4 0x03c3
+#define RT5663_ADC_EQ_RECOV_5 0x03c4
+#define RT5663_ADC_EQ_RECOV_6 0x03c5
+#define RT5663_ADC_EQ_RECOV_7 0x03c6
+#define RT5663_ADC_EQ_RECOV_8 0x03c7
+#define RT5663_ADC_EQ_RECOV_9 0x03c8
+#define RT5663_ADC_EQ_RECOV_10 0x03c9
+#define RT5663_ADC_EQ_RECOV_11 0x03ca
+#define RT5663_ADC_EQ_RECOV_12 0x03cb
+#define RT5663_ADC_EQ_RECOV_13 0x03cc
+#define RT5663_VID_HIDDEN 0x03fe
+#define RT5663_VID_CUSTOMER 0x03ff
+#define RT5663_SCAN_MODE 0x07f0
+#define RT5663_I2C_BYPA 0x07fa
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5663_EN_DAC_HPO_MASK (0x1 << 14)
+#define RT5663_EN_DAC_HPO_SHIFT 14
+#define RT5663_EN_DAC_HPO_DIS (0x0 << 14)
+#define RT5663_EN_DAC_HPO_EN (0x1 << 14)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5663_GAIN_HP (0x1f << 8)
+#define RT5663_GAIN_HP_SHIFT 8
+
+/* AEC BST Control (0x000b) */
+#define RT5663_GAIN_CBJ_MASK (0xf << 8)
+#define RT5663_GAIN_CBJ_SHIFT 8
+
+/* IN1 Control / MIC GND REF (0x000c) */
+#define RT5663_IN1_DF_MASK (0x1 << 15)
+#define RT5663_IN1_DF_SHIFT 15
+
+/* Combo Jack and Type Detection Control 1 (0x0010) */
+#define RT5663_CBJ_DET_MASK (0x1 << 15)
+#define RT5663_CBJ_DET_SHIFT 15
+#define RT5663_CBJ_DET_DIS (0x0 << 15)
+#define RT5663_CBJ_DET_EN (0x1 << 15)
+#define RT5663_DET_TYPE_MASK (0x1 << 12)
+#define RT5663_DET_TYPE_SHIFT 12
+#define RT5663_DET_TYPE_WLCSP (0x0 << 12)
+#define RT5663_DET_TYPE_QFN (0x1 << 12)
+#define RT5663_VREF_BIAS_MASK (0x1 << 6)
+#define RT5663_VREF_BIAS_SHIFT 6
+#define RT5663_VREF_BIAS_FSM (0x0 << 6)
+#define RT5663_VREF_BIAS_REG (0x1 << 6)
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5663_RECMIX1L_BST1_CBJ (0x1 << 7)
+#define RT5663_RECMIX1L_BST1_CBJ_SHIFT 7
+#define RT5663_RECMIX1L_BST2 (0x1 << 4)
+#define RT5663_RECMIX1L_BST2_SHIFT 4
+
+/* REC Right Mixer Control 2 (0x003e) */
+#define RT5663_RECMIX1R_BST2 (0x1 << 4)
+#define RT5663_RECMIX1R_BST2_SHIFT 4
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5663_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5663_DAC_L1_VOL_SHIFT 8
+#define RT5663_DAC_R1_VOL_MASK (0xff)
+#define RT5663_DAC_R1_VOL_SHIFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5663_ADC_L_MUTE_MASK (0x1 << 15)
+#define RT5663_ADC_L_MUTE_SHIFT 15
+#define RT5663_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5663_ADC_L_VOL_SHIFT 8
+#define RT5663_ADC_R_MUTE_MASK (0x1 << 7)
+#define RT5663_ADC_R_MUTE_SHIFT 7
+#define RT5663_ADC_R_VOL_MASK (0x7f)
+#define RT5663_ADC_R_VOL_SHIFT 0
+
+/* Stereo ADC Mixer Control (0x0026) */
+#define RT5663_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5663_M_STO1_ADC_L1_SHIFT 15
+#define RT5663_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5663_M_STO1_ADC_L2_SHIFT 14
+#define RT5663_STO1_ADC_L1_SRC (0x1 << 13)
+#define RT5663_STO1_ADC_L1_SRC_SHIFT 13
+#define RT5663_STO1_ADC_L2_SRC (0x1 << 12)
+#define RT5663_STO1_ADC_L2_SRC_SHIFT 12
+#define RT5663_STO1_ADC_L_SRC (0x3 << 10)
+#define RT5663_STO1_ADC_L_SRC_SHIFT 10
+#define RT5663_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5663_M_STO1_ADC_R1_SHIFT 7
+#define RT5663_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5663_M_STO1_ADC_R2_SHIFT 6
+#define RT5663_STO1_ADC_R1_SRC (0x1 << 5)
+#define RT5663_STO1_ADC_R1_SRC_SHIFT 5
+#define RT5663_STO1_ADC_R2_SRC (0x1 << 4)
+#define RT5663_STO1_ADC_R2_SRC_SHIFT 4
+#define RT5663_STO1_ADC_R_SRC (0x3 << 2)
+#define RT5663_STO1_ADC_R_SRC_SHIFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5663_M_ADCMIX_L (0x1 << 15)
+#define RT5663_M_ADCMIX_L_SHIFT 15
+#define RT5663_M_DAC1_L (0x1 << 14)
+#define RT5663_M_DAC1_L_SHIFT 14
+#define RT5663_M_ADCMIX_R (0x1 << 7)
+#define RT5663_M_ADCMIX_R_SHIFT 7
+#define RT5663_M_DAC1_R (0x1 << 6)
+#define RT5663_M_DAC1_R_SHIFT 6
+
+/* Stereo DAC Mixer Control (0x002a) */
+#define RT5663_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5663_M_DAC_L1_STO_L_SHIFT 15
+#define RT5663_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5663_M_DAC_R1_STO_L_SHIFT 13
+#define RT5663_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5663_M_DAC_L1_STO_R_SHIFT 7
+#define RT5663_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5663_M_DAC_R1_STO_R_SHIFT 5
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5663_PWR_I2S1 (0x1 << 15)
+#define RT5663_PWR_I2S1_SHIFT 15
+#define RT5663_PWR_DAC_L1 (0x1 << 11)
+#define RT5663_PWR_DAC_L1_SHIFT 11
+#define RT5663_PWR_DAC_R1 (0x1 << 10)
+#define RT5663_PWR_DAC_R1_SHIFT 10
+#define RT5663_PWR_LDO_DACREF_MASK (0x1 << 8)
+#define RT5663_PWR_LDO_DACREF_SHIFT 8
+#define RT5663_PWR_LDO_DACREF_ON (0x1 << 8)
+#define RT5663_PWR_LDO_DACREF_DOWN (0x0 << 8)
+#define RT5663_PWR_LDO_SHIFT 8
+#define RT5663_PWR_ADC_L1 (0x1 << 4)
+#define RT5663_PWR_ADC_L1_SHIFT 4
+#define RT5663_PWR_ADC_R1 (0x1 << 3)
+#define RT5663_PWR_ADC_R1_SHIFT 3
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5663_PWR_ADC_S1F (0x1 << 15)
+#define RT5663_PWR_ADC_S1F_SHIFT 15
+#define RT5663_PWR_DAC_S1F (0x1 << 10)
+#define RT5663_PWR_DAC_S1F_SHIFT 10
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5663_PWR_VREF1 (0x1 << 15)
+#define RT5663_PWR_VREF1_MASK (0x1 << 15)
+#define RT5663_PWR_VREF1_SHIFT 15
+#define RT5663_PWR_FV1 (0x1 << 14)
+#define RT5663_PWR_FV1_MASK (0x1 << 14)
+#define RT5663_PWR_FV1_SHIFT 14
+#define RT5663_PWR_VREF2 (0x1 << 13)
+#define RT5663_PWR_VREF2_MASK (0x1 << 13)
+#define RT5663_PWR_VREF2_SHIFT 13
+#define RT5663_PWR_FV2 (0x1 << 12)
+#define RT5663_PWR_FV2_MASK (0x1 << 12)
+#define RT5663_PWR_FV2_SHIFT 12
+#define RT5663_PWR_MB (0x1 << 9)
+#define RT5663_PWR_MB_MASK (0x1 << 9)
+#define RT5663_PWR_MB_SHIFT 9
+#define RT5663_AMP_HP_MASK (0x3 << 2)
+#define RT5663_AMP_HP_SHIFT 2
+#define RT5663_AMP_HP_1X (0x0 << 2)
+#define RT5663_AMP_HP_3X (0x1 << 2)
+#define RT5663_AMP_HP_5X (0x3 << 2)
+#define RT5663_LDO1_DVO_MASK (0x3)
+#define RT5663_LDO1_DVO_SHIFT 0
+#define RT5663_LDO1_DVO_0_9V (0x0)
+#define RT5663_LDO1_DVO_1_0V (0x1)
+#define RT5663_LDO1_DVO_1_2V (0x2)
+#define RT5663_LDO1_DVO_1_4V (0x3)
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5663_PWR_BST1 (0x1 << 15)
+#define RT5663_PWR_BST1_MASK (0x1 << 15)
+#define RT5663_PWR_BST1_SHIFT 15
+#define RT5663_PWR_BST1_OFF (0x0 << 15)
+#define RT5663_PWR_BST1_ON (0x1 << 15)
+#define RT5663_PWR_BST2 (0x1 << 14)
+#define RT5663_PWR_BST2_MASK (0x1 << 14)
+#define RT5663_PWR_BST2_SHIFT 14
+#define RT5663_PWR_MB1 (0x1 << 11)
+#define RT5663_PWR_MB1_SHIFT 11
+#define RT5663_PWR_MB2 (0x1 << 10)
+#define RT5663_PWR_MB2_SHIFT 10
+#define RT5663_PWR_BST2_OP (0x1 << 6)
+#define RT5663_PWR_BST2_OP_MASK (0x1 << 6)
+#define RT5663_PWR_BST2_OP_SHIFT 6
+#define RT5663_PWR_JD1 (0x1 << 3)
+#define RT5663_PWR_JD1_MASK (0x1 << 3)
+#define RT5663_PWR_JD1_SHIFT 3
+#define RT5663_PWR_JD2 (0x1 << 2)
+#define RT5663_PWR_JD2_MASK (0x1 << 2)
+#define RT5663_PWR_JD2_SHIFT 2
+#define RT5663_PWR_RECMIX1 (0x1 << 1)
+#define RT5663_PWR_RECMIX1_SHIFT 1
+#define RT5663_PWR_RECMIX2 (0x1)
+#define RT5663_PWR_RECMIX2_SHIFT 0
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5663_PWR_CBJ_MASK (0x1 << 9)
+#define RT5663_PWR_CBJ_SHIFT 9
+#define RT5663_PWR_CBJ_OFF (0x0 << 9)
+#define RT5663_PWR_CBJ_ON (0x1 << 9)
+#define RT5663_PWR_PLL (0x1 << 6)
+#define RT5663_PWR_PLL_SHIFT 6
+#define RT5663_PWR_LDO2 (0x1 << 2)
+#define RT5663_PWR_LDO2_SHIFT 2
+
+/* Power Management for Volume (0x0067) */
+#define RT5663_V2_PWR_MIC_DET (0x1 << 5)
+#define RT5663_V2_PWR_MIC_DET_SHIFT 5
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5663_EN_ANA_CLK_DET_MASK (0x1 << 15)
+#define RT5663_EN_ANA_CLK_DET_SHIFT 15
+#define RT5663_EN_ANA_CLK_DET_DIS (0x0 << 15)
+#define RT5663_EN_ANA_CLK_DET_AUTO (0x1 << 15)
+#define RT5663_PWR_CLK_DET_MASK (0x1)
+#define RT5663_PWR_CLK_DET_SHIFT 0
+#define RT5663_PWR_CLK_DET_DIS (0x0)
+#define RT5663_PWR_CLK_DET_EN (0x1)
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5663_I2S_MS_MASK (0x1 << 15)
+#define RT5663_I2S_MS_SHIFT 15
+#define RT5663_I2S_MS_M (0x0 << 15)
+#define RT5663_I2S_MS_S (0x1 << 15)
+#define RT5663_I2S_BP_MASK (0x1 << 8)
+#define RT5663_I2S_BP_SHIFT 8
+#define RT5663_I2S_BP_NOR (0x0 << 8)
+#define RT5663_I2S_BP_INV (0x1 << 8)
+#define RT5663_I2S_DL_MASK (0x3 << 4)
+#define RT5663_I2S_DL_SHIFT 4
+#define RT5663_I2S_DL_16 (0x0 << 4)
+#define RT5663_I2S_DL_20 (0x1 << 4)
+#define RT5663_I2S_DL_24 (0x2 << 4)
+#define RT5663_I2S_DL_8 (0x3 << 4)
+#define RT5663_I2S_DF_MASK (0x7)
+#define RT5663_I2S_DF_SHIFT 0
+#define RT5663_I2S_DF_I2S (0x0)
+#define RT5663_I2S_DF_LEFT (0x1)
+#define RT5663_I2S_DF_PCM_A (0x2)
+#define RT5663_I2S_DF_PCM_B (0x3)
+#define RT5663_I2S_DF_PCM_A_N (0x6)
+#define RT5663_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5663_I2S_PD1_MASK (0x7 << 12)
+#define RT5663_I2S_PD1_SHIFT 12
+#define RT5663_M_I2S_DIV_MASK (0x7 << 8)
+#define RT5663_M_I2S_DIV_SHIFT 8
+#define RT5663_CLK_SRC_MASK (0x3 << 4)
+#define RT5663_CLK_SRC_MCLK (0x0 << 4)
+#define RT5663_CLK_SRC_PLL_OUT (0x1 << 4)
+#define RT5663_CLK_SRC_DIV (0x2 << 4)
+#define RT5663_CLK_SRC_RC (0x3 << 4)
+#define RT5663_DAC_OSR_MASK (0x3 << 2)
+#define RT5663_DAC_OSR_SHIFT 2
+#define RT5663_DAC_OSR_128 (0x0 << 2)
+#define RT5663_DAC_OSR_64 (0x1 << 2)
+#define RT5663_DAC_OSR_32 (0x2 << 2)
+#define RT5663_ADC_OSR_MASK (0x3)
+#define RT5663_ADC_OSR_SHIFT 0
+#define RT5663_ADC_OSR_128 (0x0)
+#define RT5663_ADC_OSR_64 (0x1)
+#define RT5663_ADC_OSR_32 (0x2)
+
+/* TDM1 control 1 (0x0078) */
+#define RT5663_TDM_MODE_MASK (0x1 << 15)
+#define RT5663_TDM_MODE_SHIFT 15
+#define RT5663_TDM_MODE_I2S (0x0 << 15)
+#define RT5663_TDM_MODE_TDM (0x1 << 15)
+#define RT5663_TDM_IN_CH_MASK (0x3 << 10)
+#define RT5663_TDM_IN_CH_SHIFT 10
+#define RT5663_TDM_IN_CH_2 (0x0 << 10)
+#define RT5663_TDM_IN_CH_4 (0x1 << 10)
+#define RT5663_TDM_IN_CH_6 (0x2 << 10)
+#define RT5663_TDM_IN_CH_8 (0x3 << 10)
+#define RT5663_TDM_OUT_CH_MASK (0x3 << 8)
+#define RT5663_TDM_OUT_CH_SHIFT 8
+#define RT5663_TDM_OUT_CH_2 (0x0 << 8)
+#define RT5663_TDM_OUT_CH_4 (0x1 << 8)
+#define RT5663_TDM_OUT_CH_6 (0x2 << 8)
+#define RT5663_TDM_OUT_CH_8 (0x3 << 8)
+#define RT5663_TDM_IN_LEN_MASK (0x3 << 6)
+#define RT5663_TDM_IN_LEN_SHIFT 6
+#define RT5663_TDM_IN_LEN_16 (0x0 << 6)
+#define RT5663_TDM_IN_LEN_20 (0x1 << 6)
+#define RT5663_TDM_IN_LEN_24 (0x2 << 6)
+#define RT5663_TDM_IN_LEN_32 (0x3 << 6)
+#define RT5663_TDM_OUT_LEN_MASK (0x3 << 4)
+#define RT5663_TDM_OUT_LEN_SHIFT 4
+#define RT5663_TDM_OUT_LEN_16 (0x0 << 4)
+#define RT5663_TDM_OUT_LEN_20 (0x1 << 4)
+#define RT5663_TDM_OUT_LEN_24 (0x2 << 4)
+#define RT5663_TDM_OUT_LEN_32 (0x3 << 4)
+
+/* Global Clock Control (0x0080) */
+#define RT5663_SCLK_SRC_MASK (0x3 << 14)
+#define RT5663_SCLK_SRC_SHIFT 14
+#define RT5663_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5663_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5663_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5663_PLL1_SRC_MASK (0x7 << 11)
+#define RT5663_PLL1_SRC_SHIFT 11
+#define RT5663_PLL1_SRC_MCLK (0x0 << 11)
+#define RT5663_PLL1_SRC_BCLK1 (0x1 << 11)
+#define RT5663_V2_PLL1_SRC_MASK (0x7 << 8)
+#define RT5663_V2_PLL1_SRC_SHIFT 8
+#define RT5663_V2_PLL1_SRC_MCLK (0x0 << 8)
+#define RT5663_V2_PLL1_SRC_BCLK1 (0x1 << 8)
+#define RT5663_PLL1_PD_MASK (0x1 << 4)
+#define RT5663_PLL1_PD_SHIFT 4
+
+#define RT5663_PLL_INP_MAX 40000000
+#define RT5663_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x0081) */
+#define RT5663_PLL_N_MAX 0x001ff
+#define RT5663_PLL_N_MASK (RT5663_PLL_N_MAX << 7)
+#define RT5663_PLL_N_SHIFT 7
+#define RT5663_PLL_K_MAX 0x001f
+#define RT5663_PLL_K_MASK (RT5663_PLL_K_MAX)
+#define RT5663_PLL_K_SHIFT 0
+
+/* PLL M/N/K Code Control 2 (0x0082) */
+#define RT5663_PLL_M_MAX 0x00f
+#define RT5663_PLL_M_MASK (RT5663_PLL_M_MAX << 12)
+#define RT5663_PLL_M_SHIFT 12
+#define RT5663_PLL_M_BP (0x1 << 11)
+#define RT5663_PLL_M_BP_SHIFT 11
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5663_V2_I2S1_ASRC_MASK (0x1 << 13)
+#define RT5663_V2_I2S1_ASRC_SHIFT 13
+#define RT5663_V2_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5663_V2_DAC_STO1_ASRC_SHIFT 12
+#define RT5663_V2_ADC_STO1_ASRC_MASK (0x1 << 4)
+#define RT5663_V2_ADC_STO1_ASRC_SHIFT 4
+
+/* PLL tracking mode 2 (0x0084)*/
+#define RT5663_DA_STO1_TRACK_MASK (0x7 << 12)
+#define RT5663_DA_STO1_TRACK_SHIFT 12
+#define RT5663_DA_STO1_TRACK_SYSCLK (0x0 << 12)
+#define RT5663_DA_STO1_TRACK_I2S1 (0x1 << 12)
+
+/* PLL tracking mode 3 (0x0085)*/
+#define RT5663_V2_AD_STO1_TRACK_MASK (0x7 << 12)
+#define RT5663_V2_AD_STO1_TRACK_SHIFT 12
+#define RT5663_V2_AD_STO1_TRACK_SYSCLK (0x0 << 12)
+#define RT5663_V2_AD_STO1_TRACK_I2S1 (0x1 << 12)
+
+/* HPOUT Charge pump control 1 (0x0091) */
+#define RT5663_OSW_HP_L_MASK (0x1 << 11)
+#define RT5663_OSW_HP_L_SHIFT 11
+#define RT5663_OSW_HP_L_EN (0x1 << 11)
+#define RT5663_OSW_HP_L_DIS (0x0 << 11)
+#define RT5663_OSW_HP_R_MASK (0x1 << 10)
+#define RT5663_OSW_HP_R_SHIFT 10
+#define RT5663_OSW_HP_R_EN (0x1 << 10)
+#define RT5663_OSW_HP_R_DIS (0x0 << 10)
+#define RT5663_SEL_PM_HP_MASK (0x3 << 8)
+#define RT5663_SEL_PM_HP_SHIFT 8
+#define RT5663_SEL_PM_HP_0_6 (0x0 << 8)
+#define RT5663_SEL_PM_HP_0_9 (0x1 << 8)
+#define RT5663_SEL_PM_HP_1_8 (0x2 << 8)
+#define RT5663_SEL_PM_HP_HIGH (0x3 << 8)
+#define RT5663_OVCD_HP_MASK (0x1 << 2)
+#define RT5663_OVCD_HP_SHIFT 2
+#define RT5663_OVCD_HP_EN (0x1 << 2)
+#define RT5663_OVCD_HP_DIS (0x0 << 2)
+
+/* RC Clock Control (0x0094) */
+#define RT5663_DIG_25M_CLK_MASK (0x1 << 9)
+#define RT5663_DIG_25M_CLK_SHIFT 9
+#define RT5663_DIG_25M_CLK_DIS (0x0 << 9)
+#define RT5663_DIG_25M_CLK_EN (0x1 << 9)
+#define RT5663_DIG_1M_CLK_MASK (0x1 << 8)
+#define RT5663_DIG_1M_CLK_SHIFT 8
+#define RT5663_DIG_1M_CLK_DIS (0x0 << 8)
+#define RT5663_DIG_1M_CLK_EN (0x1 << 8)
+
+/* Auto Turn On 1M RC CLK (0x009f) */
+#define RT5663_IRQ_POW_SAV_MASK (0x1 << 15)
+#define RT5663_IRQ_POW_SAV_SHIFT 15
+#define RT5663_IRQ_POW_SAV_DIS (0x0 << 15)
+#define RT5663_IRQ_POW_SAV_EN (0x1 << 15)
+#define RT5663_IRQ_POW_SAV_JD1_MASK (0x1 << 14)
+#define RT5663_IRQ_POW_SAV_JD1_SHIFT 14
+#define RT5663_IRQ_POW_SAV_JD1_DIS (0x0 << 14)
+#define RT5663_IRQ_POW_SAV_JD1_EN (0x1 << 14)
+#define RT5663_IRQ_MANUAL_MASK (0x1 << 8)
+#define RT5663_IRQ_MANUAL_SHIFT 8
+#define RT5663_IRQ_MANUAL_DIS (0x0 << 8)
+#define RT5663_IRQ_MANUAL_EN (0x1 << 8)
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5663_EN_CB_JD_MASK (0x1 << 3)
+#define RT5663_EN_CB_JD_SHIFT 3
+#define RT5663_EN_CB_JD_EN (0x1 << 3)
+#define RT5663_EN_CB_JD_DIS (0x0 << 3)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5663_V2_EN_IRQ_INLINE_MASK (0x1 << 6)
+#define RT5663_V2_EN_IRQ_INLINE_SHIFT 6
+#define RT5663_V2_EN_IRQ_INLINE_BYP (0x0 << 6)
+#define RT5663_V2_EN_IRQ_INLINE_NOR (0x1 << 6)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5663_GP1_PIN_MASK (0x1 << 15)
+#define RT5663_GP1_PIN_SHIFT 15
+#define RT5663_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5663_GP1_PIN_IRQ (0x1 << 15)
+
+/* GPIO Control 2 (0x00c1) */
+#define RT5663_GP4_PIN_CONF_MASK (0x1 << 5)
+#define RT5663_GP4_PIN_CONF_SHIFT 5
+#define RT5663_GP4_PIN_CONF_INPUT (0x0 << 5)
+#define RT5663_GP4_PIN_CONF_OUTPUT (0x1 << 5)
+
+/* GPIO Control 2 (0x00c2) */
+#define RT5663_GP8_PIN_CONF_MASK (0x1 << 13)
+#define RT5663_GP8_PIN_CONF_SHIFT 13
+#define RT5663_GP8_PIN_CONF_INPUT (0x0 << 13)
+#define RT5663_GP8_PIN_CONF_OUTPUT (0x1 << 13)
+
+/* 4 Buttons Inline Command Function 1 (0x00df) */
+#define RT5663_4BTN_CLK_DEB_MASK (0x3 << 2)
+#define RT5663_4BTN_CLK_DEB_SHIFT 2
+#define RT5663_4BTN_CLK_DEB_8MS (0x0 << 2)
+#define RT5663_4BTN_CLK_DEB_16MS (0x1 << 2)
+#define RT5663_4BTN_CLK_DEB_32MS (0x2 << 2)
+#define RT5663_4BTN_CLK_DEB_65MS (0x3 << 2)
+
+/* Inline Command Function 6 (0x00e0) */
+#define RT5663_EN_4BTN_INL_MASK (0x1 << 15)
+#define RT5663_EN_4BTN_INL_SHIFT 15
+#define RT5663_EN_4BTN_INL_DIS (0x0 << 15)
+#define RT5663_EN_4BTN_INL_EN (0x1 << 15)
+#define RT5663_RESET_4BTN_INL_MASK (0x1 << 14)
+#define RT5663_RESET_4BTN_INL_SHIFT 14
+#define RT5663_RESET_4BTN_INL_RESET (0x0 << 14)
+#define RT5663_RESET_4BTN_INL_NOR (0x1 << 14)
+
+/* Digital Misc Control (0x00fa) */
+#define RT5663_DIG_GATE_CTRL_MASK 0x1
+#define RT5663_DIG_GATE_CTRL_SHIFT (0)
+#define RT5663_DIG_GATE_CTRL_DIS 0x0
+#define RT5663_DIG_GATE_CTRL_EN 0x1
+
+/* Chopper and Clock control for DAC L (0x013a)*/
+#define RT5663_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5663_CKXEN_DAC1_SHIFT 13
+#define RT5663_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5663_CKGEN_DAC1_SHIFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5663_CKXEN_ADCC_MASK (0x1 << 13)
+#define RT5663_CKXEN_ADCC_SHIFT 13
+#define RT5663_CKGEN_ADCC_MASK (0x1 << 12)
+#define RT5663_CKGEN_ADCC_SHIFT 12
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5663_HP_SIG_SRC1_MASK (0x3)
+#define RT5663_HP_SIG_SRC1_SHIFT 0
+#define RT5663_HP_SIG_SRC1_HP_DC (0x0)
+#define RT5663_HP_SIG_SRC1_HP_CALIB (0x1)
+#define RT5663_HP_SIG_SRC1_REG (0x2)
+#define RT5663_HP_SIG_SRC1_SILENCE (0x3)
+
+/* RT5663 specific register */
+#define RT5663_HP_OUT_EN 0x0002
+#define RT5663_HP_LCH_DRE 0x0005
+#define RT5663_HP_RCH_DRE 0x0006
+#define RT5663_CALIB_BST 0x000a
+#define RT5663_RECMIX 0x0010
+#define RT5663_SIL_DET_CTL 0x0015
+#define RT5663_PWR_SAV_SILDET 0x0016
+#define RT5663_SIDETONE_CTL 0x0018
+#define RT5663_STO1_DAC_DIG_VOL 0x0019
+#define RT5663_STO1_ADC_DIG_VOL 0x001c
+#define RT5663_STO1_BOOST 0x001f
+#define RT5663_HP_IMP_GAIN_1 0x0022
+#define RT5663_HP_IMP_GAIN_2 0x0023
+#define RT5663_STO1_ADC_MIXER 0x0026
+#define RT5663_AD_DA_MIXER 0x0029
+#define RT5663_STO_DAC_MIXER 0x002a
+#define RT5663_DIG_SIDE_MIXER 0x002c
+#define RT5663_BYPASS_STO_DAC 0x002d
+#define RT5663_CALIB_REC_MIX 0x0040
+#define RT5663_PWR_DIG_1 0x0061
+#define RT5663_PWR_DIG_2 0x0062
+#define RT5663_PWR_ANLG_1 0x0063
+#define RT5663_PWR_ANLG_2 0x0064
+#define RT5663_PWR_ANLG_3 0x0065
+#define RT5663_PWR_MIXER 0x0066
+#define RT5663_SIG_CLK_DET 0x006b
+#define RT5663_PRE_DIV_GATING_1 0x006e
+#define RT5663_PRE_DIV_GATING_2 0x006f
+#define RT5663_I2S1_SDP 0x0070
+#define RT5663_ADDA_CLK_1 0x0073
+#define RT5663_ADDA_RST 0x0074
+#define RT5663_FRAC_DIV_1 0x0075
+#define RT5663_FRAC_DIV_2 0x0076
+#define RT5663_TDM_1 0x0077
+#define RT5663_TDM_2 0x0078
+#define RT5663_TDM_3 0x0079
+#define RT5663_TDM_4 0x007a
+#define RT5663_TDM_5 0x007b
+#define RT5663_TDM_6 0x007c
+#define RT5663_TDM_7 0x007d
+#define RT5663_TDM_8 0x007e
+#define RT5663_TDM_9 0x007f
+#define RT5663_GLB_CLK 0x0080
+#define RT5663_PLL_1 0x0081
+#define RT5663_PLL_2 0x0082
+#define RT5663_ASRC_1 0x0083
+#define RT5663_ASRC_2 0x0084
+#define RT5663_ASRC_4 0x0086
+#define RT5663_DUMMY_REG 0x0087
+#define RT5663_ASRC_8 0x008a
+#define RT5663_ASRC_9 0x008b
+#define RT5663_ASRC_11 0x008c
+#define RT5663_DEPOP_1 0x008e
+#define RT5663_DEPOP_2 0x008f
+#define RT5663_DEPOP_3 0x0090
+#define RT5663_HP_CHARGE_PUMP_1 0x0091
+#define RT5663_HP_CHARGE_PUMP_2 0x0092
+#define RT5663_MICBIAS_1 0x0093
+#define RT5663_RC_CLK 0x0094
+#define RT5663_ASRC_11_2 0x0097
+#define RT5663_DUMMY_REG_2 0x0098
+#define RT5663_REC_PATH_GAIN 0x009a
+#define RT5663_AUTO_1MRC_CLK 0x009f
+#define RT5663_ADC_EQ_1 0x00ae
+#define RT5663_ADC_EQ_2 0x00af
+#define RT5663_IRQ_1 0x00b6
+#define RT5663_IRQ_2 0x00b7
+#define RT5663_IRQ_3 0x00b8
+#define RT5663_IRQ_4 0x00ba
+#define RT5663_IRQ_5 0x00bb
+#define RT5663_INT_ST_1 0x00be
+#define RT5663_INT_ST_2 0x00bf
+#define RT5663_GPIO_1 0x00c0
+#define RT5663_GPIO_2 0x00c1
+#define RT5663_GPIO_STA1 0x00c5
+#define RT5663_SIN_GEN_1 0x00cb
+#define RT5663_SIN_GEN_2 0x00cc
+#define RT5663_SIN_GEN_3 0x00cd
+#define RT5663_SOF_VOL_ZC1 0x00d9
+#define RT5663_IL_CMD_1 0x00db
+#define RT5663_IL_CMD_2 0x00dc
+#define RT5663_IL_CMD_3 0x00dd
+#define RT5663_IL_CMD_4 0x00de
+#define RT5663_IL_CMD_5 0x00df
+#define RT5663_IL_CMD_6 0x00e0
+#define RT5663_IL_CMD_7 0x00e1
+#define RT5663_IL_CMD_8 0x00e2
+#define RT5663_IL_CMD_PWRSAV1 0x00e4
+#define RT5663_IL_CMD_PWRSAV2 0x00e5
+#define RT5663_EM_JACK_TYPE_1 0x00e6
+#define RT5663_EM_JACK_TYPE_2 0x00e7
+#define RT5663_EM_JACK_TYPE_3 0x00e8
+#define RT5663_EM_JACK_TYPE_4 0x00e9
+#define RT5663_EM_JACK_TYPE_5 0x00ea
+#define RT5663_EM_JACK_TYPE_6 0x00eb
+#define RT5663_STO1_HPF_ADJ1 0x00ec
+#define RT5663_STO1_HPF_ADJ2 0x00ed
+#define RT5663_FAST_OFF_MICBIAS 0x00f4
+#define RT5663_JD_CTRL1 0x00f6
+#define RT5663_JD_CTRL2 0x00f8
+#define RT5663_DIG_MISC 0x00fa
+#define RT5663_DIG_VOL_ZCD 0x0100
+#define RT5663_ANA_BIAS_CUR_1 0x0108
+#define RT5663_ANA_BIAS_CUR_2 0x0109
+#define RT5663_ANA_BIAS_CUR_3 0x010a
+#define RT5663_ANA_BIAS_CUR_4 0x010b
+#define RT5663_ANA_BIAS_CUR_5 0x010c
+#define RT5663_ANA_BIAS_CUR_6 0x010d
+#define RT5663_BIAS_CUR_5 0x010e
+#define RT5663_BIAS_CUR_6 0x010f
+#define RT5663_BIAS_CUR_7 0x0110
+#define RT5663_BIAS_CUR_8 0x0111
+#define RT5663_DACREF_LDO 0x0112
+#define RT5663_DUMMY_REG_3 0x0113
+#define RT5663_BIAS_CUR_9 0x0114
+#define RT5663_DUMMY_REG_4 0x0116
+#define RT5663_VREFADJ_OP 0x0117
+#define RT5663_VREF_RECMIX 0x0118
+#define RT5663_CHARGE_PUMP_1 0x0125
+#define RT5663_CHARGE_PUMP_1_2 0x0126
+#define RT5663_CHARGE_PUMP_1_3 0x0127
+#define RT5663_CHARGE_PUMP_2 0x0128
+#define RT5663_DIG_IN_PIN1 0x0132
+#define RT5663_PAD_DRV_CTL 0x0137
+#define RT5663_PLL_INT_REG 0x0139
+#define RT5663_CHOP_DAC_L 0x013a
+#define RT5663_CHOP_ADC 0x013b
+#define RT5663_CALIB_ADC 0x013c
+#define RT5663_CHOP_DAC_R 0x013d
+#define RT5663_DUMMY_CTL_DACLR 0x013e
+#define RT5663_DUMMY_REG_5 0x0140
+#define RT5663_SOFT_RAMP 0x0141
+#define RT5663_TEST_MODE_1 0x0144
+#define RT5663_TEST_MODE_2 0x0145
+#define RT5663_TEST_MODE_3 0x0146
+#define RT5663_TEST_MODE_4 0x0147
+#define RT5663_TEST_MODE_5 0x0148
+#define RT5663_STO_DRE_1 0x0160
+#define RT5663_STO_DRE_2 0x0161
+#define RT5663_STO_DRE_3 0x0162
+#define RT5663_STO_DRE_4 0x0163
+#define RT5663_STO_DRE_5 0x0164
+#define RT5663_STO_DRE_6 0x0165
+#define RT5663_STO_DRE_7 0x0166
+#define RT5663_STO_DRE_8 0x0167
+#define RT5663_STO_DRE_9 0x0168
+#define RT5663_STO_DRE_10 0x0169
+#define RT5663_MIC_DECRO_1 0x0180
+#define RT5663_MIC_DECRO_2 0x0181
+#define RT5663_MIC_DECRO_3 0x0182
+#define RT5663_MIC_DECRO_4 0x0183
+#define RT5663_MIC_DECRO_5 0x0184
+#define RT5663_MIC_DECRO_6 0x0185
+#define RT5663_HP_DECRO_1 0x01b0
+#define RT5663_HP_DECRO_2 0x01b1
+#define RT5663_HP_DECRO_3 0x01b2
+#define RT5663_HP_DECRO_4 0x01b3
+#define RT5663_HP_DECOUP 0x01b4
+#define RT5663_HP_IMP_SEN_MAP8 0x01b5
+#define RT5663_HP_IMP_SEN_MAP9 0x01b6
+#define RT5663_HP_IMP_SEN_MAP10 0x01b7
+#define RT5663_HP_IMP_SEN_MAP11 0x01b8
+#define RT5663_HP_IMP_SEN_1 0x01c0
+#define RT5663_HP_IMP_SEN_2 0x01c1
+#define RT5663_HP_IMP_SEN_3 0x01c2
+#define RT5663_HP_IMP_SEN_4 0x01c3
+#define RT5663_HP_IMP_SEN_5 0x01c4
+#define RT5663_HP_IMP_SEN_6 0x01c5
+#define RT5663_HP_IMP_SEN_7 0x01c6
+#define RT5663_HP_IMP_SEN_8 0x01c7
+#define RT5663_HP_IMP_SEN_9 0x01c8
+#define RT5663_HP_IMP_SEN_10 0x01c9
+#define RT5663_HP_IMP_SEN_11 0x01ca
+#define RT5663_HP_IMP_SEN_12 0x01cb
+#define RT5663_HP_IMP_SEN_13 0x01cc
+#define RT5663_HP_IMP_SEN_14 0x01cd
+#define RT5663_HP_IMP_SEN_15 0x01ce
+#define RT5663_HP_IMP_SEN_16 0x01cf
+#define RT5663_HP_IMP_SEN_17 0x01d0
+#define RT5663_HP_IMP_SEN_18 0x01d1
+#define RT5663_HP_IMP_SEN_19 0x01d2
+#define RT5663_HP_IMPSEN_DIG5 0x01d3
+#define RT5663_HP_IMPSEN_MAP1 0x01d4
+#define RT5663_HP_IMPSEN_MAP2 0x01d5
+#define RT5663_HP_IMPSEN_MAP3 0x01d6
+#define RT5663_HP_IMPSEN_MAP4 0x01d7
+#define RT5663_HP_IMPSEN_MAP5 0x01d8
+#define RT5663_HP_IMPSEN_MAP7 0x01d9
+#define RT5663_HP_LOGIC_1 0x01da
+#define RT5663_HP_LOGIC_2 0x01db
+#define RT5663_HP_CALIB_1 0x01dd
+#define RT5663_HP_CALIB_1_1 0x01de
+#define RT5663_HP_CALIB_2 0x01df
+#define RT5663_HP_CALIB_3 0x01e0
+#define RT5663_HP_CALIB_4 0x01e1
+#define RT5663_HP_CALIB_5 0x01e2
+#define RT5663_HP_CALIB_5_1 0x01e3
+#define RT5663_HP_CALIB_6 0x01e4
+#define RT5663_HP_CALIB_7 0x01e5
+#define RT5663_HP_CALIB_9 0x01e6
+#define RT5663_HP_CALIB_10 0x01e7
+#define RT5663_HP_CALIB_11 0x01e8
+#define RT5663_HP_CALIB_ST1 0x01ea
+#define RT5663_HP_CALIB_ST2 0x01eb
+#define RT5663_HP_CALIB_ST3 0x01ec
+#define RT5663_HP_CALIB_ST4 0x01ed
+#define RT5663_HP_CALIB_ST5 0x01ee
+#define RT5663_HP_CALIB_ST6 0x01ef
+#define RT5663_HP_CALIB_ST7 0x01f0
+#define RT5663_HP_CALIB_ST8 0x01f1
+#define RT5663_HP_CALIB_ST9 0x01f2
+#define RT5663_HP_AMP_DET 0x0200
+#define RT5663_DUMMY_REG_6 0x0201
+#define RT5663_HP_BIAS 0x0202
+#define RT5663_CBJ_1 0x0250
+#define RT5663_CBJ_2 0x0251
+#define RT5663_CBJ_3 0x0252
+#define RT5663_DUMMY_1 0x02fa
+#define RT5663_DUMMY_2 0x02fb
+#define RT5663_DUMMY_3 0x02fc
+#define RT5663_ANA_JD 0x0300
+#define RT5663_ADC_LCH_LPF1_A1 0x03d0
+#define RT5663_ADC_RCH_LPF1_A1 0x03d1
+#define RT5663_ADC_LCH_LPF1_H0 0x03d2
+#define RT5663_ADC_RCH_LPF1_H0 0x03d3
+#define RT5663_ADC_LCH_BPF1_A1 0x03d4
+#define RT5663_ADC_RCH_BPF1_A1 0x03d5
+#define RT5663_ADC_LCH_BPF1_A2 0x03d6
+#define RT5663_ADC_RCH_BPF1_A2 0x03d7
+#define RT5663_ADC_LCH_BPF1_H0 0x03d8
+#define RT5663_ADC_RCH_BPF1_H0 0x03d9
+#define RT5663_ADC_LCH_BPF2_A1 0x03da
+#define RT5663_ADC_RCH_BPF2_A1 0x03db
+#define RT5663_ADC_LCH_BPF2_A2 0x03dc
+#define RT5663_ADC_RCH_BPF2_A2 0x03dd
+#define RT5663_ADC_LCH_BPF2_H0 0x03de
+#define RT5663_ADC_RCH_BPF2_H0 0x03df
+#define RT5663_ADC_LCH_BPF3_A1 0x03e0
+#define RT5663_ADC_RCH_BPF3_A1 0x03e1
+#define RT5663_ADC_LCH_BPF3_A2 0x03e2
+#define RT5663_ADC_RCH_BPF3_A2 0x03e3
+#define RT5663_ADC_LCH_BPF3_H0 0x03e4
+#define RT5663_ADC_RCH_BPF3_H0 0x03e5
+#define RT5663_ADC_LCH_BPF4_A1 0x03e6
+#define RT5663_ADC_RCH_BPF4_A1 0x03e7
+#define RT5663_ADC_LCH_BPF4_A2 0x03e8
+#define RT5663_ADC_RCH_BPF4_A2 0x03e9
+#define RT5663_ADC_LCH_BPF4_H0 0x03ea
+#define RT5663_ADC_RCH_BPF4_H0 0x03eb
+#define RT5663_ADC_LCH_HPF1_A1 0x03ec
+#define RT5663_ADC_RCH_HPF1_A1 0x03ed
+#define RT5663_ADC_LCH_HPF1_H0 0x03ee
+#define RT5663_ADC_RCH_HPF1_H0 0x03ef
+#define RT5663_ADC_EQ_PRE_VOL_L 0x03f0
+#define RT5663_ADC_EQ_PRE_VOL_R 0x03f1
+#define RT5663_ADC_EQ_POST_VOL_L 0x03f2
+#define RT5663_ADC_EQ_POST_VOL_R 0x03f3
+
+/* RECMIX Control (0x0010) */
+#define RT5663_RECMIX1_BST1_MASK (0x1)
+#define RT5663_RECMIX1_BST1_SHIFT 0
+#define RT5663_RECMIX1_BST1_ON (0x0)
+#define RT5663_RECMIX1_BST1_OFF (0x1)
+
+/* Bypass Stereo1 DAC Mixer Control (0x002d) */
+#define RT5663_DACL1_SRC_MASK (0x1 << 3)
+#define RT5663_DACL1_SRC_SHIFT 3
+#define RT5663_DACR1_SRC_MASK (0x1 << 2)
+#define RT5663_DACR1_SRC_SHIFT 2
+
+/* TDM control 2 (0x0078) */
+#define RT5663_DATA_SWAP_ADCDAT1_MASK (0x3 << 14)
+#define RT5663_DATA_SWAP_ADCDAT1_SHIFT 14
+#define RT5663_DATA_SWAP_ADCDAT1_LR (0x0 << 14)
+#define RT5663_DATA_SWAP_ADCDAT1_RL (0x1 << 14)
+#define RT5663_DATA_SWAP_ADCDAT1_LL (0x2 << 14)
+#define RT5663_DATA_SWAP_ADCDAT1_RR (0x3 << 14)
+
+/* TDM control 5 (0x007b) */
+#define RT5663_TDM_LENGTN_MASK (0x3)
+#define RT5663_TDM_LENGTN_SHIFT 0
+#define RT5663_TDM_LENGTN_16 (0x0)
+#define RT5663_TDM_LENGTN_20 (0x1)
+#define RT5663_TDM_LENGTN_24 (0x2)
+#define RT5663_TDM_LENGTN_32 (0x3)
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5663_I2S1_ASRC_MASK (0x1 << 11)
+#define RT5663_I2S1_ASRC_SHIFT 11
+#define RT5663_DAC_STO1_ASRC_MASK (0x1 << 10)
+#define RT5663_DAC_STO1_ASRC_SHIFT 10
+#define RT5663_ADC_STO1_ASRC_MASK (0x1 << 3)
+#define RT5663_ADC_STO1_ASRC_SHIFT 3
+
+/* PLL tracking mode 2 (0x0084)*/
+#define RT5663_DA_STO1_TRACK_MASK (0x7 << 12)
+#define RT5663_DA_STO1_TRACK_SHIFT 12
+#define RT5663_DA_STO1_TRACK_SYSCLK (0x0 << 12)
+#define RT5663_DA_STO1_TRACK_I2S1 (0x1 << 12)
+#define RT5663_AD_STO1_TRACK_MASK (0x7)
+#define RT5663_AD_STO1_TRACK_SHIFT 0
+#define RT5663_AD_STO1_TRACK_SYSCLK (0x0)
+#define RT5663_AD_STO1_TRACK_I2S1 (0x1)
+
+/* HPOUT Charge pump control 1 (0x0091) */
+#define RT5663_SI_HP_MASK (0x1 << 12)
+#define RT5663_SI_HP_SHIFT 12
+#define RT5663_SI_HP_EN (0x1 << 12)
+#define RT5663_SI_HP_DIS (0x0 << 12)
+
+/* GPIO Control 2 (0x00b6) */
+#define RT5663_GP1_PIN_CONF_MASK (0x1 << 2)
+#define RT5663_GP1_PIN_CONF_SHIFT 2
+#define RT5663_GP1_PIN_CONF_OUTPUT (0x1 << 2)
+#define RT5663_GP1_PIN_CONF_INPUT (0x0 << 2)
+
+/* GPIO Control 2 (0x00b7) */
+#define RT5663_EN_IRQ_INLINE_MASK (0x1 << 3)
+#define RT5663_EN_IRQ_INLINE_SHIFT 3
+#define RT5663_EN_IRQ_INLINE_NOR (0x1 << 3)
+#define RT5663_EN_IRQ_INLINE_BYP (0x0 << 3)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5663_GPIO1_TYPE_MASK (0x1 << 15)
+#define RT5663_GPIO1_TYPE_SHIFT 15
+#define RT5663_GPIO1_TYPE_EN (0x1 << 15)
+#define RT5663_GPIO1_TYPE_DIS (0x0 << 15)
+
+/* IRQ Control 1 (0x00c1) */
+#define RT5663_EN_IRQ_JD1_MASK (0x1 << 6)
+#define RT5663_EN_IRQ_JD1_SHIFT 6
+#define RT5663_EN_IRQ_JD1_EN (0x1 << 6)
+#define RT5663_EN_IRQ_JD1_DIS (0x0 << 6)
+#define RT5663_SEL_GPIO1_MASK (0x1 << 2)
+#define RT5663_SEL_GPIO1_SHIFT 6
+#define RT5663_SEL_GPIO1_EN (0x1 << 2)
+#define RT5663_SEL_GPIO1_DIS (0x0 << 2)
+
+/* Inline Command Function 2 (0x00dc) */
+#define RT5663_PWR_MIC_DET_MASK (0x1)
+#define RT5663_PWR_MIC_DET_SHIFT 0
+#define RT5663_PWR_MIC_DET_ON (0x1)
+#define RT5663_PWR_MIC_DET_OFF (0x0)
+
+/* Embeeded Jack and Type Detection Control 1 (0x00e6)*/
+#define RT5663_CBJ_DET_MASK (0x1 << 15)
+#define RT5663_CBJ_DET_SHIFT 15
+#define RT5663_CBJ_DET_DIS (0x0 << 15)
+#define RT5663_CBJ_DET_EN (0x1 << 15)
+#define RT5663_EXT_JD_MASK (0x1 << 11)
+#define RT5663_EXT_JD_SHIFT 11
+#define RT5663_EXT_JD_EN (0x1 << 11)
+#define RT5663_EXT_JD_DIS (0x0 << 11)
+#define RT5663_POL_EXT_JD_MASK (0x1 << 10)
+#define RT5663_POL_EXT_JD_SHIFT 10
+#define RT5663_POL_EXT_JD_EN (0x1 << 10)
+#define RT5663_POL_EXT_JD_DIS (0x0 << 10)
+#define RT5663_EM_JD_MASK (0x1 << 7)
+#define RT5663_EM_JD_SHIFT 7
+#define RT5663_EM_JD_NOR (0x1 << 7)
+#define RT5663_EM_JD_RST (0x0 << 7)
+
+/* DACREF LDO Control (0x0112)*/
+#define RT5663_PWR_LDO_DACREFL_MASK (0x1 << 9)
+#define RT5663_PWR_LDO_DACREFL_SHIFT 9
+#define RT5663_PWR_LDO_DACREFR_MASK (0x1 << 1)
+#define RT5663_PWR_LDO_DACREFR_SHIFT 1
+
+/* Stereo Dynamic Range Enhancement Control 9 (0x0168, 0x0169)*/
+#define RT5663_DRE_GAIN_HP_MASK (0x1f)
+#define RT5663_DRE_GAIN_HP_SHIFT 0
+
+/* Combo Jack Control (0x0250) */
+#define RT5663_INBUF_CBJ_BST1_MASK (0x1 << 11)
+#define RT5663_INBUF_CBJ_BST1_SHIFT 11
+#define RT5663_INBUF_CBJ_BST1_ON (0x1 << 11)
+#define RT5663_INBUF_CBJ_BST1_OFF (0x0 << 11)
+#define RT5663_CBJ_SENSE_BST1_MASK (0x1 << 10)
+#define RT5663_CBJ_SENSE_BST1_SHIFT 10
+#define RT5663_CBJ_SENSE_BST1_L (0x1 << 10)
+#define RT5663_CBJ_SENSE_BST1_R (0x0 << 10)
+
+/* Combo Jack Control (0x0251) */
+#define RT5663_GAIN_BST1_MASK (0xf)
+#define RT5663_GAIN_BST1_SHIFT 0
+
+/* Dummy register 1 (0x02fa) */
+#define RT5663_EMB_CLK_MASK (0x1 << 9)
+#define RT5663_EMB_CLK_SHIFT 9
+#define RT5663_EMB_CLK_EN (0x1 << 9)
+#define RT5663_EMB_CLK_DIS (0x0 << 9)
+#define RT5663_HPA_CPL_BIAS_MASK (0x7 << 6)
+#define RT5663_HPA_CPL_BIAS_SHIFT 6
+#define RT5663_HPA_CPL_BIAS_0_5 (0x0 << 6)
+#define RT5663_HPA_CPL_BIAS_1 (0x1 << 6)
+#define RT5663_HPA_CPL_BIAS_2 (0x2 << 6)
+#define RT5663_HPA_CPL_BIAS_3 (0x3 << 6)
+#define RT5663_HPA_CPL_BIAS_4_1 (0x4 << 6)
+#define RT5663_HPA_CPL_BIAS_4_2 (0x5 << 6)
+#define RT5663_HPA_CPL_BIAS_6 (0x6 << 6)
+#define RT5663_HPA_CPL_BIAS_8 (0x7 << 6)
+#define RT5663_HPA_CPR_BIAS_MASK (0x7 << 3)
+#define RT5663_HPA_CPR_BIAS_SHIFT 3
+#define RT5663_HPA_CPR_BIAS_0_5 (0x0 << 3)
+#define RT5663_HPA_CPR_BIAS_1 (0x1 << 3)
+#define RT5663_HPA_CPR_BIAS_2 (0x2 << 3)
+#define RT5663_HPA_CPR_BIAS_3 (0x3 << 3)
+#define RT5663_HPA_CPR_BIAS_4_1 (0x4 << 3)
+#define RT5663_HPA_CPR_BIAS_4_2 (0x5 << 3)
+#define RT5663_HPA_CPR_BIAS_6 (0x6 << 3)
+#define RT5663_HPA_CPR_BIAS_8 (0x7 << 3)
+#define RT5663_DUMMY_BIAS_MASK (0x7)
+#define RT5663_DUMMY_BIAS_SHIFT 0
+#define RT5663_DUMMY_BIAS_0_5 (0x0)
+#define RT5663_DUMMY_BIAS_1 (0x1)
+#define RT5663_DUMMY_BIAS_2 (0x2)
+#define RT5663_DUMMY_BIAS_3 (0x3)
+#define RT5663_DUMMY_BIAS_4_1 (0x4)
+#define RT5663_DUMMY_BIAS_4_2 (0x5)
+#define RT5663_DUMMY_BIAS_6 (0x6)
+#define RT5663_DUMMY_BIAS_8 (0x7)
+
+
+/* System Clock Source */
+enum {
+ RT5663_SCLK_S_MCLK,
+ RT5663_SCLK_S_PLL1,
+ RT5663_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+ RT5663_PLL1_S_MCLK,
+ RT5663_PLL1_S_BCLK1,
+};
+
+enum {
+ RT5663_AIF,
+ RT5663_AIFS,
+};
+
+/* asrc clock source */
+enum {
+ RT5663_CLK_SEL_SYS = 0x0,
+ RT5663_CLK_SEL_I2S1_ASRC = 0x1,
+};
+
+/* filter mask */
+enum {
+ RT5663_DA_STEREO_FILTER = 0x1,
+ RT5663_AD_STEREO_FILTER = 0x2,
+};
+
+int rt5663_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+#endif /* __RT5663_H__ */
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
new file mode 100644
index 000000000000..c7beccd54b16
--- /dev/null
+++ b/sound/soc/codecs/rt5665.c
@@ -0,0 +1,4868 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5665.c -- RT5665/RT5658 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5665.h>
+
+#include "rl6231.h"
+#include "rt5665.h"
+
+static const char * const rt5665_supply_names[] = {
+ "AVDD",
+ "MICVDD",
+ "VBAT",
+};
+
+struct rt5665_priv {
+ struct snd_soc_component *component;
+ struct rt5665_platform_data pdata;
+ struct regmap *regmap;
+ struct gpio_desc *gpiod_ldo1_en;
+ struct gpio_desc *gpiod_reset;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work calibrate_work;
+ struct delayed_work jd_check_work;
+ struct mutex calibrate_mutex;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5665_AIFS];
+ int bclk[RT5665_AIFS];
+ int master[RT5665_AIFS];
+ int id;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+ int irq_work_delay_time;
+ unsigned int sar_adc_value;
+ bool calibration_done;
+};
+
+static const struct reg_default rt5665_reg[] = {
+ {0x0000, 0x0000},
+ {0x0001, 0xc8c8},
+ {0x0002, 0x8080},
+ {0x0003, 0x8000},
+ {0x0004, 0xc80a},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0007, 0x0000},
+ {0x000a, 0x0000},
+ {0x000b, 0x0000},
+ {0x000c, 0x0000},
+ {0x000d, 0x0000},
+ {0x000f, 0x0808},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x1404},
+ {0x0013, 0x1000},
+ {0x0014, 0xa00a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0017, 0x0011},
+ {0x0018, 0xafaf},
+ {0x0019, 0xafaf},
+ {0x001a, 0xafaf},
+ {0x001b, 0x0011},
+ {0x001c, 0x2f2f},
+ {0x001d, 0x2f2f},
+ {0x001e, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0020, 0x0000},
+ {0x0021, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0026, 0xc0c0},
+ {0x0027, 0xc0c0},
+ {0x0028, 0xc0c0},
+ {0x0029, 0x8080},
+ {0x002a, 0xaaaa},
+ {0x002b, 0xaaaa},
+ {0x002c, 0xaba8},
+ {0x002d, 0x0000},
+ {0x002e, 0x0000},
+ {0x002f, 0x0000},
+ {0x0030, 0x0000},
+ {0x0031, 0x5000},
+ {0x0032, 0x0000},
+ {0x0033, 0x0000},
+ {0x0034, 0x0000},
+ {0x0035, 0x0000},
+ {0x003a, 0x0000},
+ {0x003b, 0x0000},
+ {0x003c, 0x00ff},
+ {0x003d, 0x0000},
+ {0x003e, 0x00ff},
+ {0x003f, 0x0000},
+ {0x0040, 0x0000},
+ {0x0041, 0x00ff},
+ {0x0042, 0x0000},
+ {0x0043, 0x00ff},
+ {0x0044, 0x0c0c},
+ {0x0049, 0xc00b},
+ {0x004a, 0x0000},
+ {0x004b, 0x031f},
+ {0x004d, 0x0000},
+ {0x004e, 0x001f},
+ {0x004f, 0x0000},
+ {0x0050, 0x001f},
+ {0x0052, 0xf000},
+ {0x0061, 0x0000},
+ {0x0062, 0x0000},
+ {0x0063, 0x003e},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x003f},
+ {0x0067, 0x0000},
+ {0x006b, 0x0000},
+ {0x006d, 0xff00},
+ {0x006e, 0x2808},
+ {0x006f, 0x000a},
+ {0x0070, 0x8000},
+ {0x0071, 0x8000},
+ {0x0072, 0x8000},
+ {0x0073, 0x7000},
+ {0x0074, 0x7770},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0078, 0x00f0},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0000},
+ {0x007d, 0x0123},
+ {0x007e, 0x4500},
+ {0x007f, 0x8003},
+ {0x0080, 0x0000},
+ {0x0081, 0x0000},
+ {0x0082, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0008},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x0089, 0x0000},
+ {0x008a, 0x0000},
+ {0x008b, 0x0000},
+ {0x008c, 0x0003},
+ {0x008e, 0x0060},
+ {0x008f, 0x1000},
+ {0x0091, 0x0c26},
+ {0x0092, 0x0073},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0098, 0x0000},
+ {0x0099, 0x0000},
+ {0x009a, 0x0007},
+ {0x009f, 0x0000},
+ {0x00a0, 0x0000},
+ {0x00a1, 0x0002},
+ {0x00a2, 0x0001},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00ae, 0x2040},
+ {0x00af, 0x0000},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0000},
+ {0x00ba, 0x0002},
+ {0x00bb, 0x0000},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0000},
+ {0x00c1, 0x0aaa},
+ {0x00c2, 0xaa80},
+ {0x00c3, 0x0003},
+ {0x00c4, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d1, 0x2244},
+ {0x00d3, 0x3300},
+ {0x00d4, 0x2200},
+ {0x00d9, 0x0809},
+ {0x00da, 0x0000},
+ {0x00db, 0x0008},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x6724},
+ {0x00de, 0x3131},
+ {0x00df, 0x0008},
+ {0x00e0, 0x4000},
+ {0x00e1, 0x3131},
+ {0x00e2, 0x600c},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00ec, 0xb300},
+ {0x00ed, 0x0000},
+ {0x00ee, 0xb320},
+ {0x00ef, 0x0000},
+ {0x00f0, 0x0201},
+ {0x00f1, 0x0ddd},
+ {0x00f2, 0x0ddd},
+ {0x00f6, 0x0000},
+ {0x00f7, 0x0000},
+ {0x00f8, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6451},
+ {0x0100, 0xaaaa},
+ {0x0101, 0x000a},
+ {0x010a, 0xaaaa},
+ {0x010b, 0xa0a0},
+ {0x010c, 0xaeae},
+ {0x010d, 0xaaaa},
+ {0x010e, 0xaaaa},
+ {0x010f, 0xaaaa},
+ {0x0110, 0xe002},
+ {0x0111, 0xa402},
+ {0x0112, 0xaaaa},
+ {0x0113, 0x2000},
+ {0x0117, 0x0f00},
+ {0x0125, 0x0410},
+ {0x0132, 0x0000},
+ {0x0133, 0x0000},
+ {0x0137, 0x5540},
+ {0x0138, 0x3700},
+ {0x0139, 0x79a1},
+ {0x013a, 0x2020},
+ {0x013b, 0x2020},
+ {0x013c, 0x2005},
+ {0x013f, 0x0000},
+ {0x0145, 0x0002},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0150, 0x0000},
+ {0x0160, 0x4eff},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0170, 0x4e87},
+ {0x0171, 0x0080},
+ {0x0172, 0x0200},
+ {0x0173, 0x0800},
+ {0x0174, 0x00ff},
+ {0x0175, 0x0000},
+ {0x0190, 0x413d},
+ {0x0191, 0x4139},
+ {0x0192, 0x4135},
+ {0x0193, 0x413d},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0196, 0x0000},
+ {0x0197, 0x0000},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01a0, 0x1e64},
+ {0x01a1, 0x06a3},
+ {0x01a2, 0x0000},
+ {0x01a3, 0x0000},
+ {0x01a4, 0x0000},
+ {0x01a5, 0x0000},
+ {0x01a6, 0x0000},
+ {0x01a7, 0x8000},
+ {0x01a8, 0x0000},
+ {0x01a9, 0x0000},
+ {0x01aa, 0x0000},
+ {0x01ab, 0x0000},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x01c3},
+ {0x01b7, 0x02a0},
+ {0x01b8, 0x03e9},
+ {0x01b9, 0x1389},
+ {0x01ba, 0xc351},
+ {0x01bb, 0x0009},
+ {0x01bc, 0x0018},
+ {0x01bd, 0x002a},
+ {0x01be, 0x004c},
+ {0x01bf, 0x0097},
+ {0x01c0, 0x433d},
+ {0x01c1, 0x0000},
+ {0x01c2, 0x0000},
+ {0x01c3, 0x0000},
+ {0x01c4, 0x0000},
+ {0x01c5, 0x0000},
+ {0x01c6, 0x0000},
+ {0x01c7, 0x0000},
+ {0x01c8, 0x40af},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x003c},
+ {0x01da, 0x0000},
+ {0x01db, 0x0000},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0320},
+ {0x01e0, 0x06a1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01ea, 0xbf3f},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0200, 0x0000},
+ {0x0201, 0x0000},
+ {0x0202, 0x0000},
+ {0x0203, 0x0000},
+ {0x0204, 0x0000},
+ {0x0205, 0x0000},
+ {0x0206, 0x0000},
+ {0x0207, 0x0000},
+ {0x0208, 0x0000},
+ {0x0210, 0x60b1},
+ {0x0211, 0xa005},
+ {0x0212, 0x024c},
+ {0x0213, 0xf7ff},
+ {0x0214, 0x024c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x02ff, 0x0110},
+ {0x0300, 0x001f},
+ {0x0301, 0x032c},
+ {0x0302, 0x5f21},
+ {0x0303, 0x4000},
+ {0x0304, 0x4000},
+ {0x0305, 0x06d5},
+ {0x0306, 0x8000},
+ {0x0307, 0x0700},
+ {0x0310, 0x4560},
+ {0x0311, 0xa4a8},
+ {0x0312, 0x7418},
+ {0x0313, 0x0000},
+ {0x0314, 0x0006},
+ {0x0315, 0xffff},
+ {0x0316, 0xc400},
+ {0x0317, 0x0000},
+ {0x0330, 0x00a6},
+ {0x0331, 0x04c3},
+ {0x0332, 0x27c8},
+ {0x0333, 0xbf50},
+ {0x0334, 0x0045},
+ {0x0335, 0x0007},
+ {0x0336, 0x7418},
+ {0x0337, 0x0501},
+ {0x0338, 0x0000},
+ {0x0339, 0x0010},
+ {0x033a, 0x1010},
+ {0x03c0, 0x7e00},
+ {0x03c1, 0x8000},
+ {0x03c2, 0x8000},
+ {0x03c3, 0x8000},
+ {0x03c4, 0x8000},
+ {0x03c5, 0x8000},
+ {0x03c6, 0x8000},
+ {0x03c7, 0x8000},
+ {0x03c8, 0x8000},
+ {0x03c9, 0x8000},
+ {0x03ca, 0x8000},
+ {0x03cb, 0x8000},
+ {0x03cc, 0x8000},
+ {0x03d0, 0x0000},
+ {0x03d1, 0x0000},
+ {0x03d2, 0x0000},
+ {0x03d3, 0x0000},
+ {0x03d4, 0x2000},
+ {0x03d5, 0x2000},
+ {0x03d6, 0x0000},
+ {0x03d7, 0x0000},
+ {0x03d8, 0x2000},
+ {0x03d9, 0x2000},
+ {0x03da, 0x2000},
+ {0x03db, 0x2000},
+ {0x03dc, 0x0000},
+ {0x03dd, 0x0000},
+ {0x03de, 0x0000},
+ {0x03df, 0x2000},
+ {0x03e0, 0x0000},
+ {0x03e1, 0x0000},
+ {0x03e2, 0x0000},
+ {0x03e3, 0x0000},
+ {0x03e4, 0x0000},
+ {0x03e5, 0x0000},
+ {0x03e6, 0x0000},
+ {0x03e7, 0x0000},
+ {0x03e8, 0x0000},
+ {0x03e9, 0x0000},
+ {0x03ea, 0x0000},
+ {0x03eb, 0x0000},
+ {0x03ec, 0x0000},
+ {0x03ed, 0x0000},
+ {0x03ee, 0x0000},
+ {0x03ef, 0x0000},
+ {0x03f0, 0x0800},
+ {0x03f1, 0x0800},
+ {0x03f2, 0x0800},
+ {0x03f3, 0x0800},
+};
+
+static bool rt5665_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5665_RESET:
+ case RT5665_EJD_CTRL_2:
+ case RT5665_GPIO_STA:
+ case RT5665_INT_ST_1:
+ case RT5665_IL_CMD_1:
+ case RT5665_4BTN_IL_CMD_1:
+ case RT5665_PSV_IL_CMD_1:
+ case RT5665_AJD1_CTRL:
+ case RT5665_JD_CTRL_3:
+ case RT5665_STO_NG2_CTRL_1:
+ case RT5665_SAR_IL_CMD_4:
+ case RT5665_DEVICE_ID:
+ case RT5665_STO1_DAC_SIL_DET ... RT5665_STO2_DAC_SIL_DET:
+ case RT5665_MONO_AMP_CALIB_STA1 ... RT5665_MONO_AMP_CALIB_STA6:
+ case RT5665_HP_IMP_SENS_CTRL_12 ... RT5665_HP_IMP_SENS_CTRL_15:
+ case RT5665_HP_CALIB_STA_1 ... RT5665_HP_CALIB_STA_11:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5665_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5665_RESET:
+ case RT5665_VENDOR_ID:
+ case RT5665_VENDOR_ID_1:
+ case RT5665_DEVICE_ID:
+ case RT5665_LOUT:
+ case RT5665_HP_CTRL_1:
+ case RT5665_HP_CTRL_2:
+ case RT5665_MONO_OUT:
+ case RT5665_HPL_GAIN:
+ case RT5665_HPR_GAIN:
+ case RT5665_MONO_GAIN:
+ case RT5665_CAL_BST_CTRL:
+ case RT5665_CBJ_BST_CTRL:
+ case RT5665_IN1_IN2:
+ case RT5665_IN3_IN4:
+ case RT5665_INL1_INR1_VOL:
+ case RT5665_EJD_CTRL_1:
+ case RT5665_EJD_CTRL_2:
+ case RT5665_EJD_CTRL_3:
+ case RT5665_EJD_CTRL_4:
+ case RT5665_EJD_CTRL_5:
+ case RT5665_EJD_CTRL_6:
+ case RT5665_EJD_CTRL_7:
+ case RT5665_DAC2_CTRL:
+ case RT5665_DAC2_DIG_VOL:
+ case RT5665_DAC1_DIG_VOL:
+ case RT5665_DAC3_DIG_VOL:
+ case RT5665_DAC3_CTRL:
+ case RT5665_STO1_ADC_DIG_VOL:
+ case RT5665_MONO_ADC_DIG_VOL:
+ case RT5665_STO2_ADC_DIG_VOL:
+ case RT5665_STO1_ADC_BOOST:
+ case RT5665_MONO_ADC_BOOST:
+ case RT5665_STO2_ADC_BOOST:
+ case RT5665_HP_IMP_GAIN_1:
+ case RT5665_HP_IMP_GAIN_2:
+ case RT5665_STO1_ADC_MIXER:
+ case RT5665_MONO_ADC_MIXER:
+ case RT5665_STO2_ADC_MIXER:
+ case RT5665_AD_DA_MIXER:
+ case RT5665_STO1_DAC_MIXER:
+ case RT5665_MONO_DAC_MIXER:
+ case RT5665_STO2_DAC_MIXER:
+ case RT5665_A_DAC1_MUX:
+ case RT5665_A_DAC2_MUX:
+ case RT5665_DIG_INF2_DATA:
+ case RT5665_DIG_INF3_DATA:
+ case RT5665_PDM_OUT_CTRL:
+ case RT5665_PDM_DATA_CTRL_1:
+ case RT5665_PDM_DATA_CTRL_2:
+ case RT5665_PDM_DATA_CTRL_3:
+ case RT5665_PDM_DATA_CTRL_4:
+ case RT5665_REC1_GAIN:
+ case RT5665_REC1_L1_MIXER:
+ case RT5665_REC1_L2_MIXER:
+ case RT5665_REC1_R1_MIXER:
+ case RT5665_REC1_R2_MIXER:
+ case RT5665_REC2_GAIN:
+ case RT5665_REC2_L1_MIXER:
+ case RT5665_REC2_L2_MIXER:
+ case RT5665_REC2_R1_MIXER:
+ case RT5665_REC2_R2_MIXER:
+ case RT5665_CAL_REC:
+ case RT5665_ALC_BACK_GAIN:
+ case RT5665_MONOMIX_GAIN:
+ case RT5665_MONOMIX_IN_GAIN:
+ case RT5665_OUT_L_GAIN:
+ case RT5665_OUT_L_MIXER:
+ case RT5665_OUT_R_GAIN:
+ case RT5665_OUT_R_MIXER:
+ case RT5665_LOUT_MIXER:
+ case RT5665_PWR_DIG_1:
+ case RT5665_PWR_DIG_2:
+ case RT5665_PWR_ANLG_1:
+ case RT5665_PWR_ANLG_2:
+ case RT5665_PWR_ANLG_3:
+ case RT5665_PWR_MIXER:
+ case RT5665_PWR_VOL:
+ case RT5665_CLK_DET:
+ case RT5665_HPF_CTRL1:
+ case RT5665_DMIC_CTRL_1:
+ case RT5665_DMIC_CTRL_2:
+ case RT5665_I2S1_SDP:
+ case RT5665_I2S2_SDP:
+ case RT5665_I2S3_SDP:
+ case RT5665_ADDA_CLK_1:
+ case RT5665_ADDA_CLK_2:
+ case RT5665_I2S1_F_DIV_CTRL_1:
+ case RT5665_I2S1_F_DIV_CTRL_2:
+ case RT5665_TDM_CTRL_1:
+ case RT5665_TDM_CTRL_2:
+ case RT5665_TDM_CTRL_3:
+ case RT5665_TDM_CTRL_4:
+ case RT5665_TDM_CTRL_5:
+ case RT5665_TDM_CTRL_6:
+ case RT5665_TDM_CTRL_7:
+ case RT5665_TDM_CTRL_8:
+ case RT5665_GLB_CLK:
+ case RT5665_PLL_CTRL_1:
+ case RT5665_PLL_CTRL_2:
+ case RT5665_ASRC_1:
+ case RT5665_ASRC_2:
+ case RT5665_ASRC_3:
+ case RT5665_ASRC_4:
+ case RT5665_ASRC_5:
+ case RT5665_ASRC_6:
+ case RT5665_ASRC_7:
+ case RT5665_ASRC_8:
+ case RT5665_ASRC_9:
+ case RT5665_ASRC_10:
+ case RT5665_DEPOP_1:
+ case RT5665_DEPOP_2:
+ case RT5665_HP_CHARGE_PUMP_1:
+ case RT5665_HP_CHARGE_PUMP_2:
+ case RT5665_MICBIAS_1:
+ case RT5665_MICBIAS_2:
+ case RT5665_ASRC_12:
+ case RT5665_ASRC_13:
+ case RT5665_ASRC_14:
+ case RT5665_RC_CLK_CTRL:
+ case RT5665_I2S_M_CLK_CTRL_1:
+ case RT5665_I2S2_F_DIV_CTRL_1:
+ case RT5665_I2S2_F_DIV_CTRL_2:
+ case RT5665_I2S3_F_DIV_CTRL_1:
+ case RT5665_I2S3_F_DIV_CTRL_2:
+ case RT5665_EQ_CTRL_1:
+ case RT5665_EQ_CTRL_2:
+ case RT5665_IRQ_CTRL_1:
+ case RT5665_IRQ_CTRL_2:
+ case RT5665_IRQ_CTRL_3:
+ case RT5665_IRQ_CTRL_4:
+ case RT5665_IRQ_CTRL_5:
+ case RT5665_IRQ_CTRL_6:
+ case RT5665_INT_ST_1:
+ case RT5665_GPIO_CTRL_1:
+ case RT5665_GPIO_CTRL_2:
+ case RT5665_GPIO_CTRL_3:
+ case RT5665_GPIO_CTRL_4:
+ case RT5665_GPIO_STA:
+ case RT5665_HP_AMP_DET_CTRL_1:
+ case RT5665_HP_AMP_DET_CTRL_2:
+ case RT5665_MID_HP_AMP_DET:
+ case RT5665_LOW_HP_AMP_DET:
+ case RT5665_SV_ZCD_1:
+ case RT5665_SV_ZCD_2:
+ case RT5665_IL_CMD_1:
+ case RT5665_IL_CMD_2:
+ case RT5665_IL_CMD_3:
+ case RT5665_IL_CMD_4:
+ case RT5665_4BTN_IL_CMD_1:
+ case RT5665_4BTN_IL_CMD_2:
+ case RT5665_4BTN_IL_CMD_3:
+ case RT5665_PSV_IL_CMD_1:
+ case RT5665_ADC_STO1_HP_CTRL_1:
+ case RT5665_ADC_STO1_HP_CTRL_2:
+ case RT5665_ADC_MONO_HP_CTRL_1:
+ case RT5665_ADC_MONO_HP_CTRL_2:
+ case RT5665_ADC_STO2_HP_CTRL_1:
+ case RT5665_ADC_STO2_HP_CTRL_2:
+ case RT5665_AJD1_CTRL:
+ case RT5665_JD1_THD:
+ case RT5665_JD2_THD:
+ case RT5665_JD_CTRL_1:
+ case RT5665_JD_CTRL_2:
+ case RT5665_JD_CTRL_3:
+ case RT5665_DIG_MISC:
+ case RT5665_DUMMY_2:
+ case RT5665_DUMMY_3:
+ case RT5665_DAC_ADC_DIG_VOL1:
+ case RT5665_DAC_ADC_DIG_VOL2:
+ case RT5665_BIAS_CUR_CTRL_1:
+ case RT5665_BIAS_CUR_CTRL_2:
+ case RT5665_BIAS_CUR_CTRL_3:
+ case RT5665_BIAS_CUR_CTRL_4:
+ case RT5665_BIAS_CUR_CTRL_5:
+ case RT5665_BIAS_CUR_CTRL_6:
+ case RT5665_BIAS_CUR_CTRL_7:
+ case RT5665_BIAS_CUR_CTRL_8:
+ case RT5665_BIAS_CUR_CTRL_9:
+ case RT5665_BIAS_CUR_CTRL_10:
+ case RT5665_VREF_REC_OP_FB_CAP_CTRL:
+ case RT5665_CHARGE_PUMP_1:
+ case RT5665_DIG_IN_CTRL_1:
+ case RT5665_DIG_IN_CTRL_2:
+ case RT5665_PAD_DRIVING_CTRL:
+ case RT5665_SOFT_RAMP_DEPOP:
+ case RT5665_PLL:
+ case RT5665_CHOP_DAC:
+ case RT5665_CHOP_ADC:
+ case RT5665_CALIB_ADC_CTRL:
+ case RT5665_VOL_TEST:
+ case RT5665_TEST_MODE_CTRL_1:
+ case RT5665_TEST_MODE_CTRL_2:
+ case RT5665_TEST_MODE_CTRL_3:
+ case RT5665_TEST_MODE_CTRL_4:
+ case RT5665_BASSBACK_CTRL:
+ case RT5665_STO_NG2_CTRL_1:
+ case RT5665_STO_NG2_CTRL_2:
+ case RT5665_STO_NG2_CTRL_3:
+ case RT5665_STO_NG2_CTRL_4:
+ case RT5665_STO_NG2_CTRL_5:
+ case RT5665_STO_NG2_CTRL_6:
+ case RT5665_STO_NG2_CTRL_7:
+ case RT5665_STO_NG2_CTRL_8:
+ case RT5665_MONO_NG2_CTRL_1:
+ case RT5665_MONO_NG2_CTRL_2:
+ case RT5665_MONO_NG2_CTRL_3:
+ case RT5665_MONO_NG2_CTRL_4:
+ case RT5665_MONO_NG2_CTRL_5:
+ case RT5665_MONO_NG2_CTRL_6:
+ case RT5665_STO1_DAC_SIL_DET:
+ case RT5665_MONOL_DAC_SIL_DET:
+ case RT5665_MONOR_DAC_SIL_DET:
+ case RT5665_STO2_DAC_SIL_DET:
+ case RT5665_SIL_PSV_CTRL1:
+ case RT5665_SIL_PSV_CTRL2:
+ case RT5665_SIL_PSV_CTRL3:
+ case RT5665_SIL_PSV_CTRL4:
+ case RT5665_SIL_PSV_CTRL5:
+ case RT5665_SIL_PSV_CTRL6:
+ case RT5665_MONO_AMP_CALIB_CTRL_1:
+ case RT5665_MONO_AMP_CALIB_CTRL_2:
+ case RT5665_MONO_AMP_CALIB_CTRL_3:
+ case RT5665_MONO_AMP_CALIB_CTRL_4:
+ case RT5665_MONO_AMP_CALIB_CTRL_5:
+ case RT5665_MONO_AMP_CALIB_CTRL_6:
+ case RT5665_MONO_AMP_CALIB_CTRL_7:
+ case RT5665_MONO_AMP_CALIB_STA1:
+ case RT5665_MONO_AMP_CALIB_STA2:
+ case RT5665_MONO_AMP_CALIB_STA3:
+ case RT5665_MONO_AMP_CALIB_STA4:
+ case RT5665_MONO_AMP_CALIB_STA6:
+ case RT5665_HP_IMP_SENS_CTRL_01:
+ case RT5665_HP_IMP_SENS_CTRL_02:
+ case RT5665_HP_IMP_SENS_CTRL_03:
+ case RT5665_HP_IMP_SENS_CTRL_04:
+ case RT5665_HP_IMP_SENS_CTRL_05:
+ case RT5665_HP_IMP_SENS_CTRL_06:
+ case RT5665_HP_IMP_SENS_CTRL_07:
+ case RT5665_HP_IMP_SENS_CTRL_08:
+ case RT5665_HP_IMP_SENS_CTRL_09:
+ case RT5665_HP_IMP_SENS_CTRL_10:
+ case RT5665_HP_IMP_SENS_CTRL_11:
+ case RT5665_HP_IMP_SENS_CTRL_12:
+ case RT5665_HP_IMP_SENS_CTRL_13:
+ case RT5665_HP_IMP_SENS_CTRL_14:
+ case RT5665_HP_IMP_SENS_CTRL_15:
+ case RT5665_HP_IMP_SENS_CTRL_16:
+ case RT5665_HP_IMP_SENS_CTRL_17:
+ case RT5665_HP_IMP_SENS_CTRL_18:
+ case RT5665_HP_IMP_SENS_CTRL_19:
+ case RT5665_HP_IMP_SENS_CTRL_20:
+ case RT5665_HP_IMP_SENS_CTRL_21:
+ case RT5665_HP_IMP_SENS_CTRL_22:
+ case RT5665_HP_IMP_SENS_CTRL_23:
+ case RT5665_HP_IMP_SENS_CTRL_24:
+ case RT5665_HP_IMP_SENS_CTRL_25:
+ case RT5665_HP_IMP_SENS_CTRL_26:
+ case RT5665_HP_IMP_SENS_CTRL_27:
+ case RT5665_HP_IMP_SENS_CTRL_28:
+ case RT5665_HP_IMP_SENS_CTRL_29:
+ case RT5665_HP_IMP_SENS_CTRL_30:
+ case RT5665_HP_IMP_SENS_CTRL_31:
+ case RT5665_HP_IMP_SENS_CTRL_32:
+ case RT5665_HP_IMP_SENS_CTRL_33:
+ case RT5665_HP_IMP_SENS_CTRL_34:
+ case RT5665_HP_LOGIC_CTRL_1:
+ case RT5665_HP_LOGIC_CTRL_2:
+ case RT5665_HP_LOGIC_CTRL_3:
+ case RT5665_HP_CALIB_CTRL_1:
+ case RT5665_HP_CALIB_CTRL_2:
+ case RT5665_HP_CALIB_CTRL_3:
+ case RT5665_HP_CALIB_CTRL_4:
+ case RT5665_HP_CALIB_CTRL_5:
+ case RT5665_HP_CALIB_CTRL_6:
+ case RT5665_HP_CALIB_CTRL_7:
+ case RT5665_HP_CALIB_CTRL_9:
+ case RT5665_HP_CALIB_CTRL_10:
+ case RT5665_HP_CALIB_CTRL_11:
+ case RT5665_HP_CALIB_STA_1:
+ case RT5665_HP_CALIB_STA_2:
+ case RT5665_HP_CALIB_STA_3:
+ case RT5665_HP_CALIB_STA_4:
+ case RT5665_HP_CALIB_STA_5:
+ case RT5665_HP_CALIB_STA_6:
+ case RT5665_HP_CALIB_STA_7:
+ case RT5665_HP_CALIB_STA_8:
+ case RT5665_HP_CALIB_STA_9:
+ case RT5665_HP_CALIB_STA_10:
+ case RT5665_HP_CALIB_STA_11:
+ case RT5665_PGM_TAB_CTRL1:
+ case RT5665_PGM_TAB_CTRL2:
+ case RT5665_PGM_TAB_CTRL3:
+ case RT5665_PGM_TAB_CTRL4:
+ case RT5665_PGM_TAB_CTRL5:
+ case RT5665_PGM_TAB_CTRL6:
+ case RT5665_PGM_TAB_CTRL7:
+ case RT5665_PGM_TAB_CTRL8:
+ case RT5665_PGM_TAB_CTRL9:
+ case RT5665_SAR_IL_CMD_1:
+ case RT5665_SAR_IL_CMD_2:
+ case RT5665_SAR_IL_CMD_3:
+ case RT5665_SAR_IL_CMD_4:
+ case RT5665_SAR_IL_CMD_5:
+ case RT5665_SAR_IL_CMD_6:
+ case RT5665_SAR_IL_CMD_7:
+ case RT5665_SAR_IL_CMD_8:
+ case RT5665_SAR_IL_CMD_9:
+ case RT5665_SAR_IL_CMD_10:
+ case RT5665_SAR_IL_CMD_11:
+ case RT5665_SAR_IL_CMD_12:
+ case RT5665_DRC1_CTRL_0:
+ case RT5665_DRC1_CTRL_1:
+ case RT5665_DRC1_CTRL_2:
+ case RT5665_DRC1_CTRL_3:
+ case RT5665_DRC1_CTRL_4:
+ case RT5665_DRC1_CTRL_5:
+ case RT5665_DRC1_CTRL_6:
+ case RT5665_DRC1_HARD_LMT_CTRL_1:
+ case RT5665_DRC1_HARD_LMT_CTRL_2:
+ case RT5665_DRC1_PRIV_1:
+ case RT5665_DRC1_PRIV_2:
+ case RT5665_DRC1_PRIV_3:
+ case RT5665_DRC1_PRIV_4:
+ case RT5665_DRC1_PRIV_5:
+ case RT5665_DRC1_PRIV_6:
+ case RT5665_DRC1_PRIV_7:
+ case RT5665_DRC1_PRIV_8:
+ case RT5665_ALC_PGA_CTRL_1:
+ case RT5665_ALC_PGA_CTRL_2:
+ case RT5665_ALC_PGA_CTRL_3:
+ case RT5665_ALC_PGA_CTRL_4:
+ case RT5665_ALC_PGA_CTRL_5:
+ case RT5665_ALC_PGA_CTRL_6:
+ case RT5665_ALC_PGA_CTRL_7:
+ case RT5665_ALC_PGA_CTRL_8:
+ case RT5665_ALC_PGA_STA_1:
+ case RT5665_ALC_PGA_STA_2:
+ case RT5665_ALC_PGA_STA_3:
+ case RT5665_EQ_AUTO_RCV_CTRL1:
+ case RT5665_EQ_AUTO_RCV_CTRL2:
+ case RT5665_EQ_AUTO_RCV_CTRL3:
+ case RT5665_EQ_AUTO_RCV_CTRL4:
+ case RT5665_EQ_AUTO_RCV_CTRL5:
+ case RT5665_EQ_AUTO_RCV_CTRL6:
+ case RT5665_EQ_AUTO_RCV_CTRL7:
+ case RT5665_EQ_AUTO_RCV_CTRL8:
+ case RT5665_EQ_AUTO_RCV_CTRL9:
+ case RT5665_EQ_AUTO_RCV_CTRL10:
+ case RT5665_EQ_AUTO_RCV_CTRL11:
+ case RT5665_EQ_AUTO_RCV_CTRL12:
+ case RT5665_EQ_AUTO_RCV_CTRL13:
+ case RT5665_ADC_L_EQ_LPF1_A1:
+ case RT5665_R_EQ_LPF1_A1:
+ case RT5665_L_EQ_LPF1_H0:
+ case RT5665_R_EQ_LPF1_H0:
+ case RT5665_L_EQ_BPF1_A1:
+ case RT5665_R_EQ_BPF1_A1:
+ case RT5665_L_EQ_BPF1_A2:
+ case RT5665_R_EQ_BPF1_A2:
+ case RT5665_L_EQ_BPF1_H0:
+ case RT5665_R_EQ_BPF1_H0:
+ case RT5665_L_EQ_BPF2_A1:
+ case RT5665_R_EQ_BPF2_A1:
+ case RT5665_L_EQ_BPF2_A2:
+ case RT5665_R_EQ_BPF2_A2:
+ case RT5665_L_EQ_BPF2_H0:
+ case RT5665_R_EQ_BPF2_H0:
+ case RT5665_L_EQ_BPF3_A1:
+ case RT5665_R_EQ_BPF3_A1:
+ case RT5665_L_EQ_BPF3_A2:
+ case RT5665_R_EQ_BPF3_A2:
+ case RT5665_L_EQ_BPF3_H0:
+ case RT5665_R_EQ_BPF3_H0:
+ case RT5665_L_EQ_BPF4_A1:
+ case RT5665_R_EQ_BPF4_A1:
+ case RT5665_L_EQ_BPF4_A2:
+ case RT5665_R_EQ_BPF4_A2:
+ case RT5665_L_EQ_BPF4_H0:
+ case RT5665_R_EQ_BPF4_H0:
+ case RT5665_L_EQ_HPF1_A1:
+ case RT5665_R_EQ_HPF1_A1:
+ case RT5665_L_EQ_HPF1_H0:
+ case RT5665_R_EQ_HPF1_H0:
+ case RT5665_L_EQ_PRE_VOL:
+ case RT5665_R_EQ_PRE_VOL:
+ case RT5665_L_EQ_POST_VOL:
+ case RT5665_R_EQ_POST_VOL:
+ case RT5665_SCAN_MODE_CTRL:
+ case RT5665_I2C_MODE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_vol_tlv, -1400, 150, 0);
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+static const DECLARE_TLV_DB_SCALE(in_bst_tlv, -1200, 75, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* Interface data select */
+static const char * const rt5665_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT01_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT23_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT45_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT67_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT01_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT23_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT45_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum,
+ RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT67_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum,
+ RT5665_DIG_INF2_DATA, RT5665_IF2_1_DAC_SEL_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum,
+ RT5665_DIG_INF2_DATA, RT5665_IF2_1_ADC_SEL_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum,
+ RT5665_DIG_INF2_DATA, RT5665_IF2_2_DAC_SEL_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum,
+ RT5665_DIG_INF2_DATA, RT5665_IF2_2_ADC_SEL_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum,
+ RT5665_DIG_INF3_DATA, RT5665_IF3_DAC_SEL_SFT, rt5665_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum,
+ RT5665_DIG_INF3_DATA, RT5665_IF3_ADC_SEL_SFT, rt5665_data_select);
+
+static const struct snd_kcontrol_new rt5665_if1_1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_1 01 ADC Swap Mux", rt5665_if1_1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_1 23 ADC Swap Mux", rt5665_if1_1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_1 45 ADC Swap Mux", rt5665_if1_1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_1 67 ADC Swap Mux", rt5665_if1_1_67_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_2_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_2 01 ADC Swap Mux", rt5665_if1_2_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_2_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_2 23 ADC1 Swap Mux", rt5665_if1_2_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_2_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_2 45 ADC1 Swap Mux", rt5665_if1_2_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if1_2_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1_2 67 ADC1 Swap Mux", rt5665_if1_2_67_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if2_1_dac_swap_mux =
+ SOC_DAPM_ENUM("IF2_1 DAC Swap Source", rt5665_if2_1_dac_enum);
+
+static const struct snd_kcontrol_new rt5665_if2_1_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2_1 ADC Swap Source", rt5665_if2_1_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if2_2_dac_swap_mux =
+ SOC_DAPM_ENUM("IF2_2 DAC Swap Source", rt5665_if2_2_dac_enum);
+
+static const struct snd_kcontrol_new rt5665_if2_2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2_2 ADC Swap Source", rt5665_if2_2_adc_enum);
+
+static const struct snd_kcontrol_new rt5665_if3_dac_swap_mux =
+ SOC_DAPM_ENUM("IF3 DAC Swap Source", rt5665_if3_dac_enum);
+
+static const struct snd_kcontrol_new rt5665_if3_adc_swap_mux =
+ SOC_DAPM_ENUM("IF3 ADC Swap Source", rt5665_if3_adc_enum);
+
+static int rt5665_hp_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ if (snd_soc_component_read(component, RT5665_STO_NG2_CTRL_1) & RT5665_NG2_EN) {
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_EN);
+ }
+
+ return ret;
+}
+
+static int rt5665_mono_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ if (snd_soc_component_read(component, RT5665_MONO_NG2_CTRL_1) & RT5665_NG2_EN) {
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_EN);
+ }
+
+ return ret;
+}
+
+static int rt5665_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5665_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5665_4BTN_IL_CMD_1, val);
+
+ return btn_type;
+}
+
+static void rt5665_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write(component, RT5665_4BTN_IL_CMD_1, 0x0003);
+ snd_soc_component_update_bits(component, RT5665_SAR_IL_CMD_9, 0x1, 0x1);
+ snd_soc_component_write(component, RT5665_IL_CMD_1, 0x0048);
+ snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2,
+ RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK,
+ RT5665_4BTN_IL_EN | RT5665_4BTN_IL_NOR);
+ snd_soc_component_update_bits(component, RT5665_IRQ_CTRL_3,
+ RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_EN);
+ } else {
+ snd_soc_component_update_bits(component, RT5665_IRQ_CTRL_3,
+ RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2,
+ RT5665_4BTN_IL_MASK, RT5665_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2,
+ RT5665_4BTN_IL_RST_MASK, RT5665_4BTN_IL_RST);
+ }
+}
+
+/**
+ * rt5665_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5665_headset_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int sar_hs_type, val;
+
+ if (jack_insert) {
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+ snd_soc_dapm_sync(dapm);
+
+ regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, 0x100,
+ 0x100);
+
+ regmap_read(rt5665->regmap, RT5665_GPIO_STA, &val);
+ if (val & 0x4) {
+ regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
+ 0x100, 0);
+
+ regmap_read(rt5665->regmap, RT5665_GPIO_STA, &val);
+ while (val & 0x4) {
+ usleep_range(10000, 15000);
+ regmap_read(rt5665->regmap, RT5665_GPIO_STA,
+ &val);
+ }
+ }
+
+ regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
+ 0x1a0, 0x120);
+ regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424);
+ regmap_write(rt5665->regmap, RT5665_IL_CMD_1, 0x0048);
+ regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291);
+
+ usleep_range(10000, 15000);
+
+ rt5665->sar_adc_value = snd_soc_component_read(rt5665->component,
+ RT5665_SAR_IL_CMD_4) & 0x7ff;
+
+ sar_hs_type = rt5665->pdata.sar_hs_type ?
+ rt5665->pdata.sar_hs_type : 729;
+
+ if (rt5665->sar_adc_value > sar_hs_type) {
+ rt5665->jack_type = SND_JACK_HEADSET;
+ rt5665_enable_push_button_irq(component, true);
+ } else {
+ rt5665->jack_type = SND_JACK_HEADPHONE;
+ regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1,
+ 0x2291);
+ regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2,
+ 0x100, 0);
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
+ snd_soc_dapm_sync(dapm);
+ }
+ } else {
+ regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0x2291);
+ regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, 0x100, 0);
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
+ snd_soc_dapm_sync(dapm);
+ if (rt5665->jack_type == SND_JACK_HEADSET)
+ rt5665_enable_push_button_irq(component, false);
+ rt5665->jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5665->jack_type);
+ return rt5665->jack_type;
+}
+
+static irqreturn_t rt5665_irq(int irq, void *data)
+{
+ struct rt5665_priv *rt5665 = data;
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5665->jack_detect_work, msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+static void rt5665_jd_check_handler(struct work_struct *work)
+{
+ struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv,
+ jd_check_work.work);
+
+ if (snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010) {
+ /* jack out */
+ rt5665->jack_type = rt5665_headset_detect(rt5665->component, 0);
+
+ snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else {
+ schedule_delayed_work(&rt5665->jd_check_work, 500);
+ }
+}
+
+static int rt5665_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ switch (rt5665->pdata.jd_src) {
+ case RT5665_JD1:
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1,
+ RT5665_GP1_PIN_MASK, RT5665_GP1_PIN_IRQ);
+ regmap_update_bits(rt5665->regmap, RT5665_RC_CLK_CTRL,
+ 0xc000, 0xc000);
+ regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_2,
+ RT5665_PWR_JD1, RT5665_PWR_JD1);
+ regmap_update_bits(rt5665->regmap, RT5665_IRQ_CTRL_1, 0x8, 0x8);
+ break;
+
+ case RT5665_JD_NULL:
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ rt5665->hs_jack = hs_jack;
+
+ return 0;
+}
+
+static void rt5665_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5665_priv *rt5665 =
+ container_of(work, struct rt5665_priv, jack_detect_work.work);
+ int val, btn_type;
+
+ while (!rt5665->component) {
+ pr_debug("%s codec = null\n", __func__);
+ usleep_range(10000, 15000);
+ }
+
+ while (!snd_soc_card_is_instantiated(rt5665->component->card)) {
+ pr_debug("%s\n", __func__);
+ usleep_range(10000, 15000);
+ }
+
+ while (!rt5665->calibration_done) {
+ pr_debug("%s calibration not ready\n", __func__);
+ usleep_range(10000, 15000);
+ }
+
+ mutex_lock(&rt5665->calibrate_mutex);
+
+ val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010;
+ if (!val) {
+ /* jack in */
+ if (rt5665->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5665->jack_type =
+ rt5665_headset_detect(rt5665->component, 1);
+ } else {
+ /* jack is already in, report button event */
+ rt5665->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5665_button_detect(rt5665->component);
+ /**
+ * rt5665 can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5665->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5665->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5665->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5665->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ btn_type = 0;
+ dev_err(rt5665->component->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5665->jack_type = rt5665_headset_detect(rt5665->component, 0);
+ }
+
+ snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (rt5665->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5665->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5665->jd_check_work);
+
+ mutex_unlock(&rt5665->calibrate_mutex);
+}
+
+static const char * const rt5665_clk_sync[] = {
+ "I2S1_1", "I2S1_2", "I2S2", "I2S3", "IF2 Slave", "IF3 Slave"
+};
+
+static const struct soc_enum rt5665_enum[] = {
+ SOC_ENUM_SINGLE(RT5665_I2S1_SDP, 11, 5, rt5665_clk_sync),
+ SOC_ENUM_SINGLE(RT5665_I2S2_SDP, 11, 5, rt5665_clk_sync),
+ SOC_ENUM_SINGLE(RT5665_I2S3_SDP, 11, 5, rt5665_clk_sync),
+};
+
+static const struct snd_kcontrol_new rt5665_snd_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", RT5665_HPL_GAIN,
+ RT5665_HPR_GAIN, RT5665_G_HP_SFT, 15, 1, snd_soc_get_volsw,
+ rt5665_hp_vol_put, hp_vol_tlv),
+
+ /* Mono Output Volume */
+ SOC_SINGLE_EXT_TLV("Mono Playback Volume", RT5665_MONO_GAIN,
+ RT5665_L_VOL_SFT, 15, 1, snd_soc_get_volsw,
+ rt5665_mono_vol_put, mono_vol_tlv),
+
+ SOC_SINGLE_TLV("MONOVOL Playback Volume", RT5665_MONO_OUT,
+ RT5665_L_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* Output Volume */
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5665_LOUT, RT5665_L_VOL_SFT,
+ RT5665_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5665_DAC1_DIG_VOL,
+ RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 175, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5665_DAC2_DIG_VOL,
+ RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 175, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC2 Playback Switch", RT5665_DAC2_CTRL,
+ RT5665_M_DAC2_L_VOL_SFT, RT5665_M_DAC2_R_VOL_SFT, 1, 1),
+
+ /* IN1/IN2/IN3/IN4 Volume */
+ SOC_SINGLE_TLV("IN1 Boost Volume", RT5665_IN1_IN2,
+ RT5665_BST1_SFT, 69, 0, in_bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost Volume", RT5665_IN1_IN2,
+ RT5665_BST2_SFT, 69, 0, in_bst_tlv),
+ SOC_SINGLE_TLV("IN3 Boost Volume", RT5665_IN3_IN4,
+ RT5665_BST3_SFT, 69, 0, in_bst_tlv),
+ SOC_SINGLE_TLV("IN4 Boost Volume", RT5665_IN3_IN4,
+ RT5665_BST4_SFT, 69, 0, in_bst_tlv),
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5665_CBJ_BST_CTRL,
+ RT5665_BST_CBJ_SFT, 8, 0, bst_tlv),
+
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5665_INL1_INR1_VOL,
+ RT5665_INL_VOL_SFT, RT5665_INR_VOL_SFT, 31, 1, in_vol_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5665_STO1_ADC_DIG_VOL,
+ RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5665_STO1_ADC_DIG_VOL,
+ RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv),
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5665_MONO_ADC_DIG_VOL,
+ RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5665_MONO_ADC_DIG_VOL,
+ RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv),
+ SOC_DOUBLE("STO2 ADC Capture Switch", RT5665_STO2_ADC_DIG_VOL,
+ RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO2 ADC Capture Volume", RT5665_STO2_ADC_DIG_VOL,
+ RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5665_STO1_ADC_BOOST,
+ RT5665_STO1_ADC_L_BST_SFT, RT5665_STO1_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+
+ SOC_DOUBLE_TLV("Mono ADC Boost Gain Volume", RT5665_MONO_ADC_BOOST,
+ RT5665_MONO_ADC_L_BST_SFT, RT5665_MONO_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+
+ SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5665_STO2_ADC_BOOST,
+ RT5665_STO2_ADC_L_BST_SFT, RT5665_STO2_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+
+ /* I2S3 CLK Source */
+ SOC_ENUM("I2S1 Master Clk Sel", rt5665_enum[0]),
+ SOC_ENUM("I2S2 Master Clk Sel", rt5665_enum[1]),
+ SOC_ENUM("I2S3 Master Clk Sel", rt5665_enum[2]),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ int pd, idx;
+
+ pd = rl6231_get_pre_div(rt5665->regmap,
+ RT5665_ADDA_CLK_1, RT5665_I2S_PD1_SFT);
+ idx = rl6231_calc_dmic_clk(rt5665->sysclk / pd);
+
+ if (idx < 0)
+ dev_err(component->dev, "Failed to set DMIC clock\n");
+ else {
+ snd_soc_component_update_bits(component, RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_CLK_MASK, idx << RT5665_DMIC_CLK_SFT);
+ }
+ return idx;
+}
+
+static int rt5665_charge_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5665_HP_CHARGE_PUMP_1,
+ RT5665_PM_HP_MASK | RT5665_OSW_L_MASK,
+ RT5665_PM_HP_HV | RT5665_OSW_L_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5665_HP_CHARGE_PUMP_1,
+ RT5665_PM_HP_MASK | RT5665_OSW_L_MASK,
+ RT5665_PM_HP_LV | RT5665_OSW_L_DIS);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5665_GLB_CLK);
+ val &= RT5665_SCLK_SRC_MASK;
+ if (val == RT5665_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, shift, val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5665_ADC_MONO_R_ASRC_SFT:
+ reg = RT5665_ASRC_3;
+ shift = RT5665_AD_MONOR_CLK_SEL_SFT;
+ break;
+ case RT5665_ADC_MONO_L_ASRC_SFT:
+ reg = RT5665_ASRC_3;
+ shift = RT5665_AD_MONOL_CLK_SEL_SFT;
+ break;
+ case RT5665_ADC_STO1_ASRC_SFT:
+ reg = RT5665_ASRC_3;
+ shift = RT5665_AD_STO1_CLK_SEL_SFT;
+ break;
+ case RT5665_ADC_STO2_ASRC_SFT:
+ reg = RT5665_ASRC_3;
+ shift = RT5665_AD_STO2_CLK_SEL_SFT;
+ break;
+ case RT5665_DAC_MONO_R_ASRC_SFT:
+ reg = RT5665_ASRC_2;
+ shift = RT5665_DA_MONOR_CLK_SEL_SFT;
+ break;
+ case RT5665_DAC_MONO_L_ASRC_SFT:
+ reg = RT5665_ASRC_2;
+ shift = RT5665_DA_MONOL_CLK_SEL_SFT;
+ break;
+ case RT5665_DAC_STO1_ASRC_SFT:
+ reg = RT5665_ASRC_2;
+ shift = RT5665_DA_STO1_CLK_SEL_SFT;
+ break;
+ case RT5665_DAC_STO2_ASRC_SFT:
+ reg = RT5665_ASRC_2;
+ shift = RT5665_DA_STO2_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
+ switch (val) {
+ case RT5665_CLK_SEL_I2S1_ASRC:
+ case RT5665_CLK_SEL_I2S2_ASRC:
+ case RT5665_CLK_SEL_I2S3_ASRC:
+ /* I2S_Pre_Div1 should be 1 in asrc mode */
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
+ RT5665_I2S_PD1_MASK, RT5665_I2S_PD1_2);
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5665_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO1_ADC_MIXER,
+ RT5665_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO1_ADC_MIXER,
+ RT5665_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO1_ADC_MIXER,
+ RT5665_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO1_ADC_MIXER,
+ RT5665_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO2_ADC_MIXER,
+ RT5665_M_STO2_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO2_ADC_MIXER,
+ RT5665_M_STO2_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO2_ADC_MIXER,
+ RT5665_M_STO2_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO2_ADC_MIXER,
+ RT5665_M_STO2_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5665_MONO_ADC_MIXER,
+ RT5665_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5665_MONO_ADC_MIXER,
+ RT5665_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5665_MONO_ADC_MIXER,
+ RT5665_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5665_MONO_ADC_MIXER,
+ RT5665_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5665_AD_DA_MIXER,
+ RT5665_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5665_AD_DA_MIXER,
+ RT5665_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5665_AD_DA_MIXER,
+ RT5665_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5665_AD_DA_MIXER,
+ RT5665_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_R1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_L2_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_R2_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_R1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_L2_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO1_DAC_MIXER,
+ RT5665_M_DAC_R2_STO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto2_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO2_DAC_MIXER,
+ RT5665_M_DAC_L1_STO2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO2_DAC_MIXER,
+ RT5665_M_DAC_L2_STO2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L3 Switch", RT5665_STO2_DAC_MIXER,
+ RT5665_M_DAC_L3_STO2_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_sto2_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO2_DAC_MIXER,
+ RT5665_M_DAC_R1_STO2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO2_DAC_MIXER,
+ RT5665_M_DAC_R2_STO2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R3 Switch", RT5665_STO2_DAC_MIXER,
+ RT5665_M_DAC_R3_STO2_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_R1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_L1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_L2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_MONO_DAC_MIXER,
+ RT5665_M_DAC_R2_MONO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5665_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_CBJ_RM1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_INL_RM1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_INR_RM1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_BST4_RM1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_BST3_RM1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_BST2_RM1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC1_L2_MIXER,
+ RT5665_M_BST1_RM1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_rec1_r_mix[] = {
+ SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_REC1_R2_MIXER,
+ RT5665_M_AEC_REF_RM1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5665_REC1_R2_MIXER,
+ RT5665_M_INR_RM1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC1_R2_MIXER,
+ RT5665_M_BST4_RM1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC1_R2_MIXER,
+ RT5665_M_BST3_RM1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC1_R2_MIXER,
+ RT5665_M_BST2_RM1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC1_R2_MIXER,
+ RT5665_M_BST1_RM1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_rec2_l_mix[] = {
+ SOC_DAPM_SINGLE("INL Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_INL_RM2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_INR_RM2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("CBJ Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_CBJ_RM2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_BST4_RM2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_BST3_RM2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_BST2_RM2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC2_L2_MIXER,
+ RT5665_M_BST1_RM2_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_rec2_r_mix[] = {
+ SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_MONOVOL_RM2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_INL_RM2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_INR_RM2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_BST4_RM2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_BST3_RM2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_BST2_RM2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC2_R2_MIXER,
+ RT5665_M_BST1_RM2_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_monovol_mix[] = {
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_DAC_L2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIX2L Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_RECMIC2L_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_BST1_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_BST2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_BST3_MM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_out_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_OUT_L_MIXER,
+ RT5665_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5665_OUT_L_MIXER,
+ RT5665_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5665_OUT_L_MIXER,
+ RT5665_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_OUT_L_MIXER,
+ RT5665_M_BST2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_OUT_L_MIXER,
+ RT5665_M_BST3_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_out_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_OUT_R_MIXER,
+ RT5665_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5665_OUT_R_MIXER,
+ RT5665_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5665_OUT_R_MIXER,
+ RT5665_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5665_OUT_R_MIXER,
+ RT5665_M_BST3_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST4 Switch", RT5665_OUT_R_MIXER,
+ RT5665_M_BST4_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_mono_mix[] = {
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_DAC_L2_MA_SFT, 1, 1),
+ SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_MONOMIX_IN_GAIN,
+ RT5665_M_MONOVOL_MA_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_lout_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_LOUT_MIXER,
+ RT5665_M_DAC_L2_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5665_LOUT_MIXER,
+ RT5665_M_OV_L_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5665_lout_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_LOUT_MIXER,
+ RT5665_M_DAC_R2_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5665_LOUT_MIXER,
+ RT5665_M_OV_R_LM_SFT, 1, 1),
+};
+
+/*DAC L2, DAC R2*/
+/*MX-17 [6:4], MX-17 [2:0]*/
+static const char * const rt5665_dac2_src[] = {
+ "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "Mono ADC MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dac_l2_enum, RT5665_DAC2_CTRL,
+ RT5665_DAC_L2_SEL_SFT, rt5665_dac2_src);
+
+static const struct snd_kcontrol_new rt5665_dac_l2_mux =
+ SOC_DAPM_ENUM("Digital DAC L2 Source", rt5665_dac_l2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dac_r2_enum, RT5665_DAC2_CTRL,
+ RT5665_DAC_R2_SEL_SFT, rt5665_dac2_src);
+
+static const struct snd_kcontrol_new rt5665_dac_r2_mux =
+ SOC_DAPM_ENUM("Digital DAC R2 Source", rt5665_dac_r2_enum);
+
+/*DAC L3, DAC R3*/
+/*MX-1B [6:4], MX-1B [2:0]*/
+static const char * const rt5665_dac3_src[] = {
+ "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "STO2 ADC MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dac_l3_enum, RT5665_DAC3_CTRL,
+ RT5665_DAC_L3_SEL_SFT, rt5665_dac3_src);
+
+static const struct snd_kcontrol_new rt5665_dac_l3_mux =
+ SOC_DAPM_ENUM("Digital DAC L3 Source", rt5665_dac_l3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dac_r3_enum, RT5665_DAC3_CTRL,
+ RT5665_DAC_R3_SEL_SFT, rt5665_dac3_src);
+
+static const struct snd_kcontrol_new rt5665_dac_r3_mux =
+ SOC_DAPM_ENUM("Digital DAC R3 Source", rt5665_dac_r3_enum);
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5665_sto1_adc1_src[] = {
+ "DD Mux", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_adc1l_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_ADC1L_SRC_SFT, rt5665_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_adc1r_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_ADC1R_SRC_SFT, rt5665_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5665_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_adcl_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_ADCL_SRC_SFT, rt5665_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5665_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_adcr_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_ADCR_SRC_SFT, rt5665_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5665_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5665_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_adc2l_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_ADC2L_SRC_SFT, rt5665_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5665_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_adc2r_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_ADC2R_SRC_SFT, rt5665_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5665_sto1_adc2r_enum);
+
+/* STO1 DMIC Source */
+/* MX-26 [8] */
+static const char * const rt5665_sto1_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_dmic_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_DMIC_SRC_SFT, rt5665_sto1_dmic_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC Mux", rt5665_sto1_dmic_enum);
+
+/* MX-26 [9] */
+static const char * const rt5665_sto1_dd_l_src[] = {
+ "STO2 DAC", "MONO DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_dd_l_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_DD_L_SRC_SFT, rt5665_sto1_dd_l_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_dd_l_mux =
+ SOC_DAPM_ENUM("Stereo1 DD L Source", rt5665_sto1_dd_l_enum);
+
+/* MX-26 [1:0] */
+static const char * const rt5665_sto1_dd_r_src[] = {
+ "STO2 DAC", "MONO DAC", "AEC REF"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto1_dd_r_enum, RT5665_STO1_ADC_MIXER,
+ RT5665_STO1_DD_R_SRC_SFT, rt5665_sto1_dd_r_src);
+
+static const struct snd_kcontrol_new rt5665_sto1_dd_r_mux =
+ SOC_DAPM_ENUM("Stereo1 DD R Source", rt5665_sto1_dd_r_enum);
+
+/* MONO ADC L2 Source */
+/* MX-27 [12] */
+static const char * const rt5665_mono_adc_l2_src[] = {
+ "DAC MIXL", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_adc_l2_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_ADC_L2_SRC_SFT, rt5665_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5665_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC L2 Source", rt5665_mono_adc_l2_enum);
+
+
+/* MONO ADC L1 Source */
+/* MX-27 [13] */
+static const char * const rt5665_mono_adc_l1_src[] = {
+ "DD Mux", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_adc_l1_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_ADC_L1_SRC_SFT, rt5665_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5665_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC L1 Source", rt5665_mono_adc_l1_enum);
+
+/* MX-27 [9][1]*/
+static const char * const rt5665_mono_dd_src[] = {
+ "STO2 DAC", "MONO DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_dd_l_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_DD_L_SRC_SFT, rt5665_mono_dd_src);
+
+static const struct snd_kcontrol_new rt5665_mono_dd_l_mux =
+ SOC_DAPM_ENUM("Mono DD L Source", rt5665_mono_dd_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_dd_r_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_DD_R_SRC_SFT, rt5665_mono_dd_src);
+
+static const struct snd_kcontrol_new rt5665_mono_dd_r_mux =
+ SOC_DAPM_ENUM("Mono DD R Source", rt5665_mono_dd_r_enum);
+
+/* MONO ADC L Source, MONO ADC R Source*/
+/* MX-27 [11:10], MX-27 [3:2] */
+static const char * const rt5665_mono_adc_src[] = {
+ "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_adc_l_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_ADC_L_SRC_SFT, rt5665_mono_adc_src);
+
+static const struct snd_kcontrol_new rt5665_mono_adc_l_mux =
+ SOC_DAPM_ENUM("Mono ADC L Source", rt5665_mono_adc_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_adcr_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_ADC_R_SRC_SFT, rt5665_mono_adc_src);
+
+static const struct snd_kcontrol_new rt5665_mono_adc_r_mux =
+ SOC_DAPM_ENUM("Mono ADC R Source", rt5665_mono_adcr_enum);
+
+/* MONO DMIC L Source */
+/* MX-27 [8] */
+static const char * const rt5665_mono_dmic_l_src[] = {
+ "DMIC1 L", "DMIC2 L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_dmic_l_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_DMIC_L_SRC_SFT, rt5665_mono_dmic_l_src);
+
+static const struct snd_kcontrol_new rt5665_mono_dmic_l_mux =
+ SOC_DAPM_ENUM("Mono DMIC L Source", rt5665_mono_dmic_l_enum);
+
+/* MONO ADC R2 Source */
+/* MX-27 [4] */
+static const char * const rt5665_mono_adc_r2_src[] = {
+ "DAC MIXR", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_adc_r2_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_ADC_R2_SRC_SFT, rt5665_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5665_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC R2 Source", rt5665_mono_adc_r2_enum);
+
+/* MONO ADC R1 Source */
+/* MX-27 [5] */
+static const char * const rt5665_mono_adc_r1_src[] = {
+ "DD Mux", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_adc_r1_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_ADC_R1_SRC_SFT, rt5665_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5665_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC R1 Source", rt5665_mono_adc_r1_enum);
+
+/* MONO DMIC R Source */
+/* MX-27 [0] */
+static const char * const rt5665_mono_dmic_r_src[] = {
+ "DMIC1 R", "DMIC2 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_mono_dmic_r_enum, RT5665_MONO_ADC_MIXER,
+ RT5665_MONO_DMIC_R_SRC_SFT, rt5665_mono_dmic_r_src);
+
+static const struct snd_kcontrol_new rt5665_mono_dmic_r_mux =
+ SOC_DAPM_ENUM("Mono DMIC R Source", rt5665_mono_dmic_r_enum);
+
+
+/* STO2 ADC1 Source */
+/* MX-28 [13] [5] */
+static const char * const rt5665_sto2_adc1_src[] = {
+ "DD Mux", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_adc1l_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_ADC1L_SRC_SFT, rt5665_sto2_adc1_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_adc1r_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_ADC1R_SRC_SFT, rt5665_sto2_adc1_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1r_enum);
+
+/* STO2 ADC Source */
+/* MX-28 [11:10] [3:2] */
+static const char * const rt5665_sto2_adc_src[] = {
+ "ADC1 L", "ADC1 R", "ADC2 L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_adcl_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_ADCL_SRC_SFT, rt5665_sto2_adc_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_adcl_mux =
+ SOC_DAPM_ENUM("Stereo2 ADCL Source", rt5665_sto2_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_adcr_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_ADCR_SRC_SFT, rt5665_sto2_adc_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_adcr_mux =
+ SOC_DAPM_ENUM("Stereo2 ADCR Source", rt5665_sto2_adcr_enum);
+
+/* STO2 ADC2 Source */
+/* MX-28 [12] [4] */
+static const char * const rt5665_sto2_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_adc2l_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_ADC2L_SRC_SFT, rt5665_sto2_adc2_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC2L Source", rt5665_sto2_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_adc2r_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_ADC2R_SRC_SFT, rt5665_sto2_adc2_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC2R Source", rt5665_sto2_adc2r_enum);
+
+/* STO2 DMIC Source */
+/* MX-28 [8] */
+static const char * const rt5665_sto2_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_dmic_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_DMIC_SRC_SFT, rt5665_sto2_dmic_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_dmic_mux =
+ SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5665_sto2_dmic_enum);
+
+/* MX-28 [9] */
+static const char * const rt5665_sto2_dd_l_src[] = {
+ "STO2 DAC", "MONO DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_dd_l_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_DD_L_SRC_SFT, rt5665_sto2_dd_l_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_dd_l_mux =
+ SOC_DAPM_ENUM("Stereo2 DD L Source", rt5665_sto2_dd_l_enum);
+
+/* MX-28 [1] */
+static const char * const rt5665_sto2_dd_r_src[] = {
+ "STO2 DAC", "MONO DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_sto2_dd_r_enum, RT5665_STO2_ADC_MIXER,
+ RT5665_STO2_DD_R_SRC_SFT, rt5665_sto2_dd_r_src);
+
+static const struct snd_kcontrol_new rt5665_sto2_dd_r_mux =
+ SOC_DAPM_ENUM("Stereo2 DD R Source", rt5665_sto2_dd_r_enum);
+
+/* DAC R1 Source, DAC L1 Source*/
+/* MX-29 [11:10], MX-29 [9:8]*/
+static const char * const rt5665_dac1_src[] = {
+ "IF1 DAC1", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dac_r1_enum, RT5665_AD_DA_MIXER,
+ RT5665_DAC1_R_SEL_SFT, rt5665_dac1_src);
+
+static const struct snd_kcontrol_new rt5665_dac_r1_mux =
+ SOC_DAPM_ENUM("DAC R1 Source", rt5665_dac_r1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dac_l1_enum, RT5665_AD_DA_MIXER,
+ RT5665_DAC1_L_SEL_SFT, rt5665_dac1_src);
+
+static const struct snd_kcontrol_new rt5665_dac_l1_mux =
+ SOC_DAPM_ENUM("DAC L1 Source", rt5665_dac_l1_enum);
+
+/* DAC Digital Mixer L Source, DAC Digital Mixer R Source*/
+/* MX-2D [13:12], MX-2D [9:8]*/
+static const char * const rt5665_dig_dac_mix_src[] = {
+ "Stereo1 DAC Mixer", "Stereo2 DAC Mixer", "Mono DAC Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dig_dac_mixl_enum, RT5665_A_DAC1_MUX,
+ RT5665_DAC_MIX_L_SFT, rt5665_dig_dac_mix_src);
+
+static const struct snd_kcontrol_new rt5665_dig_dac_mixl_mux =
+ SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5665_dig_dac_mixl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_dig_dac_mixr_enum, RT5665_A_DAC1_MUX,
+ RT5665_DAC_MIX_R_SFT, rt5665_dig_dac_mix_src);
+
+static const struct snd_kcontrol_new rt5665_dig_dac_mixr_mux =
+ SOC_DAPM_ENUM("DAC Digital Mixer R Source", rt5665_dig_dac_mixr_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2D [5:4], MX-2D [1:0]*/
+static const char * const rt5665_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1", "DMIC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_alg_dac_l1_enum, RT5665_A_DAC1_MUX,
+ RT5665_A_DACL1_SFT, rt5665_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5665_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5665_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_alg_dac_r1_enum, RT5665_A_DAC1_MUX,
+ RT5665_A_DACR1_SFT, rt5665_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5665_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5665_alg_dac_r1_enum);
+
+/* Analog DAC LR Source, Analog DAC R2 Source*/
+/* MX-2E [5:4], MX-2E [0]*/
+static const char * const rt5665_alg_dac2_src[] = {
+ "Mono DAC Mixer", "DAC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_alg_dac_l2_enum, RT5665_A_DAC2_MUX,
+ RT5665_A_DACL2_SFT, rt5665_alg_dac2_src);
+
+static const struct snd_kcontrol_new rt5665_alg_dac_l2_mux =
+ SOC_DAPM_ENUM("Analog DAC L2 Source", rt5665_alg_dac_l2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_alg_dac_r2_enum, RT5665_A_DAC2_MUX,
+ RT5665_A_DACR2_SFT, rt5665_alg_dac2_src);
+
+static const struct snd_kcontrol_new rt5665_alg_dac_r2_mux =
+ SOC_DAPM_ENUM("Analog DAC R2 Source", rt5665_alg_dac_r2_enum);
+
+/* Interface2 ADC Data Input*/
+/* MX-2F [14:12] */
+static const char * const rt5665_if2_1_adc_in_src[] = {
+ "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1",
+ "IF1 DAC2", "IF2_2 DAC", "IF3 DAC", "DAC1 MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA,
+ RT5665_IF2_1_ADC_IN_SFT, rt5665_if2_1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5665_if2_1_adc_in_mux =
+ SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_1_adc_in_enum);
+
+/* MX-2F [6:4] */
+static const char * const rt5665_if2_2_adc_in_src[] = {
+ "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1",
+ "IF1 DAC2", "IF2_1 DAC", "IF3 DAC", "DAC1 MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if2_2_adc_in_enum, RT5665_DIG_INF2_DATA,
+ RT5665_IF2_2_ADC_IN_SFT, rt5665_if2_2_adc_in_src);
+
+static const struct snd_kcontrol_new rt5665_if2_2_adc_in_mux =
+ SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_2_adc_in_enum);
+
+/* Interface3 ADC Data Input*/
+/* MX-30 [6:4] */
+static const char * const rt5665_if3_adc_in_src[] = {
+ "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1",
+ "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "DAC1 MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if3_adc_in_enum, RT5665_DIG_INF3_DATA,
+ RT5665_IF3_ADC_IN_SFT, rt5665_if3_adc_in_src);
+
+static const struct snd_kcontrol_new rt5665_if3_adc_in_mux =
+ SOC_DAPM_ENUM("IF3 ADC IN Source", rt5665_if3_adc_in_enum);
+
+/* PDM 1 L/R*/
+/* MX-31 [11:10] [9:8] */
+static const char * const rt5665_pdm_src[] = {
+ "Stereo1 DAC", "Stereo2 DAC", "Mono DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_pdm_l_enum, RT5665_PDM_OUT_CTRL,
+ RT5665_PDM1_L_SFT, rt5665_pdm_src);
+
+static const struct snd_kcontrol_new rt5665_pdm_l_mux =
+ SOC_DAPM_ENUM("PDM L Source", rt5665_pdm_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_pdm_r_enum, RT5665_PDM_OUT_CTRL,
+ RT5665_PDM1_R_SFT, rt5665_pdm_src);
+
+static const struct snd_kcontrol_new rt5665_pdm_r_mux =
+ SOC_DAPM_ENUM("PDM R Source", rt5665_pdm_r_enum);
+
+
+/* I2S1 TDM ADCDAT Source */
+/* MX-7a[10] */
+static const char * const rt5665_if1_1_adc1_data_src[] = {
+ "STO1 ADC", "IF2_1 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_1_adc1_data_enum, RT5665_TDM_CTRL_3,
+ RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_1_adc1_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_1_adc1_mux =
+ SOC_DAPM_ENUM("IF1_1 ADC1 Source", rt5665_if1_1_adc1_data_enum);
+
+/* MX-7a[9] */
+static const char * const rt5665_if1_1_adc2_data_src[] = {
+ "STO2 ADC", "IF2_2 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_1_adc2_data_enum, RT5665_TDM_CTRL_3,
+ RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_1_adc2_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_1_adc2_mux =
+ SOC_DAPM_ENUM("IF1_1 ADC2 Source", rt5665_if1_1_adc2_data_enum);
+
+/* MX-7a[8] */
+static const char * const rt5665_if1_1_adc3_data_src[] = {
+ "MONO ADC", "IF3 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_1_adc3_data_enum, RT5665_TDM_CTRL_3,
+ RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_1_adc3_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_1_adc3_mux =
+ SOC_DAPM_ENUM("IF1_1 ADC3 Source", rt5665_if1_1_adc3_data_enum);
+
+/* MX-7b[10] */
+static const char * const rt5665_if1_2_adc1_data_src[] = {
+ "STO1 ADC", "IF1 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_2_adc1_data_enum, RT5665_TDM_CTRL_4,
+ RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_2_adc1_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_2_adc1_mux =
+ SOC_DAPM_ENUM("IF1_2 ADC1 Source", rt5665_if1_2_adc1_data_enum);
+
+/* MX-7b[9] */
+static const char * const rt5665_if1_2_adc2_data_src[] = {
+ "STO2 ADC", "IF2_1 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_2_adc2_data_enum, RT5665_TDM_CTRL_4,
+ RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_2_adc2_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_2_adc2_mux =
+ SOC_DAPM_ENUM("IF1_2 ADC2 Source", rt5665_if1_2_adc2_data_enum);
+
+/* MX-7b[8] */
+static const char * const rt5665_if1_2_adc3_data_src[] = {
+ "MONO ADC", "IF2_2 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_2_adc3_data_enum, RT5665_TDM_CTRL_4,
+ RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_2_adc3_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_2_adc3_mux =
+ SOC_DAPM_ENUM("IF1_2 ADC3 Source", rt5665_if1_2_adc3_data_enum);
+
+/* MX-7b[7] */
+static const char * const rt5665_if1_2_adc4_data_src[] = {
+ "DAC1", "IF3 DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_if1_2_adc4_data_enum, RT5665_TDM_CTRL_4,
+ RT5665_IF1_ADC4_SEL_SFT, rt5665_if1_2_adc4_data_src);
+
+static const struct snd_kcontrol_new rt5665_if1_2_adc4_mux =
+ SOC_DAPM_ENUM("IF1_2 ADC4 Source", rt5665_if1_2_adc4_data_enum);
+
+/* MX-7a[4:0] MX-7b[4:0] */
+static const char * const rt5665_tdm_adc_data_src[] = {
+ "1234", "1243", "1324", "1342", "1432", "1423",
+ "2134", "2143", "2314", "2341", "2431", "2413",
+ "3124", "3142", "3214", "3241", "3412", "3421",
+ "4123", "4132", "4213", "4231", "4312", "4321"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_tdm1_adc_data_enum, RT5665_TDM_CTRL_3,
+ RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src);
+
+static const struct snd_kcontrol_new rt5665_tdm1_adc_mux =
+ SOC_DAPM_ENUM("TDM1 ADC Mux", rt5665_tdm1_adc_data_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5665_tdm2_adc_data_enum, RT5665_TDM_CTRL_4,
+ RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src);
+
+static const struct snd_kcontrol_new rt5665_tdm2_adc_mux =
+ SOC_DAPM_ENUM("TDM2 ADCDAT Source", rt5665_tdm2_adc_data_enum);
+
+/* Out Volume Switch */
+static const struct snd_kcontrol_new monovol_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_MONO_OUT, RT5665_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new outvol_l_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new outvol_r_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_VOL_R_SFT, 1, 1);
+
+/* Out Switch */
+static const struct snd_kcontrol_new mono_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_MONO_OUT, RT5665_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpo_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5665_HP_CTRL_2,
+ RT5665_VOL_L_SFT, 1, 0);
+
+static const struct snd_kcontrol_new lout_l_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_r_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new pdm_l_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_PDM_OUT_CTRL,
+ RT5665_M_PDM1_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new pdm_r_switch =
+ SOC_DAPM_SINGLE("Switch", RT5665_PDM_OUT_CTRL,
+ RT5665_M_PDM1_R_SFT, 1, 1);
+
+static int rt5665_mono_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_EN);
+ snd_soc_component_update_bits(component, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40,
+ 0x0);
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x10, 0x10);
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x20, 0x20);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x20, 0);
+ snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x10, 0);
+ snd_soc_component_update_bits(component, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40,
+ 0x40);
+ snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int rt5665_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_EN);
+ snd_soc_component_write(component, RT5665_HP_LOGIC_CTRL_2, 0x0003);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write(component, RT5665_HP_LOGIC_CTRL_2, 0x0002);
+ snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1,
+ RT5665_NG2_EN_MASK, RT5665_NG2_DIS);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int rt5665_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5665_DEPOP_1,
+ RT5665_PUMP_EN, RT5665_PUMP_EN);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5665_DEPOP_1,
+ RT5665_PUMP_EN, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int set_dmic_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*Add delay to avoid pop noise*/
+ msleep(150);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5665_set_verf(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case RT5665_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
+ RT5665_PWR_FV1, 0);
+ break;
+
+ case RT5665_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
+ RT5665_PWR_FV2, 0);
+ break;
+
+ case RT5665_PWR_VREF3_BIT:
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
+ RT5665_PWR_FV3, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(15000, 20000);
+ switch (w->shift) {
+ case RT5665_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
+ RT5665_PWR_FV1, RT5665_PWR_FV1);
+ break;
+
+ case RT5665_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
+ RT5665_PWR_FV2, RT5665_PWR_FV2);
+ break;
+
+ case RT5665_PWR_VREF3_BIT:
+ snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1,
+ RT5665_PWR_FV3, RT5665_PWR_FV3);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int val1, val2, mask1 = 0, mask2 = 0;
+
+ switch (w->shift) {
+ case RT5665_PWR_I2S2_1_BIT:
+ mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+ RT5665_GP4_PIN_MASK | RT5665_GP5_PIN_MASK;
+ val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+ RT5665_GP4_PIN_DACDAT2_1 | RT5665_GP5_PIN_ADCDAT2_1;
+ break;
+ case RT5665_PWR_I2S2_2_BIT:
+ mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+ RT5665_GP8_PIN_MASK;
+ val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+ RT5665_GP8_PIN_DACDAT2_2;
+ mask2 = RT5665_GP9_PIN_MASK;
+ val2 = RT5665_GP9_PIN_ADCDAT2_2;
+ break;
+ case RT5665_PWR_I2S3_BIT:
+ mask1 = RT5665_GP6_PIN_MASK | RT5665_GP7_PIN_MASK |
+ RT5665_GP8_PIN_MASK;
+ val1 = RT5665_GP6_PIN_BCLK3 | RT5665_GP7_PIN_LRCK3 |
+ RT5665_GP8_PIN_DACDAT3;
+ mask2 = RT5665_GP9_PIN_MASK;
+ val2 = RT5665_GP9_PIN_ADCDAT3;
+ break;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (mask1)
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_1,
+ mask1, val1);
+ if (mask2)
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_2,
+ mask2, val2);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (mask1)
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_1,
+ mask1, 0);
+ if (mask2)
+ snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_2,
+ mask2, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL", RT5665_PWR_ANLG_3, RT5665_PWR_PLL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5665_PWR_VOL,
+ RT5665_PWR_MIC_DET_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref1", RT5665_PWR_ANLG_1, RT5665_PWR_VREF1_BIT, 0,
+ rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref2", RT5665_PWR_ANLG_1, RT5665_PWR_VREF2_BIT, 0,
+ rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref3", RT5665_PWR_ANLG_1, RT5665_PWR_VREF3_BIT, 0,
+ rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5665_ASRC_1,
+ RT5665_I2S1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5665_ASRC_1,
+ RT5665_I2S2_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5665_ASRC_1,
+ RT5665_I2S3_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5665_ASRC_1,
+ RT5665_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO2 ASRC", 1, RT5665_ASRC_1,
+ RT5665_DAC_STO2_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC Mono L ASRC", 1, RT5665_ASRC_1,
+ RT5665_DAC_MONO_L_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC Mono R ASRC", 1, RT5665_ASRC_1,
+ RT5665_DAC_MONO_R_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5665_ASRC_1,
+ RT5665_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5665_ASRC_1,
+ RT5665_ADC_STO2_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC Mono L ASRC", 1, RT5665_ASRC_1,
+ RT5665_ADC_MONO_L_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC Mono R ASRC", 1, RT5665_ASRC_1,
+ RT5665_ADC_MONO_R_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5665_ASRC_1,
+ RT5665_DMIC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5665_ASRC_1,
+ RT5665_DMIC_STO2_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5665_ASRC_1,
+ RT5665_DMIC_MONO_L_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5665_ASRC_1,
+ RT5665_DMIC_MONO_R_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5665_PWR_ANLG_2, RT5665_PWR_MB1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5665_PWR_ANLG_2, RT5665_PWR_MB2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS3", RT5665_PWR_ANLG_2, RT5665_PWR_MB3_BIT,
+ 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+ SND_SOC_DAPM_INPUT("DMIC L2"),
+ SND_SOC_DAPM_INPUT("DMIC R2"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_INPUT("IN3P"),
+ SND_SOC_DAPM_INPUT("IN3N"),
+ SND_SOC_DAPM_INPUT("IN4P"),
+ SND_SOC_DAPM_INPUT("IN4N"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_2_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST3", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST4", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST1 Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST2 Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST3 Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST3_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST4 Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST4_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST1P Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST1_P_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST2P Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST2_P_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST3P Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST3_P_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BST4P Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_BST4_P_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CBJ Power", RT5665_PWR_ANLG_3,
+ RT5665_PWR_CBJ_BIT, 0, NULL, 0),
+
+
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5665_PWR_VOL, RT5665_PWR_IN_L_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5665_PWR_VOL, RT5665_PWR_IN_R_BIT,
+ 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5665_rec1_l_mix,
+ ARRAY_SIZE(rt5665_rec1_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5665_rec1_r_mix,
+ ARRAY_SIZE(rt5665_rec1_r_mix)),
+ SND_SOC_DAPM_MIXER("RECMIX2L", SND_SOC_NOPM, 0, 0, rt5665_rec2_l_mix,
+ ARRAY_SIZE(rt5665_rec2_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIX2R", SND_SOC_NOPM, 0, 0, rt5665_rec2_r_mix,
+ ARRAY_SIZE(rt5665_rec2_r_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_RM1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5665_PWR_ANLG_2,
+ RT5665_PWR_RM1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RECMIX2L Power", RT5665_PWR_MIXER,
+ RT5665_PWR_RM2_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RECMIX2R Power", RT5665_PWR_MIXER,
+ RT5665_PWR_RM2_R_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5665_PWR_DIG_1,
+ RT5665_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5665_PWR_DIG_1,
+ RT5665_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5665_PWR_DIG_1,
+ RT5665_PWR_ADC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5665_PWR_DIG_1,
+ RT5665_PWR_ADC_R2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5665_CHOP_ADC,
+ RT5665_CKGEN_ADC1_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2 clock", RT5665_CHOP_ADC,
+ RT5665_CKGEN_ADC2_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo1 DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("Stereo1 DD L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_dd_l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 DD R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto1_dd_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_adc_r2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_dmic_l_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_dmic_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_adc_l_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_adc_r_mux),
+ SND_SOC_DAPM_MUX("Mono DD L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_dd_l_mux),
+ SND_SOC_DAPM_MUX("Mono DD R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_mono_dd_r_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_adcr_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DD L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_dd_l_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DD R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_sto2_dd_r_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_ADC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_ADC_S2F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5665_STO1_ADC_DIG_VOL,
+ RT5665_L_MUTE_SFT, 1, rt5665_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5665_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5665_STO1_ADC_DIG_VOL,
+ RT5665_R_MUTE_SFT, 1, rt5665_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5665_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", RT5665_STO2_ADC_DIG_VOL,
+ RT5665_L_MUTE_SFT, 1, rt5665_sto2_adc_l_mix,
+ ARRAY_SIZE(rt5665_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", RT5665_STO2_ADC_DIG_VOL,
+ RT5665_R_MUTE_SFT, 1, rt5665_sto2_adc_r_mix,
+ ARRAY_SIZE(rt5665_sto2_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5665_MONO_ADC_DIG_VOL,
+ RT5665_L_MUTE_SFT, 1, rt5665_mono_adc_l_mix,
+ ARRAY_SIZE(rt5665_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5665_MONO_ADC_DIG_VOL,
+ RT5665_R_MUTE_SFT, 1, rt5665_mono_adc_r_mix,
+ ARRAY_SIZE(rt5665_mono_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT,
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT,
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT,
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC3 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC3 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("IF2_1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2_2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1_1_ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_adc1_mux),
+ SND_SOC_DAPM_MUX("IF1_1_ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_adc2_mux),
+ SND_SOC_DAPM_MUX("IF1_1_ADC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_adc3_mux),
+ SND_SOC_DAPM_PGA("IF1_1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("IF1_2_ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_adc1_mux),
+ SND_SOC_DAPM_MUX("IF1_2_ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_adc2_mux),
+ SND_SOC_DAPM_MUX("IF1_2_ADC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_adc3_mux),
+ SND_SOC_DAPM_MUX("IF1_2_ADC4 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_adc4_mux),
+ SND_SOC_DAPM_MUX("TDM1 slot 01 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm1_adc_mux),
+ SND_SOC_DAPM_MUX("TDM1 slot 23 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm1_adc_mux),
+ SND_SOC_DAPM_MUX("TDM1 slot 45 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm1_adc_mux),
+ SND_SOC_DAPM_MUX("TDM1 slot 67 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm1_adc_mux),
+ SND_SOC_DAPM_MUX("TDM2 slot 01 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm2_adc_mux),
+ SND_SOC_DAPM_MUX("TDM2 slot 23 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm2_adc_mux),
+ SND_SOC_DAPM_MUX("TDM2 slot 45 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm2_adc_mux),
+ SND_SOC_DAPM_MUX("TDM2 slot 67 Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_tdm2_adc_mux),
+ SND_SOC_DAPM_MUX("IF2_1 ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if2_1_adc_in_mux),
+ SND_SOC_DAPM_MUX("IF2_2 ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if2_2_adc_in_mux),
+ SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if3_adc_in_mux),
+ SND_SOC_DAPM_MUX("IF1_1 0 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 1 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 3 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 4 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 5 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 6 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_1 7 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 0 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 1 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 3 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 4 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 5 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 6 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1_2 7 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if1_2_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2_1 DAC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if2_1_dac_swap_mux),
+ SND_SOC_DAPM_MUX("IF2_1 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if2_1_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2_2 DAC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if2_2_dac_swap_mux),
+ SND_SOC_DAPM_MUX("IF2_2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if2_2_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF3 DAC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if3_dac_swap_mux),
+ SND_SOC_DAPM_MUX("IF3 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5665_if3_adc_swap_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 0", "AIF1_1 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 1", "AIF1_1 Capture",
+ 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 2", "AIF1_1 Capture",
+ 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 3", "AIF1_1 Capture",
+ 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 4", "AIF1_1 Capture",
+ 4, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 5", "AIF1_1 Capture",
+ 5, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 6", "AIF1_1 Capture",
+ 6, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 7", "AIF1_1 Capture",
+ 7, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 0", "AIF1_2 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 1", "AIF1_2 Capture",
+ 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 2", "AIF1_2 Capture",
+ 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 3", "AIF1_2 Capture",
+ 3, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 4", "AIF1_2 Capture",
+ 4, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 5", "AIF1_2 Capture",
+ 5, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 6", "AIF1_2 Capture",
+ 6, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 7", "AIF1_2 Capture",
+ 7, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2_1TX", "AIF2_1 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2_2TX", "AIF2_2 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2_1RX", "AIF2_1 Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2_2RX", "AIF2_2 Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5665_dac_l_mix, ARRAY_SIZE(rt5665_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5665_dac_r_mix, ARRAY_SIZE(rt5665_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r1_mux),
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r2_mux),
+ SND_SOC_DAPM_MUX("DAC L3 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l3_mux),
+ SND_SOC_DAPM_MUX("DAC R3 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r3_mux),
+
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5665_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5665_alg_dac_r1_mux),
+ SND_SOC_DAPM_MUX("DAC L2 Source", SND_SOC_NOPM, 0, 0,
+ &rt5665_alg_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Source", SND_SOC_NOPM, 0, 0,
+ &rt5665_alg_dac_r2_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Stereo2 Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_DAC_S2F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_DAC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5665_PWR_DIG_2,
+ RT5665_PWR_DAC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5665_sto1_dac_l_mix, ARRAY_SIZE(rt5665_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5665_sto1_dac_r_mix, ARRAY_SIZE(rt5665_sto1_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Stereo2 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5665_sto2_dac_l_mix, ARRAY_SIZE(rt5665_sto2_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo2 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5665_sto2_dac_r_mix, ARRAY_SIZE(rt5665_sto2_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5665_mono_dac_l_mix, ARRAY_SIZE(rt5665_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5665_mono_dac_r_mix, ARRAY_SIZE(rt5665_mono_dac_r_mix)),
+ SND_SOC_DAPM_MUX("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ &rt5665_dig_dac_mixl_mux),
+ SND_SOC_DAPM_MUX("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ &rt5665_dig_dac_mixr_mux),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5665_PWR_DIG_1,
+ RT5665_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5665_PWR_DIG_1,
+ RT5665_PWR_DAC_R2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("DAC1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 1, RT5665_CHOP_DAC,
+ RT5665_CKGEN_DAC1_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC 2 Clock", 1, RT5665_CHOP_DAC,
+ RT5665_CKGEN_DAC2_SFT, 0, NULL, 0),
+
+ /* OUT Mixer */
+ SND_SOC_DAPM_MIXER("MONOVOL MIX", RT5665_PWR_MIXER, RT5665_PWR_MM_BIT,
+ 0, rt5665_monovol_mix, ARRAY_SIZE(rt5665_monovol_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5665_PWR_MIXER, RT5665_PWR_OM_L_BIT,
+ 0, rt5665_out_l_mix, ARRAY_SIZE(rt5665_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5665_PWR_MIXER, RT5665_PWR_OM_R_BIT,
+ 0, rt5665_out_r_mix, ARRAY_SIZE(rt5665_out_r_mix)),
+
+ /* Output Volume */
+ SND_SOC_DAPM_SWITCH("MONOVOL", RT5665_PWR_VOL, RT5665_PWR_MV_BIT, 0,
+ &monovol_switch),
+ SND_SOC_DAPM_SWITCH("OUTVOL L", RT5665_PWR_VOL, RT5665_PWR_OV_L_BIT, 0,
+ &outvol_l_switch),
+ SND_SOC_DAPM_SWITCH("OUTVOL R", RT5665_PWR_VOL, RT5665_PWR_OV_R_BIT, 0,
+ &outvol_r_switch),
+
+ /* MONO/HPO/LOUT */
+ SND_SOC_DAPM_MIXER("Mono MIX", SND_SOC_NOPM, 0, 0, rt5665_mono_mix,
+ ARRAY_SIZE(rt5665_mono_mix)),
+ SND_SOC_DAPM_MIXER("LOUT L MIX", SND_SOC_NOPM, 0, 0, rt5665_lout_l_mix,
+ ARRAY_SIZE(rt5665_lout_l_mix)),
+ SND_SOC_DAPM_MIXER("LOUT R MIX", SND_SOC_NOPM, 0, 0, rt5665_lout_r_mix,
+ ARRAY_SIZE(rt5665_lout_r_mix)),
+ SND_SOC_DAPM_PGA_S("Mono Amp", 1, RT5665_PWR_ANLG_1, RT5665_PWR_MA_BIT,
+ 0, rt5665_mono_event, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5665_hp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT Amp", 1, RT5665_PWR_ANLG_1,
+ RT5665_PWR_LM_BIT, 0, rt5665_lout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0,
+ rt5665_charge_pump_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SWITCH("Mono Playback", SND_SOC_NOPM, 0, 0,
+ &mono_switch),
+ SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0,
+ &hpo_switch),
+ SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0,
+ &lout_l_switch),
+ SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0,
+ &lout_r_switch),
+ SND_SOC_DAPM_SWITCH("PDM L Playback", SND_SOC_NOPM, 0, 0,
+ &pdm_l_switch),
+ SND_SOC_DAPM_SWITCH("PDM R Playback", SND_SOC_NOPM, 0, 0,
+ &pdm_r_switch),
+
+ /* PDM */
+ SND_SOC_DAPM_SUPPLY("PDM Power", RT5665_PWR_DIG_2,
+ RT5665_PWR_PDM1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("PDM L Mux", SND_SOC_NOPM,
+ 0, 1, &rt5665_pdm_l_mux),
+ SND_SOC_DAPM_MUX("PDM R Mux", SND_SOC_NOPM,
+ 0, 1, &rt5665_pdm_r_mux),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5665_CLK_DET, RT5665_SYS_CLK_DET,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET HP", RT5665_CLK_DET, RT5665_HP_CLK_DET,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET MONO", RT5665_CLK_DET, RT5665_MONO_CLK_DET,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET LOUT", RT5665_CLK_DET, RT5665_LOUT_CLK_DET,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET", RT5665_CLK_DET, RT5665_POW_CLK_DET,
+ 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+ SND_SOC_DAPM_OUTPUT("PDML"),
+ SND_SOC_DAPM_OUTPUT("PDMR"),
+};
+
+static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"ADC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"ADC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"ADC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"DAC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"DAC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"DAC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll},
+ {"DAC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc},
+ {"ADC Mono Left Filter", NULL, "ADC Mono L ASRC", is_using_asrc},
+ {"ADC Mono Right Filter", NULL, "ADC Mono R ASRC", is_using_asrc},
+ {"DAC Mono Left Filter", NULL, "DAC Mono L ASRC", is_using_asrc},
+ {"DAC Mono Right Filter", NULL, "DAC Mono R ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo2 Filter", NULL, "DAC STO2 ASRC", is_using_asrc},
+ {"I2S1 ASRC", NULL, "CLKDET"},
+ {"I2S2 ASRC", NULL, "CLKDET"},
+ {"I2S3 ASRC", NULL, "CLKDET"},
+
+ /*Vref*/
+ {"Mic Det Power", NULL, "Vref2"},
+ {"MICBIAS1", NULL, "Vref1"},
+ {"MICBIAS1", NULL, "Vref2"},
+ {"MICBIAS2", NULL, "Vref1"},
+ {"MICBIAS2", NULL, "Vref2"},
+ {"MICBIAS3", NULL, "Vref1"},
+ {"MICBIAS3", NULL, "Vref2"},
+
+ {"Stereo1 DMIC L Mux", NULL, "DMIC STO1 ASRC"},
+ {"Stereo1 DMIC R Mux", NULL, "DMIC STO1 ASRC"},
+ {"Stereo2 DMIC L Mux", NULL, "DMIC STO2 ASRC"},
+ {"Stereo2 DMIC R Mux", NULL, "DMIC STO2 ASRC"},
+ {"Mono DMIC L Mux", NULL, "DMIC MONO L ASRC"},
+ {"Mono DMIC R Mux", NULL, "DMIC MONO R ASRC"},
+
+ {"I2S1_1", NULL, "I2S1 ASRC"},
+ {"I2S1_2", NULL, "I2S1 ASRC"},
+ {"I2S2_1", NULL, "I2S2 ASRC"},
+ {"I2S2_2", NULL, "I2S2 ASRC"},
+ {"I2S3", NULL, "I2S3 ASRC"},
+
+ {"CLKDET SYS", NULL, "CLKDET"},
+ {"CLKDET HP", NULL, "CLKDET"},
+ {"CLKDET MONO", NULL, "CLKDET"},
+ {"CLKDET LOUT", NULL, "CLKDET"},
+
+ {"IN1P", NULL, "LDO2"},
+ {"IN2P", NULL, "LDO2"},
+ {"IN3P", NULL, "LDO2"},
+ {"IN4P", NULL, "LDO2"},
+
+ {"DMIC1", NULL, "DMIC L1"},
+ {"DMIC1", NULL, "DMIC R1"},
+ {"DMIC2", NULL, "DMIC L2"},
+ {"DMIC2", NULL, "DMIC R2"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST1", NULL, "IN1N"},
+ {"BST1", NULL, "BST1 Power"},
+ {"BST1", NULL, "BST1P Power"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+ {"BST2", NULL, "BST2 Power"},
+ {"BST2", NULL, "BST2P Power"},
+ {"BST3", NULL, "IN3P"},
+ {"BST3", NULL, "IN3N"},
+ {"BST3", NULL, "BST3 Power"},
+ {"BST3", NULL, "BST3P Power"},
+ {"BST4", NULL, "IN4P"},
+ {"BST4", NULL, "IN4N"},
+ {"BST4", NULL, "BST4 Power"},
+ {"BST4", NULL, "BST4P Power"},
+ {"BST1 CBJ", NULL, "IN1P"},
+ {"BST1 CBJ", NULL, "IN1N"},
+ {"BST1 CBJ", NULL, "CBJ Power"},
+ {"CBJ Power", NULL, "Vref2"},
+
+ {"INL VOL", NULL, "IN3P"},
+ {"INR VOL", NULL, "IN3N"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", "INL Switch", "INL VOL"},
+ {"RECMIX1L", "INR Switch", "INR VOL"},
+ {"RECMIX1L", "BST4 Switch", "BST4"},
+ {"RECMIX1L", "BST3 Switch", "BST3"},
+ {"RECMIX1L", "BST2 Switch", "BST2"},
+ {"RECMIX1L", "BST1 Switch", "BST1"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+
+ {"RECMIX1R", "MONOVOL Switch", "MONOVOL"},
+ {"RECMIX1R", "INR Switch", "INR VOL"},
+ {"RECMIX1R", "BST4 Switch", "BST4"},
+ {"RECMIX1R", "BST3 Switch", "BST3"},
+ {"RECMIX1R", "BST2 Switch", "BST2"},
+ {"RECMIX1R", "BST1 Switch", "BST1"},
+ {"RECMIX1R", NULL, "RECMIX1R Power"},
+
+ {"RECMIX2L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX2L", "INL Switch", "INL VOL"},
+ {"RECMIX2L", "INR Switch", "INR VOL"},
+ {"RECMIX2L", "BST4 Switch", "BST4"},
+ {"RECMIX2L", "BST3 Switch", "BST3"},
+ {"RECMIX2L", "BST2 Switch", "BST2"},
+ {"RECMIX2L", "BST1 Switch", "BST1"},
+ {"RECMIX2L", NULL, "RECMIX2L Power"},
+
+ {"RECMIX2R", "MONOVOL Switch", "MONOVOL"},
+ {"RECMIX2R", "INL Switch", "INL VOL"},
+ {"RECMIX2R", "INR Switch", "INR VOL"},
+ {"RECMIX2R", "BST4 Switch", "BST4"},
+ {"RECMIX2R", "BST3 Switch", "BST3"},
+ {"RECMIX2R", "BST2 Switch", "BST2"},
+ {"RECMIX2R", "BST1 Switch", "BST1"},
+ {"RECMIX2R", NULL, "RECMIX2R Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+ {"ADC1 R", NULL, "RECMIX1R"},
+ {"ADC1 R", NULL, "ADC1 R Power"},
+ {"ADC1 R", NULL, "ADC1 clock"},
+
+ {"ADC2 L", NULL, "RECMIX2L"},
+ {"ADC2 L", NULL, "ADC2 L Power"},
+ {"ADC2 L", NULL, "ADC2 clock"},
+ {"ADC2 R", NULL, "RECMIX2R"},
+ {"ADC2 R", NULL, "ADC2 R Power"},
+ {"ADC2 R", NULL, "ADC2 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ {"Stereo1 DMIC L Mux", "DMIC1", "DMIC L1"},
+ {"Stereo1 DMIC L Mux", "DMIC2", "DMIC L2"},
+
+ {"Stereo1 DMIC R Mux", "DMIC1", "DMIC R1"},
+ {"Stereo1 DMIC R Mux", "DMIC2", "DMIC R2"},
+
+ {"Mono DMIC L Mux", "DMIC1 L", "DMIC L1"},
+ {"Mono DMIC L Mux", "DMIC2 L", "DMIC L2"},
+
+ {"Mono DMIC R Mux", "DMIC1 R", "DMIC R1"},
+ {"Mono DMIC R Mux", "DMIC2 R", "DMIC R2"},
+
+ {"Stereo2 DMIC L Mux", "DMIC1", "DMIC L1"},
+ {"Stereo2 DMIC L Mux", "DMIC2", "DMIC L2"},
+
+ {"Stereo2 DMIC R Mux", "DMIC1", "DMIC R1"},
+ {"Stereo2 DMIC R Mux", "DMIC2", "DMIC R2"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC L Mux", "ADC2 L", "ADC2 L"},
+ {"Stereo1 ADC L Mux", "ADC2 R", "ADC2 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC2 L", "ADC2 L"},
+ {"Stereo1 ADC R Mux", "ADC2 R", "ADC2 R"},
+
+ {"Stereo1 DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"},
+ {"Stereo1 DD L Mux", "MONO DAC", "Mono DAC MIXL"},
+
+ {"Stereo1 DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"},
+ {"Stereo1 DD R Mux", "MONO DAC", "Mono DAC MIXR"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DD Mux", "Stereo1 DD L Mux"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC L Mux"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DD Mux", "Stereo1 DD R Mux"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC R Mux"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR"},
+
+ {"Mono ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Mono ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Mono ADC L Mux", "ADC2 L", "ADC2 L"},
+ {"Mono ADC L Mux", "ADC2 R", "ADC2 R"},
+
+ {"Mono ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Mono ADC R Mux", "ADC1 R", "ADC1 R"},
+ {"Mono ADC R Mux", "ADC2 L", "ADC2 L"},
+ {"Mono ADC R Mux", "ADC2 R", "ADC2 R"},
+
+ {"Mono DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"},
+ {"Mono DD L Mux", "MONO DAC", "Mono DAC MIXL"},
+
+ {"Mono DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"},
+ {"Mono DD R Mux", "MONO DAC", "Mono DAC MIXR"},
+
+ {"Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux"},
+ {"Mono ADC L2 Mux", "DAC MIXL", "DAC MIXL"},
+ {"Mono ADC L1 Mux", "DD Mux", "Mono DD L Mux"},
+ {"Mono ADC L1 Mux", "ADC", "Mono ADC L Mux"},
+
+ {"Mono ADC R1 Mux", "DD Mux", "Mono DD R Mux"},
+ {"Mono ADC R1 Mux", "ADC", "Mono ADC R Mux"},
+ {"Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux"},
+ {"Mono ADC R2 Mux", "DAC MIXR", "DAC MIXR"},
+
+ {"Stereo2 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo2 ADC L Mux", "ADC2 L", "ADC2 L"},
+ {"Stereo2 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo2 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo2 ADC R Mux", "ADC2 L", "ADC2 L"},
+ {"Stereo2 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo2 DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"},
+ {"Stereo2 DD L Mux", "MONO DAC", "Mono DAC MIXL"},
+
+ {"Stereo2 DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"},
+ {"Stereo2 DD R Mux", "MONO DAC", "Mono DAC MIXR"},
+
+ {"Stereo2 ADC L1 Mux", "ADC", "Stereo2 ADC L Mux"},
+ {"Stereo2 ADC L1 Mux", "DD Mux", "Stereo2 DD L Mux"},
+ {"Stereo2 ADC L2 Mux", "DMIC", "Stereo2 DMIC L Mux"},
+ {"Stereo2 ADC L2 Mux", "DAC MIX", "DAC MIXL"},
+
+ {"Stereo2 ADC R1 Mux", "ADC", "Stereo2 ADC R Mux"},
+ {"Stereo2 ADC R1 Mux", "DD Mux", "Stereo2 DD R Mux"},
+ {"Stereo2 ADC R2 Mux", "DMIC", "Stereo2 DMIC R Mux"},
+ {"Stereo2 ADC R2 Mux", "DAC MIX", "DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"},
+ {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"},
+ {"Mono ADC MIXL", NULL, "ADC Mono Left Filter"},
+
+ {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"},
+ {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"},
+ {"Mono ADC MIXR", NULL, "ADC Mono Right Filter"},
+
+ {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"},
+ {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"},
+ {"Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter"},
+
+ {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"},
+ {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"},
+ {"Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+ {"Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL"},
+ {"Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR"},
+ {"Mono ADC MIX", NULL, "Mono ADC MIXL"},
+ {"Mono ADC MIX", NULL, "Mono ADC MIXR"},
+
+ {"IF1_1_ADC1 Mux", "STO1 ADC", "Stereo1 ADC MIX"},
+ {"IF1_1_ADC1 Mux", "IF2_1 DAC", "IF2_1 DAC"},
+ {"IF1_1_ADC2 Mux", "STO2 ADC", "Stereo2 ADC MIX"},
+ {"IF1_1_ADC2 Mux", "IF2_2 DAC", "IF2_2 DAC"},
+ {"IF1_1_ADC3 Mux", "MONO ADC", "Mono ADC MIX"},
+ {"IF1_1_ADC3 Mux", "IF3 DAC", "IF3 DAC"},
+ {"IF1_1_ADC4", NULL, "DAC1 MIX"},
+
+ {"IF1_2_ADC1 Mux", "STO1 ADC", "Stereo1 ADC MIX"},
+ {"IF1_2_ADC1 Mux", "IF1 DAC", "IF1 DAC1"},
+ {"IF1_2_ADC2 Mux", "STO2 ADC", "Stereo2 ADC MIX"},
+ {"IF1_2_ADC2 Mux", "IF2_1 DAC", "IF2_1 DAC"},
+ {"IF1_2_ADC3 Mux", "MONO ADC", "Mono ADC MIX"},
+ {"IF1_2_ADC3 Mux", "IF2_2 DAC", "IF2_2 DAC"},
+ {"IF1_2_ADC4 Mux", "DAC1", "DAC1 MIX"},
+ {"IF1_2_ADC4 Mux", "IF3 DAC", "IF3 DAC"},
+
+ {"TDM1 slot 01 Data Mux", "1234", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 01 Data Mux", "1243", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 01 Data Mux", "1324", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 01 Data Mux", "1342", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 01 Data Mux", "1432", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 01 Data Mux", "1423", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 01 Data Mux", "2134", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 01 Data Mux", "2143", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 01 Data Mux", "2314", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 01 Data Mux", "2341", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 01 Data Mux", "2431", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 01 Data Mux", "2413", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 01 Data Mux", "3124", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 01 Data Mux", "3142", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 01 Data Mux", "3214", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 01 Data Mux", "3241", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 01 Data Mux", "3412", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 01 Data Mux", "3421", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 01 Data Mux", "4123", "IF1_1_ADC4"},
+ {"TDM1 slot 01 Data Mux", "4132", "IF1_1_ADC4"},
+ {"TDM1 slot 01 Data Mux", "4213", "IF1_1_ADC4"},
+ {"TDM1 slot 01 Data Mux", "4231", "IF1_1_ADC4"},
+ {"TDM1 slot 01 Data Mux", "4312", "IF1_1_ADC4"},
+ {"TDM1 slot 01 Data Mux", "4321", "IF1_1_ADC4"},
+ {"TDM1 slot 01 Data Mux", NULL, "I2S1_1"},
+
+ {"TDM1 slot 23 Data Mux", "1234", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 23 Data Mux", "1243", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 23 Data Mux", "1324", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 23 Data Mux", "1342", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 23 Data Mux", "1432", "IF1_1_ADC4"},
+ {"TDM1 slot 23 Data Mux", "1423", "IF1_1_ADC4"},
+ {"TDM1 slot 23 Data Mux", "2134", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 23 Data Mux", "2143", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 23 Data Mux", "2314", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 23 Data Mux", "2341", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 23 Data Mux", "2431", "IF1_1_ADC4"},
+ {"TDM1 slot 23 Data Mux", "2413", "IF1_1_ADC4"},
+ {"TDM1 slot 23 Data Mux", "3124", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 23 Data Mux", "3142", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 23 Data Mux", "3214", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 23 Data Mux", "3241", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 23 Data Mux", "3412", "IF1_1_ADC4"},
+ {"TDM1 slot 23 Data Mux", "3421", "IF1_1_ADC4"},
+ {"TDM1 slot 23 Data Mux", "4123", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 23 Data Mux", "4132", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 23 Data Mux", "4213", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 23 Data Mux", "4231", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 23 Data Mux", "4312", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 23 Data Mux", "4321", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 23 Data Mux", NULL, "I2S1_1"},
+
+ {"TDM1 slot 45 Data Mux", "1234", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 45 Data Mux", "1243", "IF1_1_ADC4"},
+ {"TDM1 slot 45 Data Mux", "1324", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 45 Data Mux", "1342", "IF1_1_ADC4"},
+ {"TDM1 slot 45 Data Mux", "1432", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 45 Data Mux", "1423", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 45 Data Mux", "2134", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 45 Data Mux", "2143", "IF1_1_ADC4"},
+ {"TDM1 slot 45 Data Mux", "2314", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 45 Data Mux", "2341", "IF1_1_ADC4"},
+ {"TDM1 slot 45 Data Mux", "2431", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 45 Data Mux", "2413", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 45 Data Mux", "3124", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 45 Data Mux", "3142", "IF1_1_ADC4"},
+ {"TDM1 slot 45 Data Mux", "3214", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 45 Data Mux", "3241", "IF1_1_ADC4"},
+ {"TDM1 slot 45 Data Mux", "3412", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 45 Data Mux", "3421", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 45 Data Mux", "4123", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 45 Data Mux", "4132", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 45 Data Mux", "4213", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 45 Data Mux", "4231", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 45 Data Mux", "4312", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 45 Data Mux", "4321", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 45 Data Mux", NULL, "I2S1_1"},
+
+ {"TDM1 slot 67 Data Mux", "1234", "IF1_1_ADC4"},
+ {"TDM1 slot 67 Data Mux", "1243", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 67 Data Mux", "1324", "IF1_1_ADC4"},
+ {"TDM1 slot 67 Data Mux", "1342", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 67 Data Mux", "1432", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 67 Data Mux", "1423", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 67 Data Mux", "2134", "IF1_1_ADC4"},
+ {"TDM1 slot 67 Data Mux", "2143", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 67 Data Mux", "2314", "IF1_1_ADC4"},
+ {"TDM1 slot 67 Data Mux", "2341", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 67 Data Mux", "2431", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 67 Data Mux", "2413", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 67 Data Mux", "3124", "IF1_1_ADC4"},
+ {"TDM1 slot 67 Data Mux", "3142", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 67 Data Mux", "3214", "IF1_1_ADC4"},
+ {"TDM1 slot 67 Data Mux", "3241", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 67 Data Mux", "3412", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 67 Data Mux", "3421", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 67 Data Mux", "4123", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 67 Data Mux", "4132", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 67 Data Mux", "4213", "IF1_1_ADC3 Mux"},
+ {"TDM1 slot 67 Data Mux", "4231", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 67 Data Mux", "4312", "IF1_1_ADC2 Mux"},
+ {"TDM1 slot 67 Data Mux", "4321", "IF1_1_ADC1 Mux"},
+ {"TDM1 slot 67 Data Mux", NULL, "I2S1_1"},
+
+
+ {"TDM2 slot 01 Data Mux", "1234", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 01 Data Mux", "1243", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 01 Data Mux", "1324", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 01 Data Mux", "1342", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 01 Data Mux", "1432", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 01 Data Mux", "1423", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 01 Data Mux", "2134", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 01 Data Mux", "2143", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 01 Data Mux", "2314", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 01 Data Mux", "2341", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 01 Data Mux", "2431", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 01 Data Mux", "2413", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 01 Data Mux", "3124", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 01 Data Mux", "3142", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 01 Data Mux", "3214", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 01 Data Mux", "3241", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 01 Data Mux", "3412", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 01 Data Mux", "3421", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 01 Data Mux", "4123", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 01 Data Mux", "4132", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 01 Data Mux", "4213", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 01 Data Mux", "4231", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 01 Data Mux", "4312", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 01 Data Mux", "4321", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 01 Data Mux", NULL, "I2S1_2"},
+
+ {"TDM2 slot 23 Data Mux", "1234", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 23 Data Mux", "1243", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 23 Data Mux", "1324", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 23 Data Mux", "1342", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 23 Data Mux", "1432", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 23 Data Mux", "1423", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 23 Data Mux", "2134", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 23 Data Mux", "2143", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 23 Data Mux", "2314", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 23 Data Mux", "2341", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 23 Data Mux", "2431", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 23 Data Mux", "2413", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 23 Data Mux", "3124", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 23 Data Mux", "3142", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 23 Data Mux", "3214", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 23 Data Mux", "3241", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 23 Data Mux", "3412", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 23 Data Mux", "3421", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 23 Data Mux", "4123", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 23 Data Mux", "4132", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 23 Data Mux", "4213", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 23 Data Mux", "4231", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 23 Data Mux", "4312", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 23 Data Mux", "4321", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 23 Data Mux", NULL, "I2S1_2"},
+
+ {"TDM2 slot 45 Data Mux", "1234", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 45 Data Mux", "1243", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 45 Data Mux", "1324", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 45 Data Mux", "1342", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 45 Data Mux", "1432", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 45 Data Mux", "1423", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 45 Data Mux", "2134", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 45 Data Mux", "2143", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 45 Data Mux", "2314", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 45 Data Mux", "2341", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 45 Data Mux", "2431", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 45 Data Mux", "2413", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 45 Data Mux", "3124", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 45 Data Mux", "3142", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 45 Data Mux", "3214", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 45 Data Mux", "3241", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 45 Data Mux", "3412", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 45 Data Mux", "3421", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 45 Data Mux", "4123", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 45 Data Mux", "4132", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 45 Data Mux", "4213", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 45 Data Mux", "4231", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 45 Data Mux", "4312", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 45 Data Mux", "4321", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 45 Data Mux", NULL, "I2S1_2"},
+
+ {"TDM2 slot 67 Data Mux", "1234", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 67 Data Mux", "1243", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 67 Data Mux", "1324", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 67 Data Mux", "1342", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 67 Data Mux", "1432", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 67 Data Mux", "1423", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 67 Data Mux", "2134", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 67 Data Mux", "2143", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 67 Data Mux", "2314", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 67 Data Mux", "2341", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 67 Data Mux", "2431", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 67 Data Mux", "2413", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 67 Data Mux", "3124", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 67 Data Mux", "3142", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 67 Data Mux", "3214", "IF1_2_ADC4 Mux"},
+ {"TDM2 slot 67 Data Mux", "3241", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 67 Data Mux", "3412", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 67 Data Mux", "3421", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 67 Data Mux", "4123", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 67 Data Mux", "4132", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 67 Data Mux", "4213", "IF1_2_ADC3 Mux"},
+ {"TDM2 slot 67 Data Mux", "4231", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 67 Data Mux", "4312", "IF1_2_ADC2 Mux"},
+ {"TDM2 slot 67 Data Mux", "4321", "IF1_2_ADC1 Mux"},
+ {"TDM2 slot 67 Data Mux", NULL, "I2S1_2"},
+
+ {"IF1_1 0 ADC Swap Mux", "L/R", "TDM1 slot 01 Data Mux"},
+ {"IF1_1 0 ADC Swap Mux", "L/L", "TDM1 slot 01 Data Mux"},
+ {"IF1_1 1 ADC Swap Mux", "R/L", "TDM1 slot 01 Data Mux"},
+ {"IF1_1 1 ADC Swap Mux", "R/R", "TDM1 slot 01 Data Mux"},
+ {"IF1_1 2 ADC Swap Mux", "L/R", "TDM1 slot 23 Data Mux"},
+ {"IF1_1 2 ADC Swap Mux", "R/L", "TDM1 slot 23 Data Mux"},
+ {"IF1_1 3 ADC Swap Mux", "L/L", "TDM1 slot 23 Data Mux"},
+ {"IF1_1 3 ADC Swap Mux", "R/R", "TDM1 slot 23 Data Mux"},
+ {"IF1_1 4 ADC Swap Mux", "L/R", "TDM1 slot 45 Data Mux"},
+ {"IF1_1 4 ADC Swap Mux", "R/L", "TDM1 slot 45 Data Mux"},
+ {"IF1_1 5 ADC Swap Mux", "L/L", "TDM1 slot 45 Data Mux"},
+ {"IF1_1 5 ADC Swap Mux", "R/R", "TDM1 slot 45 Data Mux"},
+ {"IF1_1 6 ADC Swap Mux", "L/R", "TDM1 slot 67 Data Mux"},
+ {"IF1_1 6 ADC Swap Mux", "R/L", "TDM1 slot 67 Data Mux"},
+ {"IF1_1 7 ADC Swap Mux", "L/L", "TDM1 slot 67 Data Mux"},
+ {"IF1_1 7 ADC Swap Mux", "R/R", "TDM1 slot 67 Data Mux"},
+ {"IF1_2 0 ADC Swap Mux", "L/R", "TDM2 slot 01 Data Mux"},
+ {"IF1_2 0 ADC Swap Mux", "R/L", "TDM2 slot 01 Data Mux"},
+ {"IF1_2 1 ADC Swap Mux", "L/L", "TDM2 slot 01 Data Mux"},
+ {"IF1_2 1 ADC Swap Mux", "R/R", "TDM2 slot 01 Data Mux"},
+ {"IF1_2 2 ADC Swap Mux", "L/R", "TDM2 slot 23 Data Mux"},
+ {"IF1_2 2 ADC Swap Mux", "R/L", "TDM2 slot 23 Data Mux"},
+ {"IF1_2 3 ADC Swap Mux", "L/L", "TDM2 slot 23 Data Mux"},
+ {"IF1_2 3 ADC Swap Mux", "R/R", "TDM2 slot 23 Data Mux"},
+ {"IF1_2 4 ADC Swap Mux", "L/R", "TDM2 slot 45 Data Mux"},
+ {"IF1_2 4 ADC Swap Mux", "R/L", "TDM2 slot 45 Data Mux"},
+ {"IF1_2 5 ADC Swap Mux", "L/L", "TDM2 slot 45 Data Mux"},
+ {"IF1_2 5 ADC Swap Mux", "R/R", "TDM2 slot 45 Data Mux"},
+ {"IF1_2 6 ADC Swap Mux", "L/R", "TDM2 slot 67 Data Mux"},
+ {"IF1_2 6 ADC Swap Mux", "R/L", "TDM2 slot 67 Data Mux"},
+ {"IF1_2 7 ADC Swap Mux", "L/L", "TDM2 slot 67 Data Mux"},
+ {"IF1_2 7 ADC Swap Mux", "R/R", "TDM2 slot 67 Data Mux"},
+
+ {"IF2_1 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"},
+ {"IF2_1 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"},
+ {"IF2_1 ADC Mux", "MONO ADC", "Mono ADC MIX"},
+ {"IF2_1 ADC Mux", "IF1 DAC1", "IF1 DAC1"},
+ {"IF2_1 ADC Mux", "IF1 DAC2", "IF1 DAC2"},
+ {"IF2_1 ADC Mux", "IF2_2 DAC", "IF2_2 DAC"},
+ {"IF2_1 ADC Mux", "IF3 DAC", "IF3 DAC"},
+ {"IF2_1 ADC Mux", "DAC1 MIX", "DAC1 MIX"},
+ {"IF2_1 ADC", NULL, "IF2_1 ADC Mux"},
+ {"IF2_1 ADC", NULL, "I2S2_1"},
+
+ {"IF2_2 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"},
+ {"IF2_2 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"},
+ {"IF2_2 ADC Mux", "MONO ADC", "Mono ADC MIX"},
+ {"IF2_2 ADC Mux", "IF1 DAC1", "IF1 DAC1"},
+ {"IF2_2 ADC Mux", "IF1 DAC2", "IF1 DAC2"},
+ {"IF2_2 ADC Mux", "IF2_1 DAC", "IF2_1 DAC"},
+ {"IF2_2 ADC Mux", "IF3 DAC", "IF3 DAC"},
+ {"IF2_2 ADC Mux", "DAC1 MIX", "DAC1 MIX"},
+ {"IF2_2 ADC", NULL, "IF2_2 ADC Mux"},
+ {"IF2_2 ADC", NULL, "I2S2_2"},
+
+ {"IF3 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"},
+ {"IF3 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"},
+ {"IF3 ADC Mux", "MONO ADC", "Mono ADC MIX"},
+ {"IF3 ADC Mux", "IF1 DAC1", "IF1 DAC1"},
+ {"IF3 ADC Mux", "IF1 DAC2", "IF1 DAC2"},
+ {"IF3 ADC Mux", "IF2_1 DAC", "IF2_1 DAC"},
+ {"IF3 ADC Mux", "IF2_2 DAC", "IF2_2 DAC"},
+ {"IF3 ADC Mux", "DAC1 MIX", "DAC1 MIX"},
+ {"IF3 ADC", NULL, "IF3 ADC Mux"},
+ {"IF3 ADC", NULL, "I2S3"},
+
+ {"AIF1_1TX slot 0", NULL, "IF1_1 0 ADC Swap Mux"},
+ {"AIF1_1TX slot 1", NULL, "IF1_1 1 ADC Swap Mux"},
+ {"AIF1_1TX slot 2", NULL, "IF1_1 2 ADC Swap Mux"},
+ {"AIF1_1TX slot 3", NULL, "IF1_1 3 ADC Swap Mux"},
+ {"AIF1_1TX slot 4", NULL, "IF1_1 4 ADC Swap Mux"},
+ {"AIF1_1TX slot 5", NULL, "IF1_1 5 ADC Swap Mux"},
+ {"AIF1_1TX slot 6", NULL, "IF1_1 6 ADC Swap Mux"},
+ {"AIF1_1TX slot 7", NULL, "IF1_1 7 ADC Swap Mux"},
+ {"AIF1_2TX slot 0", NULL, "IF1_2 0 ADC Swap Mux"},
+ {"AIF1_2TX slot 1", NULL, "IF1_2 1 ADC Swap Mux"},
+ {"AIF1_2TX slot 2", NULL, "IF1_2 2 ADC Swap Mux"},
+ {"AIF1_2TX slot 3", NULL, "IF1_2 3 ADC Swap Mux"},
+ {"AIF1_2TX slot 4", NULL, "IF1_2 4 ADC Swap Mux"},
+ {"AIF1_2TX slot 5", NULL, "IF1_2 5 ADC Swap Mux"},
+ {"AIF1_2TX slot 6", NULL, "IF1_2 6 ADC Swap Mux"},
+ {"AIF1_2TX slot 7", NULL, "IF1_2 7 ADC Swap Mux"},
+ {"IF2_1 ADC Swap Mux", "L/R", "IF2_1 ADC"},
+ {"IF2_1 ADC Swap Mux", "R/L", "IF2_1 ADC"},
+ {"IF2_1 ADC Swap Mux", "L/L", "IF2_1 ADC"},
+ {"IF2_1 ADC Swap Mux", "R/R", "IF2_1 ADC"},
+ {"AIF2_1TX", NULL, "IF2_1 ADC Swap Mux"},
+ {"IF2_2 ADC Swap Mux", "L/R", "IF2_2 ADC"},
+ {"IF2_2 ADC Swap Mux", "R/L", "IF2_2 ADC"},
+ {"IF2_2 ADC Swap Mux", "L/L", "IF2_2 ADC"},
+ {"IF2_2 ADC Swap Mux", "R/R", "IF2_2 ADC"},
+ {"AIF2_2TX", NULL, "IF2_2 ADC Swap Mux"},
+ {"IF3 ADC Swap Mux", "L/R", "IF3 ADC"},
+ {"IF3 ADC Swap Mux", "R/L", "IF3 ADC"},
+ {"IF3 ADC Swap Mux", "L/L", "IF3 ADC"},
+ {"IF3 ADC Swap Mux", "R/R", "IF3 ADC"},
+ {"AIF3TX", NULL, "IF3 ADC Swap Mux"},
+
+ {"IF1 DAC1", NULL, "AIF1RX"},
+ {"IF1 DAC2", NULL, "AIF1RX"},
+ {"IF1 DAC3", NULL, "AIF1RX"},
+ {"IF2_1 DAC Swap Mux", "L/R", "AIF2_1RX"},
+ {"IF2_1 DAC Swap Mux", "R/L", "AIF2_1RX"},
+ {"IF2_1 DAC Swap Mux", "L/L", "AIF2_1RX"},
+ {"IF2_1 DAC Swap Mux", "R/R", "AIF2_1RX"},
+ {"IF2_2 DAC Swap Mux", "L/R", "AIF2_2RX"},
+ {"IF2_2 DAC Swap Mux", "R/L", "AIF2_2RX"},
+ {"IF2_2 DAC Swap Mux", "L/L", "AIF2_2RX"},
+ {"IF2_2 DAC Swap Mux", "R/R", "AIF2_2RX"},
+ {"IF2_1 DAC", NULL, "IF2_1 DAC Swap Mux"},
+ {"IF2_2 DAC", NULL, "IF2_2 DAC Swap Mux"},
+ {"IF3 DAC Swap Mux", "L/R", "AIF3RX"},
+ {"IF3 DAC Swap Mux", "R/L", "AIF3RX"},
+ {"IF3 DAC Swap Mux", "L/L", "AIF3RX"},
+ {"IF3 DAC Swap Mux", "R/R", "AIF3RX"},
+ {"IF3 DAC", NULL, "IF3 DAC Swap Mux"},
+
+ {"IF1 DAC1", NULL, "I2S1_1"},
+ {"IF1 DAC2", NULL, "I2S1_1"},
+ {"IF1 DAC3", NULL, "I2S1_1"},
+ {"IF2_1 DAC", NULL, "I2S2_1"},
+ {"IF2_2 DAC", NULL, "I2S2_2"},
+ {"IF3 DAC", NULL, "I2S3"},
+
+ {"IF1 DAC1 L", NULL, "IF1 DAC1"},
+ {"IF1 DAC1 R", NULL, "IF1 DAC1"},
+ {"IF1 DAC2 L", NULL, "IF1 DAC2"},
+ {"IF1 DAC2 R", NULL, "IF1 DAC2"},
+ {"IF1 DAC3 L", NULL, "IF1 DAC3"},
+ {"IF1 DAC3 R", NULL, "IF1 DAC3"},
+ {"IF2_1 DAC L", NULL, "IF2_1 DAC"},
+ {"IF2_1 DAC R", NULL, "IF2_1 DAC"},
+ {"IF2_2 DAC L", NULL, "IF2_2 DAC"},
+ {"IF2_2 DAC R", NULL, "IF2_2 DAC"},
+ {"IF3 DAC L", NULL, "IF3 DAC"},
+ {"IF3 DAC R", NULL, "IF3 DAC"},
+
+ {"DAC L1 Mux", "IF1 DAC1", "IF1 DAC1 L"},
+ {"DAC L1 Mux", "IF2_1 DAC", "IF2_1 DAC L"},
+ {"DAC L1 Mux", "IF2_2 DAC", "IF2_2 DAC L"},
+ {"DAC L1 Mux", "IF3 DAC", "IF3 DAC L"},
+ {"DAC L1 Mux", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC R1 Mux", "IF1 DAC1", "IF1 DAC1 R"},
+ {"DAC R1 Mux", "IF2_1 DAC", "IF2_1 DAC R"},
+ {"DAC R1 Mux", "IF2_2 DAC", "IF2_2 DAC R"},
+ {"DAC R1 Mux", "IF3 DAC", "IF3 DAC R"},
+ {"DAC R1 Mux", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "DAC L1 Mux"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "DAC R1 Mux"},
+
+ {"DAC1 MIX", NULL, "DAC1 MIXL"},
+ {"DAC1 MIX", NULL, "DAC1 MIXR"},
+
+ {"DAC L2 Mux", "IF1 DAC2", "IF1 DAC2 L"},
+ {"DAC L2 Mux", "IF2_1 DAC", "IF2_1 DAC L"},
+ {"DAC L2 Mux", "IF2_2 DAC", "IF2_2 DAC L"},
+ {"DAC L2 Mux", "IF3 DAC", "IF3 DAC L"},
+ {"DAC L2 Mux", "Mono ADC MIX", "Mono ADC MIXL"},
+ {"DAC L2 Mux", NULL, "DAC Mono Left Filter"},
+
+ {"DAC R2 Mux", "IF1 DAC2", "IF1 DAC2 R"},
+ {"DAC R2 Mux", "IF2_1 DAC", "IF2_1 DAC R"},
+ {"DAC R2 Mux", "IF2_2 DAC", "IF2_2 DAC R"},
+ {"DAC R2 Mux", "IF3 DAC", "IF3 DAC R"},
+ {"DAC R2 Mux", "Mono ADC MIX", "Mono ADC MIXR"},
+ {"DAC R2 Mux", NULL, "DAC Mono Right Filter"},
+
+ {"DAC L3 Mux", "IF1 DAC2", "IF1 DAC2 L"},
+ {"DAC L3 Mux", "IF2_1 DAC", "IF2_1 DAC L"},
+ {"DAC L3 Mux", "IF2_2 DAC", "IF2_2 DAC L"},
+ {"DAC L3 Mux", "IF3 DAC", "IF3 DAC L"},
+ {"DAC L3 Mux", "STO2 ADC MIX", "Stereo2 ADC MIXL"},
+ {"DAC L3 Mux", NULL, "DAC Stereo2 Filter"},
+
+ {"DAC R3 Mux", "IF1 DAC2", "IF1 DAC2 R"},
+ {"DAC R3 Mux", "IF2_1 DAC", "IF2_1 DAC R"},
+ {"DAC R3 Mux", "IF2_2 DAC", "IF2_2 DAC R"},
+ {"DAC R3 Mux", "IF3 DAC", "IF3 DAC R"},
+ {"DAC R3 Mux", "STO2 ADC MIX", "Stereo2 ADC MIXR"},
+ {"DAC R3 Mux", NULL, "DAC Stereo2 Filter"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo1 DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo1 DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"Stereo2 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo2 DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo2 DAC MIXL", "DAC L3 Switch", "DAC L3 Mux"},
+
+ {"Stereo2 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo2 DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Stereo2 DAC MIXR", "DAC R3 Switch", "DAC R3 Mux"},
+
+ {"Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Mono DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"DAC MIXL", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC MIXL", "Stereo2 DAC Mixer", "Stereo2 DAC MIXL"},
+ {"DAC MIXL", "Mono DAC Mixer", "Mono DAC MIXL"},
+ {"DAC MIXR", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+ {"DAC MIXR", "Stereo2 DAC Mixer", "Stereo2 DAC MIXR"},
+ {"DAC MIXR", "Mono DAC Mixer", "Mono DAC MIXR"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC L1 Source", "DMIC1", "DMIC L1"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+ {"DAC R1 Source", "DMIC1", "DMIC R1"},
+
+ {"DAC L2 Source", "DAC2", "DAC L2 Mux"},
+ {"DAC L2 Source", "Mono DAC Mixer", "Mono DAC MIXL"},
+ {"DAC L2 Source", NULL, "DAC L2 Power"},
+ {"DAC R2 Source", "DAC2", "DAC R2 Mux"},
+ {"DAC R2 Source", "Mono DAC Mixer", "Mono DAC MIXR"},
+ {"DAC R2 Source", NULL, "DAC R2 Power"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+ {"DAC L2", NULL, "DAC L2 Source"},
+ {"DAC R2", NULL, "DAC R2 Source"},
+
+ {"DAC L1", NULL, "DAC 1 Clock"},
+ {"DAC R1", NULL, "DAC 1 Clock"},
+ {"DAC L2", NULL, "DAC 2 Clock"},
+ {"DAC R2", NULL, "DAC 2 Clock"},
+
+ {"MONOVOL MIX", "DAC L2 Switch", "DAC L2"},
+ {"MONOVOL MIX", "RECMIX2L Switch", "RECMIX2L"},
+ {"MONOVOL MIX", "BST1 Switch", "BST1"},
+ {"MONOVOL MIX", "BST2 Switch", "BST2"},
+ {"MONOVOL MIX", "BST3 Switch", "BST3"},
+
+ {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXL", "INL Switch", "INL VOL"},
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "BST2 Switch", "BST2"},
+ {"OUT MIXL", "BST3 Switch", "BST3"},
+ {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXR", "INR Switch", "INR VOL"},
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST3 Switch", "BST3"},
+ {"OUT MIXR", "BST4 Switch", "BST4"},
+
+ {"MONOVOL", "Switch", "MONOVOL MIX"},
+ {"Mono MIX", "DAC L2 Switch", "DAC L2"},
+ {"Mono MIX", "MONOVOL Switch", "MONOVOL"},
+ {"Mono Amp", NULL, "Mono MIX"},
+ {"Mono Amp", NULL, "Vref2"},
+ {"Mono Amp", NULL, "Vref3"},
+ {"Mono Amp", NULL, "CLKDET SYS"},
+ {"Mono Amp", NULL, "CLKDET MONO"},
+ {"Mono Playback", "Switch", "Mono Amp"},
+ {"MONOOUT", NULL, "Mono Playback"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "Charge Pump"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "CLKDET HP"},
+ {"HP Amp", NULL, "CBJ Power"},
+ {"HP Amp", NULL, "Vref2"},
+ {"HPO Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HPO Playback"},
+ {"HPOR", NULL, "HPO Playback"},
+
+ {"OUTVOL L", "Switch", "OUT MIXL"},
+ {"OUTVOL R", "Switch", "OUT MIXR"},
+ {"LOUT L MIX", "DAC L2 Switch", "DAC L2"},
+ {"LOUT L MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT R MIX", "DAC R2 Switch", "DAC R2"},
+ {"LOUT R MIX", "OUTVOL R Switch", "OUTVOL R"},
+ {"LOUT Amp", NULL, "LOUT L MIX"},
+ {"LOUT Amp", NULL, "LOUT R MIX"},
+ {"LOUT Amp", NULL, "Vref1"},
+ {"LOUT Amp", NULL, "Vref2"},
+ {"LOUT Amp", NULL, "CLKDET SYS"},
+ {"LOUT Amp", NULL, "CLKDET LOUT"},
+ {"LOUT L Playback", "Switch", "LOUT Amp"},
+ {"LOUT R Playback", "Switch", "LOUT Amp"},
+ {"LOUTL", NULL, "LOUT L Playback"},
+ {"LOUTR", NULL, "LOUT R Playback"},
+
+ {"PDM L Mux", "Mono DAC", "Mono DAC MIXL"},
+ {"PDM L Mux", "Stereo1 DAC", "Stereo1 DAC MIXL"},
+ {"PDM L Mux", "Stereo2 DAC", "Stereo2 DAC MIXL"},
+ {"PDM L Mux", NULL, "PDM Power"},
+ {"PDM R Mux", "Mono DAC", "Mono DAC MIXR"},
+ {"PDM R Mux", "Stereo1 DAC", "Stereo1 DAC MIXR"},
+ {"PDM R Mux", "Stereo2 DAC", "Stereo2 DAC MIXR"},
+ {"PDM R Mux", NULL, "PDM Power"},
+ {"PDM L Playback", "Switch", "PDM L Mux"},
+ {"PDM R Playback", "Switch", "PDM R Mux"},
+ {"PDML", NULL, "PDM L Playback"},
+ {"PDMR", NULL, "PDM R Playback"},
+};
+
+static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0;
+
+ if (rx_mask || tx_mask)
+ val |= RT5665_I2S1_MODE_TDM;
+
+ switch (slots) {
+ case 4:
+ val |= RT5665_TDM_IN_CH_4;
+ val |= RT5665_TDM_OUT_CH_4;
+ break;
+ case 6:
+ val |= RT5665_TDM_IN_CH_6;
+ val |= RT5665_TDM_OUT_CH_6;
+ break;
+ case 8:
+ val |= RT5665_TDM_IN_CH_8;
+ val |= RT5665_TDM_OUT_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5665_TDM_IN_LEN_20;
+ val |= RT5665_TDM_OUT_LEN_20;
+ break;
+ case 24:
+ val |= RT5665_TDM_IN_LEN_24;
+ val |= RT5665_TDM_OUT_LEN_24;
+ break;
+ case 32:
+ val |= RT5665_TDM_IN_LEN_32;
+ val |= RT5665_TDM_OUT_LEN_32;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5665_TDM_CTRL_1,
+ RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
+ RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
+ RT5665_TDM_OUT_LEN_MASK, val);
+
+ return 0;
+}
+
+
+static int rt5665_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100;
+ int pre_div, frame_size;
+
+ rt5665->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5665->sysclk, rt5665->lrck[dai->id]);
+ if (pre_div < 0) {
+ dev_warn(component->dev, "Force using PLL");
+ snd_soc_component_set_pll(component, 0, RT5665_PLL1_S_MCLK,
+ rt5665->sysclk, rt5665->lrck[dai->id] * 512);
+ snd_soc_component_set_sysclk(component, RT5665_SCLK_S_PLL1, 0,
+ rt5665->lrck[dai->id] * 512, 0);
+ pre_div = 1;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt5665->lrck[dai->id], pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_bits = 0x0100;
+ break;
+ case 20:
+ val_len |= RT5665_I2S_DL_20;
+ val_bits = 0x1300;
+ break;
+ case 24:
+ val_len |= RT5665_I2S_DL_24;
+ val_bits = 0x2500;
+ break;
+ case 8:
+ val_len |= RT5665_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5665_AIF1_1:
+ case RT5665_AIF1_2:
+ if (params_channels(params) > 2)
+ rt5665_set_tdm_slot(dai, 0xf, 0xf,
+ params_channels(params), params_width(params));
+ reg_clk = RT5665_ADDA_CLK_1;
+ mask_clk = RT5665_I2S_PD1_MASK;
+ val_clk = pre_div << RT5665_I2S_PD1_SFT;
+ snd_soc_component_update_bits(component, RT5665_I2S1_SDP,
+ RT5665_I2S_DL_MASK, val_len);
+ break;
+ case RT5665_AIF2_1:
+ case RT5665_AIF2_2:
+ reg_clk = RT5665_ADDA_CLK_2;
+ mask_clk = RT5665_I2S_PD2_MASK;
+ val_clk = pre_div << RT5665_I2S_PD2_SFT;
+ snd_soc_component_update_bits(component, RT5665_I2S2_SDP,
+ RT5665_I2S_DL_MASK, val_len);
+ break;
+ case RT5665_AIF3:
+ reg_clk = RT5665_ADDA_CLK_2;
+ mask_clk = RT5665_I2S_PD3_MASK;
+ val_clk = pre_div << RT5665_I2S_PD3_SFT;
+ snd_soc_component_update_bits(component, RT5665_I2S3_SDP,
+ RT5665_I2S_DL_MASK, val_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, reg_clk, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits);
+
+ switch (rt5665->lrck[dai->id]) {
+ case 192000:
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
+ RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK,
+ RT5665_DAC_OSR_32 | RT5665_ADC_OSR_32);
+ break;
+ case 96000:
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
+ RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK,
+ RT5665_DAC_OSR_64 | RT5665_ADC_OSR_64);
+ break;
+ default:
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1,
+ RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK,
+ RT5665_DAC_OSR_128 | RT5665_ADC_OSR_128);
+ break;
+ }
+
+ if (rt5665->master[RT5665_AIF2_1] || rt5665->master[RT5665_AIF2_2]) {
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
+ RT5665_I2S2_M_PD_MASK, pre_div << RT5665_I2S2_M_PD_SFT);
+ }
+ if (rt5665->master[RT5665_AIF3]) {
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
+ RT5665_I2S3_M_PD_MASK, pre_div << RT5665_I2S3_M_PD_SFT);
+ }
+
+ return 0;
+}
+
+static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5665->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ reg_val |= RT5665_I2S_MS_S;
+ rt5665->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5665_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5665_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5665_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5665_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5665_AIF1_1:
+ case RT5665_AIF1_2:
+ snd_soc_component_update_bits(component, RT5665_I2S1_SDP,
+ RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK |
+ RT5665_I2S_DF_MASK, reg_val);
+ break;
+ case RT5665_AIF2_1:
+ case RT5665_AIF2_2:
+ snd_soc_component_update_bits(component, RT5665_I2S2_SDP,
+ RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK |
+ RT5665_I2S_DF_MASK, reg_val);
+ break;
+ case RT5665_AIF3:
+ snd_soc_component_update_bits(component, RT5665_I2S3_SDP,
+ RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK |
+ RT5665_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5665_set_component_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, src = 0;
+
+ if (freq == rt5665->sysclk && clk_id == rt5665->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5665_SCLK_S_MCLK:
+ reg_val |= RT5665_SCLK_SRC_MCLK;
+ src = RT5665_CLK_SRC_MCLK;
+ break;
+ case RT5665_SCLK_S_PLL1:
+ reg_val |= RT5665_SCLK_SRC_PLL1;
+ src = RT5665_CLK_SRC_PLL1;
+ break;
+ case RT5665_SCLK_S_RCCLK:
+ reg_val |= RT5665_SCLK_SRC_RCCLK;
+ src = RT5665_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
+ RT5665_SCLK_SRC_MASK, reg_val);
+
+ if (rt5665->master[RT5665_AIF2_1] || rt5665->master[RT5665_AIF2_2]) {
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
+ RT5665_I2S2_SRC_MASK, src << RT5665_I2S2_SRC_SFT);
+ }
+ if (rt5665->master[RT5665_AIF3]) {
+ snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1,
+ RT5665_I2S3_SRC_MASK, src << RT5665_I2S3_SRC_SFT);
+ }
+
+ rt5665->sysclk = freq;
+ rt5665->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5665_set_component_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5665->pll_src && freq_in == rt5665->pll_in &&
+ freq_out == rt5665->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5665->pll_in = 0;
+ rt5665->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
+ RT5665_SCLK_SRC_MASK, RT5665_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5665_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
+ RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK);
+ break;
+ case RT5665_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
+ RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK1);
+ break;
+ case RT5665_PLL1_S_BCLK2:
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
+ RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK2);
+ break;
+ case RT5665_PLL1_S_BCLK3:
+ snd_soc_component_update_bits(component, RT5665_GLB_CLK,
+ RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT5665_PLL_CTRL_1,
+ pll_code.n_code << RT5665_PLL_N_SFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5665_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5665_PLL_M_SFT) |
+ (pll_code.m_bp << RT5665_PLL_M_BP_SFT));
+
+ rt5665->pll_in = freq_in;
+ rt5665->pll_out = freq_out;
+ rt5665->pll_src = source;
+
+ return 0;
+}
+
+static int rt5665_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
+
+ rt5665->bclk[dai->id] = ratio;
+
+ if (ratio == 64) {
+ switch (dai->id) {
+ case RT5665_AIF2_1:
+ case RT5665_AIF2_2:
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_2,
+ RT5665_I2S_BCLK_MS2_MASK,
+ RT5665_I2S_BCLK_MS2_64);
+ break;
+ case RT5665_AIF3:
+ snd_soc_component_update_bits(component, RT5665_ADDA_CLK_2,
+ RT5665_I2S_BCLK_MS3_MASK,
+ RT5665_I2S_BCLK_MS3_64);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rt5665_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5665->regmap, RT5665_DIG_MISC,
+ RT5665_DIG_GATE_CTRL, RT5665_DIG_GATE_CTRL);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(rt5665->regmap, RT5665_PWR_DIG_1,
+ RT5665_PWR_LDO, RT5665_PWR_LDO);
+ regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1,
+ RT5665_PWR_MB, RT5665_PWR_MB);
+ regmap_update_bits(rt5665->regmap, RT5665_DIG_MISC,
+ RT5665_DIG_GATE_CTRL, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5665->regmap, RT5665_PWR_DIG_1,
+ RT5665_PWR_LDO, 0);
+ regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1,
+ RT5665_PWR_MB, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5665_probe(struct snd_soc_component *component)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ rt5665->component = component;
+
+ schedule_delayed_work(&rt5665->calibrate_work, msecs_to_jiffies(100));
+
+ return 0;
+}
+
+static void rt5665_remove(struct snd_soc_component *component)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt5665_suspend(struct snd_soc_component *component)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5665->regmap, true);
+ regcache_mark_dirty(rt5665->regmap);
+ return 0;
+}
+
+static int rt5665_resume(struct snd_soc_component *component)
+{
+ struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5665->regmap, false);
+ regcache_sync(rt5665->regmap);
+
+ return 0;
+}
+#else
+#define rt5665_suspend NULL
+#define rt5665_resume NULL
+#endif
+
+#define RT5665_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5665_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5665_aif_dai_ops = {
+ .hw_params = rt5665_hw_params,
+ .set_fmt = rt5665_set_dai_fmt,
+ .set_tdm_slot = rt5665_set_tdm_slot,
+ .set_bclk_ratio = rt5665_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt5665_dai[] = {
+ {
+ .name = "rt5665-aif1_1",
+ .id = RT5665_AIF1_1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1_1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .ops = &rt5665_aif_dai_ops,
+ },
+ {
+ .name = "rt5665-aif1_2",
+ .id = RT5665_AIF1_2,
+ .capture = {
+ .stream_name = "AIF1_2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .ops = &rt5665_aif_dai_ops,
+ },
+ {
+ .name = "rt5665-aif2_1",
+ .id = RT5665_AIF2_1,
+ .playback = {
+ .stream_name = "AIF2_1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2_1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .ops = &rt5665_aif_dai_ops,
+ },
+ {
+ .name = "rt5665-aif2_2",
+ .id = RT5665_AIF2_2,
+ .playback = {
+ .stream_name = "AIF2_2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2_2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .ops = &rt5665_aif_dai_ops,
+ },
+ {
+ .name = "rt5665-aif3",
+ .id = RT5665_AIF3,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5665_STEREO_RATES,
+ .formats = RT5665_FORMATS,
+ },
+ .ops = &rt5665_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5665 = {
+ .probe = rt5665_probe,
+ .remove = rt5665_remove,
+ .suspend = rt5665_suspend,
+ .resume = rt5665_resume,
+ .set_bias_level = rt5665_set_bias_level,
+ .controls = rt5665_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5665_snd_controls),
+ .dapm_widgets = rt5665_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets),
+ .dapm_routes = rt5665_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes),
+ .set_sysclk = rt5665_set_component_sysclk,
+ .set_pll = rt5665_set_component_pll,
+ .set_jack = rt5665_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+
+static const struct regmap_config rt5665_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = 0x0400,
+ .volatile_reg = rt5665_volatile_register,
+ .readable_reg = rt5665_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5665_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5665_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt5665_i2c_id[] = {
+ {"rt5665"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5665_i2c_id);
+
+static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev)
+{
+ rt5665->pdata.in1_diff = of_property_read_bool(dev->of_node,
+ "realtek,in1-differential");
+ rt5665->pdata.in2_diff = of_property_read_bool(dev->of_node,
+ "realtek,in2-differential");
+ rt5665->pdata.in3_diff = of_property_read_bool(dev->of_node,
+ "realtek,in3-differential");
+ rt5665->pdata.in4_diff = of_property_read_bool(dev->of_node,
+ "realtek,in4-differential");
+
+ of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin",
+ &rt5665->pdata.dmic1_data_pin);
+ of_property_read_u32(dev->of_node, "realtek,dmic2-data-pin",
+ &rt5665->pdata.dmic2_data_pin);
+ of_property_read_u32(dev->of_node, "realtek,jd-src",
+ &rt5665->pdata.jd_src);
+
+ return 0;
+}
+
+static void rt5665_calibrate(struct rt5665_priv *rt5665)
+{
+ int value, count;
+
+ mutex_lock(&rt5665->calibrate_mutex);
+
+ regcache_cache_bypass(rt5665->regmap, true);
+
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+ regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602);
+ regmap_write(rt5665->regmap, RT5665_HP_CHARGE_PUMP_1, 0x0c26);
+ regmap_write(rt5665->regmap, RT5665_MONOMIX_IN_GAIN, 0x021f);
+ regmap_write(rt5665->regmap, RT5665_MONO_OUT, 0x480a);
+ regmap_write(rt5665->regmap, RT5665_PWR_MIXER, 0x083f);
+ regmap_write(rt5665->regmap, RT5665_PWR_DIG_1, 0x0180);
+ regmap_write(rt5665->regmap, RT5665_EJD_CTRL_1, 0x4040);
+ regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0000);
+ regmap_write(rt5665->regmap, RT5665_DIG_MISC, 0x0001);
+ regmap_write(rt5665->regmap, RT5665_MICBIAS_2, 0x0380);
+ regmap_write(rt5665->regmap, RT5665_GLB_CLK, 0x8000);
+ regmap_write(rt5665->regmap, RT5665_ADDA_CLK_1, 0x1000);
+ regmap_write(rt5665->regmap, RT5665_CHOP_DAC, 0x3030);
+ regmap_write(rt5665->regmap, RT5665_CALIB_ADC_CTRL, 0x3c05);
+ regmap_write(rt5665->regmap, RT5665_PWR_ANLG_1, 0xaa3e);
+ usleep_range(15000, 20000);
+ regmap_write(rt5665->regmap, RT5665_PWR_ANLG_1, 0xfe7e);
+ regmap_write(rt5665->regmap, RT5665_HP_CALIB_CTRL_2, 0x0321);
+
+ regmap_write(rt5665->regmap, RT5665_HP_CALIB_CTRL_1, 0xfc00);
+ count = 0;
+ while (true) {
+ regmap_read(rt5665->regmap, RT5665_HP_CALIB_STA_1, &value);
+ if (value & 0x8000)
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (count > 60) {
+ pr_err("HP Calibration Failure\n");
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+ regcache_cache_bypass(rt5665->regmap, false);
+ goto out_unlock;
+ }
+
+ count++;
+ }
+
+ regmap_write(rt5665->regmap, RT5665_MONO_AMP_CALIB_CTRL_1, 0x9e24);
+ count = 0;
+ while (true) {
+ regmap_read(rt5665->regmap, RT5665_MONO_AMP_CALIB_STA1, &value);
+ if (value & 0x8000)
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (count > 60) {
+ pr_err("MONO Calibration Failure\n");
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+ regcache_cache_bypass(rt5665->regmap, false);
+ goto out_unlock;
+ }
+
+ count++;
+ }
+
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+ regcache_cache_bypass(rt5665->regmap, false);
+
+ regcache_mark_dirty(rt5665->regmap);
+ regcache_sync(rt5665->regmap);
+
+ regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602);
+ regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120);
+
+out_unlock:
+ rt5665->calibration_done = true;
+ mutex_unlock(&rt5665->calibrate_mutex);
+}
+
+static void rt5665_calibrate_handler(struct work_struct *work)
+{
+ struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv,
+ calibrate_work.work);
+
+ while (!snd_soc_card_is_instantiated(rt5665->component->card)) {
+ pr_debug("%s\n", __func__);
+ usleep_range(10000, 15000);
+ }
+
+ rt5665_calibrate(rt5665);
+}
+
+static int rt5665_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5665_priv *rt5665;
+ int ret;
+ unsigned int val;
+
+ rt5665 = devm_kzalloc(&i2c->dev, sizeof(struct rt5665_priv),
+ GFP_KERNEL);
+
+ if (rt5665 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5665);
+
+ if (pdata)
+ rt5665->pdata = *pdata;
+ else
+ rt5665_parse_dt(rt5665, &i2c->dev);
+
+ ret = devm_regulator_bulk_get_enable(&i2c->dev, ARRAY_SIZE(rt5665_supply_names),
+ rt5665_supply_names);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5665->gpiod_ldo1_en)) {
+ dev_err(&i2c->dev, "Failed gpio request ldo1_en\n");
+ return PTR_ERR(rt5665->gpiod_ldo1_en);
+ }
+
+ /* Sleep for 300 ms miniumum */
+ usleep_range(300000, 350000);
+
+ rt5665->regmap = devm_regmap_init_i2c(i2c, &rt5665_regmap);
+ if (IS_ERR(rt5665->regmap)) {
+ ret = PTR_ERR(rt5665->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5665->regmap, RT5665_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5665\n", val);
+ return -ENODEV;
+ }
+
+ regmap_read(rt5665->regmap, RT5665_RESET, &val);
+ switch (val) {
+ case 0x0:
+ rt5665->id = CODEC_5666;
+ break;
+ case 0x3:
+ default:
+ rt5665->id = CODEC_5665;
+ break;
+ }
+
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+
+ /* line in diff mode*/
+ if (rt5665->pdata.in1_diff)
+ regmap_update_bits(rt5665->regmap, RT5665_IN1_IN2,
+ RT5665_IN1_DF_MASK, RT5665_IN1_DF_MASK);
+ if (rt5665->pdata.in2_diff)
+ regmap_update_bits(rt5665->regmap, RT5665_IN1_IN2,
+ RT5665_IN2_DF_MASK, RT5665_IN2_DF_MASK);
+ if (rt5665->pdata.in3_diff)
+ regmap_update_bits(rt5665->regmap, RT5665_IN3_IN4,
+ RT5665_IN3_DF_MASK, RT5665_IN3_DF_MASK);
+ if (rt5665->pdata.in4_diff)
+ regmap_update_bits(rt5665->regmap, RT5665_IN3_IN4,
+ RT5665_IN4_DF_MASK, RT5665_IN4_DF_MASK);
+
+ /* DMIC pin*/
+ if (rt5665->pdata.dmic1_data_pin != RT5665_DMIC1_NULL ||
+ rt5665->pdata.dmic2_data_pin != RT5665_DMIC2_NULL) {
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_2,
+ RT5665_GP9_PIN_MASK, RT5665_GP9_PIN_DMIC1_SCL);
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1,
+ RT5665_GP8_PIN_MASK, RT5665_GP8_PIN_DMIC2_SCL);
+ switch (rt5665->pdata.dmic1_data_pin) {
+ case RT5665_DMIC1_DATA_IN2N:
+ regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_1_DP_MASK, RT5665_DMIC_1_DP_IN2N);
+ break;
+
+ case RT5665_DMIC1_DATA_GPIO4:
+ regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_1_DP_MASK, RT5665_DMIC_1_DP_GPIO4);
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1,
+ RT5665_GP4_PIN_MASK, RT5665_GP4_PIN_DMIC1_SDA);
+ break;
+
+ default:
+ dev_dbg(&i2c->dev, "no DMIC1\n");
+ break;
+ }
+
+ switch (rt5665->pdata.dmic2_data_pin) {
+ case RT5665_DMIC2_DATA_IN2P:
+ regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_2_DP_MASK, RT5665_DMIC_2_DP_IN2P);
+ break;
+
+ case RT5665_DMIC2_DATA_GPIO5:
+ regmap_update_bits(rt5665->regmap,
+ RT5665_DMIC_CTRL_1,
+ RT5665_DMIC_2_DP_MASK,
+ RT5665_DMIC_2_DP_GPIO5);
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1,
+ RT5665_GP5_PIN_MASK, RT5665_GP5_PIN_DMIC2_SDA);
+ break;
+
+ default:
+ dev_dbg(&i2c->dev, "no DMIC2\n");
+ break;
+
+ }
+ }
+
+ regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002);
+ regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
+ 0xf000 | RT5665_VREF_POW_MASK, 0xe000 | RT5665_VREF_POW_REG);
+ /* Work around for pow_pump */
+ regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET,
+ RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS);
+
+ regmap_update_bits(rt5665->regmap, RT5665_HP_CHARGE_PUMP_1,
+ RT5665_PM_HP_MASK, RT5665_PM_HP_HV);
+
+ /* Set GPIO4,8 as input for combo jack */
+ if (rt5665->id == CODEC_5666) {
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_2,
+ RT5665_GP4_PF_MASK, RT5665_GP4_PF_IN);
+ regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_3,
+ RT5665_GP8_PF_MASK, RT5665_GP8_PF_IN);
+ }
+
+ /* Enhance performance*/
+ regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1,
+ RT5665_HP_DRIVER_MASK | RT5665_LDO1_DVO_MASK,
+ RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_12);
+
+ INIT_DELAYED_WORK(&rt5665->jack_detect_work,
+ rt5665_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5665->calibrate_work,
+ rt5665_calibrate_handler);
+ INIT_DELAYED_WORK(&rt5665->jd_check_work,
+ rt5665_jd_check_handler);
+
+ mutex_init(&rt5665->calibrate_mutex);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5665", rt5665);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5665,
+ rt5665_dai, ARRAY_SIZE(rt5665_dai));
+}
+
+static void rt5665_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5665_priv *rt5665 = i2c_get_clientdata(client);
+
+ regmap_write(rt5665->regmap, RT5665_RESET, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt5665_of_match[] = {
+ {.compatible = "realtek,rt5665"},
+ {.compatible = "realtek,rt5666"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5665_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5665_acpi_match[] = {
+ { "10EC5665" },
+ { "10EC5666" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match);
+#endif
+
+static struct i2c_driver rt5665_i2c_driver = {
+ .driver = {
+ .name = "rt5665",
+ .of_match_table = of_match_ptr(rt5665_of_match),
+ .acpi_match_table = ACPI_PTR(rt5665_acpi_match),
+ },
+ .probe = rt5665_i2c_probe,
+ .shutdown = rt5665_i2c_shutdown,
+ .id_table = rt5665_i2c_id,
+};
+module_i2c_driver(rt5665_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5665 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
new file mode 100644
index 000000000000..089e4078d37a
--- /dev/null
+++ b/sound/soc/codecs/rt5665.h
@@ -0,0 +1,2002 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5665.h -- RT5665/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5665_H__
+#define __RT5665_H__
+
+#include <sound/rt5665.h>
+
+#define DEVICE_ID 0x6451
+
+/* Info */
+#define RT5665_RESET 0x0000
+#define RT5665_VENDOR_ID 0x00fd
+#define RT5665_VENDOR_ID_1 0x00fe
+#define RT5665_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5665_LOUT 0x0001
+#define RT5665_HP_CTRL_1 0x0002
+#define RT5665_HP_CTRL_2 0x0003
+#define RT5665_MONO_OUT 0x0004
+#define RT5665_HPL_GAIN 0x0005
+#define RT5665_HPR_GAIN 0x0006
+#define RT5665_MONO_GAIN 0x0007
+
+/* I/O - Input */
+#define RT5665_CAL_BST_CTRL 0x000a
+#define RT5665_CBJ_BST_CTRL 0x000b
+#define RT5665_IN1_IN2 0x000c
+#define RT5665_IN3_IN4 0x000d
+#define RT5665_INL1_INR1_VOL 0x000f
+/* I/O - Speaker */
+#define RT5665_EJD_CTRL_1 0x0010
+#define RT5665_EJD_CTRL_2 0x0011
+#define RT5665_EJD_CTRL_3 0x0012
+#define RT5665_EJD_CTRL_4 0x0013
+#define RT5665_EJD_CTRL_5 0x0014
+#define RT5665_EJD_CTRL_6 0x0015
+#define RT5665_EJD_CTRL_7 0x0016
+/* I/O - ADC/DAC/DMIC */
+#define RT5665_DAC2_CTRL 0x0017
+#define RT5665_DAC2_DIG_VOL 0x0018
+#define RT5665_DAC1_DIG_VOL 0x0019
+#define RT5665_DAC3_DIG_VOL 0x001a
+#define RT5665_DAC3_CTRL 0x001b
+#define RT5665_STO1_ADC_DIG_VOL 0x001c
+#define RT5665_MONO_ADC_DIG_VOL 0x001d
+#define RT5665_STO2_ADC_DIG_VOL 0x001e
+#define RT5665_STO1_ADC_BOOST 0x001f
+#define RT5665_MONO_ADC_BOOST 0x0020
+#define RT5665_STO2_ADC_BOOST 0x0021
+#define RT5665_HP_IMP_GAIN_1 0x0022
+#define RT5665_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5665_STO1_ADC_MIXER 0x0026
+#define RT5665_MONO_ADC_MIXER 0x0027
+#define RT5665_STO2_ADC_MIXER 0x0028
+#define RT5665_AD_DA_MIXER 0x0029
+#define RT5665_STO1_DAC_MIXER 0x002a
+#define RT5665_MONO_DAC_MIXER 0x002b
+#define RT5665_STO2_DAC_MIXER 0x002c
+#define RT5665_A_DAC1_MUX 0x002d
+#define RT5665_A_DAC2_MUX 0x002e
+#define RT5665_DIG_INF2_DATA 0x002f
+#define RT5665_DIG_INF3_DATA 0x0030
+/* Mixer - PDM */
+#define RT5665_PDM_OUT_CTRL 0x0031
+#define RT5665_PDM_DATA_CTRL_1 0x0032
+#define RT5665_PDM_DATA_CTRL_2 0x0033
+#define RT5665_PDM_DATA_CTRL_3 0x0034
+#define RT5665_PDM_DATA_CTRL_4 0x0035
+/* Mixer - ADC */
+#define RT5665_REC1_GAIN 0x003a
+#define RT5665_REC1_L1_MIXER 0x003b
+#define RT5665_REC1_L2_MIXER 0x003c
+#define RT5665_REC1_R1_MIXER 0x003d
+#define RT5665_REC1_R2_MIXER 0x003e
+#define RT5665_REC2_GAIN 0x003f
+#define RT5665_REC2_L1_MIXER 0x0040
+#define RT5665_REC2_L2_MIXER 0x0041
+#define RT5665_REC2_R1_MIXER 0x0042
+#define RT5665_REC2_R2_MIXER 0x0043
+#define RT5665_CAL_REC 0x0044
+/* Mixer - DAC */
+#define RT5665_ALC_BACK_GAIN 0x0049
+#define RT5665_MONOMIX_GAIN 0x004a
+#define RT5665_MONOMIX_IN_GAIN 0x004b
+#define RT5665_OUT_L_GAIN 0x004d
+#define RT5665_OUT_L_MIXER 0x004e
+#define RT5665_OUT_R_GAIN 0x004f
+#define RT5665_OUT_R_MIXER 0x0050
+#define RT5665_LOUT_MIXER 0x0052
+/* Power */
+#define RT5665_PWR_DIG_1 0x0061
+#define RT5665_PWR_DIG_2 0x0062
+#define RT5665_PWR_ANLG_1 0x0063
+#define RT5665_PWR_ANLG_2 0x0064
+#define RT5665_PWR_ANLG_3 0x0065
+#define RT5665_PWR_MIXER 0x0066
+#define RT5665_PWR_VOL 0x0067
+/* Clock Detect */
+#define RT5665_CLK_DET 0x006b
+/* Filter */
+#define RT5665_HPF_CTRL1 0x006d
+/* DMIC */
+#define RT5665_DMIC_CTRL_1 0x006e
+#define RT5665_DMIC_CTRL_2 0x006f
+/* Format - ADC/DAC */
+#define RT5665_I2S1_SDP 0x0070
+#define RT5665_I2S2_SDP 0x0071
+#define RT5665_I2S3_SDP 0x0072
+#define RT5665_ADDA_CLK_1 0x0073
+#define RT5665_ADDA_CLK_2 0x0074
+#define RT5665_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5665_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5665_TDM_CTRL_1 0x0078
+#define RT5665_TDM_CTRL_2 0x0079
+#define RT5665_TDM_CTRL_3 0x007a
+#define RT5665_TDM_CTRL_4 0x007b
+#define RT5665_TDM_CTRL_5 0x007c
+#define RT5665_TDM_CTRL_6 0x007d
+#define RT5665_TDM_CTRL_7 0x007e
+#define RT5665_TDM_CTRL_8 0x007f
+/* Function - Analog */
+#define RT5665_GLB_CLK 0x0080
+#define RT5665_PLL_CTRL_1 0x0081
+#define RT5665_PLL_CTRL_2 0x0082
+#define RT5665_ASRC_1 0x0083
+#define RT5665_ASRC_2 0x0084
+#define RT5665_ASRC_3 0x0085
+#define RT5665_ASRC_4 0x0086
+#define RT5665_ASRC_5 0x0087
+#define RT5665_ASRC_6 0x0088
+#define RT5665_ASRC_7 0x0089
+#define RT5665_ASRC_8 0x008a
+#define RT5665_ASRC_9 0x008b
+#define RT5665_ASRC_10 0x008c
+#define RT5665_DEPOP_1 0x008e
+#define RT5665_DEPOP_2 0x008f
+#define RT5665_HP_CHARGE_PUMP_1 0x0091
+#define RT5665_HP_CHARGE_PUMP_2 0x0092
+#define RT5665_MICBIAS_1 0x0093
+#define RT5665_MICBIAS_2 0x0094
+#define RT5665_ASRC_12 0x0098
+#define RT5665_ASRC_13 0x0099
+#define RT5665_ASRC_14 0x009a
+#define RT5665_RC_CLK_CTRL 0x009f
+#define RT5665_I2S_M_CLK_CTRL_1 0x00a0
+#define RT5665_I2S2_F_DIV_CTRL_1 0x00a1
+#define RT5665_I2S2_F_DIV_CTRL_2 0x00a2
+#define RT5665_I2S3_F_DIV_CTRL_1 0x00a3
+#define RT5665_I2S3_F_DIV_CTRL_2 0x00a4
+/* Function - Digital */
+#define RT5665_EQ_CTRL_1 0x00ae
+#define RT5665_EQ_CTRL_2 0x00af
+#define RT5665_IRQ_CTRL_1 0x00b6
+#define RT5665_IRQ_CTRL_2 0x00b7
+#define RT5665_IRQ_CTRL_3 0x00b8
+#define RT5665_IRQ_CTRL_4 0x00b9
+#define RT5665_IRQ_CTRL_5 0x00ba
+#define RT5665_IRQ_CTRL_6 0x00bb
+#define RT5665_INT_ST_1 0x00be
+#define RT5665_GPIO_CTRL_1 0x00c0
+#define RT5665_GPIO_CTRL_2 0x00c1
+#define RT5665_GPIO_CTRL_3 0x00c2
+#define RT5665_GPIO_CTRL_4 0x00c3
+#define RT5665_GPIO_STA 0x00c4
+#define RT5665_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5665_HP_AMP_DET_CTRL_2 0x00d1
+#define RT5665_MID_HP_AMP_DET 0x00d3
+#define RT5665_LOW_HP_AMP_DET 0x00d4
+#define RT5665_SV_ZCD_1 0x00d9
+#define RT5665_SV_ZCD_2 0x00da
+#define RT5665_IL_CMD_1 0x00db
+#define RT5665_IL_CMD_2 0x00dc
+#define RT5665_IL_CMD_3 0x00dd
+#define RT5665_IL_CMD_4 0x00de
+#define RT5665_4BTN_IL_CMD_1 0x00df
+#define RT5665_4BTN_IL_CMD_2 0x00e0
+#define RT5665_4BTN_IL_CMD_3 0x00e1
+#define RT5665_PSV_IL_CMD_1 0x00e2
+
+#define RT5665_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5665_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5665_ADC_MONO_HP_CTRL_1 0x00ec
+#define RT5665_ADC_MONO_HP_CTRL_2 0x00ed
+#define RT5665_ADC_STO2_HP_CTRL_1 0x00ee
+#define RT5665_ADC_STO2_HP_CTRL_2 0x00ef
+#define RT5665_AJD1_CTRL 0x00f0
+#define RT5665_JD1_THD 0x00f1
+#define RT5665_JD2_THD 0x00f2
+#define RT5665_JD_CTRL_1 0x00f6
+#define RT5665_JD_CTRL_2 0x00f7
+#define RT5665_JD_CTRL_3 0x00f8
+/* General Control */
+#define RT5665_DIG_MISC 0x00fa
+#define RT5665_DUMMY_2 0x00fb
+#define RT5665_DUMMY_3 0x00fc
+
+#define RT5665_DAC_ADC_DIG_VOL1 0x0100
+#define RT5665_DAC_ADC_DIG_VOL2 0x0101
+#define RT5665_BIAS_CUR_CTRL_1 0x010a
+#define RT5665_BIAS_CUR_CTRL_2 0x010b
+#define RT5665_BIAS_CUR_CTRL_3 0x010c
+#define RT5665_BIAS_CUR_CTRL_4 0x010d
+#define RT5665_BIAS_CUR_CTRL_5 0x010e
+#define RT5665_BIAS_CUR_CTRL_6 0x010f
+#define RT5665_BIAS_CUR_CTRL_7 0x0110
+#define RT5665_BIAS_CUR_CTRL_8 0x0111
+#define RT5665_BIAS_CUR_CTRL_9 0x0112
+#define RT5665_BIAS_CUR_CTRL_10 0x0113
+#define RT5665_VREF_REC_OP_FB_CAP_CTRL 0x0117
+#define RT5665_CHARGE_PUMP_1 0x0125
+#define RT5665_DIG_IN_CTRL_1 0x0132
+#define RT5665_DIG_IN_CTRL_2 0x0133
+#define RT5665_PAD_DRIVING_CTRL 0x0137
+#define RT5665_SOFT_RAMP_DEPOP 0x0138
+#define RT5665_PLL 0x0139
+#define RT5665_CHOP_DAC 0x013a
+#define RT5665_CHOP_ADC 0x013b
+#define RT5665_CALIB_ADC_CTRL 0x013c
+#define RT5665_VOL_TEST 0x013f
+#define RT5665_TEST_MODE_CTRL_1 0x0145
+#define RT5665_TEST_MODE_CTRL_2 0x0146
+#define RT5665_TEST_MODE_CTRL_3 0x0147
+#define RT5665_TEST_MODE_CTRL_4 0x0148
+#define RT5665_BASSBACK_CTRL 0x0150
+#define RT5665_STO_NG2_CTRL_1 0x0160
+#define RT5665_STO_NG2_CTRL_2 0x0161
+#define RT5665_STO_NG2_CTRL_3 0x0162
+#define RT5665_STO_NG2_CTRL_4 0x0163
+#define RT5665_STO_NG2_CTRL_5 0x0164
+#define RT5665_STO_NG2_CTRL_6 0x0165
+#define RT5665_STO_NG2_CTRL_7 0x0166
+#define RT5665_STO_NG2_CTRL_8 0x0167
+#define RT5665_MONO_NG2_CTRL_1 0x0170
+#define RT5665_MONO_NG2_CTRL_2 0x0171
+#define RT5665_MONO_NG2_CTRL_3 0x0172
+#define RT5665_MONO_NG2_CTRL_4 0x0173
+#define RT5665_MONO_NG2_CTRL_5 0x0174
+#define RT5665_MONO_NG2_CTRL_6 0x0175
+#define RT5665_STO1_DAC_SIL_DET 0x0190
+#define RT5665_MONOL_DAC_SIL_DET 0x0191
+#define RT5665_MONOR_DAC_SIL_DET 0x0192
+#define RT5665_STO2_DAC_SIL_DET 0x0193
+#define RT5665_SIL_PSV_CTRL1 0x0194
+#define RT5665_SIL_PSV_CTRL2 0x0195
+#define RT5665_SIL_PSV_CTRL3 0x0196
+#define RT5665_SIL_PSV_CTRL4 0x0197
+#define RT5665_SIL_PSV_CTRL5 0x0198
+#define RT5665_SIL_PSV_CTRL6 0x0199
+#define RT5665_MONO_AMP_CALIB_CTRL_1 0x01a0
+#define RT5665_MONO_AMP_CALIB_CTRL_2 0x01a1
+#define RT5665_MONO_AMP_CALIB_CTRL_3 0x01a2
+#define RT5665_MONO_AMP_CALIB_CTRL_4 0x01a3
+#define RT5665_MONO_AMP_CALIB_CTRL_5 0x01a4
+#define RT5665_MONO_AMP_CALIB_CTRL_6 0x01a5
+#define RT5665_MONO_AMP_CALIB_CTRL_7 0x01a6
+#define RT5665_MONO_AMP_CALIB_STA1 0x01a7
+#define RT5665_MONO_AMP_CALIB_STA2 0x01a8
+#define RT5665_MONO_AMP_CALIB_STA3 0x01a9
+#define RT5665_MONO_AMP_CALIB_STA4 0x01aa
+#define RT5665_MONO_AMP_CALIB_STA6 0x01ab
+#define RT5665_HP_IMP_SENS_CTRL_01 0x01b5
+#define RT5665_HP_IMP_SENS_CTRL_02 0x01b6
+#define RT5665_HP_IMP_SENS_CTRL_03 0x01b7
+#define RT5665_HP_IMP_SENS_CTRL_04 0x01b8
+#define RT5665_HP_IMP_SENS_CTRL_05 0x01b9
+#define RT5665_HP_IMP_SENS_CTRL_06 0x01ba
+#define RT5665_HP_IMP_SENS_CTRL_07 0x01bb
+#define RT5665_HP_IMP_SENS_CTRL_08 0x01bc
+#define RT5665_HP_IMP_SENS_CTRL_09 0x01bd
+#define RT5665_HP_IMP_SENS_CTRL_10 0x01be
+#define RT5665_HP_IMP_SENS_CTRL_11 0x01bf
+#define RT5665_HP_IMP_SENS_CTRL_12 0x01c0
+#define RT5665_HP_IMP_SENS_CTRL_13 0x01c1
+#define RT5665_HP_IMP_SENS_CTRL_14 0x01c2
+#define RT5665_HP_IMP_SENS_CTRL_15 0x01c3
+#define RT5665_HP_IMP_SENS_CTRL_16 0x01c4
+#define RT5665_HP_IMP_SENS_CTRL_17 0x01c5
+#define RT5665_HP_IMP_SENS_CTRL_18 0x01c6
+#define RT5665_HP_IMP_SENS_CTRL_19 0x01c7
+#define RT5665_HP_IMP_SENS_CTRL_20 0x01c8
+#define RT5665_HP_IMP_SENS_CTRL_21 0x01c9
+#define RT5665_HP_IMP_SENS_CTRL_22 0x01ca
+#define RT5665_HP_IMP_SENS_CTRL_23 0x01cb
+#define RT5665_HP_IMP_SENS_CTRL_24 0x01cc
+#define RT5665_HP_IMP_SENS_CTRL_25 0x01cd
+#define RT5665_HP_IMP_SENS_CTRL_26 0x01ce
+#define RT5665_HP_IMP_SENS_CTRL_27 0x01cf
+#define RT5665_HP_IMP_SENS_CTRL_28 0x01d0
+#define RT5665_HP_IMP_SENS_CTRL_29 0x01d1
+#define RT5665_HP_IMP_SENS_CTRL_30 0x01d2
+#define RT5665_HP_IMP_SENS_CTRL_31 0x01d3
+#define RT5665_HP_IMP_SENS_CTRL_32 0x01d4
+#define RT5665_HP_IMP_SENS_CTRL_33 0x01d5
+#define RT5665_HP_IMP_SENS_CTRL_34 0x01d6
+#define RT5665_HP_LOGIC_CTRL_1 0x01da
+#define RT5665_HP_LOGIC_CTRL_2 0x01db
+#define RT5665_HP_LOGIC_CTRL_3 0x01dc
+#define RT5665_HP_CALIB_CTRL_1 0x01de
+#define RT5665_HP_CALIB_CTRL_2 0x01df
+#define RT5665_HP_CALIB_CTRL_3 0x01e0
+#define RT5665_HP_CALIB_CTRL_4 0x01e1
+#define RT5665_HP_CALIB_CTRL_5 0x01e2
+#define RT5665_HP_CALIB_CTRL_6 0x01e3
+#define RT5665_HP_CALIB_CTRL_7 0x01e4
+#define RT5665_HP_CALIB_CTRL_9 0x01e6
+#define RT5665_HP_CALIB_CTRL_10 0x01e7
+#define RT5665_HP_CALIB_CTRL_11 0x01e8
+#define RT5665_HP_CALIB_STA_1 0x01ea
+#define RT5665_HP_CALIB_STA_2 0x01eb
+#define RT5665_HP_CALIB_STA_3 0x01ec
+#define RT5665_HP_CALIB_STA_4 0x01ed
+#define RT5665_HP_CALIB_STA_5 0x01ee
+#define RT5665_HP_CALIB_STA_6 0x01ef
+#define RT5665_HP_CALIB_STA_7 0x01f0
+#define RT5665_HP_CALIB_STA_8 0x01f1
+#define RT5665_HP_CALIB_STA_9 0x01f2
+#define RT5665_HP_CALIB_STA_10 0x01f3
+#define RT5665_HP_CALIB_STA_11 0x01f4
+#define RT5665_PGM_TAB_CTRL1 0x0200
+#define RT5665_PGM_TAB_CTRL2 0x0201
+#define RT5665_PGM_TAB_CTRL3 0x0202
+#define RT5665_PGM_TAB_CTRL4 0x0203
+#define RT5665_PGM_TAB_CTRL5 0x0204
+#define RT5665_PGM_TAB_CTRL6 0x0205
+#define RT5665_PGM_TAB_CTRL7 0x0206
+#define RT5665_PGM_TAB_CTRL8 0x0207
+#define RT5665_PGM_TAB_CTRL9 0x0208
+#define RT5665_SAR_IL_CMD_1 0x0210
+#define RT5665_SAR_IL_CMD_2 0x0211
+#define RT5665_SAR_IL_CMD_3 0x0212
+#define RT5665_SAR_IL_CMD_4 0x0213
+#define RT5665_SAR_IL_CMD_5 0x0214
+#define RT5665_SAR_IL_CMD_6 0x0215
+#define RT5665_SAR_IL_CMD_7 0x0216
+#define RT5665_SAR_IL_CMD_8 0x0217
+#define RT5665_SAR_IL_CMD_9 0x0218
+#define RT5665_SAR_IL_CMD_10 0x0219
+#define RT5665_SAR_IL_CMD_11 0x021a
+#define RT5665_SAR_IL_CMD_12 0x021b
+#define RT5665_DRC1_CTRL_0 0x02ff
+#define RT5665_DRC1_CTRL_1 0x0300
+#define RT5665_DRC1_CTRL_2 0x0301
+#define RT5665_DRC1_CTRL_3 0x0302
+#define RT5665_DRC1_CTRL_4 0x0303
+#define RT5665_DRC1_CTRL_5 0x0304
+#define RT5665_DRC1_CTRL_6 0x0305
+#define RT5665_DRC1_HARD_LMT_CTRL_1 0x0306
+#define RT5665_DRC1_HARD_LMT_CTRL_2 0x0307
+#define RT5665_DRC1_PRIV_1 0x0310
+#define RT5665_DRC1_PRIV_2 0x0311
+#define RT5665_DRC1_PRIV_3 0x0312
+#define RT5665_DRC1_PRIV_4 0x0313
+#define RT5665_DRC1_PRIV_5 0x0314
+#define RT5665_DRC1_PRIV_6 0x0315
+#define RT5665_DRC1_PRIV_7 0x0316
+#define RT5665_DRC1_PRIV_8 0x0317
+#define RT5665_ALC_PGA_CTRL_1 0x0330
+#define RT5665_ALC_PGA_CTRL_2 0x0331
+#define RT5665_ALC_PGA_CTRL_3 0x0332
+#define RT5665_ALC_PGA_CTRL_4 0x0333
+#define RT5665_ALC_PGA_CTRL_5 0x0334
+#define RT5665_ALC_PGA_CTRL_6 0x0335
+#define RT5665_ALC_PGA_CTRL_7 0x0336
+#define RT5665_ALC_PGA_CTRL_8 0x0337
+#define RT5665_ALC_PGA_STA_1 0x0338
+#define RT5665_ALC_PGA_STA_2 0x0339
+#define RT5665_ALC_PGA_STA_3 0x033a
+#define RT5665_EQ_AUTO_RCV_CTRL1 0x03c0
+#define RT5665_EQ_AUTO_RCV_CTRL2 0x03c1
+#define RT5665_EQ_AUTO_RCV_CTRL3 0x03c2
+#define RT5665_EQ_AUTO_RCV_CTRL4 0x03c3
+#define RT5665_EQ_AUTO_RCV_CTRL5 0x03c4
+#define RT5665_EQ_AUTO_RCV_CTRL6 0x03c5
+#define RT5665_EQ_AUTO_RCV_CTRL7 0x03c6
+#define RT5665_EQ_AUTO_RCV_CTRL8 0x03c7
+#define RT5665_EQ_AUTO_RCV_CTRL9 0x03c8
+#define RT5665_EQ_AUTO_RCV_CTRL10 0x03c9
+#define RT5665_EQ_AUTO_RCV_CTRL11 0x03ca
+#define RT5665_EQ_AUTO_RCV_CTRL12 0x03cb
+#define RT5665_EQ_AUTO_RCV_CTRL13 0x03cc
+#define RT5665_ADC_L_EQ_LPF1_A1 0x03d0
+#define RT5665_R_EQ_LPF1_A1 0x03d1
+#define RT5665_L_EQ_LPF1_H0 0x03d2
+#define RT5665_R_EQ_LPF1_H0 0x03d3
+#define RT5665_L_EQ_BPF1_A1 0x03d4
+#define RT5665_R_EQ_BPF1_A1 0x03d5
+#define RT5665_L_EQ_BPF1_A2 0x03d6
+#define RT5665_R_EQ_BPF1_A2 0x03d7
+#define RT5665_L_EQ_BPF1_H0 0x03d8
+#define RT5665_R_EQ_BPF1_H0 0x03d9
+#define RT5665_L_EQ_BPF2_A1 0x03da
+#define RT5665_R_EQ_BPF2_A1 0x03db
+#define RT5665_L_EQ_BPF2_A2 0x03dc
+#define RT5665_R_EQ_BPF2_A2 0x03dd
+#define RT5665_L_EQ_BPF2_H0 0x03de
+#define RT5665_R_EQ_BPF2_H0 0x03df
+#define RT5665_L_EQ_BPF3_A1 0x03e0
+#define RT5665_R_EQ_BPF3_A1 0x03e1
+#define RT5665_L_EQ_BPF3_A2 0x03e2
+#define RT5665_R_EQ_BPF3_A2 0x03e3
+#define RT5665_L_EQ_BPF3_H0 0x03e4
+#define RT5665_R_EQ_BPF3_H0 0x03e5
+#define RT5665_L_EQ_BPF4_A1 0x03e6
+#define RT5665_R_EQ_BPF4_A1 0x03e7
+#define RT5665_L_EQ_BPF4_A2 0x03e8
+#define RT5665_R_EQ_BPF4_A2 0x03e9
+#define RT5665_L_EQ_BPF4_H0 0x03ea
+#define RT5665_R_EQ_BPF4_H0 0x03eb
+#define RT5665_L_EQ_HPF1_A1 0x03ec
+#define RT5665_R_EQ_HPF1_A1 0x03ed
+#define RT5665_L_EQ_HPF1_H0 0x03ee
+#define RT5665_R_EQ_HPF1_H0 0x03ef
+#define RT5665_L_EQ_PRE_VOL 0x03f0
+#define RT5665_R_EQ_PRE_VOL 0x03f1
+#define RT5665_L_EQ_POST_VOL 0x03f2
+#define RT5665_R_EQ_POST_VOL 0x03f3
+#define RT5665_SCAN_MODE_CTRL 0x07f0
+#define RT5665_I2C_MODE 0x07fa
+
+
+
+/* global definition */
+#define RT5665_L_MUTE (0x1 << 15)
+#define RT5665_L_MUTE_SFT 15
+#define RT5665_VOL_L_MUTE (0x1 << 14)
+#define RT5665_VOL_L_SFT 14
+#define RT5665_R_MUTE (0x1 << 7)
+#define RT5665_R_MUTE_SFT 7
+#define RT5665_VOL_R_MUTE (0x1 << 6)
+#define RT5665_VOL_R_SFT 6
+#define RT5665_L_VOL_MASK (0x3f << 8)
+#define RT5665_L_VOL_SFT 8
+#define RT5665_R_VOL_MASK (0x3f)
+#define RT5665_R_VOL_SFT 0
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5665_G_HP (0xf << 8)
+#define RT5665_G_HP_SFT 8
+#define RT5665_G_STO_DA_DMIX (0xf)
+#define RT5665_G_STO_DA_SFT 0
+
+/* CBJ Control (0x000b) */
+#define RT5665_BST_CBJ_MASK (0xf << 8)
+#define RT5665_BST_CBJ_SFT 8
+
+/* IN1/IN2 Control (0x000c) */
+#define RT5665_IN1_DF_MASK (0x1 << 15)
+#define RT5665_IN1_DF 15
+#define RT5665_BST1_MASK (0x7f << 8)
+#define RT5665_BST1_SFT 8
+#define RT5665_IN2_DF_MASK (0x1 << 7)
+#define RT5665_IN2_DF 7
+#define RT5665_BST2_MASK (0x7f)
+#define RT5665_BST2_SFT 0
+
+/* IN3/IN4 Control (0x000d) */
+#define RT5665_IN3_DF_MASK (0x1 << 15)
+#define RT5665_IN3_DF 15
+#define RT5665_BST3_MASK (0x7f << 8)
+#define RT5665_BST3_SFT 8
+#define RT5665_IN4_DF_MASK (0x1 << 7)
+#define RT5665_IN4_DF 7
+#define RT5665_BST4_MASK (0x7f)
+#define RT5665_BST4_SFT 0
+
+/* INL and INR Volume Control (0x000f) */
+#define RT5665_INL_VOL_MASK (0x1f << 8)
+#define RT5665_INL_VOL_SFT 8
+#define RT5665_INR_VOL_MASK (0x1f)
+#define RT5665_INR_VOL_SFT 0
+
+/* Embeeded Jack and Type Detection Control 1 (0x0010) */
+#define RT5665_EMB_JD_EN (0x1 << 15)
+#define RT5665_EMB_JD_EN_SFT 15
+#define RT5665_JD_MODE (0x1 << 13)
+#define RT5665_JD_MODE_SFT 13
+#define RT5665_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5665_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5665_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5665_EXT_JD_DIG (0x1 << 9)
+#define RT5665_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5665_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5665_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5665_VREF_POW_MASK (0x1 << 6)
+#define RT5665_VREF_POW_FSM (0x0 << 6)
+#define RT5665_VREF_POW_REG (0x1 << 6)
+#define RT5665_MB1_PATH_MASK (0x1 << 5)
+#define RT5665_CTRL_MB1_REG (0x1 << 5)
+#define RT5665_CTRL_MB1_FSM (0x0 << 5)
+#define RT5665_MB2_PATH_MASK (0x1 << 4)
+#define RT5665_CTRL_MB2_REG (0x1 << 4)
+#define RT5665_CTRL_MB2_FSM (0x0 << 4)
+#define RT5665_TRIG_JD_MASK (0x1 << 3)
+#define RT5665_TRIG_JD_HIGH (0x1 << 3)
+#define RT5665_TRIG_JD_LOW (0x0 << 3)
+
+/* Embeeded Jack and Type Detection Control 2 (0x0011) */
+#define RT5665_EXT_JD_SRC (0x7 << 4)
+#define RT5665_EXT_JD_SRC_SFT 4
+#define RT5665_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5665_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5665_EXT_JD_SRC_JD1_1 (0x2 << 4)
+#define RT5665_EXT_JD_SRC_JD1_2 (0x3 << 4)
+#define RT5665_EXT_JD_SRC_JD2 (0x4 << 4)
+#define RT5665_EXT_JD_SRC_JD3 (0x5 << 4)
+#define RT5665_EXT_JD_SRC_MANUAL (0x6 << 4)
+
+/* Combo Jack and Type Detection Control 4 (0x0013) */
+#define RT5665_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5665_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5665_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5665_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5665_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5665_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* Slience Detection Control (0x0015) */
+#define RT5665_SIL_DET_MASK (0x1 << 15)
+#define RT5665_SIL_DET_DIS (0x0 << 15)
+#define RT5665_SIL_DET_EN (0x1 << 15)
+
+/* DAC2 Control (0x0017) */
+#define RT5665_M_DAC2_L_VOL (0x1 << 13)
+#define RT5665_M_DAC2_L_VOL_SFT 13
+#define RT5665_M_DAC2_R_VOL (0x1 << 12)
+#define RT5665_M_DAC2_R_VOL_SFT 12
+#define RT5665_DAC_L2_SEL_MASK (0x7 << 4)
+#define RT5665_DAC_L2_SEL_SFT 4
+#define RT5665_DAC_R2_SEL_MASK (0x7 << 0)
+#define RT5665_DAC_R2_SEL_SFT 0
+
+/* Sidetone Control (0x0018) */
+#define RT5665_ST_SEL_MASK (0x7 << 9)
+#define RT5665_ST_SEL_SFT 9
+#define RT5665_ST_EN (0x1 << 6)
+#define RT5665_ST_EN_SFT 6
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5665_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5665_DAC_L1_VOL_SFT 8
+#define RT5665_DAC_R1_VOL_MASK (0xff)
+#define RT5665_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x001a) */
+#define RT5665_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5665_DAC_L2_VOL_SFT 8
+#define RT5665_DAC_R2_VOL_MASK (0xff)
+#define RT5665_DAC_R2_VOL_SFT 0
+
+/* DAC3 Control (0x001b) */
+#define RT5665_M_DAC3_L_VOL (0x1 << 13)
+#define RT5665_M_DAC3_L_VOL_SFT 13
+#define RT5665_M_DAC3_R_VOL (0x1 << 12)
+#define RT5665_M_DAC3_R_VOL_SFT 12
+#define RT5665_DAC_L3_SEL_MASK (0x7 << 4)
+#define RT5665_DAC_L3_SEL_SFT 4
+#define RT5665_DAC_R3_SEL_MASK (0x7 << 0)
+#define RT5665_DAC_R3_SEL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5665_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5665_ADC_L_VOL_SFT 8
+#define RT5665_ADC_R_VOL_MASK (0x7f)
+#define RT5665_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x001d) */
+#define RT5665_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5665_MONO_ADC_L_VOL_SFT 8
+#define RT5665_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5665_MONO_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5665_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5665_STO1_ADC_L_BST_SFT 14
+#define RT5665_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5665_STO1_ADC_R_BST_SFT 12
+
+/* Mono ADC Boost Gain Control (0x0020) */
+#define RT5665_MONO_ADC_L_BST_MASK (0x3 << 14)
+#define RT5665_MONO_ADC_L_BST_SFT 14
+#define RT5665_MONO_ADC_R_BST_MASK (0x3 << 12)
+#define RT5665_MONO_ADC_R_BST_SFT 12
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5665_STO2_ADC_L_BST_MASK (0x3 << 14)
+#define RT5665_STO2_ADC_L_BST_SFT 14
+#define RT5665_STO2_ADC_R_BST_MASK (0x3 << 12)
+#define RT5665_STO2_ADC_R_BST_SFT 12
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5665_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5665_M_STO1_ADC_L1_SFT 15
+#define RT5665_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5665_M_STO1_ADC_L2_SFT 14
+#define RT5665_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5665_STO1_ADC1L_SRC_SFT 13
+#define RT5665_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5665_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5665_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5665_STO1_ADC2L_SRC_SFT 12
+#define RT5665_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5665_STO1_ADCL_SRC_SFT 10
+#define RT5665_STO1_DD_L_SRC_MASK (0x1 << 9)
+#define RT5665_STO1_DD_L_SRC_SFT 9
+#define RT5665_STO1_DMIC_SRC_MASK (0x1 << 8)
+#define RT5665_STO1_DMIC_SRC_SFT 8
+#define RT5665_STO1_DMIC_SRC_DMIC2 (0x1 << 8)
+#define RT5665_STO1_DMIC_SRC_DMIC1 (0x0 << 8)
+#define RT5665_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5665_M_STO1_ADC_R1_SFT 7
+#define RT5665_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5665_M_STO1_ADC_R2_SFT 6
+#define RT5665_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5665_STO1_ADC1R_SRC_SFT 5
+#define RT5665_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5665_STO1_ADC2R_SRC_SFT 4
+#define RT5665_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5665_STO1_ADCR_SRC_SFT 2
+#define RT5665_STO1_DD_R_SRC_MASK (0x3)
+#define RT5665_STO1_DD_R_SRC_SFT 0
+
+
+/* Mono1 ADC Mixer control (0x0027) */
+#define RT5665_M_MONO_ADC_L1 (0x1 << 15)
+#define RT5665_M_MONO_ADC_L1_SFT 15
+#define RT5665_M_MONO_ADC_L2 (0x1 << 14)
+#define RT5665_M_MONO_ADC_L2_SFT 14
+#define RT5665_MONO_ADC_L1_SRC_MASK (0x1 << 13)
+#define RT5665_MONO_ADC_L1_SRC_SFT 13
+#define RT5665_MONO_ADC_L2_SRC_MASK (0x1 << 12)
+#define RT5665_MONO_ADC_L2_SRC_SFT 12
+#define RT5665_MONO_ADC_L_SRC_MASK (0x3 << 10)
+#define RT5665_MONO_ADC_L_SRC_SFT 10
+#define RT5665_MONO_DD_L_SRC_MASK (0x1 << 9)
+#define RT5665_MONO_DD_L_SRC_SFT 9
+#define RT5665_MONO_DMIC_L_SRC_MASK (0x1 << 8)
+#define RT5665_MONO_DMIC_L_SRC_SFT 8
+#define RT5665_M_MONO_ADC_R1 (0x1 << 7)
+#define RT5665_M_MONO_ADC_R1_SFT 7
+#define RT5665_M_MONO_ADC_R2 (0x1 << 6)
+#define RT5665_M_MONO_ADC_R2_SFT 6
+#define RT5665_MONO_ADC_R1_SRC_MASK (0x1 << 5)
+#define RT5665_MONO_ADC_R1_SRC_SFT 5
+#define RT5665_MONO_ADC_R2_SRC_MASK (0x1 << 4)
+#define RT5665_MONO_ADC_R2_SRC_SFT 4
+#define RT5665_MONO_ADC_R_SRC_MASK (0x3 << 2)
+#define RT5665_MONO_ADC_R_SRC_SFT 2
+#define RT5665_MONO_DD_R_SRC_MASK (0x1 << 1)
+#define RT5665_MONO_DD_R_SRC_SFT 1
+#define RT5665_MONO_DMIC_R_SRC_MASK 0x1
+#define RT5665_MONO_DMIC_R_SRC_SFT 0
+
+/* Stereo2 ADC Mixer Control (0x0028) */
+#define RT5665_M_STO2_ADC_L1 (0x1 << 15)
+#define RT5665_M_STO2_ADC_L1_UN (0x0 << 15)
+#define RT5665_M_STO2_ADC_L1_SFT 15
+#define RT5665_M_STO2_ADC_L2 (0x1 << 14)
+#define RT5665_M_STO2_ADC_L2_SFT 14
+#define RT5665_STO2_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5665_STO2_ADC1L_SRC_SFT 13
+#define RT5665_STO2_ADC1_SRC_ADC (0x1 << 13)
+#define RT5665_STO2_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5665_STO2_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5665_STO2_ADC2L_SRC_SFT 12
+#define RT5665_STO2_ADCL_SRC_MASK (0x3 << 10)
+#define RT5665_STO2_ADCL_SRC_SFT 10
+#define RT5665_STO2_DD_L_SRC_MASK (0x1 << 9)
+#define RT5665_STO2_DD_L_SRC_SFT 9
+#define RT5665_STO2_DMIC_SRC_MASK (0x1 << 8)
+#define RT5665_STO2_DMIC_SRC_SFT 8
+#define RT5665_STO2_DMIC_SRC_DMIC2 (0x1 << 8)
+#define RT5665_STO2_DMIC_SRC_DMIC1 (0x0 << 8)
+#define RT5665_M_STO2_ADC_R1 (0x1 << 7)
+#define RT5665_M_STO2_ADC_R1_UN (0x0 << 7)
+#define RT5665_M_STO2_ADC_R1_SFT 7
+#define RT5665_M_STO2_ADC_R2 (0x1 << 6)
+#define RT5665_M_STO2_ADC_R2_SFT 6
+#define RT5665_STO2_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5665_STO2_ADC1R_SRC_SFT 5
+#define RT5665_STO2_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5665_STO2_ADC2R_SRC_SFT 4
+#define RT5665_STO2_ADCR_SRC_MASK (0x3 << 2)
+#define RT5665_STO2_ADCR_SRC_SFT 2
+#define RT5665_STO2_DD_R_SRC_MASK (0x1 << 1)
+#define RT5665_STO2_DD_R_SRC_SFT 1
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5665_M_ADCMIX_L (0x1 << 15)
+#define RT5665_M_ADCMIX_L_SFT 15
+#define RT5665_M_DAC1_L (0x1 << 14)
+#define RT5665_M_DAC1_L_SFT 14
+#define RT5665_DAC1_R_SEL_MASK (0x3 << 10)
+#define RT5665_DAC1_R_SEL_SFT 10
+#define RT5665_DAC1_L_SEL_MASK (0x3 << 8)
+#define RT5665_DAC1_L_SEL_SFT 8
+#define RT5665_M_ADCMIX_R (0x1 << 7)
+#define RT5665_M_ADCMIX_R_SFT 7
+#define RT5665_M_DAC1_R (0x1 << 6)
+#define RT5665_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5665_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5665_M_DAC_L1_STO_L_SFT 15
+#define RT5665_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5665_G_DAC_L1_STO_L_SFT 14
+#define RT5665_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5665_M_DAC_R1_STO_L_SFT 13
+#define RT5665_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5665_G_DAC_R1_STO_L_SFT 12
+#define RT5665_M_DAC_L2_STO_L (0x1 << 11)
+#define RT5665_M_DAC_L2_STO_L_SFT 11
+#define RT5665_G_DAC_L2_STO_L_MASK (0x1 << 10)
+#define RT5665_G_DAC_L2_STO_L_SFT 10
+#define RT5665_M_DAC_R2_STO_L (0x1 << 9)
+#define RT5665_M_DAC_R2_STO_L_SFT 9
+#define RT5665_G_DAC_R2_STO_L_MASK (0x1 << 8)
+#define RT5665_G_DAC_R2_STO_L_SFT 8
+#define RT5665_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5665_M_DAC_L1_STO_R_SFT 7
+#define RT5665_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5665_G_DAC_L1_STO_R_SFT 6
+#define RT5665_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5665_M_DAC_R1_STO_R_SFT 5
+#define RT5665_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5665_G_DAC_R1_STO_R_SFT 4
+#define RT5665_M_DAC_L2_STO_R (0x1 << 3)
+#define RT5665_M_DAC_L2_STO_R_SFT 3
+#define RT5665_G_DAC_L2_STO_R_MASK (0x1 << 2)
+#define RT5665_G_DAC_L2_STO_R_SFT 2
+#define RT5665_M_DAC_R2_STO_R (0x1 << 1)
+#define RT5665_M_DAC_R2_STO_R_SFT 1
+#define RT5665_G_DAC_R2_STO_R_MASK (0x1)
+#define RT5665_G_DAC_R2_STO_R_SFT 0
+
+/* Mono DAC Mixer Control (0x002b) */
+#define RT5665_M_DAC_L1_MONO_L (0x1 << 15)
+#define RT5665_M_DAC_L1_MONO_L_SFT 15
+#define RT5665_G_DAC_L1_MONO_L_MASK (0x1 << 14)
+#define RT5665_G_DAC_L1_MONO_L_SFT 14
+#define RT5665_M_DAC_R1_MONO_L (0x1 << 13)
+#define RT5665_M_DAC_R1_MONO_L_SFT 13
+#define RT5665_G_DAC_R1_MONO_L_MASK (0x1 << 12)
+#define RT5665_G_DAC_R1_MONO_L_SFT 12
+#define RT5665_M_DAC_L2_MONO_L (0x1 << 11)
+#define RT5665_M_DAC_L2_MONO_L_SFT 11
+#define RT5665_G_DAC_L2_MONO_L_MASK (0x1 << 10)
+#define RT5665_G_DAC_L2_MONO_L_SFT 10
+#define RT5665_M_DAC_R2_MONO_L (0x1 << 9)
+#define RT5665_M_DAC_R2_MONO_L_SFT 9
+#define RT5665_G_DAC_R2_MONO_L_MASK (0x1 << 8)
+#define RT5665_G_DAC_R2_MONO_L_SFT 8
+#define RT5665_M_DAC_L1_MONO_R (0x1 << 7)
+#define RT5665_M_DAC_L1_MONO_R_SFT 7
+#define RT5665_G_DAC_L1_MONO_R_MASK (0x1 << 6)
+#define RT5665_G_DAC_L1_MONO_R_SFT 6
+#define RT5665_M_DAC_R1_MONO_R (0x1 << 5)
+#define RT5665_M_DAC_R1_MONO_R_SFT 5
+#define RT5665_G_DAC_R1_MONO_R_MASK (0x1 << 4)
+#define RT5665_G_DAC_R1_MONO_R_SFT 4
+#define RT5665_M_DAC_L2_MONO_R (0x1 << 3)
+#define RT5665_M_DAC_L2_MONO_R_SFT 3
+#define RT5665_G_DAC_L2_MONO_R_MASK (0x1 << 2)
+#define RT5665_G_DAC_L2_MONO_R_SFT 2
+#define RT5665_M_DAC_R2_MONO_R (0x1 << 1)
+#define RT5665_M_DAC_R2_MONO_R_SFT 1
+#define RT5665_G_DAC_R2_MONO_R_MASK (0x1)
+#define RT5665_G_DAC_R2_MONO_R_SFT 0
+
+/* Stereo2 DAC Mixer Control (0x002c) */
+#define RT5665_M_DAC_L1_STO2_L (0x1 << 15)
+#define RT5665_M_DAC_L1_STO2_L_SFT 15
+#define RT5665_G_DAC_L1_STO2_L_MASK (0x1 << 14)
+#define RT5665_G_DAC_L1_STO2_L_SFT 14
+#define RT5665_M_DAC_L2_STO2_L (0x1 << 13)
+#define RT5665_M_DAC_L2_STO2_L_SFT 13
+#define RT5665_G_DAC_L2_STO2_L_MASK (0x1 << 12)
+#define RT5665_G_DAC_L2_STO2_L_SFT 12
+#define RT5665_M_DAC_L3_STO2_L (0x1 << 11)
+#define RT5665_M_DAC_L3_STO2_L_SFT 11
+#define RT5665_G_DAC_L3_STO2_L_MASK (0x1 << 10)
+#define RT5665_G_DAC_L3_STO2_L_SFT 10
+#define RT5665_M_ST_DAC_L1 (0x1 << 9)
+#define RT5665_M_ST_DAC_L1_SFT 9
+#define RT5665_M_ST_DAC_R1 (0x1 << 8)
+#define RT5665_M_ST_DAC_R1_SFT 8
+#define RT5665_M_DAC_R1_STO2_R (0x1 << 7)
+#define RT5665_M_DAC_R1_STO2_R_SFT 7
+#define RT5665_G_DAC_R1_STO2_R_MASK (0x1 << 6)
+#define RT5665_G_DAC_R1_STO2_R_SFT 6
+#define RT5665_M_DAC_R2_STO2_R (0x1 << 5)
+#define RT5665_M_DAC_R2_STO2_R_SFT 5
+#define RT5665_G_DAC_R2_STO2_R_MASK (0x1 << 4)
+#define RT5665_G_DAC_R2_STO2_R_SFT 4
+#define RT5665_M_DAC_R3_STO2_R (0x1 << 3)
+#define RT5665_M_DAC_R3_STO2_R_SFT 3
+#define RT5665_G_DAC_R3_STO2_R_MASK (0x1 << 2)
+#define RT5665_G_DAC_R3_STO2_R_SFT 2
+
+/* Analog DAC1 Input Source Control (0x002d) */
+#define RT5665_DAC_MIX_L_MASK (0x3 << 12)
+#define RT5665_DAC_MIX_L_SFT 12
+#define RT5665_DAC_MIX_R_MASK (0x3 << 8)
+#define RT5665_DAC_MIX_R_SFT 8
+#define RT5665_DAC_L1_SRC_MASK (0x3 << 4)
+#define RT5665_A_DACL1_SFT 4
+#define RT5665_DAC_R1_SRC_MASK (0x3)
+#define RT5665_A_DACR1_SFT 0
+
+/* Analog DAC Input Source Control (0x002e) */
+#define RT5665_A_DACL2_SEL (0x1 << 4)
+#define RT5665_A_DACL2_SFT 4
+#define RT5665_A_DACR2_SEL (0x1 << 0)
+#define RT5665_A_DACR2_SFT 0
+
+/* Digital Interface Data Control (0x002f) */
+#define RT5665_IF2_1_ADC_IN_MASK (0x7 << 12)
+#define RT5665_IF2_1_ADC_IN_SFT 12
+#define RT5665_IF2_1_DAC_SEL_MASK (0x3 << 10)
+#define RT5665_IF2_1_DAC_SEL_SFT 10
+#define RT5665_IF2_1_ADC_SEL_MASK (0x3 << 8)
+#define RT5665_IF2_1_ADC_SEL_SFT 8
+#define RT5665_IF2_2_ADC_IN_MASK (0x7 << 4)
+#define RT5665_IF2_2_ADC_IN_SFT 4
+#define RT5665_IF2_2_DAC_SEL_MASK (0x3 << 2)
+#define RT5665_IF2_2_DAC_SEL_SFT 2
+#define RT5665_IF2_2_ADC_SEL_MASK (0x3 << 0)
+#define RT5665_IF2_2_ADC_SEL_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5665_IF3_ADC_IN_MASK (0x7 << 4)
+#define RT5665_IF3_ADC_IN_SFT 4
+#define RT5665_IF3_DAC_SEL_MASK (0x3 << 2)
+#define RT5665_IF3_DAC_SEL_SFT 2
+#define RT5665_IF3_ADC_SEL_MASK (0x3 << 0)
+#define RT5665_IF3_ADC_SEL_SFT 0
+
+/* PDM Output Control (0x0031) */
+#define RT5665_M_PDM1_L (0x1 << 14)
+#define RT5665_M_PDM1_L_SFT 14
+#define RT5665_M_PDM1_R (0x1 << 12)
+#define RT5665_M_PDM1_R_SFT 12
+#define RT5665_PDM1_L_MASK (0x3 << 10)
+#define RT5665_PDM1_L_SFT 10
+#define RT5665_PDM1_R_MASK (0x3 << 8)
+#define RT5665_PDM1_R_SFT 8
+#define RT5665_PDM1_BUSY (0x1 << 6)
+#define RT5665_PDM_PATTERN (0x1 << 5)
+#define RT5665_PDM_GAIN (0x1 << 4)
+#define RT5665_LRCK_PDM_PI2C (0x1 << 3)
+#define RT5665_PDM_DIV_MASK (0x3)
+
+/*S/PDIF Output Control (0x0036) */
+#define RT5665_SPDIF_SEL_MASK (0x3 << 0)
+#define RT5665_SPDIF_SEL_SFT 0
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5665_M_CBJ_RM1_L (0x1 << 7)
+#define RT5665_M_CBJ_RM1_L_SFT 7
+#define RT5665_M_BST1_RM1_L (0x1 << 5)
+#define RT5665_M_BST1_RM1_L_SFT 5
+#define RT5665_M_BST2_RM1_L (0x1 << 4)
+#define RT5665_M_BST2_RM1_L_SFT 4
+#define RT5665_M_BST3_RM1_L (0x1 << 3)
+#define RT5665_M_BST3_RM1_L_SFT 3
+#define RT5665_M_BST4_RM1_L (0x1 << 2)
+#define RT5665_M_BST4_RM1_L_SFT 2
+#define RT5665_M_INL_RM1_L (0x1 << 1)
+#define RT5665_M_INL_RM1_L_SFT 1
+#define RT5665_M_INR_RM1_L (0x1)
+#define RT5665_M_INR_RM1_L_SFT 0
+
+/* REC Right Mixer Control 2 (0x003e) */
+#define RT5665_M_AEC_REF_RM1_R (0x1 << 7)
+#define RT5665_M_AEC_REF_RM1_R_SFT 7
+#define RT5665_M_BST1_RM1_R (0x1 << 5)
+#define RT5665_M_BST1_RM1_R_SFT 5
+#define RT5665_M_BST2_RM1_R (0x1 << 4)
+#define RT5665_M_BST2_RM1_R_SFT 4
+#define RT5665_M_BST3_RM1_R (0x1 << 3)
+#define RT5665_M_BST3_RM1_R_SFT 3
+#define RT5665_M_BST4_RM1_R (0x1 << 2)
+#define RT5665_M_BST4_RM1_R_SFT 2
+#define RT5665_M_INR_RM1_R (0x1 << 1)
+#define RT5665_M_INR_RM1_R_SFT 1
+#define RT5665_M_MONOVOL_RM1_R (0x1)
+#define RT5665_M_MONOVOL_RM1_R_SFT 0
+
+/* REC Mixer 2 Left Control 2 (0x0041) */
+#define RT5665_M_CBJ_RM2_L (0x1 << 7)
+#define RT5665_M_CBJ_RM2_L_SFT 7
+#define RT5665_M_BST1_RM2_L (0x1 << 5)
+#define RT5665_M_BST1_RM2_L_SFT 5
+#define RT5665_M_BST2_RM2_L (0x1 << 4)
+#define RT5665_M_BST2_RM2_L_SFT 4
+#define RT5665_M_BST3_RM2_L (0x1 << 3)
+#define RT5665_M_BST3_RM2_L_SFT 3
+#define RT5665_M_BST4_RM2_L (0x1 << 2)
+#define RT5665_M_BST4_RM2_L_SFT 2
+#define RT5665_M_INL_RM2_L (0x1 << 1)
+#define RT5665_M_INL_RM2_L_SFT 1
+#define RT5665_M_INR_RM2_L (0x1)
+#define RT5665_M_INR_RM2_L_SFT 0
+
+/* REC Mixer 2 Right Control 2 (0x0043) */
+#define RT5665_M_MONOVOL_RM2_R (0x1 << 7)
+#define RT5665_M_MONOVOL_RM2_R_SFT 7
+#define RT5665_M_BST1_RM2_R (0x1 << 5)
+#define RT5665_M_BST1_RM2_R_SFT 5
+#define RT5665_M_BST2_RM2_R (0x1 << 4)
+#define RT5665_M_BST2_RM2_R_SFT 4
+#define RT5665_M_BST3_RM2_R (0x1 << 3)
+#define RT5665_M_BST3_RM2_R_SFT 3
+#define RT5665_M_BST4_RM2_R (0x1 << 2)
+#define RT5665_M_BST4_RM2_R_SFT 2
+#define RT5665_M_INL_RM2_R (0x1 << 1)
+#define RT5665_M_INL_RM2_R_SFT 1
+#define RT5665_M_INR_RM2_R (0x1)
+#define RT5665_M_INR_RM2_R_SFT 0
+
+/* SPK Left Mixer Control (0x0046) */
+#define RT5665_M_BST3_SM_L (0x1 << 4)
+#define RT5665_M_BST3_SM_L_SFT 4
+#define RT5665_M_IN_R_SM_L (0x1 << 3)
+#define RT5665_M_IN_R_SM_L_SFT 3
+#define RT5665_M_IN_L_SM_L (0x1 << 2)
+#define RT5665_M_IN_L_SM_L_SFT 2
+#define RT5665_M_BST1_SM_L (0x1 << 1)
+#define RT5665_M_BST1_SM_L_SFT 1
+#define RT5665_M_DAC_L2_SM_L (0x1)
+#define RT5665_M_DAC_L2_SM_L_SFT 0
+
+/* SPK Right Mixer Control (0x0047) */
+#define RT5665_M_BST3_SM_R (0x1 << 4)
+#define RT5665_M_BST3_SM_R_SFT 4
+#define RT5665_M_IN_R_SM_R (0x1 << 3)
+#define RT5665_M_IN_R_SM_R_SFT 3
+#define RT5665_M_IN_L_SM_R (0x1 << 2)
+#define RT5665_M_IN_L_SM_R_SFT 2
+#define RT5665_M_BST4_SM_R (0x1 << 1)
+#define RT5665_M_BST4_SM_R_SFT 1
+#define RT5665_M_DAC_R2_SM_R (0x1)
+#define RT5665_M_DAC_R2_SM_R_SFT 0
+
+/* SPO Amp Input and Gain Control (0x0048) */
+#define RT5665_M_DAC_L2_SPKOMIX (0x1 << 13)
+#define RT5665_M_DAC_L2_SPKOMIX_SFT 13
+#define RT5665_M_SPKVOLL_SPKOMIX (0x1 << 12)
+#define RT5665_M_SPKVOLL_SPKOMIX_SFT 12
+#define RT5665_M_DAC_R2_SPKOMIX (0x1 << 9)
+#define RT5665_M_DAC_R2_SPKOMIX_SFT 9
+#define RT5665_M_SPKVOLR_SPKOMIX (0x1 << 8)
+#define RT5665_M_SPKVOLR_SPKOMIX_SFT 8
+
+/* MONOMIX Input and Gain Control (0x004b) */
+#define RT5665_G_MONOVOL_MA (0x1 << 10)
+#define RT5665_G_MONOVOL_MA_SFT 10
+#define RT5665_M_MONOVOL_MA (0x1 << 9)
+#define RT5665_M_MONOVOL_MA_SFT 9
+#define RT5665_M_DAC_L2_MA (0x1 << 8)
+#define RT5665_M_DAC_L2_MA_SFT 8
+#define RT5665_M_BST3_MM (0x1 << 4)
+#define RT5665_M_BST3_MM_SFT 4
+#define RT5665_M_BST2_MM (0x1 << 3)
+#define RT5665_M_BST2_MM_SFT 3
+#define RT5665_M_BST1_MM (0x1 << 2)
+#define RT5665_M_BST1_MM_SFT 2
+#define RT5665_M_RECMIC2L_MM (0x1 << 1)
+#define RT5665_M_RECMIC2L_MM_SFT 1
+#define RT5665_M_DAC_L2_MM (0x1)
+#define RT5665_M_DAC_L2_MM_SFT 0
+
+/* Output Left Mixer Control 1 (0x004d) */
+#define RT5665_G_BST3_OM_L_MASK (0x7 << 12)
+#define RT5665_G_BST3_OM_L_SFT 12
+#define RT5665_G_BST2_OM_L_MASK (0x7 << 9)
+#define RT5665_G_BST2_OM_L_SFT 9
+#define RT5665_G_BST1_OM_L_MASK (0x7 << 6)
+#define RT5665_G_BST1_OM_L_SFT 6
+#define RT5665_G_IN_L_OM_L_MASK (0x7 << 3)
+#define RT5665_G_IN_L_OM_L_SFT 3
+#define RT5665_G_DAC_L2_OM_L_MASK (0x7 << 0)
+#define RT5665_G_DAC_L2_OM_L_SFT 0
+
+/* Output Left Mixer Input Control (0x004e) */
+#define RT5665_M_BST3_OM_L (0x1 << 4)
+#define RT5665_M_BST3_OM_L_SFT 4
+#define RT5665_M_BST2_OM_L (0x1 << 3)
+#define RT5665_M_BST2_OM_L_SFT 3
+#define RT5665_M_BST1_OM_L (0x1 << 2)
+#define RT5665_M_BST1_OM_L_SFT 2
+#define RT5665_M_IN_L_OM_L (0x1 << 1)
+#define RT5665_M_IN_L_OM_L_SFT 1
+#define RT5665_M_DAC_L2_OM_L (0x1)
+#define RT5665_M_DAC_L2_OM_L_SFT 0
+
+/* Output Right Mixer Input Control (0x0050) */
+#define RT5665_M_BST4_OM_R (0x1 << 4)
+#define RT5665_M_BST4_OM_R_SFT 4
+#define RT5665_M_BST3_OM_R (0x1 << 3)
+#define RT5665_M_BST3_OM_R_SFT 3
+#define RT5665_M_BST2_OM_R (0x1 << 2)
+#define RT5665_M_BST2_OM_R_SFT 2
+#define RT5665_M_IN_R_OM_R (0x1 << 1)
+#define RT5665_M_IN_R_OM_R_SFT 1
+#define RT5665_M_DAC_R2_OM_R (0x1)
+#define RT5665_M_DAC_R2_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x0052) */
+#define RT5665_M_DAC_L2_LM (0x1 << 15)
+#define RT5665_M_DAC_L2_LM_SFT 15
+#define RT5665_M_DAC_R2_LM (0x1 << 14)
+#define RT5665_M_DAC_R2_LM_SFT 14
+#define RT5665_M_OV_L_LM (0x1 << 13)
+#define RT5665_M_OV_L_LM_SFT 13
+#define RT5665_M_OV_R_LM (0x1 << 12)
+#define RT5665_M_OV_R_LM_SFT 12
+#define RT5665_LOUT_BST_SFT 11
+#define RT5665_LOUT_DF (0x1 << 11)
+#define RT5665_LOUT_DF_SFT 11
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5665_PWR_I2S1_1 (0x1 << 15)
+#define RT5665_PWR_I2S1_1_BIT 15
+#define RT5665_PWR_I2S1_2 (0x1 << 14)
+#define RT5665_PWR_I2S1_2_BIT 14
+#define RT5665_PWR_I2S2_1 (0x1 << 13)
+#define RT5665_PWR_I2S2_1_BIT 13
+#define RT5665_PWR_I2S2_2 (0x1 << 12)
+#define RT5665_PWR_I2S2_2_BIT 12
+#define RT5665_PWR_DAC_L1 (0x1 << 11)
+#define RT5665_PWR_DAC_L1_BIT 11
+#define RT5665_PWR_DAC_R1 (0x1 << 10)
+#define RT5665_PWR_DAC_R1_BIT 10
+#define RT5665_PWR_I2S3 (0x1 << 9)
+#define RT5665_PWR_I2S3_BIT 9
+#define RT5665_PWR_LDO (0x1 << 8)
+#define RT5665_PWR_LDO_BIT 8
+#define RT5665_PWR_DAC_L2 (0x1 << 7)
+#define RT5665_PWR_DAC_L2_BIT 7
+#define RT5665_PWR_DAC_R2 (0x1 << 6)
+#define RT5665_PWR_DAC_R2_BIT 6
+#define RT5665_PWR_ADC_L1 (0x1 << 4)
+#define RT5665_PWR_ADC_L1_BIT 4
+#define RT5665_PWR_ADC_R1 (0x1 << 3)
+#define RT5665_PWR_ADC_R1_BIT 3
+#define RT5665_PWR_ADC_L2 (0x1 << 2)
+#define RT5665_PWR_ADC_L2_BIT 2
+#define RT5665_PWR_ADC_R2 (0x1 << 1)
+#define RT5665_PWR_ADC_R2_BIT 1
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5665_PWR_ADC_S1F (0x1 << 15)
+#define RT5665_PWR_ADC_S1F_BIT 15
+#define RT5665_PWR_ADC_S2F (0x1 << 14)
+#define RT5665_PWR_ADC_S2F_BIT 14
+#define RT5665_PWR_ADC_MF_L (0x1 << 13)
+#define RT5665_PWR_ADC_MF_L_BIT 13
+#define RT5665_PWR_ADC_MF_R (0x1 << 12)
+#define RT5665_PWR_ADC_MF_R_BIT 12
+#define RT5665_PWR_DAC_S2F (0x1 << 11)
+#define RT5665_PWR_DAC_S2F_BIT 11
+#define RT5665_PWR_DAC_S1F (0x1 << 10)
+#define RT5665_PWR_DAC_S1F_BIT 10
+#define RT5665_PWR_DAC_MF_L (0x1 << 9)
+#define RT5665_PWR_DAC_MF_L_BIT 9
+#define RT5665_PWR_DAC_MF_R (0x1 << 8)
+#define RT5665_PWR_DAC_MF_R_BIT 8
+#define RT5665_PWR_PDM1 (0x1 << 7)
+#define RT5665_PWR_PDM1_BIT 7
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5665_PWR_VREF1 (0x1 << 15)
+#define RT5665_PWR_VREF1_BIT 15
+#define RT5665_PWR_FV1 (0x1 << 14)
+#define RT5665_PWR_FV1_BIT 14
+#define RT5665_PWR_VREF2 (0x1 << 13)
+#define RT5665_PWR_VREF2_BIT 13
+#define RT5665_PWR_FV2 (0x1 << 12)
+#define RT5665_PWR_FV2_BIT 12
+#define RT5665_PWR_VREF3 (0x1 << 11)
+#define RT5665_PWR_VREF3_BIT 11
+#define RT5665_PWR_FV3 (0x1 << 10)
+#define RT5665_PWR_FV3_BIT 10
+#define RT5665_PWR_MB (0x1 << 9)
+#define RT5665_PWR_MB_BIT 9
+#define RT5665_PWR_LM (0x1 << 8)
+#define RT5665_PWR_LM_BIT 8
+#define RT5665_PWR_BG (0x1 << 7)
+#define RT5665_PWR_BG_BIT 7
+#define RT5665_PWR_MA (0x1 << 6)
+#define RT5665_PWR_MA_BIT 6
+#define RT5665_PWR_HA_L (0x1 << 5)
+#define RT5665_PWR_HA_L_BIT 5
+#define RT5665_PWR_HA_R (0x1 << 4)
+#define RT5665_PWR_HA_R_BIT 4
+#define RT5665_HP_DRIVER_MASK (0x3 << 2)
+#define RT5665_HP_DRIVER_1X (0x0 << 2)
+#define RT5665_HP_DRIVER_3X (0x1 << 2)
+#define RT5665_HP_DRIVER_5X (0x3 << 2)
+#define RT5665_LDO1_DVO_MASK (0x3)
+#define RT5665_LDO1_DVO_09 (0x0)
+#define RT5665_LDO1_DVO_10 (0x1)
+#define RT5665_LDO1_DVO_12 (0x2)
+#define RT5665_LDO1_DVO_14 (0x3)
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5665_PWR_BST1 (0x1 << 15)
+#define RT5665_PWR_BST1_BIT 15
+#define RT5665_PWR_BST2 (0x1 << 14)
+#define RT5665_PWR_BST2_BIT 14
+#define RT5665_PWR_BST3 (0x1 << 13)
+#define RT5665_PWR_BST3_BIT 13
+#define RT5665_PWR_BST4 (0x1 << 12)
+#define RT5665_PWR_BST4_BIT 12
+#define RT5665_PWR_MB1 (0x1 << 11)
+#define RT5665_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5665_PWR_MB1_BIT 11
+#define RT5665_PWR_MB2 (0x1 << 10)
+#define RT5665_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5665_PWR_MB2_BIT 10
+#define RT5665_PWR_MB3 (0x1 << 9)
+#define RT5665_PWR_MB3_BIT 9
+#define RT5665_PWR_BST1_P (0x1 << 7)
+#define RT5665_PWR_BST1_P_BIT 7
+#define RT5665_PWR_BST2_P (0x1 << 6)
+#define RT5665_PWR_BST2_P_BIT 6
+#define RT5665_PWR_BST3_P (0x1 << 5)
+#define RT5665_PWR_BST3_P_BIT 5
+#define RT5665_PWR_BST4_P (0x1 << 4)
+#define RT5665_PWR_BST4_P_BIT 4
+#define RT5665_PWR_JD1 (0x1 << 3)
+#define RT5665_PWR_JD1_BIT 3
+#define RT5665_PWR_JD2 (0x1 << 2)
+#define RT5665_PWR_JD2_BIT 2
+#define RT5665_PWR_RM1_L (0x1 << 1)
+#define RT5665_PWR_RM1_L_BIT 1
+#define RT5665_PWR_RM1_R (0x1)
+#define RT5665_PWR_RM1_R_BIT 0
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5665_PWR_CBJ (0x1 << 9)
+#define RT5665_PWR_CBJ_BIT 9
+#define RT5665_PWR_BST_L (0x1 << 8)
+#define RT5665_PWR_BST_L_BIT 8
+#define RT5665_PWR_BST_R (0x1 << 7)
+#define RT5665_PWR_BST_R_BIT 7
+#define RT5665_PWR_PLL (0x1 << 6)
+#define RT5665_PWR_PLL_BIT 6
+#define RT5665_PWR_LDO2 (0x1 << 2)
+#define RT5665_PWR_LDO2_BIT 2
+#define RT5665_PWR_SVD (0x1 << 1)
+#define RT5665_PWR_SVD_BIT 1
+
+/* Power Management for Mixer (0x0066) */
+#define RT5665_PWR_RM2_L (0x1 << 15)
+#define RT5665_PWR_RM2_L_BIT 15
+#define RT5665_PWR_RM2_R (0x1 << 14)
+#define RT5665_PWR_RM2_R_BIT 14
+#define RT5665_PWR_OM_L (0x1 << 13)
+#define RT5665_PWR_OM_L_BIT 13
+#define RT5665_PWR_OM_R (0x1 << 12)
+#define RT5665_PWR_OM_R_BIT 12
+#define RT5665_PWR_MM (0x1 << 11)
+#define RT5665_PWR_MM_BIT 11
+#define RT5665_PWR_AEC_REF (0x1 << 6)
+#define RT5665_PWR_AEC_REF_BIT 6
+#define RT5665_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5665_PWR_STO1_DAC_L_BIT 5
+#define RT5665_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5665_PWR_STO1_DAC_R_BIT 4
+#define RT5665_PWR_MONO_DAC_L (0x1 << 3)
+#define RT5665_PWR_MONO_DAC_L_BIT 3
+#define RT5665_PWR_MONO_DAC_R (0x1 << 2)
+#define RT5665_PWR_MONO_DAC_R_BIT 2
+#define RT5665_PWR_STO2_DAC_L (0x1 << 1)
+#define RT5665_PWR_STO2_DAC_L_BIT 1
+#define RT5665_PWR_STO2_DAC_R (0x1)
+#define RT5665_PWR_STO2_DAC_R_BIT 0
+
+/* Power Management for Volume (0x0067) */
+#define RT5665_PWR_OV_L (0x1 << 13)
+#define RT5665_PWR_OV_L_BIT 13
+#define RT5665_PWR_OV_R (0x1 << 12)
+#define RT5665_PWR_OV_R_BIT 12
+#define RT5665_PWR_IN_L (0x1 << 9)
+#define RT5665_PWR_IN_L_BIT 9
+#define RT5665_PWR_IN_R (0x1 << 8)
+#define RT5665_PWR_IN_R_BIT 8
+#define RT5665_PWR_MV (0x1 << 7)
+#define RT5665_PWR_MV_BIT 7
+#define RT5665_PWR_MIC_DET (0x1 << 5)
+#define RT5665_PWR_MIC_DET_BIT 5
+
+/* (0x006b) */
+#define RT5665_SYS_CLK_DET 15
+#define RT5665_HP_CLK_DET 14
+#define RT5665_MONO_CLK_DET 13
+#define RT5665_LOUT_CLK_DET 12
+#define RT5665_POW_CLK_DET 0
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5665_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5665_DMIC_1_EN_SFT 15
+#define RT5665_DMIC_1_DIS (0x0 << 15)
+#define RT5665_DMIC_1_EN (0x1 << 15)
+#define RT5665_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5665_DMIC_2_EN_SFT 14
+#define RT5665_DMIC_2_DIS (0x0 << 14)
+#define RT5665_DMIC_2_EN (0x1 << 14)
+#define RT5665_DMIC_2_DP_MASK (0x1 << 9)
+#define RT5665_DMIC_2_DP_SFT 9
+#define RT5665_DMIC_2_DP_GPIO5 (0x0 << 9)
+#define RT5665_DMIC_2_DP_IN2P (0x1 << 9)
+#define RT5665_DMIC_CLK_MASK (0x7 << 5)
+#define RT5665_DMIC_CLK_SFT 5
+#define RT5665_DMIC_1_DP_MASK (0x1 << 1)
+#define RT5665_DMIC_1_DP_SFT 1
+#define RT5665_DMIC_1_DP_GPIO4 (0x0 << 1)
+#define RT5665_DMIC_1_DP_IN2N (0x1 << 1)
+
+
+/* Digital Microphone Control 1 (0x006f) */
+#define RT5665_DMIC_2L_LH_MASK (0x1 << 3)
+#define RT5665_DMIC_2L_LH_SFT 3
+#define RT5665_DMIC_2L_LH_RISING (0x0 << 3)
+#define RT5665_DMIC_2L_LH_FALLING (0x1 << 3)
+#define RT5665_DMIC_2R_LH_MASK (0x1 << 2)
+#define RT5665_DMIC_2R_LH_SFT 2
+#define RT5665_DMIC_2R_LH_RISING (0x0 << 2)
+#define RT5665_DMIC_2R_LH_FALLING (0x1 << 2)
+#define RT5665_DMIC_1L_LH_MASK (0x1 << 1)
+#define RT5665_DMIC_1L_LH_SFT 1
+#define RT5665_DMIC_1L_LH_RISING (0x0 << 1)
+#define RT5665_DMIC_1L_LH_FALLING (0x1 << 1)
+#define RT5665_DMIC_1R_LH_MASK (0x1 << 0)
+#define RT5665_DMIC_1R_LH_SFT 0
+#define RT5665_DMIC_1R_LH_RISING (0x0)
+#define RT5665_DMIC_1R_LH_FALLING (0x1)
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x0070 0x0071 0x0072) */
+#define RT5665_I2S_MS_MASK (0x1 << 15)
+#define RT5665_I2S_MS_SFT 15
+#define RT5665_I2S_MS_M (0x0 << 15)
+#define RT5665_I2S_MS_S (0x1 << 15)
+#define RT5665_I2S_PIN_CFG_MASK (0x1 << 14)
+#define RT5665_I2S_PIN_CFG_SFT 14
+#define RT5665_I2S_CLK_SEL_MASK (0x1 << 11)
+#define RT5665_I2S_CLK_SEL_SFT 11
+#define RT5665_I2S_BP_MASK (0x1 << 8)
+#define RT5665_I2S_BP_SFT 8
+#define RT5665_I2S_BP_NOR (0x0 << 8)
+#define RT5665_I2S_BP_INV (0x1 << 8)
+#define RT5665_I2S_DL_MASK (0x3 << 4)
+#define RT5665_I2S_DL_SFT 4
+#define RT5665_I2S_DL_16 (0x0 << 4)
+#define RT5665_I2S_DL_20 (0x1 << 4)
+#define RT5665_I2S_DL_24 (0x2 << 4)
+#define RT5665_I2S_DL_8 (0x3 << 4)
+#define RT5665_I2S_DF_MASK (0x7)
+#define RT5665_I2S_DF_SFT 0
+#define RT5665_I2S_DF_I2S (0x0)
+#define RT5665_I2S_DF_LEFT (0x1)
+#define RT5665_I2S_DF_PCM_A (0x2)
+#define RT5665_I2S_DF_PCM_B (0x3)
+#define RT5665_I2S_DF_PCM_A_N (0x6)
+#define RT5665_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5665_I2S_PD1_MASK (0x7 << 12)
+#define RT5665_I2S_PD1_SFT 12
+#define RT5665_I2S_PD1_1 (0x0 << 12)
+#define RT5665_I2S_PD1_2 (0x1 << 12)
+#define RT5665_I2S_PD1_3 (0x2 << 12)
+#define RT5665_I2S_PD1_4 (0x3 << 12)
+#define RT5665_I2S_PD1_6 (0x4 << 12)
+#define RT5665_I2S_PD1_8 (0x5 << 12)
+#define RT5665_I2S_PD1_12 (0x6 << 12)
+#define RT5665_I2S_PD1_16 (0x7 << 12)
+#define RT5665_I2S_M_PD2_MASK (0x7 << 8)
+#define RT5665_I2S_M_PD2_SFT 8
+#define RT5665_I2S_M_PD2_1 (0x0 << 8)
+#define RT5665_I2S_M_PD2_2 (0x1 << 8)
+#define RT5665_I2S_M_PD2_3 (0x2 << 8)
+#define RT5665_I2S_M_PD2_4 (0x3 << 8)
+#define RT5665_I2S_M_PD2_6 (0x4 << 8)
+#define RT5665_I2S_M_PD2_8 (0x5 << 8)
+#define RT5665_I2S_M_PD2_12 (0x6 << 8)
+#define RT5665_I2S_M_PD2_16 (0x7 << 8)
+#define RT5665_I2S_CLK_SRC_MASK (0x3 << 4)
+#define RT5665_I2S_CLK_SRC_SFT 4
+#define RT5665_I2S_CLK_SRC_MCLK (0x0 << 4)
+#define RT5665_I2S_CLK_SRC_PLL1 (0x1 << 4)
+#define RT5665_I2S_CLK_SRC_RCCLK (0x2 << 4)
+#define RT5665_DAC_OSR_MASK (0x3 << 2)
+#define RT5665_DAC_OSR_SFT 2
+#define RT5665_DAC_OSR_128 (0x0 << 2)
+#define RT5665_DAC_OSR_64 (0x1 << 2)
+#define RT5665_DAC_OSR_32 (0x2 << 2)
+#define RT5665_ADC_OSR_MASK (0x3)
+#define RT5665_ADC_OSR_SFT 0
+#define RT5665_ADC_OSR_128 (0x0)
+#define RT5665_ADC_OSR_64 (0x1)
+#define RT5665_ADC_OSR_32 (0x2)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5665_I2S_BCLK_MS2_MASK (0x1 << 15)
+#define RT5665_I2S_BCLK_MS2_SFT 15
+#define RT5665_I2S_BCLK_MS2_32 (0x0 << 15)
+#define RT5665_I2S_BCLK_MS2_64 (0x1 << 15)
+#define RT5665_I2S_PD2_MASK (0x7 << 12)
+#define RT5665_I2S_PD2_SFT 12
+#define RT5665_I2S_PD2_1 (0x0 << 12)
+#define RT5665_I2S_PD2_2 (0x1 << 12)
+#define RT5665_I2S_PD2_3 (0x2 << 12)
+#define RT5665_I2S_PD2_4 (0x3 << 12)
+#define RT5665_I2S_PD2_6 (0x4 << 12)
+#define RT5665_I2S_PD2_8 (0x5 << 12)
+#define RT5665_I2S_PD2_12 (0x6 << 12)
+#define RT5665_I2S_PD2_16 (0x7 << 12)
+#define RT5665_I2S_BCLK_MS3_MASK (0x1 << 11)
+#define RT5665_I2S_BCLK_MS3_SFT 11
+#define RT5665_I2S_BCLK_MS3_32 (0x0 << 11)
+#define RT5665_I2S_BCLK_MS3_64 (0x1 << 11)
+#define RT5665_I2S_PD3_MASK (0x7 << 8)
+#define RT5665_I2S_PD3_SFT 8
+#define RT5665_I2S_PD3_1 (0x0 << 8)
+#define RT5665_I2S_PD3_2 (0x1 << 8)
+#define RT5665_I2S_PD3_3 (0x2 << 8)
+#define RT5665_I2S_PD3_4 (0x3 << 8)
+#define RT5665_I2S_PD3_6 (0x4 << 8)
+#define RT5665_I2S_PD3_8 (0x5 << 8)
+#define RT5665_I2S_PD3_12 (0x6 << 8)
+#define RT5665_I2S_PD3_16 (0x7 << 8)
+#define RT5665_I2S_PD4_MASK (0x7 << 4)
+#define RT5665_I2S_PD4_SFT 4
+#define RT5665_I2S_PD4_1 (0x0 << 4)
+#define RT5665_I2S_PD4_2 (0x1 << 4)
+#define RT5665_I2S_PD4_3 (0x2 << 4)
+#define RT5665_I2S_PD4_4 (0x3 << 4)
+#define RT5665_I2S_PD4_6 (0x4 << 4)
+#define RT5665_I2S_PD4_8 (0x5 << 4)
+#define RT5665_I2S_PD4_12 (0x6 << 4)
+#define RT5665_I2S_PD4_16 (0x7 << 4)
+
+/* TDM control 1 (0x0078) */
+#define RT5665_I2S1_MODE_MASK (0x1 << 15)
+#define RT5665_I2S1_MODE_I2S (0x0 << 15)
+#define RT5665_I2S1_MODE_TDM (0x1 << 15)
+#define RT5665_TDM_IN_CH_MASK (0x3 << 10)
+#define RT5665_TDM_IN_CH_2 (0x0 << 10)
+#define RT5665_TDM_IN_CH_4 (0x1 << 10)
+#define RT5665_TDM_IN_CH_6 (0x2 << 10)
+#define RT5665_TDM_IN_CH_8 (0x3 << 10)
+#define RT5665_TDM_OUT_CH_MASK (0x3 << 8)
+#define RT5665_TDM_OUT_CH_2 (0x0 << 8)
+#define RT5665_TDM_OUT_CH_4 (0x1 << 8)
+#define RT5665_TDM_OUT_CH_6 (0x2 << 8)
+#define RT5665_TDM_OUT_CH_8 (0x3 << 8)
+#define RT5665_TDM_IN_LEN_MASK (0x3 << 6)
+#define RT5665_TDM_IN_LEN_16 (0x0 << 6)
+#define RT5665_TDM_IN_LEN_20 (0x1 << 6)
+#define RT5665_TDM_IN_LEN_24 (0x2 << 6)
+#define RT5665_TDM_IN_LEN_32 (0x3 << 6)
+#define RT5665_TDM_OUT_LEN_MASK (0x3 << 4)
+#define RT5665_TDM_OUT_LEN_16 (0x0 << 4)
+#define RT5665_TDM_OUT_LEN_20 (0x1 << 4)
+#define RT5665_TDM_OUT_LEN_24 (0x2 << 4)
+#define RT5665_TDM_OUT_LEN_32 (0x3 << 4)
+
+
+/* TDM control 2 (0x0079) */
+#define RT5665_I2S1_1_DS_ADC_SLOT01_SFT 14
+#define RT5665_I2S1_1_DS_ADC_SLOT23_SFT 12
+#define RT5665_I2S1_1_DS_ADC_SLOT45_SFT 10
+#define RT5665_I2S1_1_DS_ADC_SLOT67_SFT 8
+#define RT5665_I2S1_2_DS_ADC_SLOT01_SFT 6
+#define RT5665_I2S1_2_DS_ADC_SLOT23_SFT 4
+#define RT5665_I2S1_2_DS_ADC_SLOT45_SFT 2
+#define RT5665_I2S1_2_DS_ADC_SLOT67_SFT 0
+
+/* TDM control 3/4 (0x007a) (0x007b) */
+#define RT5665_IF1_ADC1_SEL_SFT 10
+#define RT5665_IF1_ADC2_SEL_SFT 9
+#define RT5665_IF1_ADC3_SEL_SFT 8
+#define RT5665_IF1_ADC4_SEL_SFT 7
+#define RT5665_TDM_ADC_SEL_SFT 0
+#define RT5665_TDM_ADC_CTRL_MASK (0x1f << 0)
+#define RT5665_TDM_ADC_DATA_06 (0x6 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5665_SCLK_SRC_MASK (0x3 << 14)
+#define RT5665_SCLK_SRC_SFT 14
+#define RT5665_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5665_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5665_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5665_PLL1_SRC_MASK (0x7 << 8)
+#define RT5665_PLL1_SRC_SFT 8
+#define RT5665_PLL1_SRC_MCLK (0x0 << 8)
+#define RT5665_PLL1_SRC_BCLK1 (0x1 << 8)
+#define RT5665_PLL1_SRC_BCLK2 (0x2 << 8)
+#define RT5665_PLL1_SRC_BCLK3 (0x3 << 8)
+#define RT5665_PLL1_PD_MASK (0x7 << 4)
+#define RT5665_PLL1_PD_SFT 4
+
+
+#define RT5665_PLL_INP_MAX 40000000
+#define RT5665_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x0081) */
+#define RT5665_PLL_N_MAX 0x001ff
+#define RT5665_PLL_N_MASK (RT5665_PLL_N_MAX << 7)
+#define RT5665_PLL_N_SFT 7
+#define RT5665_PLL_K_MAX 0x001f
+#define RT5665_PLL_K_MASK (RT5665_PLL_K_MAX)
+#define RT5665_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x0082) */
+#define RT5665_PLL_M_MAX 0x00f
+#define RT5665_PLL_M_MASK (RT5665_PLL_M_MAX << 12)
+#define RT5665_PLL_M_SFT 12
+#define RT5665_PLL_M_BP (0x1 << 11)
+#define RT5665_PLL_M_BP_SFT 11
+#define RT5665_PLL_K_BP (0x1 << 10)
+#define RT5665_PLL_K_BP_SFT 10
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5665_I2S3_ASRC_MASK (0x1 << 15)
+#define RT5665_I2S3_ASRC_SFT 15
+#define RT5665_I2S2_ASRC_MASK (0x1 << 14)
+#define RT5665_I2S2_ASRC_SFT 14
+#define RT5665_I2S1_ASRC_MASK (0x1 << 13)
+#define RT5665_I2S1_ASRC_SFT 13
+#define RT5665_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5665_DAC_STO1_ASRC_SFT 12
+#define RT5665_DAC_STO2_ASRC_MASK (0x1 << 11)
+#define RT5665_DAC_STO2_ASRC_SFT 11
+#define RT5665_DAC_MONO_L_ASRC_MASK (0x1 << 10)
+#define RT5665_DAC_MONO_L_ASRC_SFT 10
+#define RT5665_DAC_MONO_R_ASRC_MASK (0x1 << 9)
+#define RT5665_DAC_MONO_R_ASRC_SFT 9
+#define RT5665_DMIC_STO1_ASRC_MASK (0x1 << 8)
+#define RT5665_DMIC_STO1_ASRC_SFT 8
+#define RT5665_DMIC_STO2_ASRC_MASK (0x1 << 7)
+#define RT5665_DMIC_STO2_ASRC_SFT 7
+#define RT5665_DMIC_MONO_L_ASRC_MASK (0x1 << 6)
+#define RT5665_DMIC_MONO_L_ASRC_SFT 6
+#define RT5665_DMIC_MONO_R_ASRC_MASK (0x1 << 5)
+#define RT5665_DMIC_MONO_R_ASRC_SFT 5
+#define RT5665_ADC_STO1_ASRC_MASK (0x1 << 4)
+#define RT5665_ADC_STO1_ASRC_SFT 4
+#define RT5665_ADC_STO2_ASRC_MASK (0x1 << 3)
+#define RT5665_ADC_STO2_ASRC_SFT 3
+#define RT5665_ADC_MONO_L_ASRC_MASK (0x1 << 2)
+#define RT5665_ADC_MONO_L_ASRC_SFT 2
+#define RT5665_ADC_MONO_R_ASRC_MASK (0x1 << 1)
+#define RT5665_ADC_MONO_R_ASRC_SFT 1
+
+/* PLL tracking mode 2 (0x0084)*/
+#define RT5665_DA_STO1_CLK_SEL_MASK (0x7 << 12)
+#define RT5665_DA_STO1_CLK_SEL_SFT 12
+#define RT5665_DA_STO2_CLK_SEL_MASK (0x7 << 8)
+#define RT5665_DA_STO2_CLK_SEL_SFT 8
+#define RT5665_DA_MONOL_CLK_SEL_MASK (0x7 << 4)
+#define RT5665_DA_MONOL_CLK_SEL_SFT 4
+#define RT5665_DA_MONOR_CLK_SEL_MASK (0x7)
+#define RT5665_DA_MONOR_CLK_SEL_SFT 0
+
+/* PLL tracking mode 3 (0x0085)*/
+#define RT5665_AD_STO1_CLK_SEL_MASK (0x7 << 12)
+#define RT5665_AD_STO1_CLK_SEL_SFT 12
+#define RT5665_AD_STO2_CLK_SEL_MASK (0x7 << 8)
+#define RT5665_AD_STO2_CLK_SEL_SFT 8
+#define RT5665_AD_MONOL_CLK_SEL_MASK (0x7 << 4)
+#define RT5665_AD_MONOL_CLK_SEL_SFT 4
+#define RT5665_AD_MONOR_CLK_SEL_MASK (0x7)
+#define RT5665_AD_MONOR_CLK_SEL_SFT 0
+
+/* ASRC Control 4 (0x0086) */
+#define RT5665_I2S1_RATE_MASK (0xf << 12)
+#define RT5665_I2S1_RATE_SFT 12
+#define RT5665_I2S2_RATE_MASK (0xf << 8)
+#define RT5665_I2S2_RATE_SFT 8
+#define RT5665_I2S3_RATE_MASK (0xf << 4)
+#define RT5665_I2S3_RATE_SFT 4
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5665_PUMP_EN (0x1 << 3)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5665_DEPOP_MASK (0x1 << 13)
+#define RT5665_DEPOP_SFT 13
+#define RT5665_DEPOP_AUTO (0x0 << 13)
+#define RT5665_DEPOP_MAN (0x1 << 13)
+#define RT5665_RAMP_MASK (0x1 << 12)
+#define RT5665_RAMP_SFT 12
+#define RT5665_RAMP_DIS (0x0 << 12)
+#define RT5665_RAMP_EN (0x1 << 12)
+#define RT5665_BPS_MASK (0x1 << 11)
+#define RT5665_BPS_SFT 11
+#define RT5665_BPS_DIS (0x0 << 11)
+#define RT5665_BPS_EN (0x1 << 11)
+#define RT5665_FAST_UPDN_MASK (0x1 << 10)
+#define RT5665_FAST_UPDN_SFT 10
+#define RT5665_FAST_UPDN_DIS (0x0 << 10)
+#define RT5665_FAST_UPDN_EN (0x1 << 10)
+#define RT5665_MRES_MASK (0x3 << 8)
+#define RT5665_MRES_SFT 8
+#define RT5665_MRES_15MO (0x0 << 8)
+#define RT5665_MRES_25MO (0x1 << 8)
+#define RT5665_MRES_35MO (0x2 << 8)
+#define RT5665_MRES_45MO (0x3 << 8)
+#define RT5665_VLO_MASK (0x1 << 7)
+#define RT5665_VLO_SFT 7
+#define RT5665_VLO_3V (0x0 << 7)
+#define RT5665_VLO_32V (0x1 << 7)
+#define RT5665_DIG_DP_MASK (0x1 << 6)
+#define RT5665_DIG_DP_SFT 6
+#define RT5665_DIG_DP_DIS (0x0 << 6)
+#define RT5665_DIG_DP_EN (0x1 << 6)
+#define RT5665_DP_TH_MASK (0x3 << 4)
+#define RT5665_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5665_CP_SYS_MASK (0x7 << 12)
+#define RT5665_CP_SYS_SFT 12
+#define RT5665_CP_FQ1_MASK (0x7 << 8)
+#define RT5665_CP_FQ1_SFT 8
+#define RT5665_CP_FQ2_MASK (0x7 << 4)
+#define RT5665_CP_FQ2_SFT 4
+#define RT5665_CP_FQ3_MASK (0x7)
+#define RT5665_CP_FQ3_SFT 0
+#define RT5665_CP_FQ_1_5_KHZ 0
+#define RT5665_CP_FQ_3_KHZ 1
+#define RT5665_CP_FQ_6_KHZ 2
+#define RT5665_CP_FQ_12_KHZ 3
+#define RT5665_CP_FQ_24_KHZ 4
+#define RT5665_CP_FQ_48_KHZ 5
+#define RT5665_CP_FQ_96_KHZ 6
+#define RT5665_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5665_OSW_L_MASK (0x1 << 11)
+#define RT5665_OSW_L_SFT 11
+#define RT5665_OSW_L_DIS (0x0 << 11)
+#define RT5665_OSW_L_EN (0x1 << 11)
+#define RT5665_OSW_R_MASK (0x1 << 10)
+#define RT5665_OSW_R_SFT 10
+#define RT5665_OSW_R_DIS (0x0 << 10)
+#define RT5665_OSW_R_EN (0x1 << 10)
+#define RT5665_PM_HP_MASK (0x3 << 8)
+#define RT5665_PM_HP_SFT 8
+#define RT5665_PM_HP_LV (0x0 << 8)
+#define RT5665_PM_HP_MV (0x1 << 8)
+#define RT5665_PM_HP_HV (0x2 << 8)
+#define RT5665_IB_HP_MASK (0x3 << 6)
+#define RT5665_IB_HP_SFT 6
+#define RT5665_IB_HP_125IL (0x0 << 6)
+#define RT5665_IB_HP_25IL (0x1 << 6)
+#define RT5665_IB_HP_5IL (0x2 << 6)
+#define RT5665_IB_HP_1IL (0x3 << 6)
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5665_PVDD_DET_MASK (0x1 << 15)
+#define RT5665_PVDD_DET_SFT 15
+#define RT5665_PVDD_DET_DIS (0x0 << 15)
+#define RT5665_PVDD_DET_EN (0x1 << 15)
+#define RT5665_SPK_AG_MASK (0x1 << 14)
+#define RT5665_SPK_AG_SFT 14
+#define RT5665_SPK_AG_DIS (0x0 << 14)
+#define RT5665_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control1 (0x93) */
+#define RT5665_MIC1_BS_MASK (0x1 << 15)
+#define RT5665_MIC1_BS_SFT 15
+#define RT5665_MIC1_BS_9AV (0x0 << 15)
+#define RT5665_MIC1_BS_75AV (0x1 << 15)
+#define RT5665_MIC2_BS_MASK (0x1 << 14)
+#define RT5665_MIC2_BS_SFT 14
+#define RT5665_MIC2_BS_9AV (0x0 << 14)
+#define RT5665_MIC2_BS_75AV (0x1 << 14)
+#define RT5665_MIC1_CLK_MASK (0x1 << 13)
+#define RT5665_MIC1_CLK_SFT 13
+#define RT5665_MIC1_CLK_DIS (0x0 << 13)
+#define RT5665_MIC1_CLK_EN (0x1 << 13)
+#define RT5665_MIC2_CLK_MASK (0x1 << 12)
+#define RT5665_MIC2_CLK_SFT 12
+#define RT5665_MIC2_CLK_DIS (0x0 << 12)
+#define RT5665_MIC2_CLK_EN (0x1 << 12)
+#define RT5665_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5665_MIC1_OVCD_SFT 11
+#define RT5665_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5665_MIC1_OVCD_EN (0x1 << 11)
+#define RT5665_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5665_MIC1_OVTH_SFT 9
+#define RT5665_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5665_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5665_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5665_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5665_MIC2_OVCD_SFT 8
+#define RT5665_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5665_MIC2_OVCD_EN (0x1 << 8)
+#define RT5665_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5665_MIC2_OVTH_SFT 6
+#define RT5665_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5665_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5665_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5665_PWR_MB_MASK (0x1 << 5)
+#define RT5665_PWR_MB_SFT 5
+#define RT5665_PWR_MB_PD (0x0 << 5)
+#define RT5665_PWR_MB_PU (0x1 << 5)
+
+/* Micbias Control2 (0x94) */
+#define RT5665_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5665_PWR_CLK25M_SFT 9
+#define RT5665_PWR_CLK25M_PD (0x0 << 9)
+#define RT5665_PWR_CLK25M_PU (0x1 << 9)
+#define RT5665_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5665_PWR_CLK1M_SFT 8
+#define RT5665_PWR_CLK1M_PD (0x0 << 8)
+#define RT5665_PWR_CLK1M_PU (0x1 << 8)
+
+/* I2S Master Mode Clock Control 1 (0x00a0) */
+#define RT5665_CLK_SRC_MCLK (0x0)
+#define RT5665_CLK_SRC_PLL1 (0x1)
+#define RT5665_CLK_SRC_RCCLK (0x2)
+#define RT5665_I2S_PD_1 (0x0)
+#define RT5665_I2S_PD_2 (0x1)
+#define RT5665_I2S_PD_3 (0x2)
+#define RT5665_I2S_PD_4 (0x3)
+#define RT5665_I2S_PD_6 (0x4)
+#define RT5665_I2S_PD_8 (0x5)
+#define RT5665_I2S_PD_12 (0x6)
+#define RT5665_I2S_PD_16 (0x7)
+#define RT5665_I2S2_SRC_MASK (0x3 << 12)
+#define RT5665_I2S2_SRC_SFT 12
+#define RT5665_I2S2_M_PD_MASK (0x7 << 8)
+#define RT5665_I2S2_M_PD_SFT 8
+#define RT5665_I2S3_SRC_MASK (0x3 << 4)
+#define RT5665_I2S3_SRC_SFT 4
+#define RT5665_I2S3_M_PD_MASK (0x7 << 0)
+#define RT5665_I2S3_M_PD_SFT 0
+
+
+/* EQ Control 1 (0x00b0) */
+#define RT5665_EQ_SRC_DAC (0x0 << 15)
+#define RT5665_EQ_SRC_ADC (0x1 << 15)
+#define RT5665_EQ_UPD (0x1 << 14)
+#define RT5665_EQ_UPD_BIT 14
+#define RT5665_EQ_CD_MASK (0x1 << 13)
+#define RT5665_EQ_CD_SFT 13
+#define RT5665_EQ_CD_DIS (0x0 << 13)
+#define RT5665_EQ_CD_EN (0x1 << 13)
+#define RT5665_EQ_DITH_MASK (0x3 << 8)
+#define RT5665_EQ_DITH_SFT 8
+#define RT5665_EQ_DITH_NOR (0x0 << 8)
+#define RT5665_EQ_DITH_LSB (0x1 << 8)
+#define RT5665_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5665_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* IRQ Control 1 (0x00b7) */
+#define RT5665_JD1_1_EN_MASK (0x1 << 15)
+#define RT5665_JD1_1_EN_SFT 15
+#define RT5665_JD1_1_DIS (0x0 << 15)
+#define RT5665_JD1_1_EN (0x1 << 15)
+#define RT5665_JD1_2_EN_MASK (0x1 << 12)
+#define RT5665_JD1_2_EN_SFT 12
+#define RT5665_JD1_2_DIS (0x0 << 12)
+#define RT5665_JD1_2_EN (0x1 << 12)
+
+/* IRQ Control 2 (0x00b8) */
+#define RT5665_IL_IRQ_MASK (0x1 << 6)
+#define RT5665_IL_IRQ_DIS (0x0 << 6)
+#define RT5665_IL_IRQ_EN (0x1 << 6)
+
+/* IRQ Control 5 (0x00ba) */
+#define RT5665_IRQ_JD_EN (0x1 << 3)
+#define RT5665_IRQ_JD_EN_SFT 3
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5665_GP1_PIN_MASK (0x1 << 15)
+#define RT5665_GP1_PIN_SFT 15
+#define RT5665_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5665_GP1_PIN_IRQ (0x1 << 15)
+#define RT5665_GP2_PIN_MASK (0x3 << 13)
+#define RT5665_GP2_PIN_SFT 13
+#define RT5665_GP2_PIN_GPIO2 (0x0 << 13)
+#define RT5665_GP2_PIN_BCLK2 (0x1 << 13)
+#define RT5665_GP2_PIN_PDM_SCL (0x2 << 13)
+#define RT5665_GP3_PIN_MASK (0x3 << 11)
+#define RT5665_GP3_PIN_SFT 11
+#define RT5665_GP3_PIN_GPIO3 (0x0 << 11)
+#define RT5665_GP3_PIN_LRCK2 (0x1 << 11)
+#define RT5665_GP3_PIN_PDM_SDA (0x2 << 11)
+#define RT5665_GP4_PIN_MASK (0x3 << 9)
+#define RT5665_GP4_PIN_SFT 9
+#define RT5665_GP4_PIN_GPIO4 (0x0 << 9)
+#define RT5665_GP4_PIN_DACDAT2_1 (0x1 << 9)
+#define RT5665_GP4_PIN_DMIC1_SDA (0x2 << 9)
+#define RT5665_GP5_PIN_MASK (0x3 << 7)
+#define RT5665_GP5_PIN_SFT 7
+#define RT5665_GP5_PIN_GPIO5 (0x0 << 7)
+#define RT5665_GP5_PIN_ADCDAT2_1 (0x1 << 7)
+#define RT5665_GP5_PIN_DMIC2_SDA (0x2 << 7)
+#define RT5665_GP6_PIN_MASK (0x3 << 5)
+#define RT5665_GP6_PIN_SFT 5
+#define RT5665_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5665_GP6_PIN_BCLK3 (0x1 << 5)
+#define RT5665_GP6_PIN_PDM_SCL (0x2 << 5)
+#define RT5665_GP7_PIN_MASK (0x3 << 3)
+#define RT5665_GP7_PIN_SFT 3
+#define RT5665_GP7_PIN_GPIO7 (0x0 << 3)
+#define RT5665_GP7_PIN_LRCK3 (0x1 << 3)
+#define RT5665_GP7_PIN_PDM_SDA (0x2 << 3)
+#define RT5665_GP8_PIN_MASK (0x3 << 1)
+#define RT5665_GP8_PIN_SFT 1
+#define RT5665_GP8_PIN_GPIO8 (0x0 << 1)
+#define RT5665_GP8_PIN_DACDAT3 (0x1 << 1)
+#define RT5665_GP8_PIN_DMIC2_SCL (0x2 << 1)
+#define RT5665_GP8_PIN_DACDAT2_2 (0x3 << 1)
+
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5665_GP9_PIN_MASK (0x3 << 14)
+#define RT5665_GP9_PIN_SFT 14
+#define RT5665_GP9_PIN_GPIO9 (0x0 << 14)
+#define RT5665_GP9_PIN_ADCDAT3 (0x1 << 14)
+#define RT5665_GP9_PIN_DMIC1_SCL (0x2 << 14)
+#define RT5665_GP9_PIN_ADCDAT2_2 (0x3 << 14)
+#define RT5665_GP10_PIN_MASK (0x3 << 12)
+#define RT5665_GP10_PIN_SFT 12
+#define RT5665_GP10_PIN_GPIO10 (0x0 << 12)
+#define RT5665_GP10_PIN_ADCDAT1_2 (0x1 << 12)
+#define RT5665_GP10_PIN_LPD (0x2 << 12)
+#define RT5665_GP1_PF_MASK (0x1 << 11)
+#define RT5665_GP1_PF_IN (0x0 << 11)
+#define RT5665_GP1_PF_OUT (0x1 << 11)
+#define RT5665_GP1_OUT_MASK (0x1 << 10)
+#define RT5665_GP1_OUT_H (0x0 << 10)
+#define RT5665_GP1_OUT_L (0x1 << 10)
+#define RT5665_GP2_PF_MASK (0x1 << 9)
+#define RT5665_GP2_PF_IN (0x0 << 9)
+#define RT5665_GP2_PF_OUT (0x1 << 9)
+#define RT5665_GP2_OUT_MASK (0x1 << 8)
+#define RT5665_GP2_OUT_H (0x0 << 8)
+#define RT5665_GP2_OUT_L (0x1 << 8)
+#define RT5665_GP3_PF_MASK (0x1 << 7)
+#define RT5665_GP3_PF_IN (0x0 << 7)
+#define RT5665_GP3_PF_OUT (0x1 << 7)
+#define RT5665_GP3_OUT_MASK (0x1 << 6)
+#define RT5665_GP3_OUT_H (0x0 << 6)
+#define RT5665_GP3_OUT_L (0x1 << 6)
+#define RT5665_GP4_PF_MASK (0x1 << 5)
+#define RT5665_GP4_PF_IN (0x0 << 5)
+#define RT5665_GP4_PF_OUT (0x1 << 5)
+#define RT5665_GP4_OUT_MASK (0x1 << 4)
+#define RT5665_GP4_OUT_H (0x0 << 4)
+#define RT5665_GP4_OUT_L (0x1 << 4)
+#define RT5665_GP5_PF_MASK (0x1 << 3)
+#define RT5665_GP5_PF_IN (0x0 << 3)
+#define RT5665_GP5_PF_OUT (0x1 << 3)
+#define RT5665_GP5_OUT_MASK (0x1 << 2)
+#define RT5665_GP5_OUT_H (0x0 << 2)
+#define RT5665_GP5_OUT_L (0x1 << 2)
+#define RT5665_GP6_PF_MASK (0x1 << 1)
+#define RT5665_GP6_PF_IN (0x0 << 1)
+#define RT5665_GP6_PF_OUT (0x1 << 1)
+#define RT5665_GP6_OUT_MASK (0x1)
+#define RT5665_GP6_OUT_H (0x0)
+#define RT5665_GP6_OUT_L (0x1)
+
+
+/* GPIO Control 3 (0x00c2) */
+#define RT5665_GP7_PF_MASK (0x1 << 15)
+#define RT5665_GP7_PF_IN (0x0 << 15)
+#define RT5665_GP7_PF_OUT (0x1 << 15)
+#define RT5665_GP7_OUT_MASK (0x1 << 14)
+#define RT5665_GP7_OUT_H (0x0 << 14)
+#define RT5665_GP7_OUT_L (0x1 << 14)
+#define RT5665_GP8_PF_MASK (0x1 << 13)
+#define RT5665_GP8_PF_IN (0x0 << 13)
+#define RT5665_GP8_PF_OUT (0x1 << 13)
+#define RT5665_GP8_OUT_MASK (0x1 << 12)
+#define RT5665_GP8_OUT_H (0x0 << 12)
+#define RT5665_GP8_OUT_L (0x1 << 12)
+#define RT5665_GP9_PF_MASK (0x1 << 11)
+#define RT5665_GP9_PF_IN (0x0 << 11)
+#define RT5665_GP9_PF_OUT (0x1 << 11)
+#define RT5665_GP9_OUT_MASK (0x1 << 10)
+#define RT5665_GP9_OUT_H (0x0 << 10)
+#define RT5665_GP9_OUT_L (0x1 << 10)
+#define RT5665_GP10_PF_MASK (0x1 << 9)
+#define RT5665_GP10_PF_IN (0x0 << 9)
+#define RT5665_GP10_PF_OUT (0x1 << 9)
+#define RT5665_GP10_OUT_MASK (0x1 << 8)
+#define RT5665_GP10_OUT_H (0x0 << 8)
+#define RT5665_GP10_OUT_L (0x1 << 8)
+#define RT5665_GP11_PF_MASK (0x1 << 7)
+#define RT5665_GP11_PF_IN (0x0 << 7)
+#define RT5665_GP11_PF_OUT (0x1 << 7)
+#define RT5665_GP11_OUT_MASK (0x1 << 6)
+#define RT5665_GP11_OUT_H (0x0 << 6)
+#define RT5665_GP11_OUT_L (0x1 << 6)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5665_SV_MASK (0x1 << 15)
+#define RT5665_SV_SFT 15
+#define RT5665_SV_DIS (0x0 << 15)
+#define RT5665_SV_EN (0x1 << 15)
+#define RT5665_OUT_SV_MASK (0x1 << 13)
+#define RT5665_OUT_SV_SFT 13
+#define RT5665_OUT_SV_DIS (0x0 << 13)
+#define RT5665_OUT_SV_EN (0x1 << 13)
+#define RT5665_HP_SV_MASK (0x1 << 12)
+#define RT5665_HP_SV_SFT 12
+#define RT5665_HP_SV_DIS (0x0 << 12)
+#define RT5665_HP_SV_EN (0x1 << 12)
+#define RT5665_ZCD_DIG_MASK (0x1 << 11)
+#define RT5665_ZCD_DIG_SFT 11
+#define RT5665_ZCD_DIG_DIS (0x0 << 11)
+#define RT5665_ZCD_DIG_EN (0x1 << 11)
+#define RT5665_ZCD_MASK (0x1 << 10)
+#define RT5665_ZCD_SFT 10
+#define RT5665_ZCD_PD (0x0 << 10)
+#define RT5665_ZCD_PU (0x1 << 10)
+#define RT5665_SV_DLY_MASK (0xf)
+#define RT5665_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0x00da) */
+#define RT5665_ZCD_HP_MASK (0x1 << 15)
+#define RT5665_ZCD_HP_SFT 15
+#define RT5665_ZCD_HP_DIS (0x0 << 15)
+#define RT5665_ZCD_HP_EN (0x1 << 15)
+
+/* 4 Button Inline Command Control 2 (0x00e0) */
+#define RT5665_4BTN_IL_MASK (0x1 << 15)
+#define RT5665_4BTN_IL_EN (0x1 << 15)
+#define RT5665_4BTN_IL_DIS (0x0 << 15)
+#define RT5665_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5665_4BTN_IL_NOR (0x1 << 14)
+#define RT5665_4BTN_IL_RST (0x0 << 14)
+
+/* Analog JD Control 1 (0x00f0) */
+#define RT5665_JD1_MODE_MASK (0x3 << 0)
+#define RT5665_JD1_MODE_0 (0x0 << 0)
+#define RT5665_JD1_MODE_1 (0x1 << 0)
+#define RT5665_JD1_MODE_2 (0x2 << 0)
+
+/* Jack Detect Control 3 (0x00f8) */
+#define RT5665_JD_TRI_HPO_SEL_MASK (0x7)
+#define RT5665_JD_TRI_HPO_SEL_SFT (0)
+#define RT5665_JD_HPO_GPIO_JD1 (0x0)
+#define RT5665_JD_HPO_JD1_1 (0x1)
+#define RT5665_JD_HPO_JD1_2 (0x2)
+#define RT5665_JD_HPO_JD2 (0x3)
+#define RT5665_JD_HPO_GPIO_JD2 (0x4)
+#define RT5665_JD_HPO_JD3 (0x5)
+#define RT5665_JD_HPO_JD_D (0x6)
+
+/* Digital Misc Control (0x00fa) */
+#define RT5665_AM_MASK (0x1 << 7)
+#define RT5665_AM_EN (0x1 << 7)
+#define RT5665_AM_DIS (0x1 << 7)
+#define RT5665_DIG_GATE_CTRL 0x1
+#define RT5665_DIG_GATE_CTRL_SFT (0)
+
+/* Chopper and Clock control for ADC (0x011c)*/
+#define RT5665_M_RF_DIG_MASK (0x1 << 12)
+#define RT5665_M_RF_DIG_SFT 12
+#define RT5665_M_RI_DIG (0x1 << 11)
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5665_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5665_CKXEN_DAC1_SFT 13
+#define RT5665_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5665_CKGEN_DAC1_SFT 12
+#define RT5665_CKXEN_DAC2_MASK (0x1 << 5)
+#define RT5665_CKXEN_DAC2_SFT 5
+#define RT5665_CKGEN_DAC2_MASK (0x1 << 4)
+#define RT5665_CKGEN_DAC2_SFT 4
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5665_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5665_CKXEN_ADC1_SFT 13
+#define RT5665_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5665_CKGEN_ADC1_SFT 12
+#define RT5665_CKXEN_ADC2_MASK (0x1 << 5)
+#define RT5665_CKXEN_ADC2_SFT 5
+#define RT5665_CKGEN_ADC2_MASK (0x1 << 4)
+#define RT5665_CKGEN_ADC2_SFT 4
+
+/* Volume test (0x013f)*/
+#define RT5665_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5665_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5665_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5665_AD2DA_LB_MASK (0x1 << 9)
+#define RT5665_AD2DA_LB_SFT 9
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5665_NG2_EN_MASK (0x1 << 15)
+#define RT5665_NG2_EN (0x1 << 15)
+#define RT5665_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5665_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5665_DEB_80_MS (0x0 << 4)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5665_SAR_BUTT_DET_MASK (0x1 << 15)
+#define RT5665_SAR_BUTT_DET_EN (0x1 << 15)
+#define RT5665_SAR_BUTT_DET_DIS (0x0 << 15)
+#define RT5665_SAR_BUTDET_MODE_MASK (0x1 << 14)
+#define RT5665_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5665_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5665_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5665_SAR_BUTDET_RST_NORMAL (0x1 << 13)
+#define RT5665_SAR_BUTDET_RST (0x0 << 13)
+#define RT5665_SAR_POW_MASK (0x1 << 12)
+#define RT5665_SAR_POW_EN (0x1 << 12)
+#define RT5665_SAR_POW_DIS (0x0 << 12)
+#define RT5665_SAR_RST_MASK (0x1 << 11)
+#define RT5665_SAR_RST_NORMAL (0x1 << 11)
+#define RT5665_SAR_RST (0x0 << 11)
+#define RT5665_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5665_SAR_BYPASS_EN (0x1 << 10)
+#define RT5665_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5665_SAR_SEL_MB1_MASK (0x1 << 9)
+#define RT5665_SAR_SEL_MB1_SEL (0x1 << 9)
+#define RT5665_SAR_SEL_MB1_NOSEL (0x0 << 9)
+#define RT5665_SAR_SEL_MB2_MASK (0x1 << 8)
+#define RT5665_SAR_SEL_MB2_SEL (0x1 << 8)
+#define RT5665_SAR_SEL_MB2_NOSEL (0x0 << 8)
+#define RT5665_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5665_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5665_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5665_SAR_SEL_MB1_MB2_MASK (0x1 << 5)
+#define RT5665_SAR_SEL_MB1_MB2_AUTO (0x1 << 5)
+#define RT5665_SAR_SEL_MB1_MB2_MANU (0x0 << 5)
+#define RT5665_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5665_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5665_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* System Clock Source */
+enum {
+ RT5665_SCLK_S_MCLK,
+ RT5665_SCLK_S_PLL1,
+ RT5665_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+ RT5665_PLL1_S_MCLK,
+ RT5665_PLL1_S_BCLK1,
+ RT5665_PLL1_S_BCLK2,
+ RT5665_PLL1_S_BCLK3,
+ RT5665_PLL1_S_BCLK4,
+};
+
+enum {
+ RT5665_AIF1_1,
+ RT5665_AIF1_2,
+ RT5665_AIF2_1,
+ RT5665_AIF2_2,
+ RT5665_AIF3,
+ RT5665_AIFS
+};
+
+enum {
+ CODEC_5665,
+ CODEC_5666,
+};
+
+/* filter mask */
+enum {
+ RT5665_DA_STEREO1_FILTER = 0x1,
+ RT5665_DA_STEREO2_FILTER = (0x1 << 1),
+ RT5665_DA_MONO_L_FILTER = (0x1 << 2),
+ RT5665_DA_MONO_R_FILTER = (0x1 << 3),
+ RT5665_AD_STEREO1_FILTER = (0x1 << 4),
+ RT5665_AD_STEREO2_FILTER = (0x1 << 5),
+ RT5665_AD_MONO_L_FILTER = (0x1 << 6),
+ RT5665_AD_MONO_R_FILTER = (0x1 << 7),
+};
+
+enum {
+ RT5665_CLK_SEL_SYS,
+ RT5665_CLK_SEL_I2S1_ASRC,
+ RT5665_CLK_SEL_I2S2_ASRC,
+ RT5665_CLK_SEL_I2S3_ASRC,
+ RT5665_CLK_SEL_SYS2,
+ RT5665_CLK_SEL_SYS3,
+ RT5665_CLK_SEL_SYS4,
+};
+
+#endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
new file mode 100644
index 000000000000..5fcdb50d5184
--- /dev/null
+++ b/sound/soc/codecs/rt5668.c
@@ -0,0 +1,2584 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5668.c -- RT5668B ALSA SoC audio component driver
+ *
+ * Copyright 2018 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5668.h>
+
+#include "rl6231.h"
+#include "rt5668.h"
+
+#define RT5668_NUM_SUPPLIES 3
+
+static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = {
+ "AVDD",
+ "MICVDD",
+ "VBAT",
+};
+
+struct rt5668_priv {
+ struct snd_soc_component *component;
+ struct rt5668_platform_data pdata;
+ struct gpio_desc *ldo1_en;
+ struct regmap *regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex calibrate_mutex;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5668_AIFS];
+ int bclk[RT5668_AIFS];
+ int master[RT5668_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+};
+
+static const struct reg_default rt5668_reg[] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x8000},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x800f},
+ {0x000b, 0x0000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x1404},
+ {0x0013, 0x1000},
+ {0x0014, 0xa00a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0019, 0xafaf},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x0080},
+ {0x0044, 0x0c0c},
+ {0x0049, 0x0000},
+ {0x0061, 0x0000},
+ {0x0062, 0x0000},
+ {0x0063, 0x003f},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0a10},
+ {0x0070, 0x8000},
+ {0x0071, 0x8000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x0080, 0x0000},
+ {0x0081, 0x0000},
+ {0x0082, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008d, 0x0000},
+ {0x008e, 0x0060},
+ {0x008f, 0x1000},
+ {0x0091, 0x0c26},
+ {0x0092, 0x0073},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0098, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x100c},
+ {0x009f, 0x0000},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00ae, 0x2040},
+ {0x00af, 0x0000},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d1, 0x2244},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0009},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f1, 0x00d0},
+ {0x00f2, 0x00d0},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6530},
+ {0x0100, 0xa0a0},
+ {0x010b, 0x0000},
+ {0x010c, 0xae00},
+ {0x010d, 0xaaa0},
+ {0x010e, 0x8aa2},
+ {0x010f, 0x02a2},
+ {0x0110, 0xc000},
+ {0x0111, 0x04a2},
+ {0x0112, 0x2800},
+ {0x0113, 0x0000},
+ {0x0117, 0x0100},
+ {0x0125, 0x0410},
+ {0x0132, 0x6026},
+ {0x0136, 0x5555},
+ {0x0138, 0x3700},
+ {0x013a, 0x2000},
+ {0x013b, 0x2000},
+ {0x013c, 0x2005},
+ {0x013f, 0x0000},
+ {0x0142, 0x0000},
+ {0x0145, 0x0002},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0149, 0x0000},
+ {0x0150, 0x79a1},
+ {0x0151, 0x0000},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0021},
+ {0x0190, 0x413d},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01af, 0x0000},
+ {0x01b0, 0x0400},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0000},
+ {0x01b4, 0x0000},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x01c3},
+ {0x01b7, 0x02a0},
+ {0x01b8, 0x03e9},
+ {0x01b9, 0x1389},
+ {0x01ba, 0xc351},
+ {0x01bb, 0x0009},
+ {0x01bc, 0x0018},
+ {0x01bd, 0x002a},
+ {0x01be, 0x004c},
+ {0x01bf, 0x0097},
+ {0x01c0, 0x433d},
+ {0x01c1, 0x2800},
+ {0x01c2, 0x0000},
+ {0x01c3, 0x0000},
+ {0x01c4, 0x0000},
+ {0x01c5, 0x0000},
+ {0x01c6, 0x0000},
+ {0x01c7, 0x0000},
+ {0x01c8, 0x40af},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0008},
+ {0x01d8, 0x0029},
+ {0x01d9, 0x3333},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0320},
+ {0x01e0, 0x06a1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01ea, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa005},
+ {0x0212, 0x824c},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x0250, 0x4500},
+ {0x0251, 0x40b3},
+ {0x0252, 0x0000},
+ {0x0253, 0x0000},
+ {0x0254, 0x0000},
+ {0x0255, 0x0000},
+ {0x0256, 0x0000},
+ {0x0257, 0x0000},
+ {0x0258, 0x0000},
+ {0x0259, 0x0000},
+ {0x025a, 0x0005},
+ {0x0270, 0x0000},
+ {0x02ff, 0x0110},
+ {0x0300, 0x001f},
+ {0x0301, 0x032c},
+ {0x0302, 0x5f21},
+ {0x0303, 0x4000},
+ {0x0304, 0x4000},
+ {0x0305, 0x06d5},
+ {0x0306, 0x8000},
+ {0x0307, 0x0700},
+ {0x0310, 0x4560},
+ {0x0311, 0xa4a8},
+ {0x0312, 0x7418},
+ {0x0313, 0x0000},
+ {0x0314, 0x0006},
+ {0x0315, 0xffff},
+ {0x0316, 0xc400},
+ {0x0317, 0x0000},
+ {0x03c0, 0x7e00},
+ {0x03c1, 0x8000},
+ {0x03c2, 0x8000},
+ {0x03c3, 0x8000},
+ {0x03c4, 0x8000},
+ {0x03c5, 0x8000},
+ {0x03c6, 0x8000},
+ {0x03c7, 0x8000},
+ {0x03c8, 0x8000},
+ {0x03c9, 0x8000},
+ {0x03ca, 0x8000},
+ {0x03cb, 0x8000},
+ {0x03cc, 0x8000},
+ {0x03d0, 0x0000},
+ {0x03d1, 0x0000},
+ {0x03d2, 0x0000},
+ {0x03d3, 0x0000},
+ {0x03d4, 0x2000},
+ {0x03d5, 0x2000},
+ {0x03d6, 0x0000},
+ {0x03d7, 0x0000},
+ {0x03d8, 0x2000},
+ {0x03d9, 0x2000},
+ {0x03da, 0x2000},
+ {0x03db, 0x2000},
+ {0x03dc, 0x0000},
+ {0x03dd, 0x0000},
+ {0x03de, 0x0000},
+ {0x03df, 0x2000},
+ {0x03e0, 0x0000},
+ {0x03e1, 0x0000},
+ {0x03e2, 0x0000},
+ {0x03e3, 0x0000},
+ {0x03e4, 0x0000},
+ {0x03e5, 0x0000},
+ {0x03e6, 0x0000},
+ {0x03e7, 0x0000},
+ {0x03e8, 0x0000},
+ {0x03e9, 0x0000},
+ {0x03ea, 0x0000},
+ {0x03eb, 0x0000},
+ {0x03ec, 0x0000},
+ {0x03ed, 0x0000},
+ {0x03ee, 0x0000},
+ {0x03ef, 0x0000},
+ {0x03f0, 0x0800},
+ {0x03f1, 0x0800},
+ {0x03f2, 0x0800},
+ {0x03f3, 0x0800},
+};
+
+static bool rt5668_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5668_RESET:
+ case RT5668_CBJ_CTRL_2:
+ case RT5668_INT_ST_1:
+ case RT5668_4BTN_IL_CMD_1:
+ case RT5668_AJD1_CTRL:
+ case RT5668_HP_CALIB_CTRL_1:
+ case RT5668_DEVICE_ID:
+ case RT5668_I2C_MODE:
+ case RT5668_HP_CALIB_CTRL_10:
+ case RT5668_EFUSE_CTRL_2:
+ case RT5668_JD_TOP_VC_VTRL:
+ case RT5668_HP_IMP_SENS_CTRL_19:
+ case RT5668_IL_CMD_1:
+ case RT5668_SAR_IL_CMD_2:
+ case RT5668_SAR_IL_CMD_4:
+ case RT5668_SAR_IL_CMD_10:
+ case RT5668_SAR_IL_CMD_11:
+ case RT5668_EFUSE_CTRL_6...RT5668_EFUSE_CTRL_11:
+ case RT5668_HP_CALIB_STA_1...RT5668_HP_CALIB_STA_11:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5668_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5668_RESET:
+ case RT5668_VERSION_ID:
+ case RT5668_VENDOR_ID:
+ case RT5668_DEVICE_ID:
+ case RT5668_HP_CTRL_1:
+ case RT5668_HP_CTRL_2:
+ case RT5668_HPL_GAIN:
+ case RT5668_HPR_GAIN:
+ case RT5668_I2C_CTRL:
+ case RT5668_CBJ_BST_CTRL:
+ case RT5668_CBJ_CTRL_1:
+ case RT5668_CBJ_CTRL_2:
+ case RT5668_CBJ_CTRL_3:
+ case RT5668_CBJ_CTRL_4:
+ case RT5668_CBJ_CTRL_5:
+ case RT5668_CBJ_CTRL_6:
+ case RT5668_CBJ_CTRL_7:
+ case RT5668_DAC1_DIG_VOL:
+ case RT5668_STO1_ADC_DIG_VOL:
+ case RT5668_STO1_ADC_BOOST:
+ case RT5668_HP_IMP_GAIN_1:
+ case RT5668_HP_IMP_GAIN_2:
+ case RT5668_SIDETONE_CTRL:
+ case RT5668_STO1_ADC_MIXER:
+ case RT5668_AD_DA_MIXER:
+ case RT5668_STO1_DAC_MIXER:
+ case RT5668_A_DAC1_MUX:
+ case RT5668_DIG_INF2_DATA:
+ case RT5668_REC_MIXER:
+ case RT5668_CAL_REC:
+ case RT5668_ALC_BACK_GAIN:
+ case RT5668_PWR_DIG_1:
+ case RT5668_PWR_DIG_2:
+ case RT5668_PWR_ANLG_1:
+ case RT5668_PWR_ANLG_2:
+ case RT5668_PWR_ANLG_3:
+ case RT5668_PWR_MIXER:
+ case RT5668_PWR_VOL:
+ case RT5668_CLK_DET:
+ case RT5668_RESET_LPF_CTRL:
+ case RT5668_RESET_HPF_CTRL:
+ case RT5668_DMIC_CTRL_1:
+ case RT5668_I2S1_SDP:
+ case RT5668_I2S2_SDP:
+ case RT5668_ADDA_CLK_1:
+ case RT5668_ADDA_CLK_2:
+ case RT5668_I2S1_F_DIV_CTRL_1:
+ case RT5668_I2S1_F_DIV_CTRL_2:
+ case RT5668_TDM_CTRL:
+ case RT5668_TDM_ADDA_CTRL_1:
+ case RT5668_TDM_ADDA_CTRL_2:
+ case RT5668_DATA_SEL_CTRL_1:
+ case RT5668_TDM_TCON_CTRL:
+ case RT5668_GLB_CLK:
+ case RT5668_PLL_CTRL_1:
+ case RT5668_PLL_CTRL_2:
+ case RT5668_PLL_TRACK_1:
+ case RT5668_PLL_TRACK_2:
+ case RT5668_PLL_TRACK_3:
+ case RT5668_PLL_TRACK_4:
+ case RT5668_PLL_TRACK_5:
+ case RT5668_PLL_TRACK_6:
+ case RT5668_PLL_TRACK_11:
+ case RT5668_SDW_REF_CLK:
+ case RT5668_DEPOP_1:
+ case RT5668_DEPOP_2:
+ case RT5668_HP_CHARGE_PUMP_1:
+ case RT5668_HP_CHARGE_PUMP_2:
+ case RT5668_MICBIAS_1:
+ case RT5668_MICBIAS_2:
+ case RT5668_PLL_TRACK_12:
+ case RT5668_PLL_TRACK_14:
+ case RT5668_PLL2_CTRL_1:
+ case RT5668_PLL2_CTRL_2:
+ case RT5668_PLL2_CTRL_3:
+ case RT5668_PLL2_CTRL_4:
+ case RT5668_RC_CLK_CTRL:
+ case RT5668_I2S_M_CLK_CTRL_1:
+ case RT5668_I2S2_F_DIV_CTRL_1:
+ case RT5668_I2S2_F_DIV_CTRL_2:
+ case RT5668_EQ_CTRL_1:
+ case RT5668_EQ_CTRL_2:
+ case RT5668_IRQ_CTRL_1:
+ case RT5668_IRQ_CTRL_2:
+ case RT5668_IRQ_CTRL_3:
+ case RT5668_IRQ_CTRL_4:
+ case RT5668_INT_ST_1:
+ case RT5668_GPIO_CTRL_1:
+ case RT5668_GPIO_CTRL_2:
+ case RT5668_GPIO_CTRL_3:
+ case RT5668_HP_AMP_DET_CTRL_1:
+ case RT5668_HP_AMP_DET_CTRL_2:
+ case RT5668_MID_HP_AMP_DET:
+ case RT5668_LOW_HP_AMP_DET:
+ case RT5668_DELAY_BUF_CTRL:
+ case RT5668_SV_ZCD_1:
+ case RT5668_SV_ZCD_2:
+ case RT5668_IL_CMD_1:
+ case RT5668_IL_CMD_2:
+ case RT5668_IL_CMD_3:
+ case RT5668_IL_CMD_4:
+ case RT5668_IL_CMD_5:
+ case RT5668_IL_CMD_6:
+ case RT5668_4BTN_IL_CMD_1:
+ case RT5668_4BTN_IL_CMD_2:
+ case RT5668_4BTN_IL_CMD_3:
+ case RT5668_4BTN_IL_CMD_4:
+ case RT5668_4BTN_IL_CMD_5:
+ case RT5668_4BTN_IL_CMD_6:
+ case RT5668_4BTN_IL_CMD_7:
+ case RT5668_ADC_STO1_HP_CTRL_1:
+ case RT5668_ADC_STO1_HP_CTRL_2:
+ case RT5668_AJD1_CTRL:
+ case RT5668_JD1_THD:
+ case RT5668_JD2_THD:
+ case RT5668_JD_CTRL_1:
+ case RT5668_DUMMY_1:
+ case RT5668_DUMMY_2:
+ case RT5668_DUMMY_3:
+ case RT5668_DAC_ADC_DIG_VOL1:
+ case RT5668_BIAS_CUR_CTRL_2:
+ case RT5668_BIAS_CUR_CTRL_3:
+ case RT5668_BIAS_CUR_CTRL_4:
+ case RT5668_BIAS_CUR_CTRL_5:
+ case RT5668_BIAS_CUR_CTRL_6:
+ case RT5668_BIAS_CUR_CTRL_7:
+ case RT5668_BIAS_CUR_CTRL_8:
+ case RT5668_BIAS_CUR_CTRL_9:
+ case RT5668_BIAS_CUR_CTRL_10:
+ case RT5668_VREF_REC_OP_FB_CAP_CTRL:
+ case RT5668_CHARGE_PUMP_1:
+ case RT5668_DIG_IN_CTRL_1:
+ case RT5668_PAD_DRIVING_CTRL:
+ case RT5668_SOFT_RAMP_DEPOP:
+ case RT5668_CHOP_DAC:
+ case RT5668_CHOP_ADC:
+ case RT5668_CALIB_ADC_CTRL:
+ case RT5668_VOL_TEST:
+ case RT5668_SPKVDD_DET_STA:
+ case RT5668_TEST_MODE_CTRL_1:
+ case RT5668_TEST_MODE_CTRL_2:
+ case RT5668_TEST_MODE_CTRL_3:
+ case RT5668_TEST_MODE_CTRL_4:
+ case RT5668_TEST_MODE_CTRL_5:
+ case RT5668_PLL1_INTERNAL:
+ case RT5668_PLL2_INTERNAL:
+ case RT5668_STO_NG2_CTRL_1:
+ case RT5668_STO_NG2_CTRL_2:
+ case RT5668_STO_NG2_CTRL_3:
+ case RT5668_STO_NG2_CTRL_4:
+ case RT5668_STO_NG2_CTRL_5:
+ case RT5668_STO_NG2_CTRL_6:
+ case RT5668_STO_NG2_CTRL_7:
+ case RT5668_STO_NG2_CTRL_8:
+ case RT5668_STO_NG2_CTRL_9:
+ case RT5668_STO_NG2_CTRL_10:
+ case RT5668_STO1_DAC_SIL_DET:
+ case RT5668_SIL_PSV_CTRL1:
+ case RT5668_SIL_PSV_CTRL2:
+ case RT5668_SIL_PSV_CTRL3:
+ case RT5668_SIL_PSV_CTRL4:
+ case RT5668_SIL_PSV_CTRL5:
+ case RT5668_HP_IMP_SENS_CTRL_01:
+ case RT5668_HP_IMP_SENS_CTRL_02:
+ case RT5668_HP_IMP_SENS_CTRL_03:
+ case RT5668_HP_IMP_SENS_CTRL_04:
+ case RT5668_HP_IMP_SENS_CTRL_05:
+ case RT5668_HP_IMP_SENS_CTRL_06:
+ case RT5668_HP_IMP_SENS_CTRL_07:
+ case RT5668_HP_IMP_SENS_CTRL_08:
+ case RT5668_HP_IMP_SENS_CTRL_09:
+ case RT5668_HP_IMP_SENS_CTRL_10:
+ case RT5668_HP_IMP_SENS_CTRL_11:
+ case RT5668_HP_IMP_SENS_CTRL_12:
+ case RT5668_HP_IMP_SENS_CTRL_13:
+ case RT5668_HP_IMP_SENS_CTRL_14:
+ case RT5668_HP_IMP_SENS_CTRL_15:
+ case RT5668_HP_IMP_SENS_CTRL_16:
+ case RT5668_HP_IMP_SENS_CTRL_17:
+ case RT5668_HP_IMP_SENS_CTRL_18:
+ case RT5668_HP_IMP_SENS_CTRL_19:
+ case RT5668_HP_IMP_SENS_CTRL_20:
+ case RT5668_HP_IMP_SENS_CTRL_21:
+ case RT5668_HP_IMP_SENS_CTRL_22:
+ case RT5668_HP_IMP_SENS_CTRL_23:
+ case RT5668_HP_IMP_SENS_CTRL_24:
+ case RT5668_HP_IMP_SENS_CTRL_25:
+ case RT5668_HP_IMP_SENS_CTRL_26:
+ case RT5668_HP_IMP_SENS_CTRL_27:
+ case RT5668_HP_IMP_SENS_CTRL_28:
+ case RT5668_HP_IMP_SENS_CTRL_29:
+ case RT5668_HP_IMP_SENS_CTRL_30:
+ case RT5668_HP_IMP_SENS_CTRL_31:
+ case RT5668_HP_IMP_SENS_CTRL_32:
+ case RT5668_HP_IMP_SENS_CTRL_33:
+ case RT5668_HP_IMP_SENS_CTRL_34:
+ case RT5668_HP_IMP_SENS_CTRL_35:
+ case RT5668_HP_IMP_SENS_CTRL_36:
+ case RT5668_HP_IMP_SENS_CTRL_37:
+ case RT5668_HP_IMP_SENS_CTRL_38:
+ case RT5668_HP_IMP_SENS_CTRL_39:
+ case RT5668_HP_IMP_SENS_CTRL_40:
+ case RT5668_HP_IMP_SENS_CTRL_41:
+ case RT5668_HP_IMP_SENS_CTRL_42:
+ case RT5668_HP_IMP_SENS_CTRL_43:
+ case RT5668_HP_LOGIC_CTRL_1:
+ case RT5668_HP_LOGIC_CTRL_2:
+ case RT5668_HP_LOGIC_CTRL_3:
+ case RT5668_HP_CALIB_CTRL_1:
+ case RT5668_HP_CALIB_CTRL_2:
+ case RT5668_HP_CALIB_CTRL_3:
+ case RT5668_HP_CALIB_CTRL_4:
+ case RT5668_HP_CALIB_CTRL_5:
+ case RT5668_HP_CALIB_CTRL_6:
+ case RT5668_HP_CALIB_CTRL_7:
+ case RT5668_HP_CALIB_CTRL_9:
+ case RT5668_HP_CALIB_CTRL_10:
+ case RT5668_HP_CALIB_CTRL_11:
+ case RT5668_HP_CALIB_STA_1:
+ case RT5668_HP_CALIB_STA_2:
+ case RT5668_HP_CALIB_STA_3:
+ case RT5668_HP_CALIB_STA_4:
+ case RT5668_HP_CALIB_STA_5:
+ case RT5668_HP_CALIB_STA_6:
+ case RT5668_HP_CALIB_STA_7:
+ case RT5668_HP_CALIB_STA_8:
+ case RT5668_HP_CALIB_STA_9:
+ case RT5668_HP_CALIB_STA_10:
+ case RT5668_HP_CALIB_STA_11:
+ case RT5668_SAR_IL_CMD_1:
+ case RT5668_SAR_IL_CMD_2:
+ case RT5668_SAR_IL_CMD_3:
+ case RT5668_SAR_IL_CMD_4:
+ case RT5668_SAR_IL_CMD_5:
+ case RT5668_SAR_IL_CMD_6:
+ case RT5668_SAR_IL_CMD_7:
+ case RT5668_SAR_IL_CMD_8:
+ case RT5668_SAR_IL_CMD_9:
+ case RT5668_SAR_IL_CMD_10:
+ case RT5668_SAR_IL_CMD_11:
+ case RT5668_SAR_IL_CMD_12:
+ case RT5668_SAR_IL_CMD_13:
+ case RT5668_EFUSE_CTRL_1:
+ case RT5668_EFUSE_CTRL_2:
+ case RT5668_EFUSE_CTRL_3:
+ case RT5668_EFUSE_CTRL_4:
+ case RT5668_EFUSE_CTRL_5:
+ case RT5668_EFUSE_CTRL_6:
+ case RT5668_EFUSE_CTRL_7:
+ case RT5668_EFUSE_CTRL_8:
+ case RT5668_EFUSE_CTRL_9:
+ case RT5668_EFUSE_CTRL_10:
+ case RT5668_EFUSE_CTRL_11:
+ case RT5668_JD_TOP_VC_VTRL:
+ case RT5668_DRC1_CTRL_0:
+ case RT5668_DRC1_CTRL_1:
+ case RT5668_DRC1_CTRL_2:
+ case RT5668_DRC1_CTRL_3:
+ case RT5668_DRC1_CTRL_4:
+ case RT5668_DRC1_CTRL_5:
+ case RT5668_DRC1_CTRL_6:
+ case RT5668_DRC1_HARD_LMT_CTRL_1:
+ case RT5668_DRC1_HARD_LMT_CTRL_2:
+ case RT5668_DRC1_PRIV_1:
+ case RT5668_DRC1_PRIV_2:
+ case RT5668_DRC1_PRIV_3:
+ case RT5668_DRC1_PRIV_4:
+ case RT5668_DRC1_PRIV_5:
+ case RT5668_DRC1_PRIV_6:
+ case RT5668_DRC1_PRIV_7:
+ case RT5668_DRC1_PRIV_8:
+ case RT5668_EQ_AUTO_RCV_CTRL1:
+ case RT5668_EQ_AUTO_RCV_CTRL2:
+ case RT5668_EQ_AUTO_RCV_CTRL3:
+ case RT5668_EQ_AUTO_RCV_CTRL4:
+ case RT5668_EQ_AUTO_RCV_CTRL5:
+ case RT5668_EQ_AUTO_RCV_CTRL6:
+ case RT5668_EQ_AUTO_RCV_CTRL7:
+ case RT5668_EQ_AUTO_RCV_CTRL8:
+ case RT5668_EQ_AUTO_RCV_CTRL9:
+ case RT5668_EQ_AUTO_RCV_CTRL10:
+ case RT5668_EQ_AUTO_RCV_CTRL11:
+ case RT5668_EQ_AUTO_RCV_CTRL12:
+ case RT5668_EQ_AUTO_RCV_CTRL13:
+ case RT5668_ADC_L_EQ_LPF1_A1:
+ case RT5668_R_EQ_LPF1_A1:
+ case RT5668_L_EQ_LPF1_H0:
+ case RT5668_R_EQ_LPF1_H0:
+ case RT5668_L_EQ_BPF1_A1:
+ case RT5668_R_EQ_BPF1_A1:
+ case RT5668_L_EQ_BPF1_A2:
+ case RT5668_R_EQ_BPF1_A2:
+ case RT5668_L_EQ_BPF1_H0:
+ case RT5668_R_EQ_BPF1_H0:
+ case RT5668_L_EQ_BPF2_A1:
+ case RT5668_R_EQ_BPF2_A1:
+ case RT5668_L_EQ_BPF2_A2:
+ case RT5668_R_EQ_BPF2_A2:
+ case RT5668_L_EQ_BPF2_H0:
+ case RT5668_R_EQ_BPF2_H0:
+ case RT5668_L_EQ_BPF3_A1:
+ case RT5668_R_EQ_BPF3_A1:
+ case RT5668_L_EQ_BPF3_A2:
+ case RT5668_R_EQ_BPF3_A2:
+ case RT5668_L_EQ_BPF3_H0:
+ case RT5668_R_EQ_BPF3_H0:
+ case RT5668_L_EQ_BPF4_A1:
+ case RT5668_R_EQ_BPF4_A1:
+ case RT5668_L_EQ_BPF4_A2:
+ case RT5668_R_EQ_BPF4_A2:
+ case RT5668_L_EQ_BPF4_H0:
+ case RT5668_R_EQ_BPF4_H0:
+ case RT5668_L_EQ_HPF1_A1:
+ case RT5668_R_EQ_HPF1_A1:
+ case RT5668_L_EQ_HPF1_H0:
+ case RT5668_R_EQ_HPF1_H0:
+ case RT5668_L_EQ_PRE_VOL:
+ case RT5668_R_EQ_PRE_VOL:
+ case RT5668_L_EQ_POST_VOL:
+ case RT5668_R_EQ_POST_VOL:
+ case RT5668_I2C_MODE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* Interface data select */
+static const char * const rt5668_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if2_adc_enum,
+ RT5668_DIG_INF2_DATA, RT5668_IF2_ADC_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_01_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC1_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_23_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC2_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_45_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC3_SEL_SFT, rt5668_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_if1_67_adc_enum,
+ RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC4_SEL_SFT, rt5668_data_select);
+
+static const struct snd_kcontrol_new rt5668_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5668_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5668_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5668_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5668_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5668_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5668_if1_67_adc_enum);
+
+static void rt5668_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT5668_RESET, 0);
+ regmap_write(regmap, RT5668_I2C_MODE, 1);
+}
+
+static int rt5668_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5668_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5668_4BTN_IL_CMD_1, val);
+ pr_debug("%s btn_type=%x\n", __func__, btn_type);
+
+ return btn_type;
+}
+
+static void rt5668_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1,
+ RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_EN);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13,
+ RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_BTN);
+ snd_soc_component_write(component, RT5668_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2,
+ RT5668_4BTN_IL_MASK | RT5668_4BTN_IL_RST_MASK,
+ RT5668_4BTN_IL_EN | RT5668_4BTN_IL_NOR);
+ snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3,
+ RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_EN);
+ } else {
+ snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3,
+ RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1,
+ RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_DIS);
+ snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2,
+ RT5668_4BTN_IL_MASK, RT5668_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2,
+ RT5668_4BTN_IL_RST_MASK, RT5668_4BTN_IL_RST);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13,
+ RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_TYPE);
+ }
+}
+
+/**
+ * rt5668_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5668_headset_detect(struct snd_soc_component *component,
+ int jack_insert)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int val, count;
+
+ if (jack_insert) {
+ snd_soc_dapm_force_enable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1,
+ RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_HIGH);
+
+ count = 0;
+ val = snd_soc_component_read(component, RT5668_CBJ_CTRL_2)
+ & RT5668_JACK_TYPE_MASK;
+ while (val == 0 && count < 50) {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component,
+ RT5668_CBJ_CTRL_2) & RT5668_JACK_TYPE_MASK;
+ count++;
+ }
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ rt5668->jack_type = SND_JACK_HEADSET;
+ rt5668_enable_push_button_irq(component, true);
+ break;
+ default:
+ rt5668->jack_type = SND_JACK_HEADPHONE;
+ }
+
+ } else {
+ rt5668_enable_push_button_irq(component, false);
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1,
+ RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_LOW);
+ snd_soc_dapm_disable_pin(dapm, "CBJ Power");
+ snd_soc_dapm_sync(dapm);
+
+ rt5668->jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5668->jack_type);
+ return rt5668->jack_type;
+}
+
+static irqreturn_t rt5668_irq(int irq, void *data)
+{
+ struct rt5668_priv *rt5668 = data;
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5668->jack_detect_work, msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+static void rt5668_jd_check_handler(struct work_struct *work)
+{
+ struct rt5668_priv *rt5668 = container_of(work, struct rt5668_priv,
+ jd_check_work.work);
+
+ if (snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL)
+ & RT5668_JDH_RS_MASK) {
+ /* jack out */
+ rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0);
+
+ snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else {
+ schedule_delayed_work(&rt5668->jd_check_work, 500);
+ }
+}
+
+static int rt5668_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ switch (rt5668->pdata.jd_src) {
+ case RT5668_JD1:
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_2,
+ RT5668_EXT_JD_SRC, RT5668_EXT_JD_SRC_MANUAL);
+ snd_soc_component_write(component, RT5668_CBJ_CTRL_1, 0xd002);
+ snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_3,
+ RT5668_CBJ_IN_BUF_EN, RT5668_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1,
+ RT5668_SAR_POW_MASK, RT5668_SAR_POW_EN);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_IRQ);
+ regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL,
+ RT5668_POW_IRQ | RT5668_POW_JDH |
+ RT5668_POW_ANA, RT5668_POW_IRQ |
+ RT5668_POW_JDH | RT5668_POW_ANA);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_2,
+ RT5668_PWR_JDH | RT5668_PWR_JDL,
+ RT5668_PWR_JDH | RT5668_PWR_JDL);
+ regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2,
+ RT5668_JD1_EN_MASK | RT5668_JD1_POL_MASK,
+ RT5668_JD1_EN | RT5668_JD1_POL_NOR);
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5668->jack_detect_work, msecs_to_jiffies(250));
+ break;
+
+ case RT5668_JD_NULL:
+ regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2,
+ RT5668_JD1_EN_MASK, RT5668_JD1_DIS);
+ regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL,
+ RT5668_POW_JDH | RT5668_POW_JDL, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ rt5668->hs_jack = hs_jack;
+
+ return 0;
+}
+
+static void rt5668_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5668_priv *rt5668 =
+ container_of(work, struct rt5668_priv, jack_detect_work.work);
+ int val, btn_type;
+
+ if (!rt5668->component ||
+ !snd_soc_card_is_instantiated(rt5668->component->card)) {
+ /* card not yet ready, try later */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5668->jack_detect_work, msecs_to_jiffies(15));
+ return;
+ }
+
+ mutex_lock(&rt5668->calibrate_mutex);
+
+ val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL)
+ & RT5668_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5668->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5668->jack_type =
+ rt5668_headset_detect(rt5668->component, 1);
+ } else {
+ /* jack is already in, report button event */
+ rt5668->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5668_button_detect(rt5668->component);
+ /**
+ * rt5668 can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5668->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5668->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5668->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5668->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ btn_type = 0;
+ dev_err(rt5668->component->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0);
+ }
+
+ snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (rt5668->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5668->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5668->jd_check_work);
+
+ mutex_unlock(&rt5668->calibrate_mutex);
+}
+
+static const struct snd_kcontrol_new rt5668_snd_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5668_HPL_GAIN,
+ RT5668_HPR_GAIN, RT5668_G_HP_SFT, 15, 1, hp_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5668_DAC1_DIG_VOL,
+ RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 175, 0, dac_vol_tlv),
+
+ /* IN Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5668_CBJ_BST_CTRL,
+ RT5668_BST_CBJ_SFT, 8, 0, bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_L_MUTE_SFT, RT5668_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 127, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5668_STO1_ADC_BOOST,
+ RT5668_STO1_ADC_L_BST_SFT, RT5668_STO1_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+};
+
+
+static int rt5668_div_sel(struct rt5668_priv *rt5668,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5668->sysclk < target) {
+ pr_err("sysclk rate %d is too low\n",
+ rt5668->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ pr_info("div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5668->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5668->sysclk) {
+ pr_err("can't find div for sysclk %d\n",
+ rt5668->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5668->sysclk)
+ pr_err("sysclk rate %d is too high\n",
+ rt5668->sysclk);
+
+ return size - 1;
+
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ int idx;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ idx = rt5668_div_sel(rt5668, 1500000, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_CLK_MASK, idx << RT5668_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+static int set_filter_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ int ref, val, reg, idx;
+ static const int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ val = snd_soc_component_read(component, RT5668_GPIO_CTRL_1) &
+ RT5668_GP4_PIN_MASK;
+ if (w->shift == RT5668_PWR_ADC_S1F_BIT &&
+ val == RT5668_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5668->lrck[RT5668_AIF2];
+ else
+ ref = 256 * rt5668->lrck[RT5668_AIF1];
+
+ idx = rt5668_div_sel(rt5668, ref, div, ARRAY_SIZE(div));
+
+ if (w->shift == RT5668_PWR_ADC_S1F_BIT)
+ reg = RT5668_PLL_TRACK_3;
+ else
+ reg = RT5668_PLL_TRACK_2;
+
+ snd_soc_component_update_bits(component, reg,
+ RT5668_FILTER_CLK_SEL_MASK, idx << RT5668_FILTER_CLK_SEL_SFT);
+
+ return 0;
+}
+
+static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5668_GLB_CLK);
+ val &= RT5668_SCLK_SRC_MASK;
+ if (val == RT5668_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, shift, val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5668_ADC_STO1_ASRC_SFT:
+ reg = RT5668_PLL_TRACK_3;
+ shift = RT5668_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5668_DAC_STO1_ASRC_SFT:
+ reg = RT5668_PLL_TRACK_2;
+ shift = RT5668_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
+ switch (val) {
+ case RT5668_CLK_SEL_I2S1_ASRC:
+ case RT5668_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5668_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER,
+ RT5668_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER,
+ RT5668_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5668_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5668_REC_MIXER,
+ RT5668_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5668_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc1l_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC1L_SRC_SFT, rt5668_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc1r_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC1R_SRC_SFT, rt5668_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5668_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adcl_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADCL_SRC_SFT, rt5668_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5668_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adcr_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADCR_SRC_SFT, rt5668_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5668_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5668_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc2l_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC2L_SRC_SFT, rt5668_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5668_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_sto1_adc2r_enum, RT5668_STO1_ADC_MIXER,
+ RT5668_STO1_ADC2R_SRC_SFT, rt5668_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5668_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5668_if1_adc_slot_values[] = {
+ 0,
+ 2,
+ 4,
+ 6,
+};
+
+static const char * const rt5668_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_if1_adc_slot_enum,
+ RT5668_TDM_CTRL, RT5668_TDM_ADC_LCA_SFT, RT5668_TDM_ADC_LCA_MASK,
+ rt5668_if1_adc_slot_src, rt5668_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5668_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5668_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5668_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_alg_dac_l1_enum, RT5668_A_DAC1_MUX,
+ RT5668_A_DACL1_SFT, rt5668_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5668_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5668_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5668_alg_dac_r1_enum, RT5668_A_DAC1_MUX,
+ RT5668_A_DACR1_SFT, rt5668_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5668_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5668_alg_dac_r1_enum);
+
+/* Out Switch */
+static const struct snd_kcontrol_new hpol_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1,
+ RT5668_L_MUTE_SFT, 1, 1);
+static const struct snd_kcontrol_new hpor_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1,
+ RT5668_R_MUTE_SFT, 1, 1);
+
+static int rt5668_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(component,
+ RT5668_HP_LOGIC_CTRL_2, 0x0012);
+ snd_soc_component_write(component,
+ RT5668_HP_CTRL_2, 0x6000);
+ snd_soc_component_update_bits(component, RT5668_STO_NG2_CTRL_1,
+ RT5668_NG2_EN_MASK, RT5668_NG2_EN);
+ snd_soc_component_update_bits(component,
+ RT5668_DEPOP_1, 0x60, 0x60);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ RT5668_DEPOP_1, 0x60, 0x0);
+ snd_soc_component_write(component,
+ RT5668_HP_CTRL_2, 0x0000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+
+}
+
+static int set_dmic_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*Add delay to avoid pop noise*/
+ msleep(150);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case RT5668_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV1, 0);
+ break;
+
+ case RT5668_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV2, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(15000, 20000);
+ switch (w->shift) {
+ case RT5668_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV1,
+ RT5668_PWR_FV1);
+ break;
+
+ case RT5668_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5668_PWR_ANLG_1, RT5668_PWR_FV2,
+ RT5668_PWR_FV2);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const unsigned int rt5668_adcdat_pin_values[] = {
+ 1,
+ 3,
+};
+
+static const char * const rt5668_adcdat_pin_select[] = {
+ "ADCDAT1",
+ "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_adcdat_pin_enum,
+ RT5668_GPIO_CTRL_1, RT5668_GP4_PIN_SFT, RT5668_GP4_PIN_MASK,
+ rt5668_adcdat_pin_select, rt5668_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5668_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5668_adcdat_pin_enum);
+
+static const struct snd_soc_dapm_widget rt5668_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5668_PWR_ANLG_3, RT5668_PWR_LDO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5668_PWR_ANLG_3, RT5668_PWR_PLL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2B_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2F_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref1", RT5668_PWR_ANLG_1, RT5668_PWR_VREF1_BIT, 0,
+ rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref2", RT5668_PWR_ANLG_1, RT5668_PWR_VREF2_BIT, 0,
+ rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5668_PLL_TRACK_1,
+ RT5668_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5668_PWR_ANLG_2, RT5668_PWR_MB1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5668_PWR_ANLG_2, RT5668_PWR_MB2_BIT,
+ 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("CBJ Power", RT5668_PWR_ANLG_3,
+ RT5668_PWR_CBJ_BIT, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5668_rec1_l_mix,
+ ARRAY_SIZE(rt5668_rec1_l_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5668_PWR_ANLG_2,
+ RT5668_PWR_RM1_L_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5668_PWR_DIG_1,
+ RT5668_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5668_PWR_DIG_1,
+ RT5668_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5668_CHOP_ADC,
+ RT5668_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5668_PWR_DIG_2,
+ RT5668_PWR_ADC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_L_MUTE_SFT, 1, rt5668_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5668_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5668_STO1_ADC_DIG_VOL,
+ RT5668_R_MUTE_SFT, 1, rt5668_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5668_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5668_PWR_DIG_1, RT5668_PWR_I2S1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5668_PWR_DIG_1, RT5668_PWR_I2S2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_adcdat_pin_ctrl),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0,
+ RT5668_I2S1_SDP, RT5668_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ RT5668_I2S2_SDP, RT5668_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5668_dac_l_mix, ARRAY_SIZE(rt5668_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5668_dac_r_mix, ARRAY_SIZE(rt5668_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5668_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5668_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5668_PWR_DIG_2,
+ RT5668_PWR_DAC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5668_sto1_dac_l_mix, ARRAY_SIZE(rt5668_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5668_sto1_dac_r_mix, ARRAY_SIZE(rt5668_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5668_PWR_DIG_1,
+ RT5668_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5668_PWR_DIG_1,
+ RT5668_PWR_DAC_R1_BIT, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5668_CHOP_DAC,
+ RT5668_CKGEN_DAC1_SFT, 0, NULL, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5668_hp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("HP Amp L", RT5668_PWR_ANLG_1,
+ RT5668_PWR_HA_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP Amp R", RT5668_PWR_ANLG_1,
+ RT5668_PWR_HA_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5668_DEPOP_1,
+ RT5668_PUMP_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5668_DEPOP_1,
+ RT5668_CAPLESS_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0,
+ &hpol_switch),
+ SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0,
+ &hpor_switch),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5668_CLK_DET,
+ RT5668_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5668_CLK_DET,
+ RT5668_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5668_CLK_DET,
+ RT5668_PLL2_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET", RT5668_CLK_DET,
+ RT5668_POW_CLK_DET_SFT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+
+};
+
+static const struct snd_soc_dapm_route rt5668_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+ {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+
+ /*Vref*/
+ {"MICBIAS1", NULL, "Vref1"},
+ {"MICBIAS1", NULL, "Vref2"},
+ {"MICBIAS2", NULL, "Vref1"},
+ {"MICBIAS2", NULL, "Vref2"},
+
+ {"CLKDET SYS", NULL, "CLKDET"},
+
+ {"IN1P", NULL, "LDO2"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+ {"BST1 CBJ", NULL, "CBJ Power"},
+ {"CBJ Power", NULL, "Vref2"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"IF1_ADC Mux", NULL, "I2S1"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"DAC L1", NULL, "DAC 1 Clock"},
+ {"DAC R1", NULL, "DAC 1 Clock"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "HP Amp L"},
+ {"HP Amp", NULL, "HP Amp R"},
+ {"HP Amp", NULL, "Capless"},
+ {"HP Amp", NULL, "Charge Pump"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "CBJ Power"},
+ {"HP Amp", NULL, "Vref2"},
+ {"HPOL Playback", "Switch", "HP Amp"},
+ {"HPOR Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HPOL Playback"},
+ {"HPOR", NULL, "HPOR Playback"},
+};
+
+static int rt5668_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0;
+
+ switch (slots) {
+ case 4:
+ val |= RT5668_TDM_TX_CH_4;
+ val |= RT5668_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5668_TDM_TX_CH_6;
+ val |= RT5668_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5668_TDM_TX_CH_8;
+ val |= RT5668_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5668_TDM_CTRL,
+ RT5668_TDM_TX_CH_MASK | RT5668_TDM_RX_CH_MASK, val);
+
+ switch (slot_width) {
+ case 16:
+ val = RT5668_TDM_CL_16;
+ break;
+ case 20:
+ val = RT5668_TDM_CL_20;
+ break;
+ case 24:
+ val = RT5668_TDM_CL_24;
+ break;
+ case 32:
+ val = RT5668_TDM_CL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL,
+ RT5668_TDM_CL_MASK, val);
+
+ return 0;
+}
+
+
+static int rt5668_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int pre_div, frame_size;
+
+ rt5668->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5668->sysclk, rt5668->lrck[dai->id]);
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt5668->lrck[dai->id], pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5668_I2S1_DL_20;
+ len_2 |= RT5668_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5668_I2S1_DL_24;
+ len_2 |= RT5668_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5668_I2S1_DL_32;
+ len_2 |= RT5668_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5668_I2S2_DL_8;
+ len_2 |= RT5668_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5668_AIF1:
+ snd_soc_component_update_bits(component, RT5668_I2S1_SDP,
+ RT5668_I2S1_DL_MASK, len_1);
+ if (rt5668->master[RT5668_AIF1]) {
+ snd_soc_component_update_bits(component,
+ RT5668_ADDA_CLK_1, RT5668_I2S_M_DIV_MASK,
+ pre_div << RT5668_I2S_M_DIV_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK,
+ RT5668_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK,
+ RT5668_I2S1_MONO_DIS);
+ break;
+ case RT5668_AIF2:
+ snd_soc_component_update_bits(component, RT5668_I2S2_SDP,
+ RT5668_I2S2_DL_MASK, len_2);
+ if (rt5668->master[RT5668_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_M_PD_MASK,
+ pre_div << RT5668_I2S2_M_PD_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK,
+ RT5668_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK,
+ RT5668_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5668_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5668->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt5668->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5668_I2S_BP_INV;
+ tdm_ctrl |= RT5668_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5668_AIF1)
+ tdm_ctrl |= RT5668_TDM_S_LP_INV | RT5668_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5668_AIF1)
+ tdm_ctrl |= RT5668_TDM_S_BP_INV | RT5668_TDM_S_LP_INV |
+ RT5668_TDM_M_BP_INV | RT5668_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5668_I2S_DF_LEFT;
+ tdm_ctrl |= RT5668_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5668_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5668_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5668_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5668_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5668_AIF1:
+ snd_soc_component_update_bits(component, RT5668_I2S1_SDP,
+ RT5668_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL,
+ RT5668_TDM_MS_MASK | RT5668_TDM_S_BP_MASK |
+ RT5668_TDM_DF_MASK | RT5668_TDM_M_BP_MASK |
+ RT5668_TDM_M_LP_MASK | RT5668_TDM_S_LP_MASK,
+ tdm_ctrl | rt5668->master[dai->id]);
+ break;
+ case RT5668_AIF2:
+ if (rt5668->master[dai->id] == 0)
+ reg_val |= RT5668_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5668_I2S2_SDP,
+ RT5668_I2S2_MS_MASK | RT5668_I2S_BP_MASK |
+ RT5668_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5668_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, src = 0;
+
+ if (freq == rt5668->sysclk && clk_id == rt5668->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5668_SCLK_S_MCLK:
+ reg_val |= RT5668_SCLK_SRC_MCLK;
+ src = RT5668_CLK_SRC_MCLK;
+ break;
+ case RT5668_SCLK_S_PLL1:
+ reg_val |= RT5668_SCLK_SRC_PLL1;
+ src = RT5668_CLK_SRC_PLL1;
+ break;
+ case RT5668_SCLK_S_PLL2:
+ reg_val |= RT5668_SCLK_SRC_PLL2;
+ src = RT5668_CLK_SRC_PLL2;
+ break;
+ case RT5668_SCLK_S_RCCLK:
+ reg_val |= RT5668_SCLK_SRC_RCCLK;
+ src = RT5668_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_SCLK_SRC_MASK, reg_val);
+
+ if (rt5668->master[RT5668_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_SRC_MASK,
+ src << RT5668_I2S2_SRC_SFT);
+ }
+
+ rt5668->sysclk = freq;
+ rt5668->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt5668_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5668->pll_src && freq_in == rt5668->pll_in &&
+ freq_out == rt5668->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5668->pll_in = 0;
+ rt5668->pll_out = 0;
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_SCLK_SRC_MASK, RT5668_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5668_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_MCLK);
+ break;
+ case RT5668_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5668_GLB_CLK,
+ RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT5668_PLL_CTRL_1,
+ pll_code.n_code << RT5668_PLL_N_SFT | pll_code.k_code);
+ snd_soc_component_write(component, RT5668_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5668_PLL_M_SFT) |
+ (pll_code.m_bp << RT5668_PLL_M_BP_SFT));
+
+ rt5668->pll_in = freq_in;
+ rt5668->pll_out = freq_out;
+ rt5668->pll_src = source;
+
+ return 0;
+}
+
+static int rt5668_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ rt5668->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2,
+ RT5668_I2S2_BCLK_MS2_MASK,
+ RT5668_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2,
+ RT5668_I2S2_BCLK_MS2_MASK,
+ RT5668_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5668_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_PWR_MB | RT5668_PWR_BG,
+ RT5668_PWR_MB | RT5668_PWR_BG);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1,
+ RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO,
+ RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_PWR_MB, RT5668_PWR_MB);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1,
+ RT5668_DIG_GATE_CTRL, RT5668_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1,
+ RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO, 0);
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_PWR_MB | RT5668_PWR_BG, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5668_probe(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ rt5668->component = component;
+
+ return 0;
+}
+
+static void rt5668_remove(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ rt5668_reset(rt5668->regmap);
+}
+
+#ifdef CONFIG_PM
+static int rt5668_suspend(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5668->regmap, true);
+ regcache_mark_dirty(rt5668->regmap);
+ return 0;
+}
+
+static int rt5668_resume(struct snd_soc_component *component)
+{
+ struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5668->regmap, false);
+ regcache_sync(rt5668->regmap);
+
+ return 0;
+}
+#else
+#define rt5668_suspend NULL
+#define rt5668_resume NULL
+#endif
+
+#define RT5668_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5668_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5668_aif1_dai_ops = {
+ .hw_params = rt5668_hw_params,
+ .set_fmt = rt5668_set_dai_fmt,
+ .set_tdm_slot = rt5668_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_ops rt5668_aif2_dai_ops = {
+ .hw_params = rt5668_hw_params,
+ .set_fmt = rt5668_set_dai_fmt,
+ .set_bclk_ratio = rt5668_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt5668_dai[] = {
+ {
+ .name = "rt5668-aif1",
+ .id = RT5668_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5668_STEREO_RATES,
+ .formats = RT5668_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5668_STEREO_RATES,
+ .formats = RT5668_FORMATS,
+ },
+ .ops = &rt5668_aif1_dai_ops,
+ },
+ {
+ .name = "rt5668-aif2",
+ .id = RT5668_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5668_STEREO_RATES,
+ .formats = RT5668_FORMATS,
+ },
+ .ops = &rt5668_aif2_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt5668 = {
+ .probe = rt5668_probe,
+ .remove = rt5668_remove,
+ .suspend = rt5668_suspend,
+ .resume = rt5668_resume,
+ .set_bias_level = rt5668_set_bias_level,
+ .controls = rt5668_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5668_snd_controls),
+ .dapm_widgets = rt5668_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5668_dapm_widgets),
+ .dapm_routes = rt5668_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5668_dapm_routes),
+ .set_sysclk = rt5668_set_component_sysclk,
+ .set_pll = rt5668_set_component_pll,
+ .set_jack = rt5668_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5668_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5668_I2C_MODE,
+ .volatile_reg = rt5668_volatile_register,
+ .readable_reg = rt5668_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5668_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5668_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt5668_i2c_id[] = {
+ {"rt5668b"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5668_i2c_id);
+
+static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev)
+{
+
+ of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin",
+ &rt5668->pdata.dmic1_data_pin);
+ of_property_read_u32(dev->of_node, "realtek,dmic1-clk-pin",
+ &rt5668->pdata.dmic1_clk_pin);
+ of_property_read_u32(dev->of_node, "realtek,jd-src",
+ &rt5668->pdata.jd_src);
+
+ return 0;
+}
+
+static void rt5668_calibrate(struct rt5668_priv *rt5668)
+{
+ int value, count;
+
+ mutex_lock(&rt5668->calibrate_mutex);
+
+ rt5668_reset(rt5668->regmap);
+ regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf);
+ usleep_range(15000, 20000);
+ regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xf2bf);
+ regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380);
+ regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8001);
+ regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000);
+ regmap_write(rt5668->regmap, RT5668_STO1_DAC_MIXER, 0x2080);
+ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x4040);
+ regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0069);
+ regmap_write(rt5668->regmap, RT5668_CHOP_DAC, 0x3000);
+ regmap_write(rt5668->regmap, RT5668_HP_CTRL_2, 0x6000);
+ regmap_write(rt5668->regmap, RT5668_HP_CHARGE_PUMP_1, 0x0f26);
+ regmap_write(rt5668->regmap, RT5668_CALIB_ADC_CTRL, 0x7f05);
+ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5668->regmap, RT5668_CAL_REC, 0x0d0d);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_9, 0x000f);
+ regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8d01);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5668->regmap, RT5668_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_3, 0x06a1);
+ regmap_write(rt5668->regmap, RT5668_A_DAC1_MUX, 0x0311);
+ regmap_write(rt5668->regmap, RT5668_RESET_HPF_CTRL, 0x0000);
+ regmap_write(rt5668->regmap, RT5668_ADC_STO1_HP_CTRL_1, 0x3320);
+
+ regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5668->regmap, RT5668_HP_CALIB_STA_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ pr_err("HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000);
+
+ mutex_unlock(&rt5668->calibrate_mutex);
+
+}
+
+static int rt5668_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5668_priv *rt5668;
+ int i, ret;
+ unsigned int val;
+
+ rt5668 = devm_kzalloc(&i2c->dev, sizeof(struct rt5668_priv),
+ GFP_KERNEL);
+
+ if (rt5668 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5668);
+
+ if (pdata)
+ rt5668->pdata = *pdata;
+ else
+ rt5668_parse_dt(rt5668, &i2c->dev);
+
+ rt5668->regmap = devm_regmap_init_i2c(i2c, &rt5668_regmap);
+ if (IS_ERR(rt5668->regmap)) {
+ ret = PTR_ERR(rt5668->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5668->supplies); i++)
+ rt5668->supplies[i].supply = rt5668_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5668->supplies),
+ rt5668->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5668->supplies),
+ rt5668->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ rt5668->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5668->ldo1_en)) {
+ dev_err(&i2c->dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5668->ldo1_en);
+ }
+
+ /* Sleep for 300 ms miniumum */
+ usleep_range(300000, 350000);
+
+ regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1);
+ usleep_range(10000, 15000);
+
+ regmap_read(rt5668->regmap, RT5668_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ pr_err("Device with ID register %x is not rt5668\n", val);
+ return -ENODEV;
+ }
+
+ rt5668_reset(rt5668->regmap);
+
+ rt5668_calibrate(rt5668);
+
+ regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0000);
+
+ /* DMIC pin*/
+ if (rt5668->pdata.dmic1_data_pin != RT5668_DMIC1_NULL) {
+ switch (rt5668->pdata.dmic1_data_pin) {
+ case RT5668_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP2_PIN_MASK, RT5668_GP2_PIN_DMIC_SDA);
+ break;
+
+ case RT5668_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1,
+ RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP5_PIN_MASK, RT5668_GP5_PIN_DMIC_SDA);
+ break;
+
+ default:
+ dev_dbg(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ switch (rt5668->pdata.dmic1_clk_pin) {
+ case RT5668_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_DMIC_CLK);
+ break;
+
+ case RT5668_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP3_PIN_MASK, RT5668_GP3_PIN_DMIC_CLK);
+ break;
+
+ default:
+ dev_dbg(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+ }
+
+ regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1,
+ RT5668_LDO1_DVO_MASK | RT5668_HP_DRIVER_MASK,
+ RT5668_LDO1_DVO_14 | RT5668_HP_DRIVER_5X);
+ regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380);
+ regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1,
+ RT5668_GP4_PIN_MASK | RT5668_GP5_PIN_MASK,
+ RT5668_GP4_PIN_ADCDAT1 | RT5668_GP5_PIN_DACDAT1);
+ regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000);
+
+ INIT_DELAYED_WORK(&rt5668->jack_detect_work,
+ rt5668_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5668->jd_check_work,
+ rt5668_jd_check_handler);
+
+ mutex_init(&rt5668->calibrate_mutex);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5668", rt5668);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668,
+ rt5668_dai, ARRAY_SIZE(rt5668_dai));
+}
+
+static void rt5668_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5668_priv *rt5668 = i2c_get_clientdata(client);
+
+ rt5668_reset(rt5668->regmap);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt5668_of_match[] = {
+ {.compatible = "realtek,rt5668b"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5668_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5668_acpi_match[] = {
+ { "10EC5668" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5668_acpi_match);
+#endif
+
+static struct i2c_driver rt5668_i2c_driver = {
+ .driver = {
+ .name = "rt5668b",
+ .of_match_table = of_match_ptr(rt5668_of_match),
+ .acpi_match_table = ACPI_PTR(rt5668_acpi_match),
+ },
+ .probe = rt5668_i2c_probe,
+ .shutdown = rt5668_i2c_shutdown,
+ .id_table = rt5668_i2c_id,
+};
+module_i2c_driver(rt5668_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5668B driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h
new file mode 100644
index 000000000000..b34a61d2109c
--- /dev/null
+++ b/sound/soc/codecs/rt5668.h
@@ -0,0 +1,1312 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5668.h -- RT5668/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2018 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5668_H__
+#define __RT5668_H__
+
+#include <sound/rt5668.h>
+
+#define DEVICE_ID 0x6530
+
+/* Info */
+#define RT5668_RESET 0x0000
+#define RT5668_VERSION_ID 0x00fd
+#define RT5668_VENDOR_ID 0x00fe
+#define RT5668_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5668_HP_CTRL_1 0x0002
+#define RT5668_HP_CTRL_2 0x0003
+#define RT5668_HPL_GAIN 0x0005
+#define RT5668_HPR_GAIN 0x0006
+
+#define RT5668_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5668_CBJ_BST_CTRL 0x000b
+#define RT5668_CBJ_CTRL_1 0x0010
+#define RT5668_CBJ_CTRL_2 0x0011
+#define RT5668_CBJ_CTRL_3 0x0012
+#define RT5668_CBJ_CTRL_4 0x0013
+#define RT5668_CBJ_CTRL_5 0x0014
+#define RT5668_CBJ_CTRL_6 0x0015
+#define RT5668_CBJ_CTRL_7 0x0016
+/* I/O - ADC/DAC/DMIC */
+#define RT5668_DAC1_DIG_VOL 0x0019
+#define RT5668_STO1_ADC_DIG_VOL 0x001c
+#define RT5668_STO1_ADC_BOOST 0x001f
+#define RT5668_HP_IMP_GAIN_1 0x0022
+#define RT5668_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5668_SIDETONE_CTRL 0x0024
+#define RT5668_STO1_ADC_MIXER 0x0026
+#define RT5668_AD_DA_MIXER 0x0029
+#define RT5668_STO1_DAC_MIXER 0x002a
+#define RT5668_A_DAC1_MUX 0x002b
+#define RT5668_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5668_REC_MIXER 0x003c
+#define RT5668_CAL_REC 0x0044
+#define RT5668_ALC_BACK_GAIN 0x0049
+/* Power */
+#define RT5668_PWR_DIG_1 0x0061
+#define RT5668_PWR_DIG_2 0x0062
+#define RT5668_PWR_ANLG_1 0x0063
+#define RT5668_PWR_ANLG_2 0x0064
+#define RT5668_PWR_ANLG_3 0x0065
+#define RT5668_PWR_MIXER 0x0066
+#define RT5668_PWR_VOL 0x0067
+/* Clock Detect */
+#define RT5668_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5668_RESET_LPF_CTRL 0x006c
+#define RT5668_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5668_DMIC_CTRL_1 0x006e
+/* Format - ADC/DAC */
+#define RT5668_I2S1_SDP 0x0070
+#define RT5668_I2S2_SDP 0x0071
+#define RT5668_ADDA_CLK_1 0x0073
+#define RT5668_ADDA_CLK_2 0x0074
+#define RT5668_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5668_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5668_TDM_CTRL 0x0079
+#define RT5668_TDM_ADDA_CTRL_1 0x007a
+#define RT5668_TDM_ADDA_CTRL_2 0x007b
+#define RT5668_DATA_SEL_CTRL_1 0x007c
+#define RT5668_TDM_TCON_CTRL 0x007e
+/* Function - Analog */
+#define RT5668_GLB_CLK 0x0080
+#define RT5668_PLL_CTRL_1 0x0081
+#define RT5668_PLL_CTRL_2 0x0082
+#define RT5668_PLL_TRACK_1 0x0083
+#define RT5668_PLL_TRACK_2 0x0084
+#define RT5668_PLL_TRACK_3 0x0085
+#define RT5668_PLL_TRACK_4 0x0086
+#define RT5668_PLL_TRACK_5 0x0087
+#define RT5668_PLL_TRACK_6 0x0088
+#define RT5668_PLL_TRACK_11 0x008c
+#define RT5668_SDW_REF_CLK 0x008d
+#define RT5668_DEPOP_1 0x008e
+#define RT5668_DEPOP_2 0x008f
+#define RT5668_HP_CHARGE_PUMP_1 0x0091
+#define RT5668_HP_CHARGE_PUMP_2 0x0092
+#define RT5668_MICBIAS_1 0x0093
+#define RT5668_MICBIAS_2 0x0094
+#define RT5668_PLL_TRACK_12 0x0098
+#define RT5668_PLL_TRACK_14 0x009a
+#define RT5668_PLL2_CTRL_1 0x009b
+#define RT5668_PLL2_CTRL_2 0x009c
+#define RT5668_PLL2_CTRL_3 0x009d
+#define RT5668_PLL2_CTRL_4 0x009e
+#define RT5668_RC_CLK_CTRL 0x009f
+#define RT5668_I2S_M_CLK_CTRL_1 0x00a0
+#define RT5668_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5668_I2S2_F_DIV_CTRL_2 0x00a4
+/* Function - Digital */
+#define RT5668_EQ_CTRL_1 0x00ae
+#define RT5668_EQ_CTRL_2 0x00af
+#define RT5668_IRQ_CTRL_1 0x00b6
+#define RT5668_IRQ_CTRL_2 0x00b7
+#define RT5668_IRQ_CTRL_3 0x00b8
+#define RT5668_IRQ_CTRL_4 0x00b9
+#define RT5668_INT_ST_1 0x00be
+#define RT5668_GPIO_CTRL_1 0x00c0
+#define RT5668_GPIO_CTRL_2 0x00c1
+#define RT5668_GPIO_CTRL_3 0x00c2
+#define RT5668_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5668_HP_AMP_DET_CTRL_2 0x00d1
+#define RT5668_MID_HP_AMP_DET 0x00d2
+#define RT5668_LOW_HP_AMP_DET 0x00d3
+#define RT5668_DELAY_BUF_CTRL 0x00d4
+#define RT5668_SV_ZCD_1 0x00d9
+#define RT5668_SV_ZCD_2 0x00da
+#define RT5668_IL_CMD_1 0x00db
+#define RT5668_IL_CMD_2 0x00dc
+#define RT5668_IL_CMD_3 0x00dd
+#define RT5668_IL_CMD_4 0x00de
+#define RT5668_IL_CMD_5 0x00df
+#define RT5668_IL_CMD_6 0x00e0
+#define RT5668_4BTN_IL_CMD_1 0x00e2
+#define RT5668_4BTN_IL_CMD_2 0x00e3
+#define RT5668_4BTN_IL_CMD_3 0x00e4
+#define RT5668_4BTN_IL_CMD_4 0x00e5
+#define RT5668_4BTN_IL_CMD_5 0x00e6
+#define RT5668_4BTN_IL_CMD_6 0x00e7
+#define RT5668_4BTN_IL_CMD_7 0x00e8
+
+#define RT5668_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5668_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5668_AJD1_CTRL 0x00f0
+#define RT5668_JD1_THD 0x00f1
+#define RT5668_JD2_THD 0x00f2
+#define RT5668_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5668_DUMMY_1 0x00fa
+#define RT5668_DUMMY_2 0x00fb
+#define RT5668_DUMMY_3 0x00fc
+
+#define RT5668_DAC_ADC_DIG_VOL1 0x0100
+#define RT5668_BIAS_CUR_CTRL_2 0x010b
+#define RT5668_BIAS_CUR_CTRL_3 0x010c
+#define RT5668_BIAS_CUR_CTRL_4 0x010d
+#define RT5668_BIAS_CUR_CTRL_5 0x010e
+#define RT5668_BIAS_CUR_CTRL_6 0x010f
+#define RT5668_BIAS_CUR_CTRL_7 0x0110
+#define RT5668_BIAS_CUR_CTRL_8 0x0111
+#define RT5668_BIAS_CUR_CTRL_9 0x0112
+#define RT5668_BIAS_CUR_CTRL_10 0x0113
+#define RT5668_VREF_REC_OP_FB_CAP_CTRL 0x0117
+#define RT5668_CHARGE_PUMP_1 0x0125
+#define RT5668_DIG_IN_CTRL_1 0x0132
+#define RT5668_PAD_DRIVING_CTRL 0x0136
+#define RT5668_SOFT_RAMP_DEPOP 0x0138
+#define RT5668_CHOP_DAC 0x013a
+#define RT5668_CHOP_ADC 0x013b
+#define RT5668_CALIB_ADC_CTRL 0x013c
+#define RT5668_VOL_TEST 0x013f
+#define RT5668_SPKVDD_DET_STA 0x0142
+#define RT5668_TEST_MODE_CTRL_1 0x0145
+#define RT5668_TEST_MODE_CTRL_2 0x0146
+#define RT5668_TEST_MODE_CTRL_3 0x0147
+#define RT5668_TEST_MODE_CTRL_4 0x0148
+#define RT5668_TEST_MODE_CTRL_5 0x0149
+#define RT5668_PLL1_INTERNAL 0x0150
+#define RT5668_PLL2_INTERNAL 0x0151
+#define RT5668_STO_NG2_CTRL_1 0x0160
+#define RT5668_STO_NG2_CTRL_2 0x0161
+#define RT5668_STO_NG2_CTRL_3 0x0162
+#define RT5668_STO_NG2_CTRL_4 0x0163
+#define RT5668_STO_NG2_CTRL_5 0x0164
+#define RT5668_STO_NG2_CTRL_6 0x0165
+#define RT5668_STO_NG2_CTRL_7 0x0166
+#define RT5668_STO_NG2_CTRL_8 0x0167
+#define RT5668_STO_NG2_CTRL_9 0x0168
+#define RT5668_STO_NG2_CTRL_10 0x0169
+#define RT5668_STO1_DAC_SIL_DET 0x0190
+#define RT5668_SIL_PSV_CTRL1 0x0194
+#define RT5668_SIL_PSV_CTRL2 0x0195
+#define RT5668_SIL_PSV_CTRL3 0x0197
+#define RT5668_SIL_PSV_CTRL4 0x0198
+#define RT5668_SIL_PSV_CTRL5 0x0199
+#define RT5668_HP_IMP_SENS_CTRL_01 0x01af
+#define RT5668_HP_IMP_SENS_CTRL_02 0x01b0
+#define RT5668_HP_IMP_SENS_CTRL_03 0x01b1
+#define RT5668_HP_IMP_SENS_CTRL_04 0x01b2
+#define RT5668_HP_IMP_SENS_CTRL_05 0x01b3
+#define RT5668_HP_IMP_SENS_CTRL_06 0x01b4
+#define RT5668_HP_IMP_SENS_CTRL_07 0x01b5
+#define RT5668_HP_IMP_SENS_CTRL_08 0x01b6
+#define RT5668_HP_IMP_SENS_CTRL_09 0x01b7
+#define RT5668_HP_IMP_SENS_CTRL_10 0x01b8
+#define RT5668_HP_IMP_SENS_CTRL_11 0x01b9
+#define RT5668_HP_IMP_SENS_CTRL_12 0x01ba
+#define RT5668_HP_IMP_SENS_CTRL_13 0x01bb
+#define RT5668_HP_IMP_SENS_CTRL_14 0x01bc
+#define RT5668_HP_IMP_SENS_CTRL_15 0x01bd
+#define RT5668_HP_IMP_SENS_CTRL_16 0x01be
+#define RT5668_HP_IMP_SENS_CTRL_17 0x01bf
+#define RT5668_HP_IMP_SENS_CTRL_18 0x01c0
+#define RT5668_HP_IMP_SENS_CTRL_19 0x01c1
+#define RT5668_HP_IMP_SENS_CTRL_20 0x01c2
+#define RT5668_HP_IMP_SENS_CTRL_21 0x01c3
+#define RT5668_HP_IMP_SENS_CTRL_22 0x01c4
+#define RT5668_HP_IMP_SENS_CTRL_23 0x01c5
+#define RT5668_HP_IMP_SENS_CTRL_24 0x01c6
+#define RT5668_HP_IMP_SENS_CTRL_25 0x01c7
+#define RT5668_HP_IMP_SENS_CTRL_26 0x01c8
+#define RT5668_HP_IMP_SENS_CTRL_27 0x01c9
+#define RT5668_HP_IMP_SENS_CTRL_28 0x01ca
+#define RT5668_HP_IMP_SENS_CTRL_29 0x01cb
+#define RT5668_HP_IMP_SENS_CTRL_30 0x01cc
+#define RT5668_HP_IMP_SENS_CTRL_31 0x01cd
+#define RT5668_HP_IMP_SENS_CTRL_32 0x01ce
+#define RT5668_HP_IMP_SENS_CTRL_33 0x01cf
+#define RT5668_HP_IMP_SENS_CTRL_34 0x01d0
+#define RT5668_HP_IMP_SENS_CTRL_35 0x01d1
+#define RT5668_HP_IMP_SENS_CTRL_36 0x01d2
+#define RT5668_HP_IMP_SENS_CTRL_37 0x01d3
+#define RT5668_HP_IMP_SENS_CTRL_38 0x01d4
+#define RT5668_HP_IMP_SENS_CTRL_39 0x01d5
+#define RT5668_HP_IMP_SENS_CTRL_40 0x01d6
+#define RT5668_HP_IMP_SENS_CTRL_41 0x01d7
+#define RT5668_HP_IMP_SENS_CTRL_42 0x01d8
+#define RT5668_HP_IMP_SENS_CTRL_43 0x01d9
+#define RT5668_HP_LOGIC_CTRL_1 0x01da
+#define RT5668_HP_LOGIC_CTRL_2 0x01db
+#define RT5668_HP_LOGIC_CTRL_3 0x01dc
+#define RT5668_HP_CALIB_CTRL_1 0x01de
+#define RT5668_HP_CALIB_CTRL_2 0x01df
+#define RT5668_HP_CALIB_CTRL_3 0x01e0
+#define RT5668_HP_CALIB_CTRL_4 0x01e1
+#define RT5668_HP_CALIB_CTRL_5 0x01e2
+#define RT5668_HP_CALIB_CTRL_6 0x01e3
+#define RT5668_HP_CALIB_CTRL_7 0x01e4
+#define RT5668_HP_CALIB_CTRL_9 0x01e6
+#define RT5668_HP_CALIB_CTRL_10 0x01e7
+#define RT5668_HP_CALIB_CTRL_11 0x01e8
+#define RT5668_HP_CALIB_STA_1 0x01ea
+#define RT5668_HP_CALIB_STA_2 0x01eb
+#define RT5668_HP_CALIB_STA_3 0x01ec
+#define RT5668_HP_CALIB_STA_4 0x01ed
+#define RT5668_HP_CALIB_STA_5 0x01ee
+#define RT5668_HP_CALIB_STA_6 0x01ef
+#define RT5668_HP_CALIB_STA_7 0x01f0
+#define RT5668_HP_CALIB_STA_8 0x01f1
+#define RT5668_HP_CALIB_STA_9 0x01f2
+#define RT5668_HP_CALIB_STA_10 0x01f3
+#define RT5668_HP_CALIB_STA_11 0x01f4
+#define RT5668_SAR_IL_CMD_1 0x0210
+#define RT5668_SAR_IL_CMD_2 0x0211
+#define RT5668_SAR_IL_CMD_3 0x0212
+#define RT5668_SAR_IL_CMD_4 0x0213
+#define RT5668_SAR_IL_CMD_5 0x0214
+#define RT5668_SAR_IL_CMD_6 0x0215
+#define RT5668_SAR_IL_CMD_7 0x0216
+#define RT5668_SAR_IL_CMD_8 0x0217
+#define RT5668_SAR_IL_CMD_9 0x0218
+#define RT5668_SAR_IL_CMD_10 0x0219
+#define RT5668_SAR_IL_CMD_11 0x021a
+#define RT5668_SAR_IL_CMD_12 0x021b
+#define RT5668_SAR_IL_CMD_13 0x021c
+#define RT5668_EFUSE_CTRL_1 0x0250
+#define RT5668_EFUSE_CTRL_2 0x0251
+#define RT5668_EFUSE_CTRL_3 0x0252
+#define RT5668_EFUSE_CTRL_4 0x0253
+#define RT5668_EFUSE_CTRL_5 0x0254
+#define RT5668_EFUSE_CTRL_6 0x0255
+#define RT5668_EFUSE_CTRL_7 0x0256
+#define RT5668_EFUSE_CTRL_8 0x0257
+#define RT5668_EFUSE_CTRL_9 0x0258
+#define RT5668_EFUSE_CTRL_10 0x0259
+#define RT5668_EFUSE_CTRL_11 0x025a
+#define RT5668_JD_TOP_VC_VTRL 0x0270
+#define RT5668_DRC1_CTRL_0 0x02ff
+#define RT5668_DRC1_CTRL_1 0x0300
+#define RT5668_DRC1_CTRL_2 0x0301
+#define RT5668_DRC1_CTRL_3 0x0302
+#define RT5668_DRC1_CTRL_4 0x0303
+#define RT5668_DRC1_CTRL_5 0x0304
+#define RT5668_DRC1_CTRL_6 0x0305
+#define RT5668_DRC1_HARD_LMT_CTRL_1 0x0306
+#define RT5668_DRC1_HARD_LMT_CTRL_2 0x0307
+#define RT5668_DRC1_PRIV_1 0x0310
+#define RT5668_DRC1_PRIV_2 0x0311
+#define RT5668_DRC1_PRIV_3 0x0312
+#define RT5668_DRC1_PRIV_4 0x0313
+#define RT5668_DRC1_PRIV_5 0x0314
+#define RT5668_DRC1_PRIV_6 0x0315
+#define RT5668_DRC1_PRIV_7 0x0316
+#define RT5668_DRC1_PRIV_8 0x0317
+#define RT5668_EQ_AUTO_RCV_CTRL1 0x03c0
+#define RT5668_EQ_AUTO_RCV_CTRL2 0x03c1
+#define RT5668_EQ_AUTO_RCV_CTRL3 0x03c2
+#define RT5668_EQ_AUTO_RCV_CTRL4 0x03c3
+#define RT5668_EQ_AUTO_RCV_CTRL5 0x03c4
+#define RT5668_EQ_AUTO_RCV_CTRL6 0x03c5
+#define RT5668_EQ_AUTO_RCV_CTRL7 0x03c6
+#define RT5668_EQ_AUTO_RCV_CTRL8 0x03c7
+#define RT5668_EQ_AUTO_RCV_CTRL9 0x03c8
+#define RT5668_EQ_AUTO_RCV_CTRL10 0x03c9
+#define RT5668_EQ_AUTO_RCV_CTRL11 0x03ca
+#define RT5668_EQ_AUTO_RCV_CTRL12 0x03cb
+#define RT5668_EQ_AUTO_RCV_CTRL13 0x03cc
+#define RT5668_ADC_L_EQ_LPF1_A1 0x03d0
+#define RT5668_R_EQ_LPF1_A1 0x03d1
+#define RT5668_L_EQ_LPF1_H0 0x03d2
+#define RT5668_R_EQ_LPF1_H0 0x03d3
+#define RT5668_L_EQ_BPF1_A1 0x03d4
+#define RT5668_R_EQ_BPF1_A1 0x03d5
+#define RT5668_L_EQ_BPF1_A2 0x03d6
+#define RT5668_R_EQ_BPF1_A2 0x03d7
+#define RT5668_L_EQ_BPF1_H0 0x03d8
+#define RT5668_R_EQ_BPF1_H0 0x03d9
+#define RT5668_L_EQ_BPF2_A1 0x03da
+#define RT5668_R_EQ_BPF2_A1 0x03db
+#define RT5668_L_EQ_BPF2_A2 0x03dc
+#define RT5668_R_EQ_BPF2_A2 0x03dd
+#define RT5668_L_EQ_BPF2_H0 0x03de
+#define RT5668_R_EQ_BPF2_H0 0x03df
+#define RT5668_L_EQ_BPF3_A1 0x03e0
+#define RT5668_R_EQ_BPF3_A1 0x03e1
+#define RT5668_L_EQ_BPF3_A2 0x03e2
+#define RT5668_R_EQ_BPF3_A2 0x03e3
+#define RT5668_L_EQ_BPF3_H0 0x03e4
+#define RT5668_R_EQ_BPF3_H0 0x03e5
+#define RT5668_L_EQ_BPF4_A1 0x03e6
+#define RT5668_R_EQ_BPF4_A1 0x03e7
+#define RT5668_L_EQ_BPF4_A2 0x03e8
+#define RT5668_R_EQ_BPF4_A2 0x03e9
+#define RT5668_L_EQ_BPF4_H0 0x03ea
+#define RT5668_R_EQ_BPF4_H0 0x03eb
+#define RT5668_L_EQ_HPF1_A1 0x03ec
+#define RT5668_R_EQ_HPF1_A1 0x03ed
+#define RT5668_L_EQ_HPF1_H0 0x03ee
+#define RT5668_R_EQ_HPF1_H0 0x03ef
+#define RT5668_L_EQ_PRE_VOL 0x03f0
+#define RT5668_R_EQ_PRE_VOL 0x03f1
+#define RT5668_L_EQ_POST_VOL 0x03f2
+#define RT5668_R_EQ_POST_VOL 0x03f3
+#define RT5668_I2C_MODE 0xffff
+
+
+/* global definition */
+#define RT5668_L_MUTE (0x1 << 15)
+#define RT5668_L_MUTE_SFT 15
+#define RT5668_VOL_L_MUTE (0x1 << 14)
+#define RT5668_VOL_L_SFT 14
+#define RT5668_R_MUTE (0x1 << 7)
+#define RT5668_R_MUTE_SFT 7
+#define RT5668_VOL_R_MUTE (0x1 << 6)
+#define RT5668_VOL_R_SFT 6
+#define RT5668_L_VOL_MASK (0x3f << 8)
+#define RT5668_L_VOL_SFT 8
+#define RT5668_R_VOL_MASK (0x3f)
+#define RT5668_R_VOL_SFT 0
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5668_G_HP (0xf << 8)
+#define RT5668_G_HP_SFT 8
+#define RT5668_G_STO_DA_DMIX (0xf)
+#define RT5668_G_STO_DA_SFT 0
+
+/* CBJ Control (0x000b) */
+#define RT5668_BST_CBJ_MASK (0xf << 8)
+#define RT5668_BST_CBJ_SFT 8
+
+/* Embeeded Jack and Type Detection Control 1 (0x0010) */
+#define RT5668_EMB_JD_EN (0x1 << 15)
+#define RT5668_EMB_JD_EN_SFT 15
+#define RT5668_EMB_JD_RST (0x1 << 14)
+#define RT5668_JD_MODE (0x1 << 13)
+#define RT5668_JD_MODE_SFT 13
+#define RT5668_DET_TYPE (0x1 << 12)
+#define RT5668_DET_TYPE_SFT 12
+#define RT5668_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5668_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5668_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5668_EXT_JD_DIG (0x1 << 9)
+#define RT5668_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5668_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5668_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5668_FAST_OFF_MASK (0x1 << 7)
+#define RT5668_FAST_OFF_EN (0x1 << 7)
+#define RT5668_FAST_OFF_DIS (0x0 << 7)
+#define RT5668_VREF_POW_MASK (0x1 << 6)
+#define RT5668_VREF_POW_FSM (0x0 << 6)
+#define RT5668_VREF_POW_REG (0x1 << 6)
+#define RT5668_MB1_PATH_MASK (0x1 << 5)
+#define RT5668_CTRL_MB1_REG (0x1 << 5)
+#define RT5668_CTRL_MB1_FSM (0x0 << 5)
+#define RT5668_MB2_PATH_MASK (0x1 << 4)
+#define RT5668_CTRL_MB2_REG (0x1 << 4)
+#define RT5668_CTRL_MB2_FSM (0x0 << 4)
+#define RT5668_TRIG_JD_MASK (0x1 << 3)
+#define RT5668_TRIG_JD_HIGH (0x1 << 3)
+#define RT5668_TRIG_JD_LOW (0x0 << 3)
+#define RT5668_MIC_CAP_MASK (0x1 << 1)
+#define RT5668_MIC_CAP_HS (0x1 << 1)
+#define RT5668_MIC_CAP_HP (0x0 << 1)
+#define RT5668_MIC_CAP_SRC_MASK (0x1)
+#define RT5668_MIC_CAP_SRC_REG (0x1)
+#define RT5668_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 2 (0x0011) */
+#define RT5668_EXT_JD_SRC (0x7 << 4)
+#define RT5668_EXT_JD_SRC_SFT 4
+#define RT5668_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5668_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5668_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5668_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5668_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5668_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 3 (0x0012) */
+#define RT5668_CBJ_IN_BUF_EN (0x1 << 7)
+
+/* Combo Jack and Type Detection Control 4 (0x0013) */
+#define RT5668_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5668_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5668_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5668_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5668_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5668_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5668_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5668_DAC_L1_VOL_SFT 8
+#define RT5668_DAC_R1_VOL_MASK (0xff)
+#define RT5668_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5668_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5668_ADC_L_VOL_SFT 8
+#define RT5668_ADC_R_VOL_MASK (0x7f)
+#define RT5668_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5668_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5668_STO1_ADC_L_BST_SFT 14
+#define RT5668_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5668_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5668_ST_SRC_SEL (0x1 << 8)
+#define RT5668_ST_SRC_SFT 8
+#define RT5668_ST_EN_MASK (0x1 << 6)
+#define RT5668_ST_DIS (0x0 << 6)
+#define RT5668_ST_EN (0x1 << 6)
+#define RT5668_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5668_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5668_M_STO1_ADC_L1_SFT 15
+#define RT5668_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5668_M_STO1_ADC_L2_SFT 14
+#define RT5668_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5668_STO1_ADC1L_SRC_SFT 13
+#define RT5668_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5668_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5668_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5668_STO1_ADC2L_SRC_SFT 12
+#define RT5668_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5668_STO1_ADCL_SRC_SFT 10
+#define RT5668_STO1_DD_L_SRC_MASK (0x1 << 9)
+#define RT5668_STO1_DD_L_SRC_SFT 9
+#define RT5668_STO1_DMIC_SRC_MASK (0x1 << 8)
+#define RT5668_STO1_DMIC_SRC_SFT 8
+#define RT5668_STO1_DMIC_SRC_DMIC2 (0x1 << 8)
+#define RT5668_STO1_DMIC_SRC_DMIC1 (0x0 << 8)
+#define RT5668_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5668_M_STO1_ADC_R1_SFT 7
+#define RT5668_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5668_M_STO1_ADC_R2_SFT 6
+#define RT5668_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5668_STO1_ADC1R_SRC_SFT 5
+#define RT5668_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5668_STO1_ADC2R_SRC_SFT 4
+#define RT5668_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5668_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5668_M_ADCMIX_L (0x1 << 15)
+#define RT5668_M_ADCMIX_L_SFT 15
+#define RT5668_M_DAC1_L (0x1 << 14)
+#define RT5668_M_DAC1_L_SFT 14
+#define RT5668_DAC1_R_SEL_MASK (0x1 << 10)
+#define RT5668_DAC1_R_SEL_SFT 10
+#define RT5668_DAC1_L_SEL_MASK (0x1 << 8)
+#define RT5668_DAC1_L_SEL_SFT 8
+#define RT5668_M_ADCMIX_R (0x1 << 7)
+#define RT5668_M_ADCMIX_R_SFT 7
+#define RT5668_M_DAC1_R (0x1 << 6)
+#define RT5668_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5668_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5668_M_DAC_L1_STO_L_SFT 15
+#define RT5668_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5668_G_DAC_L1_STO_L_SFT 14
+#define RT5668_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5668_M_DAC_R1_STO_L_SFT 13
+#define RT5668_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5668_G_DAC_R1_STO_L_SFT 12
+#define RT5668_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5668_M_DAC_L1_STO_R_SFT 7
+#define RT5668_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5668_G_DAC_L1_STO_R_SFT 6
+#define RT5668_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5668_M_DAC_R1_STO_R_SFT 5
+#define RT5668_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5668_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5668_M_ST_STO_L (0x1 << 9)
+#define RT5668_M_ST_STO_L_SFT 9
+#define RT5668_M_ST_STO_R (0x1 << 8)
+#define RT5668_M_ST_STO_R_SFT 8
+#define RT5668_DAC_L1_SRC_MASK (0x3 << 4)
+#define RT5668_A_DACL1_SFT 4
+#define RT5668_DAC_R1_SRC_MASK (0x3)
+#define RT5668_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5668_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5668_IF2_ADC_SEL_SFT 0
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5668_G_CBJ_RM1_L (0x7 << 10)
+#define RT5668_G_CBJ_RM1_L_SFT 10
+#define RT5668_M_CBJ_RM1_L (0x1 << 7)
+#define RT5668_M_CBJ_RM1_L_SFT 7
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5668_PWR_I2S1 (0x1 << 15)
+#define RT5668_PWR_I2S1_BIT 15
+#define RT5668_PWR_I2S2 (0x1 << 14)
+#define RT5668_PWR_I2S2_BIT 14
+#define RT5668_PWR_DAC_L1 (0x1 << 11)
+#define RT5668_PWR_DAC_L1_BIT 11
+#define RT5668_PWR_DAC_R1 (0x1 << 10)
+#define RT5668_PWR_DAC_R1_BIT 10
+#define RT5668_PWR_LDO (0x1 << 8)
+#define RT5668_PWR_LDO_BIT 8
+#define RT5668_PWR_ADC_L1 (0x1 << 4)
+#define RT5668_PWR_ADC_L1_BIT 4
+#define RT5668_PWR_ADC_R1 (0x1 << 3)
+#define RT5668_PWR_ADC_R1_BIT 3
+#define RT5668_DIG_GATE_CTRL (0x1 << 0)
+#define RT5668_DIG_GATE_CTRL_SFT 0
+
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5668_PWR_ADC_S1F (0x1 << 15)
+#define RT5668_PWR_ADC_S1F_BIT 15
+#define RT5668_PWR_DAC_S1F (0x1 << 10)
+#define RT5668_PWR_DAC_S1F_BIT 10
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5668_PWR_VREF1 (0x1 << 15)
+#define RT5668_PWR_VREF1_BIT 15
+#define RT5668_PWR_FV1 (0x1 << 14)
+#define RT5668_PWR_FV1_BIT 14
+#define RT5668_PWR_VREF2 (0x1 << 13)
+#define RT5668_PWR_VREF2_BIT 13
+#define RT5668_PWR_FV2 (0x1 << 12)
+#define RT5668_PWR_FV2_BIT 12
+#define RT5668_LDO1_DBG_MASK (0x3 << 10)
+#define RT5668_PWR_MB (0x1 << 9)
+#define RT5668_PWR_MB_BIT 9
+#define RT5668_PWR_BG (0x1 << 7)
+#define RT5668_PWR_BG_BIT 7
+#define RT5668_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5668_LDO1_BYPASS (0x1 << 6)
+#define RT5668_LDO1_NOT_BYPASS (0x0 << 6)
+#define RT5668_PWR_MA_BIT 6
+#define RT5668_LDO1_DVO_MASK (0x3 << 4)
+#define RT5668_LDO1_DVO_09 (0x0 << 4)
+#define RT5668_LDO1_DVO_10 (0x1 << 4)
+#define RT5668_LDO1_DVO_12 (0x2 << 4)
+#define RT5668_LDO1_DVO_14 (0x3 << 4)
+#define RT5668_HP_DRIVER_MASK (0x3 << 2)
+#define RT5668_HP_DRIVER_1X (0x0 << 2)
+#define RT5668_HP_DRIVER_3X (0x1 << 2)
+#define RT5668_HP_DRIVER_5X (0x3 << 2)
+#define RT5668_PWR_HA_L (0x1 << 1)
+#define RT5668_PWR_HA_L_BIT 1
+#define RT5668_PWR_HA_R (0x1 << 0)
+#define RT5668_PWR_HA_R_BIT 0
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5668_PWR_MB1 (0x1 << 11)
+#define RT5668_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5668_PWR_MB1_BIT 11
+#define RT5668_PWR_MB2 (0x1 << 10)
+#define RT5668_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5668_PWR_MB2_BIT 10
+#define RT5668_PWR_JDH (0x1 << 3)
+#define RT5668_PWR_JDH_BIT 3
+#define RT5668_PWR_JDL (0x1 << 2)
+#define RT5668_PWR_JDL_BIT 2
+#define RT5668_PWR_RM1_L (0x1 << 1)
+#define RT5668_PWR_RM1_L_BIT 1
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5668_PWR_CBJ (0x1 << 9)
+#define RT5668_PWR_CBJ_BIT 9
+#define RT5668_PWR_PLL (0x1 << 6)
+#define RT5668_PWR_PLL_BIT 6
+#define RT5668_PWR_PLL2B (0x1 << 5)
+#define RT5668_PWR_PLL2B_BIT 5
+#define RT5668_PWR_PLL2F (0x1 << 4)
+#define RT5668_PWR_PLL2F_BIT 4
+#define RT5668_PWR_LDO2 (0x1 << 2)
+#define RT5668_PWR_LDO2_BIT 2
+#define RT5668_PWR_DET_SPKVDD (0x1 << 1)
+#define RT5668_PWR_DET_SPKVDD_BIT 1
+
+/* Power Management for Mixer (0x0066) */
+#define RT5668_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5668_PWR_STO1_DAC_L_BIT 5
+#define RT5668_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5668_PWR_STO1_DAC_R_BIT 4
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5668_SYS_CLK_DET (0x1 << 15)
+#define RT5668_SYS_CLK_DET_SFT 15
+#define RT5668_PLL1_CLK_DET (0x1 << 14)
+#define RT5668_PLL1_CLK_DET_SFT 14
+#define RT5668_PLL2_CLK_DET (0x1 << 13)
+#define RT5668_PLL2_CLK_DET_SFT 13
+#define RT5668_POW_CLK_DET2_SFT 8
+#define RT5668_POW_CLK_DET_SFT 0
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5668_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5668_DMIC_1_EN_SFT 15
+#define RT5668_DMIC_1_DIS (0x0 << 15)
+#define RT5668_DMIC_1_EN (0x1 << 15)
+#define RT5668_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5668_DMIC_1_DP_SFT 4
+#define RT5668_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5668_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5668_DMIC_CLK_MASK (0xf << 0)
+#define RT5668_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5668_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5668_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5668_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5668_SEL_ADCDAT_SFT 15
+#define RT5668_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5668_I2S1_TX_CHL_SFT 12
+#define RT5668_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5668_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5668_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5668_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5668_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5668_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5668_I2S1_RX_CHL_SFT 8
+#define RT5668_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5668_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5668_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5668_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5668_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5668_I2S1_MONO_MASK (0x1 << 7)
+#define RT5668_I2S1_MONO_EN (0x1 << 7)
+#define RT5668_I2S1_MONO_DIS (0x0 << 7)
+#define RT5668_I2S2_MONO_MASK (0x1 << 6)
+#define RT5668_I2S2_MONO_EN (0x1 << 6)
+#define RT5668_I2S2_MONO_DIS (0x0 << 6)
+#define RT5668_I2S1_DL_MASK (0x7 << 4)
+#define RT5668_I2S1_DL_SFT 4
+#define RT5668_I2S1_DL_16 (0x0 << 4)
+#define RT5668_I2S1_DL_20 (0x1 << 4)
+#define RT5668_I2S1_DL_24 (0x2 << 4)
+#define RT5668_I2S1_DL_32 (0x3 << 4)
+#define RT5668_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */
+#define RT5668_I2S2_MS_MASK (0x1 << 15)
+#define RT5668_I2S2_MS_SFT 15
+#define RT5668_I2S2_MS_M (0x0 << 15)
+#define RT5668_I2S2_MS_S (0x1 << 15)
+#define RT5668_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5668_I2S2_PIN_CFG_SFT 14
+#define RT5668_I2S2_CLK_SEL_MASK (0x1 << 11)
+#define RT5668_I2S2_CLK_SEL_SFT 11
+#define RT5668_I2S2_OUT_MASK (0x1 << 9)
+#define RT5668_I2S2_OUT_SFT 9
+#define RT5668_I2S2_OUT_UM (0x0 << 9)
+#define RT5668_I2S2_OUT_M (0x1 << 9)
+#define RT5668_I2S_BP_MASK (0x1 << 8)
+#define RT5668_I2S_BP_SFT 8
+#define RT5668_I2S_BP_NOR (0x0 << 8)
+#define RT5668_I2S_BP_INV (0x1 << 8)
+#define RT5668_I2S2_MONO_EN (0x1 << 6)
+#define RT5668_I2S2_MONO_DIS (0x0 << 6)
+#define RT5668_I2S2_DL_MASK (0x3 << 4)
+#define RT5668_I2S2_DL_SFT 4
+#define RT5668_I2S2_DL_16 (0x0 << 4)
+#define RT5668_I2S2_DL_20 (0x1 << 4)
+#define RT5668_I2S2_DL_24 (0x2 << 4)
+#define RT5668_I2S2_DL_8 (0x3 << 4)
+#define RT5668_I2S_DF_MASK (0x7)
+#define RT5668_I2S_DF_SFT 0
+#define RT5668_I2S_DF_I2S (0x0)
+#define RT5668_I2S_DF_LEFT (0x1)
+#define RT5668_I2S_DF_PCM_A (0x2)
+#define RT5668_I2S_DF_PCM_B (0x3)
+#define RT5668_I2S_DF_PCM_A_N (0x6)
+#define RT5668_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5668_ADC_OSR_MASK (0xf << 12)
+#define RT5668_ADC_OSR_SFT 12
+#define RT5668_ADC_OSR_D_1 (0x0 << 12)
+#define RT5668_ADC_OSR_D_2 (0x1 << 12)
+#define RT5668_ADC_OSR_D_4 (0x2 << 12)
+#define RT5668_ADC_OSR_D_6 (0x3 << 12)
+#define RT5668_ADC_OSR_D_8 (0x4 << 12)
+#define RT5668_ADC_OSR_D_12 (0x5 << 12)
+#define RT5668_ADC_OSR_D_16 (0x6 << 12)
+#define RT5668_ADC_OSR_D_24 (0x7 << 12)
+#define RT5668_ADC_OSR_D_32 (0x8 << 12)
+#define RT5668_ADC_OSR_D_48 (0x9 << 12)
+#define RT5668_I2S_M_DIV_MASK (0xf << 12)
+#define RT5668_I2S_M_DIV_SFT 8
+#define RT5668_I2S_M_D_1 (0x0 << 8)
+#define RT5668_I2S_M_D_2 (0x1 << 8)
+#define RT5668_I2S_M_D_3 (0x2 << 8)
+#define RT5668_I2S_M_D_4 (0x3 << 8)
+#define RT5668_I2S_M_D_6 (0x4 << 8)
+#define RT5668_I2S_M_D_8 (0x5 << 8)
+#define RT5668_I2S_M_D_12 (0x6 << 8)
+#define RT5668_I2S_M_D_16 (0x7 << 8)
+#define RT5668_I2S_M_D_24 (0x8 << 8)
+#define RT5668_I2S_M_D_32 (0x9 << 8)
+#define RT5668_I2S_M_D_48 (0x10 << 8)
+#define RT5668_I2S_CLK_SRC_MASK (0x7 << 4)
+#define RT5668_I2S_CLK_SRC_SFT 4
+#define RT5668_I2S_CLK_SRC_MCLK (0x0 << 4)
+#define RT5668_I2S_CLK_SRC_PLL1 (0x1 << 4)
+#define RT5668_I2S_CLK_SRC_PLL2 (0x2 << 4)
+#define RT5668_I2S_CLK_SRC_SDW (0x3 << 4)
+#define RT5668_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */
+#define RT5668_DAC_OSR_MASK (0xf << 0)
+#define RT5668_DAC_OSR_SFT 0
+#define RT5668_DAC_OSR_D_1 (0x0 << 0)
+#define RT5668_DAC_OSR_D_2 (0x1 << 0)
+#define RT5668_DAC_OSR_D_4 (0x2 << 0)
+#define RT5668_DAC_OSR_D_6 (0x3 << 0)
+#define RT5668_DAC_OSR_D_8 (0x4 << 0)
+#define RT5668_DAC_OSR_D_12 (0x5 << 0)
+#define RT5668_DAC_OSR_D_16 (0x6 << 0)
+#define RT5668_DAC_OSR_D_24 (0x7 << 0)
+#define RT5668_DAC_OSR_D_32 (0x8 << 0)
+#define RT5668_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5668_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5668_I2S2_BCLK_MS2_SFT 11
+#define RT5668_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5668_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5668_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5668_TDM_TX_CH_2 (0x0 << 12)
+#define RT5668_TDM_TX_CH_4 (0x1 << 12)
+#define RT5668_TDM_TX_CH_6 (0x2 << 12)
+#define RT5668_TDM_TX_CH_8 (0x3 << 12)
+#define RT5668_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5668_TDM_RX_CH_2 (0x0 << 8)
+#define RT5668_TDM_RX_CH_4 (0x1 << 8)
+#define RT5668_TDM_RX_CH_6 (0x2 << 8)
+#define RT5668_TDM_RX_CH_8 (0x3 << 8)
+#define RT5668_TDM_ADC_LCA_MASK (0xf << 4)
+#define RT5668_TDM_ADC_LCA_SFT 4
+#define RT5668_TDM_ADC_DL_SFT 0
+
+/* TDM control 3 (0x007a) */
+#define RT5668_IF1_ADC1_SEL_SFT 14
+#define RT5668_IF1_ADC2_SEL_SFT 12
+#define RT5668_IF1_ADC3_SEL_SFT 10
+#define RT5668_IF1_ADC4_SEL_SFT 8
+#define RT5668_TDM_ADC_SEL_SFT 4
+
+/* TDM/I2S control (0x007e) */
+#define RT5668_TDM_S_BP_MASK (0x1 << 15)
+#define RT5668_TDM_S_BP_SFT 15
+#define RT5668_TDM_S_BP_NOR (0x0 << 15)
+#define RT5668_TDM_S_BP_INV (0x1 << 15)
+#define RT5668_TDM_S_LP_MASK (0x1 << 14)
+#define RT5668_TDM_S_LP_SFT 14
+#define RT5668_TDM_S_LP_NOR (0x0 << 14)
+#define RT5668_TDM_S_LP_INV (0x1 << 14)
+#define RT5668_TDM_DF_MASK (0x7 << 11)
+#define RT5668_TDM_DF_SFT 11
+#define RT5668_TDM_DF_I2S (0x0 << 11)
+#define RT5668_TDM_DF_LEFT (0x1 << 11)
+#define RT5668_TDM_DF_PCM_A (0x2 << 11)
+#define RT5668_TDM_DF_PCM_B (0x3 << 11)
+#define RT5668_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5668_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5668_TDM_CL_MASK (0x3 << 4)
+#define RT5668_TDM_CL_16 (0x0 << 4)
+#define RT5668_TDM_CL_20 (0x1 << 4)
+#define RT5668_TDM_CL_24 (0x2 << 4)
+#define RT5668_TDM_CL_32 (0x3 << 4)
+#define RT5668_TDM_M_BP_MASK (0x1 << 2)
+#define RT5668_TDM_M_BP_SFT 2
+#define RT5668_TDM_M_BP_NOR (0x0 << 2)
+#define RT5668_TDM_M_BP_INV (0x1 << 2)
+#define RT5668_TDM_M_LP_MASK (0x1 << 1)
+#define RT5668_TDM_M_LP_SFT 1
+#define RT5668_TDM_M_LP_NOR (0x0 << 1)
+#define RT5668_TDM_M_LP_INV (0x1 << 1)
+#define RT5668_TDM_MS_MASK (0x1 << 0)
+#define RT5668_TDM_MS_SFT 0
+#define RT5668_TDM_MS_M (0x0 << 0)
+#define RT5668_TDM_MS_S (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5668_SCLK_SRC_MASK (0x7 << 13)
+#define RT5668_SCLK_SRC_SFT 13
+#define RT5668_SCLK_SRC_MCLK (0x0 << 13)
+#define RT5668_SCLK_SRC_PLL1 (0x1 << 13)
+#define RT5668_SCLK_SRC_PLL2 (0x2 << 13)
+#define RT5668_SCLK_SRC_SDW (0x3 << 13)
+#define RT5668_SCLK_SRC_RCCLK (0x4 << 13)
+#define RT5668_PLL1_SRC_MASK (0x3 << 10)
+#define RT5668_PLL1_SRC_SFT 10
+#define RT5668_PLL1_SRC_MCLK (0x0 << 10)
+#define RT5668_PLL1_SRC_BCLK1 (0x1 << 10)
+#define RT5668_PLL1_SRC_SDW (0x2 << 10)
+#define RT5668_PLL1_SRC_RC (0x3 << 10)
+#define RT5668_PLL2_SRC_MASK (0x3 << 8)
+#define RT5668_PLL2_SRC_SFT 8
+#define RT5668_PLL2_SRC_MCLK (0x0 << 8)
+#define RT5668_PLL2_SRC_BCLK1 (0x1 << 8)
+#define RT5668_PLL2_SRC_SDW (0x2 << 8)
+#define RT5668_PLL2_SRC_RC (0x3 << 8)
+
+
+
+#define RT5668_PLL_INP_MAX 40000000
+#define RT5668_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x0081) */
+#define RT5668_PLL_N_MAX 0x001ff
+#define RT5668_PLL_N_MASK (RT5668_PLL_N_MAX << 7)
+#define RT5668_PLL_N_SFT 7
+#define RT5668_PLL_K_MAX 0x001f
+#define RT5668_PLL_K_MASK (RT5668_PLL_K_MAX)
+#define RT5668_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x0082) */
+#define RT5668_PLL_M_MAX 0x00f
+#define RT5668_PLL_M_MASK (RT5668_PLL_M_MAX << 12)
+#define RT5668_PLL_M_SFT 12
+#define RT5668_PLL_M_BP (0x1 << 11)
+#define RT5668_PLL_M_BP_SFT 11
+#define RT5668_PLL_K_BP (0x1 << 10)
+#define RT5668_PLL_K_BP_SFT 10
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5668_DA_ASRC_MASK (0x1 << 13)
+#define RT5668_DA_ASRC_SFT 13
+#define RT5668_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5668_DAC_STO1_ASRC_SFT 12
+#define RT5668_AD_ASRC_MASK (0x1 << 8)
+#define RT5668_AD_ASRC_SFT 8
+#define RT5668_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5668_AD_ASRC_SEL_SFT 4
+#define RT5668_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5668_DMIC_ASRC_SFT 3
+#define RT5668_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5668_ADC_STO1_ASRC_SFT 2
+#define RT5668_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5668_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5668_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5668_FILTER_CLK_SEL_SFT 12
+
+/* ASRC Control 4 (0x0086) */
+#define RT5668_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5668_ASRCIN_FTK_N1_SFT 14
+#define RT5668_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5668_ASRCIN_FTK_N2_SFT 12
+#define RT5668_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5668_ASRCIN_FTK_M1_SFT 8
+#define RT5668_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5668_ASRCIN_FTK_M2_SFT 4
+
+/* SoundWire reference clk (0x008d) */
+#define RT5668_PLL2_OUT_MASK (0x1 << 8)
+#define RT5668_PLL2_OUT_98M (0x0 << 8)
+#define RT5668_PLL2_OUT_49M (0x1 << 8)
+#define RT5668_SDW_REF_2_MASK (0xf << 4)
+#define RT5668_SDW_REF_2_SFT 4
+#define RT5668_SDW_REF_2_48K (0x0 << 4)
+#define RT5668_SDW_REF_2_96K (0x1 << 4)
+#define RT5668_SDW_REF_2_192K (0x2 << 4)
+#define RT5668_SDW_REF_2_32K (0x3 << 4)
+#define RT5668_SDW_REF_2_24K (0x4 << 4)
+#define RT5668_SDW_REF_2_16K (0x5 << 4)
+#define RT5668_SDW_REF_2_12K (0x6 << 4)
+#define RT5668_SDW_REF_2_8K (0x7 << 4)
+#define RT5668_SDW_REF_2_44K (0x8 << 4)
+#define RT5668_SDW_REF_2_88K (0x9 << 4)
+#define RT5668_SDW_REF_2_176K (0xa << 4)
+#define RT5668_SDW_REF_2_353K (0xb << 4)
+#define RT5668_SDW_REF_2_22K (0xc << 4)
+#define RT5668_SDW_REF_2_384K (0xd << 4)
+#define RT5668_SDW_REF_2_11K (0xe << 4)
+#define RT5668_SDW_REF_1_MASK (0xf << 0)
+#define RT5668_SDW_REF_1_SFT 0
+#define RT5668_SDW_REF_1_48K (0x0 << 0)
+#define RT5668_SDW_REF_1_96K (0x1 << 0)
+#define RT5668_SDW_REF_1_192K (0x2 << 0)
+#define RT5668_SDW_REF_1_32K (0x3 << 0)
+#define RT5668_SDW_REF_1_24K (0x4 << 0)
+#define RT5668_SDW_REF_1_16K (0x5 << 0)
+#define RT5668_SDW_REF_1_12K (0x6 << 0)
+#define RT5668_SDW_REF_1_8K (0x7 << 0)
+#define RT5668_SDW_REF_1_44K (0x8 << 0)
+#define RT5668_SDW_REF_1_88K (0x9 << 0)
+#define RT5668_SDW_REF_1_176K (0xa << 0)
+#define RT5668_SDW_REF_1_353K (0xb << 0)
+#define RT5668_SDW_REF_1_22K (0xc << 0)
+#define RT5668_SDW_REF_1_384K (0xd << 0)
+#define RT5668_SDW_REF_1_11K (0xe << 0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5668_PUMP_EN (0x1 << 3)
+#define RT5668_PUMP_EN_SFT 3
+#define RT5668_CAPLESS_EN (0x1 << 0)
+#define RT5668_CAPLESS_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5668_RAMP_MASK (0x1 << 12)
+#define RT5668_RAMP_SFT 12
+#define RT5668_RAMP_DIS (0x0 << 12)
+#define RT5668_RAMP_EN (0x1 << 12)
+#define RT5668_BPS_MASK (0x1 << 11)
+#define RT5668_BPS_SFT 11
+#define RT5668_BPS_DIS (0x0 << 11)
+#define RT5668_BPS_EN (0x1 << 11)
+#define RT5668_FAST_UPDN_MASK (0x1 << 10)
+#define RT5668_FAST_UPDN_SFT 10
+#define RT5668_FAST_UPDN_DIS (0x0 << 10)
+#define RT5668_FAST_UPDN_EN (0x1 << 10)
+#define RT5668_VLO_MASK (0x1 << 7)
+#define RT5668_VLO_SFT 7
+#define RT5668_VLO_3V (0x0 << 7)
+#define RT5668_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5668_OSW_L_MASK (0x1 << 11)
+#define RT5668_OSW_L_SFT 11
+#define RT5668_OSW_L_DIS (0x0 << 11)
+#define RT5668_OSW_L_EN (0x1 << 11)
+#define RT5668_OSW_R_MASK (0x1 << 10)
+#define RT5668_OSW_R_SFT 10
+#define RT5668_OSW_R_DIS (0x0 << 10)
+#define RT5668_OSW_R_EN (0x1 << 10)
+#define RT5668_PM_HP_MASK (0x3 << 8)
+#define RT5668_PM_HP_SFT 8
+#define RT5668_PM_HP_LV (0x0 << 8)
+#define RT5668_PM_HP_MV (0x1 << 8)
+#define RT5668_PM_HP_HV (0x2 << 8)
+#define RT5668_IB_HP_MASK (0x3 << 6)
+#define RT5668_IB_HP_SFT 6
+#define RT5668_IB_HP_125IL (0x0 << 6)
+#define RT5668_IB_HP_25IL (0x1 << 6)
+#define RT5668_IB_HP_5IL (0x2 << 6)
+#define RT5668_IB_HP_1IL (0x3 << 6)
+
+/* Micbias Control1 (0x93) */
+#define RT5668_MIC1_OV_MASK (0x3 << 14)
+#define RT5668_MIC1_OV_SFT 14
+#define RT5668_MIC1_OV_2V7 (0x0 << 14)
+#define RT5668_MIC1_OV_2V4 (0x1 << 14)
+#define RT5668_MIC1_OV_2V25 (0x3 << 14)
+#define RT5668_MIC1_OV_1V8 (0x4 << 14)
+#define RT5668_MIC1_CLK_MASK (0x1 << 13)
+#define RT5668_MIC1_CLK_SFT 13
+#define RT5668_MIC1_CLK_DIS (0x0 << 13)
+#define RT5668_MIC1_CLK_EN (0x1 << 13)
+#define RT5668_MIC1_OVCD_MASK (0x1 << 12)
+#define RT5668_MIC1_OVCD_SFT 12
+#define RT5668_MIC1_OVCD_DIS (0x0 << 12)
+#define RT5668_MIC1_OVCD_EN (0x1 << 12)
+#define RT5668_MIC1_OVTH_MASK (0x3 << 10)
+#define RT5668_MIC1_OVTH_SFT 10
+#define RT5668_MIC1_OVTH_768UA (0x0 << 10)
+#define RT5668_MIC1_OVTH_960UA (0x1 << 10)
+#define RT5668_MIC1_OVTH_1152UA (0x2 << 10)
+#define RT5668_MIC1_OVTH_1960UA (0x3 << 10)
+#define RT5668_MIC2_OV_MASK (0x3 << 8)
+#define RT5668_MIC2_OV_SFT 8
+#define RT5668_MIC2_OV_2V7 (0x0 << 8)
+#define RT5668_MIC2_OV_2V4 (0x1 << 8)
+#define RT5668_MIC2_OV_2V25 (0x3 << 8)
+#define RT5668_MIC2_OV_1V8 (0x4 << 8)
+#define RT5668_MIC2_CLK_MASK (0x1 << 7)
+#define RT5668_MIC2_CLK_SFT 7
+#define RT5668_MIC2_CLK_DIS (0x0 << 7)
+#define RT5668_MIC2_CLK_EN (0x1 << 7)
+#define RT5668_MIC2_OVTH_MASK (0x3 << 4)
+#define RT5668_MIC2_OVTH_SFT 4
+#define RT5668_MIC2_OVTH_768UA (0x0 << 4)
+#define RT5668_MIC2_OVTH_960UA (0x1 << 4)
+#define RT5668_MIC2_OVTH_1152UA (0x2 << 4)
+#define RT5668_MIC2_OVTH_1960UA (0x3 << 4)
+#define RT5668_PWR_MB_MASK (0x1 << 3)
+#define RT5668_PWR_MB_SFT 3
+#define RT5668_PWR_MB_PD (0x0 << 3)
+#define RT5668_PWR_MB_PU (0x1 << 3)
+
+/* Micbias Control2 (0x0094) */
+#define RT5668_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5668_PWR_CLK25M_SFT 9
+#define RT5668_PWR_CLK25M_PD (0x0 << 9)
+#define RT5668_PWR_CLK25M_PU (0x1 << 9)
+#define RT5668_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5668_PWR_CLK1M_SFT 8
+#define RT5668_PWR_CLK1M_PD (0x0 << 8)
+#define RT5668_PWR_CLK1M_PU (0x1 << 8)
+
+/* RC Clock Control (0x009f) */
+#define RT5668_POW_IRQ (0x1 << 15)
+#define RT5668_POW_JDH (0x1 << 14)
+#define RT5668_POW_JDL (0x1 << 13)
+#define RT5668_POW_ANA (0x1 << 12)
+
+/* I2S Master Mode Clock Control 1 (0x00a0) */
+#define RT5668_CLK_SRC_MCLK (0x0)
+#define RT5668_CLK_SRC_PLL1 (0x1)
+#define RT5668_CLK_SRC_PLL2 (0x2)
+#define RT5668_CLK_SRC_SDW (0x3)
+#define RT5668_CLK_SRC_RCCLK (0x4)
+#define RT5668_I2S_PD_1 (0x0)
+#define RT5668_I2S_PD_2 (0x1)
+#define RT5668_I2S_PD_3 (0x2)
+#define RT5668_I2S_PD_4 (0x3)
+#define RT5668_I2S_PD_6 (0x4)
+#define RT5668_I2S_PD_8 (0x5)
+#define RT5668_I2S_PD_12 (0x6)
+#define RT5668_I2S_PD_16 (0x7)
+#define RT5668_I2S_PD_24 (0x8)
+#define RT5668_I2S_PD_32 (0x9)
+#define RT5668_I2S_PD_48 (0xa)
+#define RT5668_I2S2_SRC_MASK (0x3 << 4)
+#define RT5668_I2S2_SRC_SFT 4
+#define RT5668_I2S2_M_PD_MASK (0xf << 0)
+#define RT5668_I2S2_M_PD_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5668_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5668_JD1_PULSE_EN_SFT 10
+#define RT5668_JD1_PULSE_DIS (0x0 << 10)
+#define RT5668_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5668_JD1_EN_MASK (0x1 << 15)
+#define RT5668_JD1_EN_SFT 15
+#define RT5668_JD1_DIS (0x0 << 15)
+#define RT5668_JD1_EN (0x1 << 15)
+#define RT5668_JD1_POL_MASK (0x1 << 13)
+#define RT5668_JD1_POL_NOR (0x0 << 13)
+#define RT5668_JD1_POL_INV (0x1 << 13)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5668_IL_IRQ_MASK (0x1 << 7)
+#define RT5668_IL_IRQ_DIS (0x0 << 7)
+#define RT5668_IL_IRQ_EN (0x1 << 7)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5668_GP1_PIN_MASK (0x3 << 14)
+#define RT5668_GP1_PIN_SFT 14
+#define RT5668_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5668_GP1_PIN_IRQ (0x1 << 14)
+#define RT5668_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5668_GP2_PIN_MASK (0x3 << 12)
+#define RT5668_GP2_PIN_SFT 12
+#define RT5668_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5668_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5668_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5668_GP3_PIN_MASK (0x3 << 10)
+#define RT5668_GP3_PIN_SFT 10
+#define RT5668_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5668_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5668_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5668_GP4_PIN_MASK (0x3 << 8)
+#define RT5668_GP4_PIN_SFT 8
+#define RT5668_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5668_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5668_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5668_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5668_GP5_PIN_MASK (0x3 << 6)
+#define RT5668_GP5_PIN_SFT 6
+#define RT5668_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5668_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5668_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5668_GP6_PIN_MASK (0x1 << 5)
+#define RT5668_GP6_PIN_SFT 5
+#define RT5668_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5668_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5668_GP1_PF_MASK (0x1 << 15)
+#define RT5668_GP1_PF_IN (0x0 << 15)
+#define RT5668_GP1_PF_OUT (0x1 << 15)
+#define RT5668_GP1_OUT_MASK (0x1 << 14)
+#define RT5668_GP1_OUT_L (0x0 << 14)
+#define RT5668_GP1_OUT_H (0x1 << 14)
+#define RT5668_GP2_PF_MASK (0x1 << 13)
+#define RT5668_GP2_PF_IN (0x0 << 13)
+#define RT5668_GP2_PF_OUT (0x1 << 13)
+#define RT5668_GP2_OUT_MASK (0x1 << 12)
+#define RT5668_GP2_OUT_L (0x0 << 12)
+#define RT5668_GP2_OUT_H (0x1 << 12)
+#define RT5668_GP3_PF_MASK (0x1 << 11)
+#define RT5668_GP3_PF_IN (0x0 << 11)
+#define RT5668_GP3_PF_OUT (0x1 << 11)
+#define RT5668_GP3_OUT_MASK (0x1 << 10)
+#define RT5668_GP3_OUT_L (0x0 << 10)
+#define RT5668_GP3_OUT_H (0x1 << 10)
+#define RT5668_GP4_PF_MASK (0x1 << 9)
+#define RT5668_GP4_PF_IN (0x0 << 9)
+#define RT5668_GP4_PF_OUT (0x1 << 9)
+#define RT5668_GP4_OUT_MASK (0x1 << 8)
+#define RT5668_GP4_OUT_L (0x0 << 8)
+#define RT5668_GP4_OUT_H (0x1 << 8)
+#define RT5668_GP5_PF_MASK (0x1 << 7)
+#define RT5668_GP5_PF_IN (0x0 << 7)
+#define RT5668_GP5_PF_OUT (0x1 << 7)
+#define RT5668_GP5_OUT_MASK (0x1 << 6)
+#define RT5668_GP5_OUT_L (0x0 << 6)
+#define RT5668_GP5_OUT_H (0x1 << 6)
+#define RT5668_GP6_PF_MASK (0x1 << 5)
+#define RT5668_GP6_PF_IN (0x0 << 5)
+#define RT5668_GP6_PF_OUT (0x1 << 5)
+#define RT5668_GP6_OUT_MASK (0x1 << 4)
+#define RT5668_GP6_OUT_L (0x0 << 4)
+#define RT5668_GP6_OUT_H (0x1 << 4)
+
+
+/* GPIO Status (0x00c2) */
+#define RT5668_GP6_STA (0x1 << 6)
+#define RT5668_GP5_STA (0x1 << 5)
+#define RT5668_GP4_STA (0x1 << 4)
+#define RT5668_GP3_STA (0x1 << 3)
+#define RT5668_GP2_STA (0x1 << 2)
+#define RT5668_GP1_STA (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5668_SV_MASK (0x1 << 15)
+#define RT5668_SV_SFT 15
+#define RT5668_SV_DIS (0x0 << 15)
+#define RT5668_SV_EN (0x1 << 15)
+#define RT5668_ZCD_MASK (0x1 << 10)
+#define RT5668_ZCD_SFT 10
+#define RT5668_ZCD_PD (0x0 << 10)
+#define RT5668_ZCD_PU (0x1 << 10)
+#define RT5668_SV_DLY_MASK (0xf)
+#define RT5668_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0x00da) */
+#define RT5668_ZCD_BST1_CBJ_MASK (0x1 << 7)
+#define RT5668_ZCD_BST1_CBJ_SFT 7
+#define RT5668_ZCD_BST1_CBJ_DIS (0x0 << 7)
+#define RT5668_ZCD_BST1_CBJ_EN (0x1 << 7)
+#define RT5668_ZCD_RECMIX_MASK (0x1)
+#define RT5668_ZCD_RECMIX_SFT 0
+#define RT5668_ZCD_RECMIX_DIS (0x0)
+#define RT5668_ZCD_RECMIX_EN (0x1)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5668_4BTN_IL_MASK (0x1 << 15)
+#define RT5668_4BTN_IL_EN (0x1 << 15)
+#define RT5668_4BTN_IL_DIS (0x0 << 15)
+#define RT5668_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5668_4BTN_IL_NOR (0x1 << 14)
+#define RT5668_4BTN_IL_RST (0x0 << 14)
+
+/* Analog JD Control (0x00f0) */
+#define RT5668_JDH_RS_MASK (0x1 << 4)
+#define RT5668_JDH_NO_PLUG (0x1 << 4)
+#define RT5668_JDH_PLUG (0x0 << 4)
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5668_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5668_CKXEN_DAC1_SFT 13
+#define RT5668_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5668_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5668_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5668_CKXEN_ADC1_SFT 13
+#define RT5668_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5668_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5668_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5668_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5668_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5668_AD2DA_LB_MASK (0x1 << 10)
+#define RT5668_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5668_NG2_EN_MASK (0x1 << 15)
+#define RT5668_NG2_EN (0x1 << 15)
+#define RT5668_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5668_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5668_DEB_80_MS (0x0 << 4)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5668_SAR_BUTT_DET_MASK (0x1 << 15)
+#define RT5668_SAR_BUTT_DET_EN (0x1 << 15)
+#define RT5668_SAR_BUTT_DET_DIS (0x0 << 15)
+#define RT5668_SAR_BUTDET_MODE_MASK (0x1 << 14)
+#define RT5668_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5668_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5668_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5668_SAR_BUTDET_RST_NORMAL (0x1 << 13)
+#define RT5668_SAR_BUTDET_RST (0x0 << 13)
+#define RT5668_SAR_POW_MASK (0x1 << 12)
+#define RT5668_SAR_POW_EN (0x1 << 12)
+#define RT5668_SAR_POW_DIS (0x0 << 12)
+#define RT5668_SAR_RST_MASK (0x1 << 11)
+#define RT5668_SAR_RST_NORMAL (0x1 << 11)
+#define RT5668_SAR_RST (0x0 << 11)
+#define RT5668_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5668_SAR_BYPASS_EN (0x1 << 10)
+#define RT5668_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5668_SAR_SEL_MB1_MASK (0x1 << 9)
+#define RT5668_SAR_SEL_MB1_SEL (0x1 << 9)
+#define RT5668_SAR_SEL_MB1_NOSEL (0x0 << 9)
+#define RT5668_SAR_SEL_MB2_MASK (0x1 << 8)
+#define RT5668_SAR_SEL_MB2_SEL (0x1 << 8)
+#define RT5668_SAR_SEL_MB2_NOSEL (0x0 << 8)
+#define RT5668_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5668_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5668_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5668_SAR_SEL_MB1_MB2_MASK (0x1 << 5)
+#define RT5668_SAR_SEL_MB1_MB2_AUTO (0x1 << 5)
+#define RT5668_SAR_SEL_MB1_MB2_MANU (0x0 << 5)
+#define RT5668_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5668_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5668_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5668_SAR_SOUR_MASK (0x3f)
+#define RT5668_SAR_SOUR_BTN (0x3f)
+#define RT5668_SAR_SOUR_TYPE (0x0)
+
+
+/* System Clock Source */
+enum {
+ RT5668_SCLK_S_MCLK,
+ RT5668_SCLK_S_PLL1,
+ RT5668_SCLK_S_PLL2,
+ RT5668_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5668_PLL1_S_MCLK,
+ RT5668_PLL1_S_BCLK1,
+ RT5668_PLL1_S_RCCLK,
+};
+
+enum {
+ RT5668_AIF1,
+ RT5668_AIF2,
+ RT5668_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5668_DA_STEREO1_FILTER = 0x1,
+ RT5668_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5668_CLK_SEL_SYS,
+ RT5668_CLK_SEL_I2S1_ASRC,
+ RT5668_CLK_SEL_I2S2_ASRC,
+};
+
+#endif /* __RT5668_H__ */
diff --git a/sound/soc/codecs/rt5670-dsp.h b/sound/soc/codecs/rt5670-dsp.h
index a34d0cdb8198..a07b7dfcf501 100644
--- a/sound/soc/codecs/rt5670-dsp.h
+++ b/sound/soc/codecs/rt5670-dsp.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5670-dsp.h -- RT5670 ALSA SoC DSP driver
*
* Copyright 2014 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT5670_DSP_H__
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 49a9e7049e2b..cb5d03bf4c7f 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5670.c -- RT5670 ALSA SoC audio codec driver
*
* Copyright 2014 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -28,12 +25,30 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/rt5670.h>
#include "rl6231.h"
#include "rt5670.h"
#include "rt5670-dsp.h"
+#define RT5670_GPIO1_IS_IRQ BIT(0)
+#define RT5670_IN2_DIFF BIT(1)
+#define RT5670_DMIC_EN BIT(2)
+#define RT5670_DMIC1_IN2P BIT(3)
+#define RT5670_DMIC1_GPIO6 BIT(4)
+#define RT5670_DMIC1_GPIO7 BIT(5)
+#define RT5670_DMIC2_INR BIT(6)
+#define RT5670_DMIC2_GPIO8 BIT(7)
+#define RT5670_DMIC3_GPIO5 BIT(8)
+#define RT5670_JD_MODE1 BIT(9)
+#define RT5670_JD_MODE2 BIT(10)
+#define RT5670_JD_MODE3 BIT(11)
+#define RT5670_GPIO1_IS_EXT_SPK_EN BIT(12)
+
+static unsigned long rt5670_quirk;
+static unsigned int quirk_override;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
#define RT5670_DEVICE_ID 0x6271
#define RT5670_PR_RANGE_BASE (0xff + 1)
@@ -53,8 +68,9 @@ static const struct regmap_range_cfg rt5670_ranges[] = {
static const struct reg_sequence init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
- { RT5670_PR_BASE + 0x38, 0x3ba1 },
+ { RT5670_PR_BASE + 0x38, 0x1fe1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
+ { 0x8a, 0x0123 },
};
static const struct reg_default rt5670_reg[] = {
@@ -131,7 +147,7 @@ static const struct reg_default rt5670_reg[] = {
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0123 },
{ 0x8b, 0x0000 },
{ 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
@@ -404,7 +420,7 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
/**
* rt5670_headset_detect - Detect headset.
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @jack_insert: Jack insert or not.
*
* Detect whether is headset or not when jack inserted.
@@ -412,46 +428,46 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
* Returns detect status.
*/
-static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+static int rt5670_headset_detect(struct snd_soc_component *component, int jack_insert)
{
int val;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (jack_insert) {
snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x0);
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL2,
RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
RT5670_CBJ_MN_JD);
- snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
- snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
+ snd_soc_component_write(component, RT5670_GPIO_CTRL2, 0x0004);
+ snd_soc_component_update_bits(component, RT5670_GPIO_CTRL1,
RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_write(component, RT5670_JD_CTRL3, 0x00f0);
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL2,
RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD);
- snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ snd_soc_component_update_bits(component, RT5670_CJ_CTRL2,
RT5670_CBJ_MN_JD, 0);
msleep(300);
- val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7;
+ val = snd_soc_component_read(component, RT5670_CJ_CTRL3) & 0x7;
if (val == 0x1 || val == 0x2) {
rt5670->jack_type = SND_JACK_HEADSET;
/* for push button */
- snd_soc_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);
+ snd_soc_component_update_bits(component, RT5670_INT_IRQ_ST, 0x8, 0x8);
+ snd_soc_component_update_bits(component, RT5670_IL_CMD, 0x40, 0x40);
+ snd_soc_component_read(component, RT5670_IL_CMD);
} else {
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = SND_JACK_HEADPHONE;
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
}
} else {
- snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
- snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ snd_soc_component_update_bits(component, RT5670_INT_IRQ_ST, 0x8, 0x0);
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x4);
rt5670->jack_type = 0;
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
@@ -460,35 +476,35 @@ static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
return rt5670->jack_type;
}
-void rt5670_jack_suspend(struct snd_soc_codec *codec)
+void rt5670_jack_suspend(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
rt5670->jack_type_saved = rt5670->jack_type;
- rt5670_headset_detect(codec, 0);
+ rt5670_headset_detect(component, 0);
}
EXPORT_SYMBOL_GPL(rt5670_jack_suspend);
-void rt5670_jack_resume(struct snd_soc_codec *codec)
+void rt5670_jack_resume(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (rt5670->jack_type_saved)
- rt5670_headset_detect(codec, 1);
+ rt5670_headset_detect(component, 1);
}
EXPORT_SYMBOL_GPL(rt5670_jack_resume);
-static int rt5670_button_detect(struct snd_soc_codec *codec)
+static int rt5670_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
- val = snd_soc_read(codec, RT5670_IL_CMD);
+ val = snd_soc_component_read(component, RT5670_IL_CMD);
btn_type = val & 0xff80;
- snd_soc_write(codec, RT5670_IL_CMD, val);
+ snd_soc_component_write(component, 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);
+ val = snd_soc_component_read(component, RT5670_IL_CMD);
+ snd_soc_component_write(component, RT5670_IL_CMD, val);
}
return btn_type;
@@ -501,26 +517,26 @@ static int rt5670_irq_detection(void *data)
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;
+ if (rt5670->jd_mode == 1) /* 2 port */
+ val = snd_soc_component_read(rt5670->component, RT5670_A_JD_CTRL1) & 0x0070;
else
- val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020;
+ val = snd_soc_component_read(rt5670->component, RT5670_A_JD_CTRL1) & 0x0020;
switch (val) {
/* jack in */
case 0x30: /* 2 port */
case 0x0: /* 1 port or 2 port */
if (rt5670->jack_type == 0) {
- report = rt5670_headset_detect(rt5670->codec, 1);
+ report = rt5670_headset_detect(rt5670->component, 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) {
+ if (snd_soc_component_read(rt5670->component, RT5670_INT_IRQ_ST) & 0x4) {
/* button pressed */
report = SND_JACK_HEADSET;
- btn_type = rt5670_button_detect(rt5670->codec);
+ btn_type = rt5670_button_detect(rt5670->component);
switch (btn_type) {
case 0x2000: /* up */
report |= SND_JACK_BTN_1;
@@ -532,7 +548,7 @@ static int rt5670_irq_detection(void *data)
report |= SND_JACK_BTN_2;
break;
default:
- dev_err(rt5670->codec->dev,
+ dev_err(rt5670->component->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
@@ -547,8 +563,8 @@ static int rt5670_irq_detection(void *data)
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);
+ snd_soc_component_update_bits(rt5670->component, RT5670_INT_IRQ_ST, 0x1, 0x0);
+ rt5670_headset_detect(rt5670->component, 0);
gpio->debounce_time = 150; /* for jack in */
break;
default:
@@ -558,15 +574,15 @@ static int rt5670_irq_detection(void *data)
return report;
}
-int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+int rt5670_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
int ret;
rt5670->jack = jack;
- rt5670->hp_gpio.gpiod_dev = codec->dev;
- rt5670->hp_gpio.name = "headphone detect";
+ rt5670->hp_gpio.gpiod_dev = component->dev;
+ rt5670->hp_gpio.name = "headset";
rt5670->hp_gpio.report = SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2;
rt5670->hp_gpio.debounce_time = 150;
@@ -577,7 +593,7 @@ int rt5670_set_jack_detect(struct snd_soc_codec *codec,
ret = snd_soc_jack_add_gpios(rt5670->jack, 1,
&rt5670->hp_gpio);
if (ret) {
- dev_err(codec->dev, "Adding jack GPIO failed\n");
+ dev_err(component->dev, "Adding jack GPIO failed\n");
return ret;
}
@@ -586,9 +602,9 @@ int rt5670_set_jack_detect(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(rt5670_set_jack_detect);
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
@@ -613,21 +629,69 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_dac_enum, RT5670_DIG_INF1_DATA,
static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_enum, RT5670_DIG_INF1_DATA,
RT5670_IF2_ADC_SEL_SFT, rt5670_data_select);
+/*
+ * For reliable output-mute LED control we need a "DAC1 Playback Switch" control.
+ * We emulate this by only clearing the RT5670_M_DAC1_L/_R AD_DA_MIXER register
+ * bits when both our emulated DAC1 Playback Switch control and the DAC1 MIXL/R
+ * DAPM-mixer DAC1 input are enabled.
+ */
+static void rt5670_update_ad_da_mixer_dac1_m_bits(struct rt5670_priv *rt5670)
+{
+ int val = RT5670_M_DAC1_L | RT5670_M_DAC1_R;
+
+ if (rt5670->dac1_mixl_dac1_switch && rt5670->dac1_playback_switch_l)
+ val &= ~RT5670_M_DAC1_L;
+
+ if (rt5670->dac1_mixr_dac1_switch && rt5670->dac1_playback_switch_r)
+ val &= ~RT5670_M_DAC1_R;
+
+ regmap_update_bits(rt5670->regmap, RT5670_AD_DA_MIXER,
+ RT5670_M_DAC1_L | RT5670_M_DAC1_R, val);
+}
+
+static int rt5670_dac1_playback_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5670->dac1_playback_switch_l;
+ ucontrol->value.integer.value[1] = rt5670->dac1_playback_switch_r;
+
+ return 0;
+}
+
+static int rt5670_dac1_playback_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ if (rt5670->dac1_playback_switch_l == ucontrol->value.integer.value[0] &&
+ rt5670->dac1_playback_switch_r == ucontrol->value.integer.value[1])
+ return 0;
+
+ rt5670->dac1_playback_switch_l = ucontrol->value.integer.value[0];
+ rt5670->dac1_playback_switch_r = ucontrol->value.integer.value[1];
+
+ rt5670_update_ad_da_mixer_dac1_m_bits(rt5670);
+
+ return 1;
+}
+
static const struct snd_kcontrol_new rt5670_snd_controls[] = {
/* Headphone Output Volume */
- SOC_DOUBLE("HP Playback Switch", RT5670_HP_VOL,
- RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
- 39, 0, out_vol_tlv),
+ 39, 1, out_vol_tlv),
/* OUTPUT Control */
- SOC_DOUBLE("OUT Channel Switch", RT5670_LOUT1,
- RT5670_VOL_L_SFT, RT5670_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("OUT Playback Volume", RT5670_LOUT1,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, 39, 1, out_vol_tlv),
/* DAC Digital Volume */
SOC_DOUBLE("DAC2 Playback Switch", RT5670_DAC_CTRL,
RT5670_M_DAC_L2_VOL_SFT, RT5670_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_EXT("DAC1 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt5670_dac1_playback_switch_get, rt5670_dac1_playback_switch_put),
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5670_DAC1_DIG_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
175, 0, dac_vol_tlv),
@@ -680,17 +744,17 @@ static const struct snd_kcontrol_new rt5670_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5670->sysclk / rl6231_get_pre_div(rt5670->regmap,
RT5670_ADDA_CLK1, RT5670_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5670_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5670_DMIC_CTRL1,
RT5670_DMIC_CLK_MASK, idx << RT5670_DMIC_CLK_SFT);
return idx;
}
@@ -698,8 +762,8 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
return 1;
@@ -710,7 +774,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg, shift, val;
switch (source->shift) {
@@ -746,7 +810,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
@@ -762,8 +826,8 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
return 1;
@@ -774,7 +838,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
/**
* rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -786,7 +850,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
* set of filters specified by the mask. And the codec driver will turn on ASRC
* for these filters if ASRC is selected as their clock source.
*/
-int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5670_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
unsigned int asrc2_mask = 0, asrc2_value = 0;
@@ -844,11 +908,11 @@ int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5670_ASRC_2,
+ snd_soc_component_update_bits(component, RT5670_ASRC_2,
asrc2_mask, asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5670_ASRC_3,
+ snd_soc_component_update_bits(component, RT5670_ASRC_3,
asrc3_mask, asrc3_value);
return 0;
}
@@ -897,18 +961,44 @@ static const struct snd_kcontrol_new rt5670_mono_adc_r_mix[] = {
RT5670_M_MONO_ADC_R2_SFT, 1, 1),
};
+/* See comment above rt5670_update_ad_da_mixer_dac1_m_bits() */
+static int rt5670_put_dac1_mix_dac1_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (mc->shift == 0)
+ rt5670->dac1_mixl_dac1_switch = ucontrol->value.integer.value[0];
+ else
+ rt5670->dac1_mixr_dac1_switch = ucontrol->value.integer.value[0];
+
+ /* Apply the update (if any) */
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ if (ret == 0)
+ return 0;
+
+ rt5670_update_ad_da_mixer_dac1_m_bits(rt5670);
+
+ return 1;
+}
+
+#define SOC_DAPM_SINGLE_RT5670_DAC1_SW(name, shift) \
+ SOC_SINGLE_EXT(name, SND_SOC_NOPM, shift, 1, 0, \
+ snd_soc_dapm_get_volsw, rt5670_put_dac1_mix_dac1_switch)
+
static const struct snd_kcontrol_new rt5670_dac_l_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER,
RT5670_M_ADCMIX_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5670_AD_DA_MIXER,
- RT5670_M_DAC1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 0),
};
static const struct snd_kcontrol_new rt5670_dac_r_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER,
RT5670_M_ADCMIX_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5670_AD_DA_MIXER,
- RT5670_M_DAC1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 1),
};
static const struct snd_kcontrol_new rt5670_sto_dac_l_mix[] = {
@@ -1038,20 +1128,6 @@ static const struct snd_kcontrol_new rt5670_lout_mix[] = {
RT5670_M_OV_R_LM_SFT, 1, 1),
};
-static const struct snd_kcontrol_new rt5670_hpl_mix[] = {
- SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_HPO_MIXER,
- RT5670_M_DACL1_HML_SFT, 1, 1),
- SOC_DAPM_SINGLE("INL1 Switch", RT5670_HPO_MIXER,
- RT5670_M_INL1_HML_SFT, 1, 1),
-};
-
-static const struct snd_kcontrol_new rt5670_hpr_mix[] = {
- SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_HPO_MIXER,
- RT5670_M_DACR1_HMR_SFT, 1, 1),
- SOC_DAPM_SINGLE("INR1 Switch", RT5670_HPO_MIXER,
- RT5670_M_INR1_HMR_SFT, 1, 1),
-};
-
static const struct snd_kcontrol_new lout_l_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1,
RT5670_L_MUTE_SFT, 1, 1);
@@ -1077,25 +1153,29 @@ static SOC_ENUM_SINGLE_DECL(rt5670_dac1r_enum, RT5670_AD_DA_MIXER,
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"
-};
+/* DAC2 L source*/ /* MX-1B [6:4] */
+static const char *const rt5670_dac12_src[] = {
+ "IF1 DAC", "IF2 DAC", "TxDC DAC", "VAD_ADC"
+}; /* VAD_ADC or TxDP_ADC_R */
+
+static const unsigned int rt5670_dac12_values[] = { 0, 1, 3, 5 };
-static SOC_ENUM_SINGLE_DECL(rt5670_dac2l_enum, RT5670_DAC_CTRL,
- RT5670_DAC2_L_SEL_SFT, rt5670_dac12_src);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5670_dac2l_enum, RT5670_DAC_CTRL,
+ RT5670_DAC2_L_SEL_SFT, RT5670_DAC2_L_SEL_MASK,
+ rt5670_dac12_src, rt5670_dac12_values);
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"
-};
+/*DAC2 R source*/ /* MX-1B [2:0] */
+static const char *const rt5670_dacr2_src[] = { "IF1 DAC", "IF2 DAC",
+ "TxDC DAC", "TxDP ADC" };
+
+static const unsigned int rt5670_dacr2_values[] = { 0, 1, 3, 4 };
-static SOC_ENUM_SINGLE_DECL(rt5670_dac2r_enum, RT5670_DAC_CTRL,
- RT5670_DAC2_R_SEL_SFT, rt5670_dacr2_src);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5670_dac2r_enum, RT5670_DAC_CTRL,
+ RT5670_DAC2_R_SEL_SFT, RT5670_DAC2_R_SEL_MASK,
+ rt5670_dacr2_src, rt5670_dacr2_values);
static const struct snd_kcontrol_new rt5670_dac_r2_mux =
SOC_DAPM_ENUM("DAC2 R source", rt5670_dac2r_enum);
@@ -1150,20 +1230,15 @@ static const char * const rt5670_stereo_adc1_src[] = {
static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc1_enum, RT5670_STO1_ADC_MIXER,
RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src);
-static const struct snd_kcontrol_new rt5670_sto_adc_l1_mux =
- SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5670_stereo1_adc1_enum);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_r1_mux =
- SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5670_stereo1_adc1_enum);
+static const struct snd_kcontrol_new rt5670_sto_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC 1 Mux", rt5670_stereo1_adc1_enum);
static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc1_enum, RT5670_STO2_ADC_MIXER,
RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src);
-static const struct snd_kcontrol_new rt5670_sto2_adc_l1_mux =
- SOC_DAPM_ENUM("Stereo2 ADC L1 source", rt5670_stereo2_adc1_enum);
+static const struct snd_kcontrol_new rt5670_sto2_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC 1 Mux", rt5670_stereo2_adc1_enum);
-static const struct snd_kcontrol_new rt5670_sto2_adc_r1_mux =
- SOC_DAPM_ENUM("Stereo2 ADC R1 source", rt5670_stereo2_adc1_enum);
/* MX-27 MX-26 [11] */
static const char * const rt5670_stereo_adc2_src[] = {
@@ -1173,37 +1248,14 @@ static const char * const rt5670_stereo_adc2_src[] = {
static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc2_enum, RT5670_STO1_ADC_MIXER,
RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src);
-static const struct snd_kcontrol_new rt5670_sto_adc_l2_mux =
- SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5670_stereo1_adc2_enum);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_r2_mux =
- SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5670_stereo1_adc2_enum);
+static const struct snd_kcontrol_new rt5670_sto_adc_2_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC 2 Mux", rt5670_stereo1_adc2_enum);
static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc2_enum, RT5670_STO2_ADC_MIXER,
RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src);
-static const struct snd_kcontrol_new rt5670_sto2_adc_l2_mux =
- SOC_DAPM_ENUM("Stereo2 ADC L2 source", rt5670_stereo2_adc2_enum);
-
-static const struct snd_kcontrol_new rt5670_sto2_adc_r2_mux =
- SOC_DAPM_ENUM("Stereo2 ADC R2 source", rt5670_stereo2_adc2_enum);
-
-/* MX-27 MX26 [10] */
-static const char * const rt5670_stereo_adc_src[] = {
- "ADC1L ADC2R", "ADC3"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc_enum, RT5670_STO1_ADC_MIXER,
- RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_mux =
- SOC_DAPM_ENUM("Stereo1 ADC source", rt5670_stereo1_adc_enum);
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc_enum, RT5670_STO2_ADC_MIXER,
- RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src);
-
-static const struct snd_kcontrol_new rt5670_sto2_adc_mux =
- SOC_DAPM_ENUM("Stereo2 ADC source", rt5670_stereo2_adc_enum);
+static const struct snd_kcontrol_new rt5670_sto2_adc_2_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC 2 Mux", rt5670_stereo2_adc2_enum);
/* MX-27 MX-26 [9:8] */
static const char * const rt5670_stereo_dmic_src[] = {
@@ -1222,17 +1274,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER,
static const struct snd_kcontrol_new rt5670_sto2_dmic_mux =
SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum);
-/* MX-27 [0] */
-static const char * const rt5670_stereo_dmic3_src[] = {
- "DMIC3", "PDM ADC"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo_dmic3_enum, RT5670_STO1_ADC_MIXER,
- RT5670_DMIC3_SRC_SFT, rt5670_stereo_dmic3_src);
-
-static const struct snd_kcontrol_new rt5670_sto_dmic3_mux =
- SOC_DAPM_ENUM("Stereo DMIC3 source", rt5670_stereo_dmic3_enum);
-
/* Mono ADC source */
/* MX-28 [12] */
static const char * const rt5670_mono_adc_l1_src[] = {
@@ -1325,17 +1366,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA,
static const struct snd_kcontrol_new rt5670_if2_adc_in_mux =
SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum);
-/* MX-30 [5:4] */
-static const char * const rt5670_if4_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "IF_ADC3"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_if4_adc_in_enum, RT5670_DIG_INF2_DATA,
- RT5670_IF4_ADC_IN_SFT, rt5670_if4_adc_in_src);
-
-static const struct snd_kcontrol_new rt5670_if4_adc_in_mux =
- SOC_DAPM_ENUM("IF4 ADC IN source", rt5670_if4_adc_in_enum);
-
/* MX-31 [15] [13] [11] [9] */
static const char * const rt5670_pdm_src[] = {
"Mono DAC", "Stereo DAC"
@@ -1412,8 +1442,8 @@ static const struct snd_kcontrol_new rt5670_vad_adc_mux =
static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1448,8 +1478,8 @@ static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1495,19 +1525,46 @@ static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5670_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ if (!rt5670->gpio1_is_ext_spk_en)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_HI);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_LO);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static int rt5670_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST1_P, RT5670_PWR_BST1_P);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST1_P, 0);
break;
@@ -1521,16 +1578,16 @@ static int rt5670_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5670_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST2_P, RT5670_PWR_BST2_P);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST2_P, 0);
break;
@@ -1641,23 +1698,23 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
&rt5670_sto1_dmic_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_l2_mux),
+ &rt5670_sto_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_r2_mux),
+ &rt5670_sto_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_l1_mux),
+ &rt5670_sto_adc_1_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_r1_mux),
+ &rt5670_sto_adc_1_mux),
SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
&rt5670_sto2_dmic_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_l2_mux),
+ &rt5670_sto2_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_r2_mux),
+ &rt5670_sto2_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_l1_mux),
+ &rt5670_sto2_adc_1_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_r1_mux),
+ &rt5670_sto2_adc_1_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0,
&rt5670_sto2_adc_lr_mux),
SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
@@ -1677,12 +1734,10 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
RT5670_PWR_ADC_S1F_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5670_PWR_DIG2,
RT5670_PWR_ADC_S2F_BIT, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", RT5670_STO1_ADC_DIG_VOL,
- RT5670_L_MUTE_SFT, 1, rt5670_sto1_adc_l_mix,
- ARRAY_SIZE(rt5670_sto1_adc_l_mix)),
- SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", RT5670_STO1_ADC_DIG_VOL,
- RT5670_R_MUTE_SFT, 1, rt5670_sto1_adc_r_mix,
- ARRAY_SIZE(rt5670_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_sto1_adc_l_mix, ARRAY_SIZE(rt5670_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_sto1_adc_r_mix, ARRAY_SIZE(rt5670_sto1_adc_r_mix)),
SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
rt5670_sto2_adc_l_mix,
ARRAY_SIZE(rt5670_sto2_adc_l_mix)),
@@ -1716,7 +1771,6 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
/* DSP */
SND_SOC_DAPM_PGA("TxDP_ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1909,7 +1963,9 @@ static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
- SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt5670_spk_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_OUTPUT("SPOLP"),
SND_SOC_DAPM_OUTPUT("SPOLN"),
SND_SOC_DAPM_OUTPUT("SPORP"),
@@ -2022,7 +2078,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
{ "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" },
- { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
{ "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
{ "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" },
@@ -2061,7 +2116,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" },
{ "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" },
- { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll },
{ "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
{ "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" },
@@ -2085,13 +2139,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "IF1 ADC1 IN1 Mux", "IF1_ADC3", "IF1_ADC3" },
{ "IF1 ADC1 IN2 Mux", "IF1_ADC1_IN1", "IF1 ADC1 IN1 Mux" },
- { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "IF1_ADC4" },
+ { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "TxDP_ADC" },
{ "IF1 ADC2 IN Mux", "IF_ADC2", "IF_ADC2" },
{ "IF1 ADC2 IN Mux", "VAD_ADC", "VAD_ADC" },
{ "IF1 ADC2 IN1 Mux", "IF1_ADC2_IN", "IF1 ADC2 IN Mux" },
- { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "IF1_ADC4" },
+ { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "TxDP_ADC" },
{ "IF1_ADC1" , NULL, "IF1 ADC1 IN2 Mux" },
{ "IF1_ADC2" , NULL, "IF1 ADC2 IN1 Mux" },
@@ -2316,21 +2370,21 @@ static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
static int rt5670_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
rt5670->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5670->sysclk, rt5670->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n",
rt5670->lrck[dai->id], dai->id);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
@@ -2362,20 +2416,20 @@ static int rt5670_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5670_I2S_BCLK_MS1_MASK | RT5670_I2S_PD1_MASK;
val_clk = bclk_ms << RT5670_I2S_BCLK_MS1_SFT |
pre_div << RT5670_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5670_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S1_SDP,
RT5670_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk);
break;
case RT5670_AIF2:
mask_clk = RT5670_I2S_BCLK_MS2_MASK | RT5670_I2S_PD2_MASK;
val_clk = bclk_ms << RT5670_I2S_BCLK_MS2_SFT |
pre_div << RT5670_I2S_PD2_SFT;
- snd_soc_update_bits(codec, RT5670_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S2_SDP,
RT5670_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -2384,15 +2438,15 @@ static int rt5670_hw_params(struct snd_pcm_substream *substream,
static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5670->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5670_I2S_MS_S;
rt5670->master[dai->id] = 0;
break;
@@ -2428,27 +2482,26 @@ static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5670_AIF1:
- snd_soc_update_bits(codec, RT5670_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S1_SDP,
RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK |
RT5670_I2S_DF_MASK, reg_val);
break;
case RT5670_AIF2:
- snd_soc_update_bits(codec, RT5670_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S2_SDP,
RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK |
RT5670_I2S_DF_MASK, reg_val);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
}
-static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+static int rt5670_set_codec_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (clk_id) {
@@ -2462,16 +2515,16 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5670_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, reg_val);
rt5670->sysclk = freq;
if (clk_id != RT5670_SCLK_S_RCCLK)
rt5670->sysclk_src = clk_id;
- dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ dev_dbg(component->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id);
return 0;
}
@@ -2479,8 +2532,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -2489,18 +2542,18 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5670->pll_in = 0;
rt5670->pll_out = 0;
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5670_PLL1_S_MCLK:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_MCLK);
break;
case RT5670_PLL1_S_BCLK1:
@@ -2509,38 +2562,38 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
case RT5670_PLL1_S_BCLK4:
switch (dai->id) {
case RT5670_AIF1:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK1);
break;
case RT5670_AIF2:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
- snd_soc_write(codec, RT5670_PLL_CTRL1,
+ snd_soc_component_write(component, RT5670_PLL_CTRL1,
pll_code.n_code << RT5670_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, RT5670_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5670_PLL_M_SFT |
- pll_code.m_bp << RT5670_PLL_M_BP_SFT);
+ snd_soc_component_write(component, RT5670_PLL_CTRL2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5670_PLL_M_SFT) |
+ (pll_code.m_bp << RT5670_PLL_M_BP_SFT));
rt5670->pll_in = freq_in;
rt5670->pll_out = freq_out;
@@ -2552,7 +2605,7 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
if (rx_mask || tx_mask)
@@ -2590,57 +2643,76 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5670_TDM_CTRL_1, 0x7c00, val);
+ snd_soc_component_update_bits(component, RT5670_TDM_CTRL_1, 0x7c00, val);
+
+ return 0;
+}
+
+static int rt5670_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
+ if (dai->id != RT5670_AIF1)
+ return 0;
+
+ if ((ratio % 50) == 0)
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3,
+ RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_50FS);
+ else
+ snd_soc_component_update_bits(component, RT5670_GEN_CTRL3,
+ RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_NOR);
return 0;
}
-static int rt5670_set_bias_level(struct snd_soc_codec *codec,
+static int rt5670_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ if (SND_SOC_BIAS_STANDBY == snd_soc_dapm_get_bias_level(dapm)) {
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2);
mdelay(10);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_FV1 | RT5670_PWR_FV2,
RT5670_PWR_FV1 | RT5670_PWR_FV2);
- snd_soc_update_bits(codec, RT5670_CHARGE_PUMP,
+ snd_soc_component_update_bits(component, RT5670_CHARGE_PUMP,
RT5670_OSW_L_MASK | RT5670_OSW_R_MASK,
RT5670_OSW_L_DIS | RT5670_OSW_R_DIS);
- snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x1);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
- RT5670_LDO_SEL_MASK, 0x3);
+ snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x1);
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
+ RT5670_LDO_SEL_MASK, 0x5);
}
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
- RT5670_LDO_SEL_MASK, 0x1);
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
+ RT5670_LDO_SEL_MASK, 0x3);
break;
case SND_SOC_BIAS_OFF:
- if (rt5670->pdata.jd_mode)
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ if (rt5670->jd_mode)
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2,
RT5670_PWR_MB | RT5670_PWR_BG);
else
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+ snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x0);
break;
default:
@@ -2650,12 +2722,12 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int rt5670_probe(struct snd_soc_codec *codec)
+static int rt5670_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
- switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
+ switch (snd_soc_component_read(component, RT5670_RESET) & RT5670_ID_MASK) {
case RT5670_ID_5670:
case RT5670_ID_5671:
snd_soc_dapm_new_controls(dapm,
@@ -2674,37 +2746,36 @@ static int rt5670_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5672_specific_dapm_routes));
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"The driver is for RT5670 RT5671 or RT5672 only\n");
return -ENODEV;
}
- rt5670->codec = codec;
+ rt5670->component = component;
return 0;
}
-static int rt5670_remove(struct snd_soc_codec *codec)
+static void rt5670_remove(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regmap_write(rt5670->regmap, RT5670_RESET, 0);
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)
+static int rt5670_suspend(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5670->regmap, true);
regcache_mark_dirty(rt5670->regmap);
return 0;
}
-static int rt5670_resume(struct snd_soc_codec *codec)
+static int rt5670_resume(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5670->regmap, false);
regcache_sync(rt5670->regmap);
@@ -2723,9 +2794,9 @@ static int rt5670_resume(struct snd_soc_codec *codec)
static const struct snd_soc_dai_ops rt5670_aif_dai_ops = {
.hw_params = rt5670_hw_params,
.set_fmt = rt5670_set_dai_fmt,
- .set_sysclk = rt5670_set_dai_sysclk,
.set_tdm_slot = rt5670_set_tdm_slot,
.set_pll = rt5670_set_dai_pll,
+ .set_bclk_ratio = rt5670_set_bclk_ratio,
};
static struct snd_soc_dai_driver rt5670_dai[] = {
@@ -2747,6 +2818,7 @@ static struct snd_soc_dai_driver rt5670_dai[] = {
.formats = RT5670_FORMATS,
},
.ops = &rt5670_aif_dai_ops,
+ .symmetric_rate = 1,
},
{
.name = "rt5670-aif2",
@@ -2766,33 +2838,37 @@ static struct snd_soc_dai_driver rt5670_dai[] = {
.formats = RT5670_FORMATS,
},
.ops = &rt5670_aif_dai_ops,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
- .probe = rt5670_probe,
- .remove = rt5670_remove,
- .suspend = rt5670_suspend,
- .resume = rt5670_resume,
- .set_bias_level = rt5670_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5670_snd_controls,
- .num_controls = ARRAY_SIZE(rt5670_snd_controls),
- .dapm_widgets = rt5670_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets),
- .dapm_routes = rt5670_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5670 = {
+ .probe = rt5670_probe,
+ .remove = rt5670_remove,
+ .suspend = rt5670_suspend,
+ .resume = rt5670_resume,
+ .set_bias_level = rt5670_set_bias_level,
+ .set_sysclk = rt5670_set_codec_sysclk,
+ .controls = rt5670_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5670_snd_controls),
+ .dapm_widgets = rt5670_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets),
+ .dapm_routes = rt5670_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5670_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) *
RT5670_PR_SPACING),
.volatile_reg = rt5670_volatile_register,
.readable_reg = rt5670_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5670_reg,
.num_reg_defaults = ARRAY_SIZE(rt5670_reg),
.ranges = rt5670_ranges,
@@ -2800,36 +2876,182 @@ static const struct regmap_config rt5670_regmap = {
};
static const struct i2c_device_id rt5670_i2c_id[] = {
- { "rt5670", 0 },
- { "rt5671", 0 },
- { "rt5672", 0 },
+ { "rt5670" },
+ { "rt5671" },
+ { "rt5672" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5670_acpi_match[] = {
- { "10EC5670", 0},
- { },
+ { "10EC5640" }, /* quirk */
+ { "10EC5670" },
+ { "10EC5672" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
#endif
-static const struct dmi_system_id dmi_platform_intel_braswell[] = {
+static int rt5670_quirk_cb(const struct dmi_system_id *id)
+{
+ rt5670_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id dmi_platform_intel_quirks[] = {
{
+ .callback = rt5670_quirk_cb,
.ident = "Intel Braswell",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"),
},
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC1_IN2P |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Dell Wyse 3040",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC1_IN2P |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Lenovo Thinkpad Tablet 8",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC1_IN2P |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC1_IN2P |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Lenovo Miix 2 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC1_IN2P |
+ RT5670_GPIO1_IS_EXT_SPK_EN |
+ RT5670_JD_MODE2),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Dell Venue 8 Pro 5855",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5855"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE3),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Dell Venue 10 Pro 5055",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE1),
+ },
+ {
+ .callback = rt5670_quirk_cb,
+ .ident = "Aegex 10 tablet (RU2)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AEGEX"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "RU2"),
+ },
+ .driver_data = (unsigned long *)(RT5670_DMIC_EN |
+ RT5670_DMIC2_INR |
+ RT5670_GPIO1_IS_IRQ |
+ RT5670_JD_MODE3),
},
{}
};
-static int rt5670_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+const char *rt5670_components(void)
+{
+ unsigned long quirk;
+ bool dmic1 = false;
+ bool dmic2 = false;
+ bool dmic3 = false;
+
+ if (quirk_override) {
+ quirk = quirk_override;
+ } else {
+ dmi_check_system(dmi_platform_intel_quirks);
+ quirk = rt5670_quirk;
+ }
+
+ if ((quirk & RT5670_DMIC1_IN2P) ||
+ (quirk & RT5670_DMIC1_GPIO6) ||
+ (quirk & RT5670_DMIC1_GPIO7))
+ dmic1 = true;
+
+ if ((quirk & RT5670_DMIC2_INR) ||
+ (quirk & RT5670_DMIC2_GPIO8))
+ dmic2 = true;
+
+ if (quirk & RT5670_DMIC3_GPIO5)
+ dmic3 = true;
+
+ if (dmic1 && dmic2)
+ return "cfg-spk:2 cfg-mic:dmics12";
+ else if (dmic1)
+ return "cfg-spk:2 cfg-mic:dmic1";
+ else if (dmic2)
+ return "cfg-spk:2 cfg-mic:dmic2";
+ else if (dmic3)
+ return "cfg-spk:2 cfg-mic:dmic3";
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rt5670_components);
+
+static int rt5670_i2c_probe(struct i2c_client *i2c)
{
- struct rt5670_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5670_priv *rt5670;
int ret;
unsigned int val;
@@ -2842,16 +3064,77 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5670);
- if (pdata)
- rt5670->pdata = *pdata;
+ dmi_check_system(dmi_platform_intel_quirks);
+ if (quirk_override) {
+ dev_info(&i2c->dev, "Overriding quirk 0x%x => 0x%x\n",
+ (unsigned int)rt5670_quirk, quirk_override);
+ rt5670_quirk = quirk_override;
+ }
- if (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;
+ if (rt5670_quirk & RT5670_GPIO1_IS_IRQ) {
+ rt5670->gpio1_is_irq = true;
+ dev_info(&i2c->dev, "quirk GPIO1 is IRQ\n");
+ }
+ if (rt5670_quirk & RT5670_GPIO1_IS_EXT_SPK_EN) {
+ rt5670->gpio1_is_ext_spk_en = true;
+ dev_info(&i2c->dev, "quirk GPIO1 is external speaker enable\n");
+ }
+ if (rt5670_quirk & RT5670_IN2_DIFF) {
+ rt5670->in2_diff = true;
+ dev_info(&i2c->dev, "quirk IN2_DIFF\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC_EN) {
+ rt5670->dmic_en = true;
+ dev_info(&i2c->dev, "quirk DMIC enabled\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC1_IN2P) {
+ rt5670->dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
+ dev_info(&i2c->dev, "quirk DMIC1 on IN2P pin\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC1_GPIO6) {
+ rt5670->dmic1_data_pin = RT5670_DMIC_DATA_GPIO6;
+ dev_info(&i2c->dev, "quirk DMIC1 on GPIO6 pin\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC1_GPIO7) {
+ rt5670->dmic1_data_pin = RT5670_DMIC_DATA_GPIO7;
+ dev_info(&i2c->dev, "quirk DMIC1 on GPIO7 pin\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC2_INR) {
+ rt5670->dmic2_data_pin = RT5670_DMIC_DATA_IN3N;
+ dev_info(&i2c->dev, "quirk DMIC2 on INR pin\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC2_GPIO8) {
+ rt5670->dmic2_data_pin = RT5670_DMIC_DATA_GPIO8;
+ dev_info(&i2c->dev, "quirk DMIC2 on GPIO8 pin\n");
+ }
+ if (rt5670_quirk & RT5670_DMIC3_GPIO5) {
+ rt5670->dmic3_data_pin = RT5670_DMIC_DATA_GPIO5;
+ dev_info(&i2c->dev, "quirk DMIC3 on GPIO5 pin\n");
+ }
+
+ if (rt5670_quirk & RT5670_JD_MODE1) {
+ rt5670->jd_mode = 1;
+ dev_info(&i2c->dev, "quirk JD mode 1\n");
+ }
+ if (rt5670_quirk & RT5670_JD_MODE2) {
+ rt5670->jd_mode = 2;
+ dev_info(&i2c->dev, "quirk JD mode 2\n");
+ }
+ if (rt5670_quirk & RT5670_JD_MODE3) {
+ rt5670->jd_mode = 3;
+ dev_info(&i2c->dev, "quirk JD mode 3\n");
}
+ /*
+ * Enable the emulated "DAC1 Playback Switch" by default to avoid
+ * muting the output with older UCM profiles.
+ */
+ rt5670->dac1_playback_switch_l = true;
+ rt5670->dac1_playback_switch_r = true;
+ /* The Power-On-Reset values for the DAC1 mixer have the DAC1 input enabled. */
+ rt5670->dac1_mixl_dac1_switch = true;
+ rt5670->dac1_mixr_dac1_switch = true;
+
rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap);
if (IS_ERR(rt5670->regmap)) {
ret = PTR_ERR(rt5670->regmap);
@@ -2886,11 +3169,14 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- if (rt5670->pdata.in2_diff)
+ regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC,
+ RT5670_MCLK_DET, RT5670_MCLK_DET);
+
+ if (rt5670->in2_diff)
regmap_update_bits(rt5670->regmap, RT5670_IN2,
RT5670_IN_DF2, RT5670_IN_DF2);
- if (rt5670->pdata.dev_gpio) {
+ if (rt5670->gpio1_is_irq) {
/* for push button */
regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000);
regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010);
@@ -2900,10 +3186,16 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
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) {
+ if (rt5670->gpio1_is_ext_spk_en) {
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_GPIO1);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
+ }
+
+ if (rt5670->jd_mode) {
regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK);
rt5670->sysclk = 0;
@@ -2918,7 +3210,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
RT5670_JD_TRI_CBJ_SEL_MASK |
RT5670_JD_TRI_HPO_SEL_MASK,
RT5670_JD_CBJ_JD1_1 | RT5670_JD_HPO_JD1_1);
- switch (rt5670->pdata.jd_mode) {
+ switch (rt5670->jd_mode) {
case 1:
regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1,
RT5670_JD1_MODE_MASK,
@@ -2939,12 +3231,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
}
}
- if (rt5670->pdata.dmic_en) {
+ if (rt5670->dmic_en) {
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
RT5670_GP2_PIN_MASK,
RT5670_GP2_PIN_DMIC1_SCL);
- switch (rt5670->pdata.dmic1_data_pin) {
+ switch (rt5670->dmic1_data_pin) {
case RT5670_DMIC_DATA_IN2P:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
RT5670_DMIC_1_DP_MASK,
@@ -2973,7 +3265,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
break;
}
- switch (rt5670->pdata.dmic2_data_pin) {
+ switch (rt5670->dmic2_data_pin) {
case RT5670_DMIC_DATA_IN3N:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
RT5670_DMIC_2_DP_MASK,
@@ -2993,7 +3285,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
break;
}
- switch (rt5670->pdata.dmic3_data_pin) {
+ switch (rt5670->dmic3_data_pin) {
case RT5670_DMIC_DATA_GPIO5:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL2,
RT5670_DMIC_3_DP_MASK,
@@ -3018,13 +3310,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5670,
rt5670_dai, ARRAY_SIZE(rt5670_dai));
if (ret < 0)
goto err;
- pm_runtime_put(&i2c->dev);
-
return 0;
err:
pm_runtime_disable(&i2c->dev);
@@ -3032,12 +3323,9 @@ err:
return ret;
}
-static int rt5670_i2c_remove(struct i2c_client *i2c)
+static void rt5670_i2c_remove(struct i2c_client *i2c)
{
pm_runtime_disable(&i2c->dev);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
static struct i2c_driver rt5670_i2c_driver = {
@@ -3045,7 +3333,7 @@ static struct i2c_driver rt5670_i2c_driver = {
.name = "rt5670",
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
- .probe = rt5670_i2c_probe,
+ .probe = rt5670_i2c_probe,
.remove = rt5670_i2c_remove,
.id_table = rt5670_i2c_id,
};
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index 3f1b0f1df809..5b230897f630 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -1,19 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5670.h -- RT5670 ALSA SoC audio driver
*
* Copyright 2014 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT5670_H__
#define __RT5670_H__
-#include <sound/rt5670.h>
-
/* Info */
#define RT5670_RESET 0x00
#define RT5670_VENDOR_ID 0xfd
@@ -217,12 +212,8 @@
/* global definition */
#define RT5670_L_MUTE (0x1 << 15)
#define RT5670_L_MUTE_SFT 15
-#define RT5670_VOL_L_MUTE (0x1 << 14)
-#define RT5670_VOL_L_SFT 14
#define RT5670_R_MUTE (0x1 << 7)
#define RT5670_R_MUTE_SFT 7
-#define RT5670_VOL_R_MUTE (0x1 << 6)
-#define RT5670_VOL_R_SFT 6
#define RT5670_L_VOL_MASK (0x3f << 8)
#define RT5670_L_VOL_SFT 8
#define RT5670_R_VOL_MASK (0x3f)
@@ -760,7 +751,7 @@
#define RT5670_PWR_VREF2_BIT 4
#define RT5670_PWR_FV2 (0x1 << 3)
#define RT5670_PWR_FV2_BIT 3
-#define RT5670_LDO_SEL_MASK (0x3)
+#define RT5670_LDO_SEL_MASK (0x7)
#define RT5670_LDO_SEL_SFT 0
/* Power Management for Analog 2 (0x64) */
@@ -1816,6 +1807,10 @@
#define RT5670_ZCD_HP_DIS (0x0 << 15)
#define RT5670_ZCD_HP_EN (0x1 << 15)
+/* General Control 3 (0xfc) */
+#define RT5670_TDM_DATA_MODE_SEL (0x1 << 11)
+#define RT5670_TDM_DATA_MODE_NOR (0x0 << 11)
+#define RT5670_TDM_DATA_MODE_50FS (0x1 << 11)
/* Codec Private Register definition */
/* 3D Speaker Control (0x63) */
@@ -1914,6 +1909,7 @@ enum {
#define RT5670_IF1_ADC1_IN2_SFT 11
#define RT5670_IF1_ADC2_IN1_SEL (0x1 << 10)
#define RT5670_IF1_ADC2_IN1_SFT 10
+#define RT5670_MCLK_DET (0x1 << 3)
/* General Control2 (0xfb) */
#define RT5670_RXDC_SRC_MASK (0x1 << 7)
@@ -1981,16 +1977,28 @@ enum {
RT5670_DOWN_RATE_FILTER = (0x1 << 7),
};
-int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5670_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
struct rt5670_priv {
- struct snd_soc_codec *codec;
- struct rt5670_platform_data pdata;
+ struct snd_soc_component *component;
struct regmap *regmap;
struct snd_soc_jack *jack;
struct snd_soc_jack_gpio hp_gpio;
+ int jd_mode;
+ bool in2_diff;
+ bool gpio1_is_irq;
+ bool gpio1_is_ext_spk_en;
+
+ bool dmic_en;
+ unsigned int dmic1_data_pin;
+ /* 0 = GPIO6; 1 = IN2P; 3 = GPIO7*/
+ unsigned int dmic2_data_pin;
+ /* 0 = GPIO8; 1 = IN3N; */
+ unsigned int dmic3_data_pin;
+ /* 0 = GPIO9; 1 = GPIO10; 2 = GPIO5*/
+
int sysclk;
int sysclk_src;
int lrck[RT5670_AIFS];
@@ -2005,10 +2013,17 @@ struct rt5670_priv {
int dsp_rate;
int jack_type;
int jack_type_saved;
+
+ bool dac1_mixl_dac1_switch;
+ bool dac1_mixr_dac1_switch;
+ bool dac1_playback_switch_l;
+ bool dac1_playback_switch_r;
};
-void rt5670_jack_suspend(struct snd_soc_codec *codec);
-void rt5670_jack_resume(struct snd_soc_codec *codec);
-int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+void rt5670_jack_suspend(struct snd_soc_component *component);
+void rt5670_jack_resume(struct snd_soc_component *component);
+int rt5670_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
+const char *rt5670_components(void);
+
#endif /* __RT5670_H__ */
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index 91879ea95415..885edcf0a3a5 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5677-spi.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -18,19 +15,22 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
#include <linux/sched.h>
-#include <linux/kthread.h>
#include <linux/uaccess.h>
-#include <linux/miscdevice.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_qos.h>
#include <linux/sysfs.h>
#include <linux/clk.h>
#include <linux/firmware.h>
+#include <linux/acpi.h>
+
+#include <sound/soc.h>
+#include "rt5677.h"
#include "rt5677-spi.h"
+#define DRV_NAME "rt5677spi"
+
#define RT5677_SPI_BURST_LEN 240
#define RT5677_SPI_HEADER 5
#define RT5677_SPI_FREQ 6000000
@@ -48,9 +48,366 @@
#define RT5677_SPI_WRITE_16 0x1
#define RT5677_SPI_READ_16 0x0
+#define RT5677_BUF_BYTES_TOTAL 0x20000
+#define RT5677_MIC_BUF_ADDR 0x60030000
+#define RT5677_MODEL_ADDR 0x5FFC9800
+#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \
+ sizeof(u32)))
+#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000
+
static struct spi_device *g_spi;
static DEFINE_MUTEX(spi_mutex);
+struct rt5677_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ size_t dma_offset; /* zero-based offset into runtime->dma_area */
+ size_t avail_bytes; /* number of new bytes since last period */
+ u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */
+ bool new_hotword; /* a new hotword is fired */
+};
+
+static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = RT5677_BUF_BYTES_TOTAL,
+};
+
+static struct snd_soc_dai_driver rt5677_spi_dai = {
+ /* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
+ * registered with ASoC is the name of the device "spi-RT5677AA:00",
+ * because we only have one DAI. See snd_soc_register_dais().
+ */
+ .name = "rt5677-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5677_spi_pcm_open(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
+ return 0;
+}
+
+static int rt5677_spi_pcm_close(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *codec_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(codec_component);
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt5677_dsp->copy_work);
+ rt5677->set_dsp_vad(codec_component, false);
+ return 0;
+}
+
+static int rt5677_spi_hw_params(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ rt5677_dsp->substream = substream;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ return 0;
+}
+
+static int rt5677_spi_hw_free(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ rt5677_dsp->substream = NULL;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ return 0;
+}
+
+static int rt5677_spi_prepare(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *rt5677_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(rt5677_component);
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ rt5677->set_dsp_vad(rt5677_component, true);
+ rt5677_dsp->dma_offset = 0;
+ rt5677_dsp->avail_bytes = 0;
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
+}
+
+static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
+{
+ int ret;
+ /* Grab the first 4 bytes that hold the write pointer on the
+ * dsp, and check to make sure that it points somewhere inside the
+ * buffer.
+ */
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
+ sizeof(u32));
+ if (ret)
+ return ret;
+ /* Adjust the offset so that it's zero-based */
+ *mic_write_offset = *mic_write_offset - sizeof(u32);
+ return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
+}
+
+/*
+ * Copy one contiguous block of audio samples from the DSP mic buffer to the
+ * dma_area of the pcm runtime. The receiving buffer may wrap around.
+ * @begin: start offset of the block to copy, in bytes.
+ * @end: offset of the first byte after the block to copy, must be greater
+ * than or equal to begin.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
+ u32 begin, u32 end)
+{
+ struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
+ size_t bytes_per_frame = frames_to_bytes(runtime, 1);
+ size_t first_chunk_len, second_chunk_len;
+ int ret;
+
+ if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
+ dev_err(rt5677_dsp->dev,
+ "Invalid copy from (%u, %u), dma_area size %zu\n",
+ begin, end, runtime->dma_bytes);
+ return -EINVAL;
+ }
+
+ /* The block to copy is empty */
+ if (begin == end)
+ return 0;
+
+ /* If the incoming chunk is too big for the receiving buffer, only the
+ * last "receiving buffer size - one frame" bytes are copied.
+ */
+ if (end - begin > runtime->dma_bytes - bytes_per_frame)
+ begin = end - (runtime->dma_bytes - bytes_per_frame);
+
+ /* May need to split to two chunks, calculate the size of each */
+ first_chunk_len = end - begin;
+ second_chunk_len = 0;
+ if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
+ /* Receiving buffer wrapped around */
+ second_chunk_len = first_chunk_len;
+ first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
+ second_chunk_len -= first_chunk_len;
+ }
+
+ /* Copy first chunk */
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
+ runtime->dma_area + rt5677_dsp->dma_offset,
+ first_chunk_len);
+ if (ret)
+ return ret;
+ rt5677_dsp->dma_offset += first_chunk_len;
+ if (rt5677_dsp->dma_offset == runtime->dma_bytes)
+ rt5677_dsp->dma_offset = 0;
+
+ /* Copy second chunk */
+ if (second_chunk_len) {
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
+ begin + first_chunk_len, runtime->dma_area,
+ second_chunk_len);
+ if (!ret)
+ rt5677_dsp->dma_offset = second_chunk_len;
+ }
+ return ret;
+}
+
+/*
+ * Copy a given amount of audio samples from the DSP mic buffer starting at
+ * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
+ * wrap around. mic_read_offset is updated after successful copy.
+ * @amount: amount of samples to copy, in bytes.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
+{
+ int ret = 0;
+ u32 target;
+
+ if (amount == 0)
+ return ret;
+
+ target = rt5677_dsp->mic_read_offset + amount;
+ /* Copy the first chunk in DSP's mic buffer */
+ ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
+ min(target, RT5677_MIC_BUF_BYTES));
+
+ if (target >= RT5677_MIC_BUF_BYTES) {
+ /* Wrap around, copy the second chunk */
+ target -= RT5677_MIC_BUF_BYTES;
+ ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
+ }
+
+ if (!ret)
+ rt5677_dsp->mic_read_offset = target;
+ return ret;
+}
+
+/*
+ * A delayed work that streams audio samples from the DSP mic buffer to the
+ * dma_area of the pcm runtime via SPI.
+ */
+static void rt5677_spi_copy_work(struct work_struct *work)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ container_of(work, struct rt5677_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime;
+ u32 mic_write_offset;
+ size_t new_bytes, copy_bytes, period_bytes;
+ unsigned int delay;
+ int ret = 0;
+
+ /* Ensure runtime->dma_area buffer does not go away while copying. */
+ mutex_lock(&rt5677_dsp->dma_lock);
+ if (!rt5677_dsp->substream) {
+ dev_err(rt5677_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ runtime = rt5677_dsp->substream->runtime;
+
+ if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
+ dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
+ goto done;
+ }
+
+ /* If this is the first time that we've asked for streaming data after
+ * a hotword is fired, we should start reading from the previous 2
+ * seconds of audio from wherever the mic_write_offset is currently.
+ */
+ if (rt5677_dsp->new_hotword) {
+ rt5677_dsp->new_hotword = false;
+ /* See if buffer wraparound happens */
+ if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
+ rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
+ (RT5677_MIC_BUF_FIRST_READ_SIZE -
+ mic_write_offset);
+ else
+ rt5677_dsp->mic_read_offset = mic_write_offset -
+ RT5677_MIC_BUF_FIRST_READ_SIZE;
+ }
+
+ /* Calculate the amount of new samples in bytes */
+ if (rt5677_dsp->mic_read_offset <= mic_write_offset)
+ new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
+ else
+ new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
+ - rt5677_dsp->mic_read_offset;
+
+ /* Copy all new samples from DSP mic buffer, one period at a time */
+ period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
+ while (new_bytes) {
+ copy_bytes = min(new_bytes, period_bytes
+ - rt5677_dsp->avail_bytes);
+ ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
+ if (ret) {
+ dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
+ goto done;
+ }
+ rt5677_dsp->avail_bytes += copy_bytes;
+ if (rt5677_dsp->avail_bytes >= period_bytes) {
+ snd_pcm_period_elapsed(rt5677_dsp->substream);
+ rt5677_dsp->avail_bytes = 0;
+ }
+ new_bytes -= copy_bytes;
+ }
+
+ delay = bytes_to_frames(runtime, period_bytes) / runtime->rate;
+ schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay));
+done:
+ mutex_unlock(&rt5677_dsp->dma_lock);
+}
+
+static int rt5677_spi_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ return 0;
+}
+
+static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
+{
+ struct rt5677_dsp *rt5677_dsp;
+
+ rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
+ GFP_KERNEL);
+ if (!rt5677_dsp)
+ return -ENOMEM;
+ rt5677_dsp->dev = &g_spi->dev;
+ mutex_init(&rt5677_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
+
+ snd_soc_component_set_drvdata(component, rt5677_dsp);
+ return 0;
+}
+
+static const struct snd_soc_component_driver rt5677_spi_dai_component = {
+ .name = DRV_NAME,
+ .probe = rt5677_spi_pcm_probe,
+ .open = rt5677_spi_pcm_open,
+ .close = rt5677_spi_pcm_close,
+ .hw_params = rt5677_spi_hw_params,
+ .hw_free = rt5677_spi_hw_free,
+ .prepare = rt5677_spi_prepare,
+ .pointer = rt5677_spi_pcm_pointer,
+ .pcm_construct = rt5677_spi_pcm_new,
+ .legacy_dai_naming = 1,
+};
+
/* Select a suitable transfer command for the next transfer to ensure
* the transfer address is always naturally aligned while minimizing
* the total number of transfers required.
@@ -60,13 +417,15 @@ static DEFINE_MUTEX(spi_mutex);
* RT5677_SPI_READ/WRITE_32: Transfer 4 bytes
* RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes
*
- * For example, reading 260 bytes at 0x60030002 uses the following commands:
- * 0x60030002 RT5677_SPI_READ_16 2 bytes
+ * Note:
+ * 16 Bit writes and reads are restricted to the address range
+ * 0x18020000 ~ 0x18021000
+ *
+ * For example, reading 256 bytes at 0x60030004 uses the following commands:
* 0x60030004 RT5677_SPI_READ_32 4 bytes
* 0x60030008 RT5677_SPI_READ_BURST 240 bytes
* 0x600300F8 RT5677_SPI_READ_BURST 8 bytes
* 0x60030100 RT5677_SPI_READ_32 4 bytes
- * 0x60030104 RT5677_SPI_READ_16 2 bytes
*
* Input:
* @read: true for read commands; false for write commands
@@ -81,15 +440,13 @@ static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len)
{
u8 cmd;
- if (align == 2 || align == 6 || remain == 2) {
- cmd = RT5677_SPI_READ_16;
- *len = 2;
- } else if (align == 4 || remain <= 6) {
+ if (align == 4 || remain <= 4) {
cmd = RT5677_SPI_READ_32;
*len = 4;
} else {
cmd = RT5677_SPI_READ_BURST;
- *len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN);
+ *len = (((remain - 1) >> 3) + 1) << 3;
+ *len = min_t(u32, *len, RT5677_SPI_BURST_LEN);
}
return read ? cmd : cmd + 1;
}
@@ -103,14 +460,14 @@ static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen)
u32 word_size = min_t(u32, dstlen, 8);
for (w = 0; w < dstlen; w += word_size) {
- for (i = 0; i < word_size; i++) {
+ for (i = 0; i < word_size && i + w < dstlen; i++) {
si = w + word_size - i - 1;
dst[w + i] = si < srclen ? src[si] : 0;
}
}
}
-/* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */
+/* Read DSP address space using SPI. addr and len have to be 4-byte aligned. */
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
{
u32 offset;
@@ -126,7 +483,7 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
if (!g_spi)
return -ENODEV;
- if ((addr & 1) || (len & 1)) {
+ if ((addr & 3) || (len & 3)) {
dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len);
return -EACCES;
}
@@ -154,20 +511,21 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
status |= spi_sync(g_spi, &m);
mutex_unlock(&spi_mutex);
+
/* Copy data back to caller buffer */
- rt5677_spi_reverse(cb + offset, t[1].len, body, t[1].len);
+ rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len);
}
return status;
}
EXPORT_SYMBOL_GPL(rt5677_spi_read);
-/* Write DSP address space using SPI. addr has to be 2-byte aligned.
- * If len is not 2-byte aligned, an extra byte of zero is written at the end
+/* Write DSP address space using SPI. addr has to be 4-byte aligned.
+ * If len is not 4-byte aligned, then extra zeros are written at the end
* as padding.
*/
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
{
- u32 offset, len_with_pad = len;
+ u32 offset;
int status = 0;
struct spi_transfer t;
struct spi_message m;
@@ -180,22 +538,19 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
if (!g_spi)
return -ENODEV;
- if (addr & 1) {
+ if (addr & 3) {
dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len);
return -EACCES;
}
- if (len & 1)
- len_with_pad = len + 1;
-
memset(&t, 0, sizeof(t));
t.tx_buf = buf;
t.speed_hz = RT5677_SPI_FREQ;
spi_message_init_with_transfers(&m, &t, 1);
- for (offset = 0; offset < len_with_pad;) {
+ for (offset = 0; offset < len;) {
spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7,
- len_with_pad - offset, &t.len);
+ len - offset, &t.len);
/* Construct SPI message header */
buf[0] = spi_cmd;
@@ -223,15 +578,56 @@ int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
}
EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
+void rt5677_spi_hotword_detected(void)
+{
+ struct rt5677_dsp *rt5677_dsp;
+
+ if (!g_spi)
+ return;
+
+ rt5677_dsp = dev_get_drvdata(&g_spi->dev);
+ if (!rt5677_dsp) {
+ dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
+ return;
+ }
+
+ mutex_lock(&rt5677_dsp->dma_lock);
+ dev_info(rt5677_dsp->dev, "Hotword detected\n");
+ rt5677_dsp->new_hotword = true;
+ mutex_unlock(&rt5677_dsp->dma_lock);
+
+ schedule_delayed_work(&rt5677_dsp->copy_work, 0);
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
+
static int rt5677_spi_probe(struct spi_device *spi)
{
+ int ret;
+
g_spi = spi;
- return 0;
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &rt5677_spi_dai_component,
+ &rt5677_spi_dai, 1);
+ if (ret < 0)
+ dev_err(&spi->dev, "Failed to register component.\n");
+
+ return ret;
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5677_spi_acpi_id[] = {
+ { "10EC5677" },
+ { "RT5677AA" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
+#endif
+
static struct spi_driver rt5677_spi_driver = {
.driver = {
- .name = "rt5677",
+ .name = DRV_NAME,
+ .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
};
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
index 662db16cfb6a..088b77931727 100644
--- a/sound/soc/codecs/rt5677-spi.h
+++ b/sound/soc/codecs/rt5677-spi.h
@@ -1,19 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5677-spi.h -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT5677_SPI_H__
#define __RT5677_SPI_H__
+#if IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI)
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len);
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len);
int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw);
+void rt5677_spi_hotword_detected(void);
+#else
+static inline int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
+{
+ return -EINVAL;
+}
+static inline int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
+{
+ return -EINVAL;
+}
+static inline int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
+{
+ return -EINVAL;
+}
+static inline void rt5677_spi_hotword_detected(void){}
+#endif
#endif /* __RT5677_SPI_H__ */
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 33e290b703df..d46385249867 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -1,26 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5677.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/regmap.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/firmware.h>
-#include <linux/property.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -35,6 +36,10 @@
#define RT5677_DEVICE_ID 0x6327
+/* Register controlling boot vector */
+#define RT5677_DSP_BOOT_VECTOR 0x1801f090
+#define RT5677_MODEL_ADDR 0x5FFC9800
+
#define RT5677_PR_RANGE_BASE (0xff + 1)
#define RT5677_PR_SPACING 0x100
@@ -295,6 +300,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
@@ -305,6 +311,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_IRQ_CTRL1:
case RT5677_IRQ_CTRL2:
case RT5677_GPIO_ST:
+ case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */
+ case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */
case RT5677_DSP_INB1_SRC_CTRL4:
case RT5677_DSP_INB2_SRC_CTRL4:
case RT5677_DSP_INB3_SRC_CTRL4:
@@ -545,14 +553,14 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg)
* @rt5677: Private Data.
* @addr: Address index.
* @value: Address data.
- *
+ * @opcode: opcode value
*
* Returns 0 for success or negative error code.
*/
static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
unsigned int addr, unsigned int value, unsigned int opcode)
{
- struct snd_soc_codec *codec = rt5677->codec;
+ struct snd_soc_component *component = rt5677->component;
int ret;
mutex_lock(&rt5677->dsp_cmd_lock);
@@ -560,35 +568,35 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
addr >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr msb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
addr & 0xffff);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB,
value >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set data msb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set data msb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB,
value & 0xffff);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set data lsb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
opcode);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+ dev_err(component->dev, "Failed to set op code value: %d\n", ret);
goto err;
}
@@ -600,7 +608,7 @@ err:
/**
* rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
- * rt5677: Private Data.
+ * @rt5677: Private Data.
* @addr: Address index.
* @value: Address data.
*
@@ -610,7 +618,7 @@ err:
static int rt5677_dsp_mode_i2c_read_addr(
struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
{
- struct snd_soc_codec *codec = rt5677->codec;
+ struct snd_soc_component *component = rt5677->component;
int ret;
unsigned int msb, lsb;
@@ -619,21 +627,21 @@ static int rt5677_dsp_mode_i2c_read_addr(
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
addr >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr msb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
addr & 0xffff);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+ dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret);
goto err;
}
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
0x0002);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+ dev_err(component->dev, "Failed to set op code value: %d\n", ret);
goto err;
}
@@ -649,7 +657,7 @@ err:
/**
* rt5677_dsp_mode_i2c_write - Write register on DSP mode.
- * rt5677: Private Data.
+ * @rt5677: Private Data.
* @reg: Register index.
* @value: Register data.
*
@@ -665,7 +673,7 @@ static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
/**
* rt5677_dsp_mode_i2c_read - Read register on DSP mode.
- * @codec: SoC audio codec device.
+ * @rt5677: Private Data
* @reg: Register index.
* @value: Register data.
*
@@ -683,104 +691,274 @@ static int rt5677_dsp_mode_i2c_read(
return ret;
}
-static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
-
if (on) {
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP, RT5677_PWR_DSP);
rt5677->is_dsp_mode = true;
} else {
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP, 0x0);
rt5677->is_dsp_mode = false;
}
}
-static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- static bool activity;
- int ret;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt5677->component);
+ /* Force dapm to sync before we enable the
+ * DSP to prevent write corruption
+ */
+ snd_soc_dapm_sync(dapm);
+
+ /* DMIC1 power = enabled
+ * DMIC CLK = 256 * fs / 12
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
+ RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT);
+
+ /* I2S pre divide 2 = /6 (clk_sys2) */
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6);
+
+ /* DSP Clock = MCLK1 (bypassed PLL2) */
+ regmap_write(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_DSP_CLK_SRC_BYPASS);
+
+ /* SAD Threshold1 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f);
+ /* SAD Threshold2 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5);
+ /* SAD Sample Rate Converter = Up 6 (8K to 48K)
+ * SAD Output Sample Rate = Same as I2S
+ * SAD Threshold3
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4,
+ RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK |
+ RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT);
+ /* Minimum frame level within a pre-determined duration = 32 frames
+ * Bypass ADPCM Encoder/Decoder = Bypass ADPCM
+ * Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable
+ * SAD Buffer Over-Writing = enable
+ * SAD Buffer Pop Mode Control = disable
+ * SAD Buffer Push Mode Control = enable
+ * SAD Detector Control = enable
+ * SAD Function Control = enable
+ * SAD Function Reset = normal
+ */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1,
+ RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE |
+ RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH |
+ RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC |
+ RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT);
+
+ /* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it
+ * is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save
+ * power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack
+ * Detection" for more info.
+ */
+
+ /* Private register, no doc */
+ regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4,
+ 0x0f00, 0x0100);
+
+ /* LDO2 output = 1.2V
+ * LDO1 output = 1.2V (LDO_IN = 1.8V)
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
+ 5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT);
+
+ /* Codec core power = power on
+ * LDO1 power = power on
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1);
+
+ /* Isolation for DCVDD4 = normal (set during probe)
+ * Isolation for DCVDD2 = normal (set during probe)
+ * Isolation for DSP = normal
+ * Isolation for Band 0~7 = disable
+ * Isolation for InBound 4~10 and OutBound 4~10 = disable
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
+ RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO |
+ RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO |
+ RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO |
+ RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO |
+ RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO |
+ RT5677_PWR_MLT_ISO);
+
+ /* System Band 0~7 = power on
+ * InBound 4~10 and OutBound 4~10 = power on
+ * DSP = power on
+ * DSP CPU = stop (will be set to "run" after firmware loaded)
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_SR7 | RT5677_PWR_SR6 |
+ RT5677_PWR_SR5 | RT5677_PWR_SR4 |
+ RT5677_PWR_SR3 | RT5677_PWR_SR2 |
+ RT5677_PWR_SR1 | RT5677_PWR_SR0 |
+ RT5677_PWR_MLT | RT5677_PWR_DSP |
+ RT5677_PWR_DSP_CPU);
+
+ return 0;
+}
+
+static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf,
+ unsigned int len)
+{
+ struct snd_soc_component *component = rt5677->component;
+ Elf32_Ehdr *elf_hdr;
+ Elf32_Phdr *pr_hdr;
+ Elf32_Half i;
+ int ret = 0;
+
+ if (!buf || (len < sizeof(Elf32_Ehdr)))
+ return -ENOMEM;
+
+ elf_hdr = (Elf32_Ehdr *)buf;
+#ifndef EM_XTENSA
+#define EM_XTENSA 94
+#endif
+ if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1))
+ dev_err(component->dev, "Wrong ELF header prefix\n");
+ if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr))
+ dev_err(component->dev, "Wrong ELF header size\n");
+ if (elf_hdr->e_machine != EM_XTENSA)
+ dev_err(component->dev, "Wrong DSP code file\n");
+
+ if (len < elf_hdr->e_phoff)
+ return -ENOMEM;
+ pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff);
+ for (i = 0; i < elf_hdr->e_phnum; i++) {
+ /* TODO: handle p_memsz != p_filesz */
+ if (pr_hdr->p_paddr && pr_hdr->p_filesz) {
+ dev_info(component->dev, "Load 0x%x bytes to 0x%x\n",
+ pr_hdr->p_filesz, pr_hdr->p_paddr);
+
+ ret = rt5677_spi_write(pr_hdr->p_paddr,
+ buf + pr_hdr->p_offset,
+ pr_hdr->p_filesz);
+ if (ret)
+ dev_err(component->dev, "Load firmware failed %d\n",
+ ret);
+ }
+ pr_hdr++;
+ }
+ return ret;
+}
+
+static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677)
+{
+ const struct firmware *fwp;
+ struct device *dev = rt5677->component->dev;
+ int ret = 0;
+
+ /* Load dsp firmware from rt5677_elf_vad file */
+ ret = request_firmware(&fwp, "rt5677_elf_vad", dev);
+ if (ret) {
+ dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret);
+ return ret;
+ }
+ dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size);
+
+ ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size);
+ release_firmware(fwp);
+ return ret;
+}
+
+static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on)
+{
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ rt5677->dsp_vad_en_request = on;
+ rt5677->dsp_vad_en = on;
if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
return -ENXIO;
- if (on && !activity) {
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ return 0;
+}
+
+static void rt5677_dsp_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, dsp_work.work);
+ static bool activity;
+ bool enable = rt5677->dsp_vad_en;
+ int i, val;
+
+
+ dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n",
+ enable, activity);
+
+ if (enable && !activity) {
activity = true;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Before a hotword is detected, GPIO1 pin is configured as IRQ
+ * output so that jack detect works. When a hotword is detected,
+ * the DSP firmware configures the GPIO1 pin as GPIO1 and
+ * drives a 1. rt5677_irq() is called after a rising edge on
+ * the GPIO1 pin, due to either jack detect event or hotword
+ * event, or both. All possible events are checked and handled
+ * in rt5677_irq() where GPIO1 pin is configured back to IRQ
+ * output if a hotword is detected.
+ */
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
- regmap_update_bits(rt5677->regmap,
- RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
- RT5677_LDO1_SEL_MASK, 0x0);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
- RT5677_PWR_LDO1, RT5677_PWR_LDO1);
- 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;
+ rt5677_set_vad_source(rt5677);
+ rt5677_set_dsp_mode(rt5677, true);
+
+#define RT5677_BOOT_RETRY 20
+ for (i = 0; i < RT5677_BOOT_RETRY; i++) {
+ regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val);
+ if (val == 0x3ff)
+ break;
+ udelay(500);
}
- 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_write_firmware(0x50000000, rt5677->fw1);
- release_firmware(rt5677->fw1);
+ if (i == RT5677_BOOT_RETRY && val != 0x3ff) {
+ dev_err(rt5677->component->dev, "DSP Boot Timed Out!");
+ return;
}
- ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
- codec->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x60000000, rt5677->fw2);
- release_firmware(rt5677->fw2);
- }
+ /* Boot the firmware from IRAM instead of SRAM0. */
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0019, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+ rt5677_load_dsp_from_file(rt5677);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_cache_only(rt5677->regmap, true);
- } else if (!on && activity) {
+ /* Set DSP CPU to Run */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, 0x0);
+ } else if (!enable && activity) {
activity = false;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Don't turn off the DSP while handling irqs */
+ mutex_lock(&rt5677->irq_lock);
+ /* Set DSP CPU to Stop */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
- rt5677_set_dsp_mode(codec, false);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+ rt5677_set_dsp_mode(rt5677, false);
- regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ /* Disable and clear VAD interrupt */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_mark_dirty(rt5677->regmap);
- regcache_sync(rt5677->regmap);
- }
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
- return 0;
+ mutex_unlock(&rt5677->irq_lock);
+ }
}
-static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
-static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
@@ -802,7 +980,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+ ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request;
return 0;
}
@@ -811,13 +989,8 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
-
- rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
- rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
+ rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]);
return 0;
}
@@ -833,13 +1006,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv),
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
@@ -911,15 +1084,15 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
int idx, rate;
rate = rt5677->sysclk / rl6231_get_pre_div(rt5677->regmap,
RT5677_CLK_TREE_CTRL1, RT5677_I2S_PD1_SFT);
idx = rl6231_calc_dmic_clk(rate);
if (idx < 0)
- dev_err(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
RT5677_DMIC_CLK_MASK, idx << RT5677_DMIC_CLK_SFT);
@@ -929,8 +1102,8 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int val;
regmap_read(rt5677->regmap, RT5677_GLB_CLK1, &val);
@@ -944,8 +1117,8 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int reg, shift, val;
if (source->reg == RT5677_ASRC_1) {
@@ -1027,8 +1200,8 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (rt5677->sysclk > rt5677->lrck[RT5677_AIF1] * 384)
return 1;
@@ -1038,7 +1211,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
/**
* rt5677_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -1050,10 +1223,10 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
* set of filters specified by the mask. And the codec driver will turn on ASRC
* for these filters if ASRC is selected as their clock source.
*/
-int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int asrc3_mask = 0, asrc3_value = 0;
unsigned int asrc4_mask = 0, asrc4_value = 0;
unsigned int asrc5_mask = 0, asrc5_value = 0;
@@ -1232,8 +1405,8 @@ EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src);
static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int asrc_setting;
switch (source->shift) {
@@ -1241,60 +1414,46 @@ static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source,
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >>
RT5677_AD_STO1_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 10:
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >>
RT5677_AD_STO2_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 9:
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >>
RT5677_AD_STO3_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 8:
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >>
RT5677_AD_STO4_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 7:
regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >>
RT5677_AD_MONOL_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 6:
regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >>
RT5677_AD_MONOR_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
default:
- break;
+ return 0;
}
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+
return 0;
}
@@ -2407,8 +2566,8 @@ static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -2431,8 +2590,8 @@ static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -2455,8 +2614,8 @@ static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -2477,8 +2636,8 @@ static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -2499,8 +2658,8 @@ static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -2526,8 +2685,8 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int value;
switch (event) {
@@ -2549,8 +2708,8 @@ static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int value;
switch (event) {
@@ -2572,12 +2731,13 @@ static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON &&
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON &&
!rt5677->is_vref_slow) {
mdelay(20);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -2622,7 +2782,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5677_ASRC_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5677_ASRC_1, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("I2S4 ASRC", 1, RT5677_ASRC_1, 3, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0,
+ rt5677_filter_power_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY_S("DAC MONO2 L ASRC", 1, RT5677_ASRC_2, 13, 0, NULL,
0),
SND_SOC_DAPM_SUPPLY_S("DAC MONO2 R ASRC", 1, RT5677_ASRC_2, 12, 0, NULL,
@@ -3021,6 +3182,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0),
/* Sidetone Mux */
SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
@@ -3555,11 +3717,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "SLBTX", NULL, "SLB ADC3 Mux" },
{ "SLBTX", NULL, "SLB ADC4 Mux" },
+ { "DSPTX", NULL, "IB01 Bypass Mux" },
+
{ "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" },
{ "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" },
{ "IB01 Mux", "SLB DAC 01", "SLB DAC01" },
{ "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
- { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
+ /* The IB01 Mux controls the source for InBound0 and InBound1.
+ * When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to
+ * InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for
+ * hotwording. "DAC1 FS" is not used currently.
+ *
+ * Creating a common widget node for "VAD ADC" + "DAC1 FS" and
+ * connecting the common widget to IB01 Mux causes the issue where
+ * there is an active path going from system playback -> "DAC1 FS" ->
+ * IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses
+ * DAPM. Therefore "DAC1 FS" is ignored for now.
+ */
+ { "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" },
{ "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
{ "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
@@ -4112,21 +4287,21 @@ static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = {
static int rt5677_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, val_clk, mask_clk;
int pre_div, bclk_ms, frame_size;
rt5677->lrck[dai->id] = params_rate(params);
pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
+ dev_err(component->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
rt5677->sysclk, rt5677->lrck[dai->id]);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
- dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
@@ -4197,15 +4372,15 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream,
static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5677->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5677_I2S_MS_S;
rt5677->master[dai->id] = 0;
break;
@@ -4271,8 +4446,8 @@ static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
if (freq == rt5677->sysclk && clk_id == rt5677->sysclk_src)
@@ -4289,7 +4464,7 @@ static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5677_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
@@ -4324,8 +4499,8 @@ static int rt5677_pll_calc(const unsigned int freq_in,
static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code;
int ret;
@@ -4334,7 +4509,7 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5677->pll_in = 0;
rt5677->pll_out = 0;
@@ -4374,25 +4549,25 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rt5677_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
- dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "m_bypass=%d m=%d n=%d k=%d\n",
+ dev_dbg(component->dev, "m_bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
regmap_write(rt5677->regmap, RT5677_PLL1_CTRL1,
pll_code.n_code << RT5677_PLL_N_SFT | pll_code.k_code);
regmap_write(rt5677->regmap, RT5677_PLL1_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT |
- pll_code.m_bp << RT5677_PLL_M_BP_SFT);
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT) |
+ (pll_code.m_bp << RT5677_PLL_M_BP_SFT));
rt5677->pll_in = freq_in;
rt5677->pll_out = freq_out;
@@ -4404,8 +4579,8 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
unsigned int val = 0, slot_width_25 = 0;
if (rx_mask || tx_mask)
@@ -4432,6 +4607,7 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 25:
slot_width_25 = 0x8080;
+ fallthrough;
case 24:
val |= (2 << 8);
break;
@@ -4463,22 +4639,24 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-static int rt5677_set_bias_level(struct snd_soc_codec *codec,
+static int rt5677_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ enum snd_soc_bias_level prev_bias = snd_soc_dapm_get_bias_level(dapm);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
- rt5677_set_dsp_vad(codec, false);
+ if (prev_bias == SND_SOC_BIAS_STANDBY) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
- 0x0055);
+ 5 << RT5677_LDO1_SEL_SFT |
+ 5 << RT5677_LDO2_SEL_SFT);
regmap_update_bits(rt5677->regmap,
RT5677_PR_BASE + RT5677_BIAS_CUR4,
0x0f00, 0x0f00);
@@ -4497,19 +4675,37 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
+ if (prev_bias == SND_SOC_BIAS_OFF &&
+ rt5677->dsp_vad_en_request) {
+ /* Re-enable the DSP if it was turned off at suspend */
+ rt5677->dsp_vad_en = true;
+ /* The delay is to wait for MCLK */
+ schedule_delayed_work(&rt5677->dsp_work,
+ msecs_to_jiffies(1000));
+ }
break;
case SND_SOC_BIAS_OFF:
+ flush_delayed_work(&rt5677->dsp_work);
+ if (rt5677->is_dsp_mode) {
+ /* Turn off the DSP before suspend */
+ rt5677->dsp_vad_en = false;
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ flush_delayed_work(&rt5677->dsp_work);
+ }
+
regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0);
regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000);
- regmap_write(rt5677->regmap, RT5677_PWR_DIG2, 0x0000);
- regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, 0x0022);
- regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000);
+ regmap_write(rt5677->regmap, RT5677_PWR_ANLG1,
+ 2 << RT5677_LDO1_SEL_SFT |
+ 2 << RT5677_LDO2_SEL_SFT);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE, 0);
regmap_update_bits(rt5677->regmap,
RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
if (rt5677->dsp_vad_en)
- rt5677_set_dsp_vad(codec, true);
+ rt5677_set_dsp_vad(component, true);
break;
default:
@@ -4519,60 +4715,40 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-#ifdef CONFIG_GPIOLIB
-static inline struct rt5677_priv *gpio_to_rt5677(struct gpio_chip *chip)
+static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, int m, int v)
{
- return container_of(chip, struct rt5677_priv, gpio_chip);
+ unsigned int bank = offset / 5;
+ unsigned int shift = (offset % 5) * 3;
+ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2;
+
+ return regmap_update_bits(rt5677->regmap, reg, m << shift, v << shift);
}
-static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+#ifdef CONFIG_GPIOLIB
+static int rt5677_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
-
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT);
- break;
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
+ int m = RT5677_GPIOx_OUT_MASK;
- default:
- break;
- }
+ return rt5677_update_gpio_bits(rt5677, offset, m, level);
}
static int rt5677_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
-
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x3 << (offset * 3 + 1),
- (0x2 | !!value) << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK,
- RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT);
- break;
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
+ int m = RT5677_GPIOx_DIR_MASK | RT5677_GPIOx_OUT_MASK;
+ int v = RT5677_GPIOx_DIR_OUT | level;
- default:
- break;
- }
-
- return 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
int value, ret;
ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value);
@@ -4584,27 +4760,15 @@ static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int m = RT5677_GPIOx_DIR_MASK;
+ int v = RT5677_GPIOx_DIR_IN;
- 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;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
-/** Configures the gpio as
+/*
+ * Configures the GPIO as
* 0 - floating
* 1 - pull down
* 2 - pull up
@@ -4638,46 +4802,37 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
- struct regmap_irq_chip_data *data = rt5677->irq_data;
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
int irq;
- if (offset >= RT5677_GPIO1 && offset <= RT5677_GPIO3) {
- if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
- (rt5677->pdata.jd1_gpio == 2 &&
- offset == RT5677_GPIO2) ||
- (rt5677->pdata.jd1_gpio == 3 &&
- offset == RT5677_GPIO3)) {
- irq = RT5677_IRQ_JD1;
- } else {
- return -ENXIO;
- }
- }
-
- if (offset >= RT5677_GPIO4 && offset <= RT5677_GPIO6) {
- if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) ||
- (rt5677->pdata.jd2_gpio == 2 &&
- offset == RT5677_GPIO5) ||
- (rt5677->pdata.jd2_gpio == 3 &&
- offset == RT5677_GPIO6)) {
- irq = RT5677_IRQ_JD2;
- } else if ((rt5677->pdata.jd3_gpio == 1 &&
- offset == RT5677_GPIO4) ||
- (rt5677->pdata.jd3_gpio == 2 &&
- offset == RT5677_GPIO5) ||
- (rt5677->pdata.jd3_gpio == 3 &&
- offset == RT5677_GPIO6)) {
- irq = RT5677_IRQ_JD3;
- } else {
- return -ENXIO;
- }
+ if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
+ (rt5677->pdata.jd1_gpio == 2 &&
+ offset == RT5677_GPIO2) ||
+ (rt5677->pdata.jd1_gpio == 3 &&
+ offset == RT5677_GPIO3)) {
+ irq = RT5677_IRQ_JD1;
+ } else if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) ||
+ (rt5677->pdata.jd2_gpio == 2 &&
+ offset == RT5677_GPIO5) ||
+ (rt5677->pdata.jd2_gpio == 3 &&
+ offset == RT5677_GPIO6)) {
+ irq = RT5677_IRQ_JD2;
+ } else if ((rt5677->pdata.jd3_gpio == 1 &&
+ offset == RT5677_GPIO4) ||
+ (rt5677->pdata.jd3_gpio == 2 &&
+ offset == RT5677_GPIO5) ||
+ (rt5677->pdata.jd3_gpio == 3 &&
+ offset == RT5677_GPIO6)) {
+ irq = RT5677_IRQ_JD3;
+ } else {
+ return -ENXIO;
}
- return regmap_irq_get_virq(data, irq);
+ return irq_create_mapping(rt5677->domain, irq);
}
-static struct gpio_chip rt5677_template_chip = {
- .label = "rt5677",
+static const struct gpio_chip rt5677_template_chip = {
+ .label = RT5677_DRV_NAME,
.owner = THIS_MODULE,
.direction_output = rt5677_gpio_direction_out,
.set = rt5677_gpio_set,
@@ -4697,7 +4852,7 @@ static void rt5677_init_gpio(struct i2c_client *i2c)
rt5677->gpio_chip.parent = &i2c->dev;
rt5677->gpio_chip.base = -1;
- ret = gpiochip_add(&rt5677->gpio_chip);
+ ret = gpiochip_add_data(&rt5677->gpio_chip, rt5677);
if (ret != 0)
dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
}
@@ -4723,13 +4878,13 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
}
#endif
-static int rt5677_probe(struct snd_soc_codec *codec)
+static int rt5677_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
int i;
- rt5677->codec = codec;
+ rt5677->component = component;
if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
snd_soc_dapm_add_routes(dapm,
@@ -4741,60 +4896,42 @@ static int rt5677_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5677_dmic2_clk_1));
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
- regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ ~RT5677_IRQ_DEBOUNCE_SEL_MASK, 0x0020);
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
+ RT5677_PWR_SLIM_ISO | RT5677_PWR_CORE_ISO);
for (i = 0; i < RT5677_GPIO_NUM; i++)
rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
- if (rt5677->irq_data) {
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
- 0x8000);
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
- 0x0008);
-
- if (rt5677->pdata.jd1_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD1_MASK,
- rt5677->pdata.jd1_gpio <<
- RT5677_SEL_GPIO_JD1_SFT);
-
- if (rt5677->pdata.jd2_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD2_MASK,
- rt5677->pdata.jd2_gpio <<
- RT5677_SEL_GPIO_JD2_SFT);
-
- if (rt5677->pdata.jd3_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD3_MASK,
- rt5677->pdata.jd3_gpio <<
- RT5677_SEL_GPIO_JD3_SFT);
- }
-
mutex_init(&rt5677->dsp_cmd_lock);
mutex_init(&rt5677->dsp_pri_lock);
return 0;
}
-static int rt5677_remove(struct snd_soc_codec *codec)
+static void rt5677_remove(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&rt5677->dsp_work);
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
-
- return 0;
}
#ifdef CONFIG_PM
-static int rt5677_suspend(struct snd_soc_codec *codec)
+static int rt5677_suspend(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+
+ if (rt5677->irq) {
+ cancel_delayed_work_sync(&rt5677->resume_irq_check);
+ disable_irq(rt5677->irq);
+ }
if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true);
@@ -4807,9 +4944,9 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5677_resume(struct snd_soc_codec *codec)
+static int rt5677_resume(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (!rt5677->dsp_vad_en) {
rt5677->pll_src = 0;
@@ -4824,6 +4961,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
regcache_sync(rt5677->regmap);
}
+ if (rt5677->irq) {
+ enable_irq(rt5677->irq);
+ schedule_delayed_work(&rt5677->resume_irq_check, 0);
+ }
+
return 0;
}
#else
@@ -4888,6 +5030,11 @@ static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.set_tdm_slot = rt5677_set_tdm_slot,
};
+static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = {
+ .set_sysclk = rt5677_set_dai_sysclk,
+ .set_pll = rt5677_set_dai_pll,
+};
+
static struct snd_soc_dai_driver rt5677_dai[] = {
{
.name = "rt5677-aif1",
@@ -4984,21 +5131,35 @@ static struct snd_soc_dai_driver rt5677_dai[] = {
},
.ops = &rt5677_aif_dai_ops,
},
+ {
+ .name = "rt5677-dspbuffer",
+ .id = RT5677_DSPBUFF,
+ .capture = {
+ .stream_name = "DSP Buffer",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &rt5677_dsp_dai_ops,
+ },
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
- .probe = rt5677_probe,
- .remove = rt5677_remove,
- .suspend = rt5677_suspend,
- .resume = rt5677_resume,
- .set_bias_level = rt5677_set_bias_level,
- .idle_bias_off = true,
- .controls = rt5677_snd_controls,
- .num_controls = ARRAY_SIZE(rt5677_snd_controls),
- .dapm_widgets = rt5677_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets),
- .dapm_routes = rt5677_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_rt5677 = {
+ .name = RT5677_DRV_NAME,
+ .probe = rt5677_probe,
+ .remove = rt5677_remove,
+ .suspend = rt5677_suspend,
+ .resume = rt5677_resume,
+ .set_bias_level = rt5677_set_bias_level,
+ .controls = rt5677_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5677_snd_controls),
+ .dapm_widgets = rt5677_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets),
+ .dapm_routes = rt5677_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config rt5677_regmap_physical = {
@@ -5034,68 +5195,277 @@ static const struct regmap_config rt5677_regmap = {
.num_ranges = ARRAY_SIZE(rt5677_ranges),
};
-static const struct i2c_device_id rt5677_i2c_id[] = {
- { "rt5677", RT5677 },
- { "rt5676", RT5676 },
+static const struct of_device_id rt5677_of_match[] = {
+ { .compatible = "realtek,rt5677", .data = (const void *)RT5677 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+MODULE_DEVICE_TABLE(of, rt5677_of_match);
+
+static const struct acpi_device_id rt5677_acpi_match[] = {
+ { "10EC5677", RT5677 },
+ { "RT5677CE", RT5677 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
struct device *dev)
{
- rt5677->pdata.in1_diff = device_property_read_bool(dev,
- "realtek,in1-differential");
- rt5677->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev,
- "realtek,lout1-differential");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev,
- "realtek,lout2-differential");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev,
- "realtek,lout3-differential");
+ u32 val;
+
+ rt5677->pdata.in1_diff =
+ device_property_read_bool(dev, "IN1") ||
+ device_property_read_bool(dev, "realtek,in1-differential");
+
+ rt5677->pdata.in2_diff =
+ device_property_read_bool(dev, "IN2") ||
+ device_property_read_bool(dev, "realtek,in2-differential");
+
+ rt5677->pdata.lout1_diff =
+ device_property_read_bool(dev, "OUT1") ||
+ device_property_read_bool(dev, "realtek,lout1-differential");
+
+ rt5677->pdata.lout2_diff =
+ device_property_read_bool(dev, "OUT2") ||
+ device_property_read_bool(dev, "realtek,lout2-differential");
+
+ rt5677->pdata.lout3_diff =
+ device_property_read_bool(dev, "OUT3") ||
+ device_property_read_bool(dev, "realtek,lout3-differential");
device_property_read_u8_array(dev, "realtek,gpio-config",
- rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
-
- device_property_read_u32(dev, "realtek,jd1-gpio",
- &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "realtek,jd2-gpio",
- &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "realtek,jd3-gpio",
- &rt5677->pdata.jd3_gpio);
+ rt5677->pdata.gpio_config,
+ RT5677_GPIO_NUM);
+
+ if (!device_property_read_u32(dev, "DCLK", &val) ||
+ !device_property_read_u32(dev, "realtek,dmic2_clk_pin", &val))
+ rt5677->pdata.dmic2_clk_pin = val;
+
+ if (!device_property_read_u32(dev, "JD1", &val) ||
+ !device_property_read_u32(dev, "realtek,jd1-gpio", &val))
+ rt5677->pdata.jd1_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD2", &val) ||
+ !device_property_read_u32(dev, "realtek,jd2-gpio", &val))
+ rt5677->pdata.jd2_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD3", &val) ||
+ !device_property_read_u32(dev, "realtek,jd3-gpio", &val))
+ rt5677->pdata.jd3_gpio = val;
}
-static struct regmap_irq rt5677_irqs[] = {
+struct rt5677_irq_desc {
+ unsigned int enable_mask;
+ unsigned int status_mask;
+ unsigned int polarity_mask;
+};
+
+static const struct rt5677_irq_desc rt5677_irq_descs[] = {
[RT5677_IRQ_JD1] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD1,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD1,
+ .status_mask = RT5677_STA_GPIO_JD1,
+ .polarity_mask = RT5677_INV_GPIO_JD1,
},
[RT5677_IRQ_JD2] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD2,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD2,
+ .status_mask = RT5677_STA_GPIO_JD2,
+ .polarity_mask = RT5677_INV_GPIO_JD2,
},
[RT5677_IRQ_JD3] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD3,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD3,
+ .status_mask = RT5677_STA_GPIO_JD3,
+ .polarity_mask = RT5677_INV_GPIO_JD3,
},
};
-static struct regmap_irq_chip rt5677_irq_chip = {
- .name = "rt5677",
- .irqs = rt5677_irqs,
- .num_irqs = ARRAY_SIZE(rt5677_irqs),
+static bool rt5677_check_hotword(struct rt5677_priv *rt5677)
+{
+ int reg_gpio;
+
+ if (!rt5677->is_dsp_mode)
+ return false;
+
+ if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, &reg_gpio))
+ return false;
- .num_regs = 1,
- .status_base = RT5677_IRQ_CTRL1,
- .mask_base = RT5677_IRQ_CTRL1,
- .mask_invert = 1,
+ /* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */
+ if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ)
+ return false;
+
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ rt5677_spi_hotword_detected();
+ return true;
+}
+
+static irqreturn_t rt5677_irq(int unused, void *data)
+{
+ struct rt5677_priv *rt5677 = data;
+ int ret, loop, i, reg_irq, virq;
+ bool irq_fired = false;
+
+ mutex_lock(&rt5677->irq_lock);
+
+ /*
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
+ */
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, &reg_irq);
+ if (ret) {
+ dev_err(rt5677->dev, "failed reading IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ irq_fired = true;
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+
+ /* Clear the interrupt by flipping the polarity
+ * of the interrupt source line that fired
+ */
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ }
+ }
+
+ /* Exit the loop only when we know for sure that GPIO1 pin
+ * was low at some point since irq_lock was acquired. Any event
+ * after that point creates a rising edge that triggers another
+ * call to rt5677_irq().
+ */
+ if (!irq_fired && !rt5677_check_hotword(rt5677))
+ goto exit;
+
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(rt5677->dev, "failed updating IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ }
+exit:
+ WARN_ON_ONCE(loop == 20);
+ mutex_unlock(&rt5677->irq_lock);
+ if (irq_fired)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void rt5677_resume_irq_check(struct work_struct *work)
+{
+ int i, virq;
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, resume_irq_check.work);
+
+ /* This is needed to check and clear the interrupt status register
+ * at resume. If the headset is plugged/unplugged when the device is
+ * fully suspended, there won't be a rising edge at resume to trigger
+ * the interrupt. Without this, we miss the next unplug/plug event.
+ */
+ rt5677_irq(0, rt5677);
+
+ /* Call all enabled jack detect irq handlers again. This is needed in
+ * addition to the above check for a corner case caused by jack gpio
+ * debounce. After codec irq is disabled at suspend, the delayed work
+ * scheduled by soc-jack may run and read wrong jack gpio values, since
+ * the regmap is in cache only mode. At resume, there is no irq because
+ * rt5677_irq has already ran and cleared the irq status at suspend.
+ * Without this explicit check, unplug the headset right after suspend
+ * starts, then after resume the headset is still shown as plugged in.
+ */
+ mutex_lock(&rt5677->irq_lock);
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
+ }
+ mutex_unlock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_bus_lock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ // Set the enable/disable bits for the jack detect IRQs.
+ regmap_update_bits(rt5677->regmap, RT5677_IRQ_CTRL1,
+ RT5677_EN_IRQ_GPIO_JD1 | RT5677_EN_IRQ_GPIO_JD2 |
+ RT5677_EN_IRQ_GPIO_JD3, rt5677->irq_en);
+ mutex_unlock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_enable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en |= rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static void rt5677_irq_disable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en &= ~rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static struct irq_chip rt5677_irq_chip = {
+ .name = "rt5677_irq_chip",
+ .irq_bus_lock = rt5677_irq_bus_lock,
+ .irq_bus_sync_unlock = rt5677_irq_bus_sync_unlock,
+ .irq_disable = rt5677_irq_disable,
+ .irq_enable = rt5677_irq_enable,
+};
+
+static int rt5677_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct rt5677_priv *rt5677 = h->host_data;
+
+ irq_set_chip_data(virq, rt5677);
+ irq_set_chip(virq, &rt5677_irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+ return 0;
+}
+
+
+static const struct irq_domain_ops rt5677_domain_ops = {
+ .map = rt5677_irq_map,
+ .xlate = irq_domain_xlate_twocell,
};
static int rt5677_init_irq(struct i2c_client *i2c)
{
int ret;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ unsigned int jd_mask = 0, jd_val = 0;
if (!rt5677->pdata.jd1_gpio &&
!rt5677->pdata.jd2_gpio &&
@@ -5107,30 +5477,61 @@ static int rt5677_init_irq(struct i2c_client *i2c)
return -EINVAL;
}
- ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &rt5677_irq_chip, &rt5677->irq_data);
+ mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
+ /*
+ * Select RC as the debounce clock so that GPIO works even when
+ * MCLK is gated which happens when there is no audio stream
+ * (SND_SOC_BIAS_OFF).
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ RT5677_IRQ_DEBOUNCE_SEL_MASK,
+ RT5677_IRQ_DEBOUNCE_SEL_RC);
+ /* Enable auto power on RC when GPIO states are changed */
+ regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL1, 0xff, 0xff);
+
+ /* Select and enable jack detection sources per platform data */
+ if (rt5677->pdata.jd1_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD1_MASK;
+ jd_val |= rt5677->pdata.jd1_gpio << RT5677_SEL_GPIO_JD1_SFT;
}
+ if (rt5677->pdata.jd2_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD2_MASK;
+ jd_val |= rt5677->pdata.jd2_gpio << RT5677_SEL_GPIO_JD2_SFT;
+ }
+ if (rt5677->pdata.jd3_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD3_MASK;
+ jd_val |= rt5677->pdata.jd3_gpio << RT5677_SEL_GPIO_JD3_SFT;
+ }
+ regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, jd_mask, jd_val);
- return 0;
-}
+ /* Set GPIO1 pin to be IRQ output */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
-static void rt5677_free_irq(struct i2c_client *i2c)
-{
- struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ /* Ready to listen for interrupts */
+ rt5677->domain = irq_domain_create_linear(dev_fwnode(&i2c->dev),
+ RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677);
+ if (!rt5677->domain) {
+ dev_err(&i2c->dev, "Failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
- if (rt5677->irq_data)
- regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5677_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5677", rt5677);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+
+ rt5677->irq = i2c->irq;
+
+ return ret;
}
-static int rt5677_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5677_i2c_probe(struct i2c_client *i2c)
{
- struct rt5677_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct device *dev = &i2c->dev;
struct rt5677_priv *rt5677;
int ret;
unsigned int val;
@@ -5140,14 +5541,16 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
if (rt5677 == NULL)
return -ENOMEM;
+ rt5677->dev = &i2c->dev;
+ rt5677->set_dsp_vad = rt5677_set_dsp_vad;
+ INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work);
i2c_set_clientdata(i2c, rt5677);
- rt5677->type = id->driver_data;
+ rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev);
+ if (rt5677->type == 0)
+ return -EINVAL;
- if (pdata)
- rt5677->pdata = *pdata;
- else
- rt5677_read_device_properties(rt5677, &i2c->dev);
+ rt5677_read_device_properties(rt5677, &i2c->dev);
/* pow-ldo2 and reset are optional. The codec pins may be statically
* connected on the board without gpios. If the gpio device property
@@ -5231,9 +5634,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
RT5677_GPIO5_FUNC_MASK,
RT5677_GPIO5_FUNC_DMIC);
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- RT5677_GPIO5_DIR_MASK,
- RT5677_GPIO5_DIR_OUT);
+ rt5677_update_gpio_bits(rt5677, RT5677_GPIO5,
+ RT5677_GPIOx_DIR_MASK,
+ RT5677_GPIOx_DIR_OUT);
}
if (rt5677->pdata.micbias1_vdd_3v3)
@@ -5242,31 +5645,33 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
RT5677_MICBIAS1_CTRL_VDD_3_3V);
rt5677_init_gpio(i2c);
- rt5677_init_irq(i2c);
+ ret = rt5677_init_irq(i2c);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to initialize irq: %d\n", ret);
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5677,
rt5677_dai, ARRAY_SIZE(rt5677_dai));
}
-static int rt5677_i2c_remove(struct i2c_client *i2c)
+static void rt5677_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
- rt5677_free_irq(i2c);
rt5677_free_gpio(i2c);
-
- return 0;
}
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
- .name = "rt5677",
+ .name = RT5677_DRV_NAME,
+ .of_match_table = rt5677_of_match,
+ .acpi_match_table = rt5677_acpi_match,
},
- .probe = rt5677_i2c_probe,
+ .probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
- .id_table = rt5677_i2c_id,
};
module_i2c_driver(rt5677_i2c_driver);
MODULE_DESCRIPTION("ASoC RT5677 driver");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");
+
+MODULE_FIRMWARE("rt5677_elf_vad");
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index d46855a42c40..d67ebae067d9 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1,18 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5677.h -- RT5677 ALSA SoC audio driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __RT5677_H__
#define __RT5677_H__
-#include <sound/rt5677.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
@@ -1340,6 +1336,8 @@
#define RT5677_PLL_M_SFT 12
#define RT5677_PLL_M_BP (0x1 << 11)
#define RT5677_PLL_M_BP_SFT 11
+#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1)
+#define RT5677_PLL_UPDATE_PLL1_SFT 1
/* Global Clock Control 1 (0x80) */
#define RT5677_SCLK_SRC_MASK (0x3 << 14)
@@ -1457,9 +1455,37 @@
#define RT5677_I2S4_CLK_SEL_MASK (0xf)
#define RT5677_I2S4_CLK_SEL_SFT 0
+/* VAD Function Control 1 (0x9c) */
+#define RT5677_VAD_MIN_DUR_MASK (0x3 << 13)
+#define RT5677_VAD_MIN_DUR_SFT 13
+#define RT5677_VAD_ADPCM_BYPASS (1 << 10)
+#define RT5677_VAD_ADPCM_BYPASS_BIT 10
+#define RT5677_VAD_FG2ENC (1 << 9)
+#define RT5677_VAD_FG2ENC_BIT 9
+#define RT5677_VAD_BUF_OW (1 << 8)
+#define RT5677_VAD_BUF_OW_BIT 8
+#define RT5677_VAD_CLR_FLAG (1 << 7)
+#define RT5677_VAD_CLR_FLAG_BIT 7
+#define RT5677_VAD_BUF_POP (1 << 6)
+#define RT5677_VAD_BUF_POP_BIT 6
+#define RT5677_VAD_BUF_PUSH (1 << 5)
+#define RT5677_VAD_BUF_PUSH_BIT 5
+#define RT5677_VAD_DET_ENABLE (1 << 4)
+#define RT5677_VAD_DET_ENABLE_BIT 4
+#define RT5677_VAD_FUNC_ENABLE (1 << 3)
+#define RT5677_VAD_FUNC_ENABLE_BIT 3
+#define RT5677_VAD_FUNC_RESET (1 << 2)
+#define RT5677_VAD_FUNC_RESET_BIT 2
+
/* VAD Function Control 4 (0x9f) */
-#define RT5677_VAD_SRC_MASK (0x7 << 8)
+#define RT5677_VAD_OUT_SRC_RATE_MASK (0x1 << 11)
+#define RT5677_VAD_OUT_SRC_RATE_SFT 11
+#define RT5677_VAD_OUT_SRC_MASK (0x1 << 10)
+#define RT5677_VAD_OUT_SRC_SFT 10
+#define RT5677_VAD_SRC_MASK (0x3 << 8)
#define RT5677_VAD_SRC_SFT 8
+#define RT5677_VAD_LV_DIFF_MASK (0xff << 0)
+#define RT5677_VAD_LV_DIFF_SFT 0
/* DSP InBound Control (0xa3) */
#define RT5677_IB01_SRC_MASK (0x7 << 12)
@@ -1561,81 +1587,25 @@
#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13)
#define RT5677_FUNC_MODE_JTAG (0x1 << 13)
-/* GPIO Control 2 (0xc1) */
-#define RT5677_GPIO5_DIR_MASK (0x1 << 14)
-#define RT5677_GPIO5_DIR_SFT 14
-#define RT5677_GPIO5_DIR_IN (0x0 << 14)
-#define RT5677_GPIO5_DIR_OUT (0x1 << 14)
-#define RT5677_GPIO5_OUT_MASK (0x1 << 13)
-#define RT5677_GPIO5_OUT_SFT 13
-#define RT5677_GPIO5_OUT_LO (0x0 << 13)
-#define RT5677_GPIO5_OUT_HI (0x1 << 13)
-#define RT5677_GPIO5_P_MASK (0x1 << 12)
-#define RT5677_GPIO5_P_SFT 12
-#define RT5677_GPIO5_P_NOR (0x0 << 12)
-#define RT5677_GPIO5_P_INV (0x1 << 12)
-#define RT5677_GPIO4_DIR_MASK (0x1 << 11)
-#define RT5677_GPIO4_DIR_SFT 11
-#define RT5677_GPIO4_DIR_IN (0x0 << 11)
-#define RT5677_GPIO4_DIR_OUT (0x1 << 11)
-#define RT5677_GPIO4_OUT_MASK (0x1 << 10)
-#define RT5677_GPIO4_OUT_SFT 10
-#define RT5677_GPIO4_OUT_LO (0x0 << 10)
-#define RT5677_GPIO4_OUT_HI (0x1 << 10)
-#define RT5677_GPIO4_P_MASK (0x1 << 9)
-#define RT5677_GPIO4_P_SFT 9
-#define RT5677_GPIO4_P_NOR (0x0 << 9)
-#define RT5677_GPIO4_P_INV (0x1 << 9)
-#define RT5677_GPIO3_DIR_MASK (0x1 << 8)
-#define RT5677_GPIO3_DIR_SFT 8
-#define RT5677_GPIO3_DIR_IN (0x0 << 8)
-#define RT5677_GPIO3_DIR_OUT (0x1 << 8)
-#define RT5677_GPIO3_OUT_MASK (0x1 << 7)
-#define RT5677_GPIO3_OUT_SFT 7
-#define RT5677_GPIO3_OUT_LO (0x0 << 7)
-#define RT5677_GPIO3_OUT_HI (0x1 << 7)
-#define RT5677_GPIO3_P_MASK (0x1 << 6)
-#define RT5677_GPIO3_P_SFT 6
-#define RT5677_GPIO3_P_NOR (0x0 << 6)
-#define RT5677_GPIO3_P_INV (0x1 << 6)
-#define RT5677_GPIO2_DIR_MASK (0x1 << 5)
-#define RT5677_GPIO2_DIR_SFT 5
-#define RT5677_GPIO2_DIR_IN (0x0 << 5)
-#define RT5677_GPIO2_DIR_OUT (0x1 << 5)
-#define RT5677_GPIO2_OUT_MASK (0x1 << 4)
-#define RT5677_GPIO2_OUT_SFT 4
-#define RT5677_GPIO2_OUT_LO (0x0 << 4)
-#define RT5677_GPIO2_OUT_HI (0x1 << 4)
-#define RT5677_GPIO2_P_MASK (0x1 << 3)
-#define RT5677_GPIO2_P_SFT 3
-#define RT5677_GPIO2_P_NOR (0x0 << 3)
-#define RT5677_GPIO2_P_INV (0x1 << 3)
-#define RT5677_GPIO1_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO1_DIR_SFT 2
-#define RT5677_GPIO1_DIR_IN (0x0 << 2)
-#define RT5677_GPIO1_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO1_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO1_OUT_SFT 1
-#define RT5677_GPIO1_OUT_LO (0x0 << 1)
-#define RT5677_GPIO1_OUT_HI (0x1 << 1)
-#define RT5677_GPIO1_P_MASK (0x1 << 0)
-#define RT5677_GPIO1_P_SFT 0
-#define RT5677_GPIO1_P_NOR (0x0 << 0)
-#define RT5677_GPIO1_P_INV (0x1 << 0)
-
-/* GPIO Control 3 (0xc2) */
-#define RT5677_GPIO6_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO6_DIR_SFT 2
-#define RT5677_GPIO6_DIR_IN (0x0 << 2)
-#define RT5677_GPIO6_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO6_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO6_OUT_SFT 1
-#define RT5677_GPIO6_OUT_LO (0x0 << 1)
-#define RT5677_GPIO6_OUT_HI (0x1 << 1)
-#define RT5677_GPIO6_P_MASK (0x1 << 0)
-#define RT5677_GPIO6_P_SFT 0
-#define RT5677_GPIO6_P_NOR (0x0 << 0)
-#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* GPIO Control 2 (0xc1) & 3 (0xc2) common bits */
+#define RT5677_GPIOx_DIR_MASK (0x1 << 2)
+#define RT5677_GPIOx_DIR_SFT 2
+#define RT5677_GPIOx_DIR_IN (0x0 << 2)
+#define RT5677_GPIOx_DIR_OUT (0x1 << 2)
+#define RT5677_GPIOx_OUT_MASK (0x1 << 1)
+#define RT5677_GPIOx_OUT_SFT 1
+#define RT5677_GPIOx_OUT_LO (0x0 << 1)
+#define RT5677_GPIOx_OUT_HI (0x1 << 1)
+#define RT5677_GPIOx_P_MASK (0x1 << 0)
+#define RT5677_GPIOx_P_SFT 0
+#define RT5677_GPIOx_P_NOR (0x0 << 0)
+#define RT5677_GPIOx_P_INV (0x1 << 0)
+
+/* General Control (0xfa) */
+#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_MCLK (0x0 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_RC (0x1 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_SLIM (0x2 << 3)
/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
#define RT5677_DSP_IB_01_H (0x1 << 15)
@@ -1675,6 +1645,8 @@
#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin"
#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin"
+#define RT5677_DRV_NAME "rt5677"
+
/* System Clock Source */
enum {
RT5677_SCLK_S_MCLK,
@@ -1698,6 +1670,7 @@ enum {
RT5677_AIF4,
RT5677_AIF5,
RT5677_AIFS,
+ RT5677_DSPBUFF,
};
enum {
@@ -1714,11 +1687,12 @@ enum {
RT5677_IRQ_JD1,
RT5677_IRQ_JD2,
RT5677_IRQ_JD3,
+ RT5677_IRQ_NUM,
};
enum rt5677_type {
- RT5677,
- RT5676,
+ RT5677 = 1,
+ RT5676 = 2,
};
/* ASRC clock source selection */
@@ -1761,8 +1735,38 @@ enum {
RT5677_I2S4_SOURCE = (0x1 << 18),
};
+enum rt5677_dmic2_clk {
+ RT5677_DMIC_CLK1 = 0,
+ RT5677_DMIC_CLK2 = 1,
+};
+
+struct rt5677_platform_data {
+ /* IN1/IN2/LOUT1/LOUT2/LOUT3 can optionally be differential */
+ bool in1_diff;
+ bool in2_diff;
+ bool lout1_diff;
+ bool lout2_diff;
+ bool lout3_diff;
+ /* DMIC2 clock source selection */
+ enum rt5677_dmic2_clk dmic2_clk_pin;
+
+ /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */
+ u8 gpio_config[6];
+
+ /* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */
+ unsigned int jd1_gpio;
+ /* jd2 and jd3 can select 0 ~ 3 as
+ OFF, GPIO4, GPIO5 and GPIO6 respectively */
+ unsigned int jd2_gpio;
+ unsigned int jd3_gpio;
+
+ /* Set MICBIAS1 VDD 1v8 or 3v3 */
+ bool micbias1_vdd_3v3;
+};
+
struct rt5677_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+ struct device *dev;
struct rt5677_platform_data pdata;
struct regmap *regmap, *regmap_physical;
const struct firmware *fw1, *fw2;
@@ -1782,13 +1786,23 @@ struct rt5677_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
- bool dsp_vad_en;
- struct regmap_irq_chip_data *irq_data;
+ bool dsp_vad_en_request; /* DSP VAD enable/disable request */
+ bool dsp_vad_en; /* dsp_work parameter */
bool is_dsp_mode;
bool is_vref_slow;
+ struct delayed_work dsp_work;
+
+ /* Interrupt handling */
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+ unsigned int irq_en;
+ struct delayed_work resume_irq_check;
+ int irq;
+
+ int (*set_dsp_vad)(struct snd_soc_component *component, bool on);
};
-int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
#endif /* __RT5677_H__ */
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
new file mode 100644
index 000000000000..bba987308e15
--- /dev/null
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2018 Realtek Semiconductor Corp.
+// Author: Bard Liao <bardliao@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682.h>
+
+#include "rl6231.h"
+#include "rt5682.h"
+
+static const struct rt5682_platform_data i2s_default_platform_data = {
+ .dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2,
+ .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3,
+ .jd_src = RT5682_JD1,
+ .btndet_delay = 16,
+ .dai_clk_names[RT5682_DAI_WCLK_IDX] = "rt5682-dai-wclk",
+ .dai_clk_names[RT5682_DAI_BCLK_IDX] = "rt5682-dai-bclk",
+};
+
+static const struct regmap_config rt5682_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682_I2C_MODE,
+ .volatile_reg = rt5682_volatile_register,
+ .readable_reg = rt5682_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5682_reg,
+ .num_reg_defaults = RT5682_REG_NUM,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static void rt5682_jd_check_handler(struct work_struct *work)
+{
+ struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv,
+ jd_check_work.work);
+
+ if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) & RT5682_JDH_RS_MASK)
+ /* jack out */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, 0);
+ else
+ schedule_delayed_work(&rt5682->jd_check_work, 500);
+}
+
+static irqreturn_t rt5682_irq(int irq, void *data)
+{
+ struct rt5682_priv *rt5682 = data;
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time));
+
+ return IRQ_HANDLED;
+}
+
+static struct snd_soc_dai_driver rt5682_dai[] = {
+ {
+ .name = "rt5682-aif1",
+ .id = RT5682_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682-aif2",
+ .id = RT5682_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif2_dai_ops,
+ },
+};
+
+static void rt5682_i2c_disable_regulators(void *data)
+{
+ struct rt5682_priv *rt5682 = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies);
+}
+
+static int rt5682_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5682_priv *rt5682;
+ int i, ret;
+ unsigned int val;
+
+ rt5682 = devm_kzalloc(&i2c->dev, sizeof(struct rt5682_priv),
+ GFP_KERNEL);
+ if (!rt5682)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5682);
+
+ rt5682->i2c_dev = &i2c->dev;
+
+ rt5682->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt5682->pdata = *pdata;
+ else
+ rt5682_parse_dt(rt5682, &i2c->dev);
+
+ rt5682->regmap = devm_regmap_init_i2c(i2c, &rt5682_regmap);
+ if (IS_ERR(rt5682->regmap)) {
+ ret = PTR_ERR(rt5682->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5682->supplies); i++)
+ rt5682->supplies[i].supply = rt5682_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5682->supplies),
+ rt5682->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies),
+ rt5682->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, rt5682_i2c_disable_regulators,
+ rt5682);
+ if (ret)
+ return ret;
+
+ ret = rt5682_get_ldo1(rt5682, &i2c->dev);
+ if (ret)
+ return ret;
+
+ /* Sleep for 300 ms miniumum */
+ usleep_range(300000, 350000);
+
+ regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1);
+ usleep_range(10000, 15000);
+
+ regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5682\n", val);
+ return -ENODEV;
+ }
+
+ regmap_read(rt5682->regmap, RT5682_INT_DEVICE_ID, &val);
+ if (val == 0x6956) {
+ dev_dbg(&i2c->dev, "ALC5682I-VE device\n");
+ rt5682->ve_ic = true;
+ }
+
+ mutex_init(&rt5682->calibrate_mutex);
+ rt5682_calibrate(rt5682);
+
+ rt5682_apply_patch_list(rt5682, &i2c->dev);
+
+ regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000);
+
+ /* DMIC pin*/
+ if (rt5682->pdata.dmic1_data_pin != RT5682_DMIC1_NULL) {
+ switch (rt5682->pdata.dmic1_data_pin) {
+ case RT5682_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP2_PIN_MASK, RT5682_GP2_PIN_DMIC_SDA);
+ break;
+
+ case RT5682_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP5_PIN_MASK, RT5682_GP5_PIN_DMIC_SDA);
+ break;
+
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ switch (rt5682->pdata.dmic1_clk_pin) {
+ case RT5682_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_DMIC_CLK);
+ break;
+
+ case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK);
+ if (rt5682->pdata.dmic_clk_driving_high)
+ regmap_update_bits(rt5682->regmap,
+ RT5682_PAD_DRIVING_CTRL,
+ RT5682_PAD_DRV_GP3_MASK,
+ 2 << RT5682_PAD_DRV_GP3_SFT);
+ break;
+
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+ }
+
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK,
+ RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK,
+ RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1);
+ regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000);
+ regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8,
+ RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
+ regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
+ regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
+ regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1,
+ RT5682_FIFO_CLK_DIV_MASK, RT5682_FIFO_CLK_DIV_2);
+
+ INIT_DELAYED_WORK(&rt5682->jack_detect_work,
+ rt5682_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5682->jd_check_work,
+ rt5682_jd_check_handler);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5682", rt5682);
+ if (!ret)
+ rt5682->irq = i2c->irq;
+ else
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ }
+
+#ifdef CONFIG_COMMON_CLK
+ /* Check if MCLK provided */
+ rt5682->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5682->mclk))
+ return PTR_ERR(rt5682->mclk);
+
+ /* Register CCF DAI clock control */
+ ret = rt5682_register_dai_clks(rt5682);
+ if (ret)
+ return ret;
+
+ /* Initial setup for CCF */
+ rt5682->lrck[RT5682_AIF1] = 48000;
+#endif
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &rt5682_soc_component_dev,
+ rt5682_dai, ARRAY_SIZE(rt5682_dai));
+}
+
+static void rt5682_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5682_priv *rt5682 = i2c_get_clientdata(client);
+
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682->jd_check_work);
+
+ rt5682_reset(rt5682);
+}
+
+static void rt5682_i2c_remove(struct i2c_client *client)
+{
+ rt5682_i2c_shutdown(client);
+}
+
+static const struct of_device_id rt5682_of_match[] = {
+ {.compatible = "realtek,rt5682i"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5682_of_match);
+
+static const struct acpi_device_id rt5682_acpi_match[] = {
+ { "10EC5682" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match);
+
+static const struct i2c_device_id rt5682_i2c_id[] = {
+ {"rt5682"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id);
+
+static struct i2c_driver rt5682_i2c_driver = {
+ .driver = {
+ .name = "rt5682",
+ .of_match_table = rt5682_of_match,
+ .acpi_match_table = rt5682_acpi_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = rt5682_i2c_probe,
+ .remove = rt5682_i2c_remove,
+ .shutdown = rt5682_i2c_shutdown,
+ .id_table = rt5682_i2c_id,
+};
+module_i2c_driver(rt5682_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
new file mode 100644
index 000000000000..055bea0a4a3b
--- /dev/null
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682-sdw.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Oder Chiou <oder_chiou@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/acpi.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5682.h"
+
+#define RT5682_SDW_ADDR_L 0x3000
+#define RT5682_SDW_ADDR_H 0x3001
+#define RT5682_SDW_DATA_L 0x3004
+#define RT5682_SDW_DATA_H 0x3005
+#define RT5682_SDW_CMD 0x3008
+
+static int rt5682_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ unsigned int data_l, data_h;
+
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 0);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff));
+ regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_H, &data_h);
+ regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_L, &data_l);
+
+ *val = (data_h << 8) | data_l;
+
+ dev_vdbg(dev, "[%s] %04x => %04x\n", __func__, reg, *val);
+
+ return 0;
+}
+
+static int rt5682_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 1);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff));
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_H, (val >> 8) & 0xff);
+ regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_L, (val & 0xff));
+
+ dev_vdbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt5682_sdw_indirect_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682_I2C_MODE,
+ .volatile_reg = rt5682_volatile_register,
+ .readable_reg = rt5682_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5682_reg,
+ .num_reg_defaults = RT5682_REG_NUM,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt5682_sdw_read,
+ .reg_write = rt5682_sdw_write,
+};
+
+static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt5682_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int val_p = 0, val_c = 0, osr_p = 0, osr_c = 0;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream)
+ return -ENOMEM;
+
+ if (!rt5682->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt5682->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ switch (params_rate(params)) {
+ case 48000:
+ val_p = RT5682_SDW_REF_1_48K;
+ val_c = RT5682_SDW_REF_2_48K;
+ break;
+ case 96000:
+ val_p = RT5682_SDW_REF_1_96K;
+ val_c = RT5682_SDW_REF_2_96K;
+ break;
+ case 192000:
+ val_p = RT5682_SDW_REF_1_192K;
+ val_c = RT5682_SDW_REF_2_192K;
+ break;
+ case 32000:
+ val_p = RT5682_SDW_REF_1_32K;
+ val_c = RT5682_SDW_REF_2_32K;
+ break;
+ case 24000:
+ val_p = RT5682_SDW_REF_1_24K;
+ val_c = RT5682_SDW_REF_2_24K;
+ break;
+ case 16000:
+ val_p = RT5682_SDW_REF_1_16K;
+ val_c = RT5682_SDW_REF_2_16K;
+ break;
+ case 12000:
+ val_p = RT5682_SDW_REF_1_12K;
+ val_c = RT5682_SDW_REF_2_12K;
+ break;
+ case 8000:
+ val_p = RT5682_SDW_REF_1_8K;
+ val_c = RT5682_SDW_REF_2_8K;
+ break;
+ case 44100:
+ val_p = RT5682_SDW_REF_1_44K;
+ val_c = RT5682_SDW_REF_2_44K;
+ break;
+ case 88200:
+ val_p = RT5682_SDW_REF_1_88K;
+ val_c = RT5682_SDW_REF_2_88K;
+ break;
+ case 176400:
+ val_p = RT5682_SDW_REF_1_176K;
+ val_c = RT5682_SDW_REF_2_176K;
+ break;
+ case 22050:
+ val_p = RT5682_SDW_REF_1_22K;
+ val_c = RT5682_SDW_REF_2_22K;
+ break;
+ case 11025:
+ val_p = RT5682_SDW_REF_1_11K;
+ val_c = RT5682_SDW_REF_2_11K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (params_rate(params) <= 48000) {
+ osr_p = RT5682_DAC_OSR_D_8;
+ osr_c = RT5682_ADC_OSR_D_8;
+ } else if (params_rate(params) <= 96000) {
+ osr_p = RT5682_DAC_OSR_D_4;
+ osr_c = RT5682_ADC_OSR_D_4;
+ } else {
+ osr_p = RT5682_DAC_OSR_D_2;
+ osr_c = RT5682_ADC_OSR_D_2;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK,
+ RT5682_SDW_REF_1_MASK, val_p);
+ regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1,
+ RT5682_DAC_OSR_MASK, osr_p);
+ } else {
+ regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK,
+ RT5682_SDW_REF_2_MASK, val_c);
+ regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1,
+ RT5682_ADC_OSR_MASK, osr_c);
+ }
+
+ return retval;
+}
+
+static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt5682->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt5682->slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt5682_sdw_ops = {
+ .hw_params = rt5682_sdw_hw_params,
+ .hw_free = rt5682_sdw_hw_free,
+ .set_stream = rt5682_set_sdw_stream,
+ .shutdown = rt5682_sdw_shutdown,
+};
+
+static struct snd_soc_dai_driver rt5682_dai[] = {
+ {
+ .name = "rt5682-aif1",
+ .id = RT5682_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682-aif2",
+ .id = RT5682_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_aif2_dai_ops,
+ },
+ {
+ .name = "rt5682-sdw",
+ .id = RT5682_SDW,
+ .playback = {
+ .stream_name = "SDW Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDW Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682_STEREO_RATES,
+ .formats = RT5682_FORMATS,
+ },
+ .ops = &rt5682_sdw_ops,
+ },
+};
+
+static int rt5682_sdw_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt5682_priv *rt5682;
+ int ret;
+
+ rt5682 = devm_kzalloc(dev, sizeof(*rt5682), GFP_KERNEL);
+ if (!rt5682)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt5682);
+ rt5682->slave = slave;
+ rt5682->sdw_regmap = regmap;
+ rt5682->is_sdw = true;
+
+ mutex_init(&rt5682->disable_irq_lock);
+
+ rt5682->regmap = devm_regmap_init(dev, NULL, dev,
+ &rt5682_sdw_indirect_regmap);
+ if (IS_ERR(rt5682->regmap)) {
+ ret = PTR_ERR(rt5682->regmap);
+ dev_err(dev, "%s: Failed to allocate register map: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+
+ ret = rt5682_get_ldo1(rt5682, dev);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(rt5682->sdw_regmap, true);
+ regcache_cache_only(rt5682->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt5682->hw_init = false;
+ rt5682->first_hw_init = false;
+
+ mutex_init(&rt5682->calibrate_mutex);
+ INIT_DELAYED_WORK(&rt5682->jack_detect_work,
+ rt5682_jack_detect_handler);
+
+ ret = devm_snd_soc_register_component(dev,
+ &rt5682_soc_component_dev,
+ rt5682_dai, ARRAY_SIZE(rt5682_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+static int rt5682_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ int ret = 0, loop = 10;
+ unsigned int val;
+
+ rt5682->disable_irq = false;
+
+ if (rt5682->hw_init)
+ return 0;
+
+ regcache_cache_only(rt5682->sdw_regmap, false);
+ regcache_cache_only(rt5682->regmap, false);
+ if (rt5682->first_hw_init)
+ regcache_cache_bypass(rt5682->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt5682->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ while (loop > 0) {
+ regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val);
+ if (val == DEVICE_ID)
+ break;
+ dev_warn(dev, "Device with ID register %x is not rt5682\n", val);
+ usleep_range(30000, 30005);
+ loop--;
+ }
+
+ if (val != DEVICE_ID) {
+ dev_err(dev, "%s: Device with ID register %x is not rt5682\n", __func__, val);
+ ret = -ENODEV;
+ goto err_nodev;
+ }
+
+ rt5682_calibrate(rt5682);
+
+ if (rt5682->first_hw_init) {
+ regcache_cache_bypass(rt5682->regmap, false);
+ regcache_mark_dirty(rt5682->regmap);
+ regcache_sync(rt5682->regmap);
+
+ /* volatile registers */
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2,
+ RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL);
+
+ goto reinit;
+ }
+
+ rt5682_apply_patch_list(rt5682, dev);
+
+ regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000);
+
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK,
+ RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+ regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000);
+ regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8,
+ RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
+ regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
+ regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
+
+ /* Soundwire */
+ regmap_write(rt5682->regmap, RT5682_PLL2_INTERNAL, 0xa266);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_1, 0x1700);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_2, 0x0006);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_3, 0x2600);
+ regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_4, 0x0c8f);
+ regmap_write(rt5682->regmap, RT5682_PLL_TRACK_2, 0x3000);
+ regmap_write(rt5682->regmap, RT5682_PLL_TRACK_3, 0x4000);
+ regmap_update_bits(rt5682->regmap, RT5682_GLB_CLK,
+ RT5682_SCLK_SRC_MASK | RT5682_PLL2_SRC_MASK,
+ RT5682_SCLK_SRC_PLL2 | RT5682_PLL2_SRC_SDW);
+
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2,
+ RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL);
+ regmap_write(rt5682->regmap, RT5682_CBJ_CTRL_1, 0xd142);
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_5, 0x0700, 0x0600);
+ regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_3,
+ RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN);
+ regmap_update_bits(rt5682->regmap, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_IRQ | RT5682_POW_JDH |
+ RT5682_POW_ANA, RT5682_POW_IRQ |
+ RT5682_POW_JDH | RT5682_POW_ANA);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2,
+ RT5682_PWR_JDH, RT5682_PWR_JDH);
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK | RT5682_JD1_IRQ_MASK,
+ RT5682_JD1_EN | RT5682_JD1_IRQ_PUL);
+
+reinit:
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(250));
+
+ /* Mark Slave initialization complete */
+ rt5682->hw_init = true;
+ rt5682->first_hw_init = true;
+
+err_nodev:
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static bool rt5682_sdw_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x3000:
+ case 0x3001:
+ case 0x3004:
+ case 0x3005:
+ case 0x3008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt5682_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = RT5682_I2C_MODE,
+ .readable_reg = rt5682_sdw_readable_register,
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt5682_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt5682->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt5682_io_init(&slave->dev, slave);
+}
+
+static int rt5682_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x4; /* BITMAP: 00000100 */
+ prop->sink_ports = 0x2; /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+/* Bus clock frequency */
+#define RT5682_CLK_FREQ_9600000HZ 9600000
+#define RT5682_CLK_FREQ_12000000HZ 12000000
+#define RT5682_CLK_FREQ_6000000HZ 6000000
+#define RT5682_CLK_FREQ_4800000HZ 4800000
+#define RT5682_CLK_FREQ_2400000HZ 2400000
+#define RT5682_CLK_FREQ_12288000HZ 12288000
+
+static int rt5682_clock_config(struct device *dev)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt5682->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT5682_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT5682_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT5682_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT5682_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT5682_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT5682_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt5682->sdw_regmap, 0xe0, value);
+ regmap_write(rt5682->sdw_regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+static int rt5682_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt5682->params, params, sizeof(*params));
+
+ ret = rt5682_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "%s: Invalid clk config", __func__);
+
+ return ret;
+}
+
+static int rt5682_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ mutex_lock(&rt5682->disable_irq_lock);
+ if (status->control_port & 0x4 && !rt5682->disable_irq) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time));
+ }
+ mutex_unlock(&rt5682->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt5682_slave_ops = {
+ .read_prop = rt5682_read_prop,
+ .interrupt_callback = rt5682_interrupt_callback,
+ .update_status = rt5682_update_status,
+ .bus_config = rt5682_bus_config,
+};
+
+static int rt5682_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt5682_sdw_regmap);
+ if (IS_ERR(regmap))
+ return -EINVAL;
+
+ return rt5682_sdw_init(&slave->dev, regmap, slave);
+}
+
+static int rt5682_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
+
+ if (rt5682->hw_init)
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt5682_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x5682, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt5682_id);
+
+static int rt5682_dev_suspend(struct device *dev)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+
+ if (!rt5682->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+
+ regcache_cache_only(rt5682->sdw_regmap, true);
+ regcache_cache_only(rt5682->regmap, true);
+ regcache_mark_dirty(rt5682->regmap);
+
+ return 0;
+}
+
+static int rt5682_dev_system_suspend(struct device *dev)
+{
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ if (!rt5682->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt5682->disable_irq_lock);
+ rt5682->disable_irq = true;
+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
+ SDW_SCP_INT1_IMPL_DEF, 0);
+ mutex_unlock(&rt5682->disable_irq_lock);
+
+ if (ret < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
+ }
+
+ return rt5682_dev_suspend(dev);
+}
+
+static int rt5682_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt5682->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt5682->disable_irq_lock);
+ if (rt5682->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF);
+ rt5682->disable_irq = false;
+ }
+ mutex_unlock(&rt5682->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt5682->sdw_regmap, false);
+ regcache_cache_only(rt5682->regmap, false);
+ regcache_sync(rt5682->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt5682_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt5682_dev_system_suspend, rt5682_dev_resume)
+ RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL)
+};
+
+static struct sdw_driver rt5682_sdw_driver = {
+ .driver = {
+ .name = "rt5682",
+ .pm = pm_ptr(&rt5682_pm),
+ },
+ .probe = rt5682_sdw_probe,
+ .remove = rt5682_sdw_remove,
+ .ops = &rt5682_slave_ops,
+ .id_table = rt5682_id,
+};
+module_sdw_driver(rt5682_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682 driver SDW");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
new file mode 100644
index 000000000000..d39f8e4f3474
--- /dev/null
+++ b/sound/soc/codecs/rt5682.c
@@ -0,0 +1,3191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2018 Realtek Semiconductor Corp.
+// Author: Bard Liao <bardliao@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682.h>
+
+#include "rl6231.h"
+#include "rt5682.h"
+
+const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = {
+ "AVDD",
+ "MICVDD",
+ "VBAT",
+ "DBVDD",
+ "LDO1-IN",
+};
+EXPORT_SYMBOL_GPL(rt5682_supply_names);
+
+static const struct reg_sequence patch_list[] = {
+ {RT5682_HP_IMP_SENS_CTRL_19, 0x1000},
+ {RT5682_DAC_ADC_DIG_VOL1, 0xa020},
+ {RT5682_I2C_CTRL, 0x000f},
+ {RT5682_PLL2_INTERNAL, 0x8266},
+ {RT5682_SAR_IL_CMD_1, 0x22b7},
+ {RT5682_SAR_IL_CMD_3, 0x0365},
+ {RT5682_SAR_IL_CMD_6, 0x0110},
+ {RT5682_CHARGE_PUMP_1, 0x0210},
+ {RT5682_HP_LOGIC_CTRL_2, 0x0007},
+ {RT5682_SAR_IL_CMD_2, 0xac00},
+ {RT5682_CBJ_CTRL_7, 0x0104},
+};
+
+void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(rt5682->regmap, patch_list,
+ ARRAY_SIZE(patch_list));
+ if (ret)
+ dev_warn(dev, "Failed to apply regmap patch: %d\n", ret);
+}
+EXPORT_SYMBOL_GPL(rt5682_apply_patch_list);
+
+const struct reg_default rt5682_reg[RT5682_REG_NUM] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x8000},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x800f},
+ {0x000b, 0x0000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x1404},
+ {0x0013, 0x1000},
+ {0x0014, 0xa00a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0019, 0xafaf},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x0080},
+ {0x0044, 0x0c0c},
+ {0x0049, 0x0000},
+ {0x0061, 0x0000},
+ {0x0062, 0x0000},
+ {0x0063, 0x003f},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0a10},
+ {0x0070, 0x8000},
+ {0x0071, 0x8000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x0080, 0x0000},
+ {0x0081, 0x0000},
+ {0x0082, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008d, 0x0000},
+ {0x008e, 0x0060},
+ {0x008f, 0x1000},
+ {0x0091, 0x0c26},
+ {0x0092, 0x0073},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0098, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x100c},
+ {0x009f, 0x0000},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00ae, 0x2040},
+ {0x00af, 0x0000},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d1, 0x2244},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0009},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f1, 0x00d0},
+ {0x00f2, 0x00d0},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6530},
+ {0x0100, 0xa0a0},
+ {0x010b, 0x0000},
+ {0x010c, 0xae00},
+ {0x010d, 0xaaa0},
+ {0x010e, 0x8aa2},
+ {0x010f, 0x02a2},
+ {0x0110, 0xc000},
+ {0x0111, 0x04a2},
+ {0x0112, 0x2800},
+ {0x0113, 0x0000},
+ {0x0117, 0x0100},
+ {0x0125, 0x0410},
+ {0x0132, 0x6026},
+ {0x0136, 0x5555},
+ {0x0138, 0x3700},
+ {0x013a, 0x2000},
+ {0x013b, 0x2000},
+ {0x013c, 0x2005},
+ {0x013f, 0x0000},
+ {0x0142, 0x0000},
+ {0x0145, 0x0002},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0149, 0x0000},
+ {0x0150, 0x79a1},
+ {0x0156, 0xaaaa},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0021},
+ {0x0190, 0x413d},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01af, 0x0000},
+ {0x01b0, 0x0400},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0000},
+ {0x01b4, 0x0000},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x01c3},
+ {0x01b7, 0x02a0},
+ {0x01b8, 0x03e9},
+ {0x01b9, 0x1389},
+ {0x01ba, 0xc351},
+ {0x01bb, 0x0009},
+ {0x01bc, 0x0018},
+ {0x01bd, 0x002a},
+ {0x01be, 0x004c},
+ {0x01bf, 0x0097},
+ {0x01c0, 0x433d},
+ {0x01c2, 0x0000},
+ {0x01c3, 0x0000},
+ {0x01c4, 0x0000},
+ {0x01c5, 0x0000},
+ {0x01c6, 0x0000},
+ {0x01c7, 0x0000},
+ {0x01c8, 0x40af},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0008},
+ {0x01d8, 0x0029},
+ {0x01d9, 0x3333},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0320},
+ {0x01e0, 0x06a1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01ea, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa005},
+ {0x0212, 0x824c},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x0250, 0x4500},
+ {0x0251, 0x40b3},
+ {0x0252, 0x0000},
+ {0x0253, 0x0000},
+ {0x0254, 0x0000},
+ {0x0255, 0x0000},
+ {0x0256, 0x0000},
+ {0x0257, 0x0000},
+ {0x0258, 0x0000},
+ {0x0259, 0x0000},
+ {0x025a, 0x0005},
+ {0x0270, 0x0000},
+ {0x02ff, 0x0110},
+ {0x0300, 0x001f},
+ {0x0301, 0x032c},
+ {0x0302, 0x5f21},
+ {0x0303, 0x4000},
+ {0x0304, 0x4000},
+ {0x0305, 0x06d5},
+ {0x0306, 0x8000},
+ {0x0307, 0x0700},
+ {0x0310, 0x4560},
+ {0x0311, 0xa4a8},
+ {0x0312, 0x7418},
+ {0x0313, 0x0000},
+ {0x0314, 0x0006},
+ {0x0315, 0xffff},
+ {0x0316, 0xc400},
+ {0x0317, 0x0000},
+ {0x03c0, 0x7e00},
+ {0x03c1, 0x8000},
+ {0x03c2, 0x8000},
+ {0x03c3, 0x8000},
+ {0x03c4, 0x8000},
+ {0x03c5, 0x8000},
+ {0x03c6, 0x8000},
+ {0x03c7, 0x8000},
+ {0x03c8, 0x8000},
+ {0x03c9, 0x8000},
+ {0x03ca, 0x8000},
+ {0x03cb, 0x8000},
+ {0x03cc, 0x8000},
+ {0x03d0, 0x0000},
+ {0x03d1, 0x0000},
+ {0x03d2, 0x0000},
+ {0x03d3, 0x0000},
+ {0x03d4, 0x2000},
+ {0x03d5, 0x2000},
+ {0x03d6, 0x0000},
+ {0x03d7, 0x0000},
+ {0x03d8, 0x2000},
+ {0x03d9, 0x2000},
+ {0x03da, 0x2000},
+ {0x03db, 0x2000},
+ {0x03dc, 0x0000},
+ {0x03dd, 0x0000},
+ {0x03de, 0x0000},
+ {0x03df, 0x2000},
+ {0x03e0, 0x0000},
+ {0x03e1, 0x0000},
+ {0x03e2, 0x0000},
+ {0x03e3, 0x0000},
+ {0x03e4, 0x0000},
+ {0x03e5, 0x0000},
+ {0x03e6, 0x0000},
+ {0x03e7, 0x0000},
+ {0x03e8, 0x0000},
+ {0x03e9, 0x0000},
+ {0x03ea, 0x0000},
+ {0x03eb, 0x0000},
+ {0x03ec, 0x0000},
+ {0x03ed, 0x0000},
+ {0x03ee, 0x0000},
+ {0x03ef, 0x0000},
+ {0x03f0, 0x0800},
+ {0x03f1, 0x0800},
+ {0x03f2, 0x0800},
+ {0x03f3, 0x0800},
+};
+EXPORT_SYMBOL_GPL(rt5682_reg);
+
+bool rt5682_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682_RESET:
+ case RT5682_CBJ_CTRL_2:
+ case RT5682_INT_ST_1:
+ case RT5682_4BTN_IL_CMD_1:
+ case RT5682_AJD1_CTRL:
+ case RT5682_HP_CALIB_CTRL_1:
+ case RT5682_INT_DEVICE_ID:
+ case RT5682_DEVICE_ID:
+ case RT5682_I2C_MODE:
+ case RT5682_HP_CALIB_CTRL_10:
+ case RT5682_EFUSE_CTRL_2:
+ case RT5682_JD_TOP_VC_VTRL:
+ case RT5682_HP_IMP_SENS_CTRL_19:
+ case RT5682_IL_CMD_1:
+ case RT5682_SAR_IL_CMD_2:
+ case RT5682_SAR_IL_CMD_4:
+ case RT5682_SAR_IL_CMD_10:
+ case RT5682_SAR_IL_CMD_11:
+ case RT5682_EFUSE_CTRL_6...RT5682_EFUSE_CTRL_11:
+ case RT5682_HP_CALIB_STA_1...RT5682_HP_CALIB_STA_11:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(rt5682_volatile_register);
+
+bool rt5682_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682_RESET:
+ case RT5682_INT_DEVICE_ID:
+ case RT5682_VERSION_ID:
+ case RT5682_VENDOR_ID:
+ case RT5682_DEVICE_ID:
+ case RT5682_HP_CTRL_1:
+ case RT5682_HP_CTRL_2:
+ case RT5682_HPL_GAIN:
+ case RT5682_HPR_GAIN:
+ case RT5682_I2C_CTRL:
+ case RT5682_CBJ_BST_CTRL:
+ case RT5682_CBJ_CTRL_1:
+ case RT5682_CBJ_CTRL_2:
+ case RT5682_CBJ_CTRL_3:
+ case RT5682_CBJ_CTRL_4:
+ case RT5682_CBJ_CTRL_5:
+ case RT5682_CBJ_CTRL_6:
+ case RT5682_CBJ_CTRL_7:
+ case RT5682_DAC1_DIG_VOL:
+ case RT5682_STO1_ADC_DIG_VOL:
+ case RT5682_STO1_ADC_BOOST:
+ case RT5682_HP_IMP_GAIN_1:
+ case RT5682_HP_IMP_GAIN_2:
+ case RT5682_SIDETONE_CTRL:
+ case RT5682_STO1_ADC_MIXER:
+ case RT5682_AD_DA_MIXER:
+ case RT5682_STO1_DAC_MIXER:
+ case RT5682_A_DAC1_MUX:
+ case RT5682_DIG_INF2_DATA:
+ case RT5682_REC_MIXER:
+ case RT5682_CAL_REC:
+ case RT5682_ALC_BACK_GAIN:
+ case RT5682_PWR_DIG_1:
+ case RT5682_PWR_DIG_2:
+ case RT5682_PWR_ANLG_1:
+ case RT5682_PWR_ANLG_2:
+ case RT5682_PWR_ANLG_3:
+ case RT5682_PWR_MIXER:
+ case RT5682_PWR_VOL:
+ case RT5682_CLK_DET:
+ case RT5682_RESET_LPF_CTRL:
+ case RT5682_RESET_HPF_CTRL:
+ case RT5682_DMIC_CTRL_1:
+ case RT5682_I2S1_SDP:
+ case RT5682_I2S2_SDP:
+ case RT5682_ADDA_CLK_1:
+ case RT5682_ADDA_CLK_2:
+ case RT5682_I2S1_F_DIV_CTRL_1:
+ case RT5682_I2S1_F_DIV_CTRL_2:
+ case RT5682_TDM_CTRL:
+ case RT5682_TDM_ADDA_CTRL_1:
+ case RT5682_TDM_ADDA_CTRL_2:
+ case RT5682_DATA_SEL_CTRL_1:
+ case RT5682_TDM_TCON_CTRL:
+ case RT5682_GLB_CLK:
+ case RT5682_PLL_CTRL_1:
+ case RT5682_PLL_CTRL_2:
+ case RT5682_PLL_TRACK_1:
+ case RT5682_PLL_TRACK_2:
+ case RT5682_PLL_TRACK_3:
+ case RT5682_PLL_TRACK_4:
+ case RT5682_PLL_TRACK_5:
+ case RT5682_PLL_TRACK_6:
+ case RT5682_PLL_TRACK_11:
+ case RT5682_SDW_REF_CLK:
+ case RT5682_DEPOP_1:
+ case RT5682_DEPOP_2:
+ case RT5682_HP_CHARGE_PUMP_1:
+ case RT5682_HP_CHARGE_PUMP_2:
+ case RT5682_MICBIAS_1:
+ case RT5682_MICBIAS_2:
+ case RT5682_PLL_TRACK_12:
+ case RT5682_PLL_TRACK_14:
+ case RT5682_PLL2_CTRL_1:
+ case RT5682_PLL2_CTRL_2:
+ case RT5682_PLL2_CTRL_3:
+ case RT5682_PLL2_CTRL_4:
+ case RT5682_RC_CLK_CTRL:
+ case RT5682_I2S_M_CLK_CTRL_1:
+ case RT5682_I2S2_F_DIV_CTRL_1:
+ case RT5682_I2S2_F_DIV_CTRL_2:
+ case RT5682_EQ_CTRL_1:
+ case RT5682_EQ_CTRL_2:
+ case RT5682_IRQ_CTRL_1:
+ case RT5682_IRQ_CTRL_2:
+ case RT5682_IRQ_CTRL_3:
+ case RT5682_IRQ_CTRL_4:
+ case RT5682_INT_ST_1:
+ case RT5682_GPIO_CTRL_1:
+ case RT5682_GPIO_CTRL_2:
+ case RT5682_GPIO_CTRL_3:
+ case RT5682_HP_AMP_DET_CTRL_1:
+ case RT5682_HP_AMP_DET_CTRL_2:
+ case RT5682_MID_HP_AMP_DET:
+ case RT5682_LOW_HP_AMP_DET:
+ case RT5682_DELAY_BUF_CTRL:
+ case RT5682_SV_ZCD_1:
+ case RT5682_SV_ZCD_2:
+ case RT5682_IL_CMD_1:
+ case RT5682_IL_CMD_2:
+ case RT5682_IL_CMD_3:
+ case RT5682_IL_CMD_4:
+ case RT5682_IL_CMD_5:
+ case RT5682_IL_CMD_6:
+ case RT5682_4BTN_IL_CMD_1:
+ case RT5682_4BTN_IL_CMD_2:
+ case RT5682_4BTN_IL_CMD_3:
+ case RT5682_4BTN_IL_CMD_4:
+ case RT5682_4BTN_IL_CMD_5:
+ case RT5682_4BTN_IL_CMD_6:
+ case RT5682_4BTN_IL_CMD_7:
+ case RT5682_ADC_STO1_HP_CTRL_1:
+ case RT5682_ADC_STO1_HP_CTRL_2:
+ case RT5682_AJD1_CTRL:
+ case RT5682_JD1_THD:
+ case RT5682_JD2_THD:
+ case RT5682_JD_CTRL_1:
+ case RT5682_DUMMY_1:
+ case RT5682_DUMMY_2:
+ case RT5682_DUMMY_3:
+ case RT5682_DAC_ADC_DIG_VOL1:
+ case RT5682_BIAS_CUR_CTRL_2:
+ case RT5682_BIAS_CUR_CTRL_3:
+ case RT5682_BIAS_CUR_CTRL_4:
+ case RT5682_BIAS_CUR_CTRL_5:
+ case RT5682_BIAS_CUR_CTRL_6:
+ case RT5682_BIAS_CUR_CTRL_7:
+ case RT5682_BIAS_CUR_CTRL_8:
+ case RT5682_BIAS_CUR_CTRL_9:
+ case RT5682_BIAS_CUR_CTRL_10:
+ case RT5682_VREF_REC_OP_FB_CAP_CTRL:
+ case RT5682_CHARGE_PUMP_1:
+ case RT5682_DIG_IN_CTRL_1:
+ case RT5682_PAD_DRIVING_CTRL:
+ case RT5682_SOFT_RAMP_DEPOP:
+ case RT5682_CHOP_DAC:
+ case RT5682_CHOP_ADC:
+ case RT5682_CALIB_ADC_CTRL:
+ case RT5682_VOL_TEST:
+ case RT5682_SPKVDD_DET_STA:
+ case RT5682_TEST_MODE_CTRL_1:
+ case RT5682_TEST_MODE_CTRL_2:
+ case RT5682_TEST_MODE_CTRL_3:
+ case RT5682_TEST_MODE_CTRL_4:
+ case RT5682_TEST_MODE_CTRL_5:
+ case RT5682_PLL1_INTERNAL:
+ case RT5682_PLL2_INTERNAL:
+ case RT5682_STO_NG2_CTRL_1:
+ case RT5682_STO_NG2_CTRL_2:
+ case RT5682_STO_NG2_CTRL_3:
+ case RT5682_STO_NG2_CTRL_4:
+ case RT5682_STO_NG2_CTRL_5:
+ case RT5682_STO_NG2_CTRL_6:
+ case RT5682_STO_NG2_CTRL_7:
+ case RT5682_STO_NG2_CTRL_8:
+ case RT5682_STO_NG2_CTRL_9:
+ case RT5682_STO_NG2_CTRL_10:
+ case RT5682_STO1_DAC_SIL_DET:
+ case RT5682_SIL_PSV_CTRL1:
+ case RT5682_SIL_PSV_CTRL2:
+ case RT5682_SIL_PSV_CTRL3:
+ case RT5682_SIL_PSV_CTRL4:
+ case RT5682_SIL_PSV_CTRL5:
+ case RT5682_HP_IMP_SENS_CTRL_01:
+ case RT5682_HP_IMP_SENS_CTRL_02:
+ case RT5682_HP_IMP_SENS_CTRL_03:
+ case RT5682_HP_IMP_SENS_CTRL_04:
+ case RT5682_HP_IMP_SENS_CTRL_05:
+ case RT5682_HP_IMP_SENS_CTRL_06:
+ case RT5682_HP_IMP_SENS_CTRL_07:
+ case RT5682_HP_IMP_SENS_CTRL_08:
+ case RT5682_HP_IMP_SENS_CTRL_09:
+ case RT5682_HP_IMP_SENS_CTRL_10:
+ case RT5682_HP_IMP_SENS_CTRL_11:
+ case RT5682_HP_IMP_SENS_CTRL_12:
+ case RT5682_HP_IMP_SENS_CTRL_13:
+ case RT5682_HP_IMP_SENS_CTRL_14:
+ case RT5682_HP_IMP_SENS_CTRL_15:
+ case RT5682_HP_IMP_SENS_CTRL_16:
+ case RT5682_HP_IMP_SENS_CTRL_17:
+ case RT5682_HP_IMP_SENS_CTRL_18:
+ case RT5682_HP_IMP_SENS_CTRL_19:
+ case RT5682_HP_IMP_SENS_CTRL_20:
+ case RT5682_HP_IMP_SENS_CTRL_21:
+ case RT5682_HP_IMP_SENS_CTRL_22:
+ case RT5682_HP_IMP_SENS_CTRL_23:
+ case RT5682_HP_IMP_SENS_CTRL_24:
+ case RT5682_HP_IMP_SENS_CTRL_25:
+ case RT5682_HP_IMP_SENS_CTRL_26:
+ case RT5682_HP_IMP_SENS_CTRL_27:
+ case RT5682_HP_IMP_SENS_CTRL_28:
+ case RT5682_HP_IMP_SENS_CTRL_29:
+ case RT5682_HP_IMP_SENS_CTRL_30:
+ case RT5682_HP_IMP_SENS_CTRL_31:
+ case RT5682_HP_IMP_SENS_CTRL_32:
+ case RT5682_HP_IMP_SENS_CTRL_33:
+ case RT5682_HP_IMP_SENS_CTRL_34:
+ case RT5682_HP_IMP_SENS_CTRL_35:
+ case RT5682_HP_IMP_SENS_CTRL_36:
+ case RT5682_HP_IMP_SENS_CTRL_37:
+ case RT5682_HP_IMP_SENS_CTRL_38:
+ case RT5682_HP_IMP_SENS_CTRL_39:
+ case RT5682_HP_IMP_SENS_CTRL_40:
+ case RT5682_HP_IMP_SENS_CTRL_41:
+ case RT5682_HP_IMP_SENS_CTRL_42:
+ case RT5682_HP_IMP_SENS_CTRL_43:
+ case RT5682_HP_LOGIC_CTRL_1:
+ case RT5682_HP_LOGIC_CTRL_2:
+ case RT5682_HP_LOGIC_CTRL_3:
+ case RT5682_HP_CALIB_CTRL_1:
+ case RT5682_HP_CALIB_CTRL_2:
+ case RT5682_HP_CALIB_CTRL_3:
+ case RT5682_HP_CALIB_CTRL_4:
+ case RT5682_HP_CALIB_CTRL_5:
+ case RT5682_HP_CALIB_CTRL_6:
+ case RT5682_HP_CALIB_CTRL_7:
+ case RT5682_HP_CALIB_CTRL_9:
+ case RT5682_HP_CALIB_CTRL_10:
+ case RT5682_HP_CALIB_CTRL_11:
+ case RT5682_HP_CALIB_STA_1:
+ case RT5682_HP_CALIB_STA_2:
+ case RT5682_HP_CALIB_STA_3:
+ case RT5682_HP_CALIB_STA_4:
+ case RT5682_HP_CALIB_STA_5:
+ case RT5682_HP_CALIB_STA_6:
+ case RT5682_HP_CALIB_STA_7:
+ case RT5682_HP_CALIB_STA_8:
+ case RT5682_HP_CALIB_STA_9:
+ case RT5682_HP_CALIB_STA_10:
+ case RT5682_HP_CALIB_STA_11:
+ case RT5682_SAR_IL_CMD_1:
+ case RT5682_SAR_IL_CMD_2:
+ case RT5682_SAR_IL_CMD_3:
+ case RT5682_SAR_IL_CMD_4:
+ case RT5682_SAR_IL_CMD_5:
+ case RT5682_SAR_IL_CMD_6:
+ case RT5682_SAR_IL_CMD_7:
+ case RT5682_SAR_IL_CMD_8:
+ case RT5682_SAR_IL_CMD_9:
+ case RT5682_SAR_IL_CMD_10:
+ case RT5682_SAR_IL_CMD_11:
+ case RT5682_SAR_IL_CMD_12:
+ case RT5682_SAR_IL_CMD_13:
+ case RT5682_EFUSE_CTRL_1:
+ case RT5682_EFUSE_CTRL_2:
+ case RT5682_EFUSE_CTRL_3:
+ case RT5682_EFUSE_CTRL_4:
+ case RT5682_EFUSE_CTRL_5:
+ case RT5682_EFUSE_CTRL_6:
+ case RT5682_EFUSE_CTRL_7:
+ case RT5682_EFUSE_CTRL_8:
+ case RT5682_EFUSE_CTRL_9:
+ case RT5682_EFUSE_CTRL_10:
+ case RT5682_EFUSE_CTRL_11:
+ case RT5682_JD_TOP_VC_VTRL:
+ case RT5682_DRC1_CTRL_0:
+ case RT5682_DRC1_CTRL_1:
+ case RT5682_DRC1_CTRL_2:
+ case RT5682_DRC1_CTRL_3:
+ case RT5682_DRC1_CTRL_4:
+ case RT5682_DRC1_CTRL_5:
+ case RT5682_DRC1_CTRL_6:
+ case RT5682_DRC1_HARD_LMT_CTRL_1:
+ case RT5682_DRC1_HARD_LMT_CTRL_2:
+ case RT5682_DRC1_PRIV_1:
+ case RT5682_DRC1_PRIV_2:
+ case RT5682_DRC1_PRIV_3:
+ case RT5682_DRC1_PRIV_4:
+ case RT5682_DRC1_PRIV_5:
+ case RT5682_DRC1_PRIV_6:
+ case RT5682_DRC1_PRIV_7:
+ case RT5682_DRC1_PRIV_8:
+ case RT5682_EQ_AUTO_RCV_CTRL1:
+ case RT5682_EQ_AUTO_RCV_CTRL2:
+ case RT5682_EQ_AUTO_RCV_CTRL3:
+ case RT5682_EQ_AUTO_RCV_CTRL4:
+ case RT5682_EQ_AUTO_RCV_CTRL5:
+ case RT5682_EQ_AUTO_RCV_CTRL6:
+ case RT5682_EQ_AUTO_RCV_CTRL7:
+ case RT5682_EQ_AUTO_RCV_CTRL8:
+ case RT5682_EQ_AUTO_RCV_CTRL9:
+ case RT5682_EQ_AUTO_RCV_CTRL10:
+ case RT5682_EQ_AUTO_RCV_CTRL11:
+ case RT5682_EQ_AUTO_RCV_CTRL12:
+ case RT5682_EQ_AUTO_RCV_CTRL13:
+ case RT5682_ADC_L_EQ_LPF1_A1:
+ case RT5682_R_EQ_LPF1_A1:
+ case RT5682_L_EQ_LPF1_H0:
+ case RT5682_R_EQ_LPF1_H0:
+ case RT5682_L_EQ_BPF1_A1:
+ case RT5682_R_EQ_BPF1_A1:
+ case RT5682_L_EQ_BPF1_A2:
+ case RT5682_R_EQ_BPF1_A2:
+ case RT5682_L_EQ_BPF1_H0:
+ case RT5682_R_EQ_BPF1_H0:
+ case RT5682_L_EQ_BPF2_A1:
+ case RT5682_R_EQ_BPF2_A1:
+ case RT5682_L_EQ_BPF2_A2:
+ case RT5682_R_EQ_BPF2_A2:
+ case RT5682_L_EQ_BPF2_H0:
+ case RT5682_R_EQ_BPF2_H0:
+ case RT5682_L_EQ_BPF3_A1:
+ case RT5682_R_EQ_BPF3_A1:
+ case RT5682_L_EQ_BPF3_A2:
+ case RT5682_R_EQ_BPF3_A2:
+ case RT5682_L_EQ_BPF3_H0:
+ case RT5682_R_EQ_BPF3_H0:
+ case RT5682_L_EQ_BPF4_A1:
+ case RT5682_R_EQ_BPF4_A1:
+ case RT5682_L_EQ_BPF4_A2:
+ case RT5682_R_EQ_BPF4_A2:
+ case RT5682_L_EQ_BPF4_H0:
+ case RT5682_R_EQ_BPF4_H0:
+ case RT5682_L_EQ_HPF1_A1:
+ case RT5682_R_EQ_HPF1_A1:
+ case RT5682_L_EQ_HPF1_H0:
+ case RT5682_R_EQ_HPF1_H0:
+ case RT5682_L_EQ_PRE_VOL:
+ case RT5682_R_EQ_PRE_VOL:
+ case RT5682_L_EQ_POST_VOL:
+ case RT5682_R_EQ_POST_VOL:
+ case RT5682_I2C_MODE:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(rt5682_readable_register);
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
+
+/* Interface data select */
+static const char * const rt5682_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if2_adc_enum,
+ RT5682_DIG_INF2_DATA, RT5682_IF2_ADC_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_01_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC1_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_23_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC2_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_45_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC3_SEL_SFT, rt5682_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_if1_67_adc_enum,
+ RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC4_SEL_SFT, rt5682_data_select);
+
+static const struct snd_kcontrol_new rt5682_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum);
+
+static const char * const rt5682_dac_select[] = {
+ "IF1", "SOUND"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682_dacl_enum,
+ RT5682_AD_DA_MIXER, RT5682_DAC1_L_SEL_SFT, rt5682_dac_select);
+
+static const struct snd_kcontrol_new rt5682_dac_l_mux =
+ SOC_DAPM_ENUM("DAC L Mux", rt5682_dacl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682_dacr_enum,
+ RT5682_AD_DA_MIXER, RT5682_DAC1_R_SEL_SFT, rt5682_dac_select);
+
+static const struct snd_kcontrol_new rt5682_dac_r_mux =
+ SOC_DAPM_ENUM("DAC R Mux", rt5682_dacr_enum);
+
+void rt5682_reset(struct rt5682_priv *rt5682)
+{
+ regmap_write(rt5682->regmap, RT5682_RESET, 0);
+ if (!rt5682->is_sdw)
+ regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1);
+}
+EXPORT_SYMBOL_GPL(rt5682_reset);
+
+/**
+ * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @component: SoC audio component device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the component driver will turn on
+ * ASRC for these filters if ASRC is selected as their clock source.
+ */
+int rt5682_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ switch (clk_src) {
+ case RT5682_CLK_SEL_SYS:
+ case RT5682_CLK_SEL_I2S1_ASRC:
+ case RT5682_CLK_SEL_I2S2_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5682_DA_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682_PLL_TRACK_2,
+ RT5682_FILTER_CLK_SEL_MASK,
+ clk_src << RT5682_FILTER_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5682_AD_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682_PLL_TRACK_3,
+ RT5682_FILTER_CLK_SEL_MASK,
+ clk_src << RT5682_FILTER_CLK_SEL_SFT);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_sel_asrc_clk_src);
+
+static int rt5682_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5682_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val);
+ dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type);
+ snd_soc_component_update_bits(component,
+ RT5682_SAR_IL_CMD_2, 0x10, 0x10);
+
+ return btn_type;
+}
+
+static void rt5682_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13,
+ RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_BTN);
+ snd_soc_component_write(component, RT5682_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2,
+ RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK,
+ RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR);
+ if (rt5682->is_sdw)
+ snd_soc_component_update_bits(component,
+ RT5682_IRQ_CTRL_3,
+ RT5682_IL_IRQ_MASK | RT5682_IL_IRQ_TYPE_MASK,
+ RT5682_IL_IRQ_EN | RT5682_IL_IRQ_PUL);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK,
+ RT5682_IL_IRQ_EN);
+ } else {
+ snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3,
+ RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_DIS);
+ snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2,
+ RT5682_4BTN_IL_MASK, RT5682_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2,
+ RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_RST);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13,
+ RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_TYPE);
+ }
+}
+
+/**
+ * rt5682_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ unsigned int val, count;
+
+ if (jack_insert) {
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB);
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, RT5682_PWR_CBJ);
+ snd_soc_component_update_bits(component,
+ RT5682_HP_CHARGE_PUMP_1,
+ RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
+ rt5682_enable_push_button_irq(component, false);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW);
+ usleep_range(55000, 60000);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH);
+
+ count = 0;
+ val = snd_soc_component_read(component, RT5682_CBJ_CTRL_2)
+ & RT5682_JACK_TYPE_MASK;
+ while (val == 0 && count < 50) {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component,
+ RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK;
+ count++;
+ }
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ rt5682->jack_type = SND_JACK_HEADSET;
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_FAST_OFF_MASK, RT5682_FAST_OFF_EN);
+ rt5682_enable_push_button_irq(component, true);
+ break;
+ default:
+ rt5682->jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+
+ snd_soc_component_update_bits(component,
+ RT5682_HP_CHARGE_PUMP_1,
+ RT5682_OSW_L_MASK | RT5682_OSW_R_MASK,
+ RT5682_OSW_L_EN | RT5682_OSW_R_EN);
+ snd_soc_component_update_bits(component, RT5682_MICBIAS_2,
+ RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK,
+ RT5682_PWR_CLK25M_PU | RT5682_PWR_CLK1M_PU);
+ } else {
+ rt5682_enable_push_button_irq(component, false);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW);
+ if (!snd_soc_dapm_get_pin_status(dapm, "MICBIAS") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL1") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL2B"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0);
+ if (!snd_soc_dapm_get_pin_status(dapm, "Vref2") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL1") &&
+ !snd_soc_dapm_get_pin_status(dapm, "PLL2B"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, 0);
+ snd_soc_component_update_bits(component, RT5682_MICBIAS_2,
+ RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK,
+ RT5682_PWR_CLK25M_PD | RT5682_PWR_CLK1M_PD);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_FAST_OFF_MASK, RT5682_FAST_OFF_DIS);
+
+ rt5682->jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type);
+ return rt5682->jack_type;
+}
+
+static int rt5682_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682->hs_jack = hs_jack;
+
+ if (rt5682->is_sdw && !rt5682->first_hw_init)
+ return 0;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+
+ return 0;
+ }
+
+ if (!rt5682->is_sdw) {
+ switch (rt5682->pdata.jd_src) {
+ case RT5682_JD1:
+ snd_soc_component_update_bits(component,
+ RT5682_CBJ_CTRL_5, 0x0700, 0x0600);
+ snd_soc_component_update_bits(component,
+ RT5682_CBJ_CTRL_2, RT5682_EXT_JD_SRC,
+ RT5682_EXT_JD_SRC_MANUAL);
+ snd_soc_component_write(component, RT5682_CBJ_CTRL_1,
+ 0xd142);
+ snd_soc_component_update_bits(component,
+ RT5682_CBJ_CTRL_3, RT5682_CBJ_IN_BUF_EN,
+ RT5682_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component,
+ RT5682_SAR_IL_CMD_1, RT5682_SAR_POW_MASK,
+ RT5682_SAR_POW_EN);
+ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1,
+ RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_IRQ | RT5682_POW_JDH |
+ RT5682_POW_ANA, RT5682_POW_IRQ |
+ RT5682_POW_JDH | RT5682_POW_ANA);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2,
+ RT5682_PWR_JDH, RT5682_PWR_JDH);
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK,
+ RT5682_JD1_EN | RT5682_JD1_POL_NOR);
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7,
+ 0x7f7f, (rt5682->pdata.btndet_delay << 8 |
+ rt5682->pdata.btndet_delay));
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work,
+ msecs_to_jiffies(250));
+ break;
+
+ case RT5682_JD_NULL:
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void rt5682_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(work, struct rt5682_priv, jack_detect_work.work);
+ struct snd_soc_dapm_context *dapm;
+ int val, btn_type;
+
+ if (!rt5682->component ||
+ !snd_soc_card_is_instantiated(rt5682->component->card)) {
+ /* card not yet ready, try later */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(15));
+ return;
+ }
+
+ if (rt5682->is_sdw) {
+ if (pm_runtime_status_suspended(rt5682->slave->dev.parent)) {
+ dev_dbg(&rt5682->slave->dev,
+ "%s: parent device is pm_runtime_status_suspended, skipping jack detection\n",
+ __func__);
+ return;
+ }
+ }
+
+ dapm = snd_soc_component_to_dapm(rt5682->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&rt5682->calibrate_mutex);
+
+ val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL)
+ & RT5682_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5682->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5682->jack_type =
+ rt5682_headset_detect(rt5682->component, 1);
+ rt5682->irq_work_delay_time = 0;
+ } else if ((rt5682->jack_type & SND_JACK_HEADSET) ==
+ SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ rt5682->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5682_button_detect(rt5682->component);
+ /**
+ * rt5682 can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5682->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5682->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5682->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5682->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ dev_err(rt5682->component->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0);
+ rt5682->irq_work_delay_time = 50;
+ }
+
+ mutex_unlock(&rt5682->calibrate_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (!rt5682->is_sdw) {
+ if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5682->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5682->jd_check_work);
+ }
+}
+EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler);
+
+static const struct snd_kcontrol_new rt5682_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL,
+ RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
+
+ /* IN Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL,
+ RT5682_BST_CBJ_SFT, 8, 0, bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST,
+ RT5682_STO1_ADC_L_BST_SFT, RT5682_STO1_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+};
+
+static int rt5682_div_sel(struct rt5682_priv *rt5682,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5682->sysclk < target) {
+ dev_err(rt5682->component->dev,
+ "sysclk rate %d is too low\n", rt5682->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ dev_dbg(rt5682->component->dev, "div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5682->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5682->sysclk) {
+ dev_dbg(rt5682->component->dev,
+ "can't find div for sysclk %d\n",
+ rt5682->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5682->sysclk)
+ dev_err(rt5682->component->dev,
+ "sysclk rate %d is too high\n", rt5682->sysclk);
+
+ return size - 1;
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ int idx, dmic_clk_rate = 3072000;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ if (rt5682->pdata.dmic_clk_rate)
+ dmic_clk_rate = rt5682->pdata.dmic_clk_rate;
+
+ idx = rt5682_div_sel(rt5682, dmic_clk_rate, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+static int set_filter_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ int ref, val, reg, idx;
+ static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+ static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ if (rt5682->is_sdw)
+ return 0;
+
+ val = snd_soc_component_read(component, RT5682_GPIO_CTRL_1) &
+ RT5682_GP4_PIN_MASK;
+ if (w->shift == RT5682_PWR_ADC_S1F_BIT &&
+ val == RT5682_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5682->lrck[RT5682_AIF2];
+ else
+ ref = 256 * rt5682->lrck[RT5682_AIF1];
+
+ idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f));
+
+ if (w->shift == RT5682_PWR_ADC_S1F_BIT)
+ reg = RT5682_PLL_TRACK_3;
+ else
+ reg = RT5682_PLL_TRACK_2;
+
+ snd_soc_component_update_bits(component, reg,
+ RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT);
+
+ /* select over sample rate */
+ for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) {
+ if (rt5682->sysclk <= 12288000 * div_o[idx])
+ break;
+ }
+
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1,
+ RT5682_ADC_OSR_MASK | RT5682_DAC_OSR_MASK,
+ (idx << RT5682_ADC_OSR_SFT) | (idx << RT5682_DAC_OSR_SFT));
+
+ return 0;
+}
+
+static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5682_GLB_CLK);
+ val &= RT5682_SCLK_SRC_MASK;
+ if (val == RT5682_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_sys_clk_from_pll2(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ val = snd_soc_component_read(component, RT5682_GLB_CLK);
+ val &= RT5682_SCLK_SRC_MASK;
+ if (val == RT5682_SCLK_SRC_PLL2)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, shift, val;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5682_ADC_STO1_ASRC_SFT:
+ reg = RT5682_PLL_TRACK_3;
+ shift = RT5682_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5682_DAC_STO1_ASRC_SFT:
+ reg = RT5682_PLL_TRACK_2;
+ shift = RT5682_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
+ switch (val) {
+ case RT5682_CLK_SEL_I2S1_ASRC:
+ case RT5682_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5682_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER,
+ RT5682_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER,
+ RT5682_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER,
+ RT5682_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5682_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682_REC_MIXER,
+ RT5682_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5682_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc1l_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC1L_SRC_SFT, rt5682_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc1r_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC1R_SRC_SFT, rt5682_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5682_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adcl_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADCL_SRC_SFT, rt5682_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adcr_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADCR_SRC_SFT, rt5682_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5682_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc2l_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC2L_SRC_SFT, rt5682_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_sto1_adc2r_enum, RT5682_STO1_ADC_MIXER,
+ RT5682_STO1_ADC2R_SRC_SFT, rt5682_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5682_if1_adc_slot_values[] = {
+ 0,
+ 2,
+ 4,
+ 6,
+};
+
+static const char * const rt5682_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_if1_adc_slot_enum,
+ RT5682_TDM_CTRL, RT5682_TDM_ADC_LCA_SFT, RT5682_TDM_ADC_LCA_MASK,
+ rt5682_if1_adc_slot_src, rt5682_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5682_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5682_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_alg_dac_l1_enum, RT5682_A_DAC1_MUX,
+ RT5682_A_DACL1_SFT, rt5682_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5682_alg_dac_r1_enum, RT5682_A_DAC1_MUX,
+ RT5682_A_DACR1_SFT, rt5682_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682_alg_dac_r1_enum);
+
+/* Out Switch */
+static const struct snd_kcontrol_new hpol_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
+ RT5682_L_MUTE_SFT, 1, 1);
+static const struct snd_kcontrol_new hpor_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
+ RT5682_R_MUTE_SFT, 1, 1);
+
+static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_AMP_MUTE, 0);
+ snd_soc_component_update_bits(component, RT5682_HP_LOGIC_CTRL_2,
+ RT5682_HP_LC2_SIG_SOUR2_MASK, RT5682_HP_LC2_SIG_SOUR2_REG);
+ snd_soc_component_update_bits(component,
+ RT5682_DEPOP_1, 0x60, 0x60);
+ snd_soc_component_update_bits(component,
+ RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN);
+ usleep_range(5000, 10000);
+ snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5682_HP_CTRL_2,
+ RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN, 0);
+ snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1,
+ RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_M);
+ snd_soc_component_update_bits(component,
+ RT5682_DEPOP_1, 0x60, 0x0);
+ snd_soc_component_update_bits(component,
+ RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000);
+ break;
+ }
+
+ return 0;
+}
+
+static int set_dmic_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 50, val;
+
+ if (rt5682->pdata.dmic_delay)
+ delay = rt5682->pdata.dmic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(component, RT5682_GLB_CLK);
+ val &= RT5682_SCLK_SRC_MASK;
+ if (val == RT5682_SCLK_SRC_PLL1 || val == RT5682_SCLK_SRC_PLL2)
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB);
+
+ /*Add delay to avoid pop noise*/
+ msleep(delay);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (!rt5682->jack_type) {
+ if (!snd_soc_dapm_get_pin_status(w->dapm, "MICBIAS"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0);
+ if (!snd_soc_dapm_get_pin_status(w->dapm, "Vref2"))
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_verf(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case RT5682_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV1, 0);
+ break;
+
+ case RT5682_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0);
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(15000, 20000);
+ switch (w->shift) {
+ case RT5682_PWR_VREF1_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV1,
+ RT5682_PWR_FV1);
+ break;
+
+ case RT5682_PWR_VREF2_BIT:
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2,
+ RT5682_PWR_FV2);
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const unsigned int rt5682_adcdat_pin_values[] = {
+ 1,
+ 3,
+};
+
+static const char * const rt5682_adcdat_pin_select[] = {
+ "ADCDAT1",
+ "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum,
+ RT5682_GPIO_CTRL_1, RT5682_GP4_PIN_SFT, RT5682_GP4_PIN_MASK,
+ rt5682_adcdat_pin_select, rt5682_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum);
+
+static const unsigned int rt5682_hpo_sig_out_values[] = {
+ 2,
+ 7,
+};
+
+static const char * const rt5682_hpo_sig_out_mode[] = {
+ "Legacy",
+ "OneBit",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_hpo_sig_out_enum,
+ RT5682_HP_LOGIC_CTRL_2, 0, RT5682_HP_LC2_SIG_SOUR1_MASK,
+ rt5682_hpo_sig_out_mode, rt5682_hpo_sig_out_values);
+
+static const struct snd_kcontrol_new rt5682_hpo_sig_demux =
+ SOC_DAPM_ENUM("HPO Signal Demux", rt5682_hpo_sig_out_enum);
+
+static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5682_PWR_ANLG_3, RT5682_PWR_PLL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT,
+ 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0,
+ rt5682_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Vref2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682_PLL_TRACK_1,
+ RT5682_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682_PWR_ANLG_2, RT5682_PWR_MB1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682_PWR_ANLG_2, RT5682_PWR_MB2_BIT,
+ 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1,
+ RT5682_DMIC_1_EN_SFT, 0, set_dmic_power,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix,
+ ARRAY_SIZE(rt5682_rec1_l_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682_PWR_ANLG_2,
+ RT5682_PWR_RM1_L_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682_PWR_DIG_1,
+ RT5682_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682_PWR_DIG_1,
+ RT5682_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682_CHOP_ADC,
+ RT5682_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682_PWR_DIG_2,
+ RT5682_PWR_ADC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_L_MUTE_SFT, 1, rt5682_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5682_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL,
+ RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5682_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5682_PWR_DIG_1, RT5682_PWR_I2S1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5682_PWR_DIG_1, RT5682_PWR_I2S2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SOUND DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SOUND DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_adcdat_pin_ctrl),
+
+ SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_dac_l_mux),
+ SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682_dac_r_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0,
+ RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDWRX", "SDW Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDWTX", "SDW Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682_dac_l_mix, ARRAY_SIZE(rt5682_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682_dac_r_mix, ARRAY_SIZE(rt5682_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5682_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0,
+ &rt5682_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682_PWR_DIG_2,
+ RT5682_PWR_DAC_S1F_BIT, 0, set_filter_clk,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682_sto1_dac_l_mix, ARRAY_SIZE(rt5682_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682_sto1_dac_r_mix, ARRAY_SIZE(rt5682_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682_PWR_DIG_1,
+ RT5682_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682_PWR_DIG_1,
+ RT5682_PWR_DAC_R1_BIT, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5682_CHOP_DAC,
+ RT5682_CKGEN_DAC1_SFT, 0, NULL, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682_hp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("HP Amp L", RT5682_PWR_ANLG_1,
+ RT5682_PWR_HA_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1,
+ RT5682_PWR_HA_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1,
+ RT5682_PUMP_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1,
+ RT5682_CAPLESS_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0,
+ &hpol_switch),
+ SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0,
+ &hpor_switch),
+
+ SND_SOC_DAPM_OUT_DRV("HPO Legacy", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HPO OneBit", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DEMUX("HPO Signal Demux", SND_SOC_NOPM, 0, 0, &rt5682_hpo_sig_demux),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET,
+ RT5682_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682_CLK_DET,
+ RT5682_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5682_CLK_DET,
+ RT5682_PLL2_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET", RT5682_CLK_DET,
+ RT5682_POW_CLK_DET_SFT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+ {"ADC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2},
+ {"ADC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2},
+ {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1},
+ {"DAC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2},
+ {"DAC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"ADC STO1 ASRC", NULL, "DA ASRC"},
+ {"ADC STO1 ASRC", NULL, "CLKDET"},
+ {"DAC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+ {"DAC STO1 ASRC", NULL, "CLKDET"},
+
+ /*Vref*/
+ {"MICBIAS1", NULL, "Vref1"},
+ {"MICBIAS2", NULL, "Vref1"},
+
+ {"CLKDET SYS", NULL, "CLKDET"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "I2S1"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"SDWTX", NULL, "PLL2B"},
+ {"SDWTX", NULL, "PLL2F"},
+ {"SDWTX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"SOUND DAC L", NULL, "SDWRX"},
+ {"SOUND DAC L", NULL, "DAC Stereo1 Filter"},
+ {"SOUND DAC L", NULL, "PLL2B"},
+ {"SOUND DAC L", NULL, "PLL2F"},
+ {"SOUND DAC R", NULL, "SDWRX"},
+ {"SOUND DAC R", NULL, "DAC Stereo1 Filter"},
+ {"SOUND DAC R", NULL, "PLL2B"},
+ {"SOUND DAC R", NULL, "PLL2F"},
+
+ {"DAC L Mux", "IF1", "IF1 DAC1 L"},
+ {"DAC L Mux", "SOUND", "SOUND DAC L"},
+ {"DAC R Mux", "IF1", "IF1 DAC1 R"},
+ {"DAC R Mux", "SOUND", "SOUND DAC R"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "DAC L Mux"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "DAC R Mux"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"DAC L1", NULL, "DAC 1 Clock"},
+ {"DAC R1", NULL, "DAC 1 Clock"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "HP Amp L"},
+ {"HP Amp", NULL, "HP Amp R"},
+ {"HP Amp", NULL, "Capless"},
+ {"HP Amp", NULL, "Charge Pump"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "Vref1"},
+
+ {"HPO Signal Demux", NULL, "HP Amp"},
+
+ {"HPO Legacy", "Legacy", "HPO Signal Demux"},
+ {"HPO OneBit", "OneBit", "HPO Signal Demux"},
+
+ {"HPOL Playback", "Switch", "HPO Legacy"},
+ {"HPOR Playback", "Switch", "HPO Legacy"},
+
+ {"HPOL", NULL, "HPOL Playback"},
+ {"HPOR", NULL, "HPOR Playback"},
+ {"HPOL", NULL, "HPO OneBit"},
+ {"HPOR", NULL, "HPO OneBit"},
+};
+
+static int rt5682_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int cl, val = 0;
+
+ if (tx_mask || rx_mask)
+ snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2,
+ RT5682_TDM_EN, RT5682_TDM_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2,
+ RT5682_TDM_EN, 0);
+
+ switch (slots) {
+ case 4:
+ val |= RT5682_TDM_TX_CH_4;
+ val |= RT5682_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5682_TDM_TX_CH_6;
+ val |= RT5682_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5682_TDM_TX_CH_8;
+ val |= RT5682_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682_TDM_CTRL,
+ RT5682_TDM_TX_CH_MASK | RT5682_TDM_RX_CH_MASK, val);
+
+ switch (slot_width) {
+ case 8:
+ if (tx_mask || rx_mask)
+ return -EINVAL;
+ cl = RT5682_I2S1_TX_CHL_8 | RT5682_I2S1_RX_CHL_8;
+ break;
+ case 16:
+ val = RT5682_TDM_CL_16;
+ cl = RT5682_I2S1_TX_CHL_16 | RT5682_I2S1_RX_CHL_16;
+ break;
+ case 20:
+ val = RT5682_TDM_CL_20;
+ cl = RT5682_I2S1_TX_CHL_20 | RT5682_I2S1_RX_CHL_20;
+ break;
+ case 24:
+ val = RT5682_TDM_CL_24;
+ cl = RT5682_I2S1_TX_CHL_24 | RT5682_I2S1_RX_CHL_24;
+ break;
+ case 32:
+ val = RT5682_TDM_CL_32;
+ cl = RT5682_I2S1_TX_CHL_32 | RT5682_I2S1_RX_CHL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_CL_MASK, val);
+ snd_soc_component_update_bits(component, RT5682_I2S1_SDP,
+ RT5682_I2S1_TX_CHL_MASK | RT5682_I2S1_RX_CHL_MASK, cl);
+
+ return 0;
+}
+
+static int rt5682_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int pre_div, frame_size;
+
+ rt5682->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5682->sysclk, rt5682->lrck[dai->id]);
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt5682->lrck[dai->id], pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5682_I2S1_DL_20;
+ len_2 |= RT5682_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5682_I2S1_DL_24;
+ len_2 |= RT5682_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5682_I2S1_DL_32;
+ len_2 |= RT5682_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5682_I2S2_DL_8;
+ len_2 |= RT5682_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682_AIF1:
+ snd_soc_component_update_bits(component, RT5682_I2S1_SDP,
+ RT5682_I2S1_DL_MASK, len_1);
+ if (rt5682->master[RT5682_AIF1]) {
+ snd_soc_component_update_bits(component,
+ RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK |
+ RT5682_I2S_CLK_SRC_MASK,
+ pre_div << RT5682_I2S_M_DIV_SFT |
+ (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK,
+ RT5682_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK,
+ RT5682_I2S1_MONO_DIS);
+ break;
+ case RT5682_AIF2:
+ snd_soc_component_update_bits(component, RT5682_I2S2_SDP,
+ RT5682_I2S2_DL_MASK, len_2);
+ if (rt5682->master[RT5682_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_M_PD_MASK,
+ pre_div << RT5682_I2S2_M_PD_SFT);
+ }
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component,
+ RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK,
+ RT5682_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK,
+ RT5682_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5682->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt5682->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5682_I2S_BP_INV;
+ tdm_ctrl |= RT5682_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5682_AIF1)
+ tdm_ctrl |= RT5682_TDM_S_LP_INV | RT5682_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5682_AIF1)
+ tdm_ctrl |= RT5682_TDM_S_BP_INV | RT5682_TDM_S_LP_INV |
+ RT5682_TDM_M_BP_INV | RT5682_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5682_I2S_DF_LEFT;
+ tdm_ctrl |= RT5682_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5682_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5682_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5682_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5682_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682_AIF1:
+ snd_soc_component_update_bits(component, RT5682_I2S1_SDP,
+ RT5682_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_MS_MASK | RT5682_TDM_S_BP_MASK |
+ RT5682_TDM_DF_MASK | RT5682_TDM_M_BP_MASK |
+ RT5682_TDM_M_LP_MASK | RT5682_TDM_S_LP_MASK,
+ tdm_ctrl | rt5682->master[dai->id]);
+ break;
+ case RT5682_AIF2:
+ if (rt5682->master[dai->id] == 0)
+ reg_val |= RT5682_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5682_I2S2_SDP,
+ RT5682_I2S2_MS_MASK | RT5682_I2S_BP_MASK |
+ RT5682_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5682_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, src = 0;
+
+ if (freq == rt5682->sysclk && clk_id == rt5682->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5682_SCLK_S_MCLK:
+ reg_val |= RT5682_SCLK_SRC_MCLK;
+ src = RT5682_CLK_SRC_MCLK;
+ break;
+ case RT5682_SCLK_S_PLL1:
+ reg_val |= RT5682_SCLK_SRC_PLL1;
+ src = RT5682_CLK_SRC_PLL1;
+ break;
+ case RT5682_SCLK_S_PLL2:
+ reg_val |= RT5682_SCLK_SRC_PLL2;
+ src = RT5682_CLK_SRC_PLL2;
+ break;
+ case RT5682_SCLK_S_RCCLK:
+ reg_val |= RT5682_SCLK_SRC_RCCLK;
+ src = RT5682_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT5682_GLB_CLK,
+ RT5682_SCLK_SRC_MASK, reg_val);
+
+ if (rt5682->master[RT5682_AIF2]) {
+ snd_soc_component_update_bits(component,
+ RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_SRC_MASK,
+ src << RT5682_I2S2_SRC_SFT);
+ }
+
+ rt5682->sysclk = freq;
+ rt5682->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt5682_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code, pll2f_code, pll2b_code;
+ unsigned int pll2_fout1, pll2_ps_val;
+ int ret;
+
+ if (source == rt5682->pll_src[pll_id] &&
+ freq_in == rt5682->pll_in[pll_id] &&
+ freq_out == rt5682->pll_out[pll_id])
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt5682->pll_in[pll_id] = 0;
+ rt5682->pll_out[pll_id] = 0;
+ snd_soc_component_update_bits(component, RT5682_GLB_CLK,
+ RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ if (pll_id == RT5682_PLL2) {
+ switch (source) {
+ case RT5682_PLL2_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT5682_GLB_CLK, RT5682_PLL2_SRC_MASK,
+ RT5682_PLL2_SRC_MCLK);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL2 Source %d\n",
+ source);
+ return -EINVAL;
+ }
+
+ /**
+ * PLL2 concatenates 2 PLL units.
+ * We suggest the Fout of the front PLL is 3.84MHz.
+ */
+ pll2_fout1 = 3840000;
+ ret = rl6231_pll_calc(freq_in, pll2_fout1, &pll2f_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+ dev_dbg(component->dev, "PLL2F: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n",
+ freq_in, pll2_fout1,
+ pll2f_code.m_bp,
+ (pll2f_code.m_bp ? 0 : pll2f_code.m_code),
+ pll2f_code.n_code, pll2f_code.k_code);
+
+ ret = rl6231_pll_calc(pll2_fout1, freq_out, &pll2b_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ pll2_fout1);
+ return ret;
+ }
+ dev_dbg(component->dev, "PLL2B: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n",
+ pll2_fout1, freq_out,
+ pll2b_code.m_bp,
+ (pll2b_code.m_bp ? 0 : pll2b_code.m_code),
+ pll2b_code.n_code, pll2b_code.k_code);
+
+ snd_soc_component_write(component, RT5682_PLL2_CTRL_1,
+ pll2f_code.k_code << RT5682_PLL2F_K_SFT |
+ pll2b_code.k_code << RT5682_PLL2B_K_SFT |
+ pll2b_code.m_code);
+ snd_soc_component_write(component, RT5682_PLL2_CTRL_2,
+ pll2f_code.m_code << RT5682_PLL2F_M_SFT |
+ pll2b_code.n_code);
+ snd_soc_component_write(component, RT5682_PLL2_CTRL_3,
+ pll2f_code.n_code << RT5682_PLL2F_N_SFT);
+
+ if (freq_out == 22579200)
+ pll2_ps_val = 1 << RT5682_PLL2B_SEL_PS_SFT;
+ else
+ pll2_ps_val = 1 << RT5682_PLL2B_PS_BYP_SFT;
+ snd_soc_component_update_bits(component, RT5682_PLL2_CTRL_4,
+ RT5682_PLL2B_SEL_PS_MASK | RT5682_PLL2B_PS_BYP_MASK |
+ RT5682_PLL2B_M_BP_MASK | RT5682_PLL2F_M_BP_MASK | 0xf,
+ pll2_ps_val |
+ (pll2b_code.m_bp ? 1 : 0) << RT5682_PLL2B_M_BP_SFT |
+ (pll2f_code.m_bp ? 1 : 0) << RT5682_PLL2F_M_BP_SFT |
+ 0xf);
+ } else {
+ switch (source) {
+ case RT5682_PLL1_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK,
+ RT5682_PLL1_SRC_MCLK);
+ break;
+ case RT5682_PLL1_S_BCLK1:
+ snd_soc_component_update_bits(component,
+ RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK,
+ RT5682_PLL1_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL1 Source %d\n",
+ source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported input clock %d\n",
+ freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT5682_PLL_CTRL_1,
+ (pll_code.n_code << RT5682_PLL_N_SFT) | pll_code.k_code);
+ snd_soc_component_write(component, RT5682_PLL_CTRL_2,
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT) |
+ ((pll_code.m_bp << RT5682_PLL_M_BP_SFT) | RT5682_PLL_RST));
+ }
+
+ rt5682->pll_in[pll_id] = freq_in;
+ rt5682->pll_out[pll_id] = freq_out;
+ rt5682->pll_src[pll_id] = source;
+
+ return 0;
+}
+
+static int rt5682_set_bclk1_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 256:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_256);
+ break;
+ case 128:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_128);
+ break;
+ case 64:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL,
+ RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2,
+ RT5682_I2S2_BCLK_MS2_MASK,
+ RT5682_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2,
+ RT5682_I2S2_BCLK_MS2_MASK,
+ RT5682_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_PWR_BG, RT5682_PWR_BG);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
+ RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO,
+ RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
+ RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
+ RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0);
+ regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
+ RT5682_PWR_BG, 0);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_COMMON_CLK
+#define CLK_PLL2_FIN 48000000
+#define CLK_48 48000
+#define CLK_44 44100
+
+static bool rt5682_clk_check(struct rt5682_priv *rt5682)
+{
+ if (!rt5682->master[RT5682_AIF1]) {
+ dev_dbg(rt5682->i2c_dev, "sysclk/dai not set correctly\n");
+ return false;
+ }
+ return true;
+}
+
+static int rt5682_wclk_prepare(struct clk_hw *hw)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ component = rt5682->component;
+ dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS");
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_MB, RT5682_PWR_MB);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Vref2");
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_FV2,
+ RT5682_PWR_VREF2);
+ usleep_range(55000, 60000);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_FV2, RT5682_PWR_FV2);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "I2S1");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2F");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2B");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static void rt5682_wclk_unprepare(struct clk_hw *hw)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!rt5682_clk_check(rt5682))
+ return;
+
+ component = rt5682->component;
+ dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Vref2");
+ if (!rt5682->jack_type)
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_FV2 |
+ RT5682_PWR_MB, 0);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "I2S1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2F");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2B");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682_clk_check(rt5682))
+ return 0;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ */
+ if (rt5682->lrck[RT5682_AIF1] != CLK_48 &&
+ rt5682->lrck[RT5682_AIF1] != CLK_44) {
+ dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ return 0;
+ }
+
+ return rt5682->lrck[RT5682_AIF1];
+}
+
+static int rt5682_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ * It will force to 48kHz if not both.
+ */
+ if (req->rate != CLK_48 && req->rate != CLK_44) {
+ dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ req->rate = CLK_48;
+ }
+
+ return 0;
+}
+
+static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component;
+ struct clk_hw *parent_hw;
+ const char * const clk_name = clk_hw_get_name(hw);
+ int pre_div;
+ unsigned int clk_pll2_out;
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ component = rt5682->component;
+
+ /*
+ * Whether the wclk's parent clk (mclk) exists or not, please ensure
+ * it is fixed or set to 48MHz before setting wclk rate. It's a
+ * temporary limitation. Only accept 48MHz clk as the clk provider.
+ *
+ * It will set the codec anyway by assuming mclk is 48MHz.
+ */
+ parent_hw = clk_hw_get_parent(hw);
+ if (!parent_hw)
+ dev_warn(rt5682->i2c_dev,
+ "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n",
+ CLK_PLL2_FIN);
+
+ if (parent_rate != CLK_PLL2_FIN)
+ dev_warn(rt5682->i2c_dev, "clk %s only support %d Hz input\n",
+ clk_name, CLK_PLL2_FIN);
+
+ /*
+ * To achieve the rate conversion from 48MHz to 44.1k or 48kHz,
+ * PLL2 is needed.
+ */
+ clk_pll2_out = rate * 512;
+ rt5682_set_component_pll(component, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ CLK_PLL2_FIN, clk_pll2_out);
+
+ rt5682_set_component_sysclk(component, RT5682_SCLK_S_PLL2, 0,
+ clk_pll2_out, SND_SOC_CLOCK_IN);
+
+ rt5682->lrck[RT5682_AIF1] = rate;
+
+ pre_div = rl6231_get_clk_info(rt5682->sysclk, rate);
+
+ snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1,
+ RT5682_I2S_M_DIV_MASK | RT5682_I2S_CLK_SRC_MASK,
+ pre_div << RT5682_I2S_M_DIV_SFT |
+ (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT);
+
+ return 0;
+}
+
+static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ unsigned int bclks_per_wclk;
+
+ regmap_read(rt5682->regmap, RT5682_TDM_TCON_CTRL, &bclks_per_wclk);
+
+ switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) {
+ case RT5682_TDM_BCLK_MS1_256:
+ return parent_rate * 256;
+ case RT5682_TDM_BCLK_MS1_128:
+ return parent_rate * 128;
+ case RT5682_TDM_BCLK_MS1_64:
+ return parent_rate * 64;
+ case RT5682_TDM_BCLK_MS1_32:
+ return parent_rate * 32;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long rt5682_bclk_get_factor(unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long factor;
+
+ factor = rate / parent_rate;
+ if (factor < 64)
+ return 32;
+ else if (factor < 128)
+ return 64;
+ else if (factor < 256)
+ return 128;
+ else
+ return 256;
+}
+
+static int rt5682_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_parent_rate || !rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ /*
+ * BCLK rates are set as a multiplier of WCLK in HW.
+ * We don't allow changing the parent WCLK. We just do
+ * some rounding down based on the parent WCLK rate
+ * and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = rt5682_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ struct snd_soc_component *component;
+ struct snd_soc_dai *dai;
+ unsigned long factor;
+
+ if (!rt5682_clk_check(rt5682))
+ return -EINVAL;
+
+ component = rt5682->component;
+
+ factor = rt5682_bclk_get_factor(rate, parent_rate);
+
+ for_each_component_dais(component, dai)
+ if (dai->id == RT5682_AIF1)
+ return rt5682_set_bclk1_ratio(dai, factor);
+
+ dev_err(rt5682->i2c_dev, "dai %d not found in component\n",
+ RT5682_AIF1);
+ return -ENODEV;
+}
+
+static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = {
+ [RT5682_DAI_WCLK_IDX] = {
+ .prepare = rt5682_wclk_prepare,
+ .unprepare = rt5682_wclk_unprepare,
+ .recalc_rate = rt5682_wclk_recalc_rate,
+ .determine_rate = rt5682_wclk_determine_rate,
+ .set_rate = rt5682_wclk_set_rate,
+ },
+ [RT5682_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682_bclk_recalc_rate,
+ .determine_rate = rt5682_bclk_determine_rate,
+ .set_rate = rt5682_bclk_set_rate,
+ },
+};
+
+int rt5682_register_dai_clks(struct rt5682_priv *rt5682)
+{
+ struct device *dev = rt5682->i2c_dev;
+ struct rt5682_platform_data *pdata = &rt5682->pdata;
+ struct clk_hw *dai_clk_hw;
+ int i, ret;
+
+ for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = { };
+ const struct clk_hw *parent;
+
+ dai_clk_hw = &rt5682->dai_clks_hw[i];
+
+ switch (i) {
+ case RT5682_DAI_WCLK_IDX:
+ /* Make MCLK the parent of WCLK */
+ if (rt5682->mclk) {
+ parent = __clk_get_hw(rt5682->mclk);
+ init.parent_hws = &parent;
+ init.num_parents = 1;
+ }
+ break;
+ case RT5682_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent = &rt5682->dai_clks_hw[RT5682_DAI_WCLK_IDX];
+ init.parent_hws = &parent;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ return -EINVAL;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &rt5682_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n",
+ init.name, ret);
+ return ret;
+ }
+
+ if (dev->of_node) {
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ dai_clk_hw);
+ if (ret)
+ return ret;
+ } else {
+ ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw,
+ init.name,
+ dev_name(dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_register_dai_clks);
+#endif /* CONFIG_COMMON_CLK */
+
+static int rt5682_probe(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct sdw_slave *slave;
+ unsigned long time;
+
+ rt5682->component = component;
+
+ if (rt5682->is_sdw) {
+ slave = rt5682->slave;
+ time = wait_for_completion_timeout(
+ &slave->initialization_complete,
+ msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin(dapm, "Vref2");
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static void rt5682_remove(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ rt5682_reset(rt5682);
+}
+
+#ifdef CONFIG_PM
+static int rt5682_suspend(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (rt5682->is_sdw)
+ return 0;
+
+ if (rt5682->irq)
+ disable_irq(rt5682->irq);
+
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682->jd_check_work);
+ if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ val = snd_soc_component_read(component,
+ RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK;
+
+ switch (val) {
+ case 0x1:
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK,
+ RT5682_SAR_SEL_MB1_NOSEL | RT5682_SAR_SEL_MB2_SEL);
+ break;
+ case 0x2:
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK,
+ RT5682_SAR_SEL_MB1_SEL | RT5682_SAR_SEL_MB2_NOSEL);
+ break;
+ default:
+ break;
+ }
+
+ /* enter SAR ADC power saving mode */
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK |
+ RT5682_SAR_SEL_MB1_MB2_MASK, 0);
+ usleep_range(5000, 6000);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
+ RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG);
+ usleep_range(10000, 12000);
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK,
+ RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV);
+ snd_soc_component_update_bits(component, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0);
+ }
+
+ regcache_cache_only(rt5682->regmap, true);
+ regcache_mark_dirty(rt5682->regmap);
+ return 0;
+}
+
+static int rt5682_resume(struct snd_soc_component *component)
+{
+ struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+
+ if (rt5682->is_sdw)
+ return 0;
+
+ regcache_cache_only(rt5682->regmap, false);
+ regcache_sync(rt5682->regmap);
+
+ if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1,
+ RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK,
+ RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO);
+ usleep_range(5000, 6000);
+ snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
+ RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK,
+ RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, RT5682_PWR_CBJ);
+ }
+
+ rt5682->jack_type = 0;
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682->jack_detect_work, msecs_to_jiffies(0));
+
+ if (rt5682->irq)
+ enable_irq(rt5682->irq);
+
+ return 0;
+}
+#else
+#define rt5682_suspend NULL
+#define rt5682_resume NULL
+#endif
+
+const struct snd_soc_dai_ops rt5682_aif1_dai_ops = {
+ .hw_params = rt5682_hw_params,
+ .set_fmt = rt5682_set_dai_fmt,
+ .set_tdm_slot = rt5682_set_tdm_slot,
+ .set_bclk_ratio = rt5682_set_bclk1_ratio,
+};
+EXPORT_SYMBOL_GPL(rt5682_aif1_dai_ops);
+
+const struct snd_soc_dai_ops rt5682_aif2_dai_ops = {
+ .hw_params = rt5682_hw_params,
+ .set_fmt = rt5682_set_dai_fmt,
+ .set_bclk_ratio = rt5682_set_bclk2_ratio,
+};
+EXPORT_SYMBOL_GPL(rt5682_aif2_dai_ops);
+
+const struct snd_soc_component_driver rt5682_soc_component_dev = {
+ .probe = rt5682_probe,
+ .remove = rt5682_remove,
+ .suspend = rt5682_suspend,
+ .resume = rt5682_resume,
+ .set_bias_level = rt5682_set_bias_level,
+ .controls = rt5682_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5682_snd_controls),
+ .dapm_widgets = rt5682_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5682_dapm_widgets),
+ .dapm_routes = rt5682_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5682_dapm_routes),
+ .set_sysclk = rt5682_set_component_sysclk,
+ .set_pll = rt5682_set_component_pll,
+ .set_jack = rt5682_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+EXPORT_SYMBOL_GPL(rt5682_soc_component_dev);
+
+int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
+{
+
+ device_property_read_u32(dev, "realtek,dmic1-data-pin",
+ &rt5682->pdata.dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic1-clk-pin",
+ &rt5682->pdata.dmic1_clk_pin);
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt5682->pdata.jd_src);
+ device_property_read_u32(dev, "realtek,btndet-delay",
+ &rt5682->pdata.btndet_delay);
+ device_property_read_u32(dev, "realtek,dmic-clk-rate-hz",
+ &rt5682->pdata.dmic_clk_rate);
+ device_property_read_u32(dev, "realtek,dmic-delay-ms",
+ &rt5682->pdata.dmic_delay);
+
+ if (device_property_read_string_array(dev, "clock-output-names",
+ rt5682->pdata.dai_clk_names,
+ RT5682_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ rt5682->pdata.dai_clk_names[RT5682_DAI_WCLK_IDX],
+ rt5682->pdata.dai_clk_names[RT5682_DAI_BCLK_IDX]);
+
+ rt5682->pdata.dmic_clk_driving_high = device_property_read_bool(dev,
+ "realtek,dmic-clk-driving-high");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_parse_dt);
+
+int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev)
+{
+ rt5682->ldo1_en = devm_gpiod_get_optional(dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5682->ldo1_en)) {
+ dev_err(dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5682->ldo1_en);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_get_ldo1);
+
+void rt5682_calibrate(struct rt5682_priv *rt5682)
+{
+ int value, count;
+
+ mutex_lock(&rt5682->calibrate_mutex);
+
+ rt5682_reset(rt5682);
+ regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f);
+ regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af);
+ usleep_range(15000, 20000);
+ regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300);
+ regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000);
+ regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100);
+ regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800);
+ regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000);
+ if (rt5682->ve_ic)
+ regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x7005);
+ else
+ regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005);
+ regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1);
+ regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311);
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00);
+
+ regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5682->regmap, RT5682_HP_CALIB_STA_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ dev_err(rt5682->component->dev, "HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0x002f);
+ regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+ regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000);
+ regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
+ regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
+ if (rt5682->ve_ic)
+ regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x2005);
+ else
+ regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
+ regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c);
+
+ mutex_unlock(&rt5682->calibrate_mutex);
+}
+EXPORT_SYMBOL_GPL(rt5682_calibrate);
+
+MODULE_DESCRIPTION("ASoC RT5682 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
new file mode 100644
index 000000000000..de43a5d99403
--- /dev/null
+++ b/sound/soc/codecs/rt5682.h
@@ -0,0 +1,1499 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2018 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5682_H__
+#define __RT5682_H__
+
+#include <sound/rt5682.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define DEVICE_ID 0x6530
+
+/* Info */
+#define RT5682_RESET 0x0000
+#define RT5682_INT_DEVICE_ID 0x00f9
+#define RT5682_VERSION_ID 0x00fd
+#define RT5682_VENDOR_ID 0x00fe
+#define RT5682_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5682_HP_CTRL_1 0x0002
+#define RT5682_HP_CTRL_2 0x0003
+#define RT5682_HPL_GAIN 0x0005
+#define RT5682_HPR_GAIN 0x0006
+
+#define RT5682_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5682_CBJ_BST_CTRL 0x000b
+#define RT5682_CBJ_CTRL_1 0x0010
+#define RT5682_CBJ_CTRL_2 0x0011
+#define RT5682_CBJ_CTRL_3 0x0012
+#define RT5682_CBJ_CTRL_4 0x0013
+#define RT5682_CBJ_CTRL_5 0x0014
+#define RT5682_CBJ_CTRL_6 0x0015
+#define RT5682_CBJ_CTRL_7 0x0016
+/* I/O - ADC/DAC/DMIC */
+#define RT5682_DAC1_DIG_VOL 0x0019
+#define RT5682_STO1_ADC_DIG_VOL 0x001c
+#define RT5682_STO1_ADC_BOOST 0x001f
+#define RT5682_HP_IMP_GAIN_1 0x0022
+#define RT5682_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5682_SIDETONE_CTRL 0x0024
+#define RT5682_STO1_ADC_MIXER 0x0026
+#define RT5682_AD_DA_MIXER 0x0029
+#define RT5682_STO1_DAC_MIXER 0x002a
+#define RT5682_A_DAC1_MUX 0x002b
+#define RT5682_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5682_REC_MIXER 0x003c
+#define RT5682_CAL_REC 0x0044
+#define RT5682_ALC_BACK_GAIN 0x0049
+/* Power */
+#define RT5682_PWR_DIG_1 0x0061
+#define RT5682_PWR_DIG_2 0x0062
+#define RT5682_PWR_ANLG_1 0x0063
+#define RT5682_PWR_ANLG_2 0x0064
+#define RT5682_PWR_ANLG_3 0x0065
+#define RT5682_PWR_MIXER 0x0066
+#define RT5682_PWR_VOL 0x0067
+/* Clock Detect */
+#define RT5682_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5682_RESET_LPF_CTRL 0x006c
+#define RT5682_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5682_DMIC_CTRL_1 0x006e
+/* Format - ADC/DAC */
+#define RT5682_I2S1_SDP 0x0070
+#define RT5682_I2S2_SDP 0x0071
+#define RT5682_ADDA_CLK_1 0x0073
+#define RT5682_ADDA_CLK_2 0x0074
+#define RT5682_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5682_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5682_TDM_CTRL 0x0079
+#define RT5682_TDM_ADDA_CTRL_1 0x007a
+#define RT5682_TDM_ADDA_CTRL_2 0x007b
+#define RT5682_DATA_SEL_CTRL_1 0x007c
+#define RT5682_TDM_TCON_CTRL 0x007e
+/* Function - Analog */
+#define RT5682_GLB_CLK 0x0080
+#define RT5682_PLL_CTRL_1 0x0081
+#define RT5682_PLL_CTRL_2 0x0082
+#define RT5682_PLL_TRACK_1 0x0083
+#define RT5682_PLL_TRACK_2 0x0084
+#define RT5682_PLL_TRACK_3 0x0085
+#define RT5682_PLL_TRACK_4 0x0086
+#define RT5682_PLL_TRACK_5 0x0087
+#define RT5682_PLL_TRACK_6 0x0088
+#define RT5682_PLL_TRACK_11 0x008c
+#define RT5682_SDW_REF_CLK 0x008d
+#define RT5682_DEPOP_1 0x008e
+#define RT5682_DEPOP_2 0x008f
+#define RT5682_HP_CHARGE_PUMP_1 0x0091
+#define RT5682_HP_CHARGE_PUMP_2 0x0092
+#define RT5682_MICBIAS_1 0x0093
+#define RT5682_MICBIAS_2 0x0094
+#define RT5682_PLL_TRACK_12 0x0098
+#define RT5682_PLL_TRACK_14 0x009a
+#define RT5682_PLL2_CTRL_1 0x009b
+#define RT5682_PLL2_CTRL_2 0x009c
+#define RT5682_PLL2_CTRL_3 0x009d
+#define RT5682_PLL2_CTRL_4 0x009e
+#define RT5682_RC_CLK_CTRL 0x009f
+#define RT5682_I2S_M_CLK_CTRL_1 0x00a0
+#define RT5682_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5682_I2S2_F_DIV_CTRL_2 0x00a4
+/* Function - Digital */
+#define RT5682_EQ_CTRL_1 0x00ae
+#define RT5682_EQ_CTRL_2 0x00af
+#define RT5682_IRQ_CTRL_1 0x00b6
+#define RT5682_IRQ_CTRL_2 0x00b7
+#define RT5682_IRQ_CTRL_3 0x00b8
+#define RT5682_IRQ_CTRL_4 0x00b9
+#define RT5682_INT_ST_1 0x00be
+#define RT5682_GPIO_CTRL_1 0x00c0
+#define RT5682_GPIO_CTRL_2 0x00c1
+#define RT5682_GPIO_CTRL_3 0x00c2
+#define RT5682_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5682_HP_AMP_DET_CTRL_2 0x00d1
+#define RT5682_MID_HP_AMP_DET 0x00d2
+#define RT5682_LOW_HP_AMP_DET 0x00d3
+#define RT5682_DELAY_BUF_CTRL 0x00d4
+#define RT5682_SV_ZCD_1 0x00d9
+#define RT5682_SV_ZCD_2 0x00da
+#define RT5682_IL_CMD_1 0x00db
+#define RT5682_IL_CMD_2 0x00dc
+#define RT5682_IL_CMD_3 0x00dd
+#define RT5682_IL_CMD_4 0x00de
+#define RT5682_IL_CMD_5 0x00df
+#define RT5682_IL_CMD_6 0x00e0
+#define RT5682_4BTN_IL_CMD_1 0x00e2
+#define RT5682_4BTN_IL_CMD_2 0x00e3
+#define RT5682_4BTN_IL_CMD_3 0x00e4
+#define RT5682_4BTN_IL_CMD_4 0x00e5
+#define RT5682_4BTN_IL_CMD_5 0x00e6
+#define RT5682_4BTN_IL_CMD_6 0x00e7
+#define RT5682_4BTN_IL_CMD_7 0x00e8
+
+#define RT5682_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5682_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5682_AJD1_CTRL 0x00f0
+#define RT5682_JD1_THD 0x00f1
+#define RT5682_JD2_THD 0x00f2
+#define RT5682_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5682_DUMMY_1 0x00fa
+#define RT5682_DUMMY_2 0x00fb
+#define RT5682_DUMMY_3 0x00fc
+
+#define RT5682_DAC_ADC_DIG_VOL1 0x0100
+#define RT5682_BIAS_CUR_CTRL_2 0x010b
+#define RT5682_BIAS_CUR_CTRL_3 0x010c
+#define RT5682_BIAS_CUR_CTRL_4 0x010d
+#define RT5682_BIAS_CUR_CTRL_5 0x010e
+#define RT5682_BIAS_CUR_CTRL_6 0x010f
+#define RT5682_BIAS_CUR_CTRL_7 0x0110
+#define RT5682_BIAS_CUR_CTRL_8 0x0111
+#define RT5682_BIAS_CUR_CTRL_9 0x0112
+#define RT5682_BIAS_CUR_CTRL_10 0x0113
+#define RT5682_VREF_REC_OP_FB_CAP_CTRL 0x0117
+#define RT5682_CHARGE_PUMP_1 0x0125
+#define RT5682_DIG_IN_CTRL_1 0x0132
+#define RT5682_PAD_DRIVING_CTRL 0x0136
+#define RT5682_SOFT_RAMP_DEPOP 0x0138
+#define RT5682_CHOP_DAC 0x013a
+#define RT5682_CHOP_ADC 0x013b
+#define RT5682_CALIB_ADC_CTRL 0x013c
+#define RT5682_VOL_TEST 0x013f
+#define RT5682_SPKVDD_DET_STA 0x0142
+#define RT5682_TEST_MODE_CTRL_1 0x0145
+#define RT5682_TEST_MODE_CTRL_2 0x0146
+#define RT5682_TEST_MODE_CTRL_3 0x0147
+#define RT5682_TEST_MODE_CTRL_4 0x0148
+#define RT5682_TEST_MODE_CTRL_5 0x0149
+#define RT5682_PLL1_INTERNAL 0x0150
+#define RT5682_PLL2_INTERNAL 0x0156
+#define RT5682_STO_NG2_CTRL_1 0x0160
+#define RT5682_STO_NG2_CTRL_2 0x0161
+#define RT5682_STO_NG2_CTRL_3 0x0162
+#define RT5682_STO_NG2_CTRL_4 0x0163
+#define RT5682_STO_NG2_CTRL_5 0x0164
+#define RT5682_STO_NG2_CTRL_6 0x0165
+#define RT5682_STO_NG2_CTRL_7 0x0166
+#define RT5682_STO_NG2_CTRL_8 0x0167
+#define RT5682_STO_NG2_CTRL_9 0x0168
+#define RT5682_STO_NG2_CTRL_10 0x0169
+#define RT5682_STO1_DAC_SIL_DET 0x0190
+#define RT5682_SIL_PSV_CTRL1 0x0194
+#define RT5682_SIL_PSV_CTRL2 0x0195
+#define RT5682_SIL_PSV_CTRL3 0x0197
+#define RT5682_SIL_PSV_CTRL4 0x0198
+#define RT5682_SIL_PSV_CTRL5 0x0199
+#define RT5682_HP_IMP_SENS_CTRL_01 0x01af
+#define RT5682_HP_IMP_SENS_CTRL_02 0x01b0
+#define RT5682_HP_IMP_SENS_CTRL_03 0x01b1
+#define RT5682_HP_IMP_SENS_CTRL_04 0x01b2
+#define RT5682_HP_IMP_SENS_CTRL_05 0x01b3
+#define RT5682_HP_IMP_SENS_CTRL_06 0x01b4
+#define RT5682_HP_IMP_SENS_CTRL_07 0x01b5
+#define RT5682_HP_IMP_SENS_CTRL_08 0x01b6
+#define RT5682_HP_IMP_SENS_CTRL_09 0x01b7
+#define RT5682_HP_IMP_SENS_CTRL_10 0x01b8
+#define RT5682_HP_IMP_SENS_CTRL_11 0x01b9
+#define RT5682_HP_IMP_SENS_CTRL_12 0x01ba
+#define RT5682_HP_IMP_SENS_CTRL_13 0x01bb
+#define RT5682_HP_IMP_SENS_CTRL_14 0x01bc
+#define RT5682_HP_IMP_SENS_CTRL_15 0x01bd
+#define RT5682_HP_IMP_SENS_CTRL_16 0x01be
+#define RT5682_HP_IMP_SENS_CTRL_17 0x01bf
+#define RT5682_HP_IMP_SENS_CTRL_18 0x01c0
+#define RT5682_HP_IMP_SENS_CTRL_19 0x01c1
+#define RT5682_HP_IMP_SENS_CTRL_20 0x01c2
+#define RT5682_HP_IMP_SENS_CTRL_21 0x01c3
+#define RT5682_HP_IMP_SENS_CTRL_22 0x01c4
+#define RT5682_HP_IMP_SENS_CTRL_23 0x01c5
+#define RT5682_HP_IMP_SENS_CTRL_24 0x01c6
+#define RT5682_HP_IMP_SENS_CTRL_25 0x01c7
+#define RT5682_HP_IMP_SENS_CTRL_26 0x01c8
+#define RT5682_HP_IMP_SENS_CTRL_27 0x01c9
+#define RT5682_HP_IMP_SENS_CTRL_28 0x01ca
+#define RT5682_HP_IMP_SENS_CTRL_29 0x01cb
+#define RT5682_HP_IMP_SENS_CTRL_30 0x01cc
+#define RT5682_HP_IMP_SENS_CTRL_31 0x01cd
+#define RT5682_HP_IMP_SENS_CTRL_32 0x01ce
+#define RT5682_HP_IMP_SENS_CTRL_33 0x01cf
+#define RT5682_HP_IMP_SENS_CTRL_34 0x01d0
+#define RT5682_HP_IMP_SENS_CTRL_35 0x01d1
+#define RT5682_HP_IMP_SENS_CTRL_36 0x01d2
+#define RT5682_HP_IMP_SENS_CTRL_37 0x01d3
+#define RT5682_HP_IMP_SENS_CTRL_38 0x01d4
+#define RT5682_HP_IMP_SENS_CTRL_39 0x01d5
+#define RT5682_HP_IMP_SENS_CTRL_40 0x01d6
+#define RT5682_HP_IMP_SENS_CTRL_41 0x01d7
+#define RT5682_HP_IMP_SENS_CTRL_42 0x01d8
+#define RT5682_HP_IMP_SENS_CTRL_43 0x01d9
+#define RT5682_HP_LOGIC_CTRL_1 0x01da
+#define RT5682_HP_LOGIC_CTRL_2 0x01db
+#define RT5682_HP_LOGIC_CTRL_3 0x01dc
+#define RT5682_HP_CALIB_CTRL_1 0x01de
+#define RT5682_HP_CALIB_CTRL_2 0x01df
+#define RT5682_HP_CALIB_CTRL_3 0x01e0
+#define RT5682_HP_CALIB_CTRL_4 0x01e1
+#define RT5682_HP_CALIB_CTRL_5 0x01e2
+#define RT5682_HP_CALIB_CTRL_6 0x01e3
+#define RT5682_HP_CALIB_CTRL_7 0x01e4
+#define RT5682_HP_CALIB_CTRL_9 0x01e6
+#define RT5682_HP_CALIB_CTRL_10 0x01e7
+#define RT5682_HP_CALIB_CTRL_11 0x01e8
+#define RT5682_HP_CALIB_STA_1 0x01ea
+#define RT5682_HP_CALIB_STA_2 0x01eb
+#define RT5682_HP_CALIB_STA_3 0x01ec
+#define RT5682_HP_CALIB_STA_4 0x01ed
+#define RT5682_HP_CALIB_STA_5 0x01ee
+#define RT5682_HP_CALIB_STA_6 0x01ef
+#define RT5682_HP_CALIB_STA_7 0x01f0
+#define RT5682_HP_CALIB_STA_8 0x01f1
+#define RT5682_HP_CALIB_STA_9 0x01f2
+#define RT5682_HP_CALIB_STA_10 0x01f3
+#define RT5682_HP_CALIB_STA_11 0x01f4
+#define RT5682_SAR_IL_CMD_1 0x0210
+#define RT5682_SAR_IL_CMD_2 0x0211
+#define RT5682_SAR_IL_CMD_3 0x0212
+#define RT5682_SAR_IL_CMD_4 0x0213
+#define RT5682_SAR_IL_CMD_5 0x0214
+#define RT5682_SAR_IL_CMD_6 0x0215
+#define RT5682_SAR_IL_CMD_7 0x0216
+#define RT5682_SAR_IL_CMD_8 0x0217
+#define RT5682_SAR_IL_CMD_9 0x0218
+#define RT5682_SAR_IL_CMD_10 0x0219
+#define RT5682_SAR_IL_CMD_11 0x021a
+#define RT5682_SAR_IL_CMD_12 0x021b
+#define RT5682_SAR_IL_CMD_13 0x021c
+#define RT5682_EFUSE_CTRL_1 0x0250
+#define RT5682_EFUSE_CTRL_2 0x0251
+#define RT5682_EFUSE_CTRL_3 0x0252
+#define RT5682_EFUSE_CTRL_4 0x0253
+#define RT5682_EFUSE_CTRL_5 0x0254
+#define RT5682_EFUSE_CTRL_6 0x0255
+#define RT5682_EFUSE_CTRL_7 0x0256
+#define RT5682_EFUSE_CTRL_8 0x0257
+#define RT5682_EFUSE_CTRL_9 0x0258
+#define RT5682_EFUSE_CTRL_10 0x0259
+#define RT5682_EFUSE_CTRL_11 0x025a
+#define RT5682_JD_TOP_VC_VTRL 0x0270
+#define RT5682_DRC1_CTRL_0 0x02ff
+#define RT5682_DRC1_CTRL_1 0x0300
+#define RT5682_DRC1_CTRL_2 0x0301
+#define RT5682_DRC1_CTRL_3 0x0302
+#define RT5682_DRC1_CTRL_4 0x0303
+#define RT5682_DRC1_CTRL_5 0x0304
+#define RT5682_DRC1_CTRL_6 0x0305
+#define RT5682_DRC1_HARD_LMT_CTRL_1 0x0306
+#define RT5682_DRC1_HARD_LMT_CTRL_2 0x0307
+#define RT5682_DRC1_PRIV_1 0x0310
+#define RT5682_DRC1_PRIV_2 0x0311
+#define RT5682_DRC1_PRIV_3 0x0312
+#define RT5682_DRC1_PRIV_4 0x0313
+#define RT5682_DRC1_PRIV_5 0x0314
+#define RT5682_DRC1_PRIV_6 0x0315
+#define RT5682_DRC1_PRIV_7 0x0316
+#define RT5682_DRC1_PRIV_8 0x0317
+#define RT5682_EQ_AUTO_RCV_CTRL1 0x03c0
+#define RT5682_EQ_AUTO_RCV_CTRL2 0x03c1
+#define RT5682_EQ_AUTO_RCV_CTRL3 0x03c2
+#define RT5682_EQ_AUTO_RCV_CTRL4 0x03c3
+#define RT5682_EQ_AUTO_RCV_CTRL5 0x03c4
+#define RT5682_EQ_AUTO_RCV_CTRL6 0x03c5
+#define RT5682_EQ_AUTO_RCV_CTRL7 0x03c6
+#define RT5682_EQ_AUTO_RCV_CTRL8 0x03c7
+#define RT5682_EQ_AUTO_RCV_CTRL9 0x03c8
+#define RT5682_EQ_AUTO_RCV_CTRL10 0x03c9
+#define RT5682_EQ_AUTO_RCV_CTRL11 0x03ca
+#define RT5682_EQ_AUTO_RCV_CTRL12 0x03cb
+#define RT5682_EQ_AUTO_RCV_CTRL13 0x03cc
+#define RT5682_ADC_L_EQ_LPF1_A1 0x03d0
+#define RT5682_R_EQ_LPF1_A1 0x03d1
+#define RT5682_L_EQ_LPF1_H0 0x03d2
+#define RT5682_R_EQ_LPF1_H0 0x03d3
+#define RT5682_L_EQ_BPF1_A1 0x03d4
+#define RT5682_R_EQ_BPF1_A1 0x03d5
+#define RT5682_L_EQ_BPF1_A2 0x03d6
+#define RT5682_R_EQ_BPF1_A2 0x03d7
+#define RT5682_L_EQ_BPF1_H0 0x03d8
+#define RT5682_R_EQ_BPF1_H0 0x03d9
+#define RT5682_L_EQ_BPF2_A1 0x03da
+#define RT5682_R_EQ_BPF2_A1 0x03db
+#define RT5682_L_EQ_BPF2_A2 0x03dc
+#define RT5682_R_EQ_BPF2_A2 0x03dd
+#define RT5682_L_EQ_BPF2_H0 0x03de
+#define RT5682_R_EQ_BPF2_H0 0x03df
+#define RT5682_L_EQ_BPF3_A1 0x03e0
+#define RT5682_R_EQ_BPF3_A1 0x03e1
+#define RT5682_L_EQ_BPF3_A2 0x03e2
+#define RT5682_R_EQ_BPF3_A2 0x03e3
+#define RT5682_L_EQ_BPF3_H0 0x03e4
+#define RT5682_R_EQ_BPF3_H0 0x03e5
+#define RT5682_L_EQ_BPF4_A1 0x03e6
+#define RT5682_R_EQ_BPF4_A1 0x03e7
+#define RT5682_L_EQ_BPF4_A2 0x03e8
+#define RT5682_R_EQ_BPF4_A2 0x03e9
+#define RT5682_L_EQ_BPF4_H0 0x03ea
+#define RT5682_R_EQ_BPF4_H0 0x03eb
+#define RT5682_L_EQ_HPF1_A1 0x03ec
+#define RT5682_R_EQ_HPF1_A1 0x03ed
+#define RT5682_L_EQ_HPF1_H0 0x03ee
+#define RT5682_R_EQ_HPF1_H0 0x03ef
+#define RT5682_L_EQ_PRE_VOL 0x03f0
+#define RT5682_R_EQ_PRE_VOL 0x03f1
+#define RT5682_L_EQ_POST_VOL 0x03f2
+#define RT5682_R_EQ_POST_VOL 0x03f3
+#define RT5682_I2C_MODE 0xffff
+
+
+/* global definition */
+#define RT5682_L_MUTE (0x1 << 15)
+#define RT5682_L_MUTE_SFT 15
+#define RT5682_VOL_L_MUTE (0x1 << 14)
+#define RT5682_VOL_L_SFT 14
+#define RT5682_R_MUTE (0x1 << 7)
+#define RT5682_R_MUTE_SFT 7
+#define RT5682_VOL_R_MUTE (0x1 << 6)
+#define RT5682_VOL_R_SFT 6
+#define RT5682_L_VOL_MASK (0x3f << 8)
+#define RT5682_L_VOL_SFT 8
+#define RT5682_R_VOL_MASK (0x3f)
+#define RT5682_R_VOL_SFT 0
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5682_HP_C2_DAC_AMP_MUTE_SFT 15
+#define RT5682_HP_C2_DAC_AMP_MUTE (0x1 << 15)
+#define RT5682_HP_C2_DAC_L_EN_SFT 14
+#define RT5682_HP_C2_DAC_L_EN (0x1 << 14)
+#define RT5682_HP_C2_DAC_R_EN_SFT 13
+#define RT5682_HP_C2_DAC_R_EN (0x1 << 13)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5682_G_HP (0xf << 8)
+#define RT5682_G_HP_SFT 8
+#define RT5682_G_STO_DA_DMIX (0xf)
+#define RT5682_G_STO_DA_SFT 0
+
+/* CBJ Control (0x000b) */
+#define RT5682_BST_CBJ_MASK (0xf << 8)
+#define RT5682_BST_CBJ_SFT 8
+
+/* Embeeded Jack and Type Detection Control 1 (0x0010) */
+#define RT5682_EMB_JD_EN (0x1 << 15)
+#define RT5682_EMB_JD_EN_SFT 15
+#define RT5682_EMB_JD_RST (0x1 << 14)
+#define RT5682_JD_MODE (0x1 << 13)
+#define RT5682_JD_MODE_SFT 13
+#define RT5682_DET_TYPE (0x1 << 12)
+#define RT5682_DET_TYPE_SFT 12
+#define RT5682_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5682_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5682_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5682_EXT_JD_DIG (0x1 << 9)
+#define RT5682_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5682_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5682_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5682_FAST_OFF_MASK (0x1 << 7)
+#define RT5682_FAST_OFF_EN (0x1 << 7)
+#define RT5682_FAST_OFF_DIS (0x0 << 7)
+#define RT5682_VREF_POW_MASK (0x1 << 6)
+#define RT5682_VREF_POW_FSM (0x0 << 6)
+#define RT5682_VREF_POW_REG (0x1 << 6)
+#define RT5682_MB1_PATH_MASK (0x1 << 5)
+#define RT5682_CTRL_MB1_REG (0x1 << 5)
+#define RT5682_CTRL_MB1_FSM (0x0 << 5)
+#define RT5682_MB2_PATH_MASK (0x1 << 4)
+#define RT5682_CTRL_MB2_REG (0x1 << 4)
+#define RT5682_CTRL_MB2_FSM (0x0 << 4)
+#define RT5682_TRIG_JD_MASK (0x1 << 3)
+#define RT5682_TRIG_JD_HIGH (0x1 << 3)
+#define RT5682_TRIG_JD_LOW (0x0 << 3)
+#define RT5682_MIC_CAP_MASK (0x1 << 1)
+#define RT5682_MIC_CAP_HS (0x1 << 1)
+#define RT5682_MIC_CAP_HP (0x0 << 1)
+#define RT5682_MIC_CAP_SRC_MASK (0x1)
+#define RT5682_MIC_CAP_SRC_REG (0x1)
+#define RT5682_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 2 (0x0011) */
+#define RT5682_EXT_JD_SRC (0x7 << 4)
+#define RT5682_EXT_JD_SRC_SFT 4
+#define RT5682_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5682_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5682_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5682_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5682_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5682_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 3 (0x0012) */
+#define RT5682_CBJ_IN_BUF_EN (0x1 << 7)
+
+/* Combo Jack and Type Detection Control 4 (0x0013) */
+#define RT5682_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5682_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5682_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5682_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5682_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5682_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5682_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5682_DAC_L1_VOL_SFT 8
+#define RT5682_DAC_R1_VOL_MASK (0xff)
+#define RT5682_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5682_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5682_ADC_L_VOL_SFT 8
+#define RT5682_ADC_R_VOL_MASK (0x7f)
+#define RT5682_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5682_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5682_STO1_ADC_L_BST_SFT 14
+#define RT5682_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5682_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5682_ST_SRC_SEL (0x1 << 8)
+#define RT5682_ST_SRC_SFT 8
+#define RT5682_ST_EN_MASK (0x1 << 6)
+#define RT5682_ST_DIS (0x0 << 6)
+#define RT5682_ST_EN (0x1 << 6)
+#define RT5682_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5682_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5682_M_STO1_ADC_L1_SFT 15
+#define RT5682_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5682_M_STO1_ADC_L2_SFT 14
+#define RT5682_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5682_STO1_ADC1L_SRC_SFT 13
+#define RT5682_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5682_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5682_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5682_STO1_ADC2L_SRC_SFT 12
+#define RT5682_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5682_STO1_ADCL_SRC_SFT 10
+#define RT5682_STO1_DD_L_SRC_MASK (0x1 << 9)
+#define RT5682_STO1_DD_L_SRC_SFT 9
+#define RT5682_STO1_DMIC_SRC_MASK (0x1 << 8)
+#define RT5682_STO1_DMIC_SRC_SFT 8
+#define RT5682_STO1_DMIC_SRC_DMIC2 (0x1 << 8)
+#define RT5682_STO1_DMIC_SRC_DMIC1 (0x0 << 8)
+#define RT5682_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5682_M_STO1_ADC_R1_SFT 7
+#define RT5682_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5682_M_STO1_ADC_R2_SFT 6
+#define RT5682_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5682_STO1_ADC1R_SRC_SFT 5
+#define RT5682_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5682_STO1_ADC2R_SRC_SFT 4
+#define RT5682_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5682_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5682_M_ADCMIX_L (0x1 << 15)
+#define RT5682_M_ADCMIX_L_SFT 15
+#define RT5682_M_DAC1_L (0x1 << 14)
+#define RT5682_M_DAC1_L_SFT 14
+#define RT5682_DAC1_R_SEL_MASK (0x1 << 10)
+#define RT5682_DAC1_R_SEL_SFT 10
+#define RT5682_DAC1_L_SEL_MASK (0x1 << 8)
+#define RT5682_DAC1_L_SEL_SFT 8
+#define RT5682_M_ADCMIX_R (0x1 << 7)
+#define RT5682_M_ADCMIX_R_SFT 7
+#define RT5682_M_DAC1_R (0x1 << 6)
+#define RT5682_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5682_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5682_M_DAC_L1_STO_L_SFT 15
+#define RT5682_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5682_G_DAC_L1_STO_L_SFT 14
+#define RT5682_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5682_M_DAC_R1_STO_L_SFT 13
+#define RT5682_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5682_G_DAC_R1_STO_L_SFT 12
+#define RT5682_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5682_M_DAC_L1_STO_R_SFT 7
+#define RT5682_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5682_G_DAC_L1_STO_R_SFT 6
+#define RT5682_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5682_M_DAC_R1_STO_R_SFT 5
+#define RT5682_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5682_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5682_M_ST_STO_L (0x1 << 9)
+#define RT5682_M_ST_STO_L_SFT 9
+#define RT5682_M_ST_STO_R (0x1 << 8)
+#define RT5682_M_ST_STO_R_SFT 8
+#define RT5682_DAC_L1_SRC_MASK (0x3 << 4)
+#define RT5682_A_DACL1_SFT 4
+#define RT5682_DAC_R1_SRC_MASK (0x3)
+#define RT5682_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5682_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5682_IF2_ADC_SEL_SFT 0
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5682_G_CBJ_RM1_L (0x7 << 10)
+#define RT5682_G_CBJ_RM1_L_SFT 10
+#define RT5682_M_CBJ_RM1_L (0x1 << 7)
+#define RT5682_M_CBJ_RM1_L_SFT 7
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5682_PWR_I2S1 (0x1 << 15)
+#define RT5682_PWR_I2S1_BIT 15
+#define RT5682_PWR_I2S2 (0x1 << 14)
+#define RT5682_PWR_I2S2_BIT 14
+#define RT5682_PWR_DAC_L1 (0x1 << 11)
+#define RT5682_PWR_DAC_L1_BIT 11
+#define RT5682_PWR_DAC_R1 (0x1 << 10)
+#define RT5682_PWR_DAC_R1_BIT 10
+#define RT5682_PWR_LDO (0x1 << 8)
+#define RT5682_PWR_LDO_BIT 8
+#define RT5682_PWR_ADC_L1 (0x1 << 4)
+#define RT5682_PWR_ADC_L1_BIT 4
+#define RT5682_PWR_ADC_R1 (0x1 << 3)
+#define RT5682_PWR_ADC_R1_BIT 3
+#define RT5682_DIG_GATE_CTRL (0x1 << 0)
+#define RT5682_DIG_GATE_CTRL_SFT 0
+
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5682_PWR_ADC_S1F (0x1 << 15)
+#define RT5682_PWR_ADC_S1F_BIT 15
+#define RT5682_PWR_DAC_S1F (0x1 << 10)
+#define RT5682_PWR_DAC_S1F_BIT 10
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5682_PWR_VREF1 (0x1 << 15)
+#define RT5682_PWR_VREF1_BIT 15
+#define RT5682_PWR_FV1 (0x1 << 14)
+#define RT5682_PWR_FV1_BIT 14
+#define RT5682_PWR_VREF2 (0x1 << 13)
+#define RT5682_PWR_VREF2_BIT 13
+#define RT5682_PWR_FV2 (0x1 << 12)
+#define RT5682_PWR_FV2_BIT 12
+#define RT5682_LDO1_DBG_MASK (0x3 << 10)
+#define RT5682_PWR_MB (0x1 << 9)
+#define RT5682_PWR_MB_BIT 9
+#define RT5682_PWR_BG (0x1 << 7)
+#define RT5682_PWR_BG_BIT 7
+#define RT5682_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5682_LDO1_BYPASS (0x1 << 6)
+#define RT5682_LDO1_NOT_BYPASS (0x0 << 6)
+#define RT5682_PWR_MA_BIT 6
+#define RT5682_LDO1_DVO_MASK (0x3 << 4)
+#define RT5682_LDO1_DVO_09 (0x0 << 4)
+#define RT5682_LDO1_DVO_10 (0x1 << 4)
+#define RT5682_LDO1_DVO_12 (0x2 << 4)
+#define RT5682_LDO1_DVO_14 (0x3 << 4)
+#define RT5682_HP_DRIVER_MASK (0x3 << 2)
+#define RT5682_HP_DRIVER_1X (0x0 << 2)
+#define RT5682_HP_DRIVER_3X (0x1 << 2)
+#define RT5682_HP_DRIVER_5X (0x3 << 2)
+#define RT5682_PWR_HA_L (0x1 << 1)
+#define RT5682_PWR_HA_L_BIT 1
+#define RT5682_PWR_HA_R (0x1 << 0)
+#define RT5682_PWR_HA_R_BIT 0
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5682_PWR_MB1 (0x1 << 11)
+#define RT5682_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5682_PWR_MB1_BIT 11
+#define RT5682_PWR_MB2 (0x1 << 10)
+#define RT5682_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5682_PWR_MB2_BIT 10
+#define RT5682_PWR_JDH (0x1 << 3)
+#define RT5682_PWR_JDH_BIT 3
+#define RT5682_PWR_JDL (0x1 << 2)
+#define RT5682_PWR_JDL_BIT 2
+#define RT5682_PWR_RM1_L (0x1 << 1)
+#define RT5682_PWR_RM1_L_BIT 1
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5682_PWR_CBJ (0x1 << 9)
+#define RT5682_PWR_CBJ_BIT 9
+#define RT5682_PWR_PLL (0x1 << 6)
+#define RT5682_PWR_PLL_BIT 6
+#define RT5682_PWR_PLL2B (0x1 << 5)
+#define RT5682_PWR_PLL2B_BIT 5
+#define RT5682_PWR_PLL2F (0x1 << 4)
+#define RT5682_PWR_PLL2F_BIT 4
+#define RT5682_PWR_LDO2 (0x1 << 2)
+#define RT5682_PWR_LDO2_BIT 2
+#define RT5682_PWR_DET_SPKVDD (0x1 << 1)
+#define RT5682_PWR_DET_SPKVDD_BIT 1
+
+/* Power Management for Mixer (0x0066) */
+#define RT5682_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5682_PWR_STO1_DAC_L_BIT 5
+#define RT5682_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5682_PWR_STO1_DAC_R_BIT 4
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5682_SYS_CLK_DET (0x1 << 15)
+#define RT5682_SYS_CLK_DET_SFT 15
+#define RT5682_PLL1_CLK_DET (0x1 << 14)
+#define RT5682_PLL1_CLK_DET_SFT 14
+#define RT5682_PLL2_CLK_DET (0x1 << 13)
+#define RT5682_PLL2_CLK_DET_SFT 13
+#define RT5682_POW_CLK_DET2_SFT 8
+#define RT5682_POW_CLK_DET_SFT 0
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5682_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5682_DMIC_1_EN_SFT 15
+#define RT5682_DMIC_1_DIS (0x0 << 15)
+#define RT5682_DMIC_1_EN (0x1 << 15)
+#define RT5682_FIFO_CLK_DIV_MASK (0x7 << 12)
+#define RT5682_FIFO_CLK_DIV_2 (0x1 << 12)
+#define RT5682_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5682_DMIC_1_DP_SFT 4
+#define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5682_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5682_DMIC_CLK_MASK (0xf << 0)
+#define RT5682_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5682_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5682_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5682_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5682_SEL_ADCDAT_SFT 15
+#define RT5682_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5682_I2S1_TX_CHL_SFT 12
+#define RT5682_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5682_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5682_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5682_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5682_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5682_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5682_I2S1_RX_CHL_SFT 8
+#define RT5682_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5682_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5682_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5682_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5682_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5682_I2S1_MONO_MASK (0x1 << 7)
+#define RT5682_I2S1_MONO_EN (0x1 << 7)
+#define RT5682_I2S1_MONO_DIS (0x0 << 7)
+#define RT5682_I2S2_MONO_MASK (0x1 << 6)
+#define RT5682_I2S2_MONO_EN (0x1 << 6)
+#define RT5682_I2S2_MONO_DIS (0x0 << 6)
+#define RT5682_I2S1_DL_MASK (0x7 << 4)
+#define RT5682_I2S1_DL_SFT 4
+#define RT5682_I2S1_DL_16 (0x0 << 4)
+#define RT5682_I2S1_DL_20 (0x1 << 4)
+#define RT5682_I2S1_DL_24 (0x2 << 4)
+#define RT5682_I2S1_DL_32 (0x3 << 4)
+#define RT5682_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */
+#define RT5682_I2S2_MS_MASK (0x1 << 15)
+#define RT5682_I2S2_MS_SFT 15
+#define RT5682_I2S2_MS_M (0x0 << 15)
+#define RT5682_I2S2_MS_S (0x1 << 15)
+#define RT5682_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5682_I2S2_PIN_CFG_SFT 14
+#define RT5682_I2S2_CLK_SEL_MASK (0x1 << 11)
+#define RT5682_I2S2_CLK_SEL_SFT 11
+#define RT5682_I2S2_OUT_MASK (0x1 << 9)
+#define RT5682_I2S2_OUT_SFT 9
+#define RT5682_I2S2_OUT_UM (0x0 << 9)
+#define RT5682_I2S2_OUT_M (0x1 << 9)
+#define RT5682_I2S_BP_MASK (0x1 << 8)
+#define RT5682_I2S_BP_SFT 8
+#define RT5682_I2S_BP_NOR (0x0 << 8)
+#define RT5682_I2S_BP_INV (0x1 << 8)
+#define RT5682_I2S2_MONO_EN (0x1 << 6)
+#define RT5682_I2S2_MONO_DIS (0x0 << 6)
+#define RT5682_I2S2_DL_MASK (0x3 << 4)
+#define RT5682_I2S2_DL_SFT 4
+#define RT5682_I2S2_DL_16 (0x0 << 4)
+#define RT5682_I2S2_DL_20 (0x1 << 4)
+#define RT5682_I2S2_DL_24 (0x2 << 4)
+#define RT5682_I2S2_DL_8 (0x3 << 4)
+#define RT5682_I2S_DF_MASK (0x7)
+#define RT5682_I2S_DF_SFT 0
+#define RT5682_I2S_DF_I2S (0x0)
+#define RT5682_I2S_DF_LEFT (0x1)
+#define RT5682_I2S_DF_PCM_A (0x2)
+#define RT5682_I2S_DF_PCM_B (0x3)
+#define RT5682_I2S_DF_PCM_A_N (0x6)
+#define RT5682_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5682_ADC_OSR_MASK (0xf << 12)
+#define RT5682_ADC_OSR_SFT 12
+#define RT5682_ADC_OSR_D_1 (0x0 << 12)
+#define RT5682_ADC_OSR_D_2 (0x1 << 12)
+#define RT5682_ADC_OSR_D_4 (0x2 << 12)
+#define RT5682_ADC_OSR_D_6 (0x3 << 12)
+#define RT5682_ADC_OSR_D_8 (0x4 << 12)
+#define RT5682_ADC_OSR_D_12 (0x5 << 12)
+#define RT5682_ADC_OSR_D_16 (0x6 << 12)
+#define RT5682_ADC_OSR_D_24 (0x7 << 12)
+#define RT5682_ADC_OSR_D_32 (0x8 << 12)
+#define RT5682_ADC_OSR_D_48 (0x9 << 12)
+#define RT5682_I2S_M_DIV_MASK (0xf << 8)
+#define RT5682_I2S_M_DIV_SFT 8
+#define RT5682_I2S_M_D_1 (0x0 << 8)
+#define RT5682_I2S_M_D_2 (0x1 << 8)
+#define RT5682_I2S_M_D_3 (0x2 << 8)
+#define RT5682_I2S_M_D_4 (0x3 << 8)
+#define RT5682_I2S_M_D_6 (0x4 << 8)
+#define RT5682_I2S_M_D_8 (0x5 << 8)
+#define RT5682_I2S_M_D_12 (0x6 << 8)
+#define RT5682_I2S_M_D_16 (0x7 << 8)
+#define RT5682_I2S_M_D_24 (0x8 << 8)
+#define RT5682_I2S_M_D_32 (0x9 << 8)
+#define RT5682_I2S_M_D_48 (0x10 << 8)
+#define RT5682_I2S_CLK_SRC_MASK (0x7 << 4)
+#define RT5682_I2S_CLK_SRC_SFT 4
+#define RT5682_I2S_CLK_SRC_MCLK (0x0 << 4)
+#define RT5682_I2S_CLK_SRC_PLL1 (0x1 << 4)
+#define RT5682_I2S_CLK_SRC_PLL2 (0x2 << 4)
+#define RT5682_I2S_CLK_SRC_SDW (0x3 << 4)
+#define RT5682_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */
+#define RT5682_DAC_OSR_MASK (0xf << 0)
+#define RT5682_DAC_OSR_SFT 0
+#define RT5682_DAC_OSR_D_1 (0x0 << 0)
+#define RT5682_DAC_OSR_D_2 (0x1 << 0)
+#define RT5682_DAC_OSR_D_4 (0x2 << 0)
+#define RT5682_DAC_OSR_D_6 (0x3 << 0)
+#define RT5682_DAC_OSR_D_8 (0x4 << 0)
+#define RT5682_DAC_OSR_D_12 (0x5 << 0)
+#define RT5682_DAC_OSR_D_16 (0x6 << 0)
+#define RT5682_DAC_OSR_D_24 (0x7 << 0)
+#define RT5682_DAC_OSR_D_32 (0x8 << 0)
+#define RT5682_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5682_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5682_I2S2_BCLK_MS2_SFT 11
+#define RT5682_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5682_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5682_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5682_TDM_TX_CH_2 (0x0 << 12)
+#define RT5682_TDM_TX_CH_4 (0x1 << 12)
+#define RT5682_TDM_TX_CH_6 (0x2 << 12)
+#define RT5682_TDM_TX_CH_8 (0x3 << 12)
+#define RT5682_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5682_TDM_RX_CH_2 (0x0 << 8)
+#define RT5682_TDM_RX_CH_4 (0x1 << 8)
+#define RT5682_TDM_RX_CH_6 (0x2 << 8)
+#define RT5682_TDM_RX_CH_8 (0x3 << 8)
+#define RT5682_TDM_ADC_LCA_MASK (0xf << 4)
+#define RT5682_TDM_ADC_LCA_SFT 4
+#define RT5682_TDM_ADC_DL_SFT 0
+
+/* TDM control 2 (0x007a) */
+#define RT5682_IF1_ADC1_SEL_SFT 14
+#define RT5682_IF1_ADC2_SEL_SFT 12
+#define RT5682_IF1_ADC3_SEL_SFT 10
+#define RT5682_IF1_ADC4_SEL_SFT 8
+#define RT5682_TDM_ADC_SEL_SFT 4
+
+/* TDM control 3 (0x007b) */
+#define RT5682_TDM_EN (0x1 << 7)
+
+/* TDM/I2S control (0x007e) */
+#define RT5682_TDM_S_BP_MASK (0x1 << 15)
+#define RT5682_TDM_S_BP_SFT 15
+#define RT5682_TDM_S_BP_NOR (0x0 << 15)
+#define RT5682_TDM_S_BP_INV (0x1 << 15)
+#define RT5682_TDM_S_LP_MASK (0x1 << 14)
+#define RT5682_TDM_S_LP_SFT 14
+#define RT5682_TDM_S_LP_NOR (0x0 << 14)
+#define RT5682_TDM_S_LP_INV (0x1 << 14)
+#define RT5682_TDM_DF_MASK (0x7 << 11)
+#define RT5682_TDM_DF_SFT 11
+#define RT5682_TDM_DF_I2S (0x0 << 11)
+#define RT5682_TDM_DF_LEFT (0x1 << 11)
+#define RT5682_TDM_DF_PCM_A (0x2 << 11)
+#define RT5682_TDM_DF_PCM_B (0x3 << 11)
+#define RT5682_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5682_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5682_TDM_BCLK_MS1_MASK (0x3 << 9)
+#define RT5682_TDM_BCLK_MS1_SFT 9
+#define RT5682_TDM_BCLK_MS1_32 (0x0 << 9)
+#define RT5682_TDM_BCLK_MS1_64 (0x1 << 9)
+#define RT5682_TDM_BCLK_MS1_128 (0x2 << 9)
+#define RT5682_TDM_BCLK_MS1_256 (0x3 << 9)
+#define RT5682_TDM_CL_MASK (0x3 << 4)
+#define RT5682_TDM_CL_16 (0x0 << 4)
+#define RT5682_TDM_CL_20 (0x1 << 4)
+#define RT5682_TDM_CL_24 (0x2 << 4)
+#define RT5682_TDM_CL_32 (0x3 << 4)
+#define RT5682_TDM_M_BP_MASK (0x1 << 2)
+#define RT5682_TDM_M_BP_SFT 2
+#define RT5682_TDM_M_BP_NOR (0x0 << 2)
+#define RT5682_TDM_M_BP_INV (0x1 << 2)
+#define RT5682_TDM_M_LP_MASK (0x1 << 1)
+#define RT5682_TDM_M_LP_SFT 1
+#define RT5682_TDM_M_LP_NOR (0x0 << 1)
+#define RT5682_TDM_M_LP_INV (0x1 << 1)
+#define RT5682_TDM_MS_MASK (0x1 << 0)
+#define RT5682_TDM_MS_SFT 0
+#define RT5682_TDM_MS_S (0x0 << 0)
+#define RT5682_TDM_MS_M (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5682_SCLK_SRC_MASK (0x7 << 13)
+#define RT5682_SCLK_SRC_SFT 13
+#define RT5682_SCLK_SRC_MCLK (0x0 << 13)
+#define RT5682_SCLK_SRC_PLL1 (0x1 << 13)
+#define RT5682_SCLK_SRC_PLL2 (0x2 << 13)
+#define RT5682_SCLK_SRC_SDW (0x3 << 13)
+#define RT5682_SCLK_SRC_RCCLK (0x4 << 13)
+#define RT5682_PLL2_SRC_MASK (0x3 << 10)
+#define RT5682_PLL2_SRC_SFT 10
+#define RT5682_PLL2_SRC_MCLK (0x0 << 10)
+#define RT5682_PLL2_SRC_BCLK1 (0x1 << 10)
+#define RT5682_PLL2_SRC_SDW (0x2 << 10)
+#define RT5682_PLL2_SRC_RC (0x3 << 10)
+#define RT5682_PLL1_SRC_MASK (0x3 << 8)
+#define RT5682_PLL1_SRC_SFT 8
+#define RT5682_PLL1_SRC_MCLK (0x0 << 8)
+#define RT5682_PLL1_SRC_BCLK1 (0x1 << 8)
+#define RT5682_PLL1_SRC_SDW (0x2 << 8)
+#define RT5682_PLL1_SRC_RC (0x3 << 8)
+
+
+
+#define RT5682_PLL_INP_MAX 40000000
+#define RT5682_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x0081) */
+#define RT5682_PLL_N_MAX 0x001ff
+#define RT5682_PLL_N_MASK (RT5682_PLL_N_MAX << 7)
+#define RT5682_PLL_N_SFT 7
+#define RT5682_PLL_K_MAX 0x001f
+#define RT5682_PLL_K_MASK (RT5682_PLL_K_MAX)
+#define RT5682_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x0082) */
+#define RT5682_PLL_M_MAX 0x00f
+#define RT5682_PLL_M_MASK (RT5682_PLL_M_MAX << 12)
+#define RT5682_PLL_M_SFT 12
+#define RT5682_PLL_M_BP (0x1 << 11)
+#define RT5682_PLL_M_BP_SFT 11
+#define RT5682_PLL_K_BP (0x1 << 10)
+#define RT5682_PLL_K_BP_SFT 10
+#define RT5682_PLL_RST (0x1 << 1)
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5682_DA_ASRC_MASK (0x1 << 13)
+#define RT5682_DA_ASRC_SFT 13
+#define RT5682_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5682_DAC_STO1_ASRC_SFT 12
+#define RT5682_AD_ASRC_MASK (0x1 << 8)
+#define RT5682_AD_ASRC_SFT 8
+#define RT5682_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5682_AD_ASRC_SEL_SFT 4
+#define RT5682_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5682_DMIC_ASRC_SFT 3
+#define RT5682_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5682_ADC_STO1_ASRC_SFT 2
+#define RT5682_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5682_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5682_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5682_FILTER_CLK_SEL_SFT 12
+#define RT5682_FILTER_CLK_DIV_MASK (0xf << 8)
+#define RT5682_FILTER_CLK_DIV_SFT 8
+
+/* ASRC Control 4 (0x0086) */
+#define RT5682_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5682_ASRCIN_FTK_N1_SFT 14
+#define RT5682_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5682_ASRCIN_FTK_N2_SFT 12
+#define RT5682_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5682_ASRCIN_FTK_M1_SFT 8
+#define RT5682_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5682_ASRCIN_FTK_M2_SFT 4
+
+/* SoundWire reference clk (0x008d) */
+#define RT5682_PLL2_OUT_MASK (0x1 << 8)
+#define RT5682_PLL2_OUT_98M (0x0 << 8)
+#define RT5682_PLL2_OUT_49M (0x1 << 8)
+#define RT5682_SDW_REF_2_MASK (0xf << 4)
+#define RT5682_SDW_REF_2_SFT 4
+#define RT5682_SDW_REF_2_48K (0x0 << 4)
+#define RT5682_SDW_REF_2_96K (0x1 << 4)
+#define RT5682_SDW_REF_2_192K (0x2 << 4)
+#define RT5682_SDW_REF_2_32K (0x3 << 4)
+#define RT5682_SDW_REF_2_24K (0x4 << 4)
+#define RT5682_SDW_REF_2_16K (0x5 << 4)
+#define RT5682_SDW_REF_2_12K (0x6 << 4)
+#define RT5682_SDW_REF_2_8K (0x7 << 4)
+#define RT5682_SDW_REF_2_44K (0x8 << 4)
+#define RT5682_SDW_REF_2_88K (0x9 << 4)
+#define RT5682_SDW_REF_2_176K (0xa << 4)
+#define RT5682_SDW_REF_2_353K (0xb << 4)
+#define RT5682_SDW_REF_2_22K (0xc << 4)
+#define RT5682_SDW_REF_2_384K (0xd << 4)
+#define RT5682_SDW_REF_2_11K (0xe << 4)
+#define RT5682_SDW_REF_1_MASK (0xf << 0)
+#define RT5682_SDW_REF_1_SFT 0
+#define RT5682_SDW_REF_1_48K (0x0 << 0)
+#define RT5682_SDW_REF_1_96K (0x1 << 0)
+#define RT5682_SDW_REF_1_192K (0x2 << 0)
+#define RT5682_SDW_REF_1_32K (0x3 << 0)
+#define RT5682_SDW_REF_1_24K (0x4 << 0)
+#define RT5682_SDW_REF_1_16K (0x5 << 0)
+#define RT5682_SDW_REF_1_12K (0x6 << 0)
+#define RT5682_SDW_REF_1_8K (0x7 << 0)
+#define RT5682_SDW_REF_1_44K (0x8 << 0)
+#define RT5682_SDW_REF_1_88K (0x9 << 0)
+#define RT5682_SDW_REF_1_176K (0xa << 0)
+#define RT5682_SDW_REF_1_353K (0xb << 0)
+#define RT5682_SDW_REF_1_22K (0xc << 0)
+#define RT5682_SDW_REF_1_384K (0xd << 0)
+#define RT5682_SDW_REF_1_11K (0xe << 0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5682_PUMP_EN (0x1 << 3)
+#define RT5682_PUMP_EN_SFT 3
+#define RT5682_CAPLESS_EN (0x1 << 0)
+#define RT5682_CAPLESS_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5682_RAMP_MASK (0x1 << 12)
+#define RT5682_RAMP_SFT 12
+#define RT5682_RAMP_DIS (0x0 << 12)
+#define RT5682_RAMP_EN (0x1 << 12)
+#define RT5682_BPS_MASK (0x1 << 11)
+#define RT5682_BPS_SFT 11
+#define RT5682_BPS_DIS (0x0 << 11)
+#define RT5682_BPS_EN (0x1 << 11)
+#define RT5682_FAST_UPDN_MASK (0x1 << 10)
+#define RT5682_FAST_UPDN_SFT 10
+#define RT5682_FAST_UPDN_DIS (0x0 << 10)
+#define RT5682_FAST_UPDN_EN (0x1 << 10)
+#define RT5682_VLO_MASK (0x1 << 7)
+#define RT5682_VLO_SFT 7
+#define RT5682_VLO_3V (0x0 << 7)
+#define RT5682_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5682_OSW_L_MASK (0x1 << 11)
+#define RT5682_OSW_L_SFT 11
+#define RT5682_OSW_L_DIS (0x0 << 11)
+#define RT5682_OSW_L_EN (0x1 << 11)
+#define RT5682_OSW_R_MASK (0x1 << 10)
+#define RT5682_OSW_R_SFT 10
+#define RT5682_OSW_R_DIS (0x0 << 10)
+#define RT5682_OSW_R_EN (0x1 << 10)
+#define RT5682_PM_HP_MASK (0x3 << 8)
+#define RT5682_PM_HP_SFT 8
+#define RT5682_PM_HP_LV (0x0 << 8)
+#define RT5682_PM_HP_MV (0x1 << 8)
+#define RT5682_PM_HP_HV (0x2 << 8)
+#define RT5682_IB_HP_MASK (0x3 << 6)
+#define RT5682_IB_HP_SFT 6
+#define RT5682_IB_HP_125IL (0x0 << 6)
+#define RT5682_IB_HP_25IL (0x1 << 6)
+#define RT5682_IB_HP_5IL (0x2 << 6)
+#define RT5682_IB_HP_1IL (0x3 << 6)
+
+/* Micbias Control1 (0x93) */
+#define RT5682_MIC1_OV_MASK (0x3 << 14)
+#define RT5682_MIC1_OV_SFT 14
+#define RT5682_MIC1_OV_2V7 (0x0 << 14)
+#define RT5682_MIC1_OV_2V4 (0x1 << 14)
+#define RT5682_MIC1_OV_2V25 (0x3 << 14)
+#define RT5682_MIC1_OV_1V8 (0x4 << 14)
+#define RT5682_MIC1_CLK_MASK (0x1 << 13)
+#define RT5682_MIC1_CLK_SFT 13
+#define RT5682_MIC1_CLK_DIS (0x0 << 13)
+#define RT5682_MIC1_CLK_EN (0x1 << 13)
+#define RT5682_MIC1_OVCD_MASK (0x1 << 12)
+#define RT5682_MIC1_OVCD_SFT 12
+#define RT5682_MIC1_OVCD_DIS (0x0 << 12)
+#define RT5682_MIC1_OVCD_EN (0x1 << 12)
+#define RT5682_MIC1_OVTH_MASK (0x3 << 10)
+#define RT5682_MIC1_OVTH_SFT 10
+#define RT5682_MIC1_OVTH_768UA (0x0 << 10)
+#define RT5682_MIC1_OVTH_960UA (0x1 << 10)
+#define RT5682_MIC1_OVTH_1152UA (0x2 << 10)
+#define RT5682_MIC1_OVTH_1960UA (0x3 << 10)
+#define RT5682_MIC2_OV_MASK (0x3 << 8)
+#define RT5682_MIC2_OV_SFT 8
+#define RT5682_MIC2_OV_2V7 (0x0 << 8)
+#define RT5682_MIC2_OV_2V4 (0x1 << 8)
+#define RT5682_MIC2_OV_2V25 (0x3 << 8)
+#define RT5682_MIC2_OV_1V8 (0x4 << 8)
+#define RT5682_MIC2_CLK_MASK (0x1 << 7)
+#define RT5682_MIC2_CLK_SFT 7
+#define RT5682_MIC2_CLK_DIS (0x0 << 7)
+#define RT5682_MIC2_CLK_EN (0x1 << 7)
+#define RT5682_MIC2_OVTH_MASK (0x3 << 4)
+#define RT5682_MIC2_OVTH_SFT 4
+#define RT5682_MIC2_OVTH_768UA (0x0 << 4)
+#define RT5682_MIC2_OVTH_960UA (0x1 << 4)
+#define RT5682_MIC2_OVTH_1152UA (0x2 << 4)
+#define RT5682_MIC2_OVTH_1960UA (0x3 << 4)
+#define RT5682_PWR_MB_MASK (0x1 << 3)
+#define RT5682_PWR_MB_SFT 3
+#define RT5682_PWR_MB_PD (0x0 << 3)
+#define RT5682_PWR_MB_PU (0x1 << 3)
+
+/* Micbias Control2 (0x0094) */
+#define RT5682_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5682_PWR_CLK25M_SFT 9
+#define RT5682_PWR_CLK25M_PD (0x0 << 9)
+#define RT5682_PWR_CLK25M_PU (0x1 << 9)
+#define RT5682_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5682_PWR_CLK1M_SFT 8
+#define RT5682_PWR_CLK1M_PD (0x0 << 8)
+#define RT5682_PWR_CLK1M_PU (0x1 << 8)
+
+/* PLL2 M/N/K Code Control 1 (0x009b) */
+#define RT5682_PLL2F_K_MASK (0x1f << 8)
+#define RT5682_PLL2F_K_SFT 8
+#define RT5682_PLL2B_K_MASK (0xf << 4)
+#define RT5682_PLL2B_K_SFT 4
+#define RT5682_PLL2B_M_MASK (0xf << 0)
+
+/* PLL2 M/N/K Code Control 2 (0x009c) */
+#define RT5682_PLL2F_M_MASK (0x3f << 8)
+#define RT5682_PLL2F_M_SFT 8
+#define RT5682_PLL2B_N_MASK (0x3f << 0)
+
+/* PLL2 M/N/K Code Control 2 (0x009d) */
+#define RT5682_PLL2F_N_MASK (0x7f << 8)
+#define RT5682_PLL2F_N_SFT 8
+
+/* PLL2 M/N/K Code Control 2 (0x009e) */
+#define RT5682_PLL2B_SEL_PS_MASK (0x1 << 13)
+#define RT5682_PLL2B_SEL_PS_SFT 13
+#define RT5682_PLL2B_PS_BYP_MASK (0x1 << 12)
+#define RT5682_PLL2B_PS_BYP_SFT 12
+#define RT5682_PLL2B_M_BP_MASK (0x1 << 11)
+#define RT5682_PLL2B_M_BP_SFT 11
+#define RT5682_PLL2F_M_BP_MASK (0x1 << 7)
+#define RT5682_PLL2F_M_BP_SFT 7
+
+/* RC Clock Control (0x009f) */
+#define RT5682_POW_IRQ (0x1 << 15)
+#define RT5682_POW_JDH (0x1 << 14)
+#define RT5682_POW_JDL (0x1 << 13)
+#define RT5682_POW_ANA (0x1 << 12)
+
+/* I2S Master Mode Clock Control 1 (0x00a0) */
+#define RT5682_CLK_SRC_MCLK (0x0)
+#define RT5682_CLK_SRC_PLL1 (0x1)
+#define RT5682_CLK_SRC_PLL2 (0x2)
+#define RT5682_CLK_SRC_SDW (0x3)
+#define RT5682_CLK_SRC_RCCLK (0x4)
+#define RT5682_I2S_PD_1 (0x0)
+#define RT5682_I2S_PD_2 (0x1)
+#define RT5682_I2S_PD_3 (0x2)
+#define RT5682_I2S_PD_4 (0x3)
+#define RT5682_I2S_PD_6 (0x4)
+#define RT5682_I2S_PD_8 (0x5)
+#define RT5682_I2S_PD_12 (0x6)
+#define RT5682_I2S_PD_16 (0x7)
+#define RT5682_I2S_PD_24 (0x8)
+#define RT5682_I2S_PD_32 (0x9)
+#define RT5682_I2S_PD_48 (0xa)
+#define RT5682_I2S2_SRC_MASK (0x3 << 4)
+#define RT5682_I2S2_SRC_SFT 4
+#define RT5682_I2S2_M_PD_MASK (0xf << 0)
+#define RT5682_I2S2_M_PD_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5682_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5682_JD1_PULSE_EN_SFT 10
+#define RT5682_JD1_PULSE_DIS (0x0 << 10)
+#define RT5682_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5682_JD1_EN_MASK (0x1 << 15)
+#define RT5682_JD1_EN_SFT 15
+#define RT5682_JD1_DIS (0x0 << 15)
+#define RT5682_JD1_EN (0x1 << 15)
+#define RT5682_JD1_POL_MASK (0x1 << 13)
+#define RT5682_JD1_POL_NOR (0x0 << 13)
+#define RT5682_JD1_POL_INV (0x1 << 13)
+#define RT5682_JD1_IRQ_MASK (0x1 << 10)
+#define RT5682_JD1_IRQ_LEV (0x0 << 10)
+#define RT5682_JD1_IRQ_PUL (0x1 << 10)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5682_IL_IRQ_MASK (0x1 << 7)
+#define RT5682_IL_IRQ_DIS (0x0 << 7)
+#define RT5682_IL_IRQ_EN (0x1 << 7)
+#define RT5682_IL_IRQ_TYPE_MASK (0x1 << 4)
+#define RT5682_IL_IRQ_LEV (0x0 << 4)
+#define RT5682_IL_IRQ_PUL (0x1 << 4)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5682_GP1_PIN_MASK (0x3 << 14)
+#define RT5682_GP1_PIN_SFT 14
+#define RT5682_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5682_GP1_PIN_IRQ (0x1 << 14)
+#define RT5682_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5682_GP2_PIN_MASK (0x3 << 12)
+#define RT5682_GP2_PIN_SFT 12
+#define RT5682_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5682_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5682_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5682_GP3_PIN_MASK (0x3 << 10)
+#define RT5682_GP3_PIN_SFT 10
+#define RT5682_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5682_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5682_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5682_GP4_PIN_MASK (0x3 << 8)
+#define RT5682_GP4_PIN_SFT 8
+#define RT5682_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5682_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5682_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5682_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5682_GP5_PIN_MASK (0x3 << 6)
+#define RT5682_GP5_PIN_SFT 6
+#define RT5682_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5682_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5682_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5682_GP6_PIN_MASK (0x1 << 5)
+#define RT5682_GP6_PIN_SFT 5
+#define RT5682_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5682_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5682_GP1_PF_MASK (0x1 << 15)
+#define RT5682_GP1_PF_IN (0x0 << 15)
+#define RT5682_GP1_PF_OUT (0x1 << 15)
+#define RT5682_GP1_OUT_MASK (0x1 << 14)
+#define RT5682_GP1_OUT_L (0x0 << 14)
+#define RT5682_GP1_OUT_H (0x1 << 14)
+#define RT5682_GP2_PF_MASK (0x1 << 13)
+#define RT5682_GP2_PF_IN (0x0 << 13)
+#define RT5682_GP2_PF_OUT (0x1 << 13)
+#define RT5682_GP2_OUT_MASK (0x1 << 12)
+#define RT5682_GP2_OUT_L (0x0 << 12)
+#define RT5682_GP2_OUT_H (0x1 << 12)
+#define RT5682_GP3_PF_MASK (0x1 << 11)
+#define RT5682_GP3_PF_IN (0x0 << 11)
+#define RT5682_GP3_PF_OUT (0x1 << 11)
+#define RT5682_GP3_OUT_MASK (0x1 << 10)
+#define RT5682_GP3_OUT_L (0x0 << 10)
+#define RT5682_GP3_OUT_H (0x1 << 10)
+#define RT5682_GP4_PF_MASK (0x1 << 9)
+#define RT5682_GP4_PF_IN (0x0 << 9)
+#define RT5682_GP4_PF_OUT (0x1 << 9)
+#define RT5682_GP4_OUT_MASK (0x1 << 8)
+#define RT5682_GP4_OUT_L (0x0 << 8)
+#define RT5682_GP4_OUT_H (0x1 << 8)
+#define RT5682_GP5_PF_MASK (0x1 << 7)
+#define RT5682_GP5_PF_IN (0x0 << 7)
+#define RT5682_GP5_PF_OUT (0x1 << 7)
+#define RT5682_GP5_OUT_MASK (0x1 << 6)
+#define RT5682_GP5_OUT_L (0x0 << 6)
+#define RT5682_GP5_OUT_H (0x1 << 6)
+#define RT5682_GP6_PF_MASK (0x1 << 5)
+#define RT5682_GP6_PF_IN (0x0 << 5)
+#define RT5682_GP6_PF_OUT (0x1 << 5)
+#define RT5682_GP6_OUT_MASK (0x1 << 4)
+#define RT5682_GP6_OUT_L (0x0 << 4)
+#define RT5682_GP6_OUT_H (0x1 << 4)
+
+
+/* GPIO Status (0x00c2) */
+#define RT5682_GP6_STA (0x1 << 6)
+#define RT5682_GP5_STA (0x1 << 5)
+#define RT5682_GP4_STA (0x1 << 4)
+#define RT5682_GP3_STA (0x1 << 3)
+#define RT5682_GP2_STA (0x1 << 2)
+#define RT5682_GP1_STA (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5682_SV_MASK (0x1 << 15)
+#define RT5682_SV_SFT 15
+#define RT5682_SV_DIS (0x0 << 15)
+#define RT5682_SV_EN (0x1 << 15)
+#define RT5682_ZCD_MASK (0x1 << 10)
+#define RT5682_ZCD_SFT 10
+#define RT5682_ZCD_PD (0x0 << 10)
+#define RT5682_ZCD_PU (0x1 << 10)
+#define RT5682_SV_DLY_MASK (0xf)
+#define RT5682_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0x00da) */
+#define RT5682_ZCD_BST1_CBJ_MASK (0x1 << 7)
+#define RT5682_ZCD_BST1_CBJ_SFT 7
+#define RT5682_ZCD_BST1_CBJ_DIS (0x0 << 7)
+#define RT5682_ZCD_BST1_CBJ_EN (0x1 << 7)
+#define RT5682_ZCD_RECMIX_MASK (0x1)
+#define RT5682_ZCD_RECMIX_SFT 0
+#define RT5682_ZCD_RECMIX_DIS (0x0)
+#define RT5682_ZCD_RECMIX_EN (0x1)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5682_4BTN_IL_MASK (0x1 << 15)
+#define RT5682_4BTN_IL_EN (0x1 << 15)
+#define RT5682_4BTN_IL_DIS (0x0 << 15)
+#define RT5682_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5682_4BTN_IL_NOR (0x1 << 14)
+#define RT5682_4BTN_IL_RST (0x0 << 14)
+
+/* Analog JD Control (0x00f0) */
+#define RT5682_JDH_RS_MASK (0x1 << 4)
+#define RT5682_JDH_NO_PLUG (0x1 << 4)
+#define RT5682_JDH_PLUG (0x0 << 4)
+
+/* Bias current control 8 (0x0111) */
+#define RT5682_HPA_CP_BIAS_CTRL_MASK (0x3 << 2)
+#define RT5682_HPA_CP_BIAS_2UA (0x0 << 2)
+#define RT5682_HPA_CP_BIAS_3UA (0x1 << 2)
+#define RT5682_HPA_CP_BIAS_4UA (0x2 << 2)
+#define RT5682_HPA_CP_BIAS_6UA (0x3 << 2)
+
+/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682_CP_SW_SIZE_MASK (0x7 << 8)
+#define RT5682_CP_SW_SIZE_L (0x4 << 8)
+#define RT5682_CP_SW_SIZE_M (0x2 << 8)
+#define RT5682_CP_SW_SIZE_S (0x1 << 8)
+#define RT5682_CP_CLK_HP_MASK (0x3 << 4)
+#define RT5682_CP_CLK_HP_100KHZ (0x0 << 4)
+#define RT5682_CP_CLK_HP_200KHZ (0x1 << 4)
+#define RT5682_CP_CLK_HP_300KHZ (0x2 << 4)
+#define RT5682_CP_CLK_HP_600KHZ (0x3 << 4)
+
+/* Pad Driving Control (0x0136) */
+#define RT5682_PAD_DRV_GP1_MASK (0x3 << 14)
+#define RT5682_PAD_DRV_GP1_SFT 14
+#define RT5682_PAD_DRV_GP2_MASK (0x3 << 12)
+#define RT5682_PAD_DRV_GP2_SFT 12
+#define RT5682_PAD_DRV_GP3_MASK (0x3 << 10)
+#define RT5682_PAD_DRV_GP3_SFT 10
+#define RT5682_PAD_DRV_GP4_MASK (0x3 << 8)
+#define RT5682_PAD_DRV_GP4_SFT 8
+#define RT5682_PAD_DRV_GP5_MASK (0x3 << 6)
+#define RT5682_PAD_DRV_GP5_SFT 6
+#define RT5682_PAD_DRV_GP6_MASK (0x3 << 4)
+#define RT5682_PAD_DRV_GP6_SFT 4
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5682_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5682_CKXEN_DAC1_SFT 13
+#define RT5682_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5682_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5682_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5682_CKXEN_ADC1_SFT 13
+#define RT5682_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5682_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5682_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5682_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5682_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5682_AD2DA_LB_MASK (0x1 << 10)
+#define RT5682_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5682_NG2_EN_MASK (0x1 << 15)
+#define RT5682_NG2_EN (0x1 << 15)
+#define RT5682_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5682_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5682_DEB_80_MS (0x0 << 4)
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5682_HP_LC2_SIG_SOUR2_MASK (0x1 << 4)
+#define RT5682_HP_LC2_SIG_SOUR2_REG (0x1 << 4)
+#define RT5682_HP_LC2_SIG_SOUR2_DC_CAL (0x0 << 4)
+#define RT5682_HP_LC2_SIG_SOUR1_MASK (0x7)
+#define RT5682_HP_LC2_SIG_SOUR1_1BIT (0x7)
+#define RT5682_HP_LC2_SIG_SOUR1_LEGA (0x2)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15)
+#define RT5682_SAR_BUTT_DET_EN (0x1 << 15)
+#define RT5682_SAR_BUTT_DET_DIS (0x0 << 15)
+#define RT5682_SAR_BUTDET_MODE_MASK (0x1 << 14)
+#define RT5682_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5682_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5682_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5682_SAR_BUTDET_RST_NORMAL (0x1 << 13)
+#define RT5682_SAR_BUTDET_RST (0x0 << 13)
+#define RT5682_SAR_POW_MASK (0x1 << 12)
+#define RT5682_SAR_POW_EN (0x1 << 12)
+#define RT5682_SAR_POW_DIS (0x0 << 12)
+#define RT5682_SAR_RST_MASK (0x1 << 11)
+#define RT5682_SAR_RST_NORMAL (0x1 << 11)
+#define RT5682_SAR_RST (0x0 << 11)
+#define RT5682_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5682_SAR_BYPASS_EN (0x1 << 10)
+#define RT5682_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5682_SAR_SEL_MB1_MASK (0x1 << 9)
+#define RT5682_SAR_SEL_MB1_SEL (0x1 << 9)
+#define RT5682_SAR_SEL_MB1_NOSEL (0x0 << 9)
+#define RT5682_SAR_SEL_MB2_MASK (0x1 << 8)
+#define RT5682_SAR_SEL_MB2_SEL (0x1 << 8)
+#define RT5682_SAR_SEL_MB2_NOSEL (0x0 << 8)
+#define RT5682_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5682_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5682_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5682_SAR_SEL_MB1_MB2_MASK (0x1 << 5)
+#define RT5682_SAR_SEL_MB1_MB2_AUTO (0x1 << 5)
+#define RT5682_SAR_SEL_MB1_MB2_MANU (0x0 << 5)
+#define RT5682_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5682_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5682_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5682_SAR_SOUR_MASK (0x3f)
+#define RT5682_SAR_SOUR_BTN (0x3f)
+#define RT5682_SAR_SOUR_TYPE (0x0)
+
+/* soundwire timeout */
+#define RT5682_PROBE_TIMEOUT 5000
+
+
+#define RT5682_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5682_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+/* System Clock Source */
+enum {
+ RT5682_SCLK_S_MCLK,
+ RT5682_SCLK_S_PLL1,
+ RT5682_SCLK_S_PLL2,
+ RT5682_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5682_PLL1_S_MCLK,
+ RT5682_PLL1_S_BCLK1,
+ RT5682_PLL1_S_RCCLK,
+ RT5682_PLL2_S_MCLK,
+};
+
+enum {
+ RT5682_PLL1,
+ RT5682_PLL2,
+ RT5682_PLLS,
+};
+
+enum {
+ RT5682_AIF1,
+ RT5682_AIF2,
+ RT5682_SDW,
+ RT5682_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5682_DA_STEREO1_FILTER = 0x1,
+ RT5682_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5682_CLK_SEL_SYS,
+ RT5682_CLK_SEL_I2S1_ASRC,
+ RT5682_CLK_SEL_I2S2_ASRC,
+};
+
+#define RT5682_NUM_SUPPLIES 5
+
+struct rt5682_priv {
+ struct snd_soc_component *component;
+ struct device *i2c_dev;
+ struct rt5682_platform_data pdata;
+ struct gpio_desc *ldo1_en;
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex disable_irq_lock; /* imp-def irq lock protection */
+ bool disable_irq;
+ struct mutex calibrate_mutex;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ bool is_sdw;
+ bool ve_ic;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS];
+ struct clk *mclk;
+#endif
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5682_AIFS];
+ int bclk[RT5682_AIFS];
+ int master[RT5682_AIFS];
+
+ int pll_src[RT5682_PLLS];
+ int pll_in[RT5682_PLLS];
+ int pll_out[RT5682_PLLS];
+
+ int jack_type;
+ int irq;
+ int irq_work_delay_time;
+};
+
+extern const char *rt5682_supply_names[RT5682_NUM_SUPPLIES];
+
+int rt5682_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev);
+
+void rt5682_jack_detect_handler(struct work_struct *work);
+
+bool rt5682_volatile_register(struct device *dev, unsigned int reg);
+bool rt5682_readable_register(struct device *dev, unsigned int reg);
+
+int rt5682_register_component(struct device *dev);
+void rt5682_calibrate(struct rt5682_priv *rt5682);
+void rt5682_reset(struct rt5682_priv *rt5682);
+int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev);
+int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev);
+
+int rt5682_register_dai_clks(struct rt5682_priv *rt5682);
+
+#define RT5682_REG_NUM 318
+extern const struct reg_default rt5682_reg[RT5682_REG_NUM];
+
+extern const struct snd_soc_dai_ops rt5682_aif1_dai_ops;
+extern const struct snd_soc_dai_ops rt5682_aif2_dai_ops;
+extern const struct snd_soc_component_driver rt5682_soc_component_dev;
+
+#endif /* __RT5682_H__ */
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
new file mode 100644
index 000000000000..98de94a79260
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.c
@@ -0,0 +1,3349 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682s.c -- RT5682I-VS ALSA SoC audio component driver
+//
+// Copyright 2021 Realtek Semiconductor Corp.
+// Author: Derek Fang <derek.fang@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682s.h>
+
+#include "rt5682s.h"
+
+#define DEVICE_ID 0x6749
+
+static const struct rt5682s_platform_data i2s_default_platform_data = {
+ .dmic1_data_pin = RT5682S_DMIC1_DATA_GPIO2,
+ .dmic1_clk_pin = RT5682S_DMIC1_CLK_GPIO3,
+ .jd_src = RT5682S_JD1,
+ .dai_clk_names[RT5682S_DAI_WCLK_IDX] = "rt5682-dai-wclk",
+ .dai_clk_names[RT5682S_DAI_BCLK_IDX] = "rt5682-dai-bclk",
+};
+
+static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = {
+ [RT5682S_SUPPLY_AVDD] = "AVDD",
+ [RT5682S_SUPPLY_MICVDD] = "MICVDD",
+ [RT5682S_SUPPLY_DBVDD] = "DBVDD",
+ [RT5682S_SUPPLY_LDO1_IN] = "LDO1-IN",
+};
+
+static const struct reg_sequence patch_list[] = {
+ {RT5682S_I2C_CTRL, 0x0007},
+ {RT5682S_DIG_IN_CTRL_1, 0x0000},
+ {RT5682S_CHOP_DAC_2, 0x2020},
+ {RT5682S_VREF_REC_OP_FB_CAP_CTRL_2, 0x0101},
+ {RT5682S_VREF_REC_OP_FB_CAP_CTRL_1, 0x80c0},
+ {RT5682S_HP_CALIB_CTRL_9, 0x0002},
+ {RT5682S_DEPOP_1, 0x0000},
+ {RT5682S_HP_CHARGE_PUMP_2, 0x3c15},
+ {RT5682S_DAC1_DIG_VOL, 0xfefe},
+ {RT5682S_SAR_IL_CMD_2, 0xac00},
+ {RT5682S_SAR_IL_CMD_3, 0x024c},
+ {RT5682S_CBJ_CTRL_6, 0x0804},
+};
+
+static void rt5682s_apply_patch_list(struct rt5682s_priv *rt5682s,
+ struct device *dev)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(rt5682s->regmap, patch_list, ARRAY_SIZE(patch_list));
+ if (ret)
+ dev_warn(dev, "Failed to apply regmap patch: %d\n", ret);
+}
+
+static const struct reg_default rt5682s_reg[] = {
+ {0x0002, 0x8080},
+ {0x0003, 0x0001},
+ {0x0005, 0x0000},
+ {0x0006, 0x0000},
+ {0x0008, 0x8007},
+ {0x000b, 0x0000},
+ {0x000f, 0x4000},
+ {0x0010, 0x4040},
+ {0x0011, 0x0000},
+ {0x0012, 0x0000},
+ {0x0013, 0x1200},
+ {0x0014, 0x200a},
+ {0x0015, 0x0404},
+ {0x0016, 0x0404},
+ {0x0017, 0x05a4},
+ {0x0019, 0xffff},
+ {0x001c, 0x2f2f},
+ {0x001f, 0x0000},
+ {0x0022, 0x5757},
+ {0x0023, 0x0039},
+ {0x0024, 0x000b},
+ {0x0026, 0xc0c4},
+ {0x0029, 0x8080},
+ {0x002a, 0xa0a0},
+ {0x002b, 0x0300},
+ {0x0030, 0x0000},
+ {0x003c, 0x08c0},
+ {0x0044, 0x1818},
+ {0x004b, 0x00c0},
+ {0x004c, 0x0000},
+ {0x004d, 0x0000},
+ {0x0061, 0x00c0},
+ {0x0062, 0x008a},
+ {0x0063, 0x0800},
+ {0x0064, 0x0000},
+ {0x0065, 0x0000},
+ {0x0066, 0x0030},
+ {0x0067, 0x000c},
+ {0x0068, 0x0000},
+ {0x0069, 0x0000},
+ {0x006a, 0x0000},
+ {0x006b, 0x0000},
+ {0x006c, 0x0000},
+ {0x006d, 0x2200},
+ {0x006e, 0x0810},
+ {0x006f, 0xe4de},
+ {0x0070, 0x3320},
+ {0x0071, 0x0000},
+ {0x0073, 0x0000},
+ {0x0074, 0x0000},
+ {0x0075, 0x0002},
+ {0x0076, 0x0001},
+ {0x0079, 0x0000},
+ {0x007a, 0x0000},
+ {0x007b, 0x0000},
+ {0x007c, 0x0100},
+ {0x007e, 0x0000},
+ {0x007f, 0x0000},
+ {0x0080, 0x0000},
+ {0x0083, 0x0000},
+ {0x0084, 0x0000},
+ {0x0085, 0x0000},
+ {0x0086, 0x0005},
+ {0x0087, 0x0000},
+ {0x0088, 0x0000},
+ {0x008c, 0x0003},
+ {0x008e, 0x0060},
+ {0x008f, 0x4da1},
+ {0x0091, 0x1c15},
+ {0x0092, 0x0425},
+ {0x0093, 0x0000},
+ {0x0094, 0x0080},
+ {0x0095, 0x008f},
+ {0x0096, 0x0000},
+ {0x0097, 0x0000},
+ {0x0098, 0x0000},
+ {0x0099, 0x0000},
+ {0x009a, 0x0000},
+ {0x009b, 0x0000},
+ {0x009c, 0x0000},
+ {0x009d, 0x0000},
+ {0x009e, 0x0000},
+ {0x009f, 0x0009},
+ {0x00a0, 0x0000},
+ {0x00a3, 0x0002},
+ {0x00a4, 0x0001},
+ {0x00b6, 0x0000},
+ {0x00b7, 0x0000},
+ {0x00b8, 0x0000},
+ {0x00b9, 0x0002},
+ {0x00be, 0x0000},
+ {0x00c0, 0x0160},
+ {0x00c1, 0x82a0},
+ {0x00c2, 0x0000},
+ {0x00d0, 0x0000},
+ {0x00d2, 0x3300},
+ {0x00d3, 0x2200},
+ {0x00d4, 0x0000},
+ {0x00d9, 0x0000},
+ {0x00da, 0x0000},
+ {0x00db, 0x0000},
+ {0x00dc, 0x00c0},
+ {0x00dd, 0x2220},
+ {0x00de, 0x3131},
+ {0x00df, 0x3131},
+ {0x00e0, 0x3131},
+ {0x00e2, 0x0000},
+ {0x00e3, 0x4000},
+ {0x00e4, 0x0aa0},
+ {0x00e5, 0x3131},
+ {0x00e6, 0x3131},
+ {0x00e7, 0x3131},
+ {0x00e8, 0x3131},
+ {0x00ea, 0xb320},
+ {0x00eb, 0x0000},
+ {0x00f0, 0x0000},
+ {0x00f6, 0x0000},
+ {0x00fa, 0x0000},
+ {0x00fb, 0x0000},
+ {0x00fc, 0x0000},
+ {0x00fd, 0x0000},
+ {0x00fe, 0x10ec},
+ {0x00ff, 0x6749},
+ {0x0100, 0xa000},
+ {0x010b, 0x0066},
+ {0x010c, 0x6666},
+ {0x010d, 0x2202},
+ {0x010e, 0x6666},
+ {0x010f, 0xa800},
+ {0x0110, 0x0006},
+ {0x0111, 0x0460},
+ {0x0112, 0x2000},
+ {0x0113, 0x0200},
+ {0x0117, 0x8000},
+ {0x0118, 0x0303},
+ {0x0125, 0x0020},
+ {0x0132, 0x5026},
+ {0x0136, 0x8000},
+ {0x0139, 0x0005},
+ {0x013a, 0x3030},
+ {0x013b, 0xa000},
+ {0x013c, 0x4110},
+ {0x013f, 0x0000},
+ {0x0145, 0x0022},
+ {0x0146, 0x0000},
+ {0x0147, 0x0000},
+ {0x0148, 0x0000},
+ {0x0156, 0x0022},
+ {0x0157, 0x0303},
+ {0x0158, 0x2222},
+ {0x0159, 0x0000},
+ {0x0160, 0x4ec0},
+ {0x0161, 0x0080},
+ {0x0162, 0x0200},
+ {0x0163, 0x0800},
+ {0x0164, 0x0000},
+ {0x0165, 0x0000},
+ {0x0166, 0x0000},
+ {0x0167, 0x000f},
+ {0x0168, 0x000f},
+ {0x0169, 0x0001},
+ {0x0190, 0x4131},
+ {0x0194, 0x0000},
+ {0x0195, 0x0000},
+ {0x0197, 0x0022},
+ {0x0198, 0x0000},
+ {0x0199, 0x0000},
+ {0x01ac, 0x0000},
+ {0x01ad, 0x0000},
+ {0x01ae, 0x0000},
+ {0x01af, 0x2000},
+ {0x01b0, 0x0000},
+ {0x01b1, 0x0000},
+ {0x01b2, 0x0000},
+ {0x01b3, 0x0017},
+ {0x01b4, 0x004b},
+ {0x01b5, 0x0000},
+ {0x01b6, 0x03e8},
+ {0x01b7, 0x0000},
+ {0x01b8, 0x0000},
+ {0x01b9, 0x0400},
+ {0x01ba, 0xb5b6},
+ {0x01bb, 0x9124},
+ {0x01bc, 0x4924},
+ {0x01bd, 0x0009},
+ {0x01be, 0x0018},
+ {0x01bf, 0x002a},
+ {0x01c0, 0x004c},
+ {0x01c1, 0x0097},
+ {0x01c2, 0x01c3},
+ {0x01c3, 0x03e9},
+ {0x01c4, 0x1389},
+ {0x01c5, 0xc351},
+ {0x01c6, 0x02a0},
+ {0x01c7, 0x0b0f},
+ {0x01c8, 0x402f},
+ {0x01c9, 0x0702},
+ {0x01ca, 0x0000},
+ {0x01cb, 0x0000},
+ {0x01cc, 0x5757},
+ {0x01cd, 0x5757},
+ {0x01ce, 0x5757},
+ {0x01cf, 0x5757},
+ {0x01d0, 0x5757},
+ {0x01d1, 0x5757},
+ {0x01d2, 0x5757},
+ {0x01d3, 0x5757},
+ {0x01d4, 0x5757},
+ {0x01d5, 0x5757},
+ {0x01d6, 0x0000},
+ {0x01d7, 0x0000},
+ {0x01d8, 0x0162},
+ {0x01d9, 0x0007},
+ {0x01da, 0x0000},
+ {0x01db, 0x0004},
+ {0x01dc, 0x0000},
+ {0x01de, 0x7c00},
+ {0x01df, 0x0020},
+ {0x01e0, 0x04c1},
+ {0x01e1, 0x0000},
+ {0x01e2, 0x0000},
+ {0x01e3, 0x0000},
+ {0x01e4, 0x0000},
+ {0x01e5, 0x0000},
+ {0x01e6, 0x0001},
+ {0x01e7, 0x0000},
+ {0x01e8, 0x0000},
+ {0x01eb, 0x0000},
+ {0x01ec, 0x0000},
+ {0x01ed, 0x0000},
+ {0x01ee, 0x0000},
+ {0x01ef, 0x0000},
+ {0x01f0, 0x0000},
+ {0x01f1, 0x0000},
+ {0x01f2, 0x0000},
+ {0x01f3, 0x0000},
+ {0x01f4, 0x0000},
+ {0x0210, 0x6297},
+ {0x0211, 0xa004},
+ {0x0212, 0x0365},
+ {0x0213, 0xf7ff},
+ {0x0214, 0xf24c},
+ {0x0215, 0x0102},
+ {0x0216, 0x00a3},
+ {0x0217, 0x0048},
+ {0x0218, 0xa2c0},
+ {0x0219, 0x0400},
+ {0x021a, 0x00c8},
+ {0x021b, 0x00c0},
+ {0x021c, 0x0000},
+ {0x021d, 0x024c},
+ {0x02fa, 0x0000},
+ {0x02fb, 0x0000},
+ {0x02fc, 0x0000},
+ {0x03fe, 0x0000},
+ {0x03ff, 0x0000},
+ {0x0500, 0x0000},
+ {0x0600, 0x0000},
+ {0x0610, 0x6666},
+ {0x0611, 0xa9aa},
+ {0x0620, 0x6666},
+ {0x0621, 0xa9aa},
+ {0x0630, 0x6666},
+ {0x0631, 0xa9aa},
+ {0x0640, 0x6666},
+ {0x0641, 0xa9aa},
+ {0x07fa, 0x0000},
+ {0x08fa, 0x0000},
+ {0x08fb, 0x0000},
+ {0x0d00, 0x0000},
+ {0x1100, 0x0000},
+ {0x1101, 0x0000},
+ {0x1102, 0x0000},
+ {0x1103, 0x0000},
+ {0x1104, 0x0000},
+ {0x1105, 0x0000},
+ {0x1106, 0x0000},
+ {0x1107, 0x0000},
+ {0x1108, 0x0000},
+ {0x1109, 0x0000},
+ {0x110a, 0x0000},
+ {0x110b, 0x0000},
+ {0x110c, 0x0000},
+ {0x1111, 0x0000},
+ {0x1112, 0x0000},
+ {0x1113, 0x0000},
+ {0x1114, 0x0000},
+ {0x1115, 0x0000},
+ {0x1116, 0x0000},
+ {0x1117, 0x0000},
+ {0x1118, 0x0000},
+ {0x1119, 0x0000},
+ {0x111a, 0x0000},
+ {0x111b, 0x0000},
+ {0x111c, 0x0000},
+ {0x1401, 0x0404},
+ {0x1402, 0x0007},
+ {0x1403, 0x0365},
+ {0x1404, 0x0210},
+ {0x1405, 0x0365},
+ {0x1406, 0x0210},
+ {0x1407, 0x0000},
+ {0x1408, 0x0000},
+ {0x1409, 0x0000},
+ {0x140a, 0x0000},
+ {0x140b, 0x0000},
+ {0x140c, 0x0000},
+ {0x140d, 0x0000},
+ {0x140e, 0x0000},
+ {0x140f, 0x0000},
+ {0x1410, 0x0000},
+ {0x1411, 0x0000},
+ {0x1801, 0x0004},
+ {0x1802, 0x0000},
+ {0x1803, 0x0000},
+ {0x1804, 0x0000},
+ {0x1805, 0x00ff},
+ {0x2c00, 0x0000},
+ {0x3400, 0x0200},
+ {0x3404, 0x0000},
+ {0x3405, 0x0000},
+ {0x3406, 0x0000},
+ {0x3407, 0x0000},
+ {0x3408, 0x0000},
+ {0x3409, 0x0000},
+ {0x340a, 0x0000},
+ {0x340b, 0x0000},
+ {0x340c, 0x0000},
+ {0x340d, 0x0000},
+ {0x340e, 0x0000},
+ {0x340f, 0x0000},
+ {0x3410, 0x0000},
+ {0x3411, 0x0000},
+ {0x3412, 0x0000},
+ {0x3413, 0x0000},
+ {0x3414, 0x0000},
+ {0x3415, 0x0000},
+ {0x3424, 0x0000},
+ {0x3425, 0x0000},
+ {0x3426, 0x0000},
+ {0x3427, 0x0000},
+ {0x3428, 0x0000},
+ {0x3429, 0x0000},
+ {0x342a, 0x0000},
+ {0x342b, 0x0000},
+ {0x342c, 0x0000},
+ {0x342d, 0x0000},
+ {0x342e, 0x0000},
+ {0x342f, 0x0000},
+ {0x3430, 0x0000},
+ {0x3431, 0x0000},
+ {0x3432, 0x0000},
+ {0x3433, 0x0000},
+ {0x3434, 0x0000},
+ {0x3435, 0x0000},
+ {0x3440, 0x6319},
+ {0x3441, 0x3771},
+ {0x3500, 0x0002},
+ {0x3501, 0x5728},
+ {0x3b00, 0x3010},
+ {0x3b01, 0x3300},
+ {0x3b02, 0x2200},
+ {0x3b03, 0x0100},
+};
+
+static bool rt5682s_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682S_RESET:
+ case RT5682S_CBJ_CTRL_2:
+ case RT5682S_I2S1_F_DIV_CTRL_2:
+ case RT5682S_I2S2_F_DIV_CTRL_2:
+ case RT5682S_INT_ST_1:
+ case RT5682S_GPIO_ST:
+ case RT5682S_IL_CMD_1:
+ case RT5682S_4BTN_IL_CMD_1:
+ case RT5682S_AJD1_CTRL:
+ case RT5682S_VERSION_ID...RT5682S_DEVICE_ID:
+ case RT5682S_STO_NG2_CTRL_1:
+ case RT5682S_STO_NG2_CTRL_5...RT5682S_STO_NG2_CTRL_7:
+ case RT5682S_STO1_DAC_SIL_DET:
+ case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_4:
+ case RT5682S_HP_IMP_SENS_CTRL_13:
+ case RT5682S_HP_IMP_SENS_CTRL_14:
+ case RT5682S_HP_IMP_SENS_CTRL_43...RT5682S_HP_IMP_SENS_CTRL_46:
+ case RT5682S_HP_CALIB_CTRL_1:
+ case RT5682S_HP_CALIB_CTRL_10:
+ case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11:
+ case RT5682S_SAR_IL_CMD_2...RT5682S_SAR_IL_CMD_5:
+ case RT5682S_SAR_IL_CMD_10:
+ case RT5682S_SAR_IL_CMD_11:
+ case RT5682S_VERSION_ID_HIDE:
+ case RT5682S_VERSION_ID_CUS:
+ case RT5682S_I2C_TRANS_CTRL:
+ case RT5682S_DMIC_FLOAT_DET:
+ case RT5682S_HA_CMP_OP_1:
+ case RT5682S_NEW_CBJ_DET_CTL_10...RT5682S_NEW_CBJ_DET_CTL_16:
+ case RT5682S_CLK_SW_TEST_1:
+ case RT5682S_CLK_SW_TEST_2:
+ case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18:
+ case RT5682S_PILOT_DIG_CTL_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5682s_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5682S_RESET:
+ case RT5682S_VERSION_ID:
+ case RT5682S_VENDOR_ID:
+ case RT5682S_DEVICE_ID:
+ case RT5682S_HP_CTRL_1:
+ case RT5682S_HP_CTRL_2:
+ case RT5682S_HPL_GAIN:
+ case RT5682S_HPR_GAIN:
+ case RT5682S_I2C_CTRL:
+ case RT5682S_CBJ_BST_CTRL:
+ case RT5682S_CBJ_DET_CTRL:
+ case RT5682S_CBJ_CTRL_1...RT5682S_CBJ_CTRL_8:
+ case RT5682S_DAC1_DIG_VOL:
+ case RT5682S_STO1_ADC_DIG_VOL:
+ case RT5682S_STO1_ADC_BOOST:
+ case RT5682S_HP_IMP_GAIN_1:
+ case RT5682S_HP_IMP_GAIN_2:
+ case RT5682S_SIDETONE_CTRL:
+ case RT5682S_STO1_ADC_MIXER:
+ case RT5682S_AD_DA_MIXER:
+ case RT5682S_STO1_DAC_MIXER:
+ case RT5682S_A_DAC1_MUX:
+ case RT5682S_DIG_INF2_DATA:
+ case RT5682S_REC_MIXER:
+ case RT5682S_CAL_REC:
+ case RT5682S_HP_ANA_OST_CTRL_1...RT5682S_HP_ANA_OST_CTRL_3:
+ case RT5682S_PWR_DIG_1...RT5682S_PWR_MIXER:
+ case RT5682S_MB_CTRL:
+ case RT5682S_CLK_GATE_TCON_1...RT5682S_CLK_GATE_TCON_3:
+ case RT5682S_CLK_DET...RT5682S_LPF_AD_DMIC:
+ case RT5682S_I2S1_SDP:
+ case RT5682S_I2S2_SDP:
+ case RT5682S_ADDA_CLK_1:
+ case RT5682S_ADDA_CLK_2:
+ case RT5682S_I2S1_F_DIV_CTRL_1:
+ case RT5682S_I2S1_F_DIV_CTRL_2:
+ case RT5682S_TDM_CTRL:
+ case RT5682S_TDM_ADDA_CTRL_1:
+ case RT5682S_TDM_ADDA_CTRL_2:
+ case RT5682S_DATA_SEL_CTRL_1:
+ case RT5682S_TDM_TCON_CTRL_1:
+ case RT5682S_TDM_TCON_CTRL_2:
+ case RT5682S_GLB_CLK:
+ case RT5682S_PLL_TRACK_1...RT5682S_PLL_TRACK_6:
+ case RT5682S_PLL_TRACK_11:
+ case RT5682S_DEPOP_1:
+ case RT5682S_HP_CHARGE_PUMP_1:
+ case RT5682S_HP_CHARGE_PUMP_2:
+ case RT5682S_HP_CHARGE_PUMP_3:
+ case RT5682S_MICBIAS_1...RT5682S_MICBIAS_3:
+ case RT5682S_PLL_TRACK_12...RT5682S_PLL_CTRL_7:
+ case RT5682S_RC_CLK_CTRL:
+ case RT5682S_I2S2_M_CLK_CTRL_1:
+ case RT5682S_I2S2_F_DIV_CTRL_1:
+ case RT5682S_I2S2_F_DIV_CTRL_2:
+ case RT5682S_IRQ_CTRL_1...RT5682S_IRQ_CTRL_4:
+ case RT5682S_INT_ST_1:
+ case RT5682S_GPIO_CTRL_1:
+ case RT5682S_GPIO_CTRL_2:
+ case RT5682S_GPIO_ST:
+ case RT5682S_HP_AMP_DET_CTRL_1:
+ case RT5682S_MID_HP_AMP_DET:
+ case RT5682S_LOW_HP_AMP_DET:
+ case RT5682S_DELAY_BUF_CTRL:
+ case RT5682S_SV_ZCD_1:
+ case RT5682S_SV_ZCD_2:
+ case RT5682S_IL_CMD_1...RT5682S_IL_CMD_6:
+ case RT5682S_4BTN_IL_CMD_1...RT5682S_4BTN_IL_CMD_7:
+ case RT5682S_ADC_STO1_HP_CTRL_1:
+ case RT5682S_ADC_STO1_HP_CTRL_2:
+ case RT5682S_AJD1_CTRL:
+ case RT5682S_JD_CTRL_1:
+ case RT5682S_DUMMY_1...RT5682S_DUMMY_3:
+ case RT5682S_DAC_ADC_DIG_VOL1:
+ case RT5682S_BIAS_CUR_CTRL_2...RT5682S_BIAS_CUR_CTRL_10:
+ case RT5682S_VREF_REC_OP_FB_CAP_CTRL_1:
+ case RT5682S_VREF_REC_OP_FB_CAP_CTRL_2:
+ case RT5682S_CHARGE_PUMP_1:
+ case RT5682S_DIG_IN_CTRL_1:
+ case RT5682S_PAD_DRIVING_CTRL:
+ case RT5682S_CHOP_DAC_1:
+ case RT5682S_CHOP_DAC_2:
+ case RT5682S_CHOP_ADC:
+ case RT5682S_CALIB_ADC_CTRL:
+ case RT5682S_VOL_TEST:
+ case RT5682S_SPKVDD_DET_ST:
+ case RT5682S_TEST_MODE_CTRL_1...RT5682S_TEST_MODE_CTRL_4:
+ case RT5682S_PLL_INTERNAL_1...RT5682S_PLL_INTERNAL_4:
+ case RT5682S_STO_NG2_CTRL_1...RT5682S_STO_NG2_CTRL_10:
+ case RT5682S_STO1_DAC_SIL_DET:
+ case RT5682S_SIL_PSV_CTRL1:
+ case RT5682S_SIL_PSV_CTRL2:
+ case RT5682S_SIL_PSV_CTRL3:
+ case RT5682S_SIL_PSV_CTRL4:
+ case RT5682S_SIL_PSV_CTRL5:
+ case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_46:
+ case RT5682S_HP_LOGIC_CTRL_1...RT5682S_HP_LOGIC_CTRL_3:
+ case RT5682S_HP_CALIB_CTRL_1...RT5682S_HP_CALIB_CTRL_11:
+ case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11:
+ case RT5682S_SAR_IL_CMD_1...RT5682S_SAR_IL_CMD_14:
+ case RT5682S_DUMMY_4...RT5682S_DUMMY_6:
+ case RT5682S_VERSION_ID_HIDE:
+ case RT5682S_VERSION_ID_CUS:
+ case RT5682S_SCAN_CTL:
+ case RT5682S_HP_AMP_DET:
+ case RT5682S_BIAS_CUR_CTRL_11:
+ case RT5682S_BIAS_CUR_CTRL_12:
+ case RT5682S_BIAS_CUR_CTRL_13:
+ case RT5682S_BIAS_CUR_CTRL_14:
+ case RT5682S_BIAS_CUR_CTRL_15:
+ case RT5682S_BIAS_CUR_CTRL_16:
+ case RT5682S_BIAS_CUR_CTRL_17:
+ case RT5682S_BIAS_CUR_CTRL_18:
+ case RT5682S_I2C_TRANS_CTRL:
+ case RT5682S_DUMMY_7:
+ case RT5682S_DUMMY_8:
+ case RT5682S_DMIC_FLOAT_DET:
+ case RT5682S_HA_CMP_OP_1...RT5682S_HA_CMP_OP_13:
+ case RT5682S_HA_CMP_OP_14...RT5682S_HA_CMP_OP_25:
+ case RT5682S_NEW_CBJ_DET_CTL_1...RT5682S_NEW_CBJ_DET_CTL_16:
+ case RT5682S_DA_FILTER_1...RT5682S_DA_FILTER_5:
+ case RT5682S_CLK_SW_TEST_1:
+ case RT5682S_CLK_SW_TEST_2:
+ case RT5682S_CLK_SW_TEST_3...RT5682S_CLK_SW_TEST_14:
+ case RT5682S_EFUSE_MANU_WRITE_1...RT5682S_EFUSE_MANU_WRITE_6:
+ case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18:
+ case RT5682S_EFUSE_TIMING_CTL_1:
+ case RT5682S_EFUSE_TIMING_CTL_2:
+ case RT5682S_PILOT_DIG_CTL_1:
+ case RT5682S_PILOT_DIG_CTL_2:
+ case RT5682S_HP_AMP_DET_CTL_1...RT5682S_HP_AMP_DET_CTL_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void rt5682s_reset(struct rt5682s_priv *rt5682s)
+{
+ regmap_write(rt5682s->regmap, RT5682S_RESET, 0);
+}
+
+static int rt5682s_button_detect(struct snd_soc_component *component)
+{
+ int btn_type, val;
+
+ val = snd_soc_component_read(component, RT5682S_4BTN_IL_CMD_1);
+ btn_type = val & 0xfff0;
+ snd_soc_component_write(component, RT5682S_4BTN_IL_CMD_1, val);
+ dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2,
+ RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY);
+
+ return btn_type;
+}
+
+enum {
+ SAR_PWR_OFF,
+ SAR_PWR_NORMAL,
+ SAR_PWR_SAVING,
+};
+
+static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&rt5682s->sar_mutex);
+
+ switch (mode) {
+ case SAR_PWR_SAVING:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK |
+ RT5682S_VREF_POW_MASK, RT5682S_CTRL_MB1_FSM |
+ RT5682S_CTRL_MB2_FSM | RT5682S_VREF_POW_FSM);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_MANU);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK, RT5682S_SAR_BUTDET_EN);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2,
+ RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY);
+ break;
+ case SAR_PWR_NORMAL:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK,
+ RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_SEL_MB1_2_AUTO);
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK,
+ RT5682S_SAR_BUTDET_EN | RT5682S_SAR_BUTDET_POW_NORM);
+ break;
+ case SAR_PWR_OFF:
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK,
+ RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_MANU);
+ break;
+ default:
+ dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&rt5682s->sar_mutex);
+}
+
+static void rt5682s_enable_push_button_irq(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_BTN);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_EN |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_AUTO);
+ snd_soc_component_write(component, RT5682S_IL_CMD_1, 0x0040);
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK | RT5682S_4BTN_IL_RST_MASK,
+ RT5682S_4BTN_IL_EN | RT5682S_4BTN_IL_NOR);
+ snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3,
+ RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_EN);
+}
+
+static void rt5682s_disable_push_button_irq(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3,
+ RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_DIS);
+ snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2,
+ RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK |
+ RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS |
+ RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_MANU);
+}
+
+/**
+ * rt5682s_headset_detect - Detect headset.
+ * @component: SoC audio component device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_insert)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int val, count;
+ int jack_type = 0;
+
+ if (jack_insert) {
+ rt5682s_disable_push_button_irq(component);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2, 0);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2,
+ RT5682S_PWR_FV1 | RT5682S_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_CBJ, RT5682S_PWR_CBJ);
+ snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x0365);
+ snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK,
+ RT5682S_OSW_L_DIS | RT5682S_OSW_R_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13,
+ RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW);
+ usleep_range(45000, 50000);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_HIGH);
+
+ count = 0;
+ do {
+ usleep_range(10000, 15000);
+ val = snd_soc_component_read(component, RT5682S_CBJ_CTRL_2)
+ & RT5682S_JACK_TYPE_MASK;
+ count++;
+ } while (val == 0 && count < 50);
+
+ dev_dbg(component->dev, "%s, val=%d, count=%d\n", __func__, val, count);
+
+ switch (val) {
+ case 0x1:
+ case 0x2:
+ jack_type = SND_JACK_HEADSET;
+ snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x024c);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_DIS);
+ snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_SEL_MB1_2_MASK, val << RT5682S_SAR_SEL_MB1_2_SFT);
+ rt5682s_enable_push_button_irq(component);
+ rt5682s_sar_power_mode(component, SAR_PWR_SAVING);
+ break;
+ default:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+ snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK,
+ RT5682S_OSW_L_EN | RT5682S_OSW_R_EN);
+ usleep_range(35000, 40000);
+ } else {
+ rt5682s_sar_power_mode(component, SAR_PWR_OFF);
+ rt5682s_disable_push_button_irq(component);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW);
+
+ if (!rt5682s->wclk_enabled) {
+ snd_soc_component_update_bits(component,
+ RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2 | RT5682S_PWR_MB, 0);
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_CBJ, 0);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_DIS);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3,
+ RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_DIS);
+ jack_type = 0;
+ }
+
+ dev_dbg(component->dev, "jack_type = %d\n", jack_type);
+
+ return jack_type;
+}
+
+static void rt5682s_jack_detect_handler(struct work_struct *work)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(work, struct rt5682s_priv, jack_detect_work.work);
+ struct snd_soc_dapm_context *dapm;
+ int val, btn_type;
+
+ if (!rt5682s->component ||
+ !snd_soc_card_is_instantiated(rt5682s->component->card)) {
+ /* card not yet ready, try later */
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(15));
+ return;
+ }
+
+ dapm = snd_soc_component_to_dapm(rt5682s->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&rt5682s->calibrate_mutex);
+ mutex_lock(&rt5682s->wclk_mutex);
+
+ val = snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL)
+ & RT5682S_JDH_RS_MASK;
+ if (!val) {
+ /* jack in */
+ if (rt5682s->jack_type == 0) {
+ /* jack was out, report jack type */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 1);
+ rt5682s->irq_work_delay_time = 0;
+ } else if ((rt5682s->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ rt5682s->jack_type = SND_JACK_HEADSET;
+ btn_type = rt5682s_button_detect(rt5682s->component);
+ /**
+ * rt5682s can report three kinds of button behavior,
+ * one click, double click and hold. However,
+ * currently we will report button pressed/released
+ * event. So all the three button behaviors are
+ * treated as button pressed.
+ */
+ switch (btn_type) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ rt5682s->jack_type |= SND_JACK_BTN_0;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ rt5682s->jack_type |= SND_JACK_BTN_1;
+ break;
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ rt5682s->jack_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0040:
+ case 0x0020:
+ case 0x0010:
+ rt5682s->jack_type |= SND_JACK_BTN_3;
+ break;
+ case 0x0000: /* unpressed */
+ break;
+ default:
+ dev_err(rt5682s->component->dev,
+ "Unexpected button code 0x%04x\n", btn_type);
+ break;
+ }
+ }
+ } else {
+ /* jack out */
+ rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0);
+ rt5682s->irq_work_delay_time = 50;
+ }
+
+ mutex_unlock(&rt5682s->wclk_mutex);
+ mutex_unlock(&rt5682s->calibrate_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type,
+ SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (rt5682s->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ schedule_delayed_work(&rt5682s->jd_check_work, 0);
+ else
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+}
+
+static void rt5682s_jd_check_handler(struct work_struct *work)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(work, struct rt5682s_priv, jd_check_work.work);
+
+ if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) & RT5682S_JDH_RS_MASK) {
+ /* jack out */
+ schedule_delayed_work(&rt5682s->jack_detect_work, 0);
+ } else {
+ schedule_delayed_work(&rt5682s->jd_check_work, 500);
+ }
+}
+
+static irqreturn_t rt5682s_irq(int irq, void *data)
+{
+ struct rt5682s_priv *rt5682s = data;
+
+ mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work,
+ msecs_to_jiffies(rt5682s->irq_work_delay_time));
+
+ return IRQ_HANDLED;
+}
+
+static int rt5682s_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int btndet_delay = 16;
+
+ rt5682s->hs_jack = hs_jack;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_JDH, 0);
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+
+ return 0;
+ }
+
+ switch (rt5682s->pdata.jd_src) {
+ case RT5682S_JD1:
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_5,
+ RT5682S_JD_FAST_OFF_SRC_MASK, RT5682S_JD_FAST_OFF_SRC_JDH);
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_2,
+ RT5682S_EXT_JD_SRC, RT5682S_EXT_JD_SRC_MANUAL);
+ regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_1,
+ RT5682S_EMB_JD_MASK | RT5682S_DET_TYPE |
+ RT5682S_POL_FAST_OFF_MASK | RT5682S_MIC_CAP_MASK,
+ RT5682S_EMB_JD_EN | RT5682S_DET_TYPE |
+ RT5682S_POL_FAST_OFF_LOW | RT5682S_MIC_CAP_HS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_SAR_IL_CMD_1,
+ RT5682S_SAR_POW_MASK, RT5682S_SAR_POW_EN);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_IRQ);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BGLDO, RT5682S_PWR_BGLDO);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_JD_MASK, RT5682S_PWR_JD_ENABLE);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_IRQ | RT5682S_POW_JDH, RT5682S_POW_IRQ | RT5682S_POW_JDH);
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK | RT5682S_JD1_POL_MASK,
+ RT5682S_JD1_EN | RT5682S_JD1_POL_NOR);
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_4,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_5,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_6,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+ regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_7,
+ RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK,
+ (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay));
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(250));
+ break;
+
+ case RT5682S_JD_NULL:
+ regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2,
+ RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS);
+ regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL,
+ RT5682S_POW_JDH, 0);
+ break;
+
+ default:
+ dev_warn(component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9562, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+static const DECLARE_TLV_DB_SCALE(cbj_bst_tlv, -1200, 150, 0);
+
+static const struct snd_kcontrol_new rt5682s_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682S_DAC1_DIG_VOL,
+ RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 127, 0, dac_vol_tlv),
+
+ /* CBJ Boost Volume */
+ SOC_SINGLE_TLV("CBJ Boost Volume", RT5682S_REC_MIXER,
+ RT5682S_BST_CBJ_SFT, 35, 0, cbj_bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("STO1 ADC Capture Switch", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE_SFT, RT5682S_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682S_STO1_ADC_BOOST,
+ RT5682S_STO1_ADC_L_BST_SFT, RT5682S_STO1_ADC_R_BST_SFT, 3, 0, adc_bst_tlv),
+};
+
+/**
+ * rt5682s_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @component: SoC audio component device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682S can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the component driver will turn on
+ * ASRC for these filters if ASRC is selected as their clock source.
+ */
+int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ switch (clk_src) {
+ case RT5682S_CLK_SEL_SYS:
+ case RT5682S_CLK_SEL_I2S1_ASRC:
+ case RT5682S_CLK_SEL_I2S2_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5682S_DA_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_2,
+ RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5682S_AD_STEREO1_FILTER) {
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_3,
+ RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT);
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_11,
+ RT5682S_ASRCIN_AUTO_CLKOUT_MASK, RT5682S_ASRCIN_AUTO_CLKOUT_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682s_sel_asrc_clk_src);
+
+static int rt5682s_div_sel(struct rt5682s_priv *rt5682s,
+ int target, const int div[], int size)
+{
+ int i;
+
+ if (rt5682s->sysclk < target) {
+ dev_err(rt5682s->component->dev,
+ "sysclk rate %d is too low\n", rt5682s->sysclk);
+ return 0;
+ }
+
+ for (i = 0; i < size - 1; i++) {
+ dev_dbg(rt5682s->component->dev, "div[%d]=%d\n", i, div[i]);
+ if (target * div[i] == rt5682s->sysclk)
+ return i;
+ if (target * div[i + 1] > rt5682s->sysclk) {
+ dev_dbg(rt5682s->component->dev,
+ "can't find div for sysclk %d\n", rt5682s->sysclk);
+ return i;
+ }
+ }
+
+ if (target * div[i] < rt5682s->sysclk)
+ dev_err(rt5682s->component->dev,
+ "sysclk rate %d is too high\n", rt5682s->sysclk);
+
+ return size - 1;
+}
+
+static int get_clk_info(int sclk, int rate)
+{
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int idx, dmic_clk_rate = 3072000;
+ static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128};
+
+ if (rt5682s->pdata.dmic_clk_rate)
+ dmic_clk_rate = rt5682s->pdata.dmic_clk_rate;
+
+ idx = rt5682s_div_sel(rt5682s, dmic_clk_rate, div, ARRAY_SIZE(div));
+
+ snd_soc_component_update_bits(component, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_CLK_MASK, idx << RT5682S_DMIC_CLK_SFT);
+
+ return 0;
+}
+
+
+static int rt5682s_set_pllb_power(struct rt5682s_priv *rt5682s, int on)
+{
+ struct snd_soc_component *component = rt5682s->component;
+
+ if (on) {
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | RT5682S_PWR_PLLB,
+ RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | RT5682S_PWR_PLLB);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_RSTB_PLLB, RT5682S_RSTB_PLLB);
+ } else {
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB |
+ RT5682S_RSTB_PLLB | RT5682S_PWR_PLLB, 0);
+ }
+
+ return 0;
+}
+
+static int set_pllb_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int on = 0;
+
+ if (rt5682s->wclk_enabled)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ on = 1;
+
+ rt5682s_set_pllb_power(rt5682s, on);
+
+ return 0;
+}
+
+static void rt5682s_set_filter_clk(struct rt5682s_priv *rt5682s, int reg, int ref)
+{
+ struct snd_soc_component *component = rt5682s->component;
+ int idx;
+ static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
+ static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48};
+
+ idx = rt5682s_div_sel(rt5682s, ref, div_f, ARRAY_SIZE(div_f));
+
+ snd_soc_component_update_bits(component, reg,
+ RT5682S_FILTER_CLK_DIV_MASK, idx << RT5682S_FILTER_CLK_DIV_SFT);
+
+ /* select over sample rate */
+ for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) {
+ if (rt5682s->sysclk <= 12288000 * div_o[idx])
+ break;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1,
+ RT5682S_ADC_OSR_MASK | RT5682S_DAC_OSR_MASK,
+ (idx << RT5682S_ADC_OSR_SFT) | (idx << RT5682S_DAC_OSR_SFT));
+}
+
+static int set_filter_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int ref, reg, val;
+
+ val = snd_soc_component_read(component, RT5682S_GPIO_CTRL_1)
+ & RT5682S_GP4_PIN_MASK;
+
+ if (w->shift == RT5682S_PWR_ADC_S1F_BIT && val == RT5682S_GP4_PIN_ADCDAT2)
+ ref = 256 * rt5682s->lrck[RT5682S_AIF2];
+ else
+ ref = 256 * rt5682s->lrck[RT5682S_AIF1];
+
+ if (w->shift == RT5682S_PWR_ADC_S1F_BIT)
+ reg = RT5682S_PLL_TRACK_3;
+ else
+ reg = RT5682S_PLL_TRACK_2;
+
+ rt5682s_set_filter_clk(rt5682s, reg, ref);
+
+ return 0;
+}
+
+static int set_dmic_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 50, val;
+
+ if (rt5682s->pdata.dmic_delay)
+ delay = rt5682s->pdata.dmic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = (snd_soc_component_read(component, RT5682S_GLB_CLK)
+ & RT5682S_SCLK_SRC_MASK) >> RT5682S_SCLK_SRC_SFT;
+ if (val == RT5682S_CLK_SRC_PLL1 || val == RT5682S_CLK_SRC_PLL2)
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+
+ /*Add delay to avoid pop noise*/
+ msleep(delay);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ if (!rt5682s->jack_type && !rt5682s->wclk_enabled) {
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void rt5682s_set_i2s(struct rt5682s_priv *rt5682s, int id, int on)
+{
+ struct snd_soc_component *component = rt5682s->component;
+ int pre_div;
+ unsigned int p_reg, p_mask, p_sft;
+ unsigned int c_reg, c_mask, c_sft;
+
+ if (id == RT5682S_AIF1) {
+ c_reg = RT5682S_ADDA_CLK_1;
+ c_mask = RT5682S_I2S_M_D_MASK;
+ c_sft = RT5682S_I2S_M_D_SFT;
+ p_reg = RT5682S_PWR_DIG_1;
+ p_mask = RT5682S_PWR_I2S1;
+ p_sft = RT5682S_PWR_I2S1_BIT;
+ } else {
+ c_reg = RT5682S_I2S2_M_CLK_CTRL_1;
+ c_mask = RT5682S_I2S2_M_D_MASK;
+ c_sft = RT5682S_I2S2_M_D_SFT;
+ p_reg = RT5682S_PWR_DIG_1;
+ p_mask = RT5682S_PWR_I2S2;
+ p_sft = RT5682S_PWR_I2S2_BIT;
+ }
+
+ if (on && rt5682s->master[id]) {
+ pre_div = get_clk_info(rt5682s->sysclk, rt5682s->lrck[id]);
+ if (pre_div < 0) {
+ dev_err(component->dev, "get pre_div failed\n");
+ return;
+ }
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d master\n",
+ rt5682s->lrck[id], pre_div, id);
+ snd_soc_component_update_bits(component, c_reg, c_mask, pre_div << c_sft);
+ }
+
+ snd_soc_component_update_bits(component, p_reg, p_mask, on << p_sft);
+}
+
+static int set_i2s_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int on = 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ on = 1;
+
+ if (!snd_soc_dapm_widget_name_cmp(w, "I2S1") && !rt5682s->wclk_enabled)
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, on);
+ else if (!snd_soc_dapm_widget_name_cmp(w, "I2S2"))
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF2, on);
+
+ return 0;
+}
+
+static int is_sys_clk_from_plla(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if ((rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL1) ||
+ (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2 && rt5682s->pll_comb == USE_PLLAB))
+ return 1;
+
+ return 0;
+}
+
+static int is_sys_clk_from_pllb(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2)
+ return 1;
+
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *w,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int reg, sft, val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (w->shift) {
+ case RT5682S_ADC_STO1_ASRC_SFT:
+ reg = RT5682S_PLL_TRACK_3;
+ sft = RT5682S_FILTER_CLK_SEL_SFT;
+ break;
+ case RT5682S_DAC_STO1_ASRC_SFT:
+ reg = RT5682S_PLL_TRACK_2;
+ sft = RT5682S_FILTER_CLK_SEL_SFT;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_component_read(component, reg) >> sft) & 0xf;
+ switch (val) {
+ case RT5682S_CLK_SEL_I2S1_ASRC:
+ case RT5682S_CLK_SEL_I2S2_ASRC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN);
+ snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_11, 0x6666);
+ snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_12, 0xa82a);
+
+ snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
+ RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
+ RT5682S_HPO_SEL_IP_EN_SW, RT5682S_HPO_L_PATH_EN |
+ RT5682S_HPO_R_PATH_EN | RT5682S_HPO_IP_EN_GATING);
+ usleep_range(5000, 10000);
+ snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_L | RT5682S_CP_SW_SIZE_S);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2,
+ RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK |
+ RT5682S_HPO_SEL_IP_EN_SW, 0);
+ snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN |
+ RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN, 0);
+ snd_soc_component_update_bits(component, RT5682S_DEPOP_1,
+ RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5682s_stereo1_adc_mixl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int delay = 0;
+
+ if (rt5682s->pdata.amic_delay)
+ delay = rt5682s->pdata.amic_delay;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(delay);
+ snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_L_MUTE, RT5682S_L_MUTE);
+ break;
+ }
+
+ return 0;
+}
+
+static int sar_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if ((rt5682s->jack_type & SND_JACK_HEADSET) != SND_JACK_HEADSET)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ rt5682s_sar_power_mode(component, SAR_PWR_NORMAL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ rt5682s_sar_power_mode(component, SAR_PWR_SAVING);
+ break;
+ }
+
+ return 0;
+}
+
+/* Interface data select */
+static const char * const rt5682s_data_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if2_adc_enum, RT5682S_DIG_INF2_DATA,
+ RT5682S_IF2_ADC_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_01_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC1_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_23_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC2_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_45_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC3_SEL_SFT, rt5682s_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_if1_67_adc_enum, RT5682S_TDM_ADDA_CTRL_1,
+ RT5682S_IF1_ADC4_SEL_SFT, rt5682s_data_select);
+
+static const struct snd_kcontrol_new rt5682s_if2_adc_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682s_if2_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_01_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682s_if1_01_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_23_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682s_if1_23_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_45_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682s_if1_45_adc_enum);
+
+static const struct snd_kcontrol_new rt5682s_if1_67_adc_swap_mux =
+ SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682s_if1_67_adc_enum);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5682s_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER,
+ RT5682S_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER,
+ RT5682S_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_L1_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_L1_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER,
+ RT5682S_M_DAC_R1_STO_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5682s_rec1_l_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER,
+ RT5682S_M_CBJ_RM1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5682s_rec1_r_mix[] = {
+ SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER,
+ RT5682S_M_CBJ_RM1_R_SFT, 1, 1),
+};
+
+/* STO1 ADC1 Source */
+/* MX-26 [13] [5] */
+static const char * const rt5682s_sto1_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1l_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC1L_SRC_SFT, rt5682s_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc1l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1r_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC1R_SRC_SFT, rt5682s_sto1_adc1_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc1r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1r_enum);
+
+/* STO1 ADC Source */
+/* MX-26 [11:10] [3:2] */
+static const char * const rt5682s_sto1_adc_src[] = {
+ "ADC1 L", "ADC1 R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcl_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADCL_SRC_SFT, rt5682s_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adcl_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682s_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcr_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADCR_SRC_SFT, rt5682s_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adcr_mux =
+ SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682s_sto1_adcr_enum);
+
+/* STO1 ADC2 Source */
+/* MX-26 [12] [4] */
+static const char * const rt5682s_sto1_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2l_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC2L_SRC_SFT, rt5682s_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc2l_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682s_sto1_adc2l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2r_enum, RT5682S_STO1_ADC_MIXER,
+ RT5682S_STO1_ADC2R_SRC_SFT, rt5682s_sto1_adc2_src);
+
+static const struct snd_kcontrol_new rt5682s_sto1_adc2r_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682s_sto1_adc2r_enum);
+
+/* MX-79 [6:4] I2S1 ADC data location */
+static const unsigned int rt5682s_if1_adc_slot_values[] = {
+ 0, 2, 4, 6,
+};
+
+static const char * const rt5682s_if1_adc_slot_src[] = {
+ "Slot 0", "Slot 2", "Slot 4", "Slot 6"
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_if1_adc_slot_enum,
+ RT5682S_TDM_CTRL, RT5682S_TDM_ADC_LCA_SFT, RT5682S_TDM_ADC_LCA_MASK,
+ rt5682s_if1_adc_slot_src, rt5682s_if1_adc_slot_values);
+
+static const struct snd_kcontrol_new rt5682s_if1_adc_slot_mux =
+ SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682s_if1_adc_slot_enum);
+
+/* Analog DAC L1 Source, Analog DAC R1 Source*/
+/* MX-2B [4], MX-2B [0]*/
+static const char * const rt5682s_alg_dac1_src[] = {
+ "Stereo1 DAC Mixer", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_l1_enum, RT5682S_A_DAC1_MUX,
+ RT5682S_A_DACL1_SFT, rt5682s_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682s_alg_dac_l1_mux =
+ SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682s_alg_dac_l1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_r1_enum, RT5682S_A_DAC1_MUX,
+ RT5682S_A_DACR1_SFT, rt5682s_alg_dac1_src);
+
+static const struct snd_kcontrol_new rt5682s_alg_dac_r1_mux =
+ SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682s_alg_dac_r1_enum);
+
+static const unsigned int rt5682s_adcdat_pin_values[] = {
+ 1, 3,
+};
+
+static const char * const rt5682s_adcdat_pin_select[] = {
+ "ADCDAT1", "ADCDAT2",
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_adcdat_pin_enum,
+ RT5682S_GPIO_CTRL_1, RT5682S_GP4_PIN_SFT, RT5682S_GP4_PIN_MASK,
+ rt5682s_adcdat_pin_select, rt5682s_adcdat_pin_values);
+
+static const struct snd_kcontrol_new rt5682s_adcdat_pin_ctrl =
+ SOC_DAPM_ENUM("ADCDAT", rt5682s_adcdat_pin_enum);
+
+static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO MB1", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO MB2", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_MB2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO", RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_BIT, 0, NULL, 0),
+
+ /* PLL Powers */
+ SND_SOC_DAPM_SUPPLY_S("PLLA_LDO", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_LDO_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA_BIAS", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_BIAS_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA", 0, RT5682S_PWR_ANLG_3,
+ RT5682S_PWR_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("PLLA_RST", 1, RT5682S_PWR_ANLG_3,
+ RT5682S_RSTB_PLLA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLB", SND_SOC_NOPM, 0, 0,
+ set_pllb_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DAC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_ADC_STO1_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_AD_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DA_ASRC_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682S_PLL_TRACK_1,
+ RT5682S_DMIC_ASRC_SFT, 0, NULL, 0),
+
+ /* Input Side */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MB1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MB2_BIT, 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682S_DMIC_CTRL_1, RT5682S_DMIC_1_EN_SFT, 0,
+ set_dmic_power, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682s_rec1_l_mix,
+ ARRAY_SIZE(rt5682s_rec1_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5682s_rec1_r_mix,
+ ARRAY_SIZE(rt5682s_rec1_r_mix)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682S_CAL_REC,
+ RT5682S_PWR_RM1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5682S_CAL_REC,
+ RT5682S_PWR_RM1_R_BIT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682S_PWR_DIG_1,
+ RT5682S_PWR_ADC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682S_PWR_DIG_1,
+ RT5682S_PWR_ADC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682S_CHOP_ADC,
+ RT5682S_CKGEN_ADC1_SFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc1l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc1r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc2l_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adc2r_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_sto1_adcr_mux),
+ SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_adc_slot_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682S_PWR_DIG_2,
+ RT5682S_PWR_ADC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER_E("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_adc_l_mix, ARRAY_SIZE(rt5682s_sto1_adc_l_mix),
+ rt5682s_stereo1_adc_mixl_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682S_STO1_ADC_DIG_VOL,
+ RT5682S_R_MUTE_SFT, 1, rt5682s_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5682s_sto1_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", SND_SOC_NOPM, 0, 0,
+ set_i2s_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("I2S2", SND_SOC_NOPM, 0, 0,
+ set_i2s_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_01_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_23_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_45_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if1_67_adc_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5682s_if2_adc_swap_mux),
+
+ SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, &rt5682s_adcdat_pin_ctrl),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, RT5682S_I2S1_SDP,
+ RT5682S_SEL_ADCDAT_SFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_PIN_CFG_SFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_dac_l_mix, ARRAY_SIZE(rt5682s_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682s_dac_r_mix, ARRAY_SIZE(rt5682s_dac_r_mix)),
+
+ /* DAC channel Mux */
+ SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_l1_mux),
+ SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_r1_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682S_PWR_DIG_2,
+ RT5682S_PWR_DAC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_dac_l_mix, ARRAY_SIZE(rt5682s_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5682s_sto1_dac_r_mix, ARRAY_SIZE(rt5682s_sto1_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_R1_BIT, 0),
+
+ /* HPO */
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682s_hp_amp_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* CLK DET */
+ SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682S_CLK_DET,
+ RT5682S_SYS_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682S_CLK_DET,
+ RT5682S_PLL1_CLK_DET_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MCLK0 DET PWR", RT5682S_PWR_ANLG_2,
+ RT5682S_PWR_MCLK0_WD_BIT, 0, NULL, 0),
+
+ /* SAR */
+ SND_SOC_DAPM_SUPPLY("SAR", SND_SOC_NOPM, 0, 0, sar_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route rt5682s_dapm_routes[] = {
+ /*PLL*/
+ {"ADC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla},
+ {"ADC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb},
+ {"DAC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla},
+ {"DAC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb},
+ {"PLLA", NULL, "PLLA_LDO"},
+ {"PLLA", NULL, "PLLA_BIAS"},
+ {"PLLA", NULL, "PLLA_RST"},
+
+ /*ASRC*/
+ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
+ {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
+ {"ADC STO1 ASRC", NULL, "AD ASRC"},
+ {"ADC STO1 ASRC", NULL, "DA ASRC"},
+ {"DAC STO1 ASRC", NULL, "AD ASRC"},
+ {"DAC STO1 ASRC", NULL, "DA ASRC"},
+
+ {"CLKDET SYS", NULL, "MCLK0 DET PWR"},
+
+ {"BST1 CBJ", NULL, "IN1P"},
+ {"BST1 CBJ", NULL, "SAR"},
+
+ {"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1L", NULL, "RECMIX1L Power"},
+ {"RECMIX1R", "CBJ Switch", "BST1 CBJ"},
+ {"RECMIX1R", NULL, "RECMIX1R Power"},
+
+ {"ADC1 L", NULL, "RECMIX1L"},
+ {"ADC1 L", NULL, "ADC1 L Power"},
+ {"ADC1 L", NULL, "ADC1 clock"},
+ {"ADC1 R", NULL, "RECMIX1R"},
+ {"ADC1 R", NULL, "ADC1 R Power"},
+ {"ADC1 R", NULL, "ADC1 clock"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC CLK", NULL, "DMIC ASRC"},
+
+ {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"},
+ {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"},
+ {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"},
+
+ {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"},
+ {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"},
+ {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"},
+
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"},
+ {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+
+ {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"},
+ {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"},
+ {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"},
+ {"AIF1TX", NULL, "I2S1"},
+ {"AIF1TX", NULL, "ADCDAT Mux"},
+ {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"},
+ {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"},
+ {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"},
+ {"AIF2TX", NULL, "ADCDAT Mux"},
+
+ {"IF1 DAC1 L", NULL, "AIF1RX"},
+ {"IF1 DAC1 L", NULL, "I2S1"},
+ {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"},
+ {"IF1 DAC1 R", NULL, "AIF1RX"},
+ {"IF1 DAC1 R", NULL, "I2S1"},
+ {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"},
+
+ {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"},
+ {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"},
+
+ {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"},
+ {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"},
+
+ {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"},
+ {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"},
+
+ {"DAC L1 Source", "DAC1", "DAC1 MIXL"},
+ {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"},
+ {"DAC R1 Source", "DAC1", "DAC1 MIXR"},
+ {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"},
+
+ {"DAC L1", NULL, "DAC L1 Source"},
+ {"DAC R1", NULL, "DAC R1 Source"},
+
+ {"HP Amp", NULL, "DAC L1"},
+ {"HP Amp", NULL, "DAC R1"},
+ {"HP Amp", NULL, "CLKDET SYS"},
+ {"HP Amp", NULL, "SAR"},
+
+ {"HPOL", NULL, "HP Amp"},
+ {"HPOR", NULL, "HP Amp"},
+};
+
+static int rt5682s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int cl, val = 0, tx_slotnum;
+
+ if (tx_mask || rx_mask)
+ snd_soc_component_update_bits(component,
+ RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, RT5682S_TDM_EN);
+ else
+ snd_soc_component_update_bits(component,
+ RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, 0);
+
+ /* Tx slot configuration */
+ tx_slotnum = hweight_long(tx_mask);
+ if (tx_slotnum) {
+ if (tx_slotnum > slots) {
+ dev_err(component->dev, "Invalid or oversized Tx slots.\n");
+ return -EINVAL;
+ }
+ val |= (tx_slotnum - 1) << RT5682S_TDM_ADC_DL_SFT;
+ }
+
+ switch (slots) {
+ case 4:
+ val |= RT5682S_TDM_TX_CH_4;
+ val |= RT5682S_TDM_RX_CH_4;
+ break;
+ case 6:
+ val |= RT5682S_TDM_TX_CH_6;
+ val |= RT5682S_TDM_RX_CH_6;
+ break;
+ case 8:
+ val |= RT5682S_TDM_TX_CH_8;
+ val |= RT5682S_TDM_RX_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_TDM_CTRL,
+ RT5682S_TDM_TX_CH_MASK | RT5682S_TDM_RX_CH_MASK |
+ RT5682S_TDM_ADC_DL_MASK, val);
+
+ switch (slot_width) {
+ case 8:
+ if (tx_mask || rx_mask)
+ return -EINVAL;
+ cl = RT5682S_I2S1_TX_CHL_8 | RT5682S_I2S1_RX_CHL_8;
+ break;
+ case 16:
+ val = RT5682S_TDM_CL_16;
+ cl = RT5682S_I2S1_TX_CHL_16 | RT5682S_I2S1_RX_CHL_16;
+ break;
+ case 20:
+ val = RT5682S_TDM_CL_20;
+ cl = RT5682S_I2S1_TX_CHL_20 | RT5682S_I2S1_RX_CHL_20;
+ break;
+ case 24:
+ val = RT5682S_TDM_CL_24;
+ cl = RT5682S_I2S1_TX_CHL_24 | RT5682S_I2S1_RX_CHL_24;
+ break;
+ case 32:
+ val = RT5682S_TDM_CL_32;
+ cl = RT5682S_I2S1_TX_CHL_32 | RT5682S_I2S1_RX_CHL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_CL_MASK, val);
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_TX_CHL_MASK | RT5682S_I2S1_RX_CHL_MASK, cl);
+
+ return 0;
+}
+
+static int rt5682s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int len_1 = 0, len_2 = 0;
+ int frame_size;
+
+ rt5682s->lrck[dai->id] = params_rate(params);
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ len_1 |= RT5682S_I2S1_DL_20;
+ len_2 |= RT5682S_I2S2_DL_20;
+ break;
+ case 24:
+ len_1 |= RT5682S_I2S1_DL_24;
+ len_2 |= RT5682S_I2S2_DL_24;
+ break;
+ case 32:
+ len_1 |= RT5682S_I2S1_DL_32;
+ len_2 |= RT5682S_I2S2_DL_24;
+ break;
+ case 8:
+ len_1 |= RT5682S_I2S2_DL_8;
+ len_2 |= RT5682S_I2S2_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682S_AIF1:
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_DL_MASK, len_1);
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_DIS);
+ break;
+ case RT5682S_AIF2:
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_DL_MASK, len_2);
+ if (params_channels(params) == 1) /* mono mode */
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_EN);
+ else
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_DIS);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, tdm_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ rt5682s->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ rt5682s->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5682S_I2S_BP_INV;
+ tdm_ctrl |= RT5682S_TDM_S_BP_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ if (dai->id == RT5682S_AIF1)
+ tdm_ctrl |= RT5682S_TDM_S_LP_INV | RT5682S_TDM_M_BP_INV;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ if (dai->id == RT5682S_AIF1)
+ tdm_ctrl |= RT5682S_TDM_S_BP_INV | RT5682S_TDM_S_LP_INV |
+ RT5682S_TDM_M_BP_INV | RT5682S_TDM_M_LP_INV;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5682S_I2S_DF_LEFT;
+ tdm_ctrl |= RT5682S_TDM_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5682S_I2S_DF_PCM_A;
+ tdm_ctrl |= RT5682S_TDM_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5682S_I2S_DF_PCM_B;
+ tdm_ctrl |= RT5682S_TDM_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5682S_AIF1:
+ snd_soc_component_update_bits(component, RT5682S_I2S1_SDP,
+ RT5682S_I2S_DF_MASK, reg_val);
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_MS_MASK | RT5682S_TDM_S_BP_MASK |
+ RT5682S_TDM_DF_MASK | RT5682S_TDM_M_BP_MASK |
+ RT5682S_TDM_M_LP_MASK | RT5682S_TDM_S_LP_MASK,
+ tdm_ctrl | rt5682s->master[dai->id]);
+ break;
+ case RT5682S_AIF2:
+ if (rt5682s->master[dai->id] == 0)
+ reg_val |= RT5682S_I2S2_MS_S;
+ snd_soc_component_update_bits(component, RT5682S_I2S2_SDP,
+ RT5682S_I2S2_MS_MASK | RT5682S_I2S_BP_MASK |
+ RT5682S_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5682s_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ unsigned int src = 0;
+
+ if (freq == rt5682s->sysclk && clk_id == rt5682s->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5682S_SCLK_S_MCLK:
+ src = RT5682S_CLK_SRC_MCLK;
+ break;
+ case RT5682S_SCLK_S_PLL1:
+ src = RT5682S_CLK_SRC_PLL1;
+ break;
+ case RT5682S_SCLK_S_PLL2:
+ src = RT5682S_CLK_SRC_PLL2;
+ break;
+ case RT5682S_SCLK_S_RCCLK:
+ src = RT5682S_CLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_SCLK_SRC_MASK, src << RT5682S_SCLK_SRC_SFT);
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1,
+ RT5682S_I2S_M_CLK_SRC_MASK, src << RT5682S_I2S_M_CLK_SRC_SFT);
+ snd_soc_component_update_bits(component, RT5682S_I2S2_M_CLK_CTRL_1,
+ RT5682S_I2S2_M_CLK_SRC_MASK, src << RT5682S_I2S2_M_CLK_SRC_SFT);
+
+ rt5682s->sysclk = freq;
+ rt5682s->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static const struct pll_calc_map plla_table[] = {
+ {2048000, 24576000, 0, 46, 2, true, false, false, false},
+ {256000, 24576000, 0, 382, 2, true, false, false, false},
+ {512000, 24576000, 0, 190, 2, true, false, false, false},
+ {4096000, 24576000, 0, 22, 2, true, false, false, false},
+ {1024000, 24576000, 0, 94, 2, true, false, false, false},
+ {11289600, 22579200, 1, 22, 2, false, false, false, false},
+ {1411200, 22579200, 0, 62, 2, true, false, false, false},
+ {2822400, 22579200, 0, 30, 2, true, false, false, false},
+ {12288000, 24576000, 1, 22, 2, false, false, false, false},
+ {1536000, 24576000, 0, 62, 2, true, false, false, false},
+ {3072000, 24576000, 0, 30, 2, true, false, false, false},
+ {24576000, 49152000, 4, 22, 0, false, false, false, false},
+ {3072000, 49152000, 0, 30, 0, true, false, false, false},
+ {6144000, 49152000, 0, 30, 0, false, false, false, false},
+ {49152000, 98304000, 10, 22, 0, false, true, false, false},
+ {6144000, 98304000, 0, 30, 0, false, true, false, false},
+ {12288000, 98304000, 1, 22, 0, false, true, false, false},
+ {48000000, 3840000, 10, 22, 23, false, false, false, false},
+ {24000000, 3840000, 4, 22, 23, false, false, false, false},
+ {19200000, 3840000, 3, 23, 23, false, false, false, false},
+ {38400000, 3840000, 8, 23, 23, false, false, false, false},
+};
+
+static const struct pll_calc_map pllb_table[] = {
+ {48000000, 24576000, 8, 6, 3, false, false, false, false},
+ {48000000, 22579200, 23, 12, 3, false, false, false, true},
+ {24000000, 24576000, 3, 6, 3, false, false, false, false},
+ {24000000, 22579200, 23, 26, 3, false, false, false, true},
+ {19200000, 24576000, 2, 6, 3, false, false, false, false},
+ {19200000, 22579200, 3, 5, 3, false, false, false, true},
+ {38400000, 24576000, 6, 6, 3, false, false, false, false},
+ {38400000, 22579200, 8, 5, 3, false, false, false, true},
+ {3840000, 49152000, 0, 6, 0, true, false, false, false},
+};
+
+static int find_pll_inter_combination(unsigned int f_in, unsigned int f_out,
+ struct pll_calc_map *a, struct pll_calc_map *b)
+{
+ int i, j;
+
+ /* Look at PLLA table */
+ for (i = 0; i < ARRAY_SIZE(plla_table); i++) {
+ if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == f_out) {
+ memcpy(a, plla_table + i, sizeof(*a));
+ return USE_PLLA;
+ }
+ }
+
+ /* Look at PLLB table */
+ for (i = 0; i < ARRAY_SIZE(pllb_table); i++) {
+ if (pllb_table[i].freq_in == f_in && pllb_table[i].freq_out == f_out) {
+ memcpy(b, pllb_table + i, sizeof(*b));
+ return USE_PLLB;
+ }
+ }
+
+ /* Find a combination of PLLA & PLLB */
+ for (i = ARRAY_SIZE(plla_table) - 1; i >= 0; i--) {
+ if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == 3840000) {
+ for (j = ARRAY_SIZE(pllb_table) - 1; j >= 0; j--) {
+ if (pllb_table[j].freq_in == 3840000 &&
+ pllb_table[j].freq_out == f_out) {
+ memcpy(a, plla_table + i, sizeof(*a));
+ memcpy(b, pllb_table + j, sizeof(*b));
+ return USE_PLLAB;
+ }
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int rt5682s_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct pll_calc_map a_map, b_map;
+
+ if (source == rt5682s->pll_src[pll_id] && freq_in == rt5682s->pll_in[pll_id] &&
+ freq_out == rt5682s->pll_out[pll_id])
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt5682s->pll_in[pll_id] = 0;
+ rt5682s->pll_out[pll_id] = 0;
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_SCLK_SRC_MASK, RT5682S_CLK_SRC_MCLK << RT5682S_SCLK_SRC_SFT);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5682S_PLL_S_MCLK:
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_MCLK);
+ break;
+ case RT5682S_PLL_S_BCLK1:
+ snd_soc_component_update_bits(component, RT5682S_GLB_CLK,
+ RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_BCLK1);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ rt5682s->pll_comb = find_pll_inter_combination(freq_in, freq_out,
+ &a_map, &b_map);
+
+ if ((pll_id == RT5682S_PLL1 && rt5682s->pll_comb == USE_PLLA) ||
+ (pll_id == RT5682S_PLL2 && (rt5682s->pll_comb == USE_PLLB ||
+ rt5682s->pll_comb == USE_PLLAB))) {
+ dev_dbg(component->dev,
+ "Supported freq conversion for PLL%d:(%d->%d): %d\n",
+ pll_id + 1, freq_in, freq_out, rt5682s->pll_comb);
+ } else {
+ dev_err(component->dev,
+ "Unsupported freq conversion for PLL%d:(%d->%d): %d\n",
+ pll_id + 1, freq_in, freq_out, rt5682s->pll_comb);
+ return -EINVAL;
+ }
+
+ if (rt5682s->pll_comb == USE_PLLA || rt5682s->pll_comb == USE_PLLAB) {
+ dev_dbg(component->dev,
+ "PLLA: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d\n",
+ a_map.freq_in, a_map.freq_out, a_map.m_bp, a_map.k_bp,
+ (a_map.m_bp ? 0 : a_map.m), a_map.n, (a_map.k_bp ? 0 : a_map.k));
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_1,
+ RT5682S_PLLA_N_MASK, a_map.n);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_2,
+ RT5682S_PLLA_M_MASK | RT5682S_PLLA_K_MASK,
+ a_map.m << RT5682S_PLLA_M_SFT | a_map.k);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6,
+ RT5682S_PLLA_M_BP_MASK | RT5682S_PLLA_K_BP_MASK,
+ a_map.m_bp << RT5682S_PLLA_M_BP_SFT |
+ a_map.k_bp << RT5682S_PLLA_K_BP_SFT);
+ }
+
+ if (rt5682s->pll_comb == USE_PLLB || rt5682s->pll_comb == USE_PLLAB) {
+ dev_dbg(component->dev,
+ "PLLB: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d byp_ps=%d sel_ps=%d\n",
+ b_map.freq_in, b_map.freq_out, b_map.m_bp, b_map.k_bp,
+ (b_map.m_bp ? 0 : b_map.m), b_map.n, (b_map.k_bp ? 0 : b_map.k),
+ b_map.byp_ps, b_map.sel_ps);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_3,
+ RT5682S_PLLB_N_MASK, b_map.n);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_4,
+ RT5682S_PLLB_M_MASK | RT5682S_PLLB_K_MASK,
+ b_map.m << RT5682S_PLLB_M_SFT | b_map.k);
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6,
+ RT5682S_PLLB_SEL_PS_MASK | RT5682S_PLLB_BYP_PS_MASK |
+ RT5682S_PLLB_M_BP_MASK | RT5682S_PLLB_K_BP_MASK,
+ b_map.sel_ps << RT5682S_PLLB_SEL_PS_SFT |
+ b_map.byp_ps << RT5682S_PLLB_BYP_PS_SFT |
+ b_map.m_bp << RT5682S_PLLB_M_BP_SFT |
+ b_map.k_bp << RT5682S_PLLB_K_BP_SFT);
+ }
+
+ if (rt5682s->pll_comb == USE_PLLB)
+ snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_7,
+ RT5682S_PLLB_SRC_MASK, RT5682S_PLLB_SRC_DFIN);
+
+ rt5682s->pll_in[pll_id] = freq_in;
+ rt5682s->pll_out[pll_id] = freq_out;
+ rt5682s->pll_src[pll_id] = source;
+
+ return 0;
+}
+
+static int rt5682s_set_bclk1_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 256:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_256);
+ break;
+ case 128:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_128);
+ break;
+ case 64:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1,
+ RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->bclk[dai->id] = ratio;
+
+ switch (ratio) {
+ case 64:
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2,
+ RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_64);
+ break;
+ case 32:
+ snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2,
+ RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_32);
+ break;
+ default:
+ dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5682s_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_PWR_LDO, RT5682S_PWR_LDO);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1, RT5682S_PWR_LDO, 0);
+ if (!rt5682s->wclk_enabled)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, 0);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_COMMON_CLK
+#define CLK_PLL2_FIN 48000000
+#define CLK_48 48000
+#define CLK_44 44100
+
+static bool rt5682s_clk_check(struct rt5682s_priv *rt5682s)
+{
+ if (!rt5682s->master[RT5682S_AIF1]) {
+ dev_dbg(rt5682s->component->dev, "dai clk fmt not set correctly\n");
+ return false;
+ }
+ return true;
+}
+
+static int rt5682s_wclk_prepare(struct clk_hw *hw)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ int ref, reg;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ mutex_lock(&rt5682s->wclk_mutex);
+
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_MB);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_FV2, RT5682S_PWR_FV2);
+
+ /* Set and power on I2S1 */
+ snd_soc_component_update_bits(component, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL);
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, 1);
+
+ /* Only need to power on PLLB due to the rate set restriction */
+ reg = RT5682S_PLL_TRACK_2;
+ ref = 256 * rt5682s->lrck[RT5682S_AIF1];
+ rt5682s_set_filter_clk(rt5682s, reg, ref);
+ rt5682s_set_pllb_power(rt5682s, 1);
+
+ rt5682s->wclk_enabled = 1;
+
+ mutex_unlock(&rt5682s->wclk_mutex);
+
+ return 0;
+}
+
+static void rt5682s_wclk_unprepare(struct clk_hw *hw)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return;
+
+ mutex_lock(&rt5682s->wclk_mutex);
+
+ if (!rt5682s->jack_type)
+ snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, 0);
+
+ /* Power down I2S1 */
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, 0);
+ snd_soc_component_update_bits(component, RT5682S_PWR_DIG_1,
+ RT5682S_DIG_GATE_CTRL, 0);
+
+ /* Power down PLLB */
+ rt5682s_set_pllb_power(rt5682s, 0);
+
+ rt5682s->wclk_enabled = 0;
+
+ mutex_unlock(&rt5682s->wclk_mutex);
+}
+
+static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return 0;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ */
+ if (rt5682s->lrck[RT5682S_AIF1] != CLK_48 &&
+ rt5682s->lrck[RT5682S_AIF1] != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ return 0;
+ }
+
+ return rt5682s->lrck[RT5682S_AIF1];
+}
+
+static int rt5682s_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ const char * const clk_name = clk_hw_get_name(hw);
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+ /*
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ * It will force to 48kHz if not both.
+ */
+ if (req->rate != CLK_48 && req->rate != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ req->rate = CLK_48;
+ }
+
+ return 0;
+}
+
+static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct clk *parent_clk;
+ const char * const clk_name = clk_hw_get_name(hw);
+ unsigned int clk_pll2_fout;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ /*
+ * Whether the wclk's parent clk (mclk) exists or not, please ensure
+ * it is fixed or set to 48MHz before setting wclk rate. It's a
+ * temporary limitation. Only accept 48MHz clk as the clk provider.
+ *
+ * It will set the codec anyway by assuming mclk is 48MHz.
+ */
+ parent_clk = clk_get_parent(hw->clk);
+ if (!parent_clk)
+ dev_warn(component->dev,
+ "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n",
+ CLK_PLL2_FIN);
+
+ if (parent_rate != CLK_PLL2_FIN)
+ dev_warn(component->dev, "clk %s only support %d Hz input\n",
+ clk_name, CLK_PLL2_FIN);
+
+ /*
+ * To achieve the rate conversion from 48MHz to 44.1k or 48kHz,
+ * PLL2 is needed.
+ */
+ clk_pll2_fout = rate * 512;
+ rt5682s_set_component_pll(component, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ CLK_PLL2_FIN, clk_pll2_fout);
+
+ rt5682s_set_component_sysclk(component, RT5682S_SCLK_S_PLL2, 0,
+ clk_pll2_fout, SND_SOC_CLOCK_IN);
+
+ rt5682s->lrck[RT5682S_AIF1] = rate;
+
+ return 0;
+}
+
+static unsigned long rt5682s_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ unsigned int bclks_per_wclk;
+
+ bclks_per_wclk = snd_soc_component_read(component, RT5682S_TDM_TCON_CTRL_1);
+
+ switch (bclks_per_wclk & RT5682S_TDM_BCLK_MS1_MASK) {
+ case RT5682S_TDM_BCLK_MS1_256:
+ return parent_rate * 256;
+ case RT5682S_TDM_BCLK_MS1_128:
+ return parent_rate * 128;
+ case RT5682S_TDM_BCLK_MS1_64:
+ return parent_rate * 64;
+ case RT5682S_TDM_BCLK_MS1_32:
+ return parent_rate * 32;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long rt5682s_bclk_get_factor(unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long factor;
+
+ factor = rate / parent_rate;
+ if (factor < 64)
+ return 32;
+ else if (factor < 128)
+ return 64;
+ else if (factor < 256)
+ return 128;
+ else
+ return 256;
+}
+
+static int rt5682s_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_parent_rate || !rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ /*
+ * BCLK rates are set as a multiplier of WCLK in HW.
+ * We don't allow changing the parent WCLK. We just do
+ * some rounding down based on the parent WCLK rate
+ * and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = rt5682s_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = rt5682s->component;
+ struct snd_soc_dai *dai;
+ unsigned long factor;
+
+ if (!rt5682s_clk_check(rt5682s))
+ return -EINVAL;
+
+ factor = rt5682s_bclk_get_factor(rate, parent_rate);
+
+ for_each_component_dais(component, dai)
+ if (dai->id == RT5682S_AIF1)
+ return rt5682s_set_bclk1_ratio(dai, factor);
+
+ dev_err(component->dev, "dai %d not found in component\n",
+ RT5682S_AIF1);
+ return -ENODEV;
+}
+
+static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = {
+ [RT5682S_DAI_WCLK_IDX] = {
+ .prepare = rt5682s_wclk_prepare,
+ .unprepare = rt5682s_wclk_unprepare,
+ .recalc_rate = rt5682s_wclk_recalc_rate,
+ .determine_rate = rt5682s_wclk_determine_rate,
+ .set_rate = rt5682s_wclk_set_rate,
+ },
+ [RT5682S_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682s_bclk_recalc_rate,
+ .determine_rate = rt5682s_bclk_determine_rate,
+ .set_rate = rt5682s_bclk_set_rate,
+ },
+};
+
+static int rt5682s_register_dai_clks(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ struct rt5682s_platform_data *pdata = &rt5682s->pdata;
+ struct clk_hw *dai_clk_hw;
+ int i, ret;
+
+ for (i = 0; i < RT5682S_DAI_NUM_CLKS; ++i) {
+ struct clk_init_data init = { };
+ struct clk_parent_data parent_data;
+ const struct clk_hw *parent;
+
+ dai_clk_hw = &rt5682s->dai_clks_hw[i];
+
+ switch (i) {
+ case RT5682S_DAI_WCLK_IDX:
+ /* Make MCLK the parent of WCLK */
+ if (rt5682s->mclk) {
+ parent_data = (struct clk_parent_data){
+ .fw_name = "mclk",
+ };
+ init.parent_data = &parent_data;
+ init.num_parents = 1;
+ }
+ break;
+ case RT5682S_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent = &rt5682s->dai_clks_hw[RT5682S_DAI_WCLK_IDX];
+ init.parent_hws = &parent;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ return -EINVAL;
+ }
+
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &rt5682s_dai_clk_ops[i];
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+ dai_clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, dai_clk_hw);
+ if (ret) {
+ dev_warn(dev, "Failed to register %s: %d\n", init.name, ret);
+ return ret;
+ }
+
+ if (dev->of_node) {
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, dai_clk_hw);
+ if (ret)
+ return ret;
+ } else {
+ ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw,
+ init.name, dev_name(dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rt5682s_dai_probe_clks(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Check if MCLK provided */
+ rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5682s->mclk))
+ return PTR_ERR(rt5682s->mclk);
+
+ /* Register CCF DAI clock control */
+ ret = rt5682s_register_dai_clks(component);
+ if (ret)
+ return ret;
+
+ /* Initial setup for CCF */
+ rt5682s->lrck[RT5682S_AIF1] = CLK_48;
+
+ return 0;
+}
+#else
+static inline int rt5682s_dai_probe_clks(struct snd_soc_component *component)
+{
+ return 0;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+static int rt5682s_probe(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s->component = component;
+
+ return rt5682s_dai_probe_clks(component);
+}
+
+static void rt5682s_remove(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ rt5682s_reset(rt5682s);
+}
+
+#ifdef CONFIG_PM
+static int rt5682s_suspend(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ if (rt5682s->irq)
+ disable_irq(rt5682s->irq);
+
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ if (rt5682s->hs_jack)
+ rt5682s->jack_type = rt5682s_headset_detect(component, 0);
+
+ regcache_cache_only(rt5682s->regmap, true);
+ regcache_mark_dirty(rt5682s->regmap);
+
+ return 0;
+}
+
+static int rt5682s_resume(struct snd_soc_component *component)
+{
+ struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt5682s->regmap, false);
+ regcache_sync(rt5682s->regmap);
+
+ if (rt5682s->hs_jack) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt5682s->jack_detect_work, msecs_to_jiffies(0));
+ }
+
+ if (rt5682s->irq)
+ enable_irq(rt5682s->irq);
+
+ return 0;
+}
+#else
+#define rt5682s_suspend NULL
+#define rt5682s_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops rt5682s_aif1_dai_ops = {
+ .hw_params = rt5682s_hw_params,
+ .set_fmt = rt5682s_set_dai_fmt,
+ .set_tdm_slot = rt5682s_set_tdm_slot,
+ .set_bclk_ratio = rt5682s_set_bclk1_ratio,
+};
+
+static const struct snd_soc_dai_ops rt5682s_aif2_dai_ops = {
+ .hw_params = rt5682s_hw_params,
+ .set_fmt = rt5682s_set_dai_fmt,
+ .set_bclk_ratio = rt5682s_set_bclk2_ratio,
+};
+
+static const struct snd_soc_component_driver rt5682s_soc_component_dev = {
+ .probe = rt5682s_probe,
+ .remove = rt5682s_remove,
+ .suspend = rt5682s_suspend,
+ .resume = rt5682s_resume,
+ .set_bias_level = rt5682s_set_bias_level,
+ .controls = rt5682s_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5682s_snd_controls),
+ .dapm_widgets = rt5682s_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5682s_dapm_widgets),
+ .dapm_routes = rt5682s_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5682s_dapm_routes),
+ .set_sysclk = rt5682s_set_component_sysclk,
+ .set_pll = rt5682s_set_component_pll,
+ .set_jack = rt5682s_set_jack_detect,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,dmic1-data-pin",
+ &rt5682s->pdata.dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic1-clk-pin",
+ &rt5682s->pdata.dmic1_clk_pin);
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt5682s->pdata.jd_src);
+ device_property_read_u32(dev, "realtek,dmic-clk-rate-hz",
+ &rt5682s->pdata.dmic_clk_rate);
+ device_property_read_u32(dev, "realtek,dmic-delay-ms",
+ &rt5682s->pdata.dmic_delay);
+ device_property_read_u32(dev, "realtek,amic-delay-ms",
+ &rt5682s->pdata.amic_delay);
+ device_property_read_u32(dev, "realtek,ldo-sel",
+ &rt5682s->pdata.ldo_dacref);
+
+ if (device_property_read_string_array(dev, "clock-output-names",
+ rt5682s->pdata.dai_clk_names,
+ RT5682S_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ rt5682s->pdata.dai_clk_names[RT5682S_DAI_WCLK_IDX],
+ rt5682s->pdata.dai_clk_names[RT5682S_DAI_BCLK_IDX]);
+
+ rt5682s->pdata.dmic_clk_driving_high = device_property_read_bool(dev,
+ "realtek,dmic-clk-driving-high");
+
+ return 0;
+}
+
+static void rt5682s_calibrate(struct rt5682s_priv *rt5682s)
+{
+ unsigned int count, value;
+
+ mutex_lock(&rt5682s->calibrate_mutex);
+
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80);
+ usleep_range(15000, 20000);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xfa80);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x01c0);
+ regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0380);
+ regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x8000);
+ regmap_write(rt5682s->regmap, RT5682S_ADDA_CLK_1, 0x1001);
+ regmap_write(rt5682s->regmap, RT5682S_CHOP_DAC_2, 0x3030);
+ regmap_write(rt5682s->regmap, RT5682S_CHOP_ADC, 0xb000);
+ regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0x686c);
+ regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5151);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0321);
+ regmap_write(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2, 0x0004);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0x7c00);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0xfc00);
+
+ for (count = 0; count < 60; count++) {
+ regmap_read(rt5682s->regmap, RT5682S_HP_CALIB_ST_1, &value);
+ if (!(value & 0x8000))
+ break;
+
+ usleep_range(10000, 10005);
+ }
+
+ if (count >= 60)
+ dev_err(rt5682s->component->dev, "HP Calibration Failure\n");
+
+ /* restore settings */
+ regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0180);
+ regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5858);
+ regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0xc0c4);
+ regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0320);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0);
+ regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800);
+ regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000);
+
+ mutex_unlock(&rt5682s->calibrate_mutex);
+}
+
+static const struct regmap_config rt5682s_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = RT5682S_MAX_REG,
+ .volatile_reg = rt5682s_volatile_register,
+ .readable_reg = rt5682s_readable_register,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = rt5682s_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5682s_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static struct snd_soc_dai_driver rt5682s_dai[] = {
+ {
+ .name = "rt5682s-aif1",
+ .id = RT5682S_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .ops = &rt5682s_aif1_dai_ops,
+ },
+ {
+ .name = "rt5682s-aif2",
+ .id = RT5682S_AIF2,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5682S_STEREO_RATES,
+ .formats = RT5682S_FORMATS,
+ },
+ .ops = &rt5682s_aif2_dai_ops,
+ },
+};
+
+static void rt5682s_i2c_disable_regulators(void *data)
+{
+ struct rt5682s_priv *rt5682s = data;
+ struct device *dev = regmap_get_device(rt5682s->regmap);
+ int ret;
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply AVDD: %d\n", ret);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_DBVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply DBVDD: %d\n", ret);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_LDO1_IN].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply LDO1-IN: %d\n", ret);
+
+ usleep_range(1000, 1500);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply MICVDD: %d\n", ret);
+}
+
+static int rt5682s_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5682s_priv *rt5682s;
+ int i, ret;
+ unsigned int val;
+
+ rt5682s = devm_kzalloc(&i2c->dev, sizeof(struct rt5682s_priv), GFP_KERNEL);
+ if (!rt5682s)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5682s);
+
+ rt5682s->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt5682s->pdata = *pdata;
+ else
+ rt5682s_parse_dt(rt5682s, &i2c->dev);
+
+ rt5682s->regmap = devm_regmap_init_i2c(i2c, &rt5682s_regmap);
+ if (IS_ERR(rt5682s->regmap)) {
+ ret = PTR_ERR(rt5682s->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rt5682s->supplies); i++)
+ rt5682s->supplies[i].supply = rt5682s_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, rt5682s_i2c_disable_regulators, rt5682s);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply MICVDD: %d\n", ret);
+ return ret;
+ }
+ usleep_range(1000, 1500);
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply AVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_DBVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply DBVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_LDO1_IN].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply LDO1-IN: %d\n", ret);
+ return ret;
+ }
+
+ rt5682s->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5682s->ldo1_en)) {
+ dev_err(&i2c->dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5682s->ldo1_en);
+ }
+
+ /* Sleep for 50 ms minimum */
+ usleep_range(50000, 55000);
+
+ regmap_read(rt5682s->regmap, RT5682S_DEVICE_ID, &val);
+ if (val != DEVICE_ID) {
+ dev_err(&i2c->dev, "Device with ID register %x is not rt5682s\n", val);
+ return -ENODEV;
+ }
+
+ rt5682s_reset(rt5682s);
+ rt5682s_apply_patch_list(rt5682s, &i2c->dev);
+
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_2,
+ RT5682S_DLDO_I_LIMIT_MASK, RT5682S_DLDO_I_LIMIT_DIS);
+ usleep_range(20000, 25000);
+
+ mutex_init(&rt5682s->calibrate_mutex);
+ mutex_init(&rt5682s->sar_mutex);
+ mutex_init(&rt5682s->wclk_mutex);
+ rt5682s_calibrate(rt5682s);
+
+ regmap_update_bits(rt5682s->regmap, RT5682S_MICBIAS_2,
+ RT5682S_PWR_CLK25M_MASK | RT5682S_PWR_CLK1M_MASK,
+ RT5682S_PWR_CLK25M_PD | RT5682S_PWR_CLK1M_PU);
+ regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_1,
+ RT5682S_PWR_BG, RT5682S_PWR_BG);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2,
+ RT5682S_HP_SIG_SRC_MASK, RT5682S_HP_SIG_SRC_1BIT_CTL);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_CHARGE_PUMP_2,
+ RT5682S_PM_HP_MASK, RT5682S_PM_HP_HV);
+ regmap_update_bits(rt5682s->regmap, RT5682S_HP_AMP_DET_CTL_1,
+ RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M);
+
+ /* DMIC data pin */
+ switch (rt5682s->pdata.dmic1_data_pin) {
+ case RT5682S_DMIC1_DATA_NULL:
+ break;
+ case RT5682S_DMIC1_DATA_GPIO2: /* share with LRCK2 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO2);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP2_PIN_MASK, RT5682S_GP2_PIN_DMIC_SDA);
+ break;
+ case RT5682S_DMIC1_DATA_GPIO5: /* share with DACDAT1 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1,
+ RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP5_PIN_MASK, RT5682S_GP5_PIN_DMIC_SDA);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n");
+ break;
+ }
+
+ /* DMIC clk pin */
+ switch (rt5682s->pdata.dmic1_clk_pin) {
+ case RT5682S_DMIC1_CLK_NULL:
+ break;
+ case RT5682S_DMIC1_CLK_GPIO1: /* share with IRQ */
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_DMIC_CLK);
+ break;
+ case RT5682S_DMIC1_CLK_GPIO3: /* share with BCLK2 */
+ regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1,
+ RT5682S_GP3_PIN_MASK, RT5682S_GP3_PIN_DMIC_CLK);
+ if (rt5682s->pdata.dmic_clk_driving_high)
+ regmap_update_bits(rt5682s->regmap, RT5682S_PAD_DRIVING_CTRL,
+ RT5682S_PAD_DRV_GP3_MASK, RT5682S_PAD_DRV_GP3_HIGH);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n");
+ break;
+ }
+
+ /* LDO output voltage control */
+ switch (rt5682s->pdata.ldo_dacref) {
+ case RT5682S_LDO_1_607V:
+ break;
+ case RT5682S_LDO_1_5V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_5V);
+ break;
+ case RT5682S_LDO_1_406V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_406V);
+ break;
+ case RT5682S_LDO_1_731V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_731V);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid LDO output setting.\n");
+ break;
+ }
+
+ INIT_DELAYED_WORK(&rt5682s->jack_detect_work, rt5682s_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt5682s->jd_check_work, rt5682s_jd_check_handler);
+
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682s_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "rt5682s", rt5682s);
+ if (!ret)
+ rt5682s->irq = i2c->irq;
+ else
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ }
+
+ return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev,
+ rt5682s_dai, ARRAY_SIZE(rt5682s_dai));
+}
+
+static void rt5682s_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt5682s_priv *rt5682s = i2c_get_clientdata(client);
+
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&rt5682s->jack_detect_work);
+ cancel_delayed_work_sync(&rt5682s->jd_check_work);
+
+ rt5682s_reset(rt5682s);
+}
+
+static void rt5682s_i2c_remove(struct i2c_client *client)
+{
+ rt5682s_i2c_shutdown(client);
+}
+
+static const struct of_device_id rt5682s_of_match[] = {
+ {.compatible = "realtek,rt5682s"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5682s_of_match);
+
+static const struct acpi_device_id rt5682s_acpi_match[] = {
+ {"RTL5682", 0,},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt5682s_acpi_match);
+
+static const struct i2c_device_id rt5682s_i2c_id[] = {
+ {"rt5682s"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5682s_i2c_id);
+
+static struct i2c_driver rt5682s_i2c_driver = {
+ .driver = {
+ .name = "rt5682s",
+ .of_match_table = rt5682s_of_match,
+ .acpi_match_table = rt5682s_acpi_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = rt5682s_i2c_probe,
+ .remove = rt5682s_i2c_remove,
+ .shutdown = rt5682s_i2c_shutdown,
+ .id_table = rt5682s_i2c_id,
+};
+module_i2c_driver(rt5682s_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5682I-VS driver");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
new file mode 100644
index 000000000000..67f42898de96
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.h
@@ -0,0 +1,1492 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682s.h -- RT5682I-VS ALSA SoC audio driver
+ *
+ * Copyright 2021 Realtek Microelectronics
+ * Author: Derek Fang <derek.fang@realtek.com>
+ */
+
+#ifndef __RT5682S_H__
+#define __RT5682S_H__
+
+#include <sound/rt5682s.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+
+/* Info */
+#define RT5682S_RESET 0x0000
+#define RT5682S_VERSION_ID 0x00fd
+#define RT5682S_VENDOR_ID 0x00fe
+#define RT5682S_DEVICE_ID 0x00ff
+/* I/O - Output */
+#define RT5682S_HP_CTRL_1 0x0002
+#define RT5682S_HP_CTRL_2 0x0003
+#define RT5682S_HPL_GAIN 0x0005
+#define RT5682S_HPR_GAIN 0x0006
+
+#define RT5682S_I2C_CTRL 0x0008
+
+/* I/O - Input */
+#define RT5682S_CBJ_BST_CTRL 0x000b
+#define RT5682S_CBJ_DET_CTRL 0x000f
+#define RT5682S_CBJ_CTRL_1 0x0010
+#define RT5682S_CBJ_CTRL_2 0x0011
+#define RT5682S_CBJ_CTRL_3 0x0012
+#define RT5682S_CBJ_CTRL_4 0x0013
+#define RT5682S_CBJ_CTRL_5 0x0014
+#define RT5682S_CBJ_CTRL_6 0x0015
+#define RT5682S_CBJ_CTRL_7 0x0016
+#define RT5682S_CBJ_CTRL_8 0x0017
+/* I/O - ADC/DAC/DMIC */
+#define RT5682S_DAC1_DIG_VOL 0x0019
+#define RT5682S_STO1_ADC_DIG_VOL 0x001c
+#define RT5682S_STO1_ADC_BOOST 0x001f
+#define RT5682S_HP_IMP_GAIN_1 0x0022
+#define RT5682S_HP_IMP_GAIN_2 0x0023
+/* Mixer - D-D */
+#define RT5682S_SIDETONE_CTRL 0x0024
+#define RT5682S_STO1_ADC_MIXER 0x0026
+#define RT5682S_AD_DA_MIXER 0x0029
+#define RT5682S_STO1_DAC_MIXER 0x002a
+#define RT5682S_A_DAC1_MUX 0x002b
+#define RT5682S_DIG_INF2_DATA 0x0030
+/* Mixer - ADC */
+#define RT5682S_REC_MIXER 0x003c
+#define RT5682S_CAL_REC 0x0044
+/* HP Analog Offset Control */
+#define RT5682S_HP_ANA_OST_CTRL_1 0x004b
+#define RT5682S_HP_ANA_OST_CTRL_2 0x004c
+#define RT5682S_HP_ANA_OST_CTRL_3 0x004d
+/* Power */
+#define RT5682S_PWR_DIG_1 0x0061
+#define RT5682S_PWR_DIG_2 0x0062
+#define RT5682S_PWR_ANLG_1 0x0063
+#define RT5682S_PWR_ANLG_2 0x0064
+#define RT5682S_PWR_ANLG_3 0x0065
+#define RT5682S_PWR_MIXER 0x0066
+
+#define RT5682S_MB_CTRL 0x0067
+#define RT5682S_CLK_GATE_TCON_1 0x0068
+#define RT5682S_CLK_GATE_TCON_2 0x0069
+#define RT5682S_CLK_GATE_TCON_3 0x006a
+/* Clock Detect */
+#define RT5682S_CLK_DET 0x006b
+/* Filter Auto Reset */
+#define RT5682S_RESET_LPF_CTRL 0x006c
+#define RT5682S_RESET_HPF_CTRL 0x006d
+/* DMIC */
+#define RT5682S_DMIC_CTRL_1 0x006e
+#define RT5682S_LPF_AD_DMIC 0x006f
+/* Format - ADC/DAC */
+#define RT5682S_I2S1_SDP 0x0070
+#define RT5682S_I2S2_SDP 0x0071
+#define RT5682S_ADDA_CLK_1 0x0073
+#define RT5682S_ADDA_CLK_2 0x0074
+#define RT5682S_I2S1_F_DIV_CTRL_1 0x0075
+#define RT5682S_I2S1_F_DIV_CTRL_2 0x0076
+/* Format - TDM Control */
+#define RT5682S_TDM_CTRL 0x0079
+#define RT5682S_TDM_ADDA_CTRL_1 0x007a
+#define RT5682S_TDM_ADDA_CTRL_2 0x007b
+#define RT5682S_DATA_SEL_CTRL_1 0x007c
+#define RT5682S_TDM_TCON_CTRL_1 0x007e
+#define RT5682S_TDM_TCON_CTRL_2 0x007f
+/* Function - Analog */
+#define RT5682S_GLB_CLK 0x0080
+#define RT5682S_PLL_TRACK_1 0x0083
+#define RT5682S_PLL_TRACK_2 0x0084
+#define RT5682S_PLL_TRACK_3 0x0085
+#define RT5682S_PLL_TRACK_4 0x0086
+#define RT5682S_PLL_TRACK_5 0x0087
+#define RT5682S_PLL_TRACK_6 0x0088
+#define RT5682S_PLL_TRACK_11 0x008c
+#define RT5682S_DEPOP_1 0x008e
+#define RT5682S_HP_CHARGE_PUMP_1 0x008f
+#define RT5682S_HP_CHARGE_PUMP_2 0x0091
+#define RT5682S_HP_CHARGE_PUMP_3 0x0092
+#define RT5682S_MICBIAS_1 0x0093
+#define RT5682S_MICBIAS_2 0x0094
+#define RT5682S_MICBIAS_3 0x0095
+
+#define RT5682S_PLL_TRACK_12 0x0096
+#define RT5682S_PLL_TRACK_14 0x0097
+#define RT5682S_PLL_CTRL_1 0x0098
+#define RT5682S_PLL_CTRL_2 0x0099
+#define RT5682S_PLL_CTRL_3 0x009a
+#define RT5682S_PLL_CTRL_4 0x009b
+#define RT5682S_PLL_CTRL_5 0x009c
+#define RT5682S_PLL_CTRL_6 0x009d
+#define RT5682S_PLL_CTRL_7 0x009e
+
+#define RT5682S_RC_CLK_CTRL 0x009f
+#define RT5682S_I2S2_M_CLK_CTRL_1 0x00a0
+#define RT5682S_I2S2_F_DIV_CTRL_1 0x00a3
+#define RT5682S_I2S2_F_DIV_CTRL_2 0x00a4
+
+#define RT5682S_IRQ_CTRL_1 0x00b6
+#define RT5682S_IRQ_CTRL_2 0x00b7
+#define RT5682S_IRQ_CTRL_3 0x00b8
+#define RT5682S_IRQ_CTRL_4 0x00b9
+#define RT5682S_INT_ST_1 0x00be
+#define RT5682S_GPIO_CTRL_1 0x00c0
+#define RT5682S_GPIO_CTRL_2 0x00c1
+#define RT5682S_GPIO_ST 0x00c2
+#define RT5682S_HP_AMP_DET_CTRL_1 0x00d0
+#define RT5682S_MID_HP_AMP_DET 0x00d2
+#define RT5682S_LOW_HP_AMP_DET 0x00d3
+#define RT5682S_DELAY_BUF_CTRL 0x00d4
+#define RT5682S_SV_ZCD_1 0x00d9
+#define RT5682S_SV_ZCD_2 0x00da
+#define RT5682S_IL_CMD_1 0x00db
+#define RT5682S_IL_CMD_2 0x00dc
+#define RT5682S_IL_CMD_3 0x00dd
+#define RT5682S_IL_CMD_4 0x00de
+#define RT5682S_IL_CMD_5 0x00df
+#define RT5682S_IL_CMD_6 0x00e0
+#define RT5682S_4BTN_IL_CMD_1 0x00e2
+#define RT5682S_4BTN_IL_CMD_2 0x00e3
+#define RT5682S_4BTN_IL_CMD_3 0x00e4
+#define RT5682S_4BTN_IL_CMD_4 0x00e5
+#define RT5682S_4BTN_IL_CMD_5 0x00e6
+#define RT5682S_4BTN_IL_CMD_6 0x00e7
+#define RT5682S_4BTN_IL_CMD_7 0x00e8
+
+#define RT5682S_ADC_STO1_HP_CTRL_1 0x00ea
+#define RT5682S_ADC_STO1_HP_CTRL_2 0x00eb
+#define RT5682S_AJD1_CTRL 0x00f0
+#define RT5682S_JD_CTRL_1 0x00f6
+/* General Control */
+#define RT5682S_DUMMY_1 0x00fa
+#define RT5682S_DUMMY_2 0x00fb
+#define RT5682S_DUMMY_3 0x00fc
+
+#define RT5682S_DAC_ADC_DIG_VOL1 0x0100
+#define RT5682S_BIAS_CUR_CTRL_2 0x010b
+#define RT5682S_BIAS_CUR_CTRL_3 0x010c
+#define RT5682S_BIAS_CUR_CTRL_4 0x010d
+#define RT5682S_BIAS_CUR_CTRL_5 0x010e
+#define RT5682S_BIAS_CUR_CTRL_6 0x010f
+#define RT5682S_BIAS_CUR_CTRL_7 0x0110
+#define RT5682S_BIAS_CUR_CTRL_8 0x0111
+#define RT5682S_BIAS_CUR_CTRL_9 0x0112
+#define RT5682S_BIAS_CUR_CTRL_10 0x0113
+#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_1 0x0117
+#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_2 0x0118
+#define RT5682S_CHARGE_PUMP_1 0x0125
+#define RT5682S_DIG_IN_CTRL_1 0x0132
+#define RT5682S_PAD_DRIVING_CTRL 0x0136
+#define RT5682S_CHOP_DAC_1 0x0139
+#define RT5682S_CHOP_DAC_2 0x013a
+#define RT5682S_CHOP_ADC 0x013b
+#define RT5682S_CALIB_ADC_CTRL 0x013c
+#define RT5682S_VOL_TEST 0x013f
+#define RT5682S_SPKVDD_DET_ST 0x0142
+#define RT5682S_TEST_MODE_CTRL_1 0x0145
+#define RT5682S_TEST_MODE_CTRL_2 0x0146
+#define RT5682S_TEST_MODE_CTRL_3 0x0147
+#define RT5682S_TEST_MODE_CTRL_4 0x0148
+#define RT5682S_PLL_INTERNAL_1 0x0156
+#define RT5682S_PLL_INTERNAL_2 0x0157
+#define RT5682S_PLL_INTERNAL_3 0x0158
+#define RT5682S_PLL_INTERNAL_4 0x0159
+#define RT5682S_STO_NG2_CTRL_1 0x0160
+#define RT5682S_STO_NG2_CTRL_2 0x0161
+#define RT5682S_STO_NG2_CTRL_3 0x0162
+#define RT5682S_STO_NG2_CTRL_4 0x0163
+#define RT5682S_STO_NG2_CTRL_5 0x0164
+#define RT5682S_STO_NG2_CTRL_6 0x0165
+#define RT5682S_STO_NG2_CTRL_7 0x0166
+#define RT5682S_STO_NG2_CTRL_8 0x0167
+#define RT5682S_STO_NG2_CTRL_9 0x0168
+#define RT5682S_STO_NG2_CTRL_10 0x0169
+#define RT5682S_STO1_DAC_SIL_DET 0x0190
+#define RT5682S_SIL_PSV_CTRL1 0x0194
+#define RT5682S_SIL_PSV_CTRL2 0x0195
+#define RT5682S_SIL_PSV_CTRL3 0x0197
+#define RT5682S_SIL_PSV_CTRL4 0x0198
+#define RT5682S_SIL_PSV_CTRL5 0x0199
+#define RT5682S_HP_IMP_SENS_CTRL_1 0x01ac
+#define RT5682S_HP_IMP_SENS_CTRL_2 0x01ad
+#define RT5682S_HP_IMP_SENS_CTRL_3 0x01ae
+#define RT5682S_HP_IMP_SENS_CTRL_4 0x01af
+#define RT5682S_HP_IMP_SENS_CTRL_5 0x01b0
+#define RT5682S_HP_IMP_SENS_CTRL_6 0x01b1
+#define RT5682S_HP_IMP_SENS_CTRL_7 0x01b2
+#define RT5682S_HP_IMP_SENS_CTRL_8 0x01b3
+#define RT5682S_HP_IMP_SENS_CTRL_9 0x01b4
+#define RT5682S_HP_IMP_SENS_CTRL_10 0x01b5
+#define RT5682S_HP_IMP_SENS_CTRL_11 0x01b6
+#define RT5682S_HP_IMP_SENS_CTRL_12 0x01b7
+#define RT5682S_HP_IMP_SENS_CTRL_13 0x01b8
+#define RT5682S_HP_IMP_SENS_CTRL_14 0x01b9
+#define RT5682S_HP_IMP_SENS_CTRL_15 0x01ba
+#define RT5682S_HP_IMP_SENS_CTRL_16 0x01bb
+#define RT5682S_HP_IMP_SENS_CTRL_17 0x01bc
+#define RT5682S_HP_IMP_SENS_CTRL_18 0x01bd
+#define RT5682S_HP_IMP_SENS_CTRL_19 0x01be
+#define RT5682S_HP_IMP_SENS_CTRL_20 0x01bf
+#define RT5682S_HP_IMP_SENS_CTRL_21 0x01c0
+#define RT5682S_HP_IMP_SENS_CTRL_22 0x01c1
+#define RT5682S_HP_IMP_SENS_CTRL_23 0x01c2
+#define RT5682S_HP_IMP_SENS_CTRL_24 0x01c3
+#define RT5682S_HP_IMP_SENS_CTRL_25 0x01c4
+#define RT5682S_HP_IMP_SENS_CTRL_26 0x01c5
+#define RT5682S_HP_IMP_SENS_CTRL_27 0x01c6
+#define RT5682S_HP_IMP_SENS_CTRL_28 0x01c7
+#define RT5682S_HP_IMP_SENS_CTRL_29 0x01c8
+#define RT5682S_HP_IMP_SENS_CTRL_30 0x01c9
+#define RT5682S_HP_IMP_SENS_CTRL_31 0x01ca
+#define RT5682S_HP_IMP_SENS_CTRL_32 0x01cb
+#define RT5682S_HP_IMP_SENS_CTRL_33 0x01cc
+#define RT5682S_HP_IMP_SENS_CTRL_34 0x01cd
+#define RT5682S_HP_IMP_SENS_CTRL_35 0x01ce
+#define RT5682S_HP_IMP_SENS_CTRL_36 0x01cf
+#define RT5682S_HP_IMP_SENS_CTRL_37 0x01d0
+#define RT5682S_HP_IMP_SENS_CTRL_38 0x01d1
+#define RT5682S_HP_IMP_SENS_CTRL_39 0x01d2
+#define RT5682S_HP_IMP_SENS_CTRL_40 0x01d3
+#define RT5682S_HP_IMP_SENS_CTRL_41 0x01d4
+#define RT5682S_HP_IMP_SENS_CTRL_42 0x01d5
+#define RT5682S_HP_IMP_SENS_CTRL_43 0x01d6
+#define RT5682S_HP_IMP_SENS_CTRL_44 0x01d7
+#define RT5682S_HP_IMP_SENS_CTRL_45 0x01d8
+#define RT5682S_HP_IMP_SENS_CTRL_46 0x01d9
+#define RT5682S_HP_LOGIC_CTRL_1 0x01da
+#define RT5682S_HP_LOGIC_CTRL_2 0x01db
+#define RT5682S_HP_LOGIC_CTRL_3 0x01dc
+#define RT5682S_HP_CALIB_CTRL_1 0x01de
+#define RT5682S_HP_CALIB_CTRL_2 0x01df
+#define RT5682S_HP_CALIB_CTRL_3 0x01e0
+#define RT5682S_HP_CALIB_CTRL_4 0x01e1
+#define RT5682S_HP_CALIB_CTRL_5 0x01e2
+#define RT5682S_HP_CALIB_CTRL_6 0x01e3
+#define RT5682S_HP_CALIB_CTRL_7 0x01e4
+#define RT5682S_HP_CALIB_CTRL_8 0x01e5
+#define RT5682S_HP_CALIB_CTRL_9 0x01e6
+#define RT5682S_HP_CALIB_CTRL_10 0x01e7
+#define RT5682S_HP_CALIB_CTRL_11 0x01e8
+#define RT5682S_HP_CALIB_ST_1 0x01ea
+#define RT5682S_HP_CALIB_ST_2 0x01eb
+#define RT5682S_HP_CALIB_ST_3 0x01ec
+#define RT5682S_HP_CALIB_ST_4 0x01ed
+#define RT5682S_HP_CALIB_ST_5 0x01ee
+#define RT5682S_HP_CALIB_ST_6 0x01ef
+#define RT5682S_HP_CALIB_ST_7 0x01f0
+#define RT5682S_HP_CALIB_ST_8 0x01f1
+#define RT5682S_HP_CALIB_ST_9 0x01f2
+#define RT5682S_HP_CALIB_ST_10 0x01f3
+#define RT5682S_HP_CALIB_ST_11 0x01f4
+#define RT5682S_SAR_IL_CMD_1 0x0210
+#define RT5682S_SAR_IL_CMD_2 0x0211
+#define RT5682S_SAR_IL_CMD_3 0x0212
+#define RT5682S_SAR_IL_CMD_4 0x0213
+#define RT5682S_SAR_IL_CMD_5 0x0214
+#define RT5682S_SAR_IL_CMD_6 0x0215
+#define RT5682S_SAR_IL_CMD_7 0x0216
+#define RT5682S_SAR_IL_CMD_8 0x0217
+#define RT5682S_SAR_IL_CMD_9 0x0218
+#define RT5682S_SAR_IL_CMD_10 0x0219
+#define RT5682S_SAR_IL_CMD_11 0x021a
+#define RT5682S_SAR_IL_CMD_12 0x021b
+#define RT5682S_SAR_IL_CMD_13 0x021c
+#define RT5682S_SAR_IL_CMD_14 0x021d
+#define RT5682S_DUMMY_4 0x02fa
+#define RT5682S_DUMMY_5 0x02fb
+#define RT5682S_DUMMY_6 0x02fc
+#define RT5682S_VERSION_ID_HIDE 0x03fe
+#define RT5682S_VERSION_ID_CUS 0x03ff
+#define RT5682S_SCAN_CTL 0x0500
+#define RT5682S_HP_AMP_DET 0x0600
+#define RT5682S_BIAS_CUR_CTRL_11 0x0610
+#define RT5682S_BIAS_CUR_CTRL_12 0x0611
+#define RT5682S_BIAS_CUR_CTRL_13 0x0620
+#define RT5682S_BIAS_CUR_CTRL_14 0x0621
+#define RT5682S_BIAS_CUR_CTRL_15 0x0630
+#define RT5682S_BIAS_CUR_CTRL_16 0x0631
+#define RT5682S_BIAS_CUR_CTRL_17 0x0640
+#define RT5682S_BIAS_CUR_CTRL_18 0x0641
+#define RT5682S_I2C_TRANS_CTRL 0x07fa
+#define RT5682S_DUMMY_7 0x08fa
+#define RT5682S_DUMMY_8 0x08fb
+#define RT5682S_DMIC_FLOAT_DET 0x0d00
+#define RT5682S_HA_CMP_OP_1 0x1100
+#define RT5682S_HA_CMP_OP_2 0x1101
+#define RT5682S_HA_CMP_OP_3 0x1102
+#define RT5682S_HA_CMP_OP_4 0x1103
+#define RT5682S_HA_CMP_OP_5 0x1104
+#define RT5682S_HA_CMP_OP_6 0x1105
+#define RT5682S_HA_CMP_OP_7 0x1106
+#define RT5682S_HA_CMP_OP_8 0x1107
+#define RT5682S_HA_CMP_OP_9 0x1108
+#define RT5682S_HA_CMP_OP_10 0x1109
+#define RT5682S_HA_CMP_OP_11 0x110a
+#define RT5682S_HA_CMP_OP_12 0x110b
+#define RT5682S_HA_CMP_OP_13 0x110c
+#define RT5682S_HA_CMP_OP_14 0x1111
+#define RT5682S_HA_CMP_OP_15 0x1112
+#define RT5682S_HA_CMP_OP_16 0x1113
+#define RT5682S_HA_CMP_OP_17 0x1114
+#define RT5682S_HA_CMP_OP_18 0x1115
+#define RT5682S_HA_CMP_OP_19 0x1116
+#define RT5682S_HA_CMP_OP_20 0x1117
+#define RT5682S_HA_CMP_OP_21 0x1118
+#define RT5682S_HA_CMP_OP_22 0x1119
+#define RT5682S_HA_CMP_OP_23 0x111a
+#define RT5682S_HA_CMP_OP_24 0x111b
+#define RT5682S_HA_CMP_OP_25 0x111c
+#define RT5682S_NEW_CBJ_DET_CTL_1 0x1401
+#define RT5682S_NEW_CBJ_DET_CTL_2 0x1402
+#define RT5682S_NEW_CBJ_DET_CTL_3 0x1403
+#define RT5682S_NEW_CBJ_DET_CTL_4 0x1404
+#define RT5682S_NEW_CBJ_DET_CTL_5 0x1406
+#define RT5682S_NEW_CBJ_DET_CTL_6 0x1407
+#define RT5682S_NEW_CBJ_DET_CTL_7 0x1408
+#define RT5682S_NEW_CBJ_DET_CTL_8 0x1409
+#define RT5682S_NEW_CBJ_DET_CTL_9 0x140a
+#define RT5682S_NEW_CBJ_DET_CTL_10 0x140b
+#define RT5682S_NEW_CBJ_DET_CTL_11 0x140c
+#define RT5682S_NEW_CBJ_DET_CTL_12 0x140d
+#define RT5682S_NEW_CBJ_DET_CTL_13 0x140e
+#define RT5682S_NEW_CBJ_DET_CTL_14 0x140f
+#define RT5682S_NEW_CBJ_DET_CTL_15 0x1410
+#define RT5682S_NEW_CBJ_DET_CTL_16 0x1411
+#define RT5682S_DA_FILTER_1 0x1801
+#define RT5682S_DA_FILTER_2 0x1802
+#define RT5682S_DA_FILTER_3 0x1803
+#define RT5682S_DA_FILTER_4 0x1804
+#define RT5682S_DA_FILTER_5 0x1805
+#define RT5682S_CLK_SW_TEST_1 0x2c00
+#define RT5682S_CLK_SW_TEST_2 0x3400
+#define RT5682S_CLK_SW_TEST_3 0x3404
+#define RT5682S_CLK_SW_TEST_4 0x3405
+#define RT5682S_CLK_SW_TEST_5 0x3406
+#define RT5682S_CLK_SW_TEST_6 0x3407
+#define RT5682S_CLK_SW_TEST_7 0x3408
+#define RT5682S_CLK_SW_TEST_8 0x3409
+#define RT5682S_CLK_SW_TEST_9 0x340a
+#define RT5682S_CLK_SW_TEST_10 0x340b
+#define RT5682S_CLK_SW_TEST_11 0x340c
+#define RT5682S_CLK_SW_TEST_12 0x340d
+#define RT5682S_CLK_SW_TEST_13 0x340e
+#define RT5682S_CLK_SW_TEST_14 0x340f
+#define RT5682S_EFUSE_MANU_WRITE_1 0x3410
+#define RT5682S_EFUSE_MANU_WRITE_2 0x3411
+#define RT5682S_EFUSE_MANU_WRITE_3 0x3412
+#define RT5682S_EFUSE_MANU_WRITE_4 0x3413
+#define RT5682S_EFUSE_MANU_WRITE_5 0x3414
+#define RT5682S_EFUSE_MANU_WRITE_6 0x3415
+#define RT5682S_EFUSE_READ_1 0x3424
+#define RT5682S_EFUSE_READ_2 0x3425
+#define RT5682S_EFUSE_READ_3 0x3426
+#define RT5682S_EFUSE_READ_4 0x3427
+#define RT5682S_EFUSE_READ_5 0x3428
+#define RT5682S_EFUSE_READ_6 0x3429
+#define RT5682S_EFUSE_READ_7 0x342a
+#define RT5682S_EFUSE_READ_8 0x342b
+#define RT5682S_EFUSE_READ_9 0x342c
+#define RT5682S_EFUSE_READ_10 0x342d
+#define RT5682S_EFUSE_READ_11 0x342e
+#define RT5682S_EFUSE_READ_12 0x342f
+#define RT5682S_EFUSE_READ_13 0x3430
+#define RT5682S_EFUSE_READ_14 0x3431
+#define RT5682S_EFUSE_READ_15 0x3432
+#define RT5682S_EFUSE_READ_16 0x3433
+#define RT5682S_EFUSE_READ_17 0x3434
+#define RT5682S_EFUSE_READ_18 0x3435
+#define RT5682S_EFUSE_TIMING_CTL_1 0x3440
+#define RT5682S_EFUSE_TIMING_CTL_2 0x3441
+#define RT5682S_PILOT_DIG_CTL_1 0x3500
+#define RT5682S_PILOT_DIG_CTL_2 0x3501
+#define RT5682S_HP_AMP_DET_CTL_1 0x3b00
+#define RT5682S_HP_AMP_DET_CTL_2 0x3b01
+#define RT5682S_HP_AMP_DET_CTL_3 0x3b02
+#define RT5682S_HP_AMP_DET_CTL_4 0x3b03
+
+#define RT5682S_MAX_REG (RT5682S_HP_AMP_DET_CTL_4)
+
+/* global definition */
+#define RT5682S_L_MUTE (0x1 << 15)
+#define RT5682S_L_MUTE_SFT 15
+#define RT5682S_R_MUTE (0x1 << 7)
+#define RT5682S_R_MUTE_SFT 7
+#define RT5682S_L_VOL_SFT 8
+#define RT5682S_R_VOL_SFT 0
+#define RT5682S_CLK_SRC_MCLK (0x0)
+#define RT5682S_CLK_SRC_PLL1 (0x1)
+#define RT5682S_CLK_SRC_PLL2 (0x2)
+#define RT5682S_CLK_SRC_RCCLK (0x4) /* 25M */
+
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5682S_HPO_L_PATH_MASK (0x1 << 14)
+#define RT5682S_HPO_L_PATH_EN (0x1 << 14)
+#define RT5682S_HPO_L_PATH_DIS (0x0 << 14)
+#define RT5682S_HPO_R_PATH_MASK (0x1 << 13)
+#define RT5682S_HPO_R_PATH_EN (0x1 << 13)
+#define RT5682S_HPO_R_PATH_DIS (0x0 << 13)
+#define RT5682S_HPO_SEL_IP_EN_SW (0x1)
+#define RT5682S_HPO_IP_EN_GATING (0x1)
+#define RT5682S_HPO_IP_NO_GATING (0x0)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5682S_G_HP (0xf << 8)
+#define RT5682S_G_HP_SFT 8
+#define RT5682S_G_STO_DA_DMIX (0xf)
+#define RT5682S_G_STO_DA_SFT 0
+
+/* Embeeded Jack and Type Detection Control 2 (0x0010) */
+#define RT5682S_EMB_JD_MASK (0x1 << 15)
+#define RT5682S_EMB_JD_EN (0x1 << 15)
+#define RT5682S_EMB_JD_EN_SFT 15
+#define RT5682S_EMB_JD_RST (0x1 << 14)
+#define RT5682S_JD_MODE (0x1 << 13)
+#define RT5682S_JD_MODE_SFT 13
+#define RT5682S_DET_TYPE (0x1 << 12)
+#define RT5682S_DET_TYPE_SFT 12
+#define RT5682S_POLA_EXT_JD_MASK (0x1 << 11)
+#define RT5682S_POLA_EXT_JD_LOW (0x1 << 11)
+#define RT5682S_POLA_EXT_JD_HIGH (0x0 << 11)
+#define RT5682S_SEL_FAST_OFF_MASK (0x3 << 9)
+#define RT5682S_SEL_FAST_OFF_SFT 9
+#define RT5682S_POL_FAST_OFF_MASK (0x1 << 8)
+#define RT5682S_POL_FAST_OFF_HIGH (0x1 << 8)
+#define RT5682S_POL_FAST_OFF_LOW (0x0 << 8)
+#define RT5682S_FAST_OFF_MASK (0x1 << 7)
+#define RT5682S_FAST_OFF_EN (0x1 << 7)
+#define RT5682S_FAST_OFF_DIS (0x0 << 7)
+#define RT5682S_VREF_POW_MASK (0x1 << 6)
+#define RT5682S_VREF_POW_FSM (0x0 << 6)
+#define RT5682S_VREF_POW_REG (0x1 << 6)
+#define RT5682S_MB1_PATH_BIT 5
+#define RT5682S_MB1_PATH_MASK (0x1 << 5)
+#define RT5682S_CTRL_MB1_REG (0x1 << 5)
+#define RT5682S_CTRL_MB1_FSM (0x0 << 5)
+#define RT5682S_MB2_PATH_BIT 4
+#define RT5682S_MB2_PATH_MASK (0x1 << 4)
+#define RT5682S_CTRL_MB2_REG (0x1 << 4)
+#define RT5682S_CTRL_MB2_FSM (0x0 << 4)
+#define RT5682S_TRIG_JD_MASK (0x1 << 3)
+#define RT5682S_TRIG_JD_HIGH (0x1 << 3)
+#define RT5682S_TRIG_JD_LOW (0x0 << 3)
+#define RT5682S_MIC_CAP_MASK (0x1 << 1)
+#define RT5682S_MIC_CAP_HS (0x1 << 1)
+#define RT5682S_MIC_CAP_HP (0x0 << 1)
+#define RT5682S_MIC_CAP_SRC_MASK (0x1)
+#define RT5682S_MIC_CAP_SRC_REG (0x1)
+#define RT5682S_MIC_CAP_SRC_ANA (0x0)
+
+/* Embeeded Jack and Type Detection Control 3 (0x0011) */
+#define RT5682S_SEL_CBJ_TYPE_SLOW (0x1 << 15)
+#define RT5682S_SEL_CBJ_TYPE_NORM (0x0 << 15)
+#define RT5682S_SEL_CBJ_TYPE_MASK (0x1 << 15)
+#define RT5682S_POW_BG_MB1_MASK (0x1 << 13)
+#define RT5682S_POW_BG_MB1_REG (0x1 << 13)
+#define RT5682S_POW_BG_MB1_FSM (0x0 << 13)
+#define RT5682S_POW_BG_MB2_MASK (0x1 << 12)
+#define RT5682S_POW_BG_MB2_REG (0x1 << 12)
+#define RT5682S_POW_BG_MB2_FSM (0x0 << 12)
+#define RT5682S_EXT_JD_SRC (0x7 << 4)
+#define RT5682S_EXT_JD_SRC_SFT 4
+#define RT5682S_EXT_JD_SRC_GPIO_JD1 (0x0 << 4)
+#define RT5682S_EXT_JD_SRC_GPIO_JD2 (0x1 << 4)
+#define RT5682S_EXT_JD_SRC_JDH (0x2 << 4)
+#define RT5682S_EXT_JD_SRC_JDL (0x3 << 4)
+#define RT5682S_EXT_JD_SRC_MANUAL (0x4 << 4)
+#define RT5682S_JACK_TYPE_MASK (0x3)
+
+/* Combo Jack and Type Detection Control 4 (0x0012) */
+#define RT5682S_CBJ_IN_BUF_MASK (0x1 << 7)
+#define RT5682S_CBJ_IN_BUF_EN (0x1 << 7)
+#define RT5682S_CBJ_IN_BUF_DIS (0x0 << 7)
+#define RT5682S_CBJ_IN_BUF_BIT 7
+
+/* Combo Jack and Type Detection Control 5 (0x0013) */
+#define RT5682S_SEL_SHT_MID_TON_MASK (0x3 << 12)
+#define RT5682S_SEL_SHT_MID_TON_2 (0x0 << 12)
+#define RT5682S_SEL_SHT_MID_TON_3 (0x1 << 12)
+#define RT5682S_CBJ_JD_TEST_MASK (0x1 << 6)
+#define RT5682S_CBJ_JD_TEST_NORM (0x0 << 6)
+#define RT5682S_CBJ_JD_TEST_MODE (0x1 << 6)
+
+/* Combo Jack and Type Detection Control 6 (0x0014) */
+#define RT5682S_JD_FAST_OFF_SRC_MASK (0x7 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_JDH (0x6 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO6 (0x5 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO5 (0x4 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO4 (0x3 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO3 (0x2 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO2 (0x1 << 8)
+#define RT5682S_JD_FAST_OFF_SRC_GPIO1 (0x0 << 8)
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5682S_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5682S_DAC_L1_VOL_SFT 8
+#define RT5682S_DAC_R1_VOL_MASK (0xff)
+#define RT5682S_DAC_R1_VOL_SFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5682S_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5682S_ADC_L_VOL_SFT 8
+#define RT5682S_ADC_R_VOL_MASK (0x7f)
+#define RT5682S_ADC_R_VOL_SFT 0
+
+/* Stereo1 ADC Boost Gain Control (0x001f) */
+#define RT5682S_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5682S_STO1_ADC_L_BST_SFT 14
+#define RT5682S_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5682S_STO1_ADC_R_BST_SFT 12
+
+/* Sidetone Control (0x0024) */
+#define RT5682S_ST_SRC_SEL (0x1 << 8)
+#define RT5682S_ST_SRC_SFT 8
+#define RT5682S_ST_EN_MASK (0x1 << 6)
+#define RT5682S_ST_DIS (0x0 << 6)
+#define RT5682S_ST_EN (0x1 << 6)
+#define RT5682S_ST_EN_SFT 6
+
+/* Stereo1 ADC Mixer Control (0x0026) */
+#define RT5682S_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5682S_M_STO1_ADC_L1_SFT 15
+#define RT5682S_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5682S_M_STO1_ADC_L2_SFT 14
+#define RT5682S_STO1_ADC1L_SRC_MASK (0x1 << 13)
+#define RT5682S_STO1_ADC1L_SRC_SFT 13
+#define RT5682S_STO1_ADC1_SRC_ADC (0x1 << 13)
+#define RT5682S_STO1_ADC1_SRC_DACMIX (0x0 << 13)
+#define RT5682S_STO1_ADC2L_SRC_MASK (0x1 << 12)
+#define RT5682S_STO1_ADC2L_SRC_SFT 12
+#define RT5682S_STO1_ADCL_SRC_MASK (0x3 << 10)
+#define RT5682S_STO1_ADCL_SRC_SFT 10
+#define RT5682S_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5682S_M_STO1_ADC_R1_SFT 7
+#define RT5682S_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5682S_M_STO1_ADC_R2_SFT 6
+#define RT5682S_STO1_ADC1R_SRC_MASK (0x1 << 5)
+#define RT5682S_STO1_ADC1R_SRC_SFT 5
+#define RT5682S_STO1_ADC2R_SRC_MASK (0x1 << 4)
+#define RT5682S_STO1_ADC2R_SRC_SFT 4
+#define RT5682S_STO1_ADCR_SRC_MASK (0x3 << 2)
+#define RT5682S_STO1_ADCR_SRC_SFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5682S_M_ADCMIX_L (0x1 << 15)
+#define RT5682S_M_ADCMIX_L_SFT 15
+#define RT5682S_M_DAC1_L (0x1 << 14)
+#define RT5682S_M_DAC1_L_SFT 14
+#define RT5682S_M_ADCMIX_R (0x1 << 7)
+#define RT5682S_M_ADCMIX_R_SFT 7
+#define RT5682S_M_DAC1_R (0x1 << 6)
+#define RT5682S_M_DAC1_R_SFT 6
+
+/* Stereo1 DAC Mixer Control (0x002a) */
+#define RT5682S_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5682S_M_DAC_L1_STO_L_SFT 15
+#define RT5682S_G_DAC_L1_STO_L_MASK (0x1 << 14)
+#define RT5682S_G_DAC_L1_STO_L_SFT 14
+#define RT5682S_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5682S_M_DAC_R1_STO_L_SFT 13
+#define RT5682S_G_DAC_R1_STO_L_MASK (0x1 << 12)
+#define RT5682S_G_DAC_R1_STO_L_SFT 12
+#define RT5682S_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5682S_M_DAC_L1_STO_R_SFT 7
+#define RT5682S_G_DAC_L1_STO_R_MASK (0x1 << 6)
+#define RT5682S_G_DAC_L1_STO_R_SFT 6
+#define RT5682S_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5682S_M_DAC_R1_STO_R_SFT 5
+#define RT5682S_G_DAC_R1_STO_R_MASK (0x1 << 4)
+#define RT5682S_G_DAC_R1_STO_R_SFT 4
+
+/* Analog DAC1 Input Source Control (0x002b) */
+#define RT5682S_M_ST_STO_L (0x1 << 9)
+#define RT5682S_M_ST_STO_L_SFT 9
+#define RT5682S_M_ST_STO_R (0x1 << 8)
+#define RT5682S_M_ST_STO_R_SFT 8
+#define RT5682S_DAC_L1_SRC_MASK (0x1 << 4)
+#define RT5682S_A_DACL1_SFT 4
+#define RT5682S_DAC_R1_SRC_MASK (0x1)
+#define RT5682S_A_DACR1_SFT 0
+
+/* Digital Interface Data Control (0x0030) */
+#define RT5682S_IF2_DAC_SEL_MASK (0x3 << 2)
+#define RT5682S_IF2_DAC_SEL_SFT 2
+#define RT5682S_IF2_ADC_SEL_MASK (0x3 << 0)
+#define RT5682S_IF2_ADC_SEL_SFT 0
+
+/* REC Left/Right Mixer Control 2 (0x003c) */
+#define RT5682S_BST_CBJ_MASK (0x3f << 8)
+#define RT5682S_BST_CBJ_SFT 8
+#define RT5682S_M_CBJ_RM1_L (0x1 << 7)
+#define RT5682S_M_CBJ_RM1_L_SFT 7
+#define RT5682S_M_CBJ_RM1_R (0x1 << 6)
+#define RT5682S_M_CBJ_RM1_R_SFT 6
+
+/* REC Left/Right Mixer Calibration Control(0x0044) */
+#define RT5682S_PWR_RM1_R_BIT 8
+#define RT5682S_PWR_RM1_L_BIT 0
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5682S_PWR_I2S1 (0x1 << 15)
+#define RT5682S_PWR_I2S1_BIT 15
+#define RT5682S_PWR_I2S2 (0x1 << 14)
+#define RT5682S_PWR_I2S2_BIT 14
+#define RT5682S_PRE_CHR_DAC_L1 (0x1 << 13)
+#define RT5682S_PRE_CHR_DAC_L1_BIT 13
+#define RT5682S_PRE_CHR_DAC_R1 (0x1 << 12)
+#define RT5682S_PRE_CHR_DAC_R1_BIT 12
+#define RT5682S_PWR_DAC_L1 (0x1 << 11)
+#define RT5682S_PWR_DAC_L1_BIT 11
+#define RT5682S_PWR_DAC_R1 (0x1 << 10)
+#define RT5682S_PWR_DAC_R1_BIT 10
+#define RT5682S_PWR_LDO (0x1 << 8)
+#define RT5682S_PWR_LDO_BIT 8
+#define RT5682S_PWR_D2S_L (0x1 << 7)
+#define RT5682S_PWR_D2S_L_BIT 7
+#define RT5682S_PWR_D2S_R (0x1 << 6)
+#define RT5682S_PWR_D2S_R_BIT 6
+#define RT5682S_PWR_ADC_L1 (0x1 << 4)
+#define RT5682S_PWR_ADC_L1_BIT 4
+#define RT5682S_PWR_ADC_R1 (0x1 << 3)
+#define RT5682S_PWR_ADC_R1_BIT 3
+#define RT5682S_EFUSE_SW_EN (0x1 << 2)
+#define RT5682S_EFUSE_SW_DIS (0x0 << 2)
+#define RT5682S_PWR_EFUSE (0x1 << 1)
+#define RT5682S_PWR_EFUSE_BIT 1
+#define RT5682S_DIG_GATE_CTRL (0x1 << 0)
+#define RT5682S_DIG_GATE_CTRL_SFT 0
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5682S_PWR_ADC_S1F (0x1 << 15)
+#define RT5682S_PWR_ADC_S1F_BIT 15
+#define RT5682S_PWR_DAC_S1F (0x1 << 10)
+#define RT5682S_PWR_DAC_S1F_BIT 10
+#define RT5682S_DLDO_I_LIMIT_MASK (0x1 << 7)
+#define RT5682S_DLDO_I_LIMIT_EN (0x1 << 7)
+#define RT5682S_DLDO_I_LIMIT_DIS (0x0 << 7)
+#define RT5682S_DLDO_I_BIAS_SEL_4 (0x1 << 6)
+#define RT5682S_DLDO_I_BIAS_SEL_0 (0x0 << 6)
+#define RT5682S_DLDO_REG_TEST_1 (0x1 << 5)
+#define RT5682S_DLDO_REG_TEST_0 (0x0 << 5)
+#define RT5682S_DLDO_SRC_REG (0x1 << 4)
+#define RT5682S_DLDO_SRC_EFUSE (0x0 << 4)
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5682S_PWR_VREF1 (0x1 << 15)
+#define RT5682S_PWR_VREF1_BIT 15
+#define RT5682S_PWR_FV1 (0x1 << 14)
+#define RT5682S_PWR_FV1_BIT 14
+#define RT5682S_PWR_VREF2 (0x1 << 13)
+#define RT5682S_PWR_VREF2_BIT 13
+#define RT5682S_PWR_FV2 (0x1 << 12)
+#define RT5682S_PWR_FV2_BIT 12
+#define RT5682S_LDO1_DBG_MASK (0x3 << 10)
+#define RT5682S_PWR_MB (0x1 << 9)
+#define RT5682S_PWR_MB_BIT 9
+#define RT5682S_PWR_BG (0x1 << 7)
+#define RT5682S_PWR_BG_BIT 7
+#define RT5682S_LDO1_BYPASS_MASK (0x1 << 6)
+#define RT5682S_LDO1_BYPASS (0x1 << 6)
+#define RT5682S_LDO1_NOT_BYPASS (0x0 << 6)
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5682S_PWR_MCLK0_WD (0x1 << 15)
+#define RT5682S_PWR_MCLK0_WD_BIT 15
+#define RT5682S_PWR_MCLK1_WD (0x1 << 14)
+#define RT5682S_PWR_MCLK1_WD_BIT 14
+#define RT5682S_RST_MCLK0 (0x1 << 13)
+#define RT5682S_RST_MCLK0_BIT 13
+#define RT5682S_RST_MCLK1 (0x1 << 12)
+#define RT5682S_RST_MCLK1_BIT 12
+#define RT5682S_PWR_MB1 (0x1 << 11)
+#define RT5682S_PWR_MB1_PWR_DOWN (0x0 << 11)
+#define RT5682S_PWR_MB1_BIT 11
+#define RT5682S_PWR_MB2 (0x1 << 10)
+#define RT5682S_PWR_MB2_PWR_DOWN (0x0 << 10)
+#define RT5682S_PWR_MB2_BIT 10
+#define RT5682S_PWR_JD_MASK (0x1 << 0)
+#define RT5682S_PWR_JD_ENABLE (0x1 << 0)
+#define RT5682S_PWR_JD_DISABLE (0x0 << 0)
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5682S_PWR_LDO_PLLA (0x1 << 15)
+#define RT5682S_PWR_LDO_PLLA_BIT 15
+#define RT5682S_PWR_LDO_PLLB (0x1 << 14)
+#define RT5682S_PWR_LDO_PLLB_BIT 14
+#define RT5682S_PWR_BIAS_PLLA (0x1 << 13)
+#define RT5682S_PWR_BIAS_PLLA_BIT 13
+#define RT5682S_PWR_BIAS_PLLB (0x1 << 12)
+#define RT5682S_PWR_BIAS_PLLB_BIT 12
+#define RT5682S_PWR_CBJ (0x1 << 9)
+#define RT5682S_PWR_CBJ_BIT 9
+#define RT5682S_RSTB_PLLB (0x1 << 7)
+#define RT5682S_RSTB_PLLB_BIT 7
+#define RT5682S_RSTB_PLLA (0x1 << 6)
+#define RT5682S_RSTB_PLLA_BIT 6
+#define RT5682S_PWR_PLLB (0x1 << 5)
+#define RT5682S_PWR_PLLB_BIT 5
+#define RT5682S_PWR_PLLA (0x1 << 4)
+#define RT5682S_PWR_PLLA_BIT 4
+#define RT5682S_PWR_LDO_MB2 (0x1 << 2)
+#define RT5682S_PWR_LDO_MB2_BIT 2
+#define RT5682S_PWR_LDO_MB1 (0x1 << 1)
+#define RT5682S_PWR_LDO_MB1_BIT 1
+#define RT5682S_PWR_BGLDO (0x1 << 0)
+#define RT5682S_PWR_BGLDO_BIT 0
+
+/* Power Management for Mixer (0x0066) */
+#define RT5682S_PWR_CLK_COMP_8FS (0x1 << 15)
+#define RT5682S_PWR_CLK_COMP_8FS_BIT 15
+#define RT5682S_DBG_BGLDO_MASK (0x3 << 12)
+#define RT5682S_DBG_BGLDO_SFT 12
+#define RT5682S_DBG_BGLDO_MB1_MASK (0x3 << 10)
+#define RT5682S_DBG_BGLDO_MB1_SFT 10
+#define RT5682S_DBG_BGLDO_MB2_MASK (0x3 << 8)
+#define RT5682S_DBG_BGLDO_MB2_SFT 8
+#define RT5682S_DLDO_BGLDO_MASK (0x3 << 6)
+#define RT5682S_DLDO_BGLDO_MB2_SFT 6
+#define RT5682S_PWR_STO1_DAC_L (0x1 << 5)
+#define RT5682S_PWR_STO1_DAC_L_BIT 5
+#define RT5682S_PWR_STO1_DAC_R (0x1 << 4)
+#define RT5682S_PWR_STO1_DAC_R_BIT 4
+#define RT5682S_DVO_BGLDO_MB1_MASK (0x3 << 2)
+#define RT5682S_DVO_BGLDO_MB1_SFT 2
+#define RT5682S_DVO_BGLDO_MB2_MASK (0x3 << 0)
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5682S_SYS_CLK_DET (0x1 << 15)
+#define RT5682S_SYS_CLK_DET_SFT 15
+#define RT5682S_PLL1_CLK_DET (0x1 << 14)
+#define RT5682S_PLL1_CLK_DET_SFT 14
+
+/* Digital Microphone Control 1 (0x006e) */
+#define RT5682S_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5682S_DMIC_1_EN_SFT 15
+#define RT5682S_DMIC_1_DIS (0x0 << 15)
+#define RT5682S_DMIC_1_EN (0x1 << 15)
+#define RT5682S_FIFO_CLK_DIV_MASK (0x7 << 12)
+#define RT5682S_FIFO_CLK_DIV_2 (0x1 << 12)
+#define RT5682S_DMIC_1_DP_MASK (0x3 << 4)
+#define RT5682S_DMIC_1_DP_SFT 4
+#define RT5682S_DMIC_1_DP_GPIO2 (0x0 << 4)
+#define RT5682S_DMIC_1_DP_GPIO5 (0x1 << 4)
+#define RT5682S_DMIC_CLK_MASK (0xf << 0)
+#define RT5682S_DMIC_CLK_SFT 0
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5682S_SEL_ADCDAT_MASK (0x1 << 15)
+#define RT5682S_SEL_ADCDAT_OUT (0x0 << 15)
+#define RT5682S_SEL_ADCDAT_IN (0x1 << 15)
+#define RT5682S_SEL_ADCDAT_SFT 15
+#define RT5682S_I2S1_TX_CHL_MASK (0x7 << 12)
+#define RT5682S_I2S1_TX_CHL_SFT 12
+#define RT5682S_I2S1_TX_CHL_16 (0x0 << 12)
+#define RT5682S_I2S1_TX_CHL_20 (0x1 << 12)
+#define RT5682S_I2S1_TX_CHL_24 (0x2 << 12)
+#define RT5682S_I2S1_TX_CHL_32 (0x3 << 12)
+#define RT5682S_I2S1_TX_CHL_8 (0x4 << 12)
+#define RT5682S_I2S1_RX_CHL_MASK (0x7 << 8)
+#define RT5682S_I2S1_RX_CHL_SFT 8
+#define RT5682S_I2S1_RX_CHL_16 (0x0 << 8)
+#define RT5682S_I2S1_RX_CHL_20 (0x1 << 8)
+#define RT5682S_I2S1_RX_CHL_24 (0x2 << 8)
+#define RT5682S_I2S1_RX_CHL_32 (0x3 << 8)
+#define RT5682S_I2S1_RX_CHL_8 (0x4 << 8)
+#define RT5682S_I2S1_MONO_MASK (0x1 << 7)
+#define RT5682S_I2S1_MONO_EN (0x1 << 7)
+#define RT5682S_I2S1_MONO_DIS (0x0 << 7)
+#define RT5682S_I2S1_DL_MASK (0x7 << 4)
+#define RT5682S_I2S1_DL_SFT 4
+#define RT5682S_I2S1_DL_16 (0x0 << 4)
+#define RT5682S_I2S1_DL_20 (0x1 << 4)
+#define RT5682S_I2S1_DL_24 (0x2 << 4)
+#define RT5682S_I2S1_DL_32 (0x3 << 4)
+#define RT5682S_I2S1_DL_8 (0x4 << 4)
+
+/* I2S1/2 Audio Serial Data Port Control (0x0071) */
+#define RT5682S_I2S2_MS_MASK (0x1 << 15)
+#define RT5682S_I2S2_MS_SFT 15
+#define RT5682S_I2S2_MS_M (0x0 << 15)
+#define RT5682S_I2S2_MS_S (0x1 << 15)
+#define RT5682S_I2S2_PIN_CFG_MASK (0x1 << 14)
+#define RT5682S_I2S2_PIN_CFG_SFT 14
+#define RT5682S_I2S2_OUT_MASK (0x1 << 9)
+#define RT5682S_I2S2_OUT_SFT 9
+#define RT5682S_I2S2_OUT_UM (0x0 << 9)
+#define RT5682S_I2S2_OUT_M (0x1 << 9)
+#define RT5682S_I2S_BP_MASK (0x1 << 8)
+#define RT5682S_I2S_BP_SFT 8
+#define RT5682S_I2S_BP_NOR (0x0 << 8)
+#define RT5682S_I2S_BP_INV (0x1 << 8)
+#define RT5682S_I2S2_MONO_MASK (0x1 << 7)
+#define RT5682S_I2S2_MONO_EN (0x1 << 7)
+#define RT5682S_I2S2_MONO_DIS (0x0 << 7)
+#define RT5682S_I2S2_DL_MASK (0x7 << 4)
+#define RT5682S_I2S2_DL_SFT 4
+#define RT5682S_I2S2_DL_8 (0x0 << 4)
+#define RT5682S_I2S2_DL_16 (0x1 << 4)
+#define RT5682S_I2S2_DL_20 (0x2 << 4)
+#define RT5682S_I2S2_DL_24 (0x3 << 4)
+#define RT5682S_I2S2_DL_32 (0x4 << 4)
+#define RT5682S_I2S_DF_MASK (0x7)
+#define RT5682S_I2S_DF_SFT 0
+#define RT5682S_I2S_DF_I2S (0x0)
+#define RT5682S_I2S_DF_LEFT (0x1)
+#define RT5682S_I2S_DF_PCM_A (0x2)
+#define RT5682S_I2S_DF_PCM_B (0x3)
+#define RT5682S_I2S_DF_PCM_A_N (0x6)
+#define RT5682S_I2S_DF_PCM_B_N (0x7)
+
+/* ADC/DAC Clock Control 1 (0x0073) */
+#define RT5682S_ADC_OSR_MASK (0xf << 12)
+#define RT5682S_ADC_OSR_SFT 12
+#define RT5682S_ADC_OSR_D_1 (0x0 << 12)
+#define RT5682S_ADC_OSR_D_2 (0x1 << 12)
+#define RT5682S_ADC_OSR_D_4 (0x2 << 12)
+#define RT5682S_ADC_OSR_D_6 (0x3 << 12)
+#define RT5682S_ADC_OSR_D_8 (0x4 << 12)
+#define RT5682S_ADC_OSR_D_12 (0x5 << 12)
+#define RT5682S_ADC_OSR_D_16 (0x6 << 12)
+#define RT5682S_ADC_OSR_D_24 (0x7 << 12)
+#define RT5682S_ADC_OSR_D_32 (0x8 << 12)
+#define RT5682S_ADC_OSR_D_48 (0x9 << 12)
+#define RT5682S_I2S_M_D_MASK (0xf << 8)
+#define RT5682S_I2S_M_D_SFT 8
+#define RT5682S_I2S_M_D_1 (0x0 << 8)
+#define RT5682S_I2S_M_D_2 (0x1 << 8)
+#define RT5682S_I2S_M_D_3 (0x2 << 8)
+#define RT5682S_I2S_M_D_4 (0x3 << 8)
+#define RT5682S_I2S_M_D_6 (0x4 << 8)
+#define RT5682S_I2S_M_D_8 (0x5 << 8)
+#define RT5682S_I2S_M_D_12 (0x6 << 8)
+#define RT5682S_I2S_M_D_16 (0x7 << 8)
+#define RT5682S_I2S_M_D_24 (0x8 << 8)
+#define RT5682S_I2S_M_D_32 (0x9 << 8)
+#define RT5682S_I2S_M_D_48 (0x10 << 8)
+#define RT5682S_I2S_M_CLK_SRC_MASK (0x7 << 4)
+#define RT5682S_I2S_M_CLK_SRC_SFT 4
+#define RT5682S_DAC_OSR_MASK (0xf << 0)
+#define RT5682S_DAC_OSR_SFT 0
+#define RT5682S_DAC_OSR_D_1 (0x0 << 0)
+#define RT5682S_DAC_OSR_D_2 (0x1 << 0)
+#define RT5682S_DAC_OSR_D_4 (0x2 << 0)
+#define RT5682S_DAC_OSR_D_6 (0x3 << 0)
+#define RT5682S_DAC_OSR_D_8 (0x4 << 0)
+#define RT5682S_DAC_OSR_D_12 (0x5 << 0)
+#define RT5682S_DAC_OSR_D_16 (0x6 << 0)
+#define RT5682S_DAC_OSR_D_24 (0x7 << 0)
+#define RT5682S_DAC_OSR_D_32 (0x8 << 0)
+#define RT5682S_DAC_OSR_D_48 (0x9 << 0)
+
+/* ADC/DAC Clock Control 2 (0x0074) */
+#define RT5682S_I2S2_BCLK_MS2_MASK (0x1 << 11)
+#define RT5682S_I2S2_BCLK_MS2_SFT 11
+#define RT5682S_I2S2_BCLK_MS2_32 (0x0 << 11)
+#define RT5682S_I2S2_BCLK_MS2_64 (0x1 << 11)
+
+
+/* TDM control 1 (0x0079) */
+#define RT5682S_TDM_TX_CH_MASK (0x3 << 12)
+#define RT5682S_TDM_TX_CH_2 (0x0 << 12)
+#define RT5682S_TDM_TX_CH_4 (0x1 << 12)
+#define RT5682S_TDM_TX_CH_6 (0x2 << 12)
+#define RT5682S_TDM_TX_CH_8 (0x3 << 12)
+#define RT5682S_TDM_RX_CH_MASK (0x3 << 8)
+#define RT5682S_TDM_RX_CH_2 (0x0 << 8)
+#define RT5682S_TDM_RX_CH_4 (0x1 << 8)
+#define RT5682S_TDM_RX_CH_6 (0x2 << 8)
+#define RT5682S_TDM_RX_CH_8 (0x3 << 8)
+#define RT5682S_TDM_ADC_LCA_MASK (0x7 << 4)
+#define RT5682S_TDM_ADC_LCA_SFT 4
+#define RT5682S_TDM_ADC_DL_MASK (0x3 << 0)
+#define RT5682S_TDM_ADC_DL_SFT 0
+
+/* TDM control 2 (0x007a) */
+#define RT5682S_IF1_ADC1_SEL_SFT 14
+#define RT5682S_IF1_ADC2_SEL_SFT 12
+#define RT5682S_IF1_ADC3_SEL_SFT 10
+#define RT5682S_IF1_ADC4_SEL_SFT 8
+#define RT5682S_TDM_ADC_SEL_SFT 3
+
+/* TDM control 3 (0x007b) */
+#define RT5682S_TDM_EN (0x1 << 7)
+
+/* TDM/I2S control (0x007e) */
+#define RT5682S_TDM_S_BP_MASK (0x1 << 15)
+#define RT5682S_TDM_S_BP_SFT 15
+#define RT5682S_TDM_S_BP_NOR (0x0 << 15)
+#define RT5682S_TDM_S_BP_INV (0x1 << 15)
+#define RT5682S_TDM_S_LP_MASK (0x1 << 14)
+#define RT5682S_TDM_S_LP_SFT 14
+#define RT5682S_TDM_S_LP_NOR (0x0 << 14)
+#define RT5682S_TDM_S_LP_INV (0x1 << 14)
+#define RT5682S_TDM_DF_MASK (0x7 << 11)
+#define RT5682S_TDM_DF_SFT 11
+#define RT5682S_TDM_DF_I2S (0x0 << 11)
+#define RT5682S_TDM_DF_LEFT (0x1 << 11)
+#define RT5682S_TDM_DF_PCM_A (0x2 << 11)
+#define RT5682S_TDM_DF_PCM_B (0x3 << 11)
+#define RT5682S_TDM_DF_PCM_A_N (0x6 << 11)
+#define RT5682S_TDM_DF_PCM_B_N (0x7 << 11)
+#define RT5682S_TDM_BCLK_MS1_MASK (0x3 << 8)
+#define RT5682S_TDM_BCLK_MS1_SFT 8
+#define RT5682S_TDM_BCLK_MS1_32 (0x0 << 8)
+#define RT5682S_TDM_BCLK_MS1_64 (0x1 << 8)
+#define RT5682S_TDM_BCLK_MS1_128 (0x2 << 8)
+#define RT5682S_TDM_BCLK_MS1_256 (0x3 << 8)
+#define RT5682S_TDM_BCLK_MS1_16 (0x4 << 8)
+#define RT5682S_TDM_CL_MASK (0x3 << 4)
+#define RT5682S_TDM_CL_16 (0x0 << 4)
+#define RT5682S_TDM_CL_20 (0x1 << 4)
+#define RT5682S_TDM_CL_24 (0x2 << 4)
+#define RT5682S_TDM_CL_32 (0x3 << 4)
+#define RT5682S_TDM_M_BP_MASK (0x1 << 2)
+#define RT5682S_TDM_M_BP_SFT 2
+#define RT5682S_TDM_M_BP_NOR (0x0 << 2)
+#define RT5682S_TDM_M_BP_INV (0x1 << 2)
+#define RT5682S_TDM_M_LP_MASK (0x1 << 1)
+#define RT5682S_TDM_M_LP_SFT 1
+#define RT5682S_TDM_M_LP_NOR (0x0 << 1)
+#define RT5682S_TDM_M_LP_INV (0x1 << 1)
+#define RT5682S_TDM_MS_MASK (0x1 << 0)
+#define RT5682S_TDM_MS_SFT 0
+#define RT5682S_TDM_MS_S (0x0 << 0)
+#define RT5682S_TDM_MS_M (0x1 << 0)
+
+/* Global Clock Control (0x0080) */
+#define RT5682S_SCLK_SRC_MASK (0x7 << 13)
+#define RT5682S_SCLK_SRC_SFT 13
+#define RT5682S_PLL_SRC_MASK (0x3 << 8)
+#define RT5682S_PLL_SRC_SFT 8
+#define RT5682S_PLL_SRC_MCLK (0x0 << 8)
+#define RT5682S_PLL_SRC_BCLK1 (0x1 << 8)
+#define RT5682S_PLL_SRC_RC (0x3 << 8)
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5682S_DA_ASRC_MASK (0x1 << 13)
+#define RT5682S_DA_ASRC_SFT 13
+#define RT5682S_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5682S_DAC_STO1_ASRC_SFT 12
+#define RT5682S_AD_ASRC_MASK (0x1 << 8)
+#define RT5682S_AD_ASRC_SFT 8
+#define RT5682S_AD_ASRC_SEL_MASK (0x1 << 4)
+#define RT5682S_AD_ASRC_SEL_SFT 4
+#define RT5682S_DMIC_ASRC_MASK (0x1 << 3)
+#define RT5682S_DMIC_ASRC_SFT 3
+#define RT5682S_ADC_STO1_ASRC_MASK (0x1 << 2)
+#define RT5682S_ADC_STO1_ASRC_SFT 2
+#define RT5682S_DA_ASRC_SEL_MASK (0x1 << 0)
+#define RT5682S_DA_ASRC_SEL_SFT 0
+
+/* PLL tracking mode 2 3 (0x0084)(0x0085)*/
+#define RT5682S_FILTER_CLK_SEL_MASK (0x7 << 12)
+#define RT5682S_FILTER_CLK_SEL_SFT 12
+#define RT5682S_FILTER_CLK_DIV_MASK (0xf << 8)
+#define RT5682S_FILTER_CLK_DIV_SFT 8
+
+/* ASRC Control 4 (0x0086) */
+#define RT5682S_ASRCIN_FTK_N1_MASK (0x3 << 14)
+#define RT5682S_ASRCIN_FTK_N1_SFT 14
+#define RT5682S_ASRCIN_FTK_N2_MASK (0x3 << 12)
+#define RT5682S_ASRCIN_FTK_N2_SFT 12
+#define RT5682S_ASRCIN_FTK_M1_MASK (0x7 << 8)
+#define RT5682S_ASRCIN_FTK_M1_SFT 8
+#define RT5682S_ASRCIN_FTK_M2_MASK (0x7 << 4)
+#define RT5682S_ASRCIN_FTK_M2_SFT 4
+
+/* ASRC Control 11 (0x008c) */
+#define RT5682S_ASRCIN_AUTO_CLKOUT_MASK (0x1 << 5)
+#define RT5682S_ASRCIN_AUTO_CLKOUT_EN (0x1 << 5)
+#define RT5682S_ASRCIN_AUTO_CLKOUT_DIS (0x0 << 5)
+#define RT5682S_ASRCIN_AUTO_RST_MASK (0x1 << 4)
+#define RT5682S_ASRCIN_AUTO_RST_EN (0x1 << 4)
+#define RT5682S_ASRCIN_AUTO_RST_DIS (0x0 << 4)
+#define RT5682S_SEL_LRCK_DET_MASK (0x3)
+#define RT5682S_SEL_LRCK_DET_DIV8 (0x3)
+#define RT5682S_SEL_LRCK_DET_DIV4 (0x2)
+#define RT5682S_SEL_LRCK_DET_DIV2 (0x1)
+#define RT5682S_SEL_LRCK_DET_DIV1 (0x0)
+
+/* Depop Mode Control 1 (0x008e) */
+#define RT5682S_OUT_HP_L_EN (0x1 << 6)
+#define RT5682S_OUT_HP_R_EN (0x1 << 5)
+#define RT5682S_LDO_PUMP_EN (0x1 << 4)
+#define RT5682S_LDO_PUMP_EN_SFT 4
+#define RT5682S_PUMP_EN (0x1 << 3)
+#define RT5682S_PUMP_EN_SFT 3
+#define RT5682S_CAPLESS_L_EN (0x1 << 1)
+#define RT5682S_CAPLESS_L_EN_SFT 1
+#define RT5682S_CAPLESS_R_EN (0x1 << 0)
+#define RT5682S_CAPLESS_R_EN_SFT 0
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5682S_RAMP_MASK (0x1 << 12)
+#define RT5682S_RAMP_SFT 12
+#define RT5682S_RAMP_DIS (0x0 << 12)
+#define RT5682S_RAMP_EN (0x1 << 12)
+#define RT5682S_BPS_MASK (0x1 << 11)
+#define RT5682S_BPS_SFT 11
+#define RT5682S_BPS_DIS (0x0 << 11)
+#define RT5682S_BPS_EN (0x1 << 11)
+#define RT5682S_FAST_UPDN_MASK (0x1 << 10)
+#define RT5682S_FAST_UPDN_SFT 10
+#define RT5682S_FAST_UPDN_DIS (0x0 << 10)
+#define RT5682S_FAST_UPDN_EN (0x1 << 10)
+#define RT5682S_VLO_MASK (0x1 << 7)
+#define RT5682S_VLO_SFT 7
+#define RT5682S_VLO_3V (0x0 << 7)
+#define RT5682S_VLO_33V (0x1 << 7)
+
+/* HPOUT charge pump 1 (0x0091) */
+#define RT5682S_OSW_L_MASK (0x1 << 11)
+#define RT5682S_OSW_L_SFT 11
+#define RT5682S_OSW_L_DIS (0x0 << 11)
+#define RT5682S_OSW_L_EN (0x1 << 11)
+#define RT5682S_OSW_R_MASK (0x1 << 10)
+#define RT5682S_OSW_R_SFT 10
+#define RT5682S_OSW_R_DIS (0x0 << 10)
+#define RT5682S_OSW_R_EN (0x1 << 10)
+#define RT5682S_PM_HP_MASK (0x3 << 8)
+#define RT5682S_PM_HP_SFT 8
+#define RT5682S_PM_HP_LV (0x0 << 8)
+#define RT5682S_PM_HP_MV (0x1 << 8)
+#define RT5682S_PM_HP_HV (0x2 << 8)
+
+/* Micbias Control1 (0x93) */
+#define RT5682S_MIC1_OV_MASK (0x3 << 14)
+#define RT5682S_MIC1_OV_SFT 14
+#define RT5682S_MIC1_OV_2V7 (0x0 << 14)
+#define RT5682S_MIC1_OV_2V4 (0x1 << 14)
+#define RT5682S_MIC1_OV_2V25 (0x3 << 14)
+#define RT5682S_MIC1_OV_1V8 (0x4 << 14)
+#define RT5682S_MIC2_OV_MASK (0x3 << 8)
+#define RT5682S_MIC2_OV_SFT 8
+#define RT5682S_MIC2_OV_2V7 (0x0 << 8)
+#define RT5682S_MIC2_OV_2V4 (0x1 << 8)
+#define RT5682S_MIC2_OV_2V25 (0x3 << 8)
+#define RT5682S_MIC2_OV_1V8 (0x4 << 8)
+
+/* Micbias Control2 (0x0094) */
+#define RT5682S_PWR_CLK25M_MASK (0x1 << 9)
+#define RT5682S_PWR_CLK25M_SFT 9
+#define RT5682S_PWR_CLK25M_PD (0x0 << 9)
+#define RT5682S_PWR_CLK25M_PU (0x1 << 9)
+#define RT5682S_PWR_CLK1M_MASK (0x1 << 8)
+#define RT5682S_PWR_CLK1M_SFT 8
+#define RT5682S_PWR_CLK1M_PD (0x0 << 8)
+#define RT5682S_PWR_CLK1M_PU (0x1 << 8)
+
+/* PLL M/N/K Code Control 1 (0x0098) */
+#define RT5682S_PLLA_N_MASK (0x1ff << 0)
+
+/* PLL M/N/K Code Control 2 (0x0099) */
+#define RT5682S_PLLA_M_MASK (0x1f << 8)
+#define RT5682S_PLLA_M_SFT 8
+#define RT5682S_PLLA_K_MASK (0x1f << 0)
+
+/* PLL M/N/K Code Control 3 (0x009a) */
+#define RT5682S_PLLB_N_MASK (0x3ff << 0)
+
+/* PLL M/N/K Code Control 4 (0x009b) */
+#define RT5682S_PLLB_M_MASK (0x1f << 8)
+#define RT5682S_PLLB_M_SFT 8
+#define RT5682S_PLLB_K_MASK (0x1f << 0)
+
+/* PLL M/N/K Code Control 6 (0x009d) */
+#define RT5682S_PLLB_SEL_PS_MASK (0x1 << 13)
+#define RT5682S_PLLB_SEL_PS_SFT 13
+#define RT5682S_PLLB_BYP_PS_MASK (0x1 << 12)
+#define RT5682S_PLLB_BYP_PS_SFT 12
+#define RT5682S_PLLB_M_BP_MASK (0x1 << 11)
+#define RT5682S_PLLB_M_BP_SFT 11
+#define RT5682S_PLLB_K_BP_MASK (0x1 << 10)
+#define RT5682S_PLLB_K_BP_SFT 10
+#define RT5682S_PLLA_M_BP_MASK (0x1 << 7)
+#define RT5682S_PLLA_M_BP_SFT 7
+#define RT5682S_PLLA_K_BP_MASK (0x1 << 6)
+#define RT5682S_PLLA_K_BP_SFT 6
+
+/* PLL M/N/K Code Control 7 (0x009e) */
+#define RT5682S_PLLB_SRC_MASK (0x1)
+#define RT5682S_PLLB_SRC_DFIN (0x1)
+#define RT5682S_PLLB_SRC_PLLA (0x0)
+
+/* RC Clock Control (0x009f) */
+#define RT5682S_POW_IRQ (0x1 << 15)
+#define RT5682S_POW_JDH (0x1 << 14)
+
+/* I2S2 Master Mode Clock Control 1 (0x00a0) */
+#define RT5682S_I2S2_M_CLK_SRC_MASK (0x7 << 4)
+#define RT5682S_I2S2_M_CLK_SRC_SFT 4
+#define RT5682S_I2S2_M_D_MASK (0xf << 0)
+#define RT5682S_I2S2_M_D_1 (0x0)
+#define RT5682S_I2S2_M_D_2 (0x1)
+#define RT5682S_I2S2_M_D_3 (0x2)
+#define RT5682S_I2S2_M_D_4 (0x3)
+#define RT5682S_I2S2_M_D_6 (0x4)
+#define RT5682S_I2S2_M_D_8 (0x5)
+#define RT5682S_I2S2_M_D_12 (0x6)
+#define RT5682S_I2S2_M_D_16 (0x7)
+#define RT5682S_I2S2_M_D_24 (0x8)
+#define RT5682S_I2S2_M_D_32 (0x9)
+#define RT5682S_I2S2_M_D_48 (0xa)
+#define RT5682S_I2S2_M_D_SFT 0
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5682S_JD1_PULSE_EN_MASK (0x1 << 10)
+#define RT5682S_JD1_PULSE_EN_SFT 10
+#define RT5682S_JD1_PULSE_DIS (0x0 << 10)
+#define RT5682S_JD1_PULSE_EN (0x1 << 10)
+
+/* IRQ Control 2 (0x00b7) */
+#define RT5682S_JD1_EN_MASK (0x1 << 15)
+#define RT5682S_JD1_EN_SFT 15
+#define RT5682S_JD1_DIS (0x0 << 15)
+#define RT5682S_JD1_EN (0x1 << 15)
+#define RT5682S_JD1_POL_MASK (0x1 << 13)
+#define RT5682S_JD1_POL_NOR (0x0 << 13)
+#define RT5682S_JD1_POL_INV (0x1 << 13)
+#define RT5682S_JD1_IRQ_MASK (0x1 << 10)
+#define RT5682S_JD1_IRQ_LEV (0x0 << 10)
+#define RT5682S_JD1_IRQ_PUL (0x1 << 10)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5682S_IL_IRQ_MASK (0x1 << 7)
+#define RT5682S_IL_IRQ_DIS (0x0 << 7)
+#define RT5682S_IL_IRQ_EN (0x1 << 7)
+#define RT5682S_IL_IRQ_TYPE_MASK (0x1 << 4)
+#define RT5682S_IL_IRQ_LEV (0x0 << 4)
+#define RT5682S_IL_IRQ_PUL (0x1 << 4)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5682S_GP1_PIN_MASK (0x3 << 14)
+#define RT5682S_GP1_PIN_SFT 14
+#define RT5682S_GP1_PIN_GPIO1 (0x0 << 14)
+#define RT5682S_GP1_PIN_IRQ (0x1 << 14)
+#define RT5682S_GP1_PIN_DMIC_CLK (0x2 << 14)
+#define RT5682S_GP2_PIN_MASK (0x3 << 12)
+#define RT5682S_GP2_PIN_SFT 12
+#define RT5682S_GP2_PIN_GPIO2 (0x0 << 12)
+#define RT5682S_GP2_PIN_LRCK2 (0x1 << 12)
+#define RT5682S_GP2_PIN_DMIC_SDA (0x2 << 12)
+#define RT5682S_GP3_PIN_MASK (0x3 << 10)
+#define RT5682S_GP3_PIN_SFT 10
+#define RT5682S_GP3_PIN_GPIO3 (0x0 << 10)
+#define RT5682S_GP3_PIN_BCLK2 (0x1 << 10)
+#define RT5682S_GP3_PIN_DMIC_CLK (0x2 << 10)
+#define RT5682S_GP4_PIN_MASK (0x3 << 8)
+#define RT5682S_GP4_PIN_SFT 8
+#define RT5682S_GP4_PIN_GPIO4 (0x0 << 8)
+#define RT5682S_GP4_PIN_ADCDAT1 (0x1 << 8)
+#define RT5682S_GP4_PIN_DMIC_CLK (0x2 << 8)
+#define RT5682S_GP4_PIN_ADCDAT2 (0x3 << 8)
+#define RT5682S_GP5_PIN_MASK (0x3 << 6)
+#define RT5682S_GP5_PIN_SFT 6
+#define RT5682S_GP5_PIN_GPIO5 (0x0 << 6)
+#define RT5682S_GP5_PIN_DACDAT1 (0x1 << 6)
+#define RT5682S_GP5_PIN_DMIC_SDA (0x2 << 6)
+#define RT5682S_GP6_PIN_MASK (0x1 << 5)
+#define RT5682S_GP6_PIN_SFT 5
+#define RT5682S_GP6_PIN_GPIO6 (0x0 << 5)
+#define RT5682S_GP6_PIN_LRCK1 (0x1 << 5)
+
+/* GPIO Control 2 (0x00c1)*/
+#define RT5682S_GP1_PF_MASK (0x1 << 15)
+#define RT5682S_GP1_PF_IN (0x0 << 15)
+#define RT5682S_GP1_PF_OUT (0x1 << 15)
+#define RT5682S_GP1_OUT_MASK (0x1 << 14)
+#define RT5682S_GP1_OUT_L (0x0 << 14)
+#define RT5682S_GP1_OUT_H (0x1 << 14)
+#define RT5682S_GP2_PF_MASK (0x1 << 13)
+#define RT5682S_GP2_PF_IN (0x0 << 13)
+#define RT5682S_GP2_PF_OUT (0x1 << 13)
+#define RT5682S_GP2_OUT_MASK (0x1 << 12)
+#define RT5682S_GP2_OUT_L (0x0 << 12)
+#define RT5682S_GP2_OUT_H (0x1 << 12)
+#define RT5682S_GP3_PF_MASK (0x1 << 11)
+#define RT5682S_GP3_PF_IN (0x0 << 11)
+#define RT5682S_GP3_PF_OUT (0x1 << 11)
+#define RT5682S_GP3_OUT_MASK (0x1 << 10)
+#define RT5682S_GP3_OUT_L (0x0 << 10)
+#define RT5682S_GP3_OUT_H (0x1 << 10)
+#define RT5682S_GP4_PF_MASK (0x1 << 9)
+#define RT5682S_GP4_PF_IN (0x0 << 9)
+#define RT5682S_GP4_PF_OUT (0x1 << 9)
+#define RT5682S_GP4_OUT_MASK (0x1 << 8)
+#define RT5682S_GP4_OUT_L (0x0 << 8)
+#define RT5682S_GP4_OUT_H (0x1 << 8)
+#define RT5682S_GP5_PF_MASK (0x1 << 7)
+#define RT5682S_GP5_PF_IN (0x0 << 7)
+#define RT5682S_GP5_PF_OUT (0x1 << 7)
+#define RT5682S_GP5_OUT_MASK (0x1 << 6)
+#define RT5682S_GP5_OUT_L (0x0 << 6)
+#define RT5682S_GP5_OUT_H (0x1 << 6)
+#define RT5682S_GP6_PF_MASK (0x1 << 5)
+#define RT5682S_GP6_PF_IN (0x0 << 5)
+#define RT5682S_GP6_PF_OUT (0x1 << 5)
+#define RT5682S_GP6_OUT_MASK (0x1 << 4)
+#define RT5682S_GP6_OUT_L (0x0 << 4)
+#define RT5682S_GP6_OUT_H (0x1 << 4)
+
+/* GPIO Status (0x00c2) */
+#define RT5682S_GP6_ST (0x1 << 6)
+#define RT5682S_GP5_ST (0x1 << 5)
+#define RT5682S_GP4_ST (0x1 << 4)
+#define RT5682S_GP3_ST (0x1 << 3)
+#define RT5682S_GP2_ST (0x1 << 2)
+#define RT5682S_GP1_ST (0x1 << 1)
+
+/* Soft volume and zero cross control 1 (0x00d9) */
+#define RT5682S_ZCD_MASK (0x1 << 10)
+#define RT5682S_ZCD_SFT 10
+#define RT5682S_ZCD_PD (0x0 << 10)
+#define RT5682S_ZCD_PU (0x1 << 10)
+
+/* 4 Button Inline Command Control 2 (0x00e3) */
+#define RT5682S_4BTN_IL_MASK (0x1 << 15)
+#define RT5682S_4BTN_IL_EN (0x1 << 15)
+#define RT5682S_4BTN_IL_DIS (0x0 << 15)
+#define RT5682S_4BTN_IL_RST_MASK (0x1 << 14)
+#define RT5682S_4BTN_IL_NOR (0x1 << 14)
+#define RT5682S_4BTN_IL_RST (0x0 << 14)
+
+/* 4 Button Inline Command Control 3~6 (0x00e5~0x00e8) */
+#define RT5682S_4BTN_IL_HOLD_WIN_MASK (0x7f << 8)
+#define RT5682S_4BTN_IL_HOLD_WIN_SFT 8
+#define RT5682S_4BTN_IL_CLICK_WIN_MASK (0x7f)
+#define RT5682S_4BTN_IL_CLICK_WIN_SFT 0
+
+/* Analog JD Control (0x00f0) */
+#define RT5682S_JDH_RS_MASK (0x1 << 4)
+#define RT5682S_JDH_NO_PLUG (0x1 << 4)
+#define RT5682S_JDH_PLUG (0x0 << 4)
+
+/* Bias current control 7 (0x0110) */
+#define RT5682S_LDO_DACREF_MASK (0x3 << 4)
+#define RT5682S_LDO_DACREF_1_607V (0x0 << 4)
+#define RT5682S_LDO_DACREF_1_5V (0x1 << 4)
+#define RT5682S_LDO_DACREF_1_406V (0x2 << 4)
+#define RT5682S_LDO_DACREF_1_731V (0x3 << 4)
+
+/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682S_CP_CLK_HP_MASK (0x3 << 4)
+#define RT5682S_CP_CLK_HP_100KHZ (0x0 << 4)
+#define RT5682S_CP_CLK_HP_200KHZ (0x1 << 4)
+#define RT5682S_CP_CLK_HP_300KHZ (0x2 << 4)
+#define RT5682S_CP_CLK_HP_600KHZ (0x3 << 4)
+
+/* Pad Driving Control (0x0136) */
+#define RT5682S_PAD_DRV_GP1_MASK (0x1 << 14)
+#define RT5682S_PAD_DRV_GP1_HIGH (0x1 << 14)
+#define RT5682S_PAD_DRV_GP1_LOW (0x0 << 14)
+#define RT5682S_PAD_DRV_GP2_MASK (0x1 << 12)
+#define RT5682S_PAD_DRV_GP2_HIGH (0x1 << 12)
+#define RT5682S_PAD_DRV_GP2_LOW (0x0 << 12)
+#define RT5682S_PAD_DRV_GP3_MASK (0x1 << 10)
+#define RT5682S_PAD_DRV_GP3_HIGH (0x1 << 10)
+#define RT5682S_PAD_DRV_GP3_LOW (0x0 << 10)
+#define RT5682S_PAD_DRV_GP4_MASK (0x1 << 8)
+#define RT5682S_PAD_DRV_GP4_HIGH (0x1 << 8)
+#define RT5682S_PAD_DRV_GP4_LOW (0x0 << 8)
+#define RT5682S_PAD_DRV_GP5_MASK (0x1 << 6)
+#define RT5682S_PAD_DRV_GP5_HIGH (0x1 << 6)
+#define RT5682S_PAD_DRV_GP5_LOW (0x0 << 6)
+#define RT5682S_PAD_DRV_GP6_MASK (0x1 << 4)
+#define RT5682S_PAD_DRV_GP6_HIGH (0x1 << 4)
+#define RT5682S_PAD_DRV_GP6_LOW (0x0 << 4)
+
+/* Chopper and Clock control for DAC (0x013a)*/
+#define RT5682S_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5682S_CKXEN_DAC1_SFT 13
+#define RT5682S_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5682S_CKGEN_DAC1_SFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5682S_CKXEN_ADC1_MASK (0x1 << 13)
+#define RT5682S_CKXEN_ADC1_SFT 13
+#define RT5682S_CKGEN_ADC1_MASK (0x1 << 12)
+#define RT5682S_CKGEN_ADC1_SFT 12
+
+/* Volume test (0x013f)*/
+#define RT5682S_SEL_CLK_VOL_MASK (0x1 << 15)
+#define RT5682S_SEL_CLK_VOL_EN (0x1 << 15)
+#define RT5682S_SEL_CLK_VOL_DIS (0x0 << 15)
+
+/* Test Mode Control 1 (0x0145) */
+#define RT5682S_AD2DA_LB_MASK (0x1 << 10)
+#define RT5682S_AD2DA_LB_SFT 10
+
+/* Stereo Noise Gate Control 1 (0x0160) */
+#define RT5682S_NG2_EN_MASK (0x1 << 15)
+#define RT5682S_NG2_EN (0x1 << 15)
+#define RT5682S_NG2_DIS (0x0 << 15)
+
+/* Stereo1 DAC Silence Detection Control (0x0190) */
+#define RT5682S_DEB_STO_DAC_MASK (0x7 << 4)
+#define RT5682S_DEB_80_MS (0x0 << 4)
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5682S_HP_SIG_SRC_MASK (0x3)
+#define RT5682S_HP_SIG_SRC_1BIT_CTL (0x3)
+#define RT5682S_HP_SIG_SRC_REG (0x2)
+#define RT5682S_HP_SIG_SRC_IMPE_REG (0x1)
+#define RT5682S_HP_SIG_SRC_DC_CALI (0x0)
+
+/* SAR ADC Inline Command Control 1 (0x0210) */
+#define RT5682S_SAR_BUTDET_MASK (0x1 << 15)
+#define RT5682S_SAR_BUTDET_EN (0x1 << 15)
+#define RT5682S_SAR_BUTDET_DIS (0x0 << 15)
+#define RT5682S_SAR_BUTDET_POW_MASK (0x1 << 14)
+#define RT5682S_SAR_BUTDET_POW_SAV (0x1 << 14)
+#define RT5682S_SAR_BUTDET_POW_NORM (0x0 << 14)
+#define RT5682S_SAR_BUTDET_RST_MASK (0x1 << 13)
+#define RT5682S_SAR_BUTDET_RST_NORM (0x1 << 13)
+#define RT5682S_SAR_BUTDET_RST (0x0 << 13)
+#define RT5682S_SAR_POW_MASK (0x1 << 12)
+#define RT5682S_SAR_POW_EN (0x1 << 12)
+#define RT5682S_SAR_POW_DIS (0x0 << 12)
+#define RT5682S_SAR_RST_MASK (0x1 << 11)
+#define RT5682S_SAR_RST_NORMAL (0x1 << 11)
+#define RT5682S_SAR_RST (0x0 << 11)
+#define RT5682S_SAR_BYPASS_MASK (0x1 << 10)
+#define RT5682S_SAR_BYPASS_EN (0x1 << 10)
+#define RT5682S_SAR_BYPASS_DIS (0x0 << 10)
+#define RT5682S_SAR_SEL_MB1_2_MASK (0x3 << 8)
+#define RT5682S_SAR_SEL_MB1_2_SFT 8
+#define RT5682S_SAR_SEL_MODE_MASK (0x1 << 7)
+#define RT5682S_SAR_SEL_MODE_CMP (0x1 << 7)
+#define RT5682S_SAR_SEL_MODE_ADC (0x0 << 7)
+#define RT5682S_SAR_SEL_MB1_2_CTL_MASK (0x1 << 5)
+#define RT5682S_SAR_SEL_MB1_2_AUTO (0x1 << 5)
+#define RT5682S_SAR_SEL_MB1_2_MANU (0x0 << 5)
+#define RT5682S_SAR_SEL_SIGNAL_MASK (0x1 << 4)
+#define RT5682S_SAR_SEL_SIGNAL_AUTO (0x1 << 4)
+#define RT5682S_SAR_SEL_SIGNAL_MANU (0x0 << 4)
+
+/* SAR ADC Inline Command Control 2 (0x0211) */
+#define RT5682S_SAR_ADC_PSV_MASK (0x1 << 4)
+#define RT5682S_SAR_ADC_PSV_ENTRY (0x1 << 4)
+
+
+/* SAR ADC Inline Command Control 13 (0x021c) */
+#define RT5682S_SAR_SOUR_MASK (0x3f)
+#define RT5682S_SAR_SOUR_BTN (0x3f)
+#define RT5682S_SAR_SOUR_TYPE (0x0)
+
+/* Headphone Amp Detection Control 1 (0x3b00) */
+#define RT5682S_CP_SW_SIZE_MASK (0x7 << 4)
+#define RT5682S_CP_SW_SIZE_L (0x4 << 4)
+#define RT5682S_CP_SW_SIZE_M (0x2 << 4)
+#define RT5682S_CP_SW_SIZE_S (0x1 << 4)
+
+#define RT5682S_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5682S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+/* System Clock Source */
+enum {
+ RT5682S_SCLK_S_MCLK,
+ RT5682S_SCLK_S_PLL1,
+ RT5682S_SCLK_S_PLL2,
+ RT5682S_SCLK_S_RCCLK,
+};
+
+/* PLL Source */
+enum {
+ RT5682S_PLL_S_MCLK,
+ RT5682S_PLL_S_BCLK1,
+ RT5682S_PLL_S_BCLK2,
+ RT5682S_PLL_S_RCCLK,
+};
+
+enum {
+ RT5682S_PLL1,
+ RT5682S_PLL2,
+ RT5682S_PLLS,
+};
+
+enum {
+ RT5682S_AIF1,
+ RT5682S_AIF2,
+ RT5682S_AIFS
+};
+
+/* filter mask */
+enum {
+ RT5682S_DA_STEREO1_FILTER = 0x1,
+ RT5682S_AD_STEREO1_FILTER = (0x1 << 1),
+};
+
+enum {
+ RT5682S_CLK_SEL_SYS,
+ RT5682S_CLK_SEL_I2S1_ASRC,
+ RT5682S_CLK_SEL_I2S2_ASRC,
+};
+
+enum {
+ USE_PLLA,
+ USE_PLLB,
+ USE_PLLAB,
+};
+
+struct pll_calc_map {
+ unsigned int freq_in;
+ unsigned int freq_out;
+ int m;
+ int n;
+ int k;
+ bool m_bp;
+ bool k_bp;
+ bool byp_ps;
+ bool sel_ps;
+};
+
+enum {
+ RT5682S_SUPPLY_AVDD,
+ RT5682S_SUPPLY_MICVDD,
+ RT5682S_SUPPLY_DBVDD,
+ RT5682S_SUPPLY_LDO1_IN,
+ RT5682S_NUM_SUPPLIES,
+};
+
+struct rt5682s_priv {
+ struct snd_soc_component *component;
+ struct rt5682s_platform_data pdata;
+ struct gpio_desc *ldo1_en;
+ struct regmap *regmap;
+ struct snd_soc_jack *hs_jack;
+ struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES];
+ struct delayed_work jack_detect_work;
+ struct delayed_work jd_check_work;
+ struct mutex calibrate_mutex;
+ struct mutex sar_mutex;
+ struct mutex wclk_mutex;
+
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw dai_clks_hw[RT5682S_DAI_NUM_CLKS];
+ struct clk *mclk;
+#endif
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5682S_AIFS];
+ int bclk[RT5682S_AIFS];
+ int master[RT5682S_AIFS];
+
+ int pll_src[RT5682S_PLLS];
+ int pll_in[RT5682S_PLLS];
+ int pll_out[RT5682S_PLLS];
+ int pll_comb;
+
+ int jack_type;
+ unsigned int irq;
+ int irq_work_delay_time;
+ int wclk_enabled;
+};
+
+int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component,
+ unsigned int filter_mask, unsigned int clk_src);
+
+#endif /* __RT5682S_H__ */
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
new file mode 100644
index 000000000000..44543c0da177
--- /dev/null
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt700-sdw.c -- rt700 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt700.h"
+#include "rt700-sdw.h"
+
+static bool rt700_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x2000 ... 0x200e:
+ case 0x2012 ... 0x2016:
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2231:
+ case 0x3000 ... 0x3fff:
+ case 0x7000 ... 0x7fff:
+ case 0x8300 ... 0x83ff:
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0x75201a:
+ case 0x752045:
+ case 0x752046:
+ case 0x752048:
+ case 0x75204a:
+ case 0x75206b:
+ case 0x752080:
+ case 0x752081:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt700_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2009:
+ case 0x2016:
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x200b ... 0x200e: /* i2c read */
+ case 0x2012 ... 0x2015: /* HD-A read */
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2201 ... 0x2212: /* i2c debug */
+ case 0x2220 ... 0x2223: /* decoded HD-A */
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0xff01:
+ case 0x75201a:
+ case 0x752046:
+ case 0x752080:
+ case 0x752081:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
+ unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
+ unsigned int is_hda_reg = 1, is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt700->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT700_PRIV_DATA_R_H | nid;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg3, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg4, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x3000) {
+ reg += 0x8000;
+ ret = regmap_write(rt700->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ reg += 0x2000;
+ reg |= 0x800;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg2, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x9000) {
+ ret = regmap_write(rt700->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0xb000) {
+ ret = regmap_write(rt700->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_read(rt700->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ is_hda_reg = 0;
+ }
+
+ if (is_hda_reg || is_index_reg) {
+ sdw_data_3 = 0;
+ sdw_data_2 = 0;
+ sdw_data_1 = 0;
+ sdw_data_0 = 0;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_3, &sdw_data_3);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_2, &sdw_data_2);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_1, &sdw_data_1);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt700->sdw_regmap,
+ RT700_READ_HDA_0, &sdw_data_0);
+ if (ret < 0)
+ return ret;
+ *val = ((sdw_data_3 & 0xff) << 24) |
+ ((sdw_data_2 & 0xff) << 16) |
+ ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
+ }
+
+ if (is_hda_reg == 0)
+ dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n",
+ __func__, reg, reg2, reg3, reg4, *val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x => %08x\n",
+ __func__, reg, reg2, *val);
+
+ return 0;
+}
+
+static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
+ unsigned int is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt700->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT700_PRIV_DATA_W_H | nid;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg3, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg4, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ is_index_reg = 1;
+ } else if (reg < 0x4fff) {
+ ret = regmap_write(rt700->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (reg == 0xff01) {
+ ret = regmap_write(rt700->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ ret = regmap_write(rt700->sdw_regmap,
+ reg, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt700->sdw_regmap, reg2, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt700->sdw_regmap,
+ reg2, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (reg2 == 0)
+ dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
+ __func__, reg, reg2, reg3, reg4, val2, val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
+ __func__, reg, reg2, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt700_regmap = {
+ .reg_bits = 24,
+ .val_bits = 32,
+ .readable_reg = rt700_readable_register,
+ .volatile_reg = rt700_volatile_register,
+ .max_register = 0x755800,
+ .reg_defaults = rt700_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt700_sdw_read,
+ .reg_write = rt700_sdw_write,
+};
+
+static const struct regmap_config rt700_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt700_readable_register,
+ .max_register = 0xff01,
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt700_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt700->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt700->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt700_io_init(&slave->dev, slave);
+}
+
+static int rt700_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x14; /* BITMAP: 00010100 */
+ prop->sink_ports = 0xA; /* BITMAP: 00001010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt700_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt700->params, params, sizeof(*params));
+
+ ret = rt700_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "Invalid clk config");
+
+ return ret;
+}
+
+static int rt700_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ mutex_lock(&rt700->disable_irq_lock);
+ if (status->control_port & 0x4 && !rt700->disable_irq) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_detect_work, msecs_to_jiffies(250));
+ }
+ mutex_unlock(&rt700->disable_irq_lock);
+
+ return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops rt700_slave_ops = {
+ .read_prop = rt700_read_prop,
+ .interrupt_callback = rt700_interrupt_callback,
+ .update_status = rt700_update_status,
+ .bus_config = rt700_bus_config,
+};
+
+static int rt700_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *sdw_regmap, *regmap;
+
+ /* Regmap Initialization */
+ sdw_regmap = devm_regmap_init_sdw(slave, &rt700_sdw_regmap);
+ if (IS_ERR(sdw_regmap))
+ return PTR_ERR(sdw_regmap);
+
+ regmap = devm_regmap_init(&slave->dev, NULL,
+ &slave->dev, &rt700_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt700_init(&slave->dev, sdw_regmap, regmap, slave);
+}
+
+static int rt700_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
+
+ if (rt700->hw_init) {
+ cancel_delayed_work_sync(&rt700->jack_detect_work);
+ cancel_delayed_work_sync(&rt700->jack_btn_check_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt700_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x700, 0x1, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt700_id);
+
+static int rt700_dev_suspend(struct device *dev)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+
+ if (!rt700->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt700->jack_detect_work);
+ cancel_delayed_work_sync(&rt700->jack_btn_check_work);
+
+ regcache_cache_only(rt700->regmap, true);
+
+ return 0;
+}
+
+static int rt700_dev_system_suspend(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!rt700->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt700->disable_irq_lock);
+ rt700->disable_irq = true;
+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
+ SDW_SCP_INT1_IMPL_DEF, 0);
+ mutex_unlock(&rt700->disable_irq_lock);
+
+ if (ret < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
+ }
+
+ return rt700_dev_suspend(dev);
+}
+
+#define RT700_PROBE_TIMEOUT 5000
+
+static int rt700_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt700->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT700_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt700->regmap, false);
+ regcache_sync_region(rt700->regmap, 0x3000, 0x8fff);
+ regcache_sync_region(rt700->regmap, 0x752010, 0x75206b);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt700_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume)
+ RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL)
+};
+
+static struct sdw_driver rt700_sdw_driver = {
+ .driver = {
+ .name = "rt700",
+ .pm = pm_ptr(&rt700_pm),
+ },
+ .probe = rt700_sdw_probe,
+ .remove = rt700_sdw_remove,
+ .ops = &rt700_slave_ops,
+ .id_table = rt700_id,
+};
+module_sdw_driver(rt700_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT700 driver SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h
new file mode 100644
index 000000000000..4ad0dcfd16fd
--- /dev/null
+++ b/sound/soc/codecs/rt700-sdw.h
@@ -0,0 +1,335 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt700-sdw.h -- RT700 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT700_SDW_H__
+#define __RT700_SDW_H__
+
+static const struct reg_default rt700_reg_defaults[] = {
+ { 0x0000, 0x0000 },
+ { 0x0001, 0x0000 },
+ { 0x0002, 0x0000 },
+ { 0x0003, 0x0000 },
+ { 0x0004, 0x0000 },
+ { 0x0005, 0x0001 },
+ { 0x0020, 0x0000 },
+ { 0x0022, 0x0000 },
+ { 0x0023, 0x0000 },
+ { 0x0024, 0x0000 },
+ { 0x0025, 0x0000 },
+ { 0x0026, 0x0000 },
+ { 0x0030, 0x0000 },
+ { 0x0032, 0x0000 },
+ { 0x0033, 0x0000 },
+ { 0x0034, 0x0000 },
+ { 0x0035, 0x0000 },
+ { 0x0036, 0x0000 },
+ { 0x0040, 0x0000 },
+ { 0x0041, 0x0000 },
+ { 0x0042, 0x0000 },
+ { 0x0043, 0x0000 },
+ { 0x0044, 0x0020 },
+ { 0x0045, 0x0001 },
+ { 0x0046, 0x0000 },
+ { 0x0050, 0x0000 },
+ { 0x0051, 0x0000 },
+ { 0x0052, 0x0000 },
+ { 0x0053, 0x0000 },
+ { 0x0054, 0x0000 },
+ { 0x0055, 0x0000 },
+ { 0x0060, 0x0000 },
+ { 0x0070, 0x0000 },
+ { 0x00e0, 0x0000 },
+ { 0x00f0, 0x0000 },
+ { 0x0100, 0x0000 },
+ { 0x0101, 0x0000 },
+ { 0x0102, 0x0000 },
+ { 0x0103, 0x0000 },
+ { 0x0104, 0x0000 },
+ { 0x0105, 0x0000 },
+ { 0x0120, 0x0000 },
+ { 0x0121, 0x0000 },
+ { 0x0122, 0x0000 },
+ { 0x0123, 0x0000 },
+ { 0x0124, 0x0000 },
+ { 0x0125, 0x0000 },
+ { 0x0126, 0x0000 },
+ { 0x0127, 0x0000 },
+ { 0x0130, 0x0000 },
+ { 0x0131, 0x0000 },
+ { 0x0132, 0x0000 },
+ { 0x0133, 0x0000 },
+ { 0x0134, 0x0000 },
+ { 0x0135, 0x0000 },
+ { 0x0136, 0x0000 },
+ { 0x0137, 0x0000 },
+ { 0x0200, 0x0000 },
+ { 0x0201, 0x0000 },
+ { 0x0202, 0x0000 },
+ { 0x0203, 0x0000 },
+ { 0x0204, 0x0000 },
+ { 0x0205, 0x0000 },
+ { 0x0220, 0x0000 },
+ { 0x0221, 0x0000 },
+ { 0x0222, 0x0000 },
+ { 0x0223, 0x0000 },
+ { 0x0224, 0x0000 },
+ { 0x0225, 0x0000 },
+ { 0x0226, 0x0000 },
+ { 0x0227, 0x0000 },
+ { 0x0230, 0x0000 },
+ { 0x0231, 0x0000 },
+ { 0x0232, 0x0000 },
+ { 0x0233, 0x0000 },
+ { 0x0234, 0x0000 },
+ { 0x0235, 0x0000 },
+ { 0x0236, 0x0000 },
+ { 0x0237, 0x0000 },
+ { 0x0300, 0x0000 },
+ { 0x0301, 0x0000 },
+ { 0x0302, 0x0000 },
+ { 0x0303, 0x0000 },
+ { 0x0304, 0x0000 },
+ { 0x0305, 0x0000 },
+ { 0x0320, 0x0000 },
+ { 0x0321, 0x0000 },
+ { 0x0322, 0x0000 },
+ { 0x0323, 0x0000 },
+ { 0x0324, 0x0000 },
+ { 0x0325, 0x0000 },
+ { 0x0326, 0x0000 },
+ { 0x0327, 0x0000 },
+ { 0x0330, 0x0000 },
+ { 0x0331, 0x0000 },
+ { 0x0332, 0x0000 },
+ { 0x0333, 0x0000 },
+ { 0x0334, 0x0000 },
+ { 0x0335, 0x0000 },
+ { 0x0336, 0x0000 },
+ { 0x0337, 0x0000 },
+ { 0x0400, 0x0000 },
+ { 0x0401, 0x0000 },
+ { 0x0402, 0x0000 },
+ { 0x0403, 0x0000 },
+ { 0x0404, 0x0000 },
+ { 0x0405, 0x0000 },
+ { 0x0420, 0x0000 },
+ { 0x0421, 0x0000 },
+ { 0x0422, 0x0000 },
+ { 0x0423, 0x0000 },
+ { 0x0424, 0x0000 },
+ { 0x0425, 0x0000 },
+ { 0x0426, 0x0000 },
+ { 0x0427, 0x0000 },
+ { 0x0430, 0x0000 },
+ { 0x0431, 0x0000 },
+ { 0x0432, 0x0000 },
+ { 0x0433, 0x0000 },
+ { 0x0434, 0x0000 },
+ { 0x0435, 0x0000 },
+ { 0x0436, 0x0000 },
+ { 0x0437, 0x0000 },
+ { 0x0500, 0x0000 },
+ { 0x0501, 0x0000 },
+ { 0x0502, 0x0000 },
+ { 0x0503, 0x0000 },
+ { 0x0504, 0x0000 },
+ { 0x0505, 0x0000 },
+ { 0x0520, 0x0000 },
+ { 0x0521, 0x0000 },
+ { 0x0522, 0x0000 },
+ { 0x0523, 0x0000 },
+ { 0x0524, 0x0000 },
+ { 0x0525, 0x0000 },
+ { 0x0526, 0x0000 },
+ { 0x0527, 0x0000 },
+ { 0x0530, 0x0000 },
+ { 0x0531, 0x0000 },
+ { 0x0532, 0x0000 },
+ { 0x0533, 0x0000 },
+ { 0x0534, 0x0000 },
+ { 0x0535, 0x0000 },
+ { 0x0536, 0x0000 },
+ { 0x0537, 0x0000 },
+ { 0x0600, 0x0000 },
+ { 0x0601, 0x0000 },
+ { 0x0602, 0x0000 },
+ { 0x0603, 0x0000 },
+ { 0x0604, 0x0000 },
+ { 0x0605, 0x0000 },
+ { 0x0620, 0x0000 },
+ { 0x0621, 0x0000 },
+ { 0x0622, 0x0000 },
+ { 0x0623, 0x0000 },
+ { 0x0624, 0x0000 },
+ { 0x0625, 0x0000 },
+ { 0x0626, 0x0000 },
+ { 0x0627, 0x0000 },
+ { 0x0630, 0x0000 },
+ { 0x0631, 0x0000 },
+ { 0x0632, 0x0000 },
+ { 0x0633, 0x0000 },
+ { 0x0634, 0x0000 },
+ { 0x0635, 0x0000 },
+ { 0x0636, 0x0000 },
+ { 0x0637, 0x0000 },
+ { 0x0700, 0x0000 },
+ { 0x0701, 0x0000 },
+ { 0x0702, 0x0000 },
+ { 0x0703, 0x0000 },
+ { 0x0704, 0x0000 },
+ { 0x0705, 0x0000 },
+ { 0x0720, 0x0000 },
+ { 0x0721, 0x0000 },
+ { 0x0722, 0x0000 },
+ { 0x0723, 0x0000 },
+ { 0x0724, 0x0000 },
+ { 0x0725, 0x0000 },
+ { 0x0726, 0x0000 },
+ { 0x0727, 0x0000 },
+ { 0x0730, 0x0000 },
+ { 0x0731, 0x0000 },
+ { 0x0732, 0x0000 },
+ { 0x0733, 0x0000 },
+ { 0x0734, 0x0000 },
+ { 0x0735, 0x0000 },
+ { 0x0736, 0x0000 },
+ { 0x0737, 0x0000 },
+ { 0x0800, 0x0000 },
+ { 0x0801, 0x0000 },
+ { 0x0802, 0x0000 },
+ { 0x0803, 0x0000 },
+ { 0x0804, 0x0000 },
+ { 0x0805, 0x0000 },
+ { 0x0820, 0x0000 },
+ { 0x0821, 0x0000 },
+ { 0x0822, 0x0000 },
+ { 0x0823, 0x0000 },
+ { 0x0824, 0x0000 },
+ { 0x0825, 0x0000 },
+ { 0x0826, 0x0000 },
+ { 0x0827, 0x0000 },
+ { 0x0830, 0x0000 },
+ { 0x0831, 0x0000 },
+ { 0x0832, 0x0000 },
+ { 0x0833, 0x0000 },
+ { 0x0834, 0x0000 },
+ { 0x0835, 0x0000 },
+ { 0x0836, 0x0000 },
+ { 0x0837, 0x0000 },
+ { 0x0f00, 0x0000 },
+ { 0x0f01, 0x0000 },
+ { 0x0f02, 0x0000 },
+ { 0x0f03, 0x0000 },
+ { 0x0f04, 0x0000 },
+ { 0x0f05, 0x0000 },
+ { 0x0f20, 0x0000 },
+ { 0x0f21, 0x0000 },
+ { 0x0f22, 0x0000 },
+ { 0x0f23, 0x0000 },
+ { 0x0f24, 0x0000 },
+ { 0x0f25, 0x0000 },
+ { 0x0f26, 0x0000 },
+ { 0x0f27, 0x0000 },
+ { 0x0f30, 0x0000 },
+ { 0x0f31, 0x0000 },
+ { 0x0f32, 0x0000 },
+ { 0x0f33, 0x0000 },
+ { 0x0f34, 0x0000 },
+ { 0x0f35, 0x0000 },
+ { 0x0f36, 0x0000 },
+ { 0x0f37, 0x0000 },
+ { 0x2000, 0x0000 },
+ { 0x2001, 0x0000 },
+ { 0x2002, 0x0000 },
+ { 0x2003, 0x0000 },
+ { 0x2004, 0x0000 },
+ { 0x2005, 0x0000 },
+ { 0x2006, 0x0000 },
+ { 0x2007, 0x0000 },
+ { 0x2008, 0x0000 },
+ { 0x2009, 0x0003 },
+ { 0x200a, 0x0003 },
+ { 0x200b, 0x0000 },
+ { 0x200c, 0x0000 },
+ { 0x200d, 0x0000 },
+ { 0x200e, 0x0000 },
+ { 0x2012, 0x0000 },
+ { 0x2013, 0x0000 },
+ { 0x2014, 0x0000 },
+ { 0x2015, 0x0000 },
+ { 0x2016, 0x0000 },
+ { 0x201a, 0x0000 },
+ { 0x201b, 0x0000 },
+ { 0x201c, 0x0000 },
+ { 0x201d, 0x0000 },
+ { 0x201e, 0x0000 },
+ { 0x201f, 0x0000 },
+ { 0x2020, 0x0000 },
+ { 0x2021, 0x0000 },
+ { 0x2022, 0x0000 },
+ { 0x2023, 0x0000 },
+ { 0x2024, 0x0000 },
+ { 0x2025, 0x0002 },
+ { 0x2026, 0x0000 },
+ { 0x2027, 0x0000 },
+ { 0x2029, 0x0000 },
+ { 0x202a, 0x0000 },
+ { 0x202d, 0x0000 },
+ { 0x202e, 0x0000 },
+ { 0x202f, 0x0000 },
+ { 0x2030, 0x0000 },
+ { 0x2031, 0x0000 },
+ { 0x2032, 0x0000 },
+ { 0x2033, 0x0000 },
+ { 0x2034, 0x0000 },
+ { 0x2200, 0x0000 },
+ { 0x2201, 0x0000 },
+ { 0x2202, 0x0000 },
+ { 0x2203, 0x0000 },
+ { 0x2204, 0x0000 },
+ { 0x2206, 0x0000 },
+ { 0x2207, 0x0000 },
+ { 0x2208, 0x0000 },
+ { 0x2209, 0x0000 },
+ { 0x220a, 0x0000 },
+ { 0x220b, 0x0000 },
+ { 0x220c, 0x0000 },
+ { 0x220d, 0x0000 },
+ { 0x220e, 0x0000 },
+ { 0x220f, 0x0000 },
+ { 0x2211, 0x0000 },
+ { 0x2212, 0x0000 },
+ { 0x2220, 0x0000 },
+ { 0x2221, 0x0000 },
+ { 0x2222, 0x0000 },
+ { 0x2223, 0x0000 },
+ { 0x2230, 0x0000 },
+ { 0x2231, 0x0000 },
+ { 0x3121, 0x0001 },
+ { 0x3122, 0x0000 },
+ { 0x3123, 0x0000 },
+ { 0x7303, 0x0057 },
+ { 0x7303, 0x0057 },
+ { 0x8383, 0x0057 },
+ { 0x7308, 0x0097 },
+ { 0x8388, 0x0097 },
+ { 0x7309, 0x0097 },
+ { 0x8389, 0x0097 },
+ { 0x7312, 0x0000 },
+ { 0x8392, 0x0000 },
+ { 0x7313, 0x0000 },
+ { 0x8393, 0x0000 },
+ { 0x7319, 0x0000 },
+ { 0x8399, 0x0000 },
+ { 0x75201a, 0x8003 },
+ { 0x752045, 0x5289 },
+ { 0x752048, 0xd049 },
+ { 0x75204a, 0xa83b },
+ { 0x75206b, 0x5064 },
+};
+
+#endif /* __RT700_H__ */
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
new file mode 100644
index 000000000000..21523d0a5f08
--- /dev/null
+++ b/sound/soc/codecs/rt700.c
@@ -0,0 +1,1235 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt700.c -- rt700 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/hda_verbs.h>
+#include <sound/jack.h>
+
+#include "rt700.h"
+
+static int rt700_index_write(struct regmap *regmap,
+ unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt700_index_read(struct regmap *regmap,
+ unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static unsigned int rt700_button_detect(struct rt700_priv *rt700)
+{
+ unsigned int btn_type = 0, val80, val81;
+ int ret;
+
+ ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE1, &val80);
+ if (ret < 0)
+ goto read_error;
+ ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE2, &val81);
+ if (ret < 0)
+ goto read_error;
+
+ val80 &= 0x0381;
+ val81 &= 0xff00;
+
+ switch (val80) {
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x0001:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+ switch (val81) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0200:
+ case 0x0100:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+read_error:
+ return btn_type;
+}
+
+static int rt700_headset_detect(struct rt700_priv *rt700)
+{
+ unsigned int buf, loop = 0;
+ int ret;
+ unsigned int jack_status = 0, reg;
+
+ ret = rt700_index_read(rt700->regmap,
+ RT700_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ while (loop < 500 &&
+ (buf & RT700_COMBOJACK_AUTO_DET_STATUS) == 0) {
+ loop++;
+
+ usleep_range(9000, 10000);
+ ret = rt700_index_read(rt700->regmap,
+ RT700_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
+ ret = regmap_read(rt700->regmap, reg, &jack_status);
+ if ((jack_status & (1 << 31)) == 0)
+ goto remove_error;
+ }
+
+ if (loop >= 500)
+ goto to_error;
+
+ if (buf & RT700_COMBOJACK_AUTO_DET_TRS)
+ rt700->jack_type = SND_JACK_HEADPHONE;
+ else if ((buf & RT700_COMBOJACK_AUTO_DET_CTIA) ||
+ (buf & RT700_COMBOJACK_AUTO_DET_OMTP))
+ rt700->jack_type = SND_JACK_HEADSET;
+
+ return 0;
+
+to_error:
+ ret = -ETIMEDOUT;
+ pr_err_ratelimited("Time-out error in %s\n", __func__);
+ return ret;
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+remove_error:
+ pr_err_ratelimited("Jack removal in %s\n", __func__);
+ return -ENODEV;
+}
+
+static void rt700_jack_detect_handler(struct work_struct *work)
+{
+ struct rt700_priv *rt700 =
+ container_of(work, struct rt700_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ if (!rt700->hs_jack)
+ return;
+
+ if (!snd_soc_card_is_instantiated(rt700->component->card))
+ return;
+
+ reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
+ ret = regmap_read(rt700->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ /* jack in */
+ if (rt700->jack_type == 0) {
+ ret = rt700_headset_detect(rt700);
+ if (ret < 0)
+ return;
+ if (rt700->jack_type == SND_JACK_HEADSET)
+ btn_type = rt700_button_detect(rt700);
+ } else if (rt700->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt700_button_detect(rt700);
+ }
+ } else {
+ /* jack out */
+ rt700->jack_type = 0;
+ }
+
+ dev_dbg(&rt700->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt700->jack_type);
+ dev_dbg(&rt700->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt700_btn_check_handler(struct work_struct *work)
+{
+ struct rt700_priv *rt700 = container_of(work, struct rt700_priv,
+ jack_btn_check_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
+ ret = regmap_read(rt700->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ if (rt700->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt700_button_detect(rt700);
+ }
+ } else {
+ rt700->jack_type = 0;
+ }
+
+ /* cbj comparator */
+ ret = rt700_index_read(rt700->regmap, RT700_COMBO_JACK_AUTO_CTL2, &reg);
+ if (ret < 0)
+ goto io_error;
+
+ if ((reg & 0xf0) == 0xf0)
+ btn_type = 0;
+
+ dev_dbg(&rt700->slave->dev,
+ "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt700->hs_jack, rt700->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt700_jack_init(struct rt700_priv *rt700)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt700->component);
+
+ /* power on */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ if (rt700->hs_jack) {
+ /* Enable Jack Detection */
+ regmap_write(rt700->regmap,
+ RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82);
+ regmap_write(rt700->regmap,
+ RT700_SET_HP_UNSOLICITED_ENABLE, 0x81);
+ regmap_write(rt700->regmap,
+ RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x83);
+ rt700_index_write(rt700->regmap, 0x10, 0x2420);
+ rt700_index_write(rt700->regmap, 0x19, 0x2e11);
+
+ dev_dbg(&rt700->slave->dev, "in %s enable\n", __func__);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt700->jack_detect_work, msecs_to_jiffies(250));
+ } else {
+ regmap_write(rt700->regmap,
+ RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt700->regmap,
+ RT700_SET_HP_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt700->regmap,
+ RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x00);
+
+ dev_dbg(&rt700->slave->dev, "in %s disable\n", __func__);
+ }
+
+ /* power off */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+}
+
+static int rt700_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt700->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!rt700->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt700_jack_init(rt700);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h,
+ unsigned int addr_l, unsigned int val_h,
+ unsigned int *r_val, unsigned int *l_val)
+{
+ /* R Channel */
+ *r_val = (val_h << 8);
+ regmap_read(rt700->regmap, addr_l, r_val);
+
+ /* L Channel */
+ val_h |= 0x20;
+ *l_val = (val_h << 8);
+ regmap_read(rt700->regmap, addr_h, l_val);
+}
+
+/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
+static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
+ unsigned int read_ll, read_rl;
+ int i;
+
+ /* Can't use update bit function, so read the original value first */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT700_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ /* L Channel */
+ if (mc->invert) {
+ /* for mute */
+ val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7;
+ /* keep gain */
+ read_ll = read_ll & 0x7f;
+ val_ll |= read_ll;
+ } else {
+ /* for gain */
+ val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
+ if (val_ll > mc->max)
+ val_ll = mc->max;
+ /* keep mute status */
+ read_ll = read_ll & 0x80;
+ val_ll |= read_ll;
+ }
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* R Channel */
+ if (mc->invert) {
+ /* for mute */
+ val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7;
+ /* keep gain */
+ read_rl = read_rl & 0x7f;
+ val_lr |= read_rl;
+ } else {
+ /* for gain */
+ val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
+ if (val_lr > mc->max)
+ val_lr = mc->max;
+ /* keep mute status */
+ read_rl = read_rl & 0x80;
+ val_lr |= read_rl;
+ }
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << mc->shift) | (3 << 4);
+ regmap_write(rt700->regmap,
+ addr_h, (val_h << 8 | val_ll));
+ regmap_write(rt700->regmap,
+ addr_l, (val_h << 8 | val_ll));
+ } else {
+ /* Lch*/
+ val_h = (1 << mc->shift) | (1 << 5);
+ regmap_write(rt700->regmap,
+ addr_h, (val_h << 8 | val_ll));
+
+ /* Rch */
+ val_h = (1 << mc->shift) | (1 << 4);
+ regmap_write(rt700->regmap,
+ addr_l, (val_h << 8 | val_lr));
+ }
+ /* check result */
+ if (mc->shift == RT700_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt700_get_gain(rt700, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return 0;
+}
+
+static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int addr_h, addr_l, val_h;
+ unsigned int read_ll, read_rl;
+
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT700_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (mc->invert) {
+ /* for mute status */
+ read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT);
+ read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT);
+ } else {
+ /* for gain */
+ read_ll = read_ll & 0x7f;
+ read_rl = read_rl & 0x7f;
+ }
+ ucontrol->value.integer.value[0] = read_ll;
+ ucontrol->value.integer.value[1] = read_rl;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt700_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume",
+ RT700_SET_GAIN_DAC1_H, RT700_SET_GAIN_DAC1_L,
+ RT700_DIR_OUT_SFT, 0x57, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, out_vol_tlv),
+ SOC_DOUBLE_R_EXT("ADC 08 Capture Switch",
+ RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L,
+ RT700_DIR_IN_SFT, 1, 1,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT("ADC 09 Capture Switch",
+ RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L,
+ RT700_DIR_IN_SFT, 1, 1,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume",
+ RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L,
+ RT700_DIR_IN_SFT, 0x3f, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume",
+ RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L,
+ RT700_DIR_IN_SFT, 0x3f, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("AMIC Volume",
+ RT700_SET_GAIN_AMIC_H, RT700_SET_GAIN_AMIC_L,
+ RT700_DIR_IN_SFT, 3, 0,
+ rt700_set_amp_gain_get, rt700_set_amp_gain_put, mic_vol_tlv),
+};
+
+static int rt700_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, val = 0, nid;
+ int ret;
+
+ if (strstr(ucontrol->id.name, "HPO Mux"))
+ nid = RT700_HP_OUT;
+ else if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT700_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT700_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* vid = 0xf01 */
+ reg = RT700_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt700->regmap, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt700_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, reg, nid;
+ int ret;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "HPO Mux"))
+ nid = RT700_HP_OUT;
+ else if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT700_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT700_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* Verb ID = 0x701h */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ reg = RT700_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt700->regmap, reg, &val2);
+ if (ret < 0)
+ return ret;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ reg = RT700_VERB_SET_CONNECT_SEL | nid;
+ regmap_write(rt700->regmap, reg, val);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt700_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt700_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt700_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum,
+ rt700_mux_get, rt700_mux_put);
+
+static const struct snd_kcontrol_new rt700_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum,
+ rt700_mux_get, rt700_mux_put);
+
+static const char * const out_mux_text[] = {
+ "Front",
+ "Surround",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt700_hp_enum, SND_SOC_NOPM, 0, out_mux_text);
+
+static const struct snd_kcontrol_new rt700_hp_mux =
+ SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum,
+ rt700_mux_get, rt700_mux_put);
+
+static int rt700_dac_front_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC1, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC1, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_dac_surround_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC2, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_DAC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_adc_09_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC1, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC1, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_adc_08_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC2, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt700->regmap,
+ RT700_SET_STREAMID_ADC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_hpo_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4);
+ unsigned int val_l;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val_l = 0x00;
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val_l = (1 << RT700_MUTE_SFT);
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ usleep_range(50000, 55000);
+ break;
+ }
+ return 0;
+}
+
+static int rt700_spk_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4);
+ unsigned int val_l;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val_l = 0x00;
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val_l = (1 << RT700_MUTE_SFT);
+ regmap_write(rt700->regmap,
+ RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l));
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_DAC_E("DAC Front", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_dac_front_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_dac_surround_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux,
+ rt700_hpo_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt700_spk_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_adc_09_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0,
+ rt700_adc_08_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt700_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt700_adc23_mux),
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt700_audio_map[] = {
+ {"DAC Front", NULL, "DP1RX"},
+ {"DAC Surround", NULL, "DP3RX"},
+ {"DP2TX", NULL, "ADC 09"},
+ {"DP4TX", NULL, "ADC 08"},
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 22 Mux", "DMIC", "DMIC1"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "DMIC", "DMIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+ {"HPO Mux", "Front", "DAC Front"},
+ {"HPO Mux", "Surround", "DAC Surround"},
+ {"HP", NULL, "HPO Mux"},
+ {"SPK PGA", NULL, "DAC Front"},
+ {"SPK", NULL, "SPK PGA"},
+};
+
+static int rt700_probe(struct snd_soc_component *component)
+{
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt700->component = component;
+
+ if (!rt700->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static int rt700_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(rt700->regmap,
+ RT700_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt700 = {
+ .probe = rt700_probe,
+ .set_bias_level = rt700_set_bias_level,
+ .controls = rt700_snd_controls,
+ .num_controls = ARRAY_SIZE(rt700_snd_controls),
+ .dapm_widgets = rt700_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets),
+ .dapm_routes = rt700_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt700_audio_map),
+ .set_jack = rt700_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt700_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int val = 0;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt700->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ /* This code assumes port 1 for playback and port 2 for capture */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ switch (dai->id) {
+ case RT700_AIF1:
+ break;
+ case RT700_AIF2:
+ port_config.num += 2;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt700->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 8:
+ break;
+ case 16:
+ val |= (0x1 << 4);
+ break;
+ case 20:
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ val |= (0x3 << 4);
+ break;
+ case 32:
+ val |= (0x4 << 4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 48Khz */
+ regmap_write(rt700->regmap, RT700_DAC_FORMAT_H, val);
+ regmap_write(rt700->regmap, RT700_ADC_FORMAT_H, val);
+
+ return retval;
+}
+
+static int rt700_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt700->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt700->slave, sdw_stream);
+ return 0;
+}
+
+#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT700_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt700_ops = {
+ .hw_params = rt700_pcm_hw_params,
+ .hw_free = rt700_pcm_hw_free,
+ .set_stream = rt700_set_sdw_stream,
+ .shutdown = rt700_shutdown,
+};
+
+static struct snd_soc_dai_driver rt700_dai[] = {
+ {
+ .name = "rt700-aif1",
+ .id = RT700_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .ops = &rt700_ops,
+ },
+ {
+ .name = "rt700-aif2",
+ .id = RT700_AIF2,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT700_STEREO_RATES,
+ .formats = RT700_FORMATS,
+ },
+ .ops = &rt700_ops,
+ },
+};
+
+/* Bus clock frequency */
+#define RT700_CLK_FREQ_9600000HZ 9600000
+#define RT700_CLK_FREQ_12000000HZ 12000000
+#define RT700_CLK_FREQ_6000000HZ 6000000
+#define RT700_CLK_FREQ_4800000HZ 4800000
+#define RT700_CLK_FREQ_2400000HZ 2400000
+#define RT700_CLK_FREQ_12288000HZ 12288000
+
+int rt700_clock_config(struct device *dev)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt700->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT700_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT700_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT700_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT700_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT700_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT700_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt700->regmap, 0xe0, value);
+ regmap_write(rt700->regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+int rt700_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+
+{
+ struct rt700_priv *rt700;
+ int ret;
+
+ rt700 = devm_kzalloc(dev, sizeof(*rt700), GFP_KERNEL);
+ if (!rt700)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt700);
+ rt700->slave = slave;
+ rt700->sdw_regmap = sdw_regmap;
+ rt700->regmap = regmap;
+
+ regcache_cache_only(rt700->regmap, true);
+
+ mutex_init(&rt700->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt700->jack_detect_work,
+ rt700_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt700->jack_btn_check_work,
+ rt700_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt700->hw_init = false;
+ rt700->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt700,
+ rt700_dai,
+ ARRAY_SIZE(rt700_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+int rt700_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt700_priv *rt700 = dev_get_drvdata(dev);
+
+ rt700->disable_irq = false;
+
+ if (rt700->hw_init)
+ return 0;
+
+ regcache_cache_only(rt700->regmap, false);
+ if (rt700->first_hw_init)
+ regcache_cache_bypass(rt700->regmap, true);
+
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+ if (!rt700->first_hw_init)
+ /* PM runtime status is marked as 'active' only when a Slave reports as Attached */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ /* reset */
+ regmap_write(rt700->regmap, 0xff01, 0x0000);
+ regmap_write(rt700->regmap, 0x7520, 0x001a);
+ regmap_write(rt700->regmap, 0x7420, 0xc003);
+
+ /* power on */
+ regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+ /* Set Pin Widget */
+ regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40);
+ regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40);
+ regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH);
+ regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20);
+ regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20);
+ regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20);
+
+ /* Set Configuration Default */
+ regmap_write(rt700->regmap, 0x4f12, 0x91);
+ regmap_write(rt700->regmap, 0x4e12, 0xd6);
+ regmap_write(rt700->regmap, 0x4d12, 0x11);
+ regmap_write(rt700->regmap, 0x4c12, 0x20);
+ regmap_write(rt700->regmap, 0x4f13, 0x91);
+ regmap_write(rt700->regmap, 0x4e13, 0xd6);
+ regmap_write(rt700->regmap, 0x4d13, 0x11);
+ regmap_write(rt700->regmap, 0x4c13, 0x21);
+
+ regmap_write(rt700->regmap, 0x4f19, 0x02);
+ regmap_write(rt700->regmap, 0x4e19, 0xa1);
+ regmap_write(rt700->regmap, 0x4d19, 0x90);
+ regmap_write(rt700->regmap, 0x4c19, 0x80);
+
+ /* Enable Line2 */
+ regmap_write(rt700->regmap, 0x371b, 0x40);
+ regmap_write(rt700->regmap, 0x731b, 0xb0);
+ regmap_write(rt700->regmap, 0x839b, 0x00);
+
+ /* Set index */
+ rt700_index_write(rt700->regmap, 0x4a, 0x201b);
+ rt700_index_write(rt700->regmap, 0x45, 0x5089);
+ rt700_index_write(rt700->regmap, 0x6b, 0x5064);
+ rt700_index_write(rt700->regmap, 0x48, 0xd249);
+
+ /* Finish Initial Settings, set power to D3 */
+ regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt700->hs_jack)
+ rt700_jack_init(rt700);
+
+ if (rt700->first_hw_init) {
+ regcache_cache_bypass(rt700->regmap, false);
+ regcache_mark_dirty(rt700->regmap);
+ } else
+ rt700->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt700->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT700 driver SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h
new file mode 100644
index 000000000000..491774d207de
--- /dev/null
+++ b/sound/soc/codecs/rt700.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt700.h -- RT700 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT700_H__
+#define __RT700_H__
+
+extern const struct dev_pm_ops rt700_runtime_pm;
+
+struct rt700_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ int jack_type;
+ struct mutex disable_irq_lock; /* imp-def irq lock protection */
+ bool disable_irq;
+};
+
+/* NID */
+#define RT700_AUDIO_FUNCTION_GROUP 0x01
+#define RT700_DAC_OUT1 0x02
+#define RT700_DAC_OUT2 0x03
+#define RT700_ADC_IN1 0x09
+#define RT700_ADC_IN2 0x08
+#define RT700_DMIC1 0x12
+#define RT700_DMIC2 0x13
+#define RT700_SPK_OUT 0x14
+#define RT700_MIC2 0x19
+#define RT700_LINE1 0x1a
+#define RT700_LINE2 0x1b
+#define RT700_BEEP 0x1d
+#define RT700_SPDIF 0x1e
+#define RT700_VENDOR_REGISTERS 0x20
+#define RT700_HP_OUT 0x21
+#define RT700_MIXER_IN1 0x22
+#define RT700_MIXER_IN2 0x23
+#define RT700_INLINE_CMD 0x55
+
+/* Index (NID:20h) */
+#define RT700_DAC_DC_CALI_CTL1 0x00
+#define RT700_PARA_VERB_CTL 0x1a
+#define RT700_COMBO_JACK_AUTO_CTL1 0x45
+#define RT700_COMBO_JACK_AUTO_CTL2 0x46
+#define RT700_INLINE_CMD_CTL 0x48
+#define RT700_DIGITAL_MISC_CTRL4 0x4a
+#define RT700_VREFOUT_CTL 0x6b
+#define RT700_FSM_CTL 0x6f
+#define RT700_IRQ_FLAG_TABLE1 0x80
+#define RT700_IRQ_FLAG_TABLE2 0x81
+#define RT700_IRQ_FLAG_TABLE3 0x82
+
+/* Verb */
+#define RT700_VERB_SET_CONNECT_SEL 0x3100
+#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00
+#define RT700_VERB_GET_CONNECT_SEL 0xb100
+#define RT700_VERB_SET_POWER_STATE 0x3500
+#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600
+#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700
+#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800
+#define RT700_SET_AMP_GAIN_MUTE_H 0x7300
+#define RT700_SET_AMP_GAIN_MUTE_L 0x8380
+#define RT700_VERB_GET_PIN_SENSE 0xb900
+
+#define RT700_READ_HDA_3 0x2012
+#define RT700_READ_HDA_2 0x2013
+#define RT700_READ_HDA_1 0x2014
+#define RT700_READ_HDA_0 0x2015
+#define RT700_PRIV_INDEX_W_H 0x7520
+#define RT700_PRIV_INDEX_W_L 0x85a0
+#define RT700_PRIV_DATA_W_H 0x7420
+#define RT700_PRIV_DATA_W_L 0x84a0
+#define RT700_PRIV_INDEX_R_H 0x9d20
+#define RT700_PRIV_INDEX_R_L 0xada0
+#define RT700_PRIV_DATA_R_H 0x9c20
+#define RT700_PRIV_DATA_R_L 0xaca0
+#define RT700_DAC_FORMAT_H 0x7203
+#define RT700_DAC_FORMAT_L 0x8283
+#define RT700_ADC_FORMAT_H 0x7209
+#define RT700_ADC_FORMAT_L 0x8289
+#define RT700_SET_AUDIO_POWER_STATE\
+ (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP)
+#define RT700_SET_PIN_DMIC1\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1)
+#define RT700_SET_PIN_DMIC2\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2)
+#define RT700_SET_PIN_SPK\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT)
+#define RT700_SET_PIN_HP\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT)
+#define RT700_SET_PIN_MIC2\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2)
+#define RT700_SET_PIN_LINE1\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1)
+#define RT700_SET_PIN_LINE2\
+ (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2)
+#define RT700_SET_MIC2_UNSOLICITED_ENABLE\
+ (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2)
+#define RT700_SET_HP_UNSOLICITED_ENABLE\
+ (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT)
+#define RT700_SET_INLINE_UNSOLICITED_ENABLE\
+ (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_INLINE_CMD)
+#define RT700_SET_STREAMID_DAC1\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1)
+#define RT700_SET_STREAMID_DAC2\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2)
+#define RT700_SET_STREAMID_ADC1\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1)
+#define RT700_SET_STREAMID_ADC2\
+ (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2)
+#define RT700_SET_GAIN_DAC1_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1)
+#define RT700_SET_GAIN_DAC1_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1)
+#define RT700_SET_GAIN_ADC1_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1)
+#define RT700_SET_GAIN_ADC1_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1)
+#define RT700_SET_GAIN_ADC2_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2)
+#define RT700_SET_GAIN_ADC2_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2)
+#define RT700_SET_GAIN_AMIC_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2)
+#define RT700_SET_GAIN_AMIC_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2)
+#define RT700_SET_GAIN_HP_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT)
+#define RT700_SET_GAIN_HP_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT)
+#define RT700_SET_GAIN_SPK_L\
+ (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT)
+#define RT700_SET_GAIN_SPK_H\
+ (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT)
+#define RT700_SET_EAPD_SPK\
+ (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT700_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT700_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT700_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT700_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+#define RT700_EAPD_HIGH 0x2
+#define RT700_EAPD_LOW 0x0
+#define RT700_MUTE_SFT 7
+#define RT700_DIR_IN_SFT 6
+#define RT700_DIR_OUT_SFT 7
+
+enum {
+ RT700_AIF1,
+ RT700_AIF2,
+ RT700_AIFS,
+};
+
+int rt700_io_init(struct device *dev, struct sdw_slave *slave);
+int rt700_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic);
+int rt700_clock_config(struct device *dev);
+#endif /* __RT700_H__ */
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
new file mode 100644
index 000000000000..6eb05871db37
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdw-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "rt711-sdca.h"
+#include "rt711-sdca-sdw.h"
+
+static bool rt711_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2239:
+ case 0x2f01 ... 0x2f0f:
+ case 0x2f30 ... 0x2f36:
+ case 0x2f50 ... 0x2f5a:
+ case 0x2f60:
+ case 0x3200 ... 0x3212:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2200 ... 0x2212: /* i2c debug */
+ case RT711_RC_CAL_STATUS:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x20000ff:
+ case 0x5600000 ... 0x56000ff:
+ case 0x5700000 ... 0x57000ff:
+ case 0x5800000 ... 0x58000ff:
+ case 0x5900000 ... 0x59000ff:
+ case 0x5b00000 ... 0x5b000ff:
+ case 0x5f00000 ... 0x5f000ff:
+ case 0x6100000 ... 0x61000ff:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000046:
+ case 0x2000080:
+ case 0x2000081:
+ case 0x2000083:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x5f00001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt711_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt711_sdca_readable_register,
+ .volatile_reg = rt711_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt711_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt711_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt711_sdca_mbq_readable_register,
+ .volatile_reg = rt711_sdca_mbq_volatile_register,
+ .max_register = 0x40800f12,
+ .reg_defaults = rt711_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt711_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt711->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt711->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt711_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt711->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt711_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt711_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x14; /* BITMAP: 00010100 */
+ prop->sink_ports = 0x8; /* BITMAP: 00001000 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ prop->dp0_prop = devm_kzalloc(&slave->dev, sizeof(*prop->dp0_prop),
+ GFP_KERNEL);
+ if (!prop->dp0_prop)
+ return -ENOMEM;
+
+ prop->dp0_prop->simple_ch_prep_sm = true;
+ prop->dp0_prop->ch_prep_timeout = 10;
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 700;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt711_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x, sdca_cascade=%x", __func__,
+ status->control_port, status->sdca_cascade);
+
+ if (cancel_delayed_work_sync(&rt711->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt711->scp_sdca_stat2)
+ scp_sdca_stat2 = rt711->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt711->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt711->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt711->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt711->scp_sdca_stat2 |= scp_sdca_stat2;
+
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt711->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt711->scp_sdca_stat1, rt711->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt711->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_detect_work, msecs_to_jiffies(30));
+
+ mutex_unlock(&rt711->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt711->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt711_sdca_slave_ops = {
+ .read_prop = rt711_sdca_read_prop,
+ .interrupt_callback = rt711_sdca_interrupt_callback,
+ .update_status = rt711_sdca_update_status,
+};
+
+static int rt711_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt711_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt711_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt711_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt711_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (rt711->hw_init) {
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt711->calibrate_mutex);
+ mutex_destroy(&rt711->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt711_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt711_sdca_id);
+
+static int rt711_sdca_dev_suspend(struct device *dev)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+
+ if (!rt711->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+
+ regcache_cache_only(rt711->regmap, true);
+ regcache_cache_only(rt711->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt711_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt711_sdca_priv *rt711_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt711_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt711_sdca->disable_irq_lock);
+ rt711_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt711_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt711_sdca_dev_suspend(dev);
+}
+
+#define RT711_PROBE_TIMEOUT 5000
+
+static int rt711_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt711->disable_irq_lock);
+ if (rt711->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt711->disable_irq = false;
+ }
+ mutex_unlock(&rt711->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT711_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt711->regmap, false);
+ regcache_sync(rt711->regmap);
+ regcache_cache_only(rt711->mbq_regmap, false);
+ regcache_sync(rt711->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt711_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt711-sdca",
+ .pm = pm_ptr(&rt711_sdca_pm),
+ },
+ .probe = rt711_sdca_sdw_probe,
+ .remove = rt711_sdca_sdw_remove,
+ .ops = &rt711_sdca_slave_ops,
+ .id_table = rt711_sdca_id,
+};
+module_sdw_driver(rt711_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h
new file mode 100644
index 000000000000..0d774e473ab9
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt711-sdw-sdca.h -- RT711 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDW_SDCA_H__
+#define __RT711_SDW_SDCA_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt711_sdca_reg_defaults[] = {
+ { 0x201a, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f0b, 0x00 },
+ { 0x2f0c, 0x00 },
+ { 0x2f0d, 0x00 },
+ { 0x2f0e, 0x14 },
+ { 0x2f0f, 0x00 },
+ { 0x2f50, 0x03 },
+ { 0x2f5a, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+};
+
+static const struct reg_default rt711_sdca_mbq_defaults[] = {
+ { 0x2000009, 0x1029 },
+ { 0x2000011, 0x007a },
+ { 0x200001a, 0x8003 },
+ { 0x2000045, 0x5289 },
+ { 0x2000048, 0x8049 },
+ { 0x200004a, 0xa83b },
+ { 0x200006b, 0x5064 },
+ { 0x200006f, 0x058b },
+ { 0x5800000, 0x0008 },
+ { 0x5800001, 0x0000 },
+ { 0x5f00001, 0x000a },
+ { 0x6100000, 0x6100 },
+ { 0x6100035, 0x0060 },
+ { 0x6100036, 0x0029 },
+ { 0x610003f, 0xff12 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 },
+};
+
+#endif /* __RT711_SDW_SDCA_H__ */
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
new file mode 100644
index 000000000000..3a26c782d800
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -0,0 +1,1669 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+
+#include "rt711-sdca.h"
+
+static int rt711_sdca_index_write(struct rt711_sdca_priv *rt711,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt711->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt711->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt711_sdca_index_read(struct rt711_sdca_priv *rt711,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt711->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt711->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt711_sdca_index_update_bits(struct rt711_sdca_priv *rt711,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt711_sdca_index_read(rt711, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt711_sdca_index_write(rt711, nid, reg, tmp);
+}
+
+static void rt711_sdca_reset(struct rt711_sdca_priv *rt711)
+{
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET,
+ RT711_HIDDEN_REG_SW_RESET);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+}
+
+static void rt711_sdca_ge_force_jack_type(struct rt711_sdca_priv *rt711, unsigned int det_mode)
+{
+ switch (det_mode) {
+ case 0x00:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x0000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x10, 0x00);
+ break;
+ case 0x03:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x13);
+ break;
+ case 0x05:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8400);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x15);
+ break;
+ }
+}
+
+static int rt711_sdca_calibration(struct rt711_sdca_priv *rt711)
+{
+ unsigned int val, loop_rc = 0, loop_dc = 0;
+ struct device *dev;
+ struct regmap *regmap = rt711->regmap;
+ int chk_cnt = 100;
+ int ret = 0;
+
+ mutex_lock(&rt711->calibrate_mutex);
+ dev = regmap_get_device(regmap);
+
+ regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val);
+ /* RC calibration */
+ if (!(val & 0x40))
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL,
+ RT711_MISC_POWER_CTL0, 0x0010, 0x0010);
+
+ for (loop_rc = 0; loop_rc < chk_cnt && !(val & 0x40); loop_rc++) {
+ usleep_range(10000, 11000);
+ ret = regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_rc == chk_cnt)
+ dev_err(dev, "%s, RC calibration time-out!\n", __func__);
+
+ /* HP calibration by manual mode setting */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FSM_CTL, 0x2000, 0x2000);
+
+ /* Calibration manual mode */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FSM_CTL, 0xf, RT711_CALI_CTL);
+
+ /* reset HP calibration */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST, 0x00);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST,
+ RT711_DAC_DC_FORCE_CALI_RST);
+
+ /* cal_clk_en_reg */
+ if (rt711->hw_ver == RT711_VER_VD0)
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_CLK_EN,
+ RT711_DAC_DC_CALI_CLK_EN);
+
+ /* trigger */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER,
+ RT711_DAC_DC_CALI_TRIGGER);
+
+ /* wait for calibration process */
+ rt711_sdca_index_read(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+
+ for (loop_dc = 0; loop_dc < chk_cnt &&
+ (val & RT711_DAC_DC_CALI_TRIGGER); loop_dc++) {
+ usleep_range(10000, 11000);
+ ret = rt711_sdca_index_read(rt711, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_dc == chk_cnt)
+ dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+ if (loop_dc == chk_cnt || loop_rc == chk_cnt)
+ ret = -ETIMEDOUT;
+
+_cali_fail_:
+ /* enable impedance sense */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FSM_CTL, RT711_FSM_IMP_EN, RT711_FSM_IMP_EN);
+
+ /* release HP-JD and trigger FSM */
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_DIGITAL_MISC_CTRL4, 0x201b);
+
+ mutex_unlock(&rt711->calibrate_mutex);
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt711_sdca_button_detect(struct rt711_sdca_priv *rt711)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0),
+ &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt711->regmap,
+ RT711_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01,
+ RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+
+ return btn_type;
+}
+
+static int rt711_sdca_headset_detect(struct rt711_sdca_priv *rt711)
+{
+ unsigned int det_mode;
+ int ret;
+
+ rt711_sdca_ge_force_jack_type(rt711, rt711->ge_mode_override);
+
+ /* get detected_mode */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt711->jack_type = 0;
+ break;
+ case 0x03:
+ rt711->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt711->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0),
+ det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt711->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt711_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt711_sdca_priv *rt711 =
+ container_of(work, struct rt711_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt711->hs_jack)
+ return;
+
+ if (!snd_soc_card_is_instantiated(rt711->component->card))
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt711->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt711_sdca_headset_detect(rt711);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt711->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt711_sdca_button_detect(rt711);
+
+ if (rt711->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt711->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt711->jack_type);
+ dev_dbg(&rt711->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt711->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt711->scp_sdca_stat1, rt711->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt711_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt711_sdca_priv *rt711 =
+ container_of(work, struct rt711_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt711->regmap,
+ RT711_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+ } else
+ rt711->jack_type = 0;
+
+ dev_dbg(&rt711->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711)
+{
+ mutex_lock(&rt711->calibrate_mutex);
+
+ if (rt711->hs_jack) {
+ /* Enable HID1 event & set button RTC mode */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL6, 0x80f0, 0x8000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL2, 0x11dd, 0x11dd);
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL7, 0xffff);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL9, 0xf000, 0x0000);
+
+ /* GE_mode_change_event_en & Hid1_push_button_event_en */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0c00);
+
+ switch (rt711->jd_src) {
+ case RT711_JD1:
+ /* default settings was already for JD1 */
+ break;
+ case RT711_JD2:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL,
+ RT711_JD2_DIGITAL_MODE_SEL);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ case RT711_JD2_100K:
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa47e);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL,
+ RT711_JD2_DIGITAL_MODE_SEL);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_JD2_2PORT_100K_DECODE_MASK | RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_100K_DECODE_HP | RT711_HP_JD_SEL_JD2);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI_OFF);
+ break;
+ default:
+ dev_warn(rt711->component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__);
+ } else {
+ /* disable HID 1/2 event */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0000);
+
+ dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__);
+ }
+
+ mutex_unlock(&rt711->calibrate_mutex);
+}
+
+static int rt711_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt711_sdca_jack_init(rt711);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt711_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int lvalue, rvalue;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt711->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt711->mbq_regmap, mc->rreg, &rvalue);
+
+ /* control value to 2's complement value */
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+ read_l = gain_l_val;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = (gain_l_val * 10) << mc->shift;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag && gain_l_val > mc->shift)
+ gain_l_val = (gain_l_val - mc->shift) * 75;
+ else
+ gain_l_val = (mc->shift - gain_l_val) * 75;
+ gain_l_val <<= 8;
+ gain_l_val /= 100;
+ if (!(adc_vol_flag && read_l > mc->shift)) {
+ gain_l_val = ~gain_l_val;
+ gain_l_val += 1;
+ }
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+ read_r = gain_r_val;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = (gain_r_val * 10) << mc->shift;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag && gain_r_val > mc->shift)
+ gain_r_val = (gain_r_val - mc->shift) * 75;
+ else
+ gain_r_val = (mc->shift - gain_r_val) * 75;
+ gain_r_val <<= 8;
+ gain_r_val /= 100;
+ if (!(adc_vol_flag && read_r > mc->shift)) {
+ gain_r_val = ~gain_r_val;
+ gain_r_val += 1;
+ }
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue != gain_l_val || rvalue != gain_r_val)
+ changed = 1;
+ else
+ return 0;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+ /* Lch*/
+ regmap_write(rt711->mbq_regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt711->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt711->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt711->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ break;
+ }
+
+ return i == 3 ? -EIO : changed;
+}
+
+static int rt711_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0, neg_flag = 0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt711->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt711->mbq_regmap, mc->rreg, &read_r);
+
+ /* 2's complement value to control value */
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = (read_l >> mc->shift) / 10;
+ else { /* ADC/DAC gain */
+ ctl_l = read_l;
+ if (read_l & BIT(15)) {
+ ctl_l = 0xffff & ~(read_l - 1);
+ neg_flag = 1;
+ }
+ ctl_l *= 100;
+ ctl_l >>= 8;
+ if (adc_vol_flag) {
+ if (neg_flag)
+ ctl_l = mc->shift - (ctl_l / 75);
+ else
+ ctl_l = mc->shift + (ctl_l / 75);
+ } else
+ ctl_l = mc->max - (ctl_l / 75);
+ }
+
+ neg_flag = 0;
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = (read_r >> mc->shift) / 10;
+ else { /* ADC/DAC gain */
+ ctl_r = read_r;
+ if (read_r & BIT(15)) {
+ ctl_r = 0xffff & ~(read_r - 1);
+ neg_flag = 1;
+ }
+ ctl_r *= 100;
+ ctl_r >>= 8;
+ if (adc_vol_flag) {
+ if (neg_flag)
+ ctl_r = mc->shift - (ctl_r / 75);
+ else
+ ctl_r = mc->shift + (ctl_r / 75);
+ } else
+ ctl_r = mc->max - (ctl_r / 75);
+ }
+ } else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt711_sdca_set_fu0f_capture_ctl(struct rt711_sdca_priv *rt711)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt711->fu0f_dapm_mute || rt711->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt711->fu0f_dapm_mute || rt711->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+ RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+ RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt711_sdca_set_fu1e_capture_ctl(struct rt711_sdca_priv *rt711)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+ RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+ RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt711_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt711->fu1e_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt711->fu1e_mixer_r_mute;
+ return 0;
+}
+
+static int rt711_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt711->fu1e_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt711->fu1e_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt711->fu1e_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt711->fu1e_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt711_sdca_set_fu1e_capture_ctl(rt711);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt711_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt711->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt711->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt711_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt711->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt711->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt711->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt711->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt711_sdca_set_fu0f_capture_ctl(rt711);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt711_sdca_ge_select_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val, item;
+
+ val = (rt711->ge_mode_override >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[0] = item;
+ return 0;
+}
+
+static int rt711_sdca_ge_select_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val, change = 0;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ if (rt711->ge_mode_override != val) {
+ rt711->ge_mode_override = val;
+ change = 1;
+ }
+
+ return change;
+}
+
+static const char * const rt711_sdca_ge_select[] = {
+ "Auto",
+ "Headphone",
+ "Headset",
+};
+
+static int rt711_sdca_ge_select_values[] = {
+ 0,
+ 3,
+ 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt711_sdca_ge_mode_enum, SND_SOC_NOPM,
+ 0, 0x7, rt711_sdca_ge_select, rt711_sdca_ge_select_values);
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt711_sdca_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x57, 0x57, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, out_vol_tlv),
+ SOC_DOUBLE_EXT("FU1E Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt711_sdca_fu1e_capture_get, rt711_sdca_fu1e_capture_put),
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt711_sdca_fu0f_capture_get, rt711_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x17, 0x3f, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x17, 0x3f, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU44 Gain Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R),
+ 8, 3, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU15 Gain Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R),
+ 8, 3, 0,
+ rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv),
+ SOC_ENUM_EXT("GE49 Selected Mode", rt711_sdca_ge_mode_enum,
+ rt711_sdca_ge_select_get, rt711_sdca_ge_select_put),
+};
+
+static int rt711_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 10;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 13;
+ else
+ return -EINVAL;
+
+ rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_MUX_CTL1, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+ return 0;
+}
+
+static int rt711_sdca_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 10;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 13;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_MUX_CTL1, &val2);
+ val2 = (val2 >> mask_sft) & 0x7;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_MUX_CTL1, 0x7 << mask_sft,
+ val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt711_sdca_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum,
+ rt711_sdca_mux_get, rt711_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt711_sdca_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum,
+ rt711_sdca_mux_get, rt711_sdca_mux_put);
+
+static int rt711_sdca_fu05_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_L),
+ unmute);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_R),
+ unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_L),
+ mute);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+ RT711_SDCA_CTL_FU_MUTE, CH_R),
+ mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_fu0f_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt711->fu0f_dapm_mute = false;
+ rt711_sdca_set_fu0f_capture_ctl(rt711);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt711->fu0f_dapm_mute = true;
+ rt711_sdca_set_fu0f_capture_ctl(rt711);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_fu1e_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt711->fu1e_dapm_mute = false;
+ rt711_sdca_set_fu1e_capture_ctl(rt711);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt711->fu1e_dapm_mute = true;
+ rt711_sdca_set_fu1e_capture_ctl(rt711);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_pde28_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_pde29_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE29,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE29,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_pde2a_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PDE2A,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PDE2A,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_sdca_line1_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ static unsigned int sel_mode = 0xffff;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_read(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49,
+ RT711_SDCA_CTL_SELECTED_MODE, 0),
+ &sel_mode);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE1,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x1);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49,
+ RT711_SDCA_CTL_SELECTED_MODE, 0),
+ 0x7);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE1,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x0);
+ if (sel_mode != 0xffff)
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49,
+ RT711_SDCA_CTL_SELECTED_MODE, 0),
+ sel_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static int rt711_sdca_line2_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDELINE2,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE2,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x1);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE2,
+ RT711_SDCA_CTL_VENDOR_DEF, 0),
+ 0x0);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDELINE2,
+ RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt711_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_PGA_E("LINE1 Power", SND_SOC_NOPM,
+ 0, 0, NULL, 0, rt711_sdca_line1_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE2 Power", SND_SOC_NOPM,
+ 0, 0, NULL, 0, rt711_sdca_line2_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PDE 28", SND_SOC_NOPM, 0, 0,
+ rt711_sdca_pde28_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 29", SND_SOC_NOPM, 0, 0,
+ rt711_sdca_pde29_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 2A", SND_SOC_NOPM, 0, 0,
+ rt711_sdca_pde2a_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_sdca_fu05_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_sdca_fu0f_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_sdca_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_sdca_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_sdca_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt711_sdca_audio_map[] = {
+ {"FU 05", NULL, "DP3RX"},
+ {"DP2TX", NULL, "FU 0F"},
+ {"DP4TX", NULL, "FU 1E"},
+
+ {"LINE1 Power", NULL, "LINE1"},
+ {"LINE2 Power", NULL, "LINE2"},
+ {"HP", NULL, "PDE 28"},
+ {"FU 0F", NULL, "PDE 29"},
+ {"FU 1E", NULL, "PDE 2A"},
+
+ {"FU 0F", NULL, "ADC 22 Mux"},
+ {"FU 1E", NULL, "ADC 23 Mux"},
+ {"ADC 22 Mux", "DMIC", "DMIC1"},
+ {"ADC 22 Mux", "LINE1", "LINE1 Power"},
+ {"ADC 22 Mux", "LINE2", "LINE2 Power"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "DMIC", "DMIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1 Power"},
+ {"ADC 23 Mux", "LINE2", "LINE2 Power"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+
+ {"HP", NULL, "FU 05"},
+};
+
+static int rt711_sdca_parse_dt(struct rt711_sdca_priv *rt711, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt711->jd_src);
+
+ return 0;
+}
+
+static int rt711_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711_sdca_parse_dt(rt711, &rt711->slave->dev);
+ rt711->component = component;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
+ .probe = rt711_sdca_probe,
+ .controls = rt711_sdca_snd_controls,
+ .num_controls = ARRAY_SIZE(rt711_sdca_snd_controls),
+ .dapm_widgets = rt711_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt711_sdca_dapm_widgets),
+ .dapm_routes = rt711_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map),
+ .set_jack = rt711_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt711_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt711->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_config.num = 3;
+ } else {
+ if (dai->id == RT711_AIF1)
+ port_config.num = 2;
+ else if (dai->id == RT711_AIF2)
+ port_config.num = 4;
+ else
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt711->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT711_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT711_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT711_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT711_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS11, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_CS1F, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt711->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt711->slave, sdw_stream);
+ return 0;
+}
+
+#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt711_sdca_ops = {
+ .hw_params = rt711_sdca_pcm_hw_params,
+ .hw_free = rt711_sdca_pcm_hw_free,
+ .set_stream = rt711_sdca_set_sdw_stream,
+ .shutdown = rt711_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt711_sdca_dai[] = {
+ {
+ .name = "rt711-sdca-aif1",
+ .id = RT711_AIF1,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_sdca_ops,
+ },
+ {
+ .name = "rt711-sdca-aif2",
+ .id = RT711_AIF2,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_sdca_ops,
+ }
+};
+
+int rt711_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt711_sdca_priv *rt711;
+ int ret;
+
+ rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL);
+ if (!rt711)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt711);
+ rt711->slave = slave;
+ rt711->regmap = regmap;
+ rt711->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt711->regmap, true);
+ regcache_cache_only(rt711->mbq_regmap, true);
+
+ mutex_init(&rt711->calibrate_mutex);
+ mutex_init(&rt711->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt711->hw_init = false;
+ rt711->first_hw_init = false;
+ rt711->fu0f_dapm_mute = true;
+ rt711->fu1e_dapm_mute = true;
+ rt711->fu0f_mixer_l_mute = rt711->fu0f_mixer_r_mute = true;
+ rt711->fu1e_mixer_l_mute = rt711->fu1e_mixer_r_mute = true;
+
+ /* JD source uses JD2 in default */
+ rt711->jd_src = RT711_JD2;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt711,
+ rt711_sdca_dai,
+ ARRAY_SIZE(rt711_sdca_dai));
+
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711)
+{
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_GPIO_TEST_MODE_CTL2, 0x0e00);
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_GPIO_CTL, 0x0008);
+
+ regmap_write(rt711->regmap, 0x2f5a, 0x01);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_ADC27_VOL_SET, 0x8728);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa472);
+
+ regmap_write(rt711->regmap, 0x2f50, 0x02);
+
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL,
+ RT711_MISC_POWER_CTL4, 0x6000, 0x6000);
+
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0x000c, 0x000c);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_CONFIG_CTL, 0x0000);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_VAD,
+ RT711_VAD_SRAM_CTL1, 0x0050);
+}
+
+static void rt711_sdca_vd1_io_init(struct rt711_sdca_priv *rt711)
+{
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_UNSOLICITED_CTL, 0x0300, 0x0000);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa43e);
+
+ regmap_write(rt711->regmap, 0x2f5a, 0x05);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTRL6, 0x0500);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_DMIC_CTL1, 0x6173);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_HDA_LEGACY_CONFIG_CTL, 0x0000);
+
+ rt711_sdca_index_write(rt711, RT711_VENDOR_VAD,
+ RT711_VAD_SRAM_CTL1, 0x0050);
+}
+
+int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned int val;
+
+ rt711->disable_irq = false;
+
+ if (rt711->hw_init)
+ return 0;
+
+ regcache_cache_only(rt711->regmap, false);
+ regcache_cache_only(rt711->mbq_regmap, false);
+
+ if (rt711->first_hw_init) {
+ regcache_cache_bypass(rt711->regmap, true);
+ regcache_cache_bypass(rt711->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt711_sdca_reset(rt711);
+
+ rt711_sdca_index_read(rt711, RT711_VENDOR_REG, RT711_JD_PRODUCT_NUM, &val);
+ rt711->hw_ver = val & 0xf;
+
+ if (rt711->hw_ver == RT711_VER_VD0)
+ rt711_sdca_vd0_io_init(rt711);
+ else
+ rt711_sdca_vd1_io_init(rt711);
+
+ /* DP4 mux select from 08_filter_Out_pri */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_FILTER_SRC_SEL, 0x1800, 0x0800);
+
+ /* ge_exclusive_inbox_en disable */
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+ RT711_PUSH_BTN_INT_CTL0, 0x20, 0x00);
+
+ /* calibration */
+ ret = rt711_sdca_calibration(rt711);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+
+ /* HP output enable */
+ regmap_write(rt711->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_OT1, RT711_SDCA_CTL_VENDOR_DEF, 0), 0x4);
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt711->hs_jack)
+ rt711_sdca_jack_init(rt711);
+
+ if (rt711->first_hw_init) {
+ regcache_cache_bypass(rt711->regmap, false);
+ regcache_mark_dirty(rt711->regmap);
+ regcache_cache_bypass(rt711->mbq_regmap, false);
+ regcache_mark_dirty(rt711->mbq_regmap);
+ } else
+ rt711->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt711->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
new file mode 100644
index 000000000000..15263dcb0314
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt711-sdca.h -- RT711 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2021 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDCA_H__
+#define __RT711_SDCA_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt711_sdca_priv {
+ struct regmap *regmap, *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ struct mutex disable_irq_lock; /* SDCA irq lock protection */
+ bool disable_irq;
+ int jack_type, jd_src;
+ unsigned int scp_sdca_stat1, scp_sdca_stat2;
+ int hw_ver;
+ bool fu0f_dapm_mute, fu0f_mixer_l_mute, fu0f_mixer_r_mute;
+ bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute;
+ unsigned int ge_mode_override;
+};
+
+/* NID */
+#define RT711_AUDIO_FUNCTION_GROUP 0x01
+#define RT711_DAC_OUT2 0x03
+#define RT711_ADC_IN1 0x09
+#define RT711_ADC_IN2 0x08
+#define RT711_DMIC1 0x12
+#define RT711_DMIC2 0x13
+#define RT711_MIC2 0x19
+#define RT711_LINE1 0x1a
+#define RT711_LINE2 0x1b
+#define RT711_BEEP 0x1d
+#define RT711_VENDOR_REG 0x20
+#define RT711_HP_OUT 0x21
+#define RT711_MIXER_IN1 0x22
+#define RT711_MIXER_IN2 0x23
+#define RT711_INLINE_CMD 0x55
+#define RT711_VENDOR_CALI 0x58
+#define RT711_VENDOR_IMS_DRE 0x5b
+#define RT711_VENDOR_VAD 0x5e
+#define RT711_VENDOR_ANALOG_CTL 0x5f
+#define RT711_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT711_JD_PRODUCT_NUM 0x00
+#define RT711_DMIC_CTL1 0x06
+#define RT711_JD_CTL1 0x08
+#define RT711_JD_CTL2 0x09
+#define RT711_CC_DET1 0x11
+#define RT711_PARA_VERB_CTL 0x1a
+#define RT711_COMBO_JACK_AUTO_CTL1 0x45
+#define RT711_COMBO_JACK_AUTO_CTL2 0x46
+#define RT711_COMBO_JACK_AUTO_CTL3 0x47
+#define RT711_INLINE_CMD_CTL 0x48
+#define RT711_DIGITAL_MISC_CTRL4 0x4a
+#define RT711_JD_CTRL6 0x6a
+#define RT711_VREFOUT_CTL 0x6b
+#define RT711_GPIO_TEST_MODE_CTL2 0x6d
+#define RT711_FSM_CTL 0x6f
+#define RT711_IRQ_FLAG_TABLE1 0x80
+#define RT711_IRQ_FLAG_TABLE2 0x81
+#define RT711_IRQ_FLAG_TABLE3 0x82
+#define RT711_HP_FSM_CTL 0x83
+#define RT711_TX_RX_MUX_CTL 0x91
+#define RT711_FILTER_SRC_SEL 0xb0
+#define RT711_ADC27_VOL_SET 0xb7
+
+/* Index (NID:58h) */
+#define RT711_DAC_DC_CALI_CTL1 0x00
+#define RT711_DAC_DC_CALI_CTL2 0x01
+
+/* Index (NID:5bh) */
+#define RT711_IMS_DIGITAL_CTL1 0x00
+#define RT711_HP_IMS_RESULT_L 0x20
+#define RT711_HP_IMS_RESULT_R 0x21
+
+/* Index (NID:5eh) */
+#define RT711_VAD_SRAM_CTL1 0x10
+
+/* Index (NID:5fh) */
+#define RT711_MISC_POWER_CTL0 0x01
+#define RT711_MISC_POWER_CTL4 0x05
+
+/* Index (NID:61h) */
+#define RT711_HDA_LEGACY_MUX_CTL1 0x00
+#define RT711_HDA_LEGACY_UNSOLICITED_CTL 0x03
+#define RT711_HDA_LEGACY_CONFIG_CTL 0x06
+#define RT711_HDA_LEGACY_RESET_CTL 0x08
+#define RT711_HDA_LEGACY_GPIO_CTL 0x0a
+#define RT711_ADC08_09_PDE_CTL 0x24
+#define RT711_GE_MODE_RELATED_CTL 0x35
+#define RT711_PUSH_BTN_INT_CTL0 0x36
+#define RT711_PUSH_BTN_INT_CTL1 0x37
+#define RT711_PUSH_BTN_INT_CTL2 0x38
+#define RT711_PUSH_BTN_INT_CTL6 0x3c
+#define RT711_PUSH_BTN_INT_CTL7 0x3d
+#define RT711_PUSH_BTN_INT_CTL9 0x3f
+
+/* DAC DC offset calibration control-1 (0x00)(NID:20h) */
+#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15)
+#define RT711_DAC_DC_CALI_CLK_EN (0x1 << 14)
+#define RT711_DAC_DC_FORCE_CALI_RST (0x1 << 3)
+
+/* jack detect control 1 (0x08)(NID:20h) */
+#define RT711_JD2_DIGITAL_MODE_SEL (0x1 << 1)
+
+/* jack detect control 2 (0x09)(NID:20h) */
+#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
+#define RT711_JD2_2PORT_100K_DECODE_MASK (0x1 << 12)
+#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12)
+#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
+#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
+
+/* CC DET1 (0x11)(NID:20h) */
+#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
+#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
+#define RT711_POW_CC1_AGPI (0x1 << 5)
+#define RT711_POW_CC1_AGPI_ON (0x1 << 5)
+#define RT711_POW_CC1_AGPI_OFF (0x0 << 5)
+
+/* Parameter & Verb control (0x1a)(NID:20h) */
+#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* FSM control (0x6f)(NID:20h) */
+#define RT711_CALI_CTL (0x0 << 0)
+#define RT711_COMBOJACK_CTL (0x1 << 0)
+#define RT711_IMS_CTL (0x2 << 0)
+#define RT711_DEPOP_CTL (0x3 << 0)
+#define RT711_FSM_IMP_EN (0x1 << 6)
+
+/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */
+#define RT711_TRIGGER_IMS (0x1 << 15)
+#define RT711_IMS_EN (0x1 << 6)
+
+#define RT711_EAPD_HIGH 0x2
+#define RT711_EAPD_LOW 0x0
+#define RT711_MUTE_SFT 7
+/* set input/output mapping to payload[14][15] separately */
+#define RT711_DIR_IN_SFT 6
+#define RT711_DIR_OUT_SFT 7
+
+/* RC Calibration register */
+#define RT711_RC_CAL_STATUS 0x320c
+
+/* Buffer address for HID */
+#define RT711_BUF_ADDR_HID1 0x44030000
+#define RT711_BUF_ADDR_HID2 0x44030020
+
+/* RT711 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+
+/* RT711 SDCA entity */
+#define RT711_SDCA_ENT_HID01 0x01
+#define RT711_SDCA_ENT_GE49 0x49
+#define RT711_SDCA_ENT_USER_FU05 0x05
+#define RT711_SDCA_ENT_USER_FU0F 0x0f
+#define RT711_SDCA_ENT_USER_FU1E 0x1e
+#define RT711_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT711_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT711_SDCA_ENT_PDE28 0x28
+#define RT711_SDCA_ENT_PDE29 0x29
+#define RT711_SDCA_ENT_PDE2A 0x2a
+#define RT711_SDCA_ENT_CS01 0x01
+#define RT711_SDCA_ENT_CS11 0x11
+#define RT711_SDCA_ENT_CS1F 0x1f
+#define RT711_SDCA_ENT_OT1 0x06
+#define RT711_SDCA_ENT_LINE1 0x09
+#define RT711_SDCA_ENT_LINE2 0x31
+#define RT711_SDCA_ENT_PDELINE2 0x36
+#define RT711_SDCA_ENT_USER_FU9 0x41
+
+/* RT711 SDCA control */
+#define RT711_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT711_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT711_SDCA_CTL_FU_MUTE 0x01
+#define RT711_SDCA_CTL_FU_VOLUME 0x02
+#define RT711_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT711_SDCA_CTL_SELECTED_MODE 0x01
+#define RT711_SDCA_CTL_DETECTED_MODE 0x02
+#define RT711_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT711_SDCA_CTL_VENDOR_DEF 0x30
+
+/* RT711 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* sample frequency index */
+#define RT711_SDCA_RATE_44100HZ 0x08
+#define RT711_SDCA_RATE_48000HZ 0x09
+#define RT711_SDCA_RATE_96000HZ 0x0b
+#define RT711_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT711_AIF1,
+ RT711_AIF2,
+ RT711_AIFS,
+};
+
+enum rt711_sdca_jd_src {
+ RT711_JD_NULL,
+ RT711_JD1,
+ RT711_JD2,
+ RT711_JD2_100K
+};
+
+enum rt711_sdca_ver {
+ RT711_VER_VD0,
+ RT711_VER_VD1
+};
+
+int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt711_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+
+int rt711_sdca_jack_detect(struct rt711_sdca_priv *rt711, bool *hp, bool *mic);
+#endif /* __RT711_SDCA_H__ */
diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c
new file mode 100644
index 000000000000..93a5a89a96b1
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdw.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt711-sdw.c -- rt711 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt711.h"
+#include "rt711-sdw.h"
+
+static bool rt711_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0:
+ case 0x00f0:
+ case 0x2012 ... 0x2016:
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2201 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2239:
+ case 0x2f01 ... 0x2f0f:
+ case 0x3000 ... 0x3fff:
+ case 0x7000 ... 0x7fff:
+ case 0x8300 ... 0x83ff:
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0x752008:
+ case 0x752009:
+ case 0x75200b:
+ case 0x752011:
+ case 0x75201a:
+ case 0x752045:
+ case 0x752046:
+ case 0x752048:
+ case 0x75204a:
+ case 0x75206b:
+ case 0x75206f:
+ case 0x752080:
+ case 0x752081:
+ case 0x752091:
+ case 0x755800:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt711_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2016:
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x2012 ... 0x2015: /* HD-A read */
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2201 ... 0x2212: /* i2c debug */
+ case 0x2220 ... 0x2223: /* decoded HD-A */
+ case 0x9c00 ... 0x9cff:
+ case 0xb900 ... 0xb9ff:
+ case 0xff01:
+ case 0x75201a:
+ case 0x752046:
+ case 0x752080:
+ case 0x752081:
+ case 0x755800:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
+ unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
+ unsigned int is_hda_reg = 1, is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt711->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT711_PRIV_DATA_R_H | nid;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg3, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg4, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x3000) {
+ reg += 0x8000;
+ ret = regmap_write(rt711->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ reg += 0x2000;
+ reg |= 0x800;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg2, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x9000) {
+ ret = regmap_write(rt711->sdw_regmap,
+ reg, ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0xb000) {
+ ret = regmap_write(rt711->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_read(rt711->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ is_hda_reg = 0;
+ }
+
+ if (is_hda_reg || is_index_reg) {
+ sdw_data_3 = 0;
+ sdw_data_2 = 0;
+ sdw_data_1 = 0;
+ sdw_data_0 = 0;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_3, &sdw_data_3);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_2, &sdw_data_2);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_1, &sdw_data_1);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt711->sdw_regmap,
+ RT711_READ_HDA_0, &sdw_data_0);
+ if (ret < 0)
+ return ret;
+ *val = ((sdw_data_3 & 0xff) << 24) |
+ ((sdw_data_2 & 0xff) << 16) |
+ ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
+ }
+
+ if (is_hda_reg == 0)
+ dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n",
+ __func__, reg, reg2, reg3, reg4, *val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x => %08x\n",
+ __func__, reg, reg2, *val);
+
+ return 0;
+}
+
+static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
+ unsigned int is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt711->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT711_PRIV_DATA_W_H | nid;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg3, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg4, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ is_index_reg = 1;
+ } else if (reg < 0x4fff) {
+ ret = regmap_write(rt711->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (reg == RT711_FUNC_RESET) {
+ ret = regmap_write(rt711->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ ret = regmap_write(rt711->sdw_regmap,
+ reg, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt711->sdw_regmap, reg2, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt711->sdw_regmap,
+ reg2, ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (reg2 == 0)
+ dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
+ __func__, reg, reg2, reg3, reg4, val2, val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
+ __func__, reg, reg2, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt711_regmap = {
+ .reg_bits = 24,
+ .val_bits = 32,
+ .readable_reg = rt711_readable_register,
+ .volatile_reg = rt711_volatile_register,
+ .max_register = 0x755800,
+ .reg_defaults = rt711_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt711_sdw_read,
+ .reg_write = rt711_sdw_write,
+};
+
+static const struct regmap_config rt711_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt711_readable_register,
+ .max_register = 0xff01,
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt711_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt711->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt711->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt711_io_init(&slave->dev, slave);
+}
+
+static int rt711_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x14; /* BITMAP: 00010100 */
+ prop->sink_ports = 0x8; /* BITMAP: 00001000 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt711_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt711->params, params, sizeof(*params));
+
+ ret = rt711_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "%s: Invalid clk config", __func__);
+
+ return ret;
+}
+
+static int rt711_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x", __func__, status->control_port);
+
+ mutex_lock(&rt711->disable_irq_lock);
+ if (status->control_port & 0x4 && !rt711->disable_irq) {
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_detect_work, msecs_to_jiffies(250));
+ }
+ mutex_unlock(&rt711->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt711_slave_ops = {
+ .read_prop = rt711_read_prop,
+ .interrupt_callback = rt711_interrupt_callback,
+ .update_status = rt711_update_status,
+ .bus_config = rt711_bus_config,
+};
+
+static int rt711_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *sdw_regmap, *regmap;
+
+ /* Regmap Initialization */
+ sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap);
+ if (IS_ERR(sdw_regmap))
+ return PTR_ERR(sdw_regmap);
+
+ regmap = devm_regmap_init(&slave->dev, NULL,
+ &slave->dev, &rt711_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt711_init(&slave->dev, sdw_regmap, regmap, slave);
+}
+
+static int rt711_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+ if (rt711->hw_init) {
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+ cancel_work_sync(&rt711->calibration_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt711->calibrate_mutex);
+ mutex_destroy(&rt711->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt711_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt711_id);
+
+static int rt711_dev_suspend(struct device *dev)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+
+ if (!rt711->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt711->jack_detect_work);
+ cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+ cancel_work_sync(&rt711->calibration_work);
+
+ regcache_cache_only(rt711->regmap, true);
+
+ return 0;
+}
+
+static int rt711_dev_system_suspend(struct device *dev)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ if (!rt711->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt711->disable_irq_lock);
+ rt711->disable_irq = true;
+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
+ SDW_SCP_INT1_IMPL_DEF, 0);
+ mutex_unlock(&rt711->disable_irq_lock);
+
+ if (ret < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
+ }
+
+ return rt711_dev_suspend(dev);
+}
+
+#define RT711_PROBE_TIMEOUT 5000
+
+static int rt711_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt711->disable_irq_lock);
+ if (rt711->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF);
+ rt711->disable_irq = false;
+ }
+ mutex_unlock(&rt711->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT711_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt711->regmap, false);
+ regcache_sync_region(rt711->regmap, 0x3000, 0x8fff);
+ regcache_sync_region(rt711->regmap, 0x752009, 0x752091);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt711_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume)
+ RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdw_driver = {
+ .driver = {
+ .name = "rt711",
+ .pm = pm_ptr(&rt711_pm),
+ },
+ .probe = rt711_sdw_probe,
+ .remove = rt711_sdw_remove,
+ .ops = &rt711_slave_ops,
+ .id_table = rt711_id,
+};
+module_sdw_driver(rt711_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT711 SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h
new file mode 100644
index 000000000000..6acf9858330d
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdw.h
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt711-sdw.h -- RT711 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDW_H__
+#define __RT711_SDW_H__
+
+static const struct reg_default rt711_reg_defaults[] = {
+ { 0x0000, 0x00 },
+ { 0x0001, 0x00 },
+ { 0x0002, 0x00 },
+ { 0x0003, 0x00 },
+ { 0x0004, 0x00 },
+ { 0x0005, 0x01 },
+ { 0x0020, 0x00 },
+ { 0x0022, 0x00 },
+ { 0x0023, 0x00 },
+ { 0x0024, 0x00 },
+ { 0x0025, 0x00 },
+ { 0x0026, 0x00 },
+ { 0x0030, 0x00 },
+ { 0x0032, 0x00 },
+ { 0x0033, 0x00 },
+ { 0x0034, 0x00 },
+ { 0x0035, 0x00 },
+ { 0x0036, 0x00 },
+ { 0x0040, 0x00 },
+ { 0x0041, 0x00 },
+ { 0x0042, 0x00 },
+ { 0x0043, 0x00 },
+ { 0x0044, 0x20 },
+ { 0x0045, 0x01 },
+ { 0x0046, 0x01 },
+ { 0x0050, 0x20 },
+ { 0x0051, 0x02 },
+ { 0x0052, 0x5d },
+ { 0x0053, 0x07 },
+ { 0x0054, 0x11 },
+ { 0x0055, 0x00 },
+ { 0x0060, 0x00 },
+ { 0x0070, 0x00 },
+ { 0x0080, 0xc0 },
+ { 0x0088, 0x00 },
+ { 0x00e0, 0x00 },
+ { 0x00e1, 0x00 },
+ { 0x00e2, 0x00 },
+ { 0x00e3, 0x00 },
+ { 0x00e5, 0x00 },
+ { 0x00ee, 0x00 },
+ { 0x00ef, 0x00 },
+ { 0x00f0, 0x00 },
+ { 0x00f1, 0x00 },
+ { 0x00f2, 0x00 },
+ { 0x00f3, 0x00 },
+ { 0x00f4, 0x00 },
+ { 0x00f5, 0x00 },
+ { 0x00fe, 0x00 },
+ { 0x00ff, 0x00 },
+ { 0x0100, 0x00 },
+ { 0x0101, 0x00 },
+ { 0x0102, 0x00 },
+ { 0x0103, 0x00 },
+ { 0x0104, 0x00 },
+ { 0x0105, 0x00 },
+ { 0x0120, 0x00 },
+ { 0x0122, 0x00 },
+ { 0x0123, 0x00 },
+ { 0x0124, 0x00 },
+ { 0x0125, 0x00 },
+ { 0x0126, 0x00 },
+ { 0x0127, 0x00 },
+ { 0x0130, 0x00 },
+ { 0x0132, 0x00 },
+ { 0x0133, 0x00 },
+ { 0x0134, 0x00 },
+ { 0x0135, 0x00 },
+ { 0x0136, 0x00 },
+ { 0x0137, 0x00 },
+ { 0x0200, 0x00 },
+ { 0x0201, 0x00 },
+ { 0x0202, 0x00 },
+ { 0x0203, 0x00 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x03 },
+ { 0x0220, 0x00 },
+ { 0x0222, 0x00 },
+ { 0x0223, 0x00 },
+ { 0x0224, 0x00 },
+ { 0x0225, 0x00 },
+ { 0x0226, 0x00 },
+ { 0x0227, 0x00 },
+ { 0x0230, 0x00 },
+ { 0x0232, 0x00 },
+ { 0x0233, 0x00 },
+ { 0x0234, 0x00 },
+ { 0x0235, 0x00 },
+ { 0x0236, 0x00 },
+ { 0x0237, 0x00 },
+ { 0x0300, 0x00 },
+ { 0x0301, 0x00 },
+ { 0x0302, 0x20 },
+ { 0x0303, 0x00 },
+ { 0x0304, 0x00 },
+ { 0x0305, 0x03 },
+ { 0x0320, 0x00 },
+ { 0x0322, 0x00 },
+ { 0x0323, 0x00 },
+ { 0x0324, 0x00 },
+ { 0x0325, 0x00 },
+ { 0x0326, 0x00 },
+ { 0x0327, 0x00 },
+ { 0x0330, 0x00 },
+ { 0x0332, 0x00 },
+ { 0x0333, 0x00 },
+ { 0x0334, 0x00 },
+ { 0x0335, 0x00 },
+ { 0x0336, 0x00 },
+ { 0x0337, 0x00 },
+ { 0x0400, 0x00 },
+ { 0x0401, 0x00 },
+ { 0x0402, 0x00 },
+ { 0x0403, 0x00 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x03 },
+ { 0x0420, 0x00 },
+ { 0x0422, 0x00 },
+ { 0x0423, 0x00 },
+ { 0x0424, 0x00 },
+ { 0x0425, 0x00 },
+ { 0x0426, 0x00 },
+ { 0x0427, 0x00 },
+ { 0x0430, 0x00 },
+ { 0x0432, 0x00 },
+ { 0x0433, 0x00 },
+ { 0x0434, 0x00 },
+ { 0x0435, 0x00 },
+ { 0x0436, 0x00 },
+ { 0x0437, 0x00 },
+ { 0x0f00, 0x00 },
+ { 0x0f01, 0x00 },
+ { 0x0f02, 0x20 },
+ { 0x0f03, 0x00 },
+ { 0x0f04, 0x00 },
+ { 0x0f05, 0x03 },
+ { 0x0f06, 0x00 },
+ { 0x0f07, 0x00 },
+ { 0x0f08, 0x00 },
+ { 0x0f09, 0x00 },
+ { 0x0f10, 0x00 },
+ { 0x0f11, 0x00 },
+ { 0x0f12, 0x00 },
+ { 0x0f13, 0x00 },
+ { 0x0f14, 0x00 },
+ { 0x0f15, 0x00 },
+ { 0x0f16, 0x00 },
+ { 0x0f17, 0x00 },
+ { 0x0f18, 0x00 },
+ { 0x0f19, 0x00 },
+ { 0x0f1a, 0x00 },
+ { 0x0f1b, 0x00 },
+ { 0x0f1c, 0x00 },
+ { 0x0f1d, 0x00 },
+ { 0x0f1e, 0x00 },
+ { 0x0f1f, 0x00 },
+ { 0x0f20, 0x00 },
+ { 0x0f22, 0x00 },
+ { 0x0f23, 0x00 },
+ { 0x0f24, 0x00 },
+ { 0x0f25, 0x00 },
+ { 0x0f26, 0x00 },
+ { 0x0f27, 0x00 },
+ { 0x0f30, 0x00 },
+ { 0x0f32, 0x00 },
+ { 0x0f33, 0x00 },
+ { 0x0f34, 0x00 },
+ { 0x0f35, 0x00 },
+ { 0x0f36, 0x00 },
+ { 0x0f37, 0x00 },
+ { 0x2012, 0x00 },
+ { 0x2013, 0x00 },
+ { 0x2014, 0x00 },
+ { 0x2015, 0x00 },
+ { 0x2016, 0x00 },
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x0c },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2201, 0xc7 },
+ { 0x2202, 0x0c },
+ { 0x2203, 0x22 },
+ { 0x2204, 0x04 },
+ { 0x2206, 0x00 },
+ { 0x2207, 0x00 },
+ { 0x2208, 0x00 },
+ { 0x2209, 0x00 },
+ { 0x220a, 0x00 },
+ { 0x220b, 0x00 },
+ { 0x220c, 0x00 },
+ { 0x220d, 0x04 },
+ { 0x220e, 0x00 },
+ { 0x220f, 0x00 },
+ { 0x2211, 0x01 },
+ { 0x2212, 0x00 },
+ { 0x2220, 0x00 },
+ { 0x2221, 0x00 },
+ { 0x2222, 0x00 },
+ { 0x2223, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f07, 0xcf },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f0b, 0x00 },
+ { 0x2f0c, 0x00 },
+ { 0x2f0d, 0x00 },
+ { 0x2f0e, 0x00 },
+ { 0x2f0f, 0x00 },
+ { 0x3122, 0x00 },
+ { 0x3123, 0x00 },
+ { 0x7303, 0x57 },
+ { 0x8383, 0x57 },
+ { 0x7308, 0x97 },
+ { 0x8388, 0x97 },
+ { 0x7309, 0x97 },
+ { 0x8389, 0x97 },
+ { 0x7312, 0x00 },
+ { 0x8392, 0x00 },
+ { 0x7313, 0x00 },
+ { 0x8393, 0x00 },
+ { 0x7319, 0x00 },
+ { 0x8399, 0x00 },
+ { 0x752008, 0xa807 },
+ { 0x752009, 0x1029 },
+ { 0x75200b, 0x7770 },
+ { 0x752011, 0x007a },
+ { 0x75201a, 0x8003 },
+ { 0x752045, 0x5289 },
+ { 0x752048, 0xd049 },
+ { 0x75204a, 0xa83b },
+ { 0x75206b, 0x5064 },
+ { 0x75206f, 0x058b },
+ { 0x752091, 0x0000 },
+};
+
+#endif /* __RT711_SDW_H__ */
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
new file mode 100644
index 000000000000..5dbe9b67703e
--- /dev/null
+++ b/sound/soc/codecs/rt711.c
@@ -0,0 +1,1335 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt711.c -- rt711 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/hda_verbs.h>
+#include <sound/jack.h>
+
+#include "rt711.h"
+
+static int rt711_index_write(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt711_index_read(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt711_index_update_bits(struct regmap *regmap, unsigned int nid,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp, orig;
+ int ret;
+
+ ret = rt711_index_read(regmap, nid, reg, &orig);
+ if (ret < 0)
+ return ret;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ return rt711_index_write(regmap, nid, reg, tmp);
+}
+
+static void rt711_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT711_FUNC_RESET, 0);
+ rt711_index_update_bits(regmap, RT711_VENDOR_REG,
+ RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET,
+ RT711_HIDDEN_REG_SW_RESET);
+}
+
+static int rt711_calibration(struct rt711_priv *rt711)
+{
+ unsigned int val, loop = 0;
+ struct device *dev;
+ struct regmap *regmap = rt711->regmap;
+ int ret = 0;
+
+ mutex_lock(&rt711->calibrate_mutex);
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ dev = regmap_get_device(regmap);
+
+ /* Calibration manual mode */
+ rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL,
+ 0xf, 0x0);
+
+ /* trigger */
+ rt711_index_update_bits(regmap, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER,
+ RT711_DAC_DC_CALI_TRIGGER);
+
+ /* wait for calibration process */
+ rt711_index_read(regmap, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+
+ while (val & RT711_DAC_DC_CALI_TRIGGER) {
+ if (loop >= 500) {
+ pr_err("%s, calibration time-out!\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ loop++;
+
+ usleep_range(10000, 11000);
+ rt711_index_read(regmap, RT711_VENDOR_CALI,
+ RT711_DAC_DC_CALI_CTL1, &val);
+ }
+
+ /* depop mode */
+ rt711_index_update_bits(regmap, RT711_VENDOR_REG,
+ RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL);
+
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ mutex_unlock(&rt711->calibrate_mutex);
+
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt711_button_detect(struct rt711_priv *rt711)
+{
+ unsigned int btn_type = 0, val80, val81;
+ int ret;
+
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_IRQ_FLAG_TABLE1, &val80);
+ if (ret < 0)
+ goto read_error;
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_IRQ_FLAG_TABLE2, &val81);
+ if (ret < 0)
+ goto read_error;
+
+ val80 &= 0x0381;
+ val81 &= 0xff00;
+
+ switch (val80) {
+ case 0x0200:
+ case 0x0100:
+ case 0x0080:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x0001:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+ switch (val81) {
+ case 0x8000:
+ case 0x4000:
+ case 0x2000:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ case 0x1000:
+ case 0x0800:
+ case 0x0400:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x0200:
+ case 0x0100:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ }
+read_error:
+ return btn_type;
+}
+
+static int rt711_headset_detect(struct rt711_priv *rt711)
+{
+ unsigned int buf, loop = 0;
+ int ret;
+ unsigned int jack_status = 0, reg;
+
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ while (loop < 500 &&
+ (buf & RT711_COMBOJACK_AUTO_DET_STATUS) == 0) {
+ loop++;
+
+ usleep_range(9000, 10000);
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL2, &buf);
+ if (ret < 0)
+ goto io_error;
+
+ reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT;
+ ret = regmap_read(rt711->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+ if ((jack_status & (1 << 31)) == 0)
+ goto remove_error;
+ }
+
+ if (loop >= 500)
+ goto to_error;
+
+ if (buf & RT711_COMBOJACK_AUTO_DET_TRS)
+ rt711->jack_type = SND_JACK_HEADPHONE;
+ else if ((buf & RT711_COMBOJACK_AUTO_DET_CTIA) ||
+ (buf & RT711_COMBOJACK_AUTO_DET_OMTP))
+ rt711->jack_type = SND_JACK_HEADSET;
+
+ return 0;
+
+to_error:
+ ret = -ETIMEDOUT;
+ pr_err_ratelimited("Time-out error in %s\n", __func__);
+ return ret;
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+remove_error:
+ pr_err_ratelimited("Jack removal in %s\n", __func__);
+ return -ENODEV;
+}
+
+static void rt711_jack_detect_handler(struct work_struct *work)
+{
+ struct rt711_priv *rt711 =
+ container_of(work, struct rt711_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ if (!rt711->hs_jack)
+ return;
+
+ if (!snd_soc_card_is_instantiated(rt711->component->card))
+ return;
+
+ if (pm_runtime_status_suspended(rt711->slave->dev.parent)) {
+ dev_dbg(&rt711->slave->dev,
+ "%s: parent device is pm_runtime_status_suspended, skipping jack detection\n",
+ __func__);
+ return;
+ }
+
+ reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT;
+ ret = regmap_read(rt711->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ /* jack in */
+ if (rt711->jack_type == 0) {
+ ret = rt711_headset_detect(rt711);
+ if (ret < 0)
+ return;
+ if (rt711->jack_type == SND_JACK_HEADSET)
+ btn_type = rt711_button_detect(rt711);
+ } else if (rt711->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt711_button_detect(rt711);
+ }
+ } else {
+ /* jack out */
+ rt711->jack_type = 0;
+ }
+
+ dev_dbg(&rt711->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt711->jack_type);
+ dev_dbg(&rt711->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt711_btn_check_handler(struct work_struct *work)
+{
+ struct rt711_priv *rt711 = container_of(work, struct rt711_priv,
+ jack_btn_check_work.work);
+ int btn_type = 0, ret;
+ unsigned int jack_status = 0, reg;
+
+ reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT;
+ ret = regmap_read(rt711->regmap, reg, &jack_status);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (jack_status & (1 << 31)) {
+ if (rt711->jack_type == SND_JACK_HEADSET) {
+ /* jack is already in, report button event */
+ btn_type = rt711_button_detect(rt711);
+ }
+ } else {
+ rt711->jack_type = 0;
+ }
+
+ /* cbj comparator */
+ ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL2, &reg);
+ if (ret < 0)
+ goto io_error;
+
+ if ((reg & 0xf0) == 0xf0)
+ btn_type = 0;
+
+ dev_dbg(&rt711->slave->dev,
+ "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt711->hs_jack, rt711->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt711_jack_init(struct rt711_priv *rt711)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component);
+
+ mutex_lock(&rt711->calibrate_mutex);
+ /* power on */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ if (rt711->hs_jack) {
+ /* unsolicited response & IRQ control */
+ regmap_write(rt711->regmap,
+ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82);
+ regmap_write(rt711->regmap,
+ RT711_SET_HP_UNSOLICITED_ENABLE, 0x81);
+ regmap_write(rt711->regmap,
+ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ 0x10, 0x2420);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ 0x19, 0x2e11);
+
+ switch (rt711->jd_src) {
+ case RT711_JD1:
+ /* default settings was already for JD1 */
+ break;
+ case RT711_JD2:
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_HP_JD_SEL_JD2);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ case RT711_JD2_100K:
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_100K_DECODE | RT711_JD2_1PORT_TYPE_DECODE |
+ RT711_HP_JD_SEL_JD2 | RT711_JD1_2PORT_TYPE_100K_DECODE,
+ RT711_JD2_2PORT_100K_DECODE_HP | RT711_JD2_1PORT_JD_HP |
+ RT711_HP_JD_SEL_JD2 | RT711_JD1_2PORT_JD_RESERVED);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ case RT711_JD2_1P8V_1PORT:
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_JD_MODE_SEL,
+ RT711_JD2_1_JD_MODE);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_1PORT_TYPE_DECODE |
+ RT711_HP_JD_SEL_JD2,
+ RT711_JD2_1PORT_JD_HP |
+ RT711_HP_JD_SEL_JD2);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_JD_CTL4, RT711_JD2_PAD_PULL_UP_MASK |
+ RT711_JD2_MODE_SEL_MASK,
+ RT711_JD2_PAD_PULL_UP |
+ RT711_JD2_MODE2_1P8V_1PORT);
+ rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+ break;
+ default:
+ dev_warn(rt711->component->dev, "%s: Wrong JD source\n", __func__);
+ break;
+ }
+
+ dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt711->jack_detect_work, msecs_to_jiffies(250));
+ } else {
+ regmap_write(rt711->regmap,
+ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt711->regmap,
+ RT711_SET_HP_UNSOLICITED_ENABLE, 0x00);
+ regmap_write(rt711->regmap,
+ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x00);
+
+ dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__);
+ }
+
+ /* power off */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ mutex_unlock(&rt711->calibrate_mutex);
+}
+
+static int rt711_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt711_jack_init(rt711);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h,
+ unsigned int addr_l, unsigned int val_h,
+ unsigned int *r_val, unsigned int *l_val)
+{
+ /* R Channel */
+ *r_val = (val_h << 8);
+ regmap_read(rt711->regmap, addr_l, r_val);
+
+ /* L Channel */
+ val_h |= 0x20;
+ *l_val = (val_h << 8);
+ regmap_read(rt711->regmap, addr_h, l_val);
+}
+
+/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
+static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
+ unsigned int read_ll, read_rl;
+ int i;
+
+ mutex_lock(&rt711->calibrate_mutex);
+
+ /* Can't use update bit function, so read the original value first */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT711_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ /* L Channel */
+ if (mc->invert) {
+ /* for mute/unmute */
+ val_ll = (mc->max - ucontrol->value.integer.value[0])
+ << RT711_MUTE_SFT;
+ /* keep gain */
+ read_ll = read_ll & 0x7f;
+ val_ll |= read_ll;
+ } else {
+ /* for gain */
+ val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
+ if (val_ll > mc->max)
+ val_ll = mc->max;
+ /* keep mute status */
+ read_ll = read_ll & (1 << RT711_MUTE_SFT);
+ val_ll |= read_ll;
+ }
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* R Channel */
+ if (mc->invert) {
+ /* for mute/unmute */
+ val_lr = (mc->max - ucontrol->value.integer.value[1])
+ << RT711_MUTE_SFT;
+ /* keep gain */
+ read_rl = read_rl & 0x7f;
+ val_lr |= read_rl;
+ } else {
+ /* for gain */
+ val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
+ if (val_lr > mc->max)
+ val_lr = mc->max;
+ /* keep mute status */
+ read_rl = read_rl & (1 << RT711_MUTE_SFT);
+ val_lr |= read_rl;
+ }
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << mc->shift) | (3 << 4);
+ regmap_write(rt711->regmap,
+ addr_h, (val_h << 8 | val_ll));
+ regmap_write(rt711->regmap,
+ addr_l, (val_h << 8 | val_ll));
+ } else {
+ /* Lch*/
+ val_h = (1 << mc->shift) | (1 << 5);
+ regmap_write(rt711->regmap,
+ addr_h, (val_h << 8 | val_ll));
+
+ /* Rch */
+ val_h = (1 << mc->shift) | (1 << 4);
+ regmap_write(rt711->regmap,
+ addr_l, (val_h << 8 | val_lr));
+ }
+ /* check result */
+ if (mc->shift == RT711_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt711_get_gain(rt711, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ mutex_unlock(&rt711->calibrate_mutex);
+ return 0;
+}
+
+static int rt711_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int addr_h, addr_l, val_h;
+ unsigned int read_ll, read_rl;
+
+ /* switch to get command */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT711_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (mc->invert) {
+ /* mute/unmute for switch controls */
+ read_ll = !((read_ll & 0x80) >> RT711_MUTE_SFT);
+ read_rl = !((read_rl & 0x80) >> RT711_MUTE_SFT);
+ } else {
+ /* for gain volume controls */
+ read_ll = read_ll & 0x7f;
+ read_rl = read_rl & 0x7f;
+ }
+ ucontrol->value.integer.value[0] = read_ll;
+ ucontrol->value.integer.value[1] = read_rl;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt711_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume",
+ RT711_SET_GAIN_DAC2_H, RT711_SET_GAIN_DAC2_L,
+ RT711_DIR_OUT_SFT, 0x57, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, out_vol_tlv),
+ SOC_DOUBLE_R_EXT("ADC 08 Capture Switch",
+ RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L,
+ RT711_DIR_IN_SFT, 1, 1,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT("ADC 09 Capture Switch",
+ RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L,
+ RT711_DIR_IN_SFT, 1, 1,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put),
+ SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume",
+ RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L,
+ RT711_DIR_IN_SFT, 0x3f, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume",
+ RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L,
+ RT711_DIR_IN_SFT, 0x3f, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("AMIC Volume",
+ RT711_SET_GAIN_AMIC_H, RT711_SET_GAIN_AMIC_L,
+ RT711_DIR_IN_SFT, 3, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume",
+ RT711_SET_GAIN_DMIC1_H, RT711_SET_GAIN_DMIC1_L,
+ RT711_DIR_IN_SFT, 3, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume",
+ RT711_SET_GAIN_DMIC2_H, RT711_SET_GAIN_DMIC2_L,
+ RT711_DIR_IN_SFT, 3, 0,
+ rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv),
+};
+
+static int rt711_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, val = 0, nid;
+ int ret;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT711_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT711_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* vid = 0xf01 */
+ reg = RT711_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt711->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt711_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, reg, nid;
+ int ret;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ nid = RT711_MIXER_IN1;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ nid = RT711_MIXER_IN2;
+ else
+ return -EINVAL;
+
+ /* Verb ID = 0x701h */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ reg = RT711_VERB_SET_CONNECT_SEL | nid;
+ ret = regmap_read(rt711->regmap, reg, &val2);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ reg = RT711_VERB_SET_CONNECT_SEL | nid;
+ regmap_write(rt711->regmap, reg, val);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt711_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum,
+ rt711_mux_get, rt711_mux_put);
+
+static const struct snd_kcontrol_new rt711_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum,
+ rt711_mux_get, rt711_mux_put);
+
+static int rt711_dac_surround_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val_h = (1 << RT711_DIR_OUT_SFT) | (0x3 << 4);
+ unsigned int val_l;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_DAC2, 0x10);
+
+ val_l = 0x00;
+ regmap_write(rt711->regmap,
+ RT711_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val_l = (1 << RT711_MUTE_SFT);
+ regmap_write(rt711->regmap,
+ RT711_SET_GAIN_HP_H, (val_h << 8 | val_l));
+ usleep_range(50000, 55000);
+
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_DAC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_adc_09_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC1, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC1, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int rt711_adc_08_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC2, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt711->regmap,
+ RT711_SET_STREAMID_ADC2, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt711_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_dac_surround_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_adc_09_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0,
+ rt711_adc_08_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt711_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt711_audio_map[] = {
+ {"DAC Surround", NULL, "DP3RX"},
+ {"DP2TX", NULL, "ADC 09"},
+ {"DP4TX", NULL, "ADC 08"},
+
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 22 Mux", "DMIC", "DMIC1"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "DMIC", "DMIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+
+ {"HP", NULL, "DAC Surround"},
+};
+
+static int rt711_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ mutex_lock(&rt711->calibrate_mutex);
+ regmap_write(rt711->regmap,
+ RT711_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D3);
+ mutex_unlock(&rt711->calibrate_mutex);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src",
+ &rt711->jd_src);
+
+ return 0;
+}
+
+static int rt711_probe(struct snd_soc_component *component)
+{
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt711_parse_dt(rt711, &rt711->slave->dev);
+ rt711->component = component;
+
+ if (!rt711->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
+ .probe = rt711_probe,
+ .set_bias_level = rt711_set_bias_level,
+ .controls = rt711_snd_controls,
+ .num_controls = ARRAY_SIZE(rt711_snd_controls),
+ .dapm_widgets = rt711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt711_dapm_widgets),
+ .dapm_routes = rt711_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt711_audio_map),
+ .set_jack = rt711_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt711_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int val = 0;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt711->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_config.num = 3;
+ } else {
+ if (dai->id == RT711_AIF1)
+ port_config.num = 4;
+ else if (dai->id == RT711_AIF2)
+ port_config.num = 2;
+ else
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt711->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 8:
+ break;
+ case 16:
+ val |= (0x1 << 4);
+ break;
+ case 20:
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ val |= (0x3 << 4);
+ break;
+ case 32:
+ val |= (0x4 << 4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 48Khz */
+ regmap_write(rt711->regmap, RT711_DAC_FORMAT_H, val);
+ regmap_write(rt711->regmap, RT711_ADC1_FORMAT_H, val);
+ regmap_write(rt711->regmap, RT711_ADC2_FORMAT_H, val);
+
+ return retval;
+}
+
+static int rt711_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt711->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt711->slave, sdw_stream);
+ return 0;
+}
+
+#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt711_ops = {
+ .hw_params = rt711_pcm_hw_params,
+ .hw_free = rt711_pcm_hw_free,
+ .set_stream = rt711_set_sdw_stream,
+ .shutdown = rt711_shutdown,
+};
+
+static struct snd_soc_dai_driver rt711_dai[] = {
+ {
+ .name = "rt711-aif1",
+ .id = RT711_AIF1,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_ops,
+ },
+ {
+ .name = "rt711-aif2",
+ .id = RT711_AIF2,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT711_STEREO_RATES,
+ .formats = RT711_FORMATS,
+ },
+ .ops = &rt711_ops,
+ }
+};
+
+/* Bus clock frequency */
+#define RT711_CLK_FREQ_9600000HZ 9600000
+#define RT711_CLK_FREQ_12000000HZ 12000000
+#define RT711_CLK_FREQ_6000000HZ 6000000
+#define RT711_CLK_FREQ_4800000HZ 4800000
+#define RT711_CLK_FREQ_2400000HZ 2400000
+#define RT711_CLK_FREQ_12288000HZ 12288000
+
+int rt711_clock_config(struct device *dev)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt711->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT711_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT711_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT711_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT711_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT711_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT711_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt711->regmap, 0xe0, value);
+ regmap_write(rt711->regmap, 0xf0, value);
+
+ dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq);
+
+ return 0;
+}
+
+static void rt711_calibration_work(struct work_struct *work)
+{
+ struct rt711_priv *rt711 =
+ container_of(work, struct rt711_priv, calibration_work);
+
+ rt711_calibration(rt711);
+}
+
+int rt711_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt711_priv *rt711;
+ int ret;
+
+ rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL);
+ if (!rt711)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt711);
+ rt711->slave = slave;
+ rt711->sdw_regmap = sdw_regmap;
+ rt711->regmap = regmap;
+
+ regcache_cache_only(rt711->regmap, true);
+
+ mutex_init(&rt711->calibrate_mutex);
+ mutex_init(&rt711->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_btn_check_handler);
+ INIT_WORK(&rt711->calibration_work, rt711_calibration_work);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt711->hw_init = false;
+ rt711->first_hw_init = false;
+
+ /* JD source uses JD2 in default */
+ rt711->jd_src = RT711_JD2;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt711,
+ rt711_dai,
+ ARRAY_SIZE(rt711_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+int rt711_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt711_priv *rt711 = dev_get_drvdata(dev);
+
+ rt711->disable_irq = false;
+
+ if (rt711->hw_init)
+ return 0;
+
+ regcache_cache_only(rt711->regmap, false);
+ if (rt711->first_hw_init)
+ regcache_cache_bypass(rt711->regmap, true);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt711->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt711_reset(rt711->regmap);
+
+ /* power on */
+ regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* Set Pin Widget */
+ regmap_write(rt711->regmap, RT711_SET_PIN_MIC2, 0x25);
+ regmap_write(rt711->regmap, RT711_SET_PIN_HP, 0xc0);
+ regmap_write(rt711->regmap, RT711_SET_PIN_DMIC1, 0x20);
+ regmap_write(rt711->regmap, RT711_SET_PIN_DMIC2, 0x20);
+ regmap_write(rt711->regmap, RT711_SET_PIN_LINE1, 0x20);
+ regmap_write(rt711->regmap, RT711_SET_PIN_LINE2, 0x20);
+
+ /* Mute HP/ADC1/ADC2 */
+ regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0xa080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0x9080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x6080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x5080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x6080);
+ regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x5080);
+
+ /* Set Configuration Default */
+ regmap_write(rt711->regmap, 0x4f12, 0x91);
+ regmap_write(rt711->regmap, 0x4e12, 0xd6);
+ regmap_write(rt711->regmap, 0x4d12, 0x11);
+ regmap_write(rt711->regmap, 0x4c12, 0x20);
+ regmap_write(rt711->regmap, 0x4f13, 0x91);
+ regmap_write(rt711->regmap, 0x4e13, 0xd6);
+ regmap_write(rt711->regmap, 0x4d13, 0x11);
+ regmap_write(rt711->regmap, 0x4c13, 0x21);
+ regmap_write(rt711->regmap, 0x4c21, 0xf0);
+ regmap_write(rt711->regmap, 0x4d21, 0x11);
+ regmap_write(rt711->regmap, 0x4e21, 0x11);
+ regmap_write(rt711->regmap, 0x4f21, 0x01);
+
+ /* Data port arrangement */
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_TX_RX_MUX_CTL, 0x0154);
+
+ /* Set index */
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_DIGITAL_MISC_CTRL4, 0x201b);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL1, 0x5089);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_VREFOUT_CTL, 0x5064);
+ rt711_index_write(rt711->regmap, RT711_VENDOR_REG,
+ RT711_INLINE_CMD_CTL, 0xd249);
+
+ /* Finish Initial Settings, set power to D3 */
+ regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ if (rt711->first_hw_init)
+ rt711_calibration(rt711);
+ else
+ schedule_work(&rt711->calibration_work);
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt711->hs_jack)
+ rt711_jack_init(rt711);
+
+ if (rt711->first_hw_init) {
+ regcache_cache_bypass(rt711->regmap, false);
+ regcache_mark_dirty(rt711->regmap);
+ } else
+ rt711->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt711->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT711 SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h
new file mode 100644
index 000000000000..491e357191f9
--- /dev/null
+++ b/sound/soc/codecs/rt711.h
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt711.h -- RT711 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_H__
+#define __RT711_H__
+
+extern const struct dev_pm_ops rt711_runtime_pm;
+
+struct rt711_priv {
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct work_struct calibration_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ int jack_type, jd_src;
+ struct mutex disable_irq_lock; /* imp-def irq lock protection */
+ bool disable_irq;
+};
+
+/* NID */
+#define RT711_AUDIO_FUNCTION_GROUP 0x01
+#define RT711_DAC_OUT2 0x03
+#define RT711_ADC_IN1 0x09
+#define RT711_ADC_IN2 0x08
+#define RT711_DMIC1 0x12
+#define RT711_DMIC2 0x13
+#define RT711_MIC2 0x19
+#define RT711_LINE1 0x1a
+#define RT711_LINE2 0x1b
+#define RT711_BEEP 0x1d
+#define RT711_VENDOR_REG 0x20
+#define RT711_HP_OUT 0x21
+#define RT711_MIXER_IN1 0x22
+#define RT711_MIXER_IN2 0x23
+#define RT711_INLINE_CMD 0x55
+#define RT711_VENDOR_CALI 0x58
+#define RT711_VENDOR_IMS_DRE 0x5b
+
+/* Index (NID:20h) */
+#define RT711_DAC_DC_CALI_CTL1 0x00
+#define RT711_JD_CTL1 0x08
+#define RT711_JD_CTL2 0x09
+#define RT711_JD_CTL4 0x0b
+#define RT711_CC_DET1 0x11
+#define RT711_PARA_VERB_CTL 0x1a
+#define RT711_COMBO_JACK_AUTO_CTL1 0x45
+#define RT711_COMBO_JACK_AUTO_CTL2 0x46
+#define RT711_INLINE_CMD_CTL 0x48
+#define RT711_DIGITAL_MISC_CTRL4 0x4a
+#define RT711_VREFOUT_CTL 0x6b
+#define RT711_FSM_CTL 0x6f
+#define RT711_IRQ_FLAG_TABLE1 0x80
+#define RT711_IRQ_FLAG_TABLE2 0x81
+#define RT711_IRQ_FLAG_TABLE3 0x82
+#define RT711_TX_RX_MUX_CTL 0x91
+
+/* Index (NID:5bh) */
+#define RT711_IMS_DIGITAL_CTL1 0x00
+#define RT711_HP_IMS_RESULT_L 0x20
+#define RT711_HP_IMS_RESULT_R 0x21
+
+/* Verb */
+#define RT711_VERB_SET_CONNECT_SEL 0x3100
+#define RT711_VERB_SET_EAPD_BTLENABLE 0x3c00
+#define RT711_VERB_GET_CONNECT_SEL 0xb100
+#define RT711_VERB_SET_POWER_STATE 0x3500
+#define RT711_VERB_SET_CHANNEL_STREAMID 0x3600
+#define RT711_VERB_SET_PIN_WIDGET_CONTROL 0x3700
+#define RT711_VERB_SET_UNSOLICITED_ENABLE 0x3800
+#define RT711_SET_AMP_GAIN_MUTE_H 0x7300
+#define RT711_SET_AMP_GAIN_MUTE_L 0x8380
+#define RT711_VERB_GET_POWER_STATE 0xb500
+#define RT711_VERB_GET_CHANNEL_STREAMID 0xb600
+#define RT711_VERB_GET_PIN_SENSE 0xb900
+#define RT711_FUNC_RESET 0xff01
+
+#define RT711_READ_HDA_3 0x2012
+#define RT711_READ_HDA_2 0x2013
+#define RT711_READ_HDA_1 0x2014
+#define RT711_READ_HDA_0 0x2015
+#define RT711_PRIV_INDEX_W_H 0x7500
+#define RT711_PRIV_INDEX_W_L 0x8580
+#define RT711_PRIV_DATA_W_H 0x7400
+#define RT711_PRIV_DATA_W_L 0x8480
+#define RT711_PRIV_INDEX_R_H 0x9d00
+#define RT711_PRIV_INDEX_R_L 0xad80
+#define RT711_PRIV_DATA_R_H 0x9c00
+#define RT711_PRIV_DATA_R_L 0xac80
+#define RT711_DAC_FORMAT_H 0x7203
+#define RT711_DAC_FORMAT_L 0x8283
+#define RT711_ADC1_FORMAT_H 0x7209
+#define RT711_ADC1_FORMAT_L 0x8289
+#define RT711_ADC2_FORMAT_H 0x7208
+#define RT711_ADC2_FORMAT_L 0x8288
+
+#define RT711_SET_AUDIO_POWER_STATE\
+ (RT711_VERB_SET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP)
+#define RT711_GET_AUDIO_POWER_STATE\
+ (RT711_VERB_GET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP)
+#define RT711_SET_PIN_DMIC1\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC1)
+#define RT711_SET_PIN_DMIC2\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC2)
+#define RT711_SET_PIN_HP\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_HP_OUT)
+#define RT711_SET_PIN_MIC2\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_MIC2)
+#define RT711_SET_PIN_LINE1\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE1)
+#define RT711_SET_PIN_LINE2\
+ (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE2)
+#define RT711_SET_MIC2_UNSOLICITED_ENABLE\
+ (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_MIC2)
+#define RT711_SET_HP_UNSOLICITED_ENABLE\
+ (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_HP_OUT)
+#define RT711_SET_INLINE_UNSOLICITED_ENABLE\
+ (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_INLINE_CMD)
+#define RT711_SET_STREAMID_DAC2\
+ (RT711_VERB_SET_CHANNEL_STREAMID | RT711_DAC_OUT2)
+#define RT711_SET_STREAMID_ADC1\
+ (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN1)
+#define RT711_SET_STREAMID_ADC2\
+ (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN2)
+#define RT711_GET_STREAMID_DAC2\
+ (RT711_VERB_GET_CHANNEL_STREAMID | RT711_DAC_OUT2)
+#define RT711_GET_STREAMID_ADC1\
+ (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN1)
+#define RT711_GET_STREAMID_ADC2\
+ (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN2)
+#define RT711_SET_GAIN_DAC2_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_DAC_OUT2)
+#define RT711_SET_GAIN_DAC2_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_DAC_OUT2)
+#define RT711_SET_GAIN_ADC1_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN1)
+#define RT711_SET_GAIN_ADC1_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN1)
+#define RT711_SET_GAIN_ADC2_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN2)
+#define RT711_SET_GAIN_ADC2_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN2)
+#define RT711_SET_GAIN_AMIC_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_MIC2)
+#define RT711_SET_GAIN_AMIC_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_MIC2)
+#define RT711_SET_GAIN_DMIC1_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC1)
+#define RT711_SET_GAIN_DMIC1_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC1)
+#define RT711_SET_GAIN_DMIC2_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC2)
+#define RT711_SET_GAIN_DMIC2_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC2)
+#define RT711_SET_GAIN_HP_L\
+ (RT711_SET_AMP_GAIN_MUTE_L | RT711_HP_OUT)
+#define RT711_SET_GAIN_HP_H\
+ (RT711_SET_AMP_GAIN_MUTE_H | RT711_HP_OUT)
+
+/* DAC DC offset calibration control-1 (0x00)(NID:20h) */
+#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15)
+
+/* jack detect control 1 (0x08)(NID:20h) */
+#define RT711_JD2_DIGITAL_JD_MODE_SEL (0x1 << 1)
+#define RT711_JD2_1_JD_MODE (0x0 << 1)
+#define RT711_JD2_2_JD_MODE (0x1 << 1)
+
+/* jack detect control 2 (0x09)(NID:20h) */
+#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
+#define RT711_JD2_2PORT_100K_DECODE (0x1 << 12)
+#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12)
+#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
+#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
+#define RT711_JD2_1PORT_TYPE_DECODE (0x3 << 10)
+#define RT711_JD2_1PORT_JD_LINE2 (0x0 << 10)
+#define RT711_JD2_1PORT_JD_HP (0x1 << 10)
+#define RT711_JD2_1PORT_JD_LINE1 (0x2 << 10)
+#define RT711_JD1_2PORT_TYPE_100K_DECODE (0x1 << 0)
+#define RT711_JD1_2PORT_JD_RESERVED (0x0 << 0)
+#define RT711_JD1_2PORT_JD_LINE1 (0x1 << 0)
+
+/* jack detect control 4 (0x0b)(NID:20h) */
+#define RT711_JD2_PAD_PULL_UP_MASK (0x1 << 3)
+#define RT711_JD2_PAD_NOT_PULL_UP (0x0 << 3)
+#define RT711_JD2_PAD_PULL_UP (0x1 << 3)
+#define RT711_JD2_MODE_SEL_MASK (0x3 << 0)
+#define RT711_JD2_MODE0_2PORT (0x0 << 0)
+#define RT711_JD2_MODE1_3P3V_1PORT (0x1 << 0)
+#define RT711_JD2_MODE2_1P8V_1PORT (0x2 << 0)
+
+/* CC DET1 (0x11)(NID:20h) */
+#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
+#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
+
+/* Parameter & Verb control (0x1a)(NID:20h) */
+#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* FSM control (0x6f)(NID:20h) */
+#define RT711_CALI_CTL (0x0 << 0)
+#define RT711_COMBOJACK_CTL (0x1 << 0)
+#define RT711_IMS_CTL (0x2 << 0)
+#define RT711_DEPOP_CTL (0x3 << 0)
+
+/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */
+#define RT711_TRIGGER_IMS (0x1 << 15)
+#define RT711_IMS_EN (0x1 << 6)
+
+#define RT711_EAPD_HIGH 0x2
+#define RT711_EAPD_LOW 0x0
+#define RT711_MUTE_SFT 7
+/* set input/output mapping to payload[14][15] separately */
+#define RT711_DIR_IN_SFT 6
+#define RT711_DIR_OUT_SFT 7
+
+enum {
+ RT711_AIF1,
+ RT711_AIF2,
+ RT711_AIFS,
+};
+
+enum rt711_jd_src {
+ RT711_JD_NULL,
+ RT711_JD1,
+ RT711_JD2,
+ RT711_JD2_100K,
+ RT711_JD2_1P8V_1PORT
+};
+
+int rt711_io_init(struct device *dev, struct sdw_slave *slave);
+int rt711_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+int rt711_jack_detect(struct rt711_priv *rt711, bool *hp, bool *mic);
+int rt711_clock_config(struct device *dev);
+#endif /* __RT711_H__ */
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
new file mode 100644
index 000000000000..2928649e80e4
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -0,0 +1,984 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-dmic.c -- rt712 SDCA DMIC ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-dmic.h"
+
+static bool rt712_sdca_dmic_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x201f:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2230 ... 0x2232:
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f52:
+ case 0x2f58 ... 0x2f59:
+ case 0x3201:
+ case 0x320c:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x202d ... 0x202f:
+ case 0x2230:
+ case 0x2f01:
+ case 0x2f35:
+ case 0x320c:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x200008e:
+ case 0x5300000 ... 0x530000e:
+ case 0x5400000 ... 0x540000e:
+ case 0x5600000 ... 0x5600008:
+ case 0x5700000 ... 0x570000d:
+ case 0x5800000 ... 0x5800021:
+ case 0x5900000 ... 0x5900028:
+ case 0x5a00000 ... 0x5a00009:
+ case 0x5b00000 ... 0x5b00051:
+ case 0x5c00000 ... 0x5c0009a:
+ case 0x5d00000 ... 0x5d00009:
+ case 0x5f00000 ... 0x5f00030:
+ case 0x6100000 ... 0x6100068:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000024:
+ case 0x2000046:
+ case 0x200008a:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt712_sdca_dmic_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt712_sdca_dmic_readable_register,
+ .volatile_reg = rt712_sdca_dmic_volatile_register,
+ .max_register = 0x40981300,
+ .reg_defaults = rt712_sdca_dmic_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_dmic_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt712_sdca_dmic_mbq_readable_register,
+ .volatile_reg = rt712_sdca_dmic_mbq_volatile_register,
+ .max_register = 0x40800f14,
+ .reg_defaults = rt712_sdca_dmic_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt712_sdca_dmic_index_write(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_dmic_index_read(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_dmic_index_update_bits(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt712_sdca_dmic_index_read(rt712, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt712_sdca_dmic_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+ if (rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, true);
+ regcache_cache_bypass(rt712->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0B_11_PDE_FLOAT_CTL, 0x1111);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC1_2_PDE_FLOAT_CTL, 0x1111);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_I2S_IN_OUT_PDE_FLOAT_CTL, 0x1155);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_ENT_FLOAT_CTL, 0x2626);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC_ENT_FLOAT_CTL, 0x1e19);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC_VOL_CH_FLOAT_CTL2, 0x0304);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_CONFIG_CTL0, 0x0050);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_dmic_index_write(rt712, RT712_ULTRA_SOUND_DET,
+ RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+ regmap_write(rt712->regmap, 0x2f52, 0x00);
+
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, false);
+ regcache_mark_dirty(rt712->regmap);
+ regcache_cache_bypass(rt712->mbq_regmap, false);
+ regcache_mark_dirty(rt712->mbq_regmap);
+ } else
+ rt712->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt712->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * 0x0a00;
+ else { /* ADC gain */
+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt712->slave->dev, "%s: 0x%08x can't be set\n",
+ __func__, p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_dmic_priv *rt712)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) {
+ ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00;
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E,
+ RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt712_sdca_set_fu1e_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt712_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt712_sdca_fu_info, \
+ .get = rt712_sdca_dmic_fu1e_capture_get, \
+ .put = rt712_sdca_dmic_fu1e_capture_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt712_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = {
+ RT712_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4),
+ RT712_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv),
+ RT712_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, mic_vol_tlv),
+};
+
+static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = (0x7 << mask_sft) & val2;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt712_sdca_dmic_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft,
+ val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc25_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc26_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt712_adc25_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc26_mux =
+ SOC_DAPM_ENUM_EXT("ADC 26 Mux", rt712_adc26_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static int rt712_sdca_dmic_fu1e_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu1e_dapm_mute = false;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu1e_dapm_mute = true;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_dmic_pde11_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc25_mux),
+ SND_SOC_DAPM_MUX("ADC 26 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc26_mux),
+
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+ {"DP2TX", NULL, "FU 1E"},
+
+ {"FU 1E", NULL, "PDE 11"},
+ {"FU 1E", NULL, "ADC 25 Mux"},
+ {"FU 1E", NULL, "ADC 26 Mux"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 26 Mux", "DMIC1", "DMIC1"},
+ {"ADC 26 Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->component = component;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = {
+ .probe = rt712_sdca_dmic_probe,
+ .controls = rt712_sdca_dmic_snd_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls),
+ .dapm_widgets = rt712_sdca_dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets),
+ .dapm_routes = rt712_sdca_dmic_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map),
+ .endianness = 1,
+};
+
+static int rt712_sdca_dmic_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt712_sdca_dmic_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, num_channels;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = SDW_DATA_DIR_TX;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt712->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 4) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT712_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT712_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT712_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT712_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT712_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT712_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt712->slave, sdw_stream);
+ return 0;
+}
+
+#define RT712_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_dmic_ops = {
+ .hw_params = rt712_sdca_dmic_hw_params,
+ .hw_free = rt712_sdca_dmic_hw_free,
+ .set_stream = rt712_sdca_dmic_set_sdw_stream,
+ .shutdown = rt712_sdca_dmic_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+ {
+ .name = "rt712-sdca-dmic-aif1",
+ .id = RT712_AIF1,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_dmic_ops,
+ },
+};
+
+static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712;
+ int ret;
+
+ rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+ if (!rt712)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt712);
+ rt712->slave = slave;
+ rt712->regmap = regmap;
+ rt712->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt712->hw_init = false;
+ rt712->first_hw_init = false;
+ rt712->fu1e_dapm_mute = true;
+ rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
+ rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712_dmic,
+ rt712_sdca_dmic_dai,
+ ARRAY_SIZE(rt712_sdca_dmic_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+
+static int rt712_sdca_dmic_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt712->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt712_sdca_dmic_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_dmic_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = 0;
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_dmic_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1712, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1713, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1716, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1717, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_dmic_id);
+
+static int rt712_sdca_dmic_dev_suspend(struct device *dev)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+ if (!rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_dev_system_suspend(struct device *dev)
+{
+ struct rt712_sdca_dmic_priv *rt712_sdca = dev_get_drvdata(dev);
+
+ if (!rt712_sdca->hw_init)
+ return 0;
+
+ return rt712_sdca_dmic_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int rt712_sdca_dmic_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n",
+ __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_sync(rt712->regmap);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_sync(rt712->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_dmic_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume)
+ RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL)
+};
+
+
+static const struct sdw_slave_ops rt712_sdca_dmic_slave_ops = {
+ .read_prop = rt712_sdca_dmic_read_prop,
+ .update_status = rt712_sdca_dmic_update_status,
+};
+
+static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_dmic_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt712_sdca_dmic_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt712_sdca_dmic_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static struct sdw_driver rt712_sdca_dmic_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca-dmic",
+ .pm = pm_ptr(&rt712_sdca_dmic_pm),
+ },
+ .probe = rt712_sdca_dmic_sdw_probe,
+ .remove = rt712_sdca_dmic_sdw_remove,
+ .ops = &rt712_sdca_dmic_slave_ops,
+ .id_table = rt712_sdca_dmic_id,
+};
+module_sdw_driver(rt712_sdca_dmic_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA DMIC SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h
new file mode 100644
index 000000000000..110154e74efe
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-dmic.h -- RT712 SDCA DMIC ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_DMIC_H__
+#define __RT712_SDW_DMIC_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+struct rt712_sdca_dmic_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+};
+
+struct rt712_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* SDCA (Channel) */
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+
+static const struct reg_default rt712_sdca_dmic_reg_defaults[] = {
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x00 },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x01 },
+ { 0x2f35, 0x02 },
+ { 0x2f36, 0xcf },
+ { 0x2f52, 0x08 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x3201, 0x01 },
+ { 0x320c, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+};
+
+static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = {
+ { 0x0590001e, 0x0020 },
+ { 0x06100000, 0x0010 },
+ { 0x06100006, 0x0055 },
+ { 0x06100010, 0x2630 },
+ { 0x06100011, 0x152f },
+ { 0x06100013, 0x0102 },
+ { 0x06100015, 0x2219 },
+ { 0x06100018, 0x0102 },
+ { 0x06100026, 0x2c29 },
+ { 0x06100027, 0x2d2b },
+ { 0x0610002b, 0x2a32 },
+ { 0x0610002f, 0x3355 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 },
+};
+
+#endif /* __RT712_SDW_DMIC_H__ */
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
new file mode 100644
index 000000000000..ea07131edfa2
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-sdw.h"
+
+static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x201f:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2230 ... 0x2232:
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ case 0x3201:
+ case 0x320c:
+ case 0x3301 ... 0x3303:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x202d ... 0x202f:
+ case 0x2230:
+ case 0x2f01:
+ case 0x2f35:
+ case 0x320c:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x200008e:
+ case 0x5300000 ... 0x530000e:
+ case 0x5400000 ... 0x540000e:
+ case 0x5600000 ... 0x5600008:
+ case 0x5700000 ... 0x570000d:
+ case 0x5800000 ... 0x5800021:
+ case 0x5900000 ... 0x5900028:
+ case 0x5a00000 ... 0x5a00009:
+ case 0x5b00000 ... 0x5b00051:
+ case 0x5c00000 ... 0x5c0009a:
+ case 0x5d00000 ... 0x5d00009:
+ case 0x5f00000 ... 0x5f00030:
+ case 0x6100000 ... 0x61000f1:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000020:
+ case 0x2000024:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x200008a:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt712_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt712_sdca_readable_register,
+ .volatile_reg = rt712_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt712_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt712_sdca_mbq_readable_register,
+ .volatile_reg = rt712_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt712_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt712_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt712->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt712->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt712_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt712_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ sdw_slave_read_prop(slave);
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(8) | BIT(4); /* BITMAP: 100010000 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 1380;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt712_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x, sdca_cascade=%x", __func__,
+ status->control_port, status->sdca_cascade);
+
+ if (cancel_delayed_work_sync(&rt712->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt712->scp_sdca_stat2)
+ scp_sdca_stat2 = rt712->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt712->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt712->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt712->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt712->scp_sdca_stat2 |= scp_sdca_stat2;
+
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt712->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_detect_work, msecs_to_jiffies(30));
+
+ mutex_unlock(&rt712->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt712->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt712_sdca_slave_ops = {
+ .read_prop = rt712_sdca_read_prop,
+ .interrupt_callback = rt712_sdca_interrupt_callback,
+ .update_status = rt712_sdca_update_status,
+};
+
+static int rt712_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt712_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (rt712->hw_init) {
+ cancel_delayed_work_sync(&rt712->jack_detect_work);
+ cancel_delayed_work_sync(&rt712->jack_btn_check_work);
+ }
+
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt712->calibrate_mutex);
+ mutex_destroy(&rt712->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x712, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x713, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x716, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x717, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_id);
+
+static int rt712_sdca_dev_suspend(struct device *dev)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+
+ if (!rt712->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt712->jack_detect_work);
+ cancel_delayed_work_sync(&rt712->jack_btn_check_work);
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt712_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt712_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt712_sdca->disable_irq_lock);
+ rt712_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt712_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt712_sdca_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int rt712_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt712->disable_irq_lock);
+ if (rt712->disable_irq == true) {
+
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt712->disable_irq = false;
+ }
+ mutex_unlock(&rt712->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_sync(rt712->regmap);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_sync(rt712->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt712_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca",
+ .pm = pm_ptr(&rt712_sdca_pm),
+ },
+ .probe = rt712_sdca_sdw_probe,
+ .remove = rt712_sdca_sdw_remove,
+ .ops = &rt712_sdca_slave_ops,
+ .id_table = rt712_sdca_id,
+};
+module_sdw_driver(rt712_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h
new file mode 100644
index 000000000000..99fd2d67f04d
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-sdw.h -- RT712 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_H__
+#define __RT712_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt712_sdca_reg_defaults[] = {
+
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+static const struct reg_default rt712_sdca_mbq_defaults[] = {
+ { 0x2000004, 0xaa01 },
+ { 0x200000e, 0x21e0 },
+ { 0x200004a, 0x8830 },
+ { 0x2000067, 0xf100 },
+ { 0x5800000, 0x1893 },
+ { 0x5b00000, 0x0407 },
+ { 0x5b00005, 0x0000 },
+ { 0x5b00029, 0x3fff },
+ { 0x5b0002a, 0xf000 },
+ { 0x6100000, 0x04e4 },
+ { 0x610000e, 0x0007 },
+ { 0x6100045, 0x0860 },
+ { 0x6100046, 0x0029 },
+ { 0x6100053, 0x3fff },
+ { 0x6100055, 0x0000 },
+ { 0x6100060, 0x0000 },
+ { 0x6100064, 0x8000 },
+ { 0x6100065, 0x0000 },
+ { 0x6100067, 0xff12 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+};
+
+#endif /* __RT712_SDW_H__ */
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
new file mode 100644
index 000000000000..4796fce084ff
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -0,0 +1,1926 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdca.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+
+static int rt712_sdca_index_write(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_index_read(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt712_sdca_index_read(rt712, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt712_sdca_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
+{
+ unsigned int val, loop_rc = 0, loop_dc = 0;
+ struct device *dev;
+ struct regmap *regmap = rt712->regmap;
+ int chk_cnt = 100;
+ int ret = 0;
+
+ mutex_lock(&rt712->calibrate_mutex);
+ dev = regmap_get_device(regmap);
+
+ /* Set HP-JD source from JD1 */
+ if (rt712->version_id == RT712_VA)
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a);
+
+ /* FSM switch to calibration manual mode */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100);
+
+ /* Calibration setting */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0x7883);
+
+ /* W1C Trigger DC calibration (HP & Class-D) */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0xf893);
+
+ /* wait for calibration process */
+ rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+
+ for (loop_dc = 0; loop_dc < chk_cnt &&
+ (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) {
+ usleep_range(10000, 11000);
+ ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_dc == chk_cnt)
+ dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+ if (loop_dc == chk_cnt || loop_rc == chk_cnt)
+ ret = -ETIMEDOUT;
+
+_cali_fail_:
+ /* Enable Rldet in FSM */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4500);
+
+ /* Sensing Lch+Rch */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL1, 0x040f);
+
+ /* Sine gen path control */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL5, 0x0000);
+
+ /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_DIGITAL_MISC_CTRL4, 0x0010);
+
+ mutex_unlock(&rt712->calibrate_mutex);
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0),
+ &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt712->regmap,
+ RT712_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0) {
+ /* set owner to device */
+ if (rt712->version_id == RT712_VA)
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+ else
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+ }
+
+ return btn_type;
+}
+
+static int rt712_sdca_headset_detect(struct rt712_sdca_priv *rt712)
+{
+ unsigned int det_mode;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt712->jack_type = 0;
+ break;
+ case 0x03:
+ rt712->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt712->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0),
+ det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt712->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt712_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt712_sdca_priv *rt712 =
+ container_of(work, struct rt712_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt712->hs_jack)
+ return;
+
+ if (!rt712->component->card || !rt712->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt712_sdca_headset_detect(rt712);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt712_sdca_button_detect(rt712);
+
+ if (rt712->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt712->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt712->jack_type);
+ dev_dbg(&rt712->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt712->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt712_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt712_sdca_priv *rt712 =
+ container_of(work, struct rt712_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt712->regmap,
+ RT712_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+ } else {
+ rt712->jack_type = 0;
+ }
+
+ dev_dbg(&rt712->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712)
+{
+ mutex_lock(&rt712->calibrate_mutex);
+
+ if (rt712->hs_jack) {
+ /* Enable HID1 event & set button RTC mode */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL5, 0xfff0);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL0, 0x1100, 0x1100);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL7, 0xf000, 0x0000);
+
+ /* detected_mode_change_event_en & hid1_push_button_event_en */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0c00, 0x0c00);
+ /* ge_inbox_en */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL2, 0x0020, 0x0000);
+
+ switch (rt712->jd_src) {
+ case RT712_JD1:
+ /* Set HP-JD source from JD1, VB uses JD1 in default */
+ if (rt712->version_id == RT712_VA)
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a);
+ break;
+ default:
+ dev_warn(rt712->component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__);
+
+ /* trigger GE interrupt */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0080, 0x0080);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0080, 0x0000);
+ } else {
+ /* disable HID1 & detected_mode_change event */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0c00, 0x0000);
+
+ dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__);
+ }
+
+ mutex_unlock(&rt712->calibrate_mutex);
+}
+
+static int rt712_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->hs_jack = hs_jack;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt712_sdca_jack_init(rt712);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt712_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue == gain_l_val && rvalue == gain_r_val)
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt712->mbq_regmap, mc->reg, gain_l_val);
+ /* Rch */
+ regmap_write(rt712->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return 1;
+
+ return -EIO;
+}
+
+static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = read_l / tendB;
+ else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = read_r / tendB;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err;
+ unsigned int ch_01, ch_02;
+
+ ch_01 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_02 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F,
+ RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F,
+ RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt712_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt712->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt712->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int err;
+
+ if (rt712->fu0f_mixer_l_mute == !ucontrol->value.integer.value[0] &&
+ rt712->fu0f_mixer_r_mute == !ucontrol->value.integer.value[1])
+ return 0;
+
+ rt712->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt712->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt712_sdca_set_fu0f_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+static int rt712_sdca_set_fu05_playback_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err;
+ unsigned int ch_01, ch_02;
+
+ ch_01 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_l_mute) ? 0x01 : 0x00;
+ ch_02 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt712_sdca_fu05_playback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt712->fu05_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt712->fu05_mixer_r_mute;
+ return 0;
+}
+
+static int rt712_sdca_fu05_playback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int err;
+
+ if (rt712->fu05_mixer_l_mute == !ucontrol->value.integer.value[0] &&
+ rt712->fu05_mixer_r_mute == !ucontrol->value.integer.value[1])
+ return 0;
+
+ rt712->fu05_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt712->fu05_mixer_r_mute = !ucontrol->value.integer.value[1];
+
+ err = rt712_sdca_set_fu05_playback_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv),
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x3f, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 8, 3, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv),
+ SOC_DOUBLE_EXT("FU05 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt712_sdca_fu05_playback_get, rt712_sdca_fu05_playback_put),
+};
+
+static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv),
+};
+
+static int rt712_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask = 0x3300;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val);
+
+ val = val & mask;
+ switch (val) {
+ case 0x3000:
+ val = 1;
+ break;
+ case 0x0300:
+ val = 0;
+ break;
+ }
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt712_sdca_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int mask_sft;
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (ucontrol->value.enumerated.item[0] == 0)
+ mask_sft = 12;
+ else if (ucontrol->value.enumerated.item[0] == 1)
+ mask_sft = 8;
+ else
+ return -EINVAL;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val);
+ val = (val >> mask_sft) & 0x3;
+ if (!val)
+ return 0;
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3fff);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3 << mask_sft, 0);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return 1;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt712_adc23_enum,
+ rt712_sdca_mux_get, rt712_sdca_mux_put);
+
+static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu05_dapm_mute = false;
+ rt712_sdca_set_fu05_playback_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu05_dapm_mute = true;
+ rt712_sdca_set_fu05_playback_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_fu0f_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu0f_dapm_mute = false;
+ rt712_sdca_set_fu0f_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu0f_dapm_mute = true;
+ rt712_sdca_set_fu0f_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde40_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde12_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt712_spk_l_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 0, 1, 1);
+static const struct snd_kcontrol_new rt712_spk_r_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 40", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde40_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde12_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_fu05_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_fu0f_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_audio_map[] = {
+ { "FU 05", NULL, "DP1RX" },
+ { "DP4TX", NULL, "FU 0F" },
+
+ { "FU 0F", NULL, "PDE 12" },
+ { "FU 0F", NULL, "ADC 23 Mux" },
+ { "ADC 23 Mux", "LINE2", "LINE2" },
+ { "ADC 23 Mux", "MIC2", "MIC2" },
+
+ { "HP", NULL, "PDE 40" },
+ { "HP", NULL, "FU 05" },
+};
+
+static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("FU06", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output */
+ SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt712_spk_l_dac),
+ SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt712_spk_r_dac),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = {
+ { "FU06", NULL, "DP3RX" },
+ { "FU06", NULL, "PDE 23" },
+ { "OT23 L", "Switch", "FU06" },
+ { "OT23 R", "Switch", "FU06" },
+ { "SPOL", NULL, "OT23 L" },
+ { "SPOR", NULL, "OT23 R" },
+};
+
+static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt712->jd_src);
+
+ return 0;
+}
+
+static int rt712_sdca_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712_sdca_parse_dt(rt712, &rt712->slave->dev);
+ rt712->component = component;
+
+ /* add SPK route */
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ snd_soc_add_component_controls(component,
+ rt712_sdca_spk_controls, ARRAY_SIZE(rt712_sdca_spk_controls));
+ snd_soc_dapm_new_controls(dapm,
+ rt712_sdca_spk_dapm_widgets, ARRAY_SIZE(rt712_sdca_spk_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt712_sdca_spk_dapm_routes, ARRAY_SIZE(rt712_sdca_spk_dapm_routes));
+ }
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt712->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * 0x0a00;
+ else { /* ADC gain */
+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) {
+ ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00;
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E,
+ RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt712_sdca_set_fu1e_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt712_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt712_sdca_fu_info, \
+ .get = rt712_sdca_dmic_fu1e_capture_get, \
+ .put = rt712_sdca_dmic_fu1e_capture_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt712_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = {
+ RT712_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4),
+ RT712_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv),
+ RT712_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, dmic_vol_tlv),
+};
+
+static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 0A Mux"))
+ mask_sft = 0;
+ else if (strstr(ucontrol->id.name, "ADC 0B Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (((val >> mask_sft) & 0xf) == 0x4) ? 0 : 1;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 0A Mux"))
+ mask_sft = 0;
+ else if (strstr(ucontrol->id.name, "ADC 0B Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = ((0xf << mask_sft) & val2) >> mask_sft;
+
+ if (val == 0)
+ val = 0x4;
+ else if (val >= 1)
+ val = 0xe;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, 0xf << mask_sft,
+ val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_dmic_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc0a_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc0b_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc0a_mux =
+ SOC_DAPM_ENUM_EXT("ADC 0A Mux", rt712_adc0a_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc0b_mux =
+ SOC_DAPM_ENUM_EXT("ADC 0B Mux", rt712_adc0b_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static int rt712_sdca_dmic_fu1e_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu1e_dapm_mute = false;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu1e_dapm_mute = true;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_dmic_pde11_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 0A Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc0a_mux),
+ SND_SOC_DAPM_MUX("ADC 0B Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc0b_mux),
+
+ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+ {"DP8TX", NULL, "FU 1E"},
+
+ {"FU 1E", NULL, "PDE 11"},
+ {"FU 1E", NULL, "ADC 0A Mux"},
+ {"FU 1E", NULL, "ADC 0B Mux"},
+ {"ADC 0A Mux", "DMIC1", "DMIC1"},
+ {"ADC 0A Mux", "DMIC2", "DMIC2"},
+ {"ADC 0B Mux", "DMIC1", "DMIC1"},
+ {"ADC 0B Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->dmic_component = component;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712 = {
+ .probe = rt712_sdca_probe,
+ .controls = rt712_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_controls),
+ .dapm_widgets = rt712_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dapm_widgets),
+ .dapm_routes = rt712_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_audio_map),
+ .set_jack = rt712_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = {
+ .probe = rt712_sdca_dmic_probe,
+ .controls = rt712_sdca_dmic_snd_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls),
+ .dapm_widgets = rt712_sdca_dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets),
+ .dapm_routes = rt712_sdca_dmic_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map),
+ .endianness = 1,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "dmic",
+#endif
+};
+
+static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt712_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ /* VA doesn't support AIF3 */
+ if (dai->id == RT712_AIF3 && rt712->version_id == RT712_VA)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT712_AIF1)
+ port = 1;
+ else if (dai->id == RT712_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT712_AIF1)
+ port = 4;
+ else if (dai->id == RT712_AIF3)
+ port = 8;
+ else
+ return -EINVAL;
+ }
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt712->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT712_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT712_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT712_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT712_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ switch (dai->id) {
+ case RT712_AIF1:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ case RT712_AIF2:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ case RT712_AIF3:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ default:
+ dev_err(component->dev, "%s: Wrong DAI id\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt712->slave, sdw_stream);
+ return 0;
+}
+
+#define RT712_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_ops = {
+ .hw_params = rt712_sdca_pcm_hw_params,
+ .hw_free = rt712_sdca_pcm_hw_free,
+ .set_stream = rt712_sdca_set_sdw_stream,
+ .shutdown = rt712_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dai[] = {
+ {
+ .name = "rt712-sdca-aif1",
+ .id = RT712_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ },
+ {
+ .name = "rt712-sdca-aif2",
+ .id = RT712_AIF2,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ }
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+ {
+ .name = "rt712-sdca-aif3",
+ .id = RT712_AIF3,
+ .capture = {
+ .stream_name = "DP8 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ }
+};
+
+int rt712_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712;
+ int ret;
+
+ rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+ if (!rt712)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt712);
+ rt712->slave = slave;
+ rt712->regmap = regmap;
+ rt712->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ mutex_init(&rt712->calibrate_mutex);
+ mutex_init(&rt712->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt712->hw_init = false;
+ rt712->first_hw_init = false;
+ rt712->fu0f_dapm_mute = true;
+ rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true;
+ rt712->fu1e_dapm_mute = true;
+ rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
+ rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
+ rt712->fu05_dapm_mute = true;
+ rt712->fu05_mixer_l_mute = rt712->fu05_mixer_r_mute = false;
+
+ /* JD source uses JD1 in default */
+ rt712->jd_src = RT712_JD1;
+
+ if (slave->id.part_id != RT712_PART_ID_713)
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712, rt712_sdca_dai, ARRAY_SIZE(rt712_sdca_dai));
+ else
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712, rt712_sdca_dai, 1);
+ if (ret < 0)
+ return ret;
+
+ /* only add the dmic component if a SMART_MIC function is exposed in ACPI */
+ if (sdca_device_quirk_match(slave, SDCA_QUIRKS_RT712_VB)) {
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712_dmic,
+ rt712_sdca_dmic_dai,
+ ARRAY_SIZE(rt712_sdca_dmic_dai));
+ if (ret < 0)
+ return ret;
+ rt712->dmic_function_found = true;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712)
+{
+ int ret = 0;
+ unsigned int hibernation_flag;
+ struct device *dev = &rt712->slave->dev;
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_ANALOG_CTL, RT712_MISC_POWER_CTL7, 0x0000);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+
+ /* calibration */
+ rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, &hibernation_flag);
+ if (!hibernation_flag) {
+ ret = rt712_sdca_calibration(rt712);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+ }
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3000, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIC2_LINE2_PDE_FLOAT_CTL, 0x3412);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DAC03_HP_PDE_FLOAT_CTL, 0x4040);
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL, 0x0001, 0x0000);
+ regmap_write(rt712->regmap, 0x2f50, 0x00);
+ regmap_write(rt712->regmap, 0x2f54, 0x00);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+
+ /* add SPK settings */
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_AMP_PDE_FLOAT_CTL, 0x2323);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ }
+}
+
+static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
+{
+ int ret = 0;
+ unsigned int jack_func_status, mic_func_status, amp_func_status;
+ struct device *dev = &rt712->slave->dev;
+
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s jack/mic/amp func_status=0x%x, 0x%x, 0x%x\n",
+ __func__, jack_func_status, mic_func_status, amp_func_status);
+
+ /* DMIC */
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_CH12_FLOAT_CTL, 0x0304);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL, 0x1f1e);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0B_FU_CH12_FLOAT_CTL, 0x0304);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_HDA_LEGACY_CONFIG_CTL0, 0x8010);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT11, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_index_write(rt712, RT712_ULTRA_SOUND_DET, RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+
+ /* Jack */
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_SEL_VEE2_HP_CTL1, 0x042a);
+ rt712_sdca_index_write(rt712, RT712_CHARGE_PUMP, RT712_HP_DET_CTL3, 0x1fff);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec67);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL1, 0x2802);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL6, 0xf215);
+
+ /* calibration */
+ ret = rt712_sdca_calibration(rt712);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, 0x3000, 0x0000);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_MISC_CTL_FOR_UAJ, 0x0003);
+
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+
+ /* SPK */
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec63);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL1, 0xfff5);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ }
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ unsigned int val;
+ struct sdw_slave_prop *prop = &slave->prop;
+
+ rt712->disable_irq = false;
+
+ if (rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, true);
+ regcache_cache_bypass(rt712->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val);
+ rt712->hw_id = (val & 0xf000) >> 12;
+ rt712->version_id = (val & 0x0f00) >> 8;
+ dev_dbg(&slave->dev, "%s hw_id=0x%x, version_id=0x%x\n", __func__, rt712->hw_id, rt712->version_id);
+
+ if (rt712->version_id == RT712_VA) {
+ if (rt712->dmic_function_found) {
+ dev_err(&slave->dev, "%s RT712 VA detected but SMART_MIC function exposed in ACPI\n",
+ __func__);
+ goto suspend;
+ }
+
+ rt712_sdca_va_io_init(rt712);
+ } else {
+ if (!rt712->dmic_function_found)
+ dev_warn(&slave->dev, "%s RT712 VB detected but no SMART_MIC function exposed in ACPI\n",
+ __func__);
+
+ /* multilanes and DMIC are supported by rt712vb */
+ prop->lane_control_support = true;
+ rt712_sdca_vb_io_init(rt712);
+ }
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt712->hs_jack)
+ rt712_sdca_jack_init(rt712);
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001);
+
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, false);
+ regcache_mark_dirty(rt712->regmap);
+ regcache_cache_bypass(rt712->mbq_regmap, false);
+ regcache_mark_dirty(rt712->mbq_regmap);
+ } else
+ rt712->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt712->hw_init = true;
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+suspend:
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
new file mode 100644
index 000000000000..7ab7d5feb50a
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca.h -- RT712 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_H__
+#define __RT712_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt712_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct snd_soc_component *dmic_component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ struct mutex disable_irq_lock; /* SDCA irq lock protection */
+ bool disable_irq;
+ int jack_type;
+ int jd_src;
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ unsigned int hw_id;
+ unsigned int version_id;
+ bool dmic_function_found;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+ bool fu05_dapm_mute;
+ bool fu05_mixer_l_mute;
+ bool fu05_mixer_r_mute;
+};
+
+struct rt712_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* SDCA (Channel) */
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+
+/* NID */
+#define RT712_VENDOR_REG 0x20
+#define RT712_EQ_CTRL 0x53
+#define RT712_CHARGE_PUMP 0x57
+#define RT712_VENDOR_CALI 0x58
+#define RT712_ULTRA_SOUND_DET 0x59
+#define RT712_VENDOR_IMS_DRE 0x5b
+#define RT712_VENDOR_ANALOG_CTL 0x5f
+#define RT712_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT712_JD_PRODUCT_NUM 0x00
+#define RT712_ANALOG_BIAS_CTL3 0x04
+#define RT712_JD_CTL1 0x09
+#define RT712_IO_CTL 0x0c
+#define RT712_LDO2_3_CTL1 0x0e
+#define RT712_PARA_VERB_CTL 0x1a
+#define RT712_CC_DET1 0x24
+#define RT712_CLASSD_AMP_CTL1 0x37
+#define RT712_CLASSD_AMP_CTL6 0x3c
+#define RT712_COMBO_JACK_AUTO_CTL1 0x45
+#define RT712_COMBO_JACK_AUTO_CTL2 0x46
+#define RT712_COMBO_JACK_AUTO_CTL3 0x47
+#define RT712_DIGITAL_MISC_CTRL4 0x4a
+#define RT712_FSM_CTL 0x67
+#define RT712_SW_CONFIG1 0x8a
+#define RT712_SW_CONFIG2 0x8b
+
+/* Index (NID:57h) */
+#define RT712_HP_DET_CTL3 0x0c
+
+/* Index (NID:58h) */
+#define RT712_DAC_DC_CALI_CTL1 0x00
+#define RT712_DAC_DC_CALI_CTL2 0x01
+
+/* Index (NID:59h) */
+#define RT712_ULTRA_SOUND_DETECTOR6 0x1e
+
+/* Index (NID:5bh) */
+#define RT712_IMS_DIGITAL_CTL1 0x00
+#define RT712_IMS_DIGITAL_CTL5 0x05
+#define RT712_SEL_VEE2_HP_CTL1 0x23
+#define RT712_HP_DETECT_RLDET_CTL1 0x29
+#define RT712_HP_DETECT_RLDET_CTL2 0x2a
+
+/* Index (NID:5fh) */
+#define RT712_MISC_POWER_CTL0 0x00
+#define RT712_MISC_POWER_CTL7 0x08
+
+/* Index (NID:61h) */
+#define RT712_HDA_LEGACY_MUX_CTL0 0x00
+#define RT712_HDA_LEGACY_CONFIG_CTL0 0x06
+#define RT712_HDA_LEGACY_RESET_CTL 0x08
+#define RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT712_DMIC_ENT_FLOAT_CTL 0x10
+#define RT712_DMIC_GAIN_ENT_FLOAT_CTL0 0x11
+#define RT712_DMIC_GAIN_ENT_FLOAT_CTL2 0x13
+#define RT712_ADC_ENT_FLOAT_CTL 0x15
+#define RT712_ADC_VOL_CH_FLOAT_CTL2 0x18
+#define RT712_DAC03_HP_PDE_FLOAT_CTL 0x22
+#define RT712_MIC2_LINE2_PDE_FLOAT_CTL 0x23
+#define RT712_ADC0A_08_PDE_FLOAT_CTL 0x26
+#define RT712_ADC0B_11_PDE_FLOAT_CTL 0x27
+#define RT712_DMIC1_2_PDE_FLOAT_CTL 0x2b
+#define RT712_AMP_PDE_FLOAT_CTL 0x2c
+#define RT712_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f
+#define RT712_GE_RELATED_CTL1 0x45
+#define RT712_GE_RELATED_CTL2 0x46
+#define RT712_MIXER_CTL0 0x52
+#define RT712_MIXER_CTL1 0x53
+#define RT712_EAPD_CTL 0x55
+#define RT712_UMP_HID_CTL0 0x60
+#define RT712_UMP_HID_CTL1 0x61
+#define RT712_UMP_HID_CTL2 0x62
+#define RT712_UMP_HID_CTL3 0x63
+#define RT712_UMP_HID_CTL4 0x64
+#define RT712_UMP_HID_CTL5 0x65
+#define RT712_UMP_HID_CTL6 0x66
+#define RT712_UMP_HID_CTL7 0x67
+#define RT712_UMP_HID_CTL8 0x68
+#define RT712_MISC_CTL_FOR_UAJ 0x72
+#define RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL 0xa2
+#define RT712_DMIC2_FU_IT_FLOAT_CTL 0xa6
+#define RT712_ADC0B_FU_CH12_FLOAT_CTL 0xb0
+#define RT712_DMIC2_FU_CH12_FLOAT_CTL 0xb1
+
+/* Parameter & Verb control 01 (0x1a)(NID:20h) */
+#define RT712_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT712_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT712_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT712_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT712_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* DAC DC offset calibration control-1 (0x00)(NID:58h) */
+#define RT712_DAC_DC_CALI_TRIGGER (0x1 << 15)
+
+#define RT712_EAPD_HIGH 0x2
+#define RT712_EAPD_LOW 0x0
+
+/* RC Calibration register */
+#define RT712_RC_CAL 0x3201
+
+/* Buffer address for HID */
+#define RT712_BUF_ADDR_HID1 0x44030000
+#define RT712_BUF_ADDR_HID2 0x44030020
+
+/* RT712 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT712 SDCA entity */
+#define RT712_SDCA_ENT0 0x00
+#define RT712_SDCA_ENT_HID01 0x01
+#define RT712_SDCA_ENT_GE49 0x49
+#define RT712_SDCA_ENT_USER_FU05 0x05
+#define RT712_SDCA_ENT_USER_FU06 0x06
+#define RT712_SDCA_ENT_USER_FU0F 0x0f
+#define RT712_SDCA_ENT_USER_FU10 0x19
+#define RT712_SDCA_ENT_USER_FU1E 0x1e
+#define RT712_SDCA_ENT_FU15 0x15
+#define RT712_SDCA_ENT_PDE23 0x23
+#define RT712_SDCA_ENT_PDE40 0x40
+#define RT712_SDCA_ENT_PDE11 0x11
+#define RT712_SDCA_ENT_PDE12 0x12
+#define RT712_SDCA_ENT_CS01 0x01
+#define RT712_SDCA_ENT_CS11 0x11
+#define RT712_SDCA_ENT_CS1F 0x1f
+#define RT712_SDCA_ENT_CS1C 0x1c
+#define RT712_SDCA_ENT_CS31 0x31
+#define RT712_SDCA_ENT_OT23 0x42
+#define RT712_SDCA_ENT_IT11 0x26
+#define RT712_SDCA_ENT_IT26 0x26
+#define RT712_SDCA_ENT_IT09 0x09
+#define RT712_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT712_SDCA_ENT_PLATFORM_FU44 0x44
+
+/* RT712 SDCA control */
+#define RT712_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT712_SDCA_CTL_FU_MUTE 0x01
+#define RT712_SDCA_CTL_FU_VOLUME 0x02
+#define RT712_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT712_SDCA_CTL_SELECTED_MODE 0x01
+#define RT712_SDCA_CTL_DETECTED_MODE 0x02
+#define RT712_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT712_SDCA_CTL_VENDOR_DEF 0x30
+#define RT712_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT712_SDCA_CTL_FUNC_STATUS 0x10
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+#define FUNCTION_HAS_BEEN_RESET BIT(6)
+#define FUNCTION_BUSY BIT(7)
+
+/* sample frequency index */
+#define RT712_SDCA_RATE_16000HZ 0x04
+#define RT712_SDCA_RATE_32000HZ 0x07
+#define RT712_SDCA_RATE_44100HZ 0x08
+#define RT712_SDCA_RATE_48000HZ 0x09
+#define RT712_SDCA_RATE_96000HZ 0x0b
+#define RT712_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT712_AIF1,
+ RT712_AIF2,
+ RT712_AIF3,
+};
+
+enum rt712_sdca_jd_src {
+ RT712_JD_NULL,
+ RT712_JD1,
+};
+
+enum rt712_sdca_hw_id {
+ RT712_DEV_ID_712 = 0x7,
+ RT712_DEV_ID_713 = 0x6,
+ RT712_DEV_ID_716 = 0x5,
+ RT712_DEV_ID_717 = 0x4,
+};
+
+#define RT712_PART_ID_713 0x713
+
+enum rt712_sdca_version {
+ RT712_VA,
+ RT712_VB,
+};
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt712_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+
+int rt712_sdca_jack_detect(struct rt712_sdca_priv *rt712, bool *hp, bool *mic);
+#endif /* __RT712_H__ */
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
new file mode 100644
index 000000000000..ce7d8955efc3
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt715-sdca.h"
+#include "rt715-sdca-sdw.h"
+
+static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2230 ... 0x2239:
+ case 0x2f5b:
+ case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2021:
+ case 0x2023:
+ case 0x2230:
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2200 ... 0x2212: /* i2c debug */
+ case 0x2f07:
+ case 0x2f1b ... 0x2f1e:
+ case 0x2f30 ... 0x2f34:
+ case 0x2f50 ... 0x2f51:
+ case 0x2f53 ... 0x2f59:
+ case 0x2f5c ... 0x2f5f:
+ case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200002b:
+ case 0x2000036:
+ case 0x2000037:
+ case 0x2000039:
+ case 0x2000044:
+ case 0x6100000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt715_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt715_sdca_readable_register,
+ .volatile_reg = rt715_sdca_volatile_register,
+ .max_register = 0x43ffffff,
+ .reg_defaults = rt715_reg_defaults_sdca,
+ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt715_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt715_sdca_mbq_readable_register,
+ .volatile_reg = rt715_sdca_mbq_volatile_register,
+ .max_register = 0x43ffffff,
+ .reg_defaults = rt715_mbq_reg_defaults_sdca,
+ .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt715_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt715->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt715_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt715_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x50;/* BITMAP: 01010000 */
+ prop->sink_ports = 0x0; /* BITMAP: 00000000 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ dpn = prop->src_dpn_prop;
+ i = 0;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt715_sdca_slave_ops = {
+ .read_prop = rt715_sdca_read_prop,
+ .update_status = rt715_sdca_update_status,
+};
+
+static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *mbq_regmap, *regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave);
+}
+
+static int rt715_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt715_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt715_sdca_id);
+
+static int rt715_dev_suspend(struct device *dev)
+{
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+
+ if (!rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, true);
+ regcache_mark_dirty(rt715->regmap);
+ regcache_cache_only(rt715->mbq_regmap, true);
+ regcache_mark_dirty(rt715->mbq_regmap);
+
+ return 0;
+}
+
+#define RT715_PROBE_TIMEOUT 5000
+
+static int rt715_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT715_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt715->regmap, false);
+ regcache_sync_region(rt715->regmap,
+ SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
+ CH_00),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
+ regcache_cache_only(rt715->mbq_regmap, false);
+ regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff);
+ regcache_sync_region(rt715->mbq_regmap,
+ SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
+ CH_00),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt715_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+ RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+ .driver = {
+ .name = "rt715-sdca",
+ .pm = pm_ptr(&rt715_pm),
+ },
+ .probe = rt715_sdca_sdw_probe,
+ .remove = rt715_sdca_sdw_remove,
+ .ops = &rt715_sdca_slave_ops,
+ .id_table = rt715_sdca_id,
+};
+module_sdw_driver(rt715_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h
new file mode 100644
index 000000000000..0cbc14844f8c
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDW_SDCA_H__
+#define __RT715_SDW_SDCA_H__
+
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt715_reg_defaults_sdca[] = {
+ { 0x201a, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x0b },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0e },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f0b, 0x00 },
+ { 0x2f0c, 0x00 },
+ { 0x2f0d, 0x00 },
+ { 0x2f0e, 0x12 },
+ { 0x2f0f, 0x00 },
+ { 0x2f10, 0x00 },
+ { 0x2f11, 0x00 },
+ { 0x2f12, 0x00 },
+ { 0x2f13, 0x00 },
+ { 0x2f14, 0x00 },
+ { 0x2f15, 0x00 },
+ { 0x2f16, 0x00 },
+ { 0x2f17, 0x00 },
+ { 0x2f18, 0x00 },
+ { 0x2f19, 0x03 },
+ { 0x2f1a, 0x00 },
+ { 0x2f1f, 0x10 },
+ { 0x2f20, 0x00 },
+ { 0x2f21, 0x00 },
+ { 0x2f22, 0x00 },
+ { 0x2f23, 0x00 },
+ { 0x2f24, 0x00 },
+ { 0x2f25, 0x00 },
+ { 0x2f52, 0x01 },
+ { 0x2f5a, 0x02 },
+ { 0x2f5b, 0x05 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
+ RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+};
+
+static const struct reg_default rt715_mbq_reg_defaults_sdca[] = {
+ { 0x200002b, 0x0420 },
+ { 0x2000036, 0x0000 },
+ { 0x2000037, 0x0000 },
+ { 0x2000039, 0xaa81 },
+ { 0x2000044, 0x0202 },
+ { 0x6100000, 0x0100 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
+ { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
+};
+#endif /* __RT715_SDW_SDCA_H__ */
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
new file mode 100644
index 000000000000..b6a7a807a428
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt715-sdca.h"
+
+static int rt715_sdca_index_write(struct rt715_sdca_priv *rt715,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ struct regmap *regmap = rt715->mbq_regmap;
+ unsigned int addr;
+ int ret;
+
+ addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt715->slave->dev,
+ "%s: Failed to set private value: %08x <= %04x %d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt715_sdca_index_read(struct rt715_sdca_priv *rt715,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ struct regmap *regmap = rt715->mbq_regmap;
+ unsigned int addr;
+ int ret;
+
+ addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt715->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt715_sdca_index_update_bits(struct rt715_sdca_priv *rt715,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt715_sdca_index_read(rt715, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+
+ return rt715_sdca_index_write(rt715, nid, reg, tmp);
+}
+
+static inline unsigned int rt715_sdca_vol_gain(unsigned int u_ctrl_val,
+ unsigned int vol_max, unsigned int vol_gain_sft)
+{
+ unsigned int val;
+
+ if (u_ctrl_val > vol_max)
+ u_ctrl_val = vol_max;
+ val = u_ctrl_val;
+ u_ctrl_val =
+ ((abs(u_ctrl_val - vol_gain_sft) * RT715_SDCA_DB_STEP) << 8) / 1000;
+ if (val <= vol_gain_sft) {
+ u_ctrl_val = ~u_ctrl_val;
+ u_ctrl_val += 1;
+ }
+ u_ctrl_val &= 0xffff;
+
+ return u_ctrl_val;
+}
+
+static inline unsigned int rt715_sdca_boost_gain(unsigned int u_ctrl_val,
+ unsigned int b_max, unsigned int b_gain_sft)
+{
+ if (u_ctrl_val > b_max)
+ u_ctrl_val = b_max;
+
+ return (u_ctrl_val * 10) << b_gain_sft;
+}
+
+static inline unsigned int rt715_sdca_get_gain(unsigned int reg_val,
+ unsigned int gain_sft)
+{
+ unsigned int neg_flag = 0;
+
+ if (reg_val & BIT(15)) {
+ reg_val = ~(reg_val - 1) & 0xffff;
+ neg_flag = 1;
+ }
+ reg_val *= 1000;
+ reg_val >>= 8;
+ if (neg_flag)
+ reg_val = gain_sft - reg_val / RT715_SDCA_DB_STEP;
+ else
+ reg_val = gain_sft + reg_val / RT715_SDCA_DB_STEP;
+
+ return reg_val;
+}
+
+/* SDCA Volume/Boost control */
+static int rt715_sdca_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_val, i, k_changed = 0;
+ int ret;
+
+ for (i = 0; i < 2; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_2ch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ rt715->kctl_2ch_orig[i] = ucontrol->value.integer.value[i];
+ gain_val =
+ rt715_sdca_vol_gain(ucontrol->value.integer.value[i], mc->max,
+ mc->shift);
+ ret = regmap_write(rt715->mbq_regmap, mc->reg + i, gain_val);
+ if (ret != 0) {
+ dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, mc->reg + i, gain_val);
+ return ret;
+ }
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_set_amp_gain_4ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base, k_changed = 0;
+ const unsigned int gain_sft = 0x2f;
+ unsigned int gain_val, i;
+ int ret;
+
+ for (i = 0; i < 4; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_4ch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ rt715->kctl_4ch_orig[i] = ucontrol->value.integer.value[i];
+ gain_val =
+ rt715_sdca_vol_gain(ucontrol->value.integer.value[i], p->max,
+ gain_sft);
+ ret = regmap_write(rt715->mbq_regmap, reg_base + i,
+ gain_val);
+ if (ret != 0) {
+ dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, reg_base + i, gain_val);
+ return ret;
+ }
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_set_amp_gain_8ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base, i, k_changed = 0;
+ const unsigned int gain_sft = 8;
+ unsigned int gain_val, reg;
+ int ret;
+
+ for (i = 0; i < 8; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ rt715->kctl_8ch_orig[i] = ucontrol->value.integer.value[i];
+ gain_val =
+ rt715_sdca_boost_gain(ucontrol->value.integer.value[i], p->max,
+ gain_sft);
+ reg = i < 7 ? reg_base + i : (reg_base - 1) | BIT(15);
+ ret = regmap_write(rt715->mbq_regmap, reg, gain_val);
+ if (ret != 0) {
+ dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, reg, gain_val);
+ return ret;
+ }
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int val, i;
+ int ret;
+
+ for (i = 0; i < 2; i++) {
+ ret = regmap_read(rt715->mbq_regmap, mc->reg + i, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, mc->reg + i, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, mc->shift);
+ }
+
+ return 0;
+}
+
+static int rt715_sdca_set_amp_gain_4ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base, i;
+ const unsigned int gain_sft = 0x2f;
+ unsigned int val;
+ int ret;
+
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, reg_base + i, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, gain_sft);
+ }
+
+ return 0;
+}
+
+static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base;
+ const unsigned int gain_sft = 8;
+ unsigned int val_l, val_r;
+ unsigned int i, reg;
+ int ret;
+
+ for (i = 0; i < 8; i += 2) {
+ ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val_l);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, reg_base + i, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i] = (val_l >> gain_sft) / 10;
+
+ reg = (i == 6) ? (reg_base - 1) | BIT(15) : reg_base + 1 + i;
+ ret = regmap_read(rt715->mbq_regmap, reg, &val_r);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, reg, ret);
+ return ret;
+ }
+ ucontrol->value.integer.value[i + 1] = (val_r >> gain_sft) / 10;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static int rt715_sdca_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int reg_base = p->reg_base;
+ unsigned int invert = p->invert, i;
+ int val;
+
+ for (i = 0; i < p->count; i += 2) {
+ val = snd_soc_component_read(component, reg_base + i);
+ if (val < 0)
+ return -EINVAL;
+ ucontrol->value.integer.value[i] = invert ? p->max - val : val;
+
+ val = snd_soc_component_read(component, reg_base + 1 + i);
+ if (val < 0)
+ return -EINVAL;
+ ucontrol->value.integer.value[i + 1] =
+ invert ? p->max - val : val;
+ }
+
+ return 0;
+}
+
+static int rt715_sdca_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+ unsigned int val[4] = {0}, val_mask, i, k_changed = 0;
+ unsigned int reg = p->reg_base;
+ unsigned int shift = p->shift;
+ unsigned int max = p->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = p->invert;
+ int err;
+
+ for (i = 0; i < 4; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_switch_orig[i]) {
+ k_changed = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ rt715->kctl_switch_orig[i * 2] = ucontrol->value.integer.value[i * 2];
+ val[i * 2] = ucontrol->value.integer.value[i * 2] & mask;
+ if (invert)
+ val[i * 2] = max - val[i * 2];
+ val_mask = mask << shift;
+ val[i * 2] <<= shift;
+
+ rt715->kctl_switch_orig[i * 2 + 1] =
+ ucontrol->value.integer.value[i * 2 + 1];
+ val[i * 2 + 1] =
+ ucontrol->value.integer.value[i * 2 + 1] & mask;
+ if (invert)
+ val[i * 2 + 1] = max - val[i * 2 + 1];
+
+ val[i * 2 + 1] <<= shift;
+
+ err = snd_soc_component_update_bits(component, reg + i * 2, val_mask,
+ val[i * 2]);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_component_update_bits(component, reg + 1 + i * 2,
+ val_mask, val[i * 2 + 1]);
+ if (err < 0)
+ return err;
+ }
+
+ return k_changed;
+}
+
+static int rt715_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt715_sdca_kcontrol_private *p =
+ (struct rt715_sdca_kcontrol_private *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+#define RT715_SDCA_PR_VALUE(xreg_base, xcount, xmax, xshift, xinvert) \
+ ((unsigned long)&(struct rt715_sdca_kcontrol_private) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .shift = xshift, .invert = xinvert})
+
+#define RT715_SDCA_FU_CTRL(xname, reg_base, xshift, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt715_sdca_fu_info, \
+ .get = rt715_sdca_get_volsw, \
+ .put = rt715_sdca_put_volsw, \
+ .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, \
+ xshift, xinvert)}
+
+#define RT715_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, tlv_array, xcount, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt715_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, 0, 0) }
+
+#define RT715_SDCA_BOOST_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, tlv_array, xcount, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt715_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, 0, 0) }
+
+static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = {
+ /* Capture switch */
+ SOC_DOUBLE_R("FU0A Capture Switch",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_02),
+ 0, 1, 1),
+ RT715_SDCA_FU_CTRL("FU02 Capture Switch",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01),
+ 0, 1, 1, 4),
+ RT715_SDCA_FU_CTRL("FU06 Capture Switch",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_MUTE_CTRL, CH_01),
+ 0, 1, 1, 4),
+ /* Volume Control */
+ SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01),
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_02),
+ 0x2f, 0x3f, 0,
+ rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+ in_vol_tlv),
+ RT715_SDCA_EXT_TLV("FU02 Capture Volume",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_4ch_get,
+ rt715_sdca_set_amp_gain_4ch_put,
+ in_vol_tlv, 4, 0x3f),
+ RT715_SDCA_EXT_TLV("FU06 Capture Volume",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+ RT715_SDCA_FU_VOL_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_4ch_get,
+ rt715_sdca_set_amp_gain_4ch_put,
+ in_vol_tlv, 4, 0x3f),
+ /* MIC Boost Control */
+ RT715_SDCA_BOOST_EXT_TLV("FU0E Boost",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_8ch_get,
+ rt715_sdca_set_amp_gain_8ch_put,
+ mic_vol_tlv, 8, 3),
+ RT715_SDCA_BOOST_EXT_TLV("FU0C Boost",
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+ RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01),
+ rt715_sdca_set_amp_gain_8ch_get,
+ rt715_sdca_set_amp_gain_8ch_put,
+ mic_vol_tlv, 8, 3),
+};
+
+static int rt715_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int val, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 12;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
+ mask_sft = 4;
+ else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 0;
+ else
+ return -EINVAL;
+
+ rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL,
+ RT715_HDA_LEGACY_MUX_CTL1, &val);
+ val = (val >> mask_sft) & 0xf;
+
+ /*
+ * The first two indices of ADC Mux 24/25 are routed to the same
+ * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
+ * To have a unique set of inputs, we skip the index1 of the muxes.
+ */
+ if ((strstr(ucontrol->id.name, "ADC 24 Mux") ||
+ strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0)
+ val -= 1;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt715_sdca_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 12;
+ else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
+ mask_sft = 4;
+ else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 0;
+ else
+ return -EINVAL;
+
+ /* Verb ID = 0x701h, nid = e->reg */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL,
+ RT715_HDA_LEGACY_MUX_CTL1, &val2);
+ val2 = (val2 >> mask_sft) & 0xf;
+
+ change = val != val2;
+
+ if (change)
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_HDA_CTL,
+ RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_22_23_mux_text[] = {
+ "MIC1",
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+/*
+ * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
+ * 1 will be connected to the same dmic source, therefore we skip index 1 to
+ * avoid misunderstanding on usage of dapm routing.
+ */
+static int rt715_adc_24_25_values[] = {
+ 0,
+ 2,
+ 3,
+ 4,
+ 5,
+};
+
+static const char * const adc_24_mux_text[] = {
+ "MIC2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static const char * const adc_25_mux_text[] = {
+ "MIC1",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0,
+ adc_22_23_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0,
+ adc_22_23_mux_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
+ SND_SOC_NOPM, 0, 0xf,
+ adc_24_mux_text, rt715_adc_24_25_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
+ SND_SOC_NOPM, 0, 0xf,
+ adc_25_mux_text, rt715_adc_24_25_values);
+
+static const struct snd_kcontrol_new rt715_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc24_mux =
+ SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
+ rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static int rt715_sdca_pde23_24_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
+ RT715_SDCA_REQ_POW_CTRL,
+ CH_00), 0x00);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
+ RT715_SDCA_REQ_POW_CTRL,
+ CH_00), 0x03);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt715_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0,
+ rt715_sdca_pde23_24_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc23_mux),
+ SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc24_mux),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc25_mux),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = {
+ {"DP6TX", NULL, "ADC 09"},
+ {"DP6TX", NULL, "ADC 08"},
+ {"DP4TX", NULL, "ADC 07"},
+ {"DP4TX", NULL, "ADC 27"},
+ {"DP4TX", NULL, "ADC 09"},
+ {"DP4TX", NULL, "ADC 08"},
+
+ {"LINE1", NULL, "PDE23_24"},
+ {"LINE2", NULL, "PDE23_24"},
+ {"MIC1", NULL, "PDE23_24"},
+ {"MIC2", NULL, "PDE23_24"},
+ {"DMIC1", NULL, "PDE23_24"},
+ {"DMIC2", NULL, "PDE23_24"},
+ {"DMIC3", NULL, "PDE23_24"},
+ {"DMIC4", NULL, "PDE23_24"},
+
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 07", NULL, "ADC 24 Mux"},
+ {"ADC 27", NULL, "ADC 25 Mux"},
+ {"ADC 22 Mux", "MIC1", "MIC1"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "DMIC1", "DMIC1"},
+ {"ADC 22 Mux", "DMIC2", "DMIC2"},
+ {"ADC 22 Mux", "DMIC3", "DMIC3"},
+ {"ADC 22 Mux", "DMIC4", "DMIC4"},
+ {"ADC 23 Mux", "MIC1", "MIC1"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "DMIC1", "DMIC1"},
+ {"ADC 23 Mux", "DMIC2", "DMIC2"},
+ {"ADC 23 Mux", "DMIC3", "DMIC3"},
+ {"ADC 23 Mux", "DMIC4", "DMIC4"},
+ {"ADC 24 Mux", "MIC2", "MIC2"},
+ {"ADC 24 Mux", "DMIC1", "DMIC1"},
+ {"ADC 24 Mux", "DMIC2", "DMIC2"},
+ {"ADC 24 Mux", "DMIC3", "DMIC3"},
+ {"ADC 24 Mux", "DMIC4", "DMIC4"},
+ {"ADC 25 Mux", "MIC1", "MIC1"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 25 Mux", "DMIC3", "DMIC3"},
+ {"ADC 25 Mux", "DMIC4", "DMIC4"},
+};
+
+static int rt715_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
+ .probe = rt715_sdca_probe,
+ .controls = rt715_sdca_snd_controls,
+ .num_controls = ARRAY_SIZE(rt715_sdca_snd_controls),
+ .dapm_widgets = rt715_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets),
+ .dapm_routes = rt715_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map),
+ .endianness = 1,
+};
+
+static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt715_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int val;
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ switch (dai->id) {
+ case RT715_AIF1:
+ port_config.num = 6;
+ rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
+ 0xa500);
+ break;
+ case RT715_AIF2:
+ port_config.num = 4;
+ rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
+ 0xaf00);
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt715->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(component->dev, "%s: Unable to configure port, retval:%d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0x1;
+ break;
+ case 11025:
+ val = 0x2;
+ break;
+ case 12000:
+ val = 0x3;
+ break;
+ case 16000:
+ val = 0x4;
+ break;
+ case 22050:
+ val = 0x5;
+ break;
+ case 24000:
+ val = 0x6;
+ break;
+ case 32000:
+ val = 0x7;
+ break;
+ case 44100:
+ val = 0x8;
+ break;
+ case 48000:
+ val = 0x9;
+ break;
+ case 88200:
+ val = 0xa;
+ break;
+ case 96000:
+ val = 0xb;
+ break;
+ case 176400:
+ val = 0xc;
+ break;
+ case 192000:
+ val = 0xd;
+ break;
+ case 384000:
+ val = 0xe;
+ break;
+ case 768000:
+ val = 0xf;
+ break;
+ default:
+ dev_err(component->dev, "%s: Unsupported sample rate %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN,
+ RT715_SDCA_FREQ_IND_CTRL, CH_00), val);
+
+ return 0;
+}
+
+static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt715->slave, sdw_stream);
+ return 0;
+}
+
+#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt715_sdca_ops = {
+ .hw_params = rt715_sdca_pcm_hw_params,
+ .hw_free = rt715_sdca_pcm_hw_free,
+ .set_stream = rt715_sdca_set_sdw_stream,
+ .shutdown = rt715_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt715_sdca_dai[] = {
+ {
+ .name = "rt715-sdca-aif1",
+ .id = RT715_AIF1,
+ .capture = {
+ .stream_name = "DP6 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_sdca_ops,
+ },
+ {
+ .name = "rt715-sdca-aif2",
+ .id = RT715_AIF2,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_sdca_ops,
+ },
+};
+
+/* Bus clock frequency */
+#define RT715_CLK_FREQ_9600000HZ 9600000
+#define RT715_CLK_FREQ_12000000HZ 12000000
+#define RT715_CLK_FREQ_6000000HZ 6000000
+#define RT715_CLK_FREQ_4800000HZ 4800000
+#define RT715_CLK_FREQ_2400000HZ 2400000
+#define RT715_CLK_FREQ_12288000HZ 12288000
+
+int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt715_sdca_priv *rt715;
+ int ret;
+
+ rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
+ if (!rt715)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt715);
+ rt715->slave = slave;
+ rt715->regmap = regmap;
+ rt715->mbq_regmap = mbq_regmap;
+ rt715->hw_sdw_ver = slave->id.sdw_version;
+
+ regcache_cache_only(rt715->regmap, true);
+ regcache_cache_only(rt715->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt715->hw_init = false;
+ rt715->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt715_sdca,
+ rt715_sdca_dai,
+ ARRAY_SIZE(rt715_sdca_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return ret;
+}
+
+int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int hw_ver;
+
+ if (rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, false);
+ regcache_cache_only(rt715->mbq_regmap, false);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt715->first_hw_init) {
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ rt715->first_hw_init = true;
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt715_sdca_index_read(rt715, RT715_VENDOR_REG,
+ RT715_PRODUCT_NUM, &hw_ver);
+ hw_ver = hw_ver & 0x000f;
+
+ /* set clock selector = external */
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
+ RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1);
+ /* set GPIO_4/5/6 to be 3rd/4th DMIC usage */
+ if (hw_ver == 0x0)
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_AD_FUNC_EN, 0x54, 0x54);
+ else if (hw_ver == 0x1) {
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_AD_FUNC_EN, 0x55, 0x55);
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_REV_1, 0x40, 0x40);
+ }
+ /* DFLL Calibration trigger */
+ rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+ RT715_DFLL_VAD, 0x1, 0x1);
+ /* trigger mode = VAD enable */
+ regmap_write(rt715->regmap,
+ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+ RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2);
+ /* SMPU-1 interrupt enable mask */
+ regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1);
+
+ /* Mark Slave initialization complete */
+ rt715->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h
new file mode 100644
index 000000000000..e5d6928ecaba
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt715-sdca.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDCA_H__
+#define __RT715_SDCA_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+struct rt715_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_codec *codec;
+ struct sdw_slave *slave;
+ struct delayed_work adc_mute_work;
+ int dbg_nid;
+ int dbg_vid;
+ int dbg_payload;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int l_is_unmute;
+ int r_is_unmute;
+ int hw_sdw_ver;
+ int kctl_switch_orig[4];
+ int kctl_2ch_orig[2];
+ int kctl_4ch_orig[4];
+ int kctl_8ch_orig[8];
+};
+
+struct rt715_sdca_kcontrol_private {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int shift;
+ unsigned int invert;
+};
+
+/* MIPI Register */
+#define RT715_INT_CTRL 0x005a
+#define RT715_INT_MASK 0x005e
+
+/* NID */
+#define RT715_AUDIO_FUNCTION_GROUP 0x01
+#define RT715_MIC_ADC 0x07
+#define RT715_LINE_ADC 0x08
+#define RT715_MIX_ADC 0x09
+#define RT715_DMIC1 0x12
+#define RT715_DMIC2 0x13
+#define RT715_MIC1 0x18
+#define RT715_MIC2 0x19
+#define RT715_LINE1 0x1a
+#define RT715_LINE2 0x1b
+#define RT715_DMIC3 0x1d
+#define RT715_DMIC4 0x29
+#define RT715_VENDOR_REG 0x20
+#define RT715_MUX_IN1 0x22
+#define RT715_MUX_IN2 0x23
+#define RT715_MUX_IN3 0x24
+#define RT715_MUX_IN4 0x25
+#define RT715_MIX_ADC2 0x27
+#define RT715_INLINE_CMD 0x55
+#define RT715_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT715_PRODUCT_NUM 0x0
+#define RT715_IRQ_CTRL 0x2b
+#define RT715_AD_FUNC_EN 0x36
+#define RT715_REV_1 0x37
+#define RT715_SDW_INPUT_SEL 0x39
+#define RT715_DFLL_VAD 0x44
+#define RT715_EXT_DMIC_CLK_CTRL2 0x54
+
+/* Index (NID:61h) */
+#define RT715_HDA_LEGACY_MUX_CTL1 0x00
+
+/* SDCA (Function) */
+#define FUN_JACK_CODEC 0x01
+#define FUN_MIC_ARRAY 0x02
+#define FUN_HID 0x03
+/* SDCA (Entity) */
+#define RT715_SDCA_ST_EN 0x00
+#define RT715_SDCA_CS_FREQ_IND_EN 0x01
+#define RT715_SDCA_FU_ADC8_9_VOL 0x02
+#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05
+#define RT715_SDCA_FU_ADC10_11_VOL 0x06
+#define RT715_SDCA_FU_ADC7_27_VOL 0x0a
+#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c
+#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e
+#define RT715_SDCA_CX_CLK_SEL_EN 0x10
+#define RT715_SDCA_CREQ_POW_EN 0x18
+/* SDCA (Control) */
+#define RT715_SDCA_ST_CTRL 0x00
+#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01
+#define RT715_SDCA_REQ_POW_CTRL 0x01
+#define RT715_SDCA_FU_MUTE_CTRL 0x01
+#define RT715_SDCA_FU_VOL_CTRL 0x02
+#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b
+#define RT715_SDCA_FREQ_IND_CTRL 0x10
+#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10
+#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11
+/* SDCA (Channel) */
+#define CH_00 0x00
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+#define CH_05 0x05
+#define CH_06 0x06
+#define CH_07 0x07
+#define CH_08 0x08
+
+#define RT715_SDCA_DB_STEP 375
+
+enum {
+ RT715_AIF1,
+ RT715_AIF2,
+};
+
+int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+#endif /* __RT715_SDCA_H__ */
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
new file mode 100644
index 000000000000..a3df4bbedf86
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rt715-sdw.c -- rt715 ALSA SoC audio driver
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ *
+ * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver
+ *
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt715.h"
+#include "rt715-sdw.h"
+
+static bool rt715_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e0 ... 0x00e5:
+ case 0x00ee ... 0x00ef:
+ case 0x00f0 ... 0x00f5:
+ case 0x00fe ... 0x00ff:
+ case 0x02e0:
+ case 0x02f0:
+ case 0x04e0:
+ case 0x04f0:
+ case 0x06e0:
+ case 0x06f0:
+ case 0x2000 ... 0x2016:
+ case 0x201a ... 0x2027:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2200 ... 0x2204:
+ case 0x2206 ... 0x2212:
+ case 0x2220 ... 0x2223:
+ case 0x2230 ... 0x2239:
+ case 0x22f0 ... 0x22f3:
+ case 0x3122:
+ case 0x3123:
+ case 0x3124:
+ case 0x3125:
+ case 0x3607:
+ case 0x3608:
+ case 0x3609:
+ case 0x3610:
+ case 0x3611:
+ case 0x3627:
+ case 0x3712:
+ case 0x3713:
+ case 0x3718:
+ case 0x3719:
+ case 0x371a:
+ case 0x371b:
+ case 0x371d:
+ case 0x3729:
+ case 0x385e:
+ case 0x3859:
+ case 0x4c12:
+ case 0x4c13:
+ case 0x4c1d:
+ case 0x4c29:
+ case 0x4d12:
+ case 0x4d13:
+ case 0x4d1d:
+ case 0x4d29:
+ case 0x4e12:
+ case 0x4e13:
+ case 0x4e1d:
+ case 0x4e29:
+ case 0x4f12:
+ case 0x4f13:
+ case 0x4f1d:
+ case 0x4f29:
+ case 0x7207:
+ case 0x7208:
+ case 0x7209:
+ case 0x7227:
+ case 0x7307:
+ case 0x7308:
+ case 0x7309:
+ case 0x7312:
+ case 0x7313:
+ case 0x7318:
+ case 0x7319:
+ case 0x731a:
+ case 0x731b:
+ case 0x731d:
+ case 0x7327:
+ case 0x7329:
+ case 0x8287:
+ case 0x8288:
+ case 0x8289:
+ case 0x82a7:
+ case 0x8387:
+ case 0x8388:
+ case 0x8389:
+ case 0x8392:
+ case 0x8393:
+ case 0x8398:
+ case 0x8399:
+ case 0x839a:
+ case 0x839b:
+ case 0x839d:
+ case 0x83a7:
+ case 0x83a9:
+ case 0x752001:
+ case 0x752039:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt715_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00e5:
+ case 0x00f0:
+ case 0x00f3:
+ case 0x00f5:
+ case 0x2009:
+ case 0x2016:
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x2023:
+ case 0x2230:
+ case 0x200b ... 0x200e: /* i2c read */
+ case 0x2012 ... 0x2015: /* HD-A read */
+ case 0x202d ... 0x202f: /* BRA */
+ case 0x2201 ... 0x2212: /* i2c debug */
+ case 0x2220 ... 0x2223: /* decoded HD-A */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0;
+ unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2;
+ unsigned int is_hda_reg = 1, is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt715->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT715_PRIV_DATA_R_H | nid;
+ ret = regmap_write(rt715->sdw_regmap, reg3,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg4, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x3000) {
+ reg += 0x8000;
+ ret = regmap_write(rt715->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ reg += 0x2000;
+ reg |= 0x800;
+ ret = regmap_write(rt715->sdw_regmap, reg,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x9000) {
+ ret = regmap_write(rt715->sdw_regmap, reg,
+ ((*val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0xb000) {
+ ret = regmap_write(rt715->sdw_regmap, reg, *val);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_read(rt715->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ is_hda_reg = 0;
+ }
+
+ if (is_hda_reg || is_index_reg) {
+ sdw_data_3 = 0;
+ sdw_data_2 = 0;
+ sdw_data_1 = 0;
+ sdw_data_0 = 0;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3,
+ &sdw_data_3);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2,
+ &sdw_data_2);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1,
+ &sdw_data_1);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0,
+ &sdw_data_0);
+ if (ret < 0)
+ return ret;
+ *val = ((sdw_data_3 & 0xff) << 24) |
+ ((sdw_data_2 & 0xff) << 16) |
+ ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff);
+ }
+
+ if (is_hda_reg == 0)
+ dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__,
+ reg, reg2, reg3, reg4, *val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x => %08x\n",
+ __func__, reg, reg2, *val);
+
+ return 0;
+}
+
+static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int reg2 = 0, reg3, reg4, nid, mask, val2;
+ unsigned int is_index_reg = 0;
+ int ret;
+
+ if (reg > 0xffff)
+ is_index_reg = 1;
+
+ mask = reg & 0xf000;
+
+ if (is_index_reg) { /* index registers */
+ val2 = reg & 0xff;
+ reg = reg >> 8;
+ nid = reg & 0xff;
+ ret = regmap_write(rt715->sdw_regmap, reg, 0);
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, val2);
+ if (ret < 0)
+ return ret;
+
+ reg3 = RT715_PRIV_DATA_W_H | nid;
+ ret = regmap_write(rt715->sdw_regmap, reg3,
+ ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg4 = reg3 + 0x1000;
+ reg4 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg4, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ is_index_reg = 1;
+ } else if (reg < 0x4fff) {
+ ret = regmap_write(rt715->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (reg == RT715_FUNC_RESET) {
+ ret = regmap_write(rt715->sdw_regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (mask == 0x7000) {
+ ret = regmap_write(rt715->sdw_regmap, reg,
+ ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ reg2 = reg + 0x1000;
+ reg2 |= 0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ } else if ((reg & 0xff00) == 0x8300) { /* for R channel */
+ reg2 = reg - 0x1000;
+ reg2 &= ~0x80;
+ ret = regmap_write(rt715->sdw_regmap, reg2,
+ ((val >> 8) & 0xff));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (reg2 == 0)
+ dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val);
+ else if (is_index_reg)
+ dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n",
+ __func__, reg, reg2, reg3, reg4, val2, val);
+ else
+ dev_dbg(dev, "[%s] %04x %04x <= %04x\n",
+ __func__, reg, reg2, val);
+
+ return 0;
+}
+
+static const struct regmap_config rt715_regmap = {
+ .reg_bits = 24,
+ .val_bits = 32,
+ .readable_reg = rt715_readable_register, /* Readable registers */
+ .volatile_reg = rt715_volatile_register, /* volatile register */
+ .max_register = 0x752039, /* Maximum number of register */
+ .reg_defaults = rt715_reg_defaults, /* Defaults */
+ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_read = rt715_sdw_read,
+ .reg_write = rt715_sdw_write,
+};
+
+static const struct regmap_config rt715_sdw_regmap = {
+ .name = "sdw",
+ .reg_bits = 32, /* Total register space for SDW */
+ .val_bits = 8, /* Total number of bits in register */
+ .max_register = 0xff01, /* Maximum number of register */
+ .cache_type = REGCACHE_NONE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt715_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt715->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt715_io_init(&slave->dev, slave);
+}
+
+static int rt715_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = false;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x50;/* BITMAP: 01010000 */
+ prop->sink_ports = 0x0; /* BITMAP: 00000000 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ dpn = prop->src_dpn_prop;
+ i = 0;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt715_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ memcpy(&rt715->params, params, sizeof(*params));
+
+ ret = rt715_clock_config(&slave->dev);
+ if (ret < 0)
+ dev_err(&slave->dev, "%s: Invalid clk config", __func__);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops rt715_slave_ops = {
+ .read_prop = rt715_read_prop,
+ .update_status = rt715_update_status,
+ .bus_config = rt715_bus_config,
+};
+
+static int rt715_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *sdw_regmap, *regmap;
+
+ /* Regmap Initialization */
+ sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap);
+ if (IS_ERR(sdw_regmap))
+ return PTR_ERR(sdw_regmap);
+
+ regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev,
+ &rt715_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt715_init(&slave->dev, sdw_regmap, regmap, slave);
+}
+
+static int rt715_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt715_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x2, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt715_id);
+
+static int rt715_dev_suspend(struct device *dev)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+
+ if (!rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, true);
+
+ return 0;
+}
+
+#define RT715_PROBE_TIMEOUT 5000
+
+static int rt715_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT715_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt715->regmap, false);
+ regcache_sync_region(rt715->regmap, 0x3000, 0x8fff);
+ regcache_sync_region(rt715->regmap, 0x752039, 0x752039);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt715_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+ RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+ .driver = {
+ .name = "rt715",
+ .pm = pm_ptr(&rt715_pm),
+ },
+ .probe = rt715_sdw_probe,
+ .remove = rt715_sdw_remove,
+ .ops = &rt715_slave_ops,
+ .id_table = rt715_id,
+};
+module_sdw_driver(rt715_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT715 driver SDW");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h
new file mode 100644
index 000000000000..5d7661e335ae
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdw.h
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt715-sdw.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDW_H__
+#define __RT715_SDW_H__
+
+static const struct reg_default rt715_reg_defaults[] = {
+ { 0x0000, 0x00 },
+ { 0x0001, 0x00 },
+ { 0x0002, 0x00 },
+ { 0x0003, 0x00 },
+ { 0x0004, 0x00 },
+ { 0x0005, 0x01 },
+ { 0x0020, 0x00 },
+ { 0x0022, 0x00 },
+ { 0x0023, 0x00 },
+ { 0x0024, 0x00 },
+ { 0x0025, 0x00 },
+ { 0x0026, 0x00 },
+ { 0x0030, 0x00 },
+ { 0x0032, 0x00 },
+ { 0x0033, 0x00 },
+ { 0x0034, 0x00 },
+ { 0x0035, 0x00 },
+ { 0x0036, 0x00 },
+ { 0x0040, 0x00 },
+ { 0x0041, 0x00 },
+ { 0x0042, 0x00 },
+ { 0x0043, 0x00 },
+ { 0x0044, 0x20 },
+ { 0x0045, 0x01 },
+ { 0x0046, 0x00 },
+ { 0x0050, 0x20 },
+ { 0x0051, 0x02 },
+ { 0x0052, 0x5d },
+ { 0x0053, 0x07 },
+ { 0x0054, 0x15 },
+ { 0x0055, 0x00 },
+ { 0x0060, 0x00 },
+ { 0x0070, 0x00 },
+ { 0x0080, 0x00 },
+ { 0x0088, 0x10 },
+ { 0x00e0, 0x00 },
+ { 0x00e1, 0x00 },
+ { 0x00e2, 0x00 },
+ { 0x00e3, 0x00 },
+ { 0x00e4, 0x00 },
+ { 0x00e5, 0x00 },
+ { 0x00ee, 0x00 },
+ { 0x00ef, 0x00 },
+ { 0x00f0, 0x00 },
+ { 0x00f1, 0x00 },
+ { 0x00f2, 0x00 },
+ { 0x00f3, 0x00 },
+ { 0x00f4, 0x00 },
+ { 0x00f5, 0x00 },
+ { 0x00fe, 0x00 },
+ { 0x00ff, 0x00 },
+ { 0x0200, 0x00 },
+ { 0x0201, 0x00 },
+ { 0x0202, 0x20 },
+ { 0x0203, 0x00 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x03 },
+ { 0x0220, 0x00 },
+ { 0x0221, 0x00 },
+ { 0x0222, 0x00 },
+ { 0x0223, 0x00 },
+ { 0x0224, 0x00 },
+ { 0x0225, 0x00 },
+ { 0x0226, 0x00 },
+ { 0x0227, 0x00 },
+ { 0x0230, 0x00 },
+ { 0x0231, 0x00 },
+ { 0x0232, 0x00 },
+ { 0x0233, 0x00 },
+ { 0x0234, 0x00 },
+ { 0x0235, 0x00 },
+ { 0x0236, 0x00 },
+ { 0x0237, 0x00 },
+ { 0x02e0, 0x00 },
+ { 0x02f0, 0x00 },
+ { 0x0400, 0x00 },
+ { 0x0401, 0x00 },
+ { 0x0402, 0x20 },
+ { 0x0403, 0x00 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x0f },
+ { 0x0420, 0x00 },
+ { 0x0421, 0x00 },
+ { 0x0422, 0x00 },
+ { 0x0423, 0x00 },
+ { 0x0424, 0x00 },
+ { 0x0425, 0x00 },
+ { 0x0426, 0x00 },
+ { 0x0427, 0x00 },
+ { 0x0430, 0x00 },
+ { 0x0431, 0x00 },
+ { 0x0432, 0x00 },
+ { 0x0433, 0x00 },
+ { 0x0434, 0x00 },
+ { 0x0435, 0x00 },
+ { 0x0436, 0x00 },
+ { 0x0437, 0x00 },
+ { 0x04e0, 0x00 },
+ { 0x04f0, 0x00 },
+ { 0x0600, 0x00 },
+ { 0x0601, 0x00 },
+ { 0x0602, 0x20 },
+ { 0x0603, 0x00 },
+ { 0x0604, 0x00 },
+ { 0x0605, 0xff },
+ { 0x0620, 0x00 },
+ { 0x0621, 0x00 },
+ { 0x0622, 0x00 },
+ { 0x0623, 0x00 },
+ { 0x0624, 0x00 },
+ { 0x0625, 0x00 },
+ { 0x0626, 0x00 },
+ { 0x0627, 0x00 },
+ { 0x0630, 0x00 },
+ { 0x0631, 0x00 },
+ { 0x0632, 0x00 },
+ { 0x0633, 0x00 },
+ { 0x0634, 0x00 },
+ { 0x0635, 0x00 },
+ { 0x0636, 0x00 },
+ { 0x0637, 0x00 },
+ { 0x06e0, 0x00 },
+ { 0x06f0, 0x00 },
+ { 0x0f00, 0x00 },
+ { 0x0f01, 0x00 },
+ { 0x0f02, 0x00 },
+ { 0x0f03, 0x00 },
+ { 0x0f04, 0x00 },
+ { 0x0f05, 0xff },
+ { 0x0f06, 0x00 },
+ { 0x0f07, 0x00 },
+ { 0x0f08, 0x00 },
+ { 0x0f09, 0x00 },
+ { 0x0f0a, 0x00 },
+ { 0x0f0b, 0x00 },
+ { 0x0f0c, 0x00 },
+ { 0x0f0d, 0x00 },
+ { 0x0f0e, 0x00 },
+ { 0x0f0f, 0x00 },
+ { 0x0f10, 0x00 },
+ { 0x0f11, 0x00 },
+ { 0x0f12, 0x00 },
+ { 0x0f13, 0x00 },
+ { 0x0f14, 0x00 },
+ { 0x0f15, 0x00 },
+ { 0x0f16, 0x00 },
+ { 0x0f17, 0x00 },
+ { 0x0f18, 0x00 },
+ { 0x0f19, 0x00 },
+ { 0x0f1a, 0x00 },
+ { 0x0f1b, 0x00 },
+ { 0x0f1c, 0x00 },
+ { 0x0f1d, 0x00 },
+ { 0x0f1e, 0x00 },
+ { 0x0f1f, 0x00 },
+ { 0x0f20, 0x00 },
+ { 0x0f21, 0x00 },
+ { 0x0f22, 0x00 },
+ { 0x0f23, 0x00 },
+ { 0x0f24, 0x00 },
+ { 0x0f25, 0x00 },
+ { 0x0f26, 0x00 },
+ { 0x0f27, 0x00 },
+ { 0x0f30, 0x00 },
+ { 0x0f31, 0x00 },
+ { 0x0f32, 0x00 },
+ { 0x0f33, 0x00 },
+ { 0x0f34, 0x00 },
+ { 0x0f35, 0x00 },
+ { 0x0f36, 0x00 },
+ { 0x0f37, 0x00 },
+ { 0x2000, 0x00 },
+ { 0x2001, 0x00 },
+ { 0x2002, 0x00 },
+ { 0x2003, 0x00 },
+ { 0x2004, 0x00 },
+ { 0x2005, 0x00 },
+ { 0x2006, 0x00 },
+ { 0x2007, 0x00 },
+ { 0x2008, 0x00 },
+ { 0x2009, 0x03 },
+ { 0x200a, 0x00 },
+ { 0x200b, 0x00 },
+ { 0x200c, 0x00 },
+ { 0x200d, 0x00 },
+ { 0x200e, 0x00 },
+ { 0x200f, 0x10 },
+ { 0x2010, 0x00 },
+ { 0x2011, 0x00 },
+ { 0x2012, 0x00 },
+ { 0x2013, 0x00 },
+ { 0x2014, 0x00 },
+ { 0x2015, 0x00 },
+ { 0x2016, 0x00 },
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x00 },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2020, 0x00 },
+ { 0x2021, 0x00 },
+ { 0x2022, 0x00 },
+ { 0x2023, 0x00 },
+ { 0x2024, 0x00 },
+ { 0x2025, 0x01 },
+ { 0x2026, 0x00 },
+ { 0x2027, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2200, 0x00 },
+ { 0x2201, 0x00 },
+ { 0x2202, 0x00 },
+ { 0x2203, 0x00 },
+ { 0x2204, 0x00 },
+ { 0x2206, 0x00 },
+ { 0x2207, 0x00 },
+ { 0x2208, 0x00 },
+ { 0x2209, 0x00 },
+ { 0x220a, 0x00 },
+ { 0x220b, 0x00 },
+ { 0x220c, 0x00 },
+ { 0x220d, 0x00 },
+ { 0x220e, 0x00 },
+ { 0x220f, 0x00 },
+ { 0x2210, 0x00 },
+ { 0x2211, 0x00 },
+ { 0x2212, 0x00 },
+ { 0x2220, 0x00 },
+ { 0x2221, 0x00 },
+ { 0x2222, 0x00 },
+ { 0x2223, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x0f },
+ { 0x2232, 0x00 },
+ { 0x2233, 0x00 },
+ { 0x2234, 0x00 },
+ { 0x2235, 0x00 },
+ { 0x2236, 0x00 },
+ { 0x2237, 0x00 },
+ { 0x2238, 0x00 },
+ { 0x2239, 0x00 },
+ { 0x22f0, 0x00 },
+ { 0x22f1, 0x00 },
+ { 0x22f2, 0x00 },
+ { 0x22f3, 0x00 },
+ { 0x3122, 0x02 },
+ { 0x3123, 0x03 },
+ { 0x3124, 0x00 },
+ { 0x3125, 0x01 },
+ { 0x3607, 0x00 },
+ { 0x3608, 0x00 },
+ { 0x3609, 0x00 },
+ { 0x3610, 0x00 },
+ { 0x3611, 0x00 },
+ { 0x3627, 0x00 },
+ { 0x3712, 0x00 },
+ { 0x3713, 0x00 },
+ { 0x3718, 0x00 },
+ { 0x3719, 0x00 },
+ { 0x371a, 0x00 },
+ { 0x371b, 0x00 },
+ { 0x371d, 0x00 },
+ { 0x3729, 0x00 },
+ { 0x385e, 0x00 },
+ { 0x3859, 0x00 },
+ { 0x4c12, 0x411111f0 },
+ { 0x4c13, 0x411111f0 },
+ { 0x4c1d, 0x411111f0 },
+ { 0x4c29, 0x411111f0 },
+ { 0x4d12, 0x411111f0 },
+ { 0x4d13, 0x411111f0 },
+ { 0x4d1d, 0x411111f0 },
+ { 0x4d29, 0x411111f0 },
+ { 0x4e12, 0x411111f0 },
+ { 0x4e13, 0x411111f0 },
+ { 0x4e1d, 0x411111f0 },
+ { 0x4e29, 0x411111f0 },
+ { 0x4f12, 0x411111f0 },
+ { 0x4f13, 0x411111f0 },
+ { 0x4f1d, 0x411111f0 },
+ { 0x4f29, 0x411111f0 },
+ { 0x7207, 0x00 },
+ { 0x8287, 0x00 },
+ { 0x7208, 0x00 },
+ { 0x8288, 0x00 },
+ { 0x7209, 0x00 },
+ { 0x8289, 0x00 },
+ { 0x7227, 0x00 },
+ { 0x82a7, 0x00 },
+ { 0x7307, 0x97 },
+ { 0x8387, 0x97 },
+ { 0x7308, 0x97 },
+ { 0x8388, 0x97 },
+ { 0x7309, 0x97 },
+ { 0x8389, 0x97 },
+ { 0x7312, 0x00 },
+ { 0x8392, 0x00 },
+ { 0x7313, 0x00 },
+ { 0x8393, 0x00 },
+ { 0x7318, 0x00 },
+ { 0x8398, 0x00 },
+ { 0x7319, 0x00 },
+ { 0x8399, 0x00 },
+ { 0x731a, 0x00 },
+ { 0x839a, 0x00 },
+ { 0x731b, 0x00 },
+ { 0x839b, 0x00 },
+ { 0x731d, 0x00 },
+ { 0x839d, 0x00 },
+ { 0x7327, 0x97 },
+ { 0x83a7, 0x97 },
+ { 0x7329, 0x00 },
+ { 0x83a9, 0x00 },
+ { 0x752039, 0xa500 },
+};
+
+#endif /* __RT715_H__ */
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
new file mode 100644
index 000000000000..0881826de2f1
--- /dev/null
+++ b/sound/soc/codecs/rt715.c
@@ -0,0 +1,1133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rt715.c -- rt715 ALSA SoC audio driver
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ *
+ * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/hda_verbs.h>
+
+#include "rt715.h"
+
+static int rt715_index_write(struct regmap *regmap, unsigned int reg,
+ unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0) {
+ pr_err("%s: Failed to set private value: %08x <= %04x %d\n",
+ __func__, addr, value, ret);
+ }
+
+ return ret;
+}
+
+static int rt715_index_write_nid(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+static int rt715_index_read_nid(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt715_index_update_bits(struct regmap *regmap, unsigned int nid,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp, orig;
+ int ret;
+
+ ret = rt715_index_read_nid(regmap, nid, reg, &orig);
+ if (ret < 0)
+ return ret;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ return rt715_index_write_nid(regmap, nid, reg, tmp);
+}
+
+static void rt715_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT715_FUNC_RESET, 0);
+ rt715_index_update_bits(regmap, RT715_VENDOR_REGISTERS,
+ RT715_VD_CLEAR_CTRL, RT715_CLEAR_HIDDEN_REG,
+ RT715_CLEAR_HIDDEN_REG);
+}
+
+
+static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h,
+ unsigned int addr_l, unsigned int val_h,
+ unsigned int *r_val, unsigned int *l_val)
+{
+ int ret;
+ /* R Channel */
+ *r_val = val_h << 8;
+ ret = regmap_read(rt715->regmap, addr_l, r_val);
+ if (ret < 0)
+ pr_err("Failed to get R channel gain.\n");
+
+ /* L Channel */
+ val_h |= 0x20;
+ *l_val = val_h << 8;
+ ret = regmap_read(rt715->regmap, addr_h, l_val);
+ if (ret < 0)
+ pr_err("Failed to get L channel gain.\n");
+}
+
+/* For Verb-Set Amplifier Gain (Verb ID = 3h) */
+static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ unsigned int addr_h, addr_l, val_h, val_ll, val_lr;
+ unsigned int read_ll, read_rl, i;
+ unsigned int k_vol_changed = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_2ch_vol_ori[i]) {
+ k_vol_changed = 1;
+ break;
+ }
+ }
+
+ /* Can't use update bit function, so read the original value first */
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+
+ if (mc->shift == RT715_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* L Channel */
+ rt715->kctl_2ch_vol_ori[0] = ucontrol->value.integer.value[0];
+ /* for gain */
+ val_ll = ((ucontrol->value.integer.value[0]) & 0x7f);
+ if (val_ll > mc->max)
+ val_ll = mc->max;
+ /* keep mute status */
+ val_ll |= read_ll & 0x80;
+
+ /* R Channel */
+ rt715->kctl_2ch_vol_ori[1] = ucontrol->value.integer.value[1];
+ /* for gain */
+ val_lr = ((ucontrol->value.integer.value[1]) & 0x7f);
+ if (val_lr > mc->max)
+ val_lr = mc->max;
+ /* keep mute status */
+ val_lr |= read_rl & 0x80;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << mc->shift) | (3 << 4);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_ll);
+ } else {
+ /* Lch*/
+ val_h = (1 << mc->shift) | (1 << 5);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ /* Rch */
+ val_h = (1 << mc->shift) | (1 << 4);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_lr);
+ }
+ /* check result */
+ if (mc->shift == RT715_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt715_get_gain(rt715, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+
+ /* D0:power on state, D3: power saving mode */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return k_vol_changed;
+}
+
+static int rt715_set_amp_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int addr_h, addr_l, val_h;
+ unsigned int read_ll, read_rl;
+
+ addr_h = mc->reg;
+ addr_l = mc->rreg;
+ if (mc->shift == RT715_DIR_OUT_SFT) /* output */
+ val_h = 0x80;
+ else /* input */
+ val_h = 0x0;
+
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (mc->invert) {
+ /* for mute status */
+ read_ll = !(read_ll & 0x80);
+ read_rl = !(read_rl & 0x80);
+ } else {
+ /* for gain */
+ read_ll = read_ll & 0x7f;
+ read_rl = read_rl & 0x7f;
+ }
+ ucontrol->value.integer.value[0] = read_ll;
+ ucontrol->value.integer.value[1] = read_rl;
+
+ return 0;
+}
+
+static int rt715_set_main_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L };
+ unsigned int addr_h, addr_l, val_h = 0x0, val_ll, val_lr;
+ unsigned int k_shift = RT715_DIR_IN_SFT, k_changed = 0;
+ unsigned int read_ll, read_rl, i, j, loop_cnt = 4;
+
+ for (i = 0; i < 8; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_switch_ori[i])
+ k_changed = 1;
+ }
+
+ for (j = 0; j < loop_cnt; j++) {
+ /* Can't use update bit function, so read the original value first */
+ addr_h = capture_reg_H[j];
+ addr_l = capture_reg_L[j];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* L Channel */
+ /* for mute */
+ rt715->kctl_8ch_switch_ori[j * 2] =
+ ucontrol->value.integer.value[j * 2];
+ val_ll = (!ucontrol->value.integer.value[j * 2]) << 7;
+ /* keep gain */
+ val_ll |= read_ll & 0x7f;
+
+ /* R Channel */
+ /* for mute */
+ rt715->kctl_8ch_switch_ori[j * 2 + 1] =
+ ucontrol->value.integer.value[j * 2 + 1];
+ val_lr = (!ucontrol->value.integer.value[j * 2 + 1]) << 7;
+ /* keep gain */
+ val_lr |= read_rl & 0x7f;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << k_shift) | (3 << 4);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_ll);
+ } else {
+ /* Lch*/
+ val_h = (1 << k_shift) | (1 << 5);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ /* Rch */
+ val_h = (1 << k_shift) | (1 << 4);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_lr);
+ }
+ val_h = 0x0;
+ rt715_get_gain(rt715, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+ }
+
+ /* D0:power on state, D3: power saving mode */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return k_changed;
+}
+
+static int rt715_set_main_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L };
+ unsigned int addr_h, addr_l, val_h = 0x0, i, loop_cnt = 4;
+ unsigned int read_ll, read_rl;
+
+ for (i = 0; i < loop_cnt; i++) {
+ addr_h = capture_reg_H[i];
+ addr_l = capture_reg_L[i];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ ucontrol->value.integer.value[i * 2] = !(read_ll & 0x80);
+ ucontrol->value.integer.value[i * 2 + 1] = !(read_rl & 0x80);
+ }
+
+ return 0;
+}
+
+static int rt715_set_main_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L};
+ unsigned int addr_h, addr_l, val_h = 0x0, val_ll, val_lr;
+ unsigned int read_ll, read_rl, i, j, loop_cnt = 4, k_changed = 0;
+ unsigned int k_shift = RT715_DIR_IN_SFT, k_max = 0x3f;
+
+ for (i = 0; i < 8; i++) {
+ if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_vol_ori[i])
+ k_changed = 1;
+ }
+
+ for (j = 0; j < loop_cnt; j++) {
+ addr_h = capture_reg_H[j];
+ addr_l = capture_reg_L[j];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0);
+
+ /* L Channel */
+ /* for gain */
+ rt715->kctl_8ch_vol_ori[j * 2] = ucontrol->value.integer.value[j * 2];
+ val_ll = ((ucontrol->value.integer.value[j * 2]) & 0x7f);
+ if (val_ll > k_max)
+ val_ll = k_max;
+ /* keep mute status */
+ val_ll |= read_ll & 0x80;
+
+ /* R Channel */
+ /* for gain */
+ rt715->kctl_8ch_vol_ori[j * 2 + 1] =
+ ucontrol->value.integer.value[j * 2 + 1];
+ val_lr = ((ucontrol->value.integer.value[j * 2 + 1]) & 0x7f);
+ if (val_lr > k_max)
+ val_lr = k_max;
+ /* keep mute status */
+ val_lr |= read_rl & 0x80;
+
+ for (i = 0; i < 3; i++) { /* retry 3 times at most */
+ if (val_ll == val_lr) {
+ /* Set both L/R channels at the same time */
+ val_h = (1 << k_shift) | (3 << 4);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_ll);
+ } else {
+ /* Lch*/
+ val_h = (1 << k_shift) | (1 << 5);
+ regmap_write(rt715->regmap, addr_h,
+ (val_h << 8) | val_ll);
+ /* Rch */
+ val_h = (1 << k_shift) | (1 << 4);
+ regmap_write(rt715->regmap, addr_l,
+ (val_h << 8) | val_lr);
+ }
+ val_h = 0x0;
+ rt715_get_gain(rt715, addr_h, addr_l, val_h,
+ &read_rl, &read_ll);
+ if (read_rl == val_lr && read_ll == val_ll)
+ break;
+ }
+ }
+
+ /* D0:power on state, D3: power saving mode */
+ if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY)
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+ return k_changed;
+}
+
+static int rt715_set_main_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ static const unsigned int capture_reg_H[] = {
+ RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H,
+ RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H };
+ static const unsigned int capture_reg_L[] = {
+ RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L,
+ RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L };
+ unsigned int addr_h, addr_l, val_h = 0x0, i, loop_cnt = 4;
+ unsigned int read_ll, read_rl;
+
+ for (i = 0; i < loop_cnt; i++) {
+ addr_h = capture_reg_H[i];
+ addr_l = capture_reg_L[i];
+ rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll);
+
+ ucontrol->value.integer.value[i * 2] = read_ll & 0x7f;
+ ucontrol->value.integer.value[i * 2 + 1] = read_rl & 0x7f;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static int rt715_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 8;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int rt715_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 8;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x3f;
+ return 0;
+}
+
+#define RT715_MAIN_SWITCH_EXT(xname, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt715_switch_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+}
+
+#define RT715_MAIN_VOL_EXT_TLV(xname, xhandler_get, xhandler_put, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt715_vol_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+}
+
+static const struct snd_kcontrol_new rt715_snd_controls[] = {
+ /* Capture switch */
+ RT715_MAIN_SWITCH_EXT("Capture Switch",
+ rt715_set_main_switch_get, rt715_set_main_switch_put),
+ /* Volume Control */
+ RT715_MAIN_VOL_EXT_TLV("Capture Volume",
+ rt715_set_main_vol_get, rt715_set_main_vol_put, in_vol_tlv),
+ /* MIC Boost Control */
+ SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H,
+ RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H,
+ RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H,
+ RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H,
+ RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H,
+ RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H,
+ RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H,
+ RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H,
+ RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0,
+ rt715_set_amp_gain_get, rt715_set_amp_gain_put,
+ mic_vol_tlv),
+};
+
+static int rt715_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg, val;
+ int ret;
+
+ /* nid = e->reg, vid = 0xf01 */
+ reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
+ ret = regmap_read(rt715->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * The first two indices of ADC Mux 24/25 are routed to the same
+ * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
+ * To have a unique set of inputs, we skip the index1 of the muxes.
+ */
+ if ((e->reg == RT715_MUX_IN3 || e->reg == RT715_MUX_IN4) && (val > 0))
+ val -= 1;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt715_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, reg;
+ int ret;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ /* Verb ID = 0x701h, nid = e->reg */
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
+ ret = regmap_read(rt715->regmap, reg, &val2);
+ if (ret < 0) {
+ dev_err(component->dev, "%s: sdw read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ reg = RT715_VERB_SET_CONNECT_SEL | e->reg;
+ regmap_write(rt715->regmap, reg, val);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc_22_23_mux_text[] = {
+ "MIC1",
+ "MIC2",
+ "LINE1",
+ "LINE2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+/*
+ * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
+ * 1 will be connected to the same dmic source, therefore we skip index 1 to
+ * avoid misunderstanding on usage of dapm routing.
+ */
+static const unsigned int rt715_adc_24_25_values[] = {
+ 0,
+ 2,
+ 3,
+ 4,
+ 5,
+};
+
+static const char * const adc_24_mux_text[] = {
+ "MIC2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static const char * const adc_25_mux_text[] = {
+ "MIC1",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "DMIC4",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt715_adc22_enum, RT715_MUX_IN1, 0, adc_22_23_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt715_adc23_enum, RT715_MUX_IN2, 0, adc_22_23_mux_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
+ RT715_MUX_IN3, 0, 0xf,
+ adc_24_mux_text, rt715_adc_24_25_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
+ RT715_MUX_IN4, 0, 0xf,
+ adc_25_mux_text, rt715_adc_24_25_values);
+
+static const struct snd_kcontrol_new rt715_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc24_mux =
+ SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
+ rt715_mux_get, rt715_mux_put);
+
+static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_ADC("ADC 07", NULL, RT715_SET_STREAMID_MIC_ADC, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 08", NULL, RT715_SET_STREAMID_LINE_ADC, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 09", NULL, RT715_SET_STREAMID_MIX_ADC, 4, 0),
+ SND_SOC_DAPM_ADC("ADC 27", NULL, RT715_SET_STREAMID_MIX_ADC2, 4, 0),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc23_mux),
+ SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc24_mux),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt715_adc25_mux),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt715_audio_map[] = {
+ {"DP6TX", NULL, "ADC 09"},
+ {"DP6TX", NULL, "ADC 08"},
+ {"DP4TX", NULL, "ADC 07"},
+ {"DP4TX", NULL, "ADC 27"},
+ {"ADC 09", NULL, "ADC 22 Mux"},
+ {"ADC 08", NULL, "ADC 23 Mux"},
+ {"ADC 07", NULL, "ADC 24 Mux"},
+ {"ADC 27", NULL, "ADC 25 Mux"},
+ {"ADC 22 Mux", "MIC1", "MIC1"},
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 22 Mux", "DMIC1", "DMIC1"},
+ {"ADC 22 Mux", "DMIC2", "DMIC2"},
+ {"ADC 22 Mux", "DMIC3", "DMIC3"},
+ {"ADC 22 Mux", "DMIC4", "DMIC4"},
+ {"ADC 23 Mux", "MIC1", "MIC1"},
+ {"ADC 23 Mux", "MIC2", "MIC2"},
+ {"ADC 23 Mux", "LINE1", "LINE1"},
+ {"ADC 23 Mux", "LINE2", "LINE2"},
+ {"ADC 23 Mux", "DMIC1", "DMIC1"},
+ {"ADC 23 Mux", "DMIC2", "DMIC2"},
+ {"ADC 23 Mux", "DMIC3", "DMIC3"},
+ {"ADC 23 Mux", "DMIC4", "DMIC4"},
+ {"ADC 24 Mux", "MIC2", "MIC2"},
+ {"ADC 24 Mux", "DMIC1", "DMIC1"},
+ {"ADC 24 Mux", "DMIC2", "DMIC2"},
+ {"ADC 24 Mux", "DMIC3", "DMIC3"},
+ {"ADC 24 Mux", "DMIC4", "DMIC4"},
+ {"ADC 25 Mux", "MIC1", "MIC1"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 25 Mux", "DMIC3", "DMIC3"},
+ {"ADC 25 Mux", "DMIC4", "DMIC4"},
+};
+
+static int rt715_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D0);
+ msleep(RT715_POWER_UP_DELAY_MS);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(rt715->regmap,
+ RT715_SET_AUDIO_POWER_STATE,
+ AC_PWRST_D3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt715_probe(struct snd_soc_component *component)
+{
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (!rt715->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rt715 = {
+ .probe = rt715_probe,
+ .set_bias_level = rt715_set_bias_level,
+ .controls = rt715_snd_controls,
+ .num_controls = ARRAY_SIZE(rt715_snd_controls),
+ .dapm_widgets = rt715_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets),
+ .dapm_routes = rt715_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt715_audio_map),
+ .endianness = 1,
+};
+
+static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt715_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int retval;
+ unsigned int val = 0;
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ switch (dai->id) {
+ case RT715_AIF1:
+ port_config.num = 6;
+ rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa500);
+ break;
+ case RT715_AIF2:
+ port_config.num = 4;
+ rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000);
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt715->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */
+ case 44100:
+ val |= 0x40 << 8;
+ break;
+ case 48000:
+ val |= 0x0 << 8;
+ break;
+ default:
+ dev_err(component->dev, "%s: Unsupported sample rate %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 8:
+ break;
+ case 16:
+ val |= (0x1 << 4);
+ break;
+ case 20:
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ val |= (0x3 << 4);
+ break;
+ case 32:
+ val |= (0x4 << 4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt715->regmap, RT715_MIC_ADC_FORMAT_H, val);
+ regmap_write(rt715->regmap, RT715_MIC_LINE_FORMAT_H, val);
+ regmap_write(rt715->regmap, RT715_MIX_ADC_FORMAT_H, val);
+ regmap_write(rt715->regmap, RT715_MIX_ADC2_FORMAT_H, val);
+
+ return retval;
+}
+
+static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt715->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt715->slave, sdw_stream);
+ return 0;
+}
+
+#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt715_ops = {
+ .hw_params = rt715_pcm_hw_params,
+ .hw_free = rt715_pcm_hw_free,
+ .set_stream = rt715_set_sdw_stream,
+ .shutdown = rt715_shutdown,
+};
+
+static struct snd_soc_dai_driver rt715_dai[] = {
+ {
+ .name = "rt715-aif1",
+ .id = RT715_AIF1,
+ .capture = {
+ .stream_name = "DP6 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_ops,
+ },
+ {
+ .name = "rt715-aif2",
+ .id = RT715_AIF2,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT715_STEREO_RATES,
+ .formats = RT715_FORMATS,
+ },
+ .ops = &rt715_ops,
+ },
+};
+
+/* Bus clock frequency */
+#define RT715_CLK_FREQ_9600000HZ 9600000
+#define RT715_CLK_FREQ_12000000HZ 12000000
+#define RT715_CLK_FREQ_6000000HZ 6000000
+#define RT715_CLK_FREQ_4800000HZ 4800000
+#define RT715_CLK_FREQ_2400000HZ 2400000
+#define RT715_CLK_FREQ_12288000HZ 12288000
+
+int rt715_clock_config(struct device *dev)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+ unsigned int clk_freq, value;
+
+ clk_freq = (rt715->params.curr_dr_freq >> 1);
+
+ switch (clk_freq) {
+ case RT715_CLK_FREQ_12000000HZ:
+ value = 0x0;
+ break;
+ case RT715_CLK_FREQ_6000000HZ:
+ value = 0x1;
+ break;
+ case RT715_CLK_FREQ_9600000HZ:
+ value = 0x2;
+ break;
+ case RT715_CLK_FREQ_4800000HZ:
+ value = 0x3;
+ break;
+ case RT715_CLK_FREQ_2400000HZ:
+ value = 0x4;
+ break;
+ case RT715_CLK_FREQ_12288000HZ:
+ value = 0x5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(rt715->regmap, 0xe0, value);
+ regmap_write(rt715->regmap, 0xf0, value);
+
+ return 0;
+}
+
+int rt715_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt715_priv *rt715;
+ int ret;
+
+ rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
+ if (!rt715)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt715);
+ rt715->slave = slave;
+ rt715->regmap = regmap;
+ rt715->sdw_regmap = sdw_regmap;
+
+ regcache_cache_only(rt715->regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt715->hw_init = false;
+ rt715->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_rt715,
+ rt715_dai,
+ ARRAY_SIZE(rt715_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ return 0;
+}
+
+int rt715_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt715_priv *rt715 = dev_get_drvdata(dev);
+
+ if (rt715->hw_init)
+ return 0;
+
+ regcache_cache_only(rt715->regmap, false);
+
+ /*
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
+ */
+ if (!rt715->first_hw_init)
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt715_reset(rt715->regmap);
+
+ /* Mute nid=08h/09h */
+ regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080);
+ regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080);
+ /* Mute nid=07h/27h */
+ regmap_write(rt715->regmap, RT715_SET_GAIN_MIC_ADC_H, 0xb080);
+ regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC2_H, 0xb080);
+
+ /* Set Pin Widget */
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20);
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20);
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC3, 0x20);
+ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC4, 0x20);
+ /* Set Converter Stream */
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10);
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10);
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_MIC_ADC, 0x10);
+ regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC2, 0x10);
+ /* Set Configuration Default */
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT1, 0xd0);
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT4, 0x81);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT1, 0xd1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT1, 0xd0);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT4, 0x81);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT1, 0xd1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT2, 0x11);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT3, 0xa1);
+ regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT4, 0x81);
+
+ /* Finish Initial Settings, set power to D3 */
+ regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+ if (rt715->first_hw_init)
+ regcache_mark_dirty(rt715->regmap);
+ else
+ rt715->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt715->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver");
+MODULE_DESCRIPTION("ASoC rt715 driver SDW");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h
new file mode 100644
index 000000000000..a0c56aa1003a
--- /dev/null
+++ b/sound/soc/codecs/rt715.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rt715.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2019 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_H__
+#define __RT715_H__
+
+#include <linux/regulator/consumer.h>
+
+struct rt715_priv {
+ struct regmap *regmap;
+ struct regmap *sdw_regmap;
+ struct snd_soc_codec *codec;
+ struct sdw_slave *slave;
+ int dbg_nid;
+ int dbg_vid;
+ int dbg_payload;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ unsigned int kctl_2ch_vol_ori[2];
+ unsigned int kctl_8ch_switch_ori[8];
+ unsigned int kctl_8ch_vol_ori[8];
+};
+
+/* NID */
+#define RT715_AUDIO_FUNCTION_GROUP 0x01
+#define RT715_MIC_ADC 0x07
+#define RT715_LINE_ADC 0x08
+#define RT715_MIX_ADC 0x09
+#define RT715_DMIC1 0x12
+#define RT715_DMIC2 0x13
+#define RT715_MIC1 0x18
+#define RT715_MIC2 0x19
+#define RT715_LINE1 0x1a
+#define RT715_LINE2 0x1b
+#define RT715_DMIC3 0x1d
+#define RT715_DMIC4 0x29
+#define RT715_VENDOR_REGISTERS 0x20
+#define RT715_MUX_IN1 0x22
+#define RT715_MUX_IN2 0x23
+#define RT715_MUX_IN3 0x24
+#define RT715_MUX_IN4 0x25
+#define RT715_MIX_ADC2 0x27
+#define RT715_INLINE_CMD 0x55
+
+/* Index (NID:20h) */
+#define RT715_VD_CLEAR_CTRL 0x01
+#define RT715_SDW_INPUT_SEL 0x39
+#define RT715_EXT_DMIC_CLK_CTRL2 0x54
+
+/* Verb */
+#define RT715_VERB_SET_CONNECT_SEL 0x3100
+#define RT715_VERB_GET_CONNECT_SEL 0xb100
+#define RT715_VERB_SET_EAPD_BTLENABLE 0x3c00
+#define RT715_VERB_SET_POWER_STATE 0x3500
+#define RT715_VERB_SET_CHANNEL_STREAMID 0x3600
+#define RT715_VERB_SET_PIN_WIDGET_CONTROL 0x3700
+#define RT715_VERB_SET_CONFIG_DEFAULT1 0x4c00
+#define RT715_VERB_SET_CONFIG_DEFAULT2 0x4d00
+#define RT715_VERB_SET_CONFIG_DEFAULT3 0x4e00
+#define RT715_VERB_SET_CONFIG_DEFAULT4 0x4f00
+#define RT715_VERB_SET_UNSOLICITED_ENABLE 0x3800
+#define RT715_SET_AMP_GAIN_MUTE_H 0x7300
+#define RT715_SET_AMP_GAIN_MUTE_L 0x8380
+#define RT715_READ_HDA_3 0x2012
+#define RT715_READ_HDA_2 0x2013
+#define RT715_READ_HDA_1 0x2014
+#define RT715_READ_HDA_0 0x2015
+#define RT715_PRIV_INDEX_W_H 0x7520
+#define RT715_PRIV_INDEX_W_L 0x85a0
+#define RT715_PRIV_INDEX_W_H_2 0x7500
+#define RT715_PRIV_INDEX_W_L_2 0x8580
+#define RT715_PRIV_DATA_W_H 0x7420
+#define RT715_PRIV_DATA_W_L 0x84a0
+#define RT715_PRIV_INDEX_R_H 0x9d20
+#define RT715_PRIV_INDEX_R_L 0xada0
+#define RT715_PRIV_DATA_R_H 0x9c20
+#define RT715_PRIV_DATA_R_L 0xaca0
+#define RT715_MIC_ADC_FORMAT_H 0x7207
+#define RT715_MIC_ADC_FORMAT_L 0x8287
+#define RT715_MIC_LINE_FORMAT_H 0x7208
+#define RT715_MIC_LINE_FORMAT_L 0x8288
+#define RT715_MIX_ADC_FORMAT_H 0x7209
+#define RT715_MIX_ADC_FORMAT_L 0x8289
+#define RT715_MIX_ADC2_FORMAT_H 0x7227
+#define RT715_MIX_ADC2_FORMAT_L 0x82a7
+#define RT715_FUNC_RESET 0xff01
+
+#define RT715_SET_AUDIO_POWER_STATE\
+ (RT715_VERB_SET_POWER_STATE | RT715_AUDIO_FUNCTION_GROUP)
+#define RT715_SET_PIN_DMIC1\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC1)
+#define RT715_SET_PIN_DMIC2\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC2)
+#define RT715_SET_PIN_DMIC3\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC3)
+#define RT715_SET_PIN_DMIC4\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC4)
+#define RT715_SET_PIN_MIC1\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC1)
+#define RT715_SET_PIN_MIC2\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC2)
+#define RT715_SET_PIN_LINE1\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE1)
+#define RT715_SET_PIN_LINE2\
+ (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE2)
+#define RT715_SET_MIC1_UNSOLICITED_ENABLE\
+ (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC1)
+#define RT715_SET_MIC2_UNSOLICITED_ENABLE\
+ (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC2)
+#define RT715_SET_STREAMID_MIC_ADC\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIC_ADC)
+#define RT715_SET_STREAMID_LINE_ADC\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_LINE_ADC)
+#define RT715_SET_STREAMID_MIX_ADC\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC)
+#define RT715_SET_STREAMID_MIX_ADC2\
+ (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC2)
+#define RT715_SET_GAIN_MIC_ADC_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC_ADC)
+#define RT715_SET_GAIN_MIC_ADC_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC_ADC)
+#define RT715_SET_GAIN_LINE_ADC_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE_ADC)
+#define RT715_SET_GAIN_LINE_ADC_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE_ADC)
+#define RT715_SET_GAIN_MIX_ADC_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC)
+#define RT715_SET_GAIN_MIX_ADC_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC)
+#define RT715_SET_GAIN_MIX_ADC2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC2)
+#define RT715_SET_GAIN_MIX_ADC2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC2)
+#define RT715_SET_GAIN_DMIC1_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC1)
+#define RT715_SET_GAIN_DMIC1_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC1)
+#define RT715_SET_GAIN_DMIC2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC2)
+#define RT715_SET_GAIN_DMIC2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC2)
+#define RT715_SET_GAIN_DMIC3_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC3)
+#define RT715_SET_GAIN_DMIC3_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC3)
+#define RT715_SET_GAIN_DMIC4_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC4)
+#define RT715_SET_GAIN_DMIC4_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC4)
+#define RT715_SET_GAIN_MIC1_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC1)
+#define RT715_SET_GAIN_MIC1_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC1)
+#define RT715_SET_GAIN_MIC2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC2)
+#define RT715_SET_GAIN_MIC2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC2)
+#define RT715_SET_GAIN_LINE1_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE1)
+#define RT715_SET_GAIN_LINE1_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE1)
+#define RT715_SET_GAIN_LINE2_L\
+ (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE2)
+#define RT715_SET_GAIN_LINE2_H\
+ (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC2)
+#define RT715_SET_DMIC1_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1)
+#define RT715_SET_DMIC2_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT1\
+ (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC4)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT2\
+ (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC4)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT3\
+ (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC4)
+#define RT715_SET_DMIC3_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC3)
+#define RT715_SET_DMIC4_CONFIG_DEFAULT4\
+ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4)
+
+/* vendor register clear ctrl-1 (0x01)(NID:20h) */
+#define RT715_CLEAR_HIDDEN_REG (0x1 << 15)
+
+
+#define RT715_MUTE_SFT 7
+#define RT715_DIR_IN_SFT 6
+#define RT715_DIR_OUT_SFT 7
+
+enum {
+ RT715_AIF1,
+ RT715_AIF2,
+};
+
+#define RT715_POWER_UP_DELAY_MS 400
+
+int rt715_io_init(struct device *dev, struct sdw_slave *slave);
+int rt715_init(struct device *dev, struct regmap *sdw_regmap,
+ struct regmap *regmap, struct sdw_slave *slave);
+
+int rt715_clock_config(struct device *dev);
+#endif /* __RT715_H__ */
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
new file mode 100644
index 000000000000..4d8a12b13015
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca-sdw.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt721-sdca.h"
+#include "rt721-sdca-sdw.h"
+#include "rt-sdw-common.h"
+
+static bool rt721_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35:
+ case 0x2f50:
+ case 0x2f51:
+ case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+ RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+ RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01:
+ case 0x2f51:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+ RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+ RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0900004 ... 0x0900009:
+ case 0x0a00005:
+ case 0x0c00005:
+ case 0x0d00014:
+ case 0x0310100:
+ case 0x2000000 ... 0x2000003:
+ case 0x2000013:
+ case 0x200002c:
+ case 0x200003c:
+ case 0x2000046:
+ case 0x5810000:
+ case 0x5810036:
+ case 0x5810037:
+ case 0x5810038:
+ case 0x5810039:
+ case 0x5b10018:
+ case 0x5b10019:
+ case 0x5f00045:
+ case 0x5f00048:
+ case 0x6100000:
+ case 0x6100005:
+ case 0x6100006:
+ case 0x610000d:
+ case 0x6100010:
+ case 0x6100011:
+ case 0x6100013:
+ case 0x6100015:
+ case 0x6100017:
+ case 0x6100025:
+ case 0x6100029:
+ case 0x610002c ... 0x610002f:
+ case 0x6100053 ... 0x6100055:
+ case 0x6100057:
+ case 0x610005a:
+ case 0x610005b:
+ case 0x610006a:
+ case 0x610006d:
+ case 0x6100092:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0310100:
+ case 0x0900005:
+ case 0x0900009:
+ case 0x0a00005:
+ case 0x0c00005:
+ case 0x0d00014:
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x200002c:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ case 0x5810000:
+ case 0x5810036:
+ case 0x5810037:
+ case 0x5810038:
+ case 0x5810039:
+ case 0x5b10018:
+ case 0x5b10019:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt721_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt721_sdca_readable_register,
+ .volatile_reg = rt721_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt721_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt721_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt721_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt721_sdca_mbq_readable_register,
+ .volatile_reg = rt721_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt721_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt721_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt721_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt721->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt721->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt721_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt721->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt721_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt721_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ sdw_slave_read_prop(slave);
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /*
+ * port = 1 for headphone playback
+ * port = 2 for headset-mic capture
+ * port = 3 for speaker playback
+ * port = 6 for digital-mic capture
+ */
+ prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 1380;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ /* Three data lanes are supported by rt721-sdca codec */
+ prop->lane_control_support = true;
+
+ return 0;
+}
+
+static int rt721_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ if (cancel_delayed_work_sync(&rt721->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt721->scp_sdca_stat2)
+ scp_sdca_stat2 = rt721->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt721->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+
+ rt721->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+
+ rt721->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt721->scp_sdca_stat2 |= scp_sdca_stat2;
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt721->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+
+ if (status->sdca_cascade && !rt721->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_detect_work, msecs_to_jiffies(280));
+
+ mutex_unlock(&rt721->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt721->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt721_sdca_slave_ops = {
+ .read_prop = rt721_sdca_read_prop,
+ .interrupt_callback = rt721_sdca_interrupt_callback,
+ .update_status = rt721_sdca_update_status,
+};
+
+static int rt721_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt721_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt721_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt721_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt721_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+ if (rt721->hw_init) {
+ cancel_delayed_work_sync(&rt721->jack_detect_work);
+ cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+ }
+
+ if (rt721->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt721->calibrate_mutex);
+ mutex_destroy(&rt721->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt721_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x721, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt721_sdca_id);
+
+static int rt721_sdca_dev_suspend(struct device *dev)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+ if (!rt721->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt721->jack_detect_work);
+ cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+
+ regcache_cache_only(rt721->regmap, true);
+ regcache_cache_only(rt721->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt721_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt721_sdca_priv *rt721_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt721_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt721_sdca->disable_irq_lock);
+ rt721_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt721_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt721_sdca_dev_suspend(dev);
+}
+
+#define RT721_PROBE_TIMEOUT 5000
+
+static int rt721_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt721->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt721->disable_irq_lock);
+ if (rt721->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt721->disable_irq = false;
+ }
+ mutex_unlock(&rt721->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT721_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt721->regmap, false);
+ regcache_sync(rt721->regmap);
+ regcache_cache_only(rt721->mbq_regmap, false);
+ regcache_sync(rt721->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt721_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt721_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt721-sdca",
+ .owner = THIS_MODULE,
+ .pm = pm_ptr(&rt721_sdca_pm),
+ },
+ .probe = rt721_sdca_sdw_probe,
+ .remove = rt721_sdca_sdw_remove,
+ .ops = &rt721_sdca_slave_ops,
+ .id_table = rt721_sdca_id,
+};
+module_sdw_driver(rt721_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h
new file mode 100644
index 000000000000..214b31b82583
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca-sdw.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_SDW_H__
+#define __RT721_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt721_sdca_reg_defaults[] = {
+ { 0x202d, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x08 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0e },
+ { 0x2f06, 0x01 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f35, 0x00 },
+ { 0x2f50, 0xf0 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x2f5a, 0x00 },
+ { 0x2f5b, 0x07 },
+ { 0x2f5c, 0x27 },
+ { 0x2f5d, 0x07 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+};
+
+static const struct reg_default rt721_sdca_mbq_defaults[] = {
+ { 0x0900007, 0xc004 },
+ { 0x2000001, 0x0000 },
+ { 0x2000002, 0x0000 },
+ { 0x2000003, 0x0000 },
+ { 0x2000013, 0x8001 },
+ { 0x200003c, 0x0000 },
+ { 0x2000046, 0x3400 },
+ { 0x5f00044, 0x6040 },
+ { 0x5f00045, 0x3333 },
+ { 0x5f00048, 0x0000 },
+ { 0x6100005, 0x0005 },
+ { 0x6100006, 0x0000 },
+ { 0x610000d, 0x0051 },
+ { 0x6100010, 0x0180 },
+ { 0x6100011, 0x0000 },
+ { 0x6100013, 0x0000 },
+ { 0x6100015, 0x0000 },
+ { 0x6100017, 0x8049 },
+ { 0x6100025, 0x1000 },
+ { 0x6100029, 0x0809 },
+ { 0x610002c, 0x2828 },
+ { 0x610002d, 0x2929 },
+ { 0x610002e, 0x3529 },
+ { 0x610002f, 0x2901 },
+ { 0x6100053, 0x2630 },
+ { 0x6100054, 0x2a2a },
+ { 0x6100055, 0x152f },
+ { 0x6100057, 0x2200 },
+ { 0x610005a, 0x2a4b },
+ { 0x610005b, 0x2a00 },
+ { 0x610006a, 0x0102 },
+ { 0x610006d, 0x0102 },
+ { 0x6100092, 0x4f61 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+ CH_L), 0xfe00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+ CH_R), 0xfe00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_03),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_04),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x0000 },
+};
+
+#endif /* __RT721_SDW_H__ */
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
new file mode 100644
index 000000000000..8233532a1752
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -0,0 +1,1561 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt721-sdca.h"
+#include "rt-sdw-common.h"
+
+static void rt721_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt721_sdca_priv *rt721 =
+ container_of(work, struct rt721_sdca_priv, jack_detect_work.work);
+ int btn_type = 0;
+
+ if (!rt721->hs_jack)
+ return;
+
+ if (!rt721->component->card || !rt721->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */
+ if (rt721->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ rt721->jack_type = rt_sdca_headset_detect(rt721->regmap,
+ RT721_SDCA_ENT_GE49);
+ if (rt721->jack_type < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt721->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt_sdca_button_detect(rt721->regmap,
+ RT721_SDCA_ENT_HID01, RT721_BUF_ADDR_HID1,
+ RT721_SDCA_HID_ID);
+
+ if (rt721->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt721->slave->dev,
+ "in %s, jack_type=%d\n", __func__, rt721->jack_type);
+ dev_dbg(&rt721->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt721->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt721_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt721_sdca_priv *rt721 =
+ container_of(work, struct rt721_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt721->regmap,
+ RT721_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+ /* Report ID for HID1 */
+ if (buf[0] == 0x11)
+ btn_type = rt_sdca_btn_type(&buf[1]);
+ } else
+ rt721->jack_type = 0;
+
+ dev_dbg(&rt721->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt721->hs_jack, rt721->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL9, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL10, 0x2a00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL6, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL5, 0x2626);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL8, 0x1e00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL7, 0x1515);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL3, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL4, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_CTL1, 0x0000);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ regmap_write(rt721->regmap, 0x2f5c, 0x25);
+}
+
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL6, 0x5561);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_REG,
+ RT721_GPIO_PAD_CTRL5, 0x8003);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+}
+
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x8011);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL3, 0xcf00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL4, 0x000f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL1, 0x1100);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL5, 0x0c12);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
+ RT721_JD_1PIN_GAT_CTRL2, 0xc002);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3333);
+ regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
+ regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL1, 0x0140);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL18, 0x5522);
+ regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
+ regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+ RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL4, 0xa105);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3b33);
+ regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3f33);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON13, 0x6048);
+ regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
+ regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
+ regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON17, 0x0008);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL3, 0x55ff);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL4, 0xcc00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_MBIAS_LV_CTRL2, 0x6677);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF2_LV_CTRL1, 0x7600);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL2, 0x1234);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL3, 0x3512);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL1, 0x4040);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL4, 0x1201);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_BOOST_CTRL,
+ RT721_BST_4CH_TOP_GATING_CTRL1, 0x002a);
+ regmap_write(rt721->regmap, 0x2f58, 0x07);
+
+ regmap_write(rt721->regmap, 0x2f51, 0x00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_MISC_CTL, 0x0004);
+}
+
+static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721)
+{
+ mutex_lock(&rt721->calibrate_mutex);
+ if (rt721->hs_jack) {
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt721->slave->dev, "in %s enable\n", __func__);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_UAJ_CTL, 0x036E);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU03,
+ RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU0D,
+ RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_XU_REL_CTRL, 0x0000);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x4000, 0x4000);
+ }
+ mutex_unlock(&rt721->calibrate_mutex);
+}
+
+static int rt721_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt721->hs_jack = hs_jack;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt721_sdca_jack_init(rt721);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt721_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0, changed = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendA = 0x200;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (gain_l_val == 0)
+ gain_l_val = 0x8000;
+ else
+ gain_l_val = (gain_l_val - 1) * tendA;
+ } else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (gain_r_val == 0)
+ gain_r_val = 0x8000;
+ else
+ gain_r_val = (gain_r_val - 1) * tendA;
+ } else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue != gain_l_val || rvalue != gain_r_val)
+ changed = 1;
+ else
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt721->mbq_regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt721->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return changed;
+
+ return -EIO;
+}
+
+static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendA = 0x200;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ ctl_l = read_l / tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_l == 0x8000 || read_l == 0xfe00)
+ ctl_l = 0;
+ else
+ ctl_l = read_l / tendA + 1;
+ } else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) {
+ /* boost gain */
+ ctl_r = read_r / tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_r == 0x8000 || read_r == 0xfe00)
+ ctl_r = 0;
+ else
+ ctl_r = read_r / tendA + 1;
+ } else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else {
+ ctl_r = ctl_l;
+ }
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt721_sdca_set_fu1e_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt721->fu1e_mixer_mute); i++) {
+ ch_mute = rt721->fu1e_dapm_mute || rt721->fu1e_mixer_mute[i];
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt721_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt721->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt721_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt721->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt721->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt721_sdca_set_fu1e_capture_ctl(rt721);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt721_sdca_set_fu0f_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt721_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt721->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt721->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt721_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt721->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt721->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt721->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt721->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt721_sdca_set_fu0f_capture_ctl(rt721);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt721_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int rt721_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else /* ADC gain */
+ ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt721_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * boost_step;
+ else { /* ADC gain */
+ gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt721->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt721->slave->dev, "%#08x can't be set\n", p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+static const DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, -200, 200, 0);
+
+static const struct snd_kcontrol_new rt721_sdca_controls[] = {
+ /* Headphone playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ /* Headset mic capture settings */
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt721_sdca_fu0f_capture_get, rt721_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_R), 1, 0x15, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic2_boost_vol_tlv),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ /* DMIC capture settings */
+ RT_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4, rt721_sdca_fu_info,
+ rt721_sdca_fu1e_capture_get, rt721_sdca_fu1e_capture_put),
+ RT_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_VOLUME, CH_01),
+ rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+ 4, 0x3f, mic_vol_tlv, rt721_sdca_fu_info),
+ RT_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+ 4, 3, boost_vol_tlv, rt721_sdca_fu_info),
+};
+
+static int rt721_sdca_adc_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft, mask;
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+ mask_sft = 12;
+ mask = 0x7;
+ } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+ mask_sft = 10;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+ mask_sft = 8;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+ mask_sft = 6;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+ mask_sft = 4;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+ mask_sft = 2;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+ mask_sft = 0;
+ mask = 0x3;
+ } else
+ return -EINVAL;
+
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & mask;
+
+ return 0;
+}
+
+static int rt721_sdca_adc_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val, val2 = 0, change, mask_sft, mask;
+ unsigned int check;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+ mask_sft = 12;
+ mask = 0x7;
+ } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+ mask_sft = 10;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+ mask_sft = 8;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+ mask_sft = 6;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+ mask_sft = 4;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+ mask_sft = 2;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+ mask_sft = 0;
+ mask = 0x3;
+ } else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &val2);
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux"))
+ val2 = (val2 >> mask_sft) & 0x7;
+ else
+ val2 = (val2 >> mask_sft) & 0x3;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &check);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, mask << mask_sft,
+ val << mask_sft);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc09_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+};
+static const char * const adc07_10_mux_text[] = {
+ "DMIC1 RE",
+ "DMIC1 FE",
+ "DMIC2 RE",
+ "DMIC2 FE",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt721_adc09_enum, SND_SOC_NOPM, 0, adc09_mux_text);
+static SOC_ENUM_SINGLE_DECL(
+ rt721_dmic_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt721_sdca_adc09_mux =
+ SOC_DAPM_ENUM_EXT("ADC 09 Mux", rt721_adc09_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 08 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 08 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 10 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 10 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 07 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 07 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+
+
+static int rt721_sdca_fu42_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(100);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu21_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu113_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt721->fu1e_dapm_mute = false;
+ rt721_sdca_set_fu1e_capture_ctl(rt721);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt721->fu1e_dapm_mute = true;
+ rt721_sdca_set_fu1e_capture_ctl(rt721);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu36_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt721->fu0f_dapm_mute = false;
+ rt721_sdca_set_fu0f_capture_ctl(rt721);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt721->fu0f_dapm_mute = true;
+ rt721_sdca_set_fu0f_capture_ctl(rt721);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde47_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde41_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde11_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde34_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt721_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_INPUT("DMIC1_2"),
+ SND_SOC_DAPM_INPUT("DMIC3_4"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 41", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde41_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde47_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde34_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 23", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu42_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu36_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu113_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 09 Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc09_mux),
+ SND_SOC_DAPM_MUX("ADC 08 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc08_r_mux),
+ SND_SOC_DAPM_MUX("ADC 08 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc08_l_mux),
+ SND_SOC_DAPM_MUX("ADC 10 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc10_r_mux),
+ SND_SOC_DAPM_MUX("ADC 10 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc10_l_mux),
+ SND_SOC_DAPM_MUX("ADC 07 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc07_r_mux),
+ SND_SOC_DAPM_MUX("ADC 07 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc07_l_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt721_sdca_audio_map[] = {
+ {"FU 42", NULL, "DP1RX"},
+ {"FU 21", NULL, "DP3RX"},
+ {"FU 23", NULL, "DP3RX"},
+
+ {"ADC 09 Mux", "MIC2", "MIC2"},
+ {"ADC 09 Mux", "LINE1", "LINE1"},
+ {"ADC 09 Mux", "LINE2", "LINE2"},
+ {"ADC 07 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 07 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 07 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 07 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 07 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 07 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 07 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 07 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 08 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 08 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 08 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 08 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 08 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 08 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 08 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 08 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 10 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 10 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 10 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 10 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 10 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 10 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 10 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 10 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"FU 36", NULL, "PDE 34"},
+ {"FU 36", NULL, "ADC 09 Mux"},
+ {"FU 113", NULL, "PDE 11"},
+ {"FU 113", NULL, "ADC 07 R Mux"},
+ {"FU 113", NULL, "ADC 07 L Mux"},
+ {"FU 113", NULL, "ADC 10 R Mux"},
+ {"FU 113", NULL, "ADC 10 L Mux"},
+ {"DP2TX", NULL, "FU 36"},
+ {"DP6TX", NULL, "FU 113"},
+
+ {"HP", NULL, "PDE 47"},
+ {"HP", NULL, "FU 42"},
+ {"SPK", NULL, "PDE 41"},
+ {"SPK", NULL, "FU 21"},
+ {"SPK", NULL, "FU 23"},
+};
+
+static int rt721_sdca_parse_dt(struct rt721_sdca_priv *rt721, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt721->jd_src);
+
+ return 0;
+}
+
+static int rt721_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt721_sdca_parse_dt(rt721, &rt721->slave->dev);
+ rt721->component = component;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt721 = {
+ .probe = rt721_sdca_probe,
+ .controls = rt721_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt721_sdca_controls),
+ .dapm_widgets = rt721_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt721_sdca_dapm_widgets),
+ .dapm_routes = rt721_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt721_sdca_audio_map),
+ .set_jack = rt721_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt721_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt721_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt721->slave)
+ return -EINVAL;
+
+ /*
+ * RT721_AIF1 with port = 1 for headphone playback
+ * RT721_AIF1 with port = 2 for headset-mic capture
+ * RT721_AIF2 with port = 3 for speaker playback
+ * RT721_AIF3 with port = 6 for digital-mic capture
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT721_AIF1)
+ port = 1;
+ else if (dai->id == RT721_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT721_AIF1)
+ port = 2;
+ else if (dai->id == RT721_AIF3)
+ port = 6;
+ else
+ return -EINVAL;
+ }
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt721->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = RT721_SDCA_RATE_8000HZ;
+ break;
+ case 16000:
+ sampling_rate = RT721_SDCA_RATE_16000HZ;
+ break;
+ case 24000:
+ sampling_rate = RT721_SDCA_RATE_24000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT721_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT721_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT721_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT721_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT721_SDCA_RATE_192000HZ;
+ break;
+ case 384000:
+ sampling_rate = RT721_SDCA_RATE_384000HZ;
+ break;
+ case 768000:
+ sampling_rate = RT721_SDCA_RATE_768000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT721_AIF1) {
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ }
+
+ if (dai->id == RT721_AIF2)
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ if (dai->id == RT721_AIF3)
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ return 0;
+}
+
+static int rt721_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt721->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt721->slave, sdw_stream);
+ return 0;
+}
+
+#define RT721_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT721_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt721_sdca_ops = {
+ .hw_params = rt721_sdca_pcm_hw_params,
+ .hw_free = rt721_sdca_pcm_hw_free,
+ .set_stream = rt721_sdca_set_sdw_stream,
+ .shutdown = rt721_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt721_sdca_dai[] = {
+ {
+ .name = "rt721-sdca-aif1",
+ .id = RT721_AIF1,
+ .playback = {
+ .stream_name = "DP1 Headphone Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ },
+ {
+ .name = "rt721-sdca-aif2",
+ .id = RT721_AIF2,
+ .playback = {
+ .stream_name = "DP3 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ },
+ {
+ .name = "rt721-sdca-aif3",
+ .id = RT721_AIF3,
+ .capture = {
+ .stream_name = "DP6 DMic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ }
+};
+
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721;
+
+ rt721 = devm_kzalloc(dev, sizeof(*rt721), GFP_KERNEL);
+ if (!rt721)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt721);
+ rt721->slave = slave;
+ rt721->regmap = regmap;
+ rt721->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt721->regmap, true);
+ regcache_cache_only(rt721->mbq_regmap, true);
+
+ mutex_init(&rt721->calibrate_mutex);
+ mutex_init(&rt721->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt721->jack_detect_work, rt721_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt721->jack_btn_check_work, rt721_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt721->hw_init = false;
+ rt721->first_hw_init = false;
+ rt721->fu1e_dapm_mute = true;
+ rt721->fu0f_dapm_mute = true;
+ rt721->fu0f_mixer_l_mute = rt721->fu0f_mixer_r_mute = true;
+ rt721->fu1e_mixer_mute[0] = rt721->fu1e_mixer_mute[1] =
+ rt721->fu1e_mixer_mute[2] = rt721->fu1e_mixer_mute[3] = true;
+
+ return devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
+}
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+ rt721->disable_irq = false;
+
+ if (rt721->hw_init)
+ return 0;
+
+ regcache_cache_only(rt721->regmap, false);
+ regcache_cache_only(rt721->mbq_regmap, false);
+ if (rt721->first_hw_init) {
+ regcache_cache_bypass(rt721->regmap, true);
+ regcache_cache_bypass(rt721->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+ rt721_sdca_dmic_preset(rt721);
+ rt721_sdca_amp_preset(rt721);
+ rt721_sdca_jack_preset(rt721);
+ if (rt721->first_hw_init) {
+ regcache_cache_bypass(rt721->regmap, false);
+ regcache_mark_dirty(rt721->regmap);
+ regcache_cache_bypass(rt721->mbq_regmap, false);
+ regcache_mark_dirty(rt721->mbq_regmap);
+ } else
+ rt721->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt721->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h
new file mode 100644
index 000000000000..24ce188562ba
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.h
@@ -0,0 +1,274 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_H__
+#define __RT721_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt721_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct mutex calibrate_mutex;
+ struct mutex disable_irq_lock;
+ bool disable_irq;
+ /* For Headset jack & Headphone */
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ int jack_type;
+ int jd_src;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ /* For DMIC */
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+};
+
+struct rt721_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* NID */
+#define RT721_ANA_POW_PART 0x01
+#define RT721_DAC_CTRL 0x04
+#define RT721_JD_CTRL 0x09
+#define RT721_CBJ_CTRL 0x0a
+#define RT721_CAP_PORT_CTRL 0x0c
+#define RT721_CLASD_AMP_CTRL 0x0d
+#define RT721_BOOST_CTRL 0x0f
+#define RT721_VENDOR_REG 0x20
+#define RT721_RC_CALIB_CTRL 0x40
+#define RT721_VENDOR_EQ_L 0x53
+#define RT721_VENDOR_EQ_R 0x54
+#define RT721_VENDOR_HP_CALI 0x56
+#define RT721_VENDOR_CHARGE_PUMP 0x57
+#define RT721_VENDOR_CLASD_CALI 0x58
+#define RT721_VENDOR_IMS_DRE 0x5b
+#define RT721_VENDOR_SPK_EFUSE 0x5c
+#define RT721_VENDOR_LEVEL_CTRL 0x5d
+#define RT721_VENDOR_ANA_CTL 0x5f
+#define RT721_HDA_SDCA_FLOAT 0x61
+
+/* Index (NID:01h) */
+#define RT721_MBIAS_LV_CTRL2 0x07
+#define RT721_VREF1_HV_CTRL1 0x0a
+#define RT721_VREF2_LV_CTRL1 0x0b
+
+/* Index (NID:04h) */
+#define RT721_DAC_2CH_CTRL3 0x02
+#define RT721_DAC_2CH_CTRL4 0x03
+
+/* Index (NID:09h) */
+#define RT721_JD_1PIN_GAT_CTRL2 0x07
+
+/* Index (NID:0ah) */
+#define RT721_CBJ_A0_GAT_CTRL1 0x04
+#define RT721_CBJ_A0_GAT_CTRL2 0x05
+
+/* Index (NID:0Ch) */
+#define RT721_HP_AMP_2CH_CAL1 0x05
+#define RT721_HP_AMP_2CH_CAL4 0x08
+#define RT721_HP_AMP_2CH_CAL18 0x1b
+
+/* Index (NID:0dh) */
+#define RT721_CLASD_AMP_2CH_CAL 0x14
+
+/* Index (NID:0fh) */
+#define RT721_BST_4CH_TOP_GATING_CTRL1 0x05
+
+/* Index (NID:20h) */
+#define RT721_JD_PRODUCT_NUM 0x00
+#define RT721_ANALOG_BIAS_CTL3 0x04
+#define RT721_JD_CTRL1 0x09
+#define RT721_LDO2_3_CTL1 0x0e
+#define RT721_GPIO_PAD_CTRL5 0x13
+#define RT721_LDO1_CTL 0x1a
+#define RT721_HP_JD_CTRL 0x24
+#define RT721_VD_HIDDEN_CTRL 0x26
+#define RT721_CLSD_CTRL6 0x3c
+#define RT721_COMBO_JACK_AUTO_CTL1 0x45
+#define RT721_COMBO_JACK_AUTO_CTL2 0x46
+#define RT721_COMBO_JACK_AUTO_CTL3 0x47
+#define RT721_DIGITAL_MISC_CTRL4 0x4a
+#define RT721_VREFO_GAT 0x63
+#define RT721_FSM_CTL 0x67
+#define RT721_SDCA_INTR_REC 0x82
+#define RT721_SW_CONFIG1 0x8a
+#define RT721_SW_CONFIG2 0x8b
+
+/* Index (NID:40h) */
+#define RT721_RC_CALIB_CTRL0 0x00
+
+/* Index (NID:58h) */
+#define RT721_DAC_DC_CALI_CTL1 0x01
+#define RT721_DAC_DC_CALI_CTL2 0x02
+#define RT721_DAC_DC_CALI_CTL3 0x03
+
+/* Index (NID:5fh) */
+#define RT721_MISC_POWER_CTL0 0x00
+#define RT721_MISC_POWER_CTL31 0x31
+#define RT721_UAJ_TOP_TCON13 0x44
+#define RT721_UAJ_TOP_TCON14 0x45
+#define RT721_UAJ_TOP_TCON17 0x48
+
+/* Index (NID:61h) */
+#define RT721_HDA_LEGACY_MUX_CTL0 0x00
+#define RT721_HDA_LEGACY_UAJ_CTL 0x02
+#define RT721_HDA_LEGACY_CTL1 0x05
+#define RT721_HDA_LEGACY_RESET_CTL 0x06
+#define RT721_MISC_CTL 0x07
+#define RT721_XU_REL_CTRL 0x0c
+#define RT721_GE_REL_CTRL1 0x0d
+#define RT721_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT721_GE_SDCA_RST_CTRL 0x10
+#define RT721_INT_RST_EN_CTRL 0x11
+#define RT721_XU_EVENT_EN 0x13
+#define RT721_INLINE_CTL2 0x17
+#define RT721_UMP_HID_CTRL1 0x18
+#define RT721_UMP_HID_CTRL2 0x19
+#define RT721_UMP_HID_CTRL3 0x1a
+#define RT721_UMP_HID_CTRL4 0x1b
+#define RT721_UMP_HID_CTRL5 0x1c
+#define RT721_FUNC_FLOAT_CTL0 0x22
+#define RT721_FUNC_FLOAT_CTL1 0x23
+#define RT721_FUNC_FLOAT_CTL2 0x24
+#define RT721_FUNC_FLOAT_CTL3 0x25
+#define RT721_ENT_FLOAT_CTL0 0x29
+#define RT721_ENT_FLOAT_CTL1 0x2c
+#define RT721_ENT_FLOAT_CTL2 0x2d
+#define RT721_ENT_FLOAT_CTL3 0x2e
+#define RT721_ENT_FLOAT_CTL4 0x2f
+#define RT721_CH_FLOAT_CTL1 0x45
+#define RT721_CH_FLOAT_CTL2 0x46
+#define RT721_ENT_FLOAT_CTL5 0x53
+#define RT721_ENT_FLOAT_CTL6 0x54
+#define RT721_ENT_FLOAT_CTL7 0x55
+#define RT721_ENT_FLOAT_CTL8 0x57
+#define RT721_ENT_FLOAT_CTL9 0x5a
+#define RT721_ENT_FLOAT_CTL10 0x5b
+#define RT721_CH_FLOAT_CTL3 0x6a
+#define RT721_CH_FLOAT_CTL4 0x6d
+#define RT721_CH_FLOAT_CTL5 0x70
+#define RT721_CH_FLOAT_CTL6 0x92
+
+/* Parameter & Verb control 01 (0x26)(NID:20h) */
+#define RT721_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* Buffer address for HID */
+#define RT721_BUF_ADDR_HID1 0x44030000
+#define RT721_BUF_ADDR_HID2 0x44030020
+
+/* RT721 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT721 SDCA entity */
+#define RT721_SDCA_ENT_HID01 0x01
+#define RT721_SDCA_ENT_XUV 0x03
+#define RT721_SDCA_ENT_GE49 0x49
+#define RT721_SDCA_ENT_USER_FU05 0x05
+#define RT721_SDCA_ENT_USER_FU06 0x06
+#define RT721_SDCA_ENT_USER_FU0F 0x0f
+#define RT721_SDCA_ENT_USER_FU10 0x19
+#define RT721_SDCA_ENT_USER_FU1E 0x1e
+#define RT721_SDCA_ENT_FU15 0x15
+#define RT721_SDCA_ENT_PDE23 0x23
+#define RT721_SDCA_ENT_PDE40 0x40
+#define RT721_SDCA_ENT_PDE41 0x41
+#define RT721_SDCA_ENT_PDE11 0x11
+#define RT721_SDCA_ENT_PDE12 0x12
+#define RT721_SDCA_ENT_PDE2A 0x2a
+#define RT721_SDCA_ENT_CS01 0x01
+#define RT721_SDCA_ENT_CS11 0x11
+#define RT721_SDCA_ENT_CS1F 0x1f
+#define RT721_SDCA_ENT_CS1C 0x1c
+#define RT721_SDCA_ENT_CS31 0x31
+#define RT721_SDCA_ENT_OT23 0x42
+#define RT721_SDCA_ENT_IT26 0x26
+#define RT721_SDCA_ENT_IT09 0x09
+#define RT721_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT721_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT721_SDCA_ENT_XU03 0x03
+#define RT721_SDCA_ENT_XU0D 0x0d
+#define RT721_SDCA_ENT_FU55 0x55
+
+/* RT721 SDCA control */
+#define RT721_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT721_SDCA_CTL_FU_MUTE 0x01
+#define RT721_SDCA_CTL_FU_VOLUME 0x02
+#define RT721_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT721_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT721_SDCA_CTL_SELECTED_MODE 0x01
+#define RT721_SDCA_CTL_DETECTED_MODE 0x02
+#define RT721_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT721_SDCA_CTL_VENDOR_DEF 0x30
+#define RT721_SDCA_CTL_XUV 0x34
+#define RT721_SDCA_CTL_FU_CH_GAIN 0x0b
+
+/* RT721 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+#define CH_08 0x08
+#define CH_09 0x09
+#define CH_0A 0x0a
+
+/* sample frequency index */
+#define RT721_SDCA_RATE_8000HZ 0x01
+#define RT721_SDCA_RATE_11025HZ 0x02
+#define RT721_SDCA_RATE_12000HZ 0x03
+#define RT721_SDCA_RATE_16000HZ 0x04
+#define RT721_SDCA_RATE_22050HZ 0x05
+#define RT721_SDCA_RATE_24000HZ 0x06
+#define RT721_SDCA_RATE_32000HZ 0x07
+#define RT721_SDCA_RATE_44100HZ 0x08
+#define RT721_SDCA_RATE_48000HZ 0x09
+#define RT721_SDCA_RATE_88200HZ 0x0a
+#define RT721_SDCA_RATE_96000HZ 0x0b
+#define RT721_SDCA_RATE_176400HZ 0x0c
+#define RT721_SDCA_RATE_192000HZ 0x0d
+#define RT721_SDCA_RATE_384000HZ 0x0e
+#define RT721_SDCA_RATE_768000HZ 0x0f
+
+/* RT721 HID ID */
+#define RT721_SDCA_HID_ID 0x11
+
+enum {
+ RT721_AIF1, /* For headset mic and headphone */
+ RT721_AIF2, /* For speaker */
+ RT721_AIF3, /* For dmic */
+ RT721_AIFS,
+};
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+#endif /* __RT721_H__ */
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
new file mode 100644
index 000000000000..a0f5601a262a
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -0,0 +1,557 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt722-sdca.h"
+#include "rt722-sdca-sdw.h"
+
+static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50 ... 0x2f52:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, RT722_SDCA_CTL_SELECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D,
+ RT722_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ return 1;
+ case 0x2000000 ... 0x2000024:
+ case 0x2000029 ... 0x200004a:
+ case 0x2000051 ... 0x2000052:
+ case 0x200005a ... 0x200005b:
+ case 0x2000061 ... 0x2000069:
+ case 0x200006b:
+ case 0x2000070:
+ case 0x200007f:
+ case 0x2000082 ... 0x200008e:
+ case 0x2000090 ... 0x2000094:
+ case 0x3110000:
+ case 0x5300000 ... 0x5300002:
+ case 0x5400002:
+ case 0x5600000 ... 0x5600007:
+ case 0x5700000 ... 0x5700004:
+ case 0x5800000 ... 0x5800004:
+ case 0x5810000:
+ case 0x5b00003:
+ case 0x5c00011:
+ case 0x5d00006:
+ case 0x5f00000 ... 0x5f0000d:
+ case 0x5f00030:
+ case 0x6100000 ... 0x6100051:
+ case 0x6100055 ... 0x6100057:
+ case 0x6100060:
+ case 0x6100062:
+ case 0x6100064 ... 0x6100065:
+ case 0x6100067:
+ case 0x6100070 ... 0x610007c:
+ case 0x6100080:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_R):
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_sdw_mbq_cfg rt722_mbq_config = {
+ .mbq_size = rt722_sdca_mbq_size,
+};
+
+static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ return rt722_sdca_mbq_size(dev, reg) > 0;
+}
+
+static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01:
+ case 0x2f54:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER,
+ 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ case 0x3110000:
+ case 0x5800003:
+ case 0x5810000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt722_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt722_sdca_readable_register,
+ .volatile_reg = rt722_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt722_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt722_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt722_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt722->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt722->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt722_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt722->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt722_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt722_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ sdw_slave_read_lane_mapping(slave);
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /*
+ * port = 1 for headphone playback
+ * port = 2 for headset-mic capture
+ * port = 3 for speaker playback
+ * port = 6 for digital-mic capture
+ */
+ prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 900;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ /* Three data lanes are supported by rt722-sdca codec */
+ prop->lane_control_support = true;
+
+ return 0;
+}
+
+static int rt722_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ if (cancel_delayed_work_sync(&rt722->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt722->scp_sdca_stat2)
+ scp_sdca_stat2 = rt722->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt722->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt722->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt722->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt722->scp_sdca_stat2 |= scp_sdca_stat2;
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt722->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt722->scp_sdca_stat1, rt722->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt722->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt722->jack_detect_work, msecs_to_jiffies(280));
+
+ mutex_unlock(&rt722->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt722->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt722_sdca_slave_ops = {
+ .read_prop = rt722_sdca_read_prop,
+ .interrupt_callback = rt722_sdca_interrupt_callback,
+ .update_status = rt722_sdca_update_status,
+};
+
+static int rt722_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave,
+ &rt722_sdca_regmap,
+ &rt722_mbq_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt722_sdca_init(&slave->dev, regmap, slave);
+}
+
+static int rt722_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev);
+
+ if (rt722->hw_init) {
+ cancel_delayed_work_sync(&rt722->jack_detect_work);
+ cancel_delayed_work_sync(&rt722->jack_btn_check_work);
+ }
+
+ if (rt722->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt722->calibrate_mutex);
+ mutex_destroy(&rt722->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt722_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x722, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt722_sdca_id);
+
+static int rt722_sdca_dev_suspend(struct device *dev)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+
+ if (!rt722->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt722->jack_detect_work);
+ cancel_delayed_work_sync(&rt722->jack_btn_check_work);
+
+ regcache_cache_only(rt722->regmap, true);
+
+ return 0;
+}
+
+static int rt722_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt722_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt722_sdca->disable_irq_lock);
+ rt722_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt722_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt722_sdca_dev_suspend(dev);
+}
+
+#define RT722_PROBE_TIMEOUT 5000
+
+static int rt722_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt722->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt722->disable_irq_lock);
+ if (rt722->disable_irq == true) {
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ rt722->disable_irq = false;
+ }
+ mutex_unlock(&rt722->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT722_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt722->regmap, false);
+ regcache_sync(rt722->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt722_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt722_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt722-sdca",
+ .pm = pm_ptr(&rt722_sdca_pm),
+ },
+ .probe = rt722_sdca_sdw_probe,
+ .remove = rt722_sdca_sdw_remove,
+ .ops = &rt722_sdca_slave_ops,
+ .id_table = rt722_sdca_id,
+};
+module_sdw_driver(rt722_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h
new file mode 100644
index 000000000000..c5dd472a2c00
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt722-sdca-sdw.h -- RT722 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT722_SDW_H__
+#define __RT722_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt722_sdca_reg_defaults[] = {
+ { 0x202d, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f35, 0x00 },
+ { 0x2f36, 0x00 },
+ { 0x2f50, 0xf0 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x2f5a, 0x07 },
+ { 0x2f5b, 0x07 },
+ { 0x2f5c, 0x27 },
+ { 0x2f5d, 0x07 },
+ { 0x200003c, 0xc214 },
+ { 0x2000046, 0x8004 },
+ { 0x5810000, 0x702d },
+ { 0x6100000, 0x0201 },
+ { 0x6100006, 0x0005 },
+ { 0x6100010, 0x2630 },
+ { 0x6100011, 0x152f },
+ { 0x6100013, 0x0102 },
+ { 0x6100015, 0x2200 },
+ { 0x6100017, 0x0102 },
+ { 0x6100025, 0x2a29 },
+ { 0x6100026, 0x2a00 },
+ { 0x6100028, 0x2a2a },
+ { 0x6100029, 0x4141 },
+ { 0x6100055, 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_03),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0),
+ 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+#endif /* __RT722_SDW_H__ */
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
new file mode 100644
index 000000000000..79b8b7e70a33
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -0,0 +1,1583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt722-sdca.h"
+
+#define RT722_NID_ADDR(nid, reg) ((nid) << 20 | (reg))
+
+int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
+ int ret;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, addr, value, ret);
+
+ return ret;
+}
+
+int rt722_sdca_index_read(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, addr, *value, ret);
+
+ return ret;
+}
+
+static int rt722_sdca_index_update_bits(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt722_sdca_index_read(rt722, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt722_sdca_index_write(rt722, nid, reg, tmp);
+}
+
+static int rt722_sdca_btn_type(unsigned char *buffer)
+{
+ if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || (*(buffer + 1) == 0x01) ||
+ (*(buffer + 1) == 0x10))
+ return SND_JACK_BTN_2;
+ else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || (*(buffer + 1) == 0x02) ||
+ (*(buffer + 1) == 0x20))
+ return SND_JACK_BTN_3;
+ else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || (*(buffer + 1) == 0x04) ||
+ (*(buffer + 1) == 0x40))
+ return SND_JACK_BTN_0;
+ else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || (*(buffer + 1) == 0x08) ||
+ (*(buffer + 1) == 0x80))
+ return SND_JACK_BTN_1;
+
+ return 0;
+}
+
+static unsigned int rt722_sdca_button_detect(struct rt722_sdca_priv *rt722)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt722->regmap,
+ RT722_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11)
+ btn_type = rt722_sdca_btn_type(&buf[1]);
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return btn_type;
+}
+
+static int rt722_sdca_headset_detect(struct rt722_sdca_priv *rt722)
+{
+ unsigned int det_mode;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49,
+ RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt722->jack_type = 0;
+ break;
+ case 0x03:
+ rt722->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt722->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49,
+ RT722_SDCA_CTL_SELECTED_MODE, 0), det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt722->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt722_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt722_sdca_priv *rt722 =
+ container_of(work, struct rt722_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt722->hs_jack)
+ return;
+
+ if (!rt722->component->card || !rt722->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt722_sdca_headset_detect(rt722);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt722->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt722_sdca_button_detect(rt722);
+
+ if (rt722->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt722->slave->dev,
+ "in %s, jack_type=%d\n", __func__, rt722->jack_type);
+ dev_dbg(&rt722->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt722->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt722->scp_sdca_stat1, rt722->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt722->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt722_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt722_sdca_priv *rt722 =
+ container_of(work, struct rt722_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49,
+ RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt722->regmap,
+ RT722_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11)
+ btn_type = rt722_sdca_btn_type(&buf[1]);
+ } else
+ rt722->jack_type = 0;
+
+ dev_dbg(&rt722->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt722->hs_jack, rt722->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt722->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722)
+{
+ mutex_lock(&rt722->calibrate_mutex);
+ if (rt722->hs_jack) {
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt722->slave->dev, "in %s enable\n", __func__);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_UNSOL_CTL, 0x016E);
+ /* set XU(et03h) & XU(et0Dh) to Not bypassed */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03,
+ RT722_SDCA_CTL_SELECTED_MODE, 0), 0);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D,
+ RT722_SDCA_CTL_SELECTED_MODE, 0), 0);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL1, 0x0000);
+ /* trigger GE interrupt */
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_GE_RELATED_CTL2, 0x4000, 0x4000);
+ }
+ mutex_unlock(&rt722->calibrate_mutex);
+}
+
+static int rt722_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt722->hs_jack = hs_jack;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt722_sdca_jack_init(rt722);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0, changed = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt722->regmap, mc->reg, &lvalue);
+ regmap_read(rt722->regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue != gain_l_val || rvalue != gain_r_val)
+ changed = 1;
+ else
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt722->regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt722->regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return changed;
+
+ return -EIO;
+}
+
+static int rt722_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+ strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = read_l / tendB;
+ else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = read_r / tendB;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else {
+ ctl_r = ctl_l;
+ }
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt722_sdca_set_fu1e_capture_ctl(struct rt722_sdca_priv *rt722)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt722->fu1e_mixer_mute); i++) {
+ ch_mute = rt722->fu1e_dapm_mute || rt722->fu1e_mixer_mute[i];
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt722_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt722->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt722->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt722->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt722_sdca_set_fu1e_capture_ctl(rt722);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt722_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt722->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt722->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt722_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt722->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt722->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt722->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt722->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt722_sdca_set_fu0f_capture_ctl(rt722);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt722_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ if (p->max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = p->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int regvalue, ctl, i;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt722->regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else /* ADC gain */
+ ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_dmic_kctrl_priv *p =
+ (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned int boost_step = 0x0a00;
+ unsigned int vol_max = 0x1e00;
+ unsigned int gain_val[4];
+ unsigned int i, adc_vol_flag = 0, changed = 0;
+ unsigned int regvalue[4];
+ const unsigned int interval_offset = 0xc0;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+ adc_vol_flag = 1;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ regmap_read(rt722->regmap, p->reg_base + i, &regvalue[i]);
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ if (!adc_vol_flag) /* boost gain */
+ gain_val[i] = gain_val[i] * boost_step;
+ else { /* ADC gain */
+ gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset);
+ gain_val[i] &= 0xffff;
+ }
+
+ if (regvalue[i] != gain_val[i])
+ changed = 1;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < p->count; i++) {
+ err = regmap_write(rt722->regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt722->slave->dev, "%s: %#08x can't be set\n",
+ __func__, p->reg_base + i);
+ }
+
+ return changed;
+}
+
+#define RT722_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt722_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT722_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt722_sdca_fu_info, \
+ .get = rt722_sdca_fu1e_capture_get, \
+ .put = rt722_sdca_fu1e_capture_put, \
+ .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT722_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = rt722_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt722_sdca_controls[] = {
+ /* Headphone playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv),
+ /* Headset mic capture settings */
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt722_sdca_fu0f_capture_get, rt722_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv),
+ /* DMIC capture settings */
+ RT722_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4),
+ RT722_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_VOLUME, CH_01),
+ rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put,
+ 4, 0x3f, mic_vol_tlv),
+ RT722_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15,
+ RT722_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put,
+ 4, 3, boost_vol_tlv),
+};
+
+static const char * const adc22_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+};
+
+static const char * const adc07_10_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc22_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 12, adc22_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc24_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 4, adc07_10_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc25_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt722_sdca_adc22_mux =
+ SOC_DAPM_ENUM("ADC 22 Mux", rt722_adc22_enum);
+
+static const struct snd_kcontrol_new rt722_sdca_adc24_mux =
+ SOC_DAPM_ENUM("ADC 24 Mux", rt722_adc24_enum);
+
+static const struct snd_kcontrol_new rt722_sdca_adc25_mux =
+ SOC_DAPM_ENUM("ADC 25 Mux", rt722_adc25_enum);
+
+static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt722->fu1e_dapm_mute = false;
+ rt722_sdca_set_fu1e_capture_ctl(rt722);
+ usleep_range(150000, 160000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt722->fu1e_dapm_mute = true;
+ rt722_sdca_set_fu1e_capture_ctl(rt722);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt722->fu0f_dapm_mute = false;
+ rt722_sdca_set_fu0f_capture_ctl(rt722);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt722->fu0f_dapm_mute = true;
+ rt722_sdca_set_fu0f_capture_ctl(rt722);
+ break;
+ }
+ return 0;
+}
+
+static void rt722_pde_transition_delay(struct rt722_sdca_priv *rt722, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 1000, val;
+
+ pm_runtime_mark_last_busy(&rt722->slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(func, entity, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt722->slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ }
+}
+
+static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt722_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+ SND_SOC_DAPM_INPUT("DMIC1_2"),
+ SND_SOC_DAPM_INPUT("DMIC3_4"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde47_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0,
+ rt722_sdca_pde12_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu42_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu36_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0,
+ rt722_sdca_fu113_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+ &rt722_sdca_adc22_mux),
+ SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+ &rt722_sdca_adc24_mux),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt722_sdca_adc25_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt722_sdca_audio_map[] = {
+ {"FU 42", NULL, "DP1RX"},
+ {"FU 21", NULL, "DP3RX"},
+
+ {"ADC 22 Mux", "MIC2", "MIC2"},
+ {"ADC 22 Mux", "LINE1", "LINE1"},
+ {"ADC 22 Mux", "LINE2", "LINE2"},
+ {"ADC 24 Mux", "DMIC1", "DMIC1_2"},
+ {"ADC 24 Mux", "DMIC2", "DMIC3_4"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1_2"},
+ {"ADC 25 Mux", "DMIC2", "DMIC3_4"},
+ {"FU 36", NULL, "PDE 12"},
+ {"FU 36", NULL, "ADC 22 Mux"},
+ {"FU 113", NULL, "PDE 11"},
+ {"FU 113", NULL, "ADC 24 Mux"},
+ {"FU 113", NULL, "ADC 25 Mux"},
+ {"DP2TX", NULL, "FU 36"},
+ {"DP6TX", NULL, "FU 113"},
+
+ {"HP", NULL, "PDE 47"},
+ {"HP", NULL, "FU 42"},
+ {"SPK", NULL, "PDE 23"},
+ {"SPK", NULL, "FU 21"},
+};
+
+static int rt722_sdca_parse_dt(struct rt722_sdca_priv *rt722, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt722->jd_src);
+
+ return 0;
+}
+
+static int rt722_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt722_sdca_parse_dt(rt722, &rt722->slave->dev);
+ rt722->component = component;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt722 = {
+ .probe = rt722_sdca_probe,
+ .controls = rt722_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt722_sdca_controls),
+ .dapm_widgets = rt722_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt722_sdca_dapm_widgets),
+ .dapm_routes = rt722_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt722_sdca_audio_map),
+ .set_jack = rt722_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt722_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt722_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt722->slave)
+ return -EINVAL;
+
+ /*
+ * RT722_AIF1 with port = 1 for headphone playback
+ * RT722_AIF1 with port = 2 for headset-mic capture
+ * RT722_AIF2 with port = 3 for speaker playback
+ * RT722_AIF3 with port = 6 for digital-mic capture
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT722_AIF1)
+ port = 1;
+ else if (dai->id == RT722_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT722_AIF1)
+ port = 2;
+ else if (dai->id == RT722_AIF3)
+ port = 6;
+ else
+ return -EINVAL;
+ }
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt722->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT722_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT722_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT722_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT722_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT722_AIF1) {
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ }
+
+ if (dai->id == RT722_AIF2)
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ if (dai->id == RT722_AIF3)
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ return 0;
+}
+
+static int rt722_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt722->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt722->slave, sdw_stream);
+ return 0;
+}
+
+#define RT722_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT722_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt722_sdca_ops = {
+ .hw_params = rt722_sdca_pcm_hw_params,
+ .hw_free = rt722_sdca_pcm_hw_free,
+ .set_stream = rt722_sdca_set_sdw_stream,
+ .shutdown = rt722_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt722_sdca_dai[] = {
+ {
+ .name = "rt722-sdca-aif1",
+ .id = RT722_AIF1,
+ .playback = {
+ .stream_name = "DP1 Headphone Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ },
+ {
+ .name = "rt722-sdca-aif2",
+ .id = RT722_AIF2,
+ .playback = {
+ .stream_name = "DP3 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ },
+ {
+ .name = "rt722-sdca-aif3",
+ .id = RT722_AIF3,
+ .capture = {
+ .stream_name = "DP6 DMic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ }
+};
+
+int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722;
+
+ rt722 = devm_kzalloc(dev, sizeof(*rt722), GFP_KERNEL);
+ if (!rt722)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt722);
+ rt722->slave = slave;
+ rt722->regmap = regmap;
+
+ regcache_cache_only(rt722->regmap, true);
+
+ mutex_init(&rt722->calibrate_mutex);
+ mutex_init(&rt722->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt722->jack_detect_work, rt722_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt722->jack_btn_check_work, rt722_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt722->hw_init = false;
+ rt722->first_hw_init = false;
+ rt722->fu1e_dapm_mute = true;
+ rt722->fu0f_dapm_mute = true;
+ rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true;
+ rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] =
+ rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true;
+
+ return devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai));
+}
+
+static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722)
+{
+ unsigned int mic_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ dev_dbg(dev, "%s mic func_status=0x%x\n", __func__, mic_func_status);
+
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Set AD07 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29);
+ /* Set AD10 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC10_PDE_FLOAT_CTL, 0x2a00);
+ /* Set DMIC1/DMIC2 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a);
+ /* Set DMIC2 IT entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_ENT_FLOAT_CTL, 0x2626);
+ /* Set AD10 FU entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC_ENT_FLOAT_CTL, 0x1e00);
+ /* Set DMIC2 FU entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+ /* Set AD10 FU channel floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304);
+ /* Set DMIC2 FU channel floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+ /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000);
+ /* Enable vf707_r12_05/vf707_r13_05 */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ /* Fine tune PDE2A latency */
+ regmap_write(rt722->regmap, 0x2f5c, 0x25);
+ /* PHYtiming TDZ/TZD control */
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f52, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
+{
+ unsigned int amp_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Set DVQ=01 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
+ 0xc215);
+ /* Reset dc_cal_top */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
+ 0x702c);
+ /* W1C Trigger Calibration */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
+ 0xf02d);
+ /* Set DAC02/ClassD power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL,
+ 0x2323);
+ /* Set EAPD high */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL,
+ 0x0002);
+ /* Enable vf707_r14 */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f54, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
+{
+ int loop_check, chk_cnt = 100, ret;
+ unsigned int calib_status = 0;
+ unsigned int jack_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ dev_dbg(dev, "%s jack func_status=0x%x\n", __func__, jack_func_status);
+
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Config analog bias */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3,
+ 0xa081);
+ /* GE related settings */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2,
+ 0xa009);
+ /* Button A, B, C, D bypass mode */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4,
+ 0xcf00);
+ /* HID1 slot enable */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5,
+ 0x000f);
+ /* Report ID for HID1 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0,
+ 0x1100);
+ /* OSC/OOC for slot 2, 3 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7,
+ 0x0c12);
+ /* Set JD de-bounce clock control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1,
+ 0x7002);
+ /* Set DVQ=01 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
+ 0xc215);
+ /* FSM switch to calibration manual mode */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL,
+ 0x4100);
+ /* W1C Trigger DC calibration (HP) */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3,
+ 0x008d);
+ /* check HP calibration FSM status */
+ for (loop_check = 0; loop_check < chk_cnt; loop_check++) {
+ usleep_range(10000, 11000);
+ ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI,
+ RT722_DAC_DC_CALI_CTL3, &calib_status);
+ if (ret < 0)
+ dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret);
+ if ((calib_status & 0x0040) == 0x0)
+ break;
+ }
+
+ if (loop_check == chk_cnt)
+ dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__);
+
+ /* Set ADC09 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL,
+ 0x2a12);
+ /* Set MIC2 and LINE1 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL,
+ 0x3429);
+ /* Set ET41h and LINE2 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL,
+ 0x4112);
+ /* Set DAC03 and HP power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL,
+ 0x4040);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1,
+ 0x4141);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1,
+ 0x0101);
+ /* Fine tune PDE40 latency */
+ regmap_write(rt722->regmap, 0x2f58, 0x07);
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+ /* MIC VRefo */
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200);
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_VREFO_GAT, 0x4000, 0x4000);
+ /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4,
+ 0x0010);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f51, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+ unsigned int val;
+
+ rt722->disable_irq = false;
+
+ if (rt722->hw_init)
+ return 0;
+
+ regcache_cache_only(rt722->regmap, false);
+ if (rt722->first_hw_init) {
+ regcache_cache_bypass(rt722->regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt722_sdca_index_read(rt722, RT722_VENDOR_REG, RT722_JD_PRODUCT_NUM, &val);
+ rt722->hw_vid = (val & 0x0f00) >> 8;
+ dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid);
+
+ rt722_sdca_dmic_preset(rt722);
+ rt722_sdca_amp_preset(rt722);
+ rt722_sdca_jack_preset(rt722);
+
+ if (rt722->first_hw_init) {
+ regcache_cache_bypass(rt722->regmap, false);
+ regcache_mark_dirty(rt722->regmap);
+ } else
+ rt722->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt722->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
new file mode 100644
index 000000000000..823abee9ab76
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt722-sdca.h -- RT722 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT722_H__
+#define __RT722_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt722_sdca_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct mutex calibrate_mutex;
+ struct mutex disable_irq_lock;
+ bool disable_irq;
+ /* For Headset jack & Headphone */
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ int jack_type;
+ int jd_src;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ /* For DMIC */
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+ int hw_vid;
+};
+
+struct rt722_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* NID */
+#define RT722_VENDOR_REG 0x20
+#define RT722_VENDOR_CALI 0x58
+#define RT722_VENDOR_SPK_EFUSE 0x5c
+#define RT722_VENDOR_IMS_DRE 0x5b
+#define RT722_VENDOR_ANALOG_CTL 0x5f
+#define RT722_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT722_JD_PRODUCT_NUM 0x00
+#define RT722_ANALOG_BIAS_CTL3 0x04
+#define RT722_JD_CTRL1 0x09
+#define RT722_LDO2_3_CTL1 0x0e
+#define RT722_LDO1_CTL 0x1a
+#define RT722_HP_JD_CTRL 0x24
+#define RT722_CLSD_CTRL6 0x3c
+#define RT722_COMBO_JACK_AUTO_CTL1 0x45
+#define RT722_COMBO_JACK_AUTO_CTL2 0x46
+#define RT722_COMBO_JACK_AUTO_CTL3 0x47
+#define RT722_DIGITAL_MISC_CTRL4 0x4a
+#define RT722_VREFO_GAT 0x63
+#define RT722_FSM_CTL 0x67
+#define RT722_SDCA_INTR_REC 0x82
+#define RT722_SW_CONFIG1 0x8a
+#define RT722_SW_CONFIG2 0x8b
+
+/* Index (NID:58h) */
+#define RT722_DAC_DC_CALI_CTL0 0x00
+#define RT722_DAC_DC_CALI_CTL1 0x01
+#define RT722_DAC_DC_CALI_CTL2 0x02
+#define RT722_DAC_DC_CALI_CTL3 0x03
+
+/* Index (NID:59h) */
+#define RT722_ULTRA_SOUND_DETECTOR6 0x1e
+
+/* Index (NID:5bh) */
+#define RT722_IMS_DIGITAL_CTL1 0x00
+#define RT722_IMS_DIGITAL_CTL5 0x05
+#define RT722_HP_DETECT_RLDET_CTL1 0x29
+#define RT722_HP_DETECT_RLDET_CTL2 0x2a
+
+/* Index (NID:5fh) */
+#define RT722_MISC_POWER_CTL0 0x00
+#define RT722_MISC_POWER_CTL7 0x08
+
+/* Index (NID:61h) */
+#define RT722_HDA_LEGACY_MUX_CTL0 0x00
+#define RT722_HDA_LEGACY_UNSOL_CTL 0x03
+#define RT722_HDA_LEGACY_CONFIG_CTL0 0x06
+#define RT722_HDA_LEGACY_RESET_CTL 0x08
+#define RT722_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT722_DMIC_ENT_FLOAT_CTL 0x10
+#define RT722_DMIC_GAIN_ENT_FLOAT_CTL0 0x11
+#define RT722_DMIC_GAIN_ENT_FLOAT_CTL2 0x13
+#define RT722_ADC_ENT_FLOAT_CTL 0x15
+#define RT722_ADC_VOL_CH_FLOAT_CTL 0x17
+#define RT722_ADC_SAMPLE_RATE_FLOAT 0x18
+#define RT722_DAC03_HP_PDE_FLOAT_CTL 0x22
+#define RT722_MIC2_LINE2_PDE_FLOAT_CTL 0x23
+#define RT722_ET41_LINE2_PDE_FLOAT_CTL 0x24
+#define RT722_ADC0A_08_PDE_FLOAT_CTL 0x25
+#define RT722_ADC10_PDE_FLOAT_CTL 0x26
+#define RT722_DMIC1_2_PDE_FLOAT_CTL 0x28
+#define RT722_AMP_PDE_FLOAT_CTL 0x29
+#define RT722_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f
+#define RT722_GE_RELATED_CTL1 0x45
+#define RT722_GE_RELATED_CTL2 0x46
+#define RT722_MIXER_CTL0 0x52
+#define RT722_MIXER_CTL1 0x53
+#define RT722_EAPD_CTL 0x55
+#define RT722_UMP_HID_CTL0 0x60
+#define RT722_UMP_HID_CTL1 0x61
+#define RT722_UMP_HID_CTL2 0x62
+#define RT722_UMP_HID_CTL3 0x63
+#define RT722_UMP_HID_CTL4 0x64
+#define RT722_UMP_HID_CTL5 0x65
+#define RT722_UMP_HID_CTL6 0x66
+#define RT722_UMP_HID_CTL7 0x67
+#define RT722_UMP_HID_CTL8 0x68
+#define RT722_FLOAT_CTRL_1 0x70
+#define RT722_ENT_FLOAT_CTRL_1 0x76
+
+/* Parameter & Verb control 01 (0x1a)(NID:20h) */
+#define RT722_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT722_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT722_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT722_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT722_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* DAC calibration control (0x00)(NID:58h) */
+#define RT722_DC_CALIB_CTRL (0x1 << 16)
+/* DAC DC offset calibration control-1 (0x01)(NID:58h) */
+#define RT722_PDM_DC_CALIB_STATUS (0x1 << 15)
+
+#define RT722_EAPD_HIGH 0x2
+#define RT722_EAPD_LOW 0x0
+
+/* Buffer address for HID */
+#define RT722_BUF_ADDR_HID1 0x44030000
+#define RT722_BUF_ADDR_HID2 0x44030020
+
+/* RT722 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT722 SDCA entity */
+#define RT722_SDCA_ENT_HID01 0x01
+#define RT722_SDCA_ENT_GE49 0x49
+#define RT722_SDCA_ENT_USER_FU05 0x05
+#define RT722_SDCA_ENT_USER_FU06 0x06
+#define RT722_SDCA_ENT_USER_FU0F 0x0f
+#define RT722_SDCA_ENT_USER_FU10 0x19
+#define RT722_SDCA_ENT_USER_FU1E 0x1e
+#define RT722_SDCA_ENT_FU15 0x15
+#define RT722_SDCA_ENT_PDE23 0x23
+#define RT722_SDCA_ENT_PDE40 0x40
+#define RT722_SDCA_ENT_PDE11 0x11
+#define RT722_SDCA_ENT_PDE12 0x12
+#define RT722_SDCA_ENT_PDE2A 0x2a
+#define RT722_SDCA_ENT_CS01 0x01
+#define RT722_SDCA_ENT_CS11 0x11
+#define RT722_SDCA_ENT_CS1F 0x1f
+#define RT722_SDCA_ENT_CS1C 0x1c
+#define RT722_SDCA_ENT_CS31 0x31
+#define RT722_SDCA_ENT_OT23 0x42
+#define RT722_SDCA_ENT_IT26 0x26
+#define RT722_SDCA_ENT_IT09 0x09
+#define RT722_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT722_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT722_SDCA_ENT_XU03 0x03
+#define RT722_SDCA_ENT_XU0D 0x0d
+#define RT722_SDCA_ENT0 0x00
+
+/* RT722 SDCA control */
+#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT722_SDCA_CTL_FU_MUTE 0x01
+#define RT722_SDCA_CTL_FU_VOLUME 0x02
+#define RT722_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT722_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT722_SDCA_CTL_SELECTED_MODE 0x01
+#define RT722_SDCA_CTL_DETECTED_MODE 0x02
+#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT722_SDCA_CTL_VENDOR_DEF 0x30
+#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT722_SDCA_CTL_FUNC_STATUS 0x10
+#define RT722_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+
+/* RT722 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+#define CH_08 0x08
+
+/* sample frequency index */
+#define RT722_SDCA_RATE_16000HZ 0x04
+#define RT722_SDCA_RATE_32000HZ 0x07
+#define RT722_SDCA_RATE_44100HZ 0x08
+#define RT722_SDCA_RATE_48000HZ 0x09
+#define RT722_SDCA_RATE_96000HZ 0x0b
+#define RT722_SDCA_RATE_192000HZ 0x0d
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
+enum {
+ RT722_AIF1, /* For headset mic and headphone */
+ RT722_AIF2, /* For speaker */
+ RT722_AIF3, /* For dmic */
+ RT722_AIFS,
+};
+
+enum rt722_sdca_jd_src {
+ RT722_JD_NULL,
+ RT722_JD1,
+};
+
+enum rt722_sdca_version {
+ RT722_VA,
+ RT722_VB,
+};
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave);
+int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int value);
+int rt722_sdca_index_read(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int *value);
+
+int rt722_sdca_jack_detect(struct rt722_sdca_priv *rt722, bool *hp, bool *mic);
+#endif /* __RT722_H__ */
diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c
new file mode 100644
index 000000000000..97f56af25577
--- /dev/null
+++ b/sound/soc/codecs/rt9120.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define RT9120_REG_DEVID 0x00
+#define RT9120_REG_I2SFMT 0x02
+#define RT9120_REG_I2SWL 0x03
+#define RT9120_REG_SDIOSEL 0x04
+#define RT9120_REG_SYSCTL 0x05
+#define RT9120_REG_SPKGAIN 0x07
+#define RT9120_REG_VOLRAMP 0x0A
+#define RT9120_REG_ERRRPT 0x10
+#define RT9120_REG_MSVOL 0x20
+#define RT9120_REG_SWRESET 0x40
+#define RT9120_REG_INTERCFG 0x63
+#define RT9120_REG_INTERNAL0 0x65
+#define RT9120_REG_INTERNAL1 0x69
+#define RT9120_REG_UVPOPT 0x6C
+#define RT9120_REG_DIGCFG 0xF8
+
+#define RT9120_VID_MASK GENMASK(15, 8)
+#define RT9120_SWRST_MASK BIT(7)
+#define RT9120_MUTE_MASK GENMASK(5, 4)
+#define RT9120_I2SFMT_MASK GENMASK(4, 2)
+#define RT9120_I2SFMT_SHIFT 2
+#define RT9120_CFG_FMT_I2S 0
+#define RT9120_CFG_FMT_LEFTJ 1
+#define RT9120_CFG_FMT_RIGHTJ 2
+#define RT9120_CFG_FMT_DSPA 3
+#define RT9120_CFG_FMT_DSPB 7
+#define RT9120_AUDBIT_MASK GENMASK(1, 0)
+#define RT9120_CFG_AUDBIT_16 0
+#define RT9120_CFG_AUDBIT_20 1
+#define RT9120_CFG_AUDBIT_24 2
+#define RT9120_AUDWL_MASK GENMASK(5, 0)
+#define RT9120_CFG_WORDLEN_16 16
+#define RT9120_CFG_WORDLEN_24 24
+#define RT9120_CFG_WORDLEN_32 32
+#define RT9120_DVDD_UVSEL_MASK GENMASK(5, 4)
+#define RT9120_AUTOSYNC_MASK BIT(6)
+
+#define RT9120_VENDOR_ID 0x42
+#define RT9120S_VENDOR_ID 0x43
+#define RT9120_RESET_WAITMS 20
+#define RT9120_CHIPON_WAITMS 20
+#define RT9120_AMPON_WAITMS 50
+#define RT9120_AMPOFF_WAITMS 100
+#define RT9120_LVAPP_THRESUV 2000000
+
+/* 8000 to 192000 supported , only 176400 not support */
+#define RT9120_RATES_MASK (SNDRV_PCM_RATE_8000_192000 &\
+ ~SNDRV_PCM_RATE_176400)
+#define RT9120_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+enum {
+ CHIP_IDX_RT9120 = 0,
+ CHIP_IDX_RT9120S,
+ CHIP_IDX_MAX
+};
+
+struct rt9120_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *pwdnn_gpio;
+ int chip_idx;
+};
+
+/* 11bit [min,max,step] = [-103.9375dB, 24dB, 0.0625dB] */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -1039375, 625, 1);
+
+/* {6, 8, 10, 12, 13, 14, 15, 16}dB */
+static const DECLARE_TLV_DB_RANGE(classd_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(600, 200, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(1300, 100, 0)
+);
+
+static const char * const sdo_select_text[] = {
+ "None", "INTF", "Final", "RMS Detect"
+};
+
+static const struct soc_enum sdo_select_enum =
+ SOC_ENUM_SINGLE(RT9120_REG_SDIOSEL, 4, ARRAY_SIZE(sdo_select_text),
+ sdo_select_text);
+
+static const struct snd_kcontrol_new rt9120_snd_controls[] = {
+ SOC_SINGLE_TLV("MS Volume", RT9120_REG_MSVOL, 0, 2047, 1, digital_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RT9120_REG_SPKGAIN, 0, 7, 0, classd_tlv),
+ SOC_SINGLE("PBTL Switch", RT9120_REG_SYSCTL, 3, 1, 0),
+ SOC_ENUM("SDO Select", sdo_select_enum),
+};
+
+static int internal_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write(comp, RT9120_REG_ERRRPT, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(RT9120_AMPON_WAITMS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(RT9120_AMPOFF_WAITMS);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9120_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("DMIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("LDAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("RDAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("PWND", RT9120_REG_SYSCTL, 6, 1,
+ internal_power_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA("SPKL PA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKR PA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route rt9120_dapm_routes[] = {
+ { "DMIX", NULL, "AIF Playback" },
+ /* SPKL */
+ { "LDAC", NULL, "PWND" },
+ { "LDAC", NULL, "DMIX" },
+ { "SPKL PA", NULL, "LDAC" },
+ { "SPKL", NULL, "SPKL PA" },
+ /* SPKR */
+ { "RDAC", NULL, "PWND" },
+ { "RDAC", NULL, "DMIX" },
+ { "SPKR PA", NULL, "RDAC" },
+ { "SPKR", NULL, "SPKR PA" },
+ /* Cap */
+ { "AIF Capture", NULL, "LDAC" },
+ { "AIF Capture", NULL, "RDAC" },
+};
+
+static int rt9120_codec_probe(struct snd_soc_component *comp)
+{
+ struct rt9120_data *data = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, data->regmap);
+
+ pm_runtime_get_sync(comp->dev);
+
+ /* Internal setting */
+ if (data->chip_idx == CHIP_IDX_RT9120S) {
+ snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde);
+ snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x66);
+ } else
+ snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04);
+
+ pm_runtime_mark_last_busy(comp->dev);
+ pm_runtime_put(comp->dev);
+
+ return 0;
+}
+
+static int rt9120_codec_suspend(struct snd_soc_component *comp)
+{
+ return pm_runtime_force_suspend(comp->dev);
+}
+
+static int rt9120_codec_resume(struct snd_soc_component *comp)
+{
+ return pm_runtime_force_resume(comp->dev);
+}
+
+static const struct snd_soc_component_driver rt9120_component_driver = {
+ .probe = rt9120_codec_probe,
+ .suspend = rt9120_codec_suspend,
+ .resume = rt9120_codec_resume,
+ .controls = rt9120_snd_controls,
+ .num_controls = ARRAY_SIZE(rt9120_snd_controls),
+ .dapm_widgets = rt9120_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets),
+ .dapm_routes = rt9120_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes),
+ .endianness = 1,
+};
+
+static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ unsigned int format;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = RT9120_CFG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = RT9120_CFG_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = RT9120_CFG_FMT_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = RT9120_CFG_FMT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = RT9120_CFG_FMT_DSPB;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT,
+ RT9120_I2SFMT_MASK,
+ format << RT9120_I2SFMT_SHIFT);
+ return 0;
+}
+
+static int rt9120_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ unsigned int param_width, param_slot_width, auto_sync;
+ int width, fs;
+
+ switch (width = params_width(param)) {
+ case 16:
+ param_width = RT9120_CFG_AUDBIT_16;
+ break;
+ case 20:
+ param_width = RT9120_CFG_AUDBIT_20;
+ break;
+ case 24:
+ case 32:
+ param_width = RT9120_CFG_AUDBIT_24;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported data width [%d]\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT,
+ RT9120_AUDBIT_MASK, param_width);
+
+ switch (width = params_physical_width(param)) {
+ case 16:
+ param_slot_width = RT9120_CFG_WORDLEN_16;
+ break;
+ case 24:
+ param_slot_width = RT9120_CFG_WORDLEN_24;
+ break;
+ case 32:
+ param_slot_width = RT9120_CFG_WORDLEN_32;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported slot width [%d]\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp, RT9120_REG_I2SWL,
+ RT9120_AUDWL_MASK, param_slot_width);
+
+ fs = width * params_channels(param);
+ /* If fs is divided by 48, disable auto sync */
+ if (fs % 48 == 0)
+ auto_sync = 0;
+ else
+ auto_sync = RT9120_AUTOSYNC_MASK;
+
+ snd_soc_component_update_bits(comp, RT9120_REG_DIGCFG,
+ RT9120_AUTOSYNC_MASK, auto_sync);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9120_dai_ops = {
+ .set_fmt = rt9120_set_fmt,
+ .hw_params = rt9120_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9120_dai = {
+ .name = "rt9120_aif",
+ .playback = {
+ .stream_name = "AIF Playback",
+ .rates = RT9120_RATES_MASK,
+ .formats = RT9120_FMTS_MASK,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "AIF Capture",
+ .rates = RT9120_RATES_MASK,
+ .formats = RT9120_FMTS_MASK,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9120_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct regmap_range rt9120_rd_yes_ranges[] = {
+ regmap_reg_range(0x00, 0x0C),
+ regmap_reg_range(0x10, 0x15),
+ regmap_reg_range(0x20, 0x27),
+ regmap_reg_range(0x30, 0x38),
+ regmap_reg_range(0x3A, 0x40),
+ regmap_reg_range(0x63, 0x63),
+ regmap_reg_range(0x65, 0x65),
+ regmap_reg_range(0x69, 0x69),
+ regmap_reg_range(0x6C, 0x6C),
+ regmap_reg_range(0xF8, 0xF8)
+};
+
+static const struct regmap_access_table rt9120_rd_table = {
+ .yes_ranges = rt9120_rd_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt9120_rd_yes_ranges),
+};
+
+static const struct regmap_range rt9120_wr_yes_ranges[] = {
+ regmap_reg_range(0x00, 0x00),
+ regmap_reg_range(0x02, 0x0A),
+ regmap_reg_range(0x10, 0x15),
+ regmap_reg_range(0x20, 0x27),
+ regmap_reg_range(0x30, 0x38),
+ regmap_reg_range(0x3A, 0x3D),
+ regmap_reg_range(0x40, 0x40),
+ regmap_reg_range(0x63, 0x63),
+ regmap_reg_range(0x65, 0x65),
+ regmap_reg_range(0x69, 0x69),
+ regmap_reg_range(0x6C, 0x6C),
+ regmap_reg_range(0xF8, 0xF8)
+};
+
+static const struct regmap_access_table rt9120_wr_table = {
+ .yes_ranges = rt9120_wr_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges),
+};
+
+static bool rt9120_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x01:
+ case 0x10:
+ case 0x30 ... 0x40:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt9120_get_reg_size(unsigned int reg)
+{
+ switch (reg) {
+ case 0x00:
+ case 0x20 ... 0x27:
+ return 2;
+ case 0x30 ... 0x3D:
+ return 3;
+ case 0x3E ... 0x3F:
+ return 4;
+ default:
+ return 1;
+ }
+}
+
+static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct rt9120_data *data = context;
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ int size = rt9120_get_reg_size(reg);
+ u8 raw[4] = {0};
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, raw);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ switch (size) {
+ case 4:
+ *val = be32_to_cpup((__be32 *)raw);
+ break;
+ case 3:
+ *val = raw[0] << 16 | raw[1] << 8 | raw[2];
+ break;
+ case 2:
+ *val = be16_to_cpup((__be16 *)raw);
+ break;
+ default:
+ *val = raw[0];
+ }
+
+ return 0;
+}
+
+static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct rt9120_data *data = context;
+ struct i2c_client *i2c = to_i2c_client(data->dev);
+ int size = rt9120_get_reg_size(reg);
+ __be32 be32_val;
+ u8 *rawp = (u8 *)&be32_val;
+ int offs = 4 - size;
+
+ be32_val = cpu_to_be32(val);
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs);
+}
+
+static const struct reg_default rt9120_reg_defaults[] = {
+ { .reg = 0x02, .def = 0x02 },
+ { .reg = 0x03, .def = 0xf2 },
+ { .reg = 0x04, .def = 0x01 },
+ { .reg = 0x05, .def = 0xc0 },
+ { .reg = 0x06, .def = 0x28 },
+ { .reg = 0x07, .def = 0x04 },
+ { .reg = 0x08, .def = 0xff },
+ { .reg = 0x09, .def = 0x01 },
+ { .reg = 0x0a, .def = 0x01 },
+ { .reg = 0x0b, .def = 0x00 },
+ { .reg = 0x0c, .def = 0x04 },
+ { .reg = 0x11, .def = 0x30 },
+ { .reg = 0x12, .def = 0x08 },
+ { .reg = 0x13, .def = 0x12 },
+ { .reg = 0x14, .def = 0x09 },
+ { .reg = 0x15, .def = 0x00 },
+ { .reg = 0x20, .def = 0x7ff },
+ { .reg = 0x21, .def = 0x180 },
+ { .reg = 0x22, .def = 0x180 },
+ { .reg = 0x23, .def = 0x00 },
+ { .reg = 0x24, .def = 0x80 },
+ { .reg = 0x25, .def = 0x180 },
+ { .reg = 0x26, .def = 0x640 },
+ { .reg = 0x27, .def = 0x180 },
+ { .reg = 0x63, .def = 0x5e },
+ { .reg = 0x65, .def = 0x66 },
+ { .reg = 0x6c, .def = 0xe0 },
+ { .reg = 0xf8, .def = 0x44 },
+};
+
+static const struct regmap_config rt9120_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = RT9120_REG_DIGCFG,
+ .reg_defaults = rt9120_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt9120_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .reg_read = rt9120_reg_read,
+ .reg_write = rt9120_reg_write,
+
+ .volatile_reg = rt9120_volatile_reg,
+ .wr_table = &rt9120_wr_table,
+ .rd_table = &rt9120_rd_table,
+};
+
+static int rt9120_check_vendor_info(struct rt9120_data *data)
+{
+ unsigned int devid;
+ int ret;
+
+ ret = regmap_read(data->regmap, RT9120_REG_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ devid = FIELD_GET(RT9120_VID_MASK, devid);
+ switch (devid) {
+ case RT9120_VENDOR_ID:
+ data->chip_idx = CHIP_IDX_RT9120;
+ break;
+ case RT9120S_VENDOR_ID:
+ data->chip_idx = CHIP_IDX_RT9120S;
+ break;
+ default:
+ dev_err(data->dev, "DEVID not correct [0x%0x]\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rt9120_do_register_reset(struct rt9120_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, RT9120_REG_SWRESET,
+ RT9120_SWRST_MASK);
+ if (ret)
+ return ret;
+
+ msleep(RT9120_RESET_WAITMS);
+ return 0;
+}
+
+static int rt9120_probe(struct i2c_client *i2c)
+{
+ struct rt9120_data *data;
+ struct regulator *dvdd_supply;
+ int dvdd_supply_volt, ret;
+
+ data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &i2c->dev;
+ i2c_set_clientdata(i2c, data);
+
+ data->pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->pwdnn_gpio)) {
+ dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n");
+ return PTR_ERR(data->pwdnn_gpio);
+ } else if (data->pwdnn_gpio) {
+ dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n");
+ msleep(RT9120_CHIPON_WAITMS);
+ }
+
+ data->regmap = devm_regmap_init(&i2c->dev, NULL, data,
+ &rt9120_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap [%d]\n", ret);
+ return ret;
+ }
+
+ ret = rt9120_check_vendor_info(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to check vendor info\n");
+ return ret;
+ }
+
+ ret = rt9120_do_register_reset(data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to do register reset\n");
+ return ret;
+ }
+
+ dvdd_supply = devm_regulator_get(&i2c->dev, "dvdd");
+ if (IS_ERR(dvdd_supply)) {
+ dev_err(&i2c->dev, "No dvdd regulator found\n");
+ return PTR_ERR(dvdd_supply);
+ }
+
+ dvdd_supply_volt = regulator_get_voltage(dvdd_supply);
+ if (dvdd_supply_volt <= RT9120_LVAPP_THRESUV) {
+ dev_dbg(&i2c->dev, "dvdd low voltage design\n");
+ ret = regmap_update_bits(data->regmap, RT9120_REG_UVPOPT,
+ RT9120_DVDD_UVSEL_MASK, 0);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to config dvdd uvsel\n");
+ return ret;
+ }
+ }
+
+ pm_runtime_set_autosuspend_delay(&i2c->dev, 1000);
+ pm_runtime_use_autosuspend(&i2c->dev);
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_mark_last_busy(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &rt9120_component_driver,
+ &rt9120_dai, 1);
+}
+
+static void rt9120_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+ pm_runtime_set_suspended(&i2c->dev);
+}
+
+static int rt9120_runtime_suspend(struct device *dev)
+{
+ struct rt9120_data *data = dev_get_drvdata(dev);
+
+ if (data->pwdnn_gpio) {
+ regcache_cache_only(data->regmap, true);
+ regcache_mark_dirty(data->regmap);
+ gpiod_set_value(data->pwdnn_gpio, 0);
+ }
+
+ return 0;
+}
+
+static int rt9120_runtime_resume(struct device *dev)
+{
+ struct rt9120_data *data = dev_get_drvdata(dev);
+
+ if (data->pwdnn_gpio) {
+ gpiod_set_value(data->pwdnn_gpio, 1);
+ msleep(RT9120_CHIPON_WAITMS);
+ regcache_cache_only(data->regmap, false);
+ regcache_sync(data->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt9120_pm_ops = {
+ RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL)
+};
+
+static const struct of_device_id __maybe_unused rt9120_device_table[] = {
+ { .compatible = "richtek,rt9120", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt9120_device_table);
+
+static struct i2c_driver rt9120_driver = {
+ .driver = {
+ .name = "rt9120",
+ .of_match_table = rt9120_device_table,
+ .pm = pm_ptr(&rt9120_pm_ops),
+ },
+ .probe = rt9120_probe,
+ .remove = rt9120_remove,
+};
+module_i2c_driver(rt9120_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RT9120 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c
new file mode 100644
index 000000000000..84fd3d6861de
--- /dev/null
+++ b/sound/soc/codecs/rt9123.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123.c -- RT9123 (SW I2C Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define RT9123_REG_AMPCTRL 0x01
+#define RT9123_REG_I2SOPT 0x02
+#define RT9123_REG_TDMRX 0x03
+#define RT9123_REG_SILVOLEN 0x04
+#define RT9123_REG_VOLGAIN 0x12
+#define RT9123_REG_ANAFLAG 0x36
+#define RT9123_REG_COMBOID 0xF7
+
+#define RT9123_MASK_SWRST BIT(15)
+#define RT9123_MASK_SWMUTE BIT(14)
+#define RT9123_MASK_AMPON BIT(12)
+#define RT9123_MASK_AUDBIT GENMASK(14, 12)
+#define RT9123_MASK_AUDFMT GENMASK(11, 8)
+#define RT9123_MASK_TDMRXLOC GENMASK(4, 0)
+#define RT9123_MASK_VENID GENMASK(15, 4)
+
+#define RT9123_FIXED_VENID 0x340
+
+struct rt9123_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rt9123_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct device *dev = comp->dev;
+ unsigned int enable;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ enable = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ enable = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */
+ snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-1200, 0, 0),
+ 1, 9, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 10, 10, TLV_DB_SCALE_ITEM(1400, 0, 0));
+static const char * const pwmfreq_text[] = { "300KHz", "325KHz", "350KHz", "375KHz" };
+static const struct soc_enum rt9123_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_AMPCTRL, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rt9123_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_I2SOPT, 4, ARRAY_SIZE(i2sch_text), i2sch_text);
+
+static int rt9123_kcontrol_name_comp(struct snd_kcontrol *kcontrol, const char *s)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ const char *kctlname = kcontrol->id.name;
+
+ if (comp && comp->name_prefix)
+ kctlname += strlen(comp->name_prefix) + 1;
+
+ return strcmp(kctlname, s);
+}
+
+static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG read
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to get control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG write
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to put control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static const struct snd_kcontrol_new rt9123_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RT9123_REG_VOLGAIN, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_EXT_TLV("Speaker Volume", RT9123_REG_AMPCTRL, 0, 10, 0, rt9123_xhandler_get,
+ rt9123_xhandler_put, ana_tlv),
+ SOC_ENUM_EXT("PWM Frequency Select", rt9123_pwm_freq_enum, rt9123_xhandler_get,
+ rt9123_xhandler_put),
+ SOC_ENUM("I2S CH Select", rt9123_i2sch_select_enum),
+ SOC_SINGLE("Silence Detect Switch", RT9123_REG_SILVOLEN, 14, 1, 0),
+};
+
+static const struct snd_soc_component_driver rt9123_comp_driver = {
+ .controls = rt9123_controls,
+ .num_controls = ARRAY_SIZE(rt9123_controls),
+ .dapm_widgets = rt9123_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123_dapm_widgets),
+ .dapm_routes = rt9123_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rt9123_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+
+ rt9123->dai_fmt = fmt;
+ return 0;
+}
+
+static int rt9123_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int rx_loc;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slots % 2 || slot_width % 8 ||
+ slots * slot_width > 256) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || ffs(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure rx channel data location */
+ rx_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write_field(comp, RT9123_REG_TDMRX, RT9123_MASK_TDMRXLOC, rx_loc);
+
+ rt9123->tdm_slots = slots;
+ rt9123->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rt9123_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rt9123->dai_fmt);
+ if (rt9123->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rt9123->tdm_slots ? 4 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rt9123->tdm_slots ? 12 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ audbit = 2;
+ break;
+ case 32:
+ audbit = 3;
+ break;
+ case 8:
+ audbit = 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+ if (rt9123->tdm_slots && slot_width > rt9123->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDFMT, audfmt);
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDBIT, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9123_dai_ops = {
+ .set_fmt = rt9123_dai_set_format,
+ .set_tdm_slot = rt9123_dai_set_tdm_slot,
+ .hw_params = rt9123_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9123_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9123_dai_ops,
+};
+
+static bool rt9123_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01:
+ case 0x20:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt9123_regmap_config = {
+ .name = "rt9123",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .readable_reg = rt9123_readable_reg,
+ .writeable_reg = rt9123_writeable_reg,
+ .volatile_reg = rt9123_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = RT9123_REG_ANAFLAG + 1,
+};
+
+static int rt9123_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rt9123_priv *rt9123;
+ struct regmap *regmap;
+ __be16 value;
+ u16 venid;
+ int ret;
+
+ rt9123 = devm_kzalloc(dev, sizeof(*rt9123), GFP_KERNEL);
+ if (!rt9123)
+ return -ENOMEM;
+
+ rt9123->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rt9123->enable))
+ return PTR_ERR(rt9123->enable);
+ else if (rt9123->enable)
+ usleep_range(250, 350);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_i2c_block_data(i2c, RT9123_REG_COMBOID, sizeof(value), (u8 *)&value);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read vendor-id\n");
+
+ venid = be16_to_cpu(value);
+ if ((venid & RT9123_MASK_VENID) != RT9123_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect vendor-id 0x%04x\n", venid);
+
+ /* Trigger RG reset before regmap init cache */
+ value = cpu_to_be16(RT9123_MASK_SWRST);
+ ret = i2c_smbus_write_i2c_block_data(i2c, RT9123_REG_AMPCTRL, sizeof(value), (u8 *)&value);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init_i2c(i2c, &rt9123_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ i2c_set_clientdata(i2c, rt9123);
+
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rt9123_comp_driver, &rt9123_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rt9123_runtime_suspend(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rt9123->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rt9123->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rt9123_runtime_resume(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rt9123->enable) {
+ gpiod_set_value(rt9123->enable, 1);
+ usleep_range(250, 350);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rt9123_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rt9123_runtime_suspend, rt9123_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123_device_id[] = {
+ { .compatible = "richtek,rt9123" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123_acpi_match[] = {
+ { "RT9123", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123_acpi_match);
+#endif
+
+static struct i2c_driver rt9123_i2c_driver = {
+ .driver = {
+ .name = "rt9123",
+ .of_match_table = of_match_ptr(rt9123_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123_acpi_match),
+ .pm = pm_ptr(&rt9123_dev_pm_ops),
+ },
+ .probe = rt9123_i2c_probe,
+};
+module_i2c_driver(rt9123_i2c_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c
new file mode 100644
index 000000000000..d509659e735b
--- /dev/null
+++ b/sound/soc/codecs/rt9123p.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123p.c -- RT9123 (HW Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct rt9123p_priv {
+ struct gpio_desc *enable;
+ unsigned int enable_delay;
+ int enable_switch;
+};
+
+static int rt9123p_daiops_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (!rt9123p->enable)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mdelay(rt9123p->enable_delay);
+ if (rt9123p->enable_switch) {
+ gpiod_set_value(rt9123p->enable, 1);
+ dev_dbg(comp->dev, "set enable to 1");
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ gpiod_set_value(rt9123p->enable, 0);
+ dev_dbg(comp->dev, "set enable to 0");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt9123p_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ rt9123p->enable_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ rt9123p->enable_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123p_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123p_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123p_dapm_routes[] = {
+ {"Amp Drv", NULL, "HiFi Playback"},
+ {"SPK", NULL, "Amp Drv"},
+};
+
+static const struct snd_soc_component_driver rt9123p_comp_driver = {
+ .dapm_widgets = rt9123p_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123p_dapm_widgets),
+ .dapm_routes = rt9123p_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123p_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt9123p_dai_ops = {
+ .trigger = rt9123p_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver rt9123p_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9123p_dai_ops,
+};
+
+static int rt9123p_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rt9123p_priv *rt9123p;
+ int ret;
+
+ rt9123p = devm_kzalloc(dev, sizeof(*rt9123p), GFP_KERNEL);
+ if (!rt9123p)
+ return -ENOMEM;
+
+ rt9123p->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(rt9123p->enable))
+ return PTR_ERR(rt9123p->enable);
+
+ ret = device_property_read_u32(dev, "enable-delay-ms", &rt9123p->enable_delay);
+ if (ret) {
+ rt9123p->enable_delay = 0;
+ dev_dbg(dev, "no optional property 'enable-delay-ms' found, default: no delay\n");
+ }
+
+ platform_set_drvdata(pdev, rt9123p);
+
+ return devm_snd_soc_register_component(dev, &rt9123p_comp_driver, &rt9123p_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123p_device_id[] = {
+ { .compatible = "richtek,rt9123p" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123p_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123p_acpi_match[] = {
+ { "RT9123P", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123p_acpi_match);
+#endif
+
+static struct platform_driver rt9123p_platform_driver = {
+ .driver = {
+ .name = "rt9123p",
+ .of_match_table = of_match_ptr(rt9123p_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123p_acpi_match),
+ },
+ .probe = rt9123p_platform_probe,
+};
+module_platform_driver(rt9123p_platform_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123p Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c
new file mode 100644
index 000000000000..186904b31434
--- /dev/null
+++ b/sound/soc/codecs/rtq9124.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rtq9124.c -- RTQ9124 ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define RTQ9124_REG_SDI_SEL 0x00
+#define RTQ9124_REG_SDO_SEL 0x01
+#define RTQ9124_REG_I2S_OPT 0x02
+#define RTQ9124_REG_AMP_OPT 0x03
+#define RTQ9124_REG_STATE_CTRL 0x04
+#define RTQ9124_REG_PWM_PHASE 0x05
+#define RTQ9124_REG_SIL_CTRL 0x06
+#define RTQ9124_REG_PWM_SS_OPT 0x07
+#define RTQ9124_REG_ERR_INT_0 0x10
+#define RTQ9124_REG_ERR_MASK6 0x26
+#define RTQ9124_REG_TDM_TX_CH0 0x32
+#define RTQ9124_REG_TDM_RX_CH0 0x34
+#define RTQ9124_REG_VOL_OPT 0x38
+#define RTQ9124_REG_DCR_TH 0x4B
+#define RTQ9124_REG_ERR_TH 0x4C
+#define RTQ9124_REG_PROT_EN 0x5B
+#define RTQ9124_REG_PRJ_CODE 0xF9
+
+#define RTQ9124_MASK_CS_DATA_INV BIT(9)
+#define RTQ9124_MASK_VDDIO_SDO_SEL BIT(8)
+#define RTQ9124_MASK_AUD_BITS GENMASK(5, 4)
+#define RTQ9124_MASK_AUD_FMT GENMASK(3, 0)
+#define RTQ9124_MASK_CH_STATE GENMASK(1, 0)
+#define RTQ9124_MASK_SF_RESET BIT(15)
+
+#define RTQ9124_FIXED_VENID 0x9124
+
+struct rtq9124_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rtq9124_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int i, chan_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Change state to normal */
+ chan_state = 0;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Change state to HiZ */
+ chan_state = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Before amp turn on, clear old events first */
+ for (i = 0; !chan_state && i < 8; i++)
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_INT_0 + i, 0xffff);
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_CH_STATE,
+ chan_state);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9124_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rtq9124_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rtq9124_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(1400, 200, 0));
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rtq9124_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDI_SEL, 0, ARRAY_SIZE(i2sch_text), i2sch_text);
+static const char * const sdo_vsel_text[] = { "1.8V", "3.3V" };
+static const struct soc_enum rtq9124_sdo_vselect_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDO_SEL, 8, ARRAY_SIZE(sdo_vsel_text), sdo_vsel_text);
+static const char * const pwmfreq_text[] = { "8*fs", "10*fs", "40*fs", "44*fs", "48*fs" };
+static const struct soc_enum rtq9124_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_AMP_OPT, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const out_angle_text[] = { "0", "45", "90", "135", "180", "225", "270", "315" };
+static const struct soc_enum rtq9124_out_angle_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_PWM_PHASE, 0, ARRAY_SIZE(out_angle_text), out_angle_text);
+static const char * const sdo_select_text[] = {
+ "None", "I2S DataI", "Interface", "DSP", "DF", "ISense", "ACLoad Cos", "ACLoad Sin",
+ "DCR",
+};
+static const struct soc_enum rtq9124_sdo_select_enum =
+ SOC_ENUM_DOUBLE(RTQ9124_REG_SDO_SEL, 4, 0, ARRAY_SIZE(sdo_select_text), sdo_select_text);
+static const char * const ulqm_dcvt_text[] = { "Disable", "DC", "VT", "DC+VT" };
+static const struct soc_enum rtq9124_ulqm_dcvt_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_STATE_CTRL, 10, ARRAY_SIZE(ulqm_dcvt_text), ulqm_dcvt_text);
+
+static const struct snd_kcontrol_new rtq9124_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RTQ9124_REG_VOL_OPT, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", RTQ9124_REG_AMP_OPT, 0, 6, 0, ana_tlv),
+ SOC_ENUM("I2S CH Select", rtq9124_i2sch_select_enum),
+ SOC_ENUM("SDO VDDIO Select", rtq9124_sdo_vselect_enum),
+ SOC_ENUM("PWM Frequency Select", rtq9124_pwm_freq_enum),
+ SOC_ENUM("PWM Output Phase Select", rtq9124_out_angle_enum),
+ SOC_ENUM("SDO Select", rtq9124_sdo_select_enum),
+ SOC_ENUM("ULQM DCVT Select", rtq9124_ulqm_dcvt_select_enum),
+ SOC_SINGLE("Silence Detect Enable Switch", RTQ9124_REG_SIL_CTRL, 7, 1, 0),
+ SOC_SINGLE("Spread Spectrum Enable Switch", RTQ9124_REG_PWM_SS_OPT, 7, 1, 0),
+};
+
+static int rtq9124_comp_probe(struct snd_soc_component *comp)
+{
+ /* CS Data INV */
+ snd_soc_component_write_field(comp, RTQ9124_REG_SDO_SEL, RTQ9124_MASK_CS_DATA_INV, 1);
+
+ /* RTLD */
+ snd_soc_component_write(comp, RTQ9124_REG_DCR_TH, 0x5e30);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_TH, 0x3ff);
+ snd_soc_component_write(comp, RTQ9124_REG_PROT_EN, 0x3fc);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_MASK6, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9124_comp_driver = {
+ .probe = rtq9124_comp_probe,
+ .controls = rtq9124_controls,
+ .num_controls = ARRAY_SIZE(rtq9124_controls),
+ .dapm_widgets = rtq9124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9124_dapm_widgets),
+ .dapm_routes = rtq9124_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9124_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9124_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+
+ rtq9124->dai_fmt = fmt;
+ return 0;
+}
+
+static int rtq9124_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int byte_loc, i;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slots > 16 || slot_width <= 0 || slots % 2 || slot_width % 8) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (tx_mask && (hweight_long(tx_mask) > 2 || fls(tx_mask) > slots)) {
+ dev_err(dev, "Invalid tx_mask 0x%08x, slots = %d\n", tx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure tx channel data location */
+ for (i = 0; tx_mask; i++, tx_mask ^= BIT(ffs(tx_mask) - 1)) {
+ byte_loc = (ffs(tx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_TX_CH0 + i, byte_loc);
+ }
+
+ /* Configure rx channel data location */
+ byte_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_RX_CH0, byte_loc);
+
+ rtq9124->tdm_slots = slots;
+ rtq9124->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width, bitrate;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rtq9124->dai_fmt);
+ if (rtq9124->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A &&
+ fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rtq9124->tdm_slots ? 7 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rtq9124->tdm_slots ? 15 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ if (rtq9124->tdm_slots) {
+ slot_width = params_physical_width(param);
+ if (slot_width > rtq9124->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ bitrate = rtq9124->tdm_slots * rtq9124->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "Bitrate exceed the internal PLL 24.576MHz (%d)\n", bitrate);
+ return -EINVAL;
+ }
+ }
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_FMT, audfmt);
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_BITS, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rtq9124_dai_ops = {
+ .set_fmt = rtq9124_dai_set_format,
+ .set_tdm_slot = rtq9124_dai_set_tdm_slot,
+ .hw_params = rtq9124_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rtq9124_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rtq9124_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static bool rtq9124_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30 ... 0x3D:
+ case 0x40 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x09:
+ case 0x0C ... 0x0E:
+ case 0x10 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30:
+ case 0x32 ... 0x3D:
+ case 0x40 ... 0x4E:
+ case 0x50 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0A ... 0x0B:
+ case 0x0F ... 0x17:
+ case 0x31:
+ case 0x4F:
+ case 0x51:
+ case 0x53 ... 0x57:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline u8 rtq9124_get_reg_len(unsigned int reg)
+{
+ return (reg >= 0x40 && reg <= 0x47) ? 4 : 2;
+}
+
+static int rtq9124_regmap_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)reg_buf;
+ u8 size = rtq9124_get_reg_len(reg);
+ u32 *val = val_buf;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, val_buf);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ *val = size == 4 ? be32_to_cpup(val_buf) : be16_to_cpup(val_buf);
+
+ return 0;
+}
+
+static int rtq9124_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)data, *vbuf;
+ u8 size = rtq9124_get_reg_len(reg);
+ __be16 val16 = cpu_to_be16p(data + 1);
+ __be32 val32 = cpu_to_be32p(data + 1);
+
+ vbuf = size == 4 ? (u8 *)&val32 : (u8 *)&val16;
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, vbuf);
+}
+
+static const struct regmap_config rtq9124_regmap_config = {
+ .name = "rtq9124",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .read = rtq9124_regmap_read,
+ .write = rtq9124_regmap_write,
+ .readable_reg = rtq9124_readable_reg,
+ .writeable_reg = rtq9124_writeable_reg,
+ .volatile_reg = rtq9124_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = 0xFD + 1,
+ .use_single_read = 1,
+ .use_single_write = 1,
+};
+
+static const struct reg_sequence rtq9124_init_regs[] = {
+ { 0xfb, 0x0065 },
+ { 0x93, 0x2000 },
+ { 0xfb, 0x0000 },
+};
+
+static int rtq9124_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9124_priv *rtq9124;
+ struct regmap *regmap;
+ int ret;
+
+ rtq9124 = devm_kzalloc(dev, sizeof(*rtq9124), GFP_KERNEL);
+ if (!rtq9124)
+ return -ENOMEM;
+
+ rtq9124->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rtq9124->enable))
+ return PTR_ERR(rtq9124->enable);
+ else if (rtq9124->enable)
+ usleep_range(6000, 7000);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_word_swapped(i2c, RTQ9124_REG_PRJ_CODE);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read project code\n");
+ else if (ret != RTQ9124_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect project-code 0x%04x\n", ret);
+
+ /* Trigger RG reset before regmap init */
+ ret = i2c_smbus_write_word_swapped(i2c, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_SF_RESET);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, NULL, i2c, &rtq9124_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_register_patch(regmap, rtq9124_init_regs, ARRAY_SIZE(rtq9124_init_regs));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register regmap patch\n");
+
+ i2c_set_clientdata(i2c, rtq9124);
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9124_comp_driver, &rtq9124_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rtq9124_runtime_suspend(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rtq9124->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rtq9124->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rtq9124_runtime_resume(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rtq9124->enable) {
+ gpiod_set_value(rtq9124->enable, 1);
+ usleep_range(6000, 7000);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rtq9124_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rtq9124_runtime_suspend, rtq9124_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtq9124_device_id[] = {
+ { .compatible = "richtek,rtq9124" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9124_device_id);
+#endif
+
+static struct i2c_driver rtq9124_driver = {
+ .driver = {
+ .name = "rtq9124",
+ .of_match_table = of_match_ptr(rtq9124_device_id),
+ .pm = pm_ptr(&rtq9124_dev_pm_ops),
+ },
+ .probe = rtq9124_probe,
+};
+module_i2c_driver(rtq9124_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC RTQ9124 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
new file mode 100644
index 000000000000..391cc03d687f
--- /dev/null
+++ b/sound/soc/codecs/rtq9128.c
@@ -0,0 +1,789 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) 2023 Richtek Technology Corp.
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+//
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define RTQ9128_REG_SDI_SEL 0x00
+#define RTQ9128_REG_SDO_SEL 0x01
+#define RTQ9128_REG_I2S_OPT 0x02
+#define RTQ9128_REG_MISC 0x03
+#define RTQ9128_REG_STATE_CTRL 0x04
+#define RTQ9128_REG_PLLTRI_GEN1 0x05
+#define RTQ9128_REG_PLLTRI_GEN2 0x06
+#define RTQ9128_REG_PWM_SS_OPT 0x07
+#define RTQ9128_REG_DSP_EN 0x08
+#define RTQ9128_REG_TDM_TX_CH1 0x21
+#define RTQ9128_REG_TDM_RX_CH1 0x25
+#define RTQ9128_REG_MS_VOL 0x30
+#define RTQ9128_REG_CH1_VOL 0x31
+#define RTQ9128_REG_CH2_VOL 0x32
+#define RTQ9128_REG_CH3_VOL 0x33
+#define RTQ9128_REG_CH4_VOL 0x34
+#define RTQ9128_REG_PROT_OPT 0x71
+#define RTQ9128_REG_EFUSE_DATA 0xE0
+#define RTQ9128_REG_VENDOR_ID 0xF9
+
+#define RTQ9128_CHSTAT_VAL_MASK GENMASK(1, 0)
+#define RTQ9128_DOLEN_MASK GENMASK(7, 6)
+#define RTQ9128_TDMSRCIN_MASK GENMASK(5, 4)
+#define RTQ9128_AUDBIT_MASK GENMASK(5, 4)
+#define RTQ9128_AUDFMT_MASK GENMASK(3, 0)
+#define RTQ9128_MSMUTE_MASK BIT(0)
+#define RTQ9128_DIE_CHECK_MASK GENMASK(4, 0)
+#define RTQ9128_VENDOR_ID_MASK GENMASK(19, 8)
+
+#define RTQ9128_SOFT_RESET_VAL 0x80
+#define RTQ9128_VENDOR_ID_VAL 0x470
+#define RTQ9128_ALLCH_HIZ_VAL 0x55
+#define RTQ9128_ALLCH_ULQM_VAL 0xFF
+#define RTQ9128_TKA470B_VAL 0
+#define RTQ9128_RTQ9128DH_VAL 0x0F
+#define RTQ9128_RTQ9128DL_VAL 0x10
+
+struct rtq9128_data {
+ struct gpio_desc *enable;
+ unsigned int daifmt;
+ int tdm_slots;
+ int tdm_slot_width;
+ bool tdm_input_data2_select;
+};
+
+struct rtq9128_init_reg {
+ unsigned int reg;
+ unsigned int val;
+};
+
+static int rtq9128_get_reg_size(unsigned int reg)
+{
+ switch (reg) {
+ case 0x5C ... 0x6F:
+ case 0x98 ... 0x9F:
+ case 0xC0 ... 0xC3:
+ case 0xC8 ... 0xCF:
+ case 0xDF ... 0xE5:
+ case 0xF9:
+ return 4;
+ case 0x40 ... 0x4F:
+ return 3;
+ case 0x30 ... 0x35:
+ case 0x8C ... 0x97:
+ case 0xC4 ... 0xC7:
+ case 0xD7 ... 0xDA:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int rtq9128_i2c_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 reg = *(u8 *)data;
+ int rg_size;
+
+ if (count != 5) {
+ dev_err(dev, "Invalid write for data length (%d)\n", (int)count);
+ return -EINVAL;
+ }
+
+ rg_size = rtq9128_get_reg_size(reg);
+ return i2c_smbus_write_i2c_block_data(i2c, reg, rg_size, data + count - rg_size);
+}
+
+static int rtq9128_i2c_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 reg = *(u8 *)reg_buf;
+ u8 data_tmp[4] = {};
+ int rg_size, ret;
+
+ if (reg_size != 1 || val_size != 4) {
+ dev_err(dev, "Invalid read for reg_size (%d) or val_size (%d)\n", (int)reg_size,
+ (int)val_size);
+ return -EINVAL;
+ }
+
+ rg_size = rtq9128_get_reg_size(reg);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, rg_size, data_tmp);
+ if (ret < 0)
+ return ret;
+ else if (ret != rg_size)
+ return -EIO;
+
+ memset(val_buf, 0, val_size - rg_size);
+ memcpy(val_buf + val_size - rg_size, data_tmp, rg_size);
+
+ return 0;
+}
+
+static const struct regmap_bus rtq9128_regmap_bus = {
+ .write = rtq9128_i2c_write,
+ .read = rtq9128_i2c_read,
+ .max_raw_read = 4,
+ .max_raw_write = 4,
+};
+
+static bool rtq9128_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x2B:
+ case 0x30 ... 0x35:
+ case 0x40 ... 0x56:
+ case 0x5C ... 0x76:
+ case 0x80 ... 0xAD:
+ case 0xB0 ... 0xBA:
+ case 0xC0 ... 0xE5:
+ case 0xF0 ... 0xFB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9128_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x1F:
+ case 0x21 ... 0x2B:
+ case 0x30 ... 0x35:
+ case 0x40 ... 0x56:
+ case 0x5C ... 0x76:
+ case 0x80 ... 0x8B:
+ case 0xA0 ... 0xAD:
+ case 0xB0 ... 0xBA:
+ case 0xC0:
+ case 0xD0 ... 0xDE:
+ case 0xE0 ... 0xE5:
+ case 0xF0 ... 0xF3:
+ case 0xF6 ... 0xF8:
+ case 0xFA ... 0xFB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9128_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0F ... 0x17:
+ case 0x20:
+ case 0x53:
+ case 0x55:
+ case 0x5C ... 0x6F:
+ case 0x8C ... 0x9F:
+ case 0xC0 ... 0xCF:
+ case 0xDF:
+ case 0xF0 ... 0xF1:
+ case 0xF4 ... 0xF5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rtq9128_regmap_config = {
+ .name = "rtq9128",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+
+ .readable_reg = rtq9128_is_readable_reg,
+ .writeable_reg = rtq9128_is_writeable_reg,
+ .volatile_reg = rtq9128_is_volatile_reg,
+ .num_reg_defaults_raw = RTQ9128_REG_VENDOR_ID + 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+
+static const DECLARE_TLV_DB_RANGE(spkgain_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1500, 300, 0),
+);
+
+static const char * const source_select_text[] = { "CH1", "CH2", "CH3", "CH4" };
+static const char * const pwmfreq_select_text[] = { "8fs", "10fs", "40fs", "44fs", "48fs" };
+static const char * const phase_select_text[] = {
+ "0 degree", "45 degree", "90 degree", "135 degree",
+ "180 degree", "225 degree", "270 degree", "315 degree",
+};
+static const char * const dvdduv_select_text[] = { "1P4V", "1P5V", "2P1V", "2P3V" };
+
+static const struct soc_enum rtq9128_ch1_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 6, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch2_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 4, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch3_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 2, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch4_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 0, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 4, ARRAY_SIZE(pwmfreq_select_text),
+ pwmfreq_select_text);
+static const struct soc_enum rtq9128_out2_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9128_out3_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 4, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9128_out4_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+
+/*
+ * In general usage, DVDD could be 1P8V, 3P0V or 3P3V.
+ * This DVDD undervoltage protection is to prevent from the abnormal power
+ * lose case while the amplifier is operating. Due to the different DVDD
+ * application, treat this threshold as a user choosable option.
+ */
+static const struct soc_enum rtq9128_dvdduv_select_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PROT_OPT, 6, ARRAY_SIZE(dvdduv_select_text),
+ dvdduv_select_text);
+
+static const struct snd_kcontrol_new rtq9128_snd_ctrls[] = {
+ SOC_SINGLE_TLV("MS Volume", RTQ9128_REG_MS_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH1 Volume", RTQ9128_REG_CH1_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH2 Volume", RTQ9128_REG_CH2_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH3 Volume", RTQ9128_REG_CH3_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH4 Volume", RTQ9128_REG_CH4_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RTQ9128_REG_MISC, 0, 5, 0, spkgain_tlv),
+ SOC_SINGLE("PBTL12 Switch", RTQ9128_REG_MISC, 5, 1, 0),
+ SOC_SINGLE("PBTL34 Switch", RTQ9128_REG_MISC, 4, 1, 0),
+ SOC_SINGLE("Spread Spectrum Switch", RTQ9128_REG_PWM_SS_OPT, 7, 1, 0),
+ SOC_SINGLE("SDO Select", RTQ9128_REG_SDO_SEL, 0, 15, 0),
+ SOC_ENUM("CH1 SI Select", rtq9128_ch1_si_enum),
+ SOC_ENUM("CH2 SI Select", rtq9128_ch2_si_enum),
+ SOC_ENUM("CH3 SI Select", rtq9128_ch3_si_enum),
+ SOC_ENUM("CH4 SI Select", rtq9128_ch4_si_enum),
+ SOC_ENUM("PWM FREQ Select", rtq9128_pwm_freq_enum),
+ SOC_ENUM("OUT2 Phase Select", rtq9128_out2_phase_enum),
+ SOC_ENUM("OUT3 Phase Select", rtq9128_out3_phase_enum),
+ SOC_ENUM("OUT4 Phase Select", rtq9128_out4_phase_enum),
+ SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
+};
+
+static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int shift, mask;
+ int ret;
+
+ dev_dbg(comp->dev, "%s: %s event %d\n", __func__, w->name, event);
+
+ if (snd_soc_dapm_widget_name_cmp(w, "DAC1") == 0)
+ shift = 6;
+ else if (snd_soc_dapm_widget_name_cmp(w, "DAC2") == 0)
+ shift = 4;
+ else if (snd_soc_dapm_widget_name_cmp(w, "DAC3") == 0)
+ shift = 2;
+ else
+ shift = 0;
+
+ mask = RTQ9128_CHSTAT_VAL_MASK << shift;
+
+ /* Turn channel state to Normal or HiZ */
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_STATE_CTRL, mask,
+ event != SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * For each channel turns on, HW will trigger DC load detect and DC
+ * offset calibration, the time is needed for all the actions done.
+ */
+ if (event == SND_SOC_DAPM_POST_PMU)
+ msleep(25);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9128_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC1", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC3", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC4", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+};
+
+static const struct snd_soc_dapm_route rtq9128_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+ { "DAC3", NULL, "Playback" },
+ { "DAC4", NULL, "Playback" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+ { "OUT4", NULL, "DAC4" },
+ { "Capture", NULL, "DAC1" },
+ { "Capture", NULL, "DAC2" },
+ { "Capture", NULL, "DAC3" },
+ { "Capture", NULL, "DAC4" },
+};
+
+static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
+ { 0xA0, 0xEF },
+ { 0x0D, 0x00 },
+ { 0x03, 0x05 },
+ { 0x05, 0x31 },
+ { 0x06, 0x23 },
+ { 0x70, 0x11 },
+ { 0x75, 0x1F },
+ { 0xB6, 0x03 },
+ { 0xB9, 0x03 },
+ { 0xB8, 0x03 },
+ { 0xC1, 0xFF },
+ { 0xF8, 0x72 },
+ { 0x30, 0x180 },
+};
+
+static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
+ { 0x0F, 0x00 },
+ { 0x03, 0x0D },
+ { 0xB2, 0xFF },
+ { 0xB3, 0xFF },
+ { 0x30, 0x180 },
+ { 0x8A, 0x55 },
+ { 0x72, 0x00 },
+ { 0xB1, 0xE3 },
+};
+
+static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
+ { 0x0F, 0x00 },
+ { 0x03, 0x0D },
+ { 0x30, 0x180 },
+ { 0x8A, 0x55 },
+ { 0x72, 0x00 },
+ { 0xB1, 0xE3 },
+};
+
+static int rtq9128_component_probe(struct snd_soc_component *comp)
+{
+ const struct rtq9128_init_reg *table, *curr;
+ size_t table_size;
+ unsigned int val;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(comp->dev);
+ if (ret < 0) {
+ dev_err(comp->dev, "Failed to resume device (%d)\n", ret);
+ return ret;
+ }
+
+ val = snd_soc_component_read(comp, RTQ9128_REG_EFUSE_DATA);
+
+ switch (FIELD_GET(RTQ9128_DIE_CHECK_MASK, val)) {
+ case RTQ9128_TKA470B_VAL:
+ table = rtq9128_tka470b_tables;
+ table_size = ARRAY_SIZE(rtq9128_tka470b_tables);
+ break;
+ case RTQ9128_RTQ9128DH_VAL:
+ table = rtq9128_dh_tables;
+ table_size = ARRAY_SIZE(rtq9128_dh_tables);
+ break;
+ default:
+ table = rtq9128_dl_tables;
+ table_size = ARRAY_SIZE(rtq9128_dl_tables);
+ break;
+ }
+
+ for (i = 0, curr = table; i < table_size; i++, curr++) {
+ ret = snd_soc_component_write(comp, curr->reg, curr->val);
+ if (ret < 0)
+ return ret;
+ }
+
+ pm_runtime_mark_last_busy(comp->dev);
+ pm_runtime_put(comp->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9128_comp_driver = {
+ .probe = rtq9128_component_probe,
+ .controls = rtq9128_snd_ctrls,
+ .num_controls = ARRAY_SIZE(rtq9128_snd_ctrls),
+ .dapm_widgets = rtq9128_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9128_dapm_widgets),
+ .dapm_routes = rtq9128_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9128_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = dai->dev;
+
+ dev_dbg(dev, "%s: fmt 0x%8x\n", __func__, fmt);
+
+ /* Only support bitclock & framesync as consumer */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) {
+ dev_err(dev, "Only support BCK and LRCK as consumer\n");
+ return -EINVAL;
+ }
+
+ /* Store here and will be used in runtime hw_params for DAI format setting */
+ data->daifmt = fmt;
+
+ return 0;
+}
+
+static int rtq9128_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int mask, start_loc, srcin_select;
+ int i, frame_length, ret;
+
+ dev_dbg(dev, "%s: slot %d slot_width %d, tx/rx mask 0x%x 0x%x\n", __func__, slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slot_width % 8) {
+ dev_err(dev, "Invalid slot numbers (%d) or width (%d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ /* HW supported maximum frame length 512 */
+ frame_length = slots * slot_width;
+ if (frame_length > 512) {
+ dev_err(dev, "frame length exceed the maximum (%d)\n", frame_length);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(tx_mask) > slots || hweight_long(rx_mask) > slots ||
+ fls(tx_mask) > slots || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid tx/rx mask (0x%x/0x%x)\n", tx_mask, rx_mask);
+ return -EINVAL;
+ }
+
+ for (mask = tx_mask, i = 0; i < 4 && mask; i++) {
+ start_loc = (ffs(mask) - 1) * slot_width / 8;
+ mask &= ~BIT(ffs(mask) - 1);
+
+ ret = snd_soc_component_write(comp, RTQ9128_REG_TDM_TX_CH1 + i, start_loc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assign tx_loc %d (%d)\n", i, ret);
+ return ret;
+ }
+ }
+
+ for (mask = rx_mask, i = 0; i < 4 && mask; i++) {
+ start_loc = (ffs(mask) - 1) * slot_width / 8;
+ mask &= ~BIT(ffs(mask) - 1);
+
+ ret = snd_soc_component_write(comp, RTQ9128_REG_TDM_RX_CH1 + i, start_loc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assign rx_loc %d (%d)\n", i, ret);
+ return ret;
+ }
+ }
+
+ srcin_select = data->tdm_input_data2_select ? RTQ9128_TDMSRCIN_MASK : 0;
+ ret = snd_soc_component_update_bits(comp, RTQ9128_REG_SDO_SEL, RTQ9128_TDMSRCIN_MASK,
+ srcin_select);
+ if (ret < 0) {
+ dev_err(dev, "Failed to configure TDM source input select\n");
+ return ret;
+ }
+
+ data->tdm_slots = slots;
+ data->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9128_dai_hw_params(struct snd_pcm_substream *stream, struct snd_pcm_hw_params *param,
+ struct snd_soc_dai *dai)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ unsigned int width, slot_width, bitrate, audbit, dolen;
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int fmtval, audfmt;
+ int ret;
+
+ dev_dbg(dev, "%s: width %d\n", __func__, params_width(param));
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, data->daifmt);
+ if (data->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM is used, format only support DSP_A or DSP_B\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 10;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = data->tdm_slots ? 12 : 11;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = data->tdm_slots ? 4 : 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported format 0x%8x\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 18:
+ audbit = 1;
+ break;
+ case 20:
+ audbit = 2;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width (%d)\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+
+ if (data->tdm_slots) {
+ if (slot_width > data->tdm_slot_width) {
+ dev_err(dev, "slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ /* Check BCK not exceed the maximum supported rate 24.576MHz */
+ bitrate = data->tdm_slots * data->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "bitrate exceed the maximum (%d)\n", bitrate);
+ return -EINVAL;
+ }
+
+ /* If TDM is used, configure slot width as TDM slot witdh */
+ slot_width = data->tdm_slot_width;
+ }
+
+ switch (slot_width) {
+ case 16:
+ dolen = 0;
+ break;
+ case 24:
+ dolen = 1;
+ break;
+ case 32:
+ dolen = 2;
+ break;
+ default:
+ dev_err(dev, "Unsupported slot width (%d)\n", slot_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDFMT_MASK, audfmt);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDBIT_MASK, audbit);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_SDO_SEL, RTQ9128_DOLEN_MASK, dolen);
+ return ret < 0 ? ret : 0;
+}
+
+static int rtq9128_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ int ret;
+
+ dev_dbg(dev, "%s: mute (%d), stream (%d)\n", __func__, mute, stream);
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_DSP_EN, RTQ9128_MSMUTE_MASK,
+ mute ? 1 : 0);
+ return ret < 0 ? ret : 0;
+}
+
+static const struct snd_soc_dai_ops rtq9128_dai_ops = {
+ .set_fmt = rtq9128_dai_set_fmt,
+ .set_tdm_slot = rtq9128_dai_set_tdm_slot,
+ .hw_params = rtq9128_dai_hw_params,
+ .mute_stream = rtq9128_dai_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define RTQ9128_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rtq9128_dai = {
+ .name = "rtq9128-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = RTQ9128_FMTS_MASK,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = RTQ9128_FMTS_MASK,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &rtq9128_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int rtq9128_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9128_data *data;
+ struct regmap *regmap;
+ unsigned int venid;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(data->enable))
+ return dev_err_probe(dev, PTR_ERR(data->enable), "Failed to get 'enable' gpio\n");
+ else if (data->enable)
+ usleep_range(10000, 11000);
+
+ data->tdm_input_data2_select = device_property_read_bool(dev,
+ "richtek,tdm-input-data2-select");
+
+ i2c_set_clientdata(i2c, data);
+
+ /*
+ * Due to the bad design to combine SOFT_RESET bit with other function,
+ * directly use generic i2c API to trigger SOFT_RESET.
+ */
+ ret = i2c_smbus_write_byte_data(i2c, RTQ9128_REG_MISC, RTQ9128_SOFT_RESET_VAL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger software reset\n");
+
+ /* After trigger soft reset, have to wait 10ms for digital reset done */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, &rtq9128_regmap_bus, dev, &rtq9128_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &venid);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get vendor id\n");
+
+ venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, venid);
+ if (venid != RTQ9128_VENDOR_ID_VAL)
+ return dev_err_probe(dev, -ENODEV, "Vendor ID not match (0x%x)\n", venid);
+
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1);
+}
+
+static int rtq9128_pm_runtime_suspend(struct device *dev)
+{
+ struct rtq9128_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ /* If 'enable' gpio not specified, change all channels to ultra low quiescent */
+ if (!data->enable)
+ return regmap_write(regmap, RTQ9128_REG_STATE_CTRL, RTQ9128_ALLCH_ULQM_VAL);
+
+ gpiod_set_value_cansleep(data->enable, 0);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int rtq9128_pm_runtime_resume(struct device *dev)
+{
+ struct rtq9128_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ /* If 'enable' gpio not specified, change all channels to default Hi-Z */
+ if (!data->enable)
+ return regmap_write(regmap, RTQ9128_REG_STATE_CTRL, RTQ9128_ALLCH_HIZ_VAL);
+
+ gpiod_set_value_cansleep(data->enable, 1);
+
+ /* Wait digital block to be ready */
+ usleep_range(10000, 11000);
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
+static const struct dev_pm_ops rtq9128_pm_ops = {
+ RUNTIME_PM_OPS(rtq9128_pm_runtime_suspend, rtq9128_pm_runtime_resume, NULL)
+};
+
+static const struct of_device_id rtq9128_device_table[] = {
+ { .compatible = "richtek,rtq9128" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9128_device_table);
+
+static struct i2c_driver rtq9128_driver = {
+ .driver = {
+ .name = "rtq9128",
+ .of_match_table = rtq9128_device_table,
+ .pm = pm_ptr(&rtq9128_pm_ops),
+ },
+ .probe = rtq9128_probe,
+};
+module_i2c_driver(rtq9128_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RTQ9128 4CH Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
new file mode 100644
index 000000000000..574c08b14f0c
--- /dev/null
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// sdw-mockup.c -- a mockup SoundWire codec for tests where only the host
+// drives the bus.
+//
+// Copyright(c) 2021 Intel Corporation
+//
+//
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+struct sdw_mockup_priv {
+ struct sdw_slave *slave;
+};
+
+static int sdw_mockup_component_probe(struct snd_soc_component *component)
+{
+ return 0;
+}
+
+static void sdw_mockup_component_remove(struct snd_soc_component *component)
+{
+}
+
+static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
+ .probe = sdw_mockup_component_probe,
+ .remove = sdw_mockup_component_remove,
+ .endianness = 1,
+};
+
+static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void sdw_mockup_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!sdw_mockup->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 8;
+
+ ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret)
+ dev_err(dai->dev, "Unable to configure port\n");
+
+ return ret;
+}
+
+static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_mockup->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(sdw_mockup->slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sdw_mockup_ops = {
+ .hw_params = sdw_mockup_pcm_hw_params,
+ .hw_free = sdw_mockup_pcm_hw_free,
+ .set_stream = sdw_mockup_set_sdw_stream,
+ .shutdown = sdw_mockup_shutdown,
+};
+
+static struct snd_soc_dai_driver sdw_mockup_dai[] = {
+ {
+ .name = "sdw-mockup-aif1",
+ .id = 1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "DP8 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &sdw_mockup_ops,
+ },
+};
+
+static int sdw_mockup_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ return 0;
+}
+
+static int sdw_mockup_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->paging_support = false;
+
+ /*
+ * first we need to allocate memory for set bits in port lists
+ * the port allocation is completely arbitrary:
+ * DP0 is not supported
+ * DP1 is sink
+ * DP8 is source
+ */
+ prop->source_ports = BIT(8);
+ prop->sink_ports = BIT(1);
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop),
+ GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ j++;
+ }
+
+ prop->simple_clk_stop_capable = true;
+
+ /* wake-up event */
+ prop->wake_capable = 0;
+
+ return 0;
+}
+
+static int sdw_mockup_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ return 0;
+}
+
+static int sdw_mockup_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ return 0;
+}
+
+static const struct sdw_slave_ops sdw_mockup_slave_ops = {
+ .read_prop = sdw_mockup_read_prop,
+ .interrupt_callback = sdw_mockup_interrupt_callback,
+ .update_status = sdw_mockup_update_status,
+ .bus_config = sdw_mockup_bus_config,
+};
+
+static int sdw_mockup_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &slave->dev;
+ struct sdw_mockup_priv *sdw_mockup;
+ int ret;
+
+ sdw_mockup = devm_kzalloc(dev, sizeof(*sdw_mockup), GFP_KERNEL);
+ if (!sdw_mockup)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, sdw_mockup);
+ sdw_mockup->slave = slave;
+
+ slave->is_mockup_device = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &snd_soc_sdw_mockup_component,
+ sdw_mockup_dai,
+ ARRAY_SIZE(sdw_mockup_dai));
+
+ return ret;
+}
+
+static int sdw_mockup_sdw_remove(struct sdw_slave *slave)
+{
+ return 0;
+}
+
+/*
+ * Intel reserved parts ID with the following mapping expected:
+ * 0xAAAA: generic full-duplex codec
+ * 0xAA55: headset codec (mock-up of RT711/RT5682) - full-duplex
+ * 0x55AA: amplifier (mock-up of RT1308/Maxim 98373) - playback only with
+ * IV feedback
+ * 0x5555: mic codec (mock-up of RT715) - capture-only
+ */
+static const struct sdw_device_id sdw_mockup_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0xAAAA, 0x0, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0xAA55, 0x0, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0x55AA, 0x0, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0105, 0x5555, 0x0, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, sdw_mockup_id);
+
+static struct sdw_driver sdw_mockup_sdw_driver = {
+ .driver = {
+ .name = "sdw-mockup",
+ },
+ .probe = sdw_mockup_sdw_probe,
+ .remove = sdw_mockup_sdw_remove,
+ .ops = &sdw_mockup_slave_ops,
+ .id_table = sdw_mockup_id,
+};
+module_sdw_driver(sdw_mockup_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC SDW mockup codec driver");
+MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 08b40460663c..320312f8db92 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1,12 +1,8 @@
-/*
- * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
- *
- * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
+//
+// Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -17,11 +13,11 @@
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/log2.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/pcm.h>
@@ -35,10 +31,16 @@
#define SGTL5000_DAP_REG_OFFSET 0x0100
#define SGTL5000_MAX_REG_OFFSET 0x013A
+/* Delay for the VAG ramp up */
+#define SGTL5000_VAG_POWERUP_DELAY 500 /* ms */
+/* Delay for the VAG ramp down */
+#define SGTL5000_VAG_POWERDOWN_DELAY 500 /* ms */
+
+#define SGTL5000_OUTPUTS_MUTE (SGTL5000_HP_MUTE | SGTL5000_LINE_OUT_MUTE)
+
/* default value of sgtl5000 registers */
static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_DIG_POWER, 0x0000 },
- { SGTL5000_CHIP_CLK_CTRL, 0x0008 },
{ SGTL5000_CHIP_I2S_CTRL, 0x0010 },
{ SGTL5000_CHIP_SSS_CTRL, 0x0010 },
{ SGTL5000_CHIP_ADCDAC_CTRL, 0x020c },
@@ -47,12 +49,10 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
{ SGTL5000_CHIP_ANA_CTRL, 0x0111 },
- { SGTL5000_CHIP_LINREG_CTRL, 0x0000 },
{ SGTL5000_CHIP_REF_CTRL, 0x0000 },
{ SGTL5000_CHIP_MIC_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
- { SGTL5000_CHIP_ANA_POWER, 0x7060 },
{ SGTL5000_CHIP_PLL_CTRL, 0x5000 },
{ SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_STATUS, 0x0000 },
@@ -71,12 +71,26 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
{ SGTL5000_DAP_MAIN_CHAN, 0x8000 },
{ SGTL5000_DAP_MIX_CHAN, 0x0000 },
- { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { SGTL5000_DAP_AVC_CTRL, 0x5100 },
{ SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
{ SGTL5000_DAP_AVC_ATTACK, 0x0028 },
{ SGTL5000_DAP_AVC_DECAY, 0x0050 },
};
+/* AVC: Threshold dB -> register: pre-calculated values */
+static const u16 avc_thr_db2reg[97] = {
+ 0x5168, 0x488E, 0x40AA, 0x39A1, 0x335D, 0x2DC7, 0x28CC, 0x245D, 0x2068,
+ 0x1CE2, 0x19BE, 0x16F1, 0x1472, 0x1239, 0x103E, 0x0E7A, 0x0CE6, 0x0B7F,
+ 0x0A3F, 0x0922, 0x0824, 0x0741, 0x0677, 0x05C3, 0x0522, 0x0493, 0x0414,
+ 0x03A2, 0x033D, 0x02E3, 0x0293, 0x024B, 0x020B, 0x01D2, 0x019F, 0x0172,
+ 0x014A, 0x0126, 0x0106, 0x00E9, 0x00D0, 0x00B9, 0x00A5, 0x0093, 0x0083,
+ 0x0075, 0x0068, 0x005D, 0x0052, 0x0049, 0x0041, 0x003A, 0x0034, 0x002E,
+ 0x0029, 0x0025, 0x0021, 0x001D, 0x001A, 0x0017, 0x0014, 0x0012, 0x0010,
+ 0x000E, 0x000D, 0x000B, 0x000A, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005,
+ 0x0005, 0x0004, 0x0004, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
+
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
enum sgtl5000_regulator_supplies {
VDDA,
@@ -92,41 +106,35 @@ static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
"VDDD"
};
-#define LDO_CONSUMER_NAME "VDDD_LDO"
#define LDO_VOLTAGE 1200000
+#define LINREG_VDDD ((1600 - LDO_VOLTAGE / 1000) / 50)
-static struct regulator_consumer_supply ldo_consumer[] = {
- REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
+enum sgtl5000_micbias_resistor {
+ SGTL5000_MICBIAS_OFF = 0,
+ SGTL5000_MICBIAS_2K = 2,
+ SGTL5000_MICBIAS_4K = 4,
+ SGTL5000_MICBIAS_8K = 8,
};
-static struct regulator_init_data ldo_init_data = {
- .constraints = {
- .min_uV = 1200000,
- .max_uV = 1200000,
- .valid_modes_mask = REGULATOR_MODE_NORMAL,
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &ldo_consumer[0],
+enum {
+ I2S_LRCLK_STRENGTH_DISABLE,
+ I2S_LRCLK_STRENGTH_LOW,
+ I2S_LRCLK_STRENGTH_MEDIUM,
+ I2S_LRCLK_STRENGTH_HIGH,
};
-/*
- * sgtl5000 internal ldo regulator,
- * enabled when VDDD not provided
- */
-struct ldo_regulator {
- struct regulator_desc desc;
- struct regulator_dev *dev;
- int voltage;
- void *codec_data;
- bool enabled;
+enum {
+ I2S_SCLK_STRENGTH_DISABLE,
+ I2S_SCLK_STRENGTH_LOW,
+ I2S_SCLK_STRENGTH_MEDIUM,
+ I2S_SCLK_STRENGTH_HIGH,
};
-enum sgtl5000_micbias_resistor {
- SGTL5000_MICBIAS_OFF = 0,
- SGTL5000_MICBIAS_2K = 2,
- SGTL5000_MICBIAS_4K = 4,
- SGTL5000_MICBIAS_8K = 8,
+enum {
+ HP_POWER_EVENT,
+ DAC_POWER_EVENT,
+ ADC_POWER_EVENT,
+ LAST_POWER_EVENT = ADC_POWER_EVENT
};
/* sgtl5000 private structure in codec */
@@ -135,14 +143,117 @@ struct sgtl5000_priv {
int master; /* i2s master or not */
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
- struct ldo_regulator *ldo;
+ int num_supplies;
struct regmap *regmap;
struct clk *mclk;
int revision;
u8 micbias_resistor;
u8 micbias_voltage;
+ u8 lrclk_strength;
+ u8 sclk_strength;
+ u16 mute_state[LAST_POWER_EVENT + 1];
};
+static inline int hp_sel_input(struct snd_soc_component *component)
+{
+ return (snd_soc_component_read(component, SGTL5000_CHIP_ANA_CTRL) &
+ SGTL5000_HP_SEL_MASK) >> SGTL5000_HP_SEL_SHIFT;
+}
+
+static inline u16 mute_output(struct snd_soc_component *component,
+ u16 mute_mask)
+{
+ u16 mute_reg = snd_soc_component_read(component,
+ SGTL5000_CHIP_ANA_CTRL);
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ mute_mask, mute_mask);
+ return mute_reg;
+}
+
+static inline void restore_output(struct snd_soc_component *component,
+ u16 mute_mask, u16 mute_reg)
+{
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ mute_mask, mute_reg);
+}
+
+static void vag_power_on(struct snd_soc_component *component, u32 source)
+{
+ if (snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER) &
+ SGTL5000_VAG_POWERUP)
+ return;
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+
+ /* When VAG powering on to get local loop from Line-In, the sleep
+ * is required to avoid loud pop.
+ */
+ if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN &&
+ source == HP_POWER_EVENT)
+ msleep(SGTL5000_VAG_POWERUP_DELAY);
+}
+
+static int vag_power_consumers(struct snd_soc_component *component,
+ u16 ana_pwr_reg, u32 source)
+{
+ int consumers = 0;
+
+ /* count dac/adc consumers unconditional */
+ if (ana_pwr_reg & SGTL5000_DAC_POWERUP)
+ consumers++;
+ if (ana_pwr_reg & SGTL5000_ADC_POWERUP)
+ consumers++;
+
+ /*
+ * If the event comes from HP and Line-In is selected,
+ * current action is 'DAC to be powered down'.
+ * As HP_POWERUP is not set when HP muxed to line-in,
+ * we need to keep VAG power ON.
+ */
+ if (source == HP_POWER_EVENT) {
+ if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN)
+ consumers++;
+ } else {
+ if (ana_pwr_reg & SGTL5000_HP_POWERUP)
+ consumers++;
+ }
+
+ return consumers;
+}
+
+static void vag_power_off(struct snd_soc_component *component, u32 source)
+{
+ u16 ana_pwr = snd_soc_component_read(component,
+ SGTL5000_CHIP_ANA_POWER);
+
+ if (!(ana_pwr & SGTL5000_VAG_POWERUP))
+ return;
+
+ /*
+ * This function calls when any of VAG power consumers is disappearing.
+ * Thus, if there is more than one consumer at the moment, as minimum
+ * one consumer will definitely stay after the end of the current
+ * event.
+ * Don't clear VAG_POWERUP if 2 or more consumers of VAG present:
+ * - LINE_IN (for HP events) / HP (for DAC/ADC events)
+ * - DAC
+ * - ADC
+ * (the current consumer is disappearing right now)
+ */
+ if (vag_power_consumers(component, ana_pwr, source) >= 2)
+ return;
+
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP, 0);
+ /* In power down case, we need wait 400-1000 ms
+ * when VAG fully ramped down.
+ * As longer we wait, as smaller pop we've got.
+ */
+ msleep(SGTL5000_VAG_POWERDOWN_DELAY);
+}
+
/*
* mic_bias power on/off share the same register bits with
* output impedance of mic bias, when power on mic bias, we
@@ -155,55 +266,65 @@ struct sgtl5000_priv {
static int mic_bias_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* change mic bias resistor */
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK, 0);
break;
}
return 0;
}
-/*
- * As manual described, ADC/DAC only works when VAG powerup,
- * So enabled VAG before ADC/DAC up.
- * In power down case, we need wait 400ms when vag fully ramped down.
- */
-static int power_vag_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int vag_and_mute_control(struct snd_soc_component *component,
+ int event, int event_source)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP;
+ static const u16 mute_mask[] = {
+ /*
+ * Mask for HP_POWER_EVENT.
+ * Muxing Headphones have to be wrapped with mute/unmute
+ * headphones only.
+ */
+ SGTL5000_HP_MUTE,
+ /*
+ * Masks for DAC_POWER_EVENT/ADC_POWER_EVENT.
+ * Muxing DAC or ADC block have to wrapped with mute/unmute
+ * both headphones and line-out.
+ */
+ SGTL5000_OUTPUTS_MUTE,
+ SGTL5000_OUTPUTS_MUTE
+ };
+
+ struct sgtl5000_priv *sgtl5000 =
+ snd_soc_component_get_drvdata(component);
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sgtl5000->mute_state[event_source] =
+ mute_output(component, mute_mask[event_source]);
+ break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
- msleep(400);
+ vag_power_on(component, event_source);
+ restore_output(component, mute_mask[event_source],
+ sgtl5000->mute_state[event_source]);
break;
-
case SND_SOC_DAPM_PRE_PMD:
- /*
- * Don't clear VAG_POWERUP, when both DAC and ADC are
- * operational to prevent inadvertently starving the
- * other one of them.
- */
- if ((snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER) &
- mask) != mask) {
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, 0);
- msleep(400);
- }
+ sgtl5000->mute_state[event_source] =
+ mute_output(component, mute_mask[event_source]);
+ vag_power_off(component, event_source);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ restore_output(component, mute_mask[event_source],
+ sgtl5000->mute_state[event_source]);
break;
default:
break;
@@ -212,6 +333,41 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
return 0;
}
+/*
+ * Mute Headphone when power it up/down.
+ * Control VAG power on HP power path.
+ */
+static int headphone_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ return vag_and_mute_control(component, event, HP_POWER_EVENT);
+}
+
+/* As manual describes, ADC/DAC powering up/down requires
+ * to mute outputs to avoid pops.
+ * Control VAG power on ADC/DAC power path.
+ */
+static int adc_updown_depop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ return vag_and_mute_control(component, event, ADC_POWER_EVENT);
+}
+
+static int dac_updown_depop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ return vag_and_mute_control(component, event, DAC_POWER_EVENT);
+}
+
/* input sources for ADC */
static const char *adc_mux_text[] = {
"MIC_IN", "LINE_IN"
@@ -224,17 +380,54 @@ static SOC_ENUM_SINGLE_DECL(adc_enum,
static const struct snd_kcontrol_new adc_mux =
SOC_DAPM_ENUM("Capture Mux", adc_enum);
+/* input sources for headphone */
+static const char *hp_mux_text[] = {
+ "DAC", "LINE_IN"
+};
+
+static SOC_ENUM_SINGLE_DECL(hp_enum,
+ SGTL5000_CHIP_ANA_CTRL, 6,
+ hp_mux_text);
+
+static const struct snd_kcontrol_new hp_mux =
+SOC_DAPM_ENUM("Headphone Mux", hp_enum);
+
/* input sources for DAC */
static const char *dac_mux_text[] = {
- "DAC", "LINE_IN"
+ "ADC", "I2S", "Rsvrd", "DAP"
};
static SOC_ENUM_SINGLE_DECL(dac_enum,
- SGTL5000_CHIP_ANA_CTRL, 6,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAC_SEL_SHIFT,
dac_mux_text);
static const struct snd_kcontrol_new dac_mux =
-SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+SOC_DAPM_ENUM("Digital Input Mux", dac_enum);
+
+/* input sources for DAP */
+static const char *dap_mux_text[] = {
+ "ADC", "I2S"
+};
+
+static SOC_ENUM_SINGLE_DECL(dap_enum,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_SEL_SHIFT,
+ dap_mux_text);
+
+static const struct snd_kcontrol_new dap_mux =
+SOC_DAPM_ENUM("DAP Mux", dap_enum);
+
+/* input sources for DAP mix */
+static const char *dapmix_mux_text[] = {
+ "ADC", "I2S"
+};
+
+static SOC_ENUM_SINGLE_DECL(dapmix_enum,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_MIX_SEL_SHIFT,
+ dapmix_mux_text);
+
+static const struct snd_kcontrol_new dapmix_mux =
+SOC_DAPM_ENUM("DAP MIX Mux", dapmix_enum);
+
static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINE_IN"),
@@ -247,11 +440,19 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
mic_bias_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0,
+ headphone_pga_event,
+ SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
- SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+ SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &hp_mux),
+ SND_SOC_DAPM_MUX("Digital Input Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+ SND_SOC_DAPM_MUX("DAP Mux", SGTL5000_DAP_CTRL, 0, 0, &dap_mux),
+ SND_SOC_DAPM_MUX("DAP MIX Mux", SGTL5000_DAP_CTRL, 4, 0, &dapmix_mux),
+ SND_SOC_DAPM_MIXER("DAP", SGTL5000_CHIP_DIG_POWER, 4, 0, NULL, 0),
+
/* aif for i2s input */
SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
@@ -263,11 +464,12 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
- SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
-
- SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
- SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0,
+ adc_updown_depop, SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0,
+ dac_updown_depop, SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
};
/* routes for sgtl5000 */
@@ -278,7 +480,19 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
- {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
+ {"DAP Mux", "ADC", "ADC"}, /* adc --> DAP mux */
+ {"DAP Mux", NULL, "AIFIN"}, /* i2s --> DAP mux */
+ {"DAP", NULL, "DAP Mux"}, /* DAP mux --> dap */
+
+ {"DAP MIX Mux", "ADC", "ADC"}, /* adc --> DAP MIX mux */
+ {"DAP MIX Mux", NULL, "AIFIN"}, /* i2s --> DAP MIX mux */
+ {"DAP", NULL, "DAP MIX Mux"}, /* DAP MIX mux --> dap */
+
+ {"Digital Input Mux", "ADC", "ADC"}, /* adc --> audio mux */
+ {"Digital Input Mux", NULL, "AIFIN"}, /* i2s --> audio mux */
+ {"Digital Input Mux", NULL, "DAP"}, /* dap --> audio mux */
+ {"DAC", NULL, "Digital Input Mux"}, /* audio mux --> dac */
+
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
@@ -326,12 +540,12 @@ static int dac_info_volsw(struct snd_kcontrol *kcontrol,
static int dac_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int reg;
int l;
int r;
- reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+ reg = snd_soc_component_read(component, SGTL5000_CHIP_DAC_VOL);
/* get left channel volume */
l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
@@ -379,7 +593,7 @@ static int dac_get_volsw(struct snd_kcontrol *kcontrol,
static int dac_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int reg;
int l;
int r;
@@ -399,7 +613,66 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
- snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+ snd_soc_component_write(component, SGTL5000_CHIP_DAC_VOL, reg);
+
+ return 0;
+}
+
+/*
+ * custom function to get AVC threshold
+ *
+ * The threshold dB is calculated by rearranging the calculation from the
+ * avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==>
+ * dB = ( fls(register_value) - 14.347 ) * 6.02
+ *
+ * As this calculation is expensive and the threshold dB values may not exceed
+ * 0 to 96 we use pre-calculated values.
+ */
+static int avc_get_threshold(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int db, i;
+ u16 reg = snd_soc_component_read(component, SGTL5000_DAP_AVC_THRESHOLD);
+
+ /* register value 0 => -96dB */
+ if (!reg) {
+ ucontrol->value.integer.value[0] = 96;
+ ucontrol->value.integer.value[1] = 96;
+ return 0;
+ }
+
+ /* get dB from register value (rounded down) */
+ for (i = 0; avc_thr_db2reg[i] > reg; i++)
+ ;
+ db = i;
+
+ ucontrol->value.integer.value[0] = db;
+ ucontrol->value.integer.value[1] = db;
+
+ return 0;
+}
+
+/*
+ * custom function to put AVC threshold
+ *
+ * The register value is calculated by following formula:
+ * register_value = 10^(dB/20) * 0.636 * 2^15
+ * As this calculation is expensive and the threshold dB values may not exceed
+ * 0 to 96 we use pre-calculated values.
+ */
+static int avc_put_threshold(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int db;
+ u16 reg;
+
+ db = (int)ucontrol->value.integer.value[0];
+ if (db < 0 || db > 96)
+ return -EINVAL;
+ reg = avc_thr_db2reg[db];
+ snd_soc_component_write(component, SGTL5000_DAP_AVC_THRESHOLD, reg);
return 0;
}
@@ -412,9 +685,24 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0)
);
+/* tlv for DAP channels, 0% - 100% - 200% */
+static const DECLARE_TLV_DB_SCALE(dap_volume, 0, 1, 0);
+
+/* tlv for bass bands, -11.75db to 12.0db, step .25db */
+static const DECLARE_TLV_DB_SCALE(bass_band, -1175, 25, 0);
+
/* tlv for hp volume, -51.5db to 12.0db, step .5db */
static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+/* tlv for lineout volume, 31 steps of .5db each */
+static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0);
+
+/* tlv for dap avc max gain, 0db, 6db, 12db */
+static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0);
+
+/* tlv for dap avc threshold, */
+static const DECLARE_TLV_DB_MINMAX(avc_threshold, 0, 9600);
+
static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
/* SOC_DOUBLE_S8_TLV with invert */
{
@@ -432,27 +720,73 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
SGTL5000_CHIP_ANA_ADC_CTRL,
8, 1, 0, capture_6db_attenuate),
SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
+ SOC_SINGLE("Capture Switch", SGTL5000_CHIP_ANA_CTRL, 0, 1, 1),
SOC_DOUBLE_TLV("Headphone Playback Volume",
SGTL5000_CHIP_ANA_HP_CTRL,
0, 8,
0x7f, 1,
headphone_volume),
+ SOC_SINGLE("Headphone Playback Switch", SGTL5000_CHIP_ANA_CTRL,
+ 4, 1, 1),
SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL,
5, 1, 0),
SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
0, 3, 0, mic_gain_tlv),
+
+ SOC_DOUBLE_TLV("Lineout Playback Volume",
+ SGTL5000_CHIP_LINE_OUT_VOL,
+ SGTL5000_LINE_OUT_VOL_LEFT_SHIFT,
+ SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT,
+ 0x1f, 1,
+ lineout_volume),
+ SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1),
+
+ SOC_SINGLE_TLV("DAP Main channel", SGTL5000_DAP_MAIN_CHAN,
+ 0, 0xffff, 0, dap_volume),
+
+ SOC_SINGLE_TLV("DAP Mix channel", SGTL5000_DAP_MIX_CHAN,
+ 0, 0xffff, 0, dap_volume),
+ /* Automatic Volume Control (DAP AVC) */
+ SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0),
+ SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0),
+ SOC_SINGLE_TLV("AVC Max Gain Volume", SGTL5000_DAP_AVC_CTRL, 12, 2, 0,
+ avc_max_gain),
+ SOC_SINGLE("AVC Integrator Response", SGTL5000_DAP_AVC_CTRL, 8, 3, 0),
+ SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD,
+ 0, 96, 0, avc_get_threshold, avc_put_threshold,
+ avc_threshold),
+
+ SOC_SINGLE_TLV("BASS 0", SGTL5000_DAP_EQ_BASS_BAND0,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 1", SGTL5000_DAP_EQ_BASS_BAND1,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 2", SGTL5000_DAP_EQ_BASS_BAND2,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 3", SGTL5000_DAP_EQ_BASS_BAND3,
+ 0, 0x5F, 0, bass_band),
+
+ SOC_SINGLE_TLV("BASS 4", SGTL5000_DAP_EQ_BASS_BAND4,
+ 0, 0x5F, 0, bass_band),
};
/* mute the codec used by alsa core */
-static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int sgtl5000_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+ struct snd_soc_component *component = codec_dai->component;
+ u16 i2s_pwr = SGTL5000_I2S_IN_POWERUP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
- adcdac_ctrl, mute ? adcdac_ctrl : 0);
+ /*
+ * During 'digital mute' do not mute DAC
+ * because LINE_IN would be muted aswell. We want to mute
+ * only I2S block - this can be done by powering it off
+ */
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_DIG_POWER,
+ i2s_pwr, mute ? 0 : i2s_pwr);
return 0;
}
@@ -460,8 +794,8 @@ static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
/* set codec format */
static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
u16 i2sctl = 0;
sgtl5000->master = 0;
@@ -472,9 +806,9 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
* - clock and frame master
*/
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
i2sctl |= SGTL5000_I2S_MASTER;
sgtl5000->master = 1;
break;
@@ -519,7 +853,7 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_I2S_CTRL, i2sctl);
return 0;
}
@@ -528,8 +862,8 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case SGTL5000_SYSCLK:
@@ -555,9 +889,9 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
* and above.
* 3. usage of sys_mclk is preferred over pll to save power.
*/
-static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
+static int sgtl5000_set_clock(struct snd_soc_component *component, int frame_rate)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
int clk_ctl = 0;
int sys_fs; /* sample freq */
@@ -610,7 +944,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
break;
default:
- dev_err(codec->dev, "frame rate %d not supported\n",
+ dev_err(component->dev, "frame rate %d not supported\n",
frame_rate);
return -EINVAL;
}
@@ -639,9 +973,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
SGTL5000_MCLK_FREQ_SHIFT;
} else {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PLL not supported in slave mode\n");
- dev_err(codec->dev, "%d ratio is not supported. "
+ dev_err(component->dev, "%d ratio is not supported. "
"SYS_MCLK needs to be 256, 384 or 512 * fs\n",
sgtl5000->sysclk / frame_rate);
return -EINVAL;
@@ -674,31 +1008,31 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
- snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
if (div2)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
SGTL5000_INPUT_FREQ_DIV2);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
0);
/* power up pll */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
/* if using pll, clk_ctrl must be set after pll power up */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
} else {
/* otherwise, clk_ctrl must be set before pll power down */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
/* power down pll */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
0);
}
@@ -714,8 +1048,8 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
int channels = params_channels(params);
int i2s_ctl = 0;
int stereo;
@@ -723,7 +1057,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
/* sysclk should already set */
if (!sgtl5000->sysclk) {
- dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+ dev_err(component->dev, "%s: set sysclk first!\n", __func__);
return -EFAULT;
}
@@ -733,11 +1067,11 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
stereo = SGTL5000_ADC_STEREO;
/* set mono to save power */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, stereo,
channels == 1 ? 0 : stereo);
/* set codec clock base on lrclk */
- ret = sgtl5000_set_clock(codec, params_rate(params));
+ ret = sgtl5000_set_clock(component, params_rate(params));
if (ret)
return ret;
@@ -771,162 +1105,13 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_I2S_CTRL,
SGTL5000_I2S_DLEN_MASK | SGTL5000_I2S_SCLKFREQ_MASK,
i2s_ctl);
return 0;
}
-#ifdef CONFIG_REGULATOR
-static int ldo_regulator_is_enabled(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
- return ldo->enabled;
-}
-
-static int ldo_regulator_enable(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
- struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
- int reg;
-
- if (ldo_regulator_is_enabled(dev))
- return 0;
-
- /* set regulator value firstly */
- reg = (1600 - ldo->voltage / 1000) / 50;
- reg = clamp(reg, 0x0, 0xf);
-
- /* amend the voltage value, unit: uV */
- ldo->voltage = (1600 - reg * 50) * 1000;
-
- /* set voltage to register */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, reg);
-
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINEREG_D_POWERUP,
- SGTL5000_LINEREG_D_POWERUP);
-
- /* when internal ldo is enabled, simple digital power can be disabled */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP,
- 0);
-
- ldo->enabled = 1;
- return 0;
-}
-
-static int ldo_regulator_disable(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
- struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
-
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINEREG_D_POWERUP,
- 0);
-
- /* clear voltage info */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, 0);
-
- ldo->enabled = 0;
-
- return 0;
-}
-
-static int ldo_regulator_get_voltage(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
- return ldo->voltage;
-}
-
-static struct regulator_ops ldo_regulator_ops = {
- .is_enabled = ldo_regulator_is_enabled,
- .enable = ldo_regulator_enable,
- .disable = ldo_regulator_disable,
- .get_voltage = ldo_regulator_get_voltage,
-};
-
-static int ldo_regulator_register(struct snd_soc_codec *codec,
- struct regulator_init_data *init_data,
- int voltage)
-{
- struct ldo_regulator *ldo;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- struct regulator_config config = { };
-
- ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL);
-
- if (!ldo)
- return -ENOMEM;
-
- ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL);
- if (!ldo->desc.name) {
- kfree(ldo);
- dev_err(codec->dev, "failed to allocate decs name memory\n");
- return -ENOMEM;
- }
-
- ldo->desc.type = REGULATOR_VOLTAGE;
- ldo->desc.owner = THIS_MODULE;
- ldo->desc.ops = &ldo_regulator_ops;
- ldo->desc.n_voltages = 1;
-
- ldo->codec_data = codec;
- ldo->voltage = voltage;
-
- config.dev = codec->dev;
- config.driver_data = ldo;
- config.init_data = init_data;
-
- ldo->dev = regulator_register(&ldo->desc, &config);
- if (IS_ERR(ldo->dev)) {
- int ret = PTR_ERR(ldo->dev);
-
- dev_err(codec->dev, "failed to register regulator\n");
- kfree(ldo->desc.name);
- kfree(ldo);
-
- return ret;
- }
- sgtl5000->ldo = ldo;
-
- return 0;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- struct ldo_regulator *ldo = sgtl5000->ldo;
-
- if (!ldo)
- return 0;
-
- regulator_unregister(ldo->dev);
- kfree(ldo->desc.name);
- kfree(ldo);
-
- return 0;
-}
-#else
-static int ldo_regulator_register(struct snd_soc_codec *codec,
- struct regulator_init_data *init_data,
- int voltage)
-{
- dev_err(codec->dev, "this setup needs regulator support in the kernel\n");
- return -EINVAL;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
- return 0;
-}
-#endif
-
/*
* set dac bias
* common state changes:
@@ -937,45 +1122,31 @@ static int ldo_regulator_remove(struct snd_soc_codec *codec)
* stop:
* on --> prepare --> standby
*/
-static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+static int sgtl5000_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct sgtl5000_priv *sgtl = snd_soc_component_get_drvdata(component);
int ret;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- ret = regulator_bulk_enable(
- ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- return ret;
- udelay(10);
-
- regcache_cache_only(sgtl5000->regmap, false);
-
- ret = regcache_sync(sgtl5000->regmap);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to restore cache: %d\n", ret);
-
- regcache_cache_only(sgtl5000->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-
- return ret;
- }
+ regcache_cache_only(sgtl->regmap, false);
+ ret = regcache_sync(sgtl->regmap);
+ if (ret) {
+ regcache_cache_only(sgtl->regmap, true);
+ return ret;
}
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_REFTOP_POWERUP,
+ SGTL5000_REFTOP_POWERUP);
break;
case SND_SOC_BIAS_OFF:
- regcache_cache_only(sgtl5000->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
+ regcache_cache_only(sgtl->regmap, true);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_REFTOP_POWERUP, 0);
break;
}
@@ -989,9 +1160,10 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops sgtl5000_ops = {
.hw_params = sgtl5000_pcm_hw_params,
- .digital_mute = sgtl5000_digital_mute,
+ .mute_stream = sgtl5000_mute_stream,
.set_fmt = sgtl5000_set_dai_fmt,
.set_sysclk = sgtl5000_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver sgtl5000_dai = {
@@ -1015,7 +1187,7 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.formats = SGTL5000_FORMATS,
},
.ops = &sgtl5000_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
@@ -1113,9 +1285,8 @@ static const u8 vol_quot_table[] = {
* and should be set according to:
* 1. vddd provided by external or not
* 2. vdda and vddio voltage value. > 3.1v or not
- * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
*/
-static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
+static int sgtl5000_set_power_regs(struct snd_soc_component *component)
{
int vddd;
int vdda;
@@ -1127,25 +1298,27 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
int vol_quot;
int lo_vol;
size_t i;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
- vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+ vddd = (sgtl5000->num_supplies > VDDD)
+ ? regulator_get_voltage(sgtl5000->supplies[VDDD].consumer)
+ : LDO_VOLTAGE;
vdda = vdda / 1000;
vddio = vddio / 1000;
vddd = vddd / 1000;
if (vdda <= 0 || vddio <= 0 || vddd < 0) {
- dev_err(codec->dev, "regulator voltage not set correctly\n");
+ dev_err(component->dev, "regulator voltage not set correctly\n");
return -EINVAL;
}
/* according to datasheet, maximum voltage of supplies */
if (vdda > 3600 || vddio > 3600 || vddd > 1980) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"exceed max voltage vdda %dmV vddio %dmV vddd %dmV\n",
vdda, vddio, vddd);
@@ -1153,49 +1326,36 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
}
/* reset value */
- ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+ ana_pwr = snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER);
ana_pwr |= SGTL5000_DAC_STEREO |
SGTL5000_ADC_STEREO |
SGTL5000_REFTOP_POWERUP;
- lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL);
+ lreg_ctrl = snd_soc_component_read(component, SGTL5000_CHIP_LINREG_CTRL);
if (vddio < 3100 && vdda < 3100) {
/* enable internal oscillator used for charge pump */
- snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INT_OSC_EN,
SGTL5000_INT_OSC_EN);
/* Enable VDDC charge pump */
ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
- } else if (vddio >= 3100 && vdda >= 3100) {
+ } else {
ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP;
- /* VDDC use VDDIO rail */
- lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
- lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
- SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ /*
+ * if vddio == vdda the source of charge pump should be
+ * assigned manually to VDDIO
+ */
+ if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer,
+ sgtl5000->supplies[VDDIO].consumer)) {
+ lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
+ lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
+ SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ }
}
- snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
-
- snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ snd_soc_component_write(component, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
- /* 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);
+ snd_soc_component_write(component, SGTL5000_CHIP_ANA_POWER, ana_pwr);
/*
* set ADC/DAC VAG to vdda / 2,
@@ -1210,7 +1370,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
else
vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL,
SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT);
/* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
@@ -1224,7 +1384,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) /
SGTL5000_LINE_OUT_GND_STP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_CTRL,
SGTL5000_LINE_OUT_CURRENT_MASK |
SGTL5000_LINE_OUT_GND_MASK,
lo_vag << SGTL5000_LINE_OUT_GND_SHIFT |
@@ -1238,7 +1398,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
* Searching for a suitable index solving this formula:
* idx = 40 * log10(vag_val / lo_cagcntrl) + 15
*/
- vol_quot = (vag * 100) / lo_vag;
+ vol_quot = lo_vag ? (vag * 100) / lo_vag : 0;
lo_vol = 0;
for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) {
if (vol_quot >= vol_quot_table[i])
@@ -1247,7 +1407,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
break;
}
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_VOL,
SGTL5000_LINE_OUT_VOL_RIGHT_MASK |
SGTL5000_LINE_OUT_VOL_LEFT_MASK,
lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT |
@@ -1256,172 +1416,126 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
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)
+static int sgtl5000_enable_regulators(struct i2c_client *client)
{
int ret;
int i;
int external_vddd = 0;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
struct regulator *vddd;
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++)
sgtl5000->supplies[i].supply = supply_names[i];
- /* External VDDD only works before revision 0x11 */
- if (sgtl5000->revision < 0x11) {
- vddd = regulator_get_optional(codec->dev, "VDDD");
- if (IS_ERR(vddd)) {
- /* See if it's just not registered yet */
- if (PTR_ERR(vddd) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- } else {
- external_vddd = 1;
- regulator_put(vddd);
- }
- }
-
- if (!external_vddd) {
- ret = sgtl5000_replace_vddd_with_ldo(codec);
- if (ret)
- return ret;
+ vddd = regulator_get_optional(&client->dev, "VDDD");
+ if (IS_ERR(vddd)) {
+ /* See if it's just not registered yet */
+ if (PTR_ERR(vddd) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ } else {
+ external_vddd = 1;
+ regulator_put(vddd);
}
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->num_supplies = ARRAY_SIZE(sgtl5000->supplies)
+ - 1 + external_vddd;
+ ret = regulator_bulk_get(&client->dev, sgtl5000->num_supplies,
sgtl5000->supplies);
if (ret)
- goto err_ldo_remove;
-
- ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- goto err_regulator_free;
-
- /* wait for all power rails bring up */
- udelay(10);
+ return ret;
- return 0;
+ ret = regulator_bulk_enable(sgtl5000->num_supplies,
+ sgtl5000->supplies);
+ if (!ret)
+ usleep_range(10, 20);
+ else
+ regulator_bulk_free(sgtl5000->num_supplies,
+ sgtl5000->supplies);
-err_regulator_free:
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-err_ldo_remove:
- if (!external_vddd)
- ldo_regulator_remove(codec);
return ret;
-
}
-static int sgtl5000_probe(struct snd_soc_codec *codec)
+static int sgtl5000_probe(struct snd_soc_component *component)
{
int ret;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
- ret = sgtl5000_enable_regulators(codec);
- if (ret)
- return ret;
+ u16 reg;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
+ unsigned int zcd_mask = SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN;
/* power up sgtl5000 */
- ret = sgtl5000_set_power_regs(codec);
+ ret = sgtl5000_set_power_regs(component);
if (ret)
goto err;
/* enable small pop, introduce 400ms delay in turning off */
- snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP, 1);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL,
+ SGTL5000_SMALL_POP, SGTL5000_SMALL_POP);
/* disable short cut detector */
- snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
+ snd_soc_component_write(component, SGTL5000_CHIP_SHORT_CTRL, 0);
- /*
- * set i2s as default input of sound switch
- * TODO: add sound switch to control and dapm widge.
- */
- snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL,
- SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT);
- snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER,
+ snd_soc_component_write(component, SGTL5000_CHIP_DIG_POWER,
SGTL5000_ADC_EN | SGTL5000_DAC_EN);
/* enable dac volume ramp by default */
- snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ snd_soc_component_write(component, SGTL5000_CHIP_ADCDAC_CTRL,
SGTL5000_DAC_VOL_RAMP_EN |
SGTL5000_DAC_MUTE_RIGHT |
SGTL5000_DAC_MUTE_LEFT);
- snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+ reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT |
+ (sgtl5000->sclk_strength) << SGTL5000_PAD_I2S_SCLK_SHIFT |
+ 0x1f);
+ snd_soc_component_write(component, SGTL5000_CHIP_PAD_STRENGTH, reg);
- snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
- SGTL5000_HP_ZCD_EN |
- SGTL5000_ADC_ZCD_EN);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ zcd_mask, zcd_mask);
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_VOLT_MASK,
sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT);
/*
- * disable DAP
+ * enable DAP Graphic EQ
* TODO:
- * Enable DAP in kcontrol and dapm.
+ * Add control for changing between PEQ/Tone Control/GEQ
*/
- snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+ snd_soc_component_write(component, SGTL5000_DAP_AUDIO_EQ, SGTL5000_DAP_SEL_GEQ);
+
+ /* Unmute DAC after start */
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ADCDAC_CTRL,
+ SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT, 0);
return 0;
err:
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
return ret;
}
-static int sgtl5000_remove(struct snd_soc_codec *codec)
+static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
+ /* return dai id 0, whatever the endpoint index */
return 0;
}
-static struct snd_soc_codec_driver sgtl5000_driver = {
- .probe = sgtl5000_probe,
- .remove = sgtl5000_remove,
- .set_bias_level = sgtl5000_set_bias_level,
- .suspend_bias_off = true,
- .controls = sgtl5000_snd_controls,
- .num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
- .dapm_widgets = sgtl5000_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets),
- .dapm_routes = sgtl5000_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
+static const struct snd_soc_component_driver sgtl5000_driver = {
+ .probe = sgtl5000_probe,
+ .set_bias_level = sgtl5000_set_bias_level,
+ .controls = sgtl5000_snd_controls,
+ .num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
+ .dapm_widgets = sgtl5000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets),
+ .dapm_routes = sgtl5000_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
+ .of_xlate_dai_id = sgtl5000_of_xlate_dai_id,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config sgtl5000_regmap = {
@@ -1448,8 +1562,9 @@ static const struct regmap_config sgtl5000_regmap = {
* and avoid problems like, not being able to probe after an audio playback
* followed by a system reset or a 'reboot' command in Linux
*/
-static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+static void sgtl5000_fill_defaults(struct i2c_client *client)
{
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
int i, ret, val, index;
for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
@@ -1457,52 +1572,64 @@ static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
index = sgtl5000_reg_defaults[i].reg;
ret = regmap_write(sgtl5000->regmap, index, val);
if (ret)
- return ret;
+ dev_err(&client->dev,
+ "%s: error %d setting reg 0x%02x to 0x%04x\n",
+ __func__, ret, index, val);
}
-
- return 0;
}
-static int sgtl5000_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int sgtl5000_i2c_probe(struct i2c_client *client)
{
struct sgtl5000_priv *sgtl5000;
int ret, reg, rev;
struct device_node *np = client->dev.of_node;
u32 value;
+ u16 ana_pwr;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
+ i2c_set_clientdata(client, sgtl5000);
+
+ ret = sgtl5000_enable_regulators(client);
+ if (ret)
+ return ret;
+
sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
if (IS_ERR(sgtl5000->regmap)) {
ret = PTR_ERR(sgtl5000->regmap);
dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
- return ret;
+ goto disable_regs;
}
sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
if (IS_ERR(sgtl5000->mclk)) {
ret = PTR_ERR(sgtl5000->mclk);
- dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
/* Defer the probe to see if the clk will be provided later */
if (ret == -ENOENT)
- return -EPROBE_DEFER;
- return ret;
+ ret = -EPROBE_DEFER;
+
+ dev_err_probe(&client->dev, ret, "Failed to get mclock\n");
+
+ goto disable_regs;
}
ret = clk_prepare_enable(sgtl5000->mclk);
- if (ret)
- return ret;
+ if (ret) {
+ dev_err(&client->dev, "Error enabling clock %d\n", ret);
+ goto disable_regs;
+ }
/* Need 8 clocks before I2C accesses */
udelay(1);
/* read chip information */
ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
- if (ret)
+ if (ret) {
+ dev_err(&client->dev, "Error reading chip id %d\n", ret);
goto disable_clk;
+ }
if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
SGTL5000_PARTID_PART_ID) {
@@ -1516,6 +1643,78 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
sgtl5000->revision = rev;
+ /* reconfigure the clocks in case we're using the PLL */
+ ret = regmap_write(sgtl5000->regmap,
+ SGTL5000_CHIP_CLK_CTRL,
+ SGTL5000_CHIP_CLK_CTRL_DEFAULT);
+ if (ret)
+ dev_err(&client->dev,
+ "Error %d initializing CHIP_CLK_CTRL\n", ret);
+
+ /* Mute everything to avoid pop from the following power-up */
+ ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL,
+ SGTL5000_CHIP_ANA_CTRL_DEFAULT);
+ if (ret) {
+ dev_err(&client->dev,
+ "Error %d muting outputs via CHIP_ANA_CTRL\n", ret);
+ goto disable_clk;
+ }
+
+ /*
+ * If VAG is powered-on (e.g. from previous boot), it would be disabled
+ * by the write to ANA_POWER in later steps of the probe code. This
+ * may create a loud pop even with all outputs muted. The proper way
+ * to circumvent this is disabling the bit first and waiting the proper
+ * cool-down time.
+ */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value);
+ if (ret) {
+ dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret);
+ goto disable_clk;
+ }
+ if (value & SGTL5000_VAG_POWERUP) {
+ ret = regmap_update_bits(sgtl5000->regmap,
+ SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP,
+ 0);
+ if (ret) {
+ dev_err(&client->dev, "Error %d disabling VAG\n", ret);
+ goto disable_clk;
+ }
+
+ msleep(SGTL5000_VAG_POWERDOWN_DELAY);
+ }
+
+ /* Follow section 2.2.1.1 of AN3663 */
+ ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
+ if (sgtl5000->num_supplies <= VDDD) {
+ /* internal VDDD at 1.2V */
+ ret = regmap_update_bits(sgtl5000->regmap,
+ SGTL5000_CHIP_LINREG_CTRL,
+ SGTL5000_LINREG_VDDD_MASK,
+ LINREG_VDDD);
+ if (ret)
+ dev_err(&client->dev,
+ "Error %d setting LINREG_VDDD\n", ret);
+
+ ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
+ dev_info(&client->dev,
+ "Using internal LDO instead of VDDD: check ER1 erratum\n");
+ } else {
+ /* using external LDO for VDDD
+ * Clear startup powerup and simple powerup
+ * bits to save power
+ */
+ ana_pwr &= ~(SGTL5000_STARTUP_POWERUP
+ | SGTL5000_LINREG_SIMPLE_POWERUP);
+ dev_dbg(&client->dev, "Using external VDDD\n");
+ }
+ ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ if (ret)
+ dev_err(&client->dev,
+ "Error %d setting CHIP_ANA_POWER to %04x\n",
+ ret, ana_pwr);
+
if (np) {
if (!of_property_read_u32(np,
"micbias-resistor-k-ohms", &value)) {
@@ -1557,14 +1756,24 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, sgtl5000);
+ sgtl5000->lrclk_strength = I2S_LRCLK_STRENGTH_LOW;
+ if (!of_property_read_u32(np, "lrclk-strength", &value)) {
+ if (value > I2S_LRCLK_STRENGTH_HIGH)
+ value = I2S_LRCLK_STRENGTH_LOW;
+ sgtl5000->lrclk_strength = value;
+ }
+
+ sgtl5000->sclk_strength = I2S_SCLK_STRENGTH_LOW;
+ if (!of_property_read_u32(np, "sclk-strength", &value)) {
+ if (value > I2S_SCLK_STRENGTH_HIGH)
+ value = I2S_SCLK_STRENGTH_LOW;
+ sgtl5000->sclk_strength = value;
+ }
/* Ensure sgtl5000 will start with sane register values */
- ret = sgtl5000_fill_defaults(sgtl5000);
- if (ret)
- goto disable_clk;
+ sgtl5000_fill_defaults(client);
- ret = snd_soc_register_codec(&client->dev,
+ ret = devm_snd_soc_register_component(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
if (ret)
goto disable_clk;
@@ -1573,20 +1782,34 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
disable_clk:
clk_disable_unprepare(sgtl5000->mclk);
+
+disable_regs:
+ regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+ regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+
return ret;
}
-static int sgtl5000_i2c_remove(struct i2c_client *client)
+static void sgtl5000_i2c_remove(struct i2c_client *client)
{
struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
+ regmap_write(sgtl5000->regmap, SGTL5000_CHIP_CLK_CTRL, SGTL5000_CHIP_CLK_CTRL_DEFAULT);
+ regmap_write(sgtl5000->regmap, SGTL5000_CHIP_DIG_POWER, SGTL5000_DIG_POWER_DEFAULT);
+ regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, SGTL5000_ANA_POWER_DEFAULT);
+
clk_disable_unprepare(sgtl5000->mclk);
- return 0;
+ regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+ regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+}
+
+static void sgtl5000_i2c_shutdown(struct i2c_client *client)
+{
+ sgtl5000_i2c_remove(client);
}
static const struct i2c_device_id sgtl5000_id[] = {
- {"sgtl5000", 0},
+ {"sgtl5000"},
{},
};
@@ -1600,11 +1823,12 @@ MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
static struct i2c_driver sgtl5000_i2c_driver = {
.driver = {
- .name = "sgtl5000",
- .of_match_table = sgtl5000_dt_ids,
- },
+ .name = "sgtl5000",
+ .of_match_table = sgtl5000_dt_ids,
+ },
.probe = sgtl5000_i2c_probe,
.remove = sgtl5000_i2c_remove,
+ .shutdown = sgtl5000_i2c_shutdown,
.id_table = sgtl5000_id,
};
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 1c317de26176..3a808c762299 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* sgtl5000.h - SGTL5000 audio codec interface
*
* Copyright 2010-2011 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _SGTL5000_H
@@ -83,6 +80,7 @@
/*
* SGTL5000_CHIP_DIG_POWER
*/
+#define SGTL5000_DIG_POWER_DEFAULT 0x0000
#define SGTL5000_ADC_EN 0x0040
#define SGTL5000_DAC_EN 0x0020
#define SGTL5000_DAP_POWERUP 0x0010
@@ -92,6 +90,7 @@
/*
* SGTL5000_CHIP_CLK_CTRL
*/
+#define SGTL5000_CHIP_CLK_CTRL_DEFAULT 0x0008
#define SGTL5000_RATE_MODE_MASK 0x0030
#define SGTL5000_RATE_MODE_SHIFT 4
#define SGTL5000_RATE_MODE_WIDTH 2
@@ -235,6 +234,7 @@
/*
* SGTL5000_CHIP_ANA_CTRL
*/
+#define SGTL5000_CHIP_ANA_CTRL_DEFAULT 0x0133
#define SGTL5000_LINE_OUT_MUTE 0x0100
#define SGTL5000_HP_SEL_MASK 0x0040
#define SGTL5000_HP_SEL_SHIFT 6
@@ -275,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 1
+#define SGTL5000_SMALL_POP 0x0001
/*
* SGTL5000_CHIP_MIC_CTRL
@@ -325,6 +325,7 @@
/*
* SGTL5000_CHIP_ANA_POWER
*/
+#define SGTL5000_ANA_POWER_DEFAULT 0x7060
#define SGTL5000_DAC_STEREO 0x4000
#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000
#define SGTL5000_STARTUP_POWERUP 0x1000
@@ -397,4 +398,11 @@
#define SGTL5000_SYSCLK 0x00
#define SGTL5000_LRCLK 0x01
+/*
+ * SGTL5000_DAP_AUDIO_EQ
+ */
+#define SGTL5000_DAP_SEL_PEQ 1
+#define SGTL5000_DAP_SEL_TONE_CTRL 2
+#define SGTL5000_DAP_SEL_GEQ 3
+
#endif
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index a8402d0af0ea..d87141ba8438 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
*
@@ -5,16 +6,6 @@
* Copyright (C) 2013 Andrey Smirnov
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
*/
#include <linux/module.h>
@@ -78,7 +69,7 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
int err;
u16 format = 0;
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -140,14 +131,14 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
si476x_core_lock(core);
- err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
+ err = snd_soc_component_update_bits(codec_dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
format);
si476x_core_unlock(core);
if (err < 0) {
- dev_err(codec_dai->codec->dev, "Failed to set output format\n");
+ dev_err(codec_dai->component->dev, "Failed to set output format\n");
return err;
}
@@ -163,7 +154,7 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
rate = params_rate(params);
if (rate < 32000 || rate > 48000) {
- dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate);
+ dev_err(dai->component->dev, "Rate: %d is not supported\n", rate);
return -EINVAL;
}
@@ -186,19 +177,19 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
si476x_core_lock(core);
- err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
+ err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
rate);
if (err < 0) {
- dev_err(dai->codec->dev, "Failed to set sample rate\n");
+ dev_err(dai->component->dev, "Failed to set sample rate\n");
goto out;
}
- err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
+ err = snd_soc_component_update_bits(dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
(width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
(width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
if (err < 0) {
- dev_err(dai->codec->dev, "Failed to set output width\n");
+ dev_err(dai->component->dev, "Failed to set output width\n");
goto out;
}
@@ -231,31 +222,32 @@ static struct snd_soc_dai_driver si476x_dai = {
.ops = &si476x_dai_ops,
};
-static struct regmap *si476x_get_regmap(struct device *dev)
+static int si476x_probe(struct snd_soc_component *component)
{
- return dev_get_regmap(dev->parent, NULL);
+ snd_soc_component_init_regmap(component,
+ dev_get_regmap(component->dev->parent, NULL));
+
+ return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_si476x = {
- .get_regmap = si476x_get_regmap,
- .dapm_widgets = si476x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
- .dapm_routes = si476x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_si476x = {
+ .probe = si476x_probe,
+ .dapm_widgets = si476x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
+ .dapm_routes = si476x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int si476x_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si476x,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_si476x,
&si476x_dai, 1);
}
-static int si476x_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
MODULE_ALIAS("platform:si476x-codec");
static struct platform_driver si476x_platform_driver = {
@@ -263,7 +255,6 @@ static struct platform_driver si476x_platform_driver = {
.name = "si476x-codec",
},
.probe = si476x_platform_probe,
- .remove = si476x_platform_remove,
};
module_platform_driver(si476x_platform_driver);
diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c
index 21ca3a5e9f66..07c9d89ab24a 100644
--- a/sound/soc/codecs/sigmadsp-i2c.c
+++ b/sound/soc/codecs/sigmadsp-i2c.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
@@ -31,7 +30,10 @@ static int sigmadsp_write_i2c(void *control_data,
kfree(buf);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int sigmadsp_read_i2c(void *control_data,
diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c
index 912861be5b87..ba9a6795e470 100644
--- a/sound/soc/codecs/sigmadsp-regmap.c
+++ b/sound/soc/codecs/sigmadsp-regmap.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/regmap.h>
@@ -27,7 +26,7 @@ static int sigmadsp_read_regmap(void *control_data,
}
/**
- * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * devm_sigmadsp_init_regmap() - Initialize SigmaDSP instance
* @dev: The parent device
* @regmap: Regmap instance to use
* @ops: The sigmadsp_ops to use for this instance
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index d53680ac78e4..201f74e3a7ae 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/crc32.h>
@@ -25,6 +24,8 @@
#define SIGMA_FW_CHUNK_TYPE_CONTROL 1
#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2
+#define READBACK_CTRL_NAME "ReadBack"
+
struct sigmadsp_control {
struct list_head head;
uint32_t samplerates;
@@ -32,6 +33,7 @@ struct sigmadsp_control {
unsigned int num_bytes;
const char *name;
struct snd_kcontrol *kcontrol;
+ bool is_readback;
bool cached;
uint8_t cache[];
};
@@ -41,7 +43,7 @@ struct sigmadsp_data {
uint32_t samplerates;
unsigned int addr;
unsigned int length;
- uint8_t data[];
+ uint8_t data[] __counted_by(length);
};
struct sigma_fw_chunk {
@@ -117,8 +119,7 @@ static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp,
struct sigmadsp_control *ctrl, void *data)
{
/* safeload loads up to 20 bytes in a atomic operation */
- if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops &&
- sigmadsp->ops->safeload)
+ if (ctrl->num_bytes <= 20 && sigmadsp->ops && sigmadsp->ops->safeload)
return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data,
ctrl->num_bytes);
else
@@ -143,7 +144,8 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol,
if (ret == 0) {
memcpy(ctrl->cache, data, ctrl->num_bytes);
- ctrl->cached = true;
+ if (!ctrl->is_readback)
+ ctrl->cached = true;
}
mutex_unlock(&sigmadsp->lock);
@@ -166,7 +168,8 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol,
}
if (ret == 0) {
- ctrl->cached = true;
+ if (!ctrl->is_readback)
+ ctrl->cached = true;
memcpy(ucontrol->value.bytes.data, ctrl->cache,
ctrl->num_bytes);
}
@@ -224,15 +227,22 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
if (!ctrl)
return -ENOMEM;
- name = kzalloc(name_len + 1, GFP_KERNEL);
+ name = kmemdup_nul(ctrl_chunk->name, name_len, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err_free_ctrl;
}
- memcpy(name, ctrl_chunk->name, name_len);
- name[name_len] = '\0';
ctrl->name = name;
+ /*
+ * Readbacks doesn't work with non-volatile controls, since the
+ * firmware updates the control value without driver interaction. Mark
+ * the readbacks to ensure that the values are not cached.
+ */
+ if (ctrl->name && strncmp(ctrl->name, READBACK_CTRL_NAME,
+ (sizeof(READBACK_CTRL_NAME) - 1)) == 0)
+ ctrl->is_readback = true;
+
ctrl->addr = le16_to_cpu(ctrl_chunk->addr);
ctrl->num_bytes = num_bytes;
ctrl->samplerates = le32_to_cpu(chunk->samplerates);
@@ -260,7 +270,7 @@ static int sigma_fw_load_data(struct sigmadsp *sigmadsp,
length -= sizeof(*data_chunk);
- data = kzalloc(sizeof(*data) + length, GFP_KERNEL);
+ data = kzalloc(struct_size(data, data, length), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -403,7 +413,8 @@ static int process_sigma_action(struct sigmadsp *sigmadsp,
if (len < 3)
return -EINVAL;
- data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL);
+ data = kzalloc(struct_size(data, data, size_sub(len, 2)),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -659,36 +670,19 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp,
struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
{
struct snd_card *card = sigmadsp->component->card->snd_card;
- struct snd_kcontrol_volatile *vd;
- struct snd_ctl_elem_id id;
bool active;
- bool changed = false;
+ int changed;
active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
-
- down_write(&card->controls_rwsem);
- if (!ctrl->kcontrol) {
- up_write(&card->controls_rwsem);
+ if (!ctrl->kcontrol)
return;
- }
-
- id = ctrl->kcontrol->id;
- vd = &ctrl->kcontrol->vd[0];
- if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) {
- vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- changed = true;
- }
- up_write(&card->controls_rwsem);
-
- if (active && changed) {
+ changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active);
+ if (active && changed > 0) {
mutex_lock(&sigmadsp->lock);
if (ctrl->cached)
sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache);
mutex_unlock(&sigmadsp->lock);
}
-
- if (changed)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id);
}
/**
@@ -811,4 +805,5 @@ int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
}
EXPORT_SYMBOL_GPL(sigmadsp_restrict_params);
+MODULE_DESCRIPTION("Analog Devices SigmaStudio firmware helpers");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h
index 614475cbb823..2783eff633a1 100644
--- a/sound/soc/codecs/sigmadsp.h
+++ b/sound/soc/codecs/sigmadsp.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Load firmware files from Analog Devices SigmaStudio
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __SIGMA_FIRMWARE_H__
@@ -45,7 +44,6 @@ struct sigmadsp {
struct sigmadsp *devm_sigmadsp_init(struct device *dev,
const struct sigmadsp_ops *ops, const char *firmware_name);
-void sigmadsp_reset(struct sigmadsp *sigmadsp);
int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
struct snd_pcm_substream *substream);
@@ -60,7 +58,7 @@ struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
int sigmadsp_attach(struct sigmadsp *sigmadsp,
struct snd_soc_component *component);
-int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate);
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate);
void sigmadsp_reset(struct sigmadsp *sigmadsp);
#endif
diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c
new file mode 100644
index 000000000000..d306c585b52b
--- /dev/null
+++ b/sound/soc/codecs/simple-amplifier.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "simple-amplifier"
+
+struct simple_amp {
+ struct gpio_desc *gpiod_enable;
+};
+
+static int drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct simple_amp *priv = snd_soc_component_get_drvdata(c);
+ int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ default:
+ WARN(1, "Unexpected event");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_enable, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+ SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event,
+ (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 20, 0),
+};
+
+static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = {
+ { "DRV", NULL, "INL" },
+ { "DRV", NULL, "INR" },
+ { "OUTL", NULL, "VCC" },
+ { "OUTR", NULL, "VCC" },
+ { "OUTL", NULL, "DRV" },
+ { "OUTR", NULL, "DRV" },
+};
+
+static const struct snd_soc_component_driver simple_amp_component_driver = {
+ .dapm_widgets = simple_amp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets),
+ .dapm_routes = simple_amp_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes),
+};
+
+static int simple_amp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct simple_amp *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_enable))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable),
+ "Failed to get 'enable' gpio");
+
+ return devm_snd_soc_register_component(dev,
+ &simple_amp_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id simple_amp_ids[] = {
+ { .compatible = "dioo,dio2125", },
+ { .compatible = "simple-audio-amplifier", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, simple_amp_ids);
+#endif
+
+static struct platform_driver simple_amp_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(simple_amp_ids),
+ },
+ .probe = simple_amp_probe,
+};
+
+module_platform_driver(simple_amp_driver);
+
+MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c
new file mode 100644
index 000000000000..069555f35f73
--- /dev/null
+++ b/sound/soc/codecs/simple-mux.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 Bootlin SA
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux/driver.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define MUX_TEXT_SIZE 2
+#define MUX_WIDGET_SIZE 4
+#define MUX_ROUTE_SIZE 3
+struct simple_mux {
+ struct gpio_desc *gpiod_mux;
+ unsigned int mux;
+ const char *mux_texts[MUX_TEXT_SIZE];
+ unsigned int idle_state;
+ struct soc_enum mux_enum;
+ struct snd_kcontrol_new mux_mux;
+ struct snd_soc_dapm_widget mux_widgets[MUX_WIDGET_SIZE];
+ struct snd_soc_dapm_route mux_routes[MUX_ROUTE_SIZE];
+ struct snd_soc_component_driver mux_driver;
+};
+
+static const char * const simple_mux_texts[MUX_TEXT_SIZE] = {
+ "Input 1", "Input 2"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(simple_mux_enum, simple_mux_texts);
+
+static int simple_mux_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.enumerated.item[0] = priv->mux;
+
+ return 0;
+}
+
+static int simple_mux_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ if (ucontrol->value.enumerated.item[0] > e->items)
+ return -EINVAL;
+
+ if (priv->mux == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ priv->mux = ucontrol->value.enumerated.item[0];
+
+ if (priv->idle_state != MUX_IDLE_AS_IS &&
+ snd_soc_dapm_get_bias_level(dapm) < SND_SOC_BIAS_PREPARE)
+ return 0;
+
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux);
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static unsigned int simple_mux_read(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ struct simple_mux *priv = snd_soc_component_get_drvdata(component);
+
+ return priv->mux;
+}
+
+static const struct snd_kcontrol_new simple_mux_mux =
+ SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum, simple_mux_control_get, simple_mux_control_put);
+
+static int simple_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ if (priv->idle_state != MUX_IDLE_AS_IS) {
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->idle_state);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[MUX_WIDGET_SIZE] = {
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_MUX_E("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux, // see simple_mux_probe()
+ simple_mux_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route simple_mux_dapm_routes[MUX_ROUTE_SIZE] = {
+ { "OUT", NULL, "MUX" },
+ { "MUX", "Input 1", "IN1" }, // see simple_mux_probe()
+ { "MUX", "Input 2", "IN2" }, // see simple_mux_probe()
+};
+
+static int simple_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct simple_mux *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_mux))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux),
+ "Failed to get 'mux' gpio");
+
+ /* Copy default settings */
+ memcpy(&priv->mux_texts, &simple_mux_texts, sizeof(priv->mux_texts));
+ memcpy(&priv->mux_enum, &simple_mux_enum, sizeof(priv->mux_enum));
+ memcpy(&priv->mux_mux, &simple_mux_mux, sizeof(priv->mux_mux));
+ memcpy(&priv->mux_widgets, &simple_mux_dapm_widgets, sizeof(priv->mux_widgets));
+ memcpy(&priv->mux_routes, &simple_mux_dapm_routes, sizeof(priv->mux_routes));
+
+ priv->mux_driver.dapm_widgets = priv->mux_widgets;
+ priv->mux_driver.num_dapm_widgets = MUX_WIDGET_SIZE;
+ priv->mux_driver.dapm_routes = priv->mux_routes;
+ priv->mux_driver.num_dapm_routes = MUX_ROUTE_SIZE;
+ priv->mux_driver.read = simple_mux_read;
+
+ /* Overwrite text ("Input 1", "Input 2") if property exists */
+ of_property_read_string_array(np, "state-labels", priv->mux_texts, MUX_TEXT_SIZE);
+
+ ret = of_property_read_u32(np, "idle-state", &priv->idle_state);
+ if (ret < 0) {
+ priv->idle_state = MUX_IDLE_AS_IS;
+ } else if (priv->idle_state != MUX_IDLE_AS_IS && priv->idle_state >= 2) {
+ dev_err(dev, "invalid idle-state %u\n", priv->idle_state);
+ return -EINVAL;
+ }
+
+ /* switch to use priv data instead of default */
+ priv->mux_enum.texts = priv->mux_texts;
+ priv->mux_mux.private_value = (unsigned long)&priv->mux_enum;
+ priv->mux_widgets[2].kcontrol_news = &priv->mux_mux;
+ priv->mux_routes[1].control = priv->mux_texts[0]; // "Input 1"
+ priv->mux_routes[2].control = priv->mux_texts[1]; // "Input 2"
+
+ return devm_snd_soc_register_component(dev, &priv->mux_driver, NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id simple_mux_ids[] = {
+ { .compatible = "simple-audio-mux", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, simple_mux_ids);
+#endif
+
+static struct platform_driver simple_mux_driver = {
+ .driver = {
+ .name = "simple-mux",
+ .of_match_table = of_match_ptr(simple_mux_ids),
+ },
+ .probe = simple_mux_probe,
+};
+
+module_platform_driver(simple_mux_driver);
+
+MODULE_DESCRIPTION("ASoC Simple Audio Mux driver");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
deleted file mode 100644
index 6bfd25c289d1..000000000000
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * SiRF audio codec driver
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/regmap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "sirf-audio-codec.h"
-
-struct sirf_audio_codec {
- struct clk *clk;
- struct regmap *regmap;
- u32 reg_ctrl0, reg_ctrl1;
-};
-
-static const char * const input_mode_mux[] = {"Single-ended",
- "Differential"};
-
-static const struct soc_enum input_mode_mux_enum =
- SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux);
-
-static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control =
- SOC_DAPM_ENUM("Route", input_mode_mux_enum);
-
-static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -12400, 100, 0);
-static const DECLARE_TLV_DB_SCALE(capture_vol_tlv_prima2, 500, 100, 0);
-static const DECLARE_TLV_DB_RANGE(capture_vol_tlv_atlas6,
- 0, 7, TLV_DB_SCALE_ITEM(-100, 100, 0),
- 0x22, 0x3F, TLV_DB_SCALE_ITEM(700, 100, 0),
-);
-
-static struct snd_kcontrol_new volume_controls_atlas6[] = {
- SOC_DOUBLE_TLV("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
- 0x7F, 0, playback_vol_tlv),
- SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
- 0x3F, 0, capture_vol_tlv_atlas6),
-};
-
-static struct snd_kcontrol_new volume_controls_prima2[] = {
- SOC_DOUBLE_TLV("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
- 0x7F, 0, playback_vol_tlv),
- SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
- 0x1F, 0, capture_vol_tlv_prima2),
-};
-
-static struct snd_kcontrol_new left_input_path_controls[] = {
- SOC_DAPM_SINGLE("Line Left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0),
- SOC_DAPM_SINGLE("Mic Left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0),
-};
-
-static struct snd_kcontrol_new right_input_path_controls[] = {
- SOC_DAPM_SINGLE("Line Right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0),
- SOC_DAPM_SINGLE("Mic Right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0),
-};
-
-static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0);
-
-static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0);
-
-static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0);
-
-/* After enable adc, Delay 200ms to avoid pop noise */
-static int adc_enable_delay_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- msleep(200);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static void enable_and_reset_codec(struct regmap *regmap,
- u32 codec_enable_bits, u32 codec_reset_bits)
-{
- regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
- codec_enable_bits | codec_reset_bits,
- codec_enable_bits);
- msleep(20);
- regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
- codec_reset_bits, codec_reset_bits);
-}
-
-static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
-#define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
-#define ATLAS6_CODEC_RESET_BITS (1 << 28)
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- enable_and_reset_codec(sirf_audio_codec->regmap,
- ATLAS6_CODEC_ENABLE_BITS, ATLAS6_CODEC_RESET_BITS);
- break;
- case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, 0);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
-#define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
-#define PRIMA2_CODEC_RESET_BITS (1 << 26)
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- enable_and_reset_codec(sirf_audio_codec->regmap,
- PRIMA2_CODEC_ENABLE_BITS, PRIMA2_CODEC_RESET_BITS);
- break;
- case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, 0);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets[] = {
- SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
- 25, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
- 26, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
- 27, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets[] = {
- SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
- 23, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
- 24, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
- 25, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget =
- SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
- atlas6_codec_enable_and_reset_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
-
-static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget =
- SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
- prima2_codec_enable_and_reset_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
-
-static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0),
- SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0),
- SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0,
- &left_dac_to_hp_left_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0,
- &left_dac_to_hp_right_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0,
- &right_dac_to_hp_left_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0,
- &right_dac_to_hp_right_amp_switch_control),
- SND_SOC_DAPM_OUT_DRV("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
- NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
- NULL, 0),
-
- SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0,
- &left_dac_to_speaker_lineout_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0,
- &right_dac_to_speaker_lineout_switch_control),
- SND_SOC_DAPM_OUT_DRV("Speaker amp driver", AUDIO_IC_CODEC_CTRL0, 4, 0,
- NULL, 0),
-
- SND_SOC_DAPM_OUTPUT("HPOUTL"),
- SND_SOC_DAPM_OUTPUT("HPOUTR"),
- SND_SOC_DAPM_OUTPUT("SPKOUT"),
-
- SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0,
- adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0,
- adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0,
- &left_input_path_controls[0],
- ARRAY_SIZE(left_input_path_controls)),
- SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0,
- &right_input_path_controls[0],
- ARRAY_SIZE(right_input_path_controls)),
-
- SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0,
- &sirf_audio_codec_input_mode_control),
- SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0),
- SND_SOC_DAPM_INPUT("MICIN1"),
- SND_SOC_DAPM_INPUT("MICIN2"),
- SND_SOC_DAPM_INPUT("LINEIN1"),
- SND_SOC_DAPM_INPUT("LINEIN2"),
-
- SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0,
- 30, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_route sirf_audio_codec_map[] = {
- {"SPKOUT", NULL, "Speaker Driver"},
- {"Speaker Driver", NULL, "Speaker amp driver"},
- {"Speaker amp driver", NULL, "Left dac to speaker lineout"},
- {"Speaker amp driver", NULL, "Right dac to speaker lineout"},
- {"Left dac to speaker lineout", "Switch", "DAC left"},
- {"Right dac to speaker lineout", "Switch", "DAC right"},
- {"HPOUTL", NULL, "HP Left Driver"},
- {"HPOUTR", NULL, "HP Right Driver"},
- {"HP Left Driver", NULL, "HP amp left driver"},
- {"HP Right Driver", NULL, "HP amp right driver"},
- {"HP amp left driver", NULL, "Right dac to hp left amp"},
- {"HP amp right driver", NULL , "Right dac to hp right amp"},
- {"HP amp left driver", NULL, "Left dac to hp left amp"},
- {"HP amp right driver", NULL , "Right dac to hp right amp"},
- {"Right dac to hp left amp", "Switch", "DAC left"},
- {"Right dac to hp right amp", "Switch", "DAC right"},
- {"Left dac to hp left amp", "Switch", "DAC left"},
- {"Left dac to hp right amp", "Switch", "DAC right"},
- {"DAC left", NULL, "codecclk"},
- {"DAC right", NULL, "codecclk"},
- {"DAC left", NULL, "Playback"},
- {"DAC right", NULL, "Playback"},
- {"DAC left", NULL, "HSL Phase Opposite"},
- {"DAC right", NULL, "HSL Phase Opposite"},
-
- {"Capture", NULL, "ADC left"},
- {"Capture", NULL, "ADC right"},
- {"ADC left", NULL, "codecclk"},
- {"ADC right", NULL, "codecclk"},
- {"ADC left", NULL, "Left PGA mixer"},
- {"ADC right", NULL, "Right PGA mixer"},
- {"Left PGA mixer", "Line Left Switch", "LINEIN2"},
- {"Right PGA mixer", "Line Right Switch", "LINEIN1"},
- {"Left PGA mixer", "Mic Left Switch", "MICIN2"},
- {"Right PGA mixer", "Mic Right Switch", "Mic input mode mux"},
- {"Mic input mode mux", "Single-ended", "MICIN1"},
- {"Mic input mode mux", "Differential", "MICIN1"},
-};
-
-static void sirf_audio_codec_tx_enable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_START, AUDIO_FIFO_START);
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, IC_TX_ENABLE);
-}
-
-static void sirf_audio_codec_tx_disable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, ~IC_TX_ENABLE);
-}
-
-static void sirf_audio_codec_rx_enable(struct sirf_audio_codec *sirf_audio_codec,
- int channels)
-{
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
- regmap_write(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_RXFIFO_INT_MSK, 0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_START, AUDIO_FIFO_START);
- if (channels == 1)
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO);
- else
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO);
-}
-
-static void sirf_audio_codec_rx_disable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO);
-}
-
-static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
- int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-
- /*
- * This is a workaround, When stop playback,
- * need disable HP amp, avoid the current noise.
- */
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (playback) {
- snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
- IC_HSLEN | IC_HSREN, 0);
- sirf_audio_codec_tx_disable(sirf_audio_codec);
- } else
- sirf_audio_codec_rx_disable(sirf_audio_codec);
- break;
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (playback) {
- sirf_audio_codec_tx_enable(sirf_audio_codec);
- snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
- IC_HSLEN | IC_HSREN, IC_HSLEN | IC_HSREN);
- } else
- sirf_audio_codec_rx_enable(sirf_audio_codec,
- substream->runtime->channels);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
- .trigger = sirf_audio_codec_trigger,
-};
-
-static 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 = snd_soc_codec_get_dapm(codec);
-
- pm_runtime_enable(codec->dev);
-
- if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio-codec")) {
- snd_soc_dapm_new_controls(dapm,
- prima2_output_driver_dapm_widgets,
- ARRAY_SIZE(prima2_output_driver_dapm_widgets));
- snd_soc_dapm_new_controls(dapm,
- &prima2_codec_clock_dapm_widget, 1);
- return snd_soc_add_codec_controls(codec,
- volume_controls_prima2,
- ARRAY_SIZE(volume_controls_prima2));
- }
- if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio-codec")) {
- snd_soc_dapm_new_controls(dapm,
- atlas6_output_driver_dapm_widgets,
- ARRAY_SIZE(atlas6_output_driver_dapm_widgets));
- snd_soc_dapm_new_controls(dapm,
- &atlas6_codec_clock_dapm_widget, 1);
- return snd_soc_add_codec_controls(codec,
- volume_controls_atlas6,
- ARRAY_SIZE(volume_controls_atlas6));
- }
-
- return -EINVAL;
-}
-
-static int sirf_audio_codec_remove(struct snd_soc_codec *codec)
-{
- pm_runtime_disable(codec->dev);
- return 0;
-}
-
-static struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = {
- .probe = sirf_audio_codec_probe,
- .remove = sirf_audio_codec_remove,
- .dapm_widgets = sirf_audio_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sirf_audio_codec_dapm_widgets),
- .dapm_routes = sirf_audio_codec_map,
- .num_dapm_routes = ARRAY_SIZE(sirf_audio_codec_map),
- .idle_bias_off = true,
-};
-
-static const struct of_device_id sirf_audio_codec_of_match[] = {
- { .compatible = "sirf,prima2-audio-codec" },
- { .compatible = "sirf,atlas6-audio-codec" },
- {}
-};
-MODULE_DEVICE_TABLE(of, sirf_audio_codec_of_match);
-
-static const struct regmap_config sirf_audio_codec_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK,
- .cache_type = REGCACHE_NONE,
-};
-
-static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
-{
- int ret;
- struct sirf_audio_codec *sirf_audio_codec;
- void __iomem *base;
- struct resource *mem_res;
- const struct of_device_id *match;
-
- match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
-
- sirf_audio_codec = devm_kzalloc(&pdev->dev,
- sizeof(struct sirf_audio_codec), GFP_KERNEL);
- if (!sirf_audio_codec)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, sirf_audio_codec);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem_res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &sirf_audio_codec_regmap_config);
- if (IS_ERR(sirf_audio_codec->regmap))
- return PTR_ERR(sirf_audio_codec->regmap);
-
- sirf_audio_codec->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(sirf_audio_codec->clk)) {
- dev_err(&pdev->dev, "Get clock failed.\n");
- return PTR_ERR(sirf_audio_codec->clk);
- }
-
- ret = clk_prepare_enable(sirf_audio_codec->clk);
- if (ret) {
- dev_err(&pdev->dev, "Enable clock failed.\n");
- return ret;
- }
-
- ret = snd_soc_register_codec(&(pdev->dev),
- &soc_codec_device_sirf_audio_codec,
- &sirf_audio_codec_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
- goto err_clk_put;
- }
-
- /*
- * Always open charge pump, if not, when the charge pump closed the
- * adc will not stable
- */
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- IC_CPFREQ, IC_CPFREQ);
-
- if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas6-audio-codec"))
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL0, IC_CPEN, IC_CPEN);
- return 0;
-
-err_clk_put:
- clk_disable_unprepare(sirf_audio_codec->clk);
- return ret;
-}
-
-static int sirf_audio_codec_driver_remove(struct platform_device *pdev)
-{
- struct sirf_audio_codec *sirf_audio_codec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(sirf_audio_codec->clk);
- snd_soc_unregister_codec(&(pdev->dev));
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int sirf_audio_codec_suspend(struct device *dev)
-{
- struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
-
- regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- &sirf_audio_codec->reg_ctrl0);
- regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
- &sirf_audio_codec->reg_ctrl1);
- clk_disable_unprepare(sirf_audio_codec->clk);
-
- return 0;
-}
-
-static int sirf_audio_codec_resume(struct device *dev)
-{
- struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(sirf_audio_codec->clk);
- if (ret)
- return ret;
-
- regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- sirf_audio_codec->reg_ctrl0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
- sirf_audio_codec->reg_ctrl1);
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops sirf_audio_codec_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sirf_audio_codec_suspend, sirf_audio_codec_resume)
-};
-
-static struct platform_driver sirf_audio_codec_driver = {
- .driver = {
- .name = "sirf-audio-codec",
- .of_match_table = sirf_audio_codec_of_match,
- .pm = &sirf_audio_codec_pm_ops,
- },
- .probe = sirf_audio_codec_driver_probe,
- .remove = sirf_audio_codec_driver_remove,
-};
-
-module_platform_driver(sirf_audio_codec_driver);
-
-MODULE_DESCRIPTION("SiRF audio codec driver");
-MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sirf-audio-codec.h b/sound/soc/codecs/sirf-audio-codec.h
deleted file mode 100644
index ba1adc03839f..000000000000
--- a/sound/soc/codecs/sirf-audio-codec.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * SiRF inner codec controllers define
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#ifndef _SIRF_AUDIO_CODEC_H
-#define _SIRF_AUDIO_CODEC_H
-
-
-#define AUDIO_IC_CODEC_PWR (0x00E0)
-#define AUDIO_IC_CODEC_CTRL0 (0x00E4)
-#define AUDIO_IC_CODEC_CTRL1 (0x00E8)
-#define AUDIO_IC_CODEC_CTRL2 (0x00EC)
-#define AUDIO_IC_CODEC_CTRL3 (0x00F0)
-
-#define MICBIASEN (1 << 3)
-
-#define IC_RDACEN (1 << 0)
-#define IC_LDACEN (1 << 1)
-#define IC_HSREN (1 << 2)
-#define IC_HSLEN (1 << 3)
-#define IC_SPEN (1 << 4)
-#define IC_CPEN (1 << 5)
-
-#define IC_HPRSELR (1 << 6)
-#define IC_HPLSELR (1 << 7)
-#define IC_HPRSELL (1 << 8)
-#define IC_HPLSELL (1 << 9)
-#define IC_SPSELR (1 << 10)
-#define IC_SPSELL (1 << 11)
-
-#define IC_MONOR (1 << 12)
-#define IC_MONOL (1 << 13)
-
-#define IC_RXOSRSEL (1 << 28)
-#define IC_CPFREQ (1 << 29)
-#define IC_HSINVEN (1 << 30)
-
-#define IC_MICINREN (1 << 0)
-#define IC_MICINLEN (1 << 1)
-#define IC_MICIN1SEL (1 << 2)
-#define IC_MICIN2SEL (1 << 3)
-#define IC_MICDIFSEL (1 << 4)
-#define IC_LINEIN1SEL (1 << 5)
-#define IC_LINEIN2SEL (1 << 6)
-#define IC_RADCEN (1 << 7)
-#define IC_LADCEN (1 << 8)
-#define IC_ALM (1 << 9)
-
-#define IC_DIGMICEN (1 << 22)
-#define IC_DIGMICFREQ (1 << 23)
-#define IC_ADC14B_12 (1 << 24)
-#define IC_FIRDAC_HSL_EN (1 << 25)
-#define IC_FIRDAC_HSR_EN (1 << 26)
-#define IC_FIRDAC_LOUT_EN (1 << 27)
-#define IC_POR (1 << 28)
-#define IC_CODEC_CLK_EN (1 << 29)
-#define IC_HP_3DB_BOOST (1 << 30)
-
-#define IC_ADC_LEFT_GAIN_SHIFT 16
-#define IC_ADC_RIGHT_GAIN_SHIFT 10
-#define IC_ADC_GAIN_MASK 0x3F
-#define IC_MIC_MAX_GAIN 0x39
-
-#define IC_RXPGAR_MASK 0x3F
-#define IC_RXPGAR_SHIFT 14
-#define IC_RXPGAL_MASK 0x3F
-#define IC_RXPGAL_SHIFT 21
-#define IC_RXPGAR 0x7B
-#define IC_RXPGAL 0x7B
-
-#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK 0x3F
-#define AUDIO_PORT_TX_FIFO_SC_OFFSET 0
-#define AUDIO_PORT_TX_FIFO_LC_OFFSET 10
-#define AUDIO_PORT_TX_FIFO_HC_OFFSET 20
-
-#define TX_FIFO_SC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_SC_OFFSET)
-#define TX_FIFO_LC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_LC_OFFSET)
-#define TX_FIFO_HC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_HC_OFFSET)
-
-#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK 0x0F
-#define AUDIO_PORT_RX_FIFO_SC_OFFSET 0
-#define AUDIO_PORT_RX_FIFO_LC_OFFSET 10
-#define AUDIO_PORT_RX_FIFO_HC_OFFSET 20
-
-#define RX_FIFO_SC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_SC_OFFSET)
-#define RX_FIFO_LC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_LC_OFFSET)
-#define RX_FIFO_HC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_HC_OFFSET)
-#define AUDIO_PORT_IC_CODEC_TX_CTRL (0x00F4)
-#define AUDIO_PORT_IC_CODEC_RX_CTRL (0x00F8)
-
-#define AUDIO_PORT_IC_TXFIFO_OP (0x00FC)
-#define AUDIO_PORT_IC_TXFIFO_LEV_CHK (0x0100)
-#define AUDIO_PORT_IC_TXFIFO_STS (0x0104)
-#define AUDIO_PORT_IC_TXFIFO_INT (0x0108)
-#define AUDIO_PORT_IC_TXFIFO_INT_MSK (0x010C)
-
-#define AUDIO_PORT_IC_RXFIFO_OP (0x0110)
-#define AUDIO_PORT_IC_RXFIFO_LEV_CHK (0x0114)
-#define AUDIO_PORT_IC_RXFIFO_STS (0x0118)
-#define AUDIO_PORT_IC_RXFIFO_INT (0x011C)
-#define AUDIO_PORT_IC_RXFIFO_INT_MSK (0x0120)
-
-#define AUDIO_FIFO_START (1 << 0)
-#define AUDIO_FIFO_RESET (1 << 1)
-
-#define AUDIO_FIFO_FULL (1 << 0)
-#define AUDIO_FIFO_EMPTY (1 << 1)
-#define AUDIO_FIFO_OFLOW (1 << 2)
-#define AUDIO_FIFO_UFLOW (1 << 3)
-
-#define IC_TX_ENABLE (0x03)
-#define IC_RX_ENABLE_MONO (0x01)
-#define IC_RX_ENABLE_STEREO (0x03)
-
-#endif /*__SIRF_AUDIO_CODEC_H*/
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
new file mode 100644
index 000000000000..06de2b4fce5e
--- /dev/null
+++ b/sound/soc/codecs/sma1303.c
@@ -0,0 +1,1811 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// sma1303.c -- sma1303 ALSA SoC Audio driver
+//
+// Copyright 2023 Iron Device Corporation
+//
+// Auther: Gyuhwa Park <gyuhwa.park@irondevice.com>
+// Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "sma1303.h"
+
+#define CHECK_PERIOD_TIME 1 /* sec per HZ */
+#define MAX_CONTROL_NAME 48
+
+#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\
+ _post_n, _n, _vco, _p_cp)\
+{\
+ .input_clk_name = _input_clk_name,\
+ .output_clk_name = _output_clk_name,\
+ .input_clk = _input_clk,\
+ .post_n = _post_n,\
+ .n = _n,\
+ .vco = _vco,\
+ .p_cp = _p_cp,\
+}
+
+enum sma1303_type {
+ SMA1303,
+};
+
+struct sma1303_pll_match {
+ char *input_clk_name;
+ char *output_clk_name;
+ unsigned int input_clk;
+ unsigned int post_n;
+ unsigned int n;
+ unsigned int vco;
+ unsigned int p_cp;
+};
+
+struct sma1303_priv {
+ enum sma1303_type devtype;
+ struct attribute_group *attr_grp;
+ struct delayed_work check_fault_work;
+ struct device *dev;
+ struct kobject *kobj;
+ struct regmap *regmap;
+ struct sma1303_pll_match *pll_matches;
+ bool amp_power_status;
+ bool force_mute_status;
+ int num_of_pll_matches;
+ int retry_cnt;
+ unsigned int amp_mode;
+ unsigned int cur_vol;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int init_vol;
+ unsigned int last_bclk;
+ unsigned int last_ocp_val;
+ unsigned int last_over_temp;
+ unsigned int rev_num;
+ unsigned int sys_clk_id;
+ unsigned int tdm_slot_rx;
+ unsigned int tdm_slot_tx;
+ unsigned int tsdw_cnt;
+ long check_fault_period;
+ long check_fault_status;
+};
+
+static struct sma1303_pll_match sma1303_pll_matches[] = {
+PLL_MATCH("1.411MHz", "24.595MHz", 1411200, 0x07, 0xF4, 0x8B, 0x03),
+PLL_MATCH("1.536MHz", "24.576MHz", 1536000, 0x07, 0xE0, 0x8B, 0x03),
+PLL_MATCH("3.072MHz", "24.576MHz", 3072000, 0x07, 0x70, 0x8B, 0x03),
+PLL_MATCH("6.144MHz", "24.576MHz", 6144000, 0x07, 0x70, 0x8B, 0x07),
+PLL_MATCH("12.288MHz", "24.576MHz", 12288000, 0x07, 0x70, 0x8B, 0x0B),
+PLL_MATCH("19.2MHz", "24.343MHz", 19200000, 0x07, 0x47, 0x8B, 0x0A),
+PLL_MATCH("24.576MHz", "24.576MHz", 24576000, 0x07, 0x70, 0x8B, 0x0F),
+};
+
+static int sma1303_startup(struct snd_soc_component *);
+static int sma1303_shutdown(struct snd_soc_component *);
+
+static const struct reg_default sma1303_reg_def[] = {
+ { 0x00, 0x80 },
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x11 },
+ { 0x04, 0x17 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x31 },
+ { 0x0B, 0x98 },
+ { 0x0C, 0x84 },
+ { 0x0D, 0x07 },
+ { 0x0E, 0x3F },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x14, 0x5C },
+ { 0x15, 0x01 },
+ { 0x16, 0x0F },
+ { 0x17, 0x0F },
+ { 0x18, 0x0F },
+ { 0x19, 0x00 },
+ { 0x1A, 0x00 },
+ { 0x1B, 0x00 },
+ { 0x23, 0x19 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x04 },
+ { 0x33, 0x00 },
+ { 0x36, 0x92 },
+ { 0x37, 0x27 },
+ { 0x3B, 0x5A },
+ { 0x3C, 0x20 },
+ { 0x3D, 0x00 },
+ { 0x3E, 0x03 },
+ { 0x3F, 0x0C },
+ { 0x8B, 0x07 },
+ { 0x8C, 0x70 },
+ { 0x8D, 0x8B },
+ { 0x8E, 0x6F },
+ { 0x8F, 0x03 },
+ { 0x90, 0x26 },
+ { 0x91, 0x42 },
+ { 0x92, 0xE0 },
+ { 0x94, 0x35 },
+ { 0x95, 0x0C },
+ { 0x96, 0x42 },
+ { 0x97, 0x95 },
+ { 0xA0, 0x00 },
+ { 0xA1, 0x3B },
+ { 0xA2, 0xC8 },
+ { 0xA3, 0x28 },
+ { 0xA4, 0x40 },
+ { 0xA5, 0x01 },
+ { 0xA6, 0x41 },
+ { 0xA7, 0x00 },
+};
+
+static bool sma1303_readable_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ if (reg > SMA1303_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
+ case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
+ case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
+ case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
+ case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
+ case SMA1303_33_SDM_CTRL ... SMA1303_34_OTP_DATA1:
+ case SMA1303_36_PROTECTION ... SMA1303_38_OTP_TRM0:
+ case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
+ case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
+ case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
+ case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
+ case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
+ result = true;
+ break;
+ case SMA1303_FF_DEVICE_INDEX:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static bool sma1303_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ if (reg > SMA1303_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
+ case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
+ case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
+ case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
+ case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
+ case SMA1303_33_SDM_CTRL:
+ case SMA1303_36_PROTECTION ... SMA1303_37_SLOPE_CTRL:
+ case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
+ case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
+ case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
+ case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static bool sma1303_volatile_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ switch (reg) {
+ case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
+ result = true;
+ break;
+ case SMA1303_FF_DEVICE_INDEX:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static const DECLARE_TLV_DB_SCALE(sma1303_spk_tlv, -6000, 50, 0);
+
+static int sma1303_regmap_write(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int val)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_write(sma1303->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to write [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static int sma1303_regmap_update_bits(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int mask, unsigned int val, bool *change)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_update_bits_check(sma1303->regmap, reg,
+ mask, val, change);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to update [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static int sma1303_regmap_read(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int *val)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_read(sma1303->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to read [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static const char * const sma1303_aif_in_source_text[] = {
+ "Mono", "Left", "Right"};
+static const char * const sma1303_aif_out_source_text[] = {
+ "Disable", "After_FmtC", "After_Mixer", "After_DSP", "After_Post",
+ "Clk_PLL", "Clk_OSC"};
+static const char * const sma1303_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7"};
+
+static const struct soc_enum sma1303_aif_in_source_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_in_source_text),
+ sma1303_aif_in_source_text);
+static const struct soc_enum sma1303_aif_out_source_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_out_source_text),
+ sma1303_aif_out_source_text);
+static const struct soc_enum sma1303_tdm_slot_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_tdm_slot_text),
+ sma1303_tdm_slot_text);
+
+static int sma1303_force_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1303->force_mute_status;
+ dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
+ sma1303->force_mute_status ? "ON" : "OFF");
+
+ return 0;
+}
+
+static int sma1303_force_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1303->force_mute_status == val)
+ change = false;
+ else {
+ change = true;
+ sma1303->force_mute_status = val;
+ }
+ dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
+ sma1303->force_mute_status ? "ON" : "OFF");
+
+ return change;
+}
+
+static int sma1303_postscaler_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_90_POSTSCALER, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x7E) >> 1;
+
+ return 0;
+}
+
+static int sma1303_postscaler_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER, 0x7E, (val << 1), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_tdm_slot_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_A5_TDM1, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x38) >> 3;
+ sma1303->tdm_slot_rx = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int sma1303_tdm_slot_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1, 0x38, (val << 3), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_tdm_slot_tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_A6_TDM2, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x38) >> 3;
+ sma1303->tdm_slot_tx = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int sma1303_tdm_slot_tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2, 0x38, (val << 3), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_startup(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, temp = false;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
+ SMA1303_PLL_PD2_MASK, SMA1303_PLL_OPERATION2, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
+ SMA1303_POWER_MASK, SMA1303_POWER_ON, &temp);
+ if (temp == true)
+ change = true;
+
+ if (sma1303->amp_mode == SMA1303_MONO) {
+ sma1303_regmap_update_bits(sma1303,
+ SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK,
+ SMA1303_SPK_MONO,
+ &temp);
+ if (temp == true)
+ change = true;
+
+ } else {
+ sma1303_regmap_update_bits(sma1303,
+ SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK,
+ SMA1303_SPK_STEREO,
+ &temp);
+ if (temp == true)
+ change = true;
+ }
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ sma1303->amp_power_status = true;
+
+ return change;
+}
+
+static int sma1303_shutdown(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, temp = false;
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK, SMA1303_SPK_OFF, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
+ SMA1303_POWER_MASK, SMA1303_POWER_OFF, &temp);
+ if (temp == true)
+ change = true;
+ sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
+ SMA1303_PLL_PD2_MASK, SMA1303_PLL_PD2, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303->amp_power_status = false;
+
+ return change;
+}
+
+static int sma1303_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case 0:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_ON,
+ &change);
+ sma1303->amp_mode = SMA1303_MONO;
+ break;
+ case 1:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_OFF,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_LR_DATA_SW_MASK,
+ SMA1303_LR_DATA_SW_NORMAL,
+ &temp);
+ if (temp == true)
+ change = true;
+ sma1303->amp_mode = SMA1303_STEREO;
+ break;
+ case 2:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_OFF,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_LR_DATA_SW_MASK,
+ SMA1303_LR_DATA_SW_SWAP,
+ &temp);
+ if (temp == true)
+ change = true;
+ sma1303->amp_mode = SMA1303_STEREO;
+ break;
+ default:
+ dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
+ sma1303_aif_in_source_text[mux]);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = snd_soc_dapm_kcontrol_get_value(w->kcontrols[0]);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case 0:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_OUT_SEL_DISABLE,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 1:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_FORMAT_CONVERTER,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 2:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_MIXER_OUTPUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 3:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_SPEAKER_PATH,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 4:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_POSTSCALER_OUTPUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 5:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_CLK_OUT_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_MON_OSC_PLL_MASK,
+ SMA1303_PLL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 6:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_CLK_OUT_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_MON_OSC_PLL_MASK,
+ SMA1303_OSC_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ default:
+ dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
+ sma1303_aif_out_source_text[mux]);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_sdo_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_CONFIG_MASK,
+ SMA1303_OUTPUT_PORT_ENABLE,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_SDO_OUTPUT_MASK,
+ SMA1303_NORMAL_OUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_CONFIG_MASK,
+ SMA1303_INPUT_PORT_ONLY,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_SDO_OUTPUT_MASK,
+ SMA1303_HIGH_Z_OUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_post_scaler_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ bool change = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER,
+ SMA1303_BYP_POST_MASK,
+ SMA1303_EN_POST_SCALER,
+ &change);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER,
+ SMA1303_BYP_POST_MASK,
+ SMA1303_BYP_POST_SCALER,
+ &change);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMU\n", __func__);
+ ret = sma1303_startup(component);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMD\n", __func__);
+ ret = sma1303_shutdown(component);
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_kcontrol_new sma1303_aif_in_source_control =
+ SOC_DAPM_ENUM("AIF IN Source", sma1303_aif_in_source_enum);
+static const struct snd_kcontrol_new sma1303_aif_out_source_control =
+ SOC_DAPM_ENUM("AIF OUT Source", sma1303_aif_out_source_enum);
+static const struct snd_kcontrol_new sma1303_sdo_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new sma1303_post_scaler_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new sma1303_enable_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new sma1303_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", SMA1303_0A_SPK_VOL,
+ 0, 167, 1, sma1303_spk_tlv),
+ SOC_SINGLE_BOOL_EXT("Force Mute Switch", 0,
+ sma1303_force_mute_get, sma1303_force_mute_put),
+ SOC_SINGLE_EXT("Postscaler Gain", SMA1303_90_POSTSCALER, 1, 0x30, 0,
+ sma1303_postscaler_get, sma1303_postscaler_put),
+ SOC_ENUM_EXT("TDM RX Slot Position", sma1303_tdm_slot_enum,
+ sma1303_tdm_slot_rx_get, sma1303_tdm_slot_rx_put),
+ SOC_ENUM_EXT("TDM TX Slot Position", sma1303_tdm_slot_enum,
+ sma1303_tdm_slot_tx_get, sma1303_tdm_slot_tx_put),
+};
+
+static const struct snd_soc_dapm_widget sma1303_dapm_widgets[] = {
+ /* platform domain */
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("SDO"),
+
+ /* path domain */
+ SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0,
+ &sma1303_aif_in_source_control,
+ sma1303_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E("AIF OUT Source", SND_SOC_NOPM, 0, 0,
+ &sma1303_aif_out_source_control,
+ sma1303_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
+ &sma1303_sdo_control,
+ sma1303_sdo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH_E("Post Scaler", SND_SOC_NOPM, 0, 1,
+ &sma1303_post_scaler_control,
+ sma1303_post_scaler_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sma1303_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1,
+ &sma1303_enable_control),
+
+ /* stream domain */
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sma1303_audio_map[] = {
+ /* Playback */
+ {"AIF IN Source", "Mono", "AIF IN"},
+ {"AIF IN Source", "Left", "AIF IN"},
+ {"AIF IN Source", "Right", "AIF IN"},
+
+ {"SDO Enable", "Switch", "AIF IN"},
+ {"AIF OUT Source", "Disable", "SDO Enable"},
+ {"AIF OUT Source", "After_FmtC", "SDO Enable"},
+ {"AIF OUT Source", "After_Mixer", "SDO Enable"},
+ {"AIF OUT Source", "After_DSP", "SDO Enable"},
+ {"AIF OUT Source", "After_Post", "SDO Enable"},
+ {"AIF OUT Source", "Clk_PLL", "SDO Enable"},
+ {"AIF OUT Source", "Clk_OSC", "SDO Enable"},
+
+ {"Entry", NULL, "AIF OUT Source"},
+ {"Entry", NULL, "AIF IN Source"},
+
+ {"Post Scaler", "Switch", "Entry"},
+ {"AMP Power", NULL, "Entry"},
+ {"AMP Power", NULL, "Entry"},
+
+ {"AMP Enable", "Switch", "AMP Power"},
+ {"SPK", NULL, "AMP Enable"},
+
+ /* Capture */
+ {"AIF OUT", NULL, "AMP Enable"},
+};
+
+static int sma1303_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ int i = 0, ret = 0;
+
+ dev_dbg(component->dev, "%s : BCLK = %dHz\n",
+ __func__, bclk);
+
+ if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK) {
+ dev_dbg(component->dev, "%s : MCLK is not supported\n",
+ __func__);
+ } else if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
+ for (i = 0; i < sma1303->num_of_pll_matches; i++) {
+ if (sma1303->pll_matches[i].input_clk == bclk)
+ break;
+ }
+ if (i == sma1303->num_of_pll_matches) {
+ dev_dbg(component->dev, "%s : No matching value between pll table and SCK\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_PLL_PD_MASK|SMA1303_PLL_REF_CLK_MASK,
+ SMA1303_PLL_OPERATION|SMA1303_PLL_SCK,
+ NULL);
+ }
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8B_PLL_POST_N,
+ sma1303->pll_matches[i].post_n);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8C_PLL_N,
+ sma1303->pll_matches[i].n);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8D_PLL_A_SETTING,
+ sma1303->pll_matches[i].vco);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8F_PLL_P_CP,
+ sma1303->pll_matches[i].p_cp);
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sma1303_dai_hw_params_amp(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+ int ret = 0;
+
+ if (sma1303->format == SND_SOC_DAIFMT_DSP_A)
+ bclk = params_rate(params) * sma1303->frame_size;
+ else
+ bclk = params_rate(params) * params_physical_width(params)
+ * params_channels(params);
+
+ dev_dbg(component->dev,
+ "%s : rate = %d : bit size = %d : channel = %d\n",
+ __func__, params_rate(params), params_width(params),
+ params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
+ if (sma1303->last_bclk != bclk) {
+ sma1303_setup_pll(component, bclk);
+ sma1303->last_bclk = bclk;
+ }
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 96000:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_DAC_DN_CONV_MASK,
+ SMA1303_DAC_DN_CONV_DISABLE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_LOW_FIRST_CH,
+ NULL);
+ break;
+
+ case 192000:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_DAC_DN_CONV_MASK,
+ SMA1303_DAC_DN_CONV_ENABLE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_HIGH_FIRST_CH,
+ NULL);
+ break;
+
+ default:
+ dev_err(component->dev, "%s not support rate : %d\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ } else {
+
+ switch (params_format(params)) {
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S16_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_32FS,
+ NULL);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S24_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_64FS,
+ NULL);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S32_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_64FS,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ }
+
+ switch (sma1303->format) {
+ case SND_SOC_DAIFMT_I2S:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_STANDARD_I2S,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_I2S,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_LJ,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_LJ,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_RJ_16BIT,
+ NULL);
+ break;
+ case 24:
+ case 32:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_RJ_24BIT,
+ NULL);
+ break;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_STANDARD_I2S,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_TDM,
+ NULL);
+ break;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev,
+ "%s not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sma1303_dai_set_sysclk_amp(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case SMA1303_EXTERNAL_CLOCK_19_2:
+ break;
+ case SMA1303_EXTERNAL_CLOCK_24_576:
+ break;
+ case SMA1303_PLL_CLKIN_MCLK:
+ break;
+ case SMA1303_PLL_CLKIN_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
+ return -EINVAL;
+ }
+ sma1303->sys_clk_id = clk_id;
+ return 0;
+}
+
+static int sma1303_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return ret;
+
+ if (mute) {
+ dev_dbg(component->dev, "%s : %s\n", __func__, "MUTE");
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_0E_MUTE_VOL_CTRL,
+ SMA1303_SPK_MUTE_MASK,
+ SMA1303_SPK_MUTE,
+ NULL);
+
+ /* Need to wait time for mute slope */
+ msleep(55);
+ } else {
+ if (!sma1303->force_mute_status) {
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "UNMUTE");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_0E_MUTE_VOL_CTRL,
+ SMA1303_SPK_MUTE_MASK,
+ SMA1303_SPK_UNMUTE,
+ NULL);
+ } else {
+ dev_dbg(sma1303->dev,
+ "%s : FORCE MUTE!!!\n", __func__);
+ }
+ }
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int sma1303_dai_set_fmt_amp(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(component->dev,
+ "%s : %s\n", __func__, "I2S/TDM Device mode");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_CONTROLLER_DEVICE_MASK,
+ SMA1303_DEVICE_MODE,
+ NULL);
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev,
+ "%s : %s\n", __func__, "I2S/TDM Controller mode");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_CONTROLLER_DEVICE_MASK,
+ SMA1303_CONTROLLER_MODE,
+ NULL);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "Unsupported Controller/Device : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ sma1303->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev,
+ "Unsupported Audio Interface Format : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Invert BCLK + Normal Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_SCK_RISING_MASK,
+ SMA1303_SCK_RISING_EDGE,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Invert BCLK + Invert Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK|SMA1303_SCK_RISING_MASK,
+ SMA1303_HIGH_FIRST_CH|SMA1303_SCK_RISING_EDGE,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Normal BCLK + Invert Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_HIGH_FIRST_CH,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Normal BCLK + Normal Frame");
+ break;
+ default:
+ dev_err(component->dev,
+ "Unsupported Bit & Frameclock : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int sma1303_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ dev_dbg(component->dev, "%s : slots = %d, slot_width - %d\n",
+ __func__, slots, slot_width);
+
+ sma1303->frame_size = slot_width * slots;
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_TDM,
+ NULL);
+
+ switch (slot_width) {
+ case 16:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_DL_MASK,
+ SMA1303_TDM_DL_16,
+ NULL);
+ break;
+ case 32:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_DL_MASK,
+ SMA1303_TDM_DL_32,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev, "%s not support TDM %d slot_width\n",
+ __func__, slot_width);
+ break;
+ }
+
+ switch (slots) {
+ case 4:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_N_SLOT_MASK,
+ SMA1303_TDM_N_SLOT_4,
+ NULL);
+ break;
+ case 8:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_N_SLOT_MASK,
+ SMA1303_TDM_N_SLOT_8,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev, "%s not support TDM %d slots\n",
+ __func__, slots);
+ break;
+ }
+
+ if (sma1303->tdm_slot_rx < slots)
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_SLOT1_RX_POS_MASK,
+ (sma1303->tdm_slot_rx) << 3,
+ NULL);
+ else
+ dev_err(component->dev, "%s Incorrect tdm-slot-rx %d set\n",
+ __func__, sma1303->tdm_slot_rx);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_CLK_POL_MASK,
+ SMA1303_TDM_CLK_POL_RISE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_TX_MODE_MASK,
+ SMA1303_TDM_TX_MONO,
+ NULL);
+
+ if (sma1303->tdm_slot_tx < slots)
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_SLOT1_TX_POS_MASK,
+ (sma1303->tdm_slot_tx) << 3,
+ NULL);
+ else
+ dev_err(component->dev, "%s Incorrect tdm-slot-tx %d set\n",
+ __func__, sma1303->tdm_slot_tx);
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sma1303_dai_ops_amp = {
+ .set_sysclk = sma1303_dai_set_sysclk_amp,
+ .set_fmt = sma1303_dai_set_fmt_amp,
+ .hw_params = sma1303_dai_hw_params_amp,
+ .mute_stream = sma1303_dai_mute,
+ .set_tdm_slot = sma1303_dai_set_tdm_slot,
+};
+
+#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000
+#define SMA1303_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver sma1303_dai[] = {
+ {
+ .name = "sma1303-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1303_RATES,
+ .formats = SMA1303_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1303_RATES,
+ .formats = SMA1303_FORMATS,
+ },
+ .ops = &sma1303_dai_ops_amp,
+ },
+};
+
+static void sma1303_check_fault_worker(struct work_struct *work)
+{
+ struct sma1303_priv *sma1303 =
+ container_of(work, struct sma1303_priv, check_fault_work.work);
+ int ret = 0;
+ unsigned int over_temp, ocp_val, uvlo_val;
+
+ if (sma1303->tsdw_cnt)
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_0A_SPK_VOL, &sma1303->cur_vol);
+ else
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_0A_SPK_VOL, &sma1303->init_vol);
+
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_0A_SPK_VOL : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FA_STATUS1, &over_temp);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FA_STATUS1 : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &ocp_val);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FB_STATUS2 : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &uvlo_val);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FF_DEVICE_INDEX : %d\n", ret);
+ return;
+ }
+
+ if (~over_temp & SMA1303_OT1_OK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OT1(Over Temperature Level 1)\n", __func__);
+
+ if ((sma1303->cur_vol + 6) <= 0xFF)
+ sma1303_regmap_write(sma1303,
+ SMA1303_0A_SPK_VOL, sma1303->cur_vol + 6);
+
+ sma1303->tsdw_cnt++;
+ } else if (sma1303->tsdw_cnt) {
+ sma1303_regmap_write(sma1303,
+ SMA1303_0A_SPK_VOL, sma1303->init_vol);
+ sma1303->tsdw_cnt = 0;
+ sma1303->cur_vol = sma1303->init_vol;
+ }
+
+ if (~over_temp & SMA1303_OT2_OK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OT2(Over Temperature Level 2)\n", __func__);
+ }
+ if (ocp_val & SMA1303_OCP_SPK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OCP_SPK(Over Current Protect SPK)\n", __func__);
+ }
+ if (ocp_val & SMA1303_OCP_BST_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OCP_BST(Over Current Protect Boost)\n", __func__);
+ }
+ if ((ocp_val & SMA1303_CLK_MON_STATUS) && (sma1303->amp_power_status)) {
+ dev_crit(sma1303->dev,
+ "%s : CLK_FAULT(No clock input)\n", __func__);
+ }
+ if (uvlo_val & SMA1303_UVLO_BST_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : UVLO(Under Voltage Lock Out)\n", __func__);
+ }
+
+ if ((over_temp != sma1303->last_over_temp) ||
+ (ocp_val != sma1303->last_ocp_val)) {
+
+ dev_crit(sma1303->dev, "Please check AMP status");
+ dev_dbg(sma1303->dev, "STATUS1=0x%02X : STATUS2=0x%02X\n",
+ over_temp, ocp_val);
+ sma1303->last_over_temp = over_temp;
+ sma1303->last_ocp_val = ocp_val;
+ }
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ if (!(~over_temp & SMA1303_OT1_OK_STATUS)
+ && !(~over_temp & SMA1303_OT2_OK_STATUS)
+ && !(ocp_val & SMA1303_OCP_SPK_STATUS)
+ && !(ocp_val & SMA1303_OCP_BST_STATUS)
+ && !(ocp_val & SMA1303_CLK_MON_STATUS)
+ && !(uvlo_val & SMA1303_UVLO_BST_STATUS)) {
+ }
+}
+
+static int sma1303_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static void sma1303_remove(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+}
+
+static const struct snd_soc_component_driver sma1303_component = {
+ .probe = sma1303_probe,
+ .remove = sma1303_remove,
+ .controls = sma1303_snd_controls,
+ .num_controls = ARRAY_SIZE(sma1303_snd_controls),
+ .dapm_widgets = sma1303_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sma1303_dapm_widgets),
+ .dapm_routes = sma1303_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map),
+};
+
+static const struct regmap_config sma_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SMA1303_FF_DEVICE_INDEX,
+ .readable_reg = sma1303_readable_register,
+ .writeable_reg = sma1303_writeable_register,
+ .volatile_reg = sma1303_volatile_register,
+
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = sma1303_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(sma1303_reg_def),
+};
+
+static ssize_t check_fault_period_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%ld\n", sma1303->check_fault_period);
+}
+
+static ssize_t check_fault_period_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtol(buf, 10, &sma1303->check_fault_period);
+
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_fault_period);
+
+static ssize_t check_fault_status_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%ld\n", sma1303->check_fault_status);
+}
+
+static ssize_t check_fault_status_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtol(buf, 10, &sma1303->check_fault_status);
+
+ if (ret)
+ return -EINVAL;
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_fault_status);
+
+static struct attribute *sma1303_attr[] = {
+ &dev_attr_check_fault_period.attr,
+ &dev_attr_check_fault_status.attr,
+ NULL,
+};
+
+static struct attribute_group sma1303_attr_group = {
+ .attrs = sma1303_attr,
+};
+
+static int sma1303_i2c_probe(struct i2c_client *client)
+{
+ struct sma1303_priv *sma1303;
+ int ret, i = 0;
+ unsigned int device_info, status, otp_stat;
+
+ sma1303 = devm_kzalloc(&client->dev,
+ sizeof(struct sma1303_priv), GFP_KERNEL);
+ if (!sma1303)
+ return -ENOMEM;
+ sma1303->dev = &client->dev;
+
+ sma1303->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
+ if (IS_ERR(sma1303->regmap)) {
+ ret = PTR_ERR(sma1303->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate register map: %d\n", ret);
+
+ return ret;
+ }
+
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_FF_DEVICE_INDEX, &device_info);
+
+ if ((ret != 0) || ((device_info & 0xF8) != SMA1303_DEVICE_ID)) {
+ dev_err(&client->dev, "device initialization error (%d 0x%02X)",
+ ret, device_info);
+ }
+ dev_dbg(&client->dev, "chip version 0x%02X\n", device_info);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_00_SYSTEM_CTRL,
+ SMA1303_RESETBYI2C_MASK, SMA1303_RESETBYI2C_RESET,
+ NULL);
+
+ ret += sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &status);
+ sma1303->rev_num = status & SMA1303_REV_NUM_STATUS;
+ if (sma1303->rev_num == SMA1303_REV_NUM_TV0)
+ dev_dbg(&client->dev, "SMA1303 Trimming Version 0\n");
+ else if (sma1303->rev_num == SMA1303_REV_NUM_TV1)
+ dev_dbg(&client->dev, "SMA1303 Trimming Version 1\n");
+
+ ret += sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &otp_stat);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "failed to read, register: %02X, ret: %d\n",
+ SMA1303_FF_DEVICE_INDEX, ret);
+
+ if (((sma1303->rev_num == SMA1303_REV_NUM_TV0) &&
+ ((otp_stat & 0x0E) == SMA1303_OTP_STAT_OK_0)) ||
+ ((sma1303->rev_num != SMA1303_REV_NUM_TV0) &&
+ ((otp_stat & 0x0C) == SMA1303_OTP_STAT_OK_1)))
+ dev_dbg(&client->dev, "SMA1303 OTP Status Successful\n");
+ else
+ dev_dbg(&client->dev, "SMA1303 OTP Status Fail\n");
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1303_reg_def); i++)
+ ret += sma1303_regmap_write(sma1303,
+ sma1303_reg_def[i].reg,
+ sma1303_reg_def[i].def);
+
+ sma1303->amp_mode = SMA1303_MONO;
+ sma1303->amp_power_status = false;
+ sma1303->check_fault_period = CHECK_PERIOD_TIME;
+ sma1303->check_fault_status = true;
+ sma1303->force_mute_status = false;
+ sma1303->init_vol = 0x31;
+ sma1303->cur_vol = sma1303->init_vol;
+ sma1303->last_bclk = 0;
+ sma1303->last_ocp_val = 0x08;
+ sma1303->last_over_temp = 0xC0;
+ sma1303->tsdw_cnt = 0;
+ sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT;
+ sma1303->tdm_slot_rx = 0;
+ sma1303->tdm_slot_tx = 0;
+ sma1303->sys_clk_id = SMA1303_PLL_CLKIN_BCLK;
+
+ sma1303->dev = &client->dev;
+ sma1303->kobj = &client->dev.kobj;
+
+ INIT_DELAYED_WORK(&sma1303->check_fault_work,
+ sma1303_check_fault_worker);
+
+ i2c_set_clientdata(client, sma1303);
+
+ sma1303->pll_matches = sma1303_pll_matches;
+ sma1303->num_of_pll_matches =
+ ARRAY_SIZE(sma1303_pll_matches);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &sma1303_component, sma1303_dai, 1);
+ if (ret) {
+ dev_err(&client->dev, "Failed to register component");
+
+ return ret;
+ }
+
+ sma1303->attr_grp = &sma1303_attr_group;
+ ret = sysfs_create_group(sma1303->kobj, sma1303->attr_grp);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to create attribute group [%d]\n", ret);
+ sma1303->attr_grp = NULL;
+ }
+
+ return ret;
+}
+
+static void sma1303_i2c_remove(struct i2c_client *client)
+{
+ struct sma1303_priv *sma1303 =
+ (struct sma1303_priv *) i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+}
+
+static const struct i2c_device_id sma1303_i2c_id[] = {
+ {"sma1303"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id);
+
+static const struct of_device_id sma1303_of_match[] = {
+ { .compatible = "irondevice,sma1303", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sma1303_of_match);
+
+static struct i2c_driver sma1303_i2c_driver = {
+ .driver = {
+ .name = "sma1303",
+ .of_match_table = sma1303_of_match,
+ },
+ .probe = sma1303_i2c_probe,
+ .remove = sma1303_i2c_remove,
+ .id_table = sma1303_i2c_id,
+};
+
+module_i2c_driver(sma1303_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC SMA1303 driver");
+MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("Kiseok Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h
new file mode 100644
index 000000000000..ae70f207adde
--- /dev/null
+++ b/sound/soc/codecs/sma1303.h
@@ -0,0 +1,609 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * sma1303.h -- sma1303 ALSA SoC Audio driver
+ *
+ * Copyright 2023 Iron Device Corporation
+ *
+ * Author: Kiseok Jo <kiseok.jo@irondevice.com>
+ *
+ */
+
+#ifndef _SMA1303_H
+#define _SMA1303_H
+
+#define SMA1303_I2C_ADDR_00 0x1e
+#define SMA1303_I2C_ADDR_01 0x3e
+#define SMA1303_I2C_ADDR_10 0x5e
+#define SMA1303_I2C_ADDR_11 0x7e
+
+#define SMA1303_EXTERNAL_CLOCK_19_2 0x00
+#define SMA1303_EXTERNAL_CLOCK_24_576 0x01
+#define SMA1303_PLL_CLKIN_MCLK 0x02
+#define SMA1303_PLL_CLKIN_BCLK 0x03
+
+#define SMA1303_MONO 0x00
+#define SMA1303_STEREO 0x01
+
+#define SMA1303_I2C_RETRY_COUNT 3
+
+/*
+ * SMA1303 Register Definition
+ */
+
+/* SMA1303 Register Addresses */
+#define SMA1303_00_SYSTEM_CTRL 0x00
+#define SMA1303_01_INPUT1_CTRL1 0x01
+#define SMA1303_02_INPUT1_CTRL2 0x02
+#define SMA1303_03_INPUT1_CTRL3 0x03
+#define SMA1303_04_INPUT1_CTRL4 0x04
+/* 0x05 ~ 0x08 : Reserved */
+#define SMA1303_09_OUTPUT_CTRL 0x09
+#define SMA1303_0A_SPK_VOL 0x0a
+#define SMA1303_0B_BST_TEST 0x0b
+#define SMA1303_0C_BST_TEST1 0x0c
+#define SMA1303_0D_SPK_TEST 0x0d
+#define SMA1303_0E_MUTE_VOL_CTRL 0x0e
+/* 0x0F : Reserved */
+#define SMA1303_10_SYSTEM_CTRL1 0x10
+#define SMA1303_11_SYSTEM_CTRL2 0x11
+#define SMA1303_12_SYSTEM_CTRL3 0x12
+/* 0x13 : Reserved */
+#define SMA1303_14_MODULATOR 0x14
+#define SMA1303_15_BASS_SPK1 0x15
+#define SMA1303_16_BASS_SPK2 0x16
+#define SMA1303_17_BASS_SPK3 0x17
+#define SMA1303_18_BASS_SPK4 0x18
+#define SMA1303_19_BASS_SPK5 0x19
+#define SMA1303_1A_BASS_SPK6 0x1a
+#define SMA1303_1B_BASS_SPK7 0x1b
+/* 0x1C ~ 0x22 : Reserved */
+#define SMA1303_23_COMP_LIM1 0x23
+#define SMA1303_24_COMP_LIM2 0x24
+#define SMA1303_25_COMP_LIM3 0x25
+#define SMA1303_26_COMP_LIM4 0x26
+/* 0x27 ~ 0x32 : Reserved */
+#define SMA1303_33_SDM_CTRL 0x33
+#define SMA1303_34_OTP_DATA1 0x34
+/* 0x35 : Reserved */
+#define SMA1303_36_PROTECTION 0x36
+#define SMA1303_37_SLOPE_CTRL 0x37
+#define SMA1303_38_OTP_TRM0 0x38
+/* 0x39 ~ 0x3A : Reserved */
+#define SMA1303_3B_TEST1 0x3b
+#define SMA1303_3C_TEST2 0x3c
+#define SMA1303_3D_TEST3 0x3d
+#define SMA1303_3E_ATEST1 0x3e
+#define SMA1303_3F_ATEST2 0x3f
+/* 0x40 ~ 0x8A : Reserved */
+#define SMA1303_8B_PLL_POST_N 0x8b
+#define SMA1303_8C_PLL_N 0x8c
+#define SMA1303_8D_PLL_A_SETTING 0x8d
+#define SMA1303_8E_PLL_CTRL 0x8e
+#define SMA1303_8F_PLL_P_CP 0x8f
+#define SMA1303_90_POSTSCALER 0x90
+#define SMA1303_91_CLASS_G_CTRL 0x91
+#define SMA1303_92_FDPEC_CTRL 0x92
+/* 0x93 : Reserved */
+#define SMA1303_94_BOOST_CTRL1 0x94
+#define SMA1303_95_BOOST_CTRL2 0x95
+#define SMA1303_96_BOOST_CTRL3 0x96
+#define SMA1303_97_BOOST_CTRL4 0x97
+/* 0x98 ~ 0x9F : Reserved */
+#define SMA1303_A0_PAD_CTRL0 0xa0
+#define SMA1303_A1_PAD_CTRL1 0xa1
+#define SMA1303_A2_TOP_MAN1 0xa2
+#define SMA1303_A3_TOP_MAN2 0xa3
+#define SMA1303_A4_TOP_MAN3 0xa4
+#define SMA1303_A5_TDM1 0xa5
+#define SMA1303_A6_TDM2 0xa6
+#define SMA1303_A7_CLK_MON 0xa7
+/* 0xA8 ~ 0xF9 : Reserved */
+#define SMA1303_FA_STATUS1 0xfa
+#define SMA1303_FB_STATUS2 0xfb
+/* 0xFC ~ 0xFE : Reserved */
+#define SMA1303_FF_DEVICE_INDEX 0xff
+
+/* SMA1303 Registers Bit Fields */
+
+/* SYSTEM_CTRL : 0x00 */
+#define SMA1303_RESETBYI2C_MASK (1<<1)
+#define SMA1303_RESETBYI2C_NORMAL (0<<1)
+#define SMA1303_RESETBYI2C_RESET (1<<1)
+
+#define SMA1303_POWER_MASK (1<<0)
+#define SMA1303_POWER_OFF (0<<0)
+#define SMA1303_POWER_ON (1<<0)
+
+/* INTPUT CTRL1 : 0x01 */
+#define SMA1303_CONTROLLER_DEVICE_MASK (1<<7)
+#define SMA1303_DEVICE_MODE (0<<7)
+#define SMA1303_CONTROLLER_MODE (1<<7)
+
+#define SMA1303_I2S_MODE_MASK (7<<4)
+#define SMA1303_STANDARD_I2S (0<<4)
+#define SMA1303_LJ (1<<4)
+#define SMA1303_RJ_16BIT (4<<4)
+#define SMA1303_RJ_18BIT (5<<4)
+#define SMA1303_RJ_20BIT (6<<4)
+#define SMA1303_RJ_24BIT (7<<4)
+
+#define SMA1303_LEFTPOL_MASK (1<<3)
+#define SMA1303_LOW_FIRST_CH (0<<3)
+#define SMA1303_HIGH_FIRST_CH (1<<3)
+
+#define SMA1303_SCK_RISING_MASK (1<<2)
+#define SMA1303_SCK_FALLING_EDGE (0<<2)
+#define SMA1303_SCK_RISING_EDGE (1<<2)
+
+/* INTPUT CTRL2 : 0x02 */
+#define SMA1303_IMODE_MASK (3<<6)
+#define SMA1303_I2S (0<<6)
+#define SMA1303_PCM_SHORT (1<<6)
+#define SMA1303_PCM_LONG (2<<6)
+
+#define RSMA1303_IGHT_FIRST_MASK (1<<5)
+#define SMA1303_LEFT_NORMAL (0<<5)
+#define SMA1303_RIGHT_INVERTED (1<<5)
+
+#define SMA1303_PCM_ALAW_MASK (1<<4)
+#define SMA1303_PCM_U_DECODING (0<<4)
+#define SMA1303_PCM_A_DECODING (1<<4)
+
+#define SMA1303_PCM_COMP_MASK (1<<3)
+#define SMA1303_PCM_LINEAR (0<<3)
+#define SMA1303_PCM_COMPANDING (1<<3)
+
+#define SMA1303_INPUTSEL_MASK (1<<2)
+#define SMA1303_PCM_8KHZ (0<<2)
+#define SMA1303_PCM_16KHZ (1<<2)
+
+#define SMA1303_PCM_STEREO_MASK (1<<1)
+#define SMA1303_PCM_MONO (0<<1)
+#define SMA1303_PCM_STEREO (1<<1)
+
+#define SMA1303_PCM_DL_MASK (1<<0)
+#define SMA1303_PCM_8BIT (0<<0)
+#define SMA1303_PCM_16BIT (1<<0)
+
+/* INTPUT CTRL3 : 0x03 */
+#define SMA1303_PCM_N_SLOT_MASK (15<<0)
+#define SMA1303_PCM_N_SLOT1 (0<<0)
+#define SMA1303_PCM_N_SLOT2 (1<<0)
+#define SMA1303_PCM_N_SLOT3 (2<<0)
+#define SMA1303_PCM_N_SLOT4 (3<<0)
+#define SMA1303_PCM_N_SLOT5 (4<<0)
+#define SMA1303_PCM_N_SLOT6 (5<<0)
+#define SMA1303_PCM_N_SLOT7 (6<<0)
+#define SMA1303_PCM_N_SLOT8 (7<<0)
+#define SMA1303_PCM_N_SLOT9 (8<<0)
+#define SMA1303_PCM_N_SLOT10 (9<<0)
+#define SMA1303_PCM_N_SLOT11 (10<<0)
+#define SMA1303_PCM_N_SLOT12 (11<<0)
+#define SMA1303_PCM_N_SLOT13 (12<<0)
+#define SMA1303_PCM_N_SLOT14 (13<<0)
+#define SMA1303_PCM_N_SLOT15 (14<<0)
+#define SMA1303_PCM_N_SLOT16 (15<<0)
+
+/* INTPUT CTRL4 : 0x04 */
+#define SMA1303_PCM1_SLOT_MASK (15<<4)
+#define SMA1303_PCM1_SLOT1 (0<<4)
+#define SMA1303_PCM1_SLOT2 (1<<4)
+#define SMA1303_PCM1_SLOT3 (2<<4)
+#define SMA1303_PCM1_SLOT4 (3<<4)
+#define SMA1303_PCM1_SLOT5 (4<<4)
+#define SMA1303_PCM1_SLOT6 (5<<4)
+#define SMA1303_PCM1_SLOT7 (6<<4)
+#define SMA1303_PCM1_SLOT8 (7<<4)
+#define SMA1303_PCM1_SLOT9 (8<<4)
+#define SMA1303_PCM1_SLOT10 (9<<4)
+#define SMA1303_PCM1_SLOT11 (10<<4)
+#define SMA1303_PCM1_SLOT12 (11<<4)
+#define SMA1303_PCM1_SLOT13 (12<<4)
+#define SMA1303_PCM1_SLOT14 (13<<4)
+#define SMA1303_PCM1_SLOT15 (14<<4)
+#define SMA1303_PCM1_SLOT16 (15<<4)
+
+#define SMA1303_PCM2_SLOT_MASK (15<<0)
+#define SMA1303_PCM2_SLOT1 (0<<0)
+#define SMA1303_PCM2_SLOT2 (1<<0)
+#define SMA1303_PCM2_SLOT3 (2<<0)
+#define SMA1303_PCM2_SLOT4 (3<<0)
+#define SMA1303_PCM2_SLOT5 (4<<0)
+#define SMA1303_PCM2_SLOT6 (5<<0)
+#define SMA1303_PCM2_SLOT7 (6<<0)
+#define SMA1303_PCM2_SLOT8 (7<<0)
+#define SMA1303_PCM2_SLOT9 (8<<0)
+#define SMA1303_PCM2_SLOT10 (9<<0)
+#define SMA1303_PCM2_SLOT11 (10<<0)
+#define SMA1303_PCM2_SLOT12 (11<<0)
+#define SMA1303_PCM2_SLOT13 (12<<0)
+#define SMA1303_PCM2_SLOT14 (13<<0)
+#define SMA1303_PCM2_SLOT15 (14<<0)
+#define SMA1303_PCM2_SLOT16 (15<<0)
+
+/* OUTPUT CTRL : 0x09 */
+#define SMA1303_PORT_CONFIG_MASK (3<<5)
+#define SMA1303_INPUT_PORT_ONLY (0<<5)
+#define SMA1303_OUTPUT_PORT_ENABLE (2<<5)
+
+#define SMA1303_PORT_OUT_SEL_MASK (7<<0)
+#define SMA1303_OUT_SEL_DISABLE (0<<0)
+#define SMA1303_FORMAT_CONVERTER (1<<0)
+#define SMA1303_MIXER_OUTPUT (2<<0)
+#define SMA1303_SPEAKER_PATH (3<<0)
+#define SMA1303_POSTSCALER_OUTPUT (4<<0)
+
+/* BST_TEST : 0x0B */
+#define SMA1303_BST_OFF_SLOPE_MASK (3<<6)
+#define SMA1303_BST_OFF_SLOPE_6_7ns (0<<6)
+#define SMA1303_BST_OFF_SLOPE_4_8ns (1<<6)
+#define SMA1303_BST_OFF_SLOPE_2_6ns (2<<6)
+#define SMA1303_BST_OFF_SLOPE_1_2ns (3<<6)
+
+#define SMA1303_OCP_TEST_MASK (1<<5)
+#define SMA1303_OCP_NORMAL_MODE (0<<5)
+#define SMA1303_OCP_TEST_MODE (1<<5)
+
+#define SMA1303_BST_FAST_LEBN_MASK (1<<4)
+#define SMA1303_BST_SHORT_LEB (0<<4)
+#define SMA1303_BST_LONG_LEB (1<<4)
+
+#define SMA1303_HIGH_PGAIN_MASK (1<<3)
+#define SMA1303_NORMAL_P_GAIN (0<<3)
+#define SMA1303_HIGH_P_GAIN (1<<3)
+
+#define SMA1303_VCOMP_MASK (1<<2)
+#define SMA1303_VCOMP_NORMAL_MODE (0<<2)
+#define SMA1303_VCOMP_V_MON_MODE (1<<2)
+
+#define SMA1303_PMOS_ON_MASK (1<<1)
+#define SMA1303_PMOS_NORMAL_MODE (0<<1)
+#define SMA1303_PMOS_TEST_MODE (1<<1)
+
+#define SMA1303_NMOS_ON_MASK (1<<0)
+#define SMA1303_NMOS_NORMAL_MODE (0<<0)
+#define SMA1303_NMOS_TEST_MODE (1<<0)
+
+/* BST_TEST1 : 0x0C */
+#define SMA1303_SET_OCP_H_MASK (3<<6)
+#define SMA1303_HIGH_OCP_4_5_LVL (0<<6)
+#define SMA1303_HIGH_OCP_3_2_LVL (1<<6)
+#define SMA1303_HIGH_OCP_2_1_LVL (2<<6)
+#define SMA1303_HIGH_OCP_0_9_LVL (3<<6)
+
+#define SMA1303_OCL_TEST_MASK (1<<5)
+#define SMA1303_OCL_NORMAL_MODE (0<<5)
+#define SMA1303_OCL_TEST_MODE (1<<5)
+
+#define SMA1303_LOOP_CHECK_MASK (1<<4)
+#define SMA1303_BST_LOOP_NORMAL_MODE (0<<4)
+#define SMA1303_BST_LOOP_CHECK_MODE (1<<4)
+
+#define SMA1303_EN_SH_PRT_MASK (1<<3)
+#define SMA1303_EN_SH_PRT_DISABLE (0<<3)
+#define SMA1303_EN_SH_PRT_ENABLE (1<<3)
+
+/* SPK_TEST : 0x0D */
+#define SMA1303_VREF_MON_MASK (1<<3)
+#define SMA1303_VREF_NORMAL_MODE (0<<3)
+#define SMA1303_VREF_V_MON_MODE (1<<3)
+
+#define SMA1303_SPK_OCP_DLYN_MASK (1<<2)
+#define SMA1303_SPK_OCP_LONG_DELAY (0<<2)
+#define SMA1303_SPK_OCP_NORMAL (1<<2)
+
+#define SMA1303_SPK_OFF_SLOPE_MASK (3<<0)
+#define SMA1303_SPK_OFF_SLOPE_SLOW (0<<0)
+#define SMA1303_SPK_OFF_SLOPE_FAST (3<<0)
+
+/* MUTE_VOL_CTRL : 0x0E */
+#define SMA1303_VOL_SLOPE_MASK (3<<6)
+#define SMA1303_VOL_SLOPE_OFF (0<<6)
+#define SMA1303_VOL_SLOPE_SLOW (1<<6)
+#define SMA1303_VOL_SLOPE_MID (2<<6)
+#define SMA1303_VOL_SLOPE_FAST (3<<6)
+
+#define SMA1303_MUTE_SLOPE_MASK (3<<4)
+#define SMA1303_MUTE_SLOPE_OFF (0<<4)
+#define SMA1303_MUTE_SLOPE_SLOW (1<<4)
+#define SMA1303_MUTE_SLOPE_MID (2<<4)
+#define SMA1303_MUTE_SLOPE_FAST (3<<4)
+
+#define SMA1303_SPK_MUTE_MASK (1<<0)
+#define SMA1303_SPK_UNMUTE (0<<0)
+#define SMA1303_SPK_MUTE (1<<0)
+
+/* SYSTEM_CTRL1 :0x10 */
+#define SMA1303_SPK_MODE_MASK (7<<2)
+#define SMA1303_SPK_OFF (0<<2)
+#define SMA1303_SPK_MONO (1<<2)
+#define SMA1303_SPK_STEREO (4<<2)
+
+/* SYSTEM_CTRL2 : 0x11 */
+#define SMA1303_SPK_BS_MASK (1<<6)
+#define SMA1303_SPK_BS_BYP (0<<6)
+#define SMA1303_SPK_BS_EN (1<<6)
+#define SMA1303_SPK_LIM_MASK (1<<5)
+#define SMA1303_SPK_LIM_BYP (0<<5)
+#define SMA1303_SPK_LIM_EN (1<<5)
+
+#define SMA1303_LR_DATA_SW_MASK (1<<4)
+#define SMA1303_LR_DATA_SW_NORMAL (0<<4)
+#define SMA1303_LR_DATA_SW_SWAP (1<<4)
+
+#define SMA1303_MONOMIX_MASK (1<<0)
+#define SMA1303_MONOMIX_OFF (0<<0)
+#define SMA1303_MONOMIX_ON (1<<0)
+
+/* SYSTEM_CTRL3 : 0x12 */
+#define SMA1303_INPUT_MASK (3<<6)
+#define SMA1303_INPUT_0_DB (0<<6)
+#define SMA1303_INPUT_M6_DB (1<<6)
+#define SMA1303_INPUT_M12_DB (2<<6)
+#define SMA1303_INPUT_INFI_DB (3<<6)
+#define SMA1303_INPUT_R_MASK (3<<4)
+#define SMA1303_INPUT_R_0_DB (0<<4)
+#define SMA1303_INPUT_R_M6_DB (1<<4)
+#define SMA1303_INPUT_R_M12_DB (2<<4)
+#define SMA1303_INPUT_R_INFI_DB (3<<4)
+
+/* Modulator : 0x14 */
+#define SMA1303_SPK_HYSFB_MASK (3<<6)
+#define SMA1303_HYSFB_625K (0<<6)
+#define SMA1303_HYSFB_414K (1<<6)
+#define SMA1303_HYSFB_297K (2<<6)
+#define SMA1303_HYSFB_226K (3<<6)
+#define SMA1303_SPK_BDELAY_MASK (63<<0)
+
+/* SDM CONTROL : 0x33 */
+#define SMA1303_SDM_Q_SEL_MASK (1<<2)
+#define SMA1303_QUART_SEL_1_DIV_4 (0<<2)
+#define SMA1303_QUART_SEL_1_DIV_8 (1<<2)
+
+/* OTP_DATA1 : 0x34 */
+#define SMA1303_OTP_LVL_MASK (1<<5)
+#define SMA1303_OTP_LVL_NORMAL (0<<5)
+#define SMA1303_OTP_LVL_LOW (1<<5)
+
+/* PROTECTION : 0x36 */
+#define SMA1303_EDGE_DIS_MASK (1<<7)
+#define SMA1303_EDGE_DIS_ENABLE (0<<7)
+#define SMA1303_EDGE_DIS_DISABLE (1<<7)
+
+#define SMA1303_SPK_OCP_DIS_MASK (1<<3)
+#define SMA1303_SPK_OCP_ENABLE (0<<3)
+#define SMA1303_SPK_OCP_DISABLE (1<<3)
+
+#define SMA1303_OCP_MODE_MASK (1<<2)
+#define SMA1303_AUTO_RECOVER (0<<2)
+#define SMA1303_SHUT_DOWN_PERMANENT (1<<2)
+
+#define SMA1303_OTP_MODE_MASK (3<<0)
+#define SMA1303_OTP_MODE_DISABLE (0<<0)
+#define SMA1303_IG_THR1_SHUT_THR2 (1<<0)
+#define SMA1303_REC_THR1_SHUT_THR2 (2<<0)
+#define SMA1303_SHUT_THR1_SHUT_THR2 (3<<0)
+
+/* TEST2 : 0x3C */
+#define SMA1303_SPK_HSDM_BP_MASK (1<<4)
+#define SMA1303_SPK_HSDM_ENABLE (0<<4)
+#define SMA1303_SPK_HSDM_BYPASS (1<<4)
+
+#define SMA1303_SDM_SYNC_DIS_MASK (1<<5)
+#define SMA1303_SDM_SYNC_NORMAL (0<<5)
+#define SMA1303_SDM_SYNC_DISABLE (1<<5)
+
+/* ATEST2 : 0x3F */
+#define SMA1303_SPK_OUT_FREQ_MASK (1<<2)
+#define SMA1303_SPK_OUT_FREQ_360K (0<<2)
+#define SMA1303_SPK_OUT_FREQ_410K (1<<2)
+
+#define SMA1303_LOW_POWER_MODE_MASK (1<<3)
+#define SMA1303_LOW_POWER_MODE_DISABLE (0<<3)
+#define SMA1303_LOW_POWER_MODE_ENABLE (1<<3)
+
+#define SMA1303_THERMAL_ADJUST_MASK (3<<5)
+#define SMA1303_THERMAL_150_110 (0<<5)
+#define SMA1303_THERMAL_160_120 (1<<5)
+#define SMA1303_THERMAL_140_100 (2<<5)
+
+#define SMA1303_FAST_OFF_DRIVE_SPK_MASK (1<<0)
+#define SMA1303_FAST_OFF_DRIVE_SPK_DISABLE (0<<0)
+#define SMA1303_FAST_OFF_DRIVE_SPK_ENABLE (1<<0)
+
+/* PLL_CTRL : 0x8E */
+#define SMA1303_TRM_LVL_MASK (1<<4)
+#define SMA1303_TRM_LVL_NORMAL (0<<4)
+#define SMA1303_TRM_LVL_LOW (1<<4)
+
+#define SMA1303_LOW_OCL_MODE_MASK (1<<3)
+#define SMA1303_LOW_OCL_MODE (0<<3)
+#define SMA1303_NORMAL_OCL_MODE (1<<3)
+
+#define SMA1303_PLL_PD2_MASK (7<<0)
+#define SMA1303_PLL_PD2 (7<<0)
+#define SMA1303_PLL_OPERATION2 (0<<0)
+
+/* POSTSCALER : 0x90 */
+#define SMA1303_BYP_POST_MASK (1<<0)
+#define SMA1303_EN_POST_SCALER (0<<0)
+#define SMA1303_BYP_POST_SCALER (1<<0)
+
+/* FDPEC CONTROL : 0x92 */
+#define SMA1303_FLT_VDD_GAIN_MASK (15<<4)
+#define SMA1303_FLT_VDD_GAIN_2P40 (0<<4)
+#define SMA1303_FLT_VDD_GAIN_2P45 (1<<4)
+#define SMA1303_FLT_VDD_GAIN_2P50 (2<<4)
+#define SMA1303_FLT_VDD_GAIN_2P55 (3<<4)
+#define SMA1303_FLT_VDD_GAIN_2P60 (4<<4)
+#define SMA1303_FLT_VDD_GAIN_2P65 (5<<4)
+#define SMA1303_FLT_VDD_GAIN_2P70 (6<<4)
+#define SMA1303_FLT_VDD_GAIN_2P75 (7<<4)
+#define SMA1303_FLT_VDD_GAIN_2P80 (8<<4)
+#define SMA1303_FLT_VDD_GAIN_2P85 (9<<4)
+#define SMA1303_FLT_VDD_GAIN_2P90 (10<<4)
+#define SMA1303_FLT_VDD_GAIN_2P95 (11<<4)
+#define SMA1303_FLT_VDD_GAIN_3P00 (12<<4)
+#define SMA1303_FLT_VDD_GAIN_3P05 (13<<4)
+#define SMA1303_FLT_VDD_GAIN_3P10 (14<<4)
+#define SMA1303_FLT_VDD_GAIN_3P15 (15<<4)
+
+#define SMA1303_DIS_FCHG_MASK (1<<2)
+#define SMA1303_EN_FAST_CHARGE (0<<2)
+#define SMA1303_DIS_FAST_CHARGE (1<<2)
+
+/* BOOST_CONTROL4 : 0x97 */
+#define SMA1303_TRM_VBST_MASK (7<<2)
+#define SMA1303_TRM_VBST_5P5 (0<<2)
+#define SMA1303_TRM_VBST_5P6 (1<<2)
+#define SMA1303_TRM_VBST_5P7 (2<<2)
+#define SMA1303_TRM_VBST_5P8 (3<<2)
+#define SMA1303_TRM_VBST_5P9 (4<<2)
+#define SMA1303_TRM_VBST_6P0 (5<<2)
+#define SMA1303_TRM_VBST_6P1 (6<<2)
+#define SMA1303_TRM_VBST_6P2 (7<<2)
+
+/* TOP_MAN1 : 0xA2 */
+#define SMA1303_PLL_LOCK_SKIP_MASK (1<<7)
+#define SMA1303_PLL_LOCK_ENABLE (0<<7)
+#define SMA1303_PLL_LOCK_DISABLE (1<<7)
+
+#define SMA1303_PLL_PD_MASK (1<<6)
+#define SMA1303_PLL_OPERATION (0<<6)
+#define SMA1303_PLL_PD (1<<6)
+
+#define SMA1303_PLL_DIV_MASK (3<<4)
+#define SMA1303_PLL_OUT (0<<4)
+#define SMA1303_PLL_OUT_2 (1<<4)
+#define SMA1303_PLL_OUT_4 (2<<4)
+#define SMA1303_PLL_OUT_8 (3<<4)
+
+#define SMA1303_PLL_REF_CLK_MASK (1<<3)
+#define SMA1303_PLL_REF_CLK1 (0<<3)
+#define SMA1303_PLL_SCK (1<<3)
+
+#define SMA1303_DAC_DN_CONV_MASK (1<<2)
+#define SMA1303_DAC_DN_CONV_DISABLE (0<<2)
+#define SMA1303_DAC_DN_CONV_ENABLE (1<<2)
+
+#define SMA1303_SDO_IO_MASK (1<<1)
+#define SMA1303_HIGH_Z_LRCK_H (0<<1)
+#define SMA1303_HIGH_Z_LRCK_L (1<<1)
+
+#define SMA1303_SDO_OUTPUT2_MASK (1<<0)
+#define SMA1303_SDO_NORMAL (0<<0)
+#define SMA1303_SDO_OUTPUT_ONLY (1<<0)
+
+/* TOP_MAN2 : 0xA3 */
+#define SMA1303_MON_OSC_PLL_MASK (1<<7)
+#define SMA1303_PLL_SDO (0<<7)
+#define SMA1303_OSC_SDO (1<<7)
+
+#define SMA1303_TEST_CLKO_EN_MASK (1<<6)
+#define SMA1303_NORMAL_SDO (0<<6)
+#define SMA1303_CLK_OUT_SDO (1<<6)
+
+#define SMA1303_SDO_OUTPUT_MASK (1<<3)
+#define SMA1303_NORMAL_OUT (0<<3)
+#define SMA1303_HIGH_Z_OUT (1<<3)
+
+#define SMA1303_CLOCK_MON_MASK (1<<1)
+#define SMA1303_CLOCK_MON (0<<1)
+#define SMA1303_CLOCK_NOT_MON (1<<1)
+
+#define SMA1303_OSC_PD_MASK (1<<0)
+#define SMA1303_NORMAL_OPERATION_OSC (0<<0)
+#define SMA1303_POWER_DOWN_OSC (1<<0)
+
+/* TOP_MAN3 0xA4 */
+#define SMA1303_O_FORMAT_MASK (7<<5)
+#define SMA1303_O_FMT_LJ (1<<5)
+#define SMA1303_O_FMT_I2S (2<<5)
+#define SMA1303_O_FMT_TDM (4<<5)
+
+#define SMA1303_SCK_RATE_MASK (1<<3)
+#define SMA1303_SCK_64FS (0<<3)
+#define SMA1303_SCK_32FS (2<<3)
+
+#define SMA1303_LRCK_POL_MASK (1<<0)
+#define SMA1303_L_VALID (0<<0)
+#define SMA1303_R_VALID (1<<0)
+
+/* TDM1 FORMAT : 0xA5 */
+#define SMA1303_TDM_CLK_POL_MASK (1<<7)
+#define SMA1303_TDM_CLK_POL_RISE (0<<7)
+#define SMA1303_TDM_CLK_POL_FALL (1<<7)
+
+#define SMA1303_TDM_TX_MODE_MASK (1<<6)
+#define SMA1303_TDM_TX_MONO (0<<6)
+#define SMA1303_TDM_TX_STEREO (1<<6)
+
+#define SMA1303_TDM_SLOT1_RX_POS_MASK (7<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_0 (0<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_1 (1<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_2 (2<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_3 (3<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_4 (4<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_5 (5<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_6 (6<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_7 (7<<3)
+
+#define SMA1303_TDM_SLOT2_RX_POS_MASK (7<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_0 (0<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_1 (1<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_2 (2<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_3 (3<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_4 (4<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_5 (5<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_6 (6<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_7 (7<<0)
+
+/* TDM2 FORMAT : 0xA6 */
+#define SMA1303_TDM_DL_MASK (1<<7)
+#define SMA1303_TDM_DL_16 (0<<7)
+#define SMA1303_TDM_DL_32 (1<<7)
+
+#define SMA1303_TDM_N_SLOT_MASK (1<<6)
+#define SMA1303_TDM_N_SLOT_4 (0<<6)
+#define SMA1303_TDM_N_SLOT_8 (1<<6)
+
+#define SMA1303_TDM_SLOT1_TX_POS_MASK (7<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_0 (0<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_1 (1<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_2 (2<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_3 (3<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_4 (4<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_5 (5<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_6 (6<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_7 (7<<3)
+
+#define SMA1303_TDM_SLOT2_TX_POS_MASK (7<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_0 (0<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_1 (1<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_2 (2<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_3 (3<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_4 (4<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_5 (5<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_6 (6<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_7 (7<<0)
+
+/* STATUS1 : 0xFA */
+#define SMA1303_OT1_OK_STATUS (1<<7)
+#define SMA1303_OT2_OK_STATUS (1<<6)
+
+/* STATUS2 : 0xFB */
+#define SMA1303_OCP_SPK_STATUS (1<<5)
+#define SMA1303_OCP_BST_STATUS (1<<4)
+#define SMA1303_OTP_STAT_OK_0 (5<<1)
+#define SMA1303_OTP_STAT_OK_1 (2<<2)
+
+#define SMA1303_CLK_MON_STATUS (1<<0)
+
+/* DEVICE_INFO : 0xFF */
+#define SMA1303_DEVICE_ID (2<<3)
+#define SMA1303_UVLO_BST_STATUS (1<<2)
+#define SMA1303_REV_NUM_STATUS (3<<0)
+#define SMA1303_REV_NUM_TV0 (0<<0)
+#define SMA1303_REV_NUM_TV1 (1<<0)
+
+#endif
diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c
new file mode 100644
index 000000000000..4bb59e5c0891
--- /dev/null
+++ b/sound/soc/codecs/sma1307.c
@@ -0,0 +1,2044 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// sma1307.c -- sma1307 ALSA SoC Audio driver
+//
+// Copyright 2024 Iron Device Corporation
+//
+// Auther: Gyuhwa Park <gyuwha.park@irondevice.com>
+// Auther: Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "sma1307.h"
+
+#define CHECK_PERIOD_TIME 1 /* sec per HZ */
+#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\
+ _post_n, _n, _vco, _p_cp)\
+{\
+ .input_clk_name = _input_clk_name,\
+ .output_clk_name = _output_clk_name,\
+ .input_clk = _input_clk,\
+ .post_n = _post_n,\
+ .n = _n,\
+ .vco = _vco,\
+ .p_cp = _p_cp,\
+}
+
+static const char *setting_file = "sma1307_setting.bin";
+#define SMA1307_SETTING_CHECKSUM 0x100000
+
+/* PLL clock setting Table */
+struct sma1307_pll_match {
+ char *input_clk_name;
+ char *output_clk_name;
+ unsigned int input_clk;
+ unsigned int post_n;
+ unsigned int n;
+ unsigned int vco;
+ unsigned int p_cp;
+};
+
+struct sma1307_data {
+ char *name;
+ void (*init)(struct regmap *regmap);
+};
+
+struct sma1307_priv {
+ bool check_fault_status;
+ bool force_mute_status;
+ bool sw_ot1_prot;
+ char *name;
+ enum sma1307_mode amp_mode;
+ int binary_mode;
+ int dapm_aif_in;
+ int dapm_aif_out0;
+ int dapm_aif_out1;
+ int dapm_sdo_en;
+ int dapm_sdo_setting;
+ int num_of_pll_matches;
+ int check_fault_period;
+ struct delayed_work check_fault_work;
+ struct device *dev;
+ struct kobject *kobj;
+ struct mutex default_lock;
+ struct regmap *regmap;
+ struct sma1307_setting_file set;
+ const struct sma1307_pll_match *pll_matches;
+ const struct sma1307_data *data;
+ unsigned int cur_vol;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int init_vol;
+ unsigned int last_bclk;
+ unsigned int otp_trm2;
+ unsigned int otp_trm3;
+ unsigned int rev_num;
+ unsigned int sys_clk_id;
+ unsigned int tdm_slot0_rx;
+ unsigned int tdm_slot1_rx;
+ unsigned int tdm_slot0_tx;
+ unsigned int tdm_slot1_tx;
+ unsigned int tsdw_cnt;
+};
+
+static const struct sma1307_pll_match sma1307_pll_matches[] = {
+ /* in_clk_name, out_clk_name, input_clk post_n, n, vco, p_cp */
+ PLL_MATCH("1.411MHz", "24.554MHz",
+ 1411200, 0x06, 0xD1, 0x88, 0x00),
+ PLL_MATCH("1.536MHz", "24.576MHz",
+ 1536000, 0x06, 0xC0, 0x88, 0x00),
+ PLL_MATCH("2.822MHz", "24.554MHz",
+ 2822400, 0x06, 0xD1, 0x88, 0x04),
+ PLL_MATCH("3.072MHz", "24.576MHz",
+ 3072000, 0x06, 0x60, 0x88, 0x00),
+ PLL_MATCH("6.144MHz", "24.576MHz",
+ 6144000, 0x06, 0x60, 0x88, 0x04),
+ PLL_MATCH("12.288MHz", "24.576MHz",
+ 12288000, 0x06, 0x60, 0x88, 0x08),
+ PLL_MATCH("19.2MHz", "24.48MHz",
+ 19200000, 0x06, 0x7B, 0x88, 0x0C),
+ PLL_MATCH("24.576MHz", "24.576MHz",
+ 24576000, 0x06, 0x60, 0x88, 0x0C),
+};
+
+static struct snd_soc_component *sma1307_amp_component;
+
+static void sma1307_startup(struct snd_soc_component *);
+static void sma1307_shutdown(struct snd_soc_component *);
+static void sma1307_reset(struct snd_soc_component *);
+static void sma1307_set_binary(struct snd_soc_component *);
+static void sma1307_set_default(struct snd_soc_component *);
+
+/* Initial register value - 6.0W SPK (8ohm load) */
+static const struct reg_default sma1307_reg_def[] = {
+ { 0x00, 0x80 },
+ { 0x01, 0x00 },
+ { 0x02, 0x52 },
+ { 0x03, 0x4C },
+ { 0x04, 0x47 },
+ { 0x05, 0x42 },
+ { 0x06, 0x40 },
+ { 0x07, 0x40 },
+ { 0x08, 0x3C },
+ { 0x09, 0x2F },
+ { 0x0A, 0x32 },
+ { 0x0B, 0x50 },
+ { 0x0C, 0x8C },
+ { 0x0D, 0x00 },
+ { 0x0E, 0x3F },
+ { 0x0F, 0x00 },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x09 },
+ { 0x14, 0x12 },
+ { 0x1C, 0x00 },
+ { 0x1D, 0x85 },
+ { 0x1E, 0xA1 },
+ { 0x1F, 0x67 },
+ { 0x22, 0x00 },
+ { 0x23, 0x1F },
+ { 0x24, 0x7A },
+ { 0x25, 0x00 },
+ { 0x26, 0xFF },
+ { 0x27, 0x39 },
+ { 0x28, 0x54 },
+ { 0x29, 0x92 },
+ { 0x2A, 0xB0 },
+ { 0x2B, 0xED },
+ { 0x2C, 0xED },
+ { 0x2D, 0xFF },
+ { 0x2E, 0xFF },
+ { 0x2F, 0xFF },
+ { 0x30, 0xFF },
+ { 0x31, 0xFF },
+ { 0x32, 0xFF },
+ { 0x34, 0x01 },
+ { 0x35, 0x17 },
+ { 0x36, 0x92 },
+ { 0x37, 0x00 },
+ { 0x38, 0x01 },
+ { 0x39, 0x10 },
+ { 0x3E, 0x01 },
+ { 0x3F, 0x08 },
+ { 0x8B, 0x05 },
+ { 0x8C, 0x50 },
+ { 0x8D, 0x80 },
+ { 0x8E, 0x10 },
+ { 0x8F, 0x02 },
+ { 0x90, 0x02 },
+ { 0x91, 0x83 },
+ { 0x92, 0xC0 },
+ { 0x93, 0x00 },
+ { 0x94, 0xA4 },
+ { 0x95, 0x74 },
+ { 0x96, 0x57 },
+ { 0xA2, 0xCC },
+ { 0xA3, 0x28 },
+ { 0xA4, 0x40 },
+ { 0xA5, 0x01 },
+ { 0xA6, 0x41 },
+ { 0xA7, 0x08 },
+ { 0xA8, 0x04 },
+ { 0xA9, 0x27 },
+ { 0xAA, 0x10 },
+ { 0xAB, 0x10 },
+ { 0xAC, 0x10 },
+ { 0xAD, 0x0F },
+ { 0xAE, 0xCD },
+ { 0xAF, 0x70 },
+ { 0xB0, 0x03 },
+ { 0xB1, 0xEF },
+ { 0xB2, 0x03 },
+ { 0xB3, 0xEF },
+ { 0xB4, 0xF3 },
+ { 0xB5, 0x3D },
+};
+
+static bool sma1307_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_00_SYSTEM_CTRL ... SMA1307_1F_TONE_FINE_VOLUME:
+ case SMA1307_22_COMP_HYS_SEL ... SMA1307_32_BROWN_OUT_PROT19:
+ case SMA1307_34_OCP_SPK ... SMA1307_39_PMT_NZ_VAL:
+ case SMA1307_3B_TEST1 ... SMA1307_3F_ATEST2:
+ case SMA1307_8B_PLL_POST_N ... SMA1307_9A_OTP_TRM3:
+ case SMA1307_A0_PAD_CTRL0 ... SMA1307_BE_MCBS_CTRL2:
+ case SMA1307_F5_READY_FOR_V_SAR:
+ case SMA1307_F7_READY_FOR_T_SAR ... SMA1307_FF_DEVICE_INDEX:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool sma1307_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_00_SYSTEM_CTRL ... SMA1307_1F_TONE_FINE_VOLUME:
+ case SMA1307_22_COMP_HYS_SEL ... SMA1307_32_BROWN_OUT_PROT19:
+ case SMA1307_34_OCP_SPK ... SMA1307_39_PMT_NZ_VAL:
+ case SMA1307_3B_TEST1 ... SMA1307_3F_ATEST2:
+ case SMA1307_8B_PLL_POST_N ... SMA1307_9A_OTP_TRM3:
+ case SMA1307_A0_PAD_CTRL0 ... SMA1307_BE_MCBS_CTRL2:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool sma1307_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_F8_STATUS_T1 ... SMA1307_FF_DEVICE_INDEX:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+/* DB scale conversion of speaker volume */
+static const DECLARE_TLV_DB_SCALE(sma1307_spk_tlv, -6000, 50, 0);
+
+static const char *const sma1307_aif_in_source_text[] = {
+ "Mono", "Left", "Right"
+};
+
+static const char *const sma1307_sdo_setting_text[] = {
+ "Data_One_48k", "Data_Two_48k", "Data_Two_24k",
+ "Clk_PLL", "Clk_OSC"
+};
+
+static const char *const sma1307_aif_out_source_text[] = {
+ "Disable", "After_FmtC", "After_Mixer", "After_DSP",
+ "Vrms2_Avg", "Battery", "Temperature", "After_Delay"
+};
+
+static const char *const sma1307_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static const char *const sma1307_binary_mode_text[] = {
+ "Mode0", "Mode1", "Mode2", "Mode3", "Mode4"
+};
+
+static const char *const sma1307_reset_text[] = {
+ "Reset"
+};
+
+static const struct soc_enum sma1307_aif_in_source_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_aif_in_source_text),
+ sma1307_aif_in_source_text);
+static const struct soc_enum sma1307_sdo_setting_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_sdo_setting_text),
+ sma1307_sdo_setting_text);
+static const struct soc_enum sma1307_aif_out_source_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_aif_out_source_text),
+ sma1307_aif_out_source_text);
+static const struct soc_enum sma1307_tdm_slot_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_tdm_slot_text),
+ sma1307_tdm_slot_text);
+static const struct soc_enum sma1307_binary_mode_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_binary_mode_text),
+ sma1307_binary_mode_text);
+static const struct soc_enum sma1307_reset_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_reset_text),
+ sma1307_reset_text);
+
+static int sma1307_force_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->force_mute_status;
+
+ return 0;
+}
+
+static int sma1307_force_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->force_mute_status == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->force_mute_status = val;
+ }
+
+ return change;
+}
+
+static int sma1307_tdm_slot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val1, val2;
+
+ regmap_read(sma1307->regmap, SMA1307_A5_TDM1, &val1);
+ regmap_read(sma1307->regmap, SMA1307_A6_TDM2, &val2);
+
+ if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX0_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = (val1 & SMA1307_TDM_SLOT0_RX_POS_MASK) >> 3;
+ sma1307->tdm_slot0_rx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX1_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = val1 & SMA1307_TDM_SLOT1_RX_POS_MASK;
+ sma1307->tdm_slot1_rx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX0_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = (val2 & SMA1307_TDM_SLOT0_TX_POS_MASK) >> 3;
+ sma1307->tdm_slot0_tx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX1_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = val2 & SMA1307_TDM_SLOT1_TX_POS_MASK;
+ sma1307->tdm_slot1_tx = ucontrol->value.integer.value[0];
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_tdm_slot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX0_POS_NAME)) {
+ if (sma1307->tdm_slot0_rx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot0_rx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT0_RX_POS_MASK, val << 3);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX1_POS_NAME)) {
+ if (sma1307->tdm_slot1_rx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot1_rx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT1_RX_POS_MASK, val);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX0_POS_NAME)) {
+ if (sma1307->tdm_slot0_tx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot0_tx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT0_TX_POS_MASK, val << 3);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX1_POS_NAME)) {
+ if (sma1307->tdm_slot1_tx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot1_tx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT1_TX_POS_MASK, val);
+ }
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return change;
+}
+
+static int sma1307_sw_ot1_prot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->sw_ot1_prot;
+
+ return 0;
+}
+
+static int sma1307_sw_ot1_prot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->sw_ot1_prot == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->sw_ot1_prot = val;
+ }
+
+ return change;
+}
+
+static int sma1307_check_fault_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->check_fault_status;
+
+ return 0;
+}
+
+static int sma1307_check_fault_status_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->check_fault_status == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->check_fault_status = val;
+ }
+
+ return change;
+}
+
+static int sma1307_check_fault_period_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sma1307->check_fault_period;
+
+ return 0;
+}
+
+static int sma1307_check_fault_period_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ bool change = false;
+ int val = ucontrol->value.integer.value[0];
+
+ if (val < mc->min || val > mc->max)
+ return -EINVAL;
+ if (sma1307->check_fault_period == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->check_fault_period = val;
+ }
+
+ return change;
+}
+
+static int sma1307_reset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_RESET_MASK, SMA1307_RESET_ON);
+ sma1307_reset(component);
+
+ snd_ctl_notify(component->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &kcontrol->id);
+
+ return true;
+}
+
+static int sma1307_binary_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sma1307_priv *sma1307 = snd_kcontrol_chip(kcontrol);
+
+ sma1307->binary_mode = (int)ucontrol->value.enumerated.item[0];
+ if (sma1307->set.status)
+ sma1307_set_binary(component);
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static void sma1307_startup(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_ON);
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_POWER_MASK, SMA1307_POWER_ON);
+
+ if (sma1307->amp_mode == SMA1307_MONO_MODE) {
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK,
+ SMA1307_SPK_MONO);
+ } else {
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK,
+ SMA1307_SPK_STEREO);
+ }
+
+ if (sma1307->check_fault_status) {
+ if (sma1307->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ sma1307->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+}
+
+static void sma1307_shutdown(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ /* for SMA1307A */
+ cancel_delayed_work_sync(&sma1307->check_fault_work);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK, SMA1307_SPK_MUTE);
+ /* Need to wait time for mute slope */
+ msleep(55);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK, SMA1307_SPK_OFF);
+ regmap_update_bits(sma1307->regmap, SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_OFF);
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_POWER_MASK, SMA1307_POWER_OFF);
+}
+
+static int sma1307_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = sma1307->dapm_aif_in;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case SMA1307_MONO_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_ON);
+ break;
+ case SMA1307_LEFT_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_OFF);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_LR_DATA_SW_MASK,
+ SMA1307_LR_DATA_SW_NORMAL);
+ break;
+ case SMA1307_RIGHT_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_OFF);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_LR_DATA_SW_MASK,
+ SMA1307_LR_DATA_SW_SWAP);
+ break;
+ default:
+
+ dev_err(sma1307->dev, "%s: Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ sma1307->amp_mode = mux;
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_sdo_setting_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = sma1307->dapm_sdo_setting;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case SMA1307_OUT_DATA_ONE_48K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_ONE_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_OUTPUT3_DIS
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_DATA_TWO_48K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_TWO_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_OUTPUT3_DIS
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_DATA_TWO_24K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_TWO_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_TWO_SDO_PER_CH_24K
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_CLK_PLL:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_CLK_PLL);
+
+ break;
+ case SMA1307_OUT_CLK_OSC:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_CLK_OSC);
+
+ break;
+ default:
+ dev_err(sma1307->dev, "%s: Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = 0, val = 0, mask = 0;
+
+ if (!strcmp(w->name, SMA1307_AIF_OUT0_NAME)) {
+ mux = sma1307->dapm_aif_out0;
+ val = mux;
+ mask = SMA1307_SDO_OUT0_SEL_MASK;
+ } else if (!strcmp(w->name, SMA1307_AIF_OUT1_NAME)) {
+ mux = sma1307->dapm_aif_out1;
+ val = mux << 3;
+ mask = SMA1307_SDO_OUT1_SEL_MASK;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid widget - %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(sma1307->regmap, SMA1307_09_OUTPUT_CTRL,
+ mask, val);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_sdo_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_09_OUTPUT_CTRL,
+ SMA1307_PORT_CONFIG_MASK,
+ SMA1307_OUTPUT_PORT_ENABLE);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT_MASK,
+ SMA1307_LOGIC_OUTPUT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_09_OUTPUT_CTRL,
+ SMA1307_PORT_CONFIG_MASK,
+ SMA1307_INPUT_PORT_ONLY);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT_MASK,
+ SMA1307_HIGH_Z_OUTPUT);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sma1307_startup(component);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ sma1307_shutdown(component);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_dapm_aif_in_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = (unsigned int)sma1307->dapm_aif_in;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_aif_in_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_aif_in_source_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_aif_in != val) {
+ change = true;
+ sma1307->dapm_aif_in = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_sdo_setting_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] =
+ (unsigned int)sma1307->dapm_sdo_setting;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_sdo_setting_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_sdo_setting_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_sdo_setting != val) {
+ change = true;
+ sma1307->dapm_sdo_setting = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_aif_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0;
+
+ if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT0_NAME)) {
+ val = (unsigned int)sma1307->dapm_aif_out0;
+ } else if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT1_NAME)) {
+ val = (unsigned int)sma1307->dapm_aif_out1;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+ ucontrol->value.enumerated.item[0] = val;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_aif_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_aif_out_source_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT0_NAME)) {
+ if (sma1307->dapm_aif_out0 != val) {
+ change = true;
+ sma1307->dapm_aif_out0 = val;
+ } else
+ change = false;
+ } else if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT1_NAME)) {
+ if (sma1307->dapm_aif_out1 != val) {
+ change = true;
+ sma1307->dapm_aif_out1 = val;
+ } else
+ change = false;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_sdo_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (long)sma1307->dapm_sdo_en;
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_sdo_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ if ((val < 0) || (val > 1)) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_sdo_en != val) {
+ change = true;
+ sma1307->dapm_sdo_en = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ return change;
+}
+
+static const struct snd_kcontrol_new sma1307_aif_in_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_IN_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_in_get,
+ .put = sma1307_dapm_aif_in_put,
+ .private_value = (unsigned long)&sma1307_aif_in_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_sdo_setting_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SDO Setting",
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_sdo_setting_get,
+ .put = sma1307_dapm_sdo_setting_put,
+ .private_value = (unsigned long)&sma1307_sdo_setting_enum
+};
+
+static const struct snd_kcontrol_new sma1307_aif_out0_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_OUT0_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_out_get,
+ .put = sma1307_dapm_aif_out_put,
+ .private_value = (unsigned long)&sma1307_aif_out_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_aif_out1_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_OUT1_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_out_get,
+ .put = sma1307_dapm_aif_out_put,
+ .private_value = (unsigned long)&sma1307_aif_out_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_sdo_control =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, 0, 1, 0,
+ sma1307_dapm_sdo_enable_get, sma1307_dapm_sdo_enable_put);
+
+static const struct snd_kcontrol_new sma1307_enable_control =
+ SOC_DAPM_SINGLE("Switch", SMA1307_00_SYSTEM_CTRL, 0, 1, 0);
+
+static const struct snd_kcontrol_new sma1307_binary_mode_control[] = {
+ SOC_ENUM_EXT("Binary Mode", sma1307_binary_mode_enum,
+ snd_soc_get_enum_double, sma1307_binary_mode_put),
+};
+
+static const struct snd_kcontrol_new sma1307_snd_controls[] = {
+ SOC_SINGLE_TLV(SMA1307_VOL_CTRL_NAME, SMA1307_0A_SPK_VOL,
+ 0, 167, 1, sma1307_spk_tlv),
+ SOC_ENUM_EXT(SMA1307_TDM_RX0_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_RX1_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_TX0_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_TX1_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_RESET_CTRL_NAME, sma1307_reset_enum,
+ snd_soc_get_enum_double, sma1307_reset_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_FORCE_MUTE_CTRL_NAME, 0,
+ sma1307_force_mute_get, sma1307_force_mute_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_OT1_SW_PROT_CTRL_NAME, 0,
+ sma1307_sw_ot1_prot_get, sma1307_sw_ot1_prot_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_CHECK_FAULT_STATUS_NAME, 0,
+ sma1307_check_fault_status_get,
+ sma1307_check_fault_status_put),
+ SOC_SINGLE_EXT(SMA1307_CHECK_FAULT_PERIOD_NAME, SND_SOC_NOPM, 0, 600, 0,
+ sma1307_check_fault_period_get,
+ sma1307_check_fault_period_put),
+};
+
+static const struct snd_soc_dapm_widget sma1307_dapm_widgets[] = {
+ /* platform domain */
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("SDO"),
+
+ /* path domain */
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_IN_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_in_source_control,
+ sma1307_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("SDO Setting", SND_SOC_NOPM, 0, 0,
+ &sma1307_sdo_setting_control,
+ sma1307_sdo_setting_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_OUT0_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_out0_source_control,
+ sma1307_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_OUT1_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_out1_source_control,
+ sma1307_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
+ &sma1307_sdo_control,
+ sma1307_sdo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sma1307_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 0,
+ &sma1307_enable_control),
+
+ /* stream domain */
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sma1307_audio_map[] = {
+ /* Playback */
+ { "AIF IN Source", "Mono", "AIF IN" },
+ { "AIF IN Source", "Left", "AIF IN" },
+ { "AIF IN Source", "Right", "AIF IN" },
+
+ { "SDO Enable", "Switch", "AIF IN" },
+
+ { "SDO Setting", "Data_One_48k", "SDO Enable" },
+ { "SDO Setting", "Data_Two_48k", "SDO Enable" },
+ { "SDO Setting", "Data_Two_24k", "SDO Enable" },
+ { "SDO Setting", "Clk_PLL", "SDO Enable" },
+ { "SDO Setting", "Clk_OSC", "SDO Enable" },
+
+ { "AIF OUT0 Source", "Disable", "SDO Setting" },
+ { "AIF OUT0 Source", "After_FmtC", "SDO Setting" },
+ { "AIF OUT0 Source", "After_Mixer", "SDO Setting" },
+ { "AIF OUT0 Source", "After_DSP", "SDO Setting" },
+ { "AIF OUT0 Source", "Vrms2_Avg", "SDO Setting" },
+ { "AIF OUT0 Source", "Battery", "SDO Setting" },
+ { "AIF OUT0 Source", "Temperature", "SDO Setting" },
+ { "AIF OUT0 Source", "After_Delay", "SDO Setting" },
+
+ { "AIF OUT1 Source", "Disable", "SDO Setting" },
+ { "AIF OUT1 Source", "After_FmtC", "SDO Setting" },
+ { "AIF OUT1 Source", "After_Mixer", "SDO Setting" },
+ { "AIF OUT1 Source", "After_DSP", "SDO Setting" },
+ { "AIF OUT1 Source", "Vrms2_Avg", "SDO Setting" },
+ { "AIF OUT1 Source", "Battery", "SDO Setting" },
+ { "AIF OUT1 Source", "Temperature", "SDO Setting" },
+ { "AIF OUT1 Source", "After_Delay", "SDO Setting" },
+
+ { "Entry", NULL, "AIF OUT0 Source" },
+ { "Entry", NULL, "AIF OUT1 Source" },
+ { "Entry", NULL, "AIF IN Source" },
+
+ { "AMP Power", NULL, "Entry" },
+
+ { "AMP Enable", "Switch", "AMP Power" },
+ { "SPK", NULL, "AMP Enable" },
+
+ /* Capture */
+ { "AIF OUT", NULL, "AMP Enable" },
+};
+
+static void sma1307_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ int i = 0;
+
+ dev_dbg(component->dev, "%s: BCLK = %dHz\n", __func__, bclk);
+
+ if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_MCLK) {
+ dev_warn(component->dev, "%s: MCLK is not supported\n",
+ __func__);
+ } else if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_BCLK) {
+ for (i = 0; i < sma1307->num_of_pll_matches; i++) {
+ if (sma1307->pll_matches[i].input_clk == bclk)
+ break;
+ }
+ if (i == sma1307->num_of_pll_matches) {
+ dev_warn(component->dev,
+ "%s: No matching value between pll table and SCK\n",
+ __func__);
+ return;
+ }
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_ON);
+ }
+
+ regmap_write(sma1307->regmap, SMA1307_8B_PLL_POST_N,
+ sma1307->pll_matches[i].post_n);
+ regmap_write(sma1307->regmap, SMA1307_8C_PLL_N,
+ sma1307->pll_matches[i].n);
+ regmap_write(sma1307->regmap, SMA1307_8D_PLL_A_SETTING,
+ sma1307->pll_matches[i].vco);
+ regmap_write(sma1307->regmap, SMA1307_8E_PLL_P_CP,
+ sma1307->pll_matches[i].p_cp);
+}
+
+static int sma1307_dai_hw_params_amp(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+
+ if (sma1307->format == SND_SOC_DAIFMT_DSP_A)
+ bclk = params_rate(params) * sma1307->frame_size;
+ else
+ bclk = params_rate(params) * params_physical_width(params)
+ * params_channels(params);
+
+ dev_dbg(component->dev,
+ "%s: rate = %d : bit size = %d : channel = %d\n",
+ __func__, params_rate(params), params_width(params),
+ params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_BCLK) {
+ if (sma1307->last_bclk != bclk) {
+ sma1307_setup_pll(component, bclk);
+ sma1307->last_bclk = bclk;
+ }
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ break;
+
+ case 96000:
+ dev_warn(component->dev,
+ "%s: %d rate not support SDO\n", __func__,
+ params_rate(params));
+ break;
+
+ default:
+ dev_err(component->dev, "%s: not support rate : %d\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ /* substream->stream is SNDRV_PCM_STREAM_CAPTURE */
+ } else {
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_32FS |
+ SMA1307_DATA_16BIT);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_64FS |
+ SMA1307_DATA_24BIT);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_64FS |
+ SMA1307_DATA_24BIT);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ }
+
+ switch (sma1307->format) {
+ case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_STANDARD_I2S);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_I2S_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK, SMA1307_LJ);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_LJ_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_RJ_16BIT);
+ break;
+ case 24:
+ case 32:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_RJ_24BIT);
+ break;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_STANDARD_I2S);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_TDM_FORMAT);
+ break;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_dai_set_sysclk_amp(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case SMA1307_EXTERNAL_CLOCK_19_2:
+ case SMA1307_EXTERNAL_CLOCK_24_576:
+ case SMA1307_PLL_CLKIN_MCLK:
+ case SMA1307_PLL_CLKIN_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid clk id: %d\n",
+ __func__, clk_id);
+ return -EINVAL;
+ }
+ sma1307->sys_clk_id = clk_id;
+
+ return 0;
+}
+
+static int sma1307_dai_set_fmt_amp(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(component->dev,
+ "%s: %s\n", __func__, "I2S/TDM Device mode");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_CONTROLLER_DEVICE_MASK,
+ SMA1307_DEVICE_MODE);
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev,
+ "%s: %s\n", __func__, "I2S/TDM Controller mode");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_CONTROLLER_DEVICE_MASK,
+ SMA1307_CONTROLLER_MODE);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Controller/Device : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ sma1307->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Audio Interface Format : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Invert BCLK + Normal Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_SCK_RISING_MASK,
+ SMA1307_SCK_RISING_EDGE);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Invert BCLK + Invert Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_LEFTPOL_MASK
+ | SMA1307_SCK_RISING_MASK,
+ SMA1307_HIGH_FIRST_CH
+ | SMA1307_SCK_RISING_EDGE);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Normal BCLK + Invert Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_LEFTPOL_MASK,
+ SMA1307_HIGH_FIRST_CH);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Normal BCLK + Normal Frame");
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Bit & Frameclock : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s: slots = %d, slot_width - %d\n",
+ __func__, slots, slot_width);
+
+ sma1307->frame_size = slot_width * slots;
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK, SMA1307_TDM_FORMAT);
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_TX_MODE_MASK,
+ SMA1307_TDM_TX_MONO);
+
+ switch (slot_width) {
+ case 16:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_DL_MASK,
+ SMA1307_TDM_DL_16);
+ break;
+ case 32:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_DL_MASK,
+ SMA1307_TDM_DL_32);
+ break;
+ default:
+ dev_err(component->dev, "%s: not support TDM %d slot_width\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 4:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_N_SLOT_MASK,
+ SMA1307_TDM_N_SLOT_4);
+ break;
+ case 8:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_N_SLOT_MASK,
+ SMA1307_TDM_N_SLOT_8);
+ break;
+ default:
+ dev_err(component->dev, "%s: not support TDM %d slots\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+
+ if (sma1307->tdm_slot0_rx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT0_RX_POS_MASK,
+ sma1307->tdm_slot0_rx << 3);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot0-rx %d set\n",
+ __func__, sma1307->tdm_slot0_rx);
+
+ if (sma1307->tdm_slot1_rx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT1_RX_POS_MASK,
+ sma1307->tdm_slot1_rx);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot1-rx %d set\n",
+ __func__, sma1307->tdm_slot1_rx);
+
+ if (sma1307->tdm_slot0_tx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT0_TX_POS_MASK,
+ sma1307->tdm_slot0_tx << 3);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot0-tx %d set\n",
+ __func__, sma1307->tdm_slot0_tx);
+
+ if (sma1307->tdm_slot1_tx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT1_TX_POS_MASK,
+ sma1307->tdm_slot1_tx);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot1-tx %d set\n",
+ __func__, sma1307->tdm_slot1_tx);
+
+ return 0;
+}
+
+static int sma1307_dai_mute_stream(struct snd_soc_dai *dai, int mute,
+ int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+ if (mute) {
+ dev_dbg(component->dev, "%s: %s\n", __func__, "MUTE");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK,
+ SMA1307_SPK_MUTE);
+ } else {
+ if (!sma1307->force_mute_status) {
+ dev_dbg(component->dev, "%s: %s\n", __func__,
+ "UNMUTE");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK,
+ SMA1307_SPK_UNMUTE);
+ } else {
+ dev_dbg(sma1307->dev, "%s: FORCE MUTE!!!\n", __func__);
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sma1307_dai_ops_amp = {
+ .hw_params = sma1307_dai_hw_params_amp,
+ .set_fmt = sma1307_dai_set_fmt_amp,
+ .set_sysclk = sma1307_dai_set_sysclk_amp,
+ .set_tdm_slot = sma1307_dai_set_tdm_slot,
+ .mute_stream = sma1307_dai_mute_stream,
+};
+
+#define SMA1307_RATES_PLAYBACK SNDRV_PCM_RATE_8000_96000
+#define SMA1307_RATES_CAPTURE SNDRV_PCM_RATE_8000_48000
+#define SMA1307_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver sma1307_dai[] = {
+ {
+ .name = "sma1307-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1307_RATES_PLAYBACK,
+ .formats = SMA1307_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1307_RATES_CAPTURE,
+ .formats = SMA1307_FORMATS,
+ },
+ .ops = &sma1307_dai_ops_amp,
+ },
+};
+
+static void sma1307_check_fault_worker(struct work_struct *work)
+{
+ struct sma1307_priv *sma1307 =
+ container_of(work, struct sma1307_priv, check_fault_work.work);
+ unsigned int status1_val, status2_val;
+ char *envp[3] = { NULL, NULL, NULL };
+
+ if (sma1307->tsdw_cnt)
+ regmap_read(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, &sma1307->cur_vol);
+ else
+ regmap_read(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, &sma1307->init_vol);
+
+ regmap_read(sma1307->regmap, SMA1307_FA_STATUS1, &status1_val);
+ regmap_read(sma1307->regmap, SMA1307_FB_STATUS2, &status2_val);
+
+ if (~status1_val & SMA1307_OT1_OK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OT1(Over Temperature Level 1)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1");
+ if (sma1307->sw_ot1_prot) {
+ /* Volume control (Current Volume -3dB) */
+ if ((sma1307->cur_vol + 6) <= 0xFA) {
+ sma1307->cur_vol += 6;
+ regmap_write(sma1307->regmap,
+ SMA1307_0A_SPK_VOL,
+ sma1307->cur_vol);
+ envp[1] = kasprintf(GFP_KERNEL,
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ }
+ }
+ sma1307->tsdw_cnt++;
+ } else if (sma1307->tsdw_cnt) {
+ regmap_write(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, sma1307->init_vol);
+ sma1307->tsdw_cnt = 0;
+ sma1307->cur_vol = sma1307->init_vol;
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR");
+ envp[1] = kasprintf(GFP_KERNEL,
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ }
+
+ if (~status1_val & SMA1307_OT2_OK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OT2(Over Temperature Level 2)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2");
+ }
+ if (status1_val & SMA1307_UVLO_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: UVLO(Under Voltage Lock Out)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO");
+ }
+ if (status1_val & SMA1307_OVP_BST_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OVP_BST(Over Voltage Protection)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST");
+ }
+ if (status2_val & SMA1307_OCP_SPK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OCP_SPK(Over Current Protect SPK)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK");
+ }
+ if (status2_val & SMA1307_OCP_BST_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OCP_BST(Over Current Protect Boost)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST");
+ }
+ if (status2_val & SMA1307_CLK_MON_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: CLK_FAULT(No clock input)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT");
+ }
+
+ if (envp[0] != NULL) {
+ if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp))
+ dev_err(sma1307->dev,
+ "%s: Error sending uevent\n", __func__);
+ kfree(envp[0]);
+ kfree(envp[1]);
+ }
+
+ if (sma1307->check_fault_status) {
+ if (sma1307->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ sma1307->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+}
+
+static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file)
+{
+ const struct firmware *fw;
+ int size, offset, num_mode;
+ int ret;
+
+ ret = request_firmware(&fw, file, sma1307->dev);
+
+ if (ret) {
+ dev_err(sma1307->dev, "%s: failed to read \"%s\": %pe\n",
+ __func__, setting_file, ERR_PTR(ret));
+ sma1307->set.status = false;
+ return;
+ } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) {
+ dev_err(sma1307->dev, "%s: Invalid file\n", __func__);
+ release_firmware(fw);
+ sma1307->set.status = false;
+ return;
+ }
+
+ int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL);
+ if (!data) {
+ release_firmware(fw);
+ sma1307->set.status = false;
+ return;
+ }
+ size = fw->size >> 2;
+ memcpy(data, fw->data, fw->size);
+
+ release_firmware(fw);
+
+ /* HEADER */
+ sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE;
+ sma1307->set.checksum = data[sma1307->set.header_size - 2];
+ sma1307->set.num_mode = data[sma1307->set.header_size - 1];
+ num_mode = sma1307->set.num_mode;
+ sma1307->set.header = devm_kmalloc_array(sma1307->dev,
+ sma1307->set.header_size,
+ sizeof(int),
+ GFP_KERNEL);
+ if (!sma1307->set.header) {
+ sma1307->set.status = false;
+ return;
+ }
+
+ memcpy(sma1307->set.header, data,
+ sma1307->set.header_size * sizeof(int));
+
+ if ((sma1307->set.checksum >> 8) != SMA1307_SETTING_CHECKSUM) {
+ dev_err(sma1307->dev, "%s: checksum failed \"%s\"\n",
+ __func__, setting_file);
+ sma1307->set.status = false;
+ return;
+ }
+
+ /* DEFAULT */
+ sma1307->set.def_size = SMA1307_SETTING_DEFAULT_SIZE;
+ sma1307->set.def
+ = devm_kzalloc(sma1307->dev,
+ sma1307->set.def_size * sizeof(int), GFP_KERNEL);
+ if (!sma1307->set.def) {
+ sma1307->set.status = false;
+ return;
+ }
+
+ memcpy(sma1307->set.def,
+ &data[sma1307->set.header_size],
+ sma1307->set.def_size * sizeof(int));
+
+ /* MODE */
+ offset = sma1307->set.header_size + sma1307->set.def_size;
+ sma1307->set.mode_size = DIV_ROUND_CLOSEST(size - offset, num_mode + 1);
+ for (int i = 0; i < num_mode; i++) {
+ sma1307->set.mode_set[i]
+ = devm_kzalloc(sma1307->dev,
+ sma1307->set.mode_size * 2 * sizeof(int),
+ GFP_KERNEL);
+ if (!sma1307->set.mode_set[i]) {
+ for (int j = 0; j < i; j++)
+ kfree(sma1307->set.mode_set[j]);
+ sma1307->set.status = false;
+ return;
+ }
+
+ for (int j = 0; j < sma1307->set.mode_size; j++) {
+ sma1307->set.mode_set[i][2 * j]
+ = data[offset + ((num_mode + 1) * j)];
+ sma1307->set.mode_set[i][2 * j + 1]
+ = data[offset + ((num_mode + 1) * j + i + 1)];
+ }
+ }
+
+ sma1307->set.status = true;
+
+}
+
+static void sma1307_reset(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int status = 0;
+
+ regmap_read(sma1307->regmap, SMA1307_FF_DEVICE_INDEX, &status);
+
+ sma1307->rev_num = status & SMA1307_REV_NUM_STATUS;
+ dev_dbg(component->dev, "%s: SMA1307 Revision %d\n",
+ __func__, sma1307->rev_num);
+ regmap_read(sma1307->regmap, SMA1307_99_OTP_TRM2, &sma1307->otp_trm2);
+ regmap_read(sma1307->regmap, SMA1307_9A_OTP_TRM3, &sma1307->otp_trm3);
+
+ if ((sma1307->otp_trm2 & SMA1307_OTP_STAT_MASK) != SMA1307_OTP_STAT_1)
+ dev_warn(component->dev, "%s: SMA1307 OTP Status Fail\n",
+ __func__);
+
+ /* Register Initial Value Setting */
+ sma1307_setting_loaded(sma1307, setting_file);
+ if (sma1307->set.status)
+ sma1307_set_binary(component);
+ else
+ sma1307_set_default(component);
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_93_INT_CTRL,
+ SMA1307_DIS_INT_MASK, SMA1307_HIGH_Z_INT);
+ regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->init_vol);
+}
+
+static void sma1307_set_binary(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int i = 0, mode = 0;
+
+ for (i = 0; i < (sma1307->set.def_size); i++) {
+ if (sma1307_writeable_register(sma1307->dev, i)
+ && ((i < SMA1307_97_OTP_TRM0)
+ || (i > SMA1307_9A_OTP_TRM3))) {
+ regmap_write(sma1307->regmap, i, sma1307->set.def[i]);
+
+ }
+ }
+ for (i = 0; i < (sma1307->set.mode_size); i++) {
+ if (sma1307_writeable_register(sma1307->dev, i)
+ && ((i < SMA1307_97_OTP_TRM0)
+ || (i > SMA1307_9A_OTP_TRM3))) {
+ mode = sma1307->binary_mode;
+ regmap_write(sma1307->regmap,
+ sma1307->set.mode_set[mode][2 * i],
+ sma1307->set.mode_set[mode][2 * i +
+ 1]);
+ }
+ }
+}
+
+static void sma1307_set_default(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int i = 0;
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1307_reg_def); i++)
+ regmap_write(sma1307->regmap,
+ sma1307_reg_def[i].reg,
+ sma1307_reg_def[i].def);
+
+ if (!strcmp(sma1307->name, DEVICE_NAME_SMA1307AQ))
+ sma1307->data->init(sma1307->regmap);
+}
+
+static int sma1307_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_sync(dapm);
+
+ sma1307_amp_component = component;
+
+ snd_soc_add_component_controls(component, sma1307_binary_mode_control,
+ ARRAY_SIZE(sma1307_binary_mode_control));
+ sma1307_reset(component);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver sma1307_component = {
+ .probe = sma1307_probe,
+ .controls = sma1307_snd_controls,
+ .num_controls = ARRAY_SIZE(sma1307_snd_controls),
+ .dapm_widgets = sma1307_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sma1307_dapm_widgets),
+ .dapm_routes = sma1307_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sma1307_audio_map),
+};
+
+static const struct regmap_config sma_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SMA1307_FF_DEVICE_INDEX,
+ .readable_reg = sma1307_readable_register,
+ .writeable_reg = sma1307_writeable_register,
+ .volatile_reg = sma1307_volatile_register,
+
+ .reg_defaults = sma1307_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(sma1307_reg_def),
+};
+
+static void sma1307aq_init(struct regmap *regmap)
+{
+ /* Guidelines for driving 4ohm load */
+ /* Brown Out Protection */
+ regmap_write(regmap, SMA1307_02_BROWN_OUT_PROT1, 0x62);
+ regmap_write(regmap, SMA1307_03_BROWN_OUT_PROT2, 0x5D);
+ regmap_write(regmap, SMA1307_04_BROWN_OUT_PROT3, 0x57);
+ regmap_write(regmap, SMA1307_05_BROWN_OUT_PROT8, 0x54);
+ regmap_write(regmap, SMA1307_06_BROWN_OUT_PROT9, 0x51);
+ regmap_write(regmap,
+ SMA1307_07_BROWN_OUT_PROT10, 0x4D);
+ regmap_write(regmap,
+ SMA1307_08_BROWN_OUT_PROT11, 0x4B);
+ regmap_write(regmap, SMA1307_27_BROWN_OUT_PROT4, 0x3C);
+ regmap_write(regmap, SMA1307_28_BROWN_OUT_PROT5, 0x5B);
+ regmap_write(regmap,
+ SMA1307_29_BROWN_OUT_PROT12, 0x78);
+ regmap_write(regmap,
+ SMA1307_2A_BROWN_OUT_PROT13, 0x96);
+ regmap_write(regmap,
+ SMA1307_2B_BROWN_OUT_PROT14, 0xB4);
+ regmap_write(regmap,
+ SMA1307_2C_BROWN_OUT_PROT15, 0xD3);
+ /* FDPEC Gain */
+ regmap_write(regmap, SMA1307_35_FDPEC_CTRL0, 0x16);
+ /* FLT Vdd */
+ regmap_write(regmap, SMA1307_92_FDPEC_CTRL1, 0xA0);
+ /* Boost Max */
+ regmap_write(regmap, SMA1307_AB_BOOST_CTRL4, 0x0F);
+}
+
+static const struct sma1307_data sma1307aq_data = {
+ .name = DEVICE_NAME_SMA1307AQ,
+ .init = sma1307aq_init,
+};
+
+static int sma1307_i2c_probe(struct i2c_client *client)
+{
+ struct sma1307_priv *sma1307;
+ const struct sma1307_data *data;
+ int ret = 0;
+ unsigned int device_info;
+
+ sma1307 = devm_kzalloc(&client->dev,
+ sizeof(*sma1307), GFP_KERNEL);
+ if (!sma1307)
+ return -ENOMEM;
+
+ sma1307->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
+ if (IS_ERR(sma1307->regmap)) {
+ return dev_err_probe(&client->dev, PTR_ERR(sma1307->regmap),
+ "%s: failed to allocate register map\n", __func__);
+ }
+
+ data = device_get_match_data(&client->dev);
+ if (!data)
+ return -ENODEV;
+
+ sma1307->data = data;
+
+ /* set initial value as normal AMP IC status */
+ sma1307->name = client->name;
+ sma1307->format = SND_SOC_DAIFMT_I2S;
+ sma1307->sys_clk_id = SMA1307_PLL_CLKIN_BCLK;
+ sma1307->num_of_pll_matches = ARRAY_SIZE(sma1307_pll_matches);
+
+ sma1307->check_fault_period = CHECK_PERIOD_TIME;
+ sma1307->check_fault_status = true;
+ sma1307->init_vol = 0x32;
+ sma1307->cur_vol = sma1307->init_vol;
+ sma1307->sw_ot1_prot = true;
+
+ mutex_init(&sma1307->default_lock);
+
+ INIT_DELAYED_WORK(&sma1307->check_fault_work,
+ sma1307_check_fault_worker);
+
+ sma1307->dev = &client->dev;
+ sma1307->kobj = &client->dev.kobj;
+
+ i2c_set_clientdata(client, sma1307);
+
+ sma1307->pll_matches = sma1307_pll_matches;
+
+ regmap_read(sma1307->regmap,
+ SMA1307_FF_DEVICE_INDEX, &device_info);
+
+ if ((device_info & 0xF8) != SMA1307_DEVICE_ID) {
+ dev_err(&client->dev,
+ "%s: device initialization error (0x%02X)",
+ __func__, device_info);
+ return -ENODEV;
+ }
+ dev_dbg(&client->dev, "%s: chip version 0x%02X\n",
+ __func__, device_info);
+
+ i2c_set_clientdata(client, sma1307);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &sma1307_component, sma1307_dai,
+ 1);
+
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to register component\n",
+ __func__);
+
+ return ret;
+ }
+
+ return ret;
+}
+
+static void sma1307_i2c_remove(struct i2c_client *client)
+{
+ struct sma1307_priv *sma1307 =
+ (struct sma1307_priv *)i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&sma1307->check_fault_work);
+}
+
+static const struct i2c_device_id sma1307_i2c_id[] = {
+ { "sma1307a" },
+ { "sma1307aq" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, sma1307_i2c_id);
+
+static const struct of_device_id sma1307_of_match[] = {
+ {
+ .compatible = "irondevice,sma1307a",
+ },
+ {
+ .compatible = "irondevice,sma1307aq",
+ .data = &sma1307aq_data //AEC-Q100 Qualificated
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, sma1307_of_match);
+
+static struct i2c_driver sma1307_i2c_driver = {
+ .driver = {
+ .name = "sma1307",
+ .of_match_table = sma1307_of_match,
+ },
+ .probe = sma1307_i2c_probe,
+ .remove = sma1307_i2c_remove,
+ .id_table = sma1307_i2c_id,
+};
+
+module_i2c_driver(sma1307_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC SMA1307 driver");
+MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("KS Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sma1307.h b/sound/soc/codecs/sma1307.h
new file mode 100644
index 000000000000..44aab52a32f9
--- /dev/null
+++ b/sound/soc/codecs/sma1307.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * sma1307.h -- sma1307 ALSA SoC Audio driver
+ *
+ * Copyright 2024 Iron Device Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SMA1307_H
+#define _SMA1307_H
+
+#include <sound/soc.h>
+
+enum sma1307_fault {
+ SMA1307_FAULT_OT1,
+ SMA1307_FAULT_OT2,
+ SMA1307_FAULT_UVLO,
+ SMA1307_FAULT_OVP_BST,
+ SMA1307_FAULT_OCP_SPK,
+ SMA1307_FAULT_OCP_BST,
+ SMA1307_FAULT_CLK
+};
+
+enum sma1307_mode {
+ SMA1307_MONO_MODE,
+ SMA1307_LEFT_MODE,
+ SMA1307_RIGHT_MODE,
+};
+
+enum sma1307_sdo_mode {
+ SMA1307_OUT_DATA_ONE_48K,
+ SMA1307_OUT_DATA_TWO_48K,
+ SMA1307_OUT_DATA_TWO_24K,
+ SMA1307_OUT_CLK_PLL,
+ SMA1307_OUT_CLK_OSC
+};
+
+enum sma1307_sdo_source {
+ SMA1307_OUT_DISABLE,
+ SMA1307_OUT_FORMAT_C,
+ SMA1307_OUT_MIXER_OUT,
+ SMA1307_OUT_AFTER_DSP,
+ SMA1307_OUT_VRMS2_AVG,
+ SMA1307_OUT_BATTERY,
+ SMA1307_OUT_TEMP,
+ SMA1307_OUT_AFTER_DELAY
+};
+
+struct sma1307_setting_file {
+ bool status;
+ char *header;
+ int *def;
+ int *mode_set[5];
+ int checksum;
+ int num_mode;
+ size_t header_size;
+ size_t def_size;
+ size_t mode_size;
+};
+
+#define SMA1307_I2C_ADDR_00 0x1e
+#define SMA1307_I2C_ADDR_01 0x3e
+#define SMA1307_I2C_ADDR_10 0x5e
+#define SMA1307_I2C_ADDR_11 0x7e
+
+#define DEVICE_NAME_SMA1307A "sma1307a"
+#define DEVICE_NAME_SMA1307AQ "sma1307aq"
+
+#define SMA1307_EXTERNAL_CLOCK_19_2 0x00
+#define SMA1307_EXTERNAL_CLOCK_24_576 0x01
+#define SMA1307_PLL_CLKIN_MCLK 0x02
+#define SMA1307_PLL_CLKIN_BCLK 0x03
+
+#define SMA1307_OFFSET_DEFAULT_MODE 0x00
+#define SMA1307_OFFSET_BURNING_MODE 0x01
+
+#define SMA1307_SETTING_HEADER_SIZE 0x08
+#define SMA1307_SETTING_DEFAULT_SIZE 0xC0
+
+#define SMA1307_DEFAULT_SET 0x00
+#define SMA1307_BINARY_FILE_SET 0x01
+
+/* Controls Name */
+#define SMA1307_REG_CTRL_NAME "Register Byte Control"
+#define SMA1307_VOL_CTRL_NAME "Speaker Volume"
+#define SMA1307_FORCE_MUTE_CTRL_NAME "Force Mute Switch"
+#define SMA1307_TDM_RX0_POS_NAME "TDM RX Slot0 Position"
+#define SMA1307_TDM_RX1_POS_NAME "TDM RX Slot1 Position"
+#define SMA1307_TDM_TX0_POS_NAME "TDM TX Slot0 Position"
+#define SMA1307_TDM_TX1_POS_NAME "TDM TX Slot1 Position"
+#define SMA1307_OT1_SW_PROT_CTRL_NAME "OT1 SW Protection Switch"
+#define SMA1307_RESET_CTRL_NAME "Reset Switch"
+#define SMA1307_CHECK_FAULT_STATUS_NAME "Check Fault Status"
+#define SMA1307_CHECK_FAULT_PERIOD_NAME "Check Fault Period"
+
+/* DAPM Name */
+#define SMA1307_AIF_IN_NAME "AIF IN Source"
+#define SMA1307_AIF_OUT0_NAME "AIF OUT0 Source"
+#define SMA1307_AIF_OUT1_NAME "AIF OUT1 Source"
+
+/*
+ * SMA1307 Register Definition
+ */
+
+/* SMA1307 Register Addresses */
+#define SMA1307_00_SYSTEM_CTRL 0x00
+#define SMA1307_01_INPUT_CTRL1 0x01
+#define SMA1307_02_BROWN_OUT_PROT1 0x02
+#define SMA1307_03_BROWN_OUT_PROT2 0x03
+#define SMA1307_04_BROWN_OUT_PROT3 0x04
+#define SMA1307_05_BROWN_OUT_PROT8 0x05
+#define SMA1307_06_BROWN_OUT_PROT9 0x06
+#define SMA1307_07_BROWN_OUT_PROT10 0x07
+#define SMA1307_08_BROWN_OUT_PROT11 0x08
+#define SMA1307_09_OUTPUT_CTRL 0x09
+#define SMA1307_0A_SPK_VOL 0x0A
+#define SMA1307_0B_BST_TEST 0x0B
+#define SMA1307_0C_BOOST_CTRL8 0x0C
+#define SMA1307_0D_SPK_TEST 0x0D
+#define SMA1307_0E_MUTE_VOL_CTRL 0x0E
+#define SMA1307_0F_VBAT_TEMP_SENSING 0x0F
+
+#define SMA1307_10_SYSTEM_CTRL1 0x10
+#define SMA1307_11_SYSTEM_CTRL2 0x11
+#define SMA1307_12_SYSTEM_CTRL3 0x12
+#define SMA1307_13_DELAY 0x13
+#define SMA1307_14_MODULATOR 0x14
+#define SMA1307_15_BASS_SPK1 0x15
+#define SMA1307_16_BASS_SPK2 0x16
+#define SMA1307_17_BASS_SPK3 0x17
+#define SMA1307_18_BASS_SPK4 0x18
+#define SMA1307_19_BASS_SPK5 0x19
+#define SMA1307_1A_BASS_SPK6 0x1A
+#define SMA1307_1B_BASS_SPK7 0x1B
+#define SMA1307_1C_BROWN_OUT_PROT20 0x1C
+#define SMA1307_1D_BROWN_OUT_PROT0 0x1D
+#define SMA1307_1E_TONE_GENERATOR 0x1E
+#define SMA1307_1F_TONE_FINE_VOLUME 0x1F
+
+#define SMA1307_22_COMP_HYS_SEL 0x22
+#define SMA1307_23_COMPLIM1 0x23
+#define SMA1307_24_COMPLIM2 0x24
+#define SMA1307_25_COMPLIM3 0x25
+#define SMA1307_26_COMPLIM4 0x26
+#define SMA1307_27_BROWN_OUT_PROT4 0x27
+#define SMA1307_28_BROWN_OUT_PROT5 0x28
+#define SMA1307_29_BROWN_OUT_PROT12 0x29
+#define SMA1307_2A_BROWN_OUT_PROT13 0x2A
+#define SMA1307_2B_BROWN_OUT_PROT14 0x2B
+#define SMA1307_2C_BROWN_OUT_PROT15 0x2C
+#define SMA1307_2D_BROWN_OUT_PROT6 0x2D
+#define SMA1307_2E_BROWN_OUT_PROT7 0x2E
+#define SMA1307_2F_BROWN_OUT_PROT16 0x2F
+
+#define SMA1307_30_BROWN_OUT_PROT17 0x30
+#define SMA1307_31_BROWN_OUT_PROT18 0x31
+#define SMA1307_32_BROWN_OUT_PROT19 0x32
+#define SMA1307_34_OCP_SPK 0x34
+#define SMA1307_35_FDPEC_CTRL0 0x35
+#define SMA1307_36_PROTECTION 0x36
+#define SMA1307_37_SLOPECTRL 0x37
+#define SMA1307_38_POWER_METER 0x38
+#define SMA1307_39_PMT_NZ_VAL 0x39
+#define SMA1307_3B_TEST1 0x3B
+#define SMA1307_3C_TEST2 0x3C
+#define SMA1307_3D_TEST3 0x3D
+#define SMA1307_3E_IDLE_MODE_CTRL 0x3E
+#define SMA1307_3F_ATEST2 0x3F
+#define SMA1307_8B_PLL_POST_N 0x8B
+#define SMA1307_8C_PLL_N 0x8C
+#define SMA1307_8D_PLL_A_SETTING 0x8D
+#define SMA1307_8E_PLL_P_CP 0x8E
+#define SMA1307_8F_ANALOG_TEST 0x8F
+
+#define SMA1307_90_CRESTLIM1 0x90
+#define SMA1307_91_CRESTLIM2 0x91
+#define SMA1307_92_FDPEC_CTRL1 0x92
+#define SMA1307_93_INT_CTRL 0x93
+#define SMA1307_94_BOOST_CTRL9 0x94
+#define SMA1307_95_BOOST_CTRL10 0x95
+#define SMA1307_96_BOOST_CTRL11 0x96
+#define SMA1307_97_OTP_TRM0 0x97
+#define SMA1307_98_OTP_TRM1 0x98
+#define SMA1307_99_OTP_TRM2 0x99
+#define SMA1307_9A_OTP_TRM3 0x9A
+
+#define SMA1307_A0_PAD_CTRL0 0xA0
+#define SMA1307_A1_PAD_CTRL1 0xA1
+#define SMA1307_A2_TOP_MAN1 0xA2
+#define SMA1307_A3_TOP_MAN2 0xA3
+#define SMA1307_A4_TOP_MAN3 0xA4
+#define SMA1307_A5_TDM1 0xA5
+#define SMA1307_A6_TDM2 0xA6
+#define SMA1307_A7_CLK_MON 0xA7
+#define SMA1307_A8_BOOST_CTRL1 0xA8
+#define SMA1307_A9_BOOST_CTRL2 0xA9
+#define SMA1307_AA_BOOST_CTRL3 0xAA
+#define SMA1307_AB_BOOST_CTRL4 0xAB
+#define SMA1307_AC_BOOST_CTRL5 0xAC
+#define SMA1307_AD_BOOST_CTRL6 0xAD
+#define SMA1307_AE_BOOST_CTRL7 0xAE
+#define SMA1307_AF_LPF 0xAF
+
+#define SMA1307_B0_RMS_TC1 0xB0
+#define SMA1307_B1_RMS_TC2 0xB1
+#define SMA1307_B2_AVG_TC1 0xB2
+#define SMA1307_B3_AVG_TC2 0xB3
+#define SMA1307_B4_PRVALUE1 0xB4
+#define SMA1307_B5_PRVALUE2 0xB5
+#define SMA1307_B8_SPK_NG_CTRL1 0xB8
+#define SMA1307_B9_SPK_NG_CTRL2 0xB9
+#define SMA1307_BA_DGC1 0xBA
+#define SMA1307_BB_DGC2 0xBB
+#define SMA1307_BC_DGC3 0xBC
+#define SMA1307_BD_MCBS_CTRL1 0xBD
+#define SMA1307_BE_MCBS_CTRL2 0xBE
+
+/* Status Register Read Only */
+#define SMA1307_F5_READY_FOR_V_SAR 0xF5
+#define SMA1307_F7_READY_FOR_T_SAR 0xF7
+#define SMA1307_F8_STATUS_T1 0xF8
+#define SMA1307_F9_STATUS_T2 0xF9
+#define SMA1307_FA_STATUS1 0xFA
+#define SMA1307_FB_STATUS2 0xFB
+#define SMA1307_FC_STATUS3 0xFC
+#define SMA1307_FD_STATUS4 0xFD
+#define SMA1307_FE_STATUS5 0xFE
+#define SMA1307_FF_DEVICE_INDEX 0xFF
+
+/* SMA1307 Registers Bit Fields */
+/* Power On/Off */
+#define SMA1307_POWER_MASK BIT(0)
+#define SMA1307_POWER_OFF 0
+#define SMA1307_POWER_ON BIT(0)
+
+/* Reset */
+#define SMA1307_RESET_MASK BIT(1)
+#define SMA1307_RESET_ON BIT(1)
+
+/* Left Polarity */
+#define SMA1307_LEFTPOL_MASK BIT(3)
+#define SMA1307_LOW_FIRST_CH 0
+#define SMA1307_HIGH_FIRST_CH BIT(3)
+
+/* SCK Falling/Rising */
+#define SMA1307_SCK_RISING_MASK BIT(2)
+#define SMA1307_SCK_FALLING_EDGE 0
+#define SMA1307_SCK_RISING_EDGE BIT(2)
+
+/* SPK Mute */
+#define SMA1307_SPK_MUTE_MASK BIT(0)
+#define SMA1307_SPK_UNMUTE 0
+#define SMA1307_SPK_MUTE BIT(0)
+
+/* SPK Mode */
+#define SMA1307_SPK_MODE_MASK (BIT(2)|BIT(3)|BIT(4))
+#define SMA1307_SPK_OFF 0
+#define SMA1307_SPK_MONO BIT(2)
+#define SMA1307_SPK_STEREO BIT(4)
+
+/* Mono Mix */
+#define SMA1307_MONOMIX_MASK BIT(0)
+#define SMA1307_MONOMIX_OFF 0
+#define SMA1307_MONOMIX_ON BIT(0)
+
+/* LR Data Swap */
+#define SMA1307_LR_DATA_SW_MASK BIT(4)
+#define SMA1307_LR_DATA_SW_NORMAL 0
+#define SMA1307_LR_DATA_SW_SWAP BIT(4)
+
+/* PLL On/Off */
+#define SMA1307_PLL_MASK BIT(6)
+#define SMA1307_PLL_ON 0
+#define SMA1307_PLL_OFF BIT(6)
+
+/* Input Format */
+#define SMA1307_I2S_MODE_MASK (BIT(4)|BIT(5)|BIT(6))
+#define SMA1307_STANDARD_I2S 0
+#define SMA1307_LJ BIT(4)
+#define SMA1307_RJ_16BIT BIT(6)
+#define SMA1307_RJ_18BIT (BIT(4)|BIT(6))
+#define SMA1307_RJ_20BIT (BIT(5)|BIT(6))
+#define SMA1307_RJ_24BIT (BIT(4)|BIT(5)|BIT(6))
+
+/* Controller / Device Setting */
+#define SMA1307_CONTROLLER_DEVICE_MASK BIT(7)
+#define SMA1307_DEVICE_MODE 0
+#define SMA1307_CONTROLLER_MODE BIT(7)
+
+/* Port Config */
+#define SMA1307_PORT_CONFIG_MASK (BIT(6)|BIT(7))
+#define SMA1307_INPUT_PORT_ONLY 0
+#define SMA1307_OUTPUT_PORT_ENABLE BIT(7)
+
+/* SDO Output */
+#define SMA1307_SDO_OUTPUT_MASK BIT(3)
+#define SMA1307_LOGIC_OUTPUT 0
+#define SMA1307_HIGH_Z_OUTPUT BIT(3)
+
+#define SMA1307_DATA_CLK_SEL_MASK (BIT(6)|BIT(7))
+#define SMA1307_SDO_DATA 0
+#define SMA1307_SDO_CLK_PLL BIT(6)
+#define SMA1307_SDO_CLK_OSC (BIT(6)|BIT(7))
+
+/* SDO Output2 */
+#define SMA1307_SDO_OUTPUT2_MASK BIT(0)
+#define SMA1307_ONE_SDO_PER_CH 0
+#define SMA1307_TWO_SDO_PER_CH BIT(0)
+
+/* SDO Output3 */
+#define SMA1307_SDO_OUTPUT3_MASK BIT(2)
+#define SMA1307_SDO_OUTPUT3_DIS 0
+#define SMA1307_TWO_SDO_PER_CH_24K BIT(2)
+
+/* SDO OUT1 Select*/
+#define SMA1307_SDO_OUT1_SEL_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_SDO1_DISABLE 0
+#define SMA1307_SDO1_FORMAT_C BIT(3)
+#define SMA1307_SDO1_MONO_MIX BIT(4)
+#define SMA1307_SDO1_AFTER_DSP (BIT(3)|BIT(4))
+#define SMA1307_SDO1_VRMS2_AVG BIT(5)
+#define SMA1307_SDO1_VBAT_MON (BIT(3)|BIT(5))
+#define SMA1307_SDO1_TEMP_MON (BIT(4)|BIT(5))
+#define SMA1307_SDO1_AFTER_DELAY (BIT(3)|BIT(4)|BIT(5))
+
+/* SDO OUT0 Select*/
+#define SMA1307_SDO_OUT0_SEL_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_SDO0_DISABLE 0
+#define SMA1307_SDO0_FORMAT_C BIT(0)
+#define SMA1307_SDO0_MONO_MIX BIT(1)
+#define SMA1307_SDO0_AFTER_DSP (BIT(0)|BIT(1))
+#define SMA1307_SDO0_VRMS2_AVG BIT(2)
+#define SMA1307_SDO0_VBAT_MON (BIT(0)|BIT(2))
+#define SMA1307_SDO0_TEMP_MON (BIT(1)|BIT(2))
+#define SMA1307_SDO0_AFTER_DELAY (BIT(0)|BIT(1)|BIT(2))
+
+/* INTERRUPT Operation */
+#define SMA1307_SEL_INT_MASK BIT(2)
+#define SMA1307_INT_CLEAR_AUTO 0
+#define SMA1307_INT_CLEAR_MANUAL BIT(2)
+
+/* INTERRUPT CLEAR */
+#define SMA1307_CLR_INT_MASK BIT(1)
+#define SMA1307_INT_READY 0
+#define SMA1307_INT_CLEAR BIT(1)
+
+/* INTERRUPT Disable */
+#define SMA1307_DIS_INT_MASK BIT(0)
+#define SMA1307_NORMAL_INT 0
+#define SMA1307_HIGH_Z_INT BIT(0)
+
+/* Interface Control */
+#define SMA1307_INTERFACE_MASK (BIT(5)|BIT(6)|BIT(7))
+#define SMA1307_LJ_FORMAT BIT(5)
+#define SMA1307_I2S_FORMAT (BIT(5)|BIT(6))
+#define SMA1307_TDM_FORMAT BIT(7)
+
+#define SMA1307_SCK_RATE_MASK (BIT(3)|BIT(4))
+#define SMA1307_SCK_64FS 0
+#define SMA1307_SCK_32FS BIT(4)
+
+#define SMA1307_DATA_WIDTH_MASK (BIT(1)|BIT(2))
+#define SMA1307_DATA_24BIT 0
+#define SMA1307_DATA_16BIT (BIT(1)|BIT(2))
+
+#define SMA1307_TDM_TX_MODE_MASK BIT(6)
+#define SMA1307_TDM_TX_MONO 0
+#define SMA1307_TDM_TX_STEREO BIT(6)
+
+#define SMA1307_TDM_SLOT0_RX_POS_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_0 0
+#define SMA1307_TDM_SLOT0_RX_POS_1 BIT(3)
+#define SMA1307_TDM_SLOT0_RX_POS_2 BIT(4)
+#define SMA1307_TDM_SLOT0_RX_POS_3 (BIT(3)|BIT(4))
+#define SMA1307_TDM_SLOT0_RX_POS_4 BIT(5)
+#define SMA1307_TDM_SLOT0_RX_POS_5 (BIT(3)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_6 (BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_7 (BIT(3)|BIT(4)|BIT(5))
+
+#define SMA1307_TDM_SLOT1_RX_POS_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_0 0
+#define SMA1307_TDM_SLOT1_RX_POS_1 BIT(0)
+#define SMA1307_TDM_SLOT1_RX_POS_2 BIT(1)
+#define SMA1307_TDM_SLOT1_RX_POS_3 (BIT(0)|BIT(1))
+#define SMA1307_TDM_SLOT1_RX_POS_4 BIT(2)
+#define SMA1307_TDM_SLOT1_RX_POS_5 (BIT(0)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_6 (BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_7 (BIT(0)|BIT(1)|BIT(2))
+
+/* TDM2 FORMAT : 0xA6 */
+#define SMA1307_TDM_DL_MASK BIT(7)
+#define SMA1307_TDM_DL_16 0
+#define SMA1307_TDM_DL_32 BIT(7)
+
+#define SMA1307_TDM_N_SLOT_MASK BIT(6)
+#define SMA1307_TDM_N_SLOT_4 0
+#define SMA1307_TDM_N_SLOT_8 BIT(6)
+
+#define SMA1307_TDM_SLOT0_TX_POS_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_0 0
+#define SMA1307_TDM_SLOT0_TX_POS_1 BIT(3)
+#define SMA1307_TDM_SLOT0_TX_POS_2 BIT(4)
+#define SMA1307_TDM_SLOT0_TX_POS_3 (BIT(3)|BIT(4))
+#define SMA1307_TDM_SLOT0_TX_POS_4 BIT(5)
+#define SMA1307_TDM_SLOT0_TX_POS_5 (BIT(3)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_6 (BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_7 (BIT(3)|BIT(4)|BIT(5))
+
+#define SMA1307_TDM_SLOT1_TX_POS_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_0 0
+#define SMA1307_TDM_SLOT1_TX_POS_1 BIT(0)
+#define SMA1307_TDM_SLOT1_TX_POS_2 BIT(1)
+#define SMA1307_TDM_SLOT1_TX_POS_3 (BIT(0)|BIT(1))
+#define SMA1307_TDM_SLOT1_TX_POS_4 BIT(2)
+#define SMA1307_TDM_SLOT1_TX_POS_5 (BIT(0)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_6 (BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_7 (BIT(0)|BIT(1)|BIT(2))
+
+/* OTP STATUS */
+#define SMA1307_OTP_STAT_MASK BIT(6)
+#define SMA1307_OTP_STAT_0 0
+#define SMA1307_OTP_STAT_1 BIT(6)
+
+/* STATUS */
+#define SMA1307_OT1_OK_STATUS BIT(7)
+#define SMA1307_OT2_OK_STATUS BIT(6)
+#define SMA1307_UVLO_STATUS BIT(5)
+#define SMA1307_OVP_BST_STATUS BIT(4)
+#define SMA1307_POWER_FLAG BIT(3)
+
+#define SMA1307_SCAN_CHK BIT(7)
+#define SMA1307_OCP_SPK_STATUS BIT(5)
+#define SMA1307_OCP_BST_STATUS BIT(4)
+#define SMA1307_BOP_STATE (BIT(1)|BIT(2)|BIT(3))
+#define SMA1307_CLK_MON_STATUS BIT(0)
+
+#define SMA1307_DEVICE_ID (BIT(3)|BIT(4))
+#define SMA1307_REV_NUM_STATUS (BIT(0)|BIT(1))
+#define SMA1307_REV_NUM_REV0 0
+#define SMA1307_REV_NUM_REV1 BIT(0)
+
+#endif
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
deleted file mode 100644
index 3a7de0159f24..000000000000
--- a/sound/soc/codecs/sn95031.c
+++ /dev/null
@@ -1,934 +0,0 @@
-/*
- * sn95031.c - TI sn95031 Codec driver
- *
- * Copyright (C) 2010 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#include <asm/intel_scu_ipc.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/jack.h>
-#include "sn95031.h"
-
-#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
-#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-
-/* adc helper functions */
-
-/* enables mic bias voltage */
-static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
-{
- snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
- snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
-}
-
-/* Enable/Disable the ADC depending on the argument */
-static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
-{
- int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
-
- if (val) {
- /* Enable and start the ADC */
- value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
- value &= (~SN95031_ADC_NO_LOOP);
- } else {
- /* Just stop the ADC */
- value &= (~SN95031_ADC_START);
- }
- snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
-}
-
-/*
- * finds an empty channel for conversion
- * If the ADC is not enabled then start using 0th channel
- * itself. Otherwise find an empty channel by looking for a
- * channel in which the stopbit is set to 1. returns the index
- * of the first free channel if succeeds or an error code.
- *
- * Context: can sleep
- *
- */
-static int find_free_channel(struct snd_soc_codec *sn95031_codec)
-{
- int i, value;
-
- /* check whether ADC is enabled */
- value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
-
- if ((value & SN95031_ADC_ENBL) == 0)
- return 0;
-
- /* ADC is already enabled; Looking for an empty channel */
- for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
- value = snd_soc_read(sn95031_codec,
- SN95031_ADC_CHNL_START_ADDR + i);
- if (value & SN95031_STOPBIT_MASK)
- break;
- }
- return (i == SN95031_ADC_CHANLS_MAX) ? (-EINVAL) : i;
-}
-
-/* Initialize the ADC for reading micbias values. Can sleep. */
-static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
-{
- int base_addr, chnl_addr;
- int value;
- int channel_index;
-
- /* Index of the first channel in which the stop bit is set */
- channel_index = find_free_channel(sn95031_codec);
- if (channel_index < 0) {
- pr_err("No free ADC channels");
- return channel_index;
- }
-
- base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;
-
- if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) {
- /* Reset stop bit for channels other than 0 and 12 */
- value = snd_soc_read(sn95031_codec, base_addr);
- /* Set the stop bit to zero */
- snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
- /* Index of the first free channel */
- base_addr++;
- channel_index++;
- }
-
- /* Since this is the last channel, set the stop bit
- to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
- snd_soc_write(sn95031_codec, base_addr,
- SN95031_AUDIO_DETECT_CODE | 0x10);
-
- chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
- pr_debug("mid_initialize : %x", chnl_addr);
- configure_adc(sn95031_codec, 1);
- return chnl_addr;
-}
-
-
-/* reads the ADC registers and gets the mic bias value in mV. */
-static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
-{
- u16 adc_adr = sn95031_initialize_adc(codec);
- u16 adc_val1, adc_val2;
- unsigned int mic_bias;
-
- sn95031_enable_mic_bias(codec);
-
- /* Enable the sound card for conversion before reading */
- snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
- /* Re-toggle the RRDATARD bit */
- snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);
-
- /* Read the higher bits of data */
- msleep(1000);
- adc_val1 = snd_soc_read(codec, adc_adr);
- adc_adr++;
- adc_val2 = snd_soc_read(codec, adc_adr);
-
- /* Adding lower two bits to the higher bits */
- mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
- mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
- pr_debug("mic bias = %dmV\n", mic_bias);
- return mic_bias;
-}
-/*end - adc helper functions */
-
-static int sn95031_read(void *ctx, unsigned int reg, unsigned int *val)
-{
- u8 value = 0;
- int ret;
-
- ret = intel_scu_ipc_ioread8(reg, &value);
- if (ret == 0)
- *val = value;
-
- return ret;
-}
-
-static int sn95031_write(void *ctx, unsigned int reg, unsigned int value)
-{
- return intel_scu_ipc_iowrite8(reg, value);
-}
-
-static const struct regmap_config sn95031_regmap = {
- .reg_read = sn95031_read,
- .reg_write = sn95031_write,
-};
-
-static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
-{
- switch (level) {
- case SND_SOC_BIAS_ON:
- break;
-
- case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) == 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:
- switch (snd_soc_codec_get_bias_level(codec)) {
- case 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);
- break;
- case 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;
- default:
- break;
- }
- break;
-
-
- case SND_SOC_BIAS_OFF:
- pr_debug("vaud_bias _OFF doing rail shutdown\n");
- snd_soc_write(codec, SN95031_VAUD, BIT(3));
- break;
- }
-
- 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/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h
deleted file mode 100644
index 7651fe4e6a45..000000000000
--- a/sound/soc/codecs/sn95031.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * sn95031.h - TI sn95031 Codec driver
- *
- * Copyright (C) 2010 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
- */
-#ifndef _SN95031_H
-#define _SN95031_H
-
-/*register map*/
-#define SN95031_VAUD 0xDB
-#define SN95031_VHSP 0xDC
-#define SN95031_VHSN 0xDD
-#define SN95031_VIHF 0xC9
-
-#define SN95031_AUDPLLCTRL 0x240
-#define SN95031_DMICBUF0123 0x241
-#define SN95031_DMICBUF45 0x242
-#define SN95031_DMICGPO 0x244
-#define SN95031_DMICMUX 0x245
-#define SN95031_DMICLK 0x246
-#define SN95031_MICBIAS 0x247
-#define SN95031_ADCCONFIG 0x248
-#define SN95031_MICAMP1 0x249
-#define SN95031_MICAMP2 0x24A
-#define SN95031_NOISEMUX 0x24B
-#define SN95031_AUDIOMUX12 0x24C
-#define SN95031_AUDIOMUX34 0x24D
-#define SN95031_AUDIOSINC 0x24E
-#define SN95031_AUDIOTXEN 0x24F
-#define SN95031_HSEPRXCTRL 0x250
-#define SN95031_IHFRXCTRL 0x251
-#define SN95031_HSMIXER 0x256
-#define SN95031_DACCONFIG 0x257
-#define SN95031_SOFTMUTE 0x258
-#define SN95031_HSLVOLCTRL 0x259
-#define SN95031_HSRVOLCTRL 0x25A
-#define SN95031_IHFLVOLCTRL 0x25B
-#define SN95031_IHFRVOLCTRL 0x25C
-#define SN95031_DRIVEREN 0x25D
-#define SN95031_LOCTL 0x25E
-#define SN95031_VIB1C1 0x25F
-#define SN95031_VIB1C2 0x260
-#define SN95031_VIB1C3 0x261
-#define SN95031_VIB1SPIPCM1 0x262
-#define SN95031_VIB1SPIPCM2 0x263
-#define SN95031_VIB1C5 0x264
-#define SN95031_VIB2C1 0x265
-#define SN95031_VIB2C2 0x266
-#define SN95031_VIB2C3 0x267
-#define SN95031_VIB2SPIPCM1 0x268
-#define SN95031_VIB2SPIPCM2 0x269
-#define SN95031_VIB2C5 0x26A
-#define SN95031_BTNCTRL1 0x26B
-#define SN95031_BTNCTRL2 0x26C
-#define SN95031_PCM1TXSLOT01 0x26D
-#define SN95031_PCM1TXSLOT23 0x26E
-#define SN95031_PCM1TXSLOT45 0x26F
-#define SN95031_PCM1RXSLOT0_3 0x270
-#define SN95031_PCM1RXSLOT45 0x271
-#define SN95031_PCM2TXSLOT01 0x272
-#define SN95031_PCM2TXSLOT23 0x273
-#define SN95031_PCM2TXSLOT45 0x274
-#define SN95031_PCM2RXSLOT01 0x275
-#define SN95031_PCM2RXSLOT23 0x276
-#define SN95031_PCM2RXSLOT45 0x277
-#define SN95031_PCM1C1 0x278
-#define SN95031_PCM1C2 0x279
-#define SN95031_PCM1C3 0x27A
-#define SN95031_PCM2C1 0x27B
-#define SN95031_PCM2C2 0x27C
-/*end codec register defn*/
-
-/*vendor defn these are not part of avp*/
-#define SN95031_SSR2 0x381
-#define SN95031_SSR3 0x382
-#define SN95031_SSR5 0x384
-#define SN95031_SSR6 0x385
-
-/* ADC registers */
-
-#define SN95031_ADC1CNTL1 0x1C0
-#define SN95031_ADC_ENBL 0x10
-#define SN95031_ADC_START 0x08
-#define SN95031_ADC1CNTL3 0x1C2
-#define SN95031_ADCTHERM_ENBL 0x04
-#define SN95031_ADCRRDATA_ENBL 0x05
-#define SN95031_STOPBIT_MASK 16
-#define SN95031_ADCTHERM_MASK 4
-#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */
-#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
-#define SN95031_ADC_NO_LOOP 0x07
-#define SN95031_AUDIO_GPIO_CTRL 0x070
-
-/* ADC channel code values */
-#define SN95031_AUDIO_DETECT_CODE 0x06
-
-/* ADC base addresses */
-#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
-#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
-/* multipier to convert to mV */
-#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
-
-
-struct mfld_jack_data {
- int intr_id;
- int micbias_vol;
- struct snd_soc_jack *mfld_jack;
-};
-
-extern void sn95031_jack_detection(struct snd_soc_codec *codec,
- struct mfld_jack_data *jack_data);
-
-#endif
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index 3ec41ccbf4e2..c9766979b1d7 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC SPDIF DIR (Digital Interface Reciever) driver
*
@@ -9,10 +10,6 @@
*
* Author: Vipin Kumar, <vipin.kumar@st.com>
* Copyright: (C) 2012 ST Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -31,17 +28,22 @@ static const struct snd_soc_dapm_route dir_routes[] = {
{ "Capture", NULL, "spdif-in" },
};
-#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_RATES (SNDRV_PCM_RATE_8000_768000 | \
+ SNDRV_PCM_RATE_128000)
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static struct snd_soc_codec_driver soc_codec_spdif_dir = {
- .dapm_widgets = dir_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
- .dapm_routes = dir_routes,
- .num_dapm_routes = ARRAY_SIZE(dir_routes),
+static const struct snd_soc_component_driver soc_codec_spdif_dir = {
+ .dapm_widgets = dir_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
+ .dapm_routes = dir_routes,
+ .num_dapm_routes = ARRAY_SIZE(dir_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static struct snd_soc_dai_driver dir_stub_dai = {
@@ -57,16 +59,11 @@ static struct snd_soc_dai_driver dir_stub_dai = {
static int spdif_dir_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dir,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_codec_spdif_dir,
&dir_stub_dai, 1);
}
-static int spdif_dir_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id spdif_dir_dt_ids[] = {
{ .compatible = "linux,spdif-dir", },
@@ -77,7 +74,6 @@ MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
static struct platform_driver spdif_dir_driver = {
.probe = spdif_dir_probe,
- .remove = spdif_dir_remove,
.driver = {
.name = "spdif-dir",
.of_match_table = of_match_ptr(spdif_dir_dt_ids),
diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c
index ef634a9ad673..2409fd834f84 100644
--- a/sound/soc/codecs/spdif_transmitter.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC SPDIF DIT driver
*
@@ -8,10 +9,6 @@
* Author: Steve Chen, <schen@mvista.com>
* Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
* Copyright: (C) 2009 Texas Instruments, India
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -24,10 +21,12 @@
#define DRV_NAME "spdif-dit"
-#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_RATES (SNDRV_PCM_RATE_8000_768000 | \
+ SNDRV_PCM_RATE_128000)
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE)
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dapm_widget dit_widgets[] = {
SND_SOC_DAPM_OUTPUT("spdif-out"),
@@ -37,11 +36,14 @@ static const struct snd_soc_dapm_route dit_routes[] = {
{ "spdif-out", NULL, "Playback" },
};
-static struct snd_soc_codec_driver soc_codec_spdif_dit = {
- .dapm_widgets = dit_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
- .dapm_routes = dit_routes,
- .num_dapm_routes = ARRAY_SIZE(dit_routes),
+static const struct snd_soc_component_driver soc_codec_spdif_dit = {
+ .dapm_widgets = dit_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
+ .dapm_routes = dit_routes,
+ .num_dapm_routes = ARRAY_SIZE(dit_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static struct snd_soc_dai_driver dit_stub_dai = {
@@ -57,16 +59,11 @@ static struct snd_soc_dai_driver dit_stub_dai = {
static int spdif_dit_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_codec_spdif_dit,
&dit_stub_dai, 1);
}
-static int spdif_dit_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id spdif_dit_dt_ids[] = {
{ .compatible = "linux,spdif-dit", },
@@ -77,7 +74,6 @@ MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
- .remove = spdif_dit_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(spdif_dit_dt_ids),
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
new file mode 100644
index 000000000000..55f00ce7c718
--- /dev/null
+++ b/sound/soc/codecs/src4xxx-i2c.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for SRC4XXX codecs
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt Flax <flatmax@flatmax.com>
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "src4xxx.h"
+
+static int src4xxx_i2c_probe(struct i2c_client *i2c)
+{
+ return src4xxx_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &src4xxx_regmap_config), NULL);
+}
+
+static const struct i2c_device_id src4xxx_i2c_ids[] = {
+ { "src4392" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids);
+
+static const struct of_device_id src4xxx_of_match[] __maybe_unused = {
+ { .compatible = "ti,src4392", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, src4xxx_of_match);
+
+
+static struct i2c_driver src4xxx_i2c_driver = {
+ .driver = {
+ .name = "src4xxx",
+ .of_match_table = of_match_ptr(src4xxx_of_match),
+ },
+ .probe = src4xxx_i2c_probe,
+ .id_table = src4xxx_i2c_ids,
+};
+module_i2c_driver(src4xxx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC SRC4392 CODEC I2C driver");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c
new file mode 100644
index 000000000000..5a3489475225
--- /dev/null
+++ b/sound/soc/codecs/src4xxx.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TI SRC4xxx Audio Codec driver
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt Flax <flatmax@flatmax.com>
+
+#include <linux/module.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "src4xxx.h"
+
+struct src4xxx {
+ struct regmap *regmap;
+ bool master[2];
+ int mclk_hz;
+ struct device *dev;
+};
+
+enum {SRC4XXX_PORTA, SRC4XXX_PORTB};
+
+/* SRC attenuation */
+static const DECLARE_TLV_DB_SCALE(src_tlv, -12750, 50, 0);
+
+static const struct snd_kcontrol_new src4xxx_controls[] = {
+ SOC_DOUBLE_R_TLV("SRC Volume",
+ SRC4XXX_SCR_CTL_30, SRC4XXX_SCR_CTL_31, 0, 255, 1, src_tlv),
+};
+
+/* I2S port control */
+static const char * const port_out_src_text[] = {
+ "loopback", "other_port", "DIR", "SRC"
+};
+static SOC_ENUM_SINGLE_DECL(porta_out_src_enum, SRC4XXX_PORTA_CTL_03, 4,
+ port_out_src_text);
+static SOC_ENUM_SINGLE_DECL(portb_out_src_enum, SRC4XXX_PORTB_CTL_05, 4,
+ port_out_src_text);
+static const struct snd_kcontrol_new porta_out_control =
+ SOC_DAPM_ENUM("Port A source select", porta_out_src_enum);
+static const struct snd_kcontrol_new portb_out_control =
+ SOC_DAPM_ENUM("Port B source select", portb_out_src_enum);
+
+/* Digital audio transmitter control */
+static const char * const dit_mux_text[] = {"Port A", "Port B", "DIR", "SRC"};
+static SOC_ENUM_SINGLE_DECL(dit_mux_enum, SRC4XXX_TX_CTL_07, 3, dit_mux_text);
+static const struct snd_kcontrol_new dit_mux_control =
+ SOC_DAPM_ENUM("DIT source", dit_mux_enum);
+
+/* SRC control */
+static const char * const src_in_text[] = {"Port A", "Port B", "DIR"};
+static SOC_ENUM_SINGLE_DECL(src_in_enum, SRC4XXX_SCR_CTL_2D, 0, src_in_text);
+static const struct snd_kcontrol_new src_in_control =
+ SOC_DAPM_ENUM("SRC source select", src_in_enum);
+
+/* DIR control */
+static const char * const dir_in_text[] = {"Ch 1", "Ch 2", "Ch 3", "Ch 4"};
+static SOC_ENUM_SINGLE_DECL(dir_in_enum, SRC4XXX_RCV_CTL_0D, 0, dir_in_text);
+static const struct snd_kcontrol_new dir_in_control =
+ SOC_DAPM_ENUM("Digital Input", dir_in_enum);
+
+static const struct snd_soc_dapm_widget src4xxx_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("loopback_A"),
+ SND_SOC_DAPM_INPUT("other_port_A"),
+ SND_SOC_DAPM_INPUT("DIR_A"),
+ SND_SOC_DAPM_INPUT("SRC_A"),
+ SND_SOC_DAPM_MUX("Port A source",
+ SND_SOC_NOPM, 0, 0, &porta_out_control),
+
+ SND_SOC_DAPM_INPUT("loopback_B"),
+ SND_SOC_DAPM_INPUT("other_port_B"),
+ SND_SOC_DAPM_INPUT("DIR_B"),
+ SND_SOC_DAPM_INPUT("SRC_B"),
+ SND_SOC_DAPM_MUX("Port B source",
+ SND_SOC_NOPM, 0, 0, &portb_out_control),
+
+ SND_SOC_DAPM_INPUT("Port_A"),
+ SND_SOC_DAPM_INPUT("Port_B"),
+ SND_SOC_DAPM_INPUT("DIR_"),
+
+ /* Digital audio receivers and transmitters */
+ SND_SOC_DAPM_OUTPUT("DIR_OUT"),
+ SND_SOC_DAPM_OUTPUT("SRC_OUT"),
+ SND_SOC_DAPM_MUX("DIT Out Src", SRC4XXX_PWR_RST_01,
+ SRC4XXX_ENABLE_DIT_SHIFT, 1, &dit_mux_control),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF_A_RX", "Playback A", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF_A_TX", "Capture A", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF_B_RX", "Playback B", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF_B_TX", "Capture B", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1),
+
+ SND_SOC_DAPM_MUX("SRC source", SND_SOC_NOPM, 0, 0, &src_in_control),
+
+ SND_SOC_DAPM_INPUT("MCLK"),
+ SND_SOC_DAPM_INPUT("RXMCLKI"),
+ SND_SOC_DAPM_INPUT("RXMCLKO"),
+
+ SND_SOC_DAPM_INPUT("RX1"),
+ SND_SOC_DAPM_INPUT("RX2"),
+ SND_SOC_DAPM_INPUT("RX3"),
+ SND_SOC_DAPM_INPUT("RX4"),
+ SND_SOC_DAPM_MUX("Digital Input", SRC4XXX_PWR_RST_01,
+ SRC4XXX_ENABLE_DIR_SHIFT, 1, &dir_in_control),
+};
+
+static const struct snd_soc_dapm_route src4xxx_audio_routes[] = {
+ /* I2S Input to Output Routing */
+ {"Port A source", "loopback", "loopback_A"},
+ {"Port A source", "other_port", "other_port_A"},
+ {"Port A source", "DIR", "DIR_A"},
+ {"Port A source", "SRC", "SRC_A"},
+ {"Port B source", "loopback", "loopback_B"},
+ {"Port B source", "other_port", "other_port_B"},
+ {"Port B source", "DIR", "DIR_B"},
+ {"Port B source", "SRC", "SRC_B"},
+ /* DIT muxing */
+ {"DIT Out Src", "Port A", "Capture A"},
+ {"DIT Out Src", "Port B", "Capture B"},
+ {"DIT Out Src", "DIR", "DIR_OUT"},
+ {"DIT Out Src", "SRC", "SRC_OUT"},
+
+ /* SRC input selection */
+ {"SRC source", "Port A", "Port_A"},
+ {"SRC source", "Port B", "Port_B"},
+ {"SRC source", "DIR", "DIR_"},
+ /* SRC mclk selection */
+ {"SRC mclk source", "Master (MCLK)", "MCLK"},
+ {"SRC mclk source", "Master (RXCLKI)", "RXMCLKI"},
+ {"SRC mclk source", "Recovered receiver clk", "RXMCLKO"},
+ /* DIR input selection */
+ {"Digital Input", "Ch 1", "RX1"},
+ {"Digital Input", "Ch 2", "RX2"},
+ {"Digital Input", "Ch 3", "RX3"},
+ {"Digital Input", "Ch 4", "RX4"},
+};
+
+
+static const struct snd_soc_component_driver src4xxx_driver = {
+ .controls = src4xxx_controls,
+ .num_controls = ARRAY_SIZE(src4xxx_controls),
+
+ .dapm_widgets = src4xxx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(src4xxx_dapm_widgets),
+ .dapm_routes = src4xxx_audio_routes,
+ .num_dapm_routes = ARRAY_SIZE(src4xxx_audio_routes),
+};
+
+static int src4xxx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ctrl = SRC4XXX_BUS_MASTER;
+ src4xxx->master[dai->id] = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ ctrl = 0;
+ src4xxx->master[dai->id] = false;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl |= SRC4XXX_BUS_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl |= SRC4XXX_BUS_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl |= SRC4XXX_BUS_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ regmap_update_bits(src4xxx->regmap, SRC4XXX_BUS_FMT(dai->id),
+ SRC4XXX_BUS_FMT_MS_MASK, ctrl);
+
+ return 0;
+}
+
+static int src4xxx_set_mclk_hz(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+
+ dev_info(component->dev, "changing mclk rate from %d to %d Hz\n",
+ src4xxx->mclk_hz, freq);
+ src4xxx->mclk_hz = freq;
+
+ return 0;
+}
+
+static int src4xxx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+ unsigned int mclk_div;
+ int val, pj, jd, d;
+ int reg;
+ int ret;
+
+ switch (dai->id) {
+ case SRC4XXX_PORTB:
+ reg = SRC4XXX_PORTB_CTL_06;
+ break;
+ default:
+ reg = SRC4XXX_PORTA_CTL_04;
+ break;
+ }
+
+ if (src4xxx->master[dai->id]) {
+ mclk_div = src4xxx->mclk_hz/params_rate(params);
+ if (src4xxx->mclk_hz != mclk_div*params_rate(params)) {
+ dev_err(component->dev,
+ "mclk %d / rate %d has a remainder.\n",
+ src4xxx->mclk_hz, params_rate(params));
+ return -EINVAL;
+ }
+
+ val = ((int)mclk_div - 128) / 128;
+ if ((val < 0) | (val > 3)) {
+ dev_err(component->dev,
+ "div register setting %d is out of range\n",
+ val);
+ dev_err(component->dev,
+ "unsupported sample rate %d Hz for the master clock of %d Hz\n",
+ params_rate(params), src4xxx->mclk_hz);
+ return -EINVAL;
+ }
+
+ /* set the TX DIV */
+ ret = regmap_update_bits(src4xxx->regmap,
+ SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ /* set the PLL for the digital receiver */
+ switch (src4xxx->mclk_hz) {
+ case 24576000:
+ pj = 0x22;
+ jd = 0x00;
+ d = 0x00;
+ break;
+ case 22579200:
+ pj = 0x22;
+ jd = 0x1b;
+ d = 0xa3;
+ break;
+ default:
+ /* don't error out here,
+ * other parts of the chip are still functional
+ * Dummy initialize variables to avoid
+ * -Wsometimes-uninitialized from clang.
+ */
+ dev_info(component->dev,
+ "Couldn't set the RCV PLL as this master clock rate is unknown. Chosen regmap values may not match real world values.\n");
+ pj = 0x0;
+ jd = 0xff;
+ d = 0xff;
+ break;
+ }
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_0F, pj);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_0F);
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_10, jd);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_10);
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_11, d);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_11);
+
+ ret = regmap_update_bits(src4xxx->regmap,
+ SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ return regmap_update_bits(src4xxx->regmap, reg,
+ SRC4XXX_MCLK_DIV_MASK, val);
+ } else {
+ dev_info(dai->dev, "not setting up MCLK as not master\n");
+ }
+
+ return 0;
+};
+
+static const struct snd_soc_dai_ops src4xxx_dai_ops = {
+ .hw_params = src4xxx_hw_params,
+ .set_sysclk = src4xxx_set_mclk_hz,
+ .set_fmt = src4xxx_set_dai_fmt,
+};
+
+#define SRC4XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#define SRC4XXX_RATES (SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000|\
+ SNDRV_PCM_RATE_88200|\
+ SNDRV_PCM_RATE_96000|\
+ SNDRV_PCM_RATE_176400|\
+ SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver src4xxx_dai_driver[] = {
+ {
+ .id = SRC4XXX_PORTA,
+ .name = "src4xxx-portA",
+ .playback = {
+ .stream_name = "Playback A",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture A",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .ops = &src4xxx_dai_ops,
+ },
+ {
+ .id = SRC4XXX_PORTB,
+ .name = "src4xxx-portB",
+ .playback = {
+ .stream_name = "Playback B",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture B",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .ops = &src4xxx_dai_ops,
+ },
+};
+
+static const struct reg_default src4xxx_reg_defaults[] = {
+ { SRC4XXX_PWR_RST_01, 0x00 }, /* all powered down intially */
+ { SRC4XXX_PORTA_CTL_03, 0x00 },
+ { SRC4XXX_PORTA_CTL_04, 0x00 },
+ { SRC4XXX_PORTB_CTL_05, 0x00 },
+ { SRC4XXX_PORTB_CTL_06, 0x00 },
+ { SRC4XXX_TX_CTL_07, 0x00 },
+ { SRC4XXX_TX_CTL_08, 0x00 },
+ { SRC4XXX_TX_CTL_09, 0x00 },
+ { SRC4XXX_SRC_DIT_IRQ_MSK_0B, 0x00 },
+ { SRC4XXX_SRC_DIT_IRQ_MODE_0C, 0x00 },
+ { SRC4XXX_RCV_CTL_0D, 0x00 },
+ { SRC4XXX_RCV_CTL_0E, 0x00 },
+ { SRC4XXX_RCV_PLL_0F, 0x00 }, /* not spec. in the datasheet */
+ { SRC4XXX_RCV_PLL_10, 0xff }, /* not spec. in the datasheet */
+ { SRC4XXX_RCV_PLL_11, 0xff }, /* not spec. in the datasheet */
+ { SRC4XXX_RVC_IRQ_MSK_16, 0x00 },
+ { SRC4XXX_RVC_IRQ_MSK_17, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_18, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_19, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_1A, 0x00 },
+ { SRC4XXX_GPIO_1_1B, 0x00 },
+ { SRC4XXX_GPIO_2_1C, 0x00 },
+ { SRC4XXX_GPIO_3_1D, 0x00 },
+ { SRC4XXX_GPIO_4_1E, 0x00 },
+ { SRC4XXX_SCR_CTL_2D, 0x00 },
+ { SRC4XXX_SCR_CTL_2E, 0x00 },
+ { SRC4XXX_SCR_CTL_2F, 0x00 },
+ { SRC4XXX_SCR_CTL_30, 0x00 },
+ { SRC4XXX_SCR_CTL_31, 0x00 },
+};
+
+int src4xxx_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev))
+{
+ struct src4xxx *src4xxx;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ src4xxx = devm_kzalloc(dev, sizeof(*src4xxx), GFP_KERNEL);
+ if (!src4xxx)
+ return -ENOMEM;
+
+ src4xxx->regmap = regmap;
+ src4xxx->dev = dev;
+ src4xxx->mclk_hz = 0; /* mclk has not been configured yet */
+ dev_set_drvdata(dev, src4xxx);
+
+ ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_RESET);
+ if (ret < 0)
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
+ usleep_range(1, 500); /* sleep for more then 500 ns */
+ ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_POWER_DOWN);
+ if (ret < 0)
+ dev_err(dev, "Failed to decommission reset: %d\n", ret);
+ usleep_range(500, 1000); /* sleep for 500 us or more */
+
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_PWR_RST_01,
+ SRC4XXX_POWER_ENABLE, SRC4XXX_POWER_ENABLE);
+ if (ret < 0)
+ dev_err(dev, "Failed to port A and B : %d\n", ret);
+
+ /* set receiver to use master clock (rcv mclk is most likely jittery) */
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0D,
+ SRC4XXX_RXCLK_MCLK, SRC4XXX_RXCLK_MCLK);
+ if (ret < 0)
+ dev_err(dev,
+ "Failed to enable mclk as the PLL1 DIR reference : %d\n", ret);
+
+ /* default to leaving the PLL2 running on loss of lock, divide by 8 */
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0E,
+ SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL,
+ SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL);
+ if (ret < 0)
+ dev_err(dev, "Failed to enable mclk rec and div : %d\n", ret);
+
+ ret = devm_snd_soc_register_component(dev, &src4xxx_driver,
+ src4xxx_dai_driver, ARRAY_SIZE(src4xxx_dai_driver));
+ if (ret == 0)
+ dev_info(dev, "src4392 probe ok %d\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(src4xxx_probe);
+
+static bool src4xxx_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SRC4XXX_RES_00:
+ case SRC4XXX_GLOBAL_ITR_STS_02:
+ case SRC4XXX_SRC_DIT_STS_0A:
+ case SRC4XXX_NON_AUDIO_D_12:
+ case SRC4XXX_RVC_STS_13:
+ case SRC4XXX_RVC_STS_14:
+ case SRC4XXX_RVC_STS_15:
+ case SRC4XXX_SUB_CODE_1F:
+ case SRC4XXX_SUB_CODE_20:
+ case SRC4XXX_SUB_CODE_21:
+ case SRC4XXX_SUB_CODE_22:
+ case SRC4XXX_SUB_CODE_23:
+ case SRC4XXX_SUB_CODE_24:
+ case SRC4XXX_SUB_CODE_25:
+ case SRC4XXX_SUB_CODE_26:
+ case SRC4XXX_SUB_CODE_27:
+ case SRC4XXX_SUB_CODE_28:
+ case SRC4XXX_PC_PREAMBLE_HI_29:
+ case SRC4XXX_PC_PREAMBLE_LO_2A:
+ case SRC4XXX_PD_PREAMBLE_HI_2B:
+ case SRC4XXX_PC_PREAMBLE_LO_2C:
+ case SRC4XXX_IO_RATIO_32:
+ case SRC4XXX_IO_RATIO_33:
+ return true;
+ }
+
+ if (reg > SRC4XXX_IO_RATIO_33 && reg < SRC4XXX_PAGE_SEL_7F)
+ return true;
+
+ return false;
+}
+
+const struct regmap_config src4xxx_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = SRC4XXX_IO_RATIO_33,
+
+ .reg_defaults = src4xxx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(src4xxx_reg_defaults),
+ .volatile_reg = src4xxx_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(src4xxx_regmap_config);
+
+MODULE_DESCRIPTION("ASoC SRC4XXX CODEC driver");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.h b/sound/soc/codecs/src4xxx.h
new file mode 100644
index 000000000000..5bf778fb9945
--- /dev/null
+++ b/sound/soc/codecs/src4xxx.h
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// src4xxx.h -- SRC4XXX ALSA SoC audio driver
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt R Flax <flatmax@flatmax.com>
+
+#ifndef __SRC4XXX_H__
+#define __SRC4XXX_H__
+
+#define SRC4XXX_RES_00 0x00
+#define SRC4XXX_PWR_RST_01 0x01
+#define SRC4XXX_RESET 0x80
+#define SRC4XXX_POWER_DOWN 0x00
+#define SRC4XXX_POWER_ENABLE 0x20
+#define SRC4XXX_ENABLE_SRC 0x1
+#define SRC4XXX_ENABLE_SRC_SHIFT 0
+#define SRC4XXX_ENABLE_DIR 0x2
+#define SRC4XXX_ENABLE_DIR_SHIFT 1
+#define SRC4XXX_ENABLE_DIT 0x4
+#define SRC4XXX_ENABLE_DIT_SHIFT 2
+#define SRC4XXX_ENABLE_PORT_B 0x8
+#define SRC4XXX_ENABLE_PORT_B_SHIFT 3
+#define SRC4XXX_ENABLE_PORT_A 0x10
+#define SRC4XXX_ENABLE_PORT_A_SHIFT 4
+
+#define SRC4XXX_PORTA_CTL_03 0x03
+#define SRC4XXX_BUS_MASTER 0x8
+#define SRC4XXX_BUS_LEFT_J 0x0
+#define SRC4XXX_BUS_I2S 0x1
+#define SRC4XXX_BUS_RIGHT_J_16 0x4
+#define SRC4XXX_BUS_RIGHT_J_18 0x5
+#define SRC4XXX_BUS_RIGHT_J_20 0x6
+#define SRC4XXX_BUS_RIGHT_J_24 0x7
+#define SRC4XXX_BUS_FMT_MS_MASK 0xf
+
+#define SRC4XXX_PORTA_CTL_04 0x04
+#define SRC4XXX_MCLK_DIV_MASK 0x3
+
+#define SRC4XXX_BUS_FMT(id) (SRC4XXX_PORTA_CTL_03+2*id)
+#define SRC4XXX_BUS_CLK(id) (SRC4XXX_PORTA_CTL_04+2*id)
+
+#define SRC4XXX_PORTB_CTL_05 0x05
+#define SRC4XXX_PORTB_CTL_06 0x06
+
+#define SRC4XXX_TX_CTL_07 0x07
+#define SRC4XXX_TX_MCLK_DIV_MASK 0x60
+#define SRC4XXX_TX_MCLK_DIV_SHIFT 5
+
+#define SRC4XXX_TX_CTL_08 0x08
+#define SRC4XXX_TX_CTL_09 0x09
+#define SRC4XXX_SRC_DIT_IRQ_MSK_0B 0x0B
+#define SRC4XXX_SRC_BTI_EN 0x01
+#define SRC4XXX_SRC_TSLIP_EN 0x02
+#define SRC4XXX_SRC_DIT_IRQ_MODE_0C 0x0C
+#define SRC4XXX_RCV_CTL_0D 0x0D
+#define SRC4XXX_RXCLK_RXCKI 0x0
+#define SRC4XXX_RXCLK_MCLK 0x8
+#define SRC4XXX_RCV_CTL_0E 0x0E
+#define SRC4XXX_REC_MCLK_EN 0x1
+#define SRC4XXX_PLL2_DIV_0 (0x0<<1)
+#define SRC4XXX_PLL2_DIV_2 (0x1<<1)
+#define SRC4XXX_PLL2_DIV_4 (0x2<<1)
+#define SRC4XXX_PLL2_DIV_8 (0x3<<1)
+#define SRC4XXX_PLL2_LOL 0x8
+#define SRC4XXX_RCV_PLL_0F 0x0F
+#define SRC4XXX_RCV_PLL_10 0x10
+#define SRC4XXX_RCV_PLL_11 0x11
+#define SRC4XXX_RVC_IRQ_MSK_16 0x16
+#define SRC4XXX_RVC_IRQ_MSK_17 0x17
+#define SRC4XXX_RVC_IRQ_MODE_18 0x18
+#define SRC4XXX_RVC_IRQ_MODE_19 0x19
+#define SRC4XXX_RVC_IRQ_MODE_1A 0x1A
+#define SRC4XXX_GPIO_1_1B 0x1B
+#define SRC4XXX_GPIO_2_1C 0x1C
+#define SRC4XXX_GPIO_3_1D 0x1D
+#define SRC4XXX_GPIO_4_1E 0x1E
+#define SRC4XXX_SCR_CTL_2D 0x2D
+#define SRC4XXX_SCR_CTL_2E 0x2E
+#define SRC4XXX_SCR_CTL_2F 0x2F
+#define SRC4XXX_SCR_CTL_30 0x30
+#define SRC4XXX_SCR_CTL_31 0x31
+#define SRC4XXX_PAGE_SEL_7F 0x7F
+
+// read only registers
+#define SRC4XXX_GLOBAL_ITR_STS_02 0x02
+#define SRC4XXX_SRC_DIT_STS_0A 0x0A
+#define SRC4XXX_NON_AUDIO_D_12 0x12
+#define SRC4XXX_RVC_STS_13 0x13
+#define SRC4XXX_RVC_STS_14 0x14
+#define SRC4XXX_RVC_STS_15 0x15
+#define SRC4XXX_SUB_CODE_1F 0x1F
+#define SRC4XXX_SUB_CODE_20 0x20
+#define SRC4XXX_SUB_CODE_21 0x21
+#define SRC4XXX_SUB_CODE_22 0x22
+#define SRC4XXX_SUB_CODE_23 0x23
+#define SRC4XXX_SUB_CODE_24 0x24
+#define SRC4XXX_SUB_CODE_25 0x25
+#define SRC4XXX_SUB_CODE_26 0x26
+#define SRC4XXX_SUB_CODE_27 0x27
+#define SRC4XXX_SUB_CODE_28 0x28
+#define SRC4XXX_PC_PREAMBLE_HI_29 0x29
+#define SRC4XXX_PC_PREAMBLE_LO_2A 0x2A
+#define SRC4XXX_PD_PREAMBLE_HI_2B 0x2B
+#define SRC4XXX_PC_PREAMBLE_LO_2C 0x2C
+#define SRC4XXX_IO_RATIO_32 0x32
+#define SRC4XXX_IO_RATIO_33 0x33
+
+int src4xxx_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev));
+extern const struct regmap_config src4xxx_regmap_config;
+
+#endif /* __SRC4XXX_H__ */
diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c
new file mode 100644
index 000000000000..1d022643c307
--- /dev/null
+++ b/sound/soc/codecs/ssm2305.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices SSM2305 Amplifier Driver
+//
+// Copyright (C) 2018 Pengutronix, Marco Felsch <kernel@pengutronix.de>
+//
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "ssm2305"
+
+struct ssm2305 {
+ /* shutdown gpio */
+ struct gpio_desc *gpiod_shutdown;
+};
+
+static int ssm2305_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kctrl, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct ssm2305 *data = snd_soc_component_get_drvdata(c);
+
+ gpiod_set_value_cansleep(data->gpiod_shutdown,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ssm2305_dapm_widgets[] = {
+ /* Stereo input/output */
+ SND_SOC_DAPM_INPUT("L_IN"),
+ SND_SOC_DAPM_INPUT("R_IN"),
+ SND_SOC_DAPM_OUTPUT("L_OUT"),
+ SND_SOC_DAPM_OUTPUT("R_OUT"),
+
+ SND_SOC_DAPM_SUPPLY("Power", SND_SOC_NOPM, 0, 0, ssm2305_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route ssm2305_dapm_routes[] = {
+ { "L_OUT", NULL, "L_IN" },
+ { "R_OUT", NULL, "R_IN" },
+ { "L_IN", NULL, "Power" },
+ { "R_IN", NULL, "Power" },
+};
+
+static const struct snd_soc_component_driver ssm2305_component_driver = {
+ .dapm_widgets = ssm2305_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm2305_dapm_widgets),
+ .dapm_routes = ssm2305_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm2305_dapm_routes),
+};
+
+static int ssm2305_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ssm2305 *priv;
+
+ /* Allocate the private data */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ /* Get shutdown gpio */
+ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_shutdown))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown),
+ "Failed to get 'shutdown' gpio\n");
+
+ return devm_snd_soc_register_component(dev, &ssm2305_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ssm2305_of_match[] = {
+ { .compatible = "adi,ssm2305", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssm2305_of_match);
+#endif
+
+static struct platform_driver ssm2305_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(ssm2305_of_match),
+ },
+ .probe = ssm2305_probe,
+};
+
+module_platform_driver(ssm2305_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2305 amplifier driver");
+MODULE_AUTHOR("Marco Felsch <m.felsch@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index e2e0bfa7ec20..9008e5416004 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -1,20 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_data/ssm2518.h>
+#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -115,7 +113,7 @@ struct ssm2518 {
unsigned int sysclk;
const struct snd_pcm_hw_constraint_list *constraints;
- int enable_gpio;
+ struct gpio_desc *enable_gpio;
};
static const struct reg_default ssm2518_reg_defaults[] = {
@@ -336,8 +334,8 @@ static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
static int ssm2518_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
unsigned int ctrl1, ctrl1_mask;
int mcs;
@@ -389,9 +387,9 @@ static int ssm2518_hw_params(struct snd_pcm_substream *substream,
SSM2518_POWER1_MCS_MASK, mcs << 1);
}
-static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2518_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
@@ -405,13 +403,13 @@ static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1 = 0, ctrl2 = 0;
bool invert_fclk;
int ret;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -484,8 +482,8 @@ static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
regcache_mark_dirty(ssm2518->regmap);
}
- if (gpio_is_valid(ssm2518->enable_gpio))
- gpio_set_value(ssm2518->enable_gpio, enable);
+ if (ssm2518->enable_gpio)
+ gpiod_set_value_cansleep(ssm2518->enable_gpio, enable);
regcache_cache_only(ssm2518->regmap, !enable);
@@ -498,10 +496,11 @@ static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
return ret;
}
-static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
+static int ssm2518_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret = 0;
switch (level) {
@@ -510,7 +509,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
ret = ssm2518_set_power(ssm2518, true);
break;
case SND_SOC_BIAS_OFF:
@@ -524,7 +523,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1, ctrl2;
int left_slot, right_slot;
int ret;
@@ -609,7 +608,7 @@ static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int ssm2518_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
if (ssm2518->constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -624,9 +623,10 @@ static int ssm2518_startup(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops ssm2518_dai_ops = {
.startup = ssm2518_startup,
.hw_params = ssm2518_hw_params,
- .digital_mute = ssm2518_mute,
+ .mute_stream = ssm2518_mute,
.set_fmt = ssm2518_set_dai_fmt,
.set_tdm_slot = ssm2518_set_tdm_slot,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ssm2518_dai = {
@@ -641,10 +641,10 @@ static struct snd_soc_dai_driver ssm2518_dai = {
.ops = &ssm2518_dai_ops,
};
-static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int ssm2518_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (clk_id != SSM2518_SYSCLK)
@@ -710,17 +710,17 @@ static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
SSM2518_POWER1_NO_BCLK, val);
}
-static struct snd_soc_codec_driver ssm2518_codec_driver = {
- .set_bias_level = ssm2518_set_bias_level,
- .set_sysclk = ssm2518_set_sysclk,
- .idle_bias_off = true,
-
- .controls = ssm2518_snd_controls,
- .num_controls = ARRAY_SIZE(ssm2518_snd_controls),
- .dapm_widgets = ssm2518_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
- .dapm_routes = ssm2518_routes,
- .num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
+static const struct snd_soc_component_driver ssm2518_component_driver = {
+ .set_bias_level = ssm2518_set_bias_level,
+ .set_sysclk = ssm2518_set_sysclk,
+ .controls = ssm2518_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm2518_snd_controls),
+ .dapm_widgets = ssm2518_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
+ .dapm_routes = ssm2518_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ssm2518_regmap_config = {
@@ -734,10 +734,8 @@ static const struct regmap_config ssm2518_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
};
-static int ssm2518_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ssm2518_i2c_probe(struct i2c_client *i2c)
{
- struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
struct ssm2518 *ssm2518;
int ret;
@@ -745,22 +743,14 @@ static int ssm2518_i2c_probe(struct i2c_client *i2c,
if (ssm2518 == NULL)
return -ENOMEM;
- if (pdata) {
- ssm2518->enable_gpio = pdata->enable_gpio;
- } else if (i2c->dev.of_node) {
- ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0);
- if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT)
- return ssm2518->enable_gpio;
- } else {
- ssm2518->enable_gpio = -1;
- }
+ /* Start with enabling the chip */
+ ssm2518->enable_gpio = devm_gpiod_get_optional(&i2c->dev, NULL,
+ GPIOD_OUT_HIGH);
+ ret = PTR_ERR_OR_ZERO(ssm2518->enable_gpio);
+ if (ret)
+ return ret;
- if (gpio_is_valid(ssm2518->enable_gpio)) {
- ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio,
- GPIOF_OUT_INIT_HIGH, "SSM2518 nSD");
- if (ret)
- return ret;
- }
+ gpiod_set_consumer_name(ssm2518->enable_gpio, "SSM2518 nSD");
i2c_set_clientdata(i2c, ssm2518);
@@ -790,16 +780,11 @@ static int ssm2518_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &ssm2518_component_driver,
&ssm2518_dai, 1);
}
-static int ssm2518_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id ssm2518_dt_ids[] = {
{ .compatible = "adi,ssm2518", },
@@ -809,7 +794,7 @@ MODULE_DEVICE_TABLE(of, ssm2518_dt_ids);
#endif
static const struct i2c_device_id ssm2518_i2c_ids[] = {
- { "ssm2518", 0 },
+ { "ssm2518" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
@@ -820,7 +805,6 @@ static struct i2c_driver ssm2518_driver = {
.of_match_table = of_match_ptr(ssm2518_dt_ids),
},
.probe = ssm2518_i2c_probe,
- .remove = ssm2518_i2c_remove,
.id_table = ssm2518_i2c_ids,
};
module_i2c_driver(ssm2518_driver);
diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h
index 62511d80518e..273fd0977470 100644
--- a/sound/soc/codecs/ssm2518.h
+++ b/sound/soc/codecs/ssm2518.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SND_SOC_CODECS_SSM2518_H__
diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c
index 173ba85ff59e..49c74cba17c7 100644
--- a/sound/soc/codecs/ssm2602-i2c.c
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2602/SSM2603/SSM2604 I2C audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -20,19 +19,12 @@
* low = 0x1a
* high = 0x1b
*/
-static int ssm2602_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ssm2602_i2c_probe(struct i2c_client *client)
{
- return ssm2602_probe(&client->dev, id->driver_data,
+ return ssm2602_probe(&client->dev, (uintptr_t)i2c_get_match_data(client),
devm_regmap_init_i2c(client, &ssm2602_regmap_config));
}
-static int ssm2602_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ssm2602_i2c_id[] = {
{ "ssm2602", SSM2602 },
{ "ssm2603", SSM2602 },
@@ -55,7 +47,6 @@ static struct i2c_driver ssm2602_i2c_driver = {
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_i2c_probe,
- .remove = ssm2602_i2c_remove,
.id_table = ssm2602_i2c_id,
};
module_i2c_driver(ssm2602_i2c_driver);
diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c
index 842f373045c6..bb49fb6b62ad 100644
--- a/sound/soc/codecs/ssm2602-spi.c
+++ b/sound/soc/codecs/ssm2602-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2602 SPI audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -20,12 +19,6 @@ static int ssm2602_spi_probe(struct spi_device *spi)
devm_regmap_init_spi(spi, &ssm2602_regmap_config));
}
-static int ssm2602_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct of_device_id ssm2602_of_match[] = {
{ .compatible = "adi,ssm2602", },
{ }
@@ -38,7 +31,6 @@ static struct spi_driver ssm2602_spi_driver = {
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_spi_probe,
- .remove = ssm2602_spi_remove,
};
module_spi_driver(ssm2602_spi_driver);
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 4452fea0b118..fccd2eacd7a6 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -1,31 +1,17 @@
-/*
- * File: sound/soc/codecs/ssm2602.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * Created: Tue June 06 2008
- * Description: Driver for ssm2602 sound chip
- *
- * Modified:
- * Copyright 2008 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// File: sound/soc/codecs/ssm2602.c
+// Author: Cliff Cai <Cliff.Cai@analog.com>
+//
+// Created: Tue June 06 2008
+// Description: Driver for ssm2602 sound chip
+//
+// Modified:
+// Copyright 2008 Analog Devices Inc.
+//
+// Bugs: Enter bugs at http://blackfin.uclinux.org/
+
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -54,10 +40,29 @@ struct ssm2602_priv {
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
-static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = {
+ { .reg = 0x00, .def = 0x0097 },
+ { .reg = 0x01, .def = 0x0097 },
+ { .reg = 0x02, .def = 0x0079 },
+ { .reg = 0x03, .def = 0x0079 },
+ { .reg = 0x04, .def = 0x000a },
+ { .reg = 0x05, .def = 0x0008 },
+ { .reg = 0x06, .def = 0x009f },
+ { .reg = 0x07, .def = 0x000a },
+ { .reg = 0x08, .def = 0x0000 },
+ { .reg = 0x09, .def = 0x0000 }
+};
+
+/*
+ * ssm2602 register patch
+ * Workaround for playback distortions after power up: activates digital
+ * core, and then powers on output, DAC, and whole chip at the same time
+ */
+
+static const struct reg_sequence ssm2602_patch[] = {
+ { SSM2602_ACTIVE, 0x01 },
+ { SSM2602_PWR, 0x07 },
+ { SSM2602_RESET, 0x00 },
};
@@ -104,7 +109,6 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
};
/* Output Mixer */
@@ -114,10 +118,31 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
};
+static const struct snd_kcontrol_new mic_ctl =
+ SOC_DAPM_SINGLE("Switch", SSM2602_APANA, 1, 1, 1);
+
/* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
+static int ssm2602_mic_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ /*
+ * According to the ssm2603 data sheet (control register sequencing),
+ * the digital core should be activated only after all necessary bits
+ * in the power register are enabled, and a delay determined by the
+ * decoupling capacitor on the VMID pin has passed. If the digital core
+ * is activated too early, or even before the ADC is powered up, audible
+ * artifacts appear at the beginning and end of the recorded signal.
+ *
+ * In practice, audible artifacts disappear well over 500 ms.
+ */
+ msleep(500);
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
@@ -139,6 +164,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+SND_SOC_DAPM_SWITCH_E("Mic Switch", SSM2602_APANA, 1, 1, &mic_ctl,
+ ssm2602_mic_switch_event, SND_SOC_DAPM_PRE_PMU),
+
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
@@ -171,9 +199,11 @@ static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"LHPOUT", NULL, "Output Mixer"},
{"Input Mux", "Line", "Line Input"},
- {"Input Mux", "Mic", "Mic Bias"},
+ {"Input Mux", "Mic", "Mic Switch"},
{"ADC", NULL, "Input Mux"},
+ {"Mic Switch", NULL, "Mic Bias"},
+
{"Mic Bias", NULL, "MICIN"},
};
@@ -262,9 +292,12 @@ static inline int ssm2602_get_coeff(int mclk, int rate)
int i;
for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
- if (ssm2602_coeff_table[i].rate == rate &&
- ssm2602_coeff_table[i].mclk == mclk)
- return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].rate == rate) {
+ if (ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].mclk == mclk / 2)
+ return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2;
+ }
}
return -EINVAL;
}
@@ -273,8 +306,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
unsigned int iface;
@@ -308,8 +341,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
static int ssm2602_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
if (ssm2602->sysclk_constraints) {
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -320,9 +353,9 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(dai->component);
if (mute)
regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
@@ -337,8 +370,8 @@ static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
if (dir == SND_SOC_CLOCK_IN) {
if (clk_id != SSM2602_SYSCLK)
@@ -347,18 +380,24 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (freq) {
case 12288000:
case 18432000:
+ case 24576000:
+ case 36864000:
ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
break;
case 11289600:
case 16934400:
+ case 22579200:
+ case 33868800:
ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
break;
case 12000000:
+ case 24000000:
ssm2602->sysclk_constraints = NULL;
break;
default:
return -EINVAL;
}
+
ssm2602->sysclk = freq;
} else {
unsigned int mask;
@@ -389,15 +428,15 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(codec_dai->component);
unsigned int iface = 0;
/* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -445,10 +484,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
+static int ssm2602_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -487,9 +526,10 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.hw_params = ssm2602_hw_params,
- .digital_mute = ssm2602_mute,
+ .mute_stream = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ssm2602_dai = {
@@ -507,23 +547,23 @@ static struct snd_soc_dai_driver ssm2602_dai = {
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
.ops = &ssm2602_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
-static int ssm2602_resume(struct snd_soc_codec *codec)
+static int ssm2602_resume(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
regcache_sync(ssm2602->regmap);
return 0;
}
-static int ssm2602_codec_probe(struct snd_soc_codec *codec)
+static int ssm2602_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int ret;
regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
@@ -531,7 +571,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
- ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
+ ret = snd_soc_add_component_controls(component, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
if (ret)
return ret;
@@ -545,9 +585,9 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(ssm2602_routes));
}
-static int ssm2604_codec_probe(struct snd_soc_codec *codec)
+static int ssm2604_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
@@ -559,17 +599,20 @@ static int ssm2604_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(ssm2604_routes));
}
-static int ssm260x_codec_probe(struct snd_soc_codec *codec)
+static int ssm260x_component_probe(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
int ret;
ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
+ regmap_register_patch(ssm2602->regmap, ssm2602_patch,
+ ARRAY_SIZE(ssm2602_patch));
+
/* set the update bits */
regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
@@ -581,28 +624,30 @@ static int ssm260x_codec_probe(struct snd_soc_codec *codec)
switch (ssm2602->type) {
case SSM2602:
- ret = ssm2602_codec_probe(codec);
+ ret = ssm2602_component_probe(component);
break;
case SSM2604:
- ret = ssm2604_codec_probe(codec);
+ ret = ssm2604_component_probe(component);
break;
}
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
- .probe = ssm260x_codec_probe,
- .resume = ssm2602_resume,
- .set_bias_level = ssm2602_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = ssm260x_snd_controls,
- .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
- .dapm_widgets = ssm260x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
- .dapm_routes = ssm260x_routes,
- .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
+static const struct snd_soc_component_driver soc_component_dev_ssm2602 = {
+ .probe = ssm260x_component_probe,
+ .resume = ssm2602_resume,
+ .set_bias_level = ssm2602_set_bias_level,
+ .controls = ssm260x_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
+ .dapm_widgets = ssm260x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
+ .dapm_routes = ssm260x_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
@@ -618,8 +663,8 @@ const struct regmap_config ssm2602_regmap_config = {
.volatile_reg = ssm2602_register_volatile,
.cache_type = REGCACHE_RBTREE,
- .reg_defaults_raw = ssm2602_reg,
- .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+ .reg_defaults = ssm2602_reg,
+ .num_reg_defaults = ARRAY_SIZE(ssm2602_reg),
};
EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
@@ -639,7 +684,7 @@ int ssm2602_probe(struct device *dev, enum ssm2602_type type,
ssm2602->type = type;
ssm2602->regmap = regmap;
- return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ssm2602,
&ssm2602_dai, 1);
}
EXPORT_SYMBOL_GPL(ssm2602_probe);
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
index 747538847689..05073380a4af 100644
--- a/sound/soc/codecs/ssm2602.h
+++ b/sound/soc/codecs/ssm2602.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* File: sound/soc/codecs/ssm2602.h
* Author: Cliff Cai <Cliff.Cai@analog.com>
@@ -8,21 +9,6 @@
* Copyright 2008 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SSM2602_H
diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c
new file mode 100644
index 000000000000..8c6665677a17
--- /dev/null
+++ b/sound/soc/codecs/ssm3515.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+//
+// Analog Devices' SSM3515 audio amp driver
+//
+// Copyright (C) The Asahi Linux Contributors
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+
+#define SSM3515_PWR 0x00
+#define SSM3515_PWR_APWDN_EN BIT(7)
+#define SSM3515_PWR_BSNS_PWDN BIT(6)
+#define SSM3515_PWR_S_RST BIT(1)
+#define SSM3515_PWR_SPWDN BIT(0)
+
+#define SSM3515_GEC 0x01
+#define SSM3515_GEC_EDGE BIT(4)
+#define SSM3515_GEC_EDGE_SHIFT 4
+#define SSM3515_GEC_ANA_GAIN GENMASK(1, 0)
+
+#define SSM3515_DAC 0x02
+#define SSM3515_DAC_HV BIT(7)
+#define SSM3515_DAC_MUTE BIT(6)
+#define SSM3515_DAC_HPF BIT(5)
+#define SSM3515_DAC_LPM BIT(4)
+#define SSM3515_DAC_FS GENMASK(2, 0)
+
+#define SSM3515_DAC_VOL 0x03
+
+#define SSM3515_SAI1 0x04
+#define SSM3515_SAI1_DAC_POL BIT(7)
+#define SSM3515_SAI1_BCLK_POL BIT(6)
+#define SSM3515_SAI1_TDM_BCLKS GENMASK(5, 3)
+#define SSM3515_SAI1_FSYNC_MODE BIT(2)
+#define SSM3515_SAI1_SDATA_FMT BIT(1)
+#define SSM3515_SAI1_SAI_MODE BIT(0)
+
+#define SSM3515_SAI2 0x05
+#define SSM3515_SAI2_DATA_WIDTH BIT(7)
+#define SSM3515_SAI2_AUTO_SLOT BIT(4)
+#define SSM3515_SAI2_TDM_SLOT GENMASK(3, 0)
+
+#define SSM3515_VBAT_OUT 0x06
+
+#define SSM3515_STATUS 0x0a
+#define SSM3515_STATUS_UVLO_REG BIT(6)
+#define SSM3515_STATUS_LIM_EG BIT(5)
+#define SSM3515_STATUS_CLIP BIT(4)
+#define SSM3515_STATUS_AMP_OC BIT(3)
+#define SSM3515_STATUS_OTF BIT(2)
+#define SSM3515_STATUS_OTW BIT(1)
+#define SSM3515_STATUS_BAT_WARN BIT(0)
+
+static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SSM3515_STATUS:
+ case SSM3515_VBAT_OUT:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default ssm3515_reg_defaults[] = {
+ { SSM3515_PWR, 0x81 },
+ { SSM3515_GEC, 0x01 },
+ { SSM3515_DAC, 0x32 },
+ { SSM3515_DAC_VOL, 0x40 },
+ { SSM3515_SAI1, 0x11 },
+ { SSM3515_SAI2, 0x00 },
+};
+
+static const struct regmap_config ssm3515_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = ssm3515_volatile_reg,
+ .max_register = 0xb,
+ .reg_defaults = ssm3515_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+struct ssm3515_data {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+// The specced range is -71.25...24.00 dB with step size of 0.375 dB,
+// and a mute item below that. This is represented by -71.62...24.00 dB
+// with the mute item mapped onto the low end.
+static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400);
+
+static const char * const ssm3515_ana_gain_text[] = {
+ "8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span",
+};
+
+static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC,
+ __bf_shf(SSM3515_GEC_ANA_GAIN),
+ ssm3515_ana_gain_text);
+
+static const struct snd_kcontrol_new ssm3515_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL,
+ 0, 255, 1, ssm3515_dac_volume),
+ SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC,
+ __bf_shf(SSM3515_GEC_EDGE), 1, 0),
+ SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC,
+ __bf_shf(SSM3515_DAC_HV), 1, 1),
+ SOC_SINGLE("HPF Switch", SSM3515_DAC,
+ __bf_shf(SSM3515_DAC_HPF), 1, 0),
+ SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1,
+ __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0),
+ SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum),
+};
+
+static void ssm3515_read_faults(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_read(component, SSM3515_STATUS);
+ if (ret <= 0) {
+ /*
+ * If the read was erroneous, ASoC core has printed a message,
+ * and that's all that's appropriate in handling the error here.
+ */
+ return;
+ }
+
+ dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n",
+ FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "",
+ FIELD_GET(SSM3515_STATUS_LIM_EG, ret) ? " limiter engaged" : "",
+ FIELD_GET(SSM3515_STATUS_CLIP, ret) ? " clipping detected" : "",
+ FIELD_GET(SSM3515_STATUS_AMP_OC, ret) ? " amp over-current fault" : "",
+ FIELD_GET(SSM3515_STATUS_OTF, ret) ? " overtemperature fault" : "",
+ FIELD_GET(SSM3515_STATUS_OTW, ret) ? " overtemperature warning" : "",
+ FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : "");
+}
+
+static int ssm3515_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ /* Start out muted */
+ ret = snd_soc_component_update_bits(component, SSM3515_DAC,
+ SSM3515_DAC_MUTE, SSM3515_DAC_MUTE);
+ if (ret < 0)
+ return ret;
+
+ /* Disable the 'master power-down' */
+ ret = snd_soc_component_update_bits(component, SSM3515_PWR,
+ SSM3515_PWR_SPWDN, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(dai->component,
+ SSM3515_DAC,
+ SSM3515_DAC_MUTE,
+ FIELD_PREP(SSM3515_DAC_MUTE, mute));
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int ssm3515_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret, rateval;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16:
+ case SNDRV_PCM_FORMAT_S24:
+ ret = snd_soc_component_update_bits(component,
+ SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH,
+ FIELD_PREP(SSM3515_SAI2_DATA_WIDTH,
+ params_width(params) == 16));
+ if (ret < 0)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000 ... 12000:
+ rateval = 0;
+ break;
+ case 16000 ... 24000:
+ rateval = 1;
+ break;
+ case 32000 ... 48000:
+ rateval = 2;
+ break;
+ case 64000 ... 96000:
+ rateval = 3;
+ break;
+ case 128000 ... 192000:
+ rateval = 4;
+ break;
+ case 48001 ... 63999: /* this is ...72000 but overlaps */
+ rateval = 5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ SSM3515_DAC, SSM3515_DAC_FS,
+ FIELD_PREP(SSM3515_DAC_FS, rateval));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */
+ int ret;
+ u8 sai1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ sai1 |= SSM3515_SAI1_BCLK_POL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fpol_inv = 1;
+ sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fpol_inv = 0;
+ sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ fpol_inv ^= 1;
+ break;
+ }
+
+ /* Set the serial input to 'TDM mode' */
+ sai1 |= SSM3515_SAI1_SAI_MODE;
+
+ if (fpol_inv) {
+ /*
+ * We configure the codec in a 'TDM mode', in which the
+ * FSYNC_MODE bit of SAI1 is supposed to select between
+ * what the datasheet calls 'Pulsed FSYNC mode' and '50%
+ * FSYNC mode'.
+ *
+ * Experiments suggest that this bit in fact simply selects
+ * the FSYNC polarity, so go with that.
+ */
+ sai1 |= SSM3515_SAI1_FSYNC_MODE;
+ }
+
+ ret = snd_soc_component_update_bits(component, SSM3515_SAI1,
+ SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT |
+ SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ int slot, tdm_bclks_val, ret;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ slot = __ffs(tx_mask);
+
+ if (tx_mask & ~BIT(slot))
+ return -EINVAL;
+
+ switch (slot_width) {
+ case 16:
+ tdm_bclks_val = 0;
+ break;
+ case 24:
+ tdm_bclks_val = 1;
+ break;
+ case 32:
+ tdm_bclks_val = 2;
+ break;
+ case 48:
+ tdm_bclks_val = 3;
+ break;
+ case 64:
+ tdm_bclks_val = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, SSM3515_SAI1,
+ SSM3515_SAI1_TDM_BCLKS,
+ FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, SSM3515_SAI2,
+ SSM3515_SAI2_TDM_SLOT,
+ FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ssm3515_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ /*
+ * We don't get live notification of faults, so at least at
+ * this time, when playback is over, check if we have tripped
+ * over anything and if so, log it.
+ */
+ ssm3515_read_faults(dai->component);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ssm3515_dai_ops = {
+ .mute_stream = ssm3515_mute,
+ .hw_params = ssm3515_hw_params,
+ .set_fmt = ssm3515_set_fmt,
+ .set_tdm_slot = ssm3515_set_tdm_slot,
+ .hw_free = ssm3515_hw_free,
+};
+
+static struct snd_soc_dai_driver ssm3515_dai_driver = {
+ .name = "SSM3515 SAI",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ssm3515_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = {
+ {"OUT", NULL, "DAC"},
+ {"DAC", NULL, "Playback"},
+};
+
+static const struct snd_soc_component_driver ssm3515_asoc_component = {
+ .probe = ssm3515_probe,
+ .controls = ssm3515_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm3515_snd_controls),
+ .dapm_widgets = ssm3515_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets),
+ .dapm_routes = ssm3515_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes),
+ .endianness = 1,
+};
+
+static int ssm3515_i2c_probe(struct i2c_client *client)
+{
+ struct ssm3515_data *data;
+ int ret;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &client->dev;
+ i2c_set_clientdata(client, data);
+
+ data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(data->dev, PTR_ERR(data->regmap),
+ "initializing register map\n");
+
+ /* Perform a reset */
+ ret = regmap_update_bits(data->regmap, SSM3515_PWR,
+ SSM3515_PWR_S_RST, SSM3515_PWR_S_RST);
+ if (ret < 0)
+ return dev_err_probe(data->dev, ret,
+ "performing software reset\n");
+ regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap);
+
+ return devm_snd_soc_register_component(data->dev,
+ &ssm3515_asoc_component,
+ &ssm3515_dai_driver, 1);
+}
+
+static const struct of_device_id ssm3515_of_match[] = {
+ { .compatible = "adi,ssm3515" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ssm3515_of_match);
+
+static struct i2c_driver ssm3515_i2c_driver = {
+ .driver = {
+ .name = "ssm3515",
+ .of_match_table = ssm3515_of_match,
+ },
+ .probe = ssm3515_i2c_probe,
+};
+module_i2c_driver(ssm3515_i2c_driver);
+
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index e619d5651b09..15f88624faeb 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM4567 amplifier audio driver
*
@@ -6,8 +7,6 @@
*
* Based on code copyright/by:
* Copyright 2013 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/acpi.h>
@@ -199,8 +198,8 @@ static const struct snd_soc_dapm_route ssm4567_routes[] = {
static int ssm4567_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
unsigned int dacfs;
@@ -221,9 +220,9 @@ static int ssm4567_hw_params(struct snd_pcm_substream *substream,
SSM4567_DAC_FS_MASK, dacfs);
}
-static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
+static int ssm4567_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
val = mute ? SSM4567_DAC_MUTE : 0;
@@ -279,8 +278,8 @@ static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
unsigned int ctrl1 = 0;
bool invert_fclk;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -352,6 +351,11 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
regcache_cache_only(ssm4567->regmap, !enable);
if (enable) {
+ ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET,
+ 0x00);
+ if (ret)
+ return ret;
+
ret = regmap_update_bits(ssm4567->regmap,
SSM4567_REG_POWER_CTRL,
SSM4567_POWER_SPWDN, 0x00);
@@ -361,10 +365,11 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
return ret;
}
-static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
+static int ssm4567_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+ struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret = 0;
switch (level) {
@@ -373,7 +378,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
ret = ssm4567_set_power(ssm4567, true);
break;
case SND_SOC_BIAS_OFF:
@@ -386,9 +391,10 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ssm4567_dai_ops = {
.hw_params = ssm4567_hw_params,
- .digital_mute = ssm4567_mute,
+ .mute_stream = ssm4567_mute,
.set_fmt = ssm4567_set_dai_fmt,
.set_tdm_slot = ssm4567_set_tdm_slot,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ssm4567_dai = {
@@ -412,16 +418,16 @@ static struct snd_soc_dai_driver ssm4567_dai = {
.ops = &ssm4567_dai_ops,
};
-static struct snd_soc_codec_driver ssm4567_codec_driver = {
- .set_bias_level = ssm4567_set_bias_level,
- .idle_bias_off = true,
-
- .controls = ssm4567_snd_controls,
- .num_controls = ARRAY_SIZE(ssm4567_snd_controls),
- .dapm_widgets = ssm4567_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets),
- .dapm_routes = ssm4567_routes,
- .num_dapm_routes = ARRAY_SIZE(ssm4567_routes),
+static const struct snd_soc_component_driver ssm4567_component_driver = {
+ .set_bias_level = ssm4567_set_bias_level,
+ .controls = ssm4567_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm4567_snd_controls),
+ .dapm_widgets = ssm4567_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets),
+ .dapm_routes = ssm4567_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm4567_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ssm4567_regmap_config = {
@@ -438,8 +444,7 @@ static const struct regmap_config ssm4567_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
};
-static int ssm4567_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ssm4567_i2c_probe(struct i2c_client *i2c)
{
struct ssm4567 *ssm4567;
int ret;
@@ -462,22 +467,24 @@ static int ssm4567_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev, &ssm4567_component_driver,
&ssm4567_dai, 1);
}
-static int ssm4567_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ssm4567_i2c_ids[] = {
- { "ssm4567", 0 },
+ { "ssm4567" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
+#ifdef CONFIG_OF
+static const struct of_device_id ssm4567_of_match[] = {
+ { .compatible = "adi,ssm4567", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssm4567_of_match);
+#endif
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id ssm4567_acpi_match[] = {
@@ -491,10 +498,10 @@ MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match);
static struct i2c_driver ssm4567_driver = {
.driver = {
.name = "ssm4567",
+ .of_match_table = of_match_ptr(ssm4567_of_match),
.acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
},
.probe = ssm4567_i2c_probe,
- .remove = ssm4567_i2c_remove,
.id_table = ssm4567_i2c_ids,
};
module_i2c_driver(ssm4567_driver);
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index a9844b2ac829..b9f9784f5164 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
*
@@ -9,11 +10,6 @@
* Mark Brown <broonie@opensource.wolfsonmicro.com>
* Freescale Semiconductor, Inc.
* Timur Tabi <timur@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
@@ -21,11 +17,11 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -51,12 +47,9 @@
SNDRV_PCM_RATE_192000)
#define STA32X_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
/* Power-up register defaults */
static const struct reg_default sta32x_regs[] = {
@@ -142,8 +135,9 @@ static const char *sta32x_supply_names[] = {
/* codec private data */
struct sta32x_priv {
struct regmap *regmap;
+ struct clk *xti_clk;
struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct sta32x_platform_data *pdata;
unsigned int mclk;
@@ -270,8 +264,8 @@ static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
int numcoef = kcontrol->private_value >> 16;
int index = kcontrol->private_value & 0xffff;
unsigned int cfud, val;
@@ -312,8 +306,8 @@ exit_unlock:
static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
int numcoef = kcontrol->private_value >> 16;
int index = kcontrol->private_value & 0xffff;
unsigned int cfud;
@@ -347,9 +341,9 @@ static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
+static int sta32x_sync_coef_shadow(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
unsigned int cfud;
int i;
@@ -375,16 +369,16 @@ static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
return 0;
}
-static int sta32x_cache_sync(struct snd_soc_codec *codec)
+static int sta32x_cache_sync(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
unsigned int mute;
int rc;
/* mute during register sync */
regmap_read(sta32x->regmap, STA32X_MMUTE, &mute);
regmap_write(sta32x->regmap, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE);
- sta32x_sync_coef_shadow(codec);
+ sta32x_sync_coef_shadow(component);
rc = regcache_sync(sta32x->regmap);
regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
return rc;
@@ -395,17 +389,17 @@ static void sta32x_watchdog(struct work_struct *work)
{
struct sta32x_priv *sta32x = container_of(work, struct sta32x_priv,
watchdog_work.work);
- struct snd_soc_codec *codec = sta32x->codec;
+ struct snd_soc_component *component = sta32x->component;
unsigned int confa, confa_cached;
/* check if sta32x has reset itself */
- confa_cached = snd_soc_read(codec, STA32X_CONFA);
+ confa_cached = snd_soc_component_read(component, STA32X_CONFA);
regcache_cache_bypass(sta32x->regmap, true);
- confa = snd_soc_read(codec, STA32X_CONFA);
+ confa = snd_soc_component_read(component, STA32X_CONFA);
regcache_cache_bypass(sta32x->regmap, false);
if (confa != confa_cached) {
regcache_mark_dirty(sta32x->regmap);
- sta32x_cache_sync(codec);
+ sta32x_cache_sync(component);
}
if (!sta32x->shutdown)
@@ -582,10 +576,10 @@ static int mcs_ratio_table[3][7] = {
static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "mclk=%u\n", freq);
+ dev_dbg(component->dev, "mclk=%u\n", freq);
sta32x->mclk = freq;
return 0;
@@ -602,12 +596,12 @@ static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
u8 confb = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -651,22 +645,22 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
int i, mcs = -EINVAL, ir = -EINVAL;
unsigned int confa, confb;
unsigned int rate, ratio;
int ret;
if (!sta32x->mclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"sta32x->mclk is unset. Unable to determine ratio\n");
return -EIO;
}
rate = params_rate(params);
ratio = sta32x->mclk / rate;
- dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio);
+ dev_dbg(component->dev, "rate: %u, ratio: %u\n", rate, ratio);
for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
if (interpolation_ratios[i].fs == rate) {
@@ -676,7 +670,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
}
if (ir < 0) {
- dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+ dev_err(component->dev, "Unsupported samplerate: %u\n", rate);
return -EINVAL;
}
@@ -688,7 +682,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
}
if (mcs < 0) {
- dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+ dev_err(component->dev, "Unresolvable ratio: %u\n", ratio);
return -EINVAL;
}
@@ -698,10 +692,10 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 24:
- dev_dbg(codec->dev, "24bit\n");
- /* fall through */
+ dev_dbg(component->dev, "24bit\n");
+ fallthrough;
case 32:
- dev_dbg(codec->dev, "24bit or 32bit\n");
+ dev_dbg(component->dev, "24bit or 32bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -716,7 +710,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
break;
case 20:
- dev_dbg(codec->dev, "20bit\n");
+ dev_dbg(component->dev, "20bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x4;
@@ -731,7 +725,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
break;
case 18:
- dev_dbg(codec->dev, "18bit\n");
+ dev_dbg(component->dev, "18bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x8;
@@ -746,7 +740,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
break;
case 16:
- dev_dbg(codec->dev, "16bit\n");
+ dev_dbg(component->dev, "16bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -793,20 +787,21 @@ static int sta32x_startup_sequence(struct sta32x_priv *sta32x)
/**
* sta32x_set_bias_level - DAPM callback
- * @codec: the codec device
+ * @component: the component device
* @level: DAPM power level
*
- * This is called by ALSA to put the codec into low power mode
- * or to wake it up. If the codec is powered off completely
+ * This is called by ALSA to put the component into low power mode
+ * or to wake it up. If the component is powered off completely
* all registers must be restored after power on.
*/
-static int sta32x_set_bias_level(struct snd_soc_codec *codec,
+static int sta32x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
int ret;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- dev_dbg(codec->dev, "level = %d\n", level);
+ dev_dbg(component->dev, "level = %d\n", level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
@@ -819,17 +814,17 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n", ret);
return ret;
}
sta32x_startup_sequence(sta32x);
- sta32x_cache_sync(codec);
+ sta32x_cache_sync(component);
sta32x_watchdog_start(sta32x);
}
@@ -847,8 +842,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
msleep(300);
sta32x_watchdog_stop(sta32x);
- if (sta32x->gpiod_nreset)
- gpiod_set_value(sta32x->gpiod_nreset, 0);
+ gpiod_set_value(sta32x->gpiod_nreset, 0);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
@@ -875,22 +869,35 @@ static struct snd_soc_dai_driver sta32x_dai = {
.ops = &sta32x_dai_ops,
};
-static int sta32x_probe(struct snd_soc_codec *codec)
+static int sta32x_probe(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
struct sta32x_platform_data *pdata = sta32x->pdata;
int i, ret = 0, thermal = 0;
+
+ sta32x->component = component;
+
+ if (sta32x->xti_clk) {
+ ret = clk_prepare_enable(sta32x->xti_clk);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to enable clock: %d\n", ret);
+ return ret;
+ }
+ }
+
ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
- return ret;
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_clk_disable_unprepare;
}
ret = sta32x_startup_sequence(sta32x);
if (ret < 0) {
- dev_err(codec->dev, "Failed to startup device\n");
- return ret;
+ dev_err(component->dev, "Failed to startup device\n");
+ goto err_regulator_bulk_disable;
}
/* CONFA */
@@ -969,34 +976,43 @@ static int sta32x_probe(struct snd_soc_codec *codec)
if (sta32x->pdata->needs_esd_watchdog)
INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
return 0;
+
+err_regulator_bulk_disable:
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+err_clk_disable_unprepare:
+ clk_disable_unprepare(sta32x->xti_clk);
+ return ret;
}
-static int sta32x_remove(struct snd_soc_codec *codec)
+static void sta32x_remove(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
sta32x_watchdog_stop(sta32x);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
- return 0;
+ clk_disable_unprepare(sta32x->xti_clk);
}
-static const struct snd_soc_codec_driver sta32x_codec = {
- .probe = sta32x_probe,
- .remove = sta32x_remove,
- .set_bias_level = sta32x_set_bias_level,
- .suspend_bias_off = true,
- .controls = sta32x_snd_controls,
- .num_controls = ARRAY_SIZE(sta32x_snd_controls),
- .dapm_widgets = sta32x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets),
- .dapm_routes = sta32x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes),
+static const struct snd_soc_component_driver sta32x_component = {
+ .probe = sta32x_probe,
+ .remove = sta32x_remove,
+ .set_bias_level = sta32x_set_bias_level,
+ .controls = sta32x_snd_controls,
+ .num_controls = ARRAY_SIZE(sta32x_snd_controls),
+ .dapm_widgets = sta32x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets),
+ .dapm_routes = sta32x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config sta32x_regmap = {
@@ -1005,7 +1021,7 @@ static const struct regmap_config sta32x_regmap = {
.max_register = STA32X_FDRC2,
.reg_defaults = sta32x_regs,
.num_reg_defaults = ARRAY_SIZE(sta32x_regs),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.wr_table = &sta32x_write_regs,
.rd_table = &sta32x_read_regs,
.volatile_table = &sta32x_volatile_regs,
@@ -1037,33 +1053,32 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
of_property_read_u8(np, "st,ch3-output-mapping",
&pdata->ch3_output_mapping);
- if (of_get_property(np, "st,thermal-warning-recovery", NULL))
- pdata->thermal_warning_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
- pdata->thermal_warning_adjustment = 1;
- if (of_get_property(np, "st,needs_esd_watchdog", NULL))
- pdata->needs_esd_watchdog = 1;
+ pdata->fault_detect_recovery =
+ of_property_read_bool(np, "st,fault-detect-recovery");
+ pdata->thermal_warning_recovery =
+ of_property_read_bool(np, "st,thermal-warning-recovery");
+ pdata->thermal_warning_adjustment =
+ of_property_read_bool(np, "st,thermal-warning-adjustment");
+ pdata->needs_esd_watchdog =
+ of_property_read_bool(np, "st,needs_esd_watchdog");
tmp = 140;
of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
/* CONFE */
- if (of_get_property(np, "st,max-power-use-mpcc", NULL))
- pdata->max_power_use_mpcc = 1;
-
- if (of_get_property(np, "st,max-power-correction", NULL))
- pdata->max_power_correction = 1;
-
- if (of_get_property(np, "st,am-reduction-mode", NULL))
- pdata->am_reduction_mode = 1;
-
- if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
- pdata->odd_pwm_speed_mode = 1;
+ pdata->max_power_use_mpcc =
+ of_property_read_bool(np, "st,max-power-use-mpcc");
+ pdata->max_power_correction =
+ of_property_read_bool(np, "st,max-power-correction");
+ pdata->am_reduction_mode =
+ of_property_read_bool(np, "st,am-reduction-mode");
+ pdata->odd_pwm_speed_mode =
+ of_property_read_bool(np, "st,odd-pwm-speed-mode");
/* CONFF */
- if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
- pdata->invalid_input_detect_mute = 1;
+ pdata->invalid_input_detect_mute =
+ of_property_read_bool(np, "st,invalid-input-detect-mute");
sta32x->pdata = pdata;
@@ -1071,8 +1086,7 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
}
#endif
-static int sta32x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta32x_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct sta32x_priv *sta32x;
@@ -1094,6 +1108,17 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
}
#endif
+ /* Clock */
+ sta32x->xti_clk = devm_clk_get(dev, "xti");
+ if (IS_ERR(sta32x->xti_clk)) {
+ ret = PTR_ERR(sta32x->xti_clk);
+
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ sta32x->xti_clk = NULL;
+ }
+
/* GPIOs */
sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
@@ -1120,23 +1145,18 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, sta32x);
- ret = snd_soc_register_codec(dev, &sta32x_codec, &sta32x_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &sta32x_component,
+ &sta32x_dai, 1);
if (ret < 0)
- dev_err(dev, "Failed to register codec (%d)\n", ret);
+ dev_err(dev, "Failed to register component (%d)\n", ret);
return ret;
}
-static int sta32x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id sta32x_i2c_id[] = {
- { "sta326", 0 },
- { "sta328", 0 },
- { "sta329", 0 },
+ { "sta326" },
+ { "sta328" },
+ { "sta329" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
@@ -1146,8 +1166,7 @@ static struct i2c_driver sta32x_i2c_driver = {
.name = "sta32x",
.of_match_table = of_match_ptr(st32x_dt_ids),
},
- .probe = sta32x_i2c_probe,
- .remove = sta32x_i2c_remove,
+ .probe = sta32x_i2c_probe,
.id_table = sta32x_i2c_id,
};
diff --git a/sound/soc/codecs/sta32x.h b/sound/soc/codecs/sta32x.h
index d3191c983d71..1b90d7460b4b 100644
--- a/sound/soc/codecs/sta32x.h
+++ b/sound/soc/codecs/sta32x.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
*
@@ -7,11 +8,6 @@
* based on code from:
* Wolfson Microelectronics PLC.
* Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#ifndef _ASOC_STA_32X_H
#define _ASOC_STA_32X_H
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 33a4612f0a07..71af82b099c0 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
*
@@ -11,11 +12,6 @@
* Mark Brown <broonie@opensource.wolfsonmicro.com>
* Freescale Semiconductor, Inc.
* Timur Tabi <timur@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
@@ -26,8 +22,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -52,12 +47,9 @@
SNDRV_PCM_RATE_192000)
#define STA350_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
/* Power-up register defaults */
static const struct reg_default sta350_regs[] = {
@@ -309,8 +301,8 @@ static int sta350_coefficient_info(struct snd_kcontrol *kcontrol,
static int sta350_coefficient_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
int numcoef = kcontrol->private_value >> 16;
int index = kcontrol->private_value & 0xffff;
unsigned int cfud, val;
@@ -351,8 +343,8 @@ exit_unlock:
static int sta350_coefficient_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
int numcoef = kcontrol->private_value >> 16;
int index = kcontrol->private_value & 0xffff;
unsigned int cfud;
@@ -386,9 +378,9 @@ static int sta350_coefficient_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static int sta350_sync_coef_shadow(struct snd_soc_codec *codec)
+static int sta350_sync_coef_shadow(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
unsigned int cfud;
int i;
@@ -414,16 +406,16 @@ static int sta350_sync_coef_shadow(struct snd_soc_codec *codec)
return 0;
}
-static int sta350_cache_sync(struct snd_soc_codec *codec)
+static int sta350_cache_sync(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
unsigned int mute;
int rc;
/* mute during register sync */
regmap_read(sta350->regmap, STA350_CFUD, &mute);
regmap_write(sta350->regmap, STA350_MMUTE, mute | STA350_MMUTE_MMUTE);
- sta350_sync_coef_shadow(codec);
+ sta350_sync_coef_shadow(component);
rc = regcache_sync(sta350->regmap);
regmap_write(sta350->regmap, STA350_MMUTE, mute);
return rc;
@@ -613,10 +605,10 @@ static int mcs_ratio_table[3][6] = {
static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "mclk=%u\n", freq);
+ dev_dbg(component->dev, "mclk=%u\n", freq);
sta350->mclk = freq;
return 0;
@@ -633,12 +625,12 @@ static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
unsigned int confb = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -682,22 +674,22 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
int i, mcs = -EINVAL, ir = -EINVAL;
unsigned int confa, confb;
unsigned int rate, ratio;
int ret;
if (!sta350->mclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"sta350->mclk is unset. Unable to determine ratio\n");
return -EIO;
}
rate = params_rate(params);
ratio = sta350->mclk / rate;
- dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio);
+ dev_dbg(component->dev, "rate: %u, ratio: %u\n", rate, ratio);
for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
if (interpolation_ratios[i].fs == rate) {
@@ -707,7 +699,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
}
if (ir < 0) {
- dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+ dev_err(component->dev, "Unsupported samplerate: %u\n", rate);
return -EINVAL;
}
@@ -719,7 +711,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
}
if (mcs < 0) {
- dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+ dev_err(component->dev, "Unresolvable ratio: %u\n", ratio);
return -EINVAL;
}
@@ -729,10 +721,10 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 24:
- dev_dbg(codec->dev, "24bit\n");
- /* fall through */
+ dev_dbg(component->dev, "24bit\n");
+ fallthrough;
case 32:
- dev_dbg(codec->dev, "24bit or 32bit\n");
+ dev_dbg(component->dev, "24bit or 32bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -747,7 +739,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
break;
case 20:
- dev_dbg(codec->dev, "20bit\n");
+ dev_dbg(component->dev, "20bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x4;
@@ -762,7 +754,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
break;
case 18:
- dev_dbg(codec->dev, "18bit\n");
+ dev_dbg(component->dev, "18bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x8;
@@ -777,7 +769,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
break;
case 16:
- dev_dbg(codec->dev, "16bit\n");
+ dev_dbg(component->dev, "16bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -827,20 +819,21 @@ static int sta350_startup_sequence(struct sta350_priv *sta350)
/**
* sta350_set_bias_level - DAPM callback
- * @codec: the codec device
+ * @component: the component device
* @level: DAPM power level
*
- * This is called by ALSA to put the codec into low power mode
- * or to wake it up. If the codec is powered off completely
+ * This is called by ALSA to put the component into low power mode
+ * or to wake it up. If the component is powered off completely
* all registers must be restored after power on.
*/
-static int sta350_set_bias_level(struct snd_soc_codec *codec,
+static int sta350_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
- dev_dbg(codec->dev, "level = %d\n", level);
+ dev_dbg(component->dev, "level = %d\n", level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
@@ -853,18 +846,18 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(
ARRAY_SIZE(sta350->supplies),
sta350->supplies);
if (ret < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
}
sta350_startup_sequence(sta350);
- sta350_cache_sync(codec);
+ sta350_cache_sync(component);
}
/* Power down */
@@ -911,22 +904,23 @@ static struct snd_soc_dai_driver sta350_dai = {
.ops = &sta350_dai_ops,
};
-static int sta350_probe(struct snd_soc_codec *codec)
+static int sta350_probe(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
struct sta350_platform_data *pdata = sta350->pdata;
int i, ret = 0, thermal = 0;
ret = regulator_bulk_enable(ARRAY_SIZE(sta350->supplies),
sta350->supplies);
if (ret < 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
ret = sta350_startup_sequence(sta350);
if (ret < 0) {
- dev_err(codec->dev, "Failed to startup device\n");
+ dev_err(component->dev, "Failed to startup device\n");
return ret;
}
@@ -1036,33 +1030,34 @@ static int sta350_probe(struct snd_soc_codec *codec)
sta350->coef_shadow[60] = 0x400000;
sta350->coef_shadow[61] = 0x400000;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
return 0;
}
-static int sta350_remove(struct snd_soc_codec *codec)
+static void sta350_remove(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
-
- return 0;
}
-static const struct snd_soc_codec_driver sta350_codec = {
- .probe = sta350_probe,
- .remove = sta350_remove,
- .set_bias_level = sta350_set_bias_level,
- .suspend_bias_off = true,
- .controls = sta350_snd_controls,
- .num_controls = ARRAY_SIZE(sta350_snd_controls),
- .dapm_widgets = sta350_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets),
- .dapm_routes = sta350_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes),
+static const struct snd_soc_component_driver sta350_component = {
+ .probe = sta350_probe,
+ .remove = sta350_remove,
+ .set_bias_level = sta350_set_bias_level,
+ .controls = sta350_snd_controls,
+ .num_controls = ARRAY_SIZE(sta350_snd_controls),
+ .dapm_widgets = sta350_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets),
+ .dapm_routes = sta350_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config sta350_regmap = {
@@ -1071,7 +1066,7 @@ static const struct regmap_config sta350_regmap = {
.max_register = STA350_MISC2,
.reg_defaults = sta350_regs,
.num_reg_defaults = ARRAY_SIZE(sta350_regs),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.wr_table = &sta350_write_regs,
.rd_table = &sta350_read_regs,
.volatile_table = &sta350_volatile_regs,
@@ -1112,12 +1107,12 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
of_property_read_u8(np, "st,ch3-output-mapping",
&pdata->ch3_output_mapping);
- if (of_get_property(np, "st,thermal-warning-recovery", NULL))
- pdata->thermal_warning_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
- pdata->thermal_warning_adjustment = 1;
- if (of_get_property(np, "st,fault-detect-recovery", NULL))
- pdata->fault_detect_recovery = 1;
+ pdata->thermal_warning_recovery =
+ of_property_read_bool(np, "st,thermal-warning-recovery");
+ pdata->thermal_warning_adjustment =
+ of_property_read_bool(np, "st,thermal-warning-adjustment");
+ pdata->fault_detect_recovery =
+ of_property_read_bool(np, "st,fault-detect-recovery");
pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP;
if (!of_property_read_string(np, "st,ffx-power-output-mode",
@@ -1139,41 +1134,34 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
- if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL))
- pdata->oc_warning_adjustment = 1;
+ pdata->oc_warning_adjustment =
+ of_property_read_bool(np, "st,overcurrent-warning-adjustment");
/* CONFE */
- if (of_get_property(np, "st,max-power-use-mpcc", NULL))
- pdata->max_power_use_mpcc = 1;
-
- if (of_get_property(np, "st,max-power-correction", NULL))
- pdata->max_power_correction = 1;
-
- if (of_get_property(np, "st,am-reduction-mode", NULL))
- pdata->am_reduction_mode = 1;
-
- if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
- pdata->odd_pwm_speed_mode = 1;
-
- if (of_get_property(np, "st,distortion-compensation", NULL))
- pdata->distortion_compensation = 1;
+ pdata->max_power_use_mpcc =
+ of_property_read_bool(np, "st,max-power-use-mpcc");
+ pdata->max_power_correction =
+ of_property_read_bool(np, "st,max-power-correction");
+ pdata->am_reduction_mode =
+ of_property_read_bool(np, "st,am-reduction-mode");
+ pdata->odd_pwm_speed_mode =
+ of_property_read_bool(np, "st,odd-pwm-speed-mode");
+ pdata->distortion_compensation =
+ of_property_read_bool(np, "st,distortion-compensation");
/* CONFF */
- if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
- pdata->invalid_input_detect_mute = 1;
+ pdata->invalid_input_detect_mute =
+ of_property_read_bool(np, "st,invalid-input-detect-mute");
/* MISC */
- if (of_get_property(np, "st,activate-mute-output", NULL))
- pdata->activate_mute_output = 1;
-
- if (of_get_property(np, "st,bridge-immediate-off", NULL))
- pdata->bridge_immediate_off = 1;
-
- if (of_get_property(np, "st,noise-shape-dc-cut", NULL))
- pdata->noise_shape_dc_cut = 1;
-
- if (of_get_property(np, "st,powerdown-master-volume", NULL))
- pdata->powerdown_master_vol = 1;
+ pdata->activate_mute_output =
+ of_property_read_bool(np, "st,activate-mute-output");
+ pdata->bridge_immediate_off =
+ of_property_read_bool(np, "st,bridge-immediate-off");
+ pdata->noise_shape_dc_cut =
+ of_property_read_bool(np, "st,noise-shape-dc-cut");
+ pdata->powerdown_master_vol =
+ of_property_read_bool(np, "st,powerdown-master-volume");
if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) {
if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128)
@@ -1189,8 +1177,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
}
#endif
-static int sta350_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta350_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct sta350_priv *sta350;
@@ -1242,21 +1229,18 @@ static int sta350_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, sta350);
- ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &sta350_component, &sta350_dai, 1);
if (ret < 0)
- dev_err(dev, "Failed to register codec (%d)\n", ret);
+ dev_err(dev, "Failed to register component (%d)\n", ret);
return ret;
}
-static int sta350_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+static void sta350_i2c_remove(struct i2c_client *client)
+{}
static const struct i2c_device_id sta350_i2c_id[] = {
- { "sta350", 0 },
+ { "sta350" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h
index fb7285290779..80bf56093d94 100644
--- a/sound/soc/codecs/sta350.h
+++ b/sound/soc/codecs/sta350.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
*
@@ -9,16 +10,11 @@
* Johannes Stezenbach <js@sig21.net>
* Wolfson Microelectronics PLC.
* Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#ifndef _ASOC_STA_350_H
#define _ASOC_STA_350_H
-/* STA50 register addresses */
+/* STA350 register addresses */
#define STA350_REGISTER_COUNT 0x4D
#define STA350_COEF_COUNT 62
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 2cdaca943a8c..8c3b2652b02e 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -151,28 +151,29 @@ static const struct snd_kcontrol_new sta529_snd_controls[] = {
SOC_ENUM("PWM Select", pwm_src),
};
-static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
+static int sta529_set_bias_level(struct snd_soc_component *component, enum
snd_soc_bias_level level)
{
- struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec);
+ struct sta529 *sta529 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK,
+ snd_soc_component_update_bits(component, STA529_FFXCFG0, POWER_CNTLMSAK,
POWER_UP);
- snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK,
FFX_CLK_ENB);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
regcache_sync(sta529->regmap);
- snd_soc_update_bits(codec, STA529_FFXCFG0,
+ snd_soc_component_update_bits(component, STA529_FFXCFG0,
POWER_CNTLMSAK, POWER_STDBY);
/* Making FFX output to zero */
- snd_soc_update_bits(codec, STA529_FFXCFG0, FFX_MASK,
+ snd_soc_component_update_bits(component, STA529_FFXCFG0, FFX_MASK,
FFX_OFF);
- snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK,
FFX_CLK_DIS);
break;
case SND_SOC_BIAS_OFF:
@@ -187,7 +188,7 @@ static int sta529_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int pdata, play_freq_val, record_freq_val;
int bclk_to_fs_ratio;
@@ -205,7 +206,7 @@ static int sta529_hw_params(struct snd_pcm_substream *substream,
bclk_to_fs_ratio = 2;
break;
default:
- dev_err(codec->dev, "Unsupported format\n");
+ dev_err(component->dev, "Unsupported format\n");
return -EINVAL;
}
@@ -228,44 +229,44 @@ static int sta529_hw_params(struct snd_pcm_substream *substream,
record_freq_val = 0;
break;
default:
- dev_err(codec->dev, "Unsupported rate\n");
+ dev_err(component->dev, "Unsupported rate\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_soc_update_bits(codec, STA529_S2PCFG1, PDATA_LEN_MSK,
+ snd_soc_component_update_bits(component, STA529_S2PCFG1, PDATA_LEN_MSK,
pdata << 6);
- snd_soc_update_bits(codec, STA529_S2PCFG1, BCLK_TO_FS_MSK,
+ snd_soc_component_update_bits(component, STA529_S2PCFG1, BCLK_TO_FS_MSK,
bclk_to_fs_ratio << 4);
- snd_soc_update_bits(codec, STA529_MISC, PLAY_FREQ_RANGE_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, PLAY_FREQ_RANGE_MSK,
play_freq_val << 4);
} else {
- snd_soc_update_bits(codec, STA529_P2SCFG1, PDATA_LEN_MSK,
+ snd_soc_component_update_bits(component, STA529_P2SCFG1, PDATA_LEN_MSK,
pdata << 6);
- snd_soc_update_bits(codec, STA529_P2SCFG1, BCLK_TO_FS_MSK,
+ snd_soc_component_update_bits(component, STA529_P2SCFG1, BCLK_TO_FS_MSK,
bclk_to_fs_ratio << 4);
- snd_soc_update_bits(codec, STA529_MISC, CAP_FREQ_RANGE_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, CAP_FREQ_RANGE_MSK,
record_freq_val << 2);
}
return 0;
}
-static int sta529_mute(struct snd_soc_dai *dai, int mute)
+static int sta529_mute(struct snd_soc_dai *dai, int mute, int direction)
{
u8 val = 0;
if (mute)
val |= CODEC_MUTE_VAL;
- snd_soc_update_bits(dai->codec, STA529_FFXCFG0, AUDIO_MUTE_MSK, val);
+ snd_soc_component_update_bits(dai->component, STA529_FFXCFG0, AUDIO_MUTE_MSK, val);
return 0;
}
static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode = 0;
/* interface format */
@@ -283,7 +284,7 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
+ snd_soc_component_update_bits(component, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
return 0;
}
@@ -291,7 +292,8 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
static const struct snd_soc_dai_ops sta529_dai_ops = {
.hw_params = sta529_hw_params,
.set_fmt = sta529_set_dai_fmt,
- .digital_mute = sta529_mute,
+ .mute_stream = sta529_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver sta529_dai = {
@@ -313,12 +315,14 @@ static struct snd_soc_dai_driver sta529_dai = {
.ops = &sta529_dai_ops,
};
-static const struct snd_soc_codec_driver sta529_codec_driver = {
- .set_bias_level = sta529_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = sta529_snd_controls,
- .num_controls = ARRAY_SIZE(sta529_snd_controls),
+static const struct snd_soc_component_driver sta529_component_driver = {
+ .set_bias_level = sta529_set_bias_level,
+ .controls = sta529_snd_controls,
+ .num_controls = ARRAY_SIZE(sta529_snd_controls),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config sta529_regmap = {
@@ -328,13 +332,12 @@ static const struct regmap_config sta529_regmap = {
.max_register = STA529_MAX_REGISTER,
.readable_reg = sta529_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = sta529_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults),
};
-static int sta529_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta529_i2c_probe(struct i2c_client *i2c)
{
struct sta529 *sta529;
int ret;
@@ -352,33 +355,32 @@ static int sta529_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, sta529);
- ret = snd_soc_register_codec(&i2c->dev,
- &sta529_codec_driver, &sta529_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &sta529_component_driver, &sta529_dai, 1);
if (ret != 0)
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
return ret;
}
-static int sta529_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id sta529_i2c_id[] = {
- { "sta529", 0 },
+ { "sta529" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
+static const struct of_device_id sta529_of_match[] = {
+ { .compatible = "st,sta529", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sta529_of_match);
+
static struct i2c_driver sta529_i2c_driver = {
.driver = {
.name = "sta529",
+ .of_match_table = sta529_of_match,
},
.probe = sta529_i2c_probe,
- .remove = sta529_i2c_remove,
.id_table = sta529_i2c_id,
};
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 0945c51df003..2f9f10a4dfed 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* stac9766.c -- ALSA SoC STAC9766 codec support
*
* Copyright 2009 Jon Smirl, Digispeaker
* Author: Jon Smirl <jonsmirl@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Features:-
*
* o Support for AC97 Codec, S/PDIF
@@ -18,6 +14,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -26,31 +23,56 @@
#include <sound/soc.h>
#include <sound/tlv.h>
-#include "stac9766.h"
-
#define STAC9766_VENDOR_ID 0x83847666
#define STAC9766_VENDOR_ID_MASK 0xffffffff
-/*
- * 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 */
+#define AC97_STAC_DA_CONTROL 0x6A
+#define AC97_STAC_ANALOG_SPECIAL 0x6E
+#define AC97_STAC_STEREO_MIC 0x78
+
+static const struct reg_default stac9766_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x0a, 0x0000 },
+ { 0x0c, 0x8008 },
+ { 0x0e, 0x8008 },
+ { 0x10, 0x8808 },
+ { 0x12, 0x8808 },
+ { 0x14, 0x8808 },
+ { 0x16, 0x8808 },
+ { 0x18, 0x8808 },
+ { 0x1a, 0x0000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x22, 0x0000 },
+ { 0x28, 0x0a05 },
+ { 0x2c, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x3a, 0x2000 },
+ { 0x3e, 0x0100 },
+ { 0x4c, 0x0300 },
+ { 0x4e, 0xffff },
+ { 0x50, 0x0000 },
+ { 0x52, 0x0000 },
+ { 0x54, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6e, 0x1000 },
+ { 0x72, 0x0000 },
+ { 0x78, 0x0000 },
+};
+
+static const struct regmap_config stac9766_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x78,
+ .cache_type = REGCACHE_MAPLE,
+
+ .volatile_reg = regmap_ac97_default_volatile,
+
+ .reg_defaults = stac9766_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(stac9766_reg_defaults),
};
static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX",
@@ -85,10 +107,10 @@ static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum,
static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum,
AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic);
-static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
-static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
-static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
-static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(master_tlv, -4650, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(record_tlv, 0, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(beep_tlv, -4500, 300, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mix_tlv, -3450, 150, 0);
static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
@@ -139,112 +161,61 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
};
-static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- u16 *cache = codec->reg_cache;
-
- if (reg > AC97_STAC_PAGE0) {
- stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops->write(ac97, reg, val);
- stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
- return 0;
- }
- if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
- return -EIO;
-
- soc_ac97_ops->write(ac97, reg, val);
- cache[reg / 2] = val;
- return 0;
-}
-
-static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- u16 val = 0, *cache = codec->reg_cache;
-
- if (reg > AC97_STAC_PAGE0) {
- stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
- stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
- return val;
- }
- if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
- return -EIO;
-
- if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
- reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
- reg == AC97_VENDOR_ID2) {
-
- val = soc_ac97_ops->read(ac97, reg);
- return val;
- }
- return cache[reg / 2];
-}
-
static int ac97_analog_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned short reg, vra;
-
- vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+ unsigned short reg;
- vra |= 0x1; /* enable variable rate audio */
- vra &= ~0x4; /* disable SPDIF output */
-
- stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+ /* enable variable rate audio, disable SPDIF output */
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x5, 0x1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else
reg = AC97_PCM_LR_ADC_RATE;
- return stac9766_ac97_write(codec, reg, runtime->rate);
+ return snd_soc_component_write(component, reg, runtime->rate);
}
static int ac97_digital_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned short reg, vra;
-
- stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
+ unsigned short reg;
- vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
- vra |= 0x5; /* Enable VRA and SPDIF out */
+ snd_soc_component_write(component, AC97_SPDIF, 0x2002);
- stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+ /* Enable VRA and SPDIF out */
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x5, 0x5);
reg = AC97_PCM_FRONT_DAC_RATE;
- return stac9766_ac97_write(codec, reg, runtime->rate);
+ return snd_soc_component_write(component, reg, runtime->rate);
}
-static int stac9766_set_bias_level(struct snd_soc_codec *codec,
+static int stac9766_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON: /* full On */
case SND_SOC_BIAS_PREPARE: /* partial On */
case SND_SOC_BIAS_STANDBY: /* Off, with power */
- stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0x0000);
break;
case SND_SOC_BIAS_OFF: /* Off, without power */
/* disable everything including AC link */
- stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
break;
}
return 0;
}
-static int stac9766_codec_resume(struct snd_soc_codec *codec)
+static int stac9766_component_resume(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
return snd_ac97_reset(ac97, true, STAC9766_VENDOR_ID,
STAC9766_VENDOR_ID_MASK);
@@ -297,54 +268,57 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
}
};
-static int stac9766_codec_probe(struct snd_soc_codec *codec)
+static int stac9766_component_probe(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
+ struct regmap *regmap;
+ int ret;
- ac97 = snd_soc_new_ac97_codec(codec, STAC9766_VENDOR_ID,
+ ac97 = snd_soc_new_ac97_component(component, STAC9766_VENDOR_ID,
STAC9766_VENDOR_ID_MASK);
if (IS_ERR(ac97))
return PTR_ERR(ac97);
- snd_soc_codec_set_drvdata(codec, ac97);
+ regmap = regmap_init_ac97(ac97, &stac9766_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_free_ac97;
+ }
+
+ snd_soc_component_init_regmap(component, regmap);
+ snd_soc_component_set_drvdata(component, ac97);
return 0;
+err_free_ac97:
+ snd_soc_free_ac97_component(ac97);
+ return ret;
}
-static int stac9766_codec_remove(struct snd_soc_codec *codec)
+static void stac9766_component_remove(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
- snd_soc_free_ac97_codec(ac97);
- return 0;
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(ac97);
}
-static struct snd_soc_codec_driver soc_codec_dev_stac9766 = {
- .controls = stac9766_snd_ac97_controls,
- .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls),
- .write = stac9766_ac97_write,
- .read = stac9766_ac97_read,
- .set_bias_level = stac9766_set_bias_level,
- .suspend_bias_off = true,
- .probe = stac9766_codec_probe,
- .remove = stac9766_codec_remove,
- .resume = stac9766_codec_resume,
- .reg_cache_size = ARRAY_SIZE(stac9766_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_step = 2,
- .reg_cache_default = stac9766_reg,
+static const struct snd_soc_component_driver soc_component_dev_stac9766 = {
+ .controls = stac9766_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls),
+ .set_bias_level = stac9766_set_bias_level,
+ .probe = stac9766_component_probe,
+ .remove = stac9766_component_remove,
+ .resume = stac9766_component_resume,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int stac9766_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_stac9766, stac9766_dai, ARRAY_SIZE(stac9766_dai));
-}
-
-static int stac9766_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_stac9766, stac9766_dai, ARRAY_SIZE(stac9766_dai));
}
static struct platform_driver stac9766_codec_driver = {
@@ -353,7 +327,6 @@ static struct platform_driver stac9766_codec_driver = {
},
.probe = stac9766_probe,
- .remove = stac9766_remove,
};
module_platform_driver(stac9766_codec_driver);
diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h
deleted file mode 100644
index c726f907e2c0..000000000000
--- a/sound/soc/codecs/stac9766.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * stac9766.h -- STAC9766 Soc Audio driver
- */
-
-#ifndef _STAC9766_H
-#define _STAC9766_H
-
-#define AC97_STAC_PAGE0 0x1000
-#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A)
-#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E)
-#define AC97_STAC_STEREO_MIC 0x78
-
-/* STAC9766 DAI ID's */
-#define STAC9766_DAI_AC97_ANALOG 0
-#define STAC9766_DAI_AC97_DIGITAL 1
-
-#endif
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
index 160d61a66204..4ab15be69f3a 100644
--- a/sound/soc/codecs/sti-sas.c
+++ b/sound/soc/codecs/sti-sas.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) STMicroelectronics SA 2015
* Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
* for STMicroelectronics.
- * License terms: GNU General Public License (GPL), version 2
*/
#include <linux/io.h>
@@ -14,28 +14,8 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-/* chipID supported */
-#define CHIPID_STIH416 0
-#define CHIPID_STIH407 1
-
/* DAC definitions */
-/* stih416 DAC registers */
-/* sysconf 2517: Audio-DAC-Control */
-#define STIH416_AUDIO_DAC_CTRL 0x00000814
-/* sysconf 2519: Audio-Gue-Control */
-#define STIH416_AUDIO_GLUE_CTRL 0x0000081C
-
-#define STIH416_DAC_NOT_STANDBY 0x3
-#define STIH416_DAC_SOFTMUTE 0x4
-#define STIH416_DAC_ANA_NOT_PWR 0x5
-#define STIH416_DAC_NOT_PNDBG 0x6
-
-#define STIH416_DAC_NOT_STANDBY_MASK BIT(STIH416_DAC_NOT_STANDBY)
-#define STIH416_DAC_SOFTMUTE_MASK BIT(STIH416_DAC_SOFTMUTE)
-#define STIH416_DAC_ANA_NOT_PWR_MASK BIT(STIH416_DAC_ANA_NOT_PWR)
-#define STIH416_DAC_NOT_PNDBG_MASK BIT(STIH416_DAC_NOT_PNDBG)
-
/* stih407 DAC registers */
/* sysconf 5041: Audio-Gue-Control */
#define STIH407_AUDIO_GLUE_CTRL 0x000000A4
@@ -63,39 +43,26 @@ enum {
STI_SAS_DAI_ANALOG_OUT,
};
-static const struct reg_default stih416_sas_reg_defaults[] = {
- { STIH407_AUDIO_GLUE_CTRL, 0x00000040 },
- { STIH407_AUDIO_DAC_CTRL, 0x000000000 },
-};
-
static const struct reg_default stih407_sas_reg_defaults[] = {
- { STIH416_AUDIO_DAC_CTRL, 0x000000000 },
- { STIH416_AUDIO_GLUE_CTRL, 0x00000040 },
+ { STIH407_AUDIO_DAC_CTRL, 0x000000000 },
+ { STIH407_AUDIO_GLUE_CTRL, 0x00000040 },
};
struct sti_dac_audio {
struct regmap *regmap;
struct regmap *virt_regmap;
- struct regmap_field **field;
- struct reset_control *rst;
int mclk;
};
struct sti_spdif_audio {
struct regmap *regmap;
- struct regmap_field **field;
int mclk;
};
/* device data structure */
struct sti_sas_dev_data {
- const int chipid; /* IC version */
const struct regmap_config *regmap;
const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */
- const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */
- const int num_dapm_widgets; /* dapms declaration */
- const struct snd_soc_dapm_route *dapm_routes; /* route declaration */
- const int num_dapm_routes; /* route declaration */
};
/* driver data structure */
@@ -125,14 +92,11 @@ static int sti_sas_write_reg(void *context, unsigned int reg,
unsigned int value)
{
struct sti_sas_data *drvdata = context;
- int status;
-
- status = regmap_write(drvdata->dac.regmap, reg, value);
- return status;
+ return regmap_write(drvdata->dac.regmap, reg, value);
}
-static int sti_sas_init_sas_registers(struct snd_soc_codec *codec,
+static int sti_sas_init_sas_registers(struct snd_soc_component *component,
struct sti_sas_data *data)
{
int ret;
@@ -142,59 +106,35 @@ static int sti_sas_init_sas_registers(struct snd_soc_codec *codec,
*/
/* Initialise bi-phase formatter to disabled */
- ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
SPDIF_BIPHASE_ENABLE_MASK, 0);
if (!ret)
/* Initialise bi-phase formatter idle value to 0 */
- ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
SPDIF_BIPHASE_IDLE_MASK, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to update SPDIF registers");
+ dev_err(component->dev, "Failed to update SPDIF registers\n");
return ret;
}
/* Init DAC configuration */
- switch (data->dev_data->chipid) {
- case CHIPID_STIH407:
- /* init configuration */
- ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
- STIH407_DAC_STANDBY_MASK,
- STIH407_DAC_STANDBY_MASK);
-
- if (!ret)
- ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
- STIH407_DAC_STANDBY_ANA_MASK,
- STIH407_DAC_STANDBY_ANA_MASK);
- if (!ret)
- ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
- STIH407_DAC_SOFTMUTE_MASK,
- STIH407_DAC_SOFTMUTE_MASK);
- break;
- case CHIPID_STIH416:
- ret = snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_NOT_STANDBY_MASK, 0);
- if (!ret)
- ret = snd_soc_update_bits(codec,
- STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_ANA_NOT_PWR, 0);
- if (!ret)
- ret = snd_soc_update_bits(codec,
- STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_NOT_PNDBG_MASK,
- 0);
- if (!ret)
- ret = snd_soc_update_bits(codec,
- STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_SOFTMUTE_MASK,
- STIH416_DAC_SOFTMUTE_MASK);
- break;
- default:
- return -EINVAL;
- }
+ /* init configuration */
+ ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_STANDBY_MASK,
+ STIH407_DAC_STANDBY_MASK);
+
+ if (!ret)
+ ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_STANDBY_ANA_MASK,
+ STIH407_DAC_STANDBY_ANA_MASK);
+ if (!ret)
+ ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
+ STIH407_DAC_SOFTMUTE_MASK,
+ STIH407_DAC_SOFTMUTE_MASK);
if (ret < 0) {
- dev_err(codec->dev, "Failed to update DAC registers");
+ dev_err(component->dev, "Failed to update DAC registers\n");
return ret;
}
@@ -207,47 +147,16 @@ static int sti_sas_init_sas_registers(struct snd_soc_codec *codec,
static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
/* Sanity check only */
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(dai->codec->dev,
- "%s: ERROR: Unsupporter master mask 0x%x\n",
- __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupported clocking 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
return -EINVAL;
}
return 0;
}
-static int stih416_dac_probe(struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
- struct sti_dac_audio *dac = &drvdata->dac;
-
- /* Get reset control */
- dac->rst = devm_reset_control_get(codec->dev, "dac_rst");
- if (IS_ERR(dac->rst)) {
- dev_err(dai->codec->dev,
- "%s: ERROR: DAC reset control not defined !\n",
- __func__);
- dac->rst = NULL;
- return -EFAULT;
- }
- /* Put the DAC into reset */
- reset_control_assert(dac->rst);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = {
- SND_SOC_DAPM_PGA("DAC bandgap", STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_NOT_PNDBG_MASK, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_ANA_NOT_PWR, 0, NULL, 0),
- SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_NOT_STANDBY, 0),
- SND_SOC_DAPM_OUTPUT("DAC Output"),
-};
-
static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = {
SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL,
STIH407_DAC_STANDBY_ANA, 1, NULL, 0),
@@ -256,41 +165,22 @@ static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("DAC Output"),
};
-static const struct snd_soc_dapm_route stih416_sas_route[] = {
- {"DAC Output", NULL, "DAC bandgap"},
- {"DAC Output", NULL, "DAC standby ana"},
- {"DAC standby ana", NULL, "DAC standby"},
-};
-
static const struct snd_soc_dapm_route stih407_sas_route[] = {
{"DAC Output", NULL, "DAC standby ana"},
{"DAC standby ana", NULL, "DAC standby"},
};
-static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
-{
- struct snd_soc_codec *codec = dai->codec;
-
- if (mute) {
- return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_SOFTMUTE_MASK,
- STIH416_DAC_SOFTMUTE_MASK);
- } else {
- return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL,
- STIH416_DAC_SOFTMUTE_MASK, 0);
- }
-}
static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
STIH407_DAC_SOFTMUTE_MASK,
STIH407_DAC_SOFTMUTE_MASK);
} else {
- return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL,
+ return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
STIH407_DAC_SOFTMUTE_MASK,
0);
}
@@ -302,10 +192,10 @@ static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(dai->codec->dev,
- "%s: ERROR: Unsupporter master mask 0x%x\n",
- __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupported clocking mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
return -EINVAL;
}
@@ -321,19 +211,19 @@ static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai,
static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
SPDIF_BIPHASE_ENABLE_MASK,
SPDIF_BIPHASE_ENABLE_MASK);
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL,
+ return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
SPDIF_BIPHASE_ENABLE_MASK,
0);
default:
@@ -360,8 +250,8 @@ static bool sti_sas_volatile_register(struct device *dev, unsigned int reg)
static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = dai->component;
+ struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
if (dir == SND_SOC_CLOCK_OUT)
return 0;
@@ -385,20 +275,20 @@ static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id,
static int sti_sas_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = dai->component;
+ struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
struct snd_pcm_runtime *runtime = substream->runtime;
switch (dai->id) {
case STI_SAS_DAI_SPDIF_OUT:
if ((drvdata->spdif.mclk / runtime->rate) != 128) {
- dev_err(codec->dev, "unexpected mclk-fs ratio");
+ dev_err(component->dev, "unexpected mclk-fs ratio\n");
return -EINVAL;
}
break;
case STI_SAS_DAI_ANALOG_OUT:
if ((drvdata->dac.mclk / runtime->rate) != 256) {
- dev_err(codec->dev, "unexpected mclk-fs ratio");
+ dev_err(component->dev, "unexpected mclk-fs ratio\n");
return -EINVAL;
}
break;
@@ -407,13 +297,6 @@ static int sti_sas_prepare(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_soc_dai_ops stih416_dac_ops = {
- .set_fmt = sti_sas_dac_set_fmt,
- .mute_stream = stih416_sas_dac_mute,
- .prepare = sti_sas_prepare,
- .set_sysclk = sti_sas_set_sysclk,
-};
-
static const struct snd_soc_dai_ops stih407_dac_ops = {
.set_fmt = sti_sas_dac_set_fmt,
.mute_stream = stih407_sas_dac_mute,
@@ -424,47 +307,19 @@ static const struct snd_soc_dai_ops stih407_dac_ops = {
static const struct regmap_config stih407_sas_regmap = {
.reg_bits = 32,
.val_bits = 32,
-
+ .fast_io = true,
.max_register = STIH407_AUDIO_DAC_CTRL,
.reg_defaults = stih407_sas_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults),
.volatile_reg = sti_sas_volatile_register,
- .cache_type = REGCACHE_RBTREE,
- .reg_read = sti_sas_read_reg,
- .reg_write = sti_sas_write_reg,
-};
-
-static const struct regmap_config stih416_sas_regmap = {
- .reg_bits = 32,
- .val_bits = 32,
-
- .max_register = STIH416_AUDIO_DAC_CTRL,
- .reg_defaults = stih416_sas_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(stih416_sas_reg_defaults),
- .volatile_reg = sti_sas_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_read = sti_sas_read_reg,
.reg_write = sti_sas_write_reg,
};
-static const struct sti_sas_dev_data stih416_data = {
- .chipid = CHIPID_STIH416,
- .regmap = &stih416_sas_regmap,
- .dac_ops = &stih416_dac_ops,
- .dapm_widgets = stih416_sas_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets),
- .dapm_routes = stih416_sas_route,
- .num_dapm_routes = ARRAY_SIZE(stih416_sas_route),
-};
-
static const struct sti_sas_dev_data stih407_data = {
- .chipid = CHIPID_STIH407,
.regmap = &stih407_sas_regmap,
.dac_ops = &stih407_dac_ops,
- .dapm_widgets = stih407_sas_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets),
- .dapm_routes = stih407_sas_route,
- .num_dapm_routes = ARRAY_SIZE(stih407_sas_route),
};
static struct snd_soc_dai_driver sti_sas_dai[] = {
@@ -506,42 +361,43 @@ static struct snd_soc_dai_driver sti_sas_dai[] = {
};
#ifdef CONFIG_PM_SLEEP
-static int sti_sas_resume(struct snd_soc_codec *codec)
+static int sti_sas_resume(struct snd_soc_component *component)
{
- struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
- return sti_sas_init_sas_registers(codec, drvdata);
+ return sti_sas_init_sas_registers(component, drvdata);
}
#else
#define sti_sas_resume NULL
#endif
-static int sti_sas_codec_probe(struct snd_soc_codec *codec)
+static int sti_sas_component_probe(struct snd_soc_component *component)
{
- struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
- int ret;
+ struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
- ret = sti_sas_init_sas_registers(codec, drvdata);
-
- return ret;
+ return sti_sas_init_sas_registers(component, drvdata);
}
-static struct snd_soc_codec_driver sti_sas_driver = {
- .probe = sti_sas_codec_probe,
- .resume = sti_sas_resume,
+static const struct snd_soc_component_driver sti_sas_driver = {
+ .probe = sti_sas_component_probe,
+ .resume = sti_sas_resume,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .dapm_widgets = stih407_sas_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets),
+ .dapm_routes = stih407_sas_route,
+ .num_dapm_routes = ARRAY_SIZE(stih407_sas_route),
};
static const struct of_device_id sti_sas_dev_match[] = {
{
- .compatible = "st,stih416-sas-codec",
- .data = &stih416_data,
- },
- {
.compatible = "st,stih407-sas-codec",
.data = &stih407_data,
},
{},
};
+MODULE_DEVICE_TABLE(of, sti_sas_dev_match);
static int sti_sas_driver_probe(struct platform_device *pdev)
{
@@ -558,7 +414,7 @@ static int sti_sas_driver_probe(struct platform_device *pdev)
/* Populate data structure depending on compatibility */
of_id = of_match_node(sti_sas_dev_match, pnode);
if (!of_id->data) {
- dev_err(&pdev->dev, "data associated to device is missing");
+ dev_err(&pdev->dev, "data associated to device is missing\n");
return -EINVAL;
}
@@ -584,41 +440,22 @@ static int sti_sas_driver_probe(struct platform_device *pdev)
}
drvdata->spdif.regmap = drvdata->dac.regmap;
- /* Set DAC dai probe */
- if (drvdata->dev_data->chipid == CHIPID_STIH416)
- sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe;
-
sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops;
- /* Set dapms*/
- sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets;
- sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets;
-
- sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes;
- sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes;
-
/* Store context */
dev_set_drvdata(&pdev->dev, drvdata);
- return snd_soc_register_codec(&pdev->dev, &sti_sas_driver,
+ return devm_snd_soc_register_component(&pdev->dev, &sti_sas_driver,
sti_sas_dai,
ARRAY_SIZE(sti_sas_dai));
}
-static int sti_sas_driver_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver sti_sas_platform_driver = {
.driver = {
.name = "sti-sas-codec",
.of_match_table = sti_sas_dev_match,
},
.probe = sti_sas_driver_probe,
- .remove = sti_sas_driver_remove,
};
module_platform_driver(sti_sas_platform_driver);
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index cc1d3981fa4b..43449d7c2584 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -1,26 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 - 2024 Texas Instruments Incorporated -
+ * https://www.ti.com
*
* Author: Dan Murphy <dmurphy@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -70,7 +61,7 @@ static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
};
struct tas2552_data {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regmap *regmap;
struct i2c_client *tas2552_client;
struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
@@ -88,22 +79,22 @@ struct tas2552_data {
static int tas2552_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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0);
- snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5),
+ snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xc0);
+ snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5),
(1 << 5));
- snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0);
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0);
+ snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 0);
+ snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS, 0);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS,
+ snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS,
TAS2552_SWS);
- snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1);
- snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
- snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe);
+ snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 1);
+ snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
+ snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xbe);
break;
}
return 0;
@@ -127,12 +118,14 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
&tas2552_input_mux_control),
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI OUT", "DAC Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
- SND_SOC_DAPM_OUTPUT("OUT")
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC")
};
static const struct snd_soc_dapm_route tas2552_audio_map[] = {
@@ -142,28 +135,27 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
{"ClassD", NULL, "Input selection"},
{"OUT", NULL, "ClassD"},
{"ClassD", NULL, "PLL"},
+ {"ASI OUT", NULL, "DMIC"}
};
-#ifdef CONFIG_PM
static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
{
u8 cfg1_reg = 0;
- if (!tas2552->codec)
+ if (!tas2552->component)
return;
if (sw_shutdown)
cfg1_reg = TAS2552_SWS;
- snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS,
+ snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS,
cfg1_reg);
}
-#endif
-static int tas2552_setup_pll(struct snd_soc_codec *codec,
+static int tas2552_setup_pll(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
bool bypass_pll = false;
unsigned int pll_clk = params_rate(params) * 512;
unsigned int pll_clkin = tas2552->pll_clkin;
@@ -177,32 +169,35 @@ static int tas2552_setup_pll(struct snd_soc_codec *codec,
pll_clkin += tas2552->tdm_delay;
}
- pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
- snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
+ pll_enable = snd_soc_component_read(component, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
+ snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
if (pll_clkin == pll_clk)
bypass_pll = true;
if (bypass_pll) {
/* By pass the PLL configuration */
- snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
+ snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_2,
TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
} else {
/* Fill in the PLL control registers for J & D
* pll_clk = (.5 * pll_clkin * J.D) / 2^p
* Need to fill in J and D here based on incoming freq
*/
- unsigned int d;
+ unsigned int d, q, t;
u8 j;
u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
- u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
+ u8 p = snd_soc_component_read(component, TAS2552_PLL_CTRL_1);
p = (p >> 7);
recalc:
- j = (pll_clk * 2 * (1 << p)) / pll_clkin;
- d = (pll_clk * 2 * (1 << p)) % pll_clkin;
- d /= (pll_clkin / 10000);
+ t = (pll_clk * 2) << p;
+ j = t / pll_clkin;
+ d = t % pll_clkin;
+ t = pll_clkin / 10000;
+ q = d / (t + 1);
+ d = q + ((9999 - pll_clkin % 10000) * (d / t - q)) / 10000;
if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
@@ -218,20 +213,20 @@ recalc:
goto recalc;
}
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
+ snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
pll_sel);
- snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
+ snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_1,
TAS2552_PLL_J_MASK, j);
/* Will clear the PLL_BYPASS bit */
- snd_soc_write(codec, TAS2552_PLL_CTRL_2,
+ snd_soc_component_write(component, TAS2552_PLL_CTRL_2,
TAS2552_PLL_D_UPPER(d));
- snd_soc_write(codec, TAS2552_PLL_CTRL_3,
+ snd_soc_component_write(component, TAS2552_PLL_CTRL_3,
TAS2552_PLL_D_LOWER(d));
}
/* Restore PLL status */
- snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
+ snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
pll_enable);
return 0;
@@ -241,8 +236,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
int cpf;
u8 ser_ctrl1_reg, wclk_rate;
@@ -264,7 +259,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
cpf = 64 + tas2552->tdm_delay;
break;
default:
- dev_err(codec->dev, "Not supported sample size: %d\n",
+ dev_err(component->dev, "Not supported sample size: %d\n",
params_width(params));
return -EINVAL;
}
@@ -278,7 +273,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
else
ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
- snd_soc_update_bits(codec, TAS2552_SER_CTRL_1,
+ snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1,
TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
ser_ctrl1_reg);
@@ -313,15 +308,15 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
break;
default:
- dev_err(codec->dev, "Not supported sample rate: %d\n",
+ dev_err(component->dev, "Not supported sample rate: %d\n",
params_rate(params));
return -EINVAL;
}
- snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
+ snd_soc_component_update_bits(component, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
wclk_rate);
- return tas2552_setup_pll(codec, params);
+ return tas2552_setup_pll(component, params);
}
#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
@@ -330,8 +325,8 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
static int tas2552_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
int delay = 0;
/* TDM slot selection only valid in DSP_A/_B mode */
@@ -341,32 +336,32 @@ static int tas2552_prepare(struct snd_pcm_substream *substream,
delay += tas2552->tdm_delay;
/* Configure data delay */
- snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
+ snd_soc_component_write(component, TAS2552_SER_CTRL_2, delay);
return 0;
}
static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = dai->component;
+ struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
u8 serial_format;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
serial_format = 0x00;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
serial_format = TAS2552_WCLKDIR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
serial_format = TAS2552_BCLKDIR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
break;
default:
- dev_vdbg(codec->dev, "DAI Format master is not found\n");
+ dev_vdbg(component->dev, "DAI Format master is not found\n");
return -EINVAL;
}
@@ -385,12 +380,12 @@ static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
serial_format |= TAS2552_DATAFORMAT_LEFT_J;
break;
default:
- dev_vdbg(codec->dev, "DAI Format is not found\n");
+ dev_vdbg(component->dev, "DAI Format is not found\n");
return -EINVAL;
}
tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
+ snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
serial_format);
return 0;
}
@@ -398,8 +393,8 @@ static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ struct snd_soc_component *component = dai->component;
+ struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
u8 reg, mask, val;
switch (clk_id) {
@@ -407,12 +402,12 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
case TAS2552_PLL_CLKIN_IVCLKIN:
if (freq < 512000 || freq > 24576000) {
/* out of range PLL_CLKIN, fall back to use BCLK */
- dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n",
+ dev_warn(component->dev, "Out of range PLL_CLKIN: %u\n",
freq);
clk_id = TAS2552_PLL_CLKIN_BCLK;
freq = 0;
}
- /* fall through */
+ fallthrough;
case TAS2552_PLL_CLKIN_BCLK:
case TAS2552_PLL_CLKIN_1_8_FIXED:
mask = TAS2552_PLL_SRC_MASK;
@@ -432,11 +427,11 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
tas2552->pdm_clk = freq;
break;
default:
- dev_err(codec->dev, "Invalid clk id: %d\n", clk_id);
+ dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -445,45 +440,44 @@ static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
unsigned int lsb;
if (unlikely(!tx_mask)) {
- dev_err(codec->dev, "tx masks need to be non 0\n");
+ dev_err(component->dev, "tx masks need to be non 0\n");
return -EINVAL;
}
/* TDM based on DSP mode requires slots to be adjacent */
lsb = __ffs(tx_mask);
if ((lsb + 1) != __fls(tx_mask)) {
- dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+ dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
return -EINVAL;
}
tas2552->tdm_delay = lsb * slot_width;
/* DOUT in high-impedance on inactive bit clocks */
- snd_soc_update_bits(codec, TAS2552_DOUT,
+ snd_soc_component_update_bits(component, TAS2552_DOUT,
TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
return 0;
}
-static int tas2552_mute(struct snd_soc_dai *dai, int mute)
+static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction)
{
u8 cfg1_reg = 0;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute)
cfg1_reg |= TAS2552_MUTE;
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
+ snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
return 0;
}
-#ifdef CONFIG_PM
static int tas2552_runtime_suspend(struct device *dev)
{
struct tas2552_data *tas2552 = dev_get_drvdata(dev);
@@ -511,11 +505,9 @@ static int tas2552_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops tas2552_pm = {
- SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, NULL)
};
static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
@@ -524,7 +516,8 @@ static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
.set_sysclk = tas2552_set_dai_sysclk,
.set_fmt = tas2552_set_dai_fmt,
.set_tdm_slot = tas2552_set_dai_tdm_slot,
- .digital_mute = tas2552_mute,
+ .mute_stream = tas2552_mute,
+ .no_capture_mute = 1,
};
/* Formats supported by TAS2552 driver. */
@@ -542,6 +535,13 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = TAS2552_FORMATS,
},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2552_FORMATS,
+ },
.ops = &tas2552_speaker_dai_ops,
},
};
@@ -567,112 +567,111 @@ static const struct snd_kcontrol_new tas2552_snd_controls[] = {
SOC_ENUM("DIN source", tas2552_din_source_enum),
};
-static int tas2552_codec_probe(struct snd_soc_codec *codec)
+static int tas2552_component_probe(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
int ret;
- tas2552->codec = codec;
+ tas2552->component = component;
ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n",
+ dev_err(component->dev, "Failed to enable supplies: %d\n",
ret);
return ret;
}
gpiod_set_value(tas2552->enable_gpio, 1);
- ret = pm_runtime_get_sync(codec->dev);
+ ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
- dev_err(codec->dev, "Enabling device failed: %d\n",
+ dev_err(component->dev, "Enabling device failed: %d\n",
ret);
goto probe_fail;
}
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
- snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
+ snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
+ snd_soc_component_write(component, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
TAS2552_DIN_SRC_SEL_AVG_L_R);
- snd_soc_write(codec, TAS2552_OUTPUT_DATA,
+ snd_soc_component_write(component, TAS2552_OUTPUT_DATA,
TAS2552_PDM_DATA_SEL_V_I |
TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
- snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
+ snd_soc_component_write(component, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
TAS2552_APT_THRESH_20_17);
- snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
+ snd_soc_component_write(component, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
TAS2552_LIM_EN);
return 0;
probe_fail:
+ pm_runtime_put_noidle(component->dev);
gpiod_set_value(tas2552->enable_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
- return -EIO;
+ return ret;
}
-static int tas2552_codec_remove(struct snd_soc_codec *codec)
+static void tas2552_component_remove(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
gpiod_set_value(tas2552->enable_gpio, 0);
-
- return 0;
};
#ifdef CONFIG_PM
-static int tas2552_suspend(struct snd_soc_codec *codec)
+static int tas2552_suspend(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
if (ret != 0)
- dev_err(codec->dev, "Failed to disable supplies: %d\n",
+ dev_err(component->dev, "Failed to disable supplies: %d\n",
ret);
- return 0;
+ return ret;
}
-static int tas2552_resume(struct snd_soc_codec *codec)
+static int tas2552_resume(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n",
+ dev_err(component->dev, "Failed to enable supplies: %d\n",
ret);
}
- return 0;
+ return ret;
}
#else
#define tas2552_suspend NULL
#define tas2552_resume NULL
#endif
-static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
- .probe = tas2552_codec_probe,
- .remove = tas2552_codec_remove,
- .suspend = tas2552_suspend,
- .resume = tas2552_resume,
- .ignore_pmdown_time = true,
-
- .controls = tas2552_snd_controls,
- .num_controls = ARRAY_SIZE(tas2552_snd_controls),
- .dapm_widgets = tas2552_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets),
- .dapm_routes = tas2552_audio_map,
- .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map),
+static const struct snd_soc_component_driver soc_component_dev_tas2552 = {
+ .probe = tas2552_component_probe,
+ .remove = tas2552_component_remove,
+ .suspend = tas2552_suspend,
+ .resume = tas2552_resume,
+ .controls = tas2552_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2552_snd_controls),
+ .dapm_widgets = tas2552_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets),
+ .dapm_routes = tas2552_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
};
static const struct regmap_config tas2552_regmap_config = {
@@ -685,8 +684,7 @@ static const struct regmap_config tas2552_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int tas2552_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas2552_probe(struct i2c_client *client)
{
struct device *dev;
struct tas2552_data *data;
@@ -726,29 +724,28 @@ static int tas2552_probe(struct i2c_client *client,
pm_runtime_set_autosuspend_delay(&client->dev, 1000);
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_enable(&client->dev);
- pm_runtime_mark_last_busy(&client->dev);
pm_runtime_put_sync_autosuspend(&client->dev);
dev_set_drvdata(&client->dev, data);
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_tas2552,
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_tas2552,
tas2552_dai, ARRAY_SIZE(tas2552_dai));
- if (ret < 0)
- dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register component: %d\n", ret);
+ pm_runtime_get_noresume(&client->dev);
+ }
return ret;
}
-static int tas2552_i2c_remove(struct i2c_client *client)
+static void tas2552_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
pm_runtime_disable(&client->dev);
- return 0;
}
static const struct i2c_device_id tas2552_id[] = {
- { "tas2552", 0 },
+ { "tas2552" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2552_id);
@@ -765,7 +762,7 @@ static struct i2c_driver tas2552_i2c_driver = {
.driver = {
.name = "tas2552",
.of_match_table = of_match_ptr(tas2552_of_match),
- .pm = &tas2552_pm,
+ .pm = pm_ptr(&tas2552_pm),
},
.probe = tas2552_probe,
.remove = tas2552_i2c_remove,
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index e34752b8a299..b9c2e70df57e 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -1,18 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Dan Murphy <dmurphy@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#ifndef __TAS2552_H__
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
new file mode 100644
index 000000000000..ceb367ae05ba
--- /dev/null
+++ b/sound/soc/codecs/tas2562.c
@@ -0,0 +1,781 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the Texas Instruments TAS2562 CODEC
+// Copyright (C) 2019 Texas Instruments Inc.
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tas2562.h"
+
+#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FORMAT_S32_LE)
+
+/* DVC equation involves floating point math
+ * round(10^(volume in dB/20)*2^30)
+ * so create a lookup table for 2dB step
+ */
+static const unsigned int float_vol_db_lookup[] = {
+0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151,
+0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b,
+0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a,
+0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f,
+0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7,
+0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d,
+0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a,
+0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27,
+0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68,
+0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362,
+0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000
+};
+
+struct tas2562_data {
+ struct snd_soc_component *component;
+ struct gpio_desc *sdz_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ struct i2c_client *client;
+ int v_sense_slot;
+ int i_sense_slot;
+ int volume_lvl;
+ int model_id;
+ bool dac_powered;
+ bool unmuted;
+};
+
+enum tas256x_model {
+ TAS2562,
+ TAS2564,
+ TAS2110,
+};
+
+static int tas2562_set_samplerate(struct tas2562_data *tas2562, int samplerate)
+{
+ int samp_rate;
+ int ramp_rate;
+
+ switch (samplerate) {
+ case 7350:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ;
+ break;
+ case 8000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ;
+ break;
+ case 14700:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ;
+ break;
+ case 16000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ;
+ break;
+ case 22050:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ;
+ break;
+ case 24000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ;
+ break;
+ case 29400:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ;
+ break;
+ case 32000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ;
+ break;
+ case 44100:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ;
+ break;
+ case 48000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ;
+ break;
+ case 88200:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ;
+ break;
+ case 96000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ;
+ break;
+ case 176400:
+ ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ;
+ break;
+ case 192000:
+ ramp_rate = 0;
+ samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ;
+ break;
+ default:
+ dev_info(tas2562->dev, "%s, unsupported sample rate, %d\n",
+ __func__, samplerate);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0,
+ TAS2562_TDM_CFG0_RAMPRATE_MASK, ramp_rate);
+ snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0,
+ TAS2562_TDM_CFG0_SAMPRATE_MASK, samp_rate);
+
+ return 0;
+}
+
+static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int left_slot, right_slot;
+ int slots_cfg;
+ int ret;
+
+ if (!tx_mask) {
+ dev_err(component->dev, "tx masks must not be 0\n");
+ return -EINVAL;
+ }
+
+ if (slots == 1) {
+ if (tx_mask != 1)
+ return -EINVAL;
+
+ left_slot = 0;
+ right_slot = 0;
+ } else {
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ }
+ }
+
+ slots_cfg = (right_slot << TAS2562_RIGHT_SLOT_SHIFT) | left_slot;
+
+ ret = snd_soc_component_write(component, TAS2562_TDM_CFG3, slots_cfg);
+ if (ret < 0)
+ return ret;
+
+ switch (slot_width) {
+ case 16:
+ ret = snd_soc_component_update_bits(component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXLEN_MASK,
+ TAS2562_TDM_CFG2_RXLEN_16B);
+ break;
+ case 24:
+ ret = snd_soc_component_update_bits(component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXLEN_MASK,
+ TAS2562_TDM_CFG2_RXLEN_24B);
+ break;
+ case 32:
+ ret = snd_soc_component_update_bits(component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXLEN_MASK,
+ TAS2562_TDM_CFG2_RXLEN_32B);
+ break;
+
+ case 0:
+ /* Do not change slot width */
+ break;
+ default:
+ dev_err(tas2562->dev, "slot width not supported");
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG5,
+ TAS2562_TDM_CFG5_VSNS_SLOT_MASK,
+ tas2562->v_sense_slot);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG6,
+ TAS2562_TDM_CFG6_ISNS_SLOT_MASK,
+ tas2562->i_sense_slot);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_set_bitwidth(struct tas2562_data *tas2562, int bitwidth)
+{
+ int ret;
+ int val;
+ int sense_en;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXWLEN_MASK,
+ TAS2562_TDM_CFG2_RXWLEN_16B);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXWLEN_MASK,
+ TAS2562_TDM_CFG2_RXWLEN_24B);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_update_bits(tas2562->component,
+ TAS2562_TDM_CFG2,
+ TAS2562_TDM_CFG2_RXWLEN_MASK,
+ TAS2562_TDM_CFG2_RXWLEN_32B);
+ break;
+
+ default:
+ dev_info(tas2562->dev, "Unsupported bitwidth format\n");
+ return -EINVAL;
+ }
+
+ val = snd_soc_component_read(tas2562->component, TAS2562_PWR_CTRL);
+ if (val < 0)
+ return val;
+
+ if (val & (1 << TAS2562_VSENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2562_TDM_CFG5_VSNS_EN;
+
+ ret = snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG5,
+ TAS2562_TDM_CFG5_VSNS_EN, sense_en);
+ if (ret < 0)
+ return ret;
+
+ if (val & (1 << TAS2562_ISENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2562_TDM_CFG6_ISNS_EN;
+
+ ret = snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG6,
+ TAS2562_TDM_CFG6_ISNS_EN, sense_en);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2562_set_bitwidth(tas2562, params_format(params));
+ if (ret) {
+ dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = tas2562_set_samplerate(tas2562, params_rate(params));
+ if (ret)
+ dev_err(tas2562->dev, "set sample rate failed, %d\n", ret);
+
+ return ret;
+}
+
+static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ u8 asi_cfg_1 = 0;
+ u8 tdm_rx_start_slot = 0;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 |= TAS2562_TDM_CFG1_RX_FALLING;
+ break;
+ default:
+ dev_err(tas2562->dev, "ASI format Inverse is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1,
+ TAS2562_TDM_CFG1_RX_EDGE_MASK,
+ asi_cfg_1);
+ if (ret < 0) {
+ dev_err(tas2562->dev, "Failed to set RX edge\n");
+ return ret;
+ }
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_rx_start_slot = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_rx_start_slot = 1;
+ break;
+ default:
+ dev_err(tas2562->dev,
+ "DAI Format is not found, fmt=0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1,
+ TAS2562_RX_OFF_MASK, (tdm_rx_start_slot << 1));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_update_pwr_ctrl(struct tas2562_data *tas2562)
+{
+ struct snd_soc_component *component = tas2562->component;
+ unsigned int val;
+ int ret;
+
+ if (tas2562->dac_powered)
+ val = tas2562->unmuted ?
+ TAS2562_ACTIVE : TAS2562_MUTE;
+ else
+ val = TAS2562_SHUTDOWN;
+
+ ret = snd_soc_component_update_bits(component, TAS2562_PWR_CTRL,
+ TAS2562_MODE_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2562_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(dai->component);
+
+ tas2562->unmuted = !mute;
+ return tas2562_update_pwr_ctrl(tas2562);
+}
+
+static int tas2562_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ tas2562->component = component;
+
+ if (tas2562->sdz_gpio)
+ gpiod_set_value_cansleep(tas2562->sdz_gpio, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas2562_suspend(struct snd_soc_component *component)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(tas2562->regmap, true);
+ regcache_mark_dirty(tas2562->regmap);
+
+ if (tas2562->sdz_gpio)
+ gpiod_set_value_cansleep(tas2562->sdz_gpio, 0);
+
+ return 0;
+}
+
+static int tas2562_resume(struct snd_soc_component *component)
+{
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ if (tas2562->sdz_gpio)
+ gpiod_set_value_cansleep(tas2562->sdz_gpio, 1);
+
+ regcache_cache_only(tas2562->regmap, false);
+
+ return regcache_sync(tas2562->regmap);
+}
+#else
+#define tas2562_suspend NULL
+#define tas2562_resume NULL
+#endif
+
+static const char * const tas2562_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_enum, TAS2562_TDM_CFG2, 4,
+ tas2562_ASI1_src);
+
+static const struct snd_kcontrol_new tas2562_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2562_ASI1_src_enum);
+
+static int tas2562_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tas2562->dac_powered = true;
+ ret = tas2562_update_pwr_ctrl(tas2562);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ tas2562->dac_powered = false;
+ ret = tas2562_update_pwr_ctrl(tas2562);
+ break;
+ default:
+ dev_err(tas2562->dev, "Not supported evevt\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int tas2562_volume_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = tas2562->volume_lvl;
+ return 0;
+}
+
+static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u32 reg_val;
+
+ reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2];
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG4,
+ (reg_val & 0xff));
+ if (ret)
+ return ret;
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG3,
+ ((reg_val >> 8) & 0xff));
+ if (ret)
+ return ret;
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG2,
+ ((reg_val >> 16) & 0xff));
+ if (ret)
+ return ret;
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG1,
+ ((reg_val >> 24) & 0xff));
+ if (ret)
+ return ret;
+
+ tas2562->volume_lvl = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+/* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(dvc_tlv, -11000, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0);
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_ISENSE_POWER_EN,
+ 1, 1);
+
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_VSENSE_POWER_EN,
+ 1, 1);
+
+static const struct snd_kcontrol_new tas2562_snd_controls[] = {
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 1, 0x1c, 0,
+ tas2562_dac_tlv),
+ SOC_SINGLE_EXT_TLV("Digital Volume Control", TAS2562_DVC_CFG1, 0, 110, 0,
+ tas2562_volume_control_get, tas2562_volume_control_put,
+ dvc_tlv),
+};
+
+static const struct snd_soc_dapm_widget tas2110_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route tas2110_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ { "DAC", NULL, "ASI1 Sel" },
+ { "OUT", NULL, "DAC" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tas2110 = {
+ .probe = tas2562_codec_probe,
+ .suspend = tas2562_suspend,
+ .resume = tas2562_resume,
+ .controls = tas2562_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2562_snd_controls),
+ .dapm_widgets = tas2110_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2110_dapm_widgets),
+ .dapm_routes = tas2110_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2110_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("ISENSE", TAS2562_PWR_CTRL, 3, 1, &isense_switch),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2562_PWR_CTRL, 2, 1, &vsense_switch),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route tas2562_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ { "DAC", NULL, "ASI1 Sel" },
+ { "OUT", NULL, "DAC" },
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tas2562 = {
+ .probe = tas2562_codec_probe,
+ .suspend = tas2562_suspend,
+ .resume = tas2562_resume,
+ .controls = tas2562_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2562_snd_controls),
+ .dapm_widgets = tas2562_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets),
+ .dapm_routes = tas2562_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = {
+ .hw_params = tas2562_hw_params,
+ .set_fmt = tas2562_set_dai_fmt,
+ .set_tdm_slot = tas2562_set_dai_tdm_slot,
+ .mute_stream = tas2562_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver tas2562_dai[] = {
+ {
+ .name = "tas2562-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2562_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 0,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2562_FORMATS,
+ },
+ .ops = &tas2562_speaker_dai_ops,
+ },
+};
+
+static const struct regmap_range_cfg tas2562_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 5 * 128,
+ .selector_reg = TAS2562_PAGE_CTRL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct reg_default tas2562_reg_defaults[] = {
+ { TAS2562_PAGE_CTRL, 0x00 },
+ { TAS2562_SW_RESET, 0x00 },
+ { TAS2562_PWR_CTRL, 0x0e },
+ { TAS2562_PB_CFG1, 0x20 },
+ { TAS2562_TDM_CFG0, 0x09 },
+ { TAS2562_TDM_CFG1, 0x02 },
+ { TAS2562_DVC_CFG1, 0x40 },
+ { TAS2562_DVC_CFG2, 0x40 },
+ { TAS2562_DVC_CFG3, 0x00 },
+ { TAS2562_DVC_CFG4, 0x00 },
+};
+
+static const struct regmap_config tas2562_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 5 * 128,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = tas2562_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults),
+ .ranges = tas2562_ranges,
+ .num_ranges = ARRAY_SIZE(tas2562_ranges),
+};
+
+static int tas2562_parse_dt(struct tas2562_data *tas2562)
+{
+ struct device *dev = tas2562->dev;
+ int ret = 0;
+
+ tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2562->sdz_gpio)) {
+ if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ tas2562->sdz_gpio = NULL;
+ }
+
+ /*
+ * The shut-down property is deprecated but needs to be checked for
+ * backwards compatibility.
+ */
+ if (tas2562->sdz_gpio == NULL) {
+ tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2562->sdz_gpio))
+ if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ tas2562->sdz_gpio = NULL;
+ }
+
+ if (tas2562->model_id == TAS2110)
+ return ret;
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2562->i_sense_slot);
+ if (ret) {
+ dev_err(dev, "Property %s is missing setting default slot\n",
+ "ti,imon-slot-no");
+ tas2562->i_sense_slot = 0;
+ }
+
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &tas2562->v_sense_slot);
+ if (ret) {
+ dev_info(dev, "Property %s is missing setting default slot\n",
+ "ti,vmon-slot-no");
+ tas2562->v_sense_slot = 2;
+ }
+
+ if (tas2562->v_sense_slot < tas2562->i_sense_slot) {
+ dev_err(dev, "Vsense slot must be greater than Isense slot\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct i2c_device_id tas2562_id[] = {
+ { "tas2562", TAS2562 },
+ { "tas2564", TAS2564 },
+ { "tas2110", TAS2110 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2562_id);
+
+static int tas2562_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tas2562_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->dev = &client->dev;
+ data->model_id = (uintptr_t)i2c_get_match_data(client);
+
+ tas2562_parse_dt(data);
+
+ data->regmap = devm_regmap_init_i2c(client, &tas2562_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(dev, "failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&client->dev, data);
+
+ if (data->model_id == TAS2110)
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_tas2110,
+ tas2562_dai,
+ ARRAY_SIZE(tas2562_dai));
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_tas2562,
+ tas2562_dai,
+ ARRAY_SIZE(tas2562_dai));
+
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id tas2562_of_match[] = {
+ { .compatible = "ti,tas2562", },
+ { .compatible = "ti,tas2564", },
+ { .compatible = "ti,tas2110", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tas2562_of_match);
+#endif
+
+static struct i2c_driver tas2562_i2c_driver = {
+ .driver = {
+ .name = "tas2562",
+ .of_match_table = of_match_ptr(tas2562_of_match),
+ },
+ .probe = tas2562_probe,
+ .id_table = tas2562_id,
+};
+
+module_i2c_driver(tas2562_i2c_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("TAS2562 Audio amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h
new file mode 100644
index 000000000000..55b2a1f52ca3
--- /dev/null
+++ b/sound/soc/codecs/tas2562.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tas2562.h - ALSA SoC Texas Instruments TAS2562 Mono Audio Amplifier
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ */
+
+#ifndef __TAS2562_H__
+#define __TAS2562_H__
+
+#define TAS2562_PAGE_CTRL 0x00
+
+#define TAS2562_REG(page, reg) ((page * 128) + reg)
+
+#define TAS2562_SW_RESET TAS2562_REG(0, 0x01)
+#define TAS2562_PWR_CTRL TAS2562_REG(0, 0x02)
+#define TAS2562_PB_CFG1 TAS2562_REG(0, 0x03)
+#define TAS2562_MISC_CFG1 TAS2562_REG(0, 0x04)
+#define TAS2562_MISC_CFG2 TAS2562_REG(0, 0x05)
+
+#define TAS2562_TDM_CFG0 TAS2562_REG(0, 0x06)
+#define TAS2562_TDM_CFG1 TAS2562_REG(0, 0x07)
+#define TAS2562_TDM_CFG2 TAS2562_REG(0, 0x08)
+#define TAS2562_TDM_CFG3 TAS2562_REG(0, 0x09)
+#define TAS2562_TDM_CFG4 TAS2562_REG(0, 0x0a)
+#define TAS2562_TDM_CFG5 TAS2562_REG(0, 0x0b)
+#define TAS2562_TDM_CFG6 TAS2562_REG(0, 0x0c)
+#define TAS2562_TDM_CFG7 TAS2562_REG(0, 0x0d)
+#define TAS2562_TDM_CFG8 TAS2562_REG(0, 0x0e)
+#define TAS2562_TDM_CFG9 TAS2562_REG(0, 0x0f)
+#define TAS2562_TDM_CFG10 TAS2562_REG(0, 0x10)
+#define TAS2562_TDM_DET TAS2562_REG(0, 0x11)
+#define TAS2562_REV_ID TAS2562_REG(0, 0x7d)
+
+#define TAS2562_RX_OFF_MASK GENMASK(5, 1)
+#define TAS2562_TX_OFF_MASK GENMASK(3, 1)
+#define TAS2562_RIGHT_SLOT_SHIFT 4
+
+/* Page 2 */
+#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x0c)
+#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x0d)
+#define TAS2562_DVC_CFG3 TAS2562_REG(2, 0x0e)
+#define TAS2562_DVC_CFG4 TAS2562_REG(2, 0x0f)
+
+#define TAS2562_RESET BIT(0)
+
+#define TAS2562_MODE_MASK GENMASK(1,0)
+#define TAS2562_ACTIVE 0x0
+#define TAS2562_MUTE 0x1
+#define TAS2562_SHUTDOWN 0x2
+
+#define TAS2562_TDM_CFG1_RX_EDGE_MASK BIT(0)
+#define TAS2562_TDM_CFG1_RX_FALLING 1
+
+#define TAS2562_TDM_CFG0_RAMPRATE_MASK BIT(5)
+#define TAS2562_TDM_CFG0_RAMPRATE_44_1 BIT(5)
+#define TAS2562_TDM_CFG0_SAMPRATE_MASK GENMASK(3, 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ (0x0 << 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ (0x1 << 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ (0x2 << 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ (0x3 << 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ (0x4 << 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ (0x5 << 1)
+#define TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ (0x6 << 1)
+
+#define TAS2562_TDM_CFG2_RIGHT_JUSTIFY BIT(6)
+
+#define TAS2562_TDM_CFG2_RXLEN_MASK GENMASK(1, 0)
+#define TAS2562_TDM_CFG2_RXLEN_16B 0x0
+#define TAS2562_TDM_CFG2_RXLEN_24B BIT(0)
+#define TAS2562_TDM_CFG2_RXLEN_32B BIT(1)
+
+#define TAS2562_TDM_CFG2_RXWLEN_MASK GENMASK(3, 2)
+#define TAS2562_TDM_CFG2_RXWLEN_16B 0x0
+#define TAS2562_TDM_CFG2_RXWLEN_20B BIT(2)
+#define TAS2562_TDM_CFG2_RXWLEN_24B BIT(3)
+#define TAS2562_TDM_CFG2_RXWLEN_32B (BIT(2) | BIT(3))
+
+#define TAS2562_VSENSE_POWER_EN 2
+#define TAS2562_ISENSE_POWER_EN 3
+
+#define TAS2562_TDM_CFG5_VSNS_EN BIT(6)
+#define TAS2562_TDM_CFG5_VSNS_SLOT_MASK GENMASK(5, 0)
+
+#define TAS2562_TDM_CFG6_ISNS_EN BIT(6)
+#define TAS2562_TDM_CFG6_ISNS_SLOT_MASK GENMASK(5, 0)
+
+#endif /* __TAS2562_H__ */
diff --git a/sound/soc/codecs/tas2764-quirks.h b/sound/soc/codecs/tas2764-quirks.h
new file mode 100644
index 000000000000..7a62b3ba5b40
--- /dev/null
+++ b/sound/soc/codecs/tas2764-quirks.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __TAS2764_QUIRKS__
+#define __TAS2764_QUIRKS__
+
+#include <linux/regmap.h>
+
+#include "tas2764.h"
+
+/* Bitmask of enabled Apple quirks */
+#define ENABLED_APPLE_QUIRKS 0x3f
+
+/*
+ * Disable noise gate and flip down reserved bit in NS_CFG0
+ */
+#define TAS2764_NOISE_GATE_DISABLE BIT(0)
+
+static const struct reg_sequence tas2764_noise_gate_dis_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x35), 0xb0)
+};
+
+/*
+ * CONV_VBAT_PVDD_MODE=1
+ */
+#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1)
+
+static const struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x6b), 0x41)
+};
+
+/*
+ * Reset of DAC modulator when DSP is OFF
+ */
+#define TAS2764_DMOD_RST BIT(2)
+
+static const struct reg_sequence tas2764_dmod_rst_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x76), 0x0)
+};
+
+/*
+ * Unknown 0x133/0x137 writes (maybe TDM related)
+ */
+#define TAS2764_UNK_SEQ0 BIT(3)
+
+static const struct reg_sequence tas2764_unk_seq0[] = {
+ REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80),
+ REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a),
+};
+
+/*
+ * Unknown 0x614 - 0x61f writes
+ */
+#define TAS2764_APPLE_UNK_SEQ1 BIT(4)
+
+static const struct reg_sequence tas2764_unk_seq1[] = {
+ REG_SEQ0(TAS2764_REG(0x6, 0x14), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x15), 0x13),
+ REG_SEQ0(TAS2764_REG(0x6, 0x16), 0x52),
+ REG_SEQ0(TAS2764_REG(0x6, 0x17), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x18), 0xe4),
+ REG_SEQ0(TAS2764_REG(0x6, 0x19), 0xc),
+ REG_SEQ0(TAS2764_REG(0x6, 0x16), 0xaa),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1b), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1c), 0x12),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1d), 0xa0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1e), 0xd8),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1f), 0x0),
+};
+
+/*
+ * Unknown writes in the 0xfd page (with secondary paging inside)
+ */
+#define TAS2764_APPLE_UNK_SEQ2 BIT(5)
+
+static const struct reg_sequence tas2764_unk_seq2[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x6c), 0x2),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x6d), 0xf),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
+};
+
+/*
+ * Disable 'Thermal Threshold 1'
+ */
+#define TAS2764_THERMAL_TH1_DISABLE BIT(6)
+
+static const struct reg_sequence tas2764_thermal_th1_dis_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x1, 0x47), 0x2),
+};
+
+/*
+ * Imitate Apple's shutdown dance
+ */
+#define TAS2764_SHUTDOWN_DANCE BIT(7)
+
+static const struct reg_sequence tas2764_shutdown_dance_init_seq[] = {
+ /*
+ * SDZ_MODE=01 (immediate)
+ *
+ * We want the shutdown to happen under the influence of
+ * the magic writes in the 0xfdXX region, so make sure
+ * the shutdown is immediate and there's no grace period
+ * followed by the codec part.
+ */
+ REG_SEQ0(TAS2764_REG(0x0, 0x7), 0x60),
+};
+
+static const struct reg_sequence tas2764_pre_shutdown_seq[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), /* switch hidden page */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x4), /* do write (unknown semantics) */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), /* switch hidden page back */
+};
+
+static const struct reg_sequence tas2764_post_shutdown_seq[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x0), /* revert write from pre sequence */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
+};
+
+static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764,
+ unsigned int target)
+{
+ unsigned int curr;
+ int ret;
+
+ curr = snd_soc_component_read_field(tas2764->component,
+ TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK);
+
+ if (target == curr)
+ return 0;
+
+ /* Handle power state transition to shutdown */
+ if (target == TAS2764_PWR_CTRL_SHUTDOWN &&
+ (curr == TAS2764_PWR_CTRL_MUTE || curr == TAS2764_PWR_CTRL_ACTIVE)) {
+ ret = regmap_multi_reg_write(tas2764->regmap, tas2764_pre_shutdown_seq,
+ ARRAY_SIZE(tas2764_pre_shutdown_seq));
+ if (!ret)
+ ret = snd_soc_component_update_bits(tas2764->component,
+ TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK,
+ TAS2764_PWR_CTRL_SHUTDOWN);
+ if (!ret)
+ ret = regmap_multi_reg_write(tas2764->regmap,
+ tas2764_post_shutdown_seq,
+ ARRAY_SIZE(tas2764_post_shutdown_seq));
+ }
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK, target);
+
+ return ret;
+}
+
+/*
+ * Via devicetree (TODO):
+ * - switch from spread spectrum to class-D switching
+ * - disable edge control
+ * - set BOP settings (the BOP config bits *and* BOP_SRC)
+ */
+
+/*
+ * Other setup TODOs:
+ * - DVC ramp rate
+ */
+
+static const struct tas2764_quirk_init_sequence {
+ const struct reg_sequence *seq;
+ int len;
+} tas2764_quirk_init_sequences[] = {
+ { tas2764_noise_gate_dis_seq, ARRAY_SIZE(tas2764_noise_gate_dis_seq) },
+ { tas2764_dmod_rst_seq, ARRAY_SIZE(tas2764_dmod_rst_seq) },
+ { tas2764_conv_vbat_pvdd_mode_seq, ARRAY_SIZE(tas2764_conv_vbat_pvdd_mode_seq) },
+ { tas2764_unk_seq0, ARRAY_SIZE(tas2764_unk_seq0) },
+ { tas2764_unk_seq1, ARRAY_SIZE(tas2764_unk_seq1) },
+ { tas2764_unk_seq2, ARRAY_SIZE(tas2764_unk_seq2) },
+ { tas2764_thermal_th1_dis_seq, ARRAY_SIZE(tas2764_thermal_th1_dis_seq) },
+ { tas2764_shutdown_dance_init_seq, ARRAY_SIZE(tas2764_shutdown_dance_init_seq) },
+};
+
+#endif /* __TAS2764_QUIRKS__ */
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
new file mode 100644
index 000000000000..36e25e48b354
--- /dev/null
+++ b/sound/soc/codecs/tas2764.c
@@ -0,0 +1,950 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the Texas Instruments TAS2764 CODEC
+// Copyright (C) 2020 Texas Instruments Inc.
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2764.h"
+
+enum tas2764_devid {
+ DEVID_TAS2764 = 0,
+ DEVID_SN012776 = 1
+};
+
+struct tas2764_priv {
+ struct snd_soc_component *component;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *sdz_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ int irq;
+ enum tas2764_devid devid;
+
+ int v_sense_slot;
+ int i_sense_slot;
+
+ bool dac_powered;
+ bool unmuted;
+};
+
+#include "tas2764-quirks.h"
+
+static const char *tas2764_int_ltch0_msgs[8] = {
+ "fault: over temperature", /* INT_LTCH0 & BIT(0) */
+ "fault: over current",
+ "fault: bad TDM clock",
+ "limiter active",
+ "fault: PVDD below limiter inflection point",
+ "fault: limiter max attenuation",
+ "fault: BOP infinite hold",
+ "fault: BOP mute", /* INT_LTCH0 & BIT(7) */
+};
+
+static const unsigned int tas2764_int_readout_regs[6] = {
+ TAS2764_INT_LTCH0,
+ TAS2764_INT_LTCH1,
+ TAS2764_INT_LTCH1_0,
+ TAS2764_INT_LTCH2,
+ TAS2764_INT_LTCH3,
+ TAS2764_INT_LTCH4,
+};
+
+static irqreturn_t tas2764_irq(int irq, void *data)
+{
+ struct tas2764_priv *tas2764 = data;
+ u8 latched[6] = {0, 0, 0, 0, 0, 0};
+ int ret = IRQ_NONE;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(latched); i++)
+ latched[i] = snd_soc_component_read(tas2764->component,
+ tas2764_int_readout_regs[i]);
+
+ for (i = 0; i < 8; i++) {
+ if (latched[0] & BIT(i)) {
+ dev_crit_ratelimited(tas2764->dev, "%s\n",
+ tas2764_int_ltch0_msgs[i]);
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if (latched[0]) {
+ dev_err_ratelimited(tas2764->dev, "other context to the fault: %02x,%02x,%02x,%02x,%02x",
+ latched[1], latched[2], latched[3], latched[4], latched[5]);
+ snd_soc_component_update_bits(tas2764->component,
+ TAS2764_INT_CLK_CFG,
+ TAS2764_INT_CLK_CFG_IRQZ_CLR,
+ TAS2764_INT_CLK_CFG_IRQZ_CLR);
+ }
+
+ return ret;
+}
+
+static void tas2764_reset(struct tas2764_priv *tas2764)
+{
+ if (tas2764->reset_gpio) {
+ gpiod_set_value_cansleep(tas2764->reset_gpio, 0);
+ msleep(20);
+ gpiod_set_value_cansleep(tas2764->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ snd_soc_component_write(tas2764->component, TAS2764_SW_RST,
+ TAS2764_RST);
+ usleep_range(1000, 2000);
+}
+
+static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764)
+{
+ struct snd_soc_component *component = tas2764->component;
+ unsigned int val;
+ int ret;
+
+ if (tas2764->dac_powered)
+ val = tas2764->unmuted ?
+ TAS2764_PWR_CTRL_ACTIVE : TAS2764_PWR_CTRL_MUTE;
+ else
+ val = TAS2764_PWR_CTRL_SHUTDOWN;
+
+ if (ENABLED_APPLE_QUIRKS & TAS2764_SHUTDOWN_DANCE)
+ return tas2764_do_quirky_pwr_ctrl_change(tas2764, val);
+
+ ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas2764_codec_suspend(struct snd_soc_component *component)
+{
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK,
+ TAS2764_PWR_CTRL_SHUTDOWN);
+
+ if (ret < 0)
+ return ret;
+
+ if (tas2764->sdz_gpio)
+ gpiod_set_value_cansleep(tas2764->sdz_gpio, 0);
+
+ regcache_cache_only(tas2764->regmap, true);
+ regcache_mark_dirty(tas2764->regmap);
+
+ usleep_range(6000, 7000);
+
+ return 0;
+}
+
+static int tas2764_codec_resume(struct snd_soc_component *component)
+{
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (tas2764->sdz_gpio) {
+ gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ ret = tas2764_update_pwr_ctrl(tas2764);
+
+ if (ret < 0)
+ return ret;
+
+ regcache_cache_only(tas2764->regmap, false);
+
+ return regcache_sync(tas2764->regmap);
+}
+#else
+#define tas2764_codec_suspend NULL
+#define tas2764_codec_resume NULL
+#endif
+
+static const char * const tas2764_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, TAS2764_TDM_CFG2_SCFG_SHIFT,
+ tas2764_ASI1_src);
+
+static const struct snd_kcontrol_new tas2764_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum);
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1);
+
+static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux),
+ SND_SOC_DAPM_SWITCH("ISENSE", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN,
+ 1, &isense_switch),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN,
+ 1, &vsense_switch),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2764_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ {"DAC", NULL, "ASI1 Sel"},
+ {"OUT", NULL, "DAC"},
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct tas2764_priv *tas2764 =
+ snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ if (!mute) {
+ tas2764->dac_powered = true;
+ ret = tas2764_update_pwr_ctrl(tas2764);
+ if (ret)
+ return ret;
+ }
+
+ tas2764->unmuted = !mute;
+ ret = tas2764_update_pwr_ctrl(tas2764);
+ if (ret)
+ return ret;
+
+ if (mute) {
+ /* Wait for ramp-down */
+ usleep_range(6000, 7000);
+
+ tas2764->dac_powered = false;
+ ret = tas2764_update_pwr_ctrl(tas2764);
+ if (ret)
+ return ret;
+
+ /* Wait a bit after shutdown */
+ usleep_range(2000, 3000);
+ }
+
+ return 0;
+}
+
+static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth)
+{
+ struct snd_soc_component *component = tas2764->component;
+ int sense_en;
+ int val;
+ int ret;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2764_TDM_CFG2,
+ TAS2764_TDM_CFG2_RXW_MASK,
+ TAS2764_TDM_CFG2_RXW_16BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2764_TDM_CFG2,
+ TAS2764_TDM_CFG2_RXW_MASK,
+ TAS2764_TDM_CFG2_RXW_24BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2764_TDM_CFG2,
+ TAS2764_TDM_CFG2_RXW_MASK,
+ TAS2764_TDM_CFG2_RXW_32BITS);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ val = snd_soc_component_read(tas2764->component, TAS2764_PWR_CTRL);
+ if (val < 0)
+ return val;
+
+ if (val & (1 << TAS2764_VSENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE;
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
+ TAS2764_TDM_CFG5_VSNS_ENABLE,
+ sense_en);
+ if (ret < 0)
+ return ret;
+
+ if (val & (1 << TAS2764_ISENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE;
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
+ TAS2764_TDM_CFG6_ISNS_ENABLE,
+ sense_en);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate)
+{
+ struct snd_soc_component *component = tas2764->component;
+ int ramp_rate_val;
+ int ret;
+
+ switch (samplerate) {
+ case 48000:
+ ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
+ TAS2764_TDM_CFG0_44_1_48KHZ;
+ break;
+ case 44100:
+ ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
+ TAS2764_TDM_CFG0_44_1_48KHZ;
+ break;
+ case 96000:
+ ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
+ TAS2764_TDM_CFG0_88_2_96KHZ;
+ break;
+ case 88200:
+ ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
+ TAS2764_TDM_CFG0_88_2_96KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
+ TAS2764_TDM_CFG0_SMP_MASK |
+ TAS2764_TDM_CFG0_MASK,
+ ramp_rate_val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2764_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2764_set_bitwidth(tas2764, params_format(params));
+ if (ret < 0)
+ return ret;
+
+ return tas2764_set_samplerate(tas2764, params_rate(params));
+}
+
+static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0, asi_cfg_4 = 0;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
+ fallthrough;
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING;
+ asi_cfg_4 = TAS2764_TDM_CFG4_TX_FALLING;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
+ fallthrough;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING;
+ asi_cfg_4 = TAS2764_TDM_CFG4_TX_RISING;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
+ TAS2764_TDM_CFG1_RX_MASK,
+ asi_cfg_1);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG4,
+ TAS2764_TDM_CFG4_TX_MASK,
+ asi_cfg_4);
+ if (ret < 0)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_rx_start_slot = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_LEFT_J:
+ tdm_rx_start_slot = 0;
+ break;
+ default:
+ dev_err(tas2764->dev,
+ "DAI Format is not found, fmt=0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
+ TAS2764_TDM_CFG0_FRAME_START,
+ asi_cfg_0);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
+ TAS2764_TDM_CFG1_MASK,
+ (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ int left_slot, right_slot;
+ int slots_cfg;
+ int slot_size;
+ int ret;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot;
+
+ ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, slots_cfg);
+ if (ret)
+ return ret;
+
+ switch (slot_width) {
+ case 16:
+ slot_size = TAS2764_TDM_CFG2_RXS_16BITS;
+ break;
+ case 24:
+ slot_size = TAS2764_TDM_CFG2_RXS_24BITS;
+ break;
+ case 32:
+ slot_size = TAS2764_TDM_CFG2_RXS_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
+ TAS2764_TDM_CFG2_RXS_MASK,
+ slot_size);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5,
+ TAS2764_TDM_CFG5_50_MASK,
+ tas2764->v_sense_slot);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6,
+ TAS2764_TDM_CFG6_50_MASK,
+ tas2764->i_sense_slot);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tas2764_dai_ops = {
+ .mute_stream = tas2764_mute,
+ .hw_params = tas2764_hw_params,
+ .set_fmt = tas2764_set_fmt,
+ .set_tdm_slot = tas2764_set_dai_tdm_slot,
+ .no_capture_mute = 1,
+};
+
+#define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
+
+static struct snd_soc_dai_driver tas2764_dai_driver[] = {
+ {
+ .name = "tas2764 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAS2764_RATES,
+ .formats = TAS2764_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 0,
+ .channels_max = 2,
+ .rates = TAS2764_RATES,
+ .formats = TAS2764_FORMATS,
+ },
+ .ops = &tas2764_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static uint8_t sn012776_bop_presets[] = {
+ 0x01, 0x32, 0x02, 0x22, 0x83, 0x2d, 0x80, 0x02, 0x06,
+ 0x32, 0x46, 0x30, 0x02, 0x06, 0x38, 0x40, 0x30, 0x02,
+ 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6
+};
+
+static const struct regmap_config tas2764_i2c_regmap;
+
+static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) {
+ const struct tas2764_quirk_init_sequence *init_seq =
+ &tas2764_quirk_init_sequences[i];
+
+ if (!init_seq->seq)
+ continue;
+
+ if (!(BIT(i) & ENABLED_APPLE_QUIRKS))
+ continue;
+
+ ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq,
+ init_seq->len);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result)
+{
+ int ret, reg;
+
+ ret = regmap_read(tas2764->regmap, TAS2764_TEMP, &reg);
+ if (ret)
+ return ret;
+ /*
+ * As per datasheet, subtract 93 from raw value to get degrees
+ * Celsius. hwmon wants millidegrees.
+ *
+ * NOTE: The chip will initialise the TAS2764_TEMP register to
+ * 2.6 *C to avoid triggering temperature protection. Since the
+ * ADC is powered down during software shutdown, this value will
+ * persist until the chip is fully powered up (e.g. the PCM it's
+ * attached to is opened). The ADC will power down again when
+ * the chip is put back into software shutdown, with the last
+ * value sampled persisting in the ADC's register.
+ */
+ *result = (reg - 93) * 1000;
+ return 0;
+}
+
+static umode_t tas2764_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tas2764_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tas2764_priv *tas2764 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = tas2764_read_die_temp(tas2764, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const tas2764_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops tas2764_hwmon_ops = {
+ .is_visible = tas2764_hwmon_is_visible,
+ .read = tas2764_hwmon_read,
+};
+
+static const struct hwmon_chip_info tas2764_hwmon_chip_info = {
+ .ops = &tas2764_hwmon_ops,
+ .info = tas2764_hwmon_info,
+};
+
+static int tas2764_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ int ret, i;
+
+ tas2764->component = component;
+
+ if (tas2764->sdz_gpio) {
+ gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ tas2764_reset(tas2764);
+ regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap);
+
+ if (tas2764->irq) {
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK1, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK2, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK3, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK4, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "tas2764", tas2764);
+ if (ret)
+ dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret);
+ }
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
+ TAS2764_TDM_CFG5_VSNS_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
+ TAS2764_TDM_CFG6_ISNS_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ switch (tas2764->devid) {
+ case DEVID_SN012776:
+ ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_BOP_SRC,
+ TAS2764_PWR_CTRL_BOP_SRC);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(sn012776_bop_presets); i++) {
+ ret = snd_soc_component_write(component,
+ TAS2764_BOP_CFG0 + i,
+ sn012776_bop_presets[i]);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Apply all enabled Apple quirks */
+ ret = tas2764_apply_init_quirks(tas2764);
+
+ if (ret < 0)
+ return ret;
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1);
+
+static const char * const tas2764_hpf_texts[] = {
+ "Disabled", "2 Hz", "50 Hz", "100 Hz", "200 Hz",
+ "400 Hz", "800 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2764_hpf_enum, TAS2764_DC_BLK0,
+ TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts);
+
+static const char * const tas2764_oce_texts[] = {
+ "Disable", "Retry",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2764_oce_enum, TAS2764_MISC_CFG1,
+ TAS2764_MISC_CFG1_OCE_RETRY_SHIFT, tas2764_oce_texts);
+
+static const struct snd_kcontrol_new tas2764_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0,
+ TAS2764_DVC_MAX, 1, tas2764_playback_volume),
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0,
+ tas2764_digital_tlv),
+ SOC_ENUM("HPF Corner Frequency", tas2764_hpf_enum),
+ SOC_ENUM("OCE Handling", tas2764_oce_enum),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2764 = {
+ .probe = tas2764_codec_probe,
+ .suspend = tas2764_codec_suspend,
+ .resume = tas2764_codec_resume,
+ .controls = tas2764_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2764_snd_controls),
+ .dapm_widgets = tas2764_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2764_dapm_widgets),
+ .dapm_routes = tas2764_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2764_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct reg_default tas2764_reg_defaults[] = {
+ { TAS2764_PAGE, 0x00 },
+ { TAS2764_SW_RST, 0x00 },
+ { TAS2764_PWR_CTRL, 0x1a },
+ { TAS2764_DVC, 0x00 },
+ { TAS2764_CHNL_0, 0x28 },
+ { TAS2764_TDM_CFG0, 0x09 },
+ { TAS2764_TDM_CFG1, 0x02 },
+ { TAS2764_TDM_CFG2, 0x0a },
+ { TAS2764_TDM_CFG3, 0x10 },
+ { TAS2764_TDM_CFG5, 0x42 },
+ { TAS2764_INT_CLK_CFG, 0x19 },
+};
+
+static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 0xffff,
+ .selector_reg = TAS2764_PAGE,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static bool tas2764_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2764_SW_RST:
+ case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:
+ case TAS2764_INT_CLK_CFG:
+ return true;
+ case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0):
+ /* TI's undocumented registers for the application of quirks */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas2764_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = tas2764_volatile_register,
+ .reg_defaults = tas2764_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tas2764_regmap_ranges,
+ .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges),
+ .max_register = 0xffff,
+};
+
+static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
+{
+ int ret = 0;
+
+ tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2764->reset_gpio)) {
+ if (PTR_ERR(tas2764->reset_gpio) == -EPROBE_DEFER) {
+ tas2764->reset_gpio = NULL;
+ return -EPROBE_DEFER;
+ }
+ }
+
+ tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2764->sdz_gpio)) {
+ if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ tas2764->sdz_gpio = NULL;
+ }
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2764->i_sense_slot);
+ if (ret)
+ tas2764->i_sense_slot = 0;
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &tas2764->v_sense_slot);
+ if (ret)
+ tas2764->v_sense_slot = 2;
+
+ return 0;
+}
+
+static int tas2764_i2c_probe(struct i2c_client *client)
+{
+ struct tas2764_priv *tas2764;
+ int result;
+
+ tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv),
+ GFP_KERNEL);
+ if (!tas2764)
+ return -ENOMEM;
+
+ tas2764->devid = (kernel_ulong_t)of_device_get_match_data(&client->dev);
+
+ tas2764->dev = &client->dev;
+ tas2764->irq = client->irq;
+ i2c_set_clientdata(client, tas2764);
+ dev_set_drvdata(&client->dev, tas2764);
+
+ tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap);
+ if (IS_ERR(tas2764->regmap)) {
+ result = PTR_ERR(tas2764->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ result);
+ return result;
+ }
+
+ if (client->dev.of_node) {
+ result = tas2764_parse_dt(&client->dev, tas2764);
+ if (result) {
+ dev_err(tas2764->dev, "%s: Failed to parse devicetree\n",
+ __func__);
+ return result;
+ }
+ }
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2764",
+ tas2764,
+ &tas2764_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ return dev_err_probe(&client->dev, PTR_ERR(hwmon),
+ "Failed to register temp sensor\n");
+ }
+ }
+
+
+ return devm_snd_soc_register_component(tas2764->dev,
+ &soc_component_driver_tas2764,
+ tas2764_dai_driver,
+ ARRAY_SIZE(tas2764_dai_driver));
+}
+
+static const struct i2c_device_id tas2764_i2c_id[] = {
+ { "tas2764"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2764_of_match[] = {
+ { .compatible = "ti,tas2764", .data = (void *)DEVID_TAS2764 },
+ { .compatible = "ti,sn012776", .data = (void *)DEVID_SN012776 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2764_of_match);
+#endif
+
+static struct i2c_driver tas2764_i2c_driver = {
+ .driver = {
+ .name = "tas2764",
+ .of_match_table = of_match_ptr(tas2764_of_match),
+ },
+ .probe = tas2764_i2c_probe,
+ .id_table = tas2764_i2c_id,
+};
+module_i2c_driver(tas2764_i2c_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h
new file mode 100644
index 000000000000..538290ed3d92
--- /dev/null
+++ b/sound/soc/codecs/tas2764.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tas2764.h - ALSA SoC Texas Instruments TAS2764 Mono Audio Amplifier
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ */
+
+#ifndef __TAS2764__
+#define __TAS2764__
+
+/* Book Control Register */
+#define TAS2764_BOOKCTL_PAGE 0
+#define TAS2764_BOOKCTL_REG 127
+#define TAS2764_REG(page, reg) ((page * 128) + reg)
+
+/* Page */
+#define TAS2764_PAGE TAS2764_REG(0X0, 0x00)
+#define TAS2764_PAGE_PAGE_MASK 255
+
+/* Software Reset */
+#define TAS2764_SW_RST TAS2764_REG(0X0, 0x01)
+#define TAS2764_RST BIT(0)
+
+/* Power Control */
+#define TAS2764_PWR_CTRL TAS2764_REG(0X0, 0x02)
+#define TAS2764_PWR_CTRL_MASK GENMASK(2, 0)
+#define TAS2764_PWR_CTRL_ACTIVE 0x0
+#define TAS2764_PWR_CTRL_MUTE BIT(0)
+#define TAS2764_PWR_CTRL_SHUTDOWN BIT(1)
+#define TAS2764_PWR_CTRL_BOP_SRC BIT(7)
+
+#define TAS2764_VSENSE_POWER_EN 3
+#define TAS2764_ISENSE_POWER_EN 4
+
+/* DC Blocker Control */
+#define TAS2764_DC_BLK0 TAS2764_REG(0x0, 0x04)
+#define TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT 0
+
+/* Digital Volume Control */
+#define TAS2764_DVC TAS2764_REG(0X0, 0x1a)
+#define TAS2764_DVC_MAX 0xc9
+
+#define TAS2764_CHNL_0 TAS2764_REG(0X0, 0x03)
+
+/* Miscellaneous */
+#define TAS2764_MISC_CFG1 TAS2764_REG(0x0, 0x06)
+#define TAS2764_MISC_CFG1_OCE_RETRY_SHIFT 5
+
+/* TDM Configuration Reg0 */
+#define TAS2764_TDM_CFG0 TAS2764_REG(0X0, 0x08)
+#define TAS2764_TDM_CFG0_SMP_MASK BIT(5)
+#define TAS2764_TDM_CFG0_SMP_48KHZ 0x0
+#define TAS2764_TDM_CFG0_SMP_44_1KHZ BIT(5)
+#define TAS2764_TDM_CFG0_MASK GENMASK(3, 1)
+#define TAS2764_TDM_CFG0_44_1_48KHZ BIT(3)
+#define TAS2764_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1))
+#define TAS2764_TDM_CFG0_FRAME_START BIT(0)
+
+/* TDM Configuration Reg1 */
+#define TAS2764_TDM_CFG1 TAS2764_REG(0X0, 0x09)
+#define TAS2764_TDM_CFG1_MASK GENMASK(5, 1)
+#define TAS2764_TDM_CFG1_51_SHIFT 1
+#define TAS2764_TDM_CFG1_RX_MASK BIT(0)
+#define TAS2764_TDM_CFG1_RX_RISING 0x0
+#define TAS2764_TDM_CFG1_RX_FALLING BIT(0)
+
+/* TDM Configuration Reg2 */
+#define TAS2764_TDM_CFG2 TAS2764_REG(0X0, 0x0a)
+#define TAS2764_TDM_CFG2_RXW_MASK GENMASK(3, 2)
+#define TAS2764_TDM_CFG2_RXW_16BITS 0x0
+#define TAS2764_TDM_CFG2_RXW_24BITS BIT(3)
+#define TAS2764_TDM_CFG2_RXW_32BITS (BIT(3) | BIT(2))
+#define TAS2764_TDM_CFG2_RXS_MASK GENMASK(1, 0)
+#define TAS2764_TDM_CFG2_RXS_16BITS 0x0
+#define TAS2764_TDM_CFG2_RXS_24BITS BIT(0)
+#define TAS2764_TDM_CFG2_RXS_32BITS BIT(1)
+#define TAS2764_TDM_CFG2_SCFG_SHIFT 4
+
+/* TDM Configuration Reg3 */
+#define TAS2764_TDM_CFG3 TAS2764_REG(0X0, 0x0c)
+#define TAS2764_TDM_CFG3_RXS_MASK GENMASK(7, 4)
+#define TAS2764_TDM_CFG3_RXS_SHIFT 0x4
+#define TAS2764_TDM_CFG3_MASK GENMASK(3, 0)
+
+/* TDM Configuration Reg4 */
+#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0d)
+#define TAS2764_TDM_CFG4_TX_MASK BIT(0)
+#define TAS2764_TDM_CFG4_TX_RISING 0x0
+#define TAS2764_TDM_CFG4_TX_FALLING BIT(0)
+
+/* TDM Configuration Reg5 */
+#define TAS2764_TDM_CFG5 TAS2764_REG(0X0, 0x0e)
+#define TAS2764_TDM_CFG5_VSNS_MASK BIT(6)
+#define TAS2764_TDM_CFG5_VSNS_ENABLE BIT(6)
+#define TAS2764_TDM_CFG5_50_MASK GENMASK(5, 0)
+
+/* TDM Configuration Reg6 */
+#define TAS2764_TDM_CFG6 TAS2764_REG(0X0, 0x0f)
+#define TAS2764_TDM_CFG6_ISNS_MASK BIT(6)
+#define TAS2764_TDM_CFG6_ISNS_ENABLE BIT(6)
+#define TAS2764_TDM_CFG6_50_MASK GENMASK(5, 0)
+
+/* Interrupt Masks */
+#define TAS2764_INT_MASK0 TAS2764_REG(0x0, 0x3b)
+#define TAS2764_INT_MASK1 TAS2764_REG(0x0, 0x3c)
+#define TAS2764_INT_MASK2 TAS2764_REG(0x0, 0x40)
+#define TAS2764_INT_MASK3 TAS2764_REG(0x0, 0x41)
+#define TAS2764_INT_MASK4 TAS2764_REG(0x0, 0x3d)
+
+/* Latched Fault Registers */
+#define TAS2764_INT_LTCH0 TAS2764_REG(0x0, 0x49)
+#define TAS2764_INT_LTCH1 TAS2764_REG(0x0, 0x4a)
+#define TAS2764_INT_LTCH1_0 TAS2764_REG(0x0, 0x4b)
+#define TAS2764_INT_LTCH2 TAS2764_REG(0x0, 0x4f)
+#define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50)
+#define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51)
+
+/* Readout Registers */
+#define TAS2764_TEMP TAS2764_REG(0x0, 0x56)
+
+/* Clock/IRQ Settings */
+#define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c)
+#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2)
+
+#define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d)
+
+#endif /* __TAS2764__ */
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
new file mode 100644
index 000000000000..6f878b01716f
--- /dev/null
+++ b/sound/soc/codecs/tas2770.c
@@ -0,0 +1,890 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2770 20-W Digital Input Mono Class-D
+// Audio Amplifier with Speaker I/V Sense
+//
+// Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/
+// Author: Tracy Yi <tracy-yi@ti.com>
+// Frank Shi <shifu0704@thundersoft.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2770.h"
+
+#define TAS2770_MDELAY 0xFFFFFFFE
+
+static void tas2770_reset(struct tas2770_priv *tas2770)
+{
+ if (tas2770->reset_gpio) {
+ gpiod_set_value_cansleep(tas2770->reset_gpio, 0);
+ msleep(20);
+ gpiod_set_value_cansleep(tas2770->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ snd_soc_component_write(tas2770->component, TAS2770_SW_RST,
+ TAS2770_RST);
+ usleep_range(1000, 2000);
+}
+
+static int tas2770_update_pwr_ctrl(struct tas2770_priv *tas2770)
+{
+ struct snd_soc_component *component = tas2770->component;
+ unsigned int val;
+ int ret;
+
+ if (tas2770->dac_powered)
+ val = tas2770->unmuted ?
+ TAS2770_PWR_CTRL_ACTIVE : TAS2770_PWR_CTRL_MUTE;
+ else
+ val = TAS2770_PWR_CTRL_SHUTDOWN;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas2770_codec_suspend(struct snd_soc_component *component)
+{
+ struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ regcache_cache_only(tas2770->regmap, true);
+ regcache_mark_dirty(tas2770->regmap);
+
+ if (tas2770->sdz_gpio) {
+ gpiod_set_value_cansleep(tas2770->sdz_gpio, 0);
+ } else {
+ ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ if (ret < 0) {
+ regcache_cache_only(tas2770->regmap, false);
+ regcache_sync(tas2770->regmap);
+ return ret;
+ }
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int tas2770_codec_resume(struct snd_soc_component *component)
+{
+ struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (tas2770->sdz_gpio) {
+ gpiod_set_value_cansleep(tas2770->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ } else {
+ ret = tas2770_update_pwr_ctrl(tas2770);
+ if (ret < 0)
+ return ret;
+ }
+
+ regcache_cache_only(tas2770->regmap, false);
+
+ return regcache_sync(tas2770->regmap);
+}
+#else
+#define tas2770_codec_suspend NULL
+#define tas2770_codec_resume NULL
+#endif
+
+static const char * const tas2770_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2770_ASI1_src_enum, TAS2770_TDM_CFG_REG2,
+ 4, tas2770_ASI1_src);
+
+static const struct snd_kcontrol_new tas2770_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum);
+
+static int tas2770_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tas2770->dac_powered = 1;
+ ret = tas2770_update_pwr_ctrl(tas2770);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ tas2770->dac_powered = 0;
+ ret = tas2770_update_pwr_ctrl(tas2770);
+ break;
+ default:
+ dev_err(tas2770->dev, "Not supported evevt\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 3, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1);
+
+static int sense_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * Powering up ISENSE/VSENSE requires a trip through the shutdown state.
+ * Do that here to ensure that our changes are applied properly, otherwise
+ * we might end up with non-functional IVSENSE if playback started earlier,
+ * which would break software speaker protection.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_REG:
+ return snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ case SND_SOC_DAPM_POST_REG:
+ return tas2770_update_pwr_ctrl(tas2770);
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux),
+ SND_SOC_DAPM_SWITCH_E("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch,
+ sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch,
+ sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2770_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ {"DAC", NULL, "ASI1 Sel"},
+ {"OUT", NULL, "DAC"},
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ tas2770->unmuted = !mute;
+ return tas2770_update_pwr_ctrl(tas2770);
+}
+
+static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770,
+ int i_slot, int v_slot)
+{
+ struct snd_soc_component *component = tas2770->component;
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5,
+ TAS2770_TDM_CFG_REG5_VSNS_MASK |
+ TAS2770_TDM_CFG_REG5_50_MASK,
+ TAS2770_TDM_CFG_REG5_VSNS_ENABLE |
+ v_slot);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6,
+ TAS2770_TDM_CFG_REG6_ISNS_MASK |
+ TAS2770_TDM_CFG_REG6_50_MASK,
+ TAS2770_TDM_CFG_REG6_ISNS_ENABLE |
+ i_slot);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_set_pdm_transmit(struct tas2770_priv *tas2770, int slot)
+{
+ struct snd_soc_component *component = tas2770->component;
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG7,
+ TAS2770_TDM_CFG_REG7_PDM_MASK |
+ TAS2770_TDM_CFG_REG7_50_MASK,
+ TAS2770_TDM_CFG_REG7_PDM_ENABLE |
+ slot);
+ return ret;
+}
+
+static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)
+{
+ int ret;
+ struct snd_soc_component *component = tas2770->component;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_16BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_24BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_32BITS);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate)
+{
+ struct snd_soc_component *component = tas2770->component;
+ int ramp_rate_val;
+ int ret;
+
+ switch (samplerate) {
+ case 48000:
+ ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ |
+ TAS2770_TDM_CFG_REG0_31_44_1_48KHZ;
+ break;
+ case 44100:
+ ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ |
+ TAS2770_TDM_CFG_REG0_31_44_1_48KHZ;
+ break;
+ case 96000:
+ ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ |
+ TAS2770_TDM_CFG_REG0_31_88_2_96KHZ;
+ break;
+ case 88200:
+ ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ |
+ TAS2770_TDM_CFG_REG0_31_88_2_96KHZ;
+ break;
+ case 192000:
+ ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ |
+ TAS2770_TDM_CFG_REG0_31_176_4_192KHZ;
+ break;
+ case 176400:
+ ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ |
+ TAS2770_TDM_CFG_REG0_31_176_4_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK |
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ ramp_rate_val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2770_set_bitwidth(tas2770, params_format(params));
+ if (ret)
+ return ret;
+
+ return tas2770_set_samplerate(tas2770, params_rate(params));
+}
+
+static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ u8 tdm_rx_start_slot = 0, invert_fpol = 0, fpol_preinv = 0, asi_cfg_1 = 0;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(tas2770->dev, "ASI invalid DAI clocking\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_fpol = 1;
+ fallthrough;
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_fpol = 1;
+ fallthrough;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING;
+ break;
+ default:
+ dev_err(tas2770->dev, "ASI format Inverse is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
+ TAS2770_TDM_CFG_REG1_RX_MASK,
+ asi_cfg_1);
+ if (ret < 0)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ tdm_rx_start_slot = 1;
+ fpol_preinv = 0;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_rx_start_slot = 0;
+ fpol_preinv = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_rx_start_slot = 1;
+ fpol_preinv = 1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ tdm_rx_start_slot = 0;
+ fpol_preinv = 1;
+ break;
+ default:
+ dev_err(tas2770->dev,
+ "DAI Format is not found, fmt=0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
+ TAS2770_TDM_CFG_REG1_MASK,
+ (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_FPOL_MASK,
+ (fpol_preinv ^ invert_fpol)
+ ? TAS2770_TDM_CFG_REG0_FPOL_RSING
+ : TAS2770_TDM_CFG_REG0_FPOL_FALING);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ int left_slot, right_slot;
+ int ret;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
+ TAS2770_TDM_CFG_REG3_30_MASK,
+ (left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT));
+ if (ret < 0)
+ return ret;
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
+ TAS2770_TDM_CFG_REG3_RXS_MASK,
+ (right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT));
+ if (ret < 0)
+ return ret;
+
+ switch (slot_width) {
+ case 16:
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_16BITS);
+ break;
+ case 24:
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_24BITS);
+ break;
+ case 32:
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_32BITS);
+ break;
+ case 0:
+ /* Do not change slot width */
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tas2770_dai_ops = {
+ .mute_stream = tas2770_mute,
+ .hw_params = tas2770_hw_params,
+ .set_fmt = tas2770_set_fmt,
+ .set_tdm_slot = tas2770_set_dai_tdm_slot,
+ .no_capture_mute = 1,
+};
+
+#define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000\
+ )
+
+static struct snd_soc_dai_driver tas2770_dai_driver[] = {
+ {
+ .name = "tas2770 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAS2770_RATES,
+ .formats = TAS2770_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 0,
+ .channels_max = 2,
+ .rates = TAS2770_RATES,
+ .formats = TAS2770_FORMATS,
+ },
+ .ops = &tas2770_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result)
+{
+ int ret = 0;
+ int reading, msb, lsb;
+
+ ret = regmap_read(tas2770->regmap, TAS2770_TEMP_MSB, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(tas2770->regmap, TAS2770_TEMP_LSB, &lsb);
+ if (ret)
+ return ret;
+
+ reading = (msb << 4) | (lsb >> 4);
+
+ /*
+ * As per datasheet: divide register by 16 and subtract 93 to get
+ * degrees Celsius. hwmon requires millidegrees. Let's avoid rounding
+ * errors by subtracting 93 * 16 then multiplying by 1000 / 16.
+ *
+ * NOTE: The ADC registers are initialised to 0 on reset. This means
+ * that the temperature will read -93 *C until the chip is brought out
+ * of software shutdown (e.g. the PCM it's attached to is opened). The
+ * ADC is also shut down in software shutdown/low-power mode, so the
+ * value read back from its registers will be the last value sampled
+ * before entering software shutdown.
+ */
+ *result = (reading - (93 * 16)) * (1000 / 16);
+ return 0;
+}
+
+static umode_t tas2770_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tas2770_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tas2770_priv *tas2770 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = tas2770_read_die_temp(tas2770, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const tas2770_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops tas2770_hwmon_ops = {
+ .is_visible = tas2770_hwmon_is_visible,
+ .read = tas2770_hwmon_read,
+};
+
+static const struct hwmon_chip_info tas2770_hwmon_chip_info = {
+ .ops = &tas2770_hwmon_ops,
+ .info = tas2770_hwmon_info,
+};
+
+static const struct regmap_config tas2770_i2c_regmap;
+
+static int tas2770_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ tas2770->component = component;
+
+ if (tas2770->sdz_gpio) {
+ gpiod_set_value_cansleep(tas2770->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ tas2770_reset(tas2770);
+ regmap_reinit_cache(tas2770->regmap, &tas2770_i2c_regmap);
+
+ if (tas2770->i_sense_slot != -1 && tas2770->v_sense_slot != -1) {
+ ret = tas2770_set_ivsense_transmit(tas2770, tas2770->i_sense_slot,
+ tas2770->v_sense_slot);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ if (tas2770->pdm_slot != -1) {
+ ret = tas2770_set_pdm_transmit(tas2770, tas2770->pdm_slot);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -10050, 50, 0);
+
+static const struct snd_kcontrol_new tas2770_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2,
+ 0, TAS2770_PLAY_CFG_REG2_VMAX, 1, tas2770_playback_volume),
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0, 0, 0x14, 0,
+ tas2770_digital_tlv),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2770 = {
+ .probe = tas2770_codec_probe,
+ .suspend = tas2770_codec_suspend,
+ .resume = tas2770_codec_resume,
+ .controls = tas2770_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2770_snd_controls),
+ .dapm_widgets = tas2770_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2770_dapm_widgets),
+ .dapm_routes = tas2770_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2770_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static int tas2770_register_codec(struct tas2770_priv *tas2770)
+{
+ return devm_snd_soc_register_component(tas2770->dev,
+ &soc_component_driver_tas2770,
+ tas2770_dai_driver, ARRAY_SIZE(tas2770_dai_driver));
+}
+
+static const struct reg_default tas2770_reg_defaults[] = {
+ { TAS2770_PAGE, 0x00 },
+ { TAS2770_SW_RST, 0x00 },
+ { TAS2770_PWR_CTRL, 0x0e },
+ { TAS2770_PLAY_CFG_REG0, 0x10 },
+ { TAS2770_PLAY_CFG_REG1, 0x01 },
+ { TAS2770_PLAY_CFG_REG2, 0x00 },
+ { TAS2770_MSC_CFG_REG0, 0x07 },
+ { TAS2770_TDM_CFG_REG1, 0x02 },
+ { TAS2770_TDM_CFG_REG2, 0x0a },
+ { TAS2770_TDM_CFG_REG3, 0x10 },
+ { TAS2770_INT_MASK_REG0, 0xfc },
+ { TAS2770_INT_MASK_REG1, 0xb1 },
+ { TAS2770_INT_CFG, 0x05 },
+ { TAS2770_MISC_IRQ, 0x81 },
+ { TAS2770_CLK_CGF, 0x0c },
+
+};
+
+static bool tas2770_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2770_PAGE: /* regmap implementation requires this */
+ case TAS2770_SW_RST: /* always clears after write */
+ case TAS2770_BO_PRV_REG0:/* has a self clearing bit */
+ case TAS2770_LVE_INT_REG0:
+ case TAS2770_LVE_INT_REG1:
+ case TAS2770_LAT_INT_REG0:/* Sticky interrupt flags */
+ case TAS2770_LAT_INT_REG1:/* Sticky interrupt flags */
+ case TAS2770_VBAT_MSB:
+ case TAS2770_VBAT_LSB:
+ case TAS2770_TEMP_MSB:
+ case TAS2770_TEMP_LSB:
+ return true;
+ }
+
+ return false;
+}
+
+static bool tas2770_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2770_LVE_INT_REG0:
+ case TAS2770_LVE_INT_REG1:
+ case TAS2770_LAT_INT_REG0:
+ case TAS2770_LAT_INT_REG1:
+ case TAS2770_VBAT_MSB:
+ case TAS2770_VBAT_LSB:
+ case TAS2770_TEMP_MSB:
+ case TAS2770_TEMP_LSB:
+ case TAS2770_TDM_CLK_DETC:
+ case TAS2770_REV_AND_GPID:
+ return false;
+ }
+
+ return true;
+}
+
+static const struct regmap_range_cfg tas2770_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 1 * 128,
+ .selector_reg = TAS2770_PAGE,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tas2770_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = tas2770_writeable,
+ .volatile_reg = tas2770_volatile,
+ .reg_defaults = tas2770_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2770_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tas2770_regmap_ranges,
+ .num_ranges = ARRAY_SIZE(tas2770_regmap_ranges),
+ .max_register = 1 * 128,
+};
+
+static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
+{
+ int rc = 0;
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2770->i_sense_slot);
+ if (rc) {
+ dev_info(tas2770->dev, "Property %s is missing setting default slot\n",
+ "ti,imon-slot-no");
+
+ tas2770->i_sense_slot = -1;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &tas2770->v_sense_slot);
+ if (rc) {
+ dev_info(tas2770->dev, "Property %s is missing setting default slot\n",
+ "ti,vmon-slot-no");
+
+ tas2770->v_sense_slot = -1;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,pdm-slot-no",
+ &tas2770->pdm_slot);
+ if (rc)
+ tas2770->pdm_slot = -1;
+
+ tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2770->sdz_gpio)) {
+ if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ tas2770->sdz_gpio = NULL;
+ }
+
+ return 0;
+}
+
+static int tas2770_i2c_probe(struct i2c_client *client)
+{
+ struct tas2770_priv *tas2770;
+ int result;
+
+ tas2770 = devm_kzalloc(&client->dev, sizeof(struct tas2770_priv),
+ GFP_KERNEL);
+ if (!tas2770)
+ return -ENOMEM;
+
+ tas2770->dev = &client->dev;
+ i2c_set_clientdata(client, tas2770);
+ dev_set_drvdata(&client->dev, tas2770);
+
+ tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap);
+ if (IS_ERR(tas2770->regmap)) {
+ result = PTR_ERR(tas2770->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ result);
+ return result;
+ }
+
+ if (client->dev.of_node) {
+ result = tas2770_parse_dt(&client->dev, tas2770);
+ if (result) {
+ dev_err(tas2770->dev, "%s: Failed to parse devicetree\n",
+ __func__);
+ return result;
+ }
+ }
+
+ tas2770->reset_gpio = devm_gpiod_get_optional(tas2770->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2770->reset_gpio)) {
+ if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) {
+ tas2770->reset_gpio = NULL;
+ return -EPROBE_DEFER;
+ }
+ }
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2770",
+ tas2770,
+ &tas2770_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ return dev_err_probe(&client->dev, PTR_ERR(hwmon),
+ "Failed to register temp sensor\n");
+ }
+ }
+
+ result = tas2770_register_codec(tas2770);
+ if (result)
+ dev_err(tas2770->dev, "Register codec failed.\n");
+
+ return result;
+}
+
+static const struct i2c_device_id tas2770_i2c_id[] = {
+ { "tas2770"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2770_of_match[] = {
+ { .compatible = "ti,tas2770" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2770_of_match);
+#endif
+
+static struct i2c_driver tas2770_i2c_driver = {
+ .driver = {
+ .name = "tas2770",
+ .of_match_table = of_match_ptr(tas2770_of_match),
+ },
+ .probe = tas2770_i2c_probe,
+ .id_table = tas2770_i2c_id,
+};
+module_i2c_driver(tas2770_i2c_driver);
+
+MODULE_AUTHOR("Shi Fu <shifu0704@thundersoft.com>");
+MODULE_DESCRIPTION("TAS2770 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h
new file mode 100644
index 000000000000..3fd2e7003c50
--- /dev/null
+++ b/sound/soc/codecs/tas2770.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC TAS2770 codec driver
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/
+ */
+#ifndef __TAS2770__
+#define __TAS2770__
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2770_BOOKCTL_PAGE 0
+#define TAS2770_BOOKCTL_REG 127
+#define TAS2770_REG(page, reg) ((page * 128) + reg)
+ /* Page */
+#define TAS2770_PAGE TAS2770_REG(0X0, 0x00)
+#define TAS2770_PAGE_PAGE_MASK 255
+ /* Software Reset */
+#define TAS2770_SW_RST TAS2770_REG(0X0, 0x01)
+#define TAS2770_RST BIT(0)
+ /* Power Control */
+#define TAS2770_PWR_CTRL TAS2770_REG(0X0, 0x02)
+#define TAS2770_PWR_CTRL_MASK GENMASK(1, 0)
+#define TAS2770_PWR_CTRL_ACTIVE 0x0
+#define TAS2770_PWR_CTRL_MUTE BIT(0)
+#define TAS2770_PWR_CTRL_SHUTDOWN 0x2
+ /* Playback Configuration Reg0 */
+#define TAS2770_PLAY_CFG_REG0 TAS2770_REG(0X0, 0x03)
+ /* Playback Configuration Reg1 */
+#define TAS2770_PLAY_CFG_REG1 TAS2770_REG(0X0, 0x04)
+ /* Playback Configuration Reg2 */
+#define TAS2770_PLAY_CFG_REG2 TAS2770_REG(0X0, 0x05)
+#define TAS2770_PLAY_CFG_REG2_VMAX 0xc9
+ /* Misc Configuration Reg0 */
+#define TAS2770_MSC_CFG_REG0 TAS2770_REG(0X0, 0x07)
+ /* TDM Configuration Reg0 */
+#define TAS2770_TDM_CFG_REG0 TAS2770_REG(0X0, 0x0A)
+#define TAS2770_TDM_CFG_REG0_SMP_MASK BIT(5)
+#define TAS2770_TDM_CFG_REG0_SMP_48KHZ 0x0
+#define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ BIT(5)
+#define TAS2770_TDM_CFG_REG0_31_MASK GENMASK(3, 1)
+#define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ 0x6
+#define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ 0x8
+#define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ 0xa
+#define TAS2770_TDM_CFG_REG0_FPOL_MASK BIT(0)
+#define TAS2770_TDM_CFG_REG0_FPOL_RSING 0
+#define TAS2770_TDM_CFG_REG0_FPOL_FALING 1
+ /* TDM Configuration Reg1 */
+#define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B)
+#define TAS2770_TDM_CFG_REG1_MASK GENMASK(5, 1)
+#define TAS2770_TDM_CFG_REG1_51_SHIFT 1
+#define TAS2770_TDM_CFG_REG1_RX_MASK BIT(0)
+#define TAS2770_TDM_CFG_REG1_RX_RSING 0x0
+#define TAS2770_TDM_CFG_REG1_RX_FALING BIT(0)
+ /* TDM Configuration Reg2 */
+#define TAS2770_TDM_CFG_REG2 TAS2770_REG(0X0, 0x0C)
+#define TAS2770_TDM_CFG_REG2_RXW_MASK GENMASK(3, 2)
+#define TAS2770_TDM_CFG_REG2_RXW_16BITS 0x0
+#define TAS2770_TDM_CFG_REG2_RXW_24BITS 0x8
+#define TAS2770_TDM_CFG_REG2_RXW_32BITS 0xc
+#define TAS2770_TDM_CFG_REG2_RXS_MASK GENMASK(1, 0)
+#define TAS2770_TDM_CFG_REG2_RXS_16BITS 0x0
+#define TAS2770_TDM_CFG_REG2_RXS_24BITS BIT(0)
+#define TAS2770_TDM_CFG_REG2_RXS_32BITS 0x2
+ /* TDM Configuration Reg3 */
+#define TAS2770_TDM_CFG_REG3 TAS2770_REG(0X0, 0x0D)
+#define TAS2770_TDM_CFG_REG3_RXS_MASK GENMASK(7, 4)
+#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4
+#define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0)
+#define TAS2770_TDM_CFG_REG3_30_SHIFT 0
+ /* TDM Configuration Reg5 */
+#define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F)
+#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG5_VSNS_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG5_50_MASK GENMASK(5, 0)
+ /* TDM Configuration Reg6 */
+#define TAS2770_TDM_CFG_REG6 TAS2770_REG(0X0, 0x10)
+#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0)
+ /* TDM Configuration Reg10 */
+#define TAS2770_TDM_CFG_REG7 TAS2770_REG(0X0, 0x11)
+#define TAS2770_TDM_CFG_REG7_PDM_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG7_PDM_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG7_50_MASK GENMASK(5, 0)
+ /* Brown Out Prevention Reg0 */
+#define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B)
+ /* Interrupt MASK Reg0 */
+#define TAS2770_INT_MASK_REG0 TAS2770_REG(0X0, 0x20)
+#define TAS2770_INT_REG0_DEFAULT 0xfc
+#define TAS2770_INT_MASK_REG0_DISABLE 0xff
+ /* Interrupt MASK Reg1 */
+#define TAS2770_INT_MASK_REG1 TAS2770_REG(0X0, 0x21)
+#define TAS2770_INT_REG1_DEFAULT 0xb1
+#define TAS2770_INT_MASK_REG1_DISABLE 0xff
+ /* Live-Interrupt Reg0 */
+#define TAS2770_LVE_INT_REG0 TAS2770_REG(0X0, 0x22)
+ /* Live-Interrupt Reg1 */
+#define TAS2770_LVE_INT_REG1 TAS2770_REG(0X0, 0x23)
+ /* Latched-Interrupt Reg0 */
+#define TAS2770_LAT_INT_REG0 TAS2770_REG(0X0, 0x24)
+#define TAS2770_LAT_INT_REG0_OCE_FLG BIT(1)
+#define TAS2770_LAT_INT_REG0_OTE_FLG BIT(0)
+ /* Latched-Interrupt Reg1 */
+#define TAS2770_LAT_INT_REG1 TAS2770_REG(0X0, 0x25)
+#define TAS2770_LAT_INT_REG1_VBA_TOV BIT(3)
+#define TAS2770_LAT_INT_REG1_VBA_TUV BIT(2)
+#define TAS2770_LAT_INT_REG1_BOUT_FLG BIT(1)
+ /* VBAT MSB */
+#define TAS2770_VBAT_MSB TAS2770_REG(0X0, 0x27)
+ /* VBAT LSB */
+#define TAS2770_VBAT_LSB TAS2770_REG(0X0, 0x28)
+ /* TEMP MSB */
+#define TAS2770_TEMP_MSB TAS2770_REG(0X0, 0x29)
+ /* TEMP LSB */
+#define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A)
+ /* Interrupt Configuration */
+#define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30)
+ /* Misc IRQ */
+#define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32)
+ /* Clock Configuration */
+#define TAS2770_CLK_CGF TAS2770_REG(0X0, 0x3C)
+ /* TDM Clock detection monitor */
+#define TAS2770_TDM_CLK_DETC TAS2770_REG(0X0, 0x77)
+ /* Revision and PG ID */
+#define TAS2770_REV_AND_GPID TAS2770_REG(0X0, 0x7D)
+
+#define TAS2770_POWER_ACTIVE 0
+#define TAS2770_POWER_MUTE BIT(0)
+#define TAS2770_POWER_SHUTDOWN BIT(1)
+
+#define ERROR_OVER_CURRENT BIT(0)
+#define ERROR_DIE_OVERTEMP BIT(1)
+#define ERROR_OVER_VOLTAGE BIT(2)
+#define ERROR_UNDER_VOLTAGE BIT(3)
+#define ERROR_BROWNOUT BIT(4)
+#define ERROR_CLASSD_PWR BIT(5)
+
+struct tas2770_priv {
+ struct snd_soc_component *component;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *sdz_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ int v_sense_slot;
+ int i_sense_slot;
+ int pdm_slot;
+ bool dac_powered;
+ bool unmuted;
+};
+
+#endif /* __TAS2770__ */
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
new file mode 100644
index 000000000000..a1963415c931
--- /dev/null
+++ b/sound/soc/codecs/tas2780.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0
+// Driver for the Texas Instruments TAS2780 Mono
+// Audio amplifier
+// Copyright (C) 2022 Texas Instruments Inc.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "tas2780.h"
+
+struct tas2780_priv {
+ struct snd_soc_component *component;
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ int v_sense_slot;
+ int i_sense_slot;
+};
+
+static void tas2780_reset(struct tas2780_priv *tas2780)
+{
+ int ret = 0;
+
+ if (tas2780->reset_gpio) {
+ gpiod_set_value_cansleep(tas2780->reset_gpio, 0);
+ usleep_range(2000, 2050);
+ gpiod_set_value_cansleep(tas2780->reset_gpio, 1);
+ usleep_range(2000, 2050);
+ }
+
+ ret = snd_soc_component_write(tas2780->component, TAS2780_SW_RST,
+ TAS2780_RST);
+ if (ret)
+ dev_err(tas2780->dev, "%s:errCode:0x%x Reset error!\n",
+ __func__, ret);
+}
+
+#ifdef CONFIG_PM
+static int tas2780_codec_suspend(struct snd_soc_component *component)
+{
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL,
+ TAS2780_PWR_CTRL_MASK, TAS2780_PWR_CTRL_SHUTDOWN);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%0x:power down error\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+ regcache_cache_only(tas2780->regmap, true);
+ regcache_mark_dirty(tas2780->regmap);
+err:
+ return ret;
+}
+
+static int tas2780_codec_resume(struct snd_soc_component *component)
+{
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL,
+ TAS2780_PWR_CTRL_MASK, TAS2780_PWR_CTRL_ACTIVE);
+
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%0x:power down error\n",
+ __func__, ret);
+ goto err;
+ }
+ regcache_cache_only(tas2780->regmap, false);
+ ret = regcache_sync(tas2780->regmap);
+err:
+ return ret;
+}
+#endif
+
+static const char * const tas2780_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2780_ASI1_src_enum, TAS2780_TDM_CFG2, 4, tas2780_ASI1_src);
+
+static const struct snd_kcontrol_new tas2780_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2780_ASI1_src_enum);
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2780_PWR_CTRL,
+ TAS2780_ISENSE_POWER_EN, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2780_PWR_CTRL,
+ TAS2780_VSENSE_POWER_EN, 1, 1);
+
+static const struct snd_soc_dapm_widget tas2780_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2780_asi1_mux),
+ SND_SOC_DAPM_SWITCH("ISENSE", TAS2780_PWR_CTRL,
+ TAS2780_ISENSE_POWER_EN, 1, &isense_switch),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2780_PWR_CTRL,
+ TAS2780_VSENSE_POWER_EN, 1, &vsense_switch),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2780_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ {"OUT", NULL, "ASI1 Sel"},
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static int tas2780_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL,
+ TAS2780_PWR_CTRL_MASK,
+ mute ? TAS2780_PWR_CTRL_MUTE : 0);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s: Failed to set powercontrol\n",
+ __func__);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_set_bitwidth(struct tas2780_priv *tas2780, int bitwidth)
+{
+ struct snd_soc_component *component = tas2780->component;
+ int sense_en;
+ int val;
+ int ret;
+ int slot_size;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXW_MASK,
+ TAS2780_TDM_CFG2_RXW_16BITS);
+ slot_size = TAS2780_TDM_CFG2_RXS_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXW_MASK,
+ TAS2780_TDM_CFG2_RXW_24BITS);
+ slot_size = TAS2780_TDM_CFG2_RXS_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXW_MASK,
+ TAS2780_TDM_CFG2_RXW_32BITS);
+ slot_size = TAS2780_TDM_CFG2_RXS_32BITS;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x set bitwidth error\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXS_MASK, slot_size);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x set RX slot size error\n",
+ __func__, ret);
+ goto err;
+ }
+
+ val = snd_soc_component_read(tas2780->component, TAS2780_PWR_CTRL);
+ if (val < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x read PWR_CTRL error\n",
+ __func__, val);
+ ret = val;
+ goto err;
+ }
+
+ if (val & (1 << TAS2780_VSENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2780_TDM_CFG5_VSNS_ENABLE;
+
+ ret = snd_soc_component_update_bits(tas2780->component,
+ TAS2780_TDM_CFG5, TAS2780_TDM_CFG5_VSNS_ENABLE, sense_en);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x enable vSNS error\n",
+ __func__, ret);
+ goto err;
+ }
+
+ if (val & (1 << TAS2780_ISENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2780_TDM_CFG6_ISNS_ENABLE;
+
+ ret = snd_soc_component_update_bits(tas2780->component,
+ TAS2780_TDM_CFG6, TAS2780_TDM_CFG6_ISNS_ENABLE, sense_en);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x enable iSNS error\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_set_samplerate(
+ struct tas2780_priv *tas2780, int samplerate)
+{
+ struct snd_soc_component *component = tas2780->component;
+ int ramp_rate_val;
+ int ret;
+
+ switch (samplerate) {
+ case 48000:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_48KHZ |
+ TAS2780_TDM_CFG0_44_1_48KHZ;
+ break;
+ case 44100:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_44_1KHZ |
+ TAS2780_TDM_CFG0_44_1_48KHZ;
+ break;
+ case 96000:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_48KHZ |
+ TAS2780_TDM_CFG0_88_2_96KHZ;
+ break;
+ case 88200:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_44_1KHZ |
+ TAS2780_TDM_CFG0_88_2_96KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG0,
+ TAS2780_TDM_CFG0_SMP_MASK | TAS2780_TDM_CFG0_MASK,
+ ramp_rate_val);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set ramp_rate_val\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2780_set_bitwidth(tas2780, params_format(params));
+ if (ret < 0)
+ return ret;
+
+ return tas2780_set_samplerate(tas2780, params_rate(params));
+}
+
+static int tas2780_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+ int iface;
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 = TAS2780_TDM_CFG1_RX_RISING;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 = TAS2780_TDM_CFG1_RX_FALLING;
+ break;
+ default:
+ dev_err(tas2780->dev, "ASI format Inverse is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1,
+ TAS2780_TDM_CFG1_RX_MASK, asi_cfg_1);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set asi_cfg_1\n",
+ __func__, ret);
+ goto err;
+ }
+
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_DSP_A)){
+ iface = TAS2780_TDM_CFG2_SCFG_I2S;
+ tdm_rx_start_slot = 1;
+ } else {
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_DSP_B)
+ || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_LEFT_J)) {
+ iface = TAS2780_TDM_CFG2_SCFG_LEFT_J;
+ tdm_rx_start_slot = 0;
+ } else {
+ dev_err(tas2780->dev,
+ "%s:DAI Format is not found, fmt=0x%x\n",
+ __func__, fmt);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1,
+ TAS2780_TDM_CFG1_MASK,
+ (tdm_rx_start_slot << TAS2780_TDM_CFG1_51_SHIFT));
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set tdm_rx_start_slot\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_SCFG_MASK, iface);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x Failed to set iface\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int left_slot, right_slot;
+ int slots_cfg;
+ int slot_size;
+ int ret = 0;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ slots_cfg = (right_slot << TAS2780_TDM_CFG3_RXS_SHIFT) | left_slot;
+ ret = snd_soc_component_write(component, TAS2780_TDM_CFG3, slots_cfg);
+ if (ret) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set slots_cfg\n",
+ __func__, ret);
+ goto err;
+ }
+
+ switch (slot_width) {
+ case 16:
+ slot_size = TAS2780_TDM_CFG2_RXS_16BITS;
+ break;
+ case 24:
+ slot_size = TAS2780_TDM_CFG2_RXS_24BITS;
+ break;
+ case 32:
+ slot_size = TAS2780_TDM_CFG2_RXS_32BITS;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXS_MASK, slot_size);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set slot_size\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG5,
+ TAS2780_TDM_CFG5_50_MASK, tas2780->v_sense_slot);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set v_sense_slot\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG6,
+ TAS2780_TDM_CFG6_50_MASK, tas2780->i_sense_slot);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set i_sense_slot\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tas2780_dai_ops = {
+ .mute_stream = tas2780_mute,
+ .hw_params = tas2780_hw_params,
+ .set_fmt = tas2780_set_fmt,
+ .set_tdm_slot = tas2780_set_dai_tdm_slot,
+ .no_capture_mute = 1,
+};
+
+#define TAS2780_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2780_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
+
+static struct snd_soc_dai_driver tas2780_dai_driver[] = {
+ {
+ .name = "tas2780 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TAS2780_RATES,
+ .formats = TAS2780_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAS2780_RATES,
+ .formats = TAS2780_FORMATS,
+ },
+ .ops = &tas2780_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int tas2780_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ tas2780->component = component;
+
+ tas2780_reset(tas2780);
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_IC_CFG, TAS2780_IC_CFG_MASK,
+ TAS2780_IC_CFG_ENABLE);
+ if (ret < 0)
+ dev_err(tas2780->dev, "%s:errCode:0x%0x\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2780_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2780_playback_volume, -10000, 50, 0);
+
+static const struct snd_kcontrol_new tas2780_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", TAS2780_DVC, 0,
+ TAS2780_DVC_MAX, 1, tas2780_playback_volume),
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2780_CHNL_0, 0, 0x14, 0,
+ tas2780_digital_tlv),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2780 = {
+ .probe = tas2780_codec_probe,
+#ifdef CONFIG_PM
+ .suspend = tas2780_codec_suspend,
+ .resume = tas2780_codec_resume,
+#endif
+ .controls = tas2780_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2780_snd_controls),
+ .dapm_widgets = tas2780_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2780_dapm_widgets),
+ .dapm_routes = tas2780_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2780_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct reg_default tas2780_reg_defaults[] = {
+ { TAS2780_PAGE, 0x00 },
+ { TAS2780_SW_RST, 0x00 },
+ { TAS2780_PWR_CTRL, 0x1a },
+ { TAS2780_DVC, 0x00 },
+ { TAS2780_CHNL_0, 0x00 },
+ { TAS2780_TDM_CFG0, 0x09 },
+ { TAS2780_TDM_CFG1, 0x02 },
+ { TAS2780_TDM_CFG2, 0x0a },
+ { TAS2780_TDM_CFG3, 0x10 },
+ { TAS2780_TDM_CFG5, 0x42 },
+};
+
+static const struct regmap_range_cfg tas2780_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 1 * 128,
+ .selector_reg = TAS2780_PAGE,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tas2780_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_defaults = tas2780_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2780_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tas2780_regmap_ranges,
+ .num_ranges = ARRAY_SIZE(tas2780_regmap_ranges),
+ .max_register = 1 * 128,
+};
+
+static int tas2780_parse_dt(struct device *dev, struct tas2780_priv *tas2780)
+{
+ int ret = 0;
+
+ tas2780->reset_gpio = devm_gpiod_get_optional(tas2780->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2780->reset_gpio)) {
+ if (PTR_ERR(tas2780->reset_gpio) == -EPROBE_DEFER) {
+ tas2780->reset_gpio = NULL;
+ return -EPROBE_DEFER;
+ }
+ }
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2780->i_sense_slot);
+ if (ret)
+ tas2780->i_sense_slot = 0;
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &tas2780->v_sense_slot);
+ if (ret)
+ tas2780->v_sense_slot = 2;
+
+ return 0;
+}
+
+static int tas2780_i2c_probe(struct i2c_client *client)
+{
+ struct tas2780_priv *tas2780;
+ int result;
+
+ tas2780 = devm_kzalloc(&client->dev, sizeof(struct tas2780_priv),
+ GFP_KERNEL);
+ if (!tas2780)
+ return -ENOMEM;
+ tas2780->dev = &client->dev;
+ i2c_set_clientdata(client, tas2780);
+ dev_set_drvdata(&client->dev, tas2780);
+
+ tas2780->regmap = devm_regmap_init_i2c(client, &tas2780_i2c_regmap);
+ if (IS_ERR(tas2780->regmap)) {
+ result = PTR_ERR(tas2780->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ result);
+ return result;
+ }
+
+ if (client->dev.of_node) {
+ result = tas2780_parse_dt(&client->dev, tas2780);
+ if (result) {
+ dev_err(tas2780->dev,
+ "%s: Failed to parse devicetree\n", __func__);
+ return result;
+ }
+ }
+
+ return devm_snd_soc_register_component(tas2780->dev,
+ &soc_component_driver_tas2780, tas2780_dai_driver,
+ ARRAY_SIZE(tas2780_dai_driver));
+}
+
+static const struct i2c_device_id tas2780_i2c_id[] = {
+ { "tas2780"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2780_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2780_of_match[] = {
+ { .compatible = "ti,tas2780" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2780_of_match);
+#endif
+
+static struct i2c_driver tas2780_i2c_driver = {
+ .driver = {
+ .name = "tas2780",
+ .of_match_table = of_match_ptr(tas2780_of_match),
+ },
+ .probe = tas2780_i2c_probe,
+ .id_table = tas2780_i2c_id,
+};
+module_i2c_driver(tas2780_i2c_driver);
+
+MODULE_AUTHOR("Raphael Xu <raphael-xu@ti.com>");
+MODULE_DESCRIPTION("TAS2780 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2780.h b/sound/soc/codecs/tas2780.h
new file mode 100644
index 000000000000..661c25df4e29
--- /dev/null
+++ b/sound/soc/codecs/tas2780.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * TAS2780.h - ALSA SoC Texas Instruments TAS2780 Mono Audio Amplifier
+ *
+ * Copyright (C) 2020-2022 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Raphael Xu <raphael-xu@ti.com>
+ */
+
+#ifndef __TAS2780_H__
+#define __TAS2780_H__
+
+/* Book Control Register */
+#define TAS2780_BOOKCTL_PAGE 0
+#define TAS2780_BOOKCTL_REG 127
+#define TAS2780_REG(page, reg) ((page * 128) + reg)
+
+/* Page */
+#define TAS2780_PAGE TAS2780_REG(0X0, 0x00)
+#define TAS2780_PAGE_PAGE_MASK 255
+
+/* Software Reset */
+#define TAS2780_SW_RST TAS2780_REG(0X0, 0x01)
+#define TAS2780_RST BIT(0)
+
+/* Power Control */
+#define TAS2780_PWR_CTRL TAS2780_REG(0X0, 0x02)
+#define TAS2780_PWR_CTRL_MASK GENMASK(1, 0)
+#define TAS2780_PWR_CTRL_ACTIVE 0x0
+#define TAS2780_PWR_CTRL_MUTE BIT(0)
+#define TAS2780_PWR_CTRL_SHUTDOWN BIT(1)
+
+#define TAS2780_VSENSE_POWER_EN 3
+#define TAS2780_ISENSE_POWER_EN 4
+
+/* Digital Volume Control */
+#define TAS2780_DVC TAS2780_REG(0X0, 0x1a)
+#define TAS2780_DVC_MAX 0xc9
+
+#define TAS2780_CHNL_0 TAS2780_REG(0X0, 0x03)
+
+/* TDM Configuration Reg0 */
+#define TAS2780_TDM_CFG0 TAS2780_REG(0X0, 0x08)
+#define TAS2780_TDM_CFG0_SMP_MASK BIT(5)
+#define TAS2780_TDM_CFG0_SMP_48KHZ 0x0
+#define TAS2780_TDM_CFG0_SMP_44_1KHZ BIT(5)
+#define TAS2780_TDM_CFG0_MASK GENMASK(3, 1)
+#define TAS2780_TDM_CFG0_44_1_48KHZ BIT(3)
+#define TAS2780_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1))
+
+/* TDM Configuration Reg1 */
+#define TAS2780_TDM_CFG1 TAS2780_REG(0X0, 0x09)
+#define TAS2780_TDM_CFG1_MASK GENMASK(5, 1)
+#define TAS2780_TDM_CFG1_51_SHIFT 1
+#define TAS2780_TDM_CFG1_RX_MASK BIT(0)
+#define TAS2780_TDM_CFG1_RX_RISING 0x0
+#define TAS2780_TDM_CFG1_RX_FALLING BIT(0)
+
+/* TDM Configuration Reg2 */
+#define TAS2780_TDM_CFG2 TAS2780_REG(0X0, 0x0a)
+#define TAS2780_TDM_CFG2_RXW_MASK GENMASK(3, 2)
+#define TAS2780_TDM_CFG2_RXW_16BITS 0x0
+#define TAS2780_TDM_CFG2_RXW_24BITS BIT(3)
+#define TAS2780_TDM_CFG2_RXW_32BITS (BIT(3) | BIT(2))
+#define TAS2780_TDM_CFG2_RXS_MASK GENMASK(1, 0)
+#define TAS2780_TDM_CFG2_RXS_16BITS 0x0
+#define TAS2780_TDM_CFG2_RXS_24BITS BIT(0)
+#define TAS2780_TDM_CFG2_RXS_32BITS BIT(1)
+#define TAS2780_TDM_CFG2_SCFG_MASK GENMASK(5, 4)
+#define TAS2780_TDM_CFG2_SCFG_I2S 0x0
+#define TAS2780_TDM_CFG2_SCFG_LEFT_J BIT(4)
+#define TAS2780_TDM_CFG2_SCFG_RIGHT_J BIT(5)
+
+/* TDM Configuration Reg3 */
+#define TAS2780_TDM_CFG3 TAS2780_REG(0X0, 0x0c)
+#define TAS2780_TDM_CFG3_RXS_MASK GENMASK(7, 4)
+#define TAS2780_TDM_CFG3_RXS_SHIFT 0x4
+#define TAS2780_TDM_CFG3_MASK GENMASK(3, 0)
+
+/* TDM Configuration Reg4 */
+#define TAS2780_TDM_CFG4 TAS2780_REG(0X0, 0x0d)
+#define TAS2780_TDM_CFG4_TX_OFFSET_MASK GENMASK(3, 1)
+
+/* TDM Configuration Reg5 */
+#define TAS2780_TDM_CFG5 TAS2780_REG(0X0, 0x0e)
+#define TAS2780_TDM_CFG5_VSNS_MASK BIT(6)
+#define TAS2780_TDM_CFG5_VSNS_ENABLE BIT(6)
+#define TAS2780_TDM_CFG5_50_MASK GENMASK(5, 0)
+
+/* TDM Configuration Reg6 */
+#define TAS2780_TDM_CFG6 TAS2780_REG(0X0, 0x0f)
+#define TAS2780_TDM_CFG6_ISNS_MASK BIT(6)
+#define TAS2780_TDM_CFG6_ISNS_ENABLE BIT(6)
+#define TAS2780_TDM_CFG6_50_MASK GENMASK(5, 0)
+
+/* IC CFG */
+#define TAS2780_IC_CFG TAS2780_REG(0X0, 0x5c)
+#define TAS2780_IC_CFG_MASK GENMASK(7, 6)
+#define TAS2780_IC_CFG_ENABLE (BIT(7) | BIT(6))
+
+#endif /* __TAS2780_H__ */
diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c
new file mode 100644
index 000000000000..e24d56a14cfd
--- /dev/null
+++ b/sound/soc/codecs/tas2781-comlib-i2c.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers based on I2C
+//
+// Copyright 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
+ unsigned short chn, int book)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+ struct regmap *map = tas_priv->regmap;
+
+ if (client->addr != tasdev->dev_addr) {
+ client->addr = tasdev->dev_addr;
+ /* All tas2781s share the same regmap, clear the page
+ * inside regmap once switching to another tas2781.
+ * Register 0 at any pages and any books inside tas2781
+ * is the same one for page-switching.
+ */
+ ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d channel:%d\n",
+ __func__, ret, chn);
+ goto out;
+ }
+ }
+
+ if (tasdev->cur_book != book) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ tasdev->cur_book = book;
+ }
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+
+int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
+ unsigned short chn)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ if (client->addr != tasdev->dev_addr) {
+ client->addr = tasdev->dev_addr;
+ /* All devices share the same regmap, clear the page
+ * inside regmap once switching to another device.
+ * Register 0 at any pages and any books inside tas2781
+ * is the same one for page-switching.
+ */
+ ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ return ret;
+ }
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tasdev_chn_switch);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tas_priv, unsigned short chn,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
+ mask, value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ ret = -EINVAL;
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
+
+struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
+{
+ struct tasdevice_priv *tas_priv;
+
+ tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ return NULL;
+ tas_priv->dev = &i2c->dev;
+ tas_priv->client = (void *)i2c;
+
+ return tas_priv;
+}
+EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
+
+int tasdevice_init(struct tasdevice_priv *tas_priv)
+{
+ int ret = 0;
+ int i;
+
+ tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
+ &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto out;
+ }
+
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+ tas_priv->isspi = false;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tas_priv->tasdevice[i].cur_book = -1;
+ tas_priv->tasdevice[i].cur_prog = -1;
+ tas_priv->tasdevice[i].cur_conf = -1;
+ }
+
+ tas_priv->update_bits = tasdevice_dev_update_bits;
+ tas_priv->change_chn_book = tasdevice_change_chn_book;
+ tas_priv->dev_read = tasdevice_dev_read;
+ tas_priv->dev_bulk_read = tasdevice_dev_bulk_read;
+
+ mutex_init(&tas_priv->codec_lock);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_init);
+
+static int tasdevice_clamp(int val, int max, unsigned int invert)
+{
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask;
+ int max = mc->max;
+ int err_cnt = 0;
+ int val, i, ret;
+
+ mask = (1 << fls(max)) - 1;
+ mask <<= mc->shift;
+ val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+ for (i = 0; i < tas_priv->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_priv, i,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (!ret)
+ continue;
+ err_cnt++;
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
+ }
+
+ /* All the devices set error, return 0 */
+ return (err_cnt == tas_priv->ndev) ? 0 : 1;
+}
+EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
+
+int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret = 0;
+ int val;
+
+ /* Read the primary device */
+ ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ mask = (1 << fls(max)) - 1;
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = tasdevice_clamp(val, max, invert);
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
+
+int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get digital vol error\n",
+ __func__);
+ goto out;
+ }
+
+ val = tasdevice_clamp(val, max, invert);
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
+
+int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int err_cnt = 0;
+ int ret;
+ int val, i;
+
+ val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ ret = tasdevice_dev_write(tas_priv, i, mc->reg,
+ (unsigned int)val);
+ if (!ret)
+ continue;
+ err_cnt++;
+ dev_err(tas_priv->dev,
+ "set digital vol err in dev %d\n", i);
+ }
+
+ /* All the devices set error, return 0 */
+ return (err_cnt == tas_priv->ndev) ? 0 : 1;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
+
+void tasdevice_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret, i;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ TASDEVICE_REG_SWRESET,
+ tas_dev->chip_id >= TAS5802 ?
+ TAS5825_REG_SWRESET_RESET :
+ TASDEVICE_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "dev %d swreset fail, %d\n",
+ i, ret);
+ }
+ }
+ usleep_range(1000, 1050);
+}
+EXPORT_SYMBOL_GPL(tasdevice_reset);
+
+int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+ struct module *module,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret = 0;
+
+ /* Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ mutex_lock(&tas_priv->codec_lock);
+
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin",
+ tas_priv->name_prefix, tas_priv->dev_name,
+ tas_priv->ndev);
+ else
+ scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
+ tas_priv->dev_name, tas_priv->ndev);
+ crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_priv->codec = codec;
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+ tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+ cont);
+ if (ret)
+ dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_priv->codec_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tascodec_init);
+
+MODULE_DESCRIPTION("TAS2781 common library for I2C");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
new file mode 100644
index 000000000000..4cec9f8a00af
--- /dev/null
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
+//
+// Copyright 2023 - 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/dev_printk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/tas2781.h>
+
+int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int *val)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_read);
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
+
+int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int value)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(map, TASDEVICE_PGRG(reg),
+ value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_write);
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tas_priv, unsigned short chn,
+ unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg),
+ data, len);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write);
+
+static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (!prog)
+ return;
+
+ tas_dt = &(prog->dev_data);
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &(tas_dt->dev_blks[i]);
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+}
+
+static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_prog_blk_remove(&prog[i]);
+ kfree(prog);
+}
+
+static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (cfg) {
+ tas_dt = &(cfg->dev_data);
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &(tas_dt->dev_blks[i]);
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+ }
+}
+
+static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_cfg_blk_remove(&config[i]);
+ kfree(config);
+}
+
+void tasdevice_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+
+ if (!tas_dev->fmw)
+ return;
+
+ if (tas_fmw->programs)
+ tasdev_dsp_prog_remove(tas_fmw->programs,
+ tas_fmw->nr_programs);
+ if (tas_fmw->configs)
+ tasdev_dsp_cfg_remove(tas_fmw->configs,
+ tas_fmw->nr_configurations);
+ kfree(tas_fmw);
+ tas_dev->fmw = NULL;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dsp_remove);
+
+void tasdevice_remove(struct tasdevice_priv *tas_priv)
+{
+ mutex_destroy(&tas_priv->codec_lock);
+}
+EXPORT_SYMBOL_GPL(tasdevice_remove);
+
+MODULE_DESCRIPTION("TAS2781 common library");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
new file mode 100644
index 000000000000..78fd0a5dc6f2
--- /dev/null
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -0,0 +1,2618 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// tas2781-fmwlib.c -- TASDEVICE firmware support
+//
+// Copyright 2023 - 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas2781.h>
+#include <linux/unaligned.h>
+
+#define ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define PPC_DRIVER_CRCCHK 0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1 140
+#define TAS2781_YRAM1_PAGE 42
+#define TAS2781_YRAM1_START_REG 88
+
+#define TAS2781_YRAM2_START_PAGE 43
+#define TAS2781_YRAM2_END_PAGE 49
+#define TAS2781_YRAM2_START_REG 8
+#define TAS2781_YRAM2_END_REG 127
+
+#define TAS2781_YRAM3_PAGE 50
+#define TAS2781_YRAM3_START_REG 8
+#define TAS2781_YRAM3_END_REG 27
+
+/*should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2 0
+#define TAS2781_YRAM4_START_PAGE 50
+#define TAS2781_YRAM4_END_PAGE 60
+
+#define TAS2781_YRAM5_PAGE 61
+#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG
+#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
+#define MAIN_ALL_DEVICES_1X 0x01
+#define MAIN_DEVICE_A_1X 0x02
+#define MAIN_DEVICE_B_1X 0x03
+#define MAIN_DEVICE_C_1X 0x04
+#define MAIN_DEVICE_D_1X 0x05
+#define COEFF_DEVICE_A_1X 0x12
+#define COEFF_DEVICE_B_1X 0x13
+#define COEFF_DEVICE_C_1X 0x14
+#define COEFF_DEVICE_D_1X 0x15
+#define PRE_DEVICE_A_1X 0x22
+#define PRE_DEVICE_B_1X 0x23
+#define PRE_DEVICE_C_1X 0x24
+#define PRE_DEVICE_D_1X 0x25
+#define PRE_SOFTWARE_RESET_DEVICE_A 0x41
+#define PRE_SOFTWARE_RESET_DEVICE_B 0x42
+#define PRE_SOFTWARE_RESET_DEVICE_C 0x43
+#define PRE_SOFTWARE_RESET_DEVICE_D 0x44
+#define POST_SOFTWARE_RESET_DEVICE_A 0x45
+#define POST_SOFTWARE_RESET_DEVICE_B 0x46
+#define POST_SOFTWARE_RESET_DEVICE_C 0x47
+#define POST_SOFTWARE_RESET_DEVICE_D 0x48
+
+struct tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+struct blktyp_devidx_map {
+ unsigned char blktyp;
+ unsigned char dev_idx;
+};
+
+static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4, 1, 2
+};
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { POST_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { POST_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { POST_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+ { PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+ { POST_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+};
+
+static const struct blktyp_devidx_map ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+};
+
+static const struct blktyp_devidx_map non_ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES, 0x80 },
+ { MAIN_DEVICE_A, 0x81 },
+ { COEFF_DEVICE_A, 0xC1 },
+ { PRE_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B, 0x82 },
+ { COEFF_DEVICE_B, 0xC2 },
+ { PRE_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C, 0x83 },
+ { COEFF_DEVICE_C, 0xC3 },
+ { PRE_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D, 0x84 },
+ { COEFF_DEVICE_D, 0xC4 },
+ { PRE_DEVICE_D, 0xC4 },
+};
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ struct tasdevice_priv *tas_priv, unsigned char *config_data,
+ unsigned int config_size, int *status)
+{
+ struct tasdevice_config_info *cfg_info;
+ struct tasdev_blk_data **bk_da;
+ unsigned int config_offset = 0;
+ unsigned int i;
+
+ /* In most projects are many audio cases, such as music, handfree,
+ * receiver, games, audio-to-haptics, PMIC record, bypass mode,
+ * portrait, landscape, etc. Even in multiple audios, one or
+ * two of the chips will work for the special case, such as
+ * ultrasonic application. In order to support these variable-numbers
+ * of audio cases, flexible configs have been introduced in the
+ * dsp firmware.
+ */
+ cfg_info = kzalloc(sizeof(struct tasdevice_config_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ goto out;
+ }
+
+ if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add conf: Out of boundary\n");
+ goto out;
+ }
+ /* If in the RCA bin file are several profiles with the
+ * keyword "init", init_profile_id only store the last
+ * init profile id.
+ */
+ if (strnstr(&config_data[config_offset], "init", 64)) {
+ tas_priv->rcabin.init_profile_id =
+ tas_priv->rcabin.ncfgs - 1;
+ dev_dbg(tas_priv->dev, "%s: init profile id = %d\n",
+ __func__, tas_priv->rcabin.init_profile_id);
+ }
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add config: Out of boundary\n");
+ goto out;
+ }
+
+ /* convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ /* Several kinds of dsp/algorithm firmwares can run on tas2781,
+ * the number and size of blk are not fixed and different among
+ * these firmwares.
+ */
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(struct tasdev_blk_data *), GFP_KERNEL);
+ if (!bk_da) {
+ *status = -ENOMEM;
+ goto out;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d nblocks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ bk_da[i] = kzalloc(sizeof(struct tasdev_blk_data), GFP_KERNEL);
+ if (!bk_da[i]) {
+ *status = -ENOMEM;
+ break;
+ }
+
+ bk_da[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (bk_da[i]->dev_idx == 0)
+ cfg_info->active_dev =
+ (1 << tas_priv->ndev) - 1;
+ else
+ cfg_info->active_dev |= 1 <<
+ (bk_da[i]->dev_idx - 1);
+
+ }
+ bk_da[i]->yram_checksum =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_be32(&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d blks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ /* instead of kzalloc+memcpy */
+ bk_da[i]->regdata = kmemdup(&config_data[config_offset],
+ bk_da[i]->block_size, GFP_KERNEL);
+ if (!bk_da[i]->regdata) {
+ *status = -ENOMEM;
+ goto out;
+ }
+
+ config_offset += bk_da[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+
+out:
+ return cfg_info;
+}
+
+int tasdevice_rca_parser(void *context, const struct firmware *fmw)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_config_info **cfg_info;
+ struct tasdevice_rca_hdr *fw_hdr;
+ struct tasdevice_rca *rca;
+ unsigned int total_config_sz = 0;
+ unsigned char *buf;
+ int offset = 0;
+ int ret = 0;
+ int i;
+
+ rca = &(tas_priv->rcabin);
+ /* Initialize to none */
+ rca->init_profile_id = -1;
+ fw_hdr = &(rca->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "Failed to read %s\n",
+ tas_priv->rca_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(tas_priv->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_priv->dev, "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+ if (fw_hdr->ndev != tas_priv->ndev) {
+ dev_err(tas_priv->dev,
+ "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n",
+ fw_hdr->ndev, tas_priv->ndev);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+ if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+ dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n");
+ ret = -EINVAL;
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(tas_priv->dev, "Bin file error!\n");
+ ret = -EINVAL;
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ ret = -ENOMEM;
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ rca->ncfgs += 1;
+ cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ }
+out:
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, "SND_SOC_TAS2781_FMWLIB");
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block)
+{
+
+ struct blktyp_devidx_map *p =
+ (struct blktyp_devidx_map *)non_ppc3_mapping_table;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+
+ int i, n = ARRAY_SIZE(non_ppc3_mapping_table);
+ unsigned char dev_idx = 0;
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN) {
+ p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table;
+ n = ARRAY_SIZE(ppc3_tas2781_mapping_table);
+ } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION_BASE) {
+ p = (struct blktyp_devidx_map *)ppc3_mapping_table;
+ n = ARRAY_SIZE(ppc3_mapping_table);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (block->type == p[i].blktyp) {
+ dev_idx = p[i].dev_idx;
+ break;
+ }
+ }
+
+ return dev_idx;
+}
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 16 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ /* convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ block->type = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ block->pchksum = data[offset];
+ offset++;
+
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ block->ychksum = data[offset];
+ offset++;
+
+ block->blk_size = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ block->nr_subblocks = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ /* fixed m68k compiling issue:
+ * 1. mapping table can save code field.
+ * 2. storing the dev_idx as a member of block can reduce unnecessary
+ * time and system resource comsumption of dev_idx mapping every
+ * time the block data writing to the dsp.
+ */
+ block->dev_idx = map_dev_idx(tas_fmw, block);
+
+ if (offset + block->blk_size > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL);
+ if (!block->data) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ offset += block->blk_size;
+
+out:
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ img_data->nr_blk = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(struct tasdev_blk), GFP_KERNEL);
+ if (!img_data->dev_blks) {
+ offset = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &(img_data->dev_blks[i]);
+ offset = fw_parse_block_data_kernel(tas_fmw, blk, fmw, offset);
+ if (offset < 0) {
+ offset = -EINVAL;
+ break;
+ }
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_tas5825_program_data_kernel(
+ struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ if (offset + 72 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ /* Skip 65 unused byts*/
+ offset += 65;
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return offset;
+}
+
+static int fw_parse_tas5825_configuration_data_kernel(
+ struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 80 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(config->name, &data[offset], 64);
+ /* Skip extra 8 bytes*/
+ offset += 72;
+ offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return offset;
+}
+
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ if (offset + 72 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ /*skip 72 unused byts*/
+ offset += 72;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data_kernel(
+ struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 80 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ /*skip extra 16 bytes*/
+ offset += 80;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+static void fct_param_address_parser(struct cali_reg *r,
+ struct tasdevice_fw *tas_fmw, const unsigned char *data)
+{
+ struct fct_param_address *p = &tas_fmw->fct_par_addr;
+ unsigned int i;
+
+ /*
+ * Calibration parameters locations and data schema in dsp firmware.
+ * The number of items are flexible, but not more than 20. The dsp tool
+ * will reseve 20*24-byte space for fct params. In some cases, the
+ * number of fct param is less than 20, the data will be saved from the
+ * beginning, the rest part will be stuffed with zero.
+ *
+ * fct_param_num (not more than 20)
+ * for (i = 0; i < fct_param_num; i++) {
+ * Alias of fct param (20 bytes)
+ * Book (1 byte)
+ * Page (1 byte)
+ * Offset (1 byte)
+ * CoeffLength (1 byte) = 0x1
+ * }
+ * if (20 - fct_param_num)
+ * 24*(20 - fct_param_num) pieces of '0' as stuffing
+ *
+ * As follow:
+ * umg_SsmKEGCye = Book, Page, Offset, CoeffLength
+ * iks_E0 = Book, Page, Offset, CoeffLength
+ * yep_LsqM0 = Book, Page, Offset, CoeffLength
+ * oyz_U0_ujx = Book, Page, Offset, CoeffLength
+ * iks_GC_GMgq = Book, Page, Offset, CoeffLength
+ * gou_Yao = Book, Page, Offset, CoeffLength
+ * kgd_Wsc_Qsbp = Book, Page, Offset, CoeffLength
+ * yec_CqseSsqs = Book, Page, Offset, CoeffLength
+ * iks_SogkGgog2 = Book, Page, Offset, CoeffLength
+ * yec_Sae_Y = Book, Page, Offset, CoeffLength
+ * Re_Int = Book, Page, Offset, CoeffLength
+ * SigFlag = Book, Page, Offset, CoeffLength
+ * a1_Int = Book, Page, Offset, CoeffLength
+ * a2_Int = Book, Page, Offset, CoeffLength
+ */
+ for (i = 0; i < 20; i++) {
+ const unsigned char *dat = &data[24 * i];
+
+ /*
+ * check whether current fct param is empty.
+ */
+ if (dat[23] != 1)
+ break;
+
+ if (!strncmp(dat, "umg_SsmKEGCye", 20))
+ r->pow_reg = TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* high 32-bit of real-time spk impedance */
+ else if (!strncmp(dat, "iks_E0", 20))
+ r->r0_reg = TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* inverse of real-time spk impedance */
+ else if (!strncmp(dat, "yep_LsqM0", 20))
+ r->invr0_reg =
+ TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* low 32-bit of real-time spk impedance */
+ else if (!strncmp(dat, "oyz_U0_ujx", 20))
+ r->r0_low_reg =
+ TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* Delta Thermal Limit */
+ else if (!strncmp(dat, "iks_GC_GMgq", 20))
+ r->tlimit_reg =
+ TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* Thermal data for PG 1.0 device */
+ else if (!strncmp(dat, "gou_Yao", 20))
+ memcpy(p->thr, &dat[20], 3);
+ /* Pilot tone enable flag, usually the sine wave */
+ else if (!strncmp(dat, "kgd_Wsc_Qsbp", 20))
+ memcpy(p->plt_flg, &dat[20], 3);
+ /* Pilot tone gain for calibration */
+ else if (!strncmp(dat, "yec_CqseSsqs", 20))
+ memcpy(p->sin_gn, &dat[20], 3);
+ /* Pilot tone gain for calibration, useless in PG 2.0 */
+ else if (!strncmp(dat, "iks_SogkGgog2", 20))
+ memcpy(p->sin_gn2, &dat[20], 3);
+ /* Thermal data for PG 2.0 device */
+ else if (!strncmp(dat, "yec_Sae_Y", 20))
+ memcpy(p->thr2, &dat[20], 3);
+ /* Spk Equivalent Resistance in fixed-point format */
+ else if (!strncmp(dat, "Re_Int", 20))
+ memcpy(p->r0_reg, &dat[20], 3);
+ /* Check whether the spk connection is open */
+ else if (!strncmp(dat, "SigFlag", 20))
+ memcpy(p->tf_reg, &dat[20], 3);
+ /* check spk resonant frequency */
+ else if (!strncmp(dat, "a1_Int", 20))
+ memcpy(p->a1_reg, &dat[20], 3);
+ /* check spk resonant frequency */
+ else if (!strncmp(dat, "a2_Int", 20))
+ memcpy(p->a2_reg, &dat[20], 3);
+ }
+}
+
+static int fw_parse_fct_param_address(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct calidata *cali_data = &tas_priv->cali_data;
+ struct cali_reg *r = &cali_data->cali_reg_array;
+ const unsigned char *data = fmw->data;
+
+ if (offset + 520 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+
+ /* skip reserved part */
+ offset += 40;
+
+ fct_param_address_parser(r, tas_fmw, &data[offset]);
+
+ offset += 480;
+
+ return offset;
+}
+
+static int fw_parse_variable_header_kernel(
+ struct tasdevice_priv *tas_priv, const struct firmware *fmw,
+ int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_prog *program;
+ struct tasdevice_config *config;
+ const unsigned char *buf = fmw->data;
+ unsigned short max_confs;
+ unsigned int i;
+
+ if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ fw_hdr->device_family = get_unaligned_be16(&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s:not TAS device\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->device = get_unaligned_be16(&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+ if (fw_hdr->ndev != tas_priv->ndev) {
+ dev_err(tas_priv->dev,
+ "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_priv->ndev);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs >
+ TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_priv->dev, "mnPrograms is invalid\n");
+ offset = -EINVAL;
+ goto out;
+ }
+
+ tas_fmw->programs = kcalloc(tas_fmw->nr_programs,
+ sizeof(struct tasdevice_prog), GFP_KERNEL);
+ if (!tas_fmw->programs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ program->prog_size = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused prog_size */
+ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs);
+
+ tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ /* The max number of config in firmware greater than 4 pieces of
+ * tas2781s is different from the one lower than 4 pieces of
+ * tas2781s.
+ */
+ max_confs = (fw_hdr->ndev >= 4) ?
+ TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS :
+ TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->nr_configurations == 0 ||
+ tas_fmw->nr_configurations > max_confs) {
+ dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (!tas_fmw->configs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ config = &(tas_fmw->configs[i]);
+ config->cfg_size = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused configs */
+ offset += 4 * (max_confs - tas_fmw->nr_programs);
+
+out:
+ return offset;
+}
+
+static int tasdevice_process_block(void *context, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+ int subblk_offset, chn, chnend, rc;
+ unsigned char subblk_typ = data[1];
+ int blktyp = dev_idx & 0xC0;
+ int idx = dev_idx & 0x3F;
+ bool is_err = false;
+
+ if (idx) {
+ chn = idx - 1;
+ chnend = idx;
+ } else {
+ if (tas_priv->isspi) {
+ chn = tas_priv->index;
+ chnend = chn + 1;
+ } else {
+ chn = 0;
+ chnend = tas_priv->ndev;
+ }
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_priv->tasdevice[chn].is_loading == false)
+ continue;
+
+ is_err = false;
+ subblk_offset = 2;
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W: {
+ int i;
+ unsigned short len = get_unaligned_be16(&data[2]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_priv->dev,
+ "process_block: Out of boundary\n");
+ is_err = true;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_priv->dev,
+ "process_block: single write error\n");
+ }
+ subblk_offset += 4;
+ }
+ }
+ break;
+ case TASDEVICE_CMD_BURST: {
+ unsigned short len = get_unaligned_be16(&data[2]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_priv->dev,
+ "%s: BST Out of boundary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ if (len % 4) {
+ dev_err(tas_priv->dev,
+ "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ break;
+ }
+
+ rc = tasdevice_dev_bulk_write(tas_priv, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ &(data[subblk_offset + 4]), len);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_priv->dev,
+ "%s: bulk_write error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += (len + 4);
+ }
+ break;
+ case TASDEVICE_CMD_DELAY: {
+ unsigned int sleep_time = 0;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_priv->dev,
+ "%s: delay Out of boundary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ sleep_time = get_unaligned_be16(&data[2]) * 1000;
+ usleep_range(sleep_time, sleep_time + 50);
+ subblk_offset += 2;
+ }
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_priv->dev,
+ "%s: bit write Out of boundary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ rc = tas_priv->update_bits(tas_priv, chn,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1],
+ data[subblk_offset + 5]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_priv->dev,
+ "%s: update_bits error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ }
+ if (is_err == true && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_priv->tasdevice[chn].cur_prog = -1;
+ tas_priv->tasdevice[chn].cur_conf = -1;
+ } else
+ tas_priv->tasdevice[chn].cur_conf = -1;
+ }
+ }
+
+ return subblk_offset;
+}
+
+void tasdevice_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdev_blk_data **blk_data;
+ int j, k, chn, chnend;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) {
+ dev_err(tas_priv->dev, "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ return;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(tas_priv->dev,
+ "block_type should be in range from 2 to 5\n");
+ break;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < (int)blk_data[j]->n_subblks; k++) {
+ if (blk_data[j]->dev_idx) {
+ chn = blk_data[j]->dev_idx - 1;
+ chnend = blk_data[j]->dev_idx;
+ } else {
+ chn = 0;
+ chnend = tas_priv->ndev;
+ }
+ for (; chn < chnend; chn++)
+ tas_priv->tasdevice[chn].is_loading = true;
+
+ rc = tasdevice_process_block(tas_priv,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += rc;
+ if (blk_data[j]->block_size < length) {
+ dev_err(tas_priv->dev,
+ "%s: %u %u out of boundary\n",
+ __func__, length,
+ blk_data[j]->block_size);
+ break;
+ }
+ }
+ if (length != blk_data[j]->block_size)
+ dev_err(tas_priv->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, "SND_SOC_TAS2781_FMWLIB");
+
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ const unsigned int blk_size = block->blk_size;
+ unsigned int i, length;
+ unsigned char *data = block->data;
+
+ for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ block->dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: %u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += (unsigned int)rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev, "%s: %u %u out of boundary\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int fw_parse_variable_hdr(struct tasdevice_priv
+ *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr,
+ const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ int len = strlen((char *)&buf[offset]);
+
+ len++;
+
+ if (offset + len + 8 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ offset += len;
+
+ fw_hdr->device_family = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s: not TAS device\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+
+ fw_hdr->device = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+out:
+ return offset;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv
+ *tas_priv, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+ offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+ if (offset < 0)
+ goto out;
+ if (fw_hdr->ndev != tas_priv->ndev) {
+ dev_err(tas_priv->dev,
+ "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_priv->ndev);
+ offset = -EINVAL;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Type error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ block->type = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) {
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "PChkSumPresent error\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ block->pchksum = data[offset];
+ offset++;
+
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+
+ block->nr_cmds = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ n = block->nr_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ offset = -EINVAL;
+ goto out;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (!block->data) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ offset += n;
+
+out:
+ return offset;
+}
+
+/* When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i;
+ int n;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Name error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += n;
+ img_data->nr_blk = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(struct tasdev_blk), GFP_KERNEL);
+ if (!img_data->dev_blks) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &(img_data->dev_blks[i]);
+ offset = fw_parse_block_data(tas_fmw, blk, fmw, offset);
+ if (offset < 0) {
+ offset = -EINVAL;
+ goto out;
+ }
+ }
+
+out:
+ return offset;
+}
+
+/* When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *buf = (unsigned char *)fmw->data;
+ struct tasdevice_prog *program;
+ int i;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_programs == 0) {
+ /*Not error in calibration Data file, return directly*/
+ dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n",
+ __func__);
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog),
+ GFP_KERNEL);
+ if (!tas_fmw->programs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ int n = 0;
+
+ program = &(tas_fmw->programs[i]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ /* skip '\0' and 5 unused bytes */
+ n += 6;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+/* When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_configuration_data(
+ struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+ int n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_configurations == 0) {
+ dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__);
+ /*Not error for calibration Data file, return directly*/
+ goto out;
+ }
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (!tas_fmw->configs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "File Size err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n += 15;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+static bool check_inpage_yram_rg(struct tas_crc *cd,
+ unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+
+ if (reg <= TAS2781_YRAM5_END_REG &&
+ reg >= TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_END_REG)
+ cd->len = TAS2781_YRAM5_END_REG - reg + 1;
+ else
+ cd->len = len;
+ cd->offset = reg;
+ in = true;
+ } else if (reg < TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_START_REG) {
+ cd->offset = TAS2781_YRAM5_START_REG;
+ cd->len = len - TAS2781_YRAM5_START_REG + reg;
+ in = true;
+ }
+ }
+
+ return in;
+}
+
+static bool check_inpage_yram_bk1(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg + len > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len = len - TAS2781_YRAM1_START_REG + reg;
+ in = true;
+ }
+ } else if (page == TAS2781_YRAM3_PAGE)
+ in = check_inpage_yram_rg(cd, reg, len);
+
+ return in;
+}
+
+/* Return Code:
+ * true -- the registers are in the inpage yram
+ * false -- the registers are NOT in the inpage yram
+ */
+static bool check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ in = check_inpage_yram_bk1(cd, page, reg, len);
+ goto end;
+ }
+ if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE)
+ in = check_inpage_yram_rg(cd, reg, len);
+
+end:
+ return in;
+}
+
+static bool check_inblock_yram_bk(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if ((page >= TAS2781_YRAM4_START_PAGE &&
+ page <= TAS2781_YRAM4_END_PAGE) ||
+ (page >= TAS2781_YRAM2_START_PAGE &&
+ page <= TAS2781_YRAM2_END_PAGE)) {
+ if (reg <= TAS2781_YRAM2_END_REG &&
+ reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg < TAS2781_YRAM2_START_REG) {
+ if (reg + len - 1 >= TAS2781_YRAM2_START_REG) {
+ cd->offset = TAS2781_YRAM2_START_REG;
+ cd->len = reg + len - TAS2781_YRAM2_START_REG;
+ in = true;
+ }
+ }
+ }
+
+ return in;
+}
+
+/* Return Code:
+ * true -- the registers are in the inblock yram
+ * false -- the registers are NOT in the inblock yram
+ */
+static bool check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2)
+ in = check_inblock_yram_bk(cd, page, reg, len);
+
+ return in;
+}
+
+static bool check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in;
+
+ in = check_inpage_yram(cd, book, page, reg, len);
+ if (in)
+ goto end;
+ in = check_inblock_yram(cd, book, page, reg, len);
+
+end:
+ return in;
+}
+
+static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
+ unsigned short chn, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ struct tas_crc crc_data;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128];
+ int ret = 0;
+ int i;
+ bool in;
+
+ if ((reg + len - 1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ in = check_yram(&crc_data, book, page, reg, len);
+ if (!in)
+ goto end;
+
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = tasdevice->dev_bulk_read(tasdevice, chn,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ <= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)
+ + 4)))
+ /*DSP swap command, bypass */
+ continue;
+ else
+ crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i],
+ 1, 0);
+ }
+
+ ret = crc_chksum;
+
+end:
+ return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ unsigned short chl, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ struct tas_crc crc_data;
+ unsigned int nData1;
+ int ret = 0;
+ bool in;
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ in = check_yram(&crc_data, book, page, reg, 1);
+ if (!in)
+ goto end;
+ ret = tasdevice->dev_read(tasdevice, chl,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ goto end;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg, val, nData1);
+ tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK;
+ ret = -EAGAIN;
+ goto end;
+ }
+
+ ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0);
+
+end:
+ return ret;
+}
+
+static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev)
+{
+ if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A)
+ || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C)
+ || (type == MAIN_DEVICE_D))
+ dev->cur_prog = -1;
+ else
+ dev->cur_conf = -1;
+}
+
+static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned int len,
+ unsigned char val, unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1)
+ ret = tasdev_multibytes_chksum(tas_priv, chn, book, page, reg,
+ len);
+ else
+ ret = do_singlereg_checksum(tas_priv, chn, book, page, reg,
+ val);
+
+ if (ret > 0) {
+ *crc_chksum += (unsigned char)ret;
+ goto end;
+ }
+
+ if (ret != -EAGAIN)
+ goto end;
+
+ block->nr_retry--;
+ if (block->nr_retry > 0)
+ goto end;
+
+ set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]);
+
+end:
+ return ret;
+}
+
+static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char *data,
+ unsigned int len, unsigned int *nr_cmds,
+ unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_write(tas_priv, chn,
+ TASDEVICE_REG(book, page, reg), data + 3, len);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block, chn,
+ book, page, reg, len, 0, crc_chksum);
+ } else {
+ ret = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_REG(book, page, reg), data[3]);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block, chn, book,
+ page, reg, 1, data[3], crc_chksum);
+ }
+
+ if (!block->is_ychksum_present || ret >= 0) {
+ *nr_cmds += 1;
+ if (len >= 2)
+ *nr_cmds += ((len - 2) / 4) + 1;
+ }
+
+end:
+ return ret;
+}
+
+static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn)
+{
+ unsigned int nr_value;
+ int ret;
+
+ ret = tas_priv->dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG,
+ &nr_value);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn);
+ set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]);
+ goto end;
+ }
+
+ if ((nr_value & 0xff) != block->pchksum) {
+ dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__,
+ chn);
+ dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n",
+ block->pchksum, (nr_value & 0xff));
+ tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK;
+ ret = -EAGAIN;
+ block->nr_retry--;
+
+ if (block->nr_retry <= 0)
+ set_err_prg_cfg(block->type,
+ &tas_priv->tasdevice[chn]);
+ } else
+ tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK;
+
+end:
+ return ret;
+}
+
+static int tasdev_load_blk(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn)
+{
+ unsigned int sleep_time;
+ unsigned int len;
+ unsigned int nr_cmds;
+ unsigned char *data;
+ unsigned char crc_chksum = 0;
+ unsigned char offset;
+ unsigned char book;
+ unsigned char page;
+ unsigned char val;
+ int ret = 0;
+
+ while (block->nr_retry > 0) {
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_CHECKSUM_REG, 0);
+ if (ret < 0)
+ break;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ nr_cmds = 0;
+
+ while (nr_cmds < block->nr_cmds) {
+ data = block->data + nr_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ nr_cmds++;
+ /*Single byte write*/
+ if (offset <= 0x7F) {
+ ret = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present) {
+ ret = tasdev_bytes_chksum(tas_priv,
+ block, chn, book, page, offset,
+ 1, val, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ continue;
+ }
+ /*sleep command*/
+ if (offset == 0x81) {
+ /*book -- data[0] page -- data[1]*/
+ sleep_time = ((book << 8) + page)*1000;
+ usleep_range(sleep_time, sleep_time + 50);
+ continue;
+ }
+ /*Multiple bytes write*/
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ ret = tasdev_multibytes_wr(tas_priv,
+ block, chn, book, page, offset, data,
+ len, &nr_cmds, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ }
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) /*err in current device, skip it*/
+ break;
+
+ if (block->is_pchksum_present) {
+ ret = tasdev_block_chksum(tas_priv, block, chn);
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) /*err in current device, skip it*/
+ break;
+ }
+
+ if (block->is_ychksum_present) {
+ /* TBD, open it when FW ready */
+ dev_err(tas_priv->dev,
+ "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_priv->tasdevice[chn].err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ }
+ /*skip current blk*/
+ break;
+ }
+
+end:
+ return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ int chnend = 0;
+ int ret = 0;
+ int chn = 0;
+ int rc = 0;
+
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ chn = 0;
+ chnend = tas_priv->ndev;
+ break;
+ case MAIN_DEVICE_A:
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ chn = 0;
+ chnend = 1;
+ break;
+ case MAIN_DEVICE_B:
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ chn = 1;
+ chnend = 2;
+ break;
+ case MAIN_DEVICE_C:
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ chn = 2;
+ chnend = 3;
+ break;
+ case MAIN_DEVICE_D:
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ chn = 3;
+ chnend = 4;
+ break;
+ default:
+ dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n",
+ block->type);
+ break;
+ }
+
+ for (; chn < chnend; chn++) {
+ block->nr_retry = 6;
+ if (tas_priv->tasdevice[chn].is_loading == false)
+ continue;
+ ret = tasdev_load_blk(tas_priv, block, chn);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n",
+ chn, block->type);
+ rc |= ret;
+ }
+
+ return rc;
+}
+
+static void dspbin_type_check(struct tasdevice_priv *tas_priv,
+ unsigned int ppcver)
+{
+ if (ppcver >= PPC3_VERSION_TAS2781_ALPHA_MIN) {
+ if (ppcver >= PPC3_VERSION_TAS2781_BETA_MIN)
+ tas_priv->dspbin_typ = TASDEV_BETA;
+ else if (ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN)
+ tas_priv->dspbin_typ = TASDEV_BASIC;
+ else
+ tas_priv->dspbin_typ = TASDEV_ALPHA;
+ }
+ if ((tas_priv->dspbin_typ != TASDEV_BASIC) &&
+ (ppcver < PPC3_VERSION_TAS5825_BASE))
+ tas_priv->fw_parse_fct_param_address =
+ fw_parse_fct_param_address;
+}
+
+static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
+ unsigned int drv_ver, unsigned int ppcver)
+{
+ int rc = 0;
+
+ if (drv_ver == 0x100) {
+ if (ppcver >= PPC3_VERSION_TAS5825_BASE) {
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_tas5825_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_tas5825_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ dspbin_type_check(tas_priv, ppcver);
+ } else if (ppcver >= PPC3_VERSION_BASE) {
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ dspbin_type_check(tas_priv, ppcver);
+ } else {
+ switch (ppcver) {
+ case 0x00:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_priv->dev,
+ "%s: PPCVer must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION_BASE);
+ dev_err(tas_priv->dev, " Current:0x%02x\n",
+ ppcver);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_priv->dev,
+ "DrvVer must be 0x0, 0x230 or above 0x230 ");
+ dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int load_calib_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &(dev_data->dev_blks[i]);
+ ret = tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ static const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 };
+ const unsigned char *buf = (unsigned char *)fmw->data;
+
+ if (offset + 92 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+
+ /* Convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_priv->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]);
+ offset += 72;
+
+ out:
+ return offset;
+}
+
+static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+ offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+ if (offset < 0)
+ goto out;
+ if (fw_hdr->ndev != 1) {
+ dev_err(tas_priv->dev,
+ "%s: calbin must be 1, but currently ndev(%u)\n",
+ __func__, fw_hdr->ndev);
+ offset = -EINVAL;
+ }
+
+out:
+ return offset;
+}
+
+/* When calibrated data parsing error occurs, DSP can still work with default
+ * calibrated data, memory resource related to calibrated data will be
+ * released in the tasdevice_codec_remove.
+ */
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_calibration *calibration;
+ unsigned char *data = (unsigned char *)fmw->data;
+ unsigned int i, n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ tas_fmw->nr_calibrations = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_calibrations != 1) {
+ dev_err(tas_priv->dev,
+ "%s: only supports one calibration (%d)!\n",
+ __func__, tas_fmw->nr_calibrations);
+ goto out;
+ }
+
+ tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations,
+ sizeof(struct tasdevice_calibration), GFP_KERNEL);
+ if (!tas_fmw->calibrations) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "Calibrations error\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ calibration = &(tas_fmw->calibrations[i]);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ /* skip '\0' and 2 unused bytes */
+ n += 3;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+int tas2781_load_calibration(void *context, char *file_name,
+ unsigned short i)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+ struct tasdevice *tasdev = &(tas_priv->tasdevice[i]);
+ const struct firmware *fw_entry = NULL;
+ struct tasdevice_fw *tas_fmw;
+ struct firmware fmw;
+ int offset = 0;
+ int ret;
+
+ ret = request_firmware(&fw_entry, file_name, tas_priv->dev);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: Request firmware %s failed\n",
+ __func__, file_name);
+ goto out;
+ }
+
+ if (!fw_entry->size) {
+ dev_err(tas_priv->dev, "%s: file read error: size = %lu\n",
+ __func__, (unsigned long)fw_entry->size);
+ ret = -EINVAL;
+ goto out;
+ }
+ fmw.size = fw_entry->size;
+ fmw.data = fw_entry->data;
+
+ tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw),
+ GFP_KERNEL);
+ if (!tasdev->cali_data_fmw) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset);
+ if (offset == -EINVAL) {
+ dev_err(tas_priv->dev, "fw_parse_header EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset);
+ if (offset == -EINVAL) {
+ dev_err(tas_priv->dev,
+ "%s: fw_parse_variable_header_cal EXIT!\n", __func__);
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset);
+ if (offset < 0) {
+ dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset);
+ if (offset < 0) {
+ dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset);
+ if (offset < 0) {
+ dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+
+out:
+ release_firmware(fw_entry);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB");
+
+static int tasdevice_dspfw_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr;
+ struct tasdevice_fw *tas_fmw;
+ int offset = 0;
+ int ret;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_priv->coef_binaryname);
+ return -EINVAL;
+ }
+
+ tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (!tas_priv->fmw)
+ return -ENOMEM;
+
+ tas_fmw = tas_priv->fmw;
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset);
+
+ if (offset == -EINVAL)
+ return -EINVAL;
+
+ fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+ /* Support different versions of firmware */
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ case 0x503:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ case 0x401:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ ret = dspfw_default_callback(tas_priv,
+ fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw,
+ offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_configuration_data(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ return offset;
+
+ if (tas_priv->fw_parse_fct_param_address) {
+ offset = tas_priv->fw_parse_fct_param_address(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return 0;
+}
+
+int tasdevice_dsp_parser(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+ const struct firmware *fw_entry;
+ int ret;
+
+ ret = request_firmware(&fw_entry, tas_priv->coef_binaryname,
+ tas_priv->dev);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: load %s error\n", __func__,
+ tas_priv->coef_binaryname);
+ goto out;
+ }
+
+ ret = tasdevice_dspfw_ready(fw_entry, tas_priv);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB");
+
+static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw)
+{
+ struct tasdevice_calibration *calibration;
+ struct tasdev_blk *block;
+ struct tasdevice_data *im;
+ unsigned int blks;
+ int i;
+
+ if (!tas_fmw->calibrations)
+ goto out;
+
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ calibration = &(tas_fmw->calibrations[i]);
+ if (!calibration)
+ continue;
+
+ im = &(calibration->dev_data);
+
+ if (!im->dev_blks)
+ continue;
+
+ for (blks = 0; blks < im->nr_blk; blks++) {
+ block = &(im->dev_blks[blks]);
+ if (!block)
+ continue;
+ kfree(block->data);
+ }
+ kfree(im->dev_blks);
+ }
+ kfree(tas_fmw->calibrations);
+out:
+ kfree(tas_fmw);
+}
+
+void tasdevice_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev;
+ int i;
+
+ if (!tas_priv)
+ return;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tasdev = &(tas_priv->tasdevice[i]);
+ if (!tasdev->cali_data_fmw)
+ continue;
+ tas2781_clear_calfirmware(tasdev->cali_data_fmw);
+ tasdev->cali_data_fmw = NULL;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, "SND_SOC_TAS2781_FMWLIB");
+
+void tasdevice_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **ci = rca->cfg_info;
+ int i, j;
+
+ if (!ci)
+ return;
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (!ci[i])
+ continue;
+ if (ci[i]->blk_data) {
+ for (j = 0; j < (int)ci[i]->real_nblocks; j++) {
+ if (!ci[i]->blk_data[j])
+ continue;
+ kfree(ci[i]->blk_data[j]->regdata);
+ kfree(ci[i]->blk_data[j]);
+ }
+ kfree(ci[i]->blk_data);
+ }
+ kfree(ci[i]);
+ }
+ kfree(ci);
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, "SND_SOC_TAS2781_FMWLIB");
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &(dev_data->dev_blks[i]);
+ ret = tas_priv->tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static void tasdev_load_calibrated_data(struct tasdevice_priv *priv, int i)
+{
+ struct tasdevice_fw *cal_fmw = priv->tasdevice[i].cali_data_fmw;
+ struct calidata *cali_data = &priv->cali_data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ unsigned char *data = cali_data->data;
+ struct tasdevice_calibration *cal;
+ int k = i * (cali_data->cali_dat_sz_per_dev + 1);
+ int rc;
+
+ /* Load the calibrated data from cal bin file */
+ if (!priv->is_user_space_calidata && cal_fmw) {
+ cal = cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(priv, &cal->dev_data);
+ return;
+ }
+ if (!priv->is_user_space_calidata)
+ return;
+ /* load calibrated data from user space */
+ if (data[k] != i) {
+ dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n",
+ __func__, i);
+ return;
+ }
+ k++;
+
+ rc = tasdevice_dev_bulk_write(priv, i, p->r0_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_reg bulk_wr err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->r0_low_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_low_reg err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->invr0_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d invr0_reg err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->pow_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d pow_reg bulk_wr err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->tlimit_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d tlimit_reg err = %d\n", i, rc);
+ return;
+ }
+}
+
+int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *conf;
+ int prog_status = 0;
+ int status, i;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (cfg_no >= tas_fmw->nr_configurations) {
+ dev_err(tas_priv->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->nr_configurations);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ goto out;
+ }
+
+ if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 ||
+ !cfg_info) {
+ dev_err(tas_priv->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ goto out;
+ }
+
+ for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) {
+ if (cfg_info[rca_conf_no]->active_dev & (1 << i)) {
+ if (prm_no >= 0
+ && (tas_priv->tasdevice[i].cur_prog != prm_no
+ || tas_priv->force_fwload_status)) {
+ tas_priv->tasdevice[i].cur_conf = -1;
+ tas_priv->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ } else
+ tas_priv->tasdevice[i].is_loading = false;
+ tas_priv->tasdevice[i].is_loaderr = false;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true)
+ continue;
+ if (tas_priv->tasdevice[i].is_loaderr == false &&
+ tas_priv->tasdevice[i].is_loading == true)
+ tas_priv->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+
+ for (i = 0, status = 0; i < tas_priv->ndev; i++) {
+ if (cfg_no >= 0
+ && tas_priv->tasdevice[i].cur_conf != cfg_no
+ && (cfg_info[rca_conf_no]->active_dev & (1 << i))
+ && (tas_priv->tasdevice[i].is_loaderr == false)) {
+ status++;
+ tas_priv->tasdevice[i].is_loading = true;
+ } else
+ tas_priv->tasdevice[i].is_loading = false;
+ }
+
+ if (status) {
+ conf = &(tas_fmw->configs[cfg_no]);
+ status = 0;
+ tasdevice_load_data(tas_priv, &(conf->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true) {
+ status |= BIT(i + 4);
+ continue;
+ }
+
+ if (tas_priv->tasdevice[i].is_loaderr == false &&
+ tas_priv->tasdevice[i].is_loading == true) {
+ tasdev_load_calibrated_data(tas_priv, i);
+ tas_priv->tasdevice[i].cur_conf = cfg_no;
+ }
+ }
+ } else {
+ dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
+ __func__, cfg_no);
+ }
+
+ status |= cfg_info[rca_conf_no]->active_dev;
+
+out:
+ return prog_status;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg, "SND_SOC_TAS2781_FMWLIB");
+
+int tasdevice_prmg_load(void *context, int prm_no)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ int prog_status = 0;
+ int i;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ goto out;
+ }
+
+ for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) {
+ if (prm_no >= 0 && tas_priv->tasdevice[i].cur_prog != prm_no) {
+ tas_priv->tasdevice[i].cur_conf = -1;
+ tas_priv->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_priv->tasdevice[i].is_loaderr == false
+ && tas_priv->tasdevice[i].is_loading == true)
+ tas_priv->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+
+out:
+ return prog_status;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB");
+
+void tasdevice_tuning_switch(void *context, int state)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ int profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+
+ /*
+ * Only RCA-based Playback can still work with no dsp program running
+ * inside the chip.
+ */
+ switch (tas_priv->fw_state) {
+ case TASDEVICE_RCA_FW_OK:
+ case TASDEVICE_DSP_FW_ALL_OK:
+ break;
+ default:
+ return;
+ }
+
+ if (state == 0) {
+ if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) {
+ /* dsp mode or tuning mode */
+ profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+ tasdevice_select_tuningprm_cfg(tas_priv,
+ tas_priv->cur_prog, tas_priv->cur_conf,
+ profile_cfg_id);
+ }
+
+ tasdevice_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+ } else {
+ tasdevice_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, "SND_SOC_TAS2781_FMWLIB");
+
+MODULE_DESCRIPTION("Texas Firmware Support");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000000..d1c76ab0144d
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,2132 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2563/TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// TAS2563/TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+#include <sound/tlv.h>
+#include <sound/tas2x20-tlv.h>
+#include <sound/tas2563-tlv.h>
+#include <sound/tas2781-tlv.h>
+#include <sound/tas5825-tlv.h>
+#include <linux/unaligned.h>
+
+#define X2563_CL_STT_VAL(xreg, xval) \
+{ .reg = xreg, \
+ .val = { xval }, \
+ .val_len = 1, }
+
+#define X2563_CL_STT_4BYTS(xreg, byte0, byte1, byte2, byte3) \
+{ .reg = xreg, \
+ .val = { byte0, byte1, byte2, byte3 }, \
+ .val_len = 4, }
+
+static const struct bulk_reg_val tas2563_cali_start_reg[] = {
+ X2563_CL_STT_VAL(TAS2563_IDLE, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_ENFF_REG, 0x40, 0x00, 0x00, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_DISTCK_REG, 0x40, 0x00, 0x00, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_TE_SCTHR_REG, 0x7f, 0xff, 0xff, 0xff),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_PLT_FLAG_REG, 0x40, 0x00, 0x00, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_SINEGAIN_REG, 0x0a, 0x3d, 0x70, 0xa4),
+ X2563_CL_STT_4BYTS(TAS2563_TE_TA1_REG, 0x00, 0x36, 0x91, 0x5e),
+ X2563_CL_STT_4BYTS(TAS2563_TE_TA1_AT_REG, 0x00, 0x36, 0x91, 0x5e),
+ X2563_CL_STT_4BYTS(TAS2563_TE_TA2_REG, 0x00, 0x06, 0xd3, 0x72),
+ X2563_CL_STT_4BYTS(TAS2563_TE_AT_REG, 0x00, 0x36, 0x91, 0x5e),
+ X2563_CL_STT_4BYTS(TAS2563_TE_DT_REG, 0x00, 0x36, 0x91, 0x5e),
+};
+
+#define X2781_CL_STT_VAL(xreg, xval, xlocked) \
+{ .reg = xreg, \
+ .val = { xval }, \
+ .val_len = 1, \
+ .is_locked = xlocked, }
+
+#define X2781_CL_STT_4BYTS_UNLOCKED(xreg, byte0, byte1, byte2, byte3) \
+{ .reg = xreg, \
+ .val = { byte0, byte1, byte2, byte3 }, \
+ .val_len = 4, \
+ .is_locked = false, }
+
+#define X2781_CL_STT_LEN_UNLOCKED(xreg) \
+{ .reg = xreg, \
+ .val_len = 4, \
+ .is_locked = false, }
+
+static const struct bulk_reg_val tas2781_cali_start_reg[] = {
+ X2781_CL_STT_VAL(TAS2781_PRM_INT_MASK_REG, 0xfe, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_CLK_CFG_REG, 0xdd, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_RSVD_REG, 0x20, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_TEST_57_REG, 0x14, true),
+ X2781_CL_STT_VAL(TAS2781_PRM_TEST_62_REG, 0x45, true),
+ X2781_CL_STT_VAL(TAS2781_PRM_PVDD_UVLO_REG, 0x03, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_CHNL_0_REG, 0xa8, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_NG_CFG0_REG, 0xb9, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_IDLE_CH_DET_REG, 0x92, false),
+ /*
+ * This register is pilot tone threshold, different with the
+ * calibration tool version, it will be updated in
+ * tas2781_calib_start_put(), set to 1mA.
+ */
+ X2781_CL_STT_4BYTS_UNLOCKED(0, 0x00, 0x00, 0x00, 0x56),
+ X2781_CL_STT_4BYTS_UNLOCKED(TAS2781_PRM_PLT_FLAG_REG,
+ 0x40, 0x00, 0x00, 0x00),
+ X2781_CL_STT_LEN_UNLOCKED(TAS2781_PRM_SINEGAIN_REG),
+ X2781_CL_STT_LEN_UNLOCKED(TAS2781_PRM_SINEGAIN2_REG),
+};
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2020", TAS2020 },
+ { "tas2118", TAS2118 },
+ { "tas2120", TAS2120 },
+ { "tas2320", TAS2320 },
+ { "tas2563", TAS2563 },
+ { "tas2568", TAS2568 },
+ { "tas2570", TAS2570 },
+ { "tas2572", TAS2572 },
+ { "tas2574", TAS2574 },
+ { "tas2781", TAS2781 },
+ { "tas5802", TAS5802 },
+ { "tas5806m", TAS5806M },
+ { "tas5806md", TAS5806MD },
+ { "tas5815", TAS5815 },
+ { "tas5822", TAS5822 },
+ { "tas5825", TAS5825 },
+ { "tas5827", TAS5827 },
+ { "tas5828", TAS5828 },
+ { "tas5830", TAS5830 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2020" },
+ { .compatible = "ti,tas2118" },
+ { .compatible = "ti,tas2120" },
+ { .compatible = "ti,tas2320" },
+ { .compatible = "ti,tas2563" },
+ { .compatible = "ti,tas2568" },
+ { .compatible = "ti,tas2570" },
+ { .compatible = "ti,tas2572" },
+ { .compatible = "ti,tas2574" },
+ { .compatible = "ti,tas2781" },
+ { .compatible = "ti,tas5802" },
+ { .compatible = "ti,tas5806m" },
+ { .compatible = "ti,tas5806md" },
+ { .compatible = "ti,tas5815" },
+ { .compatible = "ti,tas5822" },
+ { .compatible = "ti,tas5825" },
+ { .compatible = "ti,tas5827" },
+ { .compatible = "ti,tas5828" },
+ { .compatible = "ti,tas5830" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tasdevice_of_match);
+#endif
+
+/**
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tasdev_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ tas_priv->force_fwload_status ? "ON" : "OFF");
+
+ return 0;
+}
+
+static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(component);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ if (tas_priv->force_fwload_status == val)
+ change = false;
+ else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ tas_priv->force_fwload_status ? "ON" : "OFF");
+
+ return change;
+}
+
+static int tasdev_cali_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ struct calidata *cali_data = &priv->cali_data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned char *data = cali_data->data;
+ unsigned int i = 0;
+ unsigned int j, k;
+ int rc;
+
+ guard(mutex)(&priv->codec_lock);
+ if (!priv->is_user_space_calidata)
+ return -1;
+
+ if (!p->r0_reg)
+ return -1;
+
+ dst[i++] = bytes_ext->max;
+ dst[i++] = 'r';
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->r0_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->r0_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->r0_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->r0_low_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->r0_low_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->r0_low_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->invr0_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->invr0_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->invr0_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->pow_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->pow_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->pow_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->tlimit_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->tlimit_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->tlimit_reg);
+
+ for (j = 0, k = 0; j < priv->ndev; j++) {
+ if (j == data[k]) {
+ dst[i++] = j;
+ k++;
+ } else {
+ dev_err(priv->dev, "chn %d device %u not match\n",
+ j, data[k]);
+ k += 21;
+ continue;
+ }
+ rc = tasdevice_dev_bulk_read(priv, j, p->r0_reg, &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_reg bulk_rd err = %d\n",
+ j, rc);
+ i += 20;
+ k += 20;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d r0_data is not same\n", j);
+ k += 4;
+ i += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->r0_low_reg,
+ &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_low bulk_rd err = %d\n",
+ j, rc);
+ i += 16;
+ k += 16;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d r0_low is not same\n", j);
+ i += 4;
+ k += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->invr0_reg,
+ &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d invr0 bulk_rd err = %d\n",
+ j, rc);
+ i += 12;
+ k += 12;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d invr0 is not same\n", j);
+ i += 4;
+ k += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->pow_reg, &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d pow_reg bulk_rd err = %d\n",
+ j, rc);
+ i += 8;
+ k += 8;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d pow_reg is not same\n", j);
+ i += 4;
+ k += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->tlimit_reg,
+ &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d tlimit bulk_rd err = %d\n",
+ j, rc);
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d tlimit is not same\n", j);
+ i += 4;
+ k += 4;
+ }
+ return 0;
+}
+
+static int calib_data_get(struct tasdevice_priv *tas_priv, int reg,
+ unsigned char *dst)
+{
+ struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ int rc = -1;
+ int i;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (clt->addr == tasdev[i].dev_addr) {
+ /* First byte is the device index. */
+ dst[0] = i;
+ rc = tasdevice_dev_bulk_read(tas_priv, i, reg, &dst[1],
+ 4);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int partial_cali_data_update(int *reg, int j)
+{
+ switch (tas2781_cali_start_reg[j].reg) {
+ case 0:
+ return reg[0];
+ case TAS2781_PRM_PLT_FLAG_REG:
+ return reg[1];
+ case TAS2781_PRM_SINEGAIN_REG:
+ return reg[2];
+ case TAS2781_PRM_SINEGAIN2_REG:
+ return reg[3];
+ default:
+ return 0;
+ }
+}
+
+static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i,
+ int *reg, unsigned char *dat)
+{
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+ struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp;
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ unsigned char val[4];
+ int j, r;
+
+ if (p == NULL)
+ return;
+
+ /* Store the current setting from the chip */
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1) {
+ if (p[j].is_locked)
+ tasdevice_dev_write(tas_priv, i,
+ TAS2781_TEST_UNLOCK_REG,
+ TAS2781_TEST_PAGE_UNLOCK);
+ tasdevice_dev_read(tas_priv, i, p[j].reg,
+ (int *)&p[j].val[0]);
+ } else {
+ if (!tas_priv->dspbin_typ) {
+ r = partial_cali_data_update(reg, j);
+ if (r)
+ p[j].reg = r;
+ }
+
+ if (p[j].reg)
+ tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
+ p[j].val, 4);
+ }
+ }
+
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA)
+ tasdevice_dev_bulk_read(tas_priv, i, t->reg, t->val, 4);
+
+ /* Update the setting for calibration */
+ for (j = 0; j < sum - 4; j++) {
+ if (p[j].val_len == 1) {
+ if (p[j].is_locked)
+ tasdevice_dev_write(tas_priv, i,
+ TAS2781_TEST_UNLOCK_REG,
+ TAS2781_TEST_PAGE_UNLOCK);
+ tasdevice_dev_write(tas_priv, i, p[j].reg,
+ tas2781_cali_start_reg[j].val[0]);
+ }
+ }
+
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA) {
+ val[0] = 0x00;
+ val[1] = 0x00;
+ val[2] = 0x21;
+ val[3] = 0x8e;
+ } else {
+ val[0] = tas2781_cali_start_reg[j].val[0];
+ val[1] = tas2781_cali_start_reg[j].val[1];
+ val[2] = tas2781_cali_start_reg[j].val[2];
+ val[3] = tas2781_cali_start_reg[j].val[3];
+ }
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, val, 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg,
+ (unsigned char *)tas2781_cali_start_reg[j + 1].val, 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 2].reg, &dat[1], 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 3].reg, &dat[5], 4);
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA) {
+ val[0] = 0x00;
+ val[1] = 0x00;
+ val[2] = 0x2a;
+ val[3] = 0x0b;
+
+ tasdevice_dev_bulk_read(tas_priv, i, t->reg, val, 4);
+ }
+}
+
+static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dat = ucontrol->value.bytes.data;
+ int i, reg[4];
+ int j = 0;
+
+ guard(mutex)(&priv->codec_lock);
+ if (priv->chip_id != TAS2781 || bytes_ext->max != dat[0] ||
+ dat[1] != 'r') {
+ dev_err(priv->dev, "%s: package fmt or chipid incorrect\n",
+ __func__);
+ return 0;
+ }
+ j += 2;
+ /* refresh pilot tone and SineGain register */
+ for (i = 0; i < ARRAY_SIZE(reg); i++) {
+ reg[i] = TASDEVICE_REG(dat[j], dat[j + 1], dat[j + 2]);
+ j += 3;
+ }
+
+ for (i = 0; i < priv->ndev; i++) {
+ int k = i * 9 + j;
+
+ if (dat[k] != i) {
+ dev_err(priv->dev, "%s:no cal-setting for dev %d\n",
+ __func__, i);
+ continue;
+ }
+ sngl_calib_start(priv, i, reg, dat + k);
+ }
+ return 1;
+}
+
+static void tas2781_calib_stop_put(struct tasdevice_priv *priv)
+{
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ int i, j;
+
+ for (i = 0; i < priv->ndev; i++) {
+ struct tasdevice *tasdev = priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+ struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp;
+
+ if (p == NULL)
+ continue;
+
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1) {
+ if (p[j].is_locked)
+ tasdevice_dev_write(priv, i,
+ TAS2781_TEST_UNLOCK_REG,
+ TAS2781_TEST_PAGE_UNLOCK);
+ tasdevice_dev_write(priv, i, p[j].reg,
+ p[j].val[0]);
+ } else {
+ if (!p[j].reg)
+ continue;
+ tasdevice_dev_bulk_write(priv, i, p[j].reg,
+ p[j].val, 4);
+ }
+ }
+
+ if (priv->dspbin_typ == TASDEV_ALPHA)
+ tasdevice_dev_bulk_write(priv, i, t->reg, t->val, 4);
+ }
+}
+
+static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct bulk_reg_val *q = (struct bulk_reg_val *)tas2563_cali_start_reg;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+ int i, j;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->chip_id != TAS2563)
+ return -1;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+
+ if (p == NULL)
+ continue;
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1)
+ tasdevice_dev_read(tas_priv,
+ i, p[j].reg,
+ (unsigned int *)&p[j].val[0]);
+ else
+ tasdevice_dev_bulk_read(tas_priv,
+ i, p[j].reg, p[j].val, 4);
+ }
+
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1)
+ tasdevice_dev_write(tas_priv, i, p[j].reg,
+ q[j].val[0]);
+ else
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+ q[j].val, 4);
+ }
+ }
+
+ return 1;
+}
+
+static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
+{
+ const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+ int i, j;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+
+ if (p == NULL)
+ continue;
+
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1)
+ tasdevice_dev_write(tas_priv, i, p[j].reg,
+ p[j].val[0]);
+ else
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+ p[j].val, 4);
+ }
+ }
+}
+
+static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+
+ guard(mutex)(&priv->codec_lock);
+ if (priv->chip_id == TAS2563)
+ tas2563_calib_stop_put(priv);
+ else
+ tas2781_calib_stop_put(priv);
+
+ return 1;
+}
+
+static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ struct calidata *cali_data = &priv->cali_data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ unsigned char *src = ucontrol->value.bytes.data;
+ unsigned char *dst = cali_data->data;
+ int i = 0;
+ int j;
+
+ guard(mutex)(&priv->codec_lock);
+ if (src[0] != bytes_ext->max || src[1] != 'r') {
+ dev_err(priv->dev, "%s: pkg fmt invalid\n", __func__);
+ return 0;
+ }
+ for (j = 0; j < priv->ndev; j++) {
+ if (src[17 + j * 21] != j) {
+ dev_err(priv->dev, "%s: pkg fmt invalid\n", __func__);
+ return 0;
+ }
+ }
+ i += 2;
+ priv->is_user_space_calidata = true;
+
+ if (priv->dspbin_typ == TASDEV_BASIC) {
+ p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->r0_low_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->invr0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->pow_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->tlimit_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ } else {
+ i += 15;
+ }
+
+ memcpy(dst, &src[i], cali_data->total_sz);
+ return 1;
+}
+
+static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ int i, val, rc = -1;
+
+ dst[0] = bytes_ext->max;
+ guard(mutex)(&tas_priv->codec_lock);
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (clt->addr == tasdev[i].dev_addr) {
+ /* First byte is the device index. */
+ dst[1] = i;
+ rc = tasdevice_dev_read(tas_priv, i,
+ TAS2781_RUNTIME_LATCH_RE_REG, &val);
+ if (rc < 0)
+ dev_err(tas_priv->dev, "%s, get value error\n",
+ __func__);
+ else
+ dst[2] = val;
+
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int tasdev_tf_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TAS2781_RUNTIME_RE_REG_TF;
+
+ if (tas_priv->chip_id == TAS2781) {
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+
+ reg = TAS2781_RUNTIME_RE_REG_TF;
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->tf_reg[0], p->tf_reg[1],
+ p->tf_reg[2]);
+ } else {
+ reg = TAS2563_RUNTIME_RE_REG_TF;
+ }
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_re_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TAS2781_RUNTIME_RE_REG;
+
+ if (tas_priv->chip_id == TAS2781) {
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->r0_reg[0], p->r0_reg[1],
+ p->r0_reg[2]);
+ } else {
+ reg = TAS2563_RUNTIME_RE_REG;
+ }
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_r0_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct calidata *cali_data = &tas_priv->cali_data;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ if (tas_priv->chip_id == TAS2563)
+ reg = TAS2563_PRM_R0_REG;
+ else if (cali_data->cali_reg_array.r0_reg)
+ reg = cali_data->cali_reg_array.r0_reg;
+ else
+ return -1;
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_XMA1_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TASDEVICE_XM_A1_REG;
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->a1_reg[0], p->a1_reg[1], p->a1_reg[2]);
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_XMA2_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TASDEVICE_XM_A2_REG;
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->a2_reg[0], p->a2_reg[1], p->a2_reg[2]);
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_nop_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int tasdevice_digital_gain_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ unsigned int l = 0, r = mc->max;
+ unsigned int target, ar_mid, mid, ar_l, ar_r;
+ unsigned int reg = mc->reg;
+ unsigned char data[4];
+ int ret;
+
+ mutex_lock(&tas_dev->codec_lock);
+ /* Read the primary device */
+ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ target = get_unaligned_be32(&data[0]);
+
+ while (r > 1 + l) {
+ mid = (l + r) / 2;
+ ar_mid = get_unaligned_be32(tas_dev->dvc_tlv_table[mid]);
+ if (target < ar_mid)
+ r = mid;
+ else
+ l = mid;
+ }
+
+ ar_l = get_unaligned_be32(tas_dev->dvc_tlv_table[l]);
+ ar_r = get_unaligned_be32(tas_dev->dvc_tlv_table[r]);
+
+ /* find out the member same as or closer to the current volume */
+ ucontrol->value.integer.value[0] =
+ abs(target - ar_l) <= abs(target - ar_r) ? l : r;
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+static int tasdevice_digital_gain_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int vol = ucontrol->value.integer.value[0];
+ int status = 0, max = mc->max, rc = 1;
+ int i, ret;
+ unsigned int reg = mc->reg;
+ unsigned int volrd, volwr;
+ unsigned char data[4];
+
+ vol = clamp(vol, 0, max);
+ mutex_lock(&tas_dev->codec_lock);
+ /* Read the primary device */
+ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ rc = -1;
+ goto out;
+ }
+
+ volrd = get_unaligned_be32(&data[0]);
+ volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]);
+
+ if (volrd == volwr) {
+ rc = 0;
+ goto out;
+ }
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_bulk_write(tas_dev, i, reg,
+ (unsigned char *)tas_dev->dvc_tlv_table[vol], 4);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in dev %d\n",
+ __func__, i);
+ status |= BIT(i);
+ }
+ }
+
+ if (status)
+ rc = -1;
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ return rc;
+}
+
+static const struct snd_kcontrol_new tasdevice_cali_controls[] = {
+ SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tasdev_calib_stop_put),
+ SND_SOC_BYTES_EXT("Amp TF Data", 6, tasdev_tf_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp RE Data", 6, tasdev_re_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp R0 Data", 6, tasdev_r0_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp XMA1 Data", 6, tasdev_XMA1_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp XMA2 Data", 6, tasdev_XMA2_data_get, NULL),
+};
+
+static const struct snd_kcontrol_new tas2x20_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2X20_AMP_LEVEL,
+ 0, 0, 42, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2x20_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2X20_DVC_LEVEL,
+ 0, 0, ARRAY_SIZE(tas2x20_dvc_table) - 1, 0,
+ tasdevice_digital_gain_get, tasdevice_digital_gain_put,
+ tas2x20_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2781_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, tas2781_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas5825_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS5825_AMP_LEVEL,
+ 0, 0, 31, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS5825_DVC_LEVEL,
+ 0, 0, 254, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2781_cali_controls[] = {
+ SND_SOC_BYTES_EXT("Amp Latch Data", 3, tas2781_latch_reg_get, NULL),
+};
+
+static const struct snd_kcontrol_new tas2563_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2563_DVC_LVL, 0,
+ 0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
+ tasdevice_digital_gain_get, tasdevice_digital_gain_put,
+ tas2563_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2563_cali_controls[] = {
+ SOC_SINGLE_EXT("Calibration Start", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tas2563_calib_start_put),
+};
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+
+ if (tas_priv->rcabin.profile_cfg_id !=
+ ucontrol->value.integer.value[0]) {
+ tas_priv->rcabin.profile_cfg_id =
+ ucontrol->value.integer.value[0];
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->ndev - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_chip_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = TAS2020;
+ uinfo->value.integer.max = TAS_OTHERS;
+
+ return 0;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)tas_fw->nr_programs;
+
+ return 0;
+}
+
+static int tasdevice_info_configurations(
+ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)tas_fw->nr_configurations - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ return 0;
+}
+
+static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->chip_id;
+
+ return 0;
+}
+
+static int tasdevice_create_control(struct tasdevice_priv *tas_priv)
+{
+ struct snd_kcontrol_new *prof_ctrls;
+ int nr_controls = 1;
+ int mix_index = 0;
+ int ret;
+ char *name;
+
+ prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls,
+ sizeof(prof_ctrls[0]), GFP_KERNEL);
+ if (!prof_ctrls) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kstrdup(tas_priv->dev, "Speaker Profile Id", GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ prof_ctrls[mix_index].name = name;
+ prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ prof_ctrls[mix_index].info = tasdevice_info_profile;
+ prof_ctrls[mix_index].get = tasdevice_get_profile_id;
+ prof_ctrls[mix_index].put = tasdevice_set_profile_id;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_priv->codec,
+ prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index);
+
+out:
+ return ret;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ unsigned int nr_program = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (tas_priv->cur_prog != nr_program) {
+ tas_priv->cur_prog = nr_program;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_configuration_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ return 0;
+}
+
+static int tasdevice_configuration_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ unsigned int nr_configuration = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (tas_priv->cur_conf != nr_configuration) {
+ tas_priv->cur_conf = nr_configuration;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_active_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ int i;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (clt->addr == tasdev[i].dev_addr) {
+ ucontrol->value.integer.value[0] = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int tasdevice_active_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ int dev_id = ucontrol->value.integer.value[0];
+ int max = tas_priv->ndev - 1;
+
+ dev_id = clamp(dev_id, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdev_chn_switch(tas_priv, dev_id);
+}
+
+static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
+{
+ struct snd_kcontrol_new *dsp_ctrls;
+ char *active_dev_num, *chip_id, *fw_load;
+ char *conf_name, *prog_name;
+ int nr_controls = 5;
+ int mix_index = 0;
+
+ /* Alloc kcontrol via devm_kzalloc, which don't manually
+ * free the kcontrol
+ */
+ dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls,
+ sizeof(dsp_ctrls[0]), GFP_KERNEL);
+ if (!dsp_ctrls)
+ return -ENOMEM;
+
+ /* Create mixer items for selecting the active Program and Config */
+ prog_name = devm_kstrdup(tas_priv->dev, "Speaker Program Id",
+ GFP_KERNEL);
+ if (!prog_name)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = prog_name;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_programs;
+ dsp_ctrls[mix_index].get = tasdevice_program_get;
+ dsp_ctrls[mix_index].put = tasdevice_program_put;
+ mix_index++;
+
+ conf_name = devm_kstrdup(tas_priv->dev, "Speaker Config Id",
+ GFP_KERNEL);
+ if (!conf_name)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = conf_name;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_configurations;
+ dsp_ctrls[mix_index].get = tasdevice_configuration_get;
+ dsp_ctrls[mix_index].put = tasdevice_configuration_put;
+ mix_index++;
+
+ active_dev_num = devm_kstrdup(tas_priv->dev, "Activate Tasdevice Num",
+ GFP_KERNEL);
+ if (!active_dev_num)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = active_dev_num;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_active_num;
+ dsp_ctrls[mix_index].get = tasdevice_active_num_get;
+ dsp_ctrls[mix_index].put = tasdevice_active_num_put;
+ mix_index++;
+
+ chip_id = devm_kstrdup(tas_priv->dev, "Tasdevice Chip Id", GFP_KERNEL);
+ if (!chip_id)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = chip_id;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_chip_id;
+ dsp_ctrls[mix_index].get = tasdevice_get_chip_id;
+ mix_index++;
+
+ fw_load = devm_kstrdup(tas_priv->dev, "Speaker Force Firmware Load",
+ GFP_KERNEL);
+ if (!fw_load)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = fw_load;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = snd_soc_info_bool_ext;
+ dsp_ctrls[mix_index].put = tasdev_force_fwload_put;
+ dsp_ctrls[mix_index].get = tasdev_force_fwload_get;
+ dsp_ctrls[mix_index].private_value = 0UL;
+ mix_index++;
+
+ return snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+}
+
+static void cali_reg_update(struct bulk_reg_val *p,
+ struct fct_param_address *t)
+{
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ int reg, j;
+
+ for (j = 0; j < sum; j++) {
+ switch (tas2781_cali_start_reg[j].reg) {
+ case 0:
+ reg = TASDEVICE_REG(t->thr[0], t->thr[1], t->thr[2]);
+ break;
+ case TAS2781_PRM_PLT_FLAG_REG:
+ reg = TASDEVICE_REG(t->plt_flg[0], t->plt_flg[1],
+ t->plt_flg[2]);
+ break;
+ case TAS2781_PRM_SINEGAIN_REG:
+ reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+ t->sin_gn[2]);
+ break;
+ case TAS2781_PRM_SINEGAIN2_REG:
+ reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+ t->sin_gn[2]);
+ break;
+ default:
+ reg = 0;
+ break;
+ }
+ if (reg)
+ p[j].reg = reg;
+ }
+}
+
+static void alpa_cali_update(struct bulk_reg_val *p,
+ struct fct_param_address *t)
+{
+ p->is_locked = false;
+ p->reg = TASDEVICE_REG(t->thr2[0], t->thr2[1], t->thr2[2]);
+ p->val_len = 4;
+}
+
+static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
+{
+ struct calidata *cali_data = &priv->cali_data;
+ struct tasdevice *tasdev = priv->tasdevice;
+ struct tasdevice_fw *fmw = priv->fmw;
+ struct soc_bytes_ext *ext_cali_data;
+ struct snd_kcontrol_new *cali_ctrls;
+ unsigned int nctrls;
+ char *cali_name;
+ int rc, i;
+
+ rc = snd_soc_add_component_controls(priv->codec,
+ tasdevice_cali_controls, ARRAY_SIZE(tasdevice_cali_controls));
+ if (rc < 0) {
+ dev_err(priv->dev, "%s: Add cali controls err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
+ if (priv->chip_id == TAS2781) {
+ struct fct_param_address *t = &(fmw->fct_par_addr);
+
+ cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls;
+ nctrls = ARRAY_SIZE(tas2781_cali_controls);
+ for (i = 0; i < priv->ndev; i++) {
+ struct bulk_reg_val *p;
+
+ p = tasdev[i].cali_data_backup =
+ kmemdup(tas2781_cali_start_reg,
+ sizeof(tas2781_cali_start_reg), GFP_KERNEL);
+ if (!tasdev[i].cali_data_backup)
+ return -ENOMEM;
+ if (priv->dspbin_typ) {
+ cali_reg_update(p, t);
+ if (priv->dspbin_typ == TASDEV_ALPHA) {
+ p = &tasdev[i].alp_cali_bckp;
+ alpa_cali_update(p, t);
+ }
+ }
+ }
+ } else {
+ cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
+ nctrls = ARRAY_SIZE(tas2563_cali_controls);
+ for (i = 0; i < priv->ndev; i++) {
+ tasdev[i].cali_data_backup =
+ kmemdup(tas2563_cali_start_reg,
+ sizeof(tas2563_cali_start_reg), GFP_KERNEL);
+ if (!tasdev[i].cali_data_backup)
+ return -ENOMEM;
+ }
+ }
+
+ rc = snd_soc_add_component_controls(priv->codec, cali_ctrls, nctrls);
+ if (rc < 0) {
+ dev_err(priv->dev, "%s: Add chip cali ctrls err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
+ /* index for cali_ctrls */
+ i = 0;
+ if (priv->chip_id == TAS2781)
+ nctrls = 2;
+ else
+ nctrls = 1;
+
+ /*
+ * Alloc kcontrol via devm_kzalloc(), which don't manually
+ * free the kcontrol.
+ */
+ cali_ctrls = devm_kcalloc(priv->dev, nctrls,
+ sizeof(cali_ctrls[0]), GFP_KERNEL);
+ if (!cali_ctrls)
+ return -ENOMEM;
+
+ ext_cali_data = devm_kzalloc(priv->dev, sizeof(*ext_cali_data),
+ GFP_KERNEL);
+ if (!ext_cali_data)
+ return -ENOMEM;
+
+ cali_name = devm_kstrdup(priv->dev, "Speaker Calibrated Data",
+ GFP_KERNEL);
+ if (!cali_name)
+ return -ENOMEM;
+ /* the number of calibrated data per tas2563/tas2781 */
+ cali_data->cali_dat_sz_per_dev = 20;
+ /*
+ * Data structure for tas2563/tas2781 calibrated data:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register array for calibrated data (15 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Calibrated data for Device #i (20 bytes)
+ * }
+ */
+ ext_cali_data->max = priv->ndev *
+ (cali_data->cali_dat_sz_per_dev + 1) + 1 + 15 + 1;
+ priv->cali_data.total_sz = priv->ndev *
+ (cali_data->cali_dat_sz_per_dev + 1);
+ cali_ctrls[i].name = cali_name;
+ cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrls[i].info = snd_soc_bytes_info_ext;
+ cali_ctrls[i].get = tasdev_cali_data_get;
+ cali_ctrls[i].put = tasdev_cali_data_put;
+ cali_ctrls[i].private_value = (unsigned long)ext_cali_data;
+ i++;
+
+ cali_data->data = devm_kzalloc(priv->dev, cali_data->total_sz,
+ GFP_KERNEL);
+ if (!cali_data->data)
+ return -ENOMEM;
+
+ if (priv->chip_id == TAS2781) {
+ struct soc_bytes_ext *ext_cali_start;
+ char *cali_start_name;
+
+ ext_cali_start = devm_kzalloc(priv->dev,
+ sizeof(*ext_cali_start), GFP_KERNEL);
+ if (!ext_cali_start)
+ return -ENOMEM;
+
+ cali_start_name = devm_kstrdup(priv->dev,
+ "Calibration Start", GFP_KERNEL);
+ if (!cali_start_name)
+ return -ENOMEM;
+ /*
+ * package structure for tas2781 ftc start:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register for pilot threshold, pilot tone
+ * and sine gain (12 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Sine gain for Device #i (8 bytes)
+ * }
+ */
+ ext_cali_start->max = 14 + priv->ndev * 9;
+ cali_ctrls[i].name = cali_start_name;
+ cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrls[i].info = snd_soc_bytes_info_ext;
+ cali_ctrls[i].put = tas2781_calib_start_put;
+ cali_ctrls[i].get = tasdev_nop_get;
+ cali_ctrls[i].private_value = (unsigned long)ext_cali_start;
+ i++;
+ }
+
+ return snd_soc_add_component_controls(priv->codec, cali_ctrls,
+ nctrls < i ? nctrls : i);
+}
+
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+/*
+ * This debugfs node is a bridge to the acoustic tuning application
+ * tool which can tune the chips' acoustic effect.
+ *
+ * package structure for PPC3 communications:
+ * Pkg len (1 byte)
+ * Pkg id (1 byte, 'r' or 'w')
+ * Dev id (1 byte, i2c address)
+ * Book id (1 byte)
+ * Page id (1 byte)
+ * Reg id (1 byte)
+ * switch (pkg id) {
+ * case 'w':
+ * 1 byte, length of data to read
+ * case 'r':
+ * data payload (1~128 bytes)
+ * }
+ */
+static ssize_t acoustic_ctl_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_component *comp = file->private_data;
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct acoustic_data *p = &tas_priv->acou_data;
+ int ret = -1;
+
+ if (p->id == 'r' && p->len == count && count <= sizeof(*p))
+ ret = simple_read_from_buffer(to, count, ppos, p, p->len);
+ else
+ dev_err(tas_priv->dev, "Not ready for get.\n");
+ return ret;
+}
+
+static ssize_t acoustic_ctl_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_soc_component *comp = file->private_data;
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct acoustic_data *p = &priv->acou_data;
+ unsigned int max_pkg_len = sizeof(*p);
+ unsigned char *src;
+ int j, len, reg, val;
+ unsigned short chn;
+ int ret = -1;
+
+ if (count > sizeof(*p)) {
+ dev_err(priv->dev, "count(%u) is larger than max(%u).\n",
+ (unsigned int)count, max_pkg_len);
+ return ret;
+ }
+
+ src = memdup_user(from, count);
+ if (IS_ERR(src))
+ return PTR_ERR(src);
+
+ if (src[0] > max_pkg_len && src[0] != count) {
+ dev_err(priv->dev, "pkg(%u), max(%u), count(%u) mismatch.\n",
+ src[0], max_pkg_len, (unsigned int)count);
+ ret = 0;
+ goto exit;
+ }
+
+ switch (src[1]) {
+ case 'r':
+ /* length of data to read */
+ len = src[6];
+ break;
+ case 'w':
+ /* Skip 6 bytes for package type and register address */
+ len = src[0] - 6;
+ break;
+ default:
+ dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]);
+ ret = 0;
+ goto exit;
+ }
+
+ if (len < 1) {
+ dev_err(priv->dev, "pkg fmt invalid %02x.\n", len);
+ ret = 0;
+ goto exit;
+ }
+
+ for (j = 0; j < priv->ndev; j++)
+ if (src[2] == priv->tasdevice[j].dev_addr) {
+ chn = j;
+ break;
+ }
+ if (j >= priv->ndev) {
+ dev_err(priv->dev, "no such device 0x%02x.\n", src[2]);
+ ret = 0;
+ goto exit;
+ }
+
+ reg = TASDEVICE_REG(src[3], src[4], src[5]);
+
+ guard(mutex)(&priv->codec_lock);
+
+ if (src[1] == 'w') {
+ if (len > 1)
+ ret = tasdevice_dev_bulk_write(priv, chn, reg,
+ &src[6], len);
+ else
+ ret = tasdevice_dev_write(priv, chn, reg, src[6]);
+ } else {
+ struct acoustic_data *p = &priv->acou_data;
+
+ memcpy(p, src, 6);
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_read(priv, chn, reg,
+ p->data, len);
+ } else {
+ ret = tasdevice_dev_read(priv, chn, reg, &val);
+ p->data[0] = val;
+ }
+ p->len = len + 6;
+ }
+
+ if (ret)
+ dev_err(priv->dev, "i2c communication error.\n");
+ else
+ ret = count;
+exit:
+ kfree(src);
+ return ret;
+}
+
+static const struct file_operations acoustic_ctl_fops = {
+ .open = simple_open,
+ .read = acoustic_ctl_read,
+ .write = acoustic_ctl_write,
+};
+#endif
+
+static void tasdevice_fw_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+ struct snd_soc_component *comp = tas_priv->codec;
+ struct dentry *debugfs_root = comp->debugfs_root;
+ char *acoustic_debugfs_node;
+#endif
+ int ret = 0;
+ int i;
+
+ mutex_lock(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret) {
+ tasdevice_config_info_remove(tas_priv);
+ goto out;
+ }
+ tasdevice_create_control(tas_priv);
+
+ tasdevice_dsp_remove(tas_priv);
+ tasdevice_calbin_remove(tas_priv);
+ /*
+ * The baseline is the RCA-only case, and then the code attempts to
+ * load DSP firmware but in case of failures just keep going, i.e.
+ * failing to load DSP firmware is NOT an error.
+ */
+ tas_priv->fw_state = TASDEVICE_RCA_FW_OK;
+ /* There is no DSP firmware required for TAS2118/2X20/257X. */
+ switch (tas_priv->chip_id) {
+ case TAS2020:
+ case TAS2118:
+ case TAS2120:
+ case TAS2320:
+ case TAS2568:
+ case TAS2570:
+ case TAS2572:
+ case TAS2574:
+ goto out;
+ }
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->coef_binaryname, 64, "%s-%s_coef.bin",
+ tas_priv->name_prefix, tas_priv->dev_name);
+ else
+ scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin",
+ tas_priv->dev_name);
+ ret = tasdevice_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ goto out;
+ }
+
+ /*
+ * If no dsp-related kcontrol created, the dsp resource will be freed.
+ */
+ ret = tasdevice_dsp_create_ctrls(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dsp controls error\n");
+ goto out;
+ }
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+
+ /* There is no calibration required for TAS58XX. */
+ if (tas_priv->chip_id < TAS5802) {
+ ret = tasdevice_create_cali_ctrls(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "cali controls error\n");
+ goto out;
+ }
+ /* If calibrated data occurs error, dsp will still works
+ * with default calibrated data inside algo.
+ */
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->cal_binaryname[i], 64,
+ "%s-%s_cal_0x%02x.bin",
+ tas_priv->name_prefix,
+ tas_priv->dev_name,
+ tas_priv->tasdevice[i].dev_addr);
+ else
+ scnprintf(tas_priv->cal_binaryname[i], 64,
+ "%s_cal_0x%02x.bin",
+ tas_priv->dev_name,
+ tas_priv->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_priv,
+ tas_priv->cal_binaryname[i], i);
+ if (ret != 0)
+ dev_err(tas_priv->dev,
+ "%s: load %s error, keep default.\n",
+ __func__, tas_priv->cal_binaryname[i]);
+ }
+ }
+
+ tasdevice_prmg_load(tas_priv, 0);
+ tas_priv->cur_prog = 0;
+
+ /* Init common setting for different audio profiles */
+ if (tas_priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv,
+ tas_priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+ if (tas_priv->name_prefix)
+ acoustic_debugfs_node = devm_kasprintf(tas_priv->dev,
+ GFP_KERNEL, "%s_acoustic_ctl", tas_priv->name_prefix);
+ else
+ acoustic_debugfs_node = devm_kstrdup(tas_priv->dev,
+ "acoustic_ctl", GFP_KERNEL);
+ debugfs_create_file(acoustic_debugfs_node, 0644, debugfs_root,
+ comp, &acoustic_ctl_fops);
+#endif
+out:
+ if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) {
+ switch (tas_priv->chip_id) {
+ case TAS2563:
+ case TAS2781:
+ case TAS5802:
+ case TAS5806M:
+ case TAS5806MD:
+ case TAS5815:
+ case TAS5822:
+ case TAS5825:
+ case TAS5827:
+ case TAS5828:
+ case TAS5830:
+ /* If DSP FW fail, DSP kcontrol won't be created. */
+ tasdevice_dsp_remove(tas_priv);
+ }
+ }
+ mutex_unlock(&tas_priv->codec_lock);
+ release_firmware(fmw);
+}
+
+static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ int state = 0;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_priv->codec_lock);
+ if (event == SND_SOC_DAPM_PRE_PMD)
+ state = 1;
+ tasdevice_tuning_switch(tas_priv, state);
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_priv->codec_lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0, tasdevice_dapm_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"SPK", NULL, "ASI"},
+ {"OUT", NULL, "SPK"},
+ {"ASI OUT", NULL, "DMIC"},
+};
+
+static int tasdevice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ switch (tas_priv->fw_state) {
+ case TASDEVICE_RCA_FW_OK:
+ case TASDEVICE_DSP_FW_ALL_OK:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tasdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int slot_width;
+ unsigned int fsrate;
+ int bclk_rate;
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ case 44100:
+ break;
+ default:
+ dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ return -EINVAL;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ case 20:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ return bclk_rate;
+ }
+
+ return 0;
+}
+
+static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(codec_dai);
+
+ tas_priv->sysclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tasdevice_dai_ops = {
+ .startup = tasdevice_startup,
+ .hw_params = tasdevice_hw_params,
+ .set_sysclk = tasdevice_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
+ {
+ .name = "tasdev_codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .ops = &tasdevice_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int tasdevice_codec_probe(struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct snd_kcontrol_new *p;
+ unsigned int size;
+ int rc;
+
+ switch (tas_priv->chip_id) {
+ case TAS2020:
+ case TAS2118:
+ case TAS2120:
+ case TAS2320:
+ case TAS2568:
+ case TAS2570:
+ case TAS2572:
+ case TAS2574:
+ p = (struct snd_kcontrol_new *)tas2x20_snd_controls;
+ size = ARRAY_SIZE(tas2x20_snd_controls);
+ tas_priv->dvc_tlv_table = tas2x20_dvc_table;
+ break;
+ case TAS2781:
+ p = (struct snd_kcontrol_new *)tas2781_snd_controls;
+ size = ARRAY_SIZE(tas2781_snd_controls);
+ break;
+ case TAS5802:
+ case TAS5806M:
+ case TAS5806MD:
+ case TAS5815:
+ case TAS5822:
+ case TAS5825:
+ case TAS5827:
+ case TAS5828:
+ case TAS5830:
+ p = (struct snd_kcontrol_new *)tas5825_snd_controls;
+ size = ARRAY_SIZE(tas5825_snd_controls);
+ break;
+ default:
+ p = (struct snd_kcontrol_new *)tas2563_snd_controls;
+ size = ARRAY_SIZE(tas2563_snd_controls);
+ tas_priv->dvc_tlv_table = tas2563_dvc_table;
+ break;
+ }
+
+ rc = snd_soc_add_component_controls(codec, p, size);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: Add control err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
+ tas_priv->name_prefix = codec->name_prefix;
+ return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready);
+}
+
+static void tasdevice_deinit(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ int i;
+
+ for (i = 0; i < tas_priv->ndev; i++)
+ kfree(tasdev[i].cali_data_backup);
+
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+ tasdevice_calbin_remove(tas_priv);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static void tasdevice_codec_remove(struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ tasdevice_deinit(tas_priv);
+}
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_tasdevice = {
+ .probe = tasdevice_codec_probe,
+ .remove = tasdevice_codec_remove,
+ .dapm_widgets = tasdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets),
+ .dapm_routes = tasdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS];
+ int ndev = 0;
+ int i, rc;
+
+ if (tas_priv->isacpi) {
+ ndev = device_property_read_u32_array(&client->dev,
+ "ti,audio-slots", NULL, 0);
+ if (ndev <= 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ } else {
+ ndev = (ndev < ARRAY_SIZE(dev_addrs))
+ ? ndev : ARRAY_SIZE(dev_addrs);
+ rc = device_property_read_u32_array(&client->dev,
+ "ti,audio-slots", dev_addrs, ndev);
+ if (rc != 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+ }
+
+ tas_priv->irq =
+ acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0);
+ } else if (IS_ENABLED(CONFIG_OF)) {
+ struct device_node *np = tas_priv->dev->of_node;
+ u64 addr;
+
+ for (i = 0; i < TASDEVICE_MAX_CHANNELS; i++) {
+ if (of_property_read_reg(np, i, &addr, NULL))
+ break;
+ dev_addrs[ndev++] = addr;
+ }
+
+ tas_priv->irq = of_irq_get(np, 0);
+ } else {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+ tas_priv->ndev = ndev;
+ for (i = 0; i < ndev; i++)
+ tas_priv->tasdevice[i].dev_addr = dev_addrs[i];
+
+ tas_priv->reset = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas_priv->reset))
+ dev_err(tas_priv->dev, "%s Can't get reset GPIO\n",
+ __func__);
+
+ strscpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name,
+ sizeof(tas_priv->dev_name));
+}
+
+static int tasdevice_i2c_probe(struct i2c_client *i2c)
+{
+ const struct acpi_device_id *acpi_id;
+ struct tasdevice_priv *tas_priv;
+ int ret;
+
+ tas_priv = tasdevice_kzalloc(i2c);
+ if (!tas_priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&i2c->dev, tas_priv);
+
+ if (ACPI_HANDLE(&i2c->dev)) {
+ acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
+ &i2c->dev);
+ if (!acpi_id) {
+ dev_err(&i2c->dev, "No driver data\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ tas_priv->chip_id = acpi_id->driver_data;
+ tas_priv->isacpi = true;
+ } else {
+ tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c);
+ tas_priv->isacpi = false;
+ }
+
+ tasdevice_parse_dt(tas_priv);
+
+ ret = tasdevice_init(tas_priv);
+ if (ret)
+ goto err;
+
+ tasdevice_reset(tas_priv);
+
+ ret = devm_snd_soc_register_component(tas_priv->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: codec register error:0x%08x\n",
+ __func__, ret);
+ goto err;
+ }
+err:
+ if (ret < 0)
+ tasdevice_remove(tas_priv);
+ return ret;
+}
+
+static void tasdevice_i2c_remove(struct i2c_client *client)
+{
+ struct tasdevice_priv *tas_priv = i2c_get_clientdata(client);
+
+ tasdevice_remove(tas_priv);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id tasdevice_acpi_match[] = {
+ { "TXNW2020", TAS2020 },
+ { "TXNW2118", TAS2118 },
+ { "TXNW2120", TAS2120 },
+ { "TXNW2320", TAS2320 },
+ { "TXNW2563", TAS2563 },
+ { "TXNW2568", TAS2568 },
+ { "TXNW2570", TAS2570 },
+ { "TXNW2572", TAS2572 },
+ { "TXNW2574", TAS2574 },
+ { "TXNW2781", TAS2781 },
+ { "TXNW5802", TAS5802 },
+ { "TXNW806M", TAS5806M },
+ { "TXNW806D", TAS5806MD },
+ { "TXNW5815", TAS5815 },
+ { "TXNW5822", TAS5822 },
+ { "TXNW5825", TAS5825 },
+ { "TXNW5827", TAS5827 },
+ { "TXNW5828", TAS5828 },
+ { "TXNW5830", TAS5830 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match);
+#endif
+
+static struct i2c_driver tasdevice_i2c_driver = {
+ .driver = {
+ .name = "tasdev-codec",
+ .of_match_table = of_match_ptr(tasdevice_of_match),
+#ifdef CONFIG_ACPI
+ .acpi_match_table = ACPI_PTR(tasdevice_acpi_match),
+#endif
+ },
+ .probe = tasdevice_i2c_probe,
+ .remove = tasdevice_i2c_remove,
+ .id_table = tasdevice_id,
+};
+
+module_i2c_driver(tasdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>");
+MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>");
+MODULE_DESCRIPTION("ASoC TAS2781 Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
new file mode 100644
index 000000000000..43b779873b93
--- /dev/null
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -0,0 +1,1347 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2783 Audio Smart Amplifier
+//
+// Copyright (C) 2025 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2783 driver implements a flexible and configurable
+// algo coefficient setting for single TAS2783 chips.
+//
+// Author: Niranjan H Y <niranjanhy@ti.com>
+// Author: Baojun Xu <baojun.xu@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+
+#include <linux/unaligned.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/wait.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "tas2783.h"
+
+#define TIMEOUT_FW_DL_MS (3000)
+#define FW_DL_OFFSET 36
+#define FW_FL_HDR 12
+#define TAS2783_PROBE_TIMEOUT 5000
+#define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \
+ 0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92)
+
+static const u32 tas2783_cali_reg[] = {
+ TAS2783_CAL_R0,
+ TAS2783_CAL_INVR0,
+ TAS2783_CAL_R0LOW,
+ TAS2783_CAL_POWER,
+ TAS2783_CAL_TLIM,
+};
+
+struct bin_header_t {
+ u16 vendor_id;
+ u16 version;
+ u32 file_id;
+ u32 length;
+};
+
+struct calibration_data {
+ u32 is_valid;
+ unsigned long read_sz;
+ u8 data[TAS2783_CALIB_DATA_SZ];
+};
+
+struct tas2783_prv {
+ struct snd_soc_component *component;
+ struct calibration_data cali_data;
+ struct sdw_slave *sdw_peripheral;
+ enum sdw_slave_status status;
+ /* calibration */
+ struct mutex calib_lock;
+ /* pde and firmware download */
+ struct mutex pde_lock;
+ struct regmap *regmap;
+ struct device *dev;
+ struct class *class;
+ struct attribute_group *cal_attr_groups;
+ struct tm tm;
+ u8 rca_binaryname[64];
+ u8 dev_name[32];
+ bool hw_init;
+ /* wq for firmware download */
+ wait_queue_head_t fw_wait;
+ bool fw_dl_task_done;
+ bool fw_dl_success;
+};
+
+static const struct reg_default tas2783_reg_default[] = {
+ {TAS2783_AMP_LEVEL, 0x28},
+ {TASDEV_REG_SDW(0, 0, 0x03), 0x28},
+ {TASDEV_REG_SDW(0, 0, 0x04), 0x21},
+ {TASDEV_REG_SDW(0, 0, 0x05), 0x41},
+ {TASDEV_REG_SDW(0, 0, 0x06), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x07), 0x20},
+ {TASDEV_REG_SDW(0, 0, 0x08), 0x09},
+ {TASDEV_REG_SDW(0, 0, 0x09), 0x02},
+ {TASDEV_REG_SDW(0, 0, 0x0a), 0x0a},
+ {TASDEV_REG_SDW(0, 0, 0x0c), 0x10},
+ {TASDEV_REG_SDW(0, 0, 0x0d), 0x13},
+ {TASDEV_REG_SDW(0, 0, 0x0e), 0xc2},
+ {TASDEV_REG_SDW(0, 0, 0x0f), 0x40},
+ {TASDEV_REG_SDW(0, 0, 0x10), 0x04},
+ {TASDEV_REG_SDW(0, 0, 0x13), 0x13},
+ {TASDEV_REG_SDW(0, 0, 0x14), 0x12},
+ {TASDEV_REG_SDW(0, 0, 0x15), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x16), 0x12},
+ {TASDEV_REG_SDW(0, 0, 0x17), 0x80},
+ {TAS2783_DVC_LVL, 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x1b), 0x61},
+ {TASDEV_REG_SDW(0, 0, 0x1c), 0x36},
+ {TASDEV_REG_SDW(0, 0, 0x1d), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x1f), 0x01},
+ {TASDEV_REG_SDW(0, 0, 0x20), 0x2e},
+ {TASDEV_REG_SDW(0, 0, 0x21), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x34), 0x06},
+ {TASDEV_REG_SDW(0, 0, 0x35), 0xbd},
+ {TASDEV_REG_SDW(0, 0, 0x36), 0xad},
+ {TASDEV_REG_SDW(0, 0, 0x37), 0xa8},
+ {TASDEV_REG_SDW(0, 0, 0x38), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x3b), 0xfc},
+ {TASDEV_REG_SDW(0, 0, 0x3d), 0xdd},
+ {TASDEV_REG_SDW(0, 0, 0x40), 0xf6},
+ {TASDEV_REG_SDW(0, 0, 0x41), 0x14},
+ {TASDEV_REG_SDW(0, 0, 0x5c), 0x19},
+ {TASDEV_REG_SDW(0, 0, 0x5d), 0x80},
+ {TASDEV_REG_SDW(0, 0, 0x63), 0x48},
+ {TASDEV_REG_SDW(0, 0, 0x65), 0x08},
+ {TASDEV_REG_SDW(0, 0, 0x66), 0xb2},
+ {TASDEV_REG_SDW(0, 0, 0x67), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x6a), 0x12},
+ {TASDEV_REG_SDW(0, 0, 0x6b), 0xfb},
+ {TASDEV_REG_SDW(0, 0, 0x6c), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x6d), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x6e), 0x1a},
+ {TASDEV_REG_SDW(0, 0, 0x6f), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x70), 0x96},
+ {TASDEV_REG_SDW(0, 0, 0x71), 0x02},
+ {TASDEV_REG_SDW(0, 0, 0x73), 0x08},
+ {TASDEV_REG_SDW(0, 0, 0x75), 0xe0},
+ {TASDEV_REG_SDW(0, 0, 0x7a), 0x60},
+ {TASDEV_REG_SDW(0, 0, 0x60), 0x21},
+ {TASDEV_REG_SDW(0, 1, 0x02), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x17), 0xc0},
+ {TASDEV_REG_SDW(0, 1, 0x19), 0x60},
+ {TASDEV_REG_SDW(0, 1, 0x35), 0x75},
+ {TASDEV_REG_SDW(0, 1, 0x3d), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x3e), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x3f), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x40), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x41), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x42), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x43), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x44), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x45), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x47), 0xab},
+ {TASDEV_REG_SDW(0, 0xfd, 0x0d), 0x0d},
+ {TASDEV_REG_SDW(0, 0xfd, 0x39), 0x00},
+ {TASDEV_REG_SDW(0, 0xfd, 0x3e), 0x00},
+ {TASDEV_REG_SDW(0, 0xfd, 0x45), 0x00},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1), 0x9c00},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 3), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 4), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 5), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 2), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 3), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 4), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xb), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xc), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0},
+};
+
+static const struct reg_sequence tas2783_init_seq[] = {
+ REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x04),
+ REG_SEQ0(0x00800418, 0x00),
+ REG_SEQ0(0x00800419, 0x00),
+ REG_SEQ0(0x0080041a, 0x00),
+ REG_SEQ0(0x0080041b, 0x00),
+ REG_SEQ0(0x00800428, 0x40),
+ REG_SEQ0(0x00800429, 0x00),
+ REG_SEQ0(0x0080042a, 0x00),
+ REG_SEQ0(0x0080042b, 0x00),
+ REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x1, 0x00), 0x00),
+ REG_SEQ0(0x0080005c, 0xD9),
+ REG_SEQ0(0x00800082, 0x20),
+ REG_SEQ0(0x008000a1, 0x00),
+ REG_SEQ0(0x00800097, 0xc8),
+ REG_SEQ0(0x00800099, 0x20),
+ REG_SEQ0(0x008000c7, 0xaa),
+ REG_SEQ0(0x008000b5, 0x74),
+ REG_SEQ0(0x00800082, 0x20),
+ REG_SEQ0(0x00807e8d, 0x0d),
+ REG_SEQ0(0x00807eb9, 0x53),
+ REG_SEQ0(0x00807ebe, 0x42),
+ REG_SEQ0(0x00807ec5, 0x37),
+ REG_SEQ0(0x00800066, 0x92),
+ REG_SEQ0(0x00800003, 0x28),
+ REG_SEQ0(0x00800004, 0x21),
+ REG_SEQ0(0x00800005, 0x41),
+ REG_SEQ0(0x00800006, 0x00),
+ REG_SEQ0(0x00800007, 0x20),
+ REG_SEQ0(0x0080000c, 0x10),
+ REG_SEQ0(0x00800013, 0x08),
+ REG_SEQ0(0x00800015, 0x00),
+ REG_SEQ0(0x00800017, 0x80),
+ REG_SEQ0(0x0080001a, 0x00),
+ REG_SEQ0(0x0080001b, 0x22),
+ REG_SEQ0(0x0080001c, 0x36),
+ REG_SEQ0(0x0080001d, 0x01),
+ REG_SEQ0(0x0080001f, 0x00),
+ REG_SEQ0(0x00800020, 0x2e),
+ REG_SEQ0(0x00800034, 0x06),
+ REG_SEQ0(0x00800035, 0xb9),
+ REG_SEQ0(0x00800036, 0xad),
+ REG_SEQ0(0x00800037, 0xa8),
+ REG_SEQ0(0x00800038, 0x00),
+ REG_SEQ0(0x0080003b, 0xfc),
+ REG_SEQ0(0x0080003d, 0xdd),
+ REG_SEQ0(0x00800040, 0xf6),
+ REG_SEQ0(0x00800041, 0x14),
+ REG_SEQ0(0x0080005c, 0x19),
+ REG_SEQ0(0x0080005d, 0x80),
+ REG_SEQ0(0x00800063, 0x48),
+ REG_SEQ0(0x00800065, 0x08),
+ REG_SEQ0(0x00800067, 0x00),
+ REG_SEQ0(0x0080006a, 0x12),
+ REG_SEQ0(0x0080006b, 0x7b),
+ REG_SEQ0(0x0080006c, 0x00),
+ REG_SEQ0(0x0080006d, 0x00),
+ REG_SEQ0(0x0080006e, 0x1a),
+ REG_SEQ0(0x0080006f, 0x00),
+ REG_SEQ0(0x00800070, 0x96),
+ REG_SEQ0(0x00800071, 0x02),
+ REG_SEQ0(0x00800073, 0x08),
+ REG_SEQ0(0x00800075, 0xe0),
+ REG_SEQ0(0x0080007a, 0x60),
+ REG_SEQ0(0x008000bd, 0x00),
+ REG_SEQ0(0x008000be, 0x00),
+ REG_SEQ0(0x008000bf, 0x00),
+ REG_SEQ0(0x008000c0, 0x00),
+ REG_SEQ0(0x008000c1, 0x00),
+ REG_SEQ0(0x008000c2, 0x00),
+ REG_SEQ0(0x008000c3, 0x00),
+ REG_SEQ0(0x008000c4, 0x00),
+ REG_SEQ0(0x008000c5, 0x00),
+ REG_SEQ0(0x00800008, 0x49),
+ REG_SEQ0(0x00800009, 0x02),
+ REG_SEQ0(0x0080000a, 0x1a),
+ REG_SEQ0(0x0080000d, 0x93),
+ REG_SEQ0(0x0080000e, 0x82),
+ REG_SEQ0(0x0080000f, 0x42),
+ REG_SEQ0(0x00800010, 0x84),
+ REG_SEQ0(0x00800014, 0x0a),
+ REG_SEQ0(0x00800016, 0x00),
+ REG_SEQ0(0x00800060, 0x21),
+};
+
+static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
+{
+ switch (reg) {
+ case 0x000 ... 0x080: /* Data port 0. */
+ case 0x100 ... 0x140: /* Data port 1. */
+ case 0x200 ... 0x240: /* Data port 2. */
+ case 0x300 ... 0x340: /* Data port 3. */
+ case 0x400 ... 0x440: /* Data port 4. */
+ case 0x500 ... 0x540: /* Data port 5. */
+ case 0x800000 ... 0x803fff: /* Page 0 ~ 127. */
+ case 0x807e80 ... 0x807eff: /* Page 253. */
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23,
+ TAS2783_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, TAS2783_SDCA_CTL_FU_MUTE,
+ TAS2783_DEVICE_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 3):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 4):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xb):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xc):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS25, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS25, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0):
+ return 1;
+
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 3):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 4):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 5):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0):
+ return 2;
+
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0):
+ return 4;
+
+ default:
+ return 0;
+ }
+}
+
+static bool tas2783_readable_register(struct device *dev, unsigned int reg)
+{
+ return tas2783_sdca_mbq_size(dev, reg) > 0;
+}
+
+static bool tas2783_volatile_register(struct device *dev, u32 reg)
+{
+ switch (reg) {
+ case 0x000 ... 0x080: /* Data port 0. */
+ case 0x100 ... 0x140: /* Data port 1. */
+ case 0x200 ... 0x240: /* Data port 2. */
+ case 0x300 ... 0x340: /* Data port 3. */
+ case 0x400 ... 0x440: /* Data port 4. */
+ case 0x500 ... 0x540: /* Data port 5. */
+ case 0x800001:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = tas2783_readable_register,
+ .volatile_reg = tas2783_volatile_register,
+ .reg_defaults = tas2783_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default),
+ .max_register = 0x41008000 + TASDEV_REG_SDW(0xa1, 0x60, 0x7f),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_sdw_mbq_cfg tas2783_mbq_cfg = {
+ .mbq_size = tas2783_sdca_mbq_size,
+};
+
+static s32 tas2783_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static s32 tas2783_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static s32 tas2783_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static s32 tas2783_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new tas2783_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Amp Volume", TAS2783_AMP_LEVEL,
+ 1, 0, 20, 0, tas2783_amp_getvol,
+ tas2783_amp_putvol, tas2781_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Volume", TAS2783_DVC_LVL,
+ 0, 0, 200, 1, tas2783_digital_getvol,
+ tas2783_digital_putvol, tas2781_dvc_tlv),
+};
+
+static s32 tas2783_validate_calibdata(struct tas2783_prv *tas_dev,
+ u8 *data, u32 size)
+{
+ u32 ts, spk_count, size_calculated;
+ u32 crc_calculated, crc_read, i;
+ u32 *tmp_val;
+ struct tm tm;
+
+ i = 0;
+ tmp_val = (u32 *)data;
+ if (tmp_val[i++] != 2783) {
+ dev_err(tas_dev->dev, "cal data magic number mismatch");
+ return -EINVAL;
+ }
+
+ spk_count = tmp_val[i++];
+ if (spk_count > TAS2783_CALIB_MAX_SPK_COUNT) {
+ dev_err(tas_dev->dev, "cal data spk_count too large");
+ return -EINVAL;
+ }
+
+ ts = tmp_val[i++];
+ time64_to_tm(ts, 0, &tm);
+ dev_dbg(tas_dev->dev, "cal data timestamp: %ld-%d-%d %d:%d:%d",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ size_calculated =
+ (spk_count * TAS2783_CALIB_PARAMS * sizeof(u32)) +
+ TAS2783_CALIB_HDR_SZ + TAS2783_CALIB_CRC_SZ;
+ if (size_calculated > TAS2783_CALIB_DATA_SZ) {
+ dev_err(tas_dev->dev, "cali data sz too large");
+ return -EINVAL;
+ } else if (size < size_calculated) {
+ dev_err(tas_dev->dev, "cali data size mismatch calc=%u vs %d\n",
+ size, size_calculated);
+ return -EINVAL;
+ }
+
+ crc_calculated = crc32(~0, data,
+ size_calculated - TAS2783_CALIB_CRC_SZ) ^ ~0;
+ crc_read = tmp_val[(size_calculated - TAS2783_CALIB_CRC_SZ) / sizeof(u32)];
+ if (crc_calculated != crc_read) {
+ dev_err(tas_dev->dev,
+ "calib data integrity check fail, 0x%08x vs 0x%08x\n",
+ crc_calculated, crc_read);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32 *cali_data)
+{
+ u32 dev_count, offset, i, device_num;
+ u32 reg_value;
+ u8 buf[4];
+
+ dev_count = cali_data[1];
+ offset = 3;
+
+ for (device_num = 0; device_num < dev_count; device_num++) {
+ if (cali_data[offset] != tas_dev->sdw_peripheral->id.unique_id) {
+ offset += TAS2783_CALIB_PARAMS;
+ continue;
+ }
+ offset++;
+
+ for (i = 0; i < ARRAY_SIZE(tas2783_cali_reg); i++) {
+ reg_value = cali_data[offset + i];
+ buf[0] = reg_value >> 24;
+ buf[1] = reg_value >> 16;
+ buf[2] = reg_value >> 8;
+ buf[3] = reg_value & 0xff;
+ regmap_bulk_write(tas_dev->regmap, tas2783_cali_reg[i],
+ buf, sizeof(u32));
+ }
+ break;
+ }
+
+ if (device_num == dev_count)
+ dev_err(tas_dev->dev, "device not found\n");
+ else
+ dev_dbg(tas_dev->dev, "calib data update done\n");
+}
+
+static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
+{
+ efi_guid_t efi_guid = TAS2783_CALI_GUID;
+ u32 attr, i, *tmp_val;
+ unsigned long size;
+ s32 ret;
+ efi_status_t status;
+ static efi_char16_t efi_names[][32] = {
+ L"SmartAmpCalibrationData", L"CALI_DATA"};
+
+ tmp_val = (u32 *)tas_dev->cali_data.data;
+ attr = 0;
+
+ /*
+ * In some cases, the calibration is performed in Windows,
+ * and data was saved in UEFI. Linux can access it.
+ */
+ for (i = 0; i < ARRAY_SIZE(efi_names); i++) {
+ size = 0;
+ status = efi.get_variable(efi_names[i], &efi_guid, &attr,
+ &size, NULL);
+ if (size > TAS2783_CALIB_DATA_SZ) {
+ dev_err(tas_dev->dev, "cali data too large\n");
+ break;
+ }
+
+ tas_dev->cali_data.read_sz = size;
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ status = efi.get_variable(efi_names[i], &efi_guid, &attr,
+ &tas_dev->cali_data.read_sz,
+ tas_dev->cali_data.data);
+ dev_dbg(tas_dev->dev, "cali get %lu bytes result:%ld\n",
+ tas_dev->cali_data.read_sz, status);
+ }
+ if (status == EFI_SUCCESS)
+ break;
+ }
+
+ if (status != EFI_SUCCESS) {
+ /* Failed got calibration data from EFI. */
+ dev_dbg(tas_dev->dev, "No calibration data in UEFI.");
+ return 0;
+ }
+
+ mutex_lock(&tas_dev->calib_lock);
+ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data,
+ tas_dev->cali_data.read_sz);
+ if (!ret)
+ tas2783_set_calib_params_to_device(tas_dev, tmp_val);
+ mutex_unlock(&tas_dev->calib_lock);
+
+ return ret;
+}
+
+static s32 read_header(const u8 *data, struct bin_header_t *hdr)
+{
+ hdr->vendor_id = get_unaligned_le16(&data[0]);
+ hdr->file_id = get_unaligned_le32(&data[2]);
+ hdr->version = get_unaligned_le16(&data[6]);
+ hdr->length = get_unaligned_le32(&data[8]);
+ return 12;
+}
+
+static void tas2783_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tas2783_prv *tas_dev =
+ (struct tas2783_prv *)context;
+ const u8 *buf = NULL;
+ s32 offset = 0, img_sz, file_blk_size, ret;
+ struct bin_header_t hdr;
+
+ if (!fmw || !fmw->data) {
+ /* No firmware binary, devices will work in ROM mode. */
+ dev_err(tas_dev->dev,
+ "Failed to read %s, no side-effect on driver running\n",
+ tas_dev->rca_binaryname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ img_sz = fmw->size;
+ buf = fmw->data;
+ offset += FW_DL_OFFSET;
+ if (offset >= (img_sz - FW_FL_HDR)) {
+ dev_err(tas_dev->dev,
+ "firmware is too small");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&tas_dev->pde_lock);
+ while (offset < (img_sz - FW_FL_HDR)) {
+ memset(&hdr, 0, sizeof(hdr));
+ offset += read_header(&buf[offset], &hdr);
+ dev_dbg(tas_dev->dev,
+ "vndr=%d, file=%d, version=%d, len=%d, off=%d\n",
+ hdr.vendor_id, hdr.file_id, hdr.version,
+ hdr.length, offset);
+ /* size also includes the header */
+ file_blk_size = hdr.length - FW_FL_HDR;
+
+ /* make sure that enough data is there */
+ if (offset + file_blk_size > img_sz) {
+ ret = -EINVAL;
+ dev_err(tas_dev->dev,
+ "corrupt firmware file");
+ break;
+ }
+
+ switch (hdr.file_id) {
+ case 0:
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ PRAM_ADDR_START, file_blk_size,
+ &buf[offset]);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "PRAM update failed: %d", ret);
+ break;
+
+ case 1:
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ YRAM_ADDR_START, file_blk_size,
+ &buf[offset]);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "YRAM update failed: %d", ret);
+
+ break;
+
+ default:
+ ret = -EINVAL;
+ dev_err(tas_dev->dev, "Unsupported file");
+ break;
+ }
+
+ if (ret == 0)
+ offset += file_blk_size;
+ else
+ break;
+ }
+ mutex_unlock(&tas_dev->pde_lock);
+ if (!ret)
+ tas2783_update_calibdata(tas_dev);
+
+out:
+ if (!ret)
+ tas_dev->fw_dl_success = true;
+ tas_dev->fw_dl_task_done = true;
+ wake_up(&tas_dev->fw_wait);
+ if (fmw)
+ release_firmware(fmw);
+}
+
+static inline s32 tas_clear_latch(struct tas2783_prv *priv)
+{
+ return regmap_update_bits(priv->regmap,
+ TASDEV_REG_SDW(0, 0, 0x5c),
+ 0x04, 0x04);
+}
+
+static s32 tas_fu21_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, s32 event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(component);
+ s32 mute;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ mute = 1;
+ break;
+ }
+
+ return sdw_write_no_pm(tas_dev->sdw_peripheral,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21,
+ TAS2783_SDCA_CTL_FU_MUTE, 1), mute);
+}
+
+static s32 tas_fu23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, s32 event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(component);
+ s32 mute;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ mute = 1;
+ break;
+ }
+
+ return sdw_write_no_pm(tas_dev->sdw_peripheral,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 1), mute);
+}
+
+static const struct snd_soc_dapm_widget tas_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_DAC_E("FU21", NULL, SND_SOC_NOPM, 0, 0, tas_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU23", NULL, SND_SOC_NOPM, 0, 0, tas_fu23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route tas_audio_map[] = {
+ {"FU21", NULL, "ASI"},
+ {"SPK", NULL, "FU21"},
+ {"FU23", NULL, "ASI"},
+ {"SPK", NULL, "FU23"},
+ {"ASI OUT", NULL, "DMIC"},
+};
+
+static s32 tas_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, s32 direction)
+{
+ if (!sdw_stream)
+ return 0;
+
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void tas_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ struct sdw_slave *sdw_peripheral = tas_dev->sdw_peripheral;
+ s32 ret, retry = 3;
+
+ if (!tas_dev->fw_dl_success) {
+ dev_err(tas_dev->dev, "error playback without fw download");
+ return -EINVAL;
+ }
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream)
+ return -EINVAL;
+
+ ret = tas_clear_latch(tas_dev);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "clear latch failed, err=%d", ret);
+
+ mutex_lock(&tas_dev->pde_lock);
+ /*
+ * Sometimes, there is error returned during power on.
+ * So added retry logic to ensure power on so that
+ * port prepare succeeds
+ */
+ do {
+ ret = regmap_write(tas_dev->regmap,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
+ TAS2783_SDCA_POW_STATE_ON);
+ if (!ret)
+ break;
+ usleep_range(2000, 2200);
+ } while (retry--);
+ mutex_unlock(&tas_dev->pde_lock);
+ if (ret)
+ return ret;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params,
+ &stream_config, &port_config);
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ ret = sdw_stream_add_slave(sdw_peripheral,
+ &stream_config, &port_config, 1, sdw_stream);
+ if (ret)
+ dev_err(dai->dev, "Unable to configure port\n");
+
+ return ret;
+}
+
+static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ s32 ret;
+ struct snd_soc_component *component = dai->component;
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream);
+
+ mutex_lock(&tas_dev->pde_lock);
+ ret = regmap_write(tas_dev->regmap,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
+ TAS2783_SDCA_POW_STATE_OFF);
+ mutex_unlock(&tas_dev->pde_lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tas_dai_ops = {
+ .hw_params = tas_sdw_hw_params,
+ .hw_free = tas_sdw_pcm_hw_free,
+ .set_stream = tas_set_sdw_stream,
+ .shutdown = tas_sdw_shutdown,
+};
+
+static struct snd_soc_dai_driver tas_dai_driver[] = {
+ {
+ .name = "tas2783-codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS2783_DEVICE_RATES,
+ .formats = TAS2783_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS2783_DEVICE_RATES,
+ .formats = TAS2783_DEVICE_FORMATS,
+ },
+ .ops = &tas_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static s32 tas_component_probe(struct snd_soc_component *component)
+{
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+
+ tas_dev->component = component;
+ tas25xx_register_misc(tas_dev->sdw_peripheral);
+
+ return 0;
+}
+
+static void tas_component_remove(struct snd_soc_component *codec)
+{
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ tas25xx_deregister_misc();
+ tas_dev->component = NULL;
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_tasdevice = {
+ .probe = tas_component_probe,
+ .remove = tas_component_remove,
+ .controls = tas2783_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2783_snd_controls),
+ .dapm_widgets = tas_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas_dapm_widgets),
+ .dapm_routes = tas_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static s32 tas_init(struct tas2783_prv *tas_dev)
+{
+ s32 ret;
+
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+ ret = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tas_dai_driver,
+ ARRAY_SIZE(tas_dai_driver));
+ if (ret) {
+ dev_err(tas_dev->dev, "%s: codec register error:%d.\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(tas_dev->dev, 3000);
+ pm_runtime_use_autosuspend(tas_dev->dev);
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(tas_dev->dev);
+ pm_runtime_enable(tas_dev->dev);
+
+ return ret;
+}
+
+static s32 tas_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ s32 nval;
+ s32 i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask =
+ SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = 0x04; /* BITMAP: 00000100 */
+ prop->sink_ports = 0x2; /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = false;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = false;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ return 0;
+}
+
+static s32 tas2783_sdca_dev_suspend(struct device *dev)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+
+ if (!tas_dev->hw_init)
+ return 0;
+
+ regcache_cache_only(tas_dev->regmap, true);
+ return 0;
+}
+
+static s32 tas2783_sdca_dev_system_suspend(struct device *dev)
+{
+ return tas2783_sdca_dev_suspend(dev);
+}
+
+static s32 tas2783_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ unsigned long t;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ t = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(TAS2783_PROBE_TIMEOUT));
+ if (!t) {
+ dev_err(&slave->dev, "resume: initialization timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+ return -ETIMEDOUT;
+ }
+
+ slave->unattach_request = 0;
+
+regmap_sync:
+ regcache_cache_only(tas_dev->regmap, false);
+ regcache_sync(tas_dev->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops tas2783_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(tas2783_sdca_dev_system_suspend, tas2783_sdca_dev_resume)
+ RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL)
+};
+
+static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ s32 ret;
+ u8 unique_id = tas_dev->sdw_peripheral->id.unique_id;
+
+ if (tas_dev->hw_init)
+ return 0;
+
+ tas_dev->fw_dl_task_done = false;
+ tas_dev->fw_dl_success = false;
+ scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname),
+ "tas2783-%01x.bin", unique_id);
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tas_dev->rca_binaryname, tas_dev->dev,
+ GFP_KERNEL, tas_dev, tas2783_fw_ready);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "firmware request failed for uid=%d, ret=%d\n",
+ unique_id, ret);
+ return ret;
+ }
+
+ ret = wait_event_timeout(tas_dev->fw_wait, tas_dev->fw_dl_task_done,
+ msecs_to_jiffies(TIMEOUT_FW_DL_MS));
+ if (!ret) {
+ dev_err(tas_dev->dev, "fw request, wait_event timeout\n");
+ ret = -EAGAIN;
+ } else {
+ ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
+ ARRAY_SIZE(tas2783_init_seq));
+ tas_dev->hw_init = true;
+ }
+
+ return ret;
+}
+
+static s32 tas_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev);
+ struct device *dev = &slave->dev;
+
+ dev_dbg(dev, "Peripheral status = %s",
+ status == SDW_SLAVE_UNATTACHED ? "unattached" :
+ status == SDW_SLAVE_ATTACHED ? "attached" : "alert");
+
+ tas_dev->status = status;
+ if (status == SDW_SLAVE_UNATTACHED)
+ tas_dev->hw_init = false;
+
+ /* Perform initialization only if slave status
+ * is present and hw_init flag is false
+ */
+ if (tas_dev->hw_init || tas_dev->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* updated the cache data to device */
+ regcache_cache_only(tas_dev->regmap, false);
+ regcache_sync(tas_dev->regmap);
+
+ /* perform I/O transfers required for Slave initialization */
+ return tas_io_init(&slave->dev, slave);
+}
+
+static const struct sdw_slave_ops tas_sdw_ops = {
+ .read_prop = tas_read_prop,
+ .update_status = tas_update_status,
+};
+
+static void tas_remove(struct tas2783_prv *tas_dev)
+{
+ snd_soc_unregister_component(tas_dev->dev);
+}
+
+static s32 tas_sdw_probe(struct sdw_slave *peripheral,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+ struct device *dev = &peripheral->dev;
+ struct tas2783_prv *tas_dev;
+
+ tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL);
+ if (!tas_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed devm_kzalloc");
+
+ tas_dev->dev = dev;
+ tas_dev->sdw_peripheral = peripheral;
+ tas_dev->hw_init = false;
+ mutex_init(&tas_dev->calib_lock);
+ mutex_init(&tas_dev->pde_lock);
+
+ init_waitqueue_head(&tas_dev->fw_wait);
+ dev_set_drvdata(dev, tas_dev);
+ regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev,
+ peripheral,
+ &tas_regmap,
+ &tas2783_mbq_cfg);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed devm_regmap_init_sdw.");
+
+ /* keep in cache until the device is fully initialized */
+ regcache_cache_only(regmap, true);
+ tas_dev->regmap = regmap;
+ return tas_init(tas_dev);
+}
+
+static s32 tas_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(&peripheral->dev);
+
+ pm_runtime_disable(tas_dev->dev);
+ tas_remove(tas_dev);
+ mutex_destroy(&tas_dev->calib_lock);
+ mutex_destroy(&tas_dev->pde_lock);
+ dev_set_drvdata(&peripheral->dev, NULL);
+
+ return 0;
+}
+
+static const struct sdw_device_id tas_sdw_id[] = {
+ /* chipid for the TAS2783 is 0x0000 */
+ SDW_SLAVE_ENTRY(0x0102, 0x0000, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, tas_sdw_id);
+
+static struct sdw_driver tas_sdw_driver = {
+ .driver = {
+ .name = "slave-tas2783",
+ .pm = pm_ptr(&tas2783_sdca_pm),
+ },
+ .probe = tas_sdw_probe,
+ .remove = tas_sdw_remove,
+ .ops = &tas_sdw_ops,
+ .id_table = tas_sdw_id,
+};
+module_sdw_driver(tas_sdw_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
new file mode 100644
index 000000000000..794333e0a350
--- /dev/null
+++ b/sound/soc/codecs/tas2783.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2783 Audio Smart Amplifier
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2783 driver implements a flexible and configurable
+ * algo coefficient setting for single TAS2783 chips.
+ *
+ * Author: Niranjan H Y <niranjanhy@ti.com>
+ * Author: Baojun Xu <baojun.xu@ti.com>
+ */
+#include <linux/workqueue.h>
+
+#ifndef __TAS2783_H__
+#define __TAS2783_H__
+
+#define TAS2783_DEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200)
+#define TAS2783_DEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* book, page, register */
+#define TASDEV_REG_SDW(book, page, reg) (((book) * 256 * 128) + \
+ 0x800000 + ((page) * 128) + (reg))
+
+/* Volume control */
+#define TAS2783_DVC_LVL TASDEV_REG_SDW(0x0, 0x00, 0x1A)
+#define TAS2783_AMP_LEVEL TASDEV_REG_SDW(0x0, 0x00, 0x03)
+#define TAS2783_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define PRAM_ADDR_START TASDEV_REG_SDW(0x8c, 0x01, 0x8)
+#define PRAM_ADDR_END TASDEV_REG_SDW(0x8c, 0xff, 0x7f)
+#define YRAM_ADDR_START TASDEV_REG_SDW(0x00, 0x02, 0x8)
+#define YRAM_ADDR_END TASDEV_REG_SDW(0x00, 0x37, 0x7f)
+
+/* Calibration data */
+#define TAS2783_CAL_R0 TASDEV_REG_SDW(0, 0x16, 0x4C)
+#define TAS2783_CAL_INVR0 TASDEV_REG_SDW(0, 0x16, 0x5C)
+#define TAS2783_CAL_R0LOW TASDEV_REG_SDW(0, 0x16, 0x64)
+#define TAS2783_CAL_POWER TASDEV_REG_SDW(0, 0x15, 0x44)
+#define TAS2783_CAL_TLIM TASDEV_REG_SDW(0, 0x17, 0x58)
+
+/* TAS2783 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x01
+
+/* TAS2783 SDCA entity */
+
+#define TAS2783_SDCA_ENT_FU21 0x01
+#define TAS2783_SDCA_ENT_FU23 0x02
+#define TAS2783_SDCA_ENT_FU26 0x03
+#define TAS2783_SDCA_ENT_XU22 0x04
+#define TAS2783_SDCA_ENT_CS24 0x05
+#define TAS2783_SDCA_ENT_CS21 0x06
+#define TAS2783_SDCA_ENT_CS25 0x07
+#define TAS2783_SDCA_ENT_CS26 0x08
+#define TAS2783_SDCA_ENT_CS28 0x09
+#define TAS2783_SDCA_ENT_PDE23 0x0C
+#define TAS2783_SDCA_ENT_UDMPU23 0x0E
+#define TAS2783_SDCA_ENT_SAPU29 0x0F
+#define TAS2783_SDCA_ENT_PPU21 0x10
+#define TAS2783_SDCA_ENT_PPU26 0x11
+#define TAS2783_SDCA_ENT_TG23 0x12
+#define TAS2783_SDCA_ENT_IT21 0x13
+#define TAS2783_SDCA_ENT_IT29 0x14
+#define TAS2783_SDCA_ENT_IT26 0x15
+#define TAS2783_SDCA_ENT_IT28 0x16
+#define TAS2783_SDCA_ENT_OT24 0x17
+#define TAS2783_SDCA_ENT_OT23 0x18
+#define TAS2783_SDCA_ENT_OT25 0x19
+#define TAS2783_SDCA_ENT_OT28 0x1A
+#define TAS2783_SDCA_ENT_MU26 0x1b
+#define TAS2783_SDCA_ENT_OT127 0x1E
+#define TAS2783_SDCA_ENT_FU127 0x1F
+#define TAS2783_SDCA_ENT_CS127 0x20
+#define TAS2783_SDCA_ENT_MFPU21 0x22
+#define TAS2783_SDCA_ENT_MFPU26 0x23
+
+/* TAS2783 SDCA control */
+#define TAS2783_SDCA_CTL_REQ_POW_STATE 0x01
+#define TAS2783_SDCA_CTL_FU_MUTE 0x01
+#define TAS2783_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+#define TAS2783_DEVICE_CHANNEL_LEFT 1
+#define TAS2783_DEVICE_CHANNEL_RIGHT 2
+
+#define TAS2783_SDCA_POW_STATE_ON 0
+#define TAS2783_SDCA_POW_STATE_OFF 3
+
+/* calibration data */
+#define TAS2783_CALIB_PARAMS 6 /* 5 + 1 unique id */
+#define TAS2783_CALIB_MAX_SPK_COUNT 8
+#define TAS2783_CALIB_HDR_SZ 12
+#define TAS2783_CALIB_CRC_SZ 4
+#define TAS2783_CALIB_DATA_SZ ((TAS2783_CALIB_HDR_SZ) + TAS2783_CALIB_CRC_SZ + \
+ ((TAS2783_CALIB_PARAMS) * 4 * (TAS2783_CALIB_MAX_SPK_COUNT)))
+
+#if IS_ENABLED(CONFIG_SND_SOC_TAS2783_UTIL)
+int32_t tas25xx_register_misc(struct sdw_slave *peripheral);
+int32_t tas25xx_deregister_misc(void);
+#else
+static void tas25xx_register_misc(struct sdw_slave *peripheral) {}
+static void tas25xx_deregister_misc(void) {}
+#endif
+
+#endif /*__TAS2783_H__ */
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index d49d25d51957..12bf6a89dbd8 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TAS5086 ASoC codec driver
*
* Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* TODO:
* - implement DAPM and input muxing
* - implement modulation limit
@@ -33,14 +24,13 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -255,15 +245,15 @@ struct tas5086_private {
/* Current sample rate for de-emphasis control */
int rate;
/* GPIO driving Reset pin, if any */
- int gpio_nreset;
+ struct gpio_desc *reset;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static int tas5086_deemph[] = { 0, 32000, 44100, 48000 };
-static int tas5086_set_deemph(struct snd_soc_codec *codec)
+static int tas5086_set_deemph(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int i, val = 0;
if (priv->deemph) {
@@ -282,8 +272,8 @@ static int tas5086_set_deemph(struct snd_soc_codec *codec)
static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = priv->deemph;
@@ -293,20 +283,20 @@ static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
static int tas5086_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
priv->deemph = ucontrol->value.integer.value[0];
- return tas5086_set_deemph(codec);
+ return tas5086_set_deemph(component);
}
static int tas5086_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case TAS5086_CLK_IDX_MCLK:
@@ -323,12 +313,12 @@ static int tas5086_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int tas5086_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
/* The TAS5086 can only be slave to all clocks */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(codec->dev, "Invalid clocking mode\n");
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_err(component->dev, "Invalid clocking mode\n");
return -EINVAL;
}
@@ -361,8 +351,8 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int val;
int ret;
@@ -373,7 +363,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
ARRAY_SIZE(tas5086_sample_rates), priv->rate);
if (val < 0) {
- dev_err(codec->dev, "Invalid sample rate\n");
+ dev_err(component->dev, "Invalid sample rate\n");
return -EINVAL;
}
@@ -387,7 +377,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios),
priv->mclk / priv->rate);
if (val < 0) {
- dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n");
+ dev_err(component->dev, "Invalid MCLK / Fs ratio\n");
return -EINVAL;
}
@@ -424,7 +414,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val = 0x06;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -440,7 +430,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val += 2;
break;
default:
- dev_err(codec->dev, "Invalid bit width\n");
+ dev_err(component->dev, "Invalid bit width\n");
return -EINVAL;
}
@@ -454,13 +444,13 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return tas5086_set_deemph(codec);
+ return tas5086_set_deemph(component);
}
static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
unsigned int val = 0;
if (mute)
@@ -471,11 +461,11 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
static void tas5086_reset(struct tas5086_private *priv)
{
- if (gpio_is_valid(priv->gpio_nreset)) {
+ if (priv->reset) {
/* Reset codec - minimum assertion time is 400ns */
- gpio_direction_output(priv->gpio_nreset, 0);
+ gpiod_set_value_cansleep(priv->reset, 1);
udelay(1);
- gpio_set_value(priv->gpio_nreset, 1);
+ gpiod_set_value_cansleep(priv->reset, 0);
/* Codec needs ~15ms to wake up */
msleep(15);
@@ -496,7 +486,7 @@ static int tas5086_init(struct device *dev, struct tas5086_private *priv)
/*
* If any of the channels is configured to start in Mid-Z mode,
* configure 'part 1' of the PWM starts to use Mid-Z, and tell
- * all configured mid-z channels to start start under 'part 1'.
+ * all configured mid-z channels to start under 'part 1'.
*/
if (priv->pwm_start_mid_z)
regmap_write(priv->regmap, TAS5086_PWM_START,
@@ -773,9 +763,9 @@ static struct snd_soc_dai_driver tas5086_dai = {
};
#ifdef CONFIG_PM
-static int tas5086_soc_suspend(struct snd_soc_codec *codec)
+static int tas5086_soc_suspend(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int ret;
/* Shut down all channels */
@@ -788,9 +778,9 @@ static int tas5086_soc_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int tas5086_soc_resume(struct snd_soc_codec *codec)
+static int tas5086_soc_resume(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
@@ -800,7 +790,7 @@ static int tas5086_soc_resume(struct snd_soc_codec *codec)
tas5086_reset(priv);
regcache_mark_dirty(priv->regmap);
- ret = tas5086_init(codec->dev, priv);
+ ret = tas5086_init(component->dev, priv);
if (ret < 0)
return ret;
@@ -823,22 +813,22 @@ static const struct of_device_id tas5086_dt_ids[] = {
MODULE_DEVICE_TABLE(of, tas5086_dt_ids);
#endif
-static int tas5086_probe(struct snd_soc_codec *codec)
+static int tas5086_probe(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int i, ret;
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
if (ret < 0) {
- dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
priv->pwm_start_mid_z = 0;
priv->charge_period = 1300000; /* hardware default is 1300 ms */
- if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
- struct device_node *of_node = codec->dev->of_node;
+ if (of_match_device(of_match_ptr(tas5086_dt_ids), component->dev)) {
+ struct device_node *of_node = component->dev->of_node;
of_property_read_u32(of_node, "ti,charge-period",
&priv->charge_period);
@@ -849,13 +839,13 @@ static int tas5086_probe(struct snd_soc_codec *codec)
snprintf(name, sizeof(name),
"ti,mid-z-channel-%d", i + 1);
- if (of_get_property(of_node, name, NULL) != NULL)
+ if (of_property_read_bool(of_node, name))
priv->pwm_start_mid_z |= 1 << i;
}
}
tas5086_reset(priv);
- ret = tas5086_init(codec->dev, priv);
+ ret = tas5086_init(component->dev, priv);
if (ret < 0)
goto exit_disable_regulators;
@@ -872,20 +862,19 @@ exit_disable_regulators:
return ret;
}
-static int tas5086_remove(struct snd_soc_codec *codec)
+static void tas5086_remove(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(priv->gpio_nreset))
+ if (priv->reset) {
/* Set codec to the reset state */
- gpio_set_value(priv->gpio_nreset, 0);
+ gpiod_set_value_cansleep(priv->reset, 1);
+ }
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
-
- return 0;
};
-static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
+static const struct snd_soc_component_driver soc_component_dev_tas5086 = {
.probe = tas5086_probe,
.remove = tas5086_remove,
.suspend = tas5086_soc_suspend,
@@ -896,10 +885,13 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
.dapm_routes = tas5086_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct i2c_device_id tas5086_i2c_id[] = {
- { "tas5086", 0 },
+ { "tas5086" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
@@ -918,12 +910,10 @@ static const struct regmap_config tas5086_regmap = {
.reg_write = tas5086_reg_write,
};
-static int tas5086_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tas5086_i2c_probe(struct i2c_client *i2c)
{
struct tas5086_private *priv;
struct device *dev = &i2c->dev;
- int gpio_nreset = -EINVAL;
int i, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -949,16 +939,11 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, priv);
- if (of_match_device(of_match_ptr(tas5086_dt_ids), dev)) {
- struct device_node *of_node = dev->of_node;
- gpio_nreset = of_get_named_gpio(of_node, "reset-gpio", 0);
- }
-
- if (gpio_is_valid(gpio_nreset))
- if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset"))
- gpio_nreset = -EINVAL;
-
- priv->gpio_nreset = gpio_nreset;
+ /* Request line asserted */
+ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+ gpiod_set_consumer_name(priv->reset, "TAS5086 Reset");
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
if (ret < 0) {
@@ -983,17 +968,15 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
if (ret == 0)
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_tas5086,
&tas5086_dai, 1);
return ret;
}
-static int tas5086_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
-}
+static void tas5086_i2c_remove(struct i2c_client *i2c)
+{}
static struct i2c_driver tas5086_i2c_driver = {
.driver = {
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 39307ad41a34..19ccf8641e16 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -1,13 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TAS571x amplifier audio driver
*
* Copyright (C) 2015 Google, Inc.
* Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
*
- * 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.
+ * TAS5721 support:
+ * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
+ *
+ * TAS5707 support:
+ * Copyright (C) 2018 Jerome Brunet, Baylibre SAS <jbrunet@baylibre.com>
*/
#include <linux/clk.h>
@@ -18,13 +20,14 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/stddef.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <linux/unaligned.h>
#include "tas571x.h"
@@ -47,7 +50,7 @@ struct tas571x_private {
unsigned int format;
struct gpio_desc *reset_gpio;
struct gpio_desc *pdn_gpio;
- struct snd_soc_codec_driver codec_driver;
+ struct snd_soc_component_driver component_driver;
};
static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
@@ -57,6 +60,14 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
case TAS571X_CH1_VOL_REG:
case TAS571X_CH2_VOL_REG:
return priv->chip->vol_reg_size;
+ case TAS571X_INPUT_MUX_REG:
+ case TAS571X_CH4_SRC_SELECT_REG:
+ case TAS571X_PWM_MUX_REG:
+ case TAS5717_CH1_RIGHT_CH_MIX_REG:
+ case TAS5717_CH1_LEFT_CH_MIX_REG:
+ case TAS5717_CH2_LEFT_CH_MIX_REG:
+ case TAS5717_CH2_RIGHT_CH_MIX_REG:
+ return 4;
default:
return 1;
}
@@ -128,9 +139,132 @@ static int tas571x_reg_read(void *context, unsigned int reg,
return 0;
}
+/*
+ * register write for 8- and 20-byte registers
+ */
+static int tas571x_reg_write_multiword(struct i2c_client *client,
+ unsigned int reg, const long values[], size_t len)
+{
+ size_t i;
+ uint8_t *buf, *p;
+ int ret;
+ size_t send_size = 1 + len * sizeof(uint32_t);
+
+ buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA);
+ if (!buf)
+ return -ENOMEM;
+ buf[0] = reg;
+
+ for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t))
+ put_unaligned_be32(values[i], p);
+
+ ret = i2c_master_send(client, buf, send_size);
+
+ kfree(buf);
+
+ if (ret == send_size)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+/*
+ * register read for 8- and 20-byte registers
+ */
+static int tas571x_reg_read_multiword(struct i2c_client *client,
+ unsigned int reg, long values[], size_t len)
+{
+ unsigned int i;
+ uint8_t send_buf;
+ uint8_t *recv_buf, *p;
+ struct i2c_msg msgs[2];
+ unsigned int recv_size = len * sizeof(uint32_t);
+ int ret;
+
+ recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA);
+ if (!recv_buf)
+ return -ENOMEM;
+
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = recv_size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ goto err_ret;
+ else if (ret != ARRAY_SIZE(msgs)) {
+ ret = -EIO;
+ goto err_ret;
+ }
+
+ for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t))
+ values[i] = get_unaligned_be32(p);
+
+err_ret:
+ kfree(recv_buf);
+ return ret;
+}
+
+/*
+ * Integer array controls for setting biquad, mixer, DRC coefficients.
+ * According to the datasheet each coefficient is effectively 26bits,
+ * i.e. stored as 32bits, where bits [31:26] are ignored.
+ * TI's TAS57xx Graphical Development Environment tool however produces
+ * coefficients with more than 26 bits. For this reason we allow values
+ * in the full 32-bits reange.
+ * The coefficients are ordered as given in the TAS571x data sheet:
+ * b0, b1, b2, a1, a2
+ */
+
+static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int numcoef = kcontrol->private_value >> 16;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = numcoef;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffffffff;
+ return 0;
+}
+
+static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+
+ return tas571x_reg_read_multiword(i2c, index,
+ ucontrol->value.integer.value, numcoef);
+}
+
+static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+
+ return tas571x_reg_write_multiword(i2c, index,
+ ucontrol->value.integer.value, numcoef);
+}
+
static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
{
- struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ struct tas571x_private *priv = snd_soc_component_get_drvdata(dai->component);
priv->format = format;
@@ -141,7 +275,7 @@ static int tas571x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ struct tas571x_private *priv = snd_soc_component_get_drvdata(dai->component);
u32 val;
switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -167,10 +301,28 @@ static int tas571x_hw_params(struct snd_pcm_substream *substream,
TAS571X_SDI_FMT_MASK, val);
}
-static int tas571x_set_bias_level(struct snd_soc_codec *codec,
+static int tas571x_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 sysctl2;
+ int ret;
+
+ sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0;
+
+ ret = snd_soc_component_update_bits(component,
+ TAS571X_SYS_CTRL_2_REG,
+ TAS571X_SYS_CTRL_2_SDN_MASK,
+ sysctl2);
+ usleep_range(1000, 2000);
+
+ return ret;
+}
+
+static int tas571x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas571x_private *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -179,30 +331,19 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
if (!IS_ERR(priv->mclk)) {
ret = clk_prepare_enable(priv->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable master clock: %d\n",
ret);
return ret;
}
}
-
- gpiod_set_value(priv->pdn_gpio, 0);
- usleep_range(5000, 6000);
-
- regcache_cache_only(priv->regmap, false);
- ret = regcache_sync(priv->regmap);
- if (ret)
- return ret;
}
break;
case SND_SOC_BIAS_OFF:
- regcache_cache_only(priv->regmap, true);
- gpiod_set_value(priv->pdn_gpio, 1);
-
if (!IS_ERR(priv->mclk))
clk_disable_unprepare(priv->mclk);
break;
@@ -214,8 +355,19 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops tas571x_dai_ops = {
.set_fmt = tas571x_set_dai_fmt,
.hw_params = tas571x_hw_params,
+ .mute_stream = tas571x_mute,
+ .no_capture_mute = 1,
};
+
+#define BIQUAD_COEFS(xname, reg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = tas571x_coefficient_info, \
+ .get = tas571x_coefficient_get,\
+ .put = tas571x_coefficient_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .private_value = reg | (5 << 16) }
+
static const char *const tas5711_supply_names[] = {
"AVDD",
"DVDD",
@@ -241,6 +393,26 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
1, 1),
};
+static const struct regmap_range tas571x_readonly_regs_range[] = {
+ regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG),
+};
+
+static const struct regmap_range tas571x_volatile_regs_range[] = {
+ regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG),
+ regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG),
+};
+
+static const struct regmap_access_table tas571x_write_regs = {
+ .no_ranges = tas571x_readonly_regs_range,
+ .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range),
+};
+
+static const struct regmap_access_table tas571x_volatile_regs = {
+ .yes_ranges = tas571x_volatile_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range),
+
+};
+
static const struct reg_default tas5711_reg_defaults[] = {
{ 0x04, 0x05 },
{ 0x05, 0x40 },
@@ -260,6 +432,8 @@ static const struct regmap_config tas5711_regmap_config = {
.reg_defaults = tas5711_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults),
.cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
};
static const struct tas571x_chip tas5711_chip = {
@@ -271,6 +445,111 @@ static const struct tas571x_chip tas5711_chip = {
.vol_reg_size = 1,
};
+static const struct regmap_range tas5707_volatile_regs_range[] = {
+ regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG),
+ regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG),
+ regmap_reg_range(TAS5707_CH1_BQ0_REG, TAS5707_CH2_BQ6_REG),
+};
+
+static const struct regmap_access_table tas5707_volatile_regs = {
+ .yes_ranges = tas5707_volatile_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(tas5707_volatile_regs_range),
+
+};
+
+static const DECLARE_TLV_DB_SCALE(tas5707_volume_tlv, -7900, 50, 1);
+
+static const char * const tas5707_volume_slew_step_txt[] = {
+ "256", "512", "1024", "2048",
+};
+
+static const unsigned int tas5707_volume_slew_step_values[] = {
+ 3, 0, 1, 2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(tas5707_volume_slew_step_enum,
+ TAS571X_VOL_CFG_REG, 0, 0x3,
+ tas5707_volume_slew_step_txt,
+ tas5707_volume_slew_step_values);
+
+static const struct snd_kcontrol_new tas5707_controls[] = {
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG,
+ 0, 0xff, 1, tas5707_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG,
+ TAS571X_CH2_VOL_REG,
+ 0, 0xff, 1, tas5707_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+
+ SOC_ENUM("Slew Rate Steps", tas5707_volume_slew_step_enum),
+
+ BIQUAD_COEFS("CH1 - Biquad 0", TAS5707_CH1_BQ0_REG),
+ BIQUAD_COEFS("CH1 - Biquad 1", TAS5707_CH1_BQ1_REG),
+ BIQUAD_COEFS("CH1 - Biquad 2", TAS5707_CH1_BQ2_REG),
+ BIQUAD_COEFS("CH1 - Biquad 3", TAS5707_CH1_BQ3_REG),
+ BIQUAD_COEFS("CH1 - Biquad 4", TAS5707_CH1_BQ4_REG),
+ BIQUAD_COEFS("CH1 - Biquad 5", TAS5707_CH1_BQ5_REG),
+ BIQUAD_COEFS("CH1 - Biquad 6", TAS5707_CH1_BQ6_REG),
+
+ BIQUAD_COEFS("CH2 - Biquad 0", TAS5707_CH2_BQ0_REG),
+ BIQUAD_COEFS("CH2 - Biquad 1", TAS5707_CH2_BQ1_REG),
+ BIQUAD_COEFS("CH2 - Biquad 2", TAS5707_CH2_BQ2_REG),
+ BIQUAD_COEFS("CH2 - Biquad 3", TAS5707_CH2_BQ3_REG),
+ BIQUAD_COEFS("CH2 - Biquad 4", TAS5707_CH2_BQ4_REG),
+ BIQUAD_COEFS("CH2 - Biquad 5", TAS5707_CH2_BQ5_REG),
+ BIQUAD_COEFS("CH2 - Biquad 6", TAS5707_CH2_BQ6_REG),
+};
+
+static const struct reg_default tas5707_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x70},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x00},
+ {TAS571X_MVOL_REG, 0xff},
+ {TAS571X_CH1_VOL_REG, 0x30},
+ {TAS571X_CH2_VOL_REG, 0x30},
+ {TAS571X_VOL_CFG_REG, 0x91},
+ {TAS571X_MODULATION_LIMIT_REG, 0x02},
+ {TAS571X_IC_DELAY_CH1_REG, 0xac},
+ {TAS571X_IC_DELAY_CH2_REG, 0x54},
+ {TAS571X_IC_DELAY_CH3_REG, 0xac},
+ {TAS571X_IC_DELAY_CH4_REG, 0x54},
+ {TAS571X_START_STOP_PERIOD_REG, 0x0f},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x02},
+ {TAS571X_INPUT_MUX_REG, 0x17772},
+ {TAS571X_PWM_MUX_REG, 0x1021345},
+};
+
+static const struct regmap_config tas5707_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5707_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5707_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas5707_volatile_regs,
+};
+
+static const struct tas571x_chip tas5707_chip = {
+ .supply_names = tas5711_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5711_supply_names),
+ .controls = tas5707_controls,
+ .num_controls = ARRAY_SIZE(tas5707_controls),
+ .regmap_config = &tas5707_regmap_config,
+ .vol_reg_size = 1,
+};
+
static const char *const tas5717_supply_names[] = {
"AVDD",
"DVDD",
@@ -293,6 +572,53 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
+
+ SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+ TAS5717_CH1_LEFT_CH_MIX_REG,
+ TAS5717_CH1_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+ TAS5717_CH2_LEFT_CH_MIX_REG,
+ TAS5717_CH2_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ /*
+ * The biquads are named according to the register names.
+ * Please note that TI's TAS57xx Graphical Development Environment
+ * tool names them different.
+ */
+ BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG),
+ BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG),
+ BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG),
+ BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG),
+ BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG),
+ BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG),
+ BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG),
+ BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG),
+ BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG),
+ BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG),
+ BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG),
+ BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG),
+
+ BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG),
+ BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG),
+ BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG),
+ BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG),
+ BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG),
+ BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG),
+ BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG),
+ BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG),
+ BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG),
+ BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG),
+ BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG),
+ BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG),
+
+ BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG),
+ BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG),
+
+ BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG),
+ BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG),
};
static const struct reg_default tas5717_reg_defaults[] = {
@@ -303,6 +629,10 @@ static const struct reg_default tas5717_reg_defaults[] = {
{ 0x08, 0x00c0 },
{ 0x09, 0x00c0 },
{ 0x1b, 0x82 },
+ { TAS5717_CH1_RIGHT_CH_MIX_REG, 0x0 },
+ { TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+ { TAS5717_CH2_LEFT_CH_MIX_REG, 0x0 },
+ { TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
};
static const struct regmap_config tas5717_regmap_config = {
@@ -314,6 +644,8 @@ static const struct regmap_config tas5717_regmap_config = {
.reg_defaults = tas5717_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults),
.cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
};
/* This entry is reused for tas5719 as the software interface is identical. */
@@ -326,6 +658,247 @@ static const struct tas571x_chip tas5717_chip = {
.vol_reg_size = 2,
};
+static const char *const tas5721_supply_names[] = {
+ "AVDD",
+ "DVDD",
+ "DRVDD",
+ "PVDD",
+};
+
+static const struct snd_kcontrol_new tas5721_controls[] = {
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG,
+ 0, 0xff, 1, tas5711_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG,
+ TAS571X_CH2_VOL_REG,
+ 0, 0xff, 1, tas5711_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+};
+
+static const struct reg_default tas5721_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x00},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x00},
+ {TAS571X_MVOL_REG, 0xff},
+ {TAS571X_CH1_VOL_REG, 0x30},
+ {TAS571X_CH2_VOL_REG, 0x30},
+ {TAS571X_CH3_VOL_REG, 0x30},
+ {TAS571X_VOL_CFG_REG, 0x91},
+ {TAS571X_MODULATION_LIMIT_REG, 0x02},
+ {TAS571X_IC_DELAY_CH1_REG, 0xac},
+ {TAS571X_IC_DELAY_CH2_REG, 0x54},
+ {TAS571X_IC_DELAY_CH3_REG, 0xac},
+ {TAS571X_IC_DELAY_CH4_REG, 0x54},
+ {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30},
+ {TAS571X_START_STOP_PERIOD_REG, 0x0f},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x02},
+ {TAS571X_INPUT_MUX_REG, 0x17772},
+ {TAS571X_CH4_SRC_SELECT_REG, 0x4303},
+ {TAS571X_PWM_MUX_REG, 0x1021345},
+};
+
+static const struct regmap_config tas5721_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5721_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+static const struct snd_kcontrol_new tas5733_controls[] = {
+ /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG, 1, 0x1ff, 1,
+ tas5717_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG,
+ 1, 0x1ff, 1, tas5717_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+
+ SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+ TAS5717_CH1_LEFT_CH_MIX_REG,
+ TAS5717_CH1_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+ TAS5717_CH2_LEFT_CH_MIX_REG,
+ TAS5717_CH2_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ /*
+ * The biquads are named according to the register names.
+ * Please note that TI's TAS57xx Graphical Development Environment
+ * tool names them different.
+ */
+ BIQUAD_COEFS("CH1 - Biquad 0", TAS5733_CH1_BQ0_REG),
+ BIQUAD_COEFS("CH1 - Biquad 1", TAS5733_CH1_BQ1_REG),
+ BIQUAD_COEFS("CH1 - Biquad 2", TAS5733_CH1_BQ2_REG),
+ BIQUAD_COEFS("CH1 - Biquad 3", TAS5733_CH1_BQ3_REG),
+ BIQUAD_COEFS("CH1 - Biquad 4", TAS5733_CH1_BQ4_REG),
+ BIQUAD_COEFS("CH1 - Biquad 5", TAS5733_CH1_BQ5_REG),
+ BIQUAD_COEFS("CH1 - Biquad 6", TAS5733_CH1_BQ6_REG),
+ BIQUAD_COEFS("CH1 - Biquad 7", TAS5733_CH1_BQ7_REG),
+ BIQUAD_COEFS("CH1 - Biquad 8", TAS5733_CH1_BQ8_REG),
+ BIQUAD_COEFS("CH1 - Biquad 9", TAS5733_CH1_BQ9_REG),
+ BIQUAD_COEFS("CH1 - Biquad 10", TAS5733_CH1_BQ10_REG),
+
+ BIQUAD_COEFS("CH2 - Biquad 0", TAS5733_CH2_BQ0_REG),
+ BIQUAD_COEFS("CH2 - Biquad 1", TAS5733_CH2_BQ1_REG),
+ BIQUAD_COEFS("CH2 - Biquad 2", TAS5733_CH2_BQ2_REG),
+ BIQUAD_COEFS("CH2 - Biquad 3", TAS5733_CH2_BQ3_REG),
+ BIQUAD_COEFS("CH2 - Biquad 4", TAS5733_CH2_BQ4_REG),
+ BIQUAD_COEFS("CH2 - Biquad 5", TAS5733_CH2_BQ5_REG),
+ BIQUAD_COEFS("CH2 - Biquad 6", TAS5733_CH2_BQ6_REG),
+ BIQUAD_COEFS("CH2 - Biquad 7", TAS5733_CH2_BQ7_REG),
+ BIQUAD_COEFS("CH2 - Biquad 8", TAS5733_CH2_BQ8_REG),
+ BIQUAD_COEFS("CH2 - Biquad 9", TAS5733_CH2_BQ9_REG),
+ BIQUAD_COEFS("CH2 - Biquad 10", TAS5733_CH2_BQ10_REG),
+
+ BIQUAD_COEFS("CH1 - Cross Biquad 0", TAS5733_CH1_CBQ0_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 1", TAS5733_CH1_CBQ1_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 2", TAS5733_CH1_CBQ2_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 3", TAS5733_CH1_CBQ3_REG),
+
+ BIQUAD_COEFS("CH2 - Cross Biquad 0", TAS5733_CH2_CBQ0_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 1", TAS5733_CH2_CBQ1_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 2", TAS5733_CH2_CBQ2_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 3", TAS5733_CH2_CBQ3_REG),
+};
+
+static const char *const tas5733_supply_names[] = {
+ "AVDD",
+ "DVDD",
+ "PVDD",
+};
+
+static const struct reg_default tas5733_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x00},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x07},
+ {TAS571X_MVOL_REG, 0x03ff},
+ {TAS571X_CH1_VOL_REG, 0x00c0},
+ {TAS571X_CH2_VOL_REG, 0x00c0},
+ {TAS571X_CH3_VOL_REG, 0x00c0},
+ {TAS571X_VOL_CFG_REG, 0xf0},
+ {TAS571X_MODULATION_LIMIT_REG, 0x07},
+ {TAS571X_IC_DELAY_CH1_REG, 0xb8},
+ {TAS571X_IC_DELAY_CH2_REG, 0x60},
+ {TAS571X_IC_DELAY_CH3_REG, 0xa0},
+ {TAS571X_IC_DELAY_CH4_REG, 0x48},
+ {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30},
+ {TAS571X_START_STOP_PERIOD_REG, 0x68},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x02},
+ {TAS571X_INPUT_MUX_REG, 0x00897772},
+ {TAS571X_PWM_MUX_REG, 0x01021345},
+ {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00},
+ {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+ {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00},
+ {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
+};
+
+static const struct regmap_config tas5733_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5733_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5733_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+static const struct tas571x_chip tas5733_chip = {
+ .supply_names = tas5733_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5733_supply_names),
+ .controls = tas5733_controls,
+ .num_controls = ARRAY_SIZE(tas5733_controls),
+ .regmap_config = &tas5733_regmap_config,
+ .vol_reg_size = 2,
+};
+
+static const struct reg_default tas5753_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x41},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x00},
+ {TAS571X_MVOL_REG, 0x03ff},
+ {TAS571X_CH1_VOL_REG, 0x00c0},
+ {TAS571X_CH2_VOL_REG, 0x00c0},
+ {TAS571X_CH3_VOL_REG, 0x00c0},
+ {TAS571X_VOL_CFG_REG, 0xf0},
+ {TAS571X_MODULATION_LIMIT_REG, 0x01},
+ {TAS571X_IC_DELAY_CH1_REG, 0xac},
+ {TAS571X_IC_DELAY_CH2_REG, 0x54},
+ {TAS571X_IC_DELAY_CH3_REG, 0xac},
+ {TAS571X_IC_DELAY_CH4_REG, 0x54},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x57},
+ {TAS571X_INPUT_MUX_REG, 0x00017772},
+ {TAS571X_PWM_MUX_REG, 0x01021345},
+ {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00},
+ {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+ {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00},
+ {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
+};
+
+static const struct regmap_config tas5753_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5753_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5753_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+static const struct tas571x_chip tas5753_chip = {
+ .supply_names = tas5721_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5721_supply_names),
+ .controls = tas5733_controls,
+ .num_controls = ARRAY_SIZE(tas5733_controls),
+ .regmap_config = &tas5753_regmap_config,
+ .vol_reg_size = 2,
+};
+
+static const struct tas571x_chip tas5721_chip = {
+ .supply_names = tas5721_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5721_supply_names),
+ .controls = tas5721_controls,
+ .num_controls = ARRAY_SIZE(tas5721_controls),
+ .regmap_config = &tas5721_regmap_config,
+ .vol_reg_size = 1,
+};
+
static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
@@ -346,14 +919,14 @@ static const struct snd_soc_dapm_route tas571x_dapm_routes[] = {
{ "OUT_D", NULL, "DACR" },
};
-static const struct snd_soc_codec_driver tas571x_codec = {
- .set_bias_level = tas571x_set_bias_level,
- .idle_bias_off = true,
-
- .dapm_widgets = tas571x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets),
- .dapm_routes = tas571x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes),
+static const struct snd_soc_component_driver tas571x_component = {
+ .set_bias_level = tas571x_set_bias_level,
+ .dapm_widgets = tas571x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets),
+ .dapm_routes = tas571x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static struct snd_soc_dai_driver tas571x_dai = {
@@ -370,14 +943,10 @@ static struct snd_soc_dai_driver tas571x_dai = {
.ops = &tas571x_dai_ops,
};
-static const struct of_device_id tas571x_of_match[];
-
-static int tas571x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas571x_i2c_probe(struct i2c_client *client)
{
struct tas571x_private *priv;
struct device *dev = &client->dev;
- const struct of_device_id *of_id;
int i, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -385,12 +954,7 @@ static int tas571x_i2c_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, priv);
- of_id = of_match_device(tas571x_of_match, dev);
- if (!of_id) {
- dev_err(dev, "Unknown device type\n");
- return -EINVAL;
- }
- priv->chip = of_id->data;
+ priv->chip = i2c_get_match_data(client);
priv->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
@@ -399,7 +963,8 @@ static int tas571x_i2c_probe(struct i2c_client *client,
return PTR_ERR(priv->mclk);
}
- BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES);
+ if (WARN_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES))
+ return -EINVAL;
for (i = 0; i < priv->chip->num_supply_names; i++)
priv->supplies[i].supply = priv->chip->supply_names[i];
@@ -418,14 +983,17 @@ static int tas571x_i2c_probe(struct i2c_client *client,
priv->regmap = devm_regmap_init(dev, NULL, client,
priv->chip->regmap_config);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ goto disable_regs;
+ }
priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
if (IS_ERR(priv->pdn_gpio)) {
dev_err(dev, "error requesting pdn_gpio: %ld\n",
PTR_ERR(priv->pdn_gpio));
- return PTR_ERR(priv->pdn_gpio);
+ ret = PTR_ERR(priv->pdn_gpio);
+ goto disable_regs;
}
priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -433,26 +1001,24 @@ static int tas571x_i2c_probe(struct i2c_client *client,
if (IS_ERR(priv->reset_gpio)) {
dev_err(dev, "error requesting reset_gpio: %ld\n",
PTR_ERR(priv->reset_gpio));
- return PTR_ERR(priv->reset_gpio);
+ ret = PTR_ERR(priv->reset_gpio);
+ goto disable_regs;
} else if (priv->reset_gpio) {
/* pulse the active low reset line for ~100us */
usleep_range(100, 200);
gpiod_set_value(priv->reset_gpio, 0);
- usleep_range(12000, 20000);
+ usleep_range(13500, 20000);
}
ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
if (ret)
- return ret;
+ goto disable_regs;
- ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG,
- TAS571X_SYS_CTRL_2_SDN_MASK, 0);
- if (ret)
- return ret;
+ usleep_range(50000, 60000);
- memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
- priv->codec_driver.controls = priv->chip->controls;
- priv->codec_driver.num_controls = priv->chip->num_controls;
+ memcpy(&priv->component_driver, &tas571x_component, sizeof(priv->component_driver));
+ priv->component_driver.controls = priv->chip->controls;
+ priv->component_driver.num_controls = priv->chip->num_controls;
if (priv->chip->vol_reg_size == 2) {
/*
@@ -462,38 +1028,49 @@ static int tas571x_i2c_probe(struct i2c_client *client,
*/
ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
if (ret)
- return ret;
+ goto disable_regs;
}
- regcache_cache_only(priv->regmap, true);
- gpiod_set_value(priv->pdn_gpio, 1);
-
- return snd_soc_register_codec(&client->dev, &priv->codec_driver,
+ ret = devm_snd_soc_register_component(&client->dev,
+ &priv->component_driver,
&tas571x_dai, 1);
+ if (ret)
+ goto disable_regs;
+
+ return ret;
+
+disable_regs:
+ regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
+ return ret;
}
-static int tas571x_i2c_remove(struct i2c_client *client)
+static void tas571x_i2c_remove(struct i2c_client *client)
{
struct tas571x_private *priv = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
-
- return 0;
}
-static const struct of_device_id tas571x_of_match[] = {
+static const struct of_device_id tas571x_of_match[] __maybe_unused = {
+ { .compatible = "ti,tas5707", .data = &tas5707_chip, },
{ .compatible = "ti,tas5711", .data = &tas5711_chip, },
{ .compatible = "ti,tas5717", .data = &tas5717_chip, },
{ .compatible = "ti,tas5719", .data = &tas5717_chip, },
+ { .compatible = "ti,tas5721", .data = &tas5721_chip, },
+ { .compatible = "ti,tas5733", .data = &tas5733_chip, },
+ { .compatible = "ti,tas5753", .data = &tas5753_chip, },
{ }
};
MODULE_DEVICE_TABLE(of, tas571x_of_match);
static const struct i2c_device_id tas571x_i2c_id[] = {
- { "tas5711", 0 },
- { "tas5717", 0 },
- { "tas5719", 0 },
+ { "tas5707", (kernel_ulong_t) &tas5707_chip },
+ { "tas5711", (kernel_ulong_t) &tas5711_chip },
+ { "tas5717", (kernel_ulong_t) &tas5717_chip },
+ { "tas5719", (kernel_ulong_t) &tas5717_chip },
+ { "tas5721", (kernel_ulong_t) &tas5721_chip },
+ { "tas5733", (kernel_ulong_t) &tas5733_chip },
+ { "tas5753", (kernel_ulong_t) &tas5753_chip },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
index 0aee471232cd..2b3eff4023b9 100644
--- a/sound/soc/codecs/tas571x.h
+++ b/sound/soc/codecs/tas571x.h
@@ -1,18 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* TAS571x amplifier audio driver
*
* Copyright (C) 2015 Google, 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; either version 2 of the License, or
- * (at your option) any later version.
*/
#ifndef _TAS571X_H
#define _TAS571X_H
/* device registers */
+#define TAS571X_CLK_CTRL_REG 0x00
+#define TAS571X_DEV_ID_REG 0x01
+#define TAS571X_ERR_STATUS_REG 0x02
+#define TAS571X_SYS_CTRL_1_REG 0x03
#define TAS571X_SDI_REG 0x04
#define TAS571X_SDI_FMT_MASK 0x0f
@@ -27,7 +27,115 @@
#define TAS571X_MVOL_REG 0x07
#define TAS571X_CH1_VOL_REG 0x08
#define TAS571X_CH2_VOL_REG 0x09
+#define TAS571X_CH3_VOL_REG 0x0a
+#define TAS571X_VOL_CFG_REG 0x0e
+#define TAS571X_MODULATION_LIMIT_REG 0x10
+#define TAS571X_IC_DELAY_CH1_REG 0x11
+#define TAS571X_IC_DELAY_CH2_REG 0x12
+#define TAS571X_IC_DELAY_CH3_REG 0x13
+#define TAS571X_IC_DELAY_CH4_REG 0x14
+
+#define TAS571X_PWM_CH_SDN_GROUP_REG 0x19 /* N/A on TAS5717, TAS5719 */
+#define TAS571X_PWM_CH1_SDN_MASK (1<<0)
+#define TAS571X_PWM_CH2_SDN_SHIFT (1<<1)
+#define TAS571X_PWM_CH3_SDN_SHIFT (1<<2)
+#define TAS571X_PWM_CH4_SDN_SHIFT (1<<3)
+#define TAS571X_START_STOP_PERIOD_REG 0x1a
#define TAS571X_OSC_TRIM_REG 0x1b
+#define TAS571X_BKND_ERR_REG 0x1c
+#define TAS571X_INPUT_MUX_REG 0x20
+#define TAS571X_CH4_SRC_SELECT_REG 0x21
+#define TAS571X_PWM_MUX_REG 0x25
+
+/* 20-byte biquad registers */
+#define TAS5707_CH1_BQ0_REG 0x29
+#define TAS5707_CH1_BQ1_REG 0x2a
+#define TAS5707_CH1_BQ2_REG 0x2b
+#define TAS5707_CH1_BQ3_REG 0x2c
+#define TAS5707_CH1_BQ4_REG 0x2d
+#define TAS5707_CH1_BQ5_REG 0x2e
+#define TAS5707_CH1_BQ6_REG 0x2f
+
+#define TAS5707_CH2_BQ0_REG 0x30
+#define TAS5707_CH2_BQ1_REG 0x31
+#define TAS5707_CH2_BQ2_REG 0x32
+#define TAS5707_CH2_BQ3_REG 0x33
+#define TAS5707_CH2_BQ4_REG 0x34
+#define TAS5707_CH2_BQ5_REG 0x35
+#define TAS5707_CH2_BQ6_REG 0x36
+
+#define TAS5717_CH1_BQ0_REG 0x26
+#define TAS5717_CH1_BQ1_REG 0x27
+#define TAS5717_CH1_BQ2_REG 0x28
+#define TAS5717_CH1_BQ3_REG 0x29
+#define TAS5717_CH1_BQ4_REG 0x2a
+#define TAS5717_CH1_BQ5_REG 0x2b
+#define TAS5717_CH1_BQ6_REG 0x2c
+#define TAS5717_CH1_BQ7_REG 0x2d
+#define TAS5717_CH1_BQ8_REG 0x2e
+#define TAS5717_CH1_BQ9_REG 0x2f
+
+#define TAS5717_CH2_BQ0_REG 0x30
+#define TAS5717_CH2_BQ1_REG 0x31
+#define TAS5717_CH2_BQ2_REG 0x32
+#define TAS5717_CH2_BQ3_REG 0x33
+#define TAS5717_CH2_BQ4_REG 0x34
+#define TAS5717_CH2_BQ5_REG 0x35
+#define TAS5717_CH2_BQ6_REG 0x36
+#define TAS5717_CH2_BQ7_REG 0x37
+#define TAS5717_CH2_BQ8_REG 0x38
+#define TAS5717_CH2_BQ9_REG 0x39
+
+#define TAS5717_CH1_BQ10_REG 0x58
+#define TAS5717_CH1_BQ11_REG 0x59
+
+#define TAS5717_CH4_BQ0_REG 0x5a
+#define TAS5717_CH4_BQ1_REG 0x5b
+
+#define TAS5717_CH2_BQ10_REG 0x5c
+#define TAS5717_CH2_BQ11_REG 0x5d
+
+#define TAS5717_CH3_BQ0_REG 0x5e
+#define TAS5717_CH3_BQ1_REG 0x5f
+
+#define TAS5717_CH1_RIGHT_CH_MIX_REG 0x72
+#define TAS5717_CH1_LEFT_CH_MIX_REG 0x73
+#define TAS5717_CH2_LEFT_CH_MIX_REG 0x76
+#define TAS5717_CH2_RIGHT_CH_MIX_REG 0x77
+
+#define TAS5733_CH1_BQ0_REG 0x26
+#define TAS5733_CH1_BQ1_REG 0x27
+#define TAS5733_CH1_BQ2_REG 0x28
+#define TAS5733_CH1_BQ3_REG 0x29
+#define TAS5733_CH1_BQ4_REG 0x2a
+#define TAS5733_CH1_BQ5_REG 0x2b
+#define TAS5733_CH1_BQ6_REG 0x2c
+#define TAS5733_CH1_BQ7_REG 0x2d
+#define TAS5733_CH1_BQ8_REG 0x2e
+#define TAS5733_CH1_BQ9_REG 0x2f
+
+#define TAS5733_CH2_BQ0_REG 0x30
+#define TAS5733_CH2_BQ1_REG 0x31
+#define TAS5733_CH2_BQ2_REG 0x32
+#define TAS5733_CH2_BQ3_REG 0x33
+#define TAS5733_CH2_BQ4_REG 0x34
+#define TAS5733_CH2_BQ5_REG 0x35
+#define TAS5733_CH2_BQ6_REG 0x36
+#define TAS5733_CH2_BQ7_REG 0x37
+#define TAS5733_CH2_BQ8_REG 0x38
+#define TAS5733_CH2_BQ9_REG 0x39
+
+#define TAS5733_CH1_BQ10_REG 0x58
+#define TAS5733_CH1_CBQ0_REG 0x59
+#define TAS5733_CH1_CBQ1_REG 0x5a
+#define TAS5733_CH1_CBQ2_REG 0x5b
+#define TAS5733_CH1_CBQ3_REG 0x5c
+
+#define TAS5733_CH2_BQ10_REG 0x5d
+#define TAS5733_CH2_CBQ0_REG 0x5e
+#define TAS5733_CH2_CBQ1_REG 0x5f
+#define TAS5733_CH2_CBQ2_REG 0x60
+#define TAS5733_CH2_CBQ3_REG 0x61
#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
new file mode 100644
index 000000000000..2dcdd0a4bf80
--- /dev/null
+++ b/sound/soc/codecs/tas5720.c
@@ -0,0 +1,827 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
+ *
+ * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tas5720.h"
+
+/* Define how often to check (and clear) the fault status register (in ms) */
+#define TAS5720_FAULT_CHECK_INTERVAL 200
+
+enum tas572x_type {
+ TAS5720,
+ TAS5720A_Q1,
+ TAS5722,
+};
+
+static const char * const tas5720_supply_names[] = {
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "pvdd", /* Class-D amp and analog power supply (connected). */
+};
+
+#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names)
+
+struct tas5720_data {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ enum tas572x_type devtype;
+ struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
+ struct delayed_work fault_check_work;
+ unsigned int last_fault;
+};
+
+static int tas5720_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int rate = params_rate(params);
+ bool ssz_ds;
+ int ret;
+
+ switch (rate) {
+ case 44100:
+ case 48000:
+ ssz_ds = false;
+ break;
+ case 88200:
+ case 96000:
+ ssz_ds = true;
+ break;
+ default:
+ dev_err(component->dev, "unsupported sample rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_SSZ_DS, ssz_ds);
+ if (ret < 0) {
+ dev_err(component->dev, "error setting sample rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 serial_format;
+ int ret;
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_vdbg(component->dev, "DAI clocking invalid\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_INV_MASK)) {
+ case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
+ /* 1st data bit occur one BCLK cycle after the frame sync */
+ serial_format = TAS5720_SAIF_I2S;
+ break;
+ case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):
+ /*
+ * Note that although the TAS5720 does not have a dedicated DSP
+ * mode it doesn't care about the LRCLK duty cycle during TDM
+ * operation. Therefore we can use the device's I2S mode with
+ * its delaying of the 1st data bit to receive DSP_A formatted
+ * data. See device datasheet for additional details.
+ */
+ serial_format = TAS5720_SAIF_I2S;
+ break;
+ case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):
+ /*
+ * Similar to DSP_A, we can use the fact that the TAS5720 does
+ * not care about the LRCLK duty cycle during TDM to receive
+ * DSP_B formatted data in LEFTJ mode (no delaying of the 1st
+ * data bit).
+ */
+ serial_format = TAS5720_SAIF_LEFTJ;
+ break;
+ case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
+ /* No delay after the frame sync */
+ serial_format = TAS5720_SAIF_LEFTJ;
+ break;
+ default:
+ dev_vdbg(component->dev, "DAI Format is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_SAIF_FORMAT_MASK,
+ serial_format);
+ if (ret < 0) {
+ dev_err(component->dev, "error setting SAIF format: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ unsigned int first_slot;
+ int ret;
+
+ if (!tx_mask) {
+ dev_err(component->dev, "tx masks must not be 0\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Determine the first slot that is being requested. We will only
+ * use the first slot that is found since the TAS5720 is a mono
+ * amplifier.
+ */
+ first_slot = __ffs(tx_mask);
+
+ if (first_slot > 7) {
+ dev_err(component->dev, "slot selection out of bounds (%u)\n",
+ first_slot);
+ return -EINVAL;
+ }
+
+ /*
+ * Enable manual TDM slot selection (instead of I2C ID based).
+ * This is not applicable to TAS5720A-Q1.
+ */
+ switch (tas5720->devtype) {
+ case TAS5720A_Q1:
+ break;
+ default:
+ ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+
+ /* Configure the TDM slot to process audio from */
+ ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
+ TAS5720_TDM_SLOT_SEL_MASK, first_slot);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+ break;
+ }
+
+ /* Configure TDM slot width. This is only applicable to TAS5722. */
+ switch (tas5720->devtype) {
+ case TAS5722:
+ ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,
+ TAS5722_TDM_SLOT_16B,
+ slot_width == 16 ?
+ TAS5722_TDM_SLOT_16B : 0);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+
+error_snd_soc_component_update_bits:
+ dev_err(component->dev, "error configuring TDM mode: %d\n", ret);
+ return ret;
+}
+
+static int tas5720_mute_soc_component(struct snd_soc_component *component, int mute)
+{
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, mask;
+ int ret;
+
+ switch (tas5720->devtype) {
+ case TAS5720A_Q1:
+ reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG;
+ mask = TAS5720_Q1_MUTE;
+ break;
+ default:
+ reg = TAS5720_DIGITAL_CTRL2_REG;
+ mask = TAS5720_MUTE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0);
+ if (ret < 0) {
+ dev_err(component->dev, "error (un-)muting device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ return tas5720_mute_soc_component(dai->component, mute);
+}
+
+static void tas5720_fault_check_work(struct work_struct *work)
+{
+ struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,
+ fault_check_work.work);
+ struct device *dev = tas5720->component->dev;
+ unsigned int curr_fault;
+ int ret;
+
+ ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FAULT register: %d\n", ret);
+ goto out;
+ }
+
+ /* Check/handle all errors except SAIF clock errors */
+ curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE;
+
+ /*
+ * Only flag errors once for a given occurrence. This is needed as
+ * the TAS5720 will take time clearing the fault condition internally
+ * during which we don't want to bombard the system with the same
+ * error message over and over.
+ */
+ if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE))
+ dev_crit(dev, "experienced an over current hardware fault\n");
+
+ if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE))
+ dev_crit(dev, "experienced a DC detection fault\n");
+
+ if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE))
+ dev_crit(dev, "experienced an over temperature fault\n");
+
+ /* Store current fault value so we can detect any changes next time */
+ tas5720->last_fault = curr_fault;
+
+ if (!curr_fault)
+ goto out;
+
+ /*
+ * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching
+ * faults as long as a fault condition persists. Always going through
+ * the full sequence no matter the first return value to minimizes
+ * chances for the device to end up in shutdown mode.
+ */
+ ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, 0);
+ if (ret < 0)
+ dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
+
+ ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, TAS5720_SDZ);
+ if (ret < 0)
+ dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
+
+out:
+ /* Schedule the next fault check at the specified interval */
+ schedule_delayed_work(&tas5720->fault_check_work,
+ msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
+}
+
+static int tas5720_codec_probe(struct snd_soc_component *component)
+{
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ unsigned int device_id, expected_device_id;
+ int ret;
+
+ tas5720->component = component;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret != 0) {
+ dev_err(component->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Take a liberal approach to checking the device ID to allow the
+ * driver to be used even if the device ID does not match, however
+ * issue a warning if there is a mismatch.
+ */
+ ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
+ if (ret < 0) {
+ dev_err(component->dev, "failed to read device ID register: %d\n",
+ ret);
+ goto probe_fail;
+ }
+
+ switch (tas5720->devtype) {
+ case TAS5720:
+ expected_device_id = TAS5720_DEVICE_ID;
+ break;
+ case TAS5720A_Q1:
+ expected_device_id = TAS5720A_Q1_DEVICE_ID;
+ break;
+ case TAS5722:
+ expected_device_id = TAS5722_DEVICE_ID;
+ break;
+ default:
+ dev_err(component->dev, "unexpected private driver data\n");
+ ret = -EINVAL;
+ goto probe_fail;
+ }
+
+ if (device_id != expected_device_id)
+ dev_warn(component->dev, "wrong device ID. expected: %u read: %u\n",
+ expected_device_id, device_id);
+
+ /* Set device to mute */
+ ret = tas5720_mute_soc_component(component, 1);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+
+ /* Set Bit 7 in TAS5720_ANALOG_CTRL_REG to 1 for TAS5720A_Q1 */
+ switch (tas5720->devtype) {
+ case TAS5720A_Q1:
+ ret = snd_soc_component_update_bits(component, TAS5720_ANALOG_CTRL_REG,
+ TAS5720_Q1_RESERVED7_BIT,
+ TAS5720_Q1_RESERVED7_BIT);
+ break;
+ default:
+ break;
+ }
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+
+ /*
+ * Enter shutdown mode - our default when not playing audio - to
+ * minimize current consumption. On the TAS5720 there is no real down
+ * side doing so as all device registers are preserved and the wakeup
+ * of the codec is rather quick which we do using a dapm widget.
+ */
+ ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, 0);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+
+ INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work);
+
+ return 0;
+
+error_snd_soc_component_update_bits:
+ dev_err(component->dev, "error configuring device registers: %d\n", ret);
+
+probe_fail:
+ regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ return ret;
+}
+
+static void tas5720_codec_remove(struct snd_soc_component *component)
+{
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ cancel_delayed_work_sync(&tas5720->fault_check_work);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret < 0)
+ dev_err(component->dev, "failed to disable supplies: %d\n", ret);
+};
+
+static int tas5720_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Take TAS5720 out of shutdown mode */
+ ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, TAS5720_SDZ);
+ if (ret < 0) {
+ dev_err(component->dev, "error waking component: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Observe codec shutdown-to-active time. The datasheet only
+ * lists a nominal value however just use-it as-is without
+ * additional padding to minimize the delay introduced in
+ * starting to play audio (actually there is other setup done
+ * by the ASoC framework that will provide additional delays,
+ * so we should always be safe).
+ */
+ msleep(25);
+
+ /* Turn on TAS5720 periodic fault checking/handling */
+ tas5720->last_fault = 0;
+ schedule_delayed_work(&tas5720->fault_check_work,
+ msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
+ } else if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Disable TAS5720 periodic fault checking/handling */
+ cancel_delayed_work_sync(&tas5720->fault_check_work);
+
+ /* Place TAS5720 in shutdown mode to minimize current draw */
+ ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "error shutting down component: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas5720_suspend(struct snd_soc_component *component)
+{
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ regcache_cache_only(tas5720->regmap, true);
+ regcache_mark_dirty(tas5720->regmap);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret < 0)
+ dev_err(component->dev, "failed to disable supplies: %d\n", ret);
+
+ return ret;
+}
+
+static int tas5720_resume(struct snd_soc_component *component)
+{
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(tas5720->regmap, false);
+
+ ret = regcache_sync(tas5720->regmap);
+ if (ret < 0) {
+ dev_err(component->dev, "failed to sync regcache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#else
+#define tas5720_suspend NULL
+#define tas5720_resume NULL
+#endif
+
+static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5720_DEVICE_ID_REG:
+ case TAS5720_FAULT_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas5720_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS5720_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5720_is_volatile_reg,
+};
+
+static const struct regmap_config tas5720a_q1_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS5720_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5720_is_volatile_reg,
+};
+
+static const struct regmap_config tas5722_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS5722_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5720_is_volatile_reg,
+};
+
+/*
+ * DAC analog gain. There are four discrete values to select from, ranging
+ * from 19.2 dB to 26.3dB.
+ */
+static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
+ 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0),
+ 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0),
+ 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0),
+);
+
+/*
+ * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging
+ * from 19.2 dB to 25.0dB.
+ */
+static const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1,
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
+ 0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0),
+ 0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0),
+);
+
+/*
+ * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps
+ * depending on the device. Note that setting the gain below -100 dB
+ * (register value <0x7) is effectively a MUTE as per device datasheet.
+ *
+ * Note that for the TAS5722 the digital volume controls are actually split
+ * over two registers, so we need custom getters/setters for access.
+ */
+static DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0);
+
+static int tas5722_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+
+ val = snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG);
+ ucontrol->value.integer.value[0] = val << 1;
+
+ val = snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG);
+ ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB;
+
+ return 0;
+}
+
+static int tas5722_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1);
+ snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,
+ TAS5722_VOL_CONTROL_LSB, sel);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tas5720_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Driver Playback Volume",
+ TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+ TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
+};
+
+static const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume",
+ TAS5720_Q1_VOLUME_CTRL_LEFT_REG,
+ TAS5720_Q1_VOLUME_CTRL_RIGHT_REG,
+ 0, 0xff, 0, tas5720_dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+ TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1),
+};
+
+static const struct snd_kcontrol_new tas5722_snd_controls[] = {
+ SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume",
+ 0, 0, 511, 0,
+ tas5722_volume_get, tas5722_volume_set,
+ tas5722_dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+ TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
+};
+
+static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas5720_audio_map[] = {
+ { "DAC", NULL, "DAC IN" },
+ { "OUT", NULL, "DAC" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tas5720 = {
+ .probe = tas5720_codec_probe,
+ .remove = tas5720_codec_remove,
+ .suspend = tas5720_suspend,
+ .resume = tas5720_resume,
+ .controls = tas5720_snd_controls,
+ .num_controls = ARRAY_SIZE(tas5720_snd_controls),
+ .dapm_widgets = tas5720_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
+ .dapm_routes = tas5720_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = {
+ .probe = tas5720_codec_probe,
+ .remove = tas5720_codec_remove,
+ .suspend = tas5720_suspend,
+ .resume = tas5720_resume,
+ .controls = tas5720a_q1_snd_controls,
+ .num_controls = ARRAY_SIZE(tas5720a_q1_snd_controls),
+ .dapm_widgets = tas5720_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
+ .dapm_routes = tas5720_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tas5722 = {
+ .probe = tas5720_codec_probe,
+ .remove = tas5720_codec_remove,
+ .suspend = tas5720_suspend,
+ .resume = tas5720_resume,
+ .controls = tas5722_snd_controls,
+ .num_controls = ARRAY_SIZE(tas5722_snd_controls),
+ .dapm_widgets = tas5720_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
+ .dapm_routes = tas5720_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/* PCM rates supported by the TAS5720 driver */
+#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+/* Formats supported by TAS5720 driver */
+#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
+ .hw_params = tas5720_hw_params,
+ .set_fmt = tas5720_set_dai_fmt,
+ .set_tdm_slot = tas5720_set_dai_tdm_slot,
+ .mute_stream = tas5720_mute,
+ .no_capture_mute = 1,
+};
+
+/*
+ * TAS5720 DAI structure
+ *
+ * Note that were are advertising .playback.channels_max = 2 despite this being
+ * a mono amplifier. The reason for that is that some serial ports such as TI's
+ * McASP module have a minimum number of channels (2) that they can output.
+ * Advertising more channels than we have will allow us to interface with such
+ * a serial port without really any negative side effects as the TAS5720 will
+ * simply ignore any extra channel(s) asides from the one channel that is
+ * configured to be played back.
+ */
+static struct snd_soc_dai_driver tas5720_dai[] = {
+ {
+ .name = "tas5720-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAS5720_RATES,
+ .formats = TAS5720_FORMATS,
+ },
+ .ops = &tas5720_speaker_dai_ops,
+ },
+};
+
+static const struct i2c_device_id tas5720_id[] = {
+ { "tas5720", TAS5720 },
+ { "tas5720a-q1", TAS5720A_Q1 },
+ { "tas5722", TAS5722 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5720_id);
+
+static int tas5720_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tas5720_data *data;
+ const struct regmap_config *regmap_config;
+ int ret;
+ int i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->devtype = (uintptr_t)i2c_get_match_data(client);
+
+ switch (data->devtype) {
+ case TAS5720:
+ regmap_config = &tas5720_regmap_config;
+ break;
+ case TAS5720A_Q1:
+ regmap_config = &tas5720a_q1_regmap_config;
+ break;
+ case TAS5722:
+ regmap_config = &tas5722_regmap_config;
+ break;
+ default:
+ dev_err(dev, "unexpected private driver data\n");
+ return -EINVAL;
+ }
+ data->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(dev, "failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+ data->supplies[i].supply = tas5720_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+ data->supplies);
+ if (ret != 0) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(dev, data);
+
+ switch (data->devtype) {
+ case TAS5720:
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_tas5720,
+ tas5720_dai,
+ ARRAY_SIZE(tas5720_dai));
+ break;
+ case TAS5720A_Q1:
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_tas5720_a_q1,
+ tas5720_dai,
+ ARRAY_SIZE(tas5720_dai));
+ break;
+ case TAS5722:
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_tas5722,
+ tas5720_dai,
+ ARRAY_SIZE(tas5720_dai));
+ break;
+ default:
+ dev_err(dev, "unexpected private driver data\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ dev_err(dev, "failed to register component: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas5720_of_match[] = {
+ { .compatible = "ti,tas5720", },
+ { .compatible = "ti,tas5720a-q1", },
+ { .compatible = "ti,tas5722", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tas5720_of_match);
+#endif
+
+static struct i2c_driver tas5720_i2c_driver = {
+ .driver = {
+ .name = "tas5720",
+ .of_match_table = of_match_ptr(tas5720_of_match),
+ },
+ .probe = tas5720_probe,
+ .id_table = tas5720_id,
+};
+
+module_i2c_driver(tas5720_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_DESCRIPTION("TAS5720 Audio amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h
new file mode 100644
index 000000000000..54b59b05ef0a
--- /dev/null
+++ b/sound/soc/codecs/tas5720.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
+ *
+ * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ */
+
+#ifndef __TAS5720_H__
+#define __TAS5720_H__
+
+/* Register Address Map - first 3 regs are common for all variants */
+#define TAS5720_DEVICE_ID_REG 0x00
+#define TAS5720_POWER_CTRL_REG 0x01
+#define TAS5720_DIGITAL_CTRL1_REG 0x02
+#define TAS5720_DIGITAL_CTRL2_REG 0x03
+#define TAS5720_VOLUME_CTRL_REG 0x04
+#define TAS5720_ANALOG_CTRL_REG 0x06
+#define TAS5720_FAULT_REG 0x08
+#define TAS5720_DIGITAL_CLIP2_REG 0x10
+#define TAS5720_DIGITAL_CLIP1_REG 0x11
+#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG
+
+/* Additional TAS5722-specific Registers */
+#define TAS5722_DIGITAL_CTRL2_REG 0x13
+#define TAS5722_ANALOG_CTRL2_REG 0x14
+#define TAS5722_MAX_REG TAS5722_ANALOG_CTRL2_REG
+
+/* Register Address Map - volume controls for the TAS5720-Q1 variant */
+#define TAS5720_Q1_VOLUME_CTRL_CFG_REG 0x03
+#define TAS5720_Q1_VOLUME_CTRL_LEFT_REG 0x04
+#define TAS5720_Q1_VOLUME_CTRL_RIGHT_REG 0x05
+
+/* TAS5720_DEVICE_ID_REG */
+#define TAS5720A_Q1_DEVICE_ID 0x00
+#define TAS5720_DEVICE_ID 0x01
+#define TAS5722_DEVICE_ID 0x12
+
+/* TAS5720_POWER_CTRL_REG */
+#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2)
+#define TAS5720_SLEEP BIT(1)
+#define TAS5720_SDZ BIT(0)
+
+/* TAS5720_DIGITAL_CTRL1_REG */
+#define TAS5720_HPF_BYPASS BIT(7)
+#define TAS5720_TDM_CFG_SRC BIT(6)
+#define TAS5720_SSZ_DS BIT(3)
+#define TAS5720_SAIF_RIGHTJ_24BIT (0x0)
+#define TAS5720_SAIF_RIGHTJ_20BIT (0x1)
+#define TAS5720_SAIF_RIGHTJ_18BIT (0x2)
+#define TAS5720_SAIF_RIGHTJ_16BIT (0x3)
+#define TAS5720_SAIF_I2S (0x4)
+#define TAS5720_SAIF_LEFTJ (0x5)
+#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0)
+
+/* TAS5720_DIGITAL_CTRL2_REG */
+#define TAS5722_VOL_RAMP_RATE BIT(6)
+#define TAS5720_MUTE BIT(4)
+#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0)
+
+/* TAS5720_Q1_VOLUME_CTRL_CFG_REG */
+#define TAS5720_Q1_FADE BIT(7)
+#define TAS5720_Q1_MUTE GENMASK(1, 0)
+
+/* TAS5720_ANALOG_CTRL_REG */
+#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4)
+#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4)
+#define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4)
+#define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4)
+#define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4)
+#define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4)
+#define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4)
+#define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4)
+#define TAS5720_PWM_RATE_MASK GENMASK(6, 4)
+#define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2)
+#define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2)
+#define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2)
+#define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2)
+#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2)
+#define TAS5720_ANALOG_GAIN_SHIFT (0x2)
+
+/* TAS5720_Q1_ANALOG_CTRL_REG */
+#define TAS5720_Q1_RESERVED7_BIT BIT(7)
+#define TAS5720_Q1_CHAN_SEL BIT(1)
+
+/* TAS5720_FAULT_REG */
+#define TAS5720_OC_THRESH_100PCT (0x0 << 4)
+#define TAS5720_OC_THRESH_75PCT (0x1 << 4)
+#define TAS5720_OC_THRESH_50PCT (0x2 << 4)
+#define TAS5720_OC_THRESH_25PCT (0x3 << 4)
+#define TAS5720_OC_THRESH_MASK GENMASK(5, 4)
+#define TAS5720_CLKE BIT(3)
+#define TAS5720_OCE BIT(2)
+#define TAS5720_DCE BIT(1)
+#define TAS5720_OTE BIT(0)
+#define TAS5720_FAULT_MASK GENMASK(3, 0)
+
+/* TAS5720_DIGITAL_CLIP1_REG */
+#define TAS5720_CLIP1_MASK GENMASK(7, 2)
+#define TAS5720_CLIP1_SHIFT (0x2)
+
+/* TAS5722_DIGITAL_CTRL2_REG */
+#define TAS5722_HPF_3_7HZ (0x0 << 5)
+#define TAS5722_HPF_7_4HZ (0x1 << 5)
+#define TAS5722_HPF_14_9HZ (0x2 << 5)
+#define TAS5722_HPF_29_7HZ (0x3 << 5)
+#define TAS5722_HPF_59_4HZ (0x4 << 5)
+#define TAS5722_HPF_118_4HZ (0x5 << 5)
+#define TAS5722_HPF_235_0HZ (0x6 << 5)
+#define TAS5722_HPF_463_2HZ (0x7 << 5)
+#define TAS5722_HPF_MASK GENMASK(7, 5)
+#define TAS5722_AUTO_SLEEP_OFF (0x0 << 3)
+#define TAS5722_AUTO_SLEEP_1024LR (0x1 << 3)
+#define TAS5722_AUTO_SLEEP_65536LR (0x2 << 3)
+#define TAS5722_AUTO_SLEEP_262144LR (0x3 << 3)
+#define TAS5722_AUTO_SLEEP_MASK GENMASK(4, 3)
+#define TAS5722_TDM_SLOT_16B BIT(2)
+#define TAS5722_MCLK_PIN_CFG BIT(1)
+#define TAS5722_VOL_CONTROL_LSB BIT(0)
+
+/* TAS5722_ANALOG_CTRL2_REG */
+#define TAS5722_FAULTZ_PU BIT(3)
+#define TAS5722_VREG_LVL BIT(2)
+#define TAS5722_PWR_TUNE BIT(0)
+
+#endif /* __TAS5720_H__ */
diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c
new file mode 100644
index 000000000000..867046b7aaa0
--- /dev/null
+++ b/sound/soc/codecs/tas5805m.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the TAS5805M Audio Amplifier
+//
+// Author: Andy Liu <andy-liu@ti.com>
+// Author: Daniel Beer <daniel.beer@igorinstitute.com>
+//
+// This is based on a driver originally written by Andy Liu at TI and
+// posted here:
+//
+// https://e2e.ti.com/support/audio-group/audio/f/audio-forum/722027/linux-tas5825m-linux-drivers
+//
+// It has been simplified a little and reworked for the 5.x ALSA SoC API.
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/atomic.h>
+#include <linux/workqueue.h>
+
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+/* Datasheet-defined registers on page 0, book 0 */
+#define REG_PAGE 0x00
+#define REG_DEVICE_CTRL_1 0x02
+#define REG_DEVICE_CTRL_2 0x03
+#define REG_SIG_CH_CTRL 0x28
+#define REG_SAP_CTRL_1 0x33
+#define REG_FS_MON 0x37
+#define REG_BCK_MON 0x38
+#define REG_CLKDET_STATUS 0x39
+#define REG_VOL_CTL 0x4c
+#define REG_AGAIN 0x54
+#define REG_ADR_PIN_CTRL 0x60
+#define REG_ADR_PIN_CONFIG 0x61
+#define REG_CHAN_FAULT 0x70
+#define REG_GLOBAL_FAULT1 0x71
+#define REG_GLOBAL_FAULT2 0x72
+#define REG_FAULT 0x78
+#define REG_BOOK 0x7f
+
+/* DEVICE_CTRL_2 register values */
+#define DCTRL2_MODE_DEEP_SLEEP 0x00
+#define DCTRL2_MODE_SLEEP 0x01
+#define DCTRL2_MODE_HIZ 0x02
+#define DCTRL2_MODE_PLAY 0x03
+
+#define DCTRL2_MUTE 0x08
+#define DCTRL2_DIS_DSP 0x10
+
+/* This sequence of register writes must always be sent, prior to the
+ * 5ms delay while we wait for the DSP to boot.
+ */
+static const uint8_t dsp_cfg_preboot[] = {
+ 0x00, 0x00, 0x7f, 0x00, 0x03, 0x02, 0x01, 0x11,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x00, 0x03, 0x02,
+};
+
+static const uint32_t tas5805m_volume[] = {
+ 0x0000001B, /* 0, -110dB */ 0x0000001E, /* 1, -109dB */
+ 0x00000021, /* 2, -108dB */ 0x00000025, /* 3, -107dB */
+ 0x0000002A, /* 4, -106dB */ 0x0000002F, /* 5, -105dB */
+ 0x00000035, /* 6, -104dB */ 0x0000003B, /* 7, -103dB */
+ 0x00000043, /* 8, -102dB */ 0x0000004B, /* 9, -101dB */
+ 0x00000054, /* 10, -100dB */ 0x0000005E, /* 11, -99dB */
+ 0x0000006A, /* 12, -98dB */ 0x00000076, /* 13, -97dB */
+ 0x00000085, /* 14, -96dB */ 0x00000095, /* 15, -95dB */
+ 0x000000A7, /* 16, -94dB */ 0x000000BC, /* 17, -93dB */
+ 0x000000D3, /* 18, -92dB */ 0x000000EC, /* 19, -91dB */
+ 0x00000109, /* 20, -90dB */ 0x0000012A, /* 21, -89dB */
+ 0x0000014E, /* 22, -88dB */ 0x00000177, /* 23, -87dB */
+ 0x000001A4, /* 24, -86dB */ 0x000001D8, /* 25, -85dB */
+ 0x00000211, /* 26, -84dB */ 0x00000252, /* 27, -83dB */
+ 0x0000029A, /* 28, -82dB */ 0x000002EC, /* 29, -81dB */
+ 0x00000347, /* 30, -80dB */ 0x000003AD, /* 31, -79dB */
+ 0x00000420, /* 32, -78dB */ 0x000004A1, /* 33, -77dB */
+ 0x00000532, /* 34, -76dB */ 0x000005D4, /* 35, -75dB */
+ 0x0000068A, /* 36, -74dB */ 0x00000756, /* 37, -73dB */
+ 0x0000083B, /* 38, -72dB */ 0x0000093C, /* 39, -71dB */
+ 0x00000A5D, /* 40, -70dB */ 0x00000BA0, /* 41, -69dB */
+ 0x00000D0C, /* 42, -68dB */ 0x00000EA3, /* 43, -67dB */
+ 0x0000106C, /* 44, -66dB */ 0x0000126D, /* 45, -65dB */
+ 0x000014AD, /* 46, -64dB */ 0x00001733, /* 47, -63dB */
+ 0x00001A07, /* 48, -62dB */ 0x00001D34, /* 49, -61dB */
+ 0x000020C5, /* 50, -60dB */ 0x000024C4, /* 51, -59dB */
+ 0x00002941, /* 52, -58dB */ 0x00002E49, /* 53, -57dB */
+ 0x000033EF, /* 54, -56dB */ 0x00003A45, /* 55, -55dB */
+ 0x00004161, /* 56, -54dB */ 0x0000495C, /* 57, -53dB */
+ 0x0000524F, /* 58, -52dB */ 0x00005C5A, /* 59, -51dB */
+ 0x0000679F, /* 60, -50dB */ 0x00007444, /* 61, -49dB */
+ 0x00008274, /* 62, -48dB */ 0x0000925F, /* 63, -47dB */
+ 0x0000A43B, /* 64, -46dB */ 0x0000B845, /* 65, -45dB */
+ 0x0000CEC1, /* 66, -44dB */ 0x0000E7FB, /* 67, -43dB */
+ 0x00010449, /* 68, -42dB */ 0x0001240C, /* 69, -41dB */
+ 0x000147AE, /* 70, -40dB */ 0x00016FAA, /* 71, -39dB */
+ 0x00019C86, /* 72, -38dB */ 0x0001CEDC, /* 73, -37dB */
+ 0x00020756, /* 74, -36dB */ 0x000246B5, /* 75, -35dB */
+ 0x00028DCF, /* 76, -34dB */ 0x0002DD96, /* 77, -33dB */
+ 0x00033718, /* 78, -32dB */ 0x00039B87, /* 79, -31dB */
+ 0x00040C37, /* 80, -30dB */ 0x00048AA7, /* 81, -29dB */
+ 0x00051884, /* 82, -28dB */ 0x0005B7B1, /* 83, -27dB */
+ 0x00066A4A, /* 84, -26dB */ 0x000732AE, /* 85, -25dB */
+ 0x00081385, /* 86, -24dB */ 0x00090FCC, /* 87, -23dB */
+ 0x000A2ADB, /* 88, -22dB */ 0x000B6873, /* 89, -21dB */
+ 0x000CCCCD, /* 90, -20dB */ 0x000E5CA1, /* 91, -19dB */
+ 0x00101D3F, /* 92, -18dB */ 0x0012149A, /* 93, -17dB */
+ 0x00144961, /* 94, -16dB */ 0x0016C311, /* 95, -15dB */
+ 0x00198A13, /* 96, -14dB */ 0x001CA7D7, /* 97, -13dB */
+ 0x002026F3, /* 98, -12dB */ 0x00241347, /* 99, -11dB */
+ 0x00287A27, /* 100, -10dB */ 0x002D6A86, /* 101, -9dB */
+ 0x0032F52D, /* 102, -8dB */ 0x00392CEE, /* 103, -7dB */
+ 0x004026E7, /* 104, -6dB */ 0x0047FACD, /* 105, -5dB */
+ 0x0050C336, /* 106, -4dB */ 0x005A9DF8, /* 107, -3dB */
+ 0x0065AC8C, /* 108, -2dB */ 0x00721483, /* 109, -1dB */
+ 0x00800000, /* 110, 0dB */ 0x008F9E4D, /* 111, 1dB */
+ 0x00A12478, /* 112, 2dB */ 0x00B4CE08, /* 113, 3dB */
+ 0x00CADDC8, /* 114, 4dB */ 0x00E39EA9, /* 115, 5dB */
+ 0x00FF64C1, /* 116, 6dB */ 0x011E8E6A, /* 117, 7dB */
+ 0x0141857F, /* 118, 8dB */ 0x0168C0C6, /* 119, 9dB */
+ 0x0194C584, /* 120, 10dB */ 0x01C62940, /* 121, 11dB */
+ 0x01FD93C2, /* 122, 12dB */ 0x023BC148, /* 123, 13dB */
+ 0x02818508, /* 124, 14dB */ 0x02CFCC01, /* 125, 15dB */
+ 0x0327A01A, /* 126, 16dB */ 0x038A2BAD, /* 127, 17dB */
+ 0x03F8BD7A, /* 128, 18dB */ 0x0474CD1B, /* 129, 19dB */
+ 0x05000000, /* 130, 20dB */ 0x059C2F02, /* 131, 21dB */
+ 0x064B6CAE, /* 132, 22dB */ 0x07100C4D, /* 133, 23dB */
+ 0x07ECA9CD, /* 134, 24dB */ 0x08E43299, /* 135, 25dB */
+ 0x09F9EF8E, /* 136, 26dB */ 0x0B319025, /* 137, 27dB */
+ 0x0C8F36F2, /* 138, 28dB */ 0x0E1787B8, /* 139, 29dB */
+ 0x0FCFB725, /* 140, 30dB */ 0x11BD9C84, /* 141, 31dB */
+ 0x13E7C594, /* 142, 32dB */ 0x16558CCB, /* 143, 33dB */
+ 0x190F3254, /* 144, 34dB */ 0x1C1DF80E, /* 145, 35dB */
+ 0x1F8C4107, /* 146, 36dB */ 0x2365B4BF, /* 147, 37dB */
+ 0x27B766C2, /* 148, 38dB */ 0x2C900313, /* 149, 39dB */
+ 0x32000000, /* 150, 40dB */ 0x3819D612, /* 151, 41dB */
+ 0x3EF23ECA, /* 152, 42dB */ 0x46A07B07, /* 153, 43dB */
+ 0x4F3EA203, /* 154, 44dB */ 0x58E9F9F9, /* 155, 45dB */
+ 0x63C35B8E, /* 156, 46dB */ 0x6FEFA16D, /* 157, 47dB */
+ 0x7D982575, /* 158, 48dB */
+};
+
+#define TAS5805M_VOLUME_MAX ((int)ARRAY_SIZE(tas5805m_volume) - 1)
+#define TAS5805M_VOLUME_MIN 0
+
+struct tas5805m_priv {
+ struct i2c_client *i2c;
+ struct regulator *pvdd;
+ struct gpio_desc *gpio_pdn_n;
+
+ uint8_t *dsp_cfg_data;
+ int dsp_cfg_len;
+
+ struct regmap *regmap;
+
+ int vol[2];
+ bool is_powered;
+ bool is_muted;
+
+ struct work_struct work;
+ struct mutex lock;
+};
+
+static void set_dsp_scale(struct regmap *rm, int offset, int vol)
+{
+ uint8_t v[4];
+ uint32_t x = tas5805m_volume[vol];
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ v[3 - i] = x;
+ x >>= 8;
+ }
+
+ regmap_bulk_write(rm, offset, v, ARRAY_SIZE(v));
+}
+
+static void tas5805m_refresh(struct tas5805m_priv *tas5805m)
+{
+ struct regmap *rm = tas5805m->regmap;
+
+ dev_dbg(&tas5805m->i2c->dev, "refresh: is_muted=%d, vol=%d/%d\n",
+ tas5805m->is_muted, tas5805m->vol[0], tas5805m->vol[1]);
+
+ regmap_write(rm, REG_PAGE, 0x00);
+ regmap_write(rm, REG_BOOK, 0x8c);
+ regmap_write(rm, REG_PAGE, 0x2a);
+
+ /* Refresh volume. The actual volume control documented in the
+ * datasheet doesn't seem to work correctly. This is a pair of
+ * DSP registers which are *not* documented in the datasheet.
+ */
+ set_dsp_scale(rm, 0x24, tas5805m->vol[0]);
+ set_dsp_scale(rm, 0x28, tas5805m->vol[1]);
+
+ regmap_write(rm, REG_PAGE, 0x00);
+ regmap_write(rm, REG_BOOK, 0x00);
+
+ /* Set/clear digital soft-mute */
+ regmap_write(rm, REG_DEVICE_CTRL_2,
+ (tas5805m->is_muted ? DCTRL2_MUTE : 0) |
+ DCTRL2_MODE_PLAY);
+}
+
+static int tas5805m_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+
+ uinfo->value.integer.min = TAS5805M_VOLUME_MIN;
+ uinfo->value.integer.max = TAS5805M_VOLUME_MAX;
+ return 0;
+}
+
+static int tas5805m_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tas5805m_priv *tas5805m =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&tas5805m->lock);
+ ucontrol->value.integer.value[0] = tas5805m->vol[0];
+ ucontrol->value.integer.value[1] = tas5805m->vol[1];
+ mutex_unlock(&tas5805m->lock);
+
+ return 0;
+}
+
+static inline int volume_is_valid(int v)
+{
+ return (v >= TAS5805M_VOLUME_MIN) && (v <= TAS5805M_VOLUME_MAX);
+}
+
+static int tas5805m_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tas5805m_priv *tas5805m =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if (!(volume_is_valid(ucontrol->value.integer.value[0]) &&
+ volume_is_valid(ucontrol->value.integer.value[1])))
+ return -EINVAL;
+
+ mutex_lock(&tas5805m->lock);
+ if (tas5805m->vol[0] != ucontrol->value.integer.value[0] ||
+ tas5805m->vol[1] != ucontrol->value.integer.value[1]) {
+ tas5805m->vol[0] = ucontrol->value.integer.value[0];
+ tas5805m->vol[1] = ucontrol->value.integer.value[1];
+ dev_dbg(component->dev, "set vol=%d/%d (is_powered=%d)\n",
+ tas5805m->vol[0], tas5805m->vol[1],
+ tas5805m->is_powered);
+ if (tas5805m->is_powered)
+ tas5805m_refresh(tas5805m);
+ ret = 1;
+ }
+ mutex_unlock(&tas5805m->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new tas5805m_snd_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas5805m_vol_info,
+ .get = tas5805m_vol_get,
+ .put = tas5805m_vol_put,
+ },
+};
+
+static void send_cfg(struct regmap *rm,
+ const uint8_t *s, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i + 1 < len; i += 2)
+ regmap_write(rm, s[i], s[i + 1]);
+}
+
+/* The TAS5805M DSP can't be configured until the I2S clock has been
+ * present and stable for 5ms, or else it won't boot and we get no
+ * sound.
+ */
+static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas5805m_priv *tas5805m =
+ snd_soc_component_get_drvdata(component);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev_dbg(component->dev, "clock start\n");
+ schedule_work(&tas5805m->work);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void do_work(struct work_struct *work)
+{
+ struct tas5805m_priv *tas5805m =
+ container_of(work, struct tas5805m_priv, work);
+ struct regmap *rm = tas5805m->regmap;
+
+ dev_dbg(&tas5805m->i2c->dev, "DSP startup\n");
+
+ mutex_lock(&tas5805m->lock);
+ /* We mustn't issue any I2C transactions until the I2S
+ * clock is stable. Furthermore, we must allow a 5ms
+ * delay after the first set of register writes to
+ * allow the DSP to boot before configuring it.
+ */
+ usleep_range(5000, 10000);
+ send_cfg(rm, dsp_cfg_preboot, ARRAY_SIZE(dsp_cfg_preboot));
+ usleep_range(5000, 15000);
+ send_cfg(rm, tas5805m->dsp_cfg_data, tas5805m->dsp_cfg_len);
+
+ tas5805m->is_powered = true;
+ tas5805m_refresh(tas5805m);
+ mutex_unlock(&tas5805m->lock);
+}
+
+static int tas5805m_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas5805m_priv *tas5805m =
+ snd_soc_component_get_drvdata(component);
+ struct regmap *rm = tas5805m->regmap;
+
+ if (event & SND_SOC_DAPM_PRE_PMD) {
+ unsigned int chan, global1, global2;
+
+ dev_dbg(component->dev, "DSP shutdown\n");
+ cancel_work_sync(&tas5805m->work);
+
+ mutex_lock(&tas5805m->lock);
+ if (tas5805m->is_powered) {
+ tas5805m->is_powered = false;
+
+ regmap_write(rm, REG_PAGE, 0x00);
+ regmap_write(rm, REG_BOOK, 0x00);
+
+ regmap_read(rm, REG_CHAN_FAULT, &chan);
+ regmap_read(rm, REG_GLOBAL_FAULT1, &global1);
+ regmap_read(rm, REG_GLOBAL_FAULT2, &global2);
+
+ dev_dbg(component->dev, "fault regs: CHAN=%02x, "
+ "GLOBAL1=%02x, GLOBAL2=%02x\n",
+ chan, global1, global2);
+
+ regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ);
+ }
+ mutex_unlock(&tas5805m->lock);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route tas5805m_audio_map[] = {
+ { "DAC", NULL, "DAC IN" },
+ { "OUT", NULL, "DAC" },
+};
+
+static const struct snd_soc_dapm_widget tas5805m_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ tas5805m_dac_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_tas5805m = {
+ .controls = tas5805m_snd_controls,
+ .num_controls = ARRAY_SIZE(tas5805m_snd_controls),
+ .dapm_widgets = tas5805m_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5805m_dapm_widgets),
+ .dapm_routes = tas5805m_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas5805m_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas5805m_priv *tas5805m =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&tas5805m->lock);
+ dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n",
+ mute, tas5805m->is_powered);
+
+ tas5805m->is_muted = mute;
+ if (tas5805m->is_powered)
+ tas5805m_refresh(tas5805m);
+ mutex_unlock(&tas5805m->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tas5805m_dai_ops = {
+ .trigger = tas5805m_trigger,
+ .mute_stream = tas5805m_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver tas5805m_dai = {
+ .name = "tas5805m-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas5805m_dai_ops,
+};
+
+static const struct regmap_config tas5805m_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ /* We have quite a lot of multi-level bank switching and a
+ * relatively small number of register writes between bank
+ * switches.
+ */
+ .cache_type = REGCACHE_NONE,
+};
+
+static int tas5805m_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct regmap *regmap;
+ struct tas5805m_priv *tas5805m;
+ char filename[128];
+ const char *config_name;
+ const struct firmware *fw;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "unable to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ tas5805m = devm_kzalloc(dev, sizeof(*tas5805m), GFP_KERNEL);
+ if (!tas5805m)
+ return -ENOMEM;
+
+ tas5805m->i2c = i2c;
+ tas5805m->pvdd = devm_regulator_get(dev, "pvdd");
+ if (IS_ERR(tas5805m->pvdd)) {
+ dev_err(dev, "failed to get pvdd supply: %ld\n",
+ PTR_ERR(tas5805m->pvdd));
+ return PTR_ERR(tas5805m->pvdd);
+ }
+
+ dev_set_drvdata(dev, tas5805m);
+ tas5805m->regmap = regmap;
+ tas5805m->gpio_pdn_n = devm_gpiod_get(dev, "pdn", GPIOD_OUT_LOW);
+ if (IS_ERR(tas5805m->gpio_pdn_n)) {
+ dev_err(dev, "error requesting PDN gpio: %ld\n",
+ PTR_ERR(tas5805m->gpio_pdn_n));
+ return PTR_ERR(tas5805m->gpio_pdn_n);
+ }
+
+ /* This configuration must be generated by PPC3. The file loaded
+ * consists of a sequence of register writes, where bytes at
+ * even indices are register addresses and those at odd indices
+ * are register values.
+ *
+ * The fixed portion of PPC3's output prior to the 5ms delay
+ * should be omitted.
+ */
+ if (device_property_read_string(dev, "ti,dsp-config-name",
+ &config_name))
+ config_name = "default";
+
+ snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin",
+ config_name);
+ ret = request_firmware(&fw, filename, dev);
+ if (ret)
+ return ret;
+
+ if ((fw->size < 2) || (fw->size & 1)) {
+ dev_err(dev, "firmware is invalid\n");
+ release_firmware(fw);
+ return -EINVAL;
+ }
+
+ tas5805m->dsp_cfg_len = fw->size;
+ tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL);
+ if (!tas5805m->dsp_cfg_data) {
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ release_firmware(fw);
+
+ /* Do the first part of the power-on here, while we can expect
+ * the I2S interface to be quiet. We must raise PDN# and then
+ * wait 5ms before any I2S clock is sent, or else the internal
+ * regulator apparently won't come on.
+ *
+ * Also, we must keep the device in power down for 100ms or so
+ * after PVDD is applied, or else the ADR pin is sampled
+ * incorrectly and the device comes up with an unpredictable I2C
+ * address.
+ */
+ tas5805m->vol[0] = TAS5805M_VOLUME_MIN;
+ tas5805m->vol[1] = TAS5805M_VOLUME_MIN;
+
+ ret = regulator_enable(tas5805m->pvdd);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable pvdd: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(100000, 150000);
+ gpiod_set_value(tas5805m->gpio_pdn_n, 1);
+ usleep_range(10000, 15000);
+
+ INIT_WORK(&tas5805m->work, do_work);
+ mutex_init(&tas5805m->lock);
+
+ /* Don't register through devm. We need to be able to unregister
+ * the component prior to deasserting PDN#
+ */
+ ret = snd_soc_register_component(dev, &soc_codec_dev_tas5805m,
+ &tas5805m_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "unable to register codec: %d\n", ret);
+ gpiod_set_value(tas5805m->gpio_pdn_n, 0);
+ regulator_disable(tas5805m->pvdd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void tas5805m_i2c_remove(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct tas5805m_priv *tas5805m = dev_get_drvdata(dev);
+
+ cancel_work_sync(&tas5805m->work);
+ snd_soc_unregister_component(dev);
+ gpiod_set_value(tas5805m->gpio_pdn_n, 0);
+ usleep_range(10000, 15000);
+ regulator_disable(tas5805m->pvdd);
+}
+
+static const struct i2c_device_id tas5805m_i2c_id[] = {
+ { "tas5805m", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas5805m_of_match[] = {
+ { .compatible = "ti,tas5805m", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas5805m_of_match);
+#endif
+
+static struct i2c_driver tas5805m_i2c_driver = {
+ .probe = tas5805m_i2c_probe,
+ .remove = tas5805m_i2c_remove,
+ .id_table = tas5805m_i2c_id,
+ .driver = {
+ .name = "tas5805m",
+ .of_match_table = of_match_ptr(tas5805m_of_match),
+ },
+};
+
+module_i2c_driver(tas5805m_i2c_driver);
+
+MODULE_AUTHOR("Andy Liu <andy-liu@ti.com>");
+MODULE_AUTHOR("Daniel Beer <daniel.beer@igorinstitute.com>");
+MODULE_DESCRIPTION("TAS5805M Audio Amplifier Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
new file mode 100644
index 000000000000..85ecc246896f
--- /dev/null
+++ b/sound/soc/codecs/tas6424.c
@@ -0,0 +1,816 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/gpio/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 "tas6424.h"
+
+/* Define how often to check (and clear) the fault status register (in ms) */
+#define TAS6424_FAULT_CHECK_INTERVAL 200
+
+static const char * const tas6424_supply_names[] = {
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "vbat", /* Supply used for higher voltage analog circuits. */
+ "pvdd", /* Class-D amp output FETs supply. */
+};
+#define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names)
+
+struct tas6424_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES];
+ struct delayed_work fault_check_work;
+ unsigned int last_cfault;
+ unsigned int last_fault1;
+ unsigned int last_fault2;
+ unsigned int last_warn;
+ struct gpio_desc *standby_gpio;
+ struct gpio_desc *mute_gpio;
+};
+
+/*
+ * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
+ * setting the gain below -100 dB (register value <0x7) is effectively a MUTE
+ * as per device datasheet.
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
+
+static const struct snd_kcontrol_new tas6424_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume",
+ TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume",
+ TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume",
+ TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume",
+ TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_STROBE("Auto Diagnostics Switch", TAS6424_DC_DIAG_CTRL1,
+ TAS6424_LDGBYPASS_SHIFT, 1),
+};
+
+static int tas6424_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s() event=0x%0x\n", __func__, event);
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Observe codec shutdown-to-active time */
+ msleep(12);
+
+ /* Turn on TAS6424 periodic fault checking/handling */
+ tas6424->last_fault1 = 0;
+ tas6424->last_fault2 = 0;
+ tas6424->last_warn = 0;
+ schedule_delayed_work(&tas6424->fault_check_work,
+ msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
+ } else if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Disable TAS6424 periodic fault checking/handling */
+ cancel_delayed_work_sync(&tas6424->fault_check_work);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas6424_audio_map[] = {
+ { "DAC", NULL, "DAC IN" },
+ { "OUT", NULL, "DAC" },
+};
+
+static int tas6424_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int rate = params_rate(params);
+ unsigned int width = params_width(params);
+ u8 sap_ctrl = 0;
+
+ dev_dbg(component->dev, "%s() rate=%u width=%u\n", __func__, rate, width);
+
+ switch (rate) {
+ case 44100:
+ sap_ctrl |= TAS6424_SAP_RATE_44100;
+ break;
+ case 48000:
+ sap_ctrl |= TAS6424_SAP_RATE_48000;
+ break;
+ case 96000:
+ sap_ctrl |= TAS6424_SAP_RATE_96000;
+ break;
+ default:
+ dev_err(component->dev, "unsupported sample rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case 16:
+ sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16;
+ break;
+ case 24:
+ break;
+ default:
+ dev_err(component->dev, "unsupported sample width: %u\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, TAS6424_SAP_CTRL,
+ TAS6424_SAP_RATE_MASK |
+ TAS6424_SAP_TDM_SLOT_SZ_16,
+ sap_ctrl);
+
+ return 0;
+}
+
+static int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 serial_format = 0;
+
+ dev_dbg(component->dev, "%s() fmt=0x%0x\n", __func__, fmt);
+
+ /* clock masters */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clocking\n");
+ return -EINVAL;
+ }
+
+ /* signal polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clock signal polarity\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ serial_format |= TAS6424_SAP_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ serial_format |= TAS6424_SAP_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /*
+ * We can use the fact that the TAS6424 does not care about the
+ * LRCLK duty cycle during TDM to receive DSP_B formatted data
+ * in LEFTJ mode (no delaying of the 1st data bit).
+ */
+ serial_format |= TAS6424_SAP_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serial_format |= TAS6424_SAP_LEFTJ;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, TAS6424_SAP_CTRL,
+ TAS6424_SAP_FMT_MASK, serial_format);
+
+ return 0;
+}
+
+static int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int first_slot, last_slot;
+ bool sap_tdm_slot_last;
+
+ dev_dbg(component->dev, "%s() tx_mask=%d rx_mask=%d\n", __func__,
+ tx_mask, rx_mask);
+
+ if (!tx_mask || !rx_mask)
+ return 0; /* nothing needed to disable TDM mode */
+
+ /*
+ * Determine the first slot and last slot that is being requested so
+ * we'll be able to more easily enforce certain constraints as the
+ * TAS6424's TDM interface is not fully configurable.
+ */
+ first_slot = __ffs(tx_mask);
+ last_slot = __fls(rx_mask);
+
+ if (last_slot - first_slot != 4) {
+ dev_err(component->dev, "tdm mask must cover 4 contiguous slots\n");
+ return -EINVAL;
+ }
+
+ switch (first_slot) {
+ case 0:
+ sap_tdm_slot_last = false;
+ break;
+ case 4:
+ sap_tdm_slot_last = true;
+ break;
+ default:
+ dev_err(component->dev, "tdm mask must start at slot 0 or 4\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST,
+ sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0);
+
+ return 0;
+}
+
+static int tas6424_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ dev_dbg(component->dev, "%s() mute=%d\n", __func__, mute);
+
+ if (tas6424->mute_gpio) {
+ gpiod_set_value_cansleep(tas6424->mute_gpio, mute);
+ return 0;
+ }
+
+ if (mute)
+ val = TAS6424_ALL_STATE_MUTE;
+ else
+ val = TAS6424_ALL_STATE_PLAY;
+
+ snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, val);
+
+ return 0;
+}
+
+static int tas6424_power_off(struct snd_soc_component *component)
+{
+ struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ);
+
+ regcache_cache_only(tas6424->regmap, true);
+ regcache_mark_dirty(tas6424->regmap);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas6424_power_on(struct snd_soc_component *component)
+{
+ struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u8 chan_states;
+ int no_auto_diags = 0;
+ unsigned int reg_val;
+
+ if (!regmap_read(tas6424->regmap, TAS6424_DC_DIAG_CTRL1, &reg_val))
+ no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret < 0) {
+ dev_err(component->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(tas6424->regmap, false);
+
+ ret = regcache_sync(tas6424->regmap);
+ if (ret < 0) {
+ dev_err(component->dev, "failed to sync regcache: %d\n", ret);
+ return ret;
+ }
+
+ if (tas6424->mute_gpio) {
+ gpiod_set_value_cansleep(tas6424->mute_gpio, 0);
+ /*
+ * channels are muted via the mute pin. Don't also mute
+ * them via the registers so that subsequent register
+ * access is not necessary to un-mute the channels
+ */
+ chan_states = TAS6424_ALL_STATE_PLAY;
+ } else {
+ chan_states = TAS6424_ALL_STATE_MUTE;
+ }
+ snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, chan_states);
+
+ /* any time we come out of HIZ, the output channels automatically run DC
+ * load diagnostics if autodiagnotics are enabled. wait here until this
+ * completes.
+ */
+ if (!no_auto_diags)
+ msleep(230);
+
+ return 0;
+}
+
+static int tas6424_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ dev_dbg(component->dev, "%s() level=%d\n", __func__, level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ tas6424_power_on(component);
+ break;
+ case SND_SOC_BIAS_OFF:
+ tas6424_power_off(component);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_tas6424 = {
+ .set_bias_level = tas6424_set_bias_level,
+ .controls = tas6424_snd_controls,
+ .num_controls = ARRAY_SIZE(tas6424_snd_controls),
+ .dapm_widgets = tas6424_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas6424_dapm_widgets),
+ .dapm_routes = tas6424_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas6424_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops tas6424_speaker_dai_ops = {
+ .hw_params = tas6424_hw_params,
+ .set_fmt = tas6424_set_dai_fmt,
+ .set_tdm_slot = tas6424_set_dai_tdm_slot,
+ .mute_stream = tas6424_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver tas6424_dai[] = {
+ {
+ .name = "tas6424-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS6424_RATES,
+ .formats = TAS6424_FORMATS,
+ },
+ .ops = &tas6424_speaker_dai_ops,
+ },
+};
+
+static void tas6424_fault_check_work(struct work_struct *work)
+{
+ struct tas6424_data *tas6424 = container_of(work, struct tas6424_data,
+ fault_check_work.work);
+ struct device *dev = tas6424->dev;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret);
+ goto out;
+ }
+
+ if (!reg) {
+ tas6424->last_cfault = reg;
+ goto check_global_fault1_reg;
+ }
+
+ /*
+ * Only flag errors once for a given occurrence. This is needed as
+ * the TAS6424 will take time clearing the fault condition internally
+ * during which we don't want to bombard the system with the same
+ * error message over and over.
+ */
+ if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1))
+ dev_crit(dev, "experienced a channel 1 overcurrent fault\n");
+
+ if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2))
+ dev_crit(dev, "experienced a channel 2 overcurrent fault\n");
+
+ if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3))
+ dev_crit(dev, "experienced a channel 3 overcurrent fault\n");
+
+ if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4))
+ dev_crit(dev, "experienced a channel 4 overcurrent fault\n");
+
+ if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1))
+ dev_crit(dev, "experienced a channel 1 DC fault\n");
+
+ if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2))
+ dev_crit(dev, "experienced a channel 2 DC fault\n");
+
+ if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3))
+ dev_crit(dev, "experienced a channel 3 DC fault\n");
+
+ if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4))
+ dev_crit(dev, "experienced a channel 4 DC fault\n");
+
+ /* Store current fault1 value so we can detect any changes next time */
+ tas6424->last_cfault = reg;
+
+check_global_fault1_reg:
+ ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * Ignore any clock faults as there is no clean way to check for them.
+ * We would need to start checking for those faults *after* the SAIF
+ * stream has been setup, and stop checking *before* the stream is
+ * stopped to avoid any false-positives. However there are no
+ * appropriate hooks to monitor these events.
+ */
+ reg &= TAS6424_FAULT_PVDD_OV |
+ TAS6424_FAULT_VBAT_OV |
+ TAS6424_FAULT_PVDD_UV |
+ TAS6424_FAULT_VBAT_UV;
+
+ if (!reg) {
+ tas6424->last_fault1 = reg;
+ goto check_global_fault2_reg;
+ }
+
+ if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV))
+ dev_crit(dev, "experienced a PVDD overvoltage fault\n");
+
+ if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV))
+ dev_crit(dev, "experienced a VBAT overvoltage fault\n");
+
+ if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV))
+ dev_crit(dev, "experienced a PVDD undervoltage fault\n");
+
+ if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV))
+ dev_crit(dev, "experienced a VBAT undervoltage fault\n");
+
+ /* Store current fault1 value so we can detect any changes next time */
+ tas6424->last_fault1 = reg;
+
+check_global_fault2_reg:
+ ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret);
+ goto out;
+ }
+
+ reg &= TAS6424_FAULT_OTSD |
+ TAS6424_FAULT_OTSD_CH1 |
+ TAS6424_FAULT_OTSD_CH2 |
+ TAS6424_FAULT_OTSD_CH3 |
+ TAS6424_FAULT_OTSD_CH4;
+
+ if (!reg) {
+ tas6424->last_fault2 = reg;
+ goto check_warn_reg;
+ }
+
+ if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD))
+ dev_crit(dev, "experienced a global overtemp shutdown\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1))
+ dev_crit(dev, "experienced an overtemp shutdown on CH1\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2))
+ dev_crit(dev, "experienced an overtemp shutdown on CH2\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3))
+ dev_crit(dev, "experienced an overtemp shutdown on CH3\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4))
+ dev_crit(dev, "experienced an overtemp shutdown on CH4\n");
+
+ /* Store current fault2 value so we can detect any changes next time */
+ tas6424->last_fault2 = reg;
+
+check_warn_reg:
+ ret = regmap_read(tas6424->regmap, TAS6424_WARN, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to read WARN register: %d\n", ret);
+ goto out;
+ }
+
+ reg &= TAS6424_WARN_VDD_UV |
+ TAS6424_WARN_VDD_POR |
+ TAS6424_WARN_VDD_OTW |
+ TAS6424_WARN_VDD_OTW_CH1 |
+ TAS6424_WARN_VDD_OTW_CH2 |
+ TAS6424_WARN_VDD_OTW_CH3 |
+ TAS6424_WARN_VDD_OTW_CH4;
+
+ if (!reg) {
+ tas6424->last_warn = reg;
+ goto out;
+ }
+
+ if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV))
+ dev_warn(dev, "experienced a VDD under voltage condition\n");
+
+ if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR))
+ dev_warn(dev, "experienced a VDD POR condition\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW))
+ dev_warn(dev, "experienced a global overtemp warning\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1))
+ dev_warn(dev, "experienced an overtemp warning on CH1\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2))
+ dev_warn(dev, "experienced an overtemp warning on CH2\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3))
+ dev_warn(dev, "experienced an overtemp warning on CH3\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4))
+ dev_warn(dev, "experienced an overtemp warning on CH4\n");
+
+ /* Store current warn value so we can detect any changes next time */
+ tas6424->last_warn = reg;
+
+ /* Clear any warnings by toggling the CLEAR_FAULT control bit */
+ ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
+ TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT);
+ if (ret < 0)
+ dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
+
+ ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
+ TAS6424_CLEAR_FAULT, 0);
+ if (ret < 0)
+ dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
+
+out:
+ /* Schedule the next fault check at the specified interval */
+ schedule_delayed_work(&tas6424->fault_check_work,
+ msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
+}
+
+static const struct reg_default tas6424_reg_defaults[] = {
+ { TAS6424_MODE_CTRL, 0x00 },
+ { TAS6424_MISC_CTRL1, 0x32 },
+ { TAS6424_MISC_CTRL2, 0x62 },
+ { TAS6424_SAP_CTRL, 0x04 },
+ { TAS6424_CH_STATE_CTRL, 0x55 },
+ { TAS6424_CH1_VOL_CTRL, 0xcf },
+ { TAS6424_CH2_VOL_CTRL, 0xcf },
+ { TAS6424_CH3_VOL_CTRL, 0xcf },
+ { TAS6424_CH4_VOL_CTRL, 0xcf },
+ { TAS6424_DC_DIAG_CTRL1, 0x00 },
+ { TAS6424_DC_DIAG_CTRL2, 0x11 },
+ { TAS6424_DC_DIAG_CTRL3, 0x11 },
+ { TAS6424_PIN_CTRL, 0xff },
+ { TAS6424_AC_DIAG_CTRL1, 0x00 },
+ { TAS6424_MISC_CTRL3, 0x00 },
+ { TAS6424_CLIP_CTRL, 0x01 },
+ { TAS6424_CLIP_WINDOW, 0x14 },
+ { TAS6424_CLIP_WARN, 0x00 },
+ { TAS6424_CBC_STAT, 0x00 },
+ { TAS6424_MISC_CTRL4, 0x40 },
+};
+
+static bool tas6424_is_writable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS6424_MODE_CTRL:
+ case TAS6424_MISC_CTRL1:
+ case TAS6424_MISC_CTRL2:
+ case TAS6424_SAP_CTRL:
+ case TAS6424_CH_STATE_CTRL:
+ case TAS6424_CH1_VOL_CTRL:
+ case TAS6424_CH2_VOL_CTRL:
+ case TAS6424_CH3_VOL_CTRL:
+ case TAS6424_CH4_VOL_CTRL:
+ case TAS6424_DC_DIAG_CTRL1:
+ case TAS6424_DC_DIAG_CTRL2:
+ case TAS6424_DC_DIAG_CTRL3:
+ case TAS6424_PIN_CTRL:
+ case TAS6424_AC_DIAG_CTRL1:
+ case TAS6424_MISC_CTRL3:
+ case TAS6424_CLIP_CTRL:
+ case TAS6424_CLIP_WINDOW:
+ case TAS6424_CLIP_WARN:
+ case TAS6424_CBC_STAT:
+ case TAS6424_MISC_CTRL4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS6424_DC_LOAD_DIAG_REP12:
+ case TAS6424_DC_LOAD_DIAG_REP34:
+ case TAS6424_DC_LOAD_DIAG_REPLO:
+ case TAS6424_CHANNEL_STATE:
+ case TAS6424_CHANNEL_FAULT:
+ case TAS6424_GLOB_FAULT1:
+ case TAS6424_GLOB_FAULT2:
+ case TAS6424_WARN:
+ case TAS6424_AC_LOAD_DIAG_REP1:
+ case TAS6424_AC_LOAD_DIAG_REP2:
+ case TAS6424_AC_LOAD_DIAG_REP3:
+ case TAS6424_AC_LOAD_DIAG_REP4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas6424_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .writeable_reg = tas6424_is_writable_reg,
+ .volatile_reg = tas6424_is_volatile_reg,
+
+ .max_register = TAS6424_MAX,
+ .reg_defaults = tas6424_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas6424_of_ids[] = {
+ { .compatible = "ti,tas6424", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tas6424_of_ids);
+#endif
+
+static int tas6424_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tas6424_data *tas6424;
+ int ret;
+ int i;
+
+ tas6424 = devm_kzalloc(dev, sizeof(*tas6424), GFP_KERNEL);
+ if (!tas6424)
+ return -ENOMEM;
+ dev_set_drvdata(dev, tas6424);
+
+ tas6424->dev = dev;
+
+ tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config);
+ if (IS_ERR(tas6424->regmap)) {
+ ret = PTR_ERR(tas6424->regmap);
+ dev_err(dev, "unable to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Get control of the standby pin and set it LOW to take the codec
+ * out of the stand-by mode.
+ * Note: The actual pin polarity is taken care of in the GPIO lib
+ * according the polarity specified in the DTS.
+ */
+ tas6424->standby_gpio = devm_gpiod_get_optional(dev, "standby",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(tas6424->standby_gpio)) {
+ if (PTR_ERR(tas6424->standby_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_info(dev, "failed to get standby GPIO: %ld\n",
+ PTR_ERR(tas6424->standby_gpio));
+ tas6424->standby_gpio = NULL;
+ }
+
+ /*
+ * Get control of the mute pin and set it HIGH in order to start with
+ * all the output muted.
+ * Note: The actual pin polarity is taken care of in the GPIO lib
+ * according the polarity specified in the DTS.
+ */
+ tas6424->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas6424->mute_gpio)) {
+ if (PTR_ERR(tas6424->mute_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_info(dev, "failed to get nmute GPIO: %ld\n",
+ PTR_ERR(tas6424->mute_gpio));
+ tas6424->mute_gpio = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++)
+ tas6424->supplies[i].supply = tas6424_supply_names[i];
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret) {
+ dev_err(dev, "unable to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret) {
+ dev_err(dev, "unable to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset device to establish well-defined startup state */
+ ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL,
+ TAS6424_RESET, TAS6424_RESET);
+ if (ret) {
+ dev_err(dev, "unable to reset device: %d\n", ret);
+ goto disable_regs;
+ }
+
+ INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work);
+
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_tas6424,
+ tas6424_dai, ARRAY_SIZE(tas6424_dai));
+ if (ret < 0) {
+ dev_err(dev, "unable to register codec: %d\n", ret);
+ goto disable_regs;
+ }
+
+ return 0;
+
+disable_regs:
+ regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies);
+ return ret;
+}
+
+static void tas6424_i2c_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tas6424_data *tas6424 = dev_get_drvdata(dev);
+ int ret;
+
+ cancel_delayed_work_sync(&tas6424->fault_check_work);
+
+ /* put the codec in stand-by */
+ if (tas6424->standby_gpio)
+ gpiod_set_value_cansleep(tas6424->standby_gpio, 1);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret < 0)
+ dev_err(dev, "unable to disable supplies: %d\n", ret);
+}
+
+static const struct i2c_device_id tas6424_i2c_ids[] = {
+ { "tas6424" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids);
+
+static struct i2c_driver tas6424_i2c_driver = {
+ .driver = {
+ .name = "tas6424",
+ .of_match_table = of_match_ptr(tas6424_of_ids),
+ },
+ .probe = tas6424_i2c_probe,
+ .remove = tas6424_i2c_remove,
+ .id_table = tas6424_i2c_ids,
+};
+module_i2c_driver(tas6424_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TAS6424 Audio amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h
new file mode 100644
index 000000000000..a6a0d00e5190
--- /dev/null
+++ b/sound/soc/codecs/tas6424.h
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef __TAS6424_H__
+#define __TAS6424_H__
+
+#define TAS6424_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define TAS6424_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* Register Address Map */
+#define TAS6424_MODE_CTRL 0x00
+#define TAS6424_MISC_CTRL1 0x01
+#define TAS6424_MISC_CTRL2 0x02
+#define TAS6424_SAP_CTRL 0x03
+#define TAS6424_CH_STATE_CTRL 0x04
+#define TAS6424_CH1_VOL_CTRL 0x05
+#define TAS6424_CH2_VOL_CTRL 0x06
+#define TAS6424_CH3_VOL_CTRL 0x07
+#define TAS6424_CH4_VOL_CTRL 0x08
+#define TAS6424_DC_DIAG_CTRL1 0x09
+#define TAS6424_DC_DIAG_CTRL2 0x0a
+#define TAS6424_DC_DIAG_CTRL3 0x0b
+#define TAS6424_DC_LOAD_DIAG_REP12 0x0c
+#define TAS6424_DC_LOAD_DIAG_REP34 0x0d
+#define TAS6424_DC_LOAD_DIAG_REPLO 0x0e
+#define TAS6424_CHANNEL_STATE 0x0f
+#define TAS6424_CHANNEL_FAULT 0x10
+#define TAS6424_GLOB_FAULT1 0x11
+#define TAS6424_GLOB_FAULT2 0x12
+#define TAS6424_WARN 0x13
+#define TAS6424_PIN_CTRL 0x14
+#define TAS6424_AC_DIAG_CTRL1 0x15
+#define TAS6424_AC_DIAG_CTRL2 0x16
+#define TAS6424_AC_LOAD_DIAG_REP1 0x17
+#define TAS6424_AC_LOAD_DIAG_REP2 0x18
+#define TAS6424_AC_LOAD_DIAG_REP3 0x19
+#define TAS6424_AC_LOAD_DIAG_REP4 0x1a
+#define TAS6424_MISC_CTRL3 0x21
+#define TAS6424_CLIP_CTRL 0x22
+#define TAS6424_CLIP_WINDOW 0x23
+#define TAS6424_CLIP_WARN 0x24
+#define TAS6424_CBC_STAT 0x25
+#define TAS6424_MISC_CTRL4 0x26
+#define TAS6424_MAX TAS6424_MISC_CTRL4
+
+/* TAS6424_MODE_CTRL_REG */
+#define TAS6424_RESET BIT(7)
+
+/* TAS6424_SAP_CTRL_REG */
+#define TAS6424_SAP_RATE_MASK GENMASK(7, 6)
+#define TAS6424_SAP_RATE_44100 (0x00 << 6)
+#define TAS6424_SAP_RATE_48000 (0x01 << 6)
+#define TAS6424_SAP_RATE_96000 (0x02 << 6)
+#define TAS6424_SAP_TDM_SLOT_LAST BIT(5)
+#define TAS6424_SAP_TDM_SLOT_SZ_16 BIT(4)
+#define TAS6424_SAP_TDM_SLOT_SWAP BIT(3)
+#define TAS6424_SAP_FMT_MASK GENMASK(2, 0)
+#define TAS6424_SAP_RIGHTJ_24 (0x00 << 0)
+#define TAS6424_SAP_RIGHTJ_20 (0x01 << 0)
+#define TAS6424_SAP_RIGHTJ_18 (0x02 << 0)
+#define TAS6424_SAP_RIGHTJ_16 (0x03 << 0)
+#define TAS6424_SAP_I2S (0x04 << 0)
+#define TAS6424_SAP_LEFTJ (0x05 << 0)
+#define TAS6424_SAP_DSP (0x06 << 0)
+
+/* TAS6424_CH_STATE_CTRL_REG */
+#define TAS6424_CH1_STATE_MASK GENMASK(7, 6)
+#define TAS6424_CH1_STATE_PLAY (0x00 << 6)
+#define TAS6424_CH1_STATE_HIZ (0x01 << 6)
+#define TAS6424_CH1_STATE_MUTE (0x02 << 6)
+#define TAS6424_CH1_STATE_DIAG (0x03 << 6)
+#define TAS6424_CH2_STATE_MASK GENMASK(5, 4)
+#define TAS6424_CH2_STATE_PLAY (0x00 << 4)
+#define TAS6424_CH2_STATE_HIZ (0x01 << 4)
+#define TAS6424_CH2_STATE_MUTE (0x02 << 4)
+#define TAS6424_CH2_STATE_DIAG (0x03 << 4)
+#define TAS6424_CH3_STATE_MASK GENMASK(3, 2)
+#define TAS6424_CH3_STATE_PLAY (0x00 << 2)
+#define TAS6424_CH3_STATE_HIZ (0x01 << 2)
+#define TAS6424_CH3_STATE_MUTE (0x02 << 2)
+#define TAS6424_CH3_STATE_DIAG (0x03 << 2)
+#define TAS6424_CH4_STATE_MASK GENMASK(1, 0)
+#define TAS6424_CH4_STATE_PLAY (0x00 << 0)
+#define TAS6424_CH4_STATE_HIZ (0x01 << 0)
+#define TAS6424_CH4_STATE_MUTE (0x02 << 0)
+#define TAS6424_CH4_STATE_DIAG (0x03 << 0)
+#define TAS6424_ALL_STATE_PLAY (TAS6424_CH1_STATE_PLAY | \
+ TAS6424_CH2_STATE_PLAY | \
+ TAS6424_CH3_STATE_PLAY | \
+ TAS6424_CH4_STATE_PLAY)
+#define TAS6424_ALL_STATE_HIZ (TAS6424_CH1_STATE_HIZ | \
+ TAS6424_CH2_STATE_HIZ | \
+ TAS6424_CH3_STATE_HIZ | \
+ TAS6424_CH4_STATE_HIZ)
+#define TAS6424_ALL_STATE_MUTE (TAS6424_CH1_STATE_MUTE | \
+ TAS6424_CH2_STATE_MUTE | \
+ TAS6424_CH3_STATE_MUTE | \
+ TAS6424_CH4_STATE_MUTE)
+#define TAS6424_ALL_STATE_DIAG (TAS6424_CH1_STATE_DIAG | \
+ TAS6424_CH2_STATE_DIAG | \
+ TAS6424_CH3_STATE_DIAG | \
+ TAS6424_CH4_STATE_DIAG)
+
+/* TAS6424_DC_DIAG_CTRL1 */
+#define TAS6424_LDGBYPASS_SHIFT 0
+#define TAS6424_LDGBYPASS_MASK BIT(TAS6424_LDGBYPASS_SHIFT)
+
+/* TAS6424_GLOB_FAULT1_REG */
+#define TAS6424_FAULT_OC_CH1 BIT(7)
+#define TAS6424_FAULT_OC_CH2 BIT(6)
+#define TAS6424_FAULT_OC_CH3 BIT(5)
+#define TAS6424_FAULT_OC_CH4 BIT(4)
+#define TAS6424_FAULT_DC_CH1 BIT(3)
+#define TAS6424_FAULT_DC_CH2 BIT(2)
+#define TAS6424_FAULT_DC_CH3 BIT(1)
+#define TAS6424_FAULT_DC_CH4 BIT(0)
+
+/* TAS6424_GLOB_FAULT1_REG */
+#define TAS6424_FAULT_CLOCK BIT(4)
+#define TAS6424_FAULT_PVDD_OV BIT(3)
+#define TAS6424_FAULT_VBAT_OV BIT(2)
+#define TAS6424_FAULT_PVDD_UV BIT(1)
+#define TAS6424_FAULT_VBAT_UV BIT(0)
+
+/* TAS6424_GLOB_FAULT2_REG */
+#define TAS6424_FAULT_OTSD BIT(4)
+#define TAS6424_FAULT_OTSD_CH1 BIT(3)
+#define TAS6424_FAULT_OTSD_CH2 BIT(2)
+#define TAS6424_FAULT_OTSD_CH3 BIT(1)
+#define TAS6424_FAULT_OTSD_CH4 BIT(0)
+
+/* TAS6424_WARN_REG */
+#define TAS6424_WARN_VDD_UV BIT(6)
+#define TAS6424_WARN_VDD_POR BIT(5)
+#define TAS6424_WARN_VDD_OTW BIT(4)
+#define TAS6424_WARN_VDD_OTW_CH1 BIT(3)
+#define TAS6424_WARN_VDD_OTW_CH2 BIT(2)
+#define TAS6424_WARN_VDD_OTW_CH3 BIT(1)
+#define TAS6424_WARN_VDD_OTW_CH4 BIT(0)
+
+/* TAS6424_MISC_CTRL3_REG */
+#define TAS6424_CLEAR_FAULT BIT(7)
+#define TAS6424_PBTL_CH_SEL BIT(6)
+#define TAS6424_MASK_CBC_WARN BIT(5)
+#define TAS6424_MASK_VDD_UV BIT(4)
+#define TAS6424_OTSD_AUTO_RECOVERY BIT(3)
+
+#endif /* __TAS6424_H__ */
diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c
new file mode 100644
index 000000000000..7d6fcba9986e
--- /dev/null
+++ b/sound/soc/codecs/tda7419.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TDA7419 audio processor driver
+ *
+ * Copyright 2018 Konsulko Group
+ *
+ * Author: Matt Porter <mporter@konsulko.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define TDA7419_MAIN_SRC_REG 0x00
+#define TDA7419_LOUDNESS_REG 0x01
+#define TDA7419_MUTE_CLK_REG 0x02
+#define TDA7419_VOLUME_REG 0x03
+#define TDA7419_TREBLE_REG 0x04
+#define TDA7419_MIDDLE_REG 0x05
+#define TDA7419_BASS_REG 0x06
+#define TDA7419_SECOND_SRC_REG 0x07
+#define TDA7419_SUB_MID_BASS_REG 0x08
+#define TDA7419_MIXING_GAIN_REG 0x09
+#define TDA7419_ATTENUATOR_LF_REG 0x0a
+#define TDA7419_ATTENUATOR_RF_REG 0x0b
+#define TDA7419_ATTENUATOR_LR_REG 0x0c
+#define TDA7419_ATTENUATOR_RR_REG 0x0d
+#define TDA7419_MIXING_LEVEL_REG 0x0e
+#define TDA7419_ATTENUATOR_SUB_REG 0x0f
+#define TDA7419_SA_CLK_AC_REG 0x10
+#define TDA7419_TESTING_REG 0x11
+
+#define TDA7419_MAIN_SRC_SEL 0
+#define TDA7419_MAIN_SRC_GAIN 3
+#define TDA7419_MAIN_SRC_AUTOZERO 7
+
+#define TDA7419_LOUDNESS_ATTEN 0
+#define TDA7419_LOUDNESS_CENTER_FREQ 4
+#define TDA7419_LOUDNESS_BOOST 6
+#define TDA7419_LOUDNESS_SOFT_STEP 7
+
+#define TDA7419_VOLUME_SOFT_STEP 7
+
+#define TDA7419_SOFT_MUTE 0
+#define TDA7419_MUTE_INFLUENCE 1
+#define TDA7419_SOFT_MUTE_TIME 2
+#define TDA7419_SOFT_STEP_TIME 4
+#define TDA7419_CLK_FAST_MODE 7
+
+#define TDA7419_TREBLE_CENTER_FREQ 5
+#define TDA7419_REF_OUT_SELECT 7
+
+#define TDA7419_MIDDLE_Q_FACTOR 5
+#define TDA7419_MIDDLE_SOFT_STEP 7
+
+#define TDA7419_BASS_Q_FACTOR 5
+#define TDA7419_BASS_SOFT_STEP 7
+
+#define TDA7419_SECOND_SRC_SEL 0
+#define TDA7419_SECOND_SRC_GAIN 3
+#define TDA7419_REAR_SPKR_SRC 7
+
+#define TDA7419_SUB_CUT_OFF_FREQ 0
+#define TDA7419_MIDDLE_CENTER_FREQ 2
+#define TDA7419_BASS_CENTER_FREQ 4
+#define TDA7419_BASS_DC_MODE 6
+#define TDA7419_SMOOTHING_FILTER 7
+
+#define TDA7419_MIX_LF 0
+#define TDA7419_MIX_RF 1
+#define TDA7419_MIX_ENABLE 2
+#define TDA7419_SUB_ENABLE 3
+#define TDA7419_HPF_GAIN 4
+
+#define TDA7419_SA_Q_FACTOR 0
+#define TDA7419_RESET_MODE 1
+#define TDA7419_SA_SOURCE 2
+#define TDA7419_SA_RUN 3
+#define TDA7419_RESET 4
+#define TDA7419_CLK_SOURCE 5
+#define TDA7419_COUPLING_MODE 6
+
+struct tda7419_data {
+ struct regmap *regmap;
+};
+
+static bool tda7419_readable_reg(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct reg_default tda7419_regmap_defaults[] = {
+ { TDA7419_MAIN_SRC_REG, 0xfe },
+ { TDA7419_LOUDNESS_REG, 0xfe },
+ { TDA7419_MUTE_CLK_REG, 0xfe },
+ { TDA7419_VOLUME_REG, 0xfe },
+ { TDA7419_TREBLE_REG, 0xfe },
+ { TDA7419_MIDDLE_REG, 0xfe },
+ { TDA7419_BASS_REG, 0xfe },
+ { TDA7419_SECOND_SRC_REG, 0xfe },
+ { TDA7419_SUB_MID_BASS_REG, 0xfe },
+ { TDA7419_MIXING_GAIN_REG, 0xfe },
+ { TDA7419_ATTENUATOR_LF_REG, 0xfe },
+ { TDA7419_ATTENUATOR_RF_REG, 0xfe },
+ { TDA7419_ATTENUATOR_LR_REG, 0xfe },
+ { TDA7419_ATTENUATOR_RR_REG, 0xfe },
+ { TDA7419_MIXING_LEVEL_REG, 0xfe },
+ { TDA7419_ATTENUATOR_SUB_REG, 0xfe },
+ { TDA7419_SA_CLK_AC_REG, 0xfe },
+ { TDA7419_TESTING_REG, 0xfe },
+};
+
+static const struct regmap_config tda7419_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TDA7419_TESTING_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .readable_reg = tda7419_readable_reg,
+ .reg_defaults = tda7419_regmap_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tda7419_regmap_defaults),
+};
+
+struct tda7419_vol_control {
+ int min, max;
+ unsigned int reg, rreg, mask, thresh;
+ unsigned int invert:1;
+};
+
+static inline bool tda7419_vol_is_stereo(struct tda7419_vol_control *tvc)
+{
+ if (tvc->reg == tvc->rreg)
+ return false;
+
+ return true;
+}
+
+static int tda7419_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tda7419_vol_control *tvc =
+ (struct tda7419_vol_control *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = tda7419_vol_is_stereo(tvc) ? 2 : 1;
+ uinfo->value.integer.min = tvc->min;
+ uinfo->value.integer.max = tvc->max;
+
+ return 0;
+}
+
+static inline int tda7419_vol_get_value(int val, unsigned int mask,
+ int min, int thresh,
+ unsigned int invert)
+{
+ val &= mask;
+ if (val < thresh) {
+ if (invert)
+ val = 0 - val;
+ } else if (val > thresh) {
+ if (invert)
+ val = val - thresh;
+ else
+ val = thresh - val;
+ }
+
+ if (val < min)
+ val = min;
+
+ return val;
+}
+
+static int tda7419_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tda7419_vol_control *tvc =
+ (struct tda7419_vol_control *)kcontrol->private_value;
+ unsigned int reg = tvc->reg;
+ unsigned int rreg = tvc->rreg;
+ unsigned int mask = tvc->mask;
+ int min = tvc->min;
+ int thresh = tvc->thresh;
+ unsigned int invert = tvc->invert;
+ int val;
+
+ val = snd_soc_component_read(component, reg);
+ ucontrol->value.integer.value[0] =
+ tda7419_vol_get_value(val, mask, min, thresh, invert);
+
+ if (tda7419_vol_is_stereo(tvc)) {
+ val = snd_soc_component_read(component, rreg);
+ ucontrol->value.integer.value[1] =
+ tda7419_vol_get_value(val, mask, min, thresh, invert);
+ }
+
+ return 0;
+}
+
+static inline int tda7419_vol_put_value(int val, int thresh,
+ unsigned int invert)
+{
+ if (val < 0) {
+ if (invert)
+ val = abs(val);
+ else
+ val = thresh - val;
+ } else if ((val > 0) && invert) {
+ val += thresh;
+ }
+
+ return val;
+}
+
+static int tda7419_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_kcontrol_chip(kcontrol);
+ struct tda7419_vol_control *tvc =
+ (struct tda7419_vol_control *)kcontrol->private_value;
+ unsigned int reg = tvc->reg;
+ unsigned int rreg = tvc->rreg;
+ unsigned int mask = tvc->mask;
+ int thresh = tvc->thresh;
+ unsigned int invert = tvc->invert;
+ int val;
+ int ret;
+
+ val = tda7419_vol_put_value(ucontrol->value.integer.value[0],
+ thresh, invert);
+ ret = snd_soc_component_update_bits(component, reg,
+ mask, val);
+ if (ret < 0)
+ return ret;
+
+ if (tda7419_vol_is_stereo(tvc)) {
+ val = tda7419_vol_put_value(ucontrol->value.integer.value[1],
+ thresh, invert);
+ ret = snd_soc_component_update_bits(component, rreg,
+ mask, val);
+ }
+
+ return ret;
+}
+
+#define TDA7419_SINGLE_VALUE(xreg, xmask, xmin, xmax, xthresh, xinvert) \
+ ((unsigned long)&(struct tda7419_vol_control) \
+ {.reg = xreg, .rreg = xreg, .mask = xmask, .min = xmin, \
+ .max = xmax, .thresh = xthresh, .invert = xinvert})
+
+#define TDA7419_DOUBLE_R_VALUE(xregl, xregr, xmask, xmin, xmax, xthresh, \
+ xinvert) \
+ ((unsigned long)&(struct tda7419_vol_control) \
+ {.reg = xregl, .rreg = xregr, .mask = xmask, .min = xmin, \
+ .max = xmax, .thresh = xthresh, .invert = xinvert})
+
+#define TDA7419_SINGLE_TLV(xname, xreg, xmask, xmin, xmax, xthresh, \
+ xinvert, xtlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (xtlv_array), \
+ .info = tda7419_vol_info, \
+ .get = tda7419_vol_get, \
+ .put = tda7419_vol_put, \
+ .private_value = TDA7419_SINGLE_VALUE(xreg, xmask, xmin, \
+ xmax, xthresh, xinvert), \
+}
+
+#define TDA7419_DOUBLE_R_TLV(xname, xregl, xregr, xmask, xmin, xmax, \
+ xthresh, xinvert, xtlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (xtlv_array), \
+ .info = tda7419_vol_info, \
+ .get = tda7419_vol_get, \
+ .put = tda7419_vol_put, \
+ .private_value = TDA7419_DOUBLE_R_VALUE(xregl, xregr, xmask, \
+ xmin, xmax, xthresh, \
+ xinvert), \
+}
+
+static const char * const enum_src_sel[] = {
+ "QD", "SE1", "SE2", "SE3", "SE", "Mute", "Mute", "Mute"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_main_src_sel,
+ TDA7419_MAIN_SRC_REG, TDA7419_MAIN_SRC_SEL, enum_src_sel);
+static const struct snd_kcontrol_new soc_mux_main_src_sel =
+ SOC_DAPM_ENUM("Main Source Select", soc_enum_main_src_sel);
+static DECLARE_TLV_DB_SCALE(tlv_src_gain, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(tlv_loudness_atten, -1500, 100, 0);
+static const char * const enum_loudness_center_freq[] = {
+ "Flat", "400 Hz", "800 Hz", "2400 Hz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_loudness_center_freq,
+ TDA7419_LOUDNESS_REG, TDA7419_LOUDNESS_CENTER_FREQ,
+ enum_loudness_center_freq);
+static const char * const enum_mute_influence[] = {
+ "Pin and IIC", "IIC"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_mute_influence,
+ TDA7419_MUTE_CLK_REG, TDA7419_MUTE_INFLUENCE, enum_mute_influence);
+static const char * const enum_soft_mute_time[] = {
+ "0.48 ms", "0.96 ms", "123 ms", "123 ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_soft_mute_time,
+ TDA7419_MUTE_CLK_REG, TDA7419_SOFT_MUTE_TIME, enum_soft_mute_time);
+static const char * const enum_soft_step_time[] = {
+ "0.160 ms", "0.321 ms", "0.642 ms", "1.28 ms",
+ "2.56 ms", "5.12 ms", "10.24 ms", "20.48 ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_soft_step_time,
+ TDA7419_MUTE_CLK_REG, TDA7419_SOFT_STEP_TIME, enum_soft_step_time);
+static DECLARE_TLV_DB_SCALE(tlv_volume, -8000, 100, 1);
+static const char * const enum_treble_center_freq[] = {
+ "10.0 kHz", "12.5 kHz", "15.0 kHz", "17.5 kHz"};
+static DECLARE_TLV_DB_SCALE(tlv_filter, -1500, 100, 0);
+static SOC_ENUM_SINGLE_DECL(soc_enum_treble_center_freq,
+ TDA7419_TREBLE_REG, TDA7419_TREBLE_CENTER_FREQ,
+ enum_treble_center_freq);
+static const char * const enum_ref_out_select[] = {
+ "External Vref (4 V)", "Internal Vref (3.3 V)"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_ref_out_select,
+ TDA7419_TREBLE_REG, TDA7419_REF_OUT_SELECT, enum_ref_out_select);
+static const char * const enum_middle_q_factor[] = {
+ "0.5", "0.75", "1.0", "1.25"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_middle_q_factor,
+ TDA7419_MIDDLE_REG, TDA7419_MIDDLE_Q_FACTOR, enum_middle_q_factor);
+static const char * const enum_bass_q_factor[] = {
+ "1.0", "1.25", "1.5", "2.0"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bass_q_factor,
+ TDA7419_BASS_REG, TDA7419_BASS_Q_FACTOR, enum_bass_q_factor);
+static SOC_ENUM_SINGLE_DECL(soc_enum_second_src_sel,
+ TDA7419_SECOND_SRC_REG, TDA7419_SECOND_SRC_SEL, enum_src_sel);
+static const struct snd_kcontrol_new soc_mux_second_src_sel =
+ SOC_DAPM_ENUM("Second Source Select", soc_enum_second_src_sel);
+static const char * const enum_rear_spkr_src[] = {
+ "Main", "Second"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_rear_spkr_src,
+ TDA7419_SECOND_SRC_REG, TDA7419_REAR_SPKR_SRC, enum_rear_spkr_src);
+static const struct snd_kcontrol_new soc_mux_rear_spkr_src =
+ SOC_DAPM_ENUM("Rear Speaker Source", soc_enum_rear_spkr_src);
+static const char * const enum_sub_cut_off_freq[] = {
+ "Flat", "80 Hz", "120 Hz", "160 Hz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_sub_cut_off_freq,
+ TDA7419_SUB_MID_BASS_REG, TDA7419_SUB_CUT_OFF_FREQ,
+ enum_sub_cut_off_freq);
+static const char * const enum_middle_center_freq[] = {
+ "500 Hz", "1000 Hz", "1500 Hz", "2500 Hz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_middle_center_freq,
+ TDA7419_SUB_MID_BASS_REG, TDA7419_MIDDLE_CENTER_FREQ,
+ enum_middle_center_freq);
+static const char * const enum_bass_center_freq[] = {
+ "60 Hz", "80 Hz", "100 Hz", "200 Hz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bass_center_freq,
+ TDA7419_SUB_MID_BASS_REG, TDA7419_BASS_CENTER_FREQ,
+ enum_bass_center_freq);
+static const char * const enum_sa_q_factor[] = {
+ "3.5", "1.75" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_sa_q_factor,
+ TDA7419_SA_CLK_AC_REG, TDA7419_SA_Q_FACTOR, enum_sa_q_factor);
+static const char * const enum_reset_mode[] = {
+ "IIC", "Auto" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_reset_mode,
+ TDA7419_SA_CLK_AC_REG, TDA7419_RESET_MODE, enum_reset_mode);
+static const char * const enum_sa_src[] = {
+ "Bass", "In Gain" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_sa_src,
+ TDA7419_SA_CLK_AC_REG, TDA7419_SA_SOURCE, enum_sa_src);
+static const char * const enum_clk_src[] = {
+ "Internal", "External" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_clk_src,
+ TDA7419_SA_CLK_AC_REG, TDA7419_CLK_SOURCE, enum_clk_src);
+static const char * const enum_coupling_mode[] = {
+ "DC Coupling (without HPF)", "AC Coupling after In Gain",
+ "DC Coupling (with HPF)", "AC Coupling after Bass" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_coupling_mode,
+ TDA7419_SA_CLK_AC_REG, TDA7419_COUPLING_MODE, enum_coupling_mode);
+
+/* ASoC Controls */
+static struct snd_kcontrol_new tda7419_controls[] = {
+SOC_SINGLE_TLV("Main Source Capture Volume", TDA7419_MAIN_SRC_REG,
+ TDA7419_MAIN_SRC_GAIN, 15, 0, tlv_src_gain),
+SOC_SINGLE("Main Source AutoZero Switch", TDA7419_MAIN_SRC_REG,
+ TDA7419_MAIN_SRC_AUTOZERO, 1, 1),
+SOC_SINGLE_TLV("Loudness Playback Volume", TDA7419_LOUDNESS_REG,
+ TDA7419_LOUDNESS_ATTEN, 15, 1, tlv_loudness_atten),
+SOC_ENUM("Loudness Center Frequency", soc_enum_loudness_center_freq),
+SOC_SINGLE("Loudness High Boost Switch", TDA7419_LOUDNESS_REG,
+ TDA7419_LOUDNESS_BOOST, 1, 1),
+SOC_SINGLE("Loudness Soft Step Switch", TDA7419_LOUDNESS_REG,
+ TDA7419_LOUDNESS_SOFT_STEP, 1, 1),
+SOC_SINGLE("Soft Mute Switch", TDA7419_MUTE_CLK_REG, TDA7419_SOFT_MUTE, 1, 1),
+SOC_ENUM("Mute Influence", soc_enum_mute_influence),
+SOC_ENUM("Soft Mute Time", soc_enum_soft_mute_time),
+SOC_ENUM("Soft Step Time", soc_enum_soft_step_time),
+SOC_SINGLE("Clock Fast Mode Switch", TDA7419_MUTE_CLK_REG,
+ TDA7419_CLK_FAST_MODE, 1, 1),
+TDA7419_SINGLE_TLV("Master Playback Volume", TDA7419_VOLUME_REG,
+ 0x7f, -80, 15, 0x10, 0, tlv_volume),
+SOC_SINGLE("Volume Soft Step Switch", TDA7419_VOLUME_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+TDA7419_SINGLE_TLV("Treble Playback Volume", TDA7419_TREBLE_REG,
+ 0x1f, -15, 15, 0x10, 1, tlv_filter),
+SOC_ENUM("Treble Center Frequency", soc_enum_treble_center_freq),
+SOC_ENUM("Reference Output Select", soc_enum_ref_out_select),
+TDA7419_SINGLE_TLV("Middle Playback Volume", TDA7419_MIDDLE_REG,
+ 0x1f, -15, 15, 0x10, 1, tlv_filter),
+SOC_ENUM("Middle Q Factor", soc_enum_middle_q_factor),
+SOC_SINGLE("Middle Soft Step Switch", TDA7419_MIDDLE_REG,
+ TDA7419_MIDDLE_SOFT_STEP, 1, 1),
+TDA7419_SINGLE_TLV("Bass Playback Volume", TDA7419_BASS_REG,
+ 0x1f, -15, 15, 0x10, 1, tlv_filter),
+SOC_ENUM("Bass Q Factor", soc_enum_bass_q_factor),
+SOC_SINGLE("Bass Soft Step Switch", TDA7419_BASS_REG,
+ TDA7419_BASS_SOFT_STEP, 1, 1),
+SOC_SINGLE_TLV("Second Source Capture Volume", TDA7419_SECOND_SRC_REG,
+ TDA7419_SECOND_SRC_GAIN, 15, 0, tlv_src_gain),
+SOC_ENUM("Subwoofer Cut-off Frequency", soc_enum_sub_cut_off_freq),
+SOC_ENUM("Middle Center Frequency", soc_enum_middle_center_freq),
+SOC_ENUM("Bass Center Frequency", soc_enum_bass_center_freq),
+SOC_SINGLE("Bass DC Mode Switch", TDA7419_SUB_MID_BASS_REG,
+ TDA7419_BASS_DC_MODE, 1, 1),
+SOC_SINGLE("Smoothing Filter Switch", TDA7419_SUB_MID_BASS_REG,
+ TDA7419_SMOOTHING_FILTER, 1, 1),
+TDA7419_DOUBLE_R_TLV("Front Speaker Playback Volume", TDA7419_ATTENUATOR_LF_REG,
+ TDA7419_ATTENUATOR_RF_REG, 0x7f, -80, 15, 0x10, 0,
+ tlv_volume),
+SOC_SINGLE("Left Front Soft Step Switch", TDA7419_ATTENUATOR_LF_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+SOC_SINGLE("Right Front Soft Step Switch", TDA7419_ATTENUATOR_RF_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+TDA7419_DOUBLE_R_TLV("Rear Speaker Playback Volume", TDA7419_ATTENUATOR_LR_REG,
+ TDA7419_ATTENUATOR_RR_REG, 0x7f, -80, 15, 0x10, 0,
+ tlv_volume),
+SOC_SINGLE("Left Rear Soft Step Switch", TDA7419_ATTENUATOR_LR_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+SOC_SINGLE("Right Rear Soft Step Switch", TDA7419_ATTENUATOR_RR_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+TDA7419_SINGLE_TLV("Mixing Capture Volume", TDA7419_MIXING_LEVEL_REG,
+ 0x7f, -80, 15, 0x10, 0, tlv_volume),
+SOC_SINGLE("Mixing Level Soft Step Switch", TDA7419_MIXING_LEVEL_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+TDA7419_SINGLE_TLV("Subwoofer Playback Volume", TDA7419_ATTENUATOR_SUB_REG,
+ 0x7f, -80, 15, 0x10, 0, tlv_volume),
+SOC_SINGLE("Subwoofer Soft Step Switch", TDA7419_ATTENUATOR_SUB_REG,
+ TDA7419_VOLUME_SOFT_STEP, 1, 1),
+SOC_ENUM("Spectrum Analyzer Q Factor", soc_enum_sa_q_factor),
+SOC_ENUM("Spectrum Analyzer Reset Mode", soc_enum_reset_mode),
+SOC_ENUM("Spectrum Analyzer Source", soc_enum_sa_src),
+SOC_SINGLE("Spectrum Analyzer Run Switch", TDA7419_SA_CLK_AC_REG,
+ TDA7419_SA_RUN, 1, 1),
+SOC_SINGLE("Spectrum Analyzer Reset Switch", TDA7419_SA_CLK_AC_REG,
+ TDA7419_RESET, 1, 1),
+SOC_ENUM("Clock Source", soc_enum_clk_src),
+SOC_ENUM("Coupling Mode", soc_enum_coupling_mode),
+};
+
+static const struct snd_kcontrol_new soc_mixer_lf_output_controls[] = {
+ SOC_DAPM_SINGLE("Mix to LF Speaker Switch",
+ TDA7419_MIXING_GAIN_REG,
+ TDA7419_MIX_LF, 1, 1),
+};
+
+static const struct snd_kcontrol_new soc_mixer_rf_output_controls[] = {
+ SOC_DAPM_SINGLE("Mix to RF Speaker Switch",
+ TDA7419_MIXING_GAIN_REG,
+ TDA7419_MIX_RF, 1, 1),
+};
+
+static const struct snd_kcontrol_new soc_mix_enable_switch_controls[] = {
+ SOC_DAPM_SINGLE("Switch", TDA7419_MIXING_GAIN_REG,
+ TDA7419_MIX_ENABLE, 1, 1),
+};
+
+static const struct snd_kcontrol_new soc_sub_enable_switch_controls[] = {
+ SOC_DAPM_SINGLE("Switch", TDA7419_MIXING_GAIN_REG,
+ TDA7419_MIX_ENABLE, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget tda7419_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("SE3L"),
+ SND_SOC_DAPM_INPUT("SE3R"),
+ SND_SOC_DAPM_INPUT("SE2L"),
+ SND_SOC_DAPM_INPUT("SE2R"),
+ SND_SOC_DAPM_INPUT("SE1L"),
+ SND_SOC_DAPM_INPUT("SE1R"),
+ SND_SOC_DAPM_INPUT("DIFFL"),
+ SND_SOC_DAPM_INPUT("DIFFR"),
+ SND_SOC_DAPM_INPUT("MIX"),
+
+ SND_SOC_DAPM_MUX("Main Source Select", SND_SOC_NOPM,
+ 0, 0, &soc_mux_main_src_sel),
+ SND_SOC_DAPM_MUX("Second Source Select", SND_SOC_NOPM,
+ 0, 0, &soc_mux_second_src_sel),
+ SND_SOC_DAPM_MUX("Rear Speaker Source", SND_SOC_NOPM,
+ 0, 0, &soc_mux_rear_spkr_src),
+
+ SND_SOC_DAPM_SWITCH("Mix Enable", SND_SOC_NOPM,
+ 0, 0, &soc_mix_enable_switch_controls[0]),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("LF Output Mixer", SND_SOC_NOPM,
+ 0, 0, &soc_mixer_lf_output_controls[0],
+ ARRAY_SIZE(soc_mixer_lf_output_controls)),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("RF Output Mixer", SND_SOC_NOPM,
+ 0, 0, &soc_mixer_rf_output_controls[0],
+ ARRAY_SIZE(soc_mixer_rf_output_controls)),
+
+ SND_SOC_DAPM_SWITCH("Subwoofer Enable",
+ SND_SOC_NOPM, 0, 0,
+ &soc_sub_enable_switch_controls[0]),
+
+ SND_SOC_DAPM_OUTPUT("OUTLF"),
+ SND_SOC_DAPM_OUTPUT("OUTRF"),
+ SND_SOC_DAPM_OUTPUT("OUTLR"),
+ SND_SOC_DAPM_OUTPUT("OUTRR"),
+ SND_SOC_DAPM_OUTPUT("OUTSW"),
+};
+
+static const struct snd_soc_dapm_route tda7419_dapm_routes[] = {
+ {"Main Source Select", "SE3", "SE3L"},
+ {"Main Source Select", "SE3", "SE3R"},
+ {"Main Source Select", "SE2", "SE2L"},
+ {"Main Source Select", "SE2", "SE2R"},
+ {"Main Source Select", "SE1", "SE1L"},
+ {"Main Source Select", "SE1", "SE1R"},
+ {"Main Source Select", "SE", "DIFFL"},
+ {"Main Source Select", "SE", "DIFFR"},
+ {"Main Source Select", "QD", "DIFFL"},
+ {"Main Source Select", "QD", "DIFFR"},
+
+ {"Second Source Select", "SE3", "SE3L"},
+ {"Second Source Select", "SE3", "SE3R"},
+ {"Second Source Select", "SE2", "SE2L"},
+ {"Second Source Select", "SE2", "SE2R"},
+ {"Second Source Select", "SE1", "SE1L"},
+ {"Second Source Select", "SE1", "SE1R"},
+ {"Second Source Select", "SE", "DIFFL"},
+ {"Second Source Select", "SE", "DIFFR"},
+ {"Second Source Select", "QD", "DIFFL"},
+ {"Second Source Select", "QD", "DIFFR"},
+
+ {"Rear Speaker Source", "Main", "Main Source Select"},
+ {"Rear Speaker Source", "Second", "Second Source Select"},
+
+ {"Subwoofer Enable", "Switch", "Main Source Select"},
+
+ {"Mix Enable", "Switch", "MIX"},
+
+ {"LF Output Mixer", NULL, "Main Source Select"},
+ {"LF Output Mixer", "Mix to LF Speaker Switch", "Mix Enable"},
+ {"RF Output Mixer", NULL, "Main Source Select"},
+ {"RF Output Mixer", "Mix to RF Speaker Switch", "Mix Enable"},
+
+ {"OUTLF", NULL, "LF Output Mixer"},
+ {"OUTRF", NULL, "RF Output Mixer"},
+ {"OUTLR", NULL, "Rear Speaker Source"},
+ {"OUTRR", NULL, "Rear Speaker Source"},
+ {"OUTSW", NULL, "Subwoofer Enable"},
+};
+
+static const struct snd_soc_component_driver tda7419_component_driver = {
+ .name = "tda7419",
+ .controls = tda7419_controls,
+ .num_controls = ARRAY_SIZE(tda7419_controls),
+ .dapm_widgets = tda7419_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tda7419_dapm_widgets),
+ .dapm_routes = tda7419_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tda7419_dapm_routes),
+};
+
+static int tda7419_probe(struct i2c_client *i2c)
+{
+ struct tda7419_data *tda7419;
+ int i, ret;
+
+ tda7419 = devm_kzalloc(&i2c->dev,
+ sizeof(struct tda7419_data),
+ GFP_KERNEL);
+ if (tda7419 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tda7419);
+
+ tda7419->regmap = devm_regmap_init_i2c(i2c, &tda7419_regmap_config);
+ if (IS_ERR(tda7419->regmap)) {
+ ret = PTR_ERR(tda7419->regmap);
+ dev_err(&i2c->dev, "error initializing regmap: %d\n",
+ ret);
+ return ret;
+ }
+
+ /*
+ * Reset registers to power-on defaults. The part does not provide a
+ * soft-reset function and the registers are not readable. This ensures
+ * that the cache matches register contents even if the registers have
+ * been previously initialized and not power cycled before probe.
+ */
+ for (i = 0; i < ARRAY_SIZE(tda7419_regmap_defaults); i++)
+ regmap_write(tda7419->regmap,
+ tda7419_regmap_defaults[i].reg,
+ tda7419_regmap_defaults[i].def);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &tda7419_component_driver, NULL, 0);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "error registering component: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+static const struct i2c_device_id tda7419_i2c_id[] = {
+ { "tda7419" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda7419_i2c_id);
+
+static const struct of_device_id tda7419_of_match[] = {
+ { .compatible = "st,tda7419" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tda7419_of_match);
+
+static struct i2c_driver tda7419_driver = {
+ .driver = {
+ .name = "tda7419",
+ .of_match_table = tda7419_of_match,
+ },
+ .probe = tda7419_probe,
+ .id_table = tda7419_i2c_id,
+};
+
+module_i2c_driver(tda7419_driver);
+
+MODULE_AUTHOR("Matt Porter <mporter@konsulko.com>");
+MODULE_DESCRIPTION("TDA7419 audio processor driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
index cb5310d89c0f..ac0c5c337677 100644
--- a/sound/soc/codecs/tfa9879.c
+++ b/sound/soc/codecs/tfa9879.c
@@ -1,15 +1,9 @@
-/*
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// tfa9879.c -- driver for NXP Semiconductors TFA9879
+//
+// Copyright (C) 2014 Axentia Technologies AB
+// Author: Peter Rosin <peda@axentia.se>
#include <linux/module.h>
#include <linux/init.h>
@@ -30,8 +24,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct tfa9879_priv *tfa9879 = snd_soc_component_get_drvdata(component);
int fs;
int i2s_set = 0;
@@ -88,36 +82,37 @@ static int tfa9879_hw_params(struct snd_pcm_substream *substream,
}
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);
+ snd_soc_component_update_bits(component,
+ TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_I2S_SET_MASK,
+ i2s_set << TFA9879_I2S_SET_SHIFT);
+
+ snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_I2S_FS_MASK,
+ fs << TFA9879_I2S_FS_SHIFT);
return 0;
}
-static int tfa9879_digital_mute(struct snd_soc_dai *dai, int mute)
+static int tfa9879_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- snd_soc_update_bits(codec, TFA9879_MISC_CONTROL,
- TFA9879_S_MUTE_MASK,
- !!mute << TFA9879_S_MUTE_SHIFT);
+ snd_soc_component_update_bits(component, TFA9879_MISC_CONTROL,
+ TFA9879_S_MUTE_MASK,
+ !!mute << TFA9879_S_MUTE_SHIFT);
return 0;
}
static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tfa9879_priv *tfa9879 = snd_soc_component_get_drvdata(component);
int i2s_set;
int sck_pol;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -151,12 +146,12 @@ static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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);
+ snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_SCK_POL_MASK,
+ sck_pol << TFA9879_SCK_POL_SHIFT);
+ snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_I2S_SET_MASK,
+ i2s_set << TFA9879_I2S_SET_SHIFT);
return 0;
}
@@ -230,14 +225,16 @@ static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = {
{ "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 snd_soc_component_driver tfa9879_component = {
+ .controls = tfa9879_controls,
+ .num_controls = ARRAY_SIZE(tfa9879_controls),
+ .dapm_widgets = tfa9879_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets),
+ .dapm_routes = tfa9879_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config tfa9879_regmap = {
@@ -253,8 +250,9 @@ static const struct regmap_config tfa9879_regmap = {
static const struct snd_soc_dai_ops tfa9879_dai_ops = {
.hw_params = tfa9879_hw_params,
- .digital_mute = tfa9879_digital_mute,
+ .mute_stream = tfa9879_mute_stream,
.set_fmt = tfa9879_set_fmt,
+ .no_capture_mute = 1,
};
#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000
@@ -273,8 +271,7 @@ static struct snd_soc_dai_driver tfa9879_dai = {
.ops = &tfa9879_dai_ops,
};
-static int tfa9879_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tfa9879_i2c_probe(struct i2c_client *i2c)
{
struct tfa9879_priv *tfa9879;
int i;
@@ -294,29 +291,28 @@ static int tfa9879_i2c_probe(struct i2c_client *i2c,
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;
+ return devm_snd_soc_register_component(&i2c->dev, &tfa9879_component,
+ &tfa9879_dai, 1);
}
static const struct i2c_device_id tfa9879_i2c_id[] = {
- { "tfa9879", 0 },
+ { "tfa9879" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id);
+static const struct of_device_id tfa9879_of_match[] = {
+ { .compatible = "nxp,tfa9879", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tfa9879_of_match);
+
static struct i2c_driver tfa9879_i2c_driver = {
.driver = {
.name = "tfa9879",
+ .of_match_table = tfa9879_of_match,
},
.probe = tfa9879_i2c_probe,
- .remove = tfa9879_i2c_remove,
.id_table = tfa9879_i2c_id,
};
diff --git a/sound/soc/codecs/tfa9879.h b/sound/soc/codecs/tfa9879.h
index 3408c90c4628..66c88d0396fe 100644
--- a/sound/soc/codecs/tfa9879.h
+++ b/sound/soc/codecs/tfa9879.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* 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
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c
new file mode 100644
index 000000000000..7204be155eb9
--- /dev/null
+++ b/sound/soc/codecs/tfa989x.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Stephan Gerhold
+ *
+ * Register definitions/sequences taken from various tfa98xx kernel drivers:
+ * Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved.
+ * Copyright (C) 2013 Sony Mobile Communications Inc.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define TFA989X_STATUSREG 0x00
+#define TFA989X_BATTERYVOLTAGE 0x01
+#define TFA989X_TEMPERATURE 0x02
+#define TFA989X_REVISIONNUMBER 0x03
+#define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */
+#define TFA989X_I2SREG 0x04
+#define TFA989X_I2SREG_RCV 2 /* receiver mode */
+#define TFA989X_I2SREG_CHSA 6 /* amplifier input select */
+#define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6)
+#define TFA989X_I2SREG_I2SSR 12 /* sample rate */
+#define TFA989X_I2SREG_I2SSR_MSK GENMASK(15, 12)
+#define TFA989X_BAT_PROT 0x05
+#define TFA989X_AUDIO_CTR 0x06
+#define TFA989X_DCDCBOOST 0x07
+#define TFA989X_SPKR_CALIBRATION 0x08
+#define TFA989X_SYS_CTRL 0x09
+#define TFA989X_SYS_CTRL_PWDN 0 /* power down */
+#define TFA989X_SYS_CTRL_I2CR 1 /* I2C reset */
+#define TFA989X_SYS_CTRL_CFE 2 /* enable CoolFlux DSP */
+#define TFA989X_SYS_CTRL_AMPE 3 /* enable amplifier */
+#define TFA989X_SYS_CTRL_DCA 4 /* enable boost */
+#define TFA989X_SYS_CTRL_SBSL 5 /* DSP configured */
+#define TFA989X_SYS_CTRL_AMPC 6 /* amplifier enabled by DSP */
+#define TFA989X_I2S_SEL_REG 0x0a
+#define TFA989X_I2S_SEL_REG_SPKR_MSK GENMASK(10, 9) /* speaker impedance */
+#define TFA989X_I2S_SEL_REG_DCFG_MSK GENMASK(14, 11) /* DCDC compensation */
+#define TFA989X_HIDE_UNHIDE_KEY 0x40
+#define TFA989X_PWM_CONTROL 0x41
+#define TFA989X_CURRENTSENSE1 0x46
+#define TFA989X_CURRENTSENSE2 0x47
+#define TFA989X_CURRENTSENSE3 0x48
+#define TFA989X_CURRENTSENSE4 0x49
+
+#define TFA9890_REVISION 0x80
+#define TFA9895_REVISION 0x12
+#define TFA9897_REVISION 0x97
+
+struct tfa989x_rev {
+ unsigned int rev;
+ int (*init)(struct regmap *regmap);
+};
+
+struct tfa989x {
+ const struct tfa989x_rev *rev;
+ struct regulator *vddd_supply;
+ struct gpio_desc *rcv_gpiod;
+};
+
+static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg > TFA989X_REVISIONNUMBER;
+}
+
+static bool tfa989x_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg < TFA989X_REVISIONNUMBER;
+}
+
+static const struct regmap_config tfa989x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .writeable_reg = tfa989x_writeable_reg,
+ .volatile_reg = tfa989x_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const char * const chsa_text[] = { "Left", "Right", /* "DSP" */ };
+static SOC_ENUM_SINGLE_DECL(chsa_enum, TFA989X_I2SREG, TFA989X_I2SREG_CHSA, chsa_text);
+static const struct snd_kcontrol_new chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum);
+
+static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SUPPLY("POWER", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_PWDN, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("AMPE", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_AMPE, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Amp Input", SND_SOC_NOPM, 0, 0, &chsa_mux),
+ SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = {
+ {"OUT", NULL, "AMPE"},
+ {"AMPE", NULL, "POWER"},
+ {"AMPE", NULL, "Amp Input"},
+ {"Amp Input", "Left", "AIFINL"},
+ {"Amp Input", "Right", "AIFINR"},
+};
+
+static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component);
+
+ gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]);
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static const char * const mode_text[] = { "Speaker", "Receiver" };
+static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text);
+static const struct snd_kcontrol_new tfa989x_mode_controls[] = {
+ SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode),
+};
+
+static int tfa989x_probe(struct snd_soc_component *component)
+{
+ struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component);
+
+ if (tfa989x->rev->rev == TFA9897_REVISION)
+ return snd_soc_add_component_controls(component, tfa989x_mode_controls,
+ ARRAY_SIZE(tfa989x_mode_controls));
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver tfa989x_component = {
+ .probe = tfa989x_probe,
+ .dapm_widgets = tfa989x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets),
+ .dapm_routes = tfa989x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tfa989x_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const unsigned int tfa989x_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static int tfa989x_find_sample_rate(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tfa989x_rates); ++i)
+ if (tfa989x_rates[i] == rate)
+ return i;
+
+ return -EINVAL;
+}
+
+static int tfa989x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int sr;
+
+ sr = tfa989x_find_sample_rate(params_rate(params));
+ if (sr < 0)
+ return sr;
+
+ return snd_soc_component_update_bits(component, TFA989X_I2SREG,
+ TFA989X_I2SREG_I2SSR_MSK,
+ sr << TFA989X_I2SREG_I2SSR);
+}
+
+static const struct snd_soc_dai_ops tfa989x_dai_ops = {
+ .hw_params = tfa989x_hw_params,
+};
+
+static struct snd_soc_dai_driver tfa989x_dai = {
+ .name = "tfa989x-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tfa989x_dai_ops,
+};
+
+static int tfa9890_init(struct regmap *regmap)
+{
+ int ret;
+
+ /* temporarily allow access to hidden registers */
+ ret = regmap_write(regmap, TFA989X_HIDE_UNHIDE_KEY, 0x5a6b);
+ if (ret)
+ return ret;
+
+ /* update PLL registers */
+ ret = regmap_set_bits(regmap, 0x59, 0x3);
+ if (ret)
+ return ret;
+
+ /* hide registers again */
+ ret = regmap_write(regmap, TFA989X_HIDE_UNHIDE_KEY, 0x0000);
+ if (ret)
+ return ret;
+
+ return regmap_write(regmap, TFA989X_CURRENTSENSE2, 0x7BE1);
+}
+
+static const struct tfa989x_rev tfa9890_rev = {
+ .rev = TFA9890_REVISION,
+ .init = tfa9890_init,
+};
+
+static const struct reg_sequence tfa9895_reg_init[] = {
+ /* some other registers must be set for optimal amplifier behaviour */
+ { TFA989X_BAT_PROT, 0x13ab },
+ { TFA989X_AUDIO_CTR, 0x001f },
+
+ /* peak voltage protection is always on, but may be written */
+ { TFA989X_SPKR_CALIBRATION, 0x3c4e },
+
+ /* TFA989X_SYSCTRL_DCA = 0 */
+ { TFA989X_SYS_CTRL, 0x024d },
+ { TFA989X_PWM_CONTROL, 0x0308 },
+ { TFA989X_CURRENTSENSE4, 0x0e82 },
+};
+
+static int tfa9895_init(struct regmap *regmap)
+{
+ return regmap_multi_reg_write(regmap, tfa9895_reg_init,
+ ARRAY_SIZE(tfa9895_reg_init));
+}
+
+static const struct tfa989x_rev tfa9895_rev = {
+ .rev = TFA9895_REVISION,
+ .init = tfa9895_init,
+};
+
+static int tfa9897_init(struct regmap *regmap)
+{
+ int ret;
+
+ /* Reduce slewrate by clearing iddqtestbst to avoid booster damage */
+ ret = regmap_write(regmap, TFA989X_CURRENTSENSE3, 0x0300);
+ if (ret)
+ return ret;
+
+ /* Enable clipping */
+ ret = regmap_clear_bits(regmap, TFA989X_CURRENTSENSE4, 0x1);
+ if (ret)
+ return ret;
+
+ /* Set required TDM configuration */
+ return regmap_write(regmap, 0x14, 0x0);
+}
+
+static const struct tfa989x_rev tfa9897_rev = {
+ .rev = TFA9897_REVISION,
+ .init = tfa9897_init,
+};
+
+/*
+ * Note: At the moment this driver bypasses the "CoolFlux DSP" built into the
+ * TFA989X amplifiers. Unfortunately, there seems to be absolutely
+ * no documentation for it - the public "short datasheets" do not provide
+ * any information about the DSP or available registers.
+ *
+ * Usually the TFA989X amplifiers are configured through proprietary userspace
+ * libraries. There are also some (rather complex) kernel drivers but even those
+ * rely on obscure firmware blobs for configuration (so-called "containers").
+ * They seem to contain different "profiles" with tuned speaker settings, sample
+ * rates and volume steps (which would be better exposed as separate ALSA mixers).
+ *
+ * Bypassing the DSP disables volume control (and perhaps some speaker
+ * optimization?), but at least allows using the speaker without obscure
+ * kernel drivers and firmware.
+ *
+ * Ideally NXP (or now Goodix) should release proper documentation for these
+ * amplifiers so that support for the "CoolFlux DSP" can be implemented properly.
+ */
+static int tfa989x_dsp_bypass(struct regmap *regmap)
+{
+ int ret;
+
+ /* Clear CHSA to bypass DSP and take input from I2S 1 left channel */
+ ret = regmap_clear_bits(regmap, TFA989X_I2SREG, TFA989X_I2SREG_CHSA_MSK);
+ if (ret)
+ return ret;
+
+ /* Set DCDC compensation to off and speaker impedance to 8 ohm */
+ ret = regmap_update_bits(regmap, TFA989X_I2S_SEL_REG,
+ TFA989X_I2S_SEL_REG_DCFG_MSK |
+ TFA989X_I2S_SEL_REG_SPKR_MSK,
+ TFA989X_I2S_SEL_REG_SPKR_MSK);
+ if (ret)
+ return ret;
+
+ /* Set DCDC to follower mode and disable CoolFlux DSP */
+ return regmap_clear_bits(regmap, TFA989X_SYS_CTRL,
+ BIT(TFA989X_SYS_CTRL_DCA) |
+ BIT(TFA989X_SYS_CTRL_CFE) |
+ BIT(TFA989X_SYS_CTRL_AMPC));
+}
+
+static void tfa989x_regulator_disable(void *data)
+{
+ struct tfa989x *tfa989x = data;
+
+ regulator_disable(tfa989x->vddd_supply);
+}
+
+static int tfa989x_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ const struct tfa989x_rev *rev;
+ struct tfa989x *tfa989x;
+ struct regmap *regmap;
+ unsigned int val;
+ int ret;
+
+ rev = device_get_match_data(dev);
+ if (!rev) {
+ dev_err(dev, "unknown device revision\n");
+ return -ENODEV;
+ }
+
+ tfa989x = devm_kzalloc(dev, sizeof(*tfa989x), GFP_KERNEL);
+ if (!tfa989x)
+ return -ENOMEM;
+
+ tfa989x->rev = rev;
+ i2c_set_clientdata(i2c, tfa989x);
+
+ tfa989x->vddd_supply = devm_regulator_get(dev, "vddd");
+ if (IS_ERR(tfa989x->vddd_supply))
+ return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply),
+ "Failed to get vddd regulator\n");
+
+ if (tfa989x->rev->rev == TFA9897_REVISION) {
+ tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW);
+ if (IS_ERR(tfa989x->rcv_gpiod))
+ return PTR_ERR(tfa989x->rcv_gpiod);
+ }
+
+ regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regulator_enable(tfa989x->vddd_supply);
+ if (ret) {
+ dev_err(dev, "Failed to enable vddd regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, tfa989x_regulator_disable, tfa989x);
+ if (ret)
+ return ret;
+
+ /* Bypass regcache for reset and init sequence */
+ regcache_cache_bypass(regmap, true);
+
+ /* Dummy read to generate i2c clocks, required on some devices */
+ regmap_read(regmap, TFA989X_REVISIONNUMBER, &val);
+
+ ret = regmap_read(regmap, TFA989X_REVISIONNUMBER, &val);
+ if (ret) {
+ dev_err(dev, "failed to read revision number: %d\n", ret);
+ return ret;
+ }
+
+ val &= TFA989X_REVISIONNUMBER_REV_MSK;
+ if (val != rev->rev) {
+ dev_err(dev, "invalid revision number, expected %#x, got %#x\n",
+ rev->rev, val);
+ return -ENODEV;
+ }
+
+ ret = regmap_write(regmap, TFA989X_SYS_CTRL, BIT(TFA989X_SYS_CTRL_I2CR));
+ if (ret) {
+ dev_err(dev, "failed to reset I2C registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = rev->init(regmap);
+ if (ret) {
+ dev_err(dev, "failed to initialize registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = tfa989x_dsp_bypass(regmap);
+ if (ret) {
+ dev_err(dev, "failed to enable DSP bypass: %d\n", ret);
+ return ret;
+ }
+ regcache_cache_bypass(regmap, false);
+
+ return devm_snd_soc_register_component(dev, &tfa989x_component,
+ &tfa989x_dai, 1);
+}
+
+static const struct of_device_id tfa989x_of_match[] = {
+ { .compatible = "nxp,tfa9890", .data = &tfa9890_rev },
+ { .compatible = "nxp,tfa9895", .data = &tfa9895_rev },
+ { .compatible = "nxp,tfa9897", .data = &tfa9897_rev },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tfa989x_of_match);
+
+static struct i2c_driver tfa989x_i2c_driver = {
+ .driver = {
+ .name = "tfa989x",
+ .of_match_table = tfa989x_of_match,
+ },
+ .probe = tfa989x_i2c_probe,
+};
+module_i2c_driver(tfa989x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NXP/Goodix TFA989X (TFA1) driver");
+MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
new file mode 100644
index 000000000000..270eee1ea534
--- /dev/null
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -0,0 +1,1522 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Based on sound/soc/codecs/tlv320aic3x.c by Vladimir Barinov
+//
+// Copyright (C) 2010 Mistral Solutions Pvt Ltd.
+// Author: Shahina Shaik <shahina.s@mistralsolutions.com>
+//
+// Copyright (C) 2014-2018, Ambarella, Inc.
+// Author: Dongge wu <dgwu@ambarella.com>
+//
+// Copyright (C) 2021 Axis Communications AB
+// Author: Ricard Wanderlof <ricardw@axis.com>
+//
+
+#include <dt-bindings/sound/tlv320adc3xxx.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio/driver.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.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/initval.h>
+
+/*
+ * General definitions defining exported functionality.
+ */
+
+#define ADC3XXX_MICBIAS_PINS 2
+#define ADC3XXX_GPIO_PINS 2
+
+/* Number of GPIO pins exposed via the gpiolib interface */
+#define ADC3XXX_GPIOS_MAX (ADC3XXX_MICBIAS_PINS + ADC3XXX_GPIO_PINS)
+
+#define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000
+#define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * PLL modes, to be used for clk_id for set_sysclk callback.
+ *
+ * The default behavior (AUTO) is to take the first matching entry in the clock
+ * table, which is intended to be the PLL based one if there is more than one.
+ *
+ * Setting the clock source using simple-card (clocks or
+ * system-clock-frequency property) sets clk_id = 0 = ADC3XXX_PLL_AUTO.
+ */
+#define ADC3XXX_PLL_AUTO 0 /* Use first available mode */
+#define ADC3XXX_PLL_ENABLE 1 /* Use PLL for clock generation */
+#define ADC3XXX_PLL_BYPASS 2 /* Don't use PLL for clock generation */
+
+/* Register definitions. */
+
+#define ADC3XXX_PAGE_SIZE 128
+#define ADC3XXX_REG(page, reg) ((page * ADC3XXX_PAGE_SIZE) + reg)
+
+/*
+ * Page 0 registers.
+ */
+
+#define ADC3XXX_PAGE_SELECT ADC3XXX_REG(0, 0)
+#define ADC3XXX_RESET ADC3XXX_REG(0, 1)
+
+/* 2-3 Reserved */
+
+#define ADC3XXX_CLKGEN_MUX ADC3XXX_REG(0, 4)
+#define ADC3XXX_PLL_PROG_PR ADC3XXX_REG(0, 5)
+#define ADC3XXX_PLL_PROG_J ADC3XXX_REG(0, 6)
+#define ADC3XXX_PLL_PROG_D_MSB ADC3XXX_REG(0, 7)
+#define ADC3XXX_PLL_PROG_D_LSB ADC3XXX_REG(0, 8)
+
+/* 9-17 Reserved */
+
+#define ADC3XXX_ADC_NADC ADC3XXX_REG(0, 18)
+#define ADC3XXX_ADC_MADC ADC3XXX_REG(0, 19)
+#define ADC3XXX_ADC_AOSR ADC3XXX_REG(0, 20)
+#define ADC3XXX_ADC_IADC ADC3XXX_REG(0, 21)
+
+/* 23-24 Reserved */
+
+#define ADC3XXX_CLKOUT_MUX ADC3XXX_REG(0, 25)
+#define ADC3XXX_CLKOUT_M_DIV ADC3XXX_REG(0, 26)
+#define ADC3XXX_INTERFACE_CTRL_1 ADC3XXX_REG(0, 27)
+#define ADC3XXX_CH_OFFSET_1 ADC3XXX_REG(0, 28)
+#define ADC3XXX_INTERFACE_CTRL_2 ADC3XXX_REG(0, 29)
+#define ADC3XXX_BCLK_N_DIV ADC3XXX_REG(0, 30)
+#define ADC3XXX_INTERFACE_CTRL_3 ADC3XXX_REG(0, 31)
+#define ADC3XXX_INTERFACE_CTRL_4 ADC3XXX_REG(0, 32)
+#define ADC3XXX_INTERFACE_CTRL_5 ADC3XXX_REG(0, 33)
+#define ADC3XXX_I2S_SYNC ADC3XXX_REG(0, 34)
+/* 35 Reserved */
+#define ADC3XXX_ADC_FLAG ADC3XXX_REG(0, 36)
+#define ADC3XXX_CH_OFFSET_2 ADC3XXX_REG(0, 37)
+#define ADC3XXX_I2S_TDM_CTRL ADC3XXX_REG(0, 38)
+/* 39-41 Reserved */
+#define ADC3XXX_INTR_FLAG_1 ADC3XXX_REG(0, 42)
+#define ADC3XXX_INTR_FLAG_2 ADC3XXX_REG(0, 43)
+/* 44 Reserved */
+#define ADC3XXX_INTR_FLAG_ADC1 ADC3XXX_REG(0, 45)
+/* 46 Reserved */
+#define ADC3XXX_INTR_FLAG_ADC2 ADC3XXX_REG(0, 47)
+#define ADC3XXX_INT1_CTRL ADC3XXX_REG(0, 48)
+#define ADC3XXX_INT2_CTRL ADC3XXX_REG(0, 49)
+/* 50 Reserved */
+#define ADC3XXX_GPIO2_CTRL ADC3XXX_REG(0, 51)
+#define ADC3XXX_GPIO1_CTRL ADC3XXX_REG(0, 52)
+#define ADC3XXX_DOUT_CTRL ADC3XXX_REG(0, 53)
+/* 54-56 Reserved */
+#define ADC3XXX_SYNC_CTRL_1 ADC3XXX_REG(0, 57)
+#define ADC3XXX_SYNC_CTRL_2 ADC3XXX_REG(0, 58)
+#define ADC3XXX_CIC_GAIN_CTRL ADC3XXX_REG(0, 59)
+/* 60 Reserved */
+#define ADC3XXX_PRB_SELECT ADC3XXX_REG(0, 61)
+#define ADC3XXX_INST_MODE_CTRL ADC3XXX_REG(0, 62)
+/* 63-79 Reserved */
+#define ADC3XXX_MIC_POLARITY_CTRL ADC3XXX_REG(0, 80)
+#define ADC3XXX_ADC_DIGITAL ADC3XXX_REG(0, 81)
+#define ADC3XXX_ADC_FGA ADC3XXX_REG(0, 82)
+#define ADC3XXX_LADC_VOL ADC3XXX_REG(0, 83)
+#define ADC3XXX_RADC_VOL ADC3XXX_REG(0, 84)
+#define ADC3XXX_ADC_PHASE_COMP ADC3XXX_REG(0, 85)
+#define ADC3XXX_LEFT_CHN_AGC_1 ADC3XXX_REG(0, 86)
+#define ADC3XXX_LEFT_CHN_AGC_2 ADC3XXX_REG(0, 87)
+#define ADC3XXX_LEFT_CHN_AGC_3 ADC3XXX_REG(0, 88)
+#define ADC3XXX_LEFT_CHN_AGC_4 ADC3XXX_REG(0, 89)
+#define ADC3XXX_LEFT_CHN_AGC_5 ADC3XXX_REG(0, 90)
+#define ADC3XXX_LEFT_CHN_AGC_6 ADC3XXX_REG(0, 91)
+#define ADC3XXX_LEFT_CHN_AGC_7 ADC3XXX_REG(0, 92)
+#define ADC3XXX_LEFT_AGC_GAIN ADC3XXX_REG(0, 93)
+#define ADC3XXX_RIGHT_CHN_AGC_1 ADC3XXX_REG(0, 94)
+#define ADC3XXX_RIGHT_CHN_AGC_2 ADC3XXX_REG(0, 95)
+#define ADC3XXX_RIGHT_CHN_AGC_3 ADC3XXX_REG(0, 96)
+#define ADC3XXX_RIGHT_CHN_AGC_4 ADC3XXX_REG(0, 97)
+#define ADC3XXX_RIGHT_CHN_AGC_5 ADC3XXX_REG(0, 98)
+#define ADC3XXX_RIGHT_CHN_AGC_6 ADC3XXX_REG(0, 99)
+#define ADC3XXX_RIGHT_CHN_AGC_7 ADC3XXX_REG(0, 100)
+#define ADC3XXX_RIGHT_AGC_GAIN ADC3XXX_REG(0, 101)
+/* 102-127 Reserved */
+
+/*
+ * Page 1 registers.
+ */
+
+/* 1-25 Reserved */
+#define ADC3XXX_DITHER_CTRL ADC3XXX_REG(1, 26)
+/* 27-50 Reserved */
+#define ADC3XXX_MICBIAS_CTRL ADC3XXX_REG(1, 51)
+#define ADC3XXX_LEFT_PGA_SEL_1 ADC3XXX_REG(1, 52)
+/* 53 Reserved */
+#define ADC3XXX_LEFT_PGA_SEL_2 ADC3XXX_REG(1, 54)
+#define ADC3XXX_RIGHT_PGA_SEL_1 ADC3XXX_REG(1, 55)
+#define ADC3XXX_RIGHT_PGA_SEL_2 ADC3XXX_REG(1, 57)
+#define ADC3XXX_LEFT_APGA_CTRL ADC3XXX_REG(1, 59)
+#define ADC3XXX_RIGHT_APGA_CTRL ADC3XXX_REG(1, 60)
+#define ADC3XXX_LOW_CURRENT_MODES ADC3XXX_REG(1, 61)
+#define ADC3XXX_ANALOG_PGA_FLAGS ADC3XXX_REG(1, 62)
+/* 63-127 Reserved */
+
+/*
+ * Page 4 registers. First page of coefficient memory for the miniDSP.
+ */
+#define ADC3XXX_LEFT_ADC_IIR_COEFF_N0_MSB ADC3XXX_REG(4, 8)
+#define ADC3XXX_LEFT_ADC_IIR_COEFF_N0_LSB ADC3XXX_REG(4, 9)
+#define ADC3XXX_LEFT_ADC_IIR_COEFF_N1_MSB ADC3XXX_REG(4, 10)
+#define ADC3XXX_LEFT_ADC_IIR_COEFF_N1_LSB ADC3XXX_REG(4, 11)
+#define ADC3XXX_LEFT_ADC_IIR_COEFF_D1_MSB ADC3XXX_REG(4, 12)
+#define ADC3XXX_LEFT_ADC_IIR_COEFF_D1_LSB ADC3XXX_REG(4, 13)
+
+#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_MSB ADC3XXX_REG(4, 72)
+#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_LSB ADC3XXX_REG(4, 73)
+#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N1_MSB ADC3XXX_REG(4, 74)
+#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N1_LSB ADC3XXX_REG(4, 75)
+#define ADC3XXX_RIGHT_ADC_IIR_COEFF_D1_MSB ADC3XXX_REG(4, 76)
+#define ADC3XXX_RIGHT_ADC_IIR_COEFF_D1_LSB ADC3XXX_REG(4, 77)
+
+/*
+ * Register bits.
+ */
+
+/* PLL Enable bits */
+#define ADC3XXX_ENABLE_PLL_SHIFT 7
+#define ADC3XXX_ENABLE_PLL (1 << ADC3XXX_ENABLE_PLL_SHIFT)
+#define ADC3XXX_ENABLE_NADC_SHIFT 7
+#define ADC3XXX_ENABLE_NADC (1 << ADC3XXX_ENABLE_NADC_SHIFT)
+#define ADC3XXX_ENABLE_MADC_SHIFT 7
+#define ADC3XXX_ENABLE_MADC (1 << ADC3XXX_ENABLE_MADC_SHIFT)
+#define ADC3XXX_ENABLE_BCLK_SHIFT 7
+#define ADC3XXX_ENABLE_BCLK (1 << ADC3XXX_ENABLE_BCLK_SHIFT)
+
+/* Power bits */
+#define ADC3XXX_LADC_PWR_ON 0x80
+#define ADC3XXX_RADC_PWR_ON 0x40
+
+#define ADC3XXX_SOFT_RESET 0x01
+#define ADC3XXX_BCLK_MASTER 0x08
+#define ADC3XXX_WCLK_MASTER 0x04
+
+/* Interface register masks */
+#define ADC3XXX_FORMAT_MASK 0xc0
+#define ADC3XXX_FORMAT_SHIFT 6
+#define ADC3XXX_WLENGTH_MASK 0x30
+#define ADC3XXX_WLENGTH_SHIFT 4
+#define ADC3XXX_CLKDIR_MASK 0x0c
+#define ADC3XXX_CLKDIR_SHIFT 2
+
+/* Interface register bit patterns */
+#define ADC3XXX_FORMAT_I2S (0 << ADC3XXX_FORMAT_SHIFT)
+#define ADC3XXX_FORMAT_DSP (1 << ADC3XXX_FORMAT_SHIFT)
+#define ADC3XXX_FORMAT_RJF (2 << ADC3XXX_FORMAT_SHIFT)
+#define ADC3XXX_FORMAT_LJF (3 << ADC3XXX_FORMAT_SHIFT)
+
+#define ADC3XXX_IFACE_16BITS (0 << ADC3XXX_WLENGTH_SHIFT)
+#define ADC3XXX_IFACE_20BITS (1 << ADC3XXX_WLENGTH_SHIFT)
+#define ADC3XXX_IFACE_24BITS (2 << ADC3XXX_WLENGTH_SHIFT)
+#define ADC3XXX_IFACE_32BITS (3 << ADC3XXX_WLENGTH_SHIFT)
+
+/* PLL P/R bit offsets */
+#define ADC3XXX_PLLP_SHIFT 4
+#define ADC3XXX_PLLR_SHIFT 0
+#define ADC3XXX_PLL_PR_MASK 0x7f
+#define ADC3XXX_PLLJ_MASK 0x3f
+#define ADC3XXX_PLLD_MSB_MASK 0x3f
+#define ADC3XXX_PLLD_LSB_MASK 0xff
+#define ADC3XXX_NADC_MASK 0x7f
+#define ADC3XXX_MADC_MASK 0x7f
+#define ADC3XXX_AOSR_MASK 0xff
+#define ADC3XXX_IADC_MASK 0xff
+#define ADC3XXX_BDIV_MASK 0x7f
+
+/* PLL_CLKIN bits */
+#define ADC3XXX_PLL_CLKIN_SHIFT 2
+#define ADC3XXX_PLL_CLKIN_MCLK 0x0
+#define ADC3XXX_PLL_CLKIN_BCLK 0x1
+#define ADC3XXX_PLL_CLKIN_ZERO 0x3
+
+/* CODEC_CLKIN bits */
+#define ADC3XXX_CODEC_CLKIN_SHIFT 0
+#define ADC3XXX_CODEC_CLKIN_MCLK 0x0
+#define ADC3XXX_CODEC_CLKIN_BCLK 0x1
+#define ADC3XXX_CODEC_CLKIN_PLL_CLK 0x3
+
+#define ADC3XXX_USE_PLL ((ADC3XXX_PLL_CLKIN_MCLK << ADC3XXX_PLL_CLKIN_SHIFT) | \
+ (ADC3XXX_CODEC_CLKIN_PLL_CLK << ADC3XXX_CODEC_CLKIN_SHIFT))
+#define ADC3XXX_NO_PLL ((ADC3XXX_PLL_CLKIN_ZERO << ADC3XXX_PLL_CLKIN_SHIFT) | \
+ (ADC3XXX_CODEC_CLKIN_MCLK << ADC3XXX_CODEC_CLKIN_SHIFT))
+
+/* Analog PGA control bits */
+#define ADC3XXX_LPGA_MUTE 0x80
+#define ADC3XXX_RPGA_MUTE 0x80
+
+#define ADC3XXX_LPGA_GAIN_MASK 0x7f
+#define ADC3XXX_RPGA_GAIN_MASK 0x7f
+
+/* ADC current modes */
+#define ADC3XXX_ADC_LOW_CURR_MODE 0x01
+
+/* Left ADC Input selection bits */
+#define ADC3XXX_LCH_SEL1_SHIFT 0
+#define ADC3XXX_LCH_SEL2_SHIFT 2
+#define ADC3XXX_LCH_SEL3_SHIFT 4
+#define ADC3XXX_LCH_SEL4_SHIFT 6
+
+#define ADC3XXX_LCH_SEL1X_SHIFT 0
+#define ADC3XXX_LCH_SEL2X_SHIFT 2
+#define ADC3XXX_LCH_SEL3X_SHIFT 4
+#define ADC3XXX_LCH_COMMON_MODE 0x40
+#define ADC3XXX_BYPASS_LPGA 0x80
+
+/* Right ADC Input selection bits */
+#define ADC3XXX_RCH_SEL1_SHIFT 0
+#define ADC3XXX_RCH_SEL2_SHIFT 2
+#define ADC3XXX_RCH_SEL3_SHIFT 4
+#define ADC3XXX_RCH_SEL4_SHIFT 6
+
+#define ADC3XXX_RCH_SEL1X_SHIFT 0
+#define ADC3XXX_RCH_SEL2X_SHIFT 2
+#define ADC3XXX_RCH_SEL3X_SHIFT 4
+#define ADC3XXX_RCH_COMMON_MODE 0x40
+#define ADC3XXX_BYPASS_RPGA 0x80
+
+/* MICBIAS control bits */
+#define ADC3XXX_MICBIAS_MASK 0x3
+#define ADC3XXX_MICBIAS1_SHIFT 5
+#define ADC3XXX_MICBIAS2_SHIFT 3
+
+#define ADC3XXX_ADC_MAX_VOLUME 64
+#define ADC3XXX_ADC_POS_VOL 24
+
+/* GPIO control bits (GPIO1_CTRL and GPIO2_CTRL) */
+#define ADC3XXX_GPIO_CTRL_CFG_MASK 0x3c
+#define ADC3XXX_GPIO_CTRL_CFG_SHIFT 2
+#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK 0x01
+#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT 0
+#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_MASK 0x02
+#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_SHIFT 1
+
+enum adc3xxx_type {
+ ADC3001 = 0,
+ ADC3101
+};
+
+struct adc3xxx {
+ struct device *dev;
+ enum adc3xxx_type type;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct gpio_desc *rst_pin;
+ unsigned int pll_mode;
+ unsigned int sysclk;
+ unsigned int gpio_cfg[ADC3XXX_GPIO_PINS]; /* value+1 (0 => not set) */
+ unsigned int micbias_gpo[ADC3XXX_MICBIAS_PINS]; /* 1 => pin is GPO */
+ unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS];
+ int master;
+ u8 page_no;
+ int use_pll;
+ struct gpio_chip gpio_chip;
+};
+
+static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIO_PINS] = {
+ ADC3XXX_GPIO1_CTRL,
+ ADC3XXX_GPIO2_CTRL
+};
+
+static const unsigned int adc3xxx_micbias_shift[ADC3XXX_MICBIAS_PINS] = {
+ ADC3XXX_MICBIAS1_SHIFT,
+ ADC3XXX_MICBIAS2_SHIFT
+};
+
+static const struct reg_default adc3xxx_defaults[] = {
+ /* Page 0 */
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x00 },
+ { 4, 0x00 }, { 5, 0x11 }, { 6, 0x04 }, { 7, 0x00 },
+ { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x00 },
+ { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x00 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x01 }, { 19, 0x01 },
+ { 20, 0x80 }, { 21, 0x80 }, { 22, 0x04 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x01 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x02 }, { 30, 0x01 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x10 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 }, { 37, 0x00 }, { 38, 0x02 }, { 39, 0x00 },
+ { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x00 },
+ { 44, 0x00 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 },
+ { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x00 },
+ { 52, 0x00 }, { 53, 0x12 }, { 54, 0x00 }, { 55, 0x00 },
+ { 56, 0x00 }, { 57, 0x00 }, { 58, 0x00 }, { 59, 0x44 },
+ { 60, 0x00 }, { 61, 0x01 }, { 62, 0x00 }, { 63, 0x00 },
+ { 64, 0x00 }, { 65, 0x00 }, { 66, 0x00 }, { 67, 0x00 },
+ { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 },
+ { 72, 0x00 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 },
+ { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 },
+ { 80, 0x00 }, { 81, 0x00 }, { 82, 0x88 }, { 83, 0x00 },
+ { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 },
+ { 88, 0x7f }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 },
+ { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 },
+ { 96, 0x7f }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 },
+ { 100, 0x00 }, { 101, 0x00 }, { 102, 0x00 }, { 103, 0x00 },
+ { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 },
+ { 108, 0x00 }, { 109, 0x00 }, { 110, 0x00 }, { 111, 0x00 },
+ { 112, 0x00 }, { 113, 0x00 }, { 114, 0x00 }, { 115, 0x00 },
+ { 116, 0x00 }, { 117, 0x00 }, { 118, 0x00 }, { 119, 0x00 },
+ { 120, 0x00 }, { 121, 0x00 }, { 122, 0x00 }, { 123, 0x00 },
+ { 124, 0x00 }, { 125, 0x00 }, { 126, 0x00 }, { 127, 0x00 },
+
+ /* Page 1 */
+ { 128, 0x00 }, { 129, 0x00 }, { 130, 0x00 }, { 131, 0x00 },
+ { 132, 0x00 }, { 133, 0x00 }, { 134, 0x00 }, { 135, 0x00 },
+ { 136, 0x00 }, { 137, 0x00 }, { 138, 0x00 }, { 139, 0x00 },
+ { 140, 0x00 }, { 141, 0x00 }, { 142, 0x00 }, { 143, 0x00 },
+ { 144, 0x00 }, { 145, 0x00 }, { 146, 0x00 }, { 147, 0x00 },
+ { 148, 0x00 }, { 149, 0x00 }, { 150, 0x00 }, { 151, 0x00 },
+ { 152, 0x00 }, { 153, 0x00 }, { 154, 0x00 }, { 155, 0x00 },
+ { 156, 0x00 }, { 157, 0x00 }, { 158, 0x00 }, { 159, 0x00 },
+ { 160, 0x00 }, { 161, 0x00 }, { 162, 0x00 }, { 163, 0x00 },
+ { 164, 0x00 }, { 165, 0x00 }, { 166, 0x00 }, { 167, 0x00 },
+ { 168, 0x00 }, { 169, 0x00 }, { 170, 0x00 }, { 171, 0x00 },
+ { 172, 0x00 }, { 173, 0x00 }, { 174, 0x00 }, { 175, 0x00 },
+ { 176, 0x00 }, { 177, 0x00 }, { 178, 0x00 }, { 179, 0x00 },
+ { 180, 0xff }, { 181, 0x00 }, { 182, 0x3f }, { 183, 0xff },
+ { 184, 0x00 }, { 185, 0x3f }, { 186, 0x00 }, { 187, 0x80 },
+ { 188, 0x80 }, { 189, 0x00 }, { 190, 0x00 }, { 191, 0x00 },
+
+ /* Page 4 */
+ { 1024, 0x00 }, { 1026, 0x01 }, { 1027, 0x17 },
+ { 1028, 0x01 }, { 1029, 0x17 }, { 1030, 0x7d }, { 1031, 0xd3 },
+ { 1032, 0x7f }, { 1033, 0xff }, { 1034, 0x00 }, { 1035, 0x00 },
+ { 1036, 0x00 }, { 1037, 0x00 }, { 1038, 0x7f }, { 1039, 0xff },
+ { 1040, 0x00 }, { 1041, 0x00 }, { 1042, 0x00 }, { 1043, 0x00 },
+ { 1044, 0x00 }, { 1045, 0x00 }, { 1046, 0x00 }, { 1047, 0x00 },
+ { 1048, 0x7f }, { 1049, 0xff }, { 1050, 0x00 }, { 1051, 0x00 },
+ { 1052, 0x00 }, { 1053, 0x00 }, { 1054, 0x00 }, { 1055, 0x00 },
+ { 1056, 0x00 }, { 1057, 0x00 }, { 1058, 0x7f }, { 1059, 0xff },
+ { 1060, 0x00 }, { 1061, 0x00 }, { 1062, 0x00 }, { 1063, 0x00 },
+ { 1064, 0x00 }, { 1065, 0x00 }, { 1066, 0x00 }, { 1067, 0x00 },
+ { 1068, 0x7f }, { 1069, 0xff }, { 1070, 0x00 }, { 1071, 0x00 },
+ { 1072, 0x00 }, { 1073, 0x00 }, { 1074, 0x00 }, { 1075, 0x00 },
+ { 1076, 0x00 }, { 1077, 0x00 }, { 1078, 0x7f }, { 1079, 0xff },
+ { 1080, 0x00 }, { 1081, 0x00 }, { 1082, 0x00 }, { 1083, 0x00 },
+ { 1084, 0x00 }, { 1085, 0x00 }, { 1086, 0x00 }, { 1087, 0x00 },
+ { 1088, 0x00 }, { 1089, 0x00 }, { 1090, 0x00 }, { 1091, 0x00 },
+ { 1092, 0x00 }, { 1093, 0x00 }, { 1094, 0x00 }, { 1095, 0x00 },
+ { 1096, 0x00 }, { 1097, 0x00 }, { 1098, 0x00 }, { 1099, 0x00 },
+ { 1100, 0x00 }, { 1101, 0x00 }, { 1102, 0x00 }, { 1103, 0x00 },
+ { 1104, 0x00 }, { 1105, 0x00 }, { 1106, 0x00 }, { 1107, 0x00 },
+ { 1108, 0x00 }, { 1109, 0x00 }, { 1110, 0x00 }, { 1111, 0x00 },
+ { 1112, 0x00 }, { 1113, 0x00 }, { 1114, 0x00 }, { 1115, 0x00 },
+ { 1116, 0x00 }, { 1117, 0x00 }, { 1118, 0x00 }, { 1119, 0x00 },
+ { 1120, 0x00 }, { 1121, 0x00 }, { 1122, 0x00 }, { 1123, 0x00 },
+ { 1124, 0x00 }, { 1125, 0x00 }, { 1126, 0x00 }, { 1127, 0x00 },
+ { 1128, 0x00 }, { 1129, 0x00 }, { 1130, 0x00 }, { 1131, 0x00 },
+ { 1132, 0x00 }, { 1133, 0x00 }, { 1134, 0x00 }, { 1135, 0x00 },
+ { 1136, 0x00 }, { 1137, 0x00 }, { 1138, 0x00 }, { 1139, 0x00 },
+ { 1140, 0x00 }, { 1141, 0x00 }, { 1142, 0x00 }, { 1143, 0x00 },
+ { 1144, 0x00 }, { 1145, 0x00 }, { 1146, 0x00 }, { 1147, 0x00 },
+ { 1148, 0x00 }, { 1149, 0x00 }, { 1150, 0x00 }, { 1151, 0x00 },
+};
+
+static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADC3XXX_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg adc3xxx_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 5 * ADC3XXX_PAGE_SIZE,
+ .selector_reg = ADC3XXX_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = ADC3XXX_PAGE_SIZE,
+ }
+};
+
+static const struct regmap_config adc3xxx_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = adc3xxx_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adc3xxx_defaults),
+
+ .volatile_reg = adc3xxx_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .ranges = adc3xxx_ranges,
+ .num_ranges = ARRAY_SIZE(adc3xxx_ranges),
+ .max_register = 5 * ADC3XXX_PAGE_SIZE,
+};
+
+struct adc3xxx_rate_divs {
+ u32 mclk;
+ u32 rate;
+ u8 pll_p;
+ u8 pll_r;
+ u8 pll_j;
+ u16 pll_d;
+ u8 nadc;
+ u8 madc;
+ u8 aosr;
+};
+
+/*
+ * PLL and Clock settings.
+ * If p member is 0, PLL is not used.
+ * The order of the entries in this table have the PLL entries before
+ * the non-PLL entries, so that the PLL modes are preferred unless
+ * the PLL mode setting says otherwise.
+ */
+static const struct adc3xxx_rate_divs adc3xxx_divs[] = {
+ /* mclk, rate, p, r, j, d, nadc, madc, aosr */
+ /* 8k rate */
+ { 12000000, 8000, 1, 1, 7, 1680, 42, 2, 128 },
+ { 12288000, 8000, 1, 1, 7, 0000, 42, 2, 128 },
+ /* 11.025k rate */
+ { 12000000, 11025, 1, 1, 6, 8208, 29, 2, 128 },
+ /* 16k rate */
+ { 12000000, 16000, 1, 1, 7, 1680, 21, 2, 128 },
+ { 12288000, 16000, 1, 1, 7, 0000, 21, 2, 128 },
+ /* 22.05k rate */
+ { 12000000, 22050, 1, 1, 7, 560, 15, 2, 128 },
+ /* 32k rate */
+ { 12000000, 32000, 1, 1, 8, 1920, 12, 2, 128 },
+ { 12288000, 32000, 1, 1, 8, 0000, 12, 2, 128 },
+ /* 44.1k rate */
+ { 12000000, 44100, 1, 1, 7, 5264, 8, 2, 128 },
+ /* 48k rate */
+ { 12000000, 48000, 1, 1, 7, 1680, 7, 2, 128 },
+ { 12288000, 48000, 1, 1, 7, 0000, 7, 2, 128 },
+ { 24576000, 48000, 1, 1, 3, 5000, 7, 2, 128 }, /* With PLL */
+ { 24576000, 48000, 0, 0, 0, 0000, 2, 2, 128 }, /* Without PLL */
+ /* 88.2k rate */
+ { 12000000, 88200, 1, 1, 7, 5264, 4, 4, 64 },
+ /* 96k rate */
+ { 12000000, 96000, 1, 1, 8, 1920, 4, 4, 64 },
+};
+
+static int adc3xxx_get_divs(struct device *dev, int mclk, int rate, int pll_mode)
+{
+ int i;
+
+ dev_dbg(dev, "mclk = %d, rate = %d, clock mode %u\n",
+ mclk, rate, pll_mode);
+ for (i = 0; i < ARRAY_SIZE(adc3xxx_divs); i++) {
+ const struct adc3xxx_rate_divs *mode = &adc3xxx_divs[i];
+
+ /* Skip this entry if it doesn't fulfill the intended clock
+ * mode requirement. We consider anything besides the two
+ * modes below to be the same as ADC3XXX_PLL_AUTO.
+ */
+ if ((pll_mode == ADC3XXX_PLL_BYPASS && mode->pll_p) ||
+ (pll_mode == ADC3XXX_PLL_ENABLE && !mode->pll_p))
+ continue;
+
+ if (mode->rate == rate && mode->mclk == mclk)
+ return i;
+ }
+
+ dev_info(dev, "Master clock rate %d and sample rate %d is not supported\n",
+ mclk, rate);
+ return -EINVAL;
+}
+
+static int adc3xxx_pll_delay(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ /* 10msec delay needed after PLL power-up to allow
+ * PLL and dividers to stabilize (datasheet p13).
+ */
+ usleep_range(10000, 20000);
+
+ return 0;
+}
+
+static int adc3xxx_coefficient_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int numcoeff = kcontrol->private_value >> 16;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = numcoeff;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff; /* all coefficients are 16 bit */
+ return 0;
+}
+
+static int adc3xxx_coefficient_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int numcoeff = kcontrol->private_value >> 16;
+ int reg = kcontrol->private_value & 0xffff;
+ int index = 0;
+
+ for (index = 0; index < numcoeff; index++) {
+ unsigned int value_msb, value_lsb, value;
+
+ value_msb = snd_soc_component_read(component, reg++);
+ if ((int)value_msb < 0)
+ return (int)value_msb;
+
+ value_lsb = snd_soc_component_read(component, reg++);
+ if ((int)value_lsb < 0)
+ return (int)value_lsb;
+
+ value = (value_msb << 8) | value_lsb;
+ ucontrol->value.integer.value[index] = value;
+ }
+
+ return 0;
+}
+
+static int adc3xxx_coefficient_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int numcoeff = kcontrol->private_value >> 16;
+ int reg = kcontrol->private_value & 0xffff;
+ int index = 0;
+ int ret;
+
+ for (index = 0; index < numcoeff; index++) {
+ unsigned int value = ucontrol->value.integer.value[index];
+ unsigned int value_msb = (value >> 8) & 0xff;
+ unsigned int value_lsb = value & 0xff;
+
+ ret = snd_soc_component_write(component, reg++, value_msb);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_component_write(component, reg++, value_lsb);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* All on-chip filters have coefficients which are expressed in terms of
+ * 16 bit values, so represent them as strings of 16-bit integers.
+ */
+#define TI_COEFFICIENTS(xname, reg, numcoeffs) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = adc3xxx_coefficient_info, \
+ .get = adc3xxx_coefficient_get,\
+ .put = adc3xxx_coefficient_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .private_value = reg | (numcoeffs << 16) \
+}
+
+static const char * const adc_softstepping_text[] = { "1 step", "2 step", "off" };
+static SOC_ENUM_SINGLE_DECL(adc_softstepping_enum, ADC3XXX_ADC_DIGITAL, 0,
+ adc_softstepping_text);
+
+static const char * const multiplier_text[] = { "1", "2", "4", "8", "16", "32", "64", "128" };
+static SOC_ENUM_SINGLE_DECL(left_agc_attack_mult_enum,
+ ADC3XXX_LEFT_CHN_AGC_4, 0, multiplier_text);
+static SOC_ENUM_SINGLE_DECL(right_agc_attack_mult_enum,
+ ADC3XXX_RIGHT_CHN_AGC_4, 0, multiplier_text);
+static SOC_ENUM_SINGLE_DECL(left_agc_decay_mult_enum,
+ ADC3XXX_LEFT_CHN_AGC_5, 0, multiplier_text);
+static SOC_ENUM_SINGLE_DECL(right_agc_decay_mult_enum,
+ ADC3XXX_RIGHT_CHN_AGC_5, 0, multiplier_text);
+
+static const char * const dither_dc_offset_text[] = {
+ "0mV", "15mV", "30mV", "45mV", "60mV", "75mV", "90mV", "105mV",
+ "-15mV", "-30mV", "-45mV", "-60mV", "-75mV", "-90mV", "-105mV"
+};
+static const unsigned int dither_dc_offset_values[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15
+};
+static SOC_VALUE_ENUM_DOUBLE_DECL(dither_dc_offset_enum,
+ ADC3XXX_DITHER_CTRL,
+ 4, 0, 0xf, dither_dc_offset_text,
+ dither_dc_offset_values);
+
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_fine_tlv, -40, 10, 0);
+/* AGC target: 8 values: -5.5, -8, -10, -12, -14, -17, -20, -24 dB */
+/* It would be nice to declare these in the order above, but empirically
+ * TLV_DB_SCALE_ITEM doesn't take lightly to the increment (second) parameter
+ * being negative, despite there being examples to the contrary in other
+ * drivers. So declare these in the order from lowest to highest, and
+ * set the invert flag in the SOC_DOUBLE_R_TLV declaration instead.
+ */
+static const DECLARE_TLV_DB_RANGE(agc_target_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-2400, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(-2000, 300, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(-550, 0, 0));
+/* Since the 'disabled' value (mute) is at the highest value in the dB
+ * range (i.e. just before -32 dB) rather than the lowest, we need to resort
+ * to using a TLV_DB_RANGE in order to get the mute value in the right place.
+ */
+static const DECLARE_TLV_DB_RANGE(agc_thresh_tlv,
+ 0, 30, TLV_DB_SCALE_ITEM(-9000, 200, 0),
+ 31, 31, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */
+/* AGC hysteresis: 4 values: 1, 2, 4 dB, disabled (= mute) */
+static const DECLARE_TLV_DB_RANGE(agc_hysteresis_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(400, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */
+static const DECLARE_TLV_DB_SCALE(agc_max_tlv, 0, 50, 0);
+/* Input attenuation: -6 dB or 0 dB */
+static const DECLARE_TLV_DB_SCALE(input_attenuation_tlv, -600, 600, 0);
+
+static const struct snd_kcontrol_new adc3xxx_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADC3XXX_LEFT_APGA_CTRL,
+ ADC3XXX_RIGHT_APGA_CTRL, 0, 80, 0, pga_tlv),
+ SOC_DOUBLE("PGA Capture Switch", ADC3XXX_ADC_FGA, 7, 3, 1, 1),
+ SOC_DOUBLE_R("AGC Capture Switch", ADC3XXX_LEFT_CHN_AGC_1,
+ ADC3XXX_RIGHT_CHN_AGC_1, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("AGC Target Level Capture Volume", ADC3XXX_LEFT_CHN_AGC_1,
+ ADC3XXX_RIGHT_CHN_AGC_2, 4, 0x07, 1, agc_target_tlv),
+ SOC_DOUBLE_R_TLV("AGC Noise Threshold Capture Volume", ADC3XXX_LEFT_CHN_AGC_2,
+ ADC3XXX_RIGHT_CHN_AGC_2, 1, 0x1f, 1, agc_thresh_tlv),
+ SOC_DOUBLE_R_TLV("AGC Hysteresis Capture Volume", ADC3XXX_LEFT_CHN_AGC_2,
+ ADC3XXX_RIGHT_CHN_AGC_2, 6, 3, 0, agc_hysteresis_tlv),
+ SOC_DOUBLE_R("AGC Clip Stepping Capture Switch", ADC3XXX_LEFT_CHN_AGC_2,
+ ADC3XXX_RIGHT_CHN_AGC_2, 0, 1, 0),
+ /*
+ * Oddly enough, the data sheet says the default value
+ * for the left/right AGC maximum gain register field
+ * (ADC3XXX_LEFT/RIGHT_CHN_AGC_3 bits 0..6) is 0x7f = 127
+ * (verified empirically) even though this value (indeed, above
+ * 0x50) is specified as 'Reserved. Do not use.' in the accompanying
+ * table in the data sheet.
+ */
+ SOC_DOUBLE_R_TLV("AGC Maximum Capture Volume", ADC3XXX_LEFT_CHN_AGC_3,
+ ADC3XXX_RIGHT_CHN_AGC_3, 0, 0x50, 0, agc_max_tlv),
+ SOC_DOUBLE_R("AGC Attack Time", ADC3XXX_LEFT_CHN_AGC_4,
+ ADC3XXX_RIGHT_CHN_AGC_4, 3, 0x1f, 0),
+ /* Would like to have the multipliers as LR pairs, but there is
+ * no SOC_ENUM_foo which accepts two values in separate registers.
+ */
+ SOC_ENUM("AGC Left Attack Time Multiplier", left_agc_attack_mult_enum),
+ SOC_ENUM("AGC Right Attack Time Multiplier", right_agc_attack_mult_enum),
+ SOC_DOUBLE_R("AGC Decay Time", ADC3XXX_LEFT_CHN_AGC_5,
+ ADC3XXX_RIGHT_CHN_AGC_5, 3, 0x1f, 0),
+ SOC_ENUM("AGC Left Decay Time Multiplier", left_agc_decay_mult_enum),
+ SOC_ENUM("AGC Right Decay Time Multiplier", right_agc_decay_mult_enum),
+ SOC_DOUBLE_R("AGC Noise Debounce", ADC3XXX_LEFT_CHN_AGC_6,
+ ADC3XXX_RIGHT_CHN_AGC_6, 0, 0x1f, 0),
+ SOC_DOUBLE_R("AGC Signal Debounce", ADC3XXX_LEFT_CHN_AGC_7,
+ ADC3XXX_RIGHT_CHN_AGC_7, 0, 0x0f, 0),
+ /* Read only register */
+ SOC_DOUBLE_R_S_TLV("AGC Applied Capture Volume", ADC3XXX_LEFT_AGC_GAIN,
+ ADC3XXX_RIGHT_AGC_GAIN, 0, -24, 40, 6, 0, adc_tlv),
+ /* ADC soft stepping */
+ SOC_ENUM("ADC Soft Stepping", adc_softstepping_enum),
+ /* Left/Right Input attenuation */
+ SOC_SINGLE_TLV("Left Input IN_1L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input IN_2L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input IN_3L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input IN_1R Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input DIF_2L_3L Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input DIF_1L_1R Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Left Input DIF_2R_3R Capture Volume",
+ ADC3XXX_LEFT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_1R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_2R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_3R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input IN_1L Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input DIF_2R_3R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input DIF_1L_1R Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv),
+ SOC_SINGLE_TLV("Right Input DIF_2L_3L Capture Volume",
+ ADC3XXX_RIGHT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC Volume Control Capture Volume", ADC3XXX_LADC_VOL,
+ ADC3XXX_RADC_VOL, 0, -24, 40, 6, 0, adc_tlv),
+ /* Empirically, the following doesn't work the way it's supposed
+ * to. Values 0, -0.1, -0.2 and -0.3 dB result in the same level, and
+ * -0.4 dB drops about 0.12 dB on a specific chip.
+ */
+ SOC_DOUBLE_TLV("ADC Fine Volume Control Capture Volume", ADC3XXX_ADC_FGA,
+ 4, 0, 4, 1, adc_fine_tlv),
+ SOC_SINGLE("Left ADC Unselected CM Bias Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 6, 1, 0),
+ SOC_SINGLE("Right ADC Unselected CM Bias Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 6, 1, 0),
+ SOC_ENUM("Dither Control DC Offset", dither_dc_offset_enum),
+
+ /* Coefficient memory for miniDSP. */
+ /* For the default PRB_R1 processing block, the only available
+ * filter is the first order IIR.
+ */
+
+ TI_COEFFICIENTS("Left ADC IIR Coefficients N0 N1 D1",
+ ADC3XXX_LEFT_ADC_IIR_COEFF_N0_MSB, 3),
+
+ TI_COEFFICIENTS("Right ADC IIR Coefficients N0 N1 D1",
+ ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_MSB, 3),
+};
+
+/* Left input selection, Single Ended inputs and Differential inputs */
+static const struct snd_kcontrol_new left_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN_1L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 1, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_2L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_3L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_1, 7, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_1R Capture Switch",
+ ADC3XXX_LEFT_PGA_SEL_2, 1, 0x1, 1),
+};
+
+/* Right input selection, Single Ended inputs and Differential inputs */
+static const struct snd_kcontrol_new right_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN_1R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 1, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_2R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_3R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_1, 7, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 5, 0x1, 1),
+ SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 3, 0x1, 1),
+ SOC_DAPM_SINGLE("IN_1L Capture Switch",
+ ADC3XXX_RIGHT_PGA_SEL_2, 1, 0x1, 1),
+};
+
+/* Left Digital Mic input for left ADC */
+static const struct snd_kcontrol_new left_input_dmic_controls[] = {
+ SOC_DAPM_SINGLE("Left ADC Capture Switch",
+ ADC3XXX_ADC_DIGITAL, 3, 0x1, 0),
+};
+
+/* Right Digital Mic input for Right ADC */
+static const struct snd_kcontrol_new right_input_dmic_controls[] = {
+ SOC_DAPM_SINGLE("Right ADC Capture Switch",
+ ADC3XXX_ADC_DIGITAL, 2, 0x1, 0),
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget adc3xxx_dapm_widgets[] = {
+
+ /* Left Input Selection */
+ SND_SOC_DAPM_MIXER("Left Input", SND_SOC_NOPM, 0, 0,
+ &left_input_mixer_controls[0],
+ ARRAY_SIZE(left_input_mixer_controls)),
+ /* Right Input Selection */
+ SND_SOC_DAPM_MIXER("Right Input", SND_SOC_NOPM, 0, 0,
+ &right_input_mixer_controls[0],
+ ARRAY_SIZE(right_input_mixer_controls)),
+ /* PGA selection */
+ SND_SOC_DAPM_PGA("Left PGA", ADC3XXX_LEFT_APGA_CTRL, 7, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", ADC3XXX_RIGHT_APGA_CTRL, 7, 1, NULL, 0),
+
+ /* Digital Microphone Input Control for Left/Right ADC */
+ SND_SOC_DAPM_MIXER("Left DMic Input", SND_SOC_NOPM, 0, 0,
+ &left_input_dmic_controls[0],
+ ARRAY_SIZE(left_input_dmic_controls)),
+ SND_SOC_DAPM_MIXER("Right DMic Input", SND_SOC_NOPM, 0, 0,
+ &right_input_dmic_controls[0],
+ ARRAY_SIZE(right_input_dmic_controls)),
+
+ /* Left/Right ADC */
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ADC3XXX_ADC_DIGITAL, 7, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ADC3XXX_ADC_DIGITAL, 6, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("IN_1L"),
+ SND_SOC_DAPM_INPUT("IN_1R"),
+ SND_SOC_DAPM_INPUT("IN_2L"),
+ SND_SOC_DAPM_INPUT("IN_2R"),
+ SND_SOC_DAPM_INPUT("IN_3L"),
+ SND_SOC_DAPM_INPUT("IN_3R"),
+ SND_SOC_DAPM_INPUT("DIFL_1L_1R"),
+ SND_SOC_DAPM_INPUT("DIFL_2L_3L"),
+ SND_SOC_DAPM_INPUT("DIFL_2R_3R"),
+ SND_SOC_DAPM_INPUT("DIFR_1L_1R"),
+ SND_SOC_DAPM_INPUT("DIFR_2L_3L"),
+ SND_SOC_DAPM_INPUT("DIFR_2R_3R"),
+ SND_SOC_DAPM_INPUT("DMic_L"),
+ SND_SOC_DAPM_INPUT("DMic_R"),
+
+ /* Digital audio interface output */
+ SND_SOC_DAPM_AIF_OUT("AIF_OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Clocks */
+ SND_SOC_DAPM_SUPPLY("PLL_CLK", ADC3XXX_PLL_PROG_PR, ADC3XXX_ENABLE_PLL_SHIFT,
+ 0, adc3xxx_pll_delay, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("ADC_CLK", ADC3XXX_ADC_NADC, ADC3XXX_ENABLE_NADC_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK", ADC3XXX_ADC_MADC, ADC3XXX_ENABLE_MADC_SHIFT,
+ 0, NULL, 0),
+
+ /* This refers to the generated BCLK in master mode. */
+ SND_SOC_DAPM_SUPPLY("BCLK", ADC3XXX_BCLK_N_DIV, ADC3XXX_ENABLE_BCLK_SHIFT,
+ 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adc3xxx_intercon[] = {
+ /* Left input selection from switches */
+ { "Left Input", "IN_1L Capture Switch", "IN_1L" },
+ { "Left Input", "IN_2L Capture Switch", "IN_2L" },
+ { "Left Input", "IN_3L Capture Switch", "IN_3L" },
+ { "Left Input", "DIF_2L_3L Capture Switch", "DIFL_2L_3L" },
+ { "Left Input", "DIF_1L_1R Capture Switch", "DIFL_1L_1R" },
+ { "Left Input", "DIF_2R_3R Capture Switch", "DIFL_2R_3R" },
+ { "Left Input", "IN_1R Capture Switch", "IN_1R" },
+
+ /* Left input selection to left PGA */
+ { "Left PGA", NULL, "Left Input" },
+
+ /* Left PGA to left ADC */
+ { "Left ADC", NULL, "Left PGA" },
+
+ /* Right input selection from switches */
+ { "Right Input", "IN_1R Capture Switch", "IN_1R" },
+ { "Right Input", "IN_2R Capture Switch", "IN_2R" },
+ { "Right Input", "IN_3R Capture Switch", "IN_3R" },
+ { "Right Input", "DIF_2R_3R Capture Switch", "DIFR_2R_3R" },
+ { "Right Input", "DIF_1L_1R Capture Switch", "DIFR_1L_1R" },
+ { "Right Input", "DIF_2L_3L Capture Switch", "DIFR_2L_3L" },
+ { "Right Input", "IN_1L Capture Switch", "IN_1L" },
+
+ /* Right input selection to right PGA */
+ { "Right PGA", NULL, "Right Input" },
+
+ /* Right PGA to right ADC */
+ { "Right ADC", NULL, "Right PGA" },
+
+ /* Left DMic Input selection from switch */
+ { "Left DMic Input", "Left ADC Capture Switch", "DMic_L" },
+
+ /* Left DMic to left ADC */
+ { "Left ADC", NULL, "Left DMic Input" },
+
+ /* Right DMic Input selection from switch */
+ { "Right DMic Input", "Right ADC Capture Switch", "DMic_R" },
+
+ /* Right DMic to right ADC */
+ { "Right ADC", NULL, "Right DMic Input" },
+
+ /* ADC to AIF output */
+ { "AIF_OUT", NULL, "Left ADC" },
+ { "AIF_OUT", NULL, "Right ADC" },
+
+ /* Clocking */
+ { "ADC_MOD_CLK", NULL, "ADC_CLK" },
+ { "Left ADC", NULL, "ADC_MOD_CLK" },
+ { "Right ADC", NULL, "ADC_MOD_CLK" },
+
+ { "BCLK", NULL, "ADC_CLK" },
+};
+
+static const struct snd_soc_dapm_route adc3xxx_pll_intercon[] = {
+ { "ADC_CLK", NULL, "PLL_CLK" },
+};
+
+static const struct snd_soc_dapm_route adc3xxx_bclk_out_intercon[] = {
+ { "AIF_OUT", NULL, "BCLK" }
+};
+
+static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct adc3xxx *adc3xxx = gpiochip_get_data(chip);
+
+ if (offset >= ADC3XXX_GPIOS_MAX)
+ return -EINVAL;
+
+ if (offset < ADC3XXX_GPIO_PINS) {
+ /* GPIO1 is offset 0, GPIO2 is offset 1 */
+ /* We check here that the GPIO pins are either not configured
+ * in the DT, or that they purposely are set as outputs.
+ * (Input mode not yet implemented).
+ */
+ if (adc3xxx->gpio_cfg[offset] != 0 &&
+ adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1)
+ return -EINVAL;
+ } else if (offset >= ADC3XXX_GPIO_PINS && offset < ADC3XXX_GPIOS_MAX) {
+ /* MICBIAS1 is offset 2, MICBIAS2 is offset 3 */
+ /* We check here if the MICBIAS pins are in fact configured
+ * as GPOs.
+ */
+ if (!adc3xxx->micbias_gpo[offset - ADC3XXX_GPIO_PINS])
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adc3xxx_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct adc3xxx *adc3xxx = gpiochip_get_data(chip);
+
+ /* For the MICBIAS pins, they are by definition outputs. */
+ if (offset >= ADC3XXX_GPIO_PINS) {
+ unsigned int vg;
+ unsigned int micbias = offset - ADC3XXX_GPIO_PINS;
+
+ if (value)
+ vg = adc3xxx->micbias_vg[micbias];
+ else
+ vg = ADC3XXX_MICBIAS_OFF;
+ return regmap_update_bits(adc3xxx->regmap,
+ ADC3XXX_MICBIAS_CTRL,
+ ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias],
+ vg << adc3xxx_micbias_shift[micbias]);
+ }
+
+ /* Set GPIO output function. */
+ return regmap_update_bits(adc3xxx->regmap,
+ adc3xxx_gpio_ctrl_reg[offset],
+ ADC3XXX_GPIO_CTRL_CFG_MASK |
+ ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK,
+ ADC3XXX_GPIO_GPO << ADC3XXX_GPIO_CTRL_CFG_SHIFT |
+ !!value << ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT);
+}
+
+/* With only GPIO outputs configured, we never get the .direction_out call,
+ * so we set the output mode and output value in the same call. Hence
+ * .set in practice does the same thing as .direction_out .
+ */
+static int adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ return adc3xxx_gpio_direction_out(chip, offset, value);
+}
+
+/* Even though we only support GPIO output for now, some GPIO clients
+ * want to read the current pin state using the .get callback.
+ */
+static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct adc3xxx *adc3xxx = gpiochip_get_data(chip);
+ unsigned int regval;
+ int ret;
+
+ /* We only allow output pins, so just read the value prevously set. */
+ if (offset >= ADC3XXX_GPIO_PINS) {
+ /* MICBIAS pins */
+ unsigned int micbias = offset - ADC3XXX_GPIO_PINS;
+
+ ret = regmap_read(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, &regval);
+ if (ret)
+ return ret;
+ return ((regval >> adc3xxx_micbias_shift[micbias]) & ADC3XXX_MICBIAS_MASK) !=
+ ADC3XXX_MICBIAS_OFF;
+ }
+ ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], &regval);
+ if (ret)
+ return ret;
+ return !!(regval & ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK);
+}
+
+static const struct gpio_chip adc3xxx_gpio_chip = {
+ .label = "adc3xxx",
+ .owner = THIS_MODULE,
+ .request = adc3xxx_gpio_request,
+ .direction_output = adc3xxx_gpio_direction_out,
+ .set = adc3xxx_gpio_set,
+ .get = adc3xxx_gpio_get,
+ .can_sleep = 1,
+};
+
+static void adc3xxx_free_gpio(struct adc3xxx *adc3xxx)
+{
+#ifdef CONFIG_GPIOLIB
+ gpiochip_remove(&adc3xxx->gpio_chip);
+#endif
+}
+
+static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx)
+{
+ int gpio, micbias;
+ int ret;
+
+ adc3xxx->gpio_chip = adc3xxx_gpio_chip;
+ adc3xxx->gpio_chip.ngpio = ADC3XXX_GPIOS_MAX;
+ adc3xxx->gpio_chip.parent = adc3xxx->dev;
+ adc3xxx->gpio_chip.base = -1;
+
+ ret = gpiochip_add_data(&adc3xxx->gpio_chip, adc3xxx);
+ if (ret)
+ dev_err(adc3xxx->dev, "Failed to add gpios: %d\n", ret);
+
+ /* Set up potential GPIO configuration from the devicetree.
+ * This allows us to set up things which are not software
+ * controllable GPIOs, such as PDM microphone I/O,
+ */
+ for (gpio = 0; gpio < ADC3XXX_GPIO_PINS; gpio++) {
+ unsigned int cfg = adc3xxx->gpio_cfg[gpio];
+
+ if (cfg) {
+ cfg--; /* actual value to use is stored +1 */
+ regmap_update_bits(adc3xxx->regmap,
+ adc3xxx_gpio_ctrl_reg[gpio],
+ ADC3XXX_GPIO_CTRL_CFG_MASK,
+ cfg << ADC3XXX_GPIO_CTRL_CFG_SHIFT);
+ }
+ }
+
+ /* Set up micbias voltage. */
+ /* If pin is configured as GPO, set off initially. */
+ for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) {
+ unsigned int vg;
+
+ if (adc3xxx->micbias_gpo[micbias])
+ vg = ADC3XXX_MICBIAS_OFF;
+ else
+ vg = adc3xxx->micbias_vg[micbias];
+
+ regmap_update_bits(adc3xxx->regmap,
+ ADC3XXX_MICBIAS_CTRL,
+ ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias],
+ vg << adc3xxx_micbias_shift[micbias]);
+ }
+}
+
+static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx,
+ const char *propname, unsigned int *cfg)
+{
+ struct device *dev = adc3xxx->dev;
+ struct device_node *np = dev->of_node;
+ unsigned int val;
+
+ if (!of_property_read_u32(np, propname, &val)) {
+ if (val & ~15 || val == 7 || val >= 11) {
+ dev_err(dev, "Invalid property value for '%s'\n", propname);
+ return -EINVAL;
+ }
+ if (val == ADC3XXX_GPIO_GPI)
+ dev_warn(dev, "GPIO Input read not yet implemented\n");
+ *cfg = val + 1; /* 0 => not set up, all others shifted +1 */
+ }
+ return 0;
+}
+
+static int adc3xxx_parse_dt_micbias_gpo(struct adc3xxx *adc3xxx,
+ const char *propname,
+ unsigned int *cfg)
+{
+ struct device *dev = adc3xxx->dev;
+ struct device_node *np = dev->of_node;
+
+ *cfg = of_property_read_bool(np, propname);
+ return 0;
+}
+
+static int adc3xxx_parse_dt_micbias_vg(struct adc3xxx *adc3xxx,
+ const char *propname, unsigned int *vg)
+{
+ struct device *dev = adc3xxx->dev;
+ struct device_node *np = dev->of_node;
+ unsigned int val;
+
+ if (!of_property_read_u32(np, propname, &val)) {
+ if (val > ADC3XXX_MICBIAS_AVDD) {
+ dev_err(dev, "Invalid property value for '%s'\n", propname);
+ return -EINVAL;
+ }
+ *vg = val;
+ }
+ return 0;
+}
+
+static int adc3xxx_parse_pll_mode(uint32_t val, unsigned int *pll_mode)
+{
+ if (val != ADC3XXX_PLL_ENABLE && val != ADC3XXX_PLL_BYPASS &&
+ val != ADC3XXX_PLL_AUTO)
+ return -EINVAL;
+
+ *pll_mode = val;
+
+ return 0;
+}
+
+static void adc3xxx_setup_pll(struct snd_soc_component *component,
+ int div_entry)
+{
+ int i = div_entry;
+
+ /* P & R values */
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_PR,
+ (adc3xxx_divs[i].pll_p << ADC3XXX_PLLP_SHIFT) |
+ (adc3xxx_divs[i].pll_r << ADC3XXX_PLLR_SHIFT));
+ /* J value */
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_J,
+ adc3xxx_divs[i].pll_j & ADC3XXX_PLLJ_MASK);
+ /* D value */
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_LSB,
+ adc3xxx_divs[i].pll_d & ADC3XXX_PLLD_LSB_MASK);
+ snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_MSB,
+ (adc3xxx_divs[i].pll_d >> 8) & ADC3XXX_PLLD_MSB_MASK);
+}
+
+static int adc3xxx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(dai->component);
+ struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
+ int i, width = 16;
+ u8 iface_len, bdiv;
+
+ i = adc3xxx_get_divs(component->dev, adc3xxx->sysclk,
+ params_rate(params), adc3xxx->pll_mode);
+
+ if (i < 0)
+ return i;
+
+ /* select data word length */
+ switch (params_width(params)) {
+ case 16:
+ iface_len = ADC3XXX_IFACE_16BITS;
+ width = 16;
+ break;
+ case 20:
+ iface_len = ADC3XXX_IFACE_20BITS;
+ width = 20;
+ break;
+ case 24:
+ iface_len = ADC3XXX_IFACE_24BITS;
+ width = 24;
+ break;
+ case 32:
+ iface_len = ADC3XXX_IFACE_32BITS;
+ width = 32;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported serial data format\n");
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, ADC3XXX_INTERFACE_CTRL_1,
+ ADC3XXX_WLENGTH_MASK, iface_len);
+ if (adc3xxx_divs[i].pll_p) { /* If PLL used for this mode */
+ adc3xxx_setup_pll(component, i);
+ snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_USE_PLL);
+ if (!adc3xxx->use_pll) {
+ snd_soc_dapm_add_routes(dapm, adc3xxx_pll_intercon,
+ ARRAY_SIZE(adc3xxx_pll_intercon));
+ adc3xxx->use_pll = 1;
+ }
+ } else {
+ snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_NO_PLL);
+ if (adc3xxx->use_pll) {
+ snd_soc_dapm_del_routes(dapm, adc3xxx_pll_intercon,
+ ARRAY_SIZE(adc3xxx_pll_intercon));
+ adc3xxx->use_pll = 0;
+ }
+ }
+
+ /* NADC */
+ snd_soc_component_update_bits(component, ADC3XXX_ADC_NADC,
+ ADC3XXX_NADC_MASK, adc3xxx_divs[i].nadc);
+ /* MADC */
+ snd_soc_component_update_bits(component, ADC3XXX_ADC_MADC,
+ ADC3XXX_MADC_MASK, adc3xxx_divs[i].madc);
+ /* AOSR */
+ snd_soc_component_update_bits(component, ADC3XXX_ADC_AOSR,
+ ADC3XXX_AOSR_MASK, adc3xxx_divs[i].aosr);
+ /* BDIV N Value */
+ /* BCLK is (by default) set up to be derived from ADC_CLK */
+ bdiv = (adc3xxx_divs[i].aosr * adc3xxx_divs[i].madc) / (2 * width);
+ snd_soc_component_update_bits(component, ADC3XXX_BCLK_N_DIV,
+ ADC3XXX_BDIV_MASK, bdiv);
+
+ return 0;
+}
+
+static const char *adc3xxx_pll_mode_text(int pll_mode)
+{
+ switch (pll_mode) {
+ case ADC3XXX_PLL_AUTO:
+ return "PLL auto";
+ case ADC3XXX_PLL_ENABLE:
+ return "PLL enable";
+ case ADC3XXX_PLL_BYPASS:
+ return "PLL bypass";
+ default:
+ break;
+ }
+
+ return "PLL unknown";
+}
+
+static int adc3xxx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = adc3xxx_parse_pll_mode(clk_id, &adc3xxx->pll_mode);
+ if (ret < 0)
+ return ret;
+
+ adc3xxx->sysclk = freq;
+ dev_dbg(component->dev, "Set sysclk to %u Hz, %s\n",
+ freq, adc3xxx_pll_mode_text(adc3xxx->pll_mode));
+ return 0;
+}
+
+static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component);
+ u8 clkdir = 0, format = 0;
+ int master = 0;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ master = 1;
+ clkdir = ADC3XXX_BCLK_MASTER | ADC3XXX_WCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ master = 0;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clock setup\n");
+ return -EINVAL;
+ }
+
+ /*
+ * match both interface format and signal polarities since they
+ * are fixed
+ */
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF:
+ format = ADC3XXX_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF:
+ format = ADC3XXX_FORMAT_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF:
+ format = ADC3XXX_FORMAT_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF:
+ format = ADC3XXX_FORMAT_RJF;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF:
+ format = ADC3XXX_FORMAT_LJF;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ /* Add/del route enabling BCLK output as applicable */
+ if (master && !adc3xxx->master)
+ snd_soc_dapm_add_routes(dapm, adc3xxx_bclk_out_intercon,
+ ARRAY_SIZE(adc3xxx_bclk_out_intercon));
+ else if (!master && adc3xxx->master)
+ snd_soc_dapm_del_routes(dapm, adc3xxx_bclk_out_intercon,
+ ARRAY_SIZE(adc3xxx_bclk_out_intercon));
+ adc3xxx->master = master;
+
+ /* set clock direction and format */
+ ret = snd_soc_component_update_bits(component,
+ ADC3XXX_INTERFACE_CTRL_1,
+ ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK,
+ clkdir | format);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops adc3xxx_dai_ops = {
+ .hw_params = adc3xxx_hw_params,
+ .set_sysclk = adc3xxx_set_dai_sysclk,
+ .set_fmt = adc3xxx_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver adc3xxx_dai = {
+ .name = "tlv320adc3xxx-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ADC3XXX_RATES,
+ .formats = ADC3XXX_FORMATS,
+ },
+ .ops = &adc3xxx_dai_ops,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_adc3xxx = {
+ .controls = adc3xxx_snd_controls,
+ .num_controls = ARRAY_SIZE(adc3xxx_snd_controls),
+ .dapm_widgets = adc3xxx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets),
+ .dapm_routes = adc3xxx_intercon,
+ .num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon),
+ .endianness = 1,
+};
+
+static const struct i2c_device_id adc3xxx_i2c_id[] = {
+ { "tlv320adc3001", ADC3001 },
+ { "tlv320adc3101", ADC3101 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id);
+
+static int adc3xxx_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct adc3xxx *adc3xxx = NULL;
+ int ret;
+
+ adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL);
+ if (!adc3xxx)
+ return -ENOMEM;
+ adc3xxx->dev = dev;
+
+ adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(adc3xxx->rst_pin)) {
+ return dev_err_probe(dev, PTR_ERR(adc3xxx->rst_pin),
+ "Failed to request rst_pin\n");
+ }
+
+ adc3xxx->mclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(adc3xxx->mclk)) {
+ /*
+ * The chip itself supports running off the BCLK either
+ * directly or via the PLL, but the driver does not (yet), so
+ * having a specified mclk is required. Otherwise, we could
+ * use the lack of a clocks property to indicate when BCLK is
+ * intended as the clock source.
+ */
+ return dev_err_probe(dev, PTR_ERR(adc3xxx->mclk),
+ "Failed to acquire MCLK\n");
+ } else if (adc3xxx->mclk) {
+ ret = clk_prepare_enable(adc3xxx->mclk);
+ if (ret < 0)
+ return ret;
+ dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk));
+ }
+
+ /* Configure mode for DMDIN/GPIO1 pin */
+ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ /* Configure mode for DMCLK/GPIO2 pin */
+ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ /* Configure mode for MICBIAS1: as Mic Bias output or GPO */
+ ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias1-gpo", &adc3xxx->micbias_gpo[0]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ /* Configure mode for MICBIAS2: as Mic Bias output or GPO */
+ ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias2-gpo", &adc3xxx->micbias_gpo[1]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ /* Configure voltage for MICBIAS1 pin (ON voltage when used as GPO) */
+ ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+ /* Configure voltage for MICBIAS2 pin (ON voltage when used as GPO) */
+ ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]);
+ if (ret < 0)
+ goto err_unprepare_mclk;
+
+ adc3xxx->regmap = devm_regmap_init_i2c(i2c, &adc3xxx_regmap);
+ if (IS_ERR(adc3xxx->regmap)) {
+ ret = PTR_ERR(adc3xxx->regmap);
+ goto err_unprepare_mclk;
+ }
+
+ i2c_set_clientdata(i2c, adc3xxx);
+
+ adc3xxx->type = (uintptr_t)i2c_get_match_data(i2c);
+
+ /* Reset codec chip */
+ gpiod_set_value_cansleep(adc3xxx->rst_pin, 1);
+ usleep_range(2000, 100000); /* Requirement: > 10 ns (datasheet p13) */
+ gpiod_set_value_cansleep(adc3xxx->rst_pin, 0);
+
+ /* Potentially set up pins used as GPIOs */
+ adc3xxx_init_gpio(adc3xxx);
+
+ ret = snd_soc_register_component(dev,
+ &soc_component_dev_adc3xxx, &adc3xxx_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ goto err_unprepare_mclk;
+ }
+
+ return 0;
+
+err_unprepare_mclk:
+ clk_disable_unprepare(adc3xxx->mclk);
+ return ret;
+}
+
+static void adc3xxx_i2c_remove(struct i2c_client *client)
+{
+ struct adc3xxx *adc3xxx = i2c_get_clientdata(client);
+
+ clk_disable_unprepare(adc3xxx->mclk);
+ adc3xxx_free_gpio(adc3xxx);
+ snd_soc_unregister_component(&client->dev);
+}
+
+static const struct of_device_id tlv320adc3xxx_of_match[] = {
+ { .compatible = "ti,tlv320adc3001", },
+ { .compatible = "ti,tlv320adc3101", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match);
+
+static struct i2c_driver adc3xxx_i2c_driver = {
+ .driver = {
+ .name = "tlv320adc3xxx-codec",
+ .of_match_table = tlv320adc3xxx_of_match,
+ },
+ .probe = adc3xxx_i2c_probe,
+ .remove = adc3xxx_i2c_remove,
+ .id_table = adc3xxx_i2c_id,
+};
+
+module_i2c_driver(adc3xxx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320ADC3xxx codec driver");
+MODULE_AUTHOR("shahina.s@mistralsolutions.com");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
new file mode 100644
index 000000000000..443cf59cb71a
--- /dev/null
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -0,0 +1,1215 @@
+// SPDX-License-Identifier: GPL-2.0
+// TLV320ADCX140 Sound driver
+// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/acpi.h>
+#include <linux/of.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 "tlv320adcx140.h"
+
+struct adcx140_priv {
+ struct snd_soc_component *component;
+ struct regulator *supply_areg;
+ struct gpio_desc *gpio_reset;
+ struct regmap *regmap;
+ struct device *dev;
+
+ bool micbias_vg;
+ bool phase_calib_on;
+
+ unsigned int dai_fmt;
+ unsigned int slot_width;
+};
+
+static const char * const gpo_config_names[] = {
+ "ti,gpo-config-1",
+ "ti,gpo-config-2",
+ "ti,gpo-config-3",
+ "ti,gpo-config-4",
+};
+
+static const struct reg_default adcx140_reg_defaults[] = {
+ { ADCX140_PAGE_SELECT, 0x00 },
+ { ADCX140_SW_RESET, 0x00 },
+ { ADCX140_SLEEP_CFG, 0x00 },
+ { ADCX140_SHDN_CFG, 0x05 },
+ { ADCX140_ASI_CFG0, 0x30 },
+ { ADCX140_ASI_CFG1, 0x00 },
+ { ADCX140_ASI_CFG2, 0x00 },
+ { ADCX140_ASI_CH1, 0x00 },
+ { ADCX140_ASI_CH2, 0x01 },
+ { ADCX140_ASI_CH3, 0x02 },
+ { ADCX140_ASI_CH4, 0x03 },
+ { ADCX140_ASI_CH5, 0x04 },
+ { ADCX140_ASI_CH6, 0x05 },
+ { ADCX140_ASI_CH7, 0x06 },
+ { ADCX140_ASI_CH8, 0x07 },
+ { ADCX140_MST_CFG0, 0x02 },
+ { ADCX140_MST_CFG1, 0x48 },
+ { ADCX140_ASI_STS, 0xff },
+ { ADCX140_CLK_SRC, 0x10 },
+ { ADCX140_PDMCLK_CFG, 0x40 },
+ { ADCX140_PDM_CFG, 0x00 },
+ { ADCX140_GPIO_CFG0, 0x22 },
+ { ADCX140_GPO_CFG0, 0x00 },
+ { ADCX140_GPO_CFG1, 0x00 },
+ { ADCX140_GPO_CFG2, 0x00 },
+ { ADCX140_GPO_CFG3, 0x00 },
+ { ADCX140_GPO_VAL, 0x00 },
+ { ADCX140_GPIO_MON, 0x00 },
+ { ADCX140_GPI_CFG0, 0x00 },
+ { ADCX140_GPI_CFG1, 0x00 },
+ { ADCX140_GPI_MON, 0x00 },
+ { ADCX140_INT_CFG, 0x00 },
+ { ADCX140_INT_MASK0, 0xff },
+ { ADCX140_INT_LTCH0, 0x00 },
+ { ADCX140_BIAS_CFG, 0x00 },
+ { ADCX140_CH1_CFG0, 0x00 },
+ { ADCX140_CH1_CFG1, 0x00 },
+ { ADCX140_CH1_CFG2, 0xc9 },
+ { ADCX140_CH1_CFG3, 0x80 },
+ { ADCX140_CH1_CFG4, 0x00 },
+ { ADCX140_CH2_CFG0, 0x00 },
+ { ADCX140_CH2_CFG1, 0x00 },
+ { ADCX140_CH2_CFG2, 0xc9 },
+ { ADCX140_CH2_CFG3, 0x80 },
+ { ADCX140_CH2_CFG4, 0x00 },
+ { ADCX140_CH3_CFG0, 0x00 },
+ { ADCX140_CH3_CFG1, 0x00 },
+ { ADCX140_CH3_CFG2, 0xc9 },
+ { ADCX140_CH3_CFG3, 0x80 },
+ { ADCX140_CH3_CFG4, 0x00 },
+ { ADCX140_CH4_CFG0, 0x00 },
+ { ADCX140_CH4_CFG1, 0x00 },
+ { ADCX140_CH4_CFG2, 0xc9 },
+ { ADCX140_CH4_CFG3, 0x80 },
+ { ADCX140_CH4_CFG4, 0x00 },
+ { ADCX140_CH5_CFG2, 0xc9 },
+ { ADCX140_CH5_CFG3, 0x80 },
+ { ADCX140_CH5_CFG4, 0x00 },
+ { ADCX140_CH6_CFG2, 0xc9 },
+ { ADCX140_CH6_CFG3, 0x80 },
+ { ADCX140_CH6_CFG4, 0x00 },
+ { ADCX140_CH7_CFG2, 0xc9 },
+ { ADCX140_CH7_CFG3, 0x80 },
+ { ADCX140_CH7_CFG4, 0x00 },
+ { ADCX140_CH8_CFG2, 0xc9 },
+ { ADCX140_CH8_CFG3, 0x80 },
+ { ADCX140_CH8_CFG4, 0x00 },
+ { ADCX140_DSP_CFG0, 0x01 },
+ { ADCX140_DSP_CFG1, 0x40 },
+ { ADCX140_DRE_CFG0, 0x7b },
+ { ADCX140_AGC_CFG0, 0xe7 },
+ { ADCX140_IN_CH_EN, 0xf0 },
+ { ADCX140_ASI_OUT_CH_EN, 0x00 },
+ { ADCX140_PWR_CFG, 0x00 },
+ { ADCX140_DEV_STS0, 0x00 },
+ { ADCX140_DEV_STS1, 0x80 },
+};
+
+static const struct regmap_range_cfg adcx140_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 12 * 128,
+ .selector_reg = ADCX140_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static bool adcx140_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADCX140_SW_RESET:
+ case ADCX140_DEV_STS0:
+ case ADCX140_DEV_STS1:
+ case ADCX140_ASI_STS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config adcx140_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_defaults = adcx140_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adcx140_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+ .ranges = adcx140_ranges,
+ .num_ranges = ARRAY_SIZE(adcx140_ranges),
+ .max_register = 12 * 128,
+ .volatile_reg = adcx140_volatile,
+};
+
+/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10050, 50, 0);
+
+/* ADC gain. From 0 to 42 dB in 1 dB steps */
+static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
+
+/* DRE Level. From -12 dB to -66 dB in 1 dB steps */
+static DECLARE_TLV_DB_SCALE(dre_thresh_tlv, -6600, 100, 0);
+/* DRE Max Gain. From 2 dB to 26 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(dre_gain_tlv, 200, 200, 0);
+
+/* AGC Level. From -6 dB to -36 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(agc_thresh_tlv, -3600, 200, 0);
+/* AGC Max Gain. From 3 dB to 42 dB in 3 dB steps */
+static DECLARE_TLV_DB_SCALE(agc_gain_tlv, 300, 300, 0);
+
+static const char * const decimation_filter_text[] = {
+ "Linear Phase", "Low Latency", "Ultra-low Latency"
+};
+
+static SOC_ENUM_SINGLE_DECL(decimation_filter_enum, ADCX140_DSP_CFG0, 4,
+ decimation_filter_text);
+
+static const struct snd_kcontrol_new decimation_filter_controls[] = {
+ SOC_DAPM_ENUM("Decimation Filter", decimation_filter_enum),
+};
+
+static const char * const pdmclk_text[] = {
+ "2.8224 MHz", "1.4112 MHz", "705.6 kHz", "5.6448 MHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(pdmclk_select_enum, ADCX140_PDMCLK_CFG, 0,
+ pdmclk_text);
+
+static const struct snd_kcontrol_new pdmclk_div_controls[] = {
+ SOC_DAPM_ENUM("PDM Clk Divider Select", pdmclk_select_enum),
+};
+
+static const char * const resistor_text[] = {
+ "2.5 kOhm", "10 kOhm", "20 kOhm"
+};
+
+static SOC_ENUM_SINGLE_DECL(in1_resistor_enum, ADCX140_CH1_CFG0, 2,
+ resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2_resistor_enum, ADCX140_CH2_CFG0, 2,
+ resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3_resistor_enum, ADCX140_CH3_CFG0, 2,
+ resistor_text);
+static SOC_ENUM_SINGLE_DECL(in4_resistor_enum, ADCX140_CH4_CFG0, 2,
+ resistor_text);
+
+static const struct snd_kcontrol_new in1_resistor_controls[] = {
+ SOC_DAPM_ENUM("CH1 Resistor Select", in1_resistor_enum),
+};
+static const struct snd_kcontrol_new in2_resistor_controls[] = {
+ SOC_DAPM_ENUM("CH2 Resistor Select", in2_resistor_enum),
+};
+static const struct snd_kcontrol_new in3_resistor_controls[] = {
+ SOC_DAPM_ENUM("CH3 Resistor Select", in3_resistor_enum),
+};
+static const struct snd_kcontrol_new in4_resistor_controls[] = {
+ SOC_DAPM_ENUM("CH4 Resistor Select", in4_resistor_enum),
+};
+
+/* Analog/Digital Selection */
+static const char * const adcx140_mic_sel_text[] = {"Analog", "Line In", "Digital"};
+static const char * const adcx140_analog_sel_text[] = {"Analog", "Line In"};
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic1p_enum,
+ ADCX140_CH1_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic1p_control =
+SOC_DAPM_ENUM("MIC1P MUX", adcx140_mic1p_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic1_analog_enum,
+ ADCX140_CH1_CFG0, 7,
+ adcx140_analog_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic1_analog_control =
+SOC_DAPM_ENUM("MIC1 Analog MUX", adcx140_mic1_analog_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic1m_enum,
+ ADCX140_CH1_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic1m_control =
+SOC_DAPM_ENUM("MIC1M MUX", adcx140_mic1m_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic2p_enum,
+ ADCX140_CH2_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic2p_control =
+SOC_DAPM_ENUM("MIC2P MUX", adcx140_mic2p_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic2_analog_enum,
+ ADCX140_CH2_CFG0, 7,
+ adcx140_analog_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic2_analog_control =
+SOC_DAPM_ENUM("MIC2 Analog MUX", adcx140_mic2_analog_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic2m_enum,
+ ADCX140_CH2_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic2m_control =
+SOC_DAPM_ENUM("MIC2M MUX", adcx140_mic2m_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic3p_enum,
+ ADCX140_CH3_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic3p_control =
+SOC_DAPM_ENUM("MIC3P MUX", adcx140_mic3p_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic3_analog_enum,
+ ADCX140_CH3_CFG0, 7,
+ adcx140_analog_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic3_analog_control =
+SOC_DAPM_ENUM("MIC3 Analog MUX", adcx140_mic3_analog_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic3m_enum,
+ ADCX140_CH3_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic3m_control =
+SOC_DAPM_ENUM("MIC3M MUX", adcx140_mic3m_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic4p_enum,
+ ADCX140_CH4_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic4p_control =
+SOC_DAPM_ENUM("MIC4P MUX", adcx140_mic4p_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic4_analog_enum,
+ ADCX140_CH4_CFG0, 7,
+ adcx140_analog_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic4_analog_control =
+SOC_DAPM_ENUM("MIC4 Analog MUX", adcx140_mic4_analog_enum);
+
+static SOC_ENUM_SINGLE_DECL(adcx140_mic4m_enum,
+ ADCX140_CH4_CFG0, 5,
+ adcx140_mic_sel_text);
+
+static const struct snd_kcontrol_new adcx140_dapm_mic4m_control =
+SOC_DAPM_ENUM("MIC4M MUX", adcx140_mic4m_enum);
+
+static const struct snd_kcontrol_new adcx140_dapm_ch1_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 7, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch2_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 6, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch3_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 5, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch4_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 4, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch5_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 3, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch6_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 2, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch7_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 1, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch8_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 0, 1, 0);
+
+static const struct snd_kcontrol_new adcx140_dapm_ch1_dre_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_CH1_CFG0, 0, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch2_dre_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_CH2_CFG0, 0, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch3_dre_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_CH3_CFG0, 0, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch4_dre_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_CH4_CFG0, 0, 1, 0);
+
+static const struct snd_kcontrol_new adcx140_dapm_dre_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_DSP_CFG1, 3, 1, 0);
+
+/* Output Mixer */
+static const struct snd_kcontrol_new adcx140_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Digital CH1 Switch", 0, 0, 0, 0),
+ SOC_DAPM_SINGLE("Digital CH2 Switch", 0, 0, 0, 0),
+ SOC_DAPM_SINGLE("Digital CH3 Switch", 0, 0, 0, 0),
+ SOC_DAPM_SINGLE("Digital CH4 Switch", 0, 0, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget adcx140_dapm_widgets[] = {
+ /* Analog Differential Inputs */
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1M"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2M"),
+ SND_SOC_DAPM_INPUT("MIC3P"),
+ SND_SOC_DAPM_INPUT("MIC3M"),
+ SND_SOC_DAPM_INPUT("MIC4P"),
+ SND_SOC_DAPM_INPUT("MIC4M"),
+
+ SND_SOC_DAPM_OUTPUT("CH1_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH2_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH3_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH4_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH5_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH6_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH7_OUT"),
+ SND_SOC_DAPM_OUTPUT("CH8_OUT"),
+
+ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ &adcx140_output_mixer_controls[0],
+ ARRAY_SIZE(adcx140_output_mixer_controls)),
+
+ /* Input Selection to MIC_PGA */
+ SND_SOC_DAPM_MUX("MIC1P Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic1p_control),
+ SND_SOC_DAPM_MUX("MIC2P Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic2p_control),
+ SND_SOC_DAPM_MUX("MIC3P Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic3p_control),
+ SND_SOC_DAPM_MUX("MIC4P Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic4p_control),
+
+ /* Input Selection to MIC_PGA */
+ SND_SOC_DAPM_MUX("MIC1 Analog Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic1_analog_control),
+ SND_SOC_DAPM_MUX("MIC2 Analog Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic2_analog_control),
+ SND_SOC_DAPM_MUX("MIC3 Analog Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic3_analog_control),
+ SND_SOC_DAPM_MUX("MIC4 Analog Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic4_analog_control),
+
+ SND_SOC_DAPM_MUX("MIC1M Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic1m_control),
+ SND_SOC_DAPM_MUX("MIC2M Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic2m_control),
+ SND_SOC_DAPM_MUX("MIC3M Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic3m_control),
+ SND_SOC_DAPM_MUX("MIC4M Input Mux", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_mic4m_control),
+
+ SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("CH1_ADC", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0),
+ SND_SOC_DAPM_ADC("CH2_ADC", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0),
+ SND_SOC_DAPM_ADC("CH3_ADC", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0),
+ SND_SOC_DAPM_ADC("CH4_ADC", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0),
+
+ SND_SOC_DAPM_ADC("CH1_DIG", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0),
+ SND_SOC_DAPM_ADC("CH2_DIG", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0),
+ SND_SOC_DAPM_ADC("CH3_DIG", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0),
+ SND_SOC_DAPM_ADC("CH4_DIG", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0),
+ SND_SOC_DAPM_ADC("CH5_DIG", "CH5 Capture", ADCX140_IN_CH_EN, 3, 0),
+ SND_SOC_DAPM_ADC("CH6_DIG", "CH6 Capture", ADCX140_IN_CH_EN, 2, 0),
+ SND_SOC_DAPM_ADC("CH7_DIG", "CH7 Capture", ADCX140_IN_CH_EN, 1, 0),
+ SND_SOC_DAPM_ADC("CH8_DIG", "CH8 Capture", ADCX140_IN_CH_EN, 0, 0),
+
+
+ SND_SOC_DAPM_SWITCH("CH1_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch1_en_switch),
+ SND_SOC_DAPM_SWITCH("CH2_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch2_en_switch),
+ SND_SOC_DAPM_SWITCH("CH3_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch3_en_switch),
+ SND_SOC_DAPM_SWITCH("CH4_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch4_en_switch),
+
+ SND_SOC_DAPM_SWITCH("CH5_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch5_en_switch),
+ SND_SOC_DAPM_SWITCH("CH6_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch6_en_switch),
+ SND_SOC_DAPM_SWITCH("CH7_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch7_en_switch),
+ SND_SOC_DAPM_SWITCH("CH8_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch8_en_switch),
+
+ SND_SOC_DAPM_SWITCH("DRE_ENABLE", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_dre_en_switch),
+
+ SND_SOC_DAPM_SWITCH("CH1_DRE_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch1_dre_en_switch),
+ SND_SOC_DAPM_SWITCH("CH2_DRE_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch2_dre_en_switch),
+ SND_SOC_DAPM_SWITCH("CH3_DRE_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch3_dre_en_switch),
+ SND_SOC_DAPM_SWITCH("CH4_DRE_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch4_dre_en_switch),
+
+ SND_SOC_DAPM_MUX("IN1 Analog Mic Resistor", SND_SOC_NOPM, 0, 0,
+ in1_resistor_controls),
+ SND_SOC_DAPM_MUX("IN2 Analog Mic Resistor", SND_SOC_NOPM, 0, 0,
+ in2_resistor_controls),
+ SND_SOC_DAPM_MUX("IN3 Analog Mic Resistor", SND_SOC_NOPM, 0, 0,
+ in3_resistor_controls),
+ SND_SOC_DAPM_MUX("IN4 Analog Mic Resistor", SND_SOC_NOPM, 0, 0,
+ in4_resistor_controls),
+
+ SND_SOC_DAPM_MUX("PDM Clk Div Select", SND_SOC_NOPM, 0, 0,
+ pdmclk_div_controls),
+
+ SND_SOC_DAPM_MUX("Decimation Filter", SND_SOC_NOPM, 0, 0,
+ decimation_filter_controls),
+};
+
+static const struct snd_soc_dapm_route adcx140_audio_map[] = {
+ /* Outputs */
+ {"CH1_OUT", NULL, "Output Mixer"},
+ {"CH2_OUT", NULL, "Output Mixer"},
+ {"CH3_OUT", NULL, "Output Mixer"},
+ {"CH4_OUT", NULL, "Output Mixer"},
+
+ {"CH1_ASI_EN", "Switch", "CH1_ADC"},
+ {"CH2_ASI_EN", "Switch", "CH2_ADC"},
+ {"CH3_ASI_EN", "Switch", "CH3_ADC"},
+ {"CH4_ASI_EN", "Switch", "CH4_ADC"},
+
+ {"CH1_ASI_EN", "Switch", "CH1_DIG"},
+ {"CH2_ASI_EN", "Switch", "CH2_DIG"},
+ {"CH3_ASI_EN", "Switch", "CH3_DIG"},
+ {"CH4_ASI_EN", "Switch", "CH4_DIG"},
+ {"CH5_ASI_EN", "Switch", "CH5_DIG"},
+ {"CH6_ASI_EN", "Switch", "CH6_DIG"},
+ {"CH7_ASI_EN", "Switch", "CH7_DIG"},
+ {"CH8_ASI_EN", "Switch", "CH8_DIG"},
+
+ {"CH5_ASI_EN", "Switch", "CH5_OUT"},
+ {"CH6_ASI_EN", "Switch", "CH6_OUT"},
+ {"CH7_ASI_EN", "Switch", "CH7_OUT"},
+ {"CH8_ASI_EN", "Switch", "CH8_OUT"},
+
+ {"Decimation Filter", "Linear Phase", "DRE_ENABLE"},
+ {"Decimation Filter", "Low Latency", "DRE_ENABLE"},
+ {"Decimation Filter", "Ultra-low Latency", "DRE_ENABLE"},
+
+ {"DRE_ENABLE", "Switch", "CH1_DRE_EN"},
+ {"DRE_ENABLE", "Switch", "CH2_DRE_EN"},
+ {"DRE_ENABLE", "Switch", "CH3_DRE_EN"},
+ {"DRE_ENABLE", "Switch", "CH4_DRE_EN"},
+
+ {"CH1_DRE_EN", "Switch", "CH1_ADC"},
+ {"CH2_DRE_EN", "Switch", "CH2_ADC"},
+ {"CH3_DRE_EN", "Switch", "CH3_ADC"},
+ {"CH4_DRE_EN", "Switch", "CH4_ADC"},
+
+ /* Mic input */
+ {"CH1_ADC", NULL, "MIC_GAIN_CTL_CH1"},
+ {"CH2_ADC", NULL, "MIC_GAIN_CTL_CH2"},
+ {"CH3_ADC", NULL, "MIC_GAIN_CTL_CH3"},
+ {"CH4_ADC", NULL, "MIC_GAIN_CTL_CH4"},
+
+ {"MIC_GAIN_CTL_CH1", NULL, "IN1 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH1", NULL, "IN1 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH2", NULL, "IN2 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH2", NULL, "IN2 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH3", NULL, "IN3 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH3", NULL, "IN3 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH4", NULL, "IN4 Analog Mic Resistor"},
+ {"MIC_GAIN_CTL_CH4", NULL, "IN4 Analog Mic Resistor"},
+
+ {"IN1 Analog Mic Resistor", "2.5 kOhm", "MIC1P Input Mux"},
+ {"IN1 Analog Mic Resistor", "10 kOhm", "MIC1P Input Mux"},
+ {"IN1 Analog Mic Resistor", "20 kOhm", "MIC1P Input Mux"},
+
+ {"IN1 Analog Mic Resistor", "2.5 kOhm", "MIC1M Input Mux"},
+ {"IN1 Analog Mic Resistor", "10 kOhm", "MIC1M Input Mux"},
+ {"IN1 Analog Mic Resistor", "20 kOhm", "MIC1M Input Mux"},
+
+ {"IN2 Analog Mic Resistor", "2.5 kOhm", "MIC2P Input Mux"},
+ {"IN2 Analog Mic Resistor", "10 kOhm", "MIC2P Input Mux"},
+ {"IN2 Analog Mic Resistor", "20 kOhm", "MIC2P Input Mux"},
+
+ {"IN2 Analog Mic Resistor", "2.5 kOhm", "MIC2M Input Mux"},
+ {"IN2 Analog Mic Resistor", "10 kOhm", "MIC2M Input Mux"},
+ {"IN2 Analog Mic Resistor", "20 kOhm", "MIC2M Input Mux"},
+
+ {"IN3 Analog Mic Resistor", "2.5 kOhm", "MIC3P Input Mux"},
+ {"IN3 Analog Mic Resistor", "10 kOhm", "MIC3P Input Mux"},
+ {"IN3 Analog Mic Resistor", "20 kOhm", "MIC3P Input Mux"},
+
+ {"IN3 Analog Mic Resistor", "2.5 kOhm", "MIC3M Input Mux"},
+ {"IN3 Analog Mic Resistor", "10 kOhm", "MIC3M Input Mux"},
+ {"IN3 Analog Mic Resistor", "20 kOhm", "MIC3M Input Mux"},
+
+ {"IN4 Analog Mic Resistor", "2.5 kOhm", "MIC4P Input Mux"},
+ {"IN4 Analog Mic Resistor", "10 kOhm", "MIC4P Input Mux"},
+ {"IN4 Analog Mic Resistor", "20 kOhm", "MIC4P Input Mux"},
+
+ {"IN4 Analog Mic Resistor", "2.5 kOhm", "MIC4M Input Mux"},
+ {"IN4 Analog Mic Resistor", "10 kOhm", "MIC4M Input Mux"},
+ {"IN4 Analog Mic Resistor", "20 kOhm", "MIC4M Input Mux"},
+
+ {"PDM Clk Div Select", "2.8224 MHz", "MIC1P Input Mux"},
+ {"PDM Clk Div Select", "1.4112 MHz", "MIC1P Input Mux"},
+ {"PDM Clk Div Select", "705.6 kHz", "MIC1P Input Mux"},
+ {"PDM Clk Div Select", "5.6448 MHz", "MIC1P Input Mux"},
+
+ {"MIC1P Input Mux", NULL, "CH1_DIG"},
+ {"MIC1M Input Mux", NULL, "CH2_DIG"},
+ {"MIC2P Input Mux", NULL, "CH3_DIG"},
+ {"MIC2M Input Mux", NULL, "CH4_DIG"},
+ {"MIC3P Input Mux", NULL, "CH5_DIG"},
+ {"MIC3M Input Mux", NULL, "CH6_DIG"},
+ {"MIC4P Input Mux", NULL, "CH7_DIG"},
+ {"MIC4M Input Mux", NULL, "CH8_DIG"},
+
+ {"MIC1 Analog Mux", "Line In", "MIC1P"},
+ {"MIC2 Analog Mux", "Line In", "MIC2P"},
+ {"MIC3 Analog Mux", "Line In", "MIC3P"},
+ {"MIC4 Analog Mux", "Line In", "MIC4P"},
+
+ {"MIC1P Input Mux", "Analog", "MIC1P"},
+ {"MIC1M Input Mux", "Analog", "MIC1M"},
+ {"MIC2P Input Mux", "Analog", "MIC2P"},
+ {"MIC2M Input Mux", "Analog", "MIC2M"},
+ {"MIC3P Input Mux", "Analog", "MIC3P"},
+ {"MIC3M Input Mux", "Analog", "MIC3M"},
+ {"MIC4P Input Mux", "Analog", "MIC4P"},
+ {"MIC4M Input Mux", "Analog", "MIC4M"},
+
+ {"MIC1P Input Mux", "Digital", "MIC1P"},
+ {"MIC1M Input Mux", "Digital", "MIC1M"},
+ {"MIC2P Input Mux", "Digital", "MIC2P"},
+ {"MIC2M Input Mux", "Digital", "MIC2M"},
+ {"MIC3P Input Mux", "Digital", "MIC3P"},
+ {"MIC3M Input Mux", "Digital", "MIC3M"},
+ {"MIC4P Input Mux", "Digital", "MIC4P"},
+ {"MIC4M Input Mux", "Digital", "MIC4M"},
+};
+
+#define ADCX140_PHASE_CALIB_SWITCH(xname) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = adcx140_phase_calib_info, \
+ .get = adcx140_phase_calib_get, \
+ .put = adcx140_phase_calib_put}
+
+static int adcx140_phase_calib_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int adcx140_phase_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec);
+
+ value->value.integer.value[0] = adcx140->phase_calib_on ? 1 : 0;
+
+
+ return 0;
+}
+
+static int adcx140_phase_calib_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec);
+
+ bool v = value->value.integer.value[0] ? true : false;
+
+ if (adcx140->phase_calib_on != v) {
+ adcx140->phase_calib_on = v;
+ return 1;
+ }
+ return 0;
+}
+
+static const struct snd_kcontrol_new adcx140_snd_controls[] = {
+ SOC_SINGLE_TLV("Analog CH1 Mic Gain Volume", ADCX140_CH1_CFG1, 2, 42, 0,
+ adc_tlv),
+ SOC_SINGLE_TLV("Analog CH2 Mic Gain Volume", ADCX140_CH2_CFG1, 2, 42, 0,
+ adc_tlv),
+ SOC_SINGLE_TLV("Analog CH3 Mic Gain Volume", ADCX140_CH3_CFG1, 2, 42, 0,
+ adc_tlv),
+ SOC_SINGLE_TLV("Analog CH4 Mic Gain Volume", ADCX140_CH4_CFG1, 2, 42, 0,
+ adc_tlv),
+
+ SOC_SINGLE_TLV("DRE Threshold", ADCX140_DRE_CFG0, 4, 9, 0,
+ dre_thresh_tlv),
+ SOC_SINGLE_TLV("DRE Max Gain", ADCX140_DRE_CFG0, 0, 12, 0,
+ dre_gain_tlv),
+
+ SOC_SINGLE_TLV("AGC Threshold", ADCX140_AGC_CFG0, 4, 15, 0,
+ agc_thresh_tlv),
+ SOC_SINGLE_TLV("AGC Max Gain", ADCX140_AGC_CFG0, 0, 13, 0,
+ agc_gain_tlv),
+
+ SOC_SINGLE_TLV("Digital CH1 Out Volume", ADCX140_CH1_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH2 Out Volume", ADCX140_CH2_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH3 Out Volume", ADCX140_CH3_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH4 Out Volume", ADCX140_CH4_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH5 Out Volume", ADCX140_CH5_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH6 Out Volume", ADCX140_CH6_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH7 Out Volume", ADCX140_CH7_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2,
+ 0, 0xff, 0, dig_vol_tlv),
+ ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"),
+};
+
+static int adcx140_reset(struct adcx140_priv *adcx140)
+{
+ int ret = 0;
+
+ if (adcx140->gpio_reset) {
+ gpiod_direction_output(adcx140->gpio_reset, 0);
+ /* 8.4.1: wait for hw shutdown (25ms) + >= 1ms */
+ usleep_range(30000, 100000);
+ gpiod_direction_output(adcx140->gpio_reset, 1);
+ } else {
+ ret = regmap_write(adcx140->regmap, ADCX140_SW_RESET,
+ ADCX140_RESET);
+ }
+
+ /* 8.4.2: wait >= 10 ms after entering sleep mode. */
+ usleep_range(10000, 100000);
+
+ return ret;
+}
+
+static void adcx140_pwr_ctrl(struct adcx140_priv *adcx140, bool power_state)
+{
+ int pwr_ctrl = 0;
+ int ret = 0;
+ struct snd_soc_component *component = adcx140->component;
+
+ if (power_state)
+ pwr_ctrl = ADCX140_PWR_CFG_ADC_PDZ | ADCX140_PWR_CFG_PLL_PDZ;
+
+ if (adcx140->micbias_vg && power_state)
+ pwr_ctrl |= ADCX140_PWR_CFG_BIAS_PDZ;
+
+ if (pwr_ctrl) {
+ ret = regmap_write(adcx140->regmap, ADCX140_PHASE_CALIB,
+ adcx140->phase_calib_on ? 0x00 : 0x40);
+ if (ret)
+ dev_err(component->dev, "%s: register write error %d\n",
+ __func__, ret);
+ }
+
+ regmap_update_bits(adcx140->regmap, ADCX140_PWR_CFG,
+ ADCX140_PWR_CTRL_MSK, pwr_ctrl);
+}
+
+static int adcx140_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+ u8 data = 0;
+
+ switch (params_width(params)) {
+ case 16:
+ data = ADCX140_16_BIT_WORD;
+ break;
+ case 20:
+ data = ADCX140_20_BIT_WORD;
+ break;
+ case 24:
+ data = ADCX140_24_BIT_WORD;
+ break;
+ case 32:
+ data = ADCX140_32_BIT_WORD;
+ break;
+ default:
+ dev_err(component->dev, "%s: Unsupported width %d\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ }
+
+ adcx140_pwr_ctrl(adcx140, false);
+
+ snd_soc_component_update_bits(component, ADCX140_ASI_CFG0,
+ ADCX140_WORD_LEN_MSK, data);
+
+ adcx140_pwr_ctrl(adcx140, true);
+
+ return 0;
+}
+
+static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+ u8 iface_reg1 = 0;
+ u8 iface_reg2 = 0;
+ int offset = 0;
+ bool inverted_bclk = false;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ iface_reg2 |= ADCX140_BCLK_FSYNC_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clock provider\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg1 |= ADCX140_I2S_MODE_BIT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg1 |= ADCX140_LEFT_JUST_BIT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ offset = 1;
+ inverted_bclk = true;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ inverted_bclk = true;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ /* signal polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ inverted_bclk = !inverted_bclk;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface_reg1 |= ADCX140_FSYNCINV_BIT;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clock signal polarity\n");
+ return -EINVAL;
+ }
+
+ if (inverted_bclk)
+ iface_reg1 |= ADCX140_BCLKINV_BIT;
+
+ adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ adcx140_pwr_ctrl(adcx140, false);
+
+ snd_soc_component_update_bits(component, ADCX140_ASI_CFG0,
+ ADCX140_FSYNCINV_BIT |
+ ADCX140_BCLKINV_BIT |
+ ADCX140_ASI_FORMAT_MSK,
+ iface_reg1);
+ snd_soc_component_update_bits(component, ADCX140_MST_CFG0,
+ ADCX140_BCLK_FSYNC_MASTER, iface_reg2);
+
+ /* Configure data offset */
+ snd_soc_component_update_bits(component, ADCX140_ASI_CFG1,
+ ADCX140_TX_OFFSET_MASK, offset);
+
+ adcx140_pwr_ctrl(adcx140, true);
+
+ return 0;
+}
+
+static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * The chip itself supports arbitrary masks, but the driver currently
+ * only supports adjacent slots beginning at the first slot.
+ */
+ if (tx_mask != GENMASK(__fls(tx_mask), 0)) {
+ dev_err(component->dev, "Only lower adjacent slots are supported\n");
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 16:
+ case 20:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ adcx140->slot_width = slot_width;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops adcx140_dai_ops = {
+ .hw_params = adcx140_hw_params,
+ .set_fmt = adcx140_set_dai_fmt,
+ .set_tdm_slot = adcx140_set_dai_tdm_slot,
+};
+
+static int adcx140_configure_gpo(struct adcx140_priv *adcx140)
+{
+ u32 gpo_outputs[ADCX140_NUM_GPOS];
+ u32 gpo_output_val = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < ADCX140_NUM_GPOS; i++) {
+ ret = device_property_read_u32_array(adcx140->dev,
+ gpo_config_names[i],
+ gpo_outputs,
+ ADCX140_NUM_GPO_CFGS);
+ if (ret)
+ continue;
+
+ if (gpo_outputs[0] > ADCX140_GPO_CFG_MAX) {
+ dev_err(adcx140->dev, "GPO%d config out of range\n", i + 1);
+ return -EINVAL;
+ }
+
+ if (gpo_outputs[1] > ADCX140_GPO_DRV_MAX) {
+ dev_err(adcx140->dev, "GPO%d drive out of range\n", i + 1);
+ return -EINVAL;
+ }
+
+ gpo_output_val = gpo_outputs[0] << ADCX140_GPO_SHIFT |
+ gpo_outputs[1];
+ ret = regmap_write(adcx140->regmap, ADCX140_GPO_CFG0 + i,
+ gpo_output_val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+
+}
+
+static int adcx140_configure_gpio(struct adcx140_priv *adcx140)
+{
+ int gpio_count = 0;
+ u32 gpio_outputs[ADCX140_NUM_GPIO_CFGS];
+ u32 gpio_output_val = 0;
+ int ret;
+
+ gpio_count = device_property_count_u32(adcx140->dev,
+ "ti,gpio-config");
+ if (gpio_count <= 0)
+ return 0;
+
+ if (gpio_count != ADCX140_NUM_GPIO_CFGS)
+ return -EINVAL;
+
+ ret = device_property_read_u32_array(adcx140->dev, "ti,gpio-config",
+ gpio_outputs, gpio_count);
+ if (ret)
+ return ret;
+
+ if (gpio_outputs[0] > ADCX140_GPIO_CFG_MAX) {
+ dev_err(adcx140->dev, "GPIO config out of range\n");
+ return -EINVAL;
+ }
+
+ if (gpio_outputs[1] > ADCX140_GPIO_DRV_MAX) {
+ dev_err(adcx140->dev, "GPIO drive out of range\n");
+ return -EINVAL;
+ }
+
+ gpio_output_val = gpio_outputs[0] << ADCX140_GPIO_SHIFT
+ | gpio_outputs[1];
+
+ return regmap_write(adcx140->regmap, ADCX140_GPIO_CFG0, gpio_output_val);
+}
+
+static int adcx140_codec_probe(struct snd_soc_component *component)
+{
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+ int sleep_cfg_val = ADCX140_WAKE_DEV;
+ u32 bias_source;
+ u32 vref_source;
+ u8 bias_cfg;
+ int pdm_count;
+ u32 pdm_edges[ADCX140_NUM_PDM_EDGES];
+ u32 pdm_edge_val = 0;
+ int gpi_count;
+ u32 gpi_inputs[ADCX140_NUM_GPI_PINS];
+ u32 gpi_input_val = 0;
+ int i;
+ int ret;
+ bool tx_high_z;
+
+ ret = device_property_read_u32(adcx140->dev, "ti,mic-bias-source",
+ &bias_source);
+ if (ret || bias_source > ADCX140_MIC_BIAS_VAL_AVDD) {
+ bias_source = ADCX140_MIC_BIAS_VAL_VREF;
+ adcx140->micbias_vg = false;
+ } else {
+ adcx140->micbias_vg = true;
+ }
+
+ ret = device_property_read_u32(adcx140->dev, "ti,vref-source",
+ &vref_source);
+ if (ret)
+ vref_source = ADCX140_MIC_BIAS_VREF_275V;
+
+ if (vref_source > ADCX140_MIC_BIAS_VREF_1375V) {
+ dev_err(adcx140->dev, "Mic Bias source value is invalid\n");
+ return -EINVAL;
+ }
+
+ bias_cfg = bias_source << ADCX140_MIC_BIAS_SHIFT | vref_source;
+
+ ret = adcx140_reset(adcx140);
+ if (ret)
+ goto out;
+
+ if (adcx140->supply_areg == NULL)
+ sleep_cfg_val |= ADCX140_AREG_INTERNAL;
+
+ ret = regmap_write(adcx140->regmap, ADCX140_SLEEP_CFG, sleep_cfg_val);
+ if (ret) {
+ dev_err(adcx140->dev, "setting sleep config failed %d\n", ret);
+ goto out;
+ }
+
+ /* 8.4.3: Wait >= 1ms after entering active mode. */
+ usleep_range(1000, 100000);
+
+ pdm_count = device_property_count_u32(adcx140->dev,
+ "ti,pdm-edge-select");
+ if (pdm_count <= ADCX140_NUM_PDM_EDGES && pdm_count > 0) {
+ ret = device_property_read_u32_array(adcx140->dev,
+ "ti,pdm-edge-select",
+ pdm_edges, pdm_count);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pdm_count; i++)
+ pdm_edge_val |= pdm_edges[i] << (ADCX140_PDM_EDGE_SHIFT - i);
+
+ ret = regmap_write(adcx140->regmap, ADCX140_PDM_CFG,
+ pdm_edge_val);
+ if (ret)
+ return ret;
+ }
+
+ gpi_count = device_property_count_u32(adcx140->dev, "ti,gpi-config");
+ if (gpi_count <= ADCX140_NUM_GPI_PINS && gpi_count > 0) {
+ ret = device_property_read_u32_array(adcx140->dev,
+ "ti,gpi-config",
+ gpi_inputs, gpi_count);
+ if (ret)
+ return ret;
+
+ gpi_input_val = gpi_inputs[ADCX140_GPI1_INDEX] << ADCX140_GPI_SHIFT |
+ gpi_inputs[ADCX140_GPI2_INDEX];
+
+ ret = regmap_write(adcx140->regmap, ADCX140_GPI_CFG0,
+ gpi_input_val);
+ if (ret)
+ return ret;
+
+ gpi_input_val = gpi_inputs[ADCX140_GPI3_INDEX] << ADCX140_GPI_SHIFT |
+ gpi_inputs[ADCX140_GPI4_INDEX];
+
+ ret = regmap_write(adcx140->regmap, ADCX140_GPI_CFG1,
+ gpi_input_val);
+ if (ret)
+ return ret;
+ }
+
+ ret = adcx140_configure_gpio(adcx140);
+ if (ret)
+ return ret;
+
+ ret = adcx140_configure_gpo(adcx140);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(adcx140->regmap, ADCX140_BIAS_CFG,
+ ADCX140_MIC_BIAS_VAL_MSK |
+ ADCX140_MIC_BIAS_VREF_MSK, bias_cfg);
+ if (ret)
+ dev_err(adcx140->dev, "setting MIC bias failed %d\n", ret);
+
+ tx_high_z = device_property_read_bool(adcx140->dev, "ti,asi-tx-drive");
+ if (tx_high_z) {
+ ret = regmap_update_bits(adcx140->regmap, ADCX140_ASI_CFG0,
+ ADCX140_TX_FILL, ADCX140_TX_FILL);
+ if (ret) {
+ dev_err(adcx140->dev, "Setting Tx drive failed %d\n", ret);
+ goto out;
+ }
+ }
+
+ adcx140_pwr_ctrl(adcx140, true);
+out:
+ return ret;
+}
+
+static int adcx140_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ adcx140_pwr_ctrl(adcx140, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ adcx140_pwr_ctrl(adcx140, false);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_adcx140 = {
+ .probe = adcx140_codec_probe,
+ .set_bias_level = adcx140_set_bias_level,
+ .controls = adcx140_snd_controls,
+ .num_controls = ARRAY_SIZE(adcx140_snd_controls),
+ .dapm_widgets = adcx140_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adcx140_dapm_widgets),
+ .dapm_routes = adcx140_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(adcx140_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver adcx140_dai_driver[] = {
+ {
+ .name = "tlv320adcx140-codec",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = ADCX140_MAX_CHANNELS,
+ .rates = ADCX140_RATES,
+ .formats = ADCX140_FORMATS,
+ },
+ .ops = &adcx140_dai_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id tlv320adcx140_of_match[] = {
+ { .compatible = "ti,tlv320adc3140" },
+ { .compatible = "ti,tlv320adc5140" },
+ { .compatible = "ti,tlv320adc6140" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match);
+#endif
+
+static void adcx140_disable_regulator(void *arg)
+{
+ struct adcx140_priv *adcx140 = arg;
+
+ regulator_disable(adcx140->supply_areg);
+}
+
+static int adcx140_i2c_probe(struct i2c_client *i2c)
+{
+ struct adcx140_priv *adcx140;
+ int ret;
+
+ adcx140 = devm_kzalloc(&i2c->dev, sizeof(*adcx140), GFP_KERNEL);
+ if (!adcx140)
+ return -ENOMEM;
+
+ adcx140->phase_calib_on = false;
+ adcx140->dev = &i2c->dev;
+
+ adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(adcx140->gpio_reset))
+ dev_info(&i2c->dev, "Reset GPIO not defined\n");
+
+ adcx140->supply_areg = devm_regulator_get_optional(adcx140->dev,
+ "areg");
+ if (IS_ERR(adcx140->supply_areg)) {
+ if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ adcx140->supply_areg = NULL;
+ } else {
+ ret = regulator_enable(adcx140->supply_areg);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to enable areg\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, adcx140_disable_regulator, adcx140);
+ if (ret)
+ return ret;
+ }
+
+ adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap);
+ if (IS_ERR(adcx140->regmap)) {
+ ret = PTR_ERR(adcx140->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, adcx140);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_driver_adcx140,
+ adcx140_dai_driver, 1);
+}
+
+static const struct i2c_device_id adcx140_i2c_id[] = {
+ { "tlv320adc3140", 0 },
+ { "tlv320adc5140", 1 },
+ { "tlv320adc6140", 2 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adcx140_i2c_id);
+
+static struct i2c_driver adcx140_i2c_driver = {
+ .driver = {
+ .name = "tlv320adcx140-codec",
+ .of_match_table = of_match_ptr(tlv320adcx140_of_match),
+ },
+ .probe = adcx140_i2c_probe,
+ .id_table = adcx140_i2c_id,
+};
+module_i2c_driver(adcx140_i2c_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("ASoC TLV320ADCX140 CODEC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h
new file mode 100644
index 000000000000..27a1f1012fe2
--- /dev/null
+++ b/sound/soc/codecs/tlv320adcx140.h
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+// TLV320ADCX140 Sound driver
+// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
+
+#ifndef _TLV320ADCX140_H
+#define _TLV320ADCX140_H
+
+#define ADCX140_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+
+#define ADCX140_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define ADCX140_PAGE_SELECT 0x00
+#define ADCX140_SW_RESET 0x01
+#define ADCX140_SLEEP_CFG 0x02
+#define ADCX140_SHDN_CFG 0x05
+#define ADCX140_ASI_CFG0 0x07
+#define ADCX140_ASI_CFG1 0x08
+#define ADCX140_ASI_CFG2 0x09
+#define ADCX140_ASI_CH1 0x0b
+#define ADCX140_ASI_CH2 0x0c
+#define ADCX140_ASI_CH3 0x0d
+#define ADCX140_ASI_CH4 0x0e
+#define ADCX140_ASI_CH5 0x0f
+#define ADCX140_ASI_CH6 0x10
+#define ADCX140_ASI_CH7 0x11
+#define ADCX140_ASI_CH8 0x12
+#define ADCX140_MST_CFG0 0x13
+#define ADCX140_MST_CFG1 0x14
+#define ADCX140_ASI_STS 0x15
+#define ADCX140_CLK_SRC 0x16
+#define ADCX140_PDMCLK_CFG 0x1f
+#define ADCX140_PDM_CFG 0x20
+#define ADCX140_GPIO_CFG0 0x21
+#define ADCX140_GPO_CFG0 0x22
+#define ADCX140_GPO_CFG1 0x23
+#define ADCX140_GPO_CFG2 0x24
+#define ADCX140_GPO_CFG3 0x25
+#define ADCX140_GPO_VAL 0x29
+#define ADCX140_GPIO_MON 0x2a
+#define ADCX140_GPI_CFG0 0x2b
+#define ADCX140_GPI_CFG1 0x2c
+#define ADCX140_GPI_MON 0x2f
+#define ADCX140_INT_CFG 0x32
+#define ADCX140_INT_MASK0 0x33
+#define ADCX140_INT_LTCH0 0x36
+#define ADCX140_BIAS_CFG 0x3b
+#define ADCX140_CH1_CFG0 0x3c
+#define ADCX140_CH1_CFG1 0x3d
+#define ADCX140_CH1_CFG2 0x3e
+#define ADCX140_CH1_CFG3 0x3f
+#define ADCX140_CH1_CFG4 0x40
+#define ADCX140_CH2_CFG0 0x41
+#define ADCX140_CH2_CFG1 0x42
+#define ADCX140_CH2_CFG2 0x43
+#define ADCX140_CH2_CFG3 0x44
+#define ADCX140_CH2_CFG4 0x45
+#define ADCX140_CH3_CFG0 0x46
+#define ADCX140_CH3_CFG1 0x47
+#define ADCX140_CH3_CFG2 0x48
+#define ADCX140_CH3_CFG3 0x49
+#define ADCX140_CH3_CFG4 0x4a
+#define ADCX140_CH4_CFG0 0x4b
+#define ADCX140_CH4_CFG1 0x4c
+#define ADCX140_CH4_CFG2 0x4d
+#define ADCX140_CH4_CFG3 0x4e
+#define ADCX140_CH4_CFG4 0x4f
+#define ADCX140_CH5_CFG2 0x52
+#define ADCX140_CH5_CFG3 0x53
+#define ADCX140_CH5_CFG4 0x54
+#define ADCX140_CH6_CFG2 0x57
+#define ADCX140_CH6_CFG3 0x58
+#define ADCX140_CH6_CFG4 0x59
+#define ADCX140_CH7_CFG2 0x5c
+#define ADCX140_CH7_CFG3 0x5d
+#define ADCX140_CH7_CFG4 0x5e
+#define ADCX140_CH8_CFG2 0x61
+#define ADCX140_CH8_CFG3 0x62
+#define ADCX140_CH8_CFG4 0x63
+#define ADCX140_DSP_CFG0 0x6b
+#define ADCX140_DSP_CFG1 0x6c
+#define ADCX140_DRE_CFG0 0x6d
+#define ADCX140_AGC_CFG0 0x70
+#define ADCX140_IN_CH_EN 0x73
+#define ADCX140_ASI_OUT_CH_EN 0x74
+#define ADCX140_PWR_CFG 0x75
+#define ADCX140_DEV_STS0 0x76
+#define ADCX140_DEV_STS1 0x77
+#define ADCX140_PHASE_CALIB 0X7b
+
+#define ADCX140_RESET BIT(0)
+
+#define ADCX140_WAKE_DEV BIT(0)
+#define ADCX140_AREG_INTERNAL BIT(7)
+
+#define ADCX140_BCLKINV_BIT BIT(2)
+#define ADCX140_FSYNCINV_BIT BIT(3)
+#define ADCX140_INV_MSK (ADCX140_BCLKINV_BIT | ADCX140_FSYNCINV_BIT)
+#define ADCX140_BCLK_FSYNC_MASTER BIT(7)
+#define ADCX140_I2S_MODE_BIT BIT(6)
+#define ADCX140_LEFT_JUST_BIT BIT(7)
+#define ADCX140_ASI_FORMAT_MSK (ADCX140_I2S_MODE_BIT | ADCX140_LEFT_JUST_BIT)
+
+#define ADCX140_16_BIT_WORD 0x0
+#define ADCX140_20_BIT_WORD BIT(4)
+#define ADCX140_24_BIT_WORD BIT(5)
+#define ADCX140_32_BIT_WORD (BIT(4) | BIT(5))
+#define ADCX140_WORD_LEN_MSK 0x30
+
+#define ADCX140_MAX_CHANNELS 8
+
+#define ADCX140_MIC_BIAS_VAL_VREF 0
+#define ADCX140_MIC_BIAS_VAL_VREF_1096 1
+#define ADCX140_MIC_BIAS_VAL_AVDD 6
+#define ADCX140_MIC_BIAS_VAL_MSK GENMASK(6, 4)
+#define ADCX140_MIC_BIAS_SHIFT 4
+
+#define ADCX140_MIC_BIAS_VREF_275V 0
+#define ADCX140_MIC_BIAS_VREF_25V 1
+#define ADCX140_MIC_BIAS_VREF_1375V 2
+#define ADCX140_MIC_BIAS_VREF_MSK GENMASK(1, 0)
+
+#define ADCX140_PWR_CTRL_MSK GENMASK(7, 5)
+#define ADCX140_PWR_CFG_BIAS_PDZ BIT(7)
+#define ADCX140_PWR_CFG_ADC_PDZ BIT(6)
+#define ADCX140_PWR_CFG_PLL_PDZ BIT(5)
+
+#define ADCX140_TX_OFFSET_MASK GENMASK(4, 0)
+
+#define ADCX140_NUM_PDM_EDGES 4
+#define ADCX140_PDM_EDGE_SHIFT 7
+
+#define ADCX140_NUM_GPI_PINS 4
+#define ADCX140_GPI_SHIFT 4
+#define ADCX140_GPI1_INDEX 0
+#define ADCX140_GPI2_INDEX 1
+#define ADCX140_GPI3_INDEX 2
+#define ADCX140_GPI4_INDEX 3
+
+#define ADCX140_NUM_GPOS 4
+#define ADCX140_NUM_GPO_CFGS 2
+#define ADCX140_GPO_SHIFT 4
+#define ADCX140_GPO_CFG_MAX 4
+#define ADCX140_GPO_DRV_MAX 5
+
+#define ADCX140_TX_FILL BIT(0)
+
+#define ADCX140_NUM_GPIO_CFGS 2
+#define ADCX140_GPIO_SHIFT 4
+#define ADCX140_GPIO_CFG_MAX 15
+#define ADCX140_GPIO_DRV_MAX 5
+
+#endif /* _TLV320ADCX140_ */
diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c
index 78a94af65518..a31fb95048b8 100644
--- a/sound/soc/codecs/tlv320aic23-i2c.c
+++ b/sound/soc/codecs/tlv320aic23-i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC TLV320AIC23 codec driver I2C interface
*
@@ -5,10 +6,6 @@
* 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>
@@ -19,8 +16,7 @@
#include "tlv320aic23.h"
-static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static int tlv320aic23_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -31,24 +27,20 @@ static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
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},
+ {"tlv320aic23"},
{}
};
MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
+#ifdef CONFIG_OF
static const struct of_device_id tlv320aic23_of_match[] = {
{ .compatible = "ti,tlv320aic23", },
{ }
};
MODULE_DEVICE_TABLE(of, tlv320aic23_of_match);
+#endif
static struct i2c_driver tlv320aic23_i2c_driver = {
.driver = {
@@ -56,7 +48,6 @@ static struct i2c_driver tlv320aic23_i2c_driver = {
.of_match_table = of_match_ptr(tlv320aic23_of_match),
},
.probe = tlv320aic23_i2c_probe,
- .remove = tlv320aic23_i2c_remove,
.id_table = tlv320aic23_id,
};
diff --git a/sound/soc/codecs/tlv320aic23-spi.c b/sound/soc/codecs/tlv320aic23-spi.c
index f801ae051658..10765ae76606 100644
--- a/sound/soc/codecs/tlv320aic23-spi.c
+++ b/sound/soc/codecs/tlv320aic23-spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC TLV320AIC23 codec driver SPI interface
*
@@ -5,10 +6,6 @@
* 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>
@@ -34,18 +31,11 @@ static int aic23_spi_probe(struct spi_device *spi)
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",
},
.probe = aic23_spi_probe,
- .remove = aic23_spi_remove,
};
module_spi_driver(aic23_spi);
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index cd8c02b6e4de..04ec8fb5c6e5 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC TLV320AIC23 codec driver
*
@@ -6,10 +7,6 @@
*
* 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
@@ -70,8 +67,6 @@ static SOC_ENUM_SINGLE_DECL(rec_src_enum,
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);
@@ -82,7 +77,7 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
u16 val, reg;
val = (ucontrol->value.integer.value[0] & 0x07);
@@ -96,8 +91,8 @@ static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
*/
val = (val >= 4) ? 4 : (3 - val);
- reg = snd_soc_read(codec, TLV320AIC23_ANLG) & (~0x1C0);
- snd_soc_write(codec, TLV320AIC23_ANLG, reg | (val << 6));
+ reg = snd_soc_component_read(component, TLV320AIC23_ANLG) & (~0x1C0);
+ snd_soc_component_write(component, TLV320AIC23_ANLG, reg | (val << 6));
return 0;
}
@@ -105,10 +100,10 @@ static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
u16 val;
- val = snd_soc_read(codec, TLV320AIC23_ANLG) & (0x1C0);
+ val = snd_soc_component_read(component, TLV320AIC23_ANLG) & (0x1C0);
val = val >> 6;
val = (val >= 4) ? 4 : (3 - val);
ucontrol->value.integer.value[0] = val;
@@ -174,10 +169,9 @@ static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
{"ROUT", NULL, "Output Mixer"},
/* Inputs */
- {"Line Input", "NULL", "LLINEIN"},
- {"Line Input", "NULL", "RLINEIN"},
-
- {"Mic Input", "NULL", "MICIN"},
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+ {"Mic Input", NULL, "MICIN"},
/* input mux */
{"Capture Source", "Line", "Line Input"},
@@ -297,10 +291,10 @@ static int find_rate(int mclk, u32 need_adc, u32 need_dac)
}
#ifdef DEBUG
-static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
+static void get_current_sample_rates(struct snd_soc_component *component, int mclk,
u32 *sample_rate_adc, u32 *sample_rate_dac)
{
- int src = snd_soc_read(codec, TLV320AIC23_SRATE);
+ int src = snd_soc_component_read(component, TLV320AIC23_SRATE);
int sr = (src >> 2) & 0x0f;
int val = (mclk / bosr_usb_divisor_table[src & 3]);
int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
@@ -314,7 +308,7 @@ static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
}
#endif
-static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
+static int set_sample_rate_control(struct snd_soc_component *component, int mclk,
u32 sample_rate_adc, u32 sample_rate_dac)
{
/* Search for the right sample rate */
@@ -324,11 +318,11 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
__func__, sample_rate_adc, sample_rate_dac);
return -EINVAL;
}
- snd_soc_write(codec, TLV320AIC23_SRATE, data);
+ snd_soc_component_write(component, TLV320AIC23_SRATE, data);
#ifdef DEBUG
{
u32 adc, dac;
- get_current_sample_rates(codec, mclk, &adc, &dac);
+ get_current_sample_rates(component, mclk, &adc, &dac);
printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
adc, dac, data);
}
@@ -340,10 +334,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
u16 iface_reg;
int ret;
- struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+ struct aic23 *aic23 = snd_soc_component_get_drvdata(component);
u32 sample_rate_adc = aic23->requested_adc;
u32 sample_rate_dac = aic23->requested_dac;
u32 sample_rate = params_rate(params);
@@ -357,12 +351,12 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
if (!sample_rate_dac)
sample_rate_dac = sample_rate;
}
- ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
+ ret = set_sample_rate_control(component, aic23->mclk, sample_rate_adc,
sample_rate_dac);
if (ret < 0)
return ret;
- iface_reg = snd_soc_read(codec, TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
+ iface_reg = snd_soc_component_read(component, TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
switch (params_width(params)) {
case 16:
@@ -377,7 +371,7 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
iface_reg |= (0x03 << 2);
break;
}
- snd_soc_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+ snd_soc_component_write(component, TLV320AIC23_DIGT_FMT, iface_reg);
return 0;
}
@@ -385,10 +379,10 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
/* set active */
- snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0001);
+ snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0001);
return 0;
}
@@ -396,13 +390,13 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct aic23 *aic23 = snd_soc_component_get_drvdata(component);
/* deactivate */
- if (!snd_soc_codec_is_active(codec)) {
+ if (!snd_soc_component_active(component)) {
udelay(50);
- snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aic23->requested_dac = 0;
@@ -410,19 +404,19 @@ static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
aic23->requested_adc = 0;
}
-static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
+static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 reg;
- reg = snd_soc_read(codec, TLV320AIC23_DIGT);
+ reg = snd_soc_component_read(component, TLV320AIC23_DIGT);
if (mute)
reg |= TLV320AIC23_DACM_MUTE;
else
reg &= ~TLV320AIC23_DACM_MUTE;
- snd_soc_write(codec, TLV320AIC23_DIGT, reg);
+ snd_soc_component_write(component, TLV320AIC23_DIGT, reg);
return 0;
}
@@ -430,17 +424,16 @@ static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface_reg;
- iface_reg = snd_soc_read(codec, TLV320AIC23_DIGT_FMT) & (~0x03);
+ iface_reg = snd_soc_component_read(component, TLV320AIC23_DIGT_FMT) & (~0x03);
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface_reg |= TLV320AIC23_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface_reg &= ~TLV320AIC23_MS_MASTER;
break;
default:
@@ -455,6 +448,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
case SND_SOC_DAIFMT_DSP_A:
iface_reg |= TLV320AIC23_LRP_ON;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_B:
iface_reg |= TLV320AIC23_FOR_DSP;
break;
@@ -468,7 +462,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
- snd_soc_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+ snd_soc_component_write(component, TLV320AIC23_DIGT_FMT, iface_reg);
return 0;
}
@@ -481,29 +475,29 @@ static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
+static int tlv320aic23_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- u16 reg = snd_soc_read(codec, TLV320AIC23_PWR) & 0x17f;
+ u16 reg = snd_soc_component_read(component, TLV320AIC23_PWR) & 0x17f;
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \
TLV320AIC23_DAC_OFF);
- snd_soc_write(codec, TLV320AIC23_PWR, reg);
+ snd_soc_component_write(component, TLV320AIC23_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_write(codec, TLV320AIC23_PWR,
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0);
+ snd_soc_component_write(component, TLV320AIC23_PWR, 0x1ff);
break;
}
return 0;
@@ -517,9 +511,10 @@ 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,
+ .mute_stream = tlv320aic23_mute,
.set_fmt = tlv320aic23_set_dai_fmt,
.set_sysclk = tlv320aic23_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver tlv320aic23_dai = {
@@ -539,56 +534,58 @@ static struct snd_soc_dai_driver tlv320aic23_dai = {
.ops = &tlv320aic23_dai_ops,
};
-static int tlv320aic23_resume(struct snd_soc_codec *codec)
+static int tlv320aic23_resume(struct snd_soc_component *component)
{
- struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+ struct aic23 *aic23 = snd_soc_component_get_drvdata(component);
regcache_mark_dirty(aic23->regmap);
regcache_sync(aic23->regmap);
return 0;
}
-static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
+static int tlv320aic23_component_probe(struct snd_soc_component *component)
{
/* Reset codec */
- snd_soc_write(codec, TLV320AIC23_RESET, 0);
+ snd_soc_component_write(component, TLV320AIC23_RESET, 0);
- snd_soc_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
+ snd_soc_component_write(component, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
/* Unmute input */
- snd_soc_update_bits(codec, TLV320AIC23_LINVOL,
+ snd_soc_component_update_bits(component, TLV320AIC23_LINVOL,
TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED);
- snd_soc_update_bits(codec, TLV320AIC23_RINVOL,
+ snd_soc_component_update_bits(component, TLV320AIC23_RINVOL,
TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED);
- snd_soc_update_bits(codec, TLV320AIC23_ANLG,
+ snd_soc_component_update_bits(component, TLV320AIC23_ANLG,
TLV320AIC23_BYPASS_ON | TLV320AIC23_MICM_MUTED,
0);
/* Default output volume */
- snd_soc_write(codec, TLV320AIC23_LCHNVOL,
+ snd_soc_component_write(component, TLV320AIC23_LCHNVOL,
TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK);
- snd_soc_write(codec, TLV320AIC23_RCHNVOL,
+ snd_soc_component_write(component, TLV320AIC23_RCHNVOL,
TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK);
- snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x1);
+ snd_soc_component_write(component, 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),
+static const struct snd_soc_component_driver soc_component_dev_tlv320aic23 = {
+ .probe = tlv320aic23_component_probe,
+ .resume = tlv320aic23_resume,
+ .set_bias_level = tlv320aic23_set_bias_level,
+ .controls = tlv320aic23_snd_controls,
+ .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls),
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
int tlv320aic23_probe(struct device *dev, struct regmap *regmap)
@@ -606,7 +603,8 @@ int tlv320aic23_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, aic23);
- return snd_soc_register_codec(dev, &soc_codec_dev_tlv320aic23,
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_tlv320aic23,
&tlv320aic23_dai, 1);
}
EXPORT_SYMBOL(tlv320aic23_probe);
diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h
index 3a7235a04a89..0226be40112d 100644
--- a/sound/soc/codecs/tlv320aic23.h
+++ b/sound/soc/codecs/tlv320aic23.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 2c904d7150ad..e5dfb3d752a3 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Texas Instruments TLV320AIC26 low power audio CODEC
* ALSA SoC CODEC driver
@@ -30,8 +31,8 @@ MODULE_LICENSE("GPL");
struct aic26 {
struct spi_device *spi;
struct regmap *regmap;
- struct snd_soc_codec *codec;
- int master;
+ struct snd_soc_component *component;
+ int clock_provider;
int datfm;
int mclk;
@@ -64,8 +65,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct aic26 *aic26 = snd_soc_component_get_drvdata(component);
int fsref, divisor, wlen, pval, jval, dval, qval;
u16 reg;
@@ -112,31 +113,31 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
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);
+ snd_soc_component_write(component, AIC26_REG_PLL_PROG1, reg);
reg = dval << 2;
- snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg);
+ snd_soc_component_write(component, AIC26_REG_PLL_PROG2, reg);
- /* Audio Control 3 (master mode, fsref rate) */
- if (aic26->master)
+ /* Audio Control 3 (clock provider mode, fsref rate) */
+ if (aic26->clock_provider)
reg = 0x0800;
if (fsref == 48000)
reg = 0x2000;
- snd_soc_update_bits(codec, AIC26_REG_AUDIO_CTRL3, 0xf800, reg);
+ snd_soc_component_update_bits(component, AIC26_REG_AUDIO_CTRL3, 0xf800, reg);
/* Audio Control 1 (FSref divisor) */
reg = wlen | aic26->datfm | (divisor << 3) | divisor;
- snd_soc_update_bits(codec, AIC26_REG_AUDIO_CTRL1, 0xfff, reg);
+ snd_soc_component_update_bits(component, AIC26_REG_AUDIO_CTRL1, 0xfff, reg);
return 0;
}
-/**
+/*
* aic26_mute - Mute control to reduce noise when changing audio format
*/
-static int aic26_mute(struct snd_soc_dai *dai, int mute)
+static int aic26_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct aic26 *aic26 = snd_soc_component_get_drvdata(component);
u16 reg;
dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
@@ -146,7 +147,7 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute)
reg = 0x8080;
else
reg = 0;
- snd_soc_update_bits(codec, AIC26_REG_DAC_GAIN, 0x8000, reg);
+ snd_soc_component_update_bits(component, AIC26_REG_DAC_GAIN, 0x8000, reg);
return 0;
}
@@ -154,8 +155,8 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic26 *aic26 = snd_soc_component_get_drvdata(component);
dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
" freq=%i, dir=%i)\n",
@@ -171,16 +172,15 @@ static int aic26_set_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic26 *aic26 = snd_soc_component_get_drvdata(component);
dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
codec_dai, fmt);
- /* 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;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP: aic26->clock_provider = 1; break;
+ case SND_SOC_DAIFMT_CBC_CFC: aic26->clock_provider = 0; break;
default:
dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL;
}
@@ -210,9 +210,10 @@ static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static const struct snd_soc_dai_ops aic26_dai_ops = {
.hw_params = aic26_hw_params,
- .digital_mute = aic26_mute,
+ .mute_stream = aic26_mute,
.set_sysclk = aic26_set_sysclk,
.set_fmt = aic26_set_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver aic26_dai = {
@@ -259,74 +260,77 @@ static const struct snd_kcontrol_new aic26_snd_controls[] = {
* SPI device portion of driver: sysfs files for debugging
*/
-static ssize_t aic26_keyclick_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t keyclick_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct aic26 *aic26 = dev_get_drvdata(dev);
int val, amp, freq, len;
- val = snd_soc_read(aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val = snd_soc_component_read(aic26->component, AIC26_REG_AUDIO_CTRL2);
amp = (val >> 12) & 0x7;
freq = (125 << ((val >> 8) & 0x7)) >> 1;
len = 2 * (1 + ((val >> 4) & 0xf));
- return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+ return sysfs_emit(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
}
/* Any write to the keyclick attribute will trigger the keyclick event */
-static ssize_t aic26_keyclick_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t keyclick_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct aic26 *aic26 = dev_get_drvdata(dev);
- snd_soc_update_bits(aic26->codec, AIC26_REG_AUDIO_CTRL2,
+ snd_soc_component_update_bits(aic26->component, AIC26_REG_AUDIO_CTRL2,
0x8000, 0x800);
return count;
}
-static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
+static DEVICE_ATTR_RW(keyclick);
/* ---------------------------------------------------------------------
* SoC CODEC portion of driver: probe and release routines
*/
-static int aic26_probe(struct snd_soc_codec *codec)
+static int aic26_probe(struct snd_soc_component *component)
{
- struct aic26 *aic26 = dev_get_drvdata(codec->dev);
+ struct aic26 *aic26 = dev_get_drvdata(component->dev);
int ret, reg;
- aic26->codec = codec;
+ aic26->component = component;
/* Reset the codec to power on defaults */
- snd_soc_write(codec, AIC26_REG_RESET, 0xBB00);
+ snd_soc_component_write(component, AIC26_REG_RESET, 0xBB00);
/* Power up CODEC */
- snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0);
+ snd_soc_component_write(component, AIC26_REG_POWER_CTRL, 0);
/* Audio Control 3 (master mode, fsref rate) */
- reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3);
+ reg = snd_soc_component_read(component, AIC26_REG_AUDIO_CTRL3);
reg &= ~0xf800;
reg |= 0x0800; /* set master mode */
- snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+ snd_soc_component_write(component, AIC26_REG_AUDIO_CTRL3, reg);
/* Register the sysfs files for debugging */
/* Create SysFS files */
- ret = device_create_file(codec->dev, &dev_attr_keyclick);
+ ret = device_create_file(component->dev, &dev_attr_keyclick);
if (ret)
- dev_info(codec->dev, "error creating sysfs files\n");
+ dev_info(component->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 snd_soc_component_driver aic26_soc_component_dev = {
+ .probe = aic26_probe,
+ .controls = aic26_snd_controls,
+ .num_controls = ARRAY_SIZE(aic26_snd_controls),
+ .dapm_widgets = tlv320aic26_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets),
+ .dapm_routes = tlv320aic26_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config aic26_regmap = {
@@ -357,25 +361,18 @@ static int aic26_spi_probe(struct spi_device *spi)
/* Initialize the driver data */
aic26->spi = spi;
dev_set_drvdata(&spi->dev, aic26);
- aic26->master = 1;
+ aic26->clock_provider = 1;
- ret = snd_soc_register_codec(&spi->dev,
- &aic26_soc_codec_dev, &aic26_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &aic26_soc_component_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",
},
.probe = aic26_spi_probe,
- .remove = aic26_spi_remove,
};
module_spi_driver(aic26_spi);
diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h
index 629b85e75409..c86569883e0c 100644
--- a/sound/soc/codecs/tlv320aic26.h
+++ b/sound/soc/codecs/tlv320aic26.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Texas Instruments TLV320AIC26 low power audio CODEC
* register definitions
@@ -5,8 +6,8 @@
* Copyright (C) 2008 Secret Lab Technologies Ltd.
*/
-#ifndef _TLV320AIC16_H_
-#define _TLV320AIC16_H_
+#ifndef _TLV320AIC26_H_
+#define _TLV320AIC26_H_
/* AIC26 Registers */
#define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5)
@@ -87,4 +88,4 @@ enum aic26_wlen {
AIC26_WLEN_32 = 3 << 10,
};
-#endif /* _TLV320AIC16_H_ */
+#endif /* _TLV320AIC26_H_ */
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index ee4def4f819f..4362c2c06ce8 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1,46 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * ALSA SoC TLV320AIC31XX codec driver
+ * ALSA SoC TLV320AIC31xx CODEC Driver
*
- * Copyright (C) 2014 Texas Instruments, Inc.
- *
- * Author: Jyri Sarha <jsarha@ti.com>
+ * Copyright (C) 2014-2017 Texas Instruments Incorporated - https://www.ti.com/
+ * 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,
+ * The TLV320AIC31xx series of audio codecs are low-power, highly integrated
+ * high performance codecs which provides a stereo DAC, a mono ADC,
* and mono/stereo Class-D speaker driver.
*/
+#include <linux/unaligned.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
+#include <linux/acpi.h>
+#include <linux/firmware.h>
#include <linux/of.h>
-#include <linux/of_gpio.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 <dt-bindings/sound/tlv320aic31xx-micbias.h>
+#include <dt-bindings/sound/tlv320aic31xx.h>
#include "tlv320aic31xx.h"
+static int aic31xx_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data);
+
static const struct reg_default aic31xx_reg_defaults[] = {
{ AIC31XX_CLKMUX, 0x00 },
{ AIC31XX_PLLPR, 0x11 },
@@ -96,6 +95,7 @@ static bool aic31xx_volatile(struct device *dev, unsigned int reg)
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
case AIC31XX_INTRDACFLAG2:
case AIC31XX_INTRADCFLAG2:
+ case AIC31XX_HSDETECT:
return true;
}
return false;
@@ -143,8 +143,7 @@ static const struct regmap_config aic31xx_i2c_regmap = {
.max_register = 12 * 128,
};
-#define AIC31XX_NUM_SUPPLIES 6
-static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = {
+static const char * const aic31xx_supply_names[] = {
"HPVDD",
"SPRVDD",
"SPLVDD",
@@ -153,27 +152,38 @@ static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = {
"DVDD",
};
+#define AIC31XX_NUM_SUPPLIES ARRAY_SIZE(aic31xx_supply_names)
+
struct aic31xx_disable_nb {
struct notifier_block nb;
struct aic31xx_priv *aic31xx;
};
struct aic31xx_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
u8 i2c_regs_status;
struct device *dev;
struct regmap *regmap;
+ enum aic31xx_type codec_type;
+ struct gpio_desc *gpio_reset;
+ int micbias_vg;
struct aic31xx_pdata pdata;
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
+ struct snd_soc_jack *jack;
+ u32 sysclk_id;
unsigned int sysclk;
u8 p_div;
int rate_div_line;
+ bool master_dapm_route_applied;
+ int irq;
+ u8 ocmv; /* output common-mode voltage */
};
struct aic31xx_rate_divs {
u32 mclk_p;
u32 rate;
+ u8 pll_r;
u8 pll_j;
u16 pll_d;
u16 dosr;
@@ -184,53 +194,73 @@ struct aic31xx_rate_divs {
u8 madc;
};
-/* ADC dividers can be disabled by cofiguring them to 0 */
+/* ADC dividers can be disabled by configuring them to 0 */
static const struct aic31xx_rate_divs aic31xx_divs[] = {
- /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
+ /* mclk/p rate pll: r 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},
+ { 512000, 8000, 4, 48, 0, 128, 48, 2, 128, 48, 2},
+ {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2},
+ {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3},
+ {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2},
/* 11.025k rate */
- {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},
+ { 705600, 11025, 3, 48, 0, 128, 24, 3, 128, 24, 3},
+ {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2},
+ {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3},
+ {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2},
/* 16k rate */
- {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},
+ { 512000, 16000, 4, 48, 0, 128, 16, 3, 128, 16, 3},
+ { 1024000, 16000, 2, 48, 0, 128, 16, 3, 128, 16, 3},
+ {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2},
+ {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3},
+ {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2},
/* 22.05k rate */
- {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},
+ { 705600, 22050, 4, 36, 0, 128, 12, 3, 128, 12, 3},
+ { 1411200, 22050, 2, 36, 0, 128, 12, 3, 128, 12, 3},
+ {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2},
+ {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3},
+ {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2},
/* 32k rate */
- {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},
+ { 1024000, 32000, 2, 48, 0, 128, 12, 2, 128, 12, 2},
+ { 2048000, 32000, 1, 48, 0, 128, 12, 2, 128, 12, 2},
+ {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2},
+ {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3},
+ {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2},
/* 44.1k rate */
- {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},
+ { 1411200, 44100, 2, 32, 0, 128, 8, 2, 128, 8, 2},
+ { 2822400, 44100, 1, 32, 0, 128, 8, 2, 128, 8, 2},
+ {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2},
+ {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3},
+ {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2},
/* 48k rate */
- {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},
+ { 1536000, 48000, 2, 32, 0, 128, 8, 2, 128, 8, 2},
+ { 3072000, 48000, 1, 32, 0, 128, 8, 2, 128, 8, 2},
+ {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2},
+ {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4},
+ {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2},
/* 88.2k rate */
- {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},
+ { 2822400, 88200, 2, 16, 0, 64, 8, 2, 64, 8, 2},
+ { 5644800, 88200, 1, 16, 0, 64, 8, 2, 64, 8, 2},
+ {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2},
+ {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3},
+ {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2},
/* 96k rate */
- {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},
+ { 3072000, 96000, 2, 16, 0, 64, 8, 2, 64, 8, 2},
+ { 6144000, 96000, 1, 16, 0, 64, 8, 2, 64, 8, 2},
+ {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2},
+ {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4},
+ {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2},
/* 176.4k rate */
- {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},
+ { 5644800, 176400, 2, 8, 0, 32, 8, 2, 32, 8, 2},
+ {11289600, 176400, 1, 8, 0, 32, 8, 2, 32, 8, 2},
+ {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2},
+ {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3},
+ {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2},
/* 192k rate */
- {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},
+ { 6144000, 192000, 2, 8, 0, 32, 8, 2, 32, 8, 2},
+ {12288000, 192000, 1, 8, 0, 32, 8, 2, 32, 8, 2},
+ {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2},
+ {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4},
+ {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2},
};
static const char * const ldac_in_text[] = {
@@ -256,10 +286,28 @@ static SOC_ENUM_SINGLE_DECL(mic1rp_p_enum, AIC31XX_MICPGAPI, 4,
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 char * const hp_poweron_time_text[] = {
+ "0us", "15.3us", "153us", "1.53ms", "15.3ms", "76.2ms",
+ "153ms", "304ms", "610ms", "1.22s", "3.04s", "6.1s" };
+
+static SOC_ENUM_SINGLE_DECL(hp_poweron_time_enum, AIC31XX_HPPOP, 3,
+ hp_poweron_time_text);
+
+static const char * const hp_rampup_step_text[] = {
+ "0ms", "0.98ms", "1.95ms", "3.9ms" };
+
+static SOC_ENUM_SINGLE_DECL(hp_rampup_step_enum, AIC31XX_HPPOP, 1,
+ hp_rampup_step_text);
+
+static const char * const vol_soft_step_mode_text[] = {
+ "fast", "slow", "disabled" };
+
+static SOC_ENUM_SINGLE_DECL(vol_soft_step_mode_enum, AIC31XX_DACSETUP, 0,
+ vol_soft_step_mode_text);
+
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0);
static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0);
@@ -272,10 +320,30 @@ 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[] = {
+static const struct snd_kcontrol_new common31xx_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
+ SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
+ AIC31XX_HPRGAIN, 2, 1, 0),
+ SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
+ AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
+
+ SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
+ AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
+
+ /* HP de-pop control: apply power not immediately but via ramp
+ * function with these psarameters. Note that power up sequence
+ * has to wait for this to complete; this is implemented by
+ * polling HP driver status in aic31xx_dapm_power_event()
+ */
+ SOC_ENUM("HP Output Driver Power-On time", hp_poweron_time_enum),
+ SOC_ENUM("HP Output Driver Ramp-up step", hp_rampup_step_enum),
+
+ SOC_ENUM("Volume Soft Stepping", vol_soft_step_mode_enum),
+};
+
+static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
adc_fgain_tlv),
@@ -285,14 +353,6 @@ static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
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[] = {
@@ -349,10 +409,11 @@ static int aic31xx_wait_bits(struct aic31xx_priv *aic31xx, unsigned int 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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
unsigned int reg = AIC31XX_DACFLAG1;
unsigned int mask;
+ unsigned int timeout = 500 * USEC_PER_MSEC;
switch (WIDGET_BIT(w->reg, w->shift)) {
case WIDGET_BIT(AIC31XX_DACSETUP, 7):
@@ -363,9 +424,13 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
break;
case WIDGET_BIT(AIC31XX_HPDRIVER, 7):
mask = AIC31XX_HPLDRVPWRSTATUS_MASK;
+ if (event == SND_SOC_DAPM_POST_PMU)
+ timeout = 7 * USEC_PER_SEC;
break;
case WIDGET_BIT(AIC31XX_HPDRIVER, 6):
mask = AIC31XX_HPRDRVPWRSTATUS_MASK;
+ if (event == SND_SOC_DAPM_POST_PMU)
+ timeout = 7 * USEC_PER_SEC;
break;
case WIDGET_BIT(AIC31XX_SPKAMP, 7):
mask = AIC31XX_SPLDRVPWRSTATUS_MASK;
@@ -378,35 +443,48 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
reg = AIC31XX_ADCFLAG;
break;
default:
- dev_err(codec->dev, "Unknown widget '%s' calling %s\n",
+ dev_err(component->dev, "Unknown widget '%s' calling %s\n",
w->name, __func__);
return -EINVAL;
}
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- return aic31xx_wait_bits(aic31xx, reg, mask, mask, 5000, 100);
+ return aic31xx_wait_bits(aic31xx, reg, mask, mask,
+ 5000, timeout / 5000);
case SND_SOC_DAPM_POST_PMD:
- return aic31xx_wait_bits(aic31xx, reg, mask, 0, 5000, 100);
+ return aic31xx_wait_bits(aic31xx, reg, mask, 0,
+ 5000, timeout / 5000);
default:
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Unhandled dapm widget event %d from %s\n",
event, w->name);
}
return 0;
}
-static const struct snd_kcontrol_new left_output_switches[] = {
+static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
};
-static const struct snd_kcontrol_new right_output_switches[] = {
+static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
};
+static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
+ SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
+ SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
+ SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
+ SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
+ SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
+};
+
static const struct snd_kcontrol_new p_term_mic1lp =
SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
@@ -434,30 +512,30 @@ static const struct snd_kcontrol_new aic31xx_dapm_spr_switch =
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* change mic bias voltage to user defined */
- snd_soc_update_bits(codec, AIC31XX_MICBIAS,
+ snd_soc_component_update_bits(component, AIC31XX_MICBIAS,
AIC31XX_MICBIAS_MASK,
- aic31xx->pdata.micbias_vg <<
+ aic31xx->micbias_vg <<
AIC31XX_MICBIAS_SHIFT);
- dev_dbg(codec->dev, "%s: turned on\n", __func__);
+ dev_dbg(component->dev, "%s: turned on\n", __func__);
break;
case SND_SOC_DAPM_PRE_PMD:
/* turn mic bias off */
- snd_soc_update_bits(codec, AIC31XX_MICBIAS,
+ snd_soc_component_update_bits(component, AIC31XX_MICBIAS,
AIC31XX_MICBIAS_MASK, 0);
- dev_dbg(codec->dev, "%s: turned off\n", __func__);
+ dev_dbg(component->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),
+static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DAC Left Input",
SND_SOC_NOPM, 0, 0, &ldac_in_control),
@@ -472,14 +550,7 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
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)),
-
+ /* HP */
SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
&aic31xx_dapm_hpl_switch),
SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
@@ -493,10 +564,38 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
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),
+ /* Mic Bias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Keep BCLK/WCLK enabled even if DAC/ADC is powered down */
+ SND_SOC_DAPM_SUPPLY("Activate I2S clocks", AIC31XX_IFACE2, 2, 0,
+ NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+ dac31xx_left_output_switches,
+ ARRAY_SIZE(dac31xx_left_output_switches)),
+ SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+ dac31xx_right_output_switches,
+ ARRAY_SIZE(dac31xx_right_output_switches)),
+};
+
+static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1LP"),
+ SND_SOC_DAPM_INPUT("MIC1RP"),
+ SND_SOC_DAPM_INPUT("MIC1LM"),
/* Input Selection to MIC_PGA */
SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
@@ -506,24 +605,27 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
&p_term_mic1lm),
+ /* ADC */
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
+ aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
&m_term_mic1lm),
+
/* Enabling & Disabling MIC Gain Ctl */
SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
7, 1, NULL, 0),
- /* 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"),
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+ aic31xx_left_output_switches,
+ ARRAY_SIZE(aic31xx_left_output_switches)),
+ SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+ aic31xx_right_output_switches,
+ ARRAY_SIZE(aic31xx_right_output_switches)),
- /* Inputs */
- SND_SOC_DAPM_INPUT("MIC1LP"),
- SND_SOC_DAPM_INPUT("MIC1RP"),
- SND_SOC_DAPM_INPUT("MIC1LM"),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
@@ -553,17 +655,42 @@ static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route
-aic31xx_audio_map[] = {
+common31xx_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 Input", "Left Data", "AIF IN"},
+ {"DAC Left Input", "Right Data", "AIF IN"},
+ {"DAC Left Input", "Mono", "AIF IN"},
+ {"DAC Right Input", "Left Data", "AIF IN"},
+ {"DAC Right Input", "Right Data", "AIF IN"},
+ {"DAC Right Input", "Mono", "AIF IN"},
{"DAC Left", NULL, "DAC Left Input"},
{"DAC Right", NULL, "DAC Right Input"},
+ /* HPL path */
+ {"HP Left", "Switch", "Output Left"},
+ {"HPL Driver", NULL, "HP Left"},
+ {"HPL", NULL, "HPL Driver"},
+
+ /* HPR path */
+ {"HP Right", "Switch", "Output Right"},
+ {"HPR Driver", NULL, "HP Right"},
+ {"HPR", NULL, "HPR Driver"},
+};
+
+static const struct snd_soc_dapm_route
+dac31xx_audio_map[] = {
+ /* Left Output */
+ {"Output Left", "From Left DAC", "DAC Left"},
+ {"Output Left", "From AIN1", "AIN1"},
+ {"Output Left", "From AIN2", "AIN2"},
+
+ /* Right Output */
+ {"Output Right", "From Right DAC", "DAC Right"},
+ {"Output Right", "From AIN2", "AIN2"},
+};
+
+static const struct snd_soc_dapm_route
+aic31xx_audio_map[] = {
/* Mic input */
{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
@@ -586,6 +713,8 @@ aic31xx_audio_map[] = {
{"ADC", NULL, "MIC_GAIN_CTL"},
+ {"AIF OUT", NULL, "ADC"},
+
/* Left Output */
{"Output Left", "From Left DAC", "DAC Left"},
{"Output Left", "From MIC1LP", "MIC1LP"},
@@ -594,16 +723,6 @@ aic31xx_audio_map[] = {
/* 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
@@ -627,30 +746,88 @@ aic310x_audio_map[] = {
{"SPK", NULL, "SPK ClassD"},
};
-static int aic31xx_add_controls(struct snd_soc_codec *codec)
+/*
+ * Always connected DAPM routes for codec clock master modes.
+ * If the codec is the master on the I2S bus, we need to power up components
+ * to have valid DAC_CLK.
+ *
+ * In order to have the I2S clocks on the bus either the DACs/ADC need to be
+ * enabled, or the P0/R29/D2 (Keep bclk/wclk in power down) need to be set.
+ *
+ * Otherwise the codec will not generate clocks on the bus.
+ */
+static const struct snd_soc_dapm_route
+common31xx_cm_audio_map[] = {
+ {"HPL", NULL, "AIF IN"},
+ {"HPR", NULL, "AIF IN"},
+
+ {"AIF IN", NULL, "Activate I2S clocks"},
+};
+
+static const struct snd_soc_dapm_route
+aic31xx_cm_audio_map[] = {
+ {"AIF OUT", NULL, "MIC1LP"},
+ {"AIF OUT", NULL, "MIC1RP"},
+ {"AIF OUT", NULL, "MIC1LM"},
+
+ {"AIF OUT", NULL, "Activate I2S clocks"},
+};
+
+static int aic31xx_add_controls(struct snd_soc_component *component)
{
int ret = 0;
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
- if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
- ret = snd_soc_add_codec_controls(
- codec, aic311x_snd_controls,
+ if (!(aic31xx->codec_type & DAC31XX_BIT))
+ ret = snd_soc_add_component_controls(
+ component, aic31xx_snd_controls,
+ ARRAY_SIZE(aic31xx_snd_controls));
+ if (ret)
+ return ret;
+
+ if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT)
+ ret = snd_soc_add_component_controls(
+ component, aic311x_snd_controls,
ARRAY_SIZE(aic311x_snd_controls));
else
- ret = snd_soc_add_codec_controls(
- codec, aic310x_snd_controls,
+ ret = snd_soc_add_component_controls(
+ component, aic310x_snd_controls,
ARRAY_SIZE(aic310x_snd_controls));
return ret;
}
-static int aic31xx_add_widgets(struct snd_soc_codec *codec)
+static int aic31xx_add_widgets(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
int ret = 0;
- if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
+ if (aic31xx->codec_type & DAC31XX_BIT) {
+ ret = snd_soc_dapm_new_controls(
+ dapm, dac31xx_dapm_widgets,
+ ARRAY_SIZE(dac31xx_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
+ ARRAY_SIZE(dac31xx_audio_map));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_dapm_new_controls(
+ dapm, aic31xx_dapm_widgets,
+ ARRAY_SIZE(aic31xx_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
+ ARRAY_SIZE(aic31xx_audio_map));
+ if (ret)
+ return ret;
+ }
+
+ if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
ret = snd_soc_dapm_new_controls(
dapm, aic311x_dapm_widgets,
ARRAY_SIZE(aic311x_dapm_widgets));
@@ -677,20 +854,26 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec)
return 0;
}
-static int aic31xx_setup_pll(struct snd_soc_codec *codec,
+static int aic31xx_setup_pll(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
int bclk_score = snd_soc_params_to_frame_size(params);
- int mclk_p = aic31xx->sysclk / aic31xx->p_div;
+ int mclk_p;
int bclk_n = 0;
int match = -1;
int i;
+ if (!aic31xx->sysclk || !aic31xx->p_div) {
+ dev_err(component->dev, "Master clock not supplied\n");
+ return -EINVAL;
+ }
+ mclk_p = aic31xx->sysclk / aic31xx->p_div;
+
/* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */
- snd_soc_update_bits(codec, AIC31XX_CLKMUX,
+ snd_soc_component_update_bits(component, AIC31XX_CLKMUX,
AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL);
- snd_soc_update_bits(codec, AIC31XX_IFACE2,
+ snd_soc_component_update_bits(component, AIC31XX_IFACE2,
AIC31XX_BDIVCLK_MASK, AIC31XX_DAC2BCLK);
for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) {
@@ -709,64 +892,70 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
}
if (match == -1) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: Sample rate (%u) and format not supported\n",
__func__, params_rate(params));
- /* See bellow for details how fix this. */
+ /* See below for details on how to fix this. */
return -EINVAL;
}
if (bclk_score != 0) {
- dev_warn(codec->dev, "Can not produce exact bitclock");
+ dev_warn(component->dev, "Can not produce exact bitclock");
/* This is fine if using dsp format, but if using i2s
there may be trouble. To fix the issue edit the
aic31xx_divs table for your mclk and sample
rate. Details can be found from:
- http://www.ti.com/lit/ds/symlink/tlv320aic3100.pdf
+ https://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_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
+ (aic31xx->p_div << 4) | aic31xx_divs[i].pll_r);
+ snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
- snd_soc_write(codec, AIC31XX_PLLDMSB,
+ snd_soc_component_write(component, AIC31XX_PLLDMSB,
aic31xx_divs[i].pll_d >> 8);
- snd_soc_write(codec, AIC31XX_PLLDLSB,
+ snd_soc_component_write(component, AIC31XX_PLLDLSB,
aic31xx_divs[i].pll_d & 0xff);
/* DAC dividers configuration */
- snd_soc_update_bits(codec, AIC31XX_NDAC, AIC31XX_PLL_MASK,
+ snd_soc_component_update_bits(component, AIC31XX_NDAC, AIC31XX_PLL_MASK,
aic31xx_divs[i].ndac);
- snd_soc_update_bits(codec, AIC31XX_MDAC, AIC31XX_PLL_MASK,
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_write(component, AIC31XX_DOSRMSB, aic31xx_divs[i].dosr >> 8);
+ snd_soc_component_write(component, AIC31XX_DOSRLSB, aic31xx_divs[i].dosr & 0xff);
/* ADC dividers configuration. Write reset value 1 if not used. */
- snd_soc_update_bits(codec, AIC31XX_NADC, AIC31XX_PLL_MASK,
+ snd_soc_component_update_bits(component, AIC31XX_NADC, AIC31XX_PLL_MASK,
aic31xx_divs[i].nadc ? aic31xx_divs[i].nadc : 1);
- snd_soc_update_bits(codec, AIC31XX_MADC, AIC31XX_PLL_MASK,
+ snd_soc_component_update_bits(component, AIC31XX_MADC, AIC31XX_PLL_MASK,
aic31xx_divs[i].madc ? aic31xx_divs[i].madc : 1);
- snd_soc_write(codec, AIC31XX_AOSR, aic31xx_divs[i].aosr);
+ snd_soc_component_write(component, AIC31XX_AOSR, aic31xx_divs[i].aosr);
/* Bit clock divider configuration. */
- snd_soc_update_bits(codec, AIC31XX_BCLKN,
+ snd_soc_component_update_bits(component, AIC31XX_BCLKN,
AIC31XX_PLL_MASK, bclk_n);
aic31xx->rate_div_line = i;
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
- aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d,
- aic31xx->p_div, aic31xx_divs[i].dosr,
- aic31xx_divs[i].ndac, aic31xx_divs[i].mdac,
- aic31xx_divs[i].aosr, aic31xx_divs[i].nadc,
- aic31xx_divs[i].madc, bclk_n);
+ 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;
}
@@ -775,10 +964,11 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
u8 data = 0;
- dev_dbg(codec->dev, "## %s: width %d rate %d\n",
+ dev_dbg(component->dev, "## %s: width %d rate %d\n",
__func__, params_width(params),
params_rate(params));
@@ -798,51 +988,128 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream,
AIC31XX_IFACE1_DATALEN_SHIFT);
break;
default:
- dev_err(codec->dev, "%s: Unsupported width %d\n",
+ dev_err(component->dev, "%s: Unsupported width %d\n",
__func__, params_width(params));
return -EINVAL;
}
- snd_soc_update_bits(codec, AIC31XX_IFACE1,
+ snd_soc_component_update_bits(component, AIC31XX_IFACE1,
AIC31XX_IFACE1_DATALEN_MASK,
data);
- return aic31xx_setup_pll(codec, params);
+ /*
+ * If BCLK is used as PLL input, the sysclk is determined by the hw
+ * params. So it must be updated here to match the input frequency.
+ */
+ if (aic31xx->sysclk_id == AIC31XX_PLL_CLKIN_BCLK) {
+ aic31xx->sysclk = params_rate(params) * params_width(params) *
+ params_channels(params);
+ aic31xx->p_div = 1;
+ }
+
+ return aic31xx_setup_pll(component, params);
}
-static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute)
+static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
if (mute) {
- snd_soc_update_bits(codec, AIC31XX_DACMUTE,
+ snd_soc_component_update_bits(component, AIC31XX_DACMUTE,
AIC31XX_DACMUTE_MASK,
AIC31XX_DACMUTE_MASK);
} else {
- snd_soc_update_bits(codec, AIC31XX_DACMUTE,
+ snd_soc_component_update_bits(component, AIC31XX_DACMUTE,
AIC31XX_DACMUTE_MASK, 0x0);
}
return 0;
}
+static int aic31xx_clock_master_routes(struct snd_soc_component *component,
+ unsigned int fmt)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ if (fmt == SND_SOC_DAIFMT_CBC_CFC &&
+ aic31xx->master_dapm_route_applied) {
+ /*
+ * Remove the DAPM route(s) for codec clock master modes,
+ * if applied
+ */
+ ret = snd_soc_dapm_del_routes(dapm, common31xx_cm_audio_map,
+ ARRAY_SIZE(common31xx_cm_audio_map));
+ if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
+ ret = snd_soc_dapm_del_routes(dapm,
+ aic31xx_cm_audio_map,
+ ARRAY_SIZE(aic31xx_cm_audio_map));
+
+ if (ret)
+ return ret;
+
+ aic31xx->master_dapm_route_applied = false;
+ } else if (fmt != SND_SOC_DAIFMT_CBC_CFC &&
+ !aic31xx->master_dapm_route_applied) {
+ /*
+ * Add the needed DAPM route(s) for codec clock master modes,
+ * if it is not done already
+ */
+ ret = snd_soc_dapm_add_routes(dapm, common31xx_cm_audio_map,
+ ARRAY_SIZE(common31xx_cm_audio_map));
+ if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
+ ret = snd_soc_dapm_add_routes(dapm,
+ aic31xx_cm_audio_map,
+ ARRAY_SIZE(aic31xx_cm_audio_map));
+
+ if (ret)
+ return ret;
+
+ aic31xx->master_dapm_route_applied = true;
+ }
+
+ return 0;
+}
+
static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 iface_reg1 = 0;
u8 iface_reg2 = 0;
u8 dsp_a_val = 0;
- dev_dbg(codec->dev, "## %s: fmt = 0x%x\n", __func__, fmt);
+ dev_dbg(component->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:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER;
break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ iface_reg1 |= AIC31XX_WCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ iface_reg1 |= AIC31XX_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
default:
- dev_alert(codec->dev, "Invalid DAI master/slave interface\n");
+ dev_err(component->dev, "Invalid DAI clock provider\n");
+ return -EINVAL;
+ }
+
+ /* signal polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface_reg2 |= AIC31XX_BCLKINV_MASK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI clock signal polarity\n");
return -EINVAL;
}
@@ -852,17 +1119,14 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
case SND_SOC_DAIFMT_DSP_A:
dsp_a_val = 0x1;
+ fallthrough;
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;
- }
+ /*
+ * NOTE: This CODEC samples on the falling edge of BCLK in
+ * DSP mode, this is inverted compared to what most DAIs
+ * expect, so we invert for this mode
+ */
+ iface_reg2 ^= AIC31XX_BCLKINV_MASK;
iface_reg1 |= (AIC31XX_DSP_MODE <<
AIC31XX_IFACE1_DATATYPE_SHIFT);
break;
@@ -875,46 +1139,47 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
AIC31XX_IFACE1_DATATYPE_SHIFT);
break;
default:
- dev_err(codec->dev, "Invalid DAI interface format\n");
+ dev_err(component->dev, "Invalid DAI interface format\n");
return -EINVAL;
}
- snd_soc_update_bits(codec, AIC31XX_IFACE1,
+ snd_soc_component_update_bits(component, AIC31XX_IFACE1,
AIC31XX_IFACE1_DATATYPE_MASK |
AIC31XX_IFACE1_MASTER_MASK,
iface_reg1);
- snd_soc_update_bits(codec, AIC31XX_DATA_OFFSET,
+ snd_soc_component_update_bits(component, AIC31XX_DATA_OFFSET,
AIC31XX_DATA_OFFSET_MASK,
dsp_a_val);
- snd_soc_update_bits(codec, AIC31XX_IFACE2,
+ snd_soc_component_update_bits(component, AIC31XX_IFACE2,
AIC31XX_BCLKINV_MASK,
iface_reg2);
- return 0;
+ return aic31xx_clock_master_routes(component, fmt);
}
static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
int i;
- dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
+ dev_dbg(component->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
__func__, clk_id, freq, dir);
- for (i = 1; freq/i > 20000000 && i < 8; i++)
- ;
+ for (i = 1; i < 8; i++)
+ if (freq / i <= 20000000)
+ break;
if (freq/i > 20000000) {
dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
__func__, freq);
- return -EINVAL;
+ return -EINVAL;
}
aic31xx->p_div = i;
- for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
- aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
- ;
+ for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++)
+ if (aic31xx_divs[i].mclk_p == freq / aic31xx->p_div)
+ break;
if (i == ARRAY_SIZE(aic31xx_divs)) {
dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
__func__, freq);
@@ -922,10 +1187,12 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
/* set clock on MCLK, BCLK, or GPIO1 as PLL input */
- snd_soc_update_bits(codec, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK,
+ snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK,
clk_id << AIC31XX_PLL_CLKIN_SHIFT);
+ aic31xx->sysclk_id = clk_id;
aic31xx->sysclk = freq;
+
return 0;
}
@@ -941,8 +1208,8 @@ static int aic31xx_regulator_event(struct notifier_block *nb,
* 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);
+ if (aic31xx->gpio_reset)
+ gpiod_set_value_cansleep(aic31xx->gpio_reset, 1);
regcache_mark_dirty(aic31xx->regmap);
dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__);
@@ -951,132 +1218,169 @@ static int aic31xx_regulator_event(struct notifier_block *nb,
return 0;
}
-static void aic31xx_clk_on(struct snd_soc_codec *codec)
+static int aic31xx_reset(struct aic31xx_priv *aic31xx)
{
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (aic31xx->gpio_reset) {
+ gpiod_set_value_cansleep(aic31xx->gpio_reset, 1);
+ ndelay(10); /* At least 10ns */
+ gpiod_set_value_cansleep(aic31xx->gpio_reset, 0);
+ } else {
+ ret = regmap_write(aic31xx->regmap, AIC31XX_RESET, 1);
+ }
+ mdelay(1); /* At least 1ms */
+
+ return ret;
+}
+
+static void aic31xx_clk_on(struct snd_soc_component *component)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
u8 mask = AIC31XX_PM_MASK;
u8 on = AIC31XX_PM_MASK;
- dev_dbg(codec->dev, "codec clock -> on (rate %d)\n",
+ dev_dbg(component->dev, "codec clock -> on (rate %d)\n",
aic31xx_divs[aic31xx->rate_div_line].rate);
- snd_soc_update_bits(codec, AIC31XX_PLLPR, mask, on);
+ snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, on);
mdelay(10);
- snd_soc_update_bits(codec, AIC31XX_NDAC, mask, on);
- snd_soc_update_bits(codec, AIC31XX_MDAC, mask, on);
+ snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, on);
+ snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, on);
if (aic31xx_divs[aic31xx->rate_div_line].nadc)
- snd_soc_update_bits(codec, AIC31XX_NADC, mask, on);
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_update_bits(component, AIC31XX_MADC, mask, on);
+ snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, on);
}
-static void aic31xx_clk_off(struct snd_soc_codec *codec)
+static void aic31xx_clk_off(struct snd_soc_component *component)
{
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);
+ dev_dbg(component->dev, "codec clock -> off\n");
+ snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, off);
+ snd_soc_component_update_bits(component, AIC31XX_MADC, mask, off);
+ snd_soc_component_update_bits(component, AIC31XX_NADC, mask, off);
+ snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, off);
+ snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, off);
+ snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, off);
}
-static int aic31xx_power_on(struct snd_soc_codec *codec)
+static int aic31xx_power_on(struct snd_soc_component *component)
{
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
+ int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies),
aic31xx->supplies);
if (ret)
return ret;
- if (gpio_is_valid(aic31xx->pdata.gpio_reset)) {
- gpio_set_value(aic31xx->pdata.gpio_reset, 1);
- udelay(100);
- }
regcache_cache_only(aic31xx->regmap, false);
+
+ /* Reset device registers for a consistent power-on like state */
+ ret = aic31xx_reset(aic31xx);
+ if (ret < 0)
+ dev_err(aic31xx->dev, "Could not reset device: %d\n", ret);
+
ret = regcache_sync(aic31xx->regmap);
- if (ret != 0) {
- dev_err(codec->dev,
+ if (ret) {
+ dev_err(component->dev,
"Failed to restore cache: %d\n", ret);
regcache_cache_only(aic31xx->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
aic31xx->supplies);
return ret;
}
+
+ /*
+ * The jack detection configuration is in the same register
+ * that is used to report jack detect status so is volatile
+ * and not covered by the cache sync, restore it separately.
+ */
+ aic31xx_set_jack(component, aic31xx->jack, NULL);
+
return 0;
}
-static int aic31xx_power_off(struct snd_soc_codec *codec)
+static void aic31xx_power_off(struct snd_soc_component *component)
{
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
regcache_cache_only(aic31xx->regmap, true);
- ret = regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
- aic31xx->supplies);
-
- return ret;
+ regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
+ aic31xx->supplies);
}
-static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
+static int aic31xx_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__,
- snd_soc_codec_get_bias_level(codec), level);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ dev_dbg(component->dev, "## %s: %d -> %d\n", __func__,
+ snd_soc_dapm_get_bias_level(dapm), level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
- aic31xx_clk_on(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY)
+ aic31xx_clk_on(component);
break;
case SND_SOC_BIAS_STANDBY:
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_OFF:
- aic31xx_power_on(codec);
+ aic31xx_power_on(component);
break;
case SND_SOC_BIAS_PREPARE:
- aic31xx_clk_off(codec);
+ aic31xx_clk_off(component);
break;
default:
BUG();
}
break;
case SND_SOC_BIAS_OFF:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
- aic31xx_power_off(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY)
+ aic31xx_power_off(component);
break;
}
return 0;
}
-static int aic31xx_codec_probe(struct snd_soc_codec *codec)
+static int aic31xx_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
{
- int ret = 0;
- struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
- int i;
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
- dev_dbg(aic31xx->dev, "## %s\n", __func__);
+ aic31xx->jack = jack;
- aic31xx = snd_soc_codec_get_drvdata(codec);
+ /* Enable/Disable jack detection */
+ regmap_write(aic31xx->regmap, AIC31XX_HSDETECT,
+ jack ? AIC31XX_HSD_ENABLE : 0);
- aic31xx->codec = codec;
+ return 0;
+}
+
+static int aic31xx_codec_probe(struct snd_soc_component *component)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
+ int i, ret;
+
+ dev_dbg(aic31xx->dev, "## %s\n", __func__);
+
+ aic31xx->component = component;
for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
aic31xx->disable_nb[i].nb.notifier_call =
aic31xx_regulator_event;
aic31xx->disable_nb[i].aic31xx = aic31xx;
- ret = regulator_register_notifier(aic31xx->supplies[i].consumer,
- &aic31xx->disable_nb[i].nb);
+ ret = devm_regulator_register_notifier(
+ aic31xx->supplies[i].consumer,
+ &aic31xx->disable_nb[i].nb);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to request regulator notifier: %d\n",
ret);
return ret;
@@ -1086,46 +1390,59 @@ static int aic31xx_codec_probe(struct snd_soc_codec *codec)
regcache_cache_only(aic31xx->regmap, true);
regcache_mark_dirty(aic31xx->regmap);
- ret = aic31xx_add_controls(codec);
+ ret = aic31xx_add_controls(component);
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;
+ ret = aic31xx_add_widgets(component);
+ if (ret)
+ return ret;
- for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
- regulator_unregister_notifier(aic31xx->supplies[i].consumer,
- &aic31xx->disable_nb[i].nb);
+ /* set output common-mode voltage */
+ snd_soc_component_update_bits(component, AIC31XX_HPDRIVER,
+ AIC31XX_HPD_OCMV_MASK,
+ aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
+static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
.probe = aic31xx_codec_probe,
- .remove = aic31xx_codec_remove,
+ .set_jack = aic31xx_set_jack,
.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),
+ .controls = common31xx_snd_controls,
+ .num_controls = ARRAY_SIZE(common31xx_snd_controls),
+ .dapm_widgets = common31xx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets),
+ .dapm_routes = common31xx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(common31xx_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct snd_soc_dai_ops aic31xx_dai_ops = {
.hw_params = aic31xx_hw_params,
.set_sysclk = aic31xx_set_dai_sysclk,
.set_fmt = aic31xx_set_dai_fmt,
- .digital_mute = aic31xx_dac_mute,
+ .mute_stream = aic31xx_dac_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
+ {
+ .name = "tlv320dac31xx-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC31XX_RATES,
+ .formats = AIC31XX_FORMATS,
+ },
+ .ops = &aic31xx_dai_ops,
+ .symmetric_rate = 1,
+ }
};
static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
@@ -1133,20 +1450,20 @@ static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
.name = "tlv320aic31xx-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
},
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
},
.ops = &aic31xx_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
@@ -1158,140 +1475,379 @@ static const struct of_device_id tlv320aic31xx_of_match[] = {
{ .compatible = "ti,tlv320aic3110" },
{ .compatible = "ti,tlv320aic3120" },
{ .compatible = "ti,tlv320aic3111" },
+ { .compatible = "ti,tlv320dac3100" },
+ { .compatible = "ti,tlv320dac3101" },
{},
};
MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match);
+#endif /* CONFIG_OF */
-static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx)
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id aic31xx_acpi_match[] = {
+ { "10TI3100", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
+#endif
+
+static irqreturn_t aic31xx_irq(int irq, void *data)
{
- struct device_node *np = aic31xx->dev->of_node;
- unsigned int value = MICBIAS_2_0V;
+ struct aic31xx_priv *aic31xx = data;
+ struct device *dev = aic31xx->dev;
+ unsigned int value;
+ bool handled = false;
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 = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
+ goto exit;
}
- ret = of_get_named_gpio(np, "gpio-reset", 0);
- if (ret > 0)
- aic31xx->pdata.gpio_reset = ret;
+ if (value)
+ handled = true;
+ else
+ goto read_overflow;
+
+ if (value & AIC31XX_HPLSCDETECT)
+ dev_err(dev, "Short circuit on Left output is detected\n");
+ if (value & AIC31XX_HPRSCDETECT)
+ dev_err(dev, "Short circuit on Right output is detected\n");
+ if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) {
+ unsigned int val;
+ int status = 0;
+
+ ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2,
+ &val);
+ if (ret) {
+ dev_err(dev, "Failed to read interrupt mask: %d\n",
+ ret);
+ goto exit;
+ }
+
+ if (val & AIC31XX_BUTTONPRESS)
+ status |= SND_JACK_BTN_0;
+
+ ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read headset type: %d\n", ret);
+ goto exit;
+ }
+
+ switch ((val & AIC31XX_HSD_TYPE_MASK) >>
+ AIC31XX_HSD_TYPE_SHIFT) {
+ case AIC31XX_HSD_HP:
+ status |= SND_JACK_HEADPHONE;
+ break;
+ case AIC31XX_HSD_HS:
+ status |= SND_JACK_HEADSET;
+ break;
+ default:
+ break;
+ }
+
+ if (aic31xx->jack)
+ snd_soc_jack_report(aic31xx->jack, status,
+ AIC31XX_JACK_MASK);
+ }
+ if (value & ~(AIC31XX_HPLSCDETECT |
+ AIC31XX_HPRSCDETECT |
+ AIC31XX_HSPLUG |
+ AIC31XX_BUTTONPRESS))
+ dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
+
+read_overflow:
+ ret = regmap_read(aic31xx->regmap, AIC31XX_OFFLAG, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read overflow flag: %d\n", ret);
+ goto exit;
+ }
+
+ if (value)
+ handled = true;
+ else
+ goto exit;
+
+ if (value & AIC31XX_DAC_OF_LEFT)
+ dev_warn(dev, "Left-channel DAC overflow has occurred\n");
+ if (value & AIC31XX_DAC_OF_RIGHT)
+ dev_warn(dev, "Right-channel DAC overflow has occurred\n");
+ if (value & AIC31XX_DAC_OF_SHIFTER)
+ dev_warn(dev, "DAC barrel shifter overflow has occurred\n");
+ if (value & AIC31XX_ADC_OF)
+ dev_warn(dev, "ADC overflow has occurred\n");
+ if (value & AIC31XX_ADC_OF_SHIFTER)
+ dev_warn(dev, "ADC barrel shifter overflow has occurred\n");
+ if (value & ~(AIC31XX_DAC_OF_LEFT |
+ AIC31XX_DAC_OF_RIGHT |
+ AIC31XX_DAC_OF_SHIFTER |
+ AIC31XX_ADC_OF |
+ AIC31XX_ADC_OF_SHIFTER))
+ dev_warn(dev, "Unknown overflow interrupt flags: 0x%08x\n", value);
+
+exit:
+ if (handled)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
}
-#else /* CONFIG_OF */
-static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx)
+
+static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
{
+ struct device *dev = priv->dev;
+ int dvdd, avdd;
+ u32 value;
+
+ if (dev->fwnode &&
+ fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) {
+ /* OCMV setting is forced by DT */
+ if (value <= 3) {
+ priv->ocmv = value;
+ return;
+ }
+ }
+
+ avdd = regulator_get_voltage(priv->supplies[3].consumer);
+ dvdd = regulator_get_voltage(priv->supplies[5].consumer);
+
+ if (avdd > 3600000 || dvdd > 1950000) {
+ dev_warn(dev,
+ "Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ } else if (avdd == 3600000 && dvdd == 1950000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_8V;
+ } else if (avdd >= 3300000 && dvdd >= 1800000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_65V;
+ } else if (avdd >= 3000000 && dvdd >= 1650000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_5V;
+ } else if (avdd >= 2700000 && dvdd >= 1525000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_35V;
+ } else {
+ dev_warn(dev,
+ "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ }
}
-#endif /* CONFIG_OF */
-static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
+static const struct i2c_device_id aic31xx_i2c_id[] = {
+ { "tlv320aic310x", AIC3100 },
+ { "tlv320aic311x", AIC3110 },
+ { "tlv320aic3100", AIC3100 },
+ { "tlv320aic3110", AIC3110 },
+ { "tlv320aic3120", AIC3120 },
+ { "tlv320aic3111", AIC3111 },
+ { "tlv320dac3100", DAC3100 },
+ { "tlv320dac3101", DAC3101 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
+
+static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx,
+ const u8 *data, size_t size)
{
- int ret, i;
+ int ret, reg;
+ u16 val16;
+
+ /*
+ * Coefficients firmware binary structure. Multi-byte values are big-endian.
+ *
+ * @0, 16bits: Magic (0xB30C)
+ * @2, 16bits: Version (0x0100 for version 1.0)
+ * @4, 8bits: DAC Processing Block Selection
+ * @5, 62 16-bit values: Page 8 buffer A DAC programmable filter coefficients
+ * @129, 12 16-bit values: Page 9 Buffer A DAC programmable filter coefficients
+ *
+ * Filter coefficients are interpreted as two's complement values
+ * ranging from -32 768 to 32 767. For more details on filter coefficients,
+ * please refer to the TLV320DAC3100 datasheet, tables 6-120 and 6-123.
+ */
+
+ if (size != 153) {
+ dev_err(aic31xx->dev, "firmware size is %zu, expected 153 bytes\n", size);
+ return -EINVAL;
+ }
- dev_set_drvdata(aic31xx->dev, aic31xx);
+ /* Check magic */
+ val16 = get_unaligned_be16(data);
+ if (val16 != 0xb30c) {
+ dev_err(aic31xx->dev, "fw magic is 0x%04x expected 0xb30c\n", val16);
+ return -EINVAL;
+ }
+ data += 2;
- 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");
+ /* Check version */
+ val16 = get_unaligned_be16(data);
+ if (val16 != 0x0100) {
+ dev_err(aic31xx->dev, "invalid firmware version 0x%04x! expected 1", val16);
+ return -EINVAL;
+ }
+ data += 2;
+
+ ret = regmap_write(aic31xx->regmap, AIC31XX_DACPRB, *data);
+ if (ret) {
+ dev_err(aic31xx->dev, "failed to write PRB index: err %d\n", ret);
+ return ret;
+ }
+ data += 1;
+
+ /* Page 8 Buffer A coefficients */
+ for (reg = 2; reg < 126; reg++) {
+ ret = regmap_write(aic31xx->regmap, AIC31XX_REG(8, reg), *data);
+ if (ret) {
+ dev_err(aic31xx->dev,
+ "failed to write page 8 filter coefficient %d: err %d\n", reg, ret);
return ret;
}
+ data++;
}
- for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
- aic31xx->supplies[i].supply = aic31xx_supply_names[i];
+ /* Page 9 Buffer A coefficients */
+ for (reg = 2; reg < 26; reg++) {
+ ret = regmap_write(aic31xx->regmap, AIC31XX_REG(9, reg), *data);
+ if (ret) {
+ dev_err(aic31xx->dev,
+ "failed to write page 9 filter coefficient %d: err %d\n", reg, ret);
+ return ret;
+ }
+ data++;
+ }
- 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);
+ dev_info(aic31xx->dev, "done loading DAC filter coefficients\n");
return ret;
}
-static int aic31xx_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx,
+ const char *fw_name)
{
- struct aic31xx_priv *aic31xx;
+ const struct firmware *fw;
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);
+ ret = request_firmware(&fw, fw_name, aic31xx->dev);
+ if (ret)
+ return ret;
+
+ ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size);
+
+ release_firmware(fw);
+
+ return ret;
+}
- regmap_config = &aic31xx_i2c_regmap;
+static int aic31xx_i2c_probe(struct i2c_client *i2c)
+{
+ struct aic31xx_priv *aic31xx;
+ unsigned int micbias_value = MICBIAS_2_0V;
+ int i, ret;
aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
- if (aic31xx == NULL)
+ if (!aic31xx)
return -ENOMEM;
- aic31xx->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap);
if (IS_ERR(aic31xx->regmap)) {
ret = PTR_ERR(aic31xx->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
+ regcache_cache_only(aic31xx->regmap, true);
+
aic31xx->dev = &i2c->dev;
+ aic31xx->irq = i2c->irq;
+
+ aic31xx->codec_type = (uintptr_t)i2c_get_match_data(i2c);
- aic31xx->pdata.codec_type = id->driver_data;
+ dev_set_drvdata(aic31xx->dev, aic31xx);
- ret = aic31xx_device_init(aic31xx);
+ fwnode_property_read_u32(aic31xx->dev->fwnode, "ai31xx-micbias-vg",
+ &micbias_value);
+ switch (micbias_value) {
+ case MICBIAS_2_0V:
+ case MICBIAS_2_5V:
+ case MICBIAS_AVDDV:
+ aic31xx->micbias_vg = micbias_value;
+ break;
+ default:
+ dev_err(aic31xx->dev, "Bad ai31xx-micbias-vg value %d\n",
+ micbias_value);
+ aic31xx->micbias_vg = MICBIAS_2_0V;
+ }
+
+ if (dev_get_platdata(aic31xx->dev)) {
+ memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev), sizeof(aic31xx->pdata));
+ aic31xx->codec_type = aic31xx->pdata.codec_type;
+ aic31xx->micbias_vg = aic31xx->pdata.micbias_vg;
+ }
+
+ aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(aic31xx->gpio_reset))
+ return dev_err_probe(aic31xx->dev, PTR_ERR(aic31xx->gpio_reset),
+ "not able to acquire gpio\n");
+
+ for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
+ aic31xx->supplies[i].supply = aic31xx_supply_names[i];
+
+ ret = devm_regulator_bulk_get(aic31xx->dev,
+ ARRAY_SIZE(aic31xx->supplies),
+ aic31xx->supplies);
if (ret)
- return ret;
+ return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n");
+
+ aic31xx_configure_ocmv(aic31xx);
+
+ if (aic31xx->irq > 0) {
+ regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
+ AIC31XX_GPIO1_FUNC_MASK,
+ AIC31XX_GPIO1_INT1 <<
+ AIC31XX_GPIO1_FUNC_SHIFT);
+
+ regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
+ AIC31XX_HSPLUGDET |
+ AIC31XX_BUTTONPRESSDET |
+ AIC31XX_SC |
+ AIC31XX_ENGINE);
+
+ ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq,
+ NULL, aic31xx_irq,
+ IRQF_ONESHOT, "aic31xx-irq",
+ aic31xx);
+ if (ret) {
+ dev_err(aic31xx->dev, "Unable to request IRQ\n");
+ return ret;
+ }
+ }
- return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
- aic31xx_dai_driver,
- ARRAY_SIZE(aic31xx_dai_driver));
-}
+ if (aic31xx->codec_type == DAC3100) {
+ ret = tlv320dac3100_load_coeffs(aic31xx, "tlv320dac3100-coeffs.bin");
+ if (ret)
+ dev_warn(aic31xx->dev, "Did not load any filter coefficients\n");
+ }
-static int aic31xx_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
+ if (aic31xx->codec_type & DAC31XX_BIT)
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_driver_aic31xx,
+ dac31xx_dai_driver,
+ ARRAY_SIZE(dac31xx_dai_driver));
+ else
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_driver_aic31xx,
+ aic31xx_dai_driver,
+ ARRAY_SIZE(aic31xx_dai_driver));
}
-static 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",
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
+ .acpi_match_table = ACPI_PTR(aic31xx_acpi_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");
+MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
+MODULE_DESCRIPTION("ASoC TLV320AIC31xx CODEC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index fe16c34607bb..80d062578fb5 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -1,35 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * 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.
+ * ALSA SoC TLV320AIC31xx CODEC Driver Definitions
*
+ * Copyright (C) 2014-2017 Texas Instruments Incorporated - https://www.ti.com/
*/
+
#ifndef _TLV320AIC31XX_H
#define _TLV320AIC31XX_H
#define AIC31XX_RATES SNDRV_PCM_RATE_8000_192000
-#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
- | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \
- | SNDRV_PCM_FMTBIT_S32_LE)
+#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define AIC31XX_STEREO_CLASS_D_BIT BIT(1)
+#define AIC31XX_MINIDSP_BIT BIT(2)
+#define DAC31XX_BIT BIT(3)
-#define AIC31XX_STEREO_CLASS_D_BIT 0x1
-#define AIC31XX_MINIDSP_BIT 0x2
+#define AIC31XX_JACK_MASK (SND_JACK_HEADPHONE | \
+ SND_JACK_HEADSET | \
+ SND_JACK_BTN_0)
enum aic31xx_type {
AIC3100 = 0,
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
AIC3120 = AIC31XX_MINIDSP_BIT,
- AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
+ AIC3111 = AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT,
+ DAC3100 = DAC31XX_BIT,
+ DAC3101 = DAC31XX_BIT | AIC31XX_STEREO_CLASS_D_BIT,
};
struct aic31xx_pdata {
@@ -38,222 +39,206 @@ struct aic31xx_pdata {
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_REG(page, reg) ((page * 128) + reg)
-#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
+#define AIC31XX_PAGECTL AIC31XX_REG(0, 0) /* Page Control Register */
+/* Page 0 Registers */
+#define AIC31XX_RESET AIC31XX_REG(0, 1) /* Software reset register */
+#define AIC31XX_OT_FLAG AIC31XX_REG(0, 3) /* OT FLAG register */
+#define AIC31XX_CLKMUX AIC31XX_REG(0, 4) /* Clock clock Gen muxing, Multiplexers*/
+#define AIC31XX_PLLPR AIC31XX_REG(0, 5) /* PLL P and R-VAL register */
+#define AIC31XX_PLLJ AIC31XX_REG(0, 6) /* PLL J-VAL register */
+#define AIC31XX_PLLDMSB AIC31XX_REG(0, 7) /* PLL D-VAL MSB register */
+#define AIC31XX_PLLDLSB AIC31XX_REG(0, 8) /* PLL D-VAL LSB register */
+#define AIC31XX_NDAC AIC31XX_REG(0, 11) /* DAC NDAC_VAL register*/
+#define AIC31XX_MDAC AIC31XX_REG(0, 12) /* DAC MDAC_VAL register */
+#define AIC31XX_DOSRMSB AIC31XX_REG(0, 13) /* DAC OSR setting register 1, MSB value */
+#define AIC31XX_DOSRLSB AIC31XX_REG(0, 14) /* DAC OSR setting register 2, LSB value */
+#define AIC31XX_MINI_DSP_INPOL AIC31XX_REG(0, 16)
+#define AIC31XX_NADC AIC31XX_REG(0, 18) /* Clock setting register 8, PLL */
+#define AIC31XX_MADC AIC31XX_REG(0, 19) /* Clock setting register 9, PLL */
+#define AIC31XX_AOSR AIC31XX_REG(0, 20) /* ADC Oversampling (AOSR) Register */
+#define AIC31XX_CLKOUTMUX AIC31XX_REG(0, 25) /* Clock setting register 9, Multiplexers */
+#define AIC31XX_CLKOUTMVAL AIC31XX_REG(0, 26) /* Clock setting register 10, CLOCKOUT M divider value */
+#define AIC31XX_IFACE1 AIC31XX_REG(0, 27) /* Audio Interface Setting Register 1 */
+#define AIC31XX_DATA_OFFSET AIC31XX_REG(0, 28) /* Audio Data Slot Offset Programming */
+#define AIC31XX_IFACE2 AIC31XX_REG(0, 29) /* Audio Interface Setting Register 2 */
+#define AIC31XX_BCLKN AIC31XX_REG(0, 30) /* Clock setting register 11, BCLK N Divider */
+#define AIC31XX_IFACESEC1 AIC31XX_REG(0, 31) /* Audio Interface Setting Register 3, Secondary Audio Interface */
+#define AIC31XX_IFACESEC2 AIC31XX_REG(0, 32) /* Audio Interface Setting Register 4 */
+#define AIC31XX_IFACESEC3 AIC31XX_REG(0, 33) /* Audio Interface Setting Register 5 */
+#define AIC31XX_I2C AIC31XX_REG(0, 34) /* I2C Bus Condition */
+#define AIC31XX_ADCFLAG AIC31XX_REG(0, 36) /* ADC FLAG */
+#define AIC31XX_DACFLAG1 AIC31XX_REG(0, 37) /* DAC Flag Registers */
+#define AIC31XX_DACFLAG2 AIC31XX_REG(0, 38)
+#define AIC31XX_OFFLAG AIC31XX_REG(0, 39) /* Sticky Interrupt flag (overflow) */
+#define AIC31XX_INTRDACFLAG AIC31XX_REG(0, 44) /* Sticy DAC Interrupt flags */
+#define AIC31XX_INTRADCFLAG AIC31XX_REG(0, 45) /* Sticy ADC Interrupt flags */
+#define AIC31XX_INTRDACFLAG2 AIC31XX_REG(0, 46) /* DAC Interrupt flags 2 */
+#define AIC31XX_INTRADCFLAG2 AIC31XX_REG(0, 47) /* ADC Interrupt flags 2 */
+#define AIC31XX_INT1CTRL AIC31XX_REG(0, 48) /* INT1 interrupt control */
+#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49) /* INT2 interrupt control */
+#define AIC31XX_GPIO1 AIC31XX_REG(0, 51) /* GPIO1 control */
+#define AIC31XX_DACPRB AIC31XX_REG(0, 60)
+#define AIC31XX_ADCPRB AIC31XX_REG(0, 61) /* ADC Instruction Set Register */
+#define AIC31XX_DACSETUP AIC31XX_REG(0, 63) /* DAC channel setup register */
+#define AIC31XX_DACMUTE AIC31XX_REG(0, 64) /* DAC Mute and volume control register */
+#define AIC31XX_LDACVOL AIC31XX_REG(0, 65) /* Left DAC channel digital volume control */
+#define AIC31XX_RDACVOL AIC31XX_REG(0, 66) /* Right DAC channel digital volume control */
+#define AIC31XX_HSDETECT AIC31XX_REG(0, 67) /* Headset detection */
+#define AIC31XX_ADCSETUP AIC31XX_REG(0, 81) /* ADC Digital Mic */
+#define AIC31XX_ADCFGA AIC31XX_REG(0, 82) /* ADC Digital Volume Control Fine Adjust */
+#define AIC31XX_ADCVOL AIC31XX_REG(0, 83) /* ADC Digital Volume Control Coarse Adjust */
/* Page 1 Registers */
-/* 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 */
+#define AIC31XX_HPDRIVER AIC31XX_REG(1, 31) /* Headphone drivers */
+#define AIC31XX_SPKAMP AIC31XX_REG(1, 32) /* Class-D Speakear Amplifier */
+#define AIC31XX_HPPOP AIC31XX_REG(1, 33) /* HP Output Drivers POP Removal Settings */
+#define AIC31XX_SPPGARAMP AIC31XX_REG(1, 34) /* Output Driver PGA Ramp-Down Period Control */
+#define AIC31XX_DACMIXERROUTE AIC31XX_REG(1, 35) /* DAC_L and DAC_R Output Mixer Routing */
+#define AIC31XX_LANALOGHPL AIC31XX_REG(1, 36) /* Left Analog Vol to HPL */
+#define AIC31XX_RANALOGHPR AIC31XX_REG(1, 37) /* Right Analog Vol to HPR */
+#define AIC31XX_LANALOGSPL AIC31XX_REG(1, 38) /* Left Analog Vol to SPL */
+#define AIC31XX_RANALOGSPR AIC31XX_REG(1, 39) /* Right Analog Vol to SPR */
+#define AIC31XX_HPLGAIN AIC31XX_REG(1, 40) /* HPL Driver */
+#define AIC31XX_HPRGAIN AIC31XX_REG(1, 41) /* HPR Driver */
+#define AIC31XX_SPLGAIN AIC31XX_REG(1, 42) /* SPL Driver */
+#define AIC31XX_SPRGAIN AIC31XX_REG(1, 43) /* SPR Driver */
+#define AIC31XX_HPCONTROL AIC31XX_REG(1, 44) /* HP Driver Control */
+#define AIC31XX_MICBIAS AIC31XX_REG(1, 46) /* MIC Bias Control */
+#define AIC31XX_MICPGA AIC31XX_REG(1, 47) /* MIC PGA*/
+#define AIC31XX_MICPGAPI AIC31XX_REG(1, 48) /* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */
+#define AIC31XX_MICPGAMI AIC31XX_REG(1, 49) /* ADC Input Selection for M-Terminal */
+#define AIC31XX_MICPGACM AIC31XX_REG(1, 50) /* Input CM Settings */
+
+/* Bits, masks, and shifts */
/* AIC31XX_CLKMUX */
-#define AIC31XX_PLL_CLKIN_MASK 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
+#define AIC31XX_PLL_CLKIN_MASK GENMASK(3, 2)
+#define AIC31XX_PLL_CLKIN_SHIFT (2)
+#define AIC31XX_PLL_CLKIN_MCLK 0x00
+#define AIC31XX_PLL_CLKIN_BCLK 0x01
+#define AIC31XX_PLL_CLKIN_GPIO1 0x02
+#define AIC31XX_PLL_CLKIN_DIN 0x03
+#define AIC31XX_CODEC_CLKIN_MASK GENMASK(1, 0)
+#define AIC31XX_CODEC_CLKIN_SHIFT (0)
+#define AIC31XX_CODEC_CLKIN_MCLK 0x00
+#define AIC31XX_CODEC_CLKIN_BCLK 0x01
+#define AIC31XX_CODEC_CLKIN_GPIO1 0x02
+#define AIC31XX_CODEC_CLKIN_PLL 0x03
+
+/* AIC31XX_PLLPR */
+/* AIC31XX_NDAC */
+/* AIC31XX_MDAC */
+/* AIC31XX_NADC */
+/* AIC31XX_MADC */
+/* AIC31XX_BCLKN */
+#define AIC31XX_PLL_MASK GENMASK(6, 0)
+#define AIC31XX_PM_MASK BIT(7)
/* AIC31XX_IFACE1 */
-#define AIC31XX_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_MASK GENMASK(7, 6)
#define AIC31XX_IFACE1_DATATYPE_SHIFT (6)
#define AIC31XX_I2S_MODE 0x00
#define AIC31XX_DSP_MODE 0x01
#define AIC31XX_RIGHT_JUSTIFIED_MODE 0x02
#define AIC31XX_LEFT_JUSTIFIED_MODE 0x03
-#define AIC31XX_IFACE1_MASTER_MASK 0x0C
-#define AIC31XX_BCLK_MASTER 0x08
-#define AIC31XX_WCLK_MASTER 0x04
+#define AIC31XX_IFACE1_DATALEN_MASK GENMASK(5, 4)
+#define AIC31XX_IFACE1_DATALEN_SHIFT (4)
+#define AIC31XX_WORD_LEN_16BITS 0x00
+#define AIC31XX_WORD_LEN_20BITS 0x01
+#define AIC31XX_WORD_LEN_24BITS 0x02
+#define AIC31XX_WORD_LEN_32BITS 0x03
+#define AIC31XX_IFACE1_MASTER_MASK GENMASK(3, 2)
+#define AIC31XX_BCLK_MASTER BIT(3)
+#define AIC31XX_WCLK_MASTER BIT(2)
/* AIC31XX_DATA_OFFSET */
-#define AIC31XX_DATA_OFFSET_MASK 0xFF
+#define AIC31XX_DATA_OFFSET_MASK GENMASK(7, 0)
/* AIC31XX_IFACE2 */
-#define AIC31XX_BCLKINV_MASK 0x08
-#define AIC31XX_BDIVCLK_MASK 0x03
+#define AIC31XX_BCLKINV_MASK BIT(3)
+#define AIC31XX_BDIVCLK_MASK GENMASK(1, 0)
#define AIC31XX_DAC2BCLK 0x00
#define AIC31XX_DACMOD2BCLK 0x01
#define AIC31XX_ADC2BCLK 0x02
#define AIC31XX_ADCMOD2BCLK 0x03
+#define AIC31XX_KEEP_I2SCLK BIT(2)
/* AIC31XX_ADCFLAG */
-#define AIC31XX_ADCPWRSTATUS_MASK 0x40
+#define AIC31XX_ADCPWRSTATUS_MASK BIT(6)
/* 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
+#define AIC31XX_LDACPWRSTATUS_MASK BIT(7)
+#define AIC31XX_HPLDRVPWRSTATUS_MASK BIT(5)
+#define AIC31XX_SPLDRVPWRSTATUS_MASK BIT(4)
+#define AIC31XX_RDACPWRSTATUS_MASK BIT(3)
+#define AIC31XX_HPRDRVPWRSTATUS_MASK BIT(1)
+#define AIC31XX_SPRDRVPWRSTATUS_MASK BIT(0)
+
+/* AIC31XX_OFFLAG */
+#define AIC31XX_DAC_OF_LEFT BIT(7)
+#define AIC31XX_DAC_OF_RIGHT BIT(6)
+#define AIC31XX_DAC_OF_SHIFTER BIT(5)
+#define AIC31XX_ADC_OF BIT(3)
+#define AIC31XX_ADC_OF_SHIFTER BIT(1)
/* AIC31XX_INTRDACFLAG */
-#define AIC31XX_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
+#define AIC31XX_HPLSCDETECT BIT(7)
+#define AIC31XX_HPRSCDETECT BIT(6)
+#define AIC31XX_BUTTONPRESS BIT(5)
+#define AIC31XX_HSPLUG BIT(4)
+#define AIC31XX_LDRCTHRES BIT(3)
+#define AIC31XX_RDRCTHRES BIT(2)
+#define AIC31XX_DACSINT BIT(1)
+#define AIC31XX_DACAINT BIT(0)
/* AIC31XX_INT1CTRL */
-#define AIC31XX_HSPLUGDET_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
+#define AIC31XX_HSPLUGDET BIT(7)
+#define AIC31XX_BUTTONPRESSDET BIT(6)
+#define AIC31XX_DRCTHRES BIT(5)
+#define AIC31XX_AGCNOISE BIT(4)
+#define AIC31XX_SC BIT(3)
+#define AIC31XX_ENGINE BIT(2)
+
+/* AIC31XX_GPIO1 */
+#define AIC31XX_GPIO1_FUNC_MASK GENMASK(5, 2)
+#define AIC31XX_GPIO1_FUNC_SHIFT 2
+#define AIC31XX_GPIO1_DISABLED 0x00
+#define AIC31XX_GPIO1_INPUT 0x01
+#define AIC31XX_GPIO1_GPI 0x02
+#define AIC31XX_GPIO1_GPO 0x03
+#define AIC31XX_GPIO1_CLKOUT 0x04
+#define AIC31XX_GPIO1_INT1 0x05
+#define AIC31XX_GPIO1_INT2 0x06
+#define AIC31XX_GPIO1_ADC_WCLK 0x07
+#define AIC31XX_GPIO1_SBCLK 0x08
+#define AIC31XX_GPIO1_SWCLK 0x09
+#define AIC31XX_GPIO1_ADC_MOD_CLK 0x10
+#define AIC31XX_GPIO1_SDOUT 0x11
/* AIC31XX_DACMUTE */
-#define AIC31XX_DACMUTE_MASK 0x0C
+#define AIC31XX_DACMUTE_MASK GENMASK(3, 2)
+
+/* AIC31XX_HSDETECT */
+#define AIC31XX_HSD_ENABLE BIT(7)
+#define AIC31XX_HSD_TYPE_MASK GENMASK(6, 5)
+#define AIC31XX_HSD_TYPE_SHIFT 5
+#define AIC31XX_HSD_NONE 0x00
+#define AIC31XX_HSD_HP 0x01
+#define AIC31XX_HSD_HS 0x03
+
+/* AIC31XX_HPDRIVER */
+#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3)
+#define AIC31XX_HPD_OCMV_SHIFT 3
+#define AIC31XX_HPD_OCMV_1_35V 0x0
+#define AIC31XX_HPD_OCMV_1_5V 0x1
+#define AIC31XX_HPD_OCMV_1_65V 0x2
+#define AIC31XX_HPD_OCMV_1_8V 0x3
/* AIC31XX_MICBIAS */
-#define AIC31XX_MICBIAS_MASK 0x03
-#define AIC31XX_MICBIAS_SHIFT 0
+#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
+#define AIC31XX_MICBIAS_SHIFT 0
#endif /* _TLV320AIC31XX_H */
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c
new file mode 100644
index 000000000000..5c0a76a4a106
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic32x4-clk.c
@@ -0,0 +1,499 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Clock Tree for the Texas Instruments TLV320AIC32x4
+ *
+ * Copyright 2019 Annaliese McDermond
+ *
+ * Author: Annaliese McDermond <nh6z@nh6z.net>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+
+#include "tlv320aic32x4.h"
+
+#define to_clk_aic32x4(_hw) container_of(_hw, struct clk_aic32x4, hw)
+struct clk_aic32x4 {
+ struct clk_hw hw;
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int reg;
+};
+
+/*
+ * struct clk_aic32x4_pll_muldiv - Multiplier/divider settings
+ * @p: Divider
+ * @r: first multiplier
+ * @j: integer part of second multiplier
+ * @d: decimal part of second multiplier
+ */
+struct clk_aic32x4_pll_muldiv {
+ u8 p;
+ u16 r;
+ u8 j;
+ u16 d;
+};
+
+struct aic32x4_clkdesc {
+ const char *name;
+ const char * const *parent_names;
+ unsigned int num_parents;
+ const struct clk_ops *ops;
+ unsigned int reg;
+};
+
+static int clk_aic32x4_pll_prepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLLEN, AIC32X4_PLLEN);
+}
+
+static void clk_aic32x4_pll_unprepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLLEN, 0);
+}
+
+static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & AIC32X4_PLLEN);
+}
+
+static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll,
+ struct clk_aic32x4_pll_muldiv *settings)
+{
+ /* Change to use regmap_bulk_read? */
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
+ if (ret < 0)
+ return ret;
+ settings->r = val & AIC32X4_PLL_R_MASK;
+ settings->p = (val & AIC32X4_PLL_P_MASK) >> AIC32X4_PLL_P_SHIFT;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLJ, &val);
+ if (ret < 0)
+ return ret;
+ settings->j = val;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLDMSB, &val);
+ if (ret < 0)
+ return ret;
+ settings->d = val << 8;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLDLSB, &val);
+ if (ret < 0)
+ return ret;
+ settings->d |= val;
+
+ return 0;
+}
+
+static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll,
+ struct clk_aic32x4_pll_muldiv *settings)
+{
+ int ret;
+ /* Change to use regmap_bulk_write for some if not all? */
+
+ ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLL_R_MASK, settings->r);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLL_P_MASK,
+ settings->p << AIC32X4_PLL_P_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(pll->regmap, AIC32X4_PLLJ, settings->j);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(pll->regmap, AIC32X4_PLLDMSB, (settings->d >> 8));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(pll->regmap, AIC32X4_PLLDLSB, (settings->d & 0xff));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static unsigned long clk_aic32x4_pll_calc_rate(
+ struct clk_aic32x4_pll_muldiv *settings,
+ unsigned long parent_rate)
+{
+ u64 rate;
+ /*
+ * We scale j by 10000 to account for the decimal part of P and divide
+ * it back out later.
+ */
+ rate = (u64) parent_rate * settings->r *
+ ((settings->j * 10000) + settings->d);
+
+ return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000);
+}
+
+static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings,
+ unsigned long rate, unsigned long parent_rate)
+{
+ u64 multiplier;
+
+ settings->p = parent_rate / AIC32X4_MAX_PLL_CLKIN + 1;
+ if (settings->p > 8)
+ return -1;
+
+ /*
+ * We scale this figure by 10000 so that we can get the decimal part
+ * of the multiplier. This is because we can't do floating point
+ * math in the kernel.
+ */
+ multiplier = (u64) rate * settings->p * 10000;
+ do_div(multiplier, parent_rate);
+
+ /*
+ * J can't be over 64, so R can scale this.
+ * R can't be greater than 4.
+ */
+ settings->r = ((u32) multiplier / 640000) + 1;
+ if (settings->r > 4)
+ return -1;
+ do_div(multiplier, settings->r);
+
+ /*
+ * J can't be < 1.
+ */
+ if (multiplier < 10000)
+ return -1;
+
+ /* Figure out the integer part, J, and the fractional part, D. */
+ settings->j = (u32) multiplier / 10000;
+ settings->d = (u32) multiplier % 10000;
+
+ return 0;
+}
+
+static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+ struct clk_aic32x4_pll_muldiv settings;
+ int ret;
+
+ ret = clk_aic32x4_pll_get_muldiv(pll, &settings);
+ if (ret < 0)
+ return 0;
+
+ return clk_aic32x4_pll_calc_rate(&settings, parent_rate);
+}
+
+static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_aic32x4_pll_muldiv settings;
+ int ret;
+
+ ret = clk_aic32x4_pll_calc_muldiv(&settings, req->rate, req->best_parent_rate);
+ if (ret < 0)
+ return -EINVAL;
+
+ req->rate = clk_aic32x4_pll_calc_rate(&settings, req->best_parent_rate);
+
+ return 0;
+}
+
+static int clk_aic32x4_pll_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+ struct clk_aic32x4_pll_muldiv settings;
+ int ret;
+
+ ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, parent_rate);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = clk_aic32x4_pll_set_muldiv(pll, &settings);
+ if (ret)
+ return ret;
+
+ /* 10ms is the delay to wait before the clocks are stable */
+ msleep(10);
+
+ return 0;
+}
+
+static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(pll->regmap,
+ AIC32X4_CLKMUX,
+ AIC32X4_PLL_CLKIN_MASK,
+ index << AIC32X4_PLL_CLKIN_SHIFT);
+}
+
+static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+ unsigned int val;
+
+ regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
+
+ return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT;
+}
+
+
+static const struct clk_ops aic32x4_pll_ops = {
+ .prepare = clk_aic32x4_pll_prepare,
+ .unprepare = clk_aic32x4_pll_unprepare,
+ .is_prepared = clk_aic32x4_pll_is_prepared,
+ .recalc_rate = clk_aic32x4_pll_recalc_rate,
+ .determine_rate = clk_aic32x4_pll_determine_rate,
+ .set_rate = clk_aic32x4_pll_set_rate,
+ .set_parent = clk_aic32x4_pll_set_parent,
+ .get_parent = clk_aic32x4_pll_get_parent,
+};
+
+static int clk_aic32x4_codec_clkin_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(mux->regmap,
+ AIC32X4_CLKMUX,
+ AIC32X4_CODEC_CLKIN_MASK, index << AIC32X4_CODEC_CLKIN_SHIFT);
+}
+
+static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+ unsigned int val;
+
+ regmap_read(mux->regmap, AIC32X4_CLKMUX, &val);
+
+ return (val & AIC32X4_CODEC_CLKIN_MASK) >> AIC32X4_CODEC_CLKIN_SHIFT;
+}
+
+static const struct clk_ops aic32x4_codec_clkin_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .set_parent = clk_aic32x4_codec_clkin_set_parent,
+ .get_parent = clk_aic32x4_codec_clkin_get_parent,
+};
+
+static int clk_aic32x4_div_prepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(div->regmap, div->reg,
+ AIC32X4_DIVEN, AIC32X4_DIVEN);
+}
+
+static void clk_aic32x4_div_unprepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+
+ regmap_update_bits(div->regmap, div->reg,
+ AIC32X4_DIVEN, 0);
+}
+
+static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+ u8 divisor;
+
+ divisor = DIV_ROUND_UP(parent_rate, rate);
+ if (divisor > AIC32X4_DIV_MAX)
+ return -EINVAL;
+
+ return regmap_update_bits(div->regmap, div->reg,
+ AIC32X4_DIV_MASK, divisor);
+}
+
+static int clk_aic32x4_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned long divisor;
+
+ divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate);
+ if (divisor > AIC32X4_DIV_MAX)
+ return -EINVAL;
+
+ req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor);
+ return 0;
+}
+
+static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+ unsigned int val;
+ int err;
+
+ err = regmap_read(div->regmap, div->reg, &val);
+ if (err)
+ return 0;
+
+ val &= AIC32X4_DIV_MASK;
+ if (!val)
+ val = AIC32X4_DIV_MAX;
+
+ return DIV_ROUND_UP(parent_rate, val);
+}
+
+static const struct clk_ops aic32x4_div_ops = {
+ .prepare = clk_aic32x4_div_prepare,
+ .unprepare = clk_aic32x4_div_unprepare,
+ .set_rate = clk_aic32x4_div_set_rate,
+ .determine_rate = clk_aic32x4_div_determine_rate,
+ .recalc_rate = clk_aic32x4_div_recalc_rate,
+};
+
+static int clk_aic32x4_bdiv_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(mux->regmap, AIC32X4_IFACE3,
+ AIC32X4_BDIVCLK_MASK, index);
+}
+
+static u8 clk_aic32x4_bdiv_get_parent(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+ unsigned int val;
+
+ regmap_read(mux->regmap, AIC32X4_IFACE3, &val);
+
+ return val & AIC32X4_BDIVCLK_MASK;
+}
+
+static const struct clk_ops aic32x4_bdiv_ops = {
+ .prepare = clk_aic32x4_div_prepare,
+ .unprepare = clk_aic32x4_div_unprepare,
+ .set_parent = clk_aic32x4_bdiv_set_parent,
+ .get_parent = clk_aic32x4_bdiv_get_parent,
+ .set_rate = clk_aic32x4_div_set_rate,
+ .determine_rate = clk_aic32x4_div_determine_rate,
+ .recalc_rate = clk_aic32x4_div_recalc_rate,
+};
+
+static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = {
+ {
+ .name = "pll",
+ .parent_names =
+ (const char* []) { "mclk", "bclk", "gpio", "din" },
+ .num_parents = 4,
+ .ops = &aic32x4_pll_ops,
+ .reg = 0,
+ },
+ {
+ .name = "codec_clkin",
+ .parent_names =
+ (const char *[]) { "mclk", "bclk", "gpio", "pll" },
+ .num_parents = 4,
+ .ops = &aic32x4_codec_clkin_ops,
+ .reg = 0,
+ },
+ {
+ .name = "ndac",
+ .parent_names = (const char * []) { "codec_clkin" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_NDAC,
+ },
+ {
+ .name = "mdac",
+ .parent_names = (const char * []) { "ndac" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_MDAC,
+ },
+ {
+ .name = "nadc",
+ .parent_names = (const char * []) { "codec_clkin" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_NADC,
+ },
+ {
+ .name = "madc",
+ .parent_names = (const char * []) { "nadc" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_MADC,
+ },
+ {
+ .name = "bdiv",
+ .parent_names =
+ (const char *[]) { "ndac", "mdac", "nadc", "madc" },
+ .num_parents = 4,
+ .ops = &aic32x4_bdiv_ops,
+ .reg = AIC32X4_BCLKN,
+ },
+};
+
+static struct clk *aic32x4_register_clk(struct device *dev,
+ struct aic32x4_clkdesc *desc)
+{
+ struct clk_init_data init;
+ struct clk_aic32x4 *priv;
+ const char *devname = dev_name(dev);
+
+ init.ops = desc->ops;
+ init.name = desc->name;
+ init.parent_names = desc->parent_names;
+ init.num_parents = desc->num_parents;
+ init.flags = 0;
+
+ priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL);
+ if (priv == NULL)
+ return (struct clk *) -ENOMEM;
+
+ priv->dev = dev;
+ priv->hw.init = &init;
+ priv->regmap = dev_get_regmap(dev, NULL);
+ priv->reg = desc->reg;
+
+ clk_hw_register_clkdev(&priv->hw, desc->name, devname);
+ return devm_clk_register(dev, &priv->hw);
+}
+
+int aic32x4_register_clocks(struct device *dev, const char *mclk_name)
+{
+ int i;
+
+ /*
+ * These lines are here to preserve the current functionality of
+ * the driver with regard to the DT. These should eventually be set
+ * by DT nodes so that the connections can be set up in configuration
+ * rather than code.
+ */
+ aic32x4_clkdesc_array[0].parent_names =
+ (const char* []) { mclk_name, "bclk", "gpio", "din" };
+ aic32x4_clkdesc_array[1].parent_names =
+ (const char *[]) { mclk_name, "bclk", "gpio", "pll" };
+
+ for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i)
+ aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aic32x4_register_clocks);
diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c
new file mode 100644
index 000000000000..b27b5ae1e4b2
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2011-2019 NW Digital Radio
+ *
+ * Author: Annaliese McDermond <nh6z@nh6z.net>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic32x4.h"
+
+static int aic32x4_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *regmap;
+ struct regmap_config config;
+ enum aic32x4_type type;
+
+ config = aic32x4_regmap_config;
+ config.reg_bits = 8;
+ config.val_bits = 8;
+
+ regmap = devm_regmap_init_i2c(i2c, &config);
+ type = (uintptr_t)i2c_get_match_data(i2c);
+
+ return aic32x4_probe(&i2c->dev, regmap, type);
+}
+
+static void aic32x4_i2c_remove(struct i2c_client *i2c)
+{
+ aic32x4_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id aic32x4_i2c_id[] = {
+ { "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 },
+ { "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 },
+ { "tas2505", (kernel_ulong_t)AIC32X4_TYPE_TAS2505 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
+
+static const struct of_device_id aic32x4_of_id[] = {
+ { .compatible = "ti,tlv320aic32x4", .data = (void *)AIC32X4_TYPE_AIC32X4 },
+ { .compatible = "ti,tlv320aic32x6", .data = (void *)AIC32X4_TYPE_AIC32X6 },
+ { .compatible = "ti,tas2505", .data = (void *)AIC32X4_TYPE_TAS2505 },
+ { /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
+static struct i2c_driver aic32x4_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic32x4",
+ .of_match_table = aic32x4_of_id,
+ },
+ .probe = aic32x4_i2c_probe,
+ .remove = aic32x4_i2c_remove,
+ .id_table = aic32x4_i2c_id,
+};
+
+module_i2c_driver(aic32x4_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver I2C");
+MODULE_AUTHOR("Annaliese McDermond <nh6z@nh6z.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c
new file mode 100644
index 000000000000..92246243ff94
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic32x4-spi.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2011-2019 NW Digital Radio
+ *
+ * Author: Annaliese McDermond <nh6z@nh6z.net>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic32x4.h"
+
+static int aic32x4_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ struct regmap_config config;
+ enum aic32x4_type type;
+
+ config = aic32x4_regmap_config;
+ config.reg_bits = 7;
+ config.pad_bits = 1;
+ config.val_bits = 8;
+ config.read_flag_mask = 0x01;
+
+ regmap = devm_regmap_init_spi(spi, &config);
+ type = (uintptr_t)spi_get_device_match_data(spi);
+
+ return aic32x4_probe(&spi->dev, regmap, type);
+}
+
+static void aic32x4_spi_remove(struct spi_device *spi)
+{
+ aic32x4_remove(&spi->dev);
+}
+
+static const struct spi_device_id aic32x4_spi_id[] = {
+ { "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 },
+ { "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, aic32x4_spi_id);
+
+static const struct of_device_id aic32x4_of_id[] = {
+ { .compatible = "ti,tlv320aic32x4", .data = (void *)AIC32X4_TYPE_AIC32X4 },
+ { .compatible = "ti,tlv320aic32x6", .data = (void *)AIC32X4_TYPE_AIC32X6 },
+ { /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
+static struct spi_driver aic32x4_spi_driver = {
+ .driver = {
+ .name = "tlv320aic32x4",
+ .of_match_table = aic32x4_of_id,
+ },
+ .probe = aic32x4_spi_probe,
+ .remove = aic32x4_spi_remove,
+ .id_table = aic32x4_spi_id,
+};
+
+module_spi_driver(aic32x4_spi_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver SPI");
+MODULE_AUTHOR("Annaliese McDermond <nh6z@nh6z.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index f2d3191961e1..d85094557215 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* linux/sound/soc/codecs/tlv320aic32x4.c
*
@@ -6,75 +7,240 @@
* 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/cdev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/of_clk.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 <linux/slab.h>
-#include <sound/tlv320aic32x4.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
+#include <sound/tlv320aic32x4.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 gpio_desc *rstn_gpio;
+ const char *mclk_name;
struct regulator *supply_ldo;
struct regulator *supply_iov;
struct regulator *supply_dv;
struct regulator *supply_av;
+
+ struct aic32x4_setup_data *setup;
+ struct device *dev;
+ enum aic32x4_type type;
+
+ unsigned int fmt;
+};
+
+static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u32 adc_reg;
+
+ /*
+ * Workaround: the datasheet does not mention a required programming
+ * sequence but experiments show the ADC needs to be reset after each
+ * capture to avoid audible artifacts.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ adc_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP);
+ snd_soc_component_write(component, AIC32X4_ADCSETUP, adc_reg |
+ AIC32X4_LADC_EN | AIC32X4_RADC_EN);
+ snd_soc_component_write(component, AIC32X4_ADCSETUP, adc_reg);
+ break;
+ }
+ return 0;
+};
+
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Change Mic Bias Registor */
+ snd_soc_component_update_bits(component, AIC32X4_MICBIAS,
+ AIC32x4_MICBIAS_MASK,
+ AIC32X4_MICBIAS_LDOIN |
+ AIC32X4_MICBIAS_2075V);
+ printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, AIC32X4_MICBIAS,
+ AIC32x4_MICBIAS_MASK, 0);
+ printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n",
+ __func__);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+
+ val = snd_soc_component_read(component, AIC32X4_DINCTL);
+
+ ucontrol->value.integer.value[0] = (val & 0x01);
+
+ return 0;
+};
+
+static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+ u8 gpio_check;
+
+ val = snd_soc_component_read(component, AIC32X4_DOUTCTL);
+ gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED);
+ if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) {
+ printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP2_GPIO_OUT_HIGH))
+ return 0;
+
+ if (ucontrol->value.integer.value[0])
+ val |= ucontrol->value.integer.value[0];
+ else
+ val &= ~AIC32X4_MFP2_GPIO_OUT_HIGH;
+
+ snd_soc_component_write(component, AIC32X4_DOUTCTL, val);
+
+ return 0;
+};
+
+static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+
+ val = snd_soc_component_read(component, AIC32X4_SCLKCTL);
+
+ ucontrol->value.integer.value[0] = (val & 0x01);
+
+ return 0;
+};
+
+static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+ u8 gpio_check;
+
+ val = snd_soc_component_read(component, AIC32X4_MISOCTL);
+ gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED);
+ if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) {
+ printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP5_GPIO_OUT_HIGH))
+ return 0;
+
+ if (ucontrol->value.integer.value[0])
+ val |= ucontrol->value.integer.value[0];
+ else
+ val &= ~AIC32X4_MFP5_GPIO_OUT_HIGH;
+
+ snd_soc_component_write(component, AIC32X4_MISOCTL, val);
+
+ return 0;
+};
+
+static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+
+ val = snd_soc_component_read(component, AIC32X4_GPIOCTL);
+ ucontrol->value.integer.value[0] = ((val & 0x2) >> 1);
+
+ return 0;
+};
+
+static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u8 val;
+ u8 gpio_check;
+
+ val = snd_soc_component_read(component, AIC32X4_GPIOCTL);
+ gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT);
+ if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) {
+ printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ucontrol->value.integer.value[0] == (val & 0x1))
+ return 0;
+
+ if (ucontrol->value.integer.value[0])
+ val |= ucontrol->value.integer.value[0];
+ else
+ val &= 0xfe;
+
+ snd_soc_component_write(component, AIC32X4_GPIOCTL, val);
+
+ return 0;
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp1[] = {
+ SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp2[] = {
+ SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp3[] = {
+ SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp4[] = {
+ SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp5[] = {
+ SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio,
+ aic32x4_set_mfp5_gpio),
};
/* 0dB min, 0.5dB steps */
@@ -85,10 +251,28 @@ static DECLARE_TLV_DB_SCALE(tlv_pcm, -6350, 50, 0);
static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
/* -12dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);
+/* -6dB min, 1dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_tas_driver_gain, -5850, 50, 0);
+static DECLARE_TLV_DB_SCALE(tlv_amp_vol, 0, 600, 1);
+
+static const char * const lo_cm_text[] = {
+ "Full Chip", "1.65V",
+};
+
+static SOC_ENUM_SINGLE_DECL(lo_cm_enum, AIC32X4_CMMODE, 3, lo_cm_text);
+
+static const char * const ptm_text[] = {
+ "P3", "P2", "P1",
+};
+
+static SOC_ENUM_SINGLE_DECL(l_ptm_enum, AIC32X4_LPLAYBACK, 2, ptm_text);
+static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text);
static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
+ SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum),
+ SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum),
SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0,
tlv_driver_gain),
@@ -99,6 +283,7 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
AIC32X4_HPRGAIN, 6, 0x01, 1),
SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
AIC32X4_LORGAIN, 6, 0x01, 1),
+ SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum),
SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
AIC32X4_RMICPGAVOL, 7, 0x01, 1),
@@ -134,35 +319,6 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
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),
@@ -181,16 +337,71 @@ 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 char * const resistor_text[] = {
+ "Off", "10 kOhm", "20 kOhm", "40 kOhm",
+};
+
+/* Left mixer pins */
+static SOC_ENUM_SINGLE_DECL(in1l_lpga_p_enum, AIC32X4_LMICPGAPIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2l_lpga_p_enum, AIC32X4_LMICPGAPIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3l_lpga_p_enum, AIC32X4_LMICPGAPIN, 2, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in1r_lpga_p_enum, AIC32X4_LMICPGAPIN, 0, resistor_text);
+
+static SOC_ENUM_SINGLE_DECL(cml_lpga_n_enum, AIC32X4_LMICPGANIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2r_lpga_n_enum, AIC32X4_LMICPGANIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_text);
+
+static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum),
+};
+static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum),
+};
+static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum),
+};
+static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum),
+};
+static const struct snd_kcontrol_new cml_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum),
+};
+static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum),
+};
+static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum),
};
-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),
+/* Right mixer pins */
+static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2l_rpga_p_enum, AIC32X4_RMICPGAPIN, 0, resistor_text);
+static SOC_ENUM_SINGLE_DECL(cmr_rpga_n_enum, AIC32X4_RMICPGANIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in1l_rpga_n_enum, AIC32X4_RMICPGANIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_text);
+
+static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum),
+};
+static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum),
+};
+static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum),
+};
+static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum),
+};
+static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum),
+};
+static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum),
+};
+static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum),
};
static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
@@ -214,15 +425,43 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
&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_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in1r_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in2r_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in3r_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in2l_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ cmr_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in1l_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in3l_to_rmixer_controls),
+
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
+ SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in1l_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in2l_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in3l_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in1r_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ cml_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in2r_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in3r_to_lmixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
@@ -234,6 +473,8 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN2_R"),
SND_SOC_DAPM_INPUT("IN3_L"),
SND_SOC_DAPM_INPUT("IN3_R"),
+ SND_SOC_DAPM_INPUT("CM_L"),
+ SND_SOC_DAPM_INPUT("CM_R"),
};
static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
@@ -261,95 +502,129 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
{"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"},
+ {"Right ADC", NULL, "IN1_R to Right Mixer Positive Resistor"},
+ {"IN1_R to Right Mixer Positive Resistor", "10 kOhm", "IN1_R"},
+ {"IN1_R to Right Mixer Positive Resistor", "20 kOhm", "IN1_R"},
+ {"IN1_R to Right Mixer Positive Resistor", "40 kOhm", "IN1_R"},
+
+ {"Right ADC", NULL, "IN2_R to Right Mixer Positive Resistor"},
+ {"IN2_R to Right Mixer Positive Resistor", "10 kOhm", "IN2_R"},
+ {"IN2_R to Right Mixer Positive Resistor", "20 kOhm", "IN2_R"},
+ {"IN2_R to Right Mixer Positive Resistor", "40 kOhm", "IN2_R"},
+
+ {"Right ADC", NULL, "IN3_R to Right Mixer Positive Resistor"},
+ {"IN3_R to Right Mixer Positive Resistor", "10 kOhm", "IN3_R"},
+ {"IN3_R to Right Mixer Positive Resistor", "20 kOhm", "IN3_R"},
+ {"IN3_R to Right Mixer Positive Resistor", "40 kOhm", "IN3_R"},
+
+ {"Right ADC", NULL, "IN2_L to Right Mixer Positive Resistor"},
+ {"IN2_L to Right Mixer Positive Resistor", "10 kOhm", "IN2_L"},
+ {"IN2_L to Right Mixer Positive Resistor", "20 kOhm", "IN2_L"},
+ {"IN2_L to Right Mixer Positive Resistor", "40 kOhm", "IN2_L"},
+
+ {"Right ADC", NULL, "CM_R to Right Mixer Negative Resistor"},
+ {"CM_R to Right Mixer Negative Resistor", "10 kOhm", "CM_R"},
+ {"CM_R to Right Mixer Negative Resistor", "20 kOhm", "CM_R"},
+ {"CM_R to Right Mixer Negative Resistor", "40 kOhm", "CM_R"},
+
+ {"Right ADC", NULL, "IN1_L to Right Mixer Negative Resistor"},
+ {"IN1_L to Right Mixer Negative Resistor", "10 kOhm", "IN1_L"},
+ {"IN1_L to Right Mixer Negative Resistor", "20 kOhm", "IN1_L"},
+ {"IN1_L to Right Mixer Negative Resistor", "40 kOhm", "IN1_L"},
+
+ {"Right ADC", NULL, "IN3_L to Right Mixer Negative Resistor"},
+ {"IN3_L to Right Mixer Negative Resistor", "10 kOhm", "IN3_L"},
+ {"IN3_L to Right Mixer Negative Resistor", "20 kOhm", "IN3_L"},
+ {"IN3_L to Right Mixer Negative Resistor", "40 kOhm", "IN3_L"},
+
+ /* Left Input */
+ {"Left ADC", NULL, "IN1_L to Left Mixer Positive Resistor"},
+ {"IN1_L to Left Mixer Positive Resistor", "10 kOhm", "IN1_L"},
+ {"IN1_L to Left Mixer Positive Resistor", "20 kOhm", "IN1_L"},
+ {"IN1_L to Left Mixer Positive Resistor", "40 kOhm", "IN1_L"},
+
+ {"Left ADC", NULL, "IN2_L to Left Mixer Positive Resistor"},
+ {"IN2_L to Left Mixer Positive Resistor", "10 kOhm", "IN2_L"},
+ {"IN2_L to Left Mixer Positive Resistor", "20 kOhm", "IN2_L"},
+ {"IN2_L to Left Mixer Positive Resistor", "40 kOhm", "IN2_L"},
+
+ {"Left ADC", NULL, "IN3_L to Left Mixer Positive Resistor"},
+ {"IN3_L to Left Mixer Positive Resistor", "10 kOhm", "IN3_L"},
+ {"IN3_L to Left Mixer Positive Resistor", "20 kOhm", "IN3_L"},
+ {"IN3_L to Left Mixer Positive Resistor", "40 kOhm", "IN3_L"},
+
+ {"Left ADC", NULL, "IN1_R to Left Mixer Positive Resistor"},
+ {"IN1_R to Left Mixer Positive Resistor", "10 kOhm", "IN1_R"},
+ {"IN1_R to Left Mixer Positive Resistor", "20 kOhm", "IN1_R"},
+ {"IN1_R to Left Mixer Positive Resistor", "40 kOhm", "IN1_R"},
+
+ {"Left ADC", NULL, "CM_L to Left Mixer Negative Resistor"},
+ {"CM_L to Left Mixer Negative Resistor", "10 kOhm", "CM_L"},
+ {"CM_L to Left Mixer Negative Resistor", "20 kOhm", "CM_L"},
+ {"CM_L to Left Mixer Negative Resistor", "40 kOhm", "CM_L"},
+
+ {"Left ADC", NULL, "IN2_R to Left Mixer Negative Resistor"},
+ {"IN2_R to Left Mixer Negative Resistor", "10 kOhm", "IN2_R"},
+ {"IN2_R to Left Mixer Negative Resistor", "20 kOhm", "IN2_R"},
+ {"IN2_R to Left Mixer Negative Resistor", "40 kOhm", "IN2_R"},
+
+ {"Left ADC", NULL, "IN3_R to Left Mixer Negative Resistor"},
+ {"IN3_R to Left Mixer Negative Resistor", "10 kOhm", "IN3_R"},
+ {"IN3_R to Left Mixer Negative Resistor", "20 kOhm", "IN3_R"},
+ {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"},
};
static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
{
.selector_reg = 0,
- .selector_mask = 0xff,
+ .selector_mask = 0xff,
.window_start = 0,
.window_len = 128,
.range_min = 0,
- .range_max = AIC32X4_RMICPGAVOL,
+ .range_max = AIC32X4_REFPOWERUP,
},
};
-static const struct regmap_config aic32x4_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = AIC32X4_RMICPGAVOL,
+const struct regmap_config aic32x4_regmap_config = {
+ .max_register = AIC32X4_REFPOWERUP,
.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;
-}
+EXPORT_SYMBOL(aic32x4_regmap_config);
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_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;
+ struct snd_soc_component *component = codec_dai->component;
+ struct clk *mclk;
+ struct clk *pll;
+
+ pll = devm_clk_get(component->dev, "pll");
+ if (IS_ERR(pll))
+ return PTR_ERR(pll);
+
+ mclk = clk_get_parent(pll);
+
+ return clk_set_rate(mclk, freq);
}
static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_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:
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ u8 iface_reg_1 = 0;
+ u8 iface_reg_2 = 0;
+ u8 iface_reg_3 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
- printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n");
+ printk(KERN_ERR "aic32x4: invalid clock provider\n");
return -EINVAL;
}
@@ -357,209 +632,286 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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_1 |= (AIC32X4_DSP_MODE <<
+ AIC32X4_IFACE1_DATATYPE_SHIFT);
+ iface_reg_3 |= AIC32X4_BCLKINV_MASK; /* invert bit clock */
iface_reg_2 = 0x01; /* add offset 1 */
break;
case SND_SOC_DAIFMT_DSP_B:
- iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
- iface_reg_3 |= (1 << 3); /* invert bit clock */
+ iface_reg_1 |= (AIC32X4_DSP_MODE <<
+ AIC32X4_IFACE1_DATATYPE_SHIFT);
+ iface_reg_3 |= AIC32X4_BCLKINV_MASK; /* invert bit clock */
break;
case SND_SOC_DAIFMT_RIGHT_J:
- iface_reg_1 |=
- (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+ iface_reg_1 |= (AIC32X4_RIGHT_JUSTIFIED_MODE <<
+ AIC32X4_IFACE1_DATATYPE_SHIFT);
break;
case SND_SOC_DAIFMT_LEFT_J:
- iface_reg_1 |=
- (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+ iface_reg_1 |= (AIC32X4_LEFT_JUSTIFIED_MODE <<
+ AIC32X4_IFACE1_DATATYPE_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);
+ aic32x4->fmt = fmt;
+
+ snd_soc_component_update_bits(component, AIC32X4_IFACE1,
+ AIC32X4_IFACE1_DATATYPE_MASK |
+ AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
+ snd_soc_component_update_bits(component, AIC32X4_IFACE2,
+ AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
+ snd_soc_component_update_bits(component, AIC32X4_IFACE3,
+ AIC32X4_BCLKINV_MASK, iface_reg_3);
+
return 0;
}
-static int aic32x4_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr)
{
- 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;
+ return snd_soc_component_write(component, AIC32X4_AOSR, aosr);
+}
+
+static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr)
+{
+ snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8);
+ snd_soc_component_write(component, AIC32X4_DOSRLSB,
+ (dosr & 0xff));
+
+ return 0;
+}
+
+static int aic32x4_set_processing_blocks(struct snd_soc_component *component,
+ u8 r_block, u8 p_block)
+{
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+
+ if (aic32x4->type == AIC32X4_TYPE_TAS2505) {
+ if (r_block || p_block > 3)
+ return -EINVAL;
+
+ snd_soc_component_write(component, AIC32X4_DACSPB, p_block);
+ } else { /* AIC32x4 */
+ if (r_block > 18 || p_block > 25)
+ return -EINVAL;
+
+ snd_soc_component_write(component, AIC32X4_ADCSPB, r_block);
+ snd_soc_component_write(component, AIC32X4_DACSPB, p_block);
+ }
+
+ return 0;
+}
+
+static int aic32x4_setup_clocks(struct snd_soc_component *component,
+ unsigned int sample_rate, unsigned int channels,
+ unsigned int bit_depth)
+{
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ u8 aosr;
+ u16 dosr;
+ u8 adc_resource_class, dac_resource_class;
+ u8 madc, nadc, mdac, ndac, max_nadc, min_mdac, max_ndac;
+ u8 dosr_increment;
+ u16 max_dosr, min_dosr;
+ unsigned long adc_clock_rate, dac_clock_rate;
+ int ret;
+
+ static struct clk_bulk_data clocks[] = {
+ { .id = "pll" },
+ { .id = "nadc" },
+ { .id = "madc" },
+ { .id = "ndac" },
+ { .id = "mdac" },
+ { .id = "bdiv" },
+ };
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
+
+ if (sample_rate <= 48000) {
+ aosr = 128;
+ adc_resource_class = 6;
+ dac_resource_class = 8;
+ dosr_increment = 8;
+ if (aic32x4->type == AIC32X4_TYPE_TAS2505)
+ aic32x4_set_processing_blocks(component, 0, 1);
+ else
+ aic32x4_set_processing_blocks(component, 1, 1);
+ } else if (sample_rate <= 96000) {
+ aosr = 64;
+ adc_resource_class = 6;
+ dac_resource_class = 8;
+ dosr_increment = 4;
+ if (aic32x4->type == AIC32X4_TYPE_TAS2505)
+ aic32x4_set_processing_blocks(component, 0, 1);
+ else
+ aic32x4_set_processing_blocks(component, 1, 9);
+ } else if (sample_rate == 192000) {
+ aosr = 32;
+ adc_resource_class = 3;
+ dac_resource_class = 4;
+ dosr_increment = 2;
+ if (aic32x4->type == AIC32X4_TYPE_TAS2505)
+ aic32x4_set_processing_blocks(component, 0, 1);
+ else
+ aic32x4_set_processing_blocks(component, 13, 19);
+ } else {
+ dev_err(component->dev, "Sampling rate not supported\n");
+ return -EINVAL;
}
- /* 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)) {
+ /* PCM over I2S is always 2-channel */
+ if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ channels = 2;
+
+ madc = DIV_ROUND_UP((32 * adc_resource_class), aosr);
+ max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) *
+ dosr_increment;
+ min_dosr = (AIC32X4_MIN_DOSR_FREQ / sample_rate / dosr_increment) *
+ dosr_increment;
+ max_nadc = AIC32X4_MAX_CODEC_CLKIN_FREQ / (madc * aosr * sample_rate);
+
+ for (nadc = max_nadc; nadc > 0; --nadc) {
+ adc_clock_rate = nadc * madc * aosr * sample_rate;
+ for (dosr = max_dosr; dosr >= min_dosr;
+ dosr -= dosr_increment) {
+ min_mdac = DIV_ROUND_UP((32 * dac_resource_class), dosr);
+ max_ndac = AIC32X4_MAX_CODEC_CLKIN_FREQ /
+ (min_mdac * dosr * sample_rate);
+ for (mdac = min_mdac; mdac <= 128; ++mdac) {
+ for (ndac = max_ndac; ndac > 0; --ndac) {
+ dac_clock_rate = ndac * mdac * dosr *
+ sample_rate;
+ if (dac_clock_rate == adc_clock_rate) {
+ if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0)
+ continue;
+
+ clk_set_rate(clocks[0].clk,
+ dac_clock_rate);
+
+ clk_set_rate(clocks[1].clk,
+ sample_rate * aosr *
+ madc);
+ clk_set_rate(clocks[2].clk,
+ sample_rate * aosr);
+ aic32x4_set_aosr(component,
+ aosr);
+
+ clk_set_rate(clocks[3].clk,
+ sample_rate * dosr *
+ mdac);
+ clk_set_rate(clocks[4].clk,
+ sample_rate * dosr);
+ aic32x4_set_dosr(component,
+ dosr);
+
+ clk_set_rate(clocks[5].clk,
+ sample_rate * channels *
+ bit_depth);
+
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ dev_err(component->dev,
+ "Could not set clocks to support sample rate.\n");
+ return -EINVAL;
+}
+
+static int aic32x4_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ u8 iface1_reg = 0;
+ u8 dacsetup_reg = 0;
+
+ aic32x4_setup_clocks(component, params_rate(params),
+ params_channels(params),
+ params_physical_width(params));
+
+ switch (params_physical_width(params)) {
case 16:
+ iface1_reg |= (AIC32X4_WORD_LEN_16BITS <<
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 20:
- data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT);
+ iface1_reg |= (AIC32X4_WORD_LEN_20BITS <<
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 24:
- data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT);
+ iface1_reg |= (AIC32X4_WORD_LEN_24BITS <<
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 32:
- data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT);
+ iface1_reg |= (AIC32X4_WORD_LEN_32BITS <<
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
}
- snd_soc_write(codec, AIC32X4_IFACE1, data);
+ snd_soc_component_update_bits(component, AIC32X4_IFACE1,
+ AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
if (params_channels(params) == 1) {
- data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
+ dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
} else {
if (aic32x4->swapdacs)
- data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN;
+ dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN;
else
- data = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
+ dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
}
- snd_soc_update_bits(codec, AIC32X4_DACSETUP, AIC32X4_DAC_CHAN_MASK,
- data);
+ snd_soc_component_update_bits(component, AIC32X4_DACSETUP,
+ AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
return 0;
}
-static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
+static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 dac_reg;
+ struct snd_soc_component *component = dai->component;
+
+ snd_soc_component_update_bits(component, AIC32X4_DACMUTE,
+ AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
- 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,
+static int aic32x4_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
+ static struct clk_bulk_data clocks[] = {
+ { .id = "madc" },
+ { .id = "mdac" },
+ { .id = "bdiv" },
+ };
+
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
- /* Switch on master clock */
- ret = clk_prepare_enable(aic32x4->mclk);
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks);
if (ret) {
- dev_err(codec->dev, "Failed to enable master clock\n");
+ dev_err(component->dev, "Failed to enable clocks\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);
+ /* Initial cold start */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ break;
- /* 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);
+ clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks);
break;
case SND_SOC_BIAS_OFF:
break;
@@ -567,78 +919,137 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
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)
+#define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000
+#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops aic32x4_ops = {
.hw_params = aic32x4_hw_params,
- .digital_mute = aic32x4_mute,
+ .mute_stream = aic32x4_mute,
.set_fmt = aic32x4_set_dai_fmt,
.set_sysclk = aic32x4_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver aic32x4_dai = {
.name = "tlv320aic32x4-hifi",
.playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AIC32X4_RATES,
- .formats = AIC32X4_FORMATS,},
+ .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,},
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = AIC32X4_RATES,
+ .formats = AIC32X4_FORMATS,},
.ops = &aic32x4_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int aic32x4_probe(struct snd_soc_codec *codec)
+static void aic32x4_setup_gpios(struct snd_soc_component *component)
{
- struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
- u32 tmp_reg;
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+
+ /* setup GPIO functions */
+ /* MFP1 */
+ if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) {
+ snd_soc_component_write(component, AIC32X4_DINCTL,
+ aic32x4->setup->gpio_func[0]);
+ snd_soc_add_component_controls(component, aic32x4_mfp1,
+ ARRAY_SIZE(aic32x4_mfp1));
+ }
- if (gpio_is_valid(aic32x4->rstn_gpio)) {
- ndelay(10);
- gpio_set_value(aic32x4->rstn_gpio, 1);
+ /* MFP2 */
+ if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) {
+ snd_soc_component_write(component, AIC32X4_DOUTCTL,
+ aic32x4->setup->gpio_func[1]);
+ snd_soc_add_component_controls(component, aic32x4_mfp2,
+ ARRAY_SIZE(aic32x4_mfp2));
+ }
+
+ /* MFP3 */
+ if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) {
+ snd_soc_component_write(component, AIC32X4_SCLKCTL,
+ aic32x4->setup->gpio_func[2]);
+ snd_soc_add_component_controls(component, aic32x4_mfp3,
+ ARRAY_SIZE(aic32x4_mfp3));
+ }
+
+ /* MFP4 */
+ if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) {
+ snd_soc_component_write(component, AIC32X4_MISOCTL,
+ aic32x4->setup->gpio_func[3]);
+ snd_soc_add_component_controls(component, aic32x4_mfp4,
+ ARRAY_SIZE(aic32x4_mfp4));
+ }
+
+ /* MFP5 */
+ if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) {
+ snd_soc_component_write(component, AIC32X4_GPIOCTL,
+ aic32x4->setup->gpio_func[4]);
+ snd_soc_add_component_controls(component, aic32x4_mfp5,
+ ARRAY_SIZE(aic32x4_mfp5));
}
+}
+
+static int aic32x4_component_probe(struct snd_soc_component *component)
+{
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ u32 tmp_reg;
+ int ret;
+
+ static struct clk_bulk_data clocks[] = {
+ { .id = "codec_clkin" },
+ { .id = "pll" },
+ { .id = "bdiv" },
+ { .id = "mdac" },
+ };
+
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
+
+ if (aic32x4->setup)
+ aic32x4_setup_gpios(component);
- snd_soc_write(codec, AIC32X4_RESET, 0x01);
+ clk_set_parent(clocks[0].clk, clocks[1].clk);
+ clk_set_parent(clocks[2].clk, clocks[3].clk);
/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
- snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
- AIC32X4_MICBIAS_2075V);
+ snd_soc_component_write(component, AIC32X4_MICBIAS,
+ AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V);
}
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
- snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, AIC32X4_LDOCTL, tmp_reg);
- tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE);
+ tmp_reg = snd_soc_component_read(component, AIC32X4_CMMODE);
if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36)
tmp_reg |= AIC32X4_LDOIN_18_36;
if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED)
tmp_reg |= AIC32X4_LDOIN2HP;
- snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg);
+ snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg);
/* Mic PGA routing */
if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K)
- snd_soc_write(codec, AIC32X4_LMICPGANIN,
+ snd_soc_component_write(component, AIC32X4_LMICPGANIN,
AIC32X4_LMICPGANIN_IN2R_10K);
else
- snd_soc_write(codec, AIC32X4_LMICPGANIN,
+ snd_soc_component_write(component, AIC32X4_LMICPGANIN,
AIC32X4_LMICPGANIN_CM1L_10K);
if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K)
- snd_soc_write(codec, AIC32X4_RMICPGANIN,
+ snd_soc_component_write(component, AIC32X4_RMICPGANIN,
AIC32X4_RMICPGANIN_IN1L_10K);
else
- snd_soc_write(codec, AIC32X4_RMICPGANIN,
+ snd_soc_component_write(component, AIC32X4_RMICPGANIN,
AIC32X4_RMICPGANIN_CM1R_10K);
/*
@@ -646,34 +1057,197 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
* 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 |
+ tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP);
+ snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg |
AIC32X4_LADC_EN | AIC32X4_RADC_EN);
- snd_soc_write(codec, AIC32X4_ADCSETUP, tmp_reg);
+ snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg);
+
+ /*
+ * Enable the fast charging feature and ensure the needed 40ms ellapsed
+ * before using the analog circuits.
+ */
+ snd_soc_component_write(component, AIC32X4_REFPOWERUP,
+ AIC32X4_REFPOWERUP_40MS);
+ msleep(40);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
- .probe = aic32x4_probe,
- .set_bias_level = aic32x4_set_bias_level,
- .suspend_bias_off = true,
+static int aic32x4_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ /* return dai id 0, whatever the endpoint index */
+ return 0;
+}
- .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 const struct snd_soc_component_driver soc_component_dev_aic32x4 = {
+ .probe = aic32x4_component_probe,
+ .set_bias_level = aic32x4_set_bias_level,
+ .controls = aic32x4_snd_controls,
+ .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
+ .dapm_widgets = aic32x4_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
+ .dapm_routes = aic32x4_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
+ .of_xlate_dai_id = aic32x4_of_xlate_dai_id,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("PCM Playback Volume",
+ AIC32X4_LDACVOL, -0x7f, 0x30, tlv_pcm),
+ SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum),
+
+ SOC_SINGLE_TLV("HP Driver Gain Volume",
+ AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain),
+ SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1),
+
+ SOC_SINGLE_TLV("Speaker Driver Playback Volume",
+ TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain),
+ SOC_SINGLE_TLV("Speaker Amplifier Playback Volume",
+ TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol),
+
+ SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0),
+};
+
+static const struct snd_kcontrol_new hp_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget aic32x4_tas2505_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", AIC32X4_DACSETUP, 7, 0),
+ SND_SOC_DAPM_MIXER("HP Output Mixer", SND_SOC_NOPM, 0, 0,
+ &hp_output_mixer_controls[0],
+ ARRAY_SIZE(hp_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("HP Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Speaker Driver", TAS2505_SPK, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("Speaker"),
+};
+
+static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = {
+ /* Left Output */
+ {"HP Output Mixer", "DAC Switch", "DAC"},
+
+ {"HP Power", NULL, "HP Output Mixer"},
+ {"HP", NULL, "HP Power"},
+
+ {"Speaker Driver", NULL, "DAC"},
+ {"Speaker", NULL, "Speaker Driver"},
+};
+
+static struct snd_soc_dai_driver aic32x4_tas2505_dai = {
+ .name = "tas2505-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = AIC32X4_FORMATS,},
+ .ops = &aic32x4_ops,
+ .symmetric_rate = 1,
+};
+
+static int aic32x4_tas2505_component_probe(struct snd_soc_component *component)
+{
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ u32 tmp_reg;
+ int ret;
+
+ static struct clk_bulk_data clocks[] = {
+ { .id = "codec_clkin" },
+ { .id = "pll" },
+ { .id = "bdiv" },
+ { .id = "mdac" },
+ };
+
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
+
+ if (aic32x4->setup)
+ aic32x4_setup_gpios(component);
+
+ clk_set_parent(clocks[0].clk, clocks[1].clk);
+ clk_set_parent(clocks[2].clk, clocks[3].clk);
+
+ /* Power platform configuration */
+ if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
+ snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
+
+ tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ?
+ AIC32X4_LDOCTLEN : 0;
+ snd_soc_component_write(component, AIC32X4_LDOCTL, tmp_reg);
+
+ tmp_reg = snd_soc_component_read(component, AIC32X4_CMMODE);
+ if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36)
+ tmp_reg |= AIC32X4_LDOIN_18_36;
+ if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED)
+ tmp_reg |= AIC32X4_LDOIN2HP;
+ snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg);
+
+ /*
+ * Enable the fast charging feature and ensure the needed 40ms ellapsed
+ * before using the analog circuits.
+ */
+ snd_soc_component_write(component, TAS2505_REFPOWERUP,
+ AIC32X4_REFPOWERUP_40MS);
+ msleep(40);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = {
+ .probe = aic32x4_tas2505_component_probe,
+ .set_bias_level = aic32x4_set_bias_level,
+ .controls = aic32x4_tas2505_snd_controls,
+ .num_controls = ARRAY_SIZE(aic32x4_tas2505_snd_controls),
+ .dapm_widgets = aic32x4_tas2505_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic32x4_tas2505_dapm_widgets),
+ .dapm_routes = aic32x4_tas2505_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aic32x4_tas2505_dapm_routes),
+ .of_xlate_dai_id = aic32x4_of_xlate_dai_id,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
struct device_node *np)
{
+ struct aic32x4_setup_data *aic32x4_setup;
+ int ret;
+
+ aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup),
+ GFP_KERNEL);
+ if (!aic32x4_setup)
+ return -ENOMEM;
+
+ ret = of_property_match_string(np, "clock-names", "mclk");
+ if (ret < 0)
+ return -EINVAL;
+ aic32x4->mclk_name = of_clk_get_parent_name(np, ret);
+
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
- aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+ /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */
+ aic32x4->rstn_gpio = devm_gpiod_get_optional(aic32x4->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(aic32x4->rstn_gpio)) {
+ return dev_err_probe(aic32x4->dev, PTR_ERR(aic32x4->rstn_gpio),
+ "Failed to get reset gpio\n");
+ } else {
+ gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn");
+ }
+ if (of_property_read_u32_array(np, "aic32x4-gpio-func",
+ aic32x4_setup->gpio_func, 5) >= 0)
+ aic32x4->setup = aic32x4_setup;
return 0;
}
@@ -704,8 +1278,8 @@ static int aic32x4_setup_regulators(struct device *dev,
/* 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);
+ return dev_err_probe(dev, PTR_ERR(aic32x4->supply_iov),
+ "Missing supply 'iov'\n");
}
if (IS_ERR(aic32x4->supply_ldo)) {
@@ -713,19 +1287,17 @@ static int aic32x4_setup_regulators(struct device *dev,
return -EPROBE_DEFER;
if (IS_ERR(aic32x4->supply_dv)) {
- dev_err(dev, "Missing supply 'dv' or 'ldoin'\n");
- return PTR_ERR(aic32x4->supply_dv);
+ return dev_err_probe(dev, PTR_ERR(aic32x4->supply_dv),
+ "Missing supply 'dv' or 'ldoin'\n");
}
if (IS_ERR(aic32x4->supply_av)) {
- dev_err(dev, "Missing supply 'av' or 'ldoin'\n");
- return PTR_ERR(aic32x4->supply_av);
+ return dev_err_probe(dev, PTR_ERR(aic32x4->supply_av),
+ "Missing supply 'av' or 'ldoin'\n");
}
} else {
- if (IS_ERR(aic32x4->supply_dv) &&
- PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
+ if (PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- if (IS_ERR(aic32x4->supply_av) &&
- PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER)
+ if (PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
@@ -777,108 +1349,90 @@ error_ldo:
return ret;
}
-static int aic32x4_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+int aic32x4_probe(struct device *dev, struct regmap *regmap,
+ enum aic32x4_type type)
{
- struct aic32x4_pdata *pdata = i2c->dev.platform_data;
struct aic32x4_priv *aic32x4;
- struct device_node *np = i2c->dev.of_node;
+ struct device_node *np = dev->of_node;
int ret;
- aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv),
- GFP_KERNEL);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ aic32x4 = devm_kzalloc(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);
+ aic32x4->dev = dev;
+ aic32x4->type = type;
- i2c_set_clientdata(i2c, aic32x4);
+ dev_set_drvdata(dev, aic32x4);
- if (pdata) {
- aic32x4->power_cfg = pdata->power_cfg;
- aic32x4->swapdacs = pdata->swapdacs;
- aic32x4->micpga_routing = pdata->micpga_routing;
- aic32x4->rstn_gpio = pdata->rstn_gpio;
- } else if (np) {
+ if (np) {
ret = aic32x4_parse_dt(aic32x4, np);
if (ret) {
- dev_err(&i2c->dev, "Failed to parse DT node\n");
+ dev_err(dev, "Failed to parse DT node\n");
return ret;
}
} else {
aic32x4->power_cfg = 0;
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
- aic32x4->rstn_gpio = -1;
+ aic32x4->rstn_gpio = NULL;
+ aic32x4->mclk_name = "mclk";
}
- 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);
- }
+ ret = aic32x4_setup_regulators(dev, aic32x4);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to setup regulators\n");
- 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;
+ if (aic32x4->rstn_gpio) {
+ ndelay(10);
+ /* deassert reset */
+ gpiod_set_value_cansleep(aic32x4->rstn_gpio, 0);
+ mdelay(1);
}
- ret = aic32x4_setup_regulators(&i2c->dev, aic32x4);
- if (ret) {
- dev_err(&i2c->dev, "Failed to setup regulators\n");
- return ret;
+ ret = regmap_write(regmap, AIC32X4_RESET, 0x01);
+ if (ret)
+ goto err_disable_regulators;
+
+ ret = aic32x4_register_clocks(dev, aic32x4->mclk_name);
+ if (ret)
+ goto err_disable_regulators;
+
+ switch (aic32x4->type) {
+ case AIC32X4_TYPE_TAS2505:
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1);
+ break;
+ default:
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_dev_aic32x4, &aic32x4_dai, 1);
}
- 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;
+ dev_err(dev, "Failed to register component\n");
+ goto err_disable_regulators;
}
- i2c_set_clientdata(i2c, aic32x4);
-
return 0;
-}
-
-static int aic32x4_i2c_remove(struct i2c_client *client)
-{
- struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client);
+err_disable_regulators:
aic32x4_disable_regulators(aic32x4);
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return ret;
}
+EXPORT_SYMBOL(aic32x4_probe);
-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",
- .of_match_table = aic32x4_of_id,
- },
- .probe = aic32x4_i2c_probe,
- .remove = aic32x4_i2c_remove,
- .id_table = aic32x4_i2c_id,
-};
+void aic32x4_remove(struct device *dev)
+{
+ struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev);
-module_i2c_driver(aic32x4_i2c_driver);
+ aic32x4_disable_regulators(aic32x4);
+}
+EXPORT_SYMBOL(aic32x4_remove);
MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
index 995f033a855d..f68a846ef61d 100644
--- a/sound/soc/codecs/tlv320aic32x4.h
+++ b/sound/soc/codecs/tlv320aic32x4.h
@@ -1,149 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
+struct device;
+struct regmap_config;
+
+enum aic32x4_type {
+ AIC32X4_TYPE_AIC32X4 = 0,
+ AIC32X4_TYPE_AIC32X6,
+ AIC32X4_TYPE_TAS2505,
+};
+
+extern const struct regmap_config aic32x4_regmap_config;
+int aic32x4_probe(struct device *dev, struct regmap *regmap,
+ enum aic32x4_type type);
+void aic32x4_remove(struct device *dev);
+int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
+
/* tlv320aic32x4 register space (in decimal to match datasheet) */
-#define AIC32X4_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_REG(page, reg) ((page * 128) + reg)
+
+#define AIC32X4_PSEL AIC32X4_REG(0, 0)
+
+#define AIC32X4_RESET AIC32X4_REG(0, 1)
+#define AIC32X4_CLKMUX AIC32X4_REG(0, 4)
+#define AIC32X4_PLLPR AIC32X4_REG(0, 5)
+#define AIC32X4_PLLJ AIC32X4_REG(0, 6)
+#define AIC32X4_PLLDMSB AIC32X4_REG(0, 7)
+#define AIC32X4_PLLDLSB AIC32X4_REG(0, 8)
+#define AIC32X4_NDAC AIC32X4_REG(0, 11)
+#define AIC32X4_MDAC AIC32X4_REG(0, 12)
+#define AIC32X4_DOSRMSB AIC32X4_REG(0, 13)
+#define AIC32X4_DOSRLSB AIC32X4_REG(0, 14)
+#define AIC32X4_NADC AIC32X4_REG(0, 18)
+#define AIC32X4_MADC AIC32X4_REG(0, 19)
+#define AIC32X4_AOSR AIC32X4_REG(0, 20)
+#define AIC32X4_CLKMUX2 AIC32X4_REG(0, 25)
+#define AIC32X4_CLKOUTM AIC32X4_REG(0, 26)
+#define AIC32X4_IFACE1 AIC32X4_REG(0, 27)
+#define AIC32X4_IFACE2 AIC32X4_REG(0, 28)
+#define AIC32X4_IFACE3 AIC32X4_REG(0, 29)
+#define AIC32X4_BCLKN AIC32X4_REG(0, 30)
+#define AIC32X4_IFACE4 AIC32X4_REG(0, 31)
+#define AIC32X4_IFACE5 AIC32X4_REG(0, 32)
+#define AIC32X4_IFACE6 AIC32X4_REG(0, 33)
+#define AIC32X4_GPIOCTL AIC32X4_REG(0, 52)
+#define AIC32X4_DOUTCTL AIC32X4_REG(0, 53)
+#define AIC32X4_DINCTL AIC32X4_REG(0, 54)
+#define AIC32X4_MISOCTL AIC32X4_REG(0, 55)
+#define AIC32X4_SCLKCTL AIC32X4_REG(0, 56)
+#define AIC32X4_DACSPB AIC32X4_REG(0, 60)
+#define AIC32X4_ADCSPB AIC32X4_REG(0, 61)
+#define AIC32X4_DACSETUP AIC32X4_REG(0, 63)
+#define AIC32X4_DACMUTE AIC32X4_REG(0, 64)
+#define AIC32X4_LDACVOL AIC32X4_REG(0, 65)
+#define AIC32X4_RDACVOL AIC32X4_REG(0, 66)
+#define AIC32X4_ADCSETUP AIC32X4_REG(0, 81)
+#define AIC32X4_ADCFGA AIC32X4_REG(0, 82)
+#define AIC32X4_LADCVOL AIC32X4_REG(0, 83)
+#define AIC32X4_RADCVOL AIC32X4_REG(0, 84)
+#define AIC32X4_LAGC1 AIC32X4_REG(0, 86)
+#define AIC32X4_LAGC2 AIC32X4_REG(0, 87)
+#define AIC32X4_LAGC3 AIC32X4_REG(0, 88)
+#define AIC32X4_LAGC4 AIC32X4_REG(0, 89)
+#define AIC32X4_LAGC5 AIC32X4_REG(0, 90)
+#define AIC32X4_LAGC6 AIC32X4_REG(0, 91)
+#define AIC32X4_LAGC7 AIC32X4_REG(0, 92)
+#define AIC32X4_RAGC1 AIC32X4_REG(0, 94)
+#define AIC32X4_RAGC2 AIC32X4_REG(0, 95)
+#define AIC32X4_RAGC3 AIC32X4_REG(0, 96)
+#define AIC32X4_RAGC4 AIC32X4_REG(0, 97)
+#define AIC32X4_RAGC5 AIC32X4_REG(0, 98)
+#define AIC32X4_RAGC6 AIC32X4_REG(0, 99)
+#define AIC32X4_RAGC7 AIC32X4_REG(0, 100)
+
+#define AIC32X4_PWRCFG AIC32X4_REG(1, 1)
+#define AIC32X4_LDOCTL AIC32X4_REG(1, 2)
+#define AIC32X4_LPLAYBACK AIC32X4_REG(1, 3)
+#define AIC32X4_RPLAYBACK AIC32X4_REG(1, 4)
+#define AIC32X4_OUTPWRCTL AIC32X4_REG(1, 9)
+#define AIC32X4_CMMODE AIC32X4_REG(1, 10)
+#define AIC32X4_HPLROUTE AIC32X4_REG(1, 12)
+#define AIC32X4_HPRROUTE AIC32X4_REG(1, 13)
+#define AIC32X4_LOLROUTE AIC32X4_REG(1, 14)
+#define AIC32X4_LORROUTE AIC32X4_REG(1, 15)
+#define AIC32X4_HPLGAIN AIC32X4_REG(1, 16)
+#define AIC32X4_HPRGAIN AIC32X4_REG(1, 17)
+#define AIC32X4_LOLGAIN AIC32X4_REG(1, 18)
+#define AIC32X4_LORGAIN AIC32X4_REG(1, 19)
+#define AIC32X4_HEADSTART AIC32X4_REG(1, 20)
+#define TAS2505_SPK AIC32X4_REG(1, 45)
+#define TAS2505_SPKVOL1 AIC32X4_REG(1, 46)
+#define TAS2505_SPKVOL2 AIC32X4_REG(1, 48)
+#define AIC32X4_MICBIAS AIC32X4_REG(1, 51)
+#define AIC32X4_LMICPGAPIN AIC32X4_REG(1, 52)
+#define AIC32X4_LMICPGANIN AIC32X4_REG(1, 54)
+#define AIC32X4_RMICPGAPIN AIC32X4_REG(1, 55)
+#define AIC32X4_RMICPGANIN AIC32X4_REG(1, 57)
+#define AIC32X4_FLOATINGINPUT AIC32X4_REG(1, 58)
+#define AIC32X4_LMICPGAVOL AIC32X4_REG(1, 59)
+#define AIC32X4_RMICPGAVOL AIC32X4_REG(1, 60)
+#define TAS2505_REFPOWERUP AIC32X4_REG(1, 122)
+#define AIC32X4_REFPOWERUP AIC32X4_REG(1, 123)
+
+/* Bits, masks, and shifts */
+
+/* AIC32X4_CLKMUX */
+#define AIC32X4_PLL_CLKIN_MASK GENMASK(3, 2)
+#define AIC32X4_PLL_CLKIN_SHIFT (2)
+#define AIC32X4_PLL_CLKIN_MCLK (0x00)
+#define AIC32X4_PLL_CLKIN_BCKL (0x01)
+#define AIC32X4_PLL_CLKIN_GPIO1 (0x02)
+#define AIC32X4_PLL_CLKIN_DIN (0x03)
+#define AIC32X4_CODEC_CLKIN_MASK GENMASK(1, 0)
+#define AIC32X4_CODEC_CLKIN_SHIFT (0)
+#define AIC32X4_CODEC_CLKIN_MCLK (0x00)
+#define AIC32X4_CODEC_CLKIN_BCLK (0x01)
+#define AIC32X4_CODEC_CLKIN_GPIO1 (0x02)
+#define AIC32X4_CODEC_CLKIN_PLL (0x03)
+
+/* AIC32X4_PLLPR */
+#define AIC32X4_PLLEN BIT(7)
+#define AIC32X4_PLL_P_MASK GENMASK(6, 4)
+#define AIC32X4_PLL_P_SHIFT (4)
+#define AIC32X4_PLL_R_MASK GENMASK(3, 0)
+
+/* AIC32X4_NDAC */
+#define AIC32X4_NDACEN BIT(7)
+#define AIC32X4_NDAC_MASK GENMASK(6, 0)
+
+/* AIC32X4_MDAC */
+#define AIC32X4_MDACEN BIT(7)
+#define AIC32X4_MDAC_MASK GENMASK(6, 0)
+
+/* AIC32X4_NADC */
+#define AIC32X4_NADCEN BIT(7)
+#define AIC32X4_NADC_MASK GENMASK(6, 0)
+
+/* AIC32X4_MADC */
+#define AIC32X4_MADCEN BIT(7)
+#define AIC32X4_MADC_MASK GENMASK(6, 0)
+
+/* AIC32X4_BCLKN */
+#define AIC32X4_BCLKEN BIT(7)
+#define AIC32X4_BCLK_MASK GENMASK(6, 0)
+
+/* AIC32X4_IFACE1 */
+#define AIC32X4_IFACE1_DATATYPE_MASK GENMASK(7, 6)
+#define AIC32X4_IFACE1_DATATYPE_SHIFT (6)
+#define AIC32X4_I2S_MODE (0x00)
+#define AIC32X4_DSP_MODE (0x01)
+#define AIC32X4_RIGHT_JUSTIFIED_MODE (0x02)
+#define AIC32X4_LEFT_JUSTIFIED_MODE (0x03)
+#define AIC32X4_IFACE1_DATALEN_MASK GENMASK(5, 4)
+#define AIC32X4_IFACE1_DATALEN_SHIFT (4)
+#define AIC32X4_WORD_LEN_16BITS (0x00)
+#define AIC32X4_WORD_LEN_20BITS (0x01)
+#define AIC32X4_WORD_LEN_24BITS (0x02)
+#define AIC32X4_WORD_LEN_32BITS (0x03)
+#define AIC32X4_IFACE1_MASTER_MASK GENMASK(3, 2)
+#define AIC32X4_BCLKMASTER BIT(2)
+#define AIC32X4_WCLKMASTER BIT(3)
+
+/* AIC32X4_IFACE2 */
+#define AIC32X4_DATA_OFFSET_MASK GENMASK(7, 0)
+
+/* AIC32X4_IFACE3 */
+#define AIC32X4_BCLKINV_MASK BIT(3)
+#define AIC32X4_BDIVCLK_MASK GENMASK(1, 0)
+#define AIC32X4_BDIVCLK_SHIFT (0)
+#define AIC32X4_DAC2BCLK (0x00)
+#define AIC32X4_DACMOD2BCLK (0x01)
+#define AIC32X4_ADC2BCLK (0x02)
+#define AIC32X4_ADCMOD2BCLK (0x03)
+
+/* AIC32X4_DACSETUP */
+#define AIC32X4_DAC_CHAN_MASK GENMASK(5, 2)
+#define AIC32X4_LDAC2RCHN BIT(5)
+#define AIC32X4_LDAC2LCHN BIT(4)
+#define AIC32X4_RDAC2LCHN BIT(3)
+#define AIC32X4_RDAC2RCHN BIT(2)
+
+/* AIC32X4_DACMUTE */
+#define AIC32X4_MUTEON 0x0C
+
+/* AIC32X4_ADCSETUP */
+#define AIC32X4_LADC_EN BIT(7)
+#define AIC32X4_RADC_EN BIT(6)
+
+/* AIC32X4_PWRCFG */
+#define AIC32X4_AVDDWEAKDISABLE BIT(3)
+
+/* AIC32X4_LDOCTL */
+#define AIC32X4_LDOCTLEN BIT(0)
+
+/* AIC32X4_CMMODE */
+#define AIC32X4_LDOIN_18_36 BIT(0)
+#define AIC32X4_LDOIN2HP BIT(1)
+
+/* AIC32X4_MICBIAS */
+#define AIC32X4_MICBIAS_LDOIN BIT(3)
#define AIC32X4_MICBIAS_2075V 0x60
+#define AIC32x4_MICBIAS_MASK GENMASK(6, 3)
+/* AIC32X4_LMICPGANIN */
#define AIC32X4_LMICPGANIN_IN2R_10K 0x10
#define AIC32X4_LMICPGANIN_CM1L_10K 0x40
+
+/* AIC32X4_RMICPGANIN */
#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
#define AIC32X4_RMICPGANIN_CM1R_10K 0x40
-#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
+/* AIC32X4_REFPOWERUP */
+#define AIC32X4_REFPOWERUP_SLOW 0x04
+#define AIC32X4_REFPOWERUP_40MS 0x05
+#define AIC32X4_REFPOWERUP_80MS 0x06
+#define AIC32X4_REFPOWERUP_120MS 0x07
+
+/* Common mask and enable for all of the dividers */
+#define AIC32X4_DIVEN BIT(7)
+#define AIC32X4_DIV_MASK GENMASK(6, 0)
+#define AIC32X4_DIV_MAX 128
+
+/* Clock Limits */
+#define AIC32X4_MAX_DOSR_FREQ 6200000
+#define AIC32X4_MIN_DOSR_FREQ 2800000
+#define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000
+#define AIC32X4_MAX_PLL_CLKIN 20000000
#endif /* _TLV320AIC32X4_H */
diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c
new file mode 100644
index 000000000000..0b585925c1ac
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic3x-i2c.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * ALSA SoC TLV320AIC3x 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
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic3x.h"
+
+static const struct i2c_device_id aic3x_i2c_id[] = {
+ { "tlv320aic3x", AIC3X_MODEL_3X },
+ { "tlv320aic33", AIC3X_MODEL_33 },
+ { "tlv320aic3007", AIC3X_MODEL_3007 },
+ { "tlv320aic3104", AIC3X_MODEL_3104 },
+ { "tlv320aic3106", AIC3X_MODEL_3106 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
+
+static int aic3x_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *regmap;
+ struct regmap_config config;
+
+ config = aic3x_regmap;
+ config.reg_bits = 8;
+ config.val_bits = 8;
+
+ regmap = devm_regmap_init_i2c(i2c, &config);
+ return aic3x_probe(&i2c->dev, regmap, (uintptr_t)i2c_get_match_data(i2c));
+}
+
+static void aic3x_i2c_remove(struct i2c_client *i2c)
+{
+ aic3x_remove(&i2c->dev);
+}
+
+static const struct of_device_id aic3x_of_id[] = {
+ { .compatible = "ti,tlv320aic3x", },
+ { .compatible = "ti,tlv320aic33" },
+ { .compatible = "ti,tlv320aic3007" },
+ { .compatible = "ti,tlv320aic3104" },
+ { .compatible = "ti,tlv320aic3106" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aic3x_of_id);
+
+static struct i2c_driver aic3x_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic3x",
+ .of_match_table = aic3x_of_id,
+ },
+ .probe = aic3x_i2c_probe,
+ .remove = aic3x_i2c_remove,
+ .id_table = aic3x_i2c_id,
+};
+
+module_i2c_driver(aic3x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3x codec driver I2C");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic3x-spi.c b/sound/soc/codecs/tlv320aic3x-spi.c
new file mode 100644
index 000000000000..f8c1c16eaa0e
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic3x-spi.c
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC TLV320AIC3x 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
+ *
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic3x.h"
+
+static int aic3x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ struct regmap_config config;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ config = aic3x_regmap;
+ config.reg_bits = 7;
+ config.pad_bits = 1;
+ config.val_bits = 8;
+ config.read_flag_mask = 0x01;
+
+ dev_dbg(&spi->dev, "probing tlv320aic3x spi device\n");
+
+ regmap = devm_regmap_init_spi(spi, &config);
+ return aic3x_probe(&spi->dev, regmap, id->driver_data);
+}
+
+static void aic3x_spi_remove(struct spi_device *spi)
+{
+ aic3x_remove(&spi->dev);
+}
+
+static const struct spi_device_id aic3x_spi_id[] = {
+ { "tlv320aic3x", AIC3X_MODEL_3X },
+ { "tlv320aic33", AIC3X_MODEL_33 },
+ { "tlv320aic3007", AIC3X_MODEL_3007 },
+ { "tlv320aic3104", AIC3X_MODEL_3104 },
+ { "tlv320aic3106", AIC3X_MODEL_3106 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, aic3x_spi_id);
+
+static const struct of_device_id aic3x_of_id[] = {
+ { .compatible = "ti,tlv320aic3x", },
+ { .compatible = "ti,tlv320aic33" },
+ { .compatible = "ti,tlv320aic3007" },
+ { .compatible = "ti,tlv320aic3104" },
+ { .compatible = "ti,tlv320aic3106" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aic3x_of_id);
+
+static struct spi_driver aic3x_spi_driver = {
+ .driver = {
+ .name = "tlv320aic3x",
+ .of_match_table = aic3x_of_id,
+ },
+ .probe = aic3x_spi_probe,
+ .remove = aic3x_spi_remove,
+ .id_table = aic3x_spi_id,
+};
+
+module_spi_driver(aic3x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3x codec driver SPI");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index a564759845f9..ce22298b43ef 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1,15 +1,11 @@
-/*
- * ALSA SoC TLV320AIC3X codec driver
+// SPDX-License-Identifier: GPL-2.0-only
+/* 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.
@@ -36,12 +32,12 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.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>
@@ -49,7 +45,6 @@
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/tlv320aic3x.h>
#include "tlv320aic3x.h"
@@ -61,8 +56,6 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
"DRVDD", /* ADC Analog and Output Driver Voltage */
};
-static LIST_HEAD(reset_list);
-
struct aic3x_priv;
struct aic3x_disable_nb {
@@ -70,9 +63,13 @@ struct aic3x_disable_nb {
struct aic3x_priv *aic3x;
};
+struct aic3x_setup_data {
+ unsigned int gpio_func[2];
+};
+
/* codec private data */
struct aic3x_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regmap *regmap;
struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
@@ -81,18 +78,16 @@ struct aic3x_priv {
unsigned int dai_fmt;
unsigned int tdm_delay;
unsigned int slot_width;
- struct list_head list;
int master;
- int gpio_reset;
+ struct gpio_desc *gpio_reset;
+ bool shared_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;
+ /* Output Common-Mode Voltage */
+ u8 ocmv;
};
static const struct reg_default aic3x_reg[] = {
@@ -126,15 +121,36 @@ static const struct reg_default aic3x_reg[] = {
{ 108, 0x00 }, { 109, 0x00 },
};
-static const struct regmap_config aic3x_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
+static const struct reg_sequence aic3007_class_d[] = {
+ /* Class-D speaker driver init; datasheet p. 46 */
+ { AIC3X_PAGE_SELECT, 0x0D },
+ { 0xD, 0x0D },
+ { 0x8, 0x5C },
+ { 0x8, 0x5D },
+ { 0x8, 0x5C },
+ { AIC3X_PAGE_SELECT, 0x00 },
+};
+static bool aic3x_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AIC3X_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config aic3x_regmap = {
.max_register = DAC_ICC_ADJ,
.reg_defaults = aic3x_reg,
.num_reg_defaults = ARRAY_SIZE(aic3x_reg),
+
+ .volatile_reg = aic3x_volatile_reg,
+
.cache_type = REGCACHE_RBTREE,
};
+EXPORT_SYMBOL_GPL(aic3x_regmap);
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \
@@ -147,8 +163,8 @@ static const struct regmap_config aic3x_regmap = {
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 snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -157,7 +173,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned short val;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = {};
int connect, change;
val = (ucontrol->value.integer.value[0] & mask);
@@ -174,7 +190,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
mask <<= shift;
val <<= shift;
- change = snd_soc_test_bits(codec, reg, mask, val);
+ change = snd_soc_component_test_bits(component, reg, mask, val);
if (change) {
update.kcontrol = kcontrol;
update.reg = reg;
@@ -200,19 +216,19 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* change mic bias voltage to user defined */
- snd_soc_update_bits(codec, MICBIAS_CTRL,
+ snd_soc_component_update_bits(component, MICBIAS_CTRL,
MICBIAS_LEVEL_MASK,
aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, MICBIAS_CTRL,
+ snd_soc_component_update_bits(component, MICBIAS_CTRL,
MICBIAS_LEVEL_MASK, 0);
break;
}
@@ -309,6 +325,9 @@ static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 50, 0);
*/
static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1);
+/* Output volumes. From 0 to 9 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(out_tlv, 0, 100, 0);
+
static const struct snd_kcontrol_new aic3x_snd_controls[] = {
/* Output */
SOC_DOUBLE_R_TLV("PCM Playback Volume",
@@ -371,11 +390,17 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
DACL1_2_HPLCOM_VOL, DACR1_2_HPRCOM_VOL,
0, 118, 1, output_stage_tlv),
- /* Output pin mute controls */
+ /* Output pin controls */
+ SOC_DOUBLE_R_TLV("Line Playback Volume", LLOPM_CTRL, RLOPM_CTRL, 4,
+ 9, 0, out_tlv),
SOC_DOUBLE_R("Line Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
0x01, 0),
+ SOC_DOUBLE_R_TLV("HP Playback Volume", HPLOUT_CTRL, HPROUT_CTRL, 4,
+ 9, 0, out_tlv),
SOC_DOUBLE_R("HP Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
0x01, 0),
+ SOC_DOUBLE_R_TLV("HPCOM Playback Volume", HPLCOM_CTRL, HPRCOM_CTRL,
+ 4, 9, 0, out_tlv),
SOC_DOUBLE_R("HPCOM Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
0x01, 0),
@@ -457,6 +482,9 @@ static const struct snd_kcontrol_new aic3x_mono_controls[] = {
0, 118, 1, output_stage_tlv),
SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
+ SOC_SINGLE_TLV("Mono Playback Volume", MONOLOPM_CTRL, 4, 9, 0,
+ out_tlv),
+
};
/*
@@ -978,14 +1006,15 @@ static const struct snd_soc_dapm_route intercon_3007[] = {
{"SPOM", NULL, "Right Class-D Out"},
};
-static int aic3x_add_widgets(struct snd_soc_codec *codec)
+static int aic3x_add_widgets(struct snd_soc_component *component)
{
- struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (aic3x->model) {
case AIC3X_MODEL_3X:
case AIC3X_MODEL_33:
+ case AIC3X_MODEL_3106:
snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets,
ARRAY_SIZE(aic3x_extra_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon_extra,
@@ -1020,8 +1049,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
@@ -1032,7 +1061,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
width = params_width(params);
/* select data word length */
- data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
+ data = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
switch (width) {
case 16:
break;
@@ -1046,7 +1075,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
data |= (0x03 << 4);
break;
}
- snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+ snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLB, data);
/* Fsref can be 44100 or 48000 */
fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
@@ -1061,15 +1090,15 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
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);
+ snd_soc_component_write(component, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+ snd_soc_component_write(component, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
/* disable PLL if it is bypassed */
- snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0);
+ snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0);
} else {
- snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+ snd_soc_component_write(component, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
/* enable PLL when it is used */
- snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
+ snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG,
PLL_ENABLE, PLL_ENABLE);
}
@@ -1079,7 +1108,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
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);
+ snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
data = (fsref * 20) / params_rate(params);
@@ -1088,7 +1117,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
data /= 5;
data -= 2;
data |= (data << 4);
- snd_soc_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
+ snd_soc_component_write(component, AIC3X_SAMPLE_RATE_SEL_REG, data);
if (bypass_pll)
return 0;
@@ -1157,13 +1186,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
}
found:
- snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p);
- snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,
+ snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p);
+ snd_soc_component_write(component, AIC3X_OVRF_STATUS_AND_PLLR_REG,
pll_r << PLLR_SHIFT);
- snd_soc_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
- snd_soc_write(codec, AIC3X_PLL_PROGC_REG,
+ snd_soc_component_write(component, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
+ snd_soc_component_write(component, AIC3X_PLL_PROGC_REG,
(pll_d >> 6) << PLLD_MSB_SHIFT);
- snd_soc_write(codec, AIC3X_PLL_PROGD_REG,
+ snd_soc_component_write(component, AIC3X_PLL_PROGD_REG,
(pll_d & 0x3F) << PLLD_LSB_SHIFT);
return 0;
@@ -1172,8 +1201,8 @@ found:
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);
+ struct snd_soc_component *component = dai->component;
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int delay = 0;
int width = aic3x->slot_width;
@@ -1187,23 +1216,23 @@ static int aic3x_prepare(struct snd_pcm_substream *substream,
delay += aic3x->tdm_delay*width;
/* Configure data delay */
- snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
+ snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLC, delay);
return 0;
}
-static int aic3x_mute(struct snd_soc_dai *dai, int mute)
+static int aic3x_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- 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;
+ struct snd_soc_component *component = dai->component;
+ u8 ldac_reg = snd_soc_component_read(component, LDAC_VOL) & ~MUTE_ON;
+ u8 rdac_reg = snd_soc_component_read(component, RDAC_VOL) & ~MUTE_ON;
if (mute) {
- snd_soc_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
- snd_soc_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
+ snd_soc_component_write(component, LDAC_VOL, ldac_reg | MUTE_ON);
+ snd_soc_component_write(component, RDAC_VOL, rdac_reg | MUTE_ON);
} else {
- snd_soc_write(codec, LDAC_VOL, ldac_reg);
- snd_soc_write(codec, RDAC_VOL, rdac_reg);
+ snd_soc_component_write(component, LDAC_VOL, ldac_reg);
+ snd_soc_component_write(component, RDAC_VOL, rdac_reg);
}
return 0;
@@ -1212,13 +1241,13 @@ static int aic3x_mute(struct snd_soc_dai *dai, int mute)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
/* set clock on MCLK or GPIO2 or BCLK */
- snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK,
+ snd_soc_component_update_bits(component, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK,
clk_id << PLLCLK_IN_SHIFT);
- snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK,
+ snd_soc_component_update_bits(component, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK,
clk_id << CLKDIV_IN_SHIFT);
aic3x->sysclk = freq;
@@ -1228,23 +1257,32 @@ static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
u8 iface_areg, iface_breg;
- iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
- iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
+ iface_areg = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+ iface_breg = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLB) & 0x3f;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
aic3x->master = 1;
iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
aic3x->master = 0;
iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER);
break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ aic3x->master = 1;
+ iface_areg |= BIT_CLK_MASTER;
+ iface_areg &= ~WORD_CLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ aic3x->master = 1;
+ iface_areg |= WORD_CLK_MASTER;
+ iface_areg &= ~BIT_CLK_MASTER;
+ break;
default:
return -EINVAL;
}
@@ -1274,8 +1312,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
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);
+ snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLA, iface_areg);
+ snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLB, iface_breg);
return 0;
}
@@ -1284,24 +1322,24 @@ 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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
unsigned int lsb;
if (tx_mask != rx_mask) {
- dev_err(codec->dev, "tx and rx masks must be symmetric\n");
+ dev_err(component->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");
+ dev_err(component->dev, "tx and rx masks need to be non 0\n");
return -EINVAL;
}
/* TDM based on DSP mode requires slots to be adjacent */
lsb = __ffs(tx_mask);
if ((lsb + 1) != __fls(tx_mask)) {
- dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+ dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
return -EINVAL;
}
@@ -1312,7 +1350,7 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
case 32:
break;
default:
- dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
+ dev_err(component->dev, "Unsupported slot width %d\n", slot_width);
return -EINVAL;
}
@@ -1321,7 +1359,7 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
aic3x->slot_width = slot_width;
/* DOUT in high-impedance on inactive bit clocks */
- snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
+ snd_soc_component_update_bits(component, AIC3X_ASD_INTF_CTRLA,
DOUT_TRISTATE, DOUT_TRISTATE);
return 0;
@@ -1339,17 +1377,17 @@ static int aic3x_regulator_event(struct notifier_block *nb,
* 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);
+ if (aic3x->gpio_reset)
+ gpiod_set_value(aic3x->gpio_reset, 1);
regcache_mark_dirty(aic3x->regmap);
}
return 0;
}
-static int aic3x_set_power(struct snd_soc_codec *codec, int power)
+static int aic3x_set_power(struct snd_soc_component *component, int power)
{
- struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
unsigned int pll_c, pll_d;
int ret;
@@ -1360,11 +1398,15 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
goto out;
aic3x->power = 1;
- if (gpio_is_valid(aic3x->gpio_reset)) {
+ if (aic3x->gpio_reset) {
udelay(1);
- gpio_set_value(aic3x->gpio_reset, 1);
+ gpiod_set_value(aic3x->gpio_reset, 0);
}
+ if (aic3x->model == AIC3X_MODEL_3007)
+ regmap_multi_reg_write_bypassed(aic3x->regmap, aic3007_class_d,
+ ARRAY_SIZE(aic3007_class_d));
+
/* Sync reg_cache with the hardware */
regcache_cache_only(aic3x->regmap, false);
regcache_sync(aic3x->regmap);
@@ -1373,20 +1415,26 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
* 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);
+ pll_c = snd_soc_component_read(component, AIC3X_PLL_PROGC_REG);
+ pll_d = snd_soc_component_read(component, AIC3X_PLL_PROGD_REG);
if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def ||
pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) {
- snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c);
- snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d);
+ snd_soc_component_write(component, AIC3X_PLL_PROGC_REG, pll_c);
+ snd_soc_component_write(component, AIC3X_PLL_PROGD_REG, pll_d);
}
+
+ /*
+ * Delay is needed to reduce pop-noise after syncing back the
+ * registers
+ */
+ mdelay(50);
} else {
/*
* Do soft reset to this codec instance in order to clear
* possible VDD leakage currents in case the supply regulators
* remain on
*/
- snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
+ snd_soc_component_write(component, AIC3X_RESET, SOFT_RESET);
regcache_mark_dirty(aic3x->regmap);
aic3x->power = 0;
/* HW writes are needless when bias is off */
@@ -1398,35 +1446,36 @@ out:
return ret;
}
-static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+static int aic3x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY &&
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY &&
aic3x->master) {
/* enable pll */
- snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
+ snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG,
PLL_ENABLE, PLL_ENABLE);
}
break;
case SND_SOC_BIAS_STANDBY:
if (!aic3x->power)
- aic3x_set_power(codec, 1);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE &&
+ aic3x_set_power(component, 1);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE &&
aic3x->master) {
/* disable pll */
- snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
+ snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG,
PLL_ENABLE, 0);
}
break;
case SND_SOC_BIAS_OFF:
if (aic3x->power)
- aic3x_set_power(codec, 0);
+ aic3x_set_power(component, 0);
break;
}
@@ -1441,10 +1490,11 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops aic3x_dai_ops = {
.hw_params = aic3x_hw_params,
.prepare = aic3x_prepare,
- .digital_mute = aic3x_mute,
+ .mute_stream = aic3x_mute,
.set_sysclk = aic3x_set_dai_sysclk,
.set_fmt = aic3x_set_dai_fmt,
.set_tdm_slot = aic3x_set_dai_tdm_slot,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver aic3x_dai = {
@@ -1462,161 +1512,154 @@ static struct snd_soc_dai_driver aic3x_dai = {
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
.ops = &aic3x_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static void aic3x_mono_init(struct snd_soc_codec *codec)
+static void aic3x_mono_init(struct snd_soc_component *component)
{
/* 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);
+ snd_soc_component_write(component, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_component_write(component, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
/* unmute all outputs */
- snd_soc_update_bits(codec, MONOLOPM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_write(component, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
/* Line2 to Mono Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
}
/*
* initialise the AIC3X driver
* register the mixer and dsp interfaces with the kernel
*/
-static int aic3x_init(struct snd_soc_codec *codec)
+static int aic3x_init(struct snd_soc_component *component)
{
- struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
- snd_soc_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
- snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
+ snd_soc_component_write(component, AIC3X_PAGE_SELECT, PAGE0_SELECT);
+ snd_soc_component_write(component, AIC3X_RESET, SOFT_RESET);
/* DAC default volume and mute */
- snd_soc_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
- snd_soc_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
+ snd_soc_component_write(component, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
+ snd_soc_component_write(component, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
/* DAC to HP default volume and route to Output mixer */
- snd_soc_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);
+ snd_soc_component_write(component, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_component_write(component, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_component_write(component, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_component_write(component, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
/* DAC to Line Out default volume and route to Output mixer */
- snd_soc_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
- snd_soc_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_component_write(component, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_component_write(component, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
/* unmute all outputs */
- snd_soc_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);
+ snd_soc_component_update_bits(component, LLOPM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_component_update_bits(component, RLOPM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_component_update_bits(component, HPLOUT_CTRL, UNMUTE, UNMUTE);
+ snd_soc_component_update_bits(component, HPROUT_CTRL, UNMUTE, UNMUTE);
+ snd_soc_component_update_bits(component, HPLCOM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_component_update_bits(component, HPRCOM_CTRL, UNMUTE, UNMUTE);
/* ADC default volume and unmute */
- snd_soc_write(codec, LADC_VOL, DEFAULT_GAIN);
- snd_soc_write(codec, RADC_VOL, DEFAULT_GAIN);
+ snd_soc_component_write(component, LADC_VOL, DEFAULT_GAIN);
+ snd_soc_component_write(component, RADC_VOL, DEFAULT_GAIN);
/* By default route Line1 to ADC PGA mixer */
- snd_soc_write(codec, LINE1L_2_LADC_CTRL, 0x0);
- snd_soc_write(codec, LINE1R_2_RADC_CTRL, 0x0);
+ snd_soc_component_write(component, LINE1L_2_LADC_CTRL, 0x0);
+ snd_soc_component_write(component, LINE1R_2_RADC_CTRL, 0x0);
/* PGA to HP Bypass default volume, disconnect from Output Mixer */
- snd_soc_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);
+ snd_soc_component_write(component, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
/* PGA to Line Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
/* On tlv320aic3104, these registers are reserved and must not be written */
if (aic3x->model != AIC3X_MODEL_3104) {
/* Line2 to HP Bypass default volume, disconnect from Output Mixer */
- snd_soc_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);
+ snd_soc_component_write(component, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
/* Line2 Line Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+ snd_soc_component_write(component, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
}
switch (aic3x->model) {
case AIC3X_MODEL_3X:
case AIC3X_MODEL_33:
- aic3x_mono_init(codec);
+ case AIC3X_MODEL_3106:
+ aic3x_mono_init(component);
break;
case AIC3X_MODEL_3007:
- snd_soc_write(codec, CLASSD_CTRL, 0);
+ snd_soc_component_write(component, 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;
- }
+ /* Output common-mode voltage = 1.5 V */
+ snd_soc_component_update_bits(component, HPOUT_SC, HPOUT_SC_OCMV_MASK,
+ aic3x->ocmv << HPOUT_SC_OCMV_SHIFT);
- return false;
+ return 0;
}
-static int aic3x_probe(struct snd_soc_codec *codec)
+static int aic3x_component_probe(struct snd_soc_component *component)
{
- struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int ret, i;
- INIT_LIST_HEAD(&aic3x->list);
- aic3x->codec = codec;
+ aic3x->component = component;
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
aic3x->disable_nb[i].aic3x = aic3x;
- ret = regulator_register_notifier(aic3x->supplies[i].consumer,
- &aic3x->disable_nb[i].nb);
+ ret = devm_regulator_register_notifier(
+ aic3x->supplies[i].consumer,
+ &aic3x->disable_nb[i].nb);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to request regulator notifier: %d\n",
ret);
- goto err_notif;
+ return ret;
}
}
regcache_mark_dirty(aic3x->regmap);
- aic3x_init(codec);
+ aic3x_init(component);
if (aic3x->setup) {
if (aic3x->model != AIC3X_MODEL_3104) {
/* setup GPIO functions */
- snd_soc_write(codec, AIC3X_GPIO1_REG,
+ snd_soc_component_write(component, AIC3X_GPIO1_REG,
(aic3x->setup->gpio_func[0] & 0xf) << 4);
- snd_soc_write(codec, AIC3X_GPIO2_REG,
+ snd_soc_component_write(component, AIC3X_GPIO2_REG,
(aic3x->setup->gpio_func[1] & 0xf) << 4);
} else {
- dev_warn(codec->dev, "GPIO functionality is not supported on tlv320aic3104\n");
+ dev_warn(component->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,
+ case AIC3X_MODEL_3106:
+ snd_soc_add_component_controls(component, aic3x_extra_snd_controls,
ARRAY_SIZE(aic3x_extra_snd_controls));
- snd_soc_add_codec_controls(codec, aic3x_mono_controls,
+ snd_soc_add_component_controls(component, aic3x_mono_controls,
ARRAY_SIZE(aic3x_mono_controls));
break;
case AIC3X_MODEL_3007:
- snd_soc_add_codec_controls(codec, aic3x_extra_snd_controls,
+ snd_soc_add_component_controls(component, aic3x_extra_snd_controls,
ARRAY_SIZE(aic3x_extra_snd_controls));
- snd_soc_add_codec_controls(codec,
+ snd_soc_add_component_controls(component,
&aic3x_classd_amp_gain_ctrl, 1);
break;
case AIC3X_MODEL_3104:
@@ -1628,7 +1671,7 @@ static int aic3x_probe(struct snd_soc_codec *codec)
case AIC3X_MICBIAS_2_0V:
case AIC3X_MICBIAS_2_5V:
case AIC3X_MICBIAS_AVDDV:
- snd_soc_update_bits(codec, MICBIAS_CTRL,
+ snd_soc_component_update_bits(component, MICBIAS_CTRL,
MICBIAS_LEVEL_MASK,
(aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT);
break;
@@ -1641,87 +1684,73 @@ static int aic3x_probe(struct snd_soc_codec *codec)
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);
+ aic3x_add_widgets(component);
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),
+static const struct snd_soc_component_driver soc_component_dev_aic3x = {
+ .set_bias_level = aic3x_set_bias_level,
+ .probe = aic3x_component_probe,
+ .controls = aic3x_snd_controls,
+ .num_controls = ARRAY_SIZE(aic3x_snd_controls),
+ .dapm_widgets = aic3x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-/*
- * AIC3X 2 wire address can be up to 4 devices with device addresses
- * 0x18, 0x19, 0x1A, 0x1B
- */
+static void aic3x_configure_ocmv(struct device *dev, struct aic3x_priv *aic3x)
+{
+ struct device_node *np = dev->of_node;
+ u32 value;
+ int dvdd, avdd;
-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);
+ if (np && !of_property_read_u32(np, "ai3x-ocmv", &value)) {
+ /* OCMV setting is forced by DT */
+ if (value <= 3) {
+ aic3x->ocmv = value;
+ return;
+ }
+ }
-static const struct reg_sequence aic3007_class_d[] = {
- /* Class-D speaker driver init; datasheet p. 46 */
- { AIC3X_PAGE_SELECT, 0x0D },
- { 0xD, 0x0D },
- { 0x8, 0x5C },
- { 0x8, 0x5D },
- { 0x8, 0x5C },
- { AIC3X_PAGE_SELECT, 0x00 },
-};
+ dvdd = regulator_get_voltage(aic3x->supplies[1].consumer);
+ avdd = regulator_get_voltage(aic3x->supplies[2].consumer);
+
+ if (avdd > 3600000 || dvdd > 1950000) {
+ dev_warn(dev,
+ "Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ } else if (avdd == 3600000 && dvdd == 1950000) {
+ aic3x->ocmv = HPOUT_SC_OCMV_1_8V;
+ } else if (avdd > 3300000 && dvdd > 1800000) {
+ aic3x->ocmv = HPOUT_SC_OCMV_1_65V;
+ } else if (avdd > 3000000 && dvdd > 1650000) {
+ aic3x->ocmv = HPOUT_SC_OCMV_1_5V;
+ } else if (avdd >= 2700000 && dvdd >= 1525000) {
+ aic3x->ocmv = HPOUT_SC_OCMV_1_35V;
+ } else {
+ dev_warn(dev,
+ "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ }
+}
-/*
- * 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)
+int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver_data)
{
- 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;
+ struct device_node *np = dev->of_node;
int ret, i;
u32 value;
- aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);
+ aic3x = devm_kzalloc(dev, sizeof(struct aic3x_priv), GFP_KERNEL);
if (!aic3x)
return -ENOMEM;
- aic3x->regmap = devm_regmap_init_i2c(i2c, &aic3x_regmap);
+ aic3x->regmap = regmap;
if (IS_ERR(aic3x->regmap)) {
ret = PTR_ERR(aic3x->regmap);
return ret;
@@ -1729,23 +1758,12 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
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);
+ dev_set_drvdata(dev, aic3x);
+ if (np) {
+ ai3x_setup = devm_kzalloc(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;
@@ -1764,100 +1782,68 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
break;
default :
aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
- dev_err(&i2c->dev, "Unsuitable MicBias voltage "
+ dev_err(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;
+ aic3x->model = 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);
+ aic3x->gpio_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset);
+ if (ret) {
+ if (ret != -EBUSY)
+ return ret;
+
+ /*
+ * Apparently there are setups where the codec is sharing
+ * its reset line. Try to get it non-exclusively, although
+ * the utility of this is unclear: how do we make sure that
+ * resetting one chip will not disturb the others that share
+ * the same line?
+ */
+ aic3x->gpio_reset = devm_gpiod_get(dev, "reset",
+ GPIOD_ASIS | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset);
+ if (ret)
+ return ret;
+
+ aic3x->shared_reset = true;
}
+ gpiod_set_consumer_name(aic3x->gpio_reset, "tlv320aic3x reset");
+
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i];
- ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies),
+ ret = devm_regulator_bulk_get(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)
+ return dev_err_probe(dev, ret, "Failed to request supplies\n");
- if (ret != 0)
- goto err_gpio;
+ aic3x_configure_ocmv(dev, aic3x);
- list_add(&aic3x->list, &reset_list);
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic3x, &aic3x_dai, 1);
+ if (ret)
+ return ret;
return 0;
-
-err_gpio:
- if (gpio_is_valid(aic3x->gpio_reset) &&
- !aic3x_is_shared_reset(aic3x))
- gpio_free(aic3x->gpio_reset);
-err:
- return ret;
}
+EXPORT_SYMBOL(aic3x_probe);
-static int aic3x_i2c_remove(struct i2c_client *client)
+void aic3x_remove(struct device *dev)
{
- struct aic3x_priv *aic3x = i2c_get_clientdata(client);
+ struct aic3x_priv *aic3x = dev_get_drvdata(dev);
- 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;
+ /* Leave the codec in reset state */
+ if (aic3x->gpio_reset && !aic3x->shared_reset)
+ gpiod_set_value(aic3x->gpio_reset, 1);
}
-
-#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",
- .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);
+EXPORT_SYMBOL(aic3x_remove);
MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
MODULE_AUTHOR("Vladimir Barinov");
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index 89fa692df206..066e5a6322b8 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -1,17 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
+struct device;
+struct regmap_config;
+
+extern const struct regmap_config aic3x_regmap;
+int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver_data);
+void aic3x_remove(struct device *dev);
+
+#define AIC3X_MODEL_3X 0
+#define AIC3X_MODEL_33 1
+#define AIC3X_MODEL_3007 2
+#define AIC3X_MODEL_3104 3
+#define AIC3X_MODEL_3106 4
+
/* AIC3X register space */
#define AIC3X_CACHEREGNUM 110
@@ -243,6 +253,14 @@
#define MICBIAS_LEVEL_SHIFT (6)
#define MICBIAS_LEVEL_MASK (3 << 6)
+/* HPOUT_SC */
+#define HPOUT_SC_OCMV_MASK (3 << 6)
+#define HPOUT_SC_OCMV_SHIFT (6)
+#define HPOUT_SC_OCMV_1_35V 0
+#define HPOUT_SC_OCMV_1_5V 1
+#define HPOUT_SC_OCMV_1_65V 2
+#define HPOUT_SC_OCMV_1_8V 3
+
/* headset detection / button API */
/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
@@ -280,4 +298,47 @@ enum {
#define AIC3X_BUTTON_DEBOUNCE_SHIFT 0
#define AIC3X_BUTTON_DEBOUNCE_MASK 3
+/* GPIO API */
+enum {
+ AIC3X_GPIO1_FUNC_DISABLED = 0,
+ AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC = 1,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX = 2,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2 = 3,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4 = 4,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8 = 5,
+ AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ = 6,
+ AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ = 7,
+ AIC3X_GPIO1_FUNC_INPUT = 8,
+ AIC3X_GPIO1_FUNC_OUTPUT = 9,
+ AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK = 10,
+ AIC3X_GPIO1_FUNC_AUDIO_WORDCLK = 11,
+ AIC3X_GPIO1_FUNC_BUTTON_IRQ = 12,
+ AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ = 13,
+ AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 14,
+ AIC3X_GPIO1_FUNC_ALL_IRQ = 16
+};
+
+enum {
+ AIC3X_GPIO2_FUNC_DISABLED = 0,
+ AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ = 2,
+ AIC3X_GPIO2_FUNC_INPUT = 3,
+ AIC3X_GPIO2_FUNC_OUTPUT = 4,
+ AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT = 5,
+ AIC3X_GPIO2_FUNC_AUDIO_BITCLK = 8,
+ AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9,
+ AIC3X_GPIO2_FUNC_ALL_IRQ = 10,
+ AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11,
+ AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12,
+ AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ = 13,
+ AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ = 14,
+ AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15
+};
+
+enum aic3x_micbias_voltage {
+ AIC3X_MICBIAS_OFF = 0,
+ AIC3X_MICBIAS_2_0V = 1,
+ AIC3X_MICBIAS_2_5V = 2,
+ AIC3X_MICBIAS_AVDDV = 3,
+};
+
#endif /* _AIC3X_H */
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 781398fb2841..605da1259fc6 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -28,7 +14,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -38,7 +24,6 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/tlv320dac33-plat.h>
#include "tlv320dac33.h"
/*
@@ -63,9 +48,9 @@
(((samples)*5000) / (((burstrate)*5000) / ((burstrate) - (playrate))))
static void dac33_calculate_times(struct snd_pcm_substream *substream,
- struct snd_soc_codec *codec);
+ struct snd_soc_component *component);
static int dac33_prepare_chip(struct snd_pcm_substream *substream,
- struct snd_soc_codec *codec);
+ struct snd_soc_component *component);
enum dac33_state {
DAC33_IDLE = 0,
@@ -90,12 +75,11 @@ static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
struct tlv320dac33_priv {
struct mutex mutex;
- struct workqueue_struct *dac33_wq;
struct work_struct work;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
struct snd_pcm_substream *substream;
- int power_gpio;
+ struct gpio_desc *reset_gpiod;
int chip_power;
int irq;
unsigned int refclk;
@@ -107,6 +91,7 @@ struct tlv320dac33_priv {
int mode1_latency; /* latency caused by the i2c writes in
* us */
u8 burst_bclkdiv; /* BCLK divider value in burst mode */
+ u8 *reg_cache;
unsigned int burst_rate; /* Interface speed in Burst modes */
int keep_bclk; /* Keep the BCLK continuously running
@@ -122,7 +107,7 @@ struct tlv320dac33_priv {
unsigned int uthr;
enum dac33_state state;
- void *control_data;
+ struct i2c_client *i2c;
};
static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
@@ -171,56 +156,58 @@ static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
};
/* Register read and write */
-static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec,
+static inline unsigned int dac33_read_reg_cache(struct snd_soc_component *component,
unsigned reg)
{
- u8 *cache = codec->reg_cache;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
+ u8 *cache = dac33->reg_cache;
if (reg >= DAC33_CACHEREGNUM)
return 0;
return cache[reg];
}
-static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
+static inline void dac33_write_reg_cache(struct snd_soc_component *component,
u8 reg, u8 value)
{
- u8 *cache = codec->reg_cache;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
+ u8 *cache = dac33->reg_cache;
if (reg >= DAC33_CACHEREGNUM)
return;
cache[reg] = value;
}
-static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
+static int dac33_read(struct snd_soc_component *component, unsigned int reg,
u8 *value)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int val, ret = 0;
*value = reg & 0xff;
/* If powered off, return the cached value */
if (dac33->chip_power) {
- val = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+ val = i2c_smbus_read_byte_data(dac33->i2c, value[0]);
if (val < 0) {
- dev_err(codec->dev, "Read failed (%d)\n", val);
- value[0] = dac33_read_reg_cache(codec, reg);
+ dev_err(component->dev, "Read failed (%d)\n", val);
+ value[0] = dac33_read_reg_cache(component, reg);
ret = val;
} else {
value[0] = val;
- dac33_write_reg_cache(codec, reg, val);
+ dac33_write_reg_cache(component, reg, val);
}
} else {
- value[0] = dac33_read_reg_cache(codec, reg);
+ value[0] = dac33_read_reg_cache(component, reg);
}
return ret;
}
-static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
+static int dac33_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
u8 data[2];
int ret = 0;
@@ -232,11 +219,11 @@ static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
data[0] = reg & 0xff;
data[1] = value & 0xff;
- dac33_write_reg_cache(codec, data[0], data[1]);
+ dac33_write_reg_cache(component, data[0], data[1]);
if (dac33->chip_power) {
- ret = codec->hw_write(codec->control_data, data, 2);
+ ret = i2c_master_send(dac33->i2c, data, 2);
if (ret != 2)
- dev_err(codec->dev, "Write failed (%d)\n", ret);
+ dev_err(component->dev, "Write failed (%d)\n", ret);
else
ret = 0;
}
@@ -244,24 +231,24 @@ static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
return ret;
}
-static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static int dac33_write_locked(struct snd_soc_component *component, unsigned int reg,
+ unsigned int value)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int ret;
mutex_lock(&dac33->mutex);
- ret = dac33_write(codec, reg, value);
+ ret = dac33_write(component, reg, value);
mutex_unlock(&dac33->mutex);
return ret;
}
#define DAC33_I2C_ADDR_AUTOINC 0x80
-static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
+static int dac33_write16(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
u8 data[3];
int ret = 0;
@@ -275,15 +262,15 @@ static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
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]);
+ dac33_write_reg_cache(component, data[0], data[1]);
+ dac33_write_reg_cache(component, data[0] + 1, data[2]);
if (dac33->chip_power) {
/* We need to set autoincrement mode for 16 bit writes */
data[0] |= DAC33_I2C_ADDR_AUTOINC;
- ret = codec->hw_write(codec->control_data, data, 3);
+ ret = i2c_master_send(dac33->i2c, data, 3);
if (ret != 3)
- dev_err(codec->dev, "Write failed (%d)\n", ret);
+ dev_err(component->dev, "Write failed (%d)\n", ret);
else
ret = 0;
}
@@ -291,52 +278,52 @@ static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
return ret;
}
-static void dac33_init_chip(struct snd_soc_codec *codec)
+static void dac33_init_chip(struct snd_soc_component *component)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
if (unlikely(!dac33->chip_power))
return;
/* A : DAC sample rate Fsref/1.5 */
- dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0));
+ dac33_write(component, DAC33_DAC_CTRL_A, DAC33_DACRATE(0));
/* B : DAC src=normal, not muted */
- dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+ dac33_write(component, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
DAC33_DACSRCL_LEFT);
/* C : (defaults) */
- dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+ dac33_write(component, 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);
+ dac33_write(component, 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));
+ dac33_write(component, DAC33_LDAC_DIG_VOL_CTRL,
+ dac33_read_reg_cache(component, DAC33_LDAC_DIG_VOL_CTRL));
+ dac33_write(component, DAC33_RDAC_DIG_VOL_CTRL,
+ dac33_read_reg_cache(component, DAC33_RDAC_DIG_VOL_CTRL));
+
+ dac33_write(component, DAC33_LINEL_TO_LLO_VOL,
+ dac33_read_reg_cache(component, DAC33_LINEL_TO_LLO_VOL));
+ dac33_write(component, DAC33_LINER_TO_RLO_VOL,
+ dac33_read_reg_cache(component, DAC33_LINER_TO_RLO_VOL));
+
+ dac33_write(component, DAC33_OUT_AMP_CTRL,
+ dac33_read_reg_cache(component, DAC33_OUT_AMP_CTRL));
+
+ dac33_write(component, DAC33_LDAC_PWR_CTRL,
+ dac33_read_reg_cache(component, DAC33_LDAC_PWR_CTRL));
+ dac33_write(component, DAC33_RDAC_PWR_CTRL,
+ dac33_read_reg_cache(component, DAC33_RDAC_PWR_CTRL));
}
-static inline int dac33_read_id(struct snd_soc_codec *codec)
+static inline int dac33_read_id(struct snd_soc_component *component)
{
int i, ret = 0;
u8 reg;
for (i = 0; i < 3; i++) {
- ret = dac33_read(codec, DAC33_DEVICE_ID_MSB + i, &reg);
+ ret = dac33_read(component, DAC33_DEVICE_ID_MSB + i, &reg);
if (ret < 0)
break;
}
@@ -344,44 +331,44 @@ static inline int dac33_read_id(struct snd_soc_codec *codec)
return ret;
}
-static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
+static inline void dac33_soft_power(struct snd_soc_component *component, int power)
{
u8 reg;
- reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ reg = dac33_read_reg_cache(component, DAC33_PWR_CTRL);
if (power)
reg |= DAC33_PDNALLB;
else
reg &= ~(DAC33_PDNALLB | DAC33_OSCPDNB |
DAC33_DACRPDNB | DAC33_DACLPDNB);
- dac33_write(codec, DAC33_PWR_CTRL, reg);
+ dac33_write(component, DAC33_PWR_CTRL, reg);
}
-static inline void dac33_disable_digital(struct snd_soc_codec *codec)
+static inline void dac33_disable_digital(struct snd_soc_component *component)
{
u8 reg;
/* Stop the DAI clock */
- reg = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ reg = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B);
reg &= ~DAC33_BCLKON;
- dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg);
+ dac33_write(component, DAC33_SER_AUDIOIF_CTRL_B, reg);
/* Power down the Oscillator, and DACs */
- reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ reg = dac33_read_reg_cache(component, DAC33_PWR_CTRL);
reg &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
- dac33_write(codec, DAC33_PWR_CTRL, reg);
+ dac33_write(component, DAC33_PWR_CTRL, reg);
}
-static int dac33_hard_power(struct snd_soc_codec *codec, int power)
+static int dac33_hard_power(struct snd_soc_component *component, int power)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int ret = 0;
mutex_lock(&dac33->mutex);
/* Safety check */
if (unlikely(power == dac33->chip_power)) {
- dev_dbg(codec->dev, "Trying to set the same power state: %s\n",
+ dev_dbg(component->dev, "Trying to set the same power state: %s\n",
power ? "ON" : "OFF");
goto exit;
}
@@ -390,24 +377,36 @@ static int dac33_hard_power(struct snd_soc_codec *codec, int power)
ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n", ret);
- goto exit;
+ goto exit;
}
- if (dac33->power_gpio >= 0)
- gpio_set_value(dac33->power_gpio, 1);
+ if (dac33->reset_gpiod) {
+ ret = gpiod_set_value(dac33->reset_gpiod, 1);
+ if (ret < 0) {
+ dev_err(&dac33->i2c->dev,
+ "Failed to set reset GPIO: %d\n", ret);
+ goto exit;
+ }
+ }
dac33->chip_power = 1;
} else {
- dac33_soft_power(codec, 0);
- if (dac33->power_gpio >= 0)
- gpio_set_value(dac33->power_gpio, 0);
+ dac33_soft_power(component, 0);
+ if (dac33->reset_gpiod) {
+ ret = gpiod_set_value(dac33->reset_gpiod, 0);
+ if (ret < 0) {
+ dev_err(&dac33->i2c->dev,
+ "Failed to set reset GPIO: %d\n", ret);
+ goto exit;
+ }
+ }
ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to disable supplies: %d\n", ret);
goto exit;
}
@@ -423,18 +422,18 @@ exit:
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (likely(dac33->substream)) {
- dac33_calculate_times(dac33->substream, codec);
- dac33_prepare_chip(dac33->substream, codec);
+ dac33_calculate_times(dac33->substream, component);
+ dac33_prepare_chip(dac33->substream, component);
}
break;
case SND_SOC_DAPM_POST_PMD:
- dac33_disable_digital(codec);
+ dac33_disable_digital(component);
break;
}
return 0;
@@ -443,10 +442,10 @@ static int dac33_playback_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = dac33->fifo_mode;
+ ucontrol->value.enumerated.item[0] = dac33->fifo_mode;
return 0;
}
@@ -454,21 +453,20 @@ static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int ret = 0;
- if (dac33->fifo_mode == ucontrol->value.integer.value[0])
+ if (dac33->fifo_mode == ucontrol->value.enumerated.item[0])
return 0;
/* Do not allow changes while stream is running*/
- if (snd_soc_codec_is_active(codec))
+ if (snd_soc_component_active(component))
return -EPERM;
- if (ucontrol->value.integer.value[0] < 0 ||
- ucontrol->value.integer.value[0] >= DAC33_FIFO_LAST_MODE)
+ if (ucontrol->value.enumerated.item[0] >= DAC33_FIFO_LAST_MODE)
ret = -EINVAL;
else
- dac33->fifo_mode = ucontrol->value.integer.value[0];
+ dac33->fifo_mode = ucontrol->value.enumerated.item[0];
return ret;
}
@@ -622,9 +620,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RIGHT_LO", NULL, "Codec Power"},
};
-static int dac33_set_bias_level(struct snd_soc_codec *codec,
+static int dac33_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -633,20 +632,20 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- /* Coming from OFF, switch on the codec */
- ret = dac33_hard_power(codec, 1);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ /* Coming from OFF, switch on the component */
+ ret = dac33_hard_power(component, 1);
if (ret != 0)
return ret;
- dac33_init_chip(codec);
+ dac33_init_chip(component);
}
break;
case SND_SOC_BIAS_OFF:
- /* Do not power off, when the codec is already off */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ /* Do not power off, when the component is already off */
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
return 0;
- ret = dac33_hard_power(codec, 0);
+ ret = dac33_hard_power(component, 0);
if (ret != 0)
return ret;
break;
@@ -657,13 +656,13 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
{
- struct snd_soc_codec *codec = dac33->codec;
+ struct snd_soc_component *component = dac33->component;
unsigned int delay;
unsigned long flags;
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
- dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ dac33_write16(component, DAC33_NSAMPLE_MSB,
DAC33_THRREG(dac33->nsample));
/* Take the timestamps */
@@ -672,13 +671,13 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
dac33->t_stamp1 = dac33->t_stamp2;
spin_unlock_irqrestore(&dac33->lock, flags);
- dac33_write16(codec, DAC33_PREFILL_MSB,
+ dac33_write16(component, DAC33_PREFILL_MSB,
DAC33_THRREG(dac33->alarm_threshold));
/* Enable Alarm Threshold IRQ with a delay */
delay = SAMPLES_TO_US(dac33->burst_rate,
dac33->alarm_threshold) + 1000;
usleep_range(delay, delay + 500);
- dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+ dac33_write(component, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
break;
case DAC33_FIFO_MODE7:
/* Take the timestamp */
@@ -688,14 +687,14 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
dac33->t_stamp1 -= dac33->mode7_us_to_lthr;
spin_unlock_irqrestore(&dac33->lock, flags);
- dac33_write16(codec, DAC33_PREFILL_MSB,
+ dac33_write16(component, DAC33_PREFILL_MSB,
DAC33_THRREG(DAC33_MODE7_MARGIN));
/* Enable Upper Threshold IRQ */
- dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MUT);
+ dac33_write(component, DAC33_FIFO_IRQ_MASK, DAC33_MUT);
break;
default:
- dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dev_warn(component->dev, "Unhandled FIFO mode: %d\n",
dac33->fifo_mode);
break;
}
@@ -703,7 +702,7 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
{
- struct snd_soc_codec *codec = dac33->codec;
+ struct snd_soc_component *component = dac33->component;
unsigned long flags;
switch (dac33->fifo_mode) {
@@ -713,14 +712,14 @@ static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
dac33->t_stamp2 = ktime_to_us(ktime_get());
spin_unlock_irqrestore(&dac33->lock, flags);
- dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ dac33_write16(component, DAC33_NSAMPLE_MSB,
DAC33_THRREG(dac33->nsample));
break;
case DAC33_FIFO_MODE7:
/* At the moment we are not using interrupts in mode7 */
break;
default:
- dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dev_warn(component->dev, "Unhandled FIFO mode: %d\n",
dac33->fifo_mode);
break;
}
@@ -728,12 +727,12 @@ static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
static void dac33_work(struct work_struct *work)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct tlv320dac33_priv *dac33;
u8 reg;
dac33 = container_of(work, struct tlv320dac33_priv, work);
- codec = dac33->codec;
+ component = dac33->component;
mutex_lock(&dac33->mutex);
switch (dac33->state) {
@@ -749,12 +748,12 @@ static void dac33_work(struct work_struct *work)
case DAC33_FLUSH:
dac33->state = DAC33_IDLE;
/* Mask all interrupts from dac33 */
- dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+ dac33_write(component, DAC33_FIFO_IRQ_MASK, 0);
/* flush fifo */
- reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ reg = dac33_read_reg_cache(component, DAC33_FIFO_CTRL_A);
reg |= DAC33_FIFOFLUSH;
- dac33_write(codec, DAC33_FIFO_CTRL_A, reg);
+ dac33_write(component, DAC33_FIFO_CTRL_A, reg);
break;
}
mutex_unlock(&dac33->mutex);
@@ -762,8 +761,8 @@ static void dac33_work(struct work_struct *work)
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);
+ struct snd_soc_component *component = dev;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
unsigned long flags;
spin_lock_irqsave(&dac33->lock, flags);
@@ -772,30 +771,30 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
/* Do not schedule the workqueue in Mode7 */
if (dac33->fifo_mode != DAC33_FIFO_MODE7)
- queue_work(dac33->dac33_wq, &dac33->work);
+ schedule_work(&dac33->work);
return IRQ_HANDLED;
}
-static void dac33_oscwait(struct snd_soc_codec *codec)
+static void dac33_oscwait(struct snd_soc_component *component)
{
int timeout = 60;
u8 reg;
do {
usleep_range(1000, 2000);
- dac33_read(codec, DAC33_INT_OSC_STATUS, &reg);
+ dac33_read(component, DAC33_INT_OSC_STATUS, &reg);
} while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--);
if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL)
- dev_err(codec->dev,
+ dev_err(component->dev,
"internal oscillator calibration failed\n");
}
static int dac33_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
/* Stream started, save the substream pointer */
dac33->substream = substream;
@@ -806,8 +805,8 @@ static int dac33_startup(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
dac33->substream = NULL;
}
@@ -818,8 +817,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
/* Check parameters for validity */
switch (params_rate(params)) {
@@ -827,7 +826,7 @@ static int dac33_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "unsupported rate %d\n",
+ dev_err(component->dev, "unsupported rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -842,7 +841,7 @@ static int dac33_hw_params(struct snd_pcm_substream *substream,
dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 64);
break;
default:
- dev_err(codec->dev, "unsupported width %d\n",
+ dev_err(component->dev, "unsupported width %d\n",
params_width(params));
return -EINVAL;
}
@@ -861,9 +860,9 @@ static int dac33_hw_params(struct snd_pcm_substream *substream,
* 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 snd_soc_component *component)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
u8 aictrl_a, aictrl_b, fifoctrl_a;
@@ -875,16 +874,16 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
dac33->refclk);
break;
default:
- dev_err(codec->dev, "unsupported rate %d\n",
+ dev_err(component->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_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_A);
aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
/* Read FIFO control A, and clear FIFO flush bit */
- fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ fifoctrl_a = dac33_read_reg_cache(component, DAC33_FIFO_CTRL_A);
fifoctrl_a &= ~DAC33_FIFOFLUSH;
fifoctrl_a &= ~DAC33_WIDTH;
@@ -897,7 +896,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
aictrl_a |= (DAC33_NCYCL_32 | DAC33_WLEN_24);
break;
default:
- dev_err(codec->dev, "unsupported format %d\n",
+ dev_err(component->dev, "unsupported format %d\n",
substream->runtime->format);
return -EINVAL;
}
@@ -913,57 +912,57 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
return 0;
}
- dac33_soft_power(codec, 0);
- dac33_soft_power(codec, 1);
+ dac33_soft_power(component, 0);
+ dac33_soft_power(component, 1);
- reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
- dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp);
+ reg_tmp = dac33_read_reg_cache(component, DAC33_INT_OSC_CTRL);
+ dac33_write(component, DAC33_INT_OSC_CTRL, reg_tmp);
/* Write registers 0x08 and 0x09 (MSB, LSB) */
- dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
+ dac33_write16(component, DAC33_INT_OSC_FREQ_RAT_A, oscset);
/* OSC calibration time */
- dac33_write(codec, DAC33_CALIB_TIME, 96);
+ dac33_write(component, DAC33_CALIB_TIME, 96);
/* adjustment treshold & step */
- dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
+ dac33_write(component, 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));
+ dac33_write(component, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4));
- pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ pwr_ctrl = dac33_read_reg_cache(component, DAC33_PWR_CTRL);
pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB;
- dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+ dac33_write(component, DAC33_PWR_CTRL, pwr_ctrl);
- dac33_oscwait(codec);
+ dac33_oscwait(component);
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); /* ??? */
+ dac33_write(component, DAC33_ASRC_CTRL_A, DAC33_SRCLKDIV(1));
+ dac33_write(component, DAC33_ASRC_CTRL_B, 1); /* ??? */
/* Write registers 0x34 and 0x35 (MSB, LSB) */
- dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
+ dac33_write16(component, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
/* Set interrupts to high active */
- dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
+ dac33_write(component, 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); /* ??? */
+ dac33_write(component, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
+ dac33_write(component, DAC33_ASRC_CTRL_B, 0); /* ??? */
}
/* Interrupt behaviour configuration */
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
- dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+ dac33_write(component, 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_write(component, DAC33_FIFO_IRQ_MODE_A,
DAC33_UTM(DAC33_FIFO_IRQ_MODE_LEVEL));
break;
default:
@@ -971,7 +970,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
break;
}
- aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ aictrl_b = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B);
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
@@ -1013,9 +1012,9 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
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);
+ dac33_write(component, DAC33_FIFO_CTRL_A, fifoctrl_a);
+ dac33_write(component, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ dac33_write(component, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
/*
* BCLK divide ratio
@@ -1027,17 +1026,17 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
* 255: 255
*/
if (dac33->fifo_mode)
- dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C,
+ dac33_write(component, 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);
+ dac33_write(component, DAC33_SER_AUDIOIF_CTRL_C, 32);
else
- dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 16);
+ dac33_write(component, DAC33_SER_AUDIOIF_CTRL_C, 16);
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
- dac33_write16(codec, DAC33_ATHR_MSB,
+ dac33_write16(component, DAC33_ATHR_MSB,
DAC33_THRREG(dac33->alarm_threshold));
break;
case DAC33_FIFO_MODE7:
@@ -1045,8 +1044,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
* 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_write16(component, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr));
+ dac33_write16(component, DAC33_LTHR_MSB,
DAC33_THRREG(DAC33_MODE7_MARGIN));
break;
default:
@@ -1059,9 +1058,9 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
}
static void dac33_calculate_times(struct snd_pcm_substream *substream,
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
unsigned int period_size = substream->runtime->period_size;
unsigned int rate = substream->runtime->rate;
unsigned int nsample_limit;
@@ -1084,7 +1083,7 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream,
*/
dac33->nsample = period_size *
((dac33->alarm_threshold / period_size) +
- (dac33->alarm_threshold % period_size ?
+ ((dac33->alarm_threshold % period_size) ?
1 : 0));
else if (period_size > nsample_limit)
dac33->nsample = nsample_limit;
@@ -1118,8 +1117,8 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (cmd) {
@@ -1128,7 +1127,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (dac33->fifo_mode) {
dac33->state = DAC33_PREFILL;
- queue_work(dac33->dac33_wq, &dac33->work);
+ schedule_work(&dac33->work);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -1136,7 +1135,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (dac33->fifo_mode) {
dac33->state = DAC33_FLUSH;
- queue_work(dac33->dac33_wq, &dac33->work);
+ schedule_work(&dac33->work);
}
break;
default:
@@ -1150,8 +1149,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
unsigned long long t0, t1, t_now;
unsigned int time_delta, uthr;
int samples_out, samples_in, samples;
@@ -1283,7 +1282,7 @@ static snd_pcm_sframes_t dac33_dai_delay(
}
break;
default:
- dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dev_warn(component->dev, "Unhandled FIFO mode: %d\n",
dac33->fifo_mode);
break;
}
@@ -1294,12 +1293,12 @@ out:
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
u8 ioc_reg, asrcb_reg;
- ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
- asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B);
+ ioc_reg = dac33_read_reg_cache(component, DAC33_INT_OSC_CTRL);
+ asrcb_reg = dac33_read_reg_cache(component, DAC33_ASRC_CTRL_B);
switch (clk_id) {
case TLV320DAC33_MCLK:
ioc_reg |= DAC33_REFSEL;
@@ -1310,13 +1309,13 @@ static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
asrcb_reg &= ~DAC33_SRCREFSEL;
break;
default:
- dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id);
+ dev_err(component->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);
+ dac33_write_reg_cache(component, DAC33_INT_OSC_CTRL, ioc_reg);
+ dac33_write_reg_cache(component, DAC33_ASRC_CTRL_B, asrcb_reg);
return 0;
}
@@ -1324,22 +1323,20 @@ static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
u8 aictrl_a, aictrl_b;
- aictrl_a = dac33_read_reg_cache(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_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_b = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Codec Slave */
+ case SND_SOC_DAIFMT_CBC_CFC:
if (dac33->fifo_mode) {
- dev_err(codec->dev, "FIFO mode requires master mode\n");
+ dev_err(component->dev, "FIFO mode requires provider mode\n");
return -EINVAL;
} else
aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
@@ -1365,37 +1362,35 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
aictrl_a |= DAC33_AFMT_LEFT_J;
break;
default:
- dev_err(codec->dev, "Unsupported format (%u)\n",
+ dev_err(component->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);
+ dac33_write_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ dac33_write_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
return 0;
}
-static int dac33_soc_probe(struct snd_soc_codec *codec)
+static int dac33_soc_probe(struct snd_soc_component *component)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
int ret = 0;
- codec->control_data = dac33->control_data;
- codec->hw_write = (hw_write_t) i2c_master_send;
- dac33->codec = codec;
+ dac33->component = component;
/* Read the tlv320dac33 ID registers */
- ret = dac33_hard_power(codec, 1);
+ ret = dac33_hard_power(component, 1);
if (ret != 0) {
- dev_err(codec->dev, "Failed to power up codec: %d\n", ret);
+ dev_err(component->dev, "Failed to power up component: %d\n", ret);
goto err_power;
}
- ret = dac33_read_id(codec);
- dac33_hard_power(codec, 0);
+ ret = dac33_read_id(component);
+ dac33_hard_power(component, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read chip ID: %d\n", ret);
+ dev_err(component->dev, "Failed to read chip ID: %d\n", ret);
ret = -ENODEV;
goto err_power;
}
@@ -1404,62 +1399,50 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
if (dac33->irq >= 0) {
ret = request_irq(dac33->irq, dac33_interrupt_handler,
IRQF_TRIGGER_RISING,
- codec->component.name, codec);
+ component->name, component);
if (ret < 0) {
- dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
+ dev_err(component->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,
+ snd_soc_add_component_controls(component, dac33_mode_snd_controls,
ARRAY_SIZE(dac33_mode_snd_controls));
err_power:
return ret;
}
-static int dac33_soc_remove(struct snd_soc_codec *codec)
+static void dac33_soc_remove(struct snd_soc_component *component)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component);
if (dac33->irq >= 0) {
- free_irq(dac33->irq, dac33->codec);
- destroy_workqueue(dac33->dac33_wq);
+ free_irq(dac33->irq, dac33->component);
+ flush_work(&dac33->work);
}
- 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),
+static const struct snd_soc_component_driver soc_component_dev_tlv320dac33 = {
+ .read = dac33_read_reg_cache,
+ .write = dac33_write_locked,
+ .set_bias_level = dac33_set_bias_level,
+ .probe = dac33_soc_probe,
+ .remove = dac33_soc_remove,
+ .controls = dac33_snd_controls,
+ .num_controls = ARRAY_SIZE(dac33_snd_controls),
+ .dapm_widgets = dac33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dac33_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \
@@ -1489,50 +1472,43 @@ static struct snd_soc_dai_driver dac33_dai = {
.ops = &dac33_dai_ops,
};
-static int dac33_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int dac33_i2c_probe(struct i2c_client *client)
{
- struct tlv320dac33_platform_data *pdata;
struct tlv320dac33_priv *dac33;
int ret, i;
- if (client->dev.platform_data == NULL) {
- dev_err(&client->dev, "Platform data not set\n");
- return -ENODEV;
- }
- pdata = client->dev.platform_data;
-
dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
GFP_KERNEL);
if (dac33 == NULL)
return -ENOMEM;
- dac33->control_data = client;
+ dac33->reg_cache = devm_kmemdup_array(&client->dev, dac33_reg, ARRAY_SIZE(dac33_reg),
+ sizeof(dac33_reg[0]), GFP_KERNEL);
+ if (!dac33->reg_cache)
+ return -ENOMEM;
+
+ dac33->i2c = client;
mutex_init(&dac33->mutex);
spin_lock_init(&dac33->lock);
i2c_set_clientdata(client, dac33);
- dac33->power_gpio = pdata->power_gpio;
- dac33->burst_bclkdiv = pdata->burst_bclkdiv;
- dac33->keep_bclk = pdata->keep_bclk;
- dac33->mode1_latency = pdata->mode1_latency;
+ if (!dac33->burst_bclkdiv)
+ dac33->burst_bclkdiv = 8;
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);
+ /* request optional reset GPIO */
+ dac33->reset_gpiod =
+ devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(dac33->reset_gpiod)) {
+ ret = PTR_ERR(dac33->reset_gpiod);
+ dev_err_probe(&client->dev, ret,
+ "Failed to get reset GPIO\n");
+ goto err;
}
for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
@@ -1543,34 +1519,26 @@ static int dac33_i2c_probe(struct i2c_client *client,
if (ret != 0) {
dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
- goto err_get;
+ goto err;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_tlv320dac33, &dac33_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_tlv320dac33, &dac33_dai, 1);
if (ret < 0)
- goto err_get;
+ goto err;
return ret;
-err_get:
- if (dac33->power_gpio >= 0)
- gpio_free(dac33->power_gpio);
-err_gpio:
+
+err:
return ret;
}
-static int dac33_i2c_remove(struct i2c_client *client)
+static void dac33_i2c_remove(struct i2c_client *client)
{
struct tlv320dac33_priv *dac33 = i2c_get_clientdata(client);
if (unlikely(dac33->chip_power))
- dac33_hard_power(dac33->codec, 0);
-
- if (dac33->power_gpio >= 0)
- gpio_free(dac33->power_gpio);
-
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ dac33_hard_power(dac33->component, 0);
}
static const struct i2c_device_id tlv320dac33_i2c_id[] = {
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
index ed69670747bf..ba2c2d89c72e 100644
--- a/sound/soc/codecs/tlv320dac33.h
+++ b/sound/soc/codecs/tlv320dac33.h
@@ -1,24 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 11d85c5c787a..38cc000891ea 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -1,37 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/errno.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.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"
@@ -40,219 +26,78 @@ enum tpa_model {
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 device *dev;
+ struct regmap *regmap;
struct regulator *supply;
- int power_gpio;
- u8 power_state:1;
+ struct gpio_desc *power_gpio;
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)
+static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
{
- 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;
- }
- }
+ int ret = 0, ret2;
- /* 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) {
+ if (enable) {
ret = regulator_enable(data->supply);
if (ret != 0) {
- dev_err(&tpa6130a2_client->dev,
+ dev_err(data->dev,
"Failed to enable supply: %d\n", ret);
- goto exit;
+ return ret;
}
/* 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;
+ gpiod_set_value(data->power_gpio, 1);
+
+ /* Sync registers */
+ regcache_cache_only(data->regmap, false);
+ ret = regcache_sync(data->regmap);
+ if (ret != 0) {
+ dev_err(data->dev,
+ "Failed to sync registers: %d\n", ret);
+ regcache_cache_only(data->regmap, true);
+ gpiod_set_value(data->power_gpio, 0);
+ ret2 = regulator_disable(data->supply);
+ if (ret2 != 0)
+ dev_err(data->dev,
+ "Failed to disable supply: %d\n", ret2);
+ return ret;
}
} else {
- /* set SWS */
- val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
- val |= TPA6130A2_SWS;
- tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ /* Powered off device does not retain registers. While device
+ * is off, any register updates (i.e. volume changes) should
+ * happen in cache only.
+ */
+ regcache_mark_dirty(data->regmap);
+ regcache_cache_only(data->regmap, true);
/* Power off */
- if (data->power_gpio >= 0)
- gpio_set_value(data->power_gpio, 0);
+ gpiod_set_value(data->power_gpio, 0);
ret = regulator_disable(data->supply);
if (ret != 0) {
- dev_err(&tpa6130a2_client->dev,
+ dev_err(data->dev,
"Failed to disable supply: %d\n", ret);
- goto exit;
+ return ret;
}
-
- 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)
+static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kctrl, int event)
{
- 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);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c);
- 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;
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Before widget power up: turn chip on, sync registers */
+ return tpa6130a2_power(data, true);
+ } else {
+ /* After widget power down: turn chip off */
+ return tpa6130a2_power(data, false);
}
-
- val_reg &= ~(mask << shift);
- val_reg |= val << shift;
- tpa6130a2_i2c_write(reg, val_reg);
-
- mutex_unlock(&data->mutex);
-
- return 1;
}
/*
@@ -273,9 +118,8 @@ static const DECLARE_TLV_DB_RANGE(tpa6130_tlv,
);
static const struct snd_kcontrol_new tpa6130a2_controls[] = {
- SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+ SOC_SINGLE_TLV("Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
- tpa6130a2_get_volsw, tpa6130a2_put_volsw,
tpa6130_tlv),
};
@@ -286,94 +130,87 @@ static const DECLARE_TLV_DB_RANGE(tpa6140_tlv,
);
static const struct snd_kcontrol_new tpa6140a2_controls[] = {
- SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
+ SOC_SINGLE_TLV("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)
+static int tpa6130a2_component_probe(struct snd_soc_component *component)
{
- u8 val;
+ struct tpa6130a2_data *data = snd_soc_component_get_drvdata(component);
- 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);
- }
+ if (data->id == TPA6140A2)
+ return snd_soc_add_component_controls(component,
+ tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls));
+ else
+ return snd_soc_add_component_controls(component,
+ tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls));
}
-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);
- }
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LEFTIN"),
+ SND_SOC_DAPM_INPUT("RIGHTIN"),
+ SND_SOC_DAPM_OUTPUT("HPLEFT"),
+ SND_SOC_DAPM_OUTPUT("HPRIGHT"),
+
+ SND_SOC_DAPM_PGA("Left Mute", TPA6130A2_REG_VOL_MUTE,
+ TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Mute", TPA6130A2_REG_VOL_MUTE,
+ TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left PGA", TPA6130A2_REG_CONTROL,
+ TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", TPA6130A2_REG_CONTROL,
+ TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Power", TPA6130A2_REG_CONTROL,
+ TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
- return ret;
-}
-EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable);
+static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = {
+ { "Left PGA", NULL, "LEFTIN" },
+ { "Right PGA", NULL, "RIGHTIN" },
-int tpa6130a2_add_controls(struct snd_soc_codec *codec)
-{
- struct tpa6130a2_data *data;
+ { "Left Mute", NULL, "Left PGA" },
+ { "Right Mute", NULL, "Right PGA" },
- if (tpa6130a2_client == NULL)
- return -ENODEV;
+ { "HPLEFT", NULL, "Left Mute" },
+ { "HPRIGHT", NULL, "Right Mute" },
- data = i2c_get_clientdata(tpa6130a2_client);
+ { "Left PGA", NULL, "Power" },
+ { "Right PGA", NULL, "Power" },
+};
- 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 const struct snd_soc_component_driver tpa6130a2_component_driver = {
+ .name = "tpa6130a2",
+ .probe = tpa6130a2_component_probe,
+ .dapm_widgets = tpa6130a2_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets),
+ .dapm_routes = tpa6130a2_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes),
+};
+
+static const struct reg_default tpa6130a2_reg_defaults[] = {
+ { TPA6130A2_REG_CONTROL, TPA6130A2_SWS },
+ { TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L },
+};
-static int tpa6130a2_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct regmap_config tpa6130a2_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPA6130A2_REG_VERSION,
+ .reg_defaults = tpa6130a2_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tpa6130a2_probe(struct i2c_client *client)
{
struct device *dev;
struct tpa6130a2_data *data;
- struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
struct device_node *np = client->dev.of_node;
const char *regulator;
+ unsigned int version;
int ret;
dev = &client->dev;
@@ -382,44 +219,34 @@ static int tpa6130a2_probe(struct i2c_client *client,
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);
+ data->dev = dev;
+
+ data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ if (np) {
+ data->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
+ if (IS_ERR(data->power_gpio)) {
+ return dev_err_probe(dev, PTR_ERR(data->power_gpio),
+ "Failed to request power GPIO\n");
+ }
+ gpiod_set_consumer_name(data->power_gpio, "tpa6130a2 enable");
} 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;
+ i2c_set_clientdata(client, 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);
- }
+ data->id = (uintptr_t)i2c_get_match_data(client);
switch (data->id) {
default:
dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
data->id);
+ fallthrough;
case TPA6130A2:
regulator = "Vdd";
break;
@@ -432,48 +259,29 @@ static int tpa6130a2_probe(struct i2c_client *client,
if (IS_ERR(data->supply)) {
ret = PTR_ERR(data->supply);
dev_err(dev, "Failed to request supply: %d\n", ret);
- goto err_gpio;
+ return ret;
}
- ret = tpa6130a2_power(1);
+ ret = tpa6130a2_power(data, true);
if (ret != 0)
- goto err_gpio;
+ return ret;
/* 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);
+ regmap_read(data->regmap, TPA6130A2_REG_VERSION, &version);
+ version &= TPA6130A2_VERSION_MASK;
+ if ((version != 1) && (version != 2))
+ dev_warn(dev, "UNTESTED version detected (%d)\n", version);
/* Disable the chip */
- ret = tpa6130a2_power(0);
+ ret = tpa6130a2_power(data, false);
if (ret != 0)
- goto err_gpio;
+ return ret;
- return 0;
-
-err_gpio:
- tpa6130a2_client = NULL;
-
- return ret;
+ return devm_snd_soc_register_component(&client->dev,
+ &tpa6130a2_component_driver, NULL, 0);
}
-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", },
@@ -489,8 +297,6 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
.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);
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
index 417444020ba6..bfa1fc11a18f 100644
--- a/sound/soc/codecs/tpa6130a2.h
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -1,24 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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__
@@ -30,19 +16,20 @@
#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_SWS_SHIFT 0
+#define TPA6130A2_SWS (0x01 << TPA6130A2_SWS_SHIFT)
#define TPA6130A2_TERMAL (0x01 << 1)
#define TPA6130A2_MODE(x) (x << 4)
#define TPA6130A2_MODE_STEREO (0x00)
#define TPA6130A2_MODE_DUAL_MONO (0x01)
#define TPA6130A2_MODE_BRIDGE (0x02)
#define TPA6130A2_MODE_MASK (0x03)
-#define TPA6130A2_HP_EN_R (0x01 << 6)
-#define TPA6130A2_HP_EN_L (0x01 << 7)
+#define TPA6130A2_HP_EN_R_SHIFT 6
+#define TPA6130A2_HP_EN_R (0x01 << TPA6130A2_HP_EN_R_SHIFT)
+#define TPA6130A2_HP_EN_L_SHIFT 7
+#define TPA6130A2_HP_EN_L (0x01 << TPA6130A2_HP_EN_L_SHIFT)
/* TPA6130A2_REG_VOL_MUTE (0x02) */
#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0)
@@ -56,7 +43,4 @@
/* 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/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 43568435c208..5ce0db9326fd 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -13,8 +10,8 @@
#include <linux/init.h>
#include <linux/input.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/regmap.h>
+#include <linux/acpi.h>
#include <sound/core.h>
#include <sound/jack.h>
@@ -80,12 +77,20 @@ static const int ts3a227e_buttons[] = {
#define ADC_COMPLETE_INT_DISABLE 0x04
#define INTB_DISABLE 0x08
+/* TS3A227E_REG_SETTING_1 0x4 */
+#define DEBOUNCE_INSERTION_SETTING_SFT (0)
+#define DEBOUNCE_INSERTION_SETTING_MASK (0x7 << DEBOUNCE_PRESS_SETTING_SFT)
+
/* TS3A227E_REG_SETTING_2 0x05 */
#define KP_ENABLE 0x04
/* TS3A227E_REG_SETTING_3 0x06 */
-#define MICBIAS_SETTING_SFT (3)
+#define MICBIAS_SETTING_SFT 3
#define MICBIAS_SETTING_MASK (0x7 << MICBIAS_SETTING_SFT)
+#define DEBOUNCE_RELEASE_SETTING_SFT 2
+#define DEBOUNCE_RELEASE_SETTING_MASK (0x1 << DEBOUNCE_RELEASE_SETTING_SFT)
+#define DEBOUNCE_PRESS_SETTING_SFT 0
+#define DEBOUNCE_PRESS_SETTING_MASK (0x3 << DEBOUNCE_PRESS_SETTING_SFT)
/* TS3A227E_REG_ACCESSORY_STATUS 0x0b */
#define TYPE_3_POLE 0x01
@@ -138,7 +143,7 @@ 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_SETTING_1 ... TS3A227E_REG_SETTING_2:
case TS3A227E_REG_SWITCH_STATUS_1 ... TS3A227E_REG_ADC_OUTPUT:
return true;
default:
@@ -240,7 +245,7 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
{
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_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
@@ -252,7 +257,25 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
}
EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect);
-static struct snd_soc_component_driver ts3a227e_soc_driver;
+static int ts3a227e_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack == NULL)
+ return -EINVAL;
+
+ return ts3a227e_enable_jack_detect(component, jack);
+}
+
+static int ts3a227e_get_jack_type(struct snd_soc_component *component)
+{
+ return SND_JACK_HEADSET;
+}
+
+static const struct snd_soc_component_driver ts3a227e_soc_driver = {
+ .name = "ti,ts3a227e",
+ .set_jack = ts3a227e_set_jack,
+ .get_jack_type = ts3a227e_get_jack_type,
+};
static const struct regmap_config ts3a227e_regmap_config = {
.val_bits = 8,
@@ -271,21 +294,61 @@ static const struct regmap_config ts3a227e_regmap_config = {
static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e,
struct device *dev)
{
- u32 micbias;
+ u32 value;
+ u32 value_ms;
+ u32 setting3_value = 0;
+ u32 setting3_mask = 0;
int err;
- err = device_property_read_u32(dev, "ti,micbias", &micbias);
+ err = device_property_read_u32(dev, "ti,micbias", &value);
+ if (!err) {
+ setting3_mask = MICBIAS_SETTING_MASK;
+ setting3_value = (value << MICBIAS_SETTING_SFT) &
+ MICBIAS_SETTING_MASK;
+ }
+
+ err = device_property_read_u32(dev, "ti,debounce-release-ms",
+ &value_ms);
if (!err) {
+ value = (value_ms > 10);
+ setting3_mask |= DEBOUNCE_RELEASE_SETTING_MASK;
+ setting3_value |= (value << DEBOUNCE_RELEASE_SETTING_SFT) &
+ DEBOUNCE_RELEASE_SETTING_MASK;
+ }
+
+ err = device_property_read_u32(dev, "ti,debounce-press-ms", &value_ms);
+ if (!err) {
+ value = (value_ms + 20) / 40;
+ if (value > 3)
+ value = 3;
+ setting3_mask |= DEBOUNCE_PRESS_SETTING_MASK;
+ setting3_value |= (value << DEBOUNCE_PRESS_SETTING_SFT) &
+ DEBOUNCE_PRESS_SETTING_MASK;
+ }
+
+ if (setting3_mask)
regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3,
- MICBIAS_SETTING_MASK,
- (micbias & 0x07) << MICBIAS_SETTING_SFT);
+ setting3_mask, setting3_value);
+
+ err = device_property_read_u32(dev, "ti,debounce-insertion-ms",
+ &value_ms);
+ if (!err) {
+ if (value_ms < 165)
+ value = (value_ms + 15) / 30;
+ else if (value_ms < 1500)
+ value = 6;
+ else
+ value = 7;
+ regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_1,
+ DEBOUNCE_INSERTION_SETTING_MASK,
+ (value << DEBOUNCE_INSERTION_SETTING_SFT) &
+ DEBOUNCE_INSERTION_SETTING_MASK);
}
return 0;
}
-static int ts3a227e_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ts3a227e_i2c_probe(struct i2c_client *i2c)
{
struct ts3a227e *ts3a227e;
struct device *dev = &i2c->dev;
@@ -336,7 +399,6 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c,
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int ts3a227e_suspend(struct device *dev)
{
struct ts3a227e *ts3a227e = dev_get_drvdata(dev);
@@ -356,29 +418,39 @@ static int ts3a227e_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops ts3a227e_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume)
+ SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume)
};
static const struct i2c_device_id ts3a227e_i2c_ids[] = {
- { "ts3a227e", 0 },
+ { "ts3a227e" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids);
+#ifdef CONFIG_OF
static const struct of_device_id ts3a227e_of_match[] = {
{ .compatible = "ti,ts3a227e", },
{ }
};
MODULE_DEVICE_TABLE(of, ts3a227e_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id ts3a227e_acpi_match[] = {
+ { "104C227E", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, ts3a227e_acpi_match);
+#endif
static struct i2c_driver ts3a227e_driver = {
.driver = {
.name = "ts3a227e",
- .pm = &ts3a227e_pm,
+ .pm = pm_ptr(&ts3a227e_pm),
.of_match_table = of_match_ptr(ts3a227e_of_match),
+ .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match),
},
.probe = ts3a227e_i2c_probe,
.id_table = ts3a227e_i2c_ids,
diff --git a/sound/soc/codecs/ts3a227e.h b/sound/soc/codecs/ts3a227e.h
index e2acf9c5bebe..3565e5931ca6 100644
--- a/sound/soc/codecs/ts3a227e.h
+++ b/sound/soc/codecs/ts3a227e.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c
new file mode 100644
index 000000000000..7390ab250ebb
--- /dev/null
+++ b/sound/soc/codecs/tscs42xx.c
@@ -0,0 +1,1512 @@
+// SPDX-License-Identifier: GPL-2.0
+// tscs42xx.c -- TSCS42xx ALSA SoC Audio driver
+// Copyright 2017 Tempo Semiconductor, Inc.
+// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "tscs42xx.h"
+
+#define COEFF_SIZE 3
+#define BIQUAD_COEFF_COUNT 5
+#define BIQUAD_SIZE (COEFF_SIZE * BIQUAD_COEFF_COUNT)
+
+#define COEFF_RAM_MAX_ADDR 0xcd
+#define COEFF_RAM_COEFF_COUNT (COEFF_RAM_MAX_ADDR + 1)
+#define COEFF_RAM_SIZE (COEFF_SIZE * COEFF_RAM_COEFF_COUNT)
+
+struct tscs42xx {
+
+ int bclk_ratio;
+ int samplerate;
+ struct mutex audio_params_lock;
+
+ u8 coeff_ram[COEFF_RAM_SIZE];
+ bool coeff_ram_synced;
+ struct mutex coeff_ram_lock;
+
+ struct mutex pll_lock;
+
+ struct regmap *regmap;
+
+ struct clk *sysclk;
+ int sysclk_src_id;
+};
+
+struct coeff_ram_ctl {
+ unsigned int addr;
+ struct soc_bytes_ext bytes_ext;
+};
+
+static bool tscs42xx_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case R_DACCRWRL:
+ case R_DACCRWRM:
+ case R_DACCRWRH:
+ case R_DACCRRDL:
+ case R_DACCRRDM:
+ case R_DACCRRDH:
+ case R_DACCRSTAT:
+ case R_DACCRADDR:
+ case R_PLLCTL0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tscs42xx_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case R_DACCRWRL:
+ case R_DACCRWRM:
+ case R_DACCRWRH:
+ case R_DACCRRDL:
+ case R_DACCRRDM:
+ case R_DACCRRDH:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tscs42xx_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .volatile_reg = tscs42xx_volatile,
+ .precious_reg = tscs42xx_precious,
+ .max_register = R_DACMBCREL3H,
+
+ .cache_type = REGCACHE_RBTREE,
+ .can_multi_write = true,
+};
+
+#define MAX_PLL_LOCK_20MS_WAITS 1
+static bool plls_locked(struct snd_soc_component *component)
+{
+ int ret;
+ int count = MAX_PLL_LOCK_20MS_WAITS;
+
+ do {
+ ret = snd_soc_component_read(component, R_PLLCTL0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to read PLL lock status (%d)\n", ret);
+ return false;
+ } else if (ret > 0) {
+ return true;
+ }
+ msleep(20);
+ } while (count--);
+
+ return false;
+}
+
+static int sample_rate_to_pll_freq_out(int sample_rate)
+{
+ switch (sample_rate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ return 112896000;
+ case 8000:
+ case 16000:
+ case 32000:
+ case 48000:
+ case 96000:
+ return 122880000;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define DACCRSTAT_MAX_TRYS 10
+static int write_coeff_ram(struct snd_soc_component *component, u8 *coeff_ram,
+ unsigned int addr, unsigned int coeff_cnt)
+{
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ int cnt;
+ int trys;
+ int ret;
+
+ for (cnt = 0; cnt < coeff_cnt; cnt++, addr++) {
+
+ for (trys = 0; trys < DACCRSTAT_MAX_TRYS; trys++) {
+ ret = snd_soc_component_read(component, R_DACCRSTAT);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to read stat (%d)\n", ret);
+ return ret;
+ }
+ if (!ret)
+ break;
+ }
+
+ if (trys == DACCRSTAT_MAX_TRYS) {
+ ret = -EIO;
+ dev_err(component->dev,
+ "dac coefficient write error (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(tscs42xx->regmap, R_DACCRADDR, addr);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to write dac ram address (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(tscs42xx->regmap, R_DACCRWRL,
+ &coeff_ram[addr * COEFF_SIZE],
+ COEFF_SIZE);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to write dac ram (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int power_up_audio_plls(struct snd_soc_component *component)
+{
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ int freq_out;
+ int ret;
+ unsigned int mask;
+ unsigned int val;
+
+ freq_out = sample_rate_to_pll_freq_out(tscs42xx->samplerate);
+ switch (freq_out) {
+ case 122880000: /* 48k */
+ mask = RM_PLLCTL1C_PDB_PLL1;
+ val = RV_PLLCTL1C_PDB_PLL1_ENABLE;
+ break;
+ case 112896000: /* 44.1k */
+ mask = RM_PLLCTL1C_PDB_PLL2;
+ val = RV_PLLCTL1C_PDB_PLL2_ENABLE;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev,
+ "Unrecognized PLL output freq (%d)\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&tscs42xx->pll_lock);
+
+ ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret);
+ goto exit;
+ }
+
+ if (!plls_locked(component)) {
+ dev_err(component->dev, "Failed to lock plls\n");
+ ret = -ENOMSG;
+ goto exit;
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&tscs42xx->pll_lock);
+
+ return ret;
+}
+
+static int power_down_audio_plls(struct snd_soc_component *component)
+{
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&tscs42xx->pll_lock);
+
+ ret = snd_soc_component_update_bits(component, R_PLLCTL1C,
+ RM_PLLCTL1C_PDB_PLL1,
+ RV_PLLCTL1C_PDB_PLL1_DISABLE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret);
+ goto exit;
+ }
+ ret = snd_soc_component_update_bits(component, R_PLLCTL1C,
+ RM_PLLCTL1C_PDB_PLL2,
+ RV_PLLCTL1C_PDB_PLL2_DISABLE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret);
+ goto exit;
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&tscs42xx->pll_lock);
+
+ return ret;
+}
+
+static int coeff_ram_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ struct coeff_ram_ctl *ctl =
+ (struct coeff_ram_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ mutex_lock(&tscs42xx->coeff_ram_lock);
+
+ memcpy(ucontrol->value.bytes.data,
+ &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max);
+
+ mutex_unlock(&tscs42xx->coeff_ram_lock);
+
+ return 0;
+}
+
+static int coeff_ram_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ struct coeff_ram_ctl *ctl =
+ (struct coeff_ram_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ unsigned int coeff_cnt = params->max / COEFF_SIZE;
+ int ret;
+
+ mutex_lock(&tscs42xx->coeff_ram_lock);
+
+ tscs42xx->coeff_ram_synced = false;
+
+ memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE],
+ ucontrol->value.bytes.data, params->max);
+
+ mutex_lock(&tscs42xx->pll_lock);
+
+ if (plls_locked(component)) {
+ ret = write_coeff_ram(component, tscs42xx->coeff_ram,
+ ctl->addr, coeff_cnt);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to flush coeff ram cache (%d)\n", ret);
+ goto exit;
+ }
+ tscs42xx->coeff_ram_synced = true;
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&tscs42xx->pll_lock);
+
+ mutex_unlock(&tscs42xx->coeff_ram_lock);
+
+ return ret;
+}
+
+/* Input L Capture Route */
+static char const * const input_select_text[] = {
+ "Line 1", "Line 2", "Line 3", "D2S"
+};
+
+static const struct soc_enum left_input_select_enum =
+SOC_ENUM_SINGLE(R_INSELL, FB_INSELL, ARRAY_SIZE(input_select_text),
+ input_select_text);
+
+static const struct snd_kcontrol_new left_input_select =
+SOC_DAPM_ENUM("LEFT_INPUT_SELECT_ENUM", left_input_select_enum);
+
+/* Input R Capture Route */
+static const struct soc_enum right_input_select_enum =
+SOC_ENUM_SINGLE(R_INSELR, FB_INSELR, ARRAY_SIZE(input_select_text),
+ input_select_text);
+
+static const struct snd_kcontrol_new right_input_select =
+SOC_DAPM_ENUM("RIGHT_INPUT_SELECT_ENUM", right_input_select_enum);
+
+/* Input Channel Mapping */
+static char const * const ch_map_select_text[] = {
+ "Normal", "Left to Right", "Right to Left", "Swap"
+};
+
+static const struct soc_enum ch_map_select_enum =
+SOC_ENUM_SINGLE(R_AIC2, FB_AIC2_ADCDSEL, ARRAY_SIZE(ch_map_select_text),
+ ch_map_select_text);
+
+static int dapm_vref_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ msleep(20);
+ return 0;
+}
+
+static int dapm_micb_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ msleep(20);
+ return 0;
+}
+
+static int pll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ int ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = power_up_audio_plls(component);
+ else
+ ret = power_down_audio_plls(component);
+
+ return ret;
+}
+
+static int dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&tscs42xx->coeff_ram_lock);
+
+ if (!tscs42xx->coeff_ram_synced) {
+ ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00,
+ COEFF_RAM_COEFF_COUNT);
+ if (ret < 0)
+ goto exit;
+ tscs42xx->coeff_ram_synced = true;
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&tscs42xx->coeff_ram_lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = {
+ /* Vref */
+ SND_SOC_DAPM_SUPPLY_S("Vref", 1, R_PWRM2, FB_PWRM2_VREF, 0,
+ dapm_vref_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+
+ /* PLL */
+ SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Headphone */
+ SND_SOC_DAPM_DAC_E("DAC L", "HiFi Playback", R_PWRM2, FB_PWRM2_HPL, 0,
+ dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_DAC_E("DAC R", "HiFi Playback", R_PWRM2, FB_PWRM2_HPR, 0,
+ dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("Headphone L"),
+ SND_SOC_DAPM_OUTPUT("Headphone R"),
+
+ /* Speaker */
+ SND_SOC_DAPM_DAC_E("ClassD L", "HiFi Playback",
+ R_PWRM2, FB_PWRM2_SPKL, 0,
+ dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_DAC_E("ClassD R", "HiFi Playback",
+ R_PWRM2, FB_PWRM2_SPKR, 0,
+ dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("Speaker L"),
+ SND_SOC_DAPM_OUTPUT("Speaker R"),
+
+ /* Capture */
+ SND_SOC_DAPM_PGA("Analog In PGA L", R_PWRM1, FB_PWRM1_PGAL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Analog In PGA R", R_PWRM1, FB_PWRM1_PGAR, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Analog Boost L", R_PWRM1, FB_PWRM1_BSTL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Analog Boost R", R_PWRM1, FB_PWRM1_BSTR, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC Mute", R_CNVRTR0, FB_CNVRTR0_HPOR, true, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC L", "HiFi Capture", R_PWRM1, FB_PWRM1_ADCL, 0),
+ SND_SOC_DAPM_ADC("ADC R", "HiFi Capture", R_PWRM1, FB_PWRM1_ADCR, 0),
+
+ /* Capture Input */
+ SND_SOC_DAPM_MUX("Input L Capture Route", R_PWRM2,
+ FB_PWRM2_INSELL, 0, &left_input_select),
+ SND_SOC_DAPM_MUX("Input R Capture Route", R_PWRM2,
+ FB_PWRM2_INSELR, 0, &right_input_select),
+
+ /* Digital Mic */
+ SND_SOC_DAPM_SUPPLY_S("Digital Mic Enable", 2, R_DMICCTL,
+ FB_DMICCTL_DMICEN, 0, NULL,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+
+ /* Analog Mic */
+ SND_SOC_DAPM_SUPPLY_S("Mic Bias", 2, R_PWRM1, FB_PWRM1_MICB,
+ 0, dapm_micb_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+
+ /* Line In */
+ SND_SOC_DAPM_INPUT("Line In 1 L"),
+ SND_SOC_DAPM_INPUT("Line In 1 R"),
+ SND_SOC_DAPM_INPUT("Line In 2 L"),
+ SND_SOC_DAPM_INPUT("Line In 2 R"),
+ SND_SOC_DAPM_INPUT("Line In 3 L"),
+ SND_SOC_DAPM_INPUT("Line In 3 R"),
+};
+
+static const struct snd_soc_dapm_route tscs42xx_intercon[] = {
+ {"DAC L", NULL, "PLL"},
+ {"DAC R", NULL, "PLL"},
+ {"DAC L", NULL, "Vref"},
+ {"DAC R", NULL, "Vref"},
+ {"Headphone L", NULL, "DAC L"},
+ {"Headphone R", NULL, "DAC R"},
+
+ {"ClassD L", NULL, "PLL"},
+ {"ClassD R", NULL, "PLL"},
+ {"ClassD L", NULL, "Vref"},
+ {"ClassD R", NULL, "Vref"},
+ {"Speaker L", NULL, "ClassD L"},
+ {"Speaker R", NULL, "ClassD R"},
+
+ {"Input L Capture Route", NULL, "Vref"},
+ {"Input R Capture Route", NULL, "Vref"},
+
+ {"Mic Bias", NULL, "Vref"},
+
+ {"Input L Capture Route", "Line 1", "Line In 1 L"},
+ {"Input R Capture Route", "Line 1", "Line In 1 R"},
+ {"Input L Capture Route", "Line 2", "Line In 2 L"},
+ {"Input R Capture Route", "Line 2", "Line In 2 R"},
+ {"Input L Capture Route", "Line 3", "Line In 3 L"},
+ {"Input R Capture Route", "Line 3", "Line In 3 R"},
+
+ {"Analog In PGA L", NULL, "Input L Capture Route"},
+ {"Analog In PGA R", NULL, "Input R Capture Route"},
+ {"Analog Boost L", NULL, "Analog In PGA L"},
+ {"Analog Boost R", NULL, "Analog In PGA R"},
+ {"ADC Mute", NULL, "Analog Boost L"},
+ {"ADC Mute", NULL, "Analog Boost R"},
+ {"ADC L", NULL, "PLL"},
+ {"ADC R", NULL, "PLL"},
+ {"ADC L", NULL, "ADC Mute"},
+ {"ADC R", NULL, "ADC Mute"},
+};
+
+/************
+ * CONTROLS *
+ ************/
+
+static char const * const eq_band_enable_text[] = {
+ "Prescale only",
+ "Band1",
+ "Band1:2",
+ "Band1:3",
+ "Band1:4",
+ "Band1:5",
+ "Band1:6",
+};
+
+static char const * const level_detection_text[] = {
+ "Average",
+ "Peak",
+};
+
+static char const * const level_detection_window_text[] = {
+ "512 Samples",
+ "64 Samples",
+};
+
+static char const * const compressor_ratio_text[] = {
+ "Reserved", "1.5:1", "2:1", "3:1", "4:1", "5:1", "6:1",
+ "7:1", "8:1", "9:1", "10:1", "11:1", "12:1", "13:1", "14:1",
+ "15:1", "16:1", "17:1", "18:1", "19:1", "20:1",
+};
+
+static DECLARE_TLV_DB_SCALE(hpvol_scale, -8850, 75, 0);
+static DECLARE_TLV_DB_SCALE(spkvol_scale, -7725, 75, 0);
+static DECLARE_TLV_DB_SCALE(dacvol_scale, -9563, 38, 0);
+static DECLARE_TLV_DB_SCALE(adcvol_scale, -7125, 38, 0);
+static DECLARE_TLV_DB_SCALE(invol_scale, -1725, 75, 0);
+static DECLARE_TLV_DB_SCALE(mic_boost_scale, 0, 1000, 0);
+static DECLARE_TLV_DB_MINMAX(mugain_scale, 0, 4650);
+static DECLARE_TLV_DB_MINMAX(compth_scale, -9562, 0);
+
+static const struct soc_enum eq1_band_enable_enum =
+ SOC_ENUM_SINGLE(R_CONFIG1, FB_CONFIG1_EQ1_BE,
+ ARRAY_SIZE(eq_band_enable_text), eq_band_enable_text);
+
+static const struct soc_enum eq2_band_enable_enum =
+ SOC_ENUM_SINGLE(R_CONFIG1, FB_CONFIG1_EQ2_BE,
+ ARRAY_SIZE(eq_band_enable_text), eq_band_enable_text);
+
+static const struct soc_enum cle_level_detection_enum =
+ SOC_ENUM_SINGLE(R_CLECTL, FB_CLECTL_LVL_MODE,
+ ARRAY_SIZE(level_detection_text),
+ level_detection_text);
+
+static const struct soc_enum cle_level_detection_window_enum =
+ SOC_ENUM_SINGLE(R_CLECTL, FB_CLECTL_WINDOWSEL,
+ ARRAY_SIZE(level_detection_window_text),
+ level_detection_window_text);
+
+static const struct soc_enum mbc_level_detection_enums[] = {
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE1,
+ ARRAY_SIZE(level_detection_text),
+ level_detection_text),
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE2,
+ ARRAY_SIZE(level_detection_text),
+ level_detection_text),
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE3,
+ ARRAY_SIZE(level_detection_text),
+ level_detection_text),
+};
+
+static const struct soc_enum mbc_level_detection_window_enums[] = {
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL1,
+ ARRAY_SIZE(level_detection_window_text),
+ level_detection_window_text),
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL2,
+ ARRAY_SIZE(level_detection_window_text),
+ level_detection_window_text),
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL3,
+ ARRAY_SIZE(level_detection_window_text),
+ level_detection_window_text),
+};
+
+static const struct soc_enum compressor_ratio_enum =
+ SOC_ENUM_SINGLE(R_CMPRAT, FB_CMPRAT,
+ ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text);
+
+static const struct soc_enum dac_mbc1_compressor_ratio_enum =
+ SOC_ENUM_SINGLE(R_DACMBCRAT1, FB_DACMBCRAT1_RATIO,
+ ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text);
+
+static const struct soc_enum dac_mbc2_compressor_ratio_enum =
+ SOC_ENUM_SINGLE(R_DACMBCRAT2, FB_DACMBCRAT2_RATIO,
+ ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text);
+
+static const struct soc_enum dac_mbc3_compressor_ratio_enum =
+ SOC_ENUM_SINGLE(R_DACMBCRAT3, FB_DACMBCRAT3_RATIO,
+ ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text);
+
+static int bytes_info_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct coeff_ram_ctl *ctl =
+ (struct coeff_ram_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+#define COEFF_RAM_CTL(xname, xcount, xaddr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = bytes_info_ext, \
+ .get = coeff_ram_get, .put = coeff_ram_put, \
+ .private_value = (unsigned long)&(struct coeff_ram_ctl) { \
+ .addr = xaddr, \
+ .bytes_ext = {.max = xcount, }, \
+ } \
+}
+
+static const struct snd_kcontrol_new tscs42xx_snd_controls[] = {
+ /* Volumes */
+ SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR,
+ FB_HPVOLL, 0x7F, 0, hpvol_scale),
+ SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR,
+ FB_SPKVOLL, 0x7F, 0, spkvol_scale),
+ SOC_DOUBLE_R_TLV("Master Volume", R_DACVOLL, R_DACVOLR,
+ FB_DACVOLL, 0xFF, 0, dacvol_scale),
+ SOC_DOUBLE_R_TLV("PCM Volume", R_ADCVOLL, R_ADCVOLR,
+ FB_ADCVOLL, 0xFF, 0, adcvol_scale),
+ SOC_DOUBLE_R_TLV("Input Volume", R_INVOLL, R_INVOLR,
+ FB_INVOLL, 0x3F, 0, invol_scale),
+
+ /* INSEL */
+ SOC_DOUBLE_R_TLV("Mic Boost Volume", R_INSELL, R_INSELR,
+ FB_INSELL_MICBSTL, FV_INSELL_MICBSTL_30DB,
+ 0, mic_boost_scale),
+
+ /* Input Channel Map */
+ SOC_ENUM("Input Channel Map", ch_map_select_enum),
+
+ /* Mic Bias */
+ SOC_SINGLE("Mic Bias Boost Switch", 0x71, 0x07, 1, 0),
+
+ /* Headphone Auto Switching */
+ SOC_SINGLE("Headphone Auto Switching Switch",
+ R_CTL, FB_CTL_HPSWEN, 1, 0),
+ SOC_SINGLE("Headphone Detect Polarity Toggle Switch",
+ R_CTL, FB_CTL_HPSWPOL, 1, 0),
+
+ /* Coefficient Ram */
+ COEFF_RAM_CTL("Cascade1L BiQuad1", BIQUAD_SIZE, 0x00),
+ COEFF_RAM_CTL("Cascade1L BiQuad2", BIQUAD_SIZE, 0x05),
+ COEFF_RAM_CTL("Cascade1L BiQuad3", BIQUAD_SIZE, 0x0a),
+ COEFF_RAM_CTL("Cascade1L BiQuad4", BIQUAD_SIZE, 0x0f),
+ COEFF_RAM_CTL("Cascade1L BiQuad5", BIQUAD_SIZE, 0x14),
+ COEFF_RAM_CTL("Cascade1L BiQuad6", BIQUAD_SIZE, 0x19),
+
+ COEFF_RAM_CTL("Cascade1R BiQuad1", BIQUAD_SIZE, 0x20),
+ COEFF_RAM_CTL("Cascade1R BiQuad2", BIQUAD_SIZE, 0x25),
+ COEFF_RAM_CTL("Cascade1R BiQuad3", BIQUAD_SIZE, 0x2a),
+ COEFF_RAM_CTL("Cascade1R BiQuad4", BIQUAD_SIZE, 0x2f),
+ COEFF_RAM_CTL("Cascade1R BiQuad5", BIQUAD_SIZE, 0x34),
+ COEFF_RAM_CTL("Cascade1R BiQuad6", BIQUAD_SIZE, 0x39),
+
+ COEFF_RAM_CTL("Cascade1L Prescale", COEFF_SIZE, 0x1f),
+ COEFF_RAM_CTL("Cascade1R Prescale", COEFF_SIZE, 0x3f),
+
+ COEFF_RAM_CTL("Cascade2L BiQuad1", BIQUAD_SIZE, 0x40),
+ COEFF_RAM_CTL("Cascade2L BiQuad2", BIQUAD_SIZE, 0x45),
+ COEFF_RAM_CTL("Cascade2L BiQuad3", BIQUAD_SIZE, 0x4a),
+ COEFF_RAM_CTL("Cascade2L BiQuad4", BIQUAD_SIZE, 0x4f),
+ COEFF_RAM_CTL("Cascade2L BiQuad5", BIQUAD_SIZE, 0x54),
+ COEFF_RAM_CTL("Cascade2L BiQuad6", BIQUAD_SIZE, 0x59),
+
+ COEFF_RAM_CTL("Cascade2R BiQuad1", BIQUAD_SIZE, 0x60),
+ COEFF_RAM_CTL("Cascade2R BiQuad2", BIQUAD_SIZE, 0x65),
+ COEFF_RAM_CTL("Cascade2R BiQuad3", BIQUAD_SIZE, 0x6a),
+ COEFF_RAM_CTL("Cascade2R BiQuad4", BIQUAD_SIZE, 0x6f),
+ COEFF_RAM_CTL("Cascade2R BiQuad5", BIQUAD_SIZE, 0x74),
+ COEFF_RAM_CTL("Cascade2R BiQuad6", BIQUAD_SIZE, 0x79),
+
+ COEFF_RAM_CTL("Cascade2L Prescale", COEFF_SIZE, 0x5f),
+ COEFF_RAM_CTL("Cascade2R Prescale", COEFF_SIZE, 0x7f),
+
+ COEFF_RAM_CTL("Bass Extraction BiQuad1", BIQUAD_SIZE, 0x80),
+ COEFF_RAM_CTL("Bass Extraction BiQuad2", BIQUAD_SIZE, 0x85),
+
+ COEFF_RAM_CTL("Bass Non Linear Function 1", COEFF_SIZE, 0x8a),
+ COEFF_RAM_CTL("Bass Non Linear Function 2", COEFF_SIZE, 0x8b),
+
+ COEFF_RAM_CTL("Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c),
+
+ COEFF_RAM_CTL("Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91),
+
+ COEFF_RAM_CTL("Bass Mix", COEFF_SIZE, 0x96),
+
+ COEFF_RAM_CTL("Treb Extraction BiQuad1", BIQUAD_SIZE, 0x97),
+ COEFF_RAM_CTL("Treb Extraction BiQuad2", BIQUAD_SIZE, 0x9c),
+
+ COEFF_RAM_CTL("Treb Non Linear Function 1", COEFF_SIZE, 0xa1),
+ COEFF_RAM_CTL("Treb Non Linear Function 2", COEFF_SIZE, 0xa2),
+
+ COEFF_RAM_CTL("Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3),
+
+ COEFF_RAM_CTL("Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8),
+
+ COEFF_RAM_CTL("Treb Mix", COEFF_SIZE, 0xad),
+
+ COEFF_RAM_CTL("3D", COEFF_SIZE, 0xae),
+
+ COEFF_RAM_CTL("3D Mix", COEFF_SIZE, 0xaf),
+
+ COEFF_RAM_CTL("MBC1 BiQuad1", BIQUAD_SIZE, 0xb0),
+ COEFF_RAM_CTL("MBC1 BiQuad2", BIQUAD_SIZE, 0xb5),
+
+ COEFF_RAM_CTL("MBC2 BiQuad1", BIQUAD_SIZE, 0xba),
+ COEFF_RAM_CTL("MBC2 BiQuad2", BIQUAD_SIZE, 0xbf),
+
+ COEFF_RAM_CTL("MBC3 BiQuad1", BIQUAD_SIZE, 0xc4),
+ COEFF_RAM_CTL("MBC3 BiQuad2", BIQUAD_SIZE, 0xc9),
+
+ /* EQ */
+ SOC_SINGLE("EQ1 Switch", R_CONFIG1, FB_CONFIG1_EQ1_EN, 1, 0),
+ SOC_SINGLE("EQ2 Switch", R_CONFIG1, FB_CONFIG1_EQ2_EN, 1, 0),
+ SOC_ENUM("EQ1 Band Enable", eq1_band_enable_enum),
+ SOC_ENUM("EQ2 Band Enable", eq2_band_enable_enum),
+
+ /* CLE */
+ SOC_ENUM("CLE Level Detect",
+ cle_level_detection_enum),
+ SOC_ENUM("CLE Level Detect Win",
+ cle_level_detection_window_enum),
+ SOC_SINGLE("Expander Switch",
+ R_CLECTL, FB_CLECTL_EXP_EN, 1, 0),
+ SOC_SINGLE("Limiter Switch",
+ R_CLECTL, FB_CLECTL_LIMIT_EN, 1, 0),
+ SOC_SINGLE("Comp Switch",
+ R_CLECTL, FB_CLECTL_COMP_EN, 1, 0),
+ SOC_SINGLE_TLV("CLE Make-Up Gain Volume",
+ R_MUGAIN, FB_MUGAIN_CLEMUG, 0x1f, 0, mugain_scale),
+ SOC_SINGLE_TLV("Comp Thresh Volume",
+ R_COMPTH, FB_COMPTH, 0xff, 0, compth_scale),
+ SOC_ENUM("Comp Ratio", compressor_ratio_enum),
+ SND_SOC_BYTES("Comp Atk Time", R_CATKTCL, 2),
+
+ /* Effects */
+ SOC_SINGLE("3D Switch", R_FXCTL, FB_FXCTL_3DEN, 1, 0),
+ SOC_SINGLE("Treble Switch", R_FXCTL, FB_FXCTL_TEEN, 1, 0),
+ SOC_SINGLE("Treble Bypass Switch", R_FXCTL, FB_FXCTL_TNLFBYPASS, 1, 0),
+ SOC_SINGLE("Bass Switch", R_FXCTL, FB_FXCTL_BEEN, 1, 0),
+ SOC_SINGLE("Bass Bypass Switch", R_FXCTL, FB_FXCTL_BNLFBYPASS, 1, 0),
+
+ /* MBC */
+ SOC_SINGLE("MBC Band1 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN1, 1, 0),
+ SOC_SINGLE("MBC Band2 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN2, 1, 0),
+ SOC_SINGLE("MBC Band3 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN3, 1, 0),
+ SOC_ENUM("MBC Band1 Level Detect",
+ mbc_level_detection_enums[0]),
+ SOC_ENUM("MBC Band2 Level Detect",
+ mbc_level_detection_enums[1]),
+ SOC_ENUM("MBC Band3 Level Detect",
+ mbc_level_detection_enums[2]),
+ SOC_ENUM("MBC Band1 Level Detect Win",
+ mbc_level_detection_window_enums[0]),
+ SOC_ENUM("MBC Band2 Level Detect Win",
+ mbc_level_detection_window_enums[1]),
+ SOC_ENUM("MBC Band3 Level Detect Win",
+ mbc_level_detection_window_enums[2]),
+
+ SOC_SINGLE("MBC1 Phase Invert Switch",
+ R_DACMBCMUG1, FB_DACMBCMUG1_PHASE, 1, 0),
+ SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Volume",
+ R_DACMBCMUG1, FB_DACMBCMUG1_MUGAIN, 0x1f, 0, mugain_scale),
+ SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Volume",
+ R_DACMBCTHR1, FB_DACMBCTHR1_THRESH, 0xff, 0, compth_scale),
+ SOC_ENUM("DAC MBC1 Comp Ratio",
+ dac_mbc1_compressor_ratio_enum),
+ SND_SOC_BYTES("DAC MBC1 Comp Atk Time", R_DACMBCATK1L, 2),
+ SND_SOC_BYTES("DAC MBC1 Comp Rel Time Const",
+ R_DACMBCREL1L, 2),
+
+ SOC_SINGLE("MBC2 Phase Invert Switch",
+ R_DACMBCMUG2, FB_DACMBCMUG2_PHASE, 1, 0),
+ SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Volume",
+ R_DACMBCMUG2, FB_DACMBCMUG2_MUGAIN, 0x1f, 0, mugain_scale),
+ SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Volume",
+ R_DACMBCTHR2, FB_DACMBCTHR2_THRESH, 0xff, 0, compth_scale),
+ SOC_ENUM("DAC MBC2 Comp Ratio",
+ dac_mbc2_compressor_ratio_enum),
+ SND_SOC_BYTES("DAC MBC2 Comp Atk Time", R_DACMBCATK2L, 2),
+ SND_SOC_BYTES("DAC MBC2 Comp Rel Time Const",
+ R_DACMBCREL2L, 2),
+
+ SOC_SINGLE("MBC3 Phase Invert Switch",
+ R_DACMBCMUG3, FB_DACMBCMUG3_PHASE, 1, 0),
+ SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Volume",
+ R_DACMBCMUG3, FB_DACMBCMUG3_MUGAIN, 0x1f, 0, mugain_scale),
+ SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Volume",
+ R_DACMBCTHR3, FB_DACMBCTHR3_THRESH, 0xff, 0, compth_scale),
+ SOC_ENUM("DAC MBC3 Comp Ratio",
+ dac_mbc3_compressor_ratio_enum),
+ SND_SOC_BYTES("DAC MBC3 Comp Atk Time", R_DACMBCATK3L, 2),
+ SND_SOC_BYTES("DAC MBC3 Comp Rel Time Const",
+ R_DACMBCREL3L, 2),
+};
+
+static int setup_sample_format(struct snd_soc_component *component,
+ snd_pcm_format_t format)
+{
+ unsigned int width;
+ int ret;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ width = RV_AIC1_WL_16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ width = RV_AIC1_WL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ width = RV_AIC1_WL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ width = RV_AIC1_WL_32;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unsupported format width (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(component,
+ R_AIC1, RM_AIC1_WL, width);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set sample width (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int setup_sample_rate(struct snd_soc_component *component,
+ unsigned int rate)
+{
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ unsigned int br, bm;
+ int ret;
+
+ switch (rate) {
+ case 8000:
+ br = RV_DACSR_DBR_32;
+ bm = RV_DACSR_DBM_PT25;
+ break;
+ case 16000:
+ br = RV_DACSR_DBR_32;
+ bm = RV_DACSR_DBM_PT5;
+ break;
+ case 24000:
+ br = RV_DACSR_DBR_48;
+ bm = RV_DACSR_DBM_PT5;
+ break;
+ case 32000:
+ br = RV_DACSR_DBR_32;
+ bm = RV_DACSR_DBM_1;
+ break;
+ case 48000:
+ br = RV_DACSR_DBR_48;
+ bm = RV_DACSR_DBM_1;
+ break;
+ case 96000:
+ br = RV_DACSR_DBR_48;
+ bm = RV_DACSR_DBM_2;
+ break;
+ case 11025:
+ br = RV_DACSR_DBR_44_1;
+ bm = RV_DACSR_DBM_PT25;
+ break;
+ case 22050:
+ br = RV_DACSR_DBR_44_1;
+ bm = RV_DACSR_DBM_PT5;
+ break;
+ case 44100:
+ br = RV_DACSR_DBR_44_1;
+ bm = RV_DACSR_DBM_1;
+ break;
+ case 88200:
+ br = RV_DACSR_DBR_44_1;
+ bm = RV_DACSR_DBM_2;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported sample rate %d\n", rate);
+ return -EINVAL;
+ }
+
+ /* DAC and ADC share bit and frame clock */
+ ret = snd_soc_component_update_bits(component,
+ R_DACSR, RM_DACSR_DBR, br);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to update register (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(component,
+ R_DACSR, RM_DACSR_DBM, bm);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to update register (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(component,
+ R_ADCSR, RM_DACSR_DBR, br);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to update register (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(component,
+ R_ADCSR, RM_DACSR_DBM, bm);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to update register (%d)\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&tscs42xx->audio_params_lock);
+
+ tscs42xx->samplerate = rate;
+
+ mutex_unlock(&tscs42xx->audio_params_lock);
+
+ return 0;
+}
+
+struct reg_setting {
+ unsigned int addr;
+ unsigned int val;
+ unsigned int mask;
+};
+
+#define PLL_REG_SETTINGS_COUNT 13
+struct pll_ctl {
+ int input_freq;
+ struct reg_setting settings[PLL_REG_SETTINGS_COUNT];
+};
+
+#define PLL_CTL(f, rt, rd, r1b_l, r9, ra, rb, \
+ rc, r12, r1b_h, re, rf, r10, r11) \
+ { \
+ .input_freq = f, \
+ .settings = { \
+ {R_TIMEBASE, rt, 0xFF}, \
+ {R_PLLCTLD, rd, 0xFF}, \
+ {R_PLLCTL1B, r1b_l, 0x0F}, \
+ {R_PLLCTL9, r9, 0xFF}, \
+ {R_PLLCTLA, ra, 0xFF}, \
+ {R_PLLCTLB, rb, 0xFF}, \
+ {R_PLLCTLC, rc, 0xFF}, \
+ {R_PLLCTL12, r12, 0xFF}, \
+ {R_PLLCTL1B, r1b_h, 0xF0}, \
+ {R_PLLCTLE, re, 0xFF}, \
+ {R_PLLCTLF, rf, 0xFF}, \
+ {R_PLLCTL10, r10, 0xFF}, \
+ {R_PLLCTL11, r11, 0xFF}, \
+ }, \
+ }
+
+static const struct pll_ctl pll_ctls[] = {
+ PLL_CTL(1411200, 0x05,
+ 0x39, 0x04, 0x07, 0x02, 0xC3, 0x04,
+ 0x1B, 0x10, 0x03, 0x03, 0xD0, 0x02),
+ PLL_CTL(1536000, 0x05,
+ 0x1A, 0x04, 0x02, 0x03, 0xE0, 0x01,
+ 0x1A, 0x10, 0x02, 0x03, 0xB9, 0x01),
+ PLL_CTL(2822400, 0x0A,
+ 0x23, 0x04, 0x07, 0x04, 0xC3, 0x04,
+ 0x22, 0x10, 0x05, 0x03, 0x58, 0x02),
+ PLL_CTL(3072000, 0x0B,
+ 0x22, 0x04, 0x07, 0x03, 0x48, 0x03,
+ 0x1A, 0x10, 0x04, 0x03, 0xB9, 0x01),
+ PLL_CTL(5644800, 0x15,
+ 0x23, 0x04, 0x0E, 0x04, 0xC3, 0x04,
+ 0x1A, 0x10, 0x08, 0x03, 0xE0, 0x01),
+ PLL_CTL(6144000, 0x17,
+ 0x1A, 0x04, 0x08, 0x03, 0xE0, 0x01,
+ 0x1A, 0x10, 0x08, 0x03, 0xB9, 0x01),
+ PLL_CTL(12000000, 0x2E,
+ 0x1B, 0x04, 0x19, 0x03, 0x00, 0x03,
+ 0x2A, 0x10, 0x19, 0x05, 0x98, 0x04),
+ PLL_CTL(19200000, 0x4A,
+ 0x13, 0x04, 0x14, 0x03, 0x80, 0x01,
+ 0x1A, 0x10, 0x19, 0x03, 0xB9, 0x01),
+ PLL_CTL(22000000, 0x55,
+ 0x2A, 0x04, 0x37, 0x05, 0x00, 0x06,
+ 0x22, 0x10, 0x26, 0x03, 0x49, 0x02),
+ PLL_CTL(22579200, 0x57,
+ 0x22, 0x04, 0x31, 0x03, 0x20, 0x03,
+ 0x1A, 0x10, 0x1D, 0x03, 0xB3, 0x01),
+ PLL_CTL(24000000, 0x5D,
+ 0x13, 0x04, 0x19, 0x03, 0x80, 0x01,
+ 0x1B, 0x10, 0x19, 0x05, 0x4C, 0x02),
+ PLL_CTL(24576000, 0x5F,
+ 0x13, 0x04, 0x1D, 0x03, 0xB3, 0x01,
+ 0x22, 0x10, 0x40, 0x03, 0x72, 0x03),
+ PLL_CTL(27000000, 0x68,
+ 0x22, 0x04, 0x4B, 0x03, 0x00, 0x04,
+ 0x2A, 0x10, 0x7D, 0x03, 0x20, 0x06),
+ PLL_CTL(36000000, 0x8C,
+ 0x1B, 0x04, 0x4B, 0x03, 0x00, 0x03,
+ 0x2A, 0x10, 0x7D, 0x03, 0x98, 0x04),
+ PLL_CTL(25000000, 0x61,
+ 0x1B, 0x04, 0x37, 0x03, 0x2B, 0x03,
+ 0x1A, 0x10, 0x2A, 0x03, 0x39, 0x02),
+ PLL_CTL(26000000, 0x65,
+ 0x23, 0x04, 0x41, 0x05, 0x00, 0x06,
+ 0x1A, 0x10, 0x26, 0x03, 0xEF, 0x01),
+ PLL_CTL(12288000, 0x2F,
+ 0x1A, 0x04, 0x12, 0x03, 0x1C, 0x02,
+ 0x22, 0x10, 0x20, 0x03, 0x72, 0x03),
+ PLL_CTL(40000000, 0x9B,
+ 0x22, 0x08, 0x7D, 0x03, 0x80, 0x04,
+ 0x23, 0x10, 0x7D, 0x05, 0xE4, 0x06),
+ PLL_CTL(512000, 0x01,
+ 0x22, 0x04, 0x01, 0x03, 0xD0, 0x02,
+ 0x1B, 0x10, 0x01, 0x04, 0x72, 0x03),
+ PLL_CTL(705600, 0x02,
+ 0x22, 0x04, 0x02, 0x03, 0x15, 0x04,
+ 0x22, 0x10, 0x01, 0x04, 0x80, 0x02),
+ PLL_CTL(1024000, 0x03,
+ 0x22, 0x04, 0x02, 0x03, 0xD0, 0x02,
+ 0x1B, 0x10, 0x02, 0x04, 0x72, 0x03),
+ PLL_CTL(2048000, 0x07,
+ 0x22, 0x04, 0x04, 0x03, 0xD0, 0x02,
+ 0x1B, 0x10, 0x04, 0x04, 0x72, 0x03),
+ PLL_CTL(2400000, 0x08,
+ 0x22, 0x04, 0x05, 0x03, 0x00, 0x03,
+ 0x23, 0x10, 0x05, 0x05, 0x98, 0x04),
+};
+
+static const struct pll_ctl *get_pll_ctl(int input_freq)
+{
+ int i;
+ const struct pll_ctl *pll_ctl = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(pll_ctls); ++i)
+ if (input_freq == pll_ctls[i].input_freq) {
+ pll_ctl = &pll_ctls[i];
+ break;
+ }
+
+ return pll_ctl;
+}
+
+static int set_pll_ctl_from_input_freq(struct snd_soc_component *component,
+ const int input_freq)
+{
+ int ret;
+ int i;
+ const struct pll_ctl *pll_ctl;
+
+ pll_ctl = get_pll_ctl(input_freq);
+ if (!pll_ctl) {
+ ret = -EINVAL;
+ dev_err(component->dev, "No PLL input entry for %d (%d)\n",
+ input_freq, ret);
+ return ret;
+ }
+
+ for (i = 0; i < PLL_REG_SETTINGS_COUNT; ++i) {
+ ret = snd_soc_component_update_bits(component,
+ pll_ctl->settings[i].addr,
+ pll_ctl->settings[i].mask,
+ pll_ctl->settings[i].val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set pll ctl (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tscs42xx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ ret = setup_sample_format(component, params_format(params));
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to setup sample format (%d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = setup_sample_rate(component, params_rate(params));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to setup sample rate (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int dac_mute(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(component,
+ R_CNVRTR1, RM_CNVRTR1_DACMU,
+ RV_CNVRTR1_DACMU_ENABLE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to mute DAC (%d)\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int dac_unmute(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(component,
+ R_CNVRTR1, RM_CNVRTR1_DACMU,
+ RV_CNVRTR1_DACMU_DISABLE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to unmute DAC (%d)\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int adc_mute(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(component,
+ R_CNVRTR0, RM_CNVRTR0_ADCMU, RV_CNVRTR0_ADCMU_ENABLE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to mute ADC (%d)\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int adc_unmute(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = snd_soc_component_update_bits(component,
+ R_CNVRTR0, RM_CNVRTR0_ADCMU, RV_CNVRTR0_ADCMU_DISABLE);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to unmute ADC (%d)\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tscs42xx_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ if (mute)
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = dac_mute(component);
+ else
+ ret = adc_mute(component);
+ else
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = dac_unmute(component);
+ else
+ ret = adc_unmute(component);
+
+ return ret;
+}
+
+static int tscs42xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ /* Consumer mode not supported since it needs always-on frame clock */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ ret = snd_soc_component_update_bits(component,
+ R_AIC1, RM_AIC1_MS, RV_AIC1_MS_MASTER);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set codec DAI master (%d)\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unsupported format (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai,
+ unsigned int ratio)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+ int ret = 0;
+
+ switch (ratio) {
+ case 32:
+ value = RV_DACSR_DBCM_32;
+ break;
+ case 40:
+ value = RV_DACSR_DBCM_40;
+ break;
+ case 64:
+ value = RV_DACSR_DBCM_64;
+ break;
+ default:
+ dev_err(component->dev, "Unsupported bclk ratio (%d)\n", ret);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ R_DACSR, RM_DACSR_DBCM, value);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set DAC BCLK ratio (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(component,
+ R_ADCSR, RM_ADCSR_ABCM, value);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC BCLK ratio (%d)\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&tscs42xx->audio_params_lock);
+
+ tscs42xx->bclk_ratio = ratio;
+
+ mutex_unlock(&tscs42xx->audio_params_lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tscs42xx_dai_ops = {
+ .hw_params = tscs42xx_hw_params,
+ .mute_stream = tscs42xx_mute_stream,
+ .set_fmt = tscs42xx_set_dai_fmt,
+ .set_bclk_ratio = tscs42xx_set_dai_bclk_ratio,
+};
+
+static int part_is_valid(struct tscs42xx *tscs42xx)
+{
+ int val;
+ int ret;
+ unsigned int reg;
+
+ ret = regmap_read(tscs42xx->regmap, R_DEVIDH, &reg);
+ if (ret < 0)
+ return ret;
+
+ val = reg << 8;
+ ret = regmap_read(tscs42xx->regmap, R_DEVIDL, &reg);
+ if (ret < 0)
+ return ret;
+
+ val |= reg;
+
+ switch (val) {
+ case 0x4A74:
+ case 0x4A73:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int set_sysclk(struct snd_soc_component *component)
+{
+ struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component);
+ unsigned long freq;
+ int ret;
+
+ switch (tscs42xx->sysclk_src_id) {
+ case TSCS42XX_PLL_SRC_XTAL:
+ case TSCS42XX_PLL_SRC_MCLK1:
+ ret = snd_soc_component_write(component, R_PLLREFSEL,
+ RV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 |
+ RV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set pll reference input (%d)\n",
+ ret);
+ return ret;
+ }
+ break;
+ case TSCS42XX_PLL_SRC_MCLK2:
+ ret = snd_soc_component_write(component, R_PLLREFSEL,
+ RV_PLLREFSEL_PLL1_REF_SEL_MCLK2 |
+ RV_PLLREFSEL_PLL2_REF_SEL_MCLK2);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set PLL reference (%d)\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(component->dev, "pll src is unsupported\n");
+ return -EINVAL;
+ }
+
+ freq = clk_get_rate(tscs42xx->sysclk);
+ ret = set_pll_ctl_from_input_freq(component, freq);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to setup PLL input freq (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tscs42xx_probe(struct snd_soc_component *component)
+{
+ return set_sysclk(component);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_tscs42xx = {
+ .probe = tscs42xx_probe,
+ .dapm_widgets = tscs42xx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tscs42xx_dapm_widgets),
+ .dapm_routes = tscs42xx_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tscs42xx_intercon),
+ .controls = tscs42xx_snd_controls,
+ .num_controls = ARRAY_SIZE(tscs42xx_snd_controls),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static inline void init_coeff_ram_cache(struct tscs42xx *tscs42xx)
+{
+ static const u8 norm_addrs[] = {
+ 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1f, 0x20, 0x25, 0x2a,
+ 0x2f, 0x34, 0x39, 0x3f, 0x40, 0x45, 0x4a, 0x4f, 0x54, 0x59,
+ 0x5f, 0x60, 0x65, 0x6a, 0x6f, 0x74, 0x79, 0x7f, 0x80, 0x85,
+ 0x8c, 0x91, 0x96, 0x97, 0x9c, 0xa3, 0xa8, 0xad, 0xaf, 0xb0,
+ 0xb5, 0xba, 0xbf, 0xc4, 0xc9,
+ };
+ u8 *coeff_ram = tscs42xx->coeff_ram;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(norm_addrs); i++)
+ coeff_ram[((norm_addrs[i] + 1) * COEFF_SIZE) - 1] = 0x40;
+}
+
+#define TSCS42XX_RATES SNDRV_PCM_RATE_8000_96000
+
+#define TSCS42XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver tscs42xx_dai = {
+ .name = "tscs42xx-HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TSCS42XX_RATES,
+ .formats = TSCS42XX_FORMATS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TSCS42XX_RATES,
+ .formats = TSCS42XX_FORMATS,},
+ .ops = &tscs42xx_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct reg_sequence tscs42xx_patch[] = {
+ { R_AIC2, RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED },
+};
+
+static char const * const src_names[TSCS42XX_PLL_SRC_CNT] = {
+ "xtal", "mclk1", "mclk2"};
+
+static int tscs42xx_i2c_probe(struct i2c_client *i2c)
+{
+ struct tscs42xx *tscs42xx;
+ int src;
+ int ret;
+
+ tscs42xx = devm_kzalloc(&i2c->dev, sizeof(*tscs42xx), GFP_KERNEL);
+ if (!tscs42xx) {
+ ret = -ENOMEM;
+ dev_err(&i2c->dev,
+ "Failed to allocate memory for data (%d)\n", ret);
+ return ret;
+ }
+ i2c_set_clientdata(i2c, tscs42xx);
+
+ for (src = TSCS42XX_PLL_SRC_XTAL; src < TSCS42XX_PLL_SRC_CNT; src++) {
+ tscs42xx->sysclk = devm_clk_get(&i2c->dev, src_names[src]);
+ if (!IS_ERR(tscs42xx->sysclk)) {
+ break;
+ } else if (PTR_ERR(tscs42xx->sysclk) != -ENOENT) {
+ ret = PTR_ERR(tscs42xx->sysclk);
+ dev_err(&i2c->dev, "Failed to get sysclk (%d)\n", ret);
+ return ret;
+ }
+ }
+ if (src == TSCS42XX_PLL_SRC_CNT) {
+ ret = -EINVAL;
+ dev_err(&i2c->dev, "Failed to get a valid clock name (%d)\n",
+ ret);
+ return ret;
+ }
+ tscs42xx->sysclk_src_id = src;
+
+ tscs42xx->regmap = devm_regmap_init_i2c(i2c, &tscs42xx_regmap);
+ if (IS_ERR(tscs42xx->regmap)) {
+ ret = PTR_ERR(tscs42xx->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap (%d)\n", ret);
+ return ret;
+ }
+
+ init_coeff_ram_cache(tscs42xx);
+
+ ret = part_is_valid(tscs42xx);
+ if (ret <= 0) {
+ dev_err(&i2c->dev, "No valid part (%d)\n", ret);
+ ret = -ENODEV;
+ return ret;
+ }
+
+ ret = regmap_write(tscs42xx->regmap, R_RESET, RV_RESET_ENABLE);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to reset device (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_register_patch(tscs42xx->regmap, tscs42xx_patch,
+ ARRAY_SIZE(tscs42xx_patch));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to apply patch (%d)\n", ret);
+ return ret;
+ }
+
+ mutex_init(&tscs42xx->audio_params_lock);
+ mutex_init(&tscs42xx->coeff_ram_lock);
+ mutex_init(&tscs42xx->pll_lock);
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_tscs42xx, &tscs42xx_dai, 1);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id tscs42xx_i2c_id[] = {
+ { "tscs42A1" },
+ { "tscs42A2" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tscs42xx_i2c_id);
+
+static const struct of_device_id tscs42xx_of_match[] = {
+ { .compatible = "tempo,tscs42A1", },
+ { .compatible = "tempo,tscs42A2", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tscs42xx_of_match);
+
+static struct i2c_driver tscs42xx_i2c_driver = {
+ .driver = {
+ .name = "tscs42xx",
+ .of_match_table = tscs42xx_of_match,
+ },
+ .probe = tscs42xx_i2c_probe,
+ .id_table = tscs42xx_i2c_id,
+};
+
+module_i2c_driver(tscs42xx_i2c_driver);
+
+MODULE_AUTHOR("Tempo Semiconductor <steven.eckhoff.opensource@gmail.com");
+MODULE_DESCRIPTION("ASoC TSCS42xx driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tscs42xx.h b/sound/soc/codecs/tscs42xx.h
new file mode 100644
index 000000000000..6b3a21081635
--- /dev/null
+++ b/sound/soc/codecs/tscs42xx.h
@@ -0,0 +1,2701 @@
+// SPDX-License-Identifier: GPL-2.0
+// tscs42xx.h -- TSCS42xx ALSA SoC Audio driver
+// Copyright 2017 Tempo Semiconductor, Inc.
+// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
+
+#ifndef __WOOKIE_H__
+#define __WOOKIE_H__
+
+enum {
+ TSCS42XX_PLL_SRC_XTAL,
+ TSCS42XX_PLL_SRC_MCLK1,
+ TSCS42XX_PLL_SRC_MCLK2,
+ TSCS42XX_PLL_SRC_CNT,
+};
+
+#define R_HPVOLL 0x0
+#define R_HPVOLR 0x1
+#define R_SPKVOLL 0x2
+#define R_SPKVOLR 0x3
+#define R_DACVOLL 0x4
+#define R_DACVOLR 0x5
+#define R_ADCVOLL 0x6
+#define R_ADCVOLR 0x7
+#define R_INVOLL 0x8
+#define R_INVOLR 0x9
+#define R_INMODE 0x0B
+#define R_INSELL 0x0C
+#define R_INSELR 0x0D
+#define R_AIC1 0x13
+#define R_AIC2 0x14
+#define R_CNVRTR0 0x16
+#define R_ADCSR 0x17
+#define R_CNVRTR1 0x18
+#define R_DACSR 0x19
+#define R_PWRM1 0x1A
+#define R_PWRM2 0x1B
+#define R_CTL 0x1C
+#define R_CONFIG0 0x1F
+#define R_CONFIG1 0x20
+#define R_DMICCTL 0x24
+#define R_CLECTL 0x25
+#define R_MUGAIN 0x26
+#define R_COMPTH 0x27
+#define R_CMPRAT 0x28
+#define R_CATKTCL 0x29
+#define R_CATKTCH 0x2A
+#define R_CRELTCL 0x2B
+#define R_CRELTCH 0x2C
+#define R_LIMTH 0x2D
+#define R_LIMTGT 0x2E
+#define R_LATKTCL 0x2F
+#define R_LATKTCH 0x30
+#define R_LRELTCL 0x31
+#define R_LRELTCH 0x32
+#define R_EXPTH 0x33
+#define R_EXPRAT 0x34
+#define R_XATKTCL 0x35
+#define R_XATKTCH 0x36
+#define R_XRELTCL 0x37
+#define R_XRELTCH 0x38
+#define R_FXCTL 0x39
+#define R_DACCRWRL 0x3A
+#define R_DACCRWRM 0x3B
+#define R_DACCRWRH 0x3C
+#define R_DACCRRDL 0x3D
+#define R_DACCRRDM 0x3E
+#define R_DACCRRDH 0x3F
+#define R_DACCRADDR 0x40
+#define R_DCOFSEL 0x41
+#define R_PLLCTL9 0x4E
+#define R_PLLCTLA 0x4F
+#define R_PLLCTLB 0x50
+#define R_PLLCTLC 0x51
+#define R_PLLCTLD 0x52
+#define R_PLLCTLE 0x53
+#define R_PLLCTLF 0x54
+#define R_PLLCTL10 0x55
+#define R_PLLCTL11 0x56
+#define R_PLLCTL12 0x57
+#define R_PLLCTL1B 0x60
+#define R_PLLCTL1C 0x61
+#define R_TIMEBASE 0x77
+#define R_DEVIDL 0x7D
+#define R_DEVIDH 0x7E
+#define R_RESET 0x80
+#define R_DACCRSTAT 0x8A
+#define R_PLLCTL0 0x8E
+#define R_PLLREFSEL 0x8F
+#define R_DACMBCEN 0xC7
+#define R_DACMBCCTL 0xC8
+#define R_DACMBCMUG1 0xC9
+#define R_DACMBCTHR1 0xCA
+#define R_DACMBCRAT1 0xCB
+#define R_DACMBCATK1L 0xCC
+#define R_DACMBCATK1H 0xCD
+#define R_DACMBCREL1L 0xCE
+#define R_DACMBCREL1H 0xCF
+#define R_DACMBCMUG2 0xD0
+#define R_DACMBCTHR2 0xD1
+#define R_DACMBCRAT2 0xD2
+#define R_DACMBCATK2L 0xD3
+#define R_DACMBCATK2H 0xD4
+#define R_DACMBCREL2L 0xD5
+#define R_DACMBCREL2H 0xD6
+#define R_DACMBCMUG3 0xD7
+#define R_DACMBCTHR3 0xD8
+#define R_DACMBCRAT3 0xD9
+#define R_DACMBCATK3L 0xDA
+#define R_DACMBCATK3H 0xDB
+#define R_DACMBCREL3L 0xDC
+#define R_DACMBCREL3H 0xDD
+
+/* Helpers */
+#define RM(m, b) ((m)<<(b))
+#define RV(v, b) ((v)<<(b))
+
+/****************************
+ * R_HPVOLL (0x0) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_HPVOLL 0
+
+/* Field Masks */
+#define FM_HPVOLL 0X7F
+
+/* Field Values */
+#define FV_HPVOLL_P6DB 0x7F
+#define FV_HPVOLL_N88PT5DB 0x1
+#define FV_HPVOLL_MUTE 0x0
+
+/* Register Masks */
+#define RM_HPVOLL RM(FM_HPVOLL, FB_HPVOLL)
+
+/* Register Values */
+#define RV_HPVOLL_P6DB RV(FV_HPVOLL_P6DB, FB_HPVOLL)
+#define RV_HPVOLL_N88PT5DB RV(FV_HPVOLL_N88PT5DB, FB_HPVOLL)
+#define RV_HPVOLL_MUTE RV(FV_HPVOLL_MUTE, FB_HPVOLL)
+
+/****************************
+ * R_HPVOLR (0x1) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_HPVOLR 0
+
+/* Field Masks */
+#define FM_HPVOLR 0X7F
+
+/* Field Values */
+#define FV_HPVOLR_P6DB 0x7F
+#define FV_HPVOLR_N88PT5DB 0x1
+#define FV_HPVOLR_MUTE 0x0
+
+/* Register Masks */
+#define RM_HPVOLR RM(FM_HPVOLR, FB_HPVOLR)
+
+/* Register Values */
+#define RV_HPVOLR_P6DB RV(FV_HPVOLR_P6DB, FB_HPVOLR)
+#define RV_HPVOLR_N88PT5DB RV(FV_HPVOLR_N88PT5DB, FB_HPVOLR)
+#define RV_HPVOLR_MUTE RV(FV_HPVOLR_MUTE, FB_HPVOLR)
+
+/*****************************
+ * R_SPKVOLL (0x2) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_SPKVOLL 0
+
+/* Field Masks */
+#define FM_SPKVOLL 0X7F
+
+/* Field Values */
+#define FV_SPKVOLL_P12DB 0x7F
+#define FV_SPKVOLL_N77PT25DB 0x8
+#define FV_SPKVOLL_MUTE 0x0
+
+/* Register Masks */
+#define RM_SPKVOLL RM(FM_SPKVOLL, FB_SPKVOLL)
+
+/* Register Values */
+#define RV_SPKVOLL_P12DB RV(FV_SPKVOLL_P12DB, FB_SPKVOLL)
+#define RV_SPKVOLL_N77PT25DB \
+ RV(FV_SPKVOLL_N77PT25DB, FB_SPKVOLL)
+
+#define RV_SPKVOLL_MUTE RV(FV_SPKVOLL_MUTE, FB_SPKVOLL)
+
+/*****************************
+ * R_SPKVOLR (0x3) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_SPKVOLR 0
+
+/* Field Masks */
+#define FM_SPKVOLR 0X7F
+
+/* Field Values */
+#define FV_SPKVOLR_P12DB 0x7F
+#define FV_SPKVOLR_N77PT25DB 0x8
+#define FV_SPKVOLR_MUTE 0x0
+
+/* Register Masks */
+#define RM_SPKVOLR RM(FM_SPKVOLR, FB_SPKVOLR)
+
+/* Register Values */
+#define RV_SPKVOLR_P12DB RV(FV_SPKVOLR_P12DB, FB_SPKVOLR)
+#define RV_SPKVOLR_N77PT25DB \
+ RV(FV_SPKVOLR_N77PT25DB, FB_SPKVOLR)
+
+#define RV_SPKVOLR_MUTE RV(FV_SPKVOLR_MUTE, FB_SPKVOLR)
+
+/*****************************
+ * R_DACVOLL (0x4) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_DACVOLL 0
+
+/* Field Masks */
+#define FM_DACVOLL 0XFF
+
+/* Field Values */
+#define FV_DACVOLL_0DB 0xFF
+#define FV_DACVOLL_N95PT625DB 0x1
+#define FV_DACVOLL_MUTE 0x0
+
+/* Register Masks */
+#define RM_DACVOLL RM(FM_DACVOLL, FB_DACVOLL)
+
+/* Register Values */
+#define RV_DACVOLL_0DB RV(FV_DACVOLL_0DB, FB_DACVOLL)
+#define RV_DACVOLL_N95PT625DB \
+ RV(FV_DACVOLL_N95PT625DB, FB_DACVOLL)
+
+#define RV_DACVOLL_MUTE RV(FV_DACVOLL_MUTE, FB_DACVOLL)
+
+/*****************************
+ * R_DACVOLR (0x5) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_DACVOLR 0
+
+/* Field Masks */
+#define FM_DACVOLR 0XFF
+
+/* Field Values */
+#define FV_DACVOLR_0DB 0xFF
+#define FV_DACVOLR_N95PT625DB 0x1
+#define FV_DACVOLR_MUTE 0x0
+
+/* Register Masks */
+#define RM_DACVOLR RM(FM_DACVOLR, FB_DACVOLR)
+
+/* Register Values */
+#define RV_DACVOLR_0DB RV(FV_DACVOLR_0DB, FB_DACVOLR)
+#define RV_DACVOLR_N95PT625DB \
+ RV(FV_DACVOLR_N95PT625DB, FB_DACVOLR)
+
+#define RV_DACVOLR_MUTE RV(FV_DACVOLR_MUTE, FB_DACVOLR)
+
+/*****************************
+ * R_ADCVOLL (0x6) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_ADCVOLL 0
+
+/* Field Masks */
+#define FM_ADCVOLL 0XFF
+
+/* Field Values */
+#define FV_ADCVOLL_P24DB 0xFF
+#define FV_ADCVOLL_N71PT25DB 0x1
+#define FV_ADCVOLL_MUTE 0x0
+
+/* Register Masks */
+#define RM_ADCVOLL RM(FM_ADCVOLL, FB_ADCVOLL)
+
+/* Register Values */
+#define RV_ADCVOLL_P24DB RV(FV_ADCVOLL_P24DB, FB_ADCVOLL)
+#define RV_ADCVOLL_N71PT25DB \
+ RV(FV_ADCVOLL_N71PT25DB, FB_ADCVOLL)
+
+#define RV_ADCVOLL_MUTE RV(FV_ADCVOLL_MUTE, FB_ADCVOLL)
+
+/*****************************
+ * R_ADCVOLR (0x7) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_ADCVOLR 0
+
+/* Field Masks */
+#define FM_ADCVOLR 0XFF
+
+/* Field Values */
+#define FV_ADCVOLR_P24DB 0xFF
+#define FV_ADCVOLR_N71PT25DB 0x1
+#define FV_ADCVOLR_MUTE 0x0
+
+/* Register Masks */
+#define RM_ADCVOLR RM(FM_ADCVOLR, FB_ADCVOLR)
+
+/* Register Values */
+#define RV_ADCVOLR_P24DB RV(FV_ADCVOLR_P24DB, FB_ADCVOLR)
+#define RV_ADCVOLR_N71PT25DB \
+ RV(FV_ADCVOLR_N71PT25DB, FB_ADCVOLR)
+
+#define RV_ADCVOLR_MUTE RV(FV_ADCVOLR_MUTE, FB_ADCVOLR)
+
+/****************************
+ * R_INVOLL (0x8) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_INVOLL_INMUTEL 7
+#define FB_INVOLL_IZCL 6
+#define FB_INVOLL 0
+
+/* Field Masks */
+#define FM_INVOLL_INMUTEL 0X1
+#define FM_INVOLL_IZCL 0X1
+#define FM_INVOLL 0X3F
+
+/* Field Values */
+#define FV_INVOLL_INMUTEL_ENABLE 0x1
+#define FV_INVOLL_INMUTEL_DISABLE 0x0
+#define FV_INVOLL_IZCL_ENABLE 0x1
+#define FV_INVOLL_IZCL_DISABLE 0x0
+#define FV_INVOLL_P30DB 0x3F
+#define FV_INVOLL_N17PT25DB 0x0
+
+/* Register Masks */
+#define RM_INVOLL_INMUTEL \
+ RM(FM_INVOLL_INMUTEL, FB_INVOLL_INMUTEL)
+
+#define RM_INVOLL_IZCL RM(FM_INVOLL_IZCL, FB_INVOLL_IZCL)
+#define RM_INVOLL RM(FM_INVOLL, FB_INVOLL)
+
+/* Register Values */
+#define RV_INVOLL_INMUTEL_ENABLE \
+ RV(FV_INVOLL_INMUTEL_ENABLE, FB_INVOLL_INMUTEL)
+
+#define RV_INVOLL_INMUTEL_DISABLE \
+ RV(FV_INVOLL_INMUTEL_DISABLE, FB_INVOLL_INMUTEL)
+
+#define RV_INVOLL_IZCL_ENABLE \
+ RV(FV_INVOLL_IZCL_ENABLE, FB_INVOLL_IZCL)
+
+#define RV_INVOLL_IZCL_DISABLE \
+ RV(FV_INVOLL_IZCL_DISABLE, FB_INVOLL_IZCL)
+
+#define RV_INVOLL_P30DB RV(FV_INVOLL_P30DB, FB_INVOLL)
+#define RV_INVOLL_N17PT25DB RV(FV_INVOLL_N17PT25DB, FB_INVOLL)
+
+/****************************
+ * R_INVOLR (0x9) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_INVOLR_INMUTER 7
+#define FB_INVOLR_IZCR 6
+#define FB_INVOLR 0
+
+/* Field Masks */
+#define FM_INVOLR_INMUTER 0X1
+#define FM_INVOLR_IZCR 0X1
+#define FM_INVOLR 0X3F
+
+/* Field Values */
+#define FV_INVOLR_INMUTER_ENABLE 0x1
+#define FV_INVOLR_INMUTER_DISABLE 0x0
+#define FV_INVOLR_IZCR_ENABLE 0x1
+#define FV_INVOLR_IZCR_DISABLE 0x0
+#define FV_INVOLR_P30DB 0x3F
+#define FV_INVOLR_N17PT25DB 0x0
+
+/* Register Masks */
+#define RM_INVOLR_INMUTER \
+ RM(FM_INVOLR_INMUTER, FB_INVOLR_INMUTER)
+
+#define RM_INVOLR_IZCR RM(FM_INVOLR_IZCR, FB_INVOLR_IZCR)
+#define RM_INVOLR RM(FM_INVOLR, FB_INVOLR)
+
+/* Register Values */
+#define RV_INVOLR_INMUTER_ENABLE \
+ RV(FV_INVOLR_INMUTER_ENABLE, FB_INVOLR_INMUTER)
+
+#define RV_INVOLR_INMUTER_DISABLE \
+ RV(FV_INVOLR_INMUTER_DISABLE, FB_INVOLR_INMUTER)
+
+#define RV_INVOLR_IZCR_ENABLE \
+ RV(FV_INVOLR_IZCR_ENABLE, FB_INVOLR_IZCR)
+
+#define RV_INVOLR_IZCR_DISABLE \
+ RV(FV_INVOLR_IZCR_DISABLE, FB_INVOLR_IZCR)
+
+#define RV_INVOLR_P30DB RV(FV_INVOLR_P30DB, FB_INVOLR)
+#define RV_INVOLR_N17PT25DB RV(FV_INVOLR_N17PT25DB, FB_INVOLR)
+
+/*****************************
+ * R_INMODE (0x0B) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_INMODE_DS 0
+
+/* Field Masks */
+#define FM_INMODE_DS 0X1
+
+/* Field Values */
+#define FV_INMODE_DS_LRIN1 0x0
+#define FV_INMODE_DS_LRIN2 0x1
+
+/* Register Masks */
+#define RM_INMODE_DS RM(FM_INMODE_DS, FB_INMODE_DS)
+
+/* Register Values */
+#define RV_INMODE_DS_LRIN1 \
+ RV(FV_INMODE_DS_LRIN1, FB_INMODE_DS)
+
+#define RV_INMODE_DS_LRIN2 \
+ RV(FV_INMODE_DS_LRIN2, FB_INMODE_DS)
+
+
+/*****************************
+ * R_INSELL (0x0C) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_INSELL 6
+#define FB_INSELL_MICBSTL 4
+
+/* Field Masks */
+#define FM_INSELL 0X3
+#define FM_INSELL_MICBSTL 0X3
+
+/* Field Values */
+#define FV_INSELL_IN1 0x0
+#define FV_INSELL_IN2 0x1
+#define FV_INSELL_IN3 0x2
+#define FV_INSELL_D2S 0x3
+#define FV_INSELL_MICBSTL_OFF 0x0
+#define FV_INSELL_MICBSTL_10DB 0x1
+#define FV_INSELL_MICBSTL_20DB 0x2
+#define FV_INSELL_MICBSTL_30DB 0x3
+
+/* Register Masks */
+#define RM_INSELL RM(FM_INSELL, FB_INSELL)
+#define RM_INSELL_MICBSTL \
+ RM(FM_INSELL_MICBSTL, FB_INSELL_MICBSTL)
+
+
+/* Register Values */
+#define RV_INSELL_IN1 RV(FV_INSELL_IN1, FB_INSELL)
+#define RV_INSELL_IN2 RV(FV_INSELL_IN2, FB_INSELL)
+#define RV_INSELL_IN3 RV(FV_INSELL_IN3, FB_INSELL)
+#define RV_INSELL_D2S RV(FV_INSELL_D2S, FB_INSELL)
+#define RV_INSELL_MICBSTL_OFF \
+ RV(FV_INSELL_MICBSTL_OFF, FB_INSELL_MICBSTL)
+
+#define RV_INSELL_MICBSTL_10DB \
+ RV(FV_INSELL_MICBSTL_10DB, FB_INSELL_MICBSTL)
+
+#define RV_INSELL_MICBSTL_20DB \
+ RV(FV_INSELL_MICBSTL_20DB, FB_INSELL_MICBSTL)
+
+#define RV_INSELL_MICBSTL_30DB \
+ RV(FV_INSELL_MICBSTL_30DB, FB_INSELL_MICBSTL)
+
+
+/*****************************
+ * R_INSELR (0x0D) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_INSELR 6
+#define FB_INSELR_MICBSTR 4
+
+/* Field Masks */
+#define FM_INSELR 0X3
+#define FM_INSELR_MICBSTR 0X3
+
+/* Field Values */
+#define FV_INSELR_IN1 0x0
+#define FV_INSELR_IN2 0x1
+#define FV_INSELR_IN3 0x2
+#define FV_INSELR_D2S 0x3
+#define FV_INSELR_MICBSTR_OFF 0x0
+#define FV_INSELR_MICBSTR_10DB 0x1
+#define FV_INSELR_MICBSTR_20DB 0x2
+#define FV_INSELR_MICBSTR_30DB 0x3
+
+/* Register Masks */
+#define RM_INSELR RM(FM_INSELR, FB_INSELR)
+#define RM_INSELR_MICBSTR \
+ RM(FM_INSELR_MICBSTR, FB_INSELR_MICBSTR)
+
+
+/* Register Values */
+#define RV_INSELR_IN1 RV(FV_INSELR_IN1, FB_INSELR)
+#define RV_INSELR_IN2 RV(FV_INSELR_IN2, FB_INSELR)
+#define RV_INSELR_IN3 RV(FV_INSELR_IN3, FB_INSELR)
+#define RV_INSELR_D2S RV(FV_INSELR_D2S, FB_INSELR)
+#define RV_INSELR_MICBSTR_OFF \
+ RV(FV_INSELR_MICBSTR_OFF, FB_INSELR_MICBSTR)
+
+#define RV_INSELR_MICBSTR_10DB \
+ RV(FV_INSELR_MICBSTR_10DB, FB_INSELR_MICBSTR)
+
+#define RV_INSELR_MICBSTR_20DB \
+ RV(FV_INSELR_MICBSTR_20DB, FB_INSELR_MICBSTR)
+
+#define RV_INSELR_MICBSTR_30DB \
+ RV(FV_INSELR_MICBSTR_30DB, FB_INSELR_MICBSTR)
+
+
+/***************************
+ * R_AIC1 (0x13) *
+ ***************************/
+
+/* Field Offsets */
+#define FB_AIC1_BCLKINV 6
+#define FB_AIC1_MS 5
+#define FB_AIC1_LRP 4
+#define FB_AIC1_WL 2
+#define FB_AIC1_FORMAT 0
+
+/* Field Masks */
+#define FM_AIC1_BCLKINV 0X1
+#define FM_AIC1_MS 0X1
+#define FM_AIC1_LRP 0X1
+#define FM_AIC1_WL 0X3
+#define FM_AIC1_FORMAT 0X3
+
+/* Field Values */
+#define FV_AIC1_BCLKINV_ENABLE 0x1
+#define FV_AIC1_BCLKINV_DISABLE 0x0
+#define FV_AIC1_MS_MASTER 0x1
+#define FV_AIC1_MS_SLAVE 0x0
+#define FV_AIC1_LRP_INVERT 0x1
+#define FV_AIC1_LRP_NORMAL 0x0
+#define FV_AIC1_WL_16 0x0
+#define FV_AIC1_WL_20 0x1
+#define FV_AIC1_WL_24 0x2
+#define FV_AIC1_WL_32 0x3
+#define FV_AIC1_FORMAT_RIGHT 0x0
+#define FV_AIC1_FORMAT_LEFT 0x1
+#define FV_AIC1_FORMAT_I2S 0x2
+
+/* Register Masks */
+#define RM_AIC1_BCLKINV \
+ RM(FM_AIC1_BCLKINV, FB_AIC1_BCLKINV)
+
+#define RM_AIC1_MS RM(FM_AIC1_MS, FB_AIC1_MS)
+#define RM_AIC1_LRP RM(FM_AIC1_LRP, FB_AIC1_LRP)
+#define RM_AIC1_WL RM(FM_AIC1_WL, FB_AIC1_WL)
+#define RM_AIC1_FORMAT RM(FM_AIC1_FORMAT, FB_AIC1_FORMAT)
+
+/* Register Values */
+#define RV_AIC1_BCLKINV_ENABLE \
+ RV(FV_AIC1_BCLKINV_ENABLE, FB_AIC1_BCLKINV)
+
+#define RV_AIC1_BCLKINV_DISABLE \
+ RV(FV_AIC1_BCLKINV_DISABLE, FB_AIC1_BCLKINV)
+
+#define RV_AIC1_MS_MASTER RV(FV_AIC1_MS_MASTER, FB_AIC1_MS)
+#define RV_AIC1_MS_SLAVE RV(FV_AIC1_MS_SLAVE, FB_AIC1_MS)
+#define RV_AIC1_LRP_INVERT \
+ RV(FV_AIC1_LRP_INVERT, FB_AIC1_LRP)
+
+#define RV_AIC1_LRP_NORMAL \
+ RV(FV_AIC1_LRP_NORMAL, FB_AIC1_LRP)
+
+#define RV_AIC1_WL_16 RV(FV_AIC1_WL_16, FB_AIC1_WL)
+#define RV_AIC1_WL_20 RV(FV_AIC1_WL_20, FB_AIC1_WL)
+#define RV_AIC1_WL_24 RV(FV_AIC1_WL_24, FB_AIC1_WL)
+#define RV_AIC1_WL_32 RV(FV_AIC1_WL_32, FB_AIC1_WL)
+#define RV_AIC1_FORMAT_RIGHT \
+ RV(FV_AIC1_FORMAT_RIGHT, FB_AIC1_FORMAT)
+
+#define RV_AIC1_FORMAT_LEFT \
+ RV(FV_AIC1_FORMAT_LEFT, FB_AIC1_FORMAT)
+
+#define RV_AIC1_FORMAT_I2S \
+ RV(FV_AIC1_FORMAT_I2S, FB_AIC1_FORMAT)
+
+
+/***************************
+ * R_AIC2 (0x14) *
+ ***************************/
+
+/* Field Offsets */
+#define FB_AIC2_DACDSEL 6
+#define FB_AIC2_ADCDSEL 4
+#define FB_AIC2_TRI 3
+#define FB_AIC2_BLRCM 0
+
+/* Field Masks */
+#define FM_AIC2_DACDSEL 0X3
+#define FM_AIC2_ADCDSEL 0X3
+#define FM_AIC2_TRI 0X1
+#define FM_AIC2_BLRCM 0X7
+
+/* Field Values */
+#define FV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED 0x3
+
+/* Register Masks */
+#define RM_AIC2_DACDSEL \
+ RM(FM_AIC2_DACDSEL, FB_AIC2_DACDSEL)
+
+#define RM_AIC2_ADCDSEL \
+ RM(FM_AIC2_ADCDSEL, FB_AIC2_ADCDSEL)
+
+#define RM_AIC2_TRI RM(FM_AIC2_TRI, FB_AIC2_TRI)
+#define RM_AIC2_BLRCM RM(FM_AIC2_BLRCM, FB_AIC2_BLRCM)
+
+/* Register Values */
+#define RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED \
+ RV(FV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED, FB_AIC2_BLRCM)
+
+
+/******************************
+ * R_CNVRTR0 (0x16) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CNVRTR0_ADCPOLR 7
+#define FB_CNVRTR0_ADCPOLL 6
+#define FB_CNVRTR0_AMONOMIX 4
+#define FB_CNVRTR0_ADCMU 3
+#define FB_CNVRTR0_HPOR 2
+#define FB_CNVRTR0_ADCHPDR 1
+#define FB_CNVRTR0_ADCHPDL 0
+
+/* Field Masks */
+#define FM_CNVRTR0_ADCPOLR 0X1
+#define FM_CNVRTR0_ADCPOLL 0X1
+#define FM_CNVRTR0_AMONOMIX 0X3
+#define FM_CNVRTR0_ADCMU 0X1
+#define FM_CNVRTR0_HPOR 0X1
+#define FM_CNVRTR0_ADCHPDR 0X1
+#define FM_CNVRTR0_ADCHPDL 0X1
+
+/* Field Values */
+#define FV_CNVRTR0_ADCPOLR_INVERT 0x1
+#define FV_CNVRTR0_ADCPOLR_NORMAL 0x0
+#define FV_CNVRTR0_ADCPOLL_INVERT 0x1
+#define FV_CNVRTR0_ADCPOLL_NORMAL 0x0
+#define FV_CNVRTR0_ADCMU_ENABLE 0x1
+#define FV_CNVRTR0_ADCMU_DISABLE 0x0
+#define FV_CNVRTR0_ADCHPDR_ENABLE 0x1
+#define FV_CNVRTR0_ADCHPDR_DISABLE 0x0
+#define FV_CNVRTR0_ADCHPDL_ENABLE 0x1
+#define FV_CNVRTR0_ADCHPDL_DISABLE 0x0
+
+/* Register Masks */
+#define RM_CNVRTR0_ADCPOLR \
+ RM(FM_CNVRTR0_ADCPOLR, FB_CNVRTR0_ADCPOLR)
+
+#define RM_CNVRTR0_ADCPOLL \
+ RM(FM_CNVRTR0_ADCPOLL, FB_CNVRTR0_ADCPOLL)
+
+#define RM_CNVRTR0_AMONOMIX \
+ RM(FM_CNVRTR0_AMONOMIX, FB_CNVRTR0_AMONOMIX)
+
+#define RM_CNVRTR0_ADCMU \
+ RM(FM_CNVRTR0_ADCMU, FB_CNVRTR0_ADCMU)
+
+#define RM_CNVRTR0_HPOR \
+ RM(FM_CNVRTR0_HPOR, FB_CNVRTR0_HPOR)
+
+#define RM_CNVRTR0_ADCHPDR \
+ RM(FM_CNVRTR0_ADCHPDR, FB_CNVRTR0_ADCHPDR)
+
+#define RM_CNVRTR0_ADCHPDL \
+ RM(FM_CNVRTR0_ADCHPDL, FB_CNVRTR0_ADCHPDL)
+
+
+/* Register Values */
+#define RV_CNVRTR0_ADCPOLR_INVERT \
+ RV(FV_CNVRTR0_ADCPOLR_INVERT, FB_CNVRTR0_ADCPOLR)
+
+#define RV_CNVRTR0_ADCPOLR_NORMAL \
+ RV(FV_CNVRTR0_ADCPOLR_NORMAL, FB_CNVRTR0_ADCPOLR)
+
+#define RV_CNVRTR0_ADCPOLL_INVERT \
+ RV(FV_CNVRTR0_ADCPOLL_INVERT, FB_CNVRTR0_ADCPOLL)
+
+#define RV_CNVRTR0_ADCPOLL_NORMAL \
+ RV(FV_CNVRTR0_ADCPOLL_NORMAL, FB_CNVRTR0_ADCPOLL)
+
+#define RV_CNVRTR0_ADCMU_ENABLE \
+ RV(FV_CNVRTR0_ADCMU_ENABLE, FB_CNVRTR0_ADCMU)
+
+#define RV_CNVRTR0_ADCMU_DISABLE \
+ RV(FV_CNVRTR0_ADCMU_DISABLE, FB_CNVRTR0_ADCMU)
+
+#define RV_CNVRTR0_ADCHPDR_ENABLE \
+ RV(FV_CNVRTR0_ADCHPDR_ENABLE, FB_CNVRTR0_ADCHPDR)
+
+#define RV_CNVRTR0_ADCHPDR_DISABLE \
+ RV(FV_CNVRTR0_ADCHPDR_DISABLE, FB_CNVRTR0_ADCHPDR)
+
+#define RV_CNVRTR0_ADCHPDL_ENABLE \
+ RV(FV_CNVRTR0_ADCHPDL_ENABLE, FB_CNVRTR0_ADCHPDL)
+
+#define RV_CNVRTR0_ADCHPDL_DISABLE \
+ RV(FV_CNVRTR0_ADCHPDL_DISABLE, FB_CNVRTR0_ADCHPDL)
+
+
+/****************************
+ * R_ADCSR (0x17) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_ADCSR_ABCM 6
+#define FB_ADCSR_ABR 3
+#define FB_ADCSR_ABM 0
+
+/* Field Masks */
+#define FM_ADCSR_ABCM 0X3
+#define FM_ADCSR_ABR 0X3
+#define FM_ADCSR_ABM 0X7
+
+/* Field Values */
+#define FV_ADCSR_ABCM_AUTO 0x0
+#define FV_ADCSR_ABCM_32 0x1
+#define FV_ADCSR_ABCM_40 0x2
+#define FV_ADCSR_ABCM_64 0x3
+#define FV_ADCSR_ABR_32 0x0
+#define FV_ADCSR_ABR_44_1 0x1
+#define FV_ADCSR_ABR_48 0x2
+#define FV_ADCSR_ABM_PT25 0x0
+#define FV_ADCSR_ABM_PT5 0x1
+#define FV_ADCSR_ABM_1 0x2
+#define FV_ADCSR_ABM_2 0x3
+
+/* Register Masks */
+#define RM_ADCSR_ABCM RM(FM_ADCSR_ABCM, FB_ADCSR_ABCM)
+#define RM_ADCSR_ABR RM(FM_ADCSR_ABR, FB_ADCSR_ABR)
+#define RM_ADCSR_ABM RM(FM_ADCSR_ABM, FB_ADCSR_ABM)
+
+/* Register Values */
+#define RV_ADCSR_ABCM_AUTO \
+ RV(FV_ADCSR_ABCM_AUTO, FB_ADCSR_ABCM)
+
+#define RV_ADCSR_ABCM_32 \
+ RV(FV_ADCSR_ABCM_32, FB_ADCSR_ABCM)
+
+#define RV_ADCSR_ABCM_40 \
+ RV(FV_ADCSR_ABCM_40, FB_ADCSR_ABCM)
+
+#define RV_ADCSR_ABCM_64 \
+ RV(FV_ADCSR_ABCM_64, FB_ADCSR_ABCM)
+
+#define RV_ADCSR_ABR_32 RV(FV_ADCSR_ABR_32, FB_ADCSR_ABR)
+#define RV_ADCSR_ABR_44_1 \
+ RV(FV_ADCSR_ABR_44_1, FB_ADCSR_ABR)
+
+#define RV_ADCSR_ABR_48 RV(FV_ADCSR_ABR_48, FB_ADCSR_ABR)
+#define RV_ADCSR_ABR_ RV(FV_ADCSR_ABR_, FB_ADCSR_ABR)
+#define RV_ADCSR_ABM_PT25 \
+ RV(FV_ADCSR_ABM_PT25, FB_ADCSR_ABM)
+
+#define RV_ADCSR_ABM_PT5 RV(FV_ADCSR_ABM_PT5, FB_ADCSR_ABM)
+#define RV_ADCSR_ABM_1 RV(FV_ADCSR_ABM_1, FB_ADCSR_ABM)
+#define RV_ADCSR_ABM_2 RV(FV_ADCSR_ABM_2, FB_ADCSR_ABM)
+
+/******************************
+ * R_CNVRTR1 (0x18) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CNVRTR1_DACPOLR 7
+#define FB_CNVRTR1_DACPOLL 6
+#define FB_CNVRTR1_DMONOMIX 4
+#define FB_CNVRTR1_DACMU 3
+#define FB_CNVRTR1_DEEMPH 2
+#define FB_CNVRTR1_DACDITH 0
+
+/* Field Masks */
+#define FM_CNVRTR1_DACPOLR 0X1
+#define FM_CNVRTR1_DACPOLL 0X1
+#define FM_CNVRTR1_DMONOMIX 0X3
+#define FM_CNVRTR1_DACMU 0X1
+#define FM_CNVRTR1_DEEMPH 0X1
+#define FM_CNVRTR1_DACDITH 0X3
+
+/* Field Values */
+#define FV_CNVRTR1_DACPOLR_INVERT 0x1
+#define FV_CNVRTR1_DACPOLR_NORMAL 0x0
+#define FV_CNVRTR1_DACPOLL_INVERT 0x1
+#define FV_CNVRTR1_DACPOLL_NORMAL 0x0
+#define FV_CNVRTR1_DMONOMIX_ENABLE 0x1
+#define FV_CNVRTR1_DMONOMIX_DISABLE 0x0
+#define FV_CNVRTR1_DACMU_ENABLE 0x1
+#define FV_CNVRTR1_DACMU_DISABLE 0x0
+
+/* Register Masks */
+#define RM_CNVRTR1_DACPOLR \
+ RM(FM_CNVRTR1_DACPOLR, FB_CNVRTR1_DACPOLR)
+
+#define RM_CNVRTR1_DACPOLL \
+ RM(FM_CNVRTR1_DACPOLL, FB_CNVRTR1_DACPOLL)
+
+#define RM_CNVRTR1_DMONOMIX \
+ RM(FM_CNVRTR1_DMONOMIX, FB_CNVRTR1_DMONOMIX)
+
+#define RM_CNVRTR1_DACMU \
+ RM(FM_CNVRTR1_DACMU, FB_CNVRTR1_DACMU)
+
+#define RM_CNVRTR1_DEEMPH \
+ RM(FM_CNVRTR1_DEEMPH, FB_CNVRTR1_DEEMPH)
+
+#define RM_CNVRTR1_DACDITH \
+ RM(FM_CNVRTR1_DACDITH, FB_CNVRTR1_DACDITH)
+
+
+/* Register Values */
+#define RV_CNVRTR1_DACPOLR_INVERT \
+ RV(FV_CNVRTR1_DACPOLR_INVERT, FB_CNVRTR1_DACPOLR)
+
+#define RV_CNVRTR1_DACPOLR_NORMAL \
+ RV(FV_CNVRTR1_DACPOLR_NORMAL, FB_CNVRTR1_DACPOLR)
+
+#define RV_CNVRTR1_DACPOLL_INVERT \
+ RV(FV_CNVRTR1_DACPOLL_INVERT, FB_CNVRTR1_DACPOLL)
+
+#define RV_CNVRTR1_DACPOLL_NORMAL \
+ RV(FV_CNVRTR1_DACPOLL_NORMAL, FB_CNVRTR1_DACPOLL)
+
+#define RV_CNVRTR1_DMONOMIX_ENABLE \
+ RV(FV_CNVRTR1_DMONOMIX_ENABLE, FB_CNVRTR1_DMONOMIX)
+
+#define RV_CNVRTR1_DMONOMIX_DISABLE \
+ RV(FV_CNVRTR1_DMONOMIX_DISABLE, FB_CNVRTR1_DMONOMIX)
+
+#define RV_CNVRTR1_DACMU_ENABLE \
+ RV(FV_CNVRTR1_DACMU_ENABLE, FB_CNVRTR1_DACMU)
+
+#define RV_CNVRTR1_DACMU_DISABLE \
+ RV(FV_CNVRTR1_DACMU_DISABLE, FB_CNVRTR1_DACMU)
+
+
+/****************************
+ * R_DACSR (0x19) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_DACSR_DBCM 6
+#define FB_DACSR_DBR 3
+#define FB_DACSR_DBM 0
+
+/* Field Masks */
+#define FM_DACSR_DBCM 0X3
+#define FM_DACSR_DBR 0X3
+#define FM_DACSR_DBM 0X7
+
+/* Field Values */
+#define FV_DACSR_DBCM_AUTO 0x0
+#define FV_DACSR_DBCM_32 0x1
+#define FV_DACSR_DBCM_40 0x2
+#define FV_DACSR_DBCM_64 0x3
+#define FV_DACSR_DBR_32 0x0
+#define FV_DACSR_DBR_44_1 0x1
+#define FV_DACSR_DBR_48 0x2
+#define FV_DACSR_DBM_PT25 0x0
+#define FV_DACSR_DBM_PT5 0x1
+#define FV_DACSR_DBM_1 0x2
+#define FV_DACSR_DBM_2 0x3
+
+/* Register Masks */
+#define RM_DACSR_DBCM RM(FM_DACSR_DBCM, FB_DACSR_DBCM)
+#define RM_DACSR_DBR RM(FM_DACSR_DBR, FB_DACSR_DBR)
+#define RM_DACSR_DBM RM(FM_DACSR_DBM, FB_DACSR_DBM)
+
+/* Register Values */
+#define RV_DACSR_DBCM_AUTO \
+ RV(FV_DACSR_DBCM_AUTO, FB_DACSR_DBCM)
+
+#define RV_DACSR_DBCM_32 \
+ RV(FV_DACSR_DBCM_32, FB_DACSR_DBCM)
+
+#define RV_DACSR_DBCM_40 \
+ RV(FV_DACSR_DBCM_40, FB_DACSR_DBCM)
+
+#define RV_DACSR_DBCM_64 \
+ RV(FV_DACSR_DBCM_64, FB_DACSR_DBCM)
+
+#define RV_DACSR_DBR_32 RV(FV_DACSR_DBR_32, FB_DACSR_DBR)
+#define RV_DACSR_DBR_44_1 \
+ RV(FV_DACSR_DBR_44_1, FB_DACSR_DBR)
+
+#define RV_DACSR_DBR_48 RV(FV_DACSR_DBR_48, FB_DACSR_DBR)
+#define RV_DACSR_DBM_PT25 \
+ RV(FV_DACSR_DBM_PT25, FB_DACSR_DBM)
+
+#define RV_DACSR_DBM_PT5 RV(FV_DACSR_DBM_PT5, FB_DACSR_DBM)
+#define RV_DACSR_DBM_1 RV(FV_DACSR_DBM_1, FB_DACSR_DBM)
+#define RV_DACSR_DBM_2 RV(FV_DACSR_DBM_2, FB_DACSR_DBM)
+
+/****************************
+ * R_PWRM1 (0x1A) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_PWRM1_BSTL 7
+#define FB_PWRM1_BSTR 6
+#define FB_PWRM1_PGAL 5
+#define FB_PWRM1_PGAR 4
+#define FB_PWRM1_ADCL 3
+#define FB_PWRM1_ADCR 2
+#define FB_PWRM1_MICB 1
+#define FB_PWRM1_DIGENB 0
+
+/* Field Masks */
+#define FM_PWRM1_BSTL 0X1
+#define FM_PWRM1_BSTR 0X1
+#define FM_PWRM1_PGAL 0X1
+#define FM_PWRM1_PGAR 0X1
+#define FM_PWRM1_ADCL 0X1
+#define FM_PWRM1_ADCR 0X1
+#define FM_PWRM1_MICB 0X1
+#define FM_PWRM1_DIGENB 0X1
+
+/* Field Values */
+#define FV_PWRM1_BSTL_ENABLE 0x1
+#define FV_PWRM1_BSTL_DISABLE 0x0
+#define FV_PWRM1_BSTR_ENABLE 0x1
+#define FV_PWRM1_BSTR_DISABLE 0x0
+#define FV_PWRM1_PGAL_ENABLE 0x1
+#define FV_PWRM1_PGAL_DISABLE 0x0
+#define FV_PWRM1_PGAR_ENABLE 0x1
+#define FV_PWRM1_PGAR_DISABLE 0x0
+#define FV_PWRM1_ADCL_ENABLE 0x1
+#define FV_PWRM1_ADCL_DISABLE 0x0
+#define FV_PWRM1_ADCR_ENABLE 0x1
+#define FV_PWRM1_ADCR_DISABLE 0x0
+#define FV_PWRM1_MICB_ENABLE 0x1
+#define FV_PWRM1_MICB_DISABLE 0x0
+#define FV_PWRM1_DIGENB_DISABLE 0x1
+#define FV_PWRM1_DIGENB_ENABLE 0x0
+
+/* Register Masks */
+#define RM_PWRM1_BSTL RM(FM_PWRM1_BSTL, FB_PWRM1_BSTL)
+#define RM_PWRM1_BSTR RM(FM_PWRM1_BSTR, FB_PWRM1_BSTR)
+#define RM_PWRM1_PGAL RM(FM_PWRM1_PGAL, FB_PWRM1_PGAL)
+#define RM_PWRM1_PGAR RM(FM_PWRM1_PGAR, FB_PWRM1_PGAR)
+#define RM_PWRM1_ADCL RM(FM_PWRM1_ADCL, FB_PWRM1_ADCL)
+#define RM_PWRM1_ADCR RM(FM_PWRM1_ADCR, FB_PWRM1_ADCR)
+#define RM_PWRM1_MICB RM(FM_PWRM1_MICB, FB_PWRM1_MICB)
+#define RM_PWRM1_DIGENB \
+ RM(FM_PWRM1_DIGENB, FB_PWRM1_DIGENB)
+
+
+/* Register Values */
+#define RV_PWRM1_BSTL_ENABLE \
+ RV(FV_PWRM1_BSTL_ENABLE, FB_PWRM1_BSTL)
+
+#define RV_PWRM1_BSTL_DISABLE \
+ RV(FV_PWRM1_BSTL_DISABLE, FB_PWRM1_BSTL)
+
+#define RV_PWRM1_BSTR_ENABLE \
+ RV(FV_PWRM1_BSTR_ENABLE, FB_PWRM1_BSTR)
+
+#define RV_PWRM1_BSTR_DISABLE \
+ RV(FV_PWRM1_BSTR_DISABLE, FB_PWRM1_BSTR)
+
+#define RV_PWRM1_PGAL_ENABLE \
+ RV(FV_PWRM1_PGAL_ENABLE, FB_PWRM1_PGAL)
+
+#define RV_PWRM1_PGAL_DISABLE \
+ RV(FV_PWRM1_PGAL_DISABLE, FB_PWRM1_PGAL)
+
+#define RV_PWRM1_PGAR_ENABLE \
+ RV(FV_PWRM1_PGAR_ENABLE, FB_PWRM1_PGAR)
+
+#define RV_PWRM1_PGAR_DISABLE \
+ RV(FV_PWRM1_PGAR_DISABLE, FB_PWRM1_PGAR)
+
+#define RV_PWRM1_ADCL_ENABLE \
+ RV(FV_PWRM1_ADCL_ENABLE, FB_PWRM1_ADCL)
+
+#define RV_PWRM1_ADCL_DISABLE \
+ RV(FV_PWRM1_ADCL_DISABLE, FB_PWRM1_ADCL)
+
+#define RV_PWRM1_ADCR_ENABLE \
+ RV(FV_PWRM1_ADCR_ENABLE, FB_PWRM1_ADCR)
+
+#define RV_PWRM1_ADCR_DISABLE \
+ RV(FV_PWRM1_ADCR_DISABLE, FB_PWRM1_ADCR)
+
+#define RV_PWRM1_MICB_ENABLE \
+ RV(FV_PWRM1_MICB_ENABLE, FB_PWRM1_MICB)
+
+#define RV_PWRM1_MICB_DISABLE \
+ RV(FV_PWRM1_MICB_DISABLE, FB_PWRM1_MICB)
+
+#define RV_PWRM1_DIGENB_DISABLE \
+ RV(FV_PWRM1_DIGENB_DISABLE, FB_PWRM1_DIGENB)
+
+#define RV_PWRM1_DIGENB_ENABLE \
+ RV(FV_PWRM1_DIGENB_ENABLE, FB_PWRM1_DIGENB)
+
+
+/****************************
+ * R_PWRM2 (0x1B) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_PWRM2_D2S 7
+#define FB_PWRM2_HPL 6
+#define FB_PWRM2_HPR 5
+#define FB_PWRM2_SPKL 4
+#define FB_PWRM2_SPKR 3
+#define FB_PWRM2_INSELL 2
+#define FB_PWRM2_INSELR 1
+#define FB_PWRM2_VREF 0
+
+/* Field Masks */
+#define FM_PWRM2_D2S 0X1
+#define FM_PWRM2_HPL 0X1
+#define FM_PWRM2_HPR 0X1
+#define FM_PWRM2_SPKL 0X1
+#define FM_PWRM2_SPKR 0X1
+#define FM_PWRM2_INSELL 0X1
+#define FM_PWRM2_INSELR 0X1
+#define FM_PWRM2_VREF 0X1
+
+/* Field Values */
+#define FV_PWRM2_D2S_ENABLE 0x1
+#define FV_PWRM2_D2S_DISABLE 0x0
+#define FV_PWRM2_HPL_ENABLE 0x1
+#define FV_PWRM2_HPL_DISABLE 0x0
+#define FV_PWRM2_HPR_ENABLE 0x1
+#define FV_PWRM2_HPR_DISABLE 0x0
+#define FV_PWRM2_SPKL_ENABLE 0x1
+#define FV_PWRM2_SPKL_DISABLE 0x0
+#define FV_PWRM2_SPKR_ENABLE 0x1
+#define FV_PWRM2_SPKR_DISABLE 0x0
+#define FV_PWRM2_INSELL_ENABLE 0x1
+#define FV_PWRM2_INSELL_DISABLE 0x0
+#define FV_PWRM2_INSELR_ENABLE 0x1
+#define FV_PWRM2_INSELR_DISABLE 0x0
+#define FV_PWRM2_VREF_ENABLE 0x1
+#define FV_PWRM2_VREF_DISABLE 0x0
+
+/* Register Masks */
+#define RM_PWRM2_D2S RM(FM_PWRM2_D2S, FB_PWRM2_D2S)
+#define RM_PWRM2_HPL RM(FM_PWRM2_HPL, FB_PWRM2_HPL)
+#define RM_PWRM2_HPR RM(FM_PWRM2_HPR, FB_PWRM2_HPR)
+#define RM_PWRM2_SPKL RM(FM_PWRM2_SPKL, FB_PWRM2_SPKL)
+#define RM_PWRM2_SPKR RM(FM_PWRM2_SPKR, FB_PWRM2_SPKR)
+#define RM_PWRM2_INSELL \
+ RM(FM_PWRM2_INSELL, FB_PWRM2_INSELL)
+
+#define RM_PWRM2_INSELR \
+ RM(FM_PWRM2_INSELR, FB_PWRM2_INSELR)
+
+#define RM_PWRM2_VREF RM(FM_PWRM2_VREF, FB_PWRM2_VREF)
+
+/* Register Values */
+#define RV_PWRM2_D2S_ENABLE \
+ RV(FV_PWRM2_D2S_ENABLE, FB_PWRM2_D2S)
+
+#define RV_PWRM2_D2S_DISABLE \
+ RV(FV_PWRM2_D2S_DISABLE, FB_PWRM2_D2S)
+
+#define RV_PWRM2_HPL_ENABLE \
+ RV(FV_PWRM2_HPL_ENABLE, FB_PWRM2_HPL)
+
+#define RV_PWRM2_HPL_DISABLE \
+ RV(FV_PWRM2_HPL_DISABLE, FB_PWRM2_HPL)
+
+#define RV_PWRM2_HPR_ENABLE \
+ RV(FV_PWRM2_HPR_ENABLE, FB_PWRM2_HPR)
+
+#define RV_PWRM2_HPR_DISABLE \
+ RV(FV_PWRM2_HPR_DISABLE, FB_PWRM2_HPR)
+
+#define RV_PWRM2_SPKL_ENABLE \
+ RV(FV_PWRM2_SPKL_ENABLE, FB_PWRM2_SPKL)
+
+#define RV_PWRM2_SPKL_DISABLE \
+ RV(FV_PWRM2_SPKL_DISABLE, FB_PWRM2_SPKL)
+
+#define RV_PWRM2_SPKR_ENABLE \
+ RV(FV_PWRM2_SPKR_ENABLE, FB_PWRM2_SPKR)
+
+#define RV_PWRM2_SPKR_DISABLE \
+ RV(FV_PWRM2_SPKR_DISABLE, FB_PWRM2_SPKR)
+
+#define RV_PWRM2_INSELL_ENABLE \
+ RV(FV_PWRM2_INSELL_ENABLE, FB_PWRM2_INSELL)
+
+#define RV_PWRM2_INSELL_DISABLE \
+ RV(FV_PWRM2_INSELL_DISABLE, FB_PWRM2_INSELL)
+
+#define RV_PWRM2_INSELR_ENABLE \
+ RV(FV_PWRM2_INSELR_ENABLE, FB_PWRM2_INSELR)
+
+#define RV_PWRM2_INSELR_DISABLE \
+ RV(FV_PWRM2_INSELR_DISABLE, FB_PWRM2_INSELR)
+
+#define RV_PWRM2_VREF_ENABLE \
+ RV(FV_PWRM2_VREF_ENABLE, FB_PWRM2_VREF)
+
+#define RV_PWRM2_VREF_DISABLE \
+ RV(FV_PWRM2_VREF_DISABLE, FB_PWRM2_VREF)
+
+/******************************
+ * R_CTL (0x1C) *
+ ******************************/
+
+/* Fiel Offsets */
+#define FB_CTL_HPSWEN 7
+#define FB_CTL_HPSWPOL 6
+
+/******************************
+ * R_CONFIG0 (0x1F) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CONFIG0_ASDM 6
+#define FB_CONFIG0_DSDM 4
+#define FB_CONFIG0_DC_BYPASS 1
+#define FB_CONFIG0_SD_FORCE_ON 0
+
+/* Field Masks */
+#define FM_CONFIG0_ASDM 0X3
+#define FM_CONFIG0_DSDM 0X3
+#define FM_CONFIG0_DC_BYPASS 0X1
+#define FM_CONFIG0_SD_FORCE_ON 0X1
+
+/* Field Values */
+#define FV_CONFIG0_ASDM_HALF 0x1
+#define FV_CONFIG0_ASDM_FULL 0x2
+#define FV_CONFIG0_ASDM_AUTO 0x3
+#define FV_CONFIG0_DSDM_HALF 0x1
+#define FV_CONFIG0_DSDM_FULL 0x2
+#define FV_CONFIG0_DSDM_AUTO 0x3
+#define FV_CONFIG0_DC_BYPASS_ENABLE 0x1
+#define FV_CONFIG0_DC_BYPASS_DISABLE 0x0
+#define FV_CONFIG0_SD_FORCE_ON_ENABLE 0x1
+#define FV_CONFIG0_SD_FORCE_ON_DISABLE 0x0
+
+/* Register Masks */
+#define RM_CONFIG0_ASDM \
+ RM(FM_CONFIG0_ASDM, FB_CONFIG0_ASDM)
+
+#define RM_CONFIG0_DSDM \
+ RM(FM_CONFIG0_DSDM, FB_CONFIG0_DSDM)
+
+#define RM_CONFIG0_DC_BYPASS \
+ RM(FM_CONFIG0_DC_BYPASS, FB_CONFIG0_DC_BYPASS)
+
+#define RM_CONFIG0_SD_FORCE_ON \
+ RM(FM_CONFIG0_SD_FORCE_ON, FB_CONFIG0_SD_FORCE_ON)
+
+
+/* Register Values */
+#define RV_CONFIG0_ASDM_HALF \
+ RV(FV_CONFIG0_ASDM_HALF, FB_CONFIG0_ASDM)
+
+#define RV_CONFIG0_ASDM_FULL \
+ RV(FV_CONFIG0_ASDM_FULL, FB_CONFIG0_ASDM)
+
+#define RV_CONFIG0_ASDM_AUTO \
+ RV(FV_CONFIG0_ASDM_AUTO, FB_CONFIG0_ASDM)
+
+#define RV_CONFIG0_DSDM_HALF \
+ RV(FV_CONFIG0_DSDM_HALF, FB_CONFIG0_DSDM)
+
+#define RV_CONFIG0_DSDM_FULL \
+ RV(FV_CONFIG0_DSDM_FULL, FB_CONFIG0_DSDM)
+
+#define RV_CONFIG0_DSDM_AUTO \
+ RV(FV_CONFIG0_DSDM_AUTO, FB_CONFIG0_DSDM)
+
+#define RV_CONFIG0_DC_BYPASS_ENABLE \
+ RV(FV_CONFIG0_DC_BYPASS_ENABLE, FB_CONFIG0_DC_BYPASS)
+
+#define RV_CONFIG0_DC_BYPASS_DISABLE \
+ RV(FV_CONFIG0_DC_BYPASS_DISABLE, FB_CONFIG0_DC_BYPASS)
+
+#define RV_CONFIG0_SD_FORCE_ON_ENABLE \
+ RV(FV_CONFIG0_SD_FORCE_ON_ENABLE, FB_CONFIG0_SD_FORCE_ON)
+
+#define RV_CONFIG0_SD_FORCE_ON_DISABLE \
+ RV(FV_CONFIG0_SD_FORCE_ON_DISABLE, FB_CONFIG0_SD_FORCE_ON)
+
+
+/******************************
+ * R_CONFIG1 (0x20) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CONFIG1_EQ2_EN 7
+#define FB_CONFIG1_EQ2_BE 4
+#define FB_CONFIG1_EQ1_EN 3
+#define FB_CONFIG1_EQ1_BE 0
+
+/* Field Masks */
+#define FM_CONFIG1_EQ2_EN 0X1
+#define FM_CONFIG1_EQ2_BE 0X7
+#define FM_CONFIG1_EQ1_EN 0X1
+#define FM_CONFIG1_EQ1_BE 0X7
+
+/* Field Values */
+#define FV_CONFIG1_EQ2_EN_ENABLE 0x1
+#define FV_CONFIG1_EQ2_EN_DISABLE 0x0
+#define FV_CONFIG1_EQ2_BE_PRE 0x0
+#define FV_CONFIG1_EQ2_BE_PRE_EQ_0 0x1
+#define FV_CONFIG1_EQ2_BE_PRE_EQ0_1 0x2
+#define FV_CONFIG1_EQ2_BE_PRE_EQ0_2 0x3
+#define FV_CONFIG1_EQ2_BE_PRE_EQ0_3 0x4
+#define FV_CONFIG1_EQ2_BE_PRE_EQ0_4 0x5
+#define FV_CONFIG1_EQ2_BE_PRE_EQ0_5 0x6
+#define FV_CONFIG1_EQ1_EN_ENABLE 0x1
+#define FV_CONFIG1_EQ1_EN_DISABLE 0x0
+#define FV_CONFIG1_EQ1_BE_PRE 0x0
+#define FV_CONFIG1_EQ1_BE_PRE_EQ_0 0x1
+#define FV_CONFIG1_EQ1_BE_PRE_EQ0_1 0x2
+#define FV_CONFIG1_EQ1_BE_PRE_EQ0_2 0x3
+#define FV_CONFIG1_EQ1_BE_PRE_EQ0_3 0x4
+#define FV_CONFIG1_EQ1_BE_PRE_EQ0_4 0x5
+#define FV_CONFIG1_EQ1_BE_PRE_EQ0_5 0x6
+
+/* Register Masks */
+#define RM_CONFIG1_EQ2_EN \
+ RM(FM_CONFIG1_EQ2_EN, FB_CONFIG1_EQ2_EN)
+
+#define RM_CONFIG1_EQ2_BE \
+ RM(FM_CONFIG1_EQ2_BE, FB_CONFIG1_EQ2_BE)
+
+#define RM_CONFIG1_EQ1_EN \
+ RM(FM_CONFIG1_EQ1_EN, FB_CONFIG1_EQ1_EN)
+
+#define RM_CONFIG1_EQ1_BE \
+ RM(FM_CONFIG1_EQ1_BE, FB_CONFIG1_EQ1_BE)
+
+
+/* Register Values */
+#define RV_CONFIG1_EQ2_EN_ENABLE \
+ RV(FV_CONFIG1_EQ2_EN_ENABLE, FB_CONFIG1_EQ2_EN)
+
+#define RV_CONFIG1_EQ2_EN_DISABLE \
+ RV(FV_CONFIG1_EQ2_EN_DISABLE, FB_CONFIG1_EQ2_EN)
+
+#define RV_CONFIG1_EQ2_BE_PRE \
+ RV(FV_CONFIG1_EQ2_BE_PRE, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ2_BE_PRE_EQ_0 \
+ RV(FV_CONFIG1_EQ2_BE_PRE_EQ_0, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ2_BE_PRE_EQ0_1 \
+ RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_1, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ2_BE_PRE_EQ0_2 \
+ RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_2, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ2_BE_PRE_EQ0_3 \
+ RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_3, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ2_BE_PRE_EQ0_4 \
+ RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_4, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ2_BE_PRE_EQ0_5 \
+ RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_5, FB_CONFIG1_EQ2_BE)
+
+#define RV_CONFIG1_EQ1_EN_ENABLE \
+ RV(FV_CONFIG1_EQ1_EN_ENABLE, FB_CONFIG1_EQ1_EN)
+
+#define RV_CONFIG1_EQ1_EN_DISABLE \
+ RV(FV_CONFIG1_EQ1_EN_DISABLE, FB_CONFIG1_EQ1_EN)
+
+#define RV_CONFIG1_EQ1_BE_PRE \
+ RV(FV_CONFIG1_EQ1_BE_PRE, FB_CONFIG1_EQ1_BE)
+
+#define RV_CONFIG1_EQ1_BE_PRE_EQ_0 \
+ RV(FV_CONFIG1_EQ1_BE_PRE_EQ_0, FB_CONFIG1_EQ1_BE)
+
+#define RV_CONFIG1_EQ1_BE_PRE_EQ0_1 \
+ RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_1, FB_CONFIG1_EQ1_BE)
+
+#define RV_CONFIG1_EQ1_BE_PRE_EQ0_2 \
+ RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_2, FB_CONFIG1_EQ1_BE)
+
+#define RV_CONFIG1_EQ1_BE_PRE_EQ0_3 \
+ RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_3, FB_CONFIG1_EQ1_BE)
+
+#define RV_CONFIG1_EQ1_BE_PRE_EQ0_4 \
+ RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_4, FB_CONFIG1_EQ1_BE)
+
+#define RV_CONFIG1_EQ1_BE_PRE_EQ0_5 \
+ RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_5, FB_CONFIG1_EQ1_BE)
+
+
+/******************************
+ * R_DMICCTL (0x24) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_DMICCTL_DMICEN 7
+#define FB_DMICCTL_DMONO 4
+#define FB_DMICCTL_DMPHADJ 2
+#define FB_DMICCTL_DMRATE 0
+
+/* Field Masks */
+#define FM_DMICCTL_DMICEN 0X1
+#define FM_DMICCTL_DMONO 0X1
+#define FM_DMICCTL_DMPHADJ 0X3
+#define FM_DMICCTL_DMRATE 0X3
+
+/* Field Values */
+#define FV_DMICCTL_DMICEN_ENABLE 0x1
+#define FV_DMICCTL_DMICEN_DISABLE 0x0
+#define FV_DMICCTL_DMONO_STEREO 0x0
+#define FV_DMICCTL_DMONO_MONO 0x1
+
+/* Register Masks */
+#define RM_DMICCTL_DMICEN \
+ RM(FM_DMICCTL_DMICEN, FB_DMICCTL_DMICEN)
+
+#define RM_DMICCTL_DMONO \
+ RM(FM_DMICCTL_DMONO, FB_DMICCTL_DMONO)
+
+#define RM_DMICCTL_DMPHADJ \
+ RM(FM_DMICCTL_DMPHADJ, FB_DMICCTL_DMPHADJ)
+
+#define RM_DMICCTL_DMRATE \
+ RM(FM_DMICCTL_DMRATE, FB_DMICCTL_DMRATE)
+
+
+/* Register Values */
+#define RV_DMICCTL_DMICEN_ENABLE \
+ RV(FV_DMICCTL_DMICEN_ENABLE, FB_DMICCTL_DMICEN)
+
+#define RV_DMICCTL_DMICEN_DISABLE \
+ RV(FV_DMICCTL_DMICEN_DISABLE, FB_DMICCTL_DMICEN)
+
+#define RV_DMICCTL_DMONO_STEREO \
+ RV(FV_DMICCTL_DMONO_STEREO, FB_DMICCTL_DMONO)
+
+#define RV_DMICCTL_DMONO_MONO \
+ RV(FV_DMICCTL_DMONO_MONO, FB_DMICCTL_DMONO)
+
+
+/*****************************
+ * R_CLECTL (0x25) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_CLECTL_LVL_MODE 4
+#define FB_CLECTL_WINDOWSEL 3
+#define FB_CLECTL_EXP_EN 2
+#define FB_CLECTL_LIMIT_EN 1
+#define FB_CLECTL_COMP_EN 0
+
+/* Field Masks */
+#define FM_CLECTL_LVL_MODE 0X1
+#define FM_CLECTL_WINDOWSEL 0X1
+#define FM_CLECTL_EXP_EN 0X1
+#define FM_CLECTL_LIMIT_EN 0X1
+#define FM_CLECTL_COMP_EN 0X1
+
+/* Field Values */
+#define FV_CLECTL_LVL_MODE_AVG 0x0
+#define FV_CLECTL_LVL_MODE_PEAK 0x1
+#define FV_CLECTL_WINDOWSEL_512 0x0
+#define FV_CLECTL_WINDOWSEL_64 0x1
+#define FV_CLECTL_EXP_EN_ENABLE 0x1
+#define FV_CLECTL_EXP_EN_DISABLE 0x0
+#define FV_CLECTL_LIMIT_EN_ENABLE 0x1
+#define FV_CLECTL_LIMIT_EN_DISABLE 0x0
+#define FV_CLECTL_COMP_EN_ENABLE 0x1
+#define FV_CLECTL_COMP_EN_DISABLE 0x0
+
+/* Register Masks */
+#define RM_CLECTL_LVL_MODE \
+ RM(FM_CLECTL_LVL_MODE, FB_CLECTL_LVL_MODE)
+
+#define RM_CLECTL_WINDOWSEL \
+ RM(FM_CLECTL_WINDOWSEL, FB_CLECTL_WINDOWSEL)
+
+#define RM_CLECTL_EXP_EN \
+ RM(FM_CLECTL_EXP_EN, FB_CLECTL_EXP_EN)
+
+#define RM_CLECTL_LIMIT_EN \
+ RM(FM_CLECTL_LIMIT_EN, FB_CLECTL_LIMIT_EN)
+
+#define RM_CLECTL_COMP_EN \
+ RM(FM_CLECTL_COMP_EN, FB_CLECTL_COMP_EN)
+
+
+/* Register Values */
+#define RV_CLECTL_LVL_MODE_AVG \
+ RV(FV_CLECTL_LVL_MODE_AVG, FB_CLECTL_LVL_MODE)
+
+#define RV_CLECTL_LVL_MODE_PEAK \
+ RV(FV_CLECTL_LVL_MODE_PEAK, FB_CLECTL_LVL_MODE)
+
+#define RV_CLECTL_WINDOWSEL_512 \
+ RV(FV_CLECTL_WINDOWSEL_512, FB_CLECTL_WINDOWSEL)
+
+#define RV_CLECTL_WINDOWSEL_64 \
+ RV(FV_CLECTL_WINDOWSEL_64, FB_CLECTL_WINDOWSEL)
+
+#define RV_CLECTL_EXP_EN_ENABLE \
+ RV(FV_CLECTL_EXP_EN_ENABLE, FB_CLECTL_EXP_EN)
+
+#define RV_CLECTL_EXP_EN_DISABLE \
+ RV(FV_CLECTL_EXP_EN_DISABLE, FB_CLECTL_EXP_EN)
+
+#define RV_CLECTL_LIMIT_EN_ENABLE \
+ RV(FV_CLECTL_LIMIT_EN_ENABLE, FB_CLECTL_LIMIT_EN)
+
+#define RV_CLECTL_LIMIT_EN_DISABLE \
+ RV(FV_CLECTL_LIMIT_EN_DISABLE, FB_CLECTL_LIMIT_EN)
+
+#define RV_CLECTL_COMP_EN_ENABLE \
+ RV(FV_CLECTL_COMP_EN_ENABLE, FB_CLECTL_COMP_EN)
+
+#define RV_CLECTL_COMP_EN_DISABLE \
+ RV(FV_CLECTL_COMP_EN_DISABLE, FB_CLECTL_COMP_EN)
+
+
+/*****************************
+ * R_MUGAIN (0x26) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_MUGAIN_CLEMUG 0
+
+/* Field Masks */
+#define FM_MUGAIN_CLEMUG 0X1F
+
+/* Field Values */
+#define FV_MUGAIN_CLEMUG_46PT5DB 0x1F
+#define FV_MUGAIN_CLEMUG_0DB 0x0
+
+/* Register Masks */
+#define RM_MUGAIN_CLEMUG \
+ RM(FM_MUGAIN_CLEMUG, FB_MUGAIN_CLEMUG)
+
+
+/* Register Values */
+#define RV_MUGAIN_CLEMUG_46PT5DB \
+ RV(FV_MUGAIN_CLEMUG_46PT5DB, FB_MUGAIN_CLEMUG)
+
+#define RV_MUGAIN_CLEMUG_0DB \
+ RV(FV_MUGAIN_CLEMUG_0DB, FB_MUGAIN_CLEMUG)
+
+
+/*****************************
+ * R_COMPTH (0x27) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_COMPTH 0
+
+/* Field Masks */
+#define FM_COMPTH 0XFF
+
+/* Field Values */
+#define FV_COMPTH_0DB 0xFF
+#define FV_COMPTH_N95PT625DB 0x0
+
+/* Register Masks */
+#define RM_COMPTH RM(FM_COMPTH, FB_COMPTH)
+
+/* Register Values */
+#define RV_COMPTH_0DB RV(FV_COMPTH_0DB, FB_COMPTH)
+#define RV_COMPTH_N95PT625DB \
+ RV(FV_COMPTH_N95PT625DB, FB_COMPTH)
+
+
+/*****************************
+ * R_CMPRAT (0x28) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_CMPRAT 0
+
+/* Field Masks */
+#define FM_CMPRAT 0X1F
+
+/* Register Masks */
+#define RM_CMPRAT RM(FM_CMPRAT, FB_CMPRAT)
+
+/******************************
+ * R_CATKTCL (0x29) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CATKTCL 0
+
+/* Field Masks */
+#define FM_CATKTCL 0XFF
+
+/* Register Masks */
+#define RM_CATKTCL RM(FM_CATKTCL, FB_CATKTCL)
+
+/******************************
+ * R_CATKTCH (0x2A) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CATKTCH 0
+
+/* Field Masks */
+#define FM_CATKTCH 0XFF
+
+/* Register Masks */
+#define RM_CATKTCH RM(FM_CATKTCH, FB_CATKTCH)
+
+/******************************
+ * R_CRELTCL (0x2B) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CRELTCL 0
+
+/* Field Masks */
+#define FM_CRELTCL 0XFF
+
+/* Register Masks */
+#define RM_CRELTCL RM(FM_CRELTCL, FB_CRELTCL)
+
+/******************************
+ * R_CRELTCH (0x2C) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_CRELTCH 0
+
+/* Field Masks */
+#define FM_CRELTCH 0XFF
+
+/* Register Masks */
+#define RM_CRELTCH RM(FM_CRELTCH, FB_CRELTCH)
+
+/****************************
+ * R_LIMTH (0x2D) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_LIMTH 0
+
+/* Field Masks */
+#define FM_LIMTH 0XFF
+
+/* Field Values */
+#define FV_LIMTH_0DB 0xFF
+#define FV_LIMTH_N95PT625DB 0x0
+
+/* Register Masks */
+#define RM_LIMTH RM(FM_LIMTH, FB_LIMTH)
+
+/* Register Values */
+#define RV_LIMTH_0DB RV(FV_LIMTH_0DB, FB_LIMTH)
+#define RV_LIMTH_N95PT625DB RV(FV_LIMTH_N95PT625DB, FB_LIMTH)
+
+/*****************************
+ * R_LIMTGT (0x2E) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_LIMTGT 0
+
+/* Field Masks */
+#define FM_LIMTGT 0XFF
+
+/* Field Values */
+#define FV_LIMTGT_0DB 0xFF
+#define FV_LIMTGT_N95PT625DB 0x0
+
+/* Register Masks */
+#define RM_LIMTGT RM(FM_LIMTGT, FB_LIMTGT)
+
+/* Register Values */
+#define RV_LIMTGT_0DB RV(FV_LIMTGT_0DB, FB_LIMTGT)
+#define RV_LIMTGT_N95PT625DB \
+ RV(FV_LIMTGT_N95PT625DB, FB_LIMTGT)
+
+
+/******************************
+ * R_LATKTCL (0x2F) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_LATKTCL 0
+
+/* Field Masks */
+#define FM_LATKTCL 0XFF
+
+/* Register Masks */
+#define RM_LATKTCL RM(FM_LATKTCL, FB_LATKTCL)
+
+/******************************
+ * R_LATKTCH (0x30) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_LATKTCH 0
+
+/* Field Masks */
+#define FM_LATKTCH 0XFF
+
+/* Register Masks */
+#define RM_LATKTCH RM(FM_LATKTCH, FB_LATKTCH)
+
+/******************************
+ * R_LRELTCL (0x31) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_LRELTCL 0
+
+/* Field Masks */
+#define FM_LRELTCL 0XFF
+
+/* Register Masks */
+#define RM_LRELTCL RM(FM_LRELTCL, FB_LRELTCL)
+
+/******************************
+ * R_LRELTCH (0x32) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_LRELTCH 0
+
+/* Field Masks */
+#define FM_LRELTCH 0XFF
+
+/* Register Masks */
+#define RM_LRELTCH RM(FM_LRELTCH, FB_LRELTCH)
+
+/****************************
+ * R_EXPTH (0x33) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_EXPTH 0
+
+/* Field Masks */
+#define FM_EXPTH 0XFF
+
+/* Field Values */
+#define FV_EXPTH_0DB 0xFF
+#define FV_EXPTH_N95PT625DB 0x0
+
+/* Register Masks */
+#define RM_EXPTH RM(FM_EXPTH, FB_EXPTH)
+
+/* Register Values */
+#define RV_EXPTH_0DB RV(FV_EXPTH_0DB, FB_EXPTH)
+#define RV_EXPTH_N95PT625DB RV(FV_EXPTH_N95PT625DB, FB_EXPTH)
+
+/*****************************
+ * R_EXPRAT (0x34) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_EXPRAT 0
+
+/* Field Masks */
+#define FM_EXPRAT 0X7
+
+/* Register Masks */
+#define RM_EXPRAT RM(FM_EXPRAT, FB_EXPRAT)
+
+/******************************
+ * R_XATKTCL (0x35) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_XATKTCL 0
+
+/* Field Masks */
+#define FM_XATKTCL 0XFF
+
+/* Register Masks */
+#define RM_XATKTCL RM(FM_XATKTCL, FB_XATKTCL)
+
+/******************************
+ * R_XATKTCH (0x36) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_XATKTCH 0
+
+/* Field Masks */
+#define FM_XATKTCH 0XFF
+
+/* Register Masks */
+#define RM_XATKTCH RM(FM_XATKTCH, FB_XATKTCH)
+
+/******************************
+ * R_XRELTCL (0x37) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_XRELTCL 0
+
+/* Field Masks */
+#define FM_XRELTCL 0XFF
+
+/* Register Masks */
+#define RM_XRELTCL RM(FM_XRELTCL, FB_XRELTCL)
+
+/******************************
+ * R_XRELTCH (0x38) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_XRELTCH 0
+
+/* Field Masks */
+#define FM_XRELTCH 0XFF
+
+/* Register Masks */
+#define RM_XRELTCH RM(FM_XRELTCH, FB_XRELTCH)
+
+/****************************
+ * R_FXCTL (0x39) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_FXCTL_3DEN 4
+#define FB_FXCTL_TEEN 3
+#define FB_FXCTL_TNLFBYPASS 2
+#define FB_FXCTL_BEEN 1
+#define FB_FXCTL_BNLFBYPASS 0
+
+/* Field Masks */
+#define FM_FXCTL_3DEN 0X1
+#define FM_FXCTL_TEEN 0X1
+#define FM_FXCTL_TNLFBYPASS 0X1
+#define FM_FXCTL_BEEN 0X1
+#define FM_FXCTL_BNLFBYPASS 0X1
+
+/* Field Values */
+#define FV_FXCTL_3DEN_ENABLE 0x1
+#define FV_FXCTL_3DEN_DISABLE 0x0
+#define FV_FXCTL_TEEN_ENABLE 0x1
+#define FV_FXCTL_TEEN_DISABLE 0x0
+#define FV_FXCTL_TNLFBYPASS_ENABLE 0x1
+#define FV_FXCTL_TNLFBYPASS_DISABLE 0x0
+#define FV_FXCTL_BEEN_ENABLE 0x1
+#define FV_FXCTL_BEEN_DISABLE 0x0
+#define FV_FXCTL_BNLFBYPASS_ENABLE 0x1
+#define FV_FXCTL_BNLFBYPASS_DISABLE 0x0
+
+/* Register Masks */
+#define RM_FXCTL_3DEN RM(FM_FXCTL_3DEN, FB_FXCTL_3DEN)
+#define RM_FXCTL_TEEN RM(FM_FXCTL_TEEN, FB_FXCTL_TEEN)
+#define RM_FXCTL_TNLFBYPASS \
+ RM(FM_FXCTL_TNLFBYPASS, FB_FXCTL_TNLFBYPASS)
+
+#define RM_FXCTL_BEEN RM(FM_FXCTL_BEEN, FB_FXCTL_BEEN)
+#define RM_FXCTL_BNLFBYPASS \
+ RM(FM_FXCTL_BNLFBYPASS, FB_FXCTL_BNLFBYPASS)
+
+
+/* Register Values */
+#define RV_FXCTL_3DEN_ENABLE \
+ RV(FV_FXCTL_3DEN_ENABLE, FB_FXCTL_3DEN)
+
+#define RV_FXCTL_3DEN_DISABLE \
+ RV(FV_FXCTL_3DEN_DISABLE, FB_FXCTL_3DEN)
+
+#define RV_FXCTL_TEEN_ENABLE \
+ RV(FV_FXCTL_TEEN_ENABLE, FB_FXCTL_TEEN)
+
+#define RV_FXCTL_TEEN_DISABLE \
+ RV(FV_FXCTL_TEEN_DISABLE, FB_FXCTL_TEEN)
+
+#define RV_FXCTL_TNLFBYPASS_ENABLE \
+ RV(FV_FXCTL_TNLFBYPASS_ENABLE, FB_FXCTL_TNLFBYPASS)
+
+#define RV_FXCTL_TNLFBYPASS_DISABLE \
+ RV(FV_FXCTL_TNLFBYPASS_DISABLE, FB_FXCTL_TNLFBYPASS)
+
+#define RV_FXCTL_BEEN_ENABLE \
+ RV(FV_FXCTL_BEEN_ENABLE, FB_FXCTL_BEEN)
+
+#define RV_FXCTL_BEEN_DISABLE \
+ RV(FV_FXCTL_BEEN_DISABLE, FB_FXCTL_BEEN)
+
+#define RV_FXCTL_BNLFBYPASS_ENABLE \
+ RV(FV_FXCTL_BNLFBYPASS_ENABLE, FB_FXCTL_BNLFBYPASS)
+
+#define RV_FXCTL_BNLFBYPASS_DISABLE \
+ RV(FV_FXCTL_BNLFBYPASS_DISABLE, FB_FXCTL_BNLFBYPASS)
+
+
+/*******************************
+ * R_DACCRWRL (0x3A) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACCRWRL_DACCRWDL 0
+
+/* Field Masks */
+#define FM_DACCRWRL_DACCRWDL 0XFF
+
+/* Register Masks */
+#define RM_DACCRWRL_DACCRWDL \
+ RM(FM_DACCRWRL_DACCRWDL, FB_DACCRWRL_DACCRWDL)
+
+
+/*******************************
+ * R_DACCRWRM (0x3B) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACCRWRM_DACCRWDM 0
+
+/* Field Masks */
+#define FM_DACCRWRM_DACCRWDM 0XFF
+
+/* Register Masks */
+#define RM_DACCRWRM_DACCRWDM \
+ RM(FM_DACCRWRM_DACCRWDM, FB_DACCRWRM_DACCRWDM)
+
+
+/*******************************
+ * R_DACCRWRH (0x3C) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACCRWRH_DACCRWDH 0
+
+/* Field Masks */
+#define FM_DACCRWRH_DACCRWDH 0XFF
+
+/* Register Masks */
+#define RM_DACCRWRH_DACCRWDH \
+ RM(FM_DACCRWRH_DACCRWDH, FB_DACCRWRH_DACCRWDH)
+
+
+/*******************************
+ * R_DACCRRDL (0x3D) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACCRRDL 0
+
+/* Field Masks */
+#define FM_DACCRRDL 0XFF
+
+/* Register Masks */
+#define RM_DACCRRDL RM(FM_DACCRRDL, FB_DACCRRDL)
+
+/*******************************
+ * R_DACCRRDM (0x3E) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACCRRDM 0
+
+/* Field Masks */
+#define FM_DACCRRDM 0XFF
+
+/* Register Masks */
+#define RM_DACCRRDM RM(FM_DACCRRDM, FB_DACCRRDM)
+
+/*******************************
+ * R_DACCRRDH (0x3F) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACCRRDH 0
+
+/* Field Masks */
+#define FM_DACCRRDH 0XFF
+
+/* Register Masks */
+#define RM_DACCRRDH RM(FM_DACCRRDH, FB_DACCRRDH)
+
+/********************************
+ * R_DACCRADDR (0x40) *
+ ********************************/
+
+/* Field Offsets */
+#define FB_DACCRADDR_DACCRADD 0
+
+/* Field Masks */
+#define FM_DACCRADDR_DACCRADD 0XFF
+
+/* Register Masks */
+#define RM_DACCRADDR_DACCRADD \
+ RM(FM_DACCRADDR_DACCRADD, FB_DACCRADDR_DACCRADD)
+
+
+/******************************
+ * R_DCOFSEL (0x41) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_DCOFSEL_DC_COEF_SEL 0
+
+/* Field Masks */
+#define FM_DCOFSEL_DC_COEF_SEL 0X7
+
+/* Field Values */
+#define FV_DCOFSEL_DC_COEF_SEL_2_N8 0x0
+#define FV_DCOFSEL_DC_COEF_SEL_2_N9 0x1
+#define FV_DCOFSEL_DC_COEF_SEL_2_N10 0x2
+#define FV_DCOFSEL_DC_COEF_SEL_2_N11 0x3
+#define FV_DCOFSEL_DC_COEF_SEL_2_N12 0x4
+#define FV_DCOFSEL_DC_COEF_SEL_2_N13 0x5
+#define FV_DCOFSEL_DC_COEF_SEL_2_N14 0x6
+#define FV_DCOFSEL_DC_COEF_SEL_2_N15 0x7
+
+/* Register Masks */
+#define RM_DCOFSEL_DC_COEF_SEL \
+ RM(FM_DCOFSEL_DC_COEF_SEL, FB_DCOFSEL_DC_COEF_SEL)
+
+
+/* Register Values */
+#define RV_DCOFSEL_DC_COEF_SEL_2_N8 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N8, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N9 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N9, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N10 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N10, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N11 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N11, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N12 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N12, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N13 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N13, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N14 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N14, FB_DCOFSEL_DC_COEF_SEL)
+
+#define RV_DCOFSEL_DC_COEF_SEL_2_N15 \
+ RV(FV_DCOFSEL_DC_COEF_SEL_2_N15, FB_DCOFSEL_DC_COEF_SEL)
+
+
+/******************************
+ * R_PLLCTL9 (0x4E) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL9_REFDIV_PLL1 0
+
+/* Field Masks */
+#define FM_PLLCTL9_REFDIV_PLL1 0XFF
+
+/* Register Masks */
+#define RM_PLLCTL9_REFDIV_PLL1 \
+ RM(FM_PLLCTL9_REFDIV_PLL1, FB_PLLCTL9_REFDIV_PLL1)
+
+
+/******************************
+ * R_PLLCTLA (0x4F) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTLA_OUTDIV_PLL1 0
+
+/* Field Masks */
+#define FM_PLLCTLA_OUTDIV_PLL1 0XFF
+
+/* Register Masks */
+#define RM_PLLCTLA_OUTDIV_PLL1 \
+ RM(FM_PLLCTLA_OUTDIV_PLL1, FB_PLLCTLA_OUTDIV_PLL1)
+
+
+/******************************
+ * R_PLLCTLB (0x50) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTLB_FBDIV_PLL1L 0
+
+/* Field Masks */
+#define FM_PLLCTLB_FBDIV_PLL1L 0XFF
+
+/* Register Masks */
+#define RM_PLLCTLB_FBDIV_PLL1L \
+ RM(FM_PLLCTLB_FBDIV_PLL1L, FB_PLLCTLB_FBDIV_PLL1L)
+
+
+/******************************
+ * R_PLLCTLC (0x51) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTLC_FBDIV_PLL1H 0
+
+/* Field Masks */
+#define FM_PLLCTLC_FBDIV_PLL1H 0X7
+
+/* Register Masks */
+#define RM_PLLCTLC_FBDIV_PLL1H \
+ RM(FM_PLLCTLC_FBDIV_PLL1H, FB_PLLCTLC_FBDIV_PLL1H)
+
+
+/******************************
+ * R_PLLCTLD (0x52) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTLD_RZ_PLL1 3
+#define FB_PLLCTLD_CP_PLL1 0
+
+/* Field Masks */
+#define FM_PLLCTLD_RZ_PLL1 0X7
+#define FM_PLLCTLD_CP_PLL1 0X7
+
+/* Register Masks */
+#define RM_PLLCTLD_RZ_PLL1 \
+ RM(FM_PLLCTLD_RZ_PLL1, FB_PLLCTLD_RZ_PLL1)
+
+#define RM_PLLCTLD_CP_PLL1 \
+ RM(FM_PLLCTLD_CP_PLL1, FB_PLLCTLD_CP_PLL1)
+
+
+/******************************
+ * R_PLLCTLE (0x53) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTLE_REFDIV_PLL2 0
+
+/* Field Masks */
+#define FM_PLLCTLE_REFDIV_PLL2 0XFF
+
+/* Register Masks */
+#define RM_PLLCTLE_REFDIV_PLL2 \
+ RM(FM_PLLCTLE_REFDIV_PLL2, FB_PLLCTLE_REFDIV_PLL2)
+
+
+/******************************
+ * R_PLLCTLF (0x54) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTLF_OUTDIV_PLL2 0
+
+/* Field Masks */
+#define FM_PLLCTLF_OUTDIV_PLL2 0XFF
+
+/* Register Masks */
+#define RM_PLLCTLF_OUTDIV_PLL2 \
+ RM(FM_PLLCTLF_OUTDIV_PLL2, FB_PLLCTLF_OUTDIV_PLL2)
+
+
+/*******************************
+ * R_PLLCTL10 (0x55) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL10_FBDIV_PLL2L 0
+
+/* Field Masks */
+#define FM_PLLCTL10_FBDIV_PLL2L 0XFF
+
+/* Register Masks */
+#define RM_PLLCTL10_FBDIV_PLL2L \
+ RM(FM_PLLCTL10_FBDIV_PLL2L, FB_PLLCTL10_FBDIV_PLL2L)
+
+
+/*******************************
+ * R_PLLCTL11 (0x56) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL11_FBDIV_PLL2H 0
+
+/* Field Masks */
+#define FM_PLLCTL11_FBDIV_PLL2H 0X7
+
+/* Register Masks */
+#define RM_PLLCTL11_FBDIV_PLL2H \
+ RM(FM_PLLCTL11_FBDIV_PLL2H, FB_PLLCTL11_FBDIV_PLL2H)
+
+
+/*******************************
+ * R_PLLCTL12 (0x57) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL12_RZ_PLL2 3
+#define FB_PLLCTL12_CP_PLL2 0
+
+/* Field Masks */
+#define FM_PLLCTL12_RZ_PLL2 0X7
+#define FM_PLLCTL12_CP_PLL2 0X7
+
+/* Register Masks */
+#define RM_PLLCTL12_RZ_PLL2 \
+ RM(FM_PLLCTL12_RZ_PLL2, FB_PLLCTL12_RZ_PLL2)
+
+#define RM_PLLCTL12_CP_PLL2 \
+ RM(FM_PLLCTL12_CP_PLL2, FB_PLLCTL12_CP_PLL2)
+
+
+/*******************************
+ * R_PLLCTL1B (0x60) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL1B_VCOI_PLL2 4
+#define FB_PLLCTL1B_VCOI_PLL1 2
+
+/* Field Masks */
+#define FM_PLLCTL1B_VCOI_PLL2 0X3
+#define FM_PLLCTL1B_VCOI_PLL1 0X3
+
+/* Register Masks */
+#define RM_PLLCTL1B_VCOI_PLL2 \
+ RM(FM_PLLCTL1B_VCOI_PLL2, FB_PLLCTL1B_VCOI_PLL2)
+
+#define RM_PLLCTL1B_VCOI_PLL1 \
+ RM(FM_PLLCTL1B_VCOI_PLL1, FB_PLLCTL1B_VCOI_PLL1)
+
+
+/*******************************
+ * R_PLLCTL1C (0x61) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL1C_PDB_PLL2 2
+#define FB_PLLCTL1C_PDB_PLL1 1
+
+/* Field Masks */
+#define FM_PLLCTL1C_PDB_PLL2 0X1
+#define FM_PLLCTL1C_PDB_PLL1 0X1
+
+/* Field Values */
+#define FV_PLLCTL1C_PDB_PLL2_ENABLE 0x1
+#define FV_PLLCTL1C_PDB_PLL2_DISABLE 0x0
+#define FV_PLLCTL1C_PDB_PLL1_ENABLE 0x1
+#define FV_PLLCTL1C_PDB_PLL1_DISABLE 0x0
+
+/* Register Masks */
+#define RM_PLLCTL1C_PDB_PLL2 \
+ RM(FM_PLLCTL1C_PDB_PLL2, FB_PLLCTL1C_PDB_PLL2)
+
+#define RM_PLLCTL1C_PDB_PLL1 \
+ RM(FM_PLLCTL1C_PDB_PLL1, FB_PLLCTL1C_PDB_PLL1)
+
+
+/* Register Values */
+#define RV_PLLCTL1C_PDB_PLL2_ENABLE \
+ RV(FV_PLLCTL1C_PDB_PLL2_ENABLE, FB_PLLCTL1C_PDB_PLL2)
+
+#define RV_PLLCTL1C_PDB_PLL2_DISABLE \
+ RV(FV_PLLCTL1C_PDB_PLL2_DISABLE, FB_PLLCTL1C_PDB_PLL2)
+
+#define RV_PLLCTL1C_PDB_PLL1_ENABLE \
+ RV(FV_PLLCTL1C_PDB_PLL1_ENABLE, FB_PLLCTL1C_PDB_PLL1)
+
+#define RV_PLLCTL1C_PDB_PLL1_DISABLE \
+ RV(FV_PLLCTL1C_PDB_PLL1_DISABLE, FB_PLLCTL1C_PDB_PLL1)
+
+
+/*******************************
+ * R_TIMEBASE (0x77) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_TIMEBASE_DIVIDER 0
+
+/* Field Masks */
+#define FM_TIMEBASE_DIVIDER 0XFF
+
+/* Register Masks */
+#define RM_TIMEBASE_DIVIDER \
+ RM(FM_TIMEBASE_DIVIDER, FB_TIMEBASE_DIVIDER)
+
+
+/*****************************
+ * R_DEVIDL (0x7D) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_DEVIDL_DIDL 0
+
+/* Field Masks */
+#define FM_DEVIDL_DIDL 0XFF
+
+/* Register Masks */
+#define RM_DEVIDL_DIDL RM(FM_DEVIDL_DIDL, FB_DEVIDL_DIDL)
+
+/*****************************
+ * R_DEVIDH (0x7E) *
+ *****************************/
+
+/* Field Offsets */
+#define FB_DEVIDH_DIDH 0
+
+/* Field Masks */
+#define FM_DEVIDH_DIDH 0XFF
+
+/* Register Masks */
+#define RM_DEVIDH_DIDH RM(FM_DEVIDH_DIDH, FB_DEVIDH_DIDH)
+
+/****************************
+ * R_RESET (0x80) *
+ ****************************/
+
+/* Field Offsets */
+#define FB_RESET 0
+
+/* Field Masks */
+#define FM_RESET 0XFF
+
+/* Field Values */
+#define FV_RESET_ENABLE 0x85
+
+/* Register Masks */
+#define RM_RESET RM(FM_RESET, FB_RESET)
+
+/* Register Values */
+#define RV_RESET_ENABLE RV(FV_RESET_ENABLE, FB_RESET)
+
+/********************************
+ * R_DACCRSTAT (0x8A) *
+ ********************************/
+
+/* Field Offsets */
+#define FB_DACCRSTAT_DACCR_BUSY 7
+
+/* Field Masks */
+#define FM_DACCRSTAT_DACCR_BUSY 0X1
+
+/* Register Masks */
+#define RM_DACCRSTAT_DACCR_BUSY \
+ RM(FM_DACCRSTAT_DACCR_BUSY, FB_DACCRSTAT_DACCR_BUSY)
+
+
+/******************************
+ * R_PLLCTL0 (0x8E) *
+ ******************************/
+
+/* Field Offsets */
+#define FB_PLLCTL0_PLL2_LOCK 1
+#define FB_PLLCTL0_PLL1_LOCK 0
+
+/* Field Masks */
+#define FM_PLLCTL0_PLL2_LOCK 0X1
+#define FM_PLLCTL0_PLL1_LOCK 0X1
+
+/* Register Masks */
+#define RM_PLLCTL0_PLL2_LOCK \
+ RM(FM_PLLCTL0_PLL2_LOCK, FB_PLLCTL0_PLL2_LOCK)
+
+#define RM_PLLCTL0_PLL1_LOCK \
+ RM(FM_PLLCTL0_PLL1_LOCK, FB_PLLCTL0_PLL1_LOCK)
+
+
+/********************************
+ * R_PLLREFSEL (0x8F) *
+ ********************************/
+
+/* Field Offsets */
+#define FB_PLLREFSEL_PLL2_REF_SEL 4
+#define FB_PLLREFSEL_PLL1_REF_SEL 0
+
+/* Field Masks */
+#define FM_PLLREFSEL_PLL2_REF_SEL 0X7
+#define FM_PLLREFSEL_PLL1_REF_SEL 0X7
+
+/* Field Values */
+#define FV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1 0x0
+#define FV_PLLREFSEL_PLL2_REF_SEL_MCLK2 0x1
+#define FV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 0x0
+#define FV_PLLREFSEL_PLL1_REF_SEL_MCLK2 0x1
+
+/* Register Masks */
+#define RM_PLLREFSEL_PLL2_REF_SEL \
+ RM(FM_PLLREFSEL_PLL2_REF_SEL, FB_PLLREFSEL_PLL2_REF_SEL)
+
+#define RM_PLLREFSEL_PLL1_REF_SEL \
+ RM(FM_PLLREFSEL_PLL1_REF_SEL, FB_PLLREFSEL_PLL1_REF_SEL)
+
+
+/* Register Values */
+#define RV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1 \
+ RV(FV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1, FB_PLLREFSEL_PLL2_REF_SEL)
+
+#define RV_PLLREFSEL_PLL2_REF_SEL_MCLK2 \
+ RV(FV_PLLREFSEL_PLL2_REF_SEL_MCLK2, FB_PLLREFSEL_PLL2_REF_SEL)
+
+#define RV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 \
+ RV(FV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1, FB_PLLREFSEL_PLL1_REF_SEL)
+
+#define RV_PLLREFSEL_PLL1_REF_SEL_MCLK2 \
+ RV(FV_PLLREFSEL_PLL1_REF_SEL_MCLK2, FB_PLLREFSEL_PLL1_REF_SEL)
+
+
+/*******************************
+ * R_DACMBCEN (0xC7) *
+ *******************************/
+
+/* Field Offsets */
+#define FB_DACMBCEN_MBCEN3 2
+#define FB_DACMBCEN_MBCEN2 1
+#define FB_DACMBCEN_MBCEN1 0
+
+/* Field Masks */
+#define FM_DACMBCEN_MBCEN3 0X1
+#define FM_DACMBCEN_MBCEN2 0X1
+#define FM_DACMBCEN_MBCEN1 0X1
+
+/* Register Masks */
+#define RM_DACMBCEN_MBCEN3 \
+ RM(FM_DACMBCEN_MBCEN3, FB_DACMBCEN_MBCEN3)
+
+#define RM_DACMBCEN_MBCEN2 \
+ RM(FM_DACMBCEN_MBCEN2, FB_DACMBCEN_MBCEN2)
+
+#define RM_DACMBCEN_MBCEN1 \
+ RM(FM_DACMBCEN_MBCEN1, FB_DACMBCEN_MBCEN1)
+
+
+/********************************
+ * R_DACMBCCTL (0xC8) *
+ ********************************/
+
+/* Field Offsets */
+#define FB_DACMBCCTL_LVLMODE3 5
+#define FB_DACMBCCTL_WINSEL3 4
+#define FB_DACMBCCTL_LVLMODE2 3
+#define FB_DACMBCCTL_WINSEL2 2
+#define FB_DACMBCCTL_LVLMODE1 1
+#define FB_DACMBCCTL_WINSEL1 0
+
+/* Field Masks */
+#define FM_DACMBCCTL_LVLMODE3 0X1
+#define FM_DACMBCCTL_WINSEL3 0X1
+#define FM_DACMBCCTL_LVLMODE2 0X1
+#define FM_DACMBCCTL_WINSEL2 0X1
+#define FM_DACMBCCTL_LVLMODE1 0X1
+#define FM_DACMBCCTL_WINSEL1 0X1
+
+/* Register Masks */
+#define RM_DACMBCCTL_LVLMODE3 \
+ RM(FM_DACMBCCTL_LVLMODE3, FB_DACMBCCTL_LVLMODE3)
+
+#define RM_DACMBCCTL_WINSEL3 \
+ RM(FM_DACMBCCTL_WINSEL3, FB_DACMBCCTL_WINSEL3)
+
+#define RM_DACMBCCTL_LVLMODE2 \
+ RM(FM_DACMBCCTL_LVLMODE2, FB_DACMBCCTL_LVLMODE2)
+
+#define RM_DACMBCCTL_WINSEL2 \
+ RM(FM_DACMBCCTL_WINSEL2, FB_DACMBCCTL_WINSEL2)
+
+#define RM_DACMBCCTL_LVLMODE1 \
+ RM(FM_DACMBCCTL_LVLMODE1, FB_DACMBCCTL_LVLMODE1)
+
+#define RM_DACMBCCTL_WINSEL1 \
+ RM(FM_DACMBCCTL_WINSEL1, FB_DACMBCCTL_WINSEL1)
+
+
+/*********************************
+ * R_DACMBCMUG1 (0xC9) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCMUG1_PHASE 5
+#define FB_DACMBCMUG1_MUGAIN 0
+
+/* Field Masks */
+#define FM_DACMBCMUG1_PHASE 0X1
+#define FM_DACMBCMUG1_MUGAIN 0X1F
+
+/* Register Masks */
+#define RM_DACMBCMUG1_PHASE \
+ RM(FM_DACMBCMUG1_PHASE, FB_DACMBCMUG1_PHASE)
+
+#define RM_DACMBCMUG1_MUGAIN \
+ RM(FM_DACMBCMUG1_MUGAIN, FB_DACMBCMUG1_MUGAIN)
+
+
+/*********************************
+ * R_DACMBCTHR1 (0xCA) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCTHR1_THRESH 0
+
+/* Field Masks */
+#define FM_DACMBCTHR1_THRESH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCTHR1_THRESH \
+ RM(FM_DACMBCTHR1_THRESH, FB_DACMBCTHR1_THRESH)
+
+
+/*********************************
+ * R_DACMBCRAT1 (0xCB) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCRAT1_RATIO 0
+
+/* Field Masks */
+#define FM_DACMBCRAT1_RATIO 0X1F
+
+/* Register Masks */
+#define RM_DACMBCRAT1_RATIO \
+ RM(FM_DACMBCRAT1_RATIO, FB_DACMBCRAT1_RATIO)
+
+
+/**********************************
+ * R_DACMBCATK1L (0xCC) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCATK1L_TCATKL 0
+
+/* Field Masks */
+#define FM_DACMBCATK1L_TCATKL 0XFF
+
+/* Register Masks */
+#define RM_DACMBCATK1L_TCATKL \
+ RM(FM_DACMBCATK1L_TCATKL, FB_DACMBCATK1L_TCATKL)
+
+
+/**********************************
+ * R_DACMBCATK1H (0xCD) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCATK1H_TCATKH 0
+
+/* Field Masks */
+#define FM_DACMBCATK1H_TCATKH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCATK1H_TCATKH \
+ RM(FM_DACMBCATK1H_TCATKH, FB_DACMBCATK1H_TCATKH)
+
+
+/**********************************
+ * R_DACMBCREL1L (0xCE) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCREL1L_TCRELL 0
+
+/* Field Masks */
+#define FM_DACMBCREL1L_TCRELL 0XFF
+
+/* Register Masks */
+#define RM_DACMBCREL1L_TCRELL \
+ RM(FM_DACMBCREL1L_TCRELL, FB_DACMBCREL1L_TCRELL)
+
+
+/**********************************
+ * R_DACMBCREL1H (0xCF) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCREL1H_TCRELH 0
+
+/* Field Masks */
+#define FM_DACMBCREL1H_TCRELH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCREL1H_TCRELH \
+ RM(FM_DACMBCREL1H_TCRELH, FB_DACMBCREL1H_TCRELH)
+
+
+/*********************************
+ * R_DACMBCMUG2 (0xD0) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCMUG2_PHASE 5
+#define FB_DACMBCMUG2_MUGAIN 0
+
+/* Field Masks */
+#define FM_DACMBCMUG2_PHASE 0X1
+#define FM_DACMBCMUG2_MUGAIN 0X1F
+
+/* Register Masks */
+#define RM_DACMBCMUG2_PHASE \
+ RM(FM_DACMBCMUG2_PHASE, FB_DACMBCMUG2_PHASE)
+
+#define RM_DACMBCMUG2_MUGAIN \
+ RM(FM_DACMBCMUG2_MUGAIN, FB_DACMBCMUG2_MUGAIN)
+
+
+/*********************************
+ * R_DACMBCTHR2 (0xD1) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCTHR2_THRESH 0
+
+/* Field Masks */
+#define FM_DACMBCTHR2_THRESH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCTHR2_THRESH \
+ RM(FM_DACMBCTHR2_THRESH, FB_DACMBCTHR2_THRESH)
+
+
+/*********************************
+ * R_DACMBCRAT2 (0xD2) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCRAT2_RATIO 0
+
+/* Field Masks */
+#define FM_DACMBCRAT2_RATIO 0X1F
+
+/* Register Masks */
+#define RM_DACMBCRAT2_RATIO \
+ RM(FM_DACMBCRAT2_RATIO, FB_DACMBCRAT2_RATIO)
+
+
+/**********************************
+ * R_DACMBCATK2L (0xD3) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCATK2L_TCATKL 0
+
+/* Field Masks */
+#define FM_DACMBCATK2L_TCATKL 0XFF
+
+/* Register Masks */
+#define RM_DACMBCATK2L_TCATKL \
+ RM(FM_DACMBCATK2L_TCATKL, FB_DACMBCATK2L_TCATKL)
+
+
+/**********************************
+ * R_DACMBCATK2H (0xD4) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCATK2H_TCATKH 0
+
+/* Field Masks */
+#define FM_DACMBCATK2H_TCATKH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCATK2H_TCATKH \
+ RM(FM_DACMBCATK2H_TCATKH, FB_DACMBCATK2H_TCATKH)
+
+
+/**********************************
+ * R_DACMBCREL2L (0xD5) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCREL2L_TCRELL 0
+
+/* Field Masks */
+#define FM_DACMBCREL2L_TCRELL 0XFF
+
+/* Register Masks */
+#define RM_DACMBCREL2L_TCRELL \
+ RM(FM_DACMBCREL2L_TCRELL, FB_DACMBCREL2L_TCRELL)
+
+
+/**********************************
+ * R_DACMBCREL2H (0xD6) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCREL2H_TCRELH 0
+
+/* Field Masks */
+#define FM_DACMBCREL2H_TCRELH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCREL2H_TCRELH \
+ RM(FM_DACMBCREL2H_TCRELH, FB_DACMBCREL2H_TCRELH)
+
+
+/*********************************
+ * R_DACMBCMUG3 (0xD7) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCMUG3_PHASE 5
+#define FB_DACMBCMUG3_MUGAIN 0
+
+/* Field Masks */
+#define FM_DACMBCMUG3_PHASE 0X1
+#define FM_DACMBCMUG3_MUGAIN 0X1F
+
+/* Register Masks */
+#define RM_DACMBCMUG3_PHASE \
+ RM(FM_DACMBCMUG3_PHASE, FB_DACMBCMUG3_PHASE)
+
+#define RM_DACMBCMUG3_MUGAIN \
+ RM(FM_DACMBCMUG3_MUGAIN, FB_DACMBCMUG3_MUGAIN)
+
+
+/*********************************
+ * R_DACMBCTHR3 (0xD8) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCTHR3_THRESH 0
+
+/* Field Masks */
+#define FM_DACMBCTHR3_THRESH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCTHR3_THRESH \
+ RM(FM_DACMBCTHR3_THRESH, FB_DACMBCTHR3_THRESH)
+
+
+/*********************************
+ * R_DACMBCRAT3 (0xD9) *
+ *********************************/
+
+/* Field Offsets */
+#define FB_DACMBCRAT3_RATIO 0
+
+/* Field Masks */
+#define FM_DACMBCRAT3_RATIO 0X1F
+
+/* Register Masks */
+#define RM_DACMBCRAT3_RATIO \
+ RM(FM_DACMBCRAT3_RATIO, FB_DACMBCRAT3_RATIO)
+
+
+/**********************************
+ * R_DACMBCATK3L (0xDA) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCATK3L_TCATKL 0
+
+/* Field Masks */
+#define FM_DACMBCATK3L_TCATKL 0XFF
+
+/* Register Masks */
+#define RM_DACMBCATK3L_TCATKL \
+ RM(FM_DACMBCATK3L_TCATKL, FB_DACMBCATK3L_TCATKL)
+
+
+/**********************************
+ * R_DACMBCATK3H (0xDB) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCATK3H_TCATKH 0
+
+/* Field Masks */
+#define FM_DACMBCATK3H_TCATKH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCATK3H_TCATKH \
+ RM(FM_DACMBCATK3H_TCATKH, FB_DACMBCATK3H_TCATKH)
+
+
+/**********************************
+ * R_DACMBCREL3L (0xDC) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCREL3L_TCRELL 0
+
+/* Field Masks */
+#define FM_DACMBCREL3L_TCRELL 0XFF
+
+/* Register Masks */
+#define RM_DACMBCREL3L_TCRELL \
+ RM(FM_DACMBCREL3L_TCRELL, FB_DACMBCREL3L_TCRELL)
+
+
+/**********************************
+ * R_DACMBCREL3H (0xDD) *
+ **********************************/
+
+/* Field Offsets */
+#define FB_DACMBCREL3H_TCRELH 0
+
+/* Field Masks */
+#define FM_DACMBCREL3H_TCRELH 0XFF
+
+/* Register Masks */
+#define RM_DACMBCREL3H_TCRELH \
+ RM(FM_DACMBCREL3H_TCRELH, FB_DACMBCREL3H_TCRELH)
+
+
+#endif /* __WOOKIE_H__ */
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
new file mode 100644
index 000000000000..64d0da40fbaf
--- /dev/null
+++ b/sound/soc/codecs/tscs454.c
@@ -0,0 +1,3481 @@
+// SPDX-License-Identifier: GPL-2.0
+// tscs454.c -- TSCS454 ALSA SoC Audio driver
+// Copyright 2018 Tempo Semiconductor, Inc.
+// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/string_choices.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "tscs454.h"
+
+static const unsigned int PLL_44_1K_RATE = (44100 * 256);
+
+#define COEFF_SIZE 3
+#define BIQUAD_COEFF_COUNT 5
+#define BIQUAD_SIZE (COEFF_SIZE * BIQUAD_COEFF_COUNT)
+
+#define COEFF_RAM_MAX_ADDR 0xcd
+#define COEFF_RAM_COEFF_COUNT (COEFF_RAM_MAX_ADDR + 1)
+#define COEFF_RAM_SIZE (COEFF_SIZE * COEFF_RAM_COEFF_COUNT)
+
+enum {
+ TSCS454_DAI1_ID,
+ TSCS454_DAI2_ID,
+ TSCS454_DAI3_ID,
+ TSCS454_DAI_COUNT,
+};
+
+struct pll {
+ int id;
+ unsigned int users;
+ struct mutex lock;
+};
+
+static inline void pll_init(struct pll *pll, int id)
+{
+ pll->id = id;
+ mutex_init(&pll->lock);
+}
+
+struct internal_rate {
+ struct pll *pll;
+};
+
+struct aif {
+ unsigned int id;
+ bool provider;
+ struct pll *pll;
+};
+
+static inline void aif_init(struct aif *aif, unsigned int id)
+{
+ aif->id = id;
+}
+
+struct coeff_ram {
+ u8 cache[COEFF_RAM_SIZE];
+ bool synced;
+ struct mutex lock;
+};
+
+static inline void init_coeff_ram_cache(u8 *cache)
+{
+ static const u8 norm_addrs[] = { 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19,
+ 0x1f, 0x20, 0x25, 0x2a, 0x2f, 0x34, 0x39, 0x3f, 0x40, 0x45,
+ 0x4a, 0x4f, 0x54, 0x59, 0x5f, 0x60, 0x65, 0x6a, 0x6f, 0x74,
+ 0x79, 0x7f, 0x80, 0x85, 0x8c, 0x91, 0x96, 0x97, 0x9c, 0xa3,
+ 0xa8, 0xad, 0xaf, 0xb0, 0xb5, 0xba, 0xbf, 0xc4, 0xc9};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(norm_addrs); i++)
+ cache[((norm_addrs[i] + 1) * COEFF_SIZE) - 1] = 0x40;
+}
+
+static inline void coeff_ram_init(struct coeff_ram *ram)
+{
+ init_coeff_ram_cache(ram->cache);
+ mutex_init(&ram->lock);
+}
+
+struct aifs_status {
+ u8 streams;
+};
+
+static inline void set_aif_status_active(struct aifs_status *status,
+ int aif_id, bool playback)
+{
+ u8 mask = 0x01 << (aif_id * 2 + !playback);
+
+ status->streams |= mask;
+}
+
+static inline void set_aif_status_inactive(struct aifs_status *status,
+ int aif_id, bool playback)
+{
+ u8 mask = ~(0x01 << (aif_id * 2 + !playback));
+
+ status->streams &= mask;
+}
+
+static bool aifs_active(struct aifs_status *status)
+{
+ return status->streams;
+}
+
+static bool aif_active(struct aifs_status *status, int aif_id)
+{
+ return (0x03 << aif_id * 2) & status->streams;
+}
+
+struct tscs454 {
+ struct regmap *regmap;
+ struct aif aifs[TSCS454_DAI_COUNT];
+
+ struct aifs_status aifs_status;
+ struct mutex aifs_status_lock;
+
+ struct pll pll1;
+ struct pll pll2;
+ struct internal_rate internal_rate;
+
+ struct coeff_ram dac_ram;
+ struct coeff_ram spk_ram;
+ struct coeff_ram sub_ram;
+
+ struct clk *sysclk;
+ int sysclk_src_id;
+ unsigned int bclk_freq;
+};
+
+struct coeff_ram_ctl {
+ unsigned int addr;
+ struct soc_bytes_ext bytes_ext;
+};
+
+static const struct reg_sequence tscs454_patch[] = {
+ /* Assign ASRC out of the box so DAI 1 just works */
+ { R_AUDIOMUX1, FV_ASRCIMUX_I2S1 | FV_I2S2MUX_I2S2 },
+ { R_AUDIOMUX2, FV_ASRCOMUX_I2S1 | FV_DACMUX_I2S1 | FV_I2S3MUX_I2S3 },
+ { R_AUDIOMUX3, FV_CLSSDMUX_I2S1 | FV_SUBMUX_I2S1_LR },
+ { R_TDMCTL0, FV_TDMMD_256 },
+ { VIRT_ADDR(0x0A, 0x13), 1 << 3 },
+};
+
+static bool tscs454_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case R_PLLSTAT:
+
+ case R_SPKCRRDL:
+ case R_SPKCRRDM:
+ case R_SPKCRRDH:
+ case R_SPKCRS:
+
+ case R_DACCRRDL:
+ case R_DACCRRDM:
+ case R_DACCRRDH:
+ case R_DACCRS:
+
+ case R_SUBCRRDL:
+ case R_SUBCRRDM:
+ case R_SUBCRRDH:
+ case R_SUBCRS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tscs454_writable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case R_SPKCRRDL:
+ case R_SPKCRRDM:
+ case R_SPKCRRDH:
+
+ case R_DACCRRDL:
+ case R_DACCRRDM:
+ case R_DACCRRDH:
+
+ case R_SUBCRRDL:
+ case R_SUBCRRDM:
+ case R_SUBCRRDH:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool tscs454_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case R_SPKCRWDL:
+ case R_SPKCRWDM:
+ case R_SPKCRWDH:
+
+ case R_DACCRWDL:
+ case R_DACCRWDM:
+ case R_DACCRWDH:
+
+ case R_SUBCRWDL:
+ case R_SUBCRWDM:
+ case R_SUBCRWDH:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool tscs454_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case R_SPKCRWDL:
+ case R_SPKCRWDM:
+ case R_SPKCRWDH:
+ case R_SPKCRRDL:
+ case R_SPKCRRDM:
+ case R_SPKCRRDH:
+
+ case R_DACCRWDL:
+ case R_DACCRWDM:
+ case R_DACCRWDH:
+ case R_DACCRRDL:
+ case R_DACCRRDM:
+ case R_DACCRRDH:
+
+ case R_SUBCRWDL:
+ case R_SUBCRWDM:
+ case R_SUBCRWDH:
+ case R_SUBCRRDL:
+ case R_SUBCRRDM:
+ case R_SUBCRRDH:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg tscs454_regmap_range_cfg = {
+ .name = "Pages",
+ .range_min = VIRT_BASE,
+ .range_max = VIRT_ADDR(0xFE, 0x02),
+ .selector_reg = R_PAGESEL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x100,
+};
+
+static struct regmap_config const tscs454_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = tscs454_writable,
+ .readable_reg = tscs454_readable,
+ .volatile_reg = tscs454_volatile,
+ .precious_reg = tscs454_precious,
+ .ranges = &tscs454_regmap_range_cfg,
+ .num_ranges = 1,
+ .max_register = VIRT_ADDR(0xFE, 0x02),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static inline int tscs454_data_init(struct tscs454 *tscs454,
+ struct i2c_client *i2c)
+{
+ int i;
+ int ret;
+
+ tscs454->regmap = devm_regmap_init_i2c(i2c, &tscs454_regmap_cfg);
+ if (IS_ERR(tscs454->regmap)) {
+ ret = PTR_ERR(tscs454->regmap);
+ return ret;
+ }
+
+ for (i = 0; i < TSCS454_DAI_COUNT; i++)
+ aif_init(&tscs454->aifs[i], i);
+
+ mutex_init(&tscs454->aifs_status_lock);
+ pll_init(&tscs454->pll1, 1);
+ pll_init(&tscs454->pll2, 2);
+
+ coeff_ram_init(&tscs454->dac_ram);
+ coeff_ram_init(&tscs454->spk_ram);
+ coeff_ram_init(&tscs454->sub_ram);
+
+ return 0;
+}
+
+struct reg_setting {
+ unsigned int addr;
+ unsigned int val;
+};
+
+static int coeff_ram_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ struct coeff_ram_ctl *ctl =
+ (struct coeff_ram_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ u8 *coeff_ram;
+ struct mutex *coeff_ram_lock;
+
+ if (strstr(kcontrol->id.name, "DAC")) {
+ coeff_ram = tscs454->dac_ram.cache;
+ coeff_ram_lock = &tscs454->dac_ram.lock;
+ } else if (strstr(kcontrol->id.name, "Speaker")) {
+ coeff_ram = tscs454->spk_ram.cache;
+ coeff_ram_lock = &tscs454->spk_ram.lock;
+ } else if (strstr(kcontrol->id.name, "Sub")) {
+ coeff_ram = tscs454->sub_ram.cache;
+ coeff_ram_lock = &tscs454->sub_ram.lock;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(coeff_ram_lock);
+
+ memcpy(ucontrol->value.bytes.data,
+ &coeff_ram[ctl->addr * COEFF_SIZE], params->max);
+
+ mutex_unlock(coeff_ram_lock);
+
+ return 0;
+}
+
+#define DACCRSTAT_MAX_TRYS 10
+static int write_coeff_ram(struct snd_soc_component *component, u8 *coeff_ram,
+ unsigned int r_stat, unsigned int r_addr, unsigned int r_wr,
+ unsigned int coeff_addr, unsigned int coeff_cnt)
+{
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int cnt;
+ int trys;
+ int ret;
+
+ for (cnt = 0; cnt < coeff_cnt; cnt++, coeff_addr++) {
+
+ for (trys = 0; trys < DACCRSTAT_MAX_TRYS; trys++) {
+ val = snd_soc_component_read(component, r_stat);
+ if (!val)
+ break;
+ }
+
+ if (trys == DACCRSTAT_MAX_TRYS) {
+ ret = -EIO;
+ dev_err(component->dev,
+ "Coefficient write error (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(tscs454->regmap, r_addr, coeff_addr);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to write dac ram address (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(tscs454->regmap, r_wr,
+ &coeff_ram[coeff_addr * COEFF_SIZE],
+ COEFF_SIZE);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to write dac ram (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int coeff_ram_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ struct coeff_ram_ctl *ctl =
+ (struct coeff_ram_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ unsigned int coeff_cnt = params->max / COEFF_SIZE;
+ u8 *coeff_ram;
+ struct mutex *coeff_ram_lock;
+ bool *coeff_ram_synced;
+ unsigned int r_stat;
+ unsigned int r_addr;
+ unsigned int r_wr;
+ unsigned int val;
+ int ret;
+
+ if (strstr(kcontrol->id.name, "DAC")) {
+ coeff_ram = tscs454->dac_ram.cache;
+ coeff_ram_lock = &tscs454->dac_ram.lock;
+ coeff_ram_synced = &tscs454->dac_ram.synced;
+ r_stat = R_DACCRS;
+ r_addr = R_DACCRADD;
+ r_wr = R_DACCRWDL;
+ } else if (strstr(kcontrol->id.name, "Speaker")) {
+ coeff_ram = tscs454->spk_ram.cache;
+ coeff_ram_lock = &tscs454->spk_ram.lock;
+ coeff_ram_synced = &tscs454->spk_ram.synced;
+ r_stat = R_SPKCRS;
+ r_addr = R_SPKCRADD;
+ r_wr = R_SPKCRWDL;
+ } else if (strstr(kcontrol->id.name, "Sub")) {
+ coeff_ram = tscs454->sub_ram.cache;
+ coeff_ram_lock = &tscs454->sub_ram.lock;
+ coeff_ram_synced = &tscs454->sub_ram.synced;
+ r_stat = R_SUBCRS;
+ r_addr = R_SUBCRADD;
+ r_wr = R_SUBCRWDL;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(coeff_ram_lock);
+
+ *coeff_ram_synced = false;
+
+ memcpy(&coeff_ram[ctl->addr * COEFF_SIZE],
+ ucontrol->value.bytes.data, params->max);
+
+ mutex_lock(&tscs454->pll1.lock);
+ mutex_lock(&tscs454->pll2.lock);
+
+ val = snd_soc_component_read(component, R_PLLSTAT);
+ if (val) { /* PLLs locked */
+ ret = write_coeff_ram(component, coeff_ram,
+ r_stat, r_addr, r_wr,
+ ctl->addr, coeff_cnt);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to flush coeff ram cache (%d)\n", ret);
+ goto exit;
+ }
+ *coeff_ram_synced = true;
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&tscs454->pll2.lock);
+ mutex_unlock(&tscs454->pll1.lock);
+ mutex_unlock(coeff_ram_lock);
+
+ return ret;
+}
+
+static inline int coeff_ram_sync(struct snd_soc_component *component,
+ struct tscs454 *tscs454)
+{
+ int ret;
+
+ mutex_lock(&tscs454->dac_ram.lock);
+ if (!tscs454->dac_ram.synced) {
+ ret = write_coeff_ram(component, tscs454->dac_ram.cache,
+ R_DACCRS, R_DACCRADD, R_DACCRWDL,
+ 0x00, COEFF_RAM_COEFF_COUNT);
+ if (ret < 0) {
+ mutex_unlock(&tscs454->dac_ram.lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&tscs454->dac_ram.lock);
+
+ mutex_lock(&tscs454->spk_ram.lock);
+ if (!tscs454->spk_ram.synced) {
+ ret = write_coeff_ram(component, tscs454->spk_ram.cache,
+ R_SPKCRS, R_SPKCRADD, R_SPKCRWDL,
+ 0x00, COEFF_RAM_COEFF_COUNT);
+ if (ret < 0) {
+ mutex_unlock(&tscs454->spk_ram.lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&tscs454->spk_ram.lock);
+
+ mutex_lock(&tscs454->sub_ram.lock);
+ if (!tscs454->sub_ram.synced) {
+ ret = write_coeff_ram(component, tscs454->sub_ram.cache,
+ R_SUBCRS, R_SUBCRADD, R_SUBCRWDL,
+ 0x00, COEFF_RAM_COEFF_COUNT);
+ if (ret < 0) {
+ mutex_unlock(&tscs454->sub_ram.lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&tscs454->sub_ram.lock);
+
+ return 0;
+}
+
+#define PLL_REG_SETTINGS_COUNT 11
+struct pll_ctl {
+ int freq_in;
+ struct reg_setting settings[PLL_REG_SETTINGS_COUNT];
+};
+
+#define PLL_CTL(f, t, c1, r1, o1, f1l, f1h, c2, r2, o2, f2l, f2h) \
+ { \
+ .freq_in = f, \
+ .settings = { \
+ {R_PLL1CTL, c1}, \
+ {R_PLL1RDIV, r1}, \
+ {R_PLL1ODIV, o1}, \
+ {R_PLL1FDIVL, f1l}, \
+ {R_PLL1FDIVH, f1h}, \
+ {R_PLL2CTL, c2}, \
+ {R_PLL2RDIV, r2}, \
+ {R_PLL2ODIV, o2}, \
+ {R_PLL2FDIVL, f2l}, \
+ {R_PLL2FDIVH, f2h}, \
+ {R_TIMEBASE, t}, \
+ }, \
+ }
+
+static const struct pll_ctl pll_ctls[] = {
+ PLL_CTL(1411200, 0x05,
+ 0xB9, 0x07, 0x02, 0xC3, 0x04,
+ 0x5A, 0x02, 0x03, 0xE0, 0x01),
+ PLL_CTL(1536000, 0x05,
+ 0x5A, 0x02, 0x03, 0xE0, 0x01,
+ 0x5A, 0x02, 0x03, 0xB9, 0x01),
+ PLL_CTL(2822400, 0x0A,
+ 0x63, 0x07, 0x04, 0xC3, 0x04,
+ 0x62, 0x07, 0x03, 0x48, 0x03),
+ PLL_CTL(3072000, 0x0B,
+ 0x62, 0x07, 0x03, 0x48, 0x03,
+ 0x5A, 0x04, 0x03, 0xB9, 0x01),
+ PLL_CTL(5644800, 0x15,
+ 0x63, 0x0E, 0x04, 0xC3, 0x04,
+ 0x5A, 0x08, 0x03, 0xE0, 0x01),
+ PLL_CTL(6144000, 0x17,
+ 0x5A, 0x08, 0x03, 0xE0, 0x01,
+ 0x5A, 0x08, 0x03, 0xB9, 0x01),
+ PLL_CTL(12000000, 0x2E,
+ 0x5B, 0x19, 0x03, 0x00, 0x03,
+ 0x6A, 0x19, 0x05, 0x98, 0x04),
+ PLL_CTL(19200000, 0x4A,
+ 0x53, 0x14, 0x03, 0x80, 0x01,
+ 0x5A, 0x19, 0x03, 0xB9, 0x01),
+ PLL_CTL(22000000, 0x55,
+ 0x6A, 0x37, 0x05, 0x00, 0x06,
+ 0x62, 0x26, 0x03, 0x49, 0x02),
+ PLL_CTL(22579200, 0x57,
+ 0x62, 0x31, 0x03, 0x20, 0x03,
+ 0x53, 0x1D, 0x03, 0xB3, 0x01),
+ PLL_CTL(24000000, 0x5D,
+ 0x53, 0x19, 0x03, 0x80, 0x01,
+ 0x5B, 0x19, 0x05, 0x4C, 0x02),
+ PLL_CTL(24576000, 0x5F,
+ 0x53, 0x1D, 0x03, 0xB3, 0x01,
+ 0x62, 0x40, 0x03, 0x72, 0x03),
+ PLL_CTL(27000000, 0x68,
+ 0x62, 0x4B, 0x03, 0x00, 0x04,
+ 0x6A, 0x7D, 0x03, 0x20, 0x06),
+ PLL_CTL(36000000, 0x8C,
+ 0x5B, 0x4B, 0x03, 0x00, 0x03,
+ 0x6A, 0x7D, 0x03, 0x98, 0x04),
+ PLL_CTL(11289600, 0x2B,
+ 0x6A, 0x31, 0x03, 0x40, 0x06,
+ 0x5A, 0x12, 0x03, 0x1C, 0x02),
+ PLL_CTL(26000000, 0x65,
+ 0x63, 0x41, 0x05, 0x00, 0x06,
+ 0x5A, 0x26, 0x03, 0xEF, 0x01),
+ PLL_CTL(12288000, 0x2F,
+ 0x5A, 0x12, 0x03, 0x1C, 0x02,
+ 0x62, 0x20, 0x03, 0x72, 0x03),
+ PLL_CTL(40000000, 0x9B,
+ 0xA2, 0x7D, 0x03, 0x80, 0x04,
+ 0x63, 0x7D, 0x05, 0xE4, 0x06),
+ PLL_CTL(512000, 0x01,
+ 0x62, 0x01, 0x03, 0xD0, 0x02,
+ 0x5B, 0x01, 0x04, 0x72, 0x03),
+ PLL_CTL(705600, 0x02,
+ 0x62, 0x02, 0x03, 0x15, 0x04,
+ 0x62, 0x01, 0x04, 0x80, 0x02),
+ PLL_CTL(1024000, 0x03,
+ 0x62, 0x02, 0x03, 0xD0, 0x02,
+ 0x5B, 0x02, 0x04, 0x72, 0x03),
+ PLL_CTL(2048000, 0x07,
+ 0x62, 0x04, 0x03, 0xD0, 0x02,
+ 0x5B, 0x04, 0x04, 0x72, 0x03),
+ PLL_CTL(2400000, 0x08,
+ 0x62, 0x05, 0x03, 0x00, 0x03,
+ 0x63, 0x05, 0x05, 0x98, 0x04),
+};
+
+static inline const struct pll_ctl *get_pll_ctl(unsigned long freq_in)
+{
+ int i;
+ struct pll_ctl const *pll_ctl = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(pll_ctls); ++i)
+ if (pll_ctls[i].freq_in == freq_in) {
+ pll_ctl = &pll_ctls[i];
+ break;
+ }
+
+ return pll_ctl;
+}
+
+enum {
+ PLL_INPUT_XTAL = 0,
+ PLL_INPUT_MCLK1,
+ PLL_INPUT_MCLK2,
+ PLL_INPUT_BCLK,
+};
+
+static int set_sysclk(struct snd_soc_component *component)
+{
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ struct pll_ctl const *pll_ctl;
+ unsigned long freq;
+ int i;
+ int ret;
+
+ if (tscs454->sysclk_src_id < PLL_INPUT_BCLK)
+ freq = clk_get_rate(tscs454->sysclk);
+ else
+ freq = tscs454->bclk_freq;
+ pll_ctl = get_pll_ctl(freq);
+ if (!pll_ctl) {
+ ret = -EINVAL;
+ dev_err(component->dev,
+ "Invalid PLL input %lu (%d)\n", freq, ret);
+ return ret;
+ }
+
+ for (i = 0; i < PLL_REG_SETTINGS_COUNT; ++i) {
+ ret = snd_soc_component_write(component,
+ pll_ctl->settings[i].addr,
+ pll_ctl->settings[i].val);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set pll setting (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static inline void reserve_pll(struct pll *pll)
+{
+ mutex_lock(&pll->lock);
+ pll->users++;
+ mutex_unlock(&pll->lock);
+}
+
+static inline void free_pll(struct pll *pll)
+{
+ mutex_lock(&pll->lock);
+ pll->users--;
+ mutex_unlock(&pll->lock);
+}
+
+static int pll_connected(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(source->dapm);
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ int users;
+
+ if (strstr(source->name, "PLL 1")) {
+ mutex_lock(&tscs454->pll1.lock);
+ users = tscs454->pll1.users;
+ mutex_unlock(&tscs454->pll1.lock);
+ dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__,
+ users);
+ } else {
+ mutex_lock(&tscs454->pll2.lock);
+ users = tscs454->pll2.users;
+ mutex_unlock(&tscs454->pll2.lock);
+ dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__,
+ users);
+ }
+
+ return users;
+}
+
+/*
+ * PLL must be enabled after power up and must be disabled before power down
+ * for proper clock switching.
+ */
+static int pll_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ bool enable;
+ bool pll1;
+ unsigned int msk;
+ unsigned int val;
+ int ret;
+
+ if (strstr(w->name, "PLL 1"))
+ pll1 = true;
+ else
+ pll1 = false;
+
+ msk = pll1 ? FM_PLLCTL_PLL1CLKEN : FM_PLLCTL_PLL2CLKEN;
+
+ if (event == SND_SOC_DAPM_POST_PMU)
+ enable = true;
+ else
+ enable = false;
+
+ if (enable)
+ val = pll1 ? FV_PLL1CLKEN_ENABLE : FV_PLL2CLKEN_ENABLE;
+ else
+ /*
+ * FV_PLL1CLKEN_DISABLE and FV_PLL2CLKEN_DISABLE are
+ * identical zero vzalues, there is no need to test
+ * the PLL index
+ */
+ val = FV_PLL1CLKEN_DISABLE;
+
+ ret = snd_soc_component_update_bits(component, R_PLLCTL, msk, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to %s PLL %d (%d)\n",
+ str_enable_disable(enable), pll1 ? 1 : 2, ret);
+ return ret;
+ }
+
+ if (enable) {
+ msleep(20); // Wait for lock
+ ret = coeff_ram_sync(component, tscs454);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to sync coeff ram (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static inline int aif_set_provider(struct snd_soc_component *component,
+ unsigned int aif_id, bool provider)
+{
+ unsigned int reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ switch (aif_id) {
+ case TSCS454_DAI1_ID:
+ reg = R_I2SP1CTL;
+ break;
+ case TSCS454_DAI2_ID:
+ reg = R_I2SP2CTL;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_I2SP3CTL;
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err(component->dev, "Unknown DAI %d (%d)\n", aif_id, ret);
+ return ret;
+ }
+ mask = FM_I2SPCTL_PORTMS;
+ val = provider ? FV_PORTMS_MASTER : FV_PORTMS_SLAVE;
+
+ ret = snd_soc_component_update_bits(component, reg, mask, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set DAI %d to %s (%d)\n",
+ aif_id, provider ? "provider" : "consumer", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline
+int aif_prepare(struct snd_soc_component *component, struct aif *aif)
+{
+ int ret;
+
+ ret = aif_set_provider(component, aif->id, aif->provider);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static inline int aif_free(struct snd_soc_component *component,
+ struct aif *aif, bool playback)
+{
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&tscs454->aifs_status_lock);
+
+ dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id);
+
+ set_aif_status_inactive(&tscs454->aifs_status, aif->id, playback);
+
+ dev_dbg(component->dev, "Set aif %d inactive. Streams status is 0x%x\n",
+ aif->id, tscs454->aifs_status.streams);
+
+ if (!aif_active(&tscs454->aifs_status, aif->id)) {
+ /* Do config in slave mode */
+ aif_set_provider(component, aif->id, false);
+ dev_dbg(component->dev, "Freeing pll %d from aif %d\n",
+ aif->pll->id, aif->id);
+ free_pll(aif->pll);
+ }
+
+ if (!aifs_active(&tscs454->aifs_status)) {
+ dev_dbg(component->dev, "Freeing pll %d from ir\n",
+ tscs454->internal_rate.pll->id);
+ free_pll(tscs454->internal_rate.pll);
+ }
+
+ mutex_unlock(&tscs454->aifs_status_lock);
+
+ return 0;
+}
+
+/* R_PLLCTL PG 0 ADDR 0x15 */
+static char const * const bclk_sel_txt[] = {
+ "BCLK 1", "BCLK 2", "BCLK 3"};
+
+static struct soc_enum const bclk_sel_enum =
+ SOC_ENUM_SINGLE(R_PLLCTL, FB_PLLCTL_BCLKSEL,
+ ARRAY_SIZE(bclk_sel_txt), bclk_sel_txt);
+
+/* R_ISRC PG 0 ADDR 0x16 */
+static char const * const isrc_br_txt[] = {
+ "44.1kHz", "48kHz"};
+
+static struct soc_enum const isrc_br_enum =
+ SOC_ENUM_SINGLE(R_ISRC, FB_ISRC_IBR,
+ ARRAY_SIZE(isrc_br_txt), isrc_br_txt);
+
+static char const * const isrc_bm_txt[] = {
+ "0.25x", "0.5x", "1.0x", "2.0x"};
+
+static struct soc_enum const isrc_bm_enum =
+ SOC_ENUM_SINGLE(R_ISRC, FB_ISRC_IBM,
+ ARRAY_SIZE(isrc_bm_txt), isrc_bm_txt);
+
+/* R_SCLKCTL PG 0 ADDR 0x18 */
+static char const * const modular_rate_txt[] = {
+ "Reserved", "Half", "Full", "Auto",};
+
+static struct soc_enum const adc_modular_rate_enum =
+ SOC_ENUM_SINGLE(R_SCLKCTL, FB_SCLKCTL_ASDM,
+ ARRAY_SIZE(modular_rate_txt), modular_rate_txt);
+
+static struct soc_enum const dac_modular_rate_enum =
+ SOC_ENUM_SINGLE(R_SCLKCTL, FB_SCLKCTL_DSDM,
+ ARRAY_SIZE(modular_rate_txt), modular_rate_txt);
+
+/* R_I2SIDCTL PG 0 ADDR 0x38 */
+static char const * const data_ctrl_txt[] = {
+ "L/R", "L/L", "R/R", "R/L"};
+
+static struct soc_enum const data_in_ctrl_enums[] = {
+ SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI1DCTL,
+ ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt),
+ SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI2DCTL,
+ ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt),
+ SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI3DCTL,
+ ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt),
+};
+
+/* R_I2SODCTL PG 0 ADDR 0x39 */
+static struct soc_enum const data_out_ctrl_enums[] = {
+ SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO1DCTL,
+ ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt),
+ SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO2DCTL,
+ ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt),
+ SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO3DCTL,
+ ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt),
+};
+
+/* R_AUDIOMUX1 PG 0 ADDR 0x3A */
+static char const * const asrc_mux_txt[] = {
+ "None", "DAI 1", "DAI 2", "DAI 3"};
+
+static struct soc_enum const asrc_in_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_ASRCIMUX,
+ ARRAY_SIZE(asrc_mux_txt), asrc_mux_txt);
+
+static char const * const dai_mux_txt[] = {
+ "CH 0_1", "CH 2_3", "CH 4_5", "ADC/DMic 1",
+ "DMic 2", "ClassD", "DAC", "Sub"};
+
+static struct soc_enum const dai2_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_I2S2MUX,
+ ARRAY_SIZE(dai_mux_txt), dai_mux_txt);
+
+static struct snd_kcontrol_new const dai2_mux_dapm_enum =
+ SOC_DAPM_ENUM("DAI 2 Mux", dai2_mux_enum);
+
+static struct soc_enum const dai1_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_I2S1MUX,
+ ARRAY_SIZE(dai_mux_txt), dai_mux_txt);
+
+static struct snd_kcontrol_new const dai1_mux_dapm_enum =
+ SOC_DAPM_ENUM("DAI 1 Mux", dai1_mux_enum);
+
+/* R_AUDIOMUX2 PG 0 ADDR 0x3B */
+static struct soc_enum const asrc_out_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_ASRCOMUX,
+ ARRAY_SIZE(asrc_mux_txt), asrc_mux_txt);
+
+static struct soc_enum const dac_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_DACMUX,
+ ARRAY_SIZE(dai_mux_txt), dai_mux_txt);
+
+static struct snd_kcontrol_new const dac_mux_dapm_enum =
+ SOC_DAPM_ENUM("DAC Mux", dac_mux_enum);
+
+static struct soc_enum const dai3_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_I2S3MUX,
+ ARRAY_SIZE(dai_mux_txt), dai_mux_txt);
+
+static struct snd_kcontrol_new const dai3_mux_dapm_enum =
+ SOC_DAPM_ENUM("DAI 3 Mux", dai3_mux_enum);
+
+/* R_AUDIOMUX3 PG 0 ADDR 0x3C */
+static char const * const sub_mux_txt[] = {
+ "CH 0", "CH 1", "CH 0 + 1",
+ "CH 2", "CH 3", "CH 2 + 3",
+ "CH 4", "CH 5", "CH 4 + 5",
+ "ADC/DMic 1 Left", "ADC/DMic 1 Right",
+ "ADC/DMic 1 Left Plus Right",
+ "DMic 2 Left", "DMic 2 Right", "DMic 2 Left Plus Right",
+ "ClassD Left", "ClassD Right", "ClassD Left Plus Right"};
+
+static struct soc_enum const sub_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX3, FB_AUDIOMUX3_SUBMUX,
+ ARRAY_SIZE(sub_mux_txt), sub_mux_txt);
+
+static struct snd_kcontrol_new const sub_mux_dapm_enum =
+ SOC_DAPM_ENUM("Sub Mux", sub_mux_enum);
+
+static struct soc_enum const classd_mux_enum =
+ SOC_ENUM_SINGLE(R_AUDIOMUX3, FB_AUDIOMUX3_CLSSDMUX,
+ ARRAY_SIZE(dai_mux_txt), dai_mux_txt);
+
+static struct snd_kcontrol_new const classd_mux_dapm_enum =
+ SOC_DAPM_ENUM("ClassD Mux", classd_mux_enum);
+
+/* R_HSDCTL1 PG 1 ADDR 0x01 */
+static char const * const jack_type_txt[] = {
+ "3 Terminal", "4 Terminal"};
+
+static struct soc_enum const hp_jack_type_enum =
+ SOC_ENUM_SINGLE(R_HSDCTL1, FB_HSDCTL1_HPJKTYPE,
+ ARRAY_SIZE(jack_type_txt), jack_type_txt);
+
+static char const * const hs_det_pol_txt[] = {
+ "Rising", "Falling"};
+
+static struct soc_enum const hs_det_pol_enum =
+ SOC_ENUM_SINGLE(R_HSDCTL1, FB_HSDCTL1_HSDETPOL,
+ ARRAY_SIZE(hs_det_pol_txt), hs_det_pol_txt);
+
+/* R_HSDCTL1 PG 1 ADDR 0x02 */
+static char const * const hs_mic_bias_force_txt[] = {
+ "Off", "Ring", "Sleeve"};
+
+static struct soc_enum const hs_mic_bias_force_enum =
+ SOC_ENUM_SINGLE(R_HSDCTL2, FB_HSDCTL2_FMICBIAS1,
+ ARRAY_SIZE(hs_mic_bias_force_txt),
+ hs_mic_bias_force_txt);
+
+static char const * const plug_type_txt[] = {
+ "OMTP", "CTIA", "Reserved", "Headphone"};
+
+static struct soc_enum const plug_type_force_enum =
+ SOC_ENUM_SINGLE(R_HSDCTL2, FB_HSDCTL2_FPLUGTYPE,
+ ARRAY_SIZE(plug_type_txt), plug_type_txt);
+
+
+/* R_CH0AIC PG 1 ADDR 0x06 */
+static char const * const in_bst_mux_txt[] = {
+ "Input 1", "Input 2", "Input 3", "D2S"};
+
+static struct soc_enum const in_bst_mux_ch0_enum =
+ SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_INSELL,
+ ARRAY_SIZE(in_bst_mux_txt),
+ in_bst_mux_txt);
+static struct snd_kcontrol_new const in_bst_mux_ch0_dapm_enum =
+ SOC_DAPM_ENUM("Input Boost Channel 0 Enum",
+ in_bst_mux_ch0_enum);
+
+static DECLARE_TLV_DB_SCALE(in_bst_vol_tlv_arr, 0, 1000, 0);
+
+static char const * const adc_mux_txt[] = {
+ "Input 1 Boost Bypass", "Input 2 Boost Bypass",
+ "Input 3 Boost Bypass", "Input Boost"};
+
+static struct soc_enum const adc_mux_ch0_enum =
+ SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_LADCIN,
+ ARRAY_SIZE(adc_mux_txt), adc_mux_txt);
+static struct snd_kcontrol_new const adc_mux_ch0_dapm_enum =
+ SOC_DAPM_ENUM("ADC Channel 0 Enum", adc_mux_ch0_enum);
+
+static char const * const in_proc_mux_txt[] = {
+ "ADC", "DMic"};
+
+static struct soc_enum const in_proc_ch0_enum =
+ SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_IPCH0S,
+ ARRAY_SIZE(in_proc_mux_txt), in_proc_mux_txt);
+static struct snd_kcontrol_new const in_proc_mux_ch0_dapm_enum =
+ SOC_DAPM_ENUM("Input Processor Channel 0 Enum",
+ in_proc_ch0_enum);
+
+/* R_CH1AIC PG 1 ADDR 0x07 */
+static struct soc_enum const in_bst_mux_ch1_enum =
+ SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_INSELR,
+ ARRAY_SIZE(in_bst_mux_txt),
+ in_bst_mux_txt);
+static struct snd_kcontrol_new const in_bst_mux_ch1_dapm_enum =
+ SOC_DAPM_ENUM("Input Boost Channel 1 Enum",
+ in_bst_mux_ch1_enum);
+
+static struct soc_enum const adc_mux_ch1_enum =
+ SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_RADCIN,
+ ARRAY_SIZE(adc_mux_txt), adc_mux_txt);
+static struct snd_kcontrol_new const adc_mux_ch1_dapm_enum =
+ SOC_DAPM_ENUM("ADC Channel 1 Enum", adc_mux_ch1_enum);
+
+static struct soc_enum const in_proc_ch1_enum =
+ SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_IPCH1S,
+ ARRAY_SIZE(in_proc_mux_txt), in_proc_mux_txt);
+static struct snd_kcontrol_new const in_proc_mux_ch1_dapm_enum =
+ SOC_DAPM_ENUM("Input Processor Channel 1 Enum",
+ in_proc_ch1_enum);
+
+/* R_ICTL0 PG 1 ADDR 0x0A */
+static char const * const pol_txt[] = {
+ "Normal", "Invert"};
+
+static struct soc_enum const in_pol_ch1_enum =
+ SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_IN0POL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static struct soc_enum const in_pol_ch0_enum =
+ SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_IN1POL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static char const * const in_proc_ch_sel_txt[] = {
+ "Normal", "Mono Mix to Channel 0",
+ "Mono Mix to Channel 1", "Add"};
+
+static struct soc_enum const in_proc_ch01_sel_enum =
+ SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_INPCH10SEL,
+ ARRAY_SIZE(in_proc_ch_sel_txt),
+ in_proc_ch_sel_txt);
+
+/* R_ICTL1 PG 1 ADDR 0x0B */
+static struct soc_enum const in_pol_ch3_enum =
+ SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_IN2POL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static struct soc_enum const in_pol_ch2_enum =
+ SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_IN3POL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static struct soc_enum const in_proc_ch23_sel_enum =
+ SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_INPCH32SEL,
+ ARRAY_SIZE(in_proc_ch_sel_txt),
+ in_proc_ch_sel_txt);
+
+/* R_MICBIAS PG 1 ADDR 0x0C */
+static char const * const mic_bias_txt[] = {
+ "2.5V", "2.1V", "1.8V", "Vdd"};
+
+static struct soc_enum const mic_bias_2_enum =
+ SOC_ENUM_SINGLE(R_MICBIAS, FB_MICBIAS_MICBOV2,
+ ARRAY_SIZE(mic_bias_txt), mic_bias_txt);
+
+static struct soc_enum const mic_bias_1_enum =
+ SOC_ENUM_SINGLE(R_MICBIAS, FB_MICBIAS_MICBOV1,
+ ARRAY_SIZE(mic_bias_txt), mic_bias_txt);
+
+/* R_PGACTL0 PG 1 ADDR 0x0D */
+/* R_PGACTL1 PG 1 ADDR 0x0E */
+/* R_PGACTL2 PG 1 ADDR 0x0F */
+/* R_PGACTL3 PG 1 ADDR 0x10 */
+static DECLARE_TLV_DB_SCALE(in_pga_vol_tlv_arr, -1725, 75, 0);
+
+/* R_ICH0VOL PG1 ADDR 0x12 */
+/* R_ICH1VOL PG1 ADDR 0x13 */
+/* R_ICH2VOL PG1 ADDR 0x14 */
+/* R_ICH3VOL PG1 ADDR 0x15 */
+static DECLARE_TLV_DB_MINMAX(in_vol_tlv_arr, -7125, 2400);
+
+/* R_ASRCILVOL PG1 ADDR 0x16 */
+/* R_ASRCIRVOL PG1 ADDR 0x17 */
+/* R_ASRCOLVOL PG1 ADDR 0x18 */
+/* R_ASRCORVOL PG1 ADDR 0x19 */
+static DECLARE_TLV_DB_MINMAX(asrc_vol_tlv_arr, -9562, 600);
+
+/* R_ALCCTL0 PG1 ADDR 0x1D */
+static char const * const alc_mode_txt[] = {
+ "ALC", "Limiter"};
+
+static struct soc_enum const alc_mode_enum =
+ SOC_ENUM_SINGLE(R_ALCCTL0, FB_ALCCTL0_ALCMODE,
+ ARRAY_SIZE(alc_mode_txt), alc_mode_txt);
+
+static char const * const alc_ref_text[] = {
+ "Channel 0", "Channel 1", "Channel 2", "Channel 3", "Peak"};
+
+static struct soc_enum const alc_ref_enum =
+ SOC_ENUM_SINGLE(R_ALCCTL0, FB_ALCCTL0_ALCREF,
+ ARRAY_SIZE(alc_ref_text), alc_ref_text);
+
+/* R_ALCCTL1 PG 1 ADDR 0x1E */
+static DECLARE_TLV_DB_SCALE(alc_max_gain_tlv_arr, -1200, 600, 0);
+static DECLARE_TLV_DB_SCALE(alc_target_tlv_arr, -2850, 150, 0);
+
+/* R_ALCCTL2 PG 1 ADDR 0x1F */
+static DECLARE_TLV_DB_SCALE(alc_min_gain_tlv_arr, -1725, 600, 0);
+
+/* R_NGATE PG 1 ADDR 0x21 */
+static DECLARE_TLV_DB_SCALE(ngth_tlv_arr, -7650, 150, 0);
+
+static char const * const ngate_type_txt[] = {
+ "PGA Constant", "ADC Mute"};
+
+static struct soc_enum const ngate_type_enum =
+ SOC_ENUM_SINGLE(R_NGATE, FB_NGATE_NGG,
+ ARRAY_SIZE(ngate_type_txt), ngate_type_txt);
+
+/* R_DMICCTL PG 1 ADDR 0x22 */
+static char const * const dmic_mono_sel_txt[] = {
+ "Stereo", "Mono"};
+
+static struct soc_enum const dmic_mono_sel_enum =
+ SOC_ENUM_SINGLE(R_DMICCTL, FB_DMICCTL_DMONO,
+ ARRAY_SIZE(dmic_mono_sel_txt), dmic_mono_sel_txt);
+
+/* R_DACCTL PG 2 ADDR 0x01 */
+static struct soc_enum const dac_pol_r_enum =
+ SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACPOLR,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static struct soc_enum const dac_pol_l_enum =
+ SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACPOLL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static char const * const dac_dith_txt[] = {
+ "Half", "Full", "Disabled", "Static"};
+
+static struct soc_enum const dac_dith_enum =
+ SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACDITH,
+ ARRAY_SIZE(dac_dith_txt), dac_dith_txt);
+
+/* R_SPKCTL PG 2 ADDR 0x02 */
+static struct soc_enum const spk_pol_r_enum =
+ SOC_ENUM_SINGLE(R_SPKCTL, FB_SPKCTL_SPKPOLR,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static struct soc_enum const spk_pol_l_enum =
+ SOC_ENUM_SINGLE(R_SPKCTL, FB_SPKCTL_SPKPOLL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_SUBCTL PG 2 ADDR 0x03 */
+static struct soc_enum const sub_pol_enum =
+ SOC_ENUM_SINGLE(R_SUBCTL, FB_SUBCTL_SUBPOL,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_MVOLL PG 2 ADDR 0x08 */
+/* R_MVOLR PG 2 ADDR 0x09 */
+static DECLARE_TLV_DB_MINMAX(mvol_tlv_arr, -9562, 0);
+
+/* R_HPVOLL PG 2 ADDR 0x0A */
+/* R_HPVOLR PG 2 ADDR 0x0B */
+static DECLARE_TLV_DB_SCALE(hp_vol_tlv_arr, -8850, 75, 0);
+
+/* R_SPKVOLL PG 2 ADDR 0x0C */
+/* R_SPKVOLR PG 2 ADDR 0x0D */
+static DECLARE_TLV_DB_SCALE(spk_vol_tlv_arr, -7725, 75, 0);
+
+/* R_SPKEQFILT PG 3 ADDR 0x01 */
+static char const * const eq_txt[] = {
+ "Pre Scale",
+ "Pre Scale + EQ Band 0",
+ "Pre Scale + EQ Band 0 - 1",
+ "Pre Scale + EQ Band 0 - 2",
+ "Pre Scale + EQ Band 0 - 3",
+ "Pre Scale + EQ Band 0 - 4",
+ "Pre Scale + EQ Band 0 - 5",
+};
+
+static struct soc_enum const spk_eq_enums[] = {
+ SOC_ENUM_SINGLE(R_SPKEQFILT, FB_SPKEQFILT_EQ2BE,
+ ARRAY_SIZE(eq_txt), eq_txt),
+ SOC_ENUM_SINGLE(R_SPKEQFILT, FB_SPKEQFILT_EQ1BE,
+ ARRAY_SIZE(eq_txt), eq_txt),
+};
+
+/* R_SPKMBCCTL PG 3 ADDR 0x0B */
+static char const * const lvl_mode_txt[] = {
+ "Average", "Peak"};
+
+static struct soc_enum const spk_mbc3_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE3,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static char const * const win_sel_txt[] = {
+ "512", "64"};
+
+static struct soc_enum const spk_mbc3_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL3,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+static struct soc_enum const spk_mbc2_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE2,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const spk_mbc2_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL2,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+static struct soc_enum const spk_mbc1_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE1,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const spk_mbc1_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL1,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+/* R_SPKMBCMUG1 PG 3 ADDR 0x0C */
+static struct soc_enum const spk_mbc1_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCMUG1, FB_SPKMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+static DECLARE_TLV_DB_MINMAX(mbc_mug_tlv_arr, -4650, 0);
+
+/* R_SPKMBCTHR1 PG 3 ADDR 0x0D */
+static DECLARE_TLV_DB_MINMAX(thr_tlv_arr, -9562, 0);
+
+/* R_SPKMBCRAT1 PG 3 ADDR 0x0E */
+static char const * const comp_rat_txt[] = {
+ "Reserved", "1.5:1", "2:1", "3:1", "4:1", "5:1", "6:1",
+ "7:1", "8:1", "9:1", "10:1", "11:1", "12:1", "13:1", "14:1",
+ "15:1", "16:1", "17:1", "18:1", "19:1", "20:1"};
+
+static struct soc_enum const spk_mbc1_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCRAT1, FB_SPKMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SPKMBCMUG2 PG 3 ADDR 0x13 */
+static struct soc_enum const spk_mbc2_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCMUG2, FB_SPKMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_SPKMBCRAT2 PG 3 ADDR 0x15 */
+static struct soc_enum const spk_mbc2_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCRAT2, FB_SPKMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SPKMBCMUG3 PG 3 ADDR 0x1A */
+static struct soc_enum const spk_mbc3_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCMUG3, FB_SPKMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_SPKMBCRAT3 PG 3 ADDR 0x1C */
+static struct soc_enum const spk_mbc3_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SPKMBCRAT3, FB_SPKMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SPKCLECTL PG 3 ADDR 0x21 */
+static struct soc_enum const spk_cle_lvl_mode_enum =
+ SOC_ENUM_SINGLE(R_SPKCLECTL, FB_SPKCLECTL_LVLMODE,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const spk_cle_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SPKCLECTL, FB_SPKCLECTL_WINSEL,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+/* R_SPKCLEMUG PG 3 ADDR 0x22 */
+static DECLARE_TLV_DB_MINMAX(cle_mug_tlv_arr, 0, 4650);
+
+/* R_SPKCOMPRAT PG 3 ADDR 0x24 */
+static struct soc_enum const spk_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SPKCOMPRAT, FB_SPKCOMPRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SPKEXPTHR PG 3 ADDR 0x2F */
+static char const * const exp_rat_txt[] = {
+ "Reserved", "Reserved", "1:2", "1:3",
+ "1:4", "1:5", "1:6", "1:7"};
+
+static struct soc_enum const spk_exp_rat_enum =
+ SOC_ENUM_SINGLE(R_SPKEXPRAT, FB_SPKEXPRAT_RATIO,
+ ARRAY_SIZE(exp_rat_txt), exp_rat_txt);
+
+/* R_DACEQFILT PG 4 ADDR 0x01 */
+static struct soc_enum const dac_eq_enums[] = {
+ SOC_ENUM_SINGLE(R_DACEQFILT, FB_DACEQFILT_EQ2BE,
+ ARRAY_SIZE(eq_txt), eq_txt),
+ SOC_ENUM_SINGLE(R_DACEQFILT, FB_DACEQFILT_EQ1BE,
+ ARRAY_SIZE(eq_txt), eq_txt),
+};
+
+/* R_DACMBCCTL PG 4 ADDR 0x0B */
+static struct soc_enum const dac_mbc3_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE3,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const dac_mbc3_win_sel_enum =
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL3,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+static struct soc_enum const dac_mbc2_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE2,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const dac_mbc2_win_sel_enum =
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL2,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+static struct soc_enum const dac_mbc1_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE1,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const dac_mbc1_win_sel_enum =
+ SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL1,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+/* R_DACMBCMUG1 PG 4 ADDR 0x0C */
+static struct soc_enum const dac_mbc1_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_DACMBCMUG1, FB_DACMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_DACMBCRAT1 PG 4 ADDR 0x0E */
+static struct soc_enum const dac_mbc1_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_DACMBCRAT1, FB_DACMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_DACMBCMUG2 PG 4 ADDR 0x13 */
+static struct soc_enum const dac_mbc2_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_DACMBCMUG2, FB_DACMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_DACMBCRAT2 PG 4 ADDR 0x15 */
+static struct soc_enum const dac_mbc2_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_DACMBCRAT2, FB_DACMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_DACMBCMUG3 PG 4 ADDR 0x1A */
+static struct soc_enum const dac_mbc3_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_DACMBCMUG3, FB_DACMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_DACMBCRAT3 PG 4 ADDR 0x1C */
+static struct soc_enum const dac_mbc3_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_DACMBCRAT3, FB_DACMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_DACCLECTL PG 4 ADDR 0x21 */
+static struct soc_enum const dac_cle_lvl_mode_enum =
+ SOC_ENUM_SINGLE(R_DACCLECTL, FB_DACCLECTL_LVLMODE,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const dac_cle_win_sel_enum =
+ SOC_ENUM_SINGLE(R_DACCLECTL, FB_DACCLECTL_WINSEL,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+/* R_DACCOMPRAT PG 4 ADDR 0x24 */
+static struct soc_enum const dac_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_DACCOMPRAT, FB_DACCOMPRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_DACEXPRAT PG 4 ADDR 0x30 */
+static struct soc_enum const dac_exp_rat_enum =
+ SOC_ENUM_SINGLE(R_DACEXPRAT, FB_DACEXPRAT_RATIO,
+ ARRAY_SIZE(exp_rat_txt), exp_rat_txt);
+
+/* R_SUBEQFILT PG 5 ADDR 0x01 */
+static struct soc_enum const sub_eq_enums[] = {
+ SOC_ENUM_SINGLE(R_SUBEQFILT, FB_SUBEQFILT_EQ2BE,
+ ARRAY_SIZE(eq_txt), eq_txt),
+ SOC_ENUM_SINGLE(R_SUBEQFILT, FB_SUBEQFILT_EQ1BE,
+ ARRAY_SIZE(eq_txt), eq_txt),
+};
+
+/* R_SUBMBCCTL PG 5 ADDR 0x0B */
+static struct soc_enum const sub_mbc3_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE3,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const sub_mbc3_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL3,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+static struct soc_enum const sub_mbc2_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE2,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const sub_mbc2_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL2,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+static struct soc_enum const sub_mbc1_lvl_det_mode_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE1,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+
+static struct soc_enum const sub_mbc1_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL1,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+/* R_SUBMBCMUG1 PG 5 ADDR 0x0C */
+static struct soc_enum const sub_mbc1_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCMUG1, FB_SUBMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_SUBMBCRAT1 PG 5 ADDR 0x0E */
+static struct soc_enum const sub_mbc1_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCRAT1, FB_SUBMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SUBMBCMUG2 PG 5 ADDR 0x13 */
+static struct soc_enum const sub_mbc2_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCMUG2, FB_SUBMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_SUBMBCRAT2 PG 5 ADDR 0x15 */
+static struct soc_enum const sub_mbc2_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCRAT2, FB_SUBMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SUBMBCMUG3 PG 5 ADDR 0x1A */
+static struct soc_enum const sub_mbc3_phase_pol_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCMUG3, FB_SUBMBCMUG_PHASE,
+ ARRAY_SIZE(pol_txt), pol_txt);
+
+/* R_SUBMBCRAT3 PG 5 ADDR 0x1C */
+static struct soc_enum const sub_mbc3_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SUBMBCRAT3, FB_SUBMBCRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SUBCLECTL PG 5 ADDR 0x21 */
+static struct soc_enum const sub_cle_lvl_mode_enum =
+ SOC_ENUM_SINGLE(R_SUBCLECTL, FB_SUBCLECTL_LVLMODE,
+ ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt);
+static struct soc_enum const sub_cle_win_sel_enum =
+ SOC_ENUM_SINGLE(R_SUBCLECTL, FB_SUBCLECTL_WINSEL,
+ ARRAY_SIZE(win_sel_txt), win_sel_txt);
+
+/* R_SUBCOMPRAT PG 5 ADDR 0x24 */
+static struct soc_enum const sub_comp_rat_enum =
+ SOC_ENUM_SINGLE(R_SUBCOMPRAT, FB_SUBCOMPRAT_RATIO,
+ ARRAY_SIZE(comp_rat_txt), comp_rat_txt);
+
+/* R_SUBEXPRAT PG 5 ADDR 0x30 */
+static struct soc_enum const sub_exp_rat_enum =
+ SOC_ENUM_SINGLE(R_SUBEXPRAT, FB_SUBEXPRAT_RATIO,
+ ARRAY_SIZE(exp_rat_txt), exp_rat_txt);
+
+static int bytes_info_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct coeff_ram_ctl *ctl =
+ (struct coeff_ram_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+/* CH 0_1 Input Mux */
+static char const * const ch_0_1_mux_txt[] = {"DAI 1", "TDM 0_1"};
+
+static struct soc_enum const ch_0_1_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(ch_0_1_mux_txt), ch_0_1_mux_txt);
+
+static struct snd_kcontrol_new const ch_0_1_mux_dapm_enum =
+ SOC_DAPM_ENUM("CH 0_1 Input Mux", ch_0_1_mux_enum);
+
+/* CH 2_3 Input Mux */
+static char const * const ch_2_3_mux_txt[] = {"DAI 2", "TDM 2_3"};
+
+static struct soc_enum const ch_2_3_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(ch_2_3_mux_txt), ch_2_3_mux_txt);
+
+static struct snd_kcontrol_new const ch_2_3_mux_dapm_enum =
+ SOC_DAPM_ENUM("CH 2_3 Input Mux", ch_2_3_mux_enum);
+
+/* CH 4_5 Input Mux */
+static char const * const ch_4_5_mux_txt[] = {"DAI 3", "TDM 4_5"};
+
+static struct soc_enum const ch_4_5_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(ch_4_5_mux_txt), ch_4_5_mux_txt);
+
+static struct snd_kcontrol_new const ch_4_5_mux_dapm_enum =
+ SOC_DAPM_ENUM("CH 4_5 Input Mux", ch_4_5_mux_enum);
+
+#define COEFF_RAM_CTL(xname, xcount, xaddr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = bytes_info_ext, \
+ .get = coeff_ram_get, .put = coeff_ram_put, \
+ .private_value = (unsigned long)&(struct coeff_ram_ctl) { \
+ .addr = xaddr, \
+ .bytes_ext = {.max = xcount, }, \
+ } \
+}
+
+static struct snd_kcontrol_new const tscs454_snd_controls[] = {
+ /* R_PLLCTL PG 0 ADDR 0x15 */
+ SOC_ENUM("PLL BCLK Input", bclk_sel_enum),
+ /* R_ISRC PG 0 ADDR 0x16 */
+ SOC_ENUM("Internal Rate", isrc_br_enum),
+ SOC_ENUM("Internal Rate Multiple", isrc_bm_enum),
+ /* R_SCLKCTL PG 0 ADDR 0x18 */
+ SOC_ENUM("ADC Modular Rate", adc_modular_rate_enum),
+ SOC_ENUM("DAC Modular Rate", dac_modular_rate_enum),
+ /* R_ASRC PG 0 ADDR 0x28 */
+ SOC_SINGLE("ASRC Out High Bandwidth Switch",
+ R_ASRC, FB_ASRC_ASRCOBW, 1, 0),
+ SOC_SINGLE("ASRC In High Bandwidth Switch",
+ R_ASRC, FB_ASRC_ASRCIBW, 1, 0),
+ /* R_I2SIDCTL PG 0 ADDR 0x38 */
+ SOC_ENUM("I2S 1 Data In Control", data_in_ctrl_enums[0]),
+ SOC_ENUM("I2S 2 Data In Control", data_in_ctrl_enums[1]),
+ SOC_ENUM("I2S 3 Data In Control", data_in_ctrl_enums[2]),
+ /* R_I2SODCTL PG 0 ADDR 0x39 */
+ SOC_ENUM("I2S 1 Data Out Control", data_out_ctrl_enums[0]),
+ SOC_ENUM("I2S 2 Data Out Control", data_out_ctrl_enums[1]),
+ SOC_ENUM("I2S 3 Data Out Control", data_out_ctrl_enums[2]),
+ /* R_AUDIOMUX1 PG 0 ADDR 0x3A */
+ SOC_ENUM("ASRC In", asrc_in_mux_enum),
+ /* R_AUDIOMUX2 PG 0 ADDR 0x3B */
+ SOC_ENUM("ASRC Out", asrc_out_mux_enum),
+ /* R_HSDCTL1 PG 1 ADDR 0x01 */
+ SOC_ENUM("Headphone Jack Type", hp_jack_type_enum),
+ SOC_ENUM("Headset Detection Polarity", hs_det_pol_enum),
+ SOC_SINGLE("Headphone Detection Switch",
+ R_HSDCTL1, FB_HSDCTL1_HPID_EN, 1, 0),
+ SOC_SINGLE("Headset OMTP/CTIA Switch",
+ R_HSDCTL1, FB_HSDCTL1_GBLHS_EN, 1, 0),
+ /* R_HSDCTL1 PG 1 ADDR 0x02 */
+ SOC_ENUM("Headset Mic Bias Force", hs_mic_bias_force_enum),
+ SOC_SINGLE("Manual Mic Bias Switch",
+ R_HSDCTL2, FB_HSDCTL2_MB1MODE, 1, 0),
+ SOC_SINGLE("Ring/Sleeve Auto Switch",
+ R_HSDCTL2, FB_HSDCTL2_SWMODE, 1, 0),
+ SOC_ENUM("Manual Mode Plug Type", plug_type_force_enum),
+ /* R_CH0AIC PG 1 ADDR 0x06 */
+ SOC_SINGLE_TLV("Input Boost Channel 0 Volume", R_CH0AIC,
+ FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr),
+ /* R_CH1AIC PG 1 ADDR 0x07 */
+ SOC_SINGLE_TLV("Input Boost Channel 1 Volume", R_CH1AIC,
+ FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr),
+ /* R_CH2AIC PG 1 ADDR 0x08 */
+ SOC_SINGLE_TLV("Input Boost Channel 2 Volume", R_CH2AIC,
+ FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr),
+ /* R_CH3AIC PG 1 ADDR 0x09 */
+ SOC_SINGLE_TLV("Input Boost Channel 3 Volume", R_CH3AIC,
+ FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr),
+ /* R_ICTL0 PG 1 ADDR 0x0A */
+ SOC_ENUM("Input Channel 1 Polarity", in_pol_ch1_enum),
+ SOC_ENUM("Input Channel 0 Polarity", in_pol_ch0_enum),
+ SOC_ENUM("Input Processor Channel 0/1 Operation",
+ in_proc_ch01_sel_enum),
+ SOC_SINGLE("Input Channel 1 Mute Switch",
+ R_ICTL0, FB_ICTL0_IN1MUTE, 1, 0),
+ SOC_SINGLE("Input Channel 0 Mute Switch",
+ R_ICTL0, FB_ICTL0_IN0MUTE, 1, 0),
+ SOC_SINGLE("Input Channel 1 HPF Disable Switch",
+ R_ICTL0, FB_ICTL0_IN1HP, 1, 0),
+ SOC_SINGLE("Input Channel 0 HPF Disable Switch",
+ R_ICTL0, FB_ICTL0_IN0HP, 1, 0),
+ /* R_ICTL1 PG 1 ADDR 0x0B */
+ SOC_ENUM("Input Channel 3 Polarity", in_pol_ch3_enum),
+ SOC_ENUM("Input Channel 2 Polarity", in_pol_ch2_enum),
+ SOC_ENUM("Input Processor Channel 2/3 Operation",
+ in_proc_ch23_sel_enum),
+ SOC_SINGLE("Input Channel 3 Mute Switch",
+ R_ICTL1, FB_ICTL1_IN3MUTE, 1, 0),
+ SOC_SINGLE("Input Channel 2 Mute Switch",
+ R_ICTL1, FB_ICTL1_IN2MUTE, 1, 0),
+ SOC_SINGLE("Input Channel 3 HPF Disable Switch",
+ R_ICTL1, FB_ICTL1_IN3HP, 1, 0),
+ SOC_SINGLE("Input Channel 2 HPF Disable Switch",
+ R_ICTL1, FB_ICTL1_IN2HP, 1, 0),
+ /* R_MICBIAS PG 1 ADDR 0x0C */
+ SOC_ENUM("Mic Bias 2 Voltage", mic_bias_2_enum),
+ SOC_ENUM("Mic Bias 1 Voltage", mic_bias_1_enum),
+ /* R_PGACTL0 PG 1 ADDR 0x0D */
+ SOC_SINGLE("Input Channel 0 PGA Mute Switch",
+ R_PGACTL0, FB_PGACTL_PGAMUTE, 1, 0),
+ SOC_SINGLE_TLV("Input Channel 0 PGA Volume", R_PGACTL0,
+ FB_PGACTL_PGAVOL,
+ FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr),
+ /* R_PGACTL1 PG 1 ADDR 0x0E */
+ SOC_SINGLE("Input Channel 1 PGA Mute Switch",
+ R_PGACTL1, FB_PGACTL_PGAMUTE, 1, 0),
+ SOC_SINGLE_TLV("Input Channel 1 PGA Volume", R_PGACTL1,
+ FB_PGACTL_PGAVOL,
+ FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr),
+ /* R_PGACTL2 PG 1 ADDR 0x0F */
+ SOC_SINGLE("Input Channel 2 PGA Mute Switch",
+ R_PGACTL2, FB_PGACTL_PGAMUTE, 1, 0),
+ SOC_SINGLE_TLV("Input Channel 2 PGA Volume", R_PGACTL2,
+ FB_PGACTL_PGAVOL,
+ FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr),
+ /* R_PGACTL3 PG 1 ADDR 0x10 */
+ SOC_SINGLE("Input Channel 3 PGA Mute Switch",
+ R_PGACTL3, FB_PGACTL_PGAMUTE, 1, 0),
+ SOC_SINGLE_TLV("Input Channel 3 PGA Volume", R_PGACTL3,
+ FB_PGACTL_PGAVOL,
+ FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr),
+ /* R_ICH0VOL PG 1 ADDR 0x12 */
+ SOC_SINGLE_TLV("Input Channel 0 Volume", R_ICH0VOL,
+ FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr),
+ /* R_ICH1VOL PG 1 ADDR 0x13 */
+ SOC_SINGLE_TLV("Input Channel 1 Volume", R_ICH1VOL,
+ FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr),
+ /* R_ICH2VOL PG 1 ADDR 0x14 */
+ SOC_SINGLE_TLV("Input Channel 2 Volume", R_ICH2VOL,
+ FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr),
+ /* R_ICH3VOL PG 1 ADDR 0x15 */
+ SOC_SINGLE_TLV("Input Channel 3 Volume", R_ICH3VOL,
+ FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr),
+ /* R_ASRCILVOL PG 1 ADDR 0x16 */
+ SOC_SINGLE_TLV("ASRC Input Left Volume", R_ASRCILVOL,
+ FB_ASRCILVOL_ASRCILVOL, FM_ASRCILVOL_ASRCILVOL,
+ 0, asrc_vol_tlv_arr),
+ /* R_ASRCIRVOL PG 1 ADDR 0x17 */
+ SOC_SINGLE_TLV("ASRC Input Right Volume", R_ASRCIRVOL,
+ FB_ASRCIRVOL_ASRCIRVOL, FM_ASRCIRVOL_ASRCIRVOL,
+ 0, asrc_vol_tlv_arr),
+ /* R_ASRCOLVOL PG 1 ADDR 0x18 */
+ SOC_SINGLE_TLV("ASRC Output Left Volume", R_ASRCOLVOL,
+ FB_ASRCOLVOL_ASRCOLVOL, FM_ASRCOLVOL_ASRCOLVOL,
+ 0, asrc_vol_tlv_arr),
+ /* R_ASRCORVOL PG 1 ADDR 0x19 */
+ SOC_SINGLE_TLV("ASRC Output Right Volume", R_ASRCORVOL,
+ FB_ASRCORVOL_ASRCOLVOL, FM_ASRCORVOL_ASRCOLVOL,
+ 0, asrc_vol_tlv_arr),
+ /* R_IVOLCTLU PG 1 ADDR 0x1C */
+ /* R_ALCCTL0 PG 1 ADDR 0x1D */
+ SOC_ENUM("ALC Mode", alc_mode_enum),
+ SOC_ENUM("ALC Reference", alc_ref_enum),
+ SOC_SINGLE("Input Channel 3 ALC Switch",
+ R_ALCCTL0, FB_ALCCTL0_ALCEN3, 1, 0),
+ SOC_SINGLE("Input Channel 2 ALC Switch",
+ R_ALCCTL0, FB_ALCCTL0_ALCEN2, 1, 0),
+ SOC_SINGLE("Input Channel 1 ALC Switch",
+ R_ALCCTL0, FB_ALCCTL0_ALCEN1, 1, 0),
+ SOC_SINGLE("Input Channel 0 ALC Switch",
+ R_ALCCTL0, FB_ALCCTL0_ALCEN0, 1, 0),
+ /* R_ALCCTL1 PG 1 ADDR 0x1E */
+ SOC_SINGLE_TLV("ALC Max Gain Volume", R_ALCCTL1,
+ FB_ALCCTL1_MAXGAIN, FM_ALCCTL1_MAXGAIN,
+ 0, alc_max_gain_tlv_arr),
+ SOC_SINGLE_TLV("ALC Target Volume", R_ALCCTL1,
+ FB_ALCCTL1_ALCL, FM_ALCCTL1_ALCL,
+ 0, alc_target_tlv_arr),
+ /* R_ALCCTL2 PG 1 ADDR 0x1F */
+ SOC_SINGLE("ALC Zero Cross Switch",
+ R_ALCCTL2, FB_ALCCTL2_ALCZC, 1, 0),
+ SOC_SINGLE_TLV("ALC Min Gain Volume", R_ALCCTL2,
+ FB_ALCCTL2_MINGAIN, FM_ALCCTL2_MINGAIN,
+ 0, alc_min_gain_tlv_arr),
+ SOC_SINGLE_RANGE("ALC Hold", R_ALCCTL2,
+ FB_ALCCTL2_HLD, 0, FM_ALCCTL2_HLD, 0),
+ /* R_ALCCTL3 PG 1 ADDR 0x20 */
+ SOC_SINGLE_RANGE("ALC Decay", R_ALCCTL3,
+ FB_ALCCTL3_DCY, 0, FM_ALCCTL3_DCY, 0),
+ SOC_SINGLE_RANGE("ALC Attack", R_ALCCTL3,
+ FB_ALCCTL3_ATK, 0, FM_ALCCTL3_ATK, 0),
+ /* R_NGATE PG 1 ADDR 0x21 */
+ SOC_SINGLE_TLV("Noise Gate Threshold Volume", R_NGATE,
+ FB_NGATE_NGTH, FM_NGATE_NGTH, 0, ngth_tlv_arr),
+ SOC_ENUM("Noise Gate Type", ngate_type_enum),
+ SOC_SINGLE("Noise Gate Switch", R_NGATE, FB_NGATE_NGAT, 1, 0),
+ /* R_DMICCTL PG 1 ADDR 0x22 */
+ SOC_SINGLE("Digital Mic 2 Switch", R_DMICCTL, FB_DMICCTL_DMIC2EN, 1, 0),
+ SOC_SINGLE("Digital Mic 1 Switch", R_DMICCTL, FB_DMICCTL_DMIC1EN, 1, 0),
+ SOC_ENUM("Digital Mic Mono Select", dmic_mono_sel_enum),
+ /* R_DACCTL PG 2 ADDR 0x01 */
+ SOC_ENUM("DAC Polarity Left", dac_pol_r_enum),
+ SOC_ENUM("DAC Polarity Right", dac_pol_l_enum),
+ SOC_ENUM("DAC Dither", dac_dith_enum),
+ SOC_SINGLE("DAC Mute Switch", R_DACCTL, FB_DACCTL_DACMUTE, 1, 0),
+ SOC_SINGLE("DAC De-Emphasis Switch", R_DACCTL, FB_DACCTL_DACDEM, 1, 0),
+ /* R_SPKCTL PG 2 ADDR 0x02 */
+ SOC_ENUM("Speaker Polarity Right", spk_pol_r_enum),
+ SOC_ENUM("Speaker Polarity Left", spk_pol_l_enum),
+ SOC_SINGLE("Speaker Mute Switch", R_SPKCTL, FB_SPKCTL_SPKMUTE, 1, 0),
+ SOC_SINGLE("Speaker De-Emphasis Switch",
+ R_SPKCTL, FB_SPKCTL_SPKDEM, 1, 0),
+ /* R_SUBCTL PG 2 ADDR 0x03 */
+ SOC_ENUM("Sub Polarity", sub_pol_enum),
+ SOC_SINGLE("SUB Mute Switch", R_SUBCTL, FB_SUBCTL_SUBMUTE, 1, 0),
+ SOC_SINGLE("Sub De-Emphasis Switch", R_SUBCTL, FB_SUBCTL_SUBDEM, 1, 0),
+ /* R_DCCTL PG 2 ADDR 0x04 */
+ SOC_SINGLE("Sub DC Removal Switch", R_DCCTL, FB_DCCTL_SUBDCBYP, 1, 1),
+ SOC_SINGLE("DAC DC Removal Switch", R_DCCTL, FB_DCCTL_DACDCBYP, 1, 1),
+ SOC_SINGLE("Speaker DC Removal Switch",
+ R_DCCTL, FB_DCCTL_SPKDCBYP, 1, 1),
+ SOC_SINGLE("DC Removal Coefficient Switch", R_DCCTL, FB_DCCTL_DCCOEFSEL,
+ FM_DCCTL_DCCOEFSEL, 0),
+ /* R_OVOLCTLU PG 2 ADDR 0x06 */
+ SOC_SINGLE("Output Fade Switch", R_OVOLCTLU, FB_OVOLCTLU_OFADE, 1, 0),
+ /* R_MVOLL PG 2 ADDR 0x08 */
+ /* R_MVOLR PG 2 ADDR 0x09 */
+ SOC_DOUBLE_R_TLV("Master Volume", R_MVOLL, R_MVOLR,
+ FB_MVOLL_MVOL_L, FM_MVOLL_MVOL_L, 0, mvol_tlv_arr),
+ /* R_HPVOLL PG 2 ADDR 0x0A */
+ /* R_HPVOLR PG 2 ADDR 0x0B */
+ SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR,
+ FB_HPVOLL_HPVOL_L, FM_HPVOLL_HPVOL_L, 0,
+ hp_vol_tlv_arr),
+ /* R_SPKVOLL PG 2 ADDR 0x0C */
+ /* R_SPKVOLR PG 2 ADDR 0x0D */
+ SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR,
+ FB_SPKVOLL_SPKVOL_L, FM_SPKVOLL_SPKVOL_L, 0,
+ spk_vol_tlv_arr),
+ /* R_SUBVOL PG 2 ADDR 0x10 */
+ SOC_SINGLE_TLV("Sub Volume", R_SUBVOL,
+ FB_SUBVOL_SUBVOL, FM_SUBVOL_SUBVOL, 0, spk_vol_tlv_arr),
+ /* R_SPKEQFILT PG 3 ADDR 0x01 */
+ SOC_SINGLE("Speaker EQ 2 Switch",
+ R_SPKEQFILT, FB_SPKEQFILT_EQ2EN, 1, 0),
+ SOC_ENUM("Speaker EQ 2 Band", spk_eq_enums[0]),
+ SOC_SINGLE("Speaker EQ 1 Switch",
+ R_SPKEQFILT, FB_SPKEQFILT_EQ1EN, 1, 0),
+ SOC_ENUM("Speaker EQ 1 Band", spk_eq_enums[1]),
+ /* R_SPKMBCEN PG 3 ADDR 0x0A */
+ SOC_SINGLE("Speaker MBC 3 Switch",
+ R_SPKMBCEN, FB_SPKMBCEN_MBCEN3, 1, 0),
+ SOC_SINGLE("Speaker MBC 2 Switch",
+ R_SPKMBCEN, FB_SPKMBCEN_MBCEN2, 1, 0),
+ SOC_SINGLE("Speaker MBC 1 Switch",
+ R_SPKMBCEN, FB_SPKMBCEN_MBCEN1, 1, 0),
+ /* R_SPKMBCCTL PG 3 ADDR 0x0B */
+ SOC_ENUM("Speaker MBC 3 Mode", spk_mbc3_lvl_det_mode_enum),
+ SOC_ENUM("Speaker MBC 3 Window", spk_mbc3_win_sel_enum),
+ SOC_ENUM("Speaker MBC 2 Mode", spk_mbc2_lvl_det_mode_enum),
+ SOC_ENUM("Speaker MBC 2 Window", spk_mbc2_win_sel_enum),
+ SOC_ENUM("Speaker MBC 1 Mode", spk_mbc1_lvl_det_mode_enum),
+ SOC_ENUM("Speaker MBC 1 Window", spk_mbc1_win_sel_enum),
+ /* R_SPKMBCMUG1 PG 3 ADDR 0x0C */
+ SOC_ENUM("Speaker MBC 1 Phase Polarity", spk_mbc1_phase_pol_enum),
+ SOC_SINGLE_TLV("Speaker MBC1 Make-Up Gain Volume", R_SPKMBCMUG1,
+ FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_SPKMBCTHR1 PG 3 ADDR 0x0D */
+ SOC_SINGLE_TLV("Speaker MBC 1 Compressor Threshold Volume",
+ R_SPKMBCTHR1, FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SPKMBCRAT1 PG 3 ADDR 0x0E */
+ SOC_ENUM("Speaker MBC 1 Compressor Ratio", spk_mbc1_comp_rat_enum),
+ /* R_SPKMBCATK1L PG 3 ADDR 0x0F */
+ /* R_SPKMBCATK1H PG 3 ADDR 0x10 */
+ SND_SOC_BYTES("Speaker MBC 1 Attack", R_SPKMBCATK1L, 2),
+ /* R_SPKMBCREL1L PG 3 ADDR 0x11 */
+ /* R_SPKMBCREL1H PG 3 ADDR 0x12 */
+ SND_SOC_BYTES("Speaker MBC 1 Release", R_SPKMBCREL1L, 2),
+ /* R_SPKMBCMUG2 PG 3 ADDR 0x13 */
+ SOC_ENUM("Speaker MBC 2 Phase Polarity", spk_mbc2_phase_pol_enum),
+ SOC_SINGLE_TLV("Speaker MBC2 Make-Up Gain Volume", R_SPKMBCMUG2,
+ FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_SPKMBCTHR2 PG 3 ADDR 0x14 */
+ SOC_SINGLE_TLV("Speaker MBC 2 Compressor Threshold Volume",
+ R_SPKMBCTHR2, FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SPKMBCRAT2 PG 3 ADDR 0x15 */
+ SOC_ENUM("Speaker MBC 2 Compressor Ratio", spk_mbc2_comp_rat_enum),
+ /* R_SPKMBCATK2L PG 3 ADDR 0x16 */
+ /* R_SPKMBCATK2H PG 3 ADDR 0x17 */
+ SND_SOC_BYTES("Speaker MBC 2 Attack", R_SPKMBCATK2L, 2),
+ /* R_SPKMBCREL2L PG 3 ADDR 0x18 */
+ /* R_SPKMBCREL2H PG 3 ADDR 0x19 */
+ SND_SOC_BYTES("Speaker MBC 2 Release", R_SPKMBCREL2L, 2),
+ /* R_SPKMBCMUG3 PG 3 ADDR 0x1A */
+ SOC_ENUM("Speaker MBC 3 Phase Polarity", spk_mbc3_phase_pol_enum),
+ SOC_SINGLE_TLV("Speaker MBC 3 Make-Up Gain Volume", R_SPKMBCMUG3,
+ FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_SPKMBCTHR3 PG 3 ADDR 0x1B */
+ SOC_SINGLE_TLV("Speaker MBC 3 Threshold Volume", R_SPKMBCTHR3,
+ FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SPKMBCRAT3 PG 3 ADDR 0x1C */
+ SOC_ENUM("Speaker MBC 3 Compressor Ratio", spk_mbc3_comp_rat_enum),
+ /* R_SPKMBCATK3L PG 3 ADDR 0x1D */
+ /* R_SPKMBCATK3H PG 3 ADDR 0x1E */
+ SND_SOC_BYTES("Speaker MBC 3 Attack", R_SPKMBCATK3L, 3),
+ /* R_SPKMBCREL3L PG 3 ADDR 0x1F */
+ /* R_SPKMBCREL3H PG 3 ADDR 0x20 */
+ SND_SOC_BYTES("Speaker MBC 3 Release", R_SPKMBCREL3L, 3),
+ /* R_SPKCLECTL PG 3 ADDR 0x21 */
+ SOC_ENUM("Speaker CLE Level Mode", spk_cle_lvl_mode_enum),
+ SOC_ENUM("Speaker CLE Window", spk_cle_win_sel_enum),
+ SOC_SINGLE("Speaker CLE Expander Switch",
+ R_SPKCLECTL, FB_SPKCLECTL_EXPEN, 1, 0),
+ SOC_SINGLE("Speaker CLE Limiter Switch",
+ R_SPKCLECTL, FB_SPKCLECTL_LIMEN, 1, 0),
+ SOC_SINGLE("Speaker CLE Compressor Switch",
+ R_SPKCLECTL, FB_SPKCLECTL_COMPEN, 1, 0),
+ /* R_SPKCLEMUG PG 3 ADDR 0x22 */
+ SOC_SINGLE_TLV("Speaker CLE Make-Up Gain Volume", R_SPKCLEMUG,
+ FB_SPKCLEMUG_MUGAIN, FM_SPKCLEMUG_MUGAIN,
+ 0, cle_mug_tlv_arr),
+ /* R_SPKCOMPTHR PG 3 ADDR 0x23 */
+ SOC_SINGLE_TLV("Speaker Compressor Threshold Volume", R_SPKCOMPTHR,
+ FB_SPKCOMPTHR_THRESH, FM_SPKCOMPTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SPKCOMPRAT PG 3 ADDR 0x24 */
+ SOC_ENUM("Speaker Compressor Ratio", spk_comp_rat_enum),
+ /* R_SPKCOMPATKL PG 3 ADDR 0x25 */
+ /* R_SPKCOMPATKH PG 3 ADDR 0x26 */
+ SND_SOC_BYTES("Speaker Compressor Attack", R_SPKCOMPATKL, 2),
+ /* R_SPKCOMPRELL PG 3 ADDR 0x27 */
+ /* R_SPKCOMPRELH PG 3 ADDR 0x28 */
+ SND_SOC_BYTES("Speaker Compressor Release", R_SPKCOMPRELL, 2),
+ /* R_SPKLIMTHR PG 3 ADDR 0x29 */
+ SOC_SINGLE_TLV("Speaker Limiter Threshold Volume", R_SPKLIMTHR,
+ FB_SPKLIMTHR_THRESH, FM_SPKLIMTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SPKLIMTGT PG 3 ADDR 0x2A */
+ SOC_SINGLE_TLV("Speaker Limiter Target Volume", R_SPKLIMTGT,
+ FB_SPKLIMTGT_TARGET, FM_SPKLIMTGT_TARGET,
+ 0, thr_tlv_arr),
+ /* R_SPKLIMATKL PG 3 ADDR 0x2B */
+ /* R_SPKLIMATKH PG 3 ADDR 0x2C */
+ SND_SOC_BYTES("Speaker Limiter Attack", R_SPKLIMATKL, 2),
+ /* R_SPKLIMRELL PG 3 ADDR 0x2D */
+ /* R_SPKLIMRELR PG 3 ADDR 0x2E */
+ SND_SOC_BYTES("Speaker Limiter Release", R_SPKLIMRELL, 2),
+ /* R_SPKEXPTHR PG 3 ADDR 0x2F */
+ SOC_SINGLE_TLV("Speaker Expander Threshold Volume", R_SPKEXPTHR,
+ FB_SPKEXPTHR_THRESH, FM_SPKEXPTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SPKEXPRAT PG 3 ADDR 0x30 */
+ SOC_ENUM("Speaker Expander Ratio", spk_exp_rat_enum),
+ /* R_SPKEXPATKL PG 3 ADDR 0x31 */
+ /* R_SPKEXPATKR PG 3 ADDR 0x32 */
+ SND_SOC_BYTES("Speaker Expander Attack", R_SPKEXPATKL, 2),
+ /* R_SPKEXPRELL PG 3 ADDR 0x33 */
+ /* R_SPKEXPRELR PG 3 ADDR 0x34 */
+ SND_SOC_BYTES("Speaker Expander Release", R_SPKEXPRELL, 2),
+ /* R_SPKFXCTL PG 3 ADDR 0x35 */
+ SOC_SINGLE("Speaker 3D Switch", R_SPKFXCTL, FB_SPKFXCTL_3DEN, 1, 0),
+ SOC_SINGLE("Speaker Treble Enhancement Switch",
+ R_SPKFXCTL, FB_SPKFXCTL_TEEN, 1, 0),
+ SOC_SINGLE("Speaker Treble NLF Switch",
+ R_SPKFXCTL, FB_SPKFXCTL_TNLFBYP, 1, 1),
+ SOC_SINGLE("Speaker Bass Enhancement Switch",
+ R_SPKFXCTL, FB_SPKFXCTL_BEEN, 1, 0),
+ SOC_SINGLE("Speaker Bass NLF Switch",
+ R_SPKFXCTL, FB_SPKFXCTL_BNLFBYP, 1, 1),
+ /* R_DACEQFILT PG 4 ADDR 0x01 */
+ SOC_SINGLE("DAC EQ 2 Switch",
+ R_DACEQFILT, FB_DACEQFILT_EQ2EN, 1, 0),
+ SOC_ENUM("DAC EQ 2 Band", dac_eq_enums[0]),
+ SOC_SINGLE("DAC EQ 1 Switch", R_DACEQFILT, FB_DACEQFILT_EQ1EN, 1, 0),
+ SOC_ENUM("DAC EQ 1 Band", dac_eq_enums[1]),
+ /* R_DACMBCEN PG 4 ADDR 0x0A */
+ SOC_SINGLE("DAC MBC 3 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN3, 1, 0),
+ SOC_SINGLE("DAC MBC 2 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN2, 1, 0),
+ SOC_SINGLE("DAC MBC 1 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN1, 1, 0),
+ /* R_DACMBCCTL PG 4 ADDR 0x0B */
+ SOC_ENUM("DAC MBC 3 Mode", dac_mbc3_lvl_det_mode_enum),
+ SOC_ENUM("DAC MBC 3 Window", dac_mbc3_win_sel_enum),
+ SOC_ENUM("DAC MBC 2 Mode", dac_mbc2_lvl_det_mode_enum),
+ SOC_ENUM("DAC MBC 2 Window", dac_mbc2_win_sel_enum),
+ SOC_ENUM("DAC MBC 1 Mode", dac_mbc1_lvl_det_mode_enum),
+ SOC_ENUM("DAC MBC 1 Window", dac_mbc1_win_sel_enum),
+ /* R_DACMBCMUG1 PG 4 ADDR 0x0C */
+ SOC_ENUM("DAC MBC 1 Phase Polarity", dac_mbc1_phase_pol_enum),
+ SOC_SINGLE_TLV("DAC MBC 1 Make-Up Gain Volume", R_DACMBCMUG1,
+ FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_DACMBCTHR1 PG 4 ADDR 0x0D */
+ SOC_SINGLE_TLV("DAC MBC 1 Compressor Threshold Volume", R_DACMBCTHR1,
+ FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_DACMBCRAT1 PG 4 ADDR 0x0E */
+ SOC_ENUM("DAC MBC 1 Compressor Ratio", dac_mbc1_comp_rat_enum),
+ /* R_DACMBCATK1L PG 4 ADDR 0x0F */
+ /* R_DACMBCATK1H PG 4 ADDR 0x10 */
+ SND_SOC_BYTES("DAC MBC 1 Attack", R_DACMBCATK1L, 2),
+ /* R_DACMBCREL1L PG 4 ADDR 0x11 */
+ /* R_DACMBCREL1H PG 4 ADDR 0x12 */
+ SND_SOC_BYTES("DAC MBC 1 Release", R_DACMBCREL1L, 2),
+ /* R_DACMBCMUG2 PG 4 ADDR 0x13 */
+ SOC_ENUM("DAC MBC 2 Phase Polarity", dac_mbc2_phase_pol_enum),
+ SOC_SINGLE_TLV("DAC MBC 2 Make-Up Gain Volume", R_DACMBCMUG2,
+ FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_DACMBCTHR2 PG 4 ADDR 0x14 */
+ SOC_SINGLE_TLV("DAC MBC 2 Compressor Threshold Volume", R_DACMBCTHR2,
+ FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_DACMBCRAT2 PG 4 ADDR 0x15 */
+ SOC_ENUM("DAC MBC 2 Compressor Ratio", dac_mbc2_comp_rat_enum),
+ /* R_DACMBCATK2L PG 4 ADDR 0x16 */
+ /* R_DACMBCATK2H PG 4 ADDR 0x17 */
+ SND_SOC_BYTES("DAC MBC 2 Attack", R_DACMBCATK2L, 2),
+ /* R_DACMBCREL2L PG 4 ADDR 0x18 */
+ /* R_DACMBCREL2H PG 4 ADDR 0x19 */
+ SND_SOC_BYTES("DAC MBC 2 Release", R_DACMBCREL2L, 2),
+ /* R_DACMBCMUG3 PG 4 ADDR 0x1A */
+ SOC_ENUM("DAC MBC 3 Phase Polarity", dac_mbc3_phase_pol_enum),
+ SOC_SINGLE_TLV("DAC MBC 3 Make-Up Gain Volume", R_DACMBCMUG3,
+ FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_DACMBCTHR3 PG 4 ADDR 0x1B */
+ SOC_SINGLE_TLV("DAC MBC 3 Threshold Volume", R_DACMBCTHR3,
+ FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_DACMBCRAT3 PG 4 ADDR 0x1C */
+ SOC_ENUM("DAC MBC 3 Compressor Ratio", dac_mbc3_comp_rat_enum),
+ /* R_DACMBCATK3L PG 4 ADDR 0x1D */
+ /* R_DACMBCATK3H PG 4 ADDR 0x1E */
+ SND_SOC_BYTES("DAC MBC 3 Attack", R_DACMBCATK3L, 3),
+ /* R_DACMBCREL3L PG 4 ADDR 0x1F */
+ /* R_DACMBCREL3H PG 4 ADDR 0x20 */
+ SND_SOC_BYTES("DAC MBC 3 Release", R_DACMBCREL3L, 3),
+ /* R_DACCLECTL PG 4 ADDR 0x21 */
+ SOC_ENUM("DAC CLE Level Mode", dac_cle_lvl_mode_enum),
+ SOC_ENUM("DAC CLE Window", dac_cle_win_sel_enum),
+ SOC_SINGLE("DAC CLE Expander Switch",
+ R_DACCLECTL, FB_DACCLECTL_EXPEN, 1, 0),
+ SOC_SINGLE("DAC CLE Limiter Switch",
+ R_DACCLECTL, FB_DACCLECTL_LIMEN, 1, 0),
+ SOC_SINGLE("DAC CLE Compressor Switch",
+ R_DACCLECTL, FB_DACCLECTL_COMPEN, 1, 0),
+ /* R_DACCLEMUG PG 4 ADDR 0x22 */
+ SOC_SINGLE_TLV("DAC CLE Make-Up Gain Volume", R_DACCLEMUG,
+ FB_DACCLEMUG_MUGAIN, FM_DACCLEMUG_MUGAIN,
+ 0, cle_mug_tlv_arr),
+ /* R_DACCOMPTHR PG 4 ADDR 0x23 */
+ SOC_SINGLE_TLV("DAC Compressor Threshold Volume", R_DACCOMPTHR,
+ FB_DACCOMPTHR_THRESH, FM_DACCOMPTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_DACCOMPRAT PG 4 ADDR 0x24 */
+ SOC_ENUM("DAC Compressor Ratio", dac_comp_rat_enum),
+ /* R_DACCOMPATKL PG 4 ADDR 0x25 */
+ /* R_DACCOMPATKH PG 4 ADDR 0x26 */
+ SND_SOC_BYTES("DAC Compressor Attack", R_DACCOMPATKL, 2),
+ /* R_DACCOMPRELL PG 4 ADDR 0x27 */
+ /* R_DACCOMPRELH PG 4 ADDR 0x28 */
+ SND_SOC_BYTES("DAC Compressor Release", R_DACCOMPRELL, 2),
+ /* R_DACLIMTHR PG 4 ADDR 0x29 */
+ SOC_SINGLE_TLV("DAC Limiter Threshold Volume", R_DACLIMTHR,
+ FB_DACLIMTHR_THRESH, FM_DACLIMTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_DACLIMTGT PG 4 ADDR 0x2A */
+ SOC_SINGLE_TLV("DAC Limiter Target Volume", R_DACLIMTGT,
+ FB_DACLIMTGT_TARGET, FM_DACLIMTGT_TARGET,
+ 0, thr_tlv_arr),
+ /* R_DACLIMATKL PG 4 ADDR 0x2B */
+ /* R_DACLIMATKH PG 4 ADDR 0x2C */
+ SND_SOC_BYTES("DAC Limiter Attack", R_DACLIMATKL, 2),
+ /* R_DACLIMRELL PG 4 ADDR 0x2D */
+ /* R_DACLIMRELR PG 4 ADDR 0x2E */
+ SND_SOC_BYTES("DAC Limiter Release", R_DACLIMRELL, 2),
+ /* R_DACEXPTHR PG 4 ADDR 0x2F */
+ SOC_SINGLE_TLV("DAC Expander Threshold Volume", R_DACEXPTHR,
+ FB_DACEXPTHR_THRESH, FM_DACEXPTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_DACEXPRAT PG 4 ADDR 0x30 */
+ SOC_ENUM("DAC Expander Ratio", dac_exp_rat_enum),
+ /* R_DACEXPATKL PG 4 ADDR 0x31 */
+ /* R_DACEXPATKR PG 4 ADDR 0x32 */
+ SND_SOC_BYTES("DAC Expander Attack", R_DACEXPATKL, 2),
+ /* R_DACEXPRELL PG 4 ADDR 0x33 */
+ /* R_DACEXPRELR PG 4 ADDR 0x34 */
+ SND_SOC_BYTES("DAC Expander Release", R_DACEXPRELL, 2),
+ /* R_DACFXCTL PG 4 ADDR 0x35 */
+ SOC_SINGLE("DAC 3D Switch", R_DACFXCTL, FB_DACFXCTL_3DEN, 1, 0),
+ SOC_SINGLE("DAC Treble Enhancement Switch",
+ R_DACFXCTL, FB_DACFXCTL_TEEN, 1, 0),
+ SOC_SINGLE("DAC Treble NLF Switch",
+ R_DACFXCTL, FB_DACFXCTL_TNLFBYP, 1, 1),
+ SOC_SINGLE("DAC Bass Enhancement Switch",
+ R_DACFXCTL, FB_DACFXCTL_BEEN, 1, 0),
+ SOC_SINGLE("DAC Bass NLF Switch",
+ R_DACFXCTL, FB_DACFXCTL_BNLFBYP, 1, 1),
+ /* R_SUBEQFILT PG 5 ADDR 0x01 */
+ SOC_SINGLE("Sub EQ 2 Switch",
+ R_SUBEQFILT, FB_SUBEQFILT_EQ2EN, 1, 0),
+ SOC_ENUM("Sub EQ 2 Band", sub_eq_enums[0]),
+ SOC_SINGLE("Sub EQ 1 Switch", R_SUBEQFILT, FB_SUBEQFILT_EQ1EN, 1, 0),
+ SOC_ENUM("Sub EQ 1 Band", sub_eq_enums[1]),
+ /* R_SUBMBCEN PG 5 ADDR 0x0A */
+ SOC_SINGLE("Sub MBC 3 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN3, 1, 0),
+ SOC_SINGLE("Sub MBC 2 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN2, 1, 0),
+ SOC_SINGLE("Sub MBC 1 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN1, 1, 0),
+ /* R_SUBMBCCTL PG 5 ADDR 0x0B */
+ SOC_ENUM("Sub MBC 3 Mode", sub_mbc3_lvl_det_mode_enum),
+ SOC_ENUM("Sub MBC 3 Window", sub_mbc3_win_sel_enum),
+ SOC_ENUM("Sub MBC 2 Mode", sub_mbc2_lvl_det_mode_enum),
+ SOC_ENUM("Sub MBC 2 Window", sub_mbc2_win_sel_enum),
+ SOC_ENUM("Sub MBC 1 Mode", sub_mbc1_lvl_det_mode_enum),
+ SOC_ENUM("Sub MBC 1 Window", sub_mbc1_win_sel_enum),
+ /* R_SUBMBCMUG1 PG 5 ADDR 0x0C */
+ SOC_ENUM("Sub MBC 1 Phase Polarity", sub_mbc1_phase_pol_enum),
+ SOC_SINGLE_TLV("Sub MBC 1 Make-Up Gain Volume", R_SUBMBCMUG1,
+ FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_SUBMBCTHR1 PG 5 ADDR 0x0D */
+ SOC_SINGLE_TLV("Sub MBC 1 Compressor Threshold Volume", R_SUBMBCTHR1,
+ FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SUBMBCRAT1 PG 5 ADDR 0x0E */
+ SOC_ENUM("Sub MBC 1 Compressor Ratio", sub_mbc1_comp_rat_enum),
+ /* R_SUBMBCATK1L PG 5 ADDR 0x0F */
+ /* R_SUBMBCATK1H PG 5 ADDR 0x10 */
+ SND_SOC_BYTES("Sub MBC 1 Attack", R_SUBMBCATK1L, 2),
+ /* R_SUBMBCREL1L PG 5 ADDR 0x11 */
+ /* R_SUBMBCREL1H PG 5 ADDR 0x12 */
+ SND_SOC_BYTES("Sub MBC 1 Release", R_SUBMBCREL1L, 2),
+ /* R_SUBMBCMUG2 PG 5 ADDR 0x13 */
+ SOC_ENUM("Sub MBC 2 Phase Polarity", sub_mbc2_phase_pol_enum),
+ SOC_SINGLE_TLV("Sub MBC 2 Make-Up Gain Volume", R_SUBMBCMUG2,
+ FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_SUBMBCTHR2 PG 5 ADDR 0x14 */
+ SOC_SINGLE_TLV("Sub MBC 2 Compressor Threshold Volume", R_SUBMBCTHR2,
+ FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SUBMBCRAT2 PG 5 ADDR 0x15 */
+ SOC_ENUM("Sub MBC 2 Compressor Ratio", sub_mbc2_comp_rat_enum),
+ /* R_SUBMBCATK2L PG 5 ADDR 0x16 */
+ /* R_SUBMBCATK2H PG 5 ADDR 0x17 */
+ SND_SOC_BYTES("Sub MBC 2 Attack", R_SUBMBCATK2L, 2),
+ /* R_SUBMBCREL2L PG 5 ADDR 0x18 */
+ /* R_SUBMBCREL2H PG 5 ADDR 0x19 */
+ SND_SOC_BYTES("Sub MBC 2 Release", R_SUBMBCREL2L, 2),
+ /* R_SUBMBCMUG3 PG 5 ADDR 0x1A */
+ SOC_ENUM("Sub MBC 3 Phase Polarity", sub_mbc3_phase_pol_enum),
+ SOC_SINGLE_TLV("Sub MBC 3 Make-Up Gain Volume", R_SUBMBCMUG3,
+ FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN,
+ 0, mbc_mug_tlv_arr),
+ /* R_SUBMBCTHR3 PG 5 ADDR 0x1B */
+ SOC_SINGLE_TLV("Sub MBC 3 Threshold Volume", R_SUBMBCTHR3,
+ FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SUBMBCRAT3 PG 5 ADDR 0x1C */
+ SOC_ENUM("Sub MBC 3 Compressor Ratio", sub_mbc3_comp_rat_enum),
+ /* R_SUBMBCATK3L PG 5 ADDR 0x1D */
+ /* R_SUBMBCATK3H PG 5 ADDR 0x1E */
+ SND_SOC_BYTES("Sub MBC 3 Attack", R_SUBMBCATK3L, 3),
+ /* R_SUBMBCREL3L PG 5 ADDR 0x1F */
+ /* R_SUBMBCREL3H PG 5 ADDR 0x20 */
+ SND_SOC_BYTES("Sub MBC 3 Release", R_SUBMBCREL3L, 3),
+ /* R_SUBCLECTL PG 5 ADDR 0x21 */
+ SOC_ENUM("Sub CLE Level Mode", sub_cle_lvl_mode_enum),
+ SOC_ENUM("Sub CLE Window", sub_cle_win_sel_enum),
+ SOC_SINGLE("Sub CLE Expander Switch",
+ R_SUBCLECTL, FB_SUBCLECTL_EXPEN, 1, 0),
+ SOC_SINGLE("Sub CLE Limiter Switch",
+ R_SUBCLECTL, FB_SUBCLECTL_LIMEN, 1, 0),
+ SOC_SINGLE("Sub CLE Compressor Switch",
+ R_SUBCLECTL, FB_SUBCLECTL_COMPEN, 1, 0),
+ /* R_SUBCLEMUG PG 5 ADDR 0x22 */
+ SOC_SINGLE_TLV("Sub CLE Make-Up Gain Volume", R_SUBCLEMUG,
+ FB_SUBCLEMUG_MUGAIN, FM_SUBCLEMUG_MUGAIN,
+ 0, cle_mug_tlv_arr),
+ /* R_SUBCOMPTHR PG 5 ADDR 0x23 */
+ SOC_SINGLE_TLV("Sub Compressor Threshold Volume", R_SUBCOMPTHR,
+ FB_SUBCOMPTHR_THRESH, FM_SUBCOMPTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SUBCOMPRAT PG 5 ADDR 0x24 */
+ SOC_ENUM("Sub Compressor Ratio", sub_comp_rat_enum),
+ /* R_SUBCOMPATKL PG 5 ADDR 0x25 */
+ /* R_SUBCOMPATKH PG 5 ADDR 0x26 */
+ SND_SOC_BYTES("Sub Compressor Attack", R_SUBCOMPATKL, 2),
+ /* R_SUBCOMPRELL PG 5 ADDR 0x27 */
+ /* R_SUBCOMPRELH PG 5 ADDR 0x28 */
+ SND_SOC_BYTES("Sub Compressor Release", R_SUBCOMPRELL, 2),
+ /* R_SUBLIMTHR PG 5 ADDR 0x29 */
+ SOC_SINGLE_TLV("Sub Limiter Threshold Volume", R_SUBLIMTHR,
+ FB_SUBLIMTHR_THRESH, FM_SUBLIMTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SUBLIMTGT PG 5 ADDR 0x2A */
+ SOC_SINGLE_TLV("Sub Limiter Target Volume", R_SUBLIMTGT,
+ FB_SUBLIMTGT_TARGET, FM_SUBLIMTGT_TARGET,
+ 0, thr_tlv_arr),
+ /* R_SUBLIMATKL PG 5 ADDR 0x2B */
+ /* R_SUBLIMATKH PG 5 ADDR 0x2C */
+ SND_SOC_BYTES("Sub Limiter Attack", R_SUBLIMATKL, 2),
+ /* R_SUBLIMRELL PG 5 ADDR 0x2D */
+ /* R_SUBLIMRELR PG 5 ADDR 0x2E */
+ SND_SOC_BYTES("Sub Limiter Release", R_SUBLIMRELL, 2),
+ /* R_SUBEXPTHR PG 5 ADDR 0x2F */
+ SOC_SINGLE_TLV("Sub Expander Threshold Volume", R_SUBEXPTHR,
+ FB_SUBEXPTHR_THRESH, FM_SUBEXPTHR_THRESH,
+ 0, thr_tlv_arr),
+ /* R_SUBEXPRAT PG 5 ADDR 0x30 */
+ SOC_ENUM("Sub Expander Ratio", sub_exp_rat_enum),
+ /* R_SUBEXPATKL PG 5 ADDR 0x31 */
+ /* R_SUBEXPATKR PG 5 ADDR 0x32 */
+ SND_SOC_BYTES("Sub Expander Attack", R_SUBEXPATKL, 2),
+ /* R_SUBEXPRELL PG 5 ADDR 0x33 */
+ /* R_SUBEXPRELR PG 5 ADDR 0x34 */
+ SND_SOC_BYTES("Sub Expander Release", R_SUBEXPRELL, 2),
+ /* R_SUBFXCTL PG 5 ADDR 0x35 */
+ SOC_SINGLE("Sub Treble Enhancement Switch",
+ R_SUBFXCTL, FB_SUBFXCTL_TEEN, 1, 0),
+ SOC_SINGLE("Sub Treble NLF Switch",
+ R_SUBFXCTL, FB_SUBFXCTL_TNLFBYP, 1, 1),
+ SOC_SINGLE("Sub Bass Enhancement Switch",
+ R_SUBFXCTL, FB_SUBFXCTL_BEEN, 1, 0),
+ SOC_SINGLE("Sub Bass NLF Switch",
+ R_SUBFXCTL, FB_SUBFXCTL_BNLFBYP, 1, 1),
+ COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00),
+ COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05),
+ COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a),
+ COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f),
+ COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14),
+ COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19),
+
+ COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20),
+ COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25),
+ COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a),
+ COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f),
+ COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34),
+ COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39),
+
+ COEFF_RAM_CTL("DAC Cascade 1 Left Prescale", COEFF_SIZE, 0x1f),
+ COEFF_RAM_CTL("DAC Cascade 1 Right Prescale", COEFF_SIZE, 0x3f),
+
+ COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40),
+ COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45),
+ COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a),
+ COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f),
+ COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54),
+ COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59),
+
+ COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60),
+ COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65),
+ COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a),
+ COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f),
+ COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74),
+ COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79),
+
+ COEFF_RAM_CTL("DAC Cascade 2 Left Prescale", COEFF_SIZE, 0x5f),
+ COEFF_RAM_CTL("DAC Cascade 2 Right Prescale", COEFF_SIZE, 0x7f),
+
+ COEFF_RAM_CTL("DAC Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80),
+ COEFF_RAM_CTL("DAC Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85),
+
+ COEFF_RAM_CTL("DAC Bass Non Linear Function 1", COEFF_SIZE, 0x8a),
+ COEFF_RAM_CTL("DAC Bass Non Linear Function 2", COEFF_SIZE, 0x8b),
+
+ COEFF_RAM_CTL("DAC Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c),
+
+ COEFF_RAM_CTL("DAC Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91),
+
+ COEFF_RAM_CTL("DAC Bass Mix", COEFF_SIZE, 0x96),
+
+ COEFF_RAM_CTL("DAC Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97),
+ COEFF_RAM_CTL("DAC Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c),
+
+ COEFF_RAM_CTL("DAC Treb Non Linear Function 1", COEFF_SIZE, 0xa1),
+ COEFF_RAM_CTL("DAC Treb Non Linear Function 2", COEFF_SIZE, 0xa2),
+
+ COEFF_RAM_CTL("DAC Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3),
+
+ COEFF_RAM_CTL("DAC Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8),
+
+ COEFF_RAM_CTL("DAC Treb Mix", COEFF_SIZE, 0xad),
+
+ COEFF_RAM_CTL("DAC 3D", COEFF_SIZE, 0xae),
+
+ COEFF_RAM_CTL("DAC 3D Mix", COEFF_SIZE, 0xaf),
+
+ COEFF_RAM_CTL("DAC MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0),
+ COEFF_RAM_CTL("DAC MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5),
+
+ COEFF_RAM_CTL("DAC MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba),
+ COEFF_RAM_CTL("DAC MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf),
+
+ COEFF_RAM_CTL("DAC MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4),
+ COEFF_RAM_CTL("DAC MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9),
+
+ COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00),
+ COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05),
+ COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a),
+ COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f),
+ COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14),
+ COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19),
+
+ COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20),
+ COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25),
+ COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a),
+ COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f),
+ COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34),
+ COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39),
+
+ COEFF_RAM_CTL("Speaker Cascade 1 Left Prescale", COEFF_SIZE, 0x1f),
+ COEFF_RAM_CTL("Speaker Cascade 1 Right Prescale", COEFF_SIZE, 0x3f),
+
+ COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40),
+ COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45),
+ COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a),
+ COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f),
+ COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54),
+ COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59),
+
+ COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60),
+ COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65),
+ COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a),
+ COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f),
+ COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74),
+ COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79),
+
+ COEFF_RAM_CTL("Speaker Cascade 2 Left Prescale", COEFF_SIZE, 0x5f),
+ COEFF_RAM_CTL("Speaker Cascade 2 Right Prescale", COEFF_SIZE, 0x7f),
+
+ COEFF_RAM_CTL("Speaker Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80),
+ COEFF_RAM_CTL("Speaker Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85),
+
+ COEFF_RAM_CTL("Speaker Bass Non Linear Function 1", COEFF_SIZE, 0x8a),
+ COEFF_RAM_CTL("Speaker Bass Non Linear Function 2", COEFF_SIZE, 0x8b),
+
+ COEFF_RAM_CTL("Speaker Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c),
+
+ COEFF_RAM_CTL("Speaker Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91),
+
+ COEFF_RAM_CTL("Speaker Bass Mix", COEFF_SIZE, 0x96),
+
+ COEFF_RAM_CTL("Speaker Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97),
+ COEFF_RAM_CTL("Speaker Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c),
+
+ COEFF_RAM_CTL("Speaker Treb Non Linear Function 1", COEFF_SIZE, 0xa1),
+ COEFF_RAM_CTL("Speaker Treb Non Linear Function 2", COEFF_SIZE, 0xa2),
+
+ COEFF_RAM_CTL("Speaker Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3),
+
+ COEFF_RAM_CTL("Speaker Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8),
+
+ COEFF_RAM_CTL("Speaker Treb Mix", COEFF_SIZE, 0xad),
+
+ COEFF_RAM_CTL("Speaker 3D", COEFF_SIZE, 0xae),
+
+ COEFF_RAM_CTL("Speaker 3D Mix", COEFF_SIZE, 0xaf),
+
+ COEFF_RAM_CTL("Speaker MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0),
+ COEFF_RAM_CTL("Speaker MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5),
+
+ COEFF_RAM_CTL("Speaker MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba),
+ COEFF_RAM_CTL("Speaker MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf),
+
+ COEFF_RAM_CTL("Speaker MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4),
+ COEFF_RAM_CTL("Speaker MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9),
+
+ COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00),
+ COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05),
+ COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a),
+ COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f),
+ COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14),
+ COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19),
+
+ COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20),
+ COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25),
+ COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a),
+ COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f),
+ COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34),
+ COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39),
+
+ COEFF_RAM_CTL("Sub Cascade 1 Left Prescale", COEFF_SIZE, 0x1f),
+ COEFF_RAM_CTL("Sub Cascade 1 Right Prescale", COEFF_SIZE, 0x3f),
+
+ COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40),
+ COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45),
+ COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a),
+ COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f),
+ COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54),
+ COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59),
+
+ COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60),
+ COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65),
+ COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a),
+ COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f),
+ COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74),
+ COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79),
+
+ COEFF_RAM_CTL("Sub Cascade 2 Left Prescale", COEFF_SIZE, 0x5f),
+ COEFF_RAM_CTL("Sub Cascade 2 Right Prescale", COEFF_SIZE, 0x7f),
+
+ COEFF_RAM_CTL("Sub Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80),
+ COEFF_RAM_CTL("Sub Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85),
+
+ COEFF_RAM_CTL("Sub Bass Non Linear Function 1", COEFF_SIZE, 0x8a),
+ COEFF_RAM_CTL("Sub Bass Non Linear Function 2", COEFF_SIZE, 0x8b),
+
+ COEFF_RAM_CTL("Sub Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c),
+
+ COEFF_RAM_CTL("Sub Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91),
+
+ COEFF_RAM_CTL("Sub Bass Mix", COEFF_SIZE, 0x96),
+
+ COEFF_RAM_CTL("Sub Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97),
+ COEFF_RAM_CTL("Sub Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c),
+
+ COEFF_RAM_CTL("Sub Treb Non Linear Function 1", COEFF_SIZE, 0xa1),
+ COEFF_RAM_CTL("Sub Treb Non Linear Function 2", COEFF_SIZE, 0xa2),
+
+ COEFF_RAM_CTL("Sub Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3),
+
+ COEFF_RAM_CTL("Sub Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8),
+
+ COEFF_RAM_CTL("Sub Treb Mix", COEFF_SIZE, 0xad),
+
+ COEFF_RAM_CTL("Sub 3D", COEFF_SIZE, 0xae),
+
+ COEFF_RAM_CTL("Sub 3D Mix", COEFF_SIZE, 0xaf),
+
+ COEFF_RAM_CTL("Sub MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0),
+ COEFF_RAM_CTL("Sub MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5),
+
+ COEFF_RAM_CTL("Sub MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba),
+ COEFF_RAM_CTL("Sub MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf),
+
+ COEFF_RAM_CTL("Sub MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4),
+ COEFF_RAM_CTL("Sub MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9),
+};
+
+static struct snd_soc_dapm_widget const tscs454_dapm_widgets[] = {
+ /* R_PLLCTL PG 0 ADDR 0x15 */
+ SND_SOC_DAPM_SUPPLY("PLL 1 Power", R_PLLCTL, FB_PLLCTL_PU_PLL1, 0,
+ pll_power_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PLL 2 Power", R_PLLCTL, FB_PLLCTL_PU_PLL2, 0,
+ pll_power_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ /* R_I2SPINC0 PG 0 ADDR 0x22 */
+ SND_SOC_DAPM_AIF_OUT("DAI 3 Out", "DAI 3 Capture", 0,
+ R_I2SPINC0, FB_I2SPINC0_SDO3TRI, 1),
+ SND_SOC_DAPM_AIF_OUT("DAI 2 Out", "DAI 2 Capture", 0,
+ R_I2SPINC0, FB_I2SPINC0_SDO2TRI, 1),
+ SND_SOC_DAPM_AIF_OUT("DAI 1 Out", "DAI 1 Capture", 0,
+ R_I2SPINC0, FB_I2SPINC0_SDO1TRI, 1),
+ /* R_PWRM0 PG 0 ADDR 0x33 */
+ SND_SOC_DAPM_ADC("Input Processor Channel 3", NULL,
+ R_PWRM0, FB_PWRM0_INPROC3PU, 0),
+ SND_SOC_DAPM_ADC("Input Processor Channel 2", NULL,
+ R_PWRM0, FB_PWRM0_INPROC2PU, 0),
+ SND_SOC_DAPM_ADC("Input Processor Channel 1", NULL,
+ R_PWRM0, FB_PWRM0_INPROC1PU, 0),
+ SND_SOC_DAPM_ADC("Input Processor Channel 0", NULL,
+ R_PWRM0, FB_PWRM0_INPROC0PU, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias 2",
+ R_PWRM0, FB_PWRM0_MICB2PU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias 1", R_PWRM0,
+ FB_PWRM0_MICB1PU, 0, NULL, 0),
+ /* R_PWRM1 PG 0 ADDR 0x34 */
+ SND_SOC_DAPM_SUPPLY("Sub Power", R_PWRM1, FB_PWRM1_SUBPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Left Power",
+ R_PWRM1, FB_PWRM1_HPLPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Right Power",
+ R_PWRM1, FB_PWRM1_HPRPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Speaker Left Power",
+ R_PWRM1, FB_PWRM1_SPKLPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Speaker Right Power",
+ R_PWRM1, FB_PWRM1_SPKRPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Differential Input 2 Power",
+ R_PWRM1, FB_PWRM1_D2S2PU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Differential Input 1 Power",
+ R_PWRM1, FB_PWRM1_D2S1PU, 0, NULL, 0),
+ /* R_PWRM2 PG 0 ADDR 0x35 */
+ SND_SOC_DAPM_SUPPLY("DAI 3 Out Power",
+ R_PWRM2, FB_PWRM2_I2S3OPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAI 2 Out Power",
+ R_PWRM2, FB_PWRM2_I2S2OPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAI 1 Out Power",
+ R_PWRM2, FB_PWRM2_I2S1OPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAI 3 In Power",
+ R_PWRM2, FB_PWRM2_I2S3IPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAI 2 In Power",
+ R_PWRM2, FB_PWRM2_I2S2IPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAI 1 In Power",
+ R_PWRM2, FB_PWRM2_I2S1IPU, 0, NULL, 0),
+ /* R_PWRM3 PG 0 ADDR 0x36 */
+ SND_SOC_DAPM_SUPPLY("Line Out Left Power",
+ R_PWRM3, FB_PWRM3_LLINEPU, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Line Out Right Power",
+ R_PWRM3, FB_PWRM3_RLINEPU, 0, NULL, 0),
+ /* R_PWRM4 PG 0 ADDR 0x37 */
+ SND_SOC_DAPM_DAC("Sub", NULL, R_PWRM4, FB_PWRM4_OPSUBPU, 0),
+ SND_SOC_DAPM_DAC("DAC Left", NULL, R_PWRM4, FB_PWRM4_OPDACLPU, 0),
+ SND_SOC_DAPM_DAC("DAC Right", NULL, R_PWRM4, FB_PWRM4_OPDACRPU, 0),
+ SND_SOC_DAPM_DAC("ClassD Left", NULL, R_PWRM4, FB_PWRM4_OPSPKLPU, 0),
+ SND_SOC_DAPM_DAC("ClassD Right", NULL, R_PWRM4, FB_PWRM4_OPSPKRPU, 0),
+ /* R_AUDIOMUX1 PG 0 ADDR 0x3A */
+ SND_SOC_DAPM_MUX("DAI 2 Out Mux", SND_SOC_NOPM, 0, 0,
+ &dai2_mux_dapm_enum),
+ SND_SOC_DAPM_MUX("DAI 1 Out Mux", SND_SOC_NOPM, 0, 0,
+ &dai1_mux_dapm_enum),
+ /* R_AUDIOMUX2 PG 0 ADDR 0x3B */
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
+ &dac_mux_dapm_enum),
+ SND_SOC_DAPM_MUX("DAI 3 Out Mux", SND_SOC_NOPM, 0, 0,
+ &dai3_mux_dapm_enum),
+ /* R_AUDIOMUX3 PG 0 ADDR 0x3C */
+ SND_SOC_DAPM_MUX("Sub Mux", SND_SOC_NOPM, 0, 0,
+ &sub_mux_dapm_enum),
+ SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
+ &classd_mux_dapm_enum),
+ /* R_HSDCTL1 PG 1 ADDR 0x01 */
+ SND_SOC_DAPM_SUPPLY("GHS Detect Power", R_HSDCTL1,
+ FB_HSDCTL1_CON_DET_PWD, 1, NULL, 0),
+ /* R_CH0AIC PG 1 ADDR 0x06 */
+ SND_SOC_DAPM_MUX("Input Boost Channel 0 Mux", SND_SOC_NOPM, 0, 0,
+ &in_bst_mux_ch0_dapm_enum),
+ SND_SOC_DAPM_MUX("ADC Channel 0 Mux", SND_SOC_NOPM, 0, 0,
+ &adc_mux_ch0_dapm_enum),
+ SND_SOC_DAPM_MUX("Input Processor Channel 0 Mux", SND_SOC_NOPM, 0, 0,
+ &in_proc_mux_ch0_dapm_enum),
+ /* R_CH1AIC PG 1 ADDR 0x07 */
+ SND_SOC_DAPM_MUX("Input Boost Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &in_bst_mux_ch1_dapm_enum),
+ SND_SOC_DAPM_MUX("ADC Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &adc_mux_ch1_dapm_enum),
+ SND_SOC_DAPM_MUX("Input Processor Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &in_proc_mux_ch1_dapm_enum),
+ /* Virtual */
+ SND_SOC_DAPM_AIF_IN("DAI 3 In", "DAI 3 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAI 2 In", "DAI 2 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAI 1 In", "DAI 1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("PLLs", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("Sub Out"),
+ SND_SOC_DAPM_OUTPUT("Headphone Left"),
+ SND_SOC_DAPM_OUTPUT("Headphone Right"),
+ SND_SOC_DAPM_OUTPUT("Speaker Left"),
+ SND_SOC_DAPM_OUTPUT("Speaker Right"),
+ SND_SOC_DAPM_OUTPUT("Line Out Left"),
+ SND_SOC_DAPM_OUTPUT("Line Out Right"),
+ SND_SOC_DAPM_INPUT("D2S 2"),
+ SND_SOC_DAPM_INPUT("D2S 1"),
+ SND_SOC_DAPM_INPUT("Line In 1 Left"),
+ SND_SOC_DAPM_INPUT("Line In 1 Right"),
+ SND_SOC_DAPM_INPUT("Line In 2 Left"),
+ SND_SOC_DAPM_INPUT("Line In 2 Right"),
+ SND_SOC_DAPM_INPUT("Line In 3 Left"),
+ SND_SOC_DAPM_INPUT("Line In 3 Right"),
+ SND_SOC_DAPM_INPUT("DMic 1"),
+ SND_SOC_DAPM_INPUT("DMic 2"),
+
+ SND_SOC_DAPM_MUX("CH 0_1 Mux", SND_SOC_NOPM, 0, 0,
+ &ch_0_1_mux_dapm_enum),
+ SND_SOC_DAPM_MUX("CH 2_3 Mux", SND_SOC_NOPM, 0, 0,
+ &ch_2_3_mux_dapm_enum),
+ SND_SOC_DAPM_MUX("CH 4_5 Mux", SND_SOC_NOPM, 0, 0,
+ &ch_4_5_mux_dapm_enum),
+};
+
+static struct snd_soc_dapm_route const tscs454_intercon[] = {
+ /* PLLs */
+ {"PLLs", NULL, "PLL 1 Power", pll_connected},
+ {"PLLs", NULL, "PLL 2 Power", pll_connected},
+ /* Inputs */
+ {"DAI 3 In", NULL, "DAI 3 In Power"},
+ {"DAI 2 In", NULL, "DAI 2 In Power"},
+ {"DAI 1 In", NULL, "DAI 1 In Power"},
+ /* Outputs */
+ {"DAI 3 Out", NULL, "DAI 3 Out Power"},
+ {"DAI 2 Out", NULL, "DAI 2 Out Power"},
+ {"DAI 1 Out", NULL, "DAI 1 Out Power"},
+ /* Ch Muxing */
+ {"CH 0_1 Mux", "DAI 1", "DAI 1 In"},
+ {"CH 0_1 Mux", "TDM 0_1", "DAI 1 In"},
+ {"CH 2_3 Mux", "DAI 2", "DAI 2 In"},
+ {"CH 2_3 Mux", "TDM 2_3", "DAI 1 In"},
+ {"CH 4_5 Mux", "DAI 3", "DAI 2 In"},
+ {"CH 4_5 Mux", "TDM 4_5", "DAI 1 In"},
+ /* In/Out Muxing */
+ {"DAI 1 Out Mux", "CH 0_1", "CH 0_1 Mux"},
+ {"DAI 1 Out Mux", "CH 2_3", "CH 2_3 Mux"},
+ {"DAI 1 Out Mux", "CH 4_5", "CH 4_5 Mux"},
+ {"DAI 2 Out Mux", "CH 0_1", "CH 0_1 Mux"},
+ {"DAI 2 Out Mux", "CH 2_3", "CH 2_3 Mux"},
+ {"DAI 2 Out Mux", "CH 4_5", "CH 4_5 Mux"},
+ {"DAI 3 Out Mux", "CH 0_1", "CH 0_1 Mux"},
+ {"DAI 3 Out Mux", "CH 2_3", "CH 2_3 Mux"},
+ {"DAI 3 Out Mux", "CH 4_5", "CH 4_5 Mux"},
+ /******************
+ * Playback Paths *
+ ******************/
+ /* DAC Path */
+ {"DAC Mux", "CH 4_5", "CH 4_5 Mux"},
+ {"DAC Mux", "CH 2_3", "CH 2_3 Mux"},
+ {"DAC Mux", "CH 0_1", "CH 0_1 Mux"},
+ {"DAC Left", NULL, "DAC Mux"},
+ {"DAC Right", NULL, "DAC Mux"},
+ {"DAC Left", NULL, "PLLs"},
+ {"DAC Right", NULL, "PLLs"},
+ {"Headphone Left", NULL, "Headphone Left Power"},
+ {"Headphone Right", NULL, "Headphone Right Power"},
+ {"Headphone Left", NULL, "DAC Left"},
+ {"Headphone Right", NULL, "DAC Right"},
+ /* Line Out */
+ {"Line Out Left", NULL, "Line Out Left Power"},
+ {"Line Out Right", NULL, "Line Out Right Power"},
+ {"Line Out Left", NULL, "DAC Left"},
+ {"Line Out Right", NULL, "DAC Right"},
+ /* ClassD Path */
+ {"Speaker Mux", "CH 4_5", "CH 4_5 Mux"},
+ {"Speaker Mux", "CH 2_3", "CH 2_3 Mux"},
+ {"Speaker Mux", "CH 0_1", "CH 0_1 Mux"},
+ {"ClassD Left", NULL, "Speaker Mux"},
+ {"ClassD Right", NULL, "Speaker Mux"},
+ {"ClassD Left", NULL, "PLLs"},
+ {"ClassD Right", NULL, "PLLs"},
+ {"Speaker Left", NULL, "Speaker Left Power"},
+ {"Speaker Right", NULL, "Speaker Right Power"},
+ {"Speaker Left", NULL, "ClassD Left"},
+ {"Speaker Right", NULL, "ClassD Right"},
+ /* Sub Path */
+ {"Sub Mux", "CH 4", "CH 4_5 Mux"},
+ {"Sub Mux", "CH 5", "CH 4_5 Mux"},
+ {"Sub Mux", "CH 4 + 5", "CH 4_5 Mux"},
+ {"Sub Mux", "CH 2", "CH 2_3 Mux"},
+ {"Sub Mux", "CH 3", "CH 2_3 Mux"},
+ {"Sub Mux", "CH 2 + 3", "CH 2_3 Mux"},
+ {"Sub Mux", "CH 0", "CH 0_1 Mux"},
+ {"Sub Mux", "CH 1", "CH 0_1 Mux"},
+ {"Sub Mux", "CH 0 + 1", "CH 0_1 Mux"},
+ {"Sub Mux", "ADC/DMic 1 Left", "Input Processor Channel 0"},
+ {"Sub Mux", "ADC/DMic 1 Right", "Input Processor Channel 1"},
+ {"Sub Mux", "ADC/DMic 1 Left Plus Right", "Input Processor Channel 0"},
+ {"Sub Mux", "ADC/DMic 1 Left Plus Right", "Input Processor Channel 1"},
+ {"Sub Mux", "DMic 2 Left", "DMic 2"},
+ {"Sub Mux", "DMic 2 Right", "DMic 2"},
+ {"Sub Mux", "DMic 2 Left Plus Right", "DMic 2"},
+ {"Sub Mux", "ClassD Left", "ClassD Left"},
+ {"Sub Mux", "ClassD Right", "ClassD Right"},
+ {"Sub Mux", "ClassD Left Plus Right", "ClassD Left"},
+ {"Sub Mux", "ClassD Left Plus Right", "ClassD Right"},
+ {"Sub", NULL, "Sub Mux"},
+ {"Sub", NULL, "PLLs"},
+ {"Sub Out", NULL, "Sub Power"},
+ {"Sub Out", NULL, "Sub"},
+ /*****************
+ * Capture Paths *
+ *****************/
+ {"Input Boost Channel 0 Mux", "Input 3", "Line In 3 Left"},
+ {"Input Boost Channel 0 Mux", "Input 2", "Line In 2 Left"},
+ {"Input Boost Channel 0 Mux", "Input 1", "Line In 1 Left"},
+ {"Input Boost Channel 0 Mux", "D2S", "D2S 1"},
+
+ {"Input Boost Channel 1 Mux", "Input 3", "Line In 3 Right"},
+ {"Input Boost Channel 1 Mux", "Input 2", "Line In 2 Right"},
+ {"Input Boost Channel 1 Mux", "Input 1", "Line In 1 Right"},
+ {"Input Boost Channel 1 Mux", "D2S", "D2S 2"},
+
+ {"ADC Channel 0 Mux", "Input 3 Boost Bypass", "Line In 3 Left"},
+ {"ADC Channel 0 Mux", "Input 2 Boost Bypass", "Line In 2 Left"},
+ {"ADC Channel 0 Mux", "Input 1 Boost Bypass", "Line In 1 Left"},
+ {"ADC Channel 0 Mux", "Input Boost", "Input Boost Channel 0 Mux"},
+
+ {"ADC Channel 1 Mux", "Input 3 Boost Bypass", "Line In 3 Right"},
+ {"ADC Channel 1 Mux", "Input 2 Boost Bypass", "Line In 2 Right"},
+ {"ADC Channel 1 Mux", "Input 1 Boost Bypass", "Line In 1 Right"},
+ {"ADC Channel 1 Mux", "Input Boost", "Input Boost Channel 1 Mux"},
+
+ {"Input Processor Channel 0 Mux", "ADC", "ADC Channel 0 Mux"},
+ {"Input Processor Channel 0 Mux", "DMic", "DMic 1"},
+
+ {"Input Processor Channel 0", NULL, "PLLs"},
+ {"Input Processor Channel 0", NULL, "Input Processor Channel 0 Mux"},
+
+ {"Input Processor Channel 1 Mux", "ADC", "ADC Channel 1 Mux"},
+ {"Input Processor Channel 1 Mux", "DMic", "DMic 1"},
+
+ {"Input Processor Channel 1", NULL, "PLLs"},
+ {"Input Processor Channel 1", NULL, "Input Processor Channel 1 Mux"},
+
+ {"Input Processor Channel 2", NULL, "PLLs"},
+ {"Input Processor Channel 2", NULL, "DMic 2"},
+
+ {"Input Processor Channel 3", NULL, "PLLs"},
+ {"Input Processor Channel 3", NULL, "DMic 2"},
+
+ {"DAI 1 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"},
+ {"DAI 1 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"},
+ {"DAI 1 Out Mux", "DMic 2", "Input Processor Channel 2"},
+ {"DAI 1 Out Mux", "DMic 2", "Input Processor Channel 3"},
+
+ {"DAI 2 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"},
+ {"DAI 2 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"},
+ {"DAI 2 Out Mux", "DMic 2", "Input Processor Channel 2"},
+ {"DAI 2 Out Mux", "DMic 2", "Input Processor Channel 3"},
+
+ {"DAI 3 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"},
+ {"DAI 3 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"},
+ {"DAI 3 Out Mux", "DMic 2", "Input Processor Channel 2"},
+ {"DAI 3 Out Mux", "DMic 2", "Input Processor Channel 3"},
+
+ {"DAI 1 Out", NULL, "DAI 1 Out Mux"},
+ {"DAI 2 Out", NULL, "DAI 2 Out Mux"},
+ {"DAI 3 Out", NULL, "DAI 3 Out Mux"},
+};
+
+/* This is used when BCLK is sourcing the PLLs */
+static int tscs454_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int bclk_dai;
+
+ dev_dbg(component->dev, "%s(): freq = %u\n", __func__, freq);
+
+ val = snd_soc_component_read(component, R_PLLCTL);
+
+ bclk_dai = (val & FM_PLLCTL_BCLKSEL) >> FB_PLLCTL_BCLKSEL;
+ if (bclk_dai != dai->id)
+ return 0;
+
+ tscs454->bclk_freq = freq;
+ return set_sysclk(component);
+}
+
+static int tscs454_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ unsigned int mask;
+ int ret;
+ struct snd_soc_component *component = dai->component;
+ unsigned int val;
+ int shift;
+
+ dev_dbg(component->dev, "set_bclk_ratio() id = %d ratio = %u\n",
+ dai->id, ratio);
+
+ switch (dai->id) {
+ case TSCS454_DAI1_ID:
+ mask = FM_I2SCMC_BCMP1;
+ shift = FB_I2SCMC_BCMP1;
+ break;
+ case TSCS454_DAI2_ID:
+ mask = FM_I2SCMC_BCMP2;
+ shift = FB_I2SCMC_BCMP2;
+ break;
+ case TSCS454_DAI3_ID:
+ mask = FM_I2SCMC_BCMP3;
+ shift = FB_I2SCMC_BCMP3;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unknown audio interface (%d)\n", ret);
+ return ret;
+ }
+
+ switch (ratio) {
+ case 32:
+ val = I2SCMC_BCMP_32X;
+ break;
+ case 40:
+ val = I2SCMC_BCMP_40X;
+ break;
+ case 64:
+ val = I2SCMC_BCMP_64X;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unsupported bclk ratio (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ R_I2SCMC, mask, val << shift);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set DAI BCLK ratio (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int set_aif_provider_from_fmt(struct snd_soc_component *component,
+ struct aif *aif, unsigned int fmt)
+{
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ aif->provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ aif->provider = false;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unsupported format (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int set_aif_tdm_delay(struct snd_soc_component *component,
+ unsigned int dai_id, bool delay)
+{
+ unsigned int reg;
+ int ret;
+
+ switch (dai_id) {
+ case TSCS454_DAI1_ID:
+ reg = R_TDMCTL0;
+ break;
+ case TSCS454_DAI2_ID:
+ reg = R_PCMP2CTL0;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_PCMP3CTL0;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev,
+ "DAI %d unknown (%d)\n", dai_id + 1, ret);
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(component,
+ reg, FM_TDMCTL0_BDELAY, delay);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to setup tdm format (%d)\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int set_aif_format_from_fmt(struct snd_soc_component *component,
+ unsigned int dai_id, unsigned int fmt)
+{
+ unsigned int reg;
+ unsigned int val;
+ int ret;
+
+ switch (dai_id) {
+ case TSCS454_DAI1_ID:
+ reg = R_I2SP1CTL;
+ break;
+ case TSCS454_DAI2_ID:
+ reg = R_I2SP2CTL;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_I2SP3CTL;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev,
+ "DAI %d unknown (%d)\n", dai_id + 1, ret);
+ return ret;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = FV_FORMAT_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = FV_FORMAT_LEFT;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = FV_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret = set_aif_tdm_delay(component, dai_id, true);
+ if (ret < 0)
+ return ret;
+ val = FV_FORMAT_TDM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ret = set_aif_tdm_delay(component, dai_id, false);
+ if (ret < 0)
+ return ret;
+ val = FV_FORMAT_TDM;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Format unsupported (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ reg, FM_I2SPCTL_FORMAT, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set DAI %d format (%d)\n",
+ dai_id + 1, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int
+set_aif_clock_format_from_fmt(struct snd_soc_component *component,
+ unsigned int dai_id, unsigned int fmt)
+{
+ unsigned int reg;
+ unsigned int val;
+ int ret;
+
+ switch (dai_id) {
+ case TSCS454_DAI1_ID:
+ reg = R_I2SP1CTL;
+ break;
+ case TSCS454_DAI2_ID:
+ reg = R_I2SP2CTL;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_I2SP3CTL;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev,
+ "DAI %d unknown (%d)\n", dai_id + 1, ret);
+ return ret;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = FV_BCLKP_NOT_INVERTED | FV_LRCLKP_NOT_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = FV_BCLKP_NOT_INVERTED | FV_LRCLKP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = FV_BCLKP_INVERTED | FV_LRCLKP_NOT_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = FV_BCLKP_INVERTED | FV_LRCLKP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Format unknown (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component, reg,
+ FM_I2SPCTL_BCLKP | FM_I2SPCTL_LRCLKP, val);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set clock polarity for DAI%d (%d)\n",
+ dai_id + 1, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tscs454_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ struct aif *aif = &tscs454->aifs[dai->id];
+ int ret;
+
+ ret = set_aif_provider_from_fmt(component, aif, fmt);
+ if (ret < 0)
+ return ret;
+
+ ret = set_aif_format_from_fmt(component, dai->id, fmt);
+ if (ret < 0)
+ return ret;
+
+ ret = set_aif_clock_format_from_fmt(component, dai->id, fmt);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tscs454_dai1_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val;
+ int ret;
+
+ if (!slots)
+ return 0;
+
+ if (tx_mask >= (1 << slots) || rx_mask >= (1 << slots)) {
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid TDM slot mask (%d)\n", ret);
+ return ret;
+ }
+
+ switch (slots) {
+ case 2:
+ val = FV_TDMSO_2 | FV_TDMSI_2;
+ break;
+ case 4:
+ val = FV_TDMSO_4 | FV_TDMSI_4;
+ break;
+ case 6:
+ val = FV_TDMSO_6 | FV_TDMSI_6;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid number of slots (%d)\n", ret);
+ return ret;
+ }
+
+ switch (slot_width) {
+ case 16:
+ val = val | FV_TDMDSS_16;
+ break;
+ case 24:
+ val = val | FV_TDMDSS_24;
+ break;
+ case 32:
+ val = val | FV_TDMDSS_32;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid TDM slot width (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_write(component, R_TDMCTL1, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set slots (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tscs454_dai23_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int reg;
+ unsigned int val;
+ int ret;
+
+ if (!slots)
+ return 0;
+
+ if (tx_mask >= (1 << slots) || rx_mask >= (1 << slots)) {
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid TDM slot mask (%d)\n", ret);
+ return ret;
+ }
+
+ switch (dai->id) {
+ case TSCS454_DAI2_ID:
+ reg = R_PCMP2CTL1;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_PCMP3CTL1;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unrecognized interface %d (%d)\n",
+ dai->id, ret);
+ return ret;
+ }
+
+ switch (slots) {
+ case 1:
+ val = FV_PCMSOP_1 | FV_PCMSIP_1;
+ break;
+ case 2:
+ val = FV_PCMSOP_2 | FV_PCMSIP_2;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid number of slots (%d)\n", ret);
+ return ret;
+ }
+
+ switch (slot_width) {
+ case 16:
+ val = val | FV_PCMDSSP_16;
+ break;
+ case 24:
+ val = val | FV_PCMDSSP_24;
+ break;
+ case 32:
+ val = val | FV_PCMDSSP_32;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid TDM slot width (%d)\n", ret);
+ return ret;
+ }
+ ret = snd_soc_component_write(component, reg, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set slots (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_aif_fs(struct snd_soc_component *component,
+ unsigned int id,
+ unsigned int rate)
+{
+ unsigned int reg;
+ unsigned int br;
+ unsigned int bm;
+ int ret;
+
+ switch (rate) {
+ case 8000:
+ br = FV_I2SMBR_32;
+ bm = FV_I2SMBM_0PT25;
+ break;
+ case 16000:
+ br = FV_I2SMBR_32;
+ bm = FV_I2SMBM_0PT5;
+ break;
+ case 24000:
+ br = FV_I2SMBR_48;
+ bm = FV_I2SMBM_0PT5;
+ break;
+ case 32000:
+ br = FV_I2SMBR_32;
+ bm = FV_I2SMBM_1;
+ break;
+ case 48000:
+ br = FV_I2SMBR_48;
+ bm = FV_I2SMBM_1;
+ break;
+ case 96000:
+ br = FV_I2SMBR_48;
+ bm = FV_I2SMBM_2;
+ break;
+ case 11025:
+ br = FV_I2SMBR_44PT1;
+ bm = FV_I2SMBM_0PT25;
+ break;
+ case 22050:
+ br = FV_I2SMBR_44PT1;
+ bm = FV_I2SMBM_0PT5;
+ break;
+ case 44100:
+ br = FV_I2SMBR_44PT1;
+ bm = FV_I2SMBM_1;
+ break;
+ case 88200:
+ br = FV_I2SMBR_44PT1;
+ bm = FV_I2SMBM_2;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unsupported sample rate (%d)\n", ret);
+ return ret;
+ }
+
+ switch (id) {
+ case TSCS454_DAI1_ID:
+ reg = R_I2S1MRATE;
+ break;
+ case TSCS454_DAI2_ID:
+ reg = R_I2S2MRATE;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_I2S3MRATE;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "DAI ID not recognized (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component, reg,
+ FM_I2SMRATE_I2SMBR | FM_I2SMRATE_I2SMBM, br|bm);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to update register (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_aif_sample_format(struct snd_soc_component *component,
+ snd_pcm_format_t format,
+ int aif_id)
+{
+ unsigned int reg;
+ unsigned int width;
+ int ret;
+
+ switch (snd_pcm_format_width(format)) {
+ case 16:
+ width = FV_WL_16;
+ break;
+ case 20:
+ width = FV_WL_20;
+ break;
+ case 24:
+ width = FV_WL_24;
+ break;
+ case 32:
+ width = FV_WL_32;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Unsupported format width (%d)\n", ret);
+ return ret;
+ }
+
+ switch (aif_id) {
+ case TSCS454_DAI1_ID:
+ reg = R_I2SP1CTL;
+ break;
+ case TSCS454_DAI2_ID:
+ reg = R_I2SP2CTL;
+ break;
+ case TSCS454_DAI3_ID:
+ reg = R_I2SP3CTL;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "AIF ID not recognized (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ reg, FM_I2SPCTL_WL, width);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set sample width (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tscs454_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ unsigned int fs = params_rate(params);
+ struct aif *aif = &tscs454->aifs[dai->id];
+ unsigned int val;
+ int ret;
+
+ mutex_lock(&tscs454->aifs_status_lock);
+
+ dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__,
+ aif->id, fs);
+
+ if (!aif_active(&tscs454->aifs_status, aif->id)) {
+ if (PLL_44_1K_RATE % fs)
+ aif->pll = &tscs454->pll1;
+ else
+ aif->pll = &tscs454->pll2;
+
+ dev_dbg(component->dev, "Reserving pll %d for aif %d\n",
+ aif->pll->id, aif->id);
+
+ reserve_pll(aif->pll);
+ }
+
+ if (!aifs_active(&tscs454->aifs_status)) { /* First active aif */
+ val = snd_soc_component_read(component, R_ISRC);
+ if ((val & FM_ISRC_IBR) == FV_IBR_48)
+ tscs454->internal_rate.pll = &tscs454->pll1;
+ else
+ tscs454->internal_rate.pll = &tscs454->pll2;
+
+ dev_dbg(component->dev, "Reserving pll %d for ir\n",
+ tscs454->internal_rate.pll->id);
+
+ reserve_pll(tscs454->internal_rate.pll);
+ }
+
+ ret = set_aif_fs(component, aif->id, fs);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set aif fs (%d)\n", ret);
+ goto exit;
+ }
+
+ ret = set_aif_sample_format(component, params_format(params), aif->id);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set aif sample format (%d)\n", ret);
+ goto exit;
+ }
+
+ set_aif_status_active(&tscs454->aifs_status, aif->id,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n",
+ aif->id, tscs454->aifs_status.streams);
+
+ ret = 0;
+exit:
+ mutex_unlock(&tscs454->aifs_status_lock);
+
+ return ret;
+}
+
+static int tscs454_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ struct aif *aif = &tscs454->aifs[dai->id];
+
+ return aif_free(component, aif,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int tscs454_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct snd_soc_component *component = dai->component;
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ struct aif *aif = &tscs454->aifs[dai->id];
+
+ ret = aif_prepare(component, aif);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops const tscs454_dai1_ops = {
+ .set_sysclk = tscs454_set_sysclk,
+ .set_bclk_ratio = tscs454_set_bclk_ratio,
+ .set_fmt = tscs454_set_dai_fmt,
+ .set_tdm_slot = tscs454_dai1_set_tdm_slot,
+ .hw_params = tscs454_hw_params,
+ .hw_free = tscs454_hw_free,
+ .prepare = tscs454_prepare,
+};
+
+static struct snd_soc_dai_ops const tscs454_dai23_ops = {
+ .set_sysclk = tscs454_set_sysclk,
+ .set_bclk_ratio = tscs454_set_bclk_ratio,
+ .set_fmt = tscs454_set_dai_fmt,
+ .set_tdm_slot = tscs454_dai23_set_tdm_slot,
+ .hw_params = tscs454_hw_params,
+ .hw_free = tscs454_hw_free,
+ .prepare = tscs454_prepare,
+};
+
+static int tscs454_probe(struct snd_soc_component *component)
+{
+ struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret = 0;
+
+ switch (tscs454->sysclk_src_id) {
+ case PLL_INPUT_XTAL:
+ val = FV_PLLISEL_XTAL;
+ break;
+ case PLL_INPUT_MCLK1:
+ val = FV_PLLISEL_MCLK1;
+ break;
+ case PLL_INPUT_MCLK2:
+ val = FV_PLLISEL_MCLK2;
+ break;
+ case PLL_INPUT_BCLK:
+ val = FV_PLLISEL_BCLK;
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(component->dev, "Invalid sysclk src id (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component, R_PLLCTL,
+ FM_PLLCTL_PLLISEL, val);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set PLL input (%d)\n", ret);
+ return ret;
+ }
+
+ if (tscs454->sysclk_src_id < PLL_INPUT_BCLK)
+ ret = set_sysclk(component);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_tscs454 = {
+ .probe = tscs454_probe,
+ .dapm_widgets = tscs454_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tscs454_dapm_widgets),
+ .dapm_routes = tscs454_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tscs454_intercon),
+ .controls = tscs454_snd_controls,
+ .num_controls = ARRAY_SIZE(tscs454_snd_controls),
+ .endianness = 1,
+};
+
+#define TSCS454_RATES SNDRV_PCM_RATE_8000_96000
+
+#define TSCS454_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver tscs454_dais[] = {
+ {
+ .name = "tscs454-dai1",
+ .id = TSCS454_DAI1_ID,
+ .playback = {
+ .stream_name = "DAI 1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = TSCS454_RATES,
+ .formats = TSCS454_FORMATS,},
+ .capture = {
+ .stream_name = "DAI 1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = TSCS454_RATES,
+ .formats = TSCS454_FORMATS,},
+ .ops = &tscs454_dai1_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "tscs454-dai2",
+ .id = TSCS454_DAI2_ID,
+ .playback = {
+ .stream_name = "DAI 2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TSCS454_RATES,
+ .formats = TSCS454_FORMATS,},
+ .capture = {
+ .stream_name = "DAI 2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TSCS454_RATES,
+ .formats = TSCS454_FORMATS,},
+ .ops = &tscs454_dai23_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "tscs454-dai3",
+ .id = TSCS454_DAI3_ID,
+ .playback = {
+ .stream_name = "DAI 3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TSCS454_RATES,
+ .formats = TSCS454_FORMATS,},
+ .capture = {
+ .stream_name = "DAI 3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TSCS454_RATES,
+ .formats = TSCS454_FORMATS,},
+ .ops = &tscs454_dai23_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+static char const * const src_names[] = {
+ "xtal", "mclk1", "mclk2", "bclk"};
+
+static int tscs454_i2c_probe(struct i2c_client *i2c)
+{
+ struct tscs454 *tscs454;
+ int src;
+ int ret;
+
+ tscs454 = devm_kzalloc(&i2c->dev, sizeof(*tscs454), GFP_KERNEL);
+ if (!tscs454)
+ return -ENOMEM;
+
+ ret = tscs454_data_init(tscs454, i2c);
+ if (ret < 0)
+ return ret;
+
+ i2c_set_clientdata(i2c, tscs454);
+
+ for (src = PLL_INPUT_XTAL; src < PLL_INPUT_BCLK; src++) {
+ tscs454->sysclk = devm_clk_get(&i2c->dev, src_names[src]);
+ if (!IS_ERR(tscs454->sysclk)) {
+ break;
+ } else if (PTR_ERR(tscs454->sysclk) != -ENOENT) {
+ ret = PTR_ERR(tscs454->sysclk);
+ dev_err(&i2c->dev, "Failed to get sysclk (%d)\n", ret);
+ return ret;
+ }
+ }
+ dev_dbg(&i2c->dev, "PLL input is %s\n", src_names[src]);
+ tscs454->sysclk_src_id = src;
+
+ ret = regmap_write(tscs454->regmap,
+ R_RESET, FV_RESET_PWR_ON_DEFAULTS);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to reset the component (%d)\n", ret);
+ return ret;
+ }
+ regcache_mark_dirty(tscs454->regmap);
+
+ ret = regmap_register_patch(tscs454->regmap, tscs454_patch,
+ ARRAY_SIZE(tscs454_patch));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to apply patch (%d)\n", ret);
+ return ret;
+ }
+ /* Sync pg sel reg with cache */
+ regmap_write(tscs454->regmap, R_PAGESEL, 0x00);
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454,
+ tscs454_dais, ARRAY_SIZE(tscs454_dais));
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to register component (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id tscs454_i2c_id[] = {
+ { "tscs454" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tscs454_i2c_id);
+
+static const struct of_device_id tscs454_of_match[] = {
+ { .compatible = "tempo,tscs454", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tscs454_of_match);
+
+static struct i2c_driver tscs454_i2c_driver = {
+ .driver = {
+ .name = "tscs454",
+ .of_match_table = tscs454_of_match,
+ },
+ .probe = tscs454_i2c_probe,
+ .id_table = tscs454_i2c_id,
+};
+
+module_i2c_driver(tscs454_i2c_driver);
+
+MODULE_AUTHOR("Tempo Semiconductor <steven.eckhoff.opensource@gmail.com");
+MODULE_DESCRIPTION("ASoC TSCS454 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tscs454.h b/sound/soc/codecs/tscs454.h
new file mode 100644
index 000000000000..1142d73d3168
--- /dev/null
+++ b/sound/soc/codecs/tscs454.h
@@ -0,0 +1,2323 @@
+// SPDX-License-Identifier: GPL-2.0
+// tscs454.h -- TSCS454 ALSA SoC Audio driver
+// Copyright 2018 Tempo Semiconductor, Inc.
+// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
+
+#ifndef __REDWOODPUBLIC_H__
+#define __REDWOODPUBLIC_H__
+
+#define VIRT_BASE 0x00
+#define PAGE_LEN 0x100
+#define VIRT_PAGE_BASE(page) (VIRT_BASE + (PAGE_LEN * page))
+#define VIRT_ADDR(page, address) (VIRT_PAGE_BASE(page) + address)
+#define ADDR(page, virt_address) (virt_address - VIRT_PAGE_BASE(page))
+
+#define R_PAGESEL 0x0
+#define R_RESET VIRT_ADDR(0x0, 0x1)
+#define R_IRQEN VIRT_ADDR(0x0, 0x2)
+#define R_IRQMASK VIRT_ADDR(0x0, 0x3)
+#define R_IRQSTAT VIRT_ADDR(0x0, 0x4)
+#define R_DEVADD0 VIRT_ADDR(0x0, 0x6)
+#define R_DEVID VIRT_ADDR(0x0, 0x8)
+#define R_DEVREV VIRT_ADDR(0x0, 0x9)
+#define R_PLLSTAT VIRT_ADDR(0x0, 0x0A)
+#define R_PLL1CTL VIRT_ADDR(0x0, 0x0B)
+#define R_PLL1RDIV VIRT_ADDR(0x0, 0x0C)
+#define R_PLL1ODIV VIRT_ADDR(0x0, 0x0D)
+#define R_PLL1FDIVL VIRT_ADDR(0x0, 0x0E)
+#define R_PLL1FDIVH VIRT_ADDR(0x0, 0x0F)
+#define R_PLL2CTL VIRT_ADDR(0x0, 0x10)
+#define R_PLL2RDIV VIRT_ADDR(0x0, 0x11)
+#define R_PLL2ODIV VIRT_ADDR(0x0, 0x12)
+#define R_PLL2FDIVL VIRT_ADDR(0x0, 0x13)
+#define R_PLL2FDIVH VIRT_ADDR(0x0, 0x14)
+#define R_PLLCTL VIRT_ADDR(0x0, 0x15)
+#define R_ISRC VIRT_ADDR(0x0, 0x16)
+#define R_SCLKCTL VIRT_ADDR(0x0, 0x18)
+#define R_TIMEBASE VIRT_ADDR(0x0, 0x19)
+#define R_I2SP1CTL VIRT_ADDR(0x0, 0x1A)
+#define R_I2SP2CTL VIRT_ADDR(0x0, 0x1B)
+#define R_I2SP3CTL VIRT_ADDR(0x0, 0x1C)
+#define R_I2S1MRATE VIRT_ADDR(0x0, 0x1D)
+#define R_I2S2MRATE VIRT_ADDR(0x0, 0x1E)
+#define R_I2S3MRATE VIRT_ADDR(0x0, 0x1F)
+#define R_I2SCMC VIRT_ADDR(0x0, 0x20)
+#define R_MCLK2PINC VIRT_ADDR(0x0, 0x21)
+#define R_I2SPINC0 VIRT_ADDR(0x0, 0x22)
+#define R_I2SPINC1 VIRT_ADDR(0x0, 0x23)
+#define R_I2SPINC2 VIRT_ADDR(0x0, 0x24)
+#define R_GPIOCTL0 VIRT_ADDR(0x0, 0x25)
+#define R_GPIOCTL1 VIRT_ADDR(0x0, 0x26)
+#define R_ASRC VIRT_ADDR(0x0, 0x28)
+#define R_TDMCTL0 VIRT_ADDR(0x0, 0x2D)
+#define R_TDMCTL1 VIRT_ADDR(0x0, 0x2E)
+#define R_PCMP2CTL0 VIRT_ADDR(0x0, 0x2F)
+#define R_PCMP2CTL1 VIRT_ADDR(0x0, 0x30)
+#define R_PCMP3CTL0 VIRT_ADDR(0x0, 0x31)
+#define R_PCMP3CTL1 VIRT_ADDR(0x0, 0x32)
+#define R_PWRM0 VIRT_ADDR(0x0, 0x33)
+#define R_PWRM1 VIRT_ADDR(0x0, 0x34)
+#define R_PWRM2 VIRT_ADDR(0x0, 0x35)
+#define R_PWRM3 VIRT_ADDR(0x0, 0x36)
+#define R_PWRM4 VIRT_ADDR(0x0, 0x37)
+#define R_I2SIDCTL VIRT_ADDR(0x0, 0x38)
+#define R_I2SODCTL VIRT_ADDR(0x0, 0x39)
+#define R_AUDIOMUX1 VIRT_ADDR(0x0, 0x3A)
+#define R_AUDIOMUX2 VIRT_ADDR(0x0, 0x3B)
+#define R_AUDIOMUX3 VIRT_ADDR(0x0, 0x3C)
+#define R_HSDCTL1 VIRT_ADDR(0x1, 0x1)
+#define R_HSDCTL2 VIRT_ADDR(0x1, 0x2)
+#define R_HSDSTAT VIRT_ADDR(0x1, 0x3)
+#define R_HSDDELAY VIRT_ADDR(0x1, 0x4)
+#define R_BUTCTL VIRT_ADDR(0x1, 0x5)
+#define R_CH0AIC VIRT_ADDR(0x1, 0x6)
+#define R_CH1AIC VIRT_ADDR(0x1, 0x7)
+#define R_CH2AIC VIRT_ADDR(0x1, 0x8)
+#define R_CH3AIC VIRT_ADDR(0x1, 0x9)
+#define R_ICTL0 VIRT_ADDR(0x1, 0x0A)
+#define R_ICTL1 VIRT_ADDR(0x1, 0x0B)
+#define R_MICBIAS VIRT_ADDR(0x1, 0x0C)
+#define R_PGACTL0 VIRT_ADDR(0x1, 0x0D)
+#define R_PGACTL1 VIRT_ADDR(0x1, 0x0E)
+#define R_PGACTL2 VIRT_ADDR(0x1, 0x0F)
+#define R_PGACTL3 VIRT_ADDR(0x1, 0x10)
+#define R_PGAZ VIRT_ADDR(0x1, 0x11)
+#define R_ICH0VOL VIRT_ADDR(0x1, 0x12)
+#define R_ICH1VOL VIRT_ADDR(0x1, 0x13)
+#define R_ICH2VOL VIRT_ADDR(0x1, 0x14)
+#define R_ICH3VOL VIRT_ADDR(0x1, 0x15)
+#define R_ASRCILVOL VIRT_ADDR(0x1, 0x16)
+#define R_ASRCIRVOL VIRT_ADDR(0x1, 0x17)
+#define R_ASRCOLVOL VIRT_ADDR(0x1, 0x18)
+#define R_ASRCORVOL VIRT_ADDR(0x1, 0x19)
+#define R_IVOLCTLU VIRT_ADDR(0x1, 0x1C)
+#define R_ALCCTL0 VIRT_ADDR(0x1, 0x1D)
+#define R_ALCCTL1 VIRT_ADDR(0x1, 0x1E)
+#define R_ALCCTL2 VIRT_ADDR(0x1, 0x1F)
+#define R_ALCCTL3 VIRT_ADDR(0x1, 0x20)
+#define R_NGATE VIRT_ADDR(0x1, 0x21)
+#define R_DMICCTL VIRT_ADDR(0x1, 0x22)
+#define R_DACCTL VIRT_ADDR(0x2, 0x1)
+#define R_SPKCTL VIRT_ADDR(0x2, 0x2)
+#define R_SUBCTL VIRT_ADDR(0x2, 0x3)
+#define R_DCCTL VIRT_ADDR(0x2, 0x4)
+#define R_OVOLCTLU VIRT_ADDR(0x2, 0x6)
+#define R_MUTEC VIRT_ADDR(0x2, 0x7)
+#define R_MVOLL VIRT_ADDR(0x2, 0x8)
+#define R_MVOLR VIRT_ADDR(0x2, 0x9)
+#define R_HPVOLL VIRT_ADDR(0x2, 0x0A)
+#define R_HPVOLR VIRT_ADDR(0x2, 0x0B)
+#define R_SPKVOLL VIRT_ADDR(0x2, 0x0C)
+#define R_SPKVOLR VIRT_ADDR(0x2, 0x0D)
+#define R_SUBVOL VIRT_ADDR(0x2, 0x10)
+#define R_COP0 VIRT_ADDR(0x2, 0x11)
+#define R_COP1 VIRT_ADDR(0x2, 0x12)
+#define R_COPSTAT VIRT_ADDR(0x2, 0x13)
+#define R_PWM0 VIRT_ADDR(0x2, 0x14)
+#define R_PWM1 VIRT_ADDR(0x2, 0x15)
+#define R_PWM2 VIRT_ADDR(0x2, 0x16)
+#define R_PWM3 VIRT_ADDR(0x2, 0x17)
+#define R_HPSW VIRT_ADDR(0x2, 0x18)
+#define R_THERMTS VIRT_ADDR(0x2, 0x19)
+#define R_THERMSPK1 VIRT_ADDR(0x2, 0x1A)
+#define R_THERMSTAT VIRT_ADDR(0x2, 0x1B)
+#define R_SCSTAT VIRT_ADDR(0x2, 0x1C)
+#define R_SDMON VIRT_ADDR(0x2, 0x1D)
+#define R_SPKEQFILT VIRT_ADDR(0x3, 0x1)
+#define R_SPKCRWDL VIRT_ADDR(0x3, 0x2)
+#define R_SPKCRWDM VIRT_ADDR(0x3, 0x3)
+#define R_SPKCRWDH VIRT_ADDR(0x3, 0x4)
+#define R_SPKCRRDL VIRT_ADDR(0x3, 0x5)
+#define R_SPKCRRDM VIRT_ADDR(0x3, 0x6)
+#define R_SPKCRRDH VIRT_ADDR(0x3, 0x7)
+#define R_SPKCRADD VIRT_ADDR(0x3, 0x8)
+#define R_SPKCRS VIRT_ADDR(0x3, 0x9)
+#define R_SPKMBCEN VIRT_ADDR(0x3, 0x0A)
+#define R_SPKMBCCTL VIRT_ADDR(0x3, 0x0B)
+#define R_SPKMBCMUG1 VIRT_ADDR(0x3, 0x0C)
+#define R_SPKMBCTHR1 VIRT_ADDR(0x3, 0x0D)
+#define R_SPKMBCRAT1 VIRT_ADDR(0x3, 0x0E)
+#define R_SPKMBCATK1L VIRT_ADDR(0x3, 0x0F)
+#define R_SPKMBCATK1H VIRT_ADDR(0x3, 0x10)
+#define R_SPKMBCREL1L VIRT_ADDR(0x3, 0x11)
+#define R_SPKMBCREL1H VIRT_ADDR(0x3, 0x12)
+#define R_SPKMBCMUG2 VIRT_ADDR(0x3, 0x13)
+#define R_SPKMBCTHR2 VIRT_ADDR(0x3, 0x14)
+#define R_SPKMBCRAT2 VIRT_ADDR(0x3, 0x15)
+#define R_SPKMBCATK2L VIRT_ADDR(0x3, 0x16)
+#define R_SPKMBCATK2H VIRT_ADDR(0x3, 0x17)
+#define R_SPKMBCREL2L VIRT_ADDR(0x3, 0x18)
+#define R_SPKMBCREL2H VIRT_ADDR(0x3, 0x19)
+#define R_SPKMBCMUG3 VIRT_ADDR(0x3, 0x1A)
+#define R_SPKMBCTHR3 VIRT_ADDR(0x3, 0x1B)
+#define R_SPKMBCRAT3 VIRT_ADDR(0x3, 0x1C)
+#define R_SPKMBCATK3L VIRT_ADDR(0x3, 0x1D)
+#define R_SPKMBCATK3H VIRT_ADDR(0x3, 0x1E)
+#define R_SPKMBCREL3L VIRT_ADDR(0x3, 0x1F)
+#define R_SPKMBCREL3H VIRT_ADDR(0x3, 0x20)
+#define R_SPKCLECTL VIRT_ADDR(0x3, 0x21)
+#define R_SPKCLEMUG VIRT_ADDR(0x3, 0x22)
+#define R_SPKCOMPTHR VIRT_ADDR(0x3, 0x23)
+#define R_SPKCOMPRAT VIRT_ADDR(0x3, 0x24)
+#define R_SPKCOMPATKL VIRT_ADDR(0x3, 0x25)
+#define R_SPKCOMPATKH VIRT_ADDR(0x3, 0x26)
+#define R_SPKCOMPRELL VIRT_ADDR(0x3, 0x27)
+#define R_SPKCOMPRELH VIRT_ADDR(0x3, 0x28)
+#define R_SPKLIMTHR VIRT_ADDR(0x3, 0x29)
+#define R_SPKLIMTGT VIRT_ADDR(0x3, 0x2A)
+#define R_SPKLIMATKL VIRT_ADDR(0x3, 0x2B)
+#define R_SPKLIMATKH VIRT_ADDR(0x3, 0x2C)
+#define R_SPKLIMRELL VIRT_ADDR(0x3, 0x2D)
+#define R_SPKLIMRELH VIRT_ADDR(0x3, 0x2E)
+#define R_SPKEXPTHR VIRT_ADDR(0x3, 0x2F)
+#define R_SPKEXPRAT VIRT_ADDR(0x3, 0x30)
+#define R_SPKEXPATKL VIRT_ADDR(0x3, 0x31)
+#define R_SPKEXPATKH VIRT_ADDR(0x3, 0x32)
+#define R_SPKEXPRELL VIRT_ADDR(0x3, 0x33)
+#define R_SPKEXPRELH VIRT_ADDR(0x3, 0x34)
+#define R_SPKFXCTL VIRT_ADDR(0x3, 0x35)
+#define R_DACEQFILT VIRT_ADDR(0x4, 0x1)
+#define R_DACCRWDL VIRT_ADDR(0x4, 0x2)
+#define R_DACCRWDM VIRT_ADDR(0x4, 0x3)
+#define R_DACCRWDH VIRT_ADDR(0x4, 0x4)
+#define R_DACCRRDL VIRT_ADDR(0x4, 0x5)
+#define R_DACCRRDM VIRT_ADDR(0x4, 0x6)
+#define R_DACCRRDH VIRT_ADDR(0x4, 0x7)
+#define R_DACCRADD VIRT_ADDR(0x4, 0x8)
+#define R_DACCRS VIRT_ADDR(0x4, 0x9)
+#define R_DACMBCEN VIRT_ADDR(0x4, 0x0A)
+#define R_DACMBCCTL VIRT_ADDR(0x4, 0x0B)
+#define R_DACMBCMUG1 VIRT_ADDR(0x4, 0x0C)
+#define R_DACMBCTHR1 VIRT_ADDR(0x4, 0x0D)
+#define R_DACMBCRAT1 VIRT_ADDR(0x4, 0x0E)
+#define R_DACMBCATK1L VIRT_ADDR(0x4, 0x0F)
+#define R_DACMBCATK1H VIRT_ADDR(0x4, 0x10)
+#define R_DACMBCREL1L VIRT_ADDR(0x4, 0x11)
+#define R_DACMBCREL1H VIRT_ADDR(0x4, 0x12)
+#define R_DACMBCMUG2 VIRT_ADDR(0x4, 0x13)
+#define R_DACMBCTHR2 VIRT_ADDR(0x4, 0x14)
+#define R_DACMBCRAT2 VIRT_ADDR(0x4, 0x15)
+#define R_DACMBCATK2L VIRT_ADDR(0x4, 0x16)
+#define R_DACMBCATK2H VIRT_ADDR(0x4, 0x17)
+#define R_DACMBCREL2L VIRT_ADDR(0x4, 0x18)
+#define R_DACMBCREL2H VIRT_ADDR(0x4, 0x19)
+#define R_DACMBCMUG3 VIRT_ADDR(0x4, 0x1A)
+#define R_DACMBCTHR3 VIRT_ADDR(0x4, 0x1B)
+#define R_DACMBCRAT3 VIRT_ADDR(0x4, 0x1C)
+#define R_DACMBCATK3L VIRT_ADDR(0x4, 0x1D)
+#define R_DACMBCATK3H VIRT_ADDR(0x4, 0x1E)
+#define R_DACMBCREL3L VIRT_ADDR(0x4, 0x1F)
+#define R_DACMBCREL3H VIRT_ADDR(0x4, 0x20)
+#define R_DACCLECTL VIRT_ADDR(0x4, 0x21)
+#define R_DACCLEMUG VIRT_ADDR(0x4, 0x22)
+#define R_DACCOMPTHR VIRT_ADDR(0x4, 0x23)
+#define R_DACCOMPRAT VIRT_ADDR(0x4, 0x24)
+#define R_DACCOMPATKL VIRT_ADDR(0x4, 0x25)
+#define R_DACCOMPATKH VIRT_ADDR(0x4, 0x26)
+#define R_DACCOMPRELL VIRT_ADDR(0x4, 0x27)
+#define R_DACCOMPRELH VIRT_ADDR(0x4, 0x28)
+#define R_DACLIMTHR VIRT_ADDR(0x4, 0x29)
+#define R_DACLIMTGT VIRT_ADDR(0x4, 0x2A)
+#define R_DACLIMATKL VIRT_ADDR(0x4, 0x2B)
+#define R_DACLIMATKH VIRT_ADDR(0x4, 0x2C)
+#define R_DACLIMRELL VIRT_ADDR(0x4, 0x2D)
+#define R_DACLIMRELH VIRT_ADDR(0x4, 0x2E)
+#define R_DACEXPTHR VIRT_ADDR(0x4, 0x2F)
+#define R_DACEXPRAT VIRT_ADDR(0x4, 0x30)
+#define R_DACEXPATKL VIRT_ADDR(0x4, 0x31)
+#define R_DACEXPATKH VIRT_ADDR(0x4, 0x32)
+#define R_DACEXPRELL VIRT_ADDR(0x4, 0x33)
+#define R_DACEXPRELH VIRT_ADDR(0x4, 0x34)
+#define R_DACFXCTL VIRT_ADDR(0x4, 0x35)
+#define R_SUBEQFILT VIRT_ADDR(0x5, 0x1)
+#define R_SUBCRWDL VIRT_ADDR(0x5, 0x2)
+#define R_SUBCRWDM VIRT_ADDR(0x5, 0x3)
+#define R_SUBCRWDH VIRT_ADDR(0x5, 0x4)
+#define R_SUBCRRDL VIRT_ADDR(0x5, 0x5)
+#define R_SUBCRRDM VIRT_ADDR(0x5, 0x6)
+#define R_SUBCRRDH VIRT_ADDR(0x5, 0x7)
+#define R_SUBCRADD VIRT_ADDR(0x5, 0x8)
+#define R_SUBCRS VIRT_ADDR(0x5, 0x9)
+#define R_SUBMBCEN VIRT_ADDR(0x5, 0x0A)
+#define R_SUBMBCCTL VIRT_ADDR(0x5, 0x0B)
+#define R_SUBMBCMUG1 VIRT_ADDR(0x5, 0x0C)
+#define R_SUBMBCTHR1 VIRT_ADDR(0x5, 0x0D)
+#define R_SUBMBCRAT1 VIRT_ADDR(0x5, 0x0E)
+#define R_SUBMBCATK1L VIRT_ADDR(0x5, 0x0F)
+#define R_SUBMBCATK1H VIRT_ADDR(0x5, 0x10)
+#define R_SUBMBCREL1L VIRT_ADDR(0x5, 0x11)
+#define R_SUBMBCREL1H VIRT_ADDR(0x5, 0x12)
+#define R_SUBMBCMUG2 VIRT_ADDR(0x5, 0x13)
+#define R_SUBMBCTHR2 VIRT_ADDR(0x5, 0x14)
+#define R_SUBMBCRAT2 VIRT_ADDR(0x5, 0x15)
+#define R_SUBMBCATK2L VIRT_ADDR(0x5, 0x16)
+#define R_SUBMBCATK2H VIRT_ADDR(0x5, 0x17)
+#define R_SUBMBCREL2L VIRT_ADDR(0x5, 0x18)
+#define R_SUBMBCREL2H VIRT_ADDR(0x5, 0x19)
+#define R_SUBMBCMUG3 VIRT_ADDR(0x5, 0x1A)
+#define R_SUBMBCTHR3 VIRT_ADDR(0x5, 0x1B)
+#define R_SUBMBCRAT3 VIRT_ADDR(0x5, 0x1C)
+#define R_SUBMBCATK3L VIRT_ADDR(0x5, 0x1D)
+#define R_SUBMBCATK3H VIRT_ADDR(0x5, 0x1E)
+#define R_SUBMBCREL3L VIRT_ADDR(0x5, 0x1F)
+#define R_SUBMBCREL3H VIRT_ADDR(0x5, 0x20)
+#define R_SUBCLECTL VIRT_ADDR(0x5, 0x21)
+#define R_SUBCLEMUG VIRT_ADDR(0x5, 0x22)
+#define R_SUBCOMPTHR VIRT_ADDR(0x5, 0x23)
+#define R_SUBCOMPRAT VIRT_ADDR(0x5, 0x24)
+#define R_SUBCOMPATKL VIRT_ADDR(0x5, 0x25)
+#define R_SUBCOMPATKH VIRT_ADDR(0x5, 0x26)
+#define R_SUBCOMPRELL VIRT_ADDR(0x5, 0x27)
+#define R_SUBCOMPRELH VIRT_ADDR(0x5, 0x28)
+#define R_SUBLIMTHR VIRT_ADDR(0x5, 0x29)
+#define R_SUBLIMTGT VIRT_ADDR(0x5, 0x2A)
+#define R_SUBLIMATKL VIRT_ADDR(0x5, 0x2B)
+#define R_SUBLIMATKH VIRT_ADDR(0x5, 0x2C)
+#define R_SUBLIMRELL VIRT_ADDR(0x5, 0x2D)
+#define R_SUBLIMRELH VIRT_ADDR(0x5, 0x2E)
+#define R_SUBEXPTHR VIRT_ADDR(0x5, 0x2F)
+#define R_SUBEXPRAT VIRT_ADDR(0x5, 0x30)
+#define R_SUBEXPATKL VIRT_ADDR(0x5, 0x31)
+#define R_SUBEXPATKH VIRT_ADDR(0x5, 0x32)
+#define R_SUBEXPRELL VIRT_ADDR(0x5, 0x33)
+#define R_SUBEXPRELH VIRT_ADDR(0x5, 0x34)
+#define R_SUBFXCTL VIRT_ADDR(0x5, 0x35)
+
+// *** PLLCTL ***
+#define FB_PLLCTL_VCCI_PLL 6
+#define FM_PLLCTL_VCCI_PLL 0xC0
+
+#define FB_PLLCTL_RZ_PLL 3
+#define FM_PLLCTL_RZ_PLL 0x38
+
+#define FB_PLLCTL_CP_PLL 0
+#define FM_PLLCTL_CP_PLL 0x7
+
+// *** PLLRDIV ***
+#define FB_PLLRDIV_REFDIV_PLL 0
+#define FM_PLLRDIV_REFDIV_PLL 0xFF
+
+// *** PLLODIV ***
+#define FB_PLLODIV_OUTDIV_PLL 0
+#define FM_PLLODIV_OUTDIV_PLL 0xFF
+
+// *** PLLFDIVL ***
+#define FB_PLLFDIVL_FBDIVL_PLL 0
+#define FM_PLLFDIVL_FBDIVL_PLL 0xFF
+
+// *** PLLFDIVH ***
+#define FB_PLLFDIVH_FBDIVH_PLL 0
+#define FM_PLLFDIVH_FBDIVH_PLL 0xF
+
+// *** I2SPCTL ***
+#define FB_I2SPCTL_BCLKSTAT 7
+#define FM_I2SPCTL_BCLKSTAT 0x80
+#define FV_BCLKSTAT_LOST 0x80
+#define FV_BCLKSTAT_NOT_LOST 0x0
+
+#define FB_I2SPCTL_BCLKP 6
+#define FM_I2SPCTL_BCLKP 0x40
+#define FV_BCLKP_NOT_INVERTED 0x0
+#define FV_BCLKP_INVERTED 0x40
+
+#define FB_I2SPCTL_PORTMS 5
+#define FM_I2SPCTL_PORTMS 0x20
+#define FV_PORTMS_SLAVE 0x0
+#define FV_PORTMS_MASTER 0x20
+
+#define FB_I2SPCTL_LRCLKP 4
+#define FM_I2SPCTL_LRCLKP 0x10
+#define FV_LRCLKP_NOT_INVERTED 0x0
+#define FV_LRCLKP_INVERTED 0x10
+
+#define FB_I2SPCTL_WL 2
+#define FM_I2SPCTL_WL 0xC
+#define FV_WL_16 0x0
+#define FV_WL_20 0x4
+#define FV_WL_24 0x8
+#define FV_WL_32 0xC
+
+#define FB_I2SPCTL_FORMAT 0
+#define FM_I2SPCTL_FORMAT 0x3
+#define FV_FORMAT_RIGHT 0x0
+#define FV_FORMAT_LEFT 0x1
+#define FV_FORMAT_I2S 0x2
+#define FV_FORMAT_TDM 0x3
+
+// *** I2SMRATE ***
+#define FB_I2SMRATE_I2SMCLKHALF 7
+#define FM_I2SMRATE_I2SMCLKHALF 0x80
+#define FV_I2SMCLKHALF_I2S1MCLKDIV_DIV_2 0x0
+#define FV_I2SMCLKHALF_I2S1MCLKDIV_ONLY 0x80
+
+#define FB_I2SMRATE_I2SMCLKDIV 5
+#define FM_I2SMRATE_I2SMCLKDIV 0x60
+#define FV_I2SMCLKDIV_125 0x0
+#define FV_I2SMCLKDIV_128 0x20
+#define FV_I2SMCLKDIV_136 0x40
+#define FV_I2SMCLKDIV_192 0x60
+
+#define FB_I2SMRATE_I2SMBR 3
+#define FM_I2SMRATE_I2SMBR 0x18
+#define FV_I2SMBR_32 0x0
+#define FV_I2SMBR_44PT1 0x8
+#define FV_I2SMBR_48 0x10
+#define FV_I2SMBR_MCLK_MODE 0x18
+
+#define FB_I2SMRATE_I2SMBM 0
+#define FM_I2SMRATE_I2SMBM 0x3
+#define FV_I2SMBM_0PT25 0x0
+#define FV_I2SMBM_0PT5 0x1
+#define FV_I2SMBM_1 0x2
+#define FV_I2SMBM_2 0x3
+
+// *** PCMPCTL0 ***
+#define FB_PCMPCTL0_PCMFLENP 2
+#define FM_PCMPCTL0_PCMFLENP 0x4
+#define FV_PCMFLENP_128 0x0
+#define FV_PCMFLENP_256 0x4
+
+#define FB_PCMPCTL0_SLSYNCP 1
+#define FM_PCMPCTL0_SLSYNCP 0x2
+#define FV_SLSYNCP_SHORT 0x0
+#define FV_SLSYNCP_LONG 0x2
+
+#define FB_PCMPCTL0_BDELAYP 0
+#define FM_PCMPCTL0_BDELAYP 0x1
+#define FV_BDELAYP_NO_DELAY 0x0
+#define FV_BDELAYP_1BCLK_DELAY 0x1
+
+// *** PCMPCTL1 ***
+#define FB_PCMPCTL1_PCMMOMP 6
+#define FM_PCMPCTL1_PCMMOMP 0x40
+
+#define FB_PCMPCTL1_PCMSOP 5
+#define FM_PCMPCTL1_PCMSOP 0x20
+#define FV_PCMSOP_1 0x0
+#define FV_PCMSOP_2 0x20
+
+#define FB_PCMPCTL1_PCMDSSP 3
+#define FM_PCMPCTL1_PCMDSSP 0x18
+#define FV_PCMDSSP_16 0x0
+#define FV_PCMDSSP_24 0x8
+#define FV_PCMDSSP_32 0x10
+
+#define FB_PCMPCTL1_PCMMIMP 1
+#define FM_PCMPCTL1_PCMMIMP 0x2
+
+#define FB_PCMPCTL1_PCMSIP 0
+#define FM_PCMPCTL1_PCMSIP 0x1
+#define FV_PCMSIP_1 0x0
+#define FV_PCMSIP_2 0x1
+
+// *** CHAIC ***
+#define FB_CHAIC_MICBST 4
+#define FM_CHAIC_MICBST 0x30
+
+// *** PGACTL ***
+#define FB_PGACTL_PGAMUTE 7
+#define FM_PGACTL_PGAMUTE 0x80
+
+#define FB_PGACTL_PGAVOL 0
+#define FM_PGACTL_PGAVOL 0x3F
+
+// *** ICHVOL ***
+#define FB_ICHVOL_ICHVOL 0
+#define FM_ICHVOL_ICHVOL 0xFF
+
+// *** SPKMBCMUG ***
+#define FB_SPKMBCMUG_PHASE 5
+#define FM_SPKMBCMUG_PHASE 0x20
+
+#define FB_SPKMBCMUG_MUGAIN 0
+#define FM_SPKMBCMUG_MUGAIN 0x1F
+
+// *** SPKMBCTHR ***
+#define FB_SPKMBCTHR_THRESH 0
+#define FM_SPKMBCTHR_THRESH 0xFF
+
+// *** SPKMBCRAT ***
+#define FB_SPKMBCRAT_RATIO 0
+#define FM_SPKMBCRAT_RATIO 0x1F
+
+// *** SPKMBCATKL ***
+#define FB_SPKMBCATKL_TCATKL 0
+#define FM_SPKMBCATKL_TCATKL 0xFF
+
+// *** SPKMBCATKH ***
+#define FB_SPKMBCATKH_TCATKH 0
+#define FM_SPKMBCATKH_TCATKH 0xFF
+
+// *** SPKMBCRELL ***
+#define FB_SPKMBCRELL_TCRELL 0
+#define FM_SPKMBCRELL_TCRELL 0xFF
+
+// *** SPKMBCRELH ***
+#define FB_SPKMBCRELH_TCRELH 0
+#define FM_SPKMBCRELH_TCRELH 0xFF
+
+// *** DACMBCMUG ***
+#define FB_DACMBCMUG_PHASE 5
+#define FM_DACMBCMUG_PHASE 0x20
+
+#define FB_DACMBCMUG_MUGAIN 0
+#define FM_DACMBCMUG_MUGAIN 0x1F
+
+// *** DACMBCTHR ***
+#define FB_DACMBCTHR_THRESH 0
+#define FM_DACMBCTHR_THRESH 0xFF
+
+// *** DACMBCRAT ***
+#define FB_DACMBCRAT_RATIO 0
+#define FM_DACMBCRAT_RATIO 0x1F
+
+// *** DACMBCATKL ***
+#define FB_DACMBCATKL_TCATKL 0
+#define FM_DACMBCATKL_TCATKL 0xFF
+
+// *** DACMBCATKH ***
+#define FB_DACMBCATKH_TCATKH 0
+#define FM_DACMBCATKH_TCATKH 0xFF
+
+// *** DACMBCRELL ***
+#define FB_DACMBCRELL_TCRELL 0
+#define FM_DACMBCRELL_TCRELL 0xFF
+
+// *** DACMBCRELH ***
+#define FB_DACMBCRELH_TCRELH 0
+#define FM_DACMBCRELH_TCRELH 0xFF
+
+// *** SUBMBCMUG ***
+#define FB_SUBMBCMUG_PHASE 5
+#define FM_SUBMBCMUG_PHASE 0x20
+
+#define FB_SUBMBCMUG_MUGAIN 0
+#define FM_SUBMBCMUG_MUGAIN 0x1F
+
+// *** SUBMBCTHR ***
+#define FB_SUBMBCTHR_THRESH 0
+#define FM_SUBMBCTHR_THRESH 0xFF
+
+// *** SUBMBCRAT ***
+#define FB_SUBMBCRAT_RATIO 0
+#define FM_SUBMBCRAT_RATIO 0x1F
+
+// *** SUBMBCATKL ***
+#define FB_SUBMBCATKL_TCATKL 0
+#define FM_SUBMBCATKL_TCATKL 0xFF
+
+// *** SUBMBCATKH ***
+#define FB_SUBMBCATKH_TCATKH 0
+#define FM_SUBMBCATKH_TCATKH 0xFF
+
+// *** SUBMBCRELL ***
+#define FB_SUBMBCRELL_TCRELL 0
+#define FM_SUBMBCRELL_TCRELL 0xFF
+
+// *** SUBMBCRELH ***
+#define FB_SUBMBCRELH_TCRELH 0
+#define FM_SUBMBCRELH_TCRELH 0xFF
+
+// *** PAGESEL ***
+#define FB_PAGESEL_PAGESEL 0
+#define FM_PAGESEL_PAGESEL 0xFF
+
+// *** RESET ***
+#define FB_RESET_RESET 0
+#define FM_RESET_RESET 0xFF
+#define FV_RESET_PWR_ON_DEFAULTS 0x85
+
+// *** IRQEN ***
+#define FB_IRQEN_THRMINTEN 6
+#define FM_IRQEN_THRMINTEN 0x40
+#define FV_THRMINTEN_ENABLED 0x40
+#define FV_THRMINTEN_DISABLED 0x0
+
+#define FB_IRQEN_HBPINTEN 5
+#define FM_IRQEN_HBPINTEN 0x20
+#define FV_HBPINTEN_ENABLED 0x20
+#define FV_HBPINTEN_DISABLED 0x0
+
+#define FB_IRQEN_HSDINTEN 4
+#define FM_IRQEN_HSDINTEN 0x10
+#define FV_HSDINTEN_ENABLED 0x10
+#define FV_HSDINTEN_DISABLED 0x0
+
+#define FB_IRQEN_HPDINTEN 3
+#define FM_IRQEN_HPDINTEN 0x8
+#define FV_HPDINTEN_ENABLED 0x8
+#define FV_HPDINTEN_DISABLED 0x0
+
+#define FB_IRQEN_GPIO3INTEN 1
+#define FM_IRQEN_GPIO3INTEN 0x2
+#define FV_GPIO3INTEN_ENABLED 0x2
+#define FV_GPIO3INTEN_DISABLED 0x0
+
+#define FB_IRQEN_GPIO2INTEN 0
+#define FM_IRQEN_GPIO2INTEN 0x1
+#define FV_GPIO2INTEN_ENABLED 0x1
+#define FV_GPIO2INTEN_DISABLED 0x0
+
+#define IRQEN_GPIOINTEN_ENABLED 0x1
+#define IRQEN_GPIOINTEN_DISABLED 0x0
+
+// *** IRQMASK ***
+#define FB_IRQMASK_THRMIM 6
+#define FM_IRQMASK_THRMIM 0x40
+#define FV_THRMIM_MASKED 0x0
+#define FV_THRMIM_NOT_MASKED 0x40
+
+#define FB_IRQMASK_HBPIM 5
+#define FM_IRQMASK_HBPIM 0x20
+#define FV_HBPIM_MASKED 0x0
+#define FV_HBPIM_NOT_MASKED 0x20
+
+#define FB_IRQMASK_HSDIM 4
+#define FM_IRQMASK_HSDIM 0x10
+#define FV_HSDIM_MASKED 0x0
+#define FV_HSDIM_NOT_MASKED 0x10
+
+#define FB_IRQMASK_HPDIM 3
+#define FM_IRQMASK_HPDIM 0x8
+#define FV_HPDIM_MASKED 0x0
+#define FV_HPDIM_NOT_MASKED 0x8
+
+#define FB_IRQMASK_GPIO3M 1
+#define FM_IRQMASK_GPIO3M 0x2
+#define FV_GPIO3M_MASKED 0x0
+#define FV_GPIO3M_NOT_MASKED 0x2
+
+#define FB_IRQMASK_GPIO2M 0
+#define FM_IRQMASK_GPIO2M 0x1
+#define FV_GPIO2M_MASKED 0x0
+#define FV_GPIO2M_NOT_MASKED 0x1
+
+#define IRQMASK_GPIOM_MASKED 0x0
+#define IRQMASK_GPIOM_NOT_MASKED 0x1
+
+// *** IRQSTAT ***
+#define FB_IRQSTAT_THRMINT 6
+#define FM_IRQSTAT_THRMINT 0x40
+#define FV_THRMINT_INTERRUPTED 0x40
+#define FV_THRMINT_NOT_INTERRUPTED 0x0
+
+#define FB_IRQSTAT_HBPINT 5
+#define FM_IRQSTAT_HBPINT 0x20
+#define FV_HBPINT_INTERRUPTED 0x20
+#define FV_HBPINT_NOT_INTERRUPTED 0x0
+
+#define FB_IRQSTAT_HSDINT 4
+#define FM_IRQSTAT_HSDINT 0x10
+#define FV_HSDINT_INTERRUPTED 0x10
+#define FV_HSDINT_NOT_INTERRUPTED 0x0
+
+#define FB_IRQSTAT_HPDINT 3
+#define FM_IRQSTAT_HPDINT 0x8
+#define FV_HPDINT_INTERRUPTED 0x8
+#define FV_HPDINT_NOT_INTERRUPTED 0x0
+
+#define FB_IRQSTAT_GPIO3INT 1
+#define FM_IRQSTAT_GPIO3INT 0x2
+#define FV_GPIO3INT_INTERRUPTED 0x2
+#define FV_GPIO3INT_NOT_INTERRUPTED 0x0
+
+#define FB_IRQSTAT_GPIO2INT 0
+#define FM_IRQSTAT_GPIO2INT 0x1
+#define FV_GPIO2INT_INTERRUPTED 0x1
+#define FV_GPIO2INT_NOT_INTERRUPTED 0x0
+
+#define IRQSTAT_GPIOINT_INTERRUPTED 0x1
+#define IRQSTAT_GPIOINT_NOT_INTERRUPTED 0x0
+
+// *** DEVADD0 ***
+#define FB_DEVADD0_DEVADD0 1
+#define FM_DEVADD0_DEVADD0 0xFE
+
+#define FB_DEVADD0_I2C_ADDRLK 0
+#define FM_DEVADD0_I2C_ADDRLK 0x1
+#define FV_I2C_ADDRLK_LOCK 0x1
+
+// *** DEVID ***
+#define FB_DEVID_DEV_ID 0
+#define FM_DEVID_DEV_ID 0xFF
+
+// *** DEVREV ***
+#define FB_DEVREV_MAJ_REV 4
+#define FM_DEVREV_MAJ_REV 0xF0
+
+#define FB_DEVREV_MIN_REV 0
+#define FM_DEVREV_MIN_REV 0xF
+
+// *** PLLSTAT ***
+#define FB_PLLSTAT_PLL2LK 1
+#define FM_PLLSTAT_PLL2LK 0x2
+#define FV_PLL2LK_LOCKED 0x2
+#define FV_PLL2LK_UNLOCKED 0x0
+
+#define FB_PLLSTAT_PLL1LK 0
+#define FM_PLLSTAT_PLL1LK 0x1
+#define FV_PLL1LK_LOCKED 0x1
+#define FV_PLL1LK_UNLOCKED 0x0
+
+#define PLLSTAT_PLLLK_LOCKED 0x1
+#define PLLSTAT_PLLLK_UNLOCKED 0x0
+
+// *** PLLCTL ***
+#define FB_PLLCTL_PU_PLL2 7
+#define FM_PLLCTL_PU_PLL2 0x80
+#define FV_PU_PLL2_PWR_UP 0x80
+#define FV_PU_PLL2_PWR_DWN 0x0
+
+#define FB_PLLCTL_PU_PLL1 6
+#define FM_PLLCTL_PU_PLL1 0x40
+#define FV_PU_PLL1_PWR_UP 0x40
+#define FV_PU_PLL1_PWR_DWN 0x0
+
+#define FB_PLLCTL_PLL2CLKEN 5
+#define FM_PLLCTL_PLL2CLKEN 0x20
+#define FV_PLL2CLKEN_ENABLE 0x20
+#define FV_PLL2CLKEN_DISABLE 0x0
+
+#define FB_PLLCTL_PLL1CLKEN 4
+#define FM_PLLCTL_PLL1CLKEN 0x10
+#define FV_PLL1CLKEN_ENABLE 0x10
+#define FV_PLL1CLKEN_DISABLE 0x0
+
+#define FB_PLLCTL_BCLKSEL 2
+#define FM_PLLCTL_BCLKSEL 0xC
+#define FV_BCLKSEL_BCLK1 0x0
+#define FV_BCLKSEL_BCLK2 0x4
+#define FV_BCLKSEL_BCLK3 0x8
+
+#define FB_PLLCTL_PLLISEL 0
+#define FM_PLLCTL_PLLISEL 0x3
+#define FV_PLLISEL_XTAL 0x0
+#define FV_PLLISEL_MCLK1 0x1
+#define FV_PLLISEL_MCLK2 0x2
+#define FV_PLLISEL_BCLK 0x3
+
+#define PLLCTL_PU_PLL_PWR_UP 0x1
+#define PLLCTL_PU_PLL_PWR_DWN 0x0
+#define PLLCTL_PLLCLKEN_ENABLE 0x1
+#define PLLCTL_PLLCLKEN_DISABLE 0x0
+
+// *** ISRC ***
+#define FB_ISRC_IBR 2
+#define FM_ISRC_IBR 0x4
+#define FV_IBR_44PT1 0x0
+#define FV_IBR_48 0x4
+
+#define FB_ISRC_IBM 0
+#define FM_ISRC_IBM 0x3
+#define FV_IBM_0PT25 0x0
+#define FV_IBM_0PT5 0x1
+#define FV_IBM_1 0x2
+#define FV_IBM_2 0x3
+
+// *** SCLKCTL ***
+#define FB_SCLKCTL_ASDM 6
+#define FM_SCLKCTL_ASDM 0xC0
+#define FV_ASDM_HALF 0x40
+#define FV_ASDM_FULL 0x80
+#define FV_ASDM_AUTO 0xC0
+
+#define FB_SCLKCTL_DSDM 4
+#define FM_SCLKCTL_DSDM 0x30
+#define FV_DSDM_HALF 0x10
+#define FV_DSDM_FULL 0x20
+#define FV_DSDM_AUTO 0x30
+
+// *** TIMEBASE ***
+#define FB_TIMEBASE_TIMEBASE 0
+#define FM_TIMEBASE_TIMEBASE 0xFF
+
+// *** I2SCMC ***
+#define FB_I2SCMC_BCMP3 4
+#define FM_I2SCMC_BCMP3 0x30
+#define FV_BCMP3_AUTO 0x0
+#define FV_BCMP3_32X 0x10
+#define FV_BCMP3_40X 0x20
+#define FV_BCMP3_64X 0x30
+
+#define FB_I2SCMC_BCMP2 2
+#define FM_I2SCMC_BCMP2 0xC
+#define FV_BCMP2_AUTO 0x0
+#define FV_BCMP2_32X 0x4
+#define FV_BCMP2_40X 0x8
+#define FV_BCMP2_64X 0xC
+
+#define FB_I2SCMC_BCMP1 0
+#define FM_I2SCMC_BCMP1 0x3
+#define FV_BCMP1_AUTO 0x0
+#define FV_BCMP1_32X 0x1
+#define FV_BCMP1_40X 0x2
+#define FV_BCMP1_64X 0x3
+
+#define I2SCMC_BCMP_AUTO 0x0
+#define I2SCMC_BCMP_32X 0x1
+#define I2SCMC_BCMP_40X 0x2
+#define I2SCMC_BCMP_64X 0x3
+
+// *** MCLK2PINC ***
+#define FB_MCLK2PINC_SLEWOUT 4
+#define FM_MCLK2PINC_SLEWOUT 0xF0
+
+#define FB_MCLK2PINC_MCLK2IO 2
+#define FM_MCLK2PINC_MCLK2IO 0x4
+#define FV_MCLK2IO_INPUT 0x0
+#define FV_MCLK2IO_OUTPUT 0x4
+
+#define FB_MCLK2PINC_MCLK2OS 0
+#define FM_MCLK2PINC_MCLK2OS 0x3
+#define FV_MCLK2OS_24PT576 0x0
+#define FV_MCLK2OS_22PT5792 0x1
+#define FV_MCLK2OS_PLL2 0x2
+
+// *** I2SPINC0 ***
+#define FB_I2SPINC0_SDO3TRI 7
+#define FM_I2SPINC0_SDO3TRI 0x80
+
+#define FB_I2SPINC0_SDO2TRI 6
+#define FM_I2SPINC0_SDO2TRI 0x40
+
+#define FB_I2SPINC0_SDO1TRI 5
+#define FM_I2SPINC0_SDO1TRI 0x20
+
+#define FB_I2SPINC0_PCM3TRI 2
+#define FM_I2SPINC0_PCM3TRI 0x4
+
+#define FB_I2SPINC0_PCM2TRI 1
+#define FM_I2SPINC0_PCM2TRI 0x2
+
+#define FB_I2SPINC0_PCM1TRI 0
+#define FM_I2SPINC0_PCM1TRI 0x1
+
+// *** I2SPINC1 ***
+#define FB_I2SPINC1_SDO3PDD 2
+#define FM_I2SPINC1_SDO3PDD 0x4
+
+#define FB_I2SPINC1_SDO2PDD 1
+#define FM_I2SPINC1_SDO2PDD 0x2
+
+#define FB_I2SPINC1_SDO1PDD 0
+#define FM_I2SPINC1_SDO1PDD 0x1
+
+// *** I2SPINC2 ***
+#define FB_I2SPINC2_LR3PDD 5
+#define FM_I2SPINC2_LR3PDD 0x20
+
+#define FB_I2SPINC2_BC3PDD 4
+#define FM_I2SPINC2_BC3PDD 0x10
+
+#define FB_I2SPINC2_LR2PDD 3
+#define FM_I2SPINC2_LR2PDD 0x8
+
+#define FB_I2SPINC2_BC2PDD 2
+#define FM_I2SPINC2_BC2PDD 0x4
+
+#define FB_I2SPINC2_LR1PDD 1
+#define FM_I2SPINC2_LR1PDD 0x2
+
+#define FB_I2SPINC2_BC1PDD 0
+#define FM_I2SPINC2_BC1PDD 0x1
+
+// *** GPIOCTL0 ***
+#define FB_GPIOCTL0_GPIO3INTP 7
+#define FM_GPIOCTL0_GPIO3INTP 0x80
+
+#define FB_GPIOCTL0_GPIO2INTP 6
+#define FM_GPIOCTL0_GPIO2INTP 0x40
+
+#define FB_GPIOCTL0_GPIO3CFG 5
+#define FM_GPIOCTL0_GPIO3CFG 0x20
+
+#define FB_GPIOCTL0_GPIO2CFG 4
+#define FM_GPIOCTL0_GPIO2CFG 0x10
+
+#define FB_GPIOCTL0_GPIO3IO 3
+#define FM_GPIOCTL0_GPIO3IO 0x8
+
+#define FB_GPIOCTL0_GPIO2IO 2
+#define FM_GPIOCTL0_GPIO2IO 0x4
+
+#define FB_GPIOCTL0_GPIO1IO 1
+#define FM_GPIOCTL0_GPIO1IO 0x2
+
+#define FB_GPIOCTL0_GPIO0IO 0
+#define FM_GPIOCTL0_GPIO0IO 0x1
+
+// *** GPIOCTL1 ***
+#define FB_GPIOCTL1_GPIO3 7
+#define FM_GPIOCTL1_GPIO3 0x80
+
+#define FB_GPIOCTL1_GPIO2 6
+#define FM_GPIOCTL1_GPIO2 0x40
+
+#define FB_GPIOCTL1_GPIO1 5
+#define FM_GPIOCTL1_GPIO1 0x20
+
+#define FB_GPIOCTL1_GPIO0 4
+#define FM_GPIOCTL1_GPIO0 0x10
+
+#define FB_GPIOCTL1_GPIO3RD 3
+#define FM_GPIOCTL1_GPIO3RD 0x8
+
+#define FB_GPIOCTL1_GPIO2RD 2
+#define FM_GPIOCTL1_GPIO2RD 0x4
+
+#define FB_GPIOCTL1_GPIO1RD 1
+#define FM_GPIOCTL1_GPIO1RD 0x2
+
+#define FB_GPIOCTL1_GPIO0RD 0
+#define FM_GPIOCTL1_GPIO0RD 0x1
+
+// *** ASRC ***
+#define FB_ASRC_ASRCOBW 7
+#define FM_ASRC_ASRCOBW 0x80
+
+#define FB_ASRC_ASRCIBW 6
+#define FM_ASRC_ASRCIBW 0x40
+
+#define FB_ASRC_ASRCOB 5
+#define FM_ASRC_ASRCOB 0x20
+#define FV_ASRCOB_ACTIVE 0x0
+#define FV_ASRCOB_BYPASSED 0x20
+
+#define FB_ASRC_ASRCIB 4
+#define FM_ASRC_ASRCIB 0x10
+#define FV_ASRCIB_ACTIVE 0x0
+#define FV_ASRCIB_BYPASSED 0x10
+
+#define FB_ASRC_ASRCOL 3
+#define FM_ASRC_ASRCOL 0x8
+
+#define FB_ASRC_ASRCIL 2
+#define FM_ASRC_ASRCIL 0x4
+
+// *** TDMCTL0 ***
+#define FB_TDMCTL0_TDMMD 2
+#define FM_TDMCTL0_TDMMD 0x4
+#define FV_TDMMD_200 0x0
+#define FV_TDMMD_256 0x4
+
+#define FB_TDMCTL0_SLSYNC 1
+#define FM_TDMCTL0_SLSYNC 0x2
+#define FV_SLSYNC_SHORT 0x0
+#define FV_SLSYNC_LONG 0x2
+
+#define FB_TDMCTL0_BDELAY 0
+#define FM_TDMCTL0_BDELAY 0x1
+#define FV_BDELAY_NO_DELAY 0x0
+#define FV_BDELAY_1BCLK_DELAY 0x1
+
+// *** TDMCTL1 ***
+#define FB_TDMCTL1_TDMSO 5
+#define FM_TDMCTL1_TDMSO 0x60
+#define FV_TDMSO_2 0x0
+#define FV_TDMSO_4 0x20
+#define FV_TDMSO_6 0x40
+
+#define FB_TDMCTL1_TDMDSS 3
+#define FM_TDMCTL1_TDMDSS 0x18
+#define FV_TDMDSS_16 0x0
+#define FV_TDMDSS_24 0x10
+#define FV_TDMDSS_32 0x18
+
+#define FB_TDMCTL1_TDMSI 0
+#define FM_TDMCTL1_TDMSI 0x3
+#define FV_TDMSI_2 0x0
+#define FV_TDMSI_4 0x1
+#define FV_TDMSI_6 0x2
+
+// *** PWRM0 ***
+#define FB_PWRM0_INPROC3PU 6
+#define FM_PWRM0_INPROC3PU 0x40
+
+#define FB_PWRM0_INPROC2PU 5
+#define FM_PWRM0_INPROC2PU 0x20
+
+#define FB_PWRM0_INPROC1PU 4
+#define FM_PWRM0_INPROC1PU 0x10
+
+#define FB_PWRM0_INPROC0PU 3
+#define FM_PWRM0_INPROC0PU 0x8
+
+#define FB_PWRM0_MICB2PU 2
+#define FM_PWRM0_MICB2PU 0x4
+
+#define FB_PWRM0_MICB1PU 1
+#define FM_PWRM0_MICB1PU 0x2
+
+#define FB_PWRM0_MCLKPEN 0
+#define FM_PWRM0_MCLKPEN 0x1
+
+// *** PWRM1 ***
+#define FB_PWRM1_SUBPU 7
+#define FM_PWRM1_SUBPU 0x80
+
+#define FB_PWRM1_HPLPU 6
+#define FM_PWRM1_HPLPU 0x40
+
+#define FB_PWRM1_HPRPU 5
+#define FM_PWRM1_HPRPU 0x20
+
+#define FB_PWRM1_SPKLPU 4
+#define FM_PWRM1_SPKLPU 0x10
+
+#define FB_PWRM1_SPKRPU 3
+#define FM_PWRM1_SPKRPU 0x8
+
+#define FB_PWRM1_D2S2PU 2
+#define FM_PWRM1_D2S2PU 0x4
+
+#define FB_PWRM1_D2S1PU 1
+#define FM_PWRM1_D2S1PU 0x2
+
+#define FB_PWRM1_VREFPU 0
+#define FM_PWRM1_VREFPU 0x1
+
+// *** PWRM2 ***
+#define FB_PWRM2_I2S3OPU 5
+#define FM_PWRM2_I2S3OPU 0x20
+#define FV_I2S3OPU_PWR_DOWN 0x0
+#define FV_I2S3OPU_PWR_UP 0x20
+
+#define FB_PWRM2_I2S2OPU 4
+#define FM_PWRM2_I2S2OPU 0x10
+#define FV_I2S2OPU_PWR_DOWN 0x0
+#define FV_I2S2OPU_PWR_UP 0x10
+
+#define FB_PWRM2_I2S1OPU 3
+#define FM_PWRM2_I2S1OPU 0x8
+#define FV_I2S1OPU_PWR_DOWN 0x0
+#define FV_I2S1OPU_PWR_UP 0x8
+
+#define FB_PWRM2_I2S3IPU 2
+#define FM_PWRM2_I2S3IPU 0x4
+#define FV_I2S3IPU_PWR_DOWN 0x0
+#define FV_I2S3IPU_PWR_UP 0x4
+
+#define FB_PWRM2_I2S2IPU 1
+#define FM_PWRM2_I2S2IPU 0x2
+#define FV_I2S2IPU_PWR_DOWN 0x0
+#define FV_I2S2IPU_PWR_UP 0x2
+
+#define FB_PWRM2_I2S1IPU 0
+#define FM_PWRM2_I2S1IPU 0x1
+#define FV_I2S1IPU_PWR_DOWN 0x0
+#define FV_I2S1IPU_PWR_UP 0x1
+
+#define PWRM2_I2SOPU_PWR_DOWN 0x0
+#define PWRM2_I2SOPU_PWR_UP 0x1
+#define PWRM2_I2SIPU_PWR_DOWN 0x0
+#define PWRM2_I2SIPU_PWR_UP 0x1
+
+// *** PWRM3 ***
+#define FB_PWRM3_BGSBUP 6
+#define FM_PWRM3_BGSBUP 0x40
+#define FV_BGSBUP_ON 0x0
+#define FV_BGSBUP_OFF 0x40
+
+#define FB_PWRM3_VGBAPU 5
+#define FM_PWRM3_VGBAPU 0x20
+#define FV_VGBAPU_ON 0x0
+#define FV_VGBAPU_OFF 0x20
+
+#define FB_PWRM3_LLINEPU 4
+#define FM_PWRM3_LLINEPU 0x10
+
+#define FB_PWRM3_RLINEPU 3
+#define FM_PWRM3_RLINEPU 0x8
+
+// *** PWRM4 ***
+#define FB_PWRM4_OPSUBPU 4
+#define FM_PWRM4_OPSUBPU 0x10
+
+#define FB_PWRM4_OPDACLPU 3
+#define FM_PWRM4_OPDACLPU 0x8
+
+#define FB_PWRM4_OPDACRPU 2
+#define FM_PWRM4_OPDACRPU 0x4
+
+#define FB_PWRM4_OPSPKLPU 1
+#define FM_PWRM4_OPSPKLPU 0x2
+
+#define FB_PWRM4_OPSPKRPU 0
+#define FM_PWRM4_OPSPKRPU 0x1
+
+// *** I2SIDCTL ***
+#define FB_I2SIDCTL_I2SI3DCTL 4
+#define FM_I2SIDCTL_I2SI3DCTL 0x30
+
+#define FB_I2SIDCTL_I2SI2DCTL 2
+#define FM_I2SIDCTL_I2SI2DCTL 0xC
+
+#define FB_I2SIDCTL_I2SI1DCTL 0
+#define FM_I2SIDCTL_I2SI1DCTL 0x3
+
+// *** I2SODCTL ***
+#define FB_I2SODCTL_I2SO3DCTL 4
+#define FM_I2SODCTL_I2SO3DCTL 0x30
+
+#define FB_I2SODCTL_I2SO2DCTL 2
+#define FM_I2SODCTL_I2SO2DCTL 0xC
+
+#define FB_I2SODCTL_I2SO1DCTL 0
+#define FM_I2SODCTL_I2SO1DCTL 0x3
+
+// *** AUDIOMUX1 ***
+#define FB_AUDIOMUX1_ASRCIMUX 6
+#define FM_AUDIOMUX1_ASRCIMUX 0xC0
+#define FV_ASRCIMUX_NONE 0x0
+#define FV_ASRCIMUX_I2S1 0x40
+#define FV_ASRCIMUX_I2S2 0x80
+#define FV_ASRCIMUX_I2S3 0xC0
+
+#define FB_AUDIOMUX1_I2S2MUX 3
+#define FM_AUDIOMUX1_I2S2MUX 0x38
+#define FV_I2S2MUX_I2S1 0x0
+#define FV_I2S2MUX_I2S2 0x8
+#define FV_I2S2MUX_I2S3 0x10
+#define FV_I2S2MUX_ADC_DMIC 0x18
+#define FV_I2S2MUX_DMIC2 0x20
+#define FV_I2S2MUX_CLASSD_DSP 0x28
+#define FV_I2S2MUX_DAC_DSP 0x30
+#define FV_I2S2MUX_SUB_DSP 0x38
+
+#define FB_AUDIOMUX1_I2S1MUX 0
+#define FM_AUDIOMUX1_I2S1MUX 0x7
+#define FV_I2S1MUX_I2S1 0x0
+#define FV_I2S1MUX_I2S2 0x1
+#define FV_I2S1MUX_I2S3 0x2
+#define FV_I2S1MUX_ADC_DMIC 0x3
+#define FV_I2S1MUX_DMIC2 0x4
+#define FV_I2S1MUX_CLASSD_DSP 0x5
+#define FV_I2S1MUX_DAC_DSP 0x6
+#define FV_I2S1MUX_SUB_DSP 0x7
+
+#define AUDIOMUX1_I2SMUX_I2S1 0x0
+#define AUDIOMUX1_I2SMUX_I2S2 0x1
+#define AUDIOMUX1_I2SMUX_I2S3 0x2
+#define AUDIOMUX1_I2SMUX_ADC_DMIC 0x3
+#define AUDIOMUX1_I2SMUX_DMIC2 0x4
+#define AUDIOMUX1_I2SMUX_CLASSD_DSP 0x5
+#define AUDIOMUX1_I2SMUX_DAC_DSP 0x6
+#define AUDIOMUX1_I2SMUX_SUB_DSP 0x7
+
+// *** AUDIOMUX2 ***
+#define FB_AUDIOMUX2_ASRCOMUX 6
+#define FM_AUDIOMUX2_ASRCOMUX 0xC0
+#define FV_ASRCOMUX_NONE 0x0
+#define FV_ASRCOMUX_I2S1 0x40
+#define FV_ASRCOMUX_I2S2 0x80
+#define FV_ASRCOMUX_I2S3 0xC0
+
+#define FB_AUDIOMUX2_DACMUX 3
+#define FM_AUDIOMUX2_DACMUX 0x38
+#define FV_DACMUX_I2S1 0x0
+#define FV_DACMUX_I2S2 0x8
+#define FV_DACMUX_I2S3 0x10
+#define FV_DACMUX_ADC_DMIC 0x18
+#define FV_DACMUX_DMIC2 0x20
+#define FV_DACMUX_CLASSD_DSP 0x28
+#define FV_DACMUX_DAC_DSP 0x30
+#define FV_DACMUX_SUB_DSP 0x38
+
+#define FB_AUDIOMUX2_I2S3MUX 0
+#define FM_AUDIOMUX2_I2S3MUX 0x7
+#define FV_I2S3MUX_I2S1 0x0
+#define FV_I2S3MUX_I2S2 0x1
+#define FV_I2S3MUX_I2S3 0x2
+#define FV_I2S3MUX_ADC_DMIC 0x3
+#define FV_I2S3MUX_DMIC2 0x4
+#define FV_I2S3MUX_CLASSD_DSP 0x5
+#define FV_I2S3MUX_DAC_DSP 0x6
+#define FV_I2S3MUX_SUB_DSP 0x7
+
+// *** AUDIOMUX3 ***
+#define FB_AUDIOMUX3_SUBMUX 3
+#define FM_AUDIOMUX3_SUBMUX 0xF8
+#define FV_SUBMUX_I2S1_L 0x0
+#define FV_SUBMUX_I2S1_R 0x8
+#define FV_SUBMUX_I2S1_LR 0x10
+#define FV_SUBMUX_I2S2_L 0x18
+#define FV_SUBMUX_I2S2_R 0x20
+#define FV_SUBMUX_I2S2_LR 0x28
+#define FV_SUBMUX_I2S3_L 0x30
+#define FV_SUBMUX_I2S3_R 0x38
+#define FV_SUBMUX_I2S3_LR 0x40
+#define FV_SUBMUX_ADC_DMIC_L 0x48
+#define FV_SUBMUX_ADC_DMIC_R 0x50
+#define FV_SUBMUX_ADC_DMIC_LR 0x58
+#define FV_SUBMUX_DMIC_L 0x60
+#define FV_SUBMUX_DMIC_R 0x68
+#define FV_SUBMUX_DMIC_LR 0x70
+#define FV_SUBMUX_CLASSD_DSP_L 0x78
+#define FV_SUBMUX_CLASSD_DSP_R 0x80
+#define FV_SUBMUX_CLASSD_DSP_LR 0x88
+
+#define FB_AUDIOMUX3_CLSSDMUX 0
+#define FM_AUDIOMUX3_CLSSDMUX 0x7
+#define FV_CLSSDMUX_I2S1 0x0
+#define FV_CLSSDMUX_I2S2 0x1
+#define FV_CLSSDMUX_I2S3 0x2
+#define FV_CLSSDMUX_ADC_DMIC 0x3
+#define FV_CLSSDMUX_DMIC2 0x4
+#define FV_CLSSDMUX_CLASSD_DSP 0x5
+#define FV_CLSSDMUX_DAC_DSP 0x6
+#define FV_CLSSDMUX_SUB_DSP 0x7
+
+// *** HSDCTL1 ***
+#define FB_HSDCTL1_HPJKTYPE 7
+#define FM_HSDCTL1_HPJKTYPE 0x80
+
+#define FB_HSDCTL1_CON_DET_PWD 6
+#define FM_HSDCTL1_CON_DET_PWD 0x40
+
+#define FB_HSDCTL1_DETCYC 4
+#define FM_HSDCTL1_DETCYC 0x30
+
+#define FB_HSDCTL1_HPDLYBYP 3
+#define FM_HSDCTL1_HPDLYBYP 0x8
+
+#define FB_HSDCTL1_HSDETPOL 2
+#define FM_HSDCTL1_HSDETPOL 0x4
+
+#define FB_HSDCTL1_HPID_EN 1
+#define FM_HSDCTL1_HPID_EN 0x2
+
+#define FB_HSDCTL1_GBLHS_EN 0
+#define FM_HSDCTL1_GBLHS_EN 0x1
+
+// *** HSDCTL2 ***
+#define FB_HSDCTL2_FMICBIAS1 6
+#define FM_HSDCTL2_FMICBIAS1 0xC0
+
+#define FB_HSDCTL2_MB1MODE 5
+#define FM_HSDCTL2_MB1MODE 0x20
+#define FV_MB1MODE_AUTO 0x0
+#define FV_MB1MODE_MANUAL 0x20
+
+#define FB_HSDCTL2_FORCETRG 4
+#define FM_HSDCTL2_FORCETRG 0x10
+
+#define FB_HSDCTL2_SWMODE 3
+#define FM_HSDCTL2_SWMODE 0x8
+
+#define FB_HSDCTL2_GHSHIZ 2
+#define FM_HSDCTL2_GHSHIZ 0x4
+
+#define FB_HSDCTL2_FPLUGTYPE 0
+#define FM_HSDCTL2_FPLUGTYPE 0x3
+
+// *** HSDSTAT ***
+#define FB_HSDSTAT_MBIAS1DRV 5
+#define FM_HSDSTAT_MBIAS1DRV 0x60
+
+#define FB_HSDSTAT_HSDETSTAT 3
+#define FM_HSDSTAT_HSDETSTAT 0x8
+
+#define FB_HSDSTAT_PLUGTYPE 1
+#define FM_HSDSTAT_PLUGTYPE 0x6
+
+#define FB_HSDSTAT_HSDETDONE 0
+#define FM_HSDSTAT_HSDETDONE 0x1
+
+// *** HSDDELAY ***
+#define FB_HSDDELAY_T_STABLE 0
+#define FM_HSDDELAY_T_STABLE 0x7
+
+// *** BUTCTL ***
+#define FB_BUTCTL_BPUSHSTAT 7
+#define FM_BUTCTL_BPUSHSTAT 0x80
+
+#define FB_BUTCTL_BPUSHDET 6
+#define FM_BUTCTL_BPUSHDET 0x40
+
+#define FB_BUTCTL_BPUSHEN 5
+#define FM_BUTCTL_BPUSHEN 0x20
+
+#define FB_BUTCTL_BSTABLE_L 3
+#define FM_BUTCTL_BSTABLE_L 0x18
+
+#define FB_BUTCTL_BSTABLE_S 0
+#define FM_BUTCTL_BSTABLE_S 0x7
+
+// *** CH0AIC ***
+#define FB_CH0AIC_INSELL 6
+#define FM_CH0AIC_INSELL 0xC0
+
+#define FB_CH0AIC_MICBST0 4
+#define FM_CH0AIC_MICBST0 0x30
+
+#define FB_CH0AIC_LADCIN 2
+#define FM_CH0AIC_LADCIN 0xC
+
+#define FB_CH0AIC_IN_BYPS_L_SEL 1
+#define FM_CH0AIC_IN_BYPS_L_SEL 0x2
+
+#define FB_CH0AIC_IPCH0S 0
+#define FM_CH0AIC_IPCH0S 0x1
+
+// *** CH1AIC ***
+#define FB_CH1AIC_INSELR 6
+#define FM_CH1AIC_INSELR 0xC0
+
+#define FB_CH1AIC_MICBST1 4
+#define FM_CH1AIC_MICBST1 0x30
+
+#define FB_CH1AIC_RADCIN 2
+#define FM_CH1AIC_RADCIN 0xC
+
+#define FB_CH1AIC_IN_BYPS_R_SEL 1
+#define FM_CH1AIC_IN_BYPS_R_SEL 0x2
+
+#define FB_CH1AIC_IPCH1S 0
+#define FM_CH1AIC_IPCH1S 0x1
+
+// *** ICTL0 ***
+#define FB_ICTL0_IN1POL 7
+#define FM_ICTL0_IN1POL 0x80
+
+#define FB_ICTL0_IN0POL 6
+#define FM_ICTL0_IN0POL 0x40
+
+#define FB_ICTL0_INPCH10SEL 4
+#define FM_ICTL0_INPCH10SEL 0x30
+
+#define FB_ICTL0_IN1MUTE 3
+#define FM_ICTL0_IN1MUTE 0x8
+
+#define FB_ICTL0_IN0MUTE 2
+#define FM_ICTL0_IN0MUTE 0x4
+
+#define FB_ICTL0_IN1HP 1
+#define FM_ICTL0_IN1HP 0x2
+
+#define FB_ICTL0_IN0HP 0
+#define FM_ICTL0_IN0HP 0x1
+
+// *** ICTL1 ***
+#define FB_ICTL1_IN3POL 7
+#define FM_ICTL1_IN3POL 0x80
+
+#define FB_ICTL1_IN2POL 6
+#define FM_ICTL1_IN2POL 0x40
+
+#define FB_ICTL1_INPCH32SEL 4
+#define FM_ICTL1_INPCH32SEL 0x30
+
+#define FB_ICTL1_IN3MUTE 3
+#define FM_ICTL1_IN3MUTE 0x8
+
+#define FB_ICTL1_IN2MUTE 2
+#define FM_ICTL1_IN2MUTE 0x4
+
+#define FB_ICTL1_IN3HP 1
+#define FM_ICTL1_IN3HP 0x2
+
+#define FB_ICTL1_IN2HP 0
+#define FM_ICTL1_IN2HP 0x1
+
+// *** MICBIAS ***
+#define FB_MICBIAS_MICBOV2 4
+#define FM_MICBIAS_MICBOV2 0x30
+
+#define FB_MICBIAS_MICBOV1 6
+#define FM_MICBIAS_MICBOV1 0xC0
+
+#define FB_MICBIAS_SPARE1 2
+#define FM_MICBIAS_SPARE1 0xC
+
+#define FB_MICBIAS_SPARE2 0
+#define FM_MICBIAS_SPARE2 0x3
+
+// *** PGAZ ***
+#define FB_PGAZ_INHPOR 1
+#define FM_PGAZ_INHPOR 0x2
+
+#define FB_PGAZ_TOEN 0
+#define FM_PGAZ_TOEN 0x1
+
+// *** ASRCILVOL ***
+#define FB_ASRCILVOL_ASRCILVOL 0
+#define FM_ASRCILVOL_ASRCILVOL 0xFF
+
+// *** ASRCIRVOL ***
+#define FB_ASRCIRVOL_ASRCIRVOL 0
+#define FM_ASRCIRVOL_ASRCIRVOL 0xFF
+
+// *** ASRCOLVOL ***
+#define FB_ASRCOLVOL_ASRCOLVOL 0
+#define FM_ASRCOLVOL_ASRCOLVOL 0xFF
+
+// *** ASRCORVOL ***
+#define FB_ASRCORVOL_ASRCOLVOL 0
+#define FM_ASRCORVOL_ASRCOLVOL 0xFF
+
+// *** IVOLCTLU ***
+#define FB_IVOLCTLU_IFADE 3
+#define FM_IVOLCTLU_IFADE 0x8
+
+#define FB_IVOLCTLU_INPVOLU 2
+#define FM_IVOLCTLU_INPVOLU 0x4
+
+#define FB_IVOLCTLU_PGAVOLU 1
+#define FM_IVOLCTLU_PGAVOLU 0x2
+
+#define FB_IVOLCTLU_ASRCVOLU 0
+#define FM_IVOLCTLU_ASRCVOLU 0x1
+
+// *** ALCCTL0 ***
+#define FB_ALCCTL0_ALCMODE 7
+#define FM_ALCCTL0_ALCMODE 0x80
+
+#define FB_ALCCTL0_ALCREF 4
+#define FM_ALCCTL0_ALCREF 0x70
+
+#define FB_ALCCTL0_ALCEN3 3
+#define FM_ALCCTL0_ALCEN3 0x8
+
+#define FB_ALCCTL0_ALCEN2 2
+#define FM_ALCCTL0_ALCEN2 0x4
+
+#define FB_ALCCTL0_ALCEN1 1
+#define FM_ALCCTL0_ALCEN1 0x2
+
+#define FB_ALCCTL0_ALCEN0 0
+#define FM_ALCCTL0_ALCEN0 0x1
+
+// *** ALCCTL1 ***
+#define FB_ALCCTL1_MAXGAIN 4
+#define FM_ALCCTL1_MAXGAIN 0x70
+
+#define FB_ALCCTL1_ALCL 0
+#define FM_ALCCTL1_ALCL 0xF
+
+// *** ALCCTL2 ***
+#define FB_ALCCTL2_ALCZC 7
+#define FM_ALCCTL2_ALCZC 0x80
+
+#define FB_ALCCTL2_MINGAIN 4
+#define FM_ALCCTL2_MINGAIN 0x70
+
+#define FB_ALCCTL2_HLD 0
+#define FM_ALCCTL2_HLD 0xF
+
+// *** ALCCTL3 ***
+#define FB_ALCCTL3_DCY 4
+#define FM_ALCCTL3_DCY 0xF0
+
+#define FB_ALCCTL3_ATK 0
+#define FM_ALCCTL3_ATK 0xF
+
+// *** NGATE ***
+#define FB_NGATE_NGTH 3
+#define FM_NGATE_NGTH 0xF8
+
+#define FB_NGATE_NGG 1
+#define FM_NGATE_NGG 0x6
+
+#define FB_NGATE_NGAT 0
+#define FM_NGATE_NGAT 0x1
+
+// *** DMICCTL ***
+#define FB_DMICCTL_DMIC2EN 7
+#define FM_DMICCTL_DMIC2EN 0x80
+
+#define FB_DMICCTL_DMIC1EN 6
+#define FM_DMICCTL_DMIC1EN 0x40
+
+#define FB_DMICCTL_DMONO 4
+#define FM_DMICCTL_DMONO 0x10
+
+#define FB_DMICCTL_DMDCLK 2
+#define FM_DMICCTL_DMDCLK 0xC
+
+#define FB_DMICCTL_DMRATE 0
+#define FM_DMICCTL_DMRATE 0x3
+
+// *** DACCTL ***
+#define FB_DACCTL_DACPOLR 7
+#define FM_DACCTL_DACPOLR 0x80
+#define FV_DACPOLR_NORMAL 0x0
+#define FV_DACPOLR_INVERTED 0x80
+
+#define FB_DACCTL_DACPOLL 6
+#define FM_DACCTL_DACPOLL 0x40
+#define FV_DACPOLL_NORMAL 0x0
+#define FV_DACPOLL_INVERTED 0x40
+
+#define FB_DACCTL_DACDITH 4
+#define FM_DACCTL_DACDITH 0x30
+#define FV_DACDITH_DYNAMIC_HALF 0x0
+#define FV_DACDITH_DYNAMIC_FULL 0x10
+#define FV_DACDITH_DISABLED 0x20
+#define FV_DACDITH_STATIC 0x30
+
+#define FB_DACCTL_DACMUTE 3
+#define FM_DACCTL_DACMUTE 0x8
+#define FV_DACMUTE_ENABLE 0x8
+#define FV_DACMUTE_DISABLE 0x0
+
+#define FB_DACCTL_DACDEM 2
+#define FM_DACCTL_DACDEM 0x4
+#define FV_DACDEM_ENABLE 0x4
+#define FV_DACDEM_DISABLE 0x0
+
+#define FB_DACCTL_ABYPASS 0
+#define FM_DACCTL_ABYPASS 0x1
+
+// *** SPKCTL ***
+#define FB_SPKCTL_SPKPOLR 7
+#define FM_SPKCTL_SPKPOLR 0x80
+#define FV_SPKPOLR_NORMAL 0x0
+#define FV_SPKPOLR_INVERTED 0x80
+
+#define FB_SPKCTL_SPKPOLL 6
+#define FM_SPKCTL_SPKPOLL 0x40
+#define FV_SPKPOLL_NORMAL 0x0
+#define FV_SPKPOLL_INVERTED 0x40
+
+#define FB_SPKCTL_SPKMUTE 3
+#define FM_SPKCTL_SPKMUTE 0x8
+#define FV_SPKMUTE_ENABLE 0x8
+#define FV_SPKMUTE_DISABLE 0x0
+
+#define FB_SPKCTL_SPKDEM 2
+#define FM_SPKCTL_SPKDEM 0x4
+#define FV_SPKDEM_ENABLE 0x4
+#define FV_SPKDEM_DISABLE 0x0
+
+// *** SUBCTL ***
+#define FB_SUBCTL_SUBPOL 7
+#define FM_SUBCTL_SUBPOL 0x80
+
+#define FB_SUBCTL_SUBMUTE 3
+#define FM_SUBCTL_SUBMUTE 0x8
+
+#define FB_SUBCTL_SUBDEM 2
+#define FM_SUBCTL_SUBDEM 0x4
+
+#define FB_SUBCTL_SUBMUX 1
+#define FM_SUBCTL_SUBMUX 0x2
+
+#define FB_SUBCTL_SUBILMDIS 0
+#define FM_SUBCTL_SUBILMDIS 0x1
+
+// *** DCCTL ***
+#define FB_DCCTL_SUBDCBYP 7
+#define FM_DCCTL_SUBDCBYP 0x80
+
+#define FB_DCCTL_DACDCBYP 6
+#define FM_DCCTL_DACDCBYP 0x40
+
+#define FB_DCCTL_SPKDCBYP 5
+#define FM_DCCTL_SPKDCBYP 0x20
+
+#define FB_DCCTL_DCCOEFSEL 0
+#define FM_DCCTL_DCCOEFSEL 0x7
+
+// *** OVOLCTLU ***
+#define FB_OVOLCTLU_OFADE 4
+#define FM_OVOLCTLU_OFADE 0x10
+
+#define FB_OVOLCTLU_SUBVOLU 3
+#define FM_OVOLCTLU_SUBVOLU 0x8
+
+#define FB_OVOLCTLU_MVOLU 2
+#define FM_OVOLCTLU_MVOLU 0x4
+
+#define FB_OVOLCTLU_SPKVOLU 1
+#define FM_OVOLCTLU_SPKVOLU 0x2
+
+#define FB_OVOLCTLU_HPVOLU 0
+#define FM_OVOLCTLU_HPVOLU 0x1
+
+// *** MUTEC ***
+#define FB_MUTEC_ZDSTAT 7
+#define FM_MUTEC_ZDSTAT 0x80
+
+#define FB_MUTEC_ZDLEN 4
+#define FM_MUTEC_ZDLEN 0x30
+
+#define FB_MUTEC_APWD 3
+#define FM_MUTEC_APWD 0x8
+
+#define FB_MUTEC_AMUTE 2
+#define FM_MUTEC_AMUTE 0x4
+
+// *** MVOLL ***
+#define FB_MVOLL_MVOL_L 0
+#define FM_MVOLL_MVOL_L 0xFF
+
+// *** MVOLR ***
+#define FB_MVOLR_MVOL_R 0
+#define FM_MVOLR_MVOL_R 0xFF
+
+// *** HPVOLL ***
+#define FB_HPVOLL_HPVOL_L 0
+#define FM_HPVOLL_HPVOL_L 0x7F
+
+// *** HPVOLR ***
+#define FB_HPVOLR_HPVOL_R 0
+#define FM_HPVOLR_HPVOL_R 0x7F
+
+// *** SPKVOLL ***
+#define FB_SPKVOLL_SPKVOL_L 0
+#define FM_SPKVOLL_SPKVOL_L 0x7F
+
+// *** SPKVOLR ***
+#define FB_SPKVOLR_SPKVOL_R 0
+#define FM_SPKVOLR_SPKVOL_R 0x7F
+
+// *** SUBVOL ***
+#define FB_SUBVOL_SUBVOL 0
+#define FM_SUBVOL_SUBVOL 0x7F
+
+// *** COP0 ***
+#define FB_COP0_COPATTEN 7
+#define FM_COP0_COPATTEN 0x80
+
+#define FB_COP0_COPGAIN 6
+#define FM_COP0_COPGAIN 0x40
+
+#define FB_COP0_HDELTAEN 5
+#define FM_COP0_HDELTAEN 0x20
+
+#define FB_COP0_COPTARGET 0
+#define FM_COP0_COPTARGET 0x1F
+
+// *** COP1 ***
+#define FB_COP1_HDCOMPMODE 6
+#define FM_COP1_HDCOMPMODE 0x40
+
+#define FB_COP1_AVGLENGTH 2
+#define FM_COP1_AVGLENGTH 0x3C
+
+#define FB_COP1_MONRATE 0
+#define FM_COP1_MONRATE 0x3
+
+// *** COPSTAT ***
+#define FB_COPSTAT_HDELTADET 7
+#define FM_COPSTAT_HDELTADET 0x80
+
+#define FB_COPSTAT_UV 6
+#define FM_COPSTAT_UV 0x40
+
+#define FB_COPSTAT_COPADJ 0
+#define FM_COPSTAT_COPADJ 0x3F
+
+// *** PWM0 ***
+#define FB_PWM0_SCTO 6
+#define FM_PWM0_SCTO 0xC0
+
+#define FB_PWM0_UVLO 5
+#define FM_PWM0_UVLO 0x20
+
+#define FB_PWM0_BFDIS 3
+#define FM_PWM0_BFDIS 0x8
+
+#define FB_PWM0_PWMMODE 2
+#define FM_PWM0_PWMMODE 0x4
+
+#define FB_PWM0_NOOFFSET 0
+#define FM_PWM0_NOOFFSET 0x1
+
+// *** PWM1 ***
+#define FB_PWM1_DITHPOS 4
+#define FM_PWM1_DITHPOS 0x70
+
+#define FB_PWM1_DYNDITH 1
+#define FM_PWM1_DYNDITH 0x2
+
+#define FB_PWM1_DITHDIS 0
+#define FM_PWM1_DITHDIS 0x1
+
+// *** PWM2 ***
+// *** PWM3 ***
+#define FB_PWM3_PWMMUX 6
+#define FM_PWM3_PWMMUX 0xC0
+
+#define FB_PWM3_CVALUE 0
+#define FM_PWM3_CVALUE 0x7
+
+// *** HPSW ***
+#define FB_HPSW_HPDETSTATE 4
+#define FM_HPSW_HPDETSTATE 0x10
+
+#define FB_HPSW_HPSWEN 2
+#define FM_HPSW_HPSWEN 0xC
+
+#define FB_HPSW_HPSWPOL 1
+#define FM_HPSW_HPSWPOL 0x2
+
+#define FB_HPSW_TSDEN 0
+#define FM_HPSW_TSDEN 0x1
+
+// *** THERMTS ***
+#define FB_THERMTS_TRIPHS 7
+#define FM_THERMTS_TRIPHS 0x80
+
+#define FB_THERMTS_TRIPLS 6
+#define FM_THERMTS_TRIPLS 0x40
+
+#define FB_THERMTS_TRIPSPLIT 4
+#define FM_THERMTS_TRIPSPLIT 0x30
+
+#define FB_THERMTS_TRIPSHIFT 2
+#define FM_THERMTS_TRIPSHIFT 0xC
+
+#define FB_THERMTS_TSPOLL 0
+#define FM_THERMTS_TSPOLL 0x3
+
+// *** THERMSPK1 ***
+#define FB_THERMSPK1_FORCEPWD 7
+#define FM_THERMSPK1_FORCEPWD 0x80
+
+#define FB_THERMSPK1_INSTCUTMODE 6
+#define FM_THERMSPK1_INSTCUTMODE 0x40
+
+#define FB_THERMSPK1_INCRATIO 4
+#define FM_THERMSPK1_INCRATIO 0x30
+
+#define FB_THERMSPK1_INCSTEP 2
+#define FM_THERMSPK1_INCSTEP 0xC
+
+#define FB_THERMSPK1_DECSTEP 0
+#define FM_THERMSPK1_DECSTEP 0x3
+
+// *** THERMSTAT ***
+#define FB_THERMSTAT_FPWDS 7
+#define FM_THERMSTAT_FPWDS 0x80
+
+#define FB_THERMSTAT_VOLSTAT 0
+#define FM_THERMSTAT_VOLSTAT 0x7F
+
+// *** SCSTAT ***
+#define FB_SCSTAT_ESDF 3
+#define FM_SCSTAT_ESDF 0x18
+
+#define FB_SCSTAT_CPF 2
+#define FM_SCSTAT_CPF 0x4
+
+#define FB_SCSTAT_CLSDF 0
+#define FM_SCSTAT_CLSDF 0x3
+
+// *** SDMON ***
+#define FB_SDMON_SDFORCE 7
+#define FM_SDMON_SDFORCE 0x80
+
+#define FB_SDMON_SDVALUE 0
+#define FM_SDMON_SDVALUE 0x1F
+
+// *** SPKEQFILT ***
+#define FB_SPKEQFILT_EQ2EN 7
+#define FM_SPKEQFILT_EQ2EN 0x80
+#define FV_EQ2EN_ENABLE 0x80
+#define FV_EQ2EN_DISABLE 0x0
+
+#define FB_SPKEQFILT_EQ2BE 4
+#define FM_SPKEQFILT_EQ2BE 0x70
+
+#define FB_SPKEQFILT_EQ1EN 3
+#define FM_SPKEQFILT_EQ1EN 0x8
+#define FV_EQ1EN_ENABLE 0x8
+#define FV_EQ1EN_DISABLE 0x0
+
+#define FB_SPKEQFILT_EQ1BE 0
+#define FM_SPKEQFILT_EQ1BE 0x7
+
+#define SPKEQFILT_EQEN_ENABLE 0x1
+#define SPKEQFILT_EQEN_DISABLE 0x0
+
+// *** SPKCRWDL ***
+#define FB_SPKCRWDL_WDATA_L 0
+#define FM_SPKCRWDL_WDATA_L 0xFF
+
+// *** SPKCRWDM ***
+#define FB_SPKCRWDM_WDATA_M 0
+#define FM_SPKCRWDM_WDATA_M 0xFF
+
+// *** SPKCRWDH ***
+#define FB_SPKCRWDH_WDATA_H 0
+#define FM_SPKCRWDH_WDATA_H 0xFF
+
+// *** SPKCRRDL ***
+#define FB_SPKCRRDL_RDATA_L 0
+#define FM_SPKCRRDL_RDATA_L 0xFF
+
+// *** SPKCRRDM ***
+#define FB_SPKCRRDM_RDATA_M 0
+#define FM_SPKCRRDM_RDATA_M 0xFF
+
+// *** SPKCRRDH ***
+#define FB_SPKCRRDH_RDATA_H 0
+#define FM_SPKCRRDH_RDATA_H 0xFF
+
+// *** SPKCRADD ***
+#define FB_SPKCRADD_ADDRESS 0
+#define FM_SPKCRADD_ADDRESS 0xFF
+
+// *** SPKCRS ***
+#define FB_SPKCRS_ACCSTAT 7
+#define FM_SPKCRS_ACCSTAT 0x80
+
+// *** SPKMBCEN ***
+#define FB_SPKMBCEN_MBCEN3 2
+#define FM_SPKMBCEN_MBCEN3 0x4
+#define FV_MBCEN3_ENABLE 0x4
+#define FV_MBCEN3_DISABLE 0x0
+
+#define FB_SPKMBCEN_MBCEN2 1
+#define FM_SPKMBCEN_MBCEN2 0x2
+#define FV_MBCEN2_ENABLE 0x2
+#define FV_MBCEN2_DISABLE 0x0
+
+#define FB_SPKMBCEN_MBCEN1 0
+#define FM_SPKMBCEN_MBCEN1 0x1
+#define FV_MBCEN1_ENABLE 0x1
+#define FV_MBCEN1_DISABLE 0x0
+
+#define SPKMBCEN_MBCEN_ENABLE 0x1
+#define SPKMBCEN_MBCEN_DISABLE 0x0
+
+// *** SPKMBCCTL ***
+#define FB_SPKMBCCTL_LVLMODE3 5
+#define FM_SPKMBCCTL_LVLMODE3 0x20
+
+#define FB_SPKMBCCTL_WINSEL3 4
+#define FM_SPKMBCCTL_WINSEL3 0x10
+
+#define FB_SPKMBCCTL_LVLMODE2 3
+#define FM_SPKMBCCTL_LVLMODE2 0x8
+
+#define FB_SPKMBCCTL_WINSEL2 2
+#define FM_SPKMBCCTL_WINSEL2 0x4
+
+#define FB_SPKMBCCTL_LVLMODE1 1
+#define FM_SPKMBCCTL_LVLMODE1 0x2
+
+#define FB_SPKMBCCTL_WINSEL1 0
+#define FM_SPKMBCCTL_WINSEL1 0x1
+
+// *** SPKCLECTL ***
+#define FB_SPKCLECTL_LVLMODE 4
+#define FM_SPKCLECTL_LVLMODE 0x10
+
+#define FB_SPKCLECTL_WINSEL 3
+#define FM_SPKCLECTL_WINSEL 0x8
+
+#define FB_SPKCLECTL_EXPEN 2
+#define FM_SPKCLECTL_EXPEN 0x4
+#define FV_EXPEN_ENABLE 0x4
+#define FV_EXPEN_DISABLE 0x0
+
+#define FB_SPKCLECTL_LIMEN 1
+#define FM_SPKCLECTL_LIMEN 0x2
+#define FV_LIMEN_ENABLE 0x2
+#define FV_LIMEN_DISABLE 0x0
+
+#define FB_SPKCLECTL_COMPEN 0
+#define FM_SPKCLECTL_COMPEN 0x1
+#define FV_COMPEN_ENABLE 0x1
+#define FV_COMPEN_DISABLE 0x0
+
+// *** SPKCLEMUG ***
+#define FB_SPKCLEMUG_MUGAIN 0
+#define FM_SPKCLEMUG_MUGAIN 0x1F
+
+// *** SPKCOMPTHR ***
+#define FB_SPKCOMPTHR_THRESH 0
+#define FM_SPKCOMPTHR_THRESH 0xFF
+
+// *** SPKCOMPRAT ***
+#define FB_SPKCOMPRAT_RATIO 0
+#define FM_SPKCOMPRAT_RATIO 0x1F
+
+// *** SPKCOMPATKL ***
+#define FB_SPKCOMPATKL_TCATKL 0
+#define FM_SPKCOMPATKL_TCATKL 0xFF
+
+// *** SPKCOMPATKH ***
+#define FB_SPKCOMPATKH_TCATKH 0
+#define FM_SPKCOMPATKH_TCATKH 0xFF
+
+// *** SPKCOMPRELL ***
+#define FB_SPKCOMPRELL_TCRELL 0
+#define FM_SPKCOMPRELL_TCRELL 0xFF
+
+// *** SPKCOMPRELH ***
+#define FB_SPKCOMPRELH_TCRELH 0
+#define FM_SPKCOMPRELH_TCRELH 0xFF
+
+// *** SPKLIMTHR ***
+#define FB_SPKLIMTHR_THRESH 0
+#define FM_SPKLIMTHR_THRESH 0xFF
+
+// *** SPKLIMTGT ***
+#define FB_SPKLIMTGT_TARGET 0
+#define FM_SPKLIMTGT_TARGET 0xFF
+
+// *** SPKLIMATKL ***
+#define FB_SPKLIMATKL_TCATKL 0
+#define FM_SPKLIMATKL_TCATKL 0xFF
+
+// *** SPKLIMATKH ***
+#define FB_SPKLIMATKH_TCATKH 0
+#define FM_SPKLIMATKH_TCATKH 0xFF
+
+// *** SPKLIMRELL ***
+#define FB_SPKLIMRELL_TCRELL 0
+#define FM_SPKLIMRELL_TCRELL 0xFF
+
+// *** SPKLIMRELH ***
+#define FB_SPKLIMRELH_TCRELH 0
+#define FM_SPKLIMRELH_TCRELH 0xFF
+
+// *** SPKEXPTHR ***
+#define FB_SPKEXPTHR_THRESH 0
+#define FM_SPKEXPTHR_THRESH 0xFF
+
+// *** SPKEXPRAT ***
+#define FB_SPKEXPRAT_RATIO 0
+#define FM_SPKEXPRAT_RATIO 0x7
+
+// *** SPKEXPATKL ***
+#define FB_SPKEXPATKL_TCATKL 0
+#define FM_SPKEXPATKL_TCATKL 0xFF
+
+// *** SPKEXPATKH ***
+#define FB_SPKEXPATKH_TCATKH 0
+#define FM_SPKEXPATKH_TCATKH 0xFF
+
+// *** SPKEXPRELL ***
+#define FB_SPKEXPRELL_TCRELL 0
+#define FM_SPKEXPRELL_TCRELL 0xFF
+
+// *** SPKEXPRELH ***
+#define FB_SPKEXPRELH_TCRELH 0
+#define FM_SPKEXPRELH_TCRELH 0xFF
+
+// *** SPKFXCTL ***
+#define FB_SPKFXCTL_3DEN 4
+#define FM_SPKFXCTL_3DEN 0x10
+
+#define FB_SPKFXCTL_TEEN 3
+#define FM_SPKFXCTL_TEEN 0x8
+
+#define FB_SPKFXCTL_TNLFBYP 2
+#define FM_SPKFXCTL_TNLFBYP 0x4
+
+#define FB_SPKFXCTL_BEEN 1
+#define FM_SPKFXCTL_BEEN 0x2
+
+#define FB_SPKFXCTL_BNLFBYP 0
+#define FM_SPKFXCTL_BNLFBYP 0x1
+
+// *** DACEQFILT ***
+#define FB_DACEQFILT_EQ2EN 7
+#define FM_DACEQFILT_EQ2EN 0x80
+#define FV_EQ2EN_ENABLE 0x80
+#define FV_EQ2EN_DISABLE 0x0
+
+#define FB_DACEQFILT_EQ2BE 4
+#define FM_DACEQFILT_EQ2BE 0x70
+
+#define FB_DACEQFILT_EQ1EN 3
+#define FM_DACEQFILT_EQ1EN 0x8
+#define FV_EQ1EN_ENABLE 0x8
+#define FV_EQ1EN_DISABLE 0x0
+
+#define FB_DACEQFILT_EQ1BE 0
+#define FM_DACEQFILT_EQ1BE 0x7
+
+#define DACEQFILT_EQEN_ENABLE 0x1
+#define DACEQFILT_EQEN_DISABLE 0x0
+
+// *** DACCRWDL ***
+#define FB_DACCRWDL_WDATA_L 0
+#define FM_DACCRWDL_WDATA_L 0xFF
+
+// *** DACCRWDM ***
+#define FB_DACCRWDM_WDATA_M 0
+#define FM_DACCRWDM_WDATA_M 0xFF
+
+// *** DACCRWDH ***
+#define FB_DACCRWDH_WDATA_H 0
+#define FM_DACCRWDH_WDATA_H 0xFF
+
+// *** DACCRRDL ***
+#define FB_DACCRRDL_RDATA_L 0
+#define FM_DACCRRDL_RDATA_L 0xFF
+
+// *** DACCRRDM ***
+#define FB_DACCRRDM_RDATA_M 0
+#define FM_DACCRRDM_RDATA_M 0xFF
+
+// *** DACCRRDH ***
+#define FB_DACCRRDH_RDATA_H 0
+#define FM_DACCRRDH_RDATA_H 0xFF
+
+// *** DACCRADD ***
+#define FB_DACCRADD_ADDRESS 0
+#define FM_DACCRADD_ADDRESS 0xFF
+
+// *** DACCRS ***
+#define FB_DACCRS_ACCSTAT 7
+#define FM_DACCRS_ACCSTAT 0x80
+
+// *** DACMBCEN ***
+#define FB_DACMBCEN_MBCEN3 2
+#define FM_DACMBCEN_MBCEN3 0x4
+#define FV_MBCEN3_ENABLE 0x4
+#define FV_MBCEN3_DISABLE 0x0
+
+#define FB_DACMBCEN_MBCEN2 1
+#define FM_DACMBCEN_MBCEN2 0x2
+#define FV_MBCEN2_ENABLE 0x2
+#define FV_MBCEN2_DISABLE 0x0
+
+#define FB_DACMBCEN_MBCEN1 0
+#define FM_DACMBCEN_MBCEN1 0x1
+#define FV_MBCEN1_ENABLE 0x1
+#define FV_MBCEN1_DISABLE 0x0
+
+#define DACMBCEN_MBCEN_ENABLE 0x1
+#define DACMBCEN_MBCEN_DISABLE 0x0
+
+// *** DACMBCCTL ***
+#define FB_DACMBCCTL_LVLMODE3 5
+#define FM_DACMBCCTL_LVLMODE3 0x20
+
+#define FB_DACMBCCTL_WINSEL3 4
+#define FM_DACMBCCTL_WINSEL3 0x10
+
+#define FB_DACMBCCTL_LVLMODE2 3
+#define FM_DACMBCCTL_LVLMODE2 0x8
+
+#define FB_DACMBCCTL_WINSEL2 2
+#define FM_DACMBCCTL_WINSEL2 0x4
+
+#define FB_DACMBCCTL_LVLMODE1 1
+#define FM_DACMBCCTL_LVLMODE1 0x2
+
+#define FB_DACMBCCTL_WINSEL1 0
+#define FM_DACMBCCTL_WINSEL1 0x1
+
+// *** DACCLECTL ***
+#define FB_DACCLECTL_LVLMODE 4
+#define FM_DACCLECTL_LVLMODE 0x10
+
+#define FB_DACCLECTL_WINSEL 3
+#define FM_DACCLECTL_WINSEL 0x8
+
+#define FB_DACCLECTL_EXPEN 2
+#define FM_DACCLECTL_EXPEN 0x4
+#define FV_EXPEN_ENABLE 0x4
+#define FV_EXPEN_DISABLE 0x0
+
+#define FB_DACCLECTL_LIMEN 1
+#define FM_DACCLECTL_LIMEN 0x2
+#define FV_LIMEN_ENABLE 0x2
+#define FV_LIMEN_DISABLE 0x0
+
+#define FB_DACCLECTL_COMPEN 0
+#define FM_DACCLECTL_COMPEN 0x1
+#define FV_COMPEN_ENABLE 0x1
+#define FV_COMPEN_DISABLE 0x0
+
+// *** DACCLEMUG ***
+#define FB_DACCLEMUG_MUGAIN 0
+#define FM_DACCLEMUG_MUGAIN 0x1F
+
+// *** DACCOMPTHR ***
+#define FB_DACCOMPTHR_THRESH 0
+#define FM_DACCOMPTHR_THRESH 0xFF
+
+// *** DACCOMPRAT ***
+#define FB_DACCOMPRAT_RATIO 0
+#define FM_DACCOMPRAT_RATIO 0x1F
+
+// *** DACCOMPATKL ***
+#define FB_DACCOMPATKL_TCATKL 0
+#define FM_DACCOMPATKL_TCATKL 0xFF
+
+// *** DACCOMPATKH ***
+#define FB_DACCOMPATKH_TCATKH 0
+#define FM_DACCOMPATKH_TCATKH 0xFF
+
+// *** DACCOMPRELL ***
+#define FB_DACCOMPRELL_TCRELL 0
+#define FM_DACCOMPRELL_TCRELL 0xFF
+
+// *** DACCOMPRELH ***
+#define FB_DACCOMPRELH_TCRELH 0
+#define FM_DACCOMPRELH_TCRELH 0xFF
+
+// *** DACLIMTHR ***
+#define FB_DACLIMTHR_THRESH 0
+#define FM_DACLIMTHR_THRESH 0xFF
+
+// *** DACLIMTGT ***
+#define FB_DACLIMTGT_TARGET 0
+#define FM_DACLIMTGT_TARGET 0xFF
+
+// *** DACLIMATKL ***
+#define FB_DACLIMATKL_TCATKL 0
+#define FM_DACLIMATKL_TCATKL 0xFF
+
+// *** DACLIMATKH ***
+#define FB_DACLIMATKH_TCATKH 0
+#define FM_DACLIMATKH_TCATKH 0xFF
+
+// *** DACLIMRELL ***
+#define FB_DACLIMRELL_TCRELL 0
+#define FM_DACLIMRELL_TCRELL 0xFF
+
+// *** DACLIMRELH ***
+#define FB_DACLIMRELH_TCRELH 0
+#define FM_DACLIMRELH_TCRELH 0xFF
+
+// *** DACEXPTHR ***
+#define FB_DACEXPTHR_THRESH 0
+#define FM_DACEXPTHR_THRESH 0xFF
+
+// *** DACEXPRAT ***
+#define FB_DACEXPRAT_RATIO 0
+#define FM_DACEXPRAT_RATIO 0x7
+
+// *** DACEXPATKL ***
+#define FB_DACEXPATKL_TCATKL 0
+#define FM_DACEXPATKL_TCATKL 0xFF
+
+// *** DACEXPATKH ***
+#define FB_DACEXPATKH_TCATKH 0
+#define FM_DACEXPATKH_TCATKH 0xFF
+
+// *** DACEXPRELL ***
+#define FB_DACEXPRELL_TCRELL 0
+#define FM_DACEXPRELL_TCRELL 0xFF
+
+// *** DACEXPRELH ***
+#define FB_DACEXPRELH_TCRELH 0
+#define FM_DACEXPRELH_TCRELH 0xFF
+
+// *** DACFXCTL ***
+#define FB_DACFXCTL_3DEN 4
+#define FM_DACFXCTL_3DEN 0x10
+
+#define FB_DACFXCTL_TEEN 3
+#define FM_DACFXCTL_TEEN 0x8
+
+#define FB_DACFXCTL_TNLFBYP 2
+#define FM_DACFXCTL_TNLFBYP 0x4
+
+#define FB_DACFXCTL_BEEN 1
+#define FM_DACFXCTL_BEEN 0x2
+
+#define FB_DACFXCTL_BNLFBYP 0
+#define FM_DACFXCTL_BNLFBYP 0x1
+
+// *** SUBEQFILT ***
+#define FB_SUBEQFILT_EQ2EN 7
+#define FM_SUBEQFILT_EQ2EN 0x80
+#define FV_EQ2EN_ENABLE 0x80
+#define FV_EQ2EN_DISABLE 0x0
+
+#define FB_SUBEQFILT_EQ2BE 4
+#define FM_SUBEQFILT_EQ2BE 0x70
+
+#define FB_SUBEQFILT_EQ1EN 3
+#define FM_SUBEQFILT_EQ1EN 0x8
+#define FV_EQ1EN_ENABLE 0x8
+#define FV_EQ1EN_DISABLE 0x0
+
+#define FB_SUBEQFILT_EQ1BE 0
+#define FM_SUBEQFILT_EQ1BE 0x7
+
+#define SUBEQFILT_EQEN_ENABLE 0x1
+#define SUBEQFILT_EQEN_DISABLE 0x0
+
+// *** SUBCRWDL ***
+#define FB_SUBCRWDL_WDATA_L 0
+#define FM_SUBCRWDL_WDATA_L 0xFF
+
+// *** SUBCRWDM ***
+#define FB_SUBCRWDM_WDATA_M 0
+#define FM_SUBCRWDM_WDATA_M 0xFF
+
+// *** SUBCRWDH ***
+#define FB_SUBCRWDH_WDATA_H 0
+#define FM_SUBCRWDH_WDATA_H 0xFF
+
+// *** SUBCRRDL ***
+#define FB_SUBCRRDL_RDATA_L 0
+#define FM_SUBCRRDL_RDATA_L 0xFF
+
+// *** SUBCRRDM ***
+#define FB_SUBCRRDM_RDATA_M 0
+#define FM_SUBCRRDM_RDATA_M 0xFF
+
+// *** SUBCRRDH ***
+#define FB_SUBCRRDH_RDATA_H 0
+#define FM_SUBCRRDH_RDATA_H 0xFF
+
+// *** SUBCRADD ***
+#define FB_SUBCRADD_ADDRESS 0
+#define FM_SUBCRADD_ADDRESS 0xFF
+
+// *** SUBCRS ***
+#define FB_SUBCRS_ACCSTAT 7
+#define FM_SUBCRS_ACCSTAT 0x80
+
+// *** SUBMBCEN ***
+#define FB_SUBMBCEN_MBCEN3 2
+#define FM_SUBMBCEN_MBCEN3 0x4
+#define FV_MBCEN3_ENABLE 0x4
+#define FV_MBCEN3_DISABLE 0x0
+
+#define FB_SUBMBCEN_MBCEN2 1
+#define FM_SUBMBCEN_MBCEN2 0x2
+#define FV_MBCEN2_ENABLE 0x2
+#define FV_MBCEN2_DISABLE 0x0
+
+#define FB_SUBMBCEN_MBCEN1 0
+#define FM_SUBMBCEN_MBCEN1 0x1
+#define FV_MBCEN1_ENABLE 0x1
+#define FV_MBCEN1_DISABLE 0x0
+
+#define SUBMBCEN_MBCEN_ENABLE 0x1
+#define SUBMBCEN_MBCEN_DISABLE 0x0
+
+// *** SUBMBCCTL ***
+#define FB_SUBMBCCTL_LVLMODE3 5
+#define FM_SUBMBCCTL_LVLMODE3 0x20
+
+#define FB_SUBMBCCTL_WINSEL3 4
+#define FM_SUBMBCCTL_WINSEL3 0x10
+
+#define FB_SUBMBCCTL_LVLMODE2 3
+#define FM_SUBMBCCTL_LVLMODE2 0x8
+
+#define FB_SUBMBCCTL_WINSEL2 2
+#define FM_SUBMBCCTL_WINSEL2 0x4
+
+#define FB_SUBMBCCTL_LVLMODE1 1
+#define FM_SUBMBCCTL_LVLMODE1 0x2
+
+#define FB_SUBMBCCTL_WINSEL1 0
+#define FM_SUBMBCCTL_WINSEL1 0x1
+
+// *** SUBCLECTL ***
+#define FB_SUBCLECTL_LVLMODE 4
+#define FM_SUBCLECTL_LVLMODE 0x10
+
+#define FB_SUBCLECTL_WINSEL 3
+#define FM_SUBCLECTL_WINSEL 0x8
+
+#define FB_SUBCLECTL_EXPEN 2
+#define FM_SUBCLECTL_EXPEN 0x4
+#define FV_EXPEN_ENABLE 0x4
+#define FV_EXPEN_DISABLE 0x0
+
+#define FB_SUBCLECTL_LIMEN 1
+#define FM_SUBCLECTL_LIMEN 0x2
+#define FV_LIMEN_ENABLE 0x2
+#define FV_LIMEN_DISABLE 0x0
+
+#define FB_SUBCLECTL_COMPEN 0
+#define FM_SUBCLECTL_COMPEN 0x1
+#define FV_COMPEN_ENABLE 0x1
+#define FV_COMPEN_DISABLE 0x0
+
+// *** SUBCLEMUG ***
+#define FB_SUBCLEMUG_MUGAIN 0
+#define FM_SUBCLEMUG_MUGAIN 0x1F
+
+// *** SUBCOMPTHR ***
+#define FB_SUBCOMPTHR_THRESH 0
+#define FM_SUBCOMPTHR_THRESH 0xFF
+
+// *** SUBCOMPRAT ***
+#define FB_SUBCOMPRAT_RATIO 0
+#define FM_SUBCOMPRAT_RATIO 0x1F
+
+// *** SUBCOMPATKL ***
+#define FB_SUBCOMPATKL_TCATKL 0
+#define FM_SUBCOMPATKL_TCATKL 0xFF
+
+// *** SUBCOMPATKH ***
+#define FB_SUBCOMPATKH_TCATKH 0
+#define FM_SUBCOMPATKH_TCATKH 0xFF
+
+// *** SUBCOMPRELL ***
+#define FB_SUBCOMPRELL_TCRELL 0
+#define FM_SUBCOMPRELL_TCRELL 0xFF
+
+// *** SUBCOMPRELH ***
+#define FB_SUBCOMPRELH_TCRELH 0
+#define FM_SUBCOMPRELH_TCRELH 0xFF
+
+// *** SUBLIMTHR ***
+#define FB_SUBLIMTHR_THRESH 0
+#define FM_SUBLIMTHR_THRESH 0xFF
+
+// *** SUBLIMTGT ***
+#define FB_SUBLIMTGT_TARGET 0
+#define FM_SUBLIMTGT_TARGET 0xFF
+
+// *** SUBLIMATKL ***
+#define FB_SUBLIMATKL_TCATKL 0
+#define FM_SUBLIMATKL_TCATKL 0xFF
+
+// *** SUBLIMATKH ***
+#define FB_SUBLIMATKH_TCATKH 0
+#define FM_SUBLIMATKH_TCATKH 0xFF
+
+// *** SUBLIMRELL ***
+#define FB_SUBLIMRELL_TCRELL 0
+#define FM_SUBLIMRELL_TCRELL 0xFF
+
+// *** SUBLIMRELH ***
+#define FB_SUBLIMRELH_TCRELH 0
+#define FM_SUBLIMRELH_TCRELH 0xFF
+
+// *** SUBEXPTHR ***
+#define FB_SUBEXPTHR_THRESH 0
+#define FM_SUBEXPTHR_THRESH 0xFF
+
+// *** SUBEXPRAT ***
+#define FB_SUBEXPRAT_RATIO 0
+#define FM_SUBEXPRAT_RATIO 0x7
+
+// *** SUBEXPATKL ***
+#define FB_SUBEXPATKL_TCATKL 0
+#define FM_SUBEXPATKL_TCATKL 0xFF
+
+// *** SUBEXPATKH ***
+#define FB_SUBEXPATKH_TCATKH 0
+#define FM_SUBEXPATKH_TCATKH 0xFF
+
+// *** SUBEXPRELL ***
+#define FB_SUBEXPRELL_TCRELL 0
+#define FM_SUBEXPRELL_TCRELL 0xFF
+
+// *** SUBEXPRELH ***
+#define FB_SUBEXPRELH_TCRELH 0
+#define FM_SUBEXPRELH_TCRELH 0xFF
+
+// *** SUBFXCTL ***
+#define FB_SUBFXCTL_TEEN 3
+#define FM_SUBFXCTL_TEEN 0x8
+
+#define FB_SUBFXCTL_TNLFBYP 2
+#define FM_SUBFXCTL_TNLFBYP 0x4
+
+#define FB_SUBFXCTL_BEEN 1
+#define FM_SUBFXCTL_BEEN 0x2
+
+#define FB_SUBFXCTL_BNLFBYP 0
+#define FM_SUBFXCTL_BNLFBYP 0x1
+
+#endif /* __REDWOODPUBLIC_H__ */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index a5a4e9f75c57..9476cdfd4dde 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1,46 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/twl.h>
+#include <linux/mfd/twl4030-audio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/of.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/initval.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 */
@@ -48,6 +31,14 @@
#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
+struct twl4030_board_params {
+ unsigned int digimic_delay; /* in ms */
+ unsigned int ramp_delay_value;
+ unsigned int offset_cncl_path;
+ unsigned int hs_extmute:1;
+ struct gpio_desc *hs_extmute_gpio;
+};
+
/* codec private data */
struct twl4030_priv {
unsigned int codec_powered;
@@ -72,7 +63,7 @@ struct twl4030_priv {
u8 carkitl_enabled, carkitr_enabled;
u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1];
- struct twl4030_codec_data *pdata;
+ struct twl4030_board_params *board_params;
};
static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030)
@@ -86,9 +77,9 @@ static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030)
}
}
-static unsigned int twl4030_read(struct snd_soc_codec *codec, unsigned int reg)
+static unsigned int twl4030_read(struct snd_soc_component *component, unsigned int reg)
{
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 value = 0;
if (reg >= TWL4030_CACHEREGNUM)
@@ -151,10 +142,10 @@ static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030,
return write_to_reg;
}
-static int twl4030_write(struct snd_soc_codec *codec, unsigned int reg,
+static int twl4030_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
/* Update the ctl cache */
switch (reg) {
@@ -186,9 +177,9 @@ static inline void twl4030_wait_ms(int time)
}
}
-static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
+static void twl4030_codec_enable(struct snd_soc_component *component, int enable)
{
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
int mode;
if (enable == twl4030->codec_powered)
@@ -207,76 +198,70 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
udelay(10);
}
-static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata,
- struct device_node *node)
+static void
+twl4030_get_board_param_values(struct twl4030_board_params *board_params,
+ struct device_node *node)
{
int value;
- of_property_read_u32(node, "ti,digimic_delay",
- &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);
+ of_property_read_u32(node, "ti,digimic_delay", &board_params->digimic_delay);
+ of_property_read_u32(node, "ti,ramp_delay_value", &board_params->ramp_delay_value);
+ of_property_read_u32(node, "ti,offset_cncl_path", &board_params->offset_cncl_path);
if (!of_property_read_u32(node, "ti,hs_extmute", &value))
- pdata->hs_extmute = value;
+ board_params->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;
+ if (of_property_present(node, "ti,hs_extmute_gpio"))
+ board_params->hs_extmute = 1;
}
-static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec)
+static struct twl4030_board_params*
+twl4030_get_board_params(struct snd_soc_component *component)
{
- struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
+ struct twl4030_board_params *board_params = NULL;
struct device_node *twl4030_codec_node = NULL;
- twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node,
+ twl4030_codec_node = of_get_child_by_name(component->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");
+ if (twl4030_codec_node) {
+ board_params = devm_kzalloc(component->dev,
+ sizeof(struct twl4030_board_params),
+ GFP_KERNEL);
+ if (!board_params) {
+ of_node_put(twl4030_codec_node);
return NULL;
}
- twl4030_setup_pdata_of(pdata, twl4030_codec_node);
+ twl4030_get_board_param_values(board_params, twl4030_codec_node);
+ of_node_put(twl4030_codec_node);
}
- return pdata;
+ return board_params;
}
-static void twl4030_init_chip(struct snd_soc_codec *codec)
+static int twl4030_init_chip(struct snd_soc_component *component)
{
- struct twl4030_codec_data *pdata;
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_board_params *board_params;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 reg, byte;
int i = 0;
- pdata = twl4030_get_pdata(codec);
+ board_params = twl4030_get_board_params(component);
- if (pdata && pdata->hs_extmute) {
- if (gpio_is_valid(pdata->hs_extmute_gpio)) {
- int ret;
+ if (board_params && board_params->hs_extmute) {
+ board_params->hs_extmute_gpio = devm_gpiod_get_optional(component->dev,
+ "ti,hs_extmute",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(board_params->hs_extmute_gpio))
+ return dev_err_probe(component->dev, PTR_ERR(board_params->hs_extmute_gpio),
+ "Failed to get hs_extmute GPIO\n");
- 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;
- }
+ if (board_params->hs_extmute_gpio) {
+ gpiod_set_consumer_name(board_params->hs_extmute_gpio, "hs_extmute");
} else {
u8 pin_mux;
+ dev_info(component->dev, "use TWL4030 GPIO6\n");
+
/* Set TWL4030 GPIO6 as EXTMUTE signal */
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
TWL4030_PMBR1_REG);
@@ -291,35 +276,35 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
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_read(component, TWL4030_REG_MISC_SET_1);
+ twl4030_write(component, TWL4030_REG_MISC_SET_1,
reg | TWL4030_SMOOTH_ANAVOL_EN);
- twl4030_write(codec, TWL4030_REG_OPTION,
+ twl4030_write(component, TWL4030_REG_OPTION,
TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
/* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
- twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
+ twl4030_write(component, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
/* Machine dependent setup */
- if (!pdata)
- return;
+ if (!board_params)
+ return 0;
- twl4030->pdata = pdata;
+ twl4030->board_params = board_params;
- reg = twl4030_read(codec, TWL4030_REG_HS_POPN_SET);
+ reg = twl4030_read(component, TWL4030_REG_HS_POPN_SET);
reg &= ~TWL4030_RAMP_DELAY;
- reg |= (pdata->ramp_delay_value << 2);
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, reg);
+ reg |= (board_params->ramp_delay_value << 2);
+ twl4030_write(component, TWL4030_REG_HS_POPN_SET, reg);
/* initiate offset cancellation */
- twl4030_codec_enable(codec, 1);
+ twl4030_codec_enable(component, 1);
- reg = twl4030_read(codec, TWL4030_REG_ANAMICL);
+ reg = twl4030_read(component, TWL4030_REG_ANAMICL);
reg &= ~TWL4030_OFFSET_CNCL_SEL;
- reg |= pdata->offset_cncl_path;
- twl4030_write(codec, TWL4030_REG_ANAMICL,
+ reg |= board_params->offset_cncl_path;
+ twl4030_write(component, TWL4030_REG_ANAMICL,
reg | TWL4030_CNCL_OFFSET_START);
/*
@@ -338,12 +323,14 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
((byte & TWL4030_CNCL_OFFSET_START) ==
TWL4030_CNCL_OFFSET_START));
- twl4030_codec_enable(codec, 0);
+ twl4030_codec_enable(component, 0);
+
+ return 0;
}
-static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
+static void twl4030_apll_enable(struct snd_soc_component *component, int enable)
{
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
if (enable) {
twl4030->apll_enabled++;
@@ -562,17 +549,17 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
* 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) \
+#define TWL4030_OUTPUT_PGA(pin_name, reg) \
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); \
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); \
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); \
\
switch (event) { \
case SND_SOC_DAPM_POST_PMU: \
twl4030->pin_name##_enabled = 1; \
- twl4030_write(codec, reg, twl4030_read(codec, reg)); \
+ twl4030_write(component, reg, twl4030_read(component, reg)); \
break; \
case SND_SOC_DAPM_POST_PMD: \
twl4030->pin_name##_enabled = 0; \
@@ -582,53 +569,53 @@ static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
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);
+TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL);
+TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL);
+TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL);
+TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL);
+TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL);
-static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
+static void handsfree_ramp(struct snd_soc_component *component, int reg, int ramp)
{
unsigned char hs_ctl;
- hs_ctl = twl4030_read(codec, reg);
+ hs_ctl = twl4030_read(component, reg);
if (ramp) {
/* HF ramp-up */
hs_ctl |= TWL4030_HF_CTL_REF_EN;
- twl4030_write(codec, reg, hs_ctl);
+ twl4030_write(component, reg, hs_ctl);
udelay(10);
hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
- twl4030_write(codec, reg, hs_ctl);
+ twl4030_write(component, 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);
+ twl4030_write(component, reg, hs_ctl);
} else {
/* HF ramp-down */
hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN;
hs_ctl &= ~TWL4030_HF_CTL_HB_EN;
- twl4030_write(codec, reg, hs_ctl);
+ twl4030_write(component, reg, hs_ctl);
hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN;
- twl4030_write(codec, reg, hs_ctl);
+ twl4030_write(component, reg, hs_ctl);
udelay(40);
hs_ctl &= ~TWL4030_HF_CTL_REF_EN;
- twl4030_write(codec, reg, hs_ctl);
+ twl4030_write(component, reg, hs_ctl);
}
}
static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- handsfree_ramp(codec, TWL4030_REG_HFL_CTL, 1);
+ handsfree_ramp(component, TWL4030_REG_HFL_CTL, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- handsfree_ramp(codec, TWL4030_REG_HFL_CTL, 0);
+ handsfree_ramp(component, TWL4030_REG_HFL_CTL, 0);
break;
}
return 0;
@@ -637,14 +624,14 @@ static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- handsfree_ramp(codec, TWL4030_REG_HFR_CTL, 1);
+ handsfree_ramp(component, TWL4030_REG_HFR_CTL, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- handsfree_ramp(codec, TWL4030_REG_HFR_CTL, 0);
+ handsfree_ramp(component, TWL4030_REG_HFR_CTL, 0);
break;
}
return 0;
@@ -653,23 +640,23 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
- twl4030_write(codec, TWL4030_REG_VIBRA_SET, 0xff);
+ twl4030_write(component, TWL4030_REG_VIBRA_SET, 0xff);
return 0;
}
static int apll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- twl4030_apll_enable(codec, 1);
+ twl4030_apll_enable(component, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- twl4030_apll_enable(codec, 0);
+ twl4030_apll_enable(component, 0);
break;
}
return 0;
@@ -678,71 +665,73 @@ static int apll_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
u8 audio_if;
- audio_if = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ audio_if = twl4030_read(component, TWL4030_REG_AUDIO_IF);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Enable AIF */
/* enable the PLL before we use it to clock the DAI */
- twl4030_apll_enable(codec, 1);
+ twl4030_apll_enable(component, 1);
- twl4030_write(codec, TWL4030_REG_AUDIO_IF,
+ twl4030_write(component, TWL4030_REG_AUDIO_IF,
audio_if | TWL4030_AIF_EN);
break;
case SND_SOC_DAPM_POST_PMD:
/* disable the DAI before we stop it's source PLL */
- twl4030_write(codec, TWL4030_REG_AUDIO_IF,
+ twl4030_write(component, TWL4030_REG_AUDIO_IF,
audio_if & ~TWL4030_AIF_EN);
- twl4030_apll_enable(codec, 0);
+ twl4030_apll_enable(component, 0);
break;
}
return 0;
}
-static void headset_ramp(struct snd_soc_codec *codec, int ramp)
+static void headset_ramp(struct snd_soc_component *component, 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;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
+ struct twl4030_board_params *board_params = twl4030->board_params;
/* Base values for ramp delay calculation: 2^19 - 2^26 */
- unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
- 8388608, 16777216, 33554432, 67108864};
+ static const 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);
+ hs_gain = twl4030_read(component, TWL4030_REG_HS_GAIN_SET);
+ hs_pop = twl4030_read(component, TWL4030_REG_HS_POPN_SET);
delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
twl4030->sysclk) + 1;
/* Enable external mute control, this dramatically reduces
* the pop-noise */
- if (pdata && pdata->hs_extmute) {
- if (gpio_is_valid(pdata->hs_extmute_gpio)) {
- gpio_set_value(pdata->hs_extmute_gpio, 1);
+ if (board_params && board_params->hs_extmute) {
+ if (board_params->hs_extmute_gpio) {
+ gpiod_set_value(board_params->hs_extmute_gpio, 1);
} else {
hs_pop |= TWL4030_EXTMUTE;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
}
}
if (ramp) {
/* Headset ramp-up according to the TRM */
hs_pop |= TWL4030_VMID_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Actually write to the register */
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain,
TWL4030_REG_HS_GAIN_SET);
hs_pop |= TWL4030_RAMP_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Wait ramp delay time + 1, so the VMID can settle */
twl4030_wait_ms(delay);
} else {
/* Headset ramp-down _not_ according to
* the TRM, but in a way that it is working */
hs_pop &= ~TWL4030_RAMP_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Wait ramp delay time + 1, so the VMID can settle */
twl4030_wait_ms(delay);
/* Bypass the reg_cache to mute the headset */
@@ -750,16 +739,16 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
TWL4030_REG_HS_GAIN_SET);
hs_pop &= ~TWL4030_VMID_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ twl4030_write(component, 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);
+ if (board_params && board_params->hs_extmute) {
+ if (board_params->hs_extmute_gpio) {
+ gpiod_set_value(board_params->hs_extmute_gpio, 0);
} else {
hs_pop &= ~TWL4030_EXTMUTE;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
}
}
}
@@ -767,21 +756,21 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Do the ramp-up only once */
if (!twl4030->hsr_enabled)
- headset_ramp(codec, 1);
+ headset_ramp(component, 1);
twl4030->hsl_enabled = 1;
break;
case SND_SOC_DAPM_POST_PMD:
/* Do the ramp-down only if both headsetL/R is disabled */
if (!twl4030->hsr_enabled)
- headset_ramp(codec, 0);
+ headset_ramp(component, 0);
twl4030->hsl_enabled = 0;
break;
@@ -792,21 +781,21 @@ static int headsetlpga_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Do the ramp-up only once */
if (!twl4030->hsl_enabled)
- headset_ramp(codec, 1);
+ headset_ramp(component, 1);
twl4030->hsr_enabled = 1;
break;
case SND_SOC_DAPM_POST_PMD:
/* Do the ramp-down only if both headsetL/R is disabled */
if (!twl4030->hsl_enabled)
- headset_ramp(codec, 0);
+ headset_ramp(component, 0);
twl4030->hsr_enabled = 0;
break;
@@ -817,12 +806,12 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
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;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
+ struct twl4030_board_params *board_params = twl4030->board_params;
- if (pdata && pdata->digimic_delay)
- twl4030_wait_ms(pdata->digimic_delay);
+ if (board_params && board_params->digimic_delay)
+ twl4030_wait_ms(board_params->digimic_delay);
return 0;
}
@@ -841,7 +830,7 @@ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
@@ -849,14 +838,14 @@ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
int mask = (1 << fls(max)) - 1;
ucontrol->value.integer.value[0] =
- (snd_soc_read(codec, reg) >> shift) & mask;
+ (twl4030_read(component, reg) >> shift) & mask;
if (ucontrol->value.integer.value[0])
ucontrol->value.integer.value[0] =
max + 1 - ucontrol->value.integer.value[0];
if (shift != rshift) {
ucontrol->value.integer.value[1] =
- (snd_soc_read(codec, reg) >> rshift) & mask;
+ (twl4030_read(component, reg) >> rshift) & mask;
if (ucontrol->value.integer.value[1])
ucontrol->value.integer.value[1] =
max + 1 - ucontrol->value.integer.value[1];
@@ -870,7 +859,7 @@ static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
@@ -891,7 +880,7 @@ static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
val2 = max + 1 - val2;
val |= val2 << rshift;
}
- return snd_soc_update_bits(codec, reg, val_mask, val);
+ return snd_soc_component_update_bits(component, reg, val_mask, val);
}
static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
@@ -899,7 +888,7 @@ static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
@@ -907,9 +896,9 @@ static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
int mask = (1<<fls(max))-1;
ucontrol->value.integer.value[0] =
- (snd_soc_read(codec, reg) >> shift) & mask;
+ (twl4030_read(component, reg) >> shift) & mask;
ucontrol->value.integer.value[1] =
- (snd_soc_read(codec, reg2) >> shift) & mask;
+ (twl4030_read(component, reg2) >> shift) & mask;
if (ucontrol->value.integer.value[0])
ucontrol->value.integer.value[0] =
@@ -926,7 +915,7 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
@@ -947,11 +936,11 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- err = snd_soc_update_bits(codec, reg, val_mask, val);
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ err = snd_soc_component_update_bits(component, reg2, val_mask, val2);
return err;
}
@@ -967,11 +956,11 @@ static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
if (twl4030->configured) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"operation mode cannot be changed on-the-fly\n");
return -EBUSY;
}
@@ -1578,20 +1567,22 @@ static const struct snd_soc_dapm_route intercon[] = {
};
-static int twl4030_set_bias_level(struct snd_soc_codec *codec,
+static int twl4030_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
- twl4030_codec_enable(codec, 1);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
+ twl4030_codec_enable(component, 1);
break;
case SND_SOC_BIAS_OFF:
- twl4030_codec_enable(codec, 0);
+ twl4030_codec_enable(component, 0);
break;
}
@@ -1627,12 +1618,12 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
/* 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,
+static void twl4030_tdm_enable(struct snd_soc_component *component, int direction,
int enable)
{
u8 reg, mask;
- reg = twl4030_read(codec, TWL4030_REG_OPTION);
+ reg = twl4030_read(component, TWL4030_REG_OPTION);
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
@@ -1644,14 +1635,14 @@ static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
else
reg &= ~mask;
- twl4030_write(codec, TWL4030_REG_OPTION, reg);
+ twl4030_write(component, 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);
+ struct snd_soc_component *component = dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
if (twl4030->master_substream) {
twl4030->slave_substream = substream;
@@ -1661,7 +1652,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
if (twl4030->configured)
twl4030_constraints(twl4030, twl4030->master_substream);
} else {
- if (!(twl4030_read(codec, TWL4030_REG_CODEC_MODE) &
+ if (!(twl4030_read(component, TWL4030_REG_CODEC_MODE) &
TWL4030_OPTION_1)) {
/* In option2 4 channel is not supported, set the
* constraint for the first stream for channels, the
@@ -1679,8 +1670,8 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
if (twl4030->master_substream == substream)
twl4030->master_substream = twl4030->slave_substream;
@@ -1696,27 +1687,27 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
/* If the closing substream had 4 channel, do the necessary cleanup */
if (substream->runtime->channels == 4)
- twl4030_tdm_enable(codec, substream->stream, 0);
+ twl4030_tdm_enable(component, substream->stream, 0);
}
static int twl4030_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 mode, old_mode, format, old_format;
/* If the substream has 4 channel, do the necessary setup */
if (params_channels(params) == 4) {
- format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
- mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE);
+ format = twl4030_read(component, TWL4030_REG_AUDIO_IF);
+ mode = twl4030_read(component, TWL4030_REG_CODEC_MODE);
/* Safety check: are we in the correct operating mode and
* the interface is in TDM mode? */
if ((mode & TWL4030_OPTION_1) &&
((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM))
- twl4030_tdm_enable(codec, substream->stream, 1);
+ twl4030_tdm_enable(component, substream->stream, 1);
else
return -EINVAL;
}
@@ -1726,7 +1717,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
return 0;
/* bit rate */
- old_mode = twl4030_read(codec,
+ old_mode = twl4030_read(component,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
mode = old_mode & ~TWL4030_APLL_RATE;
@@ -1762,13 +1753,13 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
mode |= TWL4030_APLL_RATE_96000;
break;
default:
- dev_err(codec->dev, "%s: unknown rate %d\n", __func__,
+ dev_err(component->dev, "%s: unknown rate %d\n", __func__,
params_rate(params));
return -EINVAL;
}
/* sample size */
- old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ old_format = twl4030_read(component, TWL4030_REG_AUDIO_IF);
format = old_format;
format &= ~TWL4030_DATA_WIDTH;
switch (params_width(params)) {
@@ -1779,7 +1770,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
format |= TWL4030_DATA_WIDTH_32S_24W;
break;
default:
- dev_err(codec->dev, "%s: unsupported bits/sample %d\n",
+ dev_err(component->dev, "%s: unsupported bits/sample %d\n",
__func__, params_width(params));
return -EINVAL;
}
@@ -1790,13 +1781,13 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
* 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);
+ twl4030_codec_enable(component, 0);
+ twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
+ twl4030_codec_enable(component, 1);
} else {
- twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
- twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
}
}
@@ -1820,8 +1811,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 19200000:
@@ -1829,12 +1820,12 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
case 38400000:
break;
default:
- dev_err(codec->dev, "Unsupported HFCLKIN: %u\n", freq);
+ dev_err(component->dev, "Unsupported HFCLKIN: %u\n", freq);
return -EINVAL;
}
if ((freq / 1000) != twl4030->sysclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Mismatch in HFCLKIN: %u (configured: %u)\n",
freq, twl4030->sysclk * 1000);
return -EINVAL;
@@ -1845,21 +1836,20 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 old_format, format;
/* get format */
- old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ old_format = twl4030_read(component, 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:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
format &= ~(TWL4030_AIF_SLAVE_EN);
format &= ~(TWL4030_CLK256FS_EN);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
format |= TWL4030_AIF_SLAVE_EN;
format |= TWL4030_CLK256FS_EN;
break;
@@ -1886,11 +1876,11 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
* 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);
+ twl4030_codec_enable(component, 0);
+ twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
+ twl4030_codec_enable(component, 1);
} else {
- twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
}
}
@@ -1899,25 +1889,25 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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);
+ struct snd_soc_component *component = dai->component;
+ u8 reg = twl4030_read(component, TWL4030_REG_AUDIO_IF);
if (tristate)
reg |= TWL4030_AIF_TRI_EN;
else
reg &= ~TWL4030_AIF_TRI_EN;
- return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg);
+ return twl4030_write(component, TWL4030_REG_AUDIO_IF, reg);
}
/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
* (VTXL, VTXR) for uplink has to be enabled/disabled. */
-static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
+static void twl4030_voice_enable(struct snd_soc_component *component, int direction,
int enable)
{
u8 reg, mask;
- reg = twl4030_read(codec, TWL4030_REG_OPTION);
+ reg = twl4030_read(component, TWL4030_REG_OPTION);
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
mask = TWL4030_ARXL1_VRX_EN;
@@ -1929,21 +1919,21 @@ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
else
reg &= ~mask;
- twl4030_write(codec, TWL4030_REG_OPTION, reg);
+ twl4030_write(component, TWL4030_REG_OPTION, reg);
}
static int twl4030_voice_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 mode;
/* If the system master clock is not 26MHz, the voice PCM interface is
* not available.
*/
if (twl4030->sysclk != 26000) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n",
__func__, twl4030->sysclk);
return -EINVAL;
@@ -1952,11 +1942,11 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
/* If the codec mode is not option2, the voice PCM interface is not
* available.
*/
- mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE)
+ mode = twl4030_read(component, TWL4030_REG_CODEC_MODE)
& TWL4030_OPT_MODE;
if (mode != TWL4030_OPTION_2) {
- dev_err(codec->dev, "%s: the codec mode is not option2\n",
+ dev_err(component->dev, "%s: the codec mode is not option2\n",
__func__);
return -EINVAL;
}
@@ -1967,25 +1957,25 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
/* Enable voice digital filters */
- twl4030_voice_enable(codec, substream->stream, 0);
+ twl4030_voice_enable(component, substream->stream, 0);
}
static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 old_mode, mode;
/* Enable voice digital filters */
- twl4030_voice_enable(codec, substream->stream, 1);
+ twl4030_voice_enable(component, substream->stream, 1);
/* bit rate */
- old_mode = twl4030_read(codec,
+ old_mode = twl4030_read(component,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
mode = old_mode;
@@ -1997,7 +1987,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
mode |= TWL4030_SEL_16K;
break;
default:
- dev_err(codec->dev, "%s: unknown rate %d\n", __func__,
+ dev_err(component->dev, "%s: unknown rate %d\n", __func__,
params_rate(params));
return -EINVAL;
}
@@ -2008,11 +1998,11 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
* 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);
+ twl4030_codec_enable(component, 0);
+ twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_codec_enable(component, 1);
} else {
- twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
}
}
@@ -2022,17 +2012,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
if (freq != 26000000) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n",
__func__, freq / 1000);
return -EINVAL;
}
if ((freq / 1000) != twl4030->sysclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Mismatch in HFCLKIN: %u (configured: %u)\n",
freq, twl4030->sysclk * 1000);
return -EINVAL;
@@ -2043,20 +2033,19 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
u8 old_format, format;
/* get format */
- old_format = twl4030_read(codec, TWL4030_REG_VOICE_IF);
+ old_format = twl4030_read(component, 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:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
format &= ~(TWL4030_VIF_SLAVE_EN);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
format |= TWL4030_VIF_SLAVE_EN;
break;
default:
@@ -2081,11 +2070,11 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
* 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);
+ twl4030_codec_enable(component, 0);
+ twl4030_write(component, TWL4030_REG_VOICE_IF, format);
+ twl4030_codec_enable(component, 1);
} else {
- twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+ twl4030_write(component, TWL4030_REG_VOICE_IF, format);
}
}
@@ -2094,15 +2083,15 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = dai->component;
+ u8 reg = twl4030_read(component, TWL4030_REG_VOICE_IF);
if (tristate)
reg |= TWL4030_VIF_TRI_EN;
else
reg &= ~TWL4030_VIF_TRI_EN;
- return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg);
+ return twl4030_write(component, TWL4030_REG_VOICE_IF, reg);
}
#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
@@ -2163,67 +2152,47 @@ static struct snd_soc_dai_driver twl4030_dai[] = {
},
};
-static int twl4030_soc_probe(struct snd_soc_codec *codec)
+static int twl4030_soc_probe(struct snd_soc_component *component)
{
struct twl4030_priv *twl4030;
- twl4030 = devm_kzalloc(codec->dev, sizeof(struct twl4030_priv),
+ twl4030 = devm_kzalloc(component->dev, sizeof(struct twl4030_priv),
GFP_KERNEL);
if (!twl4030)
return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, twl4030);
+ snd_soc_component_set_drvdata(component, twl4030);
/* Set the defaults, and power up the codec */
twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
- twl4030_init_chip(codec);
-
- return 0;
+ return twl4030_init_chip(component);
}
-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 const struct snd_soc_component_driver soc_component_dev_twl4030 = {
+ .probe = twl4030_soc_probe,
+ .read = twl4030_read,
+ .write = twl4030_write,
+ .set_bias_level = twl4030_set_bias_level,
+ .controls = twl4030_snd_controls,
+ .num_controls = ARRAY_SIZE(twl4030_snd_controls),
+ .dapm_widgets = twl4030_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int twl4030_codec_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_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",
},
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index bc3de2e844e6..e10c51092a35 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -76,7 +62,7 @@ struct twl6040_data {
unsigned int clk_in;
unsigned int sysclk;
struct twl6040_jack_data hs_jack;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct mutex mutex;
};
@@ -106,10 +92,12 @@ static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
{ .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
};
-static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
+#define to_twl6040(component) dev_get_drvdata((component)->dev->parent)
+
+static unsigned int twl6040_read(struct snd_soc_component *component, unsigned int reg)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
+ struct twl6040 *twl6040 = to_twl6040(component);
u8 value;
if (reg >= TWL6040_CACHEREGNUM)
@@ -131,10 +119,10 @@ static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
return value;
}
-static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec,
+static bool twl6040_can_write_to_chip(struct snd_soc_component *component,
unsigned int reg)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
switch (reg) {
case TWL6040_REG_HSLCTL:
@@ -146,14 +134,14 @@ static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec,
case TWL6040_REG_HFRCTL:
return priv->dl2_unmuted;
default:
- return 1;
+ return true;
}
}
-static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
+static inline void twl6040_update_dl12_cache(struct snd_soc_component *component,
u8 reg, u8 value)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
switch (reg) {
case TWL6040_REG_HSLCTL:
@@ -168,54 +156,54 @@ static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
}
}
-static int twl6040_write(struct snd_soc_codec *codec,
+static int twl6040_write(struct snd_soc_component *component,
unsigned int reg, unsigned int value)
{
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(component);
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
- twl6040_update_dl12_cache(codec, reg, value);
- if (twl6040_can_write_to_chip(codec, reg))
+ twl6040_update_dl12_cache(component, reg, value);
+ if (twl6040_can_write_to_chip(component, reg))
return twl6040_reg_write(twl6040, reg, value);
else
return 0;
}
-static void twl6040_init_chip(struct snd_soc_codec *codec)
+static void twl6040_init_chip(struct snd_soc_component *component)
{
- 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);
+ twl6040_read(component, TWL6040_REG_TRIM1);
+ twl6040_read(component, TWL6040_REG_TRIM2);
+ twl6040_read(component, TWL6040_REG_TRIM3);
+ twl6040_read(component, TWL6040_REG_HSOTRIM);
+ twl6040_read(component, TWL6040_REG_HFOTRIM);
/* Change chip defaults */
/* No imput selected for microphone amplifiers */
- twl6040_write(codec, TWL6040_REG_MICLCTL, 0x18);
- twl6040_write(codec, TWL6040_REG_MICRCTL, 0x18);
+ twl6040_write(component, TWL6040_REG_MICLCTL, 0x18);
+ twl6040_write(component, TWL6040_REG_MICRCTL, 0x18);
/*
* We need to lower the default gain values, so the ramp code
* can work correctly for the first playback.
* This reduces the pop noise heard at the first playback.
*/
- twl6040_write(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);
+ twl6040_write(component, TWL6040_REG_HSGAIN, 0xff);
+ twl6040_write(component, TWL6040_REG_EARCTL, 0x1e);
+ twl6040_write(component, TWL6040_REG_HFLGAIN, 0x1d);
+ twl6040_write(component, TWL6040_REG_HFRGAIN, 0x1d);
+ twl6040_write(component, TWL6040_REG_LINEGAIN, 0);
}
/* set headset dac and driver power mode */
-static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
+static int headset_power_mode(struct snd_soc_component *component, int high_perf)
{
int hslctl, hsrctl;
int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE;
- hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
- hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
+ hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
if (high_perf) {
hslctl &= ~mask;
@@ -225,8 +213,8 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
hsrctl |= mask;
}
- twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
- twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
+ twl6040_write(component, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl);
return 0;
}
@@ -234,7 +222,7 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
u8 hslctl, hsrctl;
/*
@@ -242,8 +230,8 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
* 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);
+ hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
if (SND_SOC_DAPM_EVENT_ON(event)) {
hslctl |= TWL6040_HSDACENA;
hsrctl |= TWL6040_HSDACENA;
@@ -251,8 +239,8 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
hslctl &= ~TWL6040_HSDACENA;
hsrctl &= ~TWL6040_HSDACENA;
}
- twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
- twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
+ twl6040_write(component, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl);
msleep(1);
return 0;
@@ -261,17 +249,17 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
if (SND_SOC_DAPM_EVENT_ON(event)) {
/* Earphone doesn't support low power mode */
priv->hs_power_mode_locked = 1;
- ret = headset_power_mode(codec, 1);
+ ret = headset_power_mode(component, 1);
} else {
priv->hs_power_mode_locked = 0;
- ret = headset_power_mode(codec, priv->hs_power_mode);
+ ret = headset_power_mode(component, priv->hs_power_mode);
}
msleep(1);
@@ -279,16 +267,16 @@ static int twl6040_ep_drv_event(struct snd_soc_dapm_widget *w,
return ret;
}
-static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
+static void twl6040_hs_jack_report(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int status;
mutex_lock(&priv->mutex);
/* Sync status */
- status = twl6040_read(codec, TWL6040_REG_STATUS);
+ status = twl6040_read(component, TWL6040_REG_STATUS);
if (status & TWL6040_PLUGCOMP)
snd_soc_jack_report(jack, report, report);
else
@@ -297,16 +285,16 @@ static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
mutex_unlock(&priv->mutex);
}
-void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
+void twl6040_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
struct twl6040_jack_data *hs_jack = &priv->hs_jack;
hs_jack->jack = jack;
hs_jack->report = report;
- twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
+ twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report);
}
EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect);
@@ -314,17 +302,17 @@ 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 snd_soc_component *component = priv->component;
struct twl6040_jack_data *hs_jack = &priv->hs_jack;
- twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
+ twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report);
}
/* audio interrupt handler */
static irqreturn_t twl6040_audio_handler(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = data;
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
queue_delayed_work(system_power_efficient_wq,
&priv->hs_jack.work, msecs_to_jiffies(200));
@@ -335,12 +323,12 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
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 snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
/* Do not allow changes while Input/FF efect is running */
- val = twl6040_read(codec, e->reg);
+ val = twl6040_read(component, e->reg);
if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL))
return -EBUSY;
@@ -484,8 +472,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
@@ -495,13 +483,13 @@ static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int high_perf = ucontrol->value.enumerated.item[0];
int ret = 0;
if (!priv->hs_power_mode_locked)
- ret = headset_power_mode(codec, high_perf);
+ ret = headset_power_mode(component, high_perf);
if (!ret)
priv->hs_power_mode = high_perf;
@@ -512,8 +500,8 @@ static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
@@ -523,17 +511,17 @@ static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
priv->pll_power_mode = ucontrol->value.enumerated.item[0];
return 0;
}
-int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
+int twl6040_get_dl1_gain(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
if (snd_soc_dapm_get_pin_status(dapm, "EP"))
return -1; /* -1dB */
@@ -541,7 +529,7 @@ int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
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);
+ u8 val = twl6040_read(component, TWL6040_REG_HSLCTL);
if (val & TWL6040_HSDACMODE)
/* HSDACL in LP mode */
return -8; /* -8dB */
@@ -553,26 +541,26 @@ int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(twl6040_get_dl1_gain);
-int twl6040_get_clk_id(struct snd_soc_codec *codec)
+int twl6040_get_clk_id(struct snd_soc_component *component)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
return priv->pll_power_mode;
}
EXPORT_SYMBOL_GPL(twl6040_get_clk_id);
-int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim)
+int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim)
{
if (unlikely(trim >= TWL6040_TRIM_INVAL))
return -EINVAL;
- return twl6040_read(codec, TWL6040_REG_TRIM1 + trim);
+ return twl6040_read(component, TWL6040_REG_TRIM1 + trim);
}
EXPORT_SYMBOL_GPL(twl6040_get_trim_value);
-int twl6040_get_hs_step_size(struct snd_soc_codec *codec)
+int twl6040_get_hs_step_size(struct snd_soc_component *component)
{
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(component);
if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3)
/* For ES under ES_1.3 HS step is 2 mV */
@@ -606,6 +594,14 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
twl6040_headset_power_get_enum,
twl6040_headset_power_put_enum),
+ /* Left HS PDM data routed to Right HSDAC */
+ SOC_SINGLE("Headset Mono to Stereo Playback Switch",
+ TWL6040_REG_HSRCTL, 7, 1, 0),
+
+ /* Left HF PDM data routed to Right HFDAC */
+ SOC_SINGLE("Handsfree Mono to Stereo Playback Switch",
+ TWL6040_REG_HFRCTL, 5, 1, 0),
+
SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
twl6040_pll_get_enum, twl6040_pll_put_enum),
};
@@ -819,12 +815,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VIBRAR", NULL, "Vibra Right Driver"},
};
-static int twl6040_set_bias_level(struct snd_soc_codec *codec,
+static int twl6040_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct twl6040 *twl6040 = codec->control_data;
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- int ret;
+ struct twl6040 *twl6040 = to_twl6040(component);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
+ int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -832,17 +828,21 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (priv->codec_powered)
+ if (priv->codec_powered) {
+ /* Select low power PLL in standby */
+ ret = twl6040_set_pll(twl6040, TWL6040_SYSCLK_SEL_LPPLL,
+ 32768, 19200000);
break;
+ }
ret = twl6040_power(twl6040, 1);
if (ret)
- return ret;
+ break;
priv->codec_powered = 1;
/* Set external boost GPO */
- twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
+ twl6040_write(component, TWL6040_REG_GPOCTL, 0x02);
break;
case SND_SOC_BIAS_OFF:
if (!priv->codec_powered)
@@ -853,14 +853,14 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
break;
}
- return 0;
+ return ret;
}
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);
+ struct snd_soc_component *component = dai->component;
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -873,8 +873,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int rate;
rate = params_rate(params);
@@ -885,7 +885,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
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",
+ dev_err(component->dev, "HPPLL does not support rate %d\n",
rate);
return -EINVAL;
}
@@ -899,7 +899,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
priv->sysclk = 19200000;
break;
default:
- dev_err(codec->dev, "unsupported rate %d\n", rate);
+ dev_err(component->dev, "unsupported rate %d\n", rate);
return -EINVAL;
}
@@ -909,20 +909,20 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct twl6040 *twl6040 = to_twl6040(component);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int ret;
if (!priv->sysclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"no mclk configured, call set_sysclk() on init\n");
return -EINVAL;
}
ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk);
if (ret) {
- dev_err(codec->dev, "Can not set PLL (%d)\n", ret);
+ dev_err(component->dev, "Can not set PLL (%d)\n", ret);
return -EPERM;
}
@@ -932,8 +932,8 @@ static int twl6040_prepare(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case TWL6040_SYSCLK_SEL_LPPLL:
@@ -942,26 +942,26 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
priv->clk_in = freq;
break;
default:
- dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
+ dev_err(component->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,
+static void twl6040_mute_path(struct snd_soc_component *component, enum twl6040_dai_id id,
int mute)
{
- struct twl6040 *twl6040 = codec->control_data;
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040 *twl6040 = to_twl6040(component);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int hslctl, hsrctl, earctl;
int hflctl, hfrctl;
switch (id) {
case TWL6040_DAI_DL1:
- hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
- hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
- earctl = twl6040_read(codec, TWL6040_REG_EARCTL);
+ hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
+ earctl = twl6040_read(component, TWL6040_REG_EARCTL);
if (mute) {
/* Power down drivers and DACs */
@@ -977,15 +977,15 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
priv->dl1_unmuted = !mute;
break;
case TWL6040_DAI_DL2:
- hflctl = twl6040_read(codec, TWL6040_REG_HFLCTL);
- hfrctl = twl6040_read(codec, TWL6040_REG_HFRCTL);
+ hflctl = twl6040_read(component, TWL6040_REG_HFLCTL);
+ hfrctl = twl6040_read(component, TWL6040_REG_HFRCTL);
if (mute) {
/* Power down drivers and DACs */
hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
- TWL6040_HFDRVENA);
+ TWL6040_HFDRVENA | TWL6040_HFSWENA);
hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
- TWL6040_HFDRVENA);
+ TWL6040_HFDRVENA | TWL6040_HFSWENA);
}
twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
@@ -997,16 +997,16 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
}
}
-static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
+static int twl6040_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
{
switch (dai->id) {
case TWL6040_DAI_LEGACY:
- twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
- twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
+ twl6040_mute_path(dai->component, TWL6040_DAI_DL1, mute);
+ twl6040_mute_path(dai->component, TWL6040_DAI_DL2, mute);
break;
case TWL6040_DAI_DL1:
case TWL6040_DAI_DL2:
- twl6040_mute_path(dai->codec, dai->id, mute);
+ twl6040_mute_path(dai->component, dai->id, mute);
break;
default:
break;
@@ -1020,7 +1020,8 @@ static const struct snd_soc_dai_ops twl6040_dai_ops = {
.hw_params = twl6040_hw_params,
.prepare = twl6040_prepare,
.set_sysclk = twl6040_set_dai_sysclk,
- .digital_mute = twl6040_digital_mute,
+ .mute_stream = twl6040_mute_stream,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver twl6040_dai[] = {
@@ -1093,27 +1094,24 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
};
-static int twl6040_probe(struct snd_soc_codec *codec)
+static int twl6040_probe(struct snd_soc_component *component)
{
struct twl6040_data *priv;
- struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent);
- struct platform_device *pdev = to_platform_device(codec->dev);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct platform_device *pdev = to_platform_device(component->dev);
int ret = 0;
- priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(component->dev, sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, priv);
+ snd_soc_component_set_drvdata(component, priv);
- priv->codec = codec;
- codec->control_data = twl6040;
+ priv->component = component;
priv->plug_irq = platform_get_irq(pdev, 0);
- if (priv->plug_irq < 0) {
- dev_err(codec->dev, "invalid irq\n");
- return -EINVAL;
- }
+ if (priv->plug_irq < 0)
+ return priv->plug_irq;
INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
@@ -1122,62 +1120,54 @@ static int twl6040_probe(struct snd_soc_codec *codec)
ret = request_threaded_irq(priv->plug_irq, NULL,
twl6040_audio_handler,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
- "twl6040_irq_plug", codec);
+ "twl6040_irq_plug", component);
if (ret) {
- dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
+ dev_err(component->dev, "PLUG IRQ request failed: %d\n", ret);
return ret;
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
- twl6040_init_chip(codec);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
+ twl6040_init_chip(component);
return 0;
}
-static int twl6040_remove(struct snd_soc_codec *codec)
+static void twl6040_remove(struct snd_soc_component *component)
{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-
- free_irq(priv->plug_irq, codec);
+ struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
- return 0;
+ free_irq(priv->plug_irq, component);
}
-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 const struct snd_soc_component_driver soc_component_dev_twl6040 = {
+ .probe = twl6040_probe,
+ .remove = twl6040_remove,
+ .read = twl6040_read,
+ .write = twl6040_write,
+ .set_bias_level = twl6040_set_bias_level,
+ .controls = twl6040_snd_controls,
+ .num_controls = ARRAY_SIZE(twl6040_snd_controls),
+ .dapm_widgets = twl6040_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .endianness = 1,
};
static int twl6040_codec_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_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);
diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h
index 0611406ca7c0..f4f4b14cc0c4 100644
--- a/sound/soc/codecs/twl6040.h
+++ b/sound/soc/codecs/twl6040.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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__
@@ -34,11 +20,11 @@ enum twl6040_trim {
#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,
+int twl6040_get_dl1_gain(struct snd_soc_component *component);
+void twl6040_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report);
-int twl6040_get_clk_id(struct snd_soc_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);
+int twl6040_get_clk_id(struct snd_soc_component *component);
+int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim);
+int twl6040_get_hs_step_size(struct snd_soc_component *component);
#endif /* End of __TWL6040_H__ */
diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c
new file mode 100644
index 000000000000..f799772ff747
--- /dev/null
+++ b/sound/soc/codecs/uda1334.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// uda1334.c -- UDA1334 ALSA SoC Audio driver
+//
+// Based on WM8523 ALSA SoC Audio driver written by Mark Brown
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#define UDA1334_NUM_RATES 6
+
+/* codec private data */
+struct uda1334_priv {
+ struct gpio_desc *mute;
+ struct gpio_desc *deemph;
+ unsigned int sysclk;
+ unsigned int rate_constraint_list[UDA1334_NUM_RATES];
+ struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+static const struct snd_soc_dapm_widget uda1334_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda1334_dapm_routes[] = {
+ { "LINEVOUTL", NULL, "DAC" },
+ { "LINEVOUTR", NULL, "DAC" },
+};
+
+static int uda1334_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);
+ int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(uda1334->deemph, deemph);
+
+ return 0;
+};
+
+static int uda1334_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = gpiod_get_value_cansleep(uda1334->deemph);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = ret;
+
+ return 0;
+};
+
+static const struct snd_kcontrol_new uda1334_snd_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ uda1334_get_deemph, uda1334_put_deemph),
+};
+
+static const struct {
+ int value;
+ int ratio;
+} lrclk_ratios[UDA1334_NUM_RATES] = {
+ { 1, 128 },
+ { 2, 192 },
+ { 3, 256 },
+ { 4, 384 },
+ { 5, 512 },
+ { 6, 768 },
+};
+
+static int uda1334_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC - enforce this.
+ */
+ if (!uda1334->sysclk) {
+ dev_err(component->dev,
+ "No MCLK configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &uda1334->rate_constraint);
+
+ gpiod_set_value_cansleep(uda1334->mute, 1);
+
+ return 0;
+}
+
+static void uda1334_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);
+
+ gpiod_set_value_cansleep(uda1334->mute, 0);
+}
+
+static int uda1334_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int i, j = 0;
+
+ uda1334->sysclk = freq;
+
+ uda1334->rate_constraint.count = 0;
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ val = freq / lrclk_ratios[i].ratio;
+ /*
+ * Check that it's a standard rate since core can't
+ * cope with others and having the odd rates confuses
+ * constraint matching.
+ */
+
+ switch (val) {
+ case 8000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ dev_dbg(component->dev, "Supported sample rate: %dHz\n",
+ val);
+ uda1334->rate_constraint_list[j++] = val;
+ uda1334->rate_constraint.count++;
+ break;
+ default:
+ dev_dbg(component->dev, "Skipping sample rate: %dHz\n",
+ val);
+ }
+ }
+
+ /* Need at least one supported rate... */
+ if (uda1334->rate_constraint.count == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int uda1334_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
+ SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+
+ if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC)) {
+ dev_err(codec_dai->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int uda1334_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(dai->component);
+
+ if (uda1334->mute)
+ gpiod_set_value_cansleep(uda1334->mute, mute);
+
+ return 0;
+}
+
+#define UDA1334_RATES SNDRV_PCM_RATE_8000_96000
+
+#define UDA1334_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops uda1334_dai_ops = {
+ .startup = uda1334_startup,
+ .shutdown = uda1334_shutdown,
+ .set_sysclk = uda1334_set_dai_sysclk,
+ .set_fmt = uda1334_set_fmt,
+ .mute_stream = uda1334_mute_stream,
+};
+
+static struct snd_soc_dai_driver uda1334_dai = {
+ .name = "uda1334-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = UDA1334_RATES,
+ .formats = UDA1334_FORMATS,
+ },
+ .ops = &uda1334_dai_ops,
+};
+
+static int uda1334_probe(struct snd_soc_component *component)
+{
+ struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component);
+
+ uda1334->rate_constraint.list = &uda1334->rate_constraint_list[0];
+ uda1334->rate_constraint.count =
+ ARRAY_SIZE(uda1334->rate_constraint_list);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_uda1334 = {
+ .probe = uda1334_probe,
+ .controls = uda1334_snd_controls,
+ .num_controls = ARRAY_SIZE(uda1334_snd_controls),
+ .dapm_widgets = uda1334_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda1334_dapm_widgets),
+ .dapm_routes = uda1334_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda1334_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct of_device_id uda1334_of_match[] = {
+ { .compatible = "nxp,uda1334" },
+ { /* sentinel*/ }
+};
+MODULE_DEVICE_TABLE(of, uda1334_of_match);
+
+static int uda1334_codec_probe(struct platform_device *pdev)
+{
+ struct uda1334_priv *uda1334;
+ int ret;
+
+ uda1334 = devm_kzalloc(&pdev->dev, sizeof(struct uda1334_priv),
+ GFP_KERNEL);
+ if (!uda1334)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, uda1334);
+
+ uda1334->mute = devm_gpiod_get(&pdev->dev, "nxp,mute", GPIOD_OUT_LOW);
+ if (IS_ERR(uda1334->mute)) {
+ ret = PTR_ERR(uda1334->mute);
+ dev_err(&pdev->dev, "Failed to get mute line: %d\n", ret);
+ return ret;
+ }
+
+ uda1334->deemph = devm_gpiod_get(&pdev->dev, "nxp,deemph", GPIOD_OUT_LOW);
+ if (IS_ERR(uda1334->deemph)) {
+ ret = PTR_ERR(uda1334->deemph);
+ dev_err(&pdev->dev, "Failed to get deemph line: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_uda1334,
+ &uda1334_dai, 1);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+
+ return ret;
+}
+
+static struct platform_driver uda1334_codec_driver = {
+ .probe = uda1334_codec_probe,
+ .driver = {
+ .name = "uda1334-codec",
+ .of_match_table = uda1334_of_match,
+ },
+};
+module_platform_driver(uda1334_codec_driver);
+
+MODULE_DESCRIPTION("ASoC UDA1334 driver");
+MODULE_AUTHOR("Andra Danciu <andradanciu1997@gmail.com>");
+MODULE_ALIAS("platform:uda1334-codec");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c
new file mode 100644
index 000000000000..b0b29012842d
--- /dev/null
+++ b/sound/soc/codecs/uda1342.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// uda1342.c -- UDA1342 ALSA SoC Codec driver
+// Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
+//
+// Copyright 2007 Dension Audio Systems Ltd.
+// Copyright 2024 Loongson Technology Co.,Ltd.
+//
+// Modifications by Christian Pellegrin <chripell@evolware.org>
+// Further cleanup and restructuring by:
+// Binbin Zhou <zhoubinbin@loongson.cn>
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "uda1342.h"
+
+#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+struct uda1342_priv {
+ int sysclk;
+ int dai_fmt;
+
+ struct snd_pcm_substream *provider_substream;
+ struct snd_pcm_substream *consumer_substream;
+
+ struct regmap *regmap;
+ struct i2c_client *i2c;
+};
+
+static const struct reg_default uda1342_reg_defaults[] = {
+ { 0x00, 0x1042 },
+ { 0x01, 0x0000 },
+ { 0x10, 0x0088 },
+ { 0x11, 0x0000 },
+ { 0x12, 0x0000 },
+ { 0x20, 0x0080 },
+ { 0x21, 0x0080 },
+};
+
+static int uda1342_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+ unsigned int mask;
+ unsigned int val = 0;
+
+ /* Master mute */
+ mask = BIT(5);
+ if (mute)
+ val = mask;
+
+ return regmap_update_bits(uda1342->regmap, 0x10, mask, val);
+}
+
+static int uda1342_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *provider_runtime;
+
+ if (uda1342->provider_substream) {
+ provider_runtime = uda1342->provider_substream->runtime;
+
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, provider_runtime->rate);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ provider_runtime->sample_bits);
+
+ uda1342->consumer_substream = substream;
+ } else {
+ uda1342->provider_substream = substream;
+ }
+
+ return 0;
+}
+
+static void uda1342_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+
+ if (uda1342->provider_substream == substream)
+ uda1342->provider_substream = uda1342->consumer_substream;
+
+ uda1342->consumer_substream = NULL;
+}
+
+static int uda1342_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+ struct device *dev = &uda1342->i2c->dev;
+ unsigned int hw_params = 0;
+
+ if (substream == uda1342->consumer_substream)
+ return 0;
+
+ /* set SYSCLK / fs ratio */
+ switch (uda1342->sysclk / params_rate(params)) {
+ case 512:
+ break;
+ case 384:
+ hw_params |= BIT(4);
+ break;
+ case 256:
+ hw_params |= BIT(5);
+ break;
+ default:
+ dev_err(dev, "unsupported frequency\n");
+ return -EINVAL;
+ }
+
+ /* set DAI format and word length */
+ switch (uda1342->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 |= BIT(1);
+ break;
+ case 18:
+ hw_params |= BIT(2);
+ break;
+ case 20:
+ hw_params |= BIT(2) | BIT(1);
+ break;
+ default:
+ dev_err(dev, "unsupported format (right)\n");
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ hw_params |= BIT(3);
+ break;
+ default:
+ dev_err(dev, "unsupported format\n");
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(uda1342->regmap, 0x0,
+ STATUS0_DAIFMT_MASK | STATUS0_SYSCLK_MASK, hw_params);
+}
+
+static int uda1342_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+ struct device *dev = &uda1342->i2c->dev;
+
+ /*
+ * 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))) {
+ uda1342->sysclk = freq;
+ return 0;
+ }
+
+ dev_err(dev, "unsupported sysclk\n");
+
+ return -EINVAL;
+}
+
+static int uda1342_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+
+ /* codec supports only full consumer mode */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) {
+ dev_err(&uda1342->i2c->dev, "unsupported consumer mode.\n");
+ 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 */
+ uda1342->dai_fmt = fmt;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new uda1342_snd_controls[] = {
+ SOC_SINGLE("Master Playback Volume", 0x11, 0, 0x3F, 1),
+ SOC_SINGLE("Analog1 Volume", 0x12, 0, 0x1F, 1),
+};
+
+/* Common DAPM widgets */
+static const struct snd_soc_dapm_widget uda1342_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_DAC("DAC", "Playback", 0, 1, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", 0, 9, 0),
+
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda1342_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 uda1342_dai_ops = {
+ .startup = uda1342_startup,
+ .shutdown = uda1342_shutdown,
+ .hw_params = uda1342_hw_params,
+ .mute_stream = uda1342_mute,
+ .set_sysclk = uda1342_set_dai_sysclk,
+ .set_fmt = uda1342_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver uda1342_dai = {
+ .name = "uda1342-hifi",
+ /* playback capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = UDA134X_FORMATS,
+ },
+ /* capture capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = UDA134X_FORMATS,
+ },
+ /* pcm operations */
+ .ops = &uda1342_dai_ops,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_uda1342 = {
+ .controls = uda1342_snd_controls,
+ .num_controls = ARRAY_SIZE(uda1342_snd_controls),
+ .dapm_widgets = uda1342_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda1342_dapm_widgets),
+ .dapm_routes = uda1342_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda1342_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config uda1342_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = 0x21,
+ .reg_defaults = uda1342_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int uda1342_i2c_probe(struct i2c_client *i2c)
+{
+ struct uda1342_priv *uda1342;
+
+ uda1342 = devm_kzalloc(&i2c->dev, sizeof(*uda1342), GFP_KERNEL);
+ if (!uda1342)
+ return -ENOMEM;
+
+ uda1342->regmap = devm_regmap_init_i2c(i2c, &uda1342_regmap);
+ if (IS_ERR(uda1342->regmap))
+ return PTR_ERR(uda1342->regmap);
+
+ i2c_set_clientdata(i2c, uda1342);
+ uda1342->i2c = i2c;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_uda1342,
+ &uda1342_dai, 1);
+}
+
+static int uda1342_suspend(struct device *dev)
+{
+ struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
+
+ regcache_cache_only(uda1342->regmap, true);
+
+ return 0;
+}
+
+static int uda1342_resume(struct device *dev)
+{
+ struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
+
+ regcache_mark_dirty(uda1342->regmap);
+ regcache_sync(uda1342->regmap);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops,
+ uda1342_suspend, uda1342_resume, NULL);
+
+static const struct i2c_device_id uda1342_i2c_id[] = {
+ { "uda1342" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id);
+
+static const struct of_device_id uda1342_of_match[] = {
+ { .compatible = "nxp,uda1342" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, uda1342_of_match);
+
+static struct i2c_driver uda1342_i2c_driver = {
+ .driver = {
+ .name = "uda1342",
+ .of_match_table = uda1342_of_match,
+ .pm = pm_sleep_ptr(&uda1342_pm_ops),
+ },
+ .probe = uda1342_i2c_probe,
+ .id_table = uda1342_i2c_id,
+};
+module_i2c_driver(uda1342_i2c_driver);
+
+MODULE_DESCRIPTION("UDA1342 ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1342.h b/sound/soc/codecs/uda1342.h
new file mode 100644
index 000000000000..ff6aea0a8b01
--- /dev/null
+++ b/sound/soc/codecs/uda1342.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Audio support for NXP UDA1342
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright (c) 2024 Binbin Zhou <zhoubinbin@loongson.cn>
+ */
+
+#ifndef _UDA1342_H
+#define _UDA1342_H
+
+#define UDA1342_CLK 0x00
+#define UDA1342_IFACE 0x01
+#define UDA1342_PM 0x02
+#define UDA1342_AMIX 0x03
+#define UDA1342_HP 0x04
+#define UDA1342_MVOL 0x11
+#define UDA1342_MIXVOL 0x12
+#define UDA1342_MODE 0x12
+#define UDA1342_DEEMP 0x13
+#define UDA1342_MIXER 0x14
+#define UDA1342_INTSTAT 0x18
+#define UDA1342_DEC 0x20
+#define UDA1342_PGA 0x21
+#define UDA1342_ADC 0x22
+#define UDA1342_AGC 0x23
+#define UDA1342_DECSTAT 0x28
+#define UDA1342_RESET 0x7f
+
+/* 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 UDA1342_DAI_DUPLEX 0 /* playback and capture on single DAI */
+#define UDA1342_DAI_PLAYBACK 1 /* playback DAI */
+#define UDA1342_DAI_CAPTURE 2 /* capture DAI */
+
+#define STATUS0_DAIFMT_MASK (~(7 << 1))
+#define STATUS0_SYSCLK_MASK (~(3 << 4))
+
+#endif /* _UDA1342_H */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
deleted file mode 100644
index e4c694c758b8..000000000000
--- a/sound/soc/codecs/uda134x.c
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * 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;
-
- struct regmap *regmap;
- struct uda134x_platform_data *pd;
-};
-
-static const struct reg_default uda134x_reg_defaults[] = {
- { UDA134X_EA000, 0x04 },
- { UDA134X_EA001, 0x04 },
- { UDA134X_EA010, 0x04 },
- { UDA134X_EA011, 0x00 },
- { UDA134X_EA100, 0x00 },
- { UDA134X_EA101, 0x00 },
- { UDA134X_EA110, 0x00 },
- { UDA134X_EA111, 0x00 },
- { UDA134X_STATUS0, 0x00 },
- { UDA134X_STATUS1, 0x03 },
- { UDA134X_DATA000, 0x00 },
- { UDA134X_DATA001, 0x00 },
- { UDA134X_DATA010, 0x00 },
- { UDA134X_DATA011, 0x00 },
- { UDA134X_DATA1, 0x00 },
-};
-
-/*
- * Write to the uda134x registers
- *
- */
-static int uda134x_regmap_write(void *context, unsigned int reg,
- unsigned int value)
-{
- struct uda134x_platform_data *pd = context;
- int ret;
- u8 addr;
- u8 data = value;
-
- switch (reg) {
- case UDA134X_STATUS0:
- case UDA134X_STATUS1:
- addr = UDA134X_STATUS_ADDR;
- data |= (reg - UDA134X_STATUS0) << 7;
- break;
- case UDA134X_DATA000:
- case UDA134X_DATA001:
- case UDA134X_DATA010:
- case UDA134X_DATA011:
- addr = UDA134X_DATA0_ADDR;
- data |= (reg - UDA134X_DATA000) << 6;
- 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)
-{
- struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
- unsigned int mask = 1<<6;
-
- regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask);
- msleep(1);
- regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0);
-}
-
-static int uda134x_mute(struct snd_soc_dai *dai, int mute)
-{
- struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(dai->codec);
- unsigned int mask = 1<<2;
- unsigned int val;
-
- pr_debug("%s mute: %d\n", __func__, mute);
-
- if (mute)
- val = mask;
- else
- val = 0;
-
- return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val);
-}
-
-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_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- master_runtime->rate);
-
- snd_pcm_hw_constraint_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_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);
- unsigned int hw_params = 0;
-
- if (substream == uda134x->slave_substream) {
- pr_debug("%s ignoring hw_params for slave substream\n",
- __func__);
- return 0;
- }
-
- 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;
- }
-
- return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0,
- STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params);
-}
-
-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_priv *uda134x = snd_soc_codec_get_drvdata(codec);
- struct uda134x_platform_data *pd = uda134x->pd;
- 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);
- regcache_sync(uda134x->regmap);
- }
- break;
- case SND_SOC_BIAS_STANDBY:
- break;
- case SND_SOC_BIAS_OFF:
- /* power off */
- if (pd->power) {
- pd->power(0);
- regcache_mark_dirty(uda134x->regmap);
- }
- break;
- }
- 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 snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
- struct uda134x_platform_data *pd = uda134x->pd;
- const struct snd_soc_dapm_widget *widgets;
- unsigned num_widgets;
- int ret;
-
- printk(KERN_INFO "UDA134X SoC Audio Codec\n");
-
- 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;
- }
-
- 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(dapm, widgets, num_widgets);
- if (ret) {
- printk(KERN_ERR "%s failed to register dapm controls: %d",
- __func__, ret);
- 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);
- return -EINVAL;
- }
-
- if (ret < 0) {
- printk(KERN_ERR "UDA134X: failed to register controls\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
- .probe = uda134x_soc_probe,
- .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 const struct regmap_config uda134x_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = UDA134X_DATA1,
- .reg_defaults = uda134x_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
- .reg_write = uda134x_regmap_write,
-};
-
-static int uda134x_codec_probe(struct platform_device *pdev)
-{
- struct uda134x_platform_data *pd = pdev->dev.platform_data;
- struct uda134x_priv *uda134x;
-
- if (!pd) {
- dev_err(&pdev->dev, "Missing L3 bitbang function\n");
- return -ENODEV;
- }
-
- uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL);
- if (!uda134x)
- return -ENOMEM;
-
- uda134x->pd = pd;
- platform_set_drvdata(pdev, uda134x);
-
- uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,
- &uda134x_regmap_config);
- if (IS_ERR(uda134x->regmap))
- return PTR_ERR(uda134x->regmap);
-
- 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/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h
deleted file mode 100644
index e41ab38c6f69..000000000000
--- a/sound/soc/codecs/uda134x.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#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 STATUS0_DAIFMT_MASK (~(7<<1))
-#define STATUS0_SYSCLK_MASK (~(3<<4))
-
-#endif
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 35f0469ebb16..9e9c540a45ca 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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
@@ -34,10 +31,11 @@
/* codec private data */
struct uda1380_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
unsigned int dac_clk;
struct work_struct work;
- void *control_data;
+ struct i2c_client *i2c;
+ u16 *reg_cache;
};
/*
@@ -60,10 +58,12 @@ static unsigned long uda1380_cache_dirty;
/*
* read uda1380 register cache
*/
-static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_component *component,
unsigned int reg)
{
- u16 *cache = codec->reg_cache;
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
+ u16 *cache = uda1380->reg_cache;
+
if (reg == UDA1380_RESET)
return 0;
if (reg >= UDA1380_CACHEREGNUM)
@@ -74,10 +74,11 @@ static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
/*
* write uda1380 register cache
*/
-static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
+static inline void uda1380_write_reg_cache(struct snd_soc_component *component,
u16 reg, unsigned int value)
{
- u16 *cache = codec->reg_cache;
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
+ u16 *cache = uda1380->reg_cache;
if (reg >= UDA1380_CACHEREGNUM)
return;
@@ -89,9 +90,10 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
/*
* write to the UDA1380 register space
*/
-static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
+static int uda1380_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u8 data[3];
/* data is
@@ -103,18 +105,18 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
data[1] = (value & 0xff00) >> 8;
data[2] = value & 0x00ff;
- uda1380_write_reg_cache(codec, reg, value);
+ uda1380_write_reg_cache(component, 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))
+ if (!snd_soc_component_active(component) && (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) {
+ if (i2c_master_send(uda1380->i2c, data, 3) == 3) {
unsigned int val;
- i2c_master_send(codec->control_data, data, 1);
- i2c_master_recv(codec->control_data, data, 2);
+ i2c_master_send(uda1380->i2c, data, 1);
+ i2c_master_recv(uda1380->i2c, data, 2);
val = (data[0]<<8) | data[1];
if (val != value) {
pr_debug("uda1380: READ BACK VAL %x\n",
@@ -128,26 +130,28 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-static void uda1380_sync_cache(struct snd_soc_codec *codec)
+static void uda1380_sync_cache(struct snd_soc_component *component)
{
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int reg;
u8 data[3];
- u16 *cache = codec->reg_cache;
+ u16 *cache = uda1380->reg_cache;
/* Sync reg_cache with the hardware */
for (reg = 0; reg < UDA1380_MVOL; reg++) {
data[0] = reg;
data[1] = (cache[reg] & 0xff00) >> 8;
data[2] = cache[reg] & 0x00ff;
- if (codec->hw_write(codec->control_data, data, 3) != 3)
- dev_err(codec->dev, "%s: write to reg 0x%x failed\n",
+ if (i2c_master_send(uda1380->i2c, data, 3) != 3)
+ dev_err(component->dev, "%s: write to reg 0x%x failed\n",
__func__, reg);
}
}
-static int uda1380_reset(struct snd_soc_codec *codec)
+static int uda1380_reset(struct snd_soc_component *component)
{
- struct uda1380_platform_data *pdata = codec->dev->platform_data;
+ struct uda1380_platform_data *pdata = component->dev->platform_data;
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
if (gpio_is_valid(pdata->gpio_reset)) {
gpio_set_value(pdata->gpio_reset, 1);
@@ -160,8 +164,8 @@ static int uda1380_reset(struct snd_soc_codec *codec)
data[1] = 0;
data[2] = 0;
- if (codec->hw_write(codec->control_data, data, 3) != 3) {
- dev_err(codec->dev, "%s: failed\n", __func__);
+ if (i2c_master_send(uda1380->i2c, data, 3) != 3) {
+ dev_err(component->dev, "%s: failed\n", __func__);
return -EIO;
}
}
@@ -172,15 +176,15 @@ static int uda1380_reset(struct snd_soc_codec *codec)
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;
+ struct snd_soc_component *uda1380_component = uda1380->component;
int bit, reg;
for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
reg = 0x10 + bit;
pr_debug("uda1380: flush reg %x val %x:\n", reg,
- uda1380_read_reg_cache(uda1380_codec, reg));
- uda1380_write(uda1380_codec, reg,
- uda1380_read_reg_cache(uda1380_codec, reg));
+ uda1380_read_reg_cache(uda1380_component, reg));
+ uda1380_write(uda1380_component, reg,
+ uda1380_read_reg_cache(uda1380_component, reg));
clear_bit(bit, &uda1380_cache_dirty);
}
@@ -413,11 +417,11 @@ static const struct snd_soc_dapm_route uda1380_dapm_routes[] = {
static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
- iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -431,11 +435,11 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
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)
+ /* DATAI is consumer only */
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
- uda1380_write_reg_cache(codec, UDA1380_IFACE, iface);
+ uda1380_write_reg_cache(component, UDA1380_IFACE, iface);
return 0;
}
@@ -443,11 +447,11 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
- iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~R01_SFORI_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -461,11 +465,11 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
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)
+ /* DATAI is consumer only */
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
- uda1380_write(codec, UDA1380_IFACE, iface);
+ uda1380_write(component, UDA1380_IFACE, iface);
return 0;
}
@@ -473,11 +477,11 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
- iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~(R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -491,10 +495,10 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
iface |= R01_SFORO_MSB;
}
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_CBP_CFP)
iface |= R01_SIM;
- uda1380_write(codec, UDA1380_IFACE, iface);
+ uda1380_write(component, UDA1380_IFACE, iface);
return 0;
}
@@ -502,20 +506,20 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = dai->component;
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
+ int mixer = uda1380_read_reg_cache(component, UDA1380_MIXER);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ uda1380_write_reg_cache(component, UDA1380_MIXER,
mixer & ~R14_SILENCE);
schedule_work(&uda1380->work);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ uda1380_write_reg_cache(component, UDA1380_MIXER,
mixer | R14_SILENCE);
schedule_work(&uda1380->work);
break;
@@ -527,13 +531,13 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
if (clk & R00_DAC_CLK) {
int rate = params_rate(params);
- u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ u16 pm = uda1380_read_reg_cache(component, UDA1380_PM);
clk &= ~0x3; /* clear SEL_LOOP_DIV */
switch (rate) {
case 6250 ... 12500:
@@ -549,7 +553,7 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
clk |= 0x3;
break;
}
- uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
+ uda1380_write(component, UDA1380_PM, R02_PON_PLL | pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -557,20 +561,20 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
else
clk |= R00_EN_ADC | R00_EN_DEC;
- uda1380_write(codec, UDA1380_CLK, clk);
+ uda1380_write(component, UDA1380_CLK, clk);
return 0;
}
static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+ struct snd_soc_component *component = dai->component;
+ u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK);
/* shut down WSPLL power if running from this clock */
if (clk & R00_DAC_CLK) {
- u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
- uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
+ u16 pm = uda1380_read_reg_cache(component, UDA1380_PM);
+ uda1380_write(component, UDA1380_PM, ~R02_PON_PLL & pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -578,33 +582,34 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
else
clk &= ~(R00_EN_ADC | R00_EN_DEC);
- uda1380_write(codec, UDA1380_CLK, clk);
+ uda1380_write(component, UDA1380_CLK, clk);
}
-static int uda1380_set_bias_level(struct snd_soc_codec *codec,
+static int uda1380_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int pm = uda1380_read_reg_cache(component, UDA1380_PM);
int reg;
- struct uda1380_platform_data *pdata = codec->dev->platform_data;
+ struct uda1380_platform_data *pdata = component->dev->platform_data;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
/* ADC, DAC on */
- uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
+ uda1380_write(component, UDA1380_PM, R02_PON_BIAS | pm);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
- uda1380_reset(codec);
+ uda1380_reset(component);
}
- uda1380_sync_cache(codec);
+ uda1380_sync_cache(component);
}
- uda1380_write(codec, UDA1380_PM, 0x0);
+ uda1380_write(component, UDA1380_PM, 0x0);
break;
case SND_SOC_BIAS_OFF:
if (!gpio_is_valid(pdata->gpio_power))
@@ -687,36 +692,18 @@ static struct snd_soc_dai_driver uda1380_dai[] = {
},
};
-static int uda1380_probe(struct snd_soc_codec *codec)
+static int uda1380_probe(struct snd_soc_component *component)
{
- struct uda1380_platform_data *pdata =codec->dev->platform_data;
- struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
+ struct uda1380_platform_data *pdata =component->dev->platform_data;
+ struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
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;
- }
+ uda1380->component = component;
- 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 (!gpio_is_valid(pdata->gpio_power)) {
+ ret = uda1380_reset(component);
if (ret)
- goto err_free_gpio;
+ return ret;
}
INIT_WORK(&uda1380->work, uda1380_flush_work);
@@ -724,116 +711,97 @@ static int uda1380_probe(struct snd_soc_codec *codec)
/* set clock input */
switch (pdata->dac_clk) {
case UDA1380_DAC_CLK_SYSCLK:
- uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
+ uda1380_write_reg_cache(component, UDA1380_CLK, 0);
break;
case UDA1380_DAC_CLK_WSPLL:
- uda1380_write_reg_cache(codec, UDA1380_CLK,
+ uda1380_write_reg_cache(component, 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),
+static const struct snd_soc_component_driver soc_component_dev_uda1380 = {
+ .probe = uda1380_probe,
+ .read = uda1380_read_reg_cache,
+ .write = uda1380_write,
+ .set_bias_level = uda1380_set_bias_level,
+ .controls = uda1380_snd_controls,
+ .num_controls = ARRAY_SIZE(uda1380_snd_controls),
+ .dapm_widgets = uda1380_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
+ .dapm_routes = uda1380_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-#if IS_ENABLED(CONFIG_I2C)
-static int uda1380_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int uda1380_i2c_probe(struct i2c_client *i2c)
{
+ struct uda1380_platform_data *pdata = i2c->dev.platform_data;
struct uda1380_priv *uda1380;
int ret;
+ if (!pdata)
+ return -EINVAL;
+
uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv),
GFP_KERNEL);
if (uda1380 == NULL)
return -ENOMEM;
+ if (gpio_is_valid(pdata->gpio_reset)) {
+ ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset,
+ GPIOF_OUT_INIT_LOW, "uda1380 reset");
+ if (ret)
+ return ret;
+ }
+
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "uda1380 power");
+ if (ret)
+ return ret;
+ }
+
+ uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg),
+ sizeof(uda1380_reg[0]), GFP_KERNEL);
+ if (!uda1380->reg_cache)
+ return -ENOMEM;
+
i2c_set_clientdata(i2c, uda1380);
- uda1380->control_data = i2c;
+ uda1380->i2c = i2c;
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "uda1380" },
{ }
};
MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
+static const struct of_device_id uda1380_of_match[] = {
+ { .compatible = "nxp,uda1380", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, uda1380_of_match);
+
static struct i2c_driver uda1380_i2c_driver = {
.driver = {
.name = "uda1380-codec",
+ .of_match_table = uda1380_of_match,
},
- .probe = uda1380_i2c_probe,
- .remove = uda1380_i2c_remove,
+ .probe = uda1380_i2c_probe,
.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_i2c_driver(uda1380_i2c_driver);
MODULE_AUTHOR("Giorgio Padrin");
MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
index 942e3927c72b..0222f2ab818f 100644
--- a/sound/soc/codecs/uda1380.h
+++ b/sound/soc/codecs/uda1380.h
@@ -1,10 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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>
*/
@@ -72,8 +69,4 @@
#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/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c
new file mode 100644
index 000000000000..d96e23ec43d4
--- /dev/null
+++ b/sound/soc/codecs/wcd-clsh-v2.c
@@ -0,0 +1,905 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+// Copyright (c) 2017-2018, Linaro Limited
+
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include "wcd9335.h"
+#include "wcd-clsh-v2.h"
+
+struct wcd_clsh_ctrl {
+ int state;
+ int mode;
+ int flyback_users;
+ int buck_users;
+ int clsh_users;
+ int codec_version;
+ struct snd_soc_component *comp;
+};
+
+/* Class-H registers for codecs from and above WCD9335 */
+#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42)
+#define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6)
+#define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6)
+#define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 0
+#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56)
+#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A)
+#define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08)
+#define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0)
+#define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09)
+#define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0)
+#define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08)
+#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1)
+#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 0
+#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1)
+#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2)
+#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2)
+#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 0
+#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3)
+#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3)
+#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 0
+#define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6)
+#define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6
+#define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6)
+#define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0
+#define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7)
+#define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7
+#define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7)
+#define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0
+#define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09)
+#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2)
+#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x08
+#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04
+#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0
+#define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01)
+#define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0)
+#define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0)
+#define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 0
+#define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4)
+#define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5)
+#define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x40
+#define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4)
+#define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4)
+#define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 0
+#define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7)
+#define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4)
+#define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0)
+#define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3)
+#define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3)
+#define WCD9XXX_HPH_CONST_SEL_BYPASS 0
+#define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40
+#define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80
+#define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6)
+#define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD)
+#define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0)
+#define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B)
+#define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4)
+#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x20
+#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0
+#define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55)
+#define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69)
+#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41)
+#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0)
+#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1)
+#define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C)
+#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4)
+#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x50
+#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30
+
+#define WCD9XXX_BASE_ADDRESS 0x3000
+#define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008)
+#define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009)
+#define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098)
+#define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_1 (WCD9XXX_BASE_ADDRESS+0x0A5)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8)
+#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF)
+#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF)
+#define WCD9XXX_V3_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7)
+#define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1)
+#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138)
+
+#define CLSH_REQ_ENABLE true
+#define CLSH_REQ_DISABLE false
+#define WCD_USLEEP_RANGE 50
+
+enum {
+ DAC_GAIN_0DB = 0,
+ DAC_GAIN_0P2DB,
+ DAC_GAIN_0P4DB,
+ DAC_GAIN_0P6DB,
+ DAC_GAIN_0P8DB,
+ DAC_GAIN_M0P2DB,
+ DAC_GAIN_M0P4DB,
+ DAC_GAIN_M0P6DB,
+};
+
+static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,
+ bool enable)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if ((enable && ++ctrl->clsh_users == 1) ||
+ (!enable && --ctrl->clsh_users == 0))
+ snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC,
+ WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK,
+ enable);
+ if (ctrl->clsh_users < 0)
+ ctrl->clsh_users = 0;
+}
+
+static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,
+ int mode)
+{
+ /* set to HIFI */
+ if (mode == CLS_H_HIFI)
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
+ WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA);
+ else
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
+ WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT);
+}
+
+static void wcd_clsh_v3_set_buck_mode(struct snd_soc_component *component,
+ int mode)
+{
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI)
+ snd_soc_component_update_bits(component,
+ WCD9XXX_ANA_RX_SUPPLIES,
+ 0x08, 0x08); /* set to HIFI */
+ else
+ snd_soc_component_update_bits(component,
+ WCD9XXX_ANA_RX_SUPPLIES,
+ 0x08, 0x00); /* set to default */
+}
+
+static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp,
+ int mode)
+{
+ /* set to HIFI */
+ if (mode == CLS_H_HIFI)
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
+ WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA);
+ else
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
+ WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT);
+}
+
+static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl,
+ int mode,
+ bool enable)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ /* enable/disable buck */
+ if ((enable && (++ctrl->buck_users == 1)) ||
+ (!enable && (--ctrl->buck_users == 0)))
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_VPOS_EN_MASK,
+ enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT);
+ /*
+ * 500us sleep is required after buck enable/disable
+ * as per HW requirement
+ */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+}
+
+static void wcd_clsh_v3_buck_ctrl(struct snd_soc_component *component,
+ struct wcd_clsh_ctrl *ctrl,
+ int mode,
+ bool enable)
+{
+ /* enable/disable buck */
+ if ((enable && (++ctrl->buck_users == 1)) ||
+ (!enable && (--ctrl->buck_users == 0))) {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_ANA_RX_SUPPLIES,
+ (1 << 7), (enable << 7));
+ /*
+ * 500us sleep is required after buck enable/disable
+ * as per HW requirement
+ */
+ usleep_range(500, 510);
+ if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
+ mode == CLS_H_HIFI || mode == CLS_H_LP)
+ snd_soc_component_update_bits(component,
+ WCD9XXX_CLASSH_MODE_3,
+ 0x02, 0x00);
+
+ snd_soc_component_update_bits(component,
+ WCD9XXX_CLASSH_MODE_2,
+ 0xFF, 0x3A);
+ /* 500usec delay is needed as per HW requirement */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+ }
+}
+
+static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl,
+ int mode,
+ bool enable)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ /* enable/disable flyback */
+ if ((enable && (++ctrl->flyback_users == 1)) ||
+ (!enable && (--ctrl->flyback_users == 0))) {
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_VNEG_EN_MASK,
+ enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT);
+ /* 100usec delay is needed as per HW requirement */
+ usleep_range(100, 110);
+ }
+ /*
+ * 500us sleep is required after flyback enable/disable
+ * as per HW requirement
+ */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+}
+
+static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+ int val = 0;
+
+ switch (mode) {
+ case CLS_H_NORMAL:
+ case CLS_AB:
+ val = WCD9XXX_HPH_CONST_SEL_BYPASS;
+ break;
+ case CLS_H_HIFI:
+ val = WCD9XXX_HPH_CONST_SEL_HQ_PATH;
+ break;
+ case CLS_H_LP:
+ val = WCD9XXX_HPH_CONST_SEL_LP_PATH;
+ break;
+ }
+
+ snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN,
+ WCD9XXX_HPH_CONST_SEL_L_MASK,
+ val);
+
+ snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN,
+ WCD9XXX_HPH_CONST_SEL_L_MASK,
+ val);
+}
+
+static void wcd_clsh_v2_set_hph_mode(struct snd_soc_component *comp, int mode)
+{
+ int val = 0, gain = 0, res_val;
+ int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
+
+ res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM;
+ switch (mode) {
+ case CLS_H_NORMAL:
+ res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM;
+ val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
+ gain = DAC_GAIN_0DB;
+ ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
+ break;
+ case CLS_AB:
+ val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
+ gain = DAC_GAIN_0DB;
+ ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
+ break;
+ case CLS_H_HIFI:
+ val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA;
+ gain = DAC_GAIN_M0P2DB;
+ ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
+ break;
+ case CLS_H_LP:
+ val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP;
+ ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA;
+ break;
+ }
+
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH,
+ WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val);
+ snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2,
+ WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK,
+ res_val);
+ if (mode != CLS_H_LP)
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_HPH_REFBUFF_UHQA_CTL,
+ WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK,
+ gain);
+ snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1,
+ WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK,
+ ipeak);
+}
+
+static void wcd_clsh_v3_set_hph_mode(struct snd_soc_component *component,
+ int mode)
+{
+ u8 val;
+
+ switch (mode) {
+ case CLS_H_NORMAL:
+ val = 0x00;
+ break;
+ case CLS_AB:
+ case CLS_H_ULP:
+ val = 0x0C;
+ break;
+ case CLS_AB_HIFI:
+ case CLS_H_HIFI:
+ val = 0x08;
+ break;
+ case CLS_H_LP:
+ case CLS_H_LOHIFI:
+ case CLS_AB_LP:
+ case CLS_AB_LOHIFI:
+ val = 0x04;
+ break;
+ default:
+ dev_err(component->dev, "%s:Invalid mode %d\n", __func__, mode);
+ return;
+ }
+
+ snd_soc_component_update_bits(component, WCD9XXX_ANA_HPH, 0x0C, val);
+}
+
+void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, int mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if (ctrl->codec_version >= WCD937X)
+ wcd_clsh_v3_set_hph_mode(comp, mode);
+ else
+ wcd_clsh_v2_set_hph_mode(comp, mode);
+
+}
+EXPORT_SYMBOL_GPL(wcd_clsh_set_hph_mode);
+
+static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp,
+ int mode)
+{
+
+ snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
+ WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A);
+ snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
+ WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A);
+ /* Sleep needed to avoid click and pop as per HW requirement */
+ usleep_range(100, 110);
+}
+
+static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp,
+ int mode)
+{
+ if (mode == CLS_AB)
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
+ WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB);
+ else
+ snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
+ WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
+ WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H);
+}
+
+static void wcd_clsh_v3_set_buck_regulator_mode(struct snd_soc_component *component,
+ int mode)
+{
+ snd_soc_component_update_bits(component, WCD9XXX_ANA_RX_SUPPLIES,
+ 0x02, 0x00);
+}
+
+static void wcd_clsh_v3_set_flyback_mode(struct snd_soc_component *component,
+ int mode)
+{
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI) {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_ANA_RX_SUPPLIES,
+ 0x04, 0x04);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x80);
+ } else {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_ANA_RX_SUPPLIES,
+ 0x04, 0x00); /* set to Default */
+ snd_soc_component_update_bits(component,
+ WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x70);
+ }
+}
+
+static void wcd_clsh_v3_force_iq_ctl(struct snd_soc_component *component,
+ int mode, bool enable)
+{
+ if (enable) {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+ 0xE0, 0xA0);
+ /* 100usec delay is needed as per HW requirement */
+ usleep_range(100, 110);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_CLASSH_MODE_3,
+ 0x02, 0x02);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_CLASSH_MODE_2,
+ 0xFF, 0x1C);
+ if (mode == CLS_H_LOHIFI || mode == CLS_AB_LOHIFI) {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x20);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0xC0);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x02);
+ }
+ } else {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x00);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0x80);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x06);
+ }
+}
+
+static void wcd_clsh_v3_flyback_ctrl(struct snd_soc_component *component,
+ struct wcd_clsh_ctrl *ctrl,
+ int mode,
+ bool enable)
+{
+ /* enable/disable flyback */
+ if ((enable && (++ctrl->flyback_users == 1)) ||
+ (!enable && (--ctrl->flyback_users == 0))) {
+ snd_soc_component_update_bits(component,
+ WCD9XXX_FLYBACK_VNEG_CTRL_1,
+ 0xE0, 0xE0);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_ANA_RX_SUPPLIES,
+ (1 << 6), (enable << 6));
+ /*
+ * 100us sleep is required after flyback enable/disable
+ * as per HW requirement
+ */
+ usleep_range(100, 110);
+ snd_soc_component_update_bits(component,
+ WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+ 0xE0, 0xE0);
+ /* 500usec delay is needed as per HW requirement */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+ }
+}
+
+static void wcd_clsh_v3_set_flyback_current(struct snd_soc_component *component,
+ int mode)
+{
+ snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,
+ 0x0F, 0x0A);
+ snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,
+ 0xF0, 0xA0);
+ /* Sleep needed to avoid click and pop as per HW requirement */
+ usleep_range(100, 110);
+}
+
+static void wcd_clsh_v3_state_aux(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *component = ctrl->comp;
+
+ if (is_enable) {
+ wcd_clsh_v3_set_buck_mode(component, mode);
+ wcd_clsh_v3_set_flyback_mode(component, mode);
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_flyback_current(component, mode);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
+ } else {
+ wcd_clsh_v3_buck_ctrl(component, ctrl, mode, false);
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, false);
+ wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
+ wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if (mode != CLS_AB) {
+ dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n",
+ __func__, mode);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_clsh_set_buck_regulator_mode(comp, mode);
+ wcd_clsh_set_buck_mode(comp, mode);
+ wcd_clsh_set_flyback_mode(comp, mode);
+ wcd_clsh_flyback_ctrl(ctrl, mode, true);
+ wcd_clsh_set_flyback_current(comp, mode);
+ wcd_clsh_buck_ctrl(ctrl, mode, true);
+ } else {
+ wcd_clsh_buck_ctrl(ctrl, mode, false);
+ wcd_clsh_flyback_ctrl(ctrl, mode, false);
+ wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_v3_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *component = ctrl->comp;
+
+ if (mode == CLS_H_NORMAL) {
+ dev_dbg(component->dev, "%s: Normal mode not applicable for hph_r\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_clsh_v3_set_buck_regulator_mode(component, mode);
+ wcd_clsh_v3_set_flyback_mode(component, mode);
+ wcd_clsh_v3_force_iq_ctl(component, mode, true);
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_flyback_current(component, mode);
+ wcd_clsh_v3_set_buck_mode(component, mode);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_hph_mode(component, mode);
+ } else {
+ wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
+
+ /* buck and flyback set to default mode and disable */
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
+ wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
+ wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if (mode == CLS_H_NORMAL) {
+ dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ if (mode != CLS_AB) {
+ wcd_enable_clsh_block(ctrl, true);
+ /*
+ * These K1 values depend on the Headphone Impedance
+ * For now it is assumed to be 16 ohm
+ */
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_CLSH_K1_MSB,
+ WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
+ 0x00);
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_CLSH_K1_LSB,
+ WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
+ 0xC0);
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
+ }
+ wcd_clsh_set_buck_regulator_mode(comp, mode);
+ wcd_clsh_set_flyback_mode(comp, mode);
+ wcd_clsh_flyback_ctrl(ctrl, mode, true);
+ wcd_clsh_set_flyback_current(comp, mode);
+ wcd_clsh_set_buck_mode(comp, mode);
+ wcd_clsh_buck_ctrl(ctrl, mode, true);
+ wcd_clsh_v2_set_hph_mode(comp, mode);
+ wcd_clsh_set_gain_path(ctrl, mode);
+ } else {
+ wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);
+
+ if (mode != CLS_AB) {
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
+ wcd_enable_clsh_block(ctrl, false);
+ }
+ /* buck and flyback set to default mode and disable */
+ wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_v3_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *component = ctrl->comp;
+
+ if (mode == CLS_H_NORMAL) {
+ dev_dbg(component->dev, "%s: Normal mode not applicable for hph_l\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_clsh_v3_set_buck_regulator_mode(component, mode);
+ wcd_clsh_v3_set_flyback_mode(component, mode);
+ wcd_clsh_v3_force_iq_ctl(component, mode, true);
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_flyback_current(component, mode);
+ wcd_clsh_v3_set_buck_mode(component, mode);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_hph_mode(component, mode);
+ } else {
+ wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
+
+ /* set buck and flyback to Default Mode */
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
+ wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
+ wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if (mode == CLS_H_NORMAL) {
+ dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ if (mode != CLS_AB) {
+ wcd_enable_clsh_block(ctrl, true);
+ /*
+ * These K1 values depend on the Headphone Impedance
+ * For now it is assumed to be 16 ohm
+ */
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_CLSH_K1_MSB,
+ WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
+ 0x00);
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_CLSH_K1_LSB,
+ WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
+ 0xC0);
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
+ }
+ wcd_clsh_set_buck_regulator_mode(comp, mode);
+ wcd_clsh_set_flyback_mode(comp, mode);
+ wcd_clsh_flyback_ctrl(ctrl, mode, true);
+ wcd_clsh_set_flyback_current(comp, mode);
+ wcd_clsh_set_buck_mode(comp, mode);
+ wcd_clsh_buck_ctrl(ctrl, mode, true);
+ wcd_clsh_v2_set_hph_mode(comp, mode);
+ wcd_clsh_set_gain_path(ctrl, mode);
+ } else {
+ wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);
+
+ if (mode != CLS_AB) {
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
+ wcd_enable_clsh_block(ctrl, false);
+ }
+ /* set buck and flyback to Default Mode */
+ wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_v3_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *component = ctrl->comp;
+
+ if (is_enable) {
+ wcd_clsh_v3_set_buck_regulator_mode(component, mode);
+ wcd_clsh_v3_set_flyback_mode(component, mode);
+ wcd_clsh_v3_force_iq_ctl(component, mode, true);
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_flyback_current(component, mode);
+ wcd_clsh_v3_set_buck_mode(component, mode);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
+ wcd_clsh_v3_set_hph_mode(component, mode);
+ } else {
+ wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
+
+ /* set buck and flyback to Default Mode */
+ wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
+ wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
+ wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
+ wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if (mode != CLS_H_NORMAL) {
+ dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n",
+ __func__, mode);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_enable_clsh_block(ctrl, true);
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
+ wcd_clsh_set_buck_mode(comp, mode);
+ wcd_clsh_set_flyback_mode(comp, mode);
+ wcd_clsh_flyback_ctrl(ctrl, mode, true);
+ wcd_clsh_set_flyback_current(comp, mode);
+ wcd_clsh_buck_ctrl(ctrl, mode, true);
+ } else {
+ snd_soc_component_update_bits(comp,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
+ WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
+ wcd_enable_clsh_block(ctrl, false);
+ wcd_clsh_buck_ctrl(ctrl, mode, false);
+ wcd_clsh_flyback_ctrl(ctrl, mode, false);
+ wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
+ }
+}
+
+static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state,
+ bool is_enable, int mode)
+{
+ switch (req_state) {
+ case WCD_CLSH_STATE_EAR:
+ if (ctrl->codec_version >= WCD937X)
+ wcd_clsh_v3_state_ear(ctrl, req_state, is_enable, mode);
+ else
+ wcd_clsh_state_ear(ctrl, req_state, is_enable, mode);
+ break;
+ case WCD_CLSH_STATE_HPHL:
+ if (ctrl->codec_version >= WCD937X)
+ wcd_clsh_v3_state_hph_l(ctrl, req_state, is_enable, mode);
+ else
+ wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode);
+ break;
+ case WCD_CLSH_STATE_HPHR:
+ if (ctrl->codec_version >= WCD937X)
+ wcd_clsh_v3_state_hph_r(ctrl, req_state, is_enable, mode);
+ else
+ wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode);
+ break;
+ case WCD_CLSH_STATE_LO:
+ if (ctrl->codec_version < WCD937X)
+ wcd_clsh_state_lo(ctrl, req_state, is_enable, mode);
+ break;
+ case WCD_CLSH_STATE_AUX:
+ if (ctrl->codec_version >= WCD937X)
+ wcd_clsh_v3_state_aux(ctrl, req_state, is_enable, mode);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Function: wcd_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static bool wcd_clsh_is_state_valid(int state)
+{
+ switch (state) {
+ case WCD_CLSH_STATE_IDLE:
+ case WCD_CLSH_STATE_EAR:
+ case WCD_CLSH_STATE_HPHL:
+ case WCD_CLSH_STATE_HPHR:
+ case WCD_CLSH_STATE_LO:
+ case WCD_CLSH_STATE_AUX:
+ return true;
+ default:
+ return false;
+ };
+}
+
+/*
+ * Function: wcd_clsh_fsm
+ * Params: ctrl, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. ctrl will contain current
+ * class h state information
+ */
+int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl,
+ enum wcd_clsh_event clsh_event,
+ int nstate,
+ enum wcd_clsh_mode mode)
+{
+ struct snd_soc_component *comp = ctrl->comp;
+
+ if (nstate == ctrl->state)
+ return 0;
+
+ if (!wcd_clsh_is_state_valid(nstate)) {
+ dev_err(comp->dev, "Class-H not a valid new state:\n");
+ return -EINVAL;
+ }
+
+ switch (clsh_event) {
+ case WCD_CLSH_EVENT_PRE_DAC:
+ _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode);
+ break;
+ case WCD_CLSH_EVENT_POST_PA:
+ _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode);
+ break;
+ }
+
+ ctrl->state = nstate;
+ ctrl->mode = mode;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_set_state);
+
+int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl)
+{
+ return ctrl->state;
+}
+EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_get_state);
+
+struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp,
+ int version)
+{
+ struct wcd_clsh_ctrl *ctrl;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ ctrl->state = WCD_CLSH_STATE_IDLE;
+ ctrl->comp = comp;
+ ctrl->codec_version = version;
+
+ return ctrl;
+}
+EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_alloc);
+
+void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl)
+{
+ kfree(ctrl);
+}
+EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_free);
+
+MODULE_DESCRIPTION("WCD93XX Class-H driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd-clsh-v2.h b/sound/soc/codecs/wcd-clsh-v2.h
new file mode 100644
index 000000000000..eeb9bc5b01e2
--- /dev/null
+++ b/sound/soc/codecs/wcd-clsh-v2.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _WCD_CLSH_V2_H_
+#define _WCD_CLSH_V2_H_
+#include <sound/soc.h>
+
+enum wcd_clsh_event {
+ WCD_CLSH_EVENT_PRE_DAC = 1,
+ WCD_CLSH_EVENT_POST_PA,
+};
+
+/*
+ * Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: Lineout mode
+ */
+#define WCD_CLSH_STATE_IDLE 0
+#define WCD_CLSH_STATE_EAR BIT(0)
+#define WCD_CLSH_STATE_HPHL BIT(1)
+#define WCD_CLSH_STATE_HPHR BIT(2)
+#define WCD_CLSH_STATE_LO BIT(3)
+#define WCD_CLSH_STATE_AUX BIT(4)
+#define WCD_CLSH_STATE_MAX 4
+#define WCD_CLSH_V3_STATE_MAX 5
+#define NUM_CLSH_STATES_V2 BIT(WCD_CLSH_STATE_MAX)
+#define NUM_CLSH_STATES_V3 BIT(WCD_CLSH_V3_STATE_MAX)
+
+enum wcd_clsh_mode {
+ CLS_H_NORMAL = 0, /* Class-H Default */
+ CLS_H_HIFI, /* Class-H HiFi */
+ CLS_H_LP, /* Class-H Low Power */
+ CLS_AB, /* Class-AB */
+ CLS_H_LOHIFI, /* LoHIFI */
+ CLS_H_ULP, /* Ultra Low power */
+ CLS_AB_HIFI, /* Class-AB */
+ CLS_AB_LP, /* Class-AB Low Power */
+ CLS_AB_LOHIFI, /* Class-AB Low HIFI */
+ CLS_NONE, /* None of the above modes */
+};
+
+enum wcd_codec_version {
+ WCD9335 = 0,
+ WCD934X = 1,
+ /* New CLSH after this */
+ WCD937X = 2,
+ WCD938X = 3,
+ WCD939X = 4,
+};
+struct wcd_clsh_ctrl;
+
+extern struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(
+ struct snd_soc_component *comp,
+ int version);
+extern void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl);
+extern int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl);
+extern int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl,
+ enum wcd_clsh_event clsh_event,
+ int nstate,
+ enum wcd_clsh_mode mode);
+extern void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl,
+ int mode);
+
+#endif /* _WCD_CLSH_V2_H_ */
diff --git a/sound/soc/codecs/wcd-common.c b/sound/soc/codecs/wcd-common.c
new file mode 100644
index 000000000000..9016e974582f
--- /dev/null
+++ b/sound/soc/codecs/wcd-common.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/regmap.h>
+
+#include "wcd-common.h"
+
+#define WCD_MIN_MICBIAS_MV 1000
+#define WCD_DEF_MICBIAS_MV 1800
+#define WCD_MAX_MICBIAS_MV 2850
+
+#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
+
+int wcd_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
+{
+ /* min micbias voltage is 1V and maximum is 2.85V */
+ if (micb_mv < WCD_MIN_MICBIAS_MV || micb_mv > WCD_MAX_MICBIAS_MV) {
+ dev_err(dev, "Unsupported micbias voltage (%u mV)\n", micb_mv);
+ return -EINVAL;
+ }
+
+ return (micb_mv - WCD_MIN_MICBIAS_MV) / 50;
+}
+EXPORT_SYMBOL_GPL(wcd_get_micb_vout_ctl_val);
+
+static int wcd_get_micbias_val(struct device *dev, int micb_num, u32 *micb_mv)
+{
+ char micbias[64];
+ int mv;
+
+ sprintf(micbias, "qcom,micbias%d-microvolt", micb_num);
+
+ if (of_property_read_u32(dev->of_node, micbias, &mv)) {
+ dev_err(dev, "%s value not found, using default\n", micbias);
+ mv = WCD_DEF_MICBIAS_MV;
+ } else {
+ /* convert it to milli volts */
+ mv = mv/1000;
+ }
+ if (micb_mv)
+ *micb_mv = mv;
+
+ mv = wcd_get_micb_vout_ctl_val(dev, mv);
+ if (mv < 0) {
+ dev_err(dev, "Unsupported %s voltage (%d mV), falling back to default (%d mV)\n",
+ micbias, mv, WCD_DEF_MICBIAS_MV);
+ return wcd_get_micb_vout_ctl_val(dev, WCD_DEF_MICBIAS_MV);
+ }
+
+ return mv;
+}
+
+int wcd_dt_parse_micbias_info(struct wcd_common *common)
+{
+ int ret, i;
+
+ for (i = 0; i < common->max_bias; i++) {
+ ret = wcd_get_micbias_val(common->dev, i + 1, &common->micb_mv[i]);
+ if (ret < 0)
+ return ret;
+ common->micb_vout[i] = ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_dt_parse_micbias_info);
+
+static int wcd_sdw_component_bind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static void wcd_sdw_component_unbind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+const struct component_ops wcd_sdw_component_ops = {
+ .bind = wcd_sdw_component_bind,
+ .unbind = wcd_sdw_component_unbind,
+};
+EXPORT_SYMBOL_GPL(wcd_sdw_component_ops);
+
+int wcd_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
+{
+ struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
+
+ if (regmap && status == SDW_SLAVE_ATTACHED) {
+ /* Write out any cached changes that happened between probe and attach */
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_update_status);
+
+int wcd_bus_config(struct sdw_slave *slave, struct sdw_bus_params *params)
+{
+ sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), 0x01);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_bus_config);
+
+int wcd_interrupt_callback(struct sdw_slave *slave, struct irq_domain *slave_irq,
+ unsigned int wcd_intr_status0, unsigned int wcd_intr_status1,
+ unsigned int wcd_intr_status2)
+{
+ struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
+ u32 sts1, sts2, sts3;
+
+ do {
+ handle_nested_irq(irq_find_mapping(slave_irq, 0));
+ regmap_read(regmap, wcd_intr_status0, &sts1);
+ regmap_read(regmap, wcd_intr_status1, &sts2);
+ regmap_read(regmap, wcd_intr_status2, &sts3);
+
+ } while (sts1 || sts2 || sts3);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wcd_interrupt_callback);
+
+MODULE_DESCRIPTION("Common Qualcomm WCD Codec helpers driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd-common.h b/sound/soc/codecs/wcd-common.h
new file mode 100644
index 000000000000..d5c156e641fc
--- /dev/null
+++ b/sound/soc/codecs/wcd-common.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __WCD_COMMON_H__
+#define __WCD_COMMON_H__
+
+struct device;
+struct sdw_slave;
+struct sdw_bus_params;
+struct irq_domain;
+enum sdw_slave_status;
+
+#define WCD_MAX_MICBIAS 4
+
+struct wcd_sdw_ch_info {
+ int port_num;
+ unsigned int ch_mask;
+ unsigned int master_ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask) \
+ [id] = { \
+ .port_num = pn, \
+ .ch_mask = cmask, \
+ .master_ch_mask = cmask, \
+ }
+
+struct wcd_common {
+ struct device *dev;
+ int max_bias;
+ u32 micb_mv[WCD_MAX_MICBIAS];
+ u32 micb_vout[WCD_MAX_MICBIAS];
+};
+
+extern const struct component_ops wcd_sdw_component_ops;
+int wcd_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv);
+int wcd_dt_parse_micbias_info(struct wcd_common *common);
+int wcd_update_status(struct sdw_slave *slave, enum sdw_slave_status status);
+int wcd_bus_config(struct sdw_slave *slave, struct sdw_bus_params *params);
+int wcd_interrupt_callback(struct sdw_slave *slave, struct irq_domain *slave_irq,
+ unsigned int wcd_intr_status0, unsigned int wcd_intr_status1,
+ unsigned int wcd_intr_status2);
+
+#endif /* __WCD_COMMON_H__ */
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
new file mode 100644
index 000000000000..26ebcdadeb7d
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -0,0 +1,1647 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/printk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "wcd-mbhc-v2.h"
+
+#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
+#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
+#define GND_MIC_SWAP_THRESHOLD 4
+#define GND_MIC_USBC_SWAP_THRESHOLD 2
+#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
+#define HPHL_CROSS_CONN_THRESHOLD 100
+#define HS_VREF_MIN_VAL 1400
+#define FAKE_REM_RETRY_ATTEMPTS 3
+#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700
+#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75
+#define WCD_MBHC_ADC_MICBIAS_MV 1800
+#define WCD_MBHC_FAKE_INS_RETRY 4
+
+#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
+ SND_JACK_MECHANICAL)
+
+#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5)
+
+enum wcd_mbhc_adc_mux_ctl {
+ MUX_CTL_AUTO = 0,
+ MUX_CTL_IN2P,
+ MUX_CTL_IN3P,
+ MUX_CTL_IN4P,
+ MUX_CTL_HPH_L,
+ MUX_CTL_HPH_R,
+ MUX_CTL_NONE,
+};
+
+struct wcd_mbhc {
+ struct device *dev;
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ struct wcd_mbhc_config *cfg;
+ const struct wcd_mbhc_cb *mbhc_cb;
+ const struct wcd_mbhc_intr *intr_ids;
+ const struct wcd_mbhc_field *fields;
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ /* Work to handle plug report */
+ struct work_struct mbhc_plug_detect_work;
+ /* Work to correct accessory type */
+ struct work_struct correct_plug_swch;
+ struct mutex lock;
+ int buttons_pressed;
+ u32 hph_status; /* track headhpone status */
+ u8 current_plug;
+ unsigned int swap_thr;
+ bool is_btn_press;
+ bool in_swch_irq_handler;
+ bool hs_detect_work_stop;
+ bool is_hs_recording;
+ bool extn_cable_hph_rem;
+ bool force_linein;
+ bool impedance_detect;
+ unsigned long event_state;
+ unsigned long jiffies_atreport;
+ /* impedance of hphl and hphr */
+ uint32_t zl, zr;
+ /* Holds type of Headset - Mono/Stereo */
+ enum wcd_mbhc_hph_type hph_type;
+ /* Holds mbhc detection method - ADC/Legacy */
+ int mbhc_detection_logic;
+};
+
+static inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc,
+ int field, int val)
+{
+ if (!mbhc->fields[field].reg)
+ return 0;
+
+ return snd_soc_component_write_field(mbhc->component,
+ mbhc->fields[field].reg,
+ mbhc->fields[field].mask, val);
+}
+
+static inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field)
+{
+ if (!mbhc->fields[field].reg)
+ return 0;
+
+ return snd_soc_component_read_field(mbhc->component,
+ mbhc->fields[field].reg,
+ mbhc->fields[field].mask);
+}
+
+static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
+{
+ u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val);
+}
+
+static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low,
+ mbhc->cfg->btn_high,
+ mbhc->cfg->num_btn, micbias);
+}
+
+static void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc,
+ const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
+{
+
+ /*
+ * Some codecs handle micbias/pullup enablement in codec
+ * drivers itself and micbias is not needed for regular
+ * plug type detection. So if micbias_control callback function
+ * is defined, just return.
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ return;
+
+ switch (cs_mb_en) {
+ case WCD_MBHC_EN_CS:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ /* Program Button threshold registers as per CS */
+ wcd_program_btn_threshold(mbhc, false);
+ break;
+ case WCD_MBHC_EN_MB:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ /* Disable PULL_UP_EN & enable MICBIAS */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2);
+ /* Program Button threshold registers as per MICBIAS */
+ wcd_program_btn_threshold(mbhc, true);
+ break;
+ case WCD_MBHC_EN_PULLUP:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1);
+ /* Program Button threshold registers as per MICBIAS */
+ wcd_program_btn_threshold(mbhc, true);
+ break;
+ case WCD_MBHC_EN_NONE:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
+ break;
+ default:
+ dev_err(mbhc->dev, "%s: Invalid parameter", __func__);
+ break;
+ }
+}
+
+int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
+{
+
+ struct snd_soc_component *component;
+ bool micbias2 = false;
+
+ if (!mbhc)
+ return 0;
+
+ component = mbhc->component;
+
+ if (mbhc->mbhc_cb->micbias_enable_status)
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2);
+
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
+ mbhc->is_hs_recording = true;
+ break;
+ case WCD_EVENT_POST_MICBIAS_2_ON:
+ /* Disable current source if micbias2 enabled */
+ if (mbhc->mbhc_cb->mbhc_micbias_control) {
+ if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ } else {
+ mbhc->is_hs_recording = true;
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ }
+ break;
+ case WCD_EVENT_PRE_MICBIAS_2_OFF:
+ /*
+ * Before MICBIAS_2 is turned off, if FSM is enabled,
+ * make sure current source is enabled so as to detect
+ * button press/release events
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) {
+ if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ }
+ break;
+ /* MICBIAS usage change */
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
+ mbhc->is_hs_recording = false;
+ break;
+ case WCD_EVENT_POST_MICBIAS_2_OFF:
+ if (!mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->is_hs_recording = false;
+
+ /* Enable PULL UP if PA's are enabled */
+ if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
+ (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state)))
+ /* enable pullup and cs, disable mb */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
+ else
+ /* enable current source and disable mb, pullup*/
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
+
+ break;
+ case WCD_EVENT_POST_HPHL_PA_OFF:
+ clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, pullup & enable cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
+ break;
+ case WCD_EVENT_POST_HPHR_PA_OFF:
+ clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, pullup & enable cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
+ break;
+ case WCD_EVENT_PRE_HPHL_PA_ON:
+ set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, enable pullup & cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
+ break;
+ case WCD_EVENT_PRE_HPHR_PA_ON:
+ set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, enable pullup & cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
+
+static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
+{
+ return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+}
+
+static void wcd_micbias_disable(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
+
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false);
+
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(component);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
+ }
+}
+
+static void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc,
+ enum snd_jack_types jack_type)
+{
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) {
+ snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
+ mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
+ }
+
+ wcd_micbias_disable(mbhc);
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+ mbhc->zl = mbhc->zr = 0;
+ snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK);
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ mbhc->force_linein = false;
+}
+
+static void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc)
+{
+
+ if (!mbhc->impedance_detect)
+ return;
+
+ if (mbhc->cfg->linein_th != 0) {
+ u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
+ /* Set MUX_CTL to AUTO for Z-det */
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
+ }
+}
+
+static void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc,
+ enum snd_jack_types jack_type)
+{
+ bool is_pa_on;
+ /*
+ * Report removal of current jack type.
+ * Headphone to headset shouldn't report headphone
+ * removal.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
+ jack_type == SND_JACK_HEADPHONE)
+ mbhc->hph_status &= ~SND_JACK_HEADSET;
+
+ /* Report insertion */
+ switch (jack_type) {
+ case SND_JACK_HEADPHONE:
+ mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
+ break;
+ case SND_JACK_HEADSET:
+ mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
+ mbhc->jiffies_atreport = jiffies;
+ break;
+ case SND_JACK_LINEOUT:
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ break;
+ default:
+ break;
+ }
+
+
+ is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
+
+ if (!is_pa_on) {
+ wcd_mbhc_compute_impedance(mbhc);
+ if ((mbhc->zl > mbhc->cfg->linein_th) &&
+ (mbhc->zr > mbhc->cfg->linein_th) &&
+ (jack_type == SND_JACK_HEADPHONE)) {
+ jack_type = SND_JACK_LINEOUT;
+ mbhc->force_linein = true;
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ if (mbhc->hph_status) {
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT);
+ snd_soc_jack_report(mbhc->jack, mbhc->hph_status,
+ WCD_MBHC_JACK_MASK);
+ }
+ }
+ }
+
+ /* Do not calculate impedance again for lineout
+ * as during playback pa is on and impedance values
+ * will not be correct resulting in lineout detected
+ * as headphone.
+ */
+ if (is_pa_on && mbhc->force_linein) {
+ jack_type = SND_JACK_LINEOUT;
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ if (mbhc->hph_status) {
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT);
+ snd_soc_jack_report(mbhc->jack, mbhc->hph_status,
+ WCD_MBHC_JACK_MASK);
+ }
+ }
+
+ mbhc->hph_status |= jack_type;
+
+ if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control)
+ mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false);
+
+ snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL),
+ WCD_MBHC_JACK_MASK);
+}
+
+static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+
+ WARN_ON(!mutex_is_locked(&mbhc->lock));
+
+ if (!insertion) /* Report removal */
+ wcd_mbhc_report_plug_removal(mbhc, jack_type);
+ else
+ wcd_mbhc_report_plug_insertion(mbhc, jack_type);
+
+}
+
+static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
+ struct work_struct *work)
+{
+ mbhc->hs_detect_work_stop = true;
+ mutex_unlock(&mbhc->lock);
+ cancel_work_sync(work);
+ mutex_lock(&mbhc->lock);
+}
+
+static void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc)
+{
+ /* cancel pending button press */
+ wcd_cancel_btn_work(mbhc);
+ /* cancel correct work function */
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+}
+
+static void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
+{
+ wcd_mbhc_cancel_pending_work(mbhc);
+ /* Report extension cable */
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ /*
+ * Disable HPHL trigger and MIC Schmitt triggers.
+ * Setup for insertion detection.
+ */
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE);
+ /* Disable HW FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3);
+
+ /* Set the detection type appropriately */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
+ enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
+}
+
+static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+ if (mbhc->current_plug == plug_type)
+ return;
+
+ mutex_lock(&mbhc->lock);
+
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ break;
+ case MBHC_PLUG_TYPE_HIGH_HPH:
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ break;
+ case MBHC_PLUG_TYPE_GND_MIC_SWAP:
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ break;
+ default:
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ break;
+ }
+ mutex_unlock(&mbhc->lock);
+}
+
+static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
+ struct work_struct *work)
+{
+ WARN_ON(!mutex_is_locked(&mbhc->lock));
+ mbhc->hs_detect_work_stop = false;
+ schedule_work(work);
+}
+
+static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ WARN_ON(!mutex_is_locked(&mbhc->lock));
+
+ if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+ mbhc->mbhc_cb->hph_pull_down_ctrl(component, false);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control) {
+ mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2,
+ MICB_ENABLE);
+ wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+ }
+}
+
+static void mbhc_plug_detect_fn(struct work_struct *work)
+{
+ struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work);
+ struct snd_soc_component *component = mbhc->component;
+ enum snd_jack_types jack_type;
+ bool detection_type;
+
+ mutex_lock(&mbhc->lock);
+
+ mbhc->in_swch_irq_handler = true;
+
+ wcd_mbhc_cancel_pending_work(mbhc);
+
+ detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE);
+
+ /* Set the detection type appropriately */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type);
+
+ /* Enable micbias ramp */
+ if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
+ mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
+
+ if (detection_type) {
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
+ goto exit;
+ /* Make sure MASTER_BIAS_CTL is enabled */
+ mbhc->mbhc_cb->mbhc_bias(component, true);
+ mbhc->is_btn_press = false;
+ wcd_mbhc_adc_detect_plug_type(mbhc);
+ } else {
+ /* Disable HW FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ mbhc->extn_cable_hph_rem = false;
+
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE)
+ goto exit;
+
+ mbhc->is_btn_press = false;
+ switch (mbhc->current_plug) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ jack_type = SND_JACK_HEADSET;
+ break;
+ case MBHC_PLUG_TYPE_HIGH_HPH:
+ if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0);
+ jack_type = SND_JACK_LINEOUT;
+ break;
+ case MBHC_PLUG_TYPE_GND_MIC_SWAP:
+ dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n");
+ goto exit;
+ default:
+ dev_err(mbhc->dev, "Invalid current plug: %d\n",
+ mbhc->current_plug);
+ goto exit;
+ }
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, jack_type);
+ }
+
+exit:
+ mbhc->in_swch_irq_handler = false;
+ mutex_unlock(&mbhc->lock);
+}
+
+static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+
+ if (!mbhc->cfg->typec_analog_mux)
+ schedule_work(&mbhc->mbhc_plug_detect_work);
+
+ return IRQ_HANDLED;
+}
+
+int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc)
+{
+
+ if (!mbhc || !mbhc->cfg->typec_analog_mux)
+ return -EINVAL;
+
+ if (mbhc->mbhc_cb->clk_setup)
+ mbhc->mbhc_cb->clk_setup(mbhc->component, false);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0);
+
+ schedule_work(&mbhc->mbhc_plug_detect_work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug);
+
+int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc)
+{
+ if (!mbhc || !mbhc->cfg->typec_analog_mux)
+ return -EINVAL;
+
+ if (mbhc->mbhc_cb->clk_setup)
+ mbhc->mbhc_cb->clk_setup(mbhc->component, true);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
+
+ schedule_work(&mbhc->mbhc_plug_detect_work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug);
+
+static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
+{
+ int mask = 0;
+ int btn;
+
+ btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT);
+
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ default:
+ break;
+ }
+
+ return mask;
+}
+
+static void wcd_btn_long_press_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
+
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+ snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+}
+
+static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int mask;
+ unsigned long msec_val;
+
+ mutex_lock(&mbhc->lock);
+ wcd_cancel_btn_work(mbhc);
+ mbhc->is_btn_press = true;
+ msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
+
+ /* Too short, ignore button press */
+ if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN)
+ goto done;
+
+ /* If switch interrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler)
+ goto done;
+
+ /* Plug isn't headset, ignore button press */
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET)
+ goto done;
+
+ mask = wcd_mbhc_get_button_mask(mbhc);
+ mbhc->buttons_pressed |= mask;
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0)
+ WARN(1, "Button pressed twice without release event\n");
+done:
+ mutex_unlock(&mbhc->lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int ret;
+
+ mutex_lock(&mbhc->lock);
+ if (mbhc->is_btn_press)
+ mbhc->is_btn_press = false;
+ else /* fake btn press */
+ goto exit;
+
+ if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK))
+ goto exit;
+
+ ret = wcd_cancel_btn_work(mbhc);
+ if (ret == 0) { /* Reporting long button release event */
+ snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
+ } else {
+ if (!mbhc->in_swch_irq_handler) {
+ /* Reporting btn press n Release */
+ snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
+ }
+ }
+ mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
+exit:
+ mutex_unlock(&mbhc->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr)
+{
+
+ /* TODO Find a better way to report this to Userspace */
+ dev_err(mbhc->dev, "MBHC Over Current on %s detected\n",
+ hphr ? "HPHR" : "HPHL");
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
+{
+ return wcd_mbhc_hph_ocp_irq(data, false);
+}
+
+static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
+{
+ return wcd_mbhc_hph_ocp_irq(data, true);
+}
+
+static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_component *component = mbhc->component;
+ int ret;
+
+ ret = pm_runtime_get_sync(component->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(component->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(component->dev);
+ return ret;
+ }
+
+ mutex_lock(&mbhc->lock);
+
+ if (mbhc->cfg->typec_analog_mux)
+ mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD;
+ else
+ mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD;
+
+ /* setup HS detection */
+ if (mbhc->mbhc_cb->hph_pull_up_control_v2)
+ mbhc->mbhc_cb->hph_pull_up_control_v2(component,
+ mbhc->cfg->typec_analog_mux ?
+ HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT);
+ else if (mbhc->mbhc_cb->hph_pull_up_control)
+ mbhc->mbhc_cb->hph_pull_up_control(component,
+ mbhc->cfg->typec_analog_mux ?
+ I_OFF : I_DEFAULT);
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+ mbhc->cfg->typec_analog_mux ? 0 : 3);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
+ if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
+ mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
+
+ /* Plug detect is triggered manually if analog goes through USBCC */
+ if (mbhc->cfg->typec_analog_mux)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0);
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
+
+ if (mbhc->cfg->typec_analog_mux)
+ /* Insertion debounce set to 48ms */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4);
+ else
+ /* Insertion debounce set to 96ms */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
+
+ /* Button Debounce set to 16ms */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
+
+ /* enable bias */
+ mbhc->mbhc_cb->mbhc_bias(component, true);
+ /* enable MBHC clock */
+ if (mbhc->mbhc_cb->clk_setup)
+ mbhc->mbhc_cb->clk_setup(component,
+ mbhc->cfg->typec_analog_mux ? false : true);
+
+ /* program HS_VREF value */
+ wcd_program_hs_vref(mbhc);
+
+ wcd_program_btn_threshold(mbhc, false);
+
+ mutex_unlock(&mbhc->lock);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
+{
+ int micbias = 0;
+
+ if (mbhc->mbhc_cb->get_micbias_val) {
+ mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
+ } else {
+ u8 vout_ctl = 0;
+ /* Read MBHC Micbias (Mic Bias2) voltage */
+ vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
+ /* Formula for getting micbias from vout
+ * micbias = 1.0V + VOUT_CTL * 50mV
+ */
+ micbias = 1000 + (vout_ctl * 50);
+ }
+ return micbias;
+}
+
+static int wcd_get_voltage_from_adc(u8 val, int micbias)
+{
+ /* Formula for calculating voltage from ADC
+ * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
+ */
+ return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
+}
+
+static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
+{
+ u8 adc_result;
+ int output_mv;
+ int retry = 3;
+ u8 adc_en;
+
+ /* Pre-requisites for ADC continuous measurement */
+ /* Read legacy electircal detection and disable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
+ /* Set ADC to continuous measurement */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
+ /* Read ADC Enable bit to restore after adc measurement */
+ adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
+ /* Disable ADC_ENABLE bit */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ /* Disable MBHC FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ /* Set the MUX selection to IN2P */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
+ /* Enable MBHC FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ /* Enable ADC_ENABLE bit */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
+
+ while (retry--) {
+ /* wait for 3 msec before reading ADC result */
+ usleep_range(3000, 3100);
+ adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
+ }
+
+ /* Restore ADC Enable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
+ /* Get voltage from ADC result */
+ output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
+
+ return output_mv;
+}
+
+static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
+{
+ struct device *dev = mbhc->dev;
+ u8 adc_timeout = 0;
+ u8 adc_complete = 0;
+ u8 adc_result;
+ int retry = 6;
+ int ret;
+ int output_mv = 0;
+ u8 adc_en;
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ /* Read ADC Enable bit to restore after adc measurement */
+ adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
+ /* Trigger ADC one time measurement */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ /* Set the appropriate MUX selection */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
+
+ while (retry--) {
+ /* wait for 600usec to get adc results */
+ usleep_range(600, 610);
+
+ /* check for ADC Timeout */
+ adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
+ if (adc_timeout)
+ continue;
+
+ /* Read ADC complete bit */
+ adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
+ if (!adc_complete)
+ continue;
+
+ /* Read ADC result */
+ adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
+
+ /* Get voltage from ADC result */
+ output_mv = wcd_get_voltage_from_adc(adc_result,
+ wcd_mbhc_get_micbias(mbhc));
+ break;
+ }
+
+ /* Restore ADC Enable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
+
+ if (retry <= 0) {
+ dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
+ __func__, adc_complete, adc_timeout);
+ ret = -EINVAL;
+ } else {
+ ret = output_mv;
+ }
+
+ return ret;
+}
+
+/* To determine if cross connection occurred */
+static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
+{
+ u8 adc_mode, elect_ctl, adc_en, fsm_en;
+ int hphl_adc_res, hphr_adc_res;
+ bool is_cross_conn = false;
+
+ /* If PA is enabled, dont check for cross-connection */
+ if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
+ return -EINVAL;
+
+ /* Read legacy electircal detection and disable */
+ elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+
+ /* Read and set ADC to single measurement */
+ adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
+ /* Read ADC Enable bit to restore after adc measurement */
+ adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
+ /* Read FSM status */
+ fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
+
+ /* Get adc result for HPH L */
+ hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
+ if (hphl_adc_res < 0)
+ return hphl_adc_res;
+
+ /* Get adc result for HPH R in mV */
+ hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
+ if (hphr_adc_res < 0)
+ return hphr_adc_res;
+
+ if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
+ hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
+ is_cross_conn = true;
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ /* Set the MUX selection to Auto */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ /* Restore ADC Enable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
+ /* Restore ADC mode */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
+ /* Restore FSM state */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
+ /* Restore electrical detection */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
+
+ return is_cross_conn;
+}
+
+static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
+{
+ int hs_threshold, micbias_mv;
+
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ if (mbhc->cfg->hs_thr) {
+ if (mbhc->cfg->micb_mv == micbias_mv)
+ hs_threshold = mbhc->cfg->hs_thr;
+ else
+ hs_threshold = (mbhc->cfg->hs_thr *
+ micbias_mv) / mbhc->cfg->micb_mv;
+ } else {
+ hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
+ micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
+ }
+ return hs_threshold;
+}
+
+static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
+{
+ int hph_threshold, micbias_mv;
+
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ if (mbhc->cfg->hph_thr) {
+ if (mbhc->cfg->micb_mv == micbias_mv)
+ hph_threshold = mbhc->cfg->hph_thr;
+ else
+ hph_threshold = (mbhc->cfg->hph_thr *
+ micbias_mv) / mbhc->cfg->micb_mv;
+ } else {
+ hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
+ micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
+ }
+ return hph_threshold;
+}
+
+static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+ bool micbias2 = false;
+
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ if (mbhc->mbhc_cb->micbias_enable_status)
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
+ MIC_BIAS_2);
+
+ if (!mbhc->is_hs_recording && !micbias2)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ break;
+ default:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ break;
+
+ }
+}
+
+static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
+{
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADSET:
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ if (mbhc->mbhc_cb->bcs_enable)
+ mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
+ break;
+ default:
+ break;
+ }
+}
+
+static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
+
+{
+ enum wcd_mbhc_plug_type plug_type;
+ u32 hph_thr, hs_thr;
+
+ hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
+ hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
+
+ if (adc_result < hph_thr)
+ plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+ else if (adc_result > hs_thr)
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+ else
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+
+ return plug_type;
+}
+
+static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
+{
+ int hs_threshold, micbias_mv;
+
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
+ if (mbhc->cfg->micb_mv == micbias_mv)
+ hs_threshold = mbhc->cfg->hs_thr;
+ else
+ hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
+ } else {
+ hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
+ WCD_MBHC_ADC_MICBIAS_MV);
+ }
+ return hs_threshold;
+}
+
+static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
+{
+ bool is_spl_hs = false;
+ int output_mv, hs_threshold, hph_threshold;
+
+ if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ return false;
+
+ /* Bump up MIC_BIAS2 to 2.7V */
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
+ usleep_range(10000, 10100);
+
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+ hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
+ hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
+
+ if (!(output_mv > hs_threshold || output_mv < hph_threshold))
+ is_spl_hs = true;
+
+ /* Back MIC_BIAS2 to 1.8v if the type is not special headset */
+ if (!is_spl_hs) {
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
+ /* Add 10ms delay for micbias to settle */
+ usleep_range(10000, 10100);
+ }
+
+ return is_spl_hs;
+}
+
+static void wcd_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd_mbhc *mbhc;
+ struct snd_soc_component *component;
+ enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
+ unsigned long timeout;
+ int pt_gnd_mic_swap_cnt = 0;
+ int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
+ bool is_spl_hs = false;
+ bool is_pa_on;
+ int ret;
+
+ mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
+ component = mbhc->component;
+
+ ret = pm_runtime_get_sync(component->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(component->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(component->dev);
+ return;
+ }
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
+
+ /* Mask ADC COMPLETE interrupt */
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+
+ /* Check for cross connection */
+ do {
+ cross_conn = wcd_check_cross_conn(mbhc);
+ try++;
+ } while (try < mbhc->swap_thr);
+
+ if (cross_conn > 0) {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
+ plug_type);
+ goto correct_plug_type;
+ }
+
+ /* Find plug type */
+ output_mv = wcd_measure_adc_continuous(mbhc);
+ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
+
+ /*
+ * Report plug type if it is either headset or headphone
+ * else start the 3 sec loop
+ */
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
+ break;
+ default:
+ break;
+ }
+
+correct_plug_type:
+
+ /* Disable BCS slow insertion detection */
+ wcd_mbhc_bcs_enable(mbhc, plug_type, false);
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+
+ while (!time_after(jiffies, timeout)) {
+ if (mbhc->hs_detect_work_stop) {
+ wcd_micbias_disable(mbhc);
+ goto exit;
+ }
+
+ msleep(180);
+ /*
+ * Use ADC single mode to minimize the chance of missing out
+ * btn press/release for HEADSET type during correct work.
+ */
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
+ is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
+
+ if (output_mv > hs_threshold && !is_spl_hs) {
+ is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+
+ if (is_spl_hs) {
+ hs_threshold *= wcd_mbhc_get_micbias(mbhc);
+ hs_threshold /= micbias_mv;
+ }
+ }
+
+ if ((output_mv <= hs_threshold) && !is_pa_on) {
+ /* Check for cross connection*/
+ cross_conn = wcd_check_cross_conn(mbhc);
+ if (cross_conn > 0) { /* cross-connection */
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt < mbhc->swap_thr)
+ continue;
+ else
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ } else if (!cross_conn) { /* no cross connection */
+ pt_gnd_mic_swap_cnt = 0;
+ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
+ continue;
+ } else /* Error if (cross_conn < 0) */
+ continue;
+
+ if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) {
+ /* US_EU gpio present, flip switch */
+ if (mbhc->cfg->swap_gnd_mic) {
+ if (mbhc->cfg->swap_gnd_mic(component))
+ continue;
+ }
+ }
+ }
+
+ /* cable is extension cable */
+ if (output_mv > hs_threshold || mbhc->force_linein)
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+ }
+
+ wcd_mbhc_bcs_enable(mbhc, plug_type, true);
+
+ if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
+ if (is_spl_hs)
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
+ }
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+
+ /*
+ * Set DETECTION_DONE bit for HEADSET
+ * so that btn press/release interrupt can be generated.
+ * For other plug type, clear the bit.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADSET)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
+
+exit:
+ if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/)
+ mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
+
+ /*
+ * If plug type is corrected from special headset to headphone,
+ * clear the micbias enable flag, set micbias back to 1.8V and
+ * disable micbias.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
+ wcd_micbias_disable(mbhc);
+ /*
+ * Enable ADC COMPLETE interrupt for HEADPHONE.
+ * Btn release may happen after the correct work, ADC COMPLETE
+ * interrupt needs to be captured to correct plug type.
+ */
+ enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
+ }
+
+ if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+ mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
+
+ pm_runtime_put_autosuspend(component->dev);
+}
+
+static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ unsigned long timeout;
+ int adc_threshold, output_mv, retry = 0;
+
+ mutex_lock(&mbhc->lock);
+ timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
+ adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
+
+ do {
+ retry++;
+ /*
+ * read output_mv every 10ms to look for
+ * any change in IN2_P
+ */
+ usleep_range(10000, 10100);
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+
+ /* Check for fake removal */
+ if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
+ goto exit;
+ } while (!time_after(jiffies, timeout));
+
+ /*
+ * ADC COMPLETE and ELEC_REM interrupts are both enabled for
+ * HEADPHONE, need to reject the ADC COMPLETE interrupt which
+ * follows ELEC_REM one when HEADPHONE is removed.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+ mbhc->extn_cable_hph_rem = true;
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_elec_hs_report_unplug(mbhc);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+
+exit:
+ mutex_unlock(&mbhc->lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ u8 clamp_state;
+ u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
+
+ /*
+ * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
+ * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
+ * when HEADPHONE is removed.
+ */
+ if (mbhc->extn_cable_hph_rem == true) {
+ mbhc->extn_cable_hph_rem = false;
+ return IRQ_HANDLED;
+ }
+
+ do {
+ clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
+ if (clamp_state)
+ return IRQ_HANDLED;
+ /*
+ * check clamp for 120ms but at 30ms chunks to leave
+ * room for other interrupts to be processed
+ */
+ usleep_range(30000, 30100);
+ } while (--clamp_retry);
+
+ /*
+ * If current plug is headphone then there is no chance to
+ * get ADC complete interrupt, so connected cable should be
+ * headset not headphone.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
+ wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_HANDLED;
+}
+
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr)
+{
+ *zl = mbhc->zl;
+ *zr = mbhc->zr;
+
+ if (*zl && *zr)
+ return 0;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL(wcd_mbhc_get_impedance);
+
+void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
+{
+ mbhc->hph_type = hph_type;
+}
+EXPORT_SYMBOL(wcd_mbhc_set_hph_type);
+
+int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
+{
+ return mbhc->hph_type;
+}
+EXPORT_SYMBOL(wcd_mbhc_get_hph_type);
+
+int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
+ struct snd_soc_jack *jack)
+{
+ if (!mbhc || !cfg || !jack)
+ return -EINVAL;
+
+ mbhc->cfg = cfg;
+ mbhc->jack = jack;
+
+ return wcd_mbhc_initialise(mbhc);
+}
+EXPORT_SYMBOL(wcd_mbhc_start);
+
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ mbhc->hph_status = 0;
+ disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
+ disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
+}
+EXPORT_SYMBOL(wcd_mbhc_stop);
+
+int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
+{
+ struct device_node *np = dev->of_node;
+ int ret, i, microvolt;
+
+ if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
+ cfg->hphl_swh = false;
+ else
+ cfg->hphl_swh = true;
+
+ if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
+ cfg->gnd_swh = false;
+ else
+ cfg->gnd_swh = true;
+
+ ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
+ &microvolt);
+ if (ret)
+ dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
+ else
+ cfg->hs_thr = microvolt/1000;
+
+ ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
+ &microvolt);
+ if (ret)
+ dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt entry\n");
+ else
+ cfg->hph_thr = microvolt/1000;
+
+ ret = of_property_read_u32_array(np,
+ "qcom,mbhc-buttons-vthreshold-microvolt",
+ &cfg->btn_high[0],
+ WCD_MBHC_DEF_BUTTONS);
+ if (ret)
+ dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
+
+ for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
+ if (ret) /* default voltage */
+ cfg->btn_high[i] = 500000;
+ else
+ /* Micro to Milli Volts */
+ cfg->btn_high[i] = cfg->btn_high[i]/1000;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
+
+struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *intr_ids,
+ const struct wcd_mbhc_field *fields,
+ bool impedance_det_en)
+{
+ struct device *dev = component->dev;
+ struct wcd_mbhc *mbhc;
+ int ret;
+
+ if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
+ dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
+ if (!mbhc)
+ return ERR_PTR(-ENOMEM);
+
+ mbhc->component = component;
+ mbhc->dev = dev;
+ mbhc->intr_ids = intr_ids;
+ mbhc->mbhc_cb = mbhc_cb;
+ mbhc->fields = fields;
+ mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
+
+ if (mbhc_cb->compute_impedance)
+ mbhc->impedance_detect = impedance_det_en;
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
+
+ mutex_init(&mbhc->lock);
+
+ INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
+ INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn);
+
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
+ wcd_mbhc_mech_plug_detect_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "mbhc sw intr", mbhc);
+ if (ret)
+ goto err_free_mbhc;
+
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
+ wcd_mbhc_btn_press_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Button Press detect", mbhc);
+ if (ret)
+ goto err_free_sw_intr;
+
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
+ wcd_mbhc_btn_release_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Button Release detect", mbhc);
+ if (ret)
+ goto err_free_btn_press_intr;
+
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
+ wcd_mbhc_adc_hs_ins_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Elect Insert", mbhc);
+ if (ret)
+ goto err_free_btn_release_intr;
+
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
+ wcd_mbhc_adc_hs_rem_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Elect Remove", mbhc);
+ if (ret)
+ goto err_free_hs_ins_intr;
+
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
+
+ ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
+ wcd_mbhc_hphl_ocp_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPH_L OCP detect", mbhc);
+ if (ret)
+ goto err_free_hs_rem_intr;
+
+ ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
+ wcd_mbhc_hphr_ocp_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPH_R OCP detect", mbhc);
+ if (ret)
+ goto err_free_hph_left_ocp;
+
+ return mbhc;
+
+err_free_hph_left_ocp:
+ free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
+err_free_hs_rem_intr:
+ free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+err_free_hs_ins_intr:
+ free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+err_free_btn_release_intr:
+ free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
+err_free_btn_press_intr:
+ free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
+err_free_sw_intr:
+ free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
+err_free_mbhc:
+ kfree(mbhc);
+
+ dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(wcd_mbhc_init);
+
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+ free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
+ free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
+
+ mutex_lock(&mbhc->lock);
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+ cancel_work_sync(&mbhc->mbhc_plug_detect_work);
+ mutex_unlock(&mbhc->lock);
+
+ kfree(mbhc);
+}
+EXPORT_SYMBOL(wcd_mbhc_deinit);
+
+static int __init mbhc_init(void)
+{
+ return 0;
+}
+
+static void __exit mbhc_exit(void)
+{
+}
+
+module_init(mbhc_init);
+module_exit(mbhc_exit);
+
+MODULE_DESCRIPTION("wcd MBHC v2 module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
new file mode 100644
index 000000000000..a5d52b9643f5
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __WCD_MBHC_V2_H__
+#define __WCD_MBHC_V2_H__
+
+#include <sound/jack.h>
+
+#define WCD_MBHC_FIELD(id, rreg, rmask) \
+ [id] = { .reg = rreg, .mask = rmask }
+
+enum wcd_mbhc_field_function {
+ WCD_MBHC_L_DET_EN,
+ WCD_MBHC_GND_DET_EN,
+ WCD_MBHC_MECH_DETECTION_TYPE,
+ WCD_MBHC_MIC_CLAMP_CTL,
+ WCD_MBHC_ELECT_DETECTION_TYPE,
+ WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+ WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL,
+ WCD_MBHC_HPHL_PLUG_TYPE,
+ WCD_MBHC_GND_PLUG_TYPE,
+ WCD_MBHC_SW_HPH_LP_100K_TO_GND,
+ WCD_MBHC_ELECT_SCHMT_ISRC,
+ WCD_MBHC_FSM_EN,
+ WCD_MBHC_INSREM_DBNC,
+ WCD_MBHC_BTN_DBNC,
+ WCD_MBHC_HS_VREF,
+ WCD_MBHC_HS_COMP_RESULT,
+ WCD_MBHC_IN2P_CLAMP_STATE,
+ WCD_MBHC_MIC_SCHMT_RESULT,
+ WCD_MBHC_HPHL_SCHMT_RESULT,
+ WCD_MBHC_HPHR_SCHMT_RESULT,
+ WCD_MBHC_OCP_FSM_EN,
+ WCD_MBHC_BTN_RESULT,
+ WCD_MBHC_BTN_ISRC_CTL,
+ WCD_MBHC_ELECT_RESULT,
+ WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */
+ WCD_MBHC_HPH_CNP_WG_TIME,
+ WCD_MBHC_HPHR_PA_EN,
+ WCD_MBHC_HPHL_PA_EN,
+ WCD_MBHC_HPH_PA_EN,
+ WCD_MBHC_SWCH_LEVEL_REMOVE,
+ WCD_MBHC_PULLDOWN_CTRL,
+ WCD_MBHC_ANC_DET_EN,
+ WCD_MBHC_FSM_STATUS,
+ WCD_MBHC_MUX_CTL,
+ WCD_MBHC_MOISTURE_STATUS,
+ WCD_MBHC_HPHR_GND,
+ WCD_MBHC_HPHL_GND,
+ WCD_MBHC_HPHL_OCP_DET_EN,
+ WCD_MBHC_HPHR_OCP_DET_EN,
+ WCD_MBHC_HPHL_OCP_STATUS,
+ WCD_MBHC_HPHR_OCP_STATUS,
+ WCD_MBHC_ADC_EN,
+ WCD_MBHC_ADC_COMPLETE,
+ WCD_MBHC_ADC_TIMEOUT,
+ WCD_MBHC_ADC_RESULT,
+ WCD_MBHC_MICB2_VOUT,
+ WCD_MBHC_ADC_MODE,
+ WCD_MBHC_DETECTION_DONE,
+ WCD_MBHC_ELECT_ISRC_EN,
+ WCD_MBHC_REG_FUNC_MAX,
+};
+
+#define WCD_MBHC_DEF_BUTTONS 8
+#define WCD_MBHC_KEYCODE_NUM 8
+#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100
+#define WCD_MBHC_THR_HS_MICB_MV 2700
+#define WCD_MONO_HS_MIN_THR 2
+
+enum wcd_mbhc_detect_logic {
+ WCD_DETECTION_LEGACY,
+ WCD_DETECTION_ADC,
+};
+
+enum wcd_mbhc_cs_mb_en_flag {
+ WCD_MBHC_EN_CS = 0,
+ WCD_MBHC_EN_MB,
+ WCD_MBHC_EN_PULLUP,
+ WCD_MBHC_EN_NONE,
+};
+
+enum {
+ WCD_MBHC_ELEC_HS_INS,
+ WCD_MBHC_ELEC_HS_REM,
+};
+
+enum wcd_mbhc_plug_type {
+ MBHC_PLUG_TYPE_INVALID = -1,
+ MBHC_PLUG_TYPE_NONE,
+ MBHC_PLUG_TYPE_HEADSET,
+ MBHC_PLUG_TYPE_HEADPHONE,
+ MBHC_PLUG_TYPE_HIGH_HPH,
+ MBHC_PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum pa_dac_ack_flags {
+ WCD_MBHC_HPHL_PA_OFF_ACK = 0,
+ WCD_MBHC_HPHR_PA_OFF_ACK,
+};
+
+enum wcd_mbhc_btn_det_mem {
+ WCD_MBHC_BTN_DET_V_BTN_LOW,
+ WCD_MBHC_BTN_DET_V_BTN_HIGH
+};
+
+enum {
+ MIC_BIAS_1 = 1,
+ MIC_BIAS_2,
+ MIC_BIAS_3,
+ MIC_BIAS_4
+};
+
+enum {
+ MICB_PULLUP_ENABLE,
+ MICB_PULLUP_DISABLE,
+ MICB_ENABLE,
+ MICB_DISABLE,
+};
+
+enum wcd_notify_event {
+ WCD_EVENT_INVALID,
+ /* events for micbias ON and OFF */
+ WCD_EVENT_PRE_MICBIAS_2_OFF,
+ WCD_EVENT_POST_MICBIAS_2_OFF,
+ WCD_EVENT_PRE_MICBIAS_2_ON,
+ WCD_EVENT_POST_MICBIAS_2_ON,
+ WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF,
+ WCD_EVENT_PRE_DAPM_MICBIAS_2_ON,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON,
+ /* events for PA ON and OFF */
+ WCD_EVENT_PRE_HPHL_PA_ON,
+ WCD_EVENT_POST_HPHL_PA_OFF,
+ WCD_EVENT_PRE_HPHR_PA_ON,
+ WCD_EVENT_POST_HPHR_PA_OFF,
+ WCD_EVENT_PRE_HPHL_PA_OFF,
+ WCD_EVENT_PRE_HPHR_PA_OFF,
+ WCD_EVENT_OCP_OFF,
+ WCD_EVENT_OCP_ON,
+ WCD_EVENT_LAST,
+};
+
+enum wcd_mbhc_event_state {
+ WCD_MBHC_EVENT_PA_HPHL,
+ WCD_MBHC_EVENT_PA_HPHR,
+};
+
+enum wcd_mbhc_hph_type {
+ WCD_MBHC_HPH_NONE = 0,
+ WCD_MBHC_HPH_MONO,
+ WCD_MBHC_HPH_STEREO,
+};
+
+/*
+ * These enum definitions are directly mapped to the register
+ * definitions
+ */
+
+enum mbhc_hs_pullup_iref {
+ I_DEFAULT = -1,
+ I_OFF = 0,
+ I_1P0_UA,
+ I_2P0_UA,
+ I_3P0_UA,
+};
+
+enum mbhc_hs_pullup_iref_v2 {
+ HS_PULLUP_I_DEFAULT = -1,
+ HS_PULLUP_I_3P0_UA = 0,
+ HS_PULLUP_I_2P25_UA,
+ HS_PULLUP_I_1P5_UA,
+ HS_PULLUP_I_0P75_UA,
+ HS_PULLUP_I_1P125_UA = 0x05,
+ HS_PULLUP_I_0P375_UA = 0x07,
+ HS_PULLUP_I_2P0_UA,
+ HS_PULLUP_I_1P0_UA = 0x0A,
+ HS_PULLUP_I_0P5_UA,
+ HS_PULLUP_I_0P25_UA = 0x0F,
+ HS_PULLUP_I_0P125_UA = 0x17,
+ HS_PULLUP_I_OFF,
+};
+
+enum mbhc_moisture_rref {
+ R_OFF,
+ R_24_KOHM,
+ R_84_KOHM,
+ R_184_KOHM,
+};
+
+struct wcd_mbhc_config {
+ int btn_high[WCD_MBHC_DEF_BUTTONS];
+ int btn_low[WCD_MBHC_DEF_BUTTONS];
+ int v_hs_max;
+ int num_btn;
+ bool mono_stero_detection;
+ bool typec_analog_mux;
+ bool (*swap_gnd_mic)(struct snd_soc_component *component);
+ bool hs_ext_micbias;
+ bool gnd_det_en;
+ uint32_t linein_th;
+ bool moisture_en;
+ int mbhc_micbias;
+ int anc_micbias;
+ bool moisture_duty_cycle_en;
+ bool hphl_swh; /*track HPHL switch NC / NO */
+ bool gnd_swh; /*track GND switch NC / NO */
+ u32 hs_thr;
+ u32 hph_thr;
+ u32 micb_mv;
+ u32 moist_vref;
+ u32 moist_iref;
+ u32 moist_rref;
+};
+
+struct wcd_mbhc_intr {
+ int mbhc_sw_intr;
+ int mbhc_btn_press_intr;
+ int mbhc_btn_release_intr;
+ int mbhc_hs_ins_intr;
+ int mbhc_hs_rem_intr;
+ int hph_left_ocp;
+ int hph_right_ocp;
+};
+
+struct wcd_mbhc_field {
+ u16 reg;
+ u8 mask;
+};
+
+struct wcd_mbhc;
+
+struct wcd_mbhc_cb {
+ void (*update_cross_conn_thr)(struct snd_soc_component *component);
+ void (*get_micbias_val)(struct snd_soc_component *component, int *mb);
+ void (*bcs_enable)(struct snd_soc_component *component, bool bcs_enable);
+ void (*compute_impedance)(struct snd_soc_component *component,
+ uint32_t *zl, uint32_t *zr);
+ void (*set_micbias_value)(struct snd_soc_component *component);
+ void (*set_auto_zeroing)(struct snd_soc_component *component,
+ bool enable);
+ void (*clk_setup)(struct snd_soc_component *component, bool enable);
+ bool (*micbias_enable_status)(struct snd_soc_component *component, int micb_num);
+ void (*mbhc_bias)(struct snd_soc_component *component, bool enable);
+ void (*set_btn_thr)(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias);
+ void (*hph_pull_up_control)(struct snd_soc_component *component,
+ enum mbhc_hs_pullup_iref);
+ int (*mbhc_micbias_control)(struct snd_soc_component *component,
+ int micb_num, int req);
+ void (*mbhc_micb_ramp_control)(struct snd_soc_component *component,
+ bool enable);
+ bool (*extn_use_mb)(struct snd_soc_component *component);
+ int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_component *component,
+ int micb_num, bool req_en);
+ void (*mbhc_gnd_det_ctrl)(struct snd_soc_component *component,
+ bool enable);
+ void (*hph_pull_down_ctrl)(struct snd_soc_component *component,
+ bool enable);
+ void (*mbhc_moisture_config)(struct snd_soc_component *component);
+ void (*update_anc_state)(struct snd_soc_component *component,
+ bool enable, int anc_num);
+ void (*hph_pull_up_control_v2)(struct snd_soc_component *component,
+ int pull_up_cur);
+ bool (*mbhc_get_moisture_status)(struct snd_soc_component *component);
+ void (*mbhc_moisture_polling_ctrl)(struct snd_soc_component *component, bool enable);
+ void (*mbhc_moisture_detect_en)(struct snd_soc_component *component, bool enable);
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC)
+int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg);
+int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg,
+ struct snd_soc_jack *jack);
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
+void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type);
+int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc);
+int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc);
+int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc);
+struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ const struct wcd_mbhc_field *fields,
+ bool impedance_det_en);
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc);
+int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event);
+
+#else
+static inline int wcd_dt_parse_mbhc_data(struct device *dev,
+ struct wcd_mbhc_config *cfg)
+{
+ return -ENOTSUPP;
+}
+
+static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+}
+
+static inline struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ const struct wcd_mbhc_field *fields,
+ bool impedance_det_en)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+static inline void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
+{
+}
+
+static inline int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
+{
+ return -ENOTSUPP;
+}
+
+static inline int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
+{
+ return -ENOTSUPP;
+}
+
+static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+ struct wcd_mbhc_config *mbhc_cfg,
+ struct snd_soc_jack *jack)
+{
+ return 0;
+}
+
+static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc,
+ uint32_t *zl,
+ uint32_t *zr)
+{
+ *zl = 0;
+ *zr = 0;
+ return -EINVAL;
+}
+static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+}
+#endif
+
+#endif /* __WCD_MBHC_V2_H__ */
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
new file mode 100644
index 000000000000..640e43ee1975
--- /dev/null
+++ b/sound/soc/codecs/wcd9335.c
@@ -0,0 +1,5168 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+// Copyright (c) 2017-2018, Linaro Limited
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slimbus.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+#include "wcd9335.h"
+#include "wcd-clsh-v2.h"
+
+#include <dt-bindings/sound/qcom,wcd9335.h>
+
+#define WCD9335_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+/* Fractional Rates */
+#define WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100)
+#define WCD9335_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* slave port water mark level
+ * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
+ */
+#define SLAVE_PORT_WATER_MARK_6BYTES 0
+#define SLAVE_PORT_WATER_MARK_9BYTES 1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
+#define SLAVE_PORT_WATER_MARK_SHIFT 1
+#define SLAVE_PORT_ENABLE 1
+#define SLAVE_PORT_DISABLE 0
+#define WCD9335_SLIM_WATER_MARK_VAL \
+ ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+ (SLAVE_PORT_ENABLE))
+
+#define WCD9335_SLIM_NUM_PORT_REG 3
+#define WCD9335_SLIM_PGD_PORT_INT_TX_EN0 (WCD9335_SLIM_PGD_PORT_INT_EN0 + 2)
+
+#define WCD9335_MCLK_CLK_12P288MHZ 12288000
+#define WCD9335_MCLK_CLK_9P6MHZ 9600000
+
+#define WCD9335_SLIM_CLOSE_TIMEOUT 1000
+#define WCD9335_SLIM_IRQ_OVERFLOW (1 << 0)
+#define WCD9335_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define WCD9335_SLIM_IRQ_PORT_CLOSED (1 << 2)
+
+#define WCD9335_NUM_INTERPOLATORS 9
+#define WCD9335_RX_START 16
+#define WCD9335_SLIM_CH_START 128
+#define WCD9335_MAX_MICBIAS 4
+#define WCD9335_MAX_VALID_ADC_MUX 13
+#define WCD9335_INVALID_ADC_MUX 9
+
+#define TX_HPF_CUT_OFF_FREQ_MASK 0x60
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+#define WCD9335_DMIC_CLK_DIV_2 0x0
+#define WCD9335_DMIC_CLK_DIV_3 0x1
+#define WCD9335_DMIC_CLK_DIV_4 0x2
+#define WCD9335_DMIC_CLK_DIV_6 0x3
+#define WCD9335_DMIC_CLK_DIV_8 0x4
+#define WCD9335_DMIC_CLK_DIV_16 0x5
+#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02
+#define WCD9335_AMIC_PWR_LEVEL_LP 0
+#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1
+#define WCD9335_AMIC_PWR_LEVEL_HP 2
+#define WCD9335_AMIC_PWR_LVL_MASK 0x60
+#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5
+
+#define WCD9335_DEC_PWR_LVL_MASK 0x06
+#define WCD9335_DEC_PWR_LVL_LP 0x02
+#define WCD9335_DEC_PWR_LVL_HP 0x04
+#define WCD9335_DEC_PWR_LVL_DF 0x00
+
+#define WCD9335_SLIM_RX_CH(p) \
+ {.port = p + WCD9335_RX_START, .shift = p,}
+
+#define WCD9335_SLIM_TX_CH(p) \
+ {.port = p, .shift = p,}
+
+/* vout step value */
+#define WCD9335_CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25)
+
+#define WCD9335_INTERPOLATOR_PATH(id) \
+ {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \
+ {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \
+ {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \
+ {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 MUX"}, \
+ {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 MIX1"}, \
+ {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \
+ {"RX INT" #id " INTERP", NULL, "RX INT" #id " MIX2"}
+
+#define WCD9335_ADC_MUX_PATH(id) \
+ {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \
+ {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \
+ {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \
+ {"SLIM TX" #id " MUX", "DEC" #id, "ADC MUX" #id}, \
+ {"ADC MUX" #id, "DMIC", "DMIC MUX" #id}, \
+ {"ADC MUX" #id, "AMIC", "AMIC MUX" #id}, \
+ {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \
+ {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \
+ {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \
+ {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \
+ {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \
+ {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \
+ {"AMIC MUX" #id, "ADC1", "ADC1"}, \
+ {"AMIC MUX" #id, "ADC2", "ADC2"}, \
+ {"AMIC MUX" #id, "ADC3", "ADC3"}, \
+ {"AMIC MUX" #id, "ADC4", "ADC4"}, \
+ {"AMIC MUX" #id, "ADC5", "ADC5"}, \
+ {"AMIC MUX" #id, "ADC6", "ADC6"}
+
+#define NUM_CODEC_DAIS 7
+
+enum {
+ WCD9335_RX0 = 0,
+ WCD9335_RX1,
+ WCD9335_RX2,
+ WCD9335_RX3,
+ WCD9335_RX4,
+ WCD9335_RX5,
+ WCD9335_RX6,
+ WCD9335_RX7,
+ WCD9335_RX8,
+ WCD9335_RX9,
+ WCD9335_RX10,
+ WCD9335_RX11,
+ WCD9335_RX12,
+ WCD9335_RX_MAX,
+};
+
+enum {
+ WCD9335_TX0 = 0,
+ WCD9335_TX1,
+ WCD9335_TX2,
+ WCD9335_TX3,
+ WCD9335_TX4,
+ WCD9335_TX5,
+ WCD9335_TX6,
+ WCD9335_TX7,
+ WCD9335_TX8,
+ WCD9335_TX9,
+ WCD9335_TX10,
+ WCD9335_TX11,
+ WCD9335_TX12,
+ WCD9335_TX13,
+ WCD9335_TX14,
+ WCD9335_TX15,
+ WCD9335_TX_MAX,
+};
+
+enum {
+ SIDO_SOURCE_INTERNAL = 0,
+ SIDO_SOURCE_RCO_BG,
+};
+
+enum wcd9335_sido_voltage {
+ SIDO_VOLTAGE_SVS_MV = 950,
+ SIDO_VOLTAGE_NOMINAL_MV = 1100,
+};
+
+enum {
+ COMPANDER_1, /* HPH_L */
+ COMPANDER_2, /* HPH_R */
+ COMPANDER_3, /* LO1_DIFF */
+ COMPANDER_4, /* LO2_DIFF */
+ COMPANDER_5, /* LO3_SE */
+ COMPANDER_6, /* LO4_SE */
+ COMPANDER_7, /* SWR SPK CH1 */
+ COMPANDER_8, /* SWR SPK CH2 */
+ COMPANDER_MAX,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+ INTn_2_INP_SEL_RX4,
+ INTn_2_INP_SEL_RX5,
+ INTn_2_INP_SEL_RX6,
+ INTn_2_INP_SEL_RX7,
+ INTn_2_INP_SEL_PROXIMITY,
+};
+
+enum {
+ INTn_1_MIX_INP_SEL_ZERO = 0,
+ INTn_1_MIX_INP_SEL_DEC0,
+ INTn_1_MIX_INP_SEL_DEC1,
+ INTn_1_MIX_INP_SEL_IIR0,
+ INTn_1_MIX_INP_SEL_IIR1,
+ INTn_1_MIX_INP_SEL_RX0,
+ INTn_1_MIX_INP_SEL_RX1,
+ INTn_1_MIX_INP_SEL_RX2,
+ INTn_1_MIX_INP_SEL_RX3,
+ INTn_1_MIX_INP_SEL_RX4,
+ INTn_1_MIX_INP_SEL_RX5,
+ INTn_1_MIX_INP_SEL_RX6,
+ INTn_1_MIX_INP_SEL_RX7,
+
+};
+
+enum {
+ INTERP_EAR = 0,
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_LO1,
+ INTERP_LO2,
+ INTERP_LO3,
+ INTERP_LO4,
+ INTERP_SPKR1,
+ INTERP_SPKR2,
+};
+
+enum wcd_clock_type {
+ WCD_CLK_OFF,
+ WCD_CLK_RCO,
+ WCD_CLK_MCLK,
+};
+
+enum {
+ MIC_BIAS_1 = 1,
+ MIC_BIAS_2,
+ MIC_BIAS_3,
+ MIC_BIAS_4
+};
+
+enum {
+ MICB_PULLUP_ENABLE,
+ MICB_PULLUP_DISABLE,
+ MICB_ENABLE,
+ MICB_DISABLE,
+};
+
+struct wcd9335_slim_ch {
+ u32 ch_num;
+ u16 port;
+ u16 shift;
+ struct list_head list;
+};
+
+struct wcd_slim_codec_dai_data {
+ struct list_head slim_ch_list;
+ struct slim_stream_config sconfig;
+ struct slim_stream_runtime *sruntime;
+};
+
+struct wcd9335_codec {
+ struct device *dev;
+ struct clk *mclk;
+ struct clk *native_clk;
+ u32 mclk_rate;
+
+ struct slim_device *slim;
+ struct slim_device *slim_ifc_dev;
+ struct regmap *regmap;
+ struct regmap *if_regmap;
+ struct regmap_irq_chip_data *irq_data;
+
+ struct wcd9335_slim_ch rx_chs[WCD9335_RX_MAX];
+ struct wcd9335_slim_ch tx_chs[WCD9335_TX_MAX];
+ u32 num_rx_port;
+ u32 num_tx_port;
+
+ enum wcd9335_sido_voltage sido_voltage;
+
+ struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS];
+ struct snd_soc_component *component;
+
+ int master_bias_users;
+ int clk_mclk_users;
+ int clk_rco_users;
+ int sido_ccl_cnt;
+ enum wcd_clock_type clk_type;
+
+ struct wcd_clsh_ctrl *clsh_ctrl;
+ u32 hph_mode;
+ int prim_int_users[WCD9335_NUM_INTERPOLATORS];
+
+ int comp_enabled[COMPANDER_MAX];
+
+ int intr1;
+ struct gpio_desc *reset_gpio;
+
+ unsigned int rx_port_value[WCD9335_RX_MAX];
+ unsigned int tx_port_value[WCD9335_TX_MAX];
+ int hph_l_gain;
+ int hph_r_gain;
+ u32 rx_bias_count;
+
+ /*TX*/
+ int micb_ref[WCD9335_MAX_MICBIAS];
+ int pullup_ref[WCD9335_MAX_MICBIAS];
+
+ int dmic_0_1_clk_cnt;
+ int dmic_2_3_clk_cnt;
+ int dmic_4_5_clk_cnt;
+};
+
+struct wcd9335_irq {
+ int irq;
+ irqreturn_t (*handler)(int irq, void *data);
+ char *name;
+};
+
+static const char * const wcd9335_supplies[] = {
+ "vdd-buck", "vdd-buck-sido", "vdd-tx", "vdd-rx", "vdd-io",
+};
+
+static const struct wcd9335_slim_ch wcd9335_tx_chs[WCD9335_TX_MAX] = {
+ WCD9335_SLIM_TX_CH(0),
+ WCD9335_SLIM_TX_CH(1),
+ WCD9335_SLIM_TX_CH(2),
+ WCD9335_SLIM_TX_CH(3),
+ WCD9335_SLIM_TX_CH(4),
+ WCD9335_SLIM_TX_CH(5),
+ WCD9335_SLIM_TX_CH(6),
+ WCD9335_SLIM_TX_CH(7),
+ WCD9335_SLIM_TX_CH(8),
+ WCD9335_SLIM_TX_CH(9),
+ WCD9335_SLIM_TX_CH(10),
+ WCD9335_SLIM_TX_CH(11),
+ WCD9335_SLIM_TX_CH(12),
+ WCD9335_SLIM_TX_CH(13),
+ WCD9335_SLIM_TX_CH(14),
+ WCD9335_SLIM_TX_CH(15),
+};
+
+static const struct wcd9335_slim_ch wcd9335_rx_chs[WCD9335_RX_MAX] = {
+ WCD9335_SLIM_RX_CH(0), /* 16 */
+ WCD9335_SLIM_RX_CH(1), /* 17 */
+ WCD9335_SLIM_RX_CH(2),
+ WCD9335_SLIM_RX_CH(3),
+ WCD9335_SLIM_RX_CH(4),
+ WCD9335_SLIM_RX_CH(5),
+ WCD9335_SLIM_RX_CH(6),
+ WCD9335_SLIM_RX_CH(7),
+ WCD9335_SLIM_RX_CH(8),
+ WCD9335_SLIM_RX_CH(9),
+ WCD9335_SLIM_RX_CH(10),
+ WCD9335_SLIM_RX_CH(11),
+ WCD9335_SLIM_RX_CH(12),
+};
+
+struct interp_sample_rate {
+ int rate;
+ int rate_val;
+};
+
+static const struct interp_sample_rate int_mix_rate_val[] = {
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+};
+
+static const struct interp_sample_rate int_prim_rate_val[] = {
+ {8000, 0x0}, /* 8K */
+ {16000, 0x1}, /* 16K */
+ {24000, -EINVAL},/* 24K */
+ {32000, 0x3}, /* 32K */
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+ {384000, 0x7}, /* 384K */
+ {44100, 0x8}, /* 44.1K */
+};
+
+struct wcd9335_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+static const struct wcd9335_reg_mask_val wcd9335_codec_reg_init[] = {
+ /* Rbuckfly/R_EAR(32) */
+ {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00},
+ {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60},
+ {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00},
+ {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50},
+ {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50},
+ {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08},
+ {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08},
+ {WCD9335_ANA_LO_1_2, 0x3C, 0X3C},
+ {WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00},
+ {WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40},
+ {WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03},
+ {WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02},
+ {WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01},
+ {WCD9335_EAR_CMBUFF, 0x08, 0x00},
+ {WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+ {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+ {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+ {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+ {WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08},
+ {WCD9335_RCO_CTRL_2, 0x0F, 0x08},
+ {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10},
+ {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20},
+ {WCD9335_HPH_OCP_CTL, 0xFF, 0x5A},
+ {WCD9335_HPH_L_TEST, 0x01, 0x01},
+ {WCD9335_HPH_R_TEST, 0x01, 0x01},
+ {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+ {WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+ {WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18},
+ {WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+ {WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
+ {WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18},
+ {WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45},
+ {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4},
+ {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08},
+ {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02},
+};
+
+/* Cutoff frequency for high pass filter */
+static const char * const cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
+};
+
+static const char * const rx_cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ",
+ "CF_NEG_3DB_0P48HZ"
+};
+
+static const char * const rx_int0_7_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7", "PROXIMITY"
+};
+
+static const char * const rx_int_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7"
+};
+
+static const char * const rx_prim_mix_text[] = {
+ "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+ "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+ "NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_interp_mux_text[] = {
+ "ZERO", "RX INT0 MIX2",
+};
+
+static const char * const rx_int1_interp_mux_text[] = {
+ "ZERO", "RX INT1 MIX2",
+};
+
+static const char * const rx_int2_interp_mux_text[] = {
+ "ZERO", "RX INT2 MIX2",
+};
+
+static const char * const rx_int3_interp_mux_text[] = {
+ "ZERO", "RX INT3 MIX2",
+};
+
+static const char * const rx_int4_interp_mux_text[] = {
+ "ZERO", "RX INT4 MIX2",
+};
+
+static const char * const rx_int5_interp_mux_text[] = {
+ "ZERO", "RX INT5 MIX2",
+};
+
+static const char * const rx_int6_interp_mux_text[] = {
+ "ZERO", "RX INT6 MIX2",
+};
+
+static const char * const rx_int7_interp_mux_text[] = {
+ "ZERO", "RX INT7 MIX2",
+};
+
+static const char * const rx_int8_interp_mux_text[] = {
+ "ZERO", "RX INT8 SEC MIX"
+};
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB",
+ "Class-H Hi-Fi Low Power"
+};
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB",
+};
+
+static const char * const adc_mux_text[] = {
+ "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2"
+};
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5",
+ "SMIC0", "SMIC1", "SMIC2", "SMIC3"
+};
+
+static const char * const dmic_mux_alt_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5",
+};
+
+static const char * const amic_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6"
+};
+
+static const char * const sb_tx0_mux_text[] = {
+ "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192"
+};
+
+static const char * const sb_tx1_mux_text[] = {
+ "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192"
+};
+
+static const char * const sb_tx2_mux_text[] = {
+ "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192"
+};
+
+static const char * const sb_tx3_mux_text[] = {
+ "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192"
+};
+
+static const char * const sb_tx4_mux_text[] = {
+ "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192"
+};
+
+static const char * const sb_tx5_mux_text[] = {
+ "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192"
+};
+
+static const char * const sb_tx6_mux_text[] = {
+ "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192"
+};
+
+static const char * const sb_tx7_mux_text[] = {
+ "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192"
+};
+
+static const char * const sb_tx8_mux_text[] = {
+ "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0);
+
+static const struct soc_enum cf_dec0_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec5_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec6_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec7_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec8_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_int0_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int1_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int2_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int3_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int4_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int5_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int6_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int7_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int8_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct soc_enum rx_int0_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10,
+ rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int1_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int2_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int3_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int4_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int5_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int6_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int7_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10,
+ rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int8_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int0_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int1_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int2_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int0_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2,
+ rx_int0_interp_mux_text);
+
+static const struct soc_enum rx_int1_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2,
+ rx_int1_interp_mux_text);
+
+static const struct soc_enum rx_int2_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2,
+ rx_int2_interp_mux_text);
+
+static const struct soc_enum rx_int3_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2,
+ rx_int3_interp_mux_text);
+
+static const struct soc_enum rx_int4_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2,
+ rx_int4_interp_mux_text);
+
+static const struct soc_enum rx_int5_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2,
+ rx_int5_interp_mux_text);
+
+static const struct soc_enum rx_int6_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2,
+ rx_int6_interp_mux_text);
+
+static const struct soc_enum rx_int7_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2,
+ rx_int7_interp_mux_text);
+
+static const struct soc_enum rx_int8_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2,
+ rx_int8_interp_mux_text);
+
+static const struct soc_enum tx_adc_mux0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux3_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux4_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux5_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux6_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux7_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux8_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_dmic_mux0_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux2_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux3_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux4_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux5_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux6_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux7_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux8_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_amic_mux0_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux2_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux3_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux4_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux5_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux6_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux7_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux8_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum sb_tx0_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4,
+ sb_tx0_mux_text);
+
+static const struct soc_enum sb_tx1_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4,
+ sb_tx1_mux_text);
+
+static const struct soc_enum sb_tx2_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4,
+ sb_tx2_mux_text);
+
+static const struct soc_enum sb_tx3_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4,
+ sb_tx3_mux_text);
+
+static const struct soc_enum sb_tx4_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4,
+ sb_tx4_mux_text);
+
+static const struct soc_enum sb_tx5_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4,
+ sb_tx5_mux_text);
+
+static const struct soc_enum sb_tx6_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4,
+ sb_tx6_mux_text);
+
+static const struct soc_enum sb_tx7_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4,
+ sb_tx7_mux_text);
+
+static const struct soc_enum sb_tx8_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4,
+ sb_tx8_mux_text);
+
+static const struct snd_kcontrol_new rx_int0_2_mux =
+ SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_2_mux =
+ SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_2_mux =
+ SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_2_mux =
+ SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_2_mux =
+ SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_2_mux =
+ SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_2_mux =
+ SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_2_mux =
+ SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_2_mux =
+ SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_interp_mux =
+ SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int1_interp_mux =
+ SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int2_interp_mux =
+ SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int3_interp_mux =
+ SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int4_interp_mux =
+ SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int5_interp_mux =
+ SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int6_interp_mux =
+ SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int7_interp_mux =
+ SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int8_interp_mux =
+ SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux0 =
+ SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux1 =
+ SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux2 =
+ SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux3 =
+ SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux4 =
+ SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux5 =
+ SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux6 =
+ SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux7 =
+ SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux8 =
+ SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux0 =
+ SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux1 =
+ SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux2 =
+ SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux3 =
+ SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux4 =
+ SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux5 =
+ SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux6 =
+ SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux7 =
+ SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux8 =
+ SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum);
+
+static const struct snd_kcontrol_new sb_tx0_mux =
+ SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx1_mux =
+ SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx2_mux =
+ SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx3_mux =
+ SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx4_mux =
+ SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx5_mux =
+ SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx6_mux =
+ SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx7_mux =
+ SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx8_mux =
+ SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum);
+
+static int slim_rx_mux_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct device *dev = snd_soc_dapm_to_dev(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(dev);
+ u32 port_id = w->shift;
+
+ ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id];
+
+ return 0;
+}
+
+static int slim_rx_mux_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct device *dev = snd_soc_dapm_to_dev(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(dev);
+ struct soc_enum *e = (struct soc_enum *)kc->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 port_id = w->shift;
+
+ if (wcd->rx_port_value[port_id] == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+
+ /* Remove channel from any list it's in before adding it to a new one */
+ list_del_init(&wcd->rx_chs[port_id].list);
+
+ switch (wcd->rx_port_value[port_id]) {
+ case 0:
+ /* Channel already removed from lists. Nothing to do here */
+ break;
+ case 1:
+ list_add_tail(&wcd->rx_chs[port_id].list,
+ &wcd->dai[AIF1_PB].slim_ch_list);
+ break;
+ case 2:
+ list_add_tail(&wcd->rx_chs[port_id].list,
+ &wcd->dai[AIF2_PB].slim_ch_list);
+ break;
+ case 3:
+ list_add_tail(&wcd->rx_chs[port_id].list,
+ &wcd->dai[AIF3_PB].slim_ch_list);
+ break;
+ case 4:
+ list_add_tail(&wcd->rx_chs[port_id].list,
+ &wcd->dai[AIF4_PB].slim_ch_list);
+ break;
+ default:
+ dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]);
+ goto err;
+ }
+
+ snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id],
+ e, update);
+
+ return 0;
+err:
+ return -EINVAL;
+}
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kc);
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(dev);
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kc->private_value;
+ int dai_id = widget->shift;
+ int port_id = mixer->shift;
+
+ ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id] == dai_id;
+
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct device *dev = snd_soc_dapm_to_dev(widget->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(dev);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kc->private_value;
+ int enable = ucontrol->value.integer.value[0];
+ int dai_id = widget->shift;
+ int port_id = mixer->shift;
+
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set */
+ if (enable && wcd->tx_port_value[port_id] != dai_id) {
+ wcd->tx_port_value[port_id] = dai_id;
+ list_add_tail(&wcd->tx_chs[port_id].list,
+ &wcd->dai[dai_id].slim_ch_list);
+ } else if (!enable && wcd->tx_port_value[port_id] == dai_id) {
+ wcd->tx_port_value[port_id] = -1;
+ list_del_init(&wcd->tx_chs[port_id].list);
+ }
+ break;
+ default:
+ dev_err(wcd->dev, "Unknown AIF %d\n", dai_id);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new slim_rx_mux[WCD9335_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9335_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9335_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9335_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9335_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9335_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9335_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9335_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9335_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static int wcd9335_put_dec_enum(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kc);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct soc_enum *e = (struct soc_enum *)kc->private_value;
+ unsigned int val, reg, sel;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ switch (e->reg) {
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+ reg = WCD9335_CDC_TX0_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+ reg = WCD9335_CDC_TX1_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+ reg = WCD9335_CDC_TX2_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+ reg = WCD9335_CDC_TX3_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+ reg = WCD9335_CDC_TX4_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+ reg = WCD9335_CDC_TX5_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+ reg = WCD9335_CDC_TX6_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+ reg = WCD9335_CDC_TX7_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0:
+ reg = WCD9335_CDC_TX8_TX_PATH_CFG0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* AMIC: 0, DMIC: 1 */
+ sel = val ? WCD9335_CDC_TX_ADC_AMIC_SEL : WCD9335_CDC_TX_ADC_DMIC_SEL;
+ snd_soc_component_update_bits(component, reg,
+ WCD9335_CDC_TX_ADC_AMIC_DMIC_SEL_MASK,
+ sel);
+
+ return snd_soc_dapm_put_enum_double(kc, ucontrol);
+}
+
+static int wcd9335_int_dem_inp_mux_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kc->private_value;
+ struct snd_soc_component *component;
+ int reg, val;
+
+ component = snd_soc_dapm_kcontrol_to_component(kc);
+ val = ucontrol->value.enumerated.item[0];
+
+ if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0)
+ reg = WCD9335_CDC_RX0_RX_PATH_CFG0;
+ else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0)
+ reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+ else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0)
+ reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+ else
+ return -EINVAL;
+
+ /* Set Look Ahead Delay */
+ snd_soc_component_update_bits(component, reg,
+ WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN_MASK,
+ val ? WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN : 0);
+ /* Set DEM INP Select */
+ return snd_soc_dapm_put_enum_double(kc, ucontrol);
+}
+
+static const struct snd_kcontrol_new rx_int0_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int2_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new tx_adc_mux0 =
+ SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux1 =
+ SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux2 =
+ SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux3 =
+ SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux4 =
+ SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux5 =
+ SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux6 =
+ SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux7 =
+ SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux8 =
+ SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd9335_put_dec_enum);
+
+static int wcd9335_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ int rate_val,
+ u32 rate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ struct wcd9335_slim_ch *ch;
+ int val, j;
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) {
+ for (j = 0; j < WCD9335_NUM_INTERPOLATORS; j++) {
+ val = snd_soc_component_read(component,
+ WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(j)) &
+ WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+
+ if (val == (ch->shift + INTn_2_INP_SEL_RX0))
+ snd_soc_component_update_bits(component,
+ WCD9335_CDC_RX_PATH_MIX_CTL(j),
+ WCD9335_CDC_MIX_PCM_RATE_MASK,
+ rate_val);
+ }
+ }
+
+ return 0;
+}
+
+static int wcd9335_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rate_val,
+ u32 rate)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+ struct wcd9335_slim_ch *ch;
+ u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel;
+ int inp, j;
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) {
+ inp = ch->shift + INTn_1_MIX_INP_SEL_RX0;
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the slim rx port
+ * is connected
+ */
+ for (j = 0; j < WCD9335_NUM_INTERPOLATORS; j++) {
+ cfg0 = snd_soc_component_read(comp,
+ WCD9335_CDC_RX_INP_MUX_RX_INT_CFG0(j));
+ cfg1 = snd_soc_component_read(comp,
+ WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(j));
+
+ inp0_sel = cfg0 &
+ WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+ inp1_sel = (cfg0 >> 4) &
+ WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+ inp2_sel = (cfg1 >> 4) &
+ WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+
+ if ((inp0_sel == inp) || (inp1_sel == inp) ||
+ (inp2_sel == inp)) {
+ /* rate is in Hz */
+ if ((j == 0) && (rate == 44100))
+ dev_info(wcd->dev,
+ "Cannot set 44.1KHz on INT0\n");
+ else
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX_PATH_CTL(j),
+ WCD9335_CDC_MIX_PCM_RATE_MASK,
+ rate_val);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int wcd9335_set_interpolator_rate(struct snd_soc_dai *dai, u32 rate)
+{
+ int i;
+
+ /* set mixing path rate */
+ for (i = 0; i < ARRAY_SIZE(int_mix_rate_val); i++) {
+ if (rate == int_mix_rate_val[i].rate) {
+ wcd9335_set_mix_interpolator_rate(dai,
+ int_mix_rate_val[i].rate_val, rate);
+ break;
+ }
+ }
+
+ /* set primary path sample rate */
+ for (i = 0; i < ARRAY_SIZE(int_prim_rate_val); i++) {
+ if (rate == int_prim_rate_val[i].rate) {
+ wcd9335_set_prim_interpolator_rate(dai,
+ int_prim_rate_val[i].rate_val, rate);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int wcd9335_slim_set_hw_params(struct wcd9335_codec *wcd,
+ struct wcd_slim_codec_dai_data *dai_data,
+ int direction)
+{
+ struct list_head *slim_ch_list = &dai_data->slim_ch_list;
+ struct slim_stream_config *cfg = &dai_data->sconfig;
+ struct wcd9335_slim_ch *ch;
+ u16 payload = 0;
+ int ret, i;
+
+ cfg->ch_count = 0;
+ cfg->direction = direction;
+ cfg->port_mask = 0;
+
+ /* Configure slave interface device */
+ list_for_each_entry(ch, slim_ch_list, list) {
+ cfg->ch_count++;
+ payload |= 1 << ch->shift;
+ cfg->port_mask |= BIT(ch->port);
+ }
+
+ cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL);
+ if (!cfg->chs)
+ return -ENOMEM;
+
+ i = 0;
+ list_for_each_entry(ch, slim_ch_list, list) {
+ cfg->chs[i++] = ch->ch_num;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* write to interface device */
+ ret = regmap_write(wcd->if_regmap,
+ WCD9335_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port),
+ payload);
+
+ if (ret < 0)
+ goto err;
+
+ /* configure the slave port for water mark and enable*/
+ ret = regmap_write(wcd->if_regmap,
+ WCD9335_SLIM_PGD_RX_PORT_CFG(ch->port),
+ WCD9335_SLIM_WATER_MARK_VAL);
+ if (ret < 0)
+ goto err;
+ } else {
+ ret = regmap_write(wcd->if_regmap,
+ WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port),
+ payload & 0x00FF);
+ if (ret < 0)
+ goto err;
+
+ /* ports 8,9 */
+ ret = regmap_write(wcd->if_regmap,
+ WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port),
+ (payload & 0xFF00)>>8);
+ if (ret < 0)
+ goto err;
+
+ /* configure the slave port for water mark and enable*/
+ ret = regmap_write(wcd->if_regmap,
+ WCD9335_SLIM_PGD_TX_PORT_CFG(ch->port),
+ WCD9335_SLIM_WATER_MARK_VAL);
+
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ dai_data->sruntime = slim_stream_allocate(wcd->slim, "WCD9335-SLIM");
+
+ return 0;
+
+err:
+ dev_err(wcd->dev, "Error Setting slim hw params\n");
+ kfree(cfg->chs);
+ cfg->chs = NULL;
+
+ return ret;
+}
+
+static int wcd9335_set_decimator_rate(struct snd_soc_dai *dai,
+ u8 rate_val, u32 rate)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp);
+ u8 shift = 0, shift_val = 0, tx_mux_sel;
+ struct wcd9335_slim_ch *ch;
+ int tx_port, tx_port_reg;
+ int decimator = -1;
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) {
+ tx_port = ch->port;
+ if ((tx_port == 12) || (tx_port >= 14)) {
+ dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n",
+ tx_port, dai->id);
+ return -EINVAL;
+ }
+ /* Find the SB TX MUX input - which decimator is connected */
+ if (tx_port < 4) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0;
+ shift = (tx_port << 1);
+ shift_val = 0x03;
+ } else if (tx_port < 8) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1;
+ shift = ((tx_port - 4) << 1);
+ shift_val = 0x03;
+ } else if (tx_port < 11) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2;
+ shift = ((tx_port - 8) << 1);
+ shift_val = 0x03;
+ } else if (tx_port == 11) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 0;
+ shift_val = 0x0F;
+ } else /* (tx_port == 13) */ {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 4;
+ shift_val = 0x03;
+ }
+
+ tx_mux_sel = snd_soc_component_read(comp, tx_port_reg) &
+ (shift_val << shift);
+
+ tx_mux_sel = tx_mux_sel >> shift;
+ if (tx_port <= 8) {
+ if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3))
+ decimator = tx_port;
+ } else if (tx_port <= 10) {
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = ((tx_port == 9) ? 7 : 6);
+ } else if (tx_port == 11) {
+ if ((tx_mux_sel >= 1) && (tx_mux_sel < 7))
+ decimator = tx_mux_sel - 1;
+ } else if (tx_port == 13) {
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = 5;
+ }
+
+ if (decimator >= 0) {
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_TX_PATH_CTL(decimator),
+ WCD9335_CDC_TX_PATH_CTL_PCM_RATE_MASK,
+ rate_val);
+ } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) {
+ /* Check if the TX Mux input is RX MIX TXn */
+ dev_err(wcd->dev, "RX_MIX_TX%u going to SLIM TX%u\n",
+ tx_port, tx_port);
+ } else {
+ dev_err(wcd->dev, "ERROR: Invalid decimator: %d\n",
+ decimator);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int wcd9335_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wcd9335_codec *wcd;
+ int ret, tx_fs_rate = 0;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = wcd9335_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(wcd->dev, "cannot set sample rate: %u\n",
+ params_rate(params));
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16 ... 24:
+ wcd->dai[dai->id].sconfig.bps = params_width(params);
+ break;
+ default:
+ dev_err(wcd->dev, "%s: Invalid format 0x%x\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ }
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(wcd->dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+
+ }
+
+ ret = wcd9335_set_decimator_rate(dai, tx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ dev_err(wcd->dev, "Cannot set TX Decimator rate\n");
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16 ... 32:
+ wcd->dai[dai->id].sconfig.bps = params_width(params);
+ break;
+ default:
+ dev_err(wcd->dev, "%s: Invalid format 0x%x\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid stream type %d\n",
+ substream->stream);
+ return -EINVAL;
+ }
+
+ wcd->dai[dai->id].sconfig.rate = params_rate(params);
+ wcd9335_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream);
+
+ return 0;
+}
+
+static int wcd9335_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct wcd_slim_codec_dai_data *dai_data;
+ struct wcd9335_codec *wcd;
+ struct slim_stream_config *cfg;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ dai_data = &wcd->dai[dai->id];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cfg = &dai_data->sconfig;
+ slim_stream_prepare(dai_data->sruntime, cfg);
+ slim_stream_enable(dai_data->sruntime);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ slim_stream_disable(dai_data->sruntime);
+ slim_stream_unprepare(dai_data->sruntime);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
+{
+ struct wcd9335_codec *wcd;
+ int i;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ if (!tx_slot || !rx_slot) {
+ dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n",
+ tx_slot, rx_slot);
+ return -EINVAL;
+ }
+
+ wcd->num_rx_port = rx_num;
+ for (i = 0; i < rx_num; i++) {
+ wcd->rx_chs[i].ch_num = rx_slot[i];
+ INIT_LIST_HEAD(&wcd->rx_chs[i].list);
+ }
+
+ wcd->num_tx_port = tx_num;
+ for (i = 0; i < tx_num; i++) {
+ wcd->tx_chs[i].ch_num = tx_slot[i];
+ INIT_LIST_HEAD(&wcd->tx_chs[i].list);
+ }
+
+ return 0;
+}
+
+static int wcd9335_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct wcd9335_slim_ch *ch;
+ struct wcd9335_codec *wcd;
+ int i = 0;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ case AIF4_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n",
+ rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list)
+ rx_slot[i++] = ch->ch_num;
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n",
+ tx_slot, tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list)
+ tx_slot[i++] = ch->ch_num;
+
+ *tx_num = i;
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wcd9335_dai_ops = {
+ .hw_params = wcd9335_hw_params,
+ .trigger = wcd9335_trigger,
+ .set_channel_map = wcd9335_set_channel_map,
+ .get_channel_map = wcd9335_get_channel_map,
+};
+
+static struct snd_soc_dai_driver wcd9335_slim_dais[] = {
+ [0] = {
+ .name = "wcd9335_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK |
+ SNDRV_PCM_RATE_384000,
+ .formats = WCD9335_FORMATS_S16_S24_LE,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+ [1] = {
+ .name = "wcd9335_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+ [2] = {
+ .name = "wcd9335_rx2",
+ .id = AIF2_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK |
+ SNDRV_PCM_RATE_384000,
+ .formats = WCD9335_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+ [3] = {
+ .name = "wcd9335_tx2",
+ .id = AIF2_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+ [4] = {
+ .name = "wcd9335_rx3",
+ .id = AIF3_PB,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK |
+ SNDRV_PCM_RATE_384000,
+ .formats = WCD9335_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+ [5] = {
+ .name = "wcd9335_tx3",
+ .id = AIF3_CAP,
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+ [6] = {
+ .name = "wcd9335_rx4",
+ .id = AIF4_PB,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK |
+ SNDRV_PCM_RATE_384000,
+ .formats = WCD9335_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd9335_dai_ops,
+ },
+};
+
+static int wcd9335_get_compander(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ int comp = ((struct soc_mixer_control *)kc->private_value)->shift;
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+
+ ucontrol->value.integer.value[0] = wcd->comp_enabled[comp];
+ return 0;
+}
+
+static int wcd9335_set_compander(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ int comp = ((struct soc_mixer_control *) kc->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ int sel;
+
+ wcd->comp_enabled[comp] = value;
+ sel = value ? WCD9335_HPH_GAIN_SRC_SEL_COMPANDER :
+ WCD9335_HPH_GAIN_SRC_SEL_REGISTER;
+
+ /* Any specific register configuration for compander */
+ switch (comp) {
+ case COMPANDER_1:
+ /* Set Gain Source Select based on compander enable/disable */
+ snd_soc_component_update_bits(component, WCD9335_HPH_L_EN,
+ WCD9335_HPH_GAIN_SRC_SEL_MASK, sel);
+ break;
+ case COMPANDER_2:
+ snd_soc_component_update_bits(component, WCD9335_HPH_R_EN,
+ WCD9335_HPH_GAIN_SRC_SEL_MASK, sel);
+ break;
+ case COMPANDER_5:
+ snd_soc_component_update_bits(component, WCD9335_SE_LO_LO3_GAIN,
+ WCD9335_HPH_GAIN_SRC_SEL_MASK, sel);
+ break;
+ case COMPANDER_6:
+ snd_soc_component_update_bits(component, WCD9335_SE_LO_LO4_GAIN,
+ WCD9335_HPH_GAIN_SRC_SEL_MASK, sel);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_rx_hph_mode_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+
+ ucontrol->value.enumerated.item[0] = wcd->hph_mode;
+
+ return 0;
+}
+
+static int wcd9335_rx_hph_mode_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ if (mode_val == 0) {
+ dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n");
+ mode_val = CLS_H_HIFI;
+ }
+ wcd->hph_mode = mode_val;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wcd9335_snd_controls[] = {
+ /* -84dB min - 40dB max */
+ SOC_SINGLE_S8_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX0 Mix Digital Volume", WCD9335_CDC_RX0_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX1 Mix Digital Volume", WCD9335_CDC_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX2 Mix Digital Volume", WCD9335_CDC_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX3 Mix Digital Volume", WCD9335_CDC_RX3_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX4 Mix Digital Volume", WCD9335_CDC_RX4_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX5 Mix Digital Volume", WCD9335_CDC_RX5_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX6 Mix Digital Volume", WCD9335_CDC_RX6_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX7 Mix Digital Volume", WCD9335_CDC_RX7_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX8 Mix Digital Volume", WCD9335_CDC_RX8_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum),
+ SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum),
+ SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum),
+ SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum),
+ SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum),
+ SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum),
+ SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum),
+ SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum),
+ SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum),
+ SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum),
+ SOC_ENUM("RX INT5_1 HPF cut off", cf_int5_1_enum),
+ SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum),
+ SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum),
+ SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_2_enum),
+ SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum),
+ SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum),
+ SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
+ SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
+ wcd9335_get_compander, wcd9335_set_compander),
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ wcd9335_rx_hph_mode_get, wcd9335_rx_hph_mode_put),
+
+ /* Gain Controls */
+ SOC_SINGLE_TLV("EAR PA Volume", WCD9335_ANA_EAR, 4, 4, 1,
+ ear_pa_gain),
+ SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1,
+ line_gain),
+
+ SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0,
+ analog_gain),
+
+ SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
+ SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+ SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+ SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+ SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+ SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+ SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+ SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+ SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+};
+
+static const struct snd_soc_dapm_route wcd9335_audio_map[] = {
+ {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+
+ {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+
+ {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"},
+
+ {"SLIM RX0", NULL, "SLIM RX0 MUX"},
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+ WCD9335_INTERPOLATOR_PATH(0),
+ WCD9335_INTERPOLATOR_PATH(1),
+ WCD9335_INTERPOLATOR_PATH(2),
+ WCD9335_INTERPOLATOR_PATH(3),
+ WCD9335_INTERPOLATOR_PATH(4),
+ WCD9335_INTERPOLATOR_PATH(5),
+ WCD9335_INTERPOLATOR_PATH(6),
+ WCD9335_INTERPOLATOR_PATH(7),
+ WCD9335_INTERPOLATOR_PATH(8),
+
+ /* EAR PA */
+ {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"},
+ {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"},
+ {"RX INT0 DAC", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "RX INT0 DAC"},
+ {"EAR", NULL, "EAR PA"},
+
+ /* HPHL */
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"},
+ {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
+ {"RX INT1 DAC", NULL, "RX_BIAS"},
+ {"HPHL PA", NULL, "RX INT1 DAC"},
+ {"HPHL", NULL, "HPHL PA"},
+
+ /* HPHR */
+ {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"},
+ {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
+ {"RX INT2 DAC", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "RX INT2 DAC"},
+ {"HPHR", NULL, "HPHR PA"},
+
+ /* LINEOUT1 */
+ {"RX INT3 DAC", NULL, "RX INT3 INTERP"},
+ {"RX INT3 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT1 PA", NULL, "RX INT3 DAC"},
+ {"LINEOUT1", NULL, "LINEOUT1 PA"},
+
+ /* LINEOUT2 */
+ {"RX INT4 DAC", NULL, "RX INT4 INTERP"},
+ {"RX INT4 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT2 PA", NULL, "RX INT4 DAC"},
+ {"LINEOUT2", NULL, "LINEOUT2 PA"},
+
+ /* LINEOUT3 */
+ {"RX INT5 DAC", NULL, "RX INT5 INTERP"},
+ {"RX INT5 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT3 PA", NULL, "RX INT5 DAC"},
+ {"LINEOUT3", NULL, "LINEOUT3 PA"},
+
+ /* LINEOUT4 */
+ {"RX INT6 DAC", NULL, "RX INT6 INTERP"},
+ {"RX INT6 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT4 PA", NULL, "RX INT6 DAC"},
+ {"LINEOUT4", NULL, "LINEOUT4 PA"},
+
+ /* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+
+ /* ADC Mux */
+ WCD9335_ADC_MUX_PATH(0),
+ WCD9335_ADC_MUX_PATH(1),
+ WCD9335_ADC_MUX_PATH(2),
+ WCD9335_ADC_MUX_PATH(3),
+ WCD9335_ADC_MUX_PATH(4),
+ WCD9335_ADC_MUX_PATH(5),
+ WCD9335_ADC_MUX_PATH(6),
+ WCD9335_ADC_MUX_PATH(7),
+ WCD9335_ADC_MUX_PATH(8),
+
+ /* ADC Connections */
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2", NULL, "AMIC2"},
+ {"ADC3", NULL, "AMIC3"},
+ {"ADC4", NULL, "AMIC4"},
+ {"ADC5", NULL, "AMIC5"},
+ {"ADC6", NULL, "AMIC6"},
+};
+
+static int wcd9335_micbias_control(struct snd_soc_component *component,
+ int micb_num, int req, bool is_dapm)
+{
+ struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+
+ if ((micb_index < 0) || (micb_index > WCD9335_MAX_MICBIAS - 1)) {
+ dev_err(wcd->dev, "Invalid micbias index, micb_ind:%d\n",
+ micb_index);
+ return -EINVAL;
+ }
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD9335_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD9335_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD9335_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD9335_ANA_MICB4;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ wcd->pullup_ref[micb_index]++;
+ if ((wcd->pullup_ref[micb_index] == 1) &&
+ (wcd->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xC0, 0x80);
+ break;
+ case MICB_PULLUP_DISABLE:
+ wcd->pullup_ref[micb_index]--;
+ if ((wcd->pullup_ref[micb_index] == 0) &&
+ (wcd->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xC0, 0x00);
+ break;
+ case MICB_ENABLE:
+ wcd->micb_ref[micb_index]++;
+ if (wcd->micb_ref[micb_index] == 1)
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xC0, 0x40);
+ break;
+ case MICB_DISABLE:
+ wcd->micb_ref[micb_index]--;
+ if ((wcd->micb_ref[micb_index] == 0) &&
+ (wcd->pullup_ref[micb_index] > 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xC0, 0x80);
+ else if ((wcd->micb_ref[micb_index] == 0) &&
+ (wcd->pullup_ref[micb_index] == 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xC0, 0x00);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int __wcd9335_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ int micb_num;
+
+ if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1")))
+ micb_num = MIC_BIAS_1;
+ else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2")))
+ micb_num = MIC_BIAS_2;
+ else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3")))
+ micb_num = MIC_BIAS_3;
+ else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4")))
+ micb_num = MIC_BIAS_4;
+ else
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * MIC BIAS can also be requested by MBHC,
+ * so use ref count to handle micbias pullup
+ * and enable requests
+ */
+ wcd9335_micbias_control(comp, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for cnp time */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd9335_micbias_control(comp, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ return __wcd9335_codec_enable_micbias(w, event);
+}
+
+static void wcd9335_codec_set_tx_hold(struct snd_soc_component *comp,
+ u16 amic_reg, bool set)
+{
+ u8 mask = 0x20;
+ u8 val;
+
+ if (amic_reg == WCD9335_ANA_AMIC1 || amic_reg == WCD9335_ANA_AMIC3 ||
+ amic_reg == WCD9335_ANA_AMIC5)
+ mask = 0x40;
+
+ val = set ? mask : 0x00;
+
+ switch (amic_reg) {
+ case WCD9335_ANA_AMIC1:
+ case WCD9335_ANA_AMIC2:
+ snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC2, mask,
+ val);
+ break;
+ case WCD9335_ANA_AMIC3:
+ case WCD9335_ANA_AMIC4:
+ snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC4, mask,
+ val);
+ break;
+ case WCD9335_ANA_AMIC5:
+ case WCD9335_ANA_AMIC6:
+ snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC6, mask,
+ val);
+ break;
+ default:
+ dev_err(comp->dev, "%s: invalid amic: %d\n",
+ __func__, amic_reg);
+ break;
+ }
+}
+
+static int wcd9335_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd9335_codec_set_tx_hold(comp, w->reg, true);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_find_amic_input(struct snd_soc_component *comp,
+ int adc_mux_n)
+{
+ int mux_sel, reg, mreg;
+
+ if (adc_mux_n < 0 || adc_mux_n > WCD9335_MAX_VALID_ADC_MUX ||
+ adc_mux_n == WCD9335_INVALID_ADC_MUX)
+ return 0;
+
+ /* Check whether adc mux input is AMIC or DMIC */
+ if (adc_mux_n < 4) {
+ reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + 2 * adc_mux_n;
+ mreg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + 2 * adc_mux_n;
+ mux_sel = snd_soc_component_read(comp, reg) & 0x3;
+ } else {
+ reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + adc_mux_n - 4;
+ mreg = reg;
+ mux_sel = snd_soc_component_read(comp, reg) >> 6;
+ }
+
+ if (mux_sel != WCD9335_CDC_TX_INP_MUX_SEL_AMIC)
+ return 0;
+
+ return snd_soc_component_read(comp, mreg) & 0x07;
+}
+
+static u16 wcd9335_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp,
+ int amic)
+{
+ u16 pwr_level_reg = 0;
+
+ switch (amic) {
+ case 1:
+ case 2:
+ pwr_level_reg = WCD9335_ANA_AMIC1;
+ break;
+
+ case 3:
+ case 4:
+ pwr_level_reg = WCD9335_ANA_AMIC3;
+ break;
+
+ case 5:
+ case 6:
+ pwr_level_reg = WCD9335_ANA_AMIC5;
+ break;
+ default:
+ dev_err(comp->dev, "invalid amic: %d\n", amic);
+ break;
+ }
+
+ return pwr_level_reg;
+}
+
+static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator;
+ char *dec_adc_mux_name = NULL;
+ char *widget_name;
+ int ret = 0, amic_n;
+ u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ char *dec;
+ u8 hpf_coff_freq;
+
+ char *wname __free(kfree) = kmemdup_nul(w->name, 15, GFP_KERNEL);
+ if (!wname)
+ return -ENOMEM;
+
+ widget_name = wname;
+ dec_adc_mux_name = strsep(&widget_name, " ");
+ if (!dec_adc_mux_name) {
+ dev_err(comp->dev, "%s: Invalid decimator = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ dec_adc_mux_name = widget_name;
+
+ dec = strpbrk(dec_adc_mux_name, "012345678");
+ if (!dec) {
+ dev_err(comp->dev, "%s: decimator index not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(dec, 10, &decimator);
+ if (ret < 0) {
+ dev_err(comp->dev, "%s: Invalid decimator = %s\n",
+ __func__, wname);
+ return -EINVAL;
+ }
+
+ tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator;
+ hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator;
+ dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator;
+ tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ amic_n = wcd9335_codec_find_amic_input(comp, decimator);
+ if (amic_n)
+ pwr_level_reg = wcd9335_codec_get_amic_pwlvl_reg(comp,
+ amic_n);
+
+ if (pwr_level_reg) {
+ switch ((snd_soc_component_read(comp, pwr_level_reg) &
+ WCD9335_AMIC_PWR_LVL_MASK) >>
+ WCD9335_AMIC_PWR_LVL_SHIFT) {
+ case WCD9335_AMIC_PWR_LEVEL_LP:
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD9335_DEC_PWR_LVL_MASK,
+ WCD9335_DEC_PWR_LVL_LP);
+ break;
+
+ case WCD9335_AMIC_PWR_LEVEL_HP:
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD9335_DEC_PWR_LVL_MASK,
+ WCD9335_DEC_PWR_LVL_HP);
+ break;
+ case WCD9335_AMIC_PWR_LEVEL_DEFAULT:
+ default:
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD9335_DEC_PWR_LVL_MASK,
+ WCD9335_DEC_PWR_LVL_DF);
+ break;
+ }
+ }
+ hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+ if (hpf_coff_freq != CF_MIN_3DB_150HZ)
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+ /* Enable TX PGA Mute */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ 0x10, 0x10);
+ /* Enable APC */
+ snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(comp, hpf_gate_reg, 0x01, 0x00);
+
+ if (decimator == 0) {
+ snd_soc_component_write(comp,
+ WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
+ snd_soc_component_write(comp,
+ WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3);
+ snd_soc_component_write(comp,
+ WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
+ snd_soc_component_write(comp,
+ WCD9335_MBHC_ZDET_RAMP_CTL, 0x03);
+ }
+
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ 0x01, 0x01);
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ 0x10, 0x00);
+ snd_soc_component_write(comp, tx_gain_ctl_reg,
+ snd_soc_component_read(comp, tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x10);
+ snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x00);
+ if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_coff_freq << 5);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00);
+ break;
+ }
+
+ return ret;
+}
+
+static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component,
+ u32 mclk_rate)
+{
+ u8 dmic_ctl_val;
+
+ if (mclk_rate == WCD9335_MCLK_CLK_9P6MHZ)
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2;
+ else
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3;
+
+ return dmic_ctl_val;
+}
+
+static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp);
+ u8 dmic_clk_en = 0x01;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 dmic_rate_val, dmic_rate_shift = 1;
+ unsigned int dmic;
+ int ret;
+ char *wname;
+
+ wname = strpbrk(w->name, "012345");
+ if (!wname) {
+ dev_err(comp->dev, "%s: widget not found\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(wname, 10, &dmic);
+ if (ret < 0) {
+ dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &(wcd->dmic_0_1_clk_cnt);
+ dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &(wcd->dmic_2_3_clk_cnt);
+ dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &(wcd->dmic_4_5_clk_cnt);
+ dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL;
+ break;
+ default:
+ dev_err(comp->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate);
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ dmic_clk_en, dmic_clk_en);
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate);
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ dmic_clk_en, 0);
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd9335_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai,
+ struct snd_soc_component *component)
+{
+ int port_num = 0;
+ unsigned short reg = 0;
+ unsigned int val = 0;
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ struct wcd9335_slim_ch *ch;
+
+ list_for_each_entry(ch, &dai->slim_ch_list, list) {
+ if (ch->port >= WCD9335_RX_START) {
+ port_num = ch->port - WCD9335_RX_START;
+ reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + (port_num / 8);
+ } else {
+ port_num = ch->port;
+ reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+ }
+
+ regmap_read(wcd->if_regmap, reg, &val);
+ if (!(val & BIT(port_num % 8)))
+ regmap_write(wcd->if_regmap, reg,
+ val | BIT(port_num % 8));
+ }
+}
+
+static int wcd9335_codec_enable_slim(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp);
+ struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift];
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wcd9335_codec_enable_int_port(dai, comp);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ kfree(dai->sconfig.chs);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg;
+ int val = 0;
+
+ switch (w->reg) {
+ case WCD9335_CDC_RX0_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX1_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX2_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX3_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX4_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX5_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX6_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX7_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX8_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL;
+ break;
+ default:
+ dev_err(comp->dev, "%s: No gain register avail for %s\n",
+ __func__, w->name);
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(comp, gain_reg);
+ snd_soc_component_write(comp, gain_reg, val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ }
+
+ return 0;
+}
+
+static u16 wcd9335_interp_get_primary_reg(u16 reg, u16 *ind)
+{
+ u16 prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+
+ switch (reg) {
+ case WCD9335_CDC_RX0_RX_PATH_CTL:
+ case WCD9335_CDC_RX0_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+ *ind = 0;
+ break;
+ case WCD9335_CDC_RX1_RX_PATH_CTL:
+ case WCD9335_CDC_RX1_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+ *ind = 1;
+ break;
+ case WCD9335_CDC_RX2_RX_PATH_CTL:
+ case WCD9335_CDC_RX2_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+ *ind = 2;
+ break;
+ case WCD9335_CDC_RX3_RX_PATH_CTL:
+ case WCD9335_CDC_RX3_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ *ind = 3;
+ break;
+ case WCD9335_CDC_RX4_RX_PATH_CTL:
+ case WCD9335_CDC_RX4_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ *ind = 4;
+ break;
+ case WCD9335_CDC_RX5_RX_PATH_CTL:
+ case WCD9335_CDC_RX5_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ *ind = 5;
+ break;
+ case WCD9335_CDC_RX6_RX_PATH_CTL:
+ case WCD9335_CDC_RX6_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ *ind = 6;
+ break;
+ case WCD9335_CDC_RX7_RX_PATH_CTL:
+ case WCD9335_CDC_RX7_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+ *ind = 7;
+ break;
+ case WCD9335_CDC_RX8_RX_PATH_CTL:
+ case WCD9335_CDC_RX8_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+ *ind = 8;
+ break;
+ }
+
+ return prim_int_reg;
+}
+
+static void wcd9335_codec_hd2_control(struct snd_soc_component *component,
+ u16 prim_int_reg, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg = 0;
+
+ if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) {
+ hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+ }
+ if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) {
+ hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3;
+ hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK,
+ WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P2500);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK,
+ WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_2);
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK,
+ WCD9335_CDC_RX_PATH_CFG_HD2_ENABLE);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK,
+ WCD9335_CDC_RX_PATH_CFG_HD2_DISABLE);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK,
+ WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_1);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK,
+ WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000);
+ }
+}
+
+static int wcd9335_codec_enable_prim_interpolator(
+ struct snd_soc_component *comp,
+ u16 reg, int event)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+ u16 ind = 0;
+ int prim_int_reg = wcd9335_interp_get_primary_reg(reg, &ind);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd->prim_int_users[ind]++;
+ if (wcd->prim_int_users[ind] == 1) {
+ snd_soc_component_update_bits(comp, prim_int_reg,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_ENABLE);
+ wcd9335_codec_hd2_control(comp, prim_int_reg, event);
+ snd_soc_component_update_bits(comp, prim_int_reg,
+ WCD9335_CDC_RX_CLK_EN_MASK,
+ WCD9335_CDC_RX_CLK_ENABLE);
+ }
+
+ if ((reg != prim_int_reg) &&
+ ((snd_soc_component_read(comp, prim_int_reg)) &
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK))
+ snd_soc_component_update_bits(comp, reg,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd->prim_int_users[ind]--;
+ if (wcd->prim_int_users[ind] == 0) {
+ snd_soc_component_update_bits(comp, prim_int_reg,
+ WCD9335_CDC_RX_CLK_EN_MASK,
+ WCD9335_CDC_RX_CLK_DISABLE);
+ snd_soc_component_update_bits(comp, prim_int_reg,
+ WCD9335_CDC_RX_RESET_MASK,
+ WCD9335_CDC_RX_RESET_ENABLE);
+ snd_soc_component_update_bits(comp, prim_int_reg,
+ WCD9335_CDC_RX_RESET_MASK,
+ WCD9335_CDC_RX_RESET_DISABLE);
+ wcd9335_codec_hd2_control(comp, prim_int_reg, event);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_config_compander(struct snd_soc_component *component,
+ int interp_n, int event)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ int comp;
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+ /* EAR does not have compander */
+ if (!interp_n)
+ return 0;
+
+ comp = interp_n - 1;
+ if (!wcd->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL(comp);
+ rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG(comp);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_CLK_EN_MASK,
+ WCD9335_CDC_COMPANDER_CLK_ENABLE);
+ /* Reset comander */
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_SOFT_RST_MASK,
+ WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_SOFT_RST_MASK,
+ WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE);
+ /* Enables DRE in this path */
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK,
+ WCD9335_CDC_RX_PATH_CFG_CMP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_HALT_MASK,
+ WCD9335_CDC_COMPANDER_HALT);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK,
+ WCD9335_CDC_RX_PATH_CFG_CMP_DISABLE);
+
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_SOFT_RST_MASK,
+ WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_SOFT_RST_MASK,
+ WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_CLK_EN_MASK,
+ WCD9335_CDC_COMPANDER_CLK_DISABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ WCD9335_CDC_COMPANDER_HALT_MASK,
+ WCD9335_CDC_COMPANDER_NOHALT);
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg;
+ u16 reg;
+ int val;
+
+ if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT0 INTERP"))) {
+ reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT1 INTERP"))) {
+ reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT2 INTERP"))) {
+ reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT3 INTERP"))) {
+ reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT4 INTERP"))) {
+ reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT5 INTERP"))) {
+ reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT6 INTERP"))) {
+ reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT7 INTERP"))) {
+ reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL;
+ } else if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT8 INTERP"))) {
+ reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL;
+ } else {
+ dev_err(comp->dev, "%s: Interpolator reg not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reset if needed */
+ wcd9335_codec_enable_prim_interpolator(comp, reg, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wcd9335_config_compander(comp, w->shift, event);
+ val = snd_soc_component_read(comp, gain_reg);
+ snd_soc_component_write(comp, gain_reg, val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd9335_config_compander(comp, w->shift, event);
+ wcd9335_codec_enable_prim_interpolator(comp, reg, event);
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd9335_codec_hph_mode_gain_opt(struct snd_soc_component *component,
+ u8 gain)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ u8 hph_l_en, hph_r_en;
+ u8 l_val, r_val;
+ u8 hph_pa_status;
+ bool is_hphl_pa, is_hphr_pa;
+
+ hph_pa_status = snd_soc_component_read(component, WCD9335_ANA_HPH);
+ is_hphl_pa = hph_pa_status >> 7;
+ is_hphr_pa = (hph_pa_status & 0x40) >> 6;
+
+ hph_l_en = snd_soc_component_read(component, WCD9335_HPH_L_EN);
+ hph_r_en = snd_soc_component_read(component, WCD9335_HPH_R_EN);
+
+ l_val = (hph_l_en & 0xC0) | 0x20 | gain;
+ r_val = (hph_r_en & 0xC0) | 0x20 | gain;
+
+ /*
+ * Set HPH_L & HPH_R gain source selection to REGISTER
+ * for better click and pop only if corresponding PAs are
+ * not enabled. Also cache the values of the HPHL/R
+ * PA gains to be applied after PAs are enabled
+ */
+ if ((l_val != hph_l_en) && !is_hphl_pa) {
+ snd_soc_component_write(component, WCD9335_HPH_L_EN, l_val);
+ wcd->hph_l_gain = hph_l_en & 0x1F;
+ }
+
+ if ((r_val != hph_r_en) && !is_hphr_pa) {
+ snd_soc_component_write(component, WCD9335_HPH_R_EN, r_val);
+ wcd->hph_r_gain = hph_r_en & 0x1F;
+ }
+}
+
+static void wcd9335_codec_hph_lohifi_config(struct snd_soc_component *comp,
+ int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(comp, WCD9335_RX_BIAS_HPH_PA,
+ WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK,
+ 0x06);
+ snd_soc_component_update_bits(comp,
+ WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2,
+ 0xF0, 0x40);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1,
+ WCD9335_HPH_PA_GM3_IB_SCALE_MASK,
+ 0x0C);
+ wcd9335_codec_hph_mode_gain_opt(comp, 0x11);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500);
+ snd_soc_component_write(comp, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2,
+ 0x8A);
+ snd_soc_component_update_bits(comp, WCD9335_RX_BIAS_HPH_PA,
+ WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK,
+ 0x0A);
+ }
+}
+
+static void wcd9335_codec_hph_lp_config(struct snd_soc_component *comp,
+ int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1,
+ WCD9335_HPH_PA_GM3_IB_SCALE_MASK,
+ 0x0C);
+ wcd9335_codec_hph_mode_gain_opt(comp, 0x10);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_PSRREH_ENABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK,
+ WCD9335_HPH_PA_CTL2_HPH_PSRR_ENABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_RDAC_LDO_CTL,
+ WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_MASK,
+ WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_V_N1P60);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_RDAC_LDO_CTL,
+ WCD9335_HPH_RDAC_1P65_LD_OUTCTL_MASK,
+ WCD9335_HPH_RDAC_1P65_LD_OUTCTL_V_N1P60);
+ snd_soc_component_update_bits(comp,
+ WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x0F, 0x01);
+ snd_soc_component_update_bits(comp,
+ WCD9335_RX_BIAS_HPH_RDAC_LDO, 0xF0, 0x10);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_write(comp, WCD9335_RX_BIAS_HPH_RDAC_LDO,
+ 0x88);
+ snd_soc_component_write(comp, WCD9335_HPH_RDAC_LDO_CTL,
+ 0x33);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK,
+ WCD9335_HPH_PA_CTL2_HPH_PSRR_DISABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_PSRREH_DISABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_R_EN,
+ WCD9335_HPH_CONST_SEL_L_MASK,
+ WCD9335_HPH_CONST_SEL_L_HQ_PATH);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_L_EN,
+ WCD9335_HPH_CONST_SEL_L_MASK,
+ WCD9335_HPH_CONST_SEL_L_HQ_PATH);
+ }
+}
+
+static void wcd9335_codec_hph_hifi_config(struct snd_soc_component *comp,
+ int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1,
+ WCD9335_HPH_PA_GM3_IB_SCALE_MASK,
+ 0x0C);
+ wcd9335_codec_hph_mode_gain_opt(comp, 0x11);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK,
+ WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE);
+ snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK,
+ WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500);
+ }
+}
+
+static void wcd9335_codec_hph_mode_config(struct snd_soc_component *component,
+ int event, int mode)
+{
+ switch (mode) {
+ case CLS_H_LP:
+ wcd9335_codec_hph_lp_config(component, event);
+ break;
+ case CLS_H_LOHIFI:
+ wcd9335_codec_hph_lohifi_config(component, event);
+ break;
+ case CLS_H_HIFI:
+ wcd9335_codec_hph_hifi_config(component, event);
+ break;
+ }
+}
+
+static int wcd9335_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+ int hph_mode = wcd->hph_mode;
+ u8 dem_inp;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_component_read(comp,
+ WCD9335_CDC_RX1_RX_PATH_SEC0) & 0x03;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ dev_err(comp->dev, "Incorrect DEM Input\n");
+ return -EINVAL;
+ }
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+
+ wcd9335_codec_hph_mode_config(comp, event, hph_mode);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+
+ if (!(wcd_clsh_ctrl_get_state(wcd->clsh_ctrl) &
+ WCD_CLSH_STATE_HPHR))
+ wcd9335_codec_hph_mode_config(comp, event, hph_mode);
+
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_lineout_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_LO, CLS_AB);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_LO, CLS_AB);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR, CLS_H_NORMAL);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR, CLS_H_NORMAL);
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd9335_codec_hph_post_pa_config(struct wcd9335_codec *wcd,
+ int mode, int event)
+{
+ u8 scale_val = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ switch (mode) {
+ case CLS_H_HIFI:
+ scale_val = 0x3;
+ break;
+ case CLS_H_LOHIFI:
+ scale_val = 0x1;
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ scale_val = 0x6;
+ break;
+ }
+
+ if (scale_val)
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_PA_CTL1,
+ WCD9335_HPH_PA_GM3_IB_SCALE_MASK,
+ scale_val << 1);
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (wcd->comp_enabled[COMPANDER_1] ||
+ wcd->comp_enabled[COMPANDER_2]) {
+ /* GAIN Source Selection */
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_L_EN,
+ WCD9335_HPH_GAIN_SRC_SEL_MASK,
+ WCD9335_HPH_GAIN_SRC_SEL_COMPANDER);
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_R_EN,
+ WCD9335_HPH_GAIN_SRC_SEL_MASK,
+ WCD9335_HPH_GAIN_SRC_SEL_COMPANDER);
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_AUTO_CHOP,
+ WCD9335_HPH_AUTO_CHOP_MASK,
+ WCD9335_HPH_AUTO_CHOP_FORCE_ENABLE);
+ }
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_L_EN,
+ WCD9335_HPH_PA_GAIN_MASK,
+ wcd->hph_l_gain);
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_R_EN,
+ WCD9335_HPH_PA_GAIN_MASK,
+ wcd->hph_r_gain);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(wcd->component,
+ WCD9335_HPH_AUTO_CHOP,
+ WCD9335_HPH_AUTO_CHOP_MASK,
+ WCD9335_HPH_AUTO_CHOP_ENABLE_BY_CMPDR_GAIN);
+}
+
+static int wcd9335_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+ int hph_mode = wcd->hph_mode;
+ u8 dem_inp;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_component_read(comp,
+ WCD9335_CDC_RX2_RX_PATH_SEC0) &
+ WCD9335_CDC_RX_PATH_DEM_INP_SEL_MASK;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ dev_err(comp->dev, "DEM Input not set correctly, hph_mode: %d\n",
+ hph_mode);
+ return -EINVAL;
+ }
+
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+
+ wcd9335_codec_hph_mode_config(comp, event, hph_mode);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+
+ if (!(wcd_clsh_ctrl_get_state(wcd->clsh_ctrl) &
+ WCD_CLSH_STATE_HPHL))
+ wcd9335_codec_hph_mode_config(comp, event, hph_mode);
+
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR, ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+ int hph_mode = wcd->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(7000, 7100);
+
+ wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event);
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX1_RX_PATH_CTL,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_component_read(comp,
+ WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) &
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK)
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ int vol_reg = 0, mix_vol_reg = 0;
+
+ if (w->reg == WCD9335_ANA_LO_1_2) {
+ if (w->shift == 7) {
+ vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL;
+ } else if (w->shift == 6) {
+ vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL;
+ }
+ } else if (w->reg == WCD9335_ANA_LO_3_4) {
+ if (w->shift == 7) {
+ vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL;
+ } else if (w->shift == 6) {
+ vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL;
+ }
+ } else {
+ dev_err(comp->dev, "Error enabling lineout PA\n");
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* 5ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(comp, vol_reg,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_component_read(comp, mix_vol_reg)) &
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK)
+ snd_soc_component_update_bits(comp, mix_vol_reg,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd9335_codec_init_flyback(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, WCD9335_HPH_L_EN,
+ WCD9335_HPH_CONST_SEL_L_MASK,
+ WCD9335_HPH_CONST_SEL_L_BYPASS);
+ snd_soc_component_update_bits(component, WCD9335_HPH_R_EN,
+ WCD9335_HPH_CONST_SEL_L_MASK,
+ WCD9335_HPH_CONST_SEL_L_BYPASS);
+ snd_soc_component_update_bits(component, WCD9335_RX_BIAS_FLYB_BUFF,
+ WCD9335_RX_BIAS_FLYB_VPOS_5_UA_MASK,
+ WCD9335_RX_BIAS_FLYB_I_0P0_UA);
+ snd_soc_component_update_bits(component, WCD9335_RX_BIAS_FLYB_BUFF,
+ WCD9335_RX_BIAS_FLYB_VNEG_5_UA_MASK,
+ WCD9335_RX_BIAS_FLYB_I_0P0_UA);
+}
+
+static int wcd9335_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd->rx_bias_count++;
+ if (wcd->rx_bias_count == 1) {
+ wcd9335_codec_init_flyback(comp);
+ snd_soc_component_update_bits(comp,
+ WCD9335_ANA_RX_SUPPLIES,
+ WCD9335_ANA_RX_BIAS_ENABLE_MASK,
+ WCD9335_ANA_RX_BIAS_ENABLE);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd->rx_bias_count--;
+ if (!wcd->rx_bias_count)
+ snd_soc_component_update_bits(comp,
+ WCD9335_ANA_RX_SUPPLIES,
+ WCD9335_ANA_RX_BIAS_ENABLE_MASK,
+ WCD9335_ANA_RX_BIAS_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+ int hph_mode = wcd->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(7000, 7100);
+ wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event);
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX2_RX_PATH_CTL,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_component_read(comp,
+ WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) &
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK)
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* 5ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX0_RX_PATH_CTL,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_component_read(comp,
+ WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) &
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK)
+ snd_soc_component_update_bits(comp,
+ WCD9335_CDC_RX0_RX_PATH_MIX_CTL,
+ WCD9335_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD9335_CDC_RX_PGA_MUTE_DISABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t wcd9335_slimbus_irq(int irq, void *data)
+{
+ struct wcd9335_codec *wcd = data;
+ unsigned long status = 0;
+ int i, j, port_id;
+ unsigned int val, int_val = 0;
+ irqreturn_t ret = IRQ_NONE;
+ bool tx;
+ unsigned short reg = 0;
+
+ for (i = WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+ i <= WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+ regmap_read(wcd->if_regmap, i, &val);
+ status |= ((u32)val << (8 * j));
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ tx = (j >= 16);
+ port_id = (tx ? j - 16 : j);
+ regmap_read(wcd->if_regmap,
+ WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val);
+ if (val) {
+ if (!tx)
+ reg = WCD9335_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ regmap_read(
+ wcd->if_regmap, reg, &int_val);
+ /*
+ * Ignore interrupts for ports for which the
+ * interrupts are not specifically enabled.
+ */
+ if (!(int_val & (1 << (port_id % 8))))
+ continue;
+ }
+
+ if (val & WCD9335_SLIM_IRQ_OVERFLOW)
+ dev_err_ratelimited(wcd->dev,
+ "%s: overflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+
+ if (val & WCD9335_SLIM_IRQ_UNDERFLOW)
+ dev_err_ratelimited(wcd->dev,
+ "%s: underflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+
+ if ((val & WCD9335_SLIM_IRQ_OVERFLOW) ||
+ (val & WCD9335_SLIM_IRQ_UNDERFLOW)) {
+ if (!tx)
+ reg = WCD9335_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ regmap_read(
+ wcd->if_regmap, reg, &int_val);
+ if (int_val & (1 << (port_id % 8))) {
+ int_val = int_val ^ (1 << (port_id % 8));
+ regmap_write(wcd->if_regmap,
+ reg, int_val);
+ }
+ }
+
+ regmap_write(wcd->if_regmap,
+ WCD9335_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8),
+ BIT(j % 8));
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static const struct wcd9335_irq wcd9335_irqs[] = {
+ {
+ .irq = WCD9335_IRQ_SLIMBUS,
+ .handler = wcd9335_slimbus_irq,
+ .name = "SLIM Slave",
+ },
+};
+
+static int wcd9335_setup_irqs(struct wcd9335_codec *wcd)
+{
+ int irq, ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(wcd9335_irqs); i++) {
+ irq = regmap_irq_get_virq(wcd->irq_data, wcd9335_irqs[i].irq);
+ if (irq < 0) {
+ dev_err(wcd->dev, "Failed to get %s\n",
+ wcd9335_irqs[i].name);
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(wcd->dev, irq, NULL,
+ wcd9335_irqs[i].handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ wcd9335_irqs[i].name, wcd);
+ if (ret) {
+ dev_err(wcd->dev, "Failed to request %s\n",
+ wcd9335_irqs[i].name);
+ return ret;
+ }
+ }
+
+ /* enable interrupts on all slave ports */
+ for (i = 0; i < WCD9335_SLIM_NUM_PORT_REG; i++)
+ regmap_write(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_EN0 + i,
+ 0xFF);
+
+ return ret;
+}
+
+static void wcd9335_teardown_irqs(struct wcd9335_codec *wcd)
+{
+ int i;
+
+ /* disable interrupts on all slave ports */
+ for (i = 0; i < WCD9335_SLIM_NUM_PORT_REG; i++)
+ regmap_write(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_EN0 + i,
+ 0x00);
+}
+
+static void wcd9335_cdc_sido_ccl_enable(struct wcd9335_codec *wcd,
+ bool ccl_flag)
+{
+ struct snd_soc_component *comp = wcd->component;
+
+ if (ccl_flag) {
+ if (++wcd->sido_ccl_cnt == 1)
+ snd_soc_component_write(comp, WCD9335_SIDO_SIDO_CCL_10,
+ WCD9335_SIDO_SIDO_CCL_DEF_VALUE);
+ } else {
+ if (wcd->sido_ccl_cnt == 0) {
+ dev_err(wcd->dev, "sido_ccl already disabled\n");
+ return;
+ }
+ if (--wcd->sido_ccl_cnt == 0)
+ snd_soc_component_write(comp, WCD9335_SIDO_SIDO_CCL_10,
+ WCD9335_SIDO_SIDO_CCL_10_ICHARG_PWR_SEL_C320FF);
+ }
+}
+
+static int wcd9335_enable_master_bias(struct wcd9335_codec *wcd)
+{
+ wcd->master_bias_users++;
+ if (wcd->master_bias_users == 1) {
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS,
+ WCD9335_ANA_BIAS_EN_MASK,
+ WCD9335_ANA_BIAS_ENABLE);
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS,
+ WCD9335_ANA_BIAS_PRECHRG_EN_MASK,
+ WCD9335_ANA_BIAS_PRECHRG_ENABLE);
+ /*
+ * 1ms delay is required after pre-charge is enabled
+ * as per HW requirement
+ */
+ usleep_range(1000, 1100);
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS,
+ WCD9335_ANA_BIAS_PRECHRG_EN_MASK,
+ WCD9335_ANA_BIAS_PRECHRG_DISABLE);
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS,
+ WCD9335_ANA_BIAS_PRECHRG_CTL_MODE,
+ WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL);
+ }
+
+ return 0;
+}
+
+static int wcd9335_enable_mclk(struct wcd9335_codec *wcd)
+{
+ /* Enable mclk requires master bias to be enabled first */
+ if (wcd->master_bias_users <= 0)
+ return -EINVAL;
+
+ if (((wcd->clk_mclk_users == 0) && (wcd->clk_type == WCD_CLK_MCLK)) ||
+ ((wcd->clk_mclk_users > 0) && (wcd->clk_type != WCD_CLK_MCLK))) {
+ dev_err(wcd->dev, "Error enabling MCLK, clk_type: %d\n",
+ wcd->clk_type);
+ return -EINVAL;
+ }
+
+ if (++wcd->clk_mclk_users == 1) {
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP,
+ WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK,
+ WCD9335_ANA_CLK_EXT_CLKBUF_ENABLE);
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP,
+ WCD9335_ANA_CLK_MCLK_SRC_MASK,
+ WCD9335_ANA_CLK_MCLK_SRC_EXTERNAL);
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP,
+ WCD9335_ANA_CLK_MCLK_EN_MASK,
+ WCD9335_ANA_CLK_MCLK_ENABLE);
+ regmap_update_bits(wcd->regmap,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_EN_MASK,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE);
+ regmap_update_bits(wcd->regmap,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_EN_MASK,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_ENABLE);
+ /*
+ * 10us sleep is required after clock is enabled
+ * as per HW requirement
+ */
+ usleep_range(10, 15);
+ }
+
+ wcd->clk_type = WCD_CLK_MCLK;
+
+ return 0;
+}
+
+static int wcd9335_disable_mclk(struct wcd9335_codec *wcd)
+{
+ if (wcd->clk_mclk_users <= 0)
+ return -EINVAL;
+
+ if (--wcd->clk_mclk_users == 0) {
+ if (wcd->clk_rco_users > 0) {
+ /* MCLK to RCO switch */
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP,
+ WCD9335_ANA_CLK_MCLK_SRC_MASK,
+ WCD9335_ANA_CLK_MCLK_SRC_RCO);
+ wcd->clk_type = WCD_CLK_RCO;
+ } else {
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP,
+ WCD9335_ANA_CLK_MCLK_EN_MASK,
+ WCD9335_ANA_CLK_MCLK_DISABLE);
+ wcd->clk_type = WCD_CLK_OFF;
+ }
+
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP,
+ WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK,
+ WCD9335_ANA_CLK_EXT_CLKBUF_DISABLE);
+ }
+
+ return 0;
+}
+
+static int wcd9335_disable_master_bias(struct wcd9335_codec *wcd)
+{
+ if (wcd->master_bias_users <= 0)
+ return -EINVAL;
+
+ wcd->master_bias_users--;
+ if (wcd->master_bias_users == 0) {
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS,
+ WCD9335_ANA_BIAS_EN_MASK,
+ WCD9335_ANA_BIAS_DISABLE);
+ regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS,
+ WCD9335_ANA_BIAS_PRECHRG_CTL_MODE,
+ WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL);
+ }
+ return 0;
+}
+
+static int wcd9335_cdc_req_mclk_enable(struct wcd9335_codec *wcd,
+ bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ wcd9335_cdc_sido_ccl_enable(wcd, true);
+ ret = clk_prepare_enable(wcd->mclk);
+ if (ret) {
+ dev_err(wcd->dev, "%s: ext clk enable failed\n",
+ __func__);
+ goto err;
+ }
+ /* get BG */
+ wcd9335_enable_master_bias(wcd);
+ /* get MCLK */
+ wcd9335_enable_mclk(wcd);
+
+ } else {
+ /* put MCLK */
+ wcd9335_disable_mclk(wcd);
+ /* put BG */
+ wcd9335_disable_master_bias(wcd);
+ clk_disable_unprepare(wcd->mclk);
+ wcd9335_cdc_sido_ccl_enable(wcd, false);
+ }
+err:
+ return ret;
+}
+
+static void wcd9335_codec_apply_sido_voltage(struct wcd9335_codec *wcd,
+ enum wcd9335_sido_voltage req_mv)
+{
+ struct snd_soc_component *comp = wcd->component;
+ int vout_d_val;
+
+ if (req_mv == wcd->sido_voltage)
+ return;
+
+ /* compute the vout_d step value */
+ vout_d_val = WCD9335_CALCULATE_VOUT_D(req_mv) &
+ WCD9335_ANA_BUCK_VOUT_MASK;
+ snd_soc_component_write(comp, WCD9335_ANA_BUCK_VOUT_D, vout_d_val);
+ snd_soc_component_update_bits(comp, WCD9335_ANA_BUCK_CTL,
+ WCD9335_ANA_BUCK_CTL_RAMP_START_MASK,
+ WCD9335_ANA_BUCK_CTL_RAMP_START_ENABLE);
+
+ /* 1 msec sleep required after SIDO Vout_D voltage change */
+ usleep_range(1000, 1100);
+ wcd->sido_voltage = req_mv;
+ snd_soc_component_update_bits(comp, WCD9335_ANA_BUCK_CTL,
+ WCD9335_ANA_BUCK_CTL_RAMP_START_MASK,
+ WCD9335_ANA_BUCK_CTL_RAMP_START_DISABLE);
+}
+
+static int wcd9335_codec_update_sido_voltage(struct wcd9335_codec *wcd,
+ enum wcd9335_sido_voltage req_mv)
+{
+ int ret = 0;
+
+ /* enable mclk before setting SIDO voltage */
+ ret = wcd9335_cdc_req_mclk_enable(wcd, true);
+ if (ret) {
+ dev_err(wcd->dev, "Ext clk enable failed\n");
+ goto err;
+ }
+
+ wcd9335_codec_apply_sido_voltage(wcd, req_mv);
+ wcd9335_cdc_req_mclk_enable(wcd, false);
+
+err:
+ return ret;
+}
+
+static int _wcd9335_codec_enable_mclk(struct snd_soc_component *component,
+ int enable)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ int ret;
+
+ if (enable) {
+ ret = wcd9335_cdc_req_mclk_enable(wcd, true);
+ if (ret)
+ return ret;
+
+ wcd9335_codec_apply_sido_voltage(wcd,
+ SIDO_VOLTAGE_NOMINAL_MV);
+ } else {
+ wcd9335_codec_update_sido_voltage(wcd,
+ wcd->sido_voltage);
+ wcd9335_cdc_req_mclk_enable(wcd, false);
+ }
+
+ return 0;
+}
+
+static int wcd9335_codec_enable_mclk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return _wcd9335_codec_enable_mclk(comp, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return _wcd9335_codec_enable_mclk(comp, false);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wcd9335_dapm_widgets[] = {
+ /* TODO SPK1 & SPK2 OUT*/
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT3"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT4"),
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
+ AIF4_PB, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD9335_RX0, 0,
+ &slim_rx_mux[WCD9335_RX0]),
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD9335_RX1, 0,
+ &slim_rx_mux[WCD9335_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD9335_RX2, 0,
+ &slim_rx_mux[WCD9335_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD9335_RX3, 0,
+ &slim_rx_mux[WCD9335_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD9335_RX4, 0,
+ &slim_rx_mux[WCD9335_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD9335_RX5, 0,
+ &slim_rx_mux[WCD9335_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD9335_RX6, 0,
+ &slim_rx_mux[WCD9335_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD9335_RX7, 0,
+ &slim_rx_mux[WCD9335_RX7]),
+ SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int0_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int1_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int2_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int3_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int4_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int5_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int6_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int7_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int8_2_mux, wcd9335_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int5_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int5_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int5_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int6_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int6_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int6_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp2_mux),
+
+ SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT6 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int0_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int2_dem_inp_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM,
+ INTERP_EAR, 0, &rx_int0_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM,
+ INTERP_HPHL, 0, &rx_int1_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM,
+ INTERP_HPHR, 0, &rx_int2_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM,
+ INTERP_LO1, 0, &rx_int3_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM,
+ INTERP_LO2, 0, &rx_int4_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM,
+ INTERP_LO3, 0, &rx_int5_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM,
+ INTERP_LO4, 0, &rx_int6_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM,
+ INTERP_SPKR1, 0, &rx_int7_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM,
+ INTERP_SPKR2, 0, &rx_int8_interp_mux,
+ wcd9335_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd9335_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD9335_ANA_HPH,
+ 5, 0, wcd9335_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD9335_ANA_HPH,
+ 4, 0, wcd9335_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd9335_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd9335_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd9335_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd9335_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0,
+ wcd9335_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PA", WCD9335_ANA_HPH, 6, 0, NULL, 0,
+ wcd9335_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0,
+ wcd9335_codec_enable_ear_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0,
+ wcd9335_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0,
+ wcd9335_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0,
+ wcd9335_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0,
+ wcd9335_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_mclk, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* TX */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC4"),
+ SND_SOC_DAPM_INPUT("AMIC5"),
+ SND_SOC_DAPM_INPUT("AMIC6"),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, wcd9335_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0,
+ wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0,
+ wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0,
+ wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0,
+ wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0,
+ wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0,
+ wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+ wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux0),
+ SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux1),
+ SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux2),
+ SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux3),
+ SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux4),
+ SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux5),
+ SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux6),
+ SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux7),
+ SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux8),
+
+ SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux0),
+ SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux1),
+ SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux2),
+ SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux3),
+ SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux4),
+ SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux5),
+ SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux6),
+ SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux7),
+ SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux8),
+
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, WCD9335_TX0, 0,
+ &sb_tx0_mux),
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, WCD9335_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, WCD9335_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, WCD9335_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, WCD9335_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, WCD9335_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, WCD9335_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, WCD9335_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, WCD9335_TX8, 0,
+ &sb_tx8_mux),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux0, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux1, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux2, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux3, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux4, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux5, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux6, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux7, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux8, wcd9335_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+};
+
+static void wcd9335_enable_sido_buck(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, WCD9335_ANA_RCO,
+ WCD9335_ANA_RCO_BG_EN_MASK,
+ WCD9335_ANA_RCO_BG_ENABLE);
+ snd_soc_component_update_bits(component, WCD9335_ANA_BUCK_CTL,
+ WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_MASK,
+ WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_EXT);
+ /* 100us sleep needed after IREF settings */
+ usleep_range(100, 110);
+ snd_soc_component_update_bits(component, WCD9335_ANA_BUCK_CTL,
+ WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_MASK,
+ WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT);
+ /* 100us sleep needed after VREF settings */
+ usleep_range(100, 110);
+}
+
+static int wcd9335_enable_efuse_sensing(struct snd_soc_component *comp)
+{
+ _wcd9335_codec_enable_mclk(comp, true);
+ snd_soc_component_update_bits(comp,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_ENABLE);
+ /*
+ * 5ms sleep required after enabling efuse control
+ * before checking the status.
+ */
+ usleep_range(5000, 5500);
+
+ if (!(snd_soc_component_read(comp,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) &
+ WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK))
+ WARN(1, "%s: Efuse sense is not complete\n", __func__);
+
+ wcd9335_enable_sido_buck(comp);
+ _wcd9335_codec_enable_mclk(comp, false);
+
+ return 0;
+}
+
+static void wcd9335_codec_init(struct snd_soc_component *component)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ int i;
+
+ /* ungate MCLK and set clk rate */
+ regmap_update_bits(wcd->regmap, WCD9335_CODEC_RPM_CLK_GATE,
+ WCD9335_CODEC_RPM_CLK_GATE_MCLK_GATE_MASK, 0);
+
+ regmap_update_bits(wcd->regmap, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ);
+
+ for (i = 0; i < ARRAY_SIZE(wcd9335_codec_reg_init); i++)
+ snd_soc_component_update_bits(component,
+ wcd9335_codec_reg_init[i].reg,
+ wcd9335_codec_reg_init[i].mask,
+ wcd9335_codec_reg_init[i].val);
+
+ wcd9335_enable_efuse_sensing(component);
+}
+
+static int wcd9335_codec_probe(struct snd_soc_component *component)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
+ int ret;
+ int i;
+
+ snd_soc_component_init_regmap(component, wcd->regmap);
+ /* Class-H Init*/
+ wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, WCD9335);
+ if (IS_ERR(wcd->clsh_ctrl))
+ return PTR_ERR(wcd->clsh_ctrl);
+
+ /* Default HPH Mode to Class-H HiFi */
+ wcd->hph_mode = CLS_H_HIFI;
+ wcd->component = component;
+
+ wcd9335_codec_init(component);
+
+ for (i = 0; i < NUM_CODEC_DAIS; i++)
+ INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list);
+
+ ret = wcd9335_setup_irqs(wcd);
+ if (ret)
+ goto free_clsh_ctrl;
+
+ return 0;
+
+free_clsh_ctrl:
+ wcd_clsh_ctrl_free(wcd->clsh_ctrl);
+ return ret;
+}
+
+static void wcd9335_codec_remove(struct snd_soc_component *comp)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+
+ wcd_clsh_ctrl_free(wcd->clsh_ctrl);
+ wcd9335_teardown_irqs(wcd);
+}
+
+static int wcd9335_codec_set_sysclk(struct snd_soc_component *comp,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev);
+
+ wcd->mclk_rate = freq;
+
+ if (wcd->mclk_rate == WCD9335_MCLK_CLK_12P288MHZ)
+ snd_soc_component_update_bits(comp,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ);
+ else if (wcd->mclk_rate == WCD9335_MCLK_CLK_9P6MHZ)
+ snd_soc_component_update_bits(comp,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK,
+ WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ);
+
+ return clk_set_rate(wcd->mclk, freq);
+}
+
+static const struct snd_soc_component_driver wcd9335_component_drv = {
+ .probe = wcd9335_codec_probe,
+ .remove = wcd9335_codec_remove,
+ .set_sysclk = wcd9335_codec_set_sysclk,
+ .controls = wcd9335_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd9335_snd_controls),
+ .dapm_widgets = wcd9335_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wcd9335_dapm_widgets),
+ .dapm_routes = wcd9335_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map),
+ .endianness = 1,
+};
+
+static int wcd9335_probe(struct wcd9335_codec *wcd)
+{
+ struct device *dev = wcd->dev;
+
+ memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs));
+ memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs));
+
+ wcd->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV;
+
+ return devm_snd_soc_register_component(dev, &wcd9335_component_drv,
+ wcd9335_slim_dais,
+ ARRAY_SIZE(wcd9335_slim_dais));
+}
+
+static const struct regmap_range_cfg wcd9335_ranges[] = {
+ {
+ .name = "WCD9335",
+ .range_min = 0x0,
+ .range_max = WCD9335_MAX_REGISTER,
+ .selector_reg = WCD9335_SEL_REGISTER,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0x800,
+ .window_len = 0x100,
+ },
+};
+
+static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD9335_INTR_PIN1_STATUS0...WCD9335_INTR_PIN2_CLEAR3:
+ case WCD9335_ANA_MBHC_RESULT_3:
+ case WCD9335_ANA_MBHC_RESULT_2:
+ case WCD9335_ANA_MBHC_RESULT_1:
+ case WCD9335_ANA_MBHC_MECH:
+ case WCD9335_ANA_MBHC_ELECT:
+ case WCD9335_ANA_MBHC_ZDET:
+ case WCD9335_ANA_MICB2:
+ case WCD9335_ANA_RCO:
+ case WCD9335_ANA_BIAS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config wcd9335_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .max_register = WCD9335_MAX_REGISTER,
+ .can_multi_write = true,
+ .ranges = wcd9335_ranges,
+ .num_ranges = ARRAY_SIZE(wcd9335_ranges),
+ .volatile_reg = wcd9335_is_volatile_register,
+};
+
+static const struct regmap_range_cfg wcd9335_ifc_ranges[] = {
+ {
+ .name = "WCD9335-IFC-DEV",
+ .range_min = 0x0,
+ .range_max = WCD9335_MAX_REGISTER,
+ .selector_reg = WCD9335_SEL_REGISTER,
+ .selector_mask = 0xfff,
+ .selector_shift = 0,
+ .window_start = 0x800,
+ .window_len = 0x400,
+ },
+};
+
+static const struct regmap_config wcd9335_ifc_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .can_multi_write = true,
+ .max_register = WCD9335_MAX_REGISTER,
+ .ranges = wcd9335_ifc_ranges,
+ .num_ranges = ARRAY_SIZE(wcd9335_ifc_ranges),
+};
+
+static const struct regmap_irq wcd9335_codec_irqs[] = {
+ /* INTR_REG 0 */
+ [WCD9335_IRQ_SLIMBUS] = {
+ .reg_offset = 0,
+ .mask = BIT(0),
+ .type = {
+ .type_reg_offset = 0,
+ .types_supported = IRQ_TYPE_EDGE_BOTH,
+ .type_reg_mask = BIT(0),
+ },
+ },
+};
+
+static const unsigned int wcd9335_config_regs[] = {
+ WCD9335_INTR_LEVEL0,
+};
+
+static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = {
+ .name = "wcd9335_pin1_irq",
+ .status_base = WCD9335_INTR_PIN1_STATUS0,
+ .mask_base = WCD9335_INTR_PIN1_MASK0,
+ .ack_base = WCD9335_INTR_PIN1_CLEAR0,
+ .num_regs = 4,
+ .irqs = wcd9335_codec_irqs,
+ .num_irqs = ARRAY_SIZE(wcd9335_codec_irqs),
+ .config_base = wcd9335_config_regs,
+ .num_config_bases = ARRAY_SIZE(wcd9335_config_regs),
+ .num_config_regs = 4,
+ .set_type_config = regmap_irq_set_type_config_simple,
+};
+
+static int wcd9335_parse_dt(struct wcd9335_codec *wcd)
+{
+ struct device *dev = wcd->dev;
+ int ret;
+
+ wcd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd->reset_gpio), "Reset GPIO missing from DT\n");
+
+ wcd->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(wcd->mclk))
+ return dev_err_probe(dev, PTR_ERR(wcd->mclk), "mclk not found\n");
+
+ wcd->native_clk = devm_clk_get(dev, "slimbus");
+ if (IS_ERR(wcd->native_clk))
+ return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n");
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd9335_supplies),
+ wcd9335_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ return 0;
+}
+
+static int wcd9335_power_on_reset(struct wcd9335_codec *wcd)
+{
+ /*
+ * For WCD9335, it takes about 600us for the Vout_A and
+ * Vout_D to be ready after BUCK_SIDO is powered up.
+ * SYS_RST_N shouldn't be pulled high during this time
+ * Toggle the reset line to make sure the reset pulse is
+ * correctly applied
+ */
+ usleep_range(600, 650);
+
+ gpiod_set_value(wcd->reset_gpio, 1);
+ msleep(20);
+ gpiod_set_value(wcd->reset_gpio, 0);
+ msleep(20);
+
+ return 0;
+}
+
+static int wcd9335_bring_up(struct wcd9335_codec *wcd)
+{
+ struct regmap *rm = wcd->regmap;
+ int val, byte0;
+
+ regmap_read(rm, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val);
+ regmap_read(rm, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0);
+
+ if ((val < 0) || (byte0 < 0)) {
+ dev_err(wcd->dev, "WCD9335 CODEC version detection fail!\n");
+ return -EINVAL;
+ }
+
+ if (byte0 == 0x1) {
+ dev_info(wcd->dev, "WCD9335 CODEC version is v2.0\n");
+ regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+ regmap_write(rm, WCD9335_SIDO_SIDO_TEST_2, 0x00);
+ regmap_write(rm, WCD9335_SIDO_SIDO_CCL_8, 0x6F);
+ regmap_write(rm, WCD9335_BIAS_VBG_FINE_ADJ, 0x65);
+ regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+ regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+ regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+ regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+ } else {
+ dev_err(wcd->dev, "WCD9335 CODEC version not supported\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wcd9335_irq_init(struct wcd9335_codec *wcd)
+{
+ int ret;
+
+ /*
+ * INTR1 consists of all possible interrupt sources Ear OCP,
+ * HPH OCP, MBHC, MAD, VBAT, and SVA
+ * INTR2 is a subset of first interrupt sources MAD, VBAT, and SVA
+ */
+ wcd->intr1 = of_irq_get_byname(wcd->dev->of_node, "intr1");
+ if (wcd->intr1 < 0)
+ return dev_err_probe(wcd->dev, wcd->intr1,
+ "Unable to configure IRQ\n");
+
+ ret = devm_regmap_add_irq_chip(wcd->dev, wcd->regmap, wcd->intr1,
+ IRQF_TRIGGER_HIGH, 0,
+ &wcd9335_regmap_irq1_chip, &wcd->irq_data);
+ if (ret)
+ return dev_err_probe(wcd->dev, ret, "Failed to register IRQ chip\n");
+
+ return 0;
+}
+
+static int wcd9335_slim_probe(struct slim_device *slim)
+{
+ struct device *dev = &slim->dev;
+ struct wcd9335_codec *wcd;
+ int ret;
+
+ wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
+ if (!wcd)
+ return -ENOMEM;
+
+ wcd->dev = dev;
+ ret = wcd9335_parse_dt(wcd);
+ if (ret)
+ return ret;
+
+ ret = wcd9335_power_on_reset(wcd);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, wcd);
+
+ return 0;
+}
+
+static int wcd9335_slim_status(struct slim_device *sdev,
+ enum slim_device_status status)
+{
+ struct device *dev = &sdev->dev;
+ struct device_node *ifc_dev_np;
+ struct wcd9335_codec *wcd;
+ int ret;
+
+ wcd = dev_get_drvdata(dev);
+
+ ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0);
+ if (!ifc_dev_np) {
+ dev_err(dev, "No Interface device found\n");
+ return -EINVAL;
+ }
+
+ wcd->slim = sdev;
+ wcd->slim_ifc_dev = of_slim_get_device(sdev->ctrl, ifc_dev_np);
+ of_node_put(ifc_dev_np);
+ if (!wcd->slim_ifc_dev) {
+ dev_err(dev, "Unable to get SLIM Interface device\n");
+ return -EINVAL;
+ }
+
+ slim_get_logical_addr(wcd->slim_ifc_dev);
+
+ wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config);
+ if (IS_ERR(wcd->regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+ "Failed to allocate slim register map\n");
+
+ wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev,
+ &wcd9335_ifc_regmap_config);
+ if (IS_ERR(wcd->if_regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
+ "Failed to allocate ifc register map\n");
+
+ ret = wcd9335_bring_up(wcd);
+ if (ret) {
+ dev_err(dev, "Failed to bringup WCD9335\n");
+ return ret;
+ }
+
+ ret = wcd9335_irq_init(wcd);
+ if (ret)
+ return ret;
+
+ wcd9335_probe(wcd);
+
+ return 0;
+}
+
+static const struct slim_device_id wcd9335_slim_id[] = {
+ {SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9335, 0x1, 0x0},
+ {}
+};
+MODULE_DEVICE_TABLE(slim, wcd9335_slim_id);
+
+static struct slim_driver wcd9335_slim_driver = {
+ .driver = {
+ .name = "wcd9335-slim",
+ },
+ .probe = wcd9335_slim_probe,
+ .device_status = wcd9335_slim_status,
+ .id_table = wcd9335_slim_id,
+};
+
+module_slim_driver(wcd9335_slim_driver);
+MODULE_DESCRIPTION("WCD9335 slim driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h
new file mode 100644
index 000000000000..490fc44144a2
--- /dev/null
+++ b/sound/soc/codecs/wcd9335.h
@@ -0,0 +1,641 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __WCD9335_H__
+#define __WCD9335_H__
+
+/*
+ * WCD9335 register base can change according to the mode it works in.
+ * In slimbus mode the reg base starts from 0x800.
+ * In i2s/i2c mode the reg base is 0x0.
+ */
+#define WCD9335_REG(pg, r) ((pg << 8) | (r))
+#define WCD9335_REG_OFFSET(r) (r & 0xFF)
+#define WCD9335_PAGE_OFFSET(r) ((r >> 8) & 0xFF)
+
+/* Page-0 Registers */
+#define WCD9335_PAGE0_PAGE_REGISTER WCD9335_REG(0x00, 0x000)
+#define WCD9335_CODEC_RPM_CLK_GATE WCD9335_REG(0x00, 0x002)
+#define WCD9335_CODEC_RPM_CLK_GATE_MCLK_GATE_MASK GENMASK(1, 0)
+#define WCD9335_CODEC_RPM_CLK_MCLK_CFG WCD9335_REG(0x00, 0x003)
+#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ BIT(0)
+#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ BIT(0)
+#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK GENMASK(1, 0)
+#define WCD9335_CODEC_RPM_RST_CTL WCD9335_REG(0x00, 0x009)
+#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL WCD9335_REG(0x00, 0x011)
+#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0 WCD9335_REG(0x00, 0x021)
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL WCD9335_REG(0x00, 0x025)
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_SSTATE_MASK GENMASK(4, 1)
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK BIT(0)
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_ENABLE BIT(0)
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 WCD9335_REG(0x00, 0x029)
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS WCD9335_REG(0x00, 0x039)
+#define WCD9335_INTR_CFG WCD9335_REG(0x00, 0x081)
+#define WCD9335_INTR_CLR_COMMIT WCD9335_REG(0x00, 0x082)
+#define WCD9335_INTR_PIN1_MASK0 WCD9335_REG(0x00, 0x089)
+#define WCD9335_INTR_PIN1_MASK1 WCD9335_REG(0x00, 0x08a)
+#define WCD9335_INTR_PIN1_MASK2 WCD9335_REG(0x00, 0x08b)
+#define WCD9335_INTR_PIN1_MASK3 WCD9335_REG(0x00, 0x08c)
+#define WCD9335_INTR_PIN1_STATUS0 WCD9335_REG(0x00, 0x091)
+#define WCD9335_INTR_PIN1_STATUS1 WCD9335_REG(0x00, 0x092)
+#define WCD9335_INTR_PIN1_STATUS2 WCD9335_REG(0x00, 0x093)
+#define WCD9335_INTR_PIN1_STATUS3 WCD9335_REG(0x00, 0x094)
+#define WCD9335_INTR_PIN1_CLEAR0 WCD9335_REG(0x00, 0x099)
+#define WCD9335_INTR_PIN1_CLEAR1 WCD9335_REG(0x00, 0x09a)
+#define WCD9335_INTR_PIN1_CLEAR2 WCD9335_REG(0x00, 0x09b)
+#define WCD9335_INTR_PIN1_CLEAR3 WCD9335_REG(0x00, 0x09c)
+#define WCD9335_INTR_PIN2_MASK0 WCD9335_REG(0x00, 0x0a1)
+#define WCD9335_INTR_PIN2_MASK1 WCD9335_REG(0x00, 0x0a2)
+#define WCD9335_INTR_PIN2_MASK2 WCD9335_REG(0x00, 0x0a3)
+#define WCD9335_INTR_PIN2_MASK3 WCD9335_REG(0x00, 0x0a4)
+#define WCD9335_INTR_PIN2_STATUS0 WCD9335_REG(0x00, 0x0a9)
+#define WCD9335_INTR_PIN2_STATUS1 WCD9335_REG(0x00, 0x0aa)
+#define WCD9335_INTR_PIN2_STATUS2 WCD9335_REG(0x00, 0x0ab)
+#define WCD9335_INTR_PIN2_STATUS3 WCD9335_REG(0x00, 0x0ac)
+#define WCD9335_INTR_PIN2_CLEAR0 WCD9335_REG(0x00, 0x0b1)
+#define WCD9335_INTR_PIN2_CLEAR1 WCD9335_REG(0x00, 0x0b2)
+#define WCD9335_INTR_PIN2_CLEAR2 WCD9335_REG(0x00, 0x0b3)
+#define WCD9335_INTR_PIN2_CLEAR3 WCD9335_REG(0x00, 0x0b4)
+#define WCD9335_INTR_LEVEL0 WCD9335_REG(0x00, 0x0e1)
+#define WCD9335_INTR_LEVEL1 WCD9335_REG(0x00, 0x0e2)
+#define WCD9335_INTR_LEVEL2 WCD9335_REG(0x00, 0x0e3)
+#define WCD9335_INTR_LEVEL3 WCD9335_REG(0x00, 0x0e4)
+
+/* Page-1 Registers */
+#define WCD9335_CPE_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x001)
+#define WCD9335_CPE_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x002)
+#define WCD9335_CPE_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x003)
+#define WCD9335_CPE_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x004)
+#define WCD9335_CPE_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x005)
+#define WCD9335_CPE_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x006)
+#define WCD9335_CPE_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x007)
+#define WCD9335_CPE_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x008)
+#define WCD9335_CPE_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x009)
+#define WCD9335_CPE_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x00a)
+#define WCD9335_CPE_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x00b)
+#define WCD9335_CPE_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x00c)
+#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x00d)
+#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x00e)
+#define WCD9335_CPE_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x00f)
+#define WCD9335_CPE_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x010)
+#define WCD9335_CPE_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x011)
+#define WCD9335_CPE_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x012)
+#define WCD9335_CPE_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x013)
+#define WCD9335_CPE_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x014)
+#define WCD9335_CPE_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x015)
+#define WCD9335_CPE_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x016)
+#define WCD9335_CPE_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x017)
+#define WCD9335_CPE_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x018)
+#define WCD9335_CPE_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x019)
+#define WCD9335_CPE_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x01a)
+#define WCD9335_CPE_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x01b)
+#define WCD9335_CPE_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x01c)
+#define WCD9335_CPE_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x01d)
+#define WCD9335_CPE_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x01e)
+#define WCD9335_CPE_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x01f)
+#define WCD9335_CPE_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x020)
+#define WCD9335_CPE_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x021)
+#define WCD9335_CPE_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x022)
+#define WCD9335_CPE_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x023)
+#define WCD9335_CPE_FLL_FLL_MODE WCD9335_REG(0x01, 0x024)
+#define WCD9335_CPE_FLL_STATUS_0 WCD9335_REG(0x01, 0x025)
+#define WCD9335_CPE_FLL_STATUS_1 WCD9335_REG(0x01, 0x026)
+#define WCD9335_CPE_FLL_STATUS_2 WCD9335_REG(0x01, 0x027)
+#define WCD9335_CPE_FLL_STATUS_3 WCD9335_REG(0x01, 0x028)
+#define WCD9335_I2S_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x041)
+#define WCD9335_I2S_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x042)
+#define WCD9335_I2S_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x043)
+#define WCD9335_I2S_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x044)
+#define WCD9335_I2S_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x045)
+#define WCD9335_I2S_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x046)
+#define WCD9335_I2S_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x047)
+#define WCD9335_I2S_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x048)
+#define WCD9335_I2S_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x049)
+#define WCD9335_I2S_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x04a)
+#define WCD9335_I2S_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x04b)
+#define WCD9335_I2S_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x04c)
+#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x04d)
+#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x04e)
+#define WCD9335_I2S_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x04f)
+#define WCD9335_I2S_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x050)
+#define WCD9335_I2S_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x051)
+#define WCD9335_I2S_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x052)
+#define WCD9335_I2S_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x053)
+#define WCD9335_I2S_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x054)
+#define WCD9335_I2S_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x055)
+#define WCD9335_I2S_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x056)
+#define WCD9335_I2S_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x057)
+#define WCD9335_I2S_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x058)
+#define WCD9335_I2S_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x059)
+#define WCD9335_I2S_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x05a)
+#define WCD9335_I2S_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x05b)
+#define WCD9335_I2S_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x05c)
+#define WCD9335_I2S_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x05d)
+#define WCD9335_I2S_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x05e)
+#define WCD9335_I2S_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x05f)
+#define WCD9335_I2S_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x060)
+#define WCD9335_I2S_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x061)
+#define WCD9335_I2S_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x062)
+#define WCD9335_I2S_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x063)
+#define WCD9335_I2S_FLL_FLL_MODE WCD9335_REG(0x01, 0x064)
+#define WCD9335_I2S_FLL_STATUS_0 WCD9335_REG(0x01, 0x065)
+#define WCD9335_I2S_FLL_STATUS_1 WCD9335_REG(0x01, 0x066)
+#define WCD9335_I2S_FLL_STATUS_2 WCD9335_REG(0x01, 0x067)
+#define WCD9335_I2S_FLL_STATUS_3 WCD9335_REG(0x01, 0x068)
+#define WCD9335_SB_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x081)
+#define WCD9335_SB_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x082)
+#define WCD9335_SB_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x083)
+#define WCD9335_SB_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x084)
+#define WCD9335_SB_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x085)
+#define WCD9335_SB_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x086)
+#define WCD9335_SB_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x087)
+#define WCD9335_SB_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x088)
+#define WCD9335_SB_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x089)
+#define WCD9335_SB_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x08a)
+#define WCD9335_SB_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x08b)
+#define WCD9335_SB_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x08c)
+#define WCD9335_SB_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x08d)
+#define WCD9335_SB_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x08e)
+#define WCD9335_SB_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x08f)
+#define WCD9335_SB_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x090)
+#define WCD9335_SB_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x091)
+#define WCD9335_SB_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x092)
+#define WCD9335_SB_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x093)
+#define WCD9335_SB_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x094)
+#define WCD9335_SB_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x095)
+#define WCD9335_SB_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x096)
+#define WCD9335_SB_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x097)
+#define WCD9335_SB_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x098)
+#define WCD9335_SB_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x099)
+#define WCD9335_SB_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x09a)
+#define WCD9335_SB_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x09b)
+#define WCD9335_SB_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x09c)
+#define WCD9335_SB_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x09d)
+#define WCD9335_SB_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x09e)
+#define WCD9335_SB_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x09f)
+#define WCD9335_SB_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x0a0)
+#define WCD9335_SB_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x0a1)
+#define WCD9335_SB_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x0a2)
+#define WCD9335_SB_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x0a3)
+#define WCD9335_SB_FLL_FLL_MODE WCD9335_REG(0x01, 0x0a4)
+#define WCD9335_SB_FLL_STATUS_0 WCD9335_REG(0x01, 0x0a5)
+#define WCD9335_SB_FLL_STATUS_1 WCD9335_REG(0x01, 0x0a6)
+#define WCD9335_SB_FLL_STATUS_2 WCD9335_REG(0x01, 0x0a7)
+#define WCD9335_SB_FLL_STATUS_3 WCD9335_REG(0x01, 0x0a8)
+
+/* Page-2 Registers */
+#define WCD9335_PAGE2_PAGE_REGISTER WCD9335_REG(0x02, 0x000)
+#define WCD9335_CPE_SS_DMIC0_CTL WCD9335_REG(0x02, 0x063)
+#define WCD9335_CPE_SS_DMIC1_CTL WCD9335_REG(0x02, 0x064)
+#define WCD9335_CPE_SS_DMIC2_CTL WCD9335_REG(0x02, 0x065)
+#define WCD9335_CPE_SS_DMIC_CFG WCD9335_REG(0x02, 0x066)
+#define WCD9335_SOC_MAD_AUDIO_CTL_2 WCD9335_REG(0x02, 0x084)
+
+/* Page-6 Registers */
+#define WCD9335_PAGE6_PAGE_REGISTER WCD9335_REG(0x06, 0x000)
+#define WCD9335_ANA_BIAS WCD9335_REG(0x06, 0x001)
+#define WCD9335_ANA_BIAS_EN_MASK BIT(7)
+#define WCD9335_ANA_BIAS_ENABLE BIT(7)
+#define WCD9335_ANA_BIAS_DISABLE 0
+#define WCD9335_ANA_BIAS_PRECHRG_EN_MASK BIT(6)
+#define WCD9335_ANA_BIAS_PRECHRG_ENABLE BIT(6)
+#define WCD9335_ANA_BIAS_PRECHRG_DISABLE 0
+#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE BIT(5)
+#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_AUTO BIT(5)
+#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL 0
+#define WCD9335_ANA_CLK_TOP WCD9335_REG(0x06, 0x002)
+#define WCD9335_ANA_CLK_MCLK_EN_MASK BIT(2)
+#define WCD9335_ANA_CLK_MCLK_ENABLE BIT(2)
+#define WCD9335_ANA_CLK_MCLK_DISABLE 0
+#define WCD9335_ANA_CLK_MCLK_SRC_MASK BIT(3)
+#define WCD9335_ANA_CLK_MCLK_SRC_RCO BIT(3)
+#define WCD9335_ANA_CLK_MCLK_SRC_EXTERNAL 0
+#define WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK BIT(7)
+#define WCD9335_ANA_CLK_EXT_CLKBUF_ENABLE BIT(7)
+#define WCD9335_ANA_CLK_EXT_CLKBUF_DISABLE 0
+#define WCD9335_ANA_RCO WCD9335_REG(0x06, 0x003)
+#define WCD9335_ANA_RCO_BG_EN_MASK BIT(7)
+#define WCD9335_ANA_RCO_BG_ENABLE BIT(7)
+#define WCD9335_ANA_BUCK_VOUT_D WCD9335_REG(0x06, 0x005)
+#define WCD9335_ANA_BUCK_VOUT_MASK GENMASK(7, 0)
+#define WCD9335_ANA_BUCK_CTL WCD9335_REG(0x06, 0x006)
+#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_MASK BIT(1)
+#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_EXT BIT(1)
+#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_INT 0
+#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_MASK BIT(2)
+#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT BIT(2)
+#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_INT 0
+#define WCD9335_ANA_BUCK_CTL_RAMP_START_MASK BIT(7)
+#define WCD9335_ANA_BUCK_CTL_RAMP_START_ENABLE BIT(7)
+#define WCD9335_ANA_BUCK_CTL_RAMP_START_DISABLE 0
+#define WCD9335_ANA_RX_SUPPLIES WCD9335_REG(0x06, 0x008)
+#define WCD9335_ANA_RX_BIAS_ENABLE_MASK BIT(0)
+#define WCD9335_ANA_RX_BIAS_ENABLE BIT(0)
+#define WCD9335_ANA_RX_BIAS_DISABLE 0
+#define WCD9335_ANA_HPH WCD9335_REG(0x06, 0x009)
+#define WCD9335_ANA_EAR WCD9335_REG(0x06, 0x00a)
+#define WCD9335_ANA_LO_1_2 WCD9335_REG(0x06, 0x00b)
+#define WCD9335_ANA_LO_3_4 WCD9335_REG(0x06, 0x00c)
+#define WCD9335_ANA_AMIC1 WCD9335_REG(0x06, 0x00e)
+#define WCD9335_ANA_AMIC2 WCD9335_REG(0x06, 0x00f)
+#define WCD9335_ANA_AMIC3 WCD9335_REG(0x06, 0x010)
+#define WCD9335_ANA_AMIC4 WCD9335_REG(0x06, 0x011)
+#define WCD9335_ANA_AMIC5 WCD9335_REG(0x06, 0x012)
+#define WCD9335_ANA_AMIC6 WCD9335_REG(0x06, 0x013)
+#define WCD9335_ANA_MBHC_MECH WCD9335_REG(0x06, 0x014)
+#define WCD9335_MBHC_L_DET_EN_MASK BIT(7)
+#define WCD9335_MBHC_L_DET_EN BIT(7)
+#define WCD9335_MBHC_GND_DET_EN_MASK BIT(6)
+#define WCD9335_MBHC_MECH_DETECT_TYPE_MASK BIT(5)
+#define WCD9335_MBHC_MECH_DETECT_TYPE_SHIFT 5
+#define WCD9335_MBHC_HPHL_PLUG_TYPE_MASK BIT(4)
+#define WCD9335_MBHC_HPHL_PLUG_TYPE_NO BIT(4)
+#define WCD9335_MBHC_GND_PLUG_TYPE_MASK BIT(3)
+#define WCD9335_MBHC_GND_PLUG_TYPE_NO BIT(3)
+#define WCD9335_MBHC_HSL_PULLUP_COMP_EN BIT(2)
+#define WCD9335_MBHC_HPHL_100K_TO_GND_EN BIT(0)
+
+#define WCD9335_ANA_MBHC_ELECT WCD9335_REG(0x06, 0x015)
+#define WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4)
+#define WCD9335_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4)
+#define WCD9335_ANA_MBHC_BD_ISRC_OFF 0
+#define WCD9335_ANA_MBHC_BIAS_EN_MASK BIT(0)
+#define WCD9335_ANA_MBHC_BIAS_EN BIT(0)
+#define WCD9335_ANA_MBHC_ZDET WCD9335_REG(0x06, 0x016)
+#define WCD9335_ANA_MBHC_RESULT_1 WCD9335_REG(0x06, 0x017)
+#define WCD9335_ANA_MBHC_RESULT_2 WCD9335_REG(0x06, 0x018)
+#define WCD9335_ANA_MBHC_RESULT_3 WCD9335_REG(0x06, 0x019)
+#define WCD9335_MBHC_BTN_RESULT_MASK GENMASK(2, 0)
+#define WCD9335_ANA_MBHC_BTN0 WCD9335_REG(0x06, 0x01a)
+#define WCD9335_ANA_MBHC_BTN1 WCD9335_REG(0x06, 0x01b)
+#define WCD9335_ANA_MBHC_BTN2 WCD9335_REG(0x06, 0x01c)
+#define WCD9335_ANA_MBHC_BTN3 WCD9335_REG(0x06, 0x01d)
+#define WCD9335_ANA_MBHC_BTN4 WCD9335_REG(0x06, 0x01e)
+#define WCD9335_ANA_MBHC_BTN5 WCD9335_REG(0x06, 0x01f)
+#define WCD9335_ANA_MBHC_BTN6 WCD9335_REG(0x06, 0x020)
+#define WCD9335_ANA_MBHC_BTN7 WCD9335_REG(0x06, 0x021)
+#define WCD9335_ANA_MICB1 WCD9335_REG(0x06, 0x022)
+#define WCD9335_ANA_MICB2 WCD9335_REG(0x06, 0x023)
+#define WCD9335_ANA_MICB2_ENABLE BIT(6)
+#define WCD9335_ANA_MICB2_RAMP WCD9335_REG(0x06, 0x024)
+#define WCD9335_ANA_MICB3 WCD9335_REG(0x06, 0x025)
+#define WCD9335_ANA_MICB4 WCD9335_REG(0x06, 0x026)
+#define WCD9335_ANA_VBADC WCD9335_REG(0x06, 0x027)
+#define WCD9335_BIAS_VBG_FINE_ADJ WCD9335_REG(0x06, 0x029)
+#define WCD9335_RCO_CTRL_2 WCD9335_REG(0x06, 0x02f)
+#define WCD9335_SIDO_SIDO_CCL_2 WCD9335_REG(0x06, 0x042)
+#define WCD9335_SIDO_SIDO_CCL_4 WCD9335_REG(0x06, 0x044)
+#define WCD9335_SIDO_SIDO_CCL_8 WCD9335_REG(0x06, 0x048)
+#define WCD9335_SIDO_SIDO_CCL_10 WCD9335_REG(0x06, 0x04a)
+#define WCD9335_SIDO_SIDO_CCL_10_ICHARG_PWR_SEL_C320FF 0x2
+/* Comparator 1 and 2 Bias current at 1P0UA with start pulse width of C320FF */
+#define WCD9335_SIDO_SIDO_CCL_DEF_VALUE 0x6e
+#define WCD9335_SIDO_SIDO_TEST_2 WCD9335_REG(0x06, 0x055)
+#define WCD9335_MBHC_CTL_1 WCD9335_REG(0x06, 0x056)
+#define WCD9335_MBHC_BTN_DBNC_MASK GENMASK(1, 0)
+#define WCD9335_MBHC_BTN_DBNC_T_16_MS 0x2
+#define WCD9335_MBHC_CTL_RCO_EN_MASK BIT(7)
+#define WCD9335_MBHC_CTL_RCO_EN BIT(7)
+
+#define WCD9335_MBHC_CTL_2 WCD9335_REG(0x06, 0x057)
+#define WCD9335_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0)
+#define WCD9335_MBHC_HS_VREF_1P5_V 0x1
+#define WCD9335_MBHC_PLUG_DETECT_CTL WCD9335_REG(0x06, 0x058)
+#define WCD9335_MBHC_HSDET_PULLUP_CTL_MASK GENMASK(7, 6)
+#define WCD9335_MBHC_HSDET_PULLUP_CTL_SHIFT 6
+#define WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA 0x80
+#define WCD9335_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6
+
+#define WCD9335_MBHC_ZDET_RAMP_CTL WCD9335_REG(0x06, 0x05a)
+#define WCD9335_VBADC_IBIAS_FE WCD9335_REG(0x06, 0x05e)
+#define WCD9335_FLYBACK_CTRL_1 WCD9335_REG(0x06, 0x0b1)
+#define WCD9335_RX_BIAS_HPH_PA WCD9335_REG(0x06, 0x0bb)
+#define WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK GENMASK(3, 0)
+#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2 WCD9335_REG(0x06, 0x0bc)
+#define WCD9335_RX_BIAS_HPH_RDAC_LDO WCD9335_REG(0x06, 0x0bd)
+#define WCD9335_RX_BIAS_FLYB_BUFF WCD9335_REG(0x06, 0x0c7)
+#define WCD9335_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0)
+#define WCD9335_RX_BIAS_FLYB_I_0P0_UA 0
+#define WCD9335_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4)
+#define WCD9335_RX_BIAS_FLYB_MID_RST WCD9335_REG(0x06, 0x0c8)
+#define WCD9335_HPH_CNP_WG_CTL WCD9335_REG(0x06, 0x0cc)
+#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK GENMASK(2, 0)
+#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500 0x2
+#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000 0x3
+#define WCD9335_HPH_OCP_CTL WCD9335_REG(0x06, 0x0ce)
+#define WCD9335_HPH_AUTO_CHOP WCD9335_REG(0x06, 0x0cf)
+#define WCD9335_HPH_AUTO_CHOP_MASK BIT(5)
+#define WCD9335_HPH_AUTO_CHOP_FORCE_ENABLE BIT(5)
+#define WCD9335_HPH_AUTO_CHOP_ENABLE_BY_CMPDR_GAIN 0
+#define WCD9335_HPH_PA_CTL1 WCD9335_REG(0x06, 0x0d1)
+#define WCD9335_HPH_PA_GM3_IB_SCALE_MASK GENMASK(3, 1)
+#define WCD9335_HPH_PA_CTL2 WCD9335_REG(0x06, 0x0d2)
+#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK BIT(2)
+#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_ENABLE BIT(2)
+#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_DISABLE 0
+#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK BIT(3)
+#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE BIT(3)
+#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE 0
+#define WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK BIT(5)
+#define WCD9335_HPH_PA_CTL2_HPH_PSRR_ENABLE BIT(5)
+#define WCD9335_HPH_PA_CTL2_HPH_PSRR_DISABLE 0
+#define WCD9335_HPH_L_EN WCD9335_REG(0x06, 0x0d3)
+#define WCD9335_HPH_CONST_SEL_L_MASK GENMASK(7, 6)
+#define WCD9335_HPH_CONST_SEL_L_BYPASS 0
+#define WCD9335_HPH_CONST_SEL_L_LP_PATH 0x40
+#define WCD9335_HPH_CONST_SEL_L_HQ_PATH 0x80
+#define WCD9335_HPH_PA_GAIN_MASK GENMASK(4, 0)
+#define WCD9335_HPH_GAIN_SRC_SEL_MASK BIT(5)
+#define WCD9335_HPH_GAIN_SRC_SEL_COMPANDER 0
+#define WCD9335_HPH_GAIN_SRC_SEL_REGISTER BIT(5)
+#define WCD9335_HPH_L_TEST WCD9335_REG(0x06, 0x0d4)
+#define WCD9335_HPH_R_EN WCD9335_REG(0x06, 0x0d6)
+#define WCD9335_HPH_R_TEST WCD9335_REG(0x06, 0x0d7)
+#define WCD9335_HPH_R_ATEST WCD9335_REG(0x06, 0x0d8)
+#define WCD9335_HPH_RDAC_LDO_CTL WCD9335_REG(0x06, 0x0db)
+#define WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_MASK GENMASK(2, 0)
+#define WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_V_N1P60 0x1
+#define WCD9335_HPH_RDAC_1P65_LD_OUTCTL_MASK GENMASK(6, 4)
+#define WCD9335_HPH_RDAC_1P65_LD_OUTCTL_V_N1P60 0x10
+#define WCD9335_HPH_REFBUFF_LP_CTL WCD9335_REG(0x06, 0x0de)
+#define WCD9335_HPH_L_DAC_CTL WCD9335_REG(0x06, 0x0df)
+#define WCD9335_HPH_DAC_LDO_POWERMODE_MASK BIT(0)
+#define WCD9335_HPH_DAC_LDO_POWERMODE_LOWPOWER 0
+#define WCD9335_HPH_DAC_LDO_POWERMODE_UHQA BIT(0)
+#define WCD9335_HPH_DAC_LDO_UHQA_OV_MASK BIT(1)
+#define WCD9335_HPH_DAC_LDO_UHQA_OV_ENABLE BIT(1)
+#define WCD9335_HPH_DAC_LDO_UHQA_OV_DISABLE 0
+
+#define WCD9335_EAR_CMBUFF WCD9335_REG(0x06, 0x0e2)
+#define WCD9335_DIFF_LO_LO2_COMPANDER WCD9335_REG(0x06, 0x0ea)
+#define WCD9335_DIFF_LO_LO1_COMPANDER WCD9335_REG(0x06, 0x0eb)
+#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ WCD9335_REG(0x06, 0x0f1)
+#define WCD9335_DIFF_LO_COM_PA_FREQ WCD9335_REG(0x06, 0x0f2)
+#define WCD9335_SE_LO_LO3_GAIN WCD9335_REG(0x06, 0x0f8)
+#define WCD9335_SE_LO_LO3_CTRL WCD9335_REG(0x06, 0x0f9)
+#define WCD9335_SE_LO_LO4_GAIN WCD9335_REG(0x06, 0x0fa)
+
+/* Page-10 Registers */
+#define WCD9335_CDC_TX0_TX_PATH_CTL WCD9335_REG(0x0a, 0x031)
+#define WCD9335_CDC_TX_PATH_CTL_PCM_RATE_MASK GENMASK(3, 0)
+#define WCD9335_CDC_TX_PATH_CTL(dec) WCD9335_REG(0xa, (0x31 + dec * 0x10))
+#define WCD9335_CDC_TX0_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x032)
+#define WCD9335_CDC_TX_ADC_AMIC_DMIC_SEL_MASK BIT(7)
+#define WCD9335_CDC_TX_ADC_DMIC_SEL BIT(7)
+#define WCD9335_CDC_TX_ADC_AMIC_SEL 0
+#define WCD9335_CDC_TX0_TX_VOL_CTL WCD9335_REG(0x0a, 0x034)
+#define WCD9335_CDC_TX0_TX_PATH_SEC2 WCD9335_REG(0x0a, 0x039)
+#define WCD9335_CDC_TX0_TX_PATH_SEC7 WCD9335_REG(0x0a, 0x03e)
+#define WCD9335_CDC_TX1_TX_PATH_CTL WCD9335_REG(0x0a, 0x041)
+#define WCD9335_CDC_TX1_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x042)
+#define WCD9335_CDC_TX2_TX_PATH_CTL WCD9335_REG(0x0a, 0x051)
+#define WCD9335_CDC_TX2_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x052)
+#define WCD9335_CDC_TX2_TX_VOL_CTL WCD9335_REG(0x0a, 0x054)
+#define WCD9335_CDC_TX3_TX_PATH_CTL WCD9335_REG(0x0a, 0x061)
+#define WCD9335_CDC_TX3_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x062)
+#define WCD9335_CDC_TX3_TX_VOL_CTL WCD9335_REG(0x0a, 0x064)
+#define WCD9335_CDC_TX4_TX_PATH_CTL WCD9335_REG(0x0a, 0x071)
+#define WCD9335_CDC_TX4_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x072)
+#define WCD9335_CDC_TX4_TX_VOL_CTL WCD9335_REG(0x0a, 0x074)
+#define WCD9335_CDC_TX5_TX_PATH_CTL WCD9335_REG(0x0a, 0x081)
+#define WCD9335_CDC_TX5_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x082)
+#define WCD9335_CDC_TX5_TX_VOL_CTL WCD9335_REG(0x0a, 0x084)
+#define WCD9335_CDC_TX6_TX_PATH_CTL WCD9335_REG(0x0a, 0x091)
+#define WCD9335_CDC_TX6_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x092)
+#define WCD9335_CDC_TX6_TX_VOL_CTL WCD9335_REG(0x0a, 0x094)
+#define WCD9335_CDC_TX7_TX_PATH_CTL WCD9335_REG(0x0a, 0x0a1)
+#define WCD9335_CDC_TX7_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x0a2)
+#define WCD9335_CDC_TX7_TX_VOL_CTL WCD9335_REG(0x0a, 0x0a4)
+#define WCD9335_CDC_TX8_TX_PATH_CTL WCD9335_REG(0x0a, 0x0b1)
+#define WCD9335_CDC_TX8_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x0b2)
+#define WCD9335_CDC_TX8_TX_VOL_CTL WCD9335_REG(0x0a, 0x0b4)
+#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0c3)
+#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0c7)
+#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0cb)
+#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0cf)
+
+/* Page-11 Registers */
+#define WCD9335_PAGE11_PAGE_REGISTER WCD9335_REG(0x0b, 0x000)
+#define WCD9335_CDC_COMPANDER1_CTL0 WCD9335_REG(0x0b, 0x001)
+#define WCD9335_CDC_COMPANDER1_CTL(c) WCD9335_REG(0x0b, (0x001 + c * 0x8))
+#define WCD9335_CDC_COMPANDER_CLK_EN_MASK BIT(0)
+#define WCD9335_CDC_COMPANDER_CLK_ENABLE BIT(0)
+#define WCD9335_CDC_COMPANDER_CLK_DISABLE 0
+#define WCD9335_CDC_COMPANDER_SOFT_RST_MASK BIT(1)
+#define WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE BIT(1)
+#define WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE 0
+#define WCD9335_CDC_COMPANDER_HALT_MASK BIT(2)
+#define WCD9335_CDC_COMPANDER_HALT BIT(2)
+#define WCD9335_CDC_COMPANDER_NOHALT 0
+#define WCD9335_CDC_COMPANDER7_CTL3 WCD9335_REG(0x0b, 0x034)
+#define WCD9335_CDC_COMPANDER7_CTL7 WCD9335_REG(0x0b, 0x038)
+#define WCD9335_CDC_COMPANDER8_CTL3 WCD9335_REG(0x0b, 0x03c)
+#define WCD9335_CDC_COMPANDER8_CTL7 WCD9335_REG(0x0b, 0x040)
+#define WCD9335_CDC_RX0_RX_PATH_CTL WCD9335_REG(0x0b, 0x041)
+#define WCD9335_CDC_RX_PGA_MUTE_EN_MASK BIT(4)
+#define WCD9335_CDC_RX_PGA_MUTE_ENABLE BIT(4)
+#define WCD9335_CDC_RX_PGA_MUTE_DISABLE 0
+#define WCD9335_CDC_RX_CLK_EN_MASK BIT(5)
+#define WCD9335_CDC_RX_CLK_ENABLE BIT(5)
+#define WCD9335_CDC_RX_CLK_DISABLE 0
+#define WCD9335_CDC_RX_RESET_MASK BIT(6)
+#define WCD9335_CDC_RX_RESET_ENABLE BIT(6)
+#define WCD9335_CDC_RX_RESET_DISABLE 0
+#define WCD9335_CDC_RX_PATH_CTL(rx) WCD9335_REG(0x0b, (0x041 + rx * 0x14))
+#define WCD9335_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x042)
+#define WCD9335_CDC_RX0_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x043)
+#define WCD9335_CDC_RX0_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x044)
+#define WCD9335_CDC_RX0_RX_VOL_CTL WCD9335_REG(0x0b, 0x045)
+#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x046)
+#define WCD9335_CDC_MIX_PCM_RATE_MASK GENMASK(3, 0)
+#define WCD9335_CDC_RX_PATH_MIX_CTL(rx) WCD9335_REG(0x0b, (0x46 + rx * 0x14))
+#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x047)
+#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x048)
+#define WCD9335_CDC_RX0_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x049)
+#define WCD9335_CDC_RX0_RX_PATH_SEC7 WCD9335_REG(0x0b, 0x050)
+#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0 WCD9335_REG(0x0b, 0x051)
+#define WCD9335_CDC_RX1_RX_PATH_CTL WCD9335_REG(0x0b, 0x055)
+#define WCD9335_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x056)
+#define WCD9335_CDC_RX1_RX_PATH_CFG(c) WCD9335_REG(0x0b, (0x056 + c * 0x14))
+#define WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK BIT(1)
+#define WCD9335_CDC_RX_PATH_CFG_CMP_ENABLE BIT(1)
+#define WCD9335_CDC_RX_PATH_CFG_CMP_DISABLE 0
+#define WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK BIT(2)
+#define WCD9335_CDC_RX_PATH_CFG_HD2_ENABLE BIT(2)
+#define WCD9335_CDC_RX_PATH_CFG_HD2_DISABLE 0
+#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN_MASK BIT(3)
+#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN BIT(3)
+#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_DISABLE 0
+#define WCD9335_CDC_RX1_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x058)
+#define WCD9335_CDC_RX1_RX_VOL_CTL WCD9335_REG(0x0b, 0x059)
+#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x05a)
+#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x05b)
+#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x05c)
+#define WCD9335_CDC_RX1_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x05d)
+#define WCD9335_CDC_RX1_RX_PATH_SEC3 WCD9335_REG(0x0b, 0x060)
+#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK GENMASK(1, 0)
+#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_2 0x1
+#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_1 0
+#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK GENMASK(5, 2)
+#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P2500 0x10
+#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000 0
+#define WCD9335_CDC_RX2_RX_PATH_CTL WCD9335_REG(0x0b, 0x069)
+#define WCD9335_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x06a)
+#define WCD9335_CDC_RX2_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x06c)
+#define WCD9335_CDC_RX2_RX_VOL_CTL WCD9335_REG(0x0b, 0x06d)
+#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x06e)
+#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x06f)
+#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x070)
+#define WCD9335_CDC_RX2_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x071)
+#define WCD9335_CDC_RX_PATH_DEM_INP_SEL_MASK GENMASK(1, 0)
+#define WCD9335_CDC_RX2_RX_PATH_SEC3 WCD9335_REG(0x0b, 0x074)
+#define WCD9335_CDC_RX3_RX_PATH_CTL WCD9335_REG(0x0b, 0x07d)
+#define WCD9335_CDC_RX3_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x07e)
+#define WCD9335_CDC_RX3_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x080)
+#define WCD9335_CDC_RX3_RX_VOL_CTL WCD9335_REG(0x0b, 0x081)
+#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x082)
+#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x083)
+#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x084)
+#define WCD9335_CDC_RX4_RX_PATH_CTL WCD9335_REG(0x0b, 0x091)
+#define WCD9335_CDC_RX4_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x092)
+#define WCD9335_CDC_RX4_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x094)
+#define WCD9335_CDC_RX4_RX_VOL_CTL WCD9335_REG(0x0b, 0x095)
+#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x096)
+#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x097)
+#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x098)
+#define WCD9335_CDC_RX5_RX_PATH_CTL WCD9335_REG(0x0b, 0x0a5)
+#define WCD9335_CDC_RX5_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0a6)
+#define WCD9335_CDC_RX5_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0a8)
+#define WCD9335_CDC_RX5_RX_VOL_CTL WCD9335_REG(0x0b, 0x0a9)
+#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0aa)
+#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0ab)
+#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0ac)
+#define WCD9335_CDC_RX6_RX_PATH_CTL WCD9335_REG(0x0b, 0x0b9)
+#define WCD9335_CDC_RX6_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ba)
+#define WCD9335_CDC_RX6_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0bc)
+#define WCD9335_CDC_RX6_RX_VOL_CTL WCD9335_REG(0x0b, 0x0bd)
+#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0be)
+#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0bf)
+#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0c0)
+#define WCD9335_CDC_RX7_RX_PATH_CTL WCD9335_REG(0x0b, 0x0cd)
+#define WCD9335_CDC_RX7_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ce)
+#define WCD9335_CDC_RX7_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0cf)
+#define WCD9335_CDC_RX7_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0d0)
+#define WCD9335_CDC_RX7_RX_VOL_CTL WCD9335_REG(0x0b, 0x0d1)
+#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0d2)
+#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0d3)
+#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0d4)
+#define WCD9335_CDC_RX8_RX_PATH_CTL WCD9335_REG(0x0b, 0x0e1)
+#define WCD9335_CDC_RX8_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0e2)
+#define WCD9335_CDC_RX8_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0e3)
+#define WCD9335_CDC_RX8_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0e4)
+#define WCD9335_CDC_RX8_RX_VOL_CTL WCD9335_REG(0x0b, 0x0e5)
+#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0e6)
+#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0e7)
+#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0e8)
+
+/* Page-12 Registers */
+#define WCD9335_PAGE12_PAGE_REGISTER WCD9335_REG(0x0c, 0x000)
+#define WCD9335_CDC_CLSH_K2_MSB WCD9335_REG(0x0c, 0x00a)
+#define WCD9335_CDC_CLSH_K2_LSB WCD9335_REG(0x0c, 0x00b)
+#define WCD9335_CDC_BOOST0_BOOST_CTL WCD9335_REG(0x0c, 0x01a)
+#define WCD9335_CDC_BOOST0_BOOST_CFG1 WCD9335_REG(0x0c, 0x01b)
+#define WCD9335_CDC_BOOST0_BOOST_CFG2 WCD9335_REG(0x0c, 0x01c)
+#define WCD9335_CDC_BOOST1_BOOST_CTL WCD9335_REG(0x0c, 0x022)
+#define WCD9335_CDC_BOOST1_BOOST_CFG1 WCD9335_REG(0x0c, 0x023)
+#define WCD9335_CDC_BOOST1_BOOST_CFG2 WCD9335_REG(0x0c, 0x024)
+
+/* Page-13 Registers */
+#define WCD9335_PAGE13_PAGE_REGISTER WCD9335_REG(0x0d, 0x000)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0 WCD9335_REG(0x0d, 0x001)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT_CFG0(i) WCD9335_REG(0xd, (0x1 + i * 0x2))
+#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1 WCD9335_REG(0xd, 0x002)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK GENMASK(3, 0)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(i) WCD9335_REG(0xd, (0x2 + i * 0x2))
+
+#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 WCD9335_REG(0x0d, 0x003)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1 WCD9335_REG(0x0d, 0x004)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0 WCD9335_REG(0x0d, 0x005)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1 WCD9335_REG(0x0d, 0x006)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0 WCD9335_REG(0x0d, 0x007)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1 WCD9335_REG(0x0d, 0x008)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0 WCD9335_REG(0x0d, 0x009)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1 WCD9335_REG(0x0d, 0x00a)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0 WCD9335_REG(0x0d, 0x00b)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1 WCD9335_REG(0x0d, 0x00c)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0 WCD9335_REG(0x0d, 0x00d)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1 WCD9335_REG(0x0d, 0x00e)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0 WCD9335_REG(0x0d, 0x00f)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1 WCD9335_REG(0x0d, 0x010)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0 WCD9335_REG(0x0d, 0x011)
+#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1 WCD9335_REG(0x0d, 0x012)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 WCD9335_REG(0x0d, 0x01d)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 WCD9335_REG(0x0d, 0x01e)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0 WCD9335_REG(0x0d, 0x01f)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1 WCD9335_REG(0x0d, 0x020)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0 WCD9335_REG(0x0d, 0x021)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1 WCD9335_REG(0x0d, 0x022)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0 WCD9335_REG(0x0d, 0x023)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1 WCD9335_REG(0x0d, 0x024)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 WCD9335_REG(0x0d, 0x025)
+#define WCD9335_CDC_TX_INP_MUX_SEL_AMIC 0x1
+#define WCD9335_CDC_TX_INP_MUX_SEL_DMIC 0
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0 WCD9335_REG(0x0d, 0x026)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0 WCD9335_REG(0x0d, 0x027)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0 WCD9335_REG(0x0d, 0x028)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0 WCD9335_REG(0x0d, 0x029)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0 WCD9335_REG(0x0d, 0x02b)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0 WCD9335_REG(0x0d, 0x02c)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0 WCD9335_REG(0x0d, 0x02d)
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0 WCD9335_REG(0x0d, 0x02e)
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0 WCD9335_REG(0x0d, 0x03a)
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1 WCD9335_REG(0x0d, 0x03b)
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2 WCD9335_REG(0x0d, 0x03c)
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3 WCD9335_REG(0x0d, 0x03d)
+#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0x0d, 0x041)
+#define WCD9335_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0)
+#define WCD9335_CDC_CLK_RST_CTRL_MCLK_ENABLE BIT(0)
+#define WCD9335_CDC_CLK_RST_CTRL_MCLK_DISABLE 0
+#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL WCD9335_REG(0x0d, 0x042)
+#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_EN_MASK BIT(0)
+#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE BIT(0)
+#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_DISABLE 0
+#define WCD9335_CDC_TOP_TOP_CFG1 WCD9335_REG(0x0d, 0x082)
+#define WCD9335_MAX_REGISTER 0xffff
+#define WCD9335_SEL_REGISTER 0x800
+
+/* SLIMBUS Slave Registers */
+#define WCD9335_SLIM_PGD_PORT_INT_EN0 WCD9335_REG(0, 0x30)
+#define WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_0 WCD9335_REG(0, 0x34)
+#define WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_1 WCD9335_REG(0, 0x35)
+#define WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_0 WCD9335_REG(0, 0x36)
+#define WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_1 WCD9335_REG(0, 0x37)
+#define WCD9335_SLIM_PGD_PORT_INT_CLR_RX_0 WCD9335_REG(0, 0x38)
+#define WCD9335_SLIM_PGD_PORT_INT_CLR_RX_1 WCD9335_REG(0, 0x39)
+#define WCD9335_SLIM_PGD_PORT_INT_CLR_TX_0 WCD9335_REG(0, 0x3A)
+#define WCD9335_SLIM_PGD_PORT_INT_CLR_TX_1 WCD9335_REG(0, 0x3B)
+#define WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 WCD9335_REG(0, 0x60)
+#define WCD9335_SLIM_PGD_PORT_INT_TX_SOURCE0 WCD9335_REG(0, 0x70)
+#define WCD9335_SLIM_PGD_RX_PORT_CFG(p) WCD9335_REG(0, (0x30 + p))
+#define WCD9335_SLIM_PGD_PORT_CFG(p) WCD9335_REG(0, (0x40 + p))
+#define WCD9335_SLIM_PGD_TX_PORT_CFG(p) WCD9335_REG(0, (0x50 + p))
+#define WCD9335_SLIM_PGD_PORT_INT_SRC(p) WCD9335_REG(0, (0x60 + p))
+#define WCD9335_SLIM_PGD_PORT_INT_STATUS(p) WCD9335_REG(0, (0x80 + p))
+#define WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_0(p) WCD9335_REG(0, (0x100 + 4 * p))
+/* ports range from 10-16 */
+#define WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_1(p) WCD9335_REG(0, (0x101 + 4 * p))
+#define WCD9335_SLIM_PGD_RX_PORT_MULTI_CHNL_0(p) WCD9335_REG(0, (0x140 + 4 * p))
+
+#define WCD9335_IRQ_SLIMBUS 0
+#define WCD9335_IRQ_MBHC_SW_DET 8
+#define WCD9335_IRQ_MBHC_ELECT_INS_REM_DET 9
+#define WCD9335_IRQ_MBHC_BUTTON_PRESS_DET 10
+#define WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET 11
+#define WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET 12
+
+#define SLIM_MANF_ID_QCOM 0x217
+#define SLIM_PROD_CODE_WCD9335 0x1a0
+
+#define WCD9335_VERSION_2_0 2
+#define WCD9335_MAX_SUPPLY 5
+
+#endif /* __WCD9335_H__ */
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
new file mode 100644
index 000000000000..c8db33f78a1b
--- /dev/null
+++ b/sound/soc/codecs/wcd934x.c
@@ -0,0 +1,5919 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019, Linaro Limited
+
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <linux/mfd/wcd934x/wcd934x.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/slimbus.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
+#include "wcd-mbhc-v2.h"
+
+#include <dt-bindings/sound/qcom,wcd934x.h>
+
+#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+/* Fractional Rates */
+#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400)
+#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* slave port water mark level
+ * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
+ */
+#define SLAVE_PORT_WATER_MARK_6BYTES 0
+#define SLAVE_PORT_WATER_MARK_9BYTES 1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
+#define SLAVE_PORT_WATER_MARK_SHIFT 1
+#define SLAVE_PORT_ENABLE 1
+#define SLAVE_PORT_DISABLE 0
+#define WCD934X_SLIM_WATER_MARK_VAL \
+ ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+ (SLAVE_PORT_ENABLE))
+
+#define WCD934X_SLIM_NUM_PORT_REG 3
+#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (WCD934X_SLIM_PGD_PORT_INT_EN0 + 2)
+#define WCD934X_SLIM_IRQ_OVERFLOW BIT(0)
+#define WCD934X_SLIM_IRQ_UNDERFLOW BIT(1)
+#define WCD934X_SLIM_IRQ_PORT_CLOSED BIT(2)
+
+#define WCD934X_MCLK_CLK_12P288MHZ 12288000
+#define WCD934X_MCLK_CLK_9P6MHZ 9600000
+
+/* Only valid for 9.6 MHz mclk */
+#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000
+#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000
+
+/* Only valid for 12.288 MHz mclk */
+#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000
+
+#define WCD934X_DMIC_CLK_DIV_2 0x0
+#define WCD934X_DMIC_CLK_DIV_3 0x1
+#define WCD934X_DMIC_CLK_DIV_4 0x2
+#define WCD934X_DMIC_CLK_DIV_6 0x3
+#define WCD934X_DMIC_CLK_DIV_8 0x4
+#define WCD934X_DMIC_CLK_DIV_16 0x5
+#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02
+
+#define TX_HPF_CUT_OFF_FREQ_MASK 0x60
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+
+#define WCD934X_RX_START 16
+#define WCD934X_NUM_INTERPOLATORS 9
+#define WCD934X_RX_PATH_CTL_OFFSET 20
+#define WCD934X_MAX_VALID_ADC_MUX 13
+#define WCD934X_INVALID_ADC_MUX 9
+
+#define WCD934X_SLIM_RX_CH(p) \
+ {.port = p + WCD934X_RX_START, .shift = p,}
+
+#define WCD934X_SLIM_TX_CH(p) \
+ {.port = p, .shift = p,}
+
+/* Feature masks to distinguish codec version */
+#define DSD_DISABLED_MASK 0
+#define SLNQ_DISABLED_MASK 1
+
+#define DSD_DISABLED BIT(DSD_DISABLED_MASK)
+#define SLNQ_DISABLED BIT(SLNQ_DISABLED_MASK)
+
+/* As fine version info cannot be retrieved before wcd probe.
+ * Define three coarse versions for possible future use before wcd probe.
+ */
+#define WCD_VERSION_WCD9340_1_0 0x400
+#define WCD_VERSION_WCD9341_1_0 0x410
+#define WCD_VERSION_WCD9340_1_1 0x401
+#define WCD_VERSION_WCD9341_1_1 0x411
+#define WCD934X_AMIC_PWR_LEVEL_LP 0
+#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1
+#define WCD934X_AMIC_PWR_LEVEL_HP 2
+#define WCD934X_AMIC_PWR_LEVEL_HYBRID 3
+#define WCD934X_AMIC_PWR_LVL_MASK 0x60
+#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5
+
+#define WCD934X_DEC_PWR_LVL_MASK 0x06
+#define WCD934X_DEC_PWR_LVL_LP 0x02
+#define WCD934X_DEC_PWR_LVL_HP 0x04
+#define WCD934X_DEC_PWR_LVL_DF 0x00
+#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF
+
+#define WCD_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX)
+
+#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = wcd934x_iir_filter_info, \
+ .get = wcd934x_get_iir_band_audio_mixer, \
+ .put = wcd934x_put_iir_band_audio_mixer, \
+ .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \
+ .iir_idx = iidx, \
+ .band_idx = bidx, \
+ .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \
+ } \
+}
+
+/* Z value defined in milliohm */
+#define WCD934X_ZDET_VAL_32 32000
+#define WCD934X_ZDET_VAL_400 400000
+#define WCD934X_ZDET_VAL_1200 1200000
+#define WCD934X_ZDET_VAL_100K 100000000
+/* Z floating defined in ohms */
+#define WCD934X_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define WCD934X_ZDET_NUM_MEASUREMENTS 900
+#define WCD934X_MBHC_GET_C1(c) ((c & 0xC000) >> 14)
+#define WCD934X_MBHC_GET_X1(x) (x & 0x3FFF)
+/* Z value compared in milliOhm */
+#define WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define WCD934X_MBHC_ZDET_CONST (86 * 16384)
+#define WCD934X_MBHC_MOISTURE_RREF R_24_KOHM
+#define WCD934X_MBHC_MAX_BUTTONS (8)
+#define WCD_MBHC_HS_V_MAX 1600
+
+#define WCD934X_INTERPOLATOR_PATH(id) \
+ {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "IIR0", "IIR0"}, \
+ {"RX INT" #id "_1 MIX1 INP0", "IIR1", "IIR1"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "IIR0", "IIR0"}, \
+ {"RX INT" #id "_1 MIX1 INP1", "IIR1", "IIR1"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "IIR0", "IIR0"}, \
+ {"RX INT" #id "_1 MIX1 INP2", "IIR1", "IIR1"}, \
+ {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \
+ {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \
+ {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \
+ {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \
+ {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \
+ {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \
+ {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \
+ {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \
+ {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \
+ {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \
+ {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \
+ {"RX INT" #id "_2 MUX", NULL, "INT" #id "_CLK"}, \
+ {"RX INT" #id "_2 MUX", NULL, "DSMDEM" #id "_CLK"}, \
+ {"RX INT" #id "_2 INTERP", NULL, "RX INT" #id "_2 MUX"}, \
+ {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 INTERP"}, \
+ {"RX INT" #id "_1 INTERP", NULL, "RX INT" #id "_1 MIX1"}, \
+ {"RX INT" #id "_1 INTERP", NULL, "INT" #id "_CLK"}, \
+ {"RX INT" #id "_1 INTERP", NULL, "DSMDEM" #id "_CLK"}, \
+ {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 INTERP"}
+
+#define WCD934X_INTERPOLATOR_MIX2(id) \
+ {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \
+ {"RX INT" #id " MIX2", NULL, "RX INT" #id " MIX2 INP"}
+
+#define WCD934X_SLIM_RX_AIF_PATH(id) \
+ {"SLIM RX"#id" MUX", "AIF1_PB", "AIF1 PB"}, \
+ {"SLIM RX"#id" MUX", "AIF2_PB", "AIF2 PB"}, \
+ {"SLIM RX"#id" MUX", "AIF3_PB", "AIF3 PB"}, \
+ {"SLIM RX"#id" MUX", "AIF4_PB", "AIF4 PB"}, \
+ {"SLIM RX"#id, NULL, "SLIM RX"#id" MUX"}
+
+#define WCD934X_ADC_MUX(id) \
+ {"ADC MUX" #id, "DMIC", "DMIC MUX" #id }, \
+ {"ADC MUX" #id, "AMIC", "AMIC MUX" #id }, \
+ {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \
+ {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \
+ {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \
+ {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \
+ {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \
+ {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \
+ {"AMIC MUX" #id, "ADC1", "ADC1"}, \
+ {"AMIC MUX" #id, "ADC2", "ADC2"}, \
+ {"AMIC MUX" #id, "ADC3", "ADC3"}, \
+ {"AMIC MUX" #id, "ADC4", "ADC4"}
+
+#define WCD934X_IIR_INP_MUX(id) \
+ {"IIR" #id, NULL, "IIR" #id " INP0 MUX"}, \
+ {"IIR" #id " INP0 MUX", "DEC0", "ADC MUX0"}, \
+ {"IIR" #id " INP0 MUX", "DEC1", "ADC MUX1"}, \
+ {"IIR" #id " INP0 MUX", "DEC2", "ADC MUX2"}, \
+ {"IIR" #id " INP0 MUX", "DEC3", "ADC MUX3"}, \
+ {"IIR" #id " INP0 MUX", "DEC4", "ADC MUX4"}, \
+ {"IIR" #id " INP0 MUX", "DEC5", "ADC MUX5"}, \
+ {"IIR" #id " INP0 MUX", "DEC6", "ADC MUX6"}, \
+ {"IIR" #id " INP0 MUX", "DEC7", "ADC MUX7"}, \
+ {"IIR" #id " INP0 MUX", "DEC8", "ADC MUX8"}, \
+ {"IIR" #id " INP0 MUX", "RX0", "SLIM RX0"}, \
+ {"IIR" #id " INP0 MUX", "RX1", "SLIM RX1"}, \
+ {"IIR" #id " INP0 MUX", "RX2", "SLIM RX2"}, \
+ {"IIR" #id " INP0 MUX", "RX3", "SLIM RX3"}, \
+ {"IIR" #id " INP0 MUX", "RX4", "SLIM RX4"}, \
+ {"IIR" #id " INP0 MUX", "RX5", "SLIM RX5"}, \
+ {"IIR" #id " INP0 MUX", "RX6", "SLIM RX6"}, \
+ {"IIR" #id " INP0 MUX", "RX7", "SLIM RX7"}, \
+ {"IIR" #id, NULL, "IIR" #id " INP1 MUX"}, \
+ {"IIR" #id " INP1 MUX", "DEC0", "ADC MUX0"}, \
+ {"IIR" #id " INP1 MUX", "DEC1", "ADC MUX1"}, \
+ {"IIR" #id " INP1 MUX", "DEC2", "ADC MUX2"}, \
+ {"IIR" #id " INP1 MUX", "DEC3", "ADC MUX3"}, \
+ {"IIR" #id " INP1 MUX", "DEC4", "ADC MUX4"}, \
+ {"IIR" #id " INP1 MUX", "DEC5", "ADC MUX5"}, \
+ {"IIR" #id " INP1 MUX", "DEC6", "ADC MUX6"}, \
+ {"IIR" #id " INP1 MUX", "DEC7", "ADC MUX7"}, \
+ {"IIR" #id " INP1 MUX", "DEC8", "ADC MUX8"}, \
+ {"IIR" #id " INP1 MUX", "RX0", "SLIM RX0"}, \
+ {"IIR" #id " INP1 MUX", "RX1", "SLIM RX1"}, \
+ {"IIR" #id " INP1 MUX", "RX2", "SLIM RX2"}, \
+ {"IIR" #id " INP1 MUX", "RX3", "SLIM RX3"}, \
+ {"IIR" #id " INP1 MUX", "RX4", "SLIM RX4"}, \
+ {"IIR" #id " INP1 MUX", "RX5", "SLIM RX5"}, \
+ {"IIR" #id " INP1 MUX", "RX6", "SLIM RX6"}, \
+ {"IIR" #id " INP1 MUX", "RX7", "SLIM RX7"}, \
+ {"IIR" #id, NULL, "IIR" #id " INP2 MUX"}, \
+ {"IIR" #id " INP2 MUX", "DEC0", "ADC MUX0"}, \
+ {"IIR" #id " INP2 MUX", "DEC1", "ADC MUX1"}, \
+ {"IIR" #id " INP2 MUX", "DEC2", "ADC MUX2"}, \
+ {"IIR" #id " INP2 MUX", "DEC3", "ADC MUX3"}, \
+ {"IIR" #id " INP2 MUX", "DEC4", "ADC MUX4"}, \
+ {"IIR" #id " INP2 MUX", "DEC5", "ADC MUX5"}, \
+ {"IIR" #id " INP2 MUX", "DEC6", "ADC MUX6"}, \
+ {"IIR" #id " INP2 MUX", "DEC7", "ADC MUX7"}, \
+ {"IIR" #id " INP2 MUX", "DEC8", "ADC MUX8"}, \
+ {"IIR" #id " INP2 MUX", "RX0", "SLIM RX0"}, \
+ {"IIR" #id " INP2 MUX", "RX1", "SLIM RX1"}, \
+ {"IIR" #id " INP2 MUX", "RX2", "SLIM RX2"}, \
+ {"IIR" #id " INP2 MUX", "RX3", "SLIM RX3"}, \
+ {"IIR" #id " INP2 MUX", "RX4", "SLIM RX4"}, \
+ {"IIR" #id " INP2 MUX", "RX5", "SLIM RX5"}, \
+ {"IIR" #id " INP2 MUX", "RX6", "SLIM RX6"}, \
+ {"IIR" #id " INP2 MUX", "RX7", "SLIM RX7"}, \
+ {"IIR" #id, NULL, "IIR" #id " INP3 MUX"}, \
+ {"IIR" #id " INP3 MUX", "DEC0", "ADC MUX0"}, \
+ {"IIR" #id " INP3 MUX", "DEC1", "ADC MUX1"}, \
+ {"IIR" #id " INP3 MUX", "DEC2", "ADC MUX2"}, \
+ {"IIR" #id " INP3 MUX", "DEC3", "ADC MUX3"}, \
+ {"IIR" #id " INP3 MUX", "DEC4", "ADC MUX4"}, \
+ {"IIR" #id " INP3 MUX", "DEC5", "ADC MUX5"}, \
+ {"IIR" #id " INP3 MUX", "DEC6", "ADC MUX6"}, \
+ {"IIR" #id " INP3 MUX", "DEC7", "ADC MUX7"}, \
+ {"IIR" #id " INP3 MUX", "DEC8", "ADC MUX8"}, \
+ {"IIR" #id " INP3 MUX", "RX0", "SLIM RX0"}, \
+ {"IIR" #id " INP3 MUX", "RX1", "SLIM RX1"}, \
+ {"IIR" #id " INP3 MUX", "RX2", "SLIM RX2"}, \
+ {"IIR" #id " INP3 MUX", "RX3", "SLIM RX3"}, \
+ {"IIR" #id " INP3 MUX", "RX4", "SLIM RX4"}, \
+ {"IIR" #id " INP3 MUX", "RX5", "SLIM RX5"}, \
+ {"IIR" #id " INP3 MUX", "RX6", "SLIM RX6"}, \
+ {"IIR" #id " INP3 MUX", "RX7", "SLIM RX7"}
+
+#define WCD934X_SLIM_TX_AIF_PATH(id) \
+ {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \
+ {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \
+ {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \
+ {"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"}
+
+#define WCD934X_MAX_MICBIAS MIC_BIAS_4
+#define NUM_CODEC_DAIS 9
+
+enum {
+ SIDO_SOURCE_INTERNAL,
+ SIDO_SOURCE_RCO_BG,
+};
+
+enum {
+ INTERP_EAR = 0,
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_LO1,
+ INTERP_LO2,
+ INTERP_LO3_NA, /* LO3 not avalible in Tavil */
+ INTERP_LO4_NA,
+ INTERP_SPKR1, /*INT7 WSA Speakers via soundwire */
+ INTERP_SPKR2, /*INT8 WSA Speakers via soundwire */
+ INTERP_MAX,
+};
+
+enum {
+ WCD934X_RX0 = 0,
+ WCD934X_RX1,
+ WCD934X_RX2,
+ WCD934X_RX3,
+ WCD934X_RX4,
+ WCD934X_RX5,
+ WCD934X_RX6,
+ WCD934X_RX7,
+ WCD934X_RX8,
+ WCD934X_RX9,
+ WCD934X_RX10,
+ WCD934X_RX11,
+ WCD934X_RX12,
+ WCD934X_RX_MAX,
+};
+
+enum {
+ WCD934X_TX0 = 0,
+ WCD934X_TX1,
+ WCD934X_TX2,
+ WCD934X_TX3,
+ WCD934X_TX4,
+ WCD934X_TX5,
+ WCD934X_TX6,
+ WCD934X_TX7,
+ WCD934X_TX8,
+ WCD934X_TX9,
+ WCD934X_TX10,
+ WCD934X_TX11,
+ WCD934X_TX12,
+ WCD934X_TX13,
+ WCD934X_TX14,
+ WCD934X_TX15,
+ WCD934X_TX_MAX,
+};
+
+struct wcd934x_slim_ch {
+ u32 ch_num;
+ u16 port;
+ u16 shift;
+ struct list_head list;
+};
+
+static const struct wcd934x_slim_ch wcd934x_tx_chs[WCD934X_TX_MAX] = {
+ WCD934X_SLIM_TX_CH(0),
+ WCD934X_SLIM_TX_CH(1),
+ WCD934X_SLIM_TX_CH(2),
+ WCD934X_SLIM_TX_CH(3),
+ WCD934X_SLIM_TX_CH(4),
+ WCD934X_SLIM_TX_CH(5),
+ WCD934X_SLIM_TX_CH(6),
+ WCD934X_SLIM_TX_CH(7),
+ WCD934X_SLIM_TX_CH(8),
+ WCD934X_SLIM_TX_CH(9),
+ WCD934X_SLIM_TX_CH(10),
+ WCD934X_SLIM_TX_CH(11),
+ WCD934X_SLIM_TX_CH(12),
+ WCD934X_SLIM_TX_CH(13),
+ WCD934X_SLIM_TX_CH(14),
+ WCD934X_SLIM_TX_CH(15),
+};
+
+static const struct wcd934x_slim_ch wcd934x_rx_chs[WCD934X_RX_MAX] = {
+ WCD934X_SLIM_RX_CH(0), /* 16 */
+ WCD934X_SLIM_RX_CH(1), /* 17 */
+ WCD934X_SLIM_RX_CH(2),
+ WCD934X_SLIM_RX_CH(3),
+ WCD934X_SLIM_RX_CH(4),
+ WCD934X_SLIM_RX_CH(5),
+ WCD934X_SLIM_RX_CH(6),
+ WCD934X_SLIM_RX_CH(7),
+ WCD934X_SLIM_RX_CH(8),
+ WCD934X_SLIM_RX_CH(9),
+ WCD934X_SLIM_RX_CH(10),
+ WCD934X_SLIM_RX_CH(11),
+ WCD934X_SLIM_RX_CH(12),
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR0 = 0,
+ IIR1,
+ IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+enum {
+ COMPANDER_1, /* HPH_L */
+ COMPANDER_2, /* HPH_R */
+ COMPANDER_3, /* LO1_DIFF */
+ COMPANDER_4, /* LO2_DIFF */
+ COMPANDER_5, /* LO3_SE - not used in Tavil */
+ COMPANDER_6, /* LO4_SE - not used in Tavil */
+ COMPANDER_7, /* SWR SPK CH1 */
+ COMPANDER_8, /* SWR SPK CH2 */
+ COMPANDER_MAX,
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+ INTn_1_INP_SEL_IIR0,
+ INTn_1_INP_SEL_IIR1,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_RX4,
+ INTn_1_INP_SEL_RX5,
+ INTn_1_INP_SEL_RX6,
+ INTn_1_INP_SEL_RX7,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+ INTn_2_INP_SEL_RX4,
+ INTn_2_INP_SEL_RX5,
+ INTn_2_INP_SEL_RX6,
+ INTn_2_INP_SEL_RX7,
+ INTn_2_INP_SEL_PROXIMITY,
+};
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static const struct interp_sample_rate sr_val_tbl[] = {
+ {8000, 0x0},
+ {16000, 0x1},
+ {32000, 0x3},
+ {48000, 0x4},
+ {96000, 0x5},
+ {192000, 0x6},
+ {384000, 0x7},
+ {44100, 0x9},
+ {88200, 0xA},
+ {176400, 0xB},
+ {352800, 0xC},
+};
+
+struct wcd934x_mbhc_zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
+struct wcd_slim_codec_dai_data {
+ struct list_head slim_ch_list;
+ struct slim_stream_config sconfig;
+ struct slim_stream_runtime *sruntime;
+};
+
+static const struct regmap_range_cfg wcd934x_ifc_ranges[] = {
+ {
+ .name = "WCD9335-IFC-DEV",
+ .range_min = 0x0,
+ .range_max = 0xffff,
+ .selector_reg = 0x800,
+ .selector_mask = 0xfff,
+ .selector_shift = 0,
+ .window_start = 0x800,
+ .window_len = 0x400,
+ },
+};
+
+static const struct regmap_config wcd934x_ifc_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0xffff,
+ .ranges = wcd934x_ifc_ranges,
+ .num_ranges = ARRAY_SIZE(wcd934x_ifc_ranges),
+};
+
+struct wcd934x_codec {
+ struct device *dev;
+ struct clk_hw hw;
+ struct clk *extclk;
+ struct regmap *regmap;
+ struct regmap *if_regmap;
+ struct slim_device *sdev;
+ struct slim_device *sidev;
+ struct wcd_clsh_ctrl *clsh_ctrl;
+ struct wcd_common common;
+ struct snd_soc_component *component;
+ struct wcd934x_slim_ch rx_chs[WCD934X_RX_MAX];
+ struct wcd934x_slim_ch tx_chs[WCD934X_TX_MAX];
+ struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS];
+ int rate;
+ u32 version;
+ u32 hph_mode;
+ u32 tx_port_value[WCD934X_TX_MAX];
+ u32 rx_port_value[WCD934X_RX_MAX];
+ int sido_input_src;
+ int dmic_0_1_clk_cnt;
+ int dmic_2_3_clk_cnt;
+ int dmic_4_5_clk_cnt;
+ int dmic_sample_rate;
+ int comp_enabled[COMPANDER_MAX];
+ int sysclk_users;
+ struct mutex sysclk_mutex;
+ /* mbhc module */
+ struct wcd_mbhc *mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ bool mbhc_started;
+ struct mutex micb_lock;
+ u32 micb_ref[WCD934X_MAX_MICBIAS];
+ u32 pullup_ref[WCD934X_MAX_MICBIAS];
+};
+
+#define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw)
+
+struct wcd_iir_filter_ctl {
+ unsigned int iir_idx;
+ unsigned int band_idx;
+ struct soc_bytes_ext bytes_ext;
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0);
+
+/* Cutoff frequency for high pass filter */
+static const char * const cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
+};
+
+static const char * const rx_cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ",
+ "CF_NEG_3DB_0P48HZ"
+};
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB",
+ "Class-H Hi-Fi Low Power"
+};
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB",
+};
+
+static const char * const rx_int0_7_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7", "PROXIMITY"
+};
+
+static const char * const rx_int_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7"
+};
+
+static const char * const rx_prim_mix_text[] = {
+ "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+ "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const iir_inp_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6",
+ "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+ "NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_1_interp_mux_text[] = {
+ "ZERO", "RX INT0_1 MIX1",
+};
+
+static const char * const rx_int1_1_interp_mux_text[] = {
+ "ZERO", "RX INT1_1 MIX1",
+};
+
+static const char * const rx_int2_1_interp_mux_text[] = {
+ "ZERO", "RX INT2_1 MIX1",
+};
+
+static const char * const rx_int3_1_interp_mux_text[] = {
+ "ZERO", "RX INT3_1 MIX1",
+};
+
+static const char * const rx_int4_1_interp_mux_text[] = {
+ "ZERO", "RX INT4_1 MIX1",
+};
+
+static const char * const rx_int7_1_interp_mux_text[] = {
+ "ZERO", "RX INT7_1 MIX1",
+};
+
+static const char * const rx_int8_1_interp_mux_text[] = {
+ "ZERO", "RX INT8_1 MIX1",
+};
+
+static const char * const rx_int0_2_interp_mux_text[] = {
+ "ZERO", "RX INT0_2 MUX",
+};
+
+static const char * const rx_int1_2_interp_mux_text[] = {
+ "ZERO", "RX INT1_2 MUX",
+};
+
+static const char * const rx_int2_2_interp_mux_text[] = {
+ "ZERO", "RX INT2_2 MUX",
+};
+
+static const char * const rx_int3_2_interp_mux_text[] = {
+ "ZERO", "RX INT3_2 MUX",
+};
+
+static const char * const rx_int4_2_interp_mux_text[] = {
+ "ZERO", "RX INT4_2 MUX",
+};
+
+static const char * const rx_int7_2_interp_mux_text[] = {
+ "ZERO", "RX INT7_2 MUX",
+};
+
+static const char * const rx_int8_2_interp_mux_text[] = {
+ "ZERO", "RX INT8_2 MUX",
+};
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5"
+};
+
+static const char * const amic_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "ADC4"
+};
+
+static const char * const amic4_5_sel_text[] = {
+ "AMIC4", "AMIC5"
+};
+
+static const char * const adc_mux_text[] = {
+ "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2"
+};
+
+static const char * const cdc_if_tx0_mux_text[] = {
+ "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192"
+};
+
+static const char * const cdc_if_tx1_mux_text[] = {
+ "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192"
+};
+
+static const char * const cdc_if_tx2_mux_text[] = {
+ "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192"
+};
+
+static const char * const cdc_if_tx3_mux_text[] = {
+ "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192"
+};
+
+static const char * const cdc_if_tx4_mux_text[] = {
+ "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192"
+};
+
+static const char * const cdc_if_tx5_mux_text[] = {
+ "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192"
+};
+
+static const char * const cdc_if_tx6_mux_text[] = {
+ "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192"
+};
+
+static const char * const cdc_if_tx7_mux_text[] = {
+ "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192"
+};
+
+static const char * const cdc_if_tx8_mux_text[] = {
+ "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
+};
+
+static const char * const cdc_if_tx9_mux_text[] = {
+ "ZERO", "DEC7", "DEC7_192"
+};
+
+static const char * const cdc_if_tx10_mux_text[] = {
+ "ZERO", "DEC6", "DEC6_192"
+};
+
+static const char * const cdc_if_tx11_mux_text[] = {
+ "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST"
+};
+
+static const char * const cdc_if_tx11_inp1_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4",
+ "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12"
+};
+
+static const char * const cdc_if_tx13_mux_text[] = {
+ "CDC_DEC_5", "MAD_BRDCST"
+};
+
+static const char * const cdc_if_tx13_inp1_mux_text[] = {
+ "ZERO", "DEC5", "DEC5_192"
+};
+
+static const struct soc_enum cf_dec0_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec5_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec6_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec7_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec8_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_int0_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int1_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int2_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int3_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int4_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int7_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int8_1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct soc_enum rx_int0_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10,
+ rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int1_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int2_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int3_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int4_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int7_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10,
+ rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int8_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int0_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_mix2_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int1_mix2_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int2_mix2_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int3_mix2_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int4_mix2_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int7_mix2_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum iir0_inp0_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp2_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp3_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp0_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp2_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp3_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3,
+ 0, 18, iir_inp_mux_text);
+
+static const struct soc_enum rx_int0_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int1_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int2_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum tx_adc_mux0_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux2_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux3_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux4_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux5_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux6_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux7_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+static const struct soc_enum tx_adc_mux8_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4,
+ ARRAY_SIZE(adc_mux_text), adc_mux_text);
+
+static const struct soc_enum rx_int0_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2,
+ rx_int0_1_interp_mux_text);
+
+static const struct soc_enum rx_int1_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2,
+ rx_int1_1_interp_mux_text);
+
+static const struct soc_enum rx_int2_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2,
+ rx_int2_1_interp_mux_text);
+
+static const struct soc_enum rx_int3_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_1_interp_mux_text);
+
+static const struct soc_enum rx_int4_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_1_interp_mux_text);
+
+static const struct soc_enum rx_int7_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_1_interp_mux_text);
+
+static const struct soc_enum rx_int8_1_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_1_interp_mux_text);
+
+static const struct soc_enum rx_int0_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int0_2_interp_mux_text);
+
+static const struct soc_enum rx_int1_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int1_2_interp_mux_text);
+
+static const struct soc_enum rx_int2_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int2_2_interp_mux_text);
+
+static const struct soc_enum rx_int3_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_2_interp_mux_text);
+
+static const struct soc_enum rx_int4_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_2_interp_mux_text);
+
+static const struct soc_enum rx_int7_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_2_interp_mux_text);
+
+static const struct soc_enum rx_int8_2_interp_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_2_interp_mux_text);
+
+static const struct soc_enum tx_dmic_mux0_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux2_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux3_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux4_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux5_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux6_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux7_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux8_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7,
+ dmic_mux_text);
+
+static const struct soc_enum tx_amic_mux0_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux1_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux2_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux3_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux4_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux5_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux6_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux7_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 5,
+ amic_mux_text);
+static const struct soc_enum tx_amic_mux8_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 5,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic4_5_enum =
+ SOC_ENUM_SINGLE(WCD934X_TX_NEW_AMIC_4_5_SEL, 7, 2, amic4_5_sel_text);
+
+static const struct soc_enum cdc_if_tx0_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0,
+ ARRAY_SIZE(cdc_if_tx0_mux_text), cdc_if_tx0_mux_text);
+static const struct soc_enum cdc_if_tx1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2,
+ ARRAY_SIZE(cdc_if_tx1_mux_text), cdc_if_tx1_mux_text);
+static const struct soc_enum cdc_if_tx2_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4,
+ ARRAY_SIZE(cdc_if_tx2_mux_text), cdc_if_tx2_mux_text);
+static const struct soc_enum cdc_if_tx3_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6,
+ ARRAY_SIZE(cdc_if_tx3_mux_text), cdc_if_tx3_mux_text);
+static const struct soc_enum cdc_if_tx4_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0,
+ ARRAY_SIZE(cdc_if_tx4_mux_text), cdc_if_tx4_mux_text);
+static const struct soc_enum cdc_if_tx5_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2,
+ ARRAY_SIZE(cdc_if_tx5_mux_text), cdc_if_tx5_mux_text);
+static const struct soc_enum cdc_if_tx6_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4,
+ ARRAY_SIZE(cdc_if_tx6_mux_text), cdc_if_tx6_mux_text);
+static const struct soc_enum cdc_if_tx7_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6,
+ ARRAY_SIZE(cdc_if_tx7_mux_text), cdc_if_tx7_mux_text);
+static const struct soc_enum cdc_if_tx8_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0,
+ ARRAY_SIZE(cdc_if_tx8_mux_text), cdc_if_tx8_mux_text);
+static const struct soc_enum cdc_if_tx9_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2,
+ ARRAY_SIZE(cdc_if_tx9_mux_text), cdc_if_tx9_mux_text);
+static const struct soc_enum cdc_if_tx10_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4,
+ ARRAY_SIZE(cdc_if_tx10_mux_text), cdc_if_tx10_mux_text);
+static const struct soc_enum cdc_if_tx11_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0,
+ ARRAY_SIZE(cdc_if_tx11_inp1_mux_text),
+ cdc_if_tx11_inp1_mux_text);
+static const struct soc_enum cdc_if_tx11_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0,
+ ARRAY_SIZE(cdc_if_tx11_mux_text), cdc_if_tx11_mux_text);
+static const struct soc_enum cdc_if_tx13_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4,
+ ARRAY_SIZE(cdc_if_tx13_inp1_mux_text),
+ cdc_if_tx13_inp1_mux_text);
+static const struct soc_enum cdc_if_tx13_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0,
+ ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text);
+
+static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD934X_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD934X_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD934X_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD934X_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD934X_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD934X_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD934X_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD934X_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD934X_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD934X_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD934X_MBHC_NEW_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD934X_MBHC_NEW_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD934X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD934X_HPH_OCP_CTL, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD934X_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD934X_ANA_MICB2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD934X_HPH_CNP_WG_TIME, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD934X_ANA_HPH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD934X_ANA_HPH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD934X_ANA_HPH, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD934X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD934X_MBHC_CTL_BCS, 0x02),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD934X_MBHC_STATUS_SPARE_1, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD934X_MBHC_NEW_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD934X_MBHC_NEW_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD934X_HPH_PA_CTL2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD934X_HPH_PA_CTL2, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD934X_HPH_L_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD934X_HPH_R_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD934X_INTR_PIN1_STATUS0, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD934X_INTR_PIN1_STATUS0, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD934X_MBHC_NEW_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD934X_MBHC_NEW_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD934X_MBHC_NEW_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD934X_MBHC_NEW_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD934X_ANA_MICB2, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD934X_MBHC_NEW_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD934X_MBHC_NEW_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD934X_ANA_MBHC_ZDET, 0x02),
+};
+
+static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src)
+{
+ if (sido_src == wcd->sido_input_src)
+ return 0;
+
+ if (sido_src == SIDO_SOURCE_RCO_BG) {
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO,
+ WCD934X_ANA_RCO_BG_EN_MASK,
+ WCD934X_ANA_RCO_BG_ENABLE);
+ usleep_range(100, 110);
+ }
+ wcd->sido_input_src = sido_src;
+
+ return 0;
+}
+
+static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd)
+{
+ mutex_lock(&wcd->sysclk_mutex);
+
+ if (++wcd->sysclk_users != 1) {
+ mutex_unlock(&wcd->sysclk_mutex);
+ return 0;
+ }
+ mutex_unlock(&wcd->sysclk_mutex);
+
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS,
+ WCD934X_ANA_BIAS_EN_MASK,
+ WCD934X_ANA_BIAS_EN);
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS,
+ WCD934X_ANA_PRECHRG_EN_MASK,
+ WCD934X_ANA_PRECHRG_EN);
+ /*
+ * 1ms delay is required after pre-charge is enabled
+ * as per HW requirement
+ */
+ usleep_range(1000, 1100);
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS,
+ WCD934X_ANA_PRECHRG_EN_MASK, 0);
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS,
+ WCD934X_ANA_PRECHRG_MODE_MASK, 0);
+
+ /*
+ * In data clock contrl register is changed
+ * to CLK_SYS_MCLK_PRG
+ */
+
+ regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG,
+ WCD934X_EXT_CLK_BUF_EN_MASK,
+ WCD934X_EXT_CLK_BUF_EN);
+ regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG,
+ WCD934X_EXT_CLK_DIV_RATIO_MASK,
+ WCD934X_EXT_CLK_DIV_BY_2);
+ regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG,
+ WCD934X_MCLK_SRC_MASK,
+ WCD934X_MCLK_SRC_EXT_CLK);
+ regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG,
+ WCD934X_MCLK_EN_MASK, WCD934X_MCLK_EN);
+ regmap_update_bits(wcd->regmap,
+ WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ WCD934X_CDC_FS_MCLK_CNT_EN_MASK,
+ WCD934X_CDC_FS_MCLK_CNT_ENABLE);
+ regmap_update_bits(wcd->regmap,
+ WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ WCD934X_MCLK_EN_MASK,
+ WCD934X_MCLK_EN);
+ regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_GATE,
+ WCD934X_CODEC_RPM_CLK_GATE_MASK, 0x0);
+ /*
+ * 10us sleep is required after clock is enabled
+ * as per HW requirement
+ */
+ usleep_range(10, 15);
+
+ wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG);
+
+ return 0;
+}
+
+static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd)
+{
+ mutex_lock(&wcd->sysclk_mutex);
+ if (--wcd->sysclk_users != 0) {
+ mutex_unlock(&wcd->sysclk_mutex);
+ return 0;
+ }
+ mutex_unlock(&wcd->sysclk_mutex);
+
+ regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG,
+ WCD934X_EXT_CLK_BUF_EN_MASK |
+ WCD934X_MCLK_EN_MASK, 0x0);
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS,
+ WCD934X_ANA_BIAS_EN_MASK, 0);
+ regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS,
+ WCD934X_ANA_PRECHRG_EN_MASK, 0);
+
+ return 0;
+}
+
+static int __wcd934x_cdc_mclk_enable(struct wcd934x_codec *wcd, bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = clk_prepare_enable(wcd->extclk);
+
+ if (ret) {
+ dev_err(wcd->dev, "%s: ext clk enable failed\n",
+ __func__);
+ return ret;
+ }
+ ret = wcd934x_enable_ana_bias_and_sysclk(wcd);
+ } else {
+ int val;
+
+ regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ &val);
+
+ /* Don't disable clock if soundwire using it.*/
+ if (val & WCD934X_CDC_SWR_CLK_EN_MASK)
+ return 0;
+
+ wcd934x_disable_ana_bias_and_syclk(wcd);
+ clk_disable_unprepare(wcd->extclk);
+ }
+
+ return ret;
+}
+
+static int wcd934x_codec_enable_mclk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return __wcd934x_cdc_mclk_enable(wcd, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return __wcd934x_cdc_mclk_enable(wcd, false);
+ }
+
+ return 0;
+}
+
+static int wcd934x_get_version(struct wcd934x_codec *wcd)
+{
+ int val1, val2, ver, ret;
+ struct regmap *regmap;
+ u16 id_minor;
+ u32 version_mask = 0;
+
+ regmap = wcd->regmap;
+ ver = 0;
+
+ ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
+ (u8 *)&id_minor, sizeof(u16));
+
+ if (ret)
+ return ret;
+
+ regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1);
+ regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2);
+
+ version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK;
+ version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK;
+
+ switch (version_mask) {
+ case DSD_DISABLED | SLNQ_DISABLED:
+ if (id_minor == 0)
+ ver = WCD_VERSION_WCD9340_1_0;
+ else if (id_minor == 0x01)
+ ver = WCD_VERSION_WCD9340_1_1;
+ break;
+ case SLNQ_DISABLED:
+ if (id_minor == 0)
+ ver = WCD_VERSION_WCD9341_1_0;
+ else if (id_minor == 0x01)
+ ver = WCD_VERSION_WCD9341_1_1;
+ break;
+ }
+
+ wcd->version = ver;
+ dev_info(wcd->dev, "WCD934X Minor:0x%x Version:0x%x\n", id_minor, ver);
+
+ return 0;
+}
+
+static void wcd934x_enable_efuse_sensing(struct wcd934x_codec *wcd)
+{
+ int rc, val;
+
+ __wcd934x_cdc_mclk_enable(wcd, true);
+
+ regmap_update_bits(wcd->regmap,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_CTL,
+ WCD934X_EFUSE_SENSE_STATE_MASK,
+ WCD934X_EFUSE_SENSE_STATE_DEF);
+ regmap_update_bits(wcd->regmap,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_CTL,
+ WCD934X_EFUSE_SENSE_EN_MASK,
+ WCD934X_EFUSE_SENSE_ENABLE);
+ /*
+ * 5ms sleep required after enabling efuse control
+ * before checking the status.
+ */
+ usleep_range(5000, 5500);
+ wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG);
+
+ rc = regmap_read(wcd->regmap,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val);
+ if (rc || (!(val & 0x01)))
+ WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n",
+ __func__, val, rc);
+
+ __wcd934x_cdc_mclk_enable(wcd, false);
+}
+
+static int wcd934x_swrm_clock(struct wcd934x_codec *wcd, bool enable)
+{
+ if (enable) {
+ __wcd934x_cdc_mclk_enable(wcd, true);
+ regmap_update_bits(wcd->regmap,
+ WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ WCD934X_CDC_SWR_CLK_EN_MASK,
+ WCD934X_CDC_SWR_CLK_ENABLE);
+ } else {
+ regmap_update_bits(wcd->regmap,
+ WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ WCD934X_CDC_SWR_CLK_EN_MASK, 0);
+ __wcd934x_cdc_mclk_enable(wcd, false);
+ }
+
+ return 0;
+}
+
+static int wcd934x_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rate_val, u32 rate)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ struct wcd934x_slim_ch *ch;
+ u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel;
+ int inp, j;
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) {
+ inp = ch->shift + INTn_1_INP_SEL_RX0;
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the slim rx port
+ * is connected
+ */
+ for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) {
+ /* Interpolators 5 and 6 are not aviliable in Tavil */
+ if (j == INTERP_LO3_NA || j == INTERP_LO4_NA)
+ continue;
+
+ cfg0 = snd_soc_component_read(comp,
+ WCD934X_CDC_RX_INP_MUX_RX_INT_CFG0(j));
+ cfg1 = snd_soc_component_read(comp,
+ WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j));
+
+ inp0_sel = cfg0 &
+ WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+ inp1_sel = (cfg0 >> 4) &
+ WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+ inp2_sel = (cfg1 >> 4) &
+ WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+
+ if ((inp0_sel == inp) || (inp1_sel == inp) ||
+ (inp2_sel == inp)) {
+ /* rate is in Hz */
+ /*
+ * Ear and speaker primary path does not support
+ * native sample rates
+ */
+ if ((j == INTERP_EAR || j == INTERP_SPKR1 ||
+ j == INTERP_SPKR2) && rate == 44100)
+ dev_err(wcd->dev,
+ "Cannot set 44.1KHz on INT%d\n",
+ j);
+ else
+ snd_soc_component_update_bits(comp,
+ WCD934X_CDC_RX_PATH_CTL(j),
+ WCD934X_CDC_MIX_PCM_RATE_MASK,
+ rate_val);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int wcd934x_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ int rate_val, u32 rate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ struct wcd934x_slim_ch *ch;
+ int val, j;
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) {
+ for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) {
+ /* Interpolators 5 and 6 are not aviliable in Tavil */
+ if (j == INTERP_LO3_NA || j == INTERP_LO4_NA)
+ continue;
+ val = snd_soc_component_read(component,
+ WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)) &
+ WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK;
+
+ if (val == (ch->shift + INTn_2_INP_SEL_RX0)) {
+ /*
+ * Ear mix path supports only 48, 96, 192,
+ * 384KHz only
+ */
+ if ((j == INTERP_EAR) &&
+ (rate_val < 0x4 ||
+ rate_val > 0x7)) {
+ dev_err(component->dev,
+ "Invalid rate for AIF_PB DAI(%d)\n",
+ dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component,
+ WCD934X_CDC_RX_PATH_MIX_CTL(j),
+ WCD934X_CDC_MIX_PCM_RATE_MASK,
+ rate_val);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int wcd934x_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) {
+ if (sample_rate == sr_val_tbl[i].sample_rate) {
+ rate_val = sr_val_tbl[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) {
+ dev_err(dai->dev, "Unsupported sample rate: %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ ret = wcd934x_set_prim_interpolator_rate(dai, (u8)rate_val,
+ sample_rate);
+ if (ret)
+ return ret;
+ ret = wcd934x_set_mix_interpolator_rate(dai, (u8)rate_val,
+ sample_rate);
+
+ return ret;
+}
+
+static int wcd934x_set_decimator_rate(struct snd_soc_dai *dai,
+ u8 rate_val, u32 rate)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp);
+ u8 shift = 0, shift_val = 0, tx_mux_sel;
+ struct wcd934x_slim_ch *ch;
+ int tx_port, tx_port_reg;
+ int decimator = -1;
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) {
+ tx_port = ch->port;
+ /* Find the SB TX MUX input - which decimator is connected */
+ switch (tx_port) {
+ case 0 ... 3:
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0;
+ shift = (tx_port << 1);
+ shift_val = 0x03;
+ break;
+ case 4 ... 7:
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1;
+ shift = ((tx_port - 4) << 1);
+ shift_val = 0x03;
+ break;
+ case 8 ... 10:
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2;
+ shift = ((tx_port - 8) << 1);
+ shift_val = 0x03;
+ break;
+ case 11:
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 0;
+ shift_val = 0x0F;
+ break;
+ case 13:
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 4;
+ shift_val = 0x03;
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n",
+ tx_port, dai->id);
+ return -EINVAL;
+ }
+
+ tx_mux_sel = snd_soc_component_read(comp, tx_port_reg) &
+ (shift_val << shift);
+
+ tx_mux_sel = tx_mux_sel >> shift;
+ switch (tx_port) {
+ case 0 ... 8:
+ if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3))
+ decimator = tx_port;
+ break;
+ case 9 ... 10:
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = ((tx_port == 9) ? 7 : 6);
+ break;
+ case 11:
+ if ((tx_mux_sel >= 1) && (tx_mux_sel < 7))
+ decimator = tx_mux_sel - 1;
+ break;
+ case 13:
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = 5;
+ break;
+ default:
+ dev_err(wcd->dev, "ERROR: Invalid tx_port: %d\n",
+ tx_port);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(comp,
+ WCD934X_CDC_TX_PATH_CTL(decimator),
+ WCD934X_CDC_TX_PATH_CTL_PCM_RATE_MASK,
+ rate_val);
+ }
+
+ return 0;
+}
+
+static int wcd934x_slim_set_hw_params(struct wcd934x_codec *wcd,
+ struct wcd_slim_codec_dai_data *dai_data,
+ int direction)
+{
+ struct list_head *slim_ch_list = &dai_data->slim_ch_list;
+ struct slim_stream_config *cfg = &dai_data->sconfig;
+ struct wcd934x_slim_ch *ch;
+ u16 payload = 0;
+ int ret, i;
+
+ cfg->ch_count = 0;
+ cfg->direction = direction;
+ cfg->port_mask = 0;
+
+ /* Configure slave interface device */
+ list_for_each_entry(ch, slim_ch_list, list) {
+ cfg->ch_count++;
+ payload |= 1 << ch->shift;
+ cfg->port_mask |= BIT(ch->port);
+ }
+
+ cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL);
+ if (!cfg->chs)
+ return -ENOMEM;
+
+ i = 0;
+ list_for_each_entry(ch, slim_ch_list, list) {
+ cfg->chs[i++] = ch->ch_num;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* write to interface device */
+ ret = regmap_write(wcd->if_regmap,
+ WCD934X_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port),
+ payload);
+
+ if (ret < 0)
+ goto err;
+
+ /* configure the slave port for water mark and enable*/
+ ret = regmap_write(wcd->if_regmap,
+ WCD934X_SLIM_PGD_RX_PORT_CFG(ch->port),
+ WCD934X_SLIM_WATER_MARK_VAL);
+ if (ret < 0)
+ goto err;
+ } else {
+ ret = regmap_write(wcd->if_regmap,
+ WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port),
+ payload & 0x00FF);
+ if (ret < 0)
+ goto err;
+
+ /* ports 8,9 */
+ ret = regmap_write(wcd->if_regmap,
+ WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port),
+ (payload & 0xFF00) >> 8);
+ if (ret < 0)
+ goto err;
+
+ /* configure the slave port for water mark and enable*/
+ ret = regmap_write(wcd->if_regmap,
+ WCD934X_SLIM_PGD_TX_PORT_CFG(ch->port),
+ WCD934X_SLIM_WATER_MARK_VAL);
+
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ dai_data->sruntime = slim_stream_allocate(wcd->sdev, "WCD934x-SLIM");
+
+ return 0;
+
+err:
+ dev_err(wcd->dev, "Error Setting slim hw params\n");
+ kfree(cfg->chs);
+ cfg->chs = NULL;
+
+ return ret;
+}
+
+static int wcd934x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wcd934x_codec *wcd;
+ int ret, tx_fs_rate = 0;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = wcd934x_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(wcd->dev, "cannot set sample rate: %u\n",
+ params_rate(params));
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16 ... 24:
+ wcd->dai[dai->id].sconfig.bps = params_width(params);
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid format 0x%x\n",
+ params_width(params));
+ return -EINVAL;
+ }
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid TX sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
+
+ }
+
+ ret = wcd934x_set_decimator_rate(dai, tx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ dev_err(wcd->dev, "Cannot set TX Decimator rate\n");
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16 ... 32:
+ wcd->dai[dai->id].sconfig.bps = params_width(params);
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid format 0x%x\n",
+ params_width(params));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid stream type %d\n",
+ substream->stream);
+ return -EINVAL;
+ }
+
+ wcd->dai[dai->id].sconfig.rate = params_rate(params);
+
+ return wcd934x_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream);
+}
+
+static int wcd934x_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wcd_slim_codec_dai_data *dai_data;
+ struct wcd934x_codec *wcd;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ dai_data = &wcd->dai[dai->id];
+
+ kfree(dai_data->sconfig.chs);
+
+ return 0;
+}
+
+static int wcd934x_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct wcd_slim_codec_dai_data *dai_data;
+ struct wcd934x_codec *wcd;
+ struct slim_stream_config *cfg;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ dai_data = &wcd->dai[dai->id];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cfg = &dai_data->sconfig;
+ slim_stream_prepare(dai_data->sruntime, cfg);
+ slim_stream_enable(dai_data->sruntime);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ slim_stream_disable(dai_data->sruntime);
+ slim_stream_unprepare(dai_data->sruntime);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
+{
+ struct wcd934x_codec *wcd;
+ int i;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ if (tx_num > WCD934X_TX_MAX || rx_num > WCD934X_RX_MAX) {
+ dev_err(wcd->dev, "Invalid tx %d or rx %d channel count\n",
+ tx_num, rx_num);
+ return -EINVAL;
+ }
+
+ if (!tx_slot || !rx_slot) {
+ dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n",
+ tx_slot, rx_slot);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++) {
+ wcd->rx_chs[i].ch_num = rx_slot[i];
+ INIT_LIST_HEAD(&wcd->rx_chs[i].list);
+ }
+
+ for (i = 0; i < tx_num; i++) {
+ wcd->tx_chs[i].ch_num = tx_slot[i];
+ INIT_LIST_HEAD(&wcd->tx_chs[i].list);
+ }
+
+ return 0;
+}
+
+static int wcd934x_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct wcd934x_slim_ch *ch;
+ struct wcd934x_codec *wcd;
+ int i = 0;
+
+ wcd = snd_soc_component_get_drvdata(dai->component);
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ case AIF4_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n",
+ rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list)
+ rx_slot[i++] = ch->ch_num;
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n",
+ tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list)
+ tx_slot[i++] = ch->ch_num;
+
+ *tx_num = i;
+ break;
+ default:
+ dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wcd934x_dai_ops = {
+ .hw_params = wcd934x_hw_params,
+ .hw_free = wcd934x_hw_free,
+ .trigger = wcd934x_trigger,
+ .set_channel_map = wcd934x_set_channel_map,
+ .get_channel_map = wcd934x_get_channel_map,
+};
+
+static struct snd_soc_dai_driver wcd934x_slim_dais[] = {
+ [0] = {
+ .name = "wcd934x_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+ [1] = {
+ .name = "wcd934x_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD934X_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+ [2] = {
+ .name = "wcd934x_rx2",
+ .id = AIF2_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+ [3] = {
+ .name = "wcd934x_tx2",
+ .id = AIF2_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD934X_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+ [4] = {
+ .name = "wcd934x_rx3",
+ .id = AIF3_PB,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+ [5] = {
+ .name = "wcd934x_tx3",
+ .id = AIF3_CAP,
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .rates = WCD934X_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+ [6] = {
+ .name = "wcd934x_rx4",
+ .id = AIF4_PB,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd934x_dai_ops,
+ },
+};
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ return wcd934x_swrm_clock(to_wcd934x_codec(hw), true);
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ wcd934x_swrm_clock(to_wcd934x_codec(hw), false);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct wcd934x_codec *wcd = to_wcd934x_codec(hw);
+ int ret, val;
+
+ regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & WCD934X_CDC_SWR_CLK_EN_MASK;
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+
+};
+
+static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd)
+{
+ struct clk *parent = wcd->extclk;
+ struct device *dev = wcd->dev;
+ struct device_node *np = dev->parent->of_node;
+ const char *parent_clk_name = NULL;
+ const char *clk_name = "mclk";
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ if (of_property_read_u32(np, "clock-frequency", &wcd->rate))
+ return NULL;
+
+ parent_clk_name = __clk_get_name(parent);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ wcd->hw.init = &init;
+
+ hw = &wcd->hw;
+ ret = devm_clk_hw_register(wcd->dev->parent, hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return NULL;
+}
+
+static int wcd934x_init_dmic(struct snd_soc_component *comp)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ u32 def_dmic_rate, dmic_clk_drv;
+ int ret;
+
+ ret = wcd_dt_parse_mbhc_data(comp->dev, &wcd->mbhc_cfg);
+ if (ret)
+ return ret;
+
+ snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1,
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[0]);
+ snd_soc_component_update_bits(comp, WCD934X_ANA_MICB2,
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[1]);
+ snd_soc_component_update_bits(comp, WCD934X_ANA_MICB3,
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[2]);
+ snd_soc_component_update_bits(comp, WCD934X_ANA_MICB4,
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[3]);
+
+ if (wcd->rate == WCD934X_MCLK_CLK_9P6MHZ)
+ def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ else
+ def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+
+ wcd->dmic_sample_rate = def_dmic_rate;
+
+ dmic_clk_drv = 0;
+ snd_soc_component_update_bits(comp, WCD934X_TEST_DEBUG_PAD_DRVCTL_0,
+ 0x0C, dmic_clk_drv << 2);
+
+ return 0;
+}
+
+static void wcd934x_hw_init(struct wcd934x_codec *wcd)
+{
+ struct regmap *rm = wcd->regmap;
+
+ /* set SPKR rate to FS_2P4_3P072 */
+ regmap_update_bits(rm, WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08);
+ regmap_update_bits(rm, WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08);
+
+ /* Take DMICs out of reset */
+ regmap_update_bits(rm, WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00);
+}
+
+static int wcd934x_comp_init(struct snd_soc_component *component)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+
+ wcd934x_hw_init(wcd);
+ wcd934x_enable_efuse_sensing(wcd);
+ wcd934x_get_version(wcd);
+
+ return 0;
+}
+
+static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data)
+{
+ struct wcd934x_codec *wcd = data;
+ unsigned long status = 0;
+ unsigned int i, j, port_id;
+ unsigned int val, int_val = 0;
+ irqreturn_t ret = IRQ_NONE;
+ bool tx;
+ unsigned short reg = 0;
+
+ for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+ i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+ regmap_read(wcd->if_regmap, i, &val);
+ status |= ((u32)val << (8 * j));
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ tx = false;
+ port_id = j;
+
+ if (j >= 16) {
+ tx = true;
+ port_id = j - 16;
+ }
+
+ regmap_read(wcd->if_regmap,
+ WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val);
+ if (val) {
+ if (!tx)
+ reg = WCD934X_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ regmap_read(wcd->if_regmap, reg, &int_val);
+ }
+
+ if (val & WCD934X_SLIM_IRQ_OVERFLOW)
+ dev_err_ratelimited(wcd->dev,
+ "overflow error on %s port %d, value %x\n",
+ (tx ? "TX" : "RX"), port_id, val);
+
+ if (val & WCD934X_SLIM_IRQ_UNDERFLOW)
+ dev_err_ratelimited(wcd->dev,
+ "underflow error on %s port %d, value %x\n",
+ (tx ? "TX" : "RX"), port_id, val);
+
+ if ((val & WCD934X_SLIM_IRQ_OVERFLOW) ||
+ (val & WCD934X_SLIM_IRQ_UNDERFLOW)) {
+ if (!tx)
+ reg = WCD934X_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ regmap_read(
+ wcd->if_regmap, reg, &int_val);
+ if (int_val & (1 << (port_id % 8))) {
+ int_val = int_val ^ (1 << (port_id % 8));
+ regmap_write(wcd->if_regmap,
+ reg, int_val);
+ }
+ }
+
+ if (val & WCD934X_SLIM_IRQ_PORT_CLOSED)
+ dev_err_ratelimited(wcd->dev,
+ "Port Closed %s port %d, value %x\n",
+ (tx ? "TX" : "RX"), port_id, val);
+
+ regmap_write(wcd->if_regmap,
+ WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8),
+ BIT(j % 8));
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static void wcd934x_mbhc_clk_setup(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_CTL_1,
+ WCD934X_MBHC_CTL_RCO_EN_MASK, enable);
+}
+
+static void wcd934x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_ELECT,
+ WCD934X_ANA_MBHC_BIAS_EN, enable);
+}
+
+static void wcd934x_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_BTN0 + i,
+ WCD934X_MBHC_BTN_VTH_MASK, vth);
+ }
+}
+
+static bool wcd934x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
+{
+ u8 val;
+
+ if (micb_num == MIC_BIAS_2) {
+ val = snd_soc_component_read_field(component, WCD934X_ANA_MICB2,
+ WCD934X_ANA_MICB2_ENABLE_MASK);
+ if (val == WCD934X_MICB_ENABLE)
+ return true;
+ }
+ return false;
+}
+
+static void wcd934x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
+ enum mbhc_hs_pullup_iref pull_up_cur)
+{
+ /* Default pull up current to 2uA */
+ if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA ||
+ pull_up_cur == I_DEFAULT)
+ pull_up_cur = I_2P0_UA;
+
+
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_PLUG_DETECT_CTL,
+ WCD934X_HSDET_PULLUP_C_MASK, pull_up_cur);
+}
+
+static int wcd934x_micbias_control(struct snd_soc_component *component,
+ int micb_num, int req, bool is_dapm)
+{
+ struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD934X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD934X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD934X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD934X_ANA_MICB4;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+ mutex_lock(&wcd934x->micb_lock);
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ wcd934x->pullup_ref[micb_index]++;
+ if ((wcd934x->pullup_ref[micb_index] == 1) &&
+ (wcd934x->micb_ref[micb_index] == 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_PULL_UP);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (wcd934x->pullup_ref[micb_index] > 0)
+ wcd934x->pullup_ref[micb_index]--;
+
+ if ((wcd934x->pullup_ref[micb_index] == 0) &&
+ (wcd934x->micb_ref[micb_index] == 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK, 0);
+ break;
+ case MICB_ENABLE:
+ wcd934x->micb_ref[micb_index]++;
+ if (wcd934x->micb_ref[micb_index] == 1) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_ENABLE);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+ }
+
+ if (micb_num == MIC_BIAS_2 && is_dapm)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+ break;
+ case MICB_DISABLE:
+ if (wcd934x->micb_ref[micb_index] > 0)
+ wcd934x->micb_ref[micb_index]--;
+
+ if ((wcd934x->micb_ref[micb_index] == 0) &&
+ (wcd934x->pullup_ref[micb_index] > 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_PULL_UP);
+ else if ((wcd934x->micb_ref[micb_index] == 0) &&
+ (wcd934x->pullup_ref[micb_index] == 0)) {
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_PRE_MICBIAS_2_OFF);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK, 0);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ }
+ if (is_dapm && micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+ break;
+ }
+
+ mutex_unlock(&wcd934x->micb_lock);
+
+ return 0;
+}
+
+static int wcd934x_mbhc_request_micbias(struct snd_soc_component *component,
+ int micb_num, int req)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ int ret;
+
+ if (req == MICB_ENABLE)
+ __wcd934x_cdc_mclk_enable(wcd, true);
+
+ ret = wcd934x_micbias_control(component, micb_num, req, false);
+
+ if (req == MICB_DISABLE)
+ __wcd934x_cdc_mclk_enable(wcd, false);
+
+ return ret;
+}
+
+static void wcd934x_mbhc_micb_ramp_control(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_SHIFT_CTRL_MASK, 0x3);
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_SHIFT_CTRL_MASK, 0);
+ }
+}
+
+static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+ int req_volt, int micb_num)
+{
+ struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component);
+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD934X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD934X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD934X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD934X_ANA_MICB4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&wcd934x->micb_lock);
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_en = snd_soc_component_read_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK);
+ cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
+ WCD934X_MICB_VAL_MASK);
+
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
+ if (req_vout_ctl < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (cur_vout_ctl == req_vout_ctl) {
+ ret = 0;
+ goto exit;
+ }
+
+ if (micb_en == WCD934X_MICB_ENABLE)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_PULL_UP);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_MICB_VAL_MASK,
+ req_vout_ctl);
+
+ if (micb_en == WCD934X_MICB_ENABLE) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_ENABLE);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+exit:
+ mutex_unlock(&wcd934x->micb_lock);
+ return ret;
+}
+
+static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
+ int micb_num, bool req_en)
+{
+ struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component);
+ int rc, micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (wcd934x->common.micb_mv[1] >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd934x->common.micb_mv[1];
+
+ rc = wcd934x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+
+ return rc;
+}
+
+static void wcd934x_mbhc_get_result_params(struct wcd934x_codec *wcd934x,
+ s16 *d1_a, u16 noff,
+ int32_t *zdet)
+{
+ int i;
+ int val, val1;
+ s16 c1;
+ s32 x1, d1;
+ int32_t denom;
+ static const int minCode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+ };
+
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20);
+ for (i = 0; i < WCD934X_ZDET_NUM_MEASUREMENTS; i++) {
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_2, &val);
+ if (val & 0x80)
+ break;
+ }
+ val = val << 0x8;
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1);
+ val |= val1;
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00);
+ x1 = WCD934X_MBHC_GET_X1(val);
+ c1 = WCD934X_MBHC_GET_C1(val);
+ /* If ramp is not complete, give additional 5ms */
+ if ((c1 < 2) && x1)
+ usleep_range(5000, 5050);
+
+ if (!c1 || !x1) {
+ dev_err(wcd934x->dev, "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ __func__, c1, x1);
+ goto ramp_down;
+ }
+ d1 = d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - noff));
+ if (denom > 0)
+ *zdet = (WCD934X_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < minCode_param[noff])
+ *zdet = WCD934X_ZDET_FLOATING_IMPEDANCE;
+
+ dev_dbg(wcd934x->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%di (milliohm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+
+ while (x1) {
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_1, &val);
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_2, &val1);
+ val = val << 0x08;
+ val |= val1;
+ x1 = WCD934X_MBHC_GET_X1(val);
+ i++;
+ if (i == WCD934X_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+static void wcd934x_mbhc_zdet_ramp(struct snd_soc_component *component,
+ struct wcd934x_mbhc_zdet_param *zdet_param,
+ int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+ struct wcd934x_codec *wcd934x = dev_get_drvdata(component->dev);
+ int32_t zdet = 0;
+
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD934X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl);
+ snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN5,
+ WCD934X_VTH_MASK, zdet_param->btn5);
+ snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN6,
+ WCD934X_VTH_MASK, zdet_param->btn6);
+ snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN7,
+ WCD934X_VTH_MASK, zdet_param->btn7);
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD934X_ZDET_RANGE_CTL_MASK, zdet_param->noff);
+ snd_soc_component_update_bits(component, WCD934X_MBHC_NEW_ZDET_RAMP_CTL,
+ 0x0F, zdet_param->nshift);
+
+ if (!zl)
+ goto z_right;
+ /* Start impedance measurement for HPH_L */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x80, 0x80);
+ wcd934x_mbhc_get_result_params(wcd934x, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x80, 0x00);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+ /* Start impedance measurement for HPH_R */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x40, 0x40);
+ wcd934x_mbhc_get_result_params(wcd934x, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x40, 0x00);
+
+ *zr = zdet;
+}
+
+static void wcd934x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ int32_t *z_val, int flag_l_r)
+{
+ s16 q1;
+ int q1_cal;
+
+ if (*z_val < (WCD934X_ZDET_VAL_400/1000))
+ q1 = snd_soc_component_read(component,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r));
+ else
+ q1 = snd_soc_component_read(component,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r));
+ if (q1 & 0x80)
+ q1_cal = (10000 - ((q1 & 0x7F) * 25));
+ else
+ q1_cal = (10000 + (q1 * 25));
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void wcd934x_wcd_mbhc_calc_impedance(struct snd_soc_component *component,
+ uint32_t *zl, uint32_t *zr)
+{
+ struct wcd934x_codec *wcd934x = dev_get_drvdata(component->dev);
+ s16 reg0, reg1, reg2, reg3, reg4;
+ int32_t z1L, z1R, z1Ls;
+ int zMono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ struct wcd934x_mbhc_zdet_param zdet_param[] = {
+ {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+ {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+ {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+ {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+ };
+ struct wcd934x_mbhc_zdet_param *zdet_param_ptr = NULL;
+ s16 d1_a[][4] = {
+ {0, 30, 90, 30},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ };
+ s16 *d1 = NULL;
+
+ reg0 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN5);
+ reg1 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN6);
+ reg2 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN7);
+ reg3 = snd_soc_component_read(component, WCD934X_MBHC_CTL_CLK);
+ reg4 = snd_soc_component_read(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL);
+
+ if (snd_soc_component_read(component, WCD934X_ANA_MBHC_ELECT) & 0x80) {
+ is_fsm_disable = true;
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ELECT, 0x80, 0x00);
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (wcd934x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x80, 0x00);
+
+ /* Turn off 100k pull down on HPHL */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x01, 0x00);
+
+ /* First get impedance on Left */
+ d1 = d1_a[1];
+ zdet_param_ptr = &zdet_param[1];
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+ if (!WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+ goto left_ch_impedance;
+
+ /* Second ramp for left ch */
+ if (z1L < WCD934X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1L > WCD934X_ZDET_VAL_400) &&
+ (z1L <= WCD934X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1L > WCD934X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+ if ((z1L == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ (z1L > WCD934X_ZDET_VAL_100K)) {
+ *zl = WCD934X_ZDET_FLOATING_IMPEDANCE;
+ zdet_param_ptr = &zdet_param[1];
+ d1 = d1_a[1];
+ } else {
+ *zl = z1L/1000;
+ wcd934x_wcd_mbhc_qfuse_cal(component, zl, 0);
+ }
+ dev_info(component->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+ __func__, *zl);
+
+ /* Start of right impedance ramp and calculation */
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+ if (WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+ if (((z1R > WCD934X_ZDET_VAL_1200) &&
+ (zdet_param_ptr->noff == 0x6)) ||
+ ((*zl) != WCD934X_ZDET_FLOATING_IMPEDANCE))
+ goto right_ch_impedance;
+ /* Second ramp for right ch */
+ if (z1R < WCD934X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1R > WCD934X_ZDET_VAL_400) &&
+ (z1R <= WCD934X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1R > WCD934X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+ }
+right_ch_impedance:
+ if ((z1R == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ (z1R > WCD934X_ZDET_VAL_100K)) {
+ *zr = WCD934X_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1R/1000;
+ wcd934x_wcd_mbhc_qfuse_cal(component, zr, 1);
+ }
+ dev_err(component->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+ __func__, *zr);
+
+ /* Mono/stereo detection */
+ if ((*zl == WCD934X_ZDET_FLOATING_IMPEDANCE) &&
+ (*zr == WCD934X_ZDET_FLOATING_IMPEDANCE)) {
+ dev_dbg(component->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+ if ((*zl == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ (*zr == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+ ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+ dev_dbg(component->dev,
+ "%s: Mono plug type with one ch floating or shorted to GND\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_MONO);
+ goto zdet_complete;
+ }
+ snd_soc_component_write_field(component, WCD934X_HPH_R_ATEST,
+ WCD934X_HPHPA_GND_OVR_MASK, 1);
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_R_MASK, 1);
+ if (*zl < (WCD934X_ZDET_VAL_32/1000))
+ wcd934x_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, NULL, d1);
+ else
+ wcd934x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1);
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_R_MASK, 0);
+ snd_soc_component_write_field(component, WCD934X_HPH_R_ATEST,
+ WCD934X_HPHPA_GND_OVR_MASK, 0);
+ z1Ls /= 1000;
+ wcd934x_wcd_mbhc_qfuse_cal(component, &z1Ls, 0);
+ /* Parallel of left Z and 9 ohm pull down resistor */
+ zMono = ((*zl) * 9) / ((*zl) + 9);
+ z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls);
+ z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl));
+ if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) {
+ dev_err(component->dev, "%s: stereo plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_STEREO);
+ } else {
+ dev_err(component->dev, "%s: MONO plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_MONO);
+ }
+
+zdet_complete:
+ snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN5, reg0);
+ snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN6, reg1);
+ snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN7, reg2);
+ /* Turn on 100k pull down on HPHL */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x01, 0x01);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (wcd934x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x80, 0x80);
+
+ snd_soc_component_write(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+ snd_soc_component_write(component, WCD934X_MBHC_CTL_CLK, reg3);
+ if (is_fsm_disable)
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+
+static void wcd934x_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_HSG_PULLUP_COMP_EN, 1);
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_GND_DET_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_GND_DET_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_HSG_PULLUP_COMP_EN, 0);
+ }
+}
+
+static void wcd934x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_R_MASK, enable);
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_L_MASK, enable);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .clk_setup = wcd934x_mbhc_clk_setup,
+ .mbhc_bias = wcd934x_mbhc_mbhc_bias_control,
+ .set_btn_thr = wcd934x_mbhc_program_btn_thr,
+ .micbias_enable_status = wcd934x_mbhc_micb_en_status,
+ .hph_pull_up_control = wcd934x_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = wcd934x_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = wcd934x_mbhc_micb_ramp_control,
+ .mbhc_micb_ctrl_thr_mic = wcd934x_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = wcd934x_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = wcd934x_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = wcd934x_mbhc_hph_pull_down_ctrl,
+};
+
+static int wcd934x_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd->mbhc);
+
+ return 0;
+}
+
+static int wcd934x_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_mixer_control *mc;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+ wcd_mbhc_get_impedance(wcd->mbhc, &zl, &zr);
+ dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0,
+ wcd934x_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0,
+ wcd934x_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0,
+ wcd934x_hph_impedance_get, NULL),
+};
+
+static int wcd934x_mbhc_init(struct snd_soc_component *component)
+{
+ struct wcd934x_ddata *data = dev_get_drvdata(component->dev->parent);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &wcd->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_SW_DET);
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_BUTTON_PRESS_DET);
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET);
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_ELECT_INS_REM_DET);
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_HPH_PA_OCPL_FAULT);
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_HPH_PA_OCPR_FAULT);
+
+ wcd->mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+ if (IS_ERR(wcd->mbhc)) {
+ wcd->mbhc = NULL;
+ return -EINVAL;
+ }
+
+ snd_soc_add_component_controls(component, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_component_controls(component, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ return 0;
+}
+
+static void wcd934x_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+
+ if (!wcd->mbhc)
+ return;
+
+ wcd_mbhc_deinit(wcd->mbhc);
+}
+
+static int wcd934x_comp_probe(struct snd_soc_component *component)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, wcd->regmap);
+ wcd->component = component;
+
+ /* Class-H Init*/
+ wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, wcd->version);
+ if (IS_ERR(wcd->clsh_ctrl))
+ return PTR_ERR(wcd->clsh_ctrl);
+
+ /* Default HPH Mode to Class-H Low HiFi */
+ wcd->hph_mode = CLS_H_LOHIFI;
+
+ wcd934x_comp_init(component);
+
+ for (i = 0; i < NUM_CODEC_DAIS; i++)
+ INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list);
+
+
+ ret = wcd934x_init_dmic(component);
+ if (ret) {
+ dev_err(component->dev, "Failed to Initialize micbias\n");
+ return ret;
+ }
+
+ if (wcd934x_mbhc_init(component))
+ dev_err(component->dev, "Failed to Initialize MBHC\n");
+
+ return 0;
+}
+
+static void wcd934x_comp_remove(struct snd_soc_component *comp)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+
+ wcd934x_mbhc_deinit(comp);
+ wcd_clsh_ctrl_free(wcd->clsh_ctrl);
+}
+
+static int wcd934x_comp_set_sysclk(struct snd_soc_component *comp,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ int val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ;
+
+ wcd->rate = freq;
+
+ if (wcd->rate == WCD934X_MCLK_CLK_12P288MHZ)
+ val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ;
+
+ snd_soc_component_update_bits(comp, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK,
+ val);
+
+ return clk_set_rate(wcd->extclk, freq);
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx, int coeff_idx)
+{
+ u32 value = 0;
+ int reg, b2_reg;
+
+ /* Address does not automatically update if reading */
+ reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx;
+ b2_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx;
+
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx) *
+ sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_component_read(component, b2_reg);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_component_read(component, b2_reg) << 8);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_component_read(component, b2_reg) << 16);
+ snd_soc_component_write(component, reg,
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= (snd_soc_component_read(component, b2_reg) << 24);
+ return value;
+}
+
+static void set_iir_band_coeff(struct snd_soc_component *component,
+ int iir_idx, int band_idx, uint32_t value)
+{
+ int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx;
+
+ snd_soc_component_write(component, reg, (value & 0xFF));
+ snd_soc_component_write(component, reg, (value >> 8) & 0xFF);
+ snd_soc_component_write(component, reg, (value >> 16) & 0xFF);
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_component_write(component, reg, (value >> 24) & 0x3F);
+}
+
+static int wcd934x_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+ int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx;
+
+ memcpy(&coeff[0], ucontrol->value.bytes.data, params->max);
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_component_write(component, reg, (band_idx * BAND_MAX *
+ sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]);
+ set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]);
+
+ return 0;
+}
+
+static int wcd934x_get_iir_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+ int iir_idx = ctl->iir_idx;
+ int band_idx = ctl->band_idx;
+ u32 coeff[BAND_MAX];
+
+ coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0);
+ coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1);
+ coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2);
+ coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3);
+ coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4);
+
+ memcpy(ucontrol->value.bytes.data, &coeff[0], params->max);
+
+ return 0;
+}
+
+static int wcd934x_iir_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct wcd_iir_filter_ctl *ctl =
+ (struct wcd_iir_filter_ctl *)kcontrol->private_value;
+ struct soc_bytes_ext *params = &ctl->bytes_ext;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+
+static int wcd934x_compander_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ int comp = ((struct soc_mixer_control *)kc->private_value)->shift;
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+
+ ucontrol->value.integer.value[0] = wcd->comp_enabled[comp];
+
+ return 0;
+}
+
+static int wcd934x_compander_set(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ int comp = ((struct soc_mixer_control *)kc->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ int sel;
+
+ if (wcd->comp_enabled[comp] == value)
+ return 0;
+
+ wcd->comp_enabled[comp] = value;
+ sel = value ? WCD934X_HPH_GAIN_SRC_SEL_COMPANDER :
+ WCD934X_HPH_GAIN_SRC_SEL_REGISTER;
+
+ /* Any specific register configuration for compander */
+ switch (comp) {
+ case COMPANDER_1:
+ /* Set Gain Source Select based on compander enable/disable */
+ snd_soc_component_update_bits(component, WCD934X_HPH_L_EN,
+ WCD934X_HPH_GAIN_SRC_SEL_MASK,
+ sel);
+ break;
+ case COMPANDER_2:
+ snd_soc_component_update_bits(component, WCD934X_HPH_R_EN,
+ WCD934X_HPH_GAIN_SRC_SEL_MASK,
+ sel);
+ break;
+ case COMPANDER_3:
+ case COMPANDER_4:
+ case COMPANDER_7:
+ case COMPANDER_8:
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static int wcd934x_rx_hph_mode_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+
+ ucontrol->value.enumerated.item[0] = wcd->hph_mode;
+
+ return 0;
+}
+
+static int wcd934x_rx_hph_mode_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kc);
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ if (mode_val == wcd->hph_mode)
+ return 0;
+
+ if (mode_val == 0) {
+ dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n");
+ mode_val = CLS_H_LOHIFI;
+ }
+ wcd->hph_mode = mode_val;
+
+ return 1;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kc);
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(dev);
+
+ ucontrol->value.enumerated.item[0] = wcd->rx_port_value[w->shift];
+
+ return 0;
+}
+
+static int slim_rx_mux_to_dai_id(int mux)
+{
+ int aif_id;
+
+ switch (mux) {
+ case 1:
+ aif_id = AIF1_PB;
+ break;
+ case 2:
+ aif_id = AIF2_PB;
+ break;
+ case 3:
+ aif_id = AIF3_PB;
+ break;
+ case 4:
+ aif_id = AIF4_PB;
+ break;
+ default:
+ aif_id = -1;
+ break;
+ }
+
+ return aif_id;
+}
+
+static int slim_rx_mux_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct device *dev = snd_soc_dapm_to_dev(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(dev);
+ struct soc_enum *e = (struct soc_enum *)kc->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ struct wcd934x_slim_ch *ch, *c;
+ u32 port_id = w->shift;
+ bool found = false;
+ int mux_idx;
+ int prev_mux_idx = wcd->rx_port_value[port_id];
+ int aif_id;
+
+ mux_idx = ucontrol->value.enumerated.item[0];
+
+ if (mux_idx == prev_mux_idx)
+ return 0;
+
+ switch(mux_idx) {
+ case 0:
+ aif_id = slim_rx_mux_to_dai_id(prev_mux_idx);
+ if (aif_id < 0)
+ return 0;
+
+ list_for_each_entry_safe(ch, c, &wcd->dai[aif_id].slim_ch_list, list) {
+ if (ch->port == port_id + WCD934X_RX_START) {
+ found = true;
+ list_del_init(&ch->list);
+ break;
+ }
+ }
+ if (!found)
+ return 0;
+
+ break;
+ case 1 ... 4:
+ aif_id = slim_rx_mux_to_dai_id(mux_idx);
+ if (aif_id < 0)
+ return 0;
+
+ if (list_empty(&wcd->rx_chs[port_id].list)) {
+ list_add_tail(&wcd->rx_chs[port_id].list,
+ &wcd->dai[aif_id].slim_ch_list);
+ } else {
+ dev_err(wcd->dev ,"SLIM_RX%d PORT is busy\n", port_id);
+ return 0;
+ }
+ break;
+
+ default:
+ dev_err(wcd->dev, "Unknown AIF %d\n", mux_idx);
+ goto err;
+ }
+
+ wcd->rx_port_value[port_id] = mux_idx;
+ snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id],
+ e, update);
+
+ return 1;
+err:
+ return -EINVAL;
+}
+
+static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kc->private_value;
+ struct snd_soc_component *component;
+ int reg, val;
+
+ component = snd_soc_dapm_kcontrol_to_component(kc);
+ val = ucontrol->value.enumerated.item[0];
+ if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0)
+ reg = WCD934X_CDC_RX0_RX_PATH_CFG0;
+ else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0)
+ reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+ else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0)
+ reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+ else
+ return -EINVAL;
+
+ /* Set Look Ahead Delay */
+ if (val)
+ snd_soc_component_update_bits(component, reg,
+ WCD934X_RX_DLY_ZN_EN_MASK,
+ WCD934X_RX_DLY_ZN_ENABLE);
+ else
+ snd_soc_component_update_bits(component, reg,
+ WCD934X_RX_DLY_ZN_EN_MASK,
+ WCD934X_RX_DLY_ZN_DISABLE);
+
+ return snd_soc_dapm_put_enum_double(kc, ucontrol);
+}
+
+static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ u16 mic_sel_reg = 0;
+ u8 mic_sel;
+
+ comp = snd_soc_dapm_kcontrol_to_component(kcontrol);
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val > e->items - 1)
+ return -EINVAL;
+
+ switch (e->reg) {
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0;
+ else if (e->shift_l == 4)
+ mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0;
+ break;
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0;
+ break;
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0;
+ break;
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(comp->dev, "%s: e->reg: 0x%x not expected\n",
+ __func__, e->reg);
+ return -EINVAL;
+ }
+
+ /* ADC: 0, DMIC: 1 */
+ mic_sel = val ? 0x0 : 0x1;
+ if (mic_sel_reg)
+ snd_soc_component_update_bits(comp, mic_sel_reg, BIT(7),
+ mic_sel << 7);
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new rx_int0_2_mux =
+ SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_2_mux =
+ SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_2_mux =
+ SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_2_mux =
+ SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_2_mux =
+ SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_2_mux =
+ SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_2_mux =
+ SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_mix2_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int1_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_mix2_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int2_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_mix2_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int3_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_mix2_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int4_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_mix2_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int7_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_mix2_inp_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp0_mux =
+ SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum);
+static const struct snd_kcontrol_new iir0_inp1_mux =
+ SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum);
+static const struct snd_kcontrol_new iir0_inp2_mux =
+ SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum);
+static const struct snd_kcontrol_new iir0_inp3_mux =
+ SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp0_mux =
+ SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum);
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+static const struct snd_kcontrol_new iir1_inp2_mux =
+ SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum);
+static const struct snd_kcontrol_new iir1_inp3_mux =
+ SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum);
+
+static const struct snd_kcontrol_new slim_rx_mux[WCD934X_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new rx_int1_asrc_switch[] = {
+ SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int2_asrc_switch[] = {
+ SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int3_asrc_switch[] = {
+ SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int4_asrc_switch[] = {
+ SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int0_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd934x_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd934x_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int2_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ wcd934x_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int0_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT0_1 INTERP Mux", rx_int0_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT1_1 INTERP Mux", rx_int1_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT2_1 INTERP Mux", rx_int2_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT3_1 INTERP Mux", rx_int3_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT4_1 INTERP Mux", rx_int4_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT7_1 INTERP Mux", rx_int7_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_interp_mux =
+ SOC_DAPM_ENUM("RX INT8_1 INTERP Mux", rx_int8_1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int0_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT0_2 INTERP Mux", rx_int0_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int1_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT1_2 INTERP Mux", rx_int1_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int2_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT2_2 INTERP Mux", rx_int2_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int3_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT3_2 INTERP Mux", rx_int3_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int4_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT4_2 INTERP Mux", rx_int4_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int7_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT7_2 INTERP Mux", rx_int7_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int8_2_interp_mux =
+ SOC_DAPM_ENUM("RX INT8_2 INTERP Mux", rx_int8_2_interp_mux_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux0 =
+ SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux1 =
+ SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux2 =
+ SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux3 =
+ SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux4 =
+ SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux5 =
+ SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux6 =
+ SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux7 =
+ SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux8 =
+ SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux0 =
+ SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux1 =
+ SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux2 =
+ SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux3 =
+ SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux4 =
+ SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux5 =
+ SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux6 =
+ SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux7 =
+ SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux8 =
+ SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_amic4_5 =
+ SOC_DAPM_ENUM("AMIC4_5 SEL Mux", tx_amic4_5_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux0_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux1_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux2_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux3_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux4_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux5_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux6_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux7_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+static const struct snd_kcontrol_new tx_adc_mux8_mux =
+ SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_enum,
+ snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put);
+
+static const struct snd_kcontrol_new cdc_if_tx0_mux =
+ SOC_DAPM_ENUM("CDC_IF TX0 MUX Mux", cdc_if_tx0_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx1_mux =
+ SOC_DAPM_ENUM("CDC_IF TX1 MUX Mux", cdc_if_tx1_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx2_mux =
+ SOC_DAPM_ENUM("CDC_IF TX2 MUX Mux", cdc_if_tx2_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx3_mux =
+ SOC_DAPM_ENUM("CDC_IF TX3 MUX Mux", cdc_if_tx3_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx4_mux =
+ SOC_DAPM_ENUM("CDC_IF TX4 MUX Mux", cdc_if_tx4_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx5_mux =
+ SOC_DAPM_ENUM("CDC_IF TX5 MUX Mux", cdc_if_tx5_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx6_mux =
+ SOC_DAPM_ENUM("CDC_IF TX6 MUX Mux", cdc_if_tx6_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx7_mux =
+ SOC_DAPM_ENUM("CDC_IF TX7 MUX Mux", cdc_if_tx7_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx8_mux =
+ SOC_DAPM_ENUM("CDC_IF TX8 MUX Mux", cdc_if_tx8_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx9_mux =
+ SOC_DAPM_ENUM("CDC_IF TX9 MUX Mux", cdc_if_tx9_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx10_mux =
+ SOC_DAPM_ENUM("CDC_IF TX10 MUX Mux", cdc_if_tx10_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx11_mux =
+ SOC_DAPM_ENUM("CDC_IF TX11 MUX Mux", cdc_if_tx11_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx11_inp1_mux =
+ SOC_DAPM_ENUM("CDC_IF TX11 INP1 MUX Mux", cdc_if_tx11_inp1_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx13_mux =
+ SOC_DAPM_ENUM("CDC_IF TX13 MUX Mux", cdc_if_tx13_mux_enum);
+static const struct snd_kcontrol_new cdc_if_tx13_inp1_mux =
+ SOC_DAPM_ENUM("CDC_IF TX13 INP1 MUX Mux", cdc_if_tx13_inp1_mux_enum);
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kc);
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(dev);
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kc->private_value;
+ int port_id = mixer->shift;
+
+ ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id];
+
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_to_widget(kc);
+ struct device *dev = snd_soc_dapm_to_dev(widget->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(dev);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kc->private_value;
+ int enable = ucontrol->value.integer.value[0];
+ struct wcd934x_slim_ch *ch, *c;
+ int dai_id = widget->shift;
+ int port_id = mixer->shift;
+
+ /* only add to the list if value not set */
+ if (enable == wcd->tx_port_value[port_id])
+ return 0;
+
+ if (enable) {
+ if (list_empty(&wcd->tx_chs[port_id].list)) {
+ list_add_tail(&wcd->tx_chs[port_id].list,
+ &wcd->dai[dai_id].slim_ch_list);
+ } else {
+ dev_err(wcd->dev ,"SLIM_TX%d PORT is busy\n", port_id);
+ return 0;
+ }
+ } else {
+ bool found = false;
+
+ list_for_each_entry_safe(ch, c, &wcd->dai[dai_id].slim_ch_list, list) {
+ if (ch->port == port_id) {
+ found = true;
+ list_del_init(&wcd->tx_chs[port_id].list);
+ break;
+ }
+ }
+ if (!found)
+ return 0;
+ }
+
+ wcd->tx_port_value[port_id] = enable;
+ snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new wcd934x_snd_controls[] = {
+ /* Gain Controls */
+ SOC_SINGLE_TLV("EAR PA Volume", WCD934X_ANA_EAR, 4, 4, 1, ear_pa_gain),
+ SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 24, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 24, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER,
+ 3, 16, 1, line_gain),
+
+ SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain),
+
+ SOC_SINGLE_S8_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_S8_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX0 Mix Digital Volume",
+ WCD934X_CDC_RX0_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX1 Mix Digital Volume",
+ WCD934X_CDC_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX2 Mix Digital Volume",
+ WCD934X_CDC_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX3 Mix Digital Volume",
+ WCD934X_CDC_RX3_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX4 Mix Digital Volume",
+ WCD934X_CDC_RX4_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX7 Mix Digital Volume",
+ WCD934X_CDC_RX7_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX8 Mix Digital Volume",
+ WCD934X_CDC_RX8_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_S8_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_S8_TLV("IIR0 INP0 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP1 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP2 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR0 INP3 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP0 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP1 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP2 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, -84, 40,
+ digital_gain),
+ SOC_SINGLE_S8_TLV("IIR1 INP3 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, -84, 40,
+ digital_gain),
+
+ SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
+ SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+ SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+ SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+ SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+ SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+ SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+ SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+ SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+
+ SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum),
+ SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum),
+ SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum),
+ SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum),
+ SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum),
+ SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum),
+ SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum),
+ SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum),
+ SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum),
+ SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum),
+ SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum),
+ SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum),
+ SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
+ SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ wcd934x_rx_hph_mode_get, wcd934x_rx_hph_mode_put),
+
+ SOC_SINGLE("IIR1 Band1 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL,
+ 0, 1, 0),
+ SOC_SINGLE("IIR1 Band2 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL,
+ 1, 1, 0),
+ SOC_SINGLE("IIR1 Band3 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL,
+ 2, 1, 0),
+ SOC_SINGLE("IIR1 Band4 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL,
+ 3, 1, 0),
+ SOC_SINGLE("IIR1 Band5 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL,
+ 4, 1, 0),
+ SOC_SINGLE("IIR2 Band1 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL,
+ 0, 1, 0),
+ SOC_SINGLE("IIR2 Band2 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL,
+ 1, 1, 0),
+ SOC_SINGLE("IIR2 Band3 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL,
+ 2, 1, 0),
+ SOC_SINGLE("IIR2 Band4 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL,
+ 3, 1, 0),
+ SOC_SINGLE("IIR2 Band5 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL,
+ 4, 1, 0),
+ WCD_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1),
+ WCD_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2),
+ WCD_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3),
+ WCD_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4),
+ WCD_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5),
+
+ WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1),
+ WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2),
+ WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3),
+ WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4),
+ WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5),
+
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ wcd934x_compander_get, wcd934x_compander_set),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ wcd934x_compander_get, wcd934x_compander_set),
+ SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0,
+ wcd934x_compander_get, wcd934x_compander_set),
+ SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0,
+ wcd934x_compander_get, wcd934x_compander_set),
+ SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0,
+ wcd934x_compander_get, wcd934x_compander_set),
+ SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
+ wcd934x_compander_get, wcd934x_compander_set),
+};
+
+static void wcd934x_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai,
+ struct snd_soc_component *component)
+{
+ int port_num = 0;
+ unsigned short reg = 0;
+ unsigned int val = 0;
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ struct wcd934x_slim_ch *ch;
+
+ list_for_each_entry(ch, &dai->slim_ch_list, list) {
+ if (ch->port >= WCD934X_RX_START) {
+ port_num = ch->port - WCD934X_RX_START;
+ reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + (port_num / 8);
+ } else {
+ port_num = ch->port;
+ reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+ }
+
+ regmap_read(wcd->if_regmap, reg, &val);
+ if (!(val & BIT(port_num % 8)))
+ regmap_write(wcd->if_regmap, reg,
+ val | BIT(port_num % 8));
+ }
+}
+
+static int wcd934x_codec_enable_slim(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp);
+ struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift];
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wcd934x_codec_enable_int_port(dai, comp);
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd934x_codec_hd2_control(struct snd_soc_component *component,
+ u16 interp_idx, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg = 0;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+ break;
+ case INTERP_HPHR:
+ hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3;
+ hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+ break;
+ default:
+ return;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK,
+ WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P3125);
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK,
+ WCD934X_CDC_RX_PATH_CFG_HD2_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK,
+ WCD934X_CDC_RX_PATH_CFG_HD2_DISABLE);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK,
+ WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000);
+ }
+}
+
+static void wcd934x_codec_hphdelay_lutbypass(struct snd_soc_component *comp,
+ u16 interp_idx, int event)
+{
+ u8 hph_dly_mask;
+ u16 hph_lut_bypass_reg = 0;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hph_dly_mask = 1;
+ hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT;
+ break;
+ case INTERP_HPHR:
+ hph_dly_mask = 2;
+ hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT;
+ break;
+ default:
+ return;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0,
+ hph_dly_mask, 0x0);
+ snd_soc_component_update_bits(comp, hph_lut_bypass_reg,
+ WCD934X_HPH_LUT_BYPASS_MASK,
+ WCD934X_HPH_LUT_BYPASS_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0,
+ hph_dly_mask, hph_dly_mask);
+ snd_soc_component_update_bits(comp, hph_lut_bypass_reg,
+ WCD934X_HPH_LUT_BYPASS_MASK,
+ WCD934X_HPH_LUT_BYPASS_DISABLE);
+ }
+}
+
+static int wcd934x_config_compander(struct snd_soc_component *comp,
+ int interp_n, int event)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ int compander;
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+ /* EAR does not have compander */
+ if (!interp_n)
+ return 0;
+
+ compander = interp_n - 1;
+ if (!wcd->comp_enabled[compander])
+ return 0;
+
+ comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (compander * 8);
+ rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (compander * 20);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Compander Clock */
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_CLK_EN_MASK,
+ WCD934X_COMP_CLK_ENABLE);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_SOFT_RST_MASK,
+ WCD934X_COMP_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_SOFT_RST_MASK,
+ WCD934X_COMP_SOFT_RST_DISABLE);
+ snd_soc_component_update_bits(comp, rx_path_cfg0_reg,
+ WCD934X_HPH_CMP_EN_MASK,
+ WCD934X_HPH_CMP_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(comp, rx_path_cfg0_reg,
+ WCD934X_HPH_CMP_EN_MASK,
+ WCD934X_HPH_CMP_DISABLE);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_HALT_MASK,
+ WCD934X_COMP_HALT);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_SOFT_RST_MASK,
+ WCD934X_COMP_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_SOFT_RST_MASK,
+ WCD934X_COMP_SOFT_RST_DISABLE);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_CLK_EN_MASK, 0x0);
+ snd_soc_component_update_bits(comp, comp_ctl0_reg,
+ WCD934X_COMP_SOFT_RST_MASK, 0x0);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_enable_interp_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ int interp_idx = w->shift;
+ u16 main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Clk enable */
+ snd_soc_component_update_bits(comp, main_reg,
+ WCD934X_RX_CLK_EN_MASK,
+ WCD934X_RX_CLK_ENABLE);
+ wcd934x_codec_hd2_control(comp, interp_idx, event);
+ wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event);
+ wcd934x_config_compander(comp, interp_idx, event);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd934x_config_compander(comp, interp_idx, event);
+ wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event);
+ wcd934x_codec_hd2_control(comp, interp_idx, event);
+ /* Clk Disable */
+ snd_soc_component_update_bits(comp, main_reg,
+ WCD934X_RX_CLK_EN_MASK, 0);
+ /* Reset enable and disable */
+ snd_soc_component_update_bits(comp, main_reg,
+ WCD934X_RX_RESET_MASK,
+ WCD934X_RX_RESET_ENABLE);
+ snd_soc_component_update_bits(comp, main_reg,
+ WCD934X_RX_RESET_MASK,
+ WCD934X_RX_RESET_DISABLE);
+ /* Reset rate to 48K*/
+ snd_soc_component_update_bits(comp, main_reg,
+ WCD934X_RX_PCM_RATE_MASK,
+ WCD934X_RX_PCM_RATE_F_48K);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ int offset_val = 0;
+ u16 gain_reg, mix_reg;
+ int val = 0;
+
+ gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL +
+ (w->shift * WCD934X_RX_PATH_CTL_OFFSET);
+ mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL +
+ (w->shift * WCD934X_RX_PATH_CTL_OFFSET);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Clk enable */
+ snd_soc_component_update_bits(comp, mix_reg,
+ WCD934X_CDC_RX_MIX_CLK_EN_MASK,
+ WCD934X_CDC_RX_MIX_CLK_ENABLE);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(comp, gain_reg);
+ val += offset_val;
+ snd_soc_component_write(comp, gain_reg, val);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ int reg = w->reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* B1 GAIN */
+ snd_soc_component_write(comp, reg,
+ snd_soc_component_read(comp, reg));
+ /* B2 GAIN */
+ reg++;
+ snd_soc_component_write(comp, reg,
+ snd_soc_component_read(comp, reg));
+ /* B3 GAIN */
+ reg++;
+ snd_soc_component_write(comp, reg,
+ snd_soc_component_read(comp, reg));
+ /* B4 GAIN */
+ reg++;
+ snd_soc_component_write(comp, reg,
+ snd_soc_component_read(comp, reg));
+ /* B5 GAIN */
+ reg++;
+ snd_soc_component_write(comp, reg,
+ snd_soc_component_read(comp, reg));
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wcd934x_codec_enable_main_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ u16 gain_reg;
+
+ gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift *
+ WCD934X_RX_PATH_CTL_OFFSET);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write(comp, gain_reg,
+ snd_soc_component_read(comp, gain_reg));
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disable AutoChop timer during power up */
+ snd_soc_component_update_bits(comp,
+ WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0);
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR, CLS_H_NORMAL);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR, CLS_H_NORMAL);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ int hph_mode = wcd->hph_mode;
+ u8 dem_inp;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_component_read(comp,
+ WCD934X_CDC_RX1_RX_PATH_SEC0) & 0x03;
+
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ return -EINVAL;
+ }
+ if (hph_mode != CLS_H_LP)
+ /* Ripple freq control enable */
+ snd_soc_component_update_bits(comp,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ WCD934X_SIDO_RIPPLE_FREQ_EN_MASK,
+ WCD934X_SIDO_RIPPLE_FREQ_ENABLE);
+ /* Disable AutoChop timer during power up */
+ snd_soc_component_update_bits(comp,
+ WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0);
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL, hph_mode);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL, hph_mode);
+ if (hph_mode != CLS_H_LP)
+ /* Ripple freq control disable */
+ snd_soc_component_update_bits(comp,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ int hph_mode = wcd->hph_mode;
+ u8 dem_inp;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dem_inp = snd_soc_component_read(comp,
+ WCD934X_CDC_RX2_RX_PATH_SEC0) & 0x03;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ return -EINVAL;
+ }
+ if (hph_mode != CLS_H_LP)
+ /* Ripple freq control enable */
+ snd_soc_component_update_bits(comp,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ WCD934X_SIDO_RIPPLE_FREQ_EN_MASK,
+ WCD934X_SIDO_RIPPLE_FREQ_ENABLE);
+ /* Disable AutoChop timer during power up */
+ snd_soc_component_update_bits(comp,
+ WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0);
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR, hph_mode);
+ if (hph_mode != CLS_H_LP)
+ /* Ripple freq control disable */
+ snd_soc_component_update_bits(comp,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_lineout_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_LO, CLS_AB);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_LO, CLS_AB);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is needed.
+ */
+ usleep_range(20000, 20100);
+
+ snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST,
+ WCD934X_HPH_OCP_DET_MASK,
+ WCD934X_HPH_OCP_DET_ENABLE);
+ /* Remove Mute on primary path */
+ snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL,
+ WCD934X_RX_PATH_PGA_MUTE_EN_MASK,
+ 0);
+ /* Enable GM3 boost */
+ snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL,
+ WCD934X_HPH_GM3_BOOST_EN_MASK,
+ WCD934X_HPH_GM3_BOOST_ENABLE);
+ /* Enable AutoChop timer at the end of power up */
+ snd_soc_component_update_bits(comp,
+ WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK,
+ WCD934X_HPH_AUTOCHOP_TIMER_ENABLE);
+ /* Remove mix path mute */
+ snd_soc_component_update_bits(comp,
+ WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
+ WCD934X_CDC_RX_PGA_MUTE_EN_MASK, 0x00);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHL_PA_OFF);
+ /* Enable DSD Mute before PA disable */
+ snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST,
+ WCD934X_HPH_OCP_DET_MASK,
+ WCD934X_HPH_OCP_DET_DISABLE);
+ snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL,
+ WCD934X_RX_PATH_PGA_MUTE_EN_MASK,
+ WCD934X_RX_PATH_PGA_MUTE_ENABLE);
+ snd_soc_component_update_bits(comp,
+ WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
+ WCD934X_RX_PATH_PGA_MUTE_EN_MASK,
+ WCD934X_RX_PATH_PGA_MUTE_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA disable. If compander is
+ * disabled, then 20ms delay is needed after PA disable.
+ */
+ usleep_range(20000, 20100);
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHL_PA_OFF);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is needed.
+ */
+ usleep_range(20000, 20100);
+ snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST,
+ WCD934X_HPH_OCP_DET_MASK,
+ WCD934X_HPH_OCP_DET_ENABLE);
+ /* Remove mute */
+ snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL,
+ WCD934X_RX_PATH_PGA_MUTE_EN_MASK,
+ 0);
+ /* Enable GM3 boost */
+ snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL,
+ WCD934X_HPH_GM3_BOOST_EN_MASK,
+ WCD934X_HPH_GM3_BOOST_ENABLE);
+ /* Enable AutoChop timer at the end of power up */
+ snd_soc_component_update_bits(comp,
+ WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK,
+ WCD934X_HPH_AUTOCHOP_TIMER_ENABLE);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_component_read(comp,
+ WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & 0x10)
+ snd_soc_component_update_bits(comp,
+ WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
+ WCD934X_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD934X_CDC_RX_PGA_MUTE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_PRE_HPHR_PA_OFF);
+ snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST,
+ WCD934X_HPH_OCP_DET_MASK,
+ WCD934X_HPH_OCP_DET_DISABLE);
+ snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL,
+ WCD934X_RX_PATH_PGA_MUTE_EN_MASK,
+ WCD934X_RX_PATH_PGA_MUTE_ENABLE);
+ snd_soc_component_update_bits(comp,
+ WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
+ WCD934X_CDC_RX_PGA_MUTE_EN_MASK,
+ WCD934X_CDC_RX_PGA_MUTE_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA disable. If compander is
+ * disabled, then 20ms delay is needed after PA disable.
+ */
+ usleep_range(20000, 20100);
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHR_PA_OFF);
+ break;
+ }
+
+ return 0;
+}
+
+static u32 wcd934x_get_dmic_sample_rate(struct snd_soc_component *comp,
+ unsigned int dmic,
+ struct wcd934x_codec *wcd)
+{
+ u8 tx_stream_fs;
+ u8 adc_mux_index = 0, adc_mux_sel = 0;
+ bool dec_found = false;
+ u16 adc_mux_ctl_reg, tx_fs_reg;
+ u32 dmic_fs;
+
+ while (!dec_found && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) {
+ if (adc_mux_index < 4) {
+ adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ (adc_mux_index * 2);
+ } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) {
+ adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_index - 4;
+ } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) {
+ ++adc_mux_index;
+ continue;
+ }
+ adc_mux_sel = ((snd_soc_component_read(comp, adc_mux_ctl_reg)
+ & 0xF8) >> 3) - 1;
+
+ if (adc_mux_sel == dmic) {
+ dec_found = true;
+ break;
+ }
+
+ ++adc_mux_index;
+ }
+
+ if (dec_found && adc_mux_index <= 8) {
+ tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+ tx_stream_fs = snd_soc_component_read(comp, tx_fs_reg) & 0x0F;
+ if (tx_stream_fs <= 4)
+ dmic_fs = min(wcd->dmic_sample_rate, WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ);
+ else
+ dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ } else {
+ dmic_fs = wcd->dmic_sample_rate;
+ }
+
+ return dmic_fs;
+}
+
+static u8 wcd934x_get_dmic_clk_val(struct snd_soc_component *comp,
+ u32 mclk_rate, u32 dmic_clk_rate)
+{
+ u32 div_factor;
+ u8 dmic_ctl_val;
+
+ /* Default value to return in case of error */
+ if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2;
+ else
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3;
+
+ if (dmic_clk_rate == 0) {
+ dev_err(comp->dev,
+ "%s: dmic_sample_rate cannot be 0\n",
+ __func__);
+ goto done;
+ }
+
+ div_factor = mclk_rate / dmic_clk_rate;
+ switch (div_factor) {
+ case 2:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2;
+ break;
+ case 3:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3;
+ break;
+ case 4:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4;
+ break;
+ case 6:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6;
+ break;
+ case 8:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8;
+ break;
+ case 16:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16;
+ break;
+ default:
+ dev_err(comp->dev,
+ "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+ __func__, div_factor, mclk_rate, dmic_clk_rate);
+ break;
+ }
+
+done:
+ return dmic_ctl_val;
+}
+
+static int wcd934x_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ u8 dmic_clk_en = 0x01;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 dmic_rate_val, dmic_rate_shift = 1;
+ unsigned int dmic;
+ u32 dmic_sample_rate;
+ int ret;
+ char *wname;
+
+ wname = strpbrk(w->name, "012345");
+ if (!wname) {
+ dev_err(comp->dev, "%s: widget not found\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(wname, 10, &dmic);
+ if (ret < 0) {
+ dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &wcd->dmic_0_1_clk_cnt;
+ dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &wcd->dmic_2_3_clk_cnt;
+ dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &wcd->dmic_4_5_clk_cnt;
+ dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL;
+ break;
+ default:
+ dev_err(comp->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dmic_sample_rate = wcd934x_get_dmic_sample_rate(comp, dmic,
+ wcd);
+ dmic_rate_val = wcd934x_get_dmic_clk_val(comp, wcd->rate,
+ dmic_sample_rate);
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ dmic_rate_val = dmic_rate_val << dmic_rate_shift;
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ WCD934X_DMIC_RATE_MASK,
+ dmic_rate_val);
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ dmic_clk_en, dmic_clk_en);
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0)
+ snd_soc_component_update_bits(comp, dmic_clk_reg,
+ dmic_clk_en, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_find_amic_input(struct snd_soc_component *comp,
+ int adc_mux_n)
+{
+ u16 mask, shift, adc_mux_in_reg;
+ u16 amic_mux_sel_reg;
+ bool is_amic;
+
+ if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX ||
+ adc_mux_n == WCD934X_INVALID_ADC_MUX)
+ return 0;
+
+ if (adc_mux_n < 3) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ adc_mux_n;
+ mask = 0x03;
+ shift = 0;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ 2 * adc_mux_n;
+ } else if (adc_mux_n < 4) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+ mask = 0x03;
+ shift = 0;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ 2 * adc_mux_n;
+ } else if (adc_mux_n < 7) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ (adc_mux_n - 4);
+ mask = 0x0C;
+ shift = 2;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else if (adc_mux_n < 8) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+ mask = 0x0C;
+ shift = 2;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else if (adc_mux_n < 12) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ ((adc_mux_n == 8) ? (adc_mux_n - 8) :
+ (adc_mux_n - 9));
+ mask = 0x30;
+ shift = 4;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else if (adc_mux_n < 13) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+ mask = 0x30;
+ shift = 4;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1;
+ mask = 0xC0;
+ shift = 6;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ }
+
+ is_amic = (((snd_soc_component_read(comp, adc_mux_in_reg)
+ & mask) >> shift) == 1);
+ if (!is_amic)
+ return 0;
+
+ return snd_soc_component_read(comp, amic_mux_sel_reg) & 0x07;
+}
+
+static u16 wcd934x_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp,
+ int amic)
+{
+ u16 pwr_level_reg = 0;
+
+ switch (amic) {
+ case 1:
+ case 2:
+ pwr_level_reg = WCD934X_ANA_AMIC1;
+ break;
+
+ case 3:
+ case 4:
+ pwr_level_reg = WCD934X_ANA_AMIC3;
+ break;
+ default:
+ break;
+ }
+
+ return pwr_level_reg;
+}
+
+static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator;
+ char *dec_adc_mux_name = NULL;
+ char *widget_name;
+ int ret = 0, amic_n;
+ u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ char *dec;
+ u8 hpf_coff_freq;
+
+ char *wname __free(kfree) = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!wname)
+ return -ENOMEM;
+
+ widget_name = wname;
+ dec_adc_mux_name = strsep(&widget_name, " ");
+ if (!dec_adc_mux_name) {
+ dev_err(comp->dev, "%s: Invalid decimator = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ dec_adc_mux_name = widget_name;
+
+ dec = strpbrk(dec_adc_mux_name, "012345678");
+ if (!dec) {
+ dev_err(comp->dev, "%s: decimator index not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(dec, 10, &decimator);
+ if (ret < 0) {
+ dev_err(comp->dev, "%s: Invalid decimator = %s\n",
+ __func__, wname);
+ return -EINVAL;
+ }
+
+ tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator;
+ hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator;
+ dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator;
+ tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ amic_n = wcd934x_codec_find_amic_input(comp, decimator);
+ if (amic_n)
+ pwr_level_reg = wcd934x_codec_get_amic_pwlvl_reg(comp,
+ amic_n);
+
+ if (!pwr_level_reg)
+ break;
+
+ switch ((snd_soc_component_read(comp, pwr_level_reg) &
+ WCD934X_AMIC_PWR_LVL_MASK) >>
+ WCD934X_AMIC_PWR_LVL_SHIFT) {
+ case WCD934X_AMIC_PWR_LEVEL_LP:
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_LP);
+ break;
+ case WCD934X_AMIC_PWR_LEVEL_HP:
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_HP);
+ break;
+ case WCD934X_AMIC_PWR_LEVEL_DEFAULT:
+ case WCD934X_AMIC_PWR_LEVEL_HYBRID:
+ default:
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_DF);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+ if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK,
+ WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ);
+ /*
+ * Minimum 1 clk cycle delay is required as per
+ * HW spec.
+ */
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK,
+ 0);
+ }
+ /* apply gain after decimator is enabled */
+ snd_soc_component_write(comp, tx_gain_ctl_reg,
+ snd_soc_component_read(comp,
+ tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+ if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_coff_freq << 5);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK,
+ WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ);
+ /*
+ * Minimum 1 clk cycle delay is required as per
+ * HW spec.
+ */
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK,
+ 0);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ 0x10, 0x00);
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_DF);
+ break;
+ }
+
+ return ret;
+}
+
+static void wcd934x_codec_set_tx_hold(struct snd_soc_component *comp,
+ u16 amic_reg, bool set)
+{
+ u8 mask = 0x20;
+ u8 val;
+
+ if (amic_reg == WCD934X_ANA_AMIC1 ||
+ amic_reg == WCD934X_ANA_AMIC3)
+ mask = 0x40;
+
+ val = set ? mask : 0x00;
+
+ switch (amic_reg) {
+ case WCD934X_ANA_AMIC1:
+ case WCD934X_ANA_AMIC2:
+ snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC2,
+ mask, val);
+ break;
+ case WCD934X_ANA_AMIC3:
+ case WCD934X_ANA_AMIC4:
+ snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC4,
+ mask, val);
+ break;
+ default:
+ break;
+ }
+}
+
+static int wcd934x_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd934x_codec_set_tx_hold(comp, w->reg, true);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd934x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd934x_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd934x_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wcd934x_dapm_widgets[] = {
+ /* Analog Outputs */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+ SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+ SND_SOC_DAPM_OUTPUT("ANC EAR"),
+ SND_SOC_DAPM_OUTPUT("ANC HPHL"),
+ SND_SOC_DAPM_OUTPUT("ANC HPHR"),
+ SND_SOC_DAPM_OUTPUT("WDMA3_OUT"),
+ SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"),
+ SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"),
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
+ AIF4_PB, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0,
+ &slim_rx_mux[WCD934X_RX0]),
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0,
+ &slim_rx_mux[WCD934X_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0,
+ &slim_rx_mux[WCD934X_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0,
+ &slim_rx_mux[WCD934X_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0,
+ &slim_rx_mux[WCD934X_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0,
+ &slim_rx_mux[WCD934X_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0,
+ &slim_rx_mux[WCD934X_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0,
+ &slim_rx_mux[WCD934X_RX7]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0,
+ &rx_int0_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int1_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int2_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0,
+ &rx_int3_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0,
+ &rx_int4_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0,
+ &rx_int7_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0,
+ &rx_int8_2_mux, wcd934x_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp2_mux),
+ SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int1_asrc_switch,
+ ARRAY_SIZE(rx_int1_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int2_asrc_switch,
+ ARRAY_SIZE(rx_int2_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int3_asrc_switch,
+ ARRAY_SIZE(rx_int3_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int4_asrc_switch,
+ ARRAY_SIZE(rx_int4_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", WCD934X_CDC_RX0_RX_PATH_CFG0, 4,
+ 0, &rx_int0_mix2_inp_mux, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", WCD934X_CDC_RX1_RX_PATH_CFG0, 4,
+ 0, &rx_int1_mix2_inp_mux, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", WCD934X_CDC_RX2_RX_PATH_CFG0, 4,
+ 0, &rx_int2_mix2_inp_mux, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", WCD934X_CDC_RX3_RX_PATH_CFG0, 4,
+ 0, &rx_int3_mix2_inp_mux, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", WCD934X_CDC_RX4_RX_PATH_CFG0, 4,
+ 0, &rx_int4_mix2_inp_mux, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", WCD934X_CDC_RX7_RX_PATH_CFG0, 4,
+ 0, &rx_int7_mix2_inp_mux, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+ SND_SOC_DAPM_PGA_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+ 0, 0, NULL, 0, wcd934x_codec_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+ 1, 0, NULL, 0, wcd934x_codec_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int0_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int2_dem_inp_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0,
+ &rx_int0_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int1_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int2_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0,
+ &rx_int3_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0,
+ &rx_int4_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0,
+ &rx_int7_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0,
+ &rx_int8_1_interp_mux,
+ wcd934x_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int0_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int1_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int2_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT3_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int3_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT4_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int4_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT7_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int7_2_interp_mux),
+ SND_SOC_DAPM_MUX("RX INT8_2 INTERP", SND_SOC_NOPM, 0, 0,
+ &rx_int8_2_interp_mux),
+ SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd934x_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD934X_ANA_HPH,
+ 5, 0, wcd934x_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD934X_ANA_HPH,
+ 4, 0, wcd934x_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd934x_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, wcd934x_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0,
+ wcd934x_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PA", WCD934X_ANA_HPH, 6, 0, NULL, 0,
+ wcd934x_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", WCD934X_ANA_RX_SUPPLIES, 0, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SBOOST0", WCD934X_CDC_RX7_RX_PATH_CFG1,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SBOOST0_CLK", WCD934X_CDC_BOOST0_BOOST_PATH_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SBOOST1", WCD934X_CDC_RX8_RX_PATH_CFG1,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SBOOST1_CLK", WCD934X_CDC_BOOST1_BOOST_PATH_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("INT0_CLK", SND_SOC_NOPM, INTERP_EAR, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT1_CLK", SND_SOC_NOPM, INTERP_HPHL, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT2_CLK", SND_SOC_NOPM, INTERP_HPHR, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT3_CLK", SND_SOC_NOPM, INTERP_LO1, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT4_CLK", SND_SOC_NOPM, INTERP_LO2, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT7_CLK", SND_SOC_NOPM, INTERP_SPKR1, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT8_CLK", SND_SOC_NOPM, INTERP_SPKR2, 0,
+ wcd934x_codec_enable_interp_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DSMDEM0_CLK", WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSMDEM1_CLK", WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSMDEM2_CLK", WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSMDEM3_CLK", WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSMDEM4_CLK", WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSMDEM7_CLK", WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSMDEM8_CLK", WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_mclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC4"),
+ SND_SOC_DAPM_INPUT("AMIC5"),
+ SND_SOC_DAPM_INPUT("DMIC0 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC3 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC4 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC5 Pin"),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, wcd934x_codec_enable_slim,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+ wcd934x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_dmic_mux0),
+ SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_dmic_mux1),
+ SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_dmic_mux2),
+ SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_dmic_mux3),
+ SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_dmic_mux4),
+ SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_dmic_mux5),
+ SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_dmic_mux6),
+ SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_dmic_mux7),
+ SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_dmic_mux8),
+ SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_amic_mux0),
+ SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_amic_mux1),
+ SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_amic_mux2),
+ SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_amic_mux3),
+ SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_amic_mux4),
+ SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_amic_mux5),
+ SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_amic_mux6),
+ SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_amic_mux7),
+ SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_amic_mux8),
+ SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux0_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux1_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux2_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux3_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux4_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux5_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux6_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux7_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux8_mux, wcd934x_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0,
+ wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0,
+ wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0,
+ wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0,
+ wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("AMIC4_5 SEL", SND_SOC_NOPM, 0, 0, &tx_amic4_5),
+ SND_SOC_DAPM_MUX("CDC_IF TX0 MUX", SND_SOC_NOPM, WCD934X_TX0, 0,
+ &cdc_if_tx0_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX1 MUX", SND_SOC_NOPM, WCD934X_TX1, 0,
+ &cdc_if_tx1_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX2 MUX", SND_SOC_NOPM, WCD934X_TX2, 0,
+ &cdc_if_tx2_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX3 MUX", SND_SOC_NOPM, WCD934X_TX3, 0,
+ &cdc_if_tx3_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX4 MUX", SND_SOC_NOPM, WCD934X_TX4, 0,
+ &cdc_if_tx4_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX5 MUX", SND_SOC_NOPM, WCD934X_TX5, 0,
+ &cdc_if_tx5_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX6 MUX", SND_SOC_NOPM, WCD934X_TX6, 0,
+ &cdc_if_tx6_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX7 MUX", SND_SOC_NOPM, WCD934X_TX7, 0,
+ &cdc_if_tx7_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX8 MUX", SND_SOC_NOPM, WCD934X_TX8, 0,
+ &cdc_if_tx8_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX9 MUX", SND_SOC_NOPM, WCD934X_TX9, 0,
+ &cdc_if_tx9_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX10 MUX", SND_SOC_NOPM, WCD934X_TX10, 0,
+ &cdc_if_tx10_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX11 MUX", SND_SOC_NOPM, WCD934X_TX11, 0,
+ &cdc_if_tx11_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX11 INP1 MUX", SND_SOC_NOPM, WCD934X_TX11, 0,
+ &cdc_if_tx11_inp1_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX13 MUX", SND_SOC_NOPM, WCD934X_TX13, 0,
+ &cdc_if_tx13_mux),
+ SND_SOC_DAPM_MUX("CDC_IF TX13 INP1 MUX", SND_SOC_NOPM, WCD934X_TX13, 0,
+ &cdc_if_tx13_inp1_mux),
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif1_slim_cap_mixer,
+ ARRAY_SIZE(aif1_slim_cap_mixer)),
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif2_slim_cap_mixer,
+ ARRAY_SIZE(aif2_slim_cap_mixer)),
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif3_slim_cap_mixer,
+ ARRAY_SIZE(aif3_slim_cap_mixer)),
+};
+
+static const struct snd_soc_dapm_route wcd934x_audio_map[] = {
+ /* RX0-RX7 */
+ WCD934X_SLIM_RX_AIF_PATH(0),
+ WCD934X_SLIM_RX_AIF_PATH(1),
+ WCD934X_SLIM_RX_AIF_PATH(2),
+ WCD934X_SLIM_RX_AIF_PATH(3),
+ WCD934X_SLIM_RX_AIF_PATH(4),
+ WCD934X_SLIM_RX_AIF_PATH(5),
+ WCD934X_SLIM_RX_AIF_PATH(6),
+ WCD934X_SLIM_RX_AIF_PATH(7),
+
+ /* RX0 Ear out */
+ WCD934X_INTERPOLATOR_PATH(0),
+ WCD934X_INTERPOLATOR_MIX2(0),
+ {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"},
+ {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"},
+ {"RX INT0 DAC", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "RX INT0 DAC"},
+ {"EAR", NULL, "EAR PA"},
+
+ /* RX1 Headphone left */
+ WCD934X_INTERPOLATOR_PATH(1),
+ WCD934X_INTERPOLATOR_MIX2(1),
+ {"RX INT1 MIX3", NULL, "RX INT1 MIX2"},
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"},
+ {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
+ {"RX INT1 DAC", NULL, "RX_BIAS"},
+ {"HPHL PA", NULL, "RX INT1 DAC"},
+ {"HPHL", NULL, "HPHL PA"},
+
+ /* RX2 Headphone right */
+ WCD934X_INTERPOLATOR_PATH(2),
+ WCD934X_INTERPOLATOR_MIX2(2),
+ {"RX INT2 MIX3", NULL, "RX INT2 MIX2"},
+ {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"},
+ {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
+ {"RX INT2 DAC", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "RX INT2 DAC"},
+ {"HPHR", NULL, "HPHR PA"},
+
+ /* RX3 HIFi LineOut1 */
+ WCD934X_INTERPOLATOR_PATH(3),
+ WCD934X_INTERPOLATOR_MIX2(3),
+ {"RX INT3 MIX3", NULL, "RX INT3 MIX2"},
+ {"RX INT3 DAC", NULL, "RX INT3 MIX3"},
+ {"RX INT3 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT1 PA", NULL, "RX INT3 DAC"},
+ {"LINEOUT1", NULL, "LINEOUT1 PA"},
+
+ /* RX4 HIFi LineOut2 */
+ WCD934X_INTERPOLATOR_PATH(4),
+ WCD934X_INTERPOLATOR_MIX2(4),
+ {"RX INT4 MIX3", NULL, "RX INT4 MIX2"},
+ {"RX INT4 DAC", NULL, "RX INT4 MIX3"},
+ {"RX INT4 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT2 PA", NULL, "RX INT4 DAC"},
+ {"LINEOUT2", NULL, "LINEOUT2 PA"},
+
+ /* RX7 Speaker Left Out PA */
+ WCD934X_INTERPOLATOR_PATH(7),
+ WCD934X_INTERPOLATOR_MIX2(7),
+ {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"},
+ {"RX INT7 CHAIN", NULL, "RX_BIAS"},
+ {"RX INT7 CHAIN", NULL, "SBOOST0"},
+ {"RX INT7 CHAIN", NULL, "SBOOST0_CLK"},
+ {"SPK1 OUT", NULL, "RX INT7 CHAIN"},
+
+ /* RX8 Speaker Right Out PA */
+ WCD934X_INTERPOLATOR_PATH(8),
+ {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"},
+ {"RX INT8 CHAIN", NULL, "RX_BIAS"},
+ {"RX INT8 CHAIN", NULL, "SBOOST1"},
+ {"RX INT8 CHAIN", NULL, "SBOOST1_CLK"},
+ {"SPK2 OUT", NULL, "RX INT8 CHAIN"},
+
+ /* Tx */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+
+ WCD934X_SLIM_TX_AIF_PATH(0),
+ WCD934X_SLIM_TX_AIF_PATH(1),
+ WCD934X_SLIM_TX_AIF_PATH(2),
+ WCD934X_SLIM_TX_AIF_PATH(3),
+ WCD934X_SLIM_TX_AIF_PATH(4),
+ WCD934X_SLIM_TX_AIF_PATH(5),
+ WCD934X_SLIM_TX_AIF_PATH(6),
+ WCD934X_SLIM_TX_AIF_PATH(7),
+ WCD934X_SLIM_TX_AIF_PATH(8),
+
+ WCD934X_ADC_MUX(0),
+ WCD934X_ADC_MUX(1),
+ WCD934X_ADC_MUX(2),
+ WCD934X_ADC_MUX(3),
+ WCD934X_ADC_MUX(4),
+ WCD934X_ADC_MUX(5),
+ WCD934X_ADC_MUX(6),
+ WCD934X_ADC_MUX(7),
+ WCD934X_ADC_MUX(8),
+
+ {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"},
+ {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"},
+ {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"},
+ {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"},
+ {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"},
+ {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"},
+ {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"},
+ {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"},
+ {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"},
+
+ {"AMIC4_5 SEL", "AMIC4", "AMIC4"},
+ {"AMIC4_5 SEL", "AMIC5", "AMIC5"},
+
+ { "DMIC0", NULL, "DMIC0 Pin" },
+ { "DMIC1", NULL, "DMIC1 Pin" },
+ { "DMIC2", NULL, "DMIC2 Pin" },
+ { "DMIC3", NULL, "DMIC3 Pin" },
+ { "DMIC4", NULL, "DMIC4 Pin" },
+ { "DMIC5", NULL, "DMIC5 Pin" },
+
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2", NULL, "AMIC2"},
+ {"ADC3", NULL, "AMIC3"},
+ {"ADC4", NULL, "AMIC4_5 SEL"},
+
+ WCD934X_IIR_INP_MUX(0),
+ WCD934X_IIR_INP_MUX(1),
+
+ {"SRC0", NULL, "IIR0"},
+ {"SRC1", NULL, "IIR1"},
+};
+
+static int wcd934x_codec_set_jack(struct snd_soc_component *comp,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (!wcd->mbhc)
+ return -ENOTSUPP;
+
+ if (jack && !wcd->mbhc_started) {
+ ret = wcd_mbhc_start(wcd->mbhc, &wcd->mbhc_cfg, jack);
+ wcd->mbhc_started = true;
+ } else if (wcd->mbhc_started) {
+ wcd_mbhc_stop(wcd->mbhc);
+ wcd->mbhc_started = false;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver wcd934x_component_drv = {
+ .probe = wcd934x_comp_probe,
+ .remove = wcd934x_comp_remove,
+ .set_sysclk = wcd934x_comp_set_sysclk,
+ .controls = wcd934x_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd934x_snd_controls),
+ .dapm_widgets = wcd934x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wcd934x_dapm_widgets),
+ .dapm_routes = wcd934x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map),
+ .set_jack = wcd934x_codec_set_jack,
+ .endianness = 1,
+};
+
+static void wcd934x_put_device_action(void *data)
+{
+ struct device *dev = data;
+
+ put_device(dev);
+}
+
+static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
+{
+ struct device *dev = &wcd->sdev->dev;
+ struct wcd_mbhc_config *cfg = &wcd->mbhc_cfg;
+ struct device_node *ifc_dev_np;
+
+ ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0);
+ if (!ifc_dev_np)
+ return dev_err_probe(dev, -EINVAL, "No Interface device found\n");
+
+ wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np);
+ of_node_put(ifc_dev_np);
+ if (!wcd->sidev)
+ return dev_err_probe(dev, -EINVAL, "Unable to get SLIM Interface device\n");
+
+ slim_get_logical_addr(wcd->sidev);
+ wcd->if_regmap = devm_regmap_init_slimbus(wcd->sidev,
+ &wcd934x_ifc_regmap_config);
+ if (IS_ERR(wcd->if_regmap)) {
+ put_device(&wcd->sidev->dev);
+ return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
+ "Failed to allocate ifc register map\n");
+ }
+
+ of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate",
+ &wcd->dmic_sample_rate);
+
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = WCD934X_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = wcd->common.micb_mv[1];
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, cfg);
+
+ return 0;
+}
+
+static int wcd934x_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd934x_ddata *data = dev_get_drvdata(dev->parent);
+ struct wcd934x_codec *wcd;
+ int ret, irq;
+
+ wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
+ if (!wcd)
+ return -ENOMEM;
+
+ wcd->dev = dev;
+ wcd->regmap = data->regmap;
+ wcd->extclk = data->extclk;
+ wcd->sdev = to_slim_device(data->dev);
+ mutex_init(&wcd->sysclk_mutex);
+ mutex_init(&wcd->micb_lock);
+ wcd->common.dev = dev->parent;
+ wcd->common.max_bias = 4;
+
+ ret = wcd934x_codec_parse_data(wcd);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, wcd934x_put_device_action, &wcd->sidev->dev);
+ if (ret)
+ return ret;
+
+ /* set default rate 9P6MHz */
+ regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ);
+ memcpy(wcd->rx_chs, wcd934x_rx_chs, sizeof(wcd934x_rx_chs));
+ memcpy(wcd->tx_chs, wcd934x_tx_chs, sizeof(wcd934x_tx_chs));
+
+ irq = regmap_irq_get_virq(data->irq_data, WCD934X_IRQ_SLIMBUS);
+ if (irq < 0)
+ return dev_err_probe(wcd->dev, irq, "Failed to get SLIM IRQ\n");
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ wcd934x_slim_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "slim", wcd);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request slimbus irq\n");
+
+ wcd934x_register_mclk_output(wcd);
+ platform_set_drvdata(pdev, wcd);
+
+ return devm_snd_soc_register_component(dev, &wcd934x_component_drv,
+ wcd934x_slim_dais,
+ ARRAY_SIZE(wcd934x_slim_dais));
+}
+
+static const struct platform_device_id wcd934x_driver_id[] = {
+ {
+ .name = "wcd934x-codec",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, wcd934x_driver_id);
+
+static struct platform_driver wcd934x_codec_driver = {
+ .probe = &wcd934x_codec_probe,
+ .id_table = wcd934x_driver_id,
+ .driver = {
+ .name = "wcd934x-codec",
+ }
+};
+
+module_platform_driver(wcd934x_codec_driver);
+MODULE_DESCRIPTION("WCD934x codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c
new file mode 100644
index 000000000000..1878d67e3fa1
--- /dev/null
+++ b/sound/soc/codecs/wcd937x-sdw.c
@@ -0,0 +1,1115 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include "wcd937x.h"
+
+static struct wcd_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)),
+ WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_COMP_L, WCD937X_COMP_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_COMP_R, WCD937X_COMP_PORT, BIT(1)),
+ WCD_SDW_CH(WCD937X_LO, WCD937X_LO_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_DSD_L, WCD937X_DSD_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)),
+};
+
+static struct wcd_sdw_ch_info wcd937x_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_DMIC0, WCD937X_DMIC_0_3_MBHC_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_DMIC1, WCD937X_DMIC_0_3_MBHC_PORT, BIT(1)),
+ WCD_SDW_CH(WCD937X_MBHC, WCD937X_DMIC_0_3_MBHC_PORT, BIT(2)),
+ WCD_SDW_CH(WCD937X_DMIC2, WCD937X_DMIC_0_3_MBHC_PORT, BIT(2)),
+ WCD_SDW_CH(WCD937X_DMIC3, WCD937X_DMIC_0_3_MBHC_PORT, BIT(3)),
+ WCD_SDW_CH(WCD937X_DMIC4, WCD937X_DMIC_4_6_PORT, BIT(0)),
+ WCD_SDW_CH(WCD937X_DMIC5, WCD937X_DMIC_4_6_PORT, BIT(1)),
+ WCD_SDW_CH(WCD937X_DMIC6, WCD937X_DMIC_4_6_PORT, BIT(2)),
+};
+
+static struct sdw_dpn_prop wcd937x_dpn_prop[WCD937X_MAX_SWR_PORTS] = {
+ {
+ .num = 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 8,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 2,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 3,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 4,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 5,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ wcd->sconfig.ch_count = 1;
+ wcd->active_ports = 0;
+ for (i = 0; i < WCD937X_MAX_SWR_PORTS; i++) {
+ ch_mask = wcd->port_config[i].ch_mask;
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ wcd->sconfig.ch_count++;
+
+ port_config[wcd->active_ports] = wcd->port_config[i];
+ wcd->active_ports++;
+ }
+
+ wcd->sconfig.bps = 1;
+ wcd->sconfig.frame_rate = params_rate(params);
+ wcd->sconfig.direction = wcd->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
+ wcd->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig,
+ &port_config[0], wcd->active_ports,
+ wcd->sruntime);
+}
+EXPORT_SYMBOL_GPL(wcd937x_sdw_hw_params);
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering
+ * the first irq of the slave_irq irq domain, which then will
+ * be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int wcd9370_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct wcd937x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
+
+ return wcd_interrupt_callback(slave, wcd->slave_irq, WCD937X_DIGITAL_INTR_STATUS_0,
+ WCD937X_DIGITAL_INTR_STATUS_1, WCD937X_DIGITAL_INTR_STATUS_2);
+}
+
+static const struct reg_default wcd937x_defaults[] = {
+ /* Default values except for Read-Only & Volatile registers */
+ { WCD937X_ANA_BIAS, 0x00 },
+ { WCD937X_ANA_RX_SUPPLIES, 0x00 },
+ { WCD937X_ANA_HPH, 0x0c },
+ { WCD937X_ANA_EAR, 0x00 },
+ { WCD937X_ANA_EAR_COMPANDER_CTL, 0x02 },
+ { WCD937X_ANA_TX_CH1, 0x20 },
+ { WCD937X_ANA_TX_CH2, 0x00 },
+ { WCD937X_ANA_TX_CH3, 0x20 },
+ { WCD937X_ANA_TX_CH3_HPF, 0x00 },
+ { WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 },
+ { WCD937X_ANA_MICB3_DSP_EN_LOGIC, 0x00 },
+ { WCD937X_ANA_MBHC_MECH, 0x39 },
+ { WCD937X_ANA_MBHC_ELECT, 0x08 },
+ { WCD937X_ANA_MBHC_ZDET, 0x00 },
+ { WCD937X_ANA_MBHC_BTN0, 0x00 },
+ { WCD937X_ANA_MBHC_BTN1, 0x10 },
+ { WCD937X_ANA_MBHC_BTN2, 0x20 },
+ { WCD937X_ANA_MBHC_BTN3, 0x30 },
+ { WCD937X_ANA_MBHC_BTN4, 0x40 },
+ { WCD937X_ANA_MBHC_BTN5, 0x50 },
+ { WCD937X_ANA_MBHC_BTN6, 0x60 },
+ { WCD937X_ANA_MBHC_BTN7, 0x70 },
+ { WCD937X_ANA_MICB1, 0x10 },
+ { WCD937X_ANA_MICB2, 0x10 },
+ { WCD937X_ANA_MICB2_RAMP, 0x00 },
+ { WCD937X_ANA_MICB3, 0x10 },
+ { WCD937X_BIAS_CTL, 0x2a },
+ { WCD937X_BIAS_VBG_FINE_ADJ, 0x55 },
+ { WCD937X_LDOL_VDDCX_ADJUST, 0x01 },
+ { WCD937X_LDOL_DISABLE_LDOL, 0x00 },
+ { WCD937X_MBHC_CTL_CLK, 0x00 },
+ { WCD937X_MBHC_CTL_ANA, 0x00 },
+ { WCD937X_MBHC_CTL_SPARE_1, 0x00 },
+ { WCD937X_MBHC_CTL_SPARE_2, 0x00 },
+ { WCD937X_MBHC_CTL_BCS, 0x00 },
+ { WCD937X_MBHC_TEST_CTL, 0x00 },
+ { WCD937X_LDOH_MODE, 0x2b },
+ { WCD937X_LDOH_BIAS, 0x68 },
+ { WCD937X_LDOH_STB_LOADS, 0x00 },
+ { WCD937X_LDOH_SLOWRAMP, 0x50 },
+ { WCD937X_MICB1_TEST_CTL_1, 0x1a },
+ { WCD937X_MICB1_TEST_CTL_2, 0x18 },
+ { WCD937X_MICB1_TEST_CTL_3, 0xa4 },
+ { WCD937X_MICB2_TEST_CTL_1, 0x1a },
+ { WCD937X_MICB2_TEST_CTL_2, 0x18 },
+ { WCD937X_MICB2_TEST_CTL_3, 0xa4 },
+ { WCD937X_MICB3_TEST_CTL_1, 0x1a },
+ { WCD937X_MICB3_TEST_CTL_2, 0x18 },
+ { WCD937X_MICB3_TEST_CTL_3, 0xa4 },
+ { WCD937X_TX_COM_ADC_VCM, 0x39 },
+ { WCD937X_TX_COM_BIAS_ATEST, 0xc0 },
+ { WCD937X_TX_COM_ADC_INT1_IB, 0x6f },
+ { WCD937X_TX_COM_ADC_INT2_IB, 0x4f },
+ { WCD937X_TX_COM_TXFE_DIV_CTL, 0x2e },
+ { WCD937X_TX_COM_TXFE_DIV_START, 0x00 },
+ { WCD937X_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 },
+ { WCD937X_TX_COM_TXFE_DIV_STOP_12P288M, 0xff },
+ { WCD937X_TX_1_2_TEST_EN, 0xcc },
+ { WCD937X_TX_1_2_ADC_IB, 0x09 },
+ { WCD937X_TX_1_2_ATEST_REFCTL, 0x0a },
+ { WCD937X_TX_1_2_TEST_CTL, 0x38 },
+ { WCD937X_TX_1_2_TEST_BLK_EN, 0xff },
+ { WCD937X_TX_1_2_TXFE_CLKDIV, 0x00 },
+ { WCD937X_TX_3_TEST_EN, 0xcc },
+ { WCD937X_TX_3_ADC_IB, 0x09 },
+ { WCD937X_TX_3_ATEST_REFCTL, 0x0a },
+ { WCD937X_TX_3_TEST_CTL, 0x38 },
+ { WCD937X_TX_3_TEST_BLK_EN, 0xff },
+ { WCD937X_TX_3_TXFE_CLKDIV, 0x00 },
+ { WCD937X_TX_3_SPARE_MONO, 0x00 },
+ { WCD937X_CLASSH_MODE_1, 0x40 },
+ { WCD937X_CLASSH_MODE_2, 0x3a },
+ { WCD937X_CLASSH_MODE_3, 0x00 },
+ { WCD937X_CLASSH_CTRL_VCL_1, 0x70 },
+ { WCD937X_CLASSH_CTRL_VCL_2, 0x82 },
+ { WCD937X_CLASSH_CTRL_CCL_1, 0x31 },
+ { WCD937X_CLASSH_CTRL_CCL_2, 0x80 },
+ { WCD937X_CLASSH_CTRL_CCL_3, 0x80 },
+ { WCD937X_CLASSH_CTRL_CCL_4, 0x51 },
+ { WCD937X_CLASSH_CTRL_CCL_5, 0x00 },
+ { WCD937X_CLASSH_BUCK_TMUX_A_D, 0x00 },
+ { WCD937X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 },
+ { WCD937X_CLASSH_SPARE, 0x00 },
+ { WCD937X_FLYBACK_EN, 0x4e },
+ { WCD937X_FLYBACK_VNEG_CTRL_1, 0x0b },
+ { WCD937X_FLYBACK_VNEG_CTRL_2, 0x45 },
+ { WCD937X_FLYBACK_VNEG_CTRL_3, 0x74 },
+ { WCD937X_FLYBACK_VNEG_CTRL_4, 0x7f },
+ { WCD937X_FLYBACK_VNEG_CTRL_5, 0x83 },
+ { WCD937X_FLYBACK_VNEG_CTRL_6, 0x98 },
+ { WCD937X_FLYBACK_VNEG_CTRL_7, 0xa9 },
+ { WCD937X_FLYBACK_VNEG_CTRL_8, 0x68 },
+ { WCD937X_FLYBACK_VNEG_CTRL_9, 0x64 },
+ { WCD937X_FLYBACK_VNEGDAC_CTRL_1, 0xed },
+ { WCD937X_FLYBACK_VNEGDAC_CTRL_2, 0xf0 },
+ { WCD937X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 },
+ { WCD937X_FLYBACK_CTRL_1, 0x65 },
+ { WCD937X_FLYBACK_TEST_CTL, 0x00 },
+ { WCD937X_RX_AUX_SW_CTL, 0x00 },
+ { WCD937X_RX_PA_AUX_IN_CONN, 0x00 },
+ { WCD937X_RX_TIMER_DIV, 0x32 },
+ { WCD937X_RX_OCP_CTL, 0x1f },
+ { WCD937X_RX_OCP_COUNT, 0x77 },
+ { WCD937X_RX_BIAS_EAR_DAC, 0xa0 },
+ { WCD937X_RX_BIAS_EAR_AMP, 0xaa },
+ { WCD937X_RX_BIAS_HPH_LDO, 0xa9 },
+ { WCD937X_RX_BIAS_HPH_PA, 0xaa },
+ { WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a },
+ { WCD937X_RX_BIAS_HPH_RDAC_LDO, 0x88 },
+ { WCD937X_RX_BIAS_HPH_CNP1, 0x82 },
+ { WCD937X_RX_BIAS_HPH_LOWPOWER, 0x82 },
+ { WCD937X_RX_BIAS_AUX_DAC, 0xa0 },
+ { WCD937X_RX_BIAS_AUX_AMP, 0xaa },
+ { WCD937X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 },
+ { WCD937X_RX_BIAS_MISC, 0x00 },
+ { WCD937X_RX_BIAS_BUCK_RST, 0x08 },
+ { WCD937X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 },
+ { WCD937X_RX_BIAS_FLYB_ERRAMP, 0x40 },
+ { WCD937X_RX_BIAS_FLYB_BUFF, 0xaa },
+ { WCD937X_RX_BIAS_FLYB_MID_RST, 0x14 },
+ { WCD937X_HPH_CNP_EN, 0x80 },
+ { WCD937X_HPH_CNP_WG_CTL, 0x9a },
+ { WCD937X_HPH_CNP_WG_TIME, 0x14 },
+ { WCD937X_HPH_OCP_CTL, 0x28 },
+ { WCD937X_HPH_AUTO_CHOP, 0x16 },
+ { WCD937X_HPH_CHOP_CTL, 0x83 },
+ { WCD937X_HPH_PA_CTL1, 0x46 },
+ { WCD937X_HPH_PA_CTL2, 0x50 },
+ { WCD937X_HPH_L_EN, 0x80 },
+ { WCD937X_HPH_L_TEST, 0xe0 },
+ { WCD937X_HPH_L_ATEST, 0x50 },
+ { WCD937X_HPH_R_EN, 0x80 },
+ { WCD937X_HPH_R_TEST, 0xe0 },
+ { WCD937X_HPH_R_ATEST, 0x54 },
+ { WCD937X_HPH_RDAC_CLK_CTL1, 0x99 },
+ { WCD937X_HPH_RDAC_CLK_CTL2, 0x9b },
+ { WCD937X_HPH_RDAC_LDO_CTL, 0x33 },
+ { WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
+ { WCD937X_HPH_REFBUFF_UHQA_CTL, 0xa8 },
+ { WCD937X_HPH_REFBUFF_LP_CTL, 0x0e },
+ { WCD937X_HPH_L_DAC_CTL, 0x20 },
+ { WCD937X_HPH_R_DAC_CTL, 0x20 },
+ { WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL, 0x55 },
+ { WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0x19 },
+ { WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1, 0xa0 },
+ { WCD937X_EAR_EAR_EN_REG, 0x22 },
+ { WCD937X_EAR_EAR_PA_CON, 0x44 },
+ { WCD937X_EAR_EAR_SP_CON, 0xdb },
+ { WCD937X_EAR_EAR_DAC_CON, 0x80 },
+ { WCD937X_EAR_EAR_CNP_FSM_CON, 0xb2 },
+ { WCD937X_EAR_TEST_CTL, 0x00 },
+ { WCD937X_ANA_NEW_PAGE_REGISTER, 0x00 },
+ { WCD937X_HPH_NEW_ANA_HPH2, 0x00 },
+ { WCD937X_HPH_NEW_ANA_HPH3, 0x00 },
+ { WCD937X_SLEEP_CTL, 0x16 },
+ { WCD937X_SLEEP_WATCHDOG_CTL, 0x00 },
+ { WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 },
+ { WCD937X_MBHC_NEW_CTL_1, 0x02 },
+ { WCD937X_MBHC_NEW_CTL_2, 0x05 },
+ { WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 },
+ { WCD937X_MBHC_NEW_ZDET_ANA_CTL, 0x0f },
+ { WCD937X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 },
+ { WCD937X_TX_NEW_TX_CH2_SEL, 0x00 },
+ { WCD937X_AUX_AUXPA, 0x00 },
+ { WCD937X_LDORXTX_MODE, 0x0c },
+ { WCD937X_LDORXTX_CONFIG, 0x10 },
+ { WCD937X_DIE_CRACK_DIE_CRK_DET_EN, 0x00 },
+ { WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 },
+ { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 },
+ { WCD937X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
+ { WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+ { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 },
+ { WCD937X_HPH_NEW_INT_PA_MISC1, 0x22 },
+ { WCD937X_HPH_NEW_INT_PA_MISC2, 0x00 },
+ { WCD937X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+ { WCD937X_HPH_NEW_INT_HPH_TIMER1, 0xfe },
+ { WCD937X_HPH_NEW_INT_HPH_TIMER2, 0x02 },
+ { WCD937X_HPH_NEW_INT_HPH_TIMER3, 0x4e },
+ { WCD937X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
+ { WCD937X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
+ { WCD937X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+ { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 },
+ { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 },
+ { WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 },
+ { WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 },
+ { WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 },
+ { WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 },
+ { WCD937X_MBHC_NEW_INT_SPARE_2, 0x00 },
+ { WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON, 0xa8 },
+ { WCD937X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 },
+ { WCD937X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 },
+ { WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00 },
+ { WCD937X_AUX_INT_EN_REG, 0x00 },
+ { WCD937X_AUX_INT_PA_CTRL, 0x06 },
+ { WCD937X_AUX_INT_SP_CTRL, 0xd2 },
+ { WCD937X_AUX_INT_DAC_CTRL, 0x80 },
+ { WCD937X_AUX_INT_CLK_CTRL, 0x50 },
+ { WCD937X_AUX_INT_TEST_CTRL, 0x00 },
+ { WCD937X_AUX_INT_STATUS_REG, 0x00 },
+ { WCD937X_AUX_INT_MISC, 0x00 },
+ { WCD937X_LDORXTX_INT_BIAS, 0x6e },
+ { WCD937X_LDORXTX_INT_STB_LOADS_DTEST, 0x50 },
+ { WCD937X_LDORXTX_INT_TEST0, 0x1c },
+ { WCD937X_LDORXTX_INT_STARTUP_TIMER, 0xff },
+ { WCD937X_LDORXTX_INT_TEST1, 0x1f },
+ { WCD937X_LDORXTX_INT_STATUS, 0x00 },
+ { WCD937X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a },
+ { WCD937X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a },
+ { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1, 0x02 },
+ { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2, 0x60 },
+ { WCD937X_DIGITAL_PAGE_REGISTER, 0x00 },
+ { WCD937X_DIGITAL_CDC_RST_CTL, 0x03 },
+ { WCD937X_DIGITAL_TOP_CLK_CFG, 0x00 },
+ { WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x00 },
+ { WCD937X_DIGITAL_SWR_RST_EN, 0x00 },
+ { WCD937X_DIGITAL_CDC_PATH_MODE, 0x55 },
+ { WCD937X_DIGITAL_CDC_RX_RST, 0x00 },
+ { WCD937X_DIGITAL_CDC_RX0_CTL, 0xfc },
+ { WCD937X_DIGITAL_CDC_RX1_CTL, 0xfc },
+ { WCD937X_DIGITAL_CDC_RX2_CTL, 0xfc },
+ { WCD937X_DIGITAL_DEM_BYPASS_DATA0, 0x55 },
+ { WCD937X_DIGITAL_DEM_BYPASS_DATA1, 0x55 },
+ { WCD937X_DIGITAL_DEM_BYPASS_DATA2, 0x55 },
+ { WCD937X_DIGITAL_DEM_BYPASS_DATA3, 0x01 },
+ { WCD937X_DIGITAL_CDC_COMP_CTL_0, 0x00 },
+ { WCD937X_DIGITAL_CDC_RX_DELAY_CTL, 0x66 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R1, 0x4b },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R2, 0x26 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R3, 0x32 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x57 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R5, 0x63 },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R6, 0x7c },
+ { WCD937X_DIGITAL_CDC_HPH_DSM_R7, 0x57 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A1_0, 0x00 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A1_1, 0x01 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A2_0, 0x96 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A2_1, 0x09 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A3_0, 0xab },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A3_1, 0x05 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A4_0, 0x1c },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A4_1, 0x02 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A5_0, 0x17 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A5_1, 0x02 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A6_0, 0xaa },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_A7_0, 0xe3 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_C_0, 0x69 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_C_1, 0x54 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_C_2, 0x02 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_C_3, 0x15 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R1, 0xa4 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R2, 0xb5 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R3, 0x86 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R4, 0x85 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R5, 0xaa },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R6, 0xe2 },
+ { WCD937X_DIGITAL_CDC_AUX_DSM_R7, 0x62 },
+ { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 },
+ { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 },
+ { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d },
+ { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e },
+ { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 },
+ { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0, 0x00 },
+ { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1, 0xfc },
+ { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2, 0x01 },
+ { WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_SWR_CLH, 0x00 },
+ { WCD937X_DIGITAL_SWR_CLH_BYP, 0x00 },
+ { WCD937X_DIGITAL_CDC_TX0_CTL, 0x68 },
+ { WCD937X_DIGITAL_CDC_TX1_CTL, 0x68 },
+ { WCD937X_DIGITAL_CDC_TX2_CTL, 0x68 },
+ { WCD937X_DIGITAL_CDC_TX_RST, 0x00 },
+ { WCD937X_DIGITAL_CDC_REQ_CTL, 0x01 },
+ { WCD937X_DIGITAL_CDC_AMIC_CTL, 0x07 },
+ { WCD937X_DIGITAL_CDC_DMIC_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_DMIC1_CTL, 0x01 },
+ { WCD937X_DIGITAL_CDC_DMIC2_CTL, 0x01 },
+ { WCD937X_DIGITAL_CDC_DMIC3_CTL, 0x01 },
+ { WCD937X_DIGITAL_EFUSE_CTL, 0x2b },
+ { WCD937X_DIGITAL_EFUSE_PRG_CTL, 0x00 },
+ { WCD937X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 },
+ { WCD937X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 },
+ { WCD937X_DIGITAL_PDM_WD_CTL0, 0x00 },
+ { WCD937X_DIGITAL_PDM_WD_CTL1, 0x00 },
+ { WCD937X_DIGITAL_PDM_WD_CTL2, 0x00 },
+ { WCD937X_DIGITAL_INTR_MODE, 0x00 },
+ { WCD937X_DIGITAL_INTR_MASK_0, 0xff },
+ { WCD937X_DIGITAL_INTR_MASK_1, 0xff },
+ { WCD937X_DIGITAL_INTR_MASK_2, 0x0f },
+ { WCD937X_DIGITAL_INTR_CLEAR_0, 0x00 },
+ { WCD937X_DIGITAL_INTR_CLEAR_1, 0x00 },
+ { WCD937X_DIGITAL_INTR_CLEAR_2, 0x00 },
+ { WCD937X_DIGITAL_INTR_LEVEL_0, 0x00 },
+ { WCD937X_DIGITAL_INTR_LEVEL_1, 0x00 },
+ { WCD937X_DIGITAL_INTR_LEVEL_2, 0x00 },
+ { WCD937X_DIGITAL_INTR_SET_0, 0x00 },
+ { WCD937X_DIGITAL_INTR_SET_1, 0x00 },
+ { WCD937X_DIGITAL_INTR_SET_2, 0x00 },
+ { WCD937X_DIGITAL_INTR_TEST_0, 0x00 },
+ { WCD937X_DIGITAL_INTR_TEST_1, 0x00 },
+ { WCD937X_DIGITAL_INTR_TEST_2, 0x00 },
+ { WCD937X_DIGITAL_CDC_CONN_RX0_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_CONN_RX1_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_CONN_RX2_CTL, 0x00 },
+ { WCD937X_DIGITAL_CDC_CONN_TX_CTL, 0x00 },
+ { WCD937X_DIGITAL_LOOP_BACK_MODE, 0x00 },
+ { WCD937X_DIGITAL_SWR_DAC_TEST, 0x00 },
+ { WCD937X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 },
+ { WCD937X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 },
+ { WCD937X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 },
+ { WCD937X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 },
+ { WCD937X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 },
+ { WCD937X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 },
+ { WCD937X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 },
+ { WCD937X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 },
+ { WCD937X_DIGITAL_PAD_INP_DIS_0, 0x00 },
+ { WCD937X_DIGITAL_PAD_INP_DIS_1, 0x00 },
+ { WCD937X_DIGITAL_DRIVE_STRENGTH_0, 0x00 },
+ { WCD937X_DIGITAL_DRIVE_STRENGTH_1, 0x00 },
+ { WCD937X_DIGITAL_DRIVE_STRENGTH_2, 0x00 },
+ { WCD937X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f },
+ { WCD937X_DIGITAL_TX_DATA_EDGE_CTL, 0x10 },
+ { WCD937X_DIGITAL_GPIO_MODE, 0x00 },
+ { WCD937X_DIGITAL_PIN_CTL_OE, 0x00 },
+ { WCD937X_DIGITAL_PIN_CTL_DATA_0, 0x00 },
+ { WCD937X_DIGITAL_PIN_CTL_DATA_1, 0x00 },
+ { WCD937X_DIGITAL_DIG_DEBUG_CTL, 0x00 },
+ { WCD937X_DIGITAL_DIG_DEBUG_EN, 0x00 },
+ { WCD937X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 },
+ { WCD937X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 },
+ { WCD937X_DIGITAL_SSP_DBG, 0x00 },
+ { WCD937X_DIGITAL_SPARE_0, 0x00 },
+ { WCD937X_DIGITAL_SPARE_1, 0x00 },
+ { WCD937X_DIGITAL_SPARE_2, 0x00 },
+};
+
+static bool wcd937x_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD937X_ANA_BIAS:
+ case WCD937X_ANA_RX_SUPPLIES:
+ case WCD937X_ANA_HPH:
+ case WCD937X_ANA_EAR:
+ case WCD937X_ANA_EAR_COMPANDER_CTL:
+ case WCD937X_ANA_TX_CH1:
+ case WCD937X_ANA_TX_CH2:
+ case WCD937X_ANA_TX_CH3:
+ case WCD937X_ANA_TX_CH3_HPF:
+ case WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC:
+ case WCD937X_ANA_MICB3_DSP_EN_LOGIC:
+ case WCD937X_ANA_MBHC_MECH:
+ case WCD937X_ANA_MBHC_ELECT:
+ case WCD937X_ANA_MBHC_ZDET:
+ case WCD937X_ANA_MBHC_BTN0:
+ case WCD937X_ANA_MBHC_BTN1:
+ case WCD937X_ANA_MBHC_BTN2:
+ case WCD937X_ANA_MBHC_BTN3:
+ case WCD937X_ANA_MBHC_BTN4:
+ case WCD937X_ANA_MBHC_BTN5:
+ case WCD937X_ANA_MBHC_BTN6:
+ case WCD937X_ANA_MBHC_BTN7:
+ case WCD937X_ANA_MICB1:
+ case WCD937X_ANA_MICB2:
+ case WCD937X_ANA_MICB2_RAMP:
+ case WCD937X_ANA_MICB3:
+ case WCD937X_BIAS_CTL:
+ case WCD937X_BIAS_VBG_FINE_ADJ:
+ case WCD937X_LDOL_VDDCX_ADJUST:
+ case WCD937X_LDOL_DISABLE_LDOL:
+ case WCD937X_MBHC_CTL_CLK:
+ case WCD937X_MBHC_CTL_ANA:
+ case WCD937X_MBHC_CTL_SPARE_1:
+ case WCD937X_MBHC_CTL_SPARE_2:
+ case WCD937X_MBHC_CTL_BCS:
+ case WCD937X_MBHC_TEST_CTL:
+ case WCD937X_LDOH_MODE:
+ case WCD937X_LDOH_BIAS:
+ case WCD937X_LDOH_STB_LOADS:
+ case WCD937X_LDOH_SLOWRAMP:
+ case WCD937X_MICB1_TEST_CTL_1:
+ case WCD937X_MICB1_TEST_CTL_2:
+ case WCD937X_MICB1_TEST_CTL_3:
+ case WCD937X_MICB2_TEST_CTL_1:
+ case WCD937X_MICB2_TEST_CTL_2:
+ case WCD937X_MICB2_TEST_CTL_3:
+ case WCD937X_MICB3_TEST_CTL_1:
+ case WCD937X_MICB3_TEST_CTL_2:
+ case WCD937X_MICB3_TEST_CTL_3:
+ case WCD937X_TX_COM_ADC_VCM:
+ case WCD937X_TX_COM_BIAS_ATEST:
+ case WCD937X_TX_COM_ADC_INT1_IB:
+ case WCD937X_TX_COM_ADC_INT2_IB:
+ case WCD937X_TX_COM_TXFE_DIV_CTL:
+ case WCD937X_TX_COM_TXFE_DIV_START:
+ case WCD937X_TX_COM_TXFE_DIV_STOP_9P6M:
+ case WCD937X_TX_COM_TXFE_DIV_STOP_12P288M:
+ case WCD937X_TX_1_2_TEST_EN:
+ case WCD937X_TX_1_2_ADC_IB:
+ case WCD937X_TX_1_2_ATEST_REFCTL:
+ case WCD937X_TX_1_2_TEST_CTL:
+ case WCD937X_TX_1_2_TEST_BLK_EN:
+ case WCD937X_TX_1_2_TXFE_CLKDIV:
+ case WCD937X_TX_3_TEST_EN:
+ case WCD937X_TX_3_ADC_IB:
+ case WCD937X_TX_3_ATEST_REFCTL:
+ case WCD937X_TX_3_TEST_CTL:
+ case WCD937X_TX_3_TEST_BLK_EN:
+ case WCD937X_TX_3_TXFE_CLKDIV:
+ case WCD937X_CLASSH_MODE_1:
+ case WCD937X_CLASSH_MODE_2:
+ case WCD937X_CLASSH_MODE_3:
+ case WCD937X_CLASSH_CTRL_VCL_1:
+ case WCD937X_CLASSH_CTRL_VCL_2:
+ case WCD937X_CLASSH_CTRL_CCL_1:
+ case WCD937X_CLASSH_CTRL_CCL_2:
+ case WCD937X_CLASSH_CTRL_CCL_3:
+ case WCD937X_CLASSH_CTRL_CCL_4:
+ case WCD937X_CLASSH_CTRL_CCL_5:
+ case WCD937X_CLASSH_BUCK_TMUX_A_D:
+ case WCD937X_CLASSH_BUCK_SW_DRV_CNTL:
+ case WCD937X_CLASSH_SPARE:
+ case WCD937X_FLYBACK_EN:
+ case WCD937X_FLYBACK_VNEG_CTRL_1:
+ case WCD937X_FLYBACK_VNEG_CTRL_2:
+ case WCD937X_FLYBACK_VNEG_CTRL_3:
+ case WCD937X_FLYBACK_VNEG_CTRL_4:
+ case WCD937X_FLYBACK_VNEG_CTRL_5:
+ case WCD937X_FLYBACK_VNEG_CTRL_6:
+ case WCD937X_FLYBACK_VNEG_CTRL_7:
+ case WCD937X_FLYBACK_VNEG_CTRL_8:
+ case WCD937X_FLYBACK_VNEG_CTRL_9:
+ case WCD937X_FLYBACK_VNEGDAC_CTRL_1:
+ case WCD937X_FLYBACK_VNEGDAC_CTRL_2:
+ case WCD937X_FLYBACK_VNEGDAC_CTRL_3:
+ case WCD937X_FLYBACK_CTRL_1:
+ case WCD937X_FLYBACK_TEST_CTL:
+ case WCD937X_RX_AUX_SW_CTL:
+ case WCD937X_RX_PA_AUX_IN_CONN:
+ case WCD937X_RX_TIMER_DIV:
+ case WCD937X_RX_OCP_CTL:
+ case WCD937X_RX_OCP_COUNT:
+ case WCD937X_RX_BIAS_EAR_DAC:
+ case WCD937X_RX_BIAS_EAR_AMP:
+ case WCD937X_RX_BIAS_HPH_LDO:
+ case WCD937X_RX_BIAS_HPH_PA:
+ case WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2:
+ case WCD937X_RX_BIAS_HPH_RDAC_LDO:
+ case WCD937X_RX_BIAS_HPH_CNP1:
+ case WCD937X_RX_BIAS_HPH_LOWPOWER:
+ case WCD937X_RX_BIAS_AUX_DAC:
+ case WCD937X_RX_BIAS_AUX_AMP:
+ case WCD937X_RX_BIAS_VNEGDAC_BLEEDER:
+ case WCD937X_RX_BIAS_MISC:
+ case WCD937X_RX_BIAS_BUCK_RST:
+ case WCD937X_RX_BIAS_BUCK_VREF_ERRAMP:
+ case WCD937X_RX_BIAS_FLYB_ERRAMP:
+ case WCD937X_RX_BIAS_FLYB_BUFF:
+ case WCD937X_RX_BIAS_FLYB_MID_RST:
+ case WCD937X_HPH_CNP_EN:
+ case WCD937X_HPH_CNP_WG_CTL:
+ case WCD937X_HPH_CNP_WG_TIME:
+ case WCD937X_HPH_OCP_CTL:
+ case WCD937X_HPH_AUTO_CHOP:
+ case WCD937X_HPH_CHOP_CTL:
+ case WCD937X_HPH_PA_CTL1:
+ case WCD937X_HPH_PA_CTL2:
+ case WCD937X_HPH_L_EN:
+ case WCD937X_HPH_L_TEST:
+ case WCD937X_HPH_L_ATEST:
+ case WCD937X_HPH_R_EN:
+ case WCD937X_HPH_R_TEST:
+ case WCD937X_HPH_R_ATEST:
+ case WCD937X_HPH_RDAC_CLK_CTL1:
+ case WCD937X_HPH_RDAC_CLK_CTL2:
+ case WCD937X_HPH_RDAC_LDO_CTL:
+ case WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL:
+ case WCD937X_HPH_REFBUFF_UHQA_CTL:
+ case WCD937X_HPH_REFBUFF_LP_CTL:
+ case WCD937X_HPH_L_DAC_CTL:
+ case WCD937X_HPH_R_DAC_CTL:
+ case WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL:
+ case WCD937X_HPH_SURGE_HPHLR_SURGE_EN:
+ case WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1:
+ case WCD937X_EAR_EAR_EN_REG:
+ case WCD937X_EAR_EAR_PA_CON:
+ case WCD937X_EAR_EAR_SP_CON:
+ case WCD937X_EAR_EAR_DAC_CON:
+ case WCD937X_EAR_EAR_CNP_FSM_CON:
+ case WCD937X_EAR_TEST_CTL:
+ case WCD937X_HPH_NEW_ANA_HPH2:
+ case WCD937X_HPH_NEW_ANA_HPH3:
+ case WCD937X_SLEEP_CTL:
+ case WCD937X_SLEEP_WATCHDOG_CTL:
+ case WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL:
+ case WCD937X_MBHC_NEW_CTL_1:
+ case WCD937X_MBHC_NEW_CTL_2:
+ case WCD937X_MBHC_NEW_PLUG_DETECT_CTL:
+ case WCD937X_MBHC_NEW_ZDET_ANA_CTL:
+ case WCD937X_MBHC_NEW_ZDET_RAMP_CTL:
+ case WCD937X_TX_NEW_TX_CH2_SEL:
+ case WCD937X_AUX_AUXPA:
+ case WCD937X_LDORXTX_MODE:
+ case WCD937X_LDORXTX_CONFIG:
+ case WCD937X_DIE_CRACK_DIE_CRK_DET_EN:
+ case WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL:
+ case WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L:
+ case WCD937X_HPH_NEW_INT_RDAC_VREF_CTL:
+ case WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL:
+ case WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R:
+ case WCD937X_HPH_NEW_INT_PA_MISC1:
+ case WCD937X_HPH_NEW_INT_PA_MISC2:
+ case WCD937X_HPH_NEW_INT_PA_RDAC_MISC:
+ case WCD937X_HPH_NEW_INT_HPH_TIMER1:
+ case WCD937X_HPH_NEW_INT_HPH_TIMER2:
+ case WCD937X_HPH_NEW_INT_HPH_TIMER3:
+ case WCD937X_HPH_NEW_INT_HPH_TIMER4:
+ case WCD937X_HPH_NEW_INT_PA_RDAC_MISC2:
+ case WCD937X_HPH_NEW_INT_PA_RDAC_MISC3:
+ case WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI:
+ case WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP:
+ case WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP:
+ case WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL:
+ case WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL:
+ case WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT:
+ case WCD937X_MBHC_NEW_INT_SPARE_2:
+ case WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON:
+ case WCD937X_EAR_INT_NEW_CNP_VCM_CON1:
+ case WCD937X_EAR_INT_NEW_CNP_VCM_CON2:
+ case WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS:
+ case WCD937X_AUX_INT_EN_REG:
+ case WCD937X_AUX_INT_PA_CTRL:
+ case WCD937X_AUX_INT_SP_CTRL:
+ case WCD937X_AUX_INT_DAC_CTRL:
+ case WCD937X_AUX_INT_CLK_CTRL:
+ case WCD937X_AUX_INT_TEST_CTRL:
+ case WCD937X_AUX_INT_MISC:
+ case WCD937X_LDORXTX_INT_BIAS:
+ case WCD937X_LDORXTX_INT_STB_LOADS_DTEST:
+ case WCD937X_LDORXTX_INT_TEST0:
+ case WCD937X_LDORXTX_INT_STARTUP_TIMER:
+ case WCD937X_LDORXTX_INT_TEST1:
+ case WCD937X_SLEEP_INT_WATCHDOG_CTL_1:
+ case WCD937X_SLEEP_INT_WATCHDOG_CTL_2:
+ case WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1:
+ case WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2:
+ case WCD937X_DIGITAL_CDC_RST_CTL:
+ case WCD937X_DIGITAL_TOP_CLK_CFG:
+ case WCD937X_DIGITAL_CDC_ANA_CLK_CTL:
+ case WCD937X_DIGITAL_CDC_DIG_CLK_CTL:
+ case WCD937X_DIGITAL_SWR_RST_EN:
+ case WCD937X_DIGITAL_CDC_PATH_MODE:
+ case WCD937X_DIGITAL_CDC_RX_RST:
+ case WCD937X_DIGITAL_CDC_RX0_CTL:
+ case WCD937X_DIGITAL_CDC_RX1_CTL:
+ case WCD937X_DIGITAL_CDC_RX2_CTL:
+ case WCD937X_DIGITAL_DEM_BYPASS_DATA0:
+ case WCD937X_DIGITAL_DEM_BYPASS_DATA1:
+ case WCD937X_DIGITAL_DEM_BYPASS_DATA2:
+ case WCD937X_DIGITAL_DEM_BYPASS_DATA3:
+ case WCD937X_DIGITAL_CDC_COMP_CTL_0:
+ case WCD937X_DIGITAL_CDC_RX_DELAY_CTL:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A1_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A1_1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A2_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A2_1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A3_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A3_1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A4_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A4_1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A5_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A5_1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A6_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_A7_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_C_0:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_C_1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_C_2:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_C_3:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R1:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R2:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R3:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R4:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R5:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R6:
+ case WCD937X_DIGITAL_CDC_HPH_DSM_R7:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A1_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A1_1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A2_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A2_1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A3_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A3_1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A4_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A4_1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A5_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A5_1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A6_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_A7_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_C_0:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_C_1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_C_2:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_C_3:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R1:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R2:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R3:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R4:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R5:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R6:
+ case WCD937X_DIGITAL_CDC_AUX_DSM_R7:
+ case WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0:
+ case WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1:
+ case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0:
+ case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1:
+ case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2:
+ case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0:
+ case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1:
+ case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2:
+ case WCD937X_DIGITAL_CDC_HPH_GAIN_CTL:
+ case WCD937X_DIGITAL_CDC_AUX_GAIN_CTL:
+ case WCD937X_DIGITAL_CDC_EAR_PATH_CTL:
+ case WCD937X_DIGITAL_CDC_SWR_CLH:
+ case WCD937X_DIGITAL_SWR_CLH_BYP:
+ case WCD937X_DIGITAL_CDC_TX0_CTL:
+ case WCD937X_DIGITAL_CDC_TX1_CTL:
+ case WCD937X_DIGITAL_CDC_TX2_CTL:
+ case WCD937X_DIGITAL_CDC_TX_RST:
+ case WCD937X_DIGITAL_CDC_REQ_CTL:
+ case WCD937X_DIGITAL_CDC_AMIC_CTL:
+ case WCD937X_DIGITAL_CDC_DMIC_CTL:
+ case WCD937X_DIGITAL_CDC_DMIC1_CTL:
+ case WCD937X_DIGITAL_CDC_DMIC2_CTL:
+ case WCD937X_DIGITAL_CDC_DMIC3_CTL:
+ case WCD937X_DIGITAL_EFUSE_CTL:
+ case WCD937X_DIGITAL_EFUSE_PRG_CTL:
+ case WCD937X_DIGITAL_EFUSE_TEST_CTL_0:
+ case WCD937X_DIGITAL_EFUSE_TEST_CTL_1:
+ case WCD937X_DIGITAL_PDM_WD_CTL0:
+ case WCD937X_DIGITAL_PDM_WD_CTL1:
+ case WCD937X_DIGITAL_PDM_WD_CTL2:
+ case WCD937X_DIGITAL_INTR_MODE:
+ case WCD937X_DIGITAL_INTR_MASK_0:
+ case WCD937X_DIGITAL_INTR_MASK_1:
+ case WCD937X_DIGITAL_INTR_MASK_2:
+ case WCD937X_DIGITAL_INTR_CLEAR_0:
+ case WCD937X_DIGITAL_INTR_CLEAR_1:
+ case WCD937X_DIGITAL_INTR_CLEAR_2:
+ case WCD937X_DIGITAL_INTR_LEVEL_0:
+ case WCD937X_DIGITAL_INTR_LEVEL_1:
+ case WCD937X_DIGITAL_INTR_LEVEL_2:
+ case WCD937X_DIGITAL_INTR_SET_0:
+ case WCD937X_DIGITAL_INTR_SET_1:
+ case WCD937X_DIGITAL_INTR_SET_2:
+ case WCD937X_DIGITAL_INTR_TEST_0:
+ case WCD937X_DIGITAL_INTR_TEST_1:
+ case WCD937X_DIGITAL_INTR_TEST_2:
+ case WCD937X_DIGITAL_CDC_CONN_RX0_CTL:
+ case WCD937X_DIGITAL_CDC_CONN_RX1_CTL:
+ case WCD937X_DIGITAL_CDC_CONN_RX2_CTL:
+ case WCD937X_DIGITAL_CDC_CONN_TX_CTL:
+ case WCD937X_DIGITAL_LOOP_BACK_MODE:
+ case WCD937X_DIGITAL_SWR_DAC_TEST:
+ case WCD937X_DIGITAL_SWR_HM_TEST_RX_0:
+ case WCD937X_DIGITAL_SWR_HM_TEST_TX_0:
+ case WCD937X_DIGITAL_SWR_HM_TEST_RX_1:
+ case WCD937X_DIGITAL_SWR_HM_TEST_TX_1:
+ case WCD937X_DIGITAL_SWR_HM_TEST:
+ case WCD937X_DIGITAL_PAD_CTL_PDM_RX0:
+ case WCD937X_DIGITAL_PAD_CTL_PDM_RX1:
+ case WCD937X_DIGITAL_PAD_CTL_PDM_TX0:
+ case WCD937X_DIGITAL_PAD_CTL_PDM_TX1:
+ case WCD937X_DIGITAL_PAD_INP_DIS_0:
+ case WCD937X_DIGITAL_PAD_INP_DIS_1:
+ case WCD937X_DIGITAL_DRIVE_STRENGTH_0:
+ case WCD937X_DIGITAL_DRIVE_STRENGTH_1:
+ case WCD937X_DIGITAL_DRIVE_STRENGTH_2:
+ case WCD937X_DIGITAL_RX_DATA_EDGE_CTL:
+ case WCD937X_DIGITAL_TX_DATA_EDGE_CTL:
+ case WCD937X_DIGITAL_GPIO_MODE:
+ case WCD937X_DIGITAL_PIN_CTL_OE:
+ case WCD937X_DIGITAL_PIN_CTL_DATA_0:
+ case WCD937X_DIGITAL_PIN_CTL_DATA_1:
+ case WCD937X_DIGITAL_PIN_STATUS_0:
+ case WCD937X_DIGITAL_PIN_STATUS_1:
+ case WCD937X_DIGITAL_DIG_DEBUG_CTL:
+ case WCD937X_DIGITAL_DIG_DEBUG_EN:
+ case WCD937X_DIGITAL_ANA_CSR_DBG_ADD:
+ case WCD937X_DIGITAL_ANA_CSR_DBG_CTL:
+ case WCD937X_DIGITAL_SSP_DBG:
+ case WCD937X_DIGITAL_MODE_STATUS_0:
+ case WCD937X_DIGITAL_MODE_STATUS_1:
+ case WCD937X_DIGITAL_SPARE_0:
+ case WCD937X_DIGITAL_SPARE_1:
+ case WCD937X_DIGITAL_SPARE_2:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wcd937x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD937X_ANA_MBHC_RESULT_1:
+ case WCD937X_ANA_MBHC_RESULT_2:
+ case WCD937X_ANA_MBHC_RESULT_3:
+ case WCD937X_MBHC_MOISTURE_DET_FSM_STATUS:
+ case WCD937X_TX_1_2_SAR2_ERR:
+ case WCD937X_TX_1_2_SAR1_ERR:
+ case WCD937X_TX_3_SPARE_MONO:
+ case WCD937X_TX_3_SAR1_ERR:
+ case WCD937X_HPH_L_STATUS:
+ case WCD937X_HPH_R_STATUS:
+ case WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS:
+ case WCD937X_EAR_STATUS_REG_1:
+ case WCD937X_EAR_STATUS_REG_2:
+ case WCD937X_MBHC_NEW_FSM_STATUS:
+ case WCD937X_MBHC_NEW_ADC_RESULT:
+ case WCD937X_DIE_CRACK_DIE_CRK_DET_OUT:
+ case WCD937X_AUX_INT_STATUS_REG:
+ case WCD937X_LDORXTX_INT_STATUS:
+ case WCD937X_DIGITAL_CHIP_ID0:
+ case WCD937X_DIGITAL_CHIP_ID1:
+ case WCD937X_DIGITAL_CHIP_ID2:
+ case WCD937X_DIGITAL_CHIP_ID3:
+ case WCD937X_DIGITAL_EFUSE_T_DATA_0:
+ case WCD937X_DIGITAL_EFUSE_T_DATA_1:
+ case WCD937X_DIGITAL_INTR_STATUS_0:
+ case WCD937X_DIGITAL_INTR_STATUS_1:
+ case WCD937X_DIGITAL_INTR_STATUS_2:
+ case WCD937X_DIGITAL_EFUSE_REG_0:
+ case WCD937X_DIGITAL_EFUSE_REG_1:
+ case WCD937X_DIGITAL_EFUSE_REG_2:
+ case WCD937X_DIGITAL_EFUSE_REG_3:
+ case WCD937X_DIGITAL_EFUSE_REG_4:
+ case WCD937X_DIGITAL_EFUSE_REG_5:
+ case WCD937X_DIGITAL_EFUSE_REG_6:
+ case WCD937X_DIGITAL_EFUSE_REG_7:
+ case WCD937X_DIGITAL_EFUSE_REG_8:
+ case WCD937X_DIGITAL_EFUSE_REG_9:
+ case WCD937X_DIGITAL_EFUSE_REG_10:
+ case WCD937X_DIGITAL_EFUSE_REG_11:
+ case WCD937X_DIGITAL_EFUSE_REG_12:
+ case WCD937X_DIGITAL_EFUSE_REG_13:
+ case WCD937X_DIGITAL_EFUSE_REG_14:
+ case WCD937X_DIGITAL_EFUSE_REG_15:
+ case WCD937X_DIGITAL_EFUSE_REG_16:
+ case WCD937X_DIGITAL_EFUSE_REG_17:
+ case WCD937X_DIGITAL_EFUSE_REG_18:
+ case WCD937X_DIGITAL_EFUSE_REG_19:
+ case WCD937X_DIGITAL_EFUSE_REG_20:
+ case WCD937X_DIGITAL_EFUSE_REG_21:
+ case WCD937X_DIGITAL_EFUSE_REG_22:
+ case WCD937X_DIGITAL_EFUSE_REG_23:
+ case WCD937X_DIGITAL_EFUSE_REG_24:
+ case WCD937X_DIGITAL_EFUSE_REG_25:
+ case WCD937X_DIGITAL_EFUSE_REG_26:
+ case WCD937X_DIGITAL_EFUSE_REG_27:
+ case WCD937X_DIGITAL_EFUSE_REG_28:
+ case WCD937X_DIGITAL_EFUSE_REG_29:
+ case WCD937X_DIGITAL_EFUSE_REG_30:
+ case WCD937X_DIGITAL_EFUSE_REG_31:
+ return true;
+ }
+
+ return wcd937x_rdwr_register(dev, reg);
+}
+
+static bool wcd937x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD937X_ANA_MBHC_RESULT_1:
+ case WCD937X_ANA_MBHC_RESULT_2:
+ case WCD937X_ANA_MBHC_RESULT_3:
+ case WCD937X_MBHC_MOISTURE_DET_FSM_STATUS:
+ case WCD937X_TX_1_2_SAR1_ERR:
+ case WCD937X_TX_1_2_SAR2_ERR:
+ case WCD937X_TX_3_SAR1_ERR:
+ case WCD937X_HPH_L_STATUS:
+ case WCD937X_HPH_R_STATUS:
+ case WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS:
+ case WCD937X_EAR_STATUS_REG_1:
+ case WCD937X_EAR_STATUS_REG_2:
+ case WCD937X_MBHC_NEW_FSM_STATUS:
+ case WCD937X_MBHC_NEW_ADC_RESULT:
+ case WCD937X_DIE_CRACK_DIE_CRK_DET_OUT:
+ case WCD937X_DIGITAL_INTR_STATUS_0:
+ case WCD937X_DIGITAL_INTR_STATUS_1:
+ case WCD937X_DIGITAL_INTR_STATUS_2:
+ case WCD937X_DIGITAL_SWR_HM_TEST:
+ case WCD937X_DIGITAL_PIN_STATUS_0:
+ case WCD937X_DIGITAL_PIN_STATUS_1:
+ case WCD937X_DIGITAL_MODE_STATUS_0:
+ case WCD937X_DIGITAL_MODE_STATUS_1:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config wcd937x_regmap_config = {
+ .name = "wcd937x_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wcd937x_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wcd937x_defaults),
+ .max_register = WCD937X_MAX_REGISTER,
+ .readable_reg = wcd937x_readable_register,
+ .writeable_reg = wcd937x_rdwr_register,
+ .volatile_reg = wcd937x_volatile_register,
+};
+
+static const struct sdw_slave_ops wcd9370_slave_ops = {
+ .update_status = wcd_update_status,
+ .interrupt_callback = wcd9370_interrupt_callback,
+};
+
+static int wcd9370_probe(struct sdw_slave *pdev,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd937x_sdw_priv *wcd;
+ u8 master_ch_mask[WCD937X_MAX_SWR_CH_IDS];
+ int master_ch_mask_size = 0;
+ int ret, i;
+
+ wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
+ if (!wcd)
+ return -ENOMEM;
+
+ /* Port map index starts at 0, however the data port for this codec start at index 1 */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ wcd->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+ &pdev->m_port_map[1],
+ WCD937X_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+ &pdev->m_port_map[1],
+ WCD937X_MAX_SWR_PORTS);
+ }
+ if (ret < 0)
+ dev_info(dev, "Error getting static port mapping for %s (%d)\n",
+ wcd->is_tx ? "TX" : "RX", ret);
+
+ wcd->sdev = pdev;
+ dev_set_drvdata(dev, wcd);
+
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+ SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
+
+ memset(master_ch_mask, 0, WCD937X_MAX_SWR_CH_IDS);
+
+ if (wcd->is_tx) {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,tx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ } else {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,rx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+ if (wcd->is_tx) {
+ pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS, 0);
+ pdev->prop.src_dpn_prop = wcd937x_dpn_prop;
+ wcd->ch_info = &wcd937x_sdw_tx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]);
+
+ pdev->prop.wake_capable = true;
+
+ wcd->regmap = devm_regmap_init_sdw(pdev, &wcd937x_regmap_config);
+ if (IS_ERR(wcd->regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+ "Regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(wcd->regmap, true);
+ } else {
+ pdev->prop.sink_ports = GENMASK(WCD937X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = wcd937x_dpn_prop;
+ wcd->ch_info = &wcd937x_sdw_rx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]);
+ }
+
+
+ ret = component_add(dev, &wcd_sdw_component_ops);
+ if (ret)
+ return ret;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+}
+
+static int wcd9370_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &wcd_sdw_component_ops);
+
+ return 0;
+}
+
+static const struct sdw_device_id wcd9370_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10a, 0), /* WCD9370 RX/TX Device ID */
+ { },
+};
+MODULE_DEVICE_TABLE(sdw, wcd9370_slave_id);
+
+static int wcd937x_sdw_runtime_suspend(struct device *dev)
+{
+ struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ if (wcd->regmap) {
+ regcache_cache_only(wcd->regmap, true);
+ regcache_mark_dirty(wcd->regmap);
+ }
+
+ return 0;
+}
+
+static int wcd937x_sdw_runtime_resume(struct device *dev)
+{
+ struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ if (wcd->regmap) {
+ regcache_cache_only(wcd->regmap, false);
+ regcache_sync(wcd->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops wcd937x_sdw_pm_ops = {
+ RUNTIME_PM_OPS(wcd937x_sdw_runtime_suspend, wcd937x_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver wcd9370_codec_driver = {
+ .probe = wcd9370_probe,
+ .remove = wcd9370_remove,
+ .ops = &wcd9370_slave_ops,
+ .id_table = wcd9370_slave_id,
+ .driver = {
+ .name = "wcd9370-codec",
+ .pm = pm_ptr(&wcd937x_sdw_pm_ops),
+ }
+};
+module_sdw_driver(wcd9370_codec_driver);
+
+MODULE_DESCRIPTION("WCD937X SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c
new file mode 100644
index 000000000000..f1dced57a59b
--- /dev/null
+++ b/sound/soc/codecs/wcd937x.c
@@ -0,0 +1,2987 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
+#include "wcd-mbhc-v2.h"
+#include "wcd937x.h"
+
+#define CHIPID_WCD9370 0x0
+#define CHIPID_WCD9375 0x5
+
+/* Z value defined in milliohm */
+#define WCD937X_ZDET_VAL_32 (32000)
+#define WCD937X_ZDET_VAL_400 (400000)
+#define WCD937X_ZDET_VAL_1200 (1200000)
+#define WCD937X_ZDET_VAL_100K (100000000)
+/* Z floating defined in ohms */
+#define WCD937X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE)
+#define WCD937X_ZDET_NUM_MEASUREMENTS (900)
+#define WCD937X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14)
+#define WCD937X_MBHC_GET_X1(x) ((x) & 0x3FFF)
+/* Z value compared in milliOhm */
+#define WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z) (((z) > 400000) || ((z) < 32000))
+#define WCD937X_MBHC_ZDET_CONST (86 * 16384)
+#define WCD937X_MBHC_MOISTURE_RREF R_24_KOHM
+#define WCD_MBHC_HS_V_MAX 1600
+#define EAR_RX_PATH_AUX 1
+#define WCD937X_MBHC_MAX_BUTTONS 8
+
+#define WCD937X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+
+/* Fractional Rates */
+#define WCD937X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define WCD937X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+enum {
+ ALLOW_BUCK_DISABLE,
+ HPH_COMP_DELAY,
+ HPH_PA_DELAY,
+ AMIC2_BCS_ENABLE,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct wcd937x_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct wcd937x_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode;
+ struct device_node *txnode;
+ struct regmap *regmap;
+ /* micb setup lock */
+ struct mutex micb_lock;
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct wcd_clsh_ctrl *clsh_info;
+ struct wcd_common common;
+ struct irq_domain *virq;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[WCD937X_MAX_MICBIAS];
+ s32 pullup_ref[WCD937X_MAX_MICBIAS];
+ u32 hph_mode;
+ int ear_rx_path;
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ int aux_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+
+ struct gpio_desc *us_euro_gpio;
+ struct gpio_desc *reset_gpio;
+
+ atomic_t rx_clk_cnt;
+ atomic_t ana_clk_count;
+};
+
+static const char * const wcd937x_supplies[] = {
+ "vdd-rxtx", "vdd-px", "vdd-mic-bias", "vdd-buck",
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+struct wcd937x_mbhc_zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
+static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD937X_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD937X_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD937X_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD937X_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD937X_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD937X_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD937X_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD937X_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD937X_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD937X_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD937X_MBHC_NEW_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD937X_MBHC_NEW_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD937X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD937X_HPH_OCP_CTL, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD937X_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD937X_ANA_MICB2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD937X_HPH_CNP_WG_TIME, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD937X_ANA_HPH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD937X_ANA_HPH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD937X_ANA_HPH, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD937X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD937X_MBHC_CTL_BCS, 0x02),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD937X_MBHC_NEW_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD937X_MBHC_NEW_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD937X_MBHC_NEW_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD937X_HPH_PA_CTL2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD937X_HPH_PA_CTL2, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD937X_HPH_L_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD937X_HPH_R_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD937X_DIGITAL_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD937X_DIGITAL_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD937X_MBHC_NEW_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD937X_MBHC_NEW_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD937X_MBHC_NEW_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD937X_MBHC_NEW_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD937X_ANA_MICB2, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD937X_MBHC_NEW_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD937X_MBHC_NEW_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD937X_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq wcd937x_irqs[WCD937X_NUM_IRQS] = {
+ REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_SW_DET, 0, BIT(4)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_OCP_INT, 0, BIT(5)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_CNP_INT, 0, BIT(6)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_OCP_INT, 0, BIT(7)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_CNP_INT, 1, BIT(0)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_EAR_CNP_INT, 1, BIT(1)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_EAR_SCD_INT, 1, BIT(2)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_AUX_CNP_INT, 1, BIT(3)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_AUX_SCD_INT, 1, BIT(4)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_AUX_PDM_WD_INT, 1, BIT(7)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_LDORT_SCD_INT, 2, BIT(0)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)),
+ REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)),
+};
+
+static int wcd937x_handle_post_irq(void *data)
+{
+ struct wcd937x_priv *wcd937x;
+
+ if (data)
+ wcd937x = (struct wcd937x_priv *)data;
+ else
+ return IRQ_HANDLED;
+
+ regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_0, 0);
+ regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_1, 0);
+ regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_2, 0);
+
+ return IRQ_HANDLED;
+}
+
+static const u32 wcd937x_config_regs[] = {
+ WCD937X_DIGITAL_INTR_LEVEL_0,
+};
+
+static const struct regmap_irq_chip wcd937x_regmap_irq_chip = {
+ .name = "wcd937x",
+ .irqs = wcd937x_irqs,
+ .num_irqs = ARRAY_SIZE(wcd937x_irqs),
+ .num_regs = 3,
+ .status_base = WCD937X_DIGITAL_INTR_STATUS_0,
+ .mask_base = WCD937X_DIGITAL_INTR_MASK_0,
+ .ack_base = WCD937X_DIGITAL_INTR_CLEAR_0,
+ .use_ack = 1,
+ .clear_ack = 1,
+ .config_base = wcd937x_config_regs,
+ .num_config_bases = ARRAY_SIZE(wcd937x_config_regs),
+ .num_config_regs = 1,
+ .runtime_pm = true,
+ .handle_post_irq = wcd937x_handle_post_irq,
+ .irq_drv_data = NULL,
+};
+
+static void wcd937x_reset(struct wcd937x_priv *wcd937x)
+{
+ gpiod_set_value(wcd937x->reset_gpio, 1);
+ usleep_range(20, 30);
+ gpiod_set_value(wcd937x->reset_gpio, 0);
+ usleep_range(20, 30);
+}
+
+static void wcd937x_io_init(struct regmap *regmap)
+{
+ u32 val = 0, temp = 0, temp1 = 0;
+
+ regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_29, &val);
+
+ val = val & 0x0F;
+
+ regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_16, &temp);
+ regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_17, &temp1);
+
+ if (temp == 0x02 || temp1 > 0x09)
+ regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x0E, val);
+ else
+ regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x0e, 0x0e);
+
+ regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x80, 0x80);
+ usleep_range(1000, 1010);
+
+ regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x40, 0x40);
+ usleep_range(1000, 1010);
+
+ regmap_update_bits(regmap, WCD937X_LDORXTX_CONFIG, BIT(4), 0x00);
+ regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xf0, BIT(7));
+ regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(7), BIT(7));
+ regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(6), BIT(6));
+ usleep_range(10000, 10010);
+
+ regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(6), 0x00);
+ regmap_update_bits(regmap, WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xff, 0xd9);
+ regmap_update_bits(regmap, WCD937X_MICB1_TEST_CTL_1, 0xff, 0xfa);
+ regmap_update_bits(regmap, WCD937X_MICB2_TEST_CTL_1, 0xff, 0xfa);
+ regmap_update_bits(regmap, WCD937X_MICB3_TEST_CTL_1, 0xff, 0xfa);
+
+ regmap_update_bits(regmap, WCD937X_MICB1_TEST_CTL_2, 0x38, 0x00);
+ regmap_update_bits(regmap, WCD937X_MICB2_TEST_CTL_2, 0x38, 0x00);
+ regmap_update_bits(regmap, WCD937X_MICB3_TEST_CTL_2, 0x38, 0x00);
+
+ /* Set Bandgap Fine Adjustment to +5mV for Tanggu SMIC part */
+ regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_16, &val);
+ if (val == 0x01) {
+ regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0xB0);
+ } else if (val == 0x02) {
+ regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x1F, 0x04);
+ regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x1F, 0x04);
+ regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0xB0);
+ regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xF0, 0x50);
+ }
+}
+
+static int wcd937x_rx_clk_enable(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ if (atomic_read(&wcd937x->rx_clk_cnt))
+ return 0;
+
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(3), BIT(3));
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(0), BIT(0));
+ snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(0), BIT(0));
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX0_CTL, BIT(6), 0x00);
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX1_CTL, BIT(6), 0x00);
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX2_CTL, BIT(6), 0x00);
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(1), BIT(1));
+
+ atomic_inc(&wcd937x->rx_clk_cnt);
+
+ return 0;
+}
+
+static int wcd937x_rx_clk_disable(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ if (!atomic_read(&wcd937x->rx_clk_cnt)) {
+ dev_err(component->dev, "clk already disabled\n");
+ return 0;
+ }
+
+ atomic_dec(&wcd937x->rx_clk_cnt);
+
+ snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(0), 0x00);
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(1), 0x00);
+ snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(0), 0x00);
+
+ return 0;
+}
+
+static int wcd937x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd937x_rx_clk_enable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(0), BIT(0));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_HPH_GAIN_CTL,
+ BIT(2), BIT(2));
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_RDAC_CLK_CTL1,
+ BIT(7), 0x00);
+ set_bit(HPH_COMP_DELAY, &wcd937x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0f, BIT(1));
+ else if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0f, 0x06);
+
+ if (wcd937x->comp1_enable) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(1), BIT(1));
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_L_EN,
+ BIT(5), 0x00);
+
+ if (wcd937x->comp2_enable) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(0), BIT(0));
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_R_EN, BIT(5), 0x00);
+ }
+
+ if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) {
+ usleep_range(5000, 5110);
+ clear_bit(HPH_COMP_DELAY, &wcd937x->status_mask);
+ }
+ } else {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(1), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_L_EN,
+ BIT(5), BIT(5));
+ }
+
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_HPH_TIMER1,
+ BIT(1), 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0f, BIT(0));
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd937x_rx_clk_enable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(1), BIT(1));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, BIT(3), BIT(3));
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_RDAC_CLK_CTL1, BIT(7), 0x00);
+ set_bit(HPH_COMP_DELAY, &wcd937x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ 0x0f, BIT(1));
+ else if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ 0x0f, 0x06);
+ if (wcd937x->comp2_enable) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(0), BIT(0));
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_R_EN, BIT(5), 0x00);
+ if (wcd937x->comp1_enable) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(1), BIT(1));
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_L_EN,
+ BIT(5), 0x00);
+ }
+
+ if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) {
+ usleep_range(5000, 5110);
+ clear_bit(HPH_COMP_DELAY, &wcd937x->status_mask);
+ }
+ } else {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(0), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_R_EN,
+ BIT(5), BIT(5));
+ }
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_HPH_TIMER1,
+ BIT(1), 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ 0x0f, BIT(0));
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd937x_rx_clk_enable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_HPH_GAIN_CTL,
+ BIT(2), BIT(2));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(0), BIT(0));
+
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0f, BIT(1));
+ else if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0f, 0x06);
+ if (wcd937x->comp1_enable)
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(1), BIT(1));
+ usleep_range(5000, 5010);
+
+ snd_soc_component_update_bits(component, WCD937X_FLYBACK_EN, BIT(2), 0x00);
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR,
+ hph_mode);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0f, BIT(0));
+ if (wcd937x->comp1_enable)
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_COMP_CTL_0,
+ BIT(1), 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd937x_rx_clk_enable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
+ BIT(2), BIT(2));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(2), BIT(2));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_AUX_GAIN_CTL,
+ BIT(0), BIT(0));
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_AUX,
+ hph_mode);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
+ BIT(2), 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
+ snd_soc_component_update_bits(component, WCD937X_ANA_HPH,
+ BIT(4), BIT(4));
+ usleep_range(100, 110);
+ set_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL1,
+ 0x07, 0x03);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) {
+ if (wcd937x->comp2_enable)
+ usleep_range(7000, 7100);
+ else
+ usleep_range(20000, 20100);
+ clear_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ }
+
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_HPH_TIMER1,
+ BIT(1), BIT(1));
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_RX_SUPPLIES,
+ BIT(1), BIT(1));
+ enable_irq(wcd937x->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd937x->hphr_pdm_wd_int);
+ set_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_PRE_HPHR_PA_OFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) {
+ if (wcd937x->comp2_enable)
+ usleep_range(7000, 7100);
+ else
+ usleep_range(20000, 20100);
+ clear_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ }
+
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_POST_HPHR_PA_OFF);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL1, 0x07, 0x00);
+ snd_soc_component_update_bits(component, WCD937X_ANA_HPH,
+ BIT(4), 0x00);
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL,
+ hph_mode);
+ snd_soc_component_update_bits(component, WCD937X_ANA_HPH,
+ BIT(5), BIT(5));
+ usleep_range(100, 110);
+ set_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL0, 0x07, 0x03);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) {
+ if (!wcd937x->comp1_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ }
+
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_NEW_INT_HPH_TIMER1,
+ BIT(1), BIT(1));
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_RX_SUPPLIES,
+ BIT(1), BIT(1));
+ enable_irq(wcd937x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd937x->hphl_pdm_wd_int);
+ set_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) {
+ if (!wcd937x->comp1_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &wcd937x->status_mask);
+ }
+
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_POST_HPHL_PA_OFF);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL0, 0x07, 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_HPH, BIT(5), 0x00);
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL,
+ hph_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+ u8 val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = WCD937X_DIGITAL_PDM_WD_CTL2_EN |
+ WCD937X_DIGITAL_PDM_WD_CTL2_TIMEOUT_SEL |
+ WCD937X_DIGITAL_PDM_WD_CTL2_HOLD_OFF;
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL2,
+ WCD937X_DIGITAL_PDM_WD_CTL2_MASK,
+ val);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1010);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_RX_SUPPLIES,
+ BIT(1), BIT(1));
+ enable_irq(wcd937x->aux_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd937x->aux_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(2000, 2010);
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_AUX,
+ hph_mode);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL2,
+ WCD937X_DIGITAL_PDM_WD_CTL2_MASK,
+ 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd937x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable watchdog interrupt for HPHL or AUX depending on mux value */
+ wcd937x->ear_rx_path = snd_soc_component_read(component,
+ WCD937X_DIGITAL_CDC_EAR_PATH_CTL);
+
+ if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX)
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL2,
+ BIT(0), BIT(0));
+ else
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL0,
+ 0x07, 0x03);
+ if (!wcd937x->comp1_enable)
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_EAR_COMPANDER_CTL,
+ BIT(7), BIT(7));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(6000, 6010);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_RX_SUPPLIES,
+ BIT(1), BIT(1));
+
+ if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX)
+ enable_irq(wcd937x->aux_pdm_wd_int);
+ else
+ enable_irq(wcd937x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX)
+ disable_irq_nosync(wcd937x->aux_pdm_wd_int);
+ else
+ disable_irq_nosync(wcd937x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!wcd937x->comp1_enable)
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_EAR_COMPANDER_CTL,
+ BIT(7), 0x00);
+ usleep_range(7000, 7010);
+ wcd_clsh_ctrl_set_state(wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR,
+ hph_mode);
+ snd_soc_component_update_bits(component, WCD937X_FLYBACK_EN,
+ BIT(2), BIT(2));
+
+ if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX)
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL2,
+ BIT(0), 0x00);
+ else
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_PDM_WD_CTL0,
+ 0x07, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ if (event == SND_SOC_DAPM_POST_PMD) {
+ wcd937x_rx_clk_disable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(0), 0x00);
+ }
+
+ return 0;
+}
+
+static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ if (event == SND_SOC_DAPM_POST_PMD) {
+ wcd937x_rx_clk_disable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(1), 0x00);
+ }
+
+ return 0;
+}
+
+static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ if (event == SND_SOC_DAPM_POST_PMD) {
+ usleep_range(6000, 6010);
+ wcd937x_rx_clk_disable(component);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(2), 0x00);
+ }
+
+ return 0;
+}
+
+
+static int wcd937x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ bool use_amic3 = snd_soc_component_read(component, WCD937X_TX_NEW_TX_CH2_SEL) & BIT(7);
+
+ /* Enable BCS for Headset mic */
+ if (event == SND_SOC_DAPM_PRE_PMU && strnstr(w->name, "ADC", sizeof("ADC")))
+ if (w->shift == 1 && !use_amic3)
+ set_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask);
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ atomic_inc(&wcd937x->ana_clk_count);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(7), BIT(7));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(3), BIT(3));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(4), BIT(4));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask))
+ clear_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask);
+
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(3), 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_enable_req(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_REQ_CTL, BIT(1), BIT(1));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_REQ_CTL, BIT(0), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH2, BIT(6), BIT(6));
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH3_HPF, BIT(6), BIT(6));
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x70, 0x70);
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH1, BIT(7), BIT(7));
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH2, BIT(6), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH2, BIT(7), BIT(7));
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH3, BIT(7), BIT(7));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH1, BIT(7), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH2, BIT(7), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_ANA_TX_CH3, BIT(7), 0x00);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(4), 0x00);
+
+ atomic_dec(&wcd937x->ana_clk_count);
+ if (atomic_read(&wcd937x->ana_clk_count) <= 0) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
+ BIT(4), 0x00);
+ atomic_set(&wcd937x->ana_clk_count, 0);
+ }
+
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(7), 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 dmic_clk_reg;
+
+ switch (w->shift) {
+ case 0:
+ case 1:
+ dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC1_CTL;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC2_CTL;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC3_CTL;
+ break;
+ default:
+ dev_err(component->dev, "Invalid DMIC Selection\n");
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ BIT(7), BIT(7));
+ snd_soc_component_update_bits(component,
+ dmic_clk_reg, 0x07, BIT(1));
+ snd_soc_component_update_bits(component,
+ dmic_clk_reg, BIT(3), BIT(3));
+ snd_soc_component_update_bits(component,
+ dmic_clk_reg, 0x70, BIT(5));
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_micbias_control(struct snd_soc_component *component,
+ int micb_num, int req, bool is_dapm)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+
+ if (micb_index < 0 || (micb_index > WCD937X_MAX_MICBIAS - 1)) {
+ dev_err(component->dev, "Invalid micbias index, micb_ind:%d\n", micb_index);
+ return -EINVAL;
+ }
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD937X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD937X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD937X_ANA_MICB3;
+ break;
+ default:
+ dev_err(component->dev, "Invalid micbias number: %d\n", micb_num);
+ return -EINVAL;
+ }
+
+ mutex_lock(&wcd937x->micb_lock);
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ wcd937x->pullup_ref[micb_index]++;
+ if (wcd937x->pullup_ref[micb_index] == 1 &&
+ wcd937x->micb_ref[micb_index] == 0)
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xc0, BIT(7));
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (wcd937x->pullup_ref[micb_index] > 0)
+ wcd937x->pullup_ref[micb_index]++;
+ if (wcd937x->pullup_ref[micb_index] == 0 &&
+ wcd937x->micb_ref[micb_index] == 0)
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xc0, 0x00);
+ break;
+ case MICB_ENABLE:
+ wcd937x->micb_ref[micb_index]++;
+ atomic_inc(&wcd937x->ana_clk_count);
+ if (wcd937x->micb_ref[micb_index] == 1) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
+ 0xf0, 0xf0);
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
+ BIT(4), BIT(4));
+ snd_soc_component_update_bits(component,
+ WCD937X_MICB1_TEST_CTL_2,
+ BIT(0), BIT(0));
+ snd_soc_component_update_bits(component,
+ WCD937X_MICB2_TEST_CTL_2,
+ BIT(0), BIT(0));
+ snd_soc_component_update_bits(component,
+ WCD937X_MICB3_TEST_CTL_2,
+ BIT(0), BIT(0));
+ snd_soc_component_update_bits(component,
+ micb_reg, 0xc0, BIT(6));
+
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+
+ if (micb_num == MIC_BIAS_2 && is_dapm)
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+ }
+ break;
+ case MICB_DISABLE:
+ atomic_dec(&wcd937x->ana_clk_count);
+ if (wcd937x->micb_ref[micb_index] > 0)
+ wcd937x->micb_ref[micb_index]--;
+ if (wcd937x->micb_ref[micb_index] == 0 &&
+ wcd937x->pullup_ref[micb_index] > 0)
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xc0, BIT(7));
+ else if (wcd937x->micb_ref[micb_index] == 0 &&
+ wcd937x->pullup_ref[micb_index] == 0) {
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc,
+ WCD_EVENT_PRE_MICBIAS_2_OFF);
+
+ snd_soc_component_update_bits(component, micb_reg,
+ 0xc0, 0x00);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ }
+
+ if (is_dapm && micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd937x->wcd_mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+ if (atomic_read(&wcd937x->ana_clk_count) <= 0) {
+ snd_soc_component_update_bits(component,
+ WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
+ BIT(4), 0x00);
+ atomic_set(&wcd937x->ana_clk_count, 0);
+ }
+ break;
+ }
+ mutex_unlock(&wcd937x->micb_lock);
+
+ return 0;
+}
+
+static int __wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd937x_micbias_control(component, micb_num,
+ MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd937x_micbias_control(component, micb_num,
+ MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ return __wcd937x_codec_enable_micbias(w, event);
+}
+
+static int __wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd937x_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd937x_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ return __wcd937x_codec_enable_micbias_pullup(w, event);
+}
+
+static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable)
+{
+ struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1];
+ const struct wcd_sdw_ch_info *ch_info = &wcd->ch_info[ch_id];
+ u8 port_num = ch_info->port_num;
+ u8 ch_mask = ch_info->ch_mask;
+ u8 mstr_port_num, mstr_ch_mask;
+ struct sdw_slave *sdev = wcd->sdev;
+
+ port_config->num = port_num;
+
+ mstr_port_num = sdev->m_port_map[port_num];
+ mstr_ch_mask = ch_info->master_ch_mask;
+
+ if (enable) {
+ port_config->ch_mask |= ch_mask;
+ wcd->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+ } else {
+ port_config->ch_mask &= ~ch_mask;
+ wcd->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+ }
+
+ return 0;
+}
+
+static int wcd937x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd937x->hph_mode;
+ return 0;
+}
+
+static int wcd937x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ if (!mode_val)
+ mode_val = CLS_AB;
+
+ if (mode_val == wcd937x->hph_mode)
+ return 0;
+
+ switch (mode_val) {
+ case CLS_H_NORMAL:
+ case CLS_H_HIFI:
+ case CLS_H_LP:
+ case CLS_AB:
+ case CLS_H_LOHIFI:
+ case CLS_H_ULP:
+ case CLS_AB_LP:
+ case CLS_AB_HIFI:
+ wcd937x->hph_mode = mode_val;
+ return 1;
+ }
+
+ dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__);
+ return -EINVAL;
+}
+
+static int wcd937x_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ ucontrol->value.integer.value[0] = hphr ? wcd937x->comp2_enable :
+ wcd937x->comp1_enable;
+ return 0;
+}
+
+static int wcd937x_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[AIF1_PB];
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc;
+ int portidx;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr) {
+ if (value == wcd937x->comp2_enable)
+ return 0;
+
+ wcd937x->comp2_enable = value;
+ } else {
+ if (value == wcd937x->comp1_enable)
+ return 0;
+
+ wcd937x->comp1_enable = value;
+ }
+
+ portidx = wcd->ch_info[mc->reg].port_num;
+
+ if (value)
+ wcd937x_connect_port(wcd, portidx, mc->reg, true);
+ else
+ wcd937x_connect_port(wcd, portidx, mc->reg, false);
+
+ return 1;
+}
+
+static int wcd937x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(comp);
+ struct wcd937x_sdw_priv *wcd;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+
+ wcd = wcd937x->sdw_priv[dai_id];
+ portidx = wcd->ch_info[ch_idx].port_num;
+
+ ucontrol->value.integer.value[0] = wcd->port_enable[portidx];
+
+ return 0;
+}
+
+static int wcd937x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(comp);
+ struct wcd937x_sdw_priv *wcd;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+ bool enable;
+
+ wcd = wcd937x->sdw_priv[dai_id];
+
+ portidx = wcd->ch_info[ch_idx].port_num;
+
+ enable = ucontrol->value.integer.value[0];
+
+ if (enable == wcd->port_enable[portidx]) {
+ wcd937x_connect_port(wcd, portidx, ch_idx, enable);
+ return 0;
+ }
+
+ wcd->port_enable[portidx] = enable;
+ wcd937x_connect_port(wcd, portidx, ch_idx, enable);
+
+ return 1;
+}
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "CLS_H_NORMAL", "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB",
+ "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_AB_LP", "CLS_AB_HIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), rx_hph_mode_mux_text);
+
+/* MBHC related */
+static void wcd937x_mbhc_clk_setup(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_1,
+ WCD937X_MBHC_CTL_RCO_EN_MASK, enable);
+}
+
+static void wcd937x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD937X_ANA_MBHC_ELECT,
+ WCD937X_ANA_MBHC_BIAS_EN, enable);
+}
+
+static void wcd937x_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, WCD937X_ANA_MBHC_BTN0 + i,
+ WCD937X_MBHC_BTN_VTH_MASK, vth);
+ }
+}
+
+static bool wcd937x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
+{
+ u8 val;
+
+ if (micb_num == MIC_BIAS_2) {
+ val = snd_soc_component_read_field(component,
+ WCD937X_ANA_MICB2,
+ WCD937X_ANA_MICB2_ENABLE_MASK);
+ if (val == WCD937X_MICB_ENABLE)
+ return true;
+ }
+ return false;
+}
+
+static void wcd937x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
+ int pull_up_cur)
+{
+ /* Default pull up current to 2uA */
+ if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA)
+ pull_up_cur = HS_PULLUP_I_2P0_UA;
+
+ snd_soc_component_write_field(component,
+ WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT,
+ WCD937X_HSDET_PULLUP_C_MASK, pull_up_cur);
+}
+
+static int wcd937x_mbhc_request_micbias(struct snd_soc_component *component,
+ int micb_num, int req)
+{
+ return wcd937x_micbias_control(component, micb_num, req, false);
+}
+
+static void wcd937x_mbhc_micb_ramp_control(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP,
+ WCD937X_RAMP_SHIFT_CTRL_MASK, 0x0C);
+ snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP,
+ WCD937X_RAMP_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP,
+ WCD937X_RAMP_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP,
+ WCD937X_RAMP_SHIFT_CTRL_MASK, 0);
+ }
+}
+
+static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+ int req_volt, int micb_num)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD937X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD937X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD937X_ANA_MICB3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&wcd937x->micb_lock);
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_en = snd_soc_component_read_field(component, micb_reg,
+ WCD937X_MICB_EN_MASK);
+ cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
+ WCD937X_MICB_VOUT_MASK);
+
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
+ if (req_vout_ctl < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (cur_vout_ctl == req_vout_ctl) {
+ ret = 0;
+ goto exit;
+ }
+
+ if (micb_en == WCD937X_MICB_ENABLE)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD937X_MICB_EN_MASK,
+ WCD937X_MICB_PULL_UP);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD937X_MICB_VOUT_MASK,
+ req_vout_ctl);
+
+ if (micb_en == WCD937X_MICB_ENABLE) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD937X_MICB_EN_MASK,
+ WCD937X_MICB_ENABLE);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+exit:
+ mutex_unlock(&wcd937x->micb_lock);
+ return ret;
+}
+
+static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
+ int micb_num, bool req_en)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (wcd937x->common.micb_mv[2] >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd937x->common.micb_mv[2];
+
+ return wcd937x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+}
+
+static void wcd937x_mbhc_get_result_params(struct snd_soc_component *component,
+ s16 *d1_a, u16 noff,
+ int32_t *zdet)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ int i;
+ int val, val1;
+ s16 c1;
+ s32 x1, d1;
+ s32 denom;
+ static const int minCode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+ };
+
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x20);
+ for (i = 0; i < WCD937X_ZDET_NUM_MEASUREMENTS; i++) {
+ regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_2, &val);
+ if (val & 0x80)
+ break;
+ }
+ val = val << 0x8;
+ regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_1, &val1);
+ val |= val1;
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x00);
+ x1 = WCD937X_MBHC_GET_X1(val);
+ c1 = WCD937X_MBHC_GET_C1(val);
+ /* If ramp is not complete, give additional 5ms */
+ if (c1 < 2 && x1)
+ usleep_range(5000, 5050);
+
+ if (!c1 || !x1) {
+ dev_err(component->dev, "Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ c1, x1);
+ goto ramp_down;
+ }
+ d1 = d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - noff));
+ if (denom > 0)
+ *zdet = (WCD937X_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < minCode_param[noff])
+ *zdet = WCD937X_ZDET_FLOATING_IMPEDANCE;
+
+ dev_err(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d (milliohm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+ while (x1) {
+ regmap_read(wcd937x->regmap,
+ WCD937X_ANA_MBHC_RESULT_1, &val);
+ regmap_read(wcd937x->regmap,
+ WCD937X_ANA_MBHC_RESULT_2, &val1);
+ val = val << 0x08;
+ val |= val1;
+ x1 = WCD937X_MBHC_GET_X1(val);
+ i++;
+ if (i == WCD937X_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+static void wcd937x_mbhc_zdet_ramp(struct snd_soc_component *component,
+ struct wcd937x_mbhc_zdet_param *zdet_param,
+ s32 *zl, s32 *zr, s16 *d1_a)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ s32 zdet = 0;
+
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD937X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl);
+ snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN5,
+ WCD937X_VTH_MASK, zdet_param->btn5);
+ snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN6,
+ WCD937X_VTH_MASK, zdet_param->btn6);
+ snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN7,
+ WCD937X_VTH_MASK, zdet_param->btn7);
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD937X_ZDET_RANGE_CTL_MASK, zdet_param->noff);
+ snd_soc_component_update_bits(component, WCD937X_MBHC_NEW_ZDET_RAMP_CTL,
+ 0x0F, zdet_param->nshift);
+
+ if (!zl)
+ goto z_right;
+ /* Start impedance measurement for HPH_L */
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_ZDET, 0x80, 0x80);
+ wcd937x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_ZDET, 0x80, 0x00);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+ /* Start impedance measurement for HPH_R */
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_ZDET, 0x40, 0x40);
+ wcd937x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_ZDET, 0x40, 0x00);
+
+ *zr = zdet;
+}
+
+static void wcd937x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ s32 *z_val, int flag_l_r)
+{
+ s16 q1;
+ int q1_cal;
+
+ if (*z_val < (WCD937X_ZDET_VAL_400 / 1000))
+ q1 = snd_soc_component_read(component,
+ WCD937X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r));
+ else
+ q1 = snd_soc_component_read(component,
+ WCD937X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r));
+ if (q1 & 0x80)
+ q1_cal = (10000 - ((q1 & 0x7F) * 25));
+ else
+ q1_cal = (10000 + (q1 * 25));
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void wcd937x_wcd_mbhc_calc_impedance(struct snd_soc_component *component,
+ u32 *zl, u32 *zr)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ s16 reg0, reg1, reg2, reg3, reg4;
+ s32 z1l, z1r, z1ls;
+ int zMono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ struct wcd937x_mbhc_zdet_param zdet_param[] = {
+ {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+ {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+ {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+ {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+ };
+ struct wcd937x_mbhc_zdet_param *zdet_param_ptr = NULL;
+ s16 d1_a[][4] = {
+ {0, 30, 90, 30},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ };
+ s16 *d1 = NULL;
+
+ reg0 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN5);
+ reg1 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN6);
+ reg2 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN7);
+ reg3 = snd_soc_component_read(component, WCD937X_MBHC_CTL_CLK);
+ reg4 = snd_soc_component_read(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL);
+
+ if (snd_soc_component_read(component, WCD937X_ANA_MBHC_ELECT) & 0x80) {
+ is_fsm_disable = true;
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_ELECT, 0x80, 0x00);
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (wcd937x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_MECH, 0x80, 0x00);
+
+ /* Turn off 100k pull down on HPHL */
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_MECH, 0x01, 0x00);
+
+ /* Disable surge protection before impedance detection.
+ * This is done to give correct value for high impedance.
+ */
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00);
+ /* 1ms delay needed after disable surge protection */
+ usleep_range(1000, 1010);
+
+ /* First get impedance on Left */
+ d1 = d1_a[1];
+ zdet_param_ptr = &zdet_param[1];
+ wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1l, NULL, d1);
+
+ if (!WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1l))
+ goto left_ch_impedance;
+
+ /* Second ramp for left ch */
+ if (z1l < WCD937X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1l > WCD937X_ZDET_VAL_400) &&
+ (z1l <= WCD937X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1l > WCD937X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1l, NULL, d1);
+
+left_ch_impedance:
+ if (z1l == WCD937X_ZDET_FLOATING_IMPEDANCE ||
+ z1l > WCD937X_ZDET_VAL_100K) {
+ *zl = WCD937X_ZDET_FLOATING_IMPEDANCE;
+ zdet_param_ptr = &zdet_param[1];
+ d1 = d1_a[1];
+ } else {
+ *zl = z1l / 1000;
+ wcd937x_wcd_mbhc_qfuse_cal(component, zl, 0);
+ }
+
+ /* Start of right impedance ramp and calculation */
+ wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1r, d1);
+ if (WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1r)) {
+ if ((z1r > WCD937X_ZDET_VAL_1200 &&
+ zdet_param_ptr->noff == 0x6) ||
+ ((*zl) != WCD937X_ZDET_FLOATING_IMPEDANCE))
+ goto right_ch_impedance;
+ /* Second ramp for right ch */
+ if (z1r < WCD937X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1r > WCD937X_ZDET_VAL_400) &&
+ (z1r <= WCD937X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1r > WCD937X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1r, d1);
+ }
+right_ch_impedance:
+ if (z1r == WCD937X_ZDET_FLOATING_IMPEDANCE ||
+ z1r > WCD937X_ZDET_VAL_100K) {
+ *zr = WCD937X_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1r / 1000;
+ wcd937x_wcd_mbhc_qfuse_cal(component, zr, 1);
+ }
+
+ /* Mono/stereo detection */
+ if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) &&
+ (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE)) {
+ dev_err(component->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+ if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) ||
+ (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE) ||
+ ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+ ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+ wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+ goto zdet_complete;
+ }
+ snd_soc_component_write_field(component, WCD937X_HPH_R_ATEST,
+ WCD937X_HPHPA_GND_OVR_MASK, 1);
+ snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2,
+ WCD937X_HPHPA_GND_R_MASK, 1);
+ if (*zl < (WCD937X_ZDET_VAL_32 / 1000))
+ wcd937x_mbhc_zdet_ramp(component, &zdet_param[0], &z1ls, NULL, d1);
+ else
+ wcd937x_mbhc_zdet_ramp(component, &zdet_param[1], &z1ls, NULL, d1);
+ snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2,
+ WCD937X_HPHPA_GND_R_MASK, 0);
+ snd_soc_component_write_field(component, WCD937X_HPH_R_ATEST,
+ WCD937X_HPHPA_GND_OVR_MASK, 0);
+ z1ls /= 1000;
+ wcd937x_wcd_mbhc_qfuse_cal(component, &z1ls, 0);
+ /* Parallel of left Z and 9 ohm pull down resistor */
+ zMono = ((*zl) * 9) / ((*zl) + 9);
+ z_diff1 = (z1ls > zMono) ? (z1ls - zMono) : (zMono - z1ls);
+ z_diff2 = ((*zl) > z1ls) ? ((*zl) - z1ls) : (z1ls - (*zl));
+ if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + zMono)))
+ wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_STEREO);
+ else
+ wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+
+ /* Enable surge protection again after impedance detection */
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0);
+zdet_complete:
+ snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN5, reg0);
+ snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN6, reg1);
+ snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN7, reg2);
+ /* Turn on 100k pull down on HPHL */
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_MECH, 0x01, 0x01);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (wcd937x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_MECH, 0x80, 0x80);
+
+ snd_soc_component_write(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+ snd_soc_component_write(component, WCD937X_MBHC_CTL_CLK, reg3);
+ if (is_fsm_disable)
+ regmap_update_bits(wcd937x->regmap,
+ WCD937X_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+
+static void wcd937x_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH,
+ WCD937X_MBHC_HSG_PULLUP_COMP_EN, 1);
+ snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH,
+ WCD937X_MBHC_GND_DET_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH,
+ WCD937X_MBHC_GND_DET_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH,
+ WCD937X_MBHC_HSG_PULLUP_COMP_EN, 0);
+ }
+}
+
+static void wcd937x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2,
+ WCD937X_HPHPA_GND_R_MASK, enable);
+ snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2,
+ WCD937X_HPHPA_GND_L_MASK, enable);
+}
+
+static void wcd937x_mbhc_moisture_config(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ if (wcd937x->mbhc_cfg.moist_rref == R_OFF) {
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, R_OFF);
+ return;
+ }
+
+ /* Do not enable moisture detection if jack type is NC */
+ if (!wcd937x->mbhc_cfg.hphl_swh) {
+ dev_err(component->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, R_OFF);
+ return;
+ }
+
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, wcd937x->mbhc_cfg.moist_rref);
+}
+
+static void wcd937x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ if (enable)
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, wcd937x->mbhc_cfg.moist_rref);
+ else
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, R_OFF);
+}
+
+static bool wcd937x_mbhc_get_moisture_status(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ bool ret = false;
+
+ if (wcd937x->mbhc_cfg.moist_rref == R_OFF) {
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, R_OFF);
+ goto done;
+ }
+
+ /* Do not enable moisture detection if jack type is NC */
+ if (!wcd937x->mbhc_cfg.hphl_swh) {
+ dev_err(component->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2,
+ WCD937X_M_RTH_CTL_MASK, R_OFF);
+ goto done;
+ }
+
+ /*
+ * If moisture_en is already enabled, then skip to plug type
+ * detection.
+ */
+ if (snd_soc_component_read_field(component, WCD937X_MBHC_NEW_CTL_2, WCD937X_M_RTH_CTL_MASK))
+ goto done;
+
+ wcd937x_mbhc_moisture_detect_en(component, true);
+ /* Read moisture comparator status */
+ ret = ((snd_soc_component_read(component, WCD937X_MBHC_NEW_FSM_STATUS)
+ & 0x20) ? 0 : 1);
+done:
+ return ret;
+}
+
+static void wcd937x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component,
+ WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL,
+ WCD937X_MOISTURE_EN_POLLING_MASK, enable);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .clk_setup = wcd937x_mbhc_clk_setup,
+ .mbhc_bias = wcd937x_mbhc_mbhc_bias_control,
+ .set_btn_thr = wcd937x_mbhc_program_btn_thr,
+ .micbias_enable_status = wcd937x_mbhc_micb_en_status,
+ .hph_pull_up_control_v2 = wcd937x_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = wcd937x_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = wcd937x_mbhc_micb_ramp_control,
+ .mbhc_micb_ctrl_thr_mic = wcd937x_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = wcd937x_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = wcd937x_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = wcd937x_mbhc_hph_pull_down_ctrl,
+ .mbhc_moisture_config = wcd937x_mbhc_moisture_config,
+ .mbhc_get_moisture_status = wcd937x_mbhc_get_moisture_status,
+ .mbhc_moisture_polling_ctrl = wcd937x_mbhc_moisture_polling_ctrl,
+ .mbhc_moisture_detect_en = wcd937x_mbhc_moisture_detect_en,
+};
+
+static int wcd937x_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd937x->wcd_mbhc);
+
+ return 0;
+}
+
+static int wcd937x_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 zl, zr;
+ bool hphr;
+ struct soc_mixer_control *mc;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+ wcd_mbhc_get_impedance(wcd937x->wcd_mbhc, &zl, &zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0,
+ wcd937x_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0,
+ wcd937x_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0,
+ wcd937x_hph_impedance_get, NULL),
+};
+
+static int wcd937x_mbhc_init(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &wcd937x->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_MBHC_SW_DET);
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_MBHC_BUTTON_PRESS_DET);
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET);
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_MBHC_ELECT_INS_REM_DET);
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_HPHL_OCP_INT);
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_HPHR_OCP_INT);
+
+ wcd937x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+ if (IS_ERR(wcd937x->wcd_mbhc))
+ return PTR_ERR(wcd937x->wcd_mbhc);
+
+ snd_soc_add_component_controls(component, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_component_controls(component, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ return 0;
+}
+
+static void wcd937x_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(wcd937x->wcd_mbhc);
+}
+
+/* END MBHC */
+
+static const struct snd_kcontrol_new wcd937x_snd_controls[] = {
+ SOC_SINGLE_TLV("EAR_PA Volume", WCD937X_ANA_EAR_COMPANDER_CTL,
+ 2, 0x10, 0, ear_pa_gain),
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ wcd937x_rx_hph_mode_get, wcd937x_rx_hph_mode_put),
+
+ SOC_SINGLE_EXT("HPHL_COMP Switch", WCD937X_COMP_L, 0, 1, 0,
+ wcd937x_get_compander, wcd937x_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", WCD937X_COMP_R, 1, 1, 0,
+ wcd937x_get_compander, wcd937x_set_compander),
+
+ SOC_SINGLE_TLV("HPHL Volume", WCD937X_HPH_L_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD937X_HPH_R_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", WCD937X_ANA_TX_CH1, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD937X_ANA_TX_CH2, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD937X_ANA_TX_CH3, 0, 20, 0, analog_gain),
+
+ SOC_SINGLE_EXT("HPHL Switch", WCD937X_HPH_L, 0, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", WCD937X_HPH_R, 0, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("LO Switch", WCD937X_LO, 0, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+
+ SOC_SINGLE_EXT("ADC1 Switch", WCD937X_ADC1, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", WCD937X_ADC2, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("ADC3 Switch", WCD937X_ADC3, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC0 Switch", WCD937X_DMIC0, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC1 Switch", WCD937X_DMIC1, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("MBHC Switch", WCD937X_MBHC, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC2 Switch", WCD937X_DMIC2, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC3 Switch", WCD937X_DMIC3, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC4 Switch", WCD937X_DMIC4, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC5 Switch", WCD937X_DMIC5, 1, 1, 0,
+ wcd937x_get_swr_port, wcd937x_set_swr_port),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc3_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic3_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic4_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic5_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic6_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new aux_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc2_mux_text[] = {
+ "INP2", "INP3"
+};
+
+static const char * const rdac3_mux_text[] = {
+ "RX1", "RX3"
+};
+
+static const struct soc_enum adc2_enum =
+ SOC_ENUM_SINGLE(WCD937X_TX_NEW_TX_CH2_SEL, 7,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct soc_enum rdac3_enum =
+ SOC_ENUM_SINGLE(WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0,
+ ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_kcontrol_new rx_rdac3_mux = SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum);
+
+static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = {
+ /* Input widgets */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+ SND_SOC_DAPM_INPUT("IN3_AUX"),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd937x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0,
+ wcd937x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wcd937x_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wcd937x_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+ /* TX mixers */
+ SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0,
+ adc1_switch, ARRAY_SIZE(adc1_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 1, 0,
+ adc2_switch, ARRAY_SIZE(adc2_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* MIC_BIAS widgets */
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd937x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd937x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd937x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* RX widgets */
+ SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0,
+ wcd937x_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("AUX PGA", WCD937X_AUX_AUXPA, 7, 0, NULL, 0,
+ wcd937x_codec_enable_aux_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", WCD937X_ANA_HPH, 7, 0, NULL, 0,
+ wcd937x_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", WCD937X_ANA_HPH, 6, 0, NULL, 0,
+ wcd937x_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd937x_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0,
+ wcd937x_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0,
+ wcd937x_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC4", NULL, SND_SOC_NOPM, 0, 0,
+ wcd937x_codec_aux_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux),
+
+ SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wcd937x_enable_rx1, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wcd937x_enable_rx2, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wcd937x_enable_rx3, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* RX mixer widgets*/
+ SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0,
+ ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)),
+ SND_SOC_DAPM_MIXER("AUX_RDAC", SND_SOC_NOPM, 0, 0,
+ aux_rdac_switch, ARRAY_SIZE(aux_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0,
+ hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0,
+ hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)),
+
+ /* TX output widgets */
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("WCD_TX_OUTPUT"),
+
+ /* RX output widgets */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("AUX"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+
+ /* MIC_BIAS pull up widgets */
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd937x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd937x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd937x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_widget wcd9375_dapm_widgets[] = {
+ /* Input widgets */
+ SND_SOC_DAPM_INPUT("AMIC4"),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0,
+ wcd937x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wcd937x_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd937x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
+ wcd937x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0,
+ wcd937x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0,
+ wcd937x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0,
+ wcd937x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0,
+ wcd937x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX mixer widgets */
+ SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0,
+ 0, dmic1_switch, ARRAY_SIZE(dmic1_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 1,
+ 0, dmic2_switch, ARRAY_SIZE(dmic2_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 2,
+ 0, dmic3_switch, ARRAY_SIZE(dmic3_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 3,
+ 0, dmic4_switch, ARRAY_SIZE(dmic4_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 4,
+ 0, dmic5_switch, ARRAY_SIZE(dmic5_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 5,
+ 0, dmic6_switch, ARRAY_SIZE(dmic6_switch),
+ wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 2, 0, adc3_switch,
+ ARRAY_SIZE(adc3_switch), wcd937x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output widgets */
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route wcd937x_audio_map[] = {
+ { "ADC1_OUTPUT", NULL, "ADC1_MIXER" },
+ { "ADC1_MIXER", "Switch", "ADC1 REQ" },
+ { "ADC1 REQ", NULL, "ADC1" },
+ { "ADC1", NULL, "AMIC1" },
+
+ { "ADC2_OUTPUT", NULL, "ADC2_MIXER" },
+ { "ADC2_MIXER", "Switch", "ADC2 REQ" },
+ { "ADC2 REQ", NULL, "ADC2" },
+ { "ADC2", NULL, "ADC2 MUX" },
+ { "ADC2 MUX", "INP3", "AMIC3" },
+ { "ADC2 MUX", "INP2", "AMIC2" },
+
+ { "IN1_HPHL", NULL, "VDD_BUCK" },
+ { "IN1_HPHL", NULL, "CLS_H_PORT" },
+ { "RX1", NULL, "IN1_HPHL" },
+ { "RDAC1", NULL, "RX1" },
+ { "HPHL_RDAC", "Switch", "RDAC1" },
+ { "HPHL PGA", NULL, "HPHL_RDAC" },
+ { "HPHL", NULL, "HPHL PGA" },
+
+ { "IN2_HPHR", NULL, "VDD_BUCK" },
+ { "IN2_HPHR", NULL, "CLS_H_PORT" },
+ { "RX2", NULL, "IN2_HPHR" },
+ { "RDAC2", NULL, "RX2" },
+ { "HPHR_RDAC", "Switch", "RDAC2" },
+ { "HPHR PGA", NULL, "HPHR_RDAC" },
+ { "HPHR", NULL, "HPHR PGA" },
+
+ { "IN3_AUX", NULL, "VDD_BUCK" },
+ { "IN3_AUX", NULL, "CLS_H_PORT" },
+ { "RX3", NULL, "IN3_AUX" },
+ { "RDAC4", NULL, "RX3" },
+ { "AUX_RDAC", "Switch", "RDAC4" },
+ { "AUX PGA", NULL, "AUX_RDAC" },
+ { "AUX", NULL, "AUX PGA" },
+
+ { "RDAC3_MUX", "RX3", "RX3" },
+ { "RDAC3_MUX", "RX1", "RX1" },
+ { "RDAC3", NULL, "RDAC3_MUX" },
+ { "EAR_RDAC", "Switch", "RDAC3" },
+ { "EAR PGA", NULL, "EAR_RDAC" },
+ { "EAR", NULL, "EAR PGA" },
+};
+
+static const struct snd_soc_dapm_route wcd9375_audio_map[] = {
+ { "ADC3_OUTPUT", NULL, "ADC3_MIXER" },
+ { "ADC3_OUTPUT", NULL, "ADC3_MIXER" },
+ { "ADC3_MIXER", "Switch", "ADC3 REQ" },
+ { "ADC3 REQ", NULL, "ADC3" },
+ { "ADC3", NULL, "AMIC4" },
+
+ { "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" },
+ { "DMIC1_MIXER", "Switch", "DMIC1" },
+
+ { "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" },
+ { "DMIC2_MIXER", "Switch", "DMIC2" },
+
+ { "DMIC3_OUTPUT", NULL, "DMIC3_MIXER" },
+ { "DMIC3_MIXER", "Switch", "DMIC3" },
+
+ { "DMIC4_OUTPUT", NULL, "DMIC4_MIXER" },
+ { "DMIC4_MIXER", "Switch", "DMIC4" },
+
+ { "DMIC5_OUTPUT", NULL, "DMIC5_MIXER" },
+ { "DMIC5_MIXER", "Switch", "DMIC5" },
+
+ { "DMIC6_OUTPUT", NULL, "DMIC6_MIXER" },
+ { "DMIC6_MIXER", "Switch", "DMIC6" },
+};
+
+static void wcd937x_set_micbias_data(struct device *dev, struct wcd937x_priv *wcd937x)
+{
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB1, WCD937X_ANA_MICB_VOUT,
+ wcd937x->common.micb_vout[0]);
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB2, WCD937X_ANA_MICB_VOUT,
+ wcd937x->common.micb_vout[1]);
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB3, WCD937X_ANA_MICB_VOUT,
+ wcd937x->common.micb_vout[2]);
+}
+
+static irqreturn_t wcd937x_wd_handle_irq(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+static const struct irq_chip wcd_irq_chip = {
+ .name = "WCD937x",
+};
+
+static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops wcd_domain_ops = {
+ .map = wcd_irq_chip_map,
+};
+
+static int wcd937x_irq_init(struct wcd937x_priv *wcd, struct device *dev)
+{
+ wcd->virq = irq_domain_create_linear(NULL, 1, &wcd_domain_ops, NULL);
+ if (!(wcd->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ return devm_regmap_add_irq_chip(dev, wcd->regmap,
+ irq_create_mapping(wcd->virq, 0),
+ IRQF_ONESHOT, 0, &wcd937x_regmap_irq_chip,
+ &wcd->irq_chip);
+}
+
+static int wcd937x_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = wcd937x->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ int i, ret;
+ u32 chipid;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(5000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, wcd937x->regmap);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ chipid = (snd_soc_component_read(component,
+ WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1;
+ if (chipid != CHIPID_WCD9370 && chipid != CHIPID_WCD9375) {
+ dev_err(dev, "Got unknown chip id: 0x%x\n", chipid);
+ pm_runtime_put(dev);
+ return -EINVAL;
+ }
+
+ wcd937x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD937X);
+ if (IS_ERR(wcd937x->clsh_info)) {
+ pm_runtime_put(dev);
+ return PTR_ERR(wcd937x->clsh_info);
+ }
+
+ wcd937x_io_init(wcd937x->regmap);
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < wcd937x_regmap_irq_chip.num_regs; i++)
+ regmap_write(wcd937x->regmap, (WCD937X_DIGITAL_INTR_LEVEL_0 + i), 0);
+
+ pm_runtime_put(dev);
+
+ wcd937x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_HPHR_PDM_WD_INT);
+ wcd937x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_HPHL_PDM_WD_INT);
+ wcd937x->aux_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip,
+ WCD937X_IRQ_AUX_PDM_WD_INT);
+
+ /* Request for watchdog interrupt */
+ ret = devm_request_threaded_irq(dev, wcd937x->hphr_pdm_wd_int, NULL, wcd937x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WDOG INT", wcd937x);
+ if (ret)
+ dev_err(dev, "Failed to request HPHR watchdog interrupt (%d)\n", ret);
+
+ ret = devm_request_threaded_irq(dev, wcd937x->hphl_pdm_wd_int, NULL, wcd937x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WDOG INT", wcd937x);
+ if (ret)
+ dev_err(dev, "Failed to request HPHL watchdog interrupt (%d)\n", ret);
+
+ ret = devm_request_threaded_irq(dev, wcd937x->aux_pdm_wd_int, NULL, wcd937x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "AUX PDM WDOG INT", wcd937x);
+ if (ret)
+ dev_err(dev, "Failed to request Aux watchdog interrupt (%d)\n", ret);
+
+ /* Disable watchdog interrupt for HPH and AUX */
+ disable_irq_nosync(wcd937x->hphr_pdm_wd_int);
+ disable_irq_nosync(wcd937x->hphl_pdm_wd_int);
+ disable_irq_nosync(wcd937x->aux_pdm_wd_int);
+
+ if (chipid == CHIPID_WCD9375) {
+ ret = snd_soc_dapm_new_controls(dapm, wcd9375_dapm_widgets,
+ ARRAY_SIZE(wcd9375_dapm_widgets));
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to add snd_ctls\n");
+ wcd_clsh_ctrl_free(wcd937x->clsh_info);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, wcd9375_audio_map,
+ ARRAY_SIZE(wcd9375_audio_map));
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to add routes\n");
+ wcd_clsh_ctrl_free(wcd937x->clsh_info);
+ return ret;
+ }
+ }
+
+ ret = wcd937x_mbhc_init(component);
+ if (ret)
+ dev_err(component->dev, "mbhc initialization failed\n");
+
+ return ret;
+}
+
+static void wcd937x_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
+
+ wcd937x_mbhc_deinit(component);
+ free_irq(wcd937x->aux_pdm_wd_int, wcd937x);
+ free_irq(wcd937x->hphl_pdm_wd_int, wcd937x);
+ free_irq(wcd937x->hphr_pdm_wd_int, wcd937x);
+
+ wcd_clsh_ctrl_free(wcd937x->clsh_info);
+}
+
+static int wcd937x_codec_set_jack(struct snd_soc_component *comp,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct wcd937x_priv *wcd = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (jack)
+ ret = wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack);
+ else
+ wcd_mbhc_stop(wcd->wcd_mbhc);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_wcd937x = {
+ .name = "wcd937x_codec",
+ .probe = wcd937x_soc_codec_probe,
+ .remove = wcd937x_soc_codec_remove,
+ .controls = wcd937x_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd937x_snd_controls),
+ .dapm_widgets = wcd937x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wcd937x_dapm_widgets),
+ .dapm_routes = wcd937x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wcd937x_audio_map),
+ .set_jack = wcd937x_codec_set_jack,
+ .endianness = 1,
+};
+
+static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component)
+{
+ int value;
+ struct wcd937x_priv *wcd937x;
+
+ wcd937x = snd_soc_component_get_drvdata(component);
+
+ value = gpiod_get_value(wcd937x->us_euro_gpio);
+ gpiod_set_value(wcd937x->us_euro_gpio, !value);
+
+ return true;
+}
+
+static int wcd937x_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev);
+ struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id];
+
+ return wcd937x_sdw_hw_params(wcd, substream, params, dai);
+}
+
+static int wcd937x_codec_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev);
+ struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id];
+
+ return sdw_stream_remove_slave(wcd->sdev, wcd->sruntime);
+}
+
+static int wcd937x_codec_set_sdw_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev);
+ struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id];
+
+ wcd->sruntime = stream;
+
+ return 0;
+}
+
+static int wcd937x_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev);
+ struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n",
+ rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ rx_slot[i] = wcd->master_channel_map[i];
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n",
+ tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ tx_slot[i] = wcd->master_channel_map[i];
+
+ *tx_num = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wcd937x_sdw_dai_ops = {
+ .hw_params = wcd937x_codec_hw_params,
+ .hw_free = wcd937x_codec_free,
+ .set_stream = wcd937x_codec_set_sdw_stream,
+ .get_channel_map = wcd937x_get_channel_map,
+};
+
+static struct snd_soc_dai_driver wcd937x_dais[] = {
+ [0] = {
+ .name = "wcd937x-sdw-rx",
+ .playback = {
+ .stream_name = "WCD AIF Playback",
+ .rates = WCD937X_RATES | WCD937X_FRAC_RATES,
+ .formats = WCD937X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd937x_sdw_dai_ops,
+ },
+ [1] = {
+ .name = "wcd937x-sdw-tx",
+ .capture = {
+ .stream_name = "WCD AIF Capture",
+ .rates = WCD937X_RATES,
+ .formats = WCD937X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd937x_sdw_dai_ops,
+ },
+};
+
+static int wcd937x_bind(struct device *dev)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dev);
+ int ret;
+
+ /* Give the SDW subdevices some more time to settle */
+ usleep_range(5000, 5010);
+
+ ret = component_bind_all(dev, wcd937x);
+ if (ret) {
+ dev_err(dev, "Slave bind failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ wcd937x->rxdev = of_sdw_find_device_by_node(wcd937x->rxnode);
+ if (!wcd937x->rxdev) {
+ dev_err(dev, "could not find slave with matching of node\n");
+ ret = -EINVAL;
+ goto err_component_unbind;
+ }
+
+ wcd937x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd937x->rxdev);
+ wcd937x->sdw_priv[AIF1_PB]->wcd937x = wcd937x;
+
+ wcd937x->txdev = of_sdw_find_device_by_node(wcd937x->txnode);
+ if (!wcd937x->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto err_put_rxdev;
+ }
+
+ wcd937x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd937x->txdev);
+ wcd937x->sdw_priv[AIF1_CAP]->wcd937x = wcd937x;
+ wcd937x->tx_sdw_dev = dev_to_sdw_dev(wcd937x->txdev);
+ if (!wcd937x->tx_sdw_dev) {
+ dev_err(dev, "could not get txslave with matching of dev\n");
+ ret = -EINVAL;
+ goto err_put_txdev;
+ }
+
+ /*
+ * As TX is the main CSR reg interface, which should not be suspended first.
+ * expicilty add the dependency link
+ */
+ if (!device_link_add(wcd937x->rxdev, wcd937x->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "Could not devlink TX and RX\n");
+ ret = -EINVAL;
+ goto err_put_txdev;
+ }
+
+ if (!device_link_add(dev, wcd937x->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "Could not devlink WCD and TX\n");
+ ret = -EINVAL;
+ goto err_remove_link1;
+ }
+
+ if (!device_link_add(dev, wcd937x->rxdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "Could not devlink WCD and RX\n");
+ ret = -EINVAL;
+ goto err_remove_link2;
+ }
+
+ wcd937x->regmap = wcd937x->sdw_priv[AIF1_CAP]->regmap;
+ if (!wcd937x->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -EINVAL;
+ goto err_remove_link3;
+ }
+
+ ret = wcd937x_irq_init(wcd937x, dev);
+ if (ret) {
+ dev_err(dev, "IRQ init failed: %d\n", ret);
+ goto err_remove_link3;
+ }
+
+ wcd937x->sdw_priv[AIF1_PB]->slave_irq = wcd937x->virq;
+ wcd937x->sdw_priv[AIF1_CAP]->slave_irq = wcd937x->virq;
+
+ wcd937x_set_micbias_data(dev, wcd937x);
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_wcd937x,
+ wcd937x_dais, ARRAY_SIZE(wcd937x_dais));
+ if (ret) {
+ dev_err(dev, "Codec registration failed\n");
+ goto err_remove_link3;
+ }
+
+ return ret;
+
+err_remove_link3:
+ device_link_remove(dev, wcd937x->rxdev);
+err_remove_link2:
+ device_link_remove(dev, wcd937x->txdev);
+err_remove_link1:
+ device_link_remove(wcd937x->rxdev, wcd937x->txdev);
+err_put_txdev:
+ put_device(wcd937x->txdev);
+err_put_rxdev:
+ put_device(wcd937x->rxdev);
+err_component_unbind:
+ component_unbind_all(dev, wcd937x);
+ return ret;
+}
+
+static void wcd937x_unbind(struct device *dev)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ device_link_remove(dev, wcd937x->txdev);
+ device_link_remove(dev, wcd937x->rxdev);
+ device_link_remove(wcd937x->rxdev, wcd937x->txdev);
+ component_unbind_all(dev, wcd937x);
+ mutex_destroy(&wcd937x->micb_lock);
+ put_device(wcd937x->txdev);
+ put_device(wcd937x->rxdev);
+}
+
+static const struct component_master_ops wcd937x_comp_ops = {
+ .bind = wcd937x_bind,
+ .unbind = wcd937x_unbind,
+};
+
+static int wcd937x_add_slave_components(struct wcd937x_priv *wcd937x,
+ struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np = dev->of_node;
+
+ wcd937x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!wcd937x->rxnode) {
+ dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n");
+ return -ENODEV;
+ }
+ of_node_get(wcd937x->rxnode);
+ component_match_add_release(dev, matchptr, component_release_of,
+ component_compare_of, wcd937x->rxnode);
+
+ wcd937x->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!wcd937x->txnode) {
+ dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n");
+ return -ENODEV;
+ }
+ of_node_get(wcd937x->txnode);
+ component_match_add_release(dev, matchptr, component_release_of,
+ component_compare_of, wcd937x->txnode);
+
+ return 0;
+}
+
+static int wcd937x_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct device *dev = &pdev->dev;
+ struct wcd937x_priv *wcd937x;
+ struct wcd_mbhc_config *cfg;
+ int ret;
+
+ wcd937x = devm_kzalloc(dev, sizeof(*wcd937x), GFP_KERNEL);
+ if (!wcd937x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, wcd937x);
+ mutex_init(&wcd937x->micb_lock);
+ wcd937x->common.dev = dev;
+ wcd937x->common.max_bias = 3;
+
+ wcd937x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd937x->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd937x->reset_gpio),
+ "failed to reset wcd gpio\n");
+
+ wcd937x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd937x->us_euro_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd937x->us_euro_gpio),
+ "us-euro swap Control GPIO not found\n");
+
+ cfg = &wcd937x->mbhc_cfg;
+ cfg->swap_gnd_mic = wcd937x_swap_gnd_mic;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd937x_supplies),
+ wcd937x_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ ret = wcd_dt_parse_micbias_info(&wcd937x->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get micbias\n");
+
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = WCD937X_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = wcd937x->common.micb_mv[2];
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, &wcd937x->mbhc_cfg);
+
+ ret = wcd937x_add_slave_components(wcd937x, dev, &match);
+ if (ret)
+ return ret;
+
+ wcd937x_reset(wcd937x);
+
+ ret = component_master_add_with_match(dev, &wcd937x_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void wcd937x_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_master_del(&pdev->dev, &wcd937x_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id wcd937x_of_match[] = {
+ { .compatible = "qcom,wcd9370-codec" },
+ { .compatible = "qcom,wcd9375-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wcd937x_of_match);
+#endif
+
+static struct platform_driver wcd937x_codec_driver = {
+ .probe = wcd937x_probe,
+ .remove = wcd937x_remove,
+ .driver = {
+ .name = "wcd937x_codec",
+ .of_match_table = of_match_ptr(wcd937x_of_match),
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(wcd937x_codec_driver);
+MODULE_DESCRIPTION("WCD937X Codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h
new file mode 100644
index 000000000000..3d0ba3cc0ee6
--- /dev/null
+++ b/sound/soc/codecs/wcd937x.h
@@ -0,0 +1,618 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _WCD937X_REGISTERS_H
+#define _WCD937X_REGISTERS_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include "wcd-common.h"
+
+#define WCD937X_BASE_ADDRESS 0x3000
+#define WCD937X_ANA_BIAS 0x3001
+#define WCD937X_ANA_RX_SUPPLIES 0x3008
+#define WCD937X_ANA_HPH 0x3009
+#define WCD937X_ANA_EAR 0x300A
+#define WCD937X_ANA_EAR_COMPANDER_CTL 0x300B
+#define WCD937X_EAR_GAIN_MASK GENMASK(6, 2)
+#define WCD937X_ANA_TX_CH1 0x300E
+#define WCD937X_ANA_TX_CH2 0x300F
+#define WCD937X_ANA_TX_CH3 0x3010
+#define WCD937X_ANA_TX_CH3_HPF 0x3011
+#define WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC 0x3012
+#define WCD937X_ANA_MICB3_DSP_EN_LOGIC 0x3013
+#define WCD937X_ANA_MBHC_MECH 0x3014
+#define WCD937X_MBHC_L_DET_EN_MASK BIT(7)
+#define WCD937X_MBHC_L_DET_EN BIT(7)
+#define WCD937X_MBHC_GND_DET_EN_MASK BIT(6)
+#define WCD937X_MBHC_MECH_DETECT_TYPE_MASK BIT(5)
+#define WCD937X_MBHC_MECH_DETECT_TYPE_INS 1
+#define WCD937X_MBHC_HPHL_PLUG_TYPE_MASK BIT(4)
+#define WCD937X_MBHC_HPHL_PLUG_TYPE_NO 1
+#define WCD937X_MBHC_GND_PLUG_TYPE_MASK BIT(3)
+#define WCD937X_MBHC_GND_PLUG_TYPE_NO 1
+#define WCD937X_MBHC_HSL_PULLUP_COMP_EN BIT(2)
+#define WCD937X_MBHC_HSG_PULLUP_COMP_EN BIT(1)
+#define WCD937X_MBHC_HPHL_100K_TO_GND_EN BIT(0)
+#define WCD937X_ANA_MBHC_ELECT 0x3015
+#define WCD937X_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4)
+#define WCD937X_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4)
+#define WCD937X_ANA_MBHC_BD_ISRC_OFF 0
+#define WCD937X_ANA_MBHC_BIAS_EN_MASK BIT(0)
+#define WCD937X_ANA_MBHC_BIAS_EN BIT(0)
+#define WCD937X_ANA_MBHC_ZDET 0x3016
+#define WCD937X_ANA_MBHC_RESULT_1 0x3017
+#define WCD937X_ANA_MBHC_RESULT_2 0x3018
+#define WCD937X_ANA_MBHC_RESULT_3 0x3019
+#define WCD937X_MBHC_BTN_RESULT_MASK GENMASK(2, 0)
+#define WCD937X_ANA_MBHC_BTN0 0x301A
+#define WCD937X_MBHC_BTN_VTH_MASK GENMASK(7, 2)
+#define WCD937X_ANA_MBHC_BTN1 0x301B
+#define WCD937X_ANA_MBHC_BTN2 0x301C
+#define WCD937X_ANA_MBHC_BTN3 0x301D
+#define WCD937X_ANA_MBHC_BTN4 0x301E
+#define WCD937X_ANA_MBHC_BTN5 0x301F
+#define WCD937X_VTH_MASK GENMASK(7, 2)
+#define WCD937X_ANA_MBHC_BTN6 0x3020
+#define WCD937X_ANA_MBHC_BTN7 0x3021
+#define WCD937X_ANA_MICB1 0x3022
+#define WCD937X_MICB_VOUT_MASK GENMASK(5, 0)
+#define WCD937X_MICB_EN_MASK GENMASK(7, 6)
+#define WCD937X_MICB_DISABLE 0
+#define WCD937X_MICB_ENABLE 1
+#define WCD937X_MICB_PULL_UP 2
+#define WCD937X_MICB_PULL_DOWN 3
+#define WCD937X_ANA_MICB2 0x3023
+#define WCD937X_ANA_MICB2_ENABLE BIT(6)
+#define WCD937X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6)
+#define WCD937X_ANA_MICB2_VOUT_MASK GENMASK(5, 0)
+#define WCD937X_ANA_MICB2_RAMP 0x3024
+#define WCD937X_RAMP_EN_MASK BIT(7)
+#define WCD937X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2)
+#define WCD937X_ANA_MICB3 0x3025
+#define WCD937X_ANA_MICB_EN GENMASK(7, 6)
+#define WCD937X_MICB_DISABLE 0
+#define WCD937X_MICB_ENABLE 1
+#define WCD937X_MICB_PULL_UP 2
+#define WCD937X_ANA_MICB_VOUT GENMASK(5, 0)
+#define WCD937X_BIAS_CTL 0x3028
+#define WCD937X_BIAS_VBG_FINE_ADJ 0x3029
+#define WCD937X_LDOL_VDDCX_ADJUST 0x3040
+#define WCD937X_LDOL_DISABLE_LDOL 0x3041
+#define WCD937X_MBHC_CTL_CLK 0x3056
+#define WCD937X_MBHC_CTL_ANA 0x3057
+#define WCD937X_MBHC_CTL_SPARE_1 0x3058
+#define WCD937X_MBHC_CTL_SPARE_2 0x3059
+#define WCD937X_MBHC_CTL_BCS 0x305A
+#define WCD937X_MBHC_MOISTURE_DET_FSM_STATUS 0x305B
+#define WCD937X_MBHC_TEST_CTL 0x305C
+#define WCD937X_LDOH_MODE 0x3067
+#define WCD937X_LDOH_BIAS 0x3068
+#define WCD937X_LDOH_STB_LOADS 0x3069
+#define WCD937X_LDOH_SLOWRAMP 0x306A
+#define WCD937X_MICB1_TEST_CTL_1 0x306B
+#define WCD937X_MICB1_TEST_CTL_2 0x306C
+#define WCD937X_MICB1_TEST_CTL_3 0x306D
+#define WCD937X_MICB2_TEST_CTL_1 0x306E
+#define WCD937X_MICB2_TEST_CTL_2 0x306F
+#define WCD937X_MICB2_TEST_CTL_3 0x3070
+#define WCD937X_MICB3_TEST_CTL_1 0x3071
+#define WCD937X_MICB3_TEST_CTL_2 0x3072
+#define WCD937X_MICB3_TEST_CTL_3 0x3073
+#define WCD937X_TX_COM_ADC_VCM 0x3077
+#define WCD937X_TX_COM_BIAS_ATEST 0x3078
+#define WCD937X_TX_COM_ADC_INT1_IB 0x3079
+#define WCD937X_TX_COM_ADC_INT2_IB 0x307A
+#define WCD937X_TX_COM_TXFE_DIV_CTL 0x307B
+#define WCD937X_TX_COM_TXFE_DIV_START 0x307C
+#define WCD937X_TX_COM_TXFE_DIV_STOP_9P6M 0x307D
+#define WCD937X_TX_COM_TXFE_DIV_STOP_12P288M 0x307E
+#define WCD937X_TX_1_2_TEST_EN 0x307F
+#define WCD937X_TX_1_2_ADC_IB 0x3080
+#define WCD937X_TX_1_2_ATEST_REFCTL 0x3081
+#define WCD937X_TX_1_2_TEST_CTL 0x3082
+#define WCD937X_TX_1_2_TEST_BLK_EN 0x3083
+#define WCD937X_TX_1_2_TXFE_CLKDIV 0x3084
+#define WCD937X_TX_1_2_SAR2_ERR 0x3085
+#define WCD937X_TX_1_2_SAR1_ERR 0x3086
+#define WCD937X_TX_3_TEST_EN 0x3087
+#define WCD937X_TX_3_ADC_IB 0x3088
+#define WCD937X_TX_3_ATEST_REFCTL 0x3089
+#define WCD937X_TX_3_TEST_CTL 0x308A
+#define WCD937X_TX_3_TEST_BLK_EN 0x308B
+#define WCD937X_TX_3_TXFE_CLKDIV 0x308C
+#define WCD937X_TX_3_SPARE_MONO 0x308D
+#define WCD937X_TX_3_SAR1_ERR 0x308E
+#define WCD937X_CLASSH_MODE_1 0x3097
+#define WCD937X_CLASSH_MODE_2 0x3098
+#define WCD937X_CLASSH_MODE_3 0x3099
+#define WCD937X_CLASSH_CTRL_VCL_1 0x309A
+#define WCD937X_CLASSH_CTRL_VCL_2 0x309B
+#define WCD937X_CLASSH_CTRL_CCL_1 0x309C
+#define WCD937X_CLASSH_CTRL_CCL_2 0x309D
+#define WCD937X_CLASSH_CTRL_CCL_3 0x309E
+#define WCD937X_CLASSH_CTRL_CCL_4 0x309F
+#define WCD937X_CLASSH_CTRL_CCL_5 0x30A0
+#define WCD937X_CLASSH_BUCK_TMUX_A_D 0x30A1
+#define WCD937X_CLASSH_BUCK_SW_DRV_CNTL 0x30A2
+#define WCD937X_CLASSH_SPARE 0x30A3
+#define WCD937X_FLYBACK_EN 0x30A4
+#define WCD937X_FLYBACK_VNEG_CTRL_1 0x30A5
+#define WCD937X_FLYBACK_VNEG_CTRL_2 0x30A6
+#define WCD937X_FLYBACK_VNEG_CTRL_3 0x30A7
+#define WCD937X_FLYBACK_VNEG_CTRL_4 0x30A8
+#define WCD937X_FLYBACK_VNEG_CTRL_5 0x30A9
+#define WCD937X_FLYBACK_VNEG_CTRL_6 0x30AA
+#define WCD937X_FLYBACK_VNEG_CTRL_7 0x30AB
+#define WCD937X_FLYBACK_VNEG_CTRL_8 0x30AC
+#define WCD937X_FLYBACK_VNEG_CTRL_9 0x30AD
+#define WCD937X_FLYBACK_VNEGDAC_CTRL_1 0x30AE
+#define WCD937X_FLYBACK_VNEGDAC_CTRL_2 0x30AF
+#define WCD937X_FLYBACK_VNEGDAC_CTRL_3 0x30B0
+#define WCD937X_FLYBACK_CTRL_1 0x30B1
+#define WCD937X_FLYBACK_TEST_CTL 0x30B2
+#define WCD937X_RX_AUX_SW_CTL 0x30B3
+#define WCD937X_RX_PA_AUX_IN_CONN 0x30B4
+#define WCD937X_RX_TIMER_DIV 0x30B5
+#define WCD937X_RX_OCP_CTL 0x30B6
+#define WCD937X_RX_OCP_COUNT 0x30B7
+#define WCD937X_RX_BIAS_EAR_DAC 0x30B8
+#define WCD937X_RX_BIAS_EAR_AMP 0x30B9
+#define WCD937X_RX_BIAS_HPH_LDO 0x30BA
+#define WCD937X_RX_BIAS_HPH_PA 0x30BB
+#define WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2 0x30BC
+#define WCD937X_RX_BIAS_HPH_RDAC_LDO 0x30BD
+#define WCD937X_RX_BIAS_HPH_CNP1 0x30BE
+#define WCD937X_RX_BIAS_HPH_LOWPOWER 0x30BF
+#define WCD937X_RX_BIAS_AUX_DAC 0x30C0
+#define WCD937X_RX_BIAS_AUX_AMP 0x30C1
+#define WCD937X_RX_BIAS_VNEGDAC_BLEEDER 0x30C2
+#define WCD937X_RX_BIAS_MISC 0x30C3
+#define WCD937X_RX_BIAS_BUCK_RST 0x30C4
+#define WCD937X_RX_BIAS_BUCK_VREF_ERRAMP 0x30C5
+#define WCD937X_RX_BIAS_FLYB_ERRAMP 0x30C6
+#define WCD937X_RX_BIAS_FLYB_BUFF 0x30C7
+#define WCD937X_RX_BIAS_FLYB_MID_RST 0x30C8
+#define WCD937X_HPH_L_STATUS 0x30C9
+#define WCD937X_HPH_R_STATUS 0x30CA
+#define WCD937X_HPH_CNP_EN 0x30CB
+#define WCD937X_HPH_CNP_WG_CTL 0x30CC
+#define WCD937X_HPH_CNP_WG_TIME 0x30CD
+#define WCD937X_HPH_OCP_CTL 0x30CE
+#define WCD937X_HPH_AUTO_CHOP 0x30CF
+#define WCD937X_HPH_CHOP_CTL 0x30D0
+#define WCD937X_HPH_PA_CTL1 0x30D1
+#define WCD937X_HPH_PA_CTL2 0x30D2
+#define WCD937X_HPHPA_GND_R_MASK BIT(6)
+#define WCD937X_HPHPA_GND_L_MASK BIT(4)
+#define WCD937X_HPH_L_EN 0x30D3
+#define WCD937X_HPH_L_TEST 0x30D4
+#define WCD937X_HPH_L_ATEST 0x30D5
+#define WCD937X_HPH_R_EN 0x30D6
+#define WCD937X_GAIN_SRC_SEL_MASK BIT(5)
+#define WCD937X_GAIN_SRC_SEL_REGISTER 1
+#define WCD937X_HPH_R_TEST 0x30D7
+#define WCD937X_HPH_R_ATEST 0x30D8
+#define WCD937X_HPH_RDAC_CLK_CTL1 0x30D9
+#define WCD937X_HPHPA_GND_OVR_MASK BIT(1)
+#define WCD937X_CHOP_CLK_EN_MASK BIT(7)
+#define WCD937X_HPH_RDAC_CLK_CTL2 0x30DA
+#define WCD937X_HPH_RDAC_LDO_CTL 0x30DB
+#define WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL 0x30DC
+#define WCD937X_HPH_REFBUFF_UHQA_CTL 0x30DD
+#define WCD937X_HPH_REFBUFF_LP_CTL 0x30DE
+#define WCD937X_PREREF_FLIT_BYPASS_MASK BIT(0)
+#define WCD937X_HPH_L_DAC_CTL 0x30DF
+#define WCD937X_HPH_R_DAC_CTL 0x30E0
+#define WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL 0x30E1
+#define WCD937X_HPH_SURGE_HPHLR_SURGE_EN 0x30E2
+#define WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1 0x30E3
+#define WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS 0x30E4
+#define WCD937X_EAR_EAR_EN_REG 0x30E9
+#define WCD937X_EAR_EAR_PA_CON 0x30EA
+#define WCD937X_EAR_EAR_SP_CON 0x30EB
+#define WCD937X_EAR_EAR_DAC_CON 0x30EC
+#define WCD937X_EAR_EAR_CNP_FSM_CON 0x30ED
+#define WCD937X_EAR_TEST_CTL 0x30EE
+#define WCD937X_EAR_STATUS_REG_1 0x30EF
+#define WCD937X_EAR_STATUS_REG_2 0x30F0
+#define WCD937X_ANA_NEW_PAGE_REGISTER 0x3100
+#define WCD937X_HPH_NEW_ANA_HPH2 0x3101
+#define WCD937X_HPH_NEW_ANA_HPH3 0x3102
+#define WCD937X_SLEEP_CTL 0x3103
+#define WCD937X_SLEEP_WATCHDOG_CTL 0x3104
+#define WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL 0x311F
+#define WCD937X_MBHC_NEW_CTL_1 0x3120
+#define WCD937X_MBHC_CTL_RCO_EN_MASK BIT(7)
+#define WCD937X_MBHC_CTL_RCO_EN BIT(7)
+#define WCD937X_MBHC_BTN_DBNC_MASK GENMASK(1, 0)
+#define WCD937X_MBHC_BTN_DBNC_T_16_MS 0x2
+#define WCD937X_MBHC_NEW_CTL_2 0x3121
+#define WCD937X_MBHC_NEW_PLUG_DETECT_CTL 0x3122
+#define WCD937X_MBHC_NEW_ZDET_ANA_CTL 0x3123
+#define WCD937X_M_RTH_CTL_MASK GENMASK(3, 2)
+#define WCD937X_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0)
+#define WCD937X_MBHC_HS_VREF_1P5_V 0x1
+#define WCD937X_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6
+#define WCD937X_ZDET_RANGE_CTL_MASK GENMASK(3, 0)
+#define WCD937X_ZDET_MAXV_CTL_MASK GENMASK(6, 4)
+#define WCD937X_MBHC_NEW_ZDET_RAMP_CTL 0x3124
+#define WCD937X_MBHC_NEW_FSM_STATUS 0x3125
+#define WCD937X_MBHC_NEW_ADC_RESULT 0x3126
+#define WCD937X_TX_NEW_TX_CH2_SEL 0x3127
+#define WCD937X_AUX_AUXPA 0x3128
+#define WCD937X_AUXPA_CLK_EN_MASK BIT(4)
+#define WCD937X_AUXPA_CLK_EN_MASK BIT(4)
+#define WCD937X_LDORXTX_MODE 0x3129
+#define WCD937X_LDORXTX_CONFIG 0x312A
+#define WCD937X_DIE_CRACK_DIE_CRK_DET_EN 0x312C
+#define WCD937X_DIE_CRACK_DIE_CRK_DET_OUT 0x312D
+#define WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL 0x3132
+#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x3133
+#define WCD937X_HPH_NEW_INT_RDAC_VREF_CTL 0x3134
+#define WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x3135
+#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x3136
+#define WCD937X_HPH_NEW_INT_PA_MISC1 0x3137
+#define WCD937X_HPH_NEW_INT_PA_MISC2 0x3138
+#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC 0x3139
+#define WCD937X_HPH_NEW_INT_HPH_TIMER1 0x313A
+#define WCD937X_HPH_NEW_INT_HPH_TIMER2 0x313B
+#define WCD937X_HPH_NEW_INT_HPH_TIMER3 0x313C
+#define WCD937X_HPH_NEW_INT_HPH_TIMER4 0x313D
+#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC2 0x313E
+#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC3 0x313F
+#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI 0x3145
+#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP 0x3146
+#define WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP 0x3147
+#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL 0x31AF
+#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL 0x31B0
+#define WCD937X_MOISTURE_EN_POLLING_MASK BIT(2)
+#define WCD937X_HSDET_PULLUP_C_MASK GENMASK(4, 0)
+#define WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT 0x31B1
+#define WCD937X_MBHC_NEW_INT_SPARE_2 0x31B2
+#define WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON 0x31B7
+#define WCD937X_EAR_INT_NEW_CNP_VCM_CON1 0x31B8
+#define WCD937X_EAR_INT_NEW_CNP_VCM_CON2 0x31B9
+#define WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS 0x31BA
+#define WCD937X_AUX_INT_EN_REG 0x31BD
+#define WCD937X_AUX_INT_PA_CTRL 0x31BE
+#define WCD937X_AUX_INT_SP_CTRL 0x31BF
+#define WCD937X_AUX_INT_DAC_CTRL 0x31C0
+#define WCD937X_AUX_INT_CLK_CTRL 0x31C1
+#define WCD937X_AUX_INT_TEST_CTRL 0x31C2
+#define WCD937X_AUX_INT_STATUS_REG 0x31C3
+#define WCD937X_AUX_INT_MISC 0x31C4
+#define WCD937X_LDORXTX_INT_BIAS 0x31C5
+#define WCD937X_LDORXTX_INT_STB_LOADS_DTEST 0x31C6
+#define WCD937X_LDORXTX_INT_TEST0 0x31C7
+#define WCD937X_LDORXTX_INT_STARTUP_TIMER 0x31C8
+#define WCD937X_LDORXTX_INT_TEST1 0x31C9
+#define WCD937X_LDORXTX_INT_STATUS 0x31CA
+#define WCD937X_SLEEP_INT_WATCHDOG_CTL_1 0x31D0
+#define WCD937X_SLEEP_INT_WATCHDOG_CTL_2 0x31D1
+#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1 0x31D3
+#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2 0x31D4
+#define WCD937X_DIGITAL_PAGE_REGISTER 0x3400
+#define WCD937X_DIGITAL_CHIP_ID0 0x3401
+#define WCD937X_DIGITAL_CHIP_ID1 0x3402
+#define WCD937X_DIGITAL_CHIP_ID2 0x3403
+#define WCD937X_DIGITAL_CHIP_ID3 0x3404
+#define WCD937X_DIGITAL_CDC_RST_CTL 0x3406
+#define WCD937X_DIGITAL_TOP_CLK_CFG 0x3407
+#define WCD937X_DIGITAL_CDC_ANA_CLK_CTL 0x3408
+#define WCD937X_DIGITAL_CDC_DIG_CLK_CTL 0x3409
+#define WCD937X_DIGITAL_SWR_RST_EN 0x340A
+#define WCD937X_DIGITAL_CDC_PATH_MODE 0x340B
+#define WCD937X_DIGITAL_CDC_RX_RST 0x340C
+#define WCD937X_DIGITAL_CDC_RX0_CTL 0x340D
+#define WCD937X_DIGITAL_CDC_RX1_CTL 0x340E
+#define WCD937X_DIGITAL_CDC_RX2_CTL 0x340F
+#define WCD937X_DIGITAL_DEM_BYPASS_DATA0 0x3410
+#define WCD937X_DIGITAL_DEM_BYPASS_DATA1 0x3411
+#define WCD937X_DIGITAL_DEM_BYPASS_DATA2 0x3412
+#define WCD937X_DIGITAL_DEM_BYPASS_DATA3 0x3413
+#define WCD937X_DIGITAL_CDC_COMP_CTL_0 0x3414
+#define WCD937X_DIGITAL_CDC_RX_DELAY_CTL 0x3417
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_0 0x3418
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_1 0x3419
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_0 0x341A
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_1 0x341B
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_0 0x341C
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_1 0x341D
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_0 0x341E
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_1 0x341F
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_0 0x3420
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_1 0x3421
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A6_0 0x3422
+#define WCD937X_DIGITAL_CDC_HPH_DSM_A7_0 0x3423
+#define WCD937X_DIGITAL_CDC_HPH_DSM_C_0 0x3424
+#define WCD937X_DIGITAL_CDC_HPH_DSM_C_1 0x3425
+#define WCD937X_DIGITAL_CDC_HPH_DSM_C_2 0x3426
+#define WCD937X_DIGITAL_CDC_HPH_DSM_C_3 0x3427
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R1 0x3428
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R2 0x3429
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R3 0x342A
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R4 0x342B
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R5 0x342C
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R6 0x342D
+#define WCD937X_DIGITAL_CDC_HPH_DSM_R7 0x342E
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_0 0x342F
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_1 0x3430
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_0 0x3431
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_1 0x3432
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_0 0x3433
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_1 0x3434
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_0 0x3435
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_1 0x3436
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_0 0x3437
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_1 0x3438
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A6_0 0x3439
+#define WCD937X_DIGITAL_CDC_AUX_DSM_A7_0 0x343A
+#define WCD937X_DIGITAL_CDC_AUX_DSM_C_0 0x343B
+#define WCD937X_DIGITAL_CDC_AUX_DSM_C_1 0x343C
+#define WCD937X_DIGITAL_CDC_AUX_DSM_C_2 0x343D
+#define WCD937X_DIGITAL_CDC_AUX_DSM_C_3 0x343E
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R1 0x343F
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R2 0x3440
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R3 0x3441
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R4 0x3442
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R5 0x3443
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R6 0x3444
+#define WCD937X_DIGITAL_CDC_AUX_DSM_R7 0x3445
+#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0 0x3446
+#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1 0x3447
+#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0 0x3448
+#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1 0x3449
+#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2 0x344A
+#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0 0x344B
+#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1 0x344C
+#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2 0x344D
+#define WCD937X_DIGITAL_CDC_HPH_GAIN_CTL 0x344E
+#define WCD937X_DIGITAL_CDC_AUX_GAIN_CTL 0x344F
+#define WCD937X_DIGITAL_CDC_EAR_PATH_CTL 0x3450
+#define WCD937X_DIGITAL_CDC_SWR_CLH 0x3451
+#define WCD937X_DIGITAL_SWR_CLH_BYP 0x3452
+#define WCD937X_DIGITAL_CDC_TX0_CTL 0x3453
+#define WCD937X_DIGITAL_CDC_TX1_CTL 0x3454
+#define WCD937X_DIGITAL_CDC_TX2_CTL 0x3455
+#define WCD937X_DIGITAL_CDC_TX_RST 0x3456
+#define WCD937X_DIGITAL_CDC_REQ_CTL 0x3457
+#define WCD937X_DIGITAL_CDC_AMIC_CTL 0x345A
+#define WCD937X_DIGITAL_CDC_DMIC_CTL 0x345B
+#define WCD937X_DIGITAL_CDC_DMIC1_CTL 0x345C
+#define WCD937X_DIGITAL_CDC_DMIC2_CTL 0x345D
+#define WCD937X_DIGITAL_CDC_DMIC3_CTL 0x345E
+#define WCD937X_DIGITAL_EFUSE_CTL 0x345F
+#define WCD937X_DIGITAL_EFUSE_PRG_CTL 0x3460
+#define WCD937X_DIGITAL_EFUSE_TEST_CTL_0 0x3461
+#define WCD937X_DIGITAL_EFUSE_TEST_CTL_1 0x3462
+#define WCD937X_DIGITAL_EFUSE_T_DATA_0 0x3463
+#define WCD937X_DIGITAL_EFUSE_T_DATA_1 0x3464
+#define WCD937X_DIGITAL_PDM_WD_CTL0 0x3465
+#define WCD937X_DIGITAL_PDM_WD_CTL1 0x3466
+#define WCD937X_DIGITAL_PDM_WD_CTL2 0x3467
+#define WCD937X_DIGITAL_PDM_WD_CTL2_HOLD_OFF BIT(2)
+#define WCD937X_DIGITAL_PDM_WD_CTL2_TIMEOUT_SEL BIT(1)
+#define WCD937X_DIGITAL_PDM_WD_CTL2_EN BIT(0)
+#define WCD937X_DIGITAL_PDM_WD_CTL2_MASK GENMASK(2, 0)
+#define WCD937X_DIGITAL_INTR_MODE 0x346A
+#define WCD937X_DIGITAL_INTR_MASK_0 0x346B
+#define WCD937X_DIGITAL_INTR_MASK_1 0x346C
+#define WCD937X_DIGITAL_INTR_MASK_2 0x346D
+#define WCD937X_DIGITAL_INTR_STATUS_0 0x346E
+#define WCD937X_DIGITAL_INTR_STATUS_1 0x346F
+#define WCD937X_DIGITAL_INTR_STATUS_2 0x3470
+#define WCD937X_DIGITAL_INTR_CLEAR_0 0x3471
+#define WCD937X_DIGITAL_INTR_CLEAR_1 0x3472
+#define WCD937X_DIGITAL_INTR_CLEAR_2 0x3473
+#define WCD937X_DIGITAL_INTR_LEVEL_0 0x3474
+#define WCD937X_DIGITAL_INTR_LEVEL_1 0x3475
+#define WCD937X_DIGITAL_INTR_LEVEL_2 0x3476
+#define WCD937X_DIGITAL_INTR_SET_0 0x3477
+#define WCD937X_DIGITAL_INTR_SET_1 0x3478
+#define WCD937X_DIGITAL_INTR_SET_2 0x3479
+#define WCD937X_DIGITAL_INTR_TEST_0 0x347A
+#define WCD937X_DIGITAL_INTR_TEST_1 0x347B
+#define WCD937X_DIGITAL_INTR_TEST_2 0x347C
+#define WCD937X_DIGITAL_CDC_CONN_RX0_CTL 0x347F
+#define WCD937X_DIGITAL_CDC_CONN_RX1_CTL 0x3480
+#define WCD937X_DIGITAL_CDC_CONN_RX2_CTL 0x3481
+#define WCD937X_DIGITAL_CDC_CONN_TX_CTL 0x3482
+#define WCD937X_DIGITAL_LOOP_BACK_MODE 0x3483
+#define WCD937X_DIGITAL_SWR_DAC_TEST 0x3484
+#define WCD937X_DIGITAL_SWR_HM_TEST_RX_0 0x3485
+#define WCD937X_DIGITAL_SWR_HM_TEST_TX_0 0x3491
+#define WCD937X_DIGITAL_SWR_HM_TEST_RX_1 0x3492
+#define WCD937X_DIGITAL_SWR_HM_TEST_TX_1 0x3493
+#define WCD937X_DIGITAL_SWR_HM_TEST 0x3494
+#define WCD937X_DIGITAL_PAD_CTL_PDM_RX0 0x3495
+#define WCD937X_DIGITAL_PAD_CTL_PDM_RX1 0x3496
+#define WCD937X_DIGITAL_PAD_CTL_PDM_TX0 0x3497
+#define WCD937X_DIGITAL_PAD_CTL_PDM_TX1 0x3498
+#define WCD937X_DIGITAL_PAD_INP_DIS_0 0x3499
+#define WCD937X_DIGITAL_PAD_INP_DIS_1 0x349A
+#define WCD937X_DIGITAL_DRIVE_STRENGTH_0 0x349B
+#define WCD937X_DIGITAL_DRIVE_STRENGTH_1 0x349C
+#define WCD937X_DIGITAL_DRIVE_STRENGTH_2 0x349D
+#define WCD937X_DIGITAL_RX_DATA_EDGE_CTL 0x349E
+#define WCD937X_DIGITAL_TX_DATA_EDGE_CTL 0x349F
+#define WCD937X_DIGITAL_GPIO_MODE 0x34A0
+#define WCD937X_DIGITAL_PIN_CTL_OE 0x34A1
+#define WCD937X_DIGITAL_PIN_CTL_DATA_0 0x34A2
+#define WCD937X_DIGITAL_PIN_CTL_DATA_1 0x34A3
+#define WCD937X_DIGITAL_PIN_STATUS_0 0x34A4
+#define WCD937X_DIGITAL_PIN_STATUS_1 0x34A5
+#define WCD937X_DIGITAL_DIG_DEBUG_CTL 0x34A6
+#define WCD937X_DIGITAL_DIG_DEBUG_EN 0x34A7
+#define WCD937X_DIGITAL_ANA_CSR_DBG_ADD 0x34A8
+#define WCD937X_DIGITAL_ANA_CSR_DBG_CTL 0x34A9
+#define WCD937X_DIGITAL_SSP_DBG 0x34AA
+#define WCD937X_DIGITAL_MODE_STATUS_0 0x34AB
+#define WCD937X_DIGITAL_MODE_STATUS_1 0x34AC
+#define WCD937X_DIGITAL_SPARE_0 0x34AD
+#define WCD937X_DIGITAL_SPARE_1 0x34AE
+#define WCD937X_DIGITAL_SPARE_2 0x34AF
+#define WCD937X_DIGITAL_EFUSE_REG_0 0x34B0
+#define WCD937X_DIGITAL_EFUSE_REG_1 0x34B1
+#define WCD937X_DIGITAL_EFUSE_REG_2 0x34B2
+#define WCD937X_DIGITAL_EFUSE_REG_3 0x34B3
+#define WCD937X_DIGITAL_EFUSE_REG_4 0x34B4
+#define WCD937X_DIGITAL_EFUSE_REG_5 0x34B5
+#define WCD937X_DIGITAL_EFUSE_REG_6 0x34B6
+#define WCD937X_DIGITAL_EFUSE_REG_7 0x34B7
+#define WCD937X_DIGITAL_EFUSE_REG_8 0x34B8
+#define WCD937X_DIGITAL_EFUSE_REG_9 0x34B9
+#define WCD937X_DIGITAL_EFUSE_REG_10 0x34BA
+#define WCD937X_DIGITAL_EFUSE_REG_11 0x34BB
+#define WCD937X_DIGITAL_EFUSE_REG_12 0x34BC
+#define WCD937X_DIGITAL_EFUSE_REG_13 0x34BD
+#define WCD937X_DIGITAL_EFUSE_REG_14 0x34BE
+#define WCD937X_DIGITAL_EFUSE_REG_15 0x34BF
+#define WCD937X_DIGITAL_EFUSE_REG_16 0x34C0
+#define WCD937X_DIGITAL_EFUSE_REG_17 0x34C1
+#define WCD937X_DIGITAL_EFUSE_REG_18 0x34C2
+#define WCD937X_DIGITAL_EFUSE_REG_19 0x34C3
+#define WCD937X_DIGITAL_EFUSE_REG_20 0x34C4
+#define WCD937X_DIGITAL_EFUSE_REG_21 0x34C5
+#define WCD937X_DIGITAL_EFUSE_REG_22 0x34C6
+#define WCD937X_DIGITAL_EFUSE_REG_23 0x34C7
+#define WCD937X_DIGITAL_EFUSE_REG_24 0x34C8
+#define WCD937X_DIGITAL_EFUSE_REG_25 0x34C9
+#define WCD937X_DIGITAL_EFUSE_REG_26 0x34CA
+#define WCD937X_DIGITAL_EFUSE_REG_27 0x34CB
+#define WCD937X_DIGITAL_EFUSE_REG_28 0x34CC
+#define WCD937X_DIGITAL_EFUSE_REG_29 0x34CD
+#define WCD937X_DIGITAL_EFUSE_REG_30 0x34CE
+#define WCD937X_DIGITAL_EFUSE_REG_31 0x34CF
+#define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31)
+
+#define WCD937X_MAX_MICBIAS 3
+#define WCD937X_MAX_SWR_CH_IDS 15
+#define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
+
+enum wcd937x_tx_sdw_ports {
+ WCD937X_ADC_1_PORT = 1,
+ WCD937X_ADC_2_3_PORT,
+ WCD937X_DMIC_0_3_MBHC_PORT,
+ WCD937X_DMIC_4_6_PORT,
+ WCD937X_MAX_TX_SWR_PORTS = WCD937X_DMIC_4_6_PORT,
+};
+
+enum wcd937x_rx_sdw_ports {
+ WCD937X_HPH_PORT = 1,
+ WCD937X_CLSH_PORT,
+ WCD937X_COMP_PORT,
+ WCD937X_LO_PORT,
+ WCD937X_DSD_PORT,
+ WCD937X_MAX_SWR_PORTS = WCD937X_DSD_PORT,
+};
+
+struct wcd937x_priv;
+struct wcd937x_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS];
+ struct wcd_sdw_ch_info *ch_info;
+ bool port_enable[WCD937X_MAX_SWR_CH_IDS];
+ unsigned int master_channel_map[SDW_MAX_PORTS];
+ int active_ports;
+ int num_ports;
+ bool is_tx;
+ struct wcd937x_priv *wcd937x;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_WCD937X_SDW)
+int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction);
+int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+
+#else
+static inline int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET,
+ WCD937X_IRQ_MBHC_ELECT_INS_REM_DET,
+ WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ WCD937X_IRQ_MBHC_SW_DET,
+ WCD937X_IRQ_HPHR_OCP_INT,
+ WCD937X_IRQ_HPHR_CNP_INT,
+ WCD937X_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ WCD937X_IRQ_HPHL_CNP_INT,
+ WCD937X_IRQ_EAR_CNP_INT,
+ WCD937X_IRQ_EAR_SCD_INT,
+ WCD937X_IRQ_AUX_CNP_INT,
+ WCD937X_IRQ_AUX_SCD_INT,
+ WCD937X_IRQ_HPHL_PDM_WD_INT,
+ WCD937X_IRQ_HPHR_PDM_WD_INT,
+ WCD937X_IRQ_AUX_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ WCD937X_IRQ_LDORT_SCD_INT,
+ WCD937X_IRQ_MBHC_MOISTURE_INT,
+ WCD937X_IRQ_HPHL_SURGE_DET_INT,
+ WCD937X_IRQ_HPHR_SURGE_DET_INT,
+ WCD937X_NUM_IRQS,
+};
+
+enum wcd937x_tx_sdw_channels {
+ WCD937X_ADC1,
+ WCD937X_ADC2,
+ WCD937X_ADC3,
+ WCD937X_DMIC0,
+ WCD937X_DMIC1,
+ WCD937X_MBHC,
+ WCD937X_DMIC2,
+ WCD937X_DMIC3,
+ WCD937X_DMIC4,
+ WCD937X_DMIC5,
+ WCD937X_DMIC6,
+};
+
+enum wcd937x_rx_sdw_channels {
+ WCD937X_HPH_L,
+ WCD937X_HPH_R,
+ WCD937X_CLSH,
+ WCD937X_COMP_L,
+ WCD937X_COMP_R,
+ WCD937X_LO,
+ WCD937X_DSD_R,
+ WCD937X_DSD_L,
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
new file mode 100644
index 000000000000..8c8f39d04972
--- /dev/null
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -0,0 +1,1279 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "wcd938x.h"
+#include "wcd-common.h"
+
+static const struct wcd_sdw_ch_info wcd938x_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)),
+ WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_COMP_L, WCD938X_COMP_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_COMP_R, WCD938X_COMP_PORT, BIT(1)),
+ WCD_SDW_CH(WCD938X_LO, WCD938X_LO_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_DSD_L, WCD938X_DSD_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)),
+};
+
+static const struct wcd_sdw_ch_info wcd938x_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)),
+ WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_ADC4, WCD938X_ADC_3_4_PORT, BIT(1)),
+ WCD_SDW_CH(WCD938X_DMIC0, WCD938X_DMIC_0_3_MBHC_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_DMIC1, WCD938X_DMIC_0_3_MBHC_PORT, BIT(1)),
+ WCD_SDW_CH(WCD938X_MBHC, WCD938X_DMIC_0_3_MBHC_PORT, BIT(2)),
+ WCD_SDW_CH(WCD938X_DMIC2, WCD938X_DMIC_0_3_MBHC_PORT, BIT(2)),
+ WCD_SDW_CH(WCD938X_DMIC3, WCD938X_DMIC_0_3_MBHC_PORT, BIT(3)),
+ WCD_SDW_CH(WCD938X_DMIC4, WCD938X_DMIC_4_7_PORT, BIT(0)),
+ WCD_SDW_CH(WCD938X_DMIC5, WCD938X_DMIC_4_7_PORT, BIT(1)),
+ WCD_SDW_CH(WCD938X_DMIC6, WCD938X_DMIC_4_7_PORT, BIT(2)),
+ WCD_SDW_CH(WCD938X_DMIC7, WCD938X_DMIC_4_7_PORT, BIT(3)),
+};
+
+static struct sdw_dpn_prop wcd938x_dpn_prop[WCD938X_MAX_SWR_PORTS] = {
+ {
+ .num = 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 8,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 2,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 3,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 4,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 5,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ wcd->sconfig.ch_count = 1;
+ wcd->active_ports = 0;
+ for (i = 0; i < WCD938X_MAX_SWR_PORTS; i++) {
+ ch_mask = wcd->port_config[i].ch_mask;
+
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ wcd->sconfig.ch_count++;
+
+ port_config[wcd->active_ports] = wcd->port_config[i];
+ wcd->active_ports++;
+ }
+
+ wcd->sconfig.bps = 1;
+ wcd->sconfig.frame_rate = params_rate(params);
+ if (wcd->is_tx)
+ wcd->sconfig.direction = SDW_DATA_DIR_TX;
+ else
+ wcd->sconfig.direction = SDW_DATA_DIR_RX;
+
+ wcd->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig,
+ &port_config[0], wcd->active_ports,
+ wcd->sruntime);
+}
+EXPORT_SYMBOL_GPL(wcd938x_sdw_hw_params);
+
+int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ sdw_stream_remove_slave(wcd->sdev, wcd->sruntime);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd938x_sdw_free);
+
+int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ wcd->sruntime = stream;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd938x_sdw_set_sdw_stream);
+
+static int wcd9380_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct wcd938x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
+
+ return wcd_interrupt_callback(slave, wcd->slave_irq, WCD938X_DIGITAL_INTR_STATUS_0,
+ WCD938X_DIGITAL_INTR_STATUS_1, WCD938X_DIGITAL_INTR_STATUS_2);
+}
+
+static const struct reg_default wcd938x_defaults[] = {
+ {WCD938X_ANA_PAGE_REGISTER, 0x00},
+ {WCD938X_ANA_BIAS, 0x00},
+ {WCD938X_ANA_RX_SUPPLIES, 0x00},
+ {WCD938X_ANA_HPH, 0x0C},
+ {WCD938X_ANA_EAR, 0x00},
+ {WCD938X_ANA_EAR_COMPANDER_CTL, 0x02},
+ {WCD938X_ANA_TX_CH1, 0x20},
+ {WCD938X_ANA_TX_CH2, 0x00},
+ {WCD938X_ANA_TX_CH3, 0x20},
+ {WCD938X_ANA_TX_CH4, 0x00},
+ {WCD938X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00},
+ {WCD938X_ANA_MICB3_DSP_EN_LOGIC, 0x00},
+ {WCD938X_ANA_MBHC_MECH, 0x39},
+ {WCD938X_ANA_MBHC_ELECT, 0x08},
+ {WCD938X_ANA_MBHC_ZDET, 0x00},
+ {WCD938X_ANA_MBHC_RESULT_1, 0x00},
+ {WCD938X_ANA_MBHC_RESULT_2, 0x00},
+ {WCD938X_ANA_MBHC_RESULT_3, 0x00},
+ {WCD938X_ANA_MBHC_BTN0, 0x00},
+ {WCD938X_ANA_MBHC_BTN1, 0x10},
+ {WCD938X_ANA_MBHC_BTN2, 0x20},
+ {WCD938X_ANA_MBHC_BTN3, 0x30},
+ {WCD938X_ANA_MBHC_BTN4, 0x40},
+ {WCD938X_ANA_MBHC_BTN5, 0x50},
+ {WCD938X_ANA_MBHC_BTN6, 0x60},
+ {WCD938X_ANA_MBHC_BTN7, 0x70},
+ {WCD938X_ANA_MICB1, 0x10},
+ {WCD938X_ANA_MICB2, 0x10},
+ {WCD938X_ANA_MICB2_RAMP, 0x00},
+ {WCD938X_ANA_MICB3, 0x10},
+ {WCD938X_ANA_MICB4, 0x10},
+ {WCD938X_BIAS_CTL, 0x2A},
+ {WCD938X_BIAS_VBG_FINE_ADJ, 0x55},
+ {WCD938X_LDOL_VDDCX_ADJUST, 0x01},
+ {WCD938X_LDOL_DISABLE_LDOL, 0x00},
+ {WCD938X_MBHC_CTL_CLK, 0x00},
+ {WCD938X_MBHC_CTL_ANA, 0x00},
+ {WCD938X_MBHC_CTL_SPARE_1, 0x00},
+ {WCD938X_MBHC_CTL_SPARE_2, 0x00},
+ {WCD938X_MBHC_CTL_BCS, 0x00},
+ {WCD938X_MBHC_MOISTURE_DET_FSM_STATUS, 0x00},
+ {WCD938X_MBHC_TEST_CTL, 0x00},
+ {WCD938X_LDOH_MODE, 0x2B},
+ {WCD938X_LDOH_BIAS, 0x68},
+ {WCD938X_LDOH_STB_LOADS, 0x00},
+ {WCD938X_LDOH_SLOWRAMP, 0x50},
+ {WCD938X_MICB1_TEST_CTL_1, 0x1A},
+ {WCD938X_MICB1_TEST_CTL_2, 0x00},
+ {WCD938X_MICB1_TEST_CTL_3, 0xA4},
+ {WCD938X_MICB2_TEST_CTL_1, 0x1A},
+ {WCD938X_MICB2_TEST_CTL_2, 0x00},
+ {WCD938X_MICB2_TEST_CTL_3, 0x24},
+ {WCD938X_MICB3_TEST_CTL_1, 0x1A},
+ {WCD938X_MICB3_TEST_CTL_2, 0x00},
+ {WCD938X_MICB3_TEST_CTL_3, 0xA4},
+ {WCD938X_MICB4_TEST_CTL_1, 0x1A},
+ {WCD938X_MICB4_TEST_CTL_2, 0x00},
+ {WCD938X_MICB4_TEST_CTL_3, 0xA4},
+ {WCD938X_TX_COM_ADC_VCM, 0x39},
+ {WCD938X_TX_COM_BIAS_ATEST, 0xE0},
+ {WCD938X_TX_COM_SPARE1, 0x00},
+ {WCD938X_TX_COM_SPARE2, 0x00},
+ {WCD938X_TX_COM_TXFE_DIV_CTL, 0x22},
+ {WCD938X_TX_COM_TXFE_DIV_START, 0x00},
+ {WCD938X_TX_COM_SPARE3, 0x00},
+ {WCD938X_TX_COM_SPARE4, 0x00},
+ {WCD938X_TX_1_2_TEST_EN, 0xCC},
+ {WCD938X_TX_1_2_ADC_IB, 0xE9},
+ {WCD938X_TX_1_2_ATEST_REFCTL, 0x0A},
+ {WCD938X_TX_1_2_TEST_CTL, 0x38},
+ {WCD938X_TX_1_2_TEST_BLK_EN1, 0xFF},
+ {WCD938X_TX_1_2_TXFE1_CLKDIV, 0x00},
+ {WCD938X_TX_1_2_SAR2_ERR, 0x00},
+ {WCD938X_TX_1_2_SAR1_ERR, 0x00},
+ {WCD938X_TX_3_4_TEST_EN, 0xCC},
+ {WCD938X_TX_3_4_ADC_IB, 0xE9},
+ {WCD938X_TX_3_4_ATEST_REFCTL, 0x0A},
+ {WCD938X_TX_3_4_TEST_CTL, 0x38},
+ {WCD938X_TX_3_4_TEST_BLK_EN3, 0xFF},
+ {WCD938X_TX_3_4_TXFE3_CLKDIV, 0x00},
+ {WCD938X_TX_3_4_SAR4_ERR, 0x00},
+ {WCD938X_TX_3_4_SAR3_ERR, 0x00},
+ {WCD938X_TX_3_4_TEST_BLK_EN2, 0xFB},
+ {WCD938X_TX_3_4_TXFE2_CLKDIV, 0x00},
+ {WCD938X_TX_3_4_SPARE1, 0x00},
+ {WCD938X_TX_3_4_TEST_BLK_EN4, 0xFB},
+ {WCD938X_TX_3_4_TXFE4_CLKDIV, 0x00},
+ {WCD938X_TX_3_4_SPARE2, 0x00},
+ {WCD938X_CLASSH_MODE_1, 0x40},
+ {WCD938X_CLASSH_MODE_2, 0x3A},
+ {WCD938X_CLASSH_MODE_3, 0x00},
+ {WCD938X_CLASSH_CTRL_VCL_1, 0x70},
+ {WCD938X_CLASSH_CTRL_VCL_2, 0x82},
+ {WCD938X_CLASSH_CTRL_CCL_1, 0x31},
+ {WCD938X_CLASSH_CTRL_CCL_2, 0x80},
+ {WCD938X_CLASSH_CTRL_CCL_3, 0x80},
+ {WCD938X_CLASSH_CTRL_CCL_4, 0x51},
+ {WCD938X_CLASSH_CTRL_CCL_5, 0x00},
+ {WCD938X_CLASSH_BUCK_TMUX_A_D, 0x00},
+ {WCD938X_CLASSH_BUCK_SW_DRV_CNTL, 0x77},
+ {WCD938X_CLASSH_SPARE, 0x00},
+ {WCD938X_FLYBACK_EN, 0x4E},
+ {WCD938X_FLYBACK_VNEG_CTRL_1, 0x0B},
+ {WCD938X_FLYBACK_VNEG_CTRL_2, 0x45},
+ {WCD938X_FLYBACK_VNEG_CTRL_3, 0x74},
+ {WCD938X_FLYBACK_VNEG_CTRL_4, 0x7F},
+ {WCD938X_FLYBACK_VNEG_CTRL_5, 0x83},
+ {WCD938X_FLYBACK_VNEG_CTRL_6, 0x98},
+ {WCD938X_FLYBACK_VNEG_CTRL_7, 0xA9},
+ {WCD938X_FLYBACK_VNEG_CTRL_8, 0x68},
+ {WCD938X_FLYBACK_VNEG_CTRL_9, 0x64},
+ {WCD938X_FLYBACK_VNEGDAC_CTRL_1, 0xED},
+ {WCD938X_FLYBACK_VNEGDAC_CTRL_2, 0xF0},
+ {WCD938X_FLYBACK_VNEGDAC_CTRL_3, 0xA6},
+ {WCD938X_FLYBACK_CTRL_1, 0x65},
+ {WCD938X_FLYBACK_TEST_CTL, 0x00},
+ {WCD938X_RX_AUX_SW_CTL, 0x00},
+ {WCD938X_RX_PA_AUX_IN_CONN, 0x01},
+ {WCD938X_RX_TIMER_DIV, 0x32},
+ {WCD938X_RX_OCP_CTL, 0x1F},
+ {WCD938X_RX_OCP_COUNT, 0x77},
+ {WCD938X_RX_BIAS_EAR_DAC, 0xA0},
+ {WCD938X_RX_BIAS_EAR_AMP, 0xAA},
+ {WCD938X_RX_BIAS_HPH_LDO, 0xA9},
+ {WCD938X_RX_BIAS_HPH_PA, 0xAA},
+ {WCD938X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A},
+ {WCD938X_RX_BIAS_HPH_RDAC_LDO, 0x88},
+ {WCD938X_RX_BIAS_HPH_CNP1, 0x82},
+ {WCD938X_RX_BIAS_HPH_LOWPOWER, 0x82},
+ {WCD938X_RX_BIAS_AUX_DAC, 0xA0},
+ {WCD938X_RX_BIAS_AUX_AMP, 0xAA},
+ {WCD938X_RX_BIAS_VNEGDAC_BLEEDER, 0x50},
+ {WCD938X_RX_BIAS_MISC, 0x00},
+ {WCD938X_RX_BIAS_BUCK_RST, 0x08},
+ {WCD938X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44},
+ {WCD938X_RX_BIAS_FLYB_ERRAMP, 0x40},
+ {WCD938X_RX_BIAS_FLYB_BUFF, 0xAA},
+ {WCD938X_RX_BIAS_FLYB_MID_RST, 0x14},
+ {WCD938X_HPH_L_STATUS, 0x04},
+ {WCD938X_HPH_R_STATUS, 0x04},
+ {WCD938X_HPH_CNP_EN, 0x80},
+ {WCD938X_HPH_CNP_WG_CTL, 0x9A},
+ {WCD938X_HPH_CNP_WG_TIME, 0x14},
+ {WCD938X_HPH_OCP_CTL, 0x28},
+ {WCD938X_HPH_AUTO_CHOP, 0x16},
+ {WCD938X_HPH_CHOP_CTL, 0x83},
+ {WCD938X_HPH_PA_CTL1, 0x46},
+ {WCD938X_HPH_PA_CTL2, 0x50},
+ {WCD938X_HPH_L_EN, 0x80},
+ {WCD938X_HPH_L_TEST, 0xE0},
+ {WCD938X_HPH_L_ATEST, 0x50},
+ {WCD938X_HPH_R_EN, 0x80},
+ {WCD938X_HPH_R_TEST, 0xE0},
+ {WCD938X_HPH_R_ATEST, 0x54},
+ {WCD938X_HPH_RDAC_CLK_CTL1, 0x99},
+ {WCD938X_HPH_RDAC_CLK_CTL2, 0x9B},
+ {WCD938X_HPH_RDAC_LDO_CTL, 0x33},
+ {WCD938X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00},
+ {WCD938X_HPH_REFBUFF_UHQA_CTL, 0x68},
+ {WCD938X_HPH_REFBUFF_LP_CTL, 0x0E},
+ {WCD938X_HPH_L_DAC_CTL, 0x20},
+ {WCD938X_HPH_R_DAC_CTL, 0x20},
+ {WCD938X_HPH_SURGE_HPHLR_SURGE_COMP_SEL, 0x55},
+ {WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0x19},
+ {WCD938X_HPH_SURGE_HPHLR_SURGE_MISC1, 0xA0},
+ {WCD938X_HPH_SURGE_HPHLR_SURGE_STATUS, 0x00},
+ {WCD938X_EAR_EAR_EN_REG, 0x22},
+ {WCD938X_EAR_EAR_PA_CON, 0x44},
+ {WCD938X_EAR_EAR_SP_CON, 0xDB},
+ {WCD938X_EAR_EAR_DAC_CON, 0x80},
+ {WCD938X_EAR_EAR_CNP_FSM_CON, 0xB2},
+ {WCD938X_EAR_TEST_CTL, 0x00},
+ {WCD938X_EAR_STATUS_REG_1, 0x00},
+ {WCD938X_EAR_STATUS_REG_2, 0x08},
+ {WCD938X_ANA_NEW_PAGE_REGISTER, 0x00},
+ {WCD938X_HPH_NEW_ANA_HPH2, 0x00},
+ {WCD938X_HPH_NEW_ANA_HPH3, 0x00},
+ {WCD938X_SLEEP_CTL, 0x16},
+ {WCD938X_SLEEP_WATCHDOG_CTL, 0x00},
+ {WCD938X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00},
+ {WCD938X_MBHC_NEW_CTL_1, 0x02},
+ {WCD938X_MBHC_NEW_CTL_2, 0x05},
+ {WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0xE9},
+ {WCD938X_MBHC_NEW_ZDET_ANA_CTL, 0x0F},
+ {WCD938X_MBHC_NEW_ZDET_RAMP_CTL, 0x00},
+ {WCD938X_MBHC_NEW_FSM_STATUS, 0x00},
+ {WCD938X_MBHC_NEW_ADC_RESULT, 0x00},
+ {WCD938X_TX_NEW_AMIC_MUX_CFG, 0x00},
+ {WCD938X_AUX_AUXPA, 0x00},
+ {WCD938X_LDORXTX_MODE, 0x0C},
+ {WCD938X_LDORXTX_CONFIG, 0x10},
+ {WCD938X_DIE_CRACK_DIE_CRK_DET_EN, 0x00},
+ {WCD938X_DIE_CRACK_DIE_CRK_DET_OUT, 0x00},
+ {WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40},
+ {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81},
+ {WCD938X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10},
+ {WCD938X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00},
+ {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81},
+ {WCD938X_HPH_NEW_INT_PA_MISC1, 0x22},
+ {WCD938X_HPH_NEW_INT_PA_MISC2, 0x00},
+ {WCD938X_HPH_NEW_INT_PA_RDAC_MISC, 0x00},
+ {WCD938X_HPH_NEW_INT_HPH_TIMER1, 0xFE},
+ {WCD938X_HPH_NEW_INT_HPH_TIMER2, 0x02},
+ {WCD938X_HPH_NEW_INT_HPH_TIMER3, 0x4E},
+ {WCD938X_HPH_NEW_INT_HPH_TIMER4, 0x54},
+ {WCD938X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00},
+ {WCD938X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00},
+ {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW, 0x90},
+ {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW, 0x90},
+ {WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62},
+ {WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01},
+ {WCD938X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11},
+ {WCD938X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57},
+ {WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01},
+ {WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00},
+ {WCD938X_MBHC_NEW_INT_SPARE_2, 0x00},
+ {WCD938X_EAR_INT_NEW_EAR_CHOPPER_CON, 0xA8},
+ {WCD938X_EAR_INT_NEW_CNP_VCM_CON1, 0x42},
+ {WCD938X_EAR_INT_NEW_CNP_VCM_CON2, 0x22},
+ {WCD938X_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00},
+ {WCD938X_AUX_INT_EN_REG, 0x00},
+ {WCD938X_AUX_INT_PA_CTRL, 0x06},
+ {WCD938X_AUX_INT_SP_CTRL, 0xD2},
+ {WCD938X_AUX_INT_DAC_CTRL, 0x80},
+ {WCD938X_AUX_INT_CLK_CTRL, 0x50},
+ {WCD938X_AUX_INT_TEST_CTRL, 0x00},
+ {WCD938X_AUX_INT_STATUS_REG, 0x00},
+ {WCD938X_AUX_INT_MISC, 0x00},
+ {WCD938X_LDORXTX_INT_BIAS, 0x6E},
+ {WCD938X_LDORXTX_INT_STB_LOADS_DTEST, 0x50},
+ {WCD938X_LDORXTX_INT_TEST0, 0x1C},
+ {WCD938X_LDORXTX_INT_STARTUP_TIMER, 0xFF},
+ {WCD938X_LDORXTX_INT_TEST1, 0x1F},
+ {WCD938X_LDORXTX_INT_STATUS, 0x00},
+ {WCD938X_SLEEP_INT_WATCHDOG_CTL_1, 0x0A},
+ {WCD938X_SLEEP_INT_WATCHDOG_CTL_2, 0x0A},
+ {WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT1, 0x02},
+ {WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT2, 0x60},
+ {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L2, 0xFF},
+ {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L1, 0x7F},
+ {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L0, 0x3F},
+ {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP1P2M, 0x1F},
+ {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP0P6M, 0x0F},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L2L1, 0xD7},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L0, 0xC8},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_ULP, 0xC6},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L2L1, 0xD5},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L0, 0xCA},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP, 0x05},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_L2L1L0, 0xA5},
+ {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP, 0x13},
+ {WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L2L1, 0x88},
+ {WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L0ULP, 0x42},
+ {WCD938X_TX_COM_NEW_INT_TXADC_INT_L2, 0xFF},
+ {WCD938X_TX_COM_NEW_INT_TXADC_INT_L1, 0x64},
+ {WCD938X_TX_COM_NEW_INT_TXADC_INT_L0, 0x64},
+ {WCD938X_TX_COM_NEW_INT_TXADC_INT_ULP, 0x77},
+ {WCD938X_DIGITAL_PAGE_REGISTER, 0x00},
+ {WCD938X_DIGITAL_CHIP_ID0, 0x00},
+ {WCD938X_DIGITAL_CHIP_ID1, 0x00},
+ {WCD938X_DIGITAL_CHIP_ID2, 0x0D},
+ {WCD938X_DIGITAL_CHIP_ID3, 0x01},
+ {WCD938X_DIGITAL_SWR_TX_CLK_RATE, 0x00},
+ {WCD938X_DIGITAL_CDC_RST_CTL, 0x03},
+ {WCD938X_DIGITAL_TOP_CLK_CFG, 0x00},
+ {WCD938X_DIGITAL_CDC_ANA_CLK_CTL, 0x00},
+ {WCD938X_DIGITAL_CDC_DIG_CLK_CTL, 0xF0},
+ {WCD938X_DIGITAL_SWR_RST_EN, 0x00},
+ {WCD938X_DIGITAL_CDC_PATH_MODE, 0x55},
+ {WCD938X_DIGITAL_CDC_RX_RST, 0x00},
+ {WCD938X_DIGITAL_CDC_RX0_CTL, 0xFC},
+ {WCD938X_DIGITAL_CDC_RX1_CTL, 0xFC},
+ {WCD938X_DIGITAL_CDC_RX2_CTL, 0xFC},
+ {WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00},
+ {WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00},
+ {WCD938X_DIGITAL_CDC_COMP_CTL_0, 0x00},
+ {WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1E},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A3_0, 0xAC},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1A},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A5_0, 0xBC},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A6_0, 0xC7},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_A7_0, 0xF8},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_C_0, 0x47},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_C_1, 0x43},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_C_2, 0xB1},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_C_3, 0x17},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R1, 0x4D},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R2, 0x29},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R3, 0x34},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R4, 0x59},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R5, 0x66},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R6, 0x87},
+ {WCD938X_DIGITAL_CDC_HPH_DSM_R7, 0x64},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A1_0, 0x00},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A1_1, 0x01},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A2_0, 0x96},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A2_1, 0x09},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A3_0, 0xAB},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A3_1, 0x05},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A4_0, 0x1C},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A4_1, 0x02},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A5_0, 0x17},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A5_1, 0x02},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A6_0, 0xAA},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_A7_0, 0xE3},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_C_0, 0x69},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_C_1, 0x54},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_C_2, 0x02},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_C_3, 0x15},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R1, 0xA4},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R2, 0xB5},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R3, 0x86},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R4, 0x85},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R5, 0xAA},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R6, 0xE2},
+ {WCD938X_DIGITAL_CDC_AUX_DSM_R7, 0x62},
+ {WCD938X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55},
+ {WCD938X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xA9},
+ {WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3D},
+ {WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2E},
+ {WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01},
+ {WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_0, 0x00},
+ {WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_1, 0xFC},
+ {WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_2, 0x01},
+ {WCD938X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00},
+ {WCD938X_DIGITAL_CDC_AUX_GAIN_CTL, 0x00},
+ {WCD938X_DIGITAL_CDC_EAR_PATH_CTL, 0x00},
+ {WCD938X_DIGITAL_CDC_SWR_CLH, 0x00},
+ {WCD938X_DIGITAL_SWR_CLH_BYP, 0x00},
+ {WCD938X_DIGITAL_CDC_TX0_CTL, 0x68},
+ {WCD938X_DIGITAL_CDC_TX1_CTL, 0x68},
+ {WCD938X_DIGITAL_CDC_TX2_CTL, 0x68},
+ {WCD938X_DIGITAL_CDC_TX_RST, 0x00},
+ {WCD938X_DIGITAL_CDC_REQ_CTL, 0x01},
+ {WCD938X_DIGITAL_CDC_RST, 0x00},
+ {WCD938X_DIGITAL_CDC_AMIC_CTL, 0x0F},
+ {WCD938X_DIGITAL_CDC_DMIC_CTL, 0x04},
+ {WCD938X_DIGITAL_CDC_DMIC1_CTL, 0x01},
+ {WCD938X_DIGITAL_CDC_DMIC2_CTL, 0x01},
+ {WCD938X_DIGITAL_CDC_DMIC3_CTL, 0x01},
+ {WCD938X_DIGITAL_CDC_DMIC4_CTL, 0x01},
+ {WCD938X_DIGITAL_EFUSE_PRG_CTL, 0x00},
+ {WCD938X_DIGITAL_EFUSE_CTL, 0x2B},
+ {WCD938X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11},
+ {WCD938X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11},
+ {WCD938X_DIGITAL_PDM_WD_CTL0, 0x00},
+ {WCD938X_DIGITAL_PDM_WD_CTL1, 0x00},
+ {WCD938X_DIGITAL_PDM_WD_CTL2, 0x00},
+ {WCD938X_DIGITAL_INTR_MODE, 0x00},
+ {WCD938X_DIGITAL_INTR_MASK_0, 0xFF},
+ {WCD938X_DIGITAL_INTR_MASK_1, 0xFF},
+ {WCD938X_DIGITAL_INTR_MASK_2, 0x3F},
+ {WCD938X_DIGITAL_INTR_STATUS_0, 0x00},
+ {WCD938X_DIGITAL_INTR_STATUS_1, 0x00},
+ {WCD938X_DIGITAL_INTR_STATUS_2, 0x00},
+ {WCD938X_DIGITAL_INTR_CLEAR_0, 0x00},
+ {WCD938X_DIGITAL_INTR_CLEAR_1, 0x00},
+ {WCD938X_DIGITAL_INTR_CLEAR_2, 0x00},
+ {WCD938X_DIGITAL_INTR_LEVEL_0, 0x00},
+ {WCD938X_DIGITAL_INTR_LEVEL_1, 0x00},
+ {WCD938X_DIGITAL_INTR_LEVEL_2, 0x00},
+ {WCD938X_DIGITAL_INTR_SET_0, 0x00},
+ {WCD938X_DIGITAL_INTR_SET_1, 0x00},
+ {WCD938X_DIGITAL_INTR_SET_2, 0x00},
+ {WCD938X_DIGITAL_INTR_TEST_0, 0x00},
+ {WCD938X_DIGITAL_INTR_TEST_1, 0x00},
+ {WCD938X_DIGITAL_INTR_TEST_2, 0x00},
+ {WCD938X_DIGITAL_TX_MODE_DBG_EN, 0x00},
+ {WCD938X_DIGITAL_TX_MODE_DBG_0_1, 0x00},
+ {WCD938X_DIGITAL_TX_MODE_DBG_2_3, 0x00},
+ {WCD938X_DIGITAL_LB_IN_SEL_CTL, 0x00},
+ {WCD938X_DIGITAL_LOOP_BACK_MODE, 0x00},
+ {WCD938X_DIGITAL_SWR_DAC_TEST, 0x00},
+ {WCD938X_DIGITAL_SWR_HM_TEST_RX_0, 0x40},
+ {WCD938X_DIGITAL_SWR_HM_TEST_TX_0, 0x40},
+ {WCD938X_DIGITAL_SWR_HM_TEST_RX_1, 0x00},
+ {WCD938X_DIGITAL_SWR_HM_TEST_TX_1, 0x00},
+ {WCD938X_DIGITAL_SWR_HM_TEST_TX_2, 0x00},
+ {WCD938X_DIGITAL_SWR_HM_TEST_0, 0x00},
+ {WCD938X_DIGITAL_SWR_HM_TEST_1, 0x00},
+ {WCD938X_DIGITAL_PAD_CTL_SWR_0, 0x8F},
+ {WCD938X_DIGITAL_PAD_CTL_SWR_1, 0x06},
+ {WCD938X_DIGITAL_I2C_CTL, 0x00},
+ {WCD938X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00},
+ {WCD938X_DIGITAL_EFUSE_TEST_CTL_0, 0x00},
+ {WCD938X_DIGITAL_EFUSE_TEST_CTL_1, 0x00},
+ {WCD938X_DIGITAL_EFUSE_T_DATA_0, 0x00},
+ {WCD938X_DIGITAL_EFUSE_T_DATA_1, 0x00},
+ {WCD938X_DIGITAL_PAD_CTL_PDM_RX0, 0xF1},
+ {WCD938X_DIGITAL_PAD_CTL_PDM_RX1, 0xF1},
+ {WCD938X_DIGITAL_PAD_CTL_PDM_TX0, 0xF1},
+ {WCD938X_DIGITAL_PAD_CTL_PDM_TX1, 0xF1},
+ {WCD938X_DIGITAL_PAD_CTL_PDM_TX2, 0xF1},
+ {WCD938X_DIGITAL_PAD_INP_DIS_0, 0x00},
+ {WCD938X_DIGITAL_PAD_INP_DIS_1, 0x00},
+ {WCD938X_DIGITAL_DRIVE_STRENGTH_0, 0x00},
+ {WCD938X_DIGITAL_DRIVE_STRENGTH_1, 0x00},
+ {WCD938X_DIGITAL_DRIVE_STRENGTH_2, 0x00},
+ {WCD938X_DIGITAL_RX_DATA_EDGE_CTL, 0x1F},
+ {WCD938X_DIGITAL_TX_DATA_EDGE_CTL, 0x80},
+ {WCD938X_DIGITAL_GPIO_MODE, 0x00},
+ {WCD938X_DIGITAL_PIN_CTL_OE, 0x00},
+ {WCD938X_DIGITAL_PIN_CTL_DATA_0, 0x00},
+ {WCD938X_DIGITAL_PIN_CTL_DATA_1, 0x00},
+ {WCD938X_DIGITAL_PIN_STATUS_0, 0x00},
+ {WCD938X_DIGITAL_PIN_STATUS_1, 0x00},
+ {WCD938X_DIGITAL_DIG_DEBUG_CTL, 0x00},
+ {WCD938X_DIGITAL_DIG_DEBUG_EN, 0x00},
+ {WCD938X_DIGITAL_ANA_CSR_DBG_ADD, 0x00},
+ {WCD938X_DIGITAL_ANA_CSR_DBG_CTL, 0x48},
+ {WCD938X_DIGITAL_SSP_DBG, 0x00},
+ {WCD938X_DIGITAL_MODE_STATUS_0, 0x00},
+ {WCD938X_DIGITAL_MODE_STATUS_1, 0x00},
+ {WCD938X_DIGITAL_SPARE_0, 0x00},
+ {WCD938X_DIGITAL_SPARE_1, 0x00},
+ {WCD938X_DIGITAL_SPARE_2, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_0, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_1, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_2, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_3, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_4, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_5, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_6, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_7, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_8, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_9, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_10, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_11, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_12, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_13, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_14, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_15, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_16, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_17, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_18, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_19, 0xFF},
+ {WCD938X_DIGITAL_EFUSE_REG_20, 0x0E},
+ {WCD938X_DIGITAL_EFUSE_REG_21, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_22, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_23, 0xF8},
+ {WCD938X_DIGITAL_EFUSE_REG_24, 0x16},
+ {WCD938X_DIGITAL_EFUSE_REG_25, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_26, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_27, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_28, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_29, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_30, 0x00},
+ {WCD938X_DIGITAL_EFUSE_REG_31, 0x00},
+ {WCD938X_DIGITAL_TX_REQ_FB_CTL_0, 0x88},
+ {WCD938X_DIGITAL_TX_REQ_FB_CTL_1, 0x88},
+ {WCD938X_DIGITAL_TX_REQ_FB_CTL_2, 0x88},
+ {WCD938X_DIGITAL_TX_REQ_FB_CTL_3, 0x88},
+ {WCD938X_DIGITAL_TX_REQ_FB_CTL_4, 0x88},
+ {WCD938X_DIGITAL_DEM_BYPASS_DATA0, 0x55},
+ {WCD938X_DIGITAL_DEM_BYPASS_DATA1, 0x55},
+ {WCD938X_DIGITAL_DEM_BYPASS_DATA2, 0x55},
+ {WCD938X_DIGITAL_DEM_BYPASS_DATA3, 0x01},
+};
+
+static bool wcd938x_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD938X_ANA_PAGE_REGISTER:
+ case WCD938X_ANA_BIAS:
+ case WCD938X_ANA_RX_SUPPLIES:
+ case WCD938X_ANA_HPH:
+ case WCD938X_ANA_EAR:
+ case WCD938X_ANA_EAR_COMPANDER_CTL:
+ case WCD938X_ANA_TX_CH1:
+ case WCD938X_ANA_TX_CH2:
+ case WCD938X_ANA_TX_CH3:
+ case WCD938X_ANA_TX_CH4:
+ case WCD938X_ANA_MICB1_MICB2_DSP_EN_LOGIC:
+ case WCD938X_ANA_MICB3_DSP_EN_LOGIC:
+ case WCD938X_ANA_MBHC_MECH:
+ case WCD938X_ANA_MBHC_ELECT:
+ case WCD938X_ANA_MBHC_ZDET:
+ case WCD938X_ANA_MBHC_BTN0:
+ case WCD938X_ANA_MBHC_BTN1:
+ case WCD938X_ANA_MBHC_BTN2:
+ case WCD938X_ANA_MBHC_BTN3:
+ case WCD938X_ANA_MBHC_BTN4:
+ case WCD938X_ANA_MBHC_BTN5:
+ case WCD938X_ANA_MBHC_BTN6:
+ case WCD938X_ANA_MBHC_BTN7:
+ case WCD938X_ANA_MICB1:
+ case WCD938X_ANA_MICB2:
+ case WCD938X_ANA_MICB2_RAMP:
+ case WCD938X_ANA_MICB3:
+ case WCD938X_ANA_MICB4:
+ case WCD938X_BIAS_CTL:
+ case WCD938X_BIAS_VBG_FINE_ADJ:
+ case WCD938X_LDOL_VDDCX_ADJUST:
+ case WCD938X_LDOL_DISABLE_LDOL:
+ case WCD938X_MBHC_CTL_CLK:
+ case WCD938X_MBHC_CTL_ANA:
+ case WCD938X_MBHC_CTL_SPARE_1:
+ case WCD938X_MBHC_CTL_SPARE_2:
+ case WCD938X_MBHC_CTL_BCS:
+ case WCD938X_MBHC_TEST_CTL:
+ case WCD938X_LDOH_MODE:
+ case WCD938X_LDOH_BIAS:
+ case WCD938X_LDOH_STB_LOADS:
+ case WCD938X_LDOH_SLOWRAMP:
+ case WCD938X_MICB1_TEST_CTL_1:
+ case WCD938X_MICB1_TEST_CTL_2:
+ case WCD938X_MICB1_TEST_CTL_3:
+ case WCD938X_MICB2_TEST_CTL_1:
+ case WCD938X_MICB2_TEST_CTL_2:
+ case WCD938X_MICB2_TEST_CTL_3:
+ case WCD938X_MICB3_TEST_CTL_1:
+ case WCD938X_MICB3_TEST_CTL_2:
+ case WCD938X_MICB3_TEST_CTL_3:
+ case WCD938X_MICB4_TEST_CTL_1:
+ case WCD938X_MICB4_TEST_CTL_2:
+ case WCD938X_MICB4_TEST_CTL_3:
+ case WCD938X_TX_COM_ADC_VCM:
+ case WCD938X_TX_COM_BIAS_ATEST:
+ case WCD938X_TX_COM_SPARE1:
+ case WCD938X_TX_COM_SPARE2:
+ case WCD938X_TX_COM_TXFE_DIV_CTL:
+ case WCD938X_TX_COM_TXFE_DIV_START:
+ case WCD938X_TX_COM_SPARE3:
+ case WCD938X_TX_COM_SPARE4:
+ case WCD938X_TX_1_2_TEST_EN:
+ case WCD938X_TX_1_2_ADC_IB:
+ case WCD938X_TX_1_2_ATEST_REFCTL:
+ case WCD938X_TX_1_2_TEST_CTL:
+ case WCD938X_TX_1_2_TEST_BLK_EN1:
+ case WCD938X_TX_1_2_TXFE1_CLKDIV:
+ case WCD938X_TX_3_4_TEST_EN:
+ case WCD938X_TX_3_4_ADC_IB:
+ case WCD938X_TX_3_4_ATEST_REFCTL:
+ case WCD938X_TX_3_4_TEST_CTL:
+ case WCD938X_TX_3_4_TEST_BLK_EN3:
+ case WCD938X_TX_3_4_TXFE3_CLKDIV:
+ case WCD938X_TX_3_4_TEST_BLK_EN2:
+ case WCD938X_TX_3_4_TXFE2_CLKDIV:
+ case WCD938X_TX_3_4_SPARE1:
+ case WCD938X_TX_3_4_TEST_BLK_EN4:
+ case WCD938X_TX_3_4_TXFE4_CLKDIV:
+ case WCD938X_TX_3_4_SPARE2:
+ case WCD938X_CLASSH_MODE_1:
+ case WCD938X_CLASSH_MODE_2:
+ case WCD938X_CLASSH_MODE_3:
+ case WCD938X_CLASSH_CTRL_VCL_1:
+ case WCD938X_CLASSH_CTRL_VCL_2:
+ case WCD938X_CLASSH_CTRL_CCL_1:
+ case WCD938X_CLASSH_CTRL_CCL_2:
+ case WCD938X_CLASSH_CTRL_CCL_3:
+ case WCD938X_CLASSH_CTRL_CCL_4:
+ case WCD938X_CLASSH_CTRL_CCL_5:
+ case WCD938X_CLASSH_BUCK_TMUX_A_D:
+ case WCD938X_CLASSH_BUCK_SW_DRV_CNTL:
+ case WCD938X_CLASSH_SPARE:
+ case WCD938X_FLYBACK_EN:
+ case WCD938X_FLYBACK_VNEG_CTRL_1:
+ case WCD938X_FLYBACK_VNEG_CTRL_2:
+ case WCD938X_FLYBACK_VNEG_CTRL_3:
+ case WCD938X_FLYBACK_VNEG_CTRL_4:
+ case WCD938X_FLYBACK_VNEG_CTRL_5:
+ case WCD938X_FLYBACK_VNEG_CTRL_6:
+ case WCD938X_FLYBACK_VNEG_CTRL_7:
+ case WCD938X_FLYBACK_VNEG_CTRL_8:
+ case WCD938X_FLYBACK_VNEG_CTRL_9:
+ case WCD938X_FLYBACK_VNEGDAC_CTRL_1:
+ case WCD938X_FLYBACK_VNEGDAC_CTRL_2:
+ case WCD938X_FLYBACK_VNEGDAC_CTRL_3:
+ case WCD938X_FLYBACK_CTRL_1:
+ case WCD938X_FLYBACK_TEST_CTL:
+ case WCD938X_RX_AUX_SW_CTL:
+ case WCD938X_RX_PA_AUX_IN_CONN:
+ case WCD938X_RX_TIMER_DIV:
+ case WCD938X_RX_OCP_CTL:
+ case WCD938X_RX_OCP_COUNT:
+ case WCD938X_RX_BIAS_EAR_DAC:
+ case WCD938X_RX_BIAS_EAR_AMP:
+ case WCD938X_RX_BIAS_HPH_LDO:
+ case WCD938X_RX_BIAS_HPH_PA:
+ case WCD938X_RX_BIAS_HPH_RDACBUFF_CNP2:
+ case WCD938X_RX_BIAS_HPH_RDAC_LDO:
+ case WCD938X_RX_BIAS_HPH_CNP1:
+ case WCD938X_RX_BIAS_HPH_LOWPOWER:
+ case WCD938X_RX_BIAS_AUX_DAC:
+ case WCD938X_RX_BIAS_AUX_AMP:
+ case WCD938X_RX_BIAS_VNEGDAC_BLEEDER:
+ case WCD938X_RX_BIAS_MISC:
+ case WCD938X_RX_BIAS_BUCK_RST:
+ case WCD938X_RX_BIAS_BUCK_VREF_ERRAMP:
+ case WCD938X_RX_BIAS_FLYB_ERRAMP:
+ case WCD938X_RX_BIAS_FLYB_BUFF:
+ case WCD938X_RX_BIAS_FLYB_MID_RST:
+ case WCD938X_HPH_CNP_EN:
+ case WCD938X_HPH_CNP_WG_CTL:
+ case WCD938X_HPH_CNP_WG_TIME:
+ case WCD938X_HPH_OCP_CTL:
+ case WCD938X_HPH_AUTO_CHOP:
+ case WCD938X_HPH_CHOP_CTL:
+ case WCD938X_HPH_PA_CTL1:
+ case WCD938X_HPH_PA_CTL2:
+ case WCD938X_HPH_L_EN:
+ case WCD938X_HPH_L_TEST:
+ case WCD938X_HPH_L_ATEST:
+ case WCD938X_HPH_R_EN:
+ case WCD938X_HPH_R_TEST:
+ case WCD938X_HPH_R_ATEST:
+ case WCD938X_HPH_RDAC_CLK_CTL1:
+ case WCD938X_HPH_RDAC_CLK_CTL2:
+ case WCD938X_HPH_RDAC_LDO_CTL:
+ case WCD938X_HPH_RDAC_CHOP_CLK_LP_CTL:
+ case WCD938X_HPH_REFBUFF_UHQA_CTL:
+ case WCD938X_HPH_REFBUFF_LP_CTL:
+ case WCD938X_HPH_L_DAC_CTL:
+ case WCD938X_HPH_R_DAC_CTL:
+ case WCD938X_HPH_SURGE_HPHLR_SURGE_COMP_SEL:
+ case WCD938X_HPH_SURGE_HPHLR_SURGE_EN:
+ case WCD938X_HPH_SURGE_HPHLR_SURGE_MISC1:
+ case WCD938X_EAR_EAR_EN_REG:
+ case WCD938X_EAR_EAR_PA_CON:
+ case WCD938X_EAR_EAR_SP_CON:
+ case WCD938X_EAR_EAR_DAC_CON:
+ case WCD938X_EAR_EAR_CNP_FSM_CON:
+ case WCD938X_EAR_TEST_CTL:
+ case WCD938X_ANA_NEW_PAGE_REGISTER:
+ case WCD938X_HPH_NEW_ANA_HPH2:
+ case WCD938X_HPH_NEW_ANA_HPH3:
+ case WCD938X_SLEEP_CTL:
+ case WCD938X_SLEEP_WATCHDOG_CTL:
+ case WCD938X_MBHC_NEW_ELECT_REM_CLAMP_CTL:
+ case WCD938X_MBHC_NEW_CTL_1:
+ case WCD938X_MBHC_NEW_CTL_2:
+ case WCD938X_MBHC_NEW_PLUG_DETECT_CTL:
+ case WCD938X_MBHC_NEW_ZDET_ANA_CTL:
+ case WCD938X_MBHC_NEW_ZDET_RAMP_CTL:
+ case WCD938X_TX_NEW_AMIC_MUX_CFG:
+ case WCD938X_AUX_AUXPA:
+ case WCD938X_LDORXTX_MODE:
+ case WCD938X_LDORXTX_CONFIG:
+ case WCD938X_DIE_CRACK_DIE_CRK_DET_EN:
+ case WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL:
+ case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L:
+ case WCD938X_HPH_NEW_INT_RDAC_VREF_CTL:
+ case WCD938X_HPH_NEW_INT_RDAC_OVERRIDE_CTL:
+ case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R:
+ case WCD938X_HPH_NEW_INT_PA_MISC1:
+ case WCD938X_HPH_NEW_INT_PA_MISC2:
+ case WCD938X_HPH_NEW_INT_PA_RDAC_MISC:
+ case WCD938X_HPH_NEW_INT_HPH_TIMER1:
+ case WCD938X_HPH_NEW_INT_HPH_TIMER2:
+ case WCD938X_HPH_NEW_INT_HPH_TIMER3:
+ case WCD938X_HPH_NEW_INT_HPH_TIMER4:
+ case WCD938X_HPH_NEW_INT_PA_RDAC_MISC2:
+ case WCD938X_HPH_NEW_INT_PA_RDAC_MISC3:
+ case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW:
+ case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW:
+ case WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI:
+ case WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_ULP:
+ case WCD938X_RX_NEW_INT_HPH_RDAC_LDO_LP:
+ case WCD938X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL:
+ case WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL:
+ case WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT:
+ case WCD938X_MBHC_NEW_INT_SPARE_2:
+ case WCD938X_EAR_INT_NEW_EAR_CHOPPER_CON:
+ case WCD938X_EAR_INT_NEW_CNP_VCM_CON1:
+ case WCD938X_EAR_INT_NEW_CNP_VCM_CON2:
+ case WCD938X_EAR_INT_NEW_EAR_DYNAMIC_BIAS:
+ case WCD938X_AUX_INT_EN_REG:
+ case WCD938X_AUX_INT_PA_CTRL:
+ case WCD938X_AUX_INT_SP_CTRL:
+ case WCD938X_AUX_INT_DAC_CTRL:
+ case WCD938X_AUX_INT_CLK_CTRL:
+ case WCD938X_AUX_INT_TEST_CTRL:
+ case WCD938X_AUX_INT_MISC:
+ case WCD938X_LDORXTX_INT_BIAS:
+ case WCD938X_LDORXTX_INT_STB_LOADS_DTEST:
+ case WCD938X_LDORXTX_INT_TEST0:
+ case WCD938X_LDORXTX_INT_STARTUP_TIMER:
+ case WCD938X_LDORXTX_INT_TEST1:
+ case WCD938X_SLEEP_INT_WATCHDOG_CTL_1:
+ case WCD938X_SLEEP_INT_WATCHDOG_CTL_2:
+ case WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT1:
+ case WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT2:
+ case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L2:
+ case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L1:
+ case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L0:
+ case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP1P2M:
+ case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP0P6M:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L2L1:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L0:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_ULP:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L2L1:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L0:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_L2L1L0:
+ case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP:
+ case WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L2L1:
+ case WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L0ULP:
+ case WCD938X_TX_COM_NEW_INT_TXADC_INT_L2:
+ case WCD938X_TX_COM_NEW_INT_TXADC_INT_L1:
+ case WCD938X_TX_COM_NEW_INT_TXADC_INT_L0:
+ case WCD938X_TX_COM_NEW_INT_TXADC_INT_ULP:
+ case WCD938X_DIGITAL_PAGE_REGISTER:
+ case WCD938X_DIGITAL_SWR_TX_CLK_RATE:
+ case WCD938X_DIGITAL_CDC_RST_CTL:
+ case WCD938X_DIGITAL_TOP_CLK_CFG:
+ case WCD938X_DIGITAL_CDC_ANA_CLK_CTL:
+ case WCD938X_DIGITAL_CDC_DIG_CLK_CTL:
+ case WCD938X_DIGITAL_SWR_RST_EN:
+ case WCD938X_DIGITAL_CDC_PATH_MODE:
+ case WCD938X_DIGITAL_CDC_RX_RST:
+ case WCD938X_DIGITAL_CDC_RX0_CTL:
+ case WCD938X_DIGITAL_CDC_RX1_CTL:
+ case WCD938X_DIGITAL_CDC_RX2_CTL:
+ case WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1:
+ case WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3:
+ case WCD938X_DIGITAL_CDC_COMP_CTL_0:
+ case WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A1_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A1_1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A2_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A2_1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A3_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A3_1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A4_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A4_1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A5_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A5_1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A6_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_A7_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_C_0:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_C_1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_C_2:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_C_3:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R1:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R2:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R3:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R4:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R5:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R6:
+ case WCD938X_DIGITAL_CDC_HPH_DSM_R7:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A1_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A1_1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A2_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A2_1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A3_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A3_1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A4_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A4_1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A5_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A5_1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A6_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_A7_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_C_0:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_C_1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_C_2:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_C_3:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R1:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R2:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R3:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R4:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R5:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R6:
+ case WCD938X_DIGITAL_CDC_AUX_DSM_R7:
+ case WCD938X_DIGITAL_CDC_HPH_GAIN_RX_0:
+ case WCD938X_DIGITAL_CDC_HPH_GAIN_RX_1:
+ case WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_0:
+ case WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_1:
+ case WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_2:
+ case WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_0:
+ case WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_1:
+ case WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_2:
+ case WCD938X_DIGITAL_CDC_HPH_GAIN_CTL:
+ case WCD938X_DIGITAL_CDC_AUX_GAIN_CTL:
+ case WCD938X_DIGITAL_CDC_EAR_PATH_CTL:
+ case WCD938X_DIGITAL_CDC_SWR_CLH:
+ case WCD938X_DIGITAL_SWR_CLH_BYP:
+ case WCD938X_DIGITAL_CDC_TX0_CTL:
+ case WCD938X_DIGITAL_CDC_TX1_CTL:
+ case WCD938X_DIGITAL_CDC_TX2_CTL:
+ case WCD938X_DIGITAL_CDC_TX_RST:
+ case WCD938X_DIGITAL_CDC_REQ_CTL:
+ case WCD938X_DIGITAL_CDC_RST:
+ case WCD938X_DIGITAL_CDC_AMIC_CTL:
+ case WCD938X_DIGITAL_CDC_DMIC_CTL:
+ case WCD938X_DIGITAL_CDC_DMIC1_CTL:
+ case WCD938X_DIGITAL_CDC_DMIC2_CTL:
+ case WCD938X_DIGITAL_CDC_DMIC3_CTL:
+ case WCD938X_DIGITAL_CDC_DMIC4_CTL:
+ case WCD938X_DIGITAL_EFUSE_PRG_CTL:
+ case WCD938X_DIGITAL_EFUSE_CTL:
+ case WCD938X_DIGITAL_CDC_DMIC_RATE_1_2:
+ case WCD938X_DIGITAL_CDC_DMIC_RATE_3_4:
+ case WCD938X_DIGITAL_PDM_WD_CTL0:
+ case WCD938X_DIGITAL_PDM_WD_CTL1:
+ case WCD938X_DIGITAL_PDM_WD_CTL2:
+ case WCD938X_DIGITAL_INTR_MODE:
+ case WCD938X_DIGITAL_INTR_MASK_0:
+ case WCD938X_DIGITAL_INTR_MASK_1:
+ case WCD938X_DIGITAL_INTR_MASK_2:
+ case WCD938X_DIGITAL_INTR_CLEAR_0:
+ case WCD938X_DIGITAL_INTR_CLEAR_1:
+ case WCD938X_DIGITAL_INTR_CLEAR_2:
+ case WCD938X_DIGITAL_INTR_LEVEL_0:
+ case WCD938X_DIGITAL_INTR_LEVEL_1:
+ case WCD938X_DIGITAL_INTR_LEVEL_2:
+ case WCD938X_DIGITAL_INTR_SET_0:
+ case WCD938X_DIGITAL_INTR_SET_1:
+ case WCD938X_DIGITAL_INTR_SET_2:
+ case WCD938X_DIGITAL_INTR_TEST_0:
+ case WCD938X_DIGITAL_INTR_TEST_1:
+ case WCD938X_DIGITAL_INTR_TEST_2:
+ case WCD938X_DIGITAL_TX_MODE_DBG_EN:
+ case WCD938X_DIGITAL_TX_MODE_DBG_0_1:
+ case WCD938X_DIGITAL_TX_MODE_DBG_2_3:
+ case WCD938X_DIGITAL_LB_IN_SEL_CTL:
+ case WCD938X_DIGITAL_LOOP_BACK_MODE:
+ case WCD938X_DIGITAL_SWR_DAC_TEST:
+ case WCD938X_DIGITAL_SWR_HM_TEST_RX_0:
+ case WCD938X_DIGITAL_SWR_HM_TEST_TX_0:
+ case WCD938X_DIGITAL_SWR_HM_TEST_RX_1:
+ case WCD938X_DIGITAL_SWR_HM_TEST_TX_1:
+ case WCD938X_DIGITAL_SWR_HM_TEST_TX_2:
+ case WCD938X_DIGITAL_PAD_CTL_SWR_0:
+ case WCD938X_DIGITAL_PAD_CTL_SWR_1:
+ case WCD938X_DIGITAL_I2C_CTL:
+ case WCD938X_DIGITAL_CDC_TX_TANGGU_SW_MODE:
+ case WCD938X_DIGITAL_EFUSE_TEST_CTL_0:
+ case WCD938X_DIGITAL_EFUSE_TEST_CTL_1:
+ case WCD938X_DIGITAL_PAD_CTL_PDM_RX0:
+ case WCD938X_DIGITAL_PAD_CTL_PDM_RX1:
+ case WCD938X_DIGITAL_PAD_CTL_PDM_TX0:
+ case WCD938X_DIGITAL_PAD_CTL_PDM_TX1:
+ case WCD938X_DIGITAL_PAD_CTL_PDM_TX2:
+ case WCD938X_DIGITAL_PAD_INP_DIS_0:
+ case WCD938X_DIGITAL_PAD_INP_DIS_1:
+ case WCD938X_DIGITAL_DRIVE_STRENGTH_0:
+ case WCD938X_DIGITAL_DRIVE_STRENGTH_1:
+ case WCD938X_DIGITAL_DRIVE_STRENGTH_2:
+ case WCD938X_DIGITAL_RX_DATA_EDGE_CTL:
+ case WCD938X_DIGITAL_TX_DATA_EDGE_CTL:
+ case WCD938X_DIGITAL_GPIO_MODE:
+ case WCD938X_DIGITAL_PIN_CTL_OE:
+ case WCD938X_DIGITAL_PIN_CTL_DATA_0:
+ case WCD938X_DIGITAL_PIN_CTL_DATA_1:
+ case WCD938X_DIGITAL_DIG_DEBUG_CTL:
+ case WCD938X_DIGITAL_DIG_DEBUG_EN:
+ case WCD938X_DIGITAL_ANA_CSR_DBG_ADD:
+ case WCD938X_DIGITAL_ANA_CSR_DBG_CTL:
+ case WCD938X_DIGITAL_SSP_DBG:
+ case WCD938X_DIGITAL_SPARE_0:
+ case WCD938X_DIGITAL_SPARE_1:
+ case WCD938X_DIGITAL_SPARE_2:
+ case WCD938X_DIGITAL_TX_REQ_FB_CTL_0:
+ case WCD938X_DIGITAL_TX_REQ_FB_CTL_1:
+ case WCD938X_DIGITAL_TX_REQ_FB_CTL_2:
+ case WCD938X_DIGITAL_TX_REQ_FB_CTL_3:
+ case WCD938X_DIGITAL_TX_REQ_FB_CTL_4:
+ case WCD938X_DIGITAL_DEM_BYPASS_DATA0:
+ case WCD938X_DIGITAL_DEM_BYPASS_DATA1:
+ case WCD938X_DIGITAL_DEM_BYPASS_DATA2:
+ case WCD938X_DIGITAL_DEM_BYPASS_DATA3:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wcd938x_readonly_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD938X_ANA_MBHC_RESULT_1:
+ case WCD938X_ANA_MBHC_RESULT_2:
+ case WCD938X_ANA_MBHC_RESULT_3:
+ case WCD938X_MBHC_MOISTURE_DET_FSM_STATUS:
+ case WCD938X_TX_1_2_SAR2_ERR:
+ case WCD938X_TX_1_2_SAR1_ERR:
+ case WCD938X_TX_3_4_SAR4_ERR:
+ case WCD938X_TX_3_4_SAR3_ERR:
+ case WCD938X_HPH_L_STATUS:
+ case WCD938X_HPH_R_STATUS:
+ case WCD938X_HPH_SURGE_HPHLR_SURGE_STATUS:
+ case WCD938X_EAR_STATUS_REG_1:
+ case WCD938X_EAR_STATUS_REG_2:
+ case WCD938X_MBHC_NEW_FSM_STATUS:
+ case WCD938X_MBHC_NEW_ADC_RESULT:
+ case WCD938X_DIE_CRACK_DIE_CRK_DET_OUT:
+ case WCD938X_AUX_INT_STATUS_REG:
+ case WCD938X_LDORXTX_INT_STATUS:
+ case WCD938X_DIGITAL_CHIP_ID0:
+ case WCD938X_DIGITAL_CHIP_ID1:
+ case WCD938X_DIGITAL_CHIP_ID2:
+ case WCD938X_DIGITAL_CHIP_ID3:
+ case WCD938X_DIGITAL_INTR_STATUS_0:
+ case WCD938X_DIGITAL_INTR_STATUS_1:
+ case WCD938X_DIGITAL_INTR_STATUS_2:
+ case WCD938X_DIGITAL_INTR_CLEAR_0:
+ case WCD938X_DIGITAL_INTR_CLEAR_1:
+ case WCD938X_DIGITAL_INTR_CLEAR_2:
+ case WCD938X_DIGITAL_SWR_HM_TEST_0:
+ case WCD938X_DIGITAL_SWR_HM_TEST_1:
+ case WCD938X_DIGITAL_EFUSE_T_DATA_0:
+ case WCD938X_DIGITAL_EFUSE_T_DATA_1:
+ case WCD938X_DIGITAL_PIN_STATUS_0:
+ case WCD938X_DIGITAL_PIN_STATUS_1:
+ case WCD938X_DIGITAL_MODE_STATUS_0:
+ case WCD938X_DIGITAL_MODE_STATUS_1:
+ case WCD938X_DIGITAL_EFUSE_REG_0:
+ case WCD938X_DIGITAL_EFUSE_REG_1:
+ case WCD938X_DIGITAL_EFUSE_REG_2:
+ case WCD938X_DIGITAL_EFUSE_REG_3:
+ case WCD938X_DIGITAL_EFUSE_REG_4:
+ case WCD938X_DIGITAL_EFUSE_REG_5:
+ case WCD938X_DIGITAL_EFUSE_REG_6:
+ case WCD938X_DIGITAL_EFUSE_REG_7:
+ case WCD938X_DIGITAL_EFUSE_REG_8:
+ case WCD938X_DIGITAL_EFUSE_REG_9:
+ case WCD938X_DIGITAL_EFUSE_REG_10:
+ case WCD938X_DIGITAL_EFUSE_REG_11:
+ case WCD938X_DIGITAL_EFUSE_REG_12:
+ case WCD938X_DIGITAL_EFUSE_REG_13:
+ case WCD938X_DIGITAL_EFUSE_REG_14:
+ case WCD938X_DIGITAL_EFUSE_REG_15:
+ case WCD938X_DIGITAL_EFUSE_REG_16:
+ case WCD938X_DIGITAL_EFUSE_REG_17:
+ case WCD938X_DIGITAL_EFUSE_REG_18:
+ case WCD938X_DIGITAL_EFUSE_REG_19:
+ case WCD938X_DIGITAL_EFUSE_REG_20:
+ case WCD938X_DIGITAL_EFUSE_REG_21:
+ case WCD938X_DIGITAL_EFUSE_REG_22:
+ case WCD938X_DIGITAL_EFUSE_REG_23:
+ case WCD938X_DIGITAL_EFUSE_REG_24:
+ case WCD938X_DIGITAL_EFUSE_REG_25:
+ case WCD938X_DIGITAL_EFUSE_REG_26:
+ case WCD938X_DIGITAL_EFUSE_REG_27:
+ case WCD938X_DIGITAL_EFUSE_REG_28:
+ case WCD938X_DIGITAL_EFUSE_REG_29:
+ case WCD938X_DIGITAL_EFUSE_REG_30:
+ case WCD938X_DIGITAL_EFUSE_REG_31:
+ return true;
+ }
+ return false;
+}
+
+static bool wcd938x_readable_register(struct device *dev, unsigned int reg)
+{
+ bool ret;
+
+ ret = wcd938x_readonly_register(dev, reg);
+ if (!ret)
+ return wcd938x_rdwr_register(dev, reg);
+
+ return ret;
+}
+
+static bool wcd938x_writeable_register(struct device *dev, unsigned int reg)
+{
+ return wcd938x_rdwr_register(dev, reg);
+}
+
+static bool wcd938x_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg <= WCD938X_BASE_ADDRESS)
+ return false;
+
+ if (reg == WCD938X_DIGITAL_SWR_TX_CLK_RATE)
+ return true;
+
+ if (wcd938x_readonly_register(dev, reg))
+ return true;
+
+ return false;
+}
+
+static const struct regmap_config wcd938x_regmap_config = {
+ .name = "wcd938x_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wcd938x_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wcd938x_defaults),
+ .max_register = WCD938X_MAX_REGISTER,
+ .readable_reg = wcd938x_readable_register,
+ .writeable_reg = wcd938x_writeable_register,
+ .volatile_reg = wcd938x_volatile_register,
+};
+
+static const struct sdw_slave_ops wcd9380_slave_ops = {
+ .update_status = wcd_update_status,
+ .interrupt_callback = wcd9380_interrupt_callback,
+ .bus_config = wcd_bus_config,
+};
+
+static int wcd9380_probe(struct sdw_slave *pdev,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd938x_sdw_priv *wcd;
+ int ret;
+
+ wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
+ if (!wcd)
+ return -ENOMEM;
+
+ /*
+ * Port map index starts with 0, however the data port for this codec
+ * are from index 1
+ */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ wcd->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+ &pdev->m_port_map[1],
+ WCD938X_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+ &pdev->m_port_map[1],
+ WCD938X_MAX_SWR_PORTS);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static Port mapping not specified\n");
+
+ wcd->sdev = pdev;
+ dev_set_drvdata(dev, wcd);
+
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+ SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
+ if (wcd->is_tx) {
+ pdev->prop.source_ports = GENMASK(WCD938X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.src_dpn_prop = wcd938x_dpn_prop;
+ wcd->ch_info = &wcd938x_sdw_tx_ch_info[0];
+ pdev->prop.wake_capable = true;
+ } else {
+ pdev->prop.sink_ports = GENMASK(WCD938X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = wcd938x_dpn_prop;
+ wcd->ch_info = &wcd938x_sdw_rx_ch_info[0];
+ }
+
+ if (wcd->is_tx) {
+ wcd->regmap = devm_regmap_init_sdw(pdev, &wcd938x_regmap_config);
+ if (IS_ERR(wcd->regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+ "Regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(wcd->regmap, true);
+ }
+
+ ret = component_add(dev, &wcd_sdw_component_ops);
+ if (ret)
+ return ret;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+}
+
+static int wcd9380_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &wcd_sdw_component_ops);
+
+ return 0;
+}
+
+static const struct sdw_device_id wcd9380_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10d, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, wcd9380_slave_id);
+
+static int wcd938x_sdw_runtime_suspend(struct device *dev)
+{
+ struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ if (wcd->regmap) {
+ regcache_cache_only(wcd->regmap, true);
+ regcache_mark_dirty(wcd->regmap);
+ }
+
+ return 0;
+}
+
+static int wcd938x_sdw_runtime_resume(struct device *dev)
+{
+ struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ if (wcd->regmap) {
+ regcache_cache_only(wcd->regmap, false);
+ regcache_sync(wcd->regmap);
+ }
+
+ pm_runtime_mark_last_busy(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wcd938x_sdw_pm_ops = {
+ RUNTIME_PM_OPS(wcd938x_sdw_runtime_suspend, wcd938x_sdw_runtime_resume, NULL)
+};
+
+
+static struct sdw_driver wcd9380_codec_driver = {
+ .probe = wcd9380_probe,
+ .remove = wcd9380_remove,
+ .ops = &wcd9380_slave_ops,
+ .id_table = wcd9380_slave_id,
+ .driver = {
+ .name = "wcd9380-codec",
+ .pm = pm_ptr(&wcd938x_sdw_pm_ops),
+ }
+};
+module_sdw_driver(wcd9380_codec_driver);
+
+MODULE_DESCRIPTION("WCD938X SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
new file mode 100644
index 000000000000..f5b7de2bc896
--- /dev/null
+++ b/sound/soc/codecs/wcd938x.c
@@ -0,0 +1,3559 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/mux/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
+#include "wcd-mbhc-v2.h"
+#include "wcd938x.h"
+
+#define CHIPID_WCD9380 0x0
+#define CHIPID_WCD9385 0x5
+
+#define WCD938X_MAX_MICBIAS (4)
+#define WCD938X_MBHC_MAX_BUTTONS (8)
+#define TX_ADC_MAX (4)
+
+#define WCD938X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+/* Fractional Rates */
+#define WCD938X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400)
+#define WCD938X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+#define SWR_CLK_RATE_0P6MHZ (600000)
+#define SWR_CLK_RATE_1P2MHZ (1200000)
+#define SWR_CLK_RATE_2P4MHZ (2400000)
+#define SWR_CLK_RATE_4P8MHZ (4800000)
+#define SWR_CLK_RATE_9P6MHZ (9600000)
+#define SWR_CLK_RATE_11P2896MHZ (1128960)
+
+#define EAR_RX_PATH_AUX (1)
+
+#define ADC_MODE_VAL_HIFI 0x01
+#define ADC_MODE_VAL_LO_HIF 0x02
+#define ADC_MODE_VAL_NORMAL 0x03
+#define ADC_MODE_VAL_LP 0x05
+#define ADC_MODE_VAL_ULP1 0x09
+#define ADC_MODE_VAL_ULP2 0x0B
+
+/* Z value defined in milliohm */
+#define WCD938X_ZDET_VAL_32 (32000)
+#define WCD938X_ZDET_VAL_400 (400000)
+#define WCD938X_ZDET_VAL_1200 (1200000)
+#define WCD938X_ZDET_VAL_100K (100000000)
+/* Z floating defined in ohms */
+#define WCD938X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE)
+#define WCD938X_ZDET_NUM_MEASUREMENTS (900)
+#define WCD938X_MBHC_GET_C1(c) ((c & 0xC000) >> 14)
+#define WCD938X_MBHC_GET_X1(x) (x & 0x3FFF)
+/* Z value compared in milliOhm */
+#define WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define WCD938X_MBHC_ZDET_CONST (86 * 16384)
+#define WCD_MBHC_HS_V_MAX 1600
+
+#define WCD938X_EAR_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, snd_soc_get_volsw, \
+ wcd938x_ear_pa_put_gain, tlv_array)
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ WCD938X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET,
+ WCD938X_IRQ_MBHC_ELECT_INS_REM_DET,
+ WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ WCD938X_IRQ_MBHC_SW_DET,
+ WCD938X_IRQ_HPHR_OCP_INT,
+ WCD938X_IRQ_HPHR_CNP_INT,
+ WCD938X_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ WCD938X_IRQ_HPHL_CNP_INT,
+ WCD938X_IRQ_EAR_CNP_INT,
+ WCD938X_IRQ_EAR_SCD_INT,
+ WCD938X_IRQ_AUX_CNP_INT,
+ WCD938X_IRQ_AUX_SCD_INT,
+ WCD938X_IRQ_HPHL_PDM_WD_INT,
+ WCD938X_IRQ_HPHR_PDM_WD_INT,
+ WCD938X_IRQ_AUX_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ WCD938X_IRQ_LDORT_SCD_INT,
+ WCD938X_IRQ_MBHC_MOISTURE_INT,
+ WCD938X_IRQ_HPHL_SURGE_DET_INT,
+ WCD938X_IRQ_HPHR_SURGE_DET_INT,
+ WCD938X_NUM_IRQS,
+};
+
+enum {
+ WCD_ADC1 = 0,
+ WCD_ADC2,
+ WCD_ADC3,
+ WCD_ADC4,
+ ALLOW_BUCK_DISABLE,
+ HPH_COMP_DELAY,
+ HPH_PA_DELAY,
+ AMIC2_BCS_ENABLE,
+ WCD_SUPPLIES_LPM_MODE,
+};
+
+enum {
+ ADC_MODE_INVALID = 0,
+ ADC_MODE_HIFI,
+ ADC_MODE_LO_HIF,
+ ADC_MODE_NORMAL,
+ ADC_MODE_LP,
+ ADC_MODE_ULP1,
+ ADC_MODE_ULP2,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+static u8 tx_mode_bit[] = {
+ [ADC_MODE_INVALID] = 0x00,
+ [ADC_MODE_HIFI] = 0x01,
+ [ADC_MODE_LO_HIF] = 0x02,
+ [ADC_MODE_NORMAL] = 0x04,
+ [ADC_MODE_LP] = 0x08,
+ [ADC_MODE_ULP1] = 0x10,
+ [ADC_MODE_ULP2] = 0x20,
+};
+
+struct wcd938x_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct wcd938x_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode, *txnode;
+ struct regmap *regmap;
+ struct mutex micb_lock;
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct wcd_clsh_ctrl *clsh_info;
+ struct wcd_common common;
+ struct irq_domain *virq;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[WCD938X_MAX_MICBIAS];
+ s32 pullup_ref[WCD938X_MAX_MICBIAS];
+ u32 hph_mode;
+ u32 tx_mode[TX_ADC_MAX];
+ int flyback_cur_det_disable;
+ int ear_rx_path;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *us_euro_gpio;
+ struct mux_control *us_euro_mux;
+ unsigned int mux_state;
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ int aux_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+ bool ldoh;
+ bool mux_setup_done;
+};
+
+static const char * const wcd938x_supplies[] = {
+ "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias",
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
+static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000);
+
+struct wcd938x_mbhc_zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
+static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD938X_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD938X_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD938X_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD938X_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD938X_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD938X_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD938X_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD938X_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD938X_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD938X_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD938X_MBHC_NEW_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD938X_MBHC_NEW_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD938X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD938X_HPH_OCP_CTL, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD938X_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD938X_ANA_MICB2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD938X_HPH_CNP_WG_TIME, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD938X_ANA_HPH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD938X_ANA_HPH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD938X_ANA_HPH, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD938X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD938X_MBHC_CTL_BCS, 0x02),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD938X_MBHC_NEW_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD938X_MBHC_NEW_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD938X_MBHC_NEW_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD938X_HPH_PA_CTL2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD938X_HPH_PA_CTL2, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD938X_HPH_L_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD938X_HPH_R_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD938X_DIGITAL_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD938X_DIGITAL_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD938X_MBHC_NEW_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD938X_MBHC_NEW_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD938X_MBHC_NEW_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD938X_MBHC_NEW_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD938X_ANA_MICB2, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD938X_MBHC_NEW_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD938X_MBHC_NEW_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD938X_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq wcd938x_irqs[WCD938X_NUM_IRQS] = {
+ REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01),
+ REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02),
+ REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04),
+ REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08),
+ REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_SW_DET, 0, 0x10),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_OCP_INT, 0, 0x20),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_CNP_INT, 0, 0x40),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_OCP_INT, 0, 0x80),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_CNP_INT, 1, 0x01),
+ REGMAP_IRQ_REG(WCD938X_IRQ_EAR_CNP_INT, 1, 0x02),
+ REGMAP_IRQ_REG(WCD938X_IRQ_EAR_SCD_INT, 1, 0x04),
+ REGMAP_IRQ_REG(WCD938X_IRQ_AUX_CNP_INT, 1, 0x08),
+ REGMAP_IRQ_REG(WCD938X_IRQ_AUX_SCD_INT, 1, 0x10),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_PDM_WD_INT, 1, 0x20),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_PDM_WD_INT, 1, 0x40),
+ REGMAP_IRQ_REG(WCD938X_IRQ_AUX_PDM_WD_INT, 1, 0x80),
+ REGMAP_IRQ_REG(WCD938X_IRQ_LDORT_SCD_INT, 2, 0x01),
+ REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_MOISTURE_INT, 2, 0x02),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04),
+ REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08),
+};
+
+static const struct regmap_irq_chip wcd938x_regmap_irq_chip = {
+ .name = "wcd938x",
+ .irqs = wcd938x_irqs,
+ .num_irqs = ARRAY_SIZE(wcd938x_irqs),
+ .num_regs = 3,
+ .status_base = WCD938X_DIGITAL_INTR_STATUS_0,
+ .mask_base = WCD938X_DIGITAL_INTR_MASK_0,
+ .ack_base = WCD938X_DIGITAL_INTR_CLEAR_0,
+ .use_ack = 1,
+ .runtime_pm = true,
+ .irq_drv_data = NULL,
+};
+
+static int wcd938x_get_clk_rate(int mode)
+{
+ int rate;
+
+ switch (mode) {
+ case ADC_MODE_ULP2:
+ rate = SWR_CLK_RATE_0P6MHZ;
+ break;
+ case ADC_MODE_ULP1:
+ rate = SWR_CLK_RATE_1P2MHZ;
+ break;
+ case ADC_MODE_LP:
+ rate = SWR_CLK_RATE_4P8MHZ;
+ break;
+ case ADC_MODE_NORMAL:
+ case ADC_MODE_LO_HIF:
+ case ADC_MODE_HIFI:
+ case ADC_MODE_INVALID:
+ default:
+ rate = SWR_CLK_RATE_9P6MHZ;
+ break;
+ }
+
+ return rate;
+}
+
+static int wcd938x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank)
+{
+ u8 mask = (bank ? 0xF0 : 0x0F);
+ u8 val = 0;
+
+ switch (rate) {
+ case SWR_CLK_RATE_0P6MHZ:
+ val = (bank ? 0x60 : 0x06);
+ break;
+ case SWR_CLK_RATE_1P2MHZ:
+ val = (bank ? 0x50 : 0x05);
+ break;
+ case SWR_CLK_RATE_2P4MHZ:
+ val = (bank ? 0x30 : 0x03);
+ break;
+ case SWR_CLK_RATE_4P8MHZ:
+ val = (bank ? 0x10 : 0x01);
+ break;
+ case SWR_CLK_RATE_9P6MHZ:
+ default:
+ val = 0x00;
+ break;
+ }
+ snd_soc_component_update_bits(component, WCD938X_DIGITAL_SWR_TX_CLK_RATE,
+ mask, val);
+
+ return 0;
+}
+
+static int wcd938x_io_init(struct wcd938x_priv *wcd938x)
+{
+ struct regmap *rm = wcd938x->regmap;
+
+ regmap_update_bits(rm, WCD938X_SLEEP_CTL, 0x0E, 0x0E);
+ regmap_update_bits(rm, WCD938X_SLEEP_CTL, 0x80, 0x80);
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1010);
+ regmap_update_bits(rm, WCD938X_SLEEP_CTL, 0x40, 0x40);
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1010);
+ regmap_update_bits(rm, WCD938X_LDORXTX_CONFIG, 0x10, 0x00);
+ regmap_update_bits(rm, WCD938X_BIAS_VBG_FINE_ADJ,
+ 0xF0, 0x80);
+ regmap_update_bits(rm, WCD938X_ANA_BIAS, 0x80, 0x80);
+ regmap_update_bits(rm, WCD938X_ANA_BIAS, 0x40, 0x40);
+ /* 10 msec delay as per HW requirement */
+ usleep_range(10000, 10010);
+
+ regmap_update_bits(rm, WCD938X_ANA_BIAS, 0x40, 0x00);
+ regmap_update_bits(rm, WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x00);
+ regmap_update_bits(rm, WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW,
+ 0x1F, 0x15);
+ regmap_update_bits(rm, WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW,
+ 0x1F, 0x15);
+ regmap_update_bits(rm, WCD938X_HPH_REFBUFF_UHQA_CTL,
+ 0xC0, 0x80);
+ regmap_update_bits(rm, WCD938X_DIGITAL_CDC_DMIC_CTL,
+ 0x02, 0x02);
+
+ regmap_update_bits(rm, WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP,
+ 0xFF, 0x14);
+ regmap_update_bits(rm, WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP,
+ 0x1F, 0x08);
+
+ regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_0, 0xFF, 0x55);
+ regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_1, 0xFF, 0x44);
+ regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_2, 0xFF, 0x11);
+ regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_3, 0xFF, 0x00);
+ regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_4, 0xFF, 0x00);
+
+ /* Set Noise Filter Resistor value */
+ regmap_update_bits(rm, WCD938X_MICB1_TEST_CTL_1, 0xE0, 0xE0);
+ regmap_update_bits(rm, WCD938X_MICB2_TEST_CTL_1, 0xE0, 0xE0);
+ regmap_update_bits(rm, WCD938X_MICB3_TEST_CTL_1, 0xE0, 0xE0);
+ regmap_update_bits(rm, WCD938X_MICB4_TEST_CTL_1, 0xE0, 0xE0);
+
+ regmap_update_bits(rm, WCD938X_TX_3_4_TEST_BLK_EN2, 0x01, 0x00);
+ regmap_update_bits(rm, WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0);
+
+ return 0;
+
+}
+
+static int wcd938x_sdw_connect_port(const struct wcd_sdw_ch_info *ch_info,
+ struct sdw_port_config *port_config,
+ u8 enable)
+{
+ u8 ch_mask, port_num;
+
+ port_num = ch_info->port_num;
+ ch_mask = ch_info->ch_mask;
+
+ port_config->num = port_num;
+
+ if (enable)
+ port_config->ch_mask |= ch_mask;
+ else
+ port_config->ch_mask &= ~ch_mask;
+
+ return 0;
+}
+
+static int wcd938x_connect_port(struct wcd938x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable)
+{
+ return wcd938x_sdw_connect_port(&wcd->ch_info[ch_id],
+ &wcd->port_config[port_num - 1],
+ enable);
+}
+
+static int wcd938x_codec_enable_rxclk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_RX_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_RX_BIAS_EN_MASK, 1);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_RX0_CTL,
+ WCD938X_DEM_DITHER_ENABLE_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_RX1_CTL,
+ WCD938X_DEM_DITHER_ENABLE_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_RX2_CTL,
+ WCD938X_DEM_DITHER_ENABLE_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_RX_DIV2_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component, WCD938X_AUX_AUXPA,
+ WCD938X_AUXPA_CLK_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_VNEG_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_VPOS_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_RX_BIAS_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_RX_DIV2_CLK_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_RX_CLK_EN_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static int wcd938x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD0_CLK_EN_MASK, 0x01);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD938X_HPHL_RX_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_RDAC_CLK_CTL1,
+ WCD938X_CHOP_CLK_EN_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ WCD938X_HPH_RES_DIV_MASK, 0x02);
+ if (wcd938x->comp1_enable) {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0,
+ WCD938X_HPHL_COMP_EN_MASK, 1);
+ /* 5msec compander delay as per HW requirement */
+ if (!wcd938x->comp2_enable || (snd_soc_component_read(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0) & 0x01))
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1,
+ WCD938X_AUTOCHOP_TIMER_EN, 0);
+ } else {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0,
+ WCD938X_HPHL_COMP_EN_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_L_EN,
+ WCD938X_GAIN_SRC_SEL_MASK,
+ WCD938X_GAIN_SRC_SEL_REGISTER);
+
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ WCD938X_HPH_RES_DIV_MASK, 0x1);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD1_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD938X_HPHR_RX_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_RDAC_CLK_CTL1,
+ WCD938X_CHOP_CLK_EN_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ WCD938X_HPH_RES_DIV_MASK, 0x02);
+ if (wcd938x->comp2_enable) {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0,
+ WCD938X_HPHR_COMP_EN_MASK, 1);
+ /* 5msec compander delay as per HW requirement */
+ if (!wcd938x->comp1_enable ||
+ (snd_soc_component_read(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0) & 0x02))
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1,
+ WCD938X_AUTOCHOP_TIMER_EN, 0);
+ } else {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0,
+ WCD938X_HPHR_COMP_EN_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_R_EN,
+ WCD938X_GAIN_SRC_SEL_MASK,
+ WCD938X_GAIN_SRC_SEL_REGISTER);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ WCD938X_HPH_RES_DIV_MASK, 0x01);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd938x->ear_rx_path =
+ snd_soc_component_read(
+ component, WCD938X_DIGITAL_CDC_EAR_PATH_CTL);
+ if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) {
+ snd_soc_component_write_field(component,
+ WCD938X_EAR_EAR_DAC_CON,
+ WCD938X_DAC_SAMPLE_EDGE_SEL_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_AUX_GAIN_CTL,
+ WCD938X_AUX_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD2_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_GAIN_OVRD_REG_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD938X_HPHL_RX_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD0_CLK_EN_MASK, 1);
+ if (wcd938x->comp1_enable)
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0,
+ WCD938X_HPHL_COMP_EN_MASK, 1);
+ }
+ /* 5 msec delay as per HW requirement */
+ usleep_range(5000, 5010);
+ if (wcd938x->flyback_cur_det_disable == 0)
+ snd_soc_component_write_field(component, WCD938X_FLYBACK_EN,
+ WCD938X_EN_CUR_DET_MASK, 0);
+ wcd938x->flyback_cur_det_disable++;
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR,
+ wcd938x->hph_mode);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_AUX_GAIN_CTL,
+ WCD938X_AUX_EN_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD2_CLK_EN_MASK, 0);
+ } else {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD938X_HPHL_RX_EN_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD0_CLK_EN_MASK, 0);
+ if (wcd938x->comp1_enable)
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_COMP_CTL_0,
+ WCD938X_HPHL_COMP_EN_MASK, 0);
+ }
+ snd_soc_component_write_field(component, WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_GAIN_OVRD_REG_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_EAR_EAR_DAC_CON,
+ WCD938X_DAC_SAMPLE_EDGE_SEL_MASK, 1);
+ break;
+ }
+ return 0;
+
+}
+
+static int wcd938x_codec_aux_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_RX_DIV4_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_RXD2_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_AUX_GAIN_CTL,
+ WCD938X_AUX_EN_MASK, 1);
+ if (wcd938x->flyback_cur_det_disable == 0)
+ snd_soc_component_write_field(component, WCD938X_FLYBACK_EN,
+ WCD938X_EN_CUR_DET_MASK, 0);
+ wcd938x->flyback_cur_det_disable++;
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_AUX,
+ wcd938x->hph_mode);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_RX_DIV4_CLK_EN_MASK, 0);
+ break;
+ }
+ return 0;
+
+}
+
+static int wcd938x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd938x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wcd938x->ldoh)
+ snd_soc_component_write_field(component, WCD938X_LDOH_MODE,
+ WCD938X_LDOH_EN_MASK, 1);
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR, hph_mode);
+ wcd_clsh_set_hph_mode(wcd938x->clsh_info, CLS_H_HIFI);
+
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_ULP) {
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_REFBUFF_LP_CTL,
+ WCD938X_PREREF_FLIT_BYPASS_MASK, 1);
+ }
+ snd_soc_component_write_field(component, WCD938X_ANA_HPH,
+ WCD938X_HPHR_REF_EN_MASK, 1);
+ wcd_clsh_set_hph_mode(wcd938x->clsh_info, hph_mode);
+ /* 100 usec delay as per HW requirement */
+ usleep_range(100, 110);
+ set_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_PDM_WD_CTL1,
+ WCD938X_PDM_WD_EN_MASK, 0x3);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) {
+ if (!wcd938x->comp2_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_ULP)
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_REFBUFF_LP_CTL,
+ WCD938X_PREREF_FLIT_BYPASS_MASK, 0);
+ clear_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ }
+ snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1,
+ WCD938X_AUTOCHOP_TIMER_EN, 1);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+ hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_REGULATOR_MODE_MASK,
+ WCD938X_REGULATOR_MODE_CLASS_AB);
+ enable_irq(wcd938x->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd938x->hphr_pdm_wd_int);
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (!wcd938x->comp2_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ snd_soc_component_write_field(component, WCD938X_ANA_HPH,
+ WCD938X_HPHR_EN_MASK, 0);
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_PRE_HPHR_PA_OFF);
+ set_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) {
+ if (!wcd938x->comp2_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ }
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_POST_HPHR_PA_OFF);
+ snd_soc_component_write_field(component, WCD938X_ANA_HPH,
+ WCD938X_HPHR_REF_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL1,
+ WCD938X_PDM_WD_EN_MASK, 0);
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR, hph_mode);
+ if (wcd938x->ldoh)
+ snd_soc_component_write_field(component, WCD938X_LDOH_MODE,
+ WCD938X_LDOH_EN_MASK, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd938x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wcd938x->ldoh)
+ snd_soc_component_write_field(component, WCD938X_LDOH_MODE,
+ WCD938X_LDOH_EN_MASK, 1);
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL, hph_mode);
+ wcd_clsh_set_hph_mode(wcd938x->clsh_info, CLS_H_HIFI);
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_ULP) {
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_REFBUFF_LP_CTL,
+ WCD938X_PREREF_FLIT_BYPASS_MASK, 1);
+ }
+ snd_soc_component_write_field(component, WCD938X_ANA_HPH,
+ WCD938X_HPHL_REF_EN_MASK, 1);
+ wcd_clsh_set_hph_mode(wcd938x->clsh_info, hph_mode);
+ /* 100 usec delay as per HW requirement */
+ usleep_range(100, 110);
+ set_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_PDM_WD_CTL0,
+ WCD938X_PDM_WD_EN_MASK, 0x3);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) {
+ if (!wcd938x->comp1_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_ULP)
+ snd_soc_component_write_field(component,
+ WCD938X_HPH_REFBUFF_LP_CTL,
+ WCD938X_PREREF_FLIT_BYPASS_MASK, 0);
+ clear_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ }
+
+ snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1,
+ WCD938X_AUTOCHOP_TIMER_EN, 1);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+ hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_REGULATOR_MODE_MASK,
+ WCD938X_REGULATOR_MODE_CLASS_AB);
+ enable_irq(wcd938x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd938x->hphl_pdm_wd_int);
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (!wcd938x->comp1_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ snd_soc_component_write_field(component, WCD938X_ANA_HPH,
+ WCD938X_HPHL_EN_MASK, 0);
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF);
+ set_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) {
+ if (!wcd938x->comp1_enable)
+ usleep_range(21000, 21100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &wcd938x->status_mask);
+ }
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_POST_HPHL_PA_OFF);
+ snd_soc_component_write_field(component, WCD938X_ANA_HPH,
+ WCD938X_HPHL_REF_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL0,
+ WCD938X_PDM_WD_EN_MASK, 0);
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL, hph_mode);
+ if (wcd938x->ldoh)
+ snd_soc_component_write_field(component, WCD938X_LDOH_MODE,
+ WCD938X_LDOH_EN_MASK, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd938x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2,
+ WCD938X_AUX_PDM_WD_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1010);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+ hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_REGULATOR_MODE_MASK,
+ WCD938X_REGULATOR_MODE_CLASS_AB);
+ enable_irq(wcd938x->aux_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd938x->aux_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2,
+ WCD938X_AUX_PDM_WD_EN_MASK, 0);
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_AUX,
+ hph_mode);
+
+ wcd938x->flyback_cur_det_disable--;
+ if (wcd938x->flyback_cur_det_disable == 0)
+ snd_soc_component_write_field(component, WCD938X_FLYBACK_EN,
+ WCD938X_EN_CUR_DET_MASK, 1);
+ break;
+ }
+ return 0;
+}
+
+static int wcd938x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd938x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * Enable watchdog interrupt for HPHL or AUX
+ * depending on mux value
+ */
+ wcd938x->ear_rx_path = snd_soc_component_read(component,
+ WCD938X_DIGITAL_CDC_EAR_PATH_CTL);
+ if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX)
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2,
+ WCD938X_AUX_PDM_WD_EN_MASK, 1);
+ else
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_PDM_WD_CTL0,
+ WCD938X_PDM_WD_EN_MASK, 0x3);
+ if (!wcd938x->comp1_enable)
+ snd_soc_component_write_field(component,
+ WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_GAIN_OVRD_REG_MASK, 1);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 6 msec delay as per HW requirement */
+ usleep_range(6000, 6010);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+ hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+ snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES,
+ WCD938X_REGULATOR_MODE_MASK,
+ WCD938X_REGULATOR_MODE_CLASS_AB);
+ if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX)
+ enable_irq(wcd938x->aux_pdm_wd_int);
+ else
+ enable_irq(wcd938x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX)
+ disable_irq_nosync(wcd938x->aux_pdm_wd_int);
+ else
+ disable_irq_nosync(wcd938x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!wcd938x->comp1_enable)
+ snd_soc_component_write_field(component, WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_GAIN_OVRD_REG_MASK, 0);
+ /* 7 msec delay as per HW requirement */
+ usleep_range(7000, 7010);
+ if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX)
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2,
+ WCD938X_AUX_PDM_WD_EN_MASK, 0);
+ else
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL0,
+ WCD938X_PDM_WD_EN_MASK, 0);
+
+ wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR, hph_mode);
+
+ wcd938x->flyback_cur_det_disable--;
+ if (wcd938x->flyback_cur_det_disable == 0)
+ snd_soc_component_write_field(component, WCD938X_FLYBACK_EN,
+ WCD938X_EN_CUR_DET_MASK, 1);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 dmic_clk_reg, dmic_clk_en_reg;
+ u8 dmic_sel_mask, dmic_clk_mask;
+
+ switch (w->shift) {
+ case 0:
+ case 1:
+ dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_1_2;
+ dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC1_CTL;
+ dmic_clk_mask = WCD938X_DMIC1_RATE_MASK;
+ dmic_sel_mask = WCD938X_AMIC1_IN_SEL_MASK;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_1_2;
+ dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC2_CTL;
+ dmic_clk_mask = WCD938X_DMIC2_RATE_MASK;
+ dmic_sel_mask = WCD938X_AMIC3_IN_SEL_MASK;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_3_4;
+ dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC3_CTL;
+ dmic_clk_mask = WCD938X_DMIC3_RATE_MASK;
+ dmic_sel_mask = WCD938X_AMIC4_IN_SEL_MASK;
+ break;
+ case 6:
+ case 7:
+ dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_3_4;
+ dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC4_CTL;
+ dmic_clk_mask = WCD938X_DMIC4_RATE_MASK;
+ dmic_sel_mask = WCD938X_AMIC5_IN_SEL_MASK;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_AMIC_CTL,
+ dmic_sel_mask,
+ WCD938X_AMIC1_IN_SEL_DMIC);
+ /* 250us sleep as per HW requirement */
+ usleep_range(250, 260);
+ /* Setting DMIC clock rate to 2.4MHz */
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ dmic_clk_mask,
+ WCD938X_DMIC4_RATE_2P4MHZ);
+ snd_soc_component_write_field(component, dmic_clk_en_reg,
+ WCD938X_DMIC_CLK_EN_MASK, 1);
+ /* enable clock scaling */
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_DMIC_CTL,
+ WCD938X_DMIC_CLK_SCALING_EN_MASK, 0x3);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_AMIC_CTL,
+ dmic_sel_mask, WCD938X_AMIC1_IN_SEL_AMIC);
+ snd_soc_component_write_field(component, dmic_clk_en_reg,
+ WCD938X_DMIC_CLK_EN_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static int wcd938x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int bank;
+ int rate;
+
+ bank = sdw_slave_get_current_bank(wcd938x->sdw_priv[AIF1_CAP]->sdev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (strnstr(w->name, "ADC", sizeof("ADC"))) {
+ int i = 0, mode = 0;
+
+ if (test_bit(WCD_ADC1, &wcd938x->status_mask))
+ mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC1]];
+ if (test_bit(WCD_ADC2, &wcd938x->status_mask))
+ mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC2]];
+ if (test_bit(WCD_ADC3, &wcd938x->status_mask))
+ mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC3]];
+ if (test_bit(WCD_ADC4, &wcd938x->status_mask))
+ mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC4]];
+
+ if (mode != 0) {
+ for (i = 0; i < ADC_MODE_ULP2; i++) {
+ if (mode & (1 << i)) {
+ i++;
+ break;
+ }
+ }
+ }
+ rate = wcd938x_get_clk_rate(i);
+ wcd938x_set_swr_clk_rate(component, rate, bank);
+ /* Copy clk settings to active bank */
+ wcd938x_set_swr_clk_rate(component, rate, !bank);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (strnstr(w->name, "ADC", sizeof("ADC"))) {
+ rate = wcd938x_get_clk_rate(ADC_MODE_INVALID);
+ wcd938x_set_swr_clk_rate(component, rate, !bank);
+ wcd938x_set_swr_clk_rate(component, rate, bank);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_get_adc_mode(int val)
+{
+ int ret = 0;
+
+ switch (val) {
+ case ADC_MODE_INVALID:
+ ret = ADC_MODE_VAL_NORMAL;
+ break;
+ case ADC_MODE_HIFI:
+ ret = ADC_MODE_VAL_HIFI;
+ break;
+ case ADC_MODE_LO_HIF:
+ ret = ADC_MODE_VAL_LO_HIF;
+ break;
+ case ADC_MODE_NORMAL:
+ ret = ADC_MODE_VAL_NORMAL;
+ break;
+ case ADC_MODE_LP:
+ ret = ADC_MODE_VAL_LP;
+ break;
+ case ADC_MODE_ULP1:
+ ret = ADC_MODE_VAL_ULP1;
+ break;
+ case ADC_MODE_ULP2:
+ ret = ADC_MODE_VAL_ULP2;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int wcd938x_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_TX_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_TX_DIV2_CLK_EN_MASK, 1);
+ set_bit(w->shift, &wcd938x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_TX_CLK_EN_MASK, 0);
+ clear_bit(w->shift, &wcd938x->status_mask);
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd938x_tx_channel_config(struct snd_soc_component *component,
+ int channel, int mode)
+{
+ int reg, mask;
+
+ switch (channel) {
+ case 0:
+ reg = WCD938X_ANA_TX_CH2;
+ mask = WCD938X_HPF1_INIT_MASK;
+ break;
+ case 1:
+ reg = WCD938X_ANA_TX_CH2;
+ mask = WCD938X_HPF2_INIT_MASK;
+ break;
+ case 2:
+ reg = WCD938X_ANA_TX_CH4;
+ mask = WCD938X_HPF3_INIT_MASK;
+ break;
+ case 3:
+ reg = WCD938X_ANA_TX_CH4;
+ mask = WCD938X_HPF4_INIT_MASK;
+ break;
+ default:
+ return;
+ }
+
+ snd_soc_component_write_field(component, reg, mask, mode);
+}
+
+static int wcd938x_adc_enable_req(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_REQ_CTL,
+ WCD938X_FS_RATE_4P8_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_REQ_CTL,
+ WCD938X_NO_NOTCH_MASK, 0);
+ wcd938x_tx_channel_config(component, w->shift, 1);
+ mode = wcd938x_get_adc_mode(wcd938x->tx_mode[w->shift]);
+ if (mode < 0) {
+ dev_info(component->dev, "Invalid ADC mode\n");
+ return -EINVAL;
+ }
+ switch (w->shift) {
+ case 0:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD938X_TXD0_MODE_MASK, mode);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD0_CLK_EN_MASK, 1);
+ break;
+ case 1:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD938X_TXD1_MODE_MASK, mode);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD1_CLK_EN_MASK, 1);
+ break;
+ case 2:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD938X_TXD2_MODE_MASK, mode);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD2_CLK_EN_MASK, 1);
+ break;
+ case 3:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD938X_TXD3_MODE_MASK, mode);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD3_CLK_EN_MASK, 1);
+ break;
+ default:
+ break;
+ }
+
+ wcd938x_tx_channel_config(component, w->shift, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->shift) {
+ case 0:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD938X_TXD0_MODE_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD0_CLK_EN_MASK, 0);
+ break;
+ case 1:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD938X_TXD1_MODE_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD1_CLK_EN_MASK, 0);
+ break;
+ case 2:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD938X_TXD2_MODE_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD2_CLK_EN_MASK, 0);
+ break;
+ case 3:
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD938X_TXD3_MODE_MASK, 0);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TXD3_CLK_EN_MASK, 0);
+ break;
+ default:
+ break;
+ }
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_TX_DIV2_CLK_EN_MASK, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_micbias_control(struct snd_soc_component *component,
+ int micb_num, int req, bool is_dapm)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD938X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD938X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD938X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD938X_ANA_MICB4;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ wcd938x->pullup_ref[micb_index]++;
+ if ((wcd938x->pullup_ref[micb_index] == 1) &&
+ (wcd938x->micb_ref[micb_index] == 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK,
+ WCD938X_MICB_PULL_UP);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (wcd938x->pullup_ref[micb_index] > 0)
+ wcd938x->pullup_ref[micb_index]--;
+
+ if ((wcd938x->pullup_ref[micb_index] == 0) &&
+ (wcd938x->micb_ref[micb_index] == 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK, 0);
+ break;
+ case MICB_ENABLE:
+ wcd938x->micb_ref[micb_index]++;
+ if (wcd938x->micb_ref[micb_index] == 1) {
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD938X_TX_CLK_EN_MASK, 0xF);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD938X_ANA_TX_DIV2_CLK_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL,
+ WCD938X_TX_SC_CLK_EN_MASK, 1);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK,
+ WCD938X_MICB_ENABLE);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+ }
+ if (micb_num == MIC_BIAS_2 && is_dapm)
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+
+
+ break;
+ case MICB_DISABLE:
+ if (wcd938x->micb_ref[micb_index] > 0)
+ wcd938x->micb_ref[micb_index]--;
+
+ if ((wcd938x->micb_ref[micb_index] == 0) &&
+ (wcd938x->pullup_ref[micb_index] > 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK,
+ WCD938X_MICB_PULL_UP);
+ else if ((wcd938x->micb_ref[micb_index] == 0) &&
+ (wcd938x->pullup_ref[micb_index] == 0)) {
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_PRE_MICBIAS_2_OFF);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK, 0);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ }
+ if (is_dapm && micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd938x->wcd_mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd938x_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd938x_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd938x_micbias_control(component, micb_num,
+ MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd938x_micbias_control(component, micb_num,
+ MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd938x_tx_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.enumerated.item[0] = wcd938x->tx_mode[path];
+
+ return 0;
+}
+
+static int wcd938x_tx_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ if (wcd938x->tx_mode[path] == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wcd938x->tx_mode[path] = ucontrol->value.enumerated.item[0];
+
+ return 1;
+}
+
+static int wcd938x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = wcd938x->hph_mode;
+
+ return 0;
+}
+
+static int wcd938x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ if (wcd938x->hph_mode == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wcd938x->hph_mode = ucontrol->value.enumerated.item[0];
+
+ return 1;
+}
+
+static int wcd938x_ear_pa_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ if (wcd938x->comp1_enable) {
+ dev_err(component->dev, "Can not set EAR PA Gain, compander1 is enabled\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_write_field(component, WCD938X_ANA_EAR_COMPANDER_CTL,
+ WCD938X_EAR_GAIN_MASK,
+ ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static int wcd938x_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr)
+ ucontrol->value.integer.value[0] = wcd938x->comp2_enable;
+ else
+ ucontrol->value.integer.value[0] = wcd938x->comp1_enable;
+
+ return 0;
+}
+
+static int wcd938x_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct wcd938x_sdw_priv *wcd;
+ int value = ucontrol->value.integer.value[0];
+ int portidx;
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ wcd = wcd938x->sdw_priv[AIF1_PB];
+
+ if (hphr)
+ wcd938x->comp2_enable = value;
+ else
+ wcd938x->comp1_enable = value;
+
+ portidx = wcd->ch_info[mc->reg].port_num;
+
+ if (value)
+ wcd938x_connect_port(wcd, portidx, mc->reg, true);
+ else
+ wcd938x_connect_port(wcd, portidx, mc->reg, false);
+
+ return 1;
+}
+
+static int wcd938x_ldoh_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd938x->ldoh;
+
+ return 0;
+}
+
+static int wcd938x_ldoh_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ if (wcd938x->ldoh == ucontrol->value.integer.value[0])
+ return 0;
+
+ wcd938x->ldoh = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static const char * const tx_mode_mux_text_wcd9380[] = {
+ "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+};
+
+static const char * const tx_mode_mux_text[] = {
+ "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+ "ADC_ULP1", "ADC_ULP2",
+};
+
+static const char * const rx_hph_mode_mux_text_wcd9380[] = {
+ "CLS_H_INVALID", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB",
+ "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP",
+ "CLS_AB_LOHIFI",
+};
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
+ "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI",
+};
+
+static const char * const adc2_mux_text[] = {
+ "INP2", "INP3"
+};
+
+static const char * const adc3_mux_text[] = {
+ "INP4", "INP6"
+};
+
+static const char * const adc4_mux_text[] = {
+ "INP5", "INP7"
+};
+
+static const char * const rdac3_mux_text[] = {
+ "RX1", "RX3"
+};
+
+static const char * const hdr12_mux_text[] = {
+ "NO_HDR12", "HDR12"
+};
+
+static const char * const hdr34_mux_text[] = {
+ "NO_HDR34", "HDR34"
+};
+
+static const struct soc_enum tx0_mode_enum_wcd9380 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380);
+
+static const struct soc_enum tx1_mode_enum_wcd9380 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380);
+
+static const struct soc_enum tx2_mode_enum_wcd9380 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380);
+
+static const struct soc_enum tx3_mode_enum_wcd9380 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9380),
+ tx_mode_mux_text_wcd9380);
+
+static const struct soc_enum tx0_mode_enum_wcd9385 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum tx1_mode_enum_wcd9385 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum tx2_mode_enum_wcd9385 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum tx3_mode_enum_wcd9385 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum rx_hph_mode_mux_enum_wcd9380 =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9380),
+ rx_hph_mode_mux_text_wcd9380);
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const struct soc_enum adc2_enum =
+ SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 7,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct soc_enum adc3_enum =
+ SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 6,
+ ARRAY_SIZE(adc3_mux_text), adc3_mux_text);
+
+static const struct soc_enum adc4_enum =
+ SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 5,
+ ARRAY_SIZE(adc4_mux_text), adc4_mux_text);
+
+static const struct soc_enum hdr12_enum =
+ SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 4,
+ ARRAY_SIZE(hdr12_mux_text), hdr12_mux_text);
+
+static const struct soc_enum hdr34_enum =
+ SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 3,
+ ARRAY_SIZE(hdr34_mux_text), hdr34_mux_text);
+
+static const struct soc_enum rdac3_enum =
+ SOC_ENUM_SINGLE(WCD938X_DIGITAL_CDC_EAR_PATH_CTL, 0,
+ ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text);
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc3_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc4_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic3_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic4_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic5_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic6_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic7_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic8_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new aux_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new tx_adc2_mux =
+ SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_kcontrol_new tx_adc3_mux =
+ SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum);
+
+static const struct snd_kcontrol_new tx_adc4_mux =
+ SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum);
+
+static const struct snd_kcontrol_new tx_hdr12_mux =
+ SOC_DAPM_ENUM("HDR12 MUX Mux", hdr12_enum);
+
+static const struct snd_kcontrol_new tx_hdr34_mux =
+ SOC_DAPM_ENUM("HDR34 MUX Mux", hdr34_enum);
+
+static const struct snd_kcontrol_new rx_rdac3_mux =
+ SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum);
+
+static const struct snd_kcontrol_new wcd9380_snd_controls[] = {
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9380,
+ wcd938x_rx_hph_mode_get, wcd938x_rx_hph_mode_put),
+ SOC_ENUM_EXT("TX0 MODE", tx0_mode_enum_wcd9380,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX1 MODE", tx1_mode_enum_wcd9380,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX2 MODE", tx2_mode_enum_wcd9380,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX3 MODE", tx3_mode_enum_wcd9380,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+};
+
+static const struct snd_kcontrol_new wcd9385_snd_controls[] = {
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ wcd938x_rx_hph_mode_get, wcd938x_rx_hph_mode_put),
+ SOC_ENUM_EXT("TX0 MODE", tx0_mode_enum_wcd9385,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX1 MODE", tx1_mode_enum_wcd9385,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX2 MODE", tx2_mode_enum_wcd9385,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+ SOC_ENUM_EXT("TX3 MODE", tx3_mode_enum_wcd9385,
+ wcd938x_tx_mode_get, wcd938x_tx_mode_put),
+};
+
+static int wcd938x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(comp);
+ struct wcd938x_sdw_priv *wcd;
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int dai_id = mixer->shift;
+ int portidx, ch_idx = mixer->reg;
+
+
+ wcd = wcd938x->sdw_priv[dai_id];
+ portidx = wcd->ch_info[ch_idx].port_num;
+
+ ucontrol->value.integer.value[0] = wcd->port_enable[portidx];
+
+ return 0;
+}
+
+static int wcd938x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(comp);
+ struct wcd938x_sdw_priv *wcd;
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int ch_idx = mixer->reg;
+ int portidx;
+ int dai_id = mixer->shift;
+ bool enable;
+
+ wcd = wcd938x->sdw_priv[dai_id];
+
+ portidx = wcd->ch_info[ch_idx].port_num;
+ if (ucontrol->value.integer.value[0])
+ enable = true;
+ else
+ enable = false;
+
+ wcd->port_enable[portidx] = enable;
+
+ wcd938x_connect_port(wcd, portidx, ch_idx, enable);
+
+ return 1;
+
+}
+
+/* MBHC related */
+static void wcd938x_mbhc_clk_setup(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_1,
+ WCD938X_MBHC_CTL_RCO_EN_MASK, enable);
+}
+
+static void wcd938x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD938X_ANA_MBHC_ELECT,
+ WCD938X_ANA_MBHC_BIAS_EN, enable);
+}
+
+static void wcd938x_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, WCD938X_ANA_MBHC_BTN0 + i,
+ WCD938X_MBHC_BTN_VTH_MASK, vth);
+ dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+ __func__, i, btn_high[i], vth);
+ }
+}
+
+static bool wcd938x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
+{
+ u8 val;
+
+ if (micb_num == MIC_BIAS_2) {
+ val = snd_soc_component_read_field(component,
+ WCD938X_ANA_MICB2,
+ WCD938X_MICB_EN_MASK);
+ if (val == WCD938X_MICB_ENABLE)
+ return true;
+ }
+ return false;
+}
+
+static void wcd938x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
+ int pull_up_cur)
+{
+ /* Default pull up current to 2uA */
+ if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA)
+ pull_up_cur = HS_PULLUP_I_2P0_UA;
+
+ snd_soc_component_write_field(component,
+ WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT,
+ WCD938X_HSDET_PULLUP_C_MASK, pull_up_cur);
+}
+
+static int wcd938x_mbhc_request_micbias(struct snd_soc_component *component,
+ int micb_num, int req)
+{
+ return wcd938x_micbias_control(component, micb_num, req, false);
+}
+
+static void wcd938x_mbhc_micb_ramp_control(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP,
+ WCD938X_RAMP_SHIFT_CTRL_MASK, 0x0C);
+ snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP,
+ WCD938X_RAMP_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP,
+ WCD938X_RAMP_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP,
+ WCD938X_RAMP_SHIFT_CTRL_MASK, 0);
+ }
+}
+
+static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+ int req_volt, int micb_num)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD938X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD938X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD938X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD938X_ANA_MICB4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&wcd938x->micb_lock);
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_en = snd_soc_component_read_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK);
+ cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
+ WCD938X_MICB_VOUT_MASK);
+
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
+ if (req_vout_ctl < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (cur_vout_ctl == req_vout_ctl) {
+ ret = 0;
+ goto exit;
+ }
+
+ if (micb_en == WCD938X_MICB_ENABLE)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK,
+ WCD938X_MICB_PULL_UP);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_VOUT_MASK,
+ req_vout_ctl);
+
+ if (micb_en == WCD938X_MICB_ENABLE) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD938X_MICB_EN_MASK,
+ WCD938X_MICB_ENABLE);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+exit:
+ mutex_unlock(&wcd938x->micb_lock);
+ return ret;
+}
+
+static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
+ int micb_num, bool req_en)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (wcd938x->common.micb_mv[2] >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->common.micb_mv[2];
+
+ return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+}
+
+static void wcd938x_mbhc_get_result_params(struct snd_soc_component *component,
+ s16 *d1_a, u16 noff,
+ int32_t *zdet)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int i;
+ int val, val1;
+ s16 c1;
+ s32 x1, d1;
+ int32_t denom;
+ static const int minCode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+ };
+
+ regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MBHC_ZDET, 0x20, 0x20);
+ for (i = 0; i < WCD938X_ZDET_NUM_MEASUREMENTS; i++) {
+ regmap_read(wcd938x->regmap, WCD938X_ANA_MBHC_RESULT_2, &val);
+ if (val & 0x80)
+ break;
+ }
+ val = val << 0x8;
+ regmap_read(wcd938x->regmap, WCD938X_ANA_MBHC_RESULT_1, &val1);
+ val |= val1;
+ regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MBHC_ZDET, 0x20, 0x00);
+ x1 = WCD938X_MBHC_GET_X1(val);
+ c1 = WCD938X_MBHC_GET_C1(val);
+ /* If ramp is not complete, give additional 5ms */
+ if ((c1 < 2) && x1)
+ usleep_range(5000, 5050);
+
+ if (!c1 || !x1) {
+ dev_err(component->dev, "Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ c1, x1);
+ goto ramp_down;
+ }
+ d1 = d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - noff));
+ if (denom > 0)
+ *zdet = (WCD938X_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < minCode_param[noff])
+ *zdet = WCD938X_ZDET_FLOATING_IMPEDANCE;
+
+ dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d (milliohm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+ while (x1) {
+ regmap_read(wcd938x->regmap,
+ WCD938X_ANA_MBHC_RESULT_1, &val);
+ regmap_read(wcd938x->regmap,
+ WCD938X_ANA_MBHC_RESULT_2, &val1);
+ val = val << 0x08;
+ val |= val1;
+ x1 = WCD938X_MBHC_GET_X1(val);
+ i++;
+ if (i == WCD938X_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+static void wcd938x_mbhc_zdet_ramp(struct snd_soc_component *component,
+ struct wcd938x_mbhc_zdet_param *zdet_param,
+ int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ int32_t zdet = 0;
+
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD938X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl);
+ snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN5,
+ WCD938X_VTH_MASK, zdet_param->btn5);
+ snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN6,
+ WCD938X_VTH_MASK, zdet_param->btn6);
+ snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN7,
+ WCD938X_VTH_MASK, zdet_param->btn7);
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD938X_ZDET_RANGE_CTL_MASK, zdet_param->noff);
+ snd_soc_component_update_bits(component, WCD938X_MBHC_NEW_ZDET_RAMP_CTL,
+ 0x0F, zdet_param->nshift);
+
+ if (!zl)
+ goto z_right;
+ /* Start impedance measurement for HPH_L */
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_ZDET, 0x80, 0x80);
+ dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n",
+ __func__, zdet_param->noff);
+ wcd938x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_ZDET, 0x80, 0x00);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+ /* Start impedance measurement for HPH_R */
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_ZDET, 0x40, 0x40);
+ dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n",
+ __func__, zdet_param->noff);
+ wcd938x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_ZDET, 0x40, 0x00);
+
+ *zr = zdet;
+}
+
+static void wcd938x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ int32_t *z_val, int flag_l_r)
+{
+ s16 q1;
+ int q1_cal;
+
+ if (*z_val < (WCD938X_ZDET_VAL_400/1000))
+ q1 = snd_soc_component_read(component,
+ WCD938X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r));
+ else
+ q1 = snd_soc_component_read(component,
+ WCD938X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r));
+ if (q1 & 0x80)
+ q1_cal = (10000 - ((q1 & 0x7F) * 25));
+ else
+ q1_cal = (10000 + (q1 * 25));
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void wcd938x_wcd_mbhc_calc_impedance(struct snd_soc_component *component,
+ uint32_t *zl, uint32_t *zr)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ s16 reg0, reg1, reg2, reg3, reg4;
+ int32_t z1L, z1R, z1Ls;
+ int zMono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ struct wcd938x_mbhc_zdet_param zdet_param[] = {
+ {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+ {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+ {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+ {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+ };
+ struct wcd938x_mbhc_zdet_param *zdet_param_ptr = NULL;
+ s16 d1_a[][4] = {
+ {0, 30, 90, 30},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ };
+ s16 *d1 = NULL;
+
+ reg0 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN5);
+ reg1 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN6);
+ reg2 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN7);
+ reg3 = snd_soc_component_read(component, WCD938X_MBHC_CTL_CLK);
+ reg4 = snd_soc_component_read(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL);
+
+ if (snd_soc_component_read(component, WCD938X_ANA_MBHC_ELECT) & 0x80) {
+ is_fsm_disable = true;
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_ELECT, 0x80, 0x00);
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (wcd938x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_MECH, 0x80, 0x00);
+
+ /* Turn off 100k pull down on HPHL */
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_MECH, 0x01, 0x00);
+
+ /* Disable surge protection before impedance detection.
+ * This is done to give correct value for high impedance.
+ */
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00);
+ /* 1ms delay needed after disable surge protection */
+ usleep_range(1000, 1010);
+
+ /* First get impedance on Left */
+ d1 = d1_a[1];
+ zdet_param_ptr = &zdet_param[1];
+ wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+ if (!WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+ goto left_ch_impedance;
+
+ /* Second ramp for left ch */
+ if (z1L < WCD938X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1L > WCD938X_ZDET_VAL_400) &&
+ (z1L <= WCD938X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1L > WCD938X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+ if ((z1L == WCD938X_ZDET_FLOATING_IMPEDANCE) ||
+ (z1L > WCD938X_ZDET_VAL_100K)) {
+ *zl = WCD938X_ZDET_FLOATING_IMPEDANCE;
+ zdet_param_ptr = &zdet_param[1];
+ d1 = d1_a[1];
+ } else {
+ *zl = z1L/1000;
+ wcd938x_wcd_mbhc_qfuse_cal(component, zl, 0);
+ }
+ dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+ __func__, *zl);
+
+ /* Start of right impedance ramp and calculation */
+ wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+ if (WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+ if (((z1R > WCD938X_ZDET_VAL_1200) &&
+ (zdet_param_ptr->noff == 0x6)) ||
+ ((*zl) != WCD938X_ZDET_FLOATING_IMPEDANCE))
+ goto right_ch_impedance;
+ /* Second ramp for right ch */
+ if (z1R < WCD938X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1R > WCD938X_ZDET_VAL_400) &&
+ (z1R <= WCD938X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1R > WCD938X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+ }
+right_ch_impedance:
+ if ((z1R == WCD938X_ZDET_FLOATING_IMPEDANCE) ||
+ (z1R > WCD938X_ZDET_VAL_100K)) {
+ *zr = WCD938X_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1R/1000;
+ wcd938x_wcd_mbhc_qfuse_cal(component, zr, 1);
+ }
+ dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+ __func__, *zr);
+
+ /* Mono/stereo detection */
+ if ((*zl == WCD938X_ZDET_FLOATING_IMPEDANCE) &&
+ (*zr == WCD938X_ZDET_FLOATING_IMPEDANCE)) {
+ dev_dbg(component->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+ if ((*zl == WCD938X_ZDET_FLOATING_IMPEDANCE) ||
+ (*zr == WCD938X_ZDET_FLOATING_IMPEDANCE) ||
+ ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+ ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+ dev_dbg(component->dev,
+ "%s: Mono plug type with one ch floating or shorted to GND\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+ goto zdet_complete;
+ }
+ snd_soc_component_write_field(component, WCD938X_HPH_R_ATEST,
+ WCD938X_HPHPA_GND_OVR_MASK, 1);
+ snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2,
+ WCD938X_HPHPA_GND_R_MASK, 1);
+ if (*zl < (WCD938X_ZDET_VAL_32/1000))
+ wcd938x_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, NULL, d1);
+ else
+ wcd938x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1);
+ snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2,
+ WCD938X_HPHPA_GND_R_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_HPH_R_ATEST,
+ WCD938X_HPHPA_GND_OVR_MASK, 0);
+ z1Ls /= 1000;
+ wcd938x_wcd_mbhc_qfuse_cal(component, &z1Ls, 0);
+ /* Parallel of left Z and 9 ohm pull down resistor */
+ zMono = ((*zl) * 9) / ((*zl) + 9);
+ z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls);
+ z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl));
+ if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) {
+ dev_dbg(component->dev, "%s: stereo plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_STEREO);
+ } else {
+ dev_dbg(component->dev, "%s: MONO plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+ }
+
+ /* Enable surge protection again after impedance detection */
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0);
+zdet_complete:
+ snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN5, reg0);
+ snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN6, reg1);
+ snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN7, reg2);
+ /* Turn on 100k pull down on HPHL */
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_MECH, 0x01, 0x01);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (wcd938x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_MECH, 0x80, 0x80);
+
+ snd_soc_component_write(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+ snd_soc_component_write(component, WCD938X_MBHC_CTL_CLK, reg3);
+ if (is_fsm_disable)
+ regmap_update_bits(wcd938x->regmap,
+ WCD938X_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+
+static void wcd938x_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH,
+ WCD938X_MBHC_HSG_PULLUP_COMP_EN, 1);
+ snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH,
+ WCD938X_MBHC_GND_DET_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH,
+ WCD938X_MBHC_GND_DET_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH,
+ WCD938X_MBHC_HSG_PULLUP_COMP_EN, 0);
+ }
+}
+
+static void wcd938x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2,
+ WCD938X_HPHPA_GND_R_MASK, enable);
+ snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2,
+ WCD938X_HPHPA_GND_L_MASK, enable);
+}
+
+static void wcd938x_mbhc_moisture_config(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ if (wcd938x->mbhc_cfg.moist_rref == R_OFF) {
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, R_OFF);
+ return;
+ }
+
+ /* Do not enable moisture detection if jack type is NC */
+ if (!wcd938x->mbhc_cfg.hphl_swh) {
+ dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, R_OFF);
+ return;
+ }
+
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, wcd938x->mbhc_cfg.moist_rref);
+}
+
+static void wcd938x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ if (enable)
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, wcd938x->mbhc_cfg.moist_rref);
+ else
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, R_OFF);
+}
+
+static bool wcd938x_mbhc_get_moisture_status(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ bool ret = false;
+
+ if (wcd938x->mbhc_cfg.moist_rref == R_OFF) {
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, R_OFF);
+ goto done;
+ }
+
+ /* Do not enable moisture detection if jack type is NC */
+ if (!wcd938x->mbhc_cfg.hphl_swh) {
+ dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2,
+ WCD938X_M_RTH_CTL_MASK, R_OFF);
+ goto done;
+ }
+
+ /*
+ * If moisture_en is already enabled, then skip to plug type
+ * detection.
+ */
+ if (snd_soc_component_read_field(component, WCD938X_MBHC_NEW_CTL_2, WCD938X_M_RTH_CTL_MASK))
+ goto done;
+
+ wcd938x_mbhc_moisture_detect_en(component, true);
+ /* Read moisture comparator status */
+ ret = ((snd_soc_component_read(component, WCD938X_MBHC_NEW_FSM_STATUS)
+ & 0x20) ? 0 : 1);
+
+done:
+ return ret;
+
+}
+
+static void wcd938x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component,
+ WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL,
+ WCD938X_MOISTURE_EN_POLLING_MASK, enable);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .clk_setup = wcd938x_mbhc_clk_setup,
+ .mbhc_bias = wcd938x_mbhc_mbhc_bias_control,
+ .set_btn_thr = wcd938x_mbhc_program_btn_thr,
+ .micbias_enable_status = wcd938x_mbhc_micb_en_status,
+ .hph_pull_up_control_v2 = wcd938x_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = wcd938x_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = wcd938x_mbhc_micb_ramp_control,
+ .mbhc_micb_ctrl_thr_mic = wcd938x_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = wcd938x_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = wcd938x_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = wcd938x_mbhc_hph_pull_down_ctrl,
+ .mbhc_moisture_config = wcd938x_mbhc_moisture_config,
+ .mbhc_get_moisture_status = wcd938x_mbhc_get_moisture_status,
+ .mbhc_moisture_polling_ctrl = wcd938x_mbhc_moisture_polling_ctrl,
+ .mbhc_moisture_detect_en = wcd938x_mbhc_moisture_detect_en,
+};
+
+static int wcd938x_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd938x->wcd_mbhc);
+
+ return 0;
+}
+
+static int wcd938x_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_mixer_control *mc;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+ wcd_mbhc_get_impedance(wcd938x->wcd_mbhc, &zl, &zr);
+ dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0,
+ wcd938x_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0,
+ wcd938x_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0,
+ wcd938x_hph_impedance_get, NULL),
+};
+
+static int wcd938x_mbhc_init(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &wcd938x->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_MBHC_SW_DET);
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_MBHC_BUTTON_PRESS_DET);
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET);
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_MBHC_ELECT_INS_REM_DET);
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_HPHL_OCP_INT);
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_HPHR_OCP_INT);
+
+ wcd938x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+ if (IS_ERR(wcd938x->wcd_mbhc))
+ return PTR_ERR(wcd938x->wcd_mbhc);
+
+ snd_soc_add_component_controls(component, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_component_controls(component, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ return 0;
+}
+
+static void wcd938x_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(wcd938x->wcd_mbhc);
+}
+
+/* END MBHC */
+
+static const struct snd_kcontrol_new wcd938x_snd_controls[] = {
+ SOC_SINGLE_EXT("HPHL_COMP Switch", WCD938X_COMP_L, 0, 1, 0,
+ wcd938x_get_compander, wcd938x_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", WCD938X_COMP_R, 1, 1, 0,
+ wcd938x_get_compander, wcd938x_set_compander),
+ SOC_SINGLE_EXT("HPHL Switch", WCD938X_HPH_L, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", WCD938X_HPH_R, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("CLSH Switch", WCD938X_CLSH, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("LO Switch", WCD938X_LO, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DSD_L Switch", WCD938X_DSD_L, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DSD_R Switch", WCD938X_DSD_R, 0, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_TLV("HPHL Volume", WCD938X_HPH_L_EN, 0, 0x18, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD938X_HPH_R_EN, 0, 0x18, 1, line_gain),
+ WCD938X_EAR_PA_GAIN_TLV("EAR_PA Volume", WCD938X_ANA_EAR_COMPANDER_CTL,
+ 2, 0x10, 0, ear_pa_gain),
+ SOC_SINGLE_EXT("ADC1 Switch", WCD938X_ADC1, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", WCD938X_ADC2, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("ADC3 Switch", WCD938X_ADC3, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("ADC4 Switch", WCD938X_ADC4, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC0 Switch", WCD938X_DMIC0, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC1 Switch", WCD938X_DMIC1, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("MBHC Switch", WCD938X_MBHC, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC2 Switch", WCD938X_DMIC2, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC3 Switch", WCD938X_DMIC3, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC4 Switch", WCD938X_DMIC4, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC5 Switch", WCD938X_DMIC5, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC6 Switch", WCD938X_DMIC6, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC7 Switch", WCD938X_DMIC7, 1, 1, 0,
+ wcd938x_get_swr_port, wcd938x_set_swr_port),
+ SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0,
+ wcd938x_ldoh_get, wcd938x_ldoh_put),
+
+ SOC_SINGLE_TLV("ADC1 Volume", WCD938X_ANA_TX_CH1, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD938X_ANA_TX_CH2, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD938X_ANA_TX_CH3, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD938X_ANA_TX_CH4, 0, 20, 0, analog_gain),
+};
+
+static const struct snd_soc_dapm_widget wcd938x_dapm_widgets[] = {
+
+ /*input widgets*/
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC4"),
+ SND_SOC_DAPM_INPUT("AMIC5"),
+ SND_SOC_DAPM_INPUT("AMIC6"),
+ SND_SOC_DAPM_INPUT("AMIC7"),
+ SND_SOC_DAPM_MIC("Analog Mic1", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic2", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic3", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+
+ /*tx widgets*/
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0,
+ wcd938x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0,
+ wcd938x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0,
+ wcd938x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0,
+ wcd938x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wcd938x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0,
+ NULL, 0, wcd938x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0,
+ NULL, 0, wcd938x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0,
+ wcd938x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+ SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux),
+ SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux),
+ SND_SOC_DAPM_MUX("HDR12 MUX", SND_SOC_NOPM, 0, 0, &tx_hdr12_mux),
+ SND_SOC_DAPM_MUX("HDR34 MUX", SND_SOC_NOPM, 0, 0, &tx_hdr34_mux),
+
+ /*tx mixers*/
+ SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch,
+ ARRAY_SIZE(adc1_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, adc2_switch,
+ ARRAY_SIZE(adc2_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, adc3_switch,
+ ARRAY_SIZE(adc3_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0, adc4_switch,
+ ARRAY_SIZE(adc4_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch,
+ ARRAY_SIZE(dmic1_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0, dmic2_switch,
+ ARRAY_SIZE(dmic2_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0, dmic3_switch,
+ ARRAY_SIZE(dmic3_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0, dmic4_switch,
+ ARRAY_SIZE(dmic4_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0, dmic5_switch,
+ ARRAY_SIZE(dmic5_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0, dmic6_switch,
+ ARRAY_SIZE(dmic6_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0, dmic7_switch,
+ ARRAY_SIZE(dmic7_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0, dmic8_switch,
+ ARRAY_SIZE(dmic8_switch), wcd938x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* micbias widgets*/
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd938x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd938x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd938x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+ wcd938x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* micbias pull up widgets*/
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd938x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd938x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd938x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+ wcd938x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /*output widgets tx*/
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"),
+
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+ SND_SOC_DAPM_INPUT("IN3_AUX"),
+
+ /*rx widgets*/
+ SND_SOC_DAPM_PGA_E("EAR PGA", WCD938X_ANA_EAR, 7, 0, NULL, 0,
+ wcd938x_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("AUX PGA", WCD938X_AUX_AUXPA, 7, 0, NULL, 0,
+ wcd938x_codec_enable_aux_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", WCD938X_ANA_HPH, 7, 0, NULL, 0,
+ wcd938x_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", WCD938X_ANA_HPH, 6, 0, NULL, 0,
+ wcd938x_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC4", NULL, SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_aux_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux),
+
+ SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0,
+ wcd938x_codec_enable_rxclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+ /* rx mixer widgets*/
+ SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0,
+ ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)),
+ SND_SOC_DAPM_MIXER("AUX_RDAC", SND_SOC_NOPM, 0, 0,
+ aux_rdac_switch, ARRAY_SIZE(aux_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0,
+ hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0,
+ hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)),
+
+ /*output widgets rx*/
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("AUX"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+
+};
+
+static const struct snd_soc_dapm_route wcd938x_audio_map[] = {
+ {"ADC1_OUTPUT", NULL, "ADC1_MIXER"},
+ {"ADC1_MIXER", "Switch", "ADC1 REQ"},
+ {"ADC1 REQ", NULL, "ADC1"},
+ {"ADC1", NULL, "AMIC1"},
+
+ {"ADC2_OUTPUT", NULL, "ADC2_MIXER"},
+ {"ADC2_MIXER", "Switch", "ADC2 REQ"},
+ {"ADC2 REQ", NULL, "ADC2"},
+ {"ADC2", NULL, "HDR12 MUX"},
+ {"HDR12 MUX", "NO_HDR12", "ADC2 MUX"},
+ {"HDR12 MUX", "HDR12", "AMIC1"},
+ {"ADC2 MUX", "INP3", "AMIC3"},
+ {"ADC2 MUX", "INP2", "AMIC2"},
+
+ {"ADC3_OUTPUT", NULL, "ADC3_MIXER"},
+ {"ADC3_MIXER", "Switch", "ADC3 REQ"},
+ {"ADC3 REQ", NULL, "ADC3"},
+ {"ADC3", NULL, "HDR34 MUX"},
+ {"HDR34 MUX", "NO_HDR34", "ADC3 MUX"},
+ {"HDR34 MUX", "HDR34", "AMIC5"},
+ {"ADC3 MUX", "INP4", "AMIC4"},
+ {"ADC3 MUX", "INP6", "AMIC6"},
+
+ {"ADC4_OUTPUT", NULL, "ADC4_MIXER"},
+ {"ADC4_MIXER", "Switch", "ADC4 REQ"},
+ {"ADC4 REQ", NULL, "ADC4"},
+ {"ADC4", NULL, "ADC4 MUX"},
+ {"ADC4 MUX", "INP5", "AMIC5"},
+ {"ADC4 MUX", "INP7", "AMIC7"},
+
+ {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"},
+ {"DMIC1_MIXER", "Switch", "DMIC1"},
+
+ {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"},
+ {"DMIC2_MIXER", "Switch", "DMIC2"},
+
+ {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"},
+ {"DMIC3_MIXER", "Switch", "DMIC3"},
+
+ {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"},
+ {"DMIC4_MIXER", "Switch", "DMIC4"},
+
+ {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"},
+ {"DMIC5_MIXER", "Switch", "DMIC5"},
+
+ {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"},
+ {"DMIC6_MIXER", "Switch", "DMIC6"},
+
+ {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"},
+ {"DMIC7_MIXER", "Switch", "DMIC7"},
+
+ {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"},
+ {"DMIC8_MIXER", "Switch", "DMIC8"},
+
+ {"IN1_HPHL", NULL, "VDD_BUCK"},
+ {"IN1_HPHL", NULL, "CLS_H_PORT"},
+
+ {"RX1", NULL, "IN1_HPHL"},
+ {"RX1", NULL, "RXCLK"},
+ {"RDAC1", NULL, "RX1"},
+ {"HPHL_RDAC", "Switch", "RDAC1"},
+ {"HPHL PGA", NULL, "HPHL_RDAC"},
+ {"HPHL", NULL, "HPHL PGA"},
+
+ {"IN2_HPHR", NULL, "VDD_BUCK"},
+ {"IN2_HPHR", NULL, "CLS_H_PORT"},
+ {"RX2", NULL, "IN2_HPHR"},
+ {"RDAC2", NULL, "RX2"},
+ {"RX2", NULL, "RXCLK"},
+ {"HPHR_RDAC", "Switch", "RDAC2"},
+ {"HPHR PGA", NULL, "HPHR_RDAC"},
+ {"HPHR", NULL, "HPHR PGA"},
+
+ {"IN3_AUX", NULL, "VDD_BUCK"},
+ {"IN3_AUX", NULL, "CLS_H_PORT"},
+ {"RX3", NULL, "IN3_AUX"},
+ {"RDAC4", NULL, "RX3"},
+ {"RX3", NULL, "RXCLK"},
+ {"AUX_RDAC", "Switch", "RDAC4"},
+ {"AUX PGA", NULL, "AUX_RDAC"},
+ {"AUX", NULL, "AUX PGA"},
+
+ {"RDAC3_MUX", "RX3", "RX3"},
+ {"RDAC3_MUX", "RX1", "RX1"},
+ {"RDAC3", NULL, "RDAC3_MUX"},
+ {"EAR_RDAC", "Switch", "RDAC3"},
+ {"EAR PGA", NULL, "EAR_RDAC"},
+ {"EAR", NULL, "EAR PGA"},
+};
+
+static void wcd938x_set_micbias_data(struct device *dev, struct wcd938x_priv *wcd938x)
+{
+ regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB1,
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[0]);
+ regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB2,
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[1]);
+ regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB3,
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[2]);
+ regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB4,
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[3]);
+}
+
+static irqreturn_t wcd938x_wd_handle_irq(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+static const struct irq_chip wcd_irq_chip = {
+ .name = "WCD938x",
+};
+
+static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops wcd_domain_ops = {
+ .map = wcd_irq_chip_map,
+};
+
+static int wcd938x_irq_init(struct wcd938x_priv *wcd, struct device *dev)
+{
+
+ wcd->virq = irq_domain_create_linear(NULL, 1, &wcd_domain_ops, NULL);
+ if (!(wcd->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ return devm_regmap_add_irq_chip(dev, wcd->regmap,
+ irq_create_mapping(wcd->virq, 0),
+ IRQF_ONESHOT, 0, &wcd938x_regmap_irq_chip,
+ &wcd->irq_chip);
+}
+
+static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ unsigned int variant;
+ int ret, i;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(2000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, wcd938x->regmap);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ variant = snd_soc_component_read_field(component,
+ WCD938X_DIGITAL_EFUSE_REG_0,
+ WCD938X_ID_MASK);
+
+ wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X);
+ if (IS_ERR(wcd938x->clsh_info)) {
+ pm_runtime_put(dev);
+ return PTR_ERR(wcd938x->clsh_info);
+ }
+
+ wcd938x_io_init(wcd938x);
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < wcd938x_regmap_irq_chip.num_regs; i++) {
+ regmap_write(wcd938x->regmap,
+ (WCD938X_DIGITAL_INTR_LEVEL_0 + i), 0);
+ }
+
+ pm_runtime_put(dev);
+
+ wcd938x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_HPHR_PDM_WD_INT);
+ wcd938x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_HPHL_PDM_WD_INT);
+ wcd938x->aux_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip,
+ WCD938X_IRQ_AUX_PDM_WD_INT);
+
+ /* Request for watchdog interrupt */
+ ret = request_threaded_irq(wcd938x->hphr_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WD INT", wcd938x);
+ if (ret) {
+ dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret);
+ goto err_free_clsh_ctrl;
+ }
+
+ ret = request_threaded_irq(wcd938x->hphl_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WD INT", wcd938x);
+ if (ret) {
+ dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret);
+ goto err_free_hphr_pdm_wd_int;
+ }
+
+ ret = request_threaded_irq(wcd938x->aux_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "AUX PDM WD INT", wcd938x);
+ if (ret) {
+ dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret);
+ goto err_free_hphl_pdm_wd_int;
+ }
+
+ /* Disable watchdog interrupt for HPH and AUX */
+ disable_irq_nosync(wcd938x->hphr_pdm_wd_int);
+ disable_irq_nosync(wcd938x->hphl_pdm_wd_int);
+ disable_irq_nosync(wcd938x->aux_pdm_wd_int);
+
+ switch (variant) {
+ case CHIPID_WCD9380:
+ ret = snd_soc_add_component_controls(component, wcd9380_snd_controls,
+ ARRAY_SIZE(wcd9380_snd_controls));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add snd ctrls for variant: %d\n",
+ __func__, variant);
+ goto err_free_aux_pdm_wd_int;
+ }
+ break;
+ case CHIPID_WCD9385:
+ ret = snd_soc_add_component_controls(component, wcd9385_snd_controls,
+ ARRAY_SIZE(wcd9385_snd_controls));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add snd ctrls for variant: %d\n",
+ __func__, variant);
+ goto err_free_aux_pdm_wd_int;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = wcd938x_mbhc_init(component);
+ if (ret) {
+ dev_err(component->dev, "mbhc initialization failed\n");
+ goto err_free_aux_pdm_wd_int;
+ }
+
+ return 0;
+
+err_free_aux_pdm_wd_int:
+ free_irq(wcd938x->aux_pdm_wd_int, wcd938x);
+err_free_hphl_pdm_wd_int:
+ free_irq(wcd938x->hphl_pdm_wd_int, wcd938x);
+err_free_hphr_pdm_wd_int:
+ free_irq(wcd938x->hphr_pdm_wd_int, wcd938x);
+err_free_clsh_ctrl:
+ wcd_clsh_ctrl_free(wcd938x->clsh_info);
+
+ return ret;
+}
+
+static void wcd938x_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ wcd938x_mbhc_deinit(component);
+
+ free_irq(wcd938x->aux_pdm_wd_int, wcd938x);
+ free_irq(wcd938x->hphl_pdm_wd_int, wcd938x);
+ free_irq(wcd938x->hphr_pdm_wd_int, wcd938x);
+
+ wcd_clsh_ctrl_free(wcd938x->clsh_info);
+}
+
+static int wcd938x_codec_set_jack(struct snd_soc_component *comp,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct wcd938x_priv *wcd = dev_get_drvdata(comp->dev);
+
+ if (jack)
+ return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack);
+ else
+ wcd_mbhc_stop(wcd->wcd_mbhc);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_wcd938x = {
+ .name = "wcd938x_codec",
+ .probe = wcd938x_soc_codec_probe,
+ .remove = wcd938x_soc_codec_remove,
+ .controls = wcd938x_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd938x_snd_controls),
+ .dapm_widgets = wcd938x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wcd938x_dapm_widgets),
+ .dapm_routes = wcd938x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map),
+ .set_jack = wcd938x_codec_set_jack,
+ .endianness = 1,
+};
+
+static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int ret;
+
+ if (wcd938x->us_euro_mux) {
+ if (wcd938x->mux_setup_done)
+ mux_control_deselect(wcd938x->us_euro_mux);
+
+ ret = mux_control_try_select(wcd938x->us_euro_mux, !wcd938x->mux_state);
+ if (ret) {
+ dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret);
+ wcd938x->mux_setup_done = false;
+ return false;
+ }
+ wcd938x->mux_setup_done = true;
+ } else {
+ gpiod_set_value(wcd938x->us_euro_gpio, !wcd938x->mux_state);
+ }
+
+ wcd938x->mux_state = !wcd938x->mux_state;
+
+ return true;
+}
+
+
+static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device *dev)
+{
+ struct wcd_mbhc_config *cfg = &wcd938x->mbhc_cfg;
+ int ret;
+
+ wcd938x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd938x->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd938x->reset_gpio),
+ "Failed to get reset gpio\n");
+
+ if (of_property_present(dev->of_node, "mux-controls")) {
+ wcd938x->us_euro_mux = devm_mux_control_get(dev, NULL);
+ if (IS_ERR(wcd938x->us_euro_mux)) {
+ ret = PTR_ERR(wcd938x->us_euro_mux);
+ return dev_err_probe(dev, ret, "failed to get mux control\n");
+ }
+
+ ret = mux_control_try_select(wcd938x->us_euro_mux, wcd938x->mux_state);
+ if (ret) {
+ dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret);
+ return ret;
+ }
+ wcd938x->mux_setup_done = true;
+ } else {
+ wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd938x->us_euro_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
+ "us-euro swap Control GPIO not found\n");
+ }
+
+ cfg->swap_gnd_mic = wcd938x_swap_gnd_mic;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd938x_supplies),
+ wcd938x_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ ret = wcd_dt_parse_micbias_info(&wcd938x->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = WCD938X_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = wcd938x->common.micb_mv[2];
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, cfg);
+
+ return 0;
+}
+
+static int wcd938x_reset(struct wcd938x_priv *wcd938x)
+{
+ gpiod_set_value(wcd938x->reset_gpio, 1);
+ /* 20us sleep required after pulling the reset gpio to LOW */
+ usleep_range(20, 30);
+ gpiod_set_value(wcd938x->reset_gpio, 0);
+ /* 20us sleep required after pulling the reset gpio to HIGH */
+ usleep_range(20, 30);
+
+ return 0;
+}
+
+static int wcd938x_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wcd938x_priv *wcd938x = dev_get_drvdata(dai->dev);
+ struct wcd938x_sdw_priv *wcd = wcd938x->sdw_priv[dai->id];
+
+ return wcd938x_sdw_hw_params(wcd, substream, params, dai);
+}
+
+static int wcd938x_codec_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wcd938x_priv *wcd938x = dev_get_drvdata(dai->dev);
+ struct wcd938x_sdw_priv *wcd = wcd938x->sdw_priv[dai->id];
+
+ return wcd938x_sdw_free(wcd, substream, dai);
+}
+
+static int wcd938x_codec_set_sdw_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wcd938x_priv *wcd938x = dev_get_drvdata(dai->dev);
+ struct wcd938x_sdw_priv *wcd = wcd938x->sdw_priv[dai->id];
+
+ return wcd938x_sdw_set_sdw_stream(wcd, dai, stream, direction);
+
+}
+
+static const struct snd_soc_dai_ops wcd938x_sdw_dai_ops = {
+ .hw_params = wcd938x_codec_hw_params,
+ .hw_free = wcd938x_codec_free,
+ .set_stream = wcd938x_codec_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver wcd938x_dais[] = {
+ [AIF1_PB] = {
+ .name = "wcd938x-sdw-rx",
+ .playback = {
+ .stream_name = "WCD AIF1 Playback",
+ .rates = WCD938X_RATES_MASK | WCD938X_FRAC_RATES_MASK,
+ .formats = WCD938X_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd938x_sdw_dai_ops,
+ },
+ [AIF1_CAP] = {
+ .name = "wcd938x-sdw-tx",
+ .capture = {
+ .stream_name = "WCD AIF1 Capture",
+ .rates = WCD938X_RATES_MASK,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd938x_sdw_dai_ops,
+ },
+};
+
+static int wcd938x_bind(struct device *dev)
+{
+ struct wcd938x_priv *wcd938x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = component_bind_all(dev, wcd938x);
+ if (ret) {
+ dev_err(dev, "%s: Slave bind failed, ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ wcd938x->rxdev = of_sdw_find_device_by_node(wcd938x->rxnode);
+ if (!wcd938x->rxdev) {
+ dev_err(dev, "could not find slave with matching of node\n");
+ ret = -EINVAL;
+ goto err_unbind;
+ }
+ wcd938x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd938x->rxdev);
+ wcd938x->sdw_priv[AIF1_PB]->wcd938x = wcd938x;
+
+ wcd938x->txdev = of_sdw_find_device_by_node(wcd938x->txnode);
+ if (!wcd938x->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto err_put_rxdev;
+ }
+ wcd938x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd938x->txdev);
+ wcd938x->sdw_priv[AIF1_CAP]->wcd938x = wcd938x;
+ wcd938x->tx_sdw_dev = dev_to_sdw_dev(wcd938x->txdev);
+
+ /* As TX is main CSR reg interface, which should not be suspended first.
+ * expicilty add the dependency link */
+ if (!device_link_add(wcd938x->rxdev, wcd938x->txdev, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "could not devlink tx and rx\n");
+ ret = -EINVAL;
+ goto err_put_txdev;
+ }
+
+ if (!device_link_add(dev, wcd938x->txdev, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "could not devlink wcd and tx\n");
+ ret = -EINVAL;
+ goto err_remove_rxtx_link;
+ }
+
+ if (!device_link_add(dev, wcd938x->rxdev, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "could not devlink wcd and rx\n");
+ ret = -EINVAL;
+ goto err_remove_tx_link;
+ }
+
+ wcd938x->regmap = wcd938x->sdw_priv[AIF1_CAP]->regmap;
+ if (!wcd938x->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -EINVAL;
+ goto err_remove_rx_link;
+ }
+
+ ret = wcd938x_irq_init(wcd938x, dev);
+ if (ret) {
+ dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret);
+ goto err_remove_rx_link;
+ }
+
+ wcd938x->sdw_priv[AIF1_PB]->slave_irq = wcd938x->virq;
+ wcd938x->sdw_priv[AIF1_CAP]->slave_irq = wcd938x->virq;
+
+ wcd938x_set_micbias_data(dev, wcd938x);
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_wcd938x,
+ wcd938x_dais, ARRAY_SIZE(wcd938x_dais));
+ if (ret) {
+ dev_err(dev, "%s: Codec registration failed\n",
+ __func__);
+ goto err_remove_rx_link;
+ }
+
+ return 0;
+
+err_remove_rx_link:
+ device_link_remove(dev, wcd938x->rxdev);
+err_remove_tx_link:
+ device_link_remove(dev, wcd938x->txdev);
+err_remove_rxtx_link:
+ device_link_remove(wcd938x->rxdev, wcd938x->txdev);
+err_put_txdev:
+ put_device(wcd938x->txdev);
+err_put_rxdev:
+ put_device(wcd938x->rxdev);
+err_unbind:
+ component_unbind_all(dev, wcd938x);
+
+ return ret;
+}
+
+static void wcd938x_unbind(struct device *dev)
+{
+ struct wcd938x_priv *wcd938x = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ device_link_remove(dev, wcd938x->txdev);
+ device_link_remove(dev, wcd938x->rxdev);
+ device_link_remove(wcd938x->rxdev, wcd938x->txdev);
+ put_device(wcd938x->txdev);
+ put_device(wcd938x->rxdev);
+ component_unbind_all(dev, wcd938x);
+}
+
+static const struct component_master_ops wcd938x_comp_ops = {
+ .bind = wcd938x_bind,
+ .unbind = wcd938x_unbind,
+};
+
+static int wcd938x_add_slave_components(struct wcd938x_priv *wcd938x,
+ struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np;
+
+ np = dev->of_node;
+
+ wcd938x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!wcd938x->rxnode) {
+ dev_err(dev, "%s: Rx-device node not defined\n", __func__);
+ return -ENODEV;
+ }
+
+ of_node_get(wcd938x->rxnode);
+ component_match_add_release(dev, matchptr, component_release_of,
+ component_compare_of, wcd938x->rxnode);
+
+ wcd938x->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!wcd938x->txnode) {
+ dev_err(dev, "%s: Tx-device node not defined\n", __func__);
+ return -ENODEV;
+ }
+ of_node_get(wcd938x->txnode);
+ component_match_add_release(dev, matchptr, component_release_of,
+ component_compare_of, wcd938x->txnode);
+ return 0;
+}
+
+static int wcd938x_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct wcd938x_priv *wcd938x = NULL;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ wcd938x = devm_kzalloc(dev, sizeof(struct wcd938x_priv),
+ GFP_KERNEL);
+ if (!wcd938x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, wcd938x);
+ mutex_init(&wcd938x->micb_lock);
+ wcd938x->common.dev = dev;
+ wcd938x->common.max_bias = 4;
+
+ ret = wcd938x_populate_dt_data(wcd938x, dev);
+ if (ret)
+ return ret;
+
+ ret = wcd938x_add_slave_components(wcd938x, dev, &match);
+ if (ret)
+ return ret;
+
+ wcd938x_reset(wcd938x);
+
+ ret = component_master_add_with_match(dev, &wcd938x_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void wcd938x_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd938x_priv *wcd938x = dev_get_drvdata(dev);
+
+ component_master_del(dev, &wcd938x_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+
+ if (wcd938x->us_euro_mux && wcd938x->mux_setup_done)
+ mux_control_deselect(wcd938x->us_euro_mux);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id wcd938x_dt_match[] = {
+ { .compatible = "qcom,wcd9380-codec" },
+ { .compatible = "qcom,wcd9385-codec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wcd938x_dt_match);
+#endif
+
+static struct platform_driver wcd938x_codec_driver = {
+ .probe = wcd938x_probe,
+ .remove = wcd938x_remove,
+ .driver = {
+ .name = "wcd938x_codec",
+ .of_match_table = of_match_ptr(wcd938x_dt_match),
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(wcd938x_codec_driver);
+MODULE_DESCRIPTION("WCD938X Codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h
new file mode 100644
index 000000000000..c18610466d7d
--- /dev/null
+++ b/sound/soc/codecs/wcd938x.h
@@ -0,0 +1,686 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __WCD938X_H__
+#define __WCD938X_H__
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define WCD938X_BASE_ADDRESS (0x3000)
+#define WCD938X_ANA_PAGE_REGISTER (0x3000)
+#define WCD938X_ANA_BIAS (0x3001)
+#define WCD938X_ANA_RX_SUPPLIES (0x3008)
+#define WCD938X_RX_BIAS_EN_MASK BIT(0)
+#define WCD938X_REGULATOR_MODE_MASK BIT(1)
+#define WCD938X_REGULATOR_MODE_CLASS_AB 1
+#define WCD938X_VNEG_EN_MASK BIT(6)
+#define WCD938X_VPOS_EN_MASK BIT(7)
+#define WCD938X_ANA_HPH (0x3009)
+#define WCD938X_HPHR_REF_EN_MASK BIT(4)
+#define WCD938X_HPHL_REF_EN_MASK BIT(5)
+#define WCD938X_HPHR_EN_MASK BIT(6)
+#define WCD938X_HPHL_EN_MASK BIT(7)
+#define WCD938X_ANA_EAR (0x300A)
+#define WCD938X_ANA_EAR_COMPANDER_CTL (0x300B)
+#define WCD938X_GAIN_OVRD_REG_MASK BIT(7)
+#define WCD938X_EAR_GAIN_MASK GENMASK(6, 2)
+#define WCD938X_ANA_TX_CH1 (0x300E)
+#define WCD938X_ANA_TX_CH2 (0x300F)
+#define WCD938X_HPF1_INIT_MASK BIT(6)
+#define WCD938X_HPF2_INIT_MASK BIT(5)
+#define WCD938X_ANA_TX_CH3 (0x3010)
+#define WCD938X_ANA_TX_CH4 (0x3011)
+#define WCD938X_HPF3_INIT_MASK BIT(6)
+#define WCD938X_HPF4_INIT_MASK BIT(5)
+#define WCD938X_ANA_MICB1_MICB2_DSP_EN_LOGIC (0x3012)
+#define WCD938X_ANA_MICB3_DSP_EN_LOGIC (0x3013)
+#define WCD938X_ANA_MBHC_MECH (0x3014)
+#define WCD938X_MBHC_L_DET_EN_MASK BIT(7)
+#define WCD938X_MBHC_L_DET_EN BIT(7)
+#define WCD938X_MBHC_GND_DET_EN_MASK BIT(6)
+#define WCD938X_MBHC_MECH_DETECT_TYPE_MASK BIT(5)
+#define WCD938X_MBHC_MECH_DETECT_TYPE_INS 1
+#define WCD938X_MBHC_HPHL_PLUG_TYPE_MASK BIT(4)
+#define WCD938X_MBHC_HPHL_PLUG_TYPE_NO 1
+#define WCD938X_MBHC_GND_PLUG_TYPE_MASK BIT(3)
+#define WCD938X_MBHC_GND_PLUG_TYPE_NO 1
+#define WCD938X_MBHC_HSL_PULLUP_COMP_EN BIT(2)
+#define WCD938X_MBHC_HSG_PULLUP_COMP_EN BIT(1)
+#define WCD938X_MBHC_HPHL_100K_TO_GND_EN BIT(0)
+
+#define WCD938X_ANA_MBHC_ELECT (0x3015)
+#define WCD938X_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4)
+#define WCD938X_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4)
+#define WCD938X_ANA_MBHC_BD_ISRC_OFF 0
+#define WCD938X_ANA_MBHC_BIAS_EN_MASK BIT(0)
+#define WCD938X_ANA_MBHC_BIAS_EN BIT(0)
+#define WCD938X_ANA_MBHC_ZDET (0x3016)
+#define WCD938X_ANA_MBHC_RESULT_1 (0x3017)
+#define WCD938X_ANA_MBHC_RESULT_2 (0x3018)
+#define WCD938X_ANA_MBHC_RESULT_3 (0x3019)
+#define WCD938X_MBHC_BTN_RESULT_MASK GENMASK(2, 0)
+#define WCD938X_ANA_MBHC_BTN0 (0x301A)
+#define WCD938X_MBHC_BTN_VTH_MASK GENMASK(7, 2)
+#define WCD938X_ANA_MBHC_BTN1 (0x301B)
+#define WCD938X_ANA_MBHC_BTN2 (0x301C)
+#define WCD938X_ANA_MBHC_BTN3 (0x301D)
+#define WCD938X_ANA_MBHC_BTN4 (0x301E)
+#define WCD938X_ANA_MBHC_BTN5 (0x301F)
+#define WCD938X_VTH_MASK GENMASK(7, 2)
+#define WCD938X_ANA_MBHC_BTN6 (0x3020)
+#define WCD938X_ANA_MBHC_BTN7 (0x3021)
+#define WCD938X_ANA_MICB1 (0x3022)
+#define WCD938X_MICB_VOUT_MASK GENMASK(5, 0)
+#define WCD938X_MICB_EN_MASK GENMASK(7, 6)
+#define WCD938X_MICB_DISABLE 0
+#define WCD938X_MICB_ENABLE 1
+#define WCD938X_MICB_PULL_UP 2
+#define WCD938X_MICB_PULL_DOWN 3
+#define WCD938X_ANA_MICB2 (0x3023)
+#define WCD938X_ANA_MICB2_RAMP (0x3024)
+#define WCD938X_RAMP_EN_MASK BIT(7)
+#define WCD938X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2)
+#define WCD938X_ANA_MICB3 (0x3025)
+#define WCD938X_ANA_MICB4 (0x3026)
+#define WCD938X_BIAS_CTL (0x3028)
+#define WCD938X_BIAS_VBG_FINE_ADJ (0x3029)
+#define WCD938X_LDOL_VDDCX_ADJUST (0x3040)
+#define WCD938X_LDOL_DISABLE_LDOL (0x3041)
+#define WCD938X_MBHC_CTL_CLK (0x3056)
+#define WCD938X_MBHC_CTL_ANA (0x3057)
+#define WCD938X_MBHC_CTL_SPARE_1 (0x3058)
+#define WCD938X_MBHC_CTL_SPARE_2 (0x3059)
+#define WCD938X_MBHC_CTL_BCS (0x305A)
+#define WCD938X_MBHC_MOISTURE_DET_FSM_STATUS (0x305B)
+#define WCD938X_MBHC_TEST_CTL (0x305C)
+#define WCD938X_LDOH_MODE (0x3067)
+#define WCD938X_LDOH_EN_MASK BIT(7)
+#define WCD938X_LDOH_BIAS (0x3068)
+#define WCD938X_LDOH_STB_LOADS (0x3069)
+#define WCD938X_LDOH_SLOWRAMP (0x306A)
+#define WCD938X_MICB1_TEST_CTL_1 (0x306B)
+#define WCD938X_MICB1_TEST_CTL_2 (0x306C)
+#define WCD938X_MICB1_TEST_CTL_3 (0x306D)
+#define WCD938X_MICB2_TEST_CTL_1 (0x306E)
+#define WCD938X_MICB2_TEST_CTL_2 (0x306F)
+#define WCD938X_MICB2_TEST_CTL_3 (0x3070)
+#define WCD938X_MICB3_TEST_CTL_1 (0x3071)
+#define WCD938X_MICB3_TEST_CTL_2 (0x3072)
+#define WCD938X_MICB3_TEST_CTL_3 (0x3073)
+#define WCD938X_MICB4_TEST_CTL_1 (0x3074)
+#define WCD938X_MICB4_TEST_CTL_2 (0x3075)
+#define WCD938X_MICB4_TEST_CTL_3 (0x3076)
+#define WCD938X_TX_COM_ADC_VCM (0x3077)
+#define WCD938X_TX_COM_BIAS_ATEST (0x3078)
+#define WCD938X_TX_COM_SPARE1 (0x3079)
+#define WCD938X_TX_COM_SPARE2 (0x307A)
+#define WCD938X_TX_COM_TXFE_DIV_CTL (0x307B)
+#define WCD938X_TX_COM_TXFE_DIV_START (0x307C)
+#define WCD938X_TX_COM_SPARE3 (0x307D)
+#define WCD938X_TX_COM_SPARE4 (0x307E)
+#define WCD938X_TX_1_2_TEST_EN (0x307F)
+#define WCD938X_TX_1_2_ADC_IB (0x3080)
+#define WCD938X_TX_1_2_ATEST_REFCTL (0x3081)
+#define WCD938X_TX_1_2_TEST_CTL (0x3082)
+#define WCD938X_TX_1_2_TEST_BLK_EN1 (0x3083)
+#define WCD938X_TX_1_2_TXFE1_CLKDIV (0x3084)
+#define WCD938X_TX_1_2_SAR2_ERR (0x3085)
+#define WCD938X_TX_1_2_SAR1_ERR (0x3086)
+#define WCD938X_TX_3_4_TEST_EN (0x3087)
+#define WCD938X_TX_3_4_ADC_IB (0x3088)
+#define WCD938X_TX_3_4_ATEST_REFCTL (0x3089)
+#define WCD938X_TX_3_4_TEST_CTL (0x308A)
+#define WCD938X_TX_3_4_TEST_BLK_EN3 (0x308B)
+#define WCD938X_TX_3_4_TXFE3_CLKDIV (0x308C)
+#define WCD938X_TX_3_4_SAR4_ERR (0x308D)
+#define WCD938X_TX_3_4_SAR3_ERR (0x308E)
+#define WCD938X_TX_3_4_TEST_BLK_EN2 (0x308F)
+#define WCD938X_TX_3_4_TXFE2_CLKDIV (0x3090)
+#define WCD938X_TX_3_4_SPARE1 (0x3091)
+#define WCD938X_TX_3_4_TEST_BLK_EN4 (0x3092)
+#define WCD938X_TX_3_4_TXFE4_CLKDIV (0x3093)
+#define WCD938X_TX_3_4_SPARE2 (0x3094)
+#define WCD938X_CLASSH_MODE_1 (0x3097)
+#define WCD938X_CLASSH_MODE_2 (0x3098)
+#define WCD938X_CLASSH_MODE_3 (0x3099)
+#define WCD938X_CLASSH_CTRL_VCL_1 (0x309A)
+#define WCD938X_CLASSH_CTRL_VCL_2 (0x309B)
+#define WCD938X_CLASSH_CTRL_CCL_1 (0x309C)
+#define WCD938X_CLASSH_CTRL_CCL_2 (0x309D)
+#define WCD938X_CLASSH_CTRL_CCL_3 (0x309E)
+#define WCD938X_CLASSH_CTRL_CCL_4 (0x309F)
+#define WCD938X_CLASSH_CTRL_CCL_5 (0x30A0)
+#define WCD938X_CLASSH_BUCK_TMUX_A_D (0x30A1)
+#define WCD938X_CLASSH_BUCK_SW_DRV_CNTL (0x30A2)
+#define WCD938X_CLASSH_SPARE (0x30A3)
+#define WCD938X_FLYBACK_EN (0x30A4)
+#define WCD938X_EN_CUR_DET_MASK BIT(2)
+#define WCD938X_FLYBACK_VNEG_CTRL_1 (0x30A5)
+#define WCD938X_FLYBACK_VNEG_CTRL_2 (0x30A6)
+#define WCD938X_FLYBACK_VNEG_CTRL_3 (0x30A7)
+#define WCD938X_FLYBACK_VNEG_CTRL_4 (0x30A8)
+#define WCD938X_FLYBACK_VNEG_CTRL_5 (0x30A9)
+#define WCD938X_FLYBACK_VNEG_CTRL_6 (0x30AA)
+#define WCD938X_FLYBACK_VNEG_CTRL_7 (0x30AB)
+#define WCD938X_FLYBACK_VNEG_CTRL_8 (0x30AC)
+#define WCD938X_FLYBACK_VNEG_CTRL_9 (0x30AD)
+#define WCD938X_FLYBACK_VNEGDAC_CTRL_1 (0x30AE)
+#define WCD938X_FLYBACK_VNEGDAC_CTRL_2 (0x30AF)
+#define WCD938X_FLYBACK_VNEGDAC_CTRL_3 (0x30B0)
+#define WCD938X_FLYBACK_CTRL_1 (0x30B1)
+#define WCD938X_FLYBACK_TEST_CTL (0x30B2)
+#define WCD938X_RX_AUX_SW_CTL (0x30B3)
+#define WCD938X_RX_PA_AUX_IN_CONN (0x30B4)
+#define WCD938X_RX_TIMER_DIV (0x30B5)
+#define WCD938X_RX_OCP_CTL (0x30B6)
+#define WCD938X_RX_OCP_COUNT (0x30B7)
+#define WCD938X_RX_BIAS_EAR_DAC (0x30B8)
+#define WCD938X_RX_BIAS_EAR_AMP (0x30B9)
+#define WCD938X_RX_BIAS_HPH_LDO (0x30BA)
+#define WCD938X_RX_BIAS_HPH_PA (0x30BB)
+#define WCD938X_RX_BIAS_HPH_RDACBUFF_CNP2 (0x30BC)
+#define WCD938X_RX_BIAS_HPH_RDAC_LDO (0x30BD)
+#define WCD938X_RX_BIAS_HPH_CNP1 (0x30BE)
+#define WCD938X_RX_BIAS_HPH_LOWPOWER (0x30BF)
+#define WCD938X_RX_BIAS_AUX_DAC (0x30C0)
+#define WCD938X_RX_BIAS_AUX_AMP (0x30C1)
+#define WCD938X_RX_BIAS_VNEGDAC_BLEEDER (0x30C2)
+#define WCD938X_RX_BIAS_MISC (0x30C3)
+#define WCD938X_RX_BIAS_BUCK_RST (0x30C4)
+#define WCD938X_RX_BIAS_BUCK_VREF_ERRAMP (0x30C5)
+#define WCD938X_RX_BIAS_FLYB_ERRAMP (0x30C6)
+#define WCD938X_RX_BIAS_FLYB_BUFF (0x30C7)
+#define WCD938X_RX_BIAS_FLYB_MID_RST (0x30C8)
+#define WCD938X_HPH_L_STATUS (0x30C9)
+#define WCD938X_HPH_R_STATUS (0x30CA)
+#define WCD938X_HPH_CNP_EN (0x30CB)
+#define WCD938X_HPH_CNP_WG_CTL (0x30CC)
+#define WCD938X_HPH_CNP_WG_TIME (0x30CD)
+#define WCD938X_HPH_OCP_CTL (0x30CE)
+#define WCD938X_HPH_AUTO_CHOP (0x30CF)
+#define WCD938X_HPH_CHOP_CTL (0x30D0)
+#define WCD938X_HPH_PA_CTL1 (0x30D1)
+#define WCD938X_HPH_PA_CTL2 (0x30D2)
+#define WCD938X_HPHPA_GND_R_MASK BIT(6)
+#define WCD938X_HPHPA_GND_L_MASK BIT(4)
+#define WCD938X_HPH_L_EN (0x30D3)
+#define WCD938X_HPH_L_TEST (0x30D4)
+#define WCD938X_HPH_L_ATEST (0x30D5)
+#define WCD938X_HPH_R_EN (0x30D6)
+#define WCD938X_GAIN_SRC_SEL_MASK BIT(5)
+#define WCD938X_GAIN_SRC_SEL_REGISTER 1
+#define WCD938X_HPH_R_TEST (0x30D7)
+#define WCD938X_HPH_R_ATEST (0x30D8)
+#define WCD938X_HPHPA_GND_OVR_MASK BIT(1)
+#define WCD938X_HPH_RDAC_CLK_CTL1 (0x30D9)
+#define WCD938X_CHOP_CLK_EN_MASK BIT(7)
+#define WCD938X_HPH_RDAC_CLK_CTL2 (0x30DA)
+#define WCD938X_HPH_RDAC_LDO_CTL (0x30DB)
+#define WCD938X_HPH_RDAC_CHOP_CLK_LP_CTL (0x30DC)
+#define WCD938X_HPH_REFBUFF_UHQA_CTL (0x30DD)
+#define WCD938X_HPH_REFBUFF_LP_CTL (0x30DE)
+#define WCD938X_PREREF_FLIT_BYPASS_MASK BIT(0)
+#define WCD938X_HPH_L_DAC_CTL (0x30DF)
+#define WCD938X_HPH_R_DAC_CTL (0x30E0)
+#define WCD938X_HPH_SURGE_HPHLR_SURGE_COMP_SEL (0x30E1)
+#define WCD938X_HPH_SURGE_HPHLR_SURGE_EN (0x30E2)
+#define WCD938X_HPH_SURGE_HPHLR_SURGE_MISC1 (0x30E3)
+#define WCD938X_HPH_SURGE_HPHLR_SURGE_STATUS (0x30E4)
+#define WCD938X_EAR_EAR_EN_REG (0x30E9)
+#define WCD938X_EAR_EAR_PA_CON (0x30EA)
+#define WCD938X_EAR_EAR_SP_CON (0x30EB)
+#define WCD938X_EAR_EAR_DAC_CON (0x30EC)
+#define WCD938X_DAC_SAMPLE_EDGE_SEL_MASK BIT(7)
+#define WCD938X_EAR_EAR_CNP_FSM_CON (0x30ED)
+#define WCD938X_EAR_TEST_CTL (0x30EE)
+#define WCD938X_EAR_STATUS_REG_1 (0x30EF)
+#define WCD938X_EAR_STATUS_REG_2 (0x30F0)
+#define WCD938X_ANA_NEW_PAGE_REGISTER (0x3100)
+#define WCD938X_HPH_NEW_ANA_HPH2 (0x3101)
+#define WCD938X_HPH_NEW_ANA_HPH3 (0x3102)
+#define WCD938X_SLEEP_CTL (0x3103)
+#define WCD938X_SLEEP_WATCHDOG_CTL (0x3104)
+#define WCD938X_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x311F)
+#define WCD938X_MBHC_NEW_CTL_1 (0x3120)
+#define WCD938X_MBHC_CTL_RCO_EN_MASK BIT(7)
+#define WCD938X_MBHC_CTL_RCO_EN BIT(7)
+#define WCD938X_MBHC_BTN_DBNC_MASK GENMASK(1, 0)
+#define WCD938X_MBHC_BTN_DBNC_T_16_MS 0x2
+#define WCD938X_MBHC_NEW_CTL_2 (0x3121)
+#define WCD938X_M_RTH_CTL_MASK GENMASK(3, 2)
+#define WCD938X_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0)
+#define WCD938X_MBHC_HS_VREF_1P5_V 0x1
+#define WCD938X_MBHC_NEW_PLUG_DETECT_CTL (0x3122)
+#define WCD938X_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6
+
+#define WCD938X_MBHC_NEW_ZDET_ANA_CTL (0x3123)
+#define WCD938X_ZDET_RANGE_CTL_MASK GENMASK(3, 0)
+#define WCD938X_ZDET_MAXV_CTL_MASK GENMASK(6, 4)
+#define WCD938X_MBHC_NEW_ZDET_RAMP_CTL (0x3124)
+#define WCD938X_MBHC_NEW_FSM_STATUS (0x3125)
+#define WCD938X_MBHC_NEW_ADC_RESULT (0x3126)
+#define WCD938X_TX_NEW_AMIC_MUX_CFG (0x3127)
+#define WCD938X_AUX_AUXPA (0x3128)
+#define WCD938X_AUXPA_CLK_EN_MASK BIT(4)
+#define WCD938X_LDORXTX_MODE (0x3129)
+#define WCD938X_LDORXTX_CONFIG (0x312A)
+#define WCD938X_DIE_CRACK_DIE_CRK_DET_EN (0x312C)
+#define WCD938X_DIE_CRACK_DIE_CRK_DET_OUT (0x312D)
+#define WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL (0x3132)
+#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L (0x3133)
+#define WCD938X_HPH_NEW_INT_RDAC_VREF_CTL (0x3134)
+#define WCD938X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x3135)
+#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R (0x3136)
+#define WCD938X_HPH_RES_DIV_MASK GENMASK(4, 0)
+#define WCD938X_HPH_NEW_INT_PA_MISC1 (0x3137)
+#define WCD938X_HPH_NEW_INT_PA_MISC2 (0x3138)
+#define WCD938X_HPH_NEW_INT_PA_RDAC_MISC (0x3139)
+#define WCD938X_HPH_NEW_INT_HPH_TIMER1 (0x313A)
+#define WCD938X_AUTOCHOP_TIMER_EN BIT(1)
+#define WCD938X_HPH_NEW_INT_HPH_TIMER2 (0x313B)
+#define WCD938X_HPH_NEW_INT_HPH_TIMER3 (0x313C)
+#define WCD938X_HPH_NEW_INT_HPH_TIMER4 (0x313D)
+#define WCD938X_HPH_NEW_INT_PA_RDAC_MISC2 (0x313E)
+#define WCD938X_HPH_NEW_INT_PA_RDAC_MISC3 (0x313F)
+#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW (0x3140)
+#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW (0x3141)
+#define WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x3145)
+#define WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x3146)
+#define WCD938X_RX_NEW_INT_HPH_RDAC_LDO_LP (0x3147)
+#define WCD938X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x31AF)
+#define WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x31B0)
+#define WCD938X_MOISTURE_EN_POLLING_MASK BIT(2)
+#define WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT (0x31B1)
+#define WCD938X_HSDET_PULLUP_C_MASK GENMASK(4, 0)
+#define WCD938X_MBHC_NEW_INT_SPARE_2 (0x31B2)
+#define WCD938X_EAR_INT_NEW_EAR_CHOPPER_CON (0x31B7)
+#define WCD938X_EAR_INT_NEW_CNP_VCM_CON1 (0x31B8)
+#define WCD938X_EAR_INT_NEW_CNP_VCM_CON2 (0x31B9)
+#define WCD938X_EAR_INT_NEW_EAR_DYNAMIC_BIAS (0x31BA)
+#define WCD938X_AUX_INT_EN_REG (0x31BD)
+#define WCD938X_AUX_INT_PA_CTRL (0x31BE)
+#define WCD938X_AUX_INT_SP_CTRL (0x31BF)
+#define WCD938X_AUX_INT_DAC_CTRL (0x31C0)
+#define WCD938X_AUX_INT_CLK_CTRL (0x31C1)
+#define WCD938X_AUX_INT_TEST_CTRL (0x31C2)
+#define WCD938X_AUX_INT_STATUS_REG (0x31C3)
+#define WCD938X_AUX_INT_MISC (0x31C4)
+#define WCD938X_LDORXTX_INT_BIAS (0x31C5)
+#define WCD938X_LDORXTX_INT_STB_LOADS_DTEST (0x31C6)
+#define WCD938X_LDORXTX_INT_TEST0 (0x31C7)
+#define WCD938X_LDORXTX_INT_STARTUP_TIMER (0x31C8)
+#define WCD938X_LDORXTX_INT_TEST1 (0x31C9)
+#define WCD938X_LDORXTX_INT_STATUS (0x31CA)
+#define WCD938X_SLEEP_INT_WATCHDOG_CTL_1 (0x31D0)
+#define WCD938X_SLEEP_INT_WATCHDOG_CTL_2 (0x31D1)
+#define WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT1 (0x31D3)
+#define WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT2 (0x31D4)
+#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L2 (0x31D5)
+#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L1 (0x31D6)
+#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L0 (0x31D7)
+#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP1P2M (0x31D8)
+#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP0P6M (0x31D9)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L2L1 (0x31DA)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L0 (0x31DB)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_ULP (0x31DC)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L2L1 (0x31DD)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L0 (0x31DE)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP (0x31DF)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_L2L1L0 (0x31E0)
+#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP (0x31E1)
+#define WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L2L1 (0x31E2)
+#define WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L0ULP (0x31E3)
+#define WCD938X_TX_COM_NEW_INT_TXADC_INT_L2 (0x31E4)
+#define WCD938X_TX_COM_NEW_INT_TXADC_INT_L1 (0x31E5)
+#define WCD938X_TX_COM_NEW_INT_TXADC_INT_L0 (0x31E6)
+#define WCD938X_TX_COM_NEW_INT_TXADC_INT_ULP (0x31E7)
+#define WCD938X_DIGITAL_PAGE_REGISTER (0x3400)
+#define WCD938X_DIGITAL_CHIP_ID0 (0x3401)
+#define WCD938X_DIGITAL_CHIP_ID1 (0x3402)
+#define WCD938X_DIGITAL_CHIP_ID2 (0x3403)
+#define WCD938X_DIGITAL_CHIP_ID3 (0x3404)
+#define WCD938X_DIGITAL_SWR_TX_CLK_RATE (0x3405)
+#define WCD938X_DIGITAL_CDC_RST_CTL (0x3406)
+#define WCD938X_DIGITAL_TOP_CLK_CFG (0x3407)
+#define WCD938X_DIGITAL_CDC_ANA_CLK_CTL (0x3408)
+#define WCD938X_ANA_RX_CLK_EN_MASK BIT(0)
+#define WCD938X_ANA_RX_DIV2_CLK_EN_MASK BIT(1)
+#define WCD938X_ANA_RX_DIV4_CLK_EN_MASK BIT(2)
+#define WCD938X_ANA_TX_CLK_EN_MASK BIT(3)
+#define WCD938X_ANA_TX_DIV2_CLK_EN_MASK BIT(4)
+#define WCD938X_ANA_TX_DIV4_CLK_EN_MASK BIT(5)
+#define WCD938X_DIGITAL_CDC_DIG_CLK_CTL (0x3409)
+#define WCD938X_TXD3_CLK_EN_MASK BIT(7)
+#define WCD938X_TXD2_CLK_EN_MASK BIT(6)
+#define WCD938X_TXD1_CLK_EN_MASK BIT(5)
+#define WCD938X_TXD0_CLK_EN_MASK BIT(4)
+#define WCD938X_TX_CLK_EN_MASK GENMASK(7, 4)
+#define WCD938X_RXD2_CLK_EN_MASK BIT(2)
+#define WCD938X_RXD1_CLK_EN_MASK BIT(1)
+#define WCD938X_RXD0_CLK_EN_MASK BIT(0)
+#define WCD938X_DIGITAL_SWR_RST_EN (0x340A)
+#define WCD938X_DIGITAL_CDC_PATH_MODE (0x340B)
+#define WCD938X_DIGITAL_CDC_RX_RST (0x340C)
+#define WCD938X_DIGITAL_CDC_RX0_CTL (0x340D)
+#define WCD938X_DEM_DITHER_ENABLE_MASK BIT(6)
+#define WCD938X_DIGITAL_CDC_RX1_CTL (0x340E)
+#define WCD938X_DIGITAL_CDC_RX2_CTL (0x340F)
+#define WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1 (0x3410)
+#define WCD938X_TXD0_MODE_MASK GENMASK(3, 0)
+#define WCD938X_TXD1_MODE_MASK GENMASK(7, 4)
+#define WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3 (0x3411)
+#define WCD938X_TXD2_MODE_MASK GENMASK(3, 0)
+#define WCD938X_TXD3_MODE_MASK GENMASK(7, 4)
+#define WCD938X_DIGITAL_CDC_COMP_CTL_0 (0x3414)
+#define WCD938X_HPHR_COMP_EN_MASK BIT(0)
+#define WCD938X_HPHL_COMP_EN_MASK BIT(1)
+#define WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL (0x3417)
+#define WCD938X_TX_SC_CLK_EN_MASK BIT(0)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A1_0 (0x3418)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A1_1 (0x3419)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A2_0 (0x341A)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A2_1 (0x341B)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A3_0 (0x341C)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A3_1 (0x341D)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A4_0 (0x341E)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A4_1 (0x341F)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A5_0 (0x3420)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A5_1 (0x3421)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A6_0 (0x3422)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_A7_0 (0x3423)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_C_0 (0x3424)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_C_1 (0x3425)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_C_2 (0x3426)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_C_3 (0x3427)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R1 (0x3428)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R2 (0x3429)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R3 (0x342A)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R4 (0x342B)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R5 (0x342C)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R6 (0x342D)
+#define WCD938X_DIGITAL_CDC_HPH_DSM_R7 (0x342E)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A1_0 (0x342F)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A1_1 (0x3430)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A2_0 (0x3431)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A2_1 (0x3432)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A3_0 (0x3433)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A3_1 (0x3434)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A4_0 (0x3435)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A4_1 (0x3436)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A5_0 (0x3437)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A5_1 (0x3438)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A6_0 (0x3439)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_A7_0 (0x343A)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_C_0 (0x343B)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_C_1 (0x343C)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_C_2 (0x343D)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_C_3 (0x343E)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R1 (0x343F)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R2 (0x3440)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R3 (0x3441)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R4 (0x3442)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R5 (0x3443)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R6 (0x3444)
+#define WCD938X_DIGITAL_CDC_AUX_DSM_R7 (0x3445)
+#define WCD938X_DIGITAL_CDC_HPH_GAIN_RX_0 (0x3446)
+#define WCD938X_DIGITAL_CDC_HPH_GAIN_RX_1 (0x3447)
+#define WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_0 (0x3448)
+#define WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_1 (0x3449)
+#define WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_2 (0x344A)
+#define WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_0 (0x344B)
+#define WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_1 (0x344C)
+#define WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_2 (0x344D)
+#define WCD938X_DIGITAL_CDC_HPH_GAIN_CTL (0x344E)
+#define WCD938X_HPHL_RX_EN_MASK BIT(2)
+#define WCD938X_HPHR_RX_EN_MASK BIT(3)
+#define WCD938X_DIGITAL_CDC_AUX_GAIN_CTL (0x344F)
+#define WCD938X_AUX_EN_MASK BIT(0)
+#define WCD938X_DIGITAL_CDC_EAR_PATH_CTL (0x3450)
+#define WCD938X_DIGITAL_CDC_SWR_CLH (0x3451)
+#define WCD938X_DIGITAL_SWR_CLH_BYP (0x3452)
+#define WCD938X_DIGITAL_CDC_TX0_CTL (0x3453)
+#define WCD938X_DIGITAL_CDC_TX1_CTL (0x3454)
+#define WCD938X_DIGITAL_CDC_TX2_CTL (0x3455)
+#define WCD938X_DIGITAL_CDC_TX_RST (0x3456)
+#define WCD938X_DIGITAL_CDC_REQ_CTL (0x3457)
+#define WCD938X_FS_RATE_4P8_MASK BIT(1)
+#define WCD938X_NO_NOTCH_MASK BIT(0)
+#define WCD938X_DIGITAL_CDC_RST (0x3458)
+#define WCD938X_DIGITAL_CDC_AMIC_CTL (0x345A)
+#define WCD938X_AMIC1_IN_SEL_DMIC 0
+#define WCD938X_AMIC1_IN_SEL_AMIC 0
+#define WCD938X_AMIC1_IN_SEL_MASK BIT(0)
+#define WCD938X_AMIC3_IN_SEL_MASK BIT(1)
+#define WCD938X_AMIC4_IN_SEL_MASK BIT(2)
+#define WCD938X_AMIC5_IN_SEL_MASK BIT(3)
+#define WCD938X_DIGITAL_CDC_DMIC_CTL (0x345B)
+#define WCD938X_DMIC_CLK_SCALING_EN_MASK GENMASK(2, 1)
+#define WCD938X_DIGITAL_CDC_DMIC1_CTL (0x345C)
+#define WCD938X_DMIC_CLK_EN_MASK BIT(3)
+#define WCD938X_DIGITAL_CDC_DMIC2_CTL (0x345D)
+#define WCD938X_DIGITAL_CDC_DMIC3_CTL (0x345E)
+#define WCD938X_DIGITAL_CDC_DMIC4_CTL (0x345F)
+#define WCD938X_DIGITAL_EFUSE_PRG_CTL (0x3460)
+#define WCD938X_DIGITAL_EFUSE_CTL (0x3461)
+#define WCD938X_DIGITAL_CDC_DMIC_RATE_1_2 (0x3462)
+#define WCD938X_DIGITAL_CDC_DMIC_RATE_3_4 (0x3463)
+#define WCD938X_DMIC1_RATE_MASK GENMASK(3, 0)
+#define WCD938X_DMIC2_RATE_MASK GENMASK(7, 4)
+#define WCD938X_DMIC3_RATE_MASK GENMASK(3, 0)
+#define WCD938X_DMIC4_RATE_MASK GENMASK(7, 4)
+#define WCD938X_DMIC4_RATE_2P4MHZ 3
+
+#define WCD938X_DIGITAL_PDM_WD_CTL0 (0x3465)
+#define WCD938X_PDM_WD_EN_MASK GENMASK(2, 0)
+#define WCD938X_DIGITAL_PDM_WD_CTL1 (0x3466)
+#define WCD938X_DIGITAL_PDM_WD_CTL2 (0x3467)
+#define WCD938X_AUX_PDM_WD_EN_MASK GENMASK(2, 0)
+#define WCD938X_DIGITAL_INTR_MODE (0x346A)
+#define WCD938X_DIGITAL_INTR_MASK_0 (0x346B)
+#define WCD938X_DIGITAL_INTR_MASK_1 (0x346C)
+#define WCD938X_DIGITAL_INTR_MASK_2 (0x346D)
+#define WCD938X_DIGITAL_INTR_STATUS_0 (0x346E)
+#define WCD938X_DIGITAL_INTR_STATUS_1 (0x346F)
+#define WCD938X_DIGITAL_INTR_STATUS_2 (0x3470)
+#define WCD938X_DIGITAL_INTR_CLEAR_0 (0x3471)
+#define WCD938X_DIGITAL_INTR_CLEAR_1 (0x3472)
+#define WCD938X_DIGITAL_INTR_CLEAR_2 (0x3473)
+#define WCD938X_DIGITAL_INTR_LEVEL_0 (0x3474)
+#define WCD938X_DIGITAL_INTR_LEVEL_1 (0x3475)
+#define WCD938X_DIGITAL_INTR_LEVEL_2 (0x3476)
+#define WCD938X_DIGITAL_INTR_SET_0 (0x3477)
+#define WCD938X_DIGITAL_INTR_SET_1 (0x3478)
+#define WCD938X_DIGITAL_INTR_SET_2 (0x3479)
+#define WCD938X_DIGITAL_INTR_TEST_0 (0x347A)
+#define WCD938X_DIGITAL_INTR_TEST_1 (0x347B)
+#define WCD938X_DIGITAL_INTR_TEST_2 (0x347C)
+#define WCD938X_DIGITAL_TX_MODE_DBG_EN (0x347F)
+#define WCD938X_DIGITAL_TX_MODE_DBG_0_1 (0x3480)
+#define WCD938X_DIGITAL_TX_MODE_DBG_2_3 (0x3481)
+#define WCD938X_DIGITAL_LB_IN_SEL_CTL (0x3482)
+#define WCD938X_DIGITAL_LOOP_BACK_MODE (0x3483)
+#define WCD938X_DIGITAL_SWR_DAC_TEST (0x3484)
+#define WCD938X_DIGITAL_SWR_HM_TEST_RX_0 (0x3485)
+#define WCD938X_DIGITAL_SWR_HM_TEST_TX_0 (0x3486)
+#define WCD938X_DIGITAL_SWR_HM_TEST_RX_1 (0x3487)
+#define WCD938X_DIGITAL_SWR_HM_TEST_TX_1 (0x3488)
+#define WCD938X_DIGITAL_SWR_HM_TEST_TX_2 (0x3489)
+#define WCD938X_DIGITAL_SWR_HM_TEST_0 (0x348A)
+#define WCD938X_DIGITAL_SWR_HM_TEST_1 (0x348B)
+#define WCD938X_DIGITAL_PAD_CTL_SWR_0 (0x348C)
+#define WCD938X_DIGITAL_PAD_CTL_SWR_1 (0x348D)
+#define WCD938X_DIGITAL_I2C_CTL (0x348E)
+#define WCD938X_DIGITAL_CDC_TX_TANGGU_SW_MODE (0x348F)
+#define WCD938X_DIGITAL_EFUSE_TEST_CTL_0 (0x3490)
+#define WCD938X_DIGITAL_EFUSE_TEST_CTL_1 (0x3491)
+#define WCD938X_DIGITAL_EFUSE_T_DATA_0 (0x3492)
+#define WCD938X_DIGITAL_EFUSE_T_DATA_1 (0x3493)
+#define WCD938X_DIGITAL_PAD_CTL_PDM_RX0 (0x3494)
+#define WCD938X_DIGITAL_PAD_CTL_PDM_RX1 (0x3495)
+#define WCD938X_DIGITAL_PAD_CTL_PDM_TX0 (0x3496)
+#define WCD938X_DIGITAL_PAD_CTL_PDM_TX1 (0x3497)
+#define WCD938X_DIGITAL_PAD_CTL_PDM_TX2 (0x3498)
+#define WCD938X_DIGITAL_PAD_INP_DIS_0 (0x3499)
+#define WCD938X_DIGITAL_PAD_INP_DIS_1 (0x349A)
+#define WCD938X_DIGITAL_DRIVE_STRENGTH_0 (0x349B)
+#define WCD938X_DIGITAL_DRIVE_STRENGTH_1 (0x349C)
+#define WCD938X_DIGITAL_DRIVE_STRENGTH_2 (0x349D)
+#define WCD938X_DIGITAL_RX_DATA_EDGE_CTL (0x349E)
+#define WCD938X_DIGITAL_TX_DATA_EDGE_CTL (0x349F)
+#define WCD938X_DIGITAL_GPIO_MODE (0x34A0)
+#define WCD938X_DIGITAL_PIN_CTL_OE (0x34A1)
+#define WCD938X_DIGITAL_PIN_CTL_DATA_0 (0x34A2)
+#define WCD938X_DIGITAL_PIN_CTL_DATA_1 (0x34A3)
+#define WCD938X_DIGITAL_PIN_STATUS_0 (0x34A4)
+#define WCD938X_DIGITAL_PIN_STATUS_1 (0x34A5)
+#define WCD938X_DIGITAL_DIG_DEBUG_CTL (0x34A6)
+#define WCD938X_DIGITAL_DIG_DEBUG_EN (0x34A7)
+#define WCD938X_DIGITAL_ANA_CSR_DBG_ADD (0x34A8)
+#define WCD938X_DIGITAL_ANA_CSR_DBG_CTL (0x34A9)
+#define WCD938X_DIGITAL_SSP_DBG (0x34AA)
+#define WCD938X_DIGITAL_MODE_STATUS_0 (0x34AB)
+#define WCD938X_DIGITAL_MODE_STATUS_1 (0x34AC)
+#define WCD938X_DIGITAL_SPARE_0 (0x34AD)
+#define WCD938X_DIGITAL_SPARE_1 (0x34AE)
+#define WCD938X_DIGITAL_SPARE_2 (0x34AF)
+#define WCD938X_DIGITAL_EFUSE_REG_0 (0x34B0)
+#define WCD938X_ID_MASK GENMASK(4, 1)
+#define WCD938X_DIGITAL_EFUSE_REG_1 (0x34B1)
+#define WCD938X_DIGITAL_EFUSE_REG_2 (0x34B2)
+#define WCD938X_DIGITAL_EFUSE_REG_3 (0x34B3)
+#define WCD938X_DIGITAL_EFUSE_REG_4 (0x34B4)
+#define WCD938X_DIGITAL_EFUSE_REG_5 (0x34B5)
+#define WCD938X_DIGITAL_EFUSE_REG_6 (0x34B6)
+#define WCD938X_DIGITAL_EFUSE_REG_7 (0x34B7)
+#define WCD938X_DIGITAL_EFUSE_REG_8 (0x34B8)
+#define WCD938X_DIGITAL_EFUSE_REG_9 (0x34B9)
+#define WCD938X_DIGITAL_EFUSE_REG_10 (0x34BA)
+#define WCD938X_DIGITAL_EFUSE_REG_11 (0x34BB)
+#define WCD938X_DIGITAL_EFUSE_REG_12 (0x34BC)
+#define WCD938X_DIGITAL_EFUSE_REG_13 (0x34BD)
+#define WCD938X_DIGITAL_EFUSE_REG_14 (0x34BE)
+#define WCD938X_DIGITAL_EFUSE_REG_15 (0x34BF)
+#define WCD938X_DIGITAL_EFUSE_REG_16 (0x34C0)
+#define WCD938X_DIGITAL_EFUSE_REG_17 (0x34C1)
+#define WCD938X_DIGITAL_EFUSE_REG_18 (0x34C2)
+#define WCD938X_DIGITAL_EFUSE_REG_19 (0x34C3)
+#define WCD938X_DIGITAL_EFUSE_REG_20 (0x34C4)
+#define WCD938X_DIGITAL_EFUSE_REG_21 (0x34C5)
+#define WCD938X_DIGITAL_EFUSE_REG_22 (0x34C6)
+#define WCD938X_DIGITAL_EFUSE_REG_23 (0x34C7)
+#define WCD938X_DIGITAL_EFUSE_REG_24 (0x34C8)
+#define WCD938X_DIGITAL_EFUSE_REG_25 (0x34C9)
+#define WCD938X_DIGITAL_EFUSE_REG_26 (0x34CA)
+#define WCD938X_DIGITAL_EFUSE_REG_27 (0x34CB)
+#define WCD938X_DIGITAL_EFUSE_REG_28 (0x34CC)
+#define WCD938X_DIGITAL_EFUSE_REG_29 (0x34CD)
+#define WCD938X_DIGITAL_EFUSE_REG_30 (0x34CE)
+#define WCD938X_DIGITAL_EFUSE_REG_31 (0x34CF)
+#define WCD938X_DIGITAL_TX_REQ_FB_CTL_0 (0x34D0)
+#define WCD938X_DIGITAL_TX_REQ_FB_CTL_1 (0x34D1)
+#define WCD938X_DIGITAL_TX_REQ_FB_CTL_2 (0x34D2)
+#define WCD938X_DIGITAL_TX_REQ_FB_CTL_3 (0x34D3)
+#define WCD938X_DIGITAL_TX_REQ_FB_CTL_4 (0x34D4)
+#define WCD938X_DIGITAL_DEM_BYPASS_DATA0 (0x34D5)
+#define WCD938X_DIGITAL_DEM_BYPASS_DATA1 (0x34D6)
+#define WCD938X_DIGITAL_DEM_BYPASS_DATA2 (0x34D7)
+#define WCD938X_DIGITAL_DEM_BYPASS_DATA3 (0x34D8)
+#define WCD938X_MAX_REGISTER (WCD938X_DIGITAL_DEM_BYPASS_DATA3)
+
+#define WCD938X_MAX_SWR_CH_IDS 15
+
+enum wcd938x_tx_sdw_ports {
+ WCD938X_ADC_1_2_PORT = 1,
+ WCD938X_ADC_3_4_PORT,
+ /* DMIC0_0, DMIC0_1, DMIC1_0, DMIC1_1 */
+ WCD938X_DMIC_0_3_MBHC_PORT,
+ WCD938X_DMIC_4_7_PORT,
+ WCD938X_MAX_TX_SWR_PORTS = WCD938X_DMIC_4_7_PORT,
+};
+
+enum wcd938x_tx_sdw_channels {
+ WCD938X_ADC1,
+ WCD938X_ADC2,
+ WCD938X_ADC3,
+ WCD938X_ADC4,
+ WCD938X_DMIC0,
+ WCD938X_DMIC1,
+ WCD938X_MBHC,
+ WCD938X_DMIC2,
+ WCD938X_DMIC3,
+ WCD938X_DMIC4,
+ WCD938X_DMIC5,
+ WCD938X_DMIC6,
+ WCD938X_DMIC7,
+};
+
+enum wcd938x_rx_sdw_ports {
+ WCD938X_HPH_PORT = 1,
+ WCD938X_CLSH_PORT,
+ WCD938X_COMP_PORT,
+ WCD938X_LO_PORT,
+ WCD938X_DSD_PORT,
+ WCD938X_MAX_SWR_PORTS = WCD938X_DSD_PORT,
+};
+
+enum wcd938x_rx_sdw_channels {
+ WCD938X_HPH_L,
+ WCD938X_HPH_R,
+ WCD938X_CLSH,
+ WCD938X_COMP_L,
+ WCD938X_COMP_R,
+ WCD938X_LO,
+ WCD938X_DSD_R,
+ WCD938X_DSD_L,
+};
+
+struct wcd938x_priv;
+struct wcd938x_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS];
+ const struct wcd_sdw_ch_info *ch_info;
+ bool port_enable[WCD938X_MAX_SWR_CH_IDS];
+ int active_ports;
+ bool is_tx;
+ struct wcd938x_priv *wcd938x;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_WCD938X_SDW)
+int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction);
+int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+#else
+
+static inline int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_SND_SOC_WCD938X_SDW */
+#endif /* __WCD938X_H__ */
diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c
new file mode 100644
index 000000000000..da342a0c95a5
--- /dev/null
+++ b/sound/soc/codecs/wcd939x-sdw.c
@@ -0,0 +1,1477 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "wcd939x.h"
+#include "wcd-common.h"
+
+static const struct wcd_sdw_ch_info wcd939x_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)),
+ WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_COMP_L, WCD939X_COMP_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_COMP_R, WCD939X_COMP_PORT, BIT(1)),
+ WCD_SDW_CH(WCD939X_LO, WCD939X_LO_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_DSD_L, WCD939X_DSD_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_DSD_R, WCD939X_DSD_PORT, BIT(1)),
+ WCD_SDW_CH(WCD939X_HIFI_PCM_L, WCD939X_HIFI_PCM_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)),
+};
+
+static const struct wcd_sdw_ch_info wcd939x_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)),
+ WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)),
+ WCD_SDW_CH(WCD939X_ADC4, WCD939X_ADC_1_4_PORT, BIT(3)),
+ WCD_SDW_CH(WCD939X_DMIC0, WCD939X_DMIC_0_3_MBHC_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_DMIC1, WCD939X_DMIC_0_3_MBHC_PORT, BIT(1)),
+ WCD_SDW_CH(WCD939X_MBHC, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)),
+ WCD_SDW_CH(WCD939X_DMIC2, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)),
+ WCD_SDW_CH(WCD939X_DMIC3, WCD939X_DMIC_0_3_MBHC_PORT, BIT(3)),
+ WCD_SDW_CH(WCD939X_DMIC4, WCD939X_DMIC_3_7_PORT, BIT(0)),
+ WCD_SDW_CH(WCD939X_DMIC5, WCD939X_DMIC_3_7_PORT, BIT(1)),
+ WCD_SDW_CH(WCD939X_DMIC6, WCD939X_DMIC_3_7_PORT, BIT(2)),
+ WCD_SDW_CH(WCD939X_DMIC7, WCD939X_DMIC_3_7_PORT, BIT(3)),
+};
+
+static struct sdw_dpn_prop wcd939x_rx_dpn_prop[WCD939X_MAX_RX_SWR_PORTS] = {
+ {
+ .num = WCD939X_HPH_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 2,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_CLSH_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_COMP_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 2,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_LO_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_DSD_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 2,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_HIFI_PCM_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 2,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+static struct sdw_dpn_prop wcd939x_tx_dpn_prop[WCD939X_MAX_TX_SWR_PORTS] = {
+ {
+ .num = WCD939X_ADC_1_4_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_ADC_DMIC_1_2_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_DMIC_0_3_MBHC_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ },
+ {
+ .num = WCD939X_DMIC_3_7_PORT,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ wcd->sconfig.ch_count = 1;
+ wcd->active_ports = 0;
+ for (i = 0; i < WCD939X_MAX_SWR_PORTS; i++) {
+ ch_mask = wcd->port_config[i].ch_mask;
+
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ wcd->sconfig.ch_count++;
+
+ port_config[wcd->active_ports] = wcd->port_config[i];
+ wcd->active_ports++;
+ }
+
+ wcd->sconfig.bps = 1;
+ wcd->sconfig.frame_rate = params_rate(params);
+ if (wcd->is_tx)
+ wcd->sconfig.direction = SDW_DATA_DIR_TX;
+ else
+ wcd->sconfig.direction = SDW_DATA_DIR_RX;
+
+ wcd->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, &port_config[0],
+ wcd->active_ports, wcd->sruntime);
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_hw_params);
+
+int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ sdw_stream_remove_slave(wcd->sdev, wcd->sruntime);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_free);
+
+int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
+ struct snd_soc_dai *dai, void *stream,
+ int direction)
+{
+ wcd->sruntime = stream;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd939x_sdw_set_sdw_stream);
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering
+ * the first irq of the slave_irq irq domain, which then will
+ * be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int wcd9390_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
+
+ return wcd_interrupt_callback(slave, wcd->slave_irq, WCD939X_DIGITAL_INTR_STATUS_0,
+ WCD939X_DIGITAL_INTR_STATUS_1, WCD939X_DIGITAL_INTR_STATUS_2);
+}
+
+static const struct reg_default wcd939x_defaults[] = {
+ /* Default values except for Read-Only & Volatile registers */
+ { WCD939X_ANA_PAGE, 0x00 },
+ { WCD939X_ANA_BIAS, 0x00 },
+ { WCD939X_ANA_RX_SUPPLIES, 0x00 },
+ { WCD939X_ANA_HPH, 0x0c },
+ { WCD939X_ANA_EAR, 0x00 },
+ { WCD939X_ANA_EAR_COMPANDER_CTL, 0x02 },
+ { WCD939X_ANA_TX_CH1, 0x20 },
+ { WCD939X_ANA_TX_CH2, 0x00 },
+ { WCD939X_ANA_TX_CH3, 0x20 },
+ { WCD939X_ANA_TX_CH4, 0x00 },
+ { WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 },
+ { WCD939X_ANA_MICB3_DSP_EN_LOGIC, 0x00 },
+ { WCD939X_ANA_MBHC_MECH, 0x39 },
+ { WCD939X_ANA_MBHC_ELECT, 0x08 },
+ { WCD939X_ANA_MBHC_ZDET, 0x00 },
+ { WCD939X_ANA_MBHC_BTN0, 0x00 },
+ { WCD939X_ANA_MBHC_BTN1, 0x10 },
+ { WCD939X_ANA_MBHC_BTN2, 0x20 },
+ { WCD939X_ANA_MBHC_BTN3, 0x30 },
+ { WCD939X_ANA_MBHC_BTN4, 0x40 },
+ { WCD939X_ANA_MBHC_BTN5, 0x50 },
+ { WCD939X_ANA_MBHC_BTN6, 0x60 },
+ { WCD939X_ANA_MBHC_BTN7, 0x70 },
+ { WCD939X_ANA_MICB1, 0x10 },
+ { WCD939X_ANA_MICB2, 0x10 },
+ { WCD939X_ANA_MICB2_RAMP, 0x00 },
+ { WCD939X_ANA_MICB3, 0x00 },
+ { WCD939X_ANA_MICB4, 0x00 },
+ { WCD939X_BIAS_CTL, 0x2a },
+ { WCD939X_BIAS_VBG_FINE_ADJ, 0x55 },
+ { WCD939X_LDOL_VDDCX_ADJUST, 0x01 },
+ { WCD939X_LDOL_DISABLE_LDOL, 0x00 },
+ { WCD939X_MBHC_CTL_CLK, 0x00 },
+ { WCD939X_MBHC_CTL_ANA, 0x00 },
+ { WCD939X_MBHC_ZDET_VNEG_CTL, 0x00 },
+ { WCD939X_MBHC_ZDET_BIAS_CTL, 0x46 },
+ { WCD939X_MBHC_CTL_BCS, 0x00 },
+ { WCD939X_MBHC_TEST_CTL, 0x00 },
+ { WCD939X_LDOH_MODE, 0x2b },
+ { WCD939X_LDOH_BIAS, 0x68 },
+ { WCD939X_LDOH_STB_LOADS, 0x00 },
+ { WCD939X_LDOH_SLOWRAMP, 0x50 },
+ { WCD939X_MICB1_TEST_CTL_1, 0x1a },
+ { WCD939X_MICB1_TEST_CTL_2, 0x00 },
+ { WCD939X_MICB1_TEST_CTL_3, 0xa4 },
+ { WCD939X_MICB2_TEST_CTL_1, 0x1a },
+ { WCD939X_MICB2_TEST_CTL_2, 0x00 },
+ { WCD939X_MICB2_TEST_CTL_3, 0x24 },
+ { WCD939X_MICB3_TEST_CTL_1, 0x9a },
+ { WCD939X_MICB3_TEST_CTL_2, 0x80 },
+ { WCD939X_MICB3_TEST_CTL_3, 0x24 },
+ { WCD939X_MICB4_TEST_CTL_1, 0x1a },
+ { WCD939X_MICB4_TEST_CTL_2, 0x80 },
+ { WCD939X_MICB4_TEST_CTL_3, 0x24 },
+ { WCD939X_TX_COM_ADC_VCM, 0x39 },
+ { WCD939X_TX_COM_BIAS_ATEST, 0xe0 },
+ { WCD939X_TX_COM_SPARE1, 0x00 },
+ { WCD939X_TX_COM_SPARE2, 0x00 },
+ { WCD939X_TX_COM_TXFE_DIV_CTL, 0x22 },
+ { WCD939X_TX_COM_TXFE_DIV_START, 0x00 },
+ { WCD939X_TX_COM_SPARE3, 0x00 },
+ { WCD939X_TX_COM_SPARE4, 0x00 },
+ { WCD939X_TX_1_2_TEST_EN, 0xcc },
+ { WCD939X_TX_1_2_ADC_IB, 0xe9 },
+ { WCD939X_TX_1_2_ATEST_REFCTL, 0x0b },
+ { WCD939X_TX_1_2_TEST_CTL, 0x38 },
+ { WCD939X_TX_1_2_TEST_BLK_EN1, 0xff },
+ { WCD939X_TX_1_2_TXFE1_CLKDIV, 0x00 },
+ { WCD939X_TX_3_4_TEST_EN, 0xcc },
+ { WCD939X_TX_3_4_ADC_IB, 0xe9 },
+ { WCD939X_TX_3_4_ATEST_REFCTL, 0x0b },
+ { WCD939X_TX_3_4_TEST_CTL, 0x38 },
+ { WCD939X_TX_3_4_TEST_BLK_EN3, 0xff },
+ { WCD939X_TX_3_4_TXFE3_CLKDIV, 0x00 },
+ { WCD939X_TX_3_4_TEST_BLK_EN2, 0xfb },
+ { WCD939X_TX_3_4_TXFE2_CLKDIV, 0x00 },
+ { WCD939X_TX_3_4_SPARE1, 0x00 },
+ { WCD939X_TX_3_4_TEST_BLK_EN4, 0xfb },
+ { WCD939X_TX_3_4_TXFE4_CLKDIV, 0x00 },
+ { WCD939X_TX_3_4_SPARE2, 0x00 },
+ { WCD939X_CLASSH_MODE_1, 0x40 },
+ { WCD939X_CLASSH_MODE_2, 0x3a },
+ { WCD939X_CLASSH_MODE_3, 0xf0 },
+ { WCD939X_CLASSH_CTRL_VCL_1, 0x7c },
+ { WCD939X_CLASSH_CTRL_VCL_2, 0x82 },
+ { WCD939X_CLASSH_CTRL_CCL_1, 0x31 },
+ { WCD939X_CLASSH_CTRL_CCL_2, 0x80 },
+ { WCD939X_CLASSH_CTRL_CCL_3, 0x80 },
+ { WCD939X_CLASSH_CTRL_CCL_4, 0x51 },
+ { WCD939X_CLASSH_CTRL_CCL_5, 0x00 },
+ { WCD939X_CLASSH_BUCK_TMUX_A_D, 0x00 },
+ { WCD939X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 },
+ { WCD939X_CLASSH_SPARE, 0x80 },
+ { WCD939X_FLYBACK_EN, 0x4e },
+ { WCD939X_FLYBACK_VNEG_CTRL_1, 0x0b },
+ { WCD939X_FLYBACK_VNEG_CTRL_2, 0x45 },
+ { WCD939X_FLYBACK_VNEG_CTRL_3, 0x14 },
+ { WCD939X_FLYBACK_VNEG_CTRL_4, 0xdb },
+ { WCD939X_FLYBACK_VNEG_CTRL_5, 0x83 },
+ { WCD939X_FLYBACK_VNEG_CTRL_6, 0x98 },
+ { WCD939X_FLYBACK_VNEG_CTRL_7, 0xa9 },
+ { WCD939X_FLYBACK_VNEG_CTRL_8, 0x68 },
+ { WCD939X_FLYBACK_VNEG_CTRL_9, 0x66 },
+ { WCD939X_FLYBACK_VNEGDAC_CTRL_1, 0xed },
+ { WCD939X_FLYBACK_VNEGDAC_CTRL_2, 0xf8 },
+ { WCD939X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 },
+ { WCD939X_FLYBACK_CTRL_1, 0x65 },
+ { WCD939X_FLYBACK_TEST_CTL, 0x02 },
+ { WCD939X_RX_AUX_SW_CTL, 0x00 },
+ { WCD939X_RX_PA_AUX_IN_CONN, 0x01 },
+ { WCD939X_RX_TIMER_DIV, 0x32 },
+ { WCD939X_RX_OCP_CTL, 0x1f },
+ { WCD939X_RX_OCP_COUNT, 0x77 },
+ { WCD939X_RX_BIAS_EAR_DAC, 0xa0 },
+ { WCD939X_RX_BIAS_EAR_AMP, 0xaa },
+ { WCD939X_RX_BIAS_HPH_LDO, 0xa9 },
+ { WCD939X_RX_BIAS_HPH_PA, 0xaa },
+ { WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2, 0xca },
+ { WCD939X_RX_BIAS_HPH_RDAC_LDO, 0x88 },
+ { WCD939X_RX_BIAS_HPH_CNP1, 0x82 },
+ { WCD939X_RX_BIAS_HPH_LOWPOWER, 0x82 },
+ { WCD939X_RX_BIAS_AUX_DAC, 0xa0 },
+ { WCD939X_RX_BIAS_AUX_AMP, 0xaa },
+ { WCD939X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 },
+ { WCD939X_RX_BIAS_MISC, 0x00 },
+ { WCD939X_RX_BIAS_BUCK_RST, 0x08 },
+ { WCD939X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 },
+ { WCD939X_RX_BIAS_FLYB_ERRAMP, 0x40 },
+ { WCD939X_RX_BIAS_FLYB_BUFF, 0xaa },
+ { WCD939X_RX_BIAS_FLYB_MID_RST, 0x14 },
+ { WCD939X_HPH_CNP_EN, 0x80 },
+ { WCD939X_HPH_CNP_WG_CTL, 0x9a },
+ { WCD939X_HPH_CNP_WG_TIME, 0x14 },
+ { WCD939X_HPH_OCP_CTL, 0x28 },
+ { WCD939X_HPH_AUTO_CHOP, 0x56 },
+ { WCD939X_HPH_CHOP_CTL, 0x83 },
+ { WCD939X_HPH_PA_CTL1, 0x46 },
+ { WCD939X_HPH_PA_CTL2, 0x50 },
+ { WCD939X_HPH_L_EN, 0x80 },
+ { WCD939X_HPH_L_TEST, 0xe0 },
+ { WCD939X_HPH_L_ATEST, 0x50 },
+ { WCD939X_HPH_R_EN, 0x80 },
+ { WCD939X_HPH_R_TEST, 0xe0 },
+ { WCD939X_HPH_R_ATEST, 0x50 },
+ { WCD939X_HPH_RDAC_CLK_CTL1, 0x80 },
+ { WCD939X_HPH_RDAC_CLK_CTL2, 0x0b },
+ { WCD939X_HPH_RDAC_LDO_CTL, 0x33 },
+ { WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
+ { WCD939X_HPH_REFBUFF_UHQA_CTL, 0x00 },
+ { WCD939X_HPH_REFBUFF_LP_CTL, 0x8e },
+ { WCD939X_HPH_L_DAC_CTL, 0x20 },
+ { WCD939X_HPH_R_DAC_CTL, 0x20 },
+ { WCD939X_HPH_SURGE_COMP_SEL, 0x55 },
+ { WCD939X_HPH_SURGE_EN, 0x19 },
+ { WCD939X_HPH_SURGE_MISC1, 0xa0 },
+ { WCD939X_EAR_EN, 0x22 },
+ { WCD939X_EAR_PA_CON, 0x44 },
+ { WCD939X_EAR_SP_CON, 0xdb },
+ { WCD939X_EAR_DAC_CON, 0x80 },
+ { WCD939X_EAR_CNP_FSM_CON, 0xb2 },
+ { WCD939X_EAR_TEST_CTL, 0x00 },
+ { WCD939X_FLYBACK_NEW_CTRL_2, 0x00 },
+ { WCD939X_FLYBACK_NEW_CTRL_3, 0x00 },
+ { WCD939X_FLYBACK_NEW_CTRL_4, 0x44 },
+ { WCD939X_ANA_NEW_PAGE, 0x00 },
+ { WCD939X_HPH_NEW_ANA_HPH2, 0x00 },
+ { WCD939X_HPH_NEW_ANA_HPH3, 0x00 },
+ { WCD939X_SLEEP_CTL, 0x18 },
+ { WCD939X_SLEEP_WATCHDOG_CTL, 0x00 },
+ { WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 },
+ { WCD939X_MBHC_NEW_CTL_1, 0x02 },
+ { WCD939X_MBHC_NEW_CTL_2, 0x05 },
+ { WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 },
+ { WCD939X_MBHC_NEW_ZDET_ANA_CTL, 0x0f },
+ { WCD939X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 },
+ { WCD939X_TX_NEW_CH12_MUX, 0x11 },
+ { WCD939X_TX_NEW_CH34_MUX, 0x23 },
+ { WCD939X_DIE_CRACK_DET_EN, 0x00 },
+ { WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 },
+ { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L, 0x00 },
+ { WCD939X_HPH_NEW_INT_RDAC_VREF_CTL, 0x08 },
+ { WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+ { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R, 0x00 },
+ { WCD939X_HPH_NEW_INT_PA_MISC1, 0x32 },
+ { WCD939X_HPH_NEW_INT_PA_MISC2, 0x00 },
+ { WCD939X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+ { WCD939X_HPH_NEW_INT_TIMER1, 0xfe },
+ { WCD939X_HPH_NEW_INT_TIMER2, 0x02 },
+ { WCD939X_HPH_NEW_INT_TIMER3, 0x4e },
+ { WCD939X_HPH_NEW_INT_TIMER4, 0x54 },
+ { WCD939X_HPH_NEW_INT_PA_RDAC_MISC2, 0x0b },
+ { WCD939X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+ { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xa0 },
+ { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xa0 },
+ { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x64 },
+ { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 },
+ { WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 },
+ { WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 },
+ { WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 },
+ { WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 },
+ { WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW, 0x47 },
+ { WCD939X_EAR_INT_NEW_CHOPPER_CON, 0xa8 },
+ { WCD939X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 },
+ { WCD939X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 },
+ { WCD939X_EAR_INT_NEW_DYNAMIC_BIAS, 0x00 },
+ { WCD939X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a },
+ { WCD939X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a },
+ { WCD939X_DIE_CRACK_INT_DET_INT1, 0x02 },
+ { WCD939X_DIE_CRACK_INT_DET_INT2, 0x60 },
+ { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2, 0xff },
+ { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1, 0x7f },
+ { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0, 0x3f },
+ { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M, 0x1f },
+ { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M, 0x0f },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1, 0xd7 },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0, 0xc8 },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP, 0xc6 },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1, 0x95 },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0, 0x6a },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, 0x05 },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0, 0xa5 },
+ { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, 0x13 },
+ { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1, 0x88 },
+ { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP, 0x42 },
+ { WCD939X_TX_COM_NEW_INT_ADC_INT_L2, 0xff },
+ { WCD939X_TX_COM_NEW_INT_ADC_INT_L1, 0x64 },
+ { WCD939X_TX_COM_NEW_INT_ADC_INT_L0, 0x64 },
+ { WCD939X_TX_COM_NEW_INT_ADC_INT_ULP, 0x77 },
+ { WCD939X_DIGITAL_PAGE, 0x00 },
+ { WCD939X_DIGITAL_SWR_TX_CLK_RATE, 0x00 },
+ { WCD939X_DIGITAL_CDC_RST_CTL, 0x03 },
+ { WCD939X_DIGITAL_TOP_CLK_CFG, 0x00 },
+ { WCD939X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 },
+ { WCD939X_DIGITAL_CDC_DIG_CLK_CTL, 0xf0 },
+ { WCD939X_DIGITAL_SWR_RST_EN, 0x00 },
+ { WCD939X_DIGITAL_CDC_PATH_MODE, 0x55 },
+ { WCD939X_DIGITAL_CDC_RX_RST, 0x00 },
+ { WCD939X_DIGITAL_CDC_RX0_CTL, 0xfc },
+ { WCD939X_DIGITAL_CDC_RX1_CTL, 0xfc },
+ { WCD939X_DIGITAL_CDC_RX2_CTL, 0xfc },
+ { WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00 },
+ { WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00 },
+ { WCD939X_DIGITAL_CDC_COMP_CTL_0, 0x00 },
+ { WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1e },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R1, 0x4d },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R2, 0x29 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R3, 0x34 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R4, 0x59 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R5, 0x66 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R6, 0x87 },
+ { WCD939X_DIGITAL_CDC_HPH_DSM_R7, 0x64 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A1_0, 0x00 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A1_1, 0x01 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A2_0, 0x96 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A2_1, 0x09 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A3_0, 0xab },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A3_1, 0x05 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A4_0, 0x1c },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A4_1, 0x02 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A5_0, 0x17 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A5_1, 0x02 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A6_0, 0xaa },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_A7_0, 0xe3 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_C_0, 0x69 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_C_1, 0x54 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_C_2, 0x02 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_C_3, 0x15 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R1, 0xa4 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R2, 0xb5 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R3, 0x86 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R4, 0x85 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R5, 0xaa },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R6, 0xe2 },
+ { WCD939X_DIGITAL_CDC_EAR_DSM_R7, 0x62 },
+ { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 },
+ { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 },
+ { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d },
+ { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e },
+ { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 },
+ { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0, 0x00 },
+ { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1, 0xfc },
+ { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2, 0x01 },
+ { WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 },
+ { WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, 0x00 },
+ { WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 },
+ { WCD939X_DIGITAL_CDC_SWR_CLH, 0x00 },
+ { WCD939X_DIGITAL_SWR_CLH_BYP, 0x00 },
+ { WCD939X_DIGITAL_CDC_TX0_CTL, 0x68 },
+ { WCD939X_DIGITAL_CDC_TX1_CTL, 0x68 },
+ { WCD939X_DIGITAL_CDC_TX2_CTL, 0x68 },
+ { WCD939X_DIGITAL_CDC_TX_RST, 0x00 },
+ { WCD939X_DIGITAL_CDC_REQ_CTL, 0x01 },
+ { WCD939X_DIGITAL_CDC_RST, 0x00 },
+ { WCD939X_DIGITAL_CDC_AMIC_CTL, 0x0f },
+ { WCD939X_DIGITAL_CDC_DMIC_CTL, 0x04 },
+ { WCD939X_DIGITAL_CDC_DMIC1_CTL, 0x01 },
+ { WCD939X_DIGITAL_CDC_DMIC2_CTL, 0x01 },
+ { WCD939X_DIGITAL_CDC_DMIC3_CTL, 0x01 },
+ { WCD939X_DIGITAL_CDC_DMIC4_CTL, 0x01 },
+ { WCD939X_DIGITAL_EFUSE_PRG_CTL, 0x00 },
+ { WCD939X_DIGITAL_EFUSE_CTL, 0x2b },
+ { WCD939X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11 },
+ { WCD939X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11 },
+ { WCD939X_DIGITAL_PDM_WD_CTL0, 0x00 },
+ { WCD939X_DIGITAL_PDM_WD_CTL1, 0x00 },
+ { WCD939X_DIGITAL_PDM_WD_CTL2, 0x00 },
+ { WCD939X_DIGITAL_INTR_MODE, 0x00 },
+ { WCD939X_DIGITAL_INTR_MASK_0, 0xff },
+ { WCD939X_DIGITAL_INTR_MASK_1, 0xe7 },
+ { WCD939X_DIGITAL_INTR_MASK_2, 0x0e },
+ { WCD939X_DIGITAL_INTR_CLEAR_0, 0x00 },
+ { WCD939X_DIGITAL_INTR_CLEAR_1, 0x00 },
+ { WCD939X_DIGITAL_INTR_CLEAR_2, 0x00 },
+ { WCD939X_DIGITAL_INTR_LEVEL_0, 0x00 },
+ { WCD939X_DIGITAL_INTR_LEVEL_1, 0x00 },
+ { WCD939X_DIGITAL_INTR_LEVEL_2, 0x00 },
+ { WCD939X_DIGITAL_INTR_SET_0, 0x00 },
+ { WCD939X_DIGITAL_INTR_SET_1, 0x00 },
+ { WCD939X_DIGITAL_INTR_SET_2, 0x00 },
+ { WCD939X_DIGITAL_INTR_TEST_0, 0x00 },
+ { WCD939X_DIGITAL_INTR_TEST_1, 0x00 },
+ { WCD939X_DIGITAL_INTR_TEST_2, 0x00 },
+ { WCD939X_DIGITAL_TX_MODE_DBG_EN, 0x00 },
+ { WCD939X_DIGITAL_TX_MODE_DBG_0_1, 0x00 },
+ { WCD939X_DIGITAL_TX_MODE_DBG_2_3, 0x00 },
+ { WCD939X_DIGITAL_LB_IN_SEL_CTL, 0x00 },
+ { WCD939X_DIGITAL_LOOP_BACK_MODE, 0x00 },
+ { WCD939X_DIGITAL_SWR_DAC_TEST, 0x00 },
+ { WCD939X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 },
+ { WCD939X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 },
+ { WCD939X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 },
+ { WCD939X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 },
+ { WCD939X_DIGITAL_SWR_HM_TEST_TX_2, 0x00 },
+ { WCD939X_DIGITAL_PAD_CTL_SWR_0, 0x8f },
+ { WCD939X_DIGITAL_PAD_CTL_SWR_1, 0x06 },
+ { WCD939X_DIGITAL_I2C_CTL, 0x00 },
+ { WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00 },
+ { WCD939X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 },
+ { WCD939X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 },
+ { WCD939X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 },
+ { WCD939X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 },
+ { WCD939X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 },
+ { WCD939X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 },
+ { WCD939X_DIGITAL_PAD_CTL_PDM_TX2, 0xf1 },
+ { WCD939X_DIGITAL_PAD_INP_DIS_0, 0x00 },
+ { WCD939X_DIGITAL_PAD_INP_DIS_1, 0x00 },
+ { WCD939X_DIGITAL_DRIVE_STRENGTH_0, 0x00 },
+ { WCD939X_DIGITAL_DRIVE_STRENGTH_1, 0x00 },
+ { WCD939X_DIGITAL_DRIVE_STRENGTH_2, 0x00 },
+ { WCD939X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f },
+ { WCD939X_DIGITAL_TX_DATA_EDGE_CTL, 0x80 },
+ { WCD939X_DIGITAL_GPIO_MODE, 0x00 },
+ { WCD939X_DIGITAL_PIN_CTL_OE, 0x00 },
+ { WCD939X_DIGITAL_PIN_CTL_DATA_0, 0x00 },
+ { WCD939X_DIGITAL_PIN_CTL_DATA_1, 0x00 },
+ { WCD939X_DIGITAL_DIG_DEBUG_CTL, 0x00 },
+ { WCD939X_DIGITAL_DIG_DEBUG_EN, 0x00 },
+ { WCD939X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 },
+ { WCD939X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 },
+ { WCD939X_DIGITAL_SSP_DBG, 0x00 },
+ { WCD939X_DIGITAL_SPARE_0, 0x00 },
+ { WCD939X_DIGITAL_SPARE_1, 0x00 },
+ { WCD939X_DIGITAL_SPARE_2, 0x00 },
+ { WCD939X_DIGITAL_TX_REQ_FB_CTL_0, 0x88 },
+ { WCD939X_DIGITAL_TX_REQ_FB_CTL_1, 0x88 },
+ { WCD939X_DIGITAL_TX_REQ_FB_CTL_2, 0x88 },
+ { WCD939X_DIGITAL_TX_REQ_FB_CTL_3, 0x88 },
+ { WCD939X_DIGITAL_TX_REQ_FB_CTL_4, 0x88 },
+ { WCD939X_DIGITAL_DEM_BYPASS_DATA0, 0x55 },
+ { WCD939X_DIGITAL_DEM_BYPASS_DATA1, 0x55 },
+ { WCD939X_DIGITAL_DEM_BYPASS_DATA2, 0x55 },
+ { WCD939X_DIGITAL_DEM_BYPASS_DATA3, 0x01 },
+ { WCD939X_DIGITAL_DEM_SECOND_ORDER, 0x03 },
+ { WCD939X_DIGITAL_DSM_CTRL, 0x00 },
+ { WCD939X_DIGITAL_DSM_0_STATIC_DATA_0, 0x00 },
+ { WCD939X_DIGITAL_DSM_0_STATIC_DATA_1, 0x00 },
+ { WCD939X_DIGITAL_DSM_0_STATIC_DATA_2, 0x00 },
+ { WCD939X_DIGITAL_DSM_0_STATIC_DATA_3, 0x00 },
+ { WCD939X_DIGITAL_DSM_1_STATIC_DATA_0, 0x00 },
+ { WCD939X_DIGITAL_DSM_1_STATIC_DATA_1, 0x00 },
+ { WCD939X_DIGITAL_DSM_1_STATIC_DATA_2, 0x00 },
+ { WCD939X_DIGITAL_DSM_1_STATIC_DATA_3, 0x00 },
+ { WCD939X_RX_TOP_PAGE, 0x00 },
+ { WCD939X_RX_TOP_TOP_CFG0, 0x00 },
+ { WCD939X_RX_TOP_HPHL_COMP_WR_LSB, 0x00 },
+ { WCD939X_RX_TOP_HPHL_COMP_WR_MSB, 0x00 },
+ { WCD939X_RX_TOP_HPHL_COMP_LUT, 0x00 },
+ { WCD939X_RX_TOP_HPHR_COMP_WR_LSB, 0x00 },
+ { WCD939X_RX_TOP_HPHR_COMP_WR_MSB, 0x00 },
+ { WCD939X_RX_TOP_HPHR_COMP_LUT, 0x00 },
+ { WCD939X_RX_TOP_DSD0_DEBUG_CFG1, 0x05 },
+ { WCD939X_RX_TOP_DSD0_DEBUG_CFG2, 0x08 },
+ { WCD939X_RX_TOP_DSD0_DEBUG_CFG3, 0x00 },
+ { WCD939X_RX_TOP_DSD0_DEBUG_CFG4, 0x00 },
+ { WCD939X_RX_TOP_DSD1_DEBUG_CFG1, 0x03 },
+ { WCD939X_RX_TOP_DSD1_DEBUG_CFG2, 0x08 },
+ { WCD939X_RX_TOP_DSD1_DEBUG_CFG3, 0x00 },
+ { WCD939X_RX_TOP_DSD1_DEBUG_CFG4, 0x00 },
+ { WCD939X_RX_TOP_HPHL_PATH_CFG0, 0x00 },
+ { WCD939X_RX_TOP_HPHL_PATH_CFG1, 0x00 },
+ { WCD939X_RX_TOP_HPHR_PATH_CFG0, 0x00 },
+ { WCD939X_RX_TOP_HPHR_PATH_CFG1, 0x00 },
+ { WCD939X_RX_TOP_PATH_CFG2, 0x00 },
+ { WCD939X_RX_TOP_HPHL_PATH_SEC0, 0x00 },
+ { WCD939X_RX_TOP_HPHL_PATH_SEC1, 0x00 },
+ { WCD939X_RX_TOP_HPHL_PATH_SEC2, 0x00 },
+ { WCD939X_RX_TOP_HPHL_PATH_SEC3, 0x00 },
+ { WCD939X_RX_TOP_HPHR_PATH_SEC0, 0x00 },
+ { WCD939X_RX_TOP_HPHR_PATH_SEC1, 0x00 },
+ { WCD939X_RX_TOP_HPHR_PATH_SEC2, 0x00 },
+ { WCD939X_RX_TOP_HPHR_PATH_SEC3, 0x00 },
+ { WCD939X_RX_TOP_PATH_SEC4, 0x00 },
+ { WCD939X_RX_TOP_PATH_SEC5, 0x00 },
+ { WCD939X_COMPANDER_HPHL_CTL0, 0x60 },
+ { WCD939X_COMPANDER_HPHL_CTL1, 0xdb },
+ { WCD939X_COMPANDER_HPHL_CTL2, 0xff },
+ { WCD939X_COMPANDER_HPHL_CTL3, 0x35 },
+ { WCD939X_COMPANDER_HPHL_CTL4, 0xff },
+ { WCD939X_COMPANDER_HPHL_CTL5, 0x00 },
+ { WCD939X_COMPANDER_HPHL_CTL7, 0x08 },
+ { WCD939X_COMPANDER_HPHL_CTL8, 0x00 },
+ { WCD939X_COMPANDER_HPHL_CTL9, 0x00 },
+ { WCD939X_COMPANDER_HPHL_CTL10, 0x06 },
+ { WCD939X_COMPANDER_HPHL_CTL11, 0x12 },
+ { WCD939X_COMPANDER_HPHL_CTL12, 0x1e },
+ { WCD939X_COMPANDER_HPHL_CTL13, 0x2a },
+ { WCD939X_COMPANDER_HPHL_CTL14, 0x36 },
+ { WCD939X_COMPANDER_HPHL_CTL15, 0x3c },
+ { WCD939X_COMPANDER_HPHL_CTL16, 0xc4 },
+ { WCD939X_COMPANDER_HPHL_CTL17, 0x00 },
+ { WCD939X_COMPANDER_HPHL_CTL18, 0x0c },
+ { WCD939X_COMPANDER_HPHL_CTL19, 0x16 },
+ { WCD939X_R_CTL0, 0x60 },
+ { WCD939X_R_CTL1, 0xdb },
+ { WCD939X_R_CTL2, 0xff },
+ { WCD939X_R_CTL3, 0x35 },
+ { WCD939X_R_CTL4, 0xff },
+ { WCD939X_R_CTL5, 0x00 },
+ { WCD939X_R_CTL7, 0x08 },
+ { WCD939X_R_CTL8, 0x00 },
+ { WCD939X_R_CTL9, 0x00 },
+ { WCD939X_R_CTL10, 0x06 },
+ { WCD939X_R_CTL11, 0x12 },
+ { WCD939X_R_CTL12, 0x1e },
+ { WCD939X_R_CTL13, 0x2a },
+ { WCD939X_R_CTL14, 0x36 },
+ { WCD939X_R_CTL15, 0x3c },
+ { WCD939X_R_CTL16, 0xc4 },
+ { WCD939X_R_CTL17, 0x00 },
+ { WCD939X_R_CTL18, 0x0c },
+ { WCD939X_R_CTL19, 0x16 },
+ { WCD939X_E_PATH_CTL, 0x00 },
+ { WCD939X_E_CFG0, 0x07 },
+ { WCD939X_E_CFG1, 0x3c },
+ { WCD939X_E_CFG2, 0x00 },
+ { WCD939X_E_CFG3, 0x00 },
+ { WCD939X_DSD_HPHL_PATH_CTL, 0x00 },
+ { WCD939X_DSD_HPHL_CFG0, 0x00 },
+ { WCD939X_DSD_HPHL_CFG1, 0x00 },
+ { WCD939X_DSD_HPHL_CFG2, 0x22 },
+ { WCD939X_DSD_HPHL_CFG3, 0x00 },
+ { WCD939X_DSD_HPHL_CFG4, 0x1a },
+ { WCD939X_DSD_HPHL_CFG5, 0x00 },
+ { WCD939X_DSD_HPHR_PATH_CTL, 0x00 },
+ { WCD939X_DSD_HPHR_CFG0, 0x00 },
+ { WCD939X_DSD_HPHR_CFG1, 0x00 },
+ { WCD939X_DSD_HPHR_CFG2, 0x22 },
+ { WCD939X_DSD_HPHR_CFG3, 0x00 },
+ { WCD939X_DSD_HPHR_CFG4, 0x1a },
+ { WCD939X_DSD_HPHR_CFG5, 0x00 },
+};
+
+static bool wcd939x_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD939X_ANA_PAGE:
+ case WCD939X_ANA_BIAS:
+ case WCD939X_ANA_RX_SUPPLIES:
+ case WCD939X_ANA_HPH:
+ case WCD939X_ANA_EAR:
+ case WCD939X_ANA_EAR_COMPANDER_CTL:
+ case WCD939X_ANA_TX_CH1:
+ case WCD939X_ANA_TX_CH2:
+ case WCD939X_ANA_TX_CH3:
+ case WCD939X_ANA_TX_CH4:
+ case WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC:
+ case WCD939X_ANA_MICB3_DSP_EN_LOGIC:
+ case WCD939X_ANA_MBHC_MECH:
+ case WCD939X_ANA_MBHC_ELECT:
+ case WCD939X_ANA_MBHC_ZDET:
+ case WCD939X_ANA_MBHC_BTN0:
+ case WCD939X_ANA_MBHC_BTN1:
+ case WCD939X_ANA_MBHC_BTN2:
+ case WCD939X_ANA_MBHC_BTN3:
+ case WCD939X_ANA_MBHC_BTN4:
+ case WCD939X_ANA_MBHC_BTN5:
+ case WCD939X_ANA_MBHC_BTN6:
+ case WCD939X_ANA_MBHC_BTN7:
+ case WCD939X_ANA_MICB1:
+ case WCD939X_ANA_MICB2:
+ case WCD939X_ANA_MICB2_RAMP:
+ case WCD939X_ANA_MICB3:
+ case WCD939X_ANA_MICB4:
+ case WCD939X_BIAS_CTL:
+ case WCD939X_BIAS_VBG_FINE_ADJ:
+ case WCD939X_LDOL_VDDCX_ADJUST:
+ case WCD939X_LDOL_DISABLE_LDOL:
+ case WCD939X_MBHC_CTL_CLK:
+ case WCD939X_MBHC_CTL_ANA:
+ case WCD939X_MBHC_ZDET_VNEG_CTL:
+ case WCD939X_MBHC_ZDET_BIAS_CTL:
+ case WCD939X_MBHC_CTL_BCS:
+ case WCD939X_MBHC_TEST_CTL:
+ case WCD939X_LDOH_MODE:
+ case WCD939X_LDOH_BIAS:
+ case WCD939X_LDOH_STB_LOADS:
+ case WCD939X_LDOH_SLOWRAMP:
+ case WCD939X_MICB1_TEST_CTL_1:
+ case WCD939X_MICB1_TEST_CTL_2:
+ case WCD939X_MICB1_TEST_CTL_3:
+ case WCD939X_MICB2_TEST_CTL_1:
+ case WCD939X_MICB2_TEST_CTL_2:
+ case WCD939X_MICB2_TEST_CTL_3:
+ case WCD939X_MICB3_TEST_CTL_1:
+ case WCD939X_MICB3_TEST_CTL_2:
+ case WCD939X_MICB3_TEST_CTL_3:
+ case WCD939X_MICB4_TEST_CTL_1:
+ case WCD939X_MICB4_TEST_CTL_2:
+ case WCD939X_MICB4_TEST_CTL_3:
+ case WCD939X_TX_COM_ADC_VCM:
+ case WCD939X_TX_COM_BIAS_ATEST:
+ case WCD939X_TX_COM_SPARE1:
+ case WCD939X_TX_COM_SPARE2:
+ case WCD939X_TX_COM_TXFE_DIV_CTL:
+ case WCD939X_TX_COM_TXFE_DIV_START:
+ case WCD939X_TX_COM_SPARE3:
+ case WCD939X_TX_COM_SPARE4:
+ case WCD939X_TX_1_2_TEST_EN:
+ case WCD939X_TX_1_2_ADC_IB:
+ case WCD939X_TX_1_2_ATEST_REFCTL:
+ case WCD939X_TX_1_2_TEST_CTL:
+ case WCD939X_TX_1_2_TEST_BLK_EN1:
+ case WCD939X_TX_1_2_TXFE1_CLKDIV:
+ case WCD939X_TX_3_4_TEST_EN:
+ case WCD939X_TX_3_4_ADC_IB:
+ case WCD939X_TX_3_4_ATEST_REFCTL:
+ case WCD939X_TX_3_4_TEST_CTL:
+ case WCD939X_TX_3_4_TEST_BLK_EN3:
+ case WCD939X_TX_3_4_TXFE3_CLKDIV:
+ case WCD939X_TX_3_4_TEST_BLK_EN2:
+ case WCD939X_TX_3_4_TXFE2_CLKDIV:
+ case WCD939X_TX_3_4_SPARE1:
+ case WCD939X_TX_3_4_TEST_BLK_EN4:
+ case WCD939X_TX_3_4_TXFE4_CLKDIV:
+ case WCD939X_TX_3_4_SPARE2:
+ case WCD939X_CLASSH_MODE_1:
+ case WCD939X_CLASSH_MODE_2:
+ case WCD939X_CLASSH_MODE_3:
+ case WCD939X_CLASSH_CTRL_VCL_1:
+ case WCD939X_CLASSH_CTRL_VCL_2:
+ case WCD939X_CLASSH_CTRL_CCL_1:
+ case WCD939X_CLASSH_CTRL_CCL_2:
+ case WCD939X_CLASSH_CTRL_CCL_3:
+ case WCD939X_CLASSH_CTRL_CCL_4:
+ case WCD939X_CLASSH_CTRL_CCL_5:
+ case WCD939X_CLASSH_BUCK_TMUX_A_D:
+ case WCD939X_CLASSH_BUCK_SW_DRV_CNTL:
+ case WCD939X_CLASSH_SPARE:
+ case WCD939X_FLYBACK_EN:
+ case WCD939X_FLYBACK_VNEG_CTRL_1:
+ case WCD939X_FLYBACK_VNEG_CTRL_2:
+ case WCD939X_FLYBACK_VNEG_CTRL_3:
+ case WCD939X_FLYBACK_VNEG_CTRL_4:
+ case WCD939X_FLYBACK_VNEG_CTRL_5:
+ case WCD939X_FLYBACK_VNEG_CTRL_6:
+ case WCD939X_FLYBACK_VNEG_CTRL_7:
+ case WCD939X_FLYBACK_VNEG_CTRL_8:
+ case WCD939X_FLYBACK_VNEG_CTRL_9:
+ case WCD939X_FLYBACK_VNEGDAC_CTRL_1:
+ case WCD939X_FLYBACK_VNEGDAC_CTRL_2:
+ case WCD939X_FLYBACK_VNEGDAC_CTRL_3:
+ case WCD939X_FLYBACK_CTRL_1:
+ case WCD939X_FLYBACK_TEST_CTL:
+ case WCD939X_RX_AUX_SW_CTL:
+ case WCD939X_RX_PA_AUX_IN_CONN:
+ case WCD939X_RX_TIMER_DIV:
+ case WCD939X_RX_OCP_CTL:
+ case WCD939X_RX_OCP_COUNT:
+ case WCD939X_RX_BIAS_EAR_DAC:
+ case WCD939X_RX_BIAS_EAR_AMP:
+ case WCD939X_RX_BIAS_HPH_LDO:
+ case WCD939X_RX_BIAS_HPH_PA:
+ case WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2:
+ case WCD939X_RX_BIAS_HPH_RDAC_LDO:
+ case WCD939X_RX_BIAS_HPH_CNP1:
+ case WCD939X_RX_BIAS_HPH_LOWPOWER:
+ case WCD939X_RX_BIAS_AUX_DAC:
+ case WCD939X_RX_BIAS_AUX_AMP:
+ case WCD939X_RX_BIAS_VNEGDAC_BLEEDER:
+ case WCD939X_RX_BIAS_MISC:
+ case WCD939X_RX_BIAS_BUCK_RST:
+ case WCD939X_RX_BIAS_BUCK_VREF_ERRAMP:
+ case WCD939X_RX_BIAS_FLYB_ERRAMP:
+ case WCD939X_RX_BIAS_FLYB_BUFF:
+ case WCD939X_RX_BIAS_FLYB_MID_RST:
+ case WCD939X_HPH_CNP_EN:
+ case WCD939X_HPH_CNP_WG_CTL:
+ case WCD939X_HPH_CNP_WG_TIME:
+ case WCD939X_HPH_OCP_CTL:
+ case WCD939X_HPH_AUTO_CHOP:
+ case WCD939X_HPH_CHOP_CTL:
+ case WCD939X_HPH_PA_CTL1:
+ case WCD939X_HPH_PA_CTL2:
+ case WCD939X_HPH_L_EN:
+ case WCD939X_HPH_L_TEST:
+ case WCD939X_HPH_L_ATEST:
+ case WCD939X_HPH_R_EN:
+ case WCD939X_HPH_R_TEST:
+ case WCD939X_HPH_R_ATEST:
+ case WCD939X_HPH_RDAC_CLK_CTL1:
+ case WCD939X_HPH_RDAC_CLK_CTL2:
+ case WCD939X_HPH_RDAC_LDO_CTL:
+ case WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL:
+ case WCD939X_HPH_REFBUFF_UHQA_CTL:
+ case WCD939X_HPH_REFBUFF_LP_CTL:
+ case WCD939X_HPH_L_DAC_CTL:
+ case WCD939X_HPH_R_DAC_CTL:
+ case WCD939X_HPH_SURGE_COMP_SEL:
+ case WCD939X_HPH_SURGE_EN:
+ case WCD939X_HPH_SURGE_MISC1:
+ case WCD939X_EAR_EN:
+ case WCD939X_EAR_PA_CON:
+ case WCD939X_EAR_SP_CON:
+ case WCD939X_EAR_DAC_CON:
+ case WCD939X_EAR_CNP_FSM_CON:
+ case WCD939X_EAR_TEST_CTL:
+ case WCD939X_FLYBACK_NEW_CTRL_2:
+ case WCD939X_FLYBACK_NEW_CTRL_3:
+ case WCD939X_FLYBACK_NEW_CTRL_4:
+ case WCD939X_ANA_NEW_PAGE:
+ case WCD939X_HPH_NEW_ANA_HPH2:
+ case WCD939X_HPH_NEW_ANA_HPH3:
+ case WCD939X_SLEEP_CTL:
+ case WCD939X_SLEEP_WATCHDOG_CTL:
+ case WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL:
+ case WCD939X_MBHC_NEW_CTL_1:
+ case WCD939X_MBHC_NEW_CTL_2:
+ case WCD939X_MBHC_NEW_PLUG_DETECT_CTL:
+ case WCD939X_MBHC_NEW_ZDET_ANA_CTL:
+ case WCD939X_MBHC_NEW_ZDET_RAMP_CTL:
+ case WCD939X_TX_NEW_CH12_MUX:
+ case WCD939X_TX_NEW_CH34_MUX:
+ case WCD939X_DIE_CRACK_DET_EN:
+ case WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL:
+ case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L:
+ case WCD939X_HPH_NEW_INT_RDAC_VREF_CTL:
+ case WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL:
+ case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R:
+ case WCD939X_HPH_NEW_INT_PA_MISC1:
+ case WCD939X_HPH_NEW_INT_PA_MISC2:
+ case WCD939X_HPH_NEW_INT_PA_RDAC_MISC:
+ case WCD939X_HPH_NEW_INT_TIMER1:
+ case WCD939X_HPH_NEW_INT_TIMER2:
+ case WCD939X_HPH_NEW_INT_TIMER3:
+ case WCD939X_HPH_NEW_INT_TIMER4:
+ case WCD939X_HPH_NEW_INT_PA_RDAC_MISC2:
+ case WCD939X_HPH_NEW_INT_PA_RDAC_MISC3:
+ case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L:
+ case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R:
+ case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI:
+ case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP:
+ case WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP:
+ case WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL:
+ case WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL:
+ case WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT:
+ case WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW:
+ case WCD939X_EAR_INT_NEW_CHOPPER_CON:
+ case WCD939X_EAR_INT_NEW_CNP_VCM_CON1:
+ case WCD939X_EAR_INT_NEW_CNP_VCM_CON2:
+ case WCD939X_EAR_INT_NEW_DYNAMIC_BIAS:
+ case WCD939X_SLEEP_INT_WATCHDOG_CTL_1:
+ case WCD939X_SLEEP_INT_WATCHDOG_CTL_2:
+ case WCD939X_DIE_CRACK_INT_DET_INT1:
+ case WCD939X_DIE_CRACK_INT_DET_INT2:
+ case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2:
+ case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1:
+ case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0:
+ case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M:
+ case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0:
+ case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP:
+ case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1:
+ case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP:
+ case WCD939X_TX_COM_NEW_INT_ADC_INT_L2:
+ case WCD939X_TX_COM_NEW_INT_ADC_INT_L1:
+ case WCD939X_TX_COM_NEW_INT_ADC_INT_L0:
+ case WCD939X_TX_COM_NEW_INT_ADC_INT_ULP:
+ case WCD939X_DIGITAL_PAGE:
+ case WCD939X_DIGITAL_SWR_TX_CLK_RATE:
+ case WCD939X_DIGITAL_CDC_RST_CTL:
+ case WCD939X_DIGITAL_TOP_CLK_CFG:
+ case WCD939X_DIGITAL_CDC_ANA_CLK_CTL:
+ case WCD939X_DIGITAL_CDC_DIG_CLK_CTL:
+ case WCD939X_DIGITAL_SWR_RST_EN:
+ case WCD939X_DIGITAL_CDC_PATH_MODE:
+ case WCD939X_DIGITAL_CDC_RX_RST:
+ case WCD939X_DIGITAL_CDC_RX0_CTL:
+ case WCD939X_DIGITAL_CDC_RX1_CTL:
+ case WCD939X_DIGITAL_CDC_RX2_CTL:
+ case WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1:
+ case WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3:
+ case WCD939X_DIGITAL_CDC_COMP_CTL_0:
+ case WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A1_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A1_1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A2_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A2_1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A3_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A3_1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A4_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A4_1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A5_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A5_1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A6_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_A7_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_C_0:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_C_1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_C_2:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_C_3:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R1:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R2:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R3:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R4:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R5:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R6:
+ case WCD939X_DIGITAL_CDC_HPH_DSM_R7:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A1_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A1_1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A2_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A2_1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A3_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A3_1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A4_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A4_1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A5_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A5_1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A6_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_A7_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_C_0:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_C_1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_C_2:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_C_3:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R1:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R2:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R3:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R4:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R5:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R6:
+ case WCD939X_DIGITAL_CDC_EAR_DSM_R7:
+ case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0:
+ case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1:
+ case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0:
+ case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1:
+ case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2:
+ case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0:
+ case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1:
+ case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2:
+ case WCD939X_DIGITAL_CDC_HPH_GAIN_CTL:
+ case WCD939X_DIGITAL_CDC_EAR_GAIN_CTL:
+ case WCD939X_DIGITAL_CDC_EAR_PATH_CTL:
+ case WCD939X_DIGITAL_CDC_SWR_CLH:
+ case WCD939X_DIGITAL_SWR_CLH_BYP:
+ case WCD939X_DIGITAL_CDC_TX0_CTL:
+ case WCD939X_DIGITAL_CDC_TX1_CTL:
+ case WCD939X_DIGITAL_CDC_TX2_CTL:
+ case WCD939X_DIGITAL_CDC_TX_RST:
+ case WCD939X_DIGITAL_CDC_REQ_CTL:
+ case WCD939X_DIGITAL_CDC_RST:
+ case WCD939X_DIGITAL_CDC_AMIC_CTL:
+ case WCD939X_DIGITAL_CDC_DMIC_CTL:
+ case WCD939X_DIGITAL_CDC_DMIC1_CTL:
+ case WCD939X_DIGITAL_CDC_DMIC2_CTL:
+ case WCD939X_DIGITAL_CDC_DMIC3_CTL:
+ case WCD939X_DIGITAL_CDC_DMIC4_CTL:
+ case WCD939X_DIGITAL_EFUSE_PRG_CTL:
+ case WCD939X_DIGITAL_EFUSE_CTL:
+ case WCD939X_DIGITAL_CDC_DMIC_RATE_1_2:
+ case WCD939X_DIGITAL_CDC_DMIC_RATE_3_4:
+ case WCD939X_DIGITAL_PDM_WD_CTL0:
+ case WCD939X_DIGITAL_PDM_WD_CTL1:
+ case WCD939X_DIGITAL_PDM_WD_CTL2:
+ case WCD939X_DIGITAL_INTR_MODE:
+ case WCD939X_DIGITAL_INTR_MASK_0:
+ case WCD939X_DIGITAL_INTR_MASK_1:
+ case WCD939X_DIGITAL_INTR_MASK_2:
+ case WCD939X_DIGITAL_INTR_CLEAR_0:
+ case WCD939X_DIGITAL_INTR_CLEAR_1:
+ case WCD939X_DIGITAL_INTR_CLEAR_2:
+ case WCD939X_DIGITAL_INTR_LEVEL_0:
+ case WCD939X_DIGITAL_INTR_LEVEL_1:
+ case WCD939X_DIGITAL_INTR_LEVEL_2:
+ case WCD939X_DIGITAL_INTR_SET_0:
+ case WCD939X_DIGITAL_INTR_SET_1:
+ case WCD939X_DIGITAL_INTR_SET_2:
+ case WCD939X_DIGITAL_INTR_TEST_0:
+ case WCD939X_DIGITAL_INTR_TEST_1:
+ case WCD939X_DIGITAL_INTR_TEST_2:
+ case WCD939X_DIGITAL_TX_MODE_DBG_EN:
+ case WCD939X_DIGITAL_TX_MODE_DBG_0_1:
+ case WCD939X_DIGITAL_TX_MODE_DBG_2_3:
+ case WCD939X_DIGITAL_LB_IN_SEL_CTL:
+ case WCD939X_DIGITAL_LOOP_BACK_MODE:
+ case WCD939X_DIGITAL_SWR_DAC_TEST:
+ case WCD939X_DIGITAL_SWR_HM_TEST_RX_0:
+ case WCD939X_DIGITAL_SWR_HM_TEST_TX_0:
+ case WCD939X_DIGITAL_SWR_HM_TEST_RX_1:
+ case WCD939X_DIGITAL_SWR_HM_TEST_TX_1:
+ case WCD939X_DIGITAL_SWR_HM_TEST_TX_2:
+ case WCD939X_DIGITAL_PAD_CTL_SWR_0:
+ case WCD939X_DIGITAL_PAD_CTL_SWR_1:
+ case WCD939X_DIGITAL_I2C_CTL:
+ case WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE:
+ case WCD939X_DIGITAL_EFUSE_TEST_CTL_0:
+ case WCD939X_DIGITAL_EFUSE_TEST_CTL_1:
+ case WCD939X_DIGITAL_PAD_CTL_PDM_RX0:
+ case WCD939X_DIGITAL_PAD_CTL_PDM_RX1:
+ case WCD939X_DIGITAL_PAD_CTL_PDM_TX0:
+ case WCD939X_DIGITAL_PAD_CTL_PDM_TX1:
+ case WCD939X_DIGITAL_PAD_CTL_PDM_TX2:
+ case WCD939X_DIGITAL_PAD_INP_DIS_0:
+ case WCD939X_DIGITAL_PAD_INP_DIS_1:
+ case WCD939X_DIGITAL_DRIVE_STRENGTH_0:
+ case WCD939X_DIGITAL_DRIVE_STRENGTH_1:
+ case WCD939X_DIGITAL_DRIVE_STRENGTH_2:
+ case WCD939X_DIGITAL_RX_DATA_EDGE_CTL:
+ case WCD939X_DIGITAL_TX_DATA_EDGE_CTL:
+ case WCD939X_DIGITAL_GPIO_MODE:
+ case WCD939X_DIGITAL_PIN_CTL_OE:
+ case WCD939X_DIGITAL_PIN_CTL_DATA_0:
+ case WCD939X_DIGITAL_PIN_CTL_DATA_1:
+ case WCD939X_DIGITAL_DIG_DEBUG_CTL:
+ case WCD939X_DIGITAL_DIG_DEBUG_EN:
+ case WCD939X_DIGITAL_ANA_CSR_DBG_ADD:
+ case WCD939X_DIGITAL_ANA_CSR_DBG_CTL:
+ case WCD939X_DIGITAL_SSP_DBG:
+ case WCD939X_DIGITAL_SPARE_0:
+ case WCD939X_DIGITAL_SPARE_1:
+ case WCD939X_DIGITAL_SPARE_2:
+ case WCD939X_DIGITAL_TX_REQ_FB_CTL_0:
+ case WCD939X_DIGITAL_TX_REQ_FB_CTL_1:
+ case WCD939X_DIGITAL_TX_REQ_FB_CTL_2:
+ case WCD939X_DIGITAL_TX_REQ_FB_CTL_3:
+ case WCD939X_DIGITAL_TX_REQ_FB_CTL_4:
+ case WCD939X_DIGITAL_DEM_BYPASS_DATA0:
+ case WCD939X_DIGITAL_DEM_BYPASS_DATA1:
+ case WCD939X_DIGITAL_DEM_BYPASS_DATA2:
+ case WCD939X_DIGITAL_DEM_BYPASS_DATA3:
+ case WCD939X_DIGITAL_DEM_SECOND_ORDER:
+ case WCD939X_DIGITAL_DSM_CTRL:
+ case WCD939X_DIGITAL_DSM_0_STATIC_DATA_0:
+ case WCD939X_DIGITAL_DSM_0_STATIC_DATA_1:
+ case WCD939X_DIGITAL_DSM_0_STATIC_DATA_2:
+ case WCD939X_DIGITAL_DSM_0_STATIC_DATA_3:
+ case WCD939X_DIGITAL_DSM_1_STATIC_DATA_0:
+ case WCD939X_DIGITAL_DSM_1_STATIC_DATA_1:
+ case WCD939X_DIGITAL_DSM_1_STATIC_DATA_2:
+ case WCD939X_DIGITAL_DSM_1_STATIC_DATA_3:
+ case WCD939X_RX_TOP_PAGE:
+ case WCD939X_RX_TOP_TOP_CFG0:
+ case WCD939X_RX_TOP_HPHL_COMP_WR_LSB:
+ case WCD939X_RX_TOP_HPHL_COMP_WR_MSB:
+ case WCD939X_RX_TOP_HPHL_COMP_LUT:
+ case WCD939X_RX_TOP_HPHR_COMP_WR_LSB:
+ case WCD939X_RX_TOP_HPHR_COMP_WR_MSB:
+ case WCD939X_RX_TOP_HPHR_COMP_LUT:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG1:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG2:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG3:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG4:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG1:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG2:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG3:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG4:
+ case WCD939X_RX_TOP_HPHL_PATH_CFG0:
+ case WCD939X_RX_TOP_HPHL_PATH_CFG1:
+ case WCD939X_RX_TOP_HPHR_PATH_CFG0:
+ case WCD939X_RX_TOP_HPHR_PATH_CFG1:
+ case WCD939X_RX_TOP_PATH_CFG2:
+ case WCD939X_RX_TOP_HPHL_PATH_SEC0:
+ case WCD939X_RX_TOP_HPHL_PATH_SEC1:
+ case WCD939X_RX_TOP_HPHL_PATH_SEC2:
+ case WCD939X_RX_TOP_HPHL_PATH_SEC3:
+ case WCD939X_RX_TOP_HPHR_PATH_SEC0:
+ case WCD939X_RX_TOP_HPHR_PATH_SEC1:
+ case WCD939X_RX_TOP_HPHR_PATH_SEC2:
+ case WCD939X_RX_TOP_HPHR_PATH_SEC3:
+ case WCD939X_RX_TOP_PATH_SEC4:
+ case WCD939X_RX_TOP_PATH_SEC5:
+ case WCD939X_COMPANDER_HPHL_CTL0:
+ case WCD939X_COMPANDER_HPHL_CTL1:
+ case WCD939X_COMPANDER_HPHL_CTL2:
+ case WCD939X_COMPANDER_HPHL_CTL3:
+ case WCD939X_COMPANDER_HPHL_CTL4:
+ case WCD939X_COMPANDER_HPHL_CTL5:
+ case WCD939X_COMPANDER_HPHL_CTL7:
+ case WCD939X_COMPANDER_HPHL_CTL8:
+ case WCD939X_COMPANDER_HPHL_CTL9:
+ case WCD939X_COMPANDER_HPHL_CTL10:
+ case WCD939X_COMPANDER_HPHL_CTL11:
+ case WCD939X_COMPANDER_HPHL_CTL12:
+ case WCD939X_COMPANDER_HPHL_CTL13:
+ case WCD939X_COMPANDER_HPHL_CTL14:
+ case WCD939X_COMPANDER_HPHL_CTL15:
+ case WCD939X_COMPANDER_HPHL_CTL16:
+ case WCD939X_COMPANDER_HPHL_CTL17:
+ case WCD939X_COMPANDER_HPHL_CTL18:
+ case WCD939X_COMPANDER_HPHL_CTL19:
+ case WCD939X_R_CTL0:
+ case WCD939X_R_CTL1:
+ case WCD939X_R_CTL2:
+ case WCD939X_R_CTL3:
+ case WCD939X_R_CTL4:
+ case WCD939X_R_CTL5:
+ case WCD939X_R_CTL7:
+ case WCD939X_R_CTL8:
+ case WCD939X_R_CTL9:
+ case WCD939X_R_CTL10:
+ case WCD939X_R_CTL11:
+ case WCD939X_R_CTL12:
+ case WCD939X_R_CTL13:
+ case WCD939X_R_CTL14:
+ case WCD939X_R_CTL15:
+ case WCD939X_R_CTL16:
+ case WCD939X_R_CTL17:
+ case WCD939X_R_CTL18:
+ case WCD939X_R_CTL19:
+ case WCD939X_E_PATH_CTL:
+ case WCD939X_E_CFG0:
+ case WCD939X_E_CFG1:
+ case WCD939X_E_CFG2:
+ case WCD939X_E_CFG3:
+ case WCD939X_DSD_HPHL_PATH_CTL:
+ case WCD939X_DSD_HPHL_CFG0:
+ case WCD939X_DSD_HPHL_CFG1:
+ case WCD939X_DSD_HPHL_CFG2:
+ case WCD939X_DSD_HPHL_CFG3:
+ case WCD939X_DSD_HPHL_CFG4:
+ case WCD939X_DSD_HPHL_CFG5:
+ case WCD939X_DSD_HPHR_PATH_CTL:
+ case WCD939X_DSD_HPHR_CFG0:
+ case WCD939X_DSD_HPHR_CFG1:
+ case WCD939X_DSD_HPHR_CFG2:
+ case WCD939X_DSD_HPHR_CFG3:
+ case WCD939X_DSD_HPHR_CFG4:
+ case WCD939X_DSD_HPHR_CFG5:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wcd939x_readable_register(struct device *dev, unsigned int reg)
+{
+ /* Read-Only Registers */
+ switch (reg) {
+ case WCD939X_ANA_MBHC_RESULT_1:
+ case WCD939X_ANA_MBHC_RESULT_2:
+ case WCD939X_ANA_MBHC_RESULT_3:
+ case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS:
+ case WCD939X_TX_1_2_SAR2_ERR:
+ case WCD939X_TX_1_2_SAR1_ERR:
+ case WCD939X_TX_3_4_SAR4_ERR:
+ case WCD939X_TX_3_4_SAR3_ERR:
+ case WCD939X_HPH_L_STATUS:
+ case WCD939X_HPH_R_STATUS:
+ case WCD939X_HPH_SURGE_STATUS:
+ case WCD939X_EAR_STATUS_REG_1:
+ case WCD939X_EAR_STATUS_REG_2:
+ case WCD939X_MBHC_NEW_FSM_STATUS:
+ case WCD939X_MBHC_NEW_ADC_RESULT:
+ case WCD939X_DIE_CRACK_DET_OUT:
+ case WCD939X_DIGITAL_CHIP_ID0:
+ case WCD939X_DIGITAL_CHIP_ID1:
+ case WCD939X_DIGITAL_CHIP_ID2:
+ case WCD939X_DIGITAL_CHIP_ID3:
+ case WCD939X_DIGITAL_INTR_STATUS_0:
+ case WCD939X_DIGITAL_INTR_STATUS_1:
+ case WCD939X_DIGITAL_INTR_STATUS_2:
+ case WCD939X_DIGITAL_SWR_HM_TEST_0:
+ case WCD939X_DIGITAL_SWR_HM_TEST_1:
+ case WCD939X_DIGITAL_EFUSE_T_DATA_0:
+ case WCD939X_DIGITAL_EFUSE_T_DATA_1:
+ case WCD939X_DIGITAL_PIN_STATUS_0:
+ case WCD939X_DIGITAL_PIN_STATUS_1:
+ case WCD939X_DIGITAL_MODE_STATUS_0:
+ case WCD939X_DIGITAL_MODE_STATUS_1:
+ case WCD939X_DIGITAL_EFUSE_REG_0:
+ case WCD939X_DIGITAL_EFUSE_REG_1:
+ case WCD939X_DIGITAL_EFUSE_REG_2:
+ case WCD939X_DIGITAL_EFUSE_REG_3:
+ case WCD939X_DIGITAL_EFUSE_REG_4:
+ case WCD939X_DIGITAL_EFUSE_REG_5:
+ case WCD939X_DIGITAL_EFUSE_REG_6:
+ case WCD939X_DIGITAL_EFUSE_REG_7:
+ case WCD939X_DIGITAL_EFUSE_REG_8:
+ case WCD939X_DIGITAL_EFUSE_REG_9:
+ case WCD939X_DIGITAL_EFUSE_REG_10:
+ case WCD939X_DIGITAL_EFUSE_REG_11:
+ case WCD939X_DIGITAL_EFUSE_REG_12:
+ case WCD939X_DIGITAL_EFUSE_REG_13:
+ case WCD939X_DIGITAL_EFUSE_REG_14:
+ case WCD939X_DIGITAL_EFUSE_REG_15:
+ case WCD939X_DIGITAL_EFUSE_REG_16:
+ case WCD939X_DIGITAL_EFUSE_REG_17:
+ case WCD939X_DIGITAL_EFUSE_REG_18:
+ case WCD939X_DIGITAL_EFUSE_REG_19:
+ case WCD939X_DIGITAL_EFUSE_REG_20:
+ case WCD939X_DIGITAL_EFUSE_REG_21:
+ case WCD939X_DIGITAL_EFUSE_REG_22:
+ case WCD939X_DIGITAL_EFUSE_REG_23:
+ case WCD939X_DIGITAL_EFUSE_REG_24:
+ case WCD939X_DIGITAL_EFUSE_REG_25:
+ case WCD939X_DIGITAL_EFUSE_REG_26:
+ case WCD939X_DIGITAL_EFUSE_REG_27:
+ case WCD939X_DIGITAL_EFUSE_REG_28:
+ case WCD939X_DIGITAL_EFUSE_REG_29:
+ case WCD939X_DIGITAL_EFUSE_REG_30:
+ case WCD939X_DIGITAL_EFUSE_REG_31:
+ case WCD939X_RX_TOP_HPHL_COMP_RD_LSB:
+ case WCD939X_RX_TOP_HPHL_COMP_RD_MSB:
+ case WCD939X_RX_TOP_HPHR_COMP_RD_LSB:
+ case WCD939X_RX_TOP_HPHR_COMP_RD_MSB:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG5:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG6:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG5:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG6:
+ case WCD939X_COMPANDER_HPHL_CTL6:
+ case WCD939X_R_CTL6:
+ return true;
+ }
+
+ return wcd939x_rdwr_register(dev, reg);
+}
+
+static bool wcd939x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WCD939X_ANA_MBHC_RESULT_1:
+ case WCD939X_ANA_MBHC_RESULT_2:
+ case WCD939X_ANA_MBHC_RESULT_3:
+ case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS:
+ case WCD939X_TX_1_2_SAR2_ERR:
+ case WCD939X_TX_1_2_SAR1_ERR:
+ case WCD939X_TX_3_4_SAR4_ERR:
+ case WCD939X_TX_3_4_SAR3_ERR:
+ case WCD939X_HPH_L_STATUS:
+ case WCD939X_HPH_R_STATUS:
+ case WCD939X_HPH_SURGE_STATUS:
+ case WCD939X_EAR_STATUS_REG_1:
+ case WCD939X_EAR_STATUS_REG_2:
+ case WCD939X_MBHC_NEW_FSM_STATUS:
+ case WCD939X_MBHC_NEW_ADC_RESULT:
+ case WCD939X_DIE_CRACK_DET_OUT:
+ case WCD939X_DIGITAL_INTR_STATUS_0:
+ case WCD939X_DIGITAL_INTR_STATUS_1:
+ case WCD939X_DIGITAL_INTR_STATUS_2:
+ case WCD939X_DIGITAL_SWR_HM_TEST_0:
+ case WCD939X_DIGITAL_SWR_HM_TEST_1:
+ case WCD939X_DIGITAL_PIN_STATUS_0:
+ case WCD939X_DIGITAL_PIN_STATUS_1:
+ case WCD939X_DIGITAL_MODE_STATUS_0:
+ case WCD939X_DIGITAL_MODE_STATUS_1:
+ case WCD939X_RX_TOP_HPHL_COMP_RD_LSB:
+ case WCD939X_RX_TOP_HPHL_COMP_RD_MSB:
+ case WCD939X_RX_TOP_HPHR_COMP_RD_LSB:
+ case WCD939X_RX_TOP_HPHR_COMP_RD_MSB:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG5:
+ case WCD939X_RX_TOP_DSD0_DEBUG_CFG6:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG5:
+ case WCD939X_RX_TOP_DSD1_DEBUG_CFG6:
+ case WCD939X_COMPANDER_HPHL_CTL6:
+ case WCD939X_R_CTL6:
+ return true;
+ }
+ return false;
+}
+
+static bool wcd939x_writeable_register(struct device *dev, unsigned int reg)
+{
+ return wcd939x_rdwr_register(dev, reg);
+}
+
+static const struct regmap_config wcd939x_regmap_config = {
+ .name = "wcd939x_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wcd939x_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wcd939x_defaults),
+ .max_register = WCD939X_MAX_REGISTER,
+ .readable_reg = wcd939x_readable_register,
+ .writeable_reg = wcd939x_writeable_register,
+ .volatile_reg = wcd939x_volatile_register,
+};
+
+static const struct sdw_slave_ops wcd9390_slave_ops = {
+ .update_status = wcd_update_status,
+ .interrupt_callback = wcd9390_interrupt_callback,
+ .bus_config = wcd_bus_config,
+};
+
+static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd939x_sdw_priv *wcd;
+ int ret;
+
+ wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
+ if (!wcd)
+ return -ENOMEM;
+
+ /*
+ * Port map index starts with 0, however the data port for this codec
+ * are from index 1
+ */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ wcd->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,tx-port-mapping",
+ &pdev->m_port_map[1],
+ WCD939X_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,rx-port-mapping",
+ &pdev->m_port_map[1],
+ WCD939X_MAX_RX_SWR_PORTS);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static Port mapping not specified\n");
+
+ wcd->sdev = pdev;
+ dev_set_drvdata(dev, wcd);
+
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+ SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
+ if (wcd->is_tx) {
+ pdev->prop.source_ports = GENMASK(WCD939X_MAX_TX_SWR_PORTS - 1, 0);
+ pdev->prop.src_dpn_prop = wcd939x_tx_dpn_prop;
+ wcd->ch_info = &wcd939x_sdw_tx_ch_info[0];
+ pdev->prop.wake_capable = true;
+ } else {
+ pdev->prop.sink_ports = GENMASK(WCD939X_MAX_RX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = wcd939x_rx_dpn_prop;
+ wcd->ch_info = &wcd939x_sdw_rx_ch_info[0];
+ }
+
+ if (wcd->is_tx) {
+ /*
+ * Do not use devres here since devres_release_group() could
+ * be called by component_unbind() id the aggregate device
+ * fails to bind.
+ */
+ wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config);
+ if (IS_ERR(wcd->regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+ "Regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(wcd->regmap, true);
+ }
+
+ ret = component_add(dev, &wcd_sdw_component_ops);
+ if (ret)
+ goto err_free_regmap;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+
+err_free_regmap:
+ if (wcd->regmap)
+ regmap_exit(wcd->regmap);
+
+ return ret;
+}
+
+static int wcd9390_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ component_del(dev, &wcd_sdw_component_ops);
+
+ if (wcd->regmap)
+ regmap_exit(wcd->regmap);
+
+ return 0;
+}
+
+static const struct sdw_device_id wcd9390_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10e, 0), /* WCD9390 & WCD9390 RX/TX Device ID */
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, wcd9390_slave_id);
+
+static int wcd939x_sdw_runtime_suspend(struct device *dev)
+{
+ struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ if (wcd->regmap) {
+ regcache_cache_only(wcd->regmap, true);
+ regcache_mark_dirty(wcd->regmap);
+ }
+
+ return 0;
+}
+
+static int wcd939x_sdw_runtime_resume(struct device *dev)
+{
+ struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
+
+ if (wcd->regmap) {
+ regcache_cache_only(wcd->regmap, false);
+ regcache_sync(wcd->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops wcd939x_sdw_pm_ops = {
+ RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver wcd9390_codec_driver = {
+ .probe = wcd9390_probe,
+ .remove = wcd9390_remove,
+ .ops = &wcd9390_slave_ops,
+ .id_table = wcd9390_slave_id,
+ .driver = {
+ .name = "wcd9390-codec",
+ .pm = pm_ptr(&wcd939x_sdw_pm_ops),
+ }
+};
+module_sdw_driver(wcd9390_codec_driver);
+
+MODULE_DESCRIPTION("WCD939X SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c
new file mode 100644
index 000000000000..7c5dd0484384
--- /dev/null
+++ b/sound/soc/codecs/wcd939x.c
@@ -0,0 +1,3623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+#include <sound/tlv.h>
+#include <linux/of_graph.h>
+#include <linux/of.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_altmode.h>
+
+#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
+#include "wcd-mbhc-v2.h"
+#include "wcd939x.h"
+
+#define WCD939X_MAX_MICBIAS (4)
+#define WCD939X_MBHC_MAX_BUTTONS (8)
+#define TX_ADC_MAX (4)
+#define WCD_MBHC_HS_V_MAX 1600
+
+#define CHIPID_WCD9390 0x0
+#define CHIPID_WCD9395 0x5
+
+/* Version major: 1.x */
+#define CHIPID_WCD939X_VER_MAJOR_1 0x0
+/* Version minor: x.1 */
+#define CHIPID_WCD939X_VER_MINOR_1 0x3
+
+enum {
+ WCD939X_VERSION_1_0 = 0,
+ WCD939X_VERSION_1_1,
+ WCD939X_VERSION_2_0,
+};
+
+#define WCD939X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define WCD939X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+#define WCD939X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50)
+#define SWR_CLK_RATE_0P6MHZ (600000)
+#define SWR_CLK_RATE_1P2MHZ (1200000)
+#define SWR_CLK_RATE_2P4MHZ (2400000)
+#define SWR_CLK_RATE_4P8MHZ (4800000)
+#define SWR_CLK_RATE_9P6MHZ (9600000)
+#define SWR_CLK_RATE_11P2896MHZ (1128960)
+
+#define ADC_MODE_VAL_HIFI 0x01
+#define ADC_MODE_VAL_LO_HIF 0x02
+#define ADC_MODE_VAL_NORMAL 0x03
+#define ADC_MODE_VAL_LP 0x05
+#define ADC_MODE_VAL_ULP1 0x09
+#define ADC_MODE_VAL_ULP2 0x0B
+
+/* Z value defined in milliohm */
+#define WCD939X_ZDET_VAL_32 (32000)
+#define WCD939X_ZDET_VAL_400 (400000)
+#define WCD939X_ZDET_VAL_1200 (1200000)
+#define WCD939X_ZDET_VAL_100K (100000000)
+
+/* Z floating defined in ohms */
+#define WCD939X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE)
+#define WCD939X_ZDET_NUM_MEASUREMENTS (900)
+#define WCD939X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14)
+#define WCD939X_MBHC_GET_X1(x) ((x) & 0x3FFF)
+
+/* Z value compared in milliOhm */
+#define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024)
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET,
+ WCD939X_IRQ_MBHC_ELECT_INS_REM_DET,
+ WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ WCD939X_IRQ_MBHC_SW_DET,
+ WCD939X_IRQ_HPHR_OCP_INT,
+ WCD939X_IRQ_HPHR_CNP_INT,
+ WCD939X_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ WCD939X_IRQ_HPHL_CNP_INT,
+ WCD939X_IRQ_EAR_CNP_INT,
+ WCD939X_IRQ_EAR_SCD_INT,
+ WCD939X_IRQ_HPHL_PDM_WD_INT,
+ WCD939X_IRQ_HPHR_PDM_WD_INT,
+ WCD939X_IRQ_EAR_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ WCD939X_IRQ_MBHC_MOISTURE_INT,
+ WCD939X_IRQ_HPHL_SURGE_DET_INT,
+ WCD939X_IRQ_HPHR_SURGE_DET_INT,
+ WCD939X_NUM_IRQS,
+};
+
+enum {
+ MICB_BIAS_DISABLE = 0,
+ MICB_BIAS_ENABLE,
+ MICB_BIAS_PULL_UP,
+ MICB_BIAS_PULL_DOWN,
+};
+
+enum {
+ WCD_ADC1 = 0,
+ WCD_ADC2,
+ WCD_ADC3,
+ WCD_ADC4,
+ HPH_PA_DELAY,
+};
+
+enum {
+ ADC_MODE_INVALID = 0,
+ ADC_MODE_HIFI,
+ ADC_MODE_LO_HIF,
+ ADC_MODE_NORMAL,
+ ADC_MODE_LP,
+ ADC_MODE_ULP1,
+ ADC_MODE_ULP2,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+static u8 tx_mode_bit[] = {
+ [ADC_MODE_INVALID] = 0x00,
+ [ADC_MODE_HIFI] = 0x01,
+ [ADC_MODE_LO_HIF] = 0x02,
+ [ADC_MODE_NORMAL] = 0x04,
+ [ADC_MODE_LP] = 0x08,
+ [ADC_MODE_ULP1] = 0x10,
+ [ADC_MODE_ULP2] = 0x20,
+};
+
+struct zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
+struct wcd939x_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct wcd939x_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode, *txnode;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ /* micb setup lock */
+ struct mutex micb_lock;
+ /* typec handling */
+ bool typec_analog_mux;
+#if IS_ENABLED(CONFIG_TYPEC)
+ enum typec_orientation typec_orientation;
+ unsigned long typec_mode;
+ struct typec_switch *typec_switch;
+#endif /* CONFIG_TYPEC */
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct wcd_clsh_ctrl *clsh_info;
+ struct wcd_common common;
+ struct irq_domain *virq;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[WCD939X_MAX_MICBIAS];
+ s32 pullup_ref[WCD939X_MAX_MICBIAS];
+ u32 hph_mode;
+ u32 tx_mode[TX_ADC_MAX];
+ int variant;
+ struct gpio_desc *reset_gpio;
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ int ear_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+ bool ldoh;
+};
+
+static const char * const wcd939x_supplies[] = {
+ "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", "vdd-px",
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD939X_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD939X_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD939X_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD939X_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD939X_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD939X_MBHC_NEW_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD939X_MBHC_NEW_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD939X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD939X_HPH_OCP_CTL, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD939X_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD939X_ANA_MICB2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD939X_HPH_CNP_WG_TIME, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD939X_ANA_HPH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD939X_ANA_HPH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD939X_ANA_HPH, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD939X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD939X_MBHC_CTL_BCS, 0x02),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD939X_MBHC_NEW_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD939X_HPH_PA_CTL2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD939X_HPH_PA_CTL2, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD939X_HPH_L_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD939X_HPH_R_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD939X_MBHC_NEW_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD939X_MBHC_NEW_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD939X_MBHC_NEW_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD939X_MBHC_NEW_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD939X_ANA_MICB2, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD939X_MBHC_NEW_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD939X_MBHC_NEW_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD939X_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = {
+ REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01),
+ REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02),
+ REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04),
+ REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08),
+ REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_SW_DET, 0, 0x10),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_OCP_INT, 0, 0x20),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_CNP_INT, 0, 0x40),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_OCP_INT, 0, 0x80),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_CNP_INT, 1, 0x01),
+ REGMAP_IRQ_REG(WCD939X_IRQ_EAR_CNP_INT, 1, 0x02),
+ REGMAP_IRQ_REG(WCD939X_IRQ_EAR_SCD_INT, 1, 0x04),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_PDM_WD_INT, 1, 0x20),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_PDM_WD_INT, 1, 0x40),
+ REGMAP_IRQ_REG(WCD939X_IRQ_EAR_PDM_WD_INT, 1, 0x80),
+ REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_MOISTURE_INT, 2, 0x02),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04),
+ REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08),
+};
+
+static const struct regmap_irq_chip wcd939x_regmap_irq_chip = {
+ .name = "wcd939x",
+ .irqs = wcd939x_irqs,
+ .num_irqs = ARRAY_SIZE(wcd939x_irqs),
+ .num_regs = 3,
+ .status_base = WCD939X_DIGITAL_INTR_STATUS_0,
+ .mask_base = WCD939X_DIGITAL_INTR_MASK_0,
+ .ack_base = WCD939X_DIGITAL_INTR_CLEAR_0,
+ .use_ack = 1,
+ .runtime_pm = true,
+ .irq_drv_data = NULL,
+};
+
+static int wcd939x_get_clk_rate(int mode)
+{
+ int rate;
+
+ switch (mode) {
+ case ADC_MODE_ULP2:
+ rate = SWR_CLK_RATE_0P6MHZ;
+ break;
+ case ADC_MODE_ULP1:
+ rate = SWR_CLK_RATE_1P2MHZ;
+ break;
+ case ADC_MODE_LP:
+ rate = SWR_CLK_RATE_4P8MHZ;
+ break;
+ case ADC_MODE_NORMAL:
+ case ADC_MODE_LO_HIF:
+ case ADC_MODE_HIFI:
+ case ADC_MODE_INVALID:
+ default:
+ rate = SWR_CLK_RATE_9P6MHZ;
+ break;
+ }
+
+ return rate;
+}
+
+static int wcd939x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank)
+{
+ u8 mask = (bank ? 0xF0 : 0x0F);
+ u8 val = 0;
+
+ switch (rate) {
+ case SWR_CLK_RATE_0P6MHZ:
+ val = 6;
+ break;
+ case SWR_CLK_RATE_1P2MHZ:
+ val = 5;
+ break;
+ case SWR_CLK_RATE_2P4MHZ:
+ val = 3;
+ break;
+ case SWR_CLK_RATE_4P8MHZ:
+ val = 1;
+ break;
+ case SWR_CLK_RATE_9P6MHZ:
+ default:
+ val = 0;
+ break;
+ }
+
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_SWR_TX_CLK_RATE, mask, val);
+
+ return 0;
+}
+
+static int wcd939x_io_init(struct snd_soc_component *component)
+{
+ snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
+ WCD939X_BIAS_ANALOG_BIAS_EN, true);
+ snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
+ WCD939X_BIAS_PRECHRG_EN, true);
+
+ /* 10 msec delay as per HW requirement */
+ usleep_range(10000, 10010);
+ snd_soc_component_write_field(component, WCD939X_ANA_BIAS,
+ WCD939X_BIAS_PRECHRG_EN, false);
+
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x15);
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x15);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
+ WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true);
+
+ snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP,
+ WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M, 1);
+ snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP,
+ WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE, 4);
+
+ snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP,
+ WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE, 8);
+
+ snd_soc_component_write_field(component, WCD939X_MICB1_TEST_CTL_1,
+ WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+ snd_soc_component_write_field(component, WCD939X_MICB2_TEST_CTL_1,
+ WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+ snd_soc_component_write_field(component, WCD939X_MICB3_TEST_CTL_1,
+ WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+ snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_1,
+ WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7);
+ snd_soc_component_write_field(component, WCD939X_TX_3_4_TEST_BLK_EN2,
+ WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN, false);
+
+ snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+ WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false);
+ snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+ WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false);
+
+ snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL,
+ WCD939X_OCP_CTL_OCP_FSM_EN, true);
+ snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL,
+ WCD939X_OCP_CTL_SCD_OP_EN, true);
+
+ snd_soc_component_write(component, WCD939X_E_CFG0,
+ WCD939X_CFG0_IDLE_STEREO |
+ WCD939X_CFG0_AUTO_DISABLE_ANC);
+
+ return 0;
+}
+
+static int wcd939x_sdw_connect_port(const struct wcd_sdw_ch_info *ch_info,
+ struct sdw_port_config *port_config,
+ u8 enable)
+{
+ u8 ch_mask, port_num;
+
+ port_num = ch_info->port_num;
+ ch_mask = ch_info->ch_mask;
+
+ port_config->num = port_num;
+
+ if (enable)
+ port_config->ch_mask |= ch_mask;
+ else
+ port_config->ch_mask &= ~ch_mask;
+
+ return 0;
+}
+
+static int wcd939x_connect_port(struct wcd939x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable)
+{
+ return wcd939x_sdw_connect_port(&wcd->ch_info[ch_id],
+ &wcd->port_config[port_num - 1],
+ enable);
+}
+
+static int wcd939x_codec_enable_rxclk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, true);
+
+ /* Analog path clock controls */
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN,
+ true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN,
+ true);
+
+ /* Digital path clock controls */
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_VNEG_EN, false);
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_VPOS_EN, false);
+
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, false);
+
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN,
+ false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN,
+ false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, false);
+
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, false);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1,
+ WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN,
+ false);
+
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x1d);
+ if (wcd939x->comp1_enable) {
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_COMP_CTL_0,
+ WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN,
+ true);
+ /* 5msec compander delay as per HW requirement */
+ if (!wcd939x->comp2_enable ||
+ snd_soc_component_read_field(component,
+ WCD939X_DIGITAL_CDC_COMP_CTL_0,
+ WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN))
+ usleep_range(5000, 5010);
+
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+ WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN,
+ false);
+ } else {
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_COMP_CTL_0,
+ WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN,
+ false);
+ snd_soc_component_write_field(component, WCD939X_HPH_L_EN,
+ WCD939X_L_EN_GAIN_SOURCE_SEL, true);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 1);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+ w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1,
+ WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN,
+ false);
+
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x1d);
+ if (wcd939x->comp2_enable) {
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_COMP_CTL_0,
+ WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN,
+ true);
+ /* 5msec compander delay as per HW requirement */
+ if (!wcd939x->comp1_enable ||
+ snd_soc_component_read_field(component,
+ WCD939X_DIGITAL_CDC_COMP_CTL_0,
+ WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN))
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+ WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN,
+ false);
+ } else {
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_COMP_CTL_0,
+ WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN,
+ false);
+ snd_soc_component_write_field(component, WCD939X_HPH_R_EN,
+ WCD939X_R_EN_GAIN_SOURCE_SEL, true);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 1);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL,
+ WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_EAR_GAIN_CTL,
+ WCD939X_CDC_EAR_GAIN_CTL_EAR_EN, true);
+
+ snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON,
+ WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, false);
+
+ /* 5 msec delay as per HW requirement */
+ usleep_range(5000, 5010);
+ wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR, CLS_AB_HIFI);
+
+ snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
+ WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON,
+ WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd939x->hph_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wcd939x->ldoh)
+ snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+ WCD939X_MODE_LDOH_EN, true);
+
+ wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR, hph_mode);
+ wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI);
+
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP)
+ snd_soc_component_write_field(component,
+ WCD939X_HPH_REFBUFF_LP_CTL,
+ WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, true);
+ if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_PWR_LEVEL, 0);
+
+ snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
+ WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHR_REF_ENABLE, true);
+
+ if (snd_soc_component_read_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHL_REF_ENABLE))
+ usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */
+
+ set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1,
+ WCD939X_PDM_WD_CTL1_PDM_WD_EN, 3);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+ if (!wcd939x->comp2_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_ULP)
+ snd_soc_component_write_field(component,
+ WCD939X_HPH_REFBUFF_LP_CTL,
+ WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
+ false);
+ clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ }
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+ WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+ hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_REGULATOR_MODE,
+ true);
+
+ enable_irq(wcd939x->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd939x->hphr_pdm_wd_int);
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (!wcd939x->comp2_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHR_ENABLE, false);
+
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_PRE_HPHR_PA_OFF);
+ set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+ if (!wcd939x->comp2_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ }
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_POST_HPHR_PA_OFF);
+
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHR_REF_ENABLE, false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1,
+ WCD939X_PDM_WD_CTL1_PDM_WD_EN, 0);
+
+ wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR, hph_mode);
+ if (wcd939x->ldoh)
+ snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+ WCD939X_MODE_LDOH_EN, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ int hph_mode = wcd939x->hph_mode;
+
+ dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+ w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wcd939x->ldoh)
+ snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+ WCD939X_MODE_LDOH_EN, true);
+ wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL, hph_mode);
+ wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI);
+
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP)
+ snd_soc_component_write_field(component,
+ WCD939X_HPH_REFBUFF_LP_CTL,
+ WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
+ true);
+ if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_PWR_LEVEL, 0);
+
+ snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4,
+ WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd);
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHL_REF_ENABLE, true);
+
+ if (snd_soc_component_read_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHR_REF_ENABLE))
+ usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */
+
+ set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+ WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+ if (!wcd939x->comp1_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_ULP)
+ snd_soc_component_write_field(component,
+ WCD939X_HPH_REFBUFF_LP_CTL,
+ WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS,
+ false);
+ clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ }
+ snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1,
+ WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI ||
+ hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI)
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_REGULATOR_MODE,
+ true);
+ enable_irq(wcd939x->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd939x->hphl_pdm_wd_int);
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (!wcd939x->comp1_enable)
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHL_ENABLE, false);
+
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF);
+ set_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 7ms sleep is required if compander is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is required.
+ */
+ if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) {
+ if (!wcd939x->comp1_enable)
+ usleep_range(21000, 21100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &wcd939x->status_mask);
+ }
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_POST_HPHL_PA_OFF);
+ snd_soc_component_write_field(component, WCD939X_ANA_HPH,
+ WCD939X_HPH_HPHL_REF_ENABLE, false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+ WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0);
+ wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL, hph_mode);
+ if (wcd939x->ldoh)
+ snd_soc_component_write_field(component, WCD939X_LDOH_MODE,
+ WCD939X_MODE_LDOH_EN, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable watchdog interrupt for HPHL */
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+ WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3);
+ /* For EAR, use CLASS_AB regulator mode */
+ snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES,
+ WCD939X_RX_SUPPLIES_REGULATOR_MODE, true);
+ snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL,
+ WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 6 msec delay as per HW requirement */
+ usleep_range(6000, 6010);
+ enable_irq(wcd939x->ear_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(wcd939x->ear_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL,
+ WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG,
+ false);
+ /* 7 msec delay as per HW requirement */
+ usleep_range(7000, 7010);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0,
+ WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0);
+ wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR, CLS_AB_HIFI);
+ break;
+ }
+
+ return 0;
+}
+
+/* TX Controls */
+
+static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 dmic_clk_reg, dmic_clk_en_reg;
+ u8 dmic_clk_en_mask;
+ u8 dmic_ctl_mask;
+ u8 dmic_clk_mask;
+
+ switch (w->shift) {
+ case 0:
+ case 1:
+ dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2;
+ dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC1_CTL;
+ dmic_clk_en_mask = WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN;
+ dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE;
+ dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2;
+ dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC2_CTL;
+ dmic_clk_en_mask = WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN;
+ dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE;
+ dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4;
+ dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC3_CTL;
+ dmic_clk_en_mask = WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN;
+ dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE;
+ dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL;
+ break;
+ case 6:
+ case 7:
+ dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4;
+ dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC4_CTL;
+ dmic_clk_en_mask = WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN;
+ dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE;
+ dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL,
+ dmic_ctl_mask, false);
+ /* 250us sleep as per HW requirement */
+ usleep_range(250, 260);
+ if (w->shift == 2)
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DMIC2_CTL,
+ WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN,
+ true);
+ /* Setting DMIC clock rate to 2.4MHz */
+ snd_soc_component_write_field(component, dmic_clk_reg,
+ dmic_clk_mask, 3);
+ snd_soc_component_write_field(component, dmic_clk_en_reg,
+ dmic_clk_en_mask, true);
+ /* enable clock scaling */
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
+ WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL,
+ WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL,
+ dmic_ctl_mask, 1);
+ if (w->shift == 2)
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DMIC2_CTL,
+ WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN,
+ false);
+ snd_soc_component_write_field(component, dmic_clk_en_reg,
+ dmic_clk_en_mask, 0);
+ break;
+ }
+ return 0;
+}
+
+static int wcd939x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ int bank;
+ int rate;
+
+ bank = sdw_slave_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (strnstr(w->name, "ADC", sizeof("ADC"))) {
+ int mode = 0;
+
+ if (test_bit(WCD_ADC1, &wcd939x->status_mask))
+ mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC1]];
+ if (test_bit(WCD_ADC2, &wcd939x->status_mask))
+ mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC2]];
+ if (test_bit(WCD_ADC3, &wcd939x->status_mask))
+ mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC3]];
+ if (test_bit(WCD_ADC4, &wcd939x->status_mask))
+ mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC4]];
+
+ if (mode)
+ rate = wcd939x_get_clk_rate(ffs(mode) - 1);
+ else
+ rate = wcd939x_get_clk_rate(ADC_MODE_INVALID);
+ wcd939x_set_swr_clk_rate(component, rate, bank);
+ wcd939x_set_swr_clk_rate(component, rate, !bank);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (strnstr(w->name, "ADC", sizeof("ADC"))) {
+ rate = wcd939x_get_clk_rate(ADC_MODE_INVALID);
+ wcd939x_set_swr_clk_rate(component, rate, !bank);
+ wcd939x_set_swr_clk_rate(component, rate, bank);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_get_adc_mode(int val)
+{
+ int ret = 0;
+
+ switch (val) {
+ case ADC_MODE_INVALID:
+ ret = ADC_MODE_VAL_NORMAL;
+ break;
+ case ADC_MODE_HIFI:
+ ret = ADC_MODE_VAL_HIFI;
+ break;
+ case ADC_MODE_LO_HIF:
+ ret = ADC_MODE_VAL_LO_HIF;
+ break;
+ case ADC_MODE_NORMAL:
+ ret = ADC_MODE_VAL_NORMAL;
+ break;
+ case ADC_MODE_LP:
+ ret = ADC_MODE_VAL_LP;
+ break;
+ case ADC_MODE_ULP1:
+ ret = ADC_MODE_VAL_ULP1;
+ break;
+ case ADC_MODE_ULP2:
+ ret = ADC_MODE_VAL_ULP2;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int wcd939x_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
+ true);
+ set_bit(w->shift, &wcd939x->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
+ false);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN,
+ false);
+ clear_bit(w->shift, &wcd939x->status_mask);
+ break;
+ }
+
+ return 0;
+}
+
+static void wcd939x_tx_channel_config(struct snd_soc_component *component,
+ int channel, bool init)
+{
+ int reg, mask;
+
+ switch (channel) {
+ case 0:
+ reg = WCD939X_ANA_TX_CH2;
+ mask = WCD939X_TX_CH2_HPF1_INIT;
+ break;
+ case 1:
+ reg = WCD939X_ANA_TX_CH2;
+ mask = WCD939X_TX_CH2_HPF2_INIT;
+ break;
+ case 2:
+ reg = WCD939X_ANA_TX_CH4;
+ mask = WCD939X_TX_CH4_HPF3_INIT;
+ break;
+ case 3:
+ reg = WCD939X_ANA_TX_CH4;
+ mask = WCD939X_TX_CH4_HPF4_INIT;
+ break;
+ default:
+ return;
+ }
+
+ snd_soc_component_write_field(component, reg, mask, init);
+}
+
+static int wcd939x_adc_enable_req(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ int mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL,
+ WCD939X_CDC_REQ_CTL_FS_RATE_4P8, true);
+ snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL,
+ WCD939X_CDC_REQ_CTL_NO_NOTCH, false);
+
+ wcd939x_tx_channel_config(component, w->shift, true);
+ mode = wcd939x_get_adc_mode(wcd939x->tx_mode[w->shift]);
+ if (mode < 0) {
+ dev_info(component->dev, "Invalid ADC mode\n");
+ return -EINVAL;
+ }
+
+ switch (w->shift) {
+ case 0:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE,
+ mode);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN,
+ true);
+ break;
+ case 1:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE,
+ mode);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN,
+ true);
+ break;
+ case 2:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE,
+ mode);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN,
+ true);
+ break;
+ case 3:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE,
+ mode);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN,
+ true);
+ break;
+ default:
+ break;
+ }
+
+ wcd939x_tx_channel_config(component, w->shift, false);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->shift) {
+ case 0:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE,
+ false);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN,
+ false);
+ break;
+ case 1:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1,
+ WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE,
+ false);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN,
+ false);
+ break;
+ case 2:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE,
+ false);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN,
+ false);
+ break;
+ case 3:
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3,
+ WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE,
+ false);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN,
+ false);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_micbias_control(struct snd_soc_component *component,
+ int micb_num, int req, bool is_dapm)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD939X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD939X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD939X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD939X_ANA_MICB4;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ wcd939x->pullup_ref[micb_index]++;
+ if (wcd939x->pullup_ref[micb_index] == 1 &&
+ wcd939x->micb_ref[micb_index] == 0)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_PULL_UP);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (wcd939x->pullup_ref[micb_index] > 0)
+ wcd939x->pullup_ref[micb_index]--;
+ if (wcd939x->pullup_ref[micb_index] == 0 &&
+ wcd939x->micb_ref[micb_index] == 0)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_DISABLE);
+ break;
+ case MICB_ENABLE:
+ wcd939x->micb_ref[micb_index]++;
+ if (wcd939x->micb_ref[micb_index] == 1) {
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, true);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, true);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, true);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_DIG_CLK_CTL,
+ WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, true);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_ANA_CLK_CTL,
+ WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN,
+ true);
+ snd_soc_component_write_field(component,
+ WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL,
+ WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN,
+ true);
+ snd_soc_component_write_field(component,
+ WCD939X_MICB1_TEST_CTL_2,
+ WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+ snd_soc_component_write_field(component,
+ WCD939X_MICB2_TEST_CTL_2,
+ WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+ snd_soc_component_write_field(component,
+ WCD939X_MICB3_TEST_CTL_2,
+ WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+ snd_soc_component_write_field(component,
+ WCD939X_MICB4_TEST_CTL_2,
+ WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true);
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_ENABLE);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+ }
+ if (micb_num == MIC_BIAS_2 && is_dapm)
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+ break;
+ case MICB_DISABLE:
+ if (wcd939x->micb_ref[micb_index] > 0)
+ wcd939x->micb_ref[micb_index]--;
+
+ if (wcd939x->micb_ref[micb_index] == 0 &&
+ wcd939x->pullup_ref[micb_index] > 0)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_PULL_UP);
+ else if (wcd939x->micb_ref[micb_index] == 0 &&
+ wcd939x->pullup_ref[micb_index] == 0) {
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_PRE_MICBIAS_2_OFF);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_DISABLE);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ }
+ if (is_dapm && micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd939x->wcd_mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd939x_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd939x_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd939x_micbias_control(component, micb_num,
+ MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd939x_micbias_control(component, micb_num,
+ MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int wcd939x_tx_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.enumerated.item[0] = wcd939x->tx_mode[path];
+
+ return 0;
+}
+
+static int wcd939x_tx_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ if (wcd939x->tx_mode[path] == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wcd939x->tx_mode[path] = ucontrol->value.enumerated.item[0];
+
+ return 1;
+}
+
+/* RX Controls */
+
+static int wcd939x_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd939x->hph_mode;
+
+ return 0;
+}
+
+static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ if (mode_val == wcd939x->hph_mode)
+ return 0;
+
+ if (wcd939x->variant == CHIPID_WCD9390) {
+ switch (mode_val) {
+ case CLS_H_NORMAL:
+ case CLS_H_LP:
+ case CLS_AB:
+ case CLS_H_LOHIFI:
+ case CLS_H_ULP:
+ case CLS_AB_LP:
+ case CLS_AB_LOHIFI:
+ wcd939x->hph_mode = mode_val;
+ return 1;
+ }
+ } else {
+ switch (mode_val) {
+ case CLS_H_NORMAL:
+ case CLS_H_HIFI:
+ case CLS_H_LP:
+ case CLS_AB:
+ case CLS_H_LOHIFI:
+ case CLS_H_ULP:
+ case CLS_AB_HIFI:
+ case CLS_AB_LP:
+ case CLS_AB_LOHIFI:
+ wcd939x->hph_mode = mode_val;
+ return 1;
+ }
+ }
+
+ dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__);
+ return -EINVAL;
+}
+
+static int wcd939x_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ if (mc->shift)
+ ucontrol->value.integer.value[0] = wcd939x->comp2_enable ? 1 : 0;
+ else
+ ucontrol->value.integer.value[0] = wcd939x->comp1_enable ? 1 : 0;
+
+ return 0;
+}
+
+static int wcd939x_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[AIF1_PB];
+ bool value = !!ucontrol->value.integer.value[0];
+ int portidx = wcd->ch_info[mc->reg].port_num;
+
+ if (mc->shift)
+ wcd939x->comp2_enable = value;
+ else
+ wcd939x->comp1_enable = value;
+
+ if (value)
+ wcd939x_connect_port(wcd, portidx, mc->reg, true);
+ else
+ wcd939x_connect_port(wcd, portidx, mc->reg, false);
+
+ return 1;
+}
+
+static int wcd939x_ldoh_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd939x->ldoh ? 1 : 0;
+
+ return 0;
+}
+
+static int wcd939x_ldoh_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ if (wcd939x->ldoh == !!ucontrol->value.integer.value[0])
+ return 0;
+
+ wcd939x->ldoh = !!ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static const char * const tx_mode_mux_text_wcd9390[] = {
+ "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+};
+
+static const struct soc_enum tx0_mode_mux_enum_wcd9390 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+ tx_mode_mux_text_wcd9390);
+
+static const struct soc_enum tx1_mode_mux_enum_wcd9390 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+ tx_mode_mux_text_wcd9390);
+
+static const struct soc_enum tx2_mode_mux_enum_wcd9390 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+ tx_mode_mux_text_wcd9390);
+
+static const struct soc_enum tx3_mode_mux_enum_wcd9390 =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9390),
+ tx_mode_mux_text_wcd9390);
+
+static const char * const tx_mode_mux_text[] = {
+ "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP",
+ "ADC_ULP1", "ADC_ULP2",
+};
+
+static const struct soc_enum tx0_mode_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum tx1_mode_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum tx2_mode_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const struct soc_enum tx3_mode_mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text),
+ tx_mode_mux_text);
+
+static const char * const rx_hph_mode_mux_text_wcd9390[] = {
+ "CLS_H_NORMAL", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB",
+ "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP",
+ "CLS_AB_LOHIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum_wcd9390 =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9390),
+ rx_hph_mode_mux_text_wcd9390);
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "CLS_H_NORMAL", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
+ "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const struct snd_kcontrol_new wcd9390_snd_controls[] = {
+ SOC_SINGLE_TLV("EAR_PA Volume", WCD939X_ANA_EAR_COMPANDER_CTL,
+ 2, 0x10, 0, ear_pa_gain),
+
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9390,
+ wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put),
+
+ SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum_wcd9390,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+ SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum_wcd9390,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+ SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum_wcd9390,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+ SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum_wcd9390,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+};
+
+static const struct snd_kcontrol_new wcd9395_snd_controls[] = {
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put),
+
+ SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+ SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+ SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+ SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum,
+ wcd939x_tx_mode_get, wcd939x_tx_mode_put),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc3_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc4_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic3_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic4_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic5_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic6_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic7_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic8_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc1_mux_text[] = {
+ "CH1_AMIC_DISABLE", "CH1_AMIC1", "CH1_AMIC2", "CH1_AMIC3", "CH1_AMIC4", "CH1_AMIC5"
+};
+
+static const struct soc_enum adc1_enum =
+ SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 0,
+ ARRAY_SIZE(adc1_mux_text), adc1_mux_text);
+
+static const struct snd_kcontrol_new tx_adc1_mux =
+ SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum);
+
+static const char * const adc2_mux_text[] = {
+ "CH2_AMIC_DISABLE", "CH2_AMIC1", "CH2_AMIC2", "CH2_AMIC3", "CH2_AMIC4", "CH2_AMIC5"
+};
+
+static const struct soc_enum adc2_enum =
+ SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 3,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux =
+ SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const char * const adc3_mux_text[] = {
+ "CH3_AMIC_DISABLE", "CH3_AMIC1", "CH3_AMIC3", "CH3_AMIC4", "CH3_AMIC5"
+};
+
+static const struct soc_enum adc3_enum =
+ SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 0,
+ ARRAY_SIZE(adc3_mux_text), adc3_mux_text);
+
+static const struct snd_kcontrol_new tx_adc3_mux =
+ SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum);
+
+static const char * const adc4_mux_text[] = {
+ "CH4_AMIC_DISABLE", "CH4_AMIC1", "CH4_AMIC3", "CH4_AMIC4", "CH4_AMIC5"
+};
+
+static const struct soc_enum adc4_enum =
+ SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 3,
+ ARRAY_SIZE(adc4_mux_text), adc4_mux_text);
+
+static const struct snd_kcontrol_new tx_adc4_mux =
+ SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum);
+
+static const char * const rdac3_mux_text[] = {
+ "RX3", "RX1"
+};
+
+static const struct soc_enum rdac3_enum =
+ SOC_ENUM_SINGLE(WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0,
+ ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text);
+
+static const struct snd_kcontrol_new rx_rdac3_mux =
+ SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum);
+
+static int wcd939x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp);
+ struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift];
+ unsigned int portidx = wcd->ch_info[mixer->reg].port_num;
+
+ ucontrol->value.integer.value[0] = wcd->port_enable[portidx] ? 1 : 0;
+
+ return 0;
+}
+
+static const char *version_to_str(u32 version)
+{
+ switch (version) {
+ case WCD939X_VERSION_1_0:
+ return __stringify(WCD939X_1_0);
+ case WCD939X_VERSION_1_1:
+ return __stringify(WCD939X_1_1);
+ case WCD939X_VERSION_2_0:
+ return __stringify(WCD939X_2_0);
+ }
+ return NULL;
+}
+
+static int wcd939x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp);
+ struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift];
+ unsigned int portidx = wcd->ch_info[mixer->reg].port_num;
+
+ wcd->port_enable[portidx] = !!ucontrol->value.integer.value[0];
+
+ wcd939x_connect_port(wcd, portidx, mixer->reg, wcd->port_enable[portidx]);
+
+ return 1;
+}
+
+/* MBHC Related */
+
+static void wcd939x_mbhc_clk_setup(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_1,
+ WCD939X_CTL_1_RCO_EN, enable);
+}
+
+static void wcd939x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
+ WCD939X_MBHC_ELECT_BIAS_EN, enable);
+}
+
+static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = (btn_high[i] * 2) / 25;
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_BTN0 + i,
+ WCD939X_MBHC_BTN0_VTH, vth);
+ dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+ __func__, i, btn_high[i], vth);
+ }
+}
+
+static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
+{
+ if (micb_num == MIC_BIAS_2) {
+ u8 val;
+
+ val = FIELD_GET(WCD939X_MICB_ENABLE,
+ snd_soc_component_read(component, WCD939X_ANA_MICB2));
+ if (val == MICB_BIAS_ENABLE)
+ return true;
+ }
+
+ return false;
+}
+
+static void wcd939x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
+ int pull_up_cur)
+{
+ /* Default pull up current to 2uA */
+ if (pull_up_cur > HS_PULLUP_I_OFF ||
+ pull_up_cur < HS_PULLUP_I_3P0_UA ||
+ pull_up_cur == HS_PULLUP_I_DEFAULT)
+ pull_up_cur = HS_PULLUP_I_2P0_UA;
+
+ dev_dbg(component->dev, "%s: HS pull up current:%d\n",
+ __func__, pull_up_cur);
+
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT,
+ WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL, pull_up_cur);
+}
+
+static int wcd939x_mbhc_request_micbias(struct snd_soc_component *component,
+ int micb_num, int req)
+{
+ return wcd939x_micbias_control(component, micb_num, req, false);
+}
+
+static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+ WCD939X_MICB2_RAMP_SHIFT_CTL, 3);
+ snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+ WCD939X_MICB2_RAMP_RAMP_ENABLE, true);
+ } else {
+ snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+ WCD939X_MICB2_RAMP_RAMP_ENABLE, false);
+ snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP,
+ WCD939X_MICB2_RAMP_SHIFT_CTL, 0);
+ }
+}
+
+static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+ int req_volt, int micb_num)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ unsigned int micb_reg, cur_vout_ctl, micb_en;
+ int req_vout_ctl;
+ int ret = 0;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD939X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD939X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD939X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD939X_ANA_MICB4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&wcd939x->micb_lock);
+
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_en = snd_soc_component_read_field(component, micb_reg,
+ WCD939X_MICB_ENABLE);
+ cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
+ WCD939X_MICB_VOUT_CTL);
+
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
+ if (req_vout_ctl < 0) {
+ ret = req_vout_ctl;
+ goto exit;
+ }
+
+ if (cur_vout_ctl == req_vout_ctl) {
+ ret = 0;
+ goto exit;
+ }
+
+ dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n",
+ __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl),
+ req_volt, micb_en);
+
+ if (micb_en == MICB_BIAS_ENABLE)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_PULL_DOWN);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_VOUT_CTL, req_vout_ctl);
+
+ if (micb_en == MICB_BIAS_ENABLE) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD939X_MICB_ENABLE,
+ MICB_BIAS_ENABLE);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+
+exit:
+ mutex_unlock(&wcd939x->micb_lock);
+ return ret;
+}
+
+static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
+ int micb_num, bool req_en)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ int micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (wcd939x->common.micb_mv[1] >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->common.micb_mv[1];
+
+ return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+}
+
+/* Selected by WCD939X_MBHC_GET_C1() */
+static const s16 wcd939x_wcd_mbhc_d1_a[4] = {
+ 0, 30, 30, 6
+};
+
+/* Selected by zdet_param.noff */
+static const int wcd939x_mbhc_mincode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+};
+
+static const struct zdet_param wcd939x_mbhc_zdet_param = {
+ .ldo_ctl = 4,
+ .noff = 0,
+ .nshift = 6,
+ .btn5 = 0x18,
+ .btn6 = 0x60,
+ .btn7 = 0x78,
+};
+
+static void wcd939x_mbhc_get_result_params(struct snd_soc_component *component,
+ int32_t *zdet)
+{
+ const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param;
+ s32 x1, d1, denom;
+ int val;
+ s16 c1;
+ int i;
+
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+ WCD939X_MBHC_ZDET_ZDET_CHG_EN, true);
+ for (i = 0; i < WCD939X_ZDET_NUM_MEASUREMENTS; i++) {
+ val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2,
+ WCD939X_MBHC_RESULT_2_Z_RESULT_MSB);
+ if (val & BIT(7))
+ break;
+ }
+ val = val << 8;
+ val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1,
+ WCD939X_MBHC_RESULT_1_Z_RESULT_LSB);
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+ WCD939X_MBHC_ZDET_ZDET_CHG_EN, false);
+ x1 = WCD939X_MBHC_GET_X1(val);
+ c1 = WCD939X_MBHC_GET_C1(val);
+
+ /* If ramp is not complete, give additional 5ms */
+ if (c1 < 2 && x1)
+ mdelay(5);
+
+ if (!c1 || !x1) {
+ dev_dbg(component->dev,
+ "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ __func__, c1, x1);
+ goto ramp_down;
+ }
+
+ d1 = wcd939x_wcd_mbhc_d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - zdet_param->noff));
+ if (denom > 0)
+ *zdet = (WCD939X_ANA_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < wcd939x_mbhc_mincode_param[zdet_param->noff])
+ *zdet = WCD939X_ZDET_FLOATING_IMPEDANCE;
+
+ dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+ while (x1) {
+ val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1,
+ WCD939X_MBHC_RESULT_1_Z_RESULT_LSB) << 8;
+ val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2,
+ WCD939X_MBHC_RESULT_2_Z_RESULT_MSB);
+ x1 = WCD939X_MBHC_GET_X1(val);
+ i++;
+ if (i == WCD939X_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+static void wcd939x_mbhc_zdet_ramp(struct snd_soc_component *component,
+ s32 *zl, int32_t *zr)
+{
+ const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param;
+ s32 zdet = 0;
+
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD939X_ZDET_ANA_CTL_MAXV_CTL, zdet_param->ldo_ctl);
+ snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN5, WCD939X_MBHC_BTN5_VTH,
+ zdet_param->btn5);
+ snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN6, WCD939X_MBHC_BTN6_VTH,
+ zdet_param->btn6);
+ snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN7, WCD939X_MBHC_BTN7_VTH,
+ zdet_param->btn7);
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD939X_ZDET_ANA_CTL_RANGE_CTL, zdet_param->noff);
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL,
+ WCD939X_ZDET_RAMP_CTL_TIME_CTL, zdet_param->nshift);
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL,
+ WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL, 6); /*acc1_min_63 */
+
+ if (!zl)
+ goto z_right;
+
+ /* Start impedance measurement for HPH_L */
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+ WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, true);
+ dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n",
+ __func__, zdet_param->noff);
+ wcd939x_mbhc_get_result_params(component, &zdet);
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+ WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, false);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+
+ /* Start impedance measurement for HPH_R */
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+ WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, true);
+ dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n",
+ __func__, zdet_param->noff);
+ wcd939x_mbhc_get_result_params(component, &zdet);
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET,
+ WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, false);
+
+ *zr = zdet;
+}
+
+static void wcd939x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ s32 *z_val, int flag_l_r)
+{
+ int q1_cal;
+ s16 q1;
+
+ q1 = snd_soc_component_read(component, WCD939X_DIGITAL_EFUSE_REG_21 + flag_l_r);
+ if (q1 & BIT(7))
+ q1_cal = (10000 - ((q1 & GENMASK(6, 0)) * 10));
+ else
+ q1_cal = (10000 + (q1 * 10));
+
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void wcd939x_wcd_mbhc_calc_impedance(struct snd_soc_component *component,
+ u32 *zl, uint32_t *zr)
+{
+ struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev);
+ unsigned int reg0, reg1, reg2, reg3, reg4;
+ int z_mono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ s32 z1l, z1r, z1ls;
+
+ reg0 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN5);
+ reg1 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN6);
+ reg2 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN7);
+ reg3 = snd_soc_component_read(component, WCD939X_MBHC_CTL_CLK);
+ reg4 = snd_soc_component_read(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL);
+
+ if (snd_soc_component_read_field(component, WCD939X_ANA_MBHC_ELECT,
+ WCD939X_MBHC_ELECT_FSM_EN)) {
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
+ WCD939X_MBHC_ELECT_FSM_EN, false);
+ is_fsm_disable = true;
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (wcd939x->mbhc_cfg.hphl_swh)
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_L_DET_EN, false);
+
+ /* Turn off 100k pull down on HPHL */
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND,
+ false);
+
+ /*
+ * Disable surge protection before impedance detection.
+ * This is done to give correct value for high impedance.
+ */
+ snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+ WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false);
+ snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+ WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false);
+
+ /* 1ms delay needed after disable surge protection */
+ usleep_range(1000, 1010);
+
+ /* First get impedance on Left */
+ wcd939x_mbhc_zdet_ramp(component, &z1l, NULL);
+ if (z1l == WCD939X_ZDET_FLOATING_IMPEDANCE || z1l > WCD939X_ZDET_VAL_100K) {
+ *zl = WCD939X_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zl = z1l / 1000;
+ wcd939x_wcd_mbhc_qfuse_cal(component, zl, 0);
+ }
+ dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+ __func__, *zl);
+
+ /* Start of right impedance ramp and calculation */
+ wcd939x_mbhc_zdet_ramp(component, NULL, &z1r);
+ if (z1r == WCD939X_ZDET_FLOATING_IMPEDANCE || z1r > WCD939X_ZDET_VAL_100K) {
+ *zr = WCD939X_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1r / 1000;
+ wcd939x_wcd_mbhc_qfuse_cal(component, zr, 1);
+ }
+ dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+ __func__, *zr);
+
+ /* Mono/stereo detection */
+ if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE &&
+ *zr == WCD939X_ZDET_FLOATING_IMPEDANCE) {
+ dev_dbg(component->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+
+ if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE ||
+ *zr == WCD939X_ZDET_FLOATING_IMPEDANCE ||
+ (*zl < WCD_MONO_HS_MIN_THR && *zr > WCD_MONO_HS_MIN_THR) ||
+ (*zl > WCD_MONO_HS_MIN_THR && *zr < WCD_MONO_HS_MIN_THR)) {
+ dev_dbg(component->dev,
+ "%s: Mono plug type with one ch floating or shorted to GND\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+ goto zdet_complete;
+ }
+
+ snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST,
+ WCD939X_R_ATEST_HPH_GND_OVR, true);
+ snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+ WCD939X_PA_CTL2_HPHPA_GND_R, true);
+ wcd939x_mbhc_zdet_ramp(component, &z1ls, NULL);
+ snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+ WCD939X_PA_CTL2_HPHPA_GND_R, false);
+ snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST,
+ WCD939X_R_ATEST_HPH_GND_OVR, false);
+
+ z1ls /= 1000;
+ wcd939x_wcd_mbhc_qfuse_cal(component, &z1ls, 0);
+
+ /* Parallel of left Z and 9 ohm pull down resistor */
+ z_mono = (*zl * 9) / (*zl + 9);
+ z_diff1 = z1ls > z_mono ? z1ls - z_mono : z_mono - z1ls;
+ z_diff2 = *zl > z1ls ? *zl - z1ls : z1ls - *zl;
+ if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + z_mono))) {
+ dev_dbg(component->dev, "%s: stereo plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_STEREO);
+ } else {
+ dev_dbg(component->dev, "%s: MONO plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO);
+ }
+
+ /* Enable surge protection again after impedance detection */
+ snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+ WCD939X_EN_EN_SURGE_PROTECTION_HPHR, true);
+ snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN,
+ WCD939X_EN_EN_SURGE_PROTECTION_HPHL, true);
+
+zdet_complete:
+ snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN5, reg0);
+ snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN6, reg1);
+ snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN7, reg2);
+
+ /* Turn on 100k pull down on HPHL */
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, true);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (wcd939x->mbhc_cfg.hphl_swh)
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_L_DET_EN, true);
+
+ snd_soc_component_write(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+ snd_soc_component_write(component, WCD939X_MBHC_CTL_CLK, reg3);
+
+ if (is_fsm_disable)
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT,
+ WCD939X_MBHC_ELECT_FSM_EN, true);
+}
+
+static void wcd939x_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN,
+ true);
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_GND_DET_EN, true);
+ } else {
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_GND_DET_EN, false);
+ snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH,
+ WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN,
+ false);
+ }
+}
+
+static void wcd939x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+ WCD939X_PA_CTL2_HPHPA_GND_R, enable);
+ snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2,
+ WCD939X_PA_CTL2_HPHPA_GND_L, enable);
+}
+
+static void wcd939x_mbhc_moisture_config(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) {
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+ return;
+ }
+
+ /* Do not enable moisture detection if jack type is NC */
+ if (!wcd939x->mbhc_cfg.hphl_swh) {
+ dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+ return;
+ }
+
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL, wcd939x->mbhc_cfg.moist_rref);
+}
+
+static void wcd939x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ if (enable)
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL,
+ wcd939x->mbhc_cfg.moist_rref);
+ else
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+}
+
+static bool wcd939x_mbhc_get_moisture_status(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ bool ret = false;
+
+ if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) {
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+ goto done;
+ }
+
+ /* Do not enable moisture detection if jack type is NC */
+ if (!wcd939x->mbhc_cfg.hphl_swh) {
+ dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL, R_OFF);
+ goto done;
+ }
+
+ /*
+ * If moisture_en is already enabled, then skip to plug type
+ * detection.
+ */
+ if (snd_soc_component_read_field(component, WCD939X_MBHC_NEW_CTL_2,
+ WCD939X_CTL_2_M_RTH_CTL))
+ goto done;
+
+ wcd939x_mbhc_moisture_detect_en(component, true);
+
+ /* Read moisture comparator status, invert of status bit */
+ ret = !snd_soc_component_read_field(component, WCD939X_MBHC_NEW_FSM_STATUS,
+ WCD939X_FSM_STATUS_HS_M_COMP_STATUS);
+done:
+ return ret;
+}
+
+static void wcd939x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component,
+ WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL,
+ WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING,
+ enable);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .clk_setup = wcd939x_mbhc_clk_setup,
+ .mbhc_bias = wcd939x_mbhc_mbhc_bias_control,
+ .set_btn_thr = wcd939x_mbhc_program_btn_thr,
+ .micbias_enable_status = wcd939x_mbhc_micb_en_status,
+ .hph_pull_up_control_v2 = wcd939x_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = wcd939x_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = wcd939x_mbhc_micb_ramp_control,
+ .mbhc_micb_ctrl_thr_mic = wcd939x_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = wcd939x_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = wcd939x_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = wcd939x_mbhc_hph_pull_down_ctrl,
+ .mbhc_moisture_config = wcd939x_mbhc_moisture_config,
+ .mbhc_get_moisture_status = wcd939x_mbhc_get_moisture_status,
+ .mbhc_moisture_polling_ctrl = wcd939x_mbhc_moisture_polling_ctrl,
+ .mbhc_moisture_detect_en = wcd939x_mbhc_moisture_detect_en,
+};
+
+static int wcd939x_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd939x->wcd_mbhc);
+
+ return 0;
+}
+
+static int wcd939x_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ bool hphr = mc->shift;
+ u32 zl, zr;
+
+ wcd_mbhc_get_impedance(wcd939x->wcd_mbhc, &zl, &zr);
+ dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+ wcd939x_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ wcd939x_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ wcd939x_hph_impedance_get, NULL),
+};
+
+static int wcd939x_mbhc_init(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &wcd939x->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_MBHC_SW_DET);
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_MBHC_BUTTON_PRESS_DET);
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET);
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_MBHC_ELECT_INS_REM_DET);
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_HPHL_OCP_INT);
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_HPHR_OCP_INT);
+
+ wcd939x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+ if (IS_ERR(wcd939x->wcd_mbhc))
+ return PTR_ERR(wcd939x->wcd_mbhc);
+
+ snd_soc_add_component_controls(component, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_component_controls(component, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ return 0;
+}
+
+static void wcd939x_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(wcd939x->wcd_mbhc);
+}
+
+/* END MBHC */
+
+static const struct snd_kcontrol_new wcd939x_snd_controls[] = {
+ /* RX Path */
+ SOC_SINGLE_EXT("HPHL_COMP Switch", WCD939X_COMP_L, 0, 1, 0,
+ wcd939x_get_compander, wcd939x_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", WCD939X_COMP_R, 1, 1, 0,
+ wcd939x_get_compander, wcd939x_set_compander),
+ SOC_SINGLE_EXT("HPHL Switch", WCD939X_HPH_L, 0, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", WCD939X_HPH_R, 0, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("CLSH Switch", WCD939X_CLSH, 0, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("LO Switch", WCD939X_LO, 0, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DSD_L Switch", WCD939X_DSD_L, 0, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DSD_R Switch", WCD939X_DSD_R, 0, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_TLV("HPHL Volume", WCD939X_HPH_L_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD939X_HPH_R_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0,
+ wcd939x_ldoh_get, wcd939x_ldoh_put),
+
+ /* TX Path */
+ SOC_SINGLE_EXT("ADC1 Switch", WCD939X_ADC1, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", WCD939X_ADC2, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("ADC3 Switch", WCD939X_ADC3, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("ADC4 Switch", WCD939X_ADC4, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC0 Switch", WCD939X_DMIC0, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC1 Switch", WCD939X_DMIC1, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("MBHC Switch", WCD939X_MBHC, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC2 Switch", WCD939X_DMIC2, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC3 Switch", WCD939X_DMIC3, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC4 Switch", WCD939X_DMIC4, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC5 Switch", WCD939X_DMIC5, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC6 Switch", WCD939X_DMIC6, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_EXT("DMIC7 Switch", WCD939X_DMIC7, 1, 1, 0,
+ wcd939x_get_swr_port, wcd939x_set_swr_port),
+ SOC_SINGLE_TLV("ADC1 Volume", WCD939X_ANA_TX_CH1, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD939X_ANA_TX_CH2, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD939X_ANA_TX_CH3, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD939X_ANA_TX_CH4, 0, 20, 0,
+ analog_gain),
+};
+
+static const struct snd_soc_dapm_widget wcd939x_dapm_widgets[] = {
+ /*input widgets*/
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC4"),
+ SND_SOC_DAPM_INPUT("AMIC5"),
+
+ SND_SOC_DAPM_MIC("Analog Mic1", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic2", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic3", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd939x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0,
+ wcd939x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0,
+ wcd939x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0,
+ wcd939x_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0,
+ wcd939x_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wcd939x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0, NULL, 0,
+ wcd939x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0, NULL, 0,
+ wcd939x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0,
+ wcd939x_adc_enable_req,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC1 MUX", SND_SOC_NOPM, 0, 0, &tx_adc1_mux),
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+ SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux),
+ SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux),
+
+ /* tx mixers */
+ SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0,
+ adc1_switch, ARRAY_SIZE(adc1_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0,
+ adc2_switch, ARRAY_SIZE(adc2_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0,
+ adc3_switch, ARRAY_SIZE(adc3_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0,
+ adc4_switch, ARRAY_SIZE(adc4_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic7_switch, ARRAY_SIZE(dmic7_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0,
+ dmic8_switch, ARRAY_SIZE(dmic8_switch), wcd939x_tx_swr_ctrl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* micbias widgets */
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd939x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd939x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd939x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+ wcd939x_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* micbias pull up widgets */
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd939x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd939x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd939x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+ wcd939x_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* output widgets tx */
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"),
+
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+ SND_SOC_DAPM_INPUT("IN3_EAR"),
+
+ /* rx widgets */
+ SND_SOC_DAPM_PGA_E("EAR PGA", WCD939X_ANA_EAR, 7, 0, NULL, 0,
+ wcd939x_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", WCD939X_ANA_HPH, 7, 0, NULL, 0,
+ wcd939x_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", WCD939X_ANA_HPH, 6, 0, NULL, 0,
+ wcd939x_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0,
+ wcd939x_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0,
+ wcd939x_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0,
+ wcd939x_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux),
+
+ SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0,
+ wcd939x_codec_enable_rxclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+ /* rx mixer widgets */
+ SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0,
+ ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0,
+ hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0,
+ hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)),
+
+ /* output widgets rx */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+};
+
+static const struct snd_soc_dapm_route wcd939x_audio_map[] = {
+ /* TX Path */
+ {"ADC1_OUTPUT", NULL, "ADC1_MIXER"},
+ {"ADC1_MIXER", "Switch", "ADC1 REQ"},
+ {"ADC1 REQ", NULL, "ADC1"},
+ {"ADC1", NULL, "ADC1 MUX"},
+ {"ADC1 MUX", "CH1_AMIC1", "AMIC1"},
+ {"ADC1 MUX", "CH1_AMIC2", "AMIC2"},
+ {"ADC1 MUX", "CH1_AMIC3", "AMIC3"},
+ {"ADC1 MUX", "CH1_AMIC4", "AMIC4"},
+ {"ADC1 MUX", "CH1_AMIC5", "AMIC5"},
+
+ {"ADC2_OUTPUT", NULL, "ADC2_MIXER"},
+ {"ADC2_MIXER", "Switch", "ADC2 REQ"},
+ {"ADC2 REQ", NULL, "ADC2"},
+ {"ADC2", NULL, "ADC2 MUX"},
+ {"ADC2 MUX", "CH2_AMIC1", "AMIC1"},
+ {"ADC2 MUX", "CH2_AMIC2", "AMIC2"},
+ {"ADC2 MUX", "CH2_AMIC3", "AMIC3"},
+ {"ADC2 MUX", "CH2_AMIC4", "AMIC4"},
+ {"ADC2 MUX", "CH2_AMIC5", "AMIC5"},
+
+ {"ADC3_OUTPUT", NULL, "ADC3_MIXER"},
+ {"ADC3_MIXER", "Switch", "ADC3 REQ"},
+ {"ADC3 REQ", NULL, "ADC3"},
+ {"ADC3", NULL, "ADC3 MUX"},
+ {"ADC3 MUX", "CH3_AMIC1", "AMIC1"},
+ {"ADC3 MUX", "CH3_AMIC3", "AMIC3"},
+ {"ADC3 MUX", "CH3_AMIC4", "AMIC4"},
+ {"ADC3 MUX", "CH3_AMIC5", "AMIC5"},
+
+ {"ADC4_OUTPUT", NULL, "ADC4_MIXER"},
+ {"ADC4_MIXER", "Switch", "ADC4 REQ"},
+ {"ADC4 REQ", NULL, "ADC4"},
+ {"ADC4", NULL, "ADC4 MUX"},
+ {"ADC4 MUX", "CH4_AMIC1", "AMIC1"},
+ {"ADC4 MUX", "CH4_AMIC3", "AMIC3"},
+ {"ADC4 MUX", "CH4_AMIC4", "AMIC4"},
+ {"ADC4 MUX", "CH4_AMIC5", "AMIC5"},
+
+ {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"},
+ {"DMIC1_MIXER", "Switch", "DMIC1"},
+
+ {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"},
+ {"DMIC2_MIXER", "Switch", "DMIC2"},
+
+ {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"},
+ {"DMIC3_MIXER", "Switch", "DMIC3"},
+
+ {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"},
+ {"DMIC4_MIXER", "Switch", "DMIC4"},
+
+ {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"},
+ {"DMIC5_MIXER", "Switch", "DMIC5"},
+
+ {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"},
+ {"DMIC6_MIXER", "Switch", "DMIC6"},
+
+ {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"},
+ {"DMIC7_MIXER", "Switch", "DMIC7"},
+
+ {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"},
+ {"DMIC8_MIXER", "Switch", "DMIC8"},
+
+ /* RX Path */
+ {"IN1_HPHL", NULL, "VDD_BUCK"},
+ {"IN1_HPHL", NULL, "CLS_H_PORT"},
+
+ {"RX1", NULL, "IN1_HPHL"},
+ {"RX1", NULL, "RXCLK"},
+ {"RDAC1", NULL, "RX1"},
+ {"HPHL_RDAC", "Switch", "RDAC1"},
+ {"HPHL PGA", NULL, "HPHL_RDAC"},
+ {"HPHL", NULL, "HPHL PGA"},
+
+ {"IN2_HPHR", NULL, "VDD_BUCK"},
+ {"IN2_HPHR", NULL, "CLS_H_PORT"},
+ {"RX2", NULL, "IN2_HPHR"},
+ {"RDAC2", NULL, "RX2"},
+ {"RX2", NULL, "RXCLK"},
+ {"HPHR_RDAC", "Switch", "RDAC2"},
+ {"HPHR PGA", NULL, "HPHR_RDAC"},
+ {"HPHR", NULL, "HPHR PGA"},
+
+ {"IN3_EAR", NULL, "VDD_BUCK"},
+ {"RX3", NULL, "IN3_EAR"},
+ {"RX3", NULL, "RXCLK"},
+
+ {"RDAC3_MUX", "RX3", "RX3"},
+ {"RDAC3_MUX", "RX1", "RX1"},
+ {"RDAC3", NULL, "RDAC3_MUX"},
+ {"EAR_RDAC", "Switch", "RDAC3"},
+ {"EAR PGA", NULL, "EAR_RDAC"},
+ {"EAR", NULL, "EAR PGA"},
+};
+
+static void wcd939x_set_micbias_data(struct device *dev, struct wcd939x_priv *wcd939x)
+{
+ regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1,
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[0]);
+ regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2,
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[1]);
+ regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3,
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[2]);
+ regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4,
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[3]);
+}
+
+static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data)
+{
+ /*
+ * HPHR/HPHL/EAR Watchdog interrupt threaded handler
+ *
+ * Watchdog interrupts are expected to be enabled when switching
+ * on the HPHL/R and EAR RX PGA in order to make sure the interrupts
+ * are acked by the regmap_irq handler to allow PDM sync.
+ * We could leave those interrupts masked but we would not have
+ * any valid way to enable/disable them without violating irq layers.
+ *
+ * The HPHR/HPHL/EAR Watchdog interrupts are handled
+ * by regmap_irq, so requesting a threaded handler is the
+ * safest way to be able to ack those interrupts without
+ * colliding with the regmap_irq setup.
+ */
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Setup a virtual interrupt domain to hook regmap_irq
+ * The root domain will have a single interrupt which mapping
+ * will trigger the regmap_irq handler.
+ *
+ * root:
+ * wcd_irq_chip
+ * [0] wcd939x_regmap_irq_chip
+ * [0] MBHC_BUTTON_PRESS_DET
+ * [1] MBHC_BUTTON_RELEASE_DET
+ * ...
+ * [16] HPHR_SURGE_DET_INT
+ *
+ * Interrupt trigger:
+ * soundwire_interrupt_callback()
+ * \-handle_nested_irq(0)
+ * \- regmap_irq_thread()
+ * \- handle_nested_irq(i)
+ */
+static const struct irq_chip wcd_irq_chip = {
+ .name = "WCD939x",
+};
+
+static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops wcd_domain_ops = {
+ .map = wcd_irq_chip_map,
+};
+
+static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev)
+{
+ wcd->virq = irq_domain_create_linear(NULL, 1, &wcd_domain_ops, NULL);
+ if (!(wcd->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ return devm_regmap_add_irq_chip(dev, wcd->regmap,
+ irq_create_mapping(wcd->virq, 0),
+ IRQF_ONESHOT, 0, &wcd939x_regmap_irq_chip,
+ &wcd->irq_chip);
+}
+
+static int wcd939x_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ int ret, i;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(2000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, wcd939x->regmap);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ wcd939x->variant = snd_soc_component_read_field(component,
+ WCD939X_DIGITAL_EFUSE_REG_0,
+ WCD939X_EFUSE_REG_0_WCD939X_ID);
+
+ wcd939x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD939X);
+ if (IS_ERR(wcd939x->clsh_info)) {
+ pm_runtime_put(dev);
+ return PTR_ERR(wcd939x->clsh_info);
+ }
+
+ wcd939x_io_init(component);
+
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < wcd939x_regmap_irq_chip.num_regs; i++)
+ regmap_write(wcd939x->regmap,
+ (WCD939X_DIGITAL_INTR_LEVEL_0 + i), 0);
+
+ pm_runtime_put(dev);
+
+ /* Request for watchdog interrupt */
+ wcd939x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_HPHR_PDM_WD_INT);
+ wcd939x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_HPHL_PDM_WD_INT);
+ wcd939x->ear_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip,
+ WCD939X_IRQ_EAR_PDM_WD_INT);
+
+ ret = request_threaded_irq(wcd939x->hphr_pdm_wd_int, NULL, wcd939x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WD INT", wcd939x);
+ if (ret) {
+ dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret);
+ goto err_free_clsh_ctrl;
+ }
+
+ ret = request_threaded_irq(wcd939x->hphl_pdm_wd_int, NULL, wcd939x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WD INT", wcd939x);
+ if (ret) {
+ dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret);
+ goto err_free_hphr_pdm_wd_int;
+ }
+
+ ret = request_threaded_irq(wcd939x->ear_pdm_wd_int, NULL, wcd939x_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "AUX PDM WD INT", wcd939x);
+ if (ret) {
+ dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret);
+ goto err_free_hphl_pdm_wd_int;
+ }
+
+ /* Disable watchdog interrupt for HPH and AUX */
+ disable_irq_nosync(wcd939x->hphr_pdm_wd_int);
+ disable_irq_nosync(wcd939x->hphl_pdm_wd_int);
+ disable_irq_nosync(wcd939x->ear_pdm_wd_int);
+
+ switch (wcd939x->variant) {
+ case CHIPID_WCD9390:
+ ret = snd_soc_add_component_controls(component, wcd9390_snd_controls,
+ ARRAY_SIZE(wcd9390_snd_controls));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add snd ctrls for variant: %d\n",
+ __func__, wcd939x->variant);
+ goto err_free_ear_pdm_wd_int;
+ }
+ break;
+ case CHIPID_WCD9395:
+ ret = snd_soc_add_component_controls(component, wcd9395_snd_controls,
+ ARRAY_SIZE(wcd9395_snd_controls));
+ if (ret < 0) {
+ dev_err(component->dev,
+ "%s: Failed to add snd ctrls for variant: %d\n",
+ __func__, wcd939x->variant);
+ goto err_free_ear_pdm_wd_int;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = wcd939x_mbhc_init(component);
+ if (ret) {
+ dev_err(component->dev, "mbhc initialization failed\n");
+ goto err_free_ear_pdm_wd_int;
+ }
+
+ return 0;
+
+err_free_ear_pdm_wd_int:
+ free_irq(wcd939x->ear_pdm_wd_int, wcd939x);
+err_free_hphl_pdm_wd_int:
+ free_irq(wcd939x->hphl_pdm_wd_int, wcd939x);
+err_free_hphr_pdm_wd_int:
+ free_irq(wcd939x->hphr_pdm_wd_int, wcd939x);
+err_free_clsh_ctrl:
+ wcd_clsh_ctrl_free(wcd939x->clsh_info);
+
+ return ret;
+}
+
+static void wcd939x_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ wcd939x_mbhc_deinit(component);
+
+ free_irq(wcd939x->ear_pdm_wd_int, wcd939x);
+ free_irq(wcd939x->hphl_pdm_wd_int, wcd939x);
+ free_irq(wcd939x->hphr_pdm_wd_int, wcd939x);
+
+ wcd_clsh_ctrl_free(wcd939x->clsh_info);
+}
+
+static int wcd939x_codec_set_jack(struct snd_soc_component *comp,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct wcd939x_priv *wcd = dev_get_drvdata(comp->dev);
+
+ if (jack)
+ return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack);
+
+ wcd_mbhc_stop(wcd->wcd_mbhc);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_wcd939x = {
+ .name = "wcd939x_codec",
+ .probe = wcd939x_soc_codec_probe,
+ .remove = wcd939x_soc_codec_remove,
+ .controls = wcd939x_snd_controls,
+ .num_controls = ARRAY_SIZE(wcd939x_snd_controls),
+ .dapm_widgets = wcd939x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wcd939x_dapm_widgets),
+ .dapm_routes = wcd939x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wcd939x_audio_map),
+ .set_jack = wcd939x_codec_set_jack,
+ .endianness = 1,
+};
+
+#if IS_ENABLED(CONFIG_TYPEC)
+/* Get USB-C plug orientation to provide swap event for MBHC */
+static int wcd939x_typec_switch_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct wcd939x_priv *wcd939x = typec_switch_get_drvdata(sw);
+
+ wcd939x->typec_orientation = orientation;
+
+ return 0;
+}
+
+static int wcd939x_typec_mux_set(struct typec_mux_dev *mux,
+ struct typec_mux_state *state)
+{
+ struct wcd939x_priv *wcd939x = typec_mux_get_drvdata(mux);
+ unsigned int previous_mode = wcd939x->typec_mode;
+
+ if (!wcd939x->wcd_mbhc)
+ return -EINVAL;
+
+ if (wcd939x->typec_mode != state->mode) {
+ wcd939x->typec_mode = state->mode;
+
+ if (wcd939x->typec_mode == TYPEC_MODE_AUDIO)
+ return wcd_mbhc_typec_report_plug(wcd939x->wcd_mbhc);
+ else if (previous_mode == TYPEC_MODE_AUDIO)
+ return wcd_mbhc_typec_report_unplug(wcd939x->wcd_mbhc);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_TYPEC */
+
+#if IS_ENABLED(CONFIG_TYPEC)
+static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component)
+{
+ struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
+
+ if (!wcd939x->typec_analog_mux || !wcd939x->typec_switch)
+ return false;
+
+ /* Report inversion via Type Switch of USBSS */
+ typec_switch_set(wcd939x->typec_switch,
+ wcd939x->typec_orientation == TYPEC_ORIENTATION_REVERSE ?
+ TYPEC_ORIENTATION_NORMAL : TYPEC_ORIENTATION_REVERSE);
+
+ return true;
+}
+#endif /* CONFIG_TYPEC */
+
+static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device *dev)
+{
+ struct wcd_mbhc_config *cfg = &wcd939x->mbhc_cfg;
+#if IS_ENABLED(CONFIG_TYPEC)
+ struct device_node *np;
+#endif /* CONFIG_TYPEC */
+ int ret;
+
+ wcd939x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd939x->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd939x->reset_gpio),
+ "Failed to get reset gpio\n");
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd939x_supplies),
+ wcd939x_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ ret = wcd_dt_parse_micbias_info(&wcd939x->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get micbias\n");
+
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = WCD939X_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = wcd939x->common.micb_mv[1];
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, cfg);
+
+#if IS_ENABLED(CONFIG_TYPEC)
+ /*
+ * Is node has a port and a valid remote endpoint
+ * consider HP lines are connected to the USBSS part
+ */
+ np = of_graph_get_remote_node(dev->of_node, 0, 0);
+ if (np) {
+ wcd939x->typec_analog_mux = true;
+ cfg->typec_analog_mux = true;
+ cfg->swap_gnd_mic = wcd939x_swap_gnd_mic;
+ }
+#endif /* CONFIG_TYPEC */
+
+ return 0;
+}
+
+static int wcd939x_reset(struct wcd939x_priv *wcd939x)
+{
+ gpiod_set_value(wcd939x->reset_gpio, 1);
+ /* 20us sleep required after pulling the reset gpio to LOW */
+ usleep_range(20, 30);
+ gpiod_set_value(wcd939x->reset_gpio, 0);
+ /* 20us sleep required after pulling the reset gpio to HIGH */
+ usleep_range(20, 30);
+
+ return 0;
+}
+
+static int wcd939x_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev);
+ struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id];
+
+ return wcd939x_sdw_hw_params(wcd, substream, params, dai);
+}
+
+static int wcd939x_codec_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev);
+ struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id];
+
+ return wcd939x_sdw_free(wcd, substream, dai);
+}
+
+static int wcd939x_codec_set_sdw_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev);
+ struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id];
+
+ return wcd939x_sdw_set_sdw_stream(wcd, dai, stream, direction);
+}
+
+static const struct snd_soc_dai_ops wcd939x_sdw_dai_ops = {
+ .hw_params = wcd939x_codec_hw_params,
+ .hw_free = wcd939x_codec_free,
+ .set_stream = wcd939x_codec_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver wcd939x_dais[] = {
+ [0] = {
+ .name = "wcd939x-sdw-rx",
+ .playback = {
+ .stream_name = "WCD AIF1 Playback",
+ .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK,
+ .formats = WCD939X_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wcd939x_sdw_dai_ops,
+ },
+ [1] = {
+ .name = "wcd939x-sdw-tx",
+ .capture = {
+ .stream_name = "WCD AIF1 Capture",
+ .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK,
+ .formats = WCD939X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wcd939x_sdw_dai_ops,
+ },
+};
+
+static int wcd939x_bind(struct device *dev)
+{
+ struct wcd939x_priv *wcd939x = dev_get_drvdata(dev);
+ unsigned int version, id1, status1;
+ int ret;
+
+#if IS_ENABLED(CONFIG_TYPEC)
+ /*
+ * Get USBSS type-c switch to send gnd/mic swap events
+ * typec_switch is fetched now to avoid a probe deadlock since
+ * the USBSS depends on the typec_mux register in wcd939x_probe()
+ */
+ if (wcd939x->typec_analog_mux) {
+ wcd939x->typec_switch = fwnode_typec_switch_get(dev->fwnode);
+ if (IS_ERR(wcd939x->typec_switch))
+ return dev_err_probe(dev, PTR_ERR(wcd939x->typec_switch),
+ "failed to acquire orientation-switch\n");
+ }
+#endif /* CONFIG_TYPEC */
+
+ ret = component_bind_all(dev, wcd939x);
+ if (ret) {
+ dev_err(dev, "%s: Slave bind failed, ret = %d\n",
+ __func__, ret);
+ goto err_put_typec_switch;
+ }
+
+ wcd939x->rxdev = of_sdw_find_device_by_node(wcd939x->rxnode);
+ if (!wcd939x->rxdev) {
+ dev_err(dev, "could not find slave with matching of node\n");
+ ret = -EINVAL;
+ goto err_unbind;
+ }
+ wcd939x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd939x->rxdev);
+ wcd939x->sdw_priv[AIF1_PB]->wcd939x = wcd939x;
+
+ wcd939x->txdev = of_sdw_find_device_by_node(wcd939x->txnode);
+ if (!wcd939x->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto err_put_rxdev;
+ }
+ wcd939x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd939x->txdev);
+ wcd939x->sdw_priv[AIF1_CAP]->wcd939x = wcd939x;
+ wcd939x->tx_sdw_dev = dev_to_sdw_dev(wcd939x->txdev);
+
+ /*
+ * As TX is main CSR reg interface, which should not be suspended first.
+ * explicitly add the dependency link
+ */
+ if (!device_link_add(wcd939x->rxdev, wcd939x->txdev, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "could not devlink tx and rx\n");
+ ret = -EINVAL;
+ goto err_put_txdev;
+ }
+
+ if (!device_link_add(dev, wcd939x->txdev, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "could not devlink wcd and tx\n");
+ ret = -EINVAL;
+ goto err_remove_rxtx_link;
+ }
+
+ if (!device_link_add(dev, wcd939x->rxdev, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME)) {
+ dev_err(dev, "could not devlink wcd and rx\n");
+ ret = -EINVAL;
+ goto err_remove_tx_link;
+ }
+
+ /* Get regmap from TX SoundWire device */
+ wcd939x->regmap = wcd939x->sdw_priv[AIF1_CAP]->regmap;
+ if (!wcd939x->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -ENODEV;
+ goto err_remove_rx_link;
+ }
+
+ ret = wcd939x_irq_init(wcd939x, dev);
+ if (ret) {
+ dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret);
+ goto err_remove_rx_link;
+ }
+
+ wcd939x->sdw_priv[AIF1_PB]->slave_irq = wcd939x->virq;
+ wcd939x->sdw_priv[AIF1_CAP]->slave_irq = wcd939x->virq;
+
+ wcd939x_set_micbias_data(dev, wcd939x);
+
+ /* Check WCD9395 version */
+ regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1);
+ regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1);
+
+ if (id1 == CHIPID_WCD939X_VER_MAJOR_1)
+ version = ((status1 & CHIPID_WCD939X_VER_MINOR_1) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0);
+ else
+ version = WCD939X_VERSION_2_0;
+
+ dev_dbg(dev, "wcd939x version: %s\n", version_to_str(version));
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_wcd939x,
+ wcd939x_dais, ARRAY_SIZE(wcd939x_dais));
+ if (ret) {
+ dev_err(dev, "%s: Codec registration failed\n",
+ __func__);
+ goto err_remove_rx_link;
+ }
+
+ return 0;
+
+err_remove_rx_link:
+ device_link_remove(dev, wcd939x->rxdev);
+err_remove_tx_link:
+ device_link_remove(dev, wcd939x->txdev);
+err_remove_rxtx_link:
+ device_link_remove(wcd939x->rxdev, wcd939x->txdev);
+err_put_txdev:
+ put_device(wcd939x->txdev);
+err_put_rxdev:
+ put_device(wcd939x->rxdev);
+err_unbind:
+ component_unbind_all(dev, wcd939x);
+err_put_typec_switch:
+#if IS_ENABLED(CONFIG_TYPEC)
+ if (wcd939x->typec_analog_mux)
+ typec_switch_put(wcd939x->typec_switch);
+#endif /* CONFIG_TYPEC */
+
+ return ret;
+}
+
+static void wcd939x_unbind(struct device *dev)
+{
+ struct wcd939x_priv *wcd939x = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ device_link_remove(dev, wcd939x->txdev);
+ device_link_remove(dev, wcd939x->rxdev);
+ device_link_remove(wcd939x->rxdev, wcd939x->txdev);
+ put_device(wcd939x->txdev);
+ put_device(wcd939x->rxdev);
+ component_unbind_all(dev, wcd939x);
+}
+
+static const struct component_master_ops wcd939x_comp_ops = {
+ .bind = wcd939x_bind,
+ .unbind = wcd939x_unbind,
+};
+
+static void __maybe_unused wcd939x_typec_mux_unregister(void *data)
+{
+ struct typec_mux_dev *typec_mux = data;
+
+ typec_mux_unregister(typec_mux);
+}
+
+static void __maybe_unused wcd939x_typec_switch_unregister(void *data)
+{
+ struct typec_switch_dev *typec_sw = data;
+
+ typec_switch_unregister(typec_sw);
+}
+
+static int wcd939x_add_typec(struct wcd939x_priv *wcd939x, struct device *dev)
+{
+#if IS_ENABLED(CONFIG_TYPEC)
+ int ret;
+ struct typec_mux_dev *typec_mux;
+ struct typec_switch_dev *typec_sw;
+ struct typec_mux_desc mux_desc = {
+ .drvdata = wcd939x,
+ .fwnode = dev_fwnode(dev),
+ .set = wcd939x_typec_mux_set,
+ };
+ struct typec_switch_desc sw_desc = {
+ .drvdata = wcd939x,
+ .fwnode = dev_fwnode(dev),
+ .set = wcd939x_typec_switch_set,
+ };
+
+ /*
+ * Is USBSS is used to mux analog lines,
+ * register a typec mux/switch to get typec events
+ */
+ if (!wcd939x->typec_analog_mux)
+ return 0;
+
+ typec_mux = typec_mux_register(dev, &mux_desc);
+ if (IS_ERR(typec_mux))
+ return dev_err_probe(dev, PTR_ERR(typec_mux),
+ "failed to register typec mux\n");
+
+ ret = devm_add_action_or_reset(dev, wcd939x_typec_mux_unregister,
+ typec_mux);
+ if (ret)
+ return ret;
+
+ typec_sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(typec_sw))
+ return dev_err_probe(dev, PTR_ERR(typec_sw),
+ "failed to register typec switch\n");
+
+ ret = devm_add_action_or_reset(dev, wcd939x_typec_switch_unregister,
+ typec_sw);
+ if (ret)
+ return ret;
+#endif
+
+ return 0;
+}
+
+static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x,
+ struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np = dev->of_node;
+
+ wcd939x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!wcd939x->rxnode) {
+ dev_err(dev, "%s: Rx-device node not defined\n", __func__);
+ return -ENODEV;
+ }
+
+ of_node_get(wcd939x->rxnode);
+ component_match_add_release(dev, matchptr, component_release_of,
+ component_compare_of, wcd939x->rxnode);
+
+ wcd939x->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!wcd939x->txnode) {
+ dev_err(dev, "%s: Tx-device node not defined\n", __func__);
+ return -ENODEV;
+ }
+ of_node_get(wcd939x->txnode);
+ component_match_add_release(dev, matchptr, component_release_of,
+ component_compare_of, wcd939x->txnode);
+ return 0;
+}
+
+static int wcd939x_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct wcd939x_priv *wcd939x = NULL;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ wcd939x = devm_kzalloc(dev, sizeof(struct wcd939x_priv),
+ GFP_KERNEL);
+ if (!wcd939x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, wcd939x);
+ mutex_init(&wcd939x->micb_lock);
+ wcd939x->common.dev = dev;
+ wcd939x->common.max_bias = 4;
+
+ ret = wcd939x_populate_dt_data(wcd939x, dev);
+ if (ret) {
+ dev_err(dev, "%s: Fail to obtain platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = wcd939x_add_typec(wcd939x, dev);
+ if (ret)
+ return ret;
+
+ ret = wcd939x_add_slave_components(wcd939x, dev, &match);
+ if (ret)
+ return ret;
+
+ wcd939x_reset(wcd939x);
+
+ ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void wcd939x_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_master_del(dev, &wcd939x_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id wcd939x_dt_match[] = {
+ { .compatible = "qcom,wcd9390-codec" },
+ { .compatible = "qcom,wcd9395-codec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wcd939x_dt_match);
+#endif
+
+static struct platform_driver wcd939x_codec_driver = {
+ .probe = wcd939x_probe,
+ .remove = wcd939x_remove,
+ .driver = {
+ .name = "wcd939x_codec",
+ .of_match_table = of_match_ptr(wcd939x_dt_match),
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(wcd939x_codec_driver);
+MODULE_DESCRIPTION("WCD939X Codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h
new file mode 100644
index 000000000000..6bd2366587a8
--- /dev/null
+++ b/sound/soc/codecs/wcd939x.h
@@ -0,0 +1,947 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __WCD939X_H__
+#define __WCD939X_H__
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define WCD939X_BASE (0x3000)
+#define WCD939X_ANA_PAGE (0x3000)
+#define WCD939X_ANA_BIAS (0x3001)
+#define WCD939X_BIAS_ANALOG_BIAS_EN BIT(7)
+#define WCD939X_BIAS_PRECHRG_EN BIT(6)
+#define WCD939X_BIAS_PRECHRG_CTL_MODE BIT(5)
+#define WCD939X_ANA_RX_SUPPLIES (0x3008)
+#define WCD939X_RX_SUPPLIES_VPOS_EN BIT(7)
+#define WCD939X_RX_SUPPLIES_VNEG_EN BIT(6)
+#define WCD939X_RX_SUPPLIES_VPOS_PWR_LVL BIT(3)
+#define WCD939X_RX_SUPPLIES_VNEG_PWR_LVL BIT(2)
+#define WCD939X_RX_SUPPLIES_REGULATOR_MODE BIT(1)
+#define WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE BIT(0)
+#define WCD939X_ANA_HPH (0x3009)
+#define WCD939X_HPH_HPHL_ENABLE BIT(7)
+#define WCD939X_HPH_HPHR_ENABLE BIT(6)
+#define WCD939X_HPH_HPHL_REF_ENABLE BIT(5)
+#define WCD939X_HPH_HPHR_REF_ENABLE BIT(4)
+#define WCD939X_HPH_PWR_LEVEL GENMASK(3, 2)
+#define WCD939X_ANA_EAR (0x300a)
+#define WCD939X_ANA_EAR_COMPANDER_CTL (0x300b)
+#define WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG BIT(7)
+#define WCD939X_EAR_COMPANDER_CTL_EAR_GAIN GENMASK(6, 2)
+#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_BYP BIT(1)
+#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_CLK_EDGE BIT(0)
+#define WCD939X_ANA_TX_CH1 (0x300e)
+#define WCD939X_ANA_TX_CH2 (0x300f)
+#define WCD939X_TX_CH2_ENABLE BIT(7)
+#define WCD939X_TX_CH2_HPF1_INIT BIT(6)
+#define WCD939X_TX_CH2_HPF2_INIT BIT(5)
+#define WCD939X_TX_CH2_GAIN GENMASK(4, 0)
+#define WCD939X_ANA_TX_CH3 (0x3010)
+#define WCD939X_ANA_TX_CH4 (0x3011)
+#define WCD939X_TX_CH4_ENABLE BIT(7)
+#define WCD939X_TX_CH4_HPF3_INIT BIT(6)
+#define WCD939X_TX_CH4_HPF4_INIT BIT(5)
+#define WCD939X_TX_CH4_GAIN GENMASK(4, 0)
+#define WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC (0x3012)
+#define WCD939X_ANA_MICB3_DSP_EN_LOGIC (0x3013)
+#define WCD939X_ANA_MBHC_MECH (0x3014)
+#define WCD939X_MBHC_MECH_L_DET_EN BIT(7)
+#define WCD939X_MBHC_MECH_GND_DET_EN BIT(6)
+#define WCD939X_MBHC_MECH_MECH_DETECT_TYPE BIT(5)
+#define WCD939X_MBHC_MECH_HPHL_PLUG_TYPE BIT(4)
+#define WCD939X_MBHC_MECH_GND_PLUG_TYPE BIT(3)
+#define WCD939X_MBHC_MECH_MECH_HS_L_PULLUP_COMP_EN BIT(2)
+#define WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN BIT(1)
+#define WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND BIT(0)
+#define WCD939X_ANA_MBHC_ELECT (0x3015)
+#define WCD939X_MBHC_ELECT_FSM_EN BIT(7)
+#define WCD939X_MBHC_ELECT_BTNDET_ISRC_CTL GENMASK(6, 4)
+#define WCD939X_MBHC_ELECT_ELECT_DET_TYPE BIT(3)
+#define WCD939X_MBHC_ELECT_ELECT_SCHMT_ISRC_CTL GENMASK(2, 1)
+#define WCD939X_MBHC_ELECT_BIAS_EN BIT(0)
+#define WCD939X_ANA_MBHC_ZDET (0x3016)
+#define WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN BIT(7)
+#define WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN BIT(6)
+#define WCD939X_MBHC_ZDET_ZDET_CHG_EN BIT(5)
+#define WCD939X_MBHC_ZDET_ZDET_ILEAK_COMP_EN BIT(4)
+#define WCD939X_MBHC_ZDET_ELECT_ISRC_EN BIT(1)
+#define WCD939X_ANA_MBHC_RESULT_1 (0x3017)
+#define WCD939X_MBHC_RESULT_1_Z_RESULT_LSB GENMASK(7, 0)
+#define WCD939X_ANA_MBHC_RESULT_2 (0x3018)
+#define WCD939X_MBHC_RESULT_2_Z_RESULT_MSB GENMASK(7, 0)
+#define WCD939X_ANA_MBHC_RESULT_3 (0x3019)
+#define WCD939X_ANA_MBHC_BTN0 (0x301a)
+#define WCD939X_MBHC_BTN0_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN1 (0x301b)
+#define WCD939X_MBHC_BTN1_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN2 (0x301c)
+#define WCD939X_MBHC_BTN2_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN3 (0x301d)
+#define WCD939X_MBHC_BTN3_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN4 (0x301e)
+#define WCD939X_MBHC_BTN4_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN5 (0x301f)
+#define WCD939X_MBHC_BTN5_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN6 (0x3020)
+#define WCD939X_MBHC_BTN6_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MBHC_BTN7 (0x3021)
+#define WCD939X_MBHC_BTN7_VTH GENMASK(7, 2)
+#define WCD939X_ANA_MICB1 (0x3022)
+#define WCD939X_MICB_ENABLE GENMASK(7, 6)
+#define WCD939X_MICB_VOUT_CTL GENMASK(5, 0)
+#define WCD939X_ANA_MICB2 (0x3023)
+#define WCD939X_ANA_MICB2_RAMP (0x3024)
+#define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7)
+#define WCD939X_MICB2_RAMP_MB2_IN2P_SHORT_ENABLE BIT(6)
+#define WCD939X_MICB2_RAMP_ALLSW_OVRD_ENABLE BIT(5)
+#define WCD939X_MICB2_RAMP_SHIFT_CTL GENMASK(4, 2)
+#define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP GENMASK(1, 0)
+#define WCD939X_ANA_MICB3 (0x3025)
+#define WCD939X_ANA_MICB4 (0x3026)
+#define WCD939X_BIAS_CTL (0x3028)
+#define WCD939X_BIAS_VBG_FINE_ADJ (0x3029)
+#define WCD939X_LDOL_VDDCX_ADJUST (0x3040)
+#define WCD939X_LDOL_DISABLE_LDOL (0x3041)
+#define WCD939X_MBHC_CTL_CLK (0x3056)
+#define WCD939X_MBHC_CTL_ANA (0x3057)
+#define WCD939X_MBHC_ZDET_VNEG_CTL (0x3058)
+#define WCD939X_MBHC_ZDET_BIAS_CTL (0x3059)
+#define WCD939X_MBHC_CTL_BCS (0x305a)
+#define WCD939X_MBHC_MOISTURE_DET_FSM_STATUS (0x305b)
+#define WCD939X_MBHC_TEST_CTL (0x305c)
+#define WCD939X_LDOH_MODE (0x3067)
+#define WCD939X_MODE_LDOH_EN BIT(7)
+#define WCD939X_MODE_PWRDN_STATE BIT(6)
+#define WCD939X_MODE_SLOWRAMP_EN BIT(5)
+#define WCD939X_MODE_VOUT_ADJUST GENMASK(4, 3)
+#define WCD939X_MODE_VOUT_COARSE_ADJ GENMASK(2, 0)
+#define WCD939X_LDOH_BIAS (0x3068)
+#define WCD939X_LDOH_STB_LOADS (0x3069)
+#define WCD939X_LDOH_SLOWRAMP (0x306a)
+#define WCD939X_MICB1_TEST_CTL_1 (0x306b)
+#define WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL GENMASK(7, 5)
+#define WCD939X_TEST_CTL_1_EN_VREFGEN BIT(4)
+#define WCD939X_TEST_CTL_1_EN_LDO BIT(3)
+#define WCD939X_TEST_CTL_1_LDO_BLEEDER_I_CTRL GENMASK(2, 0)
+#define WCD939X_MICB1_TEST_CTL_2 (0x306c)
+#define WCD939X_TEST_CTL_2_IBIAS_VREFGEN GENMASK(7, 6)
+#define WCD939X_TEST_CTL_2_INRUSH_CURRENT_FIX_DIS BIT(5)
+#define WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER GENMASK(2, 0)
+#define WCD939X_MICB1_TEST_CTL_3 (0x306d)
+#define WCD939X_TEST_CTL_3_CFILT_REF_EN BIT(7)
+#define WCD939X_TEST_CTL_3_RZ_LDO_VAL GENMASK(6, 4)
+#define WCD939X_TEST_CTL_3_IBIAS_LDO_STG3 GENMASK(3, 2)
+#define WCD939X_TEST_CTL_3_ATEST_CTRL GENMASK(1, 0)
+#define WCD939X_MICB2_TEST_CTL_1 (0x306e)
+#define WCD939X_MICB2_TEST_CTL_2 (0x306f)
+#define WCD939X_MICB2_TEST_CTL_3 (0x3070)
+#define WCD939X_MICB3_TEST_CTL_1 (0x3071)
+#define WCD939X_MICB3_TEST_CTL_2 (0x3072)
+#define WCD939X_MICB3_TEST_CTL_3 (0x3073)
+#define WCD939X_MICB4_TEST_CTL_1 (0x3074)
+#define WCD939X_MICB4_TEST_CTL_2 (0x3075)
+#define WCD939X_MICB4_TEST_CTL_3 (0x3076)
+#define WCD939X_TX_COM_ADC_VCM (0x3077)
+#define WCD939X_TX_COM_BIAS_ATEST (0x3078)
+#define WCD939X_TX_COM_SPARE1 (0x3079)
+#define WCD939X_TX_COM_SPARE2 (0x307a)
+#define WCD939X_TX_COM_TXFE_DIV_CTL (0x307b)
+#define WCD939X_TX_COM_TXFE_DIV_START (0x307c)
+#define WCD939X_TX_COM_SPARE3 (0x307d)
+#define WCD939X_TX_COM_SPARE4 (0x307e)
+#define WCD939X_TX_1_2_TEST_EN (0x307f)
+#define WCD939X_TX_1_2_ADC_IB (0x3080)
+#define WCD939X_TX_1_2_ATEST_REFCTL (0x3081)
+#define WCD939X_TX_1_2_TEST_CTL (0x3082)
+#define WCD939X_TX_1_2_TEST_BLK_EN1 (0x3083)
+#define WCD939X_TX_1_2_TXFE1_CLKDIV (0x3084)
+#define WCD939X_TX_1_2_SAR2_ERR (0x3085)
+#define WCD939X_TX_1_2_SAR1_ERR (0x3086)
+#define WCD939X_TX_3_4_TEST_EN (0x3087)
+#define WCD939X_TX_3_4_ADC_IB (0x3088)
+#define WCD939X_TX_3_4_ATEST_REFCTL (0x3089)
+#define WCD939X_TX_3_4_TEST_CTL (0x308a)
+#define WCD939X_TX_3_4_TEST_BLK_EN3 (0x308b)
+#define WCD939X_TX_3_4_TXFE3_CLKDIV (0x308c)
+#define WCD939X_TX_3_4_SAR4_ERR (0x308d)
+#define WCD939X_TX_3_4_SAR3_ERR (0x308e)
+#define WCD939X_TX_3_4_TEST_BLK_EN2 (0x308f)
+#define WCD939X_TEST_BLK_EN2_ADC2_INT1_EN BIT(7)
+#define WCD939X_TEST_BLK_EN2_ADC2_INT2_EN BIT(6)
+#define WCD939X_TEST_BLK_EN2_ADC2_SAR_EN BIT(5)
+#define WCD939X_TEST_BLK_EN2_ADC2_CMGEN_EN BIT(4)
+#define WCD939X_TEST_BLK_EN2_ADC2_CLKGEN_EN BIT(3)
+#define WCD939X_TEST_BLK_EN2_ADC12_VREF_NONL2 GENMASK(2, 1)
+#define WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN BIT(0)
+#define WCD939X_TX_3_4_TXFE2_CLKDIV (0x3090)
+#define WCD939X_TX_3_4_SPARE1 (0x3091)
+#define WCD939X_TX_3_4_TEST_BLK_EN4 (0x3092)
+#define WCD939X_TX_3_4_TXFE4_CLKDIV (0x3093)
+#define WCD939X_TX_3_4_SPARE2 (0x3094)
+#define WCD939X_CLASSH_MODE_1 (0x3097)
+#define WCD939X_CLASSH_MODE_2 (0x3098)
+#define WCD939X_CLASSH_MODE_3 (0x3099)
+#define WCD939X_CLASSH_CTRL_VCL_1 (0x309a)
+#define WCD939X_CLASSH_CTRL_VCL_2 (0x309b)
+#define WCD939X_CLASSH_CTRL_CCL_1 (0x309c)
+#define WCD939X_CLASSH_CTRL_CCL_2 (0x309d)
+#define WCD939X_CLASSH_CTRL_CCL_3 (0x309e)
+#define WCD939X_CLASSH_CTRL_CCL_4 (0x309f)
+#define WCD939X_CLASSH_CTRL_CCL_5 (0x30a0)
+#define WCD939X_CLASSH_BUCK_TMUX_A_D (0x30a1)
+#define WCD939X_CLASSH_BUCK_SW_DRV_CNTL (0x30a2)
+#define WCD939X_CLASSH_SPARE (0x30a3)
+#define WCD939X_FLYBACK_EN (0x30a4)
+#define WCD939X_FLYBACK_VNEG_CTRL_1 (0x30a5)
+#define WCD939X_FLYBACK_VNEG_CTRL_2 (0x30a6)
+#define WCD939X_FLYBACK_VNEG_CTRL_3 (0x30a7)
+#define WCD939X_FLYBACK_VNEG_CTRL_4 (0x30a8)
+#define WCD939X_VNEG_CTRL_4_ILIM_SEL GENMASK(7, 4)
+#define WCD939X_VNEG_CTRL_4_PW_BUF_POS GENMASK(3, 2)
+#define WCD939X_VNEG_CTRL_4_PW_BUF_NEG GENMASK(1, 0)
+#define WCD939X_FLYBACK_VNEG_CTRL_5 (0x30a9)
+#define WCD939X_FLYBACK_VNEG_CTRL_6 (0x30aa)
+#define WCD939X_FLYBACK_VNEG_CTRL_7 (0x30ab)
+#define WCD939X_FLYBACK_VNEG_CTRL_8 (0x30ac)
+#define WCD939X_FLYBACK_VNEG_CTRL_9 (0x30ad)
+#define WCD939X_FLYBACK_VNEGDAC_CTRL_1 (0x30ae)
+#define WCD939X_FLYBACK_VNEGDAC_CTRL_2 (0x30af)
+#define WCD939X_FLYBACK_VNEGDAC_CTRL_3 (0x30b0)
+#define WCD939X_FLYBACK_CTRL_1 (0x30b1)
+#define WCD939X_FLYBACK_TEST_CTL (0x30b2)
+#define WCD939X_RX_AUX_SW_CTL (0x30b3)
+#define WCD939X_RX_PA_AUX_IN_CONN (0x30b4)
+#define WCD939X_RX_TIMER_DIV (0x30b5)
+#define WCD939X_RX_OCP_CTL (0x30b6)
+#define WCD939X_RX_OCP_COUNT (0x30b7)
+#define WCD939X_RX_BIAS_EAR_DAC (0x30b8)
+#define WCD939X_RX_BIAS_EAR_AMP (0x30b9)
+#define WCD939X_RX_BIAS_HPH_LDO (0x30ba)
+#define WCD939X_RX_BIAS_HPH_PA (0x30bb)
+#define WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2 (0x30bc)
+#define WCD939X_RX_BIAS_HPH_RDAC_LDO (0x30bd)
+#define WCD939X_RX_BIAS_HPH_CNP1 (0x30be)
+#define WCD939X_RX_BIAS_HPH_LOWPOWER (0x30bf)
+#define WCD939X_RX_BIAS_AUX_DAC (0x30c0)
+#define WCD939X_RX_BIAS_AUX_AMP (0x30c1)
+#define WCD939X_RX_BIAS_VNEGDAC_BLEEDER (0x30c2)
+#define WCD939X_RX_BIAS_MISC (0x30c3)
+#define WCD939X_RX_BIAS_BUCK_RST (0x30c4)
+#define WCD939X_RX_BIAS_BUCK_VREF_ERRAMP (0x30c5)
+#define WCD939X_RX_BIAS_FLYB_ERRAMP (0x30c6)
+#define WCD939X_RX_BIAS_FLYB_BUFF (0x30c7)
+#define WCD939X_RX_BIAS_FLYB_MID_RST (0x30c8)
+#define WCD939X_HPH_L_STATUS (0x30c9)
+#define WCD939X_HPH_R_STATUS (0x30ca)
+#define WCD939X_HPH_CNP_EN (0x30cb)
+#define WCD939X_HPH_CNP_WG_CTL (0x30cc)
+#define WCD939X_HPH_CNP_WG_TIME (0x30cd)
+#define WCD939X_HPH_OCP_CTL (0x30ce)
+#define WCD939X_OCP_CTL_OCP_CURR_LIMIT GENMASK(7, 5)
+#define WCD939X_OCP_CTL_OCP_FSM_EN BIT(4)
+#define WCD939X_OCP_CTL_SPARE_BITS BIT(3)
+#define WCD939X_OCP_CTL_SCD_OP_EN BIT(1)
+#define WCD939X_HPH_AUTO_CHOP (0x30cf)
+#define WCD939X_HPH_CHOP_CTL (0x30d0)
+#define WCD939X_HPH_PA_CTL1 (0x30d1)
+#define WCD939X_HPH_PA_CTL2 (0x30d2)
+#define WCD939X_PA_CTL2_HPHPA_GND_R BIT(6)
+#define WCD939X_PA_CTL2_HPHPA_GND_L BIT(4)
+#define WCD939X_PA_CTL2_GM3_CASCODE_CTL_NORMAL GENMASK(1, 0)
+#define WCD939X_HPH_L_EN (0x30d3)
+#define WCD939X_L_EN_CONST_SEL_L GENMASK(7, 6)
+#define WCD939X_L_EN_GAIN_SOURCE_SEL BIT(5)
+#define WCD939X_L_EN_SPARE_BITS GENMASK(4, 0)
+#define WCD939X_HPH_L_TEST (0x30d4)
+#define WCD939X_HPH_L_ATEST (0x30d5)
+#define WCD939X_HPH_R_EN (0x30d6)
+#define WCD939X_R_EN_CONST_SEL_R GENMASK(7, 6)
+#define WCD939X_R_EN_GAIN_SOURCE_SEL BIT(5)
+#define WCD939X_R_EN_SPARE_BITS GENMASK(4, 0)
+#define WCD939X_HPH_R_TEST (0x30d7)
+#define WCD939X_HPH_R_ATEST (0x30d8)
+#define WCD939X_R_ATEST_DACR_REF_ATEST1_CONN BIT(7)
+#define WCD939X_R_ATEST_LDO1_R_ATEST2_CONN BIT(6)
+#define WCD939X_R_ATEST_LDO_R_ATEST2_CAL BIT(5)
+#define WCD939X_R_ATEST_LDO2_R_ATEST2_CONN BIT(4)
+#define WCD939X_R_ATEST_LDO_1P65V_ATEST1_CONN BIT(3)
+#define WCD939X_R_ATEST_HPH_GND_OVR BIT(1)
+#define WCD939X_HPH_RDAC_CLK_CTL1 (0x30d9)
+#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN BIT(7)
+#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_DIV_CTRL GENMASK(6, 4)
+#define WCD939X_RDAC_CLK_CTL1_SPARE_BITS GENMASK(3, 0)
+#define WCD939X_HPH_RDAC_CLK_CTL2 (0x30da)
+#define WCD939X_HPH_RDAC_LDO_CTL (0x30db)
+#define WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL (0x30dc)
+#define WCD939X_HPH_REFBUFF_UHQA_CTL (0x30dd)
+#define WCD939X_REFBUFF_UHQA_CTL_SPARE_BITS GENMASK(7, 6)
+#define WCD939X_REFBUFF_UHQA_CTL_HPH_VNEGREG2_COMP_CTL_OV BIT(5)
+#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_RBIAS_ADJUST BIT(4)
+#define WCD939X_REFBUFF_UHQA_CTL_REFBUFP_IOUT_CTL GENMASK(3, 2)
+#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_IOUT_CTL GENMASK(1, 0)
+#define WCD939X_HPH_REFBUFF_LP_CTL (0x30de)
+#define WCD939X_REFBUFF_LP_CTL_HPH_VNEGREG2_CURR_COMP GENMASK(7, 6)
+#define WCD939X_REFBUFF_LP_CTL_SPARE_BITS GENMASK(5, 4)
+#define WCD939X_REFBUFF_LP_CTL_EN_PREREF_FILT_STARTUP_CLKDIV BIT(3)
+#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_STARTUP_CLKDIV_CTL GENMASK(2, 1)
+#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS BIT(0)
+#define WCD939X_HPH_L_DAC_CTL (0x30df)
+#define WCD939X_HPH_R_DAC_CTL (0x30e0)
+#define WCD939X_HPH_SURGE_COMP_SEL (0x30e1)
+#define WCD939X_HPH_SURGE_EN (0x30e2)
+#define WCD939X_EN_EN_SURGE_PROTECTION_HPHL BIT(7)
+#define WCD939X_EN_EN_SURGE_PROTECTION_HPHR BIT(6)
+#define WCD939X_EN_SEL_SURGE_COMP_IQ GENMASK(5, 4)
+#define WCD939X_EN_SURGE_VOLT_MODE_SHUTOFF_EN BIT(3)
+#define WCD939X_EN_LATCH_INTR_OP_STG_HIZ_EN BIT(2)
+#define WCD939X_EN_SURGE_LATCH_REG_RESET BIT(1)
+#define WCD939X_EN_SWTICH_VN_VNDAC_NSURGE_EN BIT(0)
+#define WCD939X_HPH_SURGE_MISC1 (0x30e3)
+#define WCD939X_HPH_SURGE_STATUS (0x30e4)
+#define WCD939X_EAR_EN (0x30e9)
+#define WCD939X_EAR_PA_CON (0x30ea)
+#define WCD939X_EAR_SP_CON (0x30eb)
+#define WCD939X_EAR_DAC_CON (0x30ec)
+#define WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL BIT(7)
+#define WCD939X_DAC_CON_REF_DBG_EN BIT(6)
+#define WCD939X_DAC_CON_REF_DBG_GAIN GENMASK(5, 3)
+#define WCD939X_DAC_CON_GAIN_DAC GENMASK(2, 1)
+#define WCD939X_DAC_CON_INV_DATA BIT(0)
+#define WCD939X_EAR_CNP_FSM_CON (0x30ed)
+#define WCD939X_EAR_TEST_CTL (0x30ee)
+#define WCD939X_EAR_STATUS_REG_1 (0x30ef)
+#define WCD939X_EAR_STATUS_REG_2 (0x30f0)
+#define WCD939X_FLYBACK_NEW_CTRL_2 (0x30f6)
+#define WCD939X_FLYBACK_NEW_CTRL_3 (0x30f7)
+#define WCD939X_FLYBACK_NEW_CTRL_4 (0x30f8)
+#define WCD939X_ANA_NEW_PAGE (0x3100)
+#define WCD939X_HPH_NEW_ANA_HPH2 (0x3101)
+#define WCD939X_HPH_NEW_ANA_HPH3 (0x3102)
+#define WCD939X_SLEEP_CTL (0x3103)
+#define WCD939X_SLEEP_WATCHDOG_CTL (0x3104)
+#define WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x311f)
+#define WCD939X_MBHC_NEW_CTL_1 (0x3120)
+#define WCD939X_CTL_1_RCO_EN BIT(7)
+#define WCD939X_CTL_1_ADC_MODE BIT(4)
+#define WCD939X_CTL_1_ADC_ENABLE BIT(3)
+#define WCD939X_CTL_1_DETECTION_DONE BIT(2)
+#define WCD939X_CTL_1_BTN_DBNC_CTL GENMASK(1, 0)
+#define WCD939X_MBHC_NEW_CTL_2 (0x3121)
+#define WCD939X_CTL_2_MUX_CTL GENMASK(6, 4)
+#define WCD939X_CTL_2_M_RTH_CTL GENMASK(3, 2)
+#define WCD939X_CTL_2_HS_VREF_CTL GENMASK(1, 0)
+#define WCD939X_MBHC_NEW_PLUG_DETECT_CTL (0x3122)
+#define WCD939X_MBHC_NEW_ZDET_ANA_CTL (0x3123)
+#define WCD939X_ZDET_ANA_CTL_AVERAGING_EN BIT(7)
+#define WCD939X_ZDET_ANA_CTL_MAXV_CTL GENMASK(6, 4)
+#define WCD939X_ZDET_ANA_CTL_RANGE_CTL GENMASK(3, 0)
+#define WCD939X_MBHC_NEW_ZDET_RAMP_CTL (0x3124)
+#define WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL GENMASK(6, 4)
+#define WCD939X_ZDET_RAMP_CTL_TIME_CTL GENMASK(3, 0)
+#define WCD939X_MBHC_NEW_FSM_STATUS (0x3125)
+#define WCD939X_FSM_STATUS_ADC_TIMEOUT BIT(7)
+#define WCD939X_FSM_STATUS_ADC_COMPLETE BIT(6)
+#define WCD939X_FSM_STATUS_HS_M_COMP_STATUS BIT(5)
+#define WCD939X_FSM_STATUS_FAST_PRESS_FLAG_STATUS BIT(4)
+#define WCD939X_FSM_STATUS_FAST_REMOVAL_FLAG_STATUS BIT(3)
+#define WCD939X_FSM_STATUS_REMOVAL_FLAG_STATUS BIT(2)
+#define WCD939X_FSM_STATUS_ELECT_REM_RT_STATUS BIT(1)
+#define WCD939X_FSM_STATUS_BTN_STATUS BIT(0)
+#define WCD939X_MBHC_NEW_ADC_RESULT (0x3126)
+#define WCD939X_ADC_RESULT_VALUE GENMASK(7, 0)
+#define WCD939X_TX_NEW_CH12_MUX (0x3127)
+#define WCD939X_TX_NEW_CH34_MUX (0x3128)
+#define WCD939X_DIE_CRACK_DET_EN (0x312c)
+#define WCD939X_DIE_CRACK_DET_OUT (0x312d)
+#define WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL (0x3132)
+#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L (0x3133)
+#define WCD939X_PA_GAIN_CTL_L_EN_HPHPA_2VPK BIT(7)
+#define WCD939X_PA_GAIN_CTL_L_RX_SUPPLY_LEVEL BIT(6)
+#define WCD939X_PA_GAIN_CTL_L_DAC_DR_BOOST BIT(5)
+#define WCD939X_PA_GAIN_CTL_L_VALUE GENMASK(4, 0)
+#define WCD939X_HPH_NEW_INT_RDAC_VREF_CTL (0x3134)
+#define WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x3135)
+#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R (0x3136)
+#define WCD939X_PA_GAIN_CTL_R_D_RCO_CLK_EN BIT(7)
+#define WCD939X_PA_GAIN_CTL_R_SPARE_BITS GENMASK(6, 5)
+#define WCD939X_PA_GAIN_CTL_R_VALUE GENMASK(4, 0)
+#define WCD939X_HPH_NEW_INT_PA_MISC1 (0x3137)
+#define WCD939X_HPH_NEW_INT_PA_MISC2 (0x3138)
+#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC (0x3139)
+#define WCD939X_HPH_NEW_INT_TIMER1 (0x313a)
+#define WCD939X_TIMER1_CURR_IDIV_CTL_CMPDR_OFF GENMASK(7, 5)
+#define WCD939X_TIMER1_CURR_IDIV_CTL_AUTOCHOP GENMASK(4, 2)
+#define WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN BIT(1)
+#define WCD939X_HPH_NEW_INT_TIMER2 (0x313b)
+#define WCD939X_HPH_NEW_INT_TIMER3 (0x313c)
+#define WCD939X_HPH_NEW_INT_TIMER4 (0x313d)
+#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC2 (0x313e)
+#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC3 (0x313f)
+#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L (0x3140)
+#define WCD939X_RDAC_HD2_CTL_L_EN_HD2_RES_DIV_L BIT(7)
+#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_PULLGND_L BIT(6)
+#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L GENMASK(5, 0)
+#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R (0x3141)
+#define WCD939X_RDAC_HD2_CTL_R_EN_HD2_RES_DIV_R BIT(7)
+#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_PULLGND_L BIT(6)
+#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R GENMASK(5, 0)
+#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x3145)
+#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x3146)
+#define WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP (0x3147)
+#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x31af)
+#define WCD939X_MOISTURE_DET_DC_CTRL_ONCOUNT GENMASK(6, 5)
+#define WCD939X_MOISTURE_DET_DC_CTRL_OFFCOUNT GENMASK(4, 0)
+#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x31b0)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_HPHL_PA_EN BIT(6)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_DTEST_EN GENMASK(5, 4)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_OVRD_POLLING BIT(3)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING BIT(2)
+#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_DBNC_TIME GENMASK(1, 0)
+#define WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT (0x31b1)
+#define WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL GENMASK(4, 0)
+#define WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW (0x31b2)
+#define WCD939X_EAR_INT_NEW_CHOPPER_CON (0x31b7)
+#define WCD939X_EAR_INT_NEW_CNP_VCM_CON1 (0x31b8)
+#define WCD939X_EAR_INT_NEW_CNP_VCM_CON2 (0x31b9)
+#define WCD939X_EAR_INT_NEW_DYNAMIC_BIAS (0x31ba)
+#define WCD939X_SLEEP_INT_WATCHDOG_CTL_1 (0x31d0)
+#define WCD939X_SLEEP_INT_WATCHDOG_CTL_2 (0x31d1)
+#define WCD939X_DIE_CRACK_INT_DET_INT1 (0x31d3)
+#define WCD939X_DIE_CRACK_INT_DET_INT2 (0x31d4)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2 (0x31d5)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1 (0x31d6)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0 (0x31d7)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M (0x31d8)
+#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M (0x31d9)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1 (0x31da)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0 (0x31db)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP (0x31dc)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1 (0x31dd)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0 (0x31de)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP (0x31df)
+#define WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE GENMASK(4, 0)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0 (0x31e0)
+#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP (0x31e1)
+#define WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M GENMASK(7, 4)
+#define WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE GENMASK(3, 0)
+#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1 (0x31e2)
+#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP (0x31e3)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_L2 (0x31e4)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_L1 (0x31e5)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_L0 (0x31e6)
+#define WCD939X_TX_COM_NEW_INT_ADC_INT_ULP (0x31e7)
+#define WCD939X_DIGITAL_PAGE (0x3400)
+#define WCD939X_DIGITAL_CHIP_ID0 (0x3401)
+#define WCD939X_DIGITAL_CHIP_ID1 (0x3402)
+#define WCD939X_DIGITAL_CHIP_ID2 (0x3403)
+#define WCD939X_DIGITAL_CHIP_ID3 (0x3404)
+#define WCD939X_DIGITAL_SWR_TX_CLK_RATE (0x3405)
+#define WCD939X_DIGITAL_CDC_RST_CTL (0x3406)
+#define WCD939X_DIGITAL_TOP_CLK_CFG (0x3407)
+#define WCD939X_DIGITAL_CDC_ANA_CLK_CTL (0x3408)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV4_CLK_EN BIT(5)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN BIT(3)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN BIT(2)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN BIT(1)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN BIT(0)
+#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4)
+#define WCD939X_DIGITAL_CDC_DIG_CLK_CTL (0x3409)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN BIT(7)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN BIT(6)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN BIT(5)
+#define WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN BIT(4)
+#define WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN BIT(2)
+#define WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN BIT(1)
+#define WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN BIT(0)
+#define WCD939X_DIGITAL_SWR_RST_EN (0x340a)
+#define WCD939X_DIGITAL_CDC_PATH_MODE (0x340b)
+#define WCD939X_DIGITAL_CDC_RX_RST (0x340c)
+#define WCD939X_DIGITAL_CDC_RX0_CTL (0x340d)
+#define WCD939X_DIGITAL_CDC_RX1_CTL (0x340e)
+#define WCD939X_DIGITAL_CDC_RX2_CTL (0x340f)
+#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1 (0x3410)
+#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE GENMASK(7, 4)
+#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE GENMASK(3, 0)
+#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3 (0x3411)
+#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE GENMASK(7, 4)
+#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE GENMASK(3, 0)
+#define WCD939X_DIGITAL_CDC_COMP_CTL_0 (0x3414)
+#define WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN BIT(1)
+#define WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN BIT(0)
+#define WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL (0x3417)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_MBHC_1P2M_CLK_EN BIT(5)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX3_ADC_CLK_EN BIT(4)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX2_ADC_CLK_EN BIT(3)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX1_ADC_CLK_EN BIT(2)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX0_ADC_CLK_EN BIT(1)
+#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN BIT(0)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_0 (0x3418)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_1 (0x3419)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_0 (0x341a)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_1 (0x341b)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_0 (0x341c)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_1 (0x341d)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_0 (0x341e)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_1 (0x341f)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_0 (0x3420)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_1 (0x3421)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A6_0 (0x3422)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_A7_0 (0x3423)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_0 (0x3424)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_1 (0x3425)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_2 (0x3426)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_C_3 (0x3427)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R1 (0x3428)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R2 (0x3429)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R3 (0x342a)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R4 (0x342b)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R5 (0x342c)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R6 (0x342d)
+#define WCD939X_DIGITAL_CDC_HPH_DSM_R7 (0x342e)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_0 (0x342f)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_1 (0x3430)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_0 (0x3431)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_1 (0x3432)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_0 (0x3433)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_1 (0x3434)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_0 (0x3435)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_1 (0x3436)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_0 (0x3437)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_1 (0x3438)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A6_0 (0x3439)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_A7_0 (0x343a)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_0 (0x343b)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_1 (0x343c)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_2 (0x343d)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_C_3 (0x343e)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R1 (0x343f)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R2 (0x3440)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R3 (0x3441)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R4 (0x3442)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R5 (0x3443)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R6 (0x3444)
+#define WCD939X_DIGITAL_CDC_EAR_DSM_R7 (0x3445)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0 (0x3446)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1 (0x3447)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0 (0x3448)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1 (0x3449)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2 (0x344a)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0 (0x344b)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1 (0x344c)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2 (0x344d)
+#define WCD939X_DIGITAL_CDC_HPH_GAIN_CTL (0x344e)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPH_STEREO_EN BIT(4)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN BIT(3)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN BIT(2)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_DSD_EN BIT(1)
+#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_DSD_EN BIT(0)
+#define WCD939X_DIGITAL_CDC_EAR_GAIN_CTL (0x344f)
+#define WCD939X_CDC_EAR_GAIN_CTL_EAR_EN BIT(0)
+#define WCD939X_DIGITAL_CDC_EAR_PATH_CTL (0x3450)
+#define WCD939X_DIGITAL_CDC_SWR_CLH (0x3451)
+#define WCD939X_CDC_SWR_CLH_CLH_CTL GENMASK(7, 0)
+#define WCD939X_DIGITAL_SWR_CLH_BYP (0x3452)
+#define WCD939X_DIGITAL_CDC_TX0_CTL (0x3453)
+#define WCD939X_DIGITAL_CDC_TX1_CTL (0x3454)
+#define WCD939X_DIGITAL_CDC_TX2_CTL (0x3455)
+#define WCD939X_DIGITAL_CDC_TX_RST (0x3456)
+#define WCD939X_DIGITAL_CDC_REQ_CTL (0x3457)
+#define WCD939X_CDC_REQ_CTL_TX3_WIDE_BAND BIT(5)
+#define WCD939X_CDC_REQ_CTL_TX2_WIDE_BAND BIT(4)
+#define WCD939X_CDC_REQ_CTL_TX1_WIDE_BAND BIT(3)
+#define WCD939X_CDC_REQ_CTL_TX0_WIDE_BAND BIT(2)
+#define WCD939X_CDC_REQ_CTL_FS_RATE_4P8 BIT(1)
+#define WCD939X_CDC_REQ_CTL_NO_NOTCH BIT(0)
+#define WCD939X_DIGITAL_CDC_RST (0x3458)
+#define WCD939X_DIGITAL_CDC_AMIC_CTL (0x345a)
+#define WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL BIT(3)
+#define WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL BIT(2)
+#define WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL BIT(1)
+#define WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL BIT(0)
+#define WCD939X_DIGITAL_CDC_DMIC_CTL (0x345b)
+#define WCD939X_CDC_DMIC_CTL_DMIC_LEGACY_SW_MODE BIT(3)
+#define WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN BIT(2)
+#define WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN BIT(1)
+#define WCD939X_CDC_DMIC_CTL_SOFT_RESET BIT(0)
+#define WCD939X_DIGITAL_CDC_DMIC1_CTL (0x345c)
+#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4)
+#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN BIT(3)
+#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SEL GENMASK(2, 0)
+#define WCD939X_DIGITAL_CDC_DMIC2_CTL (0x345d)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN BIT(7)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN BIT(3)
+#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SEL GENMASK(2, 0)
+#define WCD939X_DIGITAL_CDC_DMIC3_CTL (0x345e)
+#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4)
+#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN BIT(3)
+#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SEL GENMASK(2, 0)
+#define WCD939X_DIGITAL_CDC_DMIC4_CTL (0x345f)
+#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4)
+#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN BIT(3)
+#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SEL GENMASK(2, 0)
+#define WCD939X_DIGITAL_EFUSE_PRG_CTL (0x3460)
+#define WCD939X_DIGITAL_EFUSE_CTL (0x3461)
+#define WCD939X_DIGITAL_CDC_DMIC_RATE_1_2 (0x3462)
+#define WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE GENMASK(7, 4)
+#define WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE GENMASK(3, 0)
+#define WCD939X_DIGITAL_CDC_DMIC_RATE_3_4 (0x3463)
+#define WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE GENMASK(7, 4)
+#define WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE GENMASK(3, 0)
+#define WCD939X_DIGITAL_PDM_WD_CTL0 (0x3465)
+#define WCD939X_PDM_WD_CTL0_HOLD_OFF BIT(4)
+#define WCD939X_PDM_WD_CTL0_TIME_OUT_SEL BIT(3)
+#define WCD939X_PDM_WD_CTL0_PDM_WD_EN GENMASK(2, 0)
+#define WCD939X_DIGITAL_PDM_WD_CTL1 (0x3466)
+#define WCD939X_PDM_WD_CTL1_HOLD_OFF BIT(4)
+#define WCD939X_PDM_WD_CTL1_TIME_OUT_SEL BIT(3)
+#define WCD939X_PDM_WD_CTL1_PDM_WD_EN GENMASK(2, 0)
+#define WCD939X_DIGITAL_PDM_WD_CTL2 (0x3467)
+#define WCD939X_DIGITAL_INTR_MODE (0x346a)
+#define WCD939X_DIGITAL_INTR_MASK_0 (0x346b)
+#define WCD939X_DIGITAL_INTR_MASK_1 (0x346c)
+#define WCD939X_DIGITAL_INTR_MASK_2 (0x346d)
+#define WCD939X_DIGITAL_INTR_STATUS_0 (0x346e)
+#define WCD939X_DIGITAL_INTR_STATUS_1 (0x346f)
+#define WCD939X_DIGITAL_INTR_STATUS_2 (0x3470)
+#define WCD939X_DIGITAL_INTR_CLEAR_0 (0x3471)
+#define WCD939X_DIGITAL_INTR_CLEAR_1 (0x3472)
+#define WCD939X_DIGITAL_INTR_CLEAR_2 (0x3473)
+#define WCD939X_DIGITAL_INTR_LEVEL_0 (0x3474)
+#define WCD939X_DIGITAL_INTR_LEVEL_1 (0x3475)
+#define WCD939X_DIGITAL_INTR_LEVEL_2 (0x3476)
+#define WCD939X_DIGITAL_INTR_SET_0 (0x3477)
+#define WCD939X_DIGITAL_INTR_SET_1 (0x3478)
+#define WCD939X_DIGITAL_INTR_SET_2 (0x3479)
+#define WCD939X_DIGITAL_INTR_TEST_0 (0x347a)
+#define WCD939X_DIGITAL_INTR_TEST_1 (0x347b)
+#define WCD939X_DIGITAL_INTR_TEST_2 (0x347c)
+#define WCD939X_DIGITAL_TX_MODE_DBG_EN (0x347f)
+#define WCD939X_DIGITAL_TX_MODE_DBG_0_1 (0x3480)
+#define WCD939X_DIGITAL_TX_MODE_DBG_2_3 (0x3481)
+#define WCD939X_DIGITAL_LB_IN_SEL_CTL (0x3482)
+#define WCD939X_DIGITAL_LOOP_BACK_MODE (0x3483)
+#define WCD939X_DIGITAL_SWR_DAC_TEST (0x3484)
+#define WCD939X_DIGITAL_SWR_HM_TEST_RX_0 (0x3485)
+#define WCD939X_DIGITAL_SWR_HM_TEST_TX_0 (0x3486)
+#define WCD939X_DIGITAL_SWR_HM_TEST_RX_1 (0x3487)
+#define WCD939X_DIGITAL_SWR_HM_TEST_TX_1 (0x3488)
+#define WCD939X_DIGITAL_SWR_HM_TEST_TX_2 (0x3489)
+#define WCD939X_DIGITAL_SWR_HM_TEST_0 (0x348a)
+#define WCD939X_DIGITAL_SWR_HM_TEST_1 (0x348b)
+#define WCD939X_DIGITAL_PAD_CTL_SWR_0 (0x348c)
+#define WCD939X_DIGITAL_PAD_CTL_SWR_1 (0x348d)
+#define WCD939X_DIGITAL_I2C_CTL (0x348e)
+#define WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE (0x348f)
+#define WCD939X_DIGITAL_EFUSE_TEST_CTL_0 (0x3490)
+#define WCD939X_DIGITAL_EFUSE_TEST_CTL_1 (0x3491)
+#define WCD939X_DIGITAL_EFUSE_T_DATA_0 (0x3492)
+#define WCD939X_DIGITAL_EFUSE_T_DATA_1 (0x3493)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_RX0 (0x3494)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_RX1 (0x3495)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_TX0 (0x3496)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_TX1 (0x3497)
+#define WCD939X_DIGITAL_PAD_CTL_PDM_TX2 (0x3498)
+#define WCD939X_DIGITAL_PAD_INP_DIS_0 (0x3499)
+#define WCD939X_DIGITAL_PAD_INP_DIS_1 (0x349a)
+#define WCD939X_DIGITAL_DRIVE_STRENGTH_0 (0x349b)
+#define WCD939X_DIGITAL_DRIVE_STRENGTH_1 (0x349c)
+#define WCD939X_DIGITAL_DRIVE_STRENGTH_2 (0x349d)
+#define WCD939X_DIGITAL_RX_DATA_EDGE_CTL (0x349e)
+#define WCD939X_DIGITAL_TX_DATA_EDGE_CTL (0x349f)
+#define WCD939X_DIGITAL_GPIO_MODE (0x34a0)
+#define WCD939X_DIGITAL_PIN_CTL_OE (0x34a1)
+#define WCD939X_DIGITAL_PIN_CTL_DATA_0 (0x34a2)
+#define WCD939X_DIGITAL_PIN_CTL_DATA_1 (0x34a3)
+#define WCD939X_DIGITAL_PIN_STATUS_0 (0x34a4)
+#define WCD939X_DIGITAL_PIN_STATUS_1 (0x34a5)
+#define WCD939X_DIGITAL_DIG_DEBUG_CTL (0x34a6)
+#define WCD939X_DIGITAL_DIG_DEBUG_EN (0x34a7)
+#define WCD939X_DIGITAL_ANA_CSR_DBG_ADD (0x34a8)
+#define WCD939X_DIGITAL_ANA_CSR_DBG_CTL (0x34a9)
+#define WCD939X_DIGITAL_SSP_DBG (0x34aa)
+#define WCD939X_DIGITAL_MODE_STATUS_0 (0x34ab)
+#define WCD939X_DIGITAL_MODE_STATUS_1 (0x34ac)
+#define WCD939X_DIGITAL_SPARE_0 (0x34ad)
+#define WCD939X_DIGITAL_SPARE_1 (0x34ae)
+#define WCD939X_DIGITAL_SPARE_2 (0x34af)
+#define WCD939X_DIGITAL_EFUSE_REG_0 (0x34b0)
+#define WCD939X_EFUSE_REG_0_WCD939X_ID GENMASK(4, 1)
+#define WCD939X_EFUSE_REG_0_EFUSE_BLOWN BIT(0)
+#define WCD939X_DIGITAL_EFUSE_REG_1 (0x34b1)
+#define WCD939X_DIGITAL_EFUSE_REG_2 (0x34b2)
+#define WCD939X_DIGITAL_EFUSE_REG_3 (0x34b3)
+#define WCD939X_DIGITAL_EFUSE_REG_4 (0x34b4)
+#define WCD939X_DIGITAL_EFUSE_REG_5 (0x34b5)
+#define WCD939X_DIGITAL_EFUSE_REG_6 (0x34b6)
+#define WCD939X_DIGITAL_EFUSE_REG_7 (0x34b7)
+#define WCD939X_DIGITAL_EFUSE_REG_8 (0x34b8)
+#define WCD939X_DIGITAL_EFUSE_REG_9 (0x34b9)
+#define WCD939X_DIGITAL_EFUSE_REG_10 (0x34ba)
+#define WCD939X_DIGITAL_EFUSE_REG_11 (0x34bb)
+#define WCD939X_DIGITAL_EFUSE_REG_12 (0x34bc)
+#define WCD939X_DIGITAL_EFUSE_REG_13 (0x34bd)
+#define WCD939X_DIGITAL_EFUSE_REG_14 (0x34be)
+#define WCD939X_DIGITAL_EFUSE_REG_15 (0x34bf)
+#define WCD939X_DIGITAL_EFUSE_REG_16 (0x34c0)
+#define WCD939X_DIGITAL_EFUSE_REG_17 (0x34c1)
+#define WCD939X_DIGITAL_EFUSE_REG_18 (0x34c2)
+#define WCD939X_DIGITAL_EFUSE_REG_19 (0x34c3)
+#define WCD939X_DIGITAL_EFUSE_REG_20 (0x34c4)
+#define WCD939X_DIGITAL_EFUSE_REG_21 (0x34c5)
+#define WCD939X_DIGITAL_EFUSE_REG_22 (0x34c6)
+#define WCD939X_DIGITAL_EFUSE_REG_23 (0x34c7)
+#define WCD939X_DIGITAL_EFUSE_REG_24 (0x34c8)
+#define WCD939X_DIGITAL_EFUSE_REG_25 (0x34c9)
+#define WCD939X_DIGITAL_EFUSE_REG_26 (0x34ca)
+#define WCD939X_DIGITAL_EFUSE_REG_27 (0x34cb)
+#define WCD939X_DIGITAL_EFUSE_REG_28 (0x34cc)
+#define WCD939X_DIGITAL_EFUSE_REG_29 (0x34cd)
+#define WCD939X_DIGITAL_EFUSE_REG_30 (0x34ce)
+#define WCD939X_DIGITAL_EFUSE_REG_31 (0x34cf)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_0 (0x34d0)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_1 (0x34d1)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_2 (0x34d2)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_3 (0x34d3)
+#define WCD939X_DIGITAL_TX_REQ_FB_CTL_4 (0x34d4)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA0 (0x34d5)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA1 (0x34d6)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA2 (0x34d7)
+#define WCD939X_DIGITAL_DEM_BYPASS_DATA3 (0x34d8)
+#define WCD939X_DIGITAL_DEM_SECOND_ORDER (0x34d9)
+#define WCD939X_DIGITAL_DSM_CTRL (0x34da)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_0 (0x34db)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_1 (0x34dc)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_2 (0x34dd)
+#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_3 (0x34de)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_0 (0x34df)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_1 (0x34e0)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_2 (0x34e1)
+#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_3 (0x34e2)
+#define WCD939X_RX_TOP_PAGE (0x3500)
+#define WCD939X_RX_TOP_TOP_CFG0 (0x3501)
+#define WCD939X_TOP_CFG0_HPH_DAC_RATE_SEL BIT(1)
+#define WCD939X_TOP_CFG0_PGA_UPDATE BIT(0)
+#define WCD939X_RX_TOP_HPHL_COMP_WR_LSB (0x3502)
+#define WCD939X_RX_TOP_HPHL_COMP_WR_MSB (0x3503)
+#define WCD939X_RX_TOP_HPHL_COMP_LUT (0x3504)
+#define WCD939X_RX_TOP_HPHL_COMP_RD_LSB (0x3505)
+#define WCD939X_RX_TOP_HPHL_COMP_RD_MSB (0x3506)
+#define WCD939X_RX_TOP_HPHR_COMP_WR_LSB (0x3507)
+#define WCD939X_RX_TOP_HPHR_COMP_WR_MSB (0x3508)
+#define WCD939X_RX_TOP_HPHR_COMP_LUT (0x3509)
+#define WCD939X_RX_TOP_HPHR_COMP_RD_LSB (0x350a)
+#define WCD939X_RX_TOP_HPHR_COMP_RD_MSB (0x350b)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG1 (0x350c)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG2 (0x350d)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG3 (0x350e)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG4 (0x350f)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG5 (0x3510)
+#define WCD939X_RX_TOP_DSD0_DEBUG_CFG6 (0x3511)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG1 (0x3512)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG2 (0x3513)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG3 (0x3514)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG4 (0x3515)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG5 (0x3516)
+#define WCD939X_RX_TOP_DSD1_DEBUG_CFG6 (0x3517)
+#define WCD939X_RX_TOP_HPHL_PATH_CFG0 (0x351c)
+#define WCD939X_HPHL_PATH_CFG0_INT_EN BIT(1)
+#define WCD939X_HPHL_PATH_CFG0_DLY_ZN_EN BIT(0)
+#define WCD939X_RX_TOP_HPHL_PATH_CFG1 (0x351d)
+#define WCD939X_HPHL_PATH_CFG1_DSM_SOFT_RST BIT(5)
+#define WCD939X_HPHL_PATH_CFG1_INT_SOFT_RST BIT(4)
+#define WCD939X_HPHL_PATH_CFG1_FMT_CONV BIT(3)
+#define WCD939X_HPHL_PATH_CFG1_IDLE_OVRD_EN BIT(2)
+#define WCD939X_HPHL_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0)
+#define WCD939X_RX_TOP_HPHR_PATH_CFG0 (0x351e)
+#define WCD939X_HPHR_PATH_CFG0_INT_EN BIT(2)
+#define WCD939X_HPHR_PATH_CFG0_DLY_ZN_EN BIT(1)
+#define WCD939X_RX_TOP_HPHR_PATH_CFG1 (0x351f)
+#define WCD939X_HPHR_PATH_CFG1_DSM_SOFT_RST BIT(5)
+#define WCD939X_HPHR_PATH_CFG1_INT_SOFT_RST BIT(4)
+#define WCD939X_HPHR_PATH_CFG1_FMT_CONV BIT(3)
+#define WCD939X_HPHR_PATH_CFG1_IDLE_OVRD_EN BIT(2)
+#define WCD939X_HPHR_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0)
+#define WCD939X_RX_TOP_PATH_CFG2 (0x3520)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC0 (0x3521)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC1 (0x3522)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC2 (0x3523)
+#define WCD939X_RX_TOP_HPHL_PATH_SEC3 (0x3524)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC0 (0x3525)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC1 (0x3526)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC2 (0x3527)
+#define WCD939X_RX_TOP_HPHR_PATH_SEC3 (0x3528)
+#define WCD939X_RX_TOP_PATH_SEC4 (0x3529)
+#define WCD939X_RX_TOP_PATH_SEC5 (0x352a)
+#define WCD939X_COMPANDER_HPHL_CTL0 (0x3540)
+#define WCD939X_COMPANDER_HPHL_CTL1 (0x3541)
+#define WCD939X_COMPANDER_HPHL_CTL2 (0x3542)
+#define WCD939X_COMPANDER_HPHL_CTL3 (0x3543)
+#define WCD939X_COMPANDER_HPHL_CTL4 (0x3544)
+#define WCD939X_COMPANDER_HPHL_CTL5 (0x3545)
+#define WCD939X_COMPANDER_HPHL_CTL6 (0x3546)
+#define WCD939X_COMPANDER_HPHL_CTL7 (0x3547)
+#define WCD939X_COMPANDER_HPHL_CTL8 (0x3548)
+#define WCD939X_COMPANDER_HPHL_CTL9 (0x3549)
+#define WCD939X_COMPANDER_HPHL_CTL10 (0x354a)
+#define WCD939X_COMPANDER_HPHL_CTL11 (0x354b)
+#define WCD939X_COMPANDER_HPHL_CTL12 (0x354c)
+#define WCD939X_COMPANDER_HPHL_CTL13 (0x354d)
+#define WCD939X_COMPANDER_HPHL_CTL14 (0x354e)
+#define WCD939X_COMPANDER_HPHL_CTL15 (0x354f)
+#define WCD939X_COMPANDER_HPHL_CTL16 (0x3550)
+#define WCD939X_COMPANDER_HPHL_CTL17 (0x3551)
+#define WCD939X_COMPANDER_HPHL_CTL18 (0x3552)
+#define WCD939X_COMPANDER_HPHL_CTL19 (0x3553)
+#define WCD939X_R_CTL0 (0x3560)
+#define WCD939X_R_CTL1 (0x3561)
+#define WCD939X_R_CTL2 (0x3562)
+#define WCD939X_R_CTL3 (0x3563)
+#define WCD939X_R_CTL4 (0x3564)
+#define WCD939X_R_CTL5 (0x3565)
+#define WCD939X_R_CTL6 (0x3566)
+#define WCD939X_R_CTL7 (0x3567)
+#define WCD939X_R_CTL8 (0x3568)
+#define WCD939X_R_CTL9 (0x3569)
+#define WCD939X_R_CTL10 (0x356a)
+#define WCD939X_R_CTL11 (0x356b)
+#define WCD939X_R_CTL12 (0x356c)
+#define WCD939X_R_CTL13 (0x356d)
+#define WCD939X_R_CTL14 (0x356e)
+#define WCD939X_R_CTL15 (0x356f)
+#define WCD939X_R_CTL16 (0x3570)
+#define WCD939X_R_CTL17 (0x3571)
+#define WCD939X_R_CTL18 (0x3572)
+#define WCD939X_R_CTL19 (0x3573)
+#define WCD939X_E_PATH_CTL (0x3580)
+#define WCD939X_E_CFG0 (0x3581)
+#define WCD939X_CFG0_AUTO_DISABLE_ANC BIT(2)
+#define WCD939X_CFG0_AUTO_DISABLE_DSD BIT(1)
+#define WCD939X_CFG0_IDLE_STEREO BIT(0)
+#define WCD939X_E_CFG1 (0x3582)
+#define WCD939X_E_CFG2 (0x3583)
+#define WCD939X_E_CFG3 (0x3584)
+#define WCD939X_DSD_HPHL_PATH_CTL (0x3590)
+#define WCD939X_DSD_HPHL_CFG0 (0x3591)
+#define WCD939X_DSD_HPHL_CFG1 (0x3592)
+#define WCD939X_DSD_HPHL_CFG2 (0x3593)
+#define WCD939X_DSD_HPHL_CFG3 (0x3594)
+#define WCD939X_DSD_HPHL_CFG4 (0x3595)
+#define WCD939X_DSD_HPHL_CFG5 (0x3596)
+#define WCD939X_DSD_HPHR_PATH_CTL (0x35a0)
+#define WCD939X_DSD_HPHR_CFG0 (0x35a1)
+#define WCD939X_DSD_HPHR_CFG1 (0x35a2)
+#define WCD939X_DSD_HPHR_CFG2 (0x35a3)
+#define WCD939X_DSD_HPHR_CFG3 (0x35a4)
+#define WCD939X_DSD_HPHR_CFG4 (0x35a5)
+#define WCD939X_DSD_HPHR_CFG5 (0x35a6)
+#define WCD939X_MAX_REGISTER (WCD939X_DSD_HPHR_CFG5)
+
+#define WCD939X_MAX_SWR_CH_IDS (15)
+
+enum wcd939x_tx_sdw_ports {
+ WCD939X_ADC_1_4_PORT = 1,
+ WCD939X_ADC_DMIC_1_2_PORT,
+ WCD939X_DMIC_0_3_MBHC_PORT,
+ WCD939X_DMIC_3_7_PORT,
+ WCD939X_MAX_TX_SWR_PORTS = WCD939X_DMIC_3_7_PORT,
+};
+
+enum wcd939x_tx_sdw_channels {
+ WCD939X_ADC1,
+ WCD939X_ADC2,
+ WCD939X_ADC3,
+ WCD939X_ADC4,
+ WCD939X_DMIC0,
+ WCD939X_DMIC1,
+ WCD939X_MBHC,
+ WCD939X_DMIC2,
+ WCD939X_DMIC3,
+ WCD939X_DMIC4,
+ WCD939X_DMIC5,
+ WCD939X_DMIC6,
+ WCD939X_DMIC7,
+};
+
+enum wcd939x_rx_sdw_ports {
+ WCD939X_HPH_PORT = 1,
+ WCD939X_CLSH_PORT,
+ WCD939X_COMP_PORT,
+ WCD939X_LO_PORT,
+ WCD939X_DSD_PORT,
+ WCD939X_HIFI_PCM_PORT,
+ WCD939X_MAX_RX_SWR_PORTS = WCD939X_HIFI_PCM_PORT,
+ WCD939X_MAX_SWR_PORTS = WCD939X_MAX_RX_SWR_PORTS,
+};
+
+enum wcd939x_rx_sdw_channels {
+ WCD939X_HPH_L,
+ WCD939X_HPH_R,
+ WCD939X_CLSH,
+ WCD939X_COMP_L,
+ WCD939X_COMP_R,
+ WCD939X_LO,
+ WCD939X_DSD_L,
+ WCD939X_DSD_R,
+ WCD939X_HIFI_PCM_L,
+ WCD939X_HIFI_PCM_R,
+};
+
+struct wcd939x_priv;
+struct wcd939x_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS];
+ const struct wcd_sdw_ch_info *ch_info;
+ bool port_enable[WCD939X_MAX_SWR_CH_IDS];
+ int active_ports;
+ bool is_tx;
+ struct wcd939x_priv *wcd939x;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_WCD939X_SDW)
+int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction);
+int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+#else
+
+static inline int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
+ struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_SND_SOC_WCD939X_SDW */
+
+#endif /* __WCD939X_H__ */
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
deleted file mode 100644
index 7693c1129bab..000000000000
--- a/sound/soc/codecs/wl1273.c
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * 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_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, 8000);
- snd_pcm_hw_constraint_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 1);
- break;
- case WL1273_MODE_FM_RX:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- pr_err("Cannot play in RX mode.\n");
- return -EINVAL;
- }
- break;
- case WL1273_MODE_FM_TX:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- pr_err("Cannot capture in TX mode.\n");
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- 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/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h
deleted file mode 100644
index 43ec7e668c51..000000000000
--- a/sound/soc/codecs/wl1273.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index e3c34bdc2772..7511c71695c6 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm0010.c -- WM0010 DSP Driver
*
@@ -6,10 +7,6 @@
* 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>
@@ -21,8 +18,7 @@
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
@@ -47,7 +43,7 @@ struct dfw_binrec {
u8 command;
u32 length:24;
u32 address;
- uint8_t data[0];
+ uint8_t data[];
} __packed;
struct dfw_inforec {
@@ -91,15 +87,14 @@ enum wm0010_state {
};
struct wm0010_priv {
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct mutex lock;
struct device *dev;
struct wm0010_pdata pdata;
- int gpio_reset;
- int gpio_reset_value;
+ struct gpio_desc *reset;
struct regulator_bulk_data core_supplies[2];
struct regulator *dbvdd;
@@ -120,14 +115,6 @@ struct wm0010_priv {
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),
};
@@ -158,9 +145,9 @@ static const char *wm0010_state_to_str(enum wm0010_state state)
}
/* Called with wm0010->lock held */
-static void wm0010_halt(struct snd_soc_codec *codec)
+static void wm0010_halt(struct snd_soc_component *component)
{
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
unsigned long flags;
enum wm0010_state state;
@@ -178,8 +165,7 @@ static void wm0010_halt(struct snd_soc_codec *codec)
case WM0010_STAGE2:
case WM0010_FIRMWARE:
/* Remember to put chip back into reset */
- gpio_set_value_cansleep(wm0010->gpio_reset,
- wm0010->gpio_reset_value);
+ gpiod_set_value_cansleep(wm0010->reset, 1);
/* Disable the regulators */
regulator_disable(wm0010->dbvdd);
regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies),
@@ -194,7 +180,7 @@ static void wm0010_halt(struct snd_soc_codec *codec)
struct wm0010_boot_xfer {
struct list_head list;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct completion *done;
struct spi_message m;
struct spi_transfer t;
@@ -219,13 +205,13 @@ static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010)
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);
+ struct snd_soc_component *component = xfer->component;
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
u32 *out32 = xfer->t.rx_buf;
int i;
if (xfer->m.status != 0) {
- dev_err(codec->dev, "SPI transfer failed: %d\n",
+ dev_err(component->dev, "SPI transfer failed: %d\n",
xfer->m.status);
wm0010_mark_boot_failure(wm0010);
if (xfer->done)
@@ -234,11 +220,11 @@ static void wm0010_boot_xfer_complete(void *data)
}
for (i = 0; i < xfer->t.len / 4; i++) {
- dev_dbg(codec->dev, "%d: %04x\n", i, out32[i]);
+ dev_dbg(component->dev, "%d: %04x\n", i, out32[i]);
switch (be32_to_cpu(out32[i])) {
case 0xe0e0e0e0:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%d: ROM error reported in stage 2\n", i);
wm0010_mark_boot_failure(wm0010);
break;
@@ -246,82 +232,82 @@ static void wm0010_boot_xfer_complete(void *data)
case 0x55555555:
if (wm0010->state < WM0010_STAGE2)
break;
- dev_err(codec->dev,
+ dev_err(component->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");
+ dev_dbg(component->dev, "Stage2 loader running\n");
break;
case 0x0fed0007:
- dev_dbg(codec->dev, "CODE_HDR packet received\n");
+ dev_dbg(component->dev, "CODE_HDR packet received\n");
break;
case 0x0fed0008:
- dev_dbg(codec->dev, "CODE_DATA packet received\n");
+ dev_dbg(component->dev, "CODE_DATA packet received\n");
break;
case 0x0fed0009:
- dev_dbg(codec->dev, "Download complete\n");
+ dev_dbg(component->dev, "Download complete\n");
break;
case 0x0fed000c:
- dev_dbg(codec->dev, "Application start\n");
+ dev_dbg(component->dev, "Application start\n");
break;
case 0x0fed000e:
- dev_dbg(codec->dev, "PLL packet received\n");
+ dev_dbg(component->dev, "PLL packet received\n");
wm0010->pll_running = true;
break;
case 0x0fed0025:
- dev_err(codec->dev, "Device reports image too long\n");
+ dev_err(component->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");
+ dev_err(component->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");
+ dev_err(component->dev, "Device reports SPI read overflow\n");
wm0010_mark_boot_failure(wm0010);
break;
case 0x0fed0032:
- dev_err(codec->dev, "Device reports SPI underclock\n");
+ dev_err(component->dev, "Device reports SPI underclock\n");
wm0010_mark_boot_failure(wm0010);
break;
case 0x0fed0033:
- dev_err(codec->dev, "Device reports bad header packet\n");
+ dev_err(component->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");
+ dev_err(component->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");
+ dev_err(component->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");
+ dev_err(component->dev, "Device reports invalid PLL packet\n");
break;
case 0x0fed003a:
- dev_err(codec->dev, "Device reports packet alignment error\n");
+ dev_err(component->dev, "Device reports packet alignment error\n");
wm0010_mark_boot_failure(wm0010);
break;
default:
- dev_err(codec->dev, "Unrecognised return 0x%x\n",
+ dev_err(component->dev, "Unrecognised return 0x%x\n",
be32_to_cpu(out32[i]));
wm0010_mark_boot_failure(wm0010);
break;
@@ -343,14 +329,14 @@ static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len)
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)
+static int wm0010_firmware_load(const char *name, struct snd_soc_component *component)
{
- struct spi_device *spi = to_spi_device(codec->dev);
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct spi_device *spi = to_spi_device(component->dev);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
struct list_head xfer_list;
struct wm0010_boot_xfer *xfer;
int ret;
- struct completion done;
+ DECLARE_COMPLETION_ONSTACK(done);
const struct firmware *fw;
const struct dfw_binrec *rec;
const struct dfw_inforec *inforec;
@@ -360,9 +346,9 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
INIT_LIST_HEAD(&xfer_list);
- ret = request_firmware(&fw, name, codec->dev);
+ ret = request_firmware(&fw, name, component->dev);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request application(%s): %d\n",
+ dev_err(component->dev, "Failed to request application(%s): %d\n",
name, ret);
return ret;
}
@@ -374,29 +360,28 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
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");
+ dev_err(component->dev, "First record not INFO\r\n");
ret = -EINVAL;
goto abort;
}
if (inforec->info_version != INFO_VERSION) {
- dev_err(codec->dev,
+ dev_err(component->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",
+ dev_dbg(component->dev, "Version v%02d INFO record found\r\n",
inforec->info_version);
/* Check it's a DSP file */
if (dsp != DEVICE_ID_WM0010) {
- dev_err(codec->dev, "Not a WM0010 firmware file.\r\n");
+ dev_err(component->dev, "Not a WM0010 firmware file.\r\n");
ret = -EINVAL;
goto abort;
}
@@ -406,7 +391,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
rec = (void *)&rec->data[rec->length];
while (offset < fw->size) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Packet: command %d, data length = 0x%x\r\n",
rec->command, rec->length);
len = rec->length + 8;
@@ -417,7 +402,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
goto abort;
}
- xfer->codec = codec;
+ xfer->component = component;
list_add_tail(&xfer->list, &xfer_list);
out = kzalloc(len, GFP_KERNEL | GFP_DMA);
@@ -461,18 +446,18 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
rec = (void *)&rec->data[rec->length];
if (offset >= fw->size) {
- dev_dbg(codec->dev, "All transfers scheduled\n");
+ dev_dbg(component->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);
+ dev_err(component->dev, "Write failed: %d\n", ret);
goto abort1;
}
if (wm0010->boot_failed) {
- dev_dbg(codec->dev, "Boot fail!\n");
+ dev_dbg(component->dev, "Boot fail!\n");
ret = -EINVAL;
goto abort1;
}
@@ -497,10 +482,10 @@ abort:
return ret;
}
-static int wm0010_stage2_load(struct snd_soc_codec *codec)
+static int wm0010_stage2_load(struct snd_soc_component *component)
{
- struct spi_device *spi = to_spi_device(codec->dev);
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct spi_device *spi = to_spi_device(component->dev);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
const struct firmware *fw;
struct spi_message m;
struct spi_transfer t;
@@ -509,17 +494,17 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec)
int i;
int ret = 0;
- ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev);
+ ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request stage2 loader: %d\n",
+ dev_err(component->dev, "Failed to request stage2 loader: %d\n",
ret);
return ret;
}
- dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
+ dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
/* Copy to local buffer first as vmalloc causes problems for dma */
- img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
+ img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA);
if (!img) {
ret = -ENOMEM;
goto abort2;
@@ -531,8 +516,6 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec)
goto abort1;
}
- memcpy(img, &fw->data[0], fw->size);
-
spi_message_init(&m);
memset(&t, 0, sizeof(t));
t.rx_buf = out;
@@ -542,19 +525,19 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec)
t.speed_hz = wm0010->sysclk / 10;
spi_message_add_tail(&t, &m);
- dev_dbg(codec->dev, "Starting initial download at %dHz\n",
+ dev_dbg(component->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);
+ dev_err(component->dev, "Initial download failed: %d\n", ret);
goto abort;
}
/* Look for errors from the boot ROM */
for (i = 0; i < fw->size; i++) {
if (out[i] != 0x55) {
- dev_err(codec->dev, "Boot ROM error: %x in %d\n",
+ dev_err(component->dev, "Boot ROM error: %x in %d\n",
out[i], i);
wm0010_mark_boot_failure(wm0010);
ret = -EBUSY;
@@ -571,10 +554,10 @@ abort2:
return ret;
}
-static int wm0010_boot(struct snd_soc_codec *codec)
+static int wm0010_boot(struct snd_soc_component *component)
{
- struct spi_device *spi = to_spi_device(codec->dev);
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct spi_device *spi = to_spi_device(component->dev);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
unsigned long flags;
int ret;
struct spi_message m;
@@ -591,7 +574,7 @@ static int wm0010_boot(struct snd_soc_codec *codec)
spin_unlock_irqrestore(&wm0010->irq_lock, flags);
if (wm0010->sysclk > 26000000) {
- dev_err(codec->dev, "Max DSP clock frequency is 26MHz\n");
+ dev_err(component->dev, "Max DSP clock frequency is 26MHz\n");
ret = -ECANCELED;
goto err;
}
@@ -599,7 +582,7 @@ static int wm0010_boot(struct snd_soc_codec *codec)
mutex_lock(&wm0010->lock);
wm0010->pll_running = false;
- dev_dbg(codec->dev, "max_spi_freq: %d\n", wm0010->max_spi_freq);
+ dev_dbg(component->dev, "max_spi_freq: %d\n", wm0010->max_spi_freq);
ret = regulator_bulk_enable(ARRAY_SIZE(wm0010->core_supplies),
wm0010->core_supplies);
@@ -617,26 +600,26 @@ static int wm0010_boot(struct snd_soc_codec *codec)
}
/* Release reset */
- gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value);
+ gpiod_set_value_cansleep(wm0010->reset, 0);
spin_lock_irqsave(&wm0010->irq_lock, flags);
wm0010->state = WM0010_OUT_OF_RESET;
spin_unlock_irqrestore(&wm0010->irq_lock, flags);
if (!wait_for_completion_timeout(&wm0010->boot_completion,
msecs_to_jiffies(20)))
- dev_err(codec->dev, "Failed to get interrupt from DSP\n");
+ dev_err(component->dev, "Failed to get interrupt from DSP\n");
spin_lock_irqsave(&wm0010->irq_lock, flags);
wm0010->state = WM0010_BOOTROM;
spin_unlock_irqrestore(&wm0010->irq_lock, flags);
- ret = wm0010_stage2_load(codec);
+ ret = wm0010_stage2_load(component);
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");
+ dev_err(component->dev, "Failed to get interrupt from DSP loader.\n");
spin_lock_irqsave(&wm0010->irq_lock, flags);
wm0010->state = WM0010_STAGE2;
@@ -656,11 +639,8 @@ static int wm0010_boot(struct snd_soc_codec *codec)
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");
+ if (!out)
goto abort;
- }
img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img_swap)
@@ -680,14 +660,14 @@ static int wm0010_boot(struct snd_soc_codec *codec)
ret = spi_sync(spi, &m);
if (ret) {
- dev_err(codec->dev, "First PLL write failed: %d\n", ret);
+ dev_err(component->dev, "First PLL write failed: %d\n", ret);
goto abort_swap;
}
/* Use a second send of the message to get the return status */
ret = spi_sync(spi, &m);
if (ret) {
- dev_err(codec->dev, "Second PLL write failed: %d\n", ret);
+ dev_err(component->dev, "Second PLL write failed: %d\n", ret);
goto abort_swap;
}
@@ -696,7 +676,7 @@ static int wm0010_boot(struct snd_soc_codec *codec)
/* 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");
+ dev_dbg(component->dev, "PLL packet received\n");
wm0010->pll_running = true;
break;
}
@@ -706,9 +686,9 @@ static int wm0010_boot(struct snd_soc_codec *codec)
kfree(img_swap);
kfree(out);
} else
- dev_dbg(codec->dev, "Not enabling DSP PLL.");
+ dev_dbg(component->dev, "Not enabling DSP PLL.");
- ret = wm0010_firmware_load("wm0010.dfw", codec);
+ ret = wm0010_firmware_load("wm0010.dfw", component);
if (ret != 0)
goto abort;
@@ -727,7 +707,7 @@ abort_out:
kfree(out);
abort:
/* Put the chip back into reset */
- wm0010_halt(codec);
+ wm0010_halt(component);
mutex_unlock(&wm0010->lock);
return ret;
@@ -739,22 +719,23 @@ err:
return ret;
}
-static int wm0010_set_bias_level(struct snd_soc_codec *codec,
+static int wm0010_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE)
- wm0010_boot(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE)
+ wm0010_boot(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) {
mutex_lock(&wm0010->lock);
- wm0010_halt(codec);
+ wm0010_halt(component);
mutex_unlock(&wm0010->lock);
}
break;
@@ -765,10 +746,10 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source,
+static int wm0010_set_sysclk(struct snd_soc_component *component, int source,
int clk_id, unsigned int freq, int dir)
{
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
unsigned int i;
wm0010->sysclk = freq;
@@ -787,18 +768,18 @@ static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source,
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),
+static int wm0010_probe(struct snd_soc_component *component);
+
+static const struct snd_soc_component_driver soc_component_dev_wm0010 = {
+ .probe = wm0010_probe,
+ .set_bias_level = wm0010_set_bias_level,
+ .set_sysclk = wm0010_set_sysclk,
+ .dapm_widgets = wm0010_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets),
+ .dapm_routes = wm0010_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
#define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
@@ -862,18 +843,17 @@ static irqreturn_t wm0010_irq(int irq, void *data)
return IRQ_NONE;
}
-static int wm0010_probe(struct snd_soc_codec *codec)
+static int wm0010_probe(struct snd_soc_component *component)
{
- struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component);
- wm0010->codec = codec;
+ wm0010->component = component;
return 0;
}
static int wm0010_spi_probe(struct spi_device *spi)
{
- unsigned long gpio_flags;
int ret;
int trigger;
int irq;
@@ -913,31 +893,11 @@ static int wm0010_spi_probe(struct spi_device *spi)
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->reset = devm_gpiod_get(wm0010->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(wm0010->reset))
+ return dev_err_probe(wm0010->dev, PTR_ERR(wm0010->reset),
+ "could not get RESET GPIO\n");
+ gpiod_set_consumer_name(wm0010->reset, "wm0010 reset");
wm0010->state = WM0010_POWER_OFF;
@@ -961,7 +921,7 @@ static int wm0010_spi_probe(struct spi_device *spi)
if (ret) {
dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n",
irq, ret);
- return ret;
+ goto free_irq;
}
if (spi->max_speed_hz)
@@ -969,30 +929,34 @@ static int wm0010_spi_probe(struct spi_device *spi)
else
wm0010->board_max_spi_speed = 0;
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm0010, wm0010_dai,
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_wm0010, wm0010_dai,
ARRAY_SIZE(wm0010_dai));
if (ret < 0)
- return ret;
+ goto disable_irq_wake;
return 0;
+
+disable_irq_wake:
+ irq_set_irq_wake(wm0010->irq, 0);
+
+free_irq:
+ if (wm0010->irq)
+ free_irq(wm0010->irq, wm0010);
+
+ return ret;
}
-static int wm0010_spi_remove(struct spi_device *spi)
+static void 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);
+ gpiod_set_value_cansleep(wm0010->reset, 1);
irq_set_irq_wake(wm0010->irq, 0);
if (wm0010->irq)
free_irq(wm0010->irq, wm0010);
-
- return 0;
}
static struct spi_driver wm0010_spi_driver = {
@@ -1008,3 +972,6 @@ module_spi_driver(wm0010_spi_driver);
MODULE_DESCRIPTION("ASoC WM0010 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("wm0010.dfw");
+MODULE_FIRMWARE("wm0010_stage2.bin");
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index ec45c5b220b1..1f59309d8c69 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -1,47 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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 <linux/gpio/consumer.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];
+ struct gpio_desc *clk_ena;
+ struct gpio_desc *clk_sel0;
+ struct gpio_desc *clk_sel1;
+ struct gpio_desc *osr;
+ struct gpio_desc *master;
};
-static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec,
+static int wm1250_ev1_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm1250_priv *wm1250 = dev_get_drvdata(codec->dev);
- int ena;
-
- if (wm1250)
- ena = wm1250->gpios[WM1250_EV1_GPIO_CLK_ENA].gpio;
- else
- ena = -1;
+ struct wm1250_priv *wm1250 = dev_get_drvdata(component->dev);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -51,13 +35,11 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (ena >= 0)
- gpio_set_value_cansleep(ena, 1);
+ gpiod_set_value_cansleep(wm1250->clk_ena, 1);
break;
case SND_SOC_BIAS_OFF:
- if (ena >= 0)
- gpio_set_value_cansleep(ena, 0);
+ gpiod_set_value_cansleep(wm1250->clk_ena, 0);
break;
}
@@ -81,32 +63,24 @@ 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);
+ struct wm1250_priv *wm1250 = snd_soc_component_get_drvdata(dai->component);
switch (params_rate(params)) {
case 8000:
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
- 1);
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
- 1);
+ gpiod_set_value(wm1250->clk_sel0, 1);
+ gpiod_set_value(wm1250->clk_sel1, 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);
+ gpiod_set_value(wm1250->clk_sel0, 0);
+ gpiod_set_value(wm1250->clk_sel1, 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);
+ gpiod_set_value(wm1250->clk_sel0, 1);
+ gpiod_set_value(wm1250->clk_sel1, 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);
+ gpiod_set_value(wm1250->clk_sel0, 0);
+ gpiod_set_value(wm1250->clk_sel1, 0);
break;
default:
return -EINVAL;
@@ -141,63 +115,59 @@ static struct snd_soc_dai_driver wm1250_ev1_dai = {
.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 const struct snd_soc_component_driver soc_component_dev_wm1250_ev1 = {
+ .dapm_widgets = wm1250_ev1_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets),
+ .dapm_routes = wm1250_ev1_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes),
+ .set_bias_level = wm1250_ev1_set_bias_level,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm1250_ev1_pdata(struct i2c_client *i2c)
{
struct wm1250_ev1_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm1250_priv *wm1250;
- int i, ret;
if (!pdata)
return 0;
wm1250 = devm_kzalloc(&i2c->dev, sizeof(*wm1250), GFP_KERNEL);
- if (!wm1250) {
- ret = -ENOMEM;
- goto err;
- }
-
- for (i = 0; i < ARRAY_SIZE(wm1250->gpios); i++) {
- wm1250->gpios[i].gpio = pdata->gpios[i];
- wm1250->gpios[i].label = wm1250_gpio_names[i];
- wm1250->gpios[i].flags = GPIOF_OUT_INIT_LOW;
- }
- wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].flags = GPIOF_OUT_INIT_HIGH;
- wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].flags = GPIOF_OUT_INIT_HIGH;
-
- ret = gpio_request_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to get GPIOs: %d\n", ret);
- goto err;
- }
+ if (!wm1250)
+ return -ENOMEM;
+
+ wm1250->clk_ena = devm_gpiod_get(&i2c->dev, "clk-ena", GPIOD_OUT_LOW);
+ if (IS_ERR(wm1250->clk_ena))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_ena),
+ "failed to get clock enable GPIO\n");
+
+ wm1250->clk_sel0 = devm_gpiod_get(&i2c->dev, "clk-sel0", GPIOD_OUT_HIGH);
+ if (IS_ERR(wm1250->clk_sel0))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_sel0),
+ "failed to get clock sel0 GPIO\n");
+
+ wm1250->clk_sel1 = devm_gpiod_get(&i2c->dev, "clk-sel1", GPIOD_OUT_HIGH);
+ if (IS_ERR(wm1250->clk_sel1))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_sel1),
+ "failed to get clock sel1 GPIO\n");
+
+ wm1250->osr = devm_gpiod_get(&i2c->dev, "osr", GPIOD_OUT_LOW);
+ if (IS_ERR(wm1250->osr))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->osr),
+ "failed to get OSR GPIO\n");
+
+ wm1250->master = devm_gpiod_get(&i2c->dev, "master", GPIOD_OUT_LOW);
+ if (IS_ERR(wm1250->master))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->master),
+ "failed to get MASTER GPIO\n");
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));
+ return 0;
}
-static int wm1250_ev1_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static int wm1250_ev1_probe(struct i2c_client *i2c)
{
int id, board, rev, ret;
@@ -223,27 +193,18 @@ static int wm1250_ev1_probe(struct i2c_client *i2c,
if (ret != 0)
return ret;
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_wm1250_ev1,
&wm1250_ev1_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
- wm1250_ev1_free(i2c);
return ret;
}
return 0;
}
-static 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 },
+ { "wm1250-ev1" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
@@ -253,7 +214,6 @@ static struct i2c_driver wm1250_ev1_i2c_driver = {
.name = "wm1250-ev1",
},
.probe = wm1250_ev1_probe,
- .remove = wm1250_ev1_remove,
.id_table = wm1250_ev1_i2c_id,
};
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index a67ea10f41a1..126be2a2a8f3 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm2000.c -- WM2000 ALSA Soc Audio driver
*
@@ -5,15 +6,11 @@
*
* 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
+ * system-specific calibration information. If supplied as a
* sequence of ASCII-encoded hexidecimal bytes this can be converted
* into a flat binary with a command such as this on the command line:
*
@@ -88,19 +85,6 @@ static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
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;
@@ -115,14 +99,15 @@ static void wm2000_reset(struct wm2000_priv *wm2000)
static int wm2000_poll_bit(struct i2c_client *i2c,
unsigned int reg, u8 mask)
{
+ struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c);
int timeout = 4000;
- int val;
+ unsigned int val;
- val = wm2000_read(i2c, reg);
+ regmap_read(wm2000->regmap, reg, &val);
while (!(val & mask) && --timeout) {
msleep(1);
- val = wm2000_read(i2c, reg);
+ regmap_read(wm2000->regmap, reg, &val);
}
if (timeout == 0)
@@ -135,6 +120,7 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
{
struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
unsigned long rate;
+ unsigned int val;
int ret;
if (WARN_ON(wm2000->anc_mode != ANC_OFF))
@@ -213,12 +199,17 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
WM2000_MODE_THERMAL_ENABLE);
}
- ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
+ ret = regmap_read(wm2000->regmap, WM2000_REG_SPEECH_CLARITY, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Unable to read Speech Clarity: %d\n", ret);
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ return ret;
+ }
if (wm2000->speech_clarity)
- ret |= WM2000_SPEECH_CLARITY;
+ val |= WM2000_SPEECH_CLARITY;
else
- ret &= ~WM2000_SPEECH_CLARITY;
- wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
+ val &= ~WM2000_SPEECH_CLARITY;
+ wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, val);
wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02);
@@ -545,7 +536,7 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
{
struct i2c_client *i2c = wm2000->i2c;
int i, j;
- int ret;
+ int ret = 0;
if (wm2000->anc_mode == mode)
return 0;
@@ -575,7 +566,7 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
ret = anc_transitions[i].step[j](i2c,
anc_transitions[i].analogue);
if (ret != 0)
- return ret;
+ break;
}
if (anc_transitions[i].dest == ANC_OFF)
@@ -607,8 +598,8 @@ static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
ucontrol->value.integer.value[0] = wm2000->anc_active;
@@ -618,8 +609,8 @@ static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
unsigned int anc_active = ucontrol->value.integer.value[0];
int ret;
@@ -640,8 +631,8 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
ucontrol->value.integer.value[0] = wm2000->spk_ena;
@@ -651,8 +642,8 @@ static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
unsigned int val = ucontrol->value.integer.value[0];
int ret;
@@ -683,8 +674,8 @@ static const struct snd_kcontrol_new wm2000_controls[] = {
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
int ret;
mutex_lock(&wm2000->lock);
@@ -724,16 +715,16 @@ static const struct snd_soc_dapm_route wm2000_audio_map[] = {
};
#ifdef CONFIG_PM
-static int wm2000_suspend(struct snd_soc_codec *codec)
+static int wm2000_suspend(struct snd_soc_component *component)
{
- struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
return wm2000_anc_transition(wm2000, ANC_OFF);
}
-static int wm2000_resume(struct snd_soc_codec *codec)
+static int wm2000_resume(struct snd_soc_component *component)
{
- struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
return wm2000_anc_set_mode(wm2000);
}
@@ -782,9 +773,9 @@ static const struct regmap_config wm2000_regmap = {
.readable_reg = wm2000_readable_reg,
};
-static int wm2000_probe(struct snd_soc_codec *codec)
+static int wm2000_probe(struct snd_soc_component *component)
{
- struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
/* This will trigger a transition to standby mode by default */
wm2000_anc_set_mode(wm2000);
@@ -792,40 +783,39 @@ static int wm2000_probe(struct snd_soc_codec *codec)
return 0;
}
-static int wm2000_remove(struct snd_soc_codec *codec)
+static void wm2000_remove(struct snd_soc_component *component)
{
- struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev);
- return wm2000_anc_transition(wm2000, ANC_OFF);
+ 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 const struct snd_soc_component_driver soc_component_dev_wm2000 = {
+ .probe = wm2000_probe,
+ .remove = wm2000_remove,
+ .suspend = wm2000_suspend,
+ .resume = wm2000_resume,
+ .controls = wm2000_controls,
+ .num_controls = ARRAY_SIZE(wm2000_controls),
+ .dapm_widgets = wm2000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets),
+ .dapm_routes = wm2000_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
};
-static int wm2000_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static int wm2000_i2c_probe(struct i2c_client *i2c)
{
struct wm2000_priv *wm2000;
struct wm2000_platform_data *pdata;
const char *filename;
const struct firmware *fw = NULL;
int ret, i;
- int reg;
+ unsigned int reg;
u16 id;
- wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv),
- GFP_KERNEL);
+ wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL);
if (!wm2000)
return -ENOMEM;
@@ -858,9 +848,17 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
}
/* Verify that this is a WM2000 */
- reg = wm2000_read(i2c, WM2000_REG_ID1);
+ ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret);
+ return ret;
+ }
id = reg << 8;
- reg = wm2000_read(i2c, WM2000_REG_ID2);
+ ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret);
+ return ret;
+ }
id |= reg & 0xff;
if (id != 0x2000) {
@@ -869,7 +867,11 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
goto err_supplies;
}
- reg = wm2000_read(i2c, WM2000_REG_REVISON);
+ ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret);
+ return ret;
+ }
dev_info(&i2c->dev, "revision %c\n", reg + 'A');
wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK");
@@ -900,7 +902,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
wm2000->anc_download_size,
GFP_KERNEL);
if (wm2000->anc_download == NULL) {
- dev_err(&i2c->dev, "Out of memory\n");
ret = -ENOMEM;
goto err_supplies;
}
@@ -916,7 +917,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
wm2000_reset(wm2000);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm2000, NULL, 0);
err_supplies:
regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
@@ -926,15 +928,8 @@ out:
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 },
+ { "wm2000" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id);
@@ -944,7 +939,6 @@ static struct i2c_driver wm2000_i2c_driver = {
.name = "wm2000",
},
.probe = wm2000_i2c_probe,
- .remove = wm2000_i2c_remove,
.id_table = wm2000_i2c_id,
};
diff --git a/sound/soc/codecs/wm2000.h b/sound/soc/codecs/wm2000.h
index 3870c0e1d246..6d3241dea07d 100644
--- a/sound/soc/codecs/wm2000.h
+++ b/sound/soc/codecs/wm2000.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index fd1439ecb50a..87418c838ca0 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -17,7 +14,7 @@
#include <linux/pm.h>
#include <linux/firmware.h>
#include <linux/gcd.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -33,7 +30,6 @@
#include <sound/wm2200.h>
#include "wm2200.h"
-#include "wmfw.h"
#include "wm_adsp.h"
#define WM2200_DSP_CONTROL_1 0x00
@@ -75,21 +71,16 @@ static const char *wm2200_core_supply_names[WM2200_NUM_CORE_SUPPLIES] = {
"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 snd_soc_component *component;
struct wm2200_pdata pdata;
struct regulator_bulk_data core_supplies[WM2200_NUM_CORE_SUPPLIES];
+ struct gpio_desc *ldo_ena;
+ struct gpio_desc *reset;
struct completion fll_lock;
int fll_fout;
@@ -98,6 +89,8 @@ struct wm2200_priv {
int rev;
int sysclk;
+
+ unsigned int symmetric_rates:1;
};
#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
@@ -154,13 +147,13 @@ static const struct regmap_range_cfg wm2200_ranges[] = {
.window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
};
-static const struct wm_adsp_region wm2200_dsp1_regions[] = {
+static const struct cs_dsp_region wm2200_dsp1_regions[] = {
{ .type = WMFW_ADSP1_PM, .base = WM2200_DSP1_PM_BASE },
{ .type = WMFW_ADSP1_DM, .base = WM2200_DSP1_DM_BASE },
{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP1_ZM_BASE },
};
-static const struct wm_adsp_region wm2200_dsp2_regions[] = {
+static const struct cs_dsp_region wm2200_dsp2_regions[] = {
{ .type = WMFW_ADSP1_PM, .base = WM2200_DSP2_PM_BASE },
{ .type = WMFW_ADSP1_DM, .base = WM2200_DSP2_DM_BASE },
{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE },
@@ -984,9 +977,10 @@ static const struct reg_sequence wm2200_reva_patch[] = {
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);
+ if (wm2200->reset) {
+ /* Descriptor flagged active low, so this will be inverted */
+ gpiod_set_value_cansleep(wm2200->reset, 1);
+ gpiod_set_value_cansleep(wm2200->reset, 0);
return 0;
} else {
@@ -999,7 +993,7 @@ 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[] = {
+static const char * const wm2200_mixer_texts[] = {
"None",
"Tone Generator",
"AEC Loopback",
@@ -1033,7 +1027,7 @@ static const char *wm2200_mixer_texts[] = {
"DSP2.6",
};
-static int wm2200_mixer_values[] = {
+static unsigned int wm2200_mixer_values[] = {
0x00,
0x04, /* Tone */
0x08, /* AEC */
@@ -1153,8 +1147,8 @@ SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L,
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),
+SND_SOC_BYTES("LHPF1 Coefficients", WM2200_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", WM2200_HPLPF2_2, 1),
SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L,
WM2200_OUT1_OSR_SHIFT, 1, 0),
@@ -1178,6 +1172,9 @@ SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L,
SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT,
WM2200_SPK1R_MUTE_SHIFT, 1, 1),
SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
};
WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE);
@@ -1548,23 +1545,18 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
WM2200_MIXER_ROUTES("LHPF2", "LHPF2"),
};
-static int wm2200_probe(struct snd_soc_codec *codec)
+static int wm2200_probe(struct snd_soc_component *component)
{
- struct wm2200_priv *wm2200 = dev_get_drvdata(codec->dev);
- int ret;
-
- wm2200->codec = codec;
+ struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component);
- ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2);
- if (ret != 0)
- return ret;
+ wm2200->component = component;
- return ret;
+ return 0;
}
static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int lrclk, bclk, fmt_val;
lrclk = 0;
@@ -1578,26 +1570,26 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
fmt_val = 2;
break;
default:
- dev_err(codec->dev, "Unsupported DAI format %d\n",
+ dev_err(component->dev, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
lrclk |= WM2200_AIF1TX_LRCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
bclk |= WM2200_AIF1_BCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
lrclk |= WM2200_AIF1TX_LRCLK_MSTR;
bclk |= WM2200_AIF1_BCLK_MSTR;
break;
default:
- dev_err(codec->dev, "Unsupported master mode %d\n",
+ dev_err(component->dev, "Unsupported master mode %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL;
}
@@ -1619,15 +1611,15 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_1, WM2200_AIF1_BCLK_MSTR |
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_1, WM2200_AIF1_BCLK_MSTR |
WM2200_AIF1_BCLK_INV, bclk);
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_2,
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_2,
WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV,
lrclk);
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_3,
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_3,
WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV,
lrclk);
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_5,
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_5,
WM2200_AIF1_FMT_MASK, fmt_val);
return 0;
@@ -1696,8 +1688,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component);
int i, bclk, lrclk, wl, fl, sr_code;
int *bclk_rates;
@@ -1709,7 +1701,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
if (fl < 0)
return fl;
- dev_dbg(codec->dev, "Word length %d bits, frame length %d bits\n",
+ dev_dbg(component->dev, "Word length %d bits, frame length %d bits\n",
wl, fl);
/* Target BCLK rate */
@@ -1718,7 +1710,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
return bclk;
if (!wm2200->sysclk) {
- dev_err(codec->dev, "SYSCLK has no rate set\n");
+ dev_err(component->dev, "SYSCLK has no rate set\n");
return -EINVAL;
}
@@ -1726,13 +1718,13 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
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",
+ dev_err(component->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",
+ dev_dbg(component->dev, "Target BCLK is %dHz, using %dHz SYSCLK\n",
bclk, wm2200->sysclk);
if (wm2200->sysclk % 4000)
@@ -1744,52 +1736,47 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
if (bclk_rates[i] >= bclk && (bclk_rates[i] % bclk == 0))
break;
if (i == WM2200_NUM_BCLK_RATES) {
- dev_err(codec->dev,
+ dev_err(component->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,
+ dev_dbg(component->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]);
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_1,
WM2200_AIF1_BCLK_DIV_MASK, bclk);
lrclk = bclk_rates[bclk] / params_rate(params);
- dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
+ dev_dbg(component->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->symmetric_rates)
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_7,
WM2200_AIF1RX_BCPF_MASK, lrclk);
else
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_6,
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_6,
WM2200_AIF1TX_BCPF_MASK, lrclk);
i = (wl << WM2200_AIF1TX_WL_SHIFT) | wl;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_9,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_8,
WM2200_AIF1TX_WL_MASK |
WM2200_AIF1TX_SLOT_LEN_MASK, i);
- snd_soc_update_bits(codec, WM2200_CLOCKING_4,
+ snd_soc_component_update_bits(component, 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,
+static int wm2200_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
+ struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component);
int fval;
switch (clk_id) {
@@ -1797,7 +1784,7 @@ static int wm2200_set_sysclk(struct snd_soc_codec *codec, int clk_id,
break;
default:
- dev_err(codec->dev, "Unknown clock %d\n", clk_id);
+ dev_err(component->dev, "Unknown clock %d\n", clk_id);
return -EINVAL;
}
@@ -1808,7 +1795,7 @@ static int wm2200_set_sysclk(struct snd_soc_codec *codec, int clk_id,
case WM2200_CLKSRC_BCLK1:
break;
default:
- dev_err(codec->dev, "Invalid source %d\n", source);
+ dev_err(component->dev, "Invalid source %d\n", source);
return -EINVAL;
}
@@ -1818,7 +1805,7 @@ static int wm2200_set_sysclk(struct snd_soc_codec *codec, int clk_id,
fval = 2;
break;
default:
- dev_err(codec->dev, "Invalid clock rate: %d\n", freq);
+ dev_err(component->dev, "Invalid clock rate: %d\n", freq);
return -EINVAL;
}
@@ -1826,7 +1813,7 @@ static int wm2200_set_sysclk(struct snd_soc_codec *codec, int clk_id,
* match.
*/
- snd_soc_update_bits(codec, WM2200_CLOCKING_3, WM2200_SYSCLK_FREQ_MASK |
+ snd_soc_component_update_bits(component, WM2200_CLOCKING_3, WM2200_SYSCLK_FREQ_MASK |
WM2200_SYSCLK_SRC_MASK,
fval << WM2200_SYSCLK_FREQ_SHIFT | source);
@@ -1934,23 +1921,23 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+static int wm2200_set_fll(struct snd_soc_component *component, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component);
struct _fll_div factors;
int ret, i, timeout;
unsigned long time_left;
if (!Fout) {
- dev_dbg(codec->dev, "FLL disabled");
+ dev_dbg(component->dev, "FLL disabled");
if (wm2200->fll_fout)
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
wm2200->fll_fout = 0;
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_1,
WM2200_FLL_ENA, 0);
return 0;
}
@@ -1961,7 +1948,7 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
case WM2200_FLL_SRC_BCLK:
break;
default:
- dev_err(codec->dev, "Invalid FLL source %d\n", source);
+ dev_err(component->dev, "Invalid FLL source %d\n", source);
return -EINVAL;
}
@@ -1970,44 +1957,44 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
return ret;
/* Disable the FLL while we reconfigure */
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_1, WM2200_FLL_ENA, 0);
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_1, WM2200_FLL_ENA, 0);
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_2,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_2,
WM2200_FLL_OUTDIV_MASK | WM2200_FLL_FRATIO_MASK,
(factors.fll_outdiv << WM2200_FLL_OUTDIV_SHIFT) |
factors.fll_fratio);
if (factors.theta) {
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_3,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_3,
WM2200_FLL_FRACN_ENA,
WM2200_FLL_FRACN_ENA);
- snd_soc_update_bits(codec, WM2200_FLL_EFS_2,
+ snd_soc_component_update_bits(component, WM2200_FLL_EFS_2,
WM2200_FLL_EFS_ENA,
WM2200_FLL_EFS_ENA);
} else {
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_3,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_3,
WM2200_FLL_FRACN_ENA, 0);
- snd_soc_update_bits(codec, WM2200_FLL_EFS_2,
+ snd_soc_component_update_bits(component, WM2200_FLL_EFS_2,
WM2200_FLL_EFS_ENA, 0);
}
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_4, WM2200_FLL_THETA_MASK,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_4, WM2200_FLL_THETA_MASK,
factors.theta);
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_6, WM2200_FLL_N_MASK,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_6, WM2200_FLL_N_MASK,
factors.n);
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_7,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_7,
WM2200_FLL_CLK_REF_DIV_MASK |
WM2200_FLL_CLK_REF_SRC_MASK,
(factors.fll_refclk_div
<< WM2200_FLL_CLK_REF_DIV_SHIFT) | source);
- snd_soc_update_bits(codec, WM2200_FLL_EFS_1,
+ snd_soc_component_update_bits(component, WM2200_FLL_EFS_1,
WM2200_FLL_LAMBDA_MASK, factors.lambda);
/* Clear any pending completions */
try_wait_for_completion(&wm2200->fll_lock);
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
- snd_soc_update_bits(codec, WM2200_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_1,
WM2200_FLL_ENA, WM2200_FLL_ENA);
if (i2c->irq)
@@ -2015,7 +2002,7 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
else
timeout = 50;
- snd_soc_update_bits(codec, WM2200_CLOCKING_3, WM2200_SYSCLK_ENA,
+ snd_soc_component_update_bits(component, WM2200_CLOCKING_3, WM2200_SYSCLK_ENA,
WM2200_SYSCLK_ENA);
/* Poll for the lock; will use the interrupt to exit quickly */
@@ -2030,10 +2017,10 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
msleep(1);
}
- ret = snd_soc_read(codec,
+ ret = snd_soc_component_read(component,
WM2200_INTERRUPT_RAW_STATUS_2);
if (ret < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to read FLL status: %d\n",
ret);
continue;
@@ -2042,8 +2029,8 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
break;
}
if (i == timeout) {
- dev_err(codec->dev, "FLL lock timed out\n");
- pm_runtime_put(codec->dev);
+ dev_err(component->dev, "FLL lock timed out\n");
+ pm_runtime_put(component->dev);
return -ETIMEDOUT;
}
@@ -2051,33 +2038,40 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
wm2200->fll_fref = Fref;
wm2200->fll_fout = Fout;
- dev_dbg(codec->dev, "FLL running %dHz->%dHz\n", Fref, Fout);
+ dev_dbg(component->dev, "FLL running %dHz->%dHz\n", Fref, Fout);
return 0;
}
static int wm2200_dai_probe(struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
+ struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component);
unsigned int val = 0;
int ret;
- ret = snd_soc_read(codec, WM2200_GPIO_CTRL_1);
+ ret = snd_soc_component_read(component, WM2200_GPIO_CTRL_1);
if (ret >= 0) {
if ((ret & WM2200_GP1_FN_MASK) != 0) {
- dai->symmetric_rates = true;
+ wm2200->symmetric_rates = true;
val = WM2200_AIF1TX_LRCLK_SRC;
}
} else {
- dev_err(codec->dev, "Failed to read GPIO 1 config: %d\n", ret);
+ dev_err(component->dev, "Failed to read GPIO 1 config: %d\n", ret);
}
- snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_2,
+ snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_2,
WM2200_AIF1TX_LRCLK_SRC, val);
return 0;
}
+static const struct snd_soc_dai_ops wm2200_dai_ops = {
+ .probe = wm2200_dai_probe,
+ .set_fmt = wm2200_set_fmt,
+ .hw_params = wm2200_hw_params,
+};
+
#define WM2200_RATES SNDRV_PCM_RATE_8000_48000
#define WM2200_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
@@ -2085,7 +2079,6 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver wm2200_dai = {
.name = "wm2200",
- .probe = wm2200_dai_probe,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
@@ -2103,20 +2096,17 @@ static struct snd_soc_dai_driver wm2200_dai = {
.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 const struct snd_soc_component_driver soc_component_wm2200 = {
+ .probe = wm2200_probe,
+ .set_sysclk = wm2200_set_sysclk,
+ .set_pll = wm2200_set_fll,
+ .controls = wm2200_snd_controls,
+ .num_controls = ARRAY_SIZE(wm2200_snd_controls),
+ .dapm_widgets = wm2200_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm2200_dapm_widgets),
+ .dapm_routes = wm2200_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm2200_dapm_routes),
+ .endianness = 1,
};
static irqreturn_t wm2200_irq(int irq, void *data)
@@ -2164,7 +2154,7 @@ static const struct regmap_config wm2200_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults),
.volatile_reg = wm2200_volatile_register,
.readable_reg = wm2200_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.ranges = wm2200_ranges,
.num_ranges = ARRAY_SIZE(wm2200_ranges),
};
@@ -2188,8 +2178,7 @@ static const unsigned int wm2200_mic_ctrl_reg[] = {
WM2200_IN3L_CONTROL,
};
-static int wm2200_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm2200_i2c_probe(struct i2c_client *i2c)
{
struct wm2200_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm2200_priv *wm2200;
@@ -2214,23 +2203,23 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
}
for (i = 0; i < 2; i++) {
- wm2200->dsp[i].type = WMFW_ADSP1;
+ wm2200->dsp[i].cs_dsp.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[i].cs_dsp.num = i + 1;
+ wm2200->dsp[i].cs_dsp.dev = &i2c->dev;
+ wm2200->dsp[i].cs_dsp.regmap = wm2200->regmap;
+ wm2200->dsp[i].cs_dsp.sysclk_reg = WM2200_CLOCKING_3;
+ wm2200->dsp[i].cs_dsp.sysclk_mask = WM2200_SYSCLK_FREQ_MASK;
+ wm2200->dsp[i].cs_dsp.sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT;
}
- wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1;
- wm2200->dsp[0].mem = wm2200_dsp1_regions;
- wm2200->dsp[0].num_mems = ARRAY_SIZE(wm2200_dsp1_regions);
+ wm2200->dsp[0].cs_dsp.base = WM2200_DSP1_CONTROL_1;
+ wm2200->dsp[0].cs_dsp.mem = wm2200_dsp1_regions;
+ wm2200->dsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp1_regions);
- wm2200->dsp[1].base = WM2200_DSP2_CONTROL_1;
- wm2200->dsp[1].mem = wm2200_dsp2_regions;
- wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
+ wm2200->dsp[1].cs_dsp.base = WM2200_DSP2_CONTROL_1;
+ wm2200->dsp[1].cs_dsp.mem = wm2200_dsp2_regions;
+ wm2200->dsp[1].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++)
wm_adsp1_init(&wm2200->dsp[i]);
@@ -2260,28 +2249,28 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
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;
- }
+ wm2200->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wm2200->ldo_ena)) {
+ ret = PTR_ERR(wm2200->ldo_ena);
+ dev_err(&i2c->dev, "Failed to request LDOENA GPIO %d\n",
+ ret);
+ goto err_enable;
+ }
+ if (wm2200->ldo_ena) {
+ gpiod_set_consumer_name(wm2200->ldo_ena, "WM2200 LDOENA");
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;
- }
+ wm2200->reset = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm2200->reset)) {
+ ret = PTR_ERR(wm2200->reset);
+ dev_err(&i2c->dev, "Failed to request RESET GPIO %d\n",
+ ret);
+ goto err_ldo;
}
+ gpiod_set_consumer_name(wm2200->reset, "WM2200 /RESET");
ret = regmap_read(wm2200->regmap, WM2200_SOFTWARE_RESET, &reg);
if (ret < 0) {
@@ -2403,7 +2392,7 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_wm2200,
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_wm2200,
&wm2200_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
@@ -2414,42 +2403,39 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm2200);
err_reset:
- if (wm2200->pdata.reset)
- gpio_set_value_cansleep(wm2200->pdata.reset, 0);
+ gpiod_set_value_cansleep(wm2200->reset, 1);
err_ldo:
- if (wm2200->pdata.ldo_ena)
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ gpiod_set_value_cansleep(wm2200->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)
+static void wm2200_i2c_remove(struct i2c_client *i2c)
{
struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
- snd_soc_unregister_codec(&i2c->dev);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
- if (wm2200->pdata.reset)
- gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- if (wm2200->pdata.ldo_ena)
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
-
- return 0;
+ /* Assert RESET, disable LDO */
+ gpiod_set_value_cansleep(wm2200->reset, 1);
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
}
-#ifdef CONFIG_PM
static int wm2200_runtime_suspend(struct device *dev)
{
struct wm2200_priv *wm2200 = dev_get_drvdata(dev);
regcache_cache_only(wm2200->regmap, true);
regcache_mark_dirty(wm2200->regmap);
- if (wm2200->pdata.ldo_ena)
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
@@ -2469,8 +2455,8 @@ static int wm2200_runtime_resume(struct device *dev)
return ret;
}
- if (wm2200->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 1);
+ if (wm2200->ldo_ena) {
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 1);
msleep(2);
}
@@ -2479,15 +2465,13 @@ static int wm2200_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops wm2200_pm = {
- SET_RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume, NULL)
};
static const struct i2c_device_id wm2200_i2c_id[] = {
- { "wm2200", 0 },
+ { "wm2200" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id);
@@ -2495,7 +2479,7 @@ MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id);
static struct i2c_driver wm2200_i2c_driver = {
.driver = {
.name = "wm2200",
- .pm = &wm2200_pm,
+ .pm = pm_ptr(&wm2200_pm),
},
.probe = wm2200_i2c_probe,
.remove = wm2200_i2c_remove,
diff --git a/sound/soc/codecs/wm2200.h b/sound/soc/codecs/wm2200.h
index 5d719d6b4a8d..906117bd366c 100644
--- a/sound/soc/codecs/wm2200.h
+++ b/sound/soc/codecs/wm2200.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c
index e239f4bf2460..9a6ce8f2c9fc 100644
--- a/sound/soc/codecs/wm5100-tables.c
+++ b/sound/soc/codecs/wm5100-tables.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm5100-tables.c -- WM5100 ALSA SoC Audio driver data
*
* Copyright 2011-2 Wolfson Microelectronics plc
*
* Author: Mark Brown <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"
@@ -30,7 +26,7 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg)
case WM5100_OUTPUT_STATUS_2:
case WM5100_INPUT_ENABLES_STATUS:
case WM5100_MIC_DETECT_3:
- return 1;
+ return true;
default:
if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
(reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
@@ -41,9 +37,9 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg)
(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;
+ return true;
else
- return 0;
+ return false;
}
}
@@ -798,7 +794,7 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg)
case WM5100_DSP3_CONTROL_28:
case WM5100_DSP3_CONTROL_29:
case WM5100_DSP3_CONTROL_30:
- return 1;
+ return true;
default:
if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
(reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
@@ -809,9 +805,9 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg)
(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;
+ return true;
else
- return 0;
+ return false;
}
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 171a23ddd15d..96fd098a9d36 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -17,7 +14,8 @@
#include <linux/export.h>
#include <linux/pm.h>
#include <linux/gcd.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -54,9 +52,12 @@ struct wm5100_fll {
struct wm5100_priv {
struct device *dev;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regulator_bulk_data core_supplies[WM5100_NUM_CORE_SUPPLIES];
+ struct gpio_desc *reset;
+ struct gpio_desc *ldo_ena;
+ struct gpio_desc *hp_pol;
int rev;
@@ -117,16 +118,16 @@ static int wm5100_sr_regs[WM5100_SYNC_SRS] = {
WM5100_CLOCKING_6,
};
-static int wm5100_alloc_sr(struct snd_soc_codec *codec, int rate)
+static int wm5100_alloc_sr(struct snd_soc_component *component, int rate)
{
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
int sr_code, sr_free, i;
for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++)
if (wm5100_sr_code[i] == rate)
break;
if (i == ARRAY_SIZE(wm5100_sr_code)) {
- dev_err(codec->dev, "Unsupported sample rate: %dHz\n", rate);
+ dev_err(component->dev, "Unsupported sample rate: %dHz\n", rate);
return -EINVAL;
}
sr_code = i;
@@ -139,50 +140,50 @@ static int wm5100_alloc_sr(struct snd_soc_codec *codec, int rate)
sr_free = i;
continue;
}
- if ((snd_soc_read(codec, wm5100_sr_regs[i]) &
+ if ((snd_soc_component_read(component, wm5100_sr_regs[i]) &
WM5100_SAMPLE_RATE_1_MASK) == sr_code)
break;
}
if (i < ARRAY_SIZE(wm5100_sr_regs)) {
wm5100->sr_ref[i]++;
- dev_dbg(codec->dev, "SR %dHz, slot %d, ref %d\n",
+ dev_dbg(component->dev, "SR %dHz, slot %d, ref %d\n",
rate, i, wm5100->sr_ref[i]);
return i;
}
if (sr_free == -1) {
- dev_err(codec->dev, "All SR slots already in use\n");
+ dev_err(component->dev, "All SR slots already in use\n");
return -EBUSY;
}
- dev_dbg(codec->dev, "Allocating SR slot %d for %dHz\n",
+ dev_dbg(component->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],
+ snd_soc_component_update_bits(component, wm5100_sr_regs[sr_free],
WM5100_SAMPLE_RATE_1_MASK,
sr_code);
return sr_free;
} else {
- dev_err(codec->dev,
+ dev_err(component->dev,
"SR %dHz incompatible with %dHz SYSCLK and %dHz ASYNCCLK\n",
rate, wm5100->sysclk, wm5100->asyncclk);
return -EINVAL;
}
}
-static void wm5100_free_sr(struct snd_soc_codec *codec, int rate)
+static void wm5100_free_sr(struct snd_soc_component *component, int rate)
{
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
int i, sr_code;
for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++)
if (wm5100_sr_code[i] == rate)
break;
if (i == ARRAY_SIZE(wm5100_sr_code)) {
- dev_err(codec->dev, "Unsupported sample rate: %dHz\n", rate);
+ dev_err(component->dev, "Unsupported sample rate: %dHz\n", rate);
return;
}
sr_code = wm5100_sr_code[i];
@@ -191,25 +192,25 @@ static void wm5100_free_sr(struct snd_soc_codec *codec, int rate)
if (!wm5100->sr_ref[i])
continue;
- if ((snd_soc_read(codec, wm5100_sr_regs[i]) &
+ if ((snd_soc_component_read(component, wm5100_sr_regs[i]) &
WM5100_SAMPLE_RATE_1_MASK) == sr_code)
break;
}
if (i < ARRAY_SIZE(wm5100_sr_regs)) {
wm5100->sr_ref[i]--;
- dev_dbg(codec->dev, "Dereference SR %dHz, count now %d\n",
+ dev_dbg(component->dev, "Dereference SR %dHz, count now %d\n",
rate, wm5100->sr_ref[i]);
} else {
- dev_warn(codec->dev, "Freeing unreferenced sample rate %dHz\n",
+ dev_warn(component->dev, "Freeing unreferenced sample rate %dHz\n",
rate);
}
}
static int wm5100_reset(struct wm5100_priv *wm5100)
{
- if (wm5100->pdata.reset) {
- gpio_set_value_cansleep(wm5100->pdata.reset, 0);
- gpio_set_value_cansleep(wm5100->pdata.reset, 1);
+ if (wm5100->reset) {
+ gpiod_set_value_cansleep(wm5100->reset, 1);
+ gpiod_set_value_cansleep(wm5100->reset, 0);
return 0;
} else {
@@ -572,10 +573,10 @@ 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),
+SND_SOC_BYTES("LHPF1 Coefficients", WM5100_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", WM5100_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", WM5100_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", WM5100_HPLPF4_2, 1),
SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L,
WM5100_OUT1_OSR_SHIFT, 1, 0),
@@ -732,40 +733,39 @@ 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,
+static void wm5100_seq_notifier(struct snd_soc_component *component,
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);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
u16 val, expect, i;
/* Wait for the outputs to flag themselves as enabled */
if (wm5100->out_ena[0]) {
- expect = snd_soc_read(codec, WM5100_CHANNEL_ENABLES_1);
+ expect = snd_soc_component_read(component, WM5100_CHANNEL_ENABLES_1);
for (i = 0; i < 200; i++) {
- val = snd_soc_read(codec, WM5100_OUTPUT_STATUS_1);
+ val = snd_soc_component_read(component, 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",
+ dev_err(component->dev, "Timeout waiting for OUTPUT1 %x\n",
expect);
}
}
if (wm5100->out_ena[1]) {
- expect = snd_soc_read(codec, WM5100_OUTPUT_ENABLES_2);
+ expect = snd_soc_component_read(component, WM5100_OUTPUT_ENABLES_2);
for (i = 0; i < 200; i++) {
- val = snd_soc_read(codec, WM5100_OUTPUT_STATUS_2);
+ val = snd_soc_component_read(component, 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",
+ dev_err(component->dev, "Timeout waiting for OUTPUT2 %x\n",
expect);
}
}
@@ -775,8 +775,8 @@ 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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
switch (w->reg) {
case WM5100_CHANNEL_ENABLES_1:
@@ -840,17 +840,17 @@ 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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
int ret;
- ret = snd_soc_read(codec, WM5100_INTERRUPT_RAW_STATUS_3);
+ ret = snd_soc_component_read(component, WM5100_INTERRUPT_RAW_STATUS_3);
ret &= WM5100_SPK_SHUTDOWN_WARN_STS |
WM5100_SPK_SHUTDOWN_STS | WM5100_CLKGEN_ERR_STS |
WM5100_CLKGEN_ERR_ASYNC_STS;
wm5100_log_status3(wm5100, ret);
- ret = snd_soc_read(codec, WM5100_INTERRUPT_RAW_STATUS_4);
+ ret = snd_soc_component_read(component, WM5100_INTERRUPT_RAW_STATUS_4);
wm5100_log_status4(wm5100, ret);
return 0;
@@ -1281,7 +1281,7 @@ static const struct reg_sequence wm5100_reva_patches[] = {
static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int lrclk, bclk, mask, base;
base = dai->driver->base;
@@ -1297,26 +1297,26 @@ static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
mask = 2;
break;
default:
- dev_err(codec->dev, "Unsupported DAI format %d\n",
+ dev_err(component->dev, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
lrclk |= WM5100_AIF1TX_LRCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
bclk |= WM5100_AIF1_BCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
lrclk |= WM5100_AIF1TX_LRCLK_MSTR;
bclk |= WM5100_AIF1_BCLK_MSTR;
break;
default:
- dev_err(codec->dev, "Unsupported master mode %d\n",
+ dev_err(component->dev, "Unsupported master mode %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL;
}
@@ -1338,13 +1338,13 @@ static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, base + 1, WM5100_AIF1_BCLK_MSTR |
+ snd_soc_component_update_bits(component, base + 1, WM5100_AIF1_BCLK_MSTR |
WM5100_AIF1_BCLK_INV, bclk);
- snd_soc_update_bits(codec, base + 2, WM5100_AIF1TX_LRCLK_MSTR |
+ snd_soc_component_update_bits(component, base + 2, WM5100_AIF1TX_LRCLK_MSTR |
WM5100_AIF1TX_LRCLK_INV, lrclk);
- snd_soc_update_bits(codec, base + 3, WM5100_AIF1TX_LRCLK_MSTR |
+ snd_soc_component_update_bits(component, base + 3, WM5100_AIF1TX_LRCLK_MSTR |
WM5100_AIF1TX_LRCLK_INV, lrclk);
- snd_soc_update_bits(codec, base + 5, WM5100_AIF1_FMT_MASK, mask);
+ snd_soc_component_update_bits(component, base + 5, WM5100_AIF1_FMT_MASK, mask);
return 0;
}
@@ -1399,8 +1399,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
bool async = wm5100->aif_async[dai->id];
int i, base, bclk, aif_rate, lrclk, wl, fl, sr;
int *bclk_rates;
@@ -1415,7 +1415,7 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
if (fl < 0)
return fl;
- dev_dbg(codec->dev, "Word length %d bits, frame length %d bits\n",
+ dev_dbg(component->dev, "Word length %d bits, frame length %d bits\n",
wl, fl);
/* Target BCLK rate */
@@ -1426,7 +1426,7 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
/* Root for BCLK depends on SYS/ASYNCCLK */
if (!async) {
aif_rate = wm5100->sysclk;
- sr = wm5100_alloc_sr(codec, params_rate(params));
+ sr = wm5100_alloc_sr(component, params_rate(params));
if (sr < 0)
return sr;
} else {
@@ -1438,23 +1438,23 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
if (params_rate(params) == wm5100_sr_code[i])
break;
if (i == ARRAY_SIZE(wm5100_sr_code)) {
- dev_err(codec->dev, "Invalid rate %dHzn",
+ dev_err(component->dev, "Invalid rate %dHzn",
params_rate(params));
return -EINVAL;
}
/* TODO: We should really check for symmetry */
- snd_soc_update_bits(codec, WM5100_CLOCKING_8,
+ snd_soc_component_update_bits(component, WM5100_CLOCKING_8,
WM5100_ASYNC_SAMPLE_RATE_MASK, i);
}
if (!aif_rate) {
- dev_err(codec->dev, "%s has no rate set\n",
+ dev_err(component->dev, "%s has no rate set\n",
async ? "ASYNCCLK" : "SYSCLK");
return -EINVAL;
}
- dev_dbg(codec->dev, "Target BCLK is %dHz, using %dHz %s\n",
+ dev_dbg(component->dev, "Target BCLK is %dHz, using %dHz %s\n",
bclk, aif_rate, async ? "ASYNCCLK" : "SYSCLK");
if (aif_rate % 4000)
@@ -1466,37 +1466,37 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
if (bclk_rates[i] >= bclk && (bclk_rates[i] % bclk == 0))
break;
if (i == WM5100_NUM_BCLK_RATES) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"No valid BCLK for %dHz found from %dHz %s\n",
bclk, aif_rate, async ? "ASYNCCLK" : "SYSCLK");
return -EINVAL;
}
bclk = i;
- dev_dbg(codec->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]);
- snd_soc_update_bits(codec, base + 1, WM5100_AIF1_BCLK_FREQ_MASK, bclk);
+ dev_dbg(component->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]);
+ snd_soc_component_update_bits(component, base + 1, WM5100_AIF1_BCLK_FREQ_MASK, bclk);
lrclk = bclk_rates[bclk] / params_rate(params);
- dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
+ dev_dbg(component->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
wm5100->aif_symmetric[dai->id])
- snd_soc_update_bits(codec, base + 7,
+ snd_soc_component_update_bits(component, base + 7,
WM5100_AIF1RX_BCPF_MASK, lrclk);
else
- snd_soc_update_bits(codec, base + 6,
+ snd_soc_component_update_bits(component, base + 6,
WM5100_AIF1TX_BCPF_MASK, lrclk);
i = (wl << WM5100_AIF1TX_WL_SHIFT) | fl;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_update_bits(codec, base + 9,
+ snd_soc_component_update_bits(component, base + 9,
WM5100_AIF1RX_WL_MASK |
WM5100_AIF1RX_SLOT_LEN_MASK, i);
else
- snd_soc_update_bits(codec, base + 8,
+ snd_soc_component_update_bits(component, base + 8,
WM5100_AIF1TX_WL_MASK |
WM5100_AIF1TX_SLOT_LEN_MASK, i);
- snd_soc_update_bits(codec, base + 4, WM5100_AIF1_RATE_MASK, sr);
+ snd_soc_component_update_bits(component, base + 4, WM5100_AIF1_RATE_MASK, sr);
return 0;
}
@@ -1506,10 +1506,10 @@ static const struct snd_soc_dai_ops wm5100_dai_ops = {
.hw_params = wm5100_hw_params,
};
-static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int wm5100_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
int *rate_store;
int fval, audio_rate, ret, reg;
@@ -1528,7 +1528,7 @@ static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
case WM5100_CLKSRC_MCLK1:
case WM5100_CLKSRC_MCLK2:
case WM5100_CLKSRC_SYSCLK:
- snd_soc_update_bits(codec, WM5100_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM5100_CLOCKING_1,
WM5100_CLK_32K_SRC_MASK,
source);
break;
@@ -1549,7 +1549,7 @@ static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
wm5100->aif_async[clk_id - 1] = true;
break;
default:
- dev_err(codec->dev, "Invalid source %d\n", source);
+ dev_err(component->dev, "Invalid source %d\n", source);
return -EINVAL;
}
return 0;
@@ -1558,35 +1558,35 @@ static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
switch (freq) {
case 5644800:
case 6144000:
- snd_soc_update_bits(codec, WM5100_MISC_GPIO_1,
+ snd_soc_component_update_bits(component, WM5100_MISC_GPIO_1,
WM5100_OPCLK_SEL_MASK, 0);
break;
case 11289600:
case 12288000:
- snd_soc_update_bits(codec, WM5100_MISC_GPIO_1,
+ snd_soc_component_update_bits(component, WM5100_MISC_GPIO_1,
WM5100_OPCLK_SEL_MASK, 0);
break;
case 22579200:
case 24576000:
- snd_soc_update_bits(codec, WM5100_MISC_GPIO_1,
+ snd_soc_component_update_bits(component, WM5100_MISC_GPIO_1,
WM5100_OPCLK_SEL_MASK, 0);
break;
default:
- dev_err(codec->dev, "Unsupported OPCLK %dHz\n",
+ dev_err(component->dev, "Unsupported OPCLK %dHz\n",
freq);
return -EINVAL;
}
return 0;
default:
- dev_err(codec->dev, "Unknown clock %d\n", clk_id);
+ dev_err(component->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);
+ dev_err(component->dev, "Invalid source %d\n", source);
return -EINVAL;
}
@@ -1604,7 +1604,7 @@ static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
fval = 2;
break;
default:
- dev_err(codec->dev, "Invalid clock rate: %d\n", freq);
+ dev_err(component->dev, "Invalid clock rate: %d\n", freq);
return -EINVAL;
}
@@ -1631,7 +1631,7 @@ static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
* match.
*/
- snd_soc_update_bits(codec, reg, WM5100_SYSCLK_FREQ_MASK |
+ snd_soc_component_update_bits(component, reg, WM5100_SYSCLK_FREQ_MASK |
WM5100_SYSCLK_SRC_MASK,
fval << WM5100_SYSCLK_FREQ_SHIFT | source);
@@ -1640,13 +1640,13 @@ static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
* this clock rate.
*/
if (clk_id == WM5100_CLK_SYSCLK) {
- dev_dbg(codec->dev, "Setting primary audio rate to %dHz",
+ dev_dbg(component->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);
+ wm5100_free_sr(component, audio_rate);
+ ret = wm5100_alloc_sr(component, audio_rate);
if (ret != 0)
- dev_warn(codec->dev, "Primary audio slot is %d\n",
+ dev_warn(component->dev, "Primary audio slot is %d\n",
ret);
}
@@ -1754,11 +1754,11 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+static int wm5100_set_fll(struct snd_soc_component *component, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
struct _fll_div factors;
struct wm5100_fll *fll;
int ret, base, lock, i, timeout;
@@ -1776,16 +1776,16 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
lock = WM5100_FLL2_LOCK_STS;
break;
default:
- dev_err(codec->dev, "Unknown FLL %d\n",fll_id);
+ dev_err(component->dev, "Unknown FLL %d\n",fll_id);
return -EINVAL;
}
if (!Fout) {
- dev_dbg(codec->dev, "FLL%d disabled", fll_id);
+ dev_dbg(component->dev, "FLL%d disabled", fll_id);
if (fll->fout)
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
fll->fout = 0;
- snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0);
+ snd_soc_component_update_bits(component, base + 1, WM5100_FLL1_ENA, 0);
return 0;
}
@@ -1799,7 +1799,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
case WM5100_FLL_SRC_AIF3BCLK:
break;
default:
- dev_err(codec->dev, "Invalid FLL source %d\n", source);
+ dev_err(component->dev, "Invalid FLL source %d\n", source);
return -EINVAL;
}
@@ -1808,36 +1808,36 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
return ret;
/* Disable the FLL while we reconfigure */
- snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0);
+ snd_soc_component_update_bits(component, base + 1, WM5100_FLL1_ENA, 0);
- snd_soc_update_bits(codec, base + 2,
+ snd_soc_component_update_bits(component, base + 2,
WM5100_FLL1_OUTDIV_MASK | WM5100_FLL1_FRATIO_MASK,
(factors.fll_outdiv << WM5100_FLL1_OUTDIV_SHIFT) |
factors.fll_fratio);
- snd_soc_update_bits(codec, base + 3, WM5100_FLL1_THETA_MASK,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, base + 5, WM5100_FLL1_N_MASK, factors.n);
+ snd_soc_component_update_bits(component, base + 6,
WM5100_FLL1_REFCLK_DIV_MASK |
WM5100_FLL1_REFCLK_SRC_MASK,
(factors.fll_refclk_div
<< WM5100_FLL1_REFCLK_DIV_SHIFT) | source);
- snd_soc_update_bits(codec, base + 7, WM5100_FLL1_LAMBDA_MASK,
+ snd_soc_component_update_bits(component, base + 7, WM5100_FLL1_LAMBDA_MASK,
factors.lambda);
/* Clear any pending completions */
try_wait_for_completion(&fll->lock);
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
- snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA);
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM5100_CLOCKING_3, WM5100_SYSCLK_ENA,
WM5100_SYSCLK_ENA);
/* Poll for the lock; will use interrupt when we can test */
@@ -1851,10 +1851,10 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
msleep(1);
}
- ret = snd_soc_read(codec,
+ ret = snd_soc_component_read(component,
WM5100_INTERRUPT_RAW_STATUS_3);
if (ret < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to read FLL status: %d\n",
ret);
continue;
@@ -1863,8 +1863,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
break;
}
if (i == timeout) {
- dev_err(codec->dev, "FLL%d lock timed out\n", fll_id);
- pm_runtime_put(codec->dev);
+ dev_err(component->dev, "FLL%d lock timed out\n", fll_id);
+ pm_runtime_put(component->dev);
return -ETIMEDOUT;
}
@@ -1872,7 +1872,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
fll->fref = Fref;
fll->fout = Fout;
- dev_dbg(codec->dev, "FLL%d running %dHz->%dHz\n", fll_id,
+ dev_dbg(component->dev, "FLL%d running %dHz->%dHz\n", fll_id,
Fref, Fout);
return 0;
@@ -1977,7 +1977,7 @@ static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int 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);
+ gpiod_set_value_cansleep(wm5100->hp_pol, mode->hp_pol);
regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1,
WM5100_ACCDET_BIAS_SRC_MASK |
WM5100_ACCDET_SRC,
@@ -2013,7 +2013,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100)
ret = regmap_read(wm5100->regmap, WM5100_MIC_DETECT_3, &val);
if (ret != 0) {
- dev_err(wm5100->dev, "Failed to read micropone status: %d\n",
+ dev_err(wm5100->dev, "Failed to read microphone status: %d\n",
ret);
return;
}
@@ -2098,10 +2098,10 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100)
}
}
-int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+int wm5100_detect(struct snd_soc_component *component, struct snd_soc_jack *jack)
{
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
if (jack) {
wm5100->jack = jack;
@@ -2112,7 +2112,7 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
/* Slowest detection rate, gives debounce for initial
* detection */
- snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM5100_MIC_DETECT_1,
WM5100_ACCDET_BIAS_STARTTIME_MASK |
WM5100_ACCDET_RATE_MASK,
(7 << WM5100_ACCDET_BIAS_STARTTIME_SHIFT) |
@@ -2131,18 +2131,18 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
/* We start off just enabling microphone detection - even a
* plain headphone will trigger detection.
*/
- snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM5100_MIC_DETECT_1,
WM5100_ACCDET_ENA, WM5100_ACCDET_ENA);
- snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
+ snd_soc_component_update_bits(component, WM5100_INTERRUPT_STATUS_3_MASK,
WM5100_IM_ACCDET_EINT, 0);
} else {
- snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
+ snd_soc_component_update_bits(component, WM5100_INTERRUPT_STATUS_3_MASK,
WM5100_IM_HPDET_EINT |
WM5100_IM_ACCDET_EINT,
WM5100_IM_HPDET_EINT |
WM5100_IM_ACCDET_EINT);
- snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM5100_MIC_DETECT_1,
WM5100_ACCDET_ENA, 0);
wm5100->jack = NULL;
}
@@ -2236,23 +2236,20 @@ static irqreturn_t wm5100_edge_irq(int irq, void *data)
}
#ifdef CONFIG_GPIOLIB
-static inline struct wm5100_priv *gpio_to_wm5100(struct gpio_chip *chip)
+static int wm5100_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- return container_of(chip, struct wm5100_priv, gpio_chip);
-}
+ struct wm5100_priv *wm5100 = gpiochip_get_data(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);
+ return 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);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
int val, ret;
val = (1 << WM5100_GP1_FN_SHIFT) | (!!value << WM5100_GP1_LVL_SHIFT);
@@ -2268,7 +2265,7 @@ static int wm5100_gpio_direction_out(struct gpio_chip *chip,
static int wm5100_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
unsigned int reg;
int ret;
@@ -2281,7 +2278,7 @@ static int wm5100_gpio_get(struct gpio_chip *chip, unsigned offset)
static int wm5100_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
return regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
WM5100_GP1_FN_MASK | WM5100_GP1_DIR,
@@ -2289,7 +2286,7 @@ static int wm5100_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
(1 << WM5100_GP1_DIR_SHIFT));
}
-static struct gpio_chip wm5100_template_chip = {
+static const struct gpio_chip wm5100_template_chip = {
.label = "wm5100",
.owner = THIS_MODULE,
.direction_output = wm5100_gpio_direction_out,
@@ -2307,13 +2304,9 @@ static void wm5100_init_gpio(struct i2c_client *i2c)
wm5100->gpio_chip = wm5100_template_chip;
wm5100->gpio_chip.ngpio = 6;
wm5100->gpio_chip.parent = &i2c->dev;
+ wm5100->gpio_chip.base = -1;
- 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);
+ ret = gpiochip_add_data(&wm5100->gpio_chip, wm5100);
if (ret != 0)
dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
}
@@ -2334,22 +2327,22 @@ static void wm5100_free_gpio(struct i2c_client *i2c)
}
#endif
-static int wm5100_probe(struct snd_soc_codec *codec)
+static int wm5100_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
int ret, i;
- wm5100->codec = codec;
+ wm5100->component = component;
for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++)
- snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU,
+ snd_soc_component_update_bits(component, wm5100_dig_vu[i], WM5100_OUT_VU,
WM5100_OUT_VU);
/* Don't debounce interrupts to support use of SYSCLK only */
- snd_soc_write(codec, WM5100_IRQ_DEBOUNCE_1, 0);
- snd_soc_write(codec, WM5100_IRQ_DEBOUNCE_2, 0);
+ snd_soc_component_write(component, WM5100_IRQ_DEBOUNCE_1, 0);
+ snd_soc_component_write(component, WM5100_IRQ_DEBOUNCE_2, 0);
/* TODO: check if we're symmetric */
@@ -2357,49 +2350,31 @@ static int wm5100_probe(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq,
ARRAY_SIZE(wm5100_dapm_widgets_noirq));
- if (wm5100->pdata.hp_pol) {
- ret = gpio_request_one(wm5100->pdata.hp_pol,
- GPIOF_OUT_INIT_HIGH, "WM5100 HP_POL");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request HP_POL %d: %d\n",
- wm5100->pdata.hp_pol, ret);
- goto err_gpio;
- }
- }
-
- return 0;
-
-err_gpio:
-
- return ret;
-}
-
-static 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);
+ wm5100->hp_pol = devm_gpiod_get_optional(&i2c->dev, "hp-pol",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wm5100->hp_pol)) {
+ ret = PTR_ERR(wm5100->hp_pol);
+ dev_err(&i2c->dev, "Failed to request HP_POL GPIO: %d\n",
+ ret);
+ return ret;
}
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 snd_soc_component_driver soc_component_dev_wm5100 = {
+ .probe = wm5100_probe,
+ .set_sysclk = wm5100_set_sysclk,
+ .set_pll = wm5100_set_fll,
+ .seq_notifier = wm5100_seq_notifier,
+ .controls = wm5100_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5100_snd_controls),
+ .dapm_widgets = wm5100_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5100_dapm_widgets),
+ .dapm_routes = wm5100_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5100_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm5100_regmap = {
@@ -2411,7 +2386,7 @@ static const struct regmap_config wm5100_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm5100_reg_defaults),
.volatile_reg = wm5100_volatile_register,
.readable_reg = wm5100_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const unsigned int wm5100_mic_ctrl_reg[] = {
@@ -2421,8 +2396,7 @@ static const unsigned int wm5100_mic_ctrl_reg[] = {
WM5100_IN4L_CONTROL,
};
-static int wm5100_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm5100_i2c_probe(struct i2c_client *i2c)
{
struct wm5100_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm5100_priv *wm5100;
@@ -2472,26 +2446,26 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
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;
- }
+ wm5100->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wm5100->ldo_ena)) {
+ ret = PTR_ERR(wm5100->ldo_ena);
+ dev_err(&i2c->dev, "Failed to request LDOENA GPIO: %d\n", ret);
+ goto err_enable;
+ }
+ if (wm5100->ldo_ena) {
+ gpiod_set_consumer_name(wm5100->ldo_ena, "WM5100 LDOENA");
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;
- }
+ wm5100->reset = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm5100->reset)) {
+ ret = PTR_ERR(wm5100->reset);
+ dev_err(&i2c->dev, "Failed to request /RESET GPIO: %d\n", ret);
+ goto err_ldo;
}
+ gpiod_set_consumer_name(wm5100->reset, "WM5100 /RESET");
ret = regmap_read(wm5100->regmap, WM5100_SOFTWARE_RESET, &reg);
if (ret < 0) {
@@ -2616,8 +2590,8 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm5100, wm5100_dai,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm5100, wm5100_dai,
ARRAY_SIZE(wm5100_dai));
if (ret < 0) {
dev_err(&i2c->dev, "Failed to register WM5100: %d\n", ret);
@@ -2627,18 +2601,13 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
return ret;
err_reset:
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
- if (wm5100->pdata.reset) {
- gpio_set_value_cansleep(wm5100->pdata.reset, 0);
- gpio_free(wm5100->pdata.reset);
- }
+ gpiod_set_value_cansleep(wm5100->reset, 1);
err_ldo:
- if (wm5100->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
- gpio_free(wm5100->pdata.ldo_ena);
- }
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
@@ -2646,35 +2615,25 @@ err:
return ret;
}
-static int wm5100_i2c_remove(struct i2c_client *i2c)
+static void wm5100_i2c_remove(struct i2c_client *i2c)
{
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
- snd_soc_unregister_codec(&i2c->dev);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
- if (wm5100->pdata.reset) {
- gpio_set_value_cansleep(wm5100->pdata.reset, 0);
- gpio_free(wm5100->pdata.reset);
- }
- if (wm5100->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
- gpio_free(wm5100->pdata.ldo_ena);
- }
-
- return 0;
+ gpiod_set_value_cansleep(wm5100->reset, 1);
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 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);
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
@@ -2694,8 +2653,8 @@ static int wm5100_runtime_resume(struct device *dev)
return ret;
}
- if (wm5100->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1);
+ if (wm5100->ldo_ena) {
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 1);
msleep(2);
}
@@ -2704,15 +2663,13 @@ static int wm5100_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops wm5100_pm = {
- SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume, NULL)
};
static const struct i2c_device_id wm5100_i2c_id[] = {
- { "wm5100", 0 },
+ { "wm5100" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id);
@@ -2720,7 +2677,7 @@ MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id);
static struct i2c_driver wm5100_i2c_driver = {
.driver = {
.name = "wm5100",
- .pm = &wm5100_pm,
+ .pm = pm_ptr(&wm5100_pm),
},
.probe = wm5100_i2c_probe,
.remove = wm5100_i2c_remove,
diff --git a/sound/soc/codecs/wm5100.h b/sound/soc/codecs/wm5100.h
index 935a9b7fb274..602ee9632351 100644
--- a/sound/soc/codecs/wm5100.h
+++ b/sound/soc/codecs/wm5100.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -17,7 +13,7 @@
#include <sound/soc.h>
#include <linux/regmap.h>
-int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+int wm5100_detect(struct snd_soc_component *component, struct snd_soc_jack *jack);
#define WM5100_CLK_AIF1 1
#define WM5100_CLK_AIF2 2
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 64637d1cf4e5..b4d4137c05b4 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -28,12 +25,14 @@
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/registers.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "arizona.h"
#include "wm5102.h"
#include "wm_adsp.h"
+#define DRV_NAME "wm5102-codec"
+
struct wm5102_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
@@ -45,7 +44,7 @@ static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
-static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+static const struct cs_dsp_region wm5102_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
@@ -581,8 +580,8 @@ static const struct reg_default wm5102_sysclk_revb_patch[] = {
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
struct regmap *regmap = arizona->regmap;
const struct reg_default *patch = NULL;
int i, patch_size;
@@ -607,6 +606,9 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
return 0;
}
@@ -615,18 +617,18 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
}
static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ 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);
- unsigned int v;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ unsigned int v = 0;
int ret;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to read SYSCLK state: %d\n", ret);
return -EIO;
}
@@ -634,19 +636,21 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
if (v >= 3) {
- ret = arizona_dvfs_up(codec, ARIZONA_DVFS_ADSP1_RQ);
+ ret = arizona_dvfs_up(component, ARIZONA_DVFS_ADSP1_RQ);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to raise DVFS: %d\n", ret);
return ret;
}
}
+
+ wm_adsp2_set_dspclk(w, v);
break;
case SND_SOC_DAPM_POST_PMD:
- ret = arizona_dvfs_down(codec, ARIZONA_DVFS_ADSP1_RQ);
+ ret = arizona_dvfs_down(component, ARIZONA_DVFS_ADSP1_RQ);
if (ret)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Failed to lower DVFS: %d\n", ret);
break;
@@ -654,14 +658,14 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
break;
}
- return wm_adsp2_early_event(w, kcontrol, event);
+ return wm_adsp_early_event(w, kcontrol, event);
}
static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
mutex_lock(&arizona->dac_comp_lock);
put_unaligned_be16(arizona->dac_comp_coeff,
@@ -674,23 +678,26 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data);
+ int ret = 0;
mutex_lock(&arizona->dac_comp_lock);
- 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);
+ if (arizona->dac_comp_coeff != dac_comp_coeff) {
+ arizona->dac_comp_coeff = dac_comp_coeff;
+ ret = 1;
+ }
mutex_unlock(&arizona->dac_comp_lock);
- return 0;
+ return ret;
}
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
mutex_lock(&arizona->dac_comp_lock);
ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
@@ -702,17 +709,25 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ int ret = 0;
+
+ if (ucontrol->value.integer.value[0] > mc->max)
+ return -EINVAL;
mutex_lock(&arizona->dac_comp_lock);
- arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
+ if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) {
+ arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
+ ret = 1;
+ }
mutex_unlock(&arizona->dac_comp_lock);
- return 0;
+ return ret;
}
-static const char *wm5102_osr_text[] = {
+static const char * const wm5102_osr_text[] = {
"Low power", "Normal", "High performance",
};
@@ -852,6 +867,8 @@ ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
@@ -978,6 +995,8 @@ ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -1055,7 +1074,7 @@ 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[] = {
+static const char * const wm5102_aec_loopback_texts[] = {
"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
"SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R",
};
@@ -1077,9 +1096,11 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm5102_sysclk_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1203,113 +1224,112 @@ SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
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,
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX8_ENA_SHIFT, 0),
ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
- ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
- &wm5102_aec_loopback_mux),
+ 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,
@@ -1408,7 +1428,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
-WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev),
+WM_ADSP2("DSP1", 0, wm5102_adsp_power_ev),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1521,6 +1541,16 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "IN3L", NULL, "SYSCLK" },
{ "IN3R", NULL, "SYSCLK" },
+ { "ASRC1L", NULL, "SYSCLK" },
+ { "ASRC1R", NULL, "SYSCLK" },
+ { "ASRC2L", NULL, "SYSCLK" },
+ { "ASRC2R", NULL, "SYSCLK" },
+
+ { "ASRC1L", NULL, "ASYNCCLK" },
+ { "ASRC1R", NULL, "ASYNCCLK" },
+ { "ASRC2L", NULL, "ASYNCCLK" },
+ { "ASRC2R", NULL, "ASYNCCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
@@ -1599,6 +1629,8 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "Slim2 Capture", NULL, "SYSCLK" },
{ "Slim3 Capture", NULL, "SYSCLK" },
+ { "Audio Trace DSP", NULL, "DSP1" },
+
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1710,14 +1742,15 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
-static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
+static int wm5102_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int Fref, unsigned int Fout)
{
- struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec);
+ struct wm5102_priv *wm5102 = snd_soc_component_get_drvdata(component);
switch (fll_id) {
case WM5102_FLL1:
@@ -1735,11 +1768,15 @@ static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define WM5102_RATES SNDRV_PCM_RATE_8000_192000
+#define WM5102_RATES SNDRV_PCM_RATE_KNOT
#define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const struct snd_soc_dai_ops wm5102_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver wm5102_dai[] = {
{
.name = "wm5102-aif1",
@@ -1760,8 +1797,8 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.formats = WM5102_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm5102-aif2",
@@ -1782,8 +1819,8 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.formats = WM5102_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm5102-aif3",
@@ -1804,8 +1841,8 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.formats = WM5102_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm5102-slim1",
@@ -1864,25 +1901,75 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
},
.ops = &arizona_simple_dai_ops,
},
+ {
+ .name = "wm5102-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &wm5102_dai_ops,
+ },
+ {
+ .name = "wm5102-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ },
};
-static int wm5102_codec_probe(struct snd_soc_codec *codec)
+static int wm5102_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm5102_priv *priv = snd_soc_component_get_drvdata(component);
+
+ return wm_adsp_compr_open(&priv->core.adsp[0], stream);
+}
+
+static irqreturn_t wm5102_adsp2_irq(int irq, void *data)
+{
+ struct wm5102_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
int ret;
- ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[0]);
+ if (ret == -ENODEV) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wm5102_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm5102_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->core.arizona;
+ int ret;
+
+ snd_soc_component_init_regmap(component, arizona->regmap);
+
+ ret = wm_adsp2_component_probe(&priv->core.adsp[0], component);
if (ret)
return ret;
- ret = snd_soc_add_codec_controls(codec,
- arizona_adsp2_rate_controls, 1);
+ ret = snd_soc_add_component_controls(component,
+ arizona_adsp2_rate_controls, 1);
if (ret)
goto err_adsp2_codec_probe;
- arizona_init_spk(codec);
- arizona_init_gpio(codec);
+ ret = arizona_init_spk(component);
+ if (ret < 0)
+ return ret;
+
+ arizona_init_gpio(component);
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
@@ -1891,20 +1978,18 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
return 0;
err_adsp2_codec_probe:
- wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[0], component);
return ret;
}
-static int wm5102_codec_remove(struct snd_soc_codec *codec)
+static void wm5102_component_remove(struct snd_soc_component *component)
{
- struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm5102_priv *priv = snd_soc_component_get_drvdata(component);
- wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[0], component);
priv->core.arizona->dapm = NULL;
-
- return 0;
}
#define WM5102_DIG_VU 0x0200
@@ -1921,29 +2006,32 @@ static unsigned int wm5102_digital_vu[] = {
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,
+static const struct snd_compress_ops wm5102_compress_ops = {
+ .open = wm5102_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
+};
- .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 const struct snd_soc_component_driver soc_component_dev_wm5102 = {
+ .probe = wm5102_component_probe,
+ .remove = wm5102_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm5102_set_fll,
+ .set_jack = arizona_jack_set_jack,
+ .name = DRV_NAME,
+ .compress_ops = &wm5102_compress_ops,
+ .controls = wm5102_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5102_snd_controls),
+ .dapm_widgets = wm5102_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets),
+ .dapm_routes = wm5102_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm5102_probe(struct platform_device *pdev)
@@ -1958,6 +2046,14 @@ static int wm5102_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm5102);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
mutex_init(&arizona->dac_comp_lock);
wm5102->core.arizona = arizona;
@@ -1966,18 +2062,23 @@ static int wm5102_probe(struct platform_device *pdev)
arizona_init_dvfs(&wm5102->core);
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);
+ wm5102->core.adsp[0].cs_dsp.num = 1;
+ wm5102->core.adsp[0].cs_dsp.type = WMFW_ADSP2;
+ wm5102->core.adsp[0].cs_dsp.base = ARIZONA_DSP1_CONTROL_1;
+ wm5102->core.adsp[0].cs_dsp.dev = arizona->dev;
+ wm5102->core.adsp[0].cs_dsp.regmap = arizona->regmap;
+ wm5102->core.adsp[0].cs_dsp.mem = wm5102_dsp1_regions;
+ wm5102->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
ret = wm_adsp2_init(&wm5102->core.adsp[0]);
if (ret != 0)
return ret;
+ /* This may return -EPROBE_DEFER, so do this early on */
+ ret = arizona_jack_codec_dev_probe(&wm5102->core, &pdev->dev);
+ if (ret)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
wm5102->fll[i].vco_mult = 1;
@@ -2005,16 +2106,67 @@ static int wm5102_probe(struct platform_device *pdev)
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));
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
+ wm5102);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto err_jack_codec_dev;
+ }
+
+ ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to set compressed IRQ as a wake source: %d\n",
+ ret);
+
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm5102,
+ wm5102_dai,
+ ARRAY_SIZE(wm5102_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+err_jack_codec_dev:
+ pm_runtime_disable(&pdev->dev);
+ arizona_jack_codec_dev_remove(&wm5102->core);
+
+ return ret;
}
-static int wm5102_remove(struct platform_device *pdev)
+static void wm5102_remove(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
+ struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm5102->core.arizona;
+
pm_runtime_disable(&pdev->dev);
- return 0;
+ wm_adsp2_remove(&wm5102->core.adsp[0]);
+
+ arizona_free_spk_irqs(arizona);
+
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+
+ arizona_jack_codec_dev_remove(&wm5102->core);
}
static struct platform_driver wm5102_codec_driver = {
diff --git a/sound/soc/codecs/wm5102.h b/sound/soc/codecs/wm5102.h
index adb38040f661..34156cef64a7 100644
--- a/sound/soc/codecs/wm5102.h
+++ b/sound/soc/codecs/wm5102.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 6088d30962a9..7a311c869bd3 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -35,6 +32,8 @@
#define WM5110_NUM_ADSP 4
+#define DRV_NAME "wm5110-codec"
+
struct wm5110_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
@@ -46,35 +45,35 @@ struct wm5110_priv {
unsigned int in_pga_cache[6];
};
-static const struct wm_adsp_region wm5110_dsp1_regions[] = {
+static const struct cs_dsp_region wm5110_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
{ .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
};
-static const struct wm_adsp_region wm5110_dsp2_regions[] = {
+static const struct cs_dsp_region wm5110_dsp2_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x200000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x280000 },
{ .type = WMFW_ADSP2_XM, .base = 0x290000 },
{ .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
};
-static const struct wm_adsp_region wm5110_dsp3_regions[] = {
+static const struct cs_dsp_region wm5110_dsp3_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x300000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x380000 },
{ .type = WMFW_ADSP2_XM, .base = 0x390000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
};
-static const struct wm_adsp_region wm5110_dsp4_regions[] = {
+static const struct cs_dsp_region wm5110_dsp4_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x400000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x480000 },
{ .type = WMFW_ADSP2_XM, .base = 0x490000 },
{ .type = WMFW_ADSP2_YM, .base = 0x4a8000 },
};
-static const struct wm_adsp_region *wm5110_dsp_regions[] = {
+static const struct cs_dsp_region *wm5110_dsp_regions[] = {
wm5110_dsp1_regions,
wm5110_dsp2_regions,
wm5110_dsp3_regions,
@@ -159,8 +158,8 @@ static const struct reg_default wm5110_sysclk_reve_patch[] = {
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
struct regmap *regmap = arizona->regmap;
const struct reg_default *patch = NULL;
int i, patch_size;
@@ -183,7 +182,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
regmap_write_async(regmap, patch[i].reg,
patch[i].def);
break;
-
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
break;
}
@@ -191,6 +192,27 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
return 0;
}
+static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+ if (ret != 0) {
+ dev_err(component->dev, "Failed to read SYSCLK state: %d\n", ret);
+ return ret;
+ }
+
+ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ wm_adsp2_set_dspclk(w, v);
+
+ return wm_adsp_early_event(w, kcontrol, event);
+}
+
static const struct reg_sequence wm5110_no_dre_left_enable[] = {
{ 0x3024, 0xE410 },
{ 0x3025, 0x0056 },
@@ -265,10 +287,10 @@ static const struct reg_sequence wm5110_dre_right_enable[] = {
static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
- unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE);
+ unsigned int val = snd_soc_component_read(component, ARIZONA_DRE_ENABLE);
const struct reg_sequence *wseq;
int nregs;
@@ -280,7 +302,7 @@ static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w)
} else {
wseq = wm5110_no_dre_left_enable;
nregs = ARRAY_SIZE(wm5110_no_dre_left_enable);
- priv->out_up_delay += 10;
+ priv->out_up_delay += 10000;
}
break;
case ARIZONA_OUT1R_ENA_SHIFT:
@@ -290,7 +312,7 @@ static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w)
} else {
wseq = wm5110_no_dre_right_enable;
nregs = ARRAY_SIZE(wm5110_no_dre_right_enable);
- priv->out_up_delay += 10;
+ priv->out_up_delay += 10000;
}
break;
default:
@@ -302,27 +324,33 @@ static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w)
static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int val = snd_soc_component_read(component, ARIZONA_DRE_ENABLE);
switch (w->shift) {
case ARIZONA_OUT1L_ENA_SHIFT:
if (!(val & ARIZONA_DRE1L_ENA_MASK)) {
- snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
- ARIZONA_WS_TRG1, ARIZONA_WS_TRG1);
- snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
- ARIZONA_WS_TRG1, 0);
- priv->out_down_delay += 27;
+ snd_soc_component_update_bits(component,
+ ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG1,
+ ARIZONA_WS_TRG1);
+ snd_soc_component_update_bits(component,
+ ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG1, 0);
+ priv->out_down_delay += 27000;
}
break;
case ARIZONA_OUT1R_ENA_SHIFT:
if (!(val & ARIZONA_DRE1R_ENA_MASK)) {
- snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
- ARIZONA_WS_TRG2, ARIZONA_WS_TRG2);
- snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
- ARIZONA_WS_TRG2, 0);
- priv->out_down_delay += 27;
+ snd_soc_component_update_bits(component,
+ ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG2,
+ ARIZONA_WS_TRG2);
+ snd_soc_component_update_bits(component,
+ ARIZONA_SPARE_TRIGGERS,
+ ARIZONA_WS_TRG2, 0);
+ priv->out_down_delay += 27000;
}
break;
default:
@@ -335,8 +363,8 @@ static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w)
static int wm5110_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
switch (priv->arizona->rev) {
case 0 ... 3:
@@ -374,9 +402,9 @@ static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
static int wm5110_put_dre(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int ena, dre;
@@ -385,6 +413,7 @@ static int wm5110_put_dre(struct snd_kcontrol *kcontrol,
unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift;
unsigned int lold, rold;
unsigned int lena, rena;
+ bool change = false;
int ret;
snd_soc_dapm_mutex_lock(dapm);
@@ -412,8 +441,8 @@ static int wm5110_put_dre(struct snd_kcontrol *kcontrol,
goto err;
}
- ret = regmap_update_bits(arizona->regmap, ARIZONA_DRE_ENABLE,
- mask, lnew | rnew);
+ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_DRE_ENABLE,
+ mask, lnew | rnew, &change);
if (ret) {
dev_err(arizona->dev, "Failed to set DRE: %d\n", ret);
goto err;
@@ -426,6 +455,9 @@ static int wm5110_put_dre(struct snd_kcontrol *kcontrol,
if (!rnew && rold)
wm5110_clear_pga_volume(arizona, mc->rshift);
+ if (change)
+ ret = 1;
+
err:
snd_soc_dapm_mutex_unlock(dapm);
@@ -435,8 +467,8 @@ err:
static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
/*
@@ -445,7 +477,7 @@ static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
*/
snd_soc_dapm_mutex_lock(dapm);
- ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
snd_soc_dapm_mutex_unlock(dapm);
@@ -455,8 +487,8 @@ static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
/*
@@ -465,7 +497,7 @@ static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
*/
snd_soc_dapm_mutex_lock(dapm);
- ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
snd_soc_dapm_mutex_unlock(dapm);
@@ -475,9 +507,9 @@ static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
static int wm5110_in_analog_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 wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+ struct wm5110_priv *wm5110 = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
unsigned int reg, mask;
struct reg_sequence analog_seq[] = {
@@ -496,9 +528,9 @@ static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
wm5110->in_post_pending++;
return 0;
case SND_SOC_DAPM_PRE_PMU:
- wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+ wm5110->in_pga_cache[w->shift] = snd_soc_component_read(component, reg);
- snd_soc_update_bits(codec, reg, mask,
+ snd_soc_component_update_bits(component, reg, mask,
0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
wm5110->in_pre_pending--;
@@ -515,8 +547,8 @@ static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, reg, mask,
- wm5110->in_pga_cache[w->shift]);
+ snd_soc_component_update_bits(component, reg, mask,
+ wm5110->in_pga_cache[w->shift]);
wm5110->in_post_pending--;
if (wm5110->in_post_pending == 0)
@@ -534,13 +566,13 @@ static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
switch (arizona->rev) {
case 0 ... 4:
- if (arizona_input_analog(codec, w->shift))
+ if (arizona_input_analog(component, w->shift))
wm5110_in_analog_ev(w, kcontrol, event);
break;
@@ -757,6 +789,11 @@ SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+WM_ADSP2_PRELOAD_SWITCH("DSP4", 4),
+
ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
@@ -835,14 +872,14 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
ARIZONA_SPK2R_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
- ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0,
- snd_soc_get_volsw, wm5110_put_dre),
+ ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0,
+ snd_soc_get_volsw, wm5110_put_dre),
SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
- ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0,
- snd_soc_get_volsw, wm5110_put_dre),
+ ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0,
+ snd_soc_get_volsw, wm5110_put_dre),
SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE,
- ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0,
- snd_soc_get_volsw, wm5110_put_dre),
+ ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0,
+ snd_soc_get_volsw, wm5110_put_dre),
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -893,6 +930,11 @@ ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+WM_ADSP_FW_CONTROL("DSP2", 1),
+WM_ADSP_FW_CONTROL("DSP3", 2),
+WM_ADSP_FW_CONTROL("DSP4", 3),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -1008,7 +1050,7 @@ 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[] = {
+static const char * const wm5110_aec_loopback_texts[] = {
"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
"SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R",
};
@@ -1054,9 +1096,11 @@ static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
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),
+ 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1085,6 +1129,11 @@ SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -1179,10 +1228,10 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 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),
+WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev),
+WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev),
+WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev),
+WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev),
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@ -1239,18 +1288,17 @@ 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),
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &wm5110_aec_loopback_mux),
SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
- ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
- ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1303,122 +1351,122 @@ SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 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,
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
@@ -1704,6 +1752,7 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "OUT2L", NULL, "SYSCLK" },
{ "OUT2R", NULL, "SYSCLK" },
{ "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
{ "OUT4L", NULL, "SYSCLK" },
{ "OUT4R", NULL, "SYSCLK" },
{ "OUT5L", NULL, "SYSCLK" },
@@ -1720,6 +1769,16 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "IN4L", NULL, "SYSCLK" },
{ "IN4R", NULL, "SYSCLK" },
+ { "ASRC1L", NULL, "SYSCLK" },
+ { "ASRC1R", NULL, "SYSCLK" },
+ { "ASRC2L", NULL, "SYSCLK" },
+ { "ASRC2R", NULL, "SYSCLK" },
+
+ { "ASRC1L", NULL, "ASYNCCLK" },
+ { "ASRC1R", NULL, "ASYNCCLK" },
+ { "ASRC2L", NULL, "ASYNCCLK" },
+ { "ASRC2R", NULL, "ASYNCCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
@@ -1807,7 +1866,8 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "Slim3 Capture", NULL, "SYSCLK" },
{ "Voice Control DSP", NULL, "DSP3" },
- { "Voice Control DSP", NULL, "SYSCLK" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1975,16 +2035,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
{ "DRC2 Signal Activity", NULL, "DRC2L" },
{ "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
};
-static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
+static int wm5110_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int Fref, unsigned int Fout)
{
- struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+ struct wm5110_priv *wm5110 = snd_soc_component_get_drvdata(component);
switch (fll_id) {
case WM5110_FLL1:
@@ -2002,11 +2068,15 @@ static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+#define WM5110_RATES SNDRV_PCM_RATE_KNOT
#define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const struct snd_soc_dai_ops wm5110_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver wm5110_dai[] = {
{
.name = "wm5110-aif1",
@@ -2027,8 +2097,8 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.formats = WM5110_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm5110-aif2",
@@ -2049,8 +2119,8 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.formats = WM5110_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm5110-aif3",
@@ -2071,8 +2141,8 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.formats = WM5110_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm5110-slim1",
@@ -2140,7 +2210,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &wm5110_dai_ops,
},
{
.name = "wm5110-dsp-voicectrl",
@@ -2152,21 +2222,45 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.formats = WM5110_FORMATS,
},
},
+ {
+ .name = "wm5110-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &wm5110_dai_ops,
+ },
+ {
+ .name = "wm5110-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ },
};
-static int wm5110_open(struct snd_compr_stream *stream)
+static int wm5110_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct wm5110_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
- if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "wm5110-dsp-voicectrl") == 0) {
n_adsp = 2;
+ } else if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "wm5110-dsp-trace") == 0) {
+ n_adsp = 0;
} else {
dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n",
- rtd->codec_dai->name);
+ snd_soc_rtd_to_codec(rtd, 0)->name);
return -EINVAL;
}
@@ -2175,46 +2269,58 @@ static int wm5110_open(struct snd_compr_stream *stream)
static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
{
- struct wm5110_priv *florida = data;
- int ret;
+ struct wm5110_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
+ }
- ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
- if (ret == -ENODEV)
+ if (!serviced) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
return IRQ_NONE;
+ }
return IRQ_HANDLED;
}
-static int wm5110_codec_probe(struct snd_soc_codec *codec)
+static int wm5110_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm5110_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->core.arizona;
int i, ret;
- priv->core.arizona->dapm = dapm;
+ arizona->dapm = dapm;
+ snd_soc_component_init_regmap(component, arizona->regmap);
- arizona_init_spk(codec);
- arizona_init_gpio(codec);
- arizona_init_mono(codec);
-
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+ ret = arizona_init_spk(component);
+ if (ret < 0)
return ret;
- }
+
+ arizona_init_gpio(component);
+ arizona_init_mono(component);
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
- ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
+ ret = wm_adsp2_component_probe(&priv->core.adsp[i], component);
if (ret)
goto err_adsp2_codec_probe;
}
- ret = snd_soc_add_codec_controls(codec,
- arizona_adsp2_rate_controls,
- WM5110_NUM_ADSP);
+ ret = snd_soc_add_component_controls(component,
+ arizona_adsp2_rate_controls,
+ WM5110_NUM_ADSP);
if (ret)
goto err_adsp2_codec_probe;
@@ -2224,27 +2330,20 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
err_adsp2_codec_probe:
for (--i; i >= 0; --i)
- wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
-
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+ wm_adsp2_component_remove(&priv->core.adsp[i], component);
return ret;
}
-static int wm5110_codec_remove(struct snd_soc_codec *codec)
+static void wm5110_component_remove(struct snd_soc_component *component)
{
- struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
+ struct wm5110_priv *priv = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < WM5110_NUM_ADSP; ++i)
- wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
+ wm_adsp2_component_remove(&priv->core.adsp[i], component);
priv->core.arizona->dapm = NULL;
-
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- return 0;
}
#define WM5110_DIG_VU 0x0200
@@ -2264,43 +2363,32 @@ static unsigned int wm5110_digital_vu[] = {
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 struct snd_compr_ops wm5110_compr_ops = {
- .open = wm5110_open,
- .free = wm_adsp_compr_free,
- .set_params = wm_adsp_compr_set_params,
- .get_caps = wm_adsp_compr_get_caps,
- .trigger = wm_adsp_compr_trigger,
- .pointer = wm_adsp_compr_pointer,
- .copy = wm_adsp_compr_copy,
+static const struct snd_compress_ops wm5110_compress_ops = {
+ .open = wm5110_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
};
-static struct snd_soc_platform_driver wm5110_compr_platform = {
- .compr_ops = &wm5110_compr_ops,
+static const struct snd_soc_component_driver soc_component_dev_wm5110 = {
+ .probe = wm5110_component_probe,
+ .remove = wm5110_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm5110_set_fll,
+ .set_jack = arizona_jack_set_jack,
+ .name = DRV_NAME,
+ .compress_ops = &wm5110_compress_ops,
+ .controls = wm5110_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5110_snd_controls),
+ .dapm_widgets = wm5110_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets),
+ .dapm_routes = wm5110_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm5110_probe(struct platform_device *pdev)
@@ -2315,20 +2403,28 @@ static int wm5110_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm5110);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
wm5110->core.arizona = arizona;
wm5110->core.num_inputs = 8;
for (i = 0; i < WM5110_NUM_ADSP; i++) {
wm5110->core.adsp[i].part = "wm5110";
- wm5110->core.adsp[i].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].cs_dsp.num = i + 1;
+ wm5110->core.adsp[i].cs_dsp.type = WMFW_ADSP2;
+ wm5110->core.adsp[i].cs_dsp.dev = arizona->dev;
+ wm5110->core.adsp[i].cs_dsp.regmap = arizona->regmap;
- wm5110->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1
+ wm5110->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1
+ (0x100 * i);
- wm5110->core.adsp[i].mem = wm5110_dsp_regions[i];
- wm5110->core.adsp[i].num_mems
+ wm5110->core.adsp[i].cs_dsp.mem = wm5110_dsp_regions[i];
+ wm5110->core.adsp[i].cs_dsp.num_mems
= ARRAY_SIZE(wm5110_dsp1_regions);
ret = wm_adsp2_init(&wm5110->core.adsp[i]);
@@ -2336,6 +2432,11 @@ static int wm5110_probe(struct platform_device *pdev)
return ret;
}
+ /* This may return -EPROBE_DEFER, so do this early on */
+ ret = arizona_jack_codec_dev_probe(&wm5110->core, &pdev->dev);
+ if (ret)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++)
wm5110->fll[i].vco_mult = 3;
@@ -2363,29 +2464,69 @@ static int wm5110_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- goto error;
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
+ wm5110);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ goto err_jack_codec_dev;
}
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
- wm5110_dai, ARRAY_SIZE(wm5110_dai));
+ ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to set compressed IRQ as a wake source: %d\n",
+ ret);
+
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm5110,
+ wm5110_dai,
+ ARRAY_SIZE(wm5110_dai));
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_spk_irqs;
}
-error:
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+err_jack_codec_dev:
+ pm_runtime_disable(&pdev->dev);
+ arizona_jack_codec_dev_remove(&wm5110->core);
+
return ret;
}
-static int wm5110_remove(struct platform_device *pdev)
+static void wm5110_remove(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
+ struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm5110->core.arizona;
+ int i;
+
pm_runtime_disable(&pdev->dev);
- return 0;
+ for (i = 0; i < WM5110_NUM_ADSP; i++)
+ wm_adsp2_remove(&wm5110->core.adsp[i]);
+
+ arizona_free_spk_irqs(arizona);
+
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+
+ arizona_jack_codec_dev_remove(&wm5110->core);
}
static struct platform_driver wm5110_codec_driver = {
diff --git a/sound/soc/codecs/wm5110.h b/sound/soc/codecs/wm5110.h
index e6c0cd4235c5..2545e861313d 100644
--- a/sound/soc/codecs/wm5110.h
+++ b/sound/soc/codecs/wm5110.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index ffbf3df8ae97..ad68ff1790e0 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -221,7 +218,8 @@ static void wm8350_pga_work(struct work_struct *work)
/* PGA volumes have 6 bits of resolution to ramp */
for (i = 0; i <= 63; i++) {
- out1_complete = 1, out2_complete = 1;
+ 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)
@@ -256,8 +254,8 @@ static void wm8350_pga_work(struct work_struct *work)
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component);
struct wm8350_output *out;
switch (w->shift) {
@@ -299,8 +297,8 @@ static int pga_event(struct snd_soc_dapm_widget *w,
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8350_data *wm8350_priv = snd_soc_component_get_drvdata(component);
struct wm8350_output *out = NULL;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
@@ -334,16 +332,16 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
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);
+ val = snd_soc_component_read(component, reg);
+ snd_soc_component_write(component, reg, val | WM8350_OUT1_VU);
return 1;
}
static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8350_data *wm8350_priv = snd_soc_component_get_drvdata(component);
struct wm8350_output *out1 = &wm8350_priv->out1;
struct wm8350_output *out2 = &wm8350_priv->out2;
struct soc_mixer_control *mc =
@@ -753,8 +751,8 @@ static const struct snd_soc_dapm_route wm8350_dapm_routes[] = {
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 snd_soc_component *component = codec_dai->component;
+ struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component);
struct wm8350 *wm8350 = wm8350_data->wm8350;
u16 fll_4;
@@ -769,9 +767,9 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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) &
+ fll_4 = snd_soc_component_read(component, WM8350_FLL_CONTROL_4) &
~WM8350_FLL_CLK_SRC_MASK;
- snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+ snd_soc_component_write(component, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
break;
}
@@ -788,44 +786,44 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 val;
switch (div_id) {
case WM8350_ADC_CLKDIV:
- val = snd_soc_read(codec, WM8350_ADC_DIVIDER) &
+ val = snd_soc_component_read(component, WM8350_ADC_DIVIDER) &
~WM8350_ADC_CLKDIV_MASK;
- snd_soc_write(codec, WM8350_ADC_DIVIDER, val | div);
+ snd_soc_component_write(component, WM8350_ADC_DIVIDER, val | div);
break;
case WM8350_DAC_CLKDIV:
- val = snd_soc_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+ val = snd_soc_component_read(component, WM8350_DAC_CLOCK_CONTROL) &
~WM8350_DAC_CLKDIV_MASK;
- snd_soc_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+ snd_soc_component_write(component, WM8350_DAC_CLOCK_CONTROL, val | div);
break;
case WM8350_BCLK_CLKDIV:
- val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
+ val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) &
~WM8350_BCLK_DIV_MASK;
- snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div);
break;
case WM8350_OPCLK_CLKDIV:
- val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
+ val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) &
~WM8350_OPCLK_DIV_MASK;
- snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div);
break;
case WM8350_SYS_CLKDIV:
- val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
+ val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) &
~WM8350_MCLK_DIV_MASK;
- snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div);
break;
case WM8350_DACLR_CLKDIV:
- val = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
+ val = snd_soc_component_read(component, WM8350_DAC_LR_RATE) &
~WM8350_DACLRC_RATE_MASK;
- snd_soc_write(codec, WM8350_DAC_LR_RATE, val | div);
+ snd_soc_component_write(component, WM8350_DAC_LR_RATE, val | div);
break;
case WM8350_ADCLR_CLKDIV:
- val = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
+ val = snd_soc_component_read(component, WM8350_ADC_LR_RATE) &
~WM8350_ADCLRC_RATE_MASK;
- snd_soc_write(codec, WM8350_ADC_LR_RATE, val | div);
+ snd_soc_component_write(component, WM8350_ADC_LR_RATE, val | div);
break;
default:
return -EINVAL;
@@ -836,24 +834,24 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
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) &
+ struct snd_soc_component *component = codec_dai->component;
+ u16 iface = snd_soc_component_read(component, WM8350_AI_FORMATING) &
~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
- u16 master = snd_soc_read(codec, WM8350_AI_DAC_CONTROL) &
+ u16 master = snd_soc_component_read(component, WM8350_AI_DAC_CONTROL) &
~WM8350_BCLK_MSTR;
- u16 dac_lrc = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
+ u16 dac_lrc = snd_soc_component_read(component, WM8350_DAC_LR_RATE) &
~WM8350_DACLRC_ENA;
- u16 adc_lrc = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
+ u16 adc_lrc = snd_soc_component_read(component, WM8350_ADC_LR_RATE) &
~WM8350_ADCLRC_ENA;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master |= WM8350_BCLK_MSTR;
dac_lrc |= WM8350_DACLRC_ENA;
adc_lrc |= WM8350_ADCLRC_ENA;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -896,10 +894,10 @@ static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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);
+ snd_soc_component_write(component, WM8350_AI_FORMATING, iface);
+ snd_soc_component_write(component, WM8350_AI_DAC_CONTROL, master);
+ snd_soc_component_write(component, WM8350_DAC_LR_RATE, dac_lrc);
+ snd_soc_component_write(component, WM8350_ADC_LR_RATE, adc_lrc);
return 0;
}
@@ -907,10 +905,10 @@ 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 snd_soc_component *component = codec_dai->component;
+ struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component);
struct wm8350 *wm8350 = wm8350_data->wm8350;
- u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
+ u16 iface = snd_soc_component_read(component, WM8350_AI_FORMATING) &
~WM8350_AIF_WL_MASK;
/* bit size */
@@ -928,7 +926,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8350_AI_FORMATING, iface);
+ snd_soc_component_write(component, WM8350_AI_FORMATING, iface);
/* The sloping stopband filter is recommended for use with
* lower sample rates to improve performance.
@@ -945,9 +943,9 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8350_mute(struct snd_soc_dai *dai, int mute)
+static int wm8350_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val;
if (mute)
@@ -955,7 +953,7 @@ static int wm8350_mute(struct snd_soc_dai *dai, int mute)
else
val = 0;
- snd_soc_update_bits(codec, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val);
+ snd_soc_component_update_bits(component, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val);
return 0;
}
@@ -1024,8 +1022,8 @@ 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 snd_soc_component *component = codec_dai->component;
+ struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
struct wm8350 *wm8350 = priv->wm8350;
struct _fll_div fll_div;
int ret = 0;
@@ -1050,17 +1048,17 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
fll_div.ratio);
/* set up N.K & dividers */
- fll_1 = snd_soc_read(codec, WM8350_FLL_CONTROL_1) &
+ fll_1 = snd_soc_component_read(component, WM8350_FLL_CONTROL_1) &
~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
- snd_soc_write(codec, WM8350_FLL_CONTROL_1,
+ snd_soc_component_write(component, WM8350_FLL_CONTROL_1,
fll_1 | (fll_div.div << 8) | 0x50);
- snd_soc_write(codec, WM8350_FLL_CONTROL_2,
+ snd_soc_component_write(component, 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) &
+ snd_soc_component_write(component, WM8350_FLL_CONTROL_3, fll_div.k);
+ fll_4 = snd_soc_component_read(component, WM8350_FLL_CONTROL_4) &
~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
- snd_soc_write(codec, WM8350_FLL_CONTROL_4,
+ snd_soc_component_write(component, WM8350_FLL_CONTROL_4,
fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
(fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
@@ -1074,10 +1072,11 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
return 0;
}
-static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+static int wm8350_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
struct wm8350 *wm8350 = priv->wm8350;
struct wm8350_audio_platform_data *platform =
wm8350->codec.platform_data;
@@ -1101,7 +1100,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
priv->supplies);
if (ret != 0)
@@ -1310,7 +1309,7 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
/**
* wm8350_hp_jack_detect - Enable headphone jack detection.
*
- * @codec: WM8350 codec
+ * @component: WM8350 component
* @which: left or right jack detect signal
* @jack: jack to report detection events on
* @report: value to report
@@ -1318,10 +1317,10 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
* 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,
+int wm8350_hp_jack_detect(struct snd_soc_component *component, enum wm8350_jack which,
struct snd_soc_jack *jack, int report)
{
- struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
struct wm8350 *wm8350 = priv->wm8350;
int ena;
@@ -1389,7 +1388,7 @@ static irqreturn_t wm8350_mic_handler(int irq, void *data)
/**
* wm8350_mic_jack_detect - Enable microphone jack detection.
*
- * @codec: WM8350 codec
+ * @component: WM8350 component
* @jack: jack to report detection events on
* @detect_report: value to report when presence detected
* @short_report: value to report when microphone short detected
@@ -1397,11 +1396,11 @@ static irqreturn_t wm8350_mic_handler(int irq, void *data)
* 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,
+int wm8350_mic_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack,
int detect_report, int short_report)
{
- struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
struct wm8350 *wm8350 = priv->wm8350;
priv->mic.jack = jack;
@@ -1429,11 +1428,12 @@ EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect);
static const struct snd_soc_dai_ops wm8350_dai_ops = {
.hw_params = wm8350_pcm_hw_params,
- .digital_mute = wm8350_mute,
+ .mute_stream = wm8350_mute,
.set_fmt = wm8350_set_dai_fmt,
.set_sysclk = wm8350_set_dai_sysclk,
.set_pll = wm8350_set_fll,
.set_clkdiv = wm8350_set_clkdiv,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8350_dai = {
@@ -1455,24 +1455,26 @@ static struct snd_soc_dai_driver wm8350_dai = {
.ops = &wm8350_dai_ops,
};
-static int wm8350_codec_probe(struct snd_soc_codec *codec)
+static int wm8350_component_probe(struct snd_soc_component *component)
{
- struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
+ struct wm8350 *wm8350 = dev_get_platdata(component->dev);
struct wm8350_data *priv;
struct wm8350_output *out1;
struct wm8350_output *out2;
int ret, i;
if (wm8350->codec.platform_data == NULL) {
- dev_err(codec->dev, "No audio platform data supplied\n");
+ dev_err(component->dev, "No audio platform data supplied\n");
return -EINVAL;
}
- priv = devm_kzalloc(codec->dev, sizeof(struct wm8350_data),
+ priv = devm_kzalloc(component->dev, sizeof(struct wm8350_data),
GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, priv);
+
+ snd_soc_component_init_regmap(component, wm8350->regmap);
+ snd_soc_component_set_drvdata(component, priv);
priv->wm8350 = wm8350;
@@ -1495,9 +1497,9 @@ static int wm8350_codec_probe(struct snd_soc_codec *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);
+ snd_soc_component_write(component, WM8350_SECURITY, 0xa7);
+ snd_soc_component_write(component, 0xde, 0x13);
+ snd_soc_component_write(component, WM8350_SECURITY, 0);
/* read OUT1 & OUT2 volumes */
out1 = &priv->out1;
@@ -1536,24 +1538,44 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec)
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA);
- wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
+ ret = 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,
+ if (ret != 0)
+ goto err;
+
+ ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
wm8350_hpr_jack_handler, 0, "Right jack detect",
priv);
- wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
+ if (ret != 0)
+ goto free_jck_det_l;
+
+ ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
wm8350_mic_handler, 0, "Microphone short", priv);
- wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
+ if (ret != 0)
+ goto free_jck_det_r;
+
+ ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
wm8350_mic_handler, 0, "Microphone detect", priv);
+ if (ret != 0)
+ goto free_micscd;
return 0;
+
+free_micscd:
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv);
+free_jck_det_r:
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
+free_jck_det_l:
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
+err:
+ return ret;
}
-static int wm8350_codec_remove(struct snd_soc_codec *codec)
+static void wm8350_component_remove(struct snd_soc_component *component)
{
- struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
- struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
+ struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
+ struct wm8350 *wm8350 = dev_get_platdata(component->dev);
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA);
@@ -1576,50 +1598,36 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec)
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 const struct snd_soc_component_driver soc_component_dev_wm8350 = {
+ .probe = wm8350_component_probe,
+ .remove = wm8350_component_remove,
+ .set_bias_level = wm8350_set_bias_level,
+ .controls = wm8350_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8350_snd_controls),
+ .dapm_widgets = wm8350_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets),
+ .dapm_routes = wm8350_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm8350_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8350,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_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);
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
index 74108eb82938..a13d18c92d4b 100644
--- a/sound/soc/codecs/wm8350.h
+++ b/sound/soc/codecs/wm8350.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
@@ -20,9 +16,9 @@ enum wm8350_jack {
WM8350_JDR = 2,
};
-int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+int wm8350_hp_jack_detect(struct snd_soc_component *component, enum wm8350_jack which,
struct snd_soc_jack *jack, int report);
-int wm8350_mic_jack_detect(struct snd_soc_codec *codec,
+int wm8350_mic_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack,
int detect_report, int short_report);
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b1d346aa4696..0eecc8657a38 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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>
@@ -65,23 +60,19 @@ struct wm8400_priv {
int fll_in, fll_out;
};
-static void wm8400_codec_reset(struct snd_soc_codec *codec)
+static void wm8400_component_reset(struct snd_soc_component *component)
{
- struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
+ struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component);
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);
@@ -91,7 +82,7 @@ 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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int reg = mc->reg;
@@ -103,8 +94,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
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);
+ val = snd_soc_component_read(component, reg);
+ return snd_soc_component_write(component, reg, val | 0x0100);
}
#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
@@ -324,42 +315,43 @@ SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
+ struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component);
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);
+ reg = snd_soc_component_read(component, WM8400_OUTPUT_MIXER1);
if (reg & WM8400_LDLO) {
- printk(KERN_WARNING
+ dev_warn(wm8400->wm8400->dev,
"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);
+ reg = snd_soc_component_read(component, WM8400_OUTPUT_MIXER2);
if (reg & WM8400_RDRO) {
- printk(KERN_WARNING
+ dev_warn(wm8400->wm8400->dev,
"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);
+ reg = snd_soc_component_read(component, WM8400_SPEAKER_MIXER);
if (reg & WM8400_LDSPK) {
- printk(KERN_WARNING
+ dev_warn(wm8400->wm8400->dev,
"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);
+ reg = snd_soc_component_read(component, WM8400_SPEAKER_MIXER);
if (reg & WM8400_RDSPK) {
- printk(KERN_WARNING
+ dev_warn(wm8400->wm8400->dev,
"Cannot set as Speaker Mixer RDSPK Set\n");
ret = -1;
}
@@ -444,14 +436,6 @@ static SOC_ENUM_SINGLE_DECL(wm8400_ainrmux_enum,
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,
@@ -849,8 +833,8 @@ static const struct snd_soc_dapm_route wm8400_dapm_routes[] = {
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component);
wm8400->sysclk = freq;
return 0;
@@ -938,8 +922,8 @@ 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 snd_soc_component *component = codec_dai->component;
+ struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component);
struct fll_factors factors;
int ret;
u16 reg;
@@ -962,13 +946,13 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
wm8400->fll_in = freq_in;
/* We *must* disable the FLL before any changes */
- reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_2);
+ reg = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_2);
reg &= ~WM8400_FLL_ENA;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_2, reg);
- reg = snd_soc_read(codec, WM8400_FLL_CONTROL_1);
+ reg = snd_soc_component_read(component, WM8400_FLL_CONTROL_1);
reg &= ~WM8400_FLL_OSC_ENA;
- snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
+ snd_soc_component_write(component, WM8400_FLL_CONTROL_1, reg);
if (!freq_out)
return 0;
@@ -976,15 +960,15 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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_component_write(component, 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);
+ snd_soc_component_write(component, WM8400_FLL_CONTROL_2, factors.k);
+ snd_soc_component_write(component, WM8400_FLL_CONTROL_3, factors.n);
- reg = snd_soc_read(codec, WM8400_FLL_CONTROL_4);
+ reg = snd_soc_component_read(component, WM8400_FLL_CONTROL_4);
reg &= ~WM8400_FLL_OUTDIV_MASK;
reg |= factors.outdiv;
- snd_soc_write(codec, WM8400_FLL_CONTROL_4, reg);
+ snd_soc_component_write(component, WM8400_FLL_CONTROL_4, reg);
return 0;
}
@@ -995,18 +979,18 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 audio1, audio3;
- audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
- audio3 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_3);
+ audio1 = snd_soc_component_read(component, WM8400_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_component_read(component, WM8400_AUDIO_INTERFACE_3);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
audio3 &= ~WM8400_AIF_MSTR1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
audio3 |= WM8400_AIF_MSTR1;
break;
default:
@@ -1040,37 +1024,37 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
- snd_soc_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
+ snd_soc_component_write(component, WM8400_AUDIO_INTERFACE_1, audio1);
+ snd_soc_component_write(component, WM8400_AUDIO_INTERFACE_3, audio3);
return 0;
}
static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
switch (div_id) {
case WM8400_MCLK_DIV:
- reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
+ reg = snd_soc_component_read(component, WM8400_CLOCKING_2) &
~WM8400_MCLK_DIV_MASK;
- snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
+ snd_soc_component_write(component, WM8400_CLOCKING_2, reg | div);
break;
case WM8400_DACCLK_DIV:
- reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
+ reg = snd_soc_component_read(component, WM8400_CLOCKING_2) &
~WM8400_DAC_CLKDIV_MASK;
- snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
+ snd_soc_component_write(component, WM8400_CLOCKING_2, reg | div);
break;
case WM8400_ADCCLK_DIV:
- reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
+ reg = snd_soc_component_read(component, WM8400_CLOCKING_2) &
~WM8400_ADC_CLKDIV_MASK;
- snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
+ snd_soc_component_write(component, WM8400_CLOCKING_2, reg | div);
break;
case WM8400_BCLK_DIV:
- reg = snd_soc_read(codec, WM8400_CLOCKING_1) &
+ reg = snd_soc_component_read(component, WM8400_CLOCKING_1) &
~WM8400_BCLK_DIV_MASK;
- snd_soc_write(codec, WM8400_CLOCKING_1, reg | div);
+ snd_soc_component_write(component, WM8400_CLOCKING_1, reg | div);
break;
default:
return -EINVAL;
@@ -1086,8 +1070,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ u16 audio1 = snd_soc_component_read(component, WM8400_AUDIO_INTERFACE_1);
audio1 &= ~WM8400_AIF_WL_MASK;
/* bit size */
@@ -1105,28 +1089,29 @@ static int wm8400_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ snd_soc_component_write(component, WM8400_AUDIO_INTERFACE_1, audio1);
return 0;
}
-static int wm8400_mute(struct snd_soc_dai *dai, int mute)
+static int wm8400_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 val = snd_soc_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
+ struct snd_soc_component *component = dai->component;
+ u16 val = snd_soc_component_read(component, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
if (mute)
- snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+ snd_soc_component_write(component, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
else
- snd_soc_write(codec, WM8400_DAC_CTRL, val);
+ snd_soc_component_write(component, WM8400_DAC_CTRL, val);
return 0;
}
/* TODO: set bias for best performance at standby */
-static int wm8400_set_bias_level(struct snd_soc_codec *codec,
+static int wm8400_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
+ struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u16 val;
int ret;
@@ -1136,13 +1121,13 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*50k */
- val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1) &
~WM8400_VMID_MODE_MASK;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val | 0x2);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(power),
&power[0]);
if (ret != 0) {
@@ -1152,74 +1137,74 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
+ snd_soc_component_write(component, 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 |
+ snd_soc_component_write(component, 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 = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1);
val |= 0x2 | WM8400_VREF_ENA;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val);
/* Enable BUFIOEN */
- snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_SOFTST |
WM8400_BUFDCOPEN | WM8400_POBCTRL |
WM8400_BUFIOEN);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
+ snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_BUFIOEN);
}
/* VMID=2*300k */
- val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1) &
~WM8400_VMID_MODE_MASK;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
+ snd_soc_component_write(component, 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 |
+ snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_SOFTST |
WM8400_POBCTRL | WM8400_BUFIOEN);
/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ snd_soc_component_write(component, 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);
+ val = snd_soc_component_read(component, WM8400_DAC_CTRL);
+ snd_soc_component_write(component, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
/* Enable any disabled outputs */
- val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1);
val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
WM8400_OUT4_ENA | WM8400_LOUT_ENA |
WM8400_ROUT_ENA;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val);
/* Disable VMID */
val &= ~WM8400_VMID_MODE_MASK;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val);
msleep(300);
/* Enable all output discharge bits */
- snd_soc_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
+ snd_soc_component_write(component, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
WM8400_DIS_ROUT);
/* Disable VREF */
val &= ~WM8400_VREF_ENA;
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8400_ANTIPOP2, 0x0);
+ snd_soc_component_write(component, WM8400_ANTIPOP2, 0x0);
ret = regulator_bulk_disable(ARRAY_SIZE(power),
&power[0]);
@@ -1239,11 +1224,12 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8400_dai_ops = {
.hw_params = wm8400_hw_params,
- .digital_mute = wm8400_mute,
+ .mute_stream = wm8400_mute,
.set_fmt = wm8400_set_dai_fmt,
.set_clkdiv = wm8400_set_dai_clkdiv,
.set_sysclk = wm8400_set_dai_sysclk,
.set_pll = wm8400_set_dai_pll,
+ .no_capture_mute = 1,
};
/*
@@ -1273,98 +1259,85 @@ static struct snd_soc_dai_driver wm8400_dai = {
.ops = &wm8400_dai_ops,
};
-static int wm8400_codec_probe(struct snd_soc_codec *codec)
+static int wm8400_component_probe(struct snd_soc_component *component)
{
- struct wm8400 *wm8400 = dev_get_platdata(codec->dev);
+ struct wm8400 *wm8400 = dev_get_platdata(component->dev);
struct wm8400_priv *priv;
int ret;
u16 reg;
- priv = devm_kzalloc(codec->dev, sizeof(struct wm8400_priv),
+ priv = devm_kzalloc(component->dev, sizeof(struct wm8400_priv),
GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, priv);
+ snd_soc_component_init_regmap(component, wm8400->regmap);
+ snd_soc_component_set_drvdata(component, priv);
priv->wm8400 = wm8400;
ret = devm_regulator_bulk_get(wm8400->dev,
ARRAY_SIZE(power), &power[0]);
if (ret != 0) {
- dev_err(codec->dev, "Failed to get regulators: %d\n", ret);
+ dev_err(component->dev, "Failed to get regulators: %d\n", ret);
return ret;
}
- wm8400_codec_reset(codec);
+ wm8400_component_reset(component);
- reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
+ reg = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1);
+ snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
/* Latch volume update bits */
- reg = snd_soc_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
- snd_soc_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ reg = snd_soc_component_read(component, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
+ snd_soc_component_write(component, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
reg & WM8400_IPVU);
- reg = snd_soc_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
- snd_soc_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ reg = snd_soc_component_read(component, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
+ snd_soc_component_write(component, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
reg & WM8400_IPVU);
- snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
- snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_component_write(component, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_component_write(component, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
return 0;
}
-static int wm8400_codec_remove(struct snd_soc_codec *codec)
+static void wm8400_component_remove(struct snd_soc_component *component)
{
u16 reg;
- reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
- snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
+ reg = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1);
+ snd_soc_component_write(component, 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 const struct snd_soc_component_driver soc_component_dev_wm8400 = {
+ .probe = wm8400_component_probe,
+ .remove = wm8400_component_remove,
+ .set_bias_level = wm8400_set_bias_level,
+ .controls = wm8400_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8400_snd_controls),
+ .dapm_widgets = wm8400_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets),
+ .dapm_routes = wm8400_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm8400_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8400,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_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);
diff --git a/sound/soc/codecs/wm8400.h b/sound/soc/codecs/wm8400.h
index 521adb193870..9e7bd4f767e7 100644
--- a/sound/soc/codecs/wm8400.h
+++ b/sound/soc/codecs/wm8400.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 99e40e629cca..bebee333d3fd 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -1,15 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -19,7 +17,6 @@
#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>
@@ -106,7 +103,7 @@ static bool wm8510_volatile(struct device *dev, unsigned int reg)
#define WM8510_POWER1_BIASEN 0x08
#define WM8510_POWER1_BUFIOEN 0x10
-#define wm8510_reset(c) snd_soc_write(c, WM8510_RESET, 0)
+#define wm8510_reset(c) snd_soc_component_write(c, WM8510_RESET, 0)
/* codec private data */
struct wm8510_priv {
@@ -316,32 +313,32 @@ static void pll_factors(unsigned int target, unsigned int source)
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;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
- reg = snd_soc_read(codec, WM8510_CLOCK);
- snd_soc_write(codec, WM8510_CLOCK, reg & 0x0ff);
+ reg = snd_soc_component_read(component, WM8510_CLOCK);
+ snd_soc_component_write(component, WM8510_CLOCK, reg & 0x0ff);
/* Turn off PLL */
- reg = snd_soc_read(codec, WM8510_POWER1);
- snd_soc_write(codec, WM8510_POWER1, reg & 0x1df);
+ reg = snd_soc_component_read(component, WM8510_POWER1);
+ snd_soc_component_write(component, WM8510_POWER1, reg & 0x1df);
return 0;
}
pll_factors(freq_out*4, freq_in);
- snd_soc_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);
+ snd_soc_component_write(component, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_component_write(component, WM8510_PLLK1, pll_div.k >> 18);
+ snd_soc_component_write(component, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8510_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_component_read(component, WM8510_POWER1);
+ snd_soc_component_write(component, WM8510_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
- reg = snd_soc_read(codec, WM8510_CLOCK);
- snd_soc_write(codec, WM8510_CLOCK, reg | 0x100);
+ reg = snd_soc_component_read(component, WM8510_CLOCK);
+ snd_soc_component_write(component, WM8510_CLOCK, reg | 0x100);
return 0;
}
@@ -352,29 +349,29 @@ static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
switch (div_id) {
case WM8510_OPCLKDIV:
- reg = snd_soc_read(codec, WM8510_GPIO) & 0x1cf;
- snd_soc_write(codec, WM8510_GPIO, reg | div);
+ reg = snd_soc_component_read(component, WM8510_GPIO) & 0x1cf;
+ snd_soc_component_write(component, WM8510_GPIO, reg | div);
break;
case WM8510_MCLKDIV:
- reg = snd_soc_read(codec, WM8510_CLOCK) & 0x11f;
- snd_soc_write(codec, WM8510_CLOCK, reg | div);
+ reg = snd_soc_component_read(component, WM8510_CLOCK) & 0x11f;
+ snd_soc_component_write(component, WM8510_CLOCK, reg | div);
break;
case WM8510_ADCCLK:
- reg = snd_soc_read(codec, WM8510_ADC) & 0x1f7;
- snd_soc_write(codec, WM8510_ADC, reg | div);
+ reg = snd_soc_component_read(component, WM8510_ADC) & 0x1f7;
+ snd_soc_component_write(component, WM8510_ADC, reg | div);
break;
case WM8510_DACCLK:
- reg = snd_soc_read(codec, WM8510_DAC) & 0x1f7;
- snd_soc_write(codec, WM8510_DAC, reg | div);
+ reg = snd_soc_component_read(component, WM8510_DAC) & 0x1f7;
+ snd_soc_component_write(component, WM8510_DAC, reg | div);
break;
case WM8510_BCLKDIV:
- reg = snd_soc_read(codec, WM8510_CLOCK) & 0x1e3;
- snd_soc_write(codec, WM8510_CLOCK, reg | div);
+ reg = snd_soc_component_read(component, WM8510_CLOCK) & 0x1e3;
+ snd_soc_component_write(component, WM8510_CLOCK, reg | div);
break;
default:
return -EINVAL;
@@ -386,16 +383,16 @@ static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- u16 clk = snd_soc_read(codec, WM8510_CLOCK) & 0x1fe;
+ u16 clk = snd_soc_component_read(component, WM8510_CLOCK) & 0x1fe;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
clk |= 0x0001;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -435,8 +432,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8510_IFACE, iface);
- snd_soc_write(codec, WM8510_CLOCK, clk);
+ snd_soc_component_write(component, WM8510_IFACE, iface);
+ snd_soc_component_write(component, WM8510_CLOCK, clk);
return 0;
}
@@ -444,9 +441,9 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ u16 iface = snd_soc_component_read(component, WM8510_IFACE) & 0x19f;
+ u16 adn = snd_soc_component_read(component, WM8510_ADD) & 0x1f1;
/* bit size */
switch (params_width(params)) {
@@ -485,56 +482,57 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8510_IFACE, iface);
- snd_soc_write(codec, WM8510_ADD, adn);
+ snd_soc_component_write(component, WM8510_IFACE, iface);
+ snd_soc_component_write(component, WM8510_ADD, adn);
return 0;
}
-static int wm8510_mute(struct snd_soc_dai *dai, int mute)
+static int wm8510_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8510_DAC) & 0xffbf;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8510_DAC) & 0xffbf;
if (mute)
- snd_soc_write(codec, WM8510_DAC, mute_reg | 0x40);
+ snd_soc_component_write(component, WM8510_DAC, mute_reg | 0x40);
else
- snd_soc_write(codec, WM8510_DAC, mute_reg);
+ snd_soc_component_write(component, WM8510_DAC, mute_reg);
return 0;
}
/* liam need to make this lower power with dapm */
-static int wm8510_set_bias_level(struct snd_soc_codec *codec,
+static int wm8510_set_bias_level(struct snd_soc_component *component,
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;
+ struct wm8510_priv *wm8510 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 power1 = snd_soc_component_read(component, WM8510_POWER1) & ~0x3;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
power1 |= 0x1; /* VMID 50k */
- snd_soc_write(codec, WM8510_POWER1, power1);
+ snd_soc_component_write(component, WM8510_POWER1, power1);
break;
case SND_SOC_BIAS_STANDBY:
power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
regcache_sync(wm8510->regmap);
/* Initial cap charge at VMID 5k */
- snd_soc_write(codec, WM8510_POWER1, power1 | 0x3);
+ snd_soc_component_write(component, WM8510_POWER1, power1 | 0x3);
mdelay(100);
}
power1 |= 0x2; /* VMID 500k */
- snd_soc_write(codec, WM8510_POWER1, power1);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8510_POWER1, 0);
+ snd_soc_component_write(component, WM8510_POWER2, 0);
+ snd_soc_component_write(component, WM8510_POWER3, 0);
break;
}
@@ -550,10 +548,11 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8510_dai_ops = {
.hw_params = wm8510_pcm_hw_params,
- .digital_mute = wm8510_mute,
+ .mute_stream = wm8510_mute,
.set_fmt = wm8510_set_dai_fmt,
.set_clkdiv = wm8510_set_dai_clkdiv,
.set_pll = wm8510_set_dai_pll,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8510_dai = {
@@ -571,27 +570,29 @@ static struct snd_soc_dai_driver wm8510_dai = {
.rates = WM8510_RATES,
.formats = WM8510_FORMATS,},
.ops = &wm8510_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8510_probe(struct snd_soc_codec *codec)
+static int wm8510_probe(struct snd_soc_component *component)
{
- wm8510_reset(codec);
+ wm8510_reset(component);
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 snd_soc_component_driver soc_component_dev_wm8510 = {
+ .probe = wm8510_probe,
+ .set_bias_level = wm8510_set_bias_level,
+ .controls = wm8510_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8510_snd_controls),
+ .dapm_widgets = wm8510_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets),
+ .dapm_routes = wm8510_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8510_of_match[] = {
@@ -607,7 +608,7 @@ static const struct regmap_config wm8510_regmap = {
.reg_defaults = wm8510_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8510_volatile,
};
@@ -629,31 +630,23 @@ static int wm8510_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8510);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8510, &wm8510_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.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)
+static int wm8510_i2c_probe(struct i2c_client *i2c)
{
struct wm8510_priv *wm8510;
int ret;
@@ -669,20 +662,14 @@ static int wm8510_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8510);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8510, &wm8510_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8510" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
@@ -692,8 +679,7 @@ static struct i2c_driver wm8510_i2c_driver = {
.name = "wm8510",
.of_match_table = wm8510_of_match,
},
- .probe = wm8510_i2c_probe,
- .remove = wm8510_i2c_remove,
+ .probe = wm8510_i2c_probe,
.id_table = wm8510_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
index b3e26ed9f2d0..1f4354947382 100644
--- a/sound/soc/codecs/wm8510.h
+++ b/sound/soc/codecs/wm8510.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index aa287a3965e7..f003f19766e2 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -1,16 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -20,7 +17,6 @@
#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>
@@ -100,7 +96,7 @@ static const struct snd_soc_dapm_route wm8523_dapm_routes[] = {
{ "LINEVOUTR", NULL, "DAC" },
};
-static struct {
+static const struct {
int value;
int ratio;
} lrclk_ratios[WM8523_NUM_RATES] = {
@@ -113,10 +109,10 @@ static struct {
{ 7, 1152 },
};
-static struct {
+static const struct {
int value;
int ratio;
-} bclk_ratios[WM8523_NUM_RATES] = {
+} bclk_ratios[] = {
{ 2, 32 },
{ 3, 64 },
{ 4, 128 },
@@ -125,14 +121,14 @@ static struct {
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
if (!wm8523->sysclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"No MCLK configured, call set_sysclk() on init\n");
return -EINVAL;
}
@@ -148,11 +144,11 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component);
int i;
- u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
- u16 aifctrl2 = snd_soc_read(codec, WM8523_AIF_CTRL2);
+ u16 aifctrl1 = snd_soc_component_read(component, WM8523_AIF_CTRL1);
+ u16 aifctrl2 = snd_soc_component_read(component, WM8523_AIF_CTRL2);
/* Find a supported LRCLK ratio */
for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
@@ -163,7 +159,7 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
/* Should never happen, should be handled by constraints */
if (i == ARRAY_SIZE(lrclk_ratios)) {
- dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+ dev_err(component->dev, "MCLK/fs ratio %d unsupported\n",
wm8523->sysclk / params_rate(params));
return -EINVAL;
}
@@ -178,7 +174,7 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
break;
if (i == ARRAY_SIZE(bclk_ratios)) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"No matching BCLK/fs ratio for word length %d\n",
params_width(params));
return -EINVAL;
@@ -203,8 +199,8 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
- snd_soc_write(codec, WM8523_AIF_CTRL2, aifctrl2);
+ snd_soc_component_write(component, WM8523_AIF_CTRL1, aifctrl1);
+ snd_soc_component_write(component, WM8523_AIF_CTRL2, aifctrl2);
return 0;
}
@@ -212,8 +208,8 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component);
unsigned int val;
int i;
@@ -239,13 +235,13 @@ static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
case 96000:
case 176400:
case 192000:
- dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
+ dev_dbg(component->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",
+ dev_dbg(component->dev, "Skipping sample rate: %dHz\n",
val);
}
}
@@ -261,17 +257,17 @@ static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ u16 aifctrl1 = snd_soc_component_read(component, WM8523_AIF_CTRL1);
aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK |
WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aifctrl1 |= WM8523_AIF_MSTR;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -312,15 +308,16 @@ static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+ snd_soc_component_write(component, WM8523_AIF_CTRL1, aifctrl1);
return 0;
}
-static int wm8523_set_bias_level(struct snd_soc_codec *codec,
+static int wm8523_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+ struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -329,16 +326,16 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* Full power on */
- snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ snd_soc_component_update_bits(component, WM8523_PSCTRL1,
WM8523_SYS_ENA_MASK, 3);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
wm8523->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
@@ -348,21 +345,21 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec,
regcache_sync(wm8523->regmap);
/* Initial power up */
- snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ snd_soc_component_update_bits(component, WM8523_PSCTRL1,
WM8523_SYS_ENA_MASK, 1);
msleep(100);
}
/* Power up to mute */
- snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ snd_soc_component_update_bits(component, WM8523_PSCTRL1,
WM8523_SYS_ENA_MASK, 2);
break;
case SND_SOC_BIAS_OFF:
/* The chip runs through the power down sequence for us. */
- snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ snd_soc_component_update_bits(component, WM8523_PSCTRL1,
WM8523_SYS_ENA_MASK, 0);
msleep(100);
@@ -397,33 +394,35 @@ static struct snd_soc_dai_driver wm8523_dai = {
.ops = &wm8523_dai_ops,
};
-static int wm8523_probe(struct snd_soc_codec *codec)
+static int wm8523_probe(struct snd_soc_component *component)
{
- struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+ struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component);
wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
wm8523->rate_constraint.count =
ARRAY_SIZE(wm8523->rate_constraint_list);
/* Change some default settings - latch VU and enable ZC */
- snd_soc_update_bits(codec, WM8523_DAC_GAINR,
+ snd_soc_component_update_bits(component, WM8523_DAC_GAINR,
WM8523_DACR_VU, WM8523_DACR_VU);
- snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
+ snd_soc_component_update_bits(component, 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 snd_soc_component_driver soc_component_dev_wm8523 = {
+ .probe = wm8523_probe,
+ .set_bias_level = wm8523_set_bias_level,
+ .controls = wm8523_controls,
+ .num_controls = ARRAY_SIZE(wm8523_controls),
+ .dapm_widgets = wm8523_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets),
+ .dapm_routes = wm8523_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8523_of_match[] = {
@@ -439,14 +438,12 @@ static const struct regmap_config wm8523_regmap = {
.reg_defaults = wm8523_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8523_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.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)
+static int wm8523_i2c_probe(struct i2c_client *i2c)
{
struct wm8523_priv *wm8523;
unsigned int val;
@@ -510,8 +507,8 @@ static int wm8523_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8523);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8523, &wm8523_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8523, &wm8523_dai, 1);
return ret;
@@ -520,14 +517,8 @@ err_enable:
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 },
+ { "wm8523" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
@@ -537,33 +528,11 @@ static struct i2c_driver wm8523_i2c_driver = {
.name = "wm8523",
.of_match_table = wm8523_of_match,
},
- .probe = wm8523_i2c_probe,
- .remove = wm8523_i2c_remove,
+ .probe = wm8523_i2c_probe,
.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_i2c_driver(wm8523_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8523 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h
index 4d5b1eb8f2fc..5f9bb3df1866 100644
--- a/sound/soc/codecs/wm8523.h
+++ b/sound/soc/codecs/wm8523.h
@@ -1,15 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * wm8523.h -- WM8423 ASoC driver
+ * wm8523.h -- WM8523 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
diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c
new file mode 100644
index 000000000000..6b1a7450b0ac
--- /dev/null
+++ b/sound/soc/codecs/wm8524.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8524.c -- WM8524 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2017 NXP
+ *
+ * Based on WM8523 ALSA SoC Audio driver written by Mark Brown
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#define WM8524_NUM_RATES 12
+
+/* codec private data */
+struct wm8524_priv {
+ struct gpio_desc *mute;
+ unsigned int sysclk;
+ unsigned int rate_constraint_list[WM8524_NUM_RATES];
+ struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+
+static const struct snd_soc_dapm_widget wm8524_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8524_dapm_routes[] = {
+ { "LINEVOUTL", NULL, "DAC" },
+ { "LINEVOUTR", NULL, "DAC" },
+};
+
+static const struct {
+ int value;
+ int ratio;
+} lrclk_ratios[] = {
+ { 1, 128 },
+ { 2, 192 },
+ { 3, 256 },
+ { 4, 384 },
+ { 5, 512 },
+ { 6, 768 },
+ { 7, 1152 },
+};
+
+static int wm8524_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
+
+ /* The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC.
+ */
+ if (wm8524->sysclk)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &wm8524->rate_constraint);
+
+ gpiod_set_value_cansleep(wm8524->mute, 1);
+
+ return 0;
+}
+
+static void wm8524_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
+
+ gpiod_set_value_cansleep(wm8524->mute, 0);
+}
+
+static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int i, j = 0;
+
+ wm8524->rate_constraint.count = 0;
+ wm8524->sysclk = freq;
+ if (!wm8524->sysclk)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ val = freq / lrclk_ratios[i].ratio;
+ /* Check that it's a standard rate since core can't
+ * cope with others and having the odd rates confuses
+ * constraint matching.
+ */
+ switch (val) {
+ case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ dev_dbg(component->dev, "Supported sample rate: %dHz\n",
+ val);
+ wm8524->rate_constraint_list[j++] = val;
+ wm8524->rate_constraint.count++;
+ break;
+ default:
+ dev_dbg(component->dev, "Skipping sample rate: %dHz\n",
+ val);
+ }
+ }
+
+ /* Need at least one supported rate... */
+ if (wm8524->rate_constraint.count == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int wm8524_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK);
+
+ if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC)) {
+ dev_err(codec_dai->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(dai->component);
+
+ if (wm8524->mute)
+ gpiod_set_value_cansleep(wm8524->mute, mute);
+
+ return 0;
+}
+
+static int wm8524_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ /* If sysclk is not configured, no need to check the rate */
+ if (!wm8524->sysclk)
+ return 0;
+
+ /* Find a supported LRCLK rate */
+ for (i = 0; i < wm8524->rate_constraint.count; i++) {
+ if (wm8524->rate_constraint.list[i] == params_rate(params))
+ break;
+ }
+
+ if (i == wm8524->rate_constraint.count) {
+ dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n",
+ params_rate(params), wm8524->sysclk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define WM8524_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8524_dai_ops = {
+ .startup = wm8524_startup,
+ .shutdown = wm8524_shutdown,
+ .set_sysclk = wm8524_set_dai_sysclk,
+ .set_fmt = wm8524_set_fmt,
+ .mute_stream = wm8524_mute_stream,
+ .hw_params = wm8524_hw_params,
+};
+
+static struct snd_soc_dai_driver wm8524_dai = {
+ .name = "wm8524-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8524_RATES,
+ .formats = WM8524_FORMATS,
+ },
+ .ops = &wm8524_dai_ops,
+};
+
+static int wm8524_probe(struct snd_soc_component *component)
+{
+ struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
+
+ wm8524->rate_constraint.list = &wm8524->rate_constraint_list[0];
+ wm8524->rate_constraint.count =
+ ARRAY_SIZE(wm8524->rate_constraint_list);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_wm8524 = {
+ .probe = wm8524_probe,
+ .dapm_widgets = wm8524_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8524_dapm_widgets),
+ .dapm_routes = wm8524_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8524_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct of_device_id wm8524_of_match[] = {
+ { .compatible = "wlf,wm8524" },
+ { /* sentinel*/ }
+};
+MODULE_DEVICE_TABLE(of, wm8524_of_match);
+
+static int wm8524_codec_probe(struct platform_device *pdev)
+{
+ struct wm8524_priv *wm8524;
+ int ret;
+
+ wm8524 = devm_kzalloc(&pdev->dev, sizeof(struct wm8524_priv),
+ GFP_KERNEL);
+ if (wm8524 == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, wm8524);
+
+ wm8524->mute = devm_gpiod_get(&pdev->dev, "wlf,mute", GPIOD_OUT_LOW);
+ if (IS_ERR(wm8524->mute)) {
+ ret = PTR_ERR(wm8524->mute);
+ dev_err_probe(&pdev->dev, ret, "Failed to get mute line\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm8524, &wm8524_dai, 1);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+
+ return ret;
+}
+
+static struct platform_driver wm8524_codec_driver = {
+ .probe = wm8524_codec_probe,
+ .driver = {
+ .name = "wm8524-codec",
+ .of_match_table = wm8524_of_match,
+ },
+};
+module_platform_driver(wm8524_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8524 driver");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+MODULE_ALIAS("platform:wm8524-codec");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 66602bf02f6e..2be265bb0751 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -1,21 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * wm8580.c -- WM8580 ALSA Soc Audio driver
+ * wm8580.c -- WM8580 and WM8581 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.
*
+ * The WM8581 is a multichannel codec with S/PDIF support, featuring eight
+ * DAC channels and two ADC channels.
+ *
* Currently only the primary audio interface is supported - S/PDIF and
* the secondary audio interfaces are not.
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -26,7 +26,6 @@
#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>
@@ -65,6 +64,8 @@
#define WM8580_DIGITAL_ATTENUATION_DACR2 0x17
#define WM8580_DIGITAL_ATTENUATION_DACL3 0x18
#define WM8580_DIGITAL_ATTENUATION_DACR3 0x19
+#define WM8581_DIGITAL_ATTENUATION_DACL4 0x1A
+#define WM8581_DIGITAL_ATTENUATION_DACR4 0x1B
#define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C
#define WM8580_ADC_CONTROL1 0x1D
#define WM8580_SPDTXCHAN0 0x1E
@@ -236,12 +237,17 @@ static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
"PVDD",
};
+struct wm8580_driver_data {
+ int num_dacs;
+};
+
/* codec private data */
struct wm8580_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
struct pll_state a;
struct pll_state b;
+ const struct wm8580_driver_data *drvdata;
int sysclk[2];
};
@@ -252,8 +258,8 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int ret;
@@ -269,8 +275,8 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
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);
+ snd_soc_component_update_bits(component, reg, 0x100, 0x100);
+ snd_soc_component_update_bits(component, reg2, 0x100, 0x100);
return 0;
}
@@ -306,6 +312,19 @@ SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1),
SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
};
+static const struct snd_kcontrol_new wm8581_snd_controls[] = {
+SOC_DOUBLE_R_EXT_TLV("DAC4 Playback Volume",
+ WM8581_DIGITAL_ATTENUATION_DACL4,
+ WM8581_DIGITAL_ATTENUATION_DACR4,
+ 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv),
+
+SOC_SINGLE("DAC4 Deemphasis Switch", WM8580_DAC_CONTROL3, 3, 1, 0),
+
+SOC_DOUBLE("DAC4 Invert Switch", WM8580_DAC_CONTROL4, 8, 7, 1, 0),
+
+SOC_SINGLE("DAC4 Switch", WM8580_DAC_CONTROL5, 3, 1, 1),
+};
+
static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
@@ -324,6 +343,13 @@ SND_SOC_DAPM_INPUT("AINL"),
SND_SOC_DAPM_INPUT("AINR"),
};
+static const struct snd_soc_dapm_widget wm8581_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC4", "Playback", WM8580_PWRDN1, 5, 1),
+
+SND_SOC_DAPM_OUTPUT("VOUT4L"),
+SND_SOC_DAPM_OUTPUT("VOUT4R"),
+};
+
static const struct snd_soc_dapm_route wm8580_dapm_routes[] = {
{ "VOUT1L", NULL, "DAC1" },
{ "VOUT1R", NULL, "DAC1" },
@@ -338,6 +364,11 @@ static const struct snd_soc_dapm_route wm8580_dapm_routes[] = {
{ "ADC", NULL, "AINR" },
};
+static const struct snd_soc_dapm_route wm8581_dapm_routes[] = {
+ { "VOUT4L", NULL, "DAC4" },
+ { "VOUT4R", NULL, "DAC4" },
+};
+
/* PLL divisors */
struct _pll_div {
u32 prescale:1;
@@ -430,8 +461,8 @@ 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 snd_soc_component *component = codec_dai->component;
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
struct pll_state *state;
struct _pll_div pll_div;
unsigned int reg;
@@ -470,25 +501,25 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* 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);
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_write(component, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+ snd_soc_component_write(component, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8580_PLLA3 + offset,
(pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
- reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
+ reg = snd_soc_component_read(component, WM8580_PLLA4 + offset);
reg &= ~0x1b;
reg |= pll_div.prescale | pll_div.postscale << 1 |
pll_div.freqmode << 3;
- snd_soc_write(codec, WM8580_PLLA4 + offset, reg);
+ snd_soc_component_write(component, WM8580_PLLA4 + offset, reg);
/* All done, turn it on */
- snd_soc_update_bits(codec, WM8580_PWRDN2, pwr_mask, 0);
+ snd_soc_component_update_bits(component, WM8580_PWRDN2, pwr_mask, 0);
return 0;
}
@@ -504,8 +535,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
u16 paifa = 0;
u16 paifb = 0;
int i, ratio, osr;
@@ -537,12 +568,12 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
if (ratio == wm8580_sysclk_ratios[i])
break;
if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) {
- dev_err(codec->dev, "Invalid clock ratio %d/%d\n",
+ dev_err(component->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",
+ dev_dbg(component->dev, "Running at %dfs with %dHz clock\n",
wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -550,21 +581,21 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
case 128:
case 192:
osr = WM8580_DACOSR;
- dev_dbg(codec->dev, "Selecting 64x OSR\n");
+ dev_dbg(component->dev, "Selecting 64x OSR\n");
break;
default:
osr = 0;
- dev_dbg(codec->dev, "Selecting 128x OSR\n");
+ dev_dbg(component->dev, "Selecting 128x OSR\n");
break;
}
- snd_soc_update_bits(codec, WM8580_PAIF3, WM8580_DACOSR, osr);
+ snd_soc_component_update_bits(component, WM8580_PAIF3, WM8580_DACOSR, osr);
}
- snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id,
+ snd_soc_component_update_bits(component, WM8580_PAIF1 + dai->driver->id,
WM8580_AIF_RATE_MASK | WM8580_AIF_BCLKSEL_MASK,
paifa);
- snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id,
+ snd_soc_component_update_bits(component, WM8580_PAIF3 + dai->driver->id,
WM8580_AIF_LENGTH_MASK, paifb);
return 0;
}
@@ -572,21 +603,21 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int 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);
+ aifa = snd_soc_component_read(component, WM8580_PAIF1 + codec_dai->driver->id);
+ aifb = snd_soc_component_read(component, WM8580_PAIF3 + codec_dai->driver->id);
aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
aifa &= ~WM8580_AIF_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aifa |= WM8580_AIF_MS;
break;
default:
@@ -644,8 +675,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa);
- snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb);
+ snd_soc_component_write(component, WM8580_PAIF1 + codec_dai->driver->id, aifa);
+ snd_soc_component_write(component, WM8580_PAIF3 + codec_dai->driver->id, aifb);
return 0;
}
@@ -653,12 +684,12 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
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;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int reg;
switch (div_id) {
case WM8580_MCLK:
- reg = snd_soc_read(codec, WM8580_PLLB4);
+ reg = snd_soc_component_read(component, WM8580_PLLB4);
reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
switch (div) {
@@ -680,11 +711,11 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
default:
return -EINVAL;
}
- snd_soc_write(codec, WM8580_PLLB4, reg);
+ snd_soc_component_write(component, WM8580_PLLB4, reg);
break;
case WM8580_CLKOUTSRC:
- reg = snd_soc_read(codec, WM8580_PLLB4);
+ reg = snd_soc_component_read(component, WM8580_PLLB4);
reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
switch (div) {
@@ -706,7 +737,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
default:
return -EINVAL;
}
- snd_soc_write(codec, WM8580_PLLB4, reg);
+ snd_soc_component_write(component, WM8580_PLLB4, reg);
break;
default:
@@ -719,8 +750,8 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
int ret, sel, sel_mask, sel_shift;
switch (dai->driver->id) {
@@ -755,76 +786,90 @@ static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id,
sel = 3 << sel_shift;
break;
default:
- dev_err(codec->dev, "Unknown clock %d\n", clk_id);
+ dev_err(component->dev, "Unknown clock %d\n", clk_id);
return -EINVAL;
}
/* We really should validate PLL settings but not yet */
wm8580->sysclk[dai->driver->id] = freq;
- ret = snd_soc_update_bits(codec, WM8580_CLKSEL, sel_mask, sel);
+ ret = snd_soc_component_update_bits(component, 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)
+static int wm8580_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int reg;
- reg = snd_soc_read(codec, WM8580_DAC_CONTROL5);
+ reg = snd_soc_component_read(component, WM8580_DAC_CONTROL5);
if (mute)
reg |= WM8580_DAC_CONTROL5_MUTEALL;
else
reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
- snd_soc_write(codec, WM8580_DAC_CONTROL5, reg);
+ snd_soc_component_write(component, WM8580_DAC_CONTROL5, reg);
return 0;
}
-static int wm8580_set_bias_level(struct snd_soc_codec *codec,
+static int wm8580_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Power up and get individual control of the DACs */
- snd_soc_update_bits(codec, WM8580_PWRDN1,
+ snd_soc_component_update_bits(component, WM8580_PWRDN1,
WM8580_PWRDN1_PWDN |
WM8580_PWRDN1_ALLDACPD, 0);
/* Make VMID high impedance */
- snd_soc_update_bits(codec, WM8580_ADC_CONTROL1,
+ snd_soc_component_update_bits(component, WM8580_ADC_CONTROL1,
0x100, 0);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, WM8580_PWRDN1,
+ snd_soc_component_update_bits(component, WM8580_PWRDN1,
WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN);
break;
}
return 0;
}
+static int wm8580_playback_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 1, wm8580->drvdata->num_dacs * 2);
+}
+
#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops wm8580_dai_ops_playback = {
+ .startup = wm8580_playback_startup,
.set_sysclk = wm8580_set_sysclk,
.hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
- .digital_mute = wm8580_digital_mute,
+ .mute_stream = wm8580_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8580_dai_ops_capture = {
@@ -842,7 +887,6 @@ static struct snd_soc_dai_driver wm8580_dai[] = {
.playback = {
.stream_name = "Playback",
.channels_min = 1,
- .channels_max = 6,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = WM8580_FORMATS,
},
@@ -862,22 +906,36 @@ static struct snd_soc_dai_driver wm8580_dai[] = {
},
};
-static int wm8580_probe(struct snd_soc_codec *codec)
+static int wm8580_probe(struct snd_soc_component *component)
{
- struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret = 0;
+ switch (wm8580->drvdata->num_dacs) {
+ case 4:
+ snd_soc_add_component_controls(component, wm8581_snd_controls,
+ ARRAY_SIZE(wm8581_snd_controls));
+ snd_soc_dapm_new_controls(dapm, wm8581_dapm_widgets,
+ ARRAY_SIZE(wm8581_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, wm8581_dapm_routes,
+ ARRAY_SIZE(wm8581_dapm_routes));
+ break;
+ default:
+ break;
+ }
+
ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
wm8580->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
goto err_regulator_get;
}
/* Get the codec into a known state */
- ret = snd_soc_write(codec, WM8580_RESET, 0);
+ ret = snd_soc_component_write(component, WM8580_RESET, 0);
if (ret != 0) {
- dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ dev_err(component->dev, "Failed to reset component: %d\n", ret);
goto err_regulator_enable;
}
@@ -890,34 +948,28 @@ err_regulator_get:
}
/* power down chip */
-static int wm8580_remove(struct snd_soc_codec *codec)
+static void wm8580_remove(struct snd_soc_component *component)
{
- struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component);
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 snd_soc_component_driver soc_component_dev_wm8580 = {
+ .probe = wm8580_probe,
+ .remove = wm8580_remove,
+ .set_bias_level = wm8580_set_bias_level,
+ .controls = wm8580_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8580_snd_controls),
+ .dapm_widgets = wm8580_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets),
+ .dapm_routes = wm8580_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static const struct of_device_id wm8580_of_match[] = {
- { .compatible = "wlf,wm8580" },
- { },
-};
-MODULE_DEVICE_TABLE(of, wm8580_of_match);
-
static const struct regmap_config wm8580_regmap = {
.reg_bits = 7,
.val_bits = 9,
@@ -925,14 +977,20 @@ static const struct regmap_config wm8580_regmap = {
.reg_defaults = wm8580_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8580_volatile,
};
-#if IS_ENABLED(CONFIG_I2C)
-static int wm8580_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct wm8580_driver_data wm8580_data = {
+ .num_dacs = 3,
+};
+
+static const struct wm8580_driver_data wm8581_data = {
+ .num_dacs = 4,
+};
+
+static int wm8580_i2c_probe(struct i2c_client *i2c)
{
struct wm8580_priv *wm8580;
int ret, i;
@@ -958,20 +1016,26 @@ static int wm8580_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8580);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai));
+ wm8580->drvdata = i2c_get_match_data(i2c);
+ if (!wm8580->drvdata)
+ return dev_err_probe(&i2c->dev, -EINVAL, "failed to find driver data\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 of_device_id wm8580_of_match[] = {
+ { .compatible = "wlf,wm8580", .data = &wm8580_data },
+ { .compatible = "wlf,wm8581", .data = &wm8581_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8580_of_match);
static const struct i2c_device_id wm8580_i2c_id[] = {
- { "wm8580", 0 },
+ { "wm8580", (kernel_ulong_t)&wm8580_data },
+ { "wm8581", (kernel_ulong_t)&wm8581_data },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
@@ -981,35 +1045,13 @@ static struct i2c_driver wm8580_i2c_driver = {
.name = "wm8580",
.of_match_table = wm8580_of_match,
},
- .probe = wm8580_i2c_probe,
- .remove = wm8580_i2c_remove,
+ .probe = wm8580_i2c_probe,
.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_i2c_driver(wm8580_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8580 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h
index 1d34656d0dcb..34f7fee6b946 100644
--- a/sound/soc/codecs/wm8580.h
+++ b/sound/soc/codecs/wm8580.h
@@ -1,15 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* wm8580.h -- audio driver for WM8580
*
* Copyright 2008 Samsung Electronics.
* Author: Ryu Euiyoul
* ryu.real@gmail.com
- *
- * 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
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index c759ec068e97..2bab9d189519 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8711.c -- WM8711 ALSA SoC Audio driver
*
@@ -6,12 +7,9 @@
* 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -21,7 +19,6 @@
#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>
@@ -58,7 +55,7 @@ static bool wm8711_volatile(struct device *dev, unsigned int reg)
}
}
-#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
+#define wm8711_reset(c) snd_soc_component_write(c, WM8711_RESET, 0)
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
@@ -159,14 +156,14 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0xfff3;
int i = get_coeff(wm8711->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
- snd_soc_write(codec, WM8711_SRATE, srate);
+ snd_soc_component_write(component, WM8711_SRATE, srate);
/* bit size */
switch (params_width(params)) {
@@ -180,17 +177,17 @@ static int wm8711_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8711_IFACE, iface);
+ snd_soc_component_write(component, WM8711_IFACE, iface);
return 0;
}
static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
/* set active */
- snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+ snd_soc_component_write(component, WM8711_ACTIVE, 0x0001);
return 0;
}
@@ -198,24 +195,24 @@ static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
static void wm8711_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
/* deactivate */
- if (!snd_soc_codec_is_active(codec)) {
+ if (!snd_soc_component_active(component)) {
udelay(50);
- snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ snd_soc_component_write(component, WM8711_ACTIVE, 0x0);
}
}
-static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+static int wm8711_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8711_APDIGI) & 0xfff7;
if (mute)
- snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+ snd_soc_component_write(component, WM8711_APDIGI, mute_reg | 0x8);
else
- snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+ snd_soc_component_write(component, WM8711_APDIGI, mute_reg);
return 0;
}
@@ -223,8 +220,8 @@ static int wm8711_mute(struct snd_soc_dai *dai, int mute)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 11289600:
@@ -241,15 +238,15 @@ static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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;
+ struct snd_soc_component *component = codec_dai->component;
+ u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0x000c;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -293,31 +290,32 @@ static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- snd_soc_write(codec, WM8711_IFACE, iface);
+ snd_soc_component_write(component, WM8711_IFACE, iface);
return 0;
}
-static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+static int wm8711_set_bias_level(struct snd_soc_component *component,
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;
+ struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 reg = snd_soc_component_read(component, WM8711_PWR) & 0xff7f;
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_write(codec, WM8711_PWR, reg);
+ snd_soc_component_write(component, WM8711_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
regcache_sync(wm8711->regmap);
- snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+ snd_soc_component_write(component, WM8711_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, WM8711_ACTIVE, 0x0);
- snd_soc_write(codec, WM8711_PWR, 0xffff);
+ snd_soc_component_write(component, WM8711_ACTIVE, 0x0);
+ snd_soc_component_write(component, WM8711_PWR, 0xffff);
break;
}
return 0;
@@ -332,9 +330,10 @@ 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,
+ .mute_stream = wm8711_mute,
.set_sysclk = wm8711_set_dai_sysclk,
.set_fmt = wm8711_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8711_dai = {
@@ -349,35 +348,37 @@ static struct snd_soc_dai_driver wm8711_dai = {
.ops = &wm8711_ops,
};
-static int wm8711_probe(struct snd_soc_codec *codec)
+static int wm8711_probe(struct snd_soc_component *component)
{
int ret;
- ret = wm8711_reset(codec);
+ ret = wm8711_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
+ dev_err(component->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);
+ snd_soc_component_update_bits(component, WM8711_LOUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, 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 snd_soc_component_driver soc_component_dev_wm8711 = {
+ .probe = wm8711_probe,
+ .set_bias_level = wm8711_set_bias_level,
+ .controls = wm8711_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8711_snd_controls),
+ .dapm_widgets = wm8711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
+ .dapm_routes = wm8711_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8711_of_match[] = {
@@ -393,7 +394,7 @@ static const struct regmap_config wm8711_regmap = {
.reg_defaults = wm8711_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8711_volatile,
};
@@ -415,32 +416,23 @@ static int wm8711_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8711);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8711, &wm8711_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.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)
+static int wm8711_i2c_probe(struct i2c_client *client)
{
struct wm8711_priv *wm8711;
int ret;
@@ -456,20 +448,14 @@ static int wm8711_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, wm8711);
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_wm8711, &wm8711_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_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 },
+ { "wm8711" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
@@ -479,8 +465,7 @@ static struct i2c_driver wm8711_i2c_driver = {
.name = "wm8711",
.of_match_table = wm8711_of_match,
},
- .probe = wm8711_i2c_probe,
- .remove = wm8711_i2c_remove,
+ .probe = wm8711_i2c_probe,
.id_table = wm8711_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h
index a61db985499f..487a9f34d191 100644
--- a/sound/soc/codecs/wm8711.h
+++ b/sound/soc/codecs/wm8711.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* wm8711.h -- WM8711 Soc Audio driver
*
@@ -6,10 +7,6 @@
* 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
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
index bb25a75f92a2..d6b0a570dd87 100644
--- a/sound/soc/codecs/wm8727.c
+++ b/sound/soc/codecs/wm8727.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* wm8727.c
*
@@ -5,11 +6,6 @@
* 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>
@@ -19,7 +15,6 @@
#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>
@@ -41,7 +36,6 @@ static const struct snd_soc_dapm_route wm8727_dapm_routes[] = {
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
SNDRV_PCM_RATE_192000)
-
static struct snd_soc_dai_driver wm8727_dai = {
.name = "wm8727-hifi",
.playback = {
@@ -53,23 +47,20 @@ static struct snd_soc_dai_driver wm8727_dai = {
},
};
-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 const struct snd_soc_component_driver soc_component_dev_wm8727 = {
+ .dapm_widgets = wm8727_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets),
+ .dapm_routes = wm8727_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm8727_probe(struct platform_device *pdev)
{
- return 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;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm8727, &wm8727_dai, 1);
}
static struct platform_driver wm8727_codec_driver = {
@@ -78,7 +69,6 @@ static struct platform_driver wm8727_codec_driver = {
},
.probe = wm8727_probe,
- .remove = wm8727_remove,
};
module_platform_driver(wm8727_codec_driver);
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 1564e6926527..4c1a80561f06 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -1,15 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -20,7 +18,6 @@
#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>
@@ -72,15 +69,15 @@ static const struct snd_soc_dapm_route wm8728_intercon[] = {
{"VOUTR", NULL, "DAC"},
};
-static int wm8728_mute(struct snd_soc_dai *dai, int mute)
+static int wm8728_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL);
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8728_DACCTL);
if (mute)
- snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1);
+ snd_soc_component_write(component, WM8728_DACCTL, mute_reg | 1);
else
- snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1);
+ snd_soc_component_write(component, WM8728_DACCTL, mute_reg & ~1);
return 0;
}
@@ -89,8 +86,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ u16 dac = snd_soc_component_read(component, WM8728_DACCTL);
dac &= ~0x18;
@@ -107,7 +104,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, WM8728_DACCTL, dac);
+ snd_soc_component_write(component, WM8728_DACCTL, dac);
return 0;
}
@@ -115,8 +112,8 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ u16 iface = snd_soc_component_read(component, WM8728_IFCTL);
/* Currently only I2S is supported by the driver, though the
* hardware is more flexible.
@@ -131,7 +128,7 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* The hardware only support full slave mode */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -156,24 +153,25 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8728_IFCTL, iface);
+ snd_soc_component_write(component, WM8728_IFCTL, iface);
return 0;
}
-static int wm8728_set_bias_level(struct snd_soc_codec *codec,
+static int wm8728_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8728_priv *wm8728 = snd_soc_codec_get_drvdata(codec);
+ struct wm8728_priv *wm8728 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u16 reg;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Power everything up... */
- reg = snd_soc_read(codec, WM8728_DACCTL);
- snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
+ reg = snd_soc_component_read(component, WM8728_DACCTL);
+ snd_soc_component_write(component, WM8728_DACCTL, reg & ~0x4);
/* ..then sync in the register cache. */
regcache_sync(wm8728->regmap);
@@ -181,8 +179,8 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- reg = snd_soc_read(codec, WM8728_DACCTL);
- snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
+ reg = snd_soc_component_read(component, WM8728_DACCTL);
+ snd_soc_component_write(component, WM8728_DACCTL, reg | 0x4);
break;
}
return 0;
@@ -195,8 +193,9 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8728_dai_ops = {
.hw_params = wm8728_hw_params,
- .digital_mute = wm8728_mute,
+ .mute_stream = wm8728_mute,
.set_fmt = wm8728_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8728_dai = {
@@ -211,16 +210,18 @@ static struct snd_soc_dai_driver wm8728_dai = {
.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 snd_soc_component_driver soc_component_dev_wm8728 = {
+ .set_bias_level = wm8728_set_bias_level,
+ .controls = wm8728_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8728_snd_controls),
+ .dapm_widgets = wm8728_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
+ .dapm_routes = wm8728_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8728_of_match[] = {
@@ -236,7 +237,7 @@ static const struct regmap_config wm8728_regmap = {
.reg_defaults = wm8728_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#if defined(CONFIG_SPI_MASTER)
@@ -256,32 +257,23 @@ static int wm8728_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8728);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8728, &wm8728_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.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)
+static int wm8728_i2c_probe(struct i2c_client *i2c)
{
struct wm8728_priv *wm8728;
int ret;
@@ -297,20 +289,14 @@ static int wm8728_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8728);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8728, &wm8728_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8728" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
@@ -320,8 +306,7 @@ static struct i2c_driver wm8728_i2c_driver = {
.name = "wm8728",
.of_match_table = wm8728_of_match,
},
- .probe = wm8728_i2c_probe,
- .remove = wm8728_i2c_remove,
+ .probe = wm8728_i2c_probe,
.id_table = wm8728_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8728.h b/sound/soc/codecs/wm8728.h
index 8aea362ffd47..d926db5e4f80 100644
--- a/sound/soc/codecs/wm8728.h
+++ b/sound/soc/codecs/wm8728.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c
new file mode 100644
index 000000000000..1254e583af51
--- /dev/null
+++ b/sound/soc/codecs/wm8731-i2c.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8731-i2c.c -- WM8731 ALSA SoC Audio driver I2C code
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include "wm8731.h"
+
+
+static const struct of_device_id wm8731_of_match[] = {
+ { .compatible = "wlf,wm8731", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static int wm8731_i2c_probe(struct i2c_client *i2c)
+{
+ struct wm8731_priv *wm8731;
+ int ret;
+
+ wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
+ GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8731);
+
+ wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
+ if (IS_ERR(wm8731->regmap)) {
+ ret = PTR_ERR(wm8731->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return wm8731_init(&i2c->dev, wm8731);
+}
+
+static const struct i2c_device_id wm8731_i2c_id[] = {
+ { "wm8731" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
+
+static struct i2c_driver wm8731_i2c_driver = {
+ .driver = {
+ .name = "wm8731",
+ .of_match_table = wm8731_of_match,
+ },
+ .probe = wm8731_i2c_probe,
+ .id_table = wm8731_i2c_id,
+};
+
+module_i2c_driver(wm8731_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver - I2C");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c
new file mode 100644
index 000000000000..c02086afa7fb
--- /dev/null
+++ b/sound/soc/codecs/wm8731-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8731.c -- WM8731 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include "wm8731.h"
+
+static const struct of_device_id wm8731_of_match[] = {
+ { .compatible = "wlf,wm8731", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static int wm8731_spi_probe(struct spi_device *spi)
+{
+ struct wm8731_priv *wm8731;
+ int ret;
+
+ wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, wm8731);
+
+ wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
+ if (IS_ERR(wm8731->regmap)) {
+ ret = PTR_ERR(wm8731->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return wm8731_init(&spi->dev, wm8731);
+}
+
+static struct spi_driver wm8731_spi_driver = {
+ .driver = {
+ .name = "wm8731",
+ .of_match_table = wm8731_of_match,
+ },
+ .probe = wm8731_spi_probe,
+};
+
+module_spi_driver(wm8731_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver - SPI");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 4bcf5f8ece50..a03bbde5d852 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8731.c -- WM8731 ALSA SoC Audio driver
*
@@ -7,10 +8,6 @@
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.c by Liam Girdwood
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -18,13 +15,9 @@
#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>
@@ -35,7 +28,6 @@
#include "wm8731.h"
-#define WM8731_NUM_SUPPLIES 4
static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
"AVDD",
"HPVDD",
@@ -43,21 +35,6 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
"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
*/
@@ -88,9 +65,9 @@ static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum,
static int wm8731_deemph[] = { 0, 32000, 44100, 48000 };
-static int wm8731_set_deemph(struct snd_soc_codec *codec)
+static int wm8731_set_deemph(struct snd_soc_component *component)
{
- struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
int val, i, best;
/* If we're using deemphasis select the nearest available sample
@@ -110,17 +87,17 @@ static int wm8731_set_deemph(struct snd_soc_codec *codec)
val = 0;
}
- dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n",
+ dev_dbg(component->dev, "Set deemphasis %d (%dHz)\n",
best, wm8731_deemph[best]);
- return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val);
+ return snd_soc_component_update_bits(component, WM8731_APDIGI, 0x6, val);
}
static int wm8731_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8731->deemph;
@@ -130,8 +107,8 @@ static int wm8731_get_deemph(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
int ret = 0;
@@ -142,7 +119,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
if (wm8731->deemph != deemph) {
wm8731->deemph = deemph;
- wm8731_set_deemph(codec);
+ wm8731_set_deemph(component);
ret = 1;
}
@@ -214,8 +191,8 @@ 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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
return wm8731->sysclk_type == WM8731_SYSCLK_XTAL;
}
@@ -337,16 +314,16 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8731_IFACE) & 0xfff3;
int i = get_coeff(wm8731->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
wm8731->playback_fs = params_rate(params);
- snd_soc_write(codec, WM8731_SRATE, srate);
+ snd_soc_component_write(component, WM8731_SRATE, srate);
/* bit size */
switch (params_width(params)) {
@@ -358,32 +335,35 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
case 24:
iface |= 0x0008;
break;
+ case 32:
+ iface |= 0x000c;
+ break;
}
- wm8731_set_deemph(codec);
+ wm8731_set_deemph(component);
- snd_soc_write(codec, WM8731_IFACE, iface);
+ snd_soc_component_write(component, WM8731_IFACE, iface);
return 0;
}
-static int wm8731_mute(struct snd_soc_dai *dai, int mute)
+static int wm8731_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8731_APDIGI) & 0xfff7;
if (mute)
- snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8);
+ snd_soc_component_write(component, WM8731_APDIGI, mute_reg | 0x8);
else
- snd_soc_write(codec, WM8731_APDIGI, mute_reg);
+ snd_soc_component_write(component, WM8731_APDIGI, mute_reg);
return 0;
}
static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM8731_SYSCLK_XTAL:
@@ -426,15 +406,14 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -478,14 +457,15 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- snd_soc_write(codec, WM8731_IFACE, iface);
+ snd_soc_component_write(component, WM8731_IFACE, iface);
return 0;
}
-static int wm8731_set_bias_level(struct snd_soc_codec *codec,
+static int wm8731_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
u16 reg;
@@ -500,7 +480,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0)
@@ -510,13 +490,13 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
}
/* Clear PWROFF, gate CLKOUT, everything else as-is */
- reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f;
- snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
+ reg = snd_soc_component_read(component, WM8731_PWR) & 0xff7f;
+ snd_soc_component_write(component, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
if (wm8731->mclk)
clk_disable_unprepare(wm8731->mclk);
- snd_soc_write(codec, WM8731_PWR, 0xffff);
+ snd_soc_component_write(component, WM8731_PWR, 0xffff);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
regcache_mark_dirty(wm8731->regmap);
@@ -528,7 +508,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
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);
+ struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(dai->component);
if (wm8731->constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -541,14 +521,15 @@ static int wm8731_startup(struct snd_pcm_substream *substream,
#define WM8731_RATES SNDRV_PCM_RATE_8000_96000
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
- SNDRV_PCM_FMTBIT_S24_LE)
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops wm8731_dai_ops = {
.startup = wm8731_startup,
.hw_params = wm8731_hw_params,
- .digital_mute = wm8731_mute,
+ .mute_stream = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8731_dai = {
@@ -566,14 +547,41 @@ static struct snd_soc_dai_driver wm8731_dai = {
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
.ops = &wm8731_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_wm8731 = {
+ .set_bias_level = wm8731_set_bias_level,
+ .controls = wm8731_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8731_snd_controls),
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = wm8731_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int wm8731_request_supplies(struct device *dev,
- struct wm8731_priv *wm8731)
+int wm8731_init(struct device *dev, struct wm8731_priv *wm8731)
{
int ret = 0, i;
+ wm8731->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(wm8731->mclk)) {
+ ret = PTR_ERR(wm8731->mclk);
+ if (ret == -ENOENT) {
+ wm8731->mclk = NULL;
+ dev_warn(dev, "Assuming static MCLK\n");
+ } else {
+ dev_err(dev, "Failed to get MCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ mutex_init(&wm8731->lock);
+
for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
wm8731->supplies[i].supply = wm8731_supply_names[i];
@@ -591,13 +599,6 @@ static int wm8731_request_supplies(struct device *dev,
return ret;
}
- return 0;
-}
-
-static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731)
-{
- int ret = 0;
-
ret = wm8731_reset(wm8731->regmap);
if (ret < 0) {
dev_err(dev, "Failed to issue reset: %d\n", ret);
@@ -618,222 +619,35 @@ static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731)
regcache_mark_dirty(wm8731->regmap);
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_dev_wm8731, &wm8731_dai, 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_regulator_enable;
+ }
+
+ return 0;
+
err_regulator_enable:
/* Regulators will be enabled by bias management */
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
return ret;
}
+EXPORT_SYMBOL_GPL(wm8731_init);
-static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
- .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 = {
+const struct regmap_config wm8731_regmap = {
.reg_bits = 7,
.val_bits = 9,
.max_register = WM8731_RESET,
.volatile_reg = wm8731_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.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);
-
- spi_set_drvdata(spi, wm8731);
-
- ret = wm8731_request_supplies(&spi->dev, wm8731);
- if (ret != 0)
- return ret;
-
- 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;
- }
-
- ret = wm8731_hw_init(&spi->dev, wm8731);
- if (ret != 0)
- return ret;
-
- 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",
- .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);
-
- i2c_set_clientdata(i2c, wm8731);
-
- ret = wm8731_request_supplies(&i2c->dev, wm8731);
- if (ret != 0)
- return ret;
-
- 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;
- }
-
- ret = wm8731_hw_init(&i2c->dev, wm8731);
- if (ret != 0)
- return ret;
-
- 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",
- .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);
+EXPORT_SYMBOL_GPL(wm8731_regmap);
MODULE_DESCRIPTION("ASoC WM8731 driver");
MODULE_AUTHOR("Richard Purdie");
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index e9c0c76ab73b..8d5a7a9ca6b2 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* wm8731.h -- WM8731 Soc Audio driver
*
@@ -6,15 +7,18 @@
* 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
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+struct clk;
+struct snd_pcm_hw_constraint_list;
+
/* WM8731 register space */
#define WM8731_LINVOL 0x00
@@ -31,9 +35,29 @@
#define WM8731_CACHEREGNUM 10
+#define WM8731_SYSCLK_MCLK 0
#define WM8731_SYSCLK_XTAL 1
-#define WM8731_SYSCLK_MCLK 2
#define WM8731_DAI 0
+#define WM8731_NUM_SUPPLIES 4
+
+/* codec private data */
+struct wm8731_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+ const struct snd_pcm_hw_constraint_list *constraints;
+ unsigned int sysclk;
+ int sysclk_type;
+ int playback_fs;
+ bool deemph;
+
+ struct mutex lock;
+};
+
+extern const struct regmap_config wm8731_regmap;
+
+int wm8731_init(struct device *dev, struct wm8731_priv *wm8731);
+
#endif
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index e7807601e675..fee8a37ed1df 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -1,15 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -20,7 +18,6 @@
#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>
@@ -74,9 +71,9 @@ static bool wm8737_volatile(struct device *dev, unsigned int reg)
}
}
-static int wm8737_reset(struct snd_soc_codec *codec)
+static int wm8737_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, WM8737_RESET, 0);
+ return snd_soc_component_write(component, WM8737_RESET, 0);
}
static const DECLARE_TLV_DB_RANGE(micboost_tlv,
@@ -170,7 +167,7 @@ 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_ENUM("3D High Cut-off", high_3d),
SOC_SINGLE_TLV("3D ADC Volume", WM8737_3D_ENHANCE, 7, 1, 1, adc_tlv),
SOC_SINGLE("Noise Gate Switch", WM8737_NOISE_GATE, 0, 1, 0),
@@ -328,8 +325,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component);
int i;
u16 clocking = 0;
u16 af = 0;
@@ -348,7 +345,7 @@ static int wm8737_hw_params(struct snd_pcm_substream *substream,
}
if (i == ARRAY_SIZE(coeff_div)) {
- dev_err(codec->dev, "%dHz MCLK can't support %dHz\n",
+ dev_err(component->dev, "%dHz MCLK can't support %dHz\n",
wm8737->mclk, params_rate(params));
return -EINVAL;
}
@@ -371,8 +368,8 @@ static int wm8737_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT, WM8737_WL_MASK, af);
- snd_soc_update_bits(codec, WM8737_CLOCKING,
+ snd_soc_component_update_bits(component, WM8737_AUDIO_FORMAT, WM8737_WL_MASK, af);
+ snd_soc_component_update_bits(component, WM8737_CLOCKING,
WM8737_USB_MODE | WM8737_CLKDIV2 | WM8737_SR_MASK,
clocking);
@@ -382,8 +379,8 @@ static int wm8737_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -394,7 +391,7 @@ static int wm8737_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
}
- dev_err(codec->dev, "MCLK rate %dHz not supported\n", freq);
+ dev_err(component->dev, "MCLK rate %dHz not supported\n", freq);
return -EINVAL;
}
@@ -403,14 +400,14 @@ static int wm8737_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 af = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
af |= WM8737_MS;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -445,16 +442,17 @@ static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT,
+ snd_soc_component_update_bits(component, WM8737_AUDIO_FORMAT,
WM8737_FORMAT_MASK | WM8737_LRP | WM8737_MS, af);
return 0;
}
-static int wm8737_set_bias_level(struct snd_soc_codec *codec,
+static int wm8737_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
+ struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -463,16 +461,16 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID at 2*75k */
- snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
+ snd_soc_component_update_bits(component, WM8737_MISC_BIAS_CONTROL,
WM8737_VMIDSEL_MASK, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies),
wm8737->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
@@ -481,12 +479,12 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
regcache_sync(wm8737->regmap);
/* Fast VMID ramp at 2*2.5k */
- snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
+ snd_soc_component_update_bits(component, WM8737_MISC_BIAS_CONTROL,
WM8737_VMIDSEL_MASK,
2 << WM8737_VMIDSEL_SHIFT);
/* Bring VMID up */
- snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT,
+ snd_soc_component_update_bits(component, WM8737_POWER_MANAGEMENT,
WM8737_VMID_MASK |
WM8737_VREF_MASK,
WM8737_VMID_MASK |
@@ -496,14 +494,14 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
}
/* VMID at 2*300k */
- snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
+ snd_soc_component_update_bits(component, WM8737_MISC_BIAS_CONTROL,
WM8737_VMIDSEL_MASK,
1 << WM8737_VMIDSEL_SHIFT);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT,
+ snd_soc_component_update_bits(component, WM8737_POWER_MANAGEMENT,
WM8737_VMID_MASK | WM8737_VREF_MASK, 0);
regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies),
@@ -537,30 +535,31 @@ static struct snd_soc_dai_driver wm8737_dai = {
.ops = &wm8737_dai_ops,
};
-static int wm8737_probe(struct snd_soc_codec *codec)
+static int wm8737_probe(struct snd_soc_component *component)
{
- struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
+ struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
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);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
goto err_get;
}
- ret = wm8737_reset(codec);
+ ret = wm8737_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
+ dev_err(component->dev, "Failed to issue reset\n");
goto err_enable;
}
- snd_soc_update_bits(codec, WM8737_LEFT_PGA_VOLUME, WM8737_LVU,
+ snd_soc_component_update_bits(component, WM8737_LEFT_PGA_VOLUME, WM8737_LVU,
WM8737_LVU);
- snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU,
+ snd_soc_component_update_bits(component, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU,
WM8737_RVU);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
@@ -573,17 +572,19 @@ 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 snd_soc_component_driver soc_component_dev_wm8737 = {
+ .probe = wm8737_probe,
+ .set_bias_level = wm8737_set_bias_level,
+ .controls = wm8737_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8737_snd_controls),
+ .dapm_widgets = wm8737_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8737_of_match[] = {
@@ -600,14 +601,13 @@ static const struct regmap_config wm8737_regmap = {
.reg_defaults = wm8737_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8737_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8737_volatile,
};
#if IS_ENABLED(CONFIG_I2C)
-static int wm8737_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8737_i2c_probe(struct i2c_client *i2c)
{
struct wm8737_priv *wm8737;
int ret, i;
@@ -633,22 +633,15 @@ static int wm8737_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8737);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8737, &wm8737_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8737" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id);
@@ -658,8 +651,7 @@ static struct i2c_driver wm8737_i2c_driver = {
.name = "wm8737",
.of_match_table = wm8737_of_match,
},
- .probe = wm8737_i2c_probe,
- .remove = wm8737_i2c_remove,
+ .probe = wm8737_i2c_probe,
.id_table = wm8737_i2c_id,
};
#endif
@@ -691,26 +683,18 @@ static int wm8737_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8737);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8737, &wm8737_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.of_match_table = wm8737_of_match,
},
.probe = wm8737_spi_probe,
- .remove = wm8737_spi_remove,
};
#endif /* CONFIG_SPI_MASTER */
diff --git a/sound/soc/codecs/wm8737.h b/sound/soc/codecs/wm8737.h
index 23d14c8ff6e7..b95b85e03ff8 100644
--- a/sound/soc/codecs/wm8737.h
+++ b/sound/soc/codecs/wm8737.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _WM8737_H
#define _WM8737_H
@@ -7,10 +8,6 @@
* 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.
*/
/*
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 36ef91fe0511..4dfbb33edb09 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -18,10 +14,10 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
+#include <linux/of.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>
@@ -37,8 +33,6 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
"DVDD",
};
-#define WM8741_NUM_RATES 6
-
/* codec private data */
struct wm8741_priv {
struct wm8741_platform_data pdata;
@@ -61,9 +55,9 @@ static const struct reg_default wm8741_reg_defaults[] = {
{ 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */
};
-static int wm8741_reset(struct snd_soc_codec *codec)
+static int wm8741_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, WM8741_RESET, 0);
+ return snd_soc_component_write(component, WM8741_RESET, 0);
}
static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
@@ -181,8 +175,8 @@ static const struct snd_pcm_hw_constraint_list constraints_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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
if (wm8741->sysclk)
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -196,16 +190,16 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
+ unsigned int iface, mode;
int i;
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
if (!wm8741->sysclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"No MCLK configured, call set_sysclk() on init or in hw_params\n");
return -EINVAL;
}
@@ -217,7 +211,7 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream,
}
if (i == wm8741->sysclk_constraints->count) {
- dev_err(codec->dev, "LRCLK %d unsupported with MCLK %d\n",
+ dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n",
params_rate(params), wm8741->sysclk);
return -EINVAL;
}
@@ -225,36 +219,49 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream,
/* bit size */
switch (params_width(params)) {
case 16:
+ iface = 0x0;
break;
case 20:
- iface |= 0x0001;
+ iface = 0x1;
break;
case 24:
- iface |= 0x0002;
+ iface = 0x2;
break;
case 32:
- iface |= 0x0003;
+ iface = 0x3;
break;
default:
- dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d",
+ dev_dbg(component->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, rate param = %d",
+ /* oversampling rate */
+ if (params_rate(params) > 96000)
+ mode = 0x40;
+ else if (params_rate(params) > 48000)
+ mode = 0x20;
+ else
+ mode = 0x00;
+
+ dev_dbg(component->dev, "wm8741_hw_params: bit size param = %d, rate param = %d",
params_width(params), params_rate(params));
- snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
+ snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK,
+ iface);
+ snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_1, WM8741_OSR_MASK,
+ mode);
+
return 0;
}
static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
+ dev_dbg(component->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
switch (freq) {
case 0:
@@ -296,12 +303,12 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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;
+ struct snd_soc_component *component = codec_dai->component;
+ unsigned int iface;
/* check master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -310,18 +317,19 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- iface |= 0x0008;
+ iface = 0x08;
break;
case SND_SOC_DAIFMT_RIGHT_J:
+ iface = 0x00;
break;
case SND_SOC_DAIFMT_LEFT_J:
- iface |= 0x0004;
+ iface = 0x04;
break;
case SND_SOC_DAIFMT_DSP_A:
- iface |= 0x000C;
+ iface = 0x0C;
break;
case SND_SOC_DAIFMT_DSP_B:
- iface |= 0x001C;
+ iface = 0x1C;
break;
default:
return -EINVAL;
@@ -331,25 +339,37 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
- case SND_SOC_DAIFMT_IB_IF:
- iface |= 0x0010;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x10;
break;
case SND_SOC_DAIFMT_IB_NF:
- iface |= 0x0020;
+ iface |= 0x20;
break;
- case SND_SOC_DAIFMT_NB_IF:
- iface |= 0x0030;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x30;
break;
default:
return -EINVAL;
}
- dev_dbg(codec->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n",
+ dev_dbg(component->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK,
((fmt & SND_SOC_DAIFMT_INV_MASK)));
- snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
+ snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL,
+ WM8741_BCP_MASK | WM8741_LRP_MASK | WM8741_FMT_MASK,
+ iface);
+
+ return 0;
+}
+
+static int wm8741_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
+{
+ struct snd_soc_component *component = codec_dai->component;
+
+ snd_soc_component_update_bits(component, WM8741_VOLUME_CONTROL,
+ WM8741_SOFT_MASK, !!mute << WM8741_SOFT_SHIFT);
return 0;
}
@@ -366,6 +386,8 @@ static const struct snd_soc_dai_ops wm8741_dai_ops = {
.hw_params = wm8741_hw_params,
.set_sysclk = wm8741_set_dai_sysclk,
.set_fmt = wm8741_set_dai_fmt,
+ .mute_stream = wm8741_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8741_dai = {
@@ -381,18 +403,18 @@ static struct snd_soc_dai_driver wm8741_dai = {
};
#ifdef CONFIG_PM
-static int wm8741_resume(struct snd_soc_codec *codec)
+static int wm8741_resume(struct snd_soc_component *component)
{
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
#else
#define wm8741_resume NULL
#endif
-static int wm8741_configure(struct snd_soc_codec *codec)
+static int wm8741_configure(struct snd_soc_component *component)
{
- struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
/* Configure differential mode */
switch (wm8741->pdata.diff_mode) {
@@ -400,7 +422,7 @@ static int wm8741_configure(struct snd_soc_codec *codec)
case WM8741_DIFF_MODE_STEREO_REVERSED:
case WM8741_DIFF_MODE_MONO_LEFT:
case WM8741_DIFF_MODE_MONO_RIGHT:
- snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2,
+ snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_2,
WM8741_DIFF_MASK,
wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT);
break;
@@ -409,36 +431,36 @@ static int wm8741_configure(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU */
- snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+ snd_soc_component_update_bits(component, WM8741_DACLLSB_ATTENUATION,
WM8741_UPDATELL, WM8741_UPDATELL);
- snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+ snd_soc_component_update_bits(component, WM8741_DACLMSB_ATTENUATION,
WM8741_UPDATELM, WM8741_UPDATELM);
- snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+ snd_soc_component_update_bits(component, WM8741_DACRLSB_ATTENUATION,
WM8741_UPDATERL, WM8741_UPDATERL);
- snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
+ snd_soc_component_update_bits(component, WM8741_DACRMSB_ATTENUATION,
WM8741_UPDATERM, WM8741_UPDATERM);
return 0;
}
-static int wm8741_add_controls(struct snd_soc_codec *codec)
+static int wm8741_add_controls(struct snd_soc_component *component)
{
- struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
switch (wm8741->pdata.diff_mode) {
case WM8741_DIFF_MODE_STEREO:
case WM8741_DIFF_MODE_STEREO_REVERSED:
- snd_soc_add_codec_controls(codec,
+ snd_soc_add_component_controls(component,
wm8741_snd_controls_stereo,
ARRAY_SIZE(wm8741_snd_controls_stereo));
break;
case WM8741_DIFF_MODE_MONO_LEFT:
- snd_soc_add_codec_controls(codec,
+ snd_soc_add_component_controls(component,
wm8741_snd_controls_mono_left,
ARRAY_SIZE(wm8741_snd_controls_mono_left));
break;
case WM8741_DIFF_MODE_MONO_RIGHT:
- snd_soc_add_codec_controls(codec,
+ snd_soc_add_component_controls(component,
wm8741_snd_controls_mono_right,
ARRAY_SIZE(wm8741_snd_controls_mono_right));
break;
@@ -449,37 +471,37 @@ static int wm8741_add_controls(struct snd_soc_codec *codec)
return 0;
}
-static int wm8741_probe(struct snd_soc_codec *codec)
+static int wm8741_probe(struct snd_soc_component *component)
{
- struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
int ret = 0;
ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
wm8741->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
goto err_get;
}
- ret = wm8741_reset(codec);
+ ret = wm8741_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
+ dev_err(component->dev, "Failed to issue reset\n");
goto err_enable;
}
- ret = wm8741_configure(codec);
+ ret = wm8741_configure(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to change default settings\n");
+ dev_err(component->dev, "Failed to change default settings\n");
goto err_enable;
}
- ret = wm8741_add_controls(codec);
+ ret = wm8741_add_controls(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to add controls\n");
+ dev_err(component->dev, "Failed to add controls\n");
goto err_enable;
}
- dev_dbg(codec->dev, "Successful registration\n");
+ dev_dbg(component->dev, "Successful registration\n");
return ret;
err_enable:
@@ -488,24 +510,24 @@ err_get:
return ret;
}
-static int wm8741_remove(struct snd_soc_codec *codec)
+static void wm8741_remove(struct snd_soc_component *component)
{
- struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
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,
-
- .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 snd_soc_component_driver soc_component_dev_wm8741 = {
+ .probe = wm8741_probe,
+ .remove = wm8741_remove,
+ .resume = wm8741_resume,
+ .dapm_widgets = wm8741_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
+ .dapm_routes = wm8741_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8741_of_match[] = {
@@ -521,7 +543,7 @@ static const struct regmap_config wm8741_regmap = {
.reg_defaults = wm8741_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
@@ -542,8 +564,7 @@ static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
}
#if IS_ENABLED(CONFIG_I2C)
-static int wm8741_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8741_i2c_probe(struct i2c_client *i2c)
{
struct wm8741_priv *wm8741;
int ret, i;
@@ -578,20 +599,14 @@ static int wm8741_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8741);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8741, &wm8741_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8741" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
@@ -601,8 +616,7 @@ static struct i2c_driver wm8741_i2c_driver = {
.name = "wm8741",
.of_match_table = wm8741_of_match,
},
- .probe = wm8741_i2c_probe,
- .remove = wm8741_i2c_remove,
+ .probe = wm8741_i2c_probe,
.id_table = wm8741_i2c_id,
};
#endif
@@ -643,24 +657,17 @@ static int wm8741_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8741);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8741, &wm8741_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.of_match_table = wm8741_of_match,
},
.probe = wm8741_spi_probe,
- .remove = wm8741_spi_remove,
};
#endif /* CONFIG_SPI_MASTER */
diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h
index c8835f65f342..8158432f014f 100644
--- a/sound/soc/codecs/wm8741.h
+++ b/sound/soc/codecs/wm8741.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* wm8741.h -- WM8423 ASoC driver
*
@@ -6,10 +7,6 @@
* 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
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index bd9dcd2161bc..0e1d3ebb15c4 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8750.c -- WM8750 ALSA SoC audio driver
*
@@ -6,10 +7,6 @@
* 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>
@@ -18,10 +15,10 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/of.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>
@@ -86,7 +83,7 @@ struct wm8750_priv {
unsigned int sysclk;
};
-#define wm8750_reset(c) snd_soc_write(c, WM8750_RESET, 0)
+#define wm8750_reset(c) snd_soc_component_write(c, WM8750_RESET, 0)
/*
* WM8750 Controls
@@ -502,8 +499,8 @@ static inline int get_coeff(int mclk, int rate)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8750_priv *wm8750 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 11289600:
@@ -520,15 +517,15 @@ static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -571,7 +568,7 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8750_IFACE, iface);
+ snd_soc_component_write(component, WM8750_IFACE, iface);
return 0;
}
@@ -579,10 +576,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8750_priv *wm8750 = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8750_IFACE) & 0x1f3;
+ u16 srate = snd_soc_component_read(component, WM8750_SRATE) & 0x1c0;
int coeff = get_coeff(wm8750->sysclk, params_rate(params));
/* bit size */
@@ -601,54 +598,55 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, WM8750_IFACE, iface);
+ snd_soc_component_write(component, WM8750_IFACE, iface);
if (coeff >= 0)
- snd_soc_write(codec, WM8750_SRATE, srate |
+ snd_soc_component_write(component, WM8750_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
}
-static int wm8750_mute(struct snd_soc_dai *dai, int mute)
+static int wm8750_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8750_ADCDAC) & 0xfff7;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8750_ADCDAC) & 0xfff7;
if (mute)
- snd_soc_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
+ snd_soc_component_write(component, WM8750_ADCDAC, mute_reg | 0x8);
else
- snd_soc_write(codec, WM8750_ADCDAC, mute_reg);
+ snd_soc_component_write(component, WM8750_ADCDAC, mute_reg);
return 0;
}
-static int wm8750_set_bias_level(struct snd_soc_codec *codec,
+static int wm8750_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = snd_soc_read(codec, WM8750_PWR1) & 0xfe3e;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 pwr_reg = snd_soc_component_read(component, WM8750_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+ snd_soc_component_write(component, WM8750_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_cache_sync(component);
/* Set VMID to 5k */
- snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+ snd_soc_component_write(component, WM8750_PWR1, pwr_reg | 0x01c1);
/* ...and ramp */
msleep(1000);
}
/* mute dac and set vmid to 500k, enable VREF */
- snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
+ snd_soc_component_write(component, WM8750_PWR1, pwr_reg | 0x0141);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, WM8750_PWR1, 0x0001);
+ snd_soc_component_write(component, WM8750_PWR1, 0x0001);
break;
}
return 0;
@@ -663,9 +661,10 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8750_dai_ops = {
.hw_params = wm8750_pcm_hw_params,
- .digital_mute = wm8750_mute,
+ .mute_stream = wm8750_mute,
.set_fmt = wm8750_set_dai_fmt,
.set_sysclk = wm8750_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8750_dai = {
@@ -685,40 +684,42 @@ static struct snd_soc_dai_driver wm8750_dai = {
.ops = &wm8750_dai_ops,
};
-static int wm8750_probe(struct snd_soc_codec *codec)
+static int wm8750_probe(struct snd_soc_component *component)
{
int ret;
- ret = wm8750_reset(codec);
+ ret = wm8750_reset(component);
if (ret < 0) {
printk(KERN_ERR "wm8750: failed to reset: %d\n", ret);
return ret;
}
/* set the update bits */
- snd_soc_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);
+ snd_soc_component_update_bits(component, WM8750_LDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_RDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_LOUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_ROUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_LOUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_ROUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_LINVOL, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8750_RINVOL, 0x0100, 0x0100);
return ret;
}
-static 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 snd_soc_component_driver soc_component_dev_wm8750 = {
+ .probe = wm8750_probe,
+ .set_bias_level = wm8750_set_bias_level,
+ .controls = wm8750_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8750_snd_controls),
+ .dapm_widgets = wm8750_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
+ .dapm_routes = wm8750_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8750_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8750_of_match[] = {
@@ -735,7 +736,7 @@ static const struct regmap_config wm8750_regmap = {
.reg_defaults = wm8750_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8750_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#if defined(CONFIG_SPI_MASTER)
@@ -756,17 +757,11 @@ static int wm8750_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8750);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8750, &wm8750_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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 },
@@ -781,13 +776,11 @@ static struct spi_driver wm8750_spi_driver = {
},
.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)
+static int wm8750_i2c_probe(struct i2c_client *i2c)
{
struct wm8750_priv *wm8750;
struct regmap *regmap;
@@ -804,20 +797,14 @@ static int wm8750_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8750, &wm8750_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8750" },
+ { "wm8987" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
@@ -827,8 +814,7 @@ static struct i2c_driver wm8750_i2c_driver = {
.name = "wm8750",
.of_match_table = wm8750_of_match,
},
- .probe = wm8750_i2c_probe,
- .remove = wm8750_i2c_remove,
+ .probe = wm8750_i2c_probe,
.id_table = wm8750_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
index 121427c047fb..325f58aa7316 100644
--- a/sound/soc/codecs/wm8750.h
+++ b/sound/soc/codecs/wm8750.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 61299ca372ff..a532a95e8048 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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.
@@ -28,17 +24,15 @@
*
* The driver can now fast switch between the DAI configurations via a
* an alsa kcontrol. This allows the PCM to remain open.
- *
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
@@ -56,9 +50,9 @@ 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,
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_component *component,
unsigned int fmt);
-static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_voice_write_dai_fmt(struct snd_soc_component *component,
unsigned int fmt);
/*
@@ -151,7 +145,7 @@ struct wm8753_priv {
struct delayed_work charge_work;
};
-#define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0)
+#define wm8753_reset(c) snd_soc_component_write(c, WM8753_RESET, 0)
/*
* WM8753 Controls
@@ -230,39 +224,39 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = wm8753->dai_func;
+ ucontrol->value.enumerated.item[0] = wm8753->dai_func;
return 0;
}
static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
u16 ioctl;
- if (wm8753->dai_func == ucontrol->value.integer.value[0])
+ if (wm8753->dai_func == ucontrol->value.enumerated.item[0])
return 0;
- if (snd_soc_codec_is_active(codec))
+ if (snd_soc_component_active(component))
return -EBUSY;
- ioctl = snd_soc_read(codec, WM8753_IOCTL);
+ ioctl = snd_soc_component_read(component, WM8753_IOCTL);
- wm8753->dai_func = ucontrol->value.integer.value[0];
+ wm8753->dai_func = ucontrol->value.enumerated.item[0];
if (((ioctl >> 2) & 0x3) == wm8753->dai_func)
return 1;
ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2);
- snd_soc_write(codec, WM8753_IOCTL, ioctl);
+ snd_soc_component_write(component, WM8753_IOCTL, ioctl);
- wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt);
- wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt);
+ wm8753_hifi_write_dai_fmt(component, wm8753->hifi_fmt);
+ wm8753_voice_write_dai_fmt(component, wm8753->voice_fmt);
return 1;
}
@@ -281,6 +275,7 @@ static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
static const struct snd_kcontrol_new wm8753_snd_controls[] = {
+SOC_SINGLE("Hi-Fi DAC Left/Right channel Swap", WM8753_HIFI, 5, 1, 0),
SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),
SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0,
@@ -486,7 +481,7 @@ 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_MIXER("Out3 Left + Right", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("OUT3"),
@@ -745,7 +740,7 @@ static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
{
u16 reg, enable;
int offset;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
if (pll_id < WM8753_PLL1 || pll_id > WM8753_PLL2)
return -ENODEV;
@@ -753,17 +748,17 @@ static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (pll_id == WM8753_PLL1) {
offset = 0;
enable = 0x10;
- reg = snd_soc_read(codec, WM8753_CLOCK) & 0xffef;
+ reg = snd_soc_component_read(component, WM8753_CLOCK) & 0xffef;
} else {
offset = 4;
enable = 0x8;
- reg = snd_soc_read(codec, WM8753_CLOCK) & 0xfff7;
+ reg = snd_soc_component_read(component, 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);
+ snd_soc_component_write(component, WM8753_PLL1CTL1 + offset, 0x0026);
+ snd_soc_component_write(component, WM8753_CLOCK, reg);
return 0;
} else {
u16 value = 0;
@@ -774,20 +769,20 @@ static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* 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);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8753_PLL1CTL3 + offset, value);
/* bits 8:0 = PLL_K[8:0] */
value = pll_div.k & 0x0001ff;
- snd_soc_write(codec, WM8753_PLL1CTL4 + offset, value);
+ snd_soc_component_write(component, WM8753_PLL1CTL4 + offset, value);
/* set PLL as input and enable */
- snd_soc_write(codec, WM8753_PLL1CTL1 + offset, 0x0027 |
+ snd_soc_component_write(component, WM8753_PLL1CTL1 + offset, 0x0027 |
(pll_div.div2 << 3));
- snd_soc_write(codec, WM8753_CLOCK, reg | enable);
+ snd_soc_component_write(component, WM8753_CLOCK, reg | enable);
}
return 0;
}
@@ -866,8 +861,8 @@ static int get_coeff(int mclk, int rate)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 11289600:
@@ -890,10 +885,10 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
/*
* Set's ADC and Voice DAC format.
*/
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
- u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec;
+ u16 voice = snd_soc_component_read(component, WM8753_PCM) & 0x01ec;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -915,7 +910,7 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_write(codec, WM8753_PCM, voice);
+ snd_soc_component_write(component, WM8753_PCM, voice);
return 0;
}
@@ -926,10 +921,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
+ u16 voice = snd_soc_component_read(component, WM8753_PCM) & 0x01f3;
+ u16 srate = snd_soc_component_read(component, WM8753_SRATE1) & 0x017f;
/* bit size */
switch (params_width(params)) {
@@ -949,30 +944,31 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
/* sample rate */
if (params_rate(params) * 384 == wm8753->pcmclk)
srate |= 0x80;
- snd_soc_write(codec, WM8753_SRATE1, srate);
+ snd_soc_component_write(component, WM8753_SRATE1, srate);
- snd_soc_write(codec, WM8753_PCM, voice);
+ snd_soc_component_write(component, WM8753_PCM, voice);
return 0;
}
/*
* Set's PCM dai fmt and BCLK.
*/
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
u16 voice, ioctl;
- voice = snd_soc_read(codec, WM8753_PCM) & 0x011f;
- ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x015d;
+ voice = snd_soc_component_read(component, WM8753_PCM) & 0x011f;
+ ioctl = snd_soc_component_read(component, WM8753_IOCTL) & 0x015d;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ioctl |= 0x2;
- case SND_SOC_DAIFMT_CBM_CFS:
+ fallthrough;
+ case SND_SOC_DAIFMT_CBP_CFC:
voice |= 0x0040;
break;
default:
@@ -1018,29 +1014,29 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_write(codec, WM8753_PCM, voice);
- snd_soc_write(codec, WM8753_IOCTL, ioctl);
+ snd_soc_component_write(component, WM8753_PCM, voice);
+ snd_soc_component_write(component, WM8753_IOCTL, ioctl);
return 0;
}
static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
switch (div_id) {
case WM8753_PCMDIV:
- reg = snd_soc_read(codec, WM8753_CLOCK) & 0x003f;
- snd_soc_write(codec, WM8753_CLOCK, reg | div);
+ reg = snd_soc_component_read(component, WM8753_CLOCK) & 0x003f;
+ snd_soc_component_write(component, WM8753_CLOCK, reg | div);
break;
case WM8753_BCLKDIV:
- reg = snd_soc_read(codec, WM8753_SRATE2) & 0x01c7;
- snd_soc_write(codec, WM8753_SRATE2, reg | div);
+ reg = snd_soc_component_read(component, WM8753_SRATE2) & 0x01c7;
+ snd_soc_component_write(component, WM8753_SRATE2, reg | div);
break;
case WM8753_VXCLKDIV:
- reg = snd_soc_read(codec, WM8753_SRATE2) & 0x003f;
- snd_soc_write(codec, WM8753_SRATE2, reg | div);
+ reg = snd_soc_component_read(component, WM8753_SRATE2) & 0x003f;
+ snd_soc_component_write(component, WM8753_SRATE2, reg | div);
break;
default:
return -EINVAL;
@@ -1051,10 +1047,10 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
/*
* Set's HiFi DAC format.
*/
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
- u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0;
+ u16 hifi = snd_soc_component_read(component, WM8753_HIFI) & 0x01e0;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -1076,28 +1072,29 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_write(codec, WM8753_HIFI, hifi);
+ snd_soc_component_write(component, WM8753_HIFI, hifi);
return 0;
}
/*
* Set's I2S DAI format.
*/
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
u16 ioctl, hifi;
- hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f;
- ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x00ae;
+ hifi = snd_soc_component_read(component, WM8753_HIFI) & 0x013f;
+ ioctl = snd_soc_component_read(component, WM8753_IOCTL) & 0x00ae;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ioctl |= 0x1;
- case SND_SOC_DAIFMT_CBM_CFS:
+ fallthrough;
+ case SND_SOC_DAIFMT_CBP_CFC:
hifi |= 0x0040;
break;
default:
@@ -1143,8 +1140,8 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_write(codec, WM8753_HIFI, hifi);
- snd_soc_write(codec, WM8753_IOCTL, ioctl);
+ snd_soc_component_write(component, WM8753_HIFI, hifi);
+ snd_soc_component_write(component, WM8753_IOCTL, ioctl);
return 0;
}
@@ -1155,10 +1152,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
+ u16 srate = snd_soc_component_read(component, WM8753_SRATE1) & 0x01c0;
+ u16 hifi = snd_soc_component_read(component, WM8753_HIFI) & 0x01f3;
int coeff;
/* is digital filter coefficient valid ? */
@@ -1167,7 +1164,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
printk(KERN_ERR "wm8753 invalid MCLK or rate\n");
return coeff;
}
- snd_soc_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) |
+ snd_soc_component_write(component, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) |
coeff_div[coeff].usb);
/* bit size */
@@ -1185,70 +1182,70 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8753_HIFI, hifi);
+ snd_soc_component_write(component, WM8753_HIFI, hifi);
return 0;
}
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
u16 clock;
/* set clk source as pcmclk */
- clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
- snd_soc_write(codec, WM8753_CLOCK, clock);
+ clock = snd_soc_component_read(component, WM8753_CLOCK) & 0xfffb;
+ snd_soc_component_write(component, WM8753_CLOCK, clock);
- return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
+ return wm8753_vdac_adc_set_dai_fmt(component, fmt);
}
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
- return wm8753_hdac_set_dai_fmt(codec, fmt);
+ return wm8753_hdac_set_dai_fmt(component, fmt);
}
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
u16 clock;
/* set clk source as pcmclk */
- clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
- snd_soc_write(codec, WM8753_CLOCK, clock);
+ clock = snd_soc_component_read(component, WM8753_CLOCK) & 0xfffb;
+ snd_soc_component_write(component, WM8753_CLOCK, clock);
- return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
+ return wm8753_vdac_adc_set_dai_fmt(component, fmt);
}
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
u16 clock;
/* set clk source as mclk */
- clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
- snd_soc_write(codec, WM8753_CLOCK, clock | 0x4);
+ clock = snd_soc_component_read(component, WM8753_CLOCK) & 0xfffb;
+ snd_soc_component_write(component, WM8753_CLOCK, clock | 0x4);
- if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0)
+ if (wm8753_hdac_set_dai_fmt(component, fmt) < 0)
return -EINVAL;
- return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
+ return wm8753_vdac_adc_set_dai_fmt(component, fmt);
}
-static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
- struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (wm8753->dai_func) {
case 0:
- ret = wm8753_mode1h_set_dai_fmt(codec, fmt);
+ ret = wm8753_mode1h_set_dai_fmt(component, fmt);
break;
case 1:
- ret = wm8753_mode2_set_dai_fmt(codec, fmt);
+ ret = wm8753_mode2_set_dai_fmt(component, fmt);
break;
case 2:
case 3:
- ret = wm8753_mode3_4_set_dai_fmt(codec, fmt);
+ ret = wm8753_mode3_4_set_dai_fmt(component, fmt);
break;
default:
break;
@@ -1256,33 +1253,33 @@ static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
if (ret)
return ret;
- return wm8753_i2s_set_dai_fmt(codec, fmt);
+ return wm8753_i2s_set_dai_fmt(component, fmt);
}
static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
wm8753->hifi_fmt = fmt;
- return wm8753_hifi_write_dai_fmt(codec, fmt);
+ return wm8753_hifi_write_dai_fmt(component, fmt);
};
-static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+static int wm8753_voice_write_dai_fmt(struct snd_soc_component *component,
unsigned int fmt)
{
- struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
int ret = 0;
if (wm8753->dai_func != 0)
return 0;
- ret = wm8753_mode1v_set_dai_fmt(codec, fmt);
+ ret = wm8753_mode1v_set_dai_fmt(component, fmt);
if (ret)
return ret;
- ret = wm8753_pcm_set_dai_fmt(codec, fmt);
+ ret = wm8753_pcm_set_dai_fmt(component, fmt);
if (ret)
return ret;
@@ -1292,30 +1289,30 @@ static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
wm8753->voice_fmt = fmt;
- return wm8753_voice_write_dai_fmt(codec, fmt);
+ return wm8753_voice_write_dai_fmt(component, fmt);
};
-static int wm8753_mute(struct snd_soc_dai *dai, int mute)
+static int wm8753_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- 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);
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8753_DAC) & 0xfff7;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
/* the digital mute covers the HiFi and Voice DAC's on the WM8753.
* make sure we check if they are not both active when we mute */
if (mute && wm8753->dai_func == 1) {
- if (!snd_soc_codec_is_active(codec))
- snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8);
+ if (!snd_soc_component_active(component))
+ snd_soc_component_write(component, WM8753_DAC, mute_reg | 0x8);
} else {
if (mute)
- snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8);
+ snd_soc_component_write(component, WM8753_DAC, mute_reg | 0x8);
else
- snd_soc_write(codec, WM8753_DAC, mute_reg);
+ snd_soc_component_write(component, WM8753_DAC, mute_reg);
}
return 0;
@@ -1330,35 +1327,36 @@ static void wm8753_charge_work(struct work_struct *work)
regmap_update_bits(wm8753->regmap, WM8753_PWR1, 0x0180, 0x0100);
}
-static int wm8753_set_bias_level(struct snd_soc_codec *codec,
+static int wm8753_set_bias_level(struct snd_soc_component *component,
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;
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 pwr_reg = snd_soc_component_read(component, WM8753_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
+ snd_soc_component_write(component, WM8753_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_PREPARE:
/* Wait until fully charged */
flush_delayed_work(&wm8753->charge_work);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* set vmid to 5k for quick power up */
- snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
+ snd_soc_component_write(component, WM8753_PWR1, pwr_reg | 0x01c1);
schedule_delayed_work(&wm8753->charge_work,
msecs_to_jiffies(caps_charge));
} else {
/* mute dac and set vmid to 500k, enable VREF */
- snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
+ snd_soc_component_write(component, WM8753_PWR1, pwr_reg | 0x0141);
}
break;
case SND_SOC_BIAS_OFF:
cancel_delayed_work_sync(&wm8753->charge_work);
- snd_soc_write(codec, WM8753_PWR1, 0x0001);
+ snd_soc_component_write(component, WM8753_PWR1, 0x0001);
break;
}
return 0;
@@ -1385,20 +1383,22 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
*/
static const struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
.hw_params = wm8753_i2s_hw_params,
- .digital_mute = wm8753_mute,
+ .mute_stream = wm8753_mute,
.set_fmt = wm8753_hifi_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
.hw_params = wm8753_pcm_hw_params,
- .digital_mute = wm8753_mute,
+ .mute_stream = wm8753_mute,
.set_fmt = wm8753_voice_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8753_dai[] = {
@@ -1440,57 +1440,59 @@ static struct snd_soc_dai_driver wm8753_dai[] = {
},
};
-static int wm8753_resume(struct snd_soc_codec *codec)
+static int wm8753_resume(struct snd_soc_component *component)
{
- struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
regcache_sync(wm8753->regmap);
return 0;
}
-static int wm8753_probe(struct snd_soc_codec *codec)
+static int wm8753_probe(struct snd_soc_component *component)
{
- struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component);
int ret;
INIT_DELAYED_WORK(&wm8753->charge_work, wm8753_charge_work);
- ret = wm8753_reset(codec);
+ ret = wm8753_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
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);
+ snd_soc_component_update_bits(component, WM8753_LDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_RDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_LADC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_RADC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_LOUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_ROUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_LOUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_ROUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_LINVOL, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8753_RINVOL, 0x0100, 0x0100);
return 0;
}
-static 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 snd_soc_component_driver soc_component_dev_wm8753 = {
+ .probe = wm8753_probe,
+ .resume = wm8753_resume,
+ .set_bias_level = wm8753_set_bias_level,
+ .controls = wm8753_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8753_snd_controls),
+ .dapm_widgets = wm8753_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8753_dapm_widgets),
+ .dapm_routes = wm8753_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8753_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8753_of_match[] = {
@@ -1506,7 +1508,7 @@ static const struct regmap_config wm8753_regmap = {
.max_register = WM8753_ADCTL2,
.volatile_reg = wm8753_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8753_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8753_reg_defaults),
};
@@ -1532,7 +1534,7 @@ static int wm8753_spi_probe(struct spi_device *spi)
return ret;
}
- ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_wm8753,
+ ret = devm_snd_soc_register_component(&spi->dev, &soc_component_dev_wm8753,
wm8753_dai, ARRAY_SIZE(wm8753_dai));
if (ret != 0)
dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
@@ -1540,25 +1542,17 @@ static int wm8753_spi_probe(struct spi_device *spi)
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",
.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)
+static int wm8753_i2c_probe(struct i2c_client *i2c)
{
struct wm8753_priv *wm8753;
int ret;
@@ -1578,7 +1572,7 @@ static int wm8753_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8753,
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_wm8753,
wm8753_dai, ARRAY_SIZE(wm8753_dai));
if (ret != 0)
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
@@ -1586,14 +1580,8 @@ static int wm8753_i2c_probe(struct i2c_client *i2c,
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 },
+ { "wm8753" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
@@ -1603,8 +1591,7 @@ static struct i2c_driver wm8753_i2c_driver = {
.name = "wm8753",
.of_match_table = wm8753_of_match,
},
- .probe = wm8753_i2c_probe,
- .remove = wm8753_i2c_remove,
+ .probe = wm8753_i2c_probe,
.id_table = wm8753_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 94edac144bcb..5a60452e1c17 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
@@ -112,7 +107,4 @@
#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/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index df6178464b00..d382b476c89c 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -1,20 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/mod_devicetable.h>
#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>
@@ -84,7 +81,7 @@ 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;
+ struct snd_soc_component *component;
int sysclk;
};
@@ -308,14 +305,14 @@ static const struct snd_soc_dapm_route wm8770_intercon[] = {
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WM8770_OUTMUX1, 0x180, 0);
+ snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, WM8770_OUTMUX1, 0x180, 0x180);
+ snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0x180);
break;
}
@@ -325,37 +322,37 @@ static int vout12supply_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WM8770_OUTMUX2, 0x180, 0);
+ snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, WM8770_OUTMUX2, 0x180, 0x180);
+ snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0x180);
break;
}
return 0;
}
-static int wm8770_reset(struct snd_soc_codec *codec)
+static int wm8770_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, WM8770_RESET, 0);
+ return snd_soc_component_write(component, WM8770_RESET, 0);
}
static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
int iface, master;
- codec = dai->codec;
+ component = dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master = 0x100;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
master = 0;
break;
default:
@@ -392,8 +389,8 @@ static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8770_IFACECTRL, 0xf, iface);
- snd_soc_update_bits(codec, WM8770_MSTRCTRL, 0x100, master);
+ snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0xf, iface);
+ snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x100, master);
return 0;
}
@@ -411,15 +408,15 @@ 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 snd_soc_component *component;
struct wm8770_priv *wm8770;
int i;
int iface;
int shift;
int ratio;
- codec = dai->codec;
- wm8770 = snd_soc_codec_get_drvdata(codec);
+ component = dai->component;
+ wm8770 = snd_soc_component_get_drvdata(component);
iface = 0;
switch (params_width(params)) {
@@ -450,7 +447,7 @@ static int wm8770_hw_params(struct snd_pcm_substream *substream,
}
/* Only need to set MCLK/LRCLK ratio if we're master */
- if (snd_soc_read(codec, WM8770_MSTRCTRL) & 0x100) {
+ if (snd_soc_component_read(component, WM8770_MSTRCTRL) & 0x100) {
for (; i < ARRAY_SIZE(mclk_ratios); ++i) {
ratio = wm8770->sysclk / params_rate(params);
if (ratio == mclk_ratios[i])
@@ -458,51 +455,52 @@ static int wm8770_hw_params(struct snd_pcm_substream *substream,
}
if (i == ARRAY_SIZE(mclk_ratios)) {
- dev_err(codec->dev,
+ dev_err(component->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]);
+ dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]);
- snd_soc_update_bits(codec, WM8770_MSTRCTRL, 0x7 << shift,
+ snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x7 << shift,
i << shift);
}
- snd_soc_update_bits(codec, WM8770_IFACECTRL, 0x30, iface);
+ snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0x30, iface);
return 0;
}
-static int wm8770_mute(struct snd_soc_dai *dai, int mute)
+static int wm8770_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
- codec = dai->codec;
- return snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10,
+ component = dai->component;
+ return snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10,
!!mute << 4);
}
static int wm8770_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct wm8770_priv *wm8770;
- codec = dai->codec;
- wm8770 = snd_soc_codec_get_drvdata(codec);
+ component = dai->component;
+ wm8770 = snd_soc_component_get_drvdata(component);
wm8770->sysclk = freq;
return 0;
}
-static int wm8770_set_bias_level(struct snd_soc_codec *codec,
+static int wm8770_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
int ret;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wm8770_priv *wm8770;
- wm8770 = snd_soc_codec_get_drvdata(codec);
+ wm8770 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -510,11 +508,11 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
wm8770->supplies);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
@@ -523,12 +521,12 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec,
regcache_sync(wm8770->regmap);
/* global powerup */
- snd_soc_write(codec, WM8770_PWDNCTRL, 0);
+ snd_soc_component_write(component, WM8770_PWDNCTRL, 0);
}
break;
case SND_SOC_BIAS_OFF:
/* global powerdown */
- snd_soc_write(codec, WM8770_PWDNCTRL, 1);
+ snd_soc_component_write(component, WM8770_PWDNCTRL, 1);
regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies),
wm8770->supplies);
break;
@@ -541,10 +539,11 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops wm8770_dai_ops = {
- .digital_mute = wm8770_mute,
+ .mute_stream = wm8770_mute,
.hw_params = wm8770_hw_params,
.set_fmt = wm8770_set_fmt,
.set_sysclk = wm8770_set_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8770_dai = {
@@ -564,61 +563,61 @@ static struct snd_soc_dai_driver wm8770_dai = {
.formats = WM8770_FORMATS
},
.ops = &wm8770_dai_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
};
-static int wm8770_probe(struct snd_soc_codec *codec)
+static int wm8770_probe(struct snd_soc_component *component)
{
struct wm8770_priv *wm8770;
int ret;
- wm8770 = snd_soc_codec_get_drvdata(codec);
- wm8770->codec = codec;
+ wm8770 = snd_soc_component_get_drvdata(component);
+ wm8770->component = component;
ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
wm8770->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- ret = wm8770_reset(codec);
+ ret = wm8770_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
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);
+ snd_soc_component_update_bits(component, WM8770_MSDIGVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_MSALGVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_VOUT1RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_VOUT2RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_VOUT3RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_VOUT4RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_DAC1RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_DAC2RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_DAC3RVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8770_DAC4RVOL, 0x100, 0x100);
/* mute all DACs */
- snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10, 0x10);
+ snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10, 0x10);
err_reg_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies);
return ret;
}
-static 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 snd_soc_component_driver soc_component_dev_wm8770 = {
+ .probe = wm8770_probe,
+ .set_bias_level = wm8770_set_bias_level,
+ .controls = wm8770_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8770_snd_controls),
+ .dapm_widgets = wm8770_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets),
+ .dapm_routes = wm8770_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8770_intercon),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8770_of_match[] = {
@@ -634,7 +633,7 @@ static const struct regmap_config wm8770_regmap = {
.reg_defaults = wm8770_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8770_volatile_reg,
};
@@ -665,8 +664,9 @@ static int wm8770_spi_probe(struct spi_device *spi)
/* 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]);
+ ret = devm_regulator_register_notifier(
+ wm8770->supplies[i].consumer,
+ &wm8770->disable_nb[i]);
if (ret) {
dev_err(&spi->dev,
"Failed to register regulator notifier: %d\n",
@@ -680,33 +680,18 @@ static int wm8770_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8770);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8770, &wm8770_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.of_match_table = wm8770_of_match,
},
.probe = wm8770_spi_probe,
- .remove = wm8770_spi_remove
};
module_spi_driver(wm8770_spi_driver);
diff --git a/sound/soc/codecs/wm8770.h b/sound/soc/codecs/wm8770.h
index 5f1b3bda6cc8..e0a3f5a14620 100644
--- a/sound/soc/codecs/wm8770.h
+++ b/sound/soc/codecs/wm8770.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 5af44f9a8cf2..c3f340657f0c 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8776.c -- WM8776 ALSA SoC Audio driver
*
@@ -5,20 +6,16 @@
*
* 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/mod_devicetable.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/of_device.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
@@ -78,9 +75,9 @@ static bool wm8776_volatile(struct device *dev, unsigned int reg)
}
}
-static int wm8776_reset(struct snd_soc_codec *codec)
+static int wm8776_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, WM8776_RESET, 0);
+ return snd_soc_component_write(component, WM8776_RESET, 0);
}
static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1);
@@ -166,7 +163,7 @@ static const struct snd_soc_dapm_route routes[] = {
static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int reg, iface, master;
switch (dai->driver->id) {
@@ -185,9 +182,9 @@ static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
iface = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
master = 0;
break;
default:
@@ -224,8 +221,8 @@ static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* Finally, write out the values */
- snd_soc_update_bits(codec, reg, 0xf, iface);
- snd_soc_update_bits(codec, WM8776_MSTRCTRL, 0x180, master);
+ snd_soc_component_update_bits(component, reg, 0xf, iface);
+ snd_soc_component_update_bits(component, WM8776_MSTRCTRL, 0x180, master);
return 0;
}
@@ -243,8 +240,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component);
int iface_reg, iface;
int ratio_shift, master;
int i;
@@ -279,13 +276,13 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
iface = 0x30;
break;
default:
- dev_err(codec->dev, "Unsupported sample size: %i\n",
+ dev_err(component->dev, "Unsupported sample size: %i\n",
params_width(params));
return -EINVAL;
}
/* Only need to set MCLK/LRCLK ratio if we're master */
- if (snd_soc_read(codec, WM8776_MSTRCTRL) & master) {
+ if (snd_soc_component_read(component, WM8776_MSTRCTRL) & master) {
for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) {
if (wm8776->sysclk[dai->driver->id] / params_rate(params)
== mclk_ratios[i])
@@ -293,37 +290,37 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
}
if (i == ARRAY_SIZE(mclk_ratios)) {
- dev_err(codec->dev,
+ dev_err(component->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]);
+ dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]);
- snd_soc_update_bits(codec, WM8776_MSTRCTRL,
+ snd_soc_component_update_bits(component, WM8776_MSTRCTRL,
0x7 << ratio_shift, i << ratio_shift);
} else {
- dev_dbg(codec->dev, "DAI in slave mode\n");
+ dev_dbg(component->dev, "DAI in slave mode\n");
}
- snd_soc_update_bits(codec, iface_reg, 0x30, iface);
+ snd_soc_component_update_bits(component, iface_reg, 0x30, iface);
return 0;
}
-static int wm8776_mute(struct snd_soc_dai *dai, int mute)
+static int wm8776_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_write(codec, WM8776_DACMUTE, !!mute);
+ return snd_soc_component_write(component, WM8776_DACMUTE, !!mute);
}
static int wm8776_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component);
if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk)))
return -EINVAL;
@@ -333,10 +330,11 @@ static int wm8776_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int wm8776_set_bias_level(struct snd_soc_codec *codec,
+static int wm8776_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+ struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -344,16 +342,16 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == 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);
+ snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 0);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 1);
+ snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 1);
break;
}
@@ -364,10 +362,11 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops wm8776_dac_ops = {
- .digital_mute = wm8776_mute,
+ .mute_stream = wm8776_mute,
.hw_params = wm8776_hw_params,
.set_fmt = wm8776_set_fmt,
.set_sysclk = wm8776_set_sysclk,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8776_adc_ops = {
@@ -407,35 +406,37 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
},
};
-static int wm8776_probe(struct snd_soc_codec *codec)
+static int wm8776_probe(struct snd_soc_component *component)
{
int ret = 0;
- ret = wm8776_reset(codec);
+ ret = wm8776_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
/* 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);
+ snd_soc_component_update_bits(component, WM8776_HPRVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, 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 snd_soc_component_driver soc_component_dev_wm8776 = {
+ .probe = wm8776_probe,
+ .set_bias_level = wm8776_set_bias_level,
+ .controls = wm8776_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8776_snd_controls),
+ .dapm_widgets = wm8776_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets),
+ .dapm_routes = routes,
+ .num_dapm_routes = ARRAY_SIZE(routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct of_device_id wm8776_of_match[] = {
@@ -451,7 +452,7 @@ static const struct regmap_config wm8776_regmap = {
.reg_defaults = wm8776_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8776_volatile,
};
@@ -473,31 +474,23 @@ static int wm8776_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8776);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
.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)
+static int wm8776_i2c_probe(struct i2c_client *i2c)
{
struct wm8776_priv *wm8776;
int ret;
@@ -513,18 +506,12 @@ static int wm8776_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8776);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
@@ -537,8 +524,7 @@ static struct i2c_driver wm8776_i2c_driver = {
.name = "wm8776",
.of_match_table = wm8776_of_match,
},
- .probe = wm8776_i2c_probe,
- .remove = wm8776_i2c_remove,
+ .probe = wm8776_i2c_probe,
.id_table = wm8776_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8776.h b/sound/soc/codecs/wm8776.h
index 4cf1c8e0bfc9..266a48a21163 100644
--- a/sound/soc/codecs/wm8776.h
+++ b/sound/soc/codecs/wm8776.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c
index fb55fd845d27..3a2acdfa9b85 100644
--- a/sound/soc/codecs/wm8782.c
+++ b/sound/soc/codecs/wm8782.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sound/soc/codecs/wm8782.c
* simple, strap-pin configured 24bit 2ch ADC
@@ -6,13 +7,8 @@
* Author: Johannes Stezenbach <js@sig21.net>
*
* based on ad73311.c
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices Inc.
* Author: Cliff Cai <cliff.cai@analog.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/init.h>
@@ -20,12 +16,34 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
+/* regulator power supply names */
+static const char *supply_names[] = {
+ "Vdda", /* analog supply, 2.7V - 3.6V */
+ "Vdd", /* digital supply, 2.7V - 5.5V */
+};
+
+struct wm8782_priv {
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ int max_rate;
+};
+
+static int wm8782_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = sub->runtime;
+ struct wm8782_priv *priv =
+ snd_soc_component_get_drvdata(dai->component);
+
+ return snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ 8000, priv->max_rate);
+}
+
static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AINL"),
SND_SOC_DAPM_INPUT("AINR"),
@@ -36,45 +54,128 @@ static const struct snd_soc_dapm_route wm8782_dapm_routes[] = {
{ "Capture", NULL, "AINR" },
};
+static const struct snd_soc_dai_ops wm8782_dai_ops = {
+ .startup = &wm8782_dai_startup,
+};
+
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,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
+ .ops = &wm8782_dai_ops,
};
-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_soc_probe(struct snd_soc_component *component)
+{
+ struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
-static int wm8782_probe(struct platform_device *pdev)
+static void wm8782_soc_remove(struct snd_soc_component *component)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_wm8782, &wm8782_dai, 1);
+ struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
-static int wm8782_remove(struct platform_device *pdev)
+#ifdef CONFIG_PM
+static int wm8782_soc_suspend(struct snd_soc_component *component)
{
- snd_soc_unregister_codec(&pdev->dev);
+ struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
return 0;
}
+static int wm8782_soc_resume(struct snd_soc_component *component)
+{
+ struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+#else
+#define wm8782_soc_suspend NULL
+#define wm8782_soc_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct snd_soc_component_driver soc_component_dev_wm8782 = {
+ .probe = wm8782_soc_probe,
+ .remove = wm8782_soc_remove,
+ .suspend = wm8782_soc_suspend,
+ .resume = wm8782_soc_resume,
+ .dapm_widgets = wm8782_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets),
+ .dapm_routes = wm8782_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int wm8782_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct wm8782_priv *priv;
+ int ret, i, fsampen;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret < 0)
+ return ret;
+
+ // Assume lowest value by default to avoid inadvertent overclocking
+ fsampen = 0;
+
+ if (np)
+ of_property_read_u32(np, "wlf,fsampen", &fsampen);
+
+ switch (fsampen) {
+ case 0:
+ priv->max_rate = 48000;
+ break;
+ case 1:
+ priv->max_rate = 96000;
+ break;
+ case 2:
+ priv->max_rate = 192000;
+ break;
+ default:
+ dev_err(dev, "Invalid wlf,fsampen value");
+ return -EINVAL;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm8782, &wm8782_dai, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id wm8782_of_match[] = {
+ { .compatible = "wlf,wm8782", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8782_of_match);
+#endif
+
static struct platform_driver wm8782_codec_driver = {
.driver = {
.name = "wm8782",
+ .of_match_table = of_match_ptr(wm8782_of_match),
},
.probe = wm8782_probe,
- .remove = wm8782_remove,
};
module_platform_driver(wm8782_codec_driver);
diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c
index f27464c2c5ba..3380d7301b17 100644
--- a/sound/soc/codecs/wm8804-i2c.c
+++ b/sound/soc/codecs/wm8804-i2c.c
@@ -1,23 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 <linux/acpi.h>
#include "wm8804.h"
-static int wm8804_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8804_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -28,29 +25,40 @@ static int wm8804_i2c_probe(struct i2c_client *i2c,
return wm8804_probe(&i2c->dev, regmap);
}
-static int wm8804_i2c_remove(struct i2c_client *i2c)
+static void wm8804_i2c_remove(struct i2c_client *i2c)
{
wm8804_remove(&i2c->dev);
- return 0;
}
static const struct i2c_device_id wm8804_i2c_id[] = {
- { "wm8804", 0 },
+ { "wm8804" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id);
+#if defined(CONFIG_OF)
static const struct of_device_id wm8804_of_match[] = {
{ .compatible = "wlf,wm8804", },
{ }
};
MODULE_DEVICE_TABLE(of, wm8804_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id wm8804_acpi_match[] = {
+ { "1AEC8804", 0 }, /* Wolfson PCI ID + part ID */
+ { "10138804", 0 }, /* Cirrus Logic PCI ID + part ID */
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, wm8804_acpi_match);
+#endif
static struct i2c_driver wm8804_i2c_driver = {
.driver = {
.name = "wm8804",
- .pm = &wm8804_pm,
- .of_match_table = wm8804_of_match,
+ .pm = pm_ptr(&wm8804_pm),
+ .of_match_table = of_match_ptr(wm8804_of_match),
+ .acpi_match_table = ACPI_PTR(wm8804_acpi_match),
},
.probe = wm8804_i2c_probe,
.remove = wm8804_i2c_remove,
diff --git a/sound/soc/codecs/wm8804-spi.c b/sound/soc/codecs/wm8804-spi.c
index 9998c78a2325..cf74abfb1a2c 100644
--- a/sound/soc/codecs/wm8804-spi.c
+++ b/sound/soc/codecs/wm8804-spi.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -27,10 +24,9 @@ static int wm8804_spi_probe(struct spi_device *spi)
return wm8804_probe(&spi->dev, regmap);
}
-static int wm8804_spi_remove(struct spi_device *spi)
+static void wm8804_spi_remove(struct spi_device *spi)
{
wm8804_remove(&spi->dev);
- return 0;
}
static const struct of_device_id wm8804_of_match[] = {
@@ -42,7 +38,7 @@ MODULE_DEVICE_TABLE(of, wm8804_of_match);
static struct spi_driver wm8804_spi_driver = {
.driver = {
.name = "wm8804",
- .pm = &wm8804_pm,
+ .pm = pm_ptr(&wm8804_pm),
.of_match_table = wm8804_of_match,
},
.probe = wm8804_spi_probe,
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 8d914702cae4..94aa3c8de0ab 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -17,7 +14,6 @@
#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>
@@ -137,21 +133,21 @@ static const struct snd_soc_dapm_route wm8804_dapm_routes[] = {
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8804_priv *wm8804 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* power up the aif */
if (!wm8804->aif_pwr)
- snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0);
+ snd_soc_component_update_bits(component, WM8804_PWRDN, 0x10, 0x0);
wm8804->aif_pwr++;
break;
case SND_SOC_DAPM_POST_PMD:
/* power down only both paths are disabled */
wm8804->aif_pwr--;
if (!wm8804->aif_pwr)
- snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10);
+ snd_soc_component_update_bits(component, WM8804_PWRDN, 0x10, 0x10);
break;
}
@@ -161,8 +157,8 @@ static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
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 = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
unsigned int mask = 1 << e->shift_l;
@@ -173,18 +169,18 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
snd_soc_dapm_mutex_lock(dapm);
- if (snd_soc_test_bits(codec, e->reg, mask, val)) {
+ if (snd_soc_component_test_bits(component, e->reg, mask, val)) {
/* save the current power state of the transmitter */
- txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
+ txpwr = snd_soc_component_read(component, WM8804_PWRDN) & 0x4;
/* power down the transmitter */
- snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4);
+ snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, 0x4);
/* set the tx source */
- snd_soc_update_bits(codec, e->reg, mask, val);
+ snd_soc_component_update_bits(component, e->reg, mask, val);
/* restore the transmitter's configuration */
- snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
+ snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, txpwr);
}
snd_soc_dapm_mutex_unlock(dapm);
@@ -218,10 +214,10 @@ static int wm8804_soft_reset(struct wm8804_priv *wm8804)
static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
u16 format, master, bcp, lrp;
- codec = dai->codec;
+ component = dai->component;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -243,14 +239,14 @@ static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* set data format */
- snd_soc_update_bits(codec, WM8804_AIFTX, 0x3, format);
- snd_soc_update_bits(codec, WM8804_AIFRX, 0x3, format);
+ snd_soc_component_update_bits(component, WM8804_AIFTX, 0x3, format);
+ snd_soc_component_update_bits(component, WM8804_AIFRX, 0x3, format);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
master = 0;
break;
default:
@@ -259,7 +255,7 @@ static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* set master/slave mode */
- snd_soc_update_bits(codec, WM8804_AIFRX, 0x40, master << 6);
+ snd_soc_component_update_bits(component, WM8804_AIFRX, 0x40, master << 6);
bcp = lrp = 0;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -280,9 +276,9 @@ static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* set frame inversion */
- snd_soc_update_bits(codec, WM8804_AIFTX, 0x10 | 0x20,
+ snd_soc_component_update_bits(component, WM8804_AIFTX, 0x10 | 0x20,
(bcp << 4) | (lrp << 5));
- snd_soc_update_bits(codec, WM8804_AIFRX, 0x10 | 0x20,
+ snd_soc_component_update_bits(component, WM8804_AIFRX, 0x10 | 0x20,
(bcp << 4) | (lrp << 5));
return 0;
}
@@ -291,10 +287,10 @@ 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;
+ struct snd_soc_component *component;
u16 blen;
- codec = dai->codec;
+ component = dai->component;
switch (params_width(params)) {
case 16:
@@ -313,8 +309,8 @@ static int wm8804_hw_params(struct snd_pcm_substream *substream,
}
/* set word length */
- snd_soc_update_bits(codec, WM8804_AIFTX, 0xc, blen << 2);
- snd_soc_update_bits(codec, WM8804_AIFRX, 0xc, blen << 2);
+ snd_soc_component_update_bits(component, WM8804_AIFTX, 0xc, blen << 2);
+ snd_soc_component_update_bits(component, WM8804_AIFRX, 0xc, blen << 2);
return 0;
}
@@ -405,8 +401,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8804_priv *wm8804 = snd_soc_component_get_drvdata(component);
bool change;
if (!freq_in || !freq_out) {
@@ -431,18 +427,18 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
pm_runtime_get_sync(wm8804->dev);
/* set PLLN and PRESCALE */
- snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
+ snd_soc_component_update_bits(component, WM8804_PLL4, 0xf | 0x10,
pll_div.n | (pll_div.prescale << 4));
/* set mclkdiv and freqmode */
- snd_soc_update_bits(codec, WM8804_PLL5, 0x3 | 0x8,
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_write(component, WM8804_PLL1, pll_div.k & 0xff);
+ snd_soc_component_write(component, WM8804_PLL2, (pll_div.k >> 8) & 0xff);
+ snd_soc_component_write(component, WM8804_PLL3, pll_div.k >> 16);
/* power up the PLL */
- snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0);
+ snd_soc_component_update_bits(component, WM8804_PWRDN, 0x1, 0);
}
return 0;
@@ -451,15 +447,15 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
static int wm8804_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
- codec = dai->codec;
+ component = dai->component;
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);
+ snd_soc_component_update_bits(component, WM8804_PLL6, 0x80, 0x80);
else {
dev_err(dai->dev, "OSCCLOCK is not within the "
"recommended range: %uHz\n", freq);
@@ -467,13 +463,13 @@ static int wm8804_set_sysclk(struct snd_soc_dai *dai,
}
break;
case WM8804_TX_CLKSRC_PLL:
- snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0);
+ snd_soc_component_update_bits(component, WM8804_PLL6, 0x80, 0);
break;
case WM8804_CLKOUT_SRC_CLK1:
- snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0);
+ snd_soc_component_update_bits(component, WM8804_PLL6, 0x8, 0);
break;
case WM8804_CLKOUT_SRC_OSCCLK:
- snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0x8);
+ snd_soc_component_update_bits(component, WM8804_PLL6, 0x8, 0x8);
break;
default:
dev_err(dai->dev, "Unknown clock source: %d\n", clk_id);
@@ -486,17 +482,17 @@ static int wm8804_set_sysclk(struct snd_soc_dai *dai,
static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct wm8804_priv *wm8804;
- codec = dai->codec;
+ component = dai->component;
switch (div_id) {
case WM8804_CLKOUT_DIV:
- snd_soc_update_bits(codec, WM8804_PLL5, 0x30,
+ snd_soc_component_update_bits(component, WM8804_PLL5, 0x30,
(div & 0x3) << 4);
break;
case WM8804_MCLK_DIV:
- wm8804 = snd_soc_codec_get_drvdata(codec);
+ wm8804 = snd_soc_component_get_drvdata(component);
wm8804->mclk_div = div;
break;
default:
@@ -539,16 +535,16 @@ static struct snd_soc_dai_driver wm8804_dai = {
.formats = WM8804_FORMATS,
},
.ops = &wm8804_dai_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 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),
+static const struct snd_soc_component_driver soc_component_dev_wm8804 = {
+ .dapm_widgets = wm8804_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),
+ .dapm_routes = wm8804_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
const struct regmap_config wm8804_regmap_config = {
@@ -558,7 +554,7 @@ const struct regmap_config wm8804_regmap_config = {
.max_register = WM8804_MAX_REGISTER,
.volatile_reg = wm8804_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8804_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults),
};
@@ -621,8 +617,7 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
return ret;
}
- if (wm8804->reset)
- gpiod_set_value_cansleep(wm8804->reset, 1);
+ gpiod_set_value_cansleep(wm8804->reset, 1);
ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1);
if (ret < 0) {
@@ -660,7 +655,7 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
}
}
- ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_wm8804,
&wm8804_dai, 1);
if (ret < 0) {
dev_err(dev, "Failed to register CODEC: %d\n", ret);
@@ -682,11 +677,9 @@ 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);
@@ -719,12 +712,10 @@ static int wm8804_runtime_suspend(struct device *dev)
return 0;
}
-#endif
-const struct dev_pm_ops wm8804_pm = {
- SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(wm8804_pm) = {
+ 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>");
diff --git a/sound/soc/codecs/wm8804.h b/sound/soc/codecs/wm8804.h
index aa72fa66c932..64f3ccc9ac34 100644
--- a/sound/soc/codecs/wm8804.h
+++ b/sound/soc/codecs/wm8804.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 5d8dca88d612..fea629541acd 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8900.c -- WM8900 ALSA Soc Audio driver
*
@@ -5,10 +6,6 @@
*
* 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.
@@ -216,23 +213,23 @@ static bool wm8900_volatile_register(struct device *dev, unsigned int reg)
}
}
-static void wm8900_reset(struct snd_soc_codec *codec)
+static void wm8900_reset(struct snd_soc_component *component)
{
- snd_soc_write(codec, WM8900_REG_RESET, 0);
+ snd_soc_component_write(component, WM8900_REG_RESET, 0);
}
static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- u16 hpctl1 = snd_soc_read(codec, WM8900_REG_HPCTL1);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 hpctl1 = snd_soc_component_read(component, WM8900_REG_HPCTL1);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Clamp headphone outputs */
hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
WM8900_REG_HPCTL1_HP_CLAMP_OP;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
break;
case SND_SOC_DAPM_POST_PMU:
@@ -241,41 +238,41 @@ static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
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);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
msleep(400);
/* Enable the output stage */
hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
/* Remove the shorts */
hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
break;
case SND_SOC_DAPM_PRE_PMD:
/* Short the output */
hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
/* Disable the output stage */
hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
/* Clamp the outputs and power down input */
hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
WM8900_REG_HPCTL1_HP_CLAMP_OP;
hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
- snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1);
break;
case SND_SOC_DAPM_POST_PMD:
/* Disable everything */
- snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
+ snd_soc_component_write(component, WM8900_REG_HPCTL1, 0);
break;
default:
@@ -446,12 +443,6 @@ SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1,
};
-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),
@@ -635,10 +626,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
u16 reg;
- reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60;
+ reg = snd_soc_component_read(component, WM8900_REG_AUDIO1) & ~0x60;
switch (params_width(params)) {
case 16:
@@ -656,17 +647,17 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, WM8900_REG_AUDIO1, reg);
+ snd_soc_component_write(component, WM8900_REG_AUDIO1, reg);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+ reg = snd_soc_component_read(component, WM8900_REG_DACCTRL);
if (params_rate(params) <= 24000)
reg |= WM8900_REG_DACCTRL_DAC_SB_FILT;
else
reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT;
- snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
+ snd_soc_component_write(component, WM8900_REG_DACCTRL, reg);
}
return 0;
@@ -751,24 +742,24 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8900_set_fll(struct snd_soc_codec *codec,
+static int wm8900_set_fll(struct snd_soc_component *component,
int fll_id, unsigned int freq_in, unsigned int freq_out)
{
- struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
+ struct wm8900_priv *wm8900 = snd_soc_component_get_drvdata(component);
struct _fll_div fll_div;
if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out)
return 0;
/* The digital side should be disabled during any change. */
- snd_soc_update_bits(codec, WM8900_REG_POWER1,
+ snd_soc_component_update_bits(component, WM8900_REG_POWER1,
WM8900_REG_POWER1_FLL_ENA, 0);
/* Disable the FLL? */
if (!freq_in || !freq_out) {
- snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1,
WM8900_REG_CLOCKING1_MCLK_SRC, 0);
- snd_soc_update_bits(codec, WM8900_REG_FLLCTL1,
+ snd_soc_component_update_bits(component, WM8900_REG_FLLCTL1,
WM8900_REG_FLLCTL1_OSC_ENA, 0);
wm8900->fll_in = freq_in;
wm8900->fll_out = freq_out;
@@ -784,32 +775,32 @@ static int wm8900_set_fll(struct snd_soc_codec *codec,
/* The osclilator *MUST* be enabled before we enable the
* digital circuit. */
- snd_soc_write(codec, WM8900_REG_FLLCTL1,
+ snd_soc_component_write(component, 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,
+ snd_soc_component_write(component, WM8900_REG_FLLCTL4, fll_div.n >> 5);
+ snd_soc_component_write(component, WM8900_REG_FLLCTL5,
(fll_div.fllclk_div << 6) | (fll_div.n & 0x1f));
if (fll_div.k) {
- snd_soc_write(codec, WM8900_REG_FLLCTL2,
+ snd_soc_component_write(component, WM8900_REG_FLLCTL2,
(fll_div.k >> 8) | 0x100);
- snd_soc_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
+ snd_soc_component_write(component, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
} else
- snd_soc_write(codec, WM8900_REG_FLLCTL2, 0);
+ snd_soc_component_write(component, WM8900_REG_FLLCTL2, 0);
if (fll_div.fll_slow_lock_ref)
- snd_soc_write(codec, WM8900_REG_FLLCTL6,
+ snd_soc_component_write(component, WM8900_REG_FLLCTL6,
WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF);
else
- snd_soc_write(codec, WM8900_REG_FLLCTL6, 0);
+ snd_soc_component_write(component, WM8900_REG_FLLCTL6, 0);
- snd_soc_update_bits(codec, WM8900_REG_POWER1,
+ snd_soc_component_update_bits(component, WM8900_REG_POWER1,
WM8900_REG_POWER1_FLL_ENA,
WM8900_REG_POWER1_FLL_ENA);
reenable:
- snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1,
WM8900_REG_CLOCKING1_MCLK_SRC,
WM8900_REG_CLOCKING1_MCLK_SRC);
return 0;
@@ -818,41 +809,41 @@ reenable:
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);
+ return wm8900_set_fll(codec_dai->component, pll_id, freq_in, freq_out);
}
static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
switch (div_id) {
case WM8900_BCLK_DIV:
- snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1,
WM8900_REG_CLOCKING1_BCLK_MASK, div);
break;
case WM8900_OPCLK_DIV:
- snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1,
WM8900_REG_CLOCKING1_OPCLK_MASK, div);
break;
case WM8900_DAC_LRCLK:
- snd_soc_update_bits(codec, WM8900_REG_AUDIO4,
+ snd_soc_component_update_bits(component, WM8900_REG_AUDIO4,
WM8900_LRC_MASK, div);
break;
case WM8900_ADC_LRCLK:
- snd_soc_update_bits(codec, WM8900_REG_AUDIO3,
+ snd_soc_component_update_bits(component, WM8900_REG_AUDIO3,
WM8900_LRC_MASK, div);
break;
case WM8900_DAC_CLKDIV:
- snd_soc_update_bits(codec, WM8900_REG_CLOCKING2,
+ snd_soc_component_update_bits(component, WM8900_REG_CLOCKING2,
WM8900_REG_CLOCKING2_DAC_CLKDIV, div);
break;
case WM8900_ADC_CLKDIV:
- snd_soc_update_bits(codec, WM8900_REG_CLOCKING2,
+ snd_soc_component_update_bits(component, WM8900_REG_CLOCKING2,
WM8900_REG_CLOCKING2_ADC_CLKDIV, div);
break;
case WM8900_LRCLK_MODE:
- snd_soc_update_bits(codec, WM8900_REG_DACCTRL,
+ snd_soc_component_update_bits(component, WM8900_REG_DACCTRL,
WM8900_REG_DACCTRL_AIF_LRCLKRATE, div);
break;
default:
@@ -866,32 +857,32 @@ static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int 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);
+ clocking1 = snd_soc_component_read(component, WM8900_REG_CLOCKING1);
+ aif1 = snd_soc_component_read(component, WM8900_REG_AUDIO1);
+ aif3 = snd_soc_component_read(component, WM8900_REG_AUDIO3);
+ aif4 = snd_soc_component_read(component, WM8900_REG_AUDIO4);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
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:
+ case SND_SOC_DAIFMT_CBC_CFP:
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:
+ case SND_SOC_DAIFMT_CBP_CFP:
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:
+ case SND_SOC_DAIFMT_CBP_CFC:
clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
@@ -968,27 +959,27 @@ static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
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);
+ snd_soc_component_write(component, WM8900_REG_CLOCKING1, clocking1);
+ snd_soc_component_write(component, WM8900_REG_AUDIO1, aif1);
+ snd_soc_component_write(component, WM8900_REG_AUDIO3, aif3);
+ snd_soc_component_write(component, WM8900_REG_AUDIO4, aif4);
return 0;
}
-static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm8900_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
- reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+ reg = snd_soc_component_read(component, WM8900_REG_DACCTRL);
if (mute)
reg |= WM8900_REG_DACCTRL_MUTE;
else
reg &= ~WM8900_REG_DACCTRL_MUTE;
- snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
+ snd_soc_component_write(component, WM8900_REG_DACCTRL, reg);
return 0;
}
@@ -1006,7 +997,8 @@ static const struct snd_soc_dai_ops wm8900_dai_ops = {
.set_clkdiv = wm8900_set_dai_clkdiv,
.set_pll = wm8900_set_dai_pll,
.set_fmt = wm8900_set_dai_fmt,
- .digital_mute = wm8900_digital_mute,
+ .mute_stream = wm8900_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8900_dai = {
@@ -1028,18 +1020,19 @@ static struct snd_soc_dai_driver wm8900_dai = {
.ops = &wm8900_dai_ops,
};
-static int wm8900_set_bias_level(struct snd_soc_codec *codec,
+static int wm8900_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u16 reg;
switch (level) {
case SND_SOC_BIAS_ON:
/* Enable thermal shutdown */
- snd_soc_update_bits(codec, WM8900_REG_GPIO,
+ snd_soc_component_update_bits(component, WM8900_REG_GPIO,
WM8900_REG_GPIO_TEMP_ENA,
WM8900_REG_GPIO_TEMP_ENA);
- snd_soc_update_bits(codec, WM8900_REG_ADDCTL,
+ snd_soc_component_update_bits(component, WM8900_REG_ADDCTL,
WM8900_REG_ADDCTL_TEMP_SD,
WM8900_REG_ADDCTL_TEMP_SD);
break;
@@ -1049,113 +1042,115 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
/* Charge capacitors if initial power up */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* STARTUP_BIAS_ENA on */
- snd_soc_write(codec, WM8900_REG_POWER1,
+ snd_soc_component_write(component, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA);
/* Startup bias mode */
- snd_soc_write(codec, WM8900_REG_ADDCTL,
+ snd_soc_component_write(component, WM8900_REG_ADDCTL,
WM8900_REG_ADDCTL_BIAS_SRC |
WM8900_REG_ADDCTL_VMID_SOFTST);
/* VMID 2x50k */
- snd_soc_write(codec, WM8900_REG_POWER1,
+ snd_soc_component_write(component, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1);
/* Allow capacitors to charge */
schedule_timeout_interruptible(msecs_to_jiffies(400));
/* Enable bias */
- snd_soc_write(codec, WM8900_REG_POWER1,
+ snd_soc_component_write(component, 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_component_write(component, WM8900_REG_ADDCTL, 0);
- snd_soc_write(codec, WM8900_REG_POWER1,
+ snd_soc_component_write(component, 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 = snd_soc_component_read(component, WM8900_REG_POWER1);
+ snd_soc_component_write(component, WM8900_REG_POWER1,
(reg & WM8900_REG_POWER1_FLL_ENA) |
WM8900_REG_POWER1_BIAS_ENA | 0x1);
- snd_soc_write(codec, WM8900_REG_POWER2,
+ snd_soc_component_write(component, WM8900_REG_POWER2,
WM8900_REG_POWER2_SYSCLK_ENA);
- snd_soc_write(codec, WM8900_REG_POWER3, 0);
+ snd_soc_component_write(component, 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 = snd_soc_component_read(component, WM8900_REG_POWER1);
+ snd_soc_component_write(component, WM8900_REG_POWER1,
reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA);
- snd_soc_write(codec, WM8900_REG_ADDCTL,
+ snd_soc_component_write(component, WM8900_REG_ADDCTL,
WM8900_REG_ADDCTL_BIAS_SRC |
WM8900_REG_ADDCTL_VMID_SOFTST);
/* Discharge caps */
- snd_soc_write(codec, WM8900_REG_POWER1,
+ snd_soc_component_write(component, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA);
schedule_timeout_interruptible(msecs_to_jiffies(500));
/* Remove clamp */
- snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8900_REG_ADDCTL, 0);
+ snd_soc_component_write(component, WM8900_REG_POWER1, 0);
+ snd_soc_component_write(component, WM8900_REG_POWER2, 0);
+ snd_soc_component_write(component, WM8900_REG_POWER3, 0);
/* Need to let things settle before stopping the clock
* to ensure that restart works, see "Stopping the
* master clock" in the datasheet. */
schedule_timeout_interruptible(msecs_to_jiffies(1));
- snd_soc_write(codec, WM8900_REG_POWER2,
+ snd_soc_component_write(component, WM8900_REG_POWER2,
WM8900_REG_POWER2_SYSCLK_ENA);
break;
}
return 0;
}
-static int wm8900_suspend(struct snd_soc_codec *codec)
+static int wm8900_suspend(struct snd_soc_component *component)
{
- struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
+ struct wm8900_priv *wm8900 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int fll_out = wm8900->fll_out;
int fll_in = wm8900->fll_in;
int ret;
/* Stop the FLL in an orderly fashion */
- ret = wm8900_set_fll(codec, 0, 0, 0);
+ ret = wm8900_set_fll(component, 0, 0, 0);
if (ret != 0) {
- dev_err(codec->dev, "Failed to stop FLL\n");
+ dev_err(component->dev, "Failed to stop FLL\n");
return ret;
}
wm8900->fll_out = fll_out;
wm8900->fll_in = fll_in;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
return 0;
}
-static int wm8900_resume(struct snd_soc_codec *codec)
+static int wm8900_resume(struct snd_soc_component *component)
{
- struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
+ struct wm8900_priv *wm8900 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
- wm8900_reset(codec);
+ wm8900_reset(component);
ret = regcache_sync(wm8900->regmap);
if (ret != 0) {
- dev_err(codec->dev, "Failed to restore cache: %d\n", ret);
+ dev_err(component->dev, "Failed to restore cache: %d\n", ret);
return ret;
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
/* Restart the FLL? */
if (wm8900->fll_out) {
@@ -1165,9 +1160,9 @@ static int wm8900_resume(struct snd_soc_codec *codec)
wm8900->fll_in = 0;
wm8900->fll_out = 0;
- ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
+ ret = wm8900_set_fll(component, 0, fll_in, fll_out);
if (ret != 0) {
- dev_err(codec->dev, "Failed to restart FLL\n");
+ dev_err(component->dev, "Failed to restart FLL\n");
return ret;
}
}
@@ -1175,51 +1170,54 @@ static int wm8900_resume(struct snd_soc_codec *codec)
return 0;
}
-static int wm8900_probe(struct snd_soc_codec *codec)
+static int wm8900_probe(struct snd_soc_component *component)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int reg;
- reg = snd_soc_read(codec, WM8900_REG_ID);
+ reg = snd_soc_component_read(component, WM8900_REG_ID);
if (reg != 0x8900) {
- dev_err(codec->dev, "Device is not a WM8900 - ID %x\n", reg);
+ dev_err(component->dev, "Device is not a WM8900 - ID %x\n", reg);
return -ENODEV;
}
- wm8900_reset(codec);
+ wm8900_reset(component);
/* Turn the chip on */
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, 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);
+ snd_soc_component_update_bits(component, WM8900_REG_LINVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_RINVOL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_LOUT1CTL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_ROUT1CTL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_LOUT2CTL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_ROUT2CTL, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_LDAC_DV, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_RDAC_DV, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_LADC_DV, 0x100, 0x100);
+ snd_soc_component_update_bits(component, WM8900_REG_RADC_DV, 0x100, 0x100);
/* Set the DAC and mixer output bias */
- snd_soc_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
+ snd_soc_component_write(component, 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 snd_soc_component_driver soc_component_dev_wm8900 = {
+ .probe = wm8900_probe,
+ .suspend = wm8900_suspend,
+ .resume = wm8900_resume,
+ .set_bias_level = wm8900_set_bias_level,
+ .controls = wm8900_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8900_snd_controls),
+ .dapm_widgets = wm8900_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8900_dapm_widgets),
+ .dapm_routes = wm8900_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8900_regmap = {
@@ -1229,7 +1227,7 @@ static const struct regmap_config wm8900_regmap = {
.reg_defaults = wm8900_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8900_volatile_register,
};
@@ -1251,30 +1249,22 @@ static int wm8900_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8900);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8900, &wm8900_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
},
.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)
+static int wm8900_i2c_probe(struct i2c_client *i2c)
{
struct wm8900_priv *wm8900;
int ret;
@@ -1290,20 +1280,17 @@ static int wm8900_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8900);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8900, &wm8900_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 void wm8900_i2c_remove(struct i2c_client *client)
+{}
static const struct i2c_device_id wm8900_i2c_id[] = {
- { "wm8900", 0 },
+ { "wm8900" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h
index 583f257e799b..7bc95409a920 100644
--- a/sound/soc/codecs/wm8900.h
+++ b/sound/soc/codecs/wm8900.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index a82b8bc2cfc0..f73f6ad06b38 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8903.c -- WM8903 ALSA SoC Audio driver
*
@@ -6,13 +7,8 @@
*
* 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>
@@ -20,10 +16,11 @@
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/mutex.h>
@@ -115,10 +112,19 @@ static const struct reg_default wm8903_reg_defaults[] = {
{ 172, 0x0000 }, /* R172 - Analogue Output Bias 0 */
};
+#define WM8903_NUM_SUPPLIES 4
+static const char *wm8903_supply_names[WM8903_NUM_SUPPLIES] = {
+ "AVDD",
+ "CPVDD",
+ "DBVDD",
+ "DCVDD",
+};
+
struct wm8903_priv {
struct wm8903_platform_data *pdata;
struct device *dev;
struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8903_NUM_SUPPLIES];
int sysclk;
int irq;
@@ -241,10 +247,10 @@ static bool wm8903_volatile_register(struct device *dev, unsigned int reg)
case WM8903_DC_SERVO_READBACK_2:
case WM8903_DC_SERVO_READBACK_3:
case WM8903_DC_SERVO_READBACK_4:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -260,15 +266,15 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
wm8903->dcs_pending |= 1 << w->shift;
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+ snd_soc_component_update_bits(component, WM8903_DC_SERVO_0,
1 << w->shift, 0);
break;
}
@@ -279,17 +285,16 @@ static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
#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,
+static void wm8903_seq_notifier(struct snd_soc_component *component,
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);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
int i, val;
/* Complete any pending DC servo starts */
if (wm8903->dcs_pending) {
- dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ dev_dbg(component->dev, "Starting DC servo for %x\n",
wm8903->dcs_pending);
/* If we've no cached values then we need to do startup */
@@ -298,14 +303,14 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
continue;
if (wm8903->dcs_cache[i]) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Restore DC servo %d value %x\n",
3 - i, wm8903->dcs_cache[i]);
- snd_soc_write(codec, WM8903_DC_SERVO_4 + i,
+ snd_soc_component_write(component, WM8903_DC_SERVO_4 + i,
wm8903->dcs_cache[i] & 0xff);
} else {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Calibrate DC servo %d\n", 3 - i);
dcs_mode = WM8903_DCS_MODE_START_STOP;
}
@@ -315,10 +320,10 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
if (wm8903->class_w_users)
dcs_mode = WM8903_DCS_MODE_START_STOP;
- snd_soc_update_bits(codec, WM8903_DC_SERVO_2,
+ snd_soc_component_update_bits(component, WM8903_DC_SERVO_2,
WM8903_DCS_MODE_MASK, dcs_mode);
- snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+ snd_soc_component_update_bits(component, WM8903_DC_SERVO_0,
WM8903_DCS_ENA_MASK, wm8903->dcs_pending);
switch (dcs_mode) {
@@ -336,9 +341,9 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
if (!(wm8903->dcs_pending & (1 << i)))
continue;
- val = snd_soc_read(codec,
+ val = snd_soc_component_read(component,
WM8903_DC_SERVO_READBACK_1 + i);
- dev_dbg(codec->dev, "DC servo %d: %x\n",
+ dev_dbg(component->dev, "DC servo %d: %x\n",
3 - i, val);
wm8903->dcs_cache[i] = val;
}
@@ -364,18 +369,18 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
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);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
u16 reg;
int ret;
- reg = snd_soc_read(codec, WM8903_CLASS_W_0);
+ reg = snd_soc_component_read(component, WM8903_CLASS_W_0);
/* Turn it off if we're about to enable bypass */
if (ucontrol->value.integer.value[0]) {
if (wm8903->class_w_users == 0) {
- dev_dbg(codec->dev, "Disabling Class W\n");
- snd_soc_write(codec, WM8903_CLASS_W_0, reg &
+ dev_dbg(component->dev, "Disabling Class W\n");
+ snd_soc_component_write(component, WM8903_CLASS_W_0, reg &
~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V));
}
wm8903->class_w_users++;
@@ -387,14 +392,14 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
/* 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 |
+ dev_dbg(component->dev, "Enabling Class W\n");
+ snd_soc_component_write(component, WM8903_CLASS_W_0, reg |
WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
}
wm8903->class_w_users--;
}
- dev_dbg(codec->dev, "Bypass use count now %d\n",
+ dev_dbg(component->dev, "Bypass use count now %d\n",
wm8903->class_w_users);
return ret;
@@ -407,9 +412,9 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
static int wm8903_deemph[] = { 0, 32000, 44100, 48000 };
-static int wm8903_set_deemph(struct snd_soc_codec *codec)
+static int wm8903_set_deemph(struct snd_soc_component *component)
{
- struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
int val, i, best;
/* If we're using deemphasis select the nearest available sample
@@ -429,18 +434,18 @@ static int wm8903_set_deemph(struct snd_soc_codec *codec)
val = 0;
}
- dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n",
+ dev_dbg(component->dev, "Set deemphasis %d (%dHz)\n",
best, wm8903_deemph[best]);
- return snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1,
+ return snd_soc_component_update_bits(component, WM8903_DAC_DIGITAL_1,
WM8903_DEEMPH_MASK, val);
}
static int wm8903_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8903->deemph;
@@ -450,8 +455,8 @@ static int wm8903_get_deemph(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
int ret = 0;
@@ -462,7 +467,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
if (wm8903->deemph != deemph) {
wm8903->deemph = deemph;
- wm8903_set_deemph(codec);
+ wm8903_set_deemph(component);
ret = 1;
}
@@ -1091,22 +1096,24 @@ static const struct snd_soc_dapm_route wm8903_intercon[] = {
{ "Right Line Output PGA", NULL, "Charge Pump" },
};
-static int wm8903_set_bias_level(struct snd_soc_codec *codec,
+static int wm8903_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_RES_MASK,
WM8903_VMID_RES_50K);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0,
WM8903_POBCTRL | WM8903_ISEL_MASK |
WM8903_STARTUP_BIAS_ENA |
WM8903_BIAS_ENA,
@@ -1114,22 +1121,22 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
(2 << WM8903_ISEL_SHIFT) |
WM8903_STARTUP_BIAS_ENA);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
WM8903_SPK_DISCHARGE,
WM8903_SPK_DISCHARGE);
msleep(33);
- snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8903_POWER_MANAGEMENT_5,
WM8903_SPKL_ENA | WM8903_SPKR_ENA,
WM8903_SPKL_ENA | WM8903_SPKR_ENA);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
WM8903_SPK_DISCHARGE, 0);
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_TIE_ENA |
WM8903_BUFIO_ENA |
WM8903_VMID_IO_ENA |
@@ -1145,57 +1152,57 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
msleep(129);
- snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8903_POWER_MANAGEMENT_5,
WM8903_SPKL_ENA | WM8903_SPKR_ENA,
0);
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_SOFT_MASK, 0);
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_RES_MASK,
WM8903_VMID_RES_50K);
- snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0,
WM8903_BIAS_ENA | WM8903_POBCTRL,
WM8903_BIAS_ENA);
/* By default no bypass paths are enabled so
* enable Class W support.
*/
- dev_dbg(codec->dev, "Enabling Class W\n");
- snd_soc_update_bits(codec, WM8903_CLASS_W_0,
+ dev_dbg(component->dev, "Enabling Class W\n");
+ snd_soc_component_update_bits(component, WM8903_CLASS_W_0,
WM8903_CP_DYN_FREQ |
WM8903_CP_DYN_V,
WM8903_CP_DYN_FREQ |
WM8903_CP_DYN_V);
}
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_RES_MASK,
WM8903_VMID_RES_250K);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0,
WM8903_BIAS_ENA, 0);
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_SOFT_MASK,
2 << WM8903_VMID_SOFT_SHIFT);
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_BUF_ENA, 0);
msleep(290);
- snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0,
WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA |
WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK |
WM8903_VMID_SOFT_MASK |
WM8903_VMID_BUF_ENA, 0);
- snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0,
WM8903_STARTUP_BIAS_ENA, 0);
break;
}
@@ -1206,8 +1213,8 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
wm8903->sysclk = freq;
@@ -1217,22 +1224,22 @@ static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ u16 aif1 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_1);
aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK |
WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif1 |= WM8903_LRCLK_DIR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif1 |= WM8903_BCLK_DIR;
break;
default:
@@ -1296,24 +1303,24 @@ static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+ snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_1, aif1);
return 0;
}
-static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm8903_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
- reg = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
+ reg = snd_soc_component_read(component, WM8903_DAC_DIGITAL_1);
if (mute)
reg |= WM8903_DAC_MUTE;
else
reg &= ~WM8903_DAC_MUTE;
- snd_soc_write(codec, WM8903_DAC_DIGITAL_1, reg);
+ snd_soc_component_write(component, WM8903_DAC_DIGITAL_1, reg);
return 0;
}
@@ -1433,8 +1440,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
int fs = params_rate(params);
int bclk;
int bclk_div;
@@ -1445,12 +1452,12 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
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);
+ u16 aif1 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_1);
+ u16 aif2 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_2);
+ u16 aif3 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_3);
+ u16 clock0 = snd_soc_component_read(component, WM8903_CLOCK_RATES_0);
+ u16 clock1 = snd_soc_component_read(component, WM8903_CLOCK_RATES_1);
+ u16 dac_digital1 = snd_soc_component_read(component, WM8903_DAC_DIGITAL_1);
/* Enable sloping stopband filter for low sample rates */
if (fs <= 24000)
@@ -1469,7 +1476,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
}
}
- dev_dbg(codec->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate);
+ dev_dbg(component->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate);
clock1 &= ~WM8903_SAMPLE_RATE_MASK;
clock1 |= sample_rates[dsp_config].value;
@@ -1495,7 +1502,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- dev_dbg(codec->dev, "MCLK = %dHz, target sample rate = %dHz\n",
+ dev_dbg(component->dev, "MCLK = %dHz, target sample rate = %dHz\n",
wm8903->sysclk, fs);
/* We may not have an MCLK which allows us to generate exactly
@@ -1530,12 +1537,12 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
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",
+ dev_dbg(component->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n",
clk_sys_ratios[clk_config].rate,
clk_sys_ratios[clk_config].mode,
clk_sys_ratios[clk_config].div);
- dev_dbg(codec->dev, "Actual CLK_SYS = %dHz\n", clk_sys);
+ dev_dbg(component->dev, "Actual CLK_SYS = %dHz\n", clk_sys);
/* We may not get quite the right frequency if using
* approximate clocks so look for the closest match that is
@@ -1543,21 +1550,19 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
* 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",
+ dev_dbg(component->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n",
bclk_divs[bclk_div].ratio / 10, bclk,
(clk_sys * 10) / bclk_divs[bclk_div].ratio);
@@ -1565,14 +1570,14 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
aif3 |= bclk / fs;
wm8903->fs = params_rate(params);
- wm8903_set_deemph(codec);
+ wm8903_set_deemph(component);
- 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);
+ snd_soc_component_write(component, WM8903_CLOCK_RATES_0, clock0);
+ snd_soc_component_write(component, WM8903_CLOCK_RATES_1, clock1);
+ snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_1, aif1);
+ snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_2, aif2);
+ snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_3, aif3);
+ snd_soc_component_write(component, WM8903_DAC_DIGITAL_1, dac_digital1);
return 0;
}
@@ -1580,7 +1585,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
/**
* wm8903_mic_detect - Enable microphone detection via the WM8903 IRQ
*
- * @codec: WM8903 codec
+ * @component: WM8903 component
* @jack: jack to report detection events on
* @det: value to report for presence detection
* @shrt: value to report for short detection
@@ -1594,13 +1599,13 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
* 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 wm8903_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack,
int det, int shrt)
{
- struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
int irq_mask = WM8903_MICDET_EINT | WM8903_MICSHRT_EINT;
- dev_dbg(codec->dev, "Enabling microphone detection: %x %x\n",
+ dev_dbg(component->dev, "Enabling microphone detection: %x %x\n",
det, shrt);
/* Store the configuration */
@@ -1614,19 +1619,19 @@ int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
if (shrt)
irq_mask &= ~WM8903_MICSHRT_EINT;
- snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK,
+ snd_soc_component_update_bits(component, WM8903_INTERRUPT_STATUS_1_MASK,
WM8903_MICDET_EINT | WM8903_MICSHRT_EINT,
irq_mask);
if (det || shrt) {
/* Enable mic detection, this may not have been set through
* platform data (eg, if the defaults are OK). */
- snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
+ snd_soc_component_update_bits(component, WM8903_WRITE_SEQUENCER_0,
WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
- snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_MIC_BIAS_CONTROL_0,
WM8903_MICDET_ENA, WM8903_MICDET_ENA);
} else {
- snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8903_MIC_BIAS_CONTROL_0,
WM8903_MICDET_ENA, 0);
}
@@ -1731,9 +1736,10 @@ static irqreturn_t wm8903_irq(int irq, void *data)
static const struct snd_soc_dai_ops wm8903_dai_ops = {
.hw_params = wm8903_hw_params,
- .digital_mute = wm8903_digital_mute,
+ .mute_stream = wm8903_mute,
.set_fmt = wm8903_set_dai_fmt,
.set_sysclk = wm8903_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8903_dai = {
@@ -1753,12 +1759,12 @@ static struct snd_soc_dai_driver wm8903_dai = {
.formats = WM8903_FORMATS,
},
.ops = &wm8903_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8903_resume(struct snd_soc_codec *codec)
+static int wm8903_resume(struct snd_soc_component *component)
{
- struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component);
regcache_sync(wm8903->regmap);
@@ -1766,11 +1772,6 @@ static int wm8903_resume(struct snd_soc_codec *codec)
}
#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)
@@ -1781,7 +1782,7 @@ static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
unsigned int mask, val;
int ret;
@@ -1799,7 +1800,7 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
unsigned int reg;
regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, &reg);
@@ -1810,7 +1811,7 @@ static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
static int wm8903_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
unsigned int mask, val;
int ret;
@@ -1826,16 +1827,18 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip,
return 0;
}
-static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int wm8903_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
- regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset,
- WM8903_GP1_LVL_MASK,
- !!value << WM8903_GP1_LVL_SHIFT);
+ return 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 = {
+static const struct gpio_chip wm8903_template_chip = {
.label = "wm8903",
.owner = THIS_MODULE,
.request = wm8903_gpio_request,
@@ -1860,7 +1863,7 @@ static void wm8903_init_gpio(struct wm8903_priv *wm8903)
else
wm8903->gpio_chip.base = -1;
- ret = gpiochip_add(&wm8903->gpio_chip);
+ ret = gpiochip_add_data(&wm8903->gpio_chip, wm8903);
if (ret != 0)
dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret);
}
@@ -1879,18 +1882,20 @@ 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 snd_soc_component_driver soc_component_dev_wm8903 = {
+ .resume = wm8903_resume,
+ .set_bias_level = wm8903_set_bias_level,
+ .seq_notifier = wm8903_seq_notifier,
+ .controls = wm8903_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8903_snd_controls),
+ .dapm_widgets = wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
+ .dapm_routes = wm8903_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8903_regmap = {
@@ -1901,7 +1906,7 @@ static const struct regmap_config wm8903_regmap = {
.volatile_reg = wm8903_volatile_register,
.readable_reg = wm8903_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8903_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8903_reg_defaults),
};
@@ -1923,7 +1928,7 @@ static int wm8903_set_pdata_irq_trigger(struct i2c_client *i2c,
* We assume the controller imposes no restrictions,
* so we are able to select active-high
*/
- /* Fall-through */
+ fallthrough;
case IRQ_TYPE_LEVEL_HIGH:
pdata->irq_active_low = false;
break;
@@ -1978,8 +1983,7 @@ static int wm8903_set_pdata_from_of(struct i2c_client *i2c,
return 0;
}
-static int wm8903_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8903_i2c_probe(struct i2c_client *i2c)
{
struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8903_priv *wm8903;
@@ -1988,8 +1992,7 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
unsigned int val, irq_pol;
int ret, i;
- wm8903 = devm_kzalloc(&i2c->dev, sizeof(struct wm8903_priv),
- GFP_KERNEL);
+ wm8903 = devm_kzalloc(&i2c->dev, sizeof(*wm8903), GFP_KERNEL);
if (wm8903 == NULL)
return -ENOMEM;
@@ -2010,13 +2013,10 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
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");
+ wm8903->pdata = devm_kzalloc(&i2c->dev, sizeof(*wm8903->pdata),
+ GFP_KERNEL);
+ if (!wm8903->pdata)
return -ENOMEM;
- }
if (i2c->irq) {
ret = wm8903_set_pdata_irq_trigger(i2c, wm8903->pdata);
@@ -2033,6 +2033,23 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
pdata = wm8903->pdata;
+ for (i = 0; i < ARRAY_SIZE(wm8903->supplies); i++)
+ wm8903->supplies[i].supply = wm8903_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
@@ -2116,7 +2133,7 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
if (ret != 0) {
dev_err(wm8903->dev, "Failed to request IRQ: %d\n",
ret);
- return ret;
+ goto err;
}
/* Enable write sequencer interrupts */
@@ -2156,26 +2173,27 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
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);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8903, &wm8903_dai, 1);
if (ret != 0)
goto err;
return 0;
err:
+ regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
return ret;
}
-static int wm8903_i2c_remove(struct i2c_client *client)
+static void wm8903_i2c_remove(struct i2c_client *client)
{
struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
+ regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
if (client->irq)
free_irq(client->irq, wm8903);
wm8903_free_gpio(wm8903);
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
}
static const struct of_device_id wm8903_of_match[] = {
@@ -2185,7 +2203,7 @@ static const struct of_device_id wm8903_of_match[] = {
MODULE_DEVICE_TABLE(of, wm8903_of_match);
static const struct i2c_device_id wm8903_i2c_id[] = {
- { "wm8903", 0 },
+ { "wm8903" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
index 0bb4a647755d..4b036f4b755e 100644
--- a/sound/soc/codecs/wm8903.h
+++ b/sound/soc/codecs/wm8903.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
@@ -15,7 +11,7 @@
#include <linux/i2c.h>
-extern int wm8903_mic_detect(struct snd_soc_codec *codec,
+extern int wm8903_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack,
int det, int shrt);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 8172e499e6ed..4c73a340f25f 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1,19 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -316,31 +311,31 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg)
}
}
-static int wm8904_configure_clocking(struct snd_soc_codec *codec)
+static int wm8904_configure_clocking(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
unsigned int clock0, clock2, rate;
/* Gate the clock while we're updating to avoid misclocking */
- clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
- snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ clock2 = snd_soc_component_read(component, WM8904_CLOCK_RATES_2);
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
WM8904_SYSCLK_SRC, 0);
/* This should be done on init() for bypass paths */
switch (wm8904->sysclk_src) {
case WM8904_CLK_MCLK:
- dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
+ dev_dbg(component->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
clock2 &= ~WM8904_SYSCLK_SRC;
rate = wm8904->mclk_rate;
/* Ensure the FLL is stopped */
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
break;
case WM8904_CLK_FLL:
- dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+ dev_dbg(component->dev, "Using %dHz FLL clock\n",
wm8904->fll_fout);
clock2 |= WM8904_SYSCLK_SRC;
@@ -348,7 +343,7 @@ static int wm8904_configure_clocking(struct snd_soc_codec *codec)
break;
default:
- dev_err(codec->dev, "System clock not configured\n");
+ dev_err(component->dev, "System clock not configured\n");
return -EINVAL;
}
@@ -361,49 +356,49 @@ static int wm8904_configure_clocking(struct snd_soc_codec *codec)
wm8904->sysclk_rate = rate;
}
- snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
clock0);
- snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2);
- dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
+ dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
return 0;
}
-static void wm8904_set_drc(struct snd_soc_codec *codec)
+static void wm8904_set_drc(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct wm8904_pdata *pdata = wm8904->pdata;
int save, i;
/* Save any enables; the configuration should clear them. */
- save = snd_soc_read(codec, WM8904_DRC_0);
+ save = snd_soc_component_read(component, WM8904_DRC_0);
for (i = 0; i < WM8904_DRC_REGS; i++)
- snd_soc_update_bits(codec, WM8904_DRC_0 + i, 0xffff,
+ snd_soc_component_update_bits(component, WM8904_DRC_0 + i, 0xffff,
pdata->drc_cfgs[wm8904->drc_cfg].regs[i]);
/* Reenable the DRC */
- snd_soc_update_bits(codec, WM8904_DRC_0,
+ snd_soc_component_update_bits(component, WM8904_DRC_0,
WM8904_DRC_ENA | WM8904_DRC_DAC_PATH, save);
}
static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct wm8904_pdata *pdata = wm8904->pdata;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (value >= pdata->num_drc_cfgs)
return -EINVAL;
wm8904->drc_cfg = value;
- wm8904_set_drc(codec);
+ wm8904_set_drc(component);
return 0;
}
@@ -411,17 +406,17 @@ static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = wm8904->drc_cfg;
return 0;
}
-static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
+static void wm8904_set_retune_mobile(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct wm8904_pdata *pdata = wm8904->pdata;
int best, best_val, save, i, cfg;
@@ -444,7 +439,7 @@ static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n",
pdata->retune_mobile_cfgs[best].name,
pdata->retune_mobile_cfgs[best].rate,
wm8904->fs);
@@ -452,29 +447,29 @@ static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
/* The EQ will be disabled while reconfiguring it, remember the
* current configuration.
*/
- save = snd_soc_read(codec, WM8904_EQ1);
+ save = snd_soc_component_read(component, WM8904_EQ1);
for (i = 0; i < WM8904_EQ_REGS; i++)
- snd_soc_update_bits(codec, WM8904_EQ1 + i, 0xffff,
+ snd_soc_component_update_bits(component, WM8904_EQ1 + i, 0xffff,
pdata->retune_mobile_cfgs[best].regs[i]);
- snd_soc_update_bits(codec, WM8904_EQ1, WM8904_EQ_ENA, save);
+ snd_soc_component_update_bits(component, WM8904_EQ1, WM8904_EQ_ENA, save);
}
static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct wm8904_pdata *pdata = wm8904->pdata;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (value >= pdata->num_retune_mobile_cfgs)
return -EINVAL;
wm8904->retune_mobile_cfg = value;
- wm8904_set_retune_mobile(codec);
+ wm8904_set_retune_mobile(component);
return 0;
}
@@ -482,8 +477,8 @@ static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg;
@@ -492,9 +487,9 @@ static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
static int deemph_settings[] = { 0, 32000, 44100, 48000 };
-static int wm8904_set_deemph(struct snd_soc_codec *codec)
+static int wm8904_set_deemph(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
int val, i, best;
/* If we're using deemphasis select the nearest available sample
@@ -513,17 +508,17 @@ static int wm8904_set_deemph(struct snd_soc_codec *codec)
val = 0;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+ return snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1,
WM8904_DEEMPH_MASK, val);
}
static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8904->deemph;
return 0;
@@ -532,8 +527,8 @@ static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
@@ -541,7 +536,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
wm8904->deemph = deemph;
- return wm8904_set_deemph(codec);
+ return wm8904_set_deemph(component);
}
static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
@@ -550,18 +545,6 @@ 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"
};
@@ -572,7 +555,7 @@ static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int val;
int ret;
@@ -585,7 +568,7 @@ static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol,
else
val = WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5;
- snd_soc_update_bits(codec, WM8904_ADC_TEST_0,
+ snd_soc_component_update_bits(component, WM8904_ADC_TEST_0,
WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5,
val);
@@ -596,9 +579,6 @@ 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),
@@ -673,8 +653,8 @@ static int cp_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -685,11 +665,11 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
*/
switch (wm8904->sysclk_src) {
case WM8904_CLK_FLL:
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_OSC_ENA,
WM8904_FLL_OSC_ENA);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_ENA,
WM8904_FLL_ENA);
break;
@@ -700,7 +680,7 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
break;
}
@@ -711,12 +691,13 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
int reg, val;
int dcs_mask;
int dcs_l, dcs_r;
int dcs_l_reg, dcs_r_reg;
+ int an_out_reg;
int timeout;
int pwr_reg;
@@ -732,6 +713,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1;
dcs_r_reg = WM8904_DC_SERVO_8;
dcs_l_reg = WM8904_DC_SERVO_9;
+ an_out_reg = WM8904_ANALOGUE_OUT1_LEFT;
dcs_l = 0;
dcs_r = 1;
break;
@@ -740,6 +722,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3;
dcs_r_reg = WM8904_DC_SERVO_6;
dcs_l_reg = WM8904_DC_SERVO_7;
+ an_out_reg = WM8904_ANALOGUE_OUT2_LEFT;
dcs_l = 2;
dcs_r = 3;
break;
@@ -751,43 +734,43 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Power on the PGAs */
- snd_soc_update_bits(codec, pwr_reg,
+ snd_soc_component_update_bits(component, pwr_reg,
WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA);
/* Power on the amplifier */
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
WM8904_HPL_ENA | WM8904_HPR_ENA,
WM8904_HPL_ENA | WM8904_HPR_ENA);
/* Enable the first stage */
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY,
WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY);
/* Power up the DC servo */
- snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+ snd_soc_component_update_bits(component, WM8904_DC_SERVO_0,
dcs_mask, dcs_mask);
/* Either calibrate the DC servo or restore cached state
* if we have that.
*/
if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) {
- dev_dbg(codec->dev, "Restoring DC servo state\n");
+ dev_dbg(component->dev, "Restoring DC servo state\n");
- snd_soc_write(codec, dcs_l_reg,
+ snd_soc_component_write(component, dcs_l_reg,
wm8904->dcs_state[dcs_l]);
- snd_soc_write(codec, dcs_r_reg,
+ snd_soc_component_write(component, dcs_r_reg,
wm8904->dcs_state[dcs_r]);
- snd_soc_write(codec, WM8904_DC_SERVO_1, dcs_mask);
+ snd_soc_component_write(component, WM8904_DC_SERVO_1, dcs_mask);
timeout = 20;
} else {
- dev_dbg(codec->dev, "Calibrating DC servo\n");
+ dev_dbg(component->dev, "Calibrating DC servo\n");
- snd_soc_write(codec, WM8904_DC_SERVO_1,
+ snd_soc_component_write(component, WM8904_DC_SERVO_1,
dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT);
timeout = 500;
@@ -796,7 +779,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
/* Wait for DC servo to complete */
dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT;
do {
- val = snd_soc_read(codec, WM8904_DC_SERVO_READBACK_0);
+ val = snd_soc_component_read(component, WM8904_DC_SERVO_READBACK_0);
if ((val & dcs_mask) == dcs_mask)
break;
@@ -804,19 +787,23 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
} while (--timeout);
if ((val & dcs_mask) != dcs_mask)
- dev_warn(codec->dev, "DC servo timed out\n");
+ dev_warn(component->dev, "DC servo timed out\n");
else
- dev_dbg(codec->dev, "DC servo ready\n");
+ dev_dbg(component->dev, "DC servo ready\n");
/* Enable the output stage */
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP);
+
+ /* Update volume, requires PGA to be powered */
+ val = snd_soc_component_read(component, an_out_reg);
+ snd_soc_component_write(component, an_out_reg, val);
break;
case SND_SOC_DAPM_POST_PMU:
/* Unshort the output itself */
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
WM8904_HPL_RMV_SHORT |
WM8904_HPR_RMV_SHORT,
WM8904_HPL_RMV_SHORT |
@@ -826,7 +813,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMD:
/* Short the output */
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
WM8904_HPL_RMV_SHORT |
WM8904_HPR_RMV_SHORT, 0);
break;
@@ -834,21 +821,21 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
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);
+ wm8904->dcs_state[dcs_l] = snd_soc_component_read(component, dcs_l_reg);
+ wm8904->dcs_state[dcs_r] = snd_soc_component_read(component, dcs_r_reg);
- snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+ snd_soc_component_update_bits(component, WM8904_DC_SERVO_0,
dcs_mask, 0);
/* Disable the amplifier input and output stages */
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
WM8904_HPL_ENA | WM8904_HPR_ENA |
WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY |
WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
0);
/* PGAs too */
- snd_soc_update_bits(codec, pwr_reg,
+ snd_soc_component_update_bits(component, pwr_reg,
WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
0);
break;
@@ -857,6 +844,30 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static const char * const dmic_text[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(dmic_enum, WM8904_DIGITAL_MICROPHONE_0,
+ WM8904_DMIC_SRC_SHIFT, dmic_text);
+
+static const struct snd_kcontrol_new dmic_mux =
+ SOC_DAPM_ENUM("DMIC Mux", dmic_enum);
+
+static const char * const cin_text[] = {
+ "ADC", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(cin_enum, WM8904_DIGITAL_MICROPHONE_0,
+ WM8904_DMIC_ENA_SHIFT, cin_text);
+
+static const struct snd_kcontrol_new cin_mux =
+ SOC_DAPM_ENUM("Capture Input", cin_enum);
+
+static const char *input_mode_text[] = {
+ "Single-Ended", "Differential Line", "Differential Mic"
+};
+
static const char *lin_text[] = {
"IN1L", "IN2L", "IN3L"
};
@@ -871,7 +882,14 @@ 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);
+ SOC_DAPM_ENUM("Left Capture Inverting Mux", lin_inv_enum);
+
+static SOC_ENUM_SINGLE_DECL(lin_mode_enum,
+ WM8904_ANALOGUE_LEFT_INPUT_1, 0,
+ input_mode_text);
+
+static const struct snd_kcontrol_new lin_mode =
+ SOC_DAPM_ENUM("Left Capture Mode", lin_mode_enum);
static const char *rin_text[] = {
"IN1R", "IN2R", "IN3R"
@@ -887,7 +905,14 @@ 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);
+ SOC_DAPM_ENUM("Right Capture Inverting Mux", rin_inv_enum);
+
+static SOC_ENUM_SINGLE_DECL(rin_mode_enum,
+ WM8904_ANALOGUE_RIGHT_INPUT_1, 0,
+ input_mode_text);
+
+static const struct snd_kcontrol_new rin_mode =
+ SOC_DAPM_ENUM("Right Capture Mode", rin_mode_enum);
static const char *aif_text[] = {
"Left", "Right"
@@ -937,9 +962,11 @@ SND_SOC_DAPM_SUPPLY("MICBIAS", WM8904_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lin_mux),
SND_SOC_DAPM_MUX("Left Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
&lin_inv_mux),
+SND_SOC_DAPM_MUX("Left Capture Mode", SND_SOC_NOPM, 0, 0, &lin_mode),
SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rin_mux),
SND_SOC_DAPM_MUX("Right Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
&rin_inv_mux),
+SND_SOC_DAPM_MUX("Right Capture Mode", SND_SOC_NOPM, 0, 0, &rin_mode),
SND_SOC_DAPM_PGA("Left Capture PGA", WM8904_POWER_MANAGEMENT_0, 1, 0,
NULL, 0),
@@ -956,6 +983,15 @@ 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_dmic_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &dmic_mux),
+};
+
+static const struct snd_soc_dapm_widget wm8904_cin_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("Left Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux),
+SND_SOC_DAPM_MUX("Right Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux),
+};
+
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),
@@ -1062,6 +1098,12 @@ static const struct snd_soc_dapm_route adc_intercon[] = {
{ "Left Capture Inverting Mux", "IN2L", "IN2L" },
{ "Left Capture Inverting Mux", "IN3L", "IN3L" },
+ { "Left Capture Mode", "Single-Ended", "Left Capture Inverting Mux" },
+ { "Left Capture Mode", "Differential Line", "Left Capture Mux" },
+ { "Left Capture Mode", "Differential Line", "Left Capture Inverting Mux" },
+ { "Left Capture Mode", "Differential Mic", "Left Capture Mux" },
+ { "Left Capture Mode", "Differential Mic", "Left Capture Inverting Mux" },
+
{ "Right Capture Mux", "IN1R", "IN1R" },
{ "Right Capture Mux", "IN2R", "IN2R" },
{ "Right Capture Mux", "IN3R", "IN3R" },
@@ -1070,11 +1112,14 @@ static const struct snd_soc_dapm_route adc_intercon[] = {
{ "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 Mode", "Single-Ended", "Right Capture Inverting Mux" },
+ { "Right Capture Mode", "Differential Line", "Right Capture Mux" },
+ { "Right Capture Mode", "Differential Line", "Right Capture Inverting Mux" },
+ { "Right Capture Mode", "Differential Mic", "Right Capture Mux" },
+ { "Right Capture Mode", "Differential Mic", "Right Capture Inverting Mux" },
- { "Right Capture PGA", NULL, "Right Capture Mux" },
- { "Right Capture PGA", NULL, "Right Capture Inverting Mux" },
+ { "Left Capture PGA", NULL, "Left Capture Mode" },
+ { "Right Capture PGA", NULL, "Right Capture Mode" },
{ "AIFOUTL Mux", "Left", "ADCL" },
{ "AIFOUTL Mux", "Right", "ADCR" },
@@ -1085,12 +1130,45 @@ static const struct snd_soc_dapm_route adc_intercon[] = {
{ "AIFOUTR", NULL, "AIFOUTR Mux" },
{ "ADCL", NULL, "CLK_DSP" },
- { "ADCL", NULL, "Left Capture PGA" },
-
{ "ADCR", NULL, "CLK_DSP" },
+};
+
+/* No DMICs, always connect PGAs */
+static const struct snd_soc_dapm_route cin_nodmic_con[] = {
+ { "ADCL", NULL, "Left Capture PGA" },
{ "ADCR", NULL, "Right Capture PGA" },
};
+/* DMIC system in use: mux between ADC and DMICDAT1, 2 or both */
+static const struct snd_soc_dapm_route cin_adc_dmic_con[] = {
+ { "Left Capture Input", "ADC", "Left Capture PGA" },
+ { "Right Capture Input", "ADC", "Right Capture PGA" },
+
+ { "ADCL", NULL, "Left Capture Input" },
+ { "ADCR", NULL, "Right Capture Input" },
+};
+
+/* IN1L as DMICDAT1 */
+static const struct snd_soc_dapm_route cin_dmic1_con[] = {
+ { "Left Capture Input", "DMIC", "IN1L" },
+ { "Right Capture Input", "DMIC", "IN1L" },
+};
+
+/* IN1R as DMICDAT2 */
+static const struct snd_soc_dapm_route cin_dmic2_con[] = {
+ { "Left Capture Input", "DMIC", "IN1R" },
+ { "Right Capture Input", "DMIC", "IN1R" },
+};
+
+/* DMICDAT1 and DMICDAT2: mux between them, ADC still used for IN2 and IN3 */
+static const struct snd_soc_dapm_route cin_2dmics_con[] = {
+ { "DMIC Mux", "DMIC1", "IN1L" },
+ { "DMIC Mux", "DMIC2", "IN1R" },
+
+ { "Left Capture Input", "DMIC", "DMIC Mux" },
+ { "Right Capture Input", "DMIC", "DMIC Mux" },
+};
+
static const struct snd_soc_dapm_route dac_intercon[] = {
{ "DACL Mux", "Left", "AIFINL" },
{ "DACL Mux", "Right", "AIFINR" },
@@ -1165,10 +1243,10 @@ static const struct snd_soc_dapm_route wm8912_intercon[] = {
{ "LINER PGA", NULL, "DACR" },
};
-static int wm8904_add_widgets(struct snd_soc_codec *codec)
+static int wm8904_add_widgets(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets,
ARRAY_SIZE(wm8904_core_dapm_widgets));
@@ -1177,11 +1255,11 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec)
switch (wm8904->devtype) {
case WM8904:
- snd_soc_add_codec_controls(codec, wm8904_adc_snd_controls,
+ snd_soc_add_component_controls(component, wm8904_adc_snd_controls,
ARRAY_SIZE(wm8904_adc_snd_controls));
- snd_soc_add_codec_controls(codec, wm8904_dac_snd_controls,
+ snd_soc_add_component_controls(component, wm8904_dac_snd_controls,
ARRAY_SIZE(wm8904_dac_snd_controls));
- snd_soc_add_codec_controls(codec, wm8904_snd_controls,
+ snd_soc_add_component_controls(component, wm8904_snd_controls,
ARRAY_SIZE(wm8904_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8904_adc_dapm_widgets,
@@ -1200,7 +1278,7 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec)
break;
case WM8912:
- snd_soc_add_codec_controls(codec, wm8904_dac_snd_controls,
+ snd_soc_add_component_controls(component, wm8904_dac_snd_controls,
ARRAY_SIZE(wm8904_dac_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets,
@@ -1278,8 +1356,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
int ret, i, best, best_val, cur_val;
unsigned int aif1 = 0;
unsigned int aif2 = 0;
@@ -1290,7 +1368,7 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
/* 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",
+ dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n",
wm8904->tdm_slots, wm8904->tdm_width);
wm8904->bclk = snd_soc_calc_bclk(wm8904->fs,
wm8904->tdm_width, 2,
@@ -1316,9 +1394,9 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
}
- dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8904->bclk);
+ dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8904->bclk);
- ret = wm8904_configure_clocking(codec);
+ ret = wm8904_configure_clocking(component);
if (ret != 0)
return ret;
@@ -1334,7 +1412,7 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
best_val = cur_val;
}
}
- dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n",
clk_sys_rates[best].ratio);
clock1 |= (clk_sys_rates[best].clk_sys_rate
<< WM8904_CLK_SYS_RATE_SHIFT);
@@ -1350,7 +1428,7 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
best_val = cur_val;
}
}
- dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n",
sample_rates[best].rate);
clock1 |= (sample_rates[best].sample_rate
<< WM8904_SAMPLE_RATE_SHIFT);
@@ -1373,78 +1451,50 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
}
}
wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div;
- dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
bclk_divs[best].div, wm8904->bclk);
aif2 |= bclk_divs[best].bclk_div;
/* LRCLK is a simple fraction of BCLK */
- dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8904->bclk / wm8904->fs);
+ dev_dbg(component->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,
+ snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1,
WM8904_DAC_SB_FILT, dac_digital1);
- snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+ snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1,
WM8904_AIF_WL_MASK, aif1);
- snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_2,
+ snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_2,
WM8904_BCLK_DIV_MASK, aif2);
- snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+ snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_3,
WM8904_LRCLK_RATE_MASK, aif3);
- snd_soc_update_bits(codec, WM8904_CLOCK_RATES_1,
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_1,
WM8904_SAMPLE_RATE_MASK |
WM8904_CLK_SYS_RATE_MASK, clock1);
/* Update filters for the new settings */
- wm8904_set_retune_mobile(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);
+ wm8904_set_retune_mobile(component);
+ wm8904_set_deemph(component);
return 0;
}
static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int aif1 = 0;
unsigned int aif3 = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif3 |= WM8904_LRCLK_DIR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif1 |= WM8904_BCLK_DIR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif1 |= WM8904_BCLK_DIR;
aif3 |= WM8904_LRCLK_DIR;
break;
@@ -1455,6 +1505,7 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif1 |= 0x3 | WM8904_AIF_LRCLK_INV;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x3;
break;
@@ -1508,10 +1559,10 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+ snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1,
WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV |
WM8904_AIF_FMT_MASK | WM8904_BCLK_DIR, aif1);
- snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+ snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_3,
WM8904_LRCLK_DIR, aif3);
return 0;
@@ -1521,8 +1572,8 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
int aif1 = 0;
/* Don't need to validate anything if we're turning off TDM */
@@ -1560,7 +1611,7 @@ out:
wm8904->tdm_width = slot_width;
wm8904->tdm_slots = slots / 2;
- snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+ snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1,
WM8904_AIFADC_TDM | WM8904_AIFADC_TDM_CHAN |
WM8904_AIFDAC_TDM | WM8904_AIFDAC_TDM_CHAN, aif1);
@@ -1678,8 +1729,8 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
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 snd_soc_component *component = dai->component;
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct _fll_div fll_div;
int ret, val;
int clock2, fll1;
@@ -1689,19 +1740,19 @@ static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
Fout == wm8904->fll_fout)
return 0;
- clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+ clock2 = snd_soc_component_read(component, WM8904_CLOCK_RATES_2);
if (Fout == 0) {
- dev_dbg(codec->dev, "FLL disabled\n");
+ dev_dbg(component->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,
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
WM8904_CLK_SYS_ENA, 0);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
goto out;
@@ -1718,7 +1769,7 @@ static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
break;
case WM8904_FLL_FREE_RUNNING:
- dev_dbg(codec->dev, "Using free running FLL\n");
+ dev_dbg(component->dev, "Using free running FLL\n");
/* Force 12MHz and output/4 for now */
Fout = 12000000;
Fref = 12000000;
@@ -1728,20 +1779,20 @@ static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
break;
default:
- dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ dev_err(component->dev, "Unknown FLL ID %d\n", fll_id);
return -EINVAL;
}
/* Save current state then disable the FLL and SYSCLK to avoid
* misclocking */
- fll1 = snd_soc_read(codec, WM8904_FLL_CONTROL_1);
- snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ fll1 = snd_soc_component_read(component, WM8904_FLL_CONTROL_1);
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
WM8904_CLK_SYS_ENA, 0);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
/* Unlock forced oscilator control to switch it on/off */
- snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+ snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1,
WM8904_USER_KEY, WM8904_USER_KEY);
if (fll_id == WM8904_FLL_FREE_RUNNING) {
@@ -1750,24 +1801,24 @@ static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
val = 0;
}
- snd_soc_update_bits(codec, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
+ snd_soc_component_update_bits(component, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
val);
- snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
WM8904_FLL_CLK_REF_SRC_MASK, 2);
break;
}
@@ -1776,47 +1827,97 @@ static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
val = WM8904_FLL_FRACN_ENA;
else
val = 0;
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_FRACN_ENA, val);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_2,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_2,
WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK,
(fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) |
(fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT));
- snd_soc_write(codec, WM8904_FLL_CONTROL_3, fll_div.k);
+ snd_soc_component_write(component, WM8904_FLL_CONTROL_3, fll_div.k);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
fll_div.n << WM8904_FLL_N_SHIFT);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
WM8904_FLL_CLK_REF_DIV_MASK,
fll_div.fll_clk_ref_div
<< WM8904_FLL_CLK_REF_DIV_SHIFT);
- dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+ dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
wm8904->fll_fref = Fref;
wm8904->fll_fout = Fout;
wm8904->fll_src = source;
/* Enable the FLL if it was previously active */
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
WM8904_FLL_OSC_ENA, fll1);
- snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
WM8904_CLK_SYS_ENA, clock2);
return 0;
}
-static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned long mclk_freq;
+ int ret;
+
+ switch (clk_id) {
+ case WM8904_CLK_AUTO:
+ /* We don't have any rate constraints, so just ignore the
+ * request to disable constraining.
+ */
+ if (!freq)
+ return 0;
+
+ mclk_freq = clk_get_rate(priv->mclk);
+ /* enable FLL if a different sysclk is desired */
+ if (mclk_freq != freq) {
+ priv->sysclk_src = WM8904_CLK_FLL;
+ ret = wm8904_set_fll(dai, WM8904_FLL_MCLK,
+ WM8904_FLL_MCLK,
+ mclk_freq, freq);
+ if (ret)
+ return ret;
+ break;
+ }
+ clk_id = WM8904_CLK_MCLK;
+ fallthrough;
+
+ case WM8904_CLK_MCLK:
+ priv->sysclk_src = clk_id;
+ priv->mclk_rate = freq;
+ break;
+
+ case WM8904_CLK_FLL:
+ priv->sysclk_src = clk_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ wm8904_configure_clocking(component);
+
+ return 0;
+}
+
+static int wm8904_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int val;
if (mute)
@@ -1824,55 +1925,62 @@ static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
val = 0;
- snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val);
+ snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val);
return 0;
}
-static int wm8904_set_bias_level(struct snd_soc_codec *codec,
+static int wm8904_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- ret = clk_prepare_enable(wm8904->mclk);
- if (ret)
- return ret;
break;
case SND_SOC_BIAS_PREPARE:
/* VMID resistance 2*50k */
- snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_ISEL_MASK, 2 << WM8904_ISEL_SHIFT);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
wm8904->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
}
+ ret = clk_prepare_enable(wm8904->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable MCLK: %d\n", ret);
+ regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
+ wm8904->supplies);
+ return ret;
+ }
+
regcache_cache_only(wm8904->regmap, false);
regcache_sync(wm8904->regmap);
/* Enable bias */
- snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_BIAS_ENA, WM8904_BIAS_ENA);
/* Enable VMID, VMID buffering, 2*5k resistance */
- snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0,
WM8904_VMID_ENA |
WM8904_VMID_RES_MASK,
WM8904_VMID_ENA |
@@ -1883,24 +1991,25 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,
}
/* Maintain VMID with 2*250k */
- snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0,
WM8904_VMID_RES_MASK,
0x2 << WM8904_VMID_RES_SHIFT);
/* Bias current *0.5 */
- snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_ISEL_MASK, 0);
break;
case SND_SOC_BIAS_OFF:
/* Turn off VMID */
- snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0,
WM8904_VMID_RES_MASK | WM8904_VMID_ENA, 0);
/* Stop bias generation */
- snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_BIAS_ENA, 0);
+ snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0);
regcache_cache_only(wm8904->regmap, true);
regcache_mark_dirty(wm8904->regmap);
@@ -1923,7 +2032,8 @@ static const struct snd_soc_dai_ops wm8904_dai_ops = {
.set_tdm_slot = wm8904_set_tdm_slot,
.set_pll = wm8904_set_fll,
.hw_params = wm8904_hw_params,
- .digital_mute = wm8904_digital_mute,
+ .mute_stream = wm8904_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8904_dai = {
@@ -1943,12 +2053,12 @@ static struct snd_soc_dai_driver wm8904_dai = {
.formats = WM8904_FORMATS,
},
.ops = &wm8904_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec)
+static void wm8904_handle_retune_mobile_pdata(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct wm8904_pdata *pdata = wm8904->pdata;
struct snd_kcontrol_new control =
SOC_ENUM_EXT("EQ Mode",
@@ -1991,31 +2101,83 @@ static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec)
wm8904->retune_mobile_texts = t;
}
- dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n",
wm8904->num_retune_mobile_texts);
wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts;
wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts;
- ret = snd_soc_add_codec_controls(codec, &control, 1);
+ ret = snd_soc_add_component_controls(component, &control, 1);
if (ret != 0)
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to add ReTune Mobile control: %d\n", ret);
}
-static void wm8904_handle_pdata(struct snd_soc_codec *codec)
+static void wm8904_handle_dmic_pdata(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ unsigned int dmic_src;
+
+ if (!pdata->in1l_as_dmicdat1 && !pdata->in1r_as_dmicdat2) {
+ snd_soc_dapm_add_routes(dapm, cin_nodmic_con,
+ ARRAY_SIZE(cin_nodmic_con));
+ snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
+ WM8904_DMIC_ENA_MASK, 0);
+ return;
+ }
+
+ /* Need a control and routing to switch between DMIC and ADC */
+ snd_soc_dapm_new_controls(dapm, wm8904_cin_dapm_widgets,
+ ARRAY_SIZE(wm8904_cin_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, cin_adc_dmic_con,
+ ARRAY_SIZE(cin_adc_dmic_con));
+
+ if (pdata->in1l_as_dmicdat1 && pdata->in1r_as_dmicdat2) {
+ /* Need a control and routing to mux between DMICDAT1 and 2 */
+ dev_dbg(component->dev, "DMICDAT1 and DMICDAT2 in use\n");
+ snd_soc_dapm_new_controls(dapm, wm8904_dmic_dapm_widgets,
+ ARRAY_SIZE(wm8904_dmic_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, cin_2dmics_con,
+ ARRAY_SIZE(cin_2dmics_con));
+ return;
+ }
+
+ /* Either DMICDAT1 or DMICDAT2 is in use, not both */
+ if (pdata->in1l_as_dmicdat1) {
+ dmic_src = 0;
+ snd_soc_dapm_add_routes(dapm, cin_dmic1_con,
+ ARRAY_SIZE(cin_dmic1_con));
+ } else {
+ dmic_src = 1;
+ snd_soc_dapm_add_routes(dapm, cin_dmic2_con,
+ ARRAY_SIZE(cin_dmic2_con));
+ }
+ dev_dbg(component->dev, "DMIC_SRC (0 or 1): %d\n", dmic_src);
+ snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0,
+ WM8904_DMIC_SRC_MASK,
+ dmic_src << WM8904_DMIC_SRC_SHIFT);
+}
+
+static void wm8904_handle_pdata(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
struct wm8904_pdata *pdata = wm8904->pdata;
int ret, i;
if (!pdata) {
- snd_soc_add_codec_controls(codec, wm8904_eq_controls,
- ARRAY_SIZE(wm8904_eq_controls));
+ snd_soc_dapm_add_routes(dapm, cin_nodmic_con,
+ ARRAY_SIZE(cin_nodmic_con));
+ snd_soc_add_component_controls(component, wm8904_eq_controls,
+ ARRAY_SIZE(wm8904_eq_controls));
return;
}
- dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+ wm8904_handle_dmic_pdata(component);
+
+ dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
if (pdata->num_drc_cfgs) {
struct snd_kcontrol_new control =
@@ -2023,8 +2185,9 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec)
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);
+ wm8904->drc_texts = kmalloc_array(pdata->num_drc_cfgs,
+ sizeof(char *),
+ GFP_KERNEL);
if (!wm8904->drc_texts)
return;
@@ -2034,28 +2197,28 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec)
wm8904->drc_enum.items = pdata->num_drc_cfgs;
wm8904->drc_enum.texts = wm8904->drc_texts;
- ret = snd_soc_add_codec_controls(codec, &control, 1);
+ ret = snd_soc_add_component_controls(component, &control, 1);
if (ret != 0)
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to add DRC mode control: %d\n", ret);
- wm8904_set_drc(codec);
+ wm8904_set_drc(component);
}
- dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+ dev_dbg(component->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs);
if (pdata->num_retune_mobile_cfgs)
- wm8904_handle_retune_mobile_pdata(codec);
+ wm8904_handle_retune_mobile_pdata(component);
else
- snd_soc_add_codec_controls(codec, wm8904_eq_controls,
+ snd_soc_add_component_controls(component, wm8904_eq_controls,
ARRAY_SIZE(wm8904_eq_controls));
}
-static int wm8904_probe(struct snd_soc_codec *codec)
+static int wm8904_probe(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
switch (wm8904->devtype) {
case WM8904:
@@ -2064,33 +2227,33 @@ static int wm8904_probe(struct snd_soc_codec *codec)
memset(&wm8904_dai.capture, 0, sizeof(wm8904_dai.capture));
break;
default:
- dev_err(codec->dev, "Unknown device type %d\n",
+ dev_err(component->dev, "Unknown device type %d\n",
wm8904->devtype);
return -EINVAL;
}
- wm8904_handle_pdata(codec);
+ wm8904_add_widgets(component);
- wm8904_add_widgets(codec);
+ /* This can add dependent widgets, so it is done after add_widgets */
+ wm8904_handle_pdata(component);
return 0;
}
-static int wm8904_remove(struct snd_soc_codec *codec)
+static void wm8904_remove(struct snd_soc_component *component)
{
- struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
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 snd_soc_component_driver soc_component_dev_wm8904 = {
+ .probe = wm8904_probe,
+ .remove = wm8904_remove,
+ .set_bias_level = wm8904_set_bias_level,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8904_regmap = {
@@ -2101,22 +2264,19 @@ static const struct regmap_config wm8904_regmap = {
.volatile_reg = wm8904_volatile_register,
.readable_reg = wm8904_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.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,
+ .data = (void *)WM8904,
}, {
.compatible = "wlf,wm8912",
- .data = &wm8912_data,
+ .data = (void *)WM8912,
}, {
/* sentinel */
}
@@ -2124,8 +2284,187 @@ static const struct of_device_id wm8904_of_match[] = {
MODULE_DEVICE_TABLE(of, wm8904_of_match);
#endif
-static int wm8904_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+/**
+ * wm8904_read_cfg_reg_arr() - Reads a subarray from a DT u16 array
+ *
+ * @np: pointer to the device_node struct
+ * @regs_property: DT property of interest
+ * @size: size of subarrays within the array
+ * @idx: index of the subarray of interest
+ * @out: output
+ *
+ * Helper to read a subarray from a DT uint16-array,
+ * divided into equally sized arrays of size `size`
+ *
+ * Subset starts at `idx * size` and is of size `size`
+ *
+ * Return: 0 on success, negative error code otherwise
+ */
+static int wm8904_read_cfg_reg_arr(const struct device_node *np,
+ const char * const regs_property,
+ int size, int idx,
+ u16 * const out)
+{
+ int i, offset, ret;
+
+ offset = idx * size;
+
+ for (i = 0; i < size; i++) {
+ ret = of_property_read_u16_index(np, regs_property, i + offset, &out[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int wm8904_parse_retune_cfg_regs(const struct device_node *np,
+ struct wm8904_pdata *pdata, int cfg_idx)
+{
+ return wm8904_read_cfg_reg_arr(np, "wlf,retune-mobile-cfg-regs",
+ WM8904_EQ_REGS, cfg_idx,
+ &pdata->retune_mobile_cfgs[cfg_idx].regs[0]);
+}
+
+static int wm8904_parse_drc_cfg_regs(const struct device_node *np,
+ struct wm8904_pdata *pdata, int cfg_idx)
+{
+ return wm8904_read_cfg_reg_arr(np, "wlf,drc-cfg-regs",
+ WM8904_DRC_REGS, cfg_idx,
+ &pdata->drc_cfgs[cfg_idx].regs[0]);
+}
+
+static int wm8904_parse_drc_cfg_from_of(struct i2c_client *i2c,
+ struct wm8904_pdata *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ int i, n_cfgs;
+
+ n_cfgs = of_property_count_strings(np, "wlf,drc-cfg-names");
+ if (n_cfgs == -EINVAL)
+ return 0;
+
+ if (n_cfgs <= 0) {
+ dev_err(&i2c->dev, "Could not get wlf,drc-cfg-names length: %d",
+ n_cfgs);
+ return n_cfgs;
+ }
+
+ pdata->drc_cfgs = devm_kzalloc(&i2c->dev,
+ n_cfgs * sizeof(struct wm8904_drc_cfg),
+ GFP_KERNEL);
+ if (!pdata->drc_cfgs)
+ return -ENOMEM;
+
+ for (i = 0; i < n_cfgs; i++) {
+ if (wm8904_parse_drc_cfg_regs(np, pdata, i)) {
+ dev_err(&i2c->dev,
+ "Invalid 'wlf,drc-cfg-regs[%i,:]'\n", i);
+ return -EINVAL;
+ }
+
+ if (of_property_read_string_index(np, "wlf,drc-cfg-names", i,
+ &pdata->drc_cfgs[i].name)) {
+ dev_err(&i2c->dev,
+ "Invalid 'wlf,drc-cfg-names[%i]'\n", i);
+ return -EINVAL;
+ }
+ }
+
+ pdata->num_drc_cfgs = n_cfgs;
+ return 0;
+}
+
+static int wm8904_parse_retune_cfg_from_of(struct i2c_client *i2c,
+ struct wm8904_pdata *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ int i, n_cfgs;
+
+ n_cfgs = of_property_count_strings(np, "wlf,retune-mobile-cfg-names");
+ if (n_cfgs == -EINVAL)
+ return 0;
+
+ if (n_cfgs <= 0) {
+ dev_err(&i2c->dev,
+ "Could not get wlf,retune-mobile-cfg-names length: %d",
+ n_cfgs);
+ return n_cfgs;
+ }
+
+ pdata->retune_mobile_cfgs = devm_kzalloc(&i2c->dev,
+ n_cfgs * sizeof(struct wm8904_retune_mobile_cfg),
+ GFP_KERNEL);
+ if (!pdata->retune_mobile_cfgs)
+ return -ENOMEM;
+
+ for (i = 0; i < n_cfgs; i++) {
+ if (wm8904_parse_retune_cfg_regs(np, pdata, i)) {
+ dev_err(&i2c->dev,
+ "Invalid 'wlf,retune-mobile-cfg-regs[%i,:]'\n", i);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_index(np, "wlf,retune-mobile-cfg-hz", i,
+ &pdata->retune_mobile_cfgs[i].rate)) {
+ dev_err(&i2c->dev,
+ "Invalid 'wlf,retune-mobile-cfg-hz[%i]'\n", i);
+ return -EINVAL;
+ }
+
+ if (of_property_read_string_index(np, "wlf,retune-mobile-cfg-names", i,
+ &pdata->retune_mobile_cfgs[i].name)) {
+ dev_err(&i2c->dev,
+ "Invalid 'wlf,retune-mobile-cfg-names[%i]'\n", i);
+ return -EINVAL;
+ }
+ }
+
+ pdata->num_retune_mobile_cfgs = n_cfgs;
+ return 0;
+}
+
+static int wm8904_set_pdata_from_of(struct i2c_client *i2c,
+ struct wm8904_priv *wm8904)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ struct wm8904_pdata *pdata;
+ int ret, i;
+
+ pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->in1l_as_dmicdat1 =
+ of_property_read_bool(np, "wlf,in1l-as-dmicdat1");
+
+ pdata->in1r_as_dmicdat2 =
+ of_property_read_bool(np, "wlf,in1r-as-dmicdat2");
+
+ /* If absent, default to 0xFFFF for GPIO config (i.e.: don't set) */
+ for (i = 0; i < WM8904_GPIO_REGS; i++)
+ pdata->gpio_cfg[i] = 0xFFFF;
+
+ of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_cfg,
+ ARRAY_SIZE(pdata->gpio_cfg));
+
+ of_property_read_u32_array(np, "wlf,micbias-cfg", pdata->mic_cfg,
+ ARRAY_SIZE(pdata->mic_cfg));
+
+ ret = wm8904_parse_drc_cfg_from_of(i2c, pdata);
+ if (ret)
+ return ret;
+
+ ret = wm8904_parse_retune_cfg_from_of(i2c, pdata);
+ if (ret)
+ return ret;
+
+ wm8904->pdata = pdata;
+ return 0;
+}
+
+static const struct i2c_device_id wm8904_i2c_id[];
+
+static int wm8904_i2c_probe(struct i2c_client *i2c)
{
struct wm8904_priv *wm8904;
unsigned int val;
@@ -2151,20 +2490,20 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
return ret;
}
- if (i2c->dev.of_node) {
- const struct of_device_id *match;
+ wm8904->devtype = (uintptr_t)i2c_get_match_data(i2c);
- match = of_match_node(wm8904_of_match, i2c->dev.of_node);
- if (match == NULL)
- return -EINVAL;
- wm8904->devtype = *((enum wm8904_type *)match->data);
+ i2c_set_clientdata(i2c, wm8904);
+
+ if (i2c->dev.of_node) {
+ ret = wm8904_set_pdata_from_of(i2c, wm8904);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to set platform data from of: %d\n", ret);
+ return ret;
+ }
} else {
- wm8904->devtype = id->driver_data;
+ wm8904->pdata = i2c->dev.platform_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];
@@ -2234,7 +2573,8 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
/* Apply configuration from the platform data. */
if (wm8904->pdata) {
for (i = 0; i < WM8904_GPIO_REGS; i++) {
- if (!wm8904->pdata->gpio_cfg[i])
+ /* 0xFFFF in this config means "don't touch" */
+ if (wm8904->pdata->gpio_cfg[i] == 0xffff)
continue;
regmap_update_bits(wm8904->regmap,
@@ -2261,12 +2601,15 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(wm8904->regmap, WM8904_BIAS_CONTROL_0,
WM8904_POBCTRL, 0);
+ /* Fill the cache for the ADC test register */
+ regmap_read(wm8904->regmap, WM8904_ADC_TEST_0, &val);
+
/* Can leave the device powered off until we need it */
regcache_cache_only(wm8904->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8904, &wm8904_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8904, &wm8904_dai, 1);
if (ret != 0)
return ret;
@@ -2277,12 +2620,6 @@ err_enable:
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 },
@@ -2296,8 +2633,7 @@ static struct i2c_driver wm8904_i2c_driver = {
.name = "wm8904",
.of_match_table = of_match_ptr(wm8904_of_match),
},
- .probe = wm8904_i2c_probe,
- .remove = wm8904_i2c_remove,
+ .probe = wm8904_i2c_probe,
.id_table = wm8904_i2c_id,
};
diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h
index c29a0e8131ca..de6340446b1f 100644
--- a/sound/soc/codecs/wm8904.h
+++ b/sound/soc/codecs/wm8904.h
@@ -1,18 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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_AUTO 0
#define WM8904_CLK_MCLK 1
#define WM8904_CLK_FLL 2
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index f6f9395ea38e..2f55d0c572a4 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8940.c -- WM8940 ALSA Soc Audio driver
*
@@ -7,10 +8,6 @@
* 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)
@@ -40,7 +37,9 @@
#include "wm8940.h"
struct wm8940_priv {
- unsigned int sysclk;
+ unsigned int mclk;
+ unsigned int fs;
+
struct regmap *regmap;
};
@@ -221,7 +220,7 @@ static const struct snd_kcontrol_new wm8940_snd_controls[] = {
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,
+ SOC_SINGLE_TLV("Capture Boost Volume", 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),
@@ -334,25 +333,25 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = {
{"ADC", NULL, "Boost Mixer"},
};
-#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
+#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0);
static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67;
- u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe;
+ struct snd_soc_component *component = codec_dai->component;
+ u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFE67;
+ u16 clk = snd_soc_component_read(component, WM8940_CLOCK) & 0x1fe;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
clk |= 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- snd_soc_write(codec, WM8940_CLOCK, clk);
+ snd_soc_component_write(component, WM8940_CLOCK, clk);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -385,22 +384,29 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
}
- snd_soc_write(codec, WM8940_IFACE, iface);
+ snd_soc_component_write(component, WM8940_IFACE, iface);
return 0;
}
+static int wm8940_update_clocks(struct snd_soc_dai *dai);
static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_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,
+ struct snd_soc_component *component = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F;
+ u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1;
+ u16 companding = snd_soc_component_read(component,
WM8940_COMPANDINGCTL) & 0xFFDF;
int ret;
+ priv->fs = params_rate(params);
+ ret = wm8940_update_clocks(dai);
+ if (ret)
+ return ret;
+
/* LoutR control */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
&& params_channels(params) == 2)
@@ -426,7 +432,7 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
}
- ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
+ ret = snd_soc_component_write(component, WM8940_ADDCNTRL, addcntrl);
if (ret)
goto error_ret;
@@ -446,32 +452,33 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
iface |= (3 << 5);
break;
}
- ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
+ ret = snd_soc_component_write(component, WM8940_COMPANDINGCTL, companding);
if (ret)
goto error_ret;
- ret = snd_soc_write(codec, WM8940_IFACE, iface);
+ ret = snd_soc_component_write(component, WM8940_IFACE, iface);
error_ret:
return ret;
}
-static int wm8940_mute(struct snd_soc_dai *dai, int mute)
+static int wm8940_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8940_DAC) & 0xffbf;
if (mute)
mute_reg |= 0x40;
- return snd_soc_write(codec, WM8940_DAC, mute_reg);
+ return snd_soc_component_write(component, WM8940_DAC, mute_reg);
}
-static int wm8940_set_bias_level(struct snd_soc_codec *codec,
+static int wm8940_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
+ struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u16 val;
- u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
+ u16 pwr_reg = snd_soc_component_read(component, WM8940_POWER1) & 0x1F0;
int ret = 0;
switch (level) {
@@ -479,23 +486,23 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
/* 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);
+ val = snd_soc_component_read(component, WM8940_OUTPUTCTL);
+ ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, val | 0x2);
if (ret)
break;
/* set vmid to 75k */
- ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+ ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1);
break;
case SND_SOC_BIAS_PREPARE:
/* ensure bufioen and biasen */
pwr_reg |= (1 << 2) | (1 << 3);
- ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+ ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(wm8940->regmap);
if (ret < 0) {
- dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
return ret;
}
}
@@ -503,10 +510,10 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
/* 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);
+ ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x2);
break;
case SND_SOC_BIAS_OFF:
- ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
+ ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg);
break;
}
@@ -576,86 +583,145 @@ static void pll_factors(unsigned int target, unsigned int source)
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;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
/* Turn off PLL */
- reg = snd_soc_read(codec, WM8940_POWER1);
- snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
+ reg = snd_soc_component_read(component, WM8940_POWER1);
+ snd_soc_component_write(component, WM8940_POWER1, reg & 0x1df);
if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
- reg = snd_soc_read(codec, WM8940_CLOCK);
- snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
+ reg = snd_soc_component_read(component, WM8940_CLOCK);
+ snd_soc_component_write(component, WM8940_CLOCK, reg & 0x0ff);
/* Pll power down */
- snd_soc_write(codec, WM8940_PLLN, (1 << 7));
+ snd_soc_component_write(component, WM8940_PLLN, (1 << 7));
return 0;
}
/* Pll is followed by a frequency divide by 4 */
pll_factors(freq_out*4, freq_in);
if (pll_div.k)
- snd_soc_write(codec, WM8940_PLLN,
+ snd_soc_component_write(component, WM8940_PLLN,
(pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
else /* No factional component */
- snd_soc_write(codec, WM8940_PLLN,
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8940_PLLK1, pll_div.k >> 18);
+ snd_soc_component_write(component, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8940_PLLK3, pll_div.k & 0x1ff);
/* Enable the PLL */
- reg = snd_soc_read(codec, WM8940_POWER1);
- snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
+ reg = snd_soc_component_read(component, WM8940_POWER1);
+ snd_soc_component_write(component, WM8940_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
- reg = snd_soc_read(codec, WM8940_CLOCK);
- snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
+ reg = snd_soc_component_read(component, WM8940_CLOCK);
+ snd_soc_component_write(component, 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;
+ struct snd_soc_component *component = codec_dai->component;
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));
+ reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFFE3;
+ ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 2));
break;
case WM8940_MCLKDIV:
- reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
- ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
+ reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFF1F;
+ ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 5));
break;
case WM8940_OPCLKDIV:
- reg = snd_soc_read(codec, WM8940_GPIO) & 0xFFCF;
- ret = snd_soc_write(codec, WM8940_GPIO, reg | (div << 4));
+ reg = snd_soc_component_read(component, WM8940_GPIO) & 0xFFCF;
+ ret = snd_soc_component_write(component, WM8940_GPIO, reg | (div << 4));
break;
}
return ret;
}
+static unsigned int wm8940_get_mclkdiv(unsigned int f_in, unsigned int f_out,
+ int *mclkdiv)
+{
+ unsigned int ratio = 2 * f_in / f_out;
+
+ if (ratio <= 2) {
+ *mclkdiv = WM8940_MCLKDIV_1;
+ ratio = 2;
+ } else if (ratio == 3) {
+ *mclkdiv = WM8940_MCLKDIV_1_5;
+ } else if (ratio == 4) {
+ *mclkdiv = WM8940_MCLKDIV_2;
+ } else if (ratio <= 6) {
+ *mclkdiv = WM8940_MCLKDIV_3;
+ ratio = 6;
+ } else if (ratio <= 8) {
+ *mclkdiv = WM8940_MCLKDIV_4;
+ ratio = 8;
+ } else if (ratio <= 12) {
+ *mclkdiv = WM8940_MCLKDIV_6;
+ ratio = 12;
+ } else if (ratio <= 16) {
+ *mclkdiv = WM8940_MCLKDIV_8;
+ ratio = 16;
+ } else {
+ *mclkdiv = WM8940_MCLKDIV_12;
+ ratio = 24;
+ }
+
+ return f_out * ratio / 2;
+}
+
+static int wm8940_update_clocks(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec);
+ unsigned int fs256;
+ unsigned int fpll = 0;
+ unsigned int f;
+ int mclkdiv;
+
+ if (!priv->mclk || !priv->fs)
+ return 0;
+
+ fs256 = 256 * priv->fs;
+
+ f = wm8940_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
+ if (f != priv->mclk) {
+ /* The PLL performs best around 90MHz */
+ if (fs256 % 8000)
+ f = 22579200;
+ else
+ f = 24576000;
+
+ fpll = wm8940_get_mclkdiv(f, fs256, &mclkdiv);
+ }
+
+ wm8940_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
+ wm8940_set_dai_clkdiv(dai, WM8940_MCLKDIV, mclkdiv);
+
+ return 0;
+}
+
+static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec);
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ priv->mclk = freq;
+
+ return wm8940_update_clocks(dai);
+}
+
#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
@@ -667,10 +733,11 @@ static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
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,
+ .mute_stream = wm8940_mute,
.set_fmt = wm8940_set_dai_fmt,
.set_clkdiv = wm8940_set_dai_clkdiv,
.set_pll = wm8940_set_dai_pll,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8940_dai = {
@@ -690,32 +757,42 @@ static struct snd_soc_dai_driver wm8940_dai = {
.formats = WM8940_FORMATS,
},
.ops = &wm8940_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8940_probe(struct snd_soc_codec *codec)
+static int wm8940_probe(struct snd_soc_component *component)
{
- struct wm8940_setup_data *pdata = codec->dev->platform_data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8940_setup_data *pdata = component->dev->platform_data;
int ret;
u16 reg;
- ret = wm8940_reset(codec);
+ /*
+ * Check chip ID for wm8940 - value of 0x00 offset
+ * SOFTWARE_RESET on write
+ * CHIP_ID on read
+ */
+ reg = snd_soc_component_read(component, WM8940_SOFTRESET);
+ if (reg != WM8940_CHIP_ID) {
+ dev_err(component->dev, "Wrong wm8940 chip ID: 0x%x\n", reg);
+ return -ENODEV;
+ }
+
+ ret = wm8940_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
+ dev_err(component->dev, "Failed to issue reset\n");
return ret;
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
- ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
+ ret = snd_soc_component_write(component, 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 (pdata) {
+ reg = snd_soc_component_read(component, WM8940_OUTPUTCTL);
+ ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi);
if (ret < 0)
return ret;
}
@@ -723,17 +800,19 @@ static int wm8940_probe(struct snd_soc_codec *codec)
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 snd_soc_component_driver soc_component_dev_wm8940 = {
+ .probe = wm8940_probe,
+ .set_bias_level = wm8940_set_bias_level,
+ .controls = wm8940_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8940_snd_controls),
+ .dapm_widgets = wm8940_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets),
+ .dapm_routes = wm8940_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8940_regmap = {
@@ -743,13 +822,13 @@ static const struct regmap_config wm8940_regmap = {
.max_register = WM8940_MONOMIX,
.reg_defaults = wm8940_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
.readable_reg = wm8940_readable_register,
.volatile_reg = wm8940_volatile_register,
};
-static int wm8940_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8940_i2c_probe(struct i2c_client *i2c)
{
struct wm8940_priv *wm8940;
int ret;
@@ -765,31 +844,30 @@ static int wm8940_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8940);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8940, &wm8940_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8940" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
+static const struct of_device_id wm8940_of_match[] = {
+ { .compatible = "wlf,wm8940", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8940_of_match);
+
static struct i2c_driver wm8940_i2c_driver = {
.driver = {
.name = "wm8940",
+ .of_match_table = wm8940_of_match,
},
- .probe = wm8940_i2c_probe,
- .remove = wm8940_i2c_remove,
+ .probe = wm8940_i2c_probe,
.id_table = wm8940_i2c_id,
};
diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h
index 907fe192e9e0..86bbf902ef5a 100644
--- a/sound/soc/codecs/wm8940.h
+++ b/sound/soc/codecs/wm8940.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -98,5 +95,8 @@ struct wm8940_setup_data {
#define WM8940_OPCLKDIV_3 2
#define WM8940_OPCLKDIV_4 3
+/* Chip ID */
+#define WM8940_CHIP_ID 0x8940
+
#endif /* _WM8940_H */
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 9db00d53abe7..e1c61e026cbc 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -128,9 +125,9 @@ static bool wm8955_volatile(struct device *dev, unsigned int reg)
}
}
-static int wm8955_reset(struct snd_soc_codec *codec)
+static int wm8955_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, WM8955_RESET, 0);
+ return snd_soc_component_write(component, WM8955_RESET, 0);
}
struct pll_factors {
@@ -143,7 +140,7 @@ struct pll_factors {
* to allow rounding later */
#define FIXED_FLL_SIZE ((1 << 22) * 10)
-static int wm8995_pll_factors(struct device *dev,
+static int wm8955_pll_factors(struct device *dev,
int Fref, int Fout, struct pll_factors *pll)
{
u64 Kpart;
@@ -242,9 +239,9 @@ static struct {
{ 11289600, 88200, 0, 31, },
};
-static int wm8955_configure_clocking(struct snd_soc_codec *codec)
+static int wm8955_configure_clocking(struct snd_soc_component *component)
{
- struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
int i, ret, val;
int clocking = 0;
int srate = 0;
@@ -267,7 +264,7 @@ static int wm8955_configure_clocking(struct snd_soc_codec *codec)
/* We should never get here with an unsupported sample rate */
if (sr == -1) {
- dev_err(codec->dev, "Sample rate %dHz unsupported\n",
+ dev_err(component->dev, "Sample rate %dHz unsupported\n",
wm8955->fs);
WARN_ON(sr == -1);
return -EINVAL;
@@ -282,30 +279,30 @@ static int wm8955_configure_clocking(struct snd_soc_codec *codec)
/* Use the last divider configuration we saw for the
* sample rate. */
- ret = wm8995_pll_factors(codec->dev, wm8955->mclk_rate,
+ ret = wm8955_pll_factors(component->dev, wm8955->mclk_rate,
clock_cfgs[sr].mclk, &pll);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->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,
+ snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_1,
WM8955_N_MASK | WM8955_K_21_18_MASK,
(pll.n << WM8955_N_SHIFT) |
pll.k >> 18);
- snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+ snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_2,
WM8955_K_17_9_MASK,
(pll.k >> 9) & WM8955_K_17_9_MASK);
- snd_soc_update_bits(codec, WM8955_PLL_CONTROL_3,
+ snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_3,
WM8955_K_8_0_MASK,
pll.k & WM8955_K_8_0_MASK);
if (pll.k)
- snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+ snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_4,
WM8955_KEN, WM8955_KEN);
else
- snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+ snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_4,
WM8955_KEN, 0);
if (pll.outdiv)
@@ -314,17 +311,17 @@ static int wm8955_configure_clocking(struct snd_soc_codec *codec)
val = WM8955_PLL_RB;
/* Now start the PLL running */
- snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL,
WM8955_PLL_RB | WM8955_PLLOUTDIV2, val);
- snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL,
WM8955_PLLEN, WM8955_PLLEN);
}
srate = clock_cfgs[sr].usb | (clock_cfgs[sr].sr << WM8955_SR_SHIFT);
- snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+ snd_soc_component_update_bits(component, WM8955_SAMPLE_RATE,
WM8955_USB | WM8955_SR_MASK, srate);
- snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL,
WM8955_MCLKSEL, clocking);
return 0;
@@ -333,22 +330,22 @@ static int wm8955_configure_clocking(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
int ret = 0;
/* Always disable the clocks - if we're doing reconfiguration this
* avoids misclocking.
*/
- snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1,
WM8955_DIGENB, 0);
- snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL,
WM8955_PLL_RB | WM8955_PLLEN, 0);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
break;
case SND_SOC_DAPM_PRE_PMU:
- ret = wm8955_configure_clocking(codec);
+ ret = wm8955_configure_clocking(component);
break;
default:
ret = -EINVAL;
@@ -360,9 +357,9 @@ static int wm8955_sysclk(struct snd_soc_dapm_widget *w,
static int deemph_settings[] = { 0, 32000, 44100, 48000 };
-static int wm8955_set_deemph(struct snd_soc_codec *codec)
+static int wm8955_set_deemph(struct snd_soc_component *component)
{
- struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
int val, i, best;
/* If we're using deemphasis select the nearest available sample
@@ -381,17 +378,17 @@ static int wm8955_set_deemph(struct snd_soc_codec *codec)
val = 0;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, WM8955_DAC_CONTROL,
+ return snd_soc_component_update_bits(component, WM8955_DAC_CONTROL,
WM8955_DEEMPH_MASK, val);
}
static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8955->deemph;
return 0;
@@ -400,8 +397,8 @@ static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
@@ -409,7 +406,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
wm8955->deemph = deemph;
- return wm8955_set_deemph(codec);
+ return wm8955_set_deemph(component);
}
static const char *bass_mode_text[] = {
@@ -592,8 +589,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
int ret;
int wl;
@@ -613,25 +610,25 @@ static int wm8955_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8955_AUDIO_INTERFACE,
WM8955_WL_MASK, wl);
wm8955->fs = params_rate(params);
- wm8955_set_deemph(codec);
+ wm8955_set_deemph(component);
/* If the chip is clocked then disable the clocks and force a
* reconfiguration, otherwise DAPM will power up the
* clocks for us later. */
- ret = snd_soc_read(codec, WM8955_POWER_MANAGEMENT_1);
+ ret = snd_soc_component_read(component, WM8955_POWER_MANAGEMENT_1);
if (ret < 0)
return ret;
if (ret & WM8955_DIGENB) {
- snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1,
WM8955_DIGENB, 0);
- snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL,
WM8955_PLL_RB | WM8955_PLLEN, 0);
- wm8955_configure_clocking(codec);
+ wm8955_configure_clocking(component);
}
return 0;
@@ -641,8 +638,8 @@ static int wm8955_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8955_priv *priv = snd_soc_component_get_drvdata(component);
int div;
switch (clk_id) {
@@ -655,7 +652,7 @@ static int wm8955_set_sysclk(struct snd_soc_dai *dai, int clk_id,
div = 0;
}
- snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+ snd_soc_component_update_bits(component, WM8955_SAMPLE_RATE,
WM8955_MCLKDIV2, div);
break;
@@ -670,13 +667,13 @@ static int wm8955_set_sysclk(struct snd_soc_dai *dai, int clk_id,
static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 aif = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif |= WM8955_MS;
break;
default:
@@ -686,6 +683,7 @@ static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif |= WM8955_LRP;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif |= 0x3;
break;
@@ -739,7 +737,7 @@ static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8955_AUDIO_INTERFACE,
WM8955_MS | WM8955_FORMAT_MASK | WM8955_BCLKINV |
WM8955_LRP, aif);
@@ -747,9 +745,9 @@ static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
-static int wm8955_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm8955_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int val;
if (mute)
@@ -757,15 +755,16 @@ static int wm8955_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
val = 0;
- snd_soc_update_bits(codec, WM8955_DAC_CONTROL, WM8955_DACMU, val);
+ snd_soc_component_update_bits(component, WM8955_DAC_CONTROL, WM8955_DACMU, val);
return 0;
}
-static int wm8955_set_bias_level(struct snd_soc_codec *codec,
+static int wm8955_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -774,22 +773,22 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID resistance 2*50k */
- snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1,
WM8955_VMIDSEL_MASK,
0x1 << WM8955_VMIDSEL_SHIFT);
/* Default bias current */
- snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8955_ADDITIONAL_CONTROL_1,
WM8955_VSEL_MASK,
0x2 << WM8955_VSEL_SHIFT);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
wm8955->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
@@ -798,7 +797,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
regcache_sync(wm8955->regmap);
/* Enable VREF and VMID */
- snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1,
WM8955_VREF |
WM8955_VMIDSEL_MASK,
WM8955_VREF |
@@ -808,29 +807,29 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
msleep(500);
/* High resistance VROI to maintain outputs */
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8955_ADDITIONAL_CONTROL_3,
WM8955_VROI, WM8955_VROI);
}
/* Maintain VMID with 2*250k */
- snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1,
WM8955_VMIDSEL_MASK,
0x2 << WM8955_VMIDSEL_SHIFT);
/* Minimum bias current */
- snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8955_ADDITIONAL_CONTROL_1,
WM8955_VSEL_MASK, 0);
break;
case SND_SOC_BIAS_OFF:
/* Low resistance VROI to help discharge */
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8955_ADDITIONAL_CONTROL_3,
WM8955_VROI, 0);
/* Turn off VMID and VREF */
- snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1,
WM8955_VREF |
WM8955_VMIDSEL_MASK, 0);
@@ -850,7 +849,8 @@ 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,
+ .mute_stream = wm8955_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8955_dai = {
@@ -865,70 +865,71 @@ static struct snd_soc_dai_driver wm8955_dai = {
.ops = &wm8955_dai_ops,
};
-static int wm8955_probe(struct snd_soc_codec *codec)
+static int wm8955_probe(struct snd_soc_component *component)
{
- struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
- struct wm8955_pdata *pdata = dev_get_platdata(codec->dev);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
+ struct wm8955_pdata *pdata = dev_get_platdata(component->dev);
int ret, i;
for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
wm8955->supplies[i].supply = wm8955_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8955->supplies),
+ ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(wm8955->supplies),
wm8955->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
wm8955->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- ret = wm8955_reset(codec);
+ ret = wm8955_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
goto err_enable;
}
/* Change some default settings - latch VU and enable ZC */
- snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
+ snd_soc_component_update_bits(component, WM8955_LEFT_DAC_VOLUME,
WM8955_LDVU, WM8955_LDVU);
- snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
+ snd_soc_component_update_bits(component, WM8955_RIGHT_DAC_VOLUME,
WM8955_RDVU, WM8955_RDVU);
- snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
+ snd_soc_component_update_bits(component, WM8955_LOUT1_VOLUME,
WM8955_LO1VU | WM8955_LO1ZC,
WM8955_LO1VU | WM8955_LO1ZC);
- snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
+ snd_soc_component_update_bits(component, WM8955_ROUT1_VOLUME,
WM8955_RO1VU | WM8955_RO1ZC,
WM8955_RO1VU | WM8955_RO1ZC);
- snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
+ snd_soc_component_update_bits(component, WM8955_LOUT2_VOLUME,
WM8955_LO2VU | WM8955_LO2ZC,
WM8955_LO2VU | WM8955_LO2ZC);
- snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
+ snd_soc_component_update_bits(component, WM8955_ROUT2_VOLUME,
WM8955_RO2VU | WM8955_RO2ZC,
WM8955_RO2VU | WM8955_RO2ZC);
- snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_update_bits(component, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
/* Set platform data values */
if (pdata) {
if (pdata->out2_speaker)
- snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_2,
+ snd_soc_component_update_bits(component, WM8955_ADDITIONAL_CONTROL_2,
WM8955_ROUT2INV, WM8955_ROUT2INV);
if (pdata->monoin_diff)
- snd_soc_update_bits(codec, WM8955_MONO_OUT_MIX_1,
+ snd_soc_component_update_bits(component, WM8955_MONO_OUT_MIX_1,
WM8955_DMEN, WM8955_DMEN);
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
@@ -940,17 +941,19 @@ err_enable:
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 snd_soc_component_driver soc_component_dev_wm8955 = {
+ .probe = wm8955_probe,
+ .set_bias_level = wm8955_set_bias_level,
+ .controls = wm8955_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8955_snd_controls),
+ .dapm_widgets = wm8955_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8955_dapm_widgets),
+ .dapm_routes = wm8955_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8955_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8955_regmap = {
@@ -961,13 +964,12 @@ static const struct regmap_config wm8955_regmap = {
.volatile_reg = wm8955_volatile,
.writeable_reg = wm8955_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8955_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults),
};
-static int wm8955_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8955_i2c_probe(struct i2c_client *i2c)
{
struct wm8955_priv *wm8955;
int ret;
@@ -987,21 +989,14 @@ static int wm8955_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8955);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8955, &wm8955_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8955" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id);
@@ -1010,8 +1005,7 @@ static struct i2c_driver wm8955_i2c_driver = {
.driver = {
.name = "wm8955",
},
- .probe = wm8955_i2c_probe,
- .remove = wm8955_i2c_remove,
+ .probe = wm8955_i2c_probe,
.id_table = wm8955_i2c_id,
};
diff --git a/sound/soc/codecs/wm8955.h b/sound/soc/codecs/wm8955.h
index d13fd5c5fa63..3d3f9be0419e 100644
--- a/sound/soc/codecs/wm8955.h
+++ b/sound/soc/codecs/wm8955.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index c799cca5abeb..8ff0882732e7 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -28,6 +25,8 @@
#include <linux/mfd/wm8994/pdata.h>
#include <linux/mfd/wm8994/gpio.h>
+#include <linux/unaligned.h>
+
#include "wm8994.h"
#define WM_FW_BLOCK_INFO 0xff
@@ -39,10 +38,10 @@
#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,
+static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
const struct firmware *fw, bool check)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
u64 data64;
u32 data32;
const u8 *data;
@@ -55,72 +54,66 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
return 0;
if (fw->size < 32) {
- dev_err(codec->dev, "%s: firmware too short (%zd bytes)\n",
+ dev_err(component->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",
+ data32 = get_unaligned_be32(fw->data);
+ dev_err(component->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);
+ len = get_unaligned_be32(fw->data + 4);
+ data32 = get_unaligned_be32(fw->data + 8);
- 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",
+ dev_err(component->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",
+ dev_err(component->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",
+ dev_err(component->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));
+ data64 = get_unaligned_be64(fw->data + 24);
+ dev_info(component->dev, "%s timestamp %llx\n", name, data64);
} else {
- snd_soc_write(codec, 0x102, 0x2);
- snd_soc_write(codec, 0x900, 0x2);
+ snd_soc_component_write(component, 0x102, 0x2);
+ snd_soc_component_write(component, 0x900, 0x2);
}
data = fw->data + len;
len = fw->size - len;
while (len) {
if (len < 12) {
- dev_err(codec->dev, "%s short data block of %zd\n",
+ dev_err(component->dev, "%s short data block of %zd\n",
name, len);
goto err;
}
- memcpy(&data32, data + 4, sizeof(data32));
- block_len = be32_to_cpu(data32);
+ block_len = get_unaligned_be32(data + 4);
if (block_len + 8 > len) {
- dev_err(codec->dev, "%zd byte block longer than file\n",
+ dev_err(component->dev, "%zd byte block longer than file\n",
block_len);
goto err;
}
if (block_len == 0) {
- dev_err(codec->dev, "Zero length block\n");
+ dev_err(component->dev, "Zero length block\n");
goto err;
}
- memcpy(&data32, data, sizeof(data32));
- data32 = be32_to_cpu(data32);
+ data32 = get_unaligned_be32(data);
switch ((data32 >> 24) & 0xff) {
case WM_FW_BLOCK_INFO:
@@ -131,10 +124,10 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
str = kzalloc(block_len + 1, GFP_KERNEL);
if (str) {
memcpy(str, data + 8, block_len);
- dev_info(codec->dev, "%s: %s\n", name, str);
+ dev_info(component->dev, "%s: %s\n", name, str);
kfree(str);
} else {
- dev_err(codec->dev, "Out of memory\n");
+ dev_err(component->dev, "Out of memory\n");
}
break;
case WM_FW_BLOCK_PM:
@@ -144,7 +137,7 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
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,
+ dev_dbg(component->dev, "%s: %zd bytes of %x@%x\n", name,
block_len, (data32 >> 24) & 0xff,
data32 & 0xffffff);
@@ -160,7 +153,7 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
break;
default:
- dev_warn(codec->dev, "%s: unknown block type %d\n",
+ dev_warn(component->dev, "%s: unknown block type %d\n",
name, (data32 >> 24) & 0xff);
break;
}
@@ -173,10 +166,10 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
}
if (!check) {
- dev_dbg(codec->dev, "%s: download done\n", name);
+ dev_dbg(component->dev, "%s: download done\n", name);
wm8994->cur_fw = fw;
} else {
- dev_info(codec->dev, "%s: got firmware\n", name);
+ dev_info(component->dev, "%s: got firmware\n", name);
}
goto ok;
@@ -185,28 +178,28 @@ err:
ret = -EINVAL;
ok:
if (!check) {
- snd_soc_write(codec, 0x900, 0x0);
- snd_soc_write(codec, 0x102, 0x0);
+ snd_soc_component_write(component, 0x900, 0x0);
+ snd_soc_component_write(component, 0x102, 0x0);
}
return ret;
}
-static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
+static void wm8958_dsp_start_mbc(struct snd_soc_component *component, int path)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int i;
/* If the DSP is already running then noop */
- if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
+ if (snd_soc_component_read(component, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
return;
/* If we have MBC firmware download it */
if (wm8994->mbc)
- wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
+ wm8958_dsp2_fw(component, "MBC", wm8994->mbc, false);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
WM8958_DSP2_ENA, WM8958_DSP2_ENA);
/* If we've got user supplied MBC settings use them */
@@ -215,37 +208,37 @@ static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
= &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,
+ snd_soc_component_write(component, i + WM8958_MBC_BAND_1_K_1,
cfg->coeff_regs[i]);
for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
- snd_soc_write(codec,
+ snd_soc_component_write(component,
i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
cfg->cutoff_regs[i]);
}
/* Run the DSP */
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
WM8958_DSP2_RUNR);
/* And we're off! */
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
WM8958_MBC_ENA |
WM8958_MBC_SEL_MASK,
path << WM8958_MBC_SEL_SHIFT |
WM8958_MBC_ENA);
}
-static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
+static void wm8958_dsp_start_vss(struct snd_soc_component *component, int path)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int i, ena;
if (wm8994->mbc_vss)
- wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false);
+ wm8958_dsp2_fw(component, "MBC+VSS", wm8994->mbc_vss, false);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
WM8958_DSP2_ENA, WM8958_DSP2_ENA);
/* If we've got user supplied settings use them */
@@ -254,7 +247,7 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
= &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
- snd_soc_write(codec, i + 0x2800,
+ snd_soc_component_write(component, i + 0x2800,
cfg->combined_regs[i]);
}
@@ -263,7 +256,7 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
= &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]);
+ snd_soc_component_write(component, i + 0x2600, cfg->regs[i]);
}
if (control->pdata.num_vss_hpf_cfgs) {
@@ -271,11 +264,11 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
= &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]);
+ snd_soc_component_write(component, i + 0x2400, cfg->regs[i]);
}
/* Run the DSP */
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
WM8958_DSP2_RUNR);
/* Enable the algorithms we've selected */
@@ -289,23 +282,23 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
if (wm8994->vss_ena[path])
ena |= 0x1;
- snd_soc_write(codec, 0x2201, ena);
+ snd_soc_component_write(component, 0x2201, ena);
/* Switch the DSP into the data path */
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
}
-static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
+static void wm8958_dsp_start_enh_eq(struct snd_soc_component *component, int path)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int i;
- wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false);
+ wm8958_dsp2_fw(component, "ENH_EQ", wm8994->enh_eq, false);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
WM8958_DSP2_ENA, WM8958_DSP2_ENA);
/* If we've got user supplied settings use them */
@@ -314,24 +307,24 @@ static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
= &control->pdata.enh_eq_cfgs[wm8994->enh_eq_cfg];
for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
- snd_soc_write(codec, i + 0x2200,
+ snd_soc_component_write(component, i + 0x2200,
cfg->regs[i]);
}
/* Run the DSP */
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
WM8958_DSP2_RUNR);
/* Switch the DSP into the data path */
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
}
-static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
+static void wm8958_dsp_apply(struct snd_soc_component *component, 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);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ int pwr_reg = snd_soc_component_read(component, WM8994_POWER_MANAGEMENT_5);
int ena, reg, aif;
switch (path) {
@@ -359,9 +352,9 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
if (!pwr_reg)
ena = 0;
- reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
+ reg = snd_soc_component_read(component, WM8958_DSP2_PROGRAM);
- dev_dbg(codec->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
+ dev_dbg(component->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
path, wm8994->dsp_active, start, pwr_reg, reg);
if (start && ena) {
@@ -370,29 +363,29 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
return;
/* If either AIFnCLK is not yet enabled postpone */
- if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+ if (!(snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1)
& WM8994_AIF1CLK_ENA_MASK) &&
- !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+ !(snd_soc_component_read(component, 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,
+ snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
aif << WM8958_DSP2CLK_SRC_SHIFT |
WM8958_DSP2CLK_ENA);
if (wm8994->enh_eq_ena[path])
- wm8958_dsp_start_enh_eq(codec, path);
+ wm8958_dsp_start_enh_eq(component, path);
else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
wm8994->hpf2_ena[path])
- wm8958_dsp_start_vss(codec, path);
+ wm8958_dsp_start_vss(component, path);
else if (wm8994->mbc_ena[path])
- wm8958_dsp_start_mbc(codec, path);
+ wm8958_dsp_start_mbc(component, path);
wm8994->dsp_active = path;
- dev_dbg(codec->dev, "DSP running in path %d\n", path);
+ dev_dbg(component->dev, "DSP running in path %d\n", path);
}
if (!start && wm8994->dsp_active == path) {
@@ -400,37 +393,41 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
if (!(reg & WM8958_DSP2_ENA))
return;
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
WM8958_MBC_ENA, 0);
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
WM8958_DSP2_STOP);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
WM8958_DSP2_ENA, 0);
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
WM8958_DSP2CLK_ENA, 0);
wm8994->dsp_active = -1;
- dev_dbg(codec->dev, "DSP stopped\n");
+ dev_dbg(component->dev, "DSP stopped\n");
}
}
int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994 *control = dev_get_drvdata(component->dev->parent);
int i;
+ if (control->type != WM8958)
+ return 0;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
case SND_SOC_DAPM_PRE_PMU:
for (i = 0; i < 3; i++)
- wm8958_dsp_apply(codec, i, 1);
+ wm8958_dsp_apply(component, i, 1);
break;
case SND_SOC_DAPM_POST_PMD:
case SND_SOC_DAPM_PRE_PMD:
for (i = 0; i < 3; i++)
- wm8958_dsp_apply(codec, i, 0);
+ wm8958_dsp_apply(component, i, 0);
break;
}
@@ -456,14 +453,14 @@ static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
return -EBUSY;
@@ -478,8 +475,8 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
@@ -500,8 +497,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
@@ -512,8 +509,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
return 0;
@@ -522,7 +519,7 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
if (wm8958_dsp2_busy(wm8994, mbc)) {
- dev_dbg(codec->dev, "DSP2 active on %d already\n", mbc);
+ dev_dbg(component->dev, "DSP2 active on %d already\n", mbc);
return -EBUSY;
}
@@ -531,9 +528,9 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
- wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]);
+ wm8958_dsp_apply(component, mbc, wm8994->mbc_ena[mbc]);
- return 0;
+ return 1;
}
#define WM8958_MBC_SWITCH(xname, xval) {\
@@ -546,14 +543,14 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
return -EBUSY;
@@ -568,8 +565,8 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
@@ -579,14 +576,14 @@ static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
return -EBUSY;
@@ -601,8 +598,8 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
@@ -623,8 +620,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
@@ -635,8 +632,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
return 0;
@@ -648,7 +645,7 @@ static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
return -ENODEV;
if (wm8958_dsp2_busy(wm8994, vss)) {
- dev_dbg(codec->dev, "DSP2 active on %d already\n", vss);
+ dev_dbg(component->dev, "DSP2 active on %d already\n", vss);
return -EBUSY;
}
@@ -657,9 +654,9 @@ static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
- wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]);
+ wm8958_dsp_apply(component, vss, wm8994->vss_ena[vss]);
- return 0;
+ return 1;
}
@@ -684,8 +681,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (hpf < 3)
ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
@@ -699,8 +696,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (hpf < 3) {
if (wm8994->hpf1_ena[hpf % 3] ==
@@ -719,7 +716,7 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
return -ENODEV;
if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
- dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf);
+ dev_dbg(component->dev, "DSP2 active on %d already\n", hpf);
return -EBUSY;
}
@@ -731,9 +728,9 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
else
wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
- wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]);
+ wm8958_dsp_apply(component, hpf % 3, ucontrol->value.integer.value[0]);
- return 0;
+ return 1;
}
#define WM8958_HPF_SWITCH(xname, xval) {\
@@ -746,14 +743,14 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
return -EBUSY;
@@ -768,8 +765,8 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
@@ -790,8 +787,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
@@ -802,8 +799,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
return 0;
@@ -815,7 +812,7 @@ static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
return -ENODEV;
if (wm8958_dsp2_busy(wm8994, eq)) {
- dev_dbg(codec->dev, "DSP2 active on %d already\n", eq);
+ dev_dbg(component->dev, "DSP2 active on %d already\n", eq);
return -EBUSY;
}
@@ -825,9 +822,9 @@ static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
- wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]);
+ wm8958_dsp_apply(component, eq, ucontrol->value.integer.value[0]);
- return 0;
+ return 1;
}
#define WM8958_ENH_EQ_SWITCH(xname, xval) {\
@@ -863,10 +860,10 @@ 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);
+ struct snd_soc_component *component = context;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
- if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
+ if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) {
mutex_lock(&wm8994->fw_lock);
wm8994->enh_eq = fw;
mutex_unlock(&wm8994->fw_lock);
@@ -875,10 +872,10 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
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);
+ struct snd_soc_component *component = context;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
- if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
+ if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) {
mutex_lock(&wm8994->fw_lock);
wm8994->mbc_vss = fw;
mutex_unlock(&wm8994->fw_lock);
@@ -887,53 +884,54 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
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);
+ struct snd_soc_component *component = context;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
- if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) {
+ if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) {
mutex_lock(&wm8994->fw_lock);
wm8994->mbc = fw;
mutex_unlock(&wm8994->fw_lock);
}
}
-void wm8958_dsp2_init(struct snd_soc_codec *codec)
+void wm8958_dsp2_init(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int ret, i;
wm8994->dsp_active = -1;
- snd_soc_add_codec_controls(codec, wm8958_mbc_snd_controls,
+ snd_soc_add_component_controls(component, wm8958_mbc_snd_controls,
ARRAY_SIZE(wm8958_mbc_snd_controls));
- snd_soc_add_codec_controls(codec, wm8958_vss_snd_controls,
+ snd_soc_add_component_controls(component, wm8958_vss_snd_controls,
ARRAY_SIZE(wm8958_vss_snd_controls));
- snd_soc_add_codec_controls(codec, wm8958_enh_eq_snd_controls,
+ snd_soc_add_component_controls(component, wm8958_enh_eq_snd_controls,
ARRAY_SIZE(wm8958_enh_eq_snd_controls));
/* We don't *require* firmware and don't want to delay boot */
- request_firmware_nowait(THIS_MODULE, FW_ACTION_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);
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ "wm8958_mbc.wfw", component->dev, GFP_KERNEL,
+ component, wm8958_mbc_loaded);
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ "wm8958_mbc_vss.wfw", component->dev, GFP_KERNEL,
+ component, wm8958_mbc_vss_loaded);
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ "wm8958_enh_eq.wfw", component->dev, GFP_KERNEL,
+ component, wm8958_enh_eq_loaded);
if (pdata->num_mbc_cfgs) {
- struct snd_kcontrol_new control[] = {
+ struct snd_kcontrol_new mbc_control[] = {
SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
wm8958_get_mbc_enum, wm8958_put_mbc_enum),
};
/* We need an array of texts for the enum API */
- wm8994->mbc_texts = kmalloc(sizeof(char *)
- * pdata->num_mbc_cfgs, GFP_KERNEL);
+ wm8994->mbc_texts = kmalloc_array(pdata->num_mbc_cfgs,
+ sizeof(char *),
+ GFP_KERNEL);
if (!wm8994->mbc_texts)
return;
@@ -943,22 +941,23 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
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);
+ ret = snd_soc_add_component_controls(wm8994->hubs.component,
+ mbc_control, 1);
if (ret != 0)
- dev_err(wm8994->hubs.codec->dev,
+ dev_err(wm8994->hubs.component->dev,
"Failed to add MBC mode controls: %d\n", ret);
}
if (pdata->num_vss_cfgs) {
- struct snd_kcontrol_new control[] = {
+ struct snd_kcontrol_new vss_control[] = {
SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
wm8958_get_vss_enum, wm8958_put_vss_enum),
};
/* We need an array of texts for the enum API */
- wm8994->vss_texts = kmalloc(sizeof(char *)
- * pdata->num_vss_cfgs, GFP_KERNEL);
+ wm8994->vss_texts = kmalloc_array(pdata->num_vss_cfgs,
+ sizeof(char *),
+ GFP_KERNEL);
if (!wm8994->vss_texts)
return;
@@ -968,23 +967,24 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
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);
+ ret = snd_soc_add_component_controls(wm8994->hubs.component,
+ vss_control, 1);
if (ret != 0)
- dev_err(wm8994->hubs.codec->dev,
+ dev_err(wm8994->hubs.component->dev,
"Failed to add VSS mode controls: %d\n", ret);
}
if (pdata->num_vss_hpf_cfgs) {
- struct snd_kcontrol_new control[] = {
+ struct snd_kcontrol_new hpf_control[] = {
SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
wm8958_get_vss_hpf_enum,
wm8958_put_vss_hpf_enum),
};
/* We need an array of texts for the enum API */
- wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
- * pdata->num_vss_hpf_cfgs, GFP_KERNEL);
+ wm8994->vss_hpf_texts = kmalloc_array(pdata->num_vss_hpf_cfgs,
+ sizeof(char *),
+ GFP_KERNEL);
if (!wm8994->vss_hpf_texts)
return;
@@ -994,24 +994,25 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
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);
+ ret = snd_soc_add_component_controls(wm8994->hubs.component,
+ hpf_control, 1);
if (ret != 0)
- dev_err(wm8994->hubs.codec->dev,
+ dev_err(wm8994->hubs.component->dev,
"Failed to add VSS HPFmode controls: %d\n",
ret);
}
if (pdata->num_enh_eq_cfgs) {
- struct snd_kcontrol_new control[] = {
+ struct snd_kcontrol_new eq_control[] = {
SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
wm8958_get_enh_eq_enum,
wm8958_put_enh_eq_enum),
};
/* We need an array of texts for the enum API */
- wm8994->enh_eq_texts = kmalloc(sizeof(char *)
- * pdata->num_enh_eq_cfgs, GFP_KERNEL);
+ wm8994->enh_eq_texts = kmalloc_array(pdata->num_enh_eq_cfgs,
+ sizeof(char *),
+ GFP_KERNEL);
if (!wm8994->enh_eq_texts)
return;
@@ -1021,10 +1022,10 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
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);
+ ret = snd_soc_add_component_controls(wm8994->hubs.component,
+ eq_control, 1);
if (ret != 0)
- dev_err(wm8994->hubs.codec->dev,
+ dev_err(wm8994->hubs.component->dev,
"Failed to add enhanced EQ controls: %d\n",
ret);
}
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index ff237726775a..384e8e703446 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -17,6 +14,7 @@
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -48,8 +46,10 @@
#define WM8960_DISOP 0x40
#define WM8960_DRES_MASK 0x30
+#define WM8960_DSCH_TOUT 600 /* discharge timeout, ms */
+
static bool is_pll_freq_available(unsigned int source, unsigned int target);
-static int wm8960_set_pll(struct snd_soc_codec *codec,
+static int wm8960_set_pll(struct snd_soc_component *component,
unsigned int freq_in, unsigned int freq_out);
/*
* wm8960 register cache
@@ -120,10 +120,19 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg)
}
}
+#define WM8960_NUM_SUPPLIES 5
+static const char *wm8960_supply_names[WM8960_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD",
+ "SPKVDD1",
+ "SPKVDD2",
+};
+
struct wm8960_priv {
struct clk *mclk;
struct regmap *regmap;
- int (*set_bias_level)(struct snd_soc_codec *,
+ int (*set_bias_level)(struct snd_soc_component *,
enum snd_soc_bias_level level);
struct snd_soc_dapm_widget *lout1;
struct snd_soc_dapm_widget *rout1;
@@ -136,6 +145,8 @@ struct wm8960_priv {
int freq_in;
bool is_stream_in_use[2];
struct wm8960_data pdata;
+ ktime_t dsch_start;
+ struct regulator_bulk_data supplies[WM8960_NUM_SUPPLIES];
};
#define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0)
@@ -154,6 +165,7 @@ static const char *wm8960_adc_data_output_sel[] = {
"Left Data = Right ADC; Right Data = Left ADC",
};
static const char *wm8960_dmonomix[] = {"Stereo", "Mono"};
+static const char *wm8960_dacslope[] = {"Normal", "Sloping"};
static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
@@ -164,13 +176,14 @@ static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel),
SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix),
+ SOC_ENUM_SINGLE(WM8960_DACCTL2, 1, 2, wm8960_dacslope),
};
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
-static int wm8960_set_deemph(struct snd_soc_codec *codec)
+static int wm8960_set_deemph(struct snd_soc_component *component)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
int val, i, best;
/* If we're using deemphasis select the nearest available sample
@@ -189,17 +202,17 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec)
val = 0;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, WM8960_DACCTL1,
+ return snd_soc_component_update_bits(component, WM8960_DACCTL1,
0x6, val);
}
static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = wm8960->deemph;
return 0;
@@ -208,8 +221,8 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
@@ -217,7 +230,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
wm8960->deemph = deemph;
- return wm8960_set_deemph(codec);
+ return wm8960_set_deemph(component);
}
static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
@@ -226,11 +239,10 @@ static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1);
-static const unsigned int micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(micboost_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0),
2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0),
-};
+);
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
@@ -240,13 +252,13 @@ SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
7, 1, 1),
-SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
-SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv),
-SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv),
-SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume",
WM8960_RINPATH, 4, 3, 0, micboost_tlv),
@@ -307,6 +319,7 @@ SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
SOC_ENUM("ADC Data Output Select", wm8960_enum[6]),
SOC_ENUM("DAC Mono Mix", wm8960_enum[7]),
+SOC_ENUM("DAC Filter Characteristics", wm8960_enum[8]),
};
static const struct snd_kcontrol_new wm8960_lin_boost[] = {
@@ -471,11 +484,11 @@ static const struct snd_soc_dapm_route audio_paths_capless[] = {
{ "OUT3 VMID", NULL, "Right Output Mixer" },
};
-static int wm8960_add_widgets(struct snd_soc_codec *codec)
+static int wm8960_add_widgets(struct snd_soc_component *component)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
struct wm8960_data *pdata = &wm8960->pdata;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct snd_soc_dapm_widget *w;
snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
@@ -505,7 +518,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
* 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) {
+ list_for_each_entry(w, &component->card->widgets, list) {
if (w->dapm != dapm)
continue;
if (strcmp(w->name, "LOUT1 PGA") == 0)
@@ -522,15 +535,15 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -574,7 +587,7 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- snd_soc_write(codec, WM8960_IFACE1, iface);
+ snd_soc_component_write(component, WM8960_IFACE1, iface);
return 0;
}
@@ -605,70 +618,109 @@ static const int bclk_divs[] = {
120, 160, 220, 240, 320, 320, 320
};
-static int wm8960_configure_clocking(struct snd_soc_codec *codec)
+/**
+ * wm8960_configure_sysclk - checks if there is a sysclk frequency available
+ * The sysclk must be chosen such that:
+ * - sysclk = MCLK / sysclk_divs
+ * - lrclk = sysclk / dac_divs
+ * - 10 * bclk = sysclk / bclk_divs
+ *
+ * @wm8960: codec private data
+ * @mclk: MCLK used to derive sysclk
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * -1, in case no sysclk frequency available found
+ * >=0, in case we could derive bclk and lrclk from sysclk using
+ * (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
+ int *sysclk_idx, int *dac_idx, int *bclk_idx)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- int sysclk, bclk, lrclk, freq_out, freq_in;
- u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
+ int sysclk, bclk, lrclk;
int i, j, k;
+ int diff;
- if (!(iface1 & (1<<6))) {
- dev_dbg(codec->dev,
- "Codec is slave mode, no need to configure clock\n");
- return 0;
- }
-
- if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) {
- dev_err(codec->dev, "No MCLK configured\n");
- return -EINVAL;
- }
+ /* marker for no match */
+ *bclk_idx = -1;
- freq_in = wm8960->freq_in;
bclk = wm8960->bclk;
lrclk = wm8960->lrclk;
- /*
- * If it's sysclk auto mode, check if the MCLK can provide sysclk or
- * not. If MCLK can provide sysclk, using MCLK to provide sysclk
- * directly. Otherwise, auto select a available pll out frequency
- * and set PLL.
- */
- if (wm8960->clk_id == WM8960_SYSCLK_AUTO) {
- /* disable the PLL and using MCLK to provide sysclk */
- wm8960_set_pll(codec, 0, 0);
- freq_out = freq_in;
- } else if (wm8960->sysclk) {
- freq_out = wm8960->sysclk;
- } else {
- dev_err(codec->dev, "No SYSCLK configured\n");
- return -EINVAL;
- }
/* check if the sysclk frequency is available. */
for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
if (sysclk_divs[i] == -1)
continue;
- sysclk = freq_out / sysclk_divs[i];
+ sysclk = mclk / sysclk_divs[i];
for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
- if (sysclk == dac_divs[j] * lrclk) {
- for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
- if (sysclk == bclk * bclk_divs[k] / 10)
- break;
- if (k != ARRAY_SIZE(bclk_divs))
+ if (sysclk != dac_divs[j] * lrclk)
+ continue;
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ diff = sysclk - bclk * bclk_divs[k] / 10;
+ if (diff == 0) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
break;
+ }
}
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
}
if (j != ARRAY_SIZE(dac_divs))
break;
}
+ return *bclk_idx;
+}
- if (i != ARRAY_SIZE(sysclk_divs)) {
- goto configure_clock;
- } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
- dev_err(codec->dev, "failed to configure clock\n");
- return -EINVAL;
- }
- /* get a available pll out frequency and set pll */
- for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+/**
+ * wm8960_configure_pll - checks if there is a PLL out frequency available
+ * The PLL out frequency must be chosen such that:
+ * - sysclk = lrclk * dac_divs
+ * - freq_out = sysclk * sysclk_divs
+ * - 10 * sysclk = bclk * bclk_divs
+ *
+ * If we cannot find an exact match for (sysclk, lrclk, bclk)
+ * triplet, we relax the bclk such that bclk is chosen as the
+ * closest available frequency greater than expected bclk.
+ *
+ * @component: component structure
+ * @freq_in: input frequency used to derive freq out via PLL
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * < 0, in case no PLL frequency out available was found
+ * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using
+ * (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_pll(struct snd_soc_component *component, int freq_in,
+ int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
+ int sysclk, bclk, lrclk, freq_out;
+ int diff, closest, best_freq_out;
+ int i, j, k;
+
+ bclk = wm8960->bclk;
+ lrclk = wm8960->lrclk;
+ closest = freq_in;
+
+ best_freq_out = -EINVAL;
+ *sysclk_idx = *dac_idx = *bclk_idx = -1;
+
+ /*
+ * From Datasheet, the PLL performs best when f2 is between
+ * 90MHz and 100MHz, the desired sysclk output is 11.2896MHz
+ * or 12.288MHz, then sysclkdiv = 2 is the best choice.
+ * So search sysclk_divs from 2 to 1 other than from 1 to 2.
+ */
+ for (i = ARRAY_SIZE(sysclk_divs) - 1; i >= 0; --i) {
if (sysclk_divs[i] == -1)
continue;
for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
@@ -676,37 +728,100 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
freq_out = sysclk * sysclk_divs[i];
for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
- if (sysclk == bclk * bclk_divs[k] / 10 &&
- is_pll_freq_available(freq_in, freq_out)) {
- wm8960_set_pll(codec,
- freq_in, freq_out);
- break;
- } else {
+ if (!is_pll_freq_available(freq_in, freq_out))
continue;
+
+ diff = sysclk - bclk * bclk_divs[k] / 10;
+ if (diff == 0) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ return freq_out;
+ }
+ if (diff > 0 && closest > diff) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ closest = diff;
+ best_freq_out = freq_out;
}
}
- if (k != ARRAY_SIZE(bclk_divs))
- break;
}
- if (j != ARRAY_SIZE(dac_divs))
- break;
}
- if (i == ARRAY_SIZE(sysclk_divs)) {
- dev_err(codec->dev, "failed to configure clock\n");
+ return best_freq_out;
+}
+static int wm8960_configure_clocking(struct snd_soc_component *component)
+{
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
+ int freq_out, freq_in;
+ u16 iface1 = snd_soc_component_read(component, WM8960_IFACE1);
+ int i, j, k;
+ int ret;
+
+ /*
+ * For Slave mode clocking should still be configured,
+ * so this if statement should be removed, but some platform
+ * may not work if the sysclk is not configured, to avoid such
+ * compatible issue, just add '!wm8960->sysclk' condition in
+ * this if statement.
+ */
+ if (!(iface1 & (1 << 6)) && !wm8960->sysclk) {
+ dev_warn(component->dev,
+ "slave mode, but proceeding with no clock configuration\n");
+ return 0;
+ }
+
+ if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) {
+ dev_err(component->dev, "No MCLK configured\n");
return -EINVAL;
}
+ freq_in = wm8960->freq_in;
+ /*
+ * If it's sysclk auto mode, check if the MCLK can provide sysclk or
+ * not. If MCLK can provide sysclk, using MCLK to provide sysclk
+ * directly. Otherwise, auto select a available pll out frequency
+ * and set PLL.
+ */
+ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) {
+ /* disable the PLL and using MCLK to provide sysclk */
+ wm8960_set_pll(component, 0, 0);
+ freq_out = freq_in;
+ } else if (wm8960->sysclk) {
+ freq_out = wm8960->sysclk;
+ } else {
+ dev_err(component->dev, "No SYSCLK configured\n");
+ return -EINVAL;
+ }
+
+ if (wm8960->clk_id != WM8960_SYSCLK_PLL) {
+ ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k);
+ if (ret >= 0) {
+ goto configure_clock;
+ } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
+ dev_err(component->dev, "failed to configure clock\n");
+ return -EINVAL;
+ }
+ }
+
+ freq_out = wm8960_configure_pll(component, freq_in, &i, &j, &k);
+ if (freq_out < 0) {
+ dev_err(component->dev, "failed to configure clock via PLL\n");
+ return freq_out;
+ }
+ wm8960_set_pll(component, freq_in, freq_out);
+
configure_clock:
/* configure sysclk clock */
- snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1);
+ snd_soc_component_update_bits(component, WM8960_CLOCK1, 3 << 1, i << 1);
/* configure frame clock */
- snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3);
- snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6);
+ snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 3, j << 3);
+ snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 6, j << 6);
/* configure bit clock */
- snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k);
+ snd_soc_component_update_bits(component, WM8960_CLOCK2, 0xf, k);
return 0;
}
@@ -715,9 +830,9 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8960_IFACE1) & 0xfff3;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int i;
@@ -741,8 +856,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
iface |= 0x000c;
break;
}
+ fallthrough;
default:
- dev_err(codec->dev, "unsupported width %d\n",
+ dev_err(component->dev, "unsupported width %d\n",
params_width(params));
return -EINVAL;
}
@@ -750,23 +866,22 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
wm8960->lrclk = params_rate(params);
/* Update filters for the new rate */
if (tx) {
- wm8960_set_deemph(codec);
+ wm8960_set_deemph(component);
} else {
for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
if (alc_rates[i].rate == params_rate(params))
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8960_ADDCTL3, 0x7,
alc_rates[i].val);
}
/* set iface */
- snd_soc_write(codec, WM8960_IFACE1, iface);
+ snd_soc_component_write(component, WM8960_IFACE1, iface);
wm8960->is_stream_in_use[tx] = true;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON &&
- !wm8960->is_stream_in_use[!tx])
- return wm8960_configure_clocking(codec);
+ if (!wm8960->is_stream_in_use[!tx])
+ return wm8960_configure_clocking(component);
return 0;
}
@@ -774,8 +889,8 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
static int wm8960_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
wm8960->is_stream_in_use[tx] = false;
@@ -783,47 +898,49 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8960_mute(struct snd_soc_dai *dai, int mute)
+static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute)
- snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8);
+ snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0x8);
else
- snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0);
+ snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0);
return 0;
}
-static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
+static int wm8960_set_bias_level_out3(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 pm2 = snd_soc_component_read(component, WM8960_POWER2);
int ret;
+ ktime_t tout;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_STANDBY:
if (!IS_ERR(wm8960->mclk)) {
ret = clk_prepare_enable(wm8960->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable MCLK: %d\n",
ret);
return ret;
}
}
- ret = wm8960_configure_clocking(codec);
+ ret = wm8960_configure_clocking(component);
if (ret)
return ret;
/* Set VMID to 2x50k */
- snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
+ snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x80);
break;
case SND_SOC_BIAS_ON:
@@ -832,7 +949,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
* disable the pll
*/
if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
- wm8960_set_pll(codec, 0, 0);
+ wm8960_set_pll(component, 0, 0);
if (!IS_ERR(wm8960->mclk))
clk_disable_unprepare(wm8960->mclk);
@@ -845,50 +962,56 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ /* ensure discharge is complete */
+ tout = WM8960_DSCH_TOUT - ktime_ms_delta(ktime_get(), wm8960->dsch_start);
+ if (tout > 0)
+ msleep(tout);
+
regcache_sync(wm8960->regmap);
/* Enable anti-pop features */
- snd_soc_write(codec, WM8960_APOP1,
+ snd_soc_component_write(component, 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);
+ snd_soc_component_update_bits(component, WM8960_POWER1, 0x80, 0x80);
msleep(100);
/* Enable VREF */
- snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF,
+ snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF,
WM8960_VREF);
/* Disable anti-pop features */
- snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
+ snd_soc_component_write(component, WM8960_APOP1, WM8960_BUFIOEN);
}
/* Set VMID to 2x250k */
- snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100);
+ snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x100);
break;
case SND_SOC_BIAS_OFF:
/* Enable anti-pop features */
- snd_soc_write(codec, WM8960_APOP1,
+ snd_soc_component_write(component, 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);
+ /* Disable VMID and VREF, mark discharge */
+ snd_soc_component_write(component, WM8960_POWER1, 0);
+ wm8960->dsch_start = ktime_get();
break;
}
return 0;
}
-static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
+static int wm8960_set_bias_level_capless(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 pm2 = snd_soc_component_read(component, WM8960_POWER2);
int reg, ret;
switch (level) {
@@ -896,10 +1019,10 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_STANDBY:
/* Enable anti pop mode */
- snd_soc_update_bits(codec, WM8960_APOP1,
+ snd_soc_component_update_bits(component, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN,
WM8960_POBCTRL | WM8960_SOFT_ST |
@@ -913,20 +1036,20 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
reg |= WM8960_PWR2_ROUT1;
if (wm8960->out3 && wm8960->out3->power)
reg |= WM8960_PWR2_OUT3;
- snd_soc_update_bits(codec, WM8960_POWER2,
+ snd_soc_component_update_bits(component, WM8960_POWER2,
WM8960_PWR2_LOUT1 |
WM8960_PWR2_ROUT1 |
WM8960_PWR2_OUT3, reg);
/* Enable VMID at 2*50k */
- snd_soc_update_bits(codec, WM8960_POWER1,
+ snd_soc_component_update_bits(component, WM8960_POWER1,
WM8960_VMID_MASK, 0x80);
/* Ramp */
msleep(100);
/* Enable VREF */
- snd_soc_update_bits(codec, WM8960_POWER1,
+ snd_soc_component_update_bits(component, WM8960_POWER1,
WM8960_VREF, WM8960_VREF);
msleep(100);
@@ -934,14 +1057,14 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
if (!IS_ERR(wm8960->mclk)) {
ret = clk_prepare_enable(wm8960->mclk);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable MCLK: %d\n",
ret);
return ret;
}
}
- ret = wm8960_configure_clocking(codec);
+ ret = wm8960_configure_clocking(component);
if (ret)
return ret;
@@ -953,20 +1076,20 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
* disable the pll
*/
if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
- wm8960_set_pll(codec, 0, 0);
+ wm8960_set_pll(component, 0, 0);
if (!IS_ERR(wm8960->mclk))
clk_disable_unprepare(wm8960->mclk);
/* Enable anti-pop mode */
- snd_soc_update_bits(codec, WM8960_APOP1,
+ snd_soc_component_update_bits(component, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN);
/* Disable VMID and VREF */
- snd_soc_update_bits(codec, WM8960_POWER1,
+ snd_soc_component_update_bits(component, WM8960_POWER1,
WM8960_VREF | WM8960_VMID_MASK, 0);
break;
@@ -979,15 +1102,15 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- switch (snd_soc_codec_get_bias_level(codec)) {
+ switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_PREPARE:
/* Disable HP discharge */
- snd_soc_update_bits(codec, WM8960_APOP2,
+ snd_soc_component_update_bits(component, WM8960_APOP2,
WM8960_DISOP | WM8960_DRES_MASK,
0);
/* Disable anti-pop features */
- snd_soc_update_bits(codec, WM8960_APOP1,
+ snd_soc_component_update_bits(component, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN,
WM8960_POBCTRL | WM8960_SOFT_ST |
@@ -1086,7 +1209,7 @@ static int pll_factors(unsigned int source, unsigned int target,
return 0;
}
-static int wm8960_set_pll(struct snd_soc_codec *codec,
+static int wm8960_set_pll(struct snd_soc_component *component,
unsigned int freq_in, unsigned int freq_out)
{
u16 reg;
@@ -1101,29 +1224,29 @@ static int wm8960_set_pll(struct snd_soc_codec *codec,
/* 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);
+ snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0);
+ snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0);
if (!freq_in || !freq_out)
return 0;
- reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
+ reg = snd_soc_component_read(component, WM8960_PLL1) & ~0x3f;
reg |= pll_div.pre_div << 4;
reg |= pll_div.n;
if (pll_div.k) {
reg |= 0x20;
- snd_soc_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_component_write(component, WM8960_PLL2, (pll_div.k >> 16) & 0xff);
+ snd_soc_component_write(component, WM8960_PLL3, (pll_div.k >> 8) & 0xff);
+ snd_soc_component_write(component, WM8960_PLL4, pll_div.k & 0xff);
}
- snd_soc_write(codec, WM8960_PLL1, reg);
+ snd_soc_component_write(component, WM8960_PLL1, reg);
/* Turn it on */
- snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1);
+ snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0x1);
msleep(250);
- snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1);
+ snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0x1);
return 0;
}
@@ -1131,43 +1254,43 @@ static int wm8960_set_pll(struct snd_soc_codec *codec,
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;
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
wm8960->freq_in = freq_in;
if (pll_id == WM8960_SYSCLK_AUTO)
return 0;
- return wm8960_set_pll(codec, freq_in, freq_out);
+ return wm8960_set_pll(component, freq_in, freq_out);
}
static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
switch (div_id) {
case WM8960_SYSCLKDIV:
- reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
- snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_component_read(component, WM8960_CLOCK1) & 0x1f9;
+ snd_soc_component_write(component, WM8960_CLOCK1, reg | div);
break;
case WM8960_DACDIV:
- reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
- snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_component_read(component, WM8960_CLOCK1) & 0x1c7;
+ snd_soc_component_write(component, WM8960_CLOCK1, reg | div);
break;
case WM8960_OPCLKDIV:
- reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
- snd_soc_write(codec, WM8960_PLL1, reg | div);
+ reg = snd_soc_component_read(component, WM8960_PLL1) & 0x03f;
+ snd_soc_component_write(component, WM8960_PLL1, reg | div);
break;
case WM8960_DCLKDIV:
- reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
- snd_soc_write(codec, WM8960_CLOCK2, reg | div);
+ reg = snd_soc_component_read(component, WM8960_CLOCK2) & 0x03f;
+ snd_soc_component_write(component, WM8960_CLOCK2, reg | div);
break;
case WM8960_TOCLKSEL:
- reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
- snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
+ reg = snd_soc_component_read(component, WM8960_ADDCTL1) & 0x1fd;
+ snd_soc_component_write(component, WM8960_ADDCTL1, reg | div);
break;
default:
return -EINVAL;
@@ -1176,27 +1299,27 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return 0;
}
-static int wm8960_set_bias_level(struct snd_soc_codec *codec,
+static int wm8960_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
- return wm8960->set_bias_level(codec, level);
+ return wm8960->set_bias_level(component, level);
}
static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM8960_SYSCLK_MCLK:
- snd_soc_update_bits(codec, WM8960_CLOCK1,
+ snd_soc_component_update_bits(component, WM8960_CLOCK1,
0x1, WM8960_SYSCLK_MCLK);
break;
case WM8960_SYSCLK_PLL:
- snd_soc_update_bits(codec, WM8960_CLOCK1,
+ snd_soc_component_update_bits(component, WM8960_CLOCK1,
0x1, WM8960_SYSCLK_PLL);
break;
case WM8960_SYSCLK_AUTO:
@@ -1220,11 +1343,12 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
static const struct snd_soc_dai_ops wm8960_dai_ops = {
.hw_params = wm8960_hw_params,
.hw_free = wm8960_hw_free,
- .digital_mute = wm8960_mute,
+ .mute_stream = wm8960_mute,
.set_fmt = wm8960_set_dai_fmt,
.set_clkdiv = wm8960_set_dai_clkdiv,
.set_pll = wm8960_set_dai_pll,
.set_sysclk = wm8960_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8960_dai = {
@@ -1242,12 +1366,12 @@ static struct snd_soc_dai_driver wm8960_dai = {
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.ops = &wm8960_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8960_probe(struct snd_soc_codec *codec)
+static int wm8960_probe(struct snd_soc_component *component)
{
- struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
struct wm8960_data *pdata = &wm8960->pdata;
if (pdata->capless)
@@ -1255,17 +1379,20 @@ static int wm8960_probe(struct snd_soc_codec *codec)
else
wm8960->set_bias_level = wm8960_set_bias_level_out3;
- snd_soc_add_codec_controls(codec, wm8960_snd_controls,
+ snd_soc_add_component_controls(component, wm8960_snd_controls,
ARRAY_SIZE(wm8960_snd_controls));
- wm8960_add_widgets(codec);
+ wm8960_add_widgets(component);
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 snd_soc_component_driver soc_component_dev_wm8960 = {
+ .probe = wm8960_probe,
+ .set_bias_level = wm8960_set_bias_level,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8960_regmap = {
@@ -1275,7 +1402,7 @@ static const struct regmap_config wm8960_regmap = {
.reg_defaults = wm8960_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8960_volatile,
};
@@ -1290,14 +1417,21 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
if (of_property_read_bool(np, "wlf,shared-lrclk"))
pdata->shared_lrclk = true;
+
+ of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_cfg,
+ ARRAY_SIZE(pdata->gpio_cfg));
+
+ of_property_read_u32_array(np, "wlf,hp-cfg", pdata->hp_cfg,
+ ARRAY_SIZE(pdata->hp_cfg));
}
-static int wm8960_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8960_i2c_probe(struct i2c_client *i2c)
{
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8960_priv *wm8960;
+ unsigned int i;
int ret;
+ u8 val;
wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
GFP_KERNEL);
@@ -1308,21 +1442,55 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(wm8960->mclk)) {
if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
+ } else {
+ ret = clk_get_rate(wm8960->mclk);
+ if (ret >= 0) {
+ wm8960->freq_in = ret;
+ } else {
+ dev_err(&i2c->dev, "Failed to read MCLK rate: %d\n",
+ ret);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8960->supplies); i++)
+ wm8960->supplies[i].supply = wm8960_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8960->supplies),
+ wm8960->supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8960->supplies),
+ wm8960->supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
}
wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
- if (IS_ERR(wm8960->regmap))
- return PTR_ERR(wm8960->regmap);
+ if (IS_ERR(wm8960->regmap)) {
+ ret = PTR_ERR(wm8960->regmap);
+ goto bulk_disable;
+ }
if (pdata)
memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data));
else if (i2c->dev.of_node)
wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
+ ret = i2c_master_recv(i2c, &val, sizeof(val));
+ if (ret >= 0) {
+ dev_err(&i2c->dev, "Not wm8960, wm8960 reg can not read by i2c\n");
+ ret = -EINVAL;
+ goto bulk_disable;
+ }
+
ret = wm8960_reset(wm8960->regmap);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to issue reset\n");
- return ret;
+ goto bulk_disable;
}
if (wm8960->pdata.shared_lrclk) {
@@ -1331,7 +1499,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable LRCM: %d\n",
ret);
- return ret;
+ goto bulk_disable;
}
}
@@ -1347,36 +1515,69 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
+ /* ADCLRC pin configured as GPIO. */
+ regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6,
+ wm8960->pdata.gpio_cfg[0] << 6);
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 0xF << 4,
+ wm8960->pdata.gpio_cfg[1] << 4);
+
+ /* Enable headphone jack detect */
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 3 << 2,
+ wm8960->pdata.hp_cfg[0] << 2);
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 3 << 5,
+ wm8960->pdata.hp_cfg[1] << 5);
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL1, 3,
+ wm8960->pdata.hp_cfg[2]);
+
i2c_set_clientdata(i2c, wm8960);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8960, &wm8960_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8960, &wm8960_dai, 1);
+ if (ret)
+ goto bulk_disable;
+
+ return 0;
+bulk_disable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies);
return ret;
}
-static int wm8960_i2c_remove(struct i2c_client *client)
+static void wm8960_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies);
}
static const struct i2c_device_id wm8960_i2c_id[] = {
- { "wm8960", 0 },
+ { "wm8960" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
+#if defined(CONFIG_OF)
static const struct of_device_id wm8960_of_match[] = {
{ .compatible = "wlf,wm8960", },
{ }
};
MODULE_DEVICE_TABLE(of, wm8960_of_match);
+#endif
+
+#if defined(CONFIG_ACPI)
+static const struct acpi_device_id wm8960_acpi_match[] = {
+ { "1AEC8960", 0 }, /* Wolfson PCI ID + part ID */
+ { "10138960", 0 }, /* Cirrus Logic PCI ID + part ID */
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, wm8960_acpi_match);
+#endif
static struct i2c_driver wm8960_i2c_driver = {
.driver = {
.name = "wm8960",
- .of_match_table = wm8960_of_match,
+ .of_match_table = of_match_ptr(wm8960_of_match),
+ .acpi_match_table = ACPI_PTR(wm8960_acpi_match),
},
.probe = wm8960_i2c_probe,
.remove = wm8960_i2c_remove,
diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h
index ab3220d3411d..e8ff33b188e9 100644
--- a/sound/soc/codecs/wm8960.h
+++ b/sound/soc/codecs/wm8960.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -80,9 +77,9 @@
#define WM8960_SYSCLK_DIV_1 (0 << 1)
#define WM8960_SYSCLK_DIV_2 (2 << 1)
-#define WM8960_SYSCLK_MCLK (0 << 0)
+#define WM8960_SYSCLK_AUTO (0 << 0)
#define WM8960_SYSCLK_PLL (1 << 0)
-#define WM8960_SYSCLK_AUTO (2 << 0)
+#define WM8960_SYSCLK_MCLK (2 << 0)
#define WM8960_DAC_DIV_1 (0 << 3)
#define WM8960_DAC_DIV_1_5 (1 << 3)
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index e30446a04740..cfb8cfc91873 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8961.c -- WM8961 ALSA SoC Audio driver
*
@@ -5,10 +6,6 @@
*
* 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
*/
@@ -194,89 +191,89 @@ static bool wm8961_readable(struct device *dev, unsigned int reg)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 hp_reg = snd_soc_component_read(component, WM8961_ANALOGUE_HP_0);
+ u16 cp_reg = snd_soc_component_read(component, WM8961_CHARGE_PUMP_1);
+ u16 pwr_reg = snd_soc_component_read(component, WM8961_PWR_MGMT_2);
+ u16 dcs_reg = snd_soc_component_read(component, WM8961_DC_SERVO_1);
int timeout = 500;
if (event & SND_SOC_DAPM_POST_PMU) {
/* Make sure the output is shorted */
hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8961_CHARGE_PUMP_1, cp_reg);
mdelay(5);
/* Enable the PGA */
pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
- snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg);
/* Second stage enable */
hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg);
/* Enable the DC servo & trigger startup */
dcs_reg |=
WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
- dev_dbg(codec->dev, "Enabling DC servo\n");
+ dev_dbg(component->dev, "Enabling DC servo\n");
- snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+ snd_soc_component_write(component, WM8961_DC_SERVO_1, dcs_reg);
do {
msleep(1);
- dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+ dcs_reg = snd_soc_component_read(component, WM8961_DC_SERVO_1);
} while (--timeout &&
dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
WM8961_DCS_TRIG_STARTUP_HPL));
if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
WM8961_DCS_TRIG_STARTUP_HPL))
- dev_err(codec->dev, "DC servo timed out\n");
+ dev_err(component->dev, "DC servo timed out\n");
else
- dev_dbg(codec->dev, "DC servo startup complete\n");
+ dev_dbg(component->dev, "DC servo startup complete\n");
/* Enable the output stage */
hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg);
/* Remove the short on the output stage */
hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg);
}
if (event & SND_SOC_DAPM_PRE_PMD) {
/* Short the output */
hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg);
/* Disable the output stage */
hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg);
/* Disable DC offset cancellation */
dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
WM8961_DCS_ENA_CHAN_HPL);
- snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+ snd_soc_component_write(component, WM8961_DC_SERVO_1, dcs_reg);
/* Finish up */
hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
- snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, 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,
+ dev_dbg(component->dev, "Disabling charge pump\n");
+ snd_soc_component_write(component, WM8961_CHARGE_PUMP_1,
cp_reg & ~WM8961_CP_ENA);
}
@@ -286,28 +283,28 @@ static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 pwr_reg = snd_soc_component_read(component, WM8961_PWR_MGMT_2);
+ u16 spk_reg = snd_soc_component_read(component, WM8961_CLASS_D_CONTROL_1);
if (event & SND_SOC_DAPM_POST_PMU) {
/* Enable the PGA */
pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA;
- snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8961_CLASS_D_CONTROL_1, spk_reg);
}
if (event & SND_SOC_DAPM_PRE_PMD) {
/* Disable the amplifier */
spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA);
- snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+ snd_soc_component_write(component, WM8961_CLASS_D_CONTROL_1, spk_reg);
/* Disable the PGA */
pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA);
- snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+ snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg);
}
return 0;
@@ -505,15 +502,15 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8961_priv *wm8961 = snd_soc_component_get_drvdata(component);
int i, best, target, fs;
u16 reg;
fs = params_rate(params);
if (!wm8961->sysclk) {
- dev_err(codec->dev, "MCLK has not been specified\n");
+ dev_err(component->dev, "MCLK has not been specified\n");
return -EINVAL;
}
@@ -524,23 +521,23 @@ static int wm8961_hw_params(struct snd_pcm_substream *substream,
abs(wm8961_srate[best].rate - fs))
best = i;
}
- reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3);
+ reg = snd_soc_component_read(component, 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",
+ snd_soc_component_write(component, WM8961_ADDITIONAL_CONTROL_3, reg);
+ dev_dbg(component->dev, "Selected SRATE %dHz for %dHz\n",
wm8961_srate[best].rate, fs);
/* Select a CLK_SYS/fs ratio equal to or higher than required */
target = wm8961->sysclk / fs;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"SYSCLK must be at least 64*fs for DAC\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"SYSCLK must be at least 256*fs for ADC\n");
return -EINVAL;
}
@@ -550,19 +547,19 @@ static int wm8961_hw_params(struct snd_pcm_substream *substream,
break;
}
if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
- dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
+ dev_err(component->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",
+ dev_dbg(component->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
wm8961->sysclk / fs);
- reg = snd_soc_read(codec, WM8961_CLOCKING_4);
+ reg = snd_soc_component_read(component, WM8961_CLOCKING_4);
reg &= ~WM8961_CLK_SYS_RATE_MASK;
reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
- snd_soc_write(codec, WM8961_CLOCKING_4, reg);
+ snd_soc_component_write(component, WM8961_CLOCKING_4, reg);
- reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+ reg = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_0);
reg &= ~WM8961_WL_MASK;
switch (params_width(params)) {
case 16:
@@ -579,15 +576,15 @@ static int wm8961_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg);
+ snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_0, reg);
/* Sloping stop-band filter is recommended for <= 24kHz */
- reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+ reg = snd_soc_component_read(component, 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);
+ snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_2, reg);
return 0;
}
@@ -596,25 +593,25 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8961_priv *wm8961 = snd_soc_component_get_drvdata(component);
+ u16 reg = snd_soc_component_read(component, WM8961_CLOCKING1);
if (freq > 33000000) {
- dev_err(codec->dev, "MCLK must be <33MHz\n");
+ dev_err(component->dev, "MCLK must be <33MHz\n");
return -EINVAL;
}
if (freq > 16500000) {
- dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq);
+ dev_dbg(component->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);
+ dev_dbg(component->dev, "Using MCLK/1 for %dHz MCLK\n", freq);
reg &= ~WM8961_MCLKDIV;
}
- snd_soc_write(codec, WM8961_CLOCKING1, reg);
+ snd_soc_component_write(component, WM8961_CLOCKING1, reg);
wm8961->sysclk = freq;
@@ -623,17 +620,17 @@ static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
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);
+ struct snd_soc_component *component = dai->component;
+ u16 aif = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_0);
aif &= ~(WM8961_BCLKINV | WM8961_LRP |
WM8961_MS | WM8961_FORMAT_MASK);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif |= WM8961_MS;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -653,6 +650,7 @@ static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_B:
aif |= WM8961_LRP;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif |= 3;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -684,26 +682,26 @@ static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
+ return snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_0, aif);
}
static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_2);
+ struct snd_soc_component *component = dai->component;
+ u16 reg = snd_soc_component_read(component, WM8961_ADDITIONAL_CONTROL_2);
if (tristate)
reg |= WM8961_TRIS;
else
reg &= ~WM8961_TRIS;
- return snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg);
+ return snd_soc_component_write(component, WM8961_ADDITIONAL_CONTROL_2, reg);
}
-static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
+static int wm8961_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_1);
+ struct snd_soc_component *component = dai->component;
+ u16 reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_1);
if (mute)
reg |= WM8961_DACMU;
@@ -712,27 +710,27 @@ static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
msleep(17);
- return snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_1, reg);
+ return snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_1, reg);
}
static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 reg;
switch (div_id) {
case WM8961_BCLK:
- reg = snd_soc_read(codec, WM8961_CLOCKING2);
+ reg = snd_soc_component_read(component, WM8961_CLOCKING2);
reg &= ~WM8961_BCLKDIV_MASK;
reg |= div;
- snd_soc_write(codec, WM8961_CLOCKING2, reg);
+ snd_soc_component_write(component, WM8961_CLOCKING2, reg);
break;
case WM8961_LRCLK:
- reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_2);
+ reg = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_2);
reg &= ~WM8961_LRCLK_RATE_MASK;
reg |= div;
- snd_soc_write(codec, WM8961_AUDIO_INTERFACE_2, reg);
+ snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_2, reg);
break;
default:
@@ -742,9 +740,10 @@ static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
return 0;
}
-static int wm8961_set_bias_level(struct snd_soc_codec *codec,
+static int wm8961_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u16 reg;
/* This is all slightly unusual since we have no bypass paths
@@ -757,36 +756,36 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
/* Enable bias generation */
- reg = snd_soc_read(codec, WM8961_ANTI_POP);
+ reg = snd_soc_component_read(component, WM8961_ANTI_POP);
reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
- snd_soc_write(codec, WM8961_ANTI_POP, reg);
+ snd_soc_component_write(component, WM8961_ANTI_POP, reg);
/* VMID=2*50k, VREF */
- reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VMIDSEL_MASK;
reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF;
- snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg);
}
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) {
/* VREF off */
- reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VREF;
- snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg);
/* Bias generation off */
- reg = snd_soc_read(codec, WM8961_ANTI_POP);
+ reg = snd_soc_component_read(component, WM8961_ANTI_POP);
reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN);
- snd_soc_write(codec, WM8961_ANTI_POP, reg);
+ snd_soc_component_write(component, WM8961_ANTI_POP, reg);
/* VMID off */
- reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VMIDSEL_MASK;
- snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg);
}
break;
@@ -808,9 +807,10 @@ 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,
+ .mute_stream = wm8961_mute,
.set_tristate = wm8961_set_tristate,
.set_clkdiv = wm8961_set_clkdiv,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8961_dai = {
@@ -830,51 +830,51 @@ static struct snd_soc_dai_driver wm8961_dai = {
.ops = &wm8961_dai_ops,
};
-static int wm8961_probe(struct snd_soc_codec *codec)
+static int wm8961_probe(struct snd_soc_component *component)
{
u16 reg;
/* Enable class W */
- reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B);
+ reg = snd_soc_component_read(component, WM8961_CHARGE_PUMP_B);
reg |= WM8961_CP_DYN_PWR_MASK;
- snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg);
+ snd_soc_component_write(component, WM8961_CHARGE_PUMP_B, reg);
/* Latch volume update bits (right channel only, we always
* write both out) and default ZC on. */
- reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME);
- snd_soc_write(codec, WM8961_ROUT1_VOLUME,
+ reg = snd_soc_component_read(component, WM8961_ROUT1_VOLUME);
+ snd_soc_component_write(component, 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,
+ snd_soc_component_write(component, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
+ reg = snd_soc_component_read(component, WM8961_ROUT2_VOLUME);
+ snd_soc_component_write(component, WM8961_ROUT2_VOLUME,
reg | WM8961_SPKRZC | WM8961_SPKVU);
- snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);
+ snd_soc_component_write(component, 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);
+ reg = snd_soc_component_read(component, WM8961_RIGHT_ADC_VOLUME);
+ snd_soc_component_write(component, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
+ reg = snd_soc_component_read(component, WM8961_RIGHT_INPUT_VOLUME);
+ snd_soc_component_write(component, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);
/* Use soft mute by default */
- reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+ reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_2);
reg |= WM8961_DACSMM;
- snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+ snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_2, reg);
/* Use automatic clocking mode by default; for now this is all
* we support.
*/
- reg = snd_soc_read(codec, WM8961_CLOCKING_3);
+ reg = snd_soc_component_read(component, WM8961_CLOCKING_3);
reg &= ~WM8961_MANUAL_MODE;
- snd_soc_write(codec, WM8961_CLOCKING_3, reg);
+ snd_soc_component_write(component, WM8961_CLOCKING_3, reg);
return 0;
}
#ifdef CONFIG_PM
-static int wm8961_resume(struct snd_soc_codec *codec)
+static int wm8961_resume(struct snd_soc_component *component)
{
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -882,18 +882,20 @@ static int wm8961_resume(struct snd_soc_codec *codec)
#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 snd_soc_component_driver soc_component_dev_wm8961 = {
+ .probe = wm8961_probe,
+ .resume = wm8961_resume,
+ .set_bias_level = wm8961_set_bias_level,
+ .controls = wm8961_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8961_snd_controls),
+ .dapm_widgets = wm8961_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8961_regmap = {
@@ -903,14 +905,13 @@ static const struct regmap_config wm8961_regmap = {
.reg_defaults = wm8961_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8961_volatile,
.readable_reg = wm8961_readable,
};
-static int wm8961_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8961_i2c_probe(struct i2c_client *i2c)
{
struct wm8961_priv *wm8961;
unsigned int val;
@@ -959,31 +960,30 @@ static int wm8961_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8961);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8961, &wm8961_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8961" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
+static const struct of_device_id wm8961_of_match[] __maybe_unused = {
+ { .compatible = "wlf,wm8961", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8961_of_match);
+
static struct i2c_driver wm8961_i2c_driver = {
.driver = {
.name = "wm8961",
+ .of_match_table = of_match_ptr(wm8961_of_match),
},
- .probe = wm8961_i2c_probe,
- .remove = wm8961_i2c_remove,
+ .probe = wm8961_i2c_probe,
.id_table = wm8961_i2c_id,
};
diff --git a/sound/soc/codecs/wm8961.h b/sound/soc/codecs/wm8961.h
index 1d736e5701c8..d4e00e5493b9 100644
--- a/sound/soc/codecs/wm8961.h
+++ b/sound/soc/codecs/wm8961.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 88223608a33f..e9e317ce6898 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -18,7 +14,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/pm_runtime.h>
@@ -55,7 +51,7 @@ static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = {
struct wm8962_priv {
struct wm8962_pdata pdata;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
int sysclk;
int sysclk_rate;
@@ -86,6 +82,7 @@ struct wm8962_priv {
#endif
int irq;
+ bool master_flag;
};
/* We can't use the same notifier block for more than one supply and
@@ -122,7 +119,7 @@ static const struct reg_default wm8962_reg[] = {
{ 5, 0x0018 }, /* R5 - ADC & DAC Control 1 */
{ 6, 0x2008 }, /* R6 - ADC & DAC Control 2 */
{ 7, 0x000A }, /* R7 - Audio Interface 0 */
-
+ { 8, 0x01E4 }, /* R8 - Clocking2 */
{ 9, 0x0300 }, /* R9 - Audio Interface 1 */
{ 10, 0x00C0 }, /* R10 - Left DAC volume */
{ 11, 0x00C0 }, /* R11 - Right DAC volume */
@@ -792,7 +789,6 @@ 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_THERMAL_SHUTDOWN_STATUS:
case WM8962_ADDITIONAL_CONTROL_4:
@@ -961,7 +957,6 @@ static bool wm8962_readable_register(struct device *dev, unsigned int reg)
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:
@@ -1475,55 +1470,55 @@ static const DECLARE_TLV_DB_RANGE(classd_tlv,
);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
-static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
+static int wm8962_dsp2_write_config(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
return regcache_sync_region(wm8962->regmap,
WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER);
}
-static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
+static int wm8962_dsp2_set_enable(struct snd_soc_component *component, 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);
+ u16 adcl = snd_soc_component_read(component, WM8962_LEFT_ADC_VOLUME);
+ u16 adcr = snd_soc_component_read(component, WM8962_RIGHT_ADC_VOLUME);
+ u16 dac = snd_soc_component_read(component, WM8962_ADC_DAC_CONTROL_1);
/* Mute the ADCs and DACs */
- snd_soc_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,
+ snd_soc_component_write(component, WM8962_LEFT_ADC_VOLUME, 0);
+ snd_soc_component_write(component, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
+ snd_soc_component_update_bits(component, WM8962_ADC_DAC_CONTROL_1,
WM8962_DAC_MUTE, WM8962_DAC_MUTE);
- snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
+ snd_soc_component_write(component, 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,
+ snd_soc_component_write(component, WM8962_LEFT_ADC_VOLUME, adcl);
+ snd_soc_component_write(component, WM8962_RIGHT_ADC_VOLUME, adcr);
+ snd_soc_component_update_bits(component, WM8962_ADC_DAC_CONTROL_1,
WM8962_DAC_MUTE, dac);
return 0;
}
-static int wm8962_dsp2_start(struct snd_soc_codec *codec)
+static int wm8962_dsp2_start(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
- wm8962_dsp2_write_config(codec);
+ wm8962_dsp2_write_config(component);
- snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
+ snd_soc_component_write(component, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
- wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+ wm8962_dsp2_set_enable(component, wm8962->dsp2_ena);
return 0;
}
-static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
+static int wm8962_dsp2_stop(struct snd_soc_component *component)
{
- wm8962_dsp2_set_enable(codec, 0);
+ wm8962_dsp2_set_enable(component, 0);
- snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
+ snd_soc_component_write(component, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
return 0;
}
@@ -1550,8 +1545,8 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
@@ -1562,11 +1557,11 @@ 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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int old = wm8962->dsp2_ena;
int ret = 0;
- int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
+ int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) &
WM8962_DSP2_ENA;
mutex_lock(&wm8962->dsp2_ena_lock);
@@ -1583,9 +1578,9 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
if (dsp2_running) {
if (wm8962->dsp2_ena)
- wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+ wm8962_dsp2_set_enable(component, wm8962->dsp2_ena);
else
- wm8962_dsp2_stop(codec);
+ wm8962_dsp2_stop(component);
}
out:
@@ -1600,7 +1595,7 @@ out:
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int ret;
/* Apply the update (if any) */
@@ -1609,17 +1604,17 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
return 0;
/* If the left PGA is enabled hit that VU bit... */
- ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
+ ret = snd_soc_component_read(component, WM8962_PWR_MGMT_2);
if (ret & WM8962_HPOUTL_PGA_ENA) {
- snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
- snd_soc_read(codec, WM8962_HPOUTL_VOLUME));
+ snd_soc_component_write(component, WM8962_HPOUTL_VOLUME,
+ snd_soc_component_read(component, WM8962_HPOUTL_VOLUME));
return 1;
}
/* ...otherwise the right. The VU is stereo. */
if (ret & WM8962_HPOUTR_PGA_ENA)
- snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
- snd_soc_read(codec, WM8962_HPOUTR_VOLUME));
+ snd_soc_component_write(component, WM8962_HPOUTR_VOLUME,
+ snd_soc_component_read(component, WM8962_HPOUTR_VOLUME));
return 1;
}
@@ -1630,7 +1625,7 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int ret;
/* Apply the update (if any) */
@@ -1639,17 +1634,17 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
return 0;
/* If the left PGA is enabled hit that VU bit... */
- ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
+ ret = snd_soc_component_read(component, WM8962_PWR_MGMT_2);
if (ret & WM8962_SPKOUTL_PGA_ENA) {
- snd_soc_write(codec, WM8962_SPKOUTL_VOLUME,
- snd_soc_read(codec, WM8962_SPKOUTL_VOLUME));
+ snd_soc_component_write(component, WM8962_SPKOUTL_VOLUME,
+ snd_soc_component_read(component, WM8962_SPKOUTL_VOLUME));
return 1;
}
/* ...otherwise the right. The VU is stereo. */
if (ret & WM8962_SPKOUTR_PGA_ENA)
- snd_soc_write(codec, WM8962_SPKOUTR_VOLUME,
- snd_soc_read(codec, WM8962_SPKOUTR_VOLUME));
+ snd_soc_component_write(component, WM8962_SPKOUTR_VOLUME,
+ snd_soc_component_read(component, WM8962_SPKOUTR_VOLUME));
return 1;
}
@@ -1708,6 +1703,8 @@ SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8962_LEFT_DAC_VOLUME,
SOC_SINGLE("DAC High Performance Switch", WM8962_ADC_DAC_CONTROL_2, 0, 1, 0),
SOC_SINGLE("DAC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 5, 1, 0),
SOC_SINGLE("ADC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 8, 1, 0),
+SOC_SINGLE("DAC Monomix Switch", WM8962_DAC_DSP_MIXING_1, WM8962_DAC_MONOMIX_SHIFT, 1, 0),
+SOC_SINGLE("ADC Monomix Switch", WM8962_THREED1, WM8962_ADC_MONOMIX_SHIFT, 1, 0),
SOC_SINGLE("ADC High Performance Switch", WM8962_ADDITIONAL_CONTROL_1,
5, 1, 0),
@@ -1844,6 +1841,49 @@ SOC_SINGLE_TLV("SPKOUTR Mixer DACR Volume", WM8962_SPEAKER_MIXER_5,
4, 1, 0, inmix_tlv),
};
+static int tp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret, reg, val, mask;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to resume device: %d\n", ret);
+ return ret;
+ }
+
+ reg = WM8962_ADDITIONAL_CONTROL_4;
+
+ if (!snd_soc_dapm_widget_name_cmp(w, "TEMP_HP")) {
+ mask = WM8962_TEMP_ENA_HP_MASK;
+ val = WM8962_TEMP_ENA_HP;
+ } else if (!snd_soc_dapm_widget_name_cmp(w, "TEMP_SPK")) {
+ mask = WM8962_TEMP_ENA_SPK_MASK;
+ val = WM8962_TEMP_ENA_SPK;
+ } else {
+ pm_runtime_put(component->dev);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ val = 0;
+ fallthrough;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = snd_soc_component_update_bits(component, reg, mask, val);
+ break;
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ pm_runtime_put(component->dev);
+ return -EINVAL;
+ }
+
+ pm_runtime_put(component->dev);
+
+ return 0;
+}
+
static int cp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1863,7 +1903,7 @@ static int cp_event(struct snd_soc_dapm_widget *w,
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
int timeout;
int reg;
int expected = (WM8962_DCS_STARTUP_DONE_HP1L |
@@ -1871,17 +1911,17 @@ static int hp_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0,
WM8962_HP1L_ENA | WM8962_HP1R_ENA,
WM8962_HP1L_ENA | WM8962_HP1R_ENA);
udelay(20);
- snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0,
WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY,
WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY);
/* Start the DC servo */
- snd_soc_update_bits(codec, WM8962_DC_SERVO_1,
+ snd_soc_component_update_bits(component, WM8962_DC_SERVO_1,
WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
WM8962_HP1L_DCS_STARTUP |
WM8962_HP1R_DCS_STARTUP,
@@ -1893,30 +1933,30 @@ static int hp_event(struct snd_soc_dapm_widget *w,
timeout = 0;
do {
msleep(1);
- reg = snd_soc_read(codec, WM8962_DC_SERVO_6);
+ reg = snd_soc_component_read(component, WM8962_DC_SERVO_6);
if (reg < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to read DCS status: %d\n",
reg);
continue;
}
- dev_dbg(codec->dev, "DCS status: %x\n", reg);
+ dev_dbg(component->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");
+ dev_err(component->dev, "DC servo timed out\n");
else
- dev_dbg(codec->dev, "DC servo complete after %dms\n",
+ dev_dbg(component->dev, "DC servo complete after %dms\n",
timeout);
- snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0,
WM8962_HP1L_ENA_OUTP |
WM8962_HP1R_ENA_OUTP,
WM8962_HP1L_ENA_OUTP |
WM8962_HP1R_ENA_OUTP);
udelay(20);
- snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0,
WM8962_HP1L_RMV_SHORT |
WM8962_HP1R_RMV_SHORT,
WM8962_HP1L_RMV_SHORT |
@@ -1924,19 +1964,19 @@ static int hp_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0,
WM8962_HP1L_RMV_SHORT |
WM8962_HP1R_RMV_SHORT, 0);
udelay(20);
- snd_soc_update_bits(codec, WM8962_DC_SERVO_1,
+ snd_soc_component_update_bits(component, WM8962_DC_SERVO_1,
WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
WM8962_HP1L_DCS_STARTUP |
WM8962_HP1R_DCS_STARTUP,
0);
- snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0,
WM8962_HP1L_ENA | WM8962_HP1R_ENA |
WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY |
WM8962_HP1L_ENA_OUTP |
@@ -1957,7 +1997,7 @@ static int hp_event(struct snd_soc_dapm_widget *w,
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
int reg;
switch (w->shift) {
@@ -1980,7 +2020,8 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- return snd_soc_write(codec, reg, snd_soc_read(codec, reg));
+ return snd_soc_component_write(component, reg,
+ snd_soc_component_read(component, reg));
default:
WARN(1, "Invalid event %d\n", event);
return -EINVAL;
@@ -1990,18 +2031,18 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (wm8962->dsp2_ena)
- wm8962_dsp2_start(codec);
+ wm8962_dsp2_start(component);
break;
case SND_SOC_DAPM_PRE_PMD:
if (wm8962->dsp2_ena)
- wm8962_dsp2_stop(codec);
+ wm8962_dsp2_stop(component);
break;
default:
@@ -2052,6 +2093,13 @@ static SOC_ENUM_SINGLE_DECL(hpoutl_enum,
static const struct snd_kcontrol_new hpoutl_mux =
SOC_DAPM_ENUM("HPOUTL Mux", hpoutl_enum);
+static const char * const input_mode_text[] = { "Analog", "Digital" };
+
+static SOC_ENUM_SINGLE_VIRT_DECL(input_mode_enum, input_mode_text);
+
+static const struct snd_kcontrol_new input_mode_mux =
+ SOC_DAPM_ENUM("Input Mode", input_mode_enum);
+
static const struct snd_kcontrol_new inpgal[] = {
SOC_DAPM_SINGLE("IN1L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 3, 1, 0),
SOC_DAPM_SINGLE("IN2L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 2, 1, 0),
@@ -2136,8 +2184,10 @@ 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_SUPPLY("TEMP_HP", SND_SOC_NOPM, 0, 0, tp_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TEMP_SPK", SND_SOC_NOPM, 0, 0, tp_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
inpgal, ARRAY_SIZE(inpgal)),
@@ -2150,6 +2200,9 @@ SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
SND_SOC_DAPM_AIF_IN("DMIC_ENA", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+SND_SOC_DAPM_MUX("Input Mode L", SND_SOC_NOPM, 0, 0, &input_mode_mux),
+SND_SOC_DAPM_MUX("Input Mode R", SND_SOC_NOPM, 0, 0, &input_mode_mux),
+
SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
@@ -2177,6 +2230,9 @@ SND_SOC_DAPM_PGA_E("HPOUT", SND_SOC_NOPM, 0, 0, NULL, 0, hp_event,
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
+
+SND_SOC_DAPM_PGA("SPKOUTL Output", WM8962_CLASS_D_CONTROL_1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKOUTR Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0),
};
static const struct snd_soc_dapm_widget wm8962_dapm_spk_mono_widgets[] = {
@@ -2184,7 +2240,6 @@ 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"),
};
@@ -2199,9 +2254,6 @@ SND_SOC_DAPM_MUX_E("SPKOUTL PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux,
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"),
};
@@ -2229,16 +2281,19 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "DMIC_ENA", NULL, "DMICDAT" },
+ { "Input Mode L", "Analog", "MIXINL" },
+ { "Input Mode L", "Digital", "DMIC_ENA" },
+ { "Input Mode R", "Analog", "MIXINR" },
+ { "Input Mode R", "Digital", "DMIC_ENA" },
+
{ "ADCL", NULL, "SYSCLK" },
{ "ADCL", NULL, "TOCLK" },
- { "ADCL", NULL, "MIXINL" },
- { "ADCL", NULL, "DMIC_ENA" },
+ { "ADCL", NULL, "Input Mode L" },
{ "ADCL", NULL, "DSP2" },
{ "ADCR", NULL, "SYSCLK" },
{ "ADCR", NULL, "TOCLK" },
- { "ADCR", NULL, "MIXINR" },
- { "ADCR", NULL, "DMIC_ENA" },
+ { "ADCR", NULL, "Input Mode R" },
{ "ADCR", NULL, "DSP2" },
{ "STL", "Left", "ADCL" },
@@ -2311,12 +2366,18 @@ static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = {
{ "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" },
+ { "SPKOUTL Output", NULL, "Speaker PGA" },
+ { "SPKOUTL Output", NULL, "SYSCLK" },
+ { "SPKOUTL Output", NULL, "TOCLK" },
+ { "SPKOUTL Output", NULL, "TEMP_SPK" },
+
+ { "SPKOUTR Output", NULL, "Speaker PGA" },
+ { "SPKOUTR Output", NULL, "SYSCLK" },
+ { "SPKOUTR Output", NULL, "TOCLK" },
+ { "SPKOUTR Output", NULL, "TEMP_SPK" },
- { "SPKOUT", NULL, "Speaker Output" },
+ { "SPKOUT", NULL, "SPKOUTL Output" },
+ { "SPKOUT", NULL, "SPKOUTR Output" },
};
static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
@@ -2354,19 +2415,19 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
{ "SPKOUTR", NULL, "SPKOUTR Output" },
};
-static int wm8962_add_widgets(struct snd_soc_codec *codec)
+static int wm8962_add_widgets(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
struct wm8962_pdata *pdata = &wm8962->pdata;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- snd_soc_add_codec_controls(codec, wm8962_snd_controls,
+ snd_soc_add_component_controls(component, wm8962_snd_controls,
ARRAY_SIZE(wm8962_snd_controls));
if (pdata->spk_mono)
- snd_soc_add_codec_controls(codec, wm8962_spk_mono_controls,
+ snd_soc_add_component_controls(component, wm8962_spk_mono_controls,
ARRAY_SIZE(wm8962_spk_mono_controls));
else
- snd_soc_add_codec_controls(codec, wm8962_spk_stereo_controls,
+ snd_soc_add_component_controls(component, wm8962_spk_stereo_controls,
ARRAY_SIZE(wm8962_spk_stereo_controls));
@@ -2403,21 +2464,23 @@ 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)
+static void wm8962_configure_bclk(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
+ int best, min_diff, diff;
int dspclk, i;
int clocking2 = 0;
int clocking4 = 0;
int aif2 = 0;
if (!wm8962->sysclk_rate) {
- dev_dbg(codec->dev, "No SYSCLK configured\n");
+ dev_dbg(component->dev, "No SYSCLK configured\n");
return;
}
if (!wm8962->bclk || !wm8962->lrclk) {
- dev_dbg(codec->dev, "No audio clocks configured\n");
+ dev_dbg(component->dev, "No audio clocks configured\n");
return;
}
@@ -2429,32 +2492,40 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
}
if (i == ARRAY_SIZE(sysclk_rates)) {
- dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
+ dev_err(component->dev, "Unsupported sysclk ratio %d\n",
wm8962->sysclk_rate / wm8962->lrclk);
return;
}
- dev_dbg(codec->dev, "Selected sysclk ratio %d\n", sysclk_rates[i]);
+ dev_dbg(component->dev, "Selected sysclk ratio %d\n", sysclk_rates[i]);
- snd_soc_update_bits(codec, WM8962_CLOCKING_4,
+ snd_soc_component_update_bits(component, WM8962_CLOCKING_4,
WM8962_SYSCLK_RATE_MASK, clocking4);
/* DSPCLK_DIV can be only generated correctly after enabling SYSCLK.
* So we here provisionally enable it and then disable it afterward
* if current bias_level hasn't reached SND_SOC_BIAS_ON.
*/
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON)
- snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
+ snd_soc_component_update_bits(component, WM8962_CLOCKING2,
WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
- dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
+ /* DSPCLK_DIV field in WM8962_CLOCKING1 register is used to generate
+ * correct frequency of LRCLK and BCLK. Sometimes the read-only value
+ * can't be updated timely after enabling SYSCLK. This results in wrong
+ * calculation values. Delay is introduced here to wait for newest
+ * value from register. The time of the delay should be at least
+ * 500~1000us according to test.
+ */
+ usleep_range(500, 1000);
+ dspclk = snd_soc_component_read(component, WM8962_CLOCKING1);
- if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON)
- snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
+ snd_soc_component_update_bits(component, WM8962_CLOCKING2,
WM8962_SYSCLK_ENA_MASK, 0);
if (dspclk < 0) {
- dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
+ dev_err(component->dev, "Failed to read DSPCLK: %d\n", dspclk);
return;
}
@@ -2470,61 +2541,65 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
dspclk = wm8962->sysclk_rate / 4;
break;
default:
- dev_warn(codec->dev, "Unknown DSPCLK divisor read back\n");
- dspclk = wm8962->sysclk;
+ dev_warn(component->dev, "Unknown DSPCLK divisor read back\n");
+ dspclk = wm8962->sysclk_rate;
}
- dev_dbg(codec->dev, "DSPCLK is %dHz, BCLK %d\n", dspclk, wm8962->bclk);
+ dev_dbg(component->dev, "DSPCLK is %dHz, BCLK %d\n", dspclk, wm8962->bclk);
- /* We're expecting an exact match */
+ /* Search a proper bclk, not exact match. */
+ best = 0;
+ min_diff = INT_MAX;
for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
if (bclk_divs[i] < 0)
continue;
- 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;
+ diff = (dspclk / bclk_divs[i]) - wm8962->bclk;
+ if (diff < 0) /* Table is sorted */
break;
+ if (diff < min_diff) {
+ best = i;
+ min_diff = diff;
}
}
- if (i == ARRAY_SIZE(bclk_divs)) {
- dev_err(codec->dev, "Unsupported BCLK ratio %d\n",
- dspclk / wm8962->bclk);
- return;
- }
+ wm8962->bclk = dspclk / bclk_divs[best];
+ clocking2 |= best;
+ dev_dbg(component->dev, "Selected BCLK_DIV %d for %dHz\n",
+ bclk_divs[best], wm8962->bclk);
aif2 |= wm8962->bclk / wm8962->lrclk;
- dev_dbg(codec->dev, "Selected LRCLK divisor %d for %dHz\n",
+ dev_dbg(component->dev, "Selected LRCLK divisor %d for %dHz\n",
wm8962->bclk / wm8962->lrclk, wm8962->lrclk);
- snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ snd_soc_component_update_bits(component, WM8962_CLOCKING2,
WM8962_BCLK_DIV_MASK, clocking2);
- snd_soc_update_bits(codec, WM8962_AUDIO_INTERFACE_2,
+ snd_soc_component_update_bits(component, WM8962_AUDIO_INTERFACE_2,
WM8962_AIF_RATE_MASK, aif2);
}
-static int wm8962_set_bias_level(struct snd_soc_codec *codec,
+static int wm8962_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
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,
+ snd_soc_component_update_bits(component, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x80);
- wm8962_configure_bclk(codec);
+ wm8962_configure_bclk(component);
break;
case SND_SOC_BIAS_STANDBY:
/* VMID 2*250k */
- snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
+ snd_soc_component_update_bits(component, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x100);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF)
msleep(100);
break;
@@ -2556,8 +2631,9 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int i;
int aif0 = 0;
int adctl3 = 0;
@@ -2575,7 +2651,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
}
}
if (i == ARRAY_SIZE(sr_vals)) {
- dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
+ dev_err(component->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
return -EINVAL;
}
@@ -2598,17 +2674,17 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8962_AUDIO_INTERFACE_0,
+ snd_soc_component_update_bits(component, WM8962_AUDIO_INTERFACE_0,
WM8962_WL_MASK, aif0);
- snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
+ snd_soc_component_update_bits(component, 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",
+ dev_dbg(component->dev, "hw_params set BCLK %dHz LRCLK %dHz\n",
wm8962->bclk, wm8962->lrclk);
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
- wm8962_configure_bclk(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_ON)
+ wm8962_configure_bclk(component);
return 0;
}
@@ -2616,8 +2692,8 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int src;
switch (clk_id) {
@@ -2633,7 +2709,7 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_SRC_MASK,
+ snd_soc_component_update_bits(component, WM8962_CLOCKING2, WM8962_SYSCLK_SRC_MASK,
src);
wm8962->sysclk_rate = freq;
@@ -2643,12 +2719,14 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int aif0 = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif0 |= WM8962_LRCLK_INV | 3;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif0 |= 3;
@@ -2689,17 +2767,19 @@ static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
+ wm8962->master_flag = false;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif0 |= WM8962_MSTR;
+ wm8962->master_flag = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8962_AUDIO_INTERFACE_0,
+ snd_soc_component_update_bits(component, WM8962_AUDIO_INTERFACE_0,
WM8962_FMT_MASK | WM8962_BCLK_INV | WM8962_MSTR |
WM8962_LRCLK_INV, aif0);
@@ -2791,7 +2871,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
if (target % Fref == 0) {
fll_div->theta = 0;
- fll_div->lambda = 0;
+ fll_div->lambda = 1;
} else {
gcd_fll = gcd(target, fratio * Fref);
@@ -2809,12 +2889,12 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
struct _fll_div fll_div;
- unsigned long timeout;
+ unsigned long time_left;
int ret;
int fll1 = 0;
@@ -2824,15 +2904,15 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
return 0;
if (Fout == 0) {
- dev_dbg(codec->dev, "FLL disabled\n");
+ dev_dbg(component->dev, "FLL disabled\n");
wm8962->fll_fref = 0;
wm8962->fll_fout = 0;
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1,
WM8962_FLL_ENA, 0);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
return 0;
}
@@ -2842,74 +2922,78 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
return ret;
/* Parameters good, disable so we can reprogram */
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0);
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0);
switch (fll_id) {
case WM8962_FLL_MCLK:
case WM8962_FLL_BCLK:
+ fll1 |= (fll_id - 1) << WM8962_FLL_REFCLK_SRC_SHIFT;
+ break;
case WM8962_FLL_OSC:
fll1 |= (fll_id - 1) << WM8962_FLL_REFCLK_SRC_SHIFT;
+ snd_soc_component_update_bits(component, WM8962_PLL2,
+ WM8962_OSC_ENA, WM8962_OSC_ENA);
break;
case WM8962_FLL_INT:
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1,
WM8962_FLL_OSC_ENA, WM8962_FLL_OSC_ENA);
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_5,
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_5,
WM8962_FLL_FRC_NCO, WM8962_FLL_FRC_NCO);
break;
default:
- dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ dev_err(component->dev, "Unknown FLL source %d\n", source);
return -EINVAL;
}
- if (fll_div.theta || fll_div.lambda)
+ if (fll_div.theta)
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_component_update_bits(component, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0);
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_2,
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_2,
WM8962_FLL_OUTDIV_MASK |
WM8962_FLL_REFCLK_DIV_MASK,
(fll_div.fll_outdiv << WM8962_FLL_OUTDIV_SHIFT) |
(fll_div.fll_refclk_div));
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_3,
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_write(component, WM8962_FLL_CONTROL_6, fll_div.theta);
+ snd_soc_component_write(component, WM8962_FLL_CONTROL_7, fll_div.lambda);
+ snd_soc_component_write(component, WM8962_FLL_CONTROL_8, fll_div.n);
reinit_completion(&wm8962->fll_lock);
- ret = pm_runtime_get_sync(codec->dev);
+ ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
- dev_err(codec->dev, "Failed to resume device: %d\n", ret);
+ dev_err(component->dev, "Failed to resume device: %d\n", ret);
return ret;
}
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1,
WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK |
WM8962_FLL_ENA, fll1 | WM8962_FLL_ENA);
- dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+ dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
/* This should be a massive overestimate but go even
* higher if we'll error out
*/
if (wm8962->irq)
- timeout = msecs_to_jiffies(5);
+ time_left = msecs_to_jiffies(5);
else
- timeout = msecs_to_jiffies(1);
+ time_left = msecs_to_jiffies(1);
- timeout = wait_for_completion_timeout(&wm8962->fll_lock,
- timeout);
+ time_left = wait_for_completion_timeout(&wm8962->fll_lock,
+ time_left);
- if (timeout == 0 && wm8962->irq) {
- dev_err(codec->dev, "FLL lock timed out");
- snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ if (time_left == 0 && wm8962->irq) {
+ dev_err(component->dev, "FLL lock timed out");
+ snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1,
WM8962_FLL_ENA, 0);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
return -ETIMEDOUT;
}
@@ -2920,9 +3004,9 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
return 0;
}
-static int wm8962_mute(struct snd_soc_dai *dai, int mute)
+static int wm8962_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int val, ret;
if (mute)
@@ -2934,12 +3018,12 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute)
* 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,
+ ret = snd_soc_component_update_bits(component, WM8962_CLASS_D_CONTROL_1,
WM8962_DAC_MUTE_ALT, val);
if (ret < 0)
return ret;
- return snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+ return snd_soc_component_update_bits(component, WM8962_ADC_DAC_CONTROL_1,
WM8962_DAC_MUTE, val);
}
@@ -2953,7 +3037,8 @@ 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,
+ .mute_stream = wm8962_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8962_dai = {
@@ -2973,7 +3058,7 @@ static struct snd_soc_dai_driver wm8962_dai = {
.formats = WM8962_FORMATS,
},
.ops = &wm8962_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static void wm8962_mic_work(struct work_struct *work)
@@ -2981,12 +3066,12 @@ 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;
+ struct snd_soc_component *component = wm8962->component;
int status = 0;
int irq_pol = 0;
int reg;
- reg = snd_soc_read(codec, WM8962_ADDITIONAL_CONTROL_4);
+ reg = snd_soc_component_read(component, WM8962_ADDITIONAL_CONTROL_4);
if (reg & WM8962_MICDET_STS) {
status |= SND_JACK_MICROPHONE;
@@ -3001,7 +3086,7 @@ static void wm8962_mic_work(struct work_struct *work)
snd_soc_jack_report(wm8962->jack, status,
SND_JACK_MICROPHONE | SND_JACK_BTN_0);
- snd_soc_update_bits(codec, WM8962_MICINT_SOURCE_POL,
+ snd_soc_component_update_bits(component, WM8962_MICINT_SOURCE_POL,
WM8962_MICSCD_IRQ_POL |
WM8962_MICD_IRQ_POL, irq_pol);
}
@@ -3014,7 +3099,7 @@ static irqreturn_t wm8962_irq(int irq, void *data)
unsigned int active;
int reg, ret;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err(dev, "Failed to resume: %d\n", ret);
return IRQ_NONE;
@@ -3099,7 +3184,7 @@ static irqreturn_t wm8962_irq(int irq, void *data)
/**
* wm8962_mic_detect - Enable microphone detection via the WM8962 IRQ
*
- * @codec: WM8962 codec
+ * @component: WM8962 component
* @jack: jack to report detection events on
*
* Enable microphone detection via IRQ on the WM8962. If GPIOs are
@@ -3109,10 +3194,10 @@ static irqreturn_t wm8962_irq(int irq, void *data)
*
* If no jack is supplied detection will be disabled.
*/
-int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+int wm8962_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int irq_mask, enable;
wm8962->jack = jack;
@@ -3124,9 +3209,9 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
enable = 0;
}
- snd_soc_update_bits(codec, WM8962_INTERRUPT_STATUS_2_MASK,
+ snd_soc_component_update_bits(component, WM8962_INTERRUPT_STATUS_2_MASK,
WM8962_MICD_EINT | WM8962_MICSCD_EINT, irq_mask);
- snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
+ snd_soc_component_update_bits(component, WM8962_ADDITIONAL_CONTROL_4,
WM8962_MICDET_ENA, enable);
/* Send an initial empty report */
@@ -3157,8 +3242,8 @@ 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 = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = wm8962->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int i;
int reg = 0;
int best = 0;
@@ -3170,18 +3255,18 @@ static void wm8962_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n",
beep_rates[best], 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");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, WM8962_BEEP_GENERATOR_1,
+ snd_soc_component_update_bits(component, WM8962_BEEP_GENERATOR_1,
WM8962_BEEP_ENA | WM8962_BEEP_RATE_MASK, reg);
snd_soc_dapm_sync(dapm);
@@ -3193,15 +3278,16 @@ static void wm8962_beep_work(struct work_struct *work)
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);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ dev_dbg(component->dev, "Beep event %x %x\n", code, hz);
switch (code) {
case SND_BELL:
if (hz)
hz = 1000;
+ fallthrough;
case SND_TONE:
break;
default:
@@ -3214,9 +3300,8 @@ static int wm8962_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t wm8962_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
long int time;
@@ -3231,16 +3316,16 @@ static ssize_t wm8962_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, wm8962_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void wm8962_init_beep(struct snd_soc_codec *codec)
+static void wm8962_init_beep(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int ret;
- wm8962->beep = devm_input_allocate_device(codec->dev);
+ wm8962->beep = devm_input_allocate_device(component->dev);
if (!wm8962->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -3248,37 +3333,37 @@ static void wm8962_init_beep(struct snd_soc_codec *codec)
wm8962->beep_rate = 0;
wm8962->beep->name = "WM8962 Beep Generator";
- wm8962->beep->phys = dev_name(codec->dev);
+ wm8962->beep->phys = dev_name(component->dev);
wm8962->beep->id.bustype = BUS_I2C;
wm8962->beep->evbit[0] = BIT_MASK(EV_SND);
wm8962->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
wm8962->beep->event = wm8962_beep_event;
- wm8962->beep->dev.parent = codec->dev;
- input_set_drvdata(wm8962->beep, codec);
+ wm8962->beep->dev.parent = component->dev;
+ input_set_drvdata(wm8962->beep, component);
ret = input_register_device(wm8962->beep);
if (ret != 0) {
wm8962->beep = NULL;
- dev_err(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void wm8962_free_beep(struct snd_soc_codec *codec)
+static void wm8962_free_beep(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&wm8962->beep_work);
wm8962->beep = NULL;
- snd_soc_update_bits(codec, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA,0);
+ snd_soc_component_update_bits(component, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA,0);
}
static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio)
@@ -3307,14 +3392,9 @@ static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio)
}
#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);
+ struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
/* The WM8962 GPIOs aren't linearly numbered. For simplicity
* we export linear numbers and error out if the unsupported
@@ -3335,26 +3415,29 @@ static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
return 0;
}
-static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int wm8962_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
- struct snd_soc_codec *codec = wm8962->codec;
+ struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
+ struct snd_soc_component *component = wm8962->component;
- snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
- WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT);
+ return snd_soc_component_update_bits(component,
+ WM8962_GPIO_BASE + offset,
+ WM8962_GP2_LVL,
+ !!value << WM8962_GP2_LVL_SHIFT);
}
static int wm8962_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
- struct snd_soc_codec *codec = wm8962->codec;
+ struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
+ struct snd_soc_component *component = wm8962->component;
int ret, val;
/* Force function 1 (logic output) */
val = (1 << WM8962_GP2_FN_SHIFT) | (value << WM8962_GP2_LVL_SHIFT);
- ret = snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
+ ret = snd_soc_component_update_bits(component, WM8962_GPIO_BASE + offset,
WM8962_GP2_FN_MASK | WM8962_GP2_LVL, val);
if (ret < 0)
return ret;
@@ -3362,7 +3445,7 @@ static int wm8962_gpio_direction_out(struct gpio_chip *chip,
return 0;
}
-static struct gpio_chip wm8962_template_chip = {
+static const struct gpio_chip wm8962_template_chip = {
.label = "wm8962",
.owner = THIS_MODULE,
.request = wm8962_gpio_request,
@@ -3371,51 +3454,51 @@ static struct gpio_chip wm8962_template_chip = {
.can_sleep = 1,
};
-static void wm8962_init_gpio(struct snd_soc_codec *codec)
+static void wm8962_init_gpio(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
struct wm8962_pdata *pdata = &wm8962->pdata;
int ret;
wm8962->gpio_chip = wm8962_template_chip;
wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
- wm8962->gpio_chip.parent = codec->dev;
+ wm8962->gpio_chip.parent = component->dev;
if (pdata->gpio_base)
wm8962->gpio_chip.base = pdata->gpio_base;
else
wm8962->gpio_chip.base = -1;
- ret = gpiochip_add(&wm8962->gpio_chip);
+ ret = gpiochip_add_data(&wm8962->gpio_chip, wm8962);
if (ret != 0)
- dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+ dev_err(component->dev, "Failed to add GPIOs: %d\n", ret);
}
-static void wm8962_free_gpio(struct snd_soc_codec *codec)
+static void wm8962_free_gpio(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
gpiochip_remove(&wm8962->gpio_chip);
}
#else
-static void wm8962_init_gpio(struct snd_soc_codec *codec)
+static void wm8962_init_gpio(struct snd_soc_component *component)
{
}
-static void wm8962_free_gpio(struct snd_soc_codec *codec)
+static void wm8962_free_gpio(struct snd_soc_component *component)
{
}
#endif
-static int wm8962_probe(struct snd_soc_codec *codec)
+static int wm8962_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int i;
bool dmicclk, dmicdat;
- wm8962->codec = codec;
+ wm8962->component = component;
wm8962->disable_nb[0].notifier_call = wm8962_regulator_event_0;
wm8962->disable_nb[1].notifier_call = wm8962_regulator_event_1;
@@ -3428,22 +3511,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
/* 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]);
+ ret = devm_regulator_register_notifier(
+ wm8962->supplies[i].consumer,
+ &wm8962->disable_nb[i]);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to register regulator notifier: %d\n",
ret);
}
}
- wm8962_add_widgets(codec);
+ wm8962_add_widgets(component);
/* 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)
+ for (i = 1; i < WM8962_MAX_GPIO; i++) {
+ /*
+ * Register 515 (WM8962_GPIO_BASE + 3) does not exist,
+ * so skip its access
+ */
+ if (i == 3)
+ continue;
+ switch (snd_soc_component_read(component, WM8962_GPIO_BASE + i)
& WM8962_GP2_FN_MASK) {
case WM8962_GPIO_FN_DMICCLK:
dmicclk = true;
@@ -3456,40 +3546,35 @@ static int wm8962_probe(struct snd_soc_codec *codec)
}
}
if (!dmicclk || !dmicdat) {
- dev_dbg(codec->dev, "DMIC not in use, disabling\n");
- snd_soc_dapm_nc_pin(dapm, "DMICDAT");
+ dev_dbg(component->dev, "DMIC not in use, disabling\n");
+ snd_soc_dapm_disable_pin(dapm, "DMICDAT");
}
if (dmicclk != dmicdat)
- dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
+ dev_warn(component->dev, "DMIC GPIOs partially configured\n");
- wm8962_init_beep(codec);
- wm8962_init_gpio(codec);
+ wm8962_init_beep(component);
+ wm8962_init_gpio(component);
return 0;
}
-static int wm8962_remove(struct snd_soc_codec *codec)
+static void wm8962_remove(struct snd_soc_component *component)
{
- struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- int i;
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
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;
+ wm8962_free_gpio(component);
+ wm8962_free_beep(component);
}
-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,
+static const struct snd_soc_component_driver soc_component_dev_wm8962 = {
+ .probe = wm8962_probe,
+ .remove = wm8962_remove,
+ .set_bias_level = wm8962_set_bias_level,
+ .set_pll = wm8962_set_fll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
/* Improve power consumption for IN4 DC measurement mode */
@@ -3508,7 +3593,7 @@ static const struct regmap_config wm8962_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8962_reg),
.volatile_reg = wm8962_volatile_register,
.readable_reg = wm8962_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
@@ -3536,13 +3621,11 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
pdata->gpio_init[i] = 0x0;
}
- pdata->mclk = devm_clk_get(&i2c->dev, NULL);
-
- return 0;
+ pdata->mclk = devm_clk_get_optional(&i2c->dev, NULL);
+ return PTR_ERR_OR_ZERO(pdata->mclk);
}
-static int wm8962_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8962_i2c_probe(struct i2c_client *i2c)
{
struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm8962_priv *wm8962;
@@ -3570,14 +3653,6 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
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];
@@ -3718,7 +3793,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
ARRAY_SIZE(wm8962_dc_measure));
if (ret != 0)
dev_err(&i2c->dev,
- "Failed to configure for DC mesurement: %d\n",
+ "Failed to configure for DC measurement: %d\n",
ret);
}
@@ -3756,11 +3831,16 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8962, &wm8962_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8962, &wm8962_dai, 1);
if (ret < 0)
goto err_pm_runtime;
+ regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4,
+ WM8962_TEMP_ENA_HP_MASK, 0);
+ regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4,
+ WM8962_TEMP_ENA_SPK_MASK, 0);
+
regcache_cache_only(wm8962->regmap, true);
/* The drivers should power up as needed */
@@ -3776,14 +3856,11 @@ err:
return ret;
}
-static int wm8962_i2c_remove(struct i2c_client *client)
+static void wm8962_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
pm_runtime_disable(&client->dev);
- return 0;
}
-#ifdef CONFIG_PM
static int wm8962_runtime_resume(struct device *dev)
{
struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
@@ -3798,9 +3875,8 @@ static int wm8962_runtime_resume(struct device *dev)
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;
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ goto disable_clock;
}
regcache_cache_only(wm8962->regmap, false);
@@ -3812,8 +3888,8 @@ static int wm8962_runtime_resume(struct device *dev)
/* 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);
+ regmap_write_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA, 0);
/* Ensure we have soft control over all registers */
regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
@@ -3835,15 +3911,26 @@ static int wm8962_runtime_resume(struct device *dev)
WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK,
WM8962_BIAS_ENA | 0x180);
+ if (wm8962->master_flag)
+ regmap_update_bits(wm8962->regmap, WM8962_AUDIO_INTERFACE_0,
+ WM8962_MSTR, WM8962_MSTR);
msleep(5);
return 0;
+
+disable_clock:
+ clk_disable_unprepare(wm8962->pdata.mclk);
+ return ret;
}
static int wm8962_runtime_suspend(struct device *dev)
{
struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
+ if (wm8962->master_flag)
+ regmap_update_bits(wm8962->regmap, WM8962_AUDIO_INTERFACE_0,
+ WM8962_MSTR, 0);
+
regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, 0);
@@ -3860,14 +3947,14 @@ static int wm8962_runtime_suspend(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops wm8962_pm = {
- SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL)
};
static const struct i2c_device_id wm8962_i2c_id[] = {
- { "wm8962", 0 },
+ { "wm8962" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id);
@@ -3882,7 +3969,7 @@ static struct i2c_driver wm8962_i2c_driver = {
.driver = {
.name = "wm8962",
.of_match_table = wm8962_of_match,
- .pm = &wm8962_pm,
+ .pm = pm_ptr(&wm8962_pm),
},
.probe = wm8962_i2c_probe,
.remove = wm8962_i2c_remove,
diff --git a/sound/soc/codecs/wm8962.h b/sound/soc/codecs/wm8962.h
index 910aafd09d21..e7f4a70ab8a4 100644
--- a/sound/soc/codecs/wm8962.h
+++ b/sound/soc/codecs/wm8962.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -16,9 +13,9 @@
#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_SYSCLK_MCLK 0
+#define WM8962_SYSCLK_FLL 1
+#define WM8962_SYSCLK_PLL3 2
#define WM8962_FLL 1
@@ -3779,6 +3776,6 @@
#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);
+int wm8962_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack);
#endif
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 2cdde32c43c6..46aa556b44fa 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* wm8971.c -- WM8971 ALSA SoC Audio driver
*
@@ -6,11 +7,6 @@
* 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>
@@ -89,7 +85,7 @@ static const struct reg_default wm8971_reg_defaults[] = {
{ 42, 0x0079 },
};
-#define wm8971_reset(c) snd_soc_write(c, WM8971_RESET, 0)
+#define wm8971_reset(c) snd_soc_component_write(c, WM8971_RESET, 0)
/* WM8971 Controls */
static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
@@ -433,8 +429,8 @@ static int get_coeff(int mclk, int rate)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 11289600:
@@ -451,15 +447,15 @@ static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -502,7 +498,7 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8971_IFACE, iface);
+ snd_soc_component_write(component, WM8971_IFACE, iface);
return 0;
}
@@ -510,10 +506,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8971_IFACE) & 0x1f3;
+ u16 srate = snd_soc_component_read(component, WM8971_SRATE) & 0x1c0;
int coeff = get_coeff(wm8971->sysclk, params_rate(params));
/* bit size */
@@ -532,23 +528,23 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, WM8971_IFACE, iface);
+ snd_soc_component_write(component, WM8971_IFACE, iface);
if (coeff >= 0)
- snd_soc_write(codec, WM8971_SRATE, srate |
+ snd_soc_component_write(component, WM8971_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
}
-static int wm8971_mute(struct snd_soc_dai *dai, int mute)
+static int wm8971_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8971_ADCDAC) & 0xfff7;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8971_ADCDAC) & 0xfff7;
if (mute)
- snd_soc_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+ snd_soc_component_write(component, WM8971_ADCDAC, mute_reg | 0x8);
else
- snd_soc_write(codec, WM8971_ADCDAC, mute_reg);
+ snd_soc_component_write(component, WM8971_ADCDAC, mute_reg);
return 0;
}
@@ -561,37 +557,38 @@ static void wm8971_charge_work(struct work_struct *work)
regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100);
}
-static int wm8971_set_bias_level(struct snd_soc_codec *codec,
+static int wm8971_set_bias_level(struct snd_soc_component *component,
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;
+ struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 pwr_reg = snd_soc_component_read(component, WM8971_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+ snd_soc_component_write(component, WM8971_PWR1, pwr_reg | 0x00c1);
break;
case SND_SOC_BIAS_PREPARE:
/* Wait until fully charged */
flush_delayed_work(&wm8971->charge_work);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_cache_sync(component);
/* charge output caps - set vmid to 5k for quick power up */
- snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
+ snd_soc_component_write(component, WM8971_PWR1, pwr_reg | 0x01c0);
queue_delayed_work(system_power_efficient_wq,
&wm8971->charge_work, msecs_to_jiffies(1000));
} else {
/* mute dac and set vmid to 500k, enable VREF */
- snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+ snd_soc_component_write(component, WM8971_PWR1, pwr_reg | 0x0140);
}
break;
case SND_SOC_BIAS_OFF:
cancel_delayed_work_sync(&wm8971->charge_work);
- snd_soc_write(codec, WM8971_PWR1, 0x0001);
+ snd_soc_component_write(component, WM8971_PWR1, 0x0001);
break;
}
return 0;
@@ -606,9 +603,10 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8971_dai_ops = {
.hw_params = wm8971_pcm_hw_params,
- .digital_mute = wm8971_mute,
+ .mute_stream = wm8971_mute,
.set_fmt = wm8971_set_dai_fmt,
.set_sysclk = wm8971_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8971_dai = {
@@ -628,38 +626,40 @@ static struct snd_soc_dai_driver wm8971_dai = {
.ops = &wm8971_dai_ops,
};
-static int wm8971_probe(struct snd_soc_codec *codec)
+static int wm8971_probe(struct snd_soc_component *component)
{
- struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
+ struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component);
INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work);
- wm8971_reset(codec);
+ wm8971_reset(component);
/* 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);
+ snd_soc_component_update_bits(component, WM8971_LDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_RDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_LOUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_ROUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_LOUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_ROUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_LINVOL, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8971_RINVOL, 0x0100, 0x0100);
return 0;
}
-static 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 snd_soc_component_driver soc_component_dev_wm8971 = {
+ .probe = wm8971_probe,
+ .set_bias_level = wm8971_set_bias_level,
+ .controls = wm8971_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8971_snd_controls),
+ .dapm_widgets = wm8971_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8971_dapm_widgets),
+ .dapm_routes = wm8971_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8971_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8971_regmap = {
@@ -669,14 +669,12 @@ static const struct regmap_config wm8971_regmap = {
.reg_defaults = wm8971_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8971_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int wm8971_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8971_i2c_probe(struct i2c_client *i2c)
{
struct wm8971_priv *wm8971;
- int ret;
wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv),
GFP_KERNEL);
@@ -689,20 +687,12 @@ static int wm8971_i2c_probe(struct i2c_client *i2c,
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;
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8971, &wm8971_dai, 1);
}
static const struct i2c_device_id wm8971_i2c_id[] = {
- { "wm8971", 0 },
+ { "wm8971" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
@@ -711,8 +701,7 @@ static struct i2c_driver wm8971_i2c_driver = {
.driver = {
.name = "wm8971",
},
- .probe = wm8971_i2c_probe,
- .remove = wm8971_i2c_remove,
+ .probe = wm8971_i2c_probe,
.id_table = wm8971_i2c_id,
};
diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h
index f31c38fddfc4..46fa31978681 100644
--- a/sound/soc/codecs/wm8971.h
+++ b/sound/soc/codecs/wm8971.h
@@ -1,15 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index c284c7b6db8b..0bb5e947f46d 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -1,17 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -28,6 +24,11 @@
#include "wm8974.h"
+struct wm8974_priv {
+ unsigned int mclk;
+ unsigned int fs;
+};
+
static const struct reg_default wm8974_reg_defaults[] = {
{ 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 },
{ 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 },
@@ -49,7 +50,7 @@ static const struct reg_default wm8974_reg_defaults[] = {
#define WM8974_POWER1_BIASEN 0x08
#define WM8974_POWER1_BUFIOEN 0x04
-#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
+#define wm8974_reset(c) snd_soc_component_write(c, WM8974_RESET, 0)
static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
@@ -185,7 +186,7 @@ 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),
+SOC_DAPM_SINGLE("PGA Switch", WM8974_INPPGA, 6, 1, 1),
};
/* Input PGA */
@@ -195,14 +196,6 @@ 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],
@@ -253,8 +246,8 @@ static const struct snd_soc_dapm_route wm8974_dapm_routes[] = {
/* Boost Mixer */
{"ADC", NULL, "Boost Mixer"},
- {"Boost Mixer", "Aux Switch", "Aux Input"},
- {"Boost Mixer", NULL, "Input PGA"},
+ {"Boost Mixer", NULL, "Aux Input"},
+ {"Boost Mixer", "PGA Switch", "Input PGA"},
{"Boost Mixer", NULL, "MICP"},
/* Input PGA */
@@ -319,33 +312,33 @@ static void pll_factors(struct pll_ *pll_div,
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 snd_soc_component *component = codec_dai->component;
struct pll_ pll_div;
u16 reg;
if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
- reg = snd_soc_read(codec, WM8974_CLOCK);
- snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
+ reg = snd_soc_component_read(component, WM8974_CLOCK);
+ snd_soc_component_write(component, WM8974_CLOCK, reg & 0x0ff);
/* Turn off PLL */
- reg = snd_soc_read(codec, WM8974_POWER1);
- snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
+ reg = snd_soc_component_read(component, WM8974_POWER1);
+ snd_soc_component_write(component, WM8974_POWER1, reg & 0x1df);
return 0;
}
pll_factors(&pll_div, freq_out, freq_in);
- snd_soc_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);
+ snd_soc_component_write(component, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_component_write(component, WM8974_PLLK1, pll_div.k >> 18);
+ snd_soc_component_write(component, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8974_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_component_read(component, WM8974_POWER1);
+ snd_soc_component_write(component, WM8974_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
- reg = snd_soc_read(codec, WM8974_CLOCK);
- snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
+ reg = snd_soc_component_read(component, WM8974_CLOCK);
+ snd_soc_component_write(component, WM8974_CLOCK, reg | 0x100);
return 0;
}
@@ -356,21 +349,21 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
switch (div_id) {
case WM8974_OPCLKDIV:
- reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
- snd_soc_write(codec, WM8974_GPIO, reg | div);
+ reg = snd_soc_component_read(component, WM8974_GPIO) & 0x1cf;
+ snd_soc_component_write(component, WM8974_GPIO, reg | div);
break;
case WM8974_MCLKDIV:
- reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
- snd_soc_write(codec, WM8974_CLOCK, reg | div);
+ reg = snd_soc_component_read(component, WM8974_CLOCK) & 0x11f;
+ snd_soc_component_write(component, WM8974_CLOCK, reg | div);
break;
case WM8974_BCLKDIV:
- reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
- snd_soc_write(codec, WM8974_CLOCK, reg | div);
+ reg = snd_soc_component_read(component, WM8974_CLOCK) & 0x1e3;
+ snd_soc_component_write(component, WM8974_CLOCK, reg | div);
break;
default:
return -EINVAL;
@@ -379,19 +372,96 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return 0;
}
+static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out,
+ int *mclkdiv)
+{
+ unsigned int ratio = 2 * f_in / f_out;
+
+ if (ratio <= 2) {
+ *mclkdiv = WM8974_MCLKDIV_1;
+ ratio = 2;
+ } else if (ratio == 3) {
+ *mclkdiv = WM8974_MCLKDIV_1_5;
+ } else if (ratio == 4) {
+ *mclkdiv = WM8974_MCLKDIV_2;
+ } else if (ratio <= 6) {
+ *mclkdiv = WM8974_MCLKDIV_3;
+ ratio = 6;
+ } else if (ratio <= 8) {
+ *mclkdiv = WM8974_MCLKDIV_4;
+ ratio = 8;
+ } else if (ratio <= 12) {
+ *mclkdiv = WM8974_MCLKDIV_6;
+ ratio = 12;
+ } else if (ratio <= 16) {
+ *mclkdiv = WM8974_MCLKDIV_8;
+ ratio = 16;
+ } else {
+ *mclkdiv = WM8974_MCLKDIV_12;
+ ratio = 24;
+ }
+
+ return f_out * ratio / 2;
+}
+
+static int wm8974_update_clocks(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8974_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int fs256;
+ unsigned int fpll = 0;
+ unsigned int f;
+ int mclkdiv;
+
+ if (!priv->mclk || !priv->fs)
+ return 0;
+
+ fs256 = 256 * priv->fs;
+
+ f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
+ if (f != priv->mclk) {
+ /* The PLL performs best around 90MHz */
+ if (fs256 % 8000)
+ f = 22579200;
+ else
+ f = 24576000;
+
+ fpll = wm8974_get_mclkdiv(f, fs256, &mclkdiv);
+ }
+
+ wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
+ wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv);
+
+ return 0;
+}
+
+static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8974_priv *priv = snd_soc_component_get_drvdata(component);
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ priv->mclk = freq;
+
+ return wm8974_update_clocks(dai);
+}
+
static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
+ u16 clk = snd_soc_component_read(component, WM8974_CLOCK) & 0x1fe;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
clk |= 0x0001;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -408,6 +478,10 @@ static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
iface |= 0x0008;
break;
case SND_SOC_DAIFMT_DSP_A:
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_IB_IF ||
+ (fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_NB_IF) {
+ return -EINVAL;
+ }
iface |= 0x00018;
break;
default:
@@ -431,8 +505,8 @@ static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8974_IFACE, iface);
- snd_soc_write(codec, WM8974_CLOCK, clk);
+ snd_soc_component_write(component, WM8974_IFACE, iface);
+ snd_soc_component_write(component, WM8974_CLOCK, clk);
return 0;
}
@@ -440,9 +514,16 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8974_priv *priv = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8974_IFACE) & 0x19f;
+ u16 adn = snd_soc_component_read(component, WM8974_ADD) & 0x1f1;
+ int err;
+
+ priv->fs = params_rate(params);
+ err = wm8974_update_clocks(dai);
+ if (err)
+ return err;
/* bit size */
switch (params_width(params)) {
@@ -481,55 +562,56 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8974_IFACE, iface);
- snd_soc_write(codec, WM8974_ADD, adn);
+ snd_soc_component_write(component, WM8974_IFACE, iface);
+ snd_soc_component_write(component, WM8974_ADD, adn);
return 0;
}
-static int wm8974_mute(struct snd_soc_dai *dai, int mute)
+static int wm8974_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8974_DAC) & 0xffbf;
if (mute)
- snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
+ snd_soc_component_write(component, WM8974_DAC, mute_reg | 0x40);
else
- snd_soc_write(codec, WM8974_DAC, mute_reg);
+ snd_soc_component_write(component, WM8974_DAC, mute_reg);
return 0;
}
/* liam need to make this lower power with dapm */
-static int wm8974_set_bias_level(struct snd_soc_codec *codec,
+static int wm8974_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 power1 = snd_soc_component_read(component, WM8974_POWER1) & ~0x3;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
power1 |= 0x1; /* VMID 50k */
- snd_soc_write(codec, WM8974_POWER1, power1);
+ snd_soc_component_write(component, WM8974_POWER1, power1);
break;
case SND_SOC_BIAS_STANDBY:
power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- regcache_sync(dev_get_regmap(codec->dev, NULL));
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ regcache_sync(dev_get_regmap(component->dev, NULL));
/* Initial cap charge at VMID 5k */
- snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
+ snd_soc_component_write(component, WM8974_POWER1, power1 | 0x3);
mdelay(100);
}
power1 |= 0x2; /* VMID 500k */
- snd_soc_write(codec, WM8974_POWER1, power1);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8974_POWER1, 0);
+ snd_soc_component_write(component, WM8974_POWER2, 0);
+ snd_soc_component_write(component, WM8974_POWER3, 0);
break;
}
@@ -543,10 +625,12 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8974_ops = {
.hw_params = wm8974_pcm_hw_params,
- .digital_mute = wm8974_mute,
+ .mute_stream = wm8974_mute,
.set_fmt = wm8974_set_dai_fmt,
.set_clkdiv = wm8974_set_dai_clkdiv,
.set_pll = wm8974_set_dai_pll,
+ .set_sysclk = wm8974_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8974_dai = {
@@ -564,7 +648,7 @@ static struct snd_soc_dai_driver wm8974_dai = {
.rates = WM8974_RATES,
.formats = WM8974_FORMATS,},
.ops = &wm8974_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static const struct regmap_config wm8974_regmap = {
@@ -577,57 +661,58 @@ static const struct regmap_config wm8974_regmap = {
.cache_type = REGCACHE_FLAT,
};
-static int wm8974_probe(struct snd_soc_codec *codec)
+static int wm8974_probe(struct snd_soc_component *component)
{
int ret = 0;
- ret = wm8974_reset(codec);
+ ret = wm8974_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
+ dev_err(component->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 const struct snd_soc_component_driver soc_component_dev_wm8974 = {
+ .probe = wm8974_probe,
+ .set_bias_level = wm8974_set_bias_level,
+ .controls = wm8974_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8974_snd_controls),
+ .dapm_widgets = wm8974_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets),
+ .dapm_routes = wm8974_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int wm8974_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8974_i2c_probe(struct i2c_client *i2c)
{
+ struct wm8974_priv *priv;
struct regmap *regmap;
int ret;
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8974, &wm8974_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8974" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
@@ -643,8 +728,7 @@ static struct i2c_driver wm8974_i2c_driver = {
.name = "wm8974",
.of_match_table = wm8974_of_match,
},
- .probe = wm8974_i2c_probe,
- .remove = wm8974_i2c_remove,
+ .probe = wm8974_i2c_probe,
.id_table = wm8974_i2c_id,
};
diff --git a/sound/soc/codecs/wm8974.h b/sound/soc/codecs/wm8974.h
index 3c94e7bb55a6..d6175383f000 100644
--- a/sound/soc/codecs/wm8974.h
+++ b/sound/soc/codecs/wm8974.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index d36d6001fbb7..935761e50865 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8978.c -- WM8978 ALSA SoC Audio Codec driver
*
@@ -5,10 +6,6 @@
* 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>
@@ -408,7 +405,7 @@ struct wm8978_pll_div {
#define FIXED_PLL_SIZE (1 << 24)
-static void pll_factors(struct snd_soc_codec *codec,
+static void pll_factors(struct snd_soc_component *component,
struct wm8978_pll_div *pll_div, unsigned int target, unsigned int source)
{
u64 k_part;
@@ -424,7 +421,7 @@ static void pll_factors(struct snd_soc_codec *codec,
}
if (n_div < 6 || n_div > 12)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"WM8978 N value exceeds recommended range! N = %u\n",
n_div);
@@ -471,9 +468,9 @@ static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk,
* 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)
+static int wm8978_configure_pll(struct snd_soc_component *component)
{
- struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
struct wm8978_pll_div pll_div;
unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk,
f_256fs = wm8978->f_256fs;
@@ -501,13 +498,13 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
if (4 * f_opclk < 3 * f_mclk)
/* Have to use OPCLKDIV */
- opclk_div = (3 * f_mclk / 4 + f_opclk - 1) / f_opclk;
+ opclk_div = DIV_ROUND_UP(3 * f_mclk / 4, f_opclk);
else
opclk_div = 1;
- dev_dbg(codec->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div);
+ dev_dbg(component->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div);
- snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 0x30,
+ snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 0x30,
(opclk_div - 1) << 4);
wm8978->f_pllout = f_opclk * opclk_div;
@@ -533,28 +530,28 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
f2 = wm8978->f_pllout * 4;
- dev_dbg(codec->dev, "%s: f_MCLK=%uHz, f_PLLOUT=%uHz\n", __func__,
+ dev_dbg(component->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);
+ pll_factors(component, &pll_div, f2, wm8978->f_mclk);
- dev_dbg(codec->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n",
+ dev_dbg(component->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n",
__func__, pll_div.n, pll_div.k, pll_div.div2);
/* Turn PLL off for configuration... */
- snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_write(component, WM8978_PLL_N, (pll_div.div2 << 4) | pll_div.n);
+ snd_soc_component_write(component, WM8978_PLL_K1, pll_div.k >> 18);
+ snd_soc_component_write(component, WM8978_PLL_K2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8978_PLL_K3, pll_div.k & 0x1ff);
/* ...and on again */
- snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+ snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
if (f_opclk)
/* Output PLL (OPCLK) to GPIO1 */
- snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 4);
+ snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 7, 4);
return 0;
}
@@ -565,8 +562,8 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (div_id) {
@@ -588,18 +585,18 @@ static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
* find an exact MCLK divider configuration - it will
* be equal to or double the OPCLK divisor.
*/
- ret = wm8978_configure_pll(codec);
+ ret = wm8978_configure_pll(component);
break;
case WM8978_BCLKDIV:
if (div & ~0x1c)
return -EINVAL;
- snd_soc_update_bits(codec, WM8978_CLOCKING, 0x1c, div);
+ snd_soc_component_update_bits(component, WM8978_CLOCKING, 0x1c, div);
break;
default:
return -EINVAL;
}
- dev_dbg(codec->dev, "%s: ID %d, value %u\n", __func__, div_id, div);
+ dev_dbg(component->dev, "%s: ID %d, value %u\n", __func__, div_id, div);
return ret;
}
@@ -610,18 +607,18 @@ static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
int ret = 0;
- dev_dbg(codec->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq);
+ dev_dbg(component->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq);
if (freq) {
wm8978->f_mclk = freq;
/* Even if MCLK is used for system clock, might have to drive OPCLK */
if (wm8978->f_opclk)
- ret = wm8978_configure_pll(codec);
+ ret = wm8978_configure_pll(component);
/* Our sysclk is fixed to 256 * fs, will configure in .hw_params() */
@@ -631,13 +628,13 @@ static int wm8978_set_dai_sysclk(struct snd_soc_dai *codec_dai, int 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);
+ snd_soc_component_update_bits(component, WM8978_CLOCKING, 0x100, 0);
/* GPIO1 into default mode as input - before configuring PLL */
- snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
+ snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 7, 0);
/* Turn off PLL */
- snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+ snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
wm8978->sysclk = WM8978_MCLK;
wm8978->f_pllout = 0;
wm8978->f_opclk = 0;
@@ -651,22 +648,22 @@ static int wm8978_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
*/
static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
/*
* 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);
+ u16 iface = snd_soc_component_read(component, WM8978_AUDIO_INTERFACE) & ~0x198;
+ u16 clk = snd_soc_component_read(component, WM8978_CLOCKING);
- dev_dbg(codec->dev, "%s\n", __func__);
+ dev_dbg(component->dev, "%s\n", __func__);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
clk |= 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
clk &= ~1;
break;
default:
@@ -707,8 +704,8 @@ static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface);
- snd_soc_write(codec, WM8978_CLOCKING, clk);
+ snd_soc_component_write(component, WM8978_AUDIO_INTERFACE, iface);
+ snd_soc_component_write(component, WM8978_CLOCKING, clk);
return 0;
}
@@ -720,14 +717,14 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
/* Word length mask = 0x60 */
- u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60;
+ u16 iface_ctl = snd_soc_component_read(component, 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 ?
+ u16 add_ctl = snd_soc_component_read(component, WM8978_ADDITIONAL_CONTROL) & ~0xe;
+ u16 clking = snd_soc_component_read(component, WM8978_CLOCKING);
+ enum wm8978_sysclk_src current_clk_id = (clking & 0x100) ?
WM8978_PLL : WM8978_MCLK;
unsigned int f_sel, diff, diff_best = INT_MAX;
int i, best = 0;
@@ -781,7 +778,7 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream,
} else {
if (!wm8978->f_opclk) {
/* We only enter here, if OPCLK is not used */
- int ret = wm8978_configure_pll(codec);
+ int ret = wm8978_configure_pll(component);
if (ret < 0)
return ret;
}
@@ -812,81 +809,82 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream,
}
if (diff)
- dev_warn(codec->dev, "Imprecise sampling rate: %uHz%s\n",
+ dev_warn(component->dev, "Imprecise sampling rate: %uHz%s\n",
f_sel * mclk_denominator[best] / mclk_numerator[best] / 256,
wm8978->sysclk == WM8978_MCLK ?
", consider using PLL" : "");
- dev_dbg(codec->dev, "%s: width %d, rate %u, MCLK divisor #%d\n", __func__,
+ dev_dbg(component->dev, "%s: width %d, rate %u, MCLK divisor #%d\n", __func__,
params_width(params), params_rate(params), best);
/* MCLK divisor mask = 0xe0 */
- snd_soc_update_bits(codec, WM8978_CLOCKING, 0xe0, best << 5);
+ snd_soc_component_update_bits(component, WM8978_CLOCKING, 0xe0, best << 5);
- snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface_ctl);
- snd_soc_write(codec, WM8978_ADDITIONAL_CONTROL, add_ctl);
+ snd_soc_component_write(component, WM8978_AUDIO_INTERFACE, iface_ctl);
+ snd_soc_component_write(component, WM8978_ADDITIONAL_CONTROL, add_ctl);
if (wm8978->sysclk != current_clk_id) {
if (wm8978->sysclk == WM8978_PLL)
/* Run CODEC from PLL instead of MCLK */
- snd_soc_update_bits(codec, WM8978_CLOCKING,
+ snd_soc_component_update_bits(component, WM8978_CLOCKING,
0x100, 0x100);
else
/* Clock CODEC directly from MCLK */
- snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+ snd_soc_component_update_bits(component, WM8978_CLOCKING, 0x100, 0);
}
return 0;
}
-static int wm8978_mute(struct snd_soc_dai *dai, int mute)
+static int wm8978_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s: %d\n", __func__, mute);
+ dev_dbg(component->dev, "%s: %d\n", __func__, mute);
if (mute)
- snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0x40);
+ snd_soc_component_update_bits(component, WM8978_DAC_CONTROL, 0x40, 0x40);
else
- snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0);
+ snd_soc_component_update_bits(component, WM8978_DAC_CONTROL, 0x40, 0);
return 0;
}
-static int wm8978_set_bias_level(struct snd_soc_codec *codec,
+static int wm8978_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- u16 power1 = snd_soc_read(codec, WM8978_POWER_MANAGEMENT_1) & ~3;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 power1 = snd_soc_component_read(component, WM8978_POWER_MANAGEMENT_1) & ~3;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
power1 |= 1; /* VMID 75k */
- snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+ snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, power1);
break;
case SND_SOC_BIAS_STANDBY:
/* bit 3: enable bias, bit 2: enable I/O tie off buffer */
power1 |= 0xc;
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Initial cap charge at VMID 5k */
- snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1,
+ snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1,
power1 | 0x3);
mdelay(100);
}
power1 |= 0x2; /* VMID 500k */
- snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+ snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, power1);
break;
case SND_SOC_BIAS_OFF:
/* Preserve PLL - OPCLK may be used by someone */
- snd_soc_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);
+ snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, ~0x20, 0);
+ snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_2, 0);
+ snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_3, 0);
break;
}
- dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1);
+ dev_dbg(component->dev, "%s: %d, %x\n", __func__, level, power1);
return 0;
}
@@ -896,10 +894,11 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8978_dai_ops = {
.hw_params = wm8978_hw_params,
- .digital_mute = wm8978_mute,
+ .mute_stream = wm8978_mute,
.set_fmt = wm8978_set_dai_fmt,
.set_clkdiv = wm8978_set_dai_clkdiv,
.set_sysclk = wm8978_set_dai_sysclk,
+ .no_capture_mute = 1,
};
/* Also supports 12kHz */
@@ -920,34 +919,36 @@ static struct snd_soc_dai_driver wm8978_dai = {
.formats = WM8978_FORMATS,
},
.ops = &wm8978_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8978_suspend(struct snd_soc_codec *codec)
+static int wm8978_suspend(struct snd_soc_component *component)
{
- struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
/* Also switch PLL off */
- snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
+ snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, 0);
regcache_mark_dirty(wm8978->regmap);
return 0;
}
-static int wm8978_resume(struct snd_soc_codec *codec)
+static int wm8978_resume(struct snd_soc_component *component)
{
- struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
/* Sync reg_cache with the hardware */
regcache_sync(wm8978->regmap);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
if (wm8978->f_pllout)
/* Switch PLL on */
- snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+ snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
return 0;
}
@@ -971,9 +972,9 @@ static const int update_reg[] = {
WM8978_ROUT2_SPK_CONTROL,
};
-static int wm8978_probe(struct snd_soc_codec *codec)
+static int wm8978_probe(struct snd_soc_component *component)
{
- struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component);
int i;
/*
@@ -988,23 +989,25 @@ static int wm8978_probe(struct snd_soc_codec *codec)
* written.
*/
for (i = 0; i < ARRAY_SIZE(update_reg); i++)
- snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
+ snd_soc_component_update_bits(component, 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 snd_soc_component_driver soc_component_dev_wm8978 = {
+ .probe = wm8978_probe,
+ .suspend = wm8978_suspend,
+ .resume = wm8978_resume,
+ .set_bias_level = wm8978_set_bias_level,
+ .controls = wm8978_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8978_snd_controls),
+ .dapm_widgets = wm8978_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets),
+ .dapm_routes = wm8978_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8978_regmap_config = {
@@ -1014,13 +1017,12 @@ static const struct regmap_config wm8978_regmap_config = {
.max_register = WM8978_MAX_REGISTER,
.volatile_reg = wm8978_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8978_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults),
};
-static int wm8978_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8978_i2c_probe(struct i2c_client *i2c)
{
struct wm8978_priv *wm8978;
int ret;
@@ -1046,8 +1048,8 @@ static int wm8978_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8978, &wm8978_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8978, &wm8978_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
return ret;
@@ -1056,25 +1058,24 @@ static int wm8978_i2c_probe(struct i2c_client *i2c,
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 },
+ { "wm8978" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
+static const struct of_device_id wm8978_of_match[] = {
+ { .compatible = "wlf,wm8978", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8978_of_match);
+
static struct i2c_driver wm8978_i2c_driver = {
.driver = {
.name = "wm8978",
+ .of_match_table = wm8978_of_match,
},
- .probe = wm8978_i2c_probe,
- .remove = wm8978_i2c_remove,
+ .probe = wm8978_i2c_probe,
.id_table = wm8978_i2c_id,
};
diff --git a/sound/soc/codecs/wm8978.h b/sound/soc/codecs/wm8978.h
index 6ae43495b7cf..e1986dcec49a 100644
--- a/sound/soc/codecs/wm8978.h
+++ b/sound/soc/codecs/wm8978.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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__
@@ -78,8 +75,8 @@ enum wm8978_clk_id {
};
enum wm8978_sysclk_src {
+ WM8978_MCLK = 0,
WM8978_PLL,
- WM8978_MCLK
};
#endif /* __WM8978_H__ */
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index 7350ff654bbf..cd34f71cf42a 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -492,14 +489,14 @@ static const struct snd_soc_dapm_route wm8983_audio_map[] = {
static int eqmode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg;
- reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
+ reg = snd_soc_component_read(component, WM8983_EQ1_LOW_SHELF);
if (reg & WM8983_EQ3DMODE)
- ucontrol->value.integer.value[0] = 1;
+ ucontrol->value.enumerated.item[0] = 1;
else
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.enumerated.item[0] = 0;
return 0;
}
@@ -507,41 +504,41 @@ static int eqmode_get(struct snd_kcontrol *kcontrol,
static int eqmode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int regpwr2, regpwr3;
unsigned int reg_eq;
- if (ucontrol->value.integer.value[0] != 0
- && ucontrol->value.integer.value[0] != 1)
+ if (ucontrol->value.enumerated.item[0] != 0
+ && ucontrol->value.enumerated.item[0] != 1)
return -EINVAL;
- reg_eq = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
+ reg_eq = snd_soc_component_read(component, WM8983_EQ1_LOW_SHELF);
switch ((reg_eq & WM8983_EQ3DMODE) >> WM8983_EQ3DMODE_SHIFT) {
case 0:
- if (!ucontrol->value.integer.value[0])
+ if (!ucontrol->value.enumerated.item[0])
return 0;
break;
case 1:
- if (ucontrol->value.integer.value[0])
+ if (ucontrol->value.enumerated.item[0])
return 0;
break;
}
- regpwr2 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_2);
- regpwr3 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_3);
+ regpwr2 = snd_soc_component_read(component, WM8983_POWER_MANAGEMENT_2);
+ regpwr3 = snd_soc_component_read(component, WM8983_POWER_MANAGEMENT_3);
/* disable the DACs and ADCs */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_2,
WM8983_ADCENR_MASK | WM8983_ADCENL_MASK, 0);
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_3,
WM8983_DACENR_MASK | WM8983_DACENL_MASK, 0);
/* set the desired eqmode */
- snd_soc_update_bits(codec, WM8983_EQ1_LOW_SHELF,
+ snd_soc_component_update_bits(component, WM8983_EQ1_LOW_SHELF,
WM8983_EQ3DMODE_MASK,
- ucontrol->value.integer.value[0]
+ ucontrol->value.enumerated.item[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);
+ snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_2, regpwr2);
+ snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_3, regpwr3);
return 0;
}
@@ -560,18 +557,18 @@ static bool wm8983_writeable(struct device *dev, unsigned int reg)
}
}
-static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute)
+static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, WM8983_DAC_CONTROL,
+ return snd_soc_component_update_bits(component, WM8983_DAC_CONTROL,
WM8983_SOFTMUTE_MASK,
!!mute << WM8983_SOFTMUTE_SHIFT);
}
static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 format, master, bcp, lrp;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -593,14 +590,14 @@ static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE,
WM8983_FMT_MASK, format << WM8983_FMT_SHIFT);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
master = 0;
break;
default:
@@ -608,7 +605,7 @@ static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL,
WM8983_MS_MASK, master << WM8983_MS_SHIFT);
/* FIXME: We don't currently support DSP A/B modes */
@@ -639,9 +636,9 @@ static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE,
WM8983_LRCP_MASK, lrp << WM8983_LRCP_SHIFT);
- snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE,
WM8983_BCP_MASK, bcp << WM8983_BCP_SHIFT);
return 0;
}
@@ -651,8 +648,8 @@ static int wm8983_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int i;
- struct snd_soc_codec *codec = dai->codec;
- struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component);
u16 blen, srate_idx;
u32 tmp;
int srate_best;
@@ -660,7 +657,7 @@ static int wm8983_hw_params(struct snd_pcm_substream *substream,
ret = snd_soc_params_to_bclk(params);
if (ret < 0) {
- dev_err(codec->dev, "Failed to convert params to bclk: %d\n", ret);
+ dev_err(component->dev, "Failed to convert params to bclk: %d\n", ret);
return ret;
}
@@ -685,7 +682,7 @@ static int wm8983_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE,
WM8983_WL_MASK, blen << WM8983_WL_SHIFT);
/*
@@ -702,7 +699,7 @@ static int wm8983_hw_params(struct snd_pcm_substream *substream,
}
dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]);
- snd_soc_update_bits(codec, WM8983_ADDITIONAL_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_ADDITIONAL_CONTROL,
WM8983_SR_MASK, srate_idx << WM8983_SR_SHIFT);
dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8983->bclk);
@@ -721,7 +718,7 @@ static int wm8983_hw_params(struct snd_pcm_substream *substream,
}
dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio);
- snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL,
WM8983_MCLKDIV_MASK, i << WM8983_MCLKDIV_SHIFT);
/* select the appropriate bclk divider */
@@ -737,7 +734,7 @@ static int wm8983_hw_params(struct snd_pcm_substream *substream,
}
dev_dbg(dai->dev, "BCLK div = %d\n", i);
- snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL,
WM8983_BCLKDIV_MASK, i << WM8983_BCLKDIV_SHIFT);
return 0;
@@ -789,13 +786,13 @@ static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
unsigned int freq_out)
{
int ret;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct pll_div pll_div;
- codec = dai->codec;
+ component = dai->component;
if (!freq_in || !freq_out) {
/* disable the PLL */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_PLLEN_MASK, 0);
return 0;
} else {
@@ -804,19 +801,19 @@ static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
return ret;
/* disable the PLL before re-programming it */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_PLLEN_MASK, 0);
/* set PLLN and PRESCALE */
- snd_soc_write(codec, WM8983_PLL_N,
+ snd_soc_component_write(component, 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));
+ snd_soc_component_write(component, WM8983_PLL_K_3, pll_div.k & 0x1ff);
+ snd_soc_component_write(component, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8983_PLL_K_1, (pll_div.k >> 18));
/* enable the PLL */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_PLLEN_MASK, WM8983_PLLEN);
}
@@ -826,16 +823,16 @@ static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM8983_CLKSRC_MCLK:
- snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL,
WM8983_CLKSEL_MASK, 0);
break;
case WM8983_CLKSRC_PLL:
- snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL,
WM8983_CLKSEL_MASK, WM8983_CLKSEL);
break;
default:
@@ -847,110 +844,112 @@ static int wm8983_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int wm8983_set_bias_level(struct snd_soc_codec *codec,
+static int wm8983_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+ struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
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,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_VMIDSEL_MASK,
1 << WM8983_VMIDSEL_SHIFT);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(wm8983->regmap);
if (ret < 0) {
- dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
return ret;
}
/* enable anti-pop features */
- snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC,
+ snd_soc_component_update_bits(component, WM8983_OUT4_TO_ADC,
WM8983_POBCTRL_MASK | WM8983_DELEN_MASK,
WM8983_POBCTRL | WM8983_DELEN);
/* enable thermal shutdown */
- snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL,
+ snd_soc_component_update_bits(component, WM8983_OUTPUT_CTRL,
WM8983_TSDEN_MASK, WM8983_TSDEN);
/* enable BIASEN */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_BIASEN_MASK, WM8983_BIASEN);
/* VMID at 100k */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_VMIDSEL_MASK,
1 << WM8983_VMIDSEL_SHIFT);
msleep(250);
/* disable anti-pop features */
- snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC,
+ snd_soc_component_update_bits(component, WM8983_OUT4_TO_ADC,
WM8983_POBCTRL_MASK |
WM8983_DELEN_MASK, 0);
}
/* VMID at 500k */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_VMIDSEL_MASK,
2 << WM8983_VMIDSEL_SHIFT);
break;
case SND_SOC_BIAS_OFF:
/* disable thermal shutdown */
- snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL,
+ snd_soc_component_update_bits(component, WM8983_OUTPUT_CTRL,
WM8983_TSDEN_MASK, 0);
/* disable VMIDSEL and BIASEN */
- snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1,
WM8983_VMIDSEL_MASK | WM8983_BIASEN_MASK,
0);
/* wait for VMID to discharge */
msleep(100);
- snd_soc_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);
+ snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_1, 0);
+ snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_2, 0);
+ snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_3, 0);
break;
}
return 0;
}
-static int wm8983_probe(struct snd_soc_codec *codec)
+static int wm8983_probe(struct snd_soc_component *component)
{
int ret;
int i;
- ret = snd_soc_write(codec, WM8983_SOFTWARE_RESET, 0);
+ ret = snd_soc_component_write(component, WM8983_SOFTWARE_RESET, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
/* 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],
+ snd_soc_component_update_bits(component, vol_update_regs[i],
0x100, 0x100);
/* mute all outputs and set PGAs to minimum gain */
for (i = WM8983_LOUT1_HP_VOLUME_CTRL;
i <= WM8983_OUT4_MONO_MIX_CTRL; ++i)
- snd_soc_update_bits(codec, i, 0x40, 0x40);
+ snd_soc_component_update_bits(component, i, 0x40, 0x40);
/* enable soft mute */
- snd_soc_update_bits(codec, WM8983_DAC_CONTROL,
+ snd_soc_component_update_bits(component, WM8983_DAC_CONTROL,
WM8983_SOFTMUTE_MASK,
WM8983_SOFTMUTE);
/* enable BIASCUT */
- snd_soc_update_bits(codec, WM8983_BIAS_CTRL,
+ snd_soc_component_update_bits(component, WM8983_BIAS_CTRL,
WM8983_BIASCUT, WM8983_BIASCUT);
return 0;
}
static const struct snd_soc_dai_ops wm8983_dai_ops = {
- .digital_mute = wm8983_dac_mute,
+ .mute_stream = wm8983_dac_mute,
.hw_params = wm8983_hw_params,
.set_fmt = wm8983_set_fmt,
.set_sysclk = wm8983_set_sysclk,
- .set_pll = wm8983_set_pll
+ .set_pll = wm8983_set_pll,
+ .no_capture_mute = 1,
};
#define WM8983_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -973,19 +972,22 @@ static struct snd_soc_dai_driver wm8983_dai = {
.formats = WM8983_FORMATS,
},
.ops = &wm8983_dai_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 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 snd_soc_component_driver soc_component_dev_wm8983 = {
+ .probe = wm8983_probe,
+ .set_bias_level = wm8983_set_bias_level,
+ .controls = wm8983_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8983_snd_controls),
+ .dapm_widgets = wm8983_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets),
+ .dapm_routes = wm8983_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8983_regmap = {
@@ -994,7 +996,7 @@ static const struct regmap_config wm8983_regmap = {
.reg_defaults = wm8983_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8983_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = WM8983_MAX_REGISTER,
.writeable_reg = wm8983_writeable,
@@ -1019,29 +1021,21 @@ static int wm8983_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8983);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8983, &wm8983_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
},
.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)
+static int wm8983_i2c_probe(struct i2c_client *i2c)
{
struct wm8983_priv *wm8983;
int ret;
@@ -1059,20 +1053,14 @@ static int wm8983_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8983);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8983, &wm8983_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8983" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id);
@@ -1082,7 +1070,6 @@ static struct i2c_driver wm8983_i2c_driver = {
.name = "wm8983",
},
.probe = wm8983_i2c_probe,
- .remove = wm8983_i2c_remove,
.id_table = wm8983_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8983.h b/sound/soc/codecs/wm8983.h
index 71ee619c2742..994c01704d59 100644
--- a/sound/soc/codecs/wm8983.h
+++ b/sound/soc/codecs/wm8983.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 9918152a03c7..be23c0c608d1 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1,13 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * wm8985.c -- WM8985 ALSA SoC Audio driver
+ * wm8985.c -- WM8985 / WM8758 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.
+ * WM8758 support:
+ * Copyright: 2016 Barix AG
+ * Author: Petr Kulhavy <petr@barix.com>
*
* TODO:
* o Add OUT3/OUT4 mixer controls.
@@ -40,6 +40,11 @@ static const char *wm8985_supply_names[WM8985_NUM_SUPPLIES] = {
"AVDD2"
};
+enum wm8985_type {
+ WM8985,
+ WM8758,
+};
+
static const struct reg_default wm8985_reg_defaults[] = {
{ 1, 0x0000 }, /* R1 - Power management 1 */
{ 2, 0x0000 }, /* R2 - Power management 2 */
@@ -181,6 +186,7 @@ static const int volume_update_regs[] = {
struct wm8985_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8985_NUM_SUPPLIES];
+ enum wm8985_type dev_type;
unsigned int sysclk;
unsigned int bclk;
};
@@ -289,7 +295,7 @@ static const char *depth_3d_text[] = {
};
static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text);
-static const struct snd_kcontrol_new wm8985_snd_controls[] = {
+static const struct snd_kcontrol_new wm8985_common_snd_controls[] = {
SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL,
0, 1, 0),
@@ -355,10 +361,6 @@ static const struct snd_kcontrol_new wm8985_snd_controls[] = {
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),
@@ -379,20 +381,30 @@ static const struct snd_kcontrol_new wm8985_snd_controls[] = {
SOC_SINGLE_TLV("EQ5 Volume", WM8985_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv),
SOC_ENUM("3D Depth", depth_3d),
+};
+
+static const struct snd_kcontrol_new wm8985_specific_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Aux Bypass Volume",
+ WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0,
+ aux_tlv),
SOC_ENUM("Speaker Mode", speaker_mode)
};
static const struct snd_kcontrol_new left_out_mixer[] = {
SOC_DAPM_SINGLE("Line Switch", WM8985_LEFT_MIXER_CTRL, 1, 1, 0),
- SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
SOC_DAPM_SINGLE("PCM Switch", WM8985_LEFT_MIXER_CTRL, 0, 1, 0),
+
+ /* --- WM8985 only --- */
+ SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
};
static const struct snd_kcontrol_new right_out_mixer[] = {
SOC_DAPM_SINGLE("Line Switch", WM8985_RIGHT_MIXER_CTRL, 1, 1, 0),
- SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
SOC_DAPM_SINGLE("PCM Switch", WM8985_RIGHT_MIXER_CTRL, 0, 1, 0),
+
+ /* --- WM8985 only --- */
+ SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
};
static const struct snd_kcontrol_new left_input_mixer[] = {
@@ -410,6 +422,8 @@ static const struct snd_kcontrol_new right_input_mixer[] = {
static const struct snd_kcontrol_new left_boost_mixer[] = {
SOC_DAPM_SINGLE_TLV("L2 Volume", WM8985_LEFT_ADC_BOOST_CTRL,
4, 7, 0, boost_tlv),
+
+ /* --- WM8985 only --- */
SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8985_LEFT_ADC_BOOST_CTRL,
0, 7, 0, boost_tlv)
};
@@ -417,11 +431,13 @@ static const struct snd_kcontrol_new left_boost_mixer[] = {
static const struct snd_kcontrol_new right_boost_mixer[] = {
SOC_DAPM_SINGLE_TLV("R2 Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
4, 7, 0, boost_tlv),
+
+ /* --- WM8985 only --- */
SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
0, 7, 0, boost_tlv)
};
-static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget wm8985_common_dapm_widgets[] = {
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8985_POWER_MANAGEMENT_3,
0, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8985_POWER_MANAGEMENT_3,
@@ -431,21 +447,11 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
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,
@@ -468,8 +474,6 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
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"),
@@ -478,13 +482,42 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("SPKR")
};
-static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
+static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
+
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+};
+
+static const struct snd_soc_dapm_widget wm8758_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer,
+ ARRAY_SIZE(left_out_mixer) - 1),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer,
+ ARRAY_SIZE(right_out_mixer) - 1),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer,
+ ARRAY_SIZE(left_boost_mixer) - 1),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer,
+ ARRAY_SIZE(right_boost_mixer) - 1),
+};
+
+static const struct snd_soc_dapm_route wm8985_common_dapm_routes[] = {
{ "Right Output Mixer", "PCM Switch", "Right DAC" },
- { "Right Output Mixer", "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" },
@@ -501,13 +534,11 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
{ "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" },
@@ -522,18 +553,50 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
{ "Left Input Mixer", "MicN Switch", "LIN" },
{ "Left Input Mixer", "MicP Switch", "LIP" },
};
+static const struct snd_soc_dapm_route wm8985_aux_dapm_routes[] = {
+ { "Right Output Mixer", "Aux Switch", "AUXR" },
+ { "Left Output Mixer", "Aux Switch", "AUXL" },
+
+ { "Right Boost Mixer", "AUXR Volume", "AUXR" },
+ { "Left Boost Mixer", "AUXL Volume", "AUXL" },
+};
+
+static int wm8985_add_widgets(struct snd_soc_component *component)
+{
+ struct wm8985_priv *wm8985 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ switch (wm8985->dev_type) {
+ case WM8758:
+ snd_soc_dapm_new_controls(dapm, wm8758_dapm_widgets,
+ ARRAY_SIZE(wm8758_dapm_widgets));
+ break;
+
+ case WM8985:
+ snd_soc_add_component_controls(component, wm8985_specific_snd_controls,
+ ARRAY_SIZE(wm8985_specific_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, wm8985_dapm_widgets,
+ ARRAY_SIZE(wm8985_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, wm8985_aux_dapm_routes,
+ ARRAY_SIZE(wm8985_aux_dapm_routes));
+ break;
+ }
+
+ return 0;
+}
static int eqmode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg;
- reg = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF);
+ reg = snd_soc_component_read(component, WM8985_EQ1_LOW_SHELF);
if (reg & WM8985_EQ3DMODE)
- ucontrol->value.integer.value[0] = 1;
+ ucontrol->value.enumerated.item[0] = 1;
else
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.enumerated.item[0] = 0;
return 0;
}
@@ -541,66 +604,66 @@ static int eqmode_get(struct snd_kcontrol *kcontrol,
static int eqmode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int regpwr2, regpwr3;
unsigned int reg_eq;
- if (ucontrol->value.integer.value[0] != 0
- && ucontrol->value.integer.value[0] != 1)
+ if (ucontrol->value.enumerated.item[0] != 0
+ && ucontrol->value.enumerated.item[0] != 1)
return -EINVAL;
- reg_eq = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF);
+ reg_eq = snd_soc_component_read(component, WM8985_EQ1_LOW_SHELF);
switch ((reg_eq & WM8985_EQ3DMODE) >> WM8985_EQ3DMODE_SHIFT) {
case 0:
- if (!ucontrol->value.integer.value[0])
+ if (!ucontrol->value.enumerated.item[0])
return 0;
break;
case 1:
- if (ucontrol->value.integer.value[0])
+ if (ucontrol->value.enumerated.item[0])
return 0;
break;
}
- regpwr2 = snd_soc_read(codec, WM8985_POWER_MANAGEMENT_2);
- regpwr3 = snd_soc_read(codec, WM8985_POWER_MANAGEMENT_3);
+ regpwr2 = snd_soc_component_read(component, WM8985_POWER_MANAGEMENT_2);
+ regpwr3 = snd_soc_component_read(component, WM8985_POWER_MANAGEMENT_3);
/* disable the DACs and ADCs */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_2,
WM8985_ADCENR_MASK | WM8985_ADCENL_MASK, 0);
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_3,
WM8985_DACENR_MASK | WM8985_DACENL_MASK, 0);
- snd_soc_update_bits(codec, WM8985_ADDITIONAL_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_ADDITIONAL_CONTROL,
WM8985_M128ENB_MASK, WM8985_M128ENB);
/* set the desired eqmode */
- snd_soc_update_bits(codec, WM8985_EQ1_LOW_SHELF,
+ snd_soc_component_update_bits(component, WM8985_EQ1_LOW_SHELF,
WM8985_EQ3DMODE_MASK,
- ucontrol->value.integer.value[0]
+ ucontrol->value.enumerated.item[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);
+ snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_2, regpwr2);
+ snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_3, regpwr3);
return 0;
}
-static int wm8985_reset(struct snd_soc_codec *codec)
+static int wm8985_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, WM8985_SOFTWARE_RESET, 0x0);
+ return snd_soc_component_write(component, WM8985_SOFTWARE_RESET, 0x0);
}
-static int wm8985_dac_mute(struct snd_soc_dai *dai, int mute)
+static int wm8985_dac_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, WM8985_DAC_CONTROL,
+ return snd_soc_component_update_bits(component, WM8985_DAC_CONTROL,
WM8985_SOFTMUTE_MASK,
!!mute << WM8985_SOFTMUTE_SHIFT);
}
static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
u16 format, master, bcp, lrp;
- codec = dai->codec;
+ component = dai->component;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -621,14 +684,14 @@ static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE,
WM8985_FMT_MASK, format << WM8985_FMT_SHIFT);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
master = 0;
break;
default:
@@ -636,7 +699,7 @@ static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL,
WM8985_MS_MASK, master << WM8985_MS_SHIFT);
/* frame inversion is not valid for dsp modes */
@@ -673,9 +736,9 @@ static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE,
WM8985_LRP_MASK, lrp << WM8985_LRP_SHIFT);
- snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE,
WM8985_BCP_MASK, bcp << WM8985_BCP_SHIFT);
return 0;
}
@@ -685,14 +748,14 @@ static int wm8985_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int i;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct wm8985_priv *wm8985;
u16 blen, srate_idx;
unsigned int tmp;
int srate_best;
- codec = dai->codec;
- wm8985 = snd_soc_codec_get_drvdata(codec);
+ component = dai->component;
+ wm8985 = snd_soc_component_get_drvdata(component);
wm8985->bclk = snd_soc_params_to_bclk(params);
if ((int)wm8985->bclk < 0)
@@ -717,7 +780,7 @@ static int wm8985_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE,
WM8985_WL_MASK, blen << WM8985_WL_SHIFT);
/*
@@ -734,7 +797,7 @@ static int wm8985_hw_params(struct snd_pcm_substream *substream,
}
dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]);
- snd_soc_update_bits(codec, WM8985_ADDITIONAL_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_ADDITIONAL_CONTROL,
WM8985_SR_MASK, srate_idx << WM8985_SR_SHIFT);
dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8985->bclk);
@@ -753,7 +816,7 @@ static int wm8985_hw_params(struct snd_pcm_substream *substream,
}
dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio);
- snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL,
WM8985_MCLKDIV_MASK, i << WM8985_MCLKDIV_SHIFT);
/* select the appropriate bclk divider */
@@ -769,7 +832,7 @@ static int wm8985_hw_params(struct snd_pcm_substream *substream,
}
dev_dbg(dai->dev, "BCLK div = %d\n", i);
- snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL,
WM8985_BCLKDIV_MASK, i << WM8985_BCLKDIV_SHIFT);
return 0;
}
@@ -821,13 +884,13 @@ static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id,
unsigned int freq_out)
{
int ret;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct pll_div pll_div;
- codec = dai->codec;
+ component = dai->component;
if (!freq_in || !freq_out) {
/* disable the PLL */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_PLLEN_MASK, 0);
} else {
ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
@@ -835,18 +898,18 @@ static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id,
return ret;
/* set PLLN and PRESCALE */
- snd_soc_write(codec, WM8985_PLL_N,
+ snd_soc_component_write(component, 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));
+ snd_soc_component_write(component, WM8985_PLL_K_3, pll_div.k & 0x1ff);
+ snd_soc_component_write(component, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_component_write(component, WM8985_PLL_K_1, (pll_div.k >> 18));
/* set the source of the clock to be the PLL */
- snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL,
WM8985_CLKSEL_MASK, WM8985_CLKSEL);
/* enable the PLL */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_PLLEN_MASK, WM8985_PLLEN);
}
return 0;
@@ -855,21 +918,21 @@ static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id,
static int wm8985_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct wm8985_priv *wm8985;
- codec = dai->codec;
- wm8985 = snd_soc_codec_get_drvdata(codec);
+ component = dai->component;
+ wm8985 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM8985_CLKSRC_MCLK:
- snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL,
WM8985_CLKSEL_MASK, 0);
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_PLLEN_MASK, 0);
break;
case WM8985_CLKSRC_PLL:
- snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL,
WM8985_CLKSEL_MASK, WM8985_CLKSEL);
break;
default:
@@ -881,27 +944,28 @@ static int wm8985_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int wm8985_set_bias_level(struct snd_soc_codec *codec,
+static int wm8985_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
int ret;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wm8985_priv *wm8985;
- wm8985 = snd_soc_codec_get_drvdata(codec);
+ wm8985 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
/* VMID at 75k */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_VMIDSEL_MASK,
1 << WM8985_VMIDSEL_SHIFT);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
wm8985->supplies);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
@@ -910,45 +974,45 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
regcache_sync(wm8985->regmap);
/* enable anti-pop features */
- snd_soc_update_bits(codec, WM8985_OUT4_TO_ADC,
+ snd_soc_component_update_bits(component, WM8985_OUT4_TO_ADC,
WM8985_POBCTRL_MASK,
WM8985_POBCTRL);
/* enable thermal shutdown */
- snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0,
WM8985_TSDEN_MASK, WM8985_TSDEN);
- snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0,
WM8985_TSOPCTRL_MASK,
WM8985_TSOPCTRL);
/* enable BIASEN */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_BIASEN_MASK, WM8985_BIASEN);
/* VMID at 75k */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_VMIDSEL_MASK,
1 << WM8985_VMIDSEL_SHIFT);
msleep(500);
/* disable anti-pop features */
- snd_soc_update_bits(codec, WM8985_OUT4_TO_ADC,
+ snd_soc_component_update_bits(component, WM8985_OUT4_TO_ADC,
WM8985_POBCTRL_MASK, 0);
}
/* VMID at 300k */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1,
WM8985_VMIDSEL_MASK,
2 << WM8985_VMIDSEL_SHIFT);
break;
case SND_SOC_BIAS_OFF:
/* disable thermal shutdown */
- snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0,
WM8985_TSOPCTRL_MASK, 0);
- snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0,
WM8985_TSDEN_MASK, 0);
/* disable VMIDSEL and BIASEN */
- snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_1, 0);
+ snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_2, 0);
+ snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_3, 0);
regcache_mark_dirty(wm8985->regmap);
@@ -960,45 +1024,47 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int wm8985_probe(struct snd_soc_codec *codec)
+static int wm8985_probe(struct snd_soc_component *component)
{
size_t i;
struct wm8985_priv *wm8985;
int ret;
- wm8985 = snd_soc_codec_get_drvdata(codec);
+ wm8985 = snd_soc_component_get_drvdata(component);
for (i = 0; i < ARRAY_SIZE(wm8985->supplies); i++)
wm8985->supplies[i].supply = wm8985_supply_names[i];
- ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8985->supplies),
+ ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(wm8985->supplies),
wm8985->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
wm8985->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- ret = wm8985_reset(codec);
+ ret = wm8985_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
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],
+ snd_soc_component_update_bits(component, volume_update_regs[i],
0x100, 0x100);
/* enable BIASCUT */
- snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
+ snd_soc_component_update_bits(component, WM8985_BIAS_CTRL, WM8985_BIASCUT,
WM8985_BIASCUT);
+ wm8985_add_widgets(component);
+
return 0;
err_reg_enable:
@@ -1007,11 +1073,12 @@ err_reg_enable:
}
static const struct snd_soc_dai_ops wm8985_dai_ops = {
- .digital_mute = wm8985_dac_mute,
+ .mute_stream = wm8985_dac_mute,
.hw_params = wm8985_hw_params,
.set_fmt = wm8985_set_fmt,
.set_sysclk = wm8985_set_sysclk,
- .set_pll = wm8985_set_pll
+ .set_pll = wm8985_set_pll,
+ .no_capture_mute = 1,
};
#define WM8985_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -1034,20 +1101,22 @@ static struct snd_soc_dai_driver wm8985_dai = {
.formats = WM8985_FORMATS,
},
.ops = &wm8985_dai_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 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 snd_soc_component_driver soc_component_dev_wm8985 = {
+ .probe = wm8985_probe,
+ .set_bias_level = wm8985_set_bias_level,
+ .controls = wm8985_common_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8985_common_snd_controls),
+ .dapm_widgets = wm8985_common_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8985_common_dapm_widgets),
+ .dapm_routes = wm8985_common_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8985_common_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8985_regmap = {
@@ -1057,7 +1126,7 @@ static const struct regmap_config wm8985_regmap = {
.max_register = WM8985_MAX_REGISTER,
.writeable_reg = wm8985_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8985_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8985_reg_defaults),
};
@@ -1074,6 +1143,8 @@ static int wm8985_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8985);
+ wm8985->dev_type = WM8985;
+
wm8985->regmap = devm_regmap_init_spi(spi, &wm8985_regmap);
if (IS_ERR(wm8985->regmap)) {
ret = PTR_ERR(wm8985->regmap);
@@ -1082,29 +1153,22 @@ static int wm8985_spi_probe(struct spi_device *spi)
return ret;
}
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8985, &wm8985_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
},
.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)
+
+static int wm8985_i2c_probe(struct i2c_client *i2c)
{
struct wm8985_priv *wm8985;
int ret;
@@ -1115,6 +1179,8 @@ static int wm8985_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8985);
+ wm8985->dev_type = (uintptr_t)i2c_get_match_data(i2c);
+
wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
if (IS_ERR(wm8985->regmap)) {
ret = PTR_ERR(wm8985->regmap);
@@ -1123,19 +1189,14 @@ static int wm8985_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8985, &wm8985_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8985", WM8985 },
+ { "wm8758", WM8758 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
@@ -1145,7 +1206,6 @@ static struct i2c_driver wm8985_i2c_driver = {
.name = "wm8985",
},
.probe = wm8985_i2c_probe,
- .remove = wm8985_i2c_remove,
.id_table = wm8985_i2c_id
};
#endif
@@ -1183,6 +1243,6 @@ static void __exit wm8985_exit(void)
}
module_exit(wm8985_exit);
-MODULE_DESCRIPTION("ASoC WM8985 driver");
+MODULE_DESCRIPTION("ASoC WM8985 / WM8758 driver");
MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8985.h b/sound/soc/codecs/wm8985.h
index 2e71ff507638..107fae9ce014 100644
--- a/sound/soc/codecs/wm8985.h
+++ b/sound/soc/codecs/wm8985.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -290,6 +287,9 @@
#define WM8985_GPIO1GPD_MASK 0x0040 /* GPIO1GPD */
#define WM8985_GPIO1GPD_SHIFT 6 /* GPIO1GPD */
#define WM8985_GPIO1GPD_WIDTH 1 /* GPIO1GPD */
+#define WM8758_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [1:0] */
+#define WM8758_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [1:0] */
+#define WM8758_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [1:0] */
#define WM8985_GPIO1POL 0x0008 /* GPIO1POL */
#define WM8985_GPIO1POL_MASK 0x0008 /* GPIO1POL */
#define WM8985_GPIO1POL_SHIFT 3 /* GPIO1POL */
@@ -301,6 +301,12 @@
/*
* R9 (0x09) - Jack Detect Control 1
*/
+#define WM8758_JD_VMID1_MASK 0x0100 /* JD_VMID1 */
+#define WM8758_JD_VMID1_SHIFT 8 /* JD_VMID1 */
+#define WM8758_JD_VMID1_WIDTH 1 /* JD_VMID1 */
+#define WM8758_JD_VMID0_MASK 0x0080 /* JD_VMID0 */
+#define WM8758_JD_VMID0_SHIFT 7 /* JD_VMID0 */
+#define WM8758_JD_VMID0_WIDTH 1 /* JD_VMID0 */
#define WM8985_JD_EN 0x0040 /* JD_EN */
#define WM8985_JD_EN_MASK 0x0040 /* JD_EN */
#define WM8985_JD_EN_SHIFT 6 /* JD_EN */
@@ -649,6 +655,12 @@
#define WM8985_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */
#define WM8985_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */
#define WM8985_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */
+#define WM8758_VMIDTOG_MASK 0x0010 /* VMIDTOG */
+#define WM8758_VMIDTOG_SHIFT 4 /* VMIDTOG */
+#define WM8758_VMIDTOG_WIDTH 1 /* VMIDTOG */
+#define WM8758_OUT2DEL_MASK 0x0008 /* OUT2DEL */
+#define WM8758_OUT2DEL_SHIFT 3 /* OUT2DEL */
+#define WM8758_OUT2DEL_WIDTH 1 /* OUT2DEL */
#define WM8985_POBCTRL 0x0004 /* POBCTRL */
#define WM8985_POBCTRL_MASK 0x0004 /* POBCTRL */
#define WM8985_POBCTRL_SHIFT 2 /* POBCTRL */
@@ -684,6 +696,9 @@
#define WM8985_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */
#define WM8985_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */
#define WM8985_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */
+#define WM8758_DELEN2_MASK 0x0004 /* DELEN2 */
+#define WM8758_DELEN2_SHIFT 2 /* DELEN2 */
+#define WM8758_DELEN2_WIDTH 1 /* DELEN2 */
#define WM8985_BEEPEN 0x0001 /* BEEPEN */
#define WM8985_BEEPEN_MASK 0x0001 /* BEEPEN */
#define WM8985_BEEPEN_SHIFT 0 /* BEEPEN */
@@ -790,6 +805,14 @@
/*
* R49 (0x31) - Output ctrl
*/
+#define WM8758_HP_COM 0x0100 /* HP_COM */
+#define WM8758_HP_COM_MASK 0x0100 /* HP_COM */
+#define WM8758_HP_COM_SHIFT 8 /* HP_COM */
+#define WM8758_HP_COM_WIDTH 1 /* HP_COM */
+#define WM8758_LINE_COM 0x0080 /* LINE_COM */
+#define WM8758_LINE_COM_MASK 0x0080 /* LINE_COM */
+#define WM8758_LINE_COM_SHIFT 7 /* LINE_COM */
+#define WM8758_LINE_COM_WIDTH 1 /* LINE_COM */
#define WM8985_DACL2RMIX 0x0040 /* DACL2RMIX */
#define WM8985_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */
#define WM8985_DACL2RMIX_SHIFT 6 /* DACL2RMIX */
@@ -806,6 +829,14 @@
#define WM8985_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */
#define WM8985_OUT3BOOST_SHIFT 3 /* OUT3BOOST */
#define WM8985_OUT3BOOST_WIDTH 1 /* OUT3BOOST */
+#define WM8758_OUT4ENDEL 0x0010 /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_MASK 0x0010 /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_SHIFT 4 /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_WIDTH 1 /* OUT4ENDEL */
+#define WM8758_OUT3ENDEL 0x0008 /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_MASK 0x0008 /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_SHIFT 3 /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_WIDTH 1 /* OUT3ENDEL */
#define WM8985_TSOPCTRL 0x0004 /* TSOPCTRL */
#define WM8985_TSOPCTRL_MASK 0x0004 /* TSOPCTRL */
#define WM8985_TSOPCTRL_SHIFT 2 /* TSOPCTRL */
@@ -1021,6 +1052,10 @@
#define WM8985_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */
#define WM8985_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */
#define WM8985_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */
+#define WM8758_HALFIPBIAS 0x0040 /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_MASK 0x0040 /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_SHIFT 6 /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_WIDTH 1 /* HALFI_IPGA */
#define WM8985_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */
#define WM8985_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */
#define WM8985_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 895721a256f0..9bffe7a6ccec 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8988.c -- WM8988 ALSA SoC audio driver
*
@@ -5,10 +6,6 @@
* 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>
@@ -119,7 +116,7 @@ struct wm8988_priv {
const struct snd_pcm_hw_constraint_list *sysclk_constraints;
};
-#define wm8988_reset(c) snd_soc_write(c, WM8988_RESET, 0)
+#define wm8988_reset(c) snd_soc_component_write(c, WM8988_RESET, 0)
/*
* WM8988 Controls
@@ -244,16 +241,16 @@ SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 adctl2 = snd_soc_component_read(component, WM8988_ADCTL2);
/* Use the DAC to gate LRC if active, otherwise use ADC */
- if (snd_soc_read(codec, WM8988_PWR2) & 0x180)
+ if (snd_soc_component_read(component, WM8988_PWR2) & 0x180)
adctl2 &= ~0x4;
else
adctl2 |= 0x4;
- return snd_soc_write(codec, WM8988_ADCTL2, adctl2);
+ return snd_soc_component_write(component, WM8988_ADCTL2, adctl2);
}
static const char *wm8988_line_texts[] = {
@@ -276,7 +273,7 @@ static const struct soc_enum wm8988_rline_enum =
wm8988_line_texts,
wm8988_line_values);
static const struct snd_kcontrol_new wm8988_right_line_controls =
- SOC_DAPM_ENUM("Route", wm8988_lline_enum);
+ SOC_DAPM_ENUM("Route", wm8988_rline_enum);
/* Left Mixer */
static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
@@ -522,7 +519,7 @@ static inline int get_coeff(int mclk, int rate)
/* 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,
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
};
static const struct snd_pcm_hw_constraint_list constraints_12288 = {
@@ -540,7 +537,7 @@ static const struct snd_pcm_hw_constraint_list constraints_112896 = {
};
static const unsigned int rates_12[] = {
- 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 41100, 48000,
48000, 88235, 96000,
};
@@ -555,8 +552,8 @@ static const struct snd_pcm_hw_constraint_list constraints_12 = {
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 11289600:
@@ -587,15 +584,15 @@ static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -638,21 +635,21 @@ static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8988_IFACE, iface);
+ snd_soc_component_write(component, WM8988_IFACE, iface);
return 0;
}
static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
if (!wm8988->sysclk) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"No MCLK configured, call set_sysclk() on init\n");
return -EINVAL;
}
@@ -668,10 +665,10 @@ 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;
+ struct snd_soc_component *component = dai->component;
+ struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component);
+ u16 iface = snd_soc_component_read(component, WM8988_IFACE) & 0x1f3;
+ u16 srate = snd_soc_component_read(component, WM8988_SRATE) & 0x180;
int coeff;
coeff = get_coeff(wm8988->sysclk, params_rate(params));
@@ -680,7 +677,7 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
srate |= 0x40;
}
if (coeff < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Unable to configure sample rate %dHz with %dHz MCLK\n",
params_rate(params), wm8988->sysclk);
return coeff;
@@ -702,31 +699,32 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, WM8988_IFACE, iface);
+ snd_soc_component_write(component, WM8988_IFACE, iface);
if (coeff >= 0)
- snd_soc_write(codec, WM8988_SRATE, srate |
+ snd_soc_component_write(component, WM8988_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
}
-static int wm8988_mute(struct snd_soc_dai *dai, int mute)
+static int wm8988_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, WM8988_ADCDAC) & 0xfff7;
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, WM8988_ADCDAC) & 0xfff7;
if (mute)
- snd_soc_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
+ snd_soc_component_write(component, WM8988_ADCDAC, mute_reg | 0x8);
else
- snd_soc_write(codec, WM8988_ADCDAC, mute_reg);
+ snd_soc_component_write(component, WM8988_ADCDAC, mute_reg);
return 0;
}
-static int wm8988_set_bias_level(struct snd_soc_codec *codec,
+static int wm8988_set_bias_level(struct snd_soc_component *component,
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;
+ struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ u16 pwr_reg = snd_soc_component_read(component, WM8988_PWR1) & ~0x1c1;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -734,26 +732,26 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
+ snd_soc_component_write(component, WM8988_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
regcache_sync(wm8988->regmap);
/* VREF, VMID=2x5k */
- snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
+ snd_soc_component_write(component, WM8988_PWR1, pwr_reg | 0x1c1);
/* Charge caps */
msleep(100);
}
/* VREF, VMID=2*500k, digital stopped */
- snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
+ snd_soc_component_write(component, WM8988_PWR1, pwr_reg | 0x0141);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, WM8988_PWR1, 0x0000);
+ snd_soc_component_write(component, WM8988_PWR1, 0x0000);
break;
}
return 0;
@@ -769,7 +767,8 @@ static const struct snd_soc_dai_ops wm8988_ops = {
.hw_params = wm8988_pcm_hw_params,
.set_fmt = wm8988_set_dai_fmt,
.set_sysclk = wm8988_set_dai_sysclk,
- .digital_mute = wm8988_mute,
+ .mute_stream = wm8988_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8988_dai = {
@@ -789,40 +788,42 @@ static struct snd_soc_dai_driver wm8988_dai = {
.formats = WM8988_FORMATS,
},
.ops = &wm8988_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8988_probe(struct snd_soc_codec *codec)
+static int wm8988_probe(struct snd_soc_component *component)
{
int ret = 0;
- ret = wm8988_reset(codec);
+ ret = wm8988_reset(component);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
+ dev_err(component->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);
+ snd_soc_component_update_bits(component, WM8988_RADC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8988_RDAC, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8988_ROUT1V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8988_ROUT2V, 0x0100, 0x0100);
+ snd_soc_component_update_bits(component, WM8988_RINVOL, 0x0100, 0x0100);
return 0;
}
-static const struct snd_soc_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 snd_soc_component_driver soc_component_dev_wm8988 = {
+ .probe = wm8988_probe,
+ .set_bias_level = wm8988_set_bias_level,
+ .controls = wm8988_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8988_snd_controls),
+ .dapm_widgets = wm8988_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8988_dapm_widgets),
+ .dapm_routes = wm8988_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8988_regmap = {
@@ -832,7 +833,7 @@ static const struct regmap_config wm8988_regmap = {
.max_register = WM8988_LPPB,
.writeable_reg = wm8988_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8988_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8988_reg_defaults),
};
@@ -857,29 +858,21 @@ static int wm8988_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8988);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8988, &wm8988_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
},
.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)
+static int wm8988_i2c_probe(struct i2c_client *i2c)
{
struct wm8988_priv *wm8988;
int ret;
@@ -898,19 +891,13 @@ static int wm8988_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8988, &wm8988_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8988" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
@@ -919,8 +906,7 @@ static struct i2c_driver wm8988_i2c_driver = {
.driver = {
.name = "wm8988",
},
- .probe = wm8988_i2c_probe,
- .remove = wm8988_i2c_remove,
+ .probe = wm8988_i2c_probe,
.id_table = wm8988_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8988.h b/sound/soc/codecs/wm8988.h
index 5c04024e5f9f..bd8a30c1340f 100644
--- a/sound/soc/codecs/wm8988.h
+++ b/sound/soc/codecs/wm8988.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 23ecd30d8bca..9f2b42025ec9 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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>
@@ -36,84 +32,7 @@ struct wm8990_priv {
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);
+#define wm8990_reset(c) snd_soc_component_write(c, WM8990_RESET, 0)
static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
@@ -121,8 +40,6 @@ 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);
@@ -132,7 +49,7 @@ 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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int reg = mc->reg;
@@ -144,8 +61,8 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
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);
+ val = snd_soc_component_read(component, reg);
+ return snd_soc_component_write(component, reg, val | 0x0100);
}
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
@@ -374,14 +291,14 @@ SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
u32 reg_shift = kcontrol->private_value & 0xfff;
int ret = 0;
u16 reg;
switch (reg_shift) {
case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
- reg = snd_soc_read(codec, WM8990_OUTPUT_MIXER1);
+ reg = snd_soc_component_read(component, WM8990_OUTPUT_MIXER1);
if (reg & WM8990_LDLO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 1 LDLO Set\n");
@@ -389,7 +306,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
break;
case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
- reg = snd_soc_read(codec, WM8990_OUTPUT_MIXER2);
+ reg = snd_soc_component_read(component, WM8990_OUTPUT_MIXER2);
if (reg & WM8990_RDRO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 2 RDRO Set\n");
@@ -397,7 +314,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
break;
case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
- reg = snd_soc_read(codec, WM8990_SPEAKER_MIXER);
+ reg = snd_soc_component_read(component, WM8990_SPEAKER_MIXER);
if (reg & WM8990_LDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer LDSPK Set\n");
@@ -405,7 +322,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
break;
case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
- reg = snd_soc_read(codec, WM8990_SPEAKER_MIXER);
+ reg = snd_soc_component_read(component, WM8990_SPEAKER_MIXER);
if (reg & WM8990_RDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer RDSPK Set\n");
@@ -490,14 +407,6 @@ static SOC_ENUM_SINGLE_DECL(wm8990_ainrmux_enum,
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,
@@ -934,28 +843,28 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
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 snd_soc_component *component = codec_dai->component;
struct _pll_div pll_div;
if (freq_in && freq_out) {
pll_factors(&pll_div, freq_out * 4, freq_in);
/* Turn on PLL */
- snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_2,
WM8990_PLL_ENA, WM8990_PLL_ENA);
/* sysclk comes from PLL */
- snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ snd_soc_component_update_bits(component, WM8990_CLOCKING_2,
WM8990_SYSCLK_SRC, WM8990_SYSCLK_SRC);
/* set up N , fractional mode and pre-divisor if necessary */
- snd_soc_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+ snd_soc_component_write(component, 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));
+ snd_soc_component_write(component, WM8990_PLL2, (u8)(pll_div.k>>8));
+ snd_soc_component_write(component, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
} else {
/* Turn off PLL */
- snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_2,
WM8990_PLL_ENA, 0);
}
return 0;
@@ -967,8 +876,8 @@ static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8990_priv *wm8990 = snd_soc_component_get_drvdata(component);
wm8990->sysclk = freq;
return 0;
@@ -980,18 +889,18 @@ static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 audio1, audio3;
- audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
- audio3 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_3);
+ audio1 = snd_soc_component_read(component, WM8990_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_component_read(component, WM8990_AUDIO_INTERFACE_3);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
audio3 &= ~WM8990_AIF_MSTR1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
audio3 |= WM8990_AIF_MSTR1;
break;
default:
@@ -1025,31 +934,31 @@ static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
- snd_soc_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+ snd_soc_component_write(component, WM8990_AUDIO_INTERFACE_1, audio1);
+ snd_soc_component_write(component, WM8990_AUDIO_INTERFACE_3, audio3);
return 0;
}
static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
switch (div_id) {
case WM8990_MCLK_DIV:
- snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ snd_soc_component_update_bits(component, WM8990_CLOCKING_2,
WM8990_MCLK_DIV_MASK, div);
break;
case WM8990_DACCLK_DIV:
- snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ snd_soc_component_update_bits(component, WM8990_CLOCKING_2,
WM8990_DAC_CLKDIV_MASK, div);
break;
case WM8990_ADCCLK_DIV:
- snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ snd_soc_component_update_bits(component, WM8990_CLOCKING_2,
WM8990_ADC_CLKDIV_MASK, div);
break;
case WM8990_BCLK_DIV:
- snd_soc_update_bits(codec, WM8990_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8990_CLOCKING_1,
WM8990_BCLK_DIV_MASK, div);
break;
default:
@@ -1066,8 +975,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ u16 audio1 = snd_soc_component_read(component, WM8990_AUDIO_INTERFACE_1);
audio1 &= ~WM8990_AIF_WL_MASK;
/* bit size */
@@ -1085,29 +994,30 @@ static int wm8990_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ snd_soc_component_write(component, WM8990_AUDIO_INTERFACE_1, audio1);
return 0;
}
-static int wm8990_mute(struct snd_soc_dai *dai, int mute)
+static int wm8990_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 val;
- val = snd_soc_read(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+ val = snd_soc_component_read(component, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
if (mute)
- snd_soc_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+ snd_soc_component_write(component, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
else
- snd_soc_write(codec, WM8990_DAC_CTRL, val);
+ snd_soc_component_write(component, WM8990_DAC_CTRL, val);
return 0;
}
-static int wm8990_set_bias_level(struct snd_soc_codec *codec,
+static int wm8990_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8990_priv *wm8990 = snd_soc_codec_get_drvdata(codec);
+ struct wm8990_priv *wm8990 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -1116,26 +1026,26 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*50k */
- snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_1,
WM8990_VMID_MODE_MASK, 0x2);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(wm8990->regmap);
if (ret < 0) {
- dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
return ret;
}
/* Enable all output discharge bits */
- snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ snd_soc_component_write(component, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
WM8990_DIS_ROUT);
/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
- snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL |
WM8990_VMIDTOG);
@@ -1143,82 +1053,82 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
msleep(300);
/* Disable VMIDTOG */
- snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL);
/* disable all output discharge bits */
- snd_soc_write(codec, WM8990_ANTIPOP1, 0);
+ snd_soc_component_write(component, WM8990_ANTIPOP1, 0);
/* Enable outputs */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1b00);
msleep(50);
/* Enable VMID at 2x50k */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f02);
msleep(100);
/* Enable VREF */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f03);
msleep(600);
/* Enable BUFIOEN */
- snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL |
WM8990_BUFIOEN);
/* Disable outputs */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x3);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM8990_EXT_ACCESS_ENA, 0x2);
+ snd_soc_component_write(component, WM8990_EXT_CTL1, 0xa003);
+ snd_soc_component_write(component, WM8990_EXT_ACCESS_ENA, 0);
}
/* VMID=2*250k */
- snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_1,
WM8990_VMID_MODE_MASK, 0x4);
break;
case SND_SOC_BIAS_OFF:
/* Enable POBCTRL and SOFT_ST */
- snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_POBCTRL | WM8990_BUFIOEN);
/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL |
WM8990_BUFIOEN);
/* mute DAC */
- snd_soc_update_bits(codec, WM8990_DAC_CTRL,
+ snd_soc_component_update_bits(component, WM8990_DAC_CTRL,
WM8990_DAC_MUTE, WM8990_DAC_MUTE);
/* Enable any disabled outputs */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f03);
/* Disable VMID */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f01);
msleep(300);
/* Enable all output discharge bits */
- snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ snd_soc_component_write(component, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
WM8990_DIS_ROUT);
/* Disable VREF */
- snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+ snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x0);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8990_ANTIPOP2, 0x0);
+ snd_soc_component_write(component, WM8990_ANTIPOP2, 0x0);
regcache_mark_dirty(wm8990->regmap);
break;
@@ -1243,11 +1153,12 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
*/
static const struct snd_soc_dai_ops wm8990_dai_ops = {
.hw_params = wm8990_hw_params,
- .digital_mute = wm8990_mute,
+ .mute_stream = wm8990_mute,
.set_fmt = wm8990_set_dai_fmt,
.set_clkdiv = wm8990_set_dai_clkdiv,
.set_pll = wm8990_set_dai_pll,
.set_sysclk = wm8990_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver wm8990_dai = {
@@ -1272,54 +1183,46 @@ static struct snd_soc_dai_driver wm8990_dai = {
* initialise the WM8990 driver
* register the mixer and dsp interfaces with the kernel
*/
-static int wm8990_probe(struct snd_soc_codec *codec)
+static int wm8990_probe(struct snd_soc_component *component)
{
- wm8990_reset(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+
+ wm8990_reset(component);
/* charge output caps */
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
- snd_soc_update_bits(codec, WM8990_AUDIO_INTERFACE_4,
+ snd_soc_component_update_bits(component, WM8990_AUDIO_INTERFACE_4,
WM8990_ALRCGPIO1, WM8990_ALRCGPIO1);
- snd_soc_update_bits(codec, WM8990_GPIO1_GPIO2,
+ snd_soc_component_update_bits(component, WM8990_GPIO1_GPIO2,
WM8990_GPIO1_SEL_MASK, 1);
- snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, 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));
+ snd_soc_component_write(component, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_component_write(component, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
return 0;
}
-static 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 const struct snd_soc_component_driver soc_component_dev_wm8990 = {
+ .probe = wm8990_probe,
+ .set_bias_level = wm8990_set_bias_level,
+ .controls = wm8990_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8990_snd_controls),
+ .dapm_widgets = wm8990_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets),
+ .dapm_routes = wm8990_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int wm8990_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8990_i2c_probe(struct i2c_client *i2c)
{
struct wm8990_priv *wm8990;
int ret;
@@ -1331,21 +1234,14 @@ static int wm8990_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8990);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8990, &wm8990_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8990" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);
@@ -1354,8 +1250,7 @@ static struct i2c_driver wm8990_i2c_driver = {
.driver = {
.name = "wm8990",
},
- .probe = wm8990_i2c_probe,
- .remove = wm8990_i2c_remove,
+ .probe = wm8990_i2c_probe,
.id_table = wm8990_i2c_id,
};
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
index 0e9c78040c4c..315edc4b4432 100644
--- a/sound/soc/codecs/wm8990.h
+++ b/sound/soc/codecs/wm8990.h
@@ -1,15 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* wm8990.h -- audio driver for WM8990
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * 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__
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index c9ee0ac6a654..b8ed2a3e699b 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* wm8991.c -- WM8991 ALSA Soc Audio driver
*
* Copyright 2007-2010 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* Graeme.Gregory@wolfsonmicro.com
- *
- * 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>
@@ -111,19 +107,29 @@ static bool wm8991_volatile(struct device *dev, unsigned int reg)
}
}
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
-static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
-static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
-static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
-static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
-static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
-static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
-static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_pga_tlv, -1650, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(out_mix_tlv, -2100, 300, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_pga_tlv,
+ 0x00, 0x2f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(SNDRV_CTL_TLVD_DB_GAIN_MUTE, 0, 1),
+ 0x30, 0x7f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-7300, 100, 0),
+);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_dac_tlv,
+ 0x00, 0xbf, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-71625, 375, 1),
+ 0xc0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0),
+);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(in_adc_tlv,
+ 0x00, 0xef, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-71625, 375, 1),
+ 0xf0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(17625, 0, 0),
+);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_sidetone_tlv,
+ 0x00, 0x0c, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3600, 300, 0),
+ 0x0d, 0x0f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0),
+);
static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int reg = kcontrol->private_value & 0xff;
int ret;
u16 val;
@@ -133,8 +139,8 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
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);
+ val = snd_soc_component_read(component, reg);
+ return snd_soc_component_write(component, reg, val | 0x0100);
}
static const char *wm8991_digital_sidetone[] =
@@ -351,14 +357,14 @@ static const struct snd_kcontrol_new wm8991_snd_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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
u32 reg_shift = kcontrol->private_value & 0xfff;
int ret = 0;
u16 reg;
switch (reg_shift) {
case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
- reg = snd_soc_read(codec, WM8991_OUTPUT_MIXER1);
+ reg = snd_soc_component_read(component, WM8991_OUTPUT_MIXER1);
if (reg & WM8991_LDLO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 1 LDLO Set\n");
@@ -367,7 +373,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
break;
case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
- reg = snd_soc_read(codec, WM8991_OUTPUT_MIXER2);
+ reg = snd_soc_component_read(component, WM8991_OUTPUT_MIXER2);
if (reg & WM8991_RDRO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 2 RDRO Set\n");
@@ -376,7 +382,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
break;
case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
- reg = snd_soc_read(codec, WM8991_SPEAKER_MIXER);
+ reg = snd_soc_component_read(component, WM8991_SPEAKER_MIXER);
if (reg & WM8991_LDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer LDSPK Set\n");
@@ -385,7 +391,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
break;
case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
- reg = snd_soc_read(codec, WM8991_SPEAKER_MIXER);
+ reg = snd_soc_component_read(component, WM8991_SPEAKER_MIXER);
if (reg & WM8991_RDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer RDSPK Set\n");
@@ -398,7 +404,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
/* INMIX dB values */
-static const DECLARE_TLV_DB_LINEAR(in_mix_tlv, -1200, 600);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_mix_tlv, -1200, 300, 1);
/* Left In PGA Connections */
static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
@@ -470,14 +476,6 @@ static SOC_ENUM_SINGLE_DECL(wm8991_ainrmux_enum,
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,
@@ -917,31 +915,31 @@ 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 snd_soc_component *component = codec_dai->component;
struct _pll_div pll_div;
if (freq_in && freq_out) {
pll_factors(&pll_div, freq_out * 4, freq_in);
/* Turn on PLL */
- reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ reg = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_2);
reg |= WM8991_PLL_ENA;
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+ snd_soc_component_write(component, 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);
+ reg = snd_soc_component_read(component, WM8991_CLOCKING_2);
+ snd_soc_component_write(component, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);
/* set up N , fractional mode and pre-divisor if necessary */
- snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM |
+ snd_soc_component_write(component, 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));
+ snd_soc_component_write(component, WM8991_PLL2, (u8)(pll_div.k>>8));
+ snd_soc_component_write(component, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
} else {
/* Turn on PLL */
- reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ reg = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_2);
reg &= ~WM8991_PLL_ENA;
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_2, reg);
}
return 0;
}
@@ -952,18 +950,18 @@ static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 audio1, audio3;
- audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
- audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3);
+ audio1 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_3);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
audio3 &= ~WM8991_AIF_MSTR1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
audio3 |= WM8991_AIF_MSTR1;
break;
default:
@@ -997,37 +995,37 @@ static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
- snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3);
+ snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_1, audio1);
+ snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_3, audio3);
return 0;
}
static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u16 reg;
switch (div_id) {
case WM8991_MCLK_DIV:
- reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ reg = snd_soc_component_read(component, WM8991_CLOCKING_2) &
~WM8991_MCLK_DIV_MASK;
- snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div);
break;
case WM8991_DACCLK_DIV:
- reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ reg = snd_soc_component_read(component, WM8991_CLOCKING_2) &
~WM8991_DAC_CLKDIV_MASK;
- snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div);
break;
case WM8991_ADCCLK_DIV:
- reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ reg = snd_soc_component_read(component, WM8991_CLOCKING_2) &
~WM8991_ADC_CLKDIV_MASK;
- snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div);
break;
case WM8991_BCLK_DIV:
- reg = snd_soc_read(codec, WM8991_CLOCKING_1) &
+ reg = snd_soc_component_read(component, WM8991_CLOCKING_1) &
~WM8991_BCLK_DIV_MASK;
- snd_soc_write(codec, WM8991_CLOCKING_1, reg | div);
+ snd_soc_component_write(component, WM8991_CLOCKING_1, reg | div);
break;
default:
return -EINVAL;
@@ -1043,8 +1041,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ u16 audio1 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_1);
audio1 &= ~WM8991_AIF_WL_MASK;
/* bit size */
@@ -1062,27 +1060,28 @@ static int wm8991_hw_params(struct snd_pcm_substream *substream,
break;
}
- snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+ snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_1, audio1);
return 0;
}
-static int wm8991_mute(struct snd_soc_dai *dai, int mute)
+static int wm8991_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 val;
- val = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
+ val = snd_soc_component_read(component, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
if (mute)
- snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+ snd_soc_component_write(component, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
else
- snd_soc_write(codec, WM8991_DAC_CTRL, val);
+ snd_soc_component_write(component, WM8991_DAC_CTRL, val);
return 0;
}
-static int wm8991_set_bias_level(struct snd_soc_codec *codec,
+static int wm8991_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8991_priv *wm8991 = snd_soc_codec_get_drvdata(codec);
+ struct wm8991_priv *wm8991 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u16 val;
switch (level) {
@@ -1091,22 +1090,22 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*50k */
- val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+ val = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_1) &
~WM8991_VMID_MODE_MASK;
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, val | 0x2);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
regcache_sync(wm8991->regmap);
/* Enable all output discharge bits */
- snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+ snd_soc_component_write(component, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
WM8991_DIS_ROUT);
/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
- snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
WM8991_BUFDCOPEN | WM8991_POBCTRL |
WM8991_VMIDTOG);
@@ -1114,78 +1113,78 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
msleep(300);
/* Disable VMIDTOG */
- snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
WM8991_BUFDCOPEN | WM8991_POBCTRL);
/* disable all output discharge bits */
- snd_soc_write(codec, WM8991_ANTIPOP1, 0);
+ snd_soc_component_write(component, WM8991_ANTIPOP1, 0);
/* Enable outputs */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1b00);
msleep(50);
/* Enable VMID at 2x50k */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f02);
msleep(100);
/* Enable VREF */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f03);
msleep(600);
/* Enable BUFIOEN */
- snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
WM8991_BUFDCOPEN | WM8991_POBCTRL |
WM8991_BUFIOEN);
/* Disable outputs */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x3);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN);
+ snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_BUFIOEN);
}
/* VMID=2*250k */
- val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+ val = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_1) &
~WM8991_VMID_MODE_MASK;
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4);
+ snd_soc_component_write(component, 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 |
+ snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST |
WM8991_POBCTRL | WM8991_BUFIOEN);
/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ snd_soc_component_write(component, 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);
+ val = snd_soc_component_read(component, WM8991_DAC_CTRL);
+ snd_soc_component_write(component, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
/* Enable any disabled outputs */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f03);
/* Disable VMID */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f01);
msleep(300);
/* Enable all output discharge bits */
- snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+ snd_soc_component_write(component, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
WM8991_DIS_ROUT);
/* Disable VREF */
- snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0);
+ snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x0);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
+ snd_soc_component_write(component, WM8991_ANTIPOP2, 0x0);
regcache_mark_dirty(wm8991->regmap);
break;
}
@@ -1198,10 +1197,11 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops wm8991_ops = {
.hw_params = wm8991_hw_params,
- .digital_mute = wm8991_mute,
+ .mute_stream = wm8991_mute,
.set_fmt = wm8991_set_dai_fmt,
.set_clkdiv = wm8991_set_dai_clkdiv,
- .set_pll = wm8991_set_dai_pll
+ .set_pll = wm8991_set_dai_pll,
+ .no_capture_mute = 1,
};
/*
@@ -1232,16 +1232,18 @@ static struct snd_soc_dai_driver wm8991_dai = {
.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 snd_soc_component_driver soc_component_dev_wm8991 = {
+ .set_bias_level = wm8991_set_bias_level,
+ .controls = wm8991_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8991_snd_controls),
+ .dapm_widgets = wm8991_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets),
+ .dapm_routes = wm8991_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8991_regmap = {
@@ -1252,11 +1254,10 @@ static const struct regmap_config wm8991_regmap = {
.volatile_reg = wm8991_volatile,
.reg_defaults = wm8991_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int wm8991_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8991_i2c_probe(struct i2c_client *i2c)
{
struct wm8991_priv *wm8991;
unsigned int val;
@@ -1307,21 +1308,14 @@ static int wm8991_i2c_probe(struct i2c_client *i2c,
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);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 },
+ { "wm8991" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
@@ -1331,7 +1325,6 @@ static struct i2c_driver wm8991_i2c_driver = {
.name = "wm8991",
},
.probe = wm8991_i2c_probe,
- .remove = wm8991_i2c_remove,
.id_table = wm8991_i2c_id,
};
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
index 08ed383303c0..8686f01d557f 100644
--- a/sound/soc/codecs/wm8991.h
+++ b/sound/soc/codecs/wm8991.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* wm8991.h -- audio driver for WM8991
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * 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
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 8668c4c391b0..1c9299979898 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -466,14 +463,14 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+static int _wm8993_set_fll(struct snd_soc_component *component, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
u16 reg1, reg4, reg5;
struct _fll_div fll_div;
- unsigned int timeout;
+ unsigned long time_left;
int ret;
/* Any change? */
@@ -482,13 +479,13 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
/* Disable the FLL */
if (Fout == 0) {
- dev_dbg(codec->dev, "FLL disabled\n");
+ dev_dbg(component->dev, "FLL disabled\n");
wm8993->fll_fref = 0;
wm8993->fll_fout = 0;
- reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 = snd_soc_component_read(component, WM8993_FLL_CONTROL_1);
reg1 &= ~WM8993_FLL_ENA;
- snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1);
return 0;
}
@@ -497,7 +494,7 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
if (ret != 0)
return ret;
- reg5 = snd_soc_read(codec, WM8993_FLL_CONTROL_5);
+ reg5 = snd_soc_component_read(component, WM8993_FLL_CONTROL_5);
reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
switch (fll_id) {
@@ -513,55 +510,55 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
break;
default:
- dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ dev_err(component->dev, "Unknown FLL ID %d\n", fll_id);
return -EINVAL;
}
/* Any FLL configuration change requires that the FLL be
* disabled first. */
- reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 = snd_soc_component_read(component, WM8993_FLL_CONTROL_1);
reg1 &= ~WM8993_FLL_ENA;
- snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1);
/* Apply the configuration */
if (fll_div.k)
reg1 |= WM8993_FLL_FRAC_MASK;
else
reg1 &= ~WM8993_FLL_FRAC_MASK;
- snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1);
- snd_soc_write(codec, WM8993_FLL_CONTROL_2,
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_2,
(fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
(fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
- snd_soc_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_3, fll_div.k);
- reg4 = snd_soc_read(codec, WM8993_FLL_CONTROL_4);
+ reg4 = snd_soc_component_read(component, WM8993_FLL_CONTROL_4);
reg4 &= ~WM8993_FLL_N_MASK;
reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
- snd_soc_write(codec, WM8993_FLL_CONTROL_4, reg4);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_4, reg4);
reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
- snd_soc_write(codec, WM8993_FLL_CONTROL_5, reg5);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_5, reg5);
/* If we've got an interrupt wired up make sure we get it */
if (i2c->irq)
- timeout = msecs_to_jiffies(20);
+ time_left = msecs_to_jiffies(20);
else if (Fref < 1000000)
- timeout = msecs_to_jiffies(3);
+ time_left = msecs_to_jiffies(3);
else
- timeout = msecs_to_jiffies(1);
+ time_left = 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);
+ snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
- timeout = wait_for_completion_timeout(&wm8993->fll_lock, timeout);
- if (i2c->irq && !timeout)
- dev_warn(codec->dev, "Timed out waiting for FLL\n");
+ time_left = wait_for_completion_timeout(&wm8993->fll_lock, time_left);
+ if (i2c->irq && !time_left)
+ dev_warn(component->dev, "Timed out waiting for FLL\n");
- dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+ dev_dbg(component->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
wm8993->fll_fref = Fref;
wm8993->fll_fout = Fout;
@@ -573,20 +570,20 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
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);
+ return _wm8993_set_fll(dai->component, fll_id, source, Fref, Fout);
}
-static int configure_clock(struct snd_soc_codec *codec)
+static int configure_clock(struct snd_soc_component *component)
{
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
unsigned int reg;
/* This should be done on init() for bypass paths */
switch (wm8993->sysclk_source) {
case WM8993_SYSCLK_MCLK:
- dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
+ dev_dbg(component->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
- reg = snd_soc_read(codec, WM8993_CLOCKING_2);
+ reg = snd_soc_component_read(component, WM8993_CLOCKING_2);
reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
if (wm8993->mclk_rate > 13500000) {
reg |= WM8993_MCLK_DIV;
@@ -595,14 +592,14 @@ static int configure_clock(struct snd_soc_codec *codec)
reg &= ~WM8993_MCLK_DIV;
wm8993->sysclk_rate = wm8993->mclk_rate;
}
- snd_soc_write(codec, WM8993_CLOCKING_2, reg);
+ snd_soc_component_write(component, WM8993_CLOCKING_2, reg);
break;
case WM8993_SYSCLK_FLL:
- dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+ dev_dbg(component->dev, "Using %dHz FLL clock\n",
wm8993->fll_fout);
- reg = snd_soc_read(codec, WM8993_CLOCKING_2);
+ reg = snd_soc_component_read(component, WM8993_CLOCKING_2);
reg |= WM8993_SYSCLK_SRC;
if (wm8993->fll_fout > 13500000) {
reg |= WM8993_MCLK_DIV;
@@ -611,15 +608,15 @@ static int configure_clock(struct snd_soc_codec *codec)
reg &= ~WM8993_MCLK_DIV;
wm8993->sysclk_rate = wm8993->fll_fout;
}
- snd_soc_write(codec, WM8993_CLOCKING_2, reg);
+ snd_soc_component_write(component, WM8993_CLOCKING_2, reg);
break;
default:
- dev_err(codec->dev, "System clock not configured\n");
+ dev_err(component->dev, "System clock not configured\n");
return -EINVAL;
}
- dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate);
+ dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate);
return 0;
}
@@ -809,11 +806,11 @@ 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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return configure_clock(codec);
+ return configure_clock(component);
case SND_SOC_DAPM_POST_PMD:
break;
@@ -972,26 +969,27 @@ static const struct snd_soc_dapm_route routes[] = {
{ "Right Headphone Mux", "DAC", "DACR" },
};
-static int wm8993_set_bias_level(struct snd_soc_codec *codec,
+static int wm8993_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
- wm_hubs_set_bias_level(codec, level);
+ wm_hubs_set_bias_level(component, 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,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_VMID_SEL_MASK, 0x2);
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_2,
WM8993_TSHUT_ENA, WM8993_TSHUT_ENA);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
wm8993->supplies);
if (ret != 0)
@@ -1000,10 +998,10 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
regcache_cache_only(wm8993->regmap, false);
regcache_sync(wm8993->regmap);
- wm_hubs_vmid_ena(codec);
+ wm_hubs_vmid_ena(component);
/* Bring up VMID with fast soft start */
- snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ snd_soc_component_update_bits(component, WM8993_ANTIPOP2,
WM8993_STARTUP_BIAS_ENA |
WM8993_VMID_BUF_ENA |
WM8993_VMID_RAMP_MASK |
@@ -1017,40 +1015,40 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
* need the VMID buffer */
if (!wm8993->pdata.lineout1_diff ||
!wm8993->pdata.lineout2_diff)
- snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ snd_soc_component_update_bits(component, WM8993_ANTIPOP1,
WM8993_LINEOUT_VMID_BUF_ENA,
WM8993_LINEOUT_VMID_BUF_ENA);
/* VMID=2*40k */
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_VMID_SEL_MASK |
WM8993_BIAS_ENA,
WM8993_BIAS_ENA | 0x2);
msleep(32);
/* Switch to normal bias */
- snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ snd_soc_component_update_bits(component, WM8993_ANTIPOP2,
WM8993_BIAS_SRC |
WM8993_STARTUP_BIAS_ENA, 0);
}
/* VMID=2*240k */
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_VMID_SEL_MASK, 0x4);
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_2,
WM8993_TSHUT_ENA, 0);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ snd_soc_component_update_bits(component, WM8993_ANTIPOP1,
WM8993_LINEOUT_VMID_BUF_ENA, 0);
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
0);
- snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ snd_soc_component_update_bits(component, WM8993_ANTIPOP2,
WM8993_STARTUP_BIAS_ENA |
WM8993_VMID_BUF_ENA |
WM8993_VMID_RAMP_MASK |
@@ -1070,12 +1068,13 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM8993_SYSCLK_MCLK:
wm8993->mclk_rate = freq;
+ fallthrough;
case WM8993_SYSCLK_FLL:
wm8993->sysclk_source = clk_id;
break;
@@ -1090,28 +1089,28 @@ static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
+ unsigned int aif1 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_1);
+ unsigned int aif4 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_4);
aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
aif4 &= ~WM8993_LRCLK_DIR;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
wm8993->master = 0;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif4 |= WM8993_LRCLK_DIR;
wm8993->master = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif1 |= WM8993_BCLK_DIR;
wm8993->master = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif1 |= WM8993_BCLK_DIR;
aif4 |= WM8993_LRCLK_DIR;
wm8993->master = 1;
@@ -1123,6 +1122,7 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif1 |= WM8993_AIF_LRCLK_INV;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x18;
break;
@@ -1176,8 +1176,8 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
- snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+ snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_1, aif1);
+ snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_4, aif4);
return 0;
}
@@ -1186,28 +1186,28 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
int ret, i, best, best_val, cur_val;
unsigned int clocking1, clocking3, aif1, aif4;
- clocking1 = snd_soc_read(codec, WM8993_CLOCKING_1);
+ clocking1 = snd_soc_component_read(component, WM8993_CLOCKING_1);
clocking1 &= ~WM8993_BCLK_DIV_MASK;
- clocking3 = snd_soc_read(codec, WM8993_CLOCKING_3);
+ clocking3 = snd_soc_component_read(component, WM8993_CLOCKING_3);
clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
- aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
+ aif1 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_1);
aif1 &= ~WM8993_AIF_WL_MASK;
- aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
+ aif4 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_4);
aif4 &= ~WM8993_LRCLK_RATE_MASK;
/* What BCLK do we need? */
wm8993->fs = params_rate(params);
wm8993->bclk = 2 * wm8993->fs;
if (wm8993->tdm_slots) {
- dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
+ dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n",
wm8993->tdm_slots, wm8993->tdm_width);
wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots;
} else {
@@ -1232,9 +1232,9 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
}
}
- dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk);
+ dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8993->bclk);
- ret = configure_clock(codec);
+ ret = configure_clock(component);
if (ret != 0)
return ret;
@@ -1250,7 +1250,7 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
best_val = cur_val;
}
}
- dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n",
clk_sys_rates[best].ratio);
clocking3 |= (clk_sys_rates[best].clk_sys_rate
<< WM8993_CLK_SYS_RATE_SHIFT);
@@ -1266,7 +1266,7 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
best_val = cur_val;
}
}
- dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n",
sample_rates[best].rate);
clocking3 |= (sample_rates[best].sample_rate
<< WM8993_SAMPLE_RATE_SHIFT);
@@ -1285,22 +1285,22 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
}
}
wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div;
- dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
bclk_divs[best].div, wm8993->bclk);
clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT;
/* LRCLK is a simple fraction of BCLK */
- dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
+ dev_dbg(component->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);
+ snd_soc_component_write(component, WM8993_CLOCKING_1, clocking1);
+ snd_soc_component_write(component, WM8993_CLOCKING_3, clocking3);
+ snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_1, aif1);
+ snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_4, aif4);
/* ReTune Mobile? */
if (wm8993->pdata.num_retune_configs) {
- u16 eq1 = snd_soc_read(codec, WM8993_EQ1);
+ u16 eq1 = snd_soc_component_read(component, WM8993_EQ1);
struct wm8993_retune_mobile_setting *s;
best = 0;
@@ -1316,34 +1316,34 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
}
s = &wm8993->pdata.retune_configs[best];
- dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+ dev_dbg(component->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);
+ snd_soc_component_update_bits(component, 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_component_write(component, WM8993_EQ1 + i, s->config[i]);
- snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
+ snd_soc_component_update_bits(component, WM8993_EQ1, WM8993_EQ_ENA, eq1);
}
return 0;
}
-static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm8993_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int reg;
- reg = snd_soc_read(codec, WM8993_DAC_CTRL);
+ reg = snd_soc_component_read(component, WM8993_DAC_CTRL);
if (mute)
reg |= WM8993_DAC_MUTE;
else
reg &= ~WM8993_DAC_MUTE;
- snd_soc_write(codec, WM8993_DAC_CTRL, reg);
+ snd_soc_component_write(component, WM8993_DAC_CTRL, reg);
return 0;
}
@@ -1351,8 +1351,8 @@ static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
int aif1 = 0;
int aif2 = 0;
@@ -1394,9 +1394,9 @@ out:
wm8993->tdm_width = slot_width;
wm8993->tdm_slots = slots / 2;
- snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_1,
+ snd_soc_component_update_bits(component, WM8993_AUDIO_INTERFACE_1,
WM8993_AIFADC_TDM | WM8993_AIFADC_TDM_CHAN, aif1);
- snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_2,
+ snd_soc_component_update_bits(component, WM8993_AUDIO_INTERFACE_2,
WM8993_AIFDAC_TDM | WM8993_AIFDAC_TDM_CHAN, aif2);
return 0;
@@ -1445,9 +1445,10 @@ 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,
+ .mute_stream = wm8993_mute,
.set_pll = wm8993_set_fll,
.set_tdm_slot = wm8993_set_tdm_slot,
+ .no_capture_mute = 1,
};
#define WM8993_RATES SNDRV_PCM_RATE_8000_48000
@@ -1476,13 +1477,13 @@ static struct snd_soc_dai_driver wm8993_dai = {
.sig_bits = 24,
},
.ops = &wm8993_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int wm8993_probe(struct snd_soc_codec *codec)
+static int wm8993_probe(struct snd_soc_component *component)
{
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
wm8993->hubs_data.hp_startup_mode = 1;
wm8993->hubs_data.dcs_codes_l = -2;
@@ -1490,20 +1491,20 @@ static int wm8993_probe(struct snd_soc_codec *codec)
wm8993->hubs_data.series_startup = 1;
/* Latch volume update bits and default ZC on */
- snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
+ snd_soc_component_update_bits(component, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
WM8993_DAC_VU, WM8993_DAC_VU);
- snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
+ snd_soc_component_update_bits(component, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
WM8993_ADC_VU, WM8993_ADC_VU);
/* Manualy manage the HPOUT sequencing for independent stereo
* control. */
- snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, 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);
+ snd_soc_component_update_bits(component, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
- wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+ wm_hubs_handle_analogue_pdata(component, wm8993->pdata.lineout1_diff,
wm8993->pdata.lineout2_diff,
wm8993->pdata.lineout1fb,
wm8993->pdata.lineout2fb,
@@ -1514,63 +1515,65 @@ static int wm8993_probe(struct snd_soc_codec *codec)
wm8993->pdata.micbias1_lvl,
wm8993->pdata.micbias2_lvl);
- snd_soc_add_codec_controls(codec, wm8993_snd_controls,
+ snd_soc_add_component_controls(component, wm8993_snd_controls,
ARRAY_SIZE(wm8993_snd_controls));
if (wm8993->pdata.num_retune_configs != 0) {
- dev_dbg(codec->dev, "Using ReTune Mobile\n");
+ dev_dbg(component->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,
+ dev_dbg(component->dev, "No ReTune Mobile, using normal EQ\n");
+ snd_soc_add_component_controls(component, wm8993_eq_controls,
ARRAY_SIZE(wm8993_eq_controls));
}
snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets,
ARRAY_SIZE(wm8993_dapm_widgets));
- wm_hubs_add_analogue_controls(codec);
+ wm_hubs_add_analogue_controls(component);
snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
- wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
+ wm_hubs_add_analogue_routes(component, wm8993->pdata.lineout1_diff,
wm8993->pdata.lineout2_diff);
/* If the line outputs are differential then we aren't presenting
* VMID as an output and can disable it.
*/
if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff)
- dapm->idle_bias_off = 1;
+ snd_soc_dapm_set_idle_bias(dapm, false);
return 0;
}
#ifdef CONFIG_PM
-static int wm8993_suspend(struct snd_soc_codec *codec)
+static int wm8993_suspend(struct snd_soc_component *component)
{
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int fll_fout = wm8993->fll_fout;
int fll_fref = wm8993->fll_fref;
int ret;
/* Stop the FLL in an orderly fashion */
- ret = _wm8993_set_fll(codec, 0, 0, 0, 0);
+ ret = _wm8993_set_fll(component, 0, 0, 0, 0);
if (ret != 0) {
- dev_err(codec->dev, "Failed to stop FLL\n");
+ dev_err(component->dev, "Failed to stop FLL\n");
return ret;
}
wm8993->fll_fout = fll_fout;
wm8993->fll_fref = fll_fref;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
return 0;
}
-static int wm8993_resume(struct snd_soc_codec *codec)
+static int wm8993_resume(struct snd_soc_component *component)
{
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
/* Restart the FLL? */
if (wm8993->fll_fout) {
@@ -1580,10 +1583,10 @@ static int wm8993_resume(struct snd_soc_codec *codec)
wm8993->fll_fref = 0;
wm8993->fll_fout = 0;
- ret = _wm8993_set_fll(codec, 0, wm8993->fll_src,
+ ret = _wm8993_set_fll(component, 0, wm8993->fll_src,
fll_fref, fll_fout);
if (ret != 0)
- dev_err(codec->dev, "Failed to restart FLL\n");
+ dev_err(component->dev, "Failed to restart FLL\n");
}
return 0;
@@ -1608,20 +1611,22 @@ static const struct regmap_config wm8993_regmap = {
.volatile_reg = wm8993_volatile,
.readable_reg = wm8993_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.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 const struct snd_soc_component_driver soc_component_dev_wm8993 = {
+ .probe = wm8993_probe,
+ .suspend = wm8993_suspend,
+ .resume = wm8993_resume,
+ .set_bias_level = wm8993_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
-static int wm8993_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8993_i2c_probe(struct i2c_client *i2c)
{
struct wm8993_priv *wm8993;
unsigned int reg;
@@ -1703,8 +1708,8 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
regcache_cache_only(wm8993->regmap, true);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8993, &wm8993_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8993, &wm8993_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
goto err_irq;
@@ -1720,20 +1725,17 @@ err_enable:
return ret;
}
-static int wm8993_i2c_remove(struct i2c_client *i2c)
+static void 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 },
+ { "wm8993" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h
index 4478b40c86e3..91811aa158d8 100644
--- a/sound/soc/codecs/wm8993.h
+++ b/sound/soc/codecs/wm8993.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef WM8993_H
#define WM8993_H
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 2ccbb322df77..1d64c7c42ed1 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -47,10 +43,12 @@
#define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3
-static struct {
+struct wm8994_reg_mask {
unsigned int reg;
unsigned int mask;
-} wm8994_vu_bits[] = {
+};
+
+static struct wm8994_reg_mask wm8994_vu_bits[] = {
{ WM8994_LEFT_LINE_INPUT_1_2_VOLUME, WM8994_IN1_VU },
{ WM8994_RIGHT_LINE_INPUT_1_2_VOLUME, WM8994_IN1_VU },
{ WM8994_LEFT_LINE_INPUT_3_4_VOLUME, WM8994_IN2_VU },
@@ -64,14 +62,10 @@ static struct {
{ 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 },
@@ -80,6 +74,14 @@ static struct {
{ WM8994_DAC2_RIGHT_VOLUME, WM8994_DAC2_VU },
};
+/* VU bitfields for ADC2, DAC2 not available on WM1811 */
+static struct wm8994_reg_mask wm8994_adc2_dac2_vu_bits[] = {
+ { WM8994_AIF1_DAC2_LEFT_VOLUME, WM8994_AIF1DAC2_VU },
+ { WM8994_AIF1_DAC2_RIGHT_VOLUME, WM8994_AIF1DAC2_VU },
+ { WM8994_AIF1_ADC2_LEFT_VOLUME, WM8994_AIF1ADC2_VU },
+ { WM8994_AIF1_ADC2_RIGHT_VOLUME, WM8994_AIF1ADC2_VU },
+};
+
static int wm8994_drc_base[] = {
WM8994_AIF1_DRC1_1,
WM8994_AIF1_DRC2_1,
@@ -106,9 +108,9 @@ static const struct wm8958_micd_rate jackdet_rates[] = {
{ 44100 * 256, false, 7, 8 },
};
-static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
+static void wm8958_micd_set_rate(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int best, i, sysclk, val;
bool idle;
@@ -117,7 +119,7 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
idle = !wm8994->jack_mic;
- sysclk = snd_soc_read(codec, WM8994_CLOCKING_1);
+ sysclk = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (sysclk & WM8994_SYSCLK_SRC)
sysclk = wm8994->aifclk[1];
else
@@ -148,18 +150,18 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
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",
+ dev_dbg(component->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,
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1,
WM8958_MICD_BIAS_STARTTIME_MASK |
WM8958_MICD_RATE_MASK, val);
}
-static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
+static int configure_aif_clock(struct snd_soc_component *component, int aif)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
int rate;
int reg1 = 0;
int offset;
@@ -171,12 +173,12 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
switch (wm8994->sysclk[aif]) {
case WM8994_SYSCLK_MCLK1:
- rate = wm8994->mclk[0];
+ rate = wm8994->mclk_rate[0];
break;
case WM8994_SYSCLK_MCLK2:
reg1 |= 0x8;
- rate = wm8994->mclk[1];
+ rate = wm8994->mclk_rate[1];
break;
case WM8994_SYSCLK_FLL1:
@@ -197,28 +199,28 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
rate /= 2;
reg1 |= WM8994_AIF1CLK_DIV;
- dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n",
+ dev_dbg(component->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,
+ snd_soc_component_update_bits(component, WM8994_AIF1_CLOCKING_1 + offset,
WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
reg1);
return 0;
}
-static int configure_clock(struct snd_soc_codec *codec)
+static int configure_clock(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
int change, new;
/* Bring up the AIF clocks first */
- configure_aif_clock(codec, 0);
- configure_aif_clock(codec, 1);
+ configure_aif_clock(component, 0);
+ configure_aif_clock(component, 1);
/* Then switch CLK_SYS over to the higher of them; a change
* can only happen as a result of a clocking change which can
@@ -228,7 +230,7 @@ static int configure_clock(struct snd_soc_codec *codec)
/* If they're equal it doesn't matter which is used */
if (wm8994->aifclk[0] == wm8994->aifclk[1]) {
- wm8958_micd_set_rate(codec);
+ wm8958_micd_set_rate(component);
return 0;
}
@@ -237,12 +239,12 @@ static int configure_clock(struct snd_soc_codec *codec)
else
new = 0;
- change = snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ change = snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
WM8994_SYSCLK_SRC, new);
if (change)
snd_soc_dapm_sync(dapm);
- wm8958_micd_set_rate(codec);
+ wm8958_micd_set_rate(component);
return 0;
}
@@ -250,8 +252,8 @@ static int configure_clock(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ int reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
const char *clk;
/* Check what we're currently using for CLK_SYS */
@@ -260,7 +262,7 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
else
clk = "AIF1CLK";
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static const char *sidetone_hpf_text[] = {
@@ -289,7 +291,6 @@ 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, \
@@ -300,7 +301,7 @@ static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int mask, ret;
/* Can't enable both ADC and DAC paths simultaneously */
@@ -310,7 +311,7 @@ static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
else
mask = WM8994_AIF1DAC1_DRC_ENA_MASK;
- ret = snd_soc_read(codec, mc->reg);
+ ret = snd_soc_component_read(component, mc->reg);
if (ret < 0)
return ret;
if (ret & mask)
@@ -319,9 +320,9 @@ static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
return snd_soc_put_volsw(kcontrol, ucontrol);
}
-static void wm8994_set_drc(struct snd_soc_codec *codec, int drc)
+static void wm8994_set_drc(struct snd_soc_component *component, int drc)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int base = wm8994_drc_base[drc];
@@ -329,15 +330,15 @@ static void wm8994_set_drc(struct snd_soc_codec *codec, int drc)
int save, i;
/* Save any enables; the configuration should clear them. */
- save = snd_soc_read(codec, base);
+ save = snd_soc_component_read(component, base);
save &= WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
WM8994_AIF1ADC1R_DRC_ENA;
for (i = 0; i < WM8994_DRC_REGS; i++)
- snd_soc_update_bits(codec, base + i, 0xffff,
+ snd_soc_component_update_bits(component, base + i, 0xffff,
pdata->drc_cfgs[cfg].regs[i]);
- snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_DRC_ENA |
+ snd_soc_component_update_bits(component, base, WM8994_AIF1DAC1_DRC_ENA |
WM8994_AIF1ADC1L_DRC_ENA |
WM8994_AIF1ADC1R_DRC_ENA, save);
}
@@ -357,12 +358,12 @@ static int wm8994_get_drc(const char *name)
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int drc = wm8994_get_drc(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (drc < 0)
return drc;
@@ -372,7 +373,7 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
wm8994->drc_cfg[drc] = value;
- wm8994_set_drc(codec, drc);
+ wm8994_set_drc(component, drc);
return 0;
}
@@ -380,8 +381,8 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
int drc = wm8994_get_drc(kcontrol->id.name);
if (drc < 0)
@@ -391,9 +392,9 @@ static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
+static void wm8994_set_retune_mobile(struct snd_soc_component *component, int block)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int base = wm8994_retune_mobile_base[block];
@@ -430,7 +431,7 @@ static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
}
}
- dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
block,
pdata->retune_mobile_cfgs[best].name,
pdata->retune_mobile_cfgs[best].rate,
@@ -439,14 +440,14 @@ static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
/* The EQ will be disabled while reconfiguring it, remember the
* current configuration.
*/
- save = snd_soc_read(codec, base);
+ save = snd_soc_component_read(component, base);
save &= WM8994_AIF1DAC1_EQ_ENA;
for (i = 0; i < WM8994_EQ_REGS; i++)
- snd_soc_update_bits(codec, base + i, 0xffff,
+ snd_soc_component_update_bits(component, base + i, 0xffff,
pdata->retune_mobile_cfgs[best].regs[i]);
- snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_EQ_ENA, save);
+ snd_soc_component_update_bits(component, base, WM8994_AIF1DAC1_EQ_ENA, save);
}
/* Icky as hell but saves code duplication */
@@ -464,12 +465,12 @@ static int wm8994_get_retune_mobile_block(const char *name)
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (block < 0)
return block;
@@ -479,7 +480,7 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
wm8994->retune_mobile_cfg[block] = value;
- wm8994_set_retune_mobile(codec, block);
+ wm8994_set_retune_mobile(component, block);
return 0;
}
@@ -487,8 +488,8 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
if (block < 0)
@@ -537,13 +538,10 @@ static SOC_ENUM_SINGLE_DECL(dac_osr,
static SOC_ENUM_SINGLE_DECL(adc_osr,
WM8994_OVERSAMPLING, 1, osr_text);
-static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+static const struct snd_kcontrol_new wm8994_common_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
1, 119, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("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),
@@ -560,8 +558,6 @@ 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),
@@ -569,17 +565,12 @@ 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),
@@ -598,9 +589,6 @@ 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),
@@ -641,6 +629,24 @@ SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2,
8, 1, 0),
};
+/* Controls not available on WM1811 */
+static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
+ WM8994_AIF1_ADC2_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
+ WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
+
+SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf),
+SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0),
+};
+
static const struct snd_kcontrol_new wm8994_eq_controls[] = {
SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0,
eq_tlv),
@@ -732,17 +738,10 @@ SOC_SINGLE_TLV("AIF2DAC Noise Gate Threshold Volume",
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)
+static void wm1811_jackdet_set_mode(struct snd_soc_component *component, u16 mode)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (!wm8994->jackdet || !wm8994->micdet[0].jack)
return;
@@ -759,37 +758,37 @@ static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode)
if (mode != WM1811_JACKDET_MODE_NONE)
mode = WM1811_JACKDET_MODE_AUDIO;
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM1811_JACKDET_MODE_MASK, mode);
}
-static void active_reference(struct snd_soc_codec *codec)
+static void active_reference(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
mutex_lock(&wm8994->accdet_lock);
wm8994->active_refcount++;
- dev_dbg(codec->dev, "Active refcount incremented, now %d\n",
+ dev_dbg(component->dev, "Active refcount incremented, now %d\n",
wm8994->active_refcount);
/* If we're using jack detection go into audio mode */
- wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_AUDIO);
+ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO);
mutex_unlock(&wm8994->accdet_lock);
}
-static void active_dereference(struct snd_soc_codec *codec)
+static void active_dereference(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
u16 mode;
mutex_lock(&wm8994->accdet_lock);
wm8994->active_refcount--;
- dev_dbg(codec->dev, "Active refcount decremented, now %d\n",
+ dev_dbg(component->dev, "Active refcount decremented, now %d\n",
wm8994->active_refcount);
if (wm8994->active_refcount == 0) {
@@ -799,7 +798,7 @@ static void active_dereference(struct snd_soc_codec *codec)
else
mode = WM1811_JACKDET_MODE_JACK;
- wm1811_jackdet_set_mode(codec, mode);
+ wm1811_jackdet_set_mode(component, mode);
}
mutex_unlock(&wm8994->accdet_lock);
@@ -808,12 +807,12 @@ static void active_dereference(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return configure_clock(codec);
+ return configure_clock(component);
case SND_SOC_DAPM_POST_PMU:
/*
@@ -832,37 +831,38 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMD:
- configure_clock(codec);
+ configure_clock(component);
break;
}
return 0;
}
-static void vmid_reference(struct snd_soc_codec *codec)
+static void vmid_reference(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
wm8994->vmid_refcount++;
- dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n",
+ dev_dbg(component->dev, "Referencing VMID, refcount is now %d\n",
wm8994->vmid_refcount);
if (wm8994->vmid_refcount == 1) {
- snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_1,
WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH, 0);
- wm_hubs_vmid_ena(codec);
+ wm_hubs_vmid_ena(component);
switch (wm8994->vmid_mode) {
default:
WARN_ON(NULL == "Invalid VMID mode");
+ fallthrough;
case WM8994_VMID_NORMAL:
/* Startup bias, VMID ramp & buffer */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM8994_BIAS_SRC |
WM8994_VMID_DISCH |
WM8994_STARTUP_BIAS_ENA |
@@ -874,14 +874,14 @@ static void vmid_reference(struct snd_soc_codec *codec)
(0x2 << WM8994_VMID_RAMP_SHIFT));
/* Main bias enable, VMID=2x40k */
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1,
WM8994_BIAS_ENA |
WM8994_VMID_SEL_MASK,
WM8994_BIAS_ENA | 0x2);
msleep(300);
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM8994_VMID_RAMP_MASK |
WM8994_BIAS_SRC,
0);
@@ -889,7 +889,7 @@ static void vmid_reference(struct snd_soc_codec *codec)
case WM8994_VMID_FORCE:
/* Startup bias, slow VMID ramp & buffer */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM8994_BIAS_SRC |
WM8994_VMID_DISCH |
WM8994_STARTUP_BIAS_ENA |
@@ -901,14 +901,14 @@ static void vmid_reference(struct snd_soc_codec *codec)
(0x2 << WM8994_VMID_RAMP_SHIFT));
/* Main bias enable, VMID=2x40k */
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1,
WM8994_BIAS_ENA |
WM8994_VMID_SEL_MASK,
WM8994_BIAS_ENA | 0x2);
msleep(400);
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM8994_VMID_RAMP_MASK |
WM8994_BIAS_SRC,
0);
@@ -917,133 +917,192 @@ static void vmid_reference(struct snd_soc_codec *codec)
}
}
-static void vmid_dereference(struct snd_soc_codec *codec)
+static void vmid_dereference(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
wm8994->vmid_refcount--;
- dev_dbg(codec->dev, "Dereferencing VMID, refcount is now %d\n",
+ dev_dbg(component->dev, "Dereferencing VMID, refcount is now %d\n",
wm8994->vmid_refcount);
if (wm8994->vmid_refcount == 0) {
if (wm8994->hubs.lineout1_se)
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_3,
WM8994_LINEOUT1N_ENA |
WM8994_LINEOUT1P_ENA,
WM8994_LINEOUT1N_ENA |
WM8994_LINEOUT1P_ENA);
if (wm8994->hubs.lineout2_se)
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_3,
WM8994_LINEOUT2N_ENA |
WM8994_LINEOUT2P_ENA,
WM8994_LINEOUT2N_ENA |
WM8994_LINEOUT2P_ENA);
/* Start discharging VMID */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM8994_BIAS_SRC |
WM8994_VMID_DISCH,
WM8994_BIAS_SRC |
WM8994_VMID_DISCH);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1,
WM8994_VMID_SEL_MASK, 0);
msleep(400);
/* Active discharge */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_1,
WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH,
WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_3,
WM8994_LINEOUT1N_ENA |
WM8994_LINEOUT1P_ENA |
WM8994_LINEOUT2N_ENA |
WM8994_LINEOUT2P_ENA, 0);
/* Switch off startup biases */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_2,
WM8994_BIAS_SRC |
WM8994_STARTUP_BIAS_ENA |
WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK, 0);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1,
WM8994_VMID_SEL_MASK, 0);
}
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- vmid_reference(codec);
+ vmid_reference(component);
break;
case SND_SOC_DAPM_POST_PMD:
- vmid_dereference(codec);
+ vmid_dereference(component);
break;
}
return 0;
}
-static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec)
+static bool wm8994_check_class_w_digital(struct snd_soc_component *component)
{
int source = 0; /* GCC flow analysis can't track enable */
int reg, reg_r;
/* We also need the same AIF source for L/R and only one path */
- reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
+ reg = snd_soc_component_read(component, WM8994_DAC1_LEFT_MIXER_ROUTING);
switch (reg) {
case WM8994_AIF2DACL_TO_DAC1L:
- dev_vdbg(codec->dev, "Class W source AIF2DAC\n");
+ dev_vdbg(component->dev, "Class W source AIF2DAC\n");
source = 2 << WM8994_CP_DYN_SRC_SEL_SHIFT;
break;
case WM8994_AIF1DAC2L_TO_DAC1L:
- dev_vdbg(codec->dev, "Class W source AIF1DAC2\n");
+ dev_vdbg(component->dev, "Class W source AIF1DAC2\n");
source = 1 << WM8994_CP_DYN_SRC_SEL_SHIFT;
break;
case WM8994_AIF1DAC1L_TO_DAC1L:
- dev_vdbg(codec->dev, "Class W source AIF1DAC1\n");
+ dev_vdbg(component->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);
+ dev_vdbg(component->dev, "DAC mixer setting: %x\n", reg);
return false;
}
- reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
+ reg_r = snd_soc_component_read(component, WM8994_DAC1_RIGHT_MIXER_ROUTING);
if (reg_r != reg) {
- dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
+ dev_vdbg(component->dev, "Left and right DAC mixers different\n");
return false;
}
/* Set the source up */
- snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+ snd_soc_component_update_bits(component, WM8994_CLASS_W_1,
WM8994_CP_DYN_SRC_SEL_MASK, source);
return true;
}
+static void wm8994_update_vu_bits(struct snd_soc_component *component)
+{
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ struct wm8994 *control = wm8994->wm8994;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994_vu_bits); i++)
+ snd_soc_component_write(component, wm8994_vu_bits[i].reg,
+ snd_soc_component_read(component,
+ wm8994_vu_bits[i].reg));
+ if (control->type == WM1811)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994_adc2_dac2_vu_bits); i++)
+ snd_soc_component_write(component,
+ wm8994_adc2_dac2_vu_bits[i].reg,
+ snd_soc_component_read(component,
+ wm8994_adc2_dac2_vu_bits[i].reg));
+}
+
+static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enable)
+{
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ unsigned int offset, val, clk_idx;
+ int ret;
+
+ if (aif)
+ offset = 4;
+ else
+ offset = 0;
+
+ val = snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1 + offset);
+ val &= WM8994_AIF1CLK_SRC_MASK;
+
+ switch (val) {
+ case 0:
+ clk_idx = WM8994_MCLK1;
+ break;
+ case 1:
+ clk_idx = WM8994_MCLK2;
+ break;
+ default:
+ return 0;
+ }
+
+ if (enable) {
+ ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable MCLK%d\n",
+ clk_idx);
+ return ret;
+ }
+ } else {
+ clk_disable_unprepare(wm8994->mclk[clk_idx].clk);
+ }
+
+ return 0;
+}
+
static int aif1clk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA;
- int i;
+ int ret;
int dac;
int adc;
int val;
@@ -1059,11 +1118,15 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ ret = aif_mclk_set(component, 0, true);
+ if (ret < 0)
+ return ret;
+
/* Don't enable timeslot 2 if not in use */
if (wm8994->channels[0] <= 2)
mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
- val = snd_soc_read(codec, WM8994_AIF1_CONTROL_1);
+ val = snd_soc_component_read(component, WM8994_AIF1_CONTROL_1);
if ((val & WM8994_AIF1ADCL_SRC) &&
(val & WM8994_AIF1ADCR_SRC))
adc = WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC2R_ENA;
@@ -1074,7 +1137,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
adc = WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC2R_ENA |
WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC2L_ENA;
- val = snd_soc_read(codec, WM8994_AIF1_CONTROL_2);
+ val = snd_soc_component_read(component, WM8994_AIF1_CONTROL_2);
if ((val & WM8994_AIF1DACL_SRC) &&
(val & WM8994_AIF1DACR_SRC))
dac = WM8994_AIF1DAC1R_ENA | WM8994_AIF1DAC2R_ENA;
@@ -1085,21 +1148,21 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
dac = WM8994_AIF1DAC1R_ENA | WM8994_AIF1DAC2R_ENA |
WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC2L_ENA;
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4,
mask, adc);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5,
mask, dac);
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, mask,
WM8994_AIF1ADC1R_ENA |
WM8994_AIF1ADC1L_ENA |
WM8994_AIF1ADC2R_ENA |
WM8994_AIF1ADC2L_ENA);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, mask,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, mask,
WM8994_AIF1DAC1R_ENA |
WM8994_AIF1DAC1L_ENA |
WM8994_AIF1DAC2R_ENA |
@@ -1107,45 +1170,52 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
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));
+ wm8994_update_vu_bits(component);
break;
case SND_SOC_DAPM_PRE_PMD:
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5,
mask, 0);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4,
mask, 0);
- val = snd_soc_read(codec, WM8994_CLOCKING_1);
+ val = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (val & WM8994_AIF2DSPCLK_ENA)
val = WM8994_SYSDSPCLK_ENA;
else
val = 0;
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
WM8994_SYSDSPCLK_ENA |
WM8994_AIF1DSPCLK_ENA, val);
break;
}
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ aif_mclk_set(component, 0, false);
+ break;
+ }
+
return 0;
}
static int aif2clk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- int i;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int ret;
int dac;
int adc;
int val;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- val = snd_soc_read(codec, WM8994_AIF2_CONTROL_1);
+ ret = aif_mclk_set(component, 1, true);
+ if (ret < 0)
+ return ret;
+
+ val = snd_soc_component_read(component, WM8994_AIF2_CONTROL_1);
if ((val & WM8994_AIF2ADCL_SRC) &&
(val & WM8994_AIF2ADCR_SRC))
adc = WM8994_AIF2ADCR_ENA;
@@ -1156,7 +1226,7 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
adc = WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA;
- val = snd_soc_read(codec, WM8994_AIF2_CONTROL_2);
+ val = snd_soc_component_read(component, WM8994_AIF2_CONTROL_2);
if ((val & WM8994_AIF2DACL_SRC) &&
(val & WM8994_AIF2DACR_SRC))
dac = WM8994_AIF2DACR_ENA;
@@ -1166,23 +1236,23 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
else
dac = WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA;
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4,
WM8994_AIF2ADCL_ENA |
WM8994_AIF2ADCR_ENA, adc);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA |
WM8994_AIF2DACR_ENA, dac);
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
WM8994_AIF2DSPCLK_ENA |
WM8994_SYSDSPCLK_ENA,
WM8994_AIF2DSPCLK_ENA |
WM8994_SYSDSPCLK_ENA);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA |
WM8994_AIF2DACR_ENA,
WM8994_AIF2DACL_ENA |
@@ -1190,40 +1260,43 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
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));
+ wm8994_update_vu_bits(component);
break;
case SND_SOC_DAPM_PRE_PMD:
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA |
WM8994_AIF2DACR_ENA, 0);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4,
WM8994_AIF2ADCL_ENA |
WM8994_AIF2ADCR_ENA, 0);
- val = snd_soc_read(codec, WM8994_CLOCKING_1);
+ val = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (val & WM8994_AIF1DSPCLK_ENA)
val = WM8994_SYSDSPCLK_ENA;
else
val = 0;
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
WM8994_SYSDSPCLK_ENA |
WM8994_AIF2DSPCLK_ENA, val);
break;
}
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ aif_mclk_set(component, 1, false);
+ break;
+ }
+
return 0;
}
static int aif1clk_late_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1240,8 +1313,8 @@ static int aif1clk_late_ev(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1258,14 +1331,14 @@ static int aif2clk_late_ev(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (wm8994->aif1clk_enable) {
aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMU);
- snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_AIF1_CLOCKING_1,
WM8994_AIF1CLK_ENA_MASK,
WM8994_AIF1CLK_ENA);
aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMU);
@@ -1273,7 +1346,7 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w,
}
if (wm8994->aif2clk_enable) {
aif2clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMU);
- snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_AIF2_CLOCKING_1,
WM8994_AIF2CLK_ENA_MASK,
WM8994_AIF2CLK_ENA);
aif2clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMU);
@@ -1291,21 +1364,21 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
if (wm8994->aif1clk_disable) {
aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD);
- snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_AIF1_CLOCKING_1,
WM8994_AIF1CLK_ENA_MASK, 0);
aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD);
wm8994->aif1clk_disable = 0;
}
if (wm8994->aif2clk_disable) {
aif2clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD);
- snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8994_AIF2_CLOCKING_1,
WM8994_AIF2CLK_ENA_MASK, 0);
aif2clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD);
wm8994->aif2clk_disable = 0;
@@ -1333,10 +1406,10 @@ static int micbias_ev(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int mask = 1 << w->shift;
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5,
mask, mask);
return 0;
}
@@ -1374,9 +1447,9 @@ SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 0, 1, 0),
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,
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ dev_dbg(component->dev, "SRC status: %x\n",
+ snd_soc_component_read(component,
WM8994_RATE_STATUS));
return 0;
}
@@ -1442,12 +1515,12 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
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);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
- wm_hubs_update_class_w(codec);
+ wm_hubs_update_class_w(component);
return ret;
}
@@ -1621,10 +1694,10 @@ 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_PRE_PMD | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -2129,16 +2202,17 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
return 0;
}
-static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
+static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
unsigned int freq_in, unsigned int freq_out)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int reg_offset, ret;
struct fll_div fll;
u16 reg, clk1, aif_reg, aif_src;
- unsigned long timeout;
+ unsigned long time_left;
bool was_enabled;
+ struct clk *mclk;
switch (id) {
case WM8994_FLL1:
@@ -2155,7 +2229,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
return -EINVAL;
}
- reg = snd_soc_read(codec, WM8994_FLL1_CONTROL_1 + reg_offset);
+ reg = snd_soc_component_read(component, WM8994_FLL1_CONTROL_1 + reg_offset);
was_enabled = reg & WM8994_FLL1_ENA;
switch (src) {
@@ -2196,57 +2270,78 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
return ret;
/* Make sure that we're not providing SYSCLK right now */
- clk1 = snd_soc_read(codec, WM8994_CLOCKING_1);
+ clk1 = snd_soc_component_read(component, WM8994_CLOCKING_1);
if (clk1 & WM8994_SYSCLK_SRC)
aif_reg = WM8994_AIF2_CLOCKING_1;
else
aif_reg = WM8994_AIF1_CLOCKING_1;
- reg = snd_soc_read(codec, aif_reg);
+ reg = snd_soc_component_read(component, 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",
+ dev_err(component->dev, "FLL%d is currently providing SYSCLK\n",
id + 1);
return -EBUSY;
}
/* We always need to disable the FLL while reconfiguring */
- snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
WM8994_FLL1_ENA, 0);
+ /* Disable MCLK if needed before we possibly change to new clock parent */
+ if (was_enabled) {
+ reg = snd_soc_component_read(component, WM8994_FLL1_CONTROL_5
+ + reg_offset);
+ reg = ((reg & WM8994_FLL1_REFCLK_SRC_MASK)
+ >> WM8994_FLL1_REFCLK_SRC_SHIFT) + 1;
+
+ switch (reg) {
+ case WM8994_FLL_SRC_MCLK1:
+ mclk = wm8994->mclk[WM8994_MCLK1].clk;
+ break;
+ case WM8994_FLL_SRC_MCLK2:
+ mclk = wm8994->mclk[WM8994_MCLK2].clk;
+ break;
+ default:
+ mclk = NULL;
+ }
+
+ clk_disable_unprepare(mclk);
+ }
+
if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
freq_in == freq_out && freq_out) {
- dev_dbg(codec->dev, "Bypassing FLL%d\n", id + 1);
- snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ dev_dbg(component->dev, "Bypassing FLL%d\n", id + 1);
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8958_FLL1_BYP, WM8958_FLL1_BYP);
goto out;
}
reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
(fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
- snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_3 + reg_offset,
WM8994_FLL1_K_MASK, fll.k);
- snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_4 + reg_offset,
WM8994_FLL1_N_MASK,
fll.n << WM8994_FLL1_N_SHIFT);
if (fll.lambda) {
- snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
+ snd_soc_component_update_bits(component, WM8958_FLL1_EFS_1 + reg_offset,
WM8958_FLL1_LAMBDA_MASK,
fll.lambda);
- snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset,
WM8958_FLL1_EFS_ENA, 0);
}
- snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK |
WM8994_FLL1_REFCLK_SRC_MASK,
@@ -2258,19 +2353,38 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
/* Clear any pending completion from a previous failure */
try_wait_for_completion(&wm8994->fll_locked[id]);
+ switch (src) {
+ case WM8994_FLL_SRC_MCLK1:
+ mclk = wm8994->mclk[WM8994_MCLK1].clk;
+ break;
+ case WM8994_FLL_SRC_MCLK2:
+ mclk = wm8994->mclk[WM8994_MCLK2].clk;
+ break;
+ default:
+ mclk = NULL;
+ }
+
/* Enable (with fractional mode if required) */
if (freq_out) {
+ ret = clk_prepare_enable(mclk);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to enable MCLK for FLL%d\n",
+ id + 1);
+ return ret;
+ }
+
/* Enable VMID if we need it */
if (!was_enabled) {
- active_reference(codec);
+
+ active_reference(component);
switch (control->type) {
case WM8994:
- vmid_reference(codec);
+ vmid_reference(component);
break;
case WM8958:
if (control->revision < 1)
- vmid_reference(codec);
+ vmid_reference(component);
break;
default:
break;
@@ -2284,15 +2398,15 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
if (src == WM8994_FLL_SRC_INTERNAL)
reg |= WM8994_FLL1_OSC_ENA;
- snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA |
WM8994_FLL1_FRAC, reg);
if (wm8994->fll_locked_irq) {
- timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
- msecs_to_jiffies(10));
- if (timeout == 0)
- dev_warn(codec->dev,
+ time_left = wait_for_completion_timeout(&wm8994->fll_locked[id],
+ msecs_to_jiffies(10));
+ if (time_left == 0)
+ dev_warn(component->dev,
"Timed out waiting for FLL lock\n");
} else {
msleep(5);
@@ -2301,17 +2415,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
if (was_enabled) {
switch (control->type) {
case WM8994:
- vmid_dereference(codec);
+ vmid_dereference(component);
break;
case WM8958:
if (control->revision < 1)
- vmid_dereference(codec);
+ vmid_dereference(component);
break;
default:
break;
}
- active_dereference(codec);
+ active_dereference(component);
}
}
@@ -2320,29 +2434,29 @@ out:
wm8994->fll[id].out = freq_out;
wm8994->fll[id].src = src;
- configure_clock(codec);
+ configure_clock(component);
/*
* If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
* for detection.
*/
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
- dev_dbg(codec->dev, "Configuring AIFs for 128fs\n");
+ dev_dbg(component->dev, "Configuring AIFs for 128fs\n");
- wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE)
+ wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE)
& WM8994_AIF1CLK_RATE_MASK;
- wm8994->aifdiv[1] = snd_soc_read(codec, WM8994_AIF2_RATE)
+ wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE)
& WM8994_AIF1CLK_RATE_MASK;
- snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
- snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
} else if (wm8994->aifdiv[0]) {
- snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK,
wm8994->aifdiv[0]);
- snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK,
wm8994->aifdiv[1]);
@@ -2367,15 +2481,32 @@ 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);
+ return _wm8994_set_fll(dai->component, id, src, freq_in, freq_out);
+}
+
+static int wm8994_set_mclk_rate(struct wm8994_priv *wm8994, unsigned int id,
+ unsigned int *freq)
+{
+ int ret;
+
+ if (!wm8994->mclk[id].clk || *freq == wm8994->mclk_rate[id])
+ return 0;
+
+ ret = clk_set_rate(wm8994->mclk[id].clk, *freq);
+ if (ret < 0)
+ return ret;
+
+ *freq = clk_get_rate(wm8994->mclk[id].clk);
+
+ return 0;
}
static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- int i;
+ struct snd_soc_component *component = dai->component;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ int ret, i;
switch (dai->id) {
case 1:
@@ -2390,7 +2521,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
switch (clk_id) {
case WM8994_SYSCLK_MCLK1:
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
- wm8994->mclk[0] = freq;
+
+ ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
+ if (ret < 0)
+ return ret;
+
+ wm8994->mclk_rate[0] = freq;
dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
dai->id, freq);
break;
@@ -2398,7 +2534,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
case WM8994_SYSCLK_MCLK2:
/* TODO: Set GPIO AF */
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
- wm8994->mclk[1] = freq;
+
+ ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
+ if (ret < 0)
+ return ret;
+
+ wm8994->mclk_rate[1] = freq;
dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
dai->id, freq);
break;
@@ -2423,42 +2564,43 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
break;
if (i == ARRAY_SIZE(opclk_divs))
return -EINVAL;
- snd_soc_update_bits(codec, WM8994_CLOCKING_2,
+ snd_soc_component_update_bits(component, WM8994_CLOCKING_2,
WM8994_OPCLK_DIV_MASK, i);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_2,
WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
} else {
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+ snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_2,
WM8994_OPCLK_ENA, 0);
}
+ break;
default:
return -EINVAL;
}
- configure_clock(codec);
+ configure_clock(component);
/*
* If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
* for detection.
*/
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
- dev_dbg(codec->dev, "Configuring AIFs for 128fs\n");
+ dev_dbg(component->dev, "Configuring AIFs for 128fs\n");
- wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE)
+ wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE)
& WM8994_AIF1CLK_RATE_MASK;
- wm8994->aifdiv[1] = snd_soc_read(codec, WM8994_AIF2_RATE)
+ wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE)
& WM8994_AIF1CLK_RATE_MASK;
- snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
- snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
} else if (wm8994->aifdiv[0]) {
- snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK,
wm8994->aifdiv[0]);
- snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK,
wm8994->aifdiv[1]);
@@ -2469,13 +2611,14 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int wm8994_set_bias_level(struct snd_soc_codec *codec,
+static int wm8994_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wm8994 *control = wm8994->wm8994;
- wm_hubs_set_bias_level(codec, level);
+ wm_hubs_set_bias_level(component, level);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -2486,26 +2629,26 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
switch (control->type) {
case WM8958:
case WM1811:
- snd_soc_update_bits(codec, WM8958_MICBIAS1,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS1,
WM8958_MICB1_MODE, 0);
- snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_MODE, 0);
break;
default:
break;
}
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
- active_reference(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY)
+ active_reference(component);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
switch (control->type) {
case WM8958:
if (control->revision == 0) {
/* Optimise performance for rev A */
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8958_CHARGE_PUMP_2,
WM8958_CP_DISCH,
WM8958_CP_DISCH);
@@ -2517,24 +2660,24 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
}
/* Discharge LINEOUT1 & 2 */
- snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ snd_soc_component_update_bits(component, WM8994_ANTIPOP_1,
WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH,
WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH);
}
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE)
- active_dereference(codec);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE)
+ active_dereference(component);
/* MICBIAS into bypass mode on newer devices */
switch (control->type) {
case WM8958:
case WM1811:
- snd_soc_update_bits(codec, WM8958_MICBIAS1,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS1,
WM8958_MICB1_MODE,
WM8958_MICB1_MODE);
- snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_MODE,
WM8958_MICB2_MODE);
break;
@@ -2544,7 +2687,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY)
wm8994->cur_fw = NULL;
break;
}
@@ -2552,10 +2695,10 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
+int wm8994_vmid_mode(struct snd_soc_component *component, enum wm8994_vmid_mode mode)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (mode) {
case WM8994_VMID_NORMAL:
@@ -2612,8 +2755,8 @@ int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
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 snd_soc_component *component = dai->component;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int ms_reg;
int aif1_reg;
@@ -2641,9 +2784,9 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ms = WM8994_AIF1_MSTR;
break;
default:
@@ -2654,6 +2797,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_B:
aif1 |= WM8994_AIF1_LRCLK_INV;
lrclk |= WM8958_AIF1_LRCLK_INV;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x18;
break;
@@ -2715,7 +2859,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case WM1811:
case WM8958:
if (dai->id == 2)
- snd_soc_update_bits(codec, WM8958_AIF3_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8958_AIF3_CONTROL_1,
WM8994_AIF1_LRCLK_INV |
WM8958_AIF3_FMT_MASK, aif1);
break;
@@ -2724,15 +2868,15 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
}
- snd_soc_update_bits(codec, aif1_reg,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, ms_reg, WM8994_AIF1_MSTR,
ms);
- snd_soc_update_bits(codec, dac_reg,
+ snd_soc_component_update_bits(component, dac_reg,
WM8958_AIF1_LRCLK_INV, lrclk);
- snd_soc_update_bits(codec, adc_reg,
+ snd_soc_component_update_bits(component, adc_reg,
WM8958_AIF1_LRCLK_INV, lrclk);
return 0;
@@ -2767,8 +2911,8 @@ 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 snd_soc_component *component = dai->component;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int aif1_reg;
@@ -2796,7 +2940,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
lrclk_reg = WM8994_AIF1DAC_LRCLK;
} else {
lrclk_reg = WM8994_AIF1ADC_LRCLK;
- dev_dbg(codec->dev, "AIF1 using split LRCLK\n");
+ dev_dbg(component->dev, "AIF1 using split LRCLK\n");
}
break;
case 2:
@@ -2809,7 +2953,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
lrclk_reg = WM8994_AIF2DAC_LRCLK;
} else {
lrclk_reg = WM8994_AIF2ADC_LRCLK;
- dev_dbg(codec->dev, "AIF2 using split LRCLK\n");
+ dev_dbg(component->dev, "AIF2 using split LRCLK\n");
}
break;
default:
@@ -2868,7 +3012,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
dai->id, wm8994->aifclk[id], bclk_rate);
if (wm8994->channels[id] == 1 &&
- (snd_soc_read(codec, aif1_reg) & 0x18) == 0x18)
+ (snd_soc_component_read(component, aif1_reg) & 0x18) == 0x18)
aif2 |= WM8994_AIF1_MONO;
if (wm8994->aifclk[id] == 0) {
@@ -2918,24 +3062,24 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
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,
+ snd_soc_component_update_bits(component, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
+ snd_soc_component_update_bits(component, aif2_reg, WM8994_AIF1_MONO, aif2);
+ snd_soc_component_update_bits(component, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_component_update_bits(component, lrclk_reg, WM8994_AIF1DAC_RATE_MASK,
lrclk);
- snd_soc_update_bits(codec, rate_reg, WM8994_AIF1_SR_MASK |
+ snd_soc_component_update_bits(component, rate_reg, WM8994_AIF1_SR_MASK |
WM8994_AIF1CLK_RATE_MASK, rate_val);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (dai->id) {
case 1:
wm8994->dac_rates[0] = params_rate(params);
- wm8994_set_retune_mobile(codec, 0);
- wm8994_set_retune_mobile(codec, 1);
+ wm8994_set_retune_mobile(component, 0);
+ wm8994_set_retune_mobile(component, 1);
break;
case 2:
wm8994->dac_rates[1] = params_rate(params);
- wm8994_set_retune_mobile(codec, 2);
+ wm8994_set_retune_mobile(component, 2);
break;
}
}
@@ -2947,8 +3091,8 @@ 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 snd_soc_component *component = dai->component;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int aif1_reg;
int aif1 = 0;
@@ -2984,12 +3128,13 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
+ return snd_soc_component_update_bits(component, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
}
-static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int mute_reg;
int reg;
@@ -3009,14 +3154,14 @@ static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, mute_reg, WM8994_AIF1DAC1_MUTE, reg);
+ snd_soc_component_update_bits(component, mute_reg, WM8994_AIF1DAC1_MUTE, reg);
return 0;
}
static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int reg, val, mask;
switch (codec_dai->id) {
@@ -3037,19 +3182,19 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
else
val = 0;
- return snd_soc_update_bits(codec, reg, mask, val);
+ return snd_soc_component_update_bits(component, reg, mask, val);
}
static int wm8994_aif2_probe(struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
/* Disable the pulls on the AIF if we're using it to save power. */
- snd_soc_update_bits(codec, WM8994_GPIO_3,
+ snd_soc_component_update_bits(component, WM8994_GPIO_3,
WM8994_GPN_PU | WM8994_GPN_PD, 0);
- snd_soc_update_bits(codec, WM8994_GPIO_4,
+ snd_soc_component_update_bits(component, WM8994_GPIO_4,
WM8994_GPN_PU | WM8994_GPN_PD, 0);
- snd_soc_update_bits(codec, WM8994_GPIO_5,
+ snd_soc_component_update_bits(component, WM8994_GPIO_5,
WM8994_GPN_PU | WM8994_GPN_PD, 0);
return 0;
@@ -3064,18 +3209,21 @@ 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,
+ .mute_stream = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
+ .probe = wm8994_aif2_probe,
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
- .digital_mute = wm8994_aif_mute,
+ .mute_stream = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
@@ -3123,7 +3271,6 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
.formats = WM8994_FORMATS,
.sig_bits = 24,
},
- .probe = wm8994_aif2_probe,
.ops = &wm8994_aif2_dai_ops,
},
{
@@ -3150,53 +3297,54 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
};
#ifdef CONFIG_PM
-static int wm8994_codec_suspend(struct snd_soc_codec *codec)
+static int wm8994_component_suspend(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int i, ret;
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
sizeof(struct wm8994_fll_config));
- ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0);
+ ret = _wm8994_set_fll(component, i + 1, 0, 0, 0);
if (ret < 0)
- dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
+ dev_warn(component->dev, "Failed to stop FLL%d: %d\n",
i + 1, ret);
}
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
return 0;
}
-static int wm8994_codec_resume(struct snd_soc_codec *codec)
+static int wm8994_component_resume(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
int i, ret;
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
if (!wm8994->fll_suspend[i].out)
continue;
- ret = _wm8994_set_fll(codec, i + 1,
+ ret = _wm8994_set_fll(component, i + 1,
wm8994->fll_suspend[i].src,
wm8994->fll_suspend[i].in,
wm8994->fll_suspend[i].out);
if (ret < 0)
- dev_warn(codec->dev, "Failed to restore FLL%d: %d\n",
+ dev_warn(component->dev, "Failed to restore FLL%d: %d\n",
i + 1, ret);
}
return 0;
}
#else
-#define wm8994_codec_suspend NULL
-#define wm8994_codec_resume NULL
+#define wm8994_component_suspend NULL
+#define wm8994_component_resume NULL
#endif
static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
{
- struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct snd_soc_component *component = wm8994->hubs.component;
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
struct snd_kcontrol_new controls[] = {
@@ -3249,22 +3397,22 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
wm8994->retune_mobile_texts = t;
}
- dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n",
wm8994->num_retune_mobile_texts);
wm8994->retune_mobile_enum.items = wm8994->num_retune_mobile_texts;
wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
- ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
+ ret = snd_soc_add_component_controls(wm8994->hubs.component, controls,
ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(wm8994->hubs.codec->dev,
+ dev_err(wm8994->hubs.component->dev,
"Failed to add ReTune Mobile controls: %d\n", ret);
}
static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
{
- struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct snd_soc_component *component = wm8994->hubs.component;
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int ret, i;
@@ -3272,7 +3420,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
if (!pdata)
return;
- wm_hubs_handle_analogue_pdata(codec, pdata->lineout1_diff,
+ wm_hubs_handle_analogue_pdata(component, pdata->lineout1_diff,
pdata->lineout2_diff,
pdata->lineout1fb,
pdata->lineout2fb,
@@ -3283,7 +3431,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
pdata->micbias1_lvl,
pdata->micbias2_lvl);
- dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+ dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
if (pdata->num_drc_cfgs) {
struct snd_kcontrol_new controls[] = {
@@ -3296,8 +3444,8 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
};
/* 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);
+ wm8994->drc_texts = devm_kcalloc(wm8994->hubs.component->dev,
+ pdata->num_drc_cfgs, sizeof(char *), GFP_KERNEL);
if (!wm8994->drc_texts)
return;
@@ -3307,33 +3455,33 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
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,
+ ret = snd_soc_add_component_controls(wm8994->hubs.component, controls,
ARRAY_SIZE(controls));
for (i = 0; i < WM8994_NUM_DRC; i++)
- wm8994_set_drc(codec, i);
+ wm8994_set_drc(component, i);
} else {
- ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ ret = snd_soc_add_component_controls(wm8994->hubs.component,
wm8994_drc_controls,
ARRAY_SIZE(wm8994_drc_controls));
}
if (ret != 0)
- dev_err(wm8994->hubs.codec->dev,
+ dev_err(wm8994->hubs.component->dev,
"Failed to add DRC mode controls: %d\n", ret);
- dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+ dev_dbg(component->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs);
if (pdata->num_retune_mobile_cfgs)
wm8994_handle_retune_mobile_pdata(wm8994);
else
- snd_soc_add_codec_controls(wm8994->hubs.codec, wm8994_eq_controls,
+ snd_soc_add_component_controls(wm8994->hubs.component, wm8994_eq_controls,
ARRAY_SIZE(wm8994_eq_controls));
for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
if (pdata->micbias[i]) {
- snd_soc_write(codec, WM8958_MICBIAS1 + i,
+ snd_soc_component_write(component, WM8958_MICBIAS1 + i,
pdata->micbias[i] & 0xffff);
}
}
@@ -3342,7 +3490,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
/**
* wm8994_mic_detect - Enable microphone detection via the WM8994 IRQ
*
- * @codec: WM8994 codec
+ * @component: WM8994 component
* @jack: jack to report detection events on
* @micbias: microphone bias to detect on
*
@@ -3354,20 +3502,22 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
* 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 wm8994_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack,
int micbias)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994_micdet *micdet;
struct wm8994 *control = wm8994->wm8994;
int reg, ret;
if (control->type != WM8994) {
- dev_warn(codec->dev, "Not a WM8994\n");
+ dev_warn(component->dev, "Not a WM8994\n");
return -EINVAL;
}
+ pm_runtime_get_sync(component->dev);
+
switch (micbias) {
case 1:
micdet = &wm8994->micdet[0];
@@ -3384,15 +3534,15 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
break;
default:
- dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias);
+ dev_warn(component->dev, "Invalid MICBIAS %d\n", micbias);
return -EINVAL;
}
if (ret != 0)
- dev_warn(codec->dev, "Failed to configure MICBIAS%d: %d\n",
+ dev_warn(component->dev, "Failed to configure MICBIAS%d: %d\n",
micbias, ret);
- dev_dbg(codec->dev, "Configuring microphone detection on %d %p\n",
+ dev_dbg(component->dev, "Configuring microphone detection on %d %p\n",
micbias, jack);
/* Store the configuration */
@@ -3405,16 +3555,18 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
else
reg = 0;
- snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
+ snd_soc_component_update_bits(component, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
/* enable MICDET and MICSHRT deboune */
- snd_soc_update_bits(codec, WM8994_IRQ_DEBOUNCE,
+ snd_soc_component_update_bits(component, WM8994_IRQ_DEBOUNCE,
WM8994_MIC1_DET_DB_MASK | WM8994_MIC1_SHRT_DB_MASK |
WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK,
WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB);
snd_soc_dapm_sync(dapm);
+ pm_runtime_put(component->dev);
+
return 0;
}
EXPORT_SYMBOL_GPL(wm8994_mic_detect);
@@ -3486,13 +3638,13 @@ static void wm8994_mic_work(struct work_struct *work)
static irqreturn_t wm8994_mic_irq(int irq, void *data)
{
struct wm8994_priv *priv = data;
- struct snd_soc_codec *codec = priv->hubs.codec;
+ struct snd_soc_component *component = priv->hubs.component;
#ifndef CONFIG_SND_SOC_WM8994_MODULE
- trace_snd_soc_jack_irq(dev_name(codec->dev));
+ trace_snd_soc_jack_irq(dev_name(component->dev));
#endif
- pm_wakeup_event(codec->dev, 300);
+ pm_wakeup_event(component->dev, 300);
queue_delayed_work(system_power_efficient_wq,
&priv->mic_work, msecs_to_jiffies(250));
@@ -3501,25 +3653,25 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
}
/* Should be called with accdet_lock held */
-static void wm1811_micd_stop(struct snd_soc_codec *codec)
+static void wm1811_micd_stop(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (!wm8994->jackdet)
return;
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0);
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0);
- wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
+ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_JACK);
if (wm8994->wm8994->pdata.jd_ext_cap)
snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
}
-static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
+static void wm8958_button_det(struct snd_soc_component *component, u16 status)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
int report;
report = 0;
@@ -3554,14 +3706,14 @@ static void wm8958_open_circuit_work(struct work_struct *work)
mutex_lock(&wm8994->accdet_lock);
- wm1811_micd_stop(wm8994->hubs.codec);
+ wm1811_micd_stop(wm8994->hubs.component);
dev_dbg(dev, "Reporting open circuit\n");
wm8994->jack_mic = false;
wm8994->mic_detecting = true;
- wm8958_micd_set_rate(wm8994->hubs.codec);
+ wm8958_micd_set_rate(wm8994->hubs.component);
snd_soc_jack_report(wm8994->micdet[0].jack, 0,
wm8994->btn_mask |
@@ -3572,13 +3724,13 @@ static void wm8958_open_circuit_work(struct work_struct *work)
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);
+ struct snd_soc_component *component = data;
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
/* Either nothing present or just starting detection */
if (!(status & WM8958_MICD_STS)) {
/* If nothing present then clear our statuses */
- dev_dbg(codec->dev, "Detected open circuit\n");
+ dev_dbg(component->dev, "Detected open circuit\n");
queue_delayed_work(system_power_efficient_wq,
&wm8994->open_circuit_work,
@@ -3590,12 +3742,12 @@ static void wm8958_mic_id(void *data, u16 status)
* microphone.
*/
if (status & 0x600) {
- dev_dbg(codec->dev, "Detected microphone\n");
+ dev_dbg(component->dev, "Detected microphone\n");
wm8994->mic_detecting = false;
wm8994->jack_mic = true;
- wm8958_micd_set_rate(codec);
+ wm8958_micd_set_rate(component);
snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET,
SND_JACK_HEADSET);
@@ -3603,13 +3755,13 @@ static void wm8958_mic_id(void *data, u16 status)
if (status & 0xfc) {
- dev_dbg(codec->dev, "Detected headphone\n");
+ dev_dbg(component->dev, "Detected headphone\n");
wm8994->mic_detecting = false;
- wm8958_micd_set_rate(codec);
+ wm8958_micd_set_rate(component);
/* If we have jackdet that will detect removal */
- wm1811_micd_stop(codec);
+ wm1811_micd_stop(component);
snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
SND_JACK_HEADSET);
@@ -3622,10 +3774,10 @@ 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;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = wm8994->hubs.component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
/* If required for an external cap force MICBIAS on */
if (control->pdata.jd_ext_cap) {
@@ -3635,7 +3787,7 @@ static void wm1811_mic_work(struct work_struct *work)
mutex_lock(&wm8994->accdet_lock);
- dev_dbg(codec->dev, "Starting mic detection\n");
+ dev_dbg(component->dev, "Starting mic detection\n");
/* Use a user-supplied callback if we have one */
if (wm8994->micd_cb) {
@@ -3646,54 +3798,54 @@ static void wm1811_mic_work(struct work_struct *work)
* what's actually there.
*/
wm8994->mic_detecting = true;
- wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_MIC);
+ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_MIC);
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, WM8958_MICD_ENA);
}
mutex_unlock(&wm8994->accdet_lock);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
}
static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
struct wm8994 *control = wm8994->wm8994;
- struct snd_soc_codec *codec = wm8994->hubs.codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = wm8994->hubs.component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int reg, delay;
bool present;
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
cancel_delayed_work_sync(&wm8994->mic_complete_work);
mutex_lock(&wm8994->accdet_lock);
- reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
+ reg = snd_soc_component_read(component, WM1811_JACKDET_CTRL);
if (reg < 0) {
- dev_err(codec->dev, "Failed to read jack status: %d\n", reg);
+ dev_err(component->dev, "Failed to read jack status: %d\n", reg);
mutex_unlock(&wm8994->accdet_lock);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
return IRQ_NONE;
}
- dev_dbg(codec->dev, "JACKDET %x\n", reg);
+ dev_dbg(component->dev, "JACKDET %x\n", reg);
present = reg & WM1811_JACKDET_LVL;
if (present) {
- dev_dbg(codec->dev, "Jack detected\n");
+ dev_dbg(component->dev, "Jack detected\n");
- wm8958_micd_set_rate(codec);
+ wm8958_micd_set_rate(component);
- snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_DISCH, 0);
/* Disable debounce while inserted */
- snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ snd_soc_component_update_bits(component, WM1811_JACKDET_CTRL,
WM1811_JACKDET_DB, 0);
delay = control->pdata.micdet_delay;
@@ -3701,22 +3853,27 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
&wm8994->mic_work,
msecs_to_jiffies(delay));
} else {
- dev_dbg(codec->dev, "Jack not detected\n");
+ dev_dbg(component->dev, "Jack not detected\n");
+ /* Release wm8994->accdet_lock to avoid deadlock:
+ * cancel_delayed_work_sync() takes wm8994->mic_work internal
+ * lock and wm1811_mic_work takes wm8994->accdet_lock */
+ mutex_unlock(&wm8994->accdet_lock);
cancel_delayed_work_sync(&wm8994->mic_work);
+ mutex_lock(&wm8994->accdet_lock);
- snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_DISCH, WM8958_MICB2_DISCH);
/* Enable debounce while removed */
- snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ snd_soc_component_update_bits(component, WM1811_JACKDET_CTRL,
WM1811_JACKDET_DB, WM1811_JACKDET_DB);
wm8994->mic_detecting = false;
wm8994->jack_mic = false;
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, 0);
- wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
+ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_JACK);
}
mutex_unlock(&wm8994->accdet_lock);
@@ -3737,7 +3894,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
* avoid bootstrapping issues with the core. */
snd_soc_jack_report(wm8994->micdet[0].jack, 0, 0);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
return IRQ_HANDLED;
}
@@ -3752,8 +3909,12 @@ static void wm1811_jackdet_bootstrap(struct work_struct *work)
/**
* wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ
*
- * @codec: WM8958 codec
+ * @component: WM8958 component
* @jack: jack to report detection events on
+ * @det_cb: detection callback
+ * @det_cb_data: data for detection callback
+ * @id_cb: mic id callback
+ * @id_cb_data: data for mic id callback
*
* Enable microphone detection functionality for the WM8958. By
* default simple detection which supports the detection of up to 6
@@ -3765,12 +3926,12 @@ static void wm1811_jackdet_bootstrap(struct work_struct *work)
* 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,
+int wm8958_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack,
wm1811_micdet_cb det_cb, void *det_cb_data,
wm1811_mic_id_cb id_cb, void *id_cb_data)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
u16 micd_lvl_sel;
@@ -3782,6 +3943,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
return -EINVAL;
}
+ pm_runtime_get_sync(component->dev);
+
if (jack) {
snd_soc_dapm_force_enable_pin(dapm, "CLK_SYS");
snd_soc_dapm_sync(dapm);
@@ -3801,10 +3964,10 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
wm8994->mic_id_cb_data = id_cb_data;
} else {
wm8994->mic_id_cb = wm8958_mic_id;
- wm8994->mic_id_cb_data = codec;
+ wm8994->mic_id_cb_data = component;
}
- wm8958_micd_set_rate(codec);
+ wm8958_micd_set_rate(component);
/* Detect microphones and short circuits by default */
if (control->pdata.micd_lvl_sel)
@@ -3816,10 +3979,10 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
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,
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_2,
WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel);
- WARN_ON(snd_soc_codec_get_bias_level(codec) > SND_SOC_BIAS_STANDBY);
+ WARN_ON(snd_soc_dapm_get_bias_level(dapm) > SND_SOC_BIAS_STANDBY);
/*
* If we can use jack detection start off with that,
@@ -3827,29 +3990,31 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
*/
if (wm8994->jackdet) {
/* Disable debounce for the initial detect */
- snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ snd_soc_component_update_bits(component, WM1811_JACKDET_CTRL,
WM1811_JACKDET_DB, 0);
- snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_DISCH,
WM8958_MICB2_DISCH);
- snd_soc_update_bits(codec, WM8994_LDO_1,
+ snd_soc_component_update_bits(component, WM8994_LDO_1,
WM8994_LDO1_DISCH, 0);
- wm1811_jackdet_set_mode(codec,
+ wm1811_jackdet_set_mode(component,
WM1811_JACKDET_MODE_JACK);
} else {
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, WM8958_MICD_ENA);
}
} else {
- snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, 0);
- wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE);
+ wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_NONE);
snd_soc_dapm_disable_pin(dapm, "CLK_SYS");
snd_soc_dapm_sync(dapm);
}
+ pm_runtime_put(component->dev);
+
return 0;
}
EXPORT_SYMBOL_GPL(wm8958_mic_detect);
@@ -3859,9 +4024,9 @@ 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;
+ struct snd_soc_component *component = wm8994->hubs.component;
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
mutex_lock(&wm8994->accdet_lock);
@@ -3869,13 +4034,13 @@ static void wm8958_mic_work(struct work_struct *work)
mutex_unlock(&wm8994->accdet_lock);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
}
static irqreturn_t wm8958_mic_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
- struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct snd_soc_component *component = wm8994->hubs.component;
int reg, count, ret, id_delay;
/*
@@ -3883,30 +4048,30 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
* 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))
+ if (!(snd_soc_component_read(component, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
return IRQ_HANDLED;
cancel_delayed_work_sync(&wm8994->mic_complete_work);
cancel_delayed_work_sync(&wm8994->open_circuit_work);
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
/* We may occasionally read a detection without an impedence
* range being provided - if that happens loop again.
*/
count = 10;
do {
- reg = snd_soc_read(codec, WM8958_MIC_DETECT_3);
+ reg = snd_soc_component_read(component, WM8958_MIC_DETECT_3);
if (reg < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to read mic detect status: %d\n",
reg);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
return IRQ_NONE;
}
if (!(reg & WM8958_MICD_VALID)) {
- dev_dbg(codec->dev, "Mic detect data not valid\n");
+ dev_dbg(component->dev, "Mic detect data not valid\n");
goto out;
}
@@ -3917,20 +4082,20 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
} while (count--);
if (count == 0)
- dev_warn(codec->dev, "No impedance range reported for jack\n");
+ dev_warn(component->dev, "No impedance range reported for jack\n");
#ifndef CONFIG_SND_SOC_WM8994_MODULE
- trace_snd_soc_jack_irq(dev_name(codec->dev));
+ trace_snd_soc_jack_irq(dev_name(component->dev));
#endif
/* Avoid a transient report when the accessory is being removed */
if (wm8994->jackdet) {
- ret = snd_soc_read(codec, WM1811_JACKDET_CTRL);
+ ret = snd_soc_component_read(component, WM1811_JACKDET_CTRL);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read jack status: %d\n",
+ dev_err(component->dev, "Failed to read jack status: %d\n",
ret);
} else if (!(ret & WM1811_JACKDET_LVL)) {
- dev_dbg(codec->dev, "Ignoring removed jack\n");
+ dev_dbg(component->dev, "Ignoring removed jack\n");
goto out;
}
} else if (!(reg & WM8958_MICD_STS)) {
@@ -3949,49 +4114,51 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
&wm8994->mic_complete_work,
msecs_to_jiffies(id_delay));
else
- wm8958_button_det(codec, reg);
+ wm8958_button_det(component, reg);
out:
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
return IRQ_HANDLED;
}
static irqreturn_t wm8994_fifo_error(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
+ struct snd_soc_component *component = data;
- dev_err(codec->dev, "FIFO error\n");
+ dev_err(component->dev, "FIFO error\n");
return IRQ_HANDLED;
}
static irqreturn_t wm8994_temp_warn(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
+ struct snd_soc_component *component = data;
- dev_err(codec->dev, "Thermal warning\n");
+ dev_err(component->dev, "Thermal warning\n");
return IRQ_HANDLED;
}
static irqreturn_t wm8994_temp_shut(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
+ struct snd_soc_component *component = data;
- dev_crit(codec->dev, "Thermal shutdown\n");
+ dev_crit(component->dev, "Thermal shutdown\n");
return IRQ_HANDLED;
}
-static int wm8994_codec_probe(struct snd_soc_codec *codec)
+static int wm8994_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(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 = snd_soc_component_to_dapm(component);
+ struct wm8994 *control = dev_get_drvdata(component->dev->parent);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
unsigned int reg;
int ret, i;
- wm8994->hubs.codec = codec;
+ snd_soc_component_init_regmap(component, control->regmap);
+
+ wm8994->hubs.component = component;
mutex_init(&wm8994->accdet_lock);
INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
@@ -4017,8 +4184,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->micdet_irq = control->pdata.micdet_irq;
- /* By default use idle_bias_off, will override for WM8994 */
- dapm->idle_bias_off = 1;
+ /* By default use idle_bias false, will override for WM8994 */
+ snd_soc_dapm_set_idle_bias(dapm, false);
/* Set revision-specific configuration */
switch (control->type) {
@@ -4026,7 +4193,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
/* Single ended line outputs should have VMID on. */
if (!control->pdata.lineout1_diff ||
!control->pdata.lineout2_diff)
- dapm->idle_bias_off = 0;
+ snd_soc_dapm_set_idle_bias(dapm, true);
switch (control->revision) {
case 2:
@@ -4041,11 +4208,13 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->hubs.dcs_readback_mode = 2;
break;
}
+ wm8994->hubs.micd_scthr = true;
break;
case WM8958:
wm8994->hubs.dcs_readback_mode = 1;
wm8994->hubs.hp_startup_mode = 1;
+ wm8994->hubs.micd_scthr = true;
switch (control->revision) {
case 0:
@@ -4066,7 +4235,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->hubs.dcs_codes_l = -9;
wm8994->hubs.dcs_codes_r = -7;
- snd_soc_update_bits(codec, WM8994_ANALOGUE_HP_1,
+ snd_soc_component_update_bits(component, WM8994_ANALOGUE_HP_1,
WM1811_HPOUT1_ATTN, WM1811_HPOUT1_ATTN);
break;
@@ -4075,11 +4244,11 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR,
- wm8994_fifo_error, "FIFO error", codec);
+ wm8994_fifo_error, "FIFO error", component);
wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN,
- wm8994_temp_warn, "Thermal warning", codec);
+ wm8994_temp_warn, "Thermal warning", component);
wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT,
- wm8994_temp_shut, "Thermal shutdown", codec);
+ wm8994_temp_shut, "Thermal shutdown", component);
switch (control->type) {
case WM8994:
@@ -4097,7 +4266,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994);
if (ret != 0)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Failed to request Mic1 detect IRQ: %d\n",
ret);
@@ -4107,7 +4276,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994_mic_irq, "Mic 1 short",
wm8994);
if (ret != 0)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Failed to request Mic1 short IRQ: %d\n",
ret);
@@ -4116,7 +4285,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994_mic_irq, "Mic 2 detect",
wm8994);
if (ret != 0)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Failed to request Mic2 detect IRQ: %d\n",
ret);
@@ -4125,7 +4294,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994_mic_irq, "Mic 2 short",
wm8994);
if (ret != 0)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Failed to request Mic2 short IRQ: %d\n",
ret);
break;
@@ -4140,7 +4309,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
"Mic detect",
wm8994);
if (ret != 0)
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Failed to request Mic detect IRQ: %d\n",
ret);
} else {
@@ -4176,7 +4345,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
/* Make sure we can read from the GPIOs if they're inputs */
- pm_runtime_get_sync(codec->dev);
+ pm_runtime_get_sync(component->dev);
/* Remember if AIFnLRCLK is configured as a GPIO. This should be
* configured on init - if a system wants to do this dynamically
@@ -4184,44 +4353,52 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
*/
ret = regmap_read(control->regmap, WM8994_GPIO_1, &reg);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read GPIO1 state: %d\n", ret);
+ dev_err(component->dev, "Failed to read GPIO1 state: %d\n", ret);
goto err_irq;
}
if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
wm8994->lrclk_shared[0] = 1;
- wm8994_dai[0].symmetric_rates = 1;
+ wm8994_dai[0].symmetric_rate = 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);
+ dev_err(component->dev, "Failed to read GPIO6 state: %d\n", ret);
goto err_irq;
}
if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
wm8994->lrclk_shared[1] = 1;
- wm8994_dai[1].symmetric_rates = 1;
+ wm8994_dai[1].symmetric_rate = 1;
} else {
wm8994->lrclk_shared[1] = 0;
}
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->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,
+ snd_soc_component_update_bits(component, wm8994_vu_bits[i].reg,
wm8994_vu_bits[i].mask,
wm8994_vu_bits[i].mask);
+ if (control->type != WM1811) {
+ for (i = 0; i < ARRAY_SIZE(wm8994_adc2_dac2_vu_bits); i++)
+ snd_soc_component_update_bits(component,
+ wm8994_adc2_dac2_vu_bits[i].reg,
+ wm8994_adc2_dac2_vu_bits[i].mask,
+ wm8994_adc2_dac2_vu_bits[i].mask);
+ }
+
/* Set the low bit of the 3D stereo depth so TLV matches */
- snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_2,
+ snd_soc_component_update_bits(component, WM8994_AIF1_DAC1_FILTERS_2,
1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT,
1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT);
- snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_2,
+ snd_soc_component_update_bits(component, WM8994_AIF1_DAC2_FILTERS_2,
1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT,
1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT);
- snd_soc_update_bits(codec, WM8994_AIF2_DAC_FILTERS_2,
+ snd_soc_component_update_bits(component, WM8994_AIF2_DAC_FILTERS_2,
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
@@ -4231,7 +4408,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
switch (control->type) {
case WM8994:
case WM8958:
- snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8994_AIF1_CONTROL_1,
WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
break;
default:
@@ -4242,9 +4419,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
switch (control->type) {
case WM8958:
case WM1811:
- snd_soc_update_bits(codec, WM8958_MICBIAS1,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS1,
WM8958_MICB1_MODE, WM8958_MICB1_MODE);
- snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_MODE, WM8958_MICB2_MODE);
break;
default:
@@ -4252,18 +4429,20 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital;
- wm_hubs_update_class_w(codec);
+ wm_hubs_update_class_w(component);
wm8994_handle_pdata(wm8994);
- wm_hubs_add_analogue_controls(codec);
- snd_soc_add_codec_controls(codec, wm8994_snd_controls,
- ARRAY_SIZE(wm8994_snd_controls));
+ wm_hubs_add_analogue_controls(component);
+ snd_soc_add_component_controls(component, wm8994_common_snd_controls,
+ ARRAY_SIZE(wm8994_common_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets,
ARRAY_SIZE(wm8994_dapm_widgets));
switch (control->type) {
case WM8994:
+ snd_soc_add_component_controls(component, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
ARRAY_SIZE(wm8994_specific_dapm_widgets));
if (control->revision < 4) {
@@ -4283,8 +4462,10 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
break;
case WM8958:
- snd_soc_add_codec_controls(codec, wm8958_snd_controls,
- ARRAY_SIZE(wm8958_snd_controls));
+ snd_soc_add_component_controls(component, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
+ snd_soc_add_component_controls(component, wm8958_snd_controls,
+ ARRAY_SIZE(wm8958_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
if (control->revision < 1) {
@@ -4305,7 +4486,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
case WM1811:
- snd_soc_add_codec_controls(codec, wm8958_snd_controls,
+ snd_soc_add_component_controls(component, wm8958_snd_controls,
ARRAY_SIZE(wm8958_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
@@ -4318,7 +4499,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
- wm_hubs_add_analogue_routes(codec, 0, 0);
+ wm_hubs_add_analogue_routes(component, 0, 0);
ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
wm_hubs_dcs_done, "DC servo done",
&wm8994->hubs);
@@ -4356,7 +4537,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(wm8958_intercon));
}
- wm8958_dsp2_init(codec);
+ wm8958_dsp2_init(component);
break;
case WM1811:
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
@@ -4381,16 +4562,16 @@ err_irq:
&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);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, component);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, component);
return ret;
}
-static int wm8994_codec_remove(struct snd_soc_codec *codec)
+static void wm8994_component_remove(struct snd_soc_component *component)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int i;
@@ -4400,9 +4581,9 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
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);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, component);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, component);
if (wm8994->jackdet)
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994);
@@ -4429,28 +4610,23 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
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 const struct snd_soc_component_driver soc_component_dev_wm8994 = {
+ .probe = wm8994_component_probe,
+ .remove = wm8994_component_remove,
+ .suspend = wm8994_component_suspend,
+ .resume = wm8994_component_resume,
+ .set_bias_level = wm8994_set_bias_level,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm8994_probe(struct platform_device *pdev)
{
struct wm8994_priv *wm8994;
+ int ret;
wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv),
GFP_KERNEL);
@@ -4462,22 +4638,32 @@ static int wm8994_probe(struct platform_device *pdev)
wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
+ wm8994->mclk[WM8994_MCLK1].id = "MCLK1";
+ wm8994->mclk[WM8994_MCLK2].id = "MCLK2";
+
+ ret = devm_clk_bulk_get_optional(pdev->dev.parent, ARRAY_SIZE(wm8994->mclk),
+ wm8994->mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get clocks: %d\n", ret);
+ return ret;
+ }
+
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_wm8994,
wm8994_dai, ARRAY_SIZE(wm8994_dai));
+ if (ret < 0)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
}
-static int wm8994_remove(struct platform_device *pdev)
+static void 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);
@@ -4502,16 +4688,15 @@ static int wm8994_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops wm8994_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume)
+ SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume)
};
static struct platform_driver wm8994_codec_driver = {
.driver = {
.name = "wm8994-codec",
- .pm = &wm8994_pm_ops,
+ .pm = pm_ptr(&wm8994_pm_ops),
},
.probe = wm8994_probe,
.remove = wm8994_remove,
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index dd73387b1cc4..b28398aa9e48 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -1,14 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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 <linux/clk.h>
#include <sound/soc.h>
#include <linux/firmware.h>
#include <linux/completion.h>
@@ -17,6 +15,12 @@
#include "wm_hubs.h"
+enum {
+ WM8994_MCLK1,
+ WM8994_MCLK2,
+ WM8994_NUM_MCLK
+};
+
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
#define WM8994_SYSCLK_MCLK2 2
@@ -43,18 +47,18 @@ enum wm8994_vmid_mode {
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 wm8994_mic_detect(struct snd_soc_component *component, 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,
+int wm8958_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack,
+ wm1811_micdet_cb det_cb, void *det_cb_data,
wm1811_mic_id_cb id_cb, void *id_cb_data);
-int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode);
+int wm8994_vmid_mode(struct snd_soc_component *component, enum wm8994_vmid_mode mode);
int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-void wm8958_dsp2_init(struct snd_soc_codec *codec);
+void wm8958_dsp2_init(struct snd_soc_component *component);
struct wm8994_micdet {
struct snd_soc_jack *jack;
@@ -76,9 +80,10 @@ struct wm8994;
struct wm8994_priv {
struct wm_hubs_data hubs;
struct wm8994 *wm8994;
+ struct clk_bulk_data mclk[WM8994_NUM_MCLK];
int sysclk[2];
int sysclk_rate[2];
- int mclk[2];
+ int mclk_rate[2];
int aifclk[2];
int aifdiv[2];
int channels[2];
@@ -101,33 +106,33 @@ struct wm8994_priv {
int vss_ena[3];
int enh_eq_ena[3];
- /* Platform dependant DRC configuration */
+ /* Platform dependent DRC configuration */
const char **drc_texts;
int drc_cfg[WM8994_NUM_DRC];
struct soc_enum drc_enum;
- /* Platform dependant ReTune mobile configuration */
+ /* Platform dependent 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 */
+ /* Platform dependent MBC configuration */
int mbc_cfg;
const char **mbc_texts;
struct soc_enum mbc_enum;
- /* Platform dependant VSS configuration */
+ /* Platform dependent VSS configuration */
int vss_cfg;
const char **vss_texts;
struct soc_enum vss_enum;
- /* Platform dependant VSS HPF configuration */
+ /* Platform dependent VSS HPF configuration */
int vss_hpf_cfg;
const char **vss_hpf_texts;
struct soc_enum vss_hpf_enum;
- /* Platform dependant enhanced EQ configuration */
+ /* Platform dependent enhanced EQ configuration */
int enh_eq_cfg;
const char **enh_eq_texts;
struct soc_enum enh_eq_enum;
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 24500bafb0a8..104ce09c02e0 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8995.c -- WM8995 ALSA SoC Audio driver
*
@@ -6,10 +7,6 @@
* 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>
@@ -385,7 +382,7 @@ struct wm8995_priv {
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;
+ struct snd_soc_component *component;
};
/*
@@ -485,48 +482,48 @@ static const struct snd_kcontrol_new wm8995_snd_controls[] = {
WM8995_AIF2_ADC_RIGHT_VOLUME, 0, 96, 0, digital_tlv)
};
-static void wm8995_update_class_w(struct snd_soc_codec *codec)
+static void wm8995_update_class_w(struct snd_soc_component *component)
{
int enable = 1;
int source = 0; /* GCC flow analysis can't track enable */
int reg, reg_r;
/* We also need the same setting for L/R and only one path */
- reg = snd_soc_read(codec, WM8995_DAC1_LEFT_MIXER_ROUTING);
+ reg = snd_soc_component_read(component, WM8995_DAC1_LEFT_MIXER_ROUTING);
switch (reg) {
case WM8995_AIF2DACL_TO_DAC1L:
- dev_dbg(codec->dev, "Class W source AIF2DAC\n");
+ dev_dbg(component->dev, "Class W source AIF2DAC\n");
source = 2 << WM8995_CP_DYN_SRC_SEL_SHIFT;
break;
case WM8995_AIF1DAC2L_TO_DAC1L:
- dev_dbg(codec->dev, "Class W source AIF1DAC2\n");
+ dev_dbg(component->dev, "Class W source AIF1DAC2\n");
source = 1 << WM8995_CP_DYN_SRC_SEL_SHIFT;
break;
case WM8995_AIF1DAC1L_TO_DAC1L:
- dev_dbg(codec->dev, "Class W source AIF1DAC1\n");
+ dev_dbg(component->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);
+ dev_dbg(component->dev, "DAC mixer setting: %x\n", reg);
enable = 0;
break;
}
- reg_r = snd_soc_read(codec, WM8995_DAC1_RIGHT_MIXER_ROUTING);
+ reg_r = snd_soc_component_read(component, WM8995_DAC1_RIGHT_MIXER_ROUTING);
if (reg_r != reg) {
- dev_dbg(codec->dev, "Left and right DAC mixers different\n");
+ dev_dbg(component->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,
+ dev_dbg(component->dev, "Class W enabled\n");
+ snd_soc_component_update_bits(component, WM8995_CLASS_W_1,
WM8995_CP_DYN_PWR_MASK |
WM8995_CP_DYN_SRC_SEL_MASK,
source | WM8995_CP_DYN_PWR);
} else {
- dev_dbg(codec->dev, "Class W disabled\n");
- snd_soc_update_bits(codec, WM8995_CLASS_W_1,
+ dev_dbg(component->dev, "Class W disabled\n");
+ snd_soc_component_update_bits(component, WM8995_CLASS_W_1,
WM8995_CP_DYN_PWR_MASK, 0);
}
}
@@ -534,53 +531,53 @@ static void wm8995_update_class_w(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
const char *clk;
- reg = snd_soc_read(codec, WM8995_CLOCKING_1);
+ reg = snd_soc_component_read(component, WM8995_CLOCKING_1);
/* Check what we're currently using for CLK_SYS */
if (reg & WM8995_SYSCLK_SRC)
clk = "AIF2CLK";
else
clk = "AIF1CLK";
- return !strcmp(source->name, clk);
+ return !snd_soc_dapm_widget_name_cmp(source, 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);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
- wm8995_update_class_w(codec);
+ wm8995_update_class_w(component);
return ret;
}
static int hp_supply_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Enable the headphone amp */
- snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1,
WM8995_HPOUT1L_ENA_MASK |
WM8995_HPOUT1R_ENA_MASK,
WM8995_HPOUT1L_ENA |
WM8995_HPOUT1R_ENA);
/* Enable the second stage */
- snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1,
+ snd_soc_component_update_bits(component, WM8995_ANALOGUE_HP_1,
WM8995_HPOUT1L_DLY_MASK |
WM8995_HPOUT1R_DLY_MASK,
WM8995_HPOUT1L_DLY |
WM8995_HPOUT1R_DLY);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, WM8995_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, WM8995_CHARGE_PUMP_1,
WM8995_CP_ENA_MASK, 0);
break;
}
@@ -588,41 +585,41 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static void dc_servo_cmd(struct snd_soc_codec *codec,
+static void dc_servo_cmd(struct snd_soc_component *component,
unsigned int reg, unsigned int val, unsigned int mask)
{
int timeout = 10;
- dev_dbg(codec->dev, "%s: reg = %#x, val = %#x, mask = %#x\n",
+ dev_dbg(component->dev, "%s: reg = %#x, val = %#x, mask = %#x\n",
__func__, reg, val, mask);
- snd_soc_write(codec, reg, val);
+ snd_soc_component_write(component, reg, val);
while (timeout--) {
msleep(10);
- val = snd_soc_read(codec, WM8995_DC_SERVO_READBACK_0);
+ val = snd_soc_component_read(component, WM8995_DC_SERVO_READBACK_0);
if ((val & mask) == mask)
return;
}
- dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+ dev_err(component->dev, "Timed out waiting for DC Servo\n");
}
static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, WM8995_ANALOGUE_HP_1);
+ reg = snd_soc_component_read(component, WM8995_ANALOGUE_HP_1);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, WM8995_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, WM8995_CHARGE_PUMP_1,
WM8995_CP_ENA_MASK, WM8995_CP_ENA);
msleep(5);
- snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1,
WM8995_HPOUT1L_ENA_MASK |
WM8995_HPOUT1R_ENA_MASK,
WM8995_HPOUT1L_ENA | WM8995_HPOUT1R_ENA);
@@ -630,12 +627,12 @@ static int hp_event(struct snd_soc_dapm_widget *w,
udelay(20);
reg |= WM8995_HPOUT1L_DLY | WM8995_HPOUT1R_DLY;
- snd_soc_write(codec, WM8995_ANALOGUE_HP_1, reg);
+ snd_soc_component_write(component, WM8995_ANALOGUE_HP_1, reg);
- snd_soc_write(codec, WM8995_DC_SERVO_1, WM8995_DCS_ENA_CHAN_0 |
+ snd_soc_component_write(component, WM8995_DC_SERVO_1, WM8995_DCS_ENA_CHAN_0 |
WM8995_DCS_ENA_CHAN_1);
- dc_servo_cmd(codec, WM8995_DC_SERVO_2,
+ dc_servo_cmd(component, WM8995_DC_SERVO_2,
WM8995_DCS_TRIG_STARTUP_0 |
WM8995_DCS_TRIG_STARTUP_1,
WM8995_DCS_TRIG_DAC_WR_0 |
@@ -643,23 +640,23 @@ static int hp_event(struct snd_soc_dapm_widget *w,
reg |= WM8995_HPOUT1R_OUTP | WM8995_HPOUT1R_RMV_SHORT |
WM8995_HPOUT1L_OUTP | WM8995_HPOUT1L_RMV_SHORT;
- snd_soc_write(codec, WM8995_ANALOGUE_HP_1, reg);
+ snd_soc_component_write(component, WM8995_ANALOGUE_HP_1, reg);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1,
+ snd_soc_component_update_bits(component, WM8995_ANALOGUE_HP_1,
WM8995_HPOUT1L_OUTP_MASK |
WM8995_HPOUT1R_OUTP_MASK |
WM8995_HPOUT1L_RMV_SHORT_MASK |
WM8995_HPOUT1R_RMV_SHORT_MASK, 0);
- snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1,
+ snd_soc_component_update_bits(component, WM8995_ANALOGUE_HP_1,
WM8995_HPOUT1L_DLY_MASK |
WM8995_HPOUT1R_DLY_MASK, 0);
- snd_soc_write(codec, WM8995_DC_SERVO_1, 0);
+ snd_soc_component_write(component, WM8995_DC_SERVO_1, 0);
- snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1,
WM8995_HPOUT1L_ENA_MASK |
WM8995_HPOUT1R_ENA_MASK,
0);
@@ -669,14 +666,14 @@ static int hp_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
+static int configure_aif_clock(struct snd_soc_component *component, int aif)
{
struct wm8995_priv *wm8995;
int rate;
int reg1 = 0;
int offset;
- wm8995 = snd_soc_codec_get_drvdata(codec);
+ wm8995 = snd_soc_component_get_drvdata(component);
if (aif)
offset = 4;
@@ -707,29 +704,29 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
rate /= 2;
reg1 |= WM8995_AIF1CLK_DIV;
- dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n",
+ dev_dbg(component->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,
+ snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1 + offset,
WM8995_AIF1CLK_SRC_MASK | WM8995_AIF1CLK_DIV_MASK,
reg1);
return 0;
}
-static int configure_clock(struct snd_soc_codec *codec)
+static int configure_clock(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wm8995_priv *wm8995;
int change, new;
- wm8995 = snd_soc_codec_get_drvdata(codec);
+ wm8995 = snd_soc_component_get_drvdata(component);
/* Bring up the AIF clocks first */
- configure_aif_clock(codec, 0);
- configure_aif_clock(codec, 1);
+ configure_aif_clock(component, 0);
+ configure_aif_clock(component, 1);
/*
* Then switch CLK_SYS over to the higher of them; a change
@@ -747,7 +744,7 @@ static int configure_clock(struct snd_soc_codec *codec)
else
new = 0;
- change = snd_soc_update_bits(codec, WM8995_CLOCKING_1,
+ change = snd_soc_component_update_bits(component, WM8995_CLOCKING_1,
WM8995_SYSCLK_SRC_MASK, new);
if (!change)
return 0;
@@ -760,14 +757,14 @@ static int configure_clock(struct snd_soc_codec *codec)
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return configure_clock(codec);
+ return configure_clock(component);
case SND_SOC_DAPM_POST_PMD:
- configure_clock(codec);
+ configure_clock(component);
break;
}
@@ -1420,9 +1417,9 @@ static bool wm8995_volatile(struct device *dev, unsigned int reg)
}
}
-static int wm8995_aif_mute(struct snd_soc_dai *dai, int mute)
+static int wm8995_aif_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int mute_reg;
switch (dai->id) {
@@ -1436,24 +1433,24 @@ static int wm8995_aif_mute(struct snd_soc_dai *dai, int mute)
return -EINVAL;
}
- snd_soc_update_bits(codec, mute_reg, WM8995_AIF1DAC1_MUTE_MASK,
+ snd_soc_component_update_bits(component, mute_reg, WM8995_AIF1DAC1_MUTE_MASK,
!!mute << WM8995_AIF1DAC1_MUTE_SHIFT);
return 0;
}
static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
int master;
int aif;
- codec = dai->codec;
+ component = dai->component;
master = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
master = WM8995_AIF1_MSTR;
break;
default:
@@ -1465,6 +1462,7 @@ static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif |= WM8995_AIF1_LRCLK_INV;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif |= (0x3 << WM8995_AIF1_FMT_SHIFT);
break;
@@ -1519,11 +1517,11 @@ static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, WM8995_AIF1_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8995_AIF1_CONTROL_1,
WM8995_AIF1_BCLK_INV_MASK |
WM8995_AIF1_LRCLK_INV_MASK |
WM8995_AIF1_FMT_MASK, aif);
- snd_soc_update_bits(codec, WM8995_AIF1_MASTER_SLAVE,
+ snd_soc_component_update_bits(component, WM8995_AIF1_MASTER_SLAVE,
WM8995_AIF1_MSTR_MASK, master);
return 0;
}
@@ -1546,7 +1544,7 @@ 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 snd_soc_component *component;
struct wm8995_priv *wm8995;
int aif1_reg;
int bclk_reg;
@@ -1557,8 +1555,8 @@ static int wm8995_hw_params(struct snd_pcm_substream *substream,
int lrclk, bclk;
int i, rate_val, best, best_val, cur_val;
- codec = dai->codec;
- wm8995 = snd_soc_codec_get_drvdata(codec);
+ component = dai->component;
+ wm8995 = snd_soc_component_get_drvdata(component);
switch (dai->id) {
case 0:
@@ -1570,7 +1568,7 @@ static int wm8995_hw_params(struct snd_pcm_substream *substream,
lrclk_reg = WM8995_AIF1DAC_LRCLK;
} else {
lrclk_reg = WM8995_AIF1ADC_LRCLK;
- dev_dbg(codec->dev, "AIF1 using split LRCLK\n");
+ dev_dbg(component->dev, "AIF1 using split LRCLK\n");
}
break;
case 1:
@@ -1582,7 +1580,7 @@ static int wm8995_hw_params(struct snd_pcm_substream *substream,
lrclk_reg = WM8995_AIF2DAC_LRCLK;
} else {
lrclk_reg = WM8995_AIF2ADC_LRCLK;
- dev_dbg(codec->dev, "AIF2 using split LRCLK\n");
+ dev_dbg(component->dev, "AIF2 using split LRCLK\n");
}
break;
default:
@@ -1668,13 +1666,13 @@ static int wm8995_hw_params(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
lrclk, bclk_rate / lrclk);
- snd_soc_update_bits(codec, aif1_reg,
+ snd_soc_component_update_bits(component, aif1_reg,
WM8995_AIF1_WL_MASK, aif1);
- snd_soc_update_bits(codec, bclk_reg,
+ snd_soc_component_update_bits(component, bclk_reg,
WM8995_AIF1_BCLK_DIV_MASK, bclk);
- snd_soc_update_bits(codec, lrclk_reg,
+ snd_soc_component_update_bits(component, lrclk_reg,
WM8995_AIF1DAC_RATE_MASK, lrclk);
- snd_soc_update_bits(codec, rate_reg,
+ snd_soc_component_update_bits(component, rate_reg,
WM8995_AIF1_SR_MASK |
WM8995_AIF1CLK_RATE_MASK, rate_val);
return 0;
@@ -1682,7 +1680,7 @@ static int wm8995_hw_params(struct snd_pcm_substream *substream,
static int wm8995_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int reg, val, mask;
switch (codec_dai->id) {
@@ -1707,7 +1705,7 @@ static int wm8995_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
else
val = 0;
- return snd_soc_update_bits(codec, reg, mask, val);
+ return snd_soc_component_update_bits(component, reg, mask, val);
}
/* The size in bits of the FLL divide multiplied by 10
@@ -1797,19 +1795,19 @@ 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 snd_soc_component *component;
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);
+ component = dai->component;
+ wm8995 = snd_soc_component_get_drvdata(component);
- aif1 = snd_soc_read(codec, WM8995_AIF1_CLOCKING_1)
+ aif1 = snd_soc_component_read(component, WM8995_AIF1_CLOCKING_1)
& WM8995_AIF1CLK_ENA;
- aif2 = snd_soc_read(codec, WM8995_AIF2_CLOCKING_1)
+ aif2 = snd_soc_component_read(component, WM8995_AIF2_CLOCKING_1)
& WM8995_AIF2CLK_ENA;
switch (id) {
@@ -1858,35 +1856,35 @@ static int wm8995_set_fll(struct snd_soc_dai *dai, int id,
return ret;
/* Gate the AIF clocks while we reclock */
- snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1,
WM8995_AIF1CLK_ENA_MASK, 0);
- snd_soc_update_bits(codec, WM8995_AIF2_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8995_AIF2_CLOCKING_1,
WM8995_AIF2CLK_ENA_MASK, 0);
/* We always need to disable the FLL while reconfiguring */
- snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_1 + reg_offset,
+ snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_1 + reg_offset,
WM8995_FLL1_ENA_MASK, 0);
reg = (fll.outdiv << WM8995_FLL1_OUTDIV_SHIFT) |
(fll.fll_fratio << WM8995_FLL1_FRATIO_SHIFT);
- snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_2 + reg_offset,
+ snd_soc_component_update_bits(component, 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_component_write(component, WM8995_FLL1_CONTROL_3 + reg_offset, fll.k);
- snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_4 + reg_offset,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_5 + reg_offset,
WM8995_FLL1_REFCLK_DIV_MASK |
WM8995_FLL1_REFCLK_SRC_MASK,
(fll.clk_ref_div << WM8995_FLL1_REFCLK_DIV_SHIFT) |
(src - 1));
if (freq_out)
- snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_1 + reg_offset,
+ snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_1 + reg_offset,
WM8995_FLL1_ENA_MASK, WM8995_FLL1_ENA);
wm8995->fll[id].in = freq_in;
@@ -1894,12 +1892,12 @@ static int wm8995_set_fll(struct snd_soc_dai *dai, int id,
wm8995->fll[id].src = src;
/* Enable any gated AIF clocks */
- snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1,
WM8995_AIF1CLK_ENA_MASK, aif1);
- snd_soc_update_bits(codec, WM8995_AIF2_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8995_AIF2_CLOCKING_1,
WM8995_AIF2CLK_ENA_MASK, aif2);
- configure_clock(codec);
+ configure_clock(component);
return 0;
}
@@ -1907,11 +1905,11 @@ static int wm8995_set_fll(struct snd_soc_dai *dai, int id,
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 snd_soc_component *component;
struct wm8995_priv *wm8995;
- codec = dai->codec;
- wm8995 = snd_soc_codec_get_drvdata(codec);
+ component = dai->component;
+ wm8995 = snd_soc_component_get_drvdata(component);
switch (dai->id) {
case 0:
@@ -1949,24 +1947,25 @@ static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai,
return -EINVAL;
}
- configure_clock(codec);
+ configure_clock(component);
return 0;
}
-static int wm8995_set_bias_level(struct snd_soc_codec *codec,
+static int wm8995_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wm8995_priv *wm8995;
int ret;
- wm8995 = snd_soc_codec_get_drvdata(codec);
+ wm8995 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret)
@@ -1974,17 +1973,17 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
ret = regcache_sync(wm8995->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
- snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, WM8995_BG_ENA);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, 0);
regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
@@ -1994,37 +1993,23 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
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)
+static int wm8995_probe(struct snd_soc_component *component)
{
struct wm8995_priv *wm8995;
int i;
int ret;
- wm8995 = snd_soc_codec_get_drvdata(codec);
- wm8995->codec = codec;
+ wm8995 = snd_soc_component_get_drvdata(component);
+ wm8995->component = component;
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
wm8995->supplies[i].supply = wm8995_supply_names[i];
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
- wm8995->supplies);
+ ret = devm_regulator_bulk_get(component->dev,
+ ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
if (ret) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
@@ -2039,10 +2024,11 @@ static int wm8995_probe(struct snd_soc_codec *codec)
/* 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]);
+ ret = devm_regulator_register_notifier(
+ wm8995->supplies[i].consumer,
+ &wm8995->disable_nb[i]);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to register regulator notifier: %d\n",
ret);
}
@@ -2051,56 +2037,54 @@ static int wm8995_probe(struct snd_soc_codec *codec)
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;
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
}
- ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
+ ret = snd_soc_component_read(component, WM8995_SOFTWARE_RESET);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
+ dev_err(component->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);
+ dev_err(component->dev, "Invalid device ID: %#x\n", ret);
ret = -EINVAL;
goto err_reg_enable;
}
- ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
+ ret = snd_soc_component_write(component, WM8995_SOFTWARE_RESET, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ dev_err(component->dev, "Failed to issue reset: %d\n", ret);
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,
+ snd_soc_component_update_bits(component, WM8995_AIF1_DAC1_RIGHT_VOLUME,
WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU);
- snd_soc_update_bits(codec, WM8995_AIF1_DAC2_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_AIF1_DAC2_RIGHT_VOLUME,
WM8995_AIF1DAC2_VU_MASK, WM8995_AIF1DAC2_VU);
- snd_soc_update_bits(codec, WM8995_AIF2_DAC_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_AIF2_DAC_RIGHT_VOLUME,
WM8995_AIF2DAC_VU_MASK, WM8995_AIF2DAC_VU);
- snd_soc_update_bits(codec, WM8995_AIF1_ADC1_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_AIF1_ADC1_RIGHT_VOLUME,
WM8995_AIF1ADC1_VU_MASK, WM8995_AIF1ADC1_VU);
- snd_soc_update_bits(codec, WM8995_AIF1_ADC2_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_AIF1_ADC2_RIGHT_VOLUME,
WM8995_AIF1ADC2_VU_MASK, WM8995_AIF1ADC2_VU);
- snd_soc_update_bits(codec, WM8995_AIF2_ADC_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_AIF2_ADC_RIGHT_VOLUME,
WM8995_AIF2ADC_VU_MASK, WM8995_AIF1ADC2_VU);
- snd_soc_update_bits(codec, WM8995_DAC1_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_DAC1_RIGHT_VOLUME,
WM8995_DAC1_VU_MASK, WM8995_DAC1_VU);
- snd_soc_update_bits(codec, WM8995_DAC2_RIGHT_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_DAC2_RIGHT_VOLUME,
WM8995_DAC2_VU_MASK, WM8995_DAC2_VU);
- snd_soc_update_bits(codec, WM8995_RIGHT_LINE_INPUT_1_VOLUME,
+ snd_soc_component_update_bits(component, WM8995_RIGHT_LINE_INPUT_1_VOLUME,
WM8995_IN1_VU_MASK, WM8995_IN1_VU);
- wm8995_update_class_w(codec);
+ wm8995_update_class_w(component);
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;
}
@@ -2111,18 +2095,20 @@ 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,
+ .mute_stream = wm8995_aif_mute,
.set_pll = wm8995_set_fll,
.set_tristate = wm8995_set_tristate,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8995_aif2_dai_ops = {
.set_sysclk = wm8995_set_dai_sysclk,
.set_fmt = wm8995_set_dai_fmt,
.hw_params = wm8995_hw_params,
- .digital_mute = wm8995_aif_mute,
+ .mute_stream = wm8995_aif_mute,
.set_pll = wm8995_set_fll,
.set_tristate = wm8995_set_tristate,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dai_ops wm8995_aif3_dai_ops = {
@@ -2186,18 +2172,17 @@ static struct snd_soc_dai_driver wm8995_dai[] = {
}
};
-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 snd_soc_component_driver soc_component_dev_wm8995 = {
+ .probe = wm8995_probe,
+ .set_bias_level = wm8995_set_bias_level,
+ .controls = wm8995_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8995_snd_controls),
+ .dapm_widgets = wm8995_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets),
+ .dapm_routes = wm8995_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8995_intercon),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm8995_regmap = {
@@ -2209,7 +2194,7 @@ static const struct regmap_config wm8995_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8995_reg_defaults),
.volatile_reg = wm8995_volatile,
.readable_reg = wm8995_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#if defined(CONFIG_SPI_MASTER)
@@ -2231,30 +2216,22 @@ static int wm8995_spi_probe(struct spi_device *spi)
return ret;
}
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8995, wm8995_dai,
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_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",
},
.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)
+static int wm8995_i2c_probe(struct i2c_client *i2c)
{
struct wm8995_priv *wm8995;
int ret;
@@ -2272,8 +2249,8 @@ static int wm8995_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8995, wm8995_dai,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8995, wm8995_dai,
ARRAY_SIZE(wm8995_dai));
if (ret < 0)
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
@@ -2281,14 +2258,8 @@ static int wm8995_i2c_probe(struct i2c_client *i2c,
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},
+ {"wm8995"},
{}
};
@@ -2299,7 +2270,6 @@ static struct i2c_driver wm8995_i2c_driver = {
.name = "wm8995",
},
.probe = wm8995_i2c_probe,
- .remove = wm8995_i2c_remove,
.id_table = wm8995_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h
index 508ad27fe2bb..5a3cc8aec20e 100644
--- a/sound/soc/codecs/wm8995.h
+++ b/sound/soc/codecs/wm8995.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 8d7d6c01a2f7..2d9cbf66f7d4 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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>
@@ -17,7 +13,8 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -52,9 +49,9 @@ static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
struct wm8996_priv {
struct device *dev;
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
- int ldo1ena;
+ struct gpio_desc *ldo_ena;
int sysclk;
int sysclk_src;
@@ -80,7 +77,7 @@ struct wm8996_priv {
int rx_rate[WM8996_AIFS];
int bclk_rate[WM8996_AIFS];
- /* Platform dependant ReTune mobile configuration */
+ /* Platform dependent ReTune mobile configuration */
int num_retune_mobile_texts;
const char **retune_mobile_texts;
int retune_mobile_cfg[2];
@@ -334,9 +331,9 @@ static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_cutoff,
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)
+static void wm8996_set_retune_mobile(struct snd_soc_component *component, int block)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
struct wm8996_pdata *pdata = &wm8996->pdata;
int base, best, best_val, save, i, cfg, iface;
@@ -346,7 +343,7 @@ static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
switch (block) {
case 0:
base = WM8996_DSP1_RX_EQ_GAINS_1;
- if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
+ if (snd_soc_component_read(component, WM8996_POWER_MANAGEMENT_8) &
WM8996_DSP1RX_SRC)
iface = 1;
else
@@ -354,7 +351,7 @@ static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
break;
case 1:
base = WM8996_DSP1_RX_EQ_GAINS_2;
- if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
+ if (snd_soc_component_read(component, WM8996_POWER_MANAGEMENT_8) &
WM8996_DSP2RX_SRC)
iface = 1;
else
@@ -380,7 +377,7 @@ static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
}
}
- dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
block,
pdata->retune_mobile_cfgs[best].name,
pdata->retune_mobile_cfgs[best].rate,
@@ -389,14 +386,14 @@ static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
/* The EQ will be disabled while reconfiguring it, remember the
* current configuration.
*/
- save = snd_soc_read(codec, base);
+ save = snd_soc_component_read(component, base);
save &= WM8996_DSP1RX_EQ_ENA;
for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
- snd_soc_update_bits(codec, base + i, 0xffff,
+ snd_soc_component_update_bits(component, base + i, 0xffff,
pdata->retune_mobile_cfgs[best].regs[i]);
- snd_soc_update_bits(codec, base, WM8996_DSP1RX_EQ_ENA, save);
+ snd_soc_component_update_bits(component, base, WM8996_DSP1RX_EQ_ENA, save);
}
/* Icky as hell but saves code duplication */
@@ -412,11 +409,11 @@ static int wm8996_get_retune_mobile_block(const char *name)
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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
struct wm8996_pdata *pdata = &wm8996->pdata;
int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (block < 0)
return block;
@@ -426,7 +423,7 @@ static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
wm8996->retune_mobile_cfg[block] = value;
- wm8996_set_retune_mobile(codec, block);
+ wm8996_set_retune_mobile(component, block);
return 0;
}
@@ -434,8 +431,8 @@ static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
if (block < 0)
@@ -574,40 +571,40 @@ 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)
+static void wm8996_bg_enable(struct snd_soc_component *component)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
wm8996->bg_ena++;
if (wm8996->bg_ena == 1) {
- snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8996_POWER_MANAGEMENT_1,
WM8996_BG_ENA, WM8996_BG_ENA);
msleep(2);
}
}
-static void wm8996_bg_disable(struct snd_soc_codec *codec)
+static void wm8996_bg_disable(struct snd_soc_component *component)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
wm8996->bg_ena--;
if (!wm8996->bg_ena)
- snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8996_POWER_MANAGEMENT_1,
WM8996_BG_ENA, 0);
}
static int bg_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
int ret = 0;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- wm8996_bg_enable(codec);
+ wm8996_bg_enable(component);
break;
case SND_SOC_DAPM_POST_PMD:
- wm8996_bg_disable(codec);
+ wm8996_bg_disable(component);
break;
default:
WARN(1, "Invalid event %d\n", event);
@@ -634,8 +631,8 @@ static int cp_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
/* Record which outputs we enabled */
switch (event) {
@@ -653,59 +650,58 @@ static int rmv_short_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
+static void wait_for_dc_servo(struct snd_soc_component *component, u16 mask)
{
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int ret;
- unsigned long timeout = 200;
+ unsigned long time_left = 200;
- snd_soc_write(codec, WM8996_DC_SERVO_2, mask);
+ snd_soc_component_write(component, WM8996_DC_SERVO_2, mask);
/* Use the interrupt if possible */
do {
if (i2c->irq) {
- timeout = wait_for_completion_timeout(&wm8996->dcs_done,
- msecs_to_jiffies(200));
- if (timeout == 0)
- dev_err(codec->dev, "DC servo timed out\n");
+ time_left = wait_for_completion_timeout(&wm8996->dcs_done,
+ msecs_to_jiffies(200));
+ if (time_left == 0)
+ dev_err(component->dev, "DC servo timed out\n");
} else {
msleep(1);
- timeout--;
+ time_left--;
}
- ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
- dev_dbg(codec->dev, "DC servo state: %x\n", ret);
- } while (timeout && ret & mask);
+ ret = snd_soc_component_read(component, WM8996_DC_SERVO_2);
+ dev_dbg(component->dev, "DC servo state: %x\n", ret);
+ } while (time_left && ret & mask);
- if (timeout == 0)
- dev_err(codec->dev, "DC servo timed out for %x\n", mask);
+ if (time_left == 0)
+ dev_err(component->dev, "DC servo timed out for %x\n", mask);
else
- dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
+ dev_dbg(component->dev, "DC servo complete for %x\n", mask);
}
-static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
+static void wm8996_seq_notifier(struct snd_soc_component *component,
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);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
u16 val, mask;
/* Complete any pending DC servo starts */
if (wm8996->dcs_pending) {
- dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ dev_dbg(component->dev, "Starting DC servo for %x\n",
wm8996->dcs_pending);
/* Trigger a startup sequence */
- wait_for_dc_servo(codec, wm8996->dcs_pending
+ wait_for_dc_servo(component, wm8996->dcs_pending
<< WM8996_DCS_TRIG_STARTUP_0_SHIFT);
wm8996->dcs_pending = 0;
}
if (wm8996->hpout_pending != wm8996->hpout_ena) {
- dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
+ dev_dbg(component->dev, "Applying RMV_SHORTs %x->%x\n",
wm8996->hpout_ena, wm8996->hpout_pending);
val = 0;
@@ -728,7 +724,7 @@ static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
WM8996_HPOUT1R_DLY;
}
- snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, mask, val);
+ snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_1, mask, val);
val = 0;
mask = 0;
@@ -750,7 +746,7 @@ static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
WM8996_HPOUT2R_DLY;
}
- snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_2, mask, val);
+ snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_2, mask, val);
wm8996->hpout_ena = wm8996->hpout_pending;
}
@@ -759,8 +755,8 @@ static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1498,9 +1494,9 @@ static bool wm8996_readable_register(struct device *dev, unsigned int reg)
case WM8996_RIGHT_PDM_SPEAKER:
case WM8996_PDM_SPEAKER_MUTE_SEQUENCE:
case WM8996_PDM_SPEAKER_VOLUME:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1522,9 +1518,9 @@ static bool wm8996_volatile_register(struct device *dev, unsigned int reg)
case WM8996_MIC_DETECT_3:
case WM8996_HEADPHONE_DETECT_1:
case WM8996_HEADPHONE_DETECT_2:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1532,9 +1528,9 @@ 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)
+static void wm8996_update_bclk(struct snd_soc_component *component)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int aif, best, cur_val, bclk_rate, bclk_reg, i;
/* Don't bother if we're in a low frequency idle mode that
@@ -1564,18 +1560,19 @@ static void wm8996_update_bclk(struct snd_soc_codec *codec)
best = i;
}
bclk_rate = wm8996->sysclk / bclk_divs[best];
- dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ dev_dbg(component->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
bclk_divs[best], bclk_rate);
- snd_soc_update_bits(codec, bclk_reg,
+ snd_soc_component_update_bits(component, bclk_reg,
WM8996_AIF1_BCLK_DIV_MASK, best);
}
}
-static int wm8996_set_bias_level(struct snd_soc_codec *codec,
+static int wm8996_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
switch (level) {
@@ -1583,26 +1580,26 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
/* Put the MICBIASes into regulating mode */
- snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ snd_soc_component_update_bits(component, WM8996_MICBIAS_1,
WM8996_MICB1_MODE, 0);
- snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ snd_soc_component_update_bits(component, WM8996_MICBIAS_2,
WM8996_MICB2_MODE, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
wm8996->supplies);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
}
- if (wm8996->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena,
- 1);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena,
+ 1);
msleep(5);
}
@@ -1611,16 +1608,16 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
}
/* Bypass the MICBIASes for lowest power */
- snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ snd_soc_component_update_bits(component, WM8996_MICBIAS_1,
WM8996_MICB1_MODE, WM8996_MICB1_MODE);
- snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ snd_soc_component_update_bits(component, WM8996_MICBIAS_2,
WM8996_MICB2_MODE, WM8996_MICB2_MODE);
break;
case SND_SOC_BIAS_OFF:
regcache_cache_only(wm8996->regmap, true);
- if (wm8996->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
regcache_cache_only(wm8996->regmap, true);
}
regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
@@ -1633,7 +1630,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int aifctrl = 0;
int bclk = 0;
int lrclk_tx = 0;
@@ -1676,16 +1673,16 @@ static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
bclk |= WM8996_AIF1_BCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
bclk |= WM8996_AIF1_BCLK_MSTR;
lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
@@ -1710,15 +1707,15 @@ static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, aifctrl_reg, WM8996_AIF1_FMT_MASK, aifctrl);
- snd_soc_update_bits(codec, bclk_reg,
+ snd_soc_component_update_bits(component, aifctrl_reg, WM8996_AIF1_FMT_MASK, aifctrl);
+ snd_soc_component_update_bits(component, bclk_reg,
WM8996_AIF1_BCLK_INV | WM8996_AIF1_BCLK_MSTR,
bclk);
- snd_soc_update_bits(codec, lrclk_tx_reg,
+ snd_soc_component_update_bits(component, lrclk_tx_reg,
WM8996_AIF1TX_LRCLK_INV |
WM8996_AIF1TX_LRCLK_MSTR,
lrclk_tx);
- snd_soc_update_bits(codec, lrclk_rx_reg,
+ snd_soc_component_update_bits(component, lrclk_rx_reg,
WM8996_AIF1RX_LRCLK_INV |
WM8996_AIF1RX_LRCLK_MSTR,
lrclk_rx);
@@ -1734,8 +1731,8 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int bits, i, bclk_rate, best;
int aifdata = 0;
int lrclk = 0;
@@ -1745,7 +1742,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream,
switch (dai->id) {
case 0:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- (snd_soc_read(codec, WM8996_GPIO_1)) & WM8996_GP1_FN_MASK) {
+ (snd_soc_component_read(component, WM8996_GPIO_1)) & WM8996_GP1_FN_MASK) {
aifdata_reg = WM8996_AIF1RX_DATA_CONFIGURATION;
lrclk_reg = WM8996_AIF1_RX_LRCLK_1;
} else {
@@ -1756,7 +1753,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream,
break;
case 1:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- (snd_soc_read(codec, WM8996_GPIO_2)) & WM8996_GP2_FN_MASK) {
+ (snd_soc_component_read(component, WM8996_GPIO_2)) & WM8996_GP2_FN_MASK) {
aifdata_reg = WM8996_AIF2RX_DATA_CONFIGURATION;
lrclk_reg = WM8996_AIF2_RX_LRCLK_1;
} else {
@@ -1772,7 +1769,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream,
bclk_rate = snd_soc_params_to_bclk(params);
if (bclk_rate < 0) {
- dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
+ dev_err(component->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
return bclk_rate;
}
@@ -1793,19 +1790,19 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream,
}
dsp |= i << dsp_shift;
- wm8996_update_bclk(codec);
+ wm8996_update_bclk(component);
lrclk = bclk_rate / params_rate(params);
dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
lrclk, bclk_rate / lrclk);
- snd_soc_update_bits(codec, aifdata_reg,
+ snd_soc_component_update_bits(component, aifdata_reg,
WM8996_AIF1TX_WL_MASK |
WM8996_AIF1TX_SLOT_LEN_MASK,
aifdata);
- snd_soc_update_bits(codec, lrclk_reg, WM8996_AIF1RX_RATE_MASK,
+ snd_soc_component_update_bits(component, lrclk_reg, WM8996_AIF1RX_RATE_MASK,
lrclk);
- snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_2,
+ snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_2,
WM8996_DSP1_DIV_MASK << dsp_shift, dsp);
return 0;
@@ -1814,8 +1811,8 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int lfclk = 0;
int ratediv = 0;
int sync = WM8996_REG_SYNC;
@@ -1826,8 +1823,8 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai,
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,
+ old = snd_soc_component_read(component, WM8996_AIF_CLOCKING_1) & WM8996_SYSCLK_ENA;
+ snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_1,
WM8996_SYSCLK_ENA, 0);
switch (clk_id) {
@@ -1844,23 +1841,24 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai,
src = 2;
break;
default:
- dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
+ dev_err(component->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,
+ snd_soc_component_update_bits(component, WM8996_AIF_RATE,
WM8996_SYSCLK_RATE, 0);
break;
case 22579200:
case 24576000:
ratediv = WM8996_SYSCLK_DIV;
wm8996->sysclk /= 2;
+ fallthrough;
case 11289600:
case 12288000:
- snd_soc_update_bits(codec, WM8996_AIF_RATE,
+ snd_soc_component_update_bits(component, WM8996_AIF_RATE,
WM8996_SYSCLK_RATE, WM8996_SYSCLK_RATE);
break;
case 32000:
@@ -1869,20 +1867,20 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai,
sync = 0;
break;
default:
- dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
+ dev_warn(component->dev, "Unsupported clock rate %dHz\n",
wm8996->sysclk);
return -EINVAL;
}
- wm8996_update_bclk(codec);
+ wm8996_update_bclk(component);
- snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_1,
WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK,
src << WM8996_SYSCLK_SRC_SHIFT | ratediv);
- snd_soc_update_bits(codec, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk);
- snd_soc_update_bits(codec, WM8996_CONTROL_INTERFACE_1,
+ snd_soc_component_update_bits(component, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk);
+ snd_soc_component_update_bits(component, WM8996_CONTROL_INTERFACE_1,
WM8996_REG_SYNC, sync);
- snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_1,
WM8996_SYSCLK_ENA, old);
wm8996->sysclk_src = clk_id;
@@ -2001,11 +1999,11 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+static int wm8996_set_fll(struct snd_soc_component *component, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
struct _fll_div fll_div;
unsigned long timeout, time_left;
int ret, reg, retry;
@@ -2016,15 +2014,15 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
return 0;
if (Fout == 0) {
- dev_dbg(codec->dev, "FLL disabled\n");
+ dev_dbg(component->dev, "FLL disabled\n");
wm8996->fll_fref = 0;
wm8996->fll_fout = 0;
- snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_1,
WM8996_FLL_ENA, 0);
- wm8996_bg_disable(codec);
+ wm8996_bg_disable(component);
return 0;
}
@@ -2047,14 +2045,14 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
reg = 3;
break;
default:
- dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ dev_err(component->dev, "Unknown FLL source %d\n", ret);
return -EINVAL;
}
reg |= fll_div.fll_refclk_div << WM8996_FLL_REFCLK_DIV_SHIFT;
reg |= fll_div.fll_ref_freq << WM8996_FLL_REF_FREQ_SHIFT;
- snd_soc_update_bits(codec, WM8996_FLL_CONTROL_5,
+ snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_5,
WM8996_FLL_REFCLK_DIV_MASK | WM8996_FLL_REF_FREQ |
WM8996_FLL_REFCLK_SRC_MASK, reg);
@@ -2063,38 +2061,38 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
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_component_write(component, WM8996_FLL_EFS_2, reg);
- snd_soc_update_bits(codec, WM8996_FLL_CONTROL_2,
+ snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_2,
WM8996_FLL_OUTDIV_MASK |
WM8996_FLL_FRATIO_MASK,
(fll_div.fll_outdiv << WM8996_FLL_OUTDIV_SHIFT) |
(fll_div.fll_fratio));
- snd_soc_write(codec, WM8996_FLL_CONTROL_3, fll_div.theta);
+ snd_soc_component_write(component, WM8996_FLL_CONTROL_3, fll_div.theta);
- snd_soc_update_bits(codec, WM8996_FLL_CONTROL_4,
+ snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_4,
WM8996_FLL_N_MASK | WM8996_FLL_LOOP_GAIN_MASK,
(fll_div.n << WM8996_FLL_N_SHIFT) |
fll_div.fll_loop_gain);
- snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda);
+ snd_soc_component_write(component, 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);
+ ret = snd_soc_component_read(component, WM8996_FLL_CONTROL_1);
if (!(ret & WM8996_FLL_ENA))
- wm8996_bg_enable(codec);
+ wm8996_bg_enable(component);
/* Clear any pending completions (eg, from failed startups) */
try_wait_for_completion(&wm8996->fll_lock);
- snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
+ snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_1,
WM8996_FLL_ENA, WM8996_FLL_ENA);
/* The FLL supports live reconfiguration - kick that in case we were
* already enabled.
*/
- snd_soc_write(codec, WM8996_FLL_CONTROL_6, WM8996_FLL_SWITCH_CLK);
+ snd_soc_component_write(component, WM8996_FLL_CONTROL_6, WM8996_FLL_SWITCH_CLK);
/* Wait for the FLL to lock, using the interrupt if possible */
if (Fref > 1000000)
@@ -2109,7 +2107,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
timeout *= 10;
else
/* ensure timeout of atleast 1 jiffies */
- timeout = timeout/2 ? : 1;
+ timeout = (timeout/2) ? : 1;
for (retry = 0; retry < 10; retry++) {
time_left = wait_for_completion_timeout(&wm8996->fll_lock,
@@ -2120,16 +2118,16 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
break;
}
- ret = snd_soc_read(codec, WM8996_INTERRUPT_RAW_STATUS_2);
+ ret = snd_soc_component_read(component, 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");
+ dev_err(component->dev, "Timed out waiting for FLL\n");
ret = -ETIMEDOUT;
}
- dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+ dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
wm8996->fll_fref = Fref;
wm8996->fll_fout = Fout;
@@ -2139,23 +2137,20 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
#ifdef CONFIG_GPIOLIB
-static inline struct wm8996_priv *gpio_to_wm8996(struct gpio_chip *chip)
+static int wm8996_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- 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);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
- regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
- WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT);
+ return 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);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
int val;
val = (1 << WM8996_GP1_FN_SHIFT) | (!!value << WM8996_GP1_LVL_SHIFT);
@@ -2167,7 +2162,7 @@ static int wm8996_gpio_direction_out(struct gpio_chip *chip,
static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
unsigned int reg;
int ret;
@@ -2180,7 +2175,7 @@ static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
WM8996_GP1_FN_MASK | WM8996_GP1_DIR,
@@ -2188,7 +2183,7 @@ static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
(1 << WM8996_GP1_DIR_SHIFT));
}
-static struct gpio_chip wm8996_template_chip = {
+static const struct gpio_chip wm8996_template_chip = {
.label = "wm8996",
.owner = THIS_MODULE,
.direction_output = wm8996_gpio_direction_out,
@@ -2196,6 +2191,8 @@ static struct gpio_chip wm8996_template_chip = {
.direction_input = wm8996_gpio_direction_in,
.get = wm8996_gpio_get,
.can_sleep = 1,
+ .ngpio = 5,
+ .base = -1,
};
static void wm8996_init_gpio(struct wm8996_priv *wm8996)
@@ -2203,15 +2200,9 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996)
int ret;
wm8996->gpio_chip = wm8996_template_chip;
- wm8996->gpio_chip.ngpio = 5;
wm8996->gpio_chip.parent = wm8996->dev;
- if (wm8996->pdata.gpio_base)
- wm8996->gpio_chip.base = wm8996->pdata.gpio_base;
- else
- wm8996->gpio_chip.base = -1;
-
- ret = gpiochip_add(&wm8996->gpio_chip);
+ ret = gpiochip_add_data(&wm8996->gpio_chip, wm8996);
if (ret != 0)
dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret);
}
@@ -2232,6 +2223,9 @@ static void wm8996_free_gpio(struct wm8996_priv *wm8996)
/**
* wm8996_detect - Enable default WM8996 jack detection
+ * @component: ASoC component
+ * @jack: jack pointer
+ * @polarity_cb: polarity callback
*
* The WM8996 has advanced accessory detection support for headsets.
* This function provides a default implementation which integrates
@@ -2241,11 +2235,11 @@ static void wm8996_free_gpio(struct wm8996_priv *wm8996)
* 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,
+int wm8996_detect(struct snd_soc_component *component, 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 = snd_soc_codec_get_dapm(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
wm8996->jack = jack;
wm8996->detecting = true;
@@ -2253,12 +2247,12 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
wm8996->jack_flips = 0;
if (wm8996->polarity_cb)
- wm8996->polarity_cb(codec, 0);
+ wm8996->polarity_cb(component, 0);
/* Clear discarge to avoid noise during detection */
- snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ snd_soc_component_update_bits(component, WM8996_MICBIAS_1,
WM8996_MICB1_DISCH, 0);
- snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ snd_soc_component_update_bits(component, WM8996_MICBIAS_2,
WM8996_MICB2_DISCH, 0);
/* LDO2 powers the microphones, SYSCLK clocks detection */
@@ -2272,26 +2266,26 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
/* We start off just enabling microphone detection - even a
* plain headphone will trigger detection.
*/
- snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1,
WM8996_MICD_ENA, WM8996_MICD_ENA);
/* Slowest detection rate, gives debounce for initial detection */
- snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1,
WM8996_MICD_RATE_MASK,
WM8996_MICD_RATE_MASK);
/* Enable interrupts and we're off */
- snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
+ snd_soc_component_update_bits(component, WM8996_INTERRUPT_STATUS_2_MASK,
WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0);
return 0;
}
EXPORT_SYMBOL_GPL(wm8996_detect);
-static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
+static void wm8996_hpdet_irq(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int val, reg, report;
/* Assume headphone in error conditions; we need to report
@@ -2299,20 +2293,20 @@ static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
*/
report = SND_JACK_HEADPHONE;
- reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2);
+ reg = snd_soc_component_read(component, WM8996_HEADPHONE_DETECT_2);
if (reg < 0) {
- dev_err(codec->dev, "Failed to read HPDET status\n");
+ dev_err(component->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");
+ dev_err(component->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);
+ dev_dbg(component->dev, "HPDET measured %d ohms\n", val);
/* If we've got high enough impedence then report as line,
* otherwise assume headphone.
@@ -2332,28 +2326,28 @@ out:
wm8996->detecting = false;
/* If the output isn't running re-clamp it */
- if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) &
+ if (!(snd_soc_component_read(component, WM8996_POWER_MANAGEMENT_1) &
(WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT)))
- snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
+ snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_1,
WM8996_HPOUT1L_RMV_SHORT |
WM8996_HPOUT1R_RMV_SHORT, 0);
/* Go back to looking at the microphone */
- snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
+ snd_soc_component_update_bits(component, WM8996_ACCESSORY_DETECT_MODE_1,
WM8996_JD_MODE_MASK, 0);
- snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
WM8996_MICD_ENA);
snd_soc_dapm_disable_pin(dapm, "Bandgap");
snd_soc_dapm_sync(dapm);
}
-static void wm8996_hpdet_start(struct snd_soc_codec *codec)
+static void wm8996_hpdet_start(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
/* Unclamp the output, we can't measure while we're shorting it */
- snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
+ snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_1,
WM8996_HPOUT1L_RMV_SHORT |
WM8996_HPOUT1R_RMV_SHORT,
WM8996_HPOUT1L_RMV_SHORT |
@@ -2364,45 +2358,45 @@ static void wm8996_hpdet_start(struct snd_soc_codec *codec)
snd_soc_dapm_sync(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,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
+ snd_soc_component_update_bits(component, WM8996_ACCESSORY_DETECT_MODE_1,
WM8996_JD_MODE_MASK, 1);
/* Trigger a measurement */
- snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1,
+ snd_soc_component_update_bits(component, WM8996_HEADPHONE_DETECT_1,
WM8996_HP_POLL, WM8996_HP_POLL);
}
-static void wm8996_report_headphone(struct snd_soc_codec *codec)
+static void wm8996_report_headphone(struct snd_soc_component *component)
{
- dev_dbg(codec->dev, "Headphone detected\n");
- wm8996_hpdet_start(codec);
+ dev_dbg(component->dev, "Headphone detected\n");
+ wm8996_hpdet_start(component);
/* Increase the detection rate a bit for responsiveness. */
- snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1,
WM8996_MICD_RATE_MASK |
WM8996_MICD_BIAS_STARTTIME_MASK,
7 << WM8996_MICD_RATE_SHIFT |
7 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
}
-static void wm8996_micd(struct snd_soc_codec *codec)
+static void wm8996_micd(struct snd_soc_component *component)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int val, reg;
- val = snd_soc_read(codec, WM8996_MIC_DETECT_3);
+ val = snd_soc_component_read(component, WM8996_MIC_DETECT_3);
- dev_dbg(codec->dev, "Microphone event: %x\n", val);
+ dev_dbg(component->dev, "Microphone event: %x\n", val);
if (!(val & WM8996_MICD_VALID)) {
- dev_warn(codec->dev, "Microphone detection state invalid\n");
+ dev_warn(component->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");
+ dev_dbg(component->dev, "Jack removal detected\n");
wm8996->jack_mic = false;
wm8996->detecting = true;
wm8996->jack_flips = 0;
@@ -2410,7 +2404,7 @@ static void wm8996_micd(struct snd_soc_codec *codec)
SND_JACK_LINEOUT | SND_JACK_HEADSET |
SND_JACK_BTN_0);
- snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1,
WM8996_MICD_RATE_MASK |
WM8996_MICD_BIAS_STARTTIME_MASK,
WM8996_MICD_RATE_MASK |
@@ -2424,19 +2418,19 @@ static void wm8996_micd(struct snd_soc_codec *codec)
*/
if (val & 0x400) {
if (wm8996->detecting) {
- dev_dbg(codec->dev, "Microphone detected\n");
+ dev_dbg(component->dev, "Microphone detected\n");
wm8996->jack_mic = true;
- wm8996_hpdet_start(codec);
+ wm8996_hpdet_start(component);
/* Increase poll rate to give better responsiveness
* for buttons */
- snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1,
WM8996_MICD_RATE_MASK |
WM8996_MICD_BIAS_STARTTIME_MASK,
5 << WM8996_MICD_RATE_SHIFT |
7 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
} else {
- dev_dbg(codec->dev, "Mic button up\n");
+ dev_dbg(component->dev, "Mic button up\n");
snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0);
}
@@ -2453,22 +2447,22 @@ static void wm8996_micd(struct snd_soc_codec *codec)
wm8996->jack_flips++;
if (wm8996->jack_flips > 1) {
- wm8996_report_headphone(codec);
+ wm8996_report_headphone(component);
return;
}
- reg = snd_soc_read(codec, WM8996_ACCESSORY_DETECT_MODE_2);
+ reg = snd_soc_component_read(component, 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,
+ snd_soc_component_update_bits(component, WM8996_ACCESSORY_DETECT_MODE_2,
WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
WM8996_MICD_BIAS_SRC, reg);
if (wm8996->polarity_cb)
- wm8996->polarity_cb(codec,
+ wm8996->polarity_cb(component,
(reg & WM8996_MICD_SRC) != 0);
- dev_dbg(codec->dev, "Set microphone polarity to %d\n",
+ dev_dbg(component->dev, "Set microphone polarity to %d\n",
(reg & WM8996_MICD_SRC) != 0);
return;
@@ -2479,52 +2473,52 @@ static void wm8996_micd(struct snd_soc_codec *codec)
*/
if (val & 0x3fc) {
if (wm8996->jack_mic) {
- dev_dbg(codec->dev, "Mic button detected\n");
+ dev_dbg(component->dev, "Mic button detected\n");
snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0,
SND_JACK_BTN_0);
} else if (wm8996->detecting) {
- wm8996_report_headphone(codec);
+ wm8996_report_headphone(component);
}
}
}
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);
+ struct snd_soc_component *component = data;
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
int irq_val;
- irq_val = snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2);
+ irq_val = snd_soc_component_read(component, WM8996_INTERRUPT_STATUS_2);
if (irq_val < 0) {
- dev_err(codec->dev, "Failed to read IRQ status: %d\n",
+ dev_err(component->dev, "Failed to read IRQ status: %d\n",
irq_val);
return IRQ_NONE;
}
- irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK);
+ irq_val &= ~snd_soc_component_read(component, WM8996_INTERRUPT_STATUS_2_MASK);
if (!irq_val)
return IRQ_NONE;
- snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val);
+ snd_soc_component_write(component, WM8996_INTERRUPT_STATUS_2, irq_val);
if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) {
- dev_dbg(codec->dev, "DC servo IRQ\n");
+ dev_dbg(component->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");
+ dev_err(component->dev, "Digital core FIFO error\n");
if (irq_val & WM8996_FLL_LOCK_EINT) {
- dev_dbg(codec->dev, "FLL locked\n");
+ dev_dbg(component->dev, "FLL locked\n");
complete(&wm8996->fll_lock);
}
if (irq_val & WM8996_MICD_EINT)
- wm8996_micd(codec);
+ wm8996_micd(component);
if (irq_val & WM8996_HP_DONE_EINT)
- wm8996_hpdet_irq(codec);
+ wm8996_hpdet_irq(component);
return IRQ_HANDLED;
}
@@ -2543,9 +2537,9 @@ static irqreturn_t wm8996_edge_irq(int irq, void *data)
return ret;
}
-static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec)
+static void wm8996_retune_mobile_pdata(struct snd_soc_component *component)
{
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
struct wm8996_pdata *pdata = &wm8996->pdata;
struct snd_kcontrol_new controls[] = {
@@ -2594,15 +2588,15 @@ static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec)
wm8996->retune_mobile_texts = t;
}
- dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n",
wm8996->num_retune_mobile_texts);
wm8996->retune_mobile_enum.items = wm8996->num_retune_mobile_texts;
wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to add ReTune Mobile controls: %d\n", ret);
}
@@ -2615,25 +2609,25 @@ static const struct regmap_config wm8996_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8996_reg),
.volatile_reg = wm8996_volatile_register,
.readable_reg = wm8996_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int wm8996_probe(struct snd_soc_codec *codec)
+static int wm8996_probe(struct snd_soc_component *component)
{
int ret;
- struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
int irq_flags;
- wm8996->codec = codec;
+ wm8996->component = component;
init_completion(&wm8996->dcs_done);
init_completion(&wm8996->fll_lock);
if (wm8996->pdata.num_retune_mobile_cfgs)
- wm8996_retune_mobile_pdata(codec);
+ wm8996_retune_mobile_pdata(component);
else
- snd_soc_add_codec_controls(codec, wm8996_eq_controls,
+ snd_soc_add_component_controls(component, wm8996_eq_controls,
ARRAY_SIZE(wm8996_eq_controls));
if (i2c->irq) {
@@ -2647,18 +2641,18 @@ static int wm8996_probe(struct snd_soc_codec *codec)
if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
ret = request_threaded_irq(i2c->irq, NULL,
wm8996_edge_irq,
- irq_flags, "wm8996", codec);
+ irq_flags, "wm8996", component);
else
ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq,
- irq_flags, "wm8996", codec);
+ irq_flags, "wm8996", component);
if (ret == 0) {
/* Unmask the interrupt */
- snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
+ snd_soc_component_update_bits(component, WM8996_INTERRUPT_CONTROL,
WM8996_IM_IRQ, 0);
/* Enable error reporting and DC servo status */
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8996_INTERRUPT_STATUS_2_MASK,
WM8996_IM_DCS_DONE_23_EINT |
WM8996_IM_DCS_DONE_01_EINT |
@@ -2666,7 +2660,7 @@ static int wm8996_probe(struct snd_soc_codec *codec)
WM8996_IM_FIFOS_ERR_EINT,
0);
} else {
- dev_err(codec->dev, "Failed to request IRQ: %d\n",
+ dev_err(component->dev, "Failed to request IRQ: %d\n",
ret);
return ret;
}
@@ -2675,32 +2669,31 @@ static int wm8996_probe(struct snd_soc_codec *codec)
return 0;
}
-static int wm8996_remove(struct snd_soc_codec *codec)
+static void wm8996_remove(struct snd_soc_component *component)
{
- struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
- snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
+ snd_soc_component_update_bits(component, WM8996_INTERRUPT_CONTROL,
WM8996_IM_IRQ, WM8996_IM_IRQ);
if (i2c->irq)
- free_irq(i2c->irq, codec);
-
- return 0;
+ free_irq(i2c->irq, component);
}
-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,
+static const struct snd_soc_component_driver soc_component_dev_wm8996 = {
+ .probe = wm8996_probe,
+ .remove = wm8996_remove,
+ .set_bias_level = wm8996_set_bias_level,
+ .seq_notifier = wm8996_seq_notifier,
+ .controls = wm8996_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8996_snd_controls),
+ .dapm_widgets = wm8996_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets),
+ .dapm_routes = wm8996_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes),
+ .set_pll = wm8996_set_fll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
#define WM8996_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
@@ -2759,8 +2752,7 @@ static struct snd_soc_dai_driver wm8996_dai[] = {
},
};
-static int wm8996_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8996_i2c_probe(struct i2c_client *i2c)
{
struct wm8996_priv *wm8996;
int ret, i;
@@ -2778,15 +2770,15 @@ static int wm8996_i2c_probe(struct i2c_client *i2c,
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;
- }
+ wm8996->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm8996->ldo_ena)) {
+ ret = PTR_ERR(wm8996->ldo_ena);
+ dev_err(&i2c->dev, "Failed to request LDO ENA GPIO: %d\n",
+ ret);
+ goto err;
}
+ gpiod_set_consumer_name(wm8996->ldo_ena, "WM8996 ENA");
for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
wm8996->supplies[i].supply = wm8996_supply_names[i];
@@ -2804,8 +2796,9 @@ static int wm8996_i2c_probe(struct i2c_client *i2c,
/* 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]);
+ ret = devm_regulator_register_notifier(
+ wm8996->supplies[i].consumer,
+ &wm8996->disable_nb[i]);
if (ret != 0) {
dev_err(&i2c->dev,
"Failed to register regulator notifier: %d\n",
@@ -2820,8 +2813,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c,
goto err_gpio;
}
- if (wm8996->pdata.ldo_ena > 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 1);
msleep(5);
}
@@ -2853,8 +2846,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c,
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);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
regcache_cache_only(wm8996->regmap, true);
} else {
ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET,
@@ -3048,8 +3041,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c,
wm8996_init_gpio(wm8996);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8996, wm8996_dai,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm8996, wm8996_dai,
ARRAY_SIZE(wm8996_dai));
if (ret < 0)
goto err_gpiolib;
@@ -3060,37 +3053,26 @@ 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);
+ if (wm8996->ldo_ena)
+ gpiod_set_value_cansleep(wm8996->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)
+static void 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;
+ if (wm8996->ldo_ena)
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
}
static const struct i2c_device_id wm8996_i2c_id[] = {
- { "wm8996", 0 },
+ { "wm8996" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id);
diff --git a/sound/soc/codecs/wm8996.h b/sound/soc/codecs/wm8996.h
index de9ac3e44aec..12bbbb1a9db6 100644
--- a/sound/soc/codecs/wm8996.h
+++ b/sound/soc/codecs/wm8996.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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
@@ -22,9 +18,9 @@
#define WM8996_FLL_DACLRCLK1 3
#define WM8996_FLL_BCLK1 4
-typedef void (*wm8996_polarity_fn)(struct snd_soc_codec *codec, int polarity);
+typedef void (*wm8996_polarity_fn)(struct snd_soc_component *component, int polarity);
-int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+int wm8996_detect(struct snd_soc_component *component, struct snd_soc_jack *jack,
wm8996_polarity_fn polarity_cb);
/*
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index b4dba3a02aba..83106523385a 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -84,8 +81,8 @@ static const struct reg_default wm8997_sysclk_reva_patch[] = {
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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
struct regmap *regmap = arizona->regmap;
const struct reg_default *patch = NULL;
int i, patch_size;
@@ -108,6 +105,9 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
return 0;
}
@@ -115,7 +115,7 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
return arizona_dvfs_sysclk_ev(w, kcontrol, event);
}
-static const char *wm8997_osr_text[] = {
+static const char * const wm8997_osr_text[] = {
"Low power", "Normal", "High performance",
};
@@ -408,9 +408,11 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm8997_sysclk_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -511,101 +513,100 @@ SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
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,
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX6_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX7_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX8_ENA_SHIFT, 0),
SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
- ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
- &wm8997_aec_loopback_mux),
+ 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,
@@ -922,10 +923,10 @@ static const struct snd_soc_dapm_route wm8997_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
};
-static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
+static int wm8997_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int Fref, unsigned int Fout)
{
- struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec);
+ struct wm8997_priv *wm8997 = snd_soc_component_get_drvdata(component);
switch (fll_id) {
case WM8997_FLL1:
@@ -943,7 +944,7 @@ static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+#define WM8997_RATES SNDRV_PCM_RATE_KNOT
#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -968,8 +969,8 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
.formats = WM8997_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm8997-aif2",
@@ -990,8 +991,8 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
.formats = WM8997_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm8997-slim1",
@@ -1052,12 +1053,18 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
},
};
-static int wm8997_codec_probe(struct snd_soc_codec *codec)
+static int wm8997_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8997_priv *priv = snd_soc_component_get_drvdata(component);
+ struct arizona *arizona = priv->core.arizona;
+ int ret;
+
+ snd_soc_component_init_regmap(component, arizona->regmap);
- arizona_init_spk(codec);
+ ret = arizona_init_spk(component);
+ if (ret < 0)
+ return ret;
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
@@ -1066,13 +1073,11 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static int wm8997_codec_remove(struct snd_soc_codec *codec)
+static void wm8997_component_remove(struct snd_soc_component *component)
{
- struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8997_priv *priv = snd_soc_component_get_drvdata(component);
priv->core.arizona->dapm = NULL;
-
- return 0;
}
#define WM8997_DIG_VU 0x0200
@@ -1086,36 +1091,27 @@ static unsigned int wm8997_digital_vu[] = {
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 const struct snd_soc_component_driver soc_component_dev_wm8997 = {
+ .probe = wm8997_component_probe,
+ .remove = wm8997_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8997_set_fll,
+ .set_jack = arizona_jack_set_jack,
+ .controls = wm8997_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+ .dapm_widgets = wm8997_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets),
+ .dapm_routes = wm8997_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm8997_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm8997_priv *wm8997;
- int i;
+ int i, ret;
wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
GFP_KERNEL);
@@ -1123,11 +1119,24 @@ static int wm8997_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm8997);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
wm8997->core.arizona = arizona;
wm8997->core.num_inputs = 4;
arizona_init_dvfs(&wm8997->core);
+ /* This may return -EPROBE_DEFER, so do this early on */
+ ret = arizona_jack_codec_dev_probe(&wm8997->core, &pdev->dev);
+ if (ret)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
wm8997->fll[i].vco_mult = 1;
@@ -1155,16 +1164,45 @@ static int wm8997_probe(struct platform_device *pdev)
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));
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_jack_codec_dev;
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_jack_codec_dev;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm8997,
+ wm8997_dai,
+ ARRAY_SIZE(wm8997_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_jack_codec_dev:
+ pm_runtime_disable(&pdev->dev);
+ arizona_jack_codec_dev_remove(&wm8997->core);
+
+ return ret;
}
-static int wm8997_remove(struct platform_device *pdev)
+static void wm8997_remove(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
+ struct wm8997_priv *wm8997 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm8997->core.arizona;
+
pm_runtime_disable(&pdev->dev);
- return 0;
+ arizona_free_spk_irqs(arizona);
+
+ arizona_jack_codec_dev_remove(&wm8997->core);
}
static struct platform_driver wm8997_codec_driver = {
diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h
index 5e91c6a7d567..6fd7e3063655 100644
--- a/sound/soc/codecs/wm8997.h
+++ b/sound/soc/codecs/wm8997.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 7719bc509e50..8e6f03d00c71 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* wm8998.c -- ALSA SoC Audio driver for WM8998 codecs
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Author: Richard Fitzgerald <rf@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>
@@ -41,12 +38,12 @@ static int wm8998_asrc_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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int val;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- val = snd_soc_read(codec, ARIZONA_ASRC_RATE1);
+ val = snd_soc_component_read(component, ARIZONA_ASRC_RATE1);
val &= ARIZONA_ASRC_RATE1_MASK;
val >>= ARIZONA_ASRC_RATE1_SHIFT;
@@ -54,23 +51,23 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w,
case 0:
case 1:
case 2:
- val = snd_soc_read(codec,
+ val = snd_soc_component_read(component,
ARIZONA_SAMPLE_RATE_1 + val);
if (val >= 0x11) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Unsupported ASRC rate1 (%s)\n",
arizona_sample_rate_val_to_name(val));
- return -EINVAL;
+ return -EINVAL;
}
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Illegal ASRC rate1 selector (0x%x)\n",
val);
return -EINVAL;
}
- val = snd_soc_read(codec, ARIZONA_ASRC_RATE2);
+ val = snd_soc_component_read(component, ARIZONA_ASRC_RATE2);
val &= ARIZONA_ASRC_RATE2_MASK;
val >>= ARIZONA_ASRC_RATE2_SHIFT;
@@ -78,17 +75,17 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w,
case 8:
case 9:
val -= 0x8;
- val = snd_soc_read(codec,
+ val = snd_soc_component_read(component,
ARIZONA_ASYNC_SAMPLE_RATE_1 + val);
if (val >= 0x11) {
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"Unsupported ASRC rate2 (%s)\n",
arizona_sample_rate_val_to_name(val));
return -EINVAL;
}
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Illegal ASRC rate2 selector (0x%x)\n",
val);
return -EINVAL;
@@ -101,97 +98,60 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w,
return 0;
}
-static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol,
+static int wm8998_inmux_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 = snd_soc_codec_get_dapm(codec);
- struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct wm8998_priv *wm8998 = snd_soc_component_get_drvdata(component);
struct arizona *arizona = wm8998->core.arizona;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int mux, inmode;
- unsigned int mode_val, src_val;
+ unsigned int mode_reg, mode_index;
+ unsigned int mux, inmode, src_val, mode_val;
+ int change, ret;
mux = ucontrol->value.enumerated.item[0];
if (mux > 1)
return -EINVAL;
- /* L and R registers have same shift and mask */
- inmode = arizona->pdata.inmode[2 * mux];
- src_val = mux << ARIZONA_IN1L_SRC_SHIFT;
- if (inmode & ARIZONA_INMODE_SE)
- src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT;
-
- switch (arizona->pdata.inmode[0]) {
- case ARIZONA_INMODE_DMIC:
- if (mux)
- mode_val = 0; /* B always analogue */
- else
- mode_val = 1 << ARIZONA_IN1_MODE_SHIFT;
-
- snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1_MODE_MASK, mode_val);
-
- /* IN1A is digital so L and R must change together */
- /* src_val setting same for both registers */
- snd_soc_update_bits(codec,
- ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_IN1L_SRC_MASK |
- ARIZONA_IN1L_SRC_SE_MASK, src_val);
- snd_soc_update_bits(codec,
- ARIZONA_ADC_DIGITAL_VOLUME_1R,
- ARIZONA_IN1R_SRC_MASK |
- ARIZONA_IN1R_SRC_SE_MASK, src_val);
+ switch (e->reg) {
+ case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+ mode_reg = ARIZONA_IN2L_CONTROL;
+ mode_index = 1 + (2 * mux);
break;
default:
- /* both analogue */
- snd_soc_update_bits(codec,
- e->reg,
- ARIZONA_IN1L_SRC_MASK |
- ARIZONA_IN1L_SRC_SE_MASK,
- src_val);
+ mode_reg = ARIZONA_IN1L_CONTROL;
+ mode_index = (2 * mux);
break;
}
- return snd_soc_dapm_mux_update_power(dapm, kcontrol,
- ucontrol->value.enumerated.item[0],
- e, NULL);
-}
-
-static int wm8998_in2mux_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 = snd_soc_codec_get_dapm(codec);
- struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = wm8998->core.arizona;
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int mux, inmode, src_val, mode_val;
-
- mux = ucontrol->value.enumerated.item[0];
- if (mux > 1)
- return -EINVAL;
-
- inmode = arizona->pdata.inmode[1 + (2 * mux)];
+ inmode = arizona->pdata.inmode[mode_index];
if (inmode & ARIZONA_INMODE_DMIC)
- mode_val = 1 << ARIZONA_IN2_MODE_SHIFT;
+ mode_val = 1 << ARIZONA_IN1_MODE_SHIFT;
else
mode_val = 0;
- src_val = mux << ARIZONA_IN2L_SRC_SHIFT;
+ src_val = mux << ARIZONA_IN1L_SRC_SHIFT;
if (inmode & ARIZONA_INMODE_SE)
- src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT;
+ src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT;
+
+ snd_soc_component_update_bits(component, mode_reg,
+ ARIZONA_IN1_MODE_MASK, mode_val);
- snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2_MODE_MASK, mode_val);
+ change = snd_soc_component_update_bits(component, e->reg,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK,
+ src_val);
- snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK,
- src_val);
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
- return snd_soc_dapm_mux_update_power(dapm, kcontrol,
- ucontrol->value.enumerated.item[0],
- e, NULL);
+ return change;
}
static const char * const wm8998_inmux_texts[] = {
@@ -216,14 +176,14 @@ static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
static const struct snd_kcontrol_new wm8998_in1mux[2] = {
SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
- snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+ snd_soc_dapm_get_enum_double, wm8998_inmux_put),
SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum,
- snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+ snd_soc_dapm_get_enum_double, wm8998_inmux_put),
};
static const struct snd_kcontrol_new wm8998_in2mux =
SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum,
- snd_soc_dapm_get_enum_double, wm8998_in2mux_put);
+ snd_soc_dapm_get_enum_double, wm8998_inmux_put);
static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
@@ -368,7 +328,7 @@ SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
- ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
@@ -381,7 +341,7 @@ SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
0xbf, 0, digital_tlv),
SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
- ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+ 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),
@@ -541,9 +501,11 @@ static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
- ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -659,105 +621,105 @@ SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1,
- ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
- &wm8998_aec_loopback_mux[0]),
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[0]),
SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2,
- ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
- &wm8998_aec_loopback_mux[1]),
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[1]),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5,
ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5,
ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX4_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX3_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX4_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX5_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
-SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
@@ -783,9 +745,9 @@ SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL,
- ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+ ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL,
- ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+ ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL,
ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0),
@@ -966,6 +928,16 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
{ "IN2A", NULL, "SYSCLK" },
{ "IN2B", NULL, "SYSCLK" },
+ { "ASRC1L", NULL, "SYSCLK" },
+ { "ASRC1R", NULL, "SYSCLK" },
+ { "ASRC2L", NULL, "SYSCLK" },
+ { "ASRC2R", NULL, "SYSCLK" },
+
+ { "ASRC1L", NULL, "ASYNCCLK" },
+ { "ASRC1R", NULL, "ASYNCCLK" },
+ { "ASRC2L", NULL, "ASYNCCLK" },
+ { "ASRC2R", NULL, "ASYNCCLK" },
+
{ "SPD1", NULL, "SYSCLK" },
{ "SPD1", NULL, "SPD1TX1" },
{ "SPD1", NULL, "SPD1TX2" },
@@ -1166,11 +1138,12 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
-#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+#define WM8998_RATES SNDRV_PCM_RATE_KNOT
#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -1195,8 +1168,8 @@ static struct snd_soc_dai_driver wm8998_dai[] = {
.formats = WM8998_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm8998-aif2",
@@ -1217,8 +1190,8 @@ static struct snd_soc_dai_driver wm8998_dai[] = {
.formats = WM8998_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm8998-aif3",
@@ -1239,8 +1212,8 @@ static struct snd_soc_dai_driver wm8998_dai[] = {
.formats = WM8998_FORMATS,
},
.ops = &arizona_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
},
{
.name = "wm8998-slim1",
@@ -1282,10 +1255,10 @@ static struct snd_soc_dai_driver wm8998_dai[] = {
},
};
-static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
+static int wm8998_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int Fref, unsigned int Fout)
{
- struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct wm8998_priv *wm8998 = snd_soc_component_get_drvdata(component);
switch (fll_id) {
case WM8998_FLL1:
@@ -1303,28 +1276,32 @@ static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-static int wm8998_codec_probe(struct snd_soc_codec *codec)
+static int wm8998_component_probe(struct snd_soc_component *component)
{
- struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8998_priv *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct arizona *arizona = priv->core.arizona;
+ int ret;
- priv->core.arizona->dapm = dapm;
+ arizona->dapm = dapm;
+ snd_soc_component_init_regmap(component, arizona->regmap);
- arizona_init_spk(codec);
- arizona_init_gpio(codec);
+ ret = arizona_init_spk(component);
+ if (ret < 0)
+ return ret;
+
+ arizona_init_gpio(component);
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
return 0;
}
-static int wm8998_codec_remove(struct snd_soc_codec *codec)
+static void wm8998_component_remove(struct snd_soc_component *component)
{
- struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8998_priv *priv = snd_soc_component_get_drvdata(component);
priv->core.arizona->dapm = NULL;
-
- return 0;
}
#define WM8998_DIG_VU 0x0200
@@ -1341,36 +1318,27 @@ static unsigned int wm8998_digital_vu[] = {
ARIZONA_DAC_DIGITAL_VOLUME_5R,
};
-static struct regmap *wm8998_get_regmap(struct device *dev)
-{
- struct wm8998_priv *priv = dev_get_drvdata(dev);
-
- return priv->core.arizona->regmap;
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
- .probe = wm8998_codec_probe,
- .remove = wm8998_codec_remove,
- .get_regmap = wm8998_get_regmap,
-
- .idle_bias_off = true,
-
- .set_sysclk = arizona_set_sysclk,
- .set_pll = wm8998_set_fll,
-
- .controls = wm8998_snd_controls,
- .num_controls = ARRAY_SIZE(wm8998_snd_controls),
- .dapm_widgets = wm8998_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets),
- .dapm_routes = wm8998_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes),
+static const struct snd_soc_component_driver soc_component_dev_wm8998 = {
+ .probe = wm8998_component_probe,
+ .remove = wm8998_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8998_set_fll,
+ .set_jack = arizona_jack_set_jack,
+ .controls = wm8998_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8998_snd_controls),
+ .dapm_widgets = wm8998_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets),
+ .dapm_routes = wm8998_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm8998_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm8998_priv *wm8998;
- int i;
+ int i, ret;
wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
GFP_KERNEL);
@@ -1378,9 +1346,22 @@ static int wm8998_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm8998);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
wm8998->core.arizona = arizona;
wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
+ /* This may return -EPROBE_DEFER, so do this early on */
+ ret = arizona_jack_codec_dev_probe(&wm8998->core, &pdev->dev);
+ if (ret)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++)
wm8998->fll[i].vco_mult = 1;
@@ -1402,16 +1383,42 @@ static int wm8998_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
- wm8998_dai, ARRAY_SIZE(wm8998_dai));
+ arizona_init_common(arizona);
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_pm_disable;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm8998,
+ wm8998_dai,
+ ARRAY_SIZE(wm8998_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ arizona_jack_codec_dev_remove(&wm8998->core);
+
+ return ret;
}
-static int wm8998_remove(struct platform_device *pdev)
+static void wm8998_remove(struct platform_device *pdev)
{
- snd_soc_unregister_codec(&pdev->dev);
+ struct wm8998_priv *wm8998 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm8998->core.arizona;
+
pm_runtime_disable(&pdev->dev);
- return 0;
+ arizona_free_spk_irqs(arizona);
+
+ arizona_jack_codec_dev_remove(&wm8998->core);
}
static struct platform_driver wm8998_codec_driver = {
diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h
index 1e8647252162..a7f9391312d5 100644
--- a/sound/soc/codecs/wm8998.h
+++ b/sound/soc/codecs/wm8998.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* wm8998.h -- ALSA SoC Audio driver for WM8998 codecs
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Author: Richard Fitzgerald <rf@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 _WM8998_H
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index ccb3b15139ad..5bfe43c6c1f4 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -339,14 +335,14 @@ static SOC_ENUM_SINGLE_DECL(speaker_mode, WM9081_ANALOGUE_SPEAKER_2, 6,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg;
- reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+ reg = snd_soc_component_read(component, WM9081_ANALOGUE_SPEAKER_2);
if (reg & WM9081_SPK_MODE)
- ucontrol->value.integer.value[0] = 1;
+ ucontrol->value.enumerated.item[0] = 1;
else
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.enumerated.item[0] = 0;
return 0;
}
@@ -360,12 +356,12 @@ static int speaker_mode_get(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int reg_pwr = snd_soc_component_read(component, WM9081_POWER_MANAGEMENT);
+ unsigned int reg2 = snd_soc_component_read(component, WM9081_ANALOGUE_SPEAKER_2);
/* Are we changing anything? */
- if (ucontrol->value.integer.value[0] ==
+ if (ucontrol->value.enumerated.item[0] ==
((reg2 & WM9081_SPK_MODE) != 0))
return 0;
@@ -373,7 +369,7 @@ static int speaker_mode_put(struct snd_kcontrol *kcontrol,
if (reg_pwr & WM9081_SPK_ENA)
return -EINVAL;
- if (ucontrol->value.integer.value[0]) {
+ if (ucontrol->value.enumerated.item[0]) {
/* Class AB */
reg2 &= ~(WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL);
reg2 |= WM9081_SPK_MODE;
@@ -383,7 +379,7 @@ static int speaker_mode_put(struct snd_kcontrol *kcontrol,
reg2 &= ~WM9081_SPK_MODE;
}
- snd_soc_write(codec, WM9081_ANALOGUE_SPEAKER_2, reg2);
+ snd_soc_component_write(component, WM9081_ANALOGUE_SPEAKER_2, reg2);
return 0;
}
@@ -546,10 +542,10 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
+static int wm9081_set_fll(struct snd_soc_component *component, int fll_id,
unsigned int Fref, unsigned int Fout)
{
- struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
u16 reg1, reg4, reg5;
struct _fll_div fll_div;
int ret;
@@ -561,7 +557,7 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
/* Disable the FLL */
if (Fout == 0) {
- dev_dbg(codec->dev, "FLL disabled\n");
+ dev_dbg(component->dev, "FLL disabled\n");
wm9081->fll_fref = 0;
wm9081->fll_fout = 0;
@@ -572,7 +568,7 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
if (ret != 0)
return ret;
- reg5 = snd_soc_read(codec, WM9081_FLL_CONTROL_5);
+ reg5 = snd_soc_component_read(component, WM9081_FLL_CONTROL_5);
reg5 &= ~WM9081_FLL_CLK_SRC_MASK;
switch (fll_id) {
@@ -581,55 +577,55 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
break;
default:
- dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ dev_err(component->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);
+ clk_sys_reg = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_3);
if (clk_sys_reg & WM9081_CLK_SYS_ENA)
- snd_soc_write(codec, WM9081_CLOCK_CONTROL_3,
+ snd_soc_component_write(component, WM9081_CLOCK_CONTROL_3,
clk_sys_reg & ~WM9081_CLK_SYS_ENA);
/* Any FLL configuration change requires that the FLL be
* disabled first. */
- reg1 = snd_soc_read(codec, WM9081_FLL_CONTROL_1);
+ reg1 = snd_soc_component_read(component, WM9081_FLL_CONTROL_1);
reg1 &= ~WM9081_FLL_ENA;
- snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_1, reg1);
/* Apply the configuration */
if (fll_div.k)
reg1 |= WM9081_FLL_FRAC_MASK;
else
reg1 &= ~WM9081_FLL_FRAC_MASK;
- snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_1, reg1);
- snd_soc_write(codec, WM9081_FLL_CONTROL_2,
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_2,
(fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) |
(fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT));
- snd_soc_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_3, fll_div.k);
- reg4 = snd_soc_read(codec, WM9081_FLL_CONTROL_4);
+ reg4 = snd_soc_component_read(component, WM9081_FLL_CONTROL_4);
reg4 &= ~WM9081_FLL_N_MASK;
reg4 |= fll_div.n << WM9081_FLL_N_SHIFT;
- snd_soc_write(codec, WM9081_FLL_CONTROL_4, reg4);
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_4, reg4);
reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK;
reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT;
- snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5);
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_5, reg5);
/* Set gain to the recommended value */
- snd_soc_update_bits(codec, WM9081_FLL_CONTROL_4,
+ snd_soc_component_update_bits(component, WM9081_FLL_CONTROL_4,
WM9081_FLL_GAIN_MASK, 0);
/* Enable the FLL */
- snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
+ snd_soc_component_write(component, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
/* Then bring CLK_SYS up again if it was disabled */
if (clk_sys_reg & WM9081_CLK_SYS_ENA)
- snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
+ snd_soc_component_write(component, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
- dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+ dev_dbg(component->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
wm9081->fll_fref = Fref;
wm9081->fll_fout = Fout;
@@ -637,9 +633,9 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
return 0;
}
-static int configure_clock(struct snd_soc_codec *codec)
+static int configure_clock(struct snd_soc_component *component)
{
- struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
int new_sysclk, i, target;
unsigned int reg;
int ret = 0;
@@ -654,7 +650,7 @@ static int configure_clock(struct snd_soc_codec *codec)
} else {
wm9081->sysclk_rate = wm9081->mclk_rate;
}
- wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0);
+ wm9081_set_fll(component, WM9081_SYSCLK_FLL_MCLK, 0, 0);
break;
case WM9081_SYSCLK_FLL_MCLK:
@@ -695,7 +691,7 @@ static int configure_clock(struct snd_soc_codec *codec)
new_sysclk = 12288000;
}
- ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK,
+ ret = wm9081_set_fll(component, WM9081_SYSCLK_FLL_MCLK,
wm9081->mclk_rate, new_sysclk);
if (ret == 0) {
wm9081->sysclk_rate = new_sysclk;
@@ -711,21 +707,21 @@ static int configure_clock(struct snd_soc_codec *codec)
return -EINVAL;
}
- reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1);
+ reg = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_1);
if (mclkdiv)
reg |= WM9081_MCLKDIV2;
else
reg &= ~WM9081_MCLKDIV2;
- snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg);
+ snd_soc_component_write(component, WM9081_CLOCK_CONTROL_1, reg);
- reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
+ reg = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_3);
if (fll)
reg |= WM9081_CLK_SRC_SEL;
else
reg &= ~WM9081_CLK_SRC_SEL;
- snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg);
+ snd_soc_component_write(component, WM9081_CLOCK_CONTROL_3, reg);
- dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);
+ dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);
return ret;
}
@@ -733,31 +729,31 @@ static int configure_clock(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
/* This should be done on init() for bypass paths */
switch (wm9081->sysclk_source) {
case WM9081_SYSCLK_MCLK:
- dev_dbg(codec->dev, "Using %dHz MCLK\n", wm9081->mclk_rate);
+ dev_dbg(component->dev, "Using %dHz MCLK\n", wm9081->mclk_rate);
break;
case WM9081_SYSCLK_FLL_MCLK:
- dev_dbg(codec->dev, "Using %dHz MCLK with FLL\n",
+ dev_dbg(component->dev, "Using %dHz MCLK with FLL\n",
wm9081->mclk_rate);
break;
default:
- dev_err(codec->dev, "System clock not configured\n");
+ dev_err(component->dev, "System clock not configured\n");
return -EINVAL;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- configure_clock(codec);
+ configure_clock(component);
break;
case SND_SOC_DAPM_POST_PMD:
/* Disable the FLL if it's running */
- wm9081_set_fll(codec, 0, 0, 0);
+ wm9081_set_fll(component, 0, 0, 0);
break;
}
@@ -816,10 +812,11 @@ static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
{ "SPKP", NULL, "Speaker" },
};
-static int wm9081_set_bias_level(struct snd_soc_codec *codec,
+static int wm9081_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -827,31 +824,31 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*40k */
- snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ snd_soc_component_update_bits(component, WM9081_VMID_CONTROL,
WM9081_VMID_SEL_MASK, 0x2);
/* Normal bias current */
- snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1,
WM9081_STBY_BIAS_ENA, 0);
break;
case SND_SOC_BIAS_STANDBY:
/* Initial cold start */
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == 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,
+ snd_soc_component_update_bits(component, WM9081_ANTI_POP_CONTROL,
WM9081_LINEOUT_DISCH, 0);
/* Select startup bias source */
- snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1,
WM9081_BIAS_SRC | WM9081_BIAS_ENA,
WM9081_BIAS_SRC | WM9081_BIAS_ENA);
/* VMID 2*4k; Soft VMID ramp enable */
- snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ snd_soc_component_update_bits(component, WM9081_VMID_CONTROL,
WM9081_VMID_RAMP |
WM9081_VMID_SEL_MASK,
WM9081_VMID_RAMP | 0x6);
@@ -859,37 +856,37 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
mdelay(100);
/* Normal bias enable & soft start off */
- snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ snd_soc_component_update_bits(component, WM9081_VMID_CONTROL,
WM9081_VMID_RAMP, 0);
/* Standard bias source */
- snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1,
WM9081_BIAS_SRC, 0);
}
/* VMID 2*240k */
- snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ snd_soc_component_update_bits(component, WM9081_VMID_CONTROL,
WM9081_VMID_SEL_MASK, 0x04);
/* Standby bias current on */
- snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1,
WM9081_STBY_BIAS_ENA,
WM9081_STBY_BIAS_ENA);
break;
case SND_SOC_BIAS_OFF:
/* Startup bias source and disable bias */
- snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1,
WM9081_BIAS_SRC | WM9081_BIAS_ENA,
WM9081_BIAS_SRC);
/* Disable VMID with soft ramping */
- snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM9081_ANTI_POP_CONTROL,
WM9081_LINEOUT_DISCH,
WM9081_LINEOUT_DISCH);
@@ -903,26 +900,26 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
+ unsigned int aif2 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_2);
aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
wm9081->master = 0;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif2 |= WM9081_LRCLK_DIR;
wm9081->master = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif2 |= WM9081_BCLK_DIR;
wm9081->master = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR;
wm9081->master = 1;
break;
@@ -933,6 +930,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif2 |= WM9081_AIF_LRCLK_INV;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
aif2 |= 0x3;
break;
@@ -986,7 +984,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+ snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_2, aif2);
return 0;
}
@@ -995,23 +993,23 @@ 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);
+ struct snd_soc_component *component = dai->component;
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
int ret, i, best, best_val, cur_val;
unsigned int clk_ctrl2, aif1, aif2, aif3, aif4;
- clk_ctrl2 = snd_soc_read(codec, WM9081_CLOCK_CONTROL_2);
+ clk_ctrl2 = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_2);
clk_ctrl2 &= ~(WM9081_CLK_SYS_RATE_MASK | WM9081_SAMPLE_RATE_MASK);
- aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
+ aif1 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_1);
- aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
+ aif2 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_2);
aif2 &= ~WM9081_AIF_WL_MASK;
- aif3 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_3);
+ aif3 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_3);
aif3 &= ~WM9081_BCLK_DIV_MASK;
- aif4 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_4);
+ aif4 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_4);
aif4 &= ~WM9081_LRCLK_RATE_MASK;
wm9081->fs = params_rate(params);
@@ -1047,9 +1045,9 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
}
}
- dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm9081->bclk);
+ dev_dbg(component->dev, "Target BCLK is %dHz\n", wm9081->bclk);
- ret = configure_clock(codec);
+ ret = configure_clock(component);
if (ret != 0)
return ret;
@@ -1065,7 +1063,7 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
best_val = cur_val;
}
}
- dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n",
clk_sys_rates[best].ratio);
clk_ctrl2 |= (clk_sys_rates[best].clk_sys_rate
<< WM9081_CLK_SYS_RATE_SHIFT);
@@ -1081,7 +1079,7 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
best_val = cur_val;
}
}
- dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n",
sample_rates[best].rate);
clk_ctrl2 |= (sample_rates[best].sample_rate
<< WM9081_SAMPLE_RATE_SHIFT);
@@ -1100,12 +1098,12 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
}
}
wm9081->bclk = (wm9081->sysclk_rate * 10) / bclk_divs[best].div;
- dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
bclk_divs[best].div, wm9081->bclk);
aif3 |= bclk_divs[best].bclk_div;
/* LRCLK is a simple fraction of BCLK */
- dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm9081->bclk / wm9081->fs);
+ dev_dbg(component->dev, "LRCLK_RATE is %d\n", wm9081->bclk / wm9081->fs);
aif4 |= wm9081->bclk / wm9081->fs;
/* Apply a ReTune Mobile configuration if it's in use */
@@ -1126,51 +1124,51 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
}
s = &pdata->retune_configs[best];
- dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+ dev_dbg(component->dev, "ReTune Mobile %s tuned for %dHz\n",
s->name, s->rate);
/* If the EQ is enabled then disable it while we write out */
- eq1 = snd_soc_read(codec, WM9081_EQ_1) & WM9081_EQ_ENA;
+ eq1 = snd_soc_component_read(component, WM9081_EQ_1) & WM9081_EQ_ENA;
if (eq1 & WM9081_EQ_ENA)
- snd_soc_write(codec, WM9081_EQ_1, 0);
+ snd_soc_component_write(component, WM9081_EQ_1, 0);
/* Write out the other values */
for (i = 1; i < ARRAY_SIZE(s->config); i++)
- snd_soc_write(codec, WM9081_EQ_1 + i, s->config[i]);
+ snd_soc_component_write(component, WM9081_EQ_1 + i, s->config[i]);
eq1 |= (s->config[0] & ~WM9081_EQ_ENA);
- snd_soc_write(codec, WM9081_EQ_1, eq1);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, WM9081_CLOCK_CONTROL_2, clk_ctrl2);
+ snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_2, aif2);
+ snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_3, aif3);
+ snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_4, aif4);
return 0;
}
-static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int wm9081_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int reg;
- reg = snd_soc_read(codec, WM9081_DAC_DIGITAL_2);
+ reg = snd_soc_component_read(component, WM9081_DAC_DIGITAL_2);
if (mute)
reg |= WM9081_DAC_MUTE;
else
reg &= ~WM9081_DAC_MUTE;
- snd_soc_write(codec, WM9081_DAC_DIGITAL_2, reg);
+ snd_soc_component_write(component, WM9081_DAC_DIGITAL_2, reg);
return 0;
}
-static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+static int wm9081_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
- struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM9081_SYSCLK_MCLK:
@@ -1189,9 +1187,9 @@ static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id,
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);
+ struct snd_soc_component *component = dai->component;
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
+ unsigned int aif1 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_1);
aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK);
@@ -1221,7 +1219,7 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- snd_soc_write(codec, WM9081_AUDIO_INTERFACE_1, aif1);
+ snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_1, aif1);
return 0;
}
@@ -1235,8 +1233,9 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
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,
+ .mute_stream = wm9081_mute,
.set_tdm_slot = wm9081_set_tdm_slot,
+ .no_capture_mute = 1,
};
/* We report two channels because the CODEC processes a stereo signal, even
@@ -1254,40 +1253,38 @@ static struct snd_soc_dai_driver wm9081_dai = {
.ops = &wm9081_dai_ops,
};
-static int wm9081_probe(struct snd_soc_codec *codec)
+static int wm9081_probe(struct snd_soc_component *component)
{
- struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component);
/* Enable zero cross by default */
- snd_soc_update_bits(codec, WM9081_ANALOGUE_LINEOUT,
+ snd_soc_component_update_bits(component, WM9081_ANALOGUE_LINEOUT,
WM9081_LINEOUTZC, WM9081_LINEOUTZC);
- snd_soc_update_bits(codec, WM9081_ANALOGUE_SPEAKER_PGA,
+ snd_soc_component_update_bits(component, WM9081_ANALOGUE_SPEAKER_PGA,
WM9081_SPKPGAZC, WM9081_SPKPGAZC);
if (!wm9081->pdata.num_retune_configs) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"No ReTune Mobile data, using normal EQ\n");
- snd_soc_add_codec_controls(codec, wm9081_eq_controls,
+ snd_soc_add_component_controls(component, 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 snd_soc_component_driver soc_component_dev_wm9081 = {
+ .probe = wm9081_probe,
+ .set_sysclk = wm9081_set_sysclk,
+ .set_bias_level = wm9081_set_bias_level,
+ .controls = wm9081_snd_controls,
+ .num_controls = ARRAY_SIZE(wm9081_snd_controls),
+ .dapm_widgets = wm9081_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets),
+ .dapm_routes = wm9081_audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config wm9081_regmap = {
@@ -1299,12 +1296,10 @@ static const struct regmap_config wm9081_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm9081_reg),
.volatile_reg = wm9081_volatile_register,
.readable_reg = wm9081_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-#if IS_ENABLED(CONFIG_I2C)
-static int wm9081_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm9081_i2c_probe(struct i2c_client *i2c)
{
struct wm9081_priv *wm9081;
unsigned int reg;
@@ -1354,22 +1349,19 @@ static int wm9081_i2c_probe(struct i2c_client *i2c,
regcache_cache_only(wm9081->regmap, true);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm9081, &wm9081_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_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 void wm9081_i2c_remove(struct i2c_client *client)
+{}
static const struct i2c_device_id wm9081_i2c_id[] = {
- { "wm9081", 0 },
+ { "wm9081" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
@@ -1382,7 +1374,6 @@ static struct i2c_driver wm9081_i2c_driver = {
.remove = wm9081_i2c_remove,
.id_table = wm9081_i2c_id,
};
-#endif
module_i2c_driver(wm9081_i2c_driver);
diff --git a/sound/soc/codecs/wm9081.h b/sound/soc/codecs/wm9081.h
index 871cccb066dc..dc55807d9add 100644
--- a/sound/soc/codecs/wm9081.h
+++ b/sound/soc/codecs/wm9081.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef WM9081_H
#define WM9081_H
@@ -7,10 +8,6 @@
* 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>
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index 5d737290f547..5182f0839b7c 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -143,23 +130,23 @@ static bool wm9090_readable(struct device *dev, unsigned int reg)
}
}
-static void wait_for_dc_servo(struct snd_soc_codec *codec)
+static void wait_for_dc_servo(struct snd_soc_component *component)
{
unsigned int reg;
int count = 0;
- dev_dbg(codec->dev, "Waiting for DC servo...\n");
+ dev_dbg(component->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);
+ reg = snd_soc_component_read(component, WM9090_DC_SERVO_READBACK_0);
+ dev_dbg(component->dev, "DC servo status: %x\n", reg);
} while ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
!= WM9090_DCS_CAL_COMPLETE_MASK && count < 1000);
if ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
!= WM9090_DCS_CAL_COMPLETE_MASK)
- dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+ dev_err(component->dev, "Timed out waiting for DC Servo\n");
}
static const DECLARE_TLV_DB_RANGE(in_tlv,
@@ -251,22 +238,22 @@ SOC_SINGLE_TLV("MIXOUTR IN2B Volume", WM9090_OUTPUT_MIXER4, 0, 3, 1,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int reg = snd_soc_component_read(component, WM9090_ANALOGUE_HP_0);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, WM9090_CHARGE_PUMP_1,
WM9090_CP_ENA, WM9090_CP_ENA);
msleep(5);
- snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA);
reg |= WM9090_HPOUT1L_DLY | WM9090_HPOUT1R_DLY;
- snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+ snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg);
/* Start the DC servo. We don't currently use the
* ability to save the state since we don't have full
@@ -274,16 +261,16 @@ static int hp_ev(struct snd_soc_dapm_widget *w,
* DC offsets; see the WM8904 driver for an example of
* doing so.
*/
- snd_soc_write(codec, WM9090_DC_SERVO_0,
+ snd_soc_component_write(component, WM9090_DC_SERVO_0,
WM9090_DCS_ENA_CHAN_0 |
WM9090_DCS_ENA_CHAN_1 |
WM9090_DCS_TRIG_STARTUP_1 |
WM9090_DCS_TRIG_STARTUP_0);
- wait_for_dc_servo(codec);
+ wait_for_dc_servo(component);
reg |= WM9090_HPOUT1R_OUTP | WM9090_HPOUT1R_RMV_SHORT |
WM9090_HPOUT1L_OUTP | WM9090_HPOUT1L_RMV_SHORT;
- snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+ snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg);
break;
case SND_SOC_DAPM_PRE_PMD:
@@ -294,15 +281,15 @@ static int hp_ev(struct snd_soc_dapm_widget *w,
WM9090_HPOUT1R_DLY |
WM9090_HPOUT1R_OUTP);
- snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+ snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg);
- snd_soc_write(codec, WM9090_DC_SERVO_0, 0);
+ snd_soc_component_write(component, WM9090_DC_SERVO_0, 0);
- snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
0);
- snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, WM9090_CHARGE_PUMP_1,
WM9090_CP_ENA, 0);
break;
}
@@ -419,10 +406,10 @@ 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)
+static int wm9090_add_controls(struct snd_soc_component *component)
{
- struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm9090_priv *wm9090 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int i;
snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets,
@@ -430,7 +417,7 @@ static int wm9090_add_controls(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_add_codec_controls(codec, wm9090_controls,
+ snd_soc_add_component_controls(component, wm9090_controls,
ARRAY_SIZE(wm9090_controls));
if (wm9090->pdata.lin1_diff) {
@@ -439,7 +426,7 @@ static int wm9090_add_controls(struct snd_soc_codec *codec)
} 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,
+ snd_soc_add_component_controls(component, wm9090_in1_se_controls,
ARRAY_SIZE(wm9090_in1_se_controls));
}
@@ -449,18 +436,18 @@ static int wm9090_add_controls(struct snd_soc_codec *codec)
} 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,
+ snd_soc_add_component_controls(component, wm9090_in2_se_controls,
ARRAY_SIZE(wm9090_in2_se_controls));
}
if (wm9090->pdata.agc_ena) {
for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++)
- snd_soc_write(codec, WM9090_AGC_CONTROL_0 + i,
+ snd_soc_component_write(component, WM9090_AGC_CONTROL_0 + i,
wm9090->pdata.agc[i]);
- snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_3,
WM9090_AGC_ENA, WM9090_AGC_ENA);
} else {
- snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_3,
WM9090_AGC_ENA, 0);
}
@@ -472,19 +459,20 @@ static int wm9090_add_controls(struct snd_soc_codec *codec)
* 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,
+static int wm9090_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
+ struct wm9090_priv *wm9090 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, WM9090_ANTIPOP2, WM9090_VMID_ENA,
+ snd_soc_component_update_bits(component, WM9090_ANTIPOP2, WM9090_VMID_ENA,
WM9090_VMID_ENA);
- snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
WM9090_BIAS_ENA |
WM9090_VMID_RES_MASK,
WM9090_BIAS_ENA |
@@ -493,7 +481,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
/* Restore the register cache */
regcache_sync(wm9090->regmap);
}
@@ -502,9 +490,9 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
* ground referenced outputs and class D speaker mean that
* latency is not an issue.
*/
- snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0);
- snd_soc_update_bits(codec, WM9090_ANTIPOP2,
+ snd_soc_component_update_bits(component, WM9090_ANTIPOP2,
WM9090_VMID_ENA, 0);
break;
@@ -515,45 +503,47 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static int wm9090_probe(struct snd_soc_codec *codec)
+static int wm9090_probe(struct snd_soc_component *component)
{
/* Configure some defaults; they will be written out when we
* bring the bias up.
*/
- snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
+ snd_soc_component_update_bits(component, WM9090_IN1_LINE_INPUT_A_VOLUME,
WM9090_IN1_VU | WM9090_IN1A_ZC,
WM9090_IN1_VU | WM9090_IN1A_ZC);
- snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
+ snd_soc_component_update_bits(component, WM9090_IN1_LINE_INPUT_B_VOLUME,
WM9090_IN1_VU | WM9090_IN1B_ZC,
WM9090_IN1_VU | WM9090_IN1B_ZC);
- snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
+ snd_soc_component_update_bits(component, WM9090_IN2_LINE_INPUT_A_VOLUME,
WM9090_IN2_VU | WM9090_IN2A_ZC,
WM9090_IN2_VU | WM9090_IN2A_ZC);
- snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
+ snd_soc_component_update_bits(component, WM9090_IN2_LINE_INPUT_B_VOLUME,
WM9090_IN2_VU | WM9090_IN2B_ZC,
WM9090_IN2_VU | WM9090_IN2B_ZC);
- snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM9090_RIGHT_OUTPUT_VOLUME,
WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
- snd_soc_update_bits(codec, WM9090_CLOCKING_1,
+ snd_soc_component_update_bits(component, WM9090_CLOCKING_1,
WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
- wm9090_add_controls(codec);
+ wm9090_add_controls(component);
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 snd_soc_component_driver soc_component_dev_wm9090 = {
+ .probe = wm9090_probe,
+ .set_bias_level = wm9090_set_bias_level,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
};
static const struct regmap_config wm9090_regmap = {
@@ -564,14 +554,13 @@ static const struct regmap_config wm9090_regmap = {
.volatile_reg = wm9090_volatile,
.readable_reg = wm9090_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9090_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9090_reg_defaults),
};
-static int wm9090_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm9090_i2c_probe(struct i2c_client *i2c)
{
struct wm9090_priv *wm9090;
unsigned int reg;
@@ -607,8 +596,8 @@ static int wm9090_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm9090);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm9090, NULL, 0);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_wm9090, NULL, 0);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
return ret;
@@ -617,15 +606,9 @@ static int wm9090_i2c_probe(struct i2c_client *i2c,
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 },
+ { "wm9090" },
+ { "wm9093" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm9090_id);
@@ -635,7 +618,6 @@ static struct i2c_driver wm9090_i2c_driver = {
.name = "wm9090",
},
.probe = wm9090_i2c_probe,
- .remove = wm9090_i2c_remove,
.id_table = wm9090_id,
};
diff --git a/sound/soc/codecs/wm9090.h b/sound/soc/codecs/wm9090.h
index 29b9d9fc70b4..342068e93c05 100644
--- a/sound/soc/codecs/wm9090.h
+++ b/sound/soc/codecs/wm9090.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 744842c76a60..5c6aebe29cf1 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -1,50 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/mfd/wm97xx.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/ac97/codec.h>
+#include <sound/ac97/compat.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include "wm9705.h"
-
#define WM9705_VENDOR_ID 0x574d4c05
#define WM9705_VENDOR_ID_MASK 0xffffffff
-/*
- * 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 */
+struct wm9705_priv {
+ struct snd_ac97 *ac97;
+ struct wm97xx_platform_data *mfd_pdata;
+};
+
+static const struct reg_default wm9705_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x0a, 0x8000 },
+ { 0x0c, 0x8008 },
+ { 0x0e, 0x8008 },
+ { 0x10, 0x8808 },
+ { 0x12, 0x8808 },
+ { 0x14, 0x8808 },
+ { 0x16, 0x8808 },
+ { 0x18, 0x8808 },
+ { 0x1a, 0x0000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x22, 0x0000 },
+ { 0x26, 0x000f },
+ { 0x28, 0x0605 },
+ { 0x2a, 0x0000 },
+ { 0x2c, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x34, 0x2000 },
+ { 0x5a, 0x0000 },
+ { 0x5c, 0x0000 },
+ { 0x72, 0x0808 },
+ { 0x74, 0x0000 },
+ { 0x76, 0x0006 },
+ { 0x78, 0x0000 },
+ { 0x7a, 0x0000 },
+};
+
+static const struct regmap_config wm9705_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x7e,
+ .cache_type = REGCACHE_MAPLE,
+
+ .volatile_reg = regmap_ac97_default_volatile,
+
+ .reg_defaults = wm9705_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults),
};
static const struct snd_kcontrol_new wm9705_snd_ac97_controls[] = {
@@ -203,57 +228,20 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
{"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;
+ struct snd_soc_component *component = dai->component;
int reg;
- u16 vra;
- vra = ac97_read(codec, AC97_EXTENDED_STATUS);
- ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else
reg = AC97_PCM_LR_ADC_RATE;
- return ac97_write(codec, reg, substream->runtime->rate);
+ return snd_soc_component_write(component, reg, substream->runtime->rate);
}
#define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
@@ -297,29 +285,26 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
};
#ifdef CONFIG_PM
-static int wm9705_soc_suspend(struct snd_soc_codec *codec)
+static int wm9705_soc_suspend(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
-
- soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
+ regcache_cache_bypass(component->regmap, true);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
+ regcache_cache_bypass(component->regmap, false);
return 0;
}
-static int wm9705_soc_resume(struct snd_soc_codec *codec)
+static int wm9705_soc_resume(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- int i, ret;
- u16 *cache = codec->reg_cache;
+ struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component);
+ int ret;
- ret = snd_ac97_reset(ac97, true, WM9705_VENDOR_ID,
+ ret = snd_ac97_reset(wm9705->ac97, true, WM9705_VENDOR_ID,
WM9705_VENDOR_ID_MASK);
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]);
- }
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -328,60 +313,76 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
#define wm9705_soc_resume NULL
#endif
-static int wm9705_soc_probe(struct snd_soc_codec *codec)
+static int wm9705_soc_probe(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97;
-
- ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID,
- WM9705_VENDOR_ID_MASK);
- if (IS_ERR(ac97)) {
- dev_err(codec->dev, "Failed to register AC97 codec\n");
- return PTR_ERR(ac97);
+ struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap;
+
+ if (wm9705->mfd_pdata) {
+ wm9705->ac97 = wm9705->mfd_pdata->ac97;
+ regmap = wm9705->mfd_pdata->regmap;
+ } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) {
+ wm9705->ac97 = snd_soc_new_ac97_component(component, WM9705_VENDOR_ID,
+ WM9705_VENDOR_ID_MASK);
+ if (IS_ERR(wm9705->ac97)) {
+ dev_err(component->dev, "Failed to register AC97 codec\n");
+ return PTR_ERR(wm9705->ac97);
+ }
+
+ regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config);
+ if (IS_ERR(regmap)) {
+ snd_soc_free_ac97_component(wm9705->ac97);
+ return PTR_ERR(regmap);
+ }
+ } else {
+ return -ENXIO;
}
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_set_drvdata(component, wm9705->ac97);
+ snd_soc_component_init_regmap(component, regmap);
return 0;
}
-static int wm9705_soc_remove(struct snd_soc_codec *codec)
+static void wm9705_soc_remove(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component);
- snd_soc_free_ac97_codec(ac97);
- return 0;
+ if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9705->mfd_pdata) {
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(wm9705->ac97);
+ }
}
-static 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 const struct snd_soc_component_driver soc_component_dev_wm9705 = {
+ .probe = wm9705_soc_probe,
+ .remove = wm9705_soc_remove,
+ .suspend = wm9705_soc_suspend,
+ .resume = wm9705_soc_resume,
+ .controls = wm9705_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
+ .dapm_widgets = wm9705_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
+ .dapm_routes = wm9705_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm9705_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_wm9705, wm9705_dai, ARRAY_SIZE(wm9705_dai));
-}
+ struct wm9705_priv *wm9705;
-static int wm9705_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ wm9705 = devm_kzalloc(&pdev->dev, sizeof(*wm9705), GFP_KERNEL);
+ if (wm9705 == NULL)
+ return -ENOMEM;
+
+ wm9705->mfd_pdata = dev_get_platdata(&pdev->dev);
+ platform_set_drvdata(pdev, wm9705);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm9705, wm9705_dai, ARRAY_SIZE(wm9705_dai));
}
static struct platform_driver wm9705_codec_driver = {
@@ -390,7 +391,6 @@ static struct platform_driver wm9705_codec_driver = {
},
.probe = wm9705_probe,
- .remove = wm9705_remove,
};
module_platform_driver(wm9705_codec_driver);
diff --git a/sound/soc/codecs/wm9705.h b/sound/soc/codecs/wm9705.h
deleted file mode 100644
index 23ea9ce47359..000000000000
--- a/sound/soc/codecs/wm9705.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * 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/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 488a92224249..83cd42fa0c28 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -1,27 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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/mfd/wm97xx.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/ac97/codec.h>
+#include <sound/ac97/compat.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include "wm9712.h"
#define WM9712_VENDOR_ID 0x574d4c12
#define WM9712_VENDOR_ID_MASK 0xffffffff
@@ -30,33 +29,69 @@ struct wm9712_priv {
struct snd_ac97 *ac97;
unsigned int hp_mixer[2];
struct mutex lock;
+ struct wm97xx_platform_data *mfd_pdata;
};
-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);
+static const struct reg_default wm9712_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x08, 0x0f0f },
+ { 0x0a, 0xaaa0 },
+ { 0x0c, 0xc008 },
+ { 0x0e, 0x6808 },
+ { 0x10, 0xe808 },
+ { 0x12, 0xaaa0 },
+ { 0x14, 0xad00 },
+ { 0x16, 0x8000 },
+ { 0x18, 0xe808 },
+ { 0x1a, 0x3000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x22, 0x0000 },
+ { 0x26, 0x000f },
+ { 0x28, 0x0605 },
+ { 0x2a, 0x0410 },
+ { 0x2c, 0xbb80 },
+ { 0x2e, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x34, 0x2000 },
+ { 0x4c, 0xf83e },
+ { 0x4e, 0xffff },
+ { 0x50, 0x0000 },
+ { 0x52, 0x0000 },
+ { 0x56, 0xf83e },
+ { 0x58, 0x0008 },
+ { 0x5c, 0x0000 },
+ { 0x60, 0xb032 },
+ { 0x62, 0x3e00 },
+ { 0x64, 0x0000 },
+ { 0x76, 0x0006 },
+ { 0x78, 0x0001 },
+ { 0x7a, 0x0000 },
+};
-/*
- * 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 */
+static bool wm9712_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_REC_GAIN:
+ return true;
+ default:
+ return regmap_ac97_default_volatile(dev, reg);
+ }
+}
+
+static const struct regmap_config wm9712_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x7e,
+ .cache_type = REGCACHE_MAPLE,
+
+ .volatile_reg = wm9712_volatile_reg,
+
+ .reg_defaults = wm9712_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults),
};
#define HPL_MIXER 0x0
@@ -180,14 +215,14 @@ static const unsigned int wm9712_mixer_mute_regs[] = {
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);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
unsigned int val = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = {};
bool change;
mixer = mc->shift >> 8;
@@ -224,9 +259,9 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
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 snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int shift, mixer;
@@ -240,13 +275,9 @@ static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
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) \
-}
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) \
+ SOC_SINGLE_EXT(xname, SND_SOC_NOPM, ((xmixer) << 8) | (xshift), \
+ 1, 0, wm9712_hp_mixer_get, wm9712_hp_mixer_put)
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
@@ -485,75 +516,36 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
{"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;
+ struct snd_soc_component *component = dai->component;
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);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else
reg = AC97_PCM_LR_ADC_RATE;
- return ac97_write(codec, reg, runtime->rate);
+ return snd_soc_component_write(component, reg, runtime->rate);
}
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 vra, xsle;
+ struct snd_soc_component *component = dai->component;
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);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1);
+ snd_soc_component_update_bits(component, AC97_PCI_SID, 0x8000, 0x8000);
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
- return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+ return snd_soc_component_write(component, AC97_PCM_SURR_DAC_RATE, runtime->rate);
}
#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
@@ -597,7 +589,7 @@ static struct snd_soc_dai_driver wm9712_dai[] = {
}
};
-static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+static int wm9712_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -605,89 +597,98 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ snd_soc_component_write(component, 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);
+ snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
break;
}
return 0;
}
-static int wm9712_soc_resume(struct snd_soc_codec *codec)
+static int wm9712_soc_resume(struct snd_soc_component *component)
{
- struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
- int i, ret;
- u16 *cache = codec->reg_cache;
+ struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
ret = snd_ac97_reset(wm9712->ac97, true, WM9712_VENDOR_ID,
WM9712_VENDOR_ID_MASK);
if (ret < 0)
return ret;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, 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]);
- }
- }
+ if (ret == 0)
+ snd_soc_component_cache_sync(component);
return ret;
}
-static int wm9712_soc_probe(struct snd_soc_codec *codec)
+static int wm9712_soc_probe(struct snd_soc_component *component)
{
- struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
- int ret;
+ struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap;
+
+ if (wm9712->mfd_pdata) {
+ wm9712->ac97 = wm9712->mfd_pdata->ac97;
+ regmap = wm9712->mfd_pdata->regmap;
+ } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) {
+ int ret;
+
+ wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID,
+ WM9712_VENDOR_ID_MASK);
+ if (IS_ERR(wm9712->ac97)) {
+ ret = PTR_ERR(wm9712->ac97);
+ dev_err(component->dev,
+ "Failed to register AC97 codec: %d\n", ret);
+ return ret;
+ }
- wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID,
- WM9712_VENDOR_ID_MASK);
- if (IS_ERR(wm9712->ac97)) {
- ret = PTR_ERR(wm9712->ac97);
- dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
- return ret;
+ regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config);
+ if (IS_ERR(regmap)) {
+ snd_soc_free_ac97_component(wm9712->ac97);
+ return PTR_ERR(regmap);
+ }
+ } else {
+ return -ENXIO;
}
+ snd_soc_component_init_regmap(component, regmap);
+
/* set alc mux to none */
- ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
+ snd_soc_component_update_bits(component, AC97_VIDEO, 0x3000, 0x3000);
return 0;
}
-static int wm9712_soc_remove(struct snd_soc_codec *codec)
+static void wm9712_soc_remove(struct snd_soc_component *component)
{
- struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
- snd_soc_free_ac97_codec(wm9712->ac97);
- return 0;
+ if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) {
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(wm9712->ac97);
+ }
}
-static 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 const struct snd_soc_component_driver soc_component_dev_wm9712 = {
+ .probe = wm9712_soc_probe,
+ .remove = wm9712_soc_remove,
+ .resume = wm9712_soc_resume,
+ .set_bias_level = wm9712_set_bias_level,
+ .controls = wm9712_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm9712_probe(struct platform_device *pdev)
@@ -700,28 +701,22 @@ static int wm9712_probe(struct platform_device *pdev)
mutex_init(&wm9712->lock);
+ wm9712->mfd_pdata = dev_get_platdata(&pdev->dev);
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;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
}
-static struct platform_driver wm9712_codec_driver = {
+static struct platform_driver wm9712_component_driver = {
.driver = {
.name = "wm9712-codec",
},
.probe = wm9712_probe,
- .remove = wm9712_remove,
};
-module_platform_driver(wm9712_codec_driver);
+module_platform_driver(wm9712_component_driver);
MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
MODULE_AUTHOR("Liam Girdwood");
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
deleted file mode 100644
index fb69c3aa4ed0..000000000000
--- a/sound/soc/codecs/wm9712.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * 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/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 79e143625ac3..b3bbecf074ee 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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
@@ -17,12 +13,15 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/mfd/wm97xx.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/compat.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
@@ -38,6 +37,7 @@ struct wm9713_priv {
u32 pll_in; /* PLL input frequency */
unsigned int hp_mixer[2];
struct mutex lock;
+ struct wm97xx_platform_data *mfd_pdata;
};
#define HPL_MIXER 0
@@ -192,16 +192,16 @@ 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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD))
return -EINVAL;
/* Gracefully shut down the voice interface. */
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0200);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, 0x0200);
schedule_timeout_interruptible(msecs_to_jiffies(1));
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0f00);
- snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x1000, 0x1000);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, 0x0f00);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x1000, 0x1000);
return 0;
}
@@ -224,14 +224,14 @@ static const unsigned int wm9713_mixer_mute_regs[] = {
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);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component);
unsigned int val = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = {};
bool change;
mixer = mc->shift >> 8;
@@ -268,9 +268,9 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
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 snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, shift;
@@ -284,13 +284,9 @@ static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
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) \
-}
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) \
+ SOC_DOUBLE_EXT(xname, SND_SOC_NOPM, xshift, xmixer, 1, 0, \
+ wm9713_hp_mixer_get, wm9713_hp_mixer_put)
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
@@ -727,7 +723,7 @@ static const struct regmap_config wm9713_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9713_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),
@@ -749,13 +745,13 @@ struct _pll_div {
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 22) * 10)
-static void pll_factors(struct snd_soc_codec *codec,
+static void pll_factors(struct snd_soc_component *component,
struct _pll_div *pll_div, unsigned int source)
{
u64 Kpart;
unsigned int K, Ndiv, Nmod, target;
- /* The the PLL output is always 98.304MHz. */
+ /* The PLL output is always 98.304MHz. */
target = 98304000;
/* If the input frequency is over 14.4MHz then scale it down. */
@@ -785,7 +781,7 @@ static void pll_factors(struct snd_soc_codec *codec,
Ndiv = target / source;
if ((Ndiv < 5) || (Ndiv > 12))
- dev_warn(codec->dev,
+ dev_warn(component->dev,
"WM9713 PLL N value %u out of recommended range!\n",
Ndiv);
@@ -807,32 +803,32 @@ static void pll_factors(struct snd_soc_codec *codec,
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,
+static int wm9713_set_pll(struct snd_soc_component *component,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
- struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component);
u16 reg, reg2;
struct _pll_div pll_div;
/* turn PLL off ? */
if (freq_in == 0) {
/* disable PLL power and select ext source */
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0080);
- snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0200);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0080, 0x0080);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x0200, 0x0200);
wm9713->pll_in = 0;
return 0;
}
- pll_factors(codec, &pll_div, freq_in);
+ pll_factors(component, &pll_div, freq_in);
if (pll_div.k == 0) {
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
(pll_div.divsel << 9) | (pll_div.divctl << 8);
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
} else {
/* write the fractional k to the reg 0x46 pages */
reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) |
@@ -840,31 +836,31 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
/* K [21:20] */
reg = reg2 | (0x5 << 4) | (pll_div.k >> 20);
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
/* K [19:16] */
reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf);
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
/* K [15:12] */
reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf);
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
/* K [11:8] */
reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf);
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
/* K [7:4] */
reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf);
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */
- snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
+ snd_soc_component_write(component, AC97_LINE1_LEVEL, reg);
}
/* turn PLL on and select as source */
- snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0000);
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0000);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x0200, 0x0000);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0080, 0x0000);
wm9713->pll_in = freq_in;
/* wait 10ms AC97 link frames for the link to stabilise */
@@ -875,8 +871,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
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);
+ struct snd_soc_component *component = codec_dai->component;
+ return wm9713_set_pll(component, pll_id, freq_in, freq_out);
}
/*
@@ -886,10 +882,10 @@ static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
int tristate)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
if (tristate)
- snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
+ snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER,
0x6000, 0x0000);
return 0;
@@ -902,30 +898,30 @@ static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
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;
+ struct snd_soc_component *component = codec_dai->component;
switch (div_id) {
case WM9713_PCMCLK_DIV:
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, div);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, div);
break;
case WM9713_CLKA_MULT:
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0002, div);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0002, div);
break;
case WM9713_CLKB_MULT:
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0004, div);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0004, div);
break;
case WM9713_HIFI_DIV:
- snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x7000, div);
+ snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x7000, div);
break;
case WM9713_PCMBCLK_DIV:
- snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, 0x0e00, div);
+ snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 0x0e00, div);
break;
case WM9713_PCMCLK_PLL_DIV:
- snd_soc_update_bits(codec, AC97_LINE1_LEVEL,
+ snd_soc_component_update_bits(component, AC97_LINE1_LEVEL,
0x007f, div | 0x60);
break;
case WM9713_HIFI_PLL_DIV:
- snd_soc_update_bits(codec, AC97_LINE1_LEVEL,
+ snd_soc_component_update_bits(component, AC97_LINE1_LEVEL,
0x007f, div | 0x70);
break;
default:
@@ -938,25 +934,25 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
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 = snd_soc_read(codec, AC97_GPIO_CFG) & 0xffc5;
+ struct snd_soc_component *component = codec_dai->component;
+ u16 gpio = snd_soc_component_read(component, AC97_GPIO_CFG) & 0xffc5;
u16 reg = 0x8000;
/* clock masters */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
reg |= 0x4000;
gpio |= 0x0010;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
reg |= 0x6000;
gpio |= 0x0018;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg |= 0x2000;
gpio |= 0x001a;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
gpio |= 0x0012;
break;
}
@@ -992,8 +988,8 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
}
- snd_soc_write(codec, AC97_GPIO_CFG, gpio);
- snd_soc_write(codec, AC97_CENTER_LFE_MASTER, reg);
+ snd_soc_component_write(component, AC97_GPIO_CFG, gpio);
+ snd_soc_component_write(component, AC97_CENTER_LFE_MASTER, reg);
return 0;
}
@@ -1001,22 +997,22 @@ 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;
+ struct snd_soc_component *component = dai->component;
/* enable PCM interface in master mode */
switch (params_width(params)) {
case 16:
break;
case 20:
- snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
+ snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER,
0x000c, 0x0004);
break;
case 24:
- snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
+ snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER,
0x000c, 0x0008);
break;
case 32:
- snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
+ snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER,
0x000c, 0x000c);
break;
}
@@ -1026,33 +1022,33 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *runtime = substream->runtime;
int reg;
- snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else
reg = AC97_PCM_LR_ADC_RATE;
- return snd_soc_write(codec, reg, runtime->rate);
+ return snd_soc_component_write(component, reg, runtime->rate);
}
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
- snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
+ snd_soc_component_update_bits(component, AC97_PCI_SID, 0x8000, 0x8000);
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
- return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+ return snd_soc_component_write(component, AC97_PCM_SURR_DAC_RATE, runtime->rate);
}
#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \
@@ -1134,52 +1130,53 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
.rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
.ops = &wm9713_dai_ops_voice,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static int wm9713_set_bias_level(struct snd_soc_codec *codec,
+static int wm9713_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
/* enable thermal shutdown */
- snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xe400, 0x0000);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0xe400, 0x0000);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* enable master bias and vmid */
- snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xc400, 0x0000);
- snd_soc_write(codec, AC97_POWERDOWN, 0x0000);
+ snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0xc400, 0x0000);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0x0000);
break;
case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
- snd_soc_write(codec, AC97_EXTENDED_MID, 0xffff);
- snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
- snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
+ snd_soc_component_write(component, AC97_EXTENDED_MID, 0xffff);
+ snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
break;
}
return 0;
}
-static int wm9713_soc_suspend(struct snd_soc_codec *codec)
+static int wm9713_soc_suspend(struct snd_soc_component *component)
{
/* Disable everything except touchpanel - that will be handled
* by the touch driver and left disabled if touch is not in
* use. */
- snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x7fff,
+ snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x7fff,
0x7fff);
- snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
- snd_soc_write(codec, AC97_POWERDOWN, 0x6f00);
- snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
+ snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0x6f00);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
return 0;
}
-static int wm9713_soc_resume(struct snd_soc_codec *codec)
+static int wm9713_soc_resume(struct snd_soc_component *component)
{
- struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int ret;
ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID,
@@ -1187,67 +1184,76 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_force_bias_level(dapm, 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);
+ wm9713_set_pll(component, 0, wm9713->pll_in, 0);
/* only synchronise the codec if warm reset failed */
if (ret == 0) {
- regcache_mark_dirty(codec->component.regmap);
- snd_soc_cache_sync(codec);
+ regcache_mark_dirty(component->regmap);
+ snd_soc_component_cache_sync(component);
}
return ret;
}
-static int wm9713_soc_probe(struct snd_soc_codec *codec)
+static int wm9713_soc_probe(struct snd_soc_component *component)
{
- struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
- struct regmap *regmap;
-
- wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID,
- WM9713_VENDOR_ID_MASK);
- if (IS_ERR(wm9713->ac97))
- return PTR_ERR(wm9713->ac97);
-
- regmap = devm_regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config);
- if (IS_ERR(regmap)) {
- snd_soc_free_ac97_codec(wm9713->ac97);
- return PTR_ERR(regmap);
+ struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = NULL;
+
+ if (wm9713->mfd_pdata) {
+ wm9713->ac97 = wm9713->mfd_pdata->ac97;
+ regmap = wm9713->mfd_pdata->regmap;
+ } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) {
+ wm9713->ac97 = snd_soc_new_ac97_component(component, WM9713_VENDOR_ID,
+ WM9713_VENDOR_ID_MASK);
+ if (IS_ERR(wm9713->ac97))
+ return PTR_ERR(wm9713->ac97);
+ regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config);
+ if (IS_ERR(regmap)) {
+ snd_soc_free_ac97_component(wm9713->ac97);
+ return PTR_ERR(regmap);
+ }
+ } else {
+ return -ENXIO;
}
- snd_soc_codec_init_regmap(codec, regmap);
+ snd_soc_component_init_regmap(component, regmap);
/* unmute the adc - move to kcontrol */
- snd_soc_update_bits(codec, AC97_CD, 0x7fff, 0x0000);
+ snd_soc_component_update_bits(component, AC97_CD, 0x7fff, 0x0000);
return 0;
}
-static int wm9713_soc_remove(struct snd_soc_codec *codec)
+static void wm9713_soc_remove(struct snd_soc_component *component)
{
- struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component);
- snd_soc_codec_exit_regmap(codec);
- snd_soc_free_ac97_codec(wm9713->ac97);
- return 0;
+ if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9713->mfd_pdata) {
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(wm9713->ac97);
+ }
}
-static 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,
- .set_bias_level = wm9713_set_bias_level,
-
- .controls = wm9713_snd_ac97_controls,
- .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
- .dapm_widgets = wm9713_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
- .dapm_routes = wm9713_audio_map,
- .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
+static const struct snd_soc_component_driver soc_component_dev_wm9713 = {
+ .probe = wm9713_soc_probe,
+ .remove = wm9713_soc_remove,
+ .suspend = wm9713_soc_suspend,
+ .resume = wm9713_soc_resume,
+ .set_bias_level = wm9713_set_bias_level,
+ .controls = wm9713_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
+ .dapm_widgets = wm9713_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
+ .dapm_routes = wm9713_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int wm9713_probe(struct platform_device *pdev)
@@ -1260,16 +1266,11 @@ static int wm9713_probe(struct platform_device *pdev)
mutex_init(&wm9713->lock);
+ wm9713->mfd_pdata = dev_get_platdata(&pdev->dev);
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;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
}
static struct platform_driver wm9713_codec_driver = {
@@ -1278,7 +1279,6 @@ static struct platform_driver wm9713_codec_driver = {
},
.probe = wm9713_probe,
- .remove = wm9713_remove,
};
module_platform_driver(wm9713_codec_driver);
diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h
index 53df11b1f727..f0800dcca9b8 100644
--- a/sound/soc/codecs/wm9713.h
+++ b/sound/soc/codecs/wm9713.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* wm9713.h -- WM9713 Soc Audio driver
*/
@@ -41,8 +42,4 @@
#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
-
#endif
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 33806d487b8a..17cec79245d4 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1,15 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/array_size.h>
+#include <linux/cleanup.h>
+#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -17,11 +17,10 @@
#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/string.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <sound/core.h>
@@ -32,174 +31,30 @@
#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__)
+ dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_err(_dsp, fmt, ...) \
- dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+ dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_warn(_dsp, fmt, ...) \
- dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+ dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
#define adsp_info(_dsp, fmt, ...) \
- dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+ dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__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
-
-#define ADSP2_SCRATCH0 0x40
-#define ADSP2_SCRATCH1 0x41
-#define ADSP2_SCRATCH2 0x42
-#define ADSP2_SCRATCH3 0x43
-
-/*
- * ADSP2 Control
- */
+ dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
-#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 */
+#define compr_err(_obj, fmt, ...) \
+ adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
+ ##__VA_ARGS__)
+#define compr_dbg(_obj, fmt, ...) \
+ adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
+ ##__VA_ARGS__)
-/*
- * 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);
+#define ADSP_MAX_STD_CTRL_SIZE 512
- 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);
- }
-}
+static const struct cs_dsp_client_ops wm_adsp1_client_ops;
+static const struct cs_dsp_client_ops wm_adsp2_client_ops;
#define WM_ADSP_FW_MBC_VSS 0
#define WM_ADSP_FW_HIFI 1
@@ -211,9 +66,11 @@ static void wm_adsp_buf_free(struct list_head *list)
#define WM_ADSP_FW_ASR 7
#define WM_ADSP_FW_TRACE 8
#define WM_ADSP_FW_SPK_PROT 9
-#define WM_ADSP_FW_MISC 10
+#define WM_ADSP_FW_SPK_CALI 10
+#define WM_ADSP_FW_SPK_DIAG 11
+#define WM_ADSP_FW_MISC 12
-#define WM_ADSP_NUM_FW 11
+#define WM_ADSP_NUM_FW 13
static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
@@ -226,6 +83,8 @@ static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_ASR] = "ASR Assist",
[WM_ADSP_FW_TRACE] = "Dbg Trace",
[WM_ADSP_FW_SPK_PROT] = "Protection",
+ [WM_ADSP_FW_SPK_CALI] = "Calibration",
+ [WM_ADSP_FW_SPK_DIAG] = "Diagnostic",
[WM_ADSP_FW_MISC] = "Misc",
};
@@ -240,7 +99,13 @@ struct wm_adsp_system_config_xm_hdr {
__be32 wdma[8];
__be32 build_job_name[3];
__be32 build_job_number;
-};
+} __packed;
+
+struct wm_halo_system_config_xm_hdr {
+ __be32 halo_heartbeat;
+ __be32 build_job_name[3];
+ __be32 build_job_number;
+} __packed;
struct wm_adsp_alg_xm_struct {
__be32 magic;
@@ -251,15 +116,21 @@ struct wm_adsp_alg_xm_struct {
__be32 high_water_mark;
__be32 low_water_mark;
__be64 smoothed_power;
-};
+} __packed;
+
+struct wm_adsp_host_buf_coeff_v1 {
+ __be32 host_buf_ptr; /* Host buffer pointer */
+ __be32 versions; /* Version numbers */
+ __be32 name[4]; /* The buffer name */
+} __packed;
struct wm_adsp_buffer {
- __be32 X_buf_base; /* XM base addr of first X area */
- __be32 X_buf_size; /* Size of 1st X area in words */
- __be32 X_buf_base2; /* XM base addr of 2nd X area */
- __be32 X_buf_brk; /* Total X size in words */
- __be32 Y_buf_base; /* YM base addr of Y area */
- __be32 wrap; /* Total size X and Y in words */
+ __be32 buf1_base; /* Base addr of first buffer area */
+ __be32 buf1_size; /* Size of buf1 area in DSP words */
+ __be32 buf2_base; /* Base addr of 2nd buffer area */
+ __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */
+ __be32 buf3_base; /* Base addr of buf3 area */
+ __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */
__be32 high_water_mark; /* Point at which IRQ is asserted */
__be32 irq_count; /* bits 1-31 count IRQ assertions */
__be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */
@@ -272,10 +143,14 @@ struct wm_adsp_buffer {
__be32 min_free; /* min free space since stream start */
__be32 blocks_written[2]; /* total blocks written (64 bit) */
__be32 words_written[2]; /* total words written (64 bit) */
-};
+} __packed;
+
+struct wm_adsp_compr;
struct wm_adsp_compr_buf {
+ struct list_head list;
struct wm_adsp *dsp;
+ struct wm_adsp_compr *compr;
struct wm_adsp_buffer_region *regions;
u32 host_buf_ptr;
@@ -284,9 +159,13 @@ struct wm_adsp_compr_buf {
u32 irq_count;
int read_index;
int avail;
+ int host_buf_mem_type;
+
+ char *name;
};
struct wm_adsp_compr {
+ struct list_head list;
struct wm_adsp *dsp;
struct wm_adsp_compr_buf *buf;
@@ -294,15 +173,17 @@ struct wm_adsp_compr {
struct snd_compressed_buffer size;
u32 *raw_buf;
- unsigned int copied_total;
-};
+ u64 copied_total;
-#define WM_ADSP_DATA_WORD_SIZE 3
+ unsigned int sample_rate;
+
+ const char *name;
+};
#define WM_ADSP_MIN_FRAGMENTS 1
#define WM_ADSP_MAX_FRAGMENTS 256
-#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE)
-#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE)
+#define WM_ADSP_MIN_FRAGMENT_SIZE (16 * CS_DSP_DATA_WORD_SIZE)
+#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE)
#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
@@ -312,6 +193,11 @@ struct wm_adsp_compr {
#define ALG_XM_FIELD(field) \
(offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
+#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1
+
+#define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00
+#define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8
+
static int wm_adsp_buffer_init(struct wm_adsp *dsp);
static int wm_adsp_buffer_free(struct wm_adsp *dsp);
@@ -328,21 +214,21 @@ struct wm_adsp_buffer_region_def {
unsigned int size_offset;
};
-static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+static const struct wm_adsp_buffer_region_def default_regions[] = {
{
.mem_type = WMFW_ADSP2_XM,
- .base_offset = HOST_BUFFER_FIELD(X_buf_base),
- .size_offset = HOST_BUFFER_FIELD(X_buf_size),
+ .base_offset = HOST_BUFFER_FIELD(buf1_base),
+ .size_offset = HOST_BUFFER_FIELD(buf1_size),
},
{
.mem_type = WMFW_ADSP2_XM,
- .base_offset = HOST_BUFFER_FIELD(X_buf_base2),
- .size_offset = HOST_BUFFER_FIELD(X_buf_brk),
+ .base_offset = HOST_BUFFER_FIELD(buf2_base),
+ .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size),
},
{
.mem_type = WMFW_ADSP2_YM,
- .base_offset = HOST_BUFFER_FIELD(Y_buf_base),
- .size_offset = HOST_BUFFER_FIELD(wrap),
+ .base_offset = HOST_BUFFER_FIELD(buf3_base),
+ .size_offset = HOST_BUFFER_FIELD(buf_total_size),
},
};
@@ -350,20 +236,38 @@ struct wm_adsp_fw_caps {
u32 id;
struct snd_codec_desc desc;
int num_regions;
- struct wm_adsp_buffer_region_def *region_defs;
+ const struct wm_adsp_buffer_region_def *region_defs;
};
-static const struct wm_adsp_fw_caps ez2control_caps[] = {
+static const struct wm_adsp_fw_caps ctrl_caps[] = {
{
.id = SND_AUDIOCODEC_BESPOKE,
.desc = {
- .max_ch = 1,
+ .max_ch = 8,
.sample_rates = { 16000 },
.num_sample_rates = 1,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .num_regions = ARRAY_SIZE(ez2control_regions),
- .region_defs = ez2control_regions,
+ .num_regions = ARRAY_SIZE(default_regions),
+ .region_defs = default_regions,
+ },
+};
+
+static const struct wm_adsp_fw_caps trace_caps[] = {
+ {
+ .id = SND_AUDIOCODEC_BESPOKE,
+ .desc = {
+ .max_ch = 8,
+ .sample_rates = {
+ 4000, 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 64000, 88200,
+ 96000, 176400, 192000
+ },
+ .num_sample_rates = 15,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .num_regions = ARRAY_SIZE(default_regions),
+ .region_defs = default_regions,
},
};
@@ -372,6 +276,7 @@ static const struct {
int compr_direction;
int num_caps;
const struct wm_adsp_fw_caps *caps;
+ bool voice_trigger;
} wm_adsp_fw[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
[WM_ADSP_FW_HIFI] = { .file = "hifi" },
@@ -382,1979 +287,976 @@ static const struct {
[WM_ADSP_FW_CTRL] = {
.file = "ctrl",
.compr_direction = SND_COMPRESS_CAPTURE,
- .num_caps = ARRAY_SIZE(ez2control_caps),
- .caps = ez2control_caps,
+ .num_caps = ARRAY_SIZE(ctrl_caps),
+ .caps = ctrl_caps,
+ .voice_trigger = true,
},
[WM_ADSP_FW_ASR] = { .file = "asr" },
- [WM_ADSP_FW_TRACE] = { .file = "trace" },
- [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
+ [WM_ADSP_FW_TRACE] = {
+ .file = "trace",
+ .compr_direction = SND_COMPRESS_CAPTURE,
+ .num_caps = ARRAY_SIZE(trace_caps),
+ .caps = trace_caps,
+ },
+ [WM_ADSP_FW_SPK_PROT] = {
+ .file = "spk-prot",
+ .compr_direction = SND_COMPRESS_CAPTURE,
+ .num_caps = ARRAY_SIZE(trace_caps),
+ .caps = trace_caps,
+ },
+ [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" },
+ [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" },
[WM_ADSP_FW_MISC] = { .file = "misc" },
};
-struct wm_coeff_ctl_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;
- const char *fw_name;
- struct wm_adsp_alg_region alg_region;
- struct wm_coeff_ctl_ops ops;
- struct wm_adsp *dsp;
- unsigned int enabled:1;
- struct list_head list;
- void *cache;
- unsigned int offset;
- size_t len;
- unsigned int set:1;
- struct snd_kcontrol *kcontrol;
- unsigned int flags;
-};
-
-#ifdef CONFIG_DEBUG_FS
-static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
-{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
- kfree(dsp->wmfw_file_name);
- dsp->wmfw_file_name = tmp;
-}
-
-static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
-{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
- kfree(dsp->bin_file_name);
- dsp->bin_file_name = tmp;
-}
-
-static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
-{
- kfree(dsp->wmfw_file_name);
- kfree(dsp->bin_file_name);
- dsp->wmfw_file_name = NULL;
- dsp->bin_file_name = NULL;
-}
-
-static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct wm_adsp *dsp = file->private_data;
- ssize_t ret;
-
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->wmfw_file_name || !dsp->running)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->wmfw_file_name,
- strlen(dsp->wmfw_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
-}
-
-static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct wm_adsp *dsp = file->private_data;
- ssize_t ret;
-
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->bin_file_name || !dsp->running)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->bin_file_name,
- strlen(dsp->bin_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
-}
-
-static const struct {
- const char *name;
- const struct file_operations fops;
-} wm_adsp_debugfs_fops[] = {
- {
- .name = "wmfw_file_name",
- .fops = {
- .open = simple_open,
- .read = wm_adsp_debugfs_wmfw_read,
- },
- },
- {
- .name = "bin_file_name",
- .fops = {
- .open = simple_open,
- .read = wm_adsp_debugfs_bin_read,
- },
- },
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ struct soc_bytes_ext bytes_ext;
+ struct work_struct work;
};
-static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
- struct snd_soc_codec *codec)
-{
- struct dentry *root = NULL;
- char *root_name;
- int i;
-
- if (!codec->component.debugfs_root) {
- adsp_err(dsp, "No codec debugfs root\n");
- goto err;
- }
-
- root_name = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!root_name)
- goto err;
-
- snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num);
- root = debugfs_create_dir(root_name, codec->component.debugfs_root);
- kfree(root_name);
-
- if (!root)
- goto err;
-
- if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
- goto err;
-
- if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
- goto err;
-
- if (!debugfs_create_x32("fw_version", S_IRUGO, root,
- &dsp->fw_id_version))
- goto err;
-
- for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
- if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
- S_IRUGO, root, dsp,
- &wm_adsp_debugfs_fops[i].fops))
- goto err;
- }
-
- dsp->debugfs_root = root;
- return;
-
-err:
- debugfs_remove_recursive(root);
- adsp_err(dsp, "Failed to create debugfs\n");
-}
-
-static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
-{
- wm_adsp_debugfs_clear(dsp);
- debugfs_remove_recursive(dsp->debugfs_root);
-}
-#else
-static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
- struct snd_soc_codec *codec)
-{
-}
-
-static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
-{
-}
-
-static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
- const char *s)
-{
-}
-
-static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
- const char *s)
+int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
-}
-
-static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
-{
-}
-#endif
-
-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 snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
+ ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
return 0;
}
+EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
-static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
- int ret = 0;
+ struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
+ int ret = 1;
- if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
+ if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
return 0;
- if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+ if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
return -EINVAL;
- mutex_lock(&dsp[e->shift_l].pwr_lock);
+ mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock);
- if (dsp[e->shift_l].running || dsp[e->shift_l].compr)
+ if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list))
ret = -EBUSY;
else
- dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+ dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
- mutex_unlock(&dsp[e->shift_l].pwr_lock);
+ mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock);
return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
-static const struct soc_enum wm_adsp_fw_enum[] = {
+const struct soc_enum wm_adsp_fw_enum[] = {
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
};
+EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
-const struct snd_kcontrol_new wm_adsp_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),
- SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
- wm_adsp_fw_get, wm_adsp_fw_put),
-};
-EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
-
-static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
- int type)
+static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
{
- int i;
+ return container_of(ext, struct wm_coeff_ctl, bytes_ext);
+}
- 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 *mem,
- unsigned int offset)
-{
- if (WARN_ON(!mem))
- return offset;
- switch (mem->type) {
- case WMFW_ADSP1_PM:
- return mem->base + (offset * 3);
- case WMFW_ADSP1_DM:
- return mem->base + (offset * 2);
- case WMFW_ADSP2_XM:
- return mem->base + (offset * 2);
- case WMFW_ADSP2_YM:
- return mem->base + (offset * 2);
- case WMFW_ADSP1_ZM:
- return mem->base + (offset * 2);
+static int wm_coeff_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+
+ switch (cs_ctl->type) {
+ case WMFW_CTL_TYPE_ACKED:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE;
+ uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE;
+ uinfo->value.integer.step = 1;
+ uinfo->count = 1;
+ break;
default:
- WARN(1, "Unknown memory region type");
- return offset;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = cs_ctl->len;
+ break;
}
+
+ return 0;
}
-static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
+static int wm_coeff_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
{
- u16 scratch[4];
- int ret;
-
- ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
- scratch, sizeof(scratch));
- if (ret) {
- adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
- return;
- }
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ char *p = ucontrol->value.bytes.data;
- adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
- be16_to_cpu(scratch[0]),
- be16_to_cpu(scratch[1]),
- be16_to_cpu(scratch[2]),
- be16_to_cpu(scratch[3]));
+ return cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
}
-static int wm_coeff_info(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_info *uinfo)
+static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
+ const unsigned int __user *bytes, unsigned int size)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ void *scratch __free(kvfree) = vmemdup_user(bytes, size);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = ctl->len;
- return 0;
+ if (IS_ERR(scratch))
+ return PTR_ERR(scratch);
+
+ return cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, scratch, size);
}
-static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
- const void *buf, size_t len)
+static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- const struct wm_adsp_region *mem;
- struct wm_adsp *dsp = ctl->dsp;
- void *scratch;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ unsigned int val = ucontrol->value.integer.value[0];
int ret;
- unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
+ if (val == 0)
+ return 0; /* 0 means no event */
- reg = ctl->alg_region.base + ctl->offset;
- reg = wm_adsp_region_to_reg(mem, reg);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
- scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
- if (!scratch)
- return -ENOMEM;
+ if (cs_ctl->enabled)
+ ret = cs_dsp_coeff_write_acked_control(cs_ctl, val);
+ else
+ ret = -EPERM;
- ret = regmap_raw_write(dsp->regmap, reg, scratch,
- ctl->len);
- if (ret) {
- adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
- ctl->len, reg, ret);
- kfree(scratch);
- return ret;
- }
- adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
- kfree(scratch);
+ if (ret < 0)
+ return ret;
- return 0;
+ return 1;
}
-static int wm_coeff_put(struct snd_kcontrol *kctl,
+static int wm_coeff_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
char *p = ucontrol->value.bytes.data;
+
+ return cs_dsp_coeff_lock_and_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
+}
+
+static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
+ unsigned int __user *bytes, unsigned int size)
+{
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
int ret = 0;
- mutex_lock(&ctl->dsp->pwr_lock);
+ mutex_lock(&cs_ctl->dsp->pwr_lock);
- memcpy(ctl->cache, p, ctl->len);
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size);
- ctl->set = 1;
- if (ctl->enabled)
- ret = wm_coeff_write_control(ctl, p, ctl->len);
+ if (!ret && copy_to_user(bytes, cs_ctl->cache, size))
+ ret = -EFAULT;
- mutex_unlock(&ctl->dsp->pwr_lock);
+ mutex_unlock(&cs_ctl->dsp->pwr_lock);
return ret;
}
-static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
- void *buf, size_t len)
+static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- const struct wm_adsp_region *mem;
- struct wm_adsp *dsp = ctl->dsp;
- void *scratch;
- int ret;
- unsigned int reg;
-
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
-
- reg = ctl->alg_region.base + ctl->offset;
- reg = wm_adsp_region_to_reg(mem, reg);
-
- scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
- if (!scratch)
- return -ENOMEM;
-
- ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
- if (ret) {
- adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
- ctl->len, reg, ret);
- kfree(scratch);
- return ret;
- }
- adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
-
- memcpy(buf, scratch, ctl->len);
- kfree(scratch);
+ /*
+ * Although it's not useful to read an acked control, we must satisfy
+ * user-side assumptions that all controls are readable and that a
+ * write of the same value should be filtered out (it's valid to send
+ * the same event number again to the firmware). We therefore return 0,
+ * meaning "no event" so valid event numbers will always be a change
+ */
+ ucontrol->value.integer.value[0] = 0;
return 0;
}
-static int wm_coeff_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ucontrol)
+static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
- char *p = ucontrol->value.bytes.data;
- int ret = 0;
+ unsigned int out, rd, wr, vol;
- mutex_lock(&ctl->dsp->pwr_lock);
+ if (len > ADSP_MAX_STD_CTRL_SIZE) {
+ rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
+ vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
- if (ctl->enabled)
- ret = wm_coeff_read_control(ctl, p, ctl->len);
- else
- ret = -EPERM;
+ out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
} else {
- if (!ctl->flags && ctl->enabled)
- ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
+ rd = SNDRV_CTL_ELEM_ACCESS_READ;
+ wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
+ vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
- memcpy(p, ctl->cache, ctl->len);
+ out = 0;
}
- mutex_unlock(&ctl->dsp->pwr_lock);
+ if (in) {
+ out |= rd;
+ if (in & WMFW_CTL_FLAG_WRITEABLE)
+ out |= wr;
+ if (in & WMFW_CTL_FLAG_VOLATILE)
+ out |= vol;
+ } else {
+ out |= rd | wr | vol;
+ }
- return ret;
+ return out;
}
-struct wmfw_ctl_work {
- struct wm_adsp *dsp;
- struct wm_coeff_ctl *ctl;
- struct work_struct work;
-};
-
-static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
+static void wm_adsp_ctl_work(struct work_struct *work)
{
+ struct wm_coeff_ctl *ctl = container_of(work,
+ struct wm_coeff_ctl,
+ work);
+ struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp,
+ struct wm_adsp,
+ cs_dsp);
struct snd_kcontrol_new *kcontrol;
- 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;
+ return;
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;
-
- if (ctl->flags) {
- if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
- if (ctl->flags & WMFW_CTL_FLAG_READABLE)
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
- }
-
- ret = snd_soc_add_card_controls(dsp->card,
- kcontrol, 1);
- if (ret < 0)
- goto err_kcontrol;
-
- kfree(kcontrol);
-
- ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
- ctl->name);
-
- return 0;
-
-err_kcontrol:
- kfree(kcontrol);
- return ret;
-}
-
-static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
-{
- struct wm_coeff_ctl *ctl;
- int ret;
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (!ctl->enabled || ctl->set)
- continue;
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
- continue;
-
- ret = wm_coeff_read_control(ctl,
- ctl->cache,
- ctl->len);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static int wm_coeff_sync_controls(struct wm_adsp *dsp)
-{
- struct wm_coeff_ctl *ctl;
- int ret;
-
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (!ctl->enabled)
- continue;
- if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
- ret = wm_coeff_write_control(ctl,
- ctl->cache,
- ctl->len);
- if (ret < 0)
- return ret;
+ kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
+ kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
+ kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
+
+ switch (cs_ctl->type) {
+ case WMFW_CTL_TYPE_ACKED:
+ kcontrol->get = wm_coeff_get_acked;
+ kcontrol->put = wm_coeff_put_acked;
+ break;
+ default:
+ if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ ctl->bytes_ext.max = cs_ctl->len;
+ ctl->bytes_ext.get = wm_coeff_tlv_get;
+ ctl->bytes_ext.put = wm_coeff_tlv_put;
+ } else {
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
}
+ break;
}
- return 0;
-}
+ snd_soc_add_component_controls(dsp->component, kcontrol, 1);
-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->dsp, ctl_work->ctl);
- kfree(ctl_work);
+ kfree(kcontrol);
}
-static int wm_adsp_create_control(struct wm_adsp *dsp,
- const struct wm_adsp_alg_region *alg_region,
- unsigned int offset, unsigned int len,
- const char *subname, unsigned int subname_len,
- unsigned int flags)
+int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
{
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
+ struct cs_dsp *cs_dsp = &dsp->cs_dsp;
struct wm_coeff_ctl *ctl;
- struct wmfw_ctl_work *ctl_work;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- char *region_name;
+ const char *region_name;
int ret;
- if (flags & WMFW_CTL_FLAG_SYS)
+ if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
return 0;
- switch (alg_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:
- adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
+ region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
+ if (!region_name) {
+ adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type);
return -EINVAL;
}
- switch (dsp->fw_ver) {
+ switch (cs_dsp->wmfw_ver) {
case 0:
case 1:
- snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
- dsp->num, region_name, alg_region->alg);
+ ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s %s %x", cs_dsp->name, region_name,
+ cs_ctl->alg_region.alg);
+ break;
+ case 2:
+ ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s%c %.12s %x", cs_dsp->name, *region_name,
+ wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
break;
default:
- ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
- "DSP%d%c %.12s %x", dsp->num, *region_name,
- wm_adsp_fw_text[dsp->fw], alg_region->alg);
+ ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s %.12s %x", cs_dsp->name,
+ wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
+ break;
+ }
- /* Truncate the subname from the start if it is too long */
- if (subname) {
- int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
- int skip = 0;
+ if (cs_ctl->subname) {
+ int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
+ int skip = 0;
- if (subname_len > avail)
- skip = subname_len - avail;
+ if (dsp->component->name_prefix)
+ avail -= strlen(dsp->component->name_prefix) + 1;
- snprintf(name + ret,
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
- subname_len - skip, subname + skip);
- }
- break;
- }
+ /* Truncate the subname from the start if it is too long */
+ if (cs_ctl->subname_len > avail)
+ skip = cs_ctl->subname_len - avail;
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (!strcmp(ctl->name, name)) {
- if (!ctl->enabled)
- ctl->enabled = 1;
- return 0;
- }
+ snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
+ " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
}
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
if (!ctl)
return -ENOMEM;
- ctl->fw_name = wm_adsp_fw_text[dsp->fw];
- ctl->alg_region = *alg_region;
+ ctl->cs_ctl = cs_ctl;
+
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->dsp = dsp;
-
- ctl->flags = flags;
- ctl->offset = offset;
- if (len > 512) {
- adsp_warn(dsp, "Truncating control %s from %d\n",
- ctl->name, len);
- len = 512;
- }
- ctl->len = len;
- ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
- if (!ctl->cache) {
- ret = -ENOMEM;
- goto err_ctl_name;
- }
- list_add(&ctl->list, &dsp->ctl_list);
+ cs_ctl->priv = ctl;
- ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
- if (!ctl_work) {
- ret = -ENOMEM;
- goto err_ctl_cache;
- }
-
- ctl_work->dsp = dsp;
- ctl_work->ctl = ctl;
- INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
- schedule_work(&ctl_work->work);
+ INIT_WORK(&ctl->work, wm_adsp_ctl_work);
+ schedule_work(&ctl->work);
return 0;
-err_ctl_cache:
- kfree(ctl->cache);
-err_ctl_name:
- kfree(ctl->name);
err_ctl:
kfree(ctl);
return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp_control_add);
-struct wm_coeff_parsed_alg {
- int id;
- const u8 *name;
- int name_len;
- int ncoeff;
-};
-
-struct wm_coeff_parsed_coeff {
- int offset;
- int mem_type;
- const u8 *name;
- int name_len;
- int ctl_type;
- int flags;
- int len;
-};
-
-static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
+static int wm_adsp_control_add_cb(struct cs_dsp_coeff_ctl *cs_ctl)
{
- int length;
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
- switch (bytes) {
- case 1:
- length = **pos;
- break;
- case 2:
- length = le16_to_cpu(*((__le16 *)*pos));
- break;
- default:
- return 0;
- }
-
- if (str)
- *str = *pos + bytes;
-
- *pos += ((length + bytes) + 3) & ~0x03;
-
- return length;
+ if (dsp->control_add)
+ return (dsp->control_add)(dsp, cs_ctl);
+ else
+ return wm_adsp_control_add(cs_ctl);
}
-static int wm_coeff_parse_int(int bytes, const u8 **pos)
+static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
{
- int val = 0;
+ struct wm_coeff_ctl *ctl = cs_ctl->priv;
- switch (bytes) {
- case 2:
- val = le16_to_cpu(*((__le16 *)*pos));
- break;
- case 4:
- val = le32_to_cpu(*((__le32 *)*pos));
- break;
- default:
- break;
- }
-
- *pos += bytes;
+ cancel_work_sync(&ctl->work);
- return val;
+ kfree(ctl->name);
+ kfree(ctl);
}
-static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
- struct wm_coeff_parsed_alg *blk)
+int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
{
- const struct wmfw_adsp_alg_data *raw;
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ int ret;
- switch (dsp->fw_ver) {
- case 0:
- case 1:
- raw = (const struct wmfw_adsp_alg_data *)*data;
- *data = raw->data;
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
+ cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
- blk->id = le32_to_cpu(raw->id);
- blk->name = raw->name;
- blk->name_len = strlen(raw->name);
- blk->ncoeff = le32_to_cpu(raw->ncoeff);
- break;
- default:
- blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
- blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
- &blk->name);
- wm_coeff_parse_string(sizeof(u16), data, NULL);
- blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
- break;
- }
+ if (ret < 0)
+ return ret;
- adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
- adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
- adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
+ return 0;
}
+EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
-static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
- struct wm_coeff_parsed_coeff *blk)
+int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
{
- const struct wmfw_adsp_coeff_data *raw;
- const u8 *tmp;
- int length;
+ int ret;
- switch (dsp->fw_ver) {
- case 0:
- case 1:
- raw = (const struct wmfw_adsp_coeff_data *)*data;
- *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
-
- blk->offset = le16_to_cpu(raw->hdr.offset);
- blk->mem_type = le16_to_cpu(raw->hdr.type);
- blk->name = raw->name;
- blk->name_len = strlen(raw->name);
- blk->ctl_type = le16_to_cpu(raw->ctl_type);
- blk->flags = le16_to_cpu(raw->flags);
- blk->len = le32_to_cpu(raw->len);
- break;
- default:
- tmp = *data;
- blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
- blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
- length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
- blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
- &blk->name);
- wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
- wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
- blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
- blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
- blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
-
- *data = *data + sizeof(raw->hdr) + length;
- break;
- }
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg),
+ 0, buf, len);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
- adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
- adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
- adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
- adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
- adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
- adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
+ return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
-static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
- const struct wmfw_region *region)
+static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,
+ const struct firmware *wmfw_firmware,
+ char *wmfw_filename,
+ const struct firmware *coeff_firmware,
+ char *coeff_filename)
{
- struct wm_adsp_alg_region alg_region = {};
- struct wm_coeff_parsed_alg alg_blk;
- struct wm_coeff_parsed_coeff coeff_blk;
- const u8 *data = region->data;
- int i, ret;
-
- wm_coeff_parse_alg(dsp, &data, &alg_blk);
- for (i = 0; i < alg_blk.ncoeff; i++) {
- wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
+ release_firmware(wmfw_firmware);
+ kfree(wmfw_filename);
- switch (coeff_blk.ctl_type) {
- case SNDRV_CTL_ELEM_TYPE_BYTES:
- break;
- default:
- adsp_err(dsp, "Unknown control type: %d\n",
- coeff_blk.ctl_type);
- return -EINVAL;
- }
-
- alg_region.type = coeff_blk.mem_type;
- alg_region.alg = alg_blk.id;
-
- ret = wm_adsp_create_control(dsp, &alg_region,
- coeff_blk.offset,
- coeff_blk.len,
- coeff_blk.name,
- coeff_blk.name_len,
- coeff_blk.flags);
- if (ret < 0)
- adsp_err(dsp, "Failed to create control: %.*s, %d\n",
- coeff_blk.name_len, coeff_blk.name, ret);
- }
-
- return 0;
+ release_firmware(coeff_firmware);
+ kfree(coeff_filename);
}
-static int wm_adsp_load(struct wm_adsp *dsp)
+static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
+ const struct firmware **firmware, char **filename,
+ const char *dir, const char *system_name,
+ const char *asoc_component_prefix,
+ const char *filetype)
{
- 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;
- }
-
- switch (header->ver) {
- case 0:
- adsp_warn(dsp, "%s: Depreciated file format %d\n",
- file, header->ver);
- break;
- case 1:
- case 2:
- break;
- default:
- adsp_err(dsp, "%s: unknown file format %d\n",
- file, header->ver);
- goto out_fw;
- }
-
- adsp_info(dsp, "Firmware version: %d\n", header->ver);
- dsp->fw_ver = 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_ALGORITHM_DATA:
- region_name = "Algorithm";
- ret = wm_adsp_parse_coeff(dsp, region);
- if (ret != 0)
- goto out_fw;
- 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);
- }
+ struct cs_dsp *cs_dsp = &dsp->cs_dsp;
+ const char *fwf;
+ char *s, c;
+ int ret = 0;
- 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;
- }
+ if (dsp->fwf_name)
+ fwf = dsp->fwf_name;
+ else
+ fwf = dsp->cs_dsp.name;
+
+ if (system_name && asoc_component_prefix)
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part,
+ fwf, wm_adsp_fw[dsp->fw].file, system_name,
+ asoc_component_prefix, filetype);
+ else if (system_name)
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part,
+ fwf, wm_adsp_fw[dsp->fw].file, system_name,
+ filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf,
+ wm_adsp_fw[dsp->fw].file, filetype);
- 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;
- }
- }
+ if (*filename == NULL)
+ return -ENOMEM;
- pos += le32_to_cpu(region->len) + sizeof(*region);
- regions++;
+ /*
+ * Make sure that filename is lower-case and any non alpha-numeric
+ * characters except full stop and forward slash are replaced with
+ * hyphens.
+ */
+ s = *filename;
+ while (*s) {
+ c = *s;
+ if (isalnum(c))
+ *s = tolower(c);
+ else if ((c != '.') && (c != '/'))
+ *s = '-';
+ s++;
}
- ret = regmap_async_complete(regmap);
+ ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev);
if (ret != 0) {
- adsp_err(dsp, "Failed to complete async write: %d\n", ret);
- goto out_fw;
+ adsp_dbg(dsp, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ } else {
+ adsp_dbg(dsp, "Found '%s'\n", *filename);
}
- if (pos > firmware->size)
- adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
- file, regions, pos - firmware->size);
-
- wm_adsp_debugfs_save_wmfwname(dsp, file);
-
-out_fw:
- regmap_async_complete(regmap);
- wm_adsp_buf_free(&buf_list);
- release_firmware(firmware);
-out:
- kfree(file);
-
return ret;
}
-static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
- const struct wm_adsp_alg_region *alg_region)
+static const char * const cirrus_dir = "cirrus/";
+static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
{
- struct wm_coeff_ctl *ctl;
+ const char *system_name = dsp->system_name;
+ const char *suffix = dsp->component->name_prefix;
+ int ret = 0;
+
+ if (dsp->fwf_suffix)
+ suffix = dsp->fwf_suffix;
- list_for_each_entry(ctl, &dsp->ctl_list, list) {
- if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
- alg_region->alg == ctl->alg_region.alg &&
- alg_region->type == ctl->alg_region.type) {
- ctl->alg_region.base = alg_region->base;
+ if (system_name && suffix) {
+ if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
+ cirrus_dir, system_name,
+ suffix, "wmfw")) {
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ suffix, "bin");
+ return 0;
}
}
-}
-static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
- unsigned int pos, unsigned int len)
-{
- void *alg;
- int ret;
- __be32 val;
+ if (system_name) {
+ if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
+ cirrus_dir, system_name,
+ NULL, "wmfw")) {
+ if (suffix)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ suffix, "bin");
- if (n_algs == 0) {
- adsp_err(dsp, "No algorithms\n");
- return ERR_PTR(-EINVAL);
+ if (!*coeff_firmware)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ NULL, "bin");
+ return 0;
+ }
}
- if (n_algs > 1024) {
- adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
- return ERR_PTR(-EINVAL);
- }
+ /* Check system-specific bin without wmfw before falling back to generic */
+ if (dsp->wmfw_optional && system_name) {
+ if (suffix)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ suffix, "bin");
- /* Read the terminator first to validate the length */
- ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list end: %d\n",
- ret);
- return ERR_PTR(ret);
- }
+ if (!*coeff_firmware)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ NULL, "bin");
- if (be32_to_cpu(val) != 0xbedead)
- adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
- pos + len, be32_to_cpu(val));
+ if (*coeff_firmware)
+ return 0;
+ }
- alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
- if (!alg)
- return ERR_PTR(-ENOMEM);
+ /* Check legacy location */
+ if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
+ "", NULL, NULL, "wmfw")) {
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ "", NULL, NULL, "bin");
+ return 0;
+ }
- ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list: %d\n",
- ret);
- kfree(alg);
- return ERR_PTR(ret);
+ /* Fall back to generic wmfw and optional matching bin */
+ ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
+ cirrus_dir, NULL, NULL, "wmfw");
+ if (!ret || dsp->wmfw_optional) {
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, NULL, NULL, "bin");
+ return 0;
}
- return alg;
+ adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
+ cirrus_dir, dsp->part,
+ dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name,
+ wm_adsp_fw[dsp->fw].file, system_name, suffix);
+
+ return -ENOENT;
}
-static struct wm_adsp_alg_region *
- wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id)
+static int wm_adsp_common_init(struct wm_adsp *dsp)
{
- struct wm_adsp_alg_region *alg_region;
+ INIT_LIST_HEAD(&dsp->compr_list);
+ INIT_LIST_HEAD(&dsp->buffer_list);
- list_for_each_entry(alg_region, &dsp->alg_regions, list) {
- if (id == alg_region->alg && type == alg_region->type)
- return alg_region;
- }
-
- return NULL;
+ return 0;
}
-static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
- int type, __be32 id,
- __be32 base)
+int wm_adsp1_init(struct wm_adsp *dsp)
{
- struct wm_adsp_alg_region *alg_region;
-
- alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
- if (!alg_region)
- return ERR_PTR(-ENOMEM);
-
- alg_region->type = type;
- alg_region->alg = be32_to_cpu(id);
- alg_region->base = be32_to_cpu(base);
+ int ret;
- list_add_tail(&alg_region->list, &dsp->alg_regions);
+ dsp->cs_dsp.client_ops = &wm_adsp1_client_ops;
- if (dsp->fw_ver > 0)
- wm_adsp_ctl_fixup_base(dsp, alg_region);
+ ret = cs_dsp_adsp1_init(&dsp->cs_dsp);
+ if (ret)
+ return ret;
- return alg_region;
+ return wm_adsp_common_init(dsp);
}
+EXPORT_SYMBOL_GPL(wm_adsp1_init);
-static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
{
- struct wmfw_adsp1_id_hdr adsp1_id;
- struct wmfw_adsp1_alg_hdr *adsp1_alg;
- struct wm_adsp_alg_region *alg_region;
- const struct wm_adsp_region *mem;
- unsigned int pos, len;
- size_t n_algs;
- int i, ret;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ int ret = 0;
+ char *wmfw_filename = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ char *coeff_filename = NULL;
+ const struct firmware *coeff_firmware = NULL;
- mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
- if (WARN_ON(!mem))
- return -EINVAL;
+ dsp->component = component;
- ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
- sizeof(adsp1_id));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
- }
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = wm_adsp_request_firmware_files(dsp,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret)
+ break;
- n_algs = be32_to_cpu(adsp1_id.n_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,
- n_algs);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
- adsp1_id.fw.id, adsp1_id.zm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
- adsp1_id.fw.id, adsp1_id.dm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- pos = sizeof(adsp1_id) / 2;
- len = (sizeof(*adsp1_alg) * n_algs) / 2;
-
- adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
- if (IS_ERR(adsp1_alg))
- return PTR_ERR(adsp1_alg);
-
- for (i = 0; i < n_algs; i++) {
- 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));
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
- adsp1_alg[i].alg.id,
- adsp1_alg[i].dm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp1_alg[i + 1].dm);
- len -= be32_to_cpu(adsp1_alg[i].dm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
- } else {
- adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
- be32_to_cpu(adsp1_alg[i].alg.id));
- }
- }
+ ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename,
+ wm_adsp_fw_text[dsp->fw]);
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
- adsp1_alg[i].alg.id,
- adsp1_alg[i].zm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp1_alg[i + 1].zm);
- len -= be32_to_cpu(adsp1_alg[i].zm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
- } else {
- adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
- be32_to_cpu(adsp1_alg[i].alg.id));
- }
- }
+ wm_adsp_release_firmware_files(dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ cs_dsp_adsp1_power_down(&dsp->cs_dsp);
+ break;
+ default:
+ break;
}
-out:
- kfree(adsp1_alg);
return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp1_event);
-static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
+int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)
{
- struct wmfw_adsp2_id_hdr adsp2_id;
- struct wmfw_adsp2_alg_hdr *adsp2_alg;
- struct wm_adsp_alg_region *alg_region;
- const struct wm_adsp_region *mem;
- unsigned int pos, len;
- size_t n_algs;
- int i, ret;
-
- mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
- if (WARN_ON(!mem))
- return -EINVAL;
-
- ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
- sizeof(adsp2_id));
- if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm info: %d\n",
- ret);
- return ret;
- }
-
- n_algs = be32_to_cpu(adsp2_id.n_algs);
- dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
- dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
- adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- dsp->fw_id,
- (dsp->fw_id_version & 0xff0000) >> 16,
- (dsp->fw_id_version & 0xff00) >> 8,
- dsp->fw_id_version & 0xff,
- n_algs);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
- adsp2_id.fw.id, adsp2_id.xm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
- adsp2_id.fw.id, adsp2_id.ym);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
- adsp2_id.fw.id, adsp2_id.zm);
- if (IS_ERR(alg_region))
- return PTR_ERR(alg_region);
-
- pos = sizeof(adsp2_id) / 2;
- len = (sizeof(*adsp2_alg) * n_algs) / 2;
-
- adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
- if (IS_ERR(adsp2_alg))
- return PTR_ERR(adsp2_alg);
-
- for (i = 0; i < n_algs; i++) {
- 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));
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
- adsp2_alg[i].alg.id,
- adsp2_alg[i].xm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp2_alg[i + 1].xm);
- len -= be32_to_cpu(adsp2_alg[i].xm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
- } else {
- adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
- be32_to_cpu(adsp2_alg[i].alg.id));
- }
- }
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
- adsp2_alg[i].alg.id,
- adsp2_alg[i].ym);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp2_alg[i + 1].ym);
- len -= be32_to_cpu(adsp2_alg[i].ym);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
- } else {
- adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
- be32_to_cpu(adsp2_alg[i].alg.id));
- }
- }
-
- alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
- adsp2_alg[i].alg.id,
- adsp2_alg[i].zm);
- if (IS_ERR(alg_region)) {
- ret = PTR_ERR(alg_region);
- goto out;
- }
- if (dsp->fw_ver == 0) {
- if (i + 1 < n_algs) {
- len = be32_to_cpu(adsp2_alg[i + 1].zm);
- len -= be32_to_cpu(adsp2_alg[i].zm);
- len *= 4;
- wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
- } else {
- adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
- be32_to_cpu(adsp2_alg[i].alg.id));
- }
- }
- }
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+ struct wm_adsp *dsp = &dsps[w->shift];
-out:
- kfree(adsp2_alg);
- return ret;
+ return cs_dsp_set_dspclk(&dsp->cs_dsp, freq);
}
+EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);
-static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- 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;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct wm_adsp *dsp = &dsps[mc->shift - 1];
- file = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (file == NULL)
- return -ENOMEM;
+ ucontrol->value.integer.value[0] = dsp->preloaded;
- 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;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
- if (sizeof(*hdr) >= firmware->size) {
- adsp_err(dsp, "%s: file too short, %zu bytes\n",
- file, firmware->size);
- goto out_fw;
- }
+int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct wm_adsp *dsp = &dsps[mc->shift - 1];
+ char preload[32];
- hdr = (void *)&firmware->data[0];
- if (memcmp(hdr->magic, "WMDR", 4) != 0) {
- adsp_err(dsp, "%s: invalid magic\n", file);
- goto out_fw;
- }
+ if (dsp->preloaded == ucontrol->value.integer.value[0])
+ return 0;
- 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;
- }
+ snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
- 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;
+ if (ucontrol->value.integer.value[0] || dsp->toggle_preload)
+ snd_soc_dapm_force_enable_pin(dapm, preload);
+ else
+ snd_soc_dapm_disable_pin(dapm, preload);
- 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;
- }
+ snd_soc_dapm_sync(dapm);
- alg_region = wm_adsp_find_alg_region(dsp, type,
- le32_to_cpu(blk->id));
- if (alg_region) {
- reg = alg_region->base;
- reg = wm_adsp_region_to_reg(mem, reg);
- reg += offset;
- } else {
- adsp_err(dsp, "No %x for algorithm %x\n",
- type, le32_to_cpu(blk->id));
- }
- break;
+ flush_work(&dsp->boot_work);
- default:
- adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
- file, blocks, type, pos);
- break;
- }
+ dsp->preloaded = ucontrol->value.integer.value[0];
- 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;
- }
+ if (dsp->toggle_preload) {
+ snd_soc_dapm_disable_pin(dapm, preload);
+ snd_soc_dapm_sync(dapm);
+ }
- 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);
- }
- }
+ return 1;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
- pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
- blocks++;
+int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware)
+{
+ int ret = 0;
+ char *wmfw_filename = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ char *coeff_filename = NULL;
+ const struct firmware *coeff_firmware = NULL;
+
+ if (load_firmware) {
+ ret = wm_adsp_request_firmware_files(dsp,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret)
+ return ret;
}
- ret = regmap_async_complete(regmap);
- if (ret != 0)
- adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+ if (dsp->bin_mandatory && !coeff_firmware) {
+ ret = -ENOENT;
+ goto err;
+ }
- if (pos > firmware->size)
- adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
- file, blocks, pos - firmware->size);
+ ret = cs_dsp_power_up(&dsp->cs_dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename,
+ wm_adsp_fw_text[dsp->fw]);
- wm_adsp_debugfs_save_binname(dsp, file);
+err:
+ wm_adsp_release_firmware_files(dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
-out_fw:
- regmap_async_complete(regmap);
- release_firmware(firmware);
- wm_adsp_buf_free(&buf_list);
-out:
- kfree(file);
return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp_power_up);
-int wm_adsp1_init(struct wm_adsp *dsp)
+void wm_adsp_power_down(struct wm_adsp *dsp)
{
- INIT_LIST_HEAD(&dsp->alg_regions);
+ cs_dsp_power_down(&dsp->cs_dsp);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_power_down);
- mutex_init(&dsp->pwr_lock);
+static void wm_adsp_boot_work(struct work_struct *work)
+{
+ struct wm_adsp *dsp = container_of(work,
+ struct wm_adsp,
+ boot_work);
- return 0;
+ wm_adsp_power_up(dsp, true);
}
-EXPORT_SYMBOL_GPL(wm_adsp1_init);
-int wm_adsp1_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event)
+int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct wm_adsp *dsp = &dsps[w->shift];
- struct wm_adsp_alg_region *alg_region;
- struct wm_coeff_ctl *ctl;
- int ret;
- unsigned int val;
-
- dsp->card = codec->component.card;
-
- mutex_lock(&dsp->pwr_lock);
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);
- goto err_mutex;
- }
-
- 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);
- goto err_mutex;
- }
- }
-
- ret = wm_adsp_load(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp1_setup_algs(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp_load_coeff(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Initialize caches for enabled and unset controls */
- ret = wm_coeff_init_control_caches(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Sync set controls */
- ret = wm_coeff_sync_controls(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Start the core running */
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_CORE_ENA | ADSP1_START,
- ADSP1_CORE_ENA | ADSP1_START);
+ case SND_SOC_DAPM_PRE_PMU:
+ queue_work(system_dfl_wq, &dsp->boot_work);
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);
- }
+ wm_adsp_power_down(dsp);
break;
-
default:
break;
}
- mutex_unlock(&dsp->pwr_lock);
-
return 0;
-
-err_ena:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
- ADSP1_SYS_ENA, 0);
-err_mutex:
- mutex_unlock(&dsp->pwr_lock);
-
- return ret;
}
-EXPORT_SYMBOL_GPL(wm_adsp1_event);
+EXPORT_SYMBOL_GPL(wm_adsp_early_event);
-static int wm_adsp2_ena(struct wm_adsp *dsp)
+static int wm_adsp_pre_run(struct cs_dsp *cs_dsp)
{
- unsigned int val;
- int ret, count;
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
- 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;
+ if (!dsp->pre_run)
+ return 0;
- msleep(1);
- }
+ return (*dsp->pre_run)(dsp);
+}
- if (!(val & ADSP2_RAM_RDY)) {
- adsp_err(dsp, "Failed to start DSP RAM\n");
- return -EBUSY;
- }
+static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp)
+{
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
- adsp_dbg(dsp, "RAM ready after %d polls\n", count);
+ if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ return wm_adsp_buffer_init(dsp);
return 0;
}
-static void wm_adsp2_boot_work(struct work_struct *work)
+static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)
{
- struct wm_adsp *dsp = container_of(work,
- struct wm_adsp,
- boot_work);
- int ret;
- unsigned int val;
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
- mutex_lock(&dsp->pwr_lock);
+ if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ wm_adsp_buffer_free(dsp);
- /*
- * 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);
- goto err_mutex;
- }
- 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);
- goto err_mutex;
- }
-
- ret = wm_adsp2_ena(dsp);
- if (ret != 0)
- goto err_mutex;
-
- ret = wm_adsp_load(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp2_setup_algs(dsp);
- if (ret != 0)
- goto err_ena;
-
- ret = wm_adsp_load_coeff(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Initialize caches for enabled and unset controls */
- ret = wm_coeff_init_control_caches(dsp);
- if (ret != 0)
- goto err_ena;
-
- /* Sync set controls */
- ret = wm_coeff_sync_controls(dsp);
- if (ret != 0)
- goto err_ena;
-
- dsp->running = true;
+ dsp->fatal_error = false;
+}
- mutex_unlock(&dsp->pwr_lock);
+int wm_adsp_run(struct wm_adsp *dsp)
+{
+ flush_work(&dsp->boot_work);
- return;
+ return cs_dsp_run(&dsp->cs_dsp);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_run);
-err_ena:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
-err_mutex:
- mutex_unlock(&dsp->pwr_lock);
+void wm_adsp_stop(struct wm_adsp *dsp)
+{
+ cs_dsp_stop(&dsp->cs_dsp);
}
+EXPORT_SYMBOL_GPL(wm_adsp_stop);
-int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+int wm_adsp_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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
struct wm_adsp *dsp = &dsps[w->shift];
- dsp->card = codec->component.card;
-
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- queue_work(system_unbound_wq, &dsp->boot_work);
- break;
+ case SND_SOC_DAPM_POST_PMU:
+ return wm_adsp_run(dsp);
+ case SND_SOC_DAPM_PRE_PMD:
+ wm_adsp_stop(dsp);
+ return 0;
default:
- break;
+ return 0;
}
-
- return 0;
}
-EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
+EXPORT_SYMBOL_GPL(wm_adsp_event);
-int wm_adsp2_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
{
- 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);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ char preload[32];
- 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;
+ if (!dsp->cs_dsp.no_core_startstop) {
+ snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
+ snd_soc_dapm_disable_pin(dapm, preload);
+ }
- if (wm_adsp_fw[dsp->fw].num_caps != 0)
- ret = wm_adsp_buffer_init(dsp);
+ cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
- break;
+ dsp->component = component;
- case SND_SOC_DAPM_PRE_PMD:
- /* Log firmware state, it can be useful for analysis */
- wm_adsp2_show_fw_status(dsp);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
- mutex_lock(&dsp->pwr_lock);
+int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
+{
+ cs_dsp_cleanup_debugfs(&dsp->cs_dsp);
- wm_adsp_debugfs_clear(dsp);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);
- dsp->fw_id = 0;
- dsp->fw_id_version = 0;
- dsp->running = false;
+int wm_adsp2_init(struct wm_adsp *dsp)
+{
+ int ret;
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA |
- ADSP2_START, 0);
+ INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
- /* 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);
+ dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr);
+ dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
- list_for_each_entry(ctl, &dsp->ctl_list, list)
- ctl->enabled = 0;
+ ret = cs_dsp_adsp2_init(&dsp->cs_dsp);
+ if (ret)
+ return ret;
- 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);
- }
+ return wm_adsp_common_init(dsp);
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_init);
- if (wm_adsp_fw[dsp->fw].num_caps != 0)
- wm_adsp_buffer_free(dsp);
+int wm_halo_init(struct wm_adsp *dsp)
+{
+ int ret;
- mutex_unlock(&dsp->pwr_lock);
+ INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
- adsp_dbg(dsp, "Shutdown complete\n");
- break;
+ dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr);
+ dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
- default:
- break;
- }
+ ret = cs_dsp_halo_init(&dsp->cs_dsp);
+ if (ret)
+ return ret;
- return 0;
-err:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
- return ret;
+ return wm_adsp_common_init(dsp);
}
-EXPORT_SYMBOL_GPL(wm_adsp2_event);
+EXPORT_SYMBOL_GPL(wm_halo_init);
-int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
+void wm_adsp2_remove(struct wm_adsp *dsp)
{
- wm_adsp2_init_debugfs(dsp, codec);
-
- return snd_soc_add_codec_controls(codec,
- &wm_adsp_fw_controls[dsp->num - 1],
- 1);
+ cs_dsp_remove(&dsp->cs_dsp);
}
-EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe);
+EXPORT_SYMBOL_GPL(wm_adsp2_remove);
-int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec)
+static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
{
- wm_adsp2_cleanup_debugfs(dsp);
-
- return 0;
+ return compr->buf != NULL;
}
-EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove);
-int wm_adsp2_init(struct wm_adsp *dsp)
+static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
{
- int ret;
+ struct wm_adsp_compr_buf *buf = NULL, *tmp;
- /*
- * Disable the DSP memory by default when in reset for a small
- * power saving.
- */
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
- if (ret != 0) {
- adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
- return ret;
+ if (compr->dsp->fatal_error)
+ return -EINVAL;
+
+ list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
+ if (!tmp->name || !strcmp(compr->name, tmp->name)) {
+ buf = tmp;
+ break;
+ }
}
- INIT_LIST_HEAD(&dsp->alg_regions);
- INIT_LIST_HEAD(&dsp->ctl_list);
- INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
+ if (!buf)
+ return -EINVAL;
- mutex_init(&dsp->pwr_lock);
+ compr->buf = buf;
+ buf->compr = compr;
return 0;
}
-EXPORT_SYMBOL_GPL(wm_adsp2_init);
+
+static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
+{
+ if (!compr)
+ return;
+
+ /* Wake the poll so it can see buffer is no longer attached */
+ if (compr->stream)
+ snd_compr_fragment_elapsed(compr->stream);
+
+ if (wm_adsp_compr_attached(compr)) {
+ compr->buf->compr = NULL;
+ compr->buf = NULL;
+ }
+}
int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
{
- struct wm_adsp_compr *compr;
+ struct wm_adsp_compr *compr, *tmp;
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
int ret = 0;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
if (wm_adsp_fw[dsp->fw].num_caps == 0) {
- adsp_err(dsp, "Firmware does not support compressed API\n");
+ adsp_err(dsp, "%s: Firmware does not support compressed API\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
ret = -ENXIO;
goto out;
}
if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
- adsp_err(dsp, "Firmware does not support stream direction\n");
+ adsp_err(dsp, "%s: Firmware does not support stream direction\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
ret = -EINVAL;
goto out;
}
- if (dsp->compr) {
- /* It is expect this limitation will be removed in future */
- adsp_err(dsp, "Only a single stream supported per DSP\n");
- ret = -EBUSY;
- goto out;
+ list_for_each_entry(tmp, &dsp->compr_list, list) {
+ if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) {
+ adsp_err(dsp, "%s: Only a single stream supported per dai\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ ret = -EBUSY;
+ goto out;
+ }
}
compr = kzalloc(sizeof(*compr), GFP_KERNEL);
@@ -2365,31 +1267,34 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
compr->dsp = dsp;
compr->stream = stream;
+ compr->name = snd_soc_rtd_to_codec(rtd, 0)->name;
- dsp->compr = compr;
+ list_add_tail(&compr->list, &dsp->compr_list);
stream->runtime->private_data = compr;
out:
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
-int wm_adsp_compr_free(struct snd_compr_stream *stream)
+int wm_adsp_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
struct wm_adsp *dsp = compr->dsp;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
- dsp->compr = NULL;
+ wm_adsp_compr_detach(compr);
+ list_del(&compr->list);
kfree(compr->raw_buf);
kfree(compr);
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return 0;
}
@@ -2408,10 +1313,10 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
- params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) {
- adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n",
- params->buffer.fragment_size,
- params->buffer.fragments);
+ params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) {
+ compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",
+ params->buffer.fragment_size,
+ params->buffer.fragments);
return -EINVAL;
}
@@ -2439,18 +1344,19 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
return 0;
}
- adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
- params->codec.id, params->codec.ch_in, params->codec.ch_out,
- params->codec.sample_rate, params->codec.format);
+ compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
+ params->codec.id, params->codec.ch_in, params->codec.ch_out,
+ params->codec.sample_rate, params->codec.format);
return -EINVAL;
}
static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
{
- return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
+ return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE;
}
-int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
+int wm_adsp_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
struct snd_compr_params *params)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
@@ -2463,19 +1369,22 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
compr->size = params->buffer;
- adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
- compr->size.fragment_size, compr->size.fragments);
+ compr_dbg(compr, "fragment_size=%d fragments=%d\n",
+ compr->size.fragment_size, compr->size.fragments);
size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
if (!compr->raw_buf)
return -ENOMEM;
+ compr->sample_rate = params->codec.sample_rate;
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
-int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
+int wm_adsp_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
struct snd_compr_caps *caps)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
@@ -2499,90 +1408,119 @@ int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
-static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
- unsigned int mem_addr,
- unsigned int num_words, u32 *data)
+static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
+ unsigned int field_offset, u32 *data)
{
- struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
- unsigned int i, reg;
- int ret;
+ return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
+ buf->host_buf_ptr + field_offset, data);
+}
- if (!mem)
- return -EINVAL;
+static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
+ unsigned int field_offset, u32 data)
+{
+ return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
+ buf->host_buf_ptr + field_offset,
+ data);
+}
- reg = wm_adsp_region_to_reg(mem, mem_addr);
+static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
+{
+ const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
+ struct wm_adsp_buffer_region *region;
+ u32 offset = 0;
+ int i, ret;
- ret = regmap_raw_read(dsp->regmap, reg, data,
- sizeof(*data) * num_words);
- if (ret < 0)
- return ret;
+ buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions),
+ GFP_KERNEL);
+ if (!buf->regions)
+ return -ENOMEM;
- for (i = 0; i < num_words; ++i)
- data[i] = be32_to_cpu(data[i]) & 0x00ffffffu;
+ for (i = 0; i < caps->num_regions; ++i) {
+ region = &buf->regions[i];
- return 0;
-}
+ region->offset = offset;
+ region->mem_type = caps->region_defs[i].mem_type;
-static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
- unsigned int mem_addr, u32 *data)
-{
- return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data);
-}
+ ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
+ &region->base_addr);
+ if (ret < 0)
+ goto err;
-static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
- unsigned int mem_addr, u32 data)
-{
- struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
- unsigned int reg;
+ ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
+ &offset);
+ if (ret < 0)
+ goto err;
- if (!mem)
- return -EINVAL;
+ region->cumulative_size = offset;
- reg = wm_adsp_region_to_reg(mem, mem_addr);
+ compr_dbg(buf,
+ "region=%d type=%d base=%08x off=%08x size=%08x\n",
+ i, region->mem_type, region->base_addr,
+ region->offset, region->cumulative_size);
+ }
- data = cpu_to_be32(data & 0x00ffffffu);
+ return 0;
- return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data));
+err:
+ kfree(buf->regions);
+ return ret;
}
-static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
- unsigned int field_offset, u32 *data)
+static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)
{
- return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM,
- buf->host_buf_ptr + field_offset, data);
+ buf->irq_count = 0xFFFFFFFF;
+ buf->read_index = -1;
+ buf->avail = 0;
}
-static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
- unsigned int field_offset, u32 data)
+static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
{
- return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM,
- buf->host_buf_ptr + field_offset, data);
+ struct wm_adsp_compr_buf *buf;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->dsp = dsp;
+
+ wm_adsp_buffer_clear(buf);
+
+ return buf;
}
-static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
+static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
{
- struct wm_adsp_alg_region *alg_region;
- struct wm_adsp *dsp = buf->dsp;
+ struct cs_dsp_alg_region *alg_region;
+ struct wm_adsp_compr_buf *buf;
u32 xmalg, addr, magic;
int i, ret;
- alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
- xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
+ alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id);
+ if (!alg_region) {
+ adsp_err(dsp, "No algorithm region found\n");
+ return -EINVAL;
+ }
+
+ xmalg = dsp->sys_config_size / sizeof(__be32);
addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
- ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
+ ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic);
if (ret < 0)
return ret;
if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
- return -EINVAL;
+ return -ENODEV;
+
+ buf = wm_adsp_buffer_alloc(dsp);
+ if (!buf)
+ return -ENOMEM;
addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
for (i = 0; i < 5; ++i) {
- ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
- &buf->host_buf_ptr);
+ ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr,
+ &buf->host_buf_ptr);
if (ret < 0)
- return ret;
+ goto err;
if (buf->host_buf_ptr)
break;
@@ -2590,162 +1528,219 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
usleep_range(1000, 2000);
}
- if (!buf->host_buf_ptr)
- return -EIO;
+ if (!buf->host_buf_ptr) {
+ ret = -EIO;
+ goto err;
+ }
- adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
+ buf->host_buf_mem_type = WMFW_ADSP2_XM;
+
+ ret = wm_adsp_buffer_populate(buf);
+ if (ret < 0)
+ goto err;
+
+ list_add_tail(&buf->list, &dsp->buffer_list);
+
+ compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr);
return 0;
-}
-static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
-{
- const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
- struct wm_adsp_buffer_region *region;
- u32 offset = 0;
- int i, ret;
+err:
+ kfree(buf);
- for (i = 0; i < caps->num_regions; ++i) {
- region = &buf->regions[i];
+ return ret;
+}
- region->offset = offset;
- region->mem_type = caps->region_defs[i].mem_type;
+static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
+{
+ struct wm_adsp_host_buf_coeff_v1 coeff_v1;
+ struct wm_adsp_compr_buf *buf;
+ struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
+ unsigned int version = 0;
+ int ret, i;
- ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
- &region->base_addr);
+ for (i = 0; i < 5; ++i) {
+ ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1,
+ min((size_t)cs_ctl->len, sizeof(coeff_v1)));
if (ret < 0)
return ret;
- ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
- &offset);
- if (ret < 0)
- return ret;
+ if (coeff_v1.host_buf_ptr)
+ break;
- region->cumulative_size = offset;
+ usleep_range(1000, 2000);
+ }
- adsp_dbg(buf->dsp,
- "region=%d type=%d base=%04x off=%04x size=%04x\n",
- i, region->mem_type, region->base_addr,
- region->offset, region->cumulative_size);
+ if (!coeff_v1.host_buf_ptr) {
+ adsp_err(dsp, "Failed to acquire host buffer\n");
+ return -EIO;
}
- return 0;
+ buf = wm_adsp_buffer_alloc(dsp);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->host_buf_mem_type = cs_ctl->alg_region.type;
+ buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr);
+
+ ret = wm_adsp_buffer_populate(buf);
+ if (ret < 0)
+ goto err;
+
+ /*
+ * v0 host_buffer coefficients didn't have versioning, so if the
+ * control is one word, assume version 0.
+ */
+ if (cs_ctl->len == 4)
+ goto done;
+
+ version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK;
+ version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;
+
+ if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
+ adsp_err(dsp,
+ "Host buffer coeff ver %u > supported version %u\n",
+ version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));
+
+ buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part,
+ (char *)&coeff_v1.name);
+
+done:
+ list_add_tail(&buf->list, &dsp->buffer_list);
+
+ compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",
+ buf->host_buf_ptr, version);
+
+ return version;
+
+err:
+ kfree(buf);
+
+ return ret;
}
static int wm_adsp_buffer_init(struct wm_adsp *dsp)
{
- struct wm_adsp_compr_buf *buf;
+ struct cs_dsp_coeff_ctl *cs_ctl;
int ret;
- buf = kzalloc(sizeof(*buf), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- buf->dsp = dsp;
- buf->read_index = -1;
- buf->irq_count = 0xFFFFFFFF;
+ list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) {
+ if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
+ continue;
- ret = wm_adsp_buffer_locate(buf);
- if (ret < 0) {
- adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret);
- goto err_buffer;
- }
+ if (!cs_ctl->enabled)
+ continue;
- buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions,
- sizeof(*buf->regions), GFP_KERNEL);
- if (!buf->regions) {
- ret = -ENOMEM;
- goto err_buffer;
+ ret = wm_adsp_buffer_parse_coeff(cs_ctl);
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
+ goto error;
+ } else if (ret == 0) {
+ /* Only one buffer supported for version 0 */
+ return 0;
+ }
}
- ret = wm_adsp_buffer_populate(buf);
- if (ret < 0) {
- adsp_err(dsp, "Failed to populate host buffer: %d\n", ret);
- goto err_regions;
+ if (list_empty(&dsp->buffer_list)) {
+ /* Fall back to legacy support */
+ ret = wm_adsp_buffer_parse_legacy(dsp);
+ if (ret == -ENODEV)
+ adsp_info(dsp, "Legacy support not available\n");
+ else if (ret)
+ adsp_warn(dsp, "Failed to parse legacy: %d\n", ret);
}
- dsp->buffer = buf;
-
return 0;
-err_regions:
- kfree(buf->regions);
-err_buffer:
- kfree(buf);
+error:
+ wm_adsp_buffer_free(dsp);
return ret;
}
static int wm_adsp_buffer_free(struct wm_adsp *dsp)
{
- if (dsp->buffer) {
- kfree(dsp->buffer->regions);
- kfree(dsp->buffer);
+ struct wm_adsp_compr_buf *buf, *tmp;
- dsp->buffer = NULL;
+ list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
+ wm_adsp_compr_detach(buf->compr);
+
+ kfree(buf->name);
+ kfree(buf->regions);
+ list_del(&buf->list);
+ kfree(buf);
}
return 0;
}
-static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
-{
- return compr->buf != NULL;
-}
-
-static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
+static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
{
- /*
- * Note this will be more complex once each DSP can support multiple
- * streams
- */
- if (!compr->dsp->buffer)
- return -EINVAL;
+ int ret;
- compr->buf = compr->dsp->buffer;
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
+ if (ret < 0) {
+ compr_err(buf, "Failed to check buffer error: %d\n", ret);
+ return ret;
+ }
+ if (buf->error != 0) {
+ compr_err(buf, "Buffer error occurred: %d\n", buf->error);
+ return -EIO;
+ }
return 0;
}
-int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
+int wm_adsp_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, int cmd)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
struct wm_adsp *dsp = compr->dsp;
int ret = 0;
- adsp_dbg(dsp, "Trigger: %d\n", cmd);
+ compr_dbg(compr, "Trigger: %d\n", cmd);
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- if (wm_adsp_compr_attached(compr))
- break;
+ if (!wm_adsp_compr_attached(compr)) {
+ ret = wm_adsp_compr_attach(compr);
+ if (ret < 0) {
+ compr_err(compr, "Failed to link buffer and stream: %d\n",
+ ret);
+ break;
+ }
+ }
- ret = wm_adsp_compr_attach(compr);
- if (ret < 0) {
- adsp_err(dsp, "Failed to link buffer and stream: %d\n",
- ret);
+ ret = wm_adsp_buffer_get_error(compr->buf);
+ if (ret < 0)
break;
- }
/* Trigger the IRQ at one fragment of data */
ret = wm_adsp_buffer_write(compr->buf,
HOST_BUFFER_FIELD(high_water_mark),
wm_adsp_compr_frag_words(compr));
if (ret < 0) {
- adsp_err(dsp, "Failed to set high water mark: %d\n",
- ret);
+ compr_err(compr, "Failed to set high water mark: %d\n",
+ ret);
break;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
+ if (wm_adsp_compr_attached(compr))
+ wm_adsp_buffer_clear(compr->buf);
break;
default:
ret = -EINVAL;
break;
}
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -2775,7 +1770,7 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
read_index = sign_extend32(next_read_index, 23);
if (read_index < 0) {
- adsp_dbg(buf->dsp, "Avail check on unstarted stream\n");
+ compr_dbg(buf, "Avail check on unstarted stream\n");
return 0;
}
@@ -2793,8 +1788,8 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
if (avail < 0)
avail += wm_adsp_buffer_size(buf);
- adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
- buf->read_index, write_index, avail);
+ compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
+ buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE);
buf->avail = avail;
@@ -2803,49 +1798,49 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
{
- struct wm_adsp_compr_buf *buf = dsp->buffer;
- struct wm_adsp_compr *compr = dsp->compr;
+ struct wm_adsp_compr_buf *buf;
+ struct wm_adsp_compr *compr;
int ret = 0;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
- if (!buf) {
- adsp_err(dsp, "Spurious buffer IRQ\n");
+ if (list_empty(&dsp->buffer_list)) {
ret = -ENODEV;
goto out;
}
adsp_dbg(dsp, "Handling buffer IRQ\n");
- ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
- if (ret < 0) {
- adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
- goto out;
- }
- if (buf->error != 0) {
- adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
- ret = -EIO;
- goto out;
- }
+ list_for_each_entry(buf, &dsp->buffer_list, list) {
+ compr = buf->compr;
- ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
- &buf->irq_count);
- if (ret < 0) {
- adsp_err(dsp, "Failed to get irq_count: %d\n", ret);
- goto out;
- }
+ ret = wm_adsp_buffer_get_error(buf);
+ if (ret < 0)
+ goto out_notify; /* Wake poll to report error */
- ret = wm_adsp_buffer_update_avail(buf);
- if (ret < 0) {
- adsp_err(dsp, "Error reading avail: %d\n", ret);
- goto out;
- }
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
+ &buf->irq_count);
+ if (ret < 0) {
+ compr_err(buf, "Failed to get irq_count: %d\n", ret);
+ goto out;
+ }
- if (compr->stream)
- snd_compr_fragment_elapsed(compr->stream);
+ ret = wm_adsp_buffer_update_avail(buf);
+ if (ret < 0) {
+ compr_err(buf, "Error reading avail: %d\n", ret);
+ goto out;
+ }
+
+ if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
+ ret = WM_ADSP_COMPR_VOICE_TRIGGER;
+
+out_notify:
+ if (compr && compr->stream)
+ snd_compr_fragment_elapsed(compr->stream);
+ }
out:
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -2856,8 +1851,7 @@ static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
if (buf->irq_count & 0x01)
return 0;
- adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n",
- buf->irq_count);
+ compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count);
buf->irq_count |= 0x01;
@@ -2865,24 +1859,23 @@ static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
buf->irq_count);
}
-int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
- struct snd_compr_tstamp *tstamp)
+int wm_adsp_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_tstamp64 *tstamp)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
- struct wm_adsp_compr_buf *buf = compr->buf;
struct wm_adsp *dsp = compr->dsp;
+ struct wm_adsp_compr_buf *buf;
int ret = 0;
- adsp_dbg(dsp, "Pointer request\n");
+ compr_dbg(compr, "Pointer request\n");
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
- if (!compr->buf) {
- ret = -ENXIO;
- goto out;
- }
+ buf = compr->buf;
- if (compr->buf->error) {
+ if (dsp->fatal_error || !buf || buf->error) {
+ snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
ret = -EIO;
goto out;
}
@@ -2890,7 +1883,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
ret = wm_adsp_buffer_update_avail(buf);
if (ret < 0) {
- adsp_err(dsp, "Error reading avail: %d\n", ret);
+ compr_err(compr, "Error reading avail: %d\n", ret);
goto out;
}
@@ -2899,21 +1892,29 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
* DSP to inform us once a whole fragment is available.
*/
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
+ ret = wm_adsp_buffer_get_error(buf);
+ if (ret < 0) {
+ if (buf->error)
+ snd_compr_stop_error(stream,
+ SNDRV_PCM_STATE_XRUN);
+ goto out;
+ }
+
ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) {
- adsp_err(dsp,
- "Failed to re-enable buffer IRQ: %d\n",
- ret);
+ compr_err(compr, "Failed to re-enable buffer IRQ: %d\n",
+ ret);
goto out;
}
}
}
tstamp->copied_total = compr->copied_total;
- tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
+ tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE;
+ tstamp->sampling_rate = compr->sample_rate;
out:
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
@@ -2922,11 +1923,9 @@ EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
{
struct wm_adsp_compr_buf *buf = compr->buf;
- u8 *pack_in = (u8 *)compr->raw_buf;
- u8 *pack_out = (u8 *)compr->raw_buf;
unsigned int adsp_addr;
int mem_type, nwords, max_read;
- int i, j, ret;
+ int i, ret;
/* Calculate read parameters */
for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
@@ -2953,18 +1952,12 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
return 0;
/* Read data from DSP */
- ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
- nwords, compr->raw_buf);
+ ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr,
+ nwords, (__be32 *)compr->raw_buf);
if (ret < 0)
return ret;
- /* Remove the padding bytes from the data read from the DSP */
- for (i = 0; i < nwords; i++) {
- for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++)
- *pack_out++ = *pack_in++;
-
- pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE;
- }
+ cs_dsp_remove_padding(compr->raw_buf, nwords);
/* update read index to account for words read */
buf->read_index += nwords;
@@ -2989,30 +1982,30 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
int ntotal = 0;
int nwords, nbytes;
- adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
-
- if (!compr->buf)
- return -ENXIO;
+ compr_dbg(compr, "Requested read of %zu bytes\n", count);
- if (compr->buf->error)
+ if (dsp->fatal_error || !compr->buf || compr->buf->error) {
+ snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
return -EIO;
+ }
- count /= WM_ADSP_DATA_WORD_SIZE;
+ count /= CS_DSP_DATA_WORD_SIZE;
do {
nwords = wm_adsp_buffer_capture_block(compr, count);
if (nwords < 0) {
- adsp_err(dsp, "Failed to capture block: %d\n", nwords);
+ compr_err(compr, "Failed to capture block: %d\n",
+ nwords);
return nwords;
}
- nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
+ nbytes = nwords * CS_DSP_DATA_WORD_SIZE;
- adsp_dbg(dsp, "Read %d bytes\n", nbytes);
+ compr_dbg(compr, "Read %d bytes\n", nbytes);
if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
- adsp_err(dsp, "Failed to copy data to user: %d, %d\n",
- ntotal, nbytes);
+ compr_err(compr, "Failed to copy data to user: %d, %d\n",
+ ntotal, nbytes);
return -EFAULT;
}
@@ -3025,24 +2018,84 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
return ntotal;
}
-int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
+int wm_adsp_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, char __user *buf,
size_t count)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
struct wm_adsp *dsp = compr->dsp;
int ret;
- mutex_lock(&dsp->pwr_lock);
+ mutex_lock(&dsp->cs_dsp.pwr_lock);
if (stream->direction == SND_COMPRESS_CAPTURE)
ret = wm_adsp_compr_read(compr, buf, count);
else
ret = -ENOTSUPP;
- mutex_unlock(&dsp->pwr_lock);
+ mutex_unlock(&dsp->cs_dsp.pwr_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
+static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp)
+{
+ struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
+ struct wm_adsp_compr *compr;
+
+ dsp->fatal_error = true;
+
+ list_for_each_entry(compr, &dsp->compr_list, list) {
+ if (compr->stream)
+ snd_compr_fragment_elapsed(compr->stream);
+ }
+}
+
+irqreturn_t wm_adsp2_bus_error(int irq, void *data)
+{
+ struct wm_adsp *dsp = (struct wm_adsp *)data;
+
+ cs_dsp_adsp2_bus_error(&dsp->cs_dsp);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
+
+irqreturn_t wm_halo_bus_error(int irq, void *data)
+{
+ struct wm_adsp *dsp = (struct wm_adsp *)data;
+
+ cs_dsp_halo_bus_error(&dsp->cs_dsp);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_halo_bus_error);
+
+irqreturn_t wm_halo_wdt_expire(int irq, void *data)
+{
+ struct wm_adsp *dsp = data;
+
+ cs_dsp_halo_wdt_expire(&dsp->cs_dsp);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
+
+static const struct cs_dsp_client_ops wm_adsp1_client_ops = {
+ .control_add = wm_adsp_control_add_cb,
+ .control_remove = wm_adsp_control_remove,
+};
+
+static const struct cs_dsp_client_ops wm_adsp2_client_ops = {
+ .control_add = wm_adsp_control_add_cb,
+ .control_remove = wm_adsp_control_remove,
+ .pre_run = wm_adsp_pre_run,
+ .post_run = wm_adsp_event_post_run,
+ .post_stop = wm_adsp_event_post_stop,
+ .watchdog_expired = wm_adsp_fatal_error,
+};
+
+MODULE_DESCRIPTION("Cirrus Logic ASoC DSP Support");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 1a928ec54741..8035fda71f8d 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -1,121 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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 <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/compress_driver.h>
-#include "wmfw.h"
-
-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;
-};
+/* Return values for wm_adsp_compr_handle_irq */
+#define WM_ADSP_COMPR_OK 0
+#define WM_ADSP_COMPR_VOICE_TRIGGER 1
struct wm_adsp_compr;
struct wm_adsp_compr_buf;
struct wm_adsp {
+ struct cs_dsp cs_dsp;
const char *part;
- 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;
-
- unsigned int fw_id;
- unsigned int fw_id_version;
+ const char *fwf_name;
+ const char *system_name;
+ const char *fwf_suffix;
+ struct snd_soc_component *component;
- const struct wm_adsp_region *mem;
- int num_mems;
+ unsigned int sys_config_size;
int fw;
- int fw_ver;
- bool running;
-
- struct list_head ctl_list;
+ bool wmfw_optional;
+ bool bin_mandatory;
struct work_struct boot_work;
-
- struct wm_adsp_compr *compr;
- struct wm_adsp_compr_buf *buffer;
-
- struct mutex pwr_lock;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_root;
- char *wmfw_file_name;
- char *bin_file_name;
-#endif
-
+ int (*control_add)(struct wm_adsp *dsp, struct cs_dsp_coeff_ctl *cs_ctl);
+ int (*pre_run)(struct wm_adsp *dsp);
+
+ bool preloaded;
+ bool fatal_error;
+
+ struct list_head compr_list;
+ struct list_head buffer_list;
+
+ /*
+ * Flag indicating the preloader widget only needs power toggled
+ * on state change rather than held on for the duration of the
+ * preload, useful for devices that can retain firmware memory
+ * across power down.
+ */
+ bool toggle_preload;
};
#define WM_ADSP1(wname, num) \
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
-#define WM_ADSP2_E(wname, num, event_fn) \
-{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
+#define WM_ADSP2_PRELOAD_SWITCH(wname, num) \
+ SOC_SINGLE_EXT(wname " Preload Switch", SND_SOC_NOPM, num, 1, 0, \
+ wm_adsp2_preloader_get, wm_adsp2_preloader_put)
+
+#define WM_ADSP2(wname, num, event_fn) \
+ SND_SOC_DAPM_SPK(wname " Preload", NULL), \
+{ .id = snd_soc_dapm_supply, .name = wname " Preloader", \
.reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
- .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \
+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \
+ .subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \
{ .id = snd_soc_dapm_out_drv, .name = wname, \
- .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
+ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
-#define WM_ADSP2(wname, num) \
- WM_ADSP2_E(wname, num, wm_adsp2_early_event)
+#define WM_ADSP_FW_CONTROL(dspname, num) \
+ SOC_ENUM_EXT(dspname " Firmware", wm_adsp_fw_enum[num], \
+ wm_adsp_fw_get, wm_adsp_fw_put)
-extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
+extern const struct soc_enum wm_adsp_fw_enum[];
int wm_adsp1_init(struct wm_adsp *dsp);
int wm_adsp2_init(struct wm_adsp *dsp);
-int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec);
-int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
+void wm_adsp2_remove(struct wm_adsp *dsp);
+int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component);
+int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component);
+int wm_halo_init(struct wm_adsp *dsp);
+
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-int wm_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);
-extern int wm_adsp_compr_open(struct wm_adsp *dsp,
- struct snd_compr_stream *stream);
-extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
-extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
- struct snd_compr_params *params);
-extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
- struct snd_compr_caps *caps);
-extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
-extern int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
-extern int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
- struct snd_compr_tstamp *tstamp);
-extern int wm_adsp_compr_copy(struct snd_compr_stream *stream,
- char __user *buf, size_t count);
+int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware);
+void wm_adsp_power_down(struct wm_adsp *dsp);
+
+irqreturn_t wm_adsp2_bus_error(int irq, void *data);
+irqreturn_t wm_halo_bus_error(int irq, void *data);
+irqreturn_t wm_halo_wdt_expire(int irq, void *data);
+
+int wm_adsp_run(struct wm_adsp *dsp);
+void wm_adsp_stop(struct wm_adsp *dsp);
+int wm_adsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq);
+
+int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);
+int wm_adsp_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *stream);
+int wm_adsp_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_params *params);
+int wm_adsp_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps);
+int wm_adsp_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, int cmd);
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
+int wm_adsp_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_tstamp64 *tstamp);
+int wm_adsp_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ char __user *buf, size_t count);
+
+int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl);
+int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
+int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
#endif
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 624b3b9cb079..f80dfc488e3c 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
@@ -18,6 +14,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/mfd/wm8994/registers.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -60,9 +57,9 @@ static const char *speaker_mode_text[] = {
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)
+static void wait_for_dc_servo(struct snd_soc_component *component, unsigned int op)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
unsigned int reg;
int count = 0;
int timeout;
@@ -71,9 +68,9 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
/* Trigger the command */
- snd_soc_write(codec, WM8993_DC_SERVO_0, val);
+ snd_soc_component_write(component, WM8993_DC_SERVO_0, val);
- dev_dbg(codec->dev, "Waiting for DC servo...\n");
+ dev_dbg(component->dev, "Waiting for DC servo...\n");
if (hubs->dcs_done_irq)
timeout = 4;
@@ -89,12 +86,12 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
else
msleep(1);
- reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
- dev_dbg(codec->dev, "DC servo: %x\n", reg);
+ reg = snd_soc_component_read(component, WM8993_DC_SERVO_0);
+ dev_dbg(component->dev, "DC servo: %x\n", reg);
} while (reg & op && count < timeout);
if (reg & op)
- dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
+ dev_err(component->dev, "Timed out waiting for DC Servo %x\n",
op);
}
@@ -108,35 +105,35 @@ irqreturn_t wm_hubs_dcs_done(int irq, void *data)
}
EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
-static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
+static bool wm_hubs_dac_hp_direct(struct snd_soc_component *component)
{
int reg;
/* If we're going via the mixer we'll need to do additional checks */
- reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1);
+ reg = snd_soc_component_read(component, WM8993_OUTPUT_MIXER1);
if (!(reg & WM8993_DACL_TO_HPOUT1L)) {
if (reg & ~WM8993_DACL_TO_MIXOUTL) {
- dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+ dev_vdbg(component->dev, "Analogue paths connected: %x\n",
reg & ~WM8993_DACL_TO_HPOUT1L);
return false;
} else {
- dev_vdbg(codec->dev, "HPL connected to mixer\n");
+ dev_vdbg(component->dev, "HPL connected to mixer\n");
}
} else {
- dev_vdbg(codec->dev, "HPL connected to DAC\n");
+ dev_vdbg(component->dev, "HPL connected to DAC\n");
}
- reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2);
+ reg = snd_soc_component_read(component, WM8993_OUTPUT_MIXER2);
if (!(reg & WM8993_DACR_TO_HPOUT1R)) {
if (reg & ~WM8993_DACR_TO_MIXOUTR) {
- dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+ dev_vdbg(component->dev, "Analogue paths connected: %x\n",
reg & ~WM8993_DACR_TO_HPOUT1R);
return false;
} else {
- dev_vdbg(codec->dev, "HPR connected to mixer\n");
+ dev_vdbg(component->dev, "HPR connected to mixer\n");
}
} else {
- dev_vdbg(codec->dev, "HPR connected to DAC\n");
+ dev_vdbg(component->dev, "HPR connected to DAC\n");
}
return true;
@@ -149,17 +146,17 @@ struct wm_hubs_dcs_cache {
u16 dcs_cfg;
};
-static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
+static bool wm_hubs_dcs_cache_get(struct snd_soc_component *component,
struct wm_hubs_dcs_cache **entry)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
struct wm_hubs_dcs_cache *cache;
unsigned int left, right;
- left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+ left = snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME);
left &= WM8993_HPOUT1L_VOL_MASK;
- right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+ right = snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME);
right &= WM8993_HPOUT1R_VOL_MASK;
list_for_each_entry(cache, &hubs->dcs_cache, list) {
@@ -173,22 +170,22 @@ static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
return false;
}
-static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
+static void wm_hubs_dcs_cache_set(struct snd_soc_component *component, u16 dcs_cfg)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
struct wm_hubs_dcs_cache *cache;
if (hubs->no_cache_dac_hp_direct)
return;
- cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
+ cache = devm_kzalloc(component->dev, sizeof(*cache), GFP_KERNEL);
if (!cache)
return;
- cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+ cache->left = snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME);
cache->left &= WM8993_HPOUT1L_VOL_MASK;
- cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+ cache->right = snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME);
cache->right &= WM8993_HPOUT1R_VOL_MASK;
cache->dcs_cfg = dcs_cfg;
@@ -196,10 +193,10 @@ static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
list_add_tail(&cache->list, &hubs->dcs_cache);
}
-static int wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
+static int wm_hubs_read_dc_servo(struct snd_soc_component *component,
u16 *reg_l, u16 *reg_r)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
u16 dcs_reg, reg;
int ret = 0;
@@ -220,14 +217,14 @@ static int wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
*/
switch (hubs->dcs_readback_mode) {
case 0:
- *reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+ *reg_l = snd_soc_component_read(component, WM8993_DC_SERVO_READBACK_1)
& WM8993_DCS_INTEG_CHAN_0_MASK;
- *reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+ *reg_r = snd_soc_component_read(component, WM8993_DC_SERVO_READBACK_2)
& WM8993_DCS_INTEG_CHAN_1_MASK;
break;
case 2:
case 1:
- reg = snd_soc_read(codec, dcs_reg);
+ reg = snd_soc_component_read(component, dcs_reg);
*reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
*reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
@@ -242,9 +239,9 @@ static int wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
/*
* Startup calibration of the DC servo
*/
-static void enable_dc_servo(struct snd_soc_codec *codec)
+static void enable_dc_servo(struct snd_soc_component *component)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
struct wm_hubs_dcs_cache *cache;
s8 offset;
u16 reg_l, reg_r, dcs_cfg, dcs_reg;
@@ -260,12 +257,12 @@ static void enable_dc_servo(struct snd_soc_codec *codec)
/* 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",
+ if (wm_hubs_dac_hp_direct(component) &&
+ wm_hubs_dcs_cache_get(component, &cache)) {
+ dev_dbg(component->dev, "Using cached DCS offset %x for %d,%d\n",
cache->dcs_cfg, cache->left, cache->right);
- snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
- wait_for_dc_servo(codec,
+ snd_soc_component_write(component, dcs_reg, cache->dcs_cfg);
+ wait_for_dc_servo(component,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);
return;
@@ -273,48 +270,48 @@ static void enable_dc_servo(struct snd_soc_codec *codec)
if (hubs->series_startup) {
/* Set for 32 series updates */
- snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ snd_soc_component_update_bits(component, WM8993_DC_SERVO_1,
WM8993_DCS_SERIES_NO_01_MASK,
32 << WM8993_DCS_SERIES_NO_01_SHIFT);
- wait_for_dc_servo(codec,
+ wait_for_dc_servo(component,
WM8993_DCS_TRIG_SERIES_0 |
WM8993_DCS_TRIG_SERIES_1);
} else {
- wait_for_dc_servo(codec,
+ wait_for_dc_servo(component,
WM8993_DCS_TRIG_STARTUP_0 |
WM8993_DCS_TRIG_STARTUP_1);
}
- if (wm_hubs_read_dc_servo(codec, &reg_l, &reg_r) < 0)
+ if (wm_hubs_read_dc_servo(component, &reg_l, &reg_r) < 0)
return;
- dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
+ dev_dbg(component->dev, "DCS input: %x %x\n", reg_l, reg_r);
/* Apply correction to DC servo result */
if (hubs->dcs_codes_l || hubs->dcs_codes_r) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Applying %d/%d code DC servo correction\n",
hubs->dcs_codes_l, hubs->dcs_codes_r);
/* HPOUT1R */
offset = (s8)reg_r;
- dev_dbg(codec->dev, "DCS right %d->%d\n", offset,
+ dev_dbg(component->dev, "DCS right %d->%d\n", offset,
offset + hubs->dcs_codes_r);
offset += hubs->dcs_codes_r;
dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
/* HPOUT1L */
offset = (s8)reg_l;
- dev_dbg(codec->dev, "DCS left %d->%d\n", offset,
+ dev_dbg(component->dev, "DCS left %d->%d\n", offset,
offset + hubs->dcs_codes_l);
offset += hubs->dcs_codes_l;
dcs_cfg |= (u8)offset;
- dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg);
+ dev_dbg(component->dev, "DCS result: %x\n", dcs_cfg);
/* Do it */
- snd_soc_write(codec, dcs_reg, dcs_cfg);
- wait_for_dc_servo(codec,
+ snd_soc_component_write(component, dcs_reg, dcs_cfg);
+ wait_for_dc_servo(component,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);
} else {
@@ -324,8 +321,8 @@ static void enable_dc_servo(struct snd_soc_codec *codec)
/* 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);
+ if (wm_hubs_dac_hp_direct(component))
+ wm_hubs_dcs_cache_set(component, dcs_cfg);
}
/*
@@ -334,8 +331,8 @@ static void enable_dc_servo(struct snd_soc_codec *codec)
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);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
int ret;
ret = snd_soc_put_volsw(kcontrol, ucontrol);
@@ -346,9 +343,9 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
return ret;
/* Only need to do this if the outputs are active */
- if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
+ if (snd_soc_component_read(component, WM8993_POWER_MANAGEMENT_1)
& (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
WM8993_DC_SERVO_0,
WM8993_DCS_TRIG_SINGLE_0 |
WM8993_DCS_TRIG_SINGLE_1,
@@ -499,8 +496,8 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -509,28 +506,28 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
break;
case 1:
/* Enable the headphone amp */
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA |
WM8993_HPOUT1R_ENA,
WM8993_HPOUT1L_ENA |
WM8993_HPOUT1R_ENA);
/* Enable the second stage */
- snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY);
break;
default:
- dev_err(codec->dev, "Unknown HP startup mode %d\n",
+ dev_err(component->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,
+ snd_soc_component_update_bits(component, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, 0);
break;
}
@@ -541,47 +538,47 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int reg = snd_soc_component_read(component, WM8993_ANALOGUE_HP_0);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ snd_soc_component_update_bits(component, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, WM8993_CP_ENA);
msleep(5);
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
- snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+ snd_soc_component_write(component, WM8993_ANALOGUE_HP_0, reg);
- snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ snd_soc_component_update_bits(component, WM8993_DC_SERVO_1,
WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
- enable_dc_servo(codec);
+ enable_dc_servo(component);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
- snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+ snd_soc_component_write(component, WM8993_ANALOGUE_HP_0, reg);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_OUTP |
WM8993_HPOUT1R_OUTP |
WM8993_HPOUT1L_RMV_SHORT |
WM8993_HPOUT1R_RMV_SHORT, 0);
- snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY, 0);
- snd_soc_write(codec, WM8993_DC_SERVO_0, 0);
+ snd_soc_component_write(component, WM8993_DC_SERVO_0, 0);
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0);
break;
@@ -593,18 +590,18 @@ static int hp_event(struct snd_soc_dapm_widget *w,
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;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 reg = snd_soc_component_read(component, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
reg |= WM8993_HPOUT2_IN_ENA;
- snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+ snd_soc_component_write(component, WM8993_ANTIPOP1, reg);
udelay(50);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+ snd_soc_component_write(component, WM8993_ANTIPOP1, reg);
break;
default:
@@ -618,8 +615,8 @@ static int earpiece_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
bool *flag;
switch (w->shift) {
@@ -648,8 +645,8 @@ static int lineout_event(struct snd_soc_dapm_widget *w,
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);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
switch (w->shift) {
case WM8993_MICB1_ENA_SHIFT:
@@ -667,26 +664,26 @@ static int micbias_event(struct snd_soc_dapm_widget *w,
return 0;
}
-void wm_hubs_update_class_w(struct snd_soc_codec *codec)
+void wm_hubs_update_class_w(struct snd_soc_component *component)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
- if (!wm_hubs_dac_hp_direct(codec))
+ if (!wm_hubs_dac_hp_direct(component))
enable = false;
- if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec))
+ if (hubs->check_class_w_digital && !hubs->check_class_w_digital(component))
enable = false;
- dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled");
+ dev_vdbg(component->dev, "Class W %s\n", str_enabled_disabled(enable));
- snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+ snd_soc_component_update_bits(component, 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));
+ snd_soc_component_write(component, WM8993_LEFT_OUTPUT_VOLUME,
+ snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME));
+ snd_soc_component_write(component, WM8993_RIGHT_OUTPUT_VOLUME,
+ snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME));
}
EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
@@ -697,12 +694,12 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
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);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
- wm_hubs_update_class_w(codec);
+ wm_hubs_update_class_w(component);
return ret;
}
@@ -717,12 +714,12 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
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);
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
int ret;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
- wm_hubs_update_class_w(codec);
+ wm_hubs_update_class_w(component);
return ret;
}
@@ -1113,40 +1110,40 @@ static const struct snd_soc_dapm_route lineout2_se_routes[] = {
{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
};
-int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
+int wm_hubs_add_analogue_controls(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
/* Latch volume update bits & default ZC on */
- snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
WM8993_IN2_VU, WM8993_IN2_VU);
- snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+ snd_soc_component_update_bits(component, WM8993_SPEAKER_VOLUME_LEFT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
- snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
+ snd_soc_component_update_bits(component, WM8993_SPEAKER_VOLUME_RIGHT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
- snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, 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,
+ snd_soc_component_update_bits(component, WM8993_RIGHT_OPGA_VOLUME,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
- snd_soc_add_codec_controls(codec, analogue_snd_controls,
+ snd_soc_add_component_controls(component, analogue_snd_controls,
ARRAY_SIZE(analogue_snd_controls));
snd_soc_dapm_new_controls(dapm, analogue_dapm_widgets,
@@ -1155,13 +1152,13 @@ int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
-int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
+int wm_hubs_add_analogue_routes(struct snd_soc_component *component,
int lineout1_diff, int lineout2_diff)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- hubs->codec = codec;
+ hubs->component = component;
INIT_LIST_HEAD(&hubs->dcs_cache);
init_completion(&hubs->dcs_done);
@@ -1191,14 +1188,14 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
-int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+int wm_hubs_handle_analogue_pdata(struct snd_soc_component *component,
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
int jd_scthr, int jd_thr,
int micbias1_delay, int micbias2_delay,
int micbias1_lvl, int micbias2_lvl)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
hubs->lineout1_se = !lineout1_diff;
hubs->lineout2_se = !lineout2_diff;
@@ -1206,28 +1203,31 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
hubs->micb2_delay = micbias2_delay;
if (!lineout1_diff)
- snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+ snd_soc_component_update_bits(component, WM8993_LINE_MIXER1,
WM8993_LINEOUT1_MODE,
WM8993_LINEOUT1_MODE);
if (!lineout2_diff)
- snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+ snd_soc_component_update_bits(component, WM8993_LINE_MIXER2,
WM8993_LINEOUT2_MODE,
WM8993_LINEOUT2_MODE);
if (!lineout1_diff && !lineout2_diff)
- snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ snd_soc_component_update_bits(component, WM8993_ANTIPOP1,
WM8993_LINEOUT_VMID_BUF_ENA,
WM8993_LINEOUT_VMID_BUF_ENA);
if (lineout1fb)
- snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ snd_soc_component_update_bits(component, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
if (lineout2fb)
- snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ snd_soc_component_update_bits(component, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
- snd_soc_update_bits(codec, WM8993_MICBIAS,
+ if (!hubs->micd_scthr)
+ return 0;
+
+ snd_soc_component_update_bits(component, WM8993_MICBIAS,
WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
WM8993_MICB1_LVL | WM8993_MICB2_LVL,
jd_scthr << WM8993_JD_SCTHR_SHIFT |
@@ -1239,9 +1239,9 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
-void wm_hubs_vmid_ena(struct snd_soc_codec *codec)
+void wm_hubs_vmid_ena(struct snd_soc_component *component)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
int val = 0;
if (hubs->lineout1_se)
@@ -1251,25 +1251,25 @@ void wm_hubs_vmid_ena(struct snd_soc_codec *codec)
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);
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_3, val, val);
}
EXPORT_SYMBOL_GPL(wm_hubs_vmid_ena);
-void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
+void wm_hubs_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component);
int mask, val;
switch (level) {
case SND_SOC_BIAS_STANDBY:
/* Clamp the inputs to VMID while we ramp to charge caps */
- snd_soc_update_bits(codec, WM8993_INPUTS_CLAMP_REG,
+ snd_soc_component_update_bits(component, WM8993_INPUTS_CLAMP_REG,
WM8993_INPUTS_CLAMP, WM8993_INPUTS_CLAMP);
break;
case SND_SOC_BIAS_ON:
- /* Turn off any unneded single ended outputs */
+ /* Turn off any unneeded single ended outputs */
val = 0;
mask = 0;
@@ -1291,11 +1291,11 @@ void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
if (hubs->lineout2_se && hubs->lineout2p_ena)
val |= WM8993_LINEOUT2P_ENA;
- snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_3,
+ snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_3,
mask, val);
/* Remove the input clamps */
- snd_soc_update_bits(codec, WM8993_INPUTS_CLAMP_REG,
+ snd_soc_component_update_bits(component, WM8993_INPUTS_CLAMP_REG,
WM8993_INPUTS_CLAMP, 0);
break;
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 24c763df21f9..a4ed9bd31426 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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
@@ -19,7 +15,7 @@
#include <linux/list.h>
#include <sound/control.h>
-struct snd_soc_codec;
+struct snd_soc_component;
extern const unsigned int wm_hubs_spkmix_tlv[];
@@ -31,10 +27,11 @@ struct wm_hubs_data {
int hp_startup_mode;
int series_startup;
int no_series_update;
+ bool micd_scthr;
bool no_cache_dac_hp_direct;
struct list_head dcs_cache;
- bool (*check_class_w_digital)(struct snd_soc_codec *);
+ bool (*check_class_w_digital)(struct snd_soc_component *);
int micb1_delay;
int micb2_delay;
@@ -50,23 +47,23 @@ struct wm_hubs_data {
bool dcs_done_irq;
struct completion dcs_done;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
};
-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 *,
+extern int wm_hubs_add_analogue_controls(struct snd_soc_component *);
+extern int wm_hubs_add_analogue_routes(struct snd_soc_component *, int, int);
+extern int wm_hubs_handle_analogue_pdata(struct snd_soc_component *,
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
int jd_scthr, int jd_thr,
- int micbias1_dly, int micbias2_dly,
+ int micbias1_delay, int micbias2_delay,
int micbias1_lvl, int micbias2_lvl);
extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
-extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec);
-extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
+extern void wm_hubs_vmid_ena(struct snd_soc_component *component);
+extern void wm_hubs_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level);
-extern void wm_hubs_update_class_w(struct snd_soc_codec *codec);
+extern void wm_hubs_update_class_w(struct snd_soc_component *component);
extern const struct snd_kcontrol_new wm_hubs_hpl_mux;
extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
deleted file mode 100644
index 7613d60d62ea..000000000000
--- a/sound/soc/codecs/wmfw.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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>
-
-#define WMFW_MAX_ALG_NAME 256
-#define WMFW_MAX_ALG_DESCR_NAME 256
-
-#define WMFW_MAX_COEFF_NAME 256
-#define WMFW_MAX_COEFF_DESCR_NAME 256
-
-#define WMFW_CTL_FLAG_SYS 0x8000
-#define WMFW_CTL_FLAG_VOLATILE 0x0004
-#define WMFW_CTL_FLAG_WRITEABLE 0x0002
-#define WMFW_CTL_FLAG_READABLE 0x0001
-
-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 n_algs;
-} __packed;
-
-struct wmfw_adsp2_id_hdr {
- struct wmfw_id_hdr fw;
- __be32 zm;
- __be32 xm;
- __be32 ym;
- __be32 n_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_adsp_alg_data {
- __le32 id;
- u8 name[WMFW_MAX_ALG_NAME];
- u8 descr[WMFW_MAX_ALG_DESCR_NAME];
- __le32 ncoeff;
- u8 data[];
-} __packed;
-
-struct wmfw_adsp_coeff_data {
- struct {
- __le16 offset;
- __le16 type;
- __le32 size;
- } hdr;
- u8 name[WMFW_MAX_COEFF_NAME];
- u8 descr[WMFW_MAX_COEFF_DESCR_NAME];
- __le16 ctl_type;
- __le16 flags;
- __le32 len;
- u8 data[];
-} __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_ALGORITHM_DATA 0xf2
-#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
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
new file mode 100644
index 000000000000..d7aca6567c2d
--- /dev/null
+++ b/sound/soc/codecs/wsa881x.c
@@ -0,0 +1,1229 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2015-2017, The Linux Foundation.
+// Copyright (c) 2019, Linaro Limited
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define WSA881X_DIGITAL_BASE 0x3000
+#define WSA881X_ANALOG_BASE 0x3100
+
+/* Digital register address space */
+#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE + 0x0000)
+#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE + 0x0001)
+#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE + 0x0002)
+#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE + 0x0003)
+#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE + 0x0004)
+#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE + 0x0005)
+#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0006)
+#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0007)
+#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0008)
+#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE + 0x0009)
+#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE + 0x000A)
+#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE + 0x000B)
+#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE + 0x000C)
+#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE + 0x000F)
+#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE + 0x0010)
+#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE + 0x0011)
+#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE + 0x0012)
+#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE + 0x0013)
+#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE + 0x0014)
+#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE + 0x0015)
+#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE + 0x0016)
+#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE + 0x0017)
+#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE + 0x0018)
+#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE + 0x0019)
+#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE + 0x001A)
+#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE + 0x001B)
+#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE + 0x001C)
+#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE + 0x001D)
+#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE + 0x001E)
+#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE + 0x001F)
+#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE + 0x0020)
+#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE + 0x0021)
+#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE + 0x0022)
+#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE + 0x0023)
+#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE + 0x0024)
+#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE + 0x0025)
+#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE + 0x0026)
+#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0030)
+#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0031)
+#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE + 0x0032)
+#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE + 0x0033)
+#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE + 0x0034)
+#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE + 0x0035)
+#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE + 0x0037)
+#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE + 0x0038)
+#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE + 0x0039)
+#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE + 0x003B)
+#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE + 0x003C)
+#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE + 0x003D)
+#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE + 0x003E)
+#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE + 0x003F)
+#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE + 0x0044)
+#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE + 0x0045)
+#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE + 0x0050)
+#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE + 0x0051)
+#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE + 0x0052)
+#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE + 0x0080)
+#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE + 0x0081)
+#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE + 0x0082)
+#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE + 0x0083)
+#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE + 0x0084)
+#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE + 0x0085)
+#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE + 0x0086)
+#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE + 0x0087)
+#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE + 0x0088)
+#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE + 0x0089)
+#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE + 0x008A)
+#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE + 0x008B)
+#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE + 0x008C)
+#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE + 0x008D)
+#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE + 0x008E)
+#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE + 0x008F)
+#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE + 0x0090)
+#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE + 0x0091)
+#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE + 0x0092)
+#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE + 0x0093)
+#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE + 0x0094)
+#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE + 0x0095)
+#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE + 0x0096)
+#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE + 0x0097)
+#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE + 0x0098)
+#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE + 0x0099)
+#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE + 0x009A)
+#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE + 0x009B)
+#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE + 0x009C)
+#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE + 0x009D)
+#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE + 0x009E)
+#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE + 0x009F)
+#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE + 0x00BF)
+
+/* Analog Register address space */
+#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE + 0x0000)
+#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE + 0x0001)
+#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE + 0x0002)
+#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE + 0x0003)
+#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE + 0x0004)
+#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE + 0x0005)
+#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE + 0x0006)
+#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE + 0x0007)
+#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE + 0x0008)
+#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE + 0x0009)
+#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE + 0x000A)
+#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE + 0x000B)
+#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE + 0x0010)
+#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE + 0x0011)
+#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE + 0x0012)
+#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE + 0x0013)
+#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE + 0x0014)
+#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE + 0x0015)
+#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE + 0x001A)
+#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE + 0x001B)
+#define WSA881X_PA_GAIN_SEL_MASK BIT(3)
+#define WSA881X_PA_GAIN_SEL_REG BIT(3)
+#define WSA881X_PA_GAIN_SEL_DRE 0
+#define WSA881X_SPKR_PAG_GAIN_MASK GENMASK(7, 4)
+#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE + 0x001C)
+#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE + 0x001D)
+#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE + 0x001E)
+#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE + 0x001F)
+#define WSA881X_SPKR_OCP_MASK GENMASK(7, 6)
+#define WSA881X_SPKR_OCP_EN BIT(7)
+#define WSA881X_SPKR_OCP_HOLD BIT(6)
+#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE + 0x0020)
+#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE + 0x0021)
+#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE + 0x0022)
+#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE + 0x0023)
+#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE + 0x0024)
+#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE + 0x0025)
+#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE + 0x0026)
+#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE + 0x0027)
+#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE + 0x0028)
+#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE + 0x0029)
+#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE + 0x002A)
+#define WSA881X_BOOST_EN_MASK BIT(7)
+#define WSA881X_BOOST_EN BIT(7)
+#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE + 0x002B)
+#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE + 0x002C)
+#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE + 0x002D)
+#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE + 0x002E)
+#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE + 0x002F)
+#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE + 0x0030)
+#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE + 0x0031)
+#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE + 0x0032)
+#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE + 0x0033)
+#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE + 0x0034)
+#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE + 0x0035)
+#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE + 0x0036)
+#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE + 0x0037)
+#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE + 0x0038)
+#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE + 0x0039)
+#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE + 0x003A)
+#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE + 0x003B)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x003C)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE + 0x003D)
+#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE + 0x003E)
+#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE + 0x003F)
+#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE + 0x0040)
+#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x0041)
+#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE + 0x0042)
+#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE + 0x0043)
+#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE + 0x0044)
+#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE + 0x0045)
+
+#define SWRS_SCP_FRAME_CTRL_BANK(m) (0x60 + 0x10 * (m))
+#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
+#define SWR_SLV_MAX_REG_ADDR 0x390
+#define SWR_SLV_START_REG_ADDR 0x40
+#define SWR_SLV_MAX_BUF_LEN 20
+#define BYTES_PER_LINE 12
+#define SWR_SLV_RD_BUF_LEN 8
+#define SWR_SLV_WR_BUF_LEN 32
+#define SWR_SLV_MAX_DEVICES 2
+#define WSA881X_MAX_SWR_PORTS 4
+#define WSA881X_VERSION_ENTRY_SIZE 27
+#define WSA881X_OCP_CTL_TIMER_SEC 2
+#define WSA881X_OCP_CTL_TEMP_CELSIUS 25
+#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60
+#define WSA881X_PROBE_TIMEOUT 1000
+
+#define WSA881X_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wsa881x_put_pa_gain, tlv_array)
+
+static const struct reg_default wsa881x_defaults[] = {
+ { WSA881X_CHIP_ID0, 0x00 },
+ { WSA881X_CHIP_ID1, 0x00 },
+ { WSA881X_CHIP_ID2, 0x00 },
+ { WSA881X_CHIP_ID3, 0x02 },
+ { WSA881X_BUS_ID, 0x00 },
+ { WSA881X_CDC_RST_CTL, 0x00 },
+ { WSA881X_CDC_TOP_CLK_CTL, 0x03 },
+ { WSA881X_CDC_ANA_CLK_CTL, 0x00 },
+ { WSA881X_CDC_DIG_CLK_CTL, 0x00 },
+ { WSA881X_CLOCK_CONFIG, 0x00 },
+ { WSA881X_ANA_CTL, 0x08 },
+ { WSA881X_SWR_RESET_EN, 0x00 },
+ { WSA881X_TEMP_DETECT_CTL, 0x01 },
+ { WSA881X_TEMP_MSB, 0x00 },
+ { WSA881X_TEMP_LSB, 0x00 },
+ { WSA881X_TEMP_CONFIG0, 0x00 },
+ { WSA881X_TEMP_CONFIG1, 0x00 },
+ { WSA881X_CDC_CLIP_CTL, 0x03 },
+ { WSA881X_SDM_PDM9_LSB, 0x00 },
+ { WSA881X_SDM_PDM9_MSB, 0x00 },
+ { WSA881X_CDC_RX_CTL, 0x7E },
+ { WSA881X_DEM_BYPASS_DATA0, 0x00 },
+ { WSA881X_DEM_BYPASS_DATA1, 0x00 },
+ { WSA881X_DEM_BYPASS_DATA2, 0x00 },
+ { WSA881X_DEM_BYPASS_DATA3, 0x00 },
+ { WSA881X_OTP_CTRL0, 0x00 },
+ { WSA881X_OTP_CTRL1, 0x00 },
+ { WSA881X_HDRIVE_CTL_GROUP1, 0x00 },
+ { WSA881X_INTR_MODE, 0x00 },
+ { WSA881X_INTR_STATUS, 0x00 },
+ { WSA881X_INTR_CLEAR, 0x00 },
+ { WSA881X_INTR_LEVEL, 0x00 },
+ { WSA881X_INTR_SET, 0x00 },
+ { WSA881X_INTR_TEST, 0x00 },
+ { WSA881X_PDM_TEST_MODE, 0x00 },
+ { WSA881X_ATE_TEST_MODE, 0x00 },
+ { WSA881X_PIN_CTL_MODE, 0x00 },
+ { WSA881X_PIN_CTL_OE, 0x00 },
+ { WSA881X_PIN_WDATA_IOPAD, 0x00 },
+ { WSA881X_PIN_STATUS, 0x00 },
+ { WSA881X_DIG_DEBUG_MODE, 0x00 },
+ { WSA881X_DIG_DEBUG_SEL, 0x00 },
+ { WSA881X_DIG_DEBUG_EN, 0x00 },
+ { WSA881X_SWR_HM_TEST1, 0x08 },
+ { WSA881X_SWR_HM_TEST2, 0x00 },
+ { WSA881X_TEMP_DETECT_DBG_CTL, 0x00 },
+ { WSA881X_TEMP_DEBUG_MSB, 0x00 },
+ { WSA881X_TEMP_DEBUG_LSB, 0x00 },
+ { WSA881X_SAMPLE_EDGE_SEL, 0x0C },
+ { WSA881X_SPARE_0, 0x00 },
+ { WSA881X_SPARE_1, 0x00 },
+ { WSA881X_SPARE_2, 0x00 },
+ { WSA881X_OTP_REG_0, 0x01 },
+ { WSA881X_OTP_REG_1, 0xFF },
+ { WSA881X_OTP_REG_2, 0xC0 },
+ { WSA881X_OTP_REG_3, 0xFF },
+ { WSA881X_OTP_REG_4, 0xC0 },
+ { WSA881X_OTP_REG_5, 0xFF },
+ { WSA881X_OTP_REG_6, 0xFF },
+ { WSA881X_OTP_REG_7, 0xFF },
+ { WSA881X_OTP_REG_8, 0xFF },
+ { WSA881X_OTP_REG_9, 0xFF },
+ { WSA881X_OTP_REG_10, 0xFF },
+ { WSA881X_OTP_REG_11, 0xFF },
+ { WSA881X_OTP_REG_12, 0xFF },
+ { WSA881X_OTP_REG_13, 0xFF },
+ { WSA881X_OTP_REG_14, 0xFF },
+ { WSA881X_OTP_REG_15, 0xFF },
+ { WSA881X_OTP_REG_16, 0xFF },
+ { WSA881X_OTP_REG_17, 0xFF },
+ { WSA881X_OTP_REG_18, 0xFF },
+ { WSA881X_OTP_REG_19, 0xFF },
+ { WSA881X_OTP_REG_20, 0xFF },
+ { WSA881X_OTP_REG_21, 0xFF },
+ { WSA881X_OTP_REG_22, 0xFF },
+ { WSA881X_OTP_REG_23, 0xFF },
+ { WSA881X_OTP_REG_24, 0x03 },
+ { WSA881X_OTP_REG_25, 0x01 },
+ { WSA881X_OTP_REG_26, 0x03 },
+ { WSA881X_OTP_REG_27, 0x11 },
+ { WSA881X_OTP_REG_63, 0x40 },
+ /* WSA881x Analog registers */
+ { WSA881X_BIAS_REF_CTRL, 0x6C },
+ { WSA881X_BIAS_TEST, 0x16 },
+ { WSA881X_BIAS_BIAS, 0xF0 },
+ { WSA881X_TEMP_OP, 0x00 },
+ { WSA881X_TEMP_IREF_CTRL, 0x56 },
+ { WSA881X_TEMP_ISENS_CTRL, 0x47 },
+ { WSA881X_TEMP_CLK_CTRL, 0x87 },
+ { WSA881X_TEMP_TEST, 0x00 },
+ { WSA881X_TEMP_BIAS, 0x51 },
+ { WSA881X_TEMP_DOUT_MSB, 0x00 },
+ { WSA881X_TEMP_DOUT_LSB, 0x00 },
+ { WSA881X_ADC_EN_MODU_V, 0x00 },
+ { WSA881X_ADC_EN_MODU_I, 0x00 },
+ { WSA881X_ADC_EN_DET_TEST_V, 0x00 },
+ { WSA881X_ADC_EN_DET_TEST_I, 0x00 },
+ { WSA881X_ADC_EN_SEL_IBAIS, 0x10 },
+ { WSA881X_SPKR_DRV_EN, 0x74 },
+ { WSA881X_SPKR_DRV_DBG, 0x15 },
+ { WSA881X_SPKR_PWRSTG_DBG, 0x00 },
+ { WSA881X_SPKR_OCP_CTL, 0xD4 },
+ { WSA881X_SPKR_CLIP_CTL, 0x90 },
+ { WSA881X_SPKR_PA_INT, 0x54 },
+ { WSA881X_SPKR_BIAS_CAL, 0xAC },
+ { WSA881X_SPKR_STATUS1, 0x00 },
+ { WSA881X_SPKR_STATUS2, 0x00 },
+ { WSA881X_BOOST_EN_CTL, 0x18 },
+ { WSA881X_BOOST_CURRENT_LIMIT, 0x7A },
+ { WSA881X_BOOST_PRESET_OUT2, 0x70 },
+ { WSA881X_BOOST_FORCE_OUT, 0x0E },
+ { WSA881X_BOOST_LDO_PROG, 0x16 },
+ { WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71 },
+ { WSA881X_BOOST_RON_CTL, 0x0F },
+ { WSA881X_BOOST_ZX_CTL, 0x34 },
+ { WSA881X_BOOST_START_CTL, 0x23 },
+ { WSA881X_BOOST_MISC1_CTL, 0x80 },
+ { WSA881X_BOOST_MISC2_CTL, 0x00 },
+ { WSA881X_BOOST_MISC3_CTL, 0x00 },
+ { WSA881X_BOOST_ATEST_CTL, 0x00 },
+ { WSA881X_SPKR_PROT_FE_GAIN, 0x46 },
+ { WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B },
+ { WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D },
+ { WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D },
+ { WSA881X_SPKR_PROT_ATEST1, 0x01 },
+ { WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D },
+ { WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D },
+ { WSA881X_SPKR_PROT_SAR, 0x00 },
+ { WSA881X_SPKR_STATUS3, 0x00 },
+};
+
+static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = {
+ { WSA881X_SPKR_DRV_GAIN, 0x41, 0 },
+ { WSA881X_SPKR_MISC_CTL1, 0x87, 0 },
+};
+
+static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = {
+ { WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0 },
+ { WSA881X_SPKR_PROT_ATEST2, 0x0A, 0 },
+ { WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0 },
+};
+
+/* Default register reset values for WSA881x rev 2.0 */
+static const struct reg_sequence wsa881x_rev_2_0[] = {
+ { WSA881X_RESET_CTL, 0x00, 0x00 },
+ { WSA881X_TADC_VALUE_CTL, 0x01, 0x00 },
+ { WSA881X_INTR_MASK, 0x1B, 0x00 },
+ { WSA881X_IOPAD_CTL, 0x00, 0x00 },
+ { WSA881X_OTP_REG_28, 0x3F, 0x00 },
+ { WSA881X_OTP_REG_29, 0x3F, 0x00 },
+ { WSA881X_OTP_REG_30, 0x01, 0x00 },
+ { WSA881X_OTP_REG_31, 0x01, 0x00 },
+ { WSA881X_TEMP_ADC_CTRL, 0x03, 0x00 },
+ { WSA881X_ADC_SEL_IBIAS, 0x45, 0x00 },
+ { WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00 },
+ { WSA881X_SPKR_DAC_CTL, 0x42, 0x00 },
+ { WSA881X_SPKR_BBM_CTL, 0x02, 0x00 },
+ { WSA881X_SPKR_MISC_CTL1, 0x40, 0x00 },
+ { WSA881X_SPKR_MISC_CTL2, 0x07, 0x00 },
+ { WSA881X_SPKR_BIAS_INT, 0x5F, 0x00 },
+ { WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00 },
+ { WSA881X_BOOST_PS_CTL, 0xA0, 0x00 },
+ { WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00 },
+ { WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00 },
+ { WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00 },
+ { WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00 },
+ { WSA881X_BONGO_RESRV_REG2, 0x07, 0x00 },
+};
+
+enum wsa_port_ids {
+ WSA881X_PORT_DAC,
+ WSA881X_PORT_COMP,
+ WSA881X_PORT_BOOST,
+ WSA881X_PORT_VISENSE,
+};
+
+/* 4 ports */
+static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
+ [WSA881X_PORT_DAC] = {
+ .num = WSA881X_PORT_DAC + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA881X_PORT_COMP] = {
+ .num = WSA881X_PORT_COMP + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA881X_PORT_BOOST] = {
+ .num = WSA881X_PORT_BOOST + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA881X_PORT_VISENSE] = {
+ .num = WSA881X_PORT_VISENSE + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }
+};
+
+static const struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = {
+ [WSA881X_PORT_DAC] = {
+ .num = WSA881X_PORT_DAC + 1,
+ .ch_mask = 0x1,
+ },
+ [WSA881X_PORT_COMP] = {
+ .num = WSA881X_PORT_COMP + 1,
+ .ch_mask = 0xf,
+ },
+ [WSA881X_PORT_BOOST] = {
+ .num = WSA881X_PORT_BOOST + 1,
+ .ch_mask = 0x3,
+ },
+ [WSA881X_PORT_VISENSE] = {
+ .num = WSA881X_PORT_VISENSE + 1,
+ .ch_mask = 0x3,
+ },
+};
+
+static bool wsa881x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA881X_CHIP_ID0:
+ case WSA881X_CHIP_ID1:
+ case WSA881X_CHIP_ID2:
+ case WSA881X_CHIP_ID3:
+ case WSA881X_BUS_ID:
+ case WSA881X_CDC_RST_CTL:
+ case WSA881X_CDC_TOP_CLK_CTL:
+ case WSA881X_CDC_ANA_CLK_CTL:
+ case WSA881X_CDC_DIG_CLK_CTL:
+ case WSA881X_CLOCK_CONFIG:
+ case WSA881X_ANA_CTL:
+ case WSA881X_SWR_RESET_EN:
+ case WSA881X_RESET_CTL:
+ case WSA881X_TADC_VALUE_CTL:
+ case WSA881X_TEMP_DETECT_CTL:
+ case WSA881X_TEMP_MSB:
+ case WSA881X_TEMP_LSB:
+ case WSA881X_TEMP_CONFIG0:
+ case WSA881X_TEMP_CONFIG1:
+ case WSA881X_CDC_CLIP_CTL:
+ case WSA881X_SDM_PDM9_LSB:
+ case WSA881X_SDM_PDM9_MSB:
+ case WSA881X_CDC_RX_CTL:
+ case WSA881X_DEM_BYPASS_DATA0:
+ case WSA881X_DEM_BYPASS_DATA1:
+ case WSA881X_DEM_BYPASS_DATA2:
+ case WSA881X_DEM_BYPASS_DATA3:
+ case WSA881X_OTP_CTRL0:
+ case WSA881X_OTP_CTRL1:
+ case WSA881X_HDRIVE_CTL_GROUP1:
+ case WSA881X_INTR_MODE:
+ case WSA881X_INTR_MASK:
+ case WSA881X_INTR_STATUS:
+ case WSA881X_INTR_CLEAR:
+ case WSA881X_INTR_LEVEL:
+ case WSA881X_INTR_SET:
+ case WSA881X_INTR_TEST:
+ case WSA881X_PDM_TEST_MODE:
+ case WSA881X_ATE_TEST_MODE:
+ case WSA881X_PIN_CTL_MODE:
+ case WSA881X_PIN_CTL_OE:
+ case WSA881X_PIN_WDATA_IOPAD:
+ case WSA881X_PIN_STATUS:
+ case WSA881X_DIG_DEBUG_MODE:
+ case WSA881X_DIG_DEBUG_SEL:
+ case WSA881X_DIG_DEBUG_EN:
+ case WSA881X_SWR_HM_TEST1:
+ case WSA881X_SWR_HM_TEST2:
+ case WSA881X_TEMP_DETECT_DBG_CTL:
+ case WSA881X_TEMP_DEBUG_MSB:
+ case WSA881X_TEMP_DEBUG_LSB:
+ case WSA881X_SAMPLE_EDGE_SEL:
+ case WSA881X_IOPAD_CTL:
+ case WSA881X_SPARE_0:
+ case WSA881X_SPARE_1:
+ case WSA881X_SPARE_2:
+ case WSA881X_OTP_REG_0:
+ case WSA881X_OTP_REG_1:
+ case WSA881X_OTP_REG_2:
+ case WSA881X_OTP_REG_3:
+ case WSA881X_OTP_REG_4:
+ case WSA881X_OTP_REG_5:
+ case WSA881X_OTP_REG_6:
+ case WSA881X_OTP_REG_7:
+ case WSA881X_OTP_REG_8:
+ case WSA881X_OTP_REG_9:
+ case WSA881X_OTP_REG_10:
+ case WSA881X_OTP_REG_11:
+ case WSA881X_OTP_REG_12:
+ case WSA881X_OTP_REG_13:
+ case WSA881X_OTP_REG_14:
+ case WSA881X_OTP_REG_15:
+ case WSA881X_OTP_REG_16:
+ case WSA881X_OTP_REG_17:
+ case WSA881X_OTP_REG_18:
+ case WSA881X_OTP_REG_19:
+ case WSA881X_OTP_REG_20:
+ case WSA881X_OTP_REG_21:
+ case WSA881X_OTP_REG_22:
+ case WSA881X_OTP_REG_23:
+ case WSA881X_OTP_REG_24:
+ case WSA881X_OTP_REG_25:
+ case WSA881X_OTP_REG_26:
+ case WSA881X_OTP_REG_27:
+ case WSA881X_OTP_REG_28:
+ case WSA881X_OTP_REG_29:
+ case WSA881X_OTP_REG_30:
+ case WSA881X_OTP_REG_31:
+ case WSA881X_OTP_REG_63:
+ case WSA881X_BIAS_REF_CTRL:
+ case WSA881X_BIAS_TEST:
+ case WSA881X_BIAS_BIAS:
+ case WSA881X_TEMP_OP:
+ case WSA881X_TEMP_IREF_CTRL:
+ case WSA881X_TEMP_ISENS_CTRL:
+ case WSA881X_TEMP_CLK_CTRL:
+ case WSA881X_TEMP_TEST:
+ case WSA881X_TEMP_BIAS:
+ case WSA881X_TEMP_ADC_CTRL:
+ case WSA881X_TEMP_DOUT_MSB:
+ case WSA881X_TEMP_DOUT_LSB:
+ case WSA881X_ADC_EN_MODU_V:
+ case WSA881X_ADC_EN_MODU_I:
+ case WSA881X_ADC_EN_DET_TEST_V:
+ case WSA881X_ADC_EN_DET_TEST_I:
+ case WSA881X_ADC_SEL_IBIAS:
+ case WSA881X_ADC_EN_SEL_IBAIS:
+ case WSA881X_SPKR_DRV_EN:
+ case WSA881X_SPKR_DRV_GAIN:
+ case WSA881X_SPKR_DAC_CTL:
+ case WSA881X_SPKR_DRV_DBG:
+ case WSA881X_SPKR_PWRSTG_DBG:
+ case WSA881X_SPKR_OCP_CTL:
+ case WSA881X_SPKR_CLIP_CTL:
+ case WSA881X_SPKR_BBM_CTL:
+ case WSA881X_SPKR_MISC_CTL1:
+ case WSA881X_SPKR_MISC_CTL2:
+ case WSA881X_SPKR_BIAS_INT:
+ case WSA881X_SPKR_PA_INT:
+ case WSA881X_SPKR_BIAS_CAL:
+ case WSA881X_SPKR_BIAS_PSRR:
+ case WSA881X_SPKR_STATUS1:
+ case WSA881X_SPKR_STATUS2:
+ case WSA881X_BOOST_EN_CTL:
+ case WSA881X_BOOST_CURRENT_LIMIT:
+ case WSA881X_BOOST_PS_CTL:
+ case WSA881X_BOOST_PRESET_OUT1:
+ case WSA881X_BOOST_PRESET_OUT2:
+ case WSA881X_BOOST_FORCE_OUT:
+ case WSA881X_BOOST_LDO_PROG:
+ case WSA881X_BOOST_SLOPE_COMP_ISENSE_FB:
+ case WSA881X_BOOST_RON_CTL:
+ case WSA881X_BOOST_LOOP_STABILITY:
+ case WSA881X_BOOST_ZX_CTL:
+ case WSA881X_BOOST_START_CTL:
+ case WSA881X_BOOST_MISC1_CTL:
+ case WSA881X_BOOST_MISC2_CTL:
+ case WSA881X_BOOST_MISC3_CTL:
+ case WSA881X_BOOST_ATEST_CTL:
+ case WSA881X_SPKR_PROT_FE_GAIN:
+ case WSA881X_SPKR_PROT_FE_CM_LDO_SET:
+ case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1:
+ case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2:
+ case WSA881X_SPKR_PROT_ATEST1:
+ case WSA881X_SPKR_PROT_ATEST2:
+ case WSA881X_SPKR_PROT_FE_VSENSE_VCM:
+ case WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1:
+ case WSA881X_BONGO_RESRV_REG1:
+ case WSA881X_BONGO_RESRV_REG2:
+ case WSA881X_SPKR_PROT_SAR:
+ case WSA881X_SPKR_STATUS3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wsa881x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA881X_CHIP_ID0:
+ case WSA881X_CHIP_ID1:
+ case WSA881X_CHIP_ID2:
+ case WSA881X_CHIP_ID3:
+ case WSA881X_BUS_ID:
+ case WSA881X_TEMP_MSB:
+ case WSA881X_TEMP_LSB:
+ case WSA881X_SDM_PDM9_LSB:
+ case WSA881X_SDM_PDM9_MSB:
+ case WSA881X_OTP_CTRL1:
+ case WSA881X_INTR_STATUS:
+ case WSA881X_ATE_TEST_MODE:
+ case WSA881X_PIN_STATUS:
+ case WSA881X_SWR_HM_TEST2:
+ case WSA881X_SPKR_STATUS1:
+ case WSA881X_SPKR_STATUS2:
+ case WSA881X_SPKR_STATUS3:
+ case WSA881X_OTP_REG_0:
+ case WSA881X_OTP_REG_1:
+ case WSA881X_OTP_REG_2:
+ case WSA881X_OTP_REG_3:
+ case WSA881X_OTP_REG_4:
+ case WSA881X_OTP_REG_5:
+ case WSA881X_OTP_REG_31:
+ case WSA881X_TEMP_DOUT_MSB:
+ case WSA881X_TEMP_DOUT_LSB:
+ case WSA881X_TEMP_OP:
+ case WSA881X_SPKR_PROT_SAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config wsa881x_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wsa881x_defaults,
+ .max_register = WSA881X_SPKR_STATUS3,
+ .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults),
+ .volatile_reg = wsa881x_volatile_register,
+ .readable_reg = wsa881x_readable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+};
+
+enum {
+ G_18DB = 0,
+ G_16P5DB,
+ G_15DB,
+ G_13P5DB,
+ G_12DB,
+ G_10P5DB,
+ G_9DB,
+ G_7P5DB,
+ G_6DB,
+ G_4P5DB,
+ G_3DB,
+ G_1P5DB,
+ G_0DB,
+};
+
+/*
+ * Private data Structure for wsa881x. All parameters related to
+ * WSA881X codec needs to be defined here.
+ */
+struct wsa881x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct sdw_slave *slave;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS];
+ struct gpio_desc *sd_n;
+ /*
+ * Logical state for SD_N GPIO: high for shutdown, low for enable.
+ * For backwards compatibility.
+ */
+ unsigned int sd_n_val;
+ int active_ports;
+ bool port_prepared[WSA881X_MAX_SWR_PORTS];
+ bool port_enable[WSA881X_MAX_SWR_PORTS];
+};
+
+static void wsa881x_init(struct wsa881x_priv *wsa881x)
+{
+ struct regmap *rm = wsa881x->regmap;
+ unsigned int val = 0;
+
+ regmap_register_patch(wsa881x->regmap, wsa881x_rev_2_0,
+ ARRAY_SIZE(wsa881x_rev_2_0));
+
+ /* Enable software reset output from soundwire slave */
+ regmap_update_bits(rm, WSA881X_SWR_RESET_EN, 0x07, 0x07);
+
+ /* Bring out of analog reset */
+ regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x02, 0x02);
+
+ /* Bring out of digital reset */
+ regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x01, 0x01);
+ regmap_update_bits(rm, WSA881X_CLOCK_CONFIG, 0x10, 0x10);
+ regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x02, 0x02);
+ regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80);
+ regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06);
+ regmap_update_bits(rm, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00);
+ regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0xF0, 0x40);
+ regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0x0E, 0x0E);
+ regmap_update_bits(rm, WSA881X_BOOST_LOOP_STABILITY, 0x03, 0x03);
+ regmap_update_bits(rm, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14);
+ regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x80, 0x80);
+ regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x03, 0x00);
+ regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x0C, 0x04);
+ regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x03, 0x00);
+
+ regmap_read(rm, WSA881X_OTP_REG_0, &val);
+ if (val)
+ regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT1, 0xF0, 0x70);
+
+ regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT2, 0xF0, 0x30);
+ regmap_update_bits(rm, WSA881X_SPKR_DRV_EN, 0x08, 0x08);
+ regmap_update_bits(rm, WSA881X_BOOST_CURRENT_LIMIT, 0x0F, 0x08);
+ regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x30, 0x30);
+ regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00);
+ regmap_update_bits(rm, WSA881X_OTP_REG_28, 0x3F, 0x3A);
+ regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG1, 0xFF, 0xB2);
+ regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG2, 0xFF, 0x05);
+}
+
+static int wsa881x_component_probe(struct snd_soc_component *comp)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, wsa881x->regmap);
+
+ return 0;
+}
+
+static int wsa881x_put_pa_gain(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kc);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kc->private_value;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ int val, ret, min_gain, max_gain;
+
+ ret = pm_runtime_resume_and_get(comp->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ max_gain = (max - ucontrol->value.integer.value[0]) & mask;
+ /*
+ * Gain has to set incrementally in 4 steps
+ * as per HW sequence
+ */
+ if (max_gain > G_4P5DB)
+ min_gain = G_0DB;
+ else
+ min_gain = max_gain + 3;
+ /*
+ * 1ms delay is needed before change in gain
+ * as per HW requirement.
+ */
+ usleep_range(1000, 1010);
+
+ for (val = min_gain; max_gain <= val; val--) {
+ ret = snd_soc_component_update_bits(comp,
+ WSA881X_SPKR_DRV_GAIN,
+ WSA881X_SPKR_PAG_GAIN_MASK,
+ val << 4);
+ if (ret < 0)
+ dev_err(comp->dev, "Failed to change PA gain");
+
+ usleep_range(1000, 1010);
+ }
+
+ pm_runtime_put_autosuspend(comp->dev);
+
+ return 1;
+}
+
+static int wsa881x_get_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ ucontrol->value.integer.value[0] = data->port_enable[portidx];
+
+
+ return 0;
+}
+
+static int wsa881x_boost_ctrl(struct snd_soc_component *comp, bool enable)
+{
+ if (enable)
+ snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL,
+ WSA881X_BOOST_EN_MASK,
+ WSA881X_BOOST_EN);
+ else
+ snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL,
+ WSA881X_BOOST_EN_MASK, 0);
+ /*
+ * 1.5ms sleep is needed after boost enable/disable as per
+ * HW requirement
+ */
+ usleep_range(1500, 1510);
+ return 0;
+}
+
+static int wsa881x_set_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ if (ucontrol->value.integer.value[0]) {
+ if (data->port_enable[portidx])
+ return 0;
+
+ data->port_enable[portidx] = true;
+ } else {
+ if (!data->port_enable[portidx])
+ return 0;
+
+ data->port_enable[portidx] = false;
+ }
+
+ if (portidx == WSA881X_PORT_BOOST) /* Boost Switch */
+ wsa881x_boost_ctrl(comp, data->port_enable[portidx]);
+
+ return 1;
+}
+
+static const char * const smart_boost_lvl_text[] = {
+ "6.625 V", "6.750 V", "6.875 V", "7.000 V",
+ "7.125 V", "7.250 V", "7.375 V", "7.500 V",
+ "7.625 V", "7.750 V", "7.875 V", "8.000 V",
+ "8.125 V", "8.250 V", "8.375 V", "8.500 V"
+};
+
+static const struct soc_enum smart_boost_lvl_enum =
+ SOC_ENUM_SINGLE(WSA881X_BOOST_PRESET_OUT1, 0,
+ ARRAY_SIZE(smart_boost_lvl_text),
+ smart_boost_lvl_text);
+
+static const DECLARE_TLV_DB_SCALE(pa_gain, 0, 150, 0);
+
+static const struct snd_kcontrol_new wsa881x_snd_controls[] = {
+ SOC_ENUM("Smart Boost Level", smart_boost_lvl_enum),
+ WSA881X_PA_GAIN_TLV("PA Volume", WSA881X_SPKR_DRV_GAIN,
+ 4, 0xC, 1, pa_gain),
+ SOC_SINGLE_EXT("DAC Switch", WSA881X_PORT_DAC, 0, 1, 0,
+ wsa881x_get_port, wsa881x_set_port),
+ SOC_SINGLE_EXT("COMP Switch", WSA881X_PORT_COMP, 0, 1, 0,
+ wsa881x_get_port, wsa881x_set_port),
+ SOC_SINGLE_EXT("BOOST Switch", WSA881X_PORT_BOOST, 0, 1, 0,
+ wsa881x_get_port, wsa881x_set_port),
+ SOC_SINGLE_EXT("VISENSE Switch", WSA881X_PORT_VISENSE, 0, 1, 0,
+ wsa881x_get_port, wsa881x_set_port),
+};
+
+static const struct snd_soc_dapm_route wsa881x_audio_map[] = {
+ { "RDAC", NULL, "IN" },
+ { "RDAC", NULL, "DCLK" },
+ { "RDAC", NULL, "ACLK" },
+ { "RDAC", NULL, "Bandgap" },
+ { "SPKR PGA", NULL, "RDAC" },
+ { "SPKR", NULL, "SPKR PGA" },
+};
+
+static int wsa881x_visense_txfe_ctrl(struct snd_soc_component *comp,
+ bool enable)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp);
+
+ if (enable) {
+ regmap_multi_reg_write(wsa881x->regmap, wsa881x_vi_txfe_en_2_0,
+ ARRAY_SIZE(wsa881x_vi_txfe_en_2_0));
+ } else {
+ snd_soc_component_update_bits(comp,
+ WSA881X_SPKR_PROT_FE_VSENSE_VCM,
+ 0x08, 0x08);
+ /*
+ * 200us sleep is needed after visense txfe disable as per
+ * HW requirement.
+ */
+ usleep_range(200, 210);
+ snd_soc_component_update_bits(comp, WSA881X_SPKR_PROT_FE_GAIN,
+ 0x01, 0x00);
+ }
+ return 0;
+}
+
+static int wsa881x_visense_adc_ctrl(struct snd_soc_component *comp,
+ bool enable)
+{
+ snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_V, BIT(7),
+ (enable << 7));
+ snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_I, BIT(7),
+ (enable << 7));
+ return 0;
+}
+
+static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL,
+ WSA881X_SPKR_OCP_MASK,
+ WSA881X_SPKR_OCP_EN);
+ regmap_multi_reg_write(wsa881x->regmap, wsa881x_pre_pmu_pa_2_0,
+ ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0));
+
+ snd_soc_component_update_bits(comp, WSA881X_SPKR_DRV_GAIN,
+ WSA881X_PA_GAIN_SEL_MASK,
+ WSA881X_PA_GAIN_SEL_REG);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) {
+ wsa881x_visense_txfe_ctrl(comp, true);
+ snd_soc_component_update_bits(comp,
+ WSA881X_ADC_EN_SEL_IBAIS,
+ 0x07, 0x01);
+ wsa881x_visense_adc_ctrl(comp, true);
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) {
+ wsa881x_visense_adc_ctrl(comp, false);
+ wsa881x_visense_txfe_ctrl(comp, false);
+ }
+
+ snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL,
+ WSA881X_SPKR_OCP_MASK,
+ WSA881X_SPKR_OCP_EN |
+ WSA881X_SPKR_OCP_HOLD);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0,
+ NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("SPKR PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DCLK", WSA881X_CDC_DIG_CLK_CTL, 0, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("ACLK", WSA881X_CDC_ANA_CLK_CTL, 0, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Bandgap", WSA881X_TEMP_OP, 3, 0,
+ NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static int wsa881x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev);
+ int i;
+
+ wsa881x->active_ports = 0;
+ for (i = 0; i < WSA881X_MAX_SWR_PORTS; i++) {
+ if (!wsa881x->port_enable[i])
+ continue;
+
+ wsa881x->port_config[wsa881x->active_ports] =
+ wsa881x_pconfig[i];
+ wsa881x->active_ports++;
+ }
+
+ return sdw_stream_add_slave(wsa881x->slave, &wsa881x->sconfig,
+ wsa881x->port_config, wsa881x->active_ports,
+ wsa881x->sruntime);
+}
+
+static int wsa881x_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev);
+
+ sdw_stream_remove_slave(wsa881x->slave, wsa881x->sruntime);
+
+ return 0;
+}
+
+static int wsa881x_set_sdw_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev);
+
+ wsa881x->sruntime = stream;
+
+ return 0;
+}
+
+static int wsa881x_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev);
+
+ if (mute)
+ regmap_update_bits(wsa881x->regmap, WSA881X_SPKR_DRV_EN, 0x80,
+ 0x00);
+ else
+ regmap_update_bits(wsa881x->regmap, WSA881X_SPKR_DRV_EN, 0x80,
+ 0x80);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wsa881x_dai_ops = {
+ .hw_params = wsa881x_hw_params,
+ .hw_free = wsa881x_hw_free,
+ .mute_stream = wsa881x_digital_mute,
+ .set_stream = wsa881x_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver wsa881x_dais[] = {
+ {
+ .name = "SPKR",
+ .id = 0,
+ .playback = {
+ .stream_name = "SPKR Playback",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &wsa881x_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver wsa881x_component_drv = {
+ .name = "WSA881x",
+ .probe = wsa881x_component_probe,
+ .controls = wsa881x_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa881x_snd_controls),
+ .dapm_widgets = wsa881x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
+ .dapm_routes = wsa881x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+ .endianness = 1,
+};
+
+static int wsa881x_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_ATTACHED && slave->dev_num > 0)
+ wsa881x_init(wsa881x);
+
+ return 0;
+}
+
+static int wsa881x_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev);
+
+ if (state == SDW_OPS_PORT_POST_PREP)
+ wsa881x->port_prepared[prepare_ch->num - 1] = true;
+ else
+ wsa881x->port_prepared[prepare_ch->num - 1] = false;
+
+ return 0;
+}
+
+static int wsa881x_bus_config(struct sdw_slave *slave,
+ struct sdw_bus_params *params)
+{
+ sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank),
+ 0x01);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops wsa881x_slave_ops = {
+ .update_status = wsa881x_update_status,
+ .bus_config = wsa881x_bus_config,
+ .port_prep = wsa881x_port_prep,
+};
+
+static int wsa881x_probe(struct sdw_slave *pdev,
+ const struct sdw_device_id *id)
+{
+ struct wsa881x_priv *wsa881x;
+ struct device *dev = &pdev->dev;
+
+ wsa881x = devm_kzalloc(dev, sizeof(*wsa881x), GFP_KERNEL);
+ if (!wsa881x)
+ return -ENOMEM;
+
+ wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", 0);
+ if (IS_ERR(wsa881x->sd_n))
+ return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n),
+ "Shutdown Control GPIO not found\n");
+
+ /*
+ * Backwards compatibility work-around.
+ *
+ * The SD_N GPIO is active low, however upstream DTS used always active
+ * high. Changing the flag in driver and DTS will break backwards
+ * compatibility, so add a simple value inversion to work with both old
+ * and new DTS.
+ *
+ * This won't work properly with DTS using the flags properly in cases:
+ * 1. Old DTS with proper ACTIVE_LOW, however such case was broken
+ * before as the driver required the active high.
+ * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case
+ * (not existing upstream) but possible. This is the price of
+ * backwards compatibility, therefore this hack should be removed at
+ * some point.
+ */
+ wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n);
+ if (!wsa881x->sd_n_val)
+ dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO.");
+
+ dev_set_drvdata(dev, wsa881x);
+ wsa881x->slave = pdev;
+ wsa881x->dev = dev;
+ wsa881x->sconfig.ch_count = 1;
+ wsa881x->sconfig.bps = 1;
+ wsa881x->sconfig.frame_rate = 48000;
+ wsa881x->sconfig.direction = SDW_DATA_DIR_RX;
+ wsa881x->sconfig.type = SDW_STREAM_PDM;
+ pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ pdev->prop.clk_stop_mode1 = true;
+ gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
+
+ wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config);
+ if (IS_ERR(wsa881x->regmap))
+ return dev_err_probe(dev, PTR_ERR(wsa881x->regmap), "regmap_init failed\n");
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return devm_snd_soc_register_component(dev,
+ &wsa881x_component_drv,
+ wsa881x_dais,
+ ARRAY_SIZE(wsa881x_dais));
+}
+
+static int wsa881x_runtime_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
+
+ gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int wsa881x_runtime_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
+ unsigned long time;
+
+ gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(WSA881X_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(dev, "Initialization not complete, timed out\n");
+ gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
+ return -ETIMEDOUT;
+ }
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wsa881x_pm_ops = {
+ RUNTIME_PM_OPS(wsa881x_runtime_suspend, wsa881x_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id wsa881x_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x2010, 0),
+ SDW_SLAVE_ENTRY(0x0217, 0x2110, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, wsa881x_slave_id);
+
+static struct sdw_driver wsa881x_codec_driver = {
+ .probe = wsa881x_probe,
+ .ops = &wsa881x_slave_ops,
+ .id_table = wsa881x_slave_id,
+ .driver = {
+ .name = "wsa881x-codec",
+ .pm = pm_ptr(&wsa881x_pm_ops),
+ }
+};
+module_sdw_driver(wsa881x_codec_driver);
+
+MODULE_DESCRIPTION("WSA881x codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
new file mode 100644
index 000000000000..c3046e260cb9
--- /dev/null
+++ b/sound/soc/codecs/wsa883x.c
@@ -0,0 +1,1721 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define WSA883X_BASE 0x3000
+#define WSA883X_ANA_BG_TSADC_BASE (WSA883X_BASE + 0x00000001)
+#define WSA883X_REF_CTRL (WSA883X_ANA_BG_TSADC_BASE + 0x0000)
+#define WSA883X_TEST_CTL_0 (WSA883X_ANA_BG_TSADC_BASE + 0x0001)
+#define WSA883X_BIAS_0 (WSA883X_ANA_BG_TSADC_BASE + 0x0002)
+#define WSA883X_OP_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0003)
+#define WSA883X_IREF_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0004)
+#define WSA883X_ISENS_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0005)
+#define WSA883X_CLK_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0006)
+#define WSA883X_TEST_CTL_1 (WSA883X_ANA_BG_TSADC_BASE + 0x0007)
+#define WSA883X_BIAS_1 (WSA883X_ANA_BG_TSADC_BASE + 0x0008)
+#define WSA883X_ADC_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0009)
+#define WSA883X_DOUT_MSB (WSA883X_ANA_BG_TSADC_BASE + 0x000A)
+#define WSA883X_DOUT_LSB (WSA883X_ANA_BG_TSADC_BASE + 0x000B)
+#define WSA883X_VBAT_SNS (WSA883X_ANA_BG_TSADC_BASE + 0x000C)
+#define WSA883X_ITRIM_CODE (WSA883X_ANA_BG_TSADC_BASE + 0x000D)
+
+#define WSA883X_ANA_IVSENSE_BASE (WSA883X_BASE + 0x0000000F)
+#define WSA883X_EN (WSA883X_ANA_IVSENSE_BASE + 0x0000)
+#define WSA883X_OVERRIDE1 (WSA883X_ANA_IVSENSE_BASE + 0x0001)
+#define WSA883X_OVERRIDE2 (WSA883X_ANA_IVSENSE_BASE + 0x0002)
+#define WSA883X_VSENSE1 (WSA883X_ANA_IVSENSE_BASE + 0x0003)
+#define WSA883X_ISENSE1 (WSA883X_ANA_IVSENSE_BASE + 0x0004)
+#define WSA883X_ISENSE2 (WSA883X_ANA_IVSENSE_BASE + 0x0005)
+#define WSA883X_ISENSE_CAL (WSA883X_ANA_IVSENSE_BASE + 0x0006)
+#define WSA883X_MISC (WSA883X_ANA_IVSENSE_BASE + 0x0007)
+#define WSA883X_ADC_0 (WSA883X_ANA_IVSENSE_BASE + 0x0008)
+#define WSA883X_ADC_1 (WSA883X_ANA_IVSENSE_BASE + 0x0009)
+#define WSA883X_ADC_2 (WSA883X_ANA_IVSENSE_BASE + 0x000A)
+#define WSA883X_ADC_3 (WSA883X_ANA_IVSENSE_BASE + 0x000B)
+#define WSA883X_ADC_4 (WSA883X_ANA_IVSENSE_BASE + 0x000C)
+#define WSA883X_ADC_5 (WSA883X_ANA_IVSENSE_BASE + 0x000D)
+#define WSA883X_ADC_6 (WSA883X_ANA_IVSENSE_BASE + 0x000E)
+#define WSA883X_ADC_7 (WSA883X_ANA_IVSENSE_BASE + 0x000F)
+#define WSA883X_STATUS (WSA883X_ANA_IVSENSE_BASE + 0x0010)
+
+#define WSA883X_ANA_SPK_TOP_BASE (WSA883X_BASE + 0x00000025)
+#define WSA883X_DAC_CTRL_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0000)
+#define WSA883X_DAC_EN_DEBUG_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0001)
+#define WSA883X_DAC_OPAMP_BIAS1_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0002)
+#define WSA883X_DAC_OPAMP_BIAS2_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0003)
+#define WSA883X_DAC_VCM_CTRL_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0004)
+#define WSA883X_DAC_VOLTAGE_CTRL_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0005)
+#define WSA883X_ATEST1_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0006)
+#define WSA883X_ATEST2_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0007)
+#define WSA883X_SPKR_TOP_BIAS_REG1 (WSA883X_ANA_SPK_TOP_BASE + 0x0008)
+#define WSA883X_SPKR_TOP_BIAS_REG2 (WSA883X_ANA_SPK_TOP_BASE + 0x0009)
+#define WSA883X_SPKR_TOP_BIAS_REG3 (WSA883X_ANA_SPK_TOP_BASE + 0x000A)
+#define WSA883X_SPKR_TOP_BIAS_REG4 (WSA883X_ANA_SPK_TOP_BASE + 0x000B)
+#define WSA883X_SPKR_CLIP_DET_REG (WSA883X_ANA_SPK_TOP_BASE + 0x000C)
+#define WSA883X_SPKR_DRV_LF_BLK_EN (WSA883X_ANA_SPK_TOP_BASE + 0x000D)
+#define WSA883X_SPKR_DRV_LF_EN (WSA883X_ANA_SPK_TOP_BASE + 0x000E)
+#define WSA883X_SPKR_DRV_LF_MASK_DCC_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x000F)
+#define WSA883X_SPKR_DRV_LF_MISC_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0010)
+#define WSA883X_SPKR_DRV_LF_REG_GAIN (WSA883X_ANA_SPK_TOP_BASE + 0x0011)
+#define WSA883X_SPKR_DRV_OS_CAL_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0012)
+#define WSA883X_SPKR_DRV_OS_CAL_CTL1 (WSA883X_ANA_SPK_TOP_BASE + 0x0013)
+#define WSA883X_SPKR_PWM_CLK_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0014)
+#define WSA883X_SPKR_PWM_FREQ_SEL_MASK BIT(3)
+#define WSA883X_SPKR_PWM_FREQ_F300KHZ 0
+#define WSA883X_SPKR_PWM_FREQ_F600KHZ 1
+#define WSA883X_SPKR_PDRV_HS_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0015)
+#define WSA883X_SPKR_PDRV_LS_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0016)
+#define WSA883X_SPKR_PWRSTG_DBG (WSA883X_ANA_SPK_TOP_BASE + 0x0017)
+#define WSA883X_SPKR_OCP_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0018)
+#define WSA883X_SPKR_BBM_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0019)
+#define WSA883X_PA_STATUS0 (WSA883X_ANA_SPK_TOP_BASE + 0x001A)
+#define WSA883X_PA_STATUS1 (WSA883X_ANA_SPK_TOP_BASE + 0x001B)
+#define WSA883X_PA_STATUS2 (WSA883X_ANA_SPK_TOP_BASE + 0x001C)
+
+#define WSA883X_ANA_BOOST_BASE (WSA883X_BASE + 0x00000043)
+#define WSA883X_EN_CTRL (WSA883X_ANA_BOOST_BASE + 0x0000)
+#define WSA883X_CURRENT_LIMIT (WSA883X_ANA_BOOST_BASE + 0x0001)
+#define WSA883X_IBIAS1 (WSA883X_ANA_BOOST_BASE + 0x0002)
+#define WSA883X_IBIAS2 (WSA883X_ANA_BOOST_BASE + 0x0003)
+#define WSA883X_IBIAS3 (WSA883X_ANA_BOOST_BASE + 0x0004)
+#define WSA883X_LDO_PROG (WSA883X_ANA_BOOST_BASE + 0x0005)
+#define WSA883X_STABILITY_CTRL1 (WSA883X_ANA_BOOST_BASE + 0x0006)
+#define WSA883X_STABILITY_CTRL2 (WSA883X_ANA_BOOST_BASE + 0x0007)
+#define WSA883X_PWRSTAGE_CTRL1 (WSA883X_ANA_BOOST_BASE + 0x0008)
+#define WSA883X_PWRSTAGE_CTRL2 (WSA883X_ANA_BOOST_BASE + 0x0009)
+#define WSA883X_BYPASS_1 (WSA883X_ANA_BOOST_BASE + 0x000A)
+#define WSA883X_BYPASS_2 (WSA883X_ANA_BOOST_BASE + 0x000B)
+#define WSA883X_ZX_CTRL_1 (WSA883X_ANA_BOOST_BASE + 0x000C)
+#define WSA883X_ZX_CTRL_2 (WSA883X_ANA_BOOST_BASE + 0x000D)
+#define WSA883X_MISC1 (WSA883X_ANA_BOOST_BASE + 0x000E)
+#define WSA883X_MISC2 (WSA883X_ANA_BOOST_BASE + 0x000F)
+#define WSA883X_GMAMP_SUP1 (WSA883X_ANA_BOOST_BASE + 0x0010)
+#define WSA883X_PWRSTAGE_CTRL3 (WSA883X_ANA_BOOST_BASE + 0x0011)
+#define WSA883X_PWRSTAGE_CTRL4 (WSA883X_ANA_BOOST_BASE + 0x0012)
+#define WSA883X_TEST1 (WSA883X_ANA_BOOST_BASE + 0x0013)
+#define WSA883X_SPARE1 (WSA883X_ANA_BOOST_BASE + 0x0014)
+#define WSA883X_SPARE2 (WSA883X_ANA_BOOST_BASE + 0x0015)
+
+#define WSA883X_ANA_PON_LDOL_BASE (WSA883X_BASE + 0x00000059)
+#define WSA883X_PON_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0000)
+#define WSA883X_PON_CLT_1 (WSA883X_ANA_PON_LDOL_BASE + 0x0001)
+#define WSA883X_PON_CTL_2 (WSA883X_ANA_PON_LDOL_BASE + 0x0002)
+#define WSA883X_PON_CTL_3 (WSA883X_ANA_PON_LDOL_BASE + 0x0003)
+#define WSA883X_CKWD_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0004)
+#define WSA883X_CKWD_CTL_1 (WSA883X_ANA_PON_LDOL_BASE + 0x0005)
+#define WSA883X_CKWD_CTL_2 (WSA883X_ANA_PON_LDOL_BASE + 0x0006)
+#define WSA883X_CKSK_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0007)
+#define WSA883X_PADSW_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0008)
+#define WSA883X_TEST_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0009)
+#define WSA883X_TEST_1 (WSA883X_ANA_PON_LDOL_BASE + 0x000A)
+#define WSA883X_STATUS_0 (WSA883X_ANA_PON_LDOL_BASE + 0x000B)
+#define WSA883X_STATUS_1 (WSA883X_ANA_PON_LDOL_BASE + 0x000C)
+
+#define WSA883X_DIG_CTRL_BASE (WSA883X_BASE + 0x00000400)
+#define WSA883X_CHIP_ID0 (WSA883X_DIG_CTRL_BASE + 0x0001)
+#define WSA883X_CHIP_ID1 (WSA883X_DIG_CTRL_BASE + 0x0002)
+#define WSA883X_CHIP_ID2 (WSA883X_DIG_CTRL_BASE + 0x0003)
+#define WSA883X_CHIP_ID3 (WSA883X_DIG_CTRL_BASE + 0x0004)
+#define WSA883X_BUS_ID (WSA883X_DIG_CTRL_BASE + 0x0005)
+#define WSA883X_CDC_RST_CTL (WSA883X_DIG_CTRL_BASE + 0x0006)
+#define WSA883X_TOP_CLK_CFG (WSA883X_DIG_CTRL_BASE + 0x0007)
+#define WSA883X_CDC_PATH_MODE (WSA883X_DIG_CTRL_BASE + 0x0008)
+#define WSA883X_RXD_MODE_MASK BIT(1)
+#define WSA883X_RXD_MODE_NORMAL 0
+#define WSA883X_RXD_MODE_HIFI 1
+#define WSA883X_CDC_CLK_CTL (WSA883X_DIG_CTRL_BASE + 0x0009)
+#define WSA883X_SWR_RESET_EN (WSA883X_DIG_CTRL_BASE + 0x000A)
+#define WSA883X_RESET_CTL (WSA883X_DIG_CTRL_BASE + 0x000B)
+#define WSA883X_PA_FSM_CTL (WSA883X_DIG_CTRL_BASE + 0x0010)
+#define WSA883X_GLOBAL_PA_EN_MASK BIT(0)
+#define WSA883X_GLOBAL_PA_ENABLE 1
+#define WSA883X_PA_FSM_TIMER0 (WSA883X_DIG_CTRL_BASE + 0x0011)
+#define WSA883X_PA_FSM_TIMER1 (WSA883X_DIG_CTRL_BASE + 0x0012)
+#define WSA883X_PA_FSM_STA (WSA883X_DIG_CTRL_BASE + 0x0013)
+#define WSA883X_PA_FSM_ERR_COND (WSA883X_DIG_CTRL_BASE + 0x0014)
+#define WSA883X_PA_FSM_MSK (WSA883X_DIG_CTRL_BASE + 0x0015)
+#define WSA883X_PA_FSM_BYP (WSA883X_DIG_CTRL_BASE + 0x0016)
+#define WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK 0x01
+#define WSA883X_PA_FSM_BYP_DC_CAL_EN_SHIFT 0
+#define WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK 0x02
+#define WSA883X_PA_FSM_BYP_CLK_WD_EN_SHIFT 1
+#define WSA883X_PA_FSM_BYP_BG_EN_MASK 0x04
+#define WSA883X_PA_FSM_BYP_BG_EN_SHIFT 2
+#define WSA883X_PA_FSM_BYP_BOOST_EN_MASK 0x08
+#define WSA883X_PA_FSM_BYP_BOOST_EN_SHIFT 3
+#define WSA883X_PA_FSM_BYP_PA_EN_MASK 0x10
+#define WSA883X_PA_FSM_BYP_PA_EN_SHIFT 4
+#define WSA883X_PA_FSM_BYP_D_UNMUTE_MASK 0x20
+#define WSA883X_PA_FSM_BYP_D_UNMUTE_SHIFT 5
+#define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK 0x40
+#define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_SHIFT 6
+#define WSA883X_PA_FSM_BYP_TSADC_EN_MASK 0x80
+#define WSA883X_PA_FSM_BYP_TSADC_EN_SHIFT 7
+#define WSA883X_PA_FSM_DBG (WSA883X_DIG_CTRL_BASE + 0x0017)
+#define WSA883X_TADC_VALUE_CTL (WSA883X_DIG_CTRL_BASE + 0x0020)
+#define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01
+#define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0
+#define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02
+#define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1
+#define WSA883X_TEMP_DETECT_CTL (WSA883X_DIG_CTRL_BASE + 0x0021)
+#define WSA883X_TEMP_MSB (WSA883X_DIG_CTRL_BASE + 0x0022)
+#define WSA883X_TEMP_LSB (WSA883X_DIG_CTRL_BASE + 0x0023)
+#define WSA883X_TEMP_CONFIG0 (WSA883X_DIG_CTRL_BASE + 0x0024)
+#define WSA883X_TEMP_CONFIG1 (WSA883X_DIG_CTRL_BASE + 0x0025)
+#define WSA883X_VBAT_ADC_FLT_CTL (WSA883X_DIG_CTRL_BASE + 0x0026)
+#define WSA883X_VBAT_ADC_FLT_EN_MASK BIT(0)
+#define WSA883X_VBAT_ADC_COEF_SEL_MASK GENMASK(3, 1)
+#define WSA883X_VBAT_ADC_COEF_F_1DIV2 0x0
+#define WSA883X_VBAT_ADC_COEF_F_1DIV16 0x3
+#define WSA883X_VBAT_DIN_MSB (WSA883X_DIG_CTRL_BASE + 0x0027)
+#define WSA883X_VBAT_DIN_LSB (WSA883X_DIG_CTRL_BASE + 0x0028)
+#define WSA883X_VBAT_DOUT (WSA883X_DIG_CTRL_BASE + 0x0029)
+#define WSA883X_SDM_PDM9_LSB (WSA883X_DIG_CTRL_BASE + 0x002A)
+#define WSA883X_SDM_PDM9_MSB (WSA883X_DIG_CTRL_BASE + 0x002B)
+#define WSA883X_CDC_RX_CTL (WSA883X_DIG_CTRL_BASE + 0x0030)
+#define WSA883X_CDC_SPK_DSM_A1_0 (WSA883X_DIG_CTRL_BASE + 0x0031)
+#define WSA883X_CDC_SPK_DSM_A1_1 (WSA883X_DIG_CTRL_BASE + 0x0032)
+#define WSA883X_CDC_SPK_DSM_A2_0 (WSA883X_DIG_CTRL_BASE + 0x0033)
+#define WSA883X_CDC_SPK_DSM_A2_1 (WSA883X_DIG_CTRL_BASE + 0x0034)
+#define WSA883X_CDC_SPK_DSM_A3_0 (WSA883X_DIG_CTRL_BASE + 0x0035)
+#define WSA883X_CDC_SPK_DSM_A3_1 (WSA883X_DIG_CTRL_BASE + 0x0036)
+#define WSA883X_CDC_SPK_DSM_A4_0 (WSA883X_DIG_CTRL_BASE + 0x0037)
+#define WSA883X_CDC_SPK_DSM_A4_1 (WSA883X_DIG_CTRL_BASE + 0x0038)
+#define WSA883X_CDC_SPK_DSM_A5_0 (WSA883X_DIG_CTRL_BASE + 0x0039)
+#define WSA883X_CDC_SPK_DSM_A5_1 (WSA883X_DIG_CTRL_BASE + 0x003A)
+#define WSA883X_CDC_SPK_DSM_A6_0 (WSA883X_DIG_CTRL_BASE + 0x003B)
+#define WSA883X_CDC_SPK_DSM_A7_0 (WSA883X_DIG_CTRL_BASE + 0x003C)
+#define WSA883X_CDC_SPK_DSM_C_0 (WSA883X_DIG_CTRL_BASE + 0x003D)
+#define WSA883X_CDC_SPK_DSM_C_1 (WSA883X_DIG_CTRL_BASE + 0x003E)
+#define WSA883X_CDC_SPK_DSM_C_2 (WSA883X_DIG_CTRL_BASE + 0x003F)
+#define WSA883X_CDC_SPK_DSM_C_3 (WSA883X_DIG_CTRL_BASE + 0x0040)
+#define WSA883X_CDC_SPK_DSM_R1 (WSA883X_DIG_CTRL_BASE + 0x0041)
+#define WSA883X_CDC_SPK_DSM_R2 (WSA883X_DIG_CTRL_BASE + 0x0042)
+#define WSA883X_CDC_SPK_DSM_R3 (WSA883X_DIG_CTRL_BASE + 0x0043)
+#define WSA883X_CDC_SPK_DSM_R4 (WSA883X_DIG_CTRL_BASE + 0x0044)
+#define WSA883X_CDC_SPK_DSM_R5 (WSA883X_DIG_CTRL_BASE + 0x0045)
+#define WSA883X_CDC_SPK_DSM_R6 (WSA883X_DIG_CTRL_BASE + 0x0046)
+#define WSA883X_CDC_SPK_DSM_R7 (WSA883X_DIG_CTRL_BASE + 0x0047)
+#define WSA883X_CDC_SPK_GAIN_PDM_0 (WSA883X_DIG_CTRL_BASE + 0x0048)
+#define WSA883X_CDC_SPK_GAIN_PDM_1 (WSA883X_DIG_CTRL_BASE + 0x0049)
+#define WSA883X_CDC_SPK_GAIN_PDM_2 (WSA883X_DIG_CTRL_BASE + 0x004A)
+#define WSA883X_PDM_WD_CTL (WSA883X_DIG_CTRL_BASE + 0x004B)
+#define WSA883X_PDM_EN_MASK BIT(0)
+#define WSA883X_PDM_ENABLE BIT(0)
+#define WSA883X_DEM_BYPASS_DATA0 (WSA883X_DIG_CTRL_BASE + 0x004C)
+#define WSA883X_DEM_BYPASS_DATA1 (WSA883X_DIG_CTRL_BASE + 0x004D)
+#define WSA883X_DEM_BYPASS_DATA2 (WSA883X_DIG_CTRL_BASE + 0x004E)
+#define WSA883X_DEM_BYPASS_DATA3 (WSA883X_DIG_CTRL_BASE + 0x004F)
+#define WSA883X_WAVG_CTL (WSA883X_DIG_CTRL_BASE + 0x0050)
+#define WSA883X_WAVG_LRA_PER_0 (WSA883X_DIG_CTRL_BASE + 0x0051)
+#define WSA883X_WAVG_LRA_PER_1 (WSA883X_DIG_CTRL_BASE + 0x0052)
+#define WSA883X_WAVG_DELTA_THETA_0 (WSA883X_DIG_CTRL_BASE + 0x0053)
+#define WSA883X_WAVG_DELTA_THETA_1 (WSA883X_DIG_CTRL_BASE + 0x0054)
+#define WSA883X_WAVG_DIRECT_AMP_0 (WSA883X_DIG_CTRL_BASE + 0x0055)
+#define WSA883X_WAVG_DIRECT_AMP_1 (WSA883X_DIG_CTRL_BASE + 0x0056)
+#define WSA883X_WAVG_PTRN_AMP0_0 (WSA883X_DIG_CTRL_BASE + 0x0057)
+#define WSA883X_WAVG_PTRN_AMP0_1 (WSA883X_DIG_CTRL_BASE + 0x0058)
+#define WSA883X_WAVG_PTRN_AMP1_0 (WSA883X_DIG_CTRL_BASE + 0x0059)
+#define WSA883X_WAVG_PTRN_AMP1_1 (WSA883X_DIG_CTRL_BASE + 0x005A)
+#define WSA883X_WAVG_PTRN_AMP2_0 (WSA883X_DIG_CTRL_BASE + 0x005B)
+#define WSA883X_WAVG_PTRN_AMP2_1 (WSA883X_DIG_CTRL_BASE + 0x005C)
+#define WSA883X_WAVG_PTRN_AMP3_0 (WSA883X_DIG_CTRL_BASE + 0x005D)
+#define WSA883X_WAVG_PTRN_AMP3_1 (WSA883X_DIG_CTRL_BASE + 0x005E)
+#define WSA883X_WAVG_PTRN_AMP4_0 (WSA883X_DIG_CTRL_BASE + 0x005F)
+#define WSA883X_WAVG_PTRN_AMP4_1 (WSA883X_DIG_CTRL_BASE + 0x0060)
+#define WSA883X_WAVG_PTRN_AMP5_0 (WSA883X_DIG_CTRL_BASE + 0x0061)
+#define WSA883X_WAVG_PTRN_AMP5_1 (WSA883X_DIG_CTRL_BASE + 0x0062)
+#define WSA883X_WAVG_PTRN_AMP6_0 (WSA883X_DIG_CTRL_BASE + 0x0063)
+#define WSA883X_WAVG_PTRN_AMP6_1 (WSA883X_DIG_CTRL_BASE + 0x0064)
+#define WSA883X_WAVG_PTRN_AMP7_0 (WSA883X_DIG_CTRL_BASE + 0x0065)
+#define WSA883X_WAVG_PTRN_AMP7_1 (WSA883X_DIG_CTRL_BASE + 0x0066)
+#define WSA883X_WAVG_PER_0_1 (WSA883X_DIG_CTRL_BASE + 0x0067)
+#define WSA883X_WAVG_PER_2_3 (WSA883X_DIG_CTRL_BASE + 0x0068)
+#define WSA883X_WAVG_PER_4_5 (WSA883X_DIG_CTRL_BASE + 0x0069)
+#define WSA883X_WAVG_PER_6_7 (WSA883X_DIG_CTRL_BASE + 0x006A)
+#define WSA883X_WAVG_STA (WSA883X_DIG_CTRL_BASE + 0x006B)
+#define WSA883X_DRE_CTL_0 (WSA883X_DIG_CTRL_BASE + 0x006C)
+#define WSA883X_DRE_OFFSET_MASK GENMASK(2, 0)
+#define WSA883X_DRE_PROG_DELAY_MASK GENMASK(7, 4)
+#define WSA883X_DRE_CTL_1 (WSA883X_DIG_CTRL_BASE + 0x006D)
+#define WSA883X_DRE_GAIN_EN_MASK BIT(0)
+#define WSA883X_DRE_GAIN_FROM_CSR 1
+#define WSA883X_DRE_IDLE_DET_CTL (WSA883X_DIG_CTRL_BASE + 0x006E)
+#define WSA883X_CLSH_CTL_0 (WSA883X_DIG_CTRL_BASE + 0x0070)
+#define WSA883X_CLSH_CTL_1 (WSA883X_DIG_CTRL_BASE + 0x0071)
+#define WSA883X_CLSH_V_HD_PA (WSA883X_DIG_CTRL_BASE + 0x0072)
+#define WSA883X_CLSH_V_PA_MIN (WSA883X_DIG_CTRL_BASE + 0x0073)
+#define WSA883X_CLSH_OVRD_VAL (WSA883X_DIG_CTRL_BASE + 0x0074)
+#define WSA883X_CLSH_HARD_MAX (WSA883X_DIG_CTRL_BASE + 0x0075)
+#define WSA883X_CLSH_SOFT_MAX (WSA883X_DIG_CTRL_BASE + 0x0076)
+#define WSA883X_CLSH_SIG_DP (WSA883X_DIG_CTRL_BASE + 0x0077)
+#define WSA883X_TAGC_CTL (WSA883X_DIG_CTRL_BASE + 0x0078)
+#define WSA883X_TAGC_TIME (WSA883X_DIG_CTRL_BASE + 0x0079)
+#define WSA883X_TAGC_E2E_GAIN (WSA883X_DIG_CTRL_BASE + 0x007A)
+#define WSA883X_TAGC_FORCE_VAL (WSA883X_DIG_CTRL_BASE + 0x007B)
+#define WSA883X_VAGC_CTL (WSA883X_DIG_CTRL_BASE + 0x007C)
+#define WSA883X_VAGC_TIME (WSA883X_DIG_CTRL_BASE + 0x007D)
+#define WSA883X_VAGC_ATTN_LVL_1_2 (WSA883X_DIG_CTRL_BASE + 0x007E)
+#define WSA883X_VAGC_ATTN_LVL_3 (WSA883X_DIG_CTRL_BASE + 0x007F)
+#define WSA883X_INTR_MODE (WSA883X_DIG_CTRL_BASE + 0x0080)
+#define WSA883X_INTR_MASK0 (WSA883X_DIG_CTRL_BASE + 0x0081)
+#define WSA883X_INTR_MASK1 (WSA883X_DIG_CTRL_BASE + 0x0082)
+#define WSA883X_INTR_STATUS0 (WSA883X_DIG_CTRL_BASE + 0x0083)
+#define WSA883X_INTR_STATUS1 (WSA883X_DIG_CTRL_BASE + 0x0084)
+#define WSA883X_INTR_CLEAR0 (WSA883X_DIG_CTRL_BASE + 0x0085)
+#define WSA883X_INTR_CLEAR1 (WSA883X_DIG_CTRL_BASE + 0x0086)
+#define WSA883X_INTR_LEVEL0 (WSA883X_DIG_CTRL_BASE + 0x0087)
+#define WSA883X_INTR_LEVEL1 (WSA883X_DIG_CTRL_BASE + 0x0088)
+#define WSA883X_INTR_SET0 (WSA883X_DIG_CTRL_BASE + 0x0089)
+#define WSA883X_INTR_SET1 (WSA883X_DIG_CTRL_BASE + 0x008A)
+#define WSA883X_INTR_TEST0 (WSA883X_DIG_CTRL_BASE + 0x008B)
+#define WSA883X_INTR_TEST1 (WSA883X_DIG_CTRL_BASE + 0x008C)
+#define WSA883X_OTP_CTRL0 (WSA883X_DIG_CTRL_BASE + 0x0090)
+#define WSA883X_OTP_CTRL1 (WSA883X_DIG_CTRL_BASE + 0x0091)
+#define WSA883X_HDRIVE_CTL_GROUP1 (WSA883X_DIG_CTRL_BASE + 0x0092)
+#define WSA883X_PIN_CTL (WSA883X_DIG_CTRL_BASE + 0x0093)
+#define WSA883X_PIN_CTL_OE (WSA883X_DIG_CTRL_BASE + 0x0094)
+#define WSA883X_PIN_WDATA_IOPAD (WSA883X_DIG_CTRL_BASE + 0x0095)
+#define WSA883X_PIN_STATUS (WSA883X_DIG_CTRL_BASE + 0x0096)
+#define WSA883X_I2C_SLAVE_CTL (WSA883X_DIG_CTRL_BASE + 0x0097)
+#define WSA883X_PDM_TEST_MODE (WSA883X_DIG_CTRL_BASE + 0x00A0)
+#define WSA883X_ATE_TEST_MODE (WSA883X_DIG_CTRL_BASE + 0x00A1)
+#define WSA883X_DIG_DEBUG_MODE (WSA883X_DIG_CTRL_BASE + 0x00A3)
+#define WSA883X_DIG_DEBUG_SEL (WSA883X_DIG_CTRL_BASE + 0x00A4)
+#define WSA883X_DIG_DEBUG_EN (WSA883X_DIG_CTRL_BASE + 0x00A5)
+#define WSA883X_SWR_HM_TEST0 (WSA883X_DIG_CTRL_BASE + 0x00A6)
+#define WSA883X_SWR_HM_TEST1 (WSA883X_DIG_CTRL_BASE + 0x00A7)
+#define WSA883X_SWR_PAD_CTL (WSA883X_DIG_CTRL_BASE + 0x00A8)
+#define WSA883X_TADC_DETECT_DBG_CTL (WSA883X_DIG_CTRL_BASE + 0x00A9)
+#define WSA883X_TADC_DEBUG_MSB (WSA883X_DIG_CTRL_BASE + 0x00AA)
+#define WSA883X_TADC_DEBUG_LSB (WSA883X_DIG_CTRL_BASE + 0x00AB)
+#define WSA883X_SAMPLE_EDGE_SEL (WSA883X_DIG_CTRL_BASE + 0x00AC)
+#define WSA883X_SWR_EDGE_SEL (WSA883X_DIG_CTRL_BASE + 0x00AD)
+#define WSA883X_TEST_MODE_CTL (WSA883X_DIG_CTRL_BASE + 0x00AE)
+#define WSA883X_IOPAD_CTL (WSA883X_DIG_CTRL_BASE + 0x00AF)
+#define WSA883X_ANA_CSR_DBG_ADD (WSA883X_DIG_CTRL_BASE + 0x00B0)
+#define WSA883X_ANA_CSR_DBG_CTL (WSA883X_DIG_CTRL_BASE + 0x00B1)
+#define WSA883X_SPARE_R (WSA883X_DIG_CTRL_BASE + 0x00BC)
+#define WSA883X_SPARE_0 (WSA883X_DIG_CTRL_BASE + 0x00BD)
+#define WSA883X_SPARE_1 (WSA883X_DIG_CTRL_BASE + 0x00BE)
+#define WSA883X_SPARE_2 (WSA883X_DIG_CTRL_BASE + 0x00BF)
+#define WSA883X_SCODE (WSA883X_DIG_CTRL_BASE + 0x00C0)
+
+#define WSA883X_DIG_TRIM_BASE (WSA883X_BASE + 0x00000500)
+#define WSA883X_OTP_REG_0 (WSA883X_DIG_TRIM_BASE + 0x0080)
+#define WSA883X_ID_MASK GENMASK(3, 0)
+#define WSA883X_OTP_REG_1 (WSA883X_DIG_TRIM_BASE + 0x0081)
+#define WSA883X_OTP_REG_2 (WSA883X_DIG_TRIM_BASE + 0x0082)
+#define WSA883X_OTP_REG_3 (WSA883X_DIG_TRIM_BASE + 0x0083)
+#define WSA883X_OTP_REG_4 (WSA883X_DIG_TRIM_BASE + 0x0084)
+#define WSA883X_OTP_REG_5 (WSA883X_DIG_TRIM_BASE + 0x0085)
+#define WSA883X_OTP_REG_6 (WSA883X_DIG_TRIM_BASE + 0x0086)
+#define WSA883X_OTP_REG_7 (WSA883X_DIG_TRIM_BASE + 0x0087)
+#define WSA883X_OTP_REG_8 (WSA883X_DIG_TRIM_BASE + 0x0088)
+#define WSA883X_OTP_REG_9 (WSA883X_DIG_TRIM_BASE + 0x0089)
+#define WSA883X_OTP_REG_10 (WSA883X_DIG_TRIM_BASE + 0x008A)
+#define WSA883X_OTP_REG_11 (WSA883X_DIG_TRIM_BASE + 0x008B)
+#define WSA883X_OTP_REG_12 (WSA883X_DIG_TRIM_BASE + 0x008C)
+#define WSA883X_OTP_REG_13 (WSA883X_DIG_TRIM_BASE + 0x008D)
+#define WSA883X_OTP_REG_14 (WSA883X_DIG_TRIM_BASE + 0x008E)
+#define WSA883X_OTP_REG_15 (WSA883X_DIG_TRIM_BASE + 0x008F)
+#define WSA883X_OTP_REG_16 (WSA883X_DIG_TRIM_BASE + 0x0090)
+#define WSA883X_OTP_REG_17 (WSA883X_DIG_TRIM_BASE + 0x0091)
+#define WSA883X_OTP_REG_18 (WSA883X_DIG_TRIM_BASE + 0x0092)
+#define WSA883X_OTP_REG_19 (WSA883X_DIG_TRIM_BASE + 0x0093)
+#define WSA883X_OTP_REG_20 (WSA883X_DIG_TRIM_BASE + 0x0094)
+#define WSA883X_OTP_REG_21 (WSA883X_DIG_TRIM_BASE + 0x0095)
+#define WSA883X_OTP_REG_22 (WSA883X_DIG_TRIM_BASE + 0x0096)
+#define WSA883X_OTP_REG_23 (WSA883X_DIG_TRIM_BASE + 0x0097)
+#define WSA883X_OTP_REG_24 (WSA883X_DIG_TRIM_BASE + 0x0098)
+#define WSA883X_OTP_REG_25 (WSA883X_DIG_TRIM_BASE + 0x0099)
+#define WSA883X_OTP_REG_26 (WSA883X_DIG_TRIM_BASE + 0x009A)
+#define WSA883X_OTP_REG_27 (WSA883X_DIG_TRIM_BASE + 0x009B)
+#define WSA883X_OTP_REG_28 (WSA883X_DIG_TRIM_BASE + 0x009C)
+#define WSA883X_OTP_REG_29 (WSA883X_DIG_TRIM_BASE + 0x009D)
+#define WSA883X_OTP_REG_30 (WSA883X_DIG_TRIM_BASE + 0x009E)
+#define WSA883X_OTP_REG_31 (WSA883X_DIG_TRIM_BASE + 0x009F)
+#define WSA883X_OTP_REG_32 (WSA883X_DIG_TRIM_BASE + 0x00A0)
+#define WSA883X_OTP_REG_33 (WSA883X_DIG_TRIM_BASE + 0x00A1)
+#define WSA883X_OTP_REG_34 (WSA883X_DIG_TRIM_BASE + 0x00A2)
+#define WSA883X_OTP_REG_35 (WSA883X_DIG_TRIM_BASE + 0x00A3)
+#define WSA883X_OTP_REG_63 (WSA883X_DIG_TRIM_BASE + 0x00BF)
+
+#define WSA883X_DIG_EMEM_BASE (WSA883X_BASE + 0x000005C0)
+#define WSA883X_EMEM_0 (WSA883X_DIG_EMEM_BASE + 0x0000)
+#define WSA883X_EMEM_1 (WSA883X_DIG_EMEM_BASE + 0x0001)
+#define WSA883X_EMEM_2 (WSA883X_DIG_EMEM_BASE + 0x0002)
+#define WSA883X_EMEM_3 (WSA883X_DIG_EMEM_BASE + 0x0003)
+#define WSA883X_EMEM_4 (WSA883X_DIG_EMEM_BASE + 0x0004)
+#define WSA883X_EMEM_5 (WSA883X_DIG_EMEM_BASE + 0x0005)
+#define WSA883X_EMEM_6 (WSA883X_DIG_EMEM_BASE + 0x0006)
+#define WSA883X_EMEM_7 (WSA883X_DIG_EMEM_BASE + 0x0007)
+#define WSA883X_EMEM_8 (WSA883X_DIG_EMEM_BASE + 0x0008)
+#define WSA883X_EMEM_9 (WSA883X_DIG_EMEM_BASE + 0x0009)
+#define WSA883X_EMEM_10 (WSA883X_DIG_EMEM_BASE + 0x000A)
+#define WSA883X_EMEM_11 (WSA883X_DIG_EMEM_BASE + 0x000B)
+#define WSA883X_EMEM_12 (WSA883X_DIG_EMEM_BASE + 0x000C)
+#define WSA883X_EMEM_13 (WSA883X_DIG_EMEM_BASE + 0x000D)
+#define WSA883X_EMEM_14 (WSA883X_DIG_EMEM_BASE + 0x000E)
+#define WSA883X_EMEM_15 (WSA883X_DIG_EMEM_BASE + 0x000F)
+#define WSA883X_EMEM_16 (WSA883X_DIG_EMEM_BASE + 0x0010)
+#define WSA883X_EMEM_17 (WSA883X_DIG_EMEM_BASE + 0x0011)
+#define WSA883X_EMEM_18 (WSA883X_DIG_EMEM_BASE + 0x0012)
+#define WSA883X_EMEM_19 (WSA883X_DIG_EMEM_BASE + 0x0013)
+#define WSA883X_EMEM_20 (WSA883X_DIG_EMEM_BASE + 0x0014)
+#define WSA883X_EMEM_21 (WSA883X_DIG_EMEM_BASE + 0x0015)
+#define WSA883X_EMEM_22 (WSA883X_DIG_EMEM_BASE + 0x0016)
+#define WSA883X_EMEM_23 (WSA883X_DIG_EMEM_BASE + 0x0017)
+#define WSA883X_EMEM_24 (WSA883X_DIG_EMEM_BASE + 0x0018)
+#define WSA883X_EMEM_25 (WSA883X_DIG_EMEM_BASE + 0x0019)
+#define WSA883X_EMEM_26 (WSA883X_DIG_EMEM_BASE + 0x001A)
+#define WSA883X_EMEM_27 (WSA883X_DIG_EMEM_BASE + 0x001B)
+#define WSA883X_EMEM_28 (WSA883X_DIG_EMEM_BASE + 0x001C)
+#define WSA883X_EMEM_29 (WSA883X_DIG_EMEM_BASE + 0x001D)
+#define WSA883X_EMEM_30 (WSA883X_DIG_EMEM_BASE + 0x001E)
+#define WSA883X_EMEM_31 (WSA883X_DIG_EMEM_BASE + 0x001F)
+#define WSA883X_EMEM_32 (WSA883X_DIG_EMEM_BASE + 0x0020)
+#define WSA883X_EMEM_33 (WSA883X_DIG_EMEM_BASE + 0x0021)
+#define WSA883X_EMEM_34 (WSA883X_DIG_EMEM_BASE + 0x0022)
+#define WSA883X_EMEM_35 (WSA883X_DIG_EMEM_BASE + 0x0023)
+#define WSA883X_EMEM_36 (WSA883X_DIG_EMEM_BASE + 0x0024)
+#define WSA883X_EMEM_37 (WSA883X_DIG_EMEM_BASE + 0x0025)
+#define WSA883X_EMEM_38 (WSA883X_DIG_EMEM_BASE + 0x0026)
+#define WSA883X_EMEM_39 (WSA883X_DIG_EMEM_BASE + 0x0027)
+#define WSA883X_EMEM_40 (WSA883X_DIG_EMEM_BASE + 0x0028)
+#define WSA883X_EMEM_41 (WSA883X_DIG_EMEM_BASE + 0x0029)
+#define WSA883X_EMEM_42 (WSA883X_DIG_EMEM_BASE + 0x002A)
+#define WSA883X_EMEM_43 (WSA883X_DIG_EMEM_BASE + 0x002B)
+#define WSA883X_EMEM_44 (WSA883X_DIG_EMEM_BASE + 0x002C)
+#define WSA883X_EMEM_45 (WSA883X_DIG_EMEM_BASE + 0x002D)
+#define WSA883X_EMEM_46 (WSA883X_DIG_EMEM_BASE + 0x002E)
+#define WSA883X_EMEM_47 (WSA883X_DIG_EMEM_BASE + 0x002F)
+#define WSA883X_EMEM_48 (WSA883X_DIG_EMEM_BASE + 0x0030)
+#define WSA883X_EMEM_49 (WSA883X_DIG_EMEM_BASE + 0x0031)
+#define WSA883X_EMEM_50 (WSA883X_DIG_EMEM_BASE + 0x0032)
+#define WSA883X_EMEM_51 (WSA883X_DIG_EMEM_BASE + 0x0033)
+#define WSA883X_EMEM_52 (WSA883X_DIG_EMEM_BASE + 0x0034)
+#define WSA883X_EMEM_53 (WSA883X_DIG_EMEM_BASE + 0x0035)
+#define WSA883X_EMEM_54 (WSA883X_DIG_EMEM_BASE + 0x0036)
+#define WSA883X_EMEM_55 (WSA883X_DIG_EMEM_BASE + 0x0037)
+#define WSA883X_EMEM_56 (WSA883X_DIG_EMEM_BASE + 0x0038)
+#define WSA883X_EMEM_57 (WSA883X_DIG_EMEM_BASE + 0x0039)
+#define WSA883X_EMEM_58 (WSA883X_DIG_EMEM_BASE + 0x003A)
+#define WSA883X_EMEM_59 (WSA883X_DIG_EMEM_BASE + 0x003B)
+#define WSA883X_EMEM_60 (WSA883X_DIG_EMEM_BASE + 0x003C)
+#define WSA883X_EMEM_61 (WSA883X_DIG_EMEM_BASE + 0x003D)
+#define WSA883X_EMEM_62 (WSA883X_DIG_EMEM_BASE + 0x003E)
+#define WSA883X_EMEM_63 (WSA883X_DIG_EMEM_BASE + 0x003F)
+
+#define WSA883X_NUM_REGISTERS (WSA883X_EMEM_63 + 1)
+#define WSA883X_MAX_REGISTER (WSA883X_NUM_REGISTERS - 1)
+
+#define WSA883X_VERSION_1_0 0
+#define WSA883X_VERSION_1_1 1
+
+#define WSA883X_MAX_SWR_PORTS 4
+#define WSA883X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define WSA883X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define WSA883X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Two-point trimming for temperature calibration */
+#define WSA883X_T1_TEMP -10L
+#define WSA883X_T2_TEMP 150L
+
+/*
+ * Device will report senseless data in many cases, so discard any measurements
+ * outside of valid range.
+ */
+#define WSA883X_LOW_TEMP_THRESHOLD 5
+#define WSA883X_HIGH_TEMP_THRESHOLD 45
+
+struct wsa883x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator *vdd;
+ struct sdw_slave *slave;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WSA883X_MAX_SWR_PORTS];
+ struct gpio_desc *sd_n;
+ struct reset_control *sd_reset;
+ bool port_prepared[WSA883X_MAX_SWR_PORTS];
+ bool port_enable[WSA883X_MAX_SWR_PORTS];
+ int active_ports;
+ int dev_mode;
+ int comp_offset;
+ /*
+ * Protects temperature reading code (related to speaker protection) and
+ * fields: temperature and pa_on.
+ */
+ struct mutex sp_lock;
+ unsigned int temperature;
+ bool pa_on;
+};
+
+enum {
+ WSA8830 = 0,
+ WSA8835,
+ WSA8832,
+ WSA8835_V2 = 5,
+};
+
+enum {
+ COMP_OFFSET0,
+ COMP_OFFSET1,
+ COMP_OFFSET2,
+ COMP_OFFSET3,
+ COMP_OFFSET4,
+};
+
+enum wsa_port_ids {
+ WSA883X_PORT_DAC,
+ WSA883X_PORT_COMP,
+ WSA883X_PORT_BOOST,
+ WSA883X_PORT_VISENSE,
+};
+
+static const char * const wsa_dev_mode_text[] = {
+ "Speaker", "Receiver", "Ultrasound"
+};
+
+enum {
+ SPEAKER,
+ RECEIVER,
+ ULTRASOUND,
+};
+
+static const struct soc_enum wsa_dev_mode_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_dev_mode_text), wsa_dev_mode_text);
+
+/* 4 ports */
+static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = {
+ [WSA883X_PORT_DAC] = {
+ .num = WSA883X_PORT_DAC + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA883X_PORT_COMP] = {
+ .num = WSA883X_PORT_COMP + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA883X_PORT_BOOST] = {
+ .num = WSA883X_PORT_BOOST + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA883X_PORT_VISENSE] = {
+ .num = WSA883X_PORT_VISENSE + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }
+};
+
+static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = {
+ [WSA883X_PORT_DAC] = {
+ .num = WSA883X_PORT_DAC + 1,
+ .ch_mask = 0x1,
+ },
+ [WSA883X_PORT_COMP] = {
+ .num = WSA883X_PORT_COMP + 1,
+ .ch_mask = 0xf,
+ },
+ [WSA883X_PORT_BOOST] = {
+ .num = WSA883X_PORT_BOOST + 1,
+ .ch_mask = 0x3,
+ },
+ [WSA883X_PORT_VISENSE] = {
+ .num = WSA883X_PORT_VISENSE + 1,
+ .ch_mask = 0x1,
+ },
+};
+
+static const struct reg_default wsa883x_defaults[] = {
+ { WSA883X_REF_CTRL, 0xD5 },
+ { WSA883X_TEST_CTL_0, 0x06 },
+ { WSA883X_BIAS_0, 0xD2 },
+ { WSA883X_OP_CTL, 0xE0 },
+ { WSA883X_IREF_CTL, 0x57 },
+ { WSA883X_ISENS_CTL, 0x47 },
+ { WSA883X_CLK_CTL, 0x87 },
+ { WSA883X_TEST_CTL_1, 0x00 },
+ { WSA883X_BIAS_1, 0x51 },
+ { WSA883X_ADC_CTL, 0x01 },
+ { WSA883X_DOUT_MSB, 0x00 },
+ { WSA883X_DOUT_LSB, 0x00 },
+ { WSA883X_VBAT_SNS, 0x40 },
+ { WSA883X_ITRIM_CODE, 0x9F },
+ { WSA883X_EN, 0x20 },
+ { WSA883X_OVERRIDE1, 0x00 },
+ { WSA883X_OVERRIDE2, 0x08 },
+ { WSA883X_VSENSE1, 0xD3 },
+ { WSA883X_ISENSE1, 0xD4 },
+ { WSA883X_ISENSE2, 0x20 },
+ { WSA883X_ISENSE_CAL, 0x00 },
+ { WSA883X_MISC, 0x08 },
+ { WSA883X_ADC_0, 0x00 },
+ { WSA883X_ADC_1, 0x00 },
+ { WSA883X_ADC_2, 0x40 },
+ { WSA883X_ADC_3, 0x80 },
+ { WSA883X_ADC_4, 0x25 },
+ { WSA883X_ADC_5, 0x25 },
+ { WSA883X_ADC_6, 0x08 },
+ { WSA883X_ADC_7, 0x81 },
+ { WSA883X_STATUS, 0x00 },
+ { WSA883X_DAC_CTRL_REG, 0x53 },
+ { WSA883X_DAC_EN_DEBUG_REG, 0x00 },
+ { WSA883X_DAC_OPAMP_BIAS1_REG, 0x48 },
+ { WSA883X_DAC_OPAMP_BIAS2_REG, 0x48 },
+ { WSA883X_DAC_VCM_CTRL_REG, 0x88 },
+ { WSA883X_DAC_VOLTAGE_CTRL_REG, 0xA5 },
+ { WSA883X_ATEST1_REG, 0x00 },
+ { WSA883X_ATEST2_REG, 0x00 },
+ { WSA883X_SPKR_TOP_BIAS_REG1, 0x6A },
+ { WSA883X_SPKR_TOP_BIAS_REG2, 0x65 },
+ { WSA883X_SPKR_TOP_BIAS_REG3, 0x55 },
+ { WSA883X_SPKR_TOP_BIAS_REG4, 0xA9 },
+ { WSA883X_SPKR_CLIP_DET_REG, 0x9C },
+ { WSA883X_SPKR_DRV_LF_BLK_EN, 0x0F },
+ { WSA883X_SPKR_DRV_LF_EN, 0x0A },
+ { WSA883X_SPKR_DRV_LF_MASK_DCC_CTL, 0x00 },
+ { WSA883X_SPKR_DRV_LF_MISC_CTL, 0x3A },
+ { WSA883X_SPKR_DRV_LF_REG_GAIN, 0x00 },
+ { WSA883X_SPKR_DRV_OS_CAL_CTL, 0x00 },
+ { WSA883X_SPKR_DRV_OS_CAL_CTL1, 0x90 },
+ { WSA883X_SPKR_PWM_CLK_CTL, 0x00 },
+ { WSA883X_SPKR_PDRV_HS_CTL, 0x52 },
+ { WSA883X_SPKR_PDRV_LS_CTL, 0x48 },
+ { WSA883X_SPKR_PWRSTG_DBG, 0x08 },
+ { WSA883X_SPKR_OCP_CTL, 0xE2 },
+ { WSA883X_SPKR_BBM_CTL, 0x92 },
+ { WSA883X_PA_STATUS0, 0x00 },
+ { WSA883X_PA_STATUS1, 0x00 },
+ { WSA883X_PA_STATUS2, 0x80 },
+ { WSA883X_EN_CTRL, 0x44 },
+ { WSA883X_CURRENT_LIMIT, 0xCC },
+ { WSA883X_IBIAS1, 0x00 },
+ { WSA883X_IBIAS2, 0x00 },
+ { WSA883X_IBIAS3, 0x00 },
+ { WSA883X_LDO_PROG, 0x02 },
+ { WSA883X_STABILITY_CTRL1, 0x8E },
+ { WSA883X_STABILITY_CTRL2, 0x10 },
+ { WSA883X_PWRSTAGE_CTRL1, 0x06 },
+ { WSA883X_PWRSTAGE_CTRL2, 0x00 },
+ { WSA883X_BYPASS_1, 0x19 },
+ { WSA883X_BYPASS_2, 0x13 },
+ { WSA883X_ZX_CTRL_1, 0xF0 },
+ { WSA883X_ZX_CTRL_2, 0x04 },
+ { WSA883X_MISC1, 0x06 },
+ { WSA883X_MISC2, 0xA0 },
+ { WSA883X_GMAMP_SUP1, 0x82 },
+ { WSA883X_PWRSTAGE_CTRL3, 0x39 },
+ { WSA883X_PWRSTAGE_CTRL4, 0x5F },
+ { WSA883X_TEST1, 0x00 },
+ { WSA883X_SPARE1, 0x00 },
+ { WSA883X_SPARE2, 0x00 },
+ { WSA883X_PON_CTL_0, 0x10 },
+ { WSA883X_PON_CLT_1, 0xE0 },
+ { WSA883X_PON_CTL_2, 0x90 },
+ { WSA883X_PON_CTL_3, 0x70 },
+ { WSA883X_CKWD_CTL_0, 0x34 },
+ { WSA883X_CKWD_CTL_1, 0x0F },
+ { WSA883X_CKWD_CTL_2, 0x00 },
+ { WSA883X_CKSK_CTL_0, 0x00 },
+ { WSA883X_PADSW_CTL_0, 0x00 },
+ { WSA883X_TEST_0, 0x00 },
+ { WSA883X_TEST_1, 0x00 },
+ { WSA883X_STATUS_0, 0x00 },
+ { WSA883X_STATUS_1, 0x00 },
+ { WSA883X_CHIP_ID0, 0x00 },
+ { WSA883X_CHIP_ID1, 0x00 },
+ { WSA883X_CHIP_ID2, 0x02 },
+ { WSA883X_CHIP_ID3, 0x02 },
+ { WSA883X_BUS_ID, 0x00 },
+ { WSA883X_CDC_RST_CTL, 0x01 },
+ { WSA883X_TOP_CLK_CFG, 0x00 },
+ { WSA883X_CDC_PATH_MODE, 0x00 },
+ { WSA883X_CDC_CLK_CTL, 0xFF },
+ { WSA883X_SWR_RESET_EN, 0x00 },
+ { WSA883X_RESET_CTL, 0x00 },
+ { WSA883X_PA_FSM_CTL, 0x00 },
+ { WSA883X_PA_FSM_TIMER0, 0x80 },
+ { WSA883X_PA_FSM_TIMER1, 0x80 },
+ { WSA883X_PA_FSM_STA, 0x00 },
+ { WSA883X_PA_FSM_ERR_COND, 0x00 },
+ { WSA883X_PA_FSM_MSK, 0x00 },
+ { WSA883X_PA_FSM_BYP, 0x01 },
+ { WSA883X_PA_FSM_DBG, 0x00 },
+ { WSA883X_TADC_VALUE_CTL, 0x03 },
+ { WSA883X_TEMP_DETECT_CTL, 0x01 },
+ { WSA883X_TEMP_MSB, 0x00 },
+ { WSA883X_TEMP_LSB, 0x00 },
+ { WSA883X_TEMP_CONFIG0, 0x00 },
+ { WSA883X_TEMP_CONFIG1, 0x00 },
+ { WSA883X_VBAT_ADC_FLT_CTL, 0x00 },
+ { WSA883X_VBAT_DIN_MSB, 0x00 },
+ { WSA883X_VBAT_DIN_LSB, 0x00 },
+ { WSA883X_VBAT_DOUT, 0x00 },
+ { WSA883X_SDM_PDM9_LSB, 0x00 },
+ { WSA883X_SDM_PDM9_MSB, 0x00 },
+ { WSA883X_CDC_RX_CTL, 0xFE },
+ { WSA883X_CDC_SPK_DSM_A1_0, 0x00 },
+ { WSA883X_CDC_SPK_DSM_A1_1, 0x01 },
+ { WSA883X_CDC_SPK_DSM_A2_0, 0x96 },
+ { WSA883X_CDC_SPK_DSM_A2_1, 0x09 },
+ { WSA883X_CDC_SPK_DSM_A3_0, 0xAB },
+ { WSA883X_CDC_SPK_DSM_A3_1, 0x05 },
+ { WSA883X_CDC_SPK_DSM_A4_0, 0x1C },
+ { WSA883X_CDC_SPK_DSM_A4_1, 0x02 },
+ { WSA883X_CDC_SPK_DSM_A5_0, 0x17 },
+ { WSA883X_CDC_SPK_DSM_A5_1, 0x02 },
+ { WSA883X_CDC_SPK_DSM_A6_0, 0xAA },
+ { WSA883X_CDC_SPK_DSM_A7_0, 0xE3 },
+ { WSA883X_CDC_SPK_DSM_C_0, 0x69 },
+ { WSA883X_CDC_SPK_DSM_C_1, 0x54 },
+ { WSA883X_CDC_SPK_DSM_C_2, 0x02 },
+ { WSA883X_CDC_SPK_DSM_C_3, 0x15 },
+ { WSA883X_CDC_SPK_DSM_R1, 0xA4 },
+ { WSA883X_CDC_SPK_DSM_R2, 0xB5 },
+ { WSA883X_CDC_SPK_DSM_R3, 0x86 },
+ { WSA883X_CDC_SPK_DSM_R4, 0x85 },
+ { WSA883X_CDC_SPK_DSM_R5, 0xAA },
+ { WSA883X_CDC_SPK_DSM_R6, 0xE2 },
+ { WSA883X_CDC_SPK_DSM_R7, 0x62 },
+ { WSA883X_CDC_SPK_GAIN_PDM_0, 0x00 },
+ { WSA883X_CDC_SPK_GAIN_PDM_1, 0xFC },
+ { WSA883X_CDC_SPK_GAIN_PDM_2, 0x05 },
+ { WSA883X_PDM_WD_CTL, 0x00 },
+ { WSA883X_DEM_BYPASS_DATA0, 0x00 },
+ { WSA883X_DEM_BYPASS_DATA1, 0x00 },
+ { WSA883X_DEM_BYPASS_DATA2, 0x00 },
+ { WSA883X_DEM_BYPASS_DATA3, 0x00 },
+ { WSA883X_WAVG_CTL, 0x06 },
+ { WSA883X_WAVG_LRA_PER_0, 0xD1 },
+ { WSA883X_WAVG_LRA_PER_1, 0x00 },
+ { WSA883X_WAVG_DELTA_THETA_0, 0xE6 },
+ { WSA883X_WAVG_DELTA_THETA_1, 0x04 },
+ { WSA883X_WAVG_DIRECT_AMP_0, 0x50 },
+ { WSA883X_WAVG_DIRECT_AMP_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP0_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP0_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP1_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP1_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP2_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP2_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP3_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP3_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP4_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP4_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP5_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP5_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP6_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP6_1, 0x00 },
+ { WSA883X_WAVG_PTRN_AMP7_0, 0x50 },
+ { WSA883X_WAVG_PTRN_AMP7_1, 0x00 },
+ { WSA883X_WAVG_PER_0_1, 0x88 },
+ { WSA883X_WAVG_PER_2_3, 0x88 },
+ { WSA883X_WAVG_PER_4_5, 0x88 },
+ { WSA883X_WAVG_PER_6_7, 0x88 },
+ { WSA883X_WAVG_STA, 0x00 },
+ { WSA883X_DRE_CTL_0, 0x70 },
+ { WSA883X_DRE_CTL_1, 0x08 },
+ { WSA883X_DRE_IDLE_DET_CTL, 0x1F },
+ { WSA883X_CLSH_CTL_0, 0x37 },
+ { WSA883X_CLSH_CTL_1, 0x81 },
+ { WSA883X_CLSH_V_HD_PA, 0x0F },
+ { WSA883X_CLSH_V_PA_MIN, 0x00 },
+ { WSA883X_CLSH_OVRD_VAL, 0x00 },
+ { WSA883X_CLSH_HARD_MAX, 0xFF },
+ { WSA883X_CLSH_SOFT_MAX, 0xF5 },
+ { WSA883X_CLSH_SIG_DP, 0x00 },
+ { WSA883X_TAGC_CTL, 0x10 },
+ { WSA883X_TAGC_TIME, 0x20 },
+ { WSA883X_TAGC_E2E_GAIN, 0x02 },
+ { WSA883X_TAGC_FORCE_VAL, 0x00 },
+ { WSA883X_VAGC_CTL, 0x00 },
+ { WSA883X_VAGC_TIME, 0x08 },
+ { WSA883X_VAGC_ATTN_LVL_1_2, 0x21 },
+ { WSA883X_VAGC_ATTN_LVL_3, 0x03 },
+ { WSA883X_INTR_MODE, 0x00 },
+ { WSA883X_INTR_MASK0, 0x90 },
+ { WSA883X_INTR_MASK1, 0x00 },
+ { WSA883X_INTR_STATUS0, 0x00 },
+ { WSA883X_INTR_STATUS1, 0x00 },
+ { WSA883X_INTR_CLEAR0, 0x00 },
+ { WSA883X_INTR_CLEAR1, 0x00 },
+ { WSA883X_INTR_LEVEL0, 0x00 },
+ { WSA883X_INTR_LEVEL1, 0x00 },
+ { WSA883X_INTR_SET0, 0x00 },
+ { WSA883X_INTR_SET1, 0x00 },
+ { WSA883X_INTR_TEST0, 0x00 },
+ { WSA883X_INTR_TEST1, 0x00 },
+ { WSA883X_OTP_CTRL0, 0x00 },
+ { WSA883X_OTP_CTRL1, 0x00 },
+ { WSA883X_HDRIVE_CTL_GROUP1, 0x00 },
+ { WSA883X_PIN_CTL, 0x04 },
+ { WSA883X_PIN_CTL_OE, 0x00 },
+ { WSA883X_PIN_WDATA_IOPAD, 0x00 },
+ { WSA883X_PIN_STATUS, 0x00 },
+ { WSA883X_I2C_SLAVE_CTL, 0x00 },
+ { WSA883X_PDM_TEST_MODE, 0x00 },
+ { WSA883X_ATE_TEST_MODE, 0x00 },
+ { WSA883X_DIG_DEBUG_MODE, 0x00 },
+ { WSA883X_DIG_DEBUG_SEL, 0x00 },
+ { WSA883X_DIG_DEBUG_EN, 0x00 },
+ { WSA883X_SWR_HM_TEST0, 0x08 },
+ { WSA883X_SWR_HM_TEST1, 0x00 },
+ { WSA883X_SWR_PAD_CTL, 0x37 },
+ { WSA883X_TADC_DETECT_DBG_CTL, 0x00 },
+ { WSA883X_TADC_DEBUG_MSB, 0x00 },
+ { WSA883X_TADC_DEBUG_LSB, 0x00 },
+ { WSA883X_SAMPLE_EDGE_SEL, 0x7F },
+ { WSA883X_SWR_EDGE_SEL, 0x00 },
+ { WSA883X_TEST_MODE_CTL, 0x04 },
+ { WSA883X_IOPAD_CTL, 0x00 },
+ { WSA883X_ANA_CSR_DBG_ADD, 0x00 },
+ { WSA883X_ANA_CSR_DBG_CTL, 0x12 },
+ { WSA883X_SPARE_R, 0x00 },
+ { WSA883X_SPARE_0, 0x00 },
+ { WSA883X_SPARE_1, 0x00 },
+ { WSA883X_SPARE_2, 0x00 },
+ { WSA883X_SCODE, 0x00 },
+ { WSA883X_OTP_REG_0, 0x05 },
+ { WSA883X_OTP_REG_1, 0xFF },
+ { WSA883X_OTP_REG_2, 0xC0 },
+ { WSA883X_OTP_REG_3, 0xFF },
+ { WSA883X_OTP_REG_4, 0xC0 },
+ { WSA883X_OTP_REG_5, 0xFF },
+ { WSA883X_OTP_REG_6, 0xFF },
+ { WSA883X_OTP_REG_7, 0xFF },
+ { WSA883X_OTP_REG_8, 0xFF },
+ { WSA883X_OTP_REG_9, 0xFF },
+ { WSA883X_OTP_REG_10, 0xFF },
+ { WSA883X_OTP_REG_11, 0xFF },
+ { WSA883X_OTP_REG_12, 0xFF },
+ { WSA883X_OTP_REG_13, 0xFF },
+ { WSA883X_OTP_REG_14, 0xFF },
+ { WSA883X_OTP_REG_15, 0xFF },
+ { WSA883X_OTP_REG_16, 0xFF },
+ { WSA883X_OTP_REG_17, 0xFF },
+ { WSA883X_OTP_REG_18, 0xFF },
+ { WSA883X_OTP_REG_19, 0xFF },
+ { WSA883X_OTP_REG_20, 0xFF },
+ { WSA883X_OTP_REG_21, 0xFF },
+ { WSA883X_OTP_REG_22, 0xFF },
+ { WSA883X_OTP_REG_23, 0xFF },
+ { WSA883X_OTP_REG_24, 0x37 },
+ { WSA883X_OTP_REG_25, 0x3F },
+ { WSA883X_OTP_REG_26, 0x03 },
+ { WSA883X_OTP_REG_27, 0x00 },
+ { WSA883X_OTP_REG_28, 0x00 },
+ { WSA883X_OTP_REG_29, 0x00 },
+ { WSA883X_OTP_REG_30, 0x00 },
+ { WSA883X_OTP_REG_31, 0x03 },
+ { WSA883X_OTP_REG_32, 0x00 },
+ { WSA883X_OTP_REG_33, 0xFF },
+ { WSA883X_OTP_REG_34, 0x00 },
+ { WSA883X_OTP_REG_35, 0x00 },
+ { WSA883X_OTP_REG_63, 0x40 },
+ { WSA883X_EMEM_0, 0x00 },
+ { WSA883X_EMEM_1, 0x00 },
+ { WSA883X_EMEM_2, 0x00 },
+ { WSA883X_EMEM_3, 0x00 },
+ { WSA883X_EMEM_4, 0x00 },
+ { WSA883X_EMEM_5, 0x00 },
+ { WSA883X_EMEM_6, 0x00 },
+ { WSA883X_EMEM_7, 0x00 },
+ { WSA883X_EMEM_8, 0x00 },
+ { WSA883X_EMEM_9, 0x00 },
+ { WSA883X_EMEM_10, 0x00 },
+ { WSA883X_EMEM_11, 0x00 },
+ { WSA883X_EMEM_12, 0x00 },
+ { WSA883X_EMEM_13, 0x00 },
+ { WSA883X_EMEM_14, 0x00 },
+ { WSA883X_EMEM_15, 0x00 },
+ { WSA883X_EMEM_16, 0x00 },
+ { WSA883X_EMEM_17, 0x00 },
+ { WSA883X_EMEM_18, 0x00 },
+ { WSA883X_EMEM_19, 0x00 },
+ { WSA883X_EMEM_20, 0x00 },
+ { WSA883X_EMEM_21, 0x00 },
+ { WSA883X_EMEM_22, 0x00 },
+ { WSA883X_EMEM_23, 0x00 },
+ { WSA883X_EMEM_24, 0x00 },
+ { WSA883X_EMEM_25, 0x00 },
+ { WSA883X_EMEM_26, 0x00 },
+ { WSA883X_EMEM_27, 0x00 },
+ { WSA883X_EMEM_28, 0x00 },
+ { WSA883X_EMEM_29, 0x00 },
+ { WSA883X_EMEM_30, 0x00 },
+ { WSA883X_EMEM_31, 0x00 },
+ { WSA883X_EMEM_32, 0x00 },
+ { WSA883X_EMEM_33, 0x00 },
+ { WSA883X_EMEM_34, 0x00 },
+ { WSA883X_EMEM_35, 0x00 },
+ { WSA883X_EMEM_36, 0x00 },
+ { WSA883X_EMEM_37, 0x00 },
+ { WSA883X_EMEM_38, 0x00 },
+ { WSA883X_EMEM_39, 0x00 },
+ { WSA883X_EMEM_40, 0x00 },
+ { WSA883X_EMEM_41, 0x00 },
+ { WSA883X_EMEM_42, 0x00 },
+ { WSA883X_EMEM_43, 0x00 },
+ { WSA883X_EMEM_44, 0x00 },
+ { WSA883X_EMEM_45, 0x00 },
+ { WSA883X_EMEM_46, 0x00 },
+ { WSA883X_EMEM_47, 0x00 },
+ { WSA883X_EMEM_48, 0x00 },
+ { WSA883X_EMEM_49, 0x00 },
+ { WSA883X_EMEM_50, 0x00 },
+ { WSA883X_EMEM_51, 0x00 },
+ { WSA883X_EMEM_52, 0x00 },
+ { WSA883X_EMEM_53, 0x00 },
+ { WSA883X_EMEM_54, 0x00 },
+ { WSA883X_EMEM_55, 0x00 },
+ { WSA883X_EMEM_56, 0x00 },
+ { WSA883X_EMEM_57, 0x00 },
+ { WSA883X_EMEM_58, 0x00 },
+ { WSA883X_EMEM_59, 0x00 },
+ { WSA883X_EMEM_60, 0x00 },
+ { WSA883X_EMEM_61, 0x00 },
+ { WSA883X_EMEM_62, 0x00 },
+ { WSA883X_EMEM_63, 0x00 },
+};
+
+static bool wsa883x_readonly_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA883X_DOUT_MSB:
+ case WSA883X_DOUT_LSB:
+ case WSA883X_STATUS:
+ case WSA883X_PA_STATUS0:
+ case WSA883X_PA_STATUS1:
+ case WSA883X_PA_STATUS2:
+ case WSA883X_STATUS_0:
+ case WSA883X_STATUS_1:
+ case WSA883X_CHIP_ID0:
+ case WSA883X_CHIP_ID1:
+ case WSA883X_CHIP_ID2:
+ case WSA883X_CHIP_ID3:
+ case WSA883X_BUS_ID:
+ case WSA883X_PA_FSM_STA:
+ case WSA883X_PA_FSM_ERR_COND:
+ case WSA883X_TEMP_MSB:
+ case WSA883X_TEMP_LSB:
+ case WSA883X_VBAT_DIN_MSB:
+ case WSA883X_VBAT_DIN_LSB:
+ case WSA883X_VBAT_DOUT:
+ case WSA883X_SDM_PDM9_LSB:
+ case WSA883X_SDM_PDM9_MSB:
+ case WSA883X_WAVG_STA:
+ case WSA883X_INTR_STATUS0:
+ case WSA883X_INTR_STATUS1:
+ case WSA883X_OTP_CTRL1:
+ case WSA883X_PIN_STATUS:
+ case WSA883X_ATE_TEST_MODE:
+ case WSA883X_SWR_HM_TEST1:
+ case WSA883X_SPARE_R:
+ case WSA883X_OTP_REG_0:
+ return true;
+ }
+ return false;
+}
+
+static bool wsa883x_writeable_register(struct device *dev, unsigned int reg)
+{
+ return !wsa883x_readonly_register(dev, reg);
+}
+
+static bool wsa883x_volatile_register(struct device *dev, unsigned int reg)
+{
+ return wsa883x_readonly_register(dev, reg);
+}
+
+static const struct regmap_config wsa883x_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wsa883x_defaults,
+ .max_register = WSA883X_MAX_REGISTER,
+ .num_reg_defaults = ARRAY_SIZE(wsa883x_defaults),
+ .volatile_reg = wsa883x_volatile_register,
+ .writeable_reg = wsa883x_writeable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .use_single_read = true,
+};
+
+static const struct reg_sequence reg_init[] = {
+ {WSA883X_PA_FSM_BYP, 0x00},
+ {WSA883X_ADC_6, 0x02},
+ {WSA883X_CDC_SPK_DSM_A2_0, 0x0A},
+ {WSA883X_CDC_SPK_DSM_A2_1, 0x08},
+ {WSA883X_CDC_SPK_DSM_A3_0, 0xF3},
+ {WSA883X_CDC_SPK_DSM_A3_1, 0x07},
+ {WSA883X_CDC_SPK_DSM_A4_0, 0x79},
+ {WSA883X_CDC_SPK_DSM_A4_1, 0x02},
+ {WSA883X_CDC_SPK_DSM_A5_0, 0x0B},
+ {WSA883X_CDC_SPK_DSM_A5_1, 0x02},
+ {WSA883X_CDC_SPK_DSM_A6_0, 0x8A},
+ {WSA883X_CDC_SPK_DSM_A7_0, 0x9B},
+ {WSA883X_CDC_SPK_DSM_C_0, 0x68},
+ {WSA883X_CDC_SPK_DSM_C_1, 0x54},
+ {WSA883X_CDC_SPK_DSM_C_2, 0xF2},
+ {WSA883X_CDC_SPK_DSM_C_3, 0x20},
+ {WSA883X_CDC_SPK_DSM_R1, 0x83},
+ {WSA883X_CDC_SPK_DSM_R2, 0x7F},
+ {WSA883X_CDC_SPK_DSM_R3, 0x9D},
+ {WSA883X_CDC_SPK_DSM_R4, 0x82},
+ {WSA883X_CDC_SPK_DSM_R5, 0x8B},
+ {WSA883X_CDC_SPK_DSM_R6, 0x9B},
+ {WSA883X_CDC_SPK_DSM_R7, 0x3F},
+ {WSA883X_VBAT_SNS, 0x20},
+ {WSA883X_DRE_CTL_0, 0x92},
+ {WSA883X_DRE_IDLE_DET_CTL, 0x0F},
+ {WSA883X_CURRENT_LIMIT, 0xC4},
+ {WSA883X_VAGC_TIME, 0x0F},
+ {WSA883X_VAGC_ATTN_LVL_1_2, 0x00},
+ {WSA883X_VAGC_ATTN_LVL_3, 0x01},
+ {WSA883X_VAGC_CTL, 0x01},
+ {WSA883X_TAGC_CTL, 0x1A},
+ {WSA883X_TAGC_TIME, 0x2C},
+ {WSA883X_TEMP_CONFIG0, 0x02},
+ {WSA883X_TEMP_CONFIG1, 0x02},
+ {WSA883X_OTP_REG_1, 0x49},
+ {WSA883X_OTP_REG_2, 0x80},
+ {WSA883X_OTP_REG_3, 0xC9},
+ {WSA883X_OTP_REG_4, 0x40},
+ {WSA883X_TAGC_CTL, 0x1B},
+ {WSA883X_ADC_2, 0x00},
+ {WSA883X_ADC_7, 0x85},
+ {WSA883X_ADC_7, 0x87},
+ {WSA883X_CKWD_CTL_0, 0x14},
+ {WSA883X_CKWD_CTL_1, 0x1B},
+ {WSA883X_GMAMP_SUP1, 0xE2},
+};
+
+static int wsa883x_init(struct wsa883x_priv *wsa883x)
+{
+ struct regmap *regmap = wsa883x->regmap;
+ int variant, version, ret;
+
+ ret = regmap_read(regmap, WSA883X_OTP_REG_0, &variant);
+ if (ret)
+ return ret;
+ variant = variant & WSA883X_ID_MASK;
+
+ ret = regmap_read(regmap, WSA883X_CHIP_ID0, &version);
+ if (ret)
+ return ret;
+
+ switch (variant) {
+ case WSA8830:
+ dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8830\n",
+ version);
+ break;
+ case WSA8835:
+ dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8835\n",
+ version);
+ break;
+ case WSA8832:
+ dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8832\n",
+ version);
+ break;
+ case WSA8835_V2:
+ dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8835_V2\n",
+ version);
+ break;
+ default:
+ break;
+ }
+
+ wsa883x->comp_offset = COMP_OFFSET2;
+
+ /* Initial settings */
+ regmap_multi_reg_write(regmap, reg_init, ARRAY_SIZE(reg_init));
+
+ if (variant == WSA8830 || variant == WSA8832) {
+ wsa883x->comp_offset = COMP_OFFSET3;
+ regmap_update_bits(regmap, WSA883X_DRE_CTL_0,
+ WSA883X_DRE_OFFSET_MASK,
+ wsa883x->comp_offset);
+ }
+
+ return 0;
+}
+
+static int wsa883x_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct wsa883x_priv *wsa883x = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_ATTACHED && slave->dev_num > 0)
+ return wsa883x_init(wsa883x);
+
+ return 0;
+}
+
+static int wsa883x_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct wsa883x_priv *wsa883x = dev_get_drvdata(&slave->dev);
+
+ if (state == SDW_OPS_PORT_POST_PREP)
+ wsa883x->port_prepared[prepare_ch->num - 1] = true;
+ else
+ wsa883x->port_prepared[prepare_ch->num - 1] = false;
+
+ return 0;
+}
+
+static const struct sdw_slave_ops wsa883x_slave_ops = {
+ .update_status = wsa883x_update_status,
+ .port_prep = wsa883x_port_prep,
+};
+
+static int wsa_dev_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = wsa883x->dev_mode;
+
+ return 0;
+}
+
+static int wsa_dev_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component);
+
+ if (wsa883x->dev_mode == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wsa883x->dev_mode = ucontrol->value.enumerated.item[0];
+
+ return 1;
+}
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(pa_gain,
+ 0, 14, TLV_DB_SCALE_ITEM(-300, 0, 0),
+ 15, 29, TLV_DB_SCALE_ITEM(-300, 150, 0),
+ 30, 31, TLV_DB_SCALE_ITEM(1800, 0, 0),
+);
+
+static int wsa883x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wsa883x_priv *data = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ ucontrol->value.integer.value[0] = data->port_enable[portidx];
+
+ return 0;
+}
+
+static int wsa883x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wsa883x_priv *data = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ if (ucontrol->value.integer.value[0]) {
+ if (data->port_enable[portidx])
+ return 0;
+
+ data->port_enable[portidx] = true;
+ } else {
+ if (!data->port_enable[portidx])
+ return 0;
+
+ data->port_enable[portidx] = false;
+ }
+
+ return 1;
+}
+
+static int wsa883x_get_comp_offset(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa883x->comp_offset;
+
+ return 0;
+}
+
+static int wsa883x_set_comp_offset(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component);
+
+ if (wsa883x->comp_offset == ucontrol->value.integer.value[0])
+ return 0;
+
+ wsa883x->comp_offset = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int wsa883x_codec_probe(struct snd_soc_component *comp)
+{
+ struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, wsa883x->regmap);
+
+ return 0;
+}
+
+static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ mutex_lock(&wsa883x->sp_lock);
+ wsa883x->pa_on = true;
+ mutex_unlock(&wsa883x->sp_lock);
+
+ switch (wsa883x->dev_mode) {
+ case RECEIVER:
+ snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE,
+ WSA883X_RXD_MODE_MASK,
+ WSA883X_RXD_MODE_HIFI);
+ snd_soc_component_write_field(component, WSA883X_SPKR_PWM_CLK_CTL,
+ WSA883X_SPKR_PWM_FREQ_SEL_MASK,
+ WSA883X_SPKR_PWM_FREQ_F600KHZ);
+ snd_soc_component_write_field(component, WSA883X_DRE_CTL_0,
+ WSA883X_DRE_PROG_DELAY_MASK, 0x0);
+ break;
+ case SPEAKER:
+ snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE,
+ WSA883X_RXD_MODE_MASK,
+ WSA883X_RXD_MODE_NORMAL);
+ snd_soc_component_write_field(component, WSA883X_SPKR_PWM_CLK_CTL,
+ WSA883X_SPKR_PWM_FREQ_SEL_MASK,
+ WSA883X_SPKR_PWM_FREQ_F300KHZ);
+ snd_soc_component_write_field(component, WSA883X_DRE_CTL_0,
+ WSA883X_DRE_PROG_DELAY_MASK, 0x9);
+ break;
+ default:
+ break;
+ }
+
+ if (wsa883x->port_enable[WSA883X_PORT_COMP])
+ snd_soc_component_write_field(component, WSA883X_DRE_CTL_0,
+ WSA883X_DRE_OFFSET_MASK,
+ wsa883x->comp_offset);
+ snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL,
+ WSA883X_VBAT_ADC_COEF_SEL_MASK,
+ WSA883X_VBAT_ADC_COEF_F_1DIV16);
+ snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL,
+ WSA883X_VBAT_ADC_FLT_EN_MASK, 0x1);
+ snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL,
+ WSA883X_PDM_EN_MASK,
+ WSA883X_PDM_ENABLE);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL,
+ WSA883X_VBAT_ADC_FLT_EN_MASK, 0x0);
+ snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL,
+ WSA883X_VBAT_ADC_COEF_SEL_MASK,
+ WSA883X_VBAT_ADC_COEF_F_1DIV2);
+ snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL,
+ WSA883X_GLOBAL_PA_EN_MASK, 0);
+ snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL,
+ WSA883X_PDM_EN_MASK, 0);
+ mutex_lock(&wsa883x->sp_lock);
+ wsa883x->pa_on = false;
+ mutex_unlock(&wsa883x->sp_lock);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wsa883x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_SPK("SPKR", wsa883x_spkr_event),
+};
+
+static const struct snd_kcontrol_new wsa883x_snd_controls[] = {
+ SOC_SINGLE_RANGE_TLV("PA Volume", WSA883X_DRE_CTL_1, 1,
+ 0x0, 0x1f, 1, pa_gain),
+ SOC_ENUM_EXT("WSA MODE", wsa_dev_mode_enum,
+ wsa_dev_mode_get, wsa_dev_mode_put),
+ SOC_SINGLE_EXT("COMP Offset", SND_SOC_NOPM, 0, 4, 0,
+ wsa883x_get_comp_offset, wsa883x_set_comp_offset),
+ SOC_SINGLE_EXT("DAC Switch", WSA883X_PORT_DAC, 0, 1, 0,
+ wsa883x_get_swr_port, wsa883x_set_swr_port),
+ SOC_SINGLE_EXT("COMP Switch", WSA883X_PORT_COMP, 0, 1, 0,
+ wsa883x_get_swr_port, wsa883x_set_swr_port),
+ SOC_SINGLE_EXT("BOOST Switch", WSA883X_PORT_BOOST, 0, 1, 0,
+ wsa883x_get_swr_port, wsa883x_set_swr_port),
+ SOC_SINGLE_EXT("VISENSE Switch", WSA883X_PORT_VISENSE, 0, 1, 0,
+ wsa883x_get_swr_port, wsa883x_set_swr_port),
+};
+
+static const struct snd_soc_dapm_route wsa883x_audio_map[] = {
+ {"SPKR", NULL, "IN"},
+};
+
+static const struct snd_soc_component_driver wsa883x_component_drv = {
+ .name = "WSA883x",
+ .probe = wsa883x_codec_probe,
+ .controls = wsa883x_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa883x_snd_controls),
+ .dapm_widgets = wsa883x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa883x_dapm_widgets),
+ .dapm_routes = wsa883x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa883x_audio_map),
+};
+
+static int wsa883x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wsa883x_priv *wsa883x = dev_get_drvdata(dai->dev);
+ int i;
+
+ wsa883x->active_ports = 0;
+ for (i = 0; i < WSA883X_MAX_SWR_PORTS; i++) {
+ if (!wsa883x->port_enable[i])
+ continue;
+
+ wsa883x->port_config[wsa883x->active_ports] = wsa883x_pconfig[i];
+ wsa883x->active_ports++;
+ }
+
+ wsa883x->sconfig.frame_rate = params_rate(params);
+
+ return sdw_stream_add_slave(wsa883x->slave, &wsa883x->sconfig,
+ wsa883x->port_config, wsa883x->active_ports,
+ wsa883x->sruntime);
+}
+
+static int wsa883x_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wsa883x_priv *wsa883x = dev_get_drvdata(dai->dev);
+
+ sdw_stream_remove_slave(wsa883x->slave, wsa883x->sruntime);
+
+ return 0;
+}
+
+static int wsa883x_set_sdw_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wsa883x_priv *wsa883x = dev_get_drvdata(dai->dev);
+
+ wsa883x->sruntime = stream;
+
+ return 0;
+}
+
+static int wsa883x_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ snd_soc_component_write_field(component, WSA883X_DRE_CTL_1,
+ WSA883X_DRE_GAIN_EN_MASK, 0);
+ snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL,
+ WSA883X_GLOBAL_PA_EN_MASK, 0);
+
+ } else {
+ snd_soc_component_write_field(component, WSA883X_DRE_CTL_1,
+ WSA883X_DRE_GAIN_EN_MASK,
+ WSA883X_DRE_GAIN_FROM_CSR);
+ snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL,
+ WSA883X_GLOBAL_PA_EN_MASK,
+ WSA883X_GLOBAL_PA_ENABLE);
+
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wsa883x_dai_ops = {
+ .hw_params = wsa883x_hw_params,
+ .hw_free = wsa883x_hw_free,
+ .mute_stream = wsa883x_digital_mute,
+ .set_stream = wsa883x_set_sdw_stream,
+ .mute_unmute_on_trigger = true,
+};
+
+static struct snd_soc_dai_driver wsa883x_dais[] = {
+ {
+ .name = "SPKR",
+ .playback = {
+ .stream_name = "SPKR Playback",
+ .rates = WSA883X_RATES | WSA883X_FRAC_RATES,
+ .formats = WSA883X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &wsa883x_dai_ops,
+ },
+};
+
+static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
+{
+ unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0;
+ unsigned int dmeas_msb = 0, dmeas_lsb = 0;
+ int d1, d2, dmeas;
+ unsigned int mask;
+ int ret, range;
+ long val;
+
+ guard(mutex)(&wsa883x->sp_lock);
+
+ if (wsa883x->pa_on) {
+ /*
+ * Reading temperature is possible only when Power Amplifier is
+ * off. Report last cached data.
+ */
+ *temp = wsa883x->temperature * 1000;
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(wsa883x->dev);
+ if (ret < 0)
+ return ret;
+
+ mask = WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK |
+ WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK |
+ WSA883X_PA_FSM_BYP_BG_EN_MASK |
+ WSA883X_PA_FSM_BYP_D_UNMUTE_MASK |
+ WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK |
+ WSA883X_PA_FSM_BYP_TSADC_EN_MASK;
+
+ /*
+ * Here and further do not care about read or update failures.
+ * For example, before turning the amplifier on for the first
+ * time, reading WSA883X_TEMP_DIN_MSB will always return 0.
+ * Instead, check if returned value is within reasonable
+ * thresholds.
+ */
+ regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, mask);
+
+ regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL,
+ WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0));
+
+ regmap_read(wsa883x->regmap, WSA883X_TEMP_MSB, &dmeas_msb);
+ regmap_read(wsa883x->regmap, WSA883X_TEMP_LSB, &dmeas_lsb);
+
+ regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL,
+ WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1));
+
+ regmap_read(wsa883x->regmap, WSA883X_OTP_REG_1, &d1_msb);
+ regmap_read(wsa883x->regmap, WSA883X_OTP_REG_2, &d1_lsb);
+ regmap_read(wsa883x->regmap, WSA883X_OTP_REG_3, &d2_msb);
+ regmap_read(wsa883x->regmap, WSA883X_OTP_REG_4, &d2_lsb);
+
+ regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, 0x0);
+
+ dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6;
+ d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6;
+ d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6;
+
+ if (d1 == d2) {
+ /* Incorrect data in OTP? */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1));
+ range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD;
+ if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) {
+ wsa883x->temperature = val;
+ *temp = val * 1000;
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+out:
+ pm_runtime_put_autosuspend(wsa883x->dev);
+
+ return ret;
+}
+
+static umode_t wsa883x_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa883x_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = wsa883x_get_temp(dev_get_drvdata(dev), temp);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const wsa883x_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops wsa883x_hwmon_ops = {
+ .is_visible = wsa883x_hwmon_is_visible,
+ .read = wsa883x_hwmon_read,
+};
+
+static const struct hwmon_chip_info wsa883x_hwmon_chip_info = {
+ .ops = &wsa883x_hwmon_ops,
+ .info = wsa883x_hwmon_info,
+};
+
+static void wsa883x_reset_assert(void *data)
+{
+ struct wsa883x_priv *wsa883x = data;
+
+ if (wsa883x->sd_reset)
+ reset_control_assert(wsa883x->sd_reset);
+ else
+ gpiod_direction_output(wsa883x->sd_n, 1);
+}
+
+static void wsa883x_reset_deassert(struct wsa883x_priv *wsa883x)
+{
+ if (wsa883x->sd_reset)
+ reset_control_deassert(wsa883x->sd_reset);
+ else
+ gpiod_direction_output(wsa883x->sd_n, 0);
+}
+
+static int wsa883x_get_reset(struct device *dev, struct wsa883x_priv *wsa883x)
+{
+ wsa883x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(wsa883x->sd_reset))
+ return dev_err_probe(dev, PTR_ERR(wsa883x->sd_reset),
+ "Failed to get reset\n");
+
+ /* if sd_reset: NULL, so use the backwards compatible way for powerdown-gpios */
+ if (!wsa883x->sd_reset) {
+ wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wsa883x->sd_n))
+ return dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
+ "Shutdown Control GPIO not found\n");
+ }
+
+ return 0;
+}
+
+static int wsa883x_probe(struct sdw_slave *pdev,
+ const struct sdw_device_id *id)
+{
+ struct wsa883x_priv *wsa883x;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ wsa883x = devm_kzalloc(dev, sizeof(*wsa883x), GFP_KERNEL);
+ if (!wsa883x)
+ return -ENOMEM;
+
+ wsa883x->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(wsa883x->vdd))
+ return dev_err_probe(dev, PTR_ERR(wsa883x->vdd),
+ "No vdd regulator found\n");
+
+ ret = regulator_enable(wsa883x->vdd);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n");
+
+ ret = wsa883x_get_reset(dev, wsa883x);
+ if (ret)
+ goto err;
+
+ dev_set_drvdata(dev, wsa883x);
+ wsa883x->slave = pdev;
+ wsa883x->dev = dev;
+ wsa883x->sconfig.ch_count = 1;
+ wsa883x->sconfig.bps = 1;
+ wsa883x->sconfig.direction = SDW_DATA_DIR_RX;
+ wsa883x->sconfig.type = SDW_STREAM_PDM;
+ mutex_init(&wsa883x->sp_lock);
+
+ /*
+ * Port map index starts with 0, however the data port for this codec
+ * are from index 1
+ */
+ if (of_property_read_u32_array(dev->of_node, "qcom,port-mapping", &pdev->m_port_map[1],
+ WSA883X_MAX_SWR_PORTS))
+ dev_dbg(dev, "Static Port mapping not specified\n");
+
+ pdev->prop.sink_ports = GENMASK(WSA883X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.simple_clk_stop_capable = true;
+ pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ wsa883x_reset_deassert(wsa883x);
+ ret = devm_add_action_or_reset(dev, wsa883x_reset_assert, wsa883x);
+ if (ret)
+ return ret;
+
+ wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config);
+ if (IS_ERR(wsa883x->regmap)) {
+ ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap),
+ "regmap_init failed\n");
+ goto err;
+ }
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(dev, "wsa883x",
+ wsa883x,
+ &wsa883x_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon))
+ return dev_err_probe(dev, PTR_ERR(hwmon),
+ "Failed to register hwmon sensor\n");
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = devm_snd_soc_register_component(dev,
+ &wsa883x_component_drv,
+ wsa883x_dais,
+ ARRAY_SIZE(wsa883x_dais));
+err:
+ if (ret)
+ regulator_disable(wsa883x->vdd);
+
+ return ret;
+
+}
+
+static int wsa883x_runtime_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int wsa883x_runtime_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wsa883x_pm_ops = {
+ RUNTIME_PM_OPS(wsa883x_runtime_suspend, wsa883x_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id wsa883x_swr_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x0202, 0),
+ {},
+};
+
+MODULE_DEVICE_TABLE(sdw, wsa883x_swr_id);
+
+static struct sdw_driver wsa883x_codec_driver = {
+ .driver = {
+ .name = "wsa883x-codec",
+ .pm = pm_ptr(&wsa883x_pm_ops),
+ .suppress_bind_attrs = true,
+ },
+ .probe = wsa883x_probe,
+ .ops = &wsa883x_slave_ops,
+ .id_table = wsa883x_swr_id,
+};
+
+module_sdw_driver(wsa883x_codec_driver);
+
+MODULE_DESCRIPTION("WSA883x codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
new file mode 100644
index 000000000000..887edd2be705
--- /dev/null
+++ b/sound/soc/codecs/wsa884x.c
@@ -0,0 +1,2181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023, Linaro Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define WSA884X_BASE 0x3000
+#define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001)
+#define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00)
+#define WSA884X_ADC_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x01)
+#define WSA884X_BOP1_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x02)
+#define WSA884X_BOP2_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x03)
+#define WSA884X_BOP2_PROG_BOP2_VTH_MASK 0xf0
+#define WSA884X_BOP2_PROG_BOP2_VTH_SHIFT 4
+#define WSA884X_BOP2_PROG_BOP2_HYST_MASK 0x0f
+#define WSA884X_BOP2_PROG_BOP2_HYST_SHIFT 0
+#define WSA884X_UVLO_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x04)
+#define WSA884X_UVLO_PROG1 (WSA884X_ANA_BG_TSADC_BASE + 0x05)
+#define WSA884X_SPARE_CTRL_0 (WSA884X_ANA_BG_TSADC_BASE + 0x06)
+#define WSA884X_SPARE_CTRL_1 (WSA884X_ANA_BG_TSADC_BASE + 0x07)
+#define WSA884X_SPARE_CTRL_2 (WSA884X_ANA_BG_TSADC_BASE + 0x08)
+#define WSA884X_SPARE_CTRL_3 (WSA884X_ANA_BG_TSADC_BASE + 0x09)
+#define WSA884X_REF_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x0a)
+#define WSA884X_REF_CTRL_BG_RDY_SEL_MASK 0x03
+#define WSA884X_REF_CTRL_BG_RDY_SEL_SHIFT 0
+#define WSA884X_BG_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0b)
+#define WSA884X_BG_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x0c)
+#define WSA884X_ADC_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x0d)
+#define WSA884X_ADC_IREF_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0e)
+#define WSA884X_ADC_ISENS_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0f)
+#define WSA884X_ADC_CLK_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x10)
+#define WSA884X_ADC_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x11)
+#define WSA884X_ADC_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x12)
+#define WSA884X_VBAT_SNS (WSA884X_ANA_BG_TSADC_BASE + 0x13)
+#define WSA884X_DOUT_MSB (WSA884X_ANA_BG_TSADC_BASE + 0x14)
+#define WSA884X_DOUT_LSB (WSA884X_ANA_BG_TSADC_BASE + 0x15)
+#define WSA884X_BOP_ATEST_SEL (WSA884X_ANA_BG_TSADC_BASE + 0x16)
+#define WSA884X_MISC0 (WSA884X_ANA_BG_TSADC_BASE + 0x17)
+#define WSA884X_MISC1 (WSA884X_ANA_BG_TSADC_BASE + 0x18)
+#define WSA884X_MISC2 (WSA884X_ANA_BG_TSADC_BASE + 0x19)
+#define WSA884X_MISC3 (WSA884X_ANA_BG_TSADC_BASE + 0x1a)
+#define WSA884X_SPARE_TSBG_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1b)
+#define WSA884X_SPARE_TUNE_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1c)
+#define WSA884X_SPARE_TUNE_1 (WSA884X_ANA_BG_TSADC_BASE + 0x1d)
+
+#define WSA884X_ANA_IVSENSE_BASE (WSA884X_BASE + 0x0020)
+#define WSA884X_VSENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x00)
+#define WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK 0xe0
+#define WSA884X_VSENSE1_GAIN_VSENSE_FE_SHIFT 5
+#define WSA884X_ISENSE2 (WSA884X_ANA_IVSENSE_BASE + 0x01)
+#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK 0xe0
+#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_SHIFT 5
+
+#define WSA884X_SPARE_CTL_1 (WSA884X_ANA_IVSENSE_BASE + 0x02)
+#define WSA884X_SPARE_CTL_2 (WSA884X_ANA_IVSENSE_BASE + 0x03)
+#define WSA884X_SPARE_CTL_3 (WSA884X_ANA_IVSENSE_BASE + 0x04)
+#define WSA884X_SPARE_CTL_4 (WSA884X_ANA_IVSENSE_BASE + 0x05)
+#define WSA884X_EN (WSA884X_ANA_IVSENSE_BASE + 0x06)
+#define WSA884X_OVERRIDE1 (WSA884X_ANA_IVSENSE_BASE + 0x07)
+#define WSA884X_OVERRIDE2 (WSA884X_ANA_IVSENSE_BASE + 0x08)
+#define WSA884X_ISENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x09)
+#define WSA884X_ISENSE_CAL (WSA884X_ANA_IVSENSE_BASE + 0x0a)
+#define WSA884X_MISC (WSA884X_ANA_IVSENSE_BASE + 0x0b)
+#define WSA884X_ADC_0 (WSA884X_ANA_IVSENSE_BASE + 0x0c)
+#define WSA884X_ADC_1 (WSA884X_ANA_IVSENSE_BASE + 0x0d)
+#define WSA884X_ADC_2 (WSA884X_ANA_IVSENSE_BASE + 0x0e)
+#define WSA884X_ADC_3 (WSA884X_ANA_IVSENSE_BASE + 0x0f)
+#define WSA884X_ADC_4 (WSA884X_ANA_IVSENSE_BASE + 0x10)
+#define WSA884X_ADC_5 (WSA884X_ANA_IVSENSE_BASE + 0x11)
+#define WSA884X_ADC_6 (WSA884X_ANA_IVSENSE_BASE + 0x12)
+#define WSA884X_ADC_7 (WSA884X_ANA_IVSENSE_BASE + 0x13)
+#define WSA884X_STATUS (WSA884X_ANA_IVSENSE_BASE + 0x14)
+#define WSA884X_IVSENSE_SPARE_TUNE_1 (WSA884X_ANA_IVSENSE_BASE + 0x15)
+#define WSA884X_SPARE_TUNE_2 (WSA884X_ANA_IVSENSE_BASE + 0x16)
+#define WSA884X_SPARE_TUNE_3 (WSA884X_ANA_IVSENSE_BASE + 0x17)
+#define WSA884X_SPARE_TUNE_4 (WSA884X_ANA_IVSENSE_BASE + 0x18)
+
+#define WSA884X_ANA_SPK_TOP_BASE (WSA884X_BASE + 0x0040)
+#define WSA884X_TOP_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x00)
+#define WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK 0x01
+#define WSA884X_CLIP_DET_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x01)
+#define WSA884X_CLIP_DET_CTRL2 (WSA884X_ANA_SPK_TOP_BASE + 0x02)
+#define WSA884X_DAC_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x03)
+#define WSA884X_DAC_VCM_CTRL_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x04)
+#define WSA884X_DAC_VCM_CTRL_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x05)
+#define WSA884X_DAC_VCM_CTRL_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x06)
+#define WSA884X_DAC_VCM_CTRL_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x07)
+#define WSA884X_DAC_VCM_CTRL_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x08)
+#define WSA884X_DAC_VCM_CTRL_REG6 (WSA884X_ANA_SPK_TOP_BASE + 0x09)
+#define WSA884X_PWM_CLK_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0a)
+#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_MASK 0x80
+#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_SHIFT 7
+#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_MASK 0x40
+#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_SHIFT 6
+#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_MASK 0x30
+#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_SHIFT 4
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK 0x08
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_SHIFT 3
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_MASK 0x06
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_SHIFT 1
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_MASK 0x01
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_SHIFT 0
+#define WSA884X_DRV_LF_LDO_SEL (WSA884X_ANA_SPK_TOP_BASE + 0x0b)
+#define WSA884X_OCP_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0c)
+#define WSA884X_PDRV_HS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0d)
+#define WSA884X_PDRV_LS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0e)
+#define WSA884X_SPK_TOP_SPARE_CTL_1 (WSA884X_ANA_SPK_TOP_BASE + 0x0f)
+#define WSA884X_SPK_TOP_SPARE_CTL_2 (WSA884X_ANA_SPK_TOP_BASE + 0x10)
+#define WSA884X_SPK_TOP_SPARE_CTL_3 (WSA884X_ANA_SPK_TOP_BASE + 0x11)
+#define WSA884X_SPK_TOP_SPARE_CTL_4 (WSA884X_ANA_SPK_TOP_BASE + 0x12)
+#define WSA884X_SPARE_CTL_5 (WSA884X_ANA_SPK_TOP_BASE + 0x13)
+#define WSA884X_DAC_EN_DEBUG_REG (WSA884X_ANA_SPK_TOP_BASE + 0x14)
+#define WSA884X_DAC_OPAMP_BIAS1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x15)
+#define WSA884X_DAC_OPAMP_BIAS2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x16)
+#define WSA884X_DAC_TUNE1 (WSA884X_ANA_SPK_TOP_BASE + 0x17)
+#define WSA884X_DAC_VOLTAGE_CTRL_REG (WSA884X_ANA_SPK_TOP_BASE + 0x18)
+#define WSA884X_ATEST1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x19)
+#define WSA884X_ATEST2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x1a)
+#define WSA884X_TOP_BIAS_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x1b)
+#define WSA884X_TOP_BIAS_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1c)
+#define WSA884X_TOP_BIAS_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x1d)
+#define WSA884X_TOP_BIAS_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x1e)
+#define WSA884X_PWRSTG_DBG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1f)
+#define WSA884X_DRV_LF_BLK_EN (WSA884X_ANA_SPK_TOP_BASE + 0x20)
+#define WSA884X_DRV_LF_EN (WSA884X_ANA_SPK_TOP_BASE + 0x21)
+#define WSA884X_DRV_LF_MASK_DCC_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x22)
+#define WSA884X_DRV_LF_MISC_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x23)
+#define WSA884X_DRV_LF_REG_GAIN (WSA884X_ANA_SPK_TOP_BASE + 0x24)
+#define WSA884X_DRV_OS_CAL_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x25)
+#define WSA884X_DRV_OS_CAL_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x26)
+#define WSA884X_PWRSTG_DBG (WSA884X_ANA_SPK_TOP_BASE + 0x27)
+#define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28)
+#define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29)
+#define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a)
+#define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b)
+#define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c)
+#define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d)
+#define WSA884X_SPK_TOP_SPARE_TUNE_3 (WSA884X_ANA_SPK_TOP_BASE + 0x2e)
+#define WSA884X_SPK_TOP_SPARE_TUNE_4 (WSA884X_ANA_SPK_TOP_BASE + 0x2f)
+#define WSA884X_SPARE_TUNE_5 (WSA884X_ANA_SPK_TOP_BASE + 0x30)
+#define WSA884X_SPARE_TUNE_6 (WSA884X_ANA_SPK_TOP_BASE + 0x31)
+#define WSA884X_SPARE_TUNE_7 (WSA884X_ANA_SPK_TOP_BASE + 0x32)
+#define WSA884X_SPARE_TUNE_8 (WSA884X_ANA_SPK_TOP_BASE + 0x33)
+#define WSA884X_SPARE_TUNE_9 (WSA884X_ANA_SPK_TOP_BASE + 0x34)
+#define WSA884X_SPARE_TUNE_10 (WSA884X_ANA_SPK_TOP_BASE + 0x35)
+#define WSA884X_PA_STATUS0 (WSA884X_ANA_SPK_TOP_BASE + 0x36)
+#define WSA884X_PA_STATUS1 (WSA884X_ANA_SPK_TOP_BASE + 0x37)
+#define WSA884X_PA_STATUS2 (WSA884X_ANA_SPK_TOP_BASE + 0x38)
+#define WSA884X_PA_STATUS3 (WSA884X_ANA_SPK_TOP_BASE + 0x39)
+#define WSA884X_PA_STATUS4 (WSA884X_ANA_SPK_TOP_BASE + 0x3a)
+#define WSA884X_PA_STATUS5 (WSA884X_ANA_SPK_TOP_BASE + 0x3b)
+#define WSA884X_SPARE_RO_1 (WSA884X_ANA_SPK_TOP_BASE + 0x3c)
+#define WSA884X_SPARE_RO_2 (WSA884X_ANA_SPK_TOP_BASE + 0x3d)
+#define WSA884X_SPARE_RO_3 (WSA884X_ANA_SPK_TOP_BASE + 0x3e)
+
+#define WSA884X_ANA_BOOST_BASE (WSA884X_BASE + 0x0090)
+#define WSA884X_STB_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x00)
+#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK 0xf8
+#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_SHIFT 3
+#define WSA884X_STB_CTRL1_VOUT_FS_MASK 0x07
+#define WSA884X_STB_CTRL1_VOUT_FS_SHIFT 0
+#define WSA884X_CURRENT_LIMIT (WSA884X_ANA_BOOST_BASE + 0x01)
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK 0x80
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_SHIFT 7
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK 0x7c
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_SHIFT 2
+#define WSA884X_CURRENT_LIMIT_CLK_PHASE_SHIFT 0
+#define WSA884X_BYP_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x02)
+#define WSA884X_SPARE_CTL_0 (WSA884X_ANA_BOOST_BASE + 0x03)
+#define WSA884X_BOOST_SPARE_CTL_1 (WSA884X_ANA_BOOST_BASE + 0x04)
+#define WSA884X_SPARE_RO_0 (WSA884X_ANA_BOOST_BASE + 0x05)
+#define WSA884X_BOOST_SPARE_RO_1 (WSA884X_ANA_BOOST_BASE + 0x06)
+#define WSA884X_IBIAS1 (WSA884X_ANA_BOOST_BASE + 0x07)
+#define WSA884X_IBIAS2 (WSA884X_ANA_BOOST_BASE + 0x08)
+#define WSA884X_IBIAS3 (WSA884X_ANA_BOOST_BASE + 0x09)
+#define WSA884X_EN_CTRL (WSA884X_ANA_BOOST_BASE + 0x0a)
+#define WSA884X_STB_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0b)
+#define WSA884X_STB_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0c)
+#define WSA884X_STB_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x0d)
+#define WSA884X_BYP_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0e)
+#define WSA884X_BYP_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0f)
+#define WSA884X_ZX_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x10)
+#define WSA884X_ZX_CTRL1_ZX_DET_EN_MASK 0x80
+#define WSA884X_ZX_CTRL1_ZX_DET_EN_SHIFT 7
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_MASK 0x40
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_SHIFT 6
+#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_MASK 0x20
+#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_SHIFT 5
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK 0x18
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_SHIFT 3
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_MASK 0x04
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_SHIFT 2
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_MASK 0x02
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_SHIFT 1
+#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_MASK 0x01
+#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_SHIFT 0
+#define WSA884X_ZX_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x11)
+#define WSA884X_BLEEDER_CTRL (WSA884X_ANA_BOOST_BASE + 0x12)
+#define WSA884X_BOOST_MISC (WSA884X_ANA_BOOST_BASE + 0x13)
+#define WSA884X_PWRSTAGE_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x14)
+#define WSA884X_PWRSTAGE_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x15)
+#define WSA884X_PWRSTAGE_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x16)
+#define WSA884X_PWRSTAGE_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x17)
+#define WSA884X_MAXD_REG1 (WSA884X_ANA_BOOST_BASE + 0x18)
+#define WSA884X_MAXD_REG2 (WSA884X_ANA_BOOST_BASE + 0x19)
+#define WSA884X_ILIM_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1a)
+#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_MASK 0x80
+#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_SHIFT 0x07
+#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_MASK 0x40
+#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_SHIFT 0x06
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_MASK 0x38
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_SHIFT 0x03
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK 0x07
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_SHIFT 0x00
+#define WSA884X_ILIM_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1b)
+#define WSA884X_TEST_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1c)
+#define WSA884X_TEST_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1d)
+#define WSA884X_SPARE1 (WSA884X_ANA_BOOST_BASE + 0x1e)
+#define WSA884X_BOOT_CAP_CHECK (WSA884X_ANA_BOOST_BASE + 0x1f)
+
+#define WSA884X_ANA_PON_LDOL_BASE (WSA884X_BASE + 0x00b0)
+#define WSA884X_PON_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x00)
+#define WSA884X_PWRSAV_CTL (WSA884X_ANA_PON_LDOL_BASE + 0x01)
+#define WSA884X_PON_LDOL_SPARE_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x02)
+#define WSA884X_PON_LDOL_SPARE_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x03)
+#define WSA884X_PON_LDOL_SPARE_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x04)
+#define WSA884X_PON_LDOL_SPARE_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x05)
+#define WSA884X_PON_CLT_1 (WSA884X_ANA_PON_LDOL_BASE + 0x06)
+#define WSA884X_PON_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x07)
+#define WSA884X_PON_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x08)
+#define WSA884X_CKWD_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x09)
+#define WSA884X_CKWD_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0a)
+#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK 0x20
+#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_SHIFT 5
+#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK 0x1f
+#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_SHIFT 0
+#define WSA884X_CKWD_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x0b)
+#define WSA884X_CKSK_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0c)
+#define WSA884X_PADSW_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0d)
+#define WSA884X_TEST_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0e)
+#define WSA884X_TEST_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0f)
+#define WSA884X_STATUS_0 (WSA884X_ANA_PON_LDOL_BASE + 0x10)
+#define WSA884X_STATUS_1 (WSA884X_ANA_PON_LDOL_BASE + 0x11)
+#define WSA884X_PON_LDOL_SPARE_TUNE_0 (WSA884X_ANA_PON_LDOL_BASE + 0x12)
+#define WSA884X_PON_LDOL_SPARE_TUNE_1 (WSA884X_ANA_PON_LDOL_BASE + 0x13)
+#define WSA884X_PON_LDOL_SPARE_TUNE_2 (WSA884X_ANA_PON_LDOL_BASE + 0x14)
+#define WSA884X_PON_LDOL_SPARE_TUNE_3 (WSA884X_ANA_PON_LDOL_BASE + 0x15)
+#define WSA884X_PON_LDOL_SPARE_TUNE_4 (WSA884X_ANA_PON_LDOL_BASE + 0x16)
+
+#define WSA884X_DIG_CTRL0_BASE (WSA884X_BASE + 0x0400)
+#define WSA884X_DIG_CTRL0_PAGE (WSA884X_DIG_CTRL0_BASE + 0x00)
+#define WSA884X_CHIP_ID0 (WSA884X_DIG_CTRL0_BASE + 0x01)
+#define WSA884X_CHIP_ID1 (WSA884X_DIG_CTRL0_BASE + 0x02)
+#define WSA884X_CHIP_ID2 (WSA884X_DIG_CTRL0_BASE + 0x03)
+#define WSA884X_CHIP_ID3 (WSA884X_DIG_CTRL0_BASE + 0x04)
+#define WSA884X_BUS_ID (WSA884X_DIG_CTRL0_BASE + 0x05)
+#define WSA884X_CDC_RST_CTL (WSA884X_DIG_CTRL0_BASE + 0x10)
+#define WSA884X_SWR_RESET_EN (WSA884X_DIG_CTRL0_BASE + 0x14)
+#define WSA884X_TOP_CLK_CFG (WSA884X_DIG_CTRL0_BASE + 0x18)
+#define WSA884X_SWR_CLK_RATE (WSA884X_DIG_CTRL0_BASE + 0x19)
+#define WSA884X_CDC_PATH_MODE (WSA884X_DIG_CTRL0_BASE + 0x1a)
+#define WSA884X_CDC_PATH_MODE_RXD_MODE_MASK 0x02
+#define WSA884X_CDC_PATH_MODE_RXD_MODE_SHIFT 0
+#define WSA884X_CDC_PATH_MODE_TXD_MODE_MASK 0x01
+#define WSA884X_CDC_PATH_MODE_TXD_MODE_SHIFT 0
+#define WSA884X_CDC_CLK_CTL (WSA884X_DIG_CTRL0_BASE + 0x1c)
+#define WSA884X_PA_FSM_EN (WSA884X_DIG_CTRL0_BASE + 0x30)
+#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK 0x01
+#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_SHIFT 0
+#define WSA884X_PA_FSM_CTL0 (WSA884X_DIG_CTRL0_BASE + 0x31)
+#define WSA884X_PA_FSM_CTL1 (WSA884X_DIG_CTRL0_BASE + 0x32)
+#define WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK 0x38
+#define WSA884X_PA_FSM_TIMER0 (WSA884X_DIG_CTRL0_BASE + 0x33)
+#define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34)
+#define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35)
+#define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36)
+#define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37)
+#define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38)
+#define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39)
+#define WSA884X_PA_FSM_MSK0 (WSA884X_DIG_CTRL0_BASE + 0x3a)
+#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b)
+#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c)
+#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d)
+#define WSA884X_PA_FSM_BYP0_DC_CAL_EN_MASK 0x01
+#define WSA884X_PA_FSM_BYP0_DC_CAL_EN_SHIFT 0
+#define WSA884X_PA_FSM_BYP0_CLK_WD_EN_MASK 0x02
+#define WSA884X_PA_FSM_BYP0_CLK_WD_EN_SHIFT 1
+#define WSA884X_PA_FSM_BYP0_BG_EN_MASK 0x04
+#define WSA884X_PA_FSM_BYP0_BG_EN_SHIFT 2
+#define WSA884X_PA_FSM_BYP0_BOOST_EN_MASK 0x08
+#define WSA884X_PA_FSM_BYP0_BOOST_EN_SHIFT 3
+#define WSA884X_PA_FSM_BYP0_PA_EN_MASK 0x10
+#define WSA884X_PA_FSM_BYP0_PA_EN_SHIFT 4
+#define WSA884X_PA_FSM_BYP0_D_UNMUTE_MASK 0x20
+#define WSA884X_PA_FSM_BYP0_D_UNMUTE_SHIFT 5
+#define WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_MASK 0x40
+#define WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_SHIFT 6
+#define WSA884X_PA_FSM_BYP0_TSADC_EN_MASK 0x80
+#define WSA884X_PA_FSM_BYP0_TSADC_EN_SHIFT 7
+#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e)
+#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50)
+#define WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01
+#define WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0
+#define WSA884X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02
+#define WSA884X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1
+#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51)
+#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52)
+#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53)
+#define WSA884X_TEMP_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x54)
+#define WSA884X_TEMP_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x55)
+#define WSA884X_TEMP_CONFIG0 (WSA884X_DIG_CTRL0_BASE + 0x56)
+#define WSA884X_TEMP_CONFIG1 (WSA884X_DIG_CTRL0_BASE + 0x57)
+#define WSA884X_VBAT_THRM_FLT_CTL (WSA884X_DIG_CTRL0_BASE + 0x58)
+#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_MASK 0xe0
+#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_SHIFT 5
+#define WSA884X_VBAT_THRM_FLT_CTL_THRM_FLT_EN_SHIFT 4
+#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK 0x0e
+#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_SHIFT 1
+#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_FLT_EN_SHIFT 0
+#define WSA884X_VBAT_CAL_CTL (WSA884X_DIG_CTRL0_BASE + 0x59)
+#define WSA884X_VBAT_CAL_CTL_RESERVE_MASK 0x0e
+#define WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK 0x01
+#define WSA884X_VBAT_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x5a)
+#define WSA884X_VBAT_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x5b)
+#define WSA884X_VBAT_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x5c)
+#define WSA884X_VBAT_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x5d)
+#define WSA884X_VBAT_CAL_MSB (WSA884X_DIG_CTRL0_BASE + 0x5e)
+#define WSA884X_VBAT_CAL_LSB (WSA884X_DIG_CTRL0_BASE + 0x5f)
+#define WSA884X_UVLO_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x60)
+#define WSA884X_BOP_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x61)
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK 0x1e
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_SHIFT 1
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK 0x1
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_SHIFT 0
+#define WSA884X_VBAT_ZONE_DETC_CTL (WSA884X_DIG_CTRL0_BASE + 0x64)
+#define WSA884X_CPS_CTL (WSA884X_DIG_CTRL0_BASE + 0x68)
+#define WSA884X_CDC_RX_CTL (WSA884X_DIG_CTRL0_BASE + 0x70)
+#define WSA884X_CDC_SPK_DSM_A1_0 (WSA884X_DIG_CTRL0_BASE + 0x71)
+#define WSA884X_CDC_SPK_DSM_A1_1 (WSA884X_DIG_CTRL0_BASE + 0x72)
+#define WSA884X_CDC_SPK_DSM_A2_0 (WSA884X_DIG_CTRL0_BASE + 0x73)
+#define WSA884X_CDC_SPK_DSM_A2_1 (WSA884X_DIG_CTRL0_BASE + 0x74)
+#define WSA884X_CDC_SPK_DSM_A3_0 (WSA884X_DIG_CTRL0_BASE + 0x75)
+#define WSA884X_CDC_SPK_DSM_A3_1 (WSA884X_DIG_CTRL0_BASE + 0x76)
+#define WSA884X_CDC_SPK_DSM_A4_0 (WSA884X_DIG_CTRL0_BASE + 0x77)
+#define WSA884X_CDC_SPK_DSM_A4_1 (WSA884X_DIG_CTRL0_BASE + 0x78)
+#define WSA884X_CDC_SPK_DSM_A5_0 (WSA884X_DIG_CTRL0_BASE + 0x79)
+#define WSA884X_CDC_SPK_DSM_A5_1 (WSA884X_DIG_CTRL0_BASE + 0x7a)
+#define WSA884X_CDC_SPK_DSM_A6_0 (WSA884X_DIG_CTRL0_BASE + 0x7b)
+#define WSA884X_CDC_SPK_DSM_A7_0 (WSA884X_DIG_CTRL0_BASE + 0x7c)
+#define WSA884X_CDC_SPK_DSM_C_0 (WSA884X_DIG_CTRL0_BASE + 0x7d)
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK 0xf0
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_SHIFT 4
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK 0x0f
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_SHIFT 0
+#define WSA884X_CDC_SPK_DSM_C_1 (WSA884X_DIG_CTRL0_BASE + 0x7e)
+#define WSA884X_CDC_SPK_DSM_C_2 (WSA884X_DIG_CTRL0_BASE + 0x7f)
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK 0xf0
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_SHIFT 4
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_MASK 0x0f
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_SHIFT 0
+#define WSA884X_CDC_SPK_DSM_C_3 (WSA884X_DIG_CTRL0_BASE + 0x80)
+#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK 0x3f
+#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_SHIFT 0
+#define WSA884X_CDC_SPK_DSM_R1 (WSA884X_DIG_CTRL0_BASE + 0x81)
+#define WSA884X_CDC_SPK_DSM_R2 (WSA884X_DIG_CTRL0_BASE + 0x82)
+#define WSA884X_CDC_SPK_DSM_R3 (WSA884X_DIG_CTRL0_BASE + 0x83)
+#define WSA884X_CDC_SPK_DSM_R4 (WSA884X_DIG_CTRL0_BASE + 0x84)
+#define WSA884X_CDC_SPK_DSM_R5 (WSA884X_DIG_CTRL0_BASE + 0x85)
+#define WSA884X_CDC_SPK_DSM_R6 (WSA884X_DIG_CTRL0_BASE + 0x86)
+#define WSA884X_CDC_SPK_DSM_R7 (WSA884X_DIG_CTRL0_BASE + 0x87)
+#define WSA884X_CDC_SPK_GAIN_PDM_0 (WSA884X_DIG_CTRL0_BASE + 0x88)
+#define WSA884X_CDC_SPK_GAIN_PDM_1 (WSA884X_DIG_CTRL0_BASE + 0x89)
+#define WSA884X_CDC_SPK_GAIN_PDM_2 (WSA884X_DIG_CTRL0_BASE + 0x8a)
+#define WSA884X_PDM_WD_CTL (WSA884X_DIG_CTRL0_BASE + 0x8b)
+#define WSA884X_PDM_WD_CTL_HOLD_OFF_MASK 0x04
+#define WSA884X_PDM_WD_CTL_HOLD_OFF_SHIFT 2
+#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_MASK 0x02
+#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_SHIFT 1
+#define WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK 0x01
+#define WSA884X_PDM_WD_CTL_PDM_WD_EN_SHIFT 0
+#define WSA884X_DEM_BYPASS_DATA0 (WSA884X_DIG_CTRL0_BASE + 0x90)
+#define WSA884X_DEM_BYPASS_DATA1 (WSA884X_DIG_CTRL0_BASE + 0x91)
+#define WSA884X_DEM_BYPASS_DATA2 (WSA884X_DIG_CTRL0_BASE + 0x92)
+#define WSA884X_DEM_BYPASS_DATA3 (WSA884X_DIG_CTRL0_BASE + 0x93)
+#define WSA884X_DRE_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xb0)
+#define WSA884X_DRE_CTL_0_PROG_DELAY_MASK 0xf0
+#define WSA884X_DRE_CTL_0_PROG_DELAY_SHIFT 4
+#define WSA884X_DRE_CTL_0_OFFSET_MASK 0x07
+#define WSA884X_DRE_CTL_0_OFFSET_SHIFT 0
+#define WSA884X_DRE_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xb1)
+#define WSA884X_DRE_CTL_1_CSR_GAIN_MASK 0x3e
+#define WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT 1
+#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK 0x01
+#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_SHIFT 0
+#define WSA884X_DRE_IDLE_DET_CTL (WSA884X_DIG_CTRL0_BASE + 0xb2)
+#define WSA884X_GAIN_RAMPING_CTL (WSA884X_DIG_CTRL0_BASE + 0xb8)
+#define WSA884X_GAIN_RAMPING_MIN (WSA884X_DIG_CTRL0_BASE + 0xb9)
+#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK 0x1f
+#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_SHIFT 0
+#define WSA884X_TAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc0)
+#define WSA884X_TAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc1)
+#define WSA884X_TAGC_FORCE_VAL (WSA884X_DIG_CTRL0_BASE + 0xc2)
+#define WSA884X_VAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc8)
+#define WSA884X_VAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc9)
+#define WSA884X_VAGC_ATTN_LVL_1 (WSA884X_DIG_CTRL0_BASE + 0xca)
+#define WSA884X_VAGC_ATTN_LVL_2 (WSA884X_DIG_CTRL0_BASE + 0xcb)
+#define WSA884X_VAGC_ATTN_LVL_3 (WSA884X_DIG_CTRL0_BASE + 0xcc)
+#define WSA884X_CLSH_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xd0)
+#define WSA884X_CLSH_CTL_0_CSR_GAIN_EN_SHIFT 7
+#define WSA884X_CLSH_CTL_0_DLY_CODE_MASK 0x70
+#define WSA884X_CLSH_CTL_0_DLY_CODE_SHIFT 4
+#define WSA884X_CLSH_CTL_0_DLY_RST_SHIFT 3
+#define WSA884X_CLSH_CTL_0_DLY_EN_SHIFT 2
+#define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1
+#define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0
+#define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1)
+#define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2)
+#define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3)
+#define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4)
+#define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5)
+#define WSA884X_CLSH_SOFT_MAX (WSA884X_DIG_CTRL0_BASE + 0xd6)
+#define WSA884X_CLSH_SIG_DP (WSA884X_DIG_CTRL0_BASE + 0xd7)
+#define WSA884X_PBR_DELAY_CTL (WSA884X_DIG_CTRL0_BASE + 0xd8)
+#define WSA884X_CLSH_SRL_MAX_PBR (WSA884X_DIG_CTRL0_BASE + 0xe0)
+#define WSA884X_PBR_MAX_VOLTAGE 20
+#define WSA884X_PBR_MAX_CODE 255
+#define WSA884X_VTH_TO_REG(vth) \
+ ((vth) != 0 ? (((vth) - 150) * WSA884X_PBR_MAX_CODE / (WSA884X_PBR_MAX_VOLTAGE * 100) + 1) : 0)
+#define WSA884X_CLSH_VTH1 (WSA884X_DIG_CTRL0_BASE + 0xe1)
+#define WSA884X_CLSH_VTH2 (WSA884X_DIG_CTRL0_BASE + 0xe2)
+#define WSA884X_CLSH_VTH3 (WSA884X_DIG_CTRL0_BASE + 0xe3)
+#define WSA884X_CLSH_VTH4 (WSA884X_DIG_CTRL0_BASE + 0xe4)
+#define WSA884X_CLSH_VTH5 (WSA884X_DIG_CTRL0_BASE + 0xe5)
+#define WSA884X_CLSH_VTH6 (WSA884X_DIG_CTRL0_BASE + 0xe6)
+#define WSA884X_CLSH_VTH7 (WSA884X_DIG_CTRL0_BASE + 0xe7)
+#define WSA884X_CLSH_VTH8 (WSA884X_DIG_CTRL0_BASE + 0xe8)
+#define WSA884X_CLSH_VTH9 (WSA884X_DIG_CTRL0_BASE + 0xe9)
+#define WSA884X_CLSH_VTH10 (WSA884X_DIG_CTRL0_BASE + 0xea)
+#define WSA884X_CLSH_VTH11 (WSA884X_DIG_CTRL0_BASE + 0xeb)
+#define WSA884X_CLSH_VTH12 (WSA884X_DIG_CTRL0_BASE + 0xec)
+#define WSA884X_CLSH_VTH13 (WSA884X_DIG_CTRL0_BASE + 0xed)
+#define WSA884X_CLSH_VTH14 (WSA884X_DIG_CTRL0_BASE + 0xee)
+#define WSA884X_CLSH_VTH15 (WSA884X_DIG_CTRL0_BASE + 0xef)
+
+#define WSA884X_DIG_CTRL1_BASE (WSA884X_BASE + 0x0500)
+#define WSA884X_DIG_CTRL1_PAGE (WSA884X_DIG_CTRL1_BASE + 0x00)
+#define WSA884X_VPHX_SYS_EN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x01)
+#define WSA884X_ANA_WO_CTL_0 (WSA884X_DIG_CTRL1_BASE + 0x04)
+#define WSA884X_ANA_WO_CTL_0_MODE_SHIFT 0
+#define WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK 0xc0
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_DISABLE 0x0
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB 0xa
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB 0x7
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK 0x3c
+#define WSA884X_ANA_WO_CTL_0_PA_MIN_GAIN_BYP_MASK 0x02
+#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER 0x1
+#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK 0x01
+#define WSA884X_ANA_WO_CTL_1 (WSA884X_DIG_CTRL1_BASE + 0x05)
+#define WSA884X_PIN_CTL (WSA884X_DIG_CTRL1_BASE + 0x10)
+#define WSA884X_PIN_CTL_OE (WSA884X_DIG_CTRL1_BASE + 0x11)
+#define WSA884X_PIN_WDATA_IOPAD (WSA884X_DIG_CTRL1_BASE + 0x12)
+#define WSA884X_PIN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x13)
+#define WSA884X_I2C_SLAVE_CTL (WSA884X_DIG_CTRL1_BASE + 0x14)
+#define WSA884X_SPMI_PAD_CTL0 (WSA884X_DIG_CTRL1_BASE + 0x15)
+#define WSA884X_SPMI_PAD_CTL1 (WSA884X_DIG_CTRL1_BASE + 0x16)
+#define WSA884X_SPMI_PAD_CTL2 (WSA884X_DIG_CTRL1_BASE + 0x17)
+#define WSA884X_MEM_CTL (WSA884X_DIG_CTRL1_BASE + 0x18)
+#define WSA884X_SWR_HM_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x19)
+#define WSA884X_SWR_HM_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x1a)
+#define WSA884X_OTP_CTRL0 (WSA884X_DIG_CTRL1_BASE + 0x30)
+#define WSA884X_OTP_CTRL1 (WSA884X_DIG_CTRL1_BASE + 0x31)
+#define WSA884X_OTP_CTRL2 (WSA884X_DIG_CTRL1_BASE + 0x32)
+#define WSA884X_OTP_STAT (WSA884X_DIG_CTRL1_BASE + 0x33)
+#define WSA884X_OTP_PRG_TCSP0 (WSA884X_DIG_CTRL1_BASE + 0x34)
+#define WSA884X_OTP_PRG_TCSP1 (WSA884X_DIG_CTRL1_BASE + 0x35)
+#define WSA884X_OTP_PRG_TPPS (WSA884X_DIG_CTRL1_BASE + 0x36)
+#define WSA884X_OTP_PRG_TVPS (WSA884X_DIG_CTRL1_BASE + 0x37)
+#define WSA884X_OTP_PRG_TVPH (WSA884X_DIG_CTRL1_BASE + 0x38)
+#define WSA884X_OTP_PRG_TPPR0 (WSA884X_DIG_CTRL1_BASE + 0x39)
+#define WSA884X_OTP_PRG_TPPR1 (WSA884X_DIG_CTRL1_BASE + 0x3a)
+#define WSA884X_OTP_PRG_TPPH (WSA884X_DIG_CTRL1_BASE + 0x3b)
+#define WSA884X_OTP_PRG_END (WSA884X_DIG_CTRL1_BASE + 0x3c)
+#define WSA884X_WAVG_PLAY (WSA884X_DIG_CTRL1_BASE + 0x40)
+#define WSA884X_WAVG_CTL (WSA884X_DIG_CTRL1_BASE + 0x41)
+#define WSA884X_WAVG_LRA_PER_0 (WSA884X_DIG_CTRL1_BASE + 0x43)
+#define WSA884X_WAVG_LRA_PER_1 (WSA884X_DIG_CTRL1_BASE + 0x44)
+#define WSA884X_WAVG_DELTA_THETA_0 (WSA884X_DIG_CTRL1_BASE + 0x45)
+#define WSA884X_WAVG_DELTA_THETA_1 (WSA884X_DIG_CTRL1_BASE + 0x46)
+#define WSA884X_WAVG_DIRECT_AMP_0 (WSA884X_DIG_CTRL1_BASE + 0x47)
+#define WSA884X_WAVG_DIRECT_AMP_1 (WSA884X_DIG_CTRL1_BASE + 0x48)
+#define WSA884X_WAVG_PTRN_AMP0_0 (WSA884X_DIG_CTRL1_BASE + 0x49)
+#define WSA884X_WAVG_PTRN_AMP0_1 (WSA884X_DIG_CTRL1_BASE + 0x4a)
+#define WSA884X_WAVG_PTRN_AMP1_0 (WSA884X_DIG_CTRL1_BASE + 0x4b)
+#define WSA884X_WAVG_PTRN_AMP1_1 (WSA884X_DIG_CTRL1_BASE + 0x4c)
+#define WSA884X_WAVG_PTRN_AMP2_0 (WSA884X_DIG_CTRL1_BASE + 0x4d)
+#define WSA884X_WAVG_PTRN_AMP2_1 (WSA884X_DIG_CTRL1_BASE + 0x4e)
+#define WSA884X_WAVG_PTRN_AMP3_0 (WSA884X_DIG_CTRL1_BASE + 0x4f)
+#define WSA884X_WAVG_PTRN_AMP3_1 (WSA884X_DIG_CTRL1_BASE + 0x50)
+#define WSA884X_WAVG_PTRN_AMP4_0 (WSA884X_DIG_CTRL1_BASE + 0x51)
+#define WSA884X_WAVG_PTRN_AMP4_1 (WSA884X_DIG_CTRL1_BASE + 0x52)
+#define WSA884X_WAVG_PTRN_AMP5_0 (WSA884X_DIG_CTRL1_BASE + 0x53)
+#define WSA884X_WAVG_PTRN_AMP5_1 (WSA884X_DIG_CTRL1_BASE + 0x54)
+#define WSA884X_WAVG_PTRN_AMP6_0 (WSA884X_DIG_CTRL1_BASE + 0x55)
+#define WSA884X_WAVG_PTRN_AMP6_1 (WSA884X_DIG_CTRL1_BASE + 0x56)
+#define WSA884X_WAVG_PTRN_AMP7_0 (WSA884X_DIG_CTRL1_BASE + 0x57)
+#define WSA884X_WAVG_PTRN_AMP7_1 (WSA884X_DIG_CTRL1_BASE + 0x58)
+#define WSA884X_WAVG_PER_0_1 (WSA884X_DIG_CTRL1_BASE + 0x59)
+#define WSA884X_WAVG_PER_2_3 (WSA884X_DIG_CTRL1_BASE + 0x5a)
+#define WSA884X_WAVG_PER_4_5 (WSA884X_DIG_CTRL1_BASE + 0x5b)
+#define WSA884X_WAVG_PER_6_7 (WSA884X_DIG_CTRL1_BASE + 0x5c)
+#define WSA884X_WAVG_STA (WSA884X_DIG_CTRL1_BASE + 0x5d)
+#define WSA884X_INTR_MODE (WSA884X_DIG_CTRL1_BASE + 0x80)
+#define WSA884X_INTR_MASK0 (WSA884X_DIG_CTRL1_BASE + 0x81)
+#define WSA884X_INTR_MASK1 (WSA884X_DIG_CTRL1_BASE + 0x82)
+#define WSA884X_INTR_STATUS0 (WSA884X_DIG_CTRL1_BASE + 0x83)
+#define WSA884X_INTR_STATUS1 (WSA884X_DIG_CTRL1_BASE + 0x84)
+#define WSA884X_INTR_CLEAR0 (WSA884X_DIG_CTRL1_BASE + 0x85)
+#define WSA884X_INTR_CLEAR1 (WSA884X_DIG_CTRL1_BASE + 0x86)
+#define WSA884X_INTR_LEVEL0 (WSA884X_DIG_CTRL1_BASE + 0x87)
+#define WSA884X_INTR_LEVEL1 (WSA884X_DIG_CTRL1_BASE + 0x88)
+#define WSA884X_INTR_SET0 (WSA884X_DIG_CTRL1_BASE + 0x89)
+#define WSA884X_INTR_SET1 (WSA884X_DIG_CTRL1_BASE + 0x8a)
+#define WSA884X_INTR_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x8b)
+#define WSA884X_INTR_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x8c)
+#define WSA884X_PDM_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc0)
+#define WSA884X_ATE_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc1)
+#define WSA884X_PA_FSM_DBG (WSA884X_DIG_CTRL1_BASE + 0xc2)
+#define WSA884X_DIG_DEBUG_MODE (WSA884X_DIG_CTRL1_BASE + 0xc3)
+#define WSA884X_DIG_DEBUG_SEL (WSA884X_DIG_CTRL1_BASE + 0xc4)
+#define WSA884X_DIG_DEBUG_EN (WSA884X_DIG_CTRL1_BASE + 0xc5)
+#define WSA884X_TADC_DETECT_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xc9)
+#define WSA884X_TADC_DEBUG_MSB (WSA884X_DIG_CTRL1_BASE + 0xca)
+#define WSA884X_TADC_DEBUG_LSB (WSA884X_DIG_CTRL1_BASE + 0xcb)
+#define WSA884X_SAMPLE_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcc)
+#define WSA884X_SWR_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcd)
+#define WSA884X_TEST_MODE_CTL (WSA884X_DIG_CTRL1_BASE + 0xce)
+#define WSA884X_IOPAD_CTL (WSA884X_DIG_CTRL1_BASE + 0xcf)
+#define WSA884X_ANA_CSR_DBG_ADD (WSA884X_DIG_CTRL1_BASE + 0xd0)
+#define WSA884X_ANA_CSR_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd1)
+#define WSA884X_CLK_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd2)
+#define WSA884X_SPARE_R (WSA884X_DIG_CTRL1_BASE + 0xf0)
+#define WSA884X_SPARE_0 (WSA884X_DIG_CTRL1_BASE + 0xf1)
+#define WSA884X_SPARE_1 (WSA884X_DIG_CTRL1_BASE + 0xf2)
+#define WSA884X_SPARE_2 (WSA884X_DIG_CTRL1_BASE + 0xf3)
+#define WSA884X_SCODE (WSA884X_DIG_CTRL1_BASE + 0xff)
+
+#define WSA884X_DIG_TRIM_BASE (WSA884X_BASE + 0x0800)
+#define WSA884X_DIG_TRIM_PAGE (WSA884X_DIG_TRIM_BASE + 0x00)
+#define WSA884X_OTP_REG_0 (WSA884X_DIG_TRIM_BASE + 0x80)
+#define WSA884X_OTP_ID_WSA8840 0x0
+#define WSA884X_OTP_ID_WSA8845 0x5
+#define WSA884X_OTP_ID_WSA8845H 0xc
+#define WSA884X_OTP_REG_0_ID_MASK 0x0f
+#define WSA884X_OTP_REG_1 (WSA884X_DIG_TRIM_BASE + 0x81)
+#define WSA884X_OTP_REG_2 (WSA884X_DIG_TRIM_BASE + 0x82)
+#define WSA884X_OTP_REG_3 (WSA884X_DIG_TRIM_BASE + 0x83)
+#define WSA884X_OTP_REG_4 (WSA884X_DIG_TRIM_BASE + 0x84)
+#define WSA884X_OTP_REG_5 (WSA884X_DIG_TRIM_BASE + 0x85)
+#define WSA884X_OTP_REG_6 (WSA884X_DIG_TRIM_BASE + 0x86)
+#define WSA884X_OTP_REG_7 (WSA884X_DIG_TRIM_BASE + 0x87)
+#define WSA884X_OTP_REG_8 (WSA884X_DIG_TRIM_BASE + 0x88)
+#define WSA884X_OTP_REG_9 (WSA884X_DIG_TRIM_BASE + 0x89)
+#define WSA884X_OTP_REG_10 (WSA884X_DIG_TRIM_BASE + 0x8a)
+#define WSA884X_OTP_REG_11 (WSA884X_DIG_TRIM_BASE + 0x8b)
+#define WSA884X_OTP_REG_12 (WSA884X_DIG_TRIM_BASE + 0x8c)
+#define WSA884X_OTP_REG_13 (WSA884X_DIG_TRIM_BASE + 0x8d)
+#define WSA884X_OTP_REG_14 (WSA884X_DIG_TRIM_BASE + 0x8e)
+#define WSA884X_OTP_REG_15 (WSA884X_DIG_TRIM_BASE + 0x8f)
+#define WSA884X_OTP_REG_16 (WSA884X_DIG_TRIM_BASE + 0x90)
+#define WSA884X_OTP_REG_17 (WSA884X_DIG_TRIM_BASE + 0x91)
+#define WSA884X_OTP_REG_18 (WSA884X_DIG_TRIM_BASE + 0x92)
+#define WSA884X_OTP_REG_19 (WSA884X_DIG_TRIM_BASE + 0x93)
+#define WSA884X_OTP_REG_20 (WSA884X_DIG_TRIM_BASE + 0x94)
+#define WSA884X_OTP_REG_21 (WSA884X_DIG_TRIM_BASE + 0x95)
+#define WSA884X_OTP_REG_22 (WSA884X_DIG_TRIM_BASE + 0x96)
+#define WSA884X_OTP_REG_23 (WSA884X_DIG_TRIM_BASE + 0x97)
+#define WSA884X_OTP_REG_24 (WSA884X_DIG_TRIM_BASE + 0x98)
+#define WSA884X_OTP_REG_25 (WSA884X_DIG_TRIM_BASE + 0x99)
+#define WSA884X_OTP_REG_26 (WSA884X_DIG_TRIM_BASE + 0x9a)
+#define WSA884X_OTP_REG_27 (WSA884X_DIG_TRIM_BASE + 0x9b)
+#define WSA884X_OTP_REG_28 (WSA884X_DIG_TRIM_BASE + 0x9c)
+#define WSA884X_OTP_REG_29 (WSA884X_DIG_TRIM_BASE + 0x9d)
+#define WSA884X_OTP_REG_30 (WSA884X_DIG_TRIM_BASE + 0x9e)
+#define WSA884X_OTP_REG_31 (WSA884X_DIG_TRIM_BASE + 0x9f)
+#define WSA884X_OTP_REG_32 (WSA884X_DIG_TRIM_BASE + 0xa0)
+#define WSA884X_OTP_REG_33 (WSA884X_DIG_TRIM_BASE + 0xa1)
+#define WSA884X_OTP_REG_34 (WSA884X_DIG_TRIM_BASE + 0xa2)
+#define WSA884X_OTP_REG_35 (WSA884X_DIG_TRIM_BASE + 0xa3)
+#define WSA884X_OTP_REG_36 (WSA884X_DIG_TRIM_BASE + 0xa4)
+#define WSA884X_OTP_REG_37 (WSA884X_DIG_TRIM_BASE + 0xa5)
+#define WSA884X_OTP_REG_38 (WSA884X_DIG_TRIM_BASE + 0xa6)
+#define WSA884X_OTP_REG_38_RESERVER_MASK 0xf0
+#define WSA884X_OTP_REG_38_RESERVER_SHIFT 4
+#define WSA884X_OTP_REG_38_BST_CFG_SEL_MASK 0x08
+#define WSA884X_OTP_REG_38_BST_CFG_SEL_SHIFT 3
+#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_MASK 0x07
+#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_SHIFT 0
+#define WSA884X_OTP_REG_39 (WSA884X_DIG_TRIM_BASE + 0xa7)
+#define WSA884X_OTP_REG_40 (WSA884X_DIG_TRIM_BASE + 0xa8)
+#define WSA884X_OTP_REG_40_SPARE_TYPE2_MASK 0xc0
+#define WSA884X_OTP_REG_40_SPARE_TYPE2_SHIFT 6
+#define WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK 0x3c
+#define WSA884X_OTP_REG_40_ISENSE_RESCAL_SHIFT 2
+#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_MASK 0x2
+#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_SHIFT 1
+#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_MASK 0x1
+#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_SHIFT 0
+#define WSA884X_OTP_REG_41 (WSA884X_DIG_TRIM_BASE + 0xa9)
+#define WSA884X_OTP_REG_63 (WSA884X_DIG_TRIM_BASE + 0xbf)
+
+#define WSA884X_DIG_EMEM_BASE (WSA884X_BASE + 0x08C0)
+#define WSA884X_EMEM_0 (WSA884X_DIG_EMEM_BASE + 0x00)
+#define WSA884X_EMEM_1 (WSA884X_DIG_EMEM_BASE + 0x01)
+#define WSA884X_EMEM_2 (WSA884X_DIG_EMEM_BASE + 0x02)
+#define WSA884X_EMEM_3 (WSA884X_DIG_EMEM_BASE + 0x03)
+#define WSA884X_EMEM_4 (WSA884X_DIG_EMEM_BASE + 0x04)
+#define WSA884X_EMEM_5 (WSA884X_DIG_EMEM_BASE + 0x05)
+#define WSA884X_EMEM_6 (WSA884X_DIG_EMEM_BASE + 0x06)
+#define WSA884X_EMEM_7 (WSA884X_DIG_EMEM_BASE + 0x07)
+#define WSA884X_EMEM_8 (WSA884X_DIG_EMEM_BASE + 0x08)
+#define WSA884X_EMEM_9 (WSA884X_DIG_EMEM_BASE + 0x09)
+#define WSA884X_EMEM_10 (WSA884X_DIG_EMEM_BASE + 0x0a)
+#define WSA884X_EMEM_11 (WSA884X_DIG_EMEM_BASE + 0x0b)
+#define WSA884X_EMEM_12 (WSA884X_DIG_EMEM_BASE + 0x0c)
+#define WSA884X_EMEM_13 (WSA884X_DIG_EMEM_BASE + 0x0d)
+#define WSA884X_EMEM_14 (WSA884X_DIG_EMEM_BASE + 0x0e)
+#define WSA884X_EMEM_15 (WSA884X_DIG_EMEM_BASE + 0x0f)
+#define WSA884X_EMEM_16 (WSA884X_DIG_EMEM_BASE + 0x10)
+#define WSA884X_EMEM_17 (WSA884X_DIG_EMEM_BASE + 0x11)
+#define WSA884X_EMEM_18 (WSA884X_DIG_EMEM_BASE + 0x12)
+#define WSA884X_EMEM_19 (WSA884X_DIG_EMEM_BASE + 0x13)
+#define WSA884X_EMEM_20 (WSA884X_DIG_EMEM_BASE + 0x14)
+#define WSA884X_EMEM_21 (WSA884X_DIG_EMEM_BASE + 0x15)
+#define WSA884X_EMEM_22 (WSA884X_DIG_EMEM_BASE + 0x16)
+#define WSA884X_EMEM_23 (WSA884X_DIG_EMEM_BASE + 0x17)
+#define WSA884X_EMEM_24 (WSA884X_DIG_EMEM_BASE + 0x18)
+#define WSA884X_EMEM_25 (WSA884X_DIG_EMEM_BASE + 0x19)
+#define WSA884X_EMEM_26 (WSA884X_DIG_EMEM_BASE + 0x1a)
+#define WSA884X_EMEM_27 (WSA884X_DIG_EMEM_BASE + 0x1b)
+#define WSA884X_EMEM_28 (WSA884X_DIG_EMEM_BASE + 0x1c)
+#define WSA884X_EMEM_29 (WSA884X_DIG_EMEM_BASE + 0x1d)
+#define WSA884X_EMEM_30 (WSA884X_DIG_EMEM_BASE + 0x1e)
+#define WSA884X_EMEM_31 (WSA884X_DIG_EMEM_BASE + 0x1f)
+#define WSA884X_EMEM_32 (WSA884X_DIG_EMEM_BASE + 0x20)
+#define WSA884X_EMEM_33 (WSA884X_DIG_EMEM_BASE + 0x21)
+#define WSA884X_EMEM_34 (WSA884X_DIG_EMEM_BASE + 0x22)
+#define WSA884X_EMEM_35 (WSA884X_DIG_EMEM_BASE + 0x23)
+#define WSA884X_EMEM_36 (WSA884X_DIG_EMEM_BASE + 0x24)
+#define WSA884X_EMEM_37 (WSA884X_DIG_EMEM_BASE + 0x25)
+#define WSA884X_EMEM_38 (WSA884X_DIG_EMEM_BASE + 0x26)
+#define WSA884X_EMEM_39 (WSA884X_DIG_EMEM_BASE + 0x27)
+#define WSA884X_EMEM_40 (WSA884X_DIG_EMEM_BASE + 0x28)
+#define WSA884X_EMEM_41 (WSA884X_DIG_EMEM_BASE + 0x29)
+#define WSA884X_EMEM_42 (WSA884X_DIG_EMEM_BASE + 0x2a)
+#define WSA884X_EMEM_43 (WSA884X_DIG_EMEM_BASE + 0x2b)
+#define WSA884X_EMEM_44 (WSA884X_DIG_EMEM_BASE + 0x2c)
+#define WSA884X_EMEM_45 (WSA884X_DIG_EMEM_BASE + 0x2d)
+#define WSA884X_EMEM_46 (WSA884X_DIG_EMEM_BASE + 0x2e)
+#define WSA884X_EMEM_47 (WSA884X_DIG_EMEM_BASE + 0x2f)
+#define WSA884X_EMEM_48 (WSA884X_DIG_EMEM_BASE + 0x30)
+#define WSA884X_EMEM_49 (WSA884X_DIG_EMEM_BASE + 0x31)
+#define WSA884X_EMEM_50 (WSA884X_DIG_EMEM_BASE + 0x32)
+#define WSA884X_EMEM_51 (WSA884X_DIG_EMEM_BASE + 0x33)
+#define WSA884X_EMEM_52 (WSA884X_DIG_EMEM_BASE + 0x34)
+#define WSA884X_EMEM_53 (WSA884X_DIG_EMEM_BASE + 0x35)
+#define WSA884X_EMEM_54 (WSA884X_DIG_EMEM_BASE + 0x36)
+#define WSA884X_EMEM_55 (WSA884X_DIG_EMEM_BASE + 0x37)
+#define WSA884X_EMEM_56 (WSA884X_DIG_EMEM_BASE + 0x38)
+#define WSA884X_EMEM_57 (WSA884X_DIG_EMEM_BASE + 0x39)
+#define WSA884X_EMEM_58 (WSA884X_DIG_EMEM_BASE + 0x3a)
+#define WSA884X_EMEM_59 (WSA884X_DIG_EMEM_BASE + 0x3b)
+#define WSA884X_EMEM_60 (WSA884X_DIG_EMEM_BASE + 0x3c)
+#define WSA884X_EMEM_61 (WSA884X_DIG_EMEM_BASE + 0x3d)
+#define WSA884X_EMEM_62 (WSA884X_DIG_EMEM_BASE + 0x3e)
+#define WSA884X_EMEM_63 (WSA884X_DIG_EMEM_BASE + 0x3f)
+
+#define WSA884X_NUM_REGISTERS (WSA884X_EMEM_63 + 1)
+#define WSA884X_MAX_REGISTER (WSA884X_NUM_REGISTERS - 1)
+
+#define WSA884X_SUPPLIES_NUM 2
+#define WSA884X_MAX_SWR_PORTS 6
+#define WSA884X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define WSA884X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define WSA884X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Two-point trimming for temperature calibration */
+#define WSA884X_T1_TEMP -10L
+#define WSA884X_T2_TEMP 150L
+
+/*
+ * Device will report senseless data in many cases, so discard any measurements
+ * outside of valid range.
+ */
+#define WSA884X_LOW_TEMP_THRESHOLD 5
+#define WSA884X_HIGH_TEMP_THRESHOLD 45
+
+struct wsa884x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM];
+ struct sdw_slave *slave;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS];
+ struct gpio_desc *sd_n;
+ struct reset_control *sd_reset;
+ bool port_prepared[WSA884X_MAX_SWR_PORTS];
+ bool port_enable[WSA884X_MAX_SWR_PORTS];
+ int active_ports;
+ int dev_mode;
+ bool hw_init;
+ /*
+ * Protects temperature reading code (related to speaker protection) and
+ * fields: temperature and pa_on.
+ */
+ struct mutex sp_lock;
+ unsigned int temperature;
+ bool pa_on;
+};
+
+enum {
+ COMP_OFFSET0,
+ COMP_OFFSET1,
+ COMP_OFFSET2,
+ COMP_OFFSET3,
+ COMP_OFFSET4,
+};
+
+enum wsa884x_gain {
+ G_21_DB = 0,
+ G_19P5_DB,
+ G_18_DB,
+ G_16P5_DB,
+ G_15_DB,
+ G_13P5_DB,
+ G_12_DB,
+ G_10P5_DB,
+ G_9_DB,
+ G_7P5_DB,
+ G_6_DB,
+ G_4P5_DB,
+ G_3_DB,
+ G_1P5_DB,
+ G_0_DB,
+ G_M1P5_DB,
+ G_M3_DB,
+ G_M4P5_DB,
+ G_M6_DB,
+ G_MAX_DB,
+};
+
+enum wsa884x_isense {
+ ISENSE_6_DB = 0,
+ ISENSE_12_DB,
+ ISENSE_15_DB,
+ ISENSE_18_DB,
+};
+
+enum wsa884x_vsense {
+ VSENSE_M12_DB = 0,
+ VSENSE_M15_DB,
+ VSENSE_M18_DB,
+ VSENSE_M21_DB,
+ VSENSE_M24_DB,
+};
+
+enum wsa884x_port_ids {
+ WSA884X_PORT_DAC,
+ WSA884X_PORT_COMP,
+ WSA884X_PORT_BOOST,
+ WSA884X_PORT_PBR,
+ WSA884X_PORT_VISENSE,
+ WSA884X_PORT_CPS,
+};
+
+static const char * const wsa884x_supply_name[] = {
+ "vdd-io",
+ "vdd-1p8",
+};
+
+static const char * const wsa884x_dev_mode_text[] = {
+ "Speaker", "Receiver"
+};
+
+enum wsa884x_mode {
+ WSA884X_SPEAKER,
+ WSA884X_RECEIVER,
+};
+
+static const struct soc_enum wsa884x_dev_mode_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text);
+
+static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
+ [WSA884X_PORT_DAC] = {
+ .num = WSA884X_PORT_DAC + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA884X_PORT_COMP] = {
+ .num = WSA884X_PORT_COMP + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA884X_PORT_BOOST] = {
+ .num = WSA884X_PORT_BOOST + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA884X_PORT_PBR] = {
+ .num = WSA884X_PORT_PBR + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA884X_PORT_VISENSE] = {
+ .num = WSA884X_PORT_VISENSE + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ },
+ [WSA884X_PORT_CPS] = {
+ .num = WSA884X_PORT_CPS + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }
+};
+
+static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
+ [WSA884X_PORT_DAC] = {
+ .num = WSA884X_PORT_DAC + 1,
+ .ch_mask = 0x1,
+ },
+ [WSA884X_PORT_COMP] = {
+ .num = WSA884X_PORT_COMP + 1,
+ .ch_mask = 0xf,
+ },
+ [WSA884X_PORT_BOOST] = {
+ .num = WSA884X_PORT_BOOST + 1,
+ .ch_mask = 0x3,
+ },
+ [WSA884X_PORT_PBR] = {
+ .num = WSA884X_PORT_PBR + 1,
+ .ch_mask = 0x1,
+ },
+ [WSA884X_PORT_VISENSE] = {
+ .num = WSA884X_PORT_VISENSE + 1,
+ .ch_mask = 0x1,
+ },
+ [WSA884X_PORT_CPS] = {
+ .num = WSA884X_PORT_CPS + 1,
+ .ch_mask = 0x3,
+ },
+};
+
+static const struct reg_default wsa884x_defaults[] = {
+ { WSA884X_BG_CTRL, 0xa5 },
+ { WSA884X_ADC_CTRL, 0x00 },
+ { WSA884X_BOP1_PROG, 0x22 },
+ { WSA884X_BOP2_PROG, 0x44 },
+ { WSA884X_UVLO_PROG, 0x99 },
+ { WSA884X_UVLO_PROG1, 0x70 },
+ { WSA884X_SPARE_CTRL_0, 0x00 },
+ { WSA884X_SPARE_CTRL_1, 0x00 },
+ { WSA884X_SPARE_CTRL_2, 0x00 },
+ { WSA884X_SPARE_CTRL_3, 0x00 },
+ { WSA884X_REF_CTRL, 0xd2 },
+ { WSA884X_BG_TEST_CTL, 0x06 },
+ { WSA884X_BG_BIAS, 0xd7 },
+ { WSA884X_ADC_PROG, 0x08 },
+ { WSA884X_ADC_IREF_CTL, 0x57 },
+ { WSA884X_ADC_ISENS_CTL, 0x47 },
+ { WSA884X_ADC_CLK_CTL, 0x87 },
+ { WSA884X_ADC_TEST_CTL, 0x00 },
+ { WSA884X_ADC_BIAS, 0x51 },
+ { WSA884X_VBAT_SNS, 0xa0 },
+ { WSA884X_BOP_ATEST_SEL, 0x00 },
+ { WSA884X_MISC0, 0x04 },
+ { WSA884X_MISC1, 0x75 },
+ { WSA884X_MISC2, 0x00 },
+ { WSA884X_MISC3, 0x10 },
+ { WSA884X_SPARE_TSBG_0, 0x00 },
+ { WSA884X_SPARE_TUNE_0, 0x00 },
+ { WSA884X_SPARE_TUNE_1, 0x00 },
+ { WSA884X_VSENSE1, 0xe7 },
+ { WSA884X_ISENSE2, 0x27 },
+ { WSA884X_SPARE_CTL_1, 0x00 },
+ { WSA884X_SPARE_CTL_2, 0x00 },
+ { WSA884X_SPARE_CTL_3, 0x00 },
+ { WSA884X_SPARE_CTL_4, 0x00 },
+ { WSA884X_EN, 0x10 },
+ { WSA884X_OVERRIDE1, 0x00 },
+ { WSA884X_OVERRIDE2, 0x08 },
+ { WSA884X_ISENSE1, 0xd4 },
+ { WSA884X_ISENSE_CAL, 0x00 },
+ { WSA884X_MISC, 0x00 },
+ { WSA884X_ADC_0, 0x00 },
+ { WSA884X_ADC_1, 0x00 },
+ { WSA884X_ADC_2, 0x40 },
+ { WSA884X_ADC_3, 0x80 },
+ { WSA884X_ADC_4, 0x25 },
+ { WSA884X_ADC_5, 0x24 },
+ { WSA884X_ADC_6, 0x0a },
+ { WSA884X_ADC_7, 0x81 },
+ { WSA884X_IVSENSE_SPARE_TUNE_1, 0x00 },
+ { WSA884X_SPARE_TUNE_2, 0x00 },
+ { WSA884X_SPARE_TUNE_3, 0x00 },
+ { WSA884X_SPARE_TUNE_4, 0x00 },
+ { WSA884X_TOP_CTRL1, 0xd3 },
+ { WSA884X_CLIP_DET_CTRL1, 0x7e },
+ { WSA884X_CLIP_DET_CTRL2, 0x4c },
+ { WSA884X_DAC_CTRL1, 0xa4 },
+ { WSA884X_DAC_VCM_CTRL_REG1, 0x02 },
+ { WSA884X_DAC_VCM_CTRL_REG2, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG3, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG4, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG5, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG6, 0x00 },
+ { WSA884X_PWM_CLK_CTL, 0x20 },
+ { WSA884X_DRV_LF_LDO_SEL, 0xaa },
+ { WSA884X_OCP_CTL, 0xc6 },
+ { WSA884X_PDRV_HS_CTL, 0x52 },
+ { WSA884X_PDRV_LS_CTL, 0x4a },
+ { WSA884X_SPK_TOP_SPARE_CTL_1, 0x00 },
+ { WSA884X_SPK_TOP_SPARE_CTL_2, 0x00 },
+ { WSA884X_SPK_TOP_SPARE_CTL_3, 0x00 },
+ { WSA884X_SPK_TOP_SPARE_CTL_4, 0x00 },
+ { WSA884X_SPARE_CTL_5, 0x00 },
+ { WSA884X_DAC_EN_DEBUG_REG, 0x00 },
+ { WSA884X_DAC_OPAMP_BIAS1_REG, 0x48 },
+ { WSA884X_DAC_OPAMP_BIAS2_REG, 0x48 },
+ { WSA884X_DAC_TUNE1, 0x02 },
+ { WSA884X_DAC_VOLTAGE_CTRL_REG, 0x05 },
+ { WSA884X_ATEST1_REG, 0x00 },
+ { WSA884X_ATEST2_REG, 0x00 },
+ { WSA884X_TOP_BIAS_REG1, 0x6a },
+ { WSA884X_TOP_BIAS_REG2, 0x65 },
+ { WSA884X_TOP_BIAS_REG3, 0x55 },
+ { WSA884X_TOP_BIAS_REG4, 0xa9 },
+ { WSA884X_PWRSTG_DBG2, 0x21 },
+ { WSA884X_DRV_LF_BLK_EN, 0x0f },
+ { WSA884X_DRV_LF_EN, 0x0a },
+ { WSA884X_DRV_LF_MASK_DCC_CTL, 0x08 },
+ { WSA884X_DRV_LF_MISC_CTL1, 0x30 },
+ { WSA884X_DRV_LF_REG_GAIN, 0x00 },
+ { WSA884X_DRV_OS_CAL_CTL, 0x00 },
+ { WSA884X_DRV_OS_CAL_CTL1, 0x90 },
+ { WSA884X_PWRSTG_DBG, 0x08 },
+ { WSA884X_BBM_CTL, 0x92 },
+ { WSA884X_TOP_MISC1, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG7, 0x00 },
+ { WSA884X_TOP_BIAS_REG5, 0x15 },
+ { WSA884X_DRV_LF_MISC_CTL2, 0x00 },
+ { WSA884X_STB_CTRL1, 0x42 },
+ { WSA884X_CURRENT_LIMIT, 0x54 },
+ { WSA884X_BYP_CTRL1, 0x01 },
+ { WSA884X_SPARE_CTL_0, 0x00 },
+ { WSA884X_BOOST_SPARE_CTL_1, 0x00 },
+ { WSA884X_IBIAS1, 0x00 },
+ { WSA884X_IBIAS2, 0x00 },
+ { WSA884X_IBIAS3, 0x00 },
+ { WSA884X_EN_CTRL, 0x42 },
+ { WSA884X_STB_CTRL2, 0x03 },
+ { WSA884X_STB_CTRL3, 0x3c },
+ { WSA884X_STB_CTRL4, 0x30 },
+ { WSA884X_BYP_CTRL2, 0x97 },
+ { WSA884X_BYP_CTRL3, 0x11 },
+ { WSA884X_ZX_CTRL1, 0xf0 },
+ { WSA884X_ZX_CTRL2, 0x04 },
+ { WSA884X_BLEEDER_CTRL, 0x04 },
+ { WSA884X_BOOST_MISC, 0x62 },
+ { WSA884X_PWRSTAGE_CTRL1, 0x00 },
+ { WSA884X_PWRSTAGE_CTRL2, 0x31 },
+ { WSA884X_PWRSTAGE_CTRL3, 0x81 },
+ { WSA884X_PWRSTAGE_CTRL4, 0x5f },
+ { WSA884X_MAXD_REG1, 0x00 },
+ { WSA884X_MAXD_REG2, 0x5b },
+ { WSA884X_ILIM_CTRL1, 0xe2 },
+ { WSA884X_ILIM_CTRL2, 0x90 },
+ { WSA884X_TEST_CTRL1, 0x00 },
+ { WSA884X_TEST_CTRL2, 0x00 },
+ { WSA884X_SPARE1, 0x00 },
+ { WSA884X_BOOT_CAP_CHECK, 0x01 },
+ { WSA884X_PON_CTL_0, 0x12 },
+ { WSA884X_PWRSAV_CTL, 0xaa },
+ { WSA884X_PON_LDOL_SPARE_CTL_0, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_CTL_1, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_CTL_2, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_CTL_3, 0x00 },
+ { WSA884X_PON_CLT_1, 0xe1 },
+ { WSA884X_PON_CTL_2, 0x00 },
+ { WSA884X_PON_CTL_3, 0x70 },
+ { WSA884X_CKWD_CTL_0, 0x14 },
+ { WSA884X_CKWD_CTL_1, 0x3b },
+ { WSA884X_CKWD_CTL_2, 0x00 },
+ { WSA884X_CKSK_CTL_0, 0x00 },
+ { WSA884X_PADSW_CTL_0, 0x00 },
+ { WSA884X_TEST_0, 0x00 },
+ { WSA884X_TEST_1, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_0, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_1, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_2, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_3, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_4, 0x00 },
+ { WSA884X_DIG_CTRL0_PAGE, 0x00 },
+ { WSA884X_CDC_RST_CTL, 0x01 },
+ { WSA884X_SWR_RESET_EN, 0x00 },
+ { WSA884X_TOP_CLK_CFG, 0x00 },
+ { WSA884X_SWR_CLK_RATE, 0x00 },
+ { WSA884X_CDC_PATH_MODE, 0x00 },
+ { WSA884X_CDC_CLK_CTL, 0x1f },
+ { WSA884X_PA_FSM_EN, 0x00 },
+ { WSA884X_PA_FSM_CTL0, 0x00 },
+ { WSA884X_PA_FSM_CTL1, 0xfe },
+ { WSA884X_PA_FSM_TIMER0, 0x80 },
+ { WSA884X_PA_FSM_TIMER1, 0x80 },
+ { WSA884X_PA_FSM_ERR_CTL, 0x00 },
+ { WSA884X_PA_FSM_MSK0, 0x00 },
+ { WSA884X_PA_FSM_MSK1, 0x00 },
+ { WSA884X_PA_FSM_BYP_CTL, 0x00 },
+ { WSA884X_PA_FSM_BYP0, 0x00 },
+ { WSA884X_PA_FSM_BYP1, 0x00 },
+ { WSA884X_TADC_VALUE_CTL, 0x03 },
+ { WSA884X_TEMP_DETECT_CTL, 0x01 },
+ { WSA884X_TEMP_CONFIG0, 0x00 },
+ { WSA884X_TEMP_CONFIG1, 0x00 },
+ { WSA884X_VBAT_THRM_FLT_CTL, 0x7f },
+ { WSA884X_VBAT_CAL_CTL, 0x01 },
+ { WSA884X_UVLO_DEGLITCH_CTL, 0x05 },
+ { WSA884X_BOP_DEGLITCH_CTL, 0x05 },
+ { WSA884X_VBAT_ZONE_DETC_CTL, 0x31 },
+ { WSA884X_CPS_CTL, 0x00 },
+ { WSA884X_CDC_RX_CTL, 0xfe },
+ { WSA884X_CDC_SPK_DSM_A1_0, 0x00 },
+ { WSA884X_CDC_SPK_DSM_A1_1, 0x01 },
+ { WSA884X_CDC_SPK_DSM_A2_0, 0x96 },
+ { WSA884X_CDC_SPK_DSM_A2_1, 0x09 },
+ { WSA884X_CDC_SPK_DSM_A3_0, 0xab },
+ { WSA884X_CDC_SPK_DSM_A3_1, 0x05 },
+ { WSA884X_CDC_SPK_DSM_A4_0, 0x1c },
+ { WSA884X_CDC_SPK_DSM_A4_1, 0x02 },
+ { WSA884X_CDC_SPK_DSM_A5_0, 0x17 },
+ { WSA884X_CDC_SPK_DSM_A5_1, 0x02 },
+ { WSA884X_CDC_SPK_DSM_A6_0, 0xaa },
+ { WSA884X_CDC_SPK_DSM_A7_0, 0xe3 },
+ { WSA884X_CDC_SPK_DSM_C_0, 0x69 },
+ { WSA884X_CDC_SPK_DSM_C_1, 0x54 },
+ { WSA884X_CDC_SPK_DSM_C_2, 0x02 },
+ { WSA884X_CDC_SPK_DSM_C_3, 0x15 },
+ { WSA884X_CDC_SPK_DSM_R1, 0xa4 },
+ { WSA884X_CDC_SPK_DSM_R2, 0xb5 },
+ { WSA884X_CDC_SPK_DSM_R3, 0x86 },
+ { WSA884X_CDC_SPK_DSM_R4, 0x85 },
+ { WSA884X_CDC_SPK_DSM_R5, 0xaa },
+ { WSA884X_CDC_SPK_DSM_R6, 0xe2 },
+ { WSA884X_CDC_SPK_DSM_R7, 0x62 },
+ { WSA884X_CDC_SPK_GAIN_PDM_0, 0x00 },
+ { WSA884X_CDC_SPK_GAIN_PDM_1, 0xfc },
+ { WSA884X_CDC_SPK_GAIN_PDM_2, 0x05 },
+ { WSA884X_PDM_WD_CTL, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA0, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA1, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA2, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA3, 0x00 },
+ { WSA884X_DRE_CTL_0, 0x70 },
+ { WSA884X_DRE_CTL_1, 0x04 },
+ { WSA884X_DRE_IDLE_DET_CTL, 0x2f },
+ { WSA884X_GAIN_RAMPING_CTL, 0x50 },
+ { WSA884X_GAIN_RAMPING_MIN, 0x12 },
+ { WSA884X_TAGC_CTL, 0x15 },
+ { WSA884X_TAGC_TIME, 0xbc },
+ { WSA884X_TAGC_FORCE_VAL, 0x00 },
+ { WSA884X_VAGC_CTL, 0x01 },
+ { WSA884X_VAGC_TIME, 0x0f },
+ { WSA884X_VAGC_ATTN_LVL_1, 0x03 },
+ { WSA884X_VAGC_ATTN_LVL_2, 0x06 },
+ { WSA884X_VAGC_ATTN_LVL_3, 0x09 },
+ { WSA884X_CLSH_CTL_0, 0x37 },
+ { WSA884X_CLSH_CTL_1, 0x81 },
+ { WSA884X_CLSH_V_HD_PA, 0x0c },
+ { WSA884X_CLSH_V_PA_MIN, 0x00 },
+ { WSA884X_CLSH_OVRD_VAL, 0x00 },
+ { WSA884X_CLSH_HARD_MAX, 0xff },
+ { WSA884X_CLSH_SOFT_MAX, 0xf5 },
+ { WSA884X_CLSH_SIG_DP, 0x00 },
+ { WSA884X_PBR_DELAY_CTL, 0x07 },
+ { WSA884X_CLSH_SRL_MAX_PBR, 0x02 },
+ { WSA884X_CLSH_VTH1, 0x00 },
+ { WSA884X_CLSH_VTH2, 0x00 },
+ { WSA884X_CLSH_VTH3, 0x00 },
+ { WSA884X_CLSH_VTH4, 0x00 },
+ { WSA884X_CLSH_VTH5, 0x00 },
+ { WSA884X_CLSH_VTH6, 0x00 },
+ { WSA884X_CLSH_VTH7, 0x00 },
+ { WSA884X_CLSH_VTH8, 0x00 },
+ { WSA884X_CLSH_VTH9, 0x00 },
+ { WSA884X_CLSH_VTH10, 0x00 },
+ { WSA884X_CLSH_VTH11, 0x00 },
+ { WSA884X_CLSH_VTH12, 0x00 },
+ { WSA884X_CLSH_VTH13, 0x00 },
+ { WSA884X_CLSH_VTH14, 0x00 },
+ { WSA884X_CLSH_VTH15, 0x00 },
+ { WSA884X_DIG_CTRL1_PAGE, 0x00 },
+ { WSA884X_PIN_CTL, 0x04 },
+ { WSA884X_PIN_CTL_OE, 0x00 },
+ { WSA884X_PIN_WDATA_IOPAD, 0x00 },
+ { WSA884X_I2C_SLAVE_CTL, 0x00 },
+ { WSA884X_SPMI_PAD_CTL0, 0x2f },
+ { WSA884X_SPMI_PAD_CTL1, 0x2f },
+ { WSA884X_SPMI_PAD_CTL2, 0x2f },
+ { WSA884X_MEM_CTL, 0x00 },
+ { WSA884X_SWR_HM_TEST0, 0x08 },
+ { WSA884X_OTP_CTRL0, 0x00 },
+ { WSA884X_OTP_CTRL2, 0x00 },
+ { WSA884X_OTP_PRG_TCSP0, 0x77 },
+ { WSA884X_OTP_PRG_TCSP1, 0x00 },
+ { WSA884X_OTP_PRG_TPPS, 0x47 },
+ { WSA884X_OTP_PRG_TVPS, 0x3b },
+ { WSA884X_OTP_PRG_TVPH, 0x47 },
+ { WSA884X_OTP_PRG_TPPR0, 0x47 },
+ { WSA884X_OTP_PRG_TPPR1, 0x00 },
+ { WSA884X_OTP_PRG_TPPH, 0x47 },
+ { WSA884X_OTP_PRG_END, 0x47 },
+ { WSA884X_WAVG_PLAY, 0x00 },
+ { WSA884X_WAVG_CTL, 0x06 },
+ { WSA884X_WAVG_LRA_PER_0, 0xd1 },
+ { WSA884X_WAVG_LRA_PER_1, 0x00 },
+ { WSA884X_WAVG_DELTA_THETA_0, 0xe6 },
+ { WSA884X_WAVG_DELTA_THETA_1, 0x04 },
+ { WSA884X_WAVG_DIRECT_AMP_0, 0x50 },
+ { WSA884X_WAVG_DIRECT_AMP_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP0_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP0_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP1_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP1_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP2_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP2_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP3_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP3_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP4_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP4_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP5_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP5_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP6_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP6_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP7_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP7_1, 0x00 },
+ { WSA884X_WAVG_PER_0_1, 0x88 },
+ { WSA884X_WAVG_PER_2_3, 0x88 },
+ { WSA884X_WAVG_PER_4_5, 0x88 },
+ { WSA884X_WAVG_PER_6_7, 0x88 },
+ { WSA884X_INTR_MODE, 0x00 },
+ { WSA884X_INTR_MASK0, 0x90 },
+ { WSA884X_INTR_MASK1, 0x00 },
+ { WSA884X_INTR_CLEAR0, 0x00 },
+ { WSA884X_INTR_CLEAR1, 0x00 },
+ { WSA884X_INTR_LEVEL0, 0x04 },
+ { WSA884X_INTR_LEVEL1, 0x00 },
+ { WSA884X_INTR_SET0, 0x00 },
+ { WSA884X_INTR_SET1, 0x00 },
+ { WSA884X_INTR_TEST0, 0x00 },
+ { WSA884X_INTR_TEST1, 0x00 },
+ { WSA884X_PDM_TEST_MODE, 0x00 },
+ { WSA884X_PA_FSM_DBG, 0x00 },
+ { WSA884X_DIG_DEBUG_MODE, 0x00 },
+ { WSA884X_DIG_DEBUG_SEL, 0x00 },
+ { WSA884X_DIG_DEBUG_EN, 0x00 },
+ { WSA884X_TADC_DETECT_DBG_CTL, 0x00 },
+ { WSA884X_TADC_DEBUG_MSB, 0x00 },
+ { WSA884X_TADC_DEBUG_LSB, 0x00 },
+ { WSA884X_SAMPLE_EDGE_SEL, 0x7f },
+ { WSA884X_SWR_EDGE_SEL, 0x00 },
+ { WSA884X_TEST_MODE_CTL, 0x05 },
+ { WSA884X_IOPAD_CTL, 0x00 },
+ { WSA884X_ANA_CSR_DBG_ADD, 0x00 },
+ { WSA884X_ANA_CSR_DBG_CTL, 0x12 },
+ { WSA884X_CLK_DBG_CTL, 0x00 },
+ { WSA884X_SPARE_0, 0x00 },
+ { WSA884X_SPARE_1, 0x00 },
+ { WSA884X_SPARE_2, 0x00 },
+ { WSA884X_SCODE, 0x00 },
+ { WSA884X_DIG_TRIM_PAGE, 0x00 },
+ { WSA884X_EMEM_0, 0x00 },
+ { WSA884X_EMEM_1, 0x00 },
+ { WSA884X_EMEM_2, 0x00 },
+ { WSA884X_EMEM_3, 0x00 },
+ { WSA884X_EMEM_4, 0x00 },
+ { WSA884X_EMEM_5, 0x00 },
+ { WSA884X_EMEM_6, 0x00 },
+ { WSA884X_EMEM_7, 0x00 },
+ { WSA884X_EMEM_8, 0x00 },
+ { WSA884X_EMEM_9, 0x00 },
+ { WSA884X_EMEM_10, 0x00 },
+ { WSA884X_EMEM_11, 0x00 },
+ { WSA884X_EMEM_12, 0x00 },
+ { WSA884X_EMEM_13, 0x00 },
+ { WSA884X_EMEM_14, 0x00 },
+ { WSA884X_EMEM_15, 0x00 },
+ { WSA884X_EMEM_16, 0x00 },
+ { WSA884X_EMEM_17, 0x00 },
+ { WSA884X_EMEM_18, 0x00 },
+ { WSA884X_EMEM_19, 0x00 },
+ { WSA884X_EMEM_20, 0x00 },
+ { WSA884X_EMEM_21, 0x00 },
+ { WSA884X_EMEM_22, 0x00 },
+ { WSA884X_EMEM_23, 0x00 },
+ { WSA884X_EMEM_24, 0x00 },
+ { WSA884X_EMEM_25, 0x00 },
+ { WSA884X_EMEM_26, 0x00 },
+ { WSA884X_EMEM_27, 0x00 },
+ { WSA884X_EMEM_28, 0x00 },
+ { WSA884X_EMEM_29, 0x00 },
+ { WSA884X_EMEM_30, 0x00 },
+ { WSA884X_EMEM_31, 0x00 },
+ { WSA884X_EMEM_32, 0x00 },
+ { WSA884X_EMEM_33, 0x00 },
+ { WSA884X_EMEM_34, 0x00 },
+ { WSA884X_EMEM_35, 0x00 },
+ { WSA884X_EMEM_36, 0x00 },
+ { WSA884X_EMEM_37, 0x00 },
+ { WSA884X_EMEM_38, 0x00 },
+ { WSA884X_EMEM_39, 0x00 },
+ { WSA884X_EMEM_40, 0x00 },
+ { WSA884X_EMEM_41, 0x00 },
+ { WSA884X_EMEM_42, 0x00 },
+ { WSA884X_EMEM_43, 0x00 },
+ { WSA884X_EMEM_44, 0x00 },
+ { WSA884X_EMEM_45, 0x00 },
+ { WSA884X_EMEM_46, 0x00 },
+ { WSA884X_EMEM_47, 0x00 },
+ { WSA884X_EMEM_48, 0x00 },
+ { WSA884X_EMEM_49, 0x00 },
+ { WSA884X_EMEM_50, 0x00 },
+ { WSA884X_EMEM_51, 0x00 },
+ { WSA884X_EMEM_52, 0x00 },
+ { WSA884X_EMEM_53, 0x00 },
+ { WSA884X_EMEM_54, 0x00 },
+ { WSA884X_EMEM_55, 0x00 },
+ { WSA884X_EMEM_56, 0x00 },
+ { WSA884X_EMEM_57, 0x00 },
+ { WSA884X_EMEM_58, 0x00 },
+ { WSA884X_EMEM_59, 0x00 },
+ { WSA884X_EMEM_60, 0x00 },
+ { WSA884X_EMEM_61, 0x00 },
+ { WSA884X_EMEM_62, 0x00 },
+ { WSA884X_EMEM_63, 0x00 },
+};
+
+static bool wsa884x_readonly_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA884X_DOUT_MSB:
+ case WSA884X_DOUT_LSB:
+ case WSA884X_STATUS:
+ case WSA884X_SPK_TOP_SPARE_TUNE_2:
+ case WSA884X_SPK_TOP_SPARE_TUNE_3:
+ case WSA884X_SPK_TOP_SPARE_TUNE_4:
+ case WSA884X_SPARE_TUNE_5:
+ case WSA884X_SPARE_TUNE_6:
+ case WSA884X_SPARE_TUNE_7:
+ case WSA884X_SPARE_TUNE_8:
+ case WSA884X_SPARE_TUNE_9:
+ case WSA884X_SPARE_TUNE_10:
+ case WSA884X_PA_STATUS0:
+ case WSA884X_PA_STATUS1:
+ case WSA884X_PA_STATUS2:
+ case WSA884X_PA_STATUS3:
+ case WSA884X_PA_STATUS4:
+ case WSA884X_PA_STATUS5:
+ case WSA884X_SPARE_RO_1:
+ case WSA884X_SPARE_RO_2:
+ case WSA884X_SPARE_RO_3:
+ case WSA884X_SPARE_RO_0:
+ case WSA884X_BOOST_SPARE_RO_1:
+ case WSA884X_STATUS_0:
+ case WSA884X_STATUS_1:
+ case WSA884X_CHIP_ID0:
+ case WSA884X_CHIP_ID1:
+ case WSA884X_CHIP_ID2:
+ case WSA884X_CHIP_ID3:
+ case WSA884X_BUS_ID:
+ case WSA884X_PA_FSM_STA0:
+ case WSA884X_PA_FSM_STA1:
+ case WSA884X_PA_FSM_ERR_COND0:
+ case WSA884X_PA_FSM_ERR_COND1:
+ case WSA884X_TEMP_DIN_MSB:
+ case WSA884X_TEMP_DIN_LSB:
+ case WSA884X_TEMP_DOUT_MSB:
+ case WSA884X_TEMP_DOUT_LSB:
+ case WSA884X_VBAT_DIN_MSB:
+ case WSA884X_VBAT_DIN_LSB:
+ case WSA884X_VBAT_DOUT_MSB:
+ case WSA884X_VBAT_DOUT_LSB:
+ case WSA884X_VBAT_CAL_MSB:
+ case WSA884X_VBAT_CAL_LSB:
+ case WSA884X_VPHX_SYS_EN_STATUS:
+ case WSA884X_PIN_STATUS:
+ case WSA884X_SWR_HM_TEST1:
+ case WSA884X_OTP_CTRL1:
+ case WSA884X_OTP_STAT:
+ case WSA884X_WAVG_STA:
+ case WSA884X_INTR_STATUS0:
+ case WSA884X_INTR_STATUS1:
+ case WSA884X_ATE_TEST_MODE:
+ case WSA884X_SPARE_R:
+ return true;
+ }
+ return false;
+}
+
+static bool wsa884x_writeable_register(struct device *dev, unsigned int reg)
+{
+ return !wsa884x_readonly_register(dev, reg);
+}
+
+static bool wsa884x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA884X_ANA_WO_CTL_0:
+ case WSA884X_ANA_WO_CTL_1:
+ return true;
+ }
+ return wsa884x_readonly_register(dev, reg);
+}
+
+static const struct regmap_config wsa884x_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wsa884x_defaults,
+ .max_register = WSA884X_MAX_REGISTER,
+ .num_reg_defaults = ARRAY_SIZE(wsa884x_defaults),
+ .volatile_reg = wsa884x_volatile_register,
+ .writeable_reg = wsa884x_writeable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .use_single_read = true,
+};
+
+static const struct reg_sequence wsa884x_reg_init[] = {
+ { WSA884X_BOP2_PROG, FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_VTH_MASK, 0x6) |
+ FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_HYST_MASK, 0x6) },
+ { WSA884X_REF_CTRL, (0xd2 & ~WSA884X_REF_CTRL_BG_RDY_SEL_MASK) |
+ FIELD_PREP_CONST(WSA884X_REF_CTRL_BG_RDY_SEL_MASK, 0x1) },
+ /*
+ * Downstream suggests for batteries different than 1-Stacked (1S):
+ * { WSA884X_TOP_CTRL1, 0xd3 & ~WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK },
+ */
+ { WSA884X_STB_CTRL1, (0x42 & ~WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK) |
+ FIELD_PREP_CONST(WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK, 0xd) },
+ { WSA884X_CURRENT_LIMIT, (0x54 & ~WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK) |
+ FIELD_PREP_CONST(WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, 0x9) },
+ { WSA884X_ZX_CTRL1, (0xf0 & ~WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK) |
+ FIELD_PREP_CONST(WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK, 0x3) },
+ { WSA884X_ILIM_CTRL1, (0xe2 & ~WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK) |
+ FIELD_PREP_CONST(WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK, 0x3) },
+ { WSA884X_CKWD_CTL_1, FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK, 0x0) |
+ FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK, 0x13) },
+ { WSA884X_PA_FSM_CTL1, (0xfe & ~WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK) |
+ FIELD_PREP_CONST(WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK, 0x4) }, /* == 0xfe */
+ { WSA884X_VBAT_THRM_FLT_CTL, (0x7f & ~WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK) |
+ FIELD_PREP_CONST(WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK, 0x4) },
+ { WSA884X_VBAT_CAL_CTL, FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_RESERVE_MASK, 0x2) |
+ FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK, 0x1) },
+ { WSA884X_BOP_DEGLITCH_CTL, FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK, 0x8) |
+ FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK, 0x1) },
+ { WSA884X_CDC_SPK_DSM_A2_0, 0x0a },
+ { WSA884X_CDC_SPK_DSM_A2_1, 0x08 },
+ { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 },
+ { WSA884X_CDC_SPK_DSM_A3_1, 0x07 },
+ { WSA884X_CDC_SPK_DSM_A4_0, 0x79 },
+ { WSA884X_CDC_SPK_DSM_A5_0, 0x0b },
+ { WSA884X_CDC_SPK_DSM_A6_0, 0x8a },
+ { WSA884X_CDC_SPK_DSM_A7_0, 0x9b },
+ { WSA884X_CDC_SPK_DSM_C_0, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK, 0x6) |
+ FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK, 0x8) },
+ { WSA884X_CDC_SPK_DSM_C_2, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK, 0xf) },
+ { WSA884X_CDC_SPK_DSM_C_3, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK, 0x20) },
+ { WSA884X_CDC_SPK_DSM_R1, 0x83 },
+ { WSA884X_CDC_SPK_DSM_R2, 0x7f },
+ { WSA884X_CDC_SPK_DSM_R3, 0x9d },
+ { WSA884X_CDC_SPK_DSM_R4, 0x82 },
+ { WSA884X_CDC_SPK_DSM_R5, 0x8b },
+ { WSA884X_CDC_SPK_DSM_R6, 0x9b },
+ { WSA884X_CDC_SPK_DSM_R7, 0x3f },
+ /* Speaker mode by default */
+ { WSA884X_DRE_CTL_0, FIELD_PREP_CONST(WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x7) },
+ { WSA884X_CLSH_CTL_0, (0x37 & ~WSA884X_CLSH_CTL_0_DLY_CODE_MASK) |
+ FIELD_PREP_CONST(WSA884X_CLSH_CTL_0_DLY_CODE_MASK, 0x6) },
+ /*
+ * WSA884X_CLSH_VTH values for speaker mode with G_21_DB system gain,
+ * battery 1S and rload 8 Ohms.
+ */
+ { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(863), },
+ { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(918), },
+ { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(980), },
+ { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(1043), },
+ { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(1098), },
+ { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(1137), },
+ { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1184), },
+ { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1239), },
+ { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1278), },
+ { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1380), },
+ { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1482), },
+ { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1584), },
+ { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1663), },
+ { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1780), },
+ { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000), },
+ { WSA884X_ANA_WO_CTL_1, 0x00 },
+ { WSA884X_OTP_REG_38, 0x00 },
+ { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) },
+};
+
+static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x)
+{
+ struct regmap *regmap = wsa884x->regmap;
+ unsigned int min_gain, igain, vgain, comp_offset;
+
+ /*
+ * Downstream sets gain parameters customized per boards per use-case.
+ * Choose here some sane values matching knowon users, like QRD8550
+ * board:.
+ *
+ * Values match here downstream:
+ * For WSA884X_RECEIVER - G_7P5_DB system gain
+ * For WSA884X_SPEAKER - G_21_DB system gain
+ */
+ if (wsa884x->dev_mode == WSA884X_RECEIVER) {
+ comp_offset = COMP_OFFSET4;
+ min_gain = G_M6_DB;
+ igain = ISENSE_18_DB;
+ vgain = VSENSE_M12_DB;
+ } else {
+ /* WSA884X_SPEAKER */
+ comp_offset = COMP_OFFSET0;
+ min_gain = G_0_DB;
+ igain = ISENSE_12_DB;
+ vgain = VSENSE_M24_DB;
+ }
+
+ regmap_update_bits(regmap, WSA884X_ISENSE2,
+ WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK,
+ FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain));
+ regmap_update_bits(regmap, WSA884X_VSENSE1,
+ WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK,
+ FIELD_PREP(WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, vgain));
+ regmap_update_bits(regmap, WSA884X_GAIN_RAMPING_MIN,
+ WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK,
+ FIELD_PREP(WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, min_gain));
+
+ if (wsa884x->port_enable[WSA884X_PORT_COMP]) {
+ regmap_update_bits(regmap, WSA884X_DRE_CTL_0,
+ WSA884X_DRE_CTL_0_OFFSET_MASK,
+ FIELD_PREP(WSA884X_DRE_CTL_0_OFFSET_MASK, comp_offset));
+
+ regmap_update_bits(regmap, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0));
+ } else {
+ regmap_update_bits(regmap, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x1));
+ }
+}
+
+static void wsa884x_init(struct wsa884x_priv *wsa884x)
+{
+ unsigned int wo_ctl_0;
+ unsigned int variant = 0;
+
+ if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant))
+ variant = variant & WSA884X_OTP_REG_0_ID_MASK;
+
+ regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init,
+ ARRAY_SIZE(wsa884x_reg_init));
+
+ wo_ctl_0 = 0xc;
+ wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK,
+ WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER);
+ /* Assume that compander is enabled by default unless it is haptics sku */
+ if (variant == WSA884X_OTP_ID_WSA8845H)
+ wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK,
+ WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB);
+ else
+ wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK,
+ WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB);
+ regmap_write(wsa884x->regmap, WSA884X_ANA_WO_CTL_0, wo_ctl_0);
+
+ wsa884x_set_gain_parameters(wsa884x);
+
+ wsa884x->hw_init = false;
+}
+
+static int wsa884x_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ if (status == SDW_SLAVE_UNATTACHED) {
+ wsa884x->hw_init = false;
+ regcache_cache_only(wsa884x->regmap, true);
+ regcache_mark_dirty(wsa884x->regmap);
+ return 0;
+ }
+
+ if (wsa884x->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ regcache_cache_only(wsa884x->regmap, false);
+ ret = regcache_sync(wsa884x->regmap);
+ if (ret < 0) {
+ dev_err(&slave->dev, "Cannot sync regmap cache\n");
+ return ret;
+ }
+
+ wsa884x_init(wsa884x);
+
+ return 0;
+}
+
+static int wsa884x_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev);
+
+ if (state == SDW_OPS_PORT_POST_PREP)
+ wsa884x->port_prepared[prepare_ch->num - 1] = true;
+ else
+ wsa884x->port_prepared[prepare_ch->num - 1] = false;
+
+ return 0;
+}
+
+static const struct sdw_slave_ops wsa884x_slave_ops = {
+ .update_status = wsa884x_update_status,
+ .port_prep = wsa884x_port_prep,
+};
+
+static int wsa884x_dev_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = wsa884x->dev_mode;
+
+ return 0;
+}
+
+static int wsa884x_dev_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component);
+
+ if (wsa884x->dev_mode == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wsa884x->dev_mode = ucontrol->value.enumerated.item[0];
+
+ return 1;
+}
+
+static int wsa884x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ ucontrol->value.integer.value[0] = wsa884x->port_enable[portidx];
+
+ return 0;
+}
+
+static int wsa884x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ if (ucontrol->value.integer.value[0]) {
+ if (wsa884x->port_enable[portidx])
+ return 0;
+
+ wsa884x->port_enable[portidx] = true;
+ } else {
+ if (!wsa884x->port_enable[portidx])
+ return 0;
+
+ wsa884x->port_enable[portidx] = false;
+ }
+
+ return 1;
+}
+
+static int wsa884x_codec_probe(struct snd_soc_component *comp)
+{
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, wsa884x->regmap);
+
+ return 0;
+}
+
+static void wsa884x_spkr_post_pmu(struct snd_soc_component *component,
+ struct wsa884x_priv *wsa884x)
+{
+ unsigned int curr_limit, curr_ovrd_en;
+
+ wsa884x_set_gain_parameters(wsa884x);
+ if (wsa884x->dev_mode == WSA884X_RECEIVER) {
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_0,
+ WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x3);
+ snd_soc_component_write_field(component, WSA884X_CDC_PATH_MODE,
+ WSA884X_CDC_PATH_MODE_RXD_MODE_MASK,
+ 0x1);
+ snd_soc_component_write_field(component, WSA884X_PWM_CLK_CTL,
+ WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK,
+ 0x1);
+ } else {
+ /* WSA884X_SPEAKER */
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_0,
+ WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0xf);
+ }
+
+ if (wsa884x->port_enable[WSA884X_PORT_PBR]) {
+ curr_ovrd_en = 0x0;
+ curr_limit = 0x15;
+ } else {
+ curr_ovrd_en = 0x1;
+ if (wsa884x->dev_mode == WSA884X_RECEIVER)
+ curr_limit = 0x9;
+ else
+ curr_limit = 0x15;
+ }
+ snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT,
+ WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK,
+ curr_ovrd_en);
+ snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT,
+ WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK,
+ curr_limit);
+}
+
+static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ mutex_lock(&wsa884x->sp_lock);
+ wsa884x->pa_on = true;
+ mutex_unlock(&wsa884x->sp_lock);
+
+ wsa884x_spkr_post_pmu(component, wsa884x);
+
+ snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
+ WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
+ 0x1);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
+ WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
+ 0x0);
+
+ mutex_lock(&wsa884x->sp_lock);
+ wsa884x->pa_on = false;
+ mutex_unlock(&wsa884x->sp_lock);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wsa884x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_SPK("SPKR", wsa884x_spkr_event),
+};
+
+static const DECLARE_TLV_DB_SCALE(pa_gain, -900, 150, -900);
+
+static const struct snd_kcontrol_new wsa884x_snd_controls[] = {
+ SOC_SINGLE_RANGE_TLV("PA Volume", WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT,
+ 0x0, 0x1f, 1, pa_gain),
+ SOC_ENUM_EXT("WSA MODE", wsa884x_dev_mode_enum,
+ wsa884x_dev_mode_get, wsa884x_dev_mode_put),
+ SOC_SINGLE_EXT("DAC Switch", WSA884X_PORT_DAC, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("COMP Switch", WSA884X_PORT_COMP, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("BOOST Switch", WSA884X_PORT_BOOST, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("PBR Switch", WSA884X_PORT_PBR, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("VISENSE Switch", WSA884X_PORT_VISENSE, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("CPS Switch", WSA884X_PORT_CPS, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+};
+
+static const struct snd_soc_dapm_route wsa884x_audio_map[] = {
+ {"SPKR", NULL, "IN"},
+};
+
+static const struct snd_soc_component_driver wsa884x_component_drv = {
+ .name = "WSA884x",
+ .probe = wsa884x_codec_probe,
+ .controls = wsa884x_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa884x_snd_controls),
+ .dapm_widgets = wsa884x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa884x_dapm_widgets),
+ .dapm_routes = wsa884x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map),
+};
+
+static int wsa884x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+ int i;
+
+ wsa884x->active_ports = 0;
+ for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) {
+ if (!wsa884x->port_enable[i])
+ continue;
+
+ wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i];
+ wsa884x->active_ports++;
+ }
+
+ wsa884x->sconfig.frame_rate = params_rate(params);
+
+ return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig,
+ wsa884x->port_config, wsa884x->active_ports,
+ wsa884x->sruntime);
+}
+
+static int wsa884x_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+
+ sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime);
+
+ return 0;
+}
+
+static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ 0x0);
+ snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
+ WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
+ 0x0);
+
+ } else {
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ 0x1);
+ snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
+ WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
+ 0x1);
+ }
+
+ return 0;
+}
+
+static int wsa884x_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+
+ wsa884x->sruntime = stream;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wsa884x_dai_ops = {
+ .hw_params = wsa884x_hw_params,
+ .hw_free = wsa884x_hw_free,
+ .mute_stream = wsa884x_mute_stream,
+ .set_stream = wsa884x_set_stream,
+ .mute_unmute_on_trigger = true,
+};
+
+static struct snd_soc_dai_driver wsa884x_dais[] = {
+ {
+ .name = "SPKR",
+ .playback = {
+ .stream_name = "SPKR Playback",
+ .rates = WSA884X_RATES | WSA884X_FRAC_RATES,
+ .formats = WSA884X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &wsa884x_dai_ops,
+ },
+};
+
+static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
+{
+ unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0;
+ unsigned int dmeas_msb = 0, dmeas_lsb = 0;
+ int d1, d2, dmeas;
+ unsigned int mask;
+ long val;
+ int ret;
+
+ guard(mutex)(&wsa884x->sp_lock);
+
+ if (wsa884x->pa_on) {
+ /*
+ * Reading temperature is possible only when Power Amplifier is
+ * off. Report last cached data.
+ */
+ *temp = wsa884x->temperature * 1000;
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(wsa884x->dev);
+ if (ret < 0)
+ return ret;
+
+ mask = WSA884X_PA_FSM_BYP0_DC_CAL_EN_MASK |
+ WSA884X_PA_FSM_BYP0_CLK_WD_EN_MASK |
+ WSA884X_PA_FSM_BYP0_BG_EN_MASK |
+ WSA884X_PA_FSM_BYP0_D_UNMUTE_MASK |
+ WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_MASK |
+ WSA884X_PA_FSM_BYP0_TSADC_EN_MASK;
+ /*
+ * Here and further do not care about read or update failures.
+ * For example, before turning on Power Amplifier for the first
+ * time, reading WSA884X_TEMP_DIN_MSB will always return 0.
+ * Instead, check if returned value is within reasonable
+ * thresholds.
+ */
+ regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_BYP0, mask, mask);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_TADC_VALUE_CTL,
+ WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0));
+
+ regmap_read(wsa884x->regmap, WSA884X_TEMP_DIN_MSB, &dmeas_msb);
+ regmap_read(wsa884x->regmap, WSA884X_TEMP_DIN_LSB, &dmeas_lsb);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_TADC_VALUE_CTL,
+ WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1));
+
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_1, &d1_msb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_2, &d1_lsb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_3, &d2_msb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_4, &d2_lsb);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_BYP0, mask, 0x0);
+
+ dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6;
+ d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6;
+ d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6;
+
+ if (d1 == d2) {
+ /* Incorrect data in OTP? */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ val = WSA884X_T1_TEMP + (((dmeas - d1) * (WSA884X_T2_TEMP - WSA884X_T1_TEMP))/(d2 - d1));
+
+ dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n",
+ val, dmeas, d1, d2);
+
+ if ((val > WSA884X_LOW_TEMP_THRESHOLD) &&
+ (val < WSA884X_HIGH_TEMP_THRESHOLD)) {
+ wsa884x->temperature = val;
+ *temp = val * 1000;
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+
+out:
+ pm_runtime_put_autosuspend(wsa884x->dev);
+
+ return ret;
+}
+
+static umode_t wsa884x_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa884x_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = wsa884x_get_temp(dev_get_drvdata(dev), temp);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const wsa884x_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops wsa884x_hwmon_ops = {
+ .is_visible = wsa884x_hwmon_is_visible,
+ .read = wsa884x_hwmon_read,
+};
+
+static const struct hwmon_chip_info wsa884x_hwmon_chip_info = {
+ .ops = &wsa884x_hwmon_ops,
+ .info = wsa884x_hwmon_info,
+};
+
+static void wsa884x_reset_powerdown(void *data)
+{
+ struct wsa884x_priv *wsa884x = data;
+
+ if (wsa884x->sd_reset)
+ reset_control_assert(wsa884x->sd_reset);
+ else
+ gpiod_direction_output(wsa884x->sd_n, 1);
+}
+
+static void wsa884x_reset_deassert(struct wsa884x_priv *wsa884x)
+{
+ if (wsa884x->sd_reset)
+ reset_control_deassert(wsa884x->sd_reset);
+ else
+ gpiod_direction_output(wsa884x->sd_n, 0);
+}
+
+static void wsa884x_regulator_disable(void *data)
+{
+ regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data);
+}
+
+static int wsa884x_get_reset(struct device *dev, struct wsa884x_priv *wsa884x)
+{
+ wsa884x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(wsa884x->sd_reset))
+ return dev_err_probe(dev, PTR_ERR(wsa884x->sd_reset),
+ "Failed to get reset\n");
+ else if (wsa884x->sd_reset)
+ return 0;
+ /*
+ * else: NULL, so use the backwards compatible way for powerdown-gpios,
+ * which does not handle sharing GPIO properly.
+ */
+ wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wsa884x->sd_n))
+ return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n),
+ "Shutdown Control GPIO not found\n");
+
+ return 0;
+}
+
+static int wsa884x_probe(struct sdw_slave *pdev,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct wsa884x_priv *wsa884x;
+ unsigned int i;
+ int ret;
+
+ wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL);
+ if (!wsa884x)
+ return -ENOMEM;
+
+ mutex_init(&wsa884x->sp_lock);
+
+ for (i = 0; i < WSA884X_SUPPLIES_NUM; i++)
+ wsa884x->supplies[i].supply = wsa884x_supply_name[i];
+
+ ret = devm_regulator_bulk_get(dev, WSA884X_SUPPLIES_NUM,
+ wsa884x->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(WSA884X_SUPPLIES_NUM, wsa884x->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(dev, wsa884x_regulator_disable,
+ wsa884x->supplies);
+ if (ret)
+ return ret;
+
+ ret = wsa884x_get_reset(dev, wsa884x);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, wsa884x);
+ wsa884x->slave = pdev;
+ wsa884x->dev = dev;
+ wsa884x->dev_mode = WSA884X_SPEAKER;
+ wsa884x->sconfig.ch_count = 1;
+ wsa884x->sconfig.bps = 1;
+ wsa884x->sconfig.direction = SDW_DATA_DIR_RX;
+ wsa884x->sconfig.type = SDW_STREAM_PDM;
+
+ /*
+ * Port map index starts with 0, however the data port for this codec
+ * are from index 1
+ */
+ if (of_property_read_u32_array(dev->of_node, "qcom,port-mapping", &pdev->m_port_map[1],
+ WSA884X_MAX_SWR_PORTS))
+ dev_dbg(dev, "Static Port mapping not specified\n");
+
+ pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.simple_clk_stop_capable = true;
+ pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop;
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ wsa884x_reset_deassert(wsa884x);
+ ret = devm_add_action_or_reset(dev, wsa884x_reset_powerdown, wsa884x);
+ if (ret)
+ return ret;
+
+ wsa884x->regmap = devm_regmap_init_sdw(pdev, &wsa884x_regmap_config);
+ if (IS_ERR(wsa884x->regmap))
+ return dev_err_probe(dev, PTR_ERR(wsa884x->regmap),
+ "regmap_init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(wsa884x->regmap, true);
+ wsa884x->hw_init = true;
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(dev, "wsa884x",
+ wsa884x,
+ &wsa884x_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon))
+ return dev_err_probe(dev, PTR_ERR(hwmon),
+ "Failed to register hwmon sensor\n");
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return devm_snd_soc_register_component(dev,
+ &wsa884x_component_drv,
+ wsa884x_dais,
+ ARRAY_SIZE(wsa884x_dais));
+}
+
+static int wsa884x_runtime_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int wsa884x_runtime_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wsa884x_pm_ops = {
+ RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id wsa884x_swr_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x204, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id);
+
+static struct sdw_driver wsa884x_codec_driver = {
+ .driver = {
+ .name = "wsa884x-codec",
+ .pm = pm_ptr(&wsa884x_pm_ops),
+ },
+ .probe = wsa884x_probe,
+ .ops = &wsa884x_slave_ops,
+ .id_table = wsa884x_swr_id,
+};
+module_sdw_driver(wsa884x_codec_driver);
+
+MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
+MODULE_DESCRIPTION("WSA884x codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c
new file mode 100644
index 000000000000..7de4014e626d
--- /dev/null
+++ b/sound/soc/codecs/zl38060.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Codec driver for Microsemi ZL38060 Connected Home Audio Processor.
+//
+// Copyright(c) 2020 Sven Van Asbroeck
+
+// The ZL38060 is very flexible and configurable. This driver implements only a
+// tiny subset of the chip's possible configurations:
+//
+// - DSP block bypassed: DAI routed straight to DACs
+// microphone routed straight to DAI
+// - chip's internal clock is driven by a 12 MHz external crystal
+// - chip's DAI connected to CPU is I2S, and bit + frame clock master
+// - chip must be strapped for "host boot": in this mode, firmware will be
+// provided by this driver.
+
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/ihex.h>
+
+#include <sound/pcm_params.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "zl38060"
+
+#define ZL38_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_48000)
+#define ZL38_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+#define HBI_FIRMWARE_PAGE 0xFF
+#define ZL38_MAX_RAW_XFER 0x100
+
+#define REG_TDMA_CFG_CLK 0x0262
+#define CFG_CLK_PCLK_SHIFT 4
+#define CFG_CLK_PCLK_MASK (0x7ff << CFG_CLK_PCLK_SHIFT)
+#define CFG_CLK_PCLK(bits) ((bits - 1) << CFG_CLK_PCLK_SHIFT)
+#define CFG_CLK_MASTER BIT(15)
+#define CFG_CLK_FSRATE_MASK 0x7
+#define CFG_CLK_FSRATE_8KHZ 0x1
+#define CFG_CLK_FSRATE_16KHZ 0x2
+#define CFG_CLK_FSRATE_48KHZ 0x6
+
+#define REG_CLK_CFG 0x0016
+#define CLK_CFG_SOURCE_XTAL BIT(15)
+
+#define REG_CLK_STATUS 0x0014
+#define CLK_STATUS_HWRST BIT(0)
+
+#define REG_PARAM_RESULT 0x0034
+#define PARAM_RESULT_READY 0xD3D3
+
+#define REG_PG255_BASE_HI 0x000C
+#define REG_PG255_OFFS(addr) ((HBI_FIRMWARE_PAGE << 8) | (addr & 0xFF))
+#define REG_FWR_EXEC 0x012C
+
+#define REG_CMD 0x0032
+#define REG_HW_REV 0x0020
+#define REG_FW_PROD 0x0022
+#define REG_FW_REV 0x0024
+
+#define REG_SEMA_FLAGS 0x0006
+#define SEMA_FLAGS_BOOT_CMD BIT(0)
+#define SEMA_FLAGS_APP_REBOOT BIT(1)
+
+#define REG_HW_REV 0x0020
+#define REG_FW_PROD 0x0022
+#define REG_FW_REV 0x0024
+#define REG_GPIO_DIR 0x02DC
+#define REG_GPIO_DAT 0x02DA
+
+#define BOOTCMD_LOAD_COMPLETE 0x000D
+#define BOOTCMD_FW_GO 0x0008
+
+#define FIRMWARE_MAJOR 2
+#define FIRMWARE_MINOR 2
+
+struct zl38_codec_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ bool is_stream_in_use[2];
+ struct gpio_chip *gpio_chip;
+};
+
+static int zl38_fw_issue_command(struct regmap *regmap, u16 cmd)
+{
+ unsigned int val;
+ int err;
+
+ err = regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,
+ !(val & SEMA_FLAGS_BOOT_CMD), 10000,
+ 10000 * 100);
+ if (err)
+ return err;
+ err = regmap_write(regmap, REG_CMD, cmd);
+ if (err)
+ return err;
+ err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_BOOT_CMD,
+ SEMA_FLAGS_BOOT_CMD);
+ if (err)
+ return err;
+
+ return regmap_read_poll_timeout(regmap, REG_CMD, val, !val, 10000,
+ 10000 * 100);
+}
+
+static int zl38_fw_go(struct regmap *regmap)
+{
+ int err;
+
+ err = zl38_fw_issue_command(regmap, BOOTCMD_LOAD_COMPLETE);
+ if (err)
+ return err;
+
+ return zl38_fw_issue_command(regmap, BOOTCMD_FW_GO);
+}
+
+static int zl38_fw_enter_boot_mode(struct regmap *regmap)
+{
+ unsigned int val;
+ int err;
+
+ err = regmap_update_bits(regmap, REG_CLK_STATUS, CLK_STATUS_HWRST,
+ CLK_STATUS_HWRST);
+ if (err)
+ return err;
+
+ return regmap_read_poll_timeout(regmap, REG_PARAM_RESULT, val,
+ val == PARAM_RESULT_READY, 1000, 50000);
+}
+
+static int
+zl38_fw_send_data(struct regmap *regmap, u32 addr, const void *data, u16 len)
+{
+ __be32 addr_base = cpu_to_be32(addr & ~0xFF);
+ int err;
+
+ err = regmap_raw_write(regmap, REG_PG255_BASE_HI, &addr_base,
+ sizeof(addr_base));
+ if (err)
+ return err;
+ return regmap_raw_write(regmap, REG_PG255_OFFS(addr), data, len);
+}
+
+static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data)
+{
+ /* execution address from ihex: 32-bit little endian.
+ * device register expects 32-bit big endian.
+ */
+ u32 addr = le32_to_cpup(data);
+ __be32 baddr = cpu_to_be32(addr);
+
+ return regmap_raw_write(regmap, REG_FWR_EXEC, &baddr, sizeof(baddr));
+}
+
+static int zl38_load_firmware(struct device *dev, struct regmap *regmap)
+{
+ const struct ihex_binrec *rec;
+ const struct firmware *fw;
+ u32 addr;
+ u16 len;
+ int err;
+
+ /* how to get this firmware:
+ * 1. request and download chip firmware from Microsemi
+ * (provided by Microsemi in srec format)
+ * 2. convert downloaded firmware from srec to ihex. Simple tool:
+ * https://gitlab.com/TheSven73/s3-to-irec
+ * 3. convert ihex to binary (.fw) using ihex2fw tool which is included
+ * with the Linux kernel sources
+ */
+ err = request_ihex_firmware(&fw, "zl38060.fw", dev);
+ if (err)
+ return err;
+ err = zl38_fw_enter_boot_mode(regmap);
+ if (err)
+ goto out;
+ rec = (const struct ihex_binrec *)fw->data;
+ while (rec) {
+ addr = be32_to_cpu(rec->addr);
+ len = be16_to_cpu(rec->len);
+ if (addr) {
+ /* regular data ihex record */
+ err = zl38_fw_send_data(regmap, addr, rec->data, len);
+ } else if (len == 4) {
+ /* execution address ihex record */
+ err = zl38_fw_send_xaddr(regmap, rec->data);
+ } else {
+ err = -EINVAL;
+ }
+ if (err)
+ goto out;
+ /* next ! */
+ rec = ihex_next_binrec(rec);
+ }
+ err = zl38_fw_go(regmap);
+
+out:
+ release_firmware(fw);
+ return err;
+}
+
+
+static int zl38_software_reset(struct regmap *regmap)
+{
+ unsigned int val;
+ int err;
+
+ err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_APP_REBOOT,
+ SEMA_FLAGS_APP_REBOOT);
+ if (err)
+ return err;
+
+ /* wait for host bus interface to settle.
+ * Not sure if this is required: Microsemi's vendor driver does this,
+ * but the firmware manual does not mention it. Leave it in, there's
+ * little downside, apart from a slower reset.
+ */
+ msleep(50);
+
+ return regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,
+ !(val & SEMA_FLAGS_APP_REBOOT), 10000,
+ 10000 * 100);
+}
+
+static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
+ int err;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* firmware default is normal i2s */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* firmware default is normal bitclock and frame */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* always 32 bits per frame (= 16 bits/channel, 2 channels) */
+ err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
+ CFG_CLK_MASTER | CFG_CLK_PCLK_MASK,
+ CFG_CLK_MASTER | CFG_CLK_PCLK(32));
+ if (err)
+ return err;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zl38_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fsrate;
+ int err;
+
+ /* We cannot change hw_params while the dai is already in use - the
+ * software reset will corrupt the audio. However, this is not required,
+ * as the chip's TDM buses are fully symmetric, which mandates identical
+ * rates, channels, and samplebits for record and playback.
+ */
+ if (priv->is_stream_in_use[!tx])
+ goto skip_setup;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fsrate = CFG_CLK_FSRATE_8KHZ;
+ break;
+ case 16000:
+ fsrate = CFG_CLK_FSRATE_16KHZ;
+ break;
+ case 48000:
+ fsrate = CFG_CLK_FSRATE_48KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,
+ CFG_CLK_FSRATE_MASK, fsrate);
+ if (err)
+ return err;
+
+ /* chip requires a software reset to apply audio register changes */
+ err = zl38_software_reset(priv->regmap);
+ if (err)
+ return err;
+
+skip_setup:
+ priv->is_stream_in_use[tx] = true;
+
+ return 0;
+}
+
+static int zl38_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ priv->is_stream_in_use[tx] = false;
+
+ return 0;
+}
+
+/* stereo bypass with no AEC */
+static const struct reg_sequence cp_config_stereo_bypass[] = {
+ /* interconnects must be programmed first */
+ { 0x0210, 0x0005 }, /* DAC1 in <= I2S1-L */
+ { 0x0212, 0x0006 }, /* DAC2 in <= I2S1-R */
+ { 0x0214, 0x0001 }, /* I2S1-L in <= MIC1 */
+ { 0x0216, 0x0001 }, /* I2S1-R in <= MIC1 */
+ { 0x0224, 0x0000 }, /* AEC-S in <= n/a */
+ { 0x0226, 0x0000 }, /* AEC-R in <= n/a */
+ /* output enables must be programmed next */
+ { 0x0202, 0x000F }, /* enable I2S1 + DAC */
+};
+
+static const struct snd_soc_dai_ops zl38_dai_ops = {
+ .set_fmt = zl38_set_fmt,
+ .hw_params = zl38_hw_params,
+ .hw_free = zl38_hw_free,
+};
+
+static struct snd_soc_dai_driver zl38_dai = {
+ .name = "zl38060-tdma",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ZL38_RATES,
+ .formats = ZL38_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ZL38_RATES,
+ .formats = ZL38_FORMATS,
+ },
+ .ops = &zl38_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .symmetric_channels = 1,
+};
+
+static const struct snd_soc_dapm_widget zl38_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("DAC1"),
+ SND_SOC_DAPM_OUTPUT("DAC2"),
+
+ SND_SOC_DAPM_INPUT("DMICL"),
+};
+
+static const struct snd_soc_dapm_route zl38_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+
+ { "Capture", NULL, "DMICL" },
+};
+
+static const struct snd_soc_component_driver zl38_component_dev = {
+ .dapm_widgets = zl38_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(zl38_dapm_widgets),
+ .dapm_routes = zl38_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(zl38_dapm_routes),
+ .endianness = 1,
+};
+
+static int chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct regmap *regmap = gpiochip_get_data(c);
+ unsigned int mask = BIT(offset);
+
+ return regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0);
+}
+
+static int chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ struct regmap *regmap = gpiochip_get_data(c);
+ unsigned int mask = BIT(offset);
+ unsigned int val;
+ int err;
+
+ err = regmap_read(regmap, REG_GPIO_DAT, &val);
+ if (err)
+ return err;
+
+ return !!(val & mask);
+}
+
+static int chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ struct regmap *regmap = gpiochip_get_data(c);
+ unsigned int mask = BIT(offset);
+
+ return regmap_update_bits(regmap, REG_GPIO_DIR, mask, 0);
+}
+
+static int
+chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct regmap *regmap = gpiochip_get_data(c);
+ unsigned int mask = BIT(offset);
+ int ret;
+
+ ret = chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask);
+}
+
+static const struct gpio_chip template_chip = {
+ .owner = THIS_MODULE,
+ .label = DRV_NAME,
+
+ .base = -1,
+ .ngpio = 14,
+ .direction_input = chip_direction_input,
+ .direction_output = chip_direction_output,
+ .get = chip_gpio_get,
+ .set = chip_gpio_set,
+
+ .can_sleep = true,
+};
+
+static int zl38_check_revision(struct device *dev, struct regmap *regmap)
+{
+ unsigned int hwrev, fwprod, fwrev;
+ int fw_major, fw_minor, fw_micro;
+ int err;
+
+ err = regmap_read(regmap, REG_HW_REV, &hwrev);
+ if (err)
+ return err;
+ err = regmap_read(regmap, REG_FW_PROD, &fwprod);
+ if (err)
+ return err;
+ err = regmap_read(regmap, REG_FW_REV, &fwrev);
+ if (err)
+ return err;
+
+ fw_major = (fwrev >> 12) & 0xF;
+ fw_minor = (fwrev >> 8) & 0xF;
+ fw_micro = fwrev & 0xFF;
+ dev_info(dev, "hw rev 0x%x, fw product code %d, firmware rev %d.%d.%d",
+ hwrev & 0x1F, fwprod, fw_major, fw_minor, fw_micro);
+
+ if (fw_major != FIRMWARE_MAJOR || fw_minor < FIRMWARE_MINOR) {
+ dev_err(dev, "unsupported firmware. driver supports %d.%d",
+ FIRMWARE_MAJOR, FIRMWARE_MINOR);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zl38_bus_read(void *context,
+ const void *reg_buf, size_t reg_size,
+ void *val_buf, size_t val_size)
+{
+ struct spi_device *spi = context;
+ const u8 *reg_buf8 = reg_buf;
+ size_t len = 0;
+ u8 offs, page;
+ u8 txbuf[4];
+
+ if (reg_size != 2 || val_size > ZL38_MAX_RAW_XFER)
+ return -EINVAL;
+
+ offs = reg_buf8[1] >> 1;
+ page = reg_buf8[0];
+
+ if (page) {
+ txbuf[len++] = 0xFE;
+ txbuf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;
+ txbuf[len++] = offs;
+ txbuf[len++] = val_size / 2 - 1;
+ } else {
+ txbuf[len++] = offs | 0x80;
+ txbuf[len++] = val_size / 2 - 1;
+ }
+
+ return spi_write_then_read(spi, txbuf, len, val_buf, val_size);
+}
+
+static int zl38_bus_write(void *context, const void *data, size_t count)
+{
+ struct spi_device *spi = context;
+ u8 buf[4 + ZL38_MAX_RAW_XFER];
+ size_t val_len, len = 0;
+ const u8 *data8 = data;
+ u8 offs, page;
+
+ if (count > (2 + ZL38_MAX_RAW_XFER) || count < 4)
+ return -EINVAL;
+ val_len = count - 2;
+ offs = data8[1] >> 1;
+ page = data8[0];
+
+ if (page) {
+ buf[len++] = 0xFE;
+ buf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;
+ buf[len++] = offs;
+ buf[len++] = (val_len / 2 - 1) | 0x80;
+ } else {
+ buf[len++] = offs | 0x80;
+ buf[len++] = (val_len / 2 - 1) | 0x80;
+ }
+ memcpy(buf + len, data8 + 2, val_len);
+ len += val_len;
+
+ return spi_write(spi, buf, len);
+}
+
+static const struct regmap_bus zl38_regmap_bus = {
+ .read = zl38_bus_read,
+ .write = zl38_bus_write,
+ .max_raw_write = ZL38_MAX_RAW_XFER,
+ .max_raw_read = ZL38_MAX_RAW_XFER,
+};
+
+static const struct regmap_config zl38_regmap_conf = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_stride = 2,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int zl38_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct zl38_codec_priv *priv;
+ struct gpio_desc *reset_gpio;
+ int err;
+
+ /* get the chip to a known state by putting it in reset */
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
+ if (reset_gpio) {
+ /* datasheet: need > 10us for a digital + analog reset */
+ usleep_range(15, 50);
+ /* take the chip out of reset */
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ /* datasheet: need > 3ms for digital section to become stable */
+ usleep_range(3000, 10000);
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ dev_set_drvdata(dev, priv);
+ priv->regmap = devm_regmap_init(dev, &zl38_regmap_bus, spi,
+ &zl38_regmap_conf);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ err = zl38_load_firmware(dev, priv->regmap);
+ if (err)
+ return err;
+
+ err = zl38_check_revision(dev, priv->regmap);
+ if (err)
+ return err;
+
+ priv->gpio_chip = devm_kmemdup(dev, &template_chip,
+ sizeof(template_chip), GFP_KERNEL);
+ if (!priv->gpio_chip)
+ return -ENOMEM;
+ priv->gpio_chip->parent = dev;
+ err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap);
+ if (err)
+ return err;
+
+ /* setup the cross-point switch for stereo bypass */
+ err = regmap_multi_reg_write(priv->regmap, cp_config_stereo_bypass,
+ ARRAY_SIZE(cp_config_stereo_bypass));
+ if (err)
+ return err;
+ /* setup for 12MHz crystal connected to the chip */
+ err = regmap_update_bits(priv->regmap, REG_CLK_CFG, CLK_CFG_SOURCE_XTAL,
+ CLK_CFG_SOURCE_XTAL);
+ if (err)
+ return err;
+
+ return devm_snd_soc_register_component(dev, &zl38_component_dev,
+ &zl38_dai, 1);
+}
+
+static const struct of_device_id zl38_dt_ids[] __maybe_unused = {
+ { .compatible = "mscc,zl38060", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, zl38_dt_ids);
+
+static const struct spi_device_id zl38_spi_ids[] = {
+ { "zl38060", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, zl38_spi_ids);
+
+static struct spi_driver zl38060_spi_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(zl38_dt_ids),
+ },
+ .probe = zl38_spi_probe,
+ .id_table = zl38_spi_ids,
+};
+module_spi_driver(zl38060_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ZL38060 driver");
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
deleted file mode 100644
index 3736d9aabc56..000000000000
--- a/sound/soc/davinci/Kconfig
+++ /dev/null
@@ -1,101 +0,0 @@
-config SND_DAVINCI_SOC
- tristate
- depends on ARCH_DAVINCI
- select SND_EDMA_SOC
-
-config SND_EDMA_SOC
- tristate "SoC Audio for Texas Instruments chips using eDMA"
- depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI
- select SND_SOC_GENERIC_DMAENGINE_PCM
- help
- Say Y or M here if you want audio support for TI SoC which uses eDMA.
- The following line of SoCs are supported by this platform driver:
- - daVinci devices
- - AM335x
- - AM437x/AM438x
-
-config SND_DAVINCI_SOC_I2S
- tristate
-
-config SND_DAVINCI_SOC_MCASP
- tristate "Multichannel Audio Serial Port (McASP) support"
- depends on SND_OMAP_SOC || SND_EDMA_SOC
- help
- Say Y or M here if you want to have support for McASP IP found in
- various Texas Instruments SoCs like:
- - daVinci devices
- - Sitara line of SoCs (AM335x, AM438x, etc)
- - DRA7x devices
-
-config SND_DAVINCI_SOC_VCIF
- tristate
-
-config SND_DAVINCI_SOC_GENERIC_EVM
- tristate
- select SND_SOC_TLV320AIC3X
- select SND_DAVINCI_SOC_MCASP
-
-config SND_AM33XX_SOC_EVM
- tristate "SoC Audio for the AM33XX chip based boards"
- depends on SND_EDMA_SOC && SOC_AM33XX && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
- help
- Say Y or M if you want to add support for SoC audio on AM33XX
- boards using McASP and TLV320AIC3X codec. For example AM335X-EVM,
- AM335X-EVMSK, and BeagelBone with AudioCape boards have this
- setup.
-
-config SND_DAVINCI_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
- depends on SND_EDMA_SOC && I2C
- depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
- select SND_DAVINCI_SOC_GENERIC_EVM
- help
- Say Y if you want to add support for SoC audio on TI
- DaVinci DM6446, DM355 or DM365 EVM platforms.
-
-choice
- prompt "DM365 codec select"
- depends on SND_DAVINCI_SOC_EVM
- depends on MACH_DAVINCI_DM365_EVM
-
-config SND_DM365_AIC3X_CODEC
- tristate "Audio Codec - AIC3101"
- help
- Say Y if you want to add support for AIC3101 audio codec
-
-config SND_DM365_VOICE_CODEC
- tristate "Voice Codec - CQ93VC"
- select MFD_DAVINCI_VOICECODEC
- select SND_DAVINCI_SOC_VCIF
- select SND_SOC_CQ0093VC
- help
- Say Y if you want to add support for SoC On-chip voice codec
-endchoice
-
-config SND_DM6467_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6467 EVM"
- depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
- select SND_SOC_SPDIF
-
- help
- Say Y if you want to add support for SoC audio on TI
-
-config SND_DA830_SOC_EVM
- tristate "SoC Audio support for DA830/OMAP-L137 EVM"
- depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
-
- help
- Say Y if you want to add support for SoC audio on TI
- DA830/OMAP-L137 EVM
-
-config SND_DA850_SOC_EVM
- tristate "SoC Audio support for DA850/OMAP-L138 EVM"
- depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
- help
- Say Y if you want to add support for SoC audio on TI
- DA850/OMAP-L138 EVM
-
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
deleted file mode 100644
index f883933c1a19..000000000000
--- a/sound/soc/davinci/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# DAVINCI Platform Support
-snd-soc-edma-objs := edma-pcm.o
-snd-soc-davinci-i2s-objs := davinci-i2s.o
-snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
-snd-soc-davinci-vcif-objs:= davinci-vcif.o
-
-obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o
-obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
-obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
-obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
-
-# Generic DAVINCI/AM33xx Machine Support
-snd-soc-evm-objs := davinci-evm.o
-
-obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
deleted file mode 100644
index 731fb0d86c6a..000000000000
--- a/sound/soc/davinci/davinci-evm.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * ASoC driver for TI DAVINCI EVM platform
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-#include <asm/mach-types.h>
-
-struct snd_soc_card_drvdata_davinci {
- struct clk *mclk;
- unsigned sysclk;
-};
-
-static int evm_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *soc_card = rtd->card;
- struct snd_soc_card_drvdata_davinci *drvdata =
- snd_soc_card_get_drvdata(soc_card);
-
- if (drvdata->mclk)
- return clk_prepare_enable(drvdata->mclk);
-
- return 0;
-}
-
-static void evm_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *soc_card = rtd->card;
- struct snd_soc_card_drvdata_davinci *drvdata =
- snd_soc_card_get_drvdata(soc_card);
-
- if (drvdata->mclk)
- clk_disable_unprepare(drvdata->mclk);
-}
-
-static int evm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_card *soc_card = rtd->card;
- int ret = 0;
- unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
- snd_soc_card_get_drvdata(soc_card))->sysclk;
-
- /* set the codec system clock */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- /* set the CPU system clock */
- ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops evm_ops = {
- .startup = evm_startup,
- .shutdown = evm_shutdown,
- .hw_params = evm_hw_params,
-};
-
-/* davinci-evm machine dapm widgets */
-static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-/* davinci-evm machine audio_mapnections to the codec pins */
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Headphone connected to HPLOUT, HPROUT */
- {"Headphone Jack", NULL, "HPLOUT"},
- {"Headphone Jack", NULL, "HPROUT"},
-
- /* Line Out connected to LLOUT, RLOUT */
- {"Line Out", NULL, "LLOUT"},
- {"Line Out", NULL, "RLOUT"},
-
- /* Mic connected to (MIC3L | MIC3R) */
- {"MIC3L", NULL, "Mic Bias"},
- {"MIC3R", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Mic Jack"},
-
- /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
- {"LINE1L", NULL, "Line In"},
- {"LINE2L", NULL, "Line In"},
- {"LINE1R", NULL, "Line In"},
- {"LINE2R", NULL, "Line In"},
-};
-
-/* Logic for a aic3x as connected on a davinci-evm */
-static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- struct device_node *np = card->dev->of_node;
- int ret;
-
- /* Add davinci-evm specific widgets */
- snd_soc_dapm_new_controls(&card->dapm, aic3x_dapm_widgets,
- ARRAY_SIZE(aic3x_dapm_widgets));
-
- if (np) {
- ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
- if (ret)
- return ret;
- } else {
- /* Set up davinci-evm specific audio path audio_map */
- snd_soc_dapm_add_routes(&card->dapm, audio_map,
- ARRAY_SIZE(audio_map));
- }
-
- /* not connected */
- snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT");
- snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM");
- snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM");
-
- return 0;
-}
-
-/* davinci-evm digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link dm6446_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcbsp",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-001b",
- .platform_name = "davinci-mcbsp",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static struct snd_soc_dai_link dm355_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcbsp.1",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-001b",
- .platform_name = "davinci-mcbsp.1",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static struct snd_soc_dai_link dm365_evm_dai = {
-#ifdef CONFIG_SND_DM365_AIC3X_CODEC
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcbsp",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-mcbsp",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
- .name = "Voice Codec - CQ93VC",
- .stream_name = "CQ93",
- .cpu_dai_name = "davinci-vcif",
- .codec_dai_name = "cq93vc-hifi",
- .codec_name = "cq93vc-codec",
- .platform_name = "davinci-vcif",
-#endif
-};
-
-static struct snd_soc_dai_link dm6467_evm_dai[] = {
- {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name= "davinci-mcasp.0",
- .codec_dai_name = "tlv320aic3x-hifi",
- .platform_name = "davinci-mcasp.0",
- .codec_name = "tlv320aic3x-codec.0-001a",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
- },
- {
- .name = "McASP",
- .stream_name = "spdif",
- .cpu_dai_name= "davinci-mcasp.1",
- .codec_dai_name = "dit-hifi",
- .codec_name = "spdif_dit",
- .platform_name = "davinci-mcasp.1",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
- },
-};
-
-static struct snd_soc_dai_link da830_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcasp.1",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-mcasp.1",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static struct snd_soc_dai_link da850_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name= "davinci-mcasp.0",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-mcasp.0",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-/* davinci dm6446 evm audio machine driver */
-/*
- * ASP0 in DM6446 EVM is clocked by U55, as configured by
- * board-dm644x-evm.c using GPIOs from U18. There are six
- * options; here we "know" we use a 48 KHz sample rate.
- */
-static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = {
- .sysclk = 12288000,
-};
-
-static struct snd_soc_card dm6446_snd_soc_card_evm = {
- .name = "DaVinci DM6446 EVM",
- .owner = THIS_MODULE,
- .dai_link = &dm6446_evm_dai,
- .num_links = 1,
- .drvdata = &dm6446_snd_soc_card_drvdata,
-};
-
-/* davinci dm355 evm audio machine driver */
-/* ASP1 on DM355 EVM is clocked by an external oscillator */
-static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = {
- .sysclk = 27000000,
-};
-
-static struct snd_soc_card dm355_snd_soc_card_evm = {
- .name = "DaVinci DM355 EVM",
- .owner = THIS_MODULE,
- .dai_link = &dm355_evm_dai,
- .num_links = 1,
- .drvdata = &dm355_snd_soc_card_drvdata,
-};
-
-/* davinci dm365 evm audio machine driver */
-static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = {
- .sysclk = 27000000,
-};
-
-static struct snd_soc_card dm365_snd_soc_card_evm = {
- .name = "DaVinci DM365 EVM",
- .owner = THIS_MODULE,
- .dai_link = &dm365_evm_dai,
- .num_links = 1,
- .drvdata = &dm365_snd_soc_card_drvdata,
-};
-
-/* davinci dm6467 evm audio machine driver */
-static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = {
- .sysclk = 27000000,
-};
-
-static struct snd_soc_card dm6467_snd_soc_card_evm = {
- .name = "DaVinci DM6467 EVM",
- .owner = THIS_MODULE,
- .dai_link = dm6467_evm_dai,
- .num_links = ARRAY_SIZE(dm6467_evm_dai),
- .drvdata = &dm6467_snd_soc_card_drvdata,
-};
-
-static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = {
- .sysclk = 24576000,
-};
-
-static struct snd_soc_card da830_snd_soc_card = {
- .name = "DA830/OMAP-L137 EVM",
- .owner = THIS_MODULE,
- .dai_link = &da830_evm_dai,
- .num_links = 1,
- .drvdata = &da830_snd_soc_card_drvdata,
-};
-
-static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = {
- .sysclk = 24576000,
-};
-
-static struct snd_soc_card da850_snd_soc_card = {
- .name = "DA850/OMAP-L138 EVM",
- .owner = THIS_MODULE,
- .dai_link = &da850_evm_dai,
- .num_links = 1,
- .drvdata = &da850_snd_soc_card_drvdata,
-};
-
-#if defined(CONFIG_OF)
-
-/*
- * The struct is used as place holder. It will be completely
- * filled with data from dt node.
- */
-static struct snd_soc_dai_link evm_dai_tlv320aic3x = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .codec_dai_name = "tlv320aic3x-hifi",
- .ops = &evm_ops,
- .init = evm_aic3x_init,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static const struct of_device_id davinci_evm_dt_ids[] = {
- {
- .compatible = "ti,da830-evm-audio",
- .data = (void *) &evm_dai_tlv320aic3x,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids);
-
-/* davinci evm audio machine driver */
-static struct snd_soc_card evm_soc_card = {
- .owner = THIS_MODULE,
- .num_links = 1,
-};
-
-static int davinci_evm_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- const struct of_device_id *match =
- of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
- struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data;
- struct snd_soc_card_drvdata_davinci *drvdata = NULL;
- struct clk *mclk;
- int ret = 0;
-
- evm_soc_card.dai_link = dai;
-
- dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0);
- if (!dai->codec_of_node)
- return -EINVAL;
-
- dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0);
- if (!dai->cpu_of_node)
- return -EINVAL;
-
- dai->platform_of_node = dai->cpu_of_node;
-
- evm_soc_card.dev = &pdev->dev;
- ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model");
- if (ret)
- return ret;
-
- mclk = devm_clk_get(&pdev->dev, "mclk");
- if (PTR_ERR(mclk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (IS_ERR(mclk)) {
- dev_dbg(&pdev->dev, "mclk not found.\n");
- mclk = NULL;
- }
-
- drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
- return -ENOMEM;
-
- drvdata->mclk = mclk;
-
- ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
-
- if (ret < 0) {
- if (!drvdata->mclk) {
- dev_err(&pdev->dev,
- "No clock or clock rate defined.\n");
- return -EINVAL;
- }
- drvdata->sysclk = clk_get_rate(drvdata->mclk);
- } else if (drvdata->mclk) {
- unsigned int requestd_rate = drvdata->sysclk;
- clk_set_rate(drvdata->mclk, drvdata->sysclk);
- drvdata->sysclk = clk_get_rate(drvdata->mclk);
- if (drvdata->sysclk != requestd_rate)
- dev_warn(&pdev->dev,
- "Could not get requested rate %u using %u.\n",
- requestd_rate, drvdata->sysclk);
- }
-
- snd_soc_card_set_drvdata(&evm_soc_card, drvdata);
- ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card);
-
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-
- return ret;
-}
-
-static struct platform_driver davinci_evm_driver = {
- .probe = davinci_evm_probe,
- .driver = {
- .name = "davinci_evm",
- .pm = &snd_soc_pm_ops,
- .of_match_table = of_match_ptr(davinci_evm_dt_ids),
- },
-};
-#endif
-
-static struct platform_device *evm_snd_device;
-
-static int __init evm_init(void)
-{
- struct snd_soc_card *evm_snd_dev_data;
- int index;
- int ret;
-
- /*
- * If dtb is there, the devices will be created dynamically.
- * Only register platfrom driver structure.
- */
-#if defined(CONFIG_OF)
- if (of_have_populated_dt())
- return platform_driver_register(&davinci_evm_driver);
-#endif
-
- if (machine_is_davinci_evm()) {
- evm_snd_dev_data = &dm6446_snd_soc_card_evm;
- index = 0;
- } else if (machine_is_davinci_dm355_evm()) {
- evm_snd_dev_data = &dm355_snd_soc_card_evm;
- index = 1;
- } else if (machine_is_davinci_dm365_evm()) {
- evm_snd_dev_data = &dm365_snd_soc_card_evm;
- index = 0;
- } else if (machine_is_davinci_dm6467_evm()) {
- evm_snd_dev_data = &dm6467_snd_soc_card_evm;
- index = 0;
- } else if (machine_is_davinci_da830_evm()) {
- evm_snd_dev_data = &da830_snd_soc_card;
- index = 1;
- } else if (machine_is_davinci_da850_evm()) {
- evm_snd_dev_data = &da850_snd_soc_card;
- index = 0;
- } else
- return -EINVAL;
-
- evm_snd_device = platform_device_alloc("soc-audio", index);
- if (!evm_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
- ret = platform_device_add(evm_snd_device);
- if (ret)
- platform_device_put(evm_snd_device);
-
- return ret;
-}
-
-static void __exit evm_exit(void)
-{
-#if defined(CONFIG_OF)
- if (of_have_populated_dt()) {
- platform_driver_unregister(&davinci_evm_driver);
- return;
- }
-#endif
-
- platform_device_unregister(evm_snd_device);
-}
-
-module_init(evm_init);
-module_exit(evm_exit);
-
-MODULE_AUTHOR("Vladimir Barinov");
-MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c
deleted file mode 100644
index c77d9218795a..000000000000
--- a/sound/soc/davinci/davinci-vcif.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * ALSA SoC Voice Codec Interface for TI DAVINCI processor
- *
- * Copyright (C) 2010 Texas Instruments.
- *
- * 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/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/mfd/davinci_voicecodec.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "edma-pcm.h"
-#include "davinci-i2s.h"
-
-#define MOD_REG_BIT(val, mask, set) do { \
- if (set) { \
- val |= mask; \
- } else { \
- val &= ~mask; \
- } \
-} while (0)
-
-struct davinci_vcif_dev {
- struct davinci_vc *davinci_vc;
- struct snd_dmaengine_dai_dma_data dma_data[2];
- int dma_request[2];
-};
-
-static void davinci_vcif_start(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_vcif_dev *davinci_vcif_dev =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- u32 w;
-
- /* Start the sample generator and enable transmitter/receiver */
- w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
- else
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
-
- writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
-}
-
-static void davinci_vcif_stop(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_vcif_dev *davinci_vcif_dev =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- u32 w;
-
- /* Reset transmitter/receiver and sample rate/frame sync generators */
- w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
- else
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
-
- writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
-}
-
-static int davinci_vcif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai);
- struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- u32 w;
-
- /* Restart the codec before setup */
- davinci_vcif_stop(substream);
- davinci_vcif_start(substream);
-
- /* General line settings */
- writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL);
-
- writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR);
-
- writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN);
-
- w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
-
- /* Determine xfer data type */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_U8:
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
- DAVINCI_VC_CTRL_RD_UNSIGNED |
- DAVINCI_VC_CTRL_WD_BITS_8 |
- DAVINCI_VC_CTRL_WD_UNSIGNED, 1);
- break;
- case SNDRV_PCM_FORMAT_S8:
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
- DAVINCI_VC_CTRL_WD_BITS_8, 1);
-
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED |
- DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
- DAVINCI_VC_CTRL_RD_UNSIGNED |
- DAVINCI_VC_CTRL_WD_BITS_8 |
- DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
- break;
- default:
- printk(KERN_WARNING "davinci-vcif: unsupported PCM format");
- return -EINVAL;
- }
-
- writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
-
- return 0;
-}
-
-static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- davinci_vcif_start(substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_vcif_stop(substream);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
-
-static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
- .trigger = davinci_vcif_trigger,
- .hw_params = davinci_vcif_hw_params,
-};
-
-static int davinci_vcif_dai_probe(struct snd_soc_dai *dai)
-{
- struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
-
- return 0;
-}
-
-static struct snd_soc_dai_driver davinci_vcif_dai = {
- .probe = davinci_vcif_dai_probe,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = DAVINCI_VCIF_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = DAVINCI_VCIF_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &davinci_vcif_dai_ops,
-
-};
-
-static const struct snd_soc_component_driver davinci_vcif_component = {
- .name = "davinci-vcif",
-};
-
-static int davinci_vcif_probe(struct platform_device *pdev)
-{
- struct davinci_vc *davinci_vc = pdev->dev.platform_data;
- struct davinci_vcif_dev *davinci_vcif_dev;
- int ret;
-
- davinci_vcif_dev = devm_kzalloc(&pdev->dev,
- sizeof(struct davinci_vcif_dev),
- GFP_KERNEL);
- if (!davinci_vcif_dev) {
- dev_dbg(&pdev->dev,
- "could not allocate memory for private data\n");
- return -ENOMEM;
- }
-
- /* DMA tx params */
- davinci_vcif_dev->davinci_vc = davinci_vc;
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data =
- &davinci_vc->davinci_vcif.dma_tx_channel;
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
- davinci_vc->davinci_vcif.dma_tx_addr;
-
- /* DMA rx params */
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data =
- &davinci_vc->davinci_vcif.dma_rx_channel;
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
- davinci_vc->davinci_vcif.dma_rx_addr;
-
- dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &davinci_vcif_component,
- &davinci_vcif_dai, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "could not register dai\n");
- return ret;
- }
-
- ret = edma_pcm_platform_register(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static struct platform_driver davinci_vcif_driver = {
- .probe = davinci_vcif_probe,
- .driver = {
- .name = "davinci-vcif",
- },
-};
-
-module_platform_driver(davinci_vcif_driver);
-
-MODULE_AUTHOR("Miguel Aguilar");
-MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/edma-pcm.h b/sound/soc/davinci/edma-pcm.h
deleted file mode 100644
index b0957744851c..000000000000
--- a/sound/soc/davinci/edma-pcm.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
- *
- * Copyright (C) 2014 Texas Instruments, Inc.
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Based on: sound/soc/tegra/tegra_pcm.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.
- *
- * 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 __EDMA_PCM_H__
-#define __EDMA_PCM_H__
-
-#if IS_ENABLED(CONFIG_SND_EDMA_SOC)
-int edma_pcm_platform_register(struct device *dev);
-#else
-static inline int edma_pcm_platform_register(struct device *dev)
-{
- return 0;
-}
-#endif /* CONFIG_SND_EDMA_SOC */
-
-#endif /* __EDMA_PCM_H__ */
diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig
index d50e08517dce..6bb31b64210a 100644
--- a/sound/soc/dwc/Kconfig
+++ b/sound/soc/dwc/Kconfig
@@ -1,10 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "DesignWare"
+
config SND_DESIGNWARE_I2S
tristate "Synopsys I2S Device Driver"
- depends on CLKDEV_LOOKUP
+ depends on HAVE_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S driver for
- Synopsys desigwnware I2S device. The device supports upto
- maximum of 8 channels each for play and record.
+ Synopsys designware I2S device. The device supports up to
+ a maximum of 8 channels each for play and record.
+
+config SND_DESIGNWARE_PCM
+ bool "PCM PIO extension for I2S driver"
+ depends on SND_DESIGNWARE_I2S
+ help
+ Say Y or N if you want to add a custom ALSA extension that registers
+ a PCM and uses PIO to transfer data.
+ This functionality is specially suited for I2S devices that don't have
+ DMA support.
+endmenu
diff --git a/sound/soc/dwc/Makefile b/sound/soc/dwc/Makefile
index 319371f690f4..91e1aaab9966 100644
--- a/sound/soc/dwc/Makefile
+++ b/sound/soc/dwc/Makefile
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
# SYNOPSYS Platform Support
obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o
+designware_i2s-y := dwc-i2s.o
+designware_i2s-$(CONFIG_SND_DESIGNWARE_PCM) += dwc-pcm.o
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
deleted file mode 100644
index ce664c239be3..000000000000
--- a/sound/soc/dwc/designware_i2s.c
+++ /dev/null
@@ -1,749 +0,0 @@
-/*
- * ALSA SoC Synopsys I2S Audio Layer
- *
- * sound/soc/dwc/designware_i2s.c
- *
- * Copyright (C) 2010 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/device.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-#include <sound/designware_i2s.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-/* common register for all channel */
-#define IER 0x000
-#define IRER 0x004
-#define ITER 0x008
-#define CER 0x00C
-#define CCR 0x010
-#define RXFFR 0x014
-#define TXFFR 0x018
-
-/* I2STxRxRegisters for all channels */
-#define LRBR_LTHR(x) (0x40 * x + 0x020)
-#define RRBR_RTHR(x) (0x40 * x + 0x024)
-#define RER(x) (0x40 * x + 0x028)
-#define TER(x) (0x40 * x + 0x02C)
-#define RCR(x) (0x40 * x + 0x030)
-#define TCR(x) (0x40 * x + 0x034)
-#define ISR(x) (0x40 * x + 0x038)
-#define IMR(x) (0x40 * x + 0x03C)
-#define ROR(x) (0x40 * x + 0x040)
-#define TOR(x) (0x40 * x + 0x044)
-#define RFCR(x) (0x40 * x + 0x048)
-#define TFCR(x) (0x40 * x + 0x04C)
-#define RFF(x) (0x40 * x + 0x050)
-#define TFF(x) (0x40 * x + 0x054)
-
-/* I2SCOMPRegisters */
-#define I2S_COMP_PARAM_2 0x01F0
-#define I2S_COMP_PARAM_1 0x01F4
-#define I2S_COMP_VERSION 0x01F8
-#define I2S_COMP_TYPE 0x01FC
-
-/*
- * Component parameter register fields - define the I2S block's
- * configuration.
- */
-#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
-#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
-#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
-#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
-#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
-#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
-#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
-#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
-#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
-#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
-#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
-
-#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
-#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
-#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
-#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)
-
-/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
-#define COMP_MAX_WORDSIZE (1 << 3)
-#define COMP_MAX_DATA_WIDTH (1 << 2)
-
-#define MAX_CHANNEL_NUM 8
-#define MIN_CHANNEL_NUM 2
-
-union dw_i2s_snd_dma_data {
- struct i2s_dma_data pd;
- struct snd_dmaengine_dai_dma_data dt;
-};
-
-struct dw_i2s_dev {
- void __iomem *i2s_base;
- struct clk *clk;
- int active;
- unsigned int capability;
- unsigned int quirks;
- unsigned int i2s_reg_comp1;
- unsigned int i2s_reg_comp2;
- struct device *dev;
- u32 ccr;
- u32 xfer_resolution;
-
- /* data related to DMA transfers b/w i2s and DMAC */
- union dw_i2s_snd_dma_data play_dma_data;
- union dw_i2s_snd_dma_data capture_dma_data;
- struct i2s_clk_config_data config;
- int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
-};
-
-static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
-{
- writel(val, io_base + reg);
-}
-
-static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
-{
- return readl(io_base + reg);
-}
-
-static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
-{
- u32 i = 0;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- for (i = 0; i < 4; i++)
- i2s_write_reg(dev->i2s_base, TER(i), 0);
- } else {
- for (i = 0; i < 4; i++)
- i2s_write_reg(dev->i2s_base, RER(i), 0);
- }
-}
-
-static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
-{
- u32 i = 0;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- for (i = 0; i < 4; i++)
- i2s_read_reg(dev->i2s_base, TOR(i));
- } else {
- for (i = 0; i < 4; i++)
- i2s_read_reg(dev->i2s_base, ROR(i));
- }
-}
-
-static void i2s_start(struct dw_i2s_dev *dev,
- struct snd_pcm_substream *substream)
-{
- u32 i, irq;
- i2s_write_reg(dev->i2s_base, IER, 1);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- for (i = 0; i < 4; i++) {
- irq = i2s_read_reg(dev->i2s_base, IMR(i));
- i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
- }
- i2s_write_reg(dev->i2s_base, ITER, 1);
- } else {
- for (i = 0; i < 4; i++) {
- irq = i2s_read_reg(dev->i2s_base, IMR(i));
- i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
- }
- i2s_write_reg(dev->i2s_base, IRER, 1);
- }
-
- i2s_write_reg(dev->i2s_base, CER, 1);
-}
-
-static void i2s_stop(struct dw_i2s_dev *dev,
- struct snd_pcm_substream *substream)
-{
- u32 i = 0, irq;
-
- i2s_clear_irqs(dev, substream->stream);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- i2s_write_reg(dev->i2s_base, ITER, 0);
-
- for (i = 0; i < 4; i++) {
- irq = i2s_read_reg(dev->i2s_base, IMR(i));
- i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
- }
- } else {
- i2s_write_reg(dev->i2s_base, IRER, 0);
-
- for (i = 0; i < 4; i++) {
- irq = i2s_read_reg(dev->i2s_base, IMR(i));
- i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
- }
- }
-
- if (!dev->active) {
- i2s_write_reg(dev->i2s_base, CER, 0);
- i2s_write_reg(dev->i2s_base, IER, 0);
- }
-}
-
-static int dw_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
- union dw_i2s_snd_dma_data *dma_data = NULL;
-
- if (!(dev->capability & DWC_I2S_RECORD) &&
- (substream->stream == SNDRV_PCM_STREAM_CAPTURE))
- return -EINVAL;
-
- if (!(dev->capability & DWC_I2S_PLAY) &&
- (substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
- return -EINVAL;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dma_data = &dev->play_dma_data;
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- dma_data = &dev->capture_dma_data;
-
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
-
- return 0;
-}
-
-static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
-{
- u32 ch_reg, irq;
- struct i2s_clk_config_data *config = &dev->config;
-
-
- i2s_disable_channels(dev, stream);
-
- for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- i2s_write_reg(dev->i2s_base, TCR(ch_reg),
- dev->xfer_resolution);
- i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
- irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
- i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
- i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
- } else {
- i2s_write_reg(dev->i2s_base, RCR(ch_reg),
- dev->xfer_resolution);
- i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
- irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
- i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
- i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
- }
-
- }
-}
-
-static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- struct i2s_clk_config_data *config = &dev->config;
- int ret;
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- config->data_width = 16;
- dev->ccr = 0x00;
- dev->xfer_resolution = 0x02;
- break;
-
- case SNDRV_PCM_FORMAT_S24_LE:
- config->data_width = 24;
- dev->ccr = 0x08;
- dev->xfer_resolution = 0x04;
- break;
-
- case SNDRV_PCM_FORMAT_S32_LE:
- config->data_width = 32;
- dev->ccr = 0x10;
- dev->xfer_resolution = 0x05;
- break;
-
- default:
- dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
- return -EINVAL;
- }
-
- config->chan_nr = params_channels(params);
-
- switch (config->chan_nr) {
- case EIGHT_CHANNEL_SUPPORT:
- case SIX_CHANNEL_SUPPORT:
- case FOUR_CHANNEL_SUPPORT:
- case TWO_CHANNEL_SUPPORT:
- break;
- default:
- dev_err(dev->dev, "channel not supported\n");
- return -EINVAL;
- }
-
- dw_i2s_config(dev, substream->stream);
-
- i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
-
- config->sample_rate = params_rate(params);
-
- if (dev->capability & DW_I2S_MASTER) {
- if (dev->i2s_clk_cfg) {
- ret = dev->i2s_clk_cfg(config);
- if (ret < 0) {
- dev_err(dev->dev, "runtime audio clk config fail\n");
- return ret;
- }
- } else {
- u32 bitclk = config->sample_rate *
- config->data_width * 2;
-
- ret = clk_set_rate(dev->clk, bitclk);
- if (ret) {
- dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
- ret);
- return ret;
- }
- }
- }
- return 0;
-}
-
-static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- snd_soc_dai_set_dma_data(dai, substream, NULL);
-}
-
-static int dw_i2s_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s_write_reg(dev->i2s_base, TXFFR, 1);
- else
- i2s_write_reg(dev->i2s_base, RXFFR, 1);
-
- return 0;
-}
-
-static int dw_i2s_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- dev->active++;
- i2s_start(dev, substream);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- dev->active--;
- i2s_stop(dev, substream);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
- int ret = 0;
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- if (dev->capability & DW_I2S_SLAVE)
- ret = 0;
- else
- ret = -EINVAL;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- if (dev->capability & DW_I2S_MASTER)
- ret = 0;
- else
- ret = -EINVAL;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- ret = -EINVAL;
- break;
- default:
- dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static struct snd_soc_dai_ops dw_i2s_dai_ops = {
- .startup = dw_i2s_startup,
- .shutdown = dw_i2s_shutdown,
- .hw_params = dw_i2s_hw_params,
- .prepare = dw_i2s_prepare,
- .trigger = dw_i2s_trigger,
- .set_fmt = dw_i2s_set_fmt,
-};
-
-static const struct snd_soc_component_driver dw_i2s_component = {
- .name = "dw-i2s",
-};
-
-#ifdef CONFIG_PM
-static int dw_i2s_runtime_suspend(struct device *dev)
-{
- struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
-
- if (dw_dev->capability & DW_I2S_MASTER)
- clk_disable(dw_dev->clk);
- return 0;
-}
-
-static int dw_i2s_runtime_resume(struct device *dev)
-{
- struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
-
- if (dw_dev->capability & DW_I2S_MASTER)
- clk_enable(dw_dev->clk);
- return 0;
-}
-
-static int dw_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- if (dev->capability & DW_I2S_MASTER)
- clk_disable(dev->clk);
- return 0;
-}
-
-static int dw_i2s_resume(struct snd_soc_dai *dai)
-{
- struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- if (dev->capability & DW_I2S_MASTER)
- clk_enable(dev->clk);
-
- if (dai->playback_active)
- dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
- if (dai->capture_active)
- dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
- return 0;
-}
-
-#else
-#define dw_i2s_suspend NULL
-#define dw_i2s_resume NULL
-#endif
-
-/*
- * The following tables allow a direct lookup of various parameters
- * defined in the I2S block's configuration in terms of sound system
- * parameters. Each table is sized to the number of entries possible
- * according to the number of configuration bits describing an I2S
- * block parameter.
- */
-
-/* Maximum bit resolution of a channel - not uniformly spaced */
-static const u32 fifo_width[COMP_MAX_WORDSIZE] = {
- 12, 16, 20, 24, 32, 0, 0, 0
-};
-
-/* Width of (DMA) bus */
-static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
- DMA_SLAVE_BUSWIDTH_1_BYTE,
- DMA_SLAVE_BUSWIDTH_2_BYTES,
- DMA_SLAVE_BUSWIDTH_4_BYTES,
- DMA_SLAVE_BUSWIDTH_UNDEFINED
-};
-
-/* PCM format to support channel resolution */
-static const u32 formats[COMP_MAX_WORDSIZE] = {
- SNDRV_PCM_FMTBIT_S16_LE,
- SNDRV_PCM_FMTBIT_S16_LE,
- SNDRV_PCM_FMTBIT_S24_LE,
- SNDRV_PCM_FMTBIT_S24_LE,
- SNDRV_PCM_FMTBIT_S32_LE,
- 0,
- 0,
- 0
-};
-
-static int dw_configure_dai(struct dw_i2s_dev *dev,
- struct snd_soc_dai_driver *dw_i2s_dai,
- unsigned int rates)
-{
- /*
- * Read component parameter registers to extract
- * the I2S block's configuration.
- */
- u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
- u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
- u32 idx;
-
- if (dev->capability & DWC_I2S_RECORD &&
- dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
- comp1 = comp1 & ~BIT(5);
-
- if (COMP1_TX_ENABLED(comp1)) {
- dev_dbg(dev->dev, " designware: play supported\n");
- idx = COMP1_TX_WORDSIZE_0(comp1);
- if (WARN_ON(idx >= ARRAY_SIZE(formats)))
- return -EINVAL;
- dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
- dw_i2s_dai->playback.channels_max =
- 1 << (COMP1_TX_CHANNELS(comp1) + 1);
- dw_i2s_dai->playback.formats = formats[idx];
- dw_i2s_dai->playback.rates = rates;
- }
-
- if (COMP1_RX_ENABLED(comp1)) {
- dev_dbg(dev->dev, "designware: record supported\n");
- idx = COMP2_RX_WORDSIZE_0(comp2);
- if (WARN_ON(idx >= ARRAY_SIZE(formats)))
- return -EINVAL;
- dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
- dw_i2s_dai->capture.channels_max =
- 1 << (COMP1_RX_CHANNELS(comp1) + 1);
- dw_i2s_dai->capture.formats = formats[idx];
- dw_i2s_dai->capture.rates = rates;
- }
-
- if (COMP1_MODE_EN(comp1)) {
- dev_dbg(dev->dev, "designware: i2s master mode supported\n");
- dev->capability |= DW_I2S_MASTER;
- } else {
- dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
- dev->capability |= DW_I2S_SLAVE;
- }
-
- return 0;
-}
-
-static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
- struct snd_soc_dai_driver *dw_i2s_dai,
- struct resource *res,
- const struct i2s_platform_data *pdata)
-{
- u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
- u32 idx = COMP1_APB_DATA_WIDTH(comp1);
- int ret;
-
- if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
- return -EINVAL;
-
- ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates);
- if (ret < 0)
- return ret;
-
- /* Set DMA slaves info */
- dev->play_dma_data.pd.data = pdata->play_dma_data;
- dev->capture_dma_data.pd.data = pdata->capture_dma_data;
- dev->play_dma_data.pd.addr = res->start + I2S_TXDMA;
- dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA;
- dev->play_dma_data.pd.max_burst = 16;
- dev->capture_dma_data.pd.max_burst = 16;
- dev->play_dma_data.pd.addr_width = bus_widths[idx];
- dev->capture_dma_data.pd.addr_width = bus_widths[idx];
- dev->play_dma_data.pd.filter = pdata->filter;
- dev->capture_dma_data.pd.filter = pdata->filter;
-
- return 0;
-}
-
-static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
- struct snd_soc_dai_driver *dw_i2s_dai,
- struct resource *res)
-{
- u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
- u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
- u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
- u32 idx = COMP1_APB_DATA_WIDTH(comp1);
- u32 idx2;
- int ret;
-
- if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
- return -EINVAL;
-
- ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000);
- if (ret < 0)
- return ret;
-
- if (COMP1_TX_ENABLED(comp1)) {
- idx2 = COMP1_TX_WORDSIZE_0(comp1);
-
- dev->capability |= DWC_I2S_PLAY;
- dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
- dev->play_dma_data.dt.addr_width = bus_widths[idx];
- dev->play_dma_data.dt.chan_name = "TX";
- dev->play_dma_data.dt.fifo_size = fifo_depth *
- (fifo_width[idx2]) >> 8;
- dev->play_dma_data.dt.maxburst = 16;
- }
- if (COMP1_RX_ENABLED(comp1)) {
- idx2 = COMP2_RX_WORDSIZE_0(comp2);
-
- dev->capability |= DWC_I2S_RECORD;
- dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
- dev->capture_dma_data.dt.addr_width = bus_widths[idx];
- dev->capture_dma_data.dt.chan_name = "RX";
- dev->capture_dma_data.dt.fifo_size = fifo_depth *
- (fifo_width[idx2] >> 8);
- dev->capture_dma_data.dt.maxburst = 16;
- }
-
- return 0;
-
-}
-
-static int dw_i2s_probe(struct platform_device *pdev)
-{
- const struct i2s_platform_data *pdata = pdev->dev.platform_data;
- struct dw_i2s_dev *dev;
- struct resource *res;
- int ret;
- struct snd_soc_dai_driver *dw_i2s_dai;
- const char *clk_id;
-
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- dev_warn(&pdev->dev, "kzalloc fail\n");
- return -ENOMEM;
- }
-
- dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
- if (!dw_i2s_dai)
- return -ENOMEM;
-
- dw_i2s_dai->ops = &dw_i2s_dai_ops;
- dw_i2s_dai->suspend = dw_i2s_suspend;
- dw_i2s_dai->resume = dw_i2s_resume;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(dev->i2s_base))
- return PTR_ERR(dev->i2s_base);
-
- dev->dev = &pdev->dev;
-
- if (pdata) {
- dev->capability = pdata->cap;
- clk_id = NULL;
- dev->quirks = pdata->quirks;
- if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
- dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
- dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
- } else {
- dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
- dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
- }
- ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
- } else {
- clk_id = "i2sclk";
- ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
- }
- if (ret < 0)
- return ret;
-
- if (dev->capability & DW_I2S_MASTER) {
- if (pdata) {
- dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
- if (!dev->i2s_clk_cfg) {
- dev_err(&pdev->dev, "no clock configure method\n");
- return -ENODEV;
- }
- }
- dev->clk = devm_clk_get(&pdev->dev, clk_id);
-
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
-
- ret = clk_prepare_enable(dev->clk);
- if (ret < 0)
- return ret;
- }
-
- dev_set_drvdata(&pdev->dev, dev);
- ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
- dw_i2s_dai, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "not able to register dai\n");
- goto err_clk_disable;
- }
-
- if (!pdata) {
- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
- if (ret) {
- dev_err(&pdev->dev,
- "Could not register PCM: %d\n", ret);
- goto err_clk_disable;
- }
- }
- pm_runtime_enable(&pdev->dev);
- return 0;
-
-err_clk_disable:
- if (dev->capability & DW_I2S_MASTER)
- clk_disable_unprepare(dev->clk);
- return ret;
-}
-
-static int dw_i2s_remove(struct platform_device *pdev)
-{
- struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
-
- if (dev->capability & DW_I2S_MASTER)
- clk_disable_unprepare(dev->clk);
-
- pm_runtime_disable(&pdev->dev);
- return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id dw_i2s_of_match[] = {
- { .compatible = "snps,designware-i2s", },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
-#endif
-
-static const struct dev_pm_ops dwc_pm_ops = {
- SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
-};
-
-static struct platform_driver dw_i2s_driver = {
- .probe = dw_i2s_probe,
- .remove = dw_i2s_remove,
- .driver = {
- .name = "designware-i2s",
- .of_match_table = of_match_ptr(dw_i2s_of_match),
- .pm = &dwc_pm_ops,
- },
-};
-
-module_platform_driver(dw_i2s_driver);
-
-MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>");
-MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:designware_i2s");
diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c
new file mode 100644
index 000000000000..28001e9857d9
--- /dev/null
+++ b/sound/soc/dwc/dwc-i2s.c
@@ -0,0 +1,1104 @@
+/*
+ * ALSA SoC Synopsys I2S Audio Layer
+ *
+ * sound/soc/dwc/designware_i2s.c
+ *
+ * Copyright (C) 2010 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/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <sound/designware_i2s.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include "local.h"
+
+static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
+{
+ writel(val, io_base + reg);
+}
+
+static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
+{
+ return readl(io_base + reg);
+}
+
+static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
+{
+ u32 i = 0;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < 4; i++)
+ i2s_write_reg(dev->i2s_base, TER(i), 0);
+ } else {
+ for (i = 0; i < 4; i++)
+ i2s_write_reg(dev->i2s_base, RER(i), 0);
+ }
+}
+
+static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
+{
+ u32 i = 0;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < 4; i++)
+ i2s_read_reg(dev->i2s_base, TOR(i));
+ } else {
+ for (i = 0; i < 4; i++)
+ i2s_read_reg(dev->i2s_base, ROR(i));
+ }
+}
+
+static inline void i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream,
+ int chan_nr)
+{
+ u32 i, irq;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
+ }
+ } else {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
+ }
+ }
+}
+
+static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream,
+ int chan_nr)
+{
+ u32 i, irq;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
+ }
+ } else {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
+ }
+ }
+}
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+ struct dw_i2s_dev *dev = dev_id;
+ bool irq_valid = false;
+ u32 isr[4];
+ int i;
+
+ for (i = 0; i < 4; i++)
+ isr[i] = i2s_read_reg(dev->i2s_base, ISR(i));
+
+ i2s_clear_irqs(dev, SNDRV_PCM_STREAM_PLAYBACK);
+ i2s_clear_irqs(dev, SNDRV_PCM_STREAM_CAPTURE);
+
+ for (i = 0; i < 4; i++) {
+ /*
+ * Check if TX fifo is empty. If empty fill FIFO with samples
+ * NOTE: Only two channels supported
+ */
+ if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) {
+ dw_pcm_push_tx(dev);
+ irq_valid = true;
+ }
+
+ /*
+ * Data available. Retrieve samples from FIFO
+ * NOTE: Only two channels supported
+ */
+ if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) {
+ dw_pcm_pop_rx(dev);
+ irq_valid = true;
+ }
+
+ /* Error Handling: TX */
+ if (isr[i] & ISR_TXFO) {
+ dev_err_ratelimited(dev->dev, "TX overrun (ch_id=%d)\n", i);
+ irq_valid = true;
+ }
+
+ /* Error Handling: TX */
+ if (isr[i] & ISR_RXFO) {
+ dev_err_ratelimited(dev->dev, "RX overrun (ch_id=%d)\n", i);
+ irq_valid = true;
+ }
+ }
+
+ if (irq_valid)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void i2s_enable_dma(struct dw_i2s_dev *dev, u32 stream)
+{
+ u32 dma_reg = i2s_read_reg(dev->i2s_base, I2S_DMACR);
+
+ /* Enable DMA handshake for stream */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_reg |= I2S_DMAEN_TXBLOCK;
+ else
+ dma_reg |= I2S_DMAEN_RXBLOCK;
+
+ i2s_write_reg(dev->i2s_base, I2S_DMACR, dma_reg);
+}
+
+static void i2s_disable_dma(struct dw_i2s_dev *dev, u32 stream)
+{
+ u32 dma_reg = i2s_read_reg(dev->i2s_base, I2S_DMACR);
+
+ /* Disable DMA handshake for stream */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_reg &= ~I2S_DMAEN_TXBLOCK;
+ i2s_write_reg(dev->i2s_base, I2S_RTXDMA, 1);
+ } else {
+ dma_reg &= ~I2S_DMAEN_RXBLOCK;
+ i2s_write_reg(dev->i2s_base, I2S_RRXDMA, 1);
+ }
+ i2s_write_reg(dev->i2s_base, I2S_DMACR, dma_reg);
+}
+
+static void i2s_start(struct dw_i2s_dev *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_clk_config_data *config = &dev->config;
+
+ u32 reg = IER_IEN;
+
+ if (dev->tdm_slots) {
+ reg |= (dev->tdm_slots - 1) << IER_TDM_SLOTS_SHIFT;
+ reg |= IER_INTF_TYPE;
+ reg |= dev->frame_offset << IER_FRAME_OFF_SHIFT;
+ }
+
+ i2s_write_reg(dev->i2s_base, IER, reg);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_write_reg(dev->i2s_base, ITER, 1);
+ else
+ i2s_write_reg(dev->i2s_base, IRER, 1);
+
+ if (!(dev->use_pio || dev->is_jh7110))
+ i2s_enable_dma(dev, substream->stream);
+
+ i2s_enable_irqs(dev, substream->stream, config->chan_nr);
+ i2s_write_reg(dev->i2s_base, CER, 1);
+}
+
+static void i2s_stop(struct dw_i2s_dev *dev,
+ struct snd_pcm_substream *substream)
+{
+
+ i2s_clear_irqs(dev, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_write_reg(dev->i2s_base, ITER, 0);
+ else
+ i2s_write_reg(dev->i2s_base, IRER, 0);
+
+ if (!(dev->use_pio || dev->is_jh7110))
+ i2s_disable_dma(dev, substream->stream);
+
+ i2s_disable_irqs(dev, substream->stream, 8);
+
+
+ if (!dev->active) {
+ i2s_write_reg(dev->i2s_base, CER, 0);
+ i2s_write_reg(dev->i2s_base, IER, 0);
+ }
+}
+
+static int dw_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (dev->is_jh7110) {
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
+ }
+
+ return 0;
+}
+
+static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+{
+ u32 ch_reg;
+ struct i2s_clk_config_data *config = &dev->config;
+
+
+ i2s_disable_channels(dev, stream);
+
+ for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(dev->i2s_base, TCR(ch_reg),
+ dev->xfer_resolution);
+ i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
+ dev->fifo_th - 1);
+ i2s_write_reg(dev->i2s_base, TER(ch_reg), TER_TXCHEN |
+ dev->tdm_mask << TER_TXSLOT_SHIFT);
+ } else {
+ i2s_write_reg(dev->i2s_base, RCR(ch_reg),
+ dev->xfer_resolution);
+ i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
+ dev->fifo_th - 1);
+ i2s_write_reg(dev->i2s_base, RER(ch_reg), RER_RXCHEN |
+ dev->tdm_mask << RER_RXSLOT_SHIFT);
+ }
+
+ }
+}
+
+static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct i2s_clk_config_data *config = &dev->config;
+ int ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ config->data_width = 16;
+ dev->ccr = 0x00;
+ dev->xfer_resolution = 0x02;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ config->data_width = 24;
+ dev->ccr = 0x08;
+ dev->xfer_resolution = 0x04;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ config->data_width = 32;
+ dev->ccr = 0x10;
+ dev->xfer_resolution = 0x05;
+ break;
+
+ default:
+ dev_err(dev->dev, "designware-i2s: unsupported PCM fmt");
+ return -EINVAL;
+ }
+
+ if (dev->tdm_slots)
+ config->data_width = 32;
+
+ config->chan_nr = params_channels(params);
+
+ switch (config->chan_nr) {
+ case EIGHT_CHANNEL_SUPPORT:
+ case SIX_CHANNEL_SUPPORT:
+ case FOUR_CHANNEL_SUPPORT:
+ case TWO_CHANNEL_SUPPORT:
+ break;
+ default:
+ dev_err(dev->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+
+ dw_i2s_config(dev, substream->stream);
+
+ i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
+
+ config->sample_rate = params_rate(params);
+
+ if (dev->capability & DW_I2S_MASTER) {
+ if (dev->i2s_clk_cfg) {
+ ret = dev->i2s_clk_cfg(config);
+ if (ret < 0) {
+ dev_err(dev->dev, "runtime audio clk config fail\n");
+ return ret;
+ }
+ } else {
+ u32 bitclk = config->sample_rate *
+ config->data_width * 2;
+
+ ret = clk_set_rate(dev->clk, bitclk);
+ if (ret) {
+ dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static int dw_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_write_reg(dev->i2s_base, TXFFR, 1);
+ else
+ i2s_write_reg(dev->i2s_base, RXFFR, 1);
+
+ return 0;
+}
+
+static int dw_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev->active++;
+ i2s_start(dev, substream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dev->active--;
+ i2s_stop(dev, substream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ if (dev->capability & DW_I2S_SLAVE)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ if (dev->capability & DW_I2S_MASTER)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ ret = -EINVAL;
+ break;
+ default:
+ dev_dbg(dev->dev, "dwc : Invalid clock provider format\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ dev->frame_offset = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dev->frame_offset = 0;
+ break;
+ default:
+ dev_err(dev->dev, "DAI format unsupported");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dw_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (slot_width != 32)
+ return -EINVAL;
+
+ if (slots < 0 || slots > 16)
+ return -EINVAL;
+
+ if (rx_mask != tx_mask)
+ return -EINVAL;
+
+ if (!rx_mask)
+ return -EINVAL;
+
+ dev->tdm_slots = slots;
+ dev->tdm_mask = rx_mask;
+
+ dev->l_reg = RSLOT_TSLOT(ffs(rx_mask) - 1);
+ dev->r_reg = RSLOT_TSLOT(fls(rx_mask) - 1);
+
+ return 0;
+}
+
+static int dw_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+ .probe = dw_i2s_dai_probe,
+ .startup = dw_i2s_startup,
+ .hw_params = dw_i2s_hw_params,
+ .prepare = dw_i2s_prepare,
+ .trigger = dw_i2s_trigger,
+ .set_fmt = dw_i2s_set_fmt,
+ .set_tdm_slot = dw_i2s_set_tdm_slot,
+};
+
+static int dw_i2s_runtime_suspend(struct device *dev)
+{
+ struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+
+ if (dw_dev->capability & DW_I2S_MASTER)
+ clk_disable(dw_dev->clk);
+ return 0;
+}
+
+static int dw_i2s_runtime_resume(struct device *dev)
+{
+ struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (dw_dev->capability & DW_I2S_MASTER) {
+ ret = clk_enable(dw_dev->clk);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int dw_i2s_suspend(struct snd_soc_component *component)
+{
+ struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable(dev->clk);
+ return 0;
+}
+
+static int dw_i2s_resume(struct snd_soc_component *component)
+{
+ struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dai *dai;
+ int stream, ret;
+
+ if (dev->capability & DW_I2S_MASTER) {
+ ret = clk_enable(dev->clk);
+ if (ret)
+ return ret;
+ }
+
+ for_each_component_dais(component, dai) {
+ for_each_pcm_streams(stream)
+ if (snd_soc_dai_stream_active(dai, stream))
+ dw_i2s_config(dev, stream);
+ }
+
+ return 0;
+}
+
+#else
+#define dw_i2s_suspend NULL
+#define dw_i2s_resume NULL
+#endif
+
+static const struct snd_soc_component_driver dw_i2s_component = {
+ .name = "dw-i2s",
+ .suspend = dw_i2s_suspend,
+ .resume = dw_i2s_resume,
+ .legacy_dai_naming = 1,
+};
+
+/*
+ * The following tables allow a direct lookup of various parameters
+ * defined in the I2S block's configuration in terms of sound system
+ * parameters. Each table is sized to the number of entries possible
+ * according to the number of configuration bits describing an I2S
+ * block parameter.
+ */
+
+/* Maximum bit resolution of a channel - not uniformly spaced */
+static const u32 fifo_width[COMP_MAX_WORDSIZE] = {
+ 12, 16, 20, 24, 32, 0, 0, 0
+};
+
+/* Width of (DMA) bus */
+static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
+ DMA_SLAVE_BUSWIDTH_1_BYTE,
+ DMA_SLAVE_BUSWIDTH_2_BYTES,
+ DMA_SLAVE_BUSWIDTH_4_BYTES,
+ DMA_SLAVE_BUSWIDTH_UNDEFINED
+};
+
+/* PCM format to support channel resolution */
+static const u32 formats[COMP_MAX_WORDSIZE] = {
+ SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ 0,
+ 0,
+ 0
+};
+
+static int dw_configure_dai(struct dw_i2s_dev *dev,
+ struct snd_soc_dai_driver *dw_i2s_dai,
+ unsigned int rates)
+{
+ /*
+ * Read component parameter registers to extract
+ * the I2S block's configuration.
+ */
+ u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+ u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
+ u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+ u32 idx;
+
+ if (dev->capability & DWC_I2S_RECORD &&
+ dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
+ comp1 = comp1 & ~BIT(5);
+
+ if (dev->capability & DWC_I2S_PLAY &&
+ dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
+ comp1 = comp1 & ~BIT(6);
+
+ if (COMP1_TX_ENABLED(comp1)) {
+ dev_dbg(dev->dev, " designware: play supported\n");
+ idx = COMP1_TX_WORDSIZE_0(comp1);
+ if (WARN_ON(idx >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+ if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+ idx = 1;
+ dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
+ dw_i2s_dai->playback.channels_max =
+ 1 << (COMP1_TX_CHANNELS(comp1) + 1);
+ dw_i2s_dai->playback.formats = formats[idx];
+ dw_i2s_dai->playback.rates = rates;
+ }
+
+ if (COMP1_RX_ENABLED(comp1)) {
+ dev_dbg(dev->dev, "designware: record supported\n");
+ idx = COMP2_RX_WORDSIZE_0(comp2);
+ if (WARN_ON(idx >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+ if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+ idx = 1;
+ dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
+ dw_i2s_dai->capture.channels_max =
+ 1 << (COMP1_RX_CHANNELS(comp1) + 1);
+ dw_i2s_dai->capture.formats = formats[idx];
+ dw_i2s_dai->capture.rates = rates;
+ }
+
+ if (COMP1_MODE_EN(comp1)) {
+ dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+ dev->capability |= DW_I2S_MASTER;
+ } else {
+ dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+ dev->capability |= DW_I2S_SLAVE;
+ }
+
+ dev->fifo_th = fifo_depth / 2;
+ return 0;
+}
+
+static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
+ struct snd_soc_dai_driver *dw_i2s_dai,
+ struct resource *res,
+ const struct i2s_platform_data *pdata)
+{
+ u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+ u32 idx = COMP1_APB_DATA_WIDTH(comp1);
+ int ret;
+
+ if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+ return -EINVAL;
+
+ ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates);
+ if (ret < 0)
+ return ret;
+
+ if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+ idx = 1;
+
+ if (dev->is_jh7110) {
+ /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */
+ u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+ u32 idx2;
+
+ if (COMP1_TX_ENABLED(comp1)) {
+ idx2 = COMP1_TX_WORDSIZE_0(comp1);
+ dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
+ dev->play_dma_data.dt.fifo_size = dev->fifo_th * 2 *
+ (fifo_width[idx2]) >> 8;
+ dev->play_dma_data.dt.maxburst = 16;
+ }
+ if (COMP1_RX_ENABLED(comp1)) {
+ idx2 = COMP2_RX_WORDSIZE_0(comp2);
+ dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
+ dev->capture_dma_data.dt.fifo_size = dev->fifo_th * 2 *
+ (fifo_width[idx2] >> 8);
+ dev->capture_dma_data.dt.maxburst = 16;
+ }
+ } else {
+ /* Set DMA slaves info */
+ dev->play_dma_data.pd.data = pdata->play_dma_data;
+ dev->capture_dma_data.pd.data = pdata->capture_dma_data;
+ dev->play_dma_data.pd.addr = res->start + I2S_TXDMA;
+ dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA;
+ dev->play_dma_data.pd.max_burst = 16;
+ dev->capture_dma_data.pd.max_burst = 16;
+ dev->play_dma_data.pd.addr_width = bus_widths[idx];
+ dev->capture_dma_data.pd.addr_width = bus_widths[idx];
+ dev->play_dma_data.pd.filter = pdata->filter;
+ dev->capture_dma_data.pd.filter = pdata->filter;
+ }
+
+ return 0;
+}
+
+static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
+ struct snd_soc_dai_driver *dw_i2s_dai,
+ struct resource *res)
+{
+ u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
+ u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+ u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+ u32 idx2;
+ int ret;
+
+ ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000);
+ if (ret < 0)
+ return ret;
+
+ if (COMP1_TX_ENABLED(comp1)) {
+ idx2 = COMP1_TX_WORDSIZE_0(comp1);
+
+ dev->capability |= DWC_I2S_PLAY;
+ dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
+ dev->play_dma_data.dt.fifo_size = fifo_depth *
+ (fifo_width[idx2]) >> 8;
+ dev->play_dma_data.dt.maxburst = 16;
+ }
+ if (COMP1_RX_ENABLED(comp1)) {
+ idx2 = COMP2_RX_WORDSIZE_0(comp2);
+
+ dev->capability |= DWC_I2S_RECORD;
+ dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
+ dev->capture_dma_data.dt.fifo_size = fifo_depth *
+ (fifo_width[idx2] >> 8);
+ dev->capture_dma_data.dt.maxburst = 16;
+ }
+
+ return 0;
+
+}
+
+#ifdef CONFIG_OF
+/* clocks initialization with master mode on JH7110 SoC */
+static int jh7110_i2s_crg_master_init(struct dw_i2s_dev *dev)
+{
+ static struct clk_bulk_data clks[] = {
+ { .id = "mclk" },
+ { .id = "mclk_ext" },
+ { .id = "mclk_inner" },
+ { .id = "apb" },
+ { .id = "i2sclk" },
+ };
+ struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev);
+ int ret;
+ struct clk *pclk;
+ struct clk *bclk_mst;
+ struct clk *mclk;
+ struct clk *mclk_ext;
+ struct clk *mclk_inner;
+
+ if (IS_ERR(resets))
+ return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n");
+
+ ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks);
+ if (ret)
+ return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n");
+
+ mclk = clks[0].clk;
+ mclk_ext = clks[1].clk;
+ mclk_inner = clks[2].clk;
+ pclk = clks[3].clk;
+ bclk_mst = clks[4].clk;
+
+ ret = clk_prepare_enable(pclk);
+ if (ret)
+ goto exit;
+
+ /* Use inner mclk first and avoid uninitialized gpio for external mclk */
+ ret = clk_set_parent(mclk, mclk_inner);
+ if (ret)
+ goto err_dis_pclk;
+
+ ret = clk_prepare_enable(bclk_mst);
+ if (ret)
+ goto err_dis_pclk;
+
+ /* deassert resets before set clock parent */
+ ret = reset_control_deassert(resets);
+ if (ret)
+ goto err_dis_all;
+
+ /* external clock (12.288MHz) for Audio */
+ ret = clk_set_parent(mclk, mclk_ext);
+ if (ret)
+ goto err_dis_all;
+
+ /* i2sclk will be got and enabled repeatedly later and should be disabled now. */
+ clk_disable_unprepare(bclk_mst);
+ clk_bulk_put(ARRAY_SIZE(clks), clks);
+ dev->is_jh7110 = true;
+
+ return 0;
+
+err_dis_all:
+ clk_disable_unprepare(bclk_mst);
+err_dis_pclk:
+ clk_disable_unprepare(pclk);
+exit:
+ clk_bulk_put(ARRAY_SIZE(clks), clks);
+ return ret;
+}
+
+/* clocks initialization with slave mode on JH7110 SoC */
+static int jh7110_i2s_crg_slave_init(struct dw_i2s_dev *dev)
+{
+ static struct clk_bulk_data clks[] = {
+ { .id = "mclk" },
+ { .id = "mclk_ext" },
+ { .id = "apb" },
+ { .id = "bclk_ext" },
+ { .id = "lrck_ext" },
+ { .id = "bclk" },
+ { .id = "lrck" },
+ { .id = "mclk_inner" },
+ { .id = "i2sclk" },
+ };
+ struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev);
+ int ret;
+ struct clk *pclk;
+ struct clk *bclk_mst;
+ struct clk *bclk_ext;
+ struct clk *lrck_ext;
+ struct clk *bclk;
+ struct clk *lrck;
+ struct clk *mclk;
+ struct clk *mclk_ext;
+ struct clk *mclk_inner;
+
+ if (IS_ERR(resets))
+ return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n");
+
+ ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks);
+ if (ret)
+ return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n");
+
+ mclk = clks[0].clk;
+ mclk_ext = clks[1].clk;
+ pclk = clks[2].clk;
+ bclk_ext = clks[3].clk;
+ lrck_ext = clks[4].clk;
+ bclk = clks[5].clk;
+ lrck = clks[6].clk;
+ mclk_inner = clks[7].clk;
+ bclk_mst = clks[8].clk;
+
+ ret = clk_prepare_enable(pclk);
+ if (ret)
+ goto exit;
+
+ ret = clk_set_parent(mclk, mclk_inner);
+ if (ret)
+ goto err_dis_pclk;
+
+ ret = clk_prepare_enable(bclk_mst);
+ if (ret)
+ goto err_dis_pclk;
+
+ ret = reset_control_deassert(resets);
+ if (ret)
+ goto err_dis_all;
+
+ /* The sources of BCLK and LRCK are the external codec. */
+ ret = clk_set_parent(bclk, bclk_ext);
+ if (ret)
+ goto err_dis_all;
+
+ ret = clk_set_parent(lrck, lrck_ext);
+ if (ret)
+ goto err_dis_all;
+
+ ret = clk_set_parent(mclk, mclk_ext);
+ if (ret)
+ goto err_dis_all;
+
+ /* The i2sclk will be got and enabled repeatedly later and should be disabled now. */
+ clk_disable_unprepare(bclk_mst);
+ clk_bulk_put(ARRAY_SIZE(clks), clks);
+ dev->is_jh7110 = true;
+
+ return 0;
+
+err_dis_all:
+ clk_disable_unprepare(bclk_mst);
+err_dis_pclk:
+ clk_disable_unprepare(pclk);
+exit:
+ clk_bulk_put(ARRAY_SIZE(clks), clks);
+ return ret;
+}
+
+/* Special syscon initialization about RX channel with slave mode on JH7110 SoC */
+static int jh7110_i2srx_crg_init(struct dw_i2s_dev *dev)
+{
+ struct regmap *regmap;
+ unsigned int args[2];
+
+ regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node,
+ "starfive,syscon",
+ 2, args);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n");
+
+ /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */
+ regmap_update_bits(regmap, args[0], args[1], args[1]);
+
+ return jh7110_i2s_crg_slave_init(dev);
+}
+
+static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config)
+{
+ struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config);
+ u32 bclk_rate = config->sample_rate * 64;
+
+ return clk_set_rate(dev->clk, bclk_rate);
+}
+#endif /* CONFIG_OF */
+
+static int dw_i2s_probe(struct platform_device *pdev)
+{
+ const struct i2s_platform_data *pdata = pdev->dev.platform_data;
+ struct dw_i2s_dev *dev;
+ struct resource *res;
+ int ret, irq;
+ struct snd_soc_dai_driver *dw_i2s_dai;
+ const char *clk_id;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+ if (!dw_i2s_dai)
+ return -ENOMEM;
+
+ dw_i2s_dai->ops = &dw_i2s_dai_ops;
+
+ dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(dev->i2s_base))
+ return PTR_ERR(dev->i2s_base);
+
+ dev->dev = &pdev->dev;
+ dev->is_jh7110 = false;
+ if (pdata) {
+ if (pdata->i2s_pd_init) {
+ ret = pdata->i2s_pd_init(dev);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (!dev->is_jh7110) {
+ dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
+ if (IS_ERR(dev->reset))
+ return PTR_ERR(dev->reset);
+
+ ret = reset_control_deassert(dev->reset);
+ if (ret)
+ return ret;
+ }
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq >= 0) {
+ ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
+ pdev->name, dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ goto err_assert_reset;
+ }
+ }
+
+ dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+ dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+ if (pdata) {
+ dev->capability = pdata->cap;
+ clk_id = NULL;
+ dev->quirks = pdata->quirks;
+ if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
+ dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
+ dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
+ }
+ ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
+ } else {
+ clk_id = "i2sclk";
+ ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ }
+ if (ret < 0)
+ goto err_assert_reset;
+
+ if (dev->capability & DW_I2S_MASTER) {
+ if (pdata) {
+ dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+ if (!dev->i2s_clk_cfg) {
+ dev_err(&pdev->dev, "no clock configure method\n");
+ ret = -ENODEV;
+ goto err_assert_reset;
+ }
+ }
+ dev->clk = devm_clk_get_enabled(&pdev->dev, clk_id);
+
+ if (IS_ERR(dev->clk)) {
+ ret = PTR_ERR(dev->clk);
+ goto err_assert_reset;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, dev);
+ ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
+ dw_i2s_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "not able to register dai\n");
+ goto err_assert_reset;
+ }
+
+ if (!pdata || dev->is_jh7110) {
+ if (irq >= 0) {
+ ret = dw_pcm_register(pdev);
+ dev->use_pio = true;
+ dev->l_reg = LRBR_LTHR(0);
+ dev->r_reg = RRBR_RTHR(0);
+ } else {
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ 0);
+ dev->use_pio = false;
+ }
+
+ if (ret) {
+ dev_err(&pdev->dev, "could not register pcm: %d\n",
+ ret);
+ goto err_assert_reset;
+ }
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+
+err_assert_reset:
+ reset_control_assert(dev->reset);
+ return ret;
+}
+
+static void dw_i2s_remove(struct platform_device *pdev)
+{
+ struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
+
+ reset_control_assert(dev->reset);
+ pm_runtime_disable(&pdev->dev);
+}
+
+#ifdef CONFIG_OF
+static const struct i2s_platform_data jh7110_i2stx0_data = {
+ .cap = DWC_I2S_PLAY | DW_I2S_MASTER,
+ .channel = TWO_CHANNEL_SUPPORT,
+ .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .snd_rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+ .i2s_clk_cfg = jh7110_i2stx0_clk_cfg,
+ .i2s_pd_init = jh7110_i2s_crg_master_init,
+};
+
+static const struct i2s_platform_data jh7110_i2stx1_data = {
+ .cap = DWC_I2S_PLAY | DW_I2S_SLAVE,
+ .channel = TWO_CHANNEL_SUPPORT,
+ .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .snd_rates = SNDRV_PCM_RATE_8000_192000,
+ .i2s_pd_init = jh7110_i2s_crg_slave_init,
+};
+
+static const struct i2s_platform_data jh7110_i2srx_data = {
+ .cap = DWC_I2S_RECORD | DW_I2S_SLAVE,
+ .channel = TWO_CHANNEL_SUPPORT,
+ .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .snd_rates = SNDRV_PCM_RATE_8000_192000,
+ .i2s_pd_init = jh7110_i2srx_crg_init,
+};
+
+static const struct of_device_id dw_i2s_of_match[] = {
+ { .compatible = "snps,designware-i2s", },
+ { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, },
+ { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,},
+ { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
+#endif
+
+static const struct dev_pm_ops dwc_pm_ops = {
+ RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver dw_i2s_driver = {
+ .probe = dw_i2s_probe,
+ .remove = dw_i2s_remove,
+ .driver = {
+ .name = "designware-i2s",
+ .of_match_table = of_match_ptr(dw_i2s_of_match),
+ .pm = pm_ptr(&dwc_pm_ops),
+ },
+};
+
+module_platform_driver(dw_i2s_driver);
+
+MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>");
+MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:designware_i2s");
diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c
new file mode 100644
index 000000000000..a418265c030a
--- /dev/null
+++ b/sound/soc/dwc/dwc-pcm.c
@@ -0,0 +1,266 @@
+/*
+ * ALSA SoC Synopsys PIO PCM for I2S driver
+ *
+ * sound/soc/dwc/designware_pcm.c
+ *
+ * Copyright (C) 2016 Synopsys
+ * Jose Abreu <joabreu@synopsys.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/io.h>
+#include <linux/rcupdate.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "local.h"
+
+#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN)
+#define PERIOD_BYTES_MIN 4096
+#define PERIODS_MIN 2
+
+#define dw_pcm_tx_fn(sample_bits) \
+static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \
+ struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \
+ bool *period_elapsed) \
+{ \
+ const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
+ unsigned int period_pos = tx_ptr % runtime->period_size; \
+ int i; \
+\
+ for (i = 0; i < dev->fifo_th; i++) { \
+ iowrite32(p[tx_ptr][0], dev->i2s_base + dev->l_reg); \
+ iowrite32(p[tx_ptr][1], dev->i2s_base + dev->r_reg); \
+ period_pos++; \
+ if (++tx_ptr >= runtime->buffer_size) \
+ tx_ptr = 0; \
+ } \
+ *period_elapsed = period_pos >= runtime->period_size; \
+ return tx_ptr; \
+}
+
+#define dw_pcm_rx_fn(sample_bits) \
+static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \
+ struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \
+ bool *period_elapsed) \
+{ \
+ u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
+ unsigned int period_pos = rx_ptr % runtime->period_size; \
+ int i; \
+\
+ for (i = 0; i < dev->fifo_th; i++) { \
+ p[rx_ptr][0] = ioread32(dev->i2s_base + dev->l_reg); \
+ p[rx_ptr][1] = ioread32(dev->i2s_base + dev->r_reg); \
+ period_pos++; \
+ if (++rx_ptr >= runtime->buffer_size) \
+ rx_ptr = 0; \
+ } \
+ *period_elapsed = period_pos >= runtime->period_size; \
+ return rx_ptr; \
+}
+
+dw_pcm_tx_fn(16);
+dw_pcm_tx_fn(32);
+dw_pcm_rx_fn(16);
+dw_pcm_rx_fn(32);
+
+#undef dw_pcm_tx_fn
+#undef dw_pcm_rx_fn
+
+static const struct snd_pcm_hardware dw_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = BUFFER_BYTES_MAX,
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
+ .periods_min = PERIODS_MIN,
+ .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+ .fifo_size = 16,
+};
+
+static void dw_pcm_transfer(struct dw_i2s_dev *dev, bool push)
+{
+ struct snd_pcm_substream *substream;
+ bool active, period_elapsed;
+
+ rcu_read_lock();
+ if (push)
+ substream = rcu_dereference(dev->tx_substream);
+ else
+ substream = rcu_dereference(dev->rx_substream);
+ active = substream && snd_pcm_running(substream);
+ if (active) {
+ unsigned int ptr;
+ unsigned int new_ptr;
+
+ if (push) {
+ ptr = READ_ONCE(dev->tx_ptr);
+ new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
+ &period_elapsed);
+ cmpxchg(&dev->tx_ptr, ptr, new_ptr);
+ } else {
+ ptr = READ_ONCE(dev->rx_ptr);
+ new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
+ &period_elapsed);
+ cmpxchg(&dev->rx_ptr, ptr, new_ptr);
+ }
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ rcu_read_unlock();
+}
+
+void dw_pcm_push_tx(struct dw_i2s_dev *dev)
+{
+ dw_pcm_transfer(dev, true);
+}
+
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
+{
+ dw_pcm_transfer(dev, false);
+}
+
+static int dw_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+
+ snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ runtime->private_data = dev;
+
+ return 0;
+}
+
+static int dw_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ synchronize_rcu();
+ return 0;
+}
+
+static int dw_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dw_i2s_dev *dev = runtime->private_data;
+
+ switch (params_channels(hw_params)) {
+ case 2:
+ break;
+ default:
+ dev_err(dev->dev, "invalid channels number\n");
+ return -EINVAL;
+ }
+
+ switch (params_format(hw_params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dev->tx_fn = dw_pcm_tx_16;
+ dev->rx_fn = dw_pcm_rx_16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dev->tx_fn = dw_pcm_tx_32;
+ dev->rx_fn = dw_pcm_rx_32;
+ break;
+ default:
+ dev_err(dev->dev, "invalid format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dw_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dw_i2s_dev *dev = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ WRITE_ONCE(dev->tx_ptr, 0);
+ rcu_assign_pointer(dev->tx_substream, substream);
+ } else {
+ WRITE_ONCE(dev->rx_ptr, 0);
+ rcu_assign_pointer(dev->rx_substream, substream);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rcu_assign_pointer(dev->tx_substream, NULL);
+ else
+ rcu_assign_pointer(dev->rx_substream, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dw_i2s_dev *dev = runtime->private_data;
+ snd_pcm_uframes_t pos;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pos = READ_ONCE(dev->tx_ptr);
+ else
+ pos = READ_ONCE(dev->rx_ptr);
+
+ return pos < runtime->buffer_size ? pos : 0;
+}
+
+static int dw_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size = dw_pcm_hardware.buffer_bytes_max;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, size, size);
+ return 0;
+}
+
+static const struct snd_soc_component_driver dw_pcm_component = {
+ .open = dw_pcm_open,
+ .close = dw_pcm_close,
+ .hw_params = dw_pcm_hw_params,
+ .trigger = dw_pcm_trigger,
+ .pointer = dw_pcm_pointer,
+ .pcm_construct = dw_pcm_new,
+};
+
+int dw_pcm_register(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev, &dw_pcm_component,
+ NULL, 0);
+}
diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h
new file mode 100644
index 000000000000..dce88c9ad5f3
--- /dev/null
+++ b/sound/soc/dwc/local.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (ST) 2012 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.
+ */
+
+#ifndef __DESIGNWARE_LOCAL_H
+#define __DESIGNWARE_LOCAL_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/designware_i2s.h>
+
+/* common register for all channel */
+#define IER 0x000
+#define IRER 0x004
+#define ITER 0x008
+#define CER 0x00C
+#define CCR 0x010
+#define RXFFR 0x014
+#define TXFFR 0x018
+
+/* Enable register fields */
+#define IER_TDM_SLOTS_SHIFT 8
+#define IER_FRAME_OFF_SHIFT 5
+#define IER_FRAME_OFF BIT(5)
+#define IER_INTF_TYPE BIT(1)
+#define IER_IEN BIT(0)
+
+/* Interrupt status register fields */
+#define ISR_TXFO BIT(5)
+#define ISR_TXFE BIT(4)
+#define ISR_RXFO BIT(1)
+#define ISR_RXDA BIT(0)
+
+/* I2STxRxRegisters for all channels */
+#define LRBR_LTHR(x) (0x40 * x + 0x020)
+#define RRBR_RTHR(x) (0x40 * x + 0x024)
+#define RER(x) (0x40 * x + 0x028)
+#define TER(x) (0x40 * x + 0x02C)
+#define RCR(x) (0x40 * x + 0x030)
+#define TCR(x) (0x40 * x + 0x034)
+#define ISR(x) (0x40 * x + 0x038)
+#define IMR(x) (0x40 * x + 0x03C)
+#define ROR(x) (0x40 * x + 0x040)
+#define TOR(x) (0x40 * x + 0x044)
+#define RFCR(x) (0x40 * x + 0x048)
+#define TFCR(x) (0x40 * x + 0x04C)
+#define RFF(x) (0x40 * x + 0x050)
+#define TFF(x) (0x40 * x + 0x054)
+#define RSLOT_TSLOT(x) (0x4 * (x) + 0x224)
+
+/* Receive enable register fields */
+#define RER_RXSLOT_SHIFT 8
+#define RER_RXCHEN BIT(0)
+
+/* Transmit enable register fields */
+#define TER_TXSLOT_SHIFT 8
+#define TER_TXCHEN BIT(0)
+
+/* I2SCOMPRegisters */
+#define I2S_COMP_PARAM_2 0x01F0
+#define I2S_COMP_PARAM_1 0x01F4
+#define I2S_COMP_VERSION 0x01F8
+#define I2S_COMP_TYPE 0x01FC
+
+#define I2S_RRXDMA 0x01C4
+#define I2S_RTXDMA 0x01CC
+#define I2S_DMACR 0x0200
+#define I2S_DMAEN_RXBLOCK (1 << 16)
+#define I2S_DMAEN_TXBLOCK (1 << 17)
+
+/*
+ * Component parameter register fields - define the I2S block's
+ * configuration.
+ */
+#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
+#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
+#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
+#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
+#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
+#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
+#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
+#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
+#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
+#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
+#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
+
+#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
+#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
+#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
+#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)
+
+/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
+#define COMP_MAX_WORDSIZE (1 << 3)
+#define COMP_MAX_DATA_WIDTH (1 << 2)
+
+#define MAX_CHANNEL_NUM 8
+#define MIN_CHANNEL_NUM 2
+
+union dw_i2s_snd_dma_data {
+ struct i2s_dma_data pd;
+ struct snd_dmaengine_dai_dma_data dt;
+};
+
+struct dw_i2s_dev {
+ void __iomem *i2s_base;
+ struct clk *clk;
+ struct reset_control *reset;
+ int active;
+ unsigned int capability;
+ unsigned int quirks;
+ unsigned int i2s_reg_comp1;
+ unsigned int i2s_reg_comp2;
+ struct device *dev;
+ u32 ccr;
+ u32 xfer_resolution;
+ u32 fifo_th;
+ u32 l_reg;
+ u32 r_reg;
+ bool is_jh7110; /* Flag for StarFive JH7110 SoC */
+
+ /* data related to DMA transfers b/w i2s and DMAC */
+ union dw_i2s_snd_dma_data play_dma_data;
+ union dw_i2s_snd_dma_data capture_dma_data;
+ struct i2s_clk_config_data config;
+ int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+
+ /* data related to PIO transfers */
+ bool use_pio;
+
+ /* data related to TDM mode */
+ u32 tdm_slots;
+ u32 tdm_mask;
+ u32 frame_offset;
+
+ struct snd_pcm_substream __rcu *tx_substream;
+ struct snd_pcm_substream __rcu *rx_substream;
+ unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+ bool *period_elapsed);
+ unsigned int (*rx_fn)(struct dw_i2s_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+ bool *period_elapsed);
+ unsigned int tx_ptr;
+ unsigned int rx_ptr;
+};
+
+#if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)
+void dw_pcm_push_tx(struct dw_i2s_dev *dev);
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev);
+int dw_pcm_register(struct platform_device *pdev);
+#else
+static inline void dw_pcm_push_tx(struct dw_i2s_dev *dev) { }
+static inline void dw_pcm_pop_rx(struct dw_i2s_dev *dev) { }
+static inline int dw_pcm_register(struct platform_device *pdev)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 14dfdee05fd5..c4a00b22bc2a 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,11 +1,16 @@
-menu "SoC Audio for Freescale CPUs"
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Freescale"
comment "Common SoC Audio options for Freescale CPUs:"
config SND_SOC_FSL_ASRC
tristate "Asynchronous Sample Rate Converter (ASRC) module support"
+ depends on HAS_DMA
+ select DMA_SHARED_BUFFER
select REGMAP_MMIO
select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_COMPRESS_ACCEL
+ select SND_COMPRESS_OFFLOAD
help
Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
support for the Freescale CPUs.
@@ -17,12 +22,31 @@ config SND_SOC_FSL_SAI
select REGMAP_MMIO
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_SOC_FSL_UTILS
help
Say Y if you want to add Synchronous Audio Interface (SAI)
support for the Freescale CPUs.
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
+config SND_SOC_FSL_MQS
+ tristate "Medium Quality Sound (MQS) module support"
+ depends on SND_SOC_FSL_SAI
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add Medium Quality Sound (MQS)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
+config SND_SOC_FSL_AUDMIX
+ tristate "Audio Mixer (AUDMIX) module support"
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add Audio Mixer (AUDMIX)
+ support for the NXP iMX CPUs.
+
config SND_SOC_FSL_SSI
tristate "Synchronous Serial Interface module (SSI) support"
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
@@ -39,6 +63,8 @@ config SND_SOC_FSL_SPDIF
select REGMAP_MMIO
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC)
+ select BITREVERSE
+ select SND_SOC_FSL_UTILS
help
Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
support for the Freescale CPUs.
@@ -55,13 +81,80 @@ config SND_SOC_FSL_ESAI
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
+config SND_SOC_FSL_MICFIL
+ tristate "Pulse Density Modulation Microphone Interface (MICFIL) module support"
+ select REGMAP_MMIO
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_SOC_FSL_UTILS
+ help
+ Say Y if you want to add Pulse Density Modulation microphone
+ interface (MICFIL) support for NXP.
+
+config SND_SOC_FSL_EASRC
+ tristate "Enhanced Asynchronous Sample Rate Converter (EASRC) module support"
+ depends on SND_SOC_FSL_ASRC
+ select REGMAP_MMIO
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y if you want to add Enhanced ASRC support for NXP. The ASRC is
+ a digital module that converts audio from a source sample rate to a
+ destination sample rate. It is a new design module compare with the
+ old ASRC.
+
+config SND_SOC_FSL_XCVR
+ tristate "NXP Audio Transceiver (XCVR) module support"
+ select REGMAP_MMIO
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_SOC_FSL_UTILS
+ help
+ Say Y if you want to add Audio Transceiver (XCVR) support for NXP
+ iMX CPUs. XCVR is a digital module that supports HDMI2.1 eARC,
+ HDMI1.4 ARC and SPDIF.
+
+config SND_SOC_FSL_AUD2HTX
+ tristate "AUDIO TO HDMI TX module support"
+ depends on ARCH_MXC || COMPILE_TEST
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+ help
+ Say Y if you want to add AUDIO TO HDMI TX support for NXP.
+
config SND_SOC_FSL_UTILS
tristate
+config SND_SOC_FSL_RPMSG
+ tristate "NXP Audio Base On RPMSG support"
+ depends on COMMON_CLK
+ depends on OF && I2C
+ depends on RPMSG
+ depends on SND_IMX_SOC || SND_IMX_SOC = n
+ select SND_SOC_IMX_RPMSG if SND_IMX_SOC != n
+ help
+ Say Y if you want to add rpmsg audio support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
+config SND_SOC_FSL_LPC3XXX
+ tristate "SoC Audio for NXP LPC32XX CPUs"
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for the LPC3XXX I2S interface.
+
config SND_SOC_IMX_PCM_DMA
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
+config SND_SOC_IMX_AUDIO_RPMSG
+ tristate
+ depends on RPMSG
+
+config SND_SOC_IMX_PCM_RPMSG
+ tristate
+ depends on SND_SOC_IMX_AUDIO_RPMSG
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
config SND_SOC_IMX_AUDMUX
tristate "Digital Audio Mux module support"
help
@@ -92,20 +185,16 @@ config SND_MPC52xx_DMA
config SND_SOC_POWERPC_DMA
tristate
-comment "SoC Audio support for Freescale PPC boards:"
-
-config SND_SOC_MPC8610_HPCD
- tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
- # I2C is necessary for the CS4270 driver
- depends on MPC8610_HPCD && I2C
- select SND_SOC_FSL_SSI
- select SND_SOC_FSL_UTILS
- select SND_SOC_POWERPC_DMA
- select SND_SOC_CS4270
- select SND_SOC_CS4270_VD33_ERRATA
- default y if MPC8610_HPCD
+config SND_SOC_POWERPC_QMC_AUDIO
+ tristate "QMC ALSA SoC support"
+ depends on CPM_QMC
help
- Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
+ ALSA SoC Audio support using the Freescale QUICC Multichannel
+ Controller (QMC).
+ Say Y or M if you want to add support for SoC audio using Freescale
+ QMC.
+
+comment "SoC Audio support for Freescale PPC boards:"
config SND_SOC_P1022_DS
tristate "ALSA SoC support for the Freescale P1022 DS board"
@@ -171,75 +260,26 @@ config SND_MPC52xx_SOC_EFIKA
endif # SND_POWERPC_SOC
-if SND_IMX_SOC
-
-config SND_SOC_IMX_SSI
- tristate
- select SND_SOC_FSL_UTILS
-
config SND_SOC_IMX_PCM_FIQ
tristate
+ default y if (SND_SOC_FSL_SSI=m || SND_SOC_FSL_SPDIF=m) && (MXC_TZIC || MXC_AVIC)
select FIQ
-comment "SoC Audio support for Freescale i.MX boards:"
-
-config SND_MXC_SOC_WM1133_EV1
- tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
- depends on MACH_MX31ADS_WM1133_EV1
- select SND_SOC_WM8350
- select SND_SOC_IMX_PCM_FIQ
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Enable support for audio on the i.MX31ADS with the WM1133-EV1
- PMIC board with WM8835x fitted.
-
-config SND_SOC_MX27VIS_AIC32X4
- tristate "SoC audio support for Visstrim M10 boards"
- depends on MACH_IMX27_VISSTRIM_M10 && I2C
- select SND_SOC_TLV320AIC32X4
- select SND_SOC_IMX_PCM_DMA
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Say Y if you want to add support for SoC audio on Visstrim SM10
- board with TLV320AIC32X4 codec.
+if SND_IMX_SOC
-config SND_SOC_PHYCORE_AC97
- tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
- depends on MACH_PCM043 || MACH_PCA100
- select SND_SOC_AC97_BUS
- select SND_SOC_WM9712
- select SND_SOC_IMX_PCM_FIQ
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Say Y if you want to add support for SoC audio on Phytec phyCORE
- and phyCARD boards in AC97 mode
+comment "SoC Audio support for Freescale i.MX boards:"
config SND_SOC_EUKREA_TLV320
tristate "Eukrea TLV320"
- depends on ARCH_MXC && I2C
+ depends on ARCH_MXC && !ARM64 && I2C
select SND_SOC_TLV320AIC23_I2C
select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
select SND_SOC_FSL_SSI
select SND_SOC_IMX_PCM_DMA
help
Enable I2S based access to the TLV320AIC23B codec attached
to the SSI interface
-config SND_SOC_IMX_WM8962
- tristate "SoC Audio support for i.MX boards with wm8962"
- depends on OF && I2C && INPUT
- select SND_SOC_WM8962
- select SND_SOC_IMX_PCM_DMA
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_FSL_SSI
- help
- Say Y if you want to add support for SoC audio on an i.MX board with
- a wm8962 codec.
-
config SND_SOC_IMX_ES8328
tristate "SoC Audio support for i.MX boards with the ES8328 codec"
depends on OF && (I2C || SPI)
@@ -263,39 +303,77 @@ config SND_SOC_IMX_SGTL5000
Say Y if you want to add support for SoC audio on an i.MX board with
a sgtl5000 codec.
-config SND_SOC_IMX_SPDIF
- tristate "SoC Audio support for i.MX boards with S/PDIF"
- select SND_SOC_IMX_PCM_DMA
- select SND_SOC_FSL_SPDIF
- help
- SoC Audio support for i.MX boards with S/PDIF
- Say Y if you want to add support for SoC audio on an i.MX board with
- a S/DPDIF.
-
-config SND_SOC_IMX_MC13783
- tristate "SoC Audio support for I.MX boards with mc13783"
- depends on MFD_MC13XXX && ARM
- select SND_SOC_IMX_SSI
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_MC13783
- select SND_SOC_IMX_PCM_DMA
+ Note that this is an old driver. Consider enabling
+ SND_SOC_FSL_ASOC_CARD and SND_SOC_SGTL5000 to use the newer
+ driver.
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
depends on OF && I2C
# enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m:
depends on SND_AC97_CODEC || SND_AC97_CODEC=n
+ select SND_SIMPLE_CARD_UTILS
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_ESAI
select SND_SOC_FSL_SAI
select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_SPDIF
+ select SND_SOC_TLV320AIC31XX
+ select SND_SOC_WM8994
+ select MFD_WM8994
help
ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
- ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
- and SGTL5000.
+ ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888,
+ CS4271, CS4272, SGTL5000 and TLV320AIC32x4.
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
+config SND_SOC_IMX_AUDMIX
+ tristate "SoC Audio support for i.MX boards with AUDMIX"
+ select SND_SOC_FSL_AUDMIX
+ select SND_SOC_FSL_SAI
+ help
+ SoC Audio support for i.MX boards with Audio Mixer
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ an Audio Mixer.
+
+config SND_SOC_IMX_HDMI
+ tristate "SoC Audio support for i.MX boards with HDMI port"
+ select SND_SOC_FSL_SAI
+ select SND_SOC_FSL_AUD2HTX
+ select SND_SOC_HDMI_CODEC
+ help
+ ALSA SoC Audio support with HDMI feature for Freescale SoCs that have
+ SAI/AUD2HTX and connect with internal HDMI IP or external module
+ SII902X.
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ IMX HDMI.
+
+config SND_SOC_IMX_RPMSG
+ tristate "SoC Audio support for i.MX boards with rpmsg"
+ depends on RPMSG
+ depends on OF && I2C
+ select SND_SOC_IMX_PCM_RPMSG
+ select SND_SOC_IMX_AUDIO_RPMSG
+ help
+ SoC Audio support for i.MX boards with rpmsg.
+ There should be rpmsg devices defined in other core (M core)
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a rpmsg devices.
+
+config SND_SOC_IMX_CARD
+ tristate "SoC Audio Graph Sound Card support for i.MX boards"
+ depends on OF && I2C
+ select SND_SOC_AK4458
+ select SND_SOC_AK5558
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_SAI
+ select SND_SIMPLE_CARD_UTILS
+ help
+ This option enables audio sound card support for i.MX boards
+ with OF-graph DT bindings.
+ It also support DPCM of single CPU multi Codec ststem.
+
endif # SND_IMX_SOC
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d28dc25c9375..d656a9ab54e3 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -1,33 +1,49 @@
-# MPC8610 HPCD Machine Support
-snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o
-obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o
-
+# SPDX-License-Identifier: GPL-2.0
# P1022 DS Machine Support
-snd-soc-p1022-ds-objs := p1022_ds.o
+snd-soc-p1022-ds-y := p1022_ds.o
obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
# P1022 RDK Machine Support
-snd-soc-p1022-rdk-objs := p1022_rdk.o
+snd-soc-p1022-rdk-y := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
-snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
-snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
-snd-soc-fsl-sai-objs := fsl_sai.o
+snd-soc-fsl-audmix-y := fsl_audmix.o
+snd-soc-fsl-asoc-card-y := fsl-asoc-card.o
+snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o
+snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o
+snd-soc-fsl-sai-y := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
-snd-soc-fsl-spdif-objs := fsl_spdif.o
-snd-soc-fsl-esai-objs := fsl_esai.o
-snd-soc-fsl-utils-objs := fsl_utils.o
-snd-soc-fsl-dma-objs := fsl_dma.o
+snd-soc-fsl-spdif-y := fsl_spdif.o
+snd-soc-fsl-esai-y := fsl_esai.o
+snd-soc-fsl-micfil-y := fsl_micfil.o
+snd-soc-fsl-utils-y := fsl_utils.o
+snd-soc-fsl-dma-y := fsl_dma.o
+snd-soc-fsl-mqs-y := fsl_mqs.o
+snd-soc-fsl-easrc-y := fsl_easrc.o
+snd-soc-fsl-xcvr-y := fsl_xcvr.o
+snd-soc-fsl-aud2htx-y := fsl_aud2htx.o
+snd-soc-fsl-rpmsg-y := fsl_rpmsg.o
+snd-soc-fsl-qmc-audio-y := fsl_qmc_audio.o
+
+obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
+obj-$(CONFIG_SND_SOC_FSL_LPC3XXX) += snd-soc-fsl-lpc3xxx.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
+obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
+obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
+obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
+obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o
+obj-$(CONFIG_SND_SOC_FSL_AUD2HTX) += snd-soc-fsl-aud2htx.o
+obj-$(CONFIG_SND_SOC_FSL_RPMSG) += snd-soc-fsl-rpmsg.o
+obj-$(CONFIG_SND_SOC_POWERPC_QMC_AUDIO) += snd-soc-fsl-qmc-audio.o
# MPC5200 Platform Support
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
@@ -39,31 +55,27 @@ obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
# i.MX Platform Support
-snd-soc-imx-ssi-objs := imx-ssi.o
-snd-soc-imx-audmux-objs := imx-audmux.o
-obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
+snd-soc-imx-audmux-y := imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
+obj-$(CONFIG_SND_SOC_IMX_AUDIO_RPMSG) += imx-audio-rpmsg.o
+obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o
# i.MX Machine Support
-snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
-snd-soc-phycore-ac97-objs := phycore-ac97.o
-snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
-snd-soc-wm1133-ev1-objs := wm1133-ev1.o
-snd-soc-imx-es8328-objs := imx-es8328.o
-snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
-snd-soc-imx-wm8962-objs := imx-wm8962.o
-snd-soc-imx-spdif-objs := imx-spdif.o
-snd-soc-imx-mc13783-objs := imx-mc13783.o
+snd-soc-eukrea-tlv320-y := eukrea-tlv320.o
+snd-soc-imx-es8328-y := imx-es8328.o
+snd-soc-imx-sgtl5000-y := imx-sgtl5000.o
+snd-soc-imx-audmix-y := imx-audmix.o
+snd-soc-imx-hdmi-y := imx-hdmi.o
+snd-soc-imx-rpmsg-y := imx-rpmsg.o
+snd-soc-imx-card-y := imx-card.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
-obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
-obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
-obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
-obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
-obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
-obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
+obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
+obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
+obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
+obj-$(CONFIG_SND_SOC_IMX_CARD) += snd-soc-imx-card.o
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c
index b2acd3293ea8..de17b103a4cf 100644
--- a/sound/soc/fsl/efika-audio-fabric.c
+++ b/sound/soc/fsl/efika-audio-fabric.c
@@ -15,8 +15,8 @@
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/delay.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
@@ -26,27 +26,31 @@
#include <sound/soc.h>
#include "mpc5200_dma.h"
-#include "mpc5200_psc_ac97.h"
-#include "../codecs/stac9766.h"
#define DRV_NAME "efika-audio-fabric"
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_CPU("mpc5200-psc-ac97.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("stac9766-codec",
+ "stac9766-hifi-analog")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("mpc5200-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(iec958,
+ DAILINK_COMP_ARRAY(COMP_CPU("mpc5200-psc-ac97.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("stac9766-codec",
+ "stac9766-hifi-IEC958")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("mpc5200-pcm-audio")));
+
static struct snd_soc_dai_link efika_fabric_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 Analog",
- .codec_dai_name = "stac9766-hifi-analog",
- .cpu_dai_name = "mpc5200-psc-ac97.0",
- .platform_name = "mpc5200-pcm-audio",
- .codec_name = "stac9766-codec",
+ SND_SOC_DAILINK_REG(analog),
},
{
.name = "AC97",
.stream_name = "AC97 IEC958",
- .codec_dai_name = "stac9766-hifi-IEC958",
- .cpu_dai_name = "mpc5200-psc-ac97.1",
- .platform_name = "mpc5200-pcm-audio",
- .codec_name = "stac9766-codec",
+ SND_SOC_DAILINK_REG(iec958),
},
};
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 883087f2b092..6be074ea0b3f 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -1,19 +1,13 @@
-/*
- * eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
- *
- * Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
- *
- * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
- * which is Copyright 2009 Simtec Electronics
- * and on sound/soc/imx/phycore-ac97.c which is
- * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
+//
+// Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
+//
+// based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
+// which is Copyright 2009 Simtec Electronics
+// and on sound/soc/imx/phycore-ac97.c which is
+// Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
#include <linux/errno.h>
#include <linux/module.h>
@@ -29,7 +23,6 @@
#include "../codecs/tlv320aic23.h"
#include "imx-ssi.h"
-#include "fsl_ssi.h"
#include "imx-audmux.h"
#define CODEC_CLOCK 12000000
@@ -37,9 +30,9 @@
static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
@@ -64,17 +57,22 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops eukrea_tlv320_snd_ops = {
+static const struct snd_soc_ops eukrea_tlv320_snd_ops = {
.hw_params = eukrea_tlv320_hw_params,
};
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "tlv320aic23-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
static struct snd_soc_dai_link eukrea_tlv320_dai = {
.name = "tlv320aic23",
.stream_name = "TLV320AIC23",
- .codec_dai_name = "tlv320aic23-hifi",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &eukrea_tlv320_snd_ops,
+ SND_SOC_DAILINK_REG(hifi),
};
static struct snd_soc_card eukrea_tlv320 = {
@@ -88,7 +86,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
int ret;
int int_port = 0, ext_port;
struct device_node *np = pdev->dev.of_node;
- struct device_node *ssi_np = NULL, *codec_np = NULL;
+ struct device_node *ssi_np = NULL, *codec_np = NULL, *tmp_np = NULL;
eukrea_tlv320.dev = &pdev->dev;
if (np) {
@@ -111,7 +109,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
codec_np = of_parse_phandle(ssi_np, "codec-handle", 0);
if (codec_np)
- eukrea_tlv320_dai.codec_of_node = codec_np;
+ eukrea_tlv320_dai.codecs->of_node = codec_np;
else
dev_err(&pdev->dev, "codec-handle node missing or invalid.\n");
@@ -119,13 +117,13 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"fsl,mux-int-port node missing or invalid.\n");
- return ret;
+ goto err;
}
ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port);
if (ret) {
dev_err(&pdev->dev,
"fsl,mux-ext-port node missing or invalid.\n");
- return ret;
+ goto err;
}
/*
@@ -135,17 +133,17 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
int_port--;
ext_port--;
- eukrea_tlv320_dai.cpu_of_node = ssi_np;
- eukrea_tlv320_dai.platform_of_node = ssi_np;
+ eukrea_tlv320_dai.cpus->of_node = ssi_np;
+ eukrea_tlv320_dai.platforms->of_node = ssi_np;
} else {
- eukrea_tlv320_dai.cpu_dai_name = "imx-ssi.0";
- eukrea_tlv320_dai.platform_name = "imx-ssi.0";
- eukrea_tlv320_dai.codec_name = "tlv320aic23-codec.0-001a";
+ eukrea_tlv320_dai.cpus->dai_name = "imx-ssi.0";
+ eukrea_tlv320_dai.platforms->name = "imx-ssi.0";
+ eukrea_tlv320_dai.codecs->name = "tlv320aic23-codec.0-001a";
eukrea_tlv320.name = "cpuimx-audio";
}
if (machine_is_eukrea_cpuimx27() ||
- of_find_compatible_node(NULL, NULL, "fsl,imx21-audmux")) {
+ (tmp_np = of_find_compatible_node(NULL, NULL, "fsl,imx21-audmux"))) {
imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
IMX_AUDMUX_V1_PCR_SYN |
IMX_AUDMUX_V1_PCR_TFSDIR |
@@ -160,10 +158,11 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
IMX_AUDMUX_V1_PCR_SYN |
IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
);
+ of_node_put(tmp_np);
} else if (machine_is_eukrea_cpuimx25sd() ||
machine_is_eukrea_cpuimx35sd() ||
machine_is_eukrea_cpuimx51sd() ||
- of_find_compatible_node(NULL, NULL, "fsl,imx31-audmux")) {
+ (tmp_np = of_find_compatible_node(NULL, NULL, "fsl,imx31-audmux"))) {
if (!np)
ext_port = machine_is_eukrea_cpuimx25sd() ?
4 : 3;
@@ -180,6 +179,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
IMX_AUDMUX_V2_PTCR_SYN,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)
);
+ of_node_put(tmp_np);
} else {
if (np) {
/* The eukrea,asoc-tlv320 driver was explicitly
@@ -196,7 +196,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
}
}
- ret = snd_soc_register_card(&eukrea_tlv320);
+ ret = devm_snd_soc_register_card(&pdev->dev, &eukrea_tlv320);
err:
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
@@ -205,13 +205,6 @@ err:
return ret;
}
-static int eukrea_tlv320_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&eukrea_tlv320);
-
- return 0;
-}
-
static const struct of_device_id imx_tlv320_dt_ids[] = {
{ .compatible = "eukrea,asoc-tlv320"},
{ /* sentinel */ }
@@ -224,7 +217,6 @@ static struct platform_driver eukrea_tlv320_driver = {
.of_match_table = imx_tlv320_dt_ids,
},
.probe = eukrea_tlv320_probe,
- .remove = eukrea_tlv320_remove,
};
module_platform_driver(eukrea_tlv320_driver);
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 562b3bd22d9a..2c7eb0baa0f3 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -1,14 +1,10 @@
-/*
- * Freescale Generic ASoC Sound Card driver with ASRC
- *
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
- *
- * Author: Nicolin Chen <nicoleotsuka@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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale Generic ASoC Sound Card driver with ASRC
+//
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+//
+// Author: Nicolin Chen <nicoleotsuka@gmail.com>
#include <linux/clk.h>
#include <linux/i2c.h>
@@ -19,6 +15,8 @@
#endif
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/simple_card_utils.h>
#include "fsl_esai.h"
#include "fsl_sai.h"
@@ -27,6 +25,14 @@
#include "../codecs/sgtl5000.h"
#include "../codecs/wm8962.h"
#include "../codecs/wm8960.h"
+#include "../codecs/wm8994.h"
+#include "../codecs/tlv320aic31xx.h"
+#include "../codecs/nau8822.h"
+#include "../codecs/wm8904.h"
+
+#define DRIVER_NAME "fsl-asoc-card"
+
+#define CS427x_SYSCLK_MCLK 0
#define RX 0
#define TX 1
@@ -35,27 +41,31 @@
#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
/**
- * CODEC private data
- *
+ * struct codec_priv - CODEC private data
+ * @mclk: Main clock of the CODEC
* @mclk_freq: Clock rate of MCLK
+ * @free_freq: Clock rate of MCLK for hw_free()
* @mclk_id: MCLK (or main clock) id for set_sysclk()
* @fll_id: FLL (or secordary clock) id for set_sysclk()
* @pll_id: PLL id for set_pll()
*/
struct codec_priv {
+ struct clk *mclk;
unsigned long mclk_freq;
+ unsigned long free_freq;
u32 mclk_id;
- u32 fll_id;
- u32 pll_id;
+ int fll_id;
+ int pll_id;
};
/**
- * CPU private data
- *
- * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
- * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
- * @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ * struct cpu_priv - CPU private data
+ * @sysclk_freq: SYSCLK rates for set_sysclk()
+ * @sysclk_dir: SYSCLK directions for set_sysclk()
+ * @sysclk_id: SYSCLK ids for set_sysclk()
+ * @sysclk_ratio: SYSCLK ratio on sample rate
* @slot_width: Slot width of each frame
+ * @slot_num: Number of slots of each frame
*
* Note: [1] for tx and [0] for rx
*/
@@ -63,17 +73,21 @@ struct cpu_priv {
unsigned long sysclk_freq[2];
u32 sysclk_dir[2];
u32 sysclk_id[2];
+ u32 sysclk_ratio[2];
u32 slot_width;
+ u32 slot_num;
};
/**
- * Freescale Generic ASOC card private data
- *
- * @dai_link[3]: DAI link structure including normal one and DPCM link
+ * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data
+ * @dai_link: DAI link structure including normal one and DPCM link
+ * @hp_jack: Headphone Jack structure
+ * @mic_jack: Microphone Jack structure
* @pdev: platform device pointer
* @codec_priv: CODEC private data
* @cpu_priv: CPU private data
* @card: ASoC card structure
+ * @streams: Mask of current active streams
* @sample_rate: Current sample rate
* @sample_format: Current sample format
* @asrc_rate: ASRC sample rate used by Back-Ends
@@ -84,34 +98,58 @@ struct cpu_priv {
struct fsl_asoc_card_priv {
struct snd_soc_dai_link dai_link[3];
+ struct simple_util_jack hp_jack;
+ struct simple_util_jack mic_jack;
struct platform_device *pdev;
- struct codec_priv codec_priv;
+ struct codec_priv codec_priv[2];
struct cpu_priv cpu_priv;
struct snd_soc_card card;
+ u8 streams;
u32 sample_rate;
- u32 sample_format;
+ snd_pcm_format_t sample_format;
u32 asrc_rate;
- u32 asrc_format;
+ snd_pcm_format_t asrc_format;
u32 dai_fmt;
char name[32];
};
-/**
- * This dapm route map exsits for DPCM link only.
+/*
+ * This dapm route map exists for DPCM link only.
* The other routes shall go through Device Tree.
+ *
+ * Note: keep all ASRC routes in the second half
+ * to drop them easily for non-ASRC cases.
*/
static const struct snd_soc_dapm_route audio_map[] = {
- {"CPU-Playback", NULL, "ASRC-Playback"},
+ /* 1st half -- Normal DAPM routes */
{"Playback", NULL, "CPU-Playback"},
- {"ASRC-Capture", NULL, "CPU-Capture"},
{"CPU-Capture", NULL, "Capture"},
+ /* 2nd half -- ASRC DAPM routes */
+ {"CPU-Playback", NULL, "ASRC-Playback"},
+ {"ASRC-Capture", NULL, "CPU-Capture"},
};
static const struct snd_soc_dapm_route audio_map_ac97[] = {
- {"AC97 Playback", NULL, "ASRC-Playback"},
- {"Playback", NULL, "AC97 Playback"},
- {"ASRC-Capture", NULL, "AC97 Capture"},
- {"AC97 Capture", NULL, "Capture"},
+ /* 1st half -- Normal DAPM routes */
+ {"AC97 Playback", NULL, "CPU AC97 Playback"},
+ {"CPU AC97 Capture", NULL, "AC97 Capture"},
+ /* 2nd half -- ASRC DAPM routes */
+ {"CPU AC97 Playback", NULL, "ASRC-Playback"},
+ {"ASRC-Capture", NULL, "CPU AC97 Capture"},
+};
+
+static const struct snd_soc_dapm_route audio_map_tx[] = {
+ /* 1st half -- Normal DAPM routes */
+ {"Playback", NULL, "CPU-Playback"},
+ /* 2nd half -- ASRC DAPM routes */
+ {"CPU-Playback", NULL, "ASRC-Playback"},
+};
+
+static const struct snd_soc_dapm_route audio_map_rx[] = {
+ /* 1st half -- Normal DAPM routes */
+ {"CPU-Capture", NULL, "Capture"},
+ /* 2nd half -- ASRC DAPM routes */
+ {"ASRC-Capture", NULL, "CPU-Capture"},
};
/* Add all possible widgets into here without being redundant */
@@ -133,49 +171,129 @@ static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv)
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct codec_priv *codec_priv;
+ struct snd_soc_dai *codec_dai;
struct cpu_priv *cpu_priv = &priv->cpu_priv;
struct device *dev = rtd->card->dev;
+ unsigned int pll_out, sysclk_freq;
+ int codec_idx;
int ret;
priv->sample_rate = params_rate(params);
priv->sample_format = params_format(params);
+ priv->streams |= BIT(substream->stream);
- /*
- * If codec-dai is DAI Master and all configurations are already in the
- * set_bias_level(), bypass the remaining settings in hw_params().
- * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
- */
- if ((priv->card.set_bias_level &&
- priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) ||
- fsl_asoc_card_is_ac97(priv))
+ if (fsl_asoc_card_is_ac97(priv))
return 0;
+ if (!cpu_priv->sysclk_freq[tx] && cpu_priv->sysclk_ratio[tx])
+ sysclk_freq = priv->sample_rate * cpu_priv->sysclk_ratio[tx];
+ else
+ sysclk_freq = cpu_priv->sysclk_freq[tx];
+
/* Specific configurations of DAIs starts from here */
- ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
- cpu_priv->sysclk_freq[tx],
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), cpu_priv->sysclk_id[tx],
+ sysclk_freq,
cpu_priv->sysclk_dir[tx]);
- if (ret) {
+ if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set sysclk for cpu dai\n");
- return ret;
+ goto fail;
}
if (cpu_priv->slot_width) {
- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2,
+ if (!cpu_priv->slot_num)
+ cpu_priv->slot_num = 2;
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3,
+ cpu_priv->slot_num,
cpu_priv->slot_width);
- if (ret) {
+ if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set TDM slot for cpu dai\n");
- return ret;
+ goto fail;
+ }
+ }
+
+ /* Specific configuration for PLL */
+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
+ codec_priv = &priv->codec_priv[codec_idx];
+
+ if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
+ if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = priv->sample_rate * 384;
+ else
+ pll_out = priv->sample_rate * 256;
+
+ ret = snd_soc_dai_set_pll(codec_dai,
+ codec_priv->pll_id,
+ codec_priv->mclk_id,
+ codec_priv->mclk_freq, pll_out);
+ if (ret) {
+ dev_err(dev, "failed to start FLL: %d\n", ret);
+ goto fail;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ codec_priv->fll_id,
+ pll_out, SND_SOC_CLOCK_IN);
+
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set SYSCLK: %d\n", ret);
+ goto fail;
+ }
}
}
return 0;
+
+fail:
+ priv->streams &= ~BIT(substream->stream);
+ return ret;
}
-static struct snd_soc_ops fsl_asoc_card_ops = {
+static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct codec_priv *codec_priv;
+ struct snd_soc_dai *codec_dai;
+ struct device *dev = rtd->card->dev;
+ int codec_idx;
+ int ret;
+
+ priv->streams &= ~BIT(substream->stream);
+
+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
+ codec_priv = &priv->codec_priv[codec_idx];
+
+ if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
+ /* Force freq to be free_freq to avoid error message in codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ codec_priv->mclk_id,
+ codec_priv->free_freq,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "failed to switch away from FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai,
+ codec_priv->pll_id, 0, 0, 0);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to stop FLL: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops fsl_asoc_card_ops = {
.hw_params = fsl_asoc_card_hw_params,
+ .hw_free = fsl_asoc_card_hw_free,
};
static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -190,12 +308,12 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_none(mask);
- snd_mask_set(mask, priv->asrc_format);
+ snd_mask_set_format(mask, priv->asrc_format);
return 0;
}
-static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
+static const struct snd_soc_dai_link fsl_asoc_card_dai[] = {
/* Default ASoC DAI Link*/
{
.name = "HiFi",
@@ -206,93 +324,17 @@ static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
{
.name = "HiFi-ASRC-FE",
.stream_name = "HiFi-ASRC-FE",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.dynamic = 1,
},
{
.name = "HiFi-ASRC-BE",
.stream_name = "HiFi-ASRC-BE",
- .platform_name = "snd-soc-dummy",
.be_hw_params_fixup = be_hw_params_fixup,
.ops = &fsl_asoc_card_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.no_pcm = 1,
},
};
-static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
- struct snd_soc_dapm_context *dapm,
- enum snd_soc_bias_level level)
-{
- struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai *codec_dai;
- struct codec_priv *codec_priv = &priv->codec_priv;
- struct device *dev = card->dev;
- unsigned int pll_out;
- int ret;
-
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
- if (dapm->dev != codec_dai->dev)
- return 0;
-
- switch (level) {
- case SND_SOC_BIAS_PREPARE:
- if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
- break;
-
- if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
- pll_out = priv->sample_rate * 384;
- else
- pll_out = priv->sample_rate * 256;
-
- ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
- codec_priv->mclk_id,
- codec_priv->mclk_freq, pll_out);
- if (ret) {
- dev_err(dev, "failed to start FLL: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
- pll_out, SND_SOC_CLOCK_IN);
- if (ret) {
- dev_err(dev, "failed to set SYSCLK: %d\n", ret);
- return ret;
- }
- break;
-
- case SND_SOC_BIAS_STANDBY:
- if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
- break;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
- codec_priv->mclk_freq,
- SND_SOC_CLOCK_IN);
- if (ret) {
- dev_err(dev, "failed to switch away from FLL: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
- if (ret) {
- dev_err(dev, "failed to stop FLL: %d\n", ret);
- return ret;
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
static int fsl_asoc_card_audmux_init(struct device_node *np,
struct fsl_asoc_card_priv *priv)
{
@@ -324,8 +366,8 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
* If only 4 wires are needed, just set SSI into
* synchronous mode and enable 4 PADs in IOMUX.
*/
- switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
@@ -335,7 +377,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RCLKDIR |
@@ -345,7 +387,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_RFSDIR |
IMX_AUDMUX_V2_PTCR_TFSDIR;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RFSDIR |
@@ -355,7 +397,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_RCLKDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
@@ -421,20 +463,124 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
return 0;
}
+static int fsl_asoc_card_spdif_init(struct device_node *codec_np[],
+ struct device_node *cpu_np,
+ const char *codec_dai_name[],
+ struct fsl_asoc_card_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (!of_node_name_eq(cpu_np, "spdif")) {
+ dev_err(dev, "CPU phandle invalid, should be an SPDIF device\n");
+ return -EINVAL;
+ }
+
+ priv->dai_link[0].playback_only = true;
+ priv->dai_link[0].capture_only = true;
+
+ for (int i = 0; i < 2; i++) {
+ if (!codec_np[i])
+ break;
+
+ if (of_device_is_compatible(codec_np[i], "linux,spdif-dit")) {
+ priv->dai_link[0].capture_only = false;
+ codec_dai_name[i] = "dit-hifi";
+ } else if (of_device_is_compatible(codec_np[i], "linux,spdif-dir")) {
+ priv->dai_link[0].playback_only = false;
+ codec_dai_name[i] = "dir-hifi";
+ }
+ }
+
+ // Old SPDIF DT binding
+ if (!codec_np[0]) {
+ codec_dai_name[0] = snd_soc_dummy_dlc.dai_name;
+ if (of_property_read_bool(np, "spdif-out"))
+ priv->dai_link[0].capture_only = false;
+ if (of_property_read_bool(np, "spdif-in"))
+ priv->dai_link[0].playback_only = false;
+ }
+
+ if (priv->dai_link[0].playback_only && priv->dai_link[0].capture_only) {
+ dev_err(dev, "no enabled S/PDIF DAI link\n");
+ return -EINVAL;
+ }
+
+ if (priv->dai_link[0].playback_only) {
+ priv->dai_link[1].playback_only = true;
+ priv->dai_link[2].playback_only = true;
+ priv->card.dapm_routes = audio_map_tx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
+ } else if (priv->dai_link[0].capture_only) {
+ priv->dai_link[1].capture_only = true;
+ priv->dai_link[2].capture_only = true;
+ priv->card.dapm_routes = audio_map_rx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx);
+ }
+
+ // No DAPM routes with old bindings and dummy codec
+ if (!codec_np[0]) {
+ priv->card.dapm_routes = NULL;
+ priv->card.num_dapm_routes = 0;
+ }
+
+ if (codec_np[0] && codec_np[1]) {
+ priv->dai_link[0].num_codecs = 2;
+ priv->dai_link[2].num_codecs = 2;
+ }
+
+ return 0;
+}
+
+static int hp_jack_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(jack->card);
+
+ if (event & SND_JACK_HEADPHONE)
+ /* Disable speaker if headphone is plugged in */
+ return snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+ else
+ return snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+}
+
+static struct notifier_block hp_jack_nb = {
+ .notifier_call = hp_jack_event,
+};
+
+static int mic_jack_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(jack->card);
+
+ if (event & SND_JACK_MICROPHONE)
+ /* Disable dmic if microphone is plugged in */
+ return snd_soc_dapm_disable_pin(dapm, "DMIC");
+ else
+ return snd_soc_dapm_enable_pin(dapm, "DMIC");
+}
+
+static struct notifier_block mic_jack_nb = {
+ .notifier_call = mic_jack_event,
+};
+
static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_pcm_runtime *rtd = list_first_entry(
&card->rtd_list, struct snd_soc_pcm_runtime, list);
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct codec_priv *codec_priv = &priv->codec_priv;
+ struct snd_soc_dai *codec_dai;
+ struct codec_priv *codec_priv;
struct device *dev = card->dev;
+ int codec_idx;
int ret;
if (fsl_asoc_card_is_ac97(priv)) {
#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
/*
* Use slots 3/4 for S/PDIF so SSI won't try to enable
@@ -448,11 +594,18 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
return 0;
}
- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
- codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
- if (ret) {
- dev_err(dev, "failed to set sysclk in %s\n", __func__);
- return ret;
+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
+ codec_priv = &priv->codec_priv[codec_idx];
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
+ codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
+ return ret;
+ }
+
+ if (!IS_ERR_OR_NULL(codec_priv->mclk))
+ clk_prepare_enable(codec_priv->mclk);
}
return 0;
@@ -460,13 +613,21 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
static int fsl_asoc_card_probe(struct platform_device *pdev)
{
- struct device_node *cpu_np, *codec_np, *asrc_np;
+ struct device_node *cpu_np, *asrc_np;
+ struct snd_soc_dai_link_component *codec_comp;
+ struct device_node *codec_np[2];
struct device_node *np = pdev->dev.of_node;
struct platform_device *asrc_pdev = NULL;
+ struct device_node *bitclkprovider = NULL;
+ struct device_node *frameprovider = NULL;
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
- struct i2c_client *codec_dev;
- const char *codec_dai_name;
+ struct device *codec_dev[2] = { NULL, NULL };
+ struct snd_soc_dai_link_component *dlc;
+ const char *codec_dai_name[2];
+ const char *codec_dev_name[2];
+ u32 asrc_fmt = 0;
+ int codec_idx;
u32 width;
int ret;
@@ -474,10 +635,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ priv->pdev = pdev;
+
cpu_np = of_parse_phandle(np, "audio-cpu", 0);
- /* Give a chance to old DT binding */
+ /* Give a chance to old DT bindings */
if (!cpu_np)
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
+ if (!cpu_np)
+ cpu_np = of_parse_phandle(np, "spdif-controller", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
ret = -EINVAL;
@@ -491,23 +656,42 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto fail;
}
- codec_np = of_parse_phandle(np, "audio-codec", 0);
- if (codec_np)
- codec_dev = of_find_i2c_device_by_node(codec_np);
- else
- codec_dev = NULL;
+ codec_np[0] = of_parse_phandle(np, "audio-codec", 0);
+ codec_np[1] = of_parse_phandle(np, "audio-codec", 1);
+
+ for (codec_idx = 0; codec_idx < 2; codec_idx++) {
+ if (codec_np[codec_idx]) {
+ struct platform_device *codec_pdev;
+ struct i2c_client *codec_i2c;
+
+ codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]);
+ if (codec_i2c) {
+ codec_dev[codec_idx] = &codec_i2c->dev;
+ codec_dev_name[codec_idx] = codec_i2c->name;
+ }
+ if (!codec_dev[codec_idx]) {
+ codec_pdev = of_find_device_by_node(codec_np[codec_idx]);
+ if (codec_pdev) {
+ codec_dev[codec_idx] = &codec_pdev->dev;
+ codec_dev_name[codec_idx] = codec_pdev->name;
+ }
+ }
+ }
+ }
asrc_np = of_parse_phandle(np, "audio-asrc", 0);
if (asrc_np)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
- if (codec_dev) {
- struct clk *codec_clk = clk_get(&codec_dev->dev, NULL);
-
- if (!IS_ERR(codec_clk)) {
- priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
- clk_put(codec_clk);
+ for (codec_idx = 0; codec_idx < 2; codec_idx++) {
+ if (codec_dev[codec_idx]) {
+ struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL);
+
+ if (!IS_ERR(codec_clk)) {
+ priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
+ }
}
}
@@ -518,97 +702,268 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Assign a default DAI format, and allow each card to overwrite it */
priv->dai_fmt = DAI_FMT_BASE;
+ memcpy(priv->dai_link, fsl_asoc_card_dai,
+ sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
+ /*
+ * "Default ASoC DAI Link": 1 cpus, 2 codecs, 1 platforms
+ * "DPCM Link Front-End": 1 cpus, 1 codecs (dummy), 1 platforms
+ * "DPCM Link Back-End": 1 cpus, 2 codecs
+ * totally 10 components
+ */
+ dlc = devm_kcalloc(&pdev->dev, 10, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc) {
+ ret = -ENOMEM;
+ goto asrc_fail;
+ }
+
+ priv->dai_link[0].cpus = &dlc[0];
+ priv->dai_link[0].num_cpus = 1;
+ priv->dai_link[0].codecs = &dlc[1];
+ priv->dai_link[0].num_codecs = 1;
+ priv->dai_link[0].platforms = &dlc[3];
+ priv->dai_link[0].num_platforms = 1;
+
+ priv->dai_link[1].cpus = &dlc[4];
+ priv->dai_link[1].num_cpus = 1;
+ priv->dai_link[1].codecs = &dlc[5];
+ priv->dai_link[1].num_codecs = 0; /* dummy */
+ priv->dai_link[1].platforms = &dlc[6];
+ priv->dai_link[1].num_platforms = 1;
+
+ priv->dai_link[2].cpus = &dlc[7];
+ priv->dai_link[2].num_cpus = 1;
+ priv->dai_link[2].codecs = &dlc[8];
+ priv->dai_link[2].num_codecs = 1;
+
+ priv->card.dapm_routes = audio_map;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
+ priv->card.driver_name = DRIVER_NAME;
+
+ for (codec_idx = 0; codec_idx < 2; codec_idx++) {
+ priv->codec_priv[codec_idx].fll_id = -1;
+ priv->codec_priv[codec_idx].pll_id = -1;
+ }
+
/* Diversify the card configurations */
if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
- codec_dai_name = "cs42888";
- priv->card.set_bias_level = NULL;
- priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
- priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
+ codec_dai_name[0] = "cs42888";
+ priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq;
+ priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq;
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.slot_width = 32;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
+ codec_dai_name[0] = "cs4271-hifi";
+ priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
- codec_dai_name = "sgtl5000";
- priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ codec_dai_name[0] = "sgtl5000";
+ priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) {
+ codec_dai_name[0] = "tlv320aic32x4-hifi";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) {
+ codec_dai_name[0] = "tlv320dac31xx-hifi";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ priv->dai_link[1].playback_only = 1;
+ priv->dai_link[2].playback_only = 1;
+ priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
+ priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+ priv->card.dapm_routes = audio_map_tx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
- codec_dai_name = "wm8962";
- priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
- priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
- priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
- priv->codec_priv.pll_id = WM8962_FLL;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ codec_dai_name[0] = "wm8962";
+ priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK;
+ priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL;
+ priv->codec_priv[0].pll_id = WM8962_FLL;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
- codec_dai_name = "wm8960-hifi";
- priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
- priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
- priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ codec_dai_name[0] = "wm8960-hifi";
+ priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO;
+ priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
- codec_dai_name = "ac97-hifi";
- priv->card.set_bias_level = NULL;
+ codec_dai_name[0] = "ac97-hifi";
priv->dai_fmt = SND_SOC_DAIFMT_AC97;
+ priv->card.dapm_routes = audio_map_ac97;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97);
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) {
+ codec_dai_name[0] = "fsl-mqs-dai";
+ priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J |
+ SND_SOC_DAIFMT_CBC_CFC |
+ SND_SOC_DAIFMT_NB_NF;
+ priv->dai_link[1].playback_only = 1;
+ priv->dai_link[2].playback_only = 1;
+ priv->card.dapm_routes = audio_map_tx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) {
+ codec_dai_name[0] = "wm8524-hifi";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ priv->dai_link[1].playback_only = 1;
+ priv->dai_link[2].playback_only = 1;
+ priv->cpu_priv.slot_width = 32;
+ priv->card.dapm_routes = audio_map_tx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
+ priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
+ priv->cpu_priv.sysclk_ratio[TX] = 256;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) {
+ codec_dai_name[0] = "si476x-codec";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ priv->card.dapm_routes = audio_map_rx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx);
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) {
+ codec_dai_name[0] = "wm8994-aif1";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1;
+ priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1;
+ priv->codec_priv[0].pll_id = WM8994_FLL1;
+ priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq;
+ priv->card.dapm_routes = NULL;
+ priv->card.num_dapm_routes = 0;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) {
+ codec_dai_name[0] = "nau8822-hifi";
+ priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK;
+ priv->codec_priv[0].fll_id = NAU8822_CLK_PLL;
+ priv->codec_priv[0].pll_id = NAU8822_CLK_PLL;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ if (codec_dev[0])
+ priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL);
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) {
+ codec_dai_name[0] = "wm8904-hifi";
+ priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK;
+ priv->codec_priv[0].fll_id = WM8904_CLK_FLL;
+ priv->codec_priv[0].pll_id = WM8904_FLL_MCLK;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) {
+ ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv);
+ if (ret)
+ goto asrc_fail;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
ret = -EINVAL;
goto asrc_fail;
}
- if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
- dev_err(&pdev->dev, "failed to find codec device\n");
- ret = -EINVAL;
+ /*
+ * Allow setting mclk-id from the device-tree node. Otherwise, the
+ * default value for each card configuration is used.
+ */
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ of_property_read_u32_index(np, "mclk-id", codec_idx,
+ &priv->codec_priv[codec_idx].mclk_id);
+ }
+
+ /* Format info from DT is optional. */
+ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider);
+ if (bitclkprovider || frameprovider) {
+ unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL);
+ bool codec_bitclkprovider = false;
+ bool codec_frameprovider = false;
+
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ if (bitclkprovider && codec_np[codec_idx] == bitclkprovider)
+ codec_bitclkprovider = true;
+ if (frameprovider && codec_np[codec_idx] == frameprovider)
+ codec_frameprovider = true;
+ }
+
+ if (codec_bitclkprovider)
+ daifmt |= (codec_frameprovider) ?
+ SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC;
+ else
+ daifmt |= (codec_frameprovider) ?
+ SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC;
+
+ /* Override dai_fmt with value from DT */
+ priv->dai_fmt = daifmt;
+ }
+
+ /* Change direction according to format */
+ if (priv->dai_fmt & SND_SOC_DAIFMT_CBP_CFP) {
+ priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_IN;
+ priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_IN;
+ }
+
+ of_node_put(bitclkprovider);
+ of_node_put(frameprovider);
+
+ if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0]
+ && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) {
+ dev_dbg(&pdev->dev, "failed to find codec device\n");
+ ret = -EPROBE_DEFER;
goto asrc_fail;
}
/* Common settings for corresponding Freescale CPU DAI driver */
- if (strstr(cpu_np->name, "ssi")) {
+ if (of_node_name_eq(cpu_np, "ssi")) {
/* Only SSI needs to configure AUDMUX */
ret = fsl_asoc_card_audmux_init(np, priv);
if (ret) {
dev_err(&pdev->dev, "failed to init audmux\n");
goto asrc_fail;
}
- } else if (strstr(cpu_np->name, "esai")) {
+ } else if (of_node_name_eq(cpu_np, "esai")) {
+ struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal");
+
+ if (!IS_ERR(esai_clk)) {
+ priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk);
+ priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk);
+ clk_put(esai_clk);
+ } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto asrc_fail;
+ }
+
priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
- } else if (strstr(cpu_np->name, "sai")) {
+ } else if (of_node_name_eq(cpu_np, "sai")) {
priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
}
- snprintf(priv->name, sizeof(priv->name), "%s-audio",
- fsl_asoc_card_is_ac97(priv) ? "ac97" :
- codec_dev->name);
-
/* Initialize sound card */
- priv->pdev = pdev;
priv->card.dev = &pdev->dev;
- priv->card.name = priv->name;
+ priv->card.owner = THIS_MODULE;
+ ret = snd_soc_of_parse_card_name(&priv->card, "model");
+ if (ret) {
+ snprintf(priv->name, sizeof(priv->name), "%s-audio",
+ fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]);
+ priv->card.name = priv->name;
+ }
priv->card.dai_link = priv->dai_link;
- priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ?
- audio_map_ac97 : audio_map;
priv->card.late_probe = fsl_asoc_card_late_probe;
- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
- memcpy(priv->dai_link, fsl_asoc_card_dai,
- sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
+ /* Drop the second half of DAPM routes -- ASRC */
+ if (!asrc_pdev)
+ priv->card.num_dapm_routes /= 2;
- ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing");
- if (ret) {
- dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
- goto asrc_fail;
+ if (of_property_present(np, "audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+ goto asrc_fail;
+ }
}
/* Normal DAI Link */
- priv->dai_link[0].cpu_of_node = cpu_np;
- priv->dai_link[0].codec_dai_name = codec_dai_name;
+ priv->dai_link[0].cpus->of_node = cpu_np;
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ codec_comp->dai_name = codec_dai_name[codec_idx];
+ }
- if (!fsl_asoc_card_is_ac97(priv))
- priv->dai_link[0].codec_of_node = codec_np;
- else {
+ // Old SPDIF DT binding support
+ if (codec_dai_name[0] == snd_soc_dummy_dlc.dai_name)
+ priv->dai_link[0].codecs[0].name = snd_soc_dummy_dlc.name;
+
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ codec_comp->of_node = codec_np[codec_idx];
+ }
+ } else {
u32 idx;
ret = of_property_read_u32(cpu_np, "cell-index", &idx);
@@ -618,25 +973,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto asrc_fail;
}
- priv->dai_link[0].codec_name =
+ priv->dai_link[0].codecs[0].name =
devm_kasprintf(&pdev->dev, GFP_KERNEL,
"ac97-codec.%u",
(unsigned int)idx);
+ if (!priv->dai_link[0].codecs[0].name) {
+ ret = -ENOMEM;
+ goto asrc_fail;
+ }
}
- priv->dai_link[0].platform_of_node = cpu_np;
+ priv->dai_link[0].platforms->of_node = cpu_np;
priv->dai_link[0].dai_fmt = priv->dai_fmt;
priv->card.num_links = 1;
if (asrc_pdev) {
- /* DPCM DAI Links only if ASRC exsits */
- priv->dai_link[1].cpu_of_node = asrc_np;
- priv->dai_link[1].platform_of_node = asrc_np;
- priv->dai_link[2].codec_dai_name = codec_dai_name;
- priv->dai_link[2].codec_of_node = codec_np;
- priv->dai_link[2].codec_name =
- priv->dai_link[0].codec_name;
- priv->dai_link[2].cpu_of_node = cpu_np;
+ /* DPCM DAI Links only if ASRC exists */
+ priv->dai_link[1].cpus->of_node = asrc_np;
+ priv->dai_link[1].platforms->of_node = asrc_np;
+ for_each_link_codecs((&(priv->dai_link[2])), codec_idx, codec_comp) {
+ codec_comp->dai_name = priv->dai_link[0].codecs[codec_idx].dai_name;
+ codec_comp->of_node = priv->dai_link[0].codecs[codec_idx].of_node;
+ codec_comp->name = priv->dai_link[0].codecs[codec_idx].name;
+ }
+ priv->dai_link[2].cpus->of_node = cpu_np;
priv->dai_link[2].dai_fmt = priv->dai_fmt;
priv->card.num_links = 3;
@@ -648,17 +1008,23 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto asrc_fail;
}
- ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-format", &asrc_fmt);
+ priv->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
if (ret) {
- dev_err(&pdev->dev, "failed to get output rate\n");
- ret = -EINVAL;
- goto asrc_fail;
+ /* Fallback to old binding; translate to asrc_format */
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-width",
+ &width);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to decide output format\n");
+ goto asrc_fail;
+ }
+
+ if (width == 24)
+ priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ else
+ priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
}
-
- if (width == 24)
- priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
- else
- priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
}
/* Finish card registering */
@@ -666,12 +1032,44 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(&priv->card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+ goto asrc_fail;
+ }
+
+ /*
+ * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and
+ * simple_util_init_jack() uses these properties for creating
+ * Headphone Jack and Microphone Jack.
+ *
+ * The notifier is initialized in snd_soc_card_jack_new(), then
+ * snd_soc_jack_notifier_register can be called.
+ */
+ if (of_property_read_bool(np, "hp-det-gpios") ||
+ of_property_read_bool(np, "hp-det-gpio") /* deprecated */) {
+ ret = simple_util_init_jack(&priv->card, &priv->hp_jack,
+ 1, NULL, "Headphone Jack");
+ if (ret)
+ goto asrc_fail;
+
+ snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb);
+ }
+
+ if (of_property_read_bool(np, "mic-det-gpios") ||
+ of_property_read_bool(np, "mic-det-gpio") /* deprecated */) {
+ ret = simple_util_init_jack(&priv->card, &priv->mic_jack,
+ 0, NULL, "Mic Jack");
+ if (ret)
+ goto asrc_fail;
+
+ snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb);
+ }
asrc_fail:
of_node_put(asrc_np);
- of_node_put(codec_np);
+ of_node_put(codec_np[0]);
+ of_node_put(codec_np[1]);
+ put_device(&cpu_pdev->dev);
fail:
of_node_put(cpu_np);
@@ -681,9 +1079,19 @@ fail:
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
{ .compatible = "fsl,imx-audio-ac97", },
{ .compatible = "fsl,imx-audio-cs42888", },
+ { .compatible = "fsl,imx-audio-cs427x", },
+ { .compatible = "fsl,imx-audio-tlv320aic32x4", },
+ { .compatible = "fsl,imx-audio-tlv320aic31xx", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
{ .compatible = "fsl,imx-audio-wm8960", },
+ { .compatible = "fsl,imx-audio-mqs", },
+ { .compatible = "fsl,imx-audio-wm8524", },
+ { .compatible = "fsl,imx-audio-si476x", },
+ { .compatible = "fsl,imx-audio-wm8958", },
+ { .compatible = "fsl,imx-audio-nau8822", },
+ { .compatible = "fsl,imx-audio-wm8904", },
+ { .compatible = "fsl,imx-audio-spdif", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
@@ -691,7 +1099,7 @@ MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
static struct platform_driver fsl_asoc_card_driver = {
.probe = fsl_asoc_card_probe,
.driver = {
- .name = "fsl-asoc-card",
+ .name = DRIVER_NAME,
.pm = &snd_soc_pm_ops,
.of_match_table = fsl_asoc_card_dt_ids,
},
@@ -700,5 +1108,5 @@ module_platform_driver(fsl_asoc_card_driver);
MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
-MODULE_ALIAS("platform:fsl-asoc-card");
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index c1a0e01cb8e7..92fb16f7be45 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -1,21 +1,17 @@
-/*
- * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
- *
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
- *
- * Author: Nicolin Chen <nicoleotsuka@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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
+//
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+//
+// Author: Nicolin Chen <nicoleotsuka@gmail.com>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_platform.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <linux/pm_runtime.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -23,68 +19,193 @@
#include "fsl_asrc.h"
#define IDEAL_RATIO_DECIMAL_DEPTH 26
+#define DIVIDER_NUM 64
+#define INIT_RETRY_NUM 50
#define pair_err(fmt, ...) \
- dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+ dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
#define pair_dbg(fmt, ...) \
- dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
-
-/* Sample rates are aligned with that defined in pcm.h file */
-static const u8 process_option[][12][2] = {
- /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
- {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
- {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
- {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
- {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
- {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
- {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
- {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
- {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
-};
+ dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+#define pair_warn(fmt, ...) \
+ dev_warn(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
/* Corresponding to process_option */
-static int supported_input_rate[] = {
- 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200,
- 96000, 176400, 192000,
+static unsigned int supported_asrc_rate[] = {
+ 5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ 64000, 88200, 96000, 128000, 176400, 192000,
};
-static int supported_asrc_rate[] = {
- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
+static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
+ .count = ARRAY_SIZE(supported_asrc_rate),
+ .list = supported_asrc_rate,
};
-/**
+/*
* The following tables map the relationship between asrc_inclk/asrc_outclk in
* fsl_asrc.h and the registers of ASRCSR
*/
-static unsigned char input_clk_map_imx35[] = {
+static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
};
-static unsigned char output_clk_map_imx35[] = {
+static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
};
/* i.MX53 uses the same map for input and output */
-static unsigned char input_clk_map_imx53[] = {
+static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
+ 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
+ 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
};
-static unsigned char output_clk_map_imx53[] = {
+static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
+ 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
+ 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
+};
+
+/*
+ * i.MX8QM/i.MX8QXP uses the same map for input and output.
+ * clk_map_imx8qm[0] is for i.MX8QM asrc0
+ * clk_map_imx8qm[1] is for i.MX8QM asrc1
+ * clk_map_imx8qxp[0] is for i.MX8QXP asrc0
+ * clk_map_imx8qxp[1] is for i.MX8QXP asrc1
+ */
+static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = {
+ {
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+ },
+ {
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
+ 0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+ 0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+ },
+};
+
+static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
+ {
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf,
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+ },
+ {
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
+ 0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+ 0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+ },
+};
+
+/*
+ * According to RM, the divider range is 1 ~ 8,
+ * prescaler is power of 2 from 1 ~ 128.
+ */
+static int asrc_clk_divider[DIVIDER_NUM] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, /* divider = 1 */
+ 2, 4, 8, 16, 32, 64, 128, 256, /* divider = 2 */
+ 3, 6, 12, 24, 48, 96, 192, 384, /* divider = 3 */
+ 4, 8, 16, 32, 64, 128, 256, 512, /* divider = 4 */
+ 5, 10, 20, 40, 80, 160, 320, 640, /* divider = 5 */
+ 6, 12, 24, 48, 96, 192, 384, 768, /* divider = 6 */
+ 7, 14, 28, 56, 112, 224, 448, 896, /* divider = 7 */
+ 8, 16, 32, 64, 128, 256, 512, 1024, /* divider = 8 */
};
-static unsigned char *clk_map[2];
+/*
+ * Check if the divider is available for internal ratio mode
+ */
+static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div)
+{
+ u32 rem, i;
+ u64 n;
+
+ if (div)
+ *div = 0;
+
+ if (clk_rate == 0 || rate == 0)
+ return false;
+
+ n = clk_rate;
+ rem = do_div(n, rate);
+
+ if (div)
+ *div = n;
+
+ if (rem != 0)
+ return false;
+
+ for (i = 0; i < DIVIDER_NUM; i++) {
+ if (n == asrc_clk_divider[i])
+ break;
+ }
+
+ if (i == DIVIDER_NUM)
+ return false;
+
+ return true;
+}
+
+/**
+ * fsl_asrc_sel_proc - Select the pre-processing and post-processing options
+ * @inrate: input sample rate
+ * @outrate: output sample rate
+ * @pre_proc: return value for pre-processing option
+ * @post_proc: return value for post-processing option
+ *
+ * Make sure to exclude following unsupported cases before
+ * calling this function:
+ * 1) inrate > 8.125 * outrate
+ * 2) inrate > 16.125 * outrate
+ *
+ */
+static void fsl_asrc_sel_proc(int inrate, int outrate,
+ int *pre_proc, int *post_proc)
+{
+ bool post_proc_cond2;
+ bool post_proc_cond0;
+
+ /* select pre_proc between [0, 2] */
+ if (inrate * 8 > 33 * outrate)
+ *pre_proc = 2;
+ else if (inrate * 8 > 15 * outrate) {
+ if (inrate > 152000)
+ *pre_proc = 2;
+ else
+ *pre_proc = 1;
+ } else if (inrate < 76000)
+ *pre_proc = 0;
+ else if (inrate > 152000)
+ *pre_proc = 2;
+ else
+ *pre_proc = 1;
+
+ /* Condition for selection of post-processing */
+ post_proc_cond2 = (inrate * 15 > outrate * 16 && outrate < 56000) ||
+ (inrate > 56000 && outrate < 56000);
+ post_proc_cond0 = inrate * 23 < outrate * 8;
+
+ if (post_proc_cond2)
+ *post_proc = 2;
+ else if (post_proc_cond0)
+ *post_proc = 0;
+ else
+ *post_proc = 1;
+}
/**
- * Request ASRC pair
+ * fsl_asrc_request_pair - Request ASRC pair
+ * @channels: number of channels
+ * @pair: pointer to pair
*
* It assigns pair by the order of A->C->B because allocation of pair B,
* within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
@@ -93,15 +214,15 @@ static unsigned char *clk_map[2];
static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
{
enum asrc_pair_index index = ASRC_INVALID_PAIR;
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
- struct device *dev = &asrc_priv->pdev->dev;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
unsigned long lock_flags;
int i, ret = 0;
- spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+ spin_lock_irqsave(&asrc->lock, lock_flags);
for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
- if (asrc_priv->pair[i] != NULL)
+ if (asrc->pair[i] != NULL)
continue;
index = i;
@@ -113,54 +234,58 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
if (index == ASRC_INVALID_PAIR) {
dev_err(dev, "all pairs are busy now\n");
ret = -EBUSY;
- } else if (asrc_priv->channel_avail < channels) {
+ } else if (asrc->channel_avail < channels) {
dev_err(dev, "can't afford required channels: %d\n", channels);
ret = -EINVAL;
} else {
- asrc_priv->channel_avail -= channels;
- asrc_priv->pair[index] = pair;
+ asrc->channel_avail -= channels;
+ asrc->pair[index] = pair;
pair->channels = channels;
pair->index = index;
}
- spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+ spin_unlock_irqrestore(&asrc->lock, lock_flags);
return ret;
}
/**
- * Release ASRC pair
+ * fsl_asrc_release_pair - Release ASRC pair
+ * @pair: pair to release
*
- * It clears the resource from asrc_priv and releases the occupied channels.
+ * It clears the resource from asrc and releases the occupied channels.
*/
static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
unsigned long lock_flags;
/* Make sure the pair is disabled */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), 0);
- spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+ spin_lock_irqsave(&asrc->lock, lock_flags);
- asrc_priv->channel_avail += pair->channels;
- asrc_priv->pair[index] = NULL;
+ asrc->channel_avail += pair->channels;
+ asrc->pair[index] = NULL;
pair->error = 0;
- spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+ spin_unlock_irqrestore(&asrc->lock, lock_flags);
}
/**
- * Configure input and output thresholds
+ * fsl_asrc_set_watermarks- configure input and output thresholds
+ * @pair: pointer to pair
+ * @in: input threshold
+ * @out: output threshold
*/
static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
- regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
ASRMCRi_EXTTHRSHi_MASK |
ASRMCRi_INFIFO_THRESHOLD_MASK |
ASRMCRi_OUTFIFO_THRESHOLD_MASK,
@@ -170,7 +295,9 @@ static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
}
/**
- * Calculate the total divisor between asrck clock rate and sample rate
+ * fsl_asrc_cal_asrck_divisor - Calculate the total divisor between asrck clock rate and sample rate
+ * @pair: pointer to pair
+ * @div: divider
*
* It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
*/
@@ -186,14 +313,17 @@ static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
}
/**
- * Calculate and set the ratio for Ideal Ratio mode only
+ * fsl_asrc_set_ideal_ratio - Calculate and set the ratio for Ideal Ratio mode only
+ * @pair: pointer to pair
+ * @inrate: input rate
+ * @outrate: output rate
*
* The ratio is a 32-bit fixed point value with 26 fractional bits.
*/
static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
int inrate, int outrate)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
unsigned long ratio;
int i;
@@ -222,29 +352,44 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
break;
}
- regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio);
- regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24);
+ regmap_write(asrc->regmap, REG_ASRIDRL(index), ratio);
+ regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
return 0;
}
/**
- * Configure the assigned ASRC pair
+ * fsl_asrc_config_pair - Configure the assigned ASRC pair
+ * @pair: pointer to pair
+ * @use_ideal_rate: boolean configuration
*
* It configures those ASRC registers according to a configuration instance
* of struct asrc_config which includes in/output sample rate, width, channel
* and clock settings.
+ *
+ * Note:
+ * The ideal ratio configuration can work with a flexible clock rate setting.
+ * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
+ * For a regular audio playback, the clock rate should not be slower than an
+ * clock rate aligning with the output sample rate; For a use case requiring
+ * faster conversion, set use_ideal_rate to have the faster speed.
*/
-static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
+static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
{
- struct asrc_config *config = pair->config;
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
+ struct asrc_config *config = pair_priv->config;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
enum asrc_pair_index index = pair->index;
+ enum asrc_word_width input_word_width;
+ enum asrc_word_width output_word_width;
u32 inrate, outrate, indiv, outdiv;
u32 clk_index[2], div[2];
+ u64 clk_rate;
int in, out, channels;
+ int pre_proc, post_proc;
struct clk *clk;
- bool ideal;
+ bool ideal, div_avail;
if (!config) {
pair_err("invalid pair config\n");
@@ -257,9 +402,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
- /* Validate output width */
- if (config->output_word_width == ASRC_WIDTH_8_BIT) {
- pair_err("does not support 8bit width output\n");
+ switch (snd_pcm_format_width(config->input_format)) {
+ case 8:
+ input_word_width = ASRC_WIDTH_8_BIT;
+ break;
+ case 16:
+ input_word_width = ASRC_WIDTH_16_BIT;
+ break;
+ case 24:
+ input_word_width = ASRC_WIDTH_24_BIT;
+ break;
+ default:
+ pair_err("does not support this input format, %d\n",
+ config->input_format);
+ return -EINVAL;
+ }
+
+ switch (snd_pcm_format_width(config->output_format)) {
+ case 16:
+ output_word_width = ASRC_WIDTH_16_BIT;
+ break;
+ case 24:
+ output_word_width = ASRC_WIDTH_24_BIT;
+ break;
+ default:
+ pair_err("does not support this output format, %d\n",
+ config->output_format);
return -EINVAL;
}
@@ -268,11 +436,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
ideal = config->inclk == INCLK_NONE;
/* Validate input and output sample rates */
- for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++)
- if (inrate == supported_input_rate[in])
+ for (in = 0; in < ARRAY_SIZE(supported_asrc_rate); in++)
+ if (inrate == supported_asrc_rate[in])
break;
- if (in == ARRAY_SIZE(supported_input_rate)) {
+ if (in == ARRAY_SIZE(supported_asrc_rate)) {
pair_err("unsupported input sample rate: %dHz\n", inrate);
return -EINVAL;
}
@@ -286,60 +454,74 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
- if ((outrate > 8000 && outrate < 30000) &&
- (outrate/inrate > 24 || inrate/outrate > 8)) {
+ if ((outrate >= 5512 && outrate <= 30000) &&
+ (outrate > 24 * inrate || inrate > 8 * outrate)) {
pair_err("exceed supported ratio range [1/24, 8] for \
inrate/outrate: %d/%d\n", inrate, outrate);
return -EINVAL;
}
/* Validate input and output clock sources */
- clk_index[IN] = clk_map[IN][config->inclk];
- clk_index[OUT] = clk_map[OUT][config->outclk];
+ clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
+ clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
/* We only have output clock for ideal ratio mode */
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
- div[IN] = clk_get_rate(clk) / inrate;
- if (div[IN] == 0) {
+ clk_rate = clk_get_rate(clk);
+ div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]);
+
+ /*
+ * The divider range is [1, 1024], defined by the hardware. For non-
+ * ideal ratio configuration, clock rate has to be strictly aligned
+ * with the sample rate. For ideal ratio configuration, clock rates
+ * only result in different converting speeds. So remainder does not
+ * matter, as long as we keep the divider within its valid range.
+ */
+ if (div[IN] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]);
return -EINVAL;
}
- clk = asrc_priv->asrck_clk[clk_index[OUT]];
+ div[IN] = min_t(u32, 1024, div[IN]);
- /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
- if (ideal)
- div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
+ clk = asrc_priv->asrck_clk[clk_index[OUT]];
+ clk_rate = clk_get_rate(clk);
+ if (ideal && use_ideal_rate)
+ div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]);
else
- div[OUT] = clk_get_rate(clk) / outrate;
+ div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]);
- if (div[OUT] == 0) {
+ /* Output divider has the same limitation as the input one */
+ if (div[OUT] == 0 || (!ideal && !div_avail)) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
outrate, clk_index[OUT]);
return -EINVAL;
}
+ div[OUT] = min_t(u32, 1024, div[OUT]);
+
/* Set the channel number */
channels = config->channel_num;
- if (asrc_priv->channel_bits < 4)
+ if (asrc_priv->soc->channel_bits < 4)
channels /= 2;
/* Update channels for current pair */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
- ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits),
- ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits));
+ regmap_update_bits(asrc->regmap, REG_ASRCNCR,
+ ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
+ ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
/* Default setting: Automatic selection for processing mode */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
- ASRCTR_USRi_MASK(index), 0);
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
+ ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
+ ASRCTR_USR(index));
/* Set the input and output clock sources */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
+ regmap_update_bits(asrc->regmap, REG_ASRCSR,
ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
ASRCSR_AICS(index, clk_index[IN]) |
ASRCSR_AOCS(index, clk_index[OUT]));
@@ -349,118 +531,183 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index),
+ regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
/* Implement word_width configurations */
- regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
+ regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
- ASRMCR1i_OW16(config->output_word_width) |
- ASRMCR1i_IWD(config->input_word_width));
+ ASRMCR1i_OW16(output_word_width) |
+ ASRMCR1i_IWD(input_word_width));
/* Enable BUFFER STALL */
- regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
/* Set default thresholds for input and output FIFO */
fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
ASRC_INPUTFIFO_THRESHOLD);
- /* Configure the followings only for Ideal Ratio mode */
+ /* Configure the following only for Ideal Ratio mode */
if (!ideal)
return 0;
/* Clear ASTSx bit to use Ideal Ratio mode */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ATSi_MASK(index), 0);
/* Enable Ideal Ratio mode */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
ASRCTR_IDR(index) | ASRCTR_USR(index));
+ fsl_asrc_sel_proc(inrate, outrate, &pre_proc, &post_proc);
+
/* Apply configurations for pre- and post-processing */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
+ regmap_update_bits(asrc->regmap, REG_ASRCFG,
ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index),
- ASRCFG_PREMOD(index, process_option[in][out][0]) |
- ASRCFG_POSTMOD(index, process_option[in][out][1]));
+ ASRCFG_PREMOD(index, pre_proc) |
+ ASRCFG_POSTMOD(index, post_proc));
return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
}
/**
- * Start the assigned ASRC pair
+ * fsl_asrc_start_pair - Start the assigned ASRC pair
+ * @pair: pointer to pair
*
* It enables the assigned pair and makes it stopped at the stall level.
*/
static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
- int reg, retry = 10, i;
+ int reg, retry = INIT_RETRY_NUM, i;
/* Enable the current pair */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
/* Wait for status of initialization */
do {
udelay(5);
- regmap_read(asrc_priv->regmap, REG_ASRCFG, &reg);
+ regmap_read(asrc->regmap, REG_ASRCFG, &reg);
reg &= ASRCFG_INIRQi_MASK(index);
} while (!reg && --retry);
+ /* NOTE: Doesn't treat initialization timeout as an error */
+ if (!retry)
+ pair_warn("initialization isn't finished\n");
+
/* Make the input fifo to ASRC STALL level */
- regmap_read(asrc_priv->regmap, REG_ASRCNCR, &reg);
+ regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
for (i = 0; i < pair->channels * 4; i++)
- regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0);
+ regmap_write(asrc->regmap, REG_ASRDI(index), 0);
/* Enable overload interrupt */
- regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE);
+ regmap_write(asrc->regmap, REG_ASRIER, ASRIER_AOLIE);
}
/**
- * Stop the assigned ASRC pair
+ * fsl_asrc_stop_pair - Stop the assigned ASRC pair
+ * @pair: pointer to pair
*/
static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
/* Stop the current pair */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), 0);
}
/**
- * Get DMA channel according to the pair and direction.
+ * fsl_asrc_get_dma_channel- Get DMA channel according to the pair and direction.
+ * @pair: pointer to pair
+ * @dir: DMA direction
*/
-struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir)
+static struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair,
+ bool dir)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
char name[4];
sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
- return dma_request_slave_channel(&asrc_priv->pdev->dev, name);
+ return dma_request_slave_channel(&asrc->pdev->dev, name);
+}
+
+static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
+
+ /* Odd channel number is not valid for older ASRC (channel_bits==3) */
+ if (asrc_priv->soc->channel_bits == 3)
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
+}
+
+/* Select proper clock source for internal ratio mode */
+static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
+ struct fsl_asrc_pair *pair,
+ int in_rate,
+ int out_rate)
+{
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
+ struct asrc_config *config = pair_priv->config;
+ int rate[2], select_clk[2]; /* Array size 2 means IN and OUT */
+ int clk_rate, clk_index;
+ int i, j;
+
+ rate[IN] = in_rate;
+ rate[OUT] = out_rate;
+
+ /* Select proper clock source for internal ratio mode */
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < ASRC_CLK_MAP_LEN; i++) {
+ clk_index = asrc_priv->clk_map[j][i];
+ clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
+ /* Only match a perfect clock source with no remainder */
+ if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL))
+ break;
+ }
+
+ select_clk[j] = i;
+ }
+
+ /* Switch to ideal ratio mode if there is no proper clock source */
+ if (select_clk[IN] == ASRC_CLK_MAP_LEN || select_clk[OUT] == ASRC_CLK_MAP_LEN) {
+ select_clk[IN] = INCLK_NONE;
+ select_clk[OUT] = OUTCLK_ASRCK1_CLK;
+ }
+
+ config->inclk = select_clk[IN];
+ config->outclk = select_clk[OUT];
}
-EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel);
static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
- int width = params_width(params);
+ struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
struct asrc_config config;
- int word_width, ret;
+ int ret;
ret = fsl_asrc_request_pair(channels, pair);
if (ret) {
@@ -468,36 +715,28 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- pair->config = &config;
-
- if (width == 16)
- width = ASRC_WIDTH_16_BIT;
- else
- width = ASRC_WIDTH_24_BIT;
-
- if (asrc_priv->asrc_width == 16)
- word_width = ASRC_WIDTH_16_BIT;
- else
- word_width = ASRC_WIDTH_24_BIT;
+ pair_priv->config = &config;
config.pair = pair->index;
config.channel_num = channels;
- config.inclk = INCLK_NONE;
- config.outclk = OUTCLK_ASRCK1_CLK;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- config.input_word_width = width;
- config.output_word_width = word_width;
+ config.input_format = params_format(params);
+ config.output_format = asrc->asrc_format;
config.input_sample_rate = rate;
- config.output_sample_rate = asrc_priv->asrc_rate;
+ config.output_sample_rate = asrc->asrc_rate;
} else {
- config.input_word_width = word_width;
- config.output_word_width = width;
- config.input_sample_rate = asrc_priv->asrc_rate;
+ config.input_format = asrc->asrc_format;
+ config.output_format = params_format(params);
+ config.input_sample_rate = asrc->asrc_rate;
config.output_sample_rate = rate;
}
- ret = fsl_asrc_config_pair(pair);
+ fsl_asrc_select_clk(asrc_priv, pair,
+ config.input_sample_rate,
+ config.output_sample_rate);
+
+ ret = fsl_asrc_config_pair(pair, false);
if (ret) {
dev_err(dai->dev, "fail to config asrc pair\n");
return ret;
@@ -542,50 +781,51 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static struct snd_soc_dai_ops fsl_asrc_dai_ops = {
- .hw_params = fsl_asrc_dai_hw_params,
- .hw_free = fsl_asrc_dai_hw_free,
- .trigger = fsl_asrc_dai_trigger,
-};
-
static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
{
- struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
- snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx,
- &asrc_priv->dma_params_rx);
+ snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
+ &asrc->dma_params_rx);
return 0;
}
-#define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000
+static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
+ .probe = fsl_asrc_dai_probe,
+ .startup = fsl_asrc_dai_startup,
+ .hw_params = fsl_asrc_dai_hw_params,
+ .hw_free = fsl_asrc_dai_hw_free,
+ .trigger = fsl_asrc_dai_trigger,
+};
+
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S20_3LE)
+ SNDRV_PCM_FMTBIT_S24_3LE)
static struct snd_soc_dai_driver fsl_asrc_dai = {
- .probe = fsl_asrc_dai_probe,
.playback = {
.stream_name = "ASRC-Playback",
.channels_min = 1,
.channels_max = 10,
- .rates = FSL_ASRC_RATES,
- .formats = FSL_ASRC_FORMATS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
},
.capture = {
.stream_name = "ASRC-Capture",
.channels_min = 1,
.channels_max = 10,
- .rates = FSL_ASRC_RATES,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_ASRC_FORMATS,
},
.ops = &fsl_asrc_dai_ops,
};
-static const struct snd_soc_component_driver fsl_asrc_component = {
- .name = "fsl-asrc-dai",
-};
-
static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -691,7 +931,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
}
}
-static struct reg_default fsl_asrc_reg[] = {
+static const struct reg_default fsl_asrc_reg[] = {
{ REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
{ REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
{ REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
@@ -726,52 +966,60 @@ static const struct regmap_config fsl_asrc_regmap_config = {
.readable_reg = fsl_asrc_readable_reg,
.volatile_reg = fsl_asrc_volatile_reg,
.writeable_reg = fsl_asrc_writeable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_FLAT,
};
/**
- * Initialize ASRC registers with a default configurations
+ * fsl_asrc_init - Initialize ASRC registers with a default configuration
+ * @asrc: ASRC context
*/
-static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
+static int fsl_asrc_init(struct fsl_asrc *asrc)
{
+ unsigned long ipg_rate;
+
/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
- regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
+ regmap_write(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
/* Disable interrupt by default */
- regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0);
+ regmap_write(asrc->regmap, REG_ASRIER, 0x0);
/* Apply recommended settings for parameters from Reference Manual */
- regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff);
- regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555);
- regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280);
- regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280);
- regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280);
+ regmap_write(asrc->regmap, REG_ASRPM1, 0x7fffff);
+ regmap_write(asrc->regmap, REG_ASRPM2, 0x255555);
+ regmap_write(asrc->regmap, REG_ASRPM3, 0xff7280);
+ regmap_write(asrc->regmap, REG_ASRPM4, 0xff7280);
+ regmap_write(asrc->regmap, REG_ASRPM5, 0xff7280);
/* Base address for task queue FIFO. Set to 0x7C */
- regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
+ regmap_update_bits(asrc->regmap, REG_ASRTFR1,
ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
- /* Set the processing clock for 76KHz to 133M */
- regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
-
- /* Set the processing clock for 56KHz to 133M */
- return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
+ /*
+ * Set the period of the 76KHz and 56KHz sampling clocks based on
+ * the ASRC processing clock.
+ * On iMX6, ipg_clk = 133MHz, REG_ASR76K = 0x06D6, REG_ASR56K = 0x0947
+ */
+ ipg_rate = clk_get_rate(asrc->ipg_clk);
+ regmap_write(asrc->regmap, REG_ASR76K, ipg_rate / 76000);
+ return regmap_write(asrc->regmap, REG_ASR56K, ipg_rate / 56000);
}
/**
- * Interrupt handler for ASRC
+ * fsl_asrc_isr- Interrupt handler for ASRC
+ * @irq: irq number
+ * @dev_id: ASRC context
*/
static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
{
- struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id;
- struct device *dev = &asrc_priv->pdev->dev;
+ struct fsl_asrc *asrc = (struct fsl_asrc *)dev_id;
+ struct device *dev = &asrc->pdev->dev;
enum asrc_pair_index index;
u32 status;
- regmap_read(asrc_priv->regmap, REG_ASRSTR, &status);
+ regmap_read(asrc->regmap, REG_ASRSTR, &status);
/* Clean overload error */
- regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE);
+ regmap_write(asrc->regmap, REG_ASRSTR, ASRSTR_AOLE);
/*
* We here use dev_dbg() for all exceptions because ASRC itself does
@@ -779,31 +1027,31 @@ static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
* interrupt would result a ridged conversion.
*/
for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
- if (!asrc_priv->pair[index])
+ if (!asrc->pair[index])
continue;
if (status & ASRSTR_ATQOL) {
- asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
+ asrc->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
}
if (status & ASRSTR_AOOL(index)) {
- asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
+ asrc->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
pair_dbg("Output Task Overload\n");
}
if (status & ASRSTR_AIOL(index)) {
- asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
+ asrc->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
pair_dbg("Input Task Overload\n");
}
if (status & ASRSTR_AODO(index)) {
- asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
+ asrc->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
pair_dbg("Output Data Buffer has overflowed\n");
}
if (status & ASRSTR_AIDU(index)) {
- asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
+ asrc->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
pair_dbg("Input Data Buffer has underflowed\n");
}
}
@@ -811,63 +1059,209 @@ static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
+{
+ return REG_ASRDx(dir, index);
+}
+
+/* Get sample numbers in FIFO */
+static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+
+ regmap_read(asrc->regmap, REG_ASRFST(index), &val);
+
+ val &= ASRFSTi_OUTPUT_FIFO_MASK;
+
+ return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
+}
+
+static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ struct asrc_config config;
+ int ret;
+
+ /* fill config */
+ config.pair = pair->index;
+ config.channel_num = pair->channels;
+ config.input_sample_rate = pair->rate[IN];
+ config.output_sample_rate = pair->rate[OUT];
+ config.input_format = pair->sample_format[IN];
+ config.output_format = pair->sample_format[OUT];
+ config.inclk = INCLK_NONE;
+ config.outclk = OUTCLK_ASRCK1_CLK;
+
+ pair_priv->config = &config;
+ ret = fsl_asrc_config_pair(pair, true);
+ if (ret) {
+ dev_err(dev, "failed to config pair: %d\n", ret);
+ return ret;
+ }
+
+ pair->first_convert = 1;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_start(struct fsl_asrc_pair *pair)
+{
+ if (pair->first_convert) {
+ fsl_asrc_start_pair(pair);
+ pair->first_convert = 0;
+ }
+ /*
+ * Clear DMA request during the stall state of ASRC:
+ * During STALL state, the remaining in input fifo would never be
+ * smaller than the input threshold while the output fifo would not
+ * be bigger than output one. Thus the DMA request would be cleared.
+ */
+ fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN,
+ ASRC_FIFO_THRESHOLD_MAX);
+
+ /* Update the real input threshold to raise DMA request */
+ fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML,
+ ASRC_M2M_OUTPUTFIFO_WML);
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_stop(struct fsl_asrc_pair *pair)
+{
+ if (!pair->first_convert) {
+ fsl_asrc_stop_pair(pair);
+ pair->first_convert = 1;
+ }
+
+ return 0;
+}
+
+/* calculate capture data length according to output data length and sample rate */
+static int fsl_asrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
+{
+ unsigned int in_width, out_width;
+ unsigned int channels = pair->channels;
+ unsigned int in_samples, out_samples;
+ unsigned int out_length;
+
+ in_width = snd_pcm_format_physical_width(pair->sample_format[IN]) / 8;
+ out_width = snd_pcm_format_physical_width(pair->sample_format[OUT]) / 8;
+
+ in_samples = input_buffer_length / in_width / channels;
+ out_samples = pair->rate[OUT] * in_samples / pair->rate[IN];
+ out_length = (out_samples - ASRC_OUTPUT_LAST_SAMPLE) * out_width * channels;
+
+ return out_length;
+}
+
+static int fsl_asrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
+ int wml = (dir == IN) ? ASRC_M2M_INPUTFIFO_WML : ASRC_M2M_OUTPUTFIFO_WML;
+
+ if (!asrc_priv->soc->use_edma)
+ return wml * pair->channels;
+ else
+ return 1;
+}
+
+static int fsl_asrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
+{
+ cap->fmt_in = FSL_ASRC_FORMATS;
+ cap->fmt_out = FSL_ASRC_FORMATS | SNDRV_PCM_FMTBIT_S8;
+
+ cap->rate_in = supported_asrc_rate;
+ cap->rate_in_count = ARRAY_SIZE(supported_asrc_rate);
+ cap->rate_out = supported_asrc_rate;
+ cap->rate_out_count = ARRAY_SIZE(supported_asrc_rate);
+ cap->chan_min = 1;
+ cap->chan_max = 10;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ int i;
+
+ for (i = 0; i < pair->channels * 4; i++)
+ regmap_write(asrc->regmap, REG_ASRDI(pair->index), 0);
+
+ pair->first_convert = 1;
+ return 0;
+}
+
+static int fsl_asrc_runtime_resume(struct device *dev);
+static int fsl_asrc_runtime_suspend(struct device *dev);
+
static int fsl_asrc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct fsl_asrc *asrc_priv;
+ struct fsl_asrc_priv *asrc_priv;
+ struct fsl_asrc *asrc;
struct resource *res;
void __iomem *regs;
int irq, ret, i;
+ u32 asrc_fmt = 0;
+ u32 map_idx;
char tmp[16];
+ u32 width;
+
+ asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
+ if (!asrc)
+ return -ENOMEM;
asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
if (!asrc_priv)
return -ENOMEM;
- asrc_priv->pdev = pdev;
+ asrc->pdev = pdev;
+ asrc->private = asrc_priv;
/* Get the addresses and IRQ */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
- asrc_priv->paddr = res->start;
+ asrc->paddr = res->start;
- asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
- &fsl_asrc_regmap_config);
- if (IS_ERR(asrc_priv->regmap)) {
+ asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_asrc_regmap_config);
+ if (IS_ERR(asrc->regmap)) {
dev_err(&pdev->dev, "failed to init regmap\n");
- return PTR_ERR(asrc_priv->regmap);
+ return PTR_ERR(asrc->regmap);
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
- dev_name(&pdev->dev), asrc_priv);
+ dev_name(&pdev->dev), asrc);
if (ret) {
dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
return ret;
}
- asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem");
- if (IS_ERR(asrc_priv->mem_clk)) {
+ asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
+ if (IS_ERR(asrc->mem_clk)) {
dev_err(&pdev->dev, "failed to get mem clock\n");
- return PTR_ERR(asrc_priv->mem_clk);
+ return PTR_ERR(asrc->mem_clk);
}
- asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(asrc_priv->ipg_clk)) {
+ asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(asrc->ipg_clk)) {
dev_err(&pdev->dev, "failed to get ipg clock\n");
- return PTR_ERR(asrc_priv->ipg_clk);
+ return PTR_ERR(asrc->ipg_clk);
}
- asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
- if (IS_ERR(asrc_priv->spba_clk))
+ asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
+ if (IS_ERR(asrc->spba_clk))
dev_warn(&pdev->dev, "failed to get spba clock\n");
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
@@ -879,79 +1273,161 @@ static int fsl_asrc_probe(struct platform_device *pdev)
}
}
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx35-asrc")) {
- asrc_priv->channel_bits = 3;
- clk_map[IN] = input_clk_map_imx35;
- clk_map[OUT] = output_clk_map_imx35;
- } else {
- asrc_priv->channel_bits = 4;
- clk_map[IN] = input_clk_map_imx53;
- clk_map[OUT] = output_clk_map_imx53;
- }
+ asrc_priv->soc = of_device_get_match_data(&pdev->dev);
+ asrc->use_edma = asrc_priv->soc->use_edma;
+ asrc->get_dma_channel = fsl_asrc_get_dma_channel;
+ asrc->request_pair = fsl_asrc_request_pair;
+ asrc->release_pair = fsl_asrc_release_pair;
+ asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
+ asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
+
+ asrc->m2m_prepare = fsl_asrc_m2m_prepare;
+ asrc->m2m_start = fsl_asrc_m2m_start;
+ asrc->m2m_stop = fsl_asrc_m2m_stop;
+ asrc->get_output_fifo_size = fsl_asrc_get_output_fifo_size;
+ asrc->m2m_calc_out_len = fsl_asrc_m2m_calc_out_len;
+ asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
+ asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
+ asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
+
+ if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
+ asrc_priv->clk_map[IN] = input_clk_map_imx35;
+ asrc_priv->clk_map[OUT] = output_clk_map_imx35;
+ } else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
+ asrc_priv->clk_map[IN] = input_clk_map_imx53;
+ asrc_priv->clk_map[OUT] = output_clk_map_imx53;
+ } else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
+ of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
+ ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get clk map index\n");
+ return ret;
+ }
- ret = fsl_asrc_init(asrc_priv);
- if (ret) {
- dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
- return -EINVAL;
+ if (map_idx > 1) {
+ dev_err(&pdev->dev, "unsupported clk map index\n");
+ return -EINVAL;
+ }
+ if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
+ asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
+ asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
+ } else {
+ asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
+ asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
+ }
}
- asrc_priv->channel_avail = 10;
+ asrc->channel_avail = 10;
ret = of_property_read_u32(np, "fsl,asrc-rate",
- &asrc_priv->asrc_rate);
+ &asrc->asrc_rate);
if (ret) {
dev_err(&pdev->dev, "failed to get output rate\n");
- return -EINVAL;
+ return ret;
}
- ret = of_property_read_u32(np, "fsl,asrc-width",
- &asrc_priv->asrc_width);
+ ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
+ asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
if (ret) {
- dev_err(&pdev->dev, "failed to get output width\n");
- return -EINVAL;
+ ret = of_property_read_u32(np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to decide output format\n");
+ return ret;
+ }
+
+ switch (width) {
+ case 16:
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 24:
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ default:
+ dev_warn(&pdev->dev,
+ "unsupported width, use default S24_LE\n");
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ }
}
- if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) {
- dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
- asrc_priv->asrc_width = 24;
+ if (!(FSL_ASRC_FORMATS & pcm_format_to_bits(asrc->asrc_format))) {
+ dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
}
- platform_set_drvdata(pdev, asrc_priv);
+ platform_set_drvdata(pdev, asrc);
+ spin_lock_init(&asrc->lock);
pm_runtime_enable(&pdev->dev);
- spin_lock_init(&asrc_priv->lock);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = fsl_asrc_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_pm_get_sync;
+
+ ret = fsl_asrc_init(asrc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
+ goto err_pm_get_sync;
+ }
+
+ ret = pm_runtime_put_sync(&pdev->dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto err_pm_get_sync;
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
&fsl_asrc_dai, 1);
if (ret) {
dev_err(&pdev->dev, "failed to register ASoC DAI\n");
- return ret;
+ goto err_pm_get_sync;
}
- ret = devm_snd_soc_register_platform(&pdev->dev, &fsl_asrc_platform);
+ ret = fsl_asrc_m2m_init(asrc);
if (ret) {
- dev_err(&pdev->dev, "failed to register ASoC platform\n");
+ dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
return ret;
}
- dev_info(&pdev->dev, "driver registered\n");
-
return 0;
+
+err_pm_get_sync:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_asrc_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void fsl_asrc_remove(struct platform_device *pdev)
+{
+ struct fsl_asrc *asrc = dev_get_drvdata(&pdev->dev);
+
+ fsl_asrc_m2m_exit(asrc);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_asrc_runtime_suspend(&pdev->dev);
}
-#ifdef CONFIG_PM
static int fsl_asrc_runtime_resume(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
+ int reg, retry = INIT_RETRY_NUM;
int i, ret;
+ u32 asrctr;
- ret = clk_prepare_enable(asrc_priv->mem_clk);
+ ret = clk_prepare_enable(asrc->mem_clk);
if (ret)
return ret;
- ret = clk_prepare_enable(asrc_priv->ipg_clk);
+ ret = clk_prepare_enable(asrc->ipg_clk);
if (ret)
goto disable_mem_clk;
- if (!IS_ERR(asrc_priv->spba_clk)) {
- ret = clk_prepare_enable(asrc_priv->spba_clk);
+ if (!IS_ERR(asrc->spba_clk)) {
+ ret = clk_prepare_enable(asrc->spba_clk);
if (ret)
goto disable_ipg_clk;
}
@@ -961,94 +1437,138 @@ static int fsl_asrc_runtime_resume(struct device *dev)
goto disable_asrck_clk;
}
+ /* Stop all pairs provisionally */
+ regmap_read(asrc->regmap, REG_ASRCTR, &asrctr);
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_ALL_MASK, 0);
+
+ /* Restore all registers */
+ regcache_cache_only(asrc->regmap, false);
+ regcache_mark_dirty(asrc->regmap);
+ regcache_sync(asrc->regmap);
+
+ regmap_update_bits(asrc->regmap, REG_ASRCFG,
+ ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
+ ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
+
+ /* Restart enabled pairs */
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_ALL_MASK, asrctr);
+
+ /* Wait for status of initialization for all enabled pairs */
+ do {
+ udelay(5);
+ regmap_read(asrc->regmap, REG_ASRCFG, &reg);
+ reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
+ } while ((reg != ((asrctr >> ASRCTR_ASRCEi_SHIFT(0)) & 0x7)) && --retry);
+
+ /*
+ * NOTE: Doesn't treat initialization timeout as an error
+ * Some of the pairs may success, then still can continue.
+ */
+ if (!retry) {
+ for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
+ if ((asrctr & ASRCTR_ASRCEi_MASK(i)) && !(reg & (1 << i)))
+ dev_warn(dev, "Pair %c initialization isn't finished\n", 'A' + i);
+ }
+ }
+
return 0;
disable_asrck_clk:
for (i--; i >= 0; i--)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
- if (!IS_ERR(asrc_priv->spba_clk))
- clk_disable_unprepare(asrc_priv->spba_clk);
+ if (!IS_ERR(asrc->spba_clk))
+ clk_disable_unprepare(asrc->spba_clk);
disable_ipg_clk:
- clk_disable_unprepare(asrc_priv->ipg_clk);
+ clk_disable_unprepare(asrc->ipg_clk);
disable_mem_clk:
- clk_disable_unprepare(asrc_priv->mem_clk);
+ clk_disable_unprepare(asrc->mem_clk);
return ret;
}
static int fsl_asrc_runtime_suspend(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
int i;
+ regmap_read(asrc->regmap, REG_ASRCFG,
+ &asrc_priv->regcache_cfg);
+
+ regcache_cache_only(asrc->regmap, true);
+
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
- if (!IS_ERR(asrc_priv->spba_clk))
- clk_disable_unprepare(asrc_priv->spba_clk);
- clk_disable_unprepare(asrc_priv->ipg_clk);
- clk_disable_unprepare(asrc_priv->mem_clk);
+ if (!IS_ERR(asrc->spba_clk))
+ clk_disable_unprepare(asrc->spba_clk);
+ clk_disable_unprepare(asrc->ipg_clk);
+ clk_disable_unprepare(asrc->mem_clk);
return 0;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PM_SLEEP
static int fsl_asrc_suspend(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
-
- regmap_read(asrc_priv->regmap, REG_ASRCFG,
- &asrc_priv->regcache_cfg);
-
- regcache_cache_only(asrc_priv->regmap, true);
- regcache_mark_dirty(asrc_priv->regmap);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ int ret;
- return 0;
+ fsl_asrc_m2m_suspend(asrc);
+ ret = pm_runtime_force_suspend(dev);
+ return ret;
}
static int fsl_asrc_resume(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
- u32 asrctr;
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ int ret;
- /* Stop all pairs provisionally */
- regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
- ASRCTR_ASRCEi_ALL_MASK, 0);
+ ret = pm_runtime_force_resume(dev);
+ fsl_asrc_m2m_resume(asrc);
+ return ret;
+}
- /* Restore all registers */
- regcache_cache_only(asrc_priv->regmap, false);
- regcache_sync(asrc_priv->regmap);
+static const struct dev_pm_ops fsl_asrc_pm = {
+ RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
+};
- regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
- ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
- ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
+static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
+ .use_edma = false,
+ .channel_bits = 3,
+};
- /* Restart enabled pairs */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
- ASRCTR_ASRCEi_ALL_MASK, asrctr);
+static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
+ .use_edma = false,
+ .channel_bits = 4,
+};
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
+static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
+ .use_edma = true,
+ .channel_bits = 4,
+};
-static const struct dev_pm_ops fsl_asrc_pm = {
- SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
+static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
+ .use_edma = true,
+ .channel_bits = 4,
};
static const struct of_device_id fsl_asrc_ids[] = {
- { .compatible = "fsl,imx35-asrc", },
- { .compatible = "fsl,imx53-asrc", },
+ { .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
+ { .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
+ { .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
+ { .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
static struct platform_driver fsl_asrc_driver = {
.probe = fsl_asrc_probe,
+ .remove = fsl_asrc_remove,
.driver = {
.name = "fsl-asrc",
.of_match_table = fsl_asrc_ids,
- .pm = &fsl_asrc_pm,
+ .pm = pm_ptr(&fsl_asrc_pm),
},
};
module_platform_driver(fsl_asrc_driver);
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 0f163abe4ba3..1c492eb237f5 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -1,21 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* fsl_asrc.h - Freescale ASRC ALSA SoC header file
*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* Author: Nicolin Chen <nicoleotsuka@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.
*/
#ifndef _FSL_ASRC_H
#define _FSL_ASRC_H
-#define IN 0
-#define OUT 1
+#include "fsl_asrc_common.h"
+#define ASRC_M2M_INPUTFIFO_WML 0x4
+#define ASRC_M2M_OUTPUTFIFO_WML 0x2
#define ASRC_DMA_BUFFER_NUM 2
#define ASRC_INPUTFIFO_THRESHOLD 32
#define ASRC_OUTPUTFIFO_THRESHOLD 32
@@ -57,7 +55,7 @@
#define REG_ASRDOC 0x74
#define REG_ASRDI(i) (REG_ASRDIA + (i << 3))
#define REG_ASRDO(i) (REG_ASRDOA + (i << 3))
-#define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i))
+#define REG_ASRDx(x, i) ((x) == IN ? REG_ASRDI(i) : REG_ASRDO(i))
#define REG_ASRIDRHA 0x80
#define REG_ASRIDRLA 0x84
@@ -260,8 +258,8 @@
#define ASRFSTi_OUTPUT_FIFO_SHIFT 12
#define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
#define ASRFSTi_IAEi_SHIFT 11
-#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_OAFi_SHIFT)
-#define ASRFSTi_IAEi (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_IAEi_SHIFT)
+#define ASRFSTi_IAEi (1 << ASRFSTi_IAEi_SHIFT)
#define ASRFSTi_INPUT_FIFO_WIDTH 7
#define ASRFSTi_INPUT_FIFO_SHIFT 0
#define ASRFSTi_INPUT_FIFO_MASK ((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1)
@@ -286,14 +284,6 @@
#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT)
#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT)
-
-enum asrc_pair_index {
- ASRC_INVALID_PAIR = -1,
- ASRC_PAIR_A = 0,
- ASRC_PAIR_B = 1,
- ASRC_PAIR_C = 2,
-};
-
#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
enum asrc_inclk {
@@ -311,6 +301,29 @@ enum asrc_inclk {
INCLK_SSI3_TX = 0x0b,
INCLK_SPDIF_TX = 0x0c,
INCLK_ASRCK1_CLK = 0x0f,
+
+ /* clocks for imx8 */
+ INCLK_AUD_PLL_DIV_CLK0 = 0x10,
+ INCLK_AUD_PLL_DIV_CLK1 = 0x11,
+ INCLK_AUD_CLK0 = 0x12,
+ INCLK_AUD_CLK1 = 0x13,
+ INCLK_ESAI0_RX_CLK = 0x14,
+ INCLK_ESAI0_TX_CLK = 0x15,
+ INCLK_SPDIF0_RX = 0x16,
+ INCLK_SPDIF1_RX = 0x17,
+ INCLK_SAI0_RX_BCLK = 0x18,
+ INCLK_SAI0_TX_BCLK = 0x19,
+ INCLK_SAI1_RX_BCLK = 0x1a,
+ INCLK_SAI1_TX_BCLK = 0x1b,
+ INCLK_SAI2_RX_BCLK = 0x1c,
+ INCLK_SAI3_RX_BCLK = 0x1d,
+ INCLK_ASRC0_MUX_CLK = 0x1e,
+
+ INCLK_ESAI1_RX_CLK = 0x20,
+ INCLK_ESAI1_TX_CLK = 0x21,
+ INCLK_SAI6_TX_BCLK = 0x22,
+ INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
+ INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
};
enum asrc_outclk {
@@ -328,9 +341,33 @@ enum asrc_outclk {
OUTCLK_SSI3_RX = 0x0b,
OUTCLK_SPDIF_RX = 0x0c,
OUTCLK_ASRCK1_CLK = 0x0f,
+
+ /* clocks for imx8 */
+ OUTCLK_AUD_PLL_DIV_CLK0 = 0x10,
+ OUTCLK_AUD_PLL_DIV_CLK1 = 0x11,
+ OUTCLK_AUD_CLK0 = 0x12,
+ OUTCLK_AUD_CLK1 = 0x13,
+ OUTCLK_ESAI0_RX_CLK = 0x14,
+ OUTCLK_ESAI0_TX_CLK = 0x15,
+ OUTCLK_SPDIF0_RX = 0x16,
+ OUTCLK_SPDIF1_RX = 0x17,
+ OUTCLK_SAI0_RX_BCLK = 0x18,
+ OUTCLK_SAI0_TX_BCLK = 0x19,
+ OUTCLK_SAI1_RX_BCLK = 0x1a,
+ OUTCLK_SAI1_TX_BCLK = 0x1b,
+ OUTCLK_SAI2_RX_BCLK = 0x1c,
+ OUTCLK_SAI3_RX_BCLK = 0x1d,
+ OUTCLK_ASRCO_MUX_CLK = 0x1e,
+
+ OUTCLK_ESAI1_RX_CLK = 0x20,
+ OUTCLK_ESAI1_TX_CLK = 0x21,
+ OUTCLK_SAI6_TX_BCLK = 0x22,
+ OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
+ OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
};
#define ASRC_CLK_MAX_NUM 16
+#define ASRC_CLK_MAP_LEN 0x30
enum asrc_word_width {
ASRC_WIDTH_24_BIT = 0,
@@ -345,8 +382,8 @@ struct asrc_config {
unsigned int dma_buffer_size;
unsigned int input_sample_rate;
unsigned int output_sample_rate;
- enum asrc_word_width input_word_width;
- enum asrc_word_width output_word_width;
+ snd_pcm_format_t input_format;
+ snd_pcm_format_t output_format;
enum asrc_inclk inclk;
enum asrc_outclk outclk;
};
@@ -391,77 +428,39 @@ struct dma_block {
};
/**
- * fsl_asrc_pair: ASRC Pair private data
+ * fsl_asrc_soc_data: soc specific data
+ *
+ * @use_edma: using edma as dma device or not
+ * @channel_bits: width of ASRCNCR register for each pair
+ */
+struct fsl_asrc_soc_data {
+ bool use_edma;
+ unsigned int channel_bits;
+};
+
+/**
+ * fsl_asrc_pair_priv: ASRC Pair private data
*
- * @asrc_priv: pointer to its parent module
* @config: configuration profile
- * @error: error record
- * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
- * @channels: occupied channel number
- * @desc: input and output dma descriptors
- * @dma_chan: inputer and output DMA channels
- * @dma_data: private dma data
- * @pos: hardware pointer position
- * @private: pair private area
*/
-struct fsl_asrc_pair {
- struct fsl_asrc *asrc_priv;
+struct fsl_asrc_pair_priv {
struct asrc_config *config;
- unsigned int error;
-
- enum asrc_pair_index index;
- unsigned int channels;
-
- struct dma_async_tx_descriptor *desc[2];
- struct dma_chan *dma_chan[2];
- struct imx_dma_data dma_data;
- unsigned int pos;
-
- void *private;
};
/**
- * fsl_asrc_pair: ASRC private data
+ * fsl_asrc_priv: ASRC private data
*
- * @dma_params_rx: DMA parameters for receive channel
- * @dma_params_tx: DMA parameters for transmit channel
- * @pdev: platform device pointer
- * @regmap: regmap handler
- * @paddr: physical address to the base address of registers
- * @mem_clk: clock source to access register
- * @ipg_clk: clock source to drive peripheral
- * @spba_clk: SPBA clock (optional, depending on SoC design)
* @asrck_clk: clock sources to driver ASRC internal logic
- * @lock: spin lock for resource protection
- * @pair: pair pointers
- * @channel_bits: width of ASRCNCR register for each pair
- * @channel_avail: non-occupied channel numbers
- * @asrc_rate: default sample rate for ASoC Back-Ends
- * @asrc_width: default sample width for ASoC Back-Ends
+ * @soc: soc specific data
+ * @clk_map: clock map for input/output clock
* @regcache_cfg: store register value of REG_ASRCFG
*/
-struct fsl_asrc {
- struct snd_dmaengine_dai_dma_data dma_params_rx;
- struct snd_dmaengine_dai_dma_data dma_params_tx;
- struct platform_device *pdev;
- struct regmap *regmap;
- unsigned long paddr;
- struct clk *mem_clk;
- struct clk *ipg_clk;
- struct clk *spba_clk;
+struct fsl_asrc_priv {
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
- spinlock_t lock;
-
- struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
- unsigned int channel_bits;
- unsigned int channel_avail;
-
- int asrc_rate;
- int asrc_width;
+ const struct fsl_asrc_soc_data *soc;
+ unsigned char *clk_map[2];
u32 regcache_cfg;
};
-extern struct snd_soc_platform_driver fsl_asrc_platform;
-struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
#endif /* _FSL_ASRC_H */
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
new file mode 100644
index 000000000000..0cd595b0f629
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 NXP
+ *
+ */
+
+#ifndef _FSL_ASRC_COMMON_H
+#define _FSL_ASRC_COMMON_H
+
+/* directions */
+#define IN 0
+#define OUT 1
+
+enum asrc_pair_index {
+ ASRC_INVALID_PAIR = -1,
+ ASRC_PAIR_A = 0,
+ ASRC_PAIR_B = 1,
+ ASRC_PAIR_C = 2,
+ ASRC_PAIR_D = 3,
+};
+
+#define PAIR_CTX_NUM 0x4
+
+/**
+ * struct fsl_asrc_m2m_cap - capability data
+ * @fmt_in: input sample format
+ * @fmt_out: output sample format
+ * @chan_min: minimum channel number
+ * @chan_max: maximum channel number
+ * @rate_in: minimum rate
+ * @rate_out: maximum rete
+ */
+struct fsl_asrc_m2m_cap {
+ u64 fmt_in;
+ u64 fmt_out;
+ int chan_min;
+ int chan_max;
+ const unsigned int *rate_in;
+ int rate_in_count;
+ const unsigned int *rate_out;
+ int rate_out_count;
+};
+
+/**
+ * fsl_asrc_pair: ASRC Pair common data
+ *
+ * @asrc: pointer to its parent module
+ * @error: error record
+ * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
+ * @channels: occupied channel number
+ * @desc: input and output dma descriptors
+ * @dma_chan: inputer and output DMA channels
+ * @dma_data: private dma data
+ * @pos: hardware pointer position
+ * @req_dma_chan: flag to release dev_to_dev chan
+ * @private: pair private area
+ * @complete: dma task complete
+ * @sample_format: format of m2m
+ * @rate: rate of m2m
+ * @buf_len: buffer length of m2m
+ * @dma_buffer: buffer pointers
+ * @first_convert: start of conversion
+ * @ratio_mod_flag: flag for new ratio modifier
+ * @ratio_mod: ratio modification
+ */
+struct fsl_asrc_pair {
+ struct fsl_asrc *asrc;
+ unsigned int error;
+
+ enum asrc_pair_index index;
+ unsigned int channels;
+
+ struct dma_async_tx_descriptor *desc[2];
+ struct dma_chan *dma_chan[2];
+ struct imx_dma_data dma_data;
+ unsigned int pos;
+ bool req_dma_chan;
+
+ void *private;
+
+ /* used for m2m */
+ struct completion complete[2];
+ snd_pcm_format_t sample_format[2];
+ unsigned int rate[2];
+ unsigned int buf_len[2];
+ struct snd_dma_buffer dma_buffer[2];
+ unsigned int first_convert;
+ bool ratio_mod_flag;
+ unsigned int ratio_mod;
+};
+
+/**
+ * fsl_asrc: ASRC common data
+ *
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @paddr: physical address to the base address of registers
+ * @mem_clk: clock source to access register
+ * @ipg_clk: clock source to drive peripheral
+ * @spba_clk: SPBA clock (optional, depending on SoC design)
+ * @card: compress sound card
+ * @lock: spin lock for resource protection
+ * @pair: pair pointers
+ * @channel_avail: non-occupied channel numbers
+ * @asrc_rate: default sample rate for ASoC Back-Ends
+ * @asrc_format: default sample format for ASoC Back-Ends
+ * @use_edma: edma is used
+ * @get_dma_channel: function pointer
+ * @request_pair: function pointer
+ * @release_pair: function pointer
+ * @get_fifo_addr: function pointer
+ * @m2m_get_cap: function pointer
+ * @m2m_prepare: function pointer
+ * @m2m_start: function pointer
+ * @m2m_unprepare: function pointer
+ * @m2m_stop: function pointer
+ * @m2m_calc_out_len: function pointer
+ * @m2m_get_maxburst: function pointer
+ * @m2m_pair_suspend: function pointer
+ * @m2m_pair_resume: function pointer
+ * @m2m_set_ratio_mod: function pointer
+ * @get_output_fifo_size: function pointer
+ * @pair_priv_size: size of pair private struct.
+ * @private: private data structure
+ */
+struct fsl_asrc {
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ unsigned long paddr;
+ struct clk *mem_clk;
+ struct clk *ipg_clk;
+ struct clk *spba_clk;
+ struct snd_card *card;
+ spinlock_t lock; /* spin lock for resource protection */
+
+ struct fsl_asrc_pair *pair[PAIR_CTX_NUM];
+ unsigned int channel_avail;
+
+ int asrc_rate;
+ snd_pcm_format_t asrc_format;
+ bool use_edma;
+
+ struct dma_chan *(*get_dma_channel)(struct fsl_asrc_pair *pair, bool dir);
+ int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
+ void (*release_pair)(struct fsl_asrc_pair *pair);
+ int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index);
+ int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap);
+
+ int (*m2m_prepare)(struct fsl_asrc_pair *pair);
+ int (*m2m_start)(struct fsl_asrc_pair *pair);
+ int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
+ int (*m2m_stop)(struct fsl_asrc_pair *pair);
+
+ int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
+ int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
+ int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair);
+ int (*m2m_pair_resume)(struct fsl_asrc_pair *pair);
+ int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val);
+
+ unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair);
+ size_t pair_priv_size;
+
+ void *private;
+};
+
+#define DRV_NAME "fsl-asrc-dai"
+extern struct snd_soc_component_driver fsl_asrc_component;
+
+int fsl_asrc_m2m_init(struct fsl_asrc *asrc);
+void fsl_asrc_m2m_exit(struct fsl_asrc *asrc);
+int fsl_asrc_m2m_resume(struct fsl_asrc *asrc);
+int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc);
+
+#endif /* _FSL_ASRC_COMMON_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index ffc000bc1f15..1bba48318e2d 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -1,22 +1,18 @@
-/*
- * Freescale ASRC ALSA SoC Platform (DMA) driver
- *
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
- *
- * Author: Nicolin Chen <nicoleotsuka@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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale ASRC ALSA SoC Platform (DMA) driver
+//
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+//
+// Author: Nicolin Chen <nicoleotsuka@gmail.com>
#include <linux/dma-mapping.h>
#include <linux/module.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
-#include "fsl_asrc.h"
+#include "fsl_asrc_common.h"
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
@@ -24,9 +20,7 @@ static struct snd_pcm_hardware snd_imx_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
@@ -58,13 +52,13 @@ static void fsl_asrc_dma_complete(void *arg)
snd_pcm_period_elapsed(substream);
}
-static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
+ struct snd_soc_component *component)
{
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct device *dev = rtd->platform->dev;
+ struct device *dev = component->dev;
unsigned long flags = DMA_CTRL_ACK;
/* Prepare and submit Front-End DMA channel */
@@ -76,7 +70,7 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
pair->dma_chan[!dir], runtime->dma_addr,
snd_pcm_lib_buffer_bytes(substream),
snd_pcm_lib_period_bytes(substream),
- dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags);
+ dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, flags);
if (!pair->desc[!dir]) {
dev_err(dev, "failed to prepare slave DMA for Front-End\n");
return -ENOMEM;
@@ -100,7 +94,8 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
return 0;
}
-static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int fsl_asrc_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@@ -110,7 +105,7 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = fsl_asrc_dma_prepare_and_submit(substream);
+ ret = fsl_asrc_dma_prepare_and_submit(substream, component);
if (ret)
return ret;
dma_async_issue_pending(pair->dma_chan[IN]);
@@ -119,8 +114,8 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- dmaengine_terminate_all(pair->dma_chan[OUT]);
- dmaengine_terminate_all(pair->dma_chan[IN]);
+ dmaengine_terminate_async(pair->dma_chan[OUT]);
+ dmaengine_terminate_async(pair->dma_chan[IN]);
break;
default:
return -EINVAL;
@@ -129,38 +124,56 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
+static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ enum sdma_peripheral_type be_peripheral_type = IMX_DMATYPE_SSI;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
- struct dma_slave_config config_fe, config_be;
+ struct dma_chan *tmp_chan = NULL, *be_chan = NULL;
+ struct snd_soc_component *component_be = NULL;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct dma_slave_config config_fe = {}, config_be = {};
+ struct sdma_peripheral_config audio_config;
enum asrc_pair_index index = pair->index;
- struct device *dev = rtd->platform->dev;
+ struct device *dev = component->dev;
+ struct device_node *of_dma_node;
int stream = substream->stream;
struct imx_dma_data *tmp_data;
struct snd_soc_dpcm *dpcm;
- struct dma_chan *tmp_chan;
struct device *dev_be;
u8 dir = tx ? OUT : IN;
dma_cap_mask_t mask;
- int ret;
+ int ret, width;
/* Fetch the Back-End dma_data from DPCM */
- list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
+ for_each_dpcm_be(rtd, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *substream_be;
- struct snd_soc_dai *dai = be->cpu_dai;
+ struct snd_soc_dai *dai_cpu = snd_soc_rtd_to_cpu(be, 0);
+ struct snd_soc_dai *dai_codec = snd_soc_rtd_to_codec(be, 0);
+ struct snd_soc_dai *dai;
if (dpcm->fe != rtd)
continue;
+ /*
+ * With audio graph card, original cpu dai is changed to codec
+ * device in backend, so if cpu dai is dummy device in backend,
+ * get the codec dai device, which is the real hardware device
+ * connected.
+ */
+ if (!snd_soc_dai_is_dummy(dai_cpu))
+ dai = dai_cpu;
+ else
+ dai = dai_codec;
+
substream_be = snd_soc_dpcm_get_substream(be, stream);
dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
dev_be = dai->dev;
@@ -173,17 +186,16 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
}
/* Override dma_data of the Front-End and config its dmaengine */
- dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index);
+ dma_params_fe = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ dma_params_fe->addr = asrc->paddr + asrc->get_fifo_addr(!dir, index);
dma_params_fe->maxburst = dma_params_be->maxburst;
- pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir);
+ pair->dma_chan[!dir] = asrc->get_dma_channel(pair, !dir);
if (!pair->dma_chan[!dir]) {
dev_err(dev, "failed to request DMA channel\n");
return -EINVAL;
}
- memset(&config_fe, 0, sizeof(config_fe));
ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe);
if (ret) {
dev_err(dev, "failed to prepare DMA config for Front-End\n");
@@ -201,30 +213,74 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_CYCLIC, mask);
- /* Get DMA request of Back-End */
- tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
- tmp_data = tmp_chan->private;
- pair->dma_data.dma_request = tmp_data->dma_request;
- dma_release_channel(tmp_chan);
+ /*
+ * The Back-End device might have already requested a DMA channel,
+ * so try to reuse it first, and then request a new one upon NULL.
+ */
+ component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME);
+ if (component_be) {
+ be_chan = soc_component_to_pcm(component_be)->chan[substream->stream];
+ tmp_chan = be_chan;
+ }
+ if (!tmp_chan) {
+ tmp_chan = dma_request_chan(dev_be, tx ? "tx" : "rx");
+ if (IS_ERR(tmp_chan)) {
+ dev_err(dev, "failed to request DMA channel for Back-End\n");
+ return -EINVAL;
+ }
+ }
- /* Get DMA request of Front-End */
- tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
- tmp_data = tmp_chan->private;
- pair->dma_data.dma_request2 = tmp_data->dma_request;
- pair->dma_data.peripheral_type = tmp_data->peripheral_type;
- pair->dma_data.priority = tmp_data->priority;
- dma_release_channel(tmp_chan);
+ /*
+ * An EDMA DEV_TO_DEV channel is fixed and bound with DMA event of each
+ * peripheral, unlike SDMA channel that is allocated dynamically. So no
+ * need to configure dma_request and dma_request2, but get dma_chan of
+ * Back-End device directly via dma_request_chan.
+ */
+ if (!asrc->use_edma) {
+ /* Get DMA request of Back-End */
+ tmp_data = tmp_chan->private;
+ pair->dma_data.dma_request = tmp_data->dma_request;
+ be_peripheral_type = tmp_data->peripheral_type;
+ if (!be_chan)
+ dma_release_channel(tmp_chan);
+
+ /* Get DMA request of Front-End */
+ tmp_chan = asrc->get_dma_channel(pair, dir);
+ tmp_data = tmp_chan->private;
+ pair->dma_data.dma_request2 = tmp_data->dma_request;
+ pair->dma_data.peripheral_type = tmp_data->peripheral_type;
+ pair->dma_data.priority = tmp_data->priority;
+ dma_release_channel(tmp_chan);
+
+ of_dma_node = pair->dma_chan[!dir]->device->dev->of_node;
+ pair->dma_chan[dir] =
+ __dma_request_channel(&mask, filter, &pair->dma_data,
+ of_dma_node);
+ pair->req_dma_chan = true;
+ } else {
+ pair->dma_chan[dir] = tmp_chan;
+ /* Do not flag to release if we are reusing the Back-End one */
+ pair->req_dma_chan = !be_chan;
+ }
- pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data);
if (!pair->dma_chan[dir]) {
dev_err(dev, "failed to request DMA channel for Back-End\n");
return -EINVAL;
}
- if (asrc_priv->asrc_width == 16)
+ width = snd_pcm_format_physical_width(asrc->asrc_format);
+ if (width < 8 || width > 64)
+ return -EINVAL;
+ else if (width == 8)
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ else if (width == 16)
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
- else
+ else if (width == 24)
+ buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
+ else if (width <= 32)
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
config_be.direction = DMA_DEV_TO_DEV;
config_be.src_addr_width = buswidth;
@@ -232,89 +288,157 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
config_be.dst_addr_width = buswidth;
config_be.dst_maxburst = dma_params_be->maxburst;
+ memset(&audio_config, 0, sizeof(audio_config));
+ config_be.peripheral_config = &audio_config;
+ config_be.peripheral_size = sizeof(audio_config);
+
+ if (tx && (be_peripheral_type == IMX_DMATYPE_SSI_DUAL ||
+ be_peripheral_type == IMX_DMATYPE_SPDIF))
+ audio_config.n_fifos_dst = 2;
+ if (!tx && (be_peripheral_type == IMX_DMATYPE_SSI_DUAL ||
+ be_peripheral_type == IMX_DMATYPE_SPDIF))
+ audio_config.n_fifos_src = 2;
+
if (tx) {
- config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index);
+ config_be.src_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
config_be.dst_addr = dma_params_be->addr;
} else {
- config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index);
+ config_be.dst_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
config_be.src_addr = dma_params_be->addr;
}
ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be);
if (ret) {
dev_err(dev, "failed to config DMA channel for Back-End\n");
+ if (pair->req_dma_chan)
+ dma_release_channel(pair->dma_chan[dir]);
return ret;
}
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
return 0;
}
-static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
+ u8 dir = tx ? OUT : IN;
- snd_pcm_set_runtime_buffer(substream, NULL);
-
- if (pair->dma_chan[IN])
- dma_release_channel(pair->dma_chan[IN]);
+ if (pair->dma_chan[!dir])
+ dma_release_channel(pair->dma_chan[!dir]);
- if (pair->dma_chan[OUT])
- dma_release_channel(pair->dma_chan[OUT]);
+ /* release dev_to_dev chan if we aren't reusing the Back-End one */
+ if (pair->dma_chan[dir] && pair->req_dma_chan)
+ dma_release_channel(pair->dma_chan[dir]);
- pair->dma_chan[IN] = NULL;
- pair->dma_chan[OUT] = NULL;
+ pair->dma_chan[!dir] = NULL;
+ pair->dma_chan[dir] = NULL;
return 0;
}
-static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_startup(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct device *dev = rtd->platform->dev;
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ struct device *dev = component->dev;
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
struct fsl_asrc_pair *pair;
+ struct dma_chan *tmp_chan = NULL;
+ u8 dir = tx ? OUT : IN;
+ bool release_pair = true;
+ int ret = 0;
- pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
- if (!pair) {
- dev_err(dev, "failed to allocate pair\n");
- return -ENOMEM;
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(dev, "failed to set pcm hw params periods\n");
+ return ret;
}
- pair->asrc_priv = asrc_priv;
+ pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
+ if (!pair)
+ return -ENOMEM;
+
+ pair->asrc = asrc;
+ pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
runtime->private_data = pair;
- snd_pcm_hw_constraint_integer(substream->runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
+ /* Request a dummy pair, which will be released later.
+ * Request pair function needs channel num as input, for this
+ * dummy pair, we just request "1" channel temporarily.
+ */
+ ret = asrc->request_pair(1, pair);
+ if (ret < 0) {
+ dev_err(dev, "failed to request asrc pair\n");
+ goto req_pair_err;
+ }
+
+ /* Request a dummy dma channel, which will be released later. */
+ tmp_chan = asrc->get_dma_channel(pair, dir);
+ if (!tmp_chan) {
+ dev_err(dev, "failed to get dma channel\n");
+ ret = -EINVAL;
+ goto dma_chan_err;
+ }
+
+ dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+
+ /* Refine the snd_imx_hardware according to caps of DMA. */
+ ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
+ dma_data,
+ &snd_imx_hardware,
+ tmp_chan);
+ if (ret < 0) {
+ dev_err(dev, "failed to refine runtime hwparams\n");
+ goto out;
+ }
+
+ release_pair = false;
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
- return 0;
+out:
+ dma_release_channel(tmp_chan);
+
+dma_chan_err:
+ asrc->release_pair(pair);
+
+req_pair_err:
+ if (release_pair)
+ kfree(pair);
+
+ return ret;
}
-static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
+static int fsl_asrc_dma_shutdown(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct fsl_asrc *asrc_priv;
+ struct fsl_asrc *asrc;
if (!pair)
return 0;
- asrc_priv = pair->asrc_priv;
+ asrc = pair->asrc;
- if (asrc_priv->pair[pair->index] == pair)
- asrc_priv->pair[pair->index] = NULL;
+ if (asrc->pair[pair->index] == pair)
+ asrc->pair[pair->index] = NULL;
kfree(pair);
return 0;
}
-static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@@ -322,22 +446,12 @@ static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *subs
return bytes_to_frames(substream->runtime, pair->pos);
}
-static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fsl_asrc_dma_hw_params,
- .hw_free = fsl_asrc_dma_hw_free,
- .trigger = fsl_asrc_dma_trigger,
- .open = fsl_asrc_dma_startup,
- .close = fsl_asrc_dma_shutdown,
- .pointer = fsl_asrc_dma_pcm_pointer,
-};
-
-static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm_substream *substream;
struct snd_pcm *pcm = rtd->pcm;
- int ret, i;
+ int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret) {
@@ -345,47 +459,19 @@ static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
- substream = pcm->streams[i].substream;
- if (!substream)
- continue;
-
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
- FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer);
- if (ret) {
- dev_err(card->dev, "failed to allocate DMA buffer\n");
- goto err;
- }
- }
-
- return 0;
-
-err:
- if (--i == 0 && pcm->streams[i].substream)
- snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
-
- return ret;
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, FSL_ASRC_DMABUF_SIZE);
}
-static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- int i;
-
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
- substream = pcm->streams[i].substream;
- if (!substream)
- continue;
-
- snd_dma_free_pages(&substream->dma_buffer);
- substream->dma_buffer.area = NULL;
- substream->dma_buffer.addr = 0;
- }
-}
-
-struct snd_soc_platform_driver fsl_asrc_platform = {
- .ops = &fsl_asrc_dma_pcm_ops,
- .pcm_new = fsl_asrc_dma_pcm_new,
- .pcm_free = fsl_asrc_dma_pcm_free,
+struct snd_soc_component_driver fsl_asrc_component = {
+ .name = DRV_NAME,
+ .hw_params = fsl_asrc_dma_hw_params,
+ .hw_free = fsl_asrc_dma_hw_free,
+ .trigger = fsl_asrc_dma_trigger,
+ .open = fsl_asrc_dma_startup,
+ .close = fsl_asrc_dma_shutdown,
+ .pointer = fsl_asrc_dma_pcm_pointer,
+ .pcm_construct = fsl_asrc_dma_pcm_new,
+ .legacy_dai_naming = 1,
};
-EXPORT_SYMBOL_GPL(fsl_asrc_platform);
+EXPORT_SYMBOL_GPL(fsl_asrc_component);
diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c
new file mode 100644
index 000000000000..f46881f71e43
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_m2m.c
@@ -0,0 +1,729 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+// Copyright (C) 2019-2024 NXP
+//
+// Freescale ASRC Memory to Memory (M2M) driver
+
+#include <linux/dma/imx-dma.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <sound/asound.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+
+#include "fsl_asrc_common.h"
+
+#define DIR_STR(dir) (dir) == IN ? "in" : "out"
+
+#define ASRC_xPUT_DMA_CALLBACK(dir) \
+ (((dir) == IN) ? asrc_input_dma_callback \
+ : asrc_output_dma_callback)
+
+/* Maximum output and capture buffer size */
+#define ASRC_M2M_BUFFER_SIZE (512 * 1024)
+
+/* Maximum output and capture period size */
+#define ASRC_M2M_PERIOD_SIZE (48 * 1024)
+
+/* dma complete callback */
+static void asrc_input_dma_callback(void *data)
+{
+ struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
+
+ complete(&pair->complete[IN]);
+}
+
+/* dma complete callback */
+static void asrc_output_dma_callback(void *data)
+{
+ struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
+
+ complete(&pair->complete[OUT]);
+}
+
+/**
+ *asrc_read_last_fifo: read all the remaining data from FIFO
+ *@pair: Structure pointer of fsl_asrc_pair
+ *@dma_vaddr: virtual address of capture buffer
+ *@length: payload length of capture buffer
+ */
+static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 i, reg, size, t_size = 0, width;
+ u32 *reg32 = NULL;
+ u16 *reg16 = NULL;
+ u8 *reg24 = NULL;
+
+ width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
+ if (width == 32)
+ reg32 = dma_vaddr + *length;
+ else if (width == 16)
+ reg16 = dma_vaddr + *length;
+ else
+ reg24 = dma_vaddr + *length;
+retry:
+ size = asrc->get_output_fifo_size(pair);
+ if (size + *length > ASRC_M2M_BUFFER_SIZE)
+ goto end;
+
+ for (i = 0; i < size * pair->channels; i++) {
+ regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), &reg);
+ if (reg32) {
+ *reg32++ = reg;
+ } else if (reg16) {
+ *reg16++ = (u16)reg;
+ } else {
+ *reg24++ = (u8)reg;
+ *reg24++ = (u8)(reg >> 8);
+ *reg24++ = (u8)(reg >> 16);
+ }
+ }
+ t_size += size;
+
+ /* In case there is data left in FIFO */
+ if (size)
+ goto retry;
+end:
+ /* Update payload length */
+ if (reg32)
+ *length += t_size * pair->channels * 4;
+ else if (reg16)
+ *length += t_size * pair->channels * 2;
+ else
+ *length += t_size * pair->channels * 3;
+}
+
+/* config dma channel */
+static int asrc_dmaconfig(struct fsl_asrc_pair *pair,
+ struct dma_chan *chan,
+ u32 dma_addr, dma_addr_t buf_addr, u32 buf_len,
+ int dir, int width)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ struct dma_slave_config slave_config;
+ enum dma_slave_buswidth buswidth;
+ unsigned int sg_len, max_period_size;
+ struct scatterlist *sg;
+ int ret, i;
+
+ switch (width) {
+ case 8:
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case 16:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case 24:
+ buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
+ break;
+ case 32:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ dev_err(dev, "invalid word width\n");
+ return -EINVAL;
+ }
+
+ memset(&slave_config, 0, sizeof(slave_config));
+ if (dir == IN) {
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = dma_addr;
+ slave_config.dst_addr_width = buswidth;
+ slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair);
+ } else {
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = dma_addr;
+ slave_config.src_addr_width = buswidth;
+ slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair);
+ }
+
+ ret = dmaengine_slave_config(chan, &slave_config);
+ if (ret) {
+ dev_err(dev, "failed to config dmaengine for %s task: %d\n",
+ DIR_STR(dir), ret);
+ return -EINVAL;
+ }
+
+ max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8);
+ /* scatter gather mode */
+ sg_len = buf_len / max_period_size;
+ if (buf_len % max_period_size)
+ sg_len += 1;
+
+ sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
+ if (!sg)
+ return -ENOMEM;
+
+ sg_init_table(sg, sg_len);
+ for (i = 0; i < (sg_len - 1); i++) {
+ sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
+ sg_dma_len(&sg[i]) = max_period_size;
+ }
+ sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
+ sg_dma_len(&sg[i]) = buf_len - i * max_period_size;
+
+ pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len,
+ slave_config.direction,
+ DMA_PREP_INTERRUPT);
+ kfree(sg);
+ if (!pair->desc[dir]) {
+ dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir));
+ return -EINVAL;
+ }
+
+ pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
+ pair->desc[dir]->callback_param = pair;
+
+ return 0;
+}
+
+/* main function of converter */
+static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ enum asrc_pair_index index = pair->index;
+ struct snd_dma_buffer *src_buf, *dst_buf;
+ unsigned int in_buf_len;
+ unsigned int out_dma_len;
+ unsigned int width;
+ u32 fifo_addr;
+ int ret = 0;
+
+ /* set ratio mod */
+ if (asrc->m2m_set_ratio_mod) {
+ if (pair->ratio_mod_flag) {
+ asrc->m2m_set_ratio_mod(pair, pair->ratio_mod);
+ pair->ratio_mod_flag = false;
+ }
+ }
+
+ src_buf = &pair->dma_buffer[IN];
+ dst_buf = &pair->dma_buffer[OUT];
+
+ width = snd_pcm_format_physical_width(pair->sample_format[IN]);
+ fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
+
+ in_buf_len = task->input_size;
+
+ if (in_buf_len < width * pair->channels / 8 ||
+ in_buf_len > ASRC_M2M_BUFFER_SIZE ||
+ in_buf_len % (width * pair->channels / 8)) {
+ dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* dma config for output dma channel */
+ ret = asrc_dmaconfig(pair,
+ pair->dma_chan[IN],
+ fifo_addr,
+ src_buf->addr,
+ in_buf_len, IN, width);
+ if (ret) {
+ dev_err(dev, "out dma config error\n");
+ goto end;
+ }
+
+ width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
+ fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
+ out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len);
+ if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) {
+ /* dma config for capture dma channel */
+ ret = asrc_dmaconfig(pair,
+ pair->dma_chan[OUT],
+ fifo_addr,
+ dst_buf->addr,
+ out_dma_len, OUT, width);
+ if (ret) {
+ dev_err(dev, "cap dma config error\n");
+ goto end;
+ }
+ } else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) {
+ dev_err(dev, "cap buffer size error\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ reinit_completion(&pair->complete[IN]);
+ reinit_completion(&pair->complete[OUT]);
+
+ /* Submit DMA request */
+ dmaengine_submit(pair->desc[IN]);
+ dma_async_issue_pending(pair->desc[IN]->chan);
+ if (out_dma_len > 0) {
+ dmaengine_submit(pair->desc[OUT]);
+ dma_async_issue_pending(pair->desc[OUT]->chan);
+ }
+
+ asrc->m2m_start(pair);
+
+ if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
+ dev_err(dev, "out DMA task timeout\n");
+ ret = -ETIMEDOUT;
+ goto end;
+ }
+
+ if (out_dma_len > 0) {
+ if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) {
+ dev_err(dev, "cap DMA task timeout\n");
+ ret = -ETIMEDOUT;
+ goto end;
+ }
+ }
+
+ /* read the last words from FIFO */
+ asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len);
+ /* update payload length for capture */
+ task->output_size = out_dma_len;
+end:
+ return ret;
+}
+
+static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct device *dev = &asrc->pdev->dev;
+ struct fsl_asrc_pair *pair;
+ int size, ret;
+
+ pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
+ if (!pair)
+ return -ENOMEM;
+
+ pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
+ pair->asrc = asrc;
+
+ init_completion(&pair->complete[IN]);
+ init_completion(&pair->complete[OUT]);
+
+ runtime->private_data = pair;
+
+ size = ASRC_M2M_BUFFER_SIZE;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]);
+ if (ret)
+ goto error_alloc_in_buf;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]);
+ if (ret)
+ goto error_alloc_out_buf;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to power up asrc\n");
+ goto err_pm_runtime;
+ }
+
+ return 0;
+
+err_pm_runtime:
+ snd_dma_free_pages(&pair->dma_buffer[OUT]);
+error_alloc_out_buf:
+ snd_dma_free_pages(&pair->dma_buffer[IN]);
+error_alloc_in_buf:
+ kfree(pair);
+ return ret;
+}
+
+static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct device *dev = &asrc->pdev->dev;
+
+ pm_runtime_put_sync(dev);
+
+ snd_dma_free_pages(&pair->dma_buffer[IN]);
+ snd_dma_free_pages(&pair->dma_buffer[OUT]);
+
+ kfree(runtime->private_data);
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc_m2m_cap cap;
+ int ret, i;
+
+ ret = asrc->m2m_get_cap(&cap);
+ if (ret)
+ return -EINVAL;
+
+ if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in)
+ pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format;
+ else
+ return -EINVAL;
+
+ if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out)
+ pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format;
+ else
+ return -EINVAL;
+
+ /* check input rate is in scope */
+ for (i = 0; i < cap.rate_in_count; i++)
+ if (params->codec.sample_rate == cap.rate_in[i]) {
+ pair->rate[IN] = params->codec.sample_rate;
+ break;
+ }
+ if (i == cap.rate_in_count)
+ return -EINVAL;
+
+ /* check output rate is in scope */
+ for (i = 0; i < cap.rate_out_count; i++)
+ if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) {
+ pair->rate[OUT] = params->codec.options.src_d.out_sample_rate;
+ break;
+ }
+ if (i == cap.rate_out_count)
+ return -EINVAL;
+
+ if (params->codec.ch_in != params->codec.ch_out ||
+ params->codec.ch_in < cap.chan_min ||
+ params->codec.ch_in > cap.chan_max)
+ return -EINVAL;
+
+ pair->channels = params->codec.ch_in;
+ pair->buf_len[IN] = params->buffer.fragment_size;
+ pair->buf_len[OUT] = params->buffer.fragment_size;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct snd_dma_buffer *dmab = dmabuf->priv;
+
+ return snd_dma_buffer_mmap(dmab, vma);
+}
+
+static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct snd_dma_buffer *dmab = attachment->dmabuf->priv;
+ struct sg_table *sgt;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return NULL;
+
+ if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0)
+ goto free;
+
+ if (dma_map_sgtable(attachment->dev, sgt, direction, 0))
+ goto free;
+
+ return sgt;
+
+free:
+ sg_free_table(sgt);
+ kfree(sgt);
+ return NULL;
+}
+
+static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+ dma_unmap_sgtable(attachment->dev, table, direction, 0);
+}
+
+static void fsl_asrc_m2m_release(struct dma_buf *dmabuf)
+{
+ /* buffer is released by fsl_asrc_m2m_comp_release() */
+}
+
+static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = {
+ .mmap = fsl_asrc_m2m_mmap,
+ .map_dma_buf = fsl_asrc_m2m_map_dma_buf,
+ .unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf,
+ .release = fsl_asrc_m2m_release,
+};
+
+static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in);
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out);
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct device *dev = &asrc->pdev->dev;
+ int ret;
+
+ exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
+ exp_info_in.size = ASRC_M2M_BUFFER_SIZE;
+ exp_info_in.flags = O_RDWR;
+ exp_info_in.priv = &pair->dma_buffer[IN];
+ task->input = dma_buf_export(&exp_info_in);
+ if (IS_ERR(task->input)) {
+ ret = PTR_ERR(task->input);
+ return ret;
+ }
+
+ exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops;
+ exp_info_out.size = ASRC_M2M_BUFFER_SIZE;
+ exp_info_out.flags = O_RDWR;
+ exp_info_out.priv = &pair->dma_buffer[OUT];
+ task->output = dma_buf_export(&exp_info_out);
+ if (IS_ERR(task->output)) {
+ ret = PTR_ERR(task->output);
+ return ret;
+ }
+
+ /* Request asrc pair/context */
+ ret = asrc->request_pair(pair->channels, pair);
+ if (ret) {
+ dev_err(dev, "failed to request pair: %d\n", ret);
+ goto err_request_pair;
+ }
+
+ ret = asrc->m2m_prepare(pair);
+ if (ret) {
+ dev_err(dev, "failed to start pair part one: %d\n", ret);
+ goto err_start_part_one;
+ }
+
+ /* Request dma channels */
+ pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
+ if (!pair->dma_chan[IN]) {
+ dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
+ ret = -EBUSY;
+ goto err_dma_channel_in;
+ }
+
+ pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
+ if (!pair->dma_chan[OUT]) {
+ dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
+ ret = -EBUSY;
+ goto err_dma_channel_out;
+ }
+
+ return 0;
+
+err_dma_channel_out:
+ dma_release_channel(pair->dma_chan[IN]);
+err_dma_channel_in:
+ if (asrc->m2m_unprepare)
+ asrc->m2m_unprepare(pair);
+err_start_part_one:
+ asrc->release_pair(pair);
+err_request_pair:
+ return ret;
+}
+
+static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ return asrc_m2m_device_run(pair, task);
+}
+
+static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ return 0;
+}
+
+static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ /* Stop & release pair/context */
+ if (asrc->m2m_stop)
+ asrc->m2m_stop(pair);
+
+ if (asrc->m2m_unprepare)
+ asrc->m2m_unprepare(pair);
+ asrc->release_pair(pair);
+
+ /* Release dma channel */
+ if (pair->dma_chan[IN])
+ dma_release_channel(pair->dma_chan[IN]);
+ if (pair->dma_chan[OUT])
+ dma_release_channel(pair->dma_chan[OUT]);
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ caps->num_codecs = 1;
+ caps->min_fragment_size = 4096;
+ caps->max_fragment_size = 4096;
+ caps->min_fragments = 1;
+ caps->max_fragments = 1;
+ caps->codecs[0] = SND_AUDIOCODEC_PCM;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
+ struct snd_compr_codec_caps *codec)
+{
+ struct fsl_asrc_m2m_cap cap;
+ snd_pcm_format_t k;
+ int j = 0;
+ int ret;
+
+ ret = asrc->m2m_get_cap(&cap);
+ if (ret)
+ return -EINVAL;
+
+ pcm_for_each_format(k) {
+ if (pcm_format_to_bits(k) & cap.fmt_in) {
+ codec->descriptor[j].max_ch = cap.chan_max;
+ memcpy(codec->descriptor[j].sample_rates,
+ cap.rate_in,
+ cap.rate_in_count * sizeof(__u32));
+ codec->descriptor[j].num_sample_rates = cap.rate_in_count;
+ codec->descriptor[j].formats = (__force __u32)k;
+ codec->descriptor[j].pcm_formats = cap.fmt_out;
+ codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0];
+ codec->descriptor[j].src.out_sample_rate_max =
+ cap.rate_out[cap.rate_out_count - 1];
+ j++;
+ }
+ }
+
+ codec->codec = SND_AUDIOCODEC_PCM;
+ codec->num_descriptors = j;
+ return 0;
+}
+
+static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+
+ return fsl_asrc_m2m_fill_codec_caps(asrc, codec);
+}
+
+static struct snd_compr_ops fsl_asrc_m2m_compr_ops = {
+ .open = fsl_asrc_m2m_comp_open,
+ .free = fsl_asrc_m2m_comp_release,
+ .set_params = fsl_asrc_m2m_comp_set_params,
+ .get_caps = fsl_asrc_m2m_get_caps,
+ .get_codec_caps = fsl_asrc_m2m_get_codec_caps,
+ .task_create = fsl_asrc_m2m_comp_task_create,
+ .task_start = fsl_asrc_m2m_comp_task_start,
+ .task_stop = fsl_asrc_m2m_comp_task_stop,
+ .task_free = fsl_asrc_m2m_comp_task_free,
+};
+
+int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc)
+{
+ struct fsl_asrc_pair *pair;
+ int i;
+
+ for (i = 0; i < PAIR_CTX_NUM; i++) {
+ pair = asrc->pair[i];
+ if (!pair || !pair->dma_buffer[IN].area || !pair->dma_buffer[OUT].area)
+ continue;
+ if (!completion_done(&pair->complete[IN])) {
+ if (pair->dma_chan[IN])
+ dmaengine_terminate_all(pair->dma_chan[IN]);
+ asrc_input_dma_callback((void *)pair);
+ }
+ if (!completion_done(&pair->complete[OUT])) {
+ if (pair->dma_chan[OUT])
+ dmaengine_terminate_all(pair->dma_chan[OUT]);
+ asrc_output_dma_callback((void *)pair);
+ }
+
+ if (asrc->m2m_pair_suspend)
+ asrc->m2m_pair_suspend(pair);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend);
+
+int fsl_asrc_m2m_resume(struct fsl_asrc *asrc)
+{
+ struct fsl_asrc_pair *pair;
+ int i;
+
+ for (i = 0; i < PAIR_CTX_NUM; i++) {
+ pair = asrc->pair[i];
+ if (!pair)
+ continue;
+ if (asrc->m2m_pair_resume)
+ asrc->m2m_pair_resume(pair);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume);
+
+int fsl_asrc_m2m_init(struct fsl_asrc *asrc)
+{
+ struct device *dev = &asrc->pdev->dev;
+ struct snd_card *card;
+ struct snd_compr *compr;
+ int ret;
+
+ ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (ret < 0)
+ return ret;
+
+ strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver));
+ strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname));
+ strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname));
+
+ asrc->card = card;
+
+ compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL);
+ if (!compr) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ compr->ops = &fsl_asrc_m2m_compr_ops;
+ compr->private_data = asrc;
+
+ ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_card_register(card);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ snd_card_free(card);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init);
+
+void fsl_asrc_m2m_exit(struct fsl_asrc *asrc)
+{
+ struct snd_card *card = asrc->card;
+
+ snd_card_free(card);
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit);
+
+MODULE_IMPORT_NS("DMA_BUF");
+MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
+MODULE_DESCRIPTION("Freescale ASRC M2M driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c
new file mode 100644
index 000000000000..da401561e2de
--- /dev/null
+++ b/sound/soc/fsl/fsl_aud2htx.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2020 NXP
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/dma-mapping.h>
+
+#include "fsl_aud2htx.h"
+#include "imx-pcm.h"
+
+static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
+ AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN);
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
+ AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
+ AUD2HTX_CTRE_DE, 0);
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
+ AUD2HTX_CTRL_EN, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev);
+
+ /* DMA request when number of entries < WTMK_LOW */
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
+ AUD2HTX_CTRE_DT_MASK, 0);
+
+ /* Disable interrupts*/
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK,
+ AUD2HTX_WM_HIGH_IRQ_MASK |
+ AUD2HTX_WM_LOW_IRQ_MASK |
+ AUD2HTX_OVF_MASK,
+ AUD2HTX_WM_HIGH_IRQ_MASK |
+ AUD2HTX_WM_LOW_IRQ_MASK |
+ AUD2HTX_OVF_MASK);
+
+ /* Configure watermark */
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
+ AUD2HTX_CTRE_WL_MASK,
+ AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT);
+ regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
+ AUD2HTX_CTRE_WH_MASK,
+ AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT);
+
+ snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx,
+ &aud2htx->dma_params_rx);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = {
+ .probe = fsl_aud2htx_dai_probe,
+ .trigger = fsl_aud2htx_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_aud2htx_dai = {
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 1,
+ .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 = FSL_AUD2HTX_FORMATS,
+ },
+ .ops = &fsl_aud2htx_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_aud2htx_component = {
+ .name = "fsl-aud2htx",
+ .legacy_dai_naming = 1,
+};
+
+static const struct reg_default fsl_aud2htx_reg_defaults[] = {
+ {AUD2HTX_CTRL, 0x00000000},
+ {AUD2HTX_CTRL_EXT, 0x00000000},
+ {AUD2HTX_WR, 0x00000000},
+ {AUD2HTX_STATUS, 0x00000000},
+ {AUD2HTX_IRQ_NOMASK, 0x00000000},
+ {AUD2HTX_IRQ_MASKED, 0x00000000},
+ {AUD2HTX_IRQ_MASK, 0x00000000},
+};
+
+static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AUD2HTX_CTRL:
+ case AUD2HTX_CTRL_EXT:
+ case AUD2HTX_STATUS:
+ case AUD2HTX_IRQ_NOMASK:
+ case AUD2HTX_IRQ_MASKED:
+ case AUD2HTX_IRQ_MASK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AUD2HTX_CTRL:
+ case AUD2HTX_CTRL_EXT:
+ case AUD2HTX_WR:
+ case AUD2HTX_IRQ_NOMASK:
+ case AUD2HTX_IRQ_MASKED:
+ case AUD2HTX_IRQ_MASK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AUD2HTX_STATUS:
+ case AUD2HTX_IRQ_NOMASK:
+ case AUD2HTX_IRQ_MASKED:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config fsl_aud2htx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = AUD2HTX_IRQ_MASK,
+ .reg_defaults = fsl_aud2htx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults),
+ .readable_reg = fsl_aud2htx_readable_reg,
+ .volatile_reg = fsl_aud2htx_volatile_reg,
+ .writeable_reg = fsl_aud2htx_writeable_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct of_device_id fsl_aud2htx_dt_ids[] = {
+ { .compatible = "fsl,imx8mp-aud2htx",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids);
+
+static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
+static int fsl_aud2htx_probe(struct platform_device *pdev)
+{
+ struct fsl_aud2htx *aud2htx;
+ struct resource *res;
+ void __iomem *regs;
+ int ret, irq;
+
+ aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL);
+ if (!aud2htx)
+ return -ENOMEM;
+
+ aud2htx->pdev = pdev;
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &fsl_aud2htx_regmap_config);
+ if (IS_ERR(aud2htx->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap");
+ return PTR_ERR(aud2htx->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0,
+ dev_name(&pdev->dev), aud2htx);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
+ return ret;
+ }
+
+ aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(aud2htx->bus_clk)) {
+ dev_err(&pdev->dev, "failed to get mem clock\n");
+ return PTR_ERR(aud2htx->bus_clk);
+ }
+
+ aud2htx->dma_params_tx.chan_name = "tx";
+ aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST;
+ aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR;
+
+ platform_set_drvdata(pdev, aud2htx);
+ pm_runtime_enable(&pdev->dev);
+
+ regcache_cache_only(aud2htx->regmap, true);
+
+ /*
+ * Register platform component before registering cpu dai for there
+ * is not defer probe for platform component in snd_soc_add_pcm_runtime().
+ */
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to pcm register\n");
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &fsl_aud2htx_component,
+ &fsl_aud2htx_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void fsl_aud2htx_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int fsl_aud2htx_runtime_suspend(struct device *dev)
+{
+ struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
+
+ regcache_cache_only(aud2htx->regmap, true);
+ clk_disable_unprepare(aud2htx->bus_clk);
+
+ return 0;
+}
+
+static int fsl_aud2htx_runtime_resume(struct device *dev)
+{
+ struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(aud2htx->bus_clk);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(aud2htx->regmap, false);
+ regcache_mark_dirty(aud2htx->regmap);
+ regcache_sync(aud2htx->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
+ RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, fsl_aud2htx_runtime_resume,
+ NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_aud2htx_driver = {
+ .probe = fsl_aud2htx_probe,
+ .remove = fsl_aud2htx_remove,
+ .driver = {
+ .name = "fsl-aud2htx",
+ .pm = pm_ptr(&fsl_aud2htx_pm_ops),
+ .of_match_table = fsl_aud2htx_dt_ids,
+ },
+};
+module_platform_driver(fsl_aud2htx_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
+MODULE_DESCRIPTION("NXP AUD2HTX driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_aud2htx.h b/sound/soc/fsl/fsl_aud2htx.h
new file mode 100644
index 000000000000..cf292e3ccc02
--- /dev/null
+++ b/sound/soc/fsl/fsl_aud2htx.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020 NXP
+ */
+
+#ifndef _FSL_AUD2HTX_H
+#define _FSL_AUD2HTX_H
+
+#define FSL_AUD2HTX_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+/* AUD2HTX Register Map */
+#define AUD2HTX_CTRL 0x0 /* AUD2HTX Control Register */
+#define AUD2HTX_CTRL_EXT 0x4 /* AUD2HTX Control Extended Register */
+#define AUD2HTX_WR 0x8 /* AUD2HTX Write Register */
+#define AUD2HTX_STATUS 0xC /* AUD2HTX Status Register */
+#define AUD2HTX_IRQ_NOMASK 0x10 /* AUD2HTX Nonmasked Interrupt Flags Register */
+#define AUD2HTX_IRQ_MASKED 0x14 /* AUD2HTX Masked Interrupt Flags Register */
+#define AUD2HTX_IRQ_MASK 0x18 /* AUD2HTX IRQ Masks Register */
+
+/* AUD2HTX Control Register */
+#define AUD2HTX_CTRL_EN BIT(0)
+
+/* AUD2HTX Control Extended Register */
+#define AUD2HTX_CTRE_DE BIT(0)
+#define AUD2HTX_CTRE_DT_SHIFT 0x1
+#define AUD2HTX_CTRE_DT_WIDTH 0x2
+#define AUD2HTX_CTRE_DT_MASK ((BIT(AUD2HTX_CTRE_DT_WIDTH) - 1) \
+ << AUD2HTX_CTRE_DT_SHIFT)
+#define AUD2HTX_CTRE_WL_SHIFT 16
+#define AUD2HTX_CTRE_WL_WIDTH 5
+#define AUD2HTX_CTRE_WL_MASK ((BIT(AUD2HTX_CTRE_WL_WIDTH) - 1) \
+ << AUD2HTX_CTRE_WL_SHIFT)
+#define AUD2HTX_CTRE_WH_SHIFT 24
+#define AUD2HTX_CTRE_WH_WIDTH 5
+#define AUD2HTX_CTRE_WH_MASK ((BIT(AUD2HTX_CTRE_WH_WIDTH) - 1) \
+ << AUD2HTX_CTRE_WH_SHIFT)
+
+/* AUD2HTX IRQ Masks Register */
+#define AUD2HTX_WM_HIGH_IRQ_MASK BIT(2)
+#define AUD2HTX_WM_LOW_IRQ_MASK BIT(1)
+#define AUD2HTX_OVF_MASK BIT(0)
+
+#define AUD2HTX_FIFO_DEPTH 0x20
+#define AUD2HTX_WTMK_LOW 0x10
+#define AUD2HTX_WTMK_HIGH 0x10
+#define AUD2HTX_MAXBURST 0x10
+
+/**
+ * fsl_aud2htx: AUD2HTX private data
+ *
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @bus_clk: clock source to access register
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ */
+struct fsl_aud2htx {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct clk *bus_clk;
+
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+};
+
+#endif /* _FSL_AUD2HTX_H */
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
new file mode 100644
index 000000000000..7981d598ba13
--- /dev/null
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2017 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_audmix.h"
+
+#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
+ SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+static const char
+ *tdm_sel[] = { "TDM1", "TDM2", },
+ *mode_sel[] = { "Disabled", "TDM1", "TDM2", "Mixed", },
+ *width_sel[] = { "16b", "18b", "20b", "24b", "32b", },
+ *endis_sel[] = { "Disabled", "Enabled", },
+ *updn_sel[] = { "Downward", "Upward", },
+ *mask_sel[] = { "Unmask", "Mask", };
+
+static const struct soc_enum fsl_audmix_enum[] = {
+/* FSL_AUDMIX_CTR enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MIXCLK_SHIFT, tdm_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTSRC_SHIFT, mode_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT, width_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKRTDF_SHIFT, mask_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKCKDF_SHIFT, mask_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCMODE_SHIFT, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCSRC_SHIFT, tdm_sel),
+/* FSL_AUDMIX_ATCR0 enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 0, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 1, updn_sel),
+/* FSL_AUDMIX_ATCR1 enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 0, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 1, updn_sel),
+};
+
+struct fsl_audmix_state {
+ u8 tdms;
+ u8 clk;
+ char msg[64];
+};
+
+static const struct fsl_audmix_state prms[4][4] = {{
+ /* DIS->DIS, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* DIS->TDM1*/
+ { .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" },
+ /* DIS->TDM2*/
+ { .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" },
+ /* DIS->MIX */
+ { .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" }
+}, { /* TDM1->DIS */
+ { .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" },
+ /* TDM1->TDM1, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* TDM1->TDM2 */
+ { .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" },
+ /* TDM1->MIX */
+ { .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" }
+}, { /* TDM2->DIS */
+ { .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" },
+ /* TDM2->TDM1 */
+ { .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" },
+ /* TDM2->TDM2, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* TDM2->MIX */
+ { .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" }
+}, { /* MIX->DIS */
+ { .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" },
+ /* MIX->TDM1 */
+ { .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" },
+ /* MIX->TDM2 */
+ { .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" },
+ /* MIX->MIX, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" }
+}, };
+
+static int fsl_audmix_state_trans(struct snd_soc_component *comp,
+ unsigned int *mask, unsigned int *ctr,
+ const struct fsl_audmix_state prm)
+{
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ /* Enforce all required TDMs are started */
+ if ((priv->tdms & prm.tdms) != prm.tdms) {
+ dev_dbg(comp->dev, "%s", prm.msg);
+ return -EINVAL;
+ }
+
+ switch (prm.clk) {
+ case 1:
+ case 2:
+ /* Set mix clock */
+ (*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK;
+ (*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int reg_val, val, mix_clk;
+
+ /* Get current state */
+ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
+ mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
+ >> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
+
+ /**
+ * Ensure the current selected mixer clock is available
+ * for configuration propagation
+ */
+ if (!(priv->tdms & BIT(mix_clk))) {
+ dev_err(comp->dev,
+ "Started TDM%d needed for config propagation!\n",
+ mix_clk + 1);
+ return -EINVAL;
+ }
+
+ if (!(priv->tdms & BIT(val))) {
+ dev_err(comp->dev,
+ "The selected clock source has no TDM%d enabled!\n",
+ val + 1);
+ return -EINVAL;
+ }
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ u32 out_src, mix_clk;
+ unsigned int reg_val, val, mask = 0, ctr = 0;
+ int ret;
+
+ /* Get current state */
+ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
+
+ /* "From" state */
+ out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK)
+ >> FSL_AUDMIX_CTR_OUTSRC_SHIFT);
+ mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
+ >> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
+
+ /* "To" state */
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
+
+ /* Check if state is changing ... */
+ if (out_src == val)
+ return 0;
+ /**
+ * Ensure the current selected mixer clock is available
+ * for configuration propagation
+ */
+ if (!(priv->tdms & BIT(mix_clk))) {
+ dev_err(comp->dev,
+ "Started TDM%d needed for config propagation!\n",
+ mix_clk + 1);
+ return -EINVAL;
+ }
+
+ /* Check state transition constraints */
+ ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]);
+ if (ret)
+ return ret;
+
+ /* Complete transition to new state */
+ mask |= FSL_AUDMIX_CTR_OUTSRC_MASK;
+ ctr |= FSL_AUDMIX_CTR_OUTSRC(val);
+
+ return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
+}
+
+static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = {
+ /* FSL_AUDMIX_CTR controls */
+ SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0],
+ snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src),
+ SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1],
+ snd_soc_get_enum_double, fsl_audmix_put_out_src),
+ SOC_ENUM("Output Width", fsl_audmix_enum[2]),
+ SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]),
+ SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]),
+ SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]),
+ SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]),
+ /* TDM1 Attenuation controls */
+ SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]),
+ SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]),
+ SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0,
+ 2, 0x00fff, 0),
+ SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0,
+ 0, 0x3ffff, 0),
+ /* TDM2 Attenuation controls */
+ SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]),
+ SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]),
+ SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1,
+ 2, 0x00fff, 0),
+ SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1,
+ 0, 0x3ffff, 0),
+};
+
+static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ u32 mask = 0, ctr = 0;
+
+ /* AUDMIX is working in DSP_A format only */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* For playback the AUDMIX is consumer, and for record is provider */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ case SND_SOC_DAIFMT_BP_FP:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Output data will be written on positive edge of the clock */
+ ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Output data will be written on negative edge of the clock */
+ ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK;
+
+ return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
+}
+
+static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned long lock_flags;
+
+ /* Capture stream shall not be handled */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&priv->lock, lock_flags);
+ priv->tdms |= BIT(dai->driver->id);
+ spin_unlock_irqrestore(&priv->lock, lock_flags);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&priv->lock, lock_flags);
+ priv->tdms &= ~BIT(dai->driver->id);
+ spin_unlock_irqrestore(&priv->lock, lock_flags);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_audmix_dai_ops = {
+ .set_fmt = fsl_audmix_dai_set_fmt,
+ .trigger = fsl_audmix_dai_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_audmix_dai[] = {
+ {
+ .id = 0,
+ .name = "audmix-0",
+ .playback = {
+ .stream_name = "AUDMIX-Playback-0",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+ {
+ .id = 1,
+ .name = "audmix-1",
+ .playback = {
+ .stream_name = "AUDMIX-Playback-1",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+ {
+ .id = 2,
+ .name = "audmix-2",
+ .capture = {
+ .stream_name = "AUDMIX-Capture-0",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver fsl_audmix_component = {
+ .name = "fsl-audmix-dai",
+ .controls = fsl_audmix_snd_controls,
+ .num_controls = ARRAY_SIZE(fsl_audmix_snd_controls),
+};
+
+static bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_AUDMIX_CTR:
+ case FSL_AUDMIX_STR:
+ case FSL_AUDMIX_ATCR0:
+ case FSL_AUDMIX_ATIVAL0:
+ case FSL_AUDMIX_ATSTPUP0:
+ case FSL_AUDMIX_ATSTPDN0:
+ case FSL_AUDMIX_ATSTPTGT0:
+ case FSL_AUDMIX_ATTNVAL0:
+ case FSL_AUDMIX_ATSTP0:
+ case FSL_AUDMIX_ATCR1:
+ case FSL_AUDMIX_ATIVAL1:
+ case FSL_AUDMIX_ATSTPUP1:
+ case FSL_AUDMIX_ATSTPDN1:
+ case FSL_AUDMIX_ATSTPTGT1:
+ case FSL_AUDMIX_ATTNVAL1:
+ case FSL_AUDMIX_ATSTP1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_AUDMIX_CTR:
+ case FSL_AUDMIX_ATCR0:
+ case FSL_AUDMIX_ATIVAL0:
+ case FSL_AUDMIX_ATSTPUP0:
+ case FSL_AUDMIX_ATSTPDN0:
+ case FSL_AUDMIX_ATSTPTGT0:
+ case FSL_AUDMIX_ATCR1:
+ case FSL_AUDMIX_ATIVAL1:
+ case FSL_AUDMIX_ATSTPUP1:
+ case FSL_AUDMIX_ATSTPDN1:
+ case FSL_AUDMIX_ATSTPTGT1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default fsl_audmix_reg[] = {
+ { FSL_AUDMIX_CTR, 0x00060 },
+ { FSL_AUDMIX_STR, 0x00003 },
+ { FSL_AUDMIX_ATCR0, 0x00000 },
+ { FSL_AUDMIX_ATIVAL0, 0x3FFFF },
+ { FSL_AUDMIX_ATSTPUP0, 0x2AAAA },
+ { FSL_AUDMIX_ATSTPDN0, 0x30000 },
+ { FSL_AUDMIX_ATSTPTGT0, 0x00010 },
+ { FSL_AUDMIX_ATTNVAL0, 0x00000 },
+ { FSL_AUDMIX_ATSTP0, 0x00000 },
+ { FSL_AUDMIX_ATCR1, 0x00000 },
+ { FSL_AUDMIX_ATIVAL1, 0x3FFFF },
+ { FSL_AUDMIX_ATSTPUP1, 0x2AAAA },
+ { FSL_AUDMIX_ATSTPDN1, 0x30000 },
+ { FSL_AUDMIX_ATSTPTGT1, 0x00010 },
+ { FSL_AUDMIX_ATTNVAL1, 0x00000 },
+ { FSL_AUDMIX_ATSTP1, 0x00000 },
+};
+
+static const struct regmap_config fsl_audmix_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_AUDMIX_ATSTP1,
+ .reg_defaults = fsl_audmix_reg,
+ .num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg),
+ .readable_reg = fsl_audmix_readable_reg,
+ .writeable_reg = fsl_audmix_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id fsl_audmix_ids[] = {
+ {
+ .compatible = "fsl,imx8qm-audmix",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
+
+static int fsl_audmix_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fsl_audmix *priv;
+ void __iomem *regs;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get the addresses */
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->regmap = devm_regmap_init_mmio(dev, regs, &fsl_audmix_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(dev, "failed to init regmap\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->ipg_clk = devm_clk_get(dev, "ipg");
+ if (IS_ERR(priv->ipg_clk)) {
+ dev_err(dev, "failed to get ipg clock\n");
+ return PTR_ERR(priv->ipg_clk);
+ }
+
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+ pm_runtime_enable(dev);
+
+ ret = devm_snd_soc_register_component(dev, &fsl_audmix_component,
+ fsl_audmix_dai,
+ ARRAY_SIZE(fsl_audmix_dai));
+ if (ret) {
+ dev_err(dev, "failed to register ASoC DAI\n");
+ goto err_disable_pm;
+ }
+
+ /*
+ * If dais property exist, then register the imx-audmix card driver.
+ * otherwise, it should be linked by audio graph card.
+ */
+ if (of_find_property(pdev->dev.of_node, "dais", NULL)) {
+ priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0);
+ if (IS_ERR(priv->pdev)) {
+ ret = PTR_ERR(priv->pdev);
+ dev_err(dev, "failed to register platform: %d\n", ret);
+ goto err_disable_pm;
+ }
+ }
+
+ return 0;
+
+err_disable_pm:
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void fsl_audmix_remove(struct platform_device *pdev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ if (priv->pdev)
+ platform_device_unregister(priv->pdev);
+}
+
+static int fsl_audmix_runtime_resume(struct device *dev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->ipg_clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable IPG clock: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(priv->regmap, false);
+ regcache_mark_dirty(priv->regmap);
+
+ return regcache_sync(priv->regmap);
+}
+
+static int fsl_audmix_runtime_suspend(struct device *dev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(priv->regmap, true);
+
+ clk_disable_unprepare(priv->ipg_clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops fsl_audmix_pm = {
+ RUNTIME_PM_OPS(fsl_audmix_runtime_suspend, fsl_audmix_runtime_resume,
+ NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_audmix_driver = {
+ .probe = fsl_audmix_probe,
+ .remove = fsl_audmix_remove,
+ .driver = {
+ .name = "fsl-audmix",
+ .of_match_table = fsl_audmix_ids,
+ .pm = pm_ptr(&fsl_audmix_pm),
+ },
+};
+module_platform_driver(fsl_audmix_driver);
+
+MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_ALIAS("platform:fsl-audmix");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_audmix.h b/sound/soc/fsl/fsl_audmix.h
new file mode 100644
index 000000000000..479f05695d53
--- /dev/null
+++ b/sound/soc/fsl/fsl_audmix.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2017 NXP
+ */
+
+#ifndef __FSL_AUDMIX_H
+#define __FSL_AUDMIX_H
+
+#define FSL_AUDMIX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+/* AUDMIX Registers */
+#define FSL_AUDMIX_CTR 0x200 /* Control */
+#define FSL_AUDMIX_STR 0x204 /* Status */
+
+#define FSL_AUDMIX_ATCR0 0x208 /* Attenuation Control */
+#define FSL_AUDMIX_ATIVAL0 0x20c /* Attenuation Initial Value */
+#define FSL_AUDMIX_ATSTPUP0 0x210 /* Attenuation step up factor */
+#define FSL_AUDMIX_ATSTPDN0 0x214 /* Attenuation step down factor */
+#define FSL_AUDMIX_ATSTPTGT0 0x218 /* Attenuation step target */
+#define FSL_AUDMIX_ATTNVAL0 0x21c /* Attenuation Value */
+#define FSL_AUDMIX_ATSTP0 0x220 /* Attenuation step number */
+
+#define FSL_AUDMIX_ATCR1 0x228 /* Attenuation Control */
+#define FSL_AUDMIX_ATIVAL1 0x22c /* Attenuation Initial Value */
+#define FSL_AUDMIX_ATSTPUP1 0x230 /* Attenuation step up factor */
+#define FSL_AUDMIX_ATSTPDN1 0x234 /* Attenuation step down factor */
+#define FSL_AUDMIX_ATSTPTGT1 0x238 /* Attenuation step target */
+#define FSL_AUDMIX_ATTNVAL1 0x23c /* Attenuation Value */
+#define FSL_AUDMIX_ATSTP1 0x240 /* Attenuation step number */
+
+/* AUDMIX Control Register */
+#define FSL_AUDMIX_CTR_MIXCLK_SHIFT 0
+#define FSL_AUDMIX_CTR_MIXCLK_MASK BIT(FSL_AUDMIX_CTR_MIXCLK_SHIFT)
+#define FSL_AUDMIX_CTR_MIXCLK(i) ((i) << FSL_AUDMIX_CTR_MIXCLK_SHIFT)
+#define FSL_AUDMIX_CTR_OUTSRC_SHIFT 1
+#define FSL_AUDMIX_CTR_OUTSRC_MASK (0x3 << FSL_AUDMIX_CTR_OUTSRC_SHIFT)
+#define FSL_AUDMIX_CTR_OUTSRC(i) (((i) << FSL_AUDMIX_CTR_OUTSRC_SHIFT)\
+ & FSL_AUDMIX_CTR_OUTSRC_MASK)
+#define FSL_AUDMIX_CTR_OUTWIDTH_SHIFT 3
+#define FSL_AUDMIX_CTR_OUTWIDTH_MASK (0x7 << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)
+#define FSL_AUDMIX_CTR_OUTWIDTH(i) (((i) << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)\
+ & FSL_AUDMIX_CTR_OUTWIDTH_MASK)
+#define FSL_AUDMIX_CTR_OUTCKPOL_SHIFT 6
+#define FSL_AUDMIX_CTR_OUTCKPOL_MASK BIT(FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
+#define FSL_AUDMIX_CTR_OUTCKPOL(i) ((i) << FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
+#define FSL_AUDMIX_CTR_MASKRTDF_SHIFT 7
+#define FSL_AUDMIX_CTR_MASKRTDF_MASK BIT(FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKRTDF(i) ((i) << FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKCKDF_SHIFT 8
+#define FSL_AUDMIX_CTR_MASKCKDF_MASK BIT(FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKCKDF(i) ((i) << FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCMODE_SHIFT 9
+#define FSL_AUDMIX_CTR_SYNCMODE_MASK BIT(FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCMODE(i) ((i) << FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCSRC_SHIFT 10
+#define FSL_AUDMIX_CTR_SYNCSRC_MASK BIT(FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCSRC(i) ((i) << FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
+
+/* AUDMIX Status Register */
+#define FSL_AUDMIX_STR_RATEDIFF BIT(0)
+#define FSL_AUDMIX_STR_CLKDIFF BIT(1)
+#define FSL_AUDMIX_STR_MIXSTAT_SHIFT 2
+#define FSL_AUDMIX_STR_MIXSTAT_MASK (0x3 << FSL_AUDMIX_STR_MIXSTAT_SHIFT)
+#define FSL_AUDMIX_STR_MIXSTAT(i) (((i) & FSL_AUDMIX_STR_MIXSTAT_MASK) \
+ >> FSL_AUDMIX_STR_MIXSTAT_SHIFT)
+/* AUDMIX Attenuation Control Register */
+#define FSL_AUDMIX_ATCR_AT_EN BIT(0)
+#define FSL_AUDMIX_ATCR_AT_UPDN BIT(1)
+#define FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT 2
+#define FSL_AUDMIX_ATCR_ATSTPDFI_MASK \
+ (0xfff << FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT)
+
+/* AUDMIX Attenuation Initial Value Register */
+#define FSL_AUDMIX_ATIVAL_ATINVAL_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Up Factor Register */
+#define FSL_AUDMIX_ATSTPUP_ATSTEPUP_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Down Factor Register */
+#define FSL_AUDMIX_ATSTPDN_ATSTEPDN_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Target Register */
+#define FSL_AUDMIX_ATSTPTGT_ATSTPTG_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Value Register */
+#define FSL_AUDMIX_ATTNVAL_ATCURVAL_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Number Register */
+#define FSL_AUDMIX_ATSTP_STPCTR_MASK 0x3FFFF
+
+#define FSL_AUDMIX_MAX_DAIS 2
+struct fsl_audmix {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct clk *ipg_clk;
+ spinlock_t lock; /* Protect tdms */
+ u8 tdms;
+};
+
+#endif /* __FSL_AUDMIX_H */
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index ccadefceeff2..aca066b5a43c 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -1,18 +1,14 @@
-/*
- * Freescale DMA ALSA SoC PCM driver
- *
- * Author: Timur Tabi <timur@freescale.com>
- *
- * Copyright 2007-2010 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 driver implements ASoC support for the Elo DMA controller, which is
- * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
- * the PCM driver is what handles the DMA buffer.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale DMA ALSA SoC PCM driver
+//
+// Author: Timur Tabi <timur@freescale.com>
+//
+// Copyright 2007-2010 Freescale Semiconductor, Inc.
+//
+// This driver implements ASoC support for the Elo DMA controller, which is
+// the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
+// the PCM driver is what handles the DMA buffer.
#include <linux/module.h>
#include <linux/init.h>
@@ -37,6 +33,8 @@
#include "fsl_dma.h"
#include "fsl_ssi.h" /* For the offset of stx0 and srx0 */
+#define DRV_NAME "fsl_dma"
+
/*
* The formats that the DMA controller supports, which is anything
* that is 8, 16, or 32 bits.
@@ -56,14 +54,13 @@
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_U32_BE)
struct dma_object {
- struct snd_soc_platform_driver dai;
+ struct snd_soc_component_driver dai;
dma_addr_t ssi_stx_phys;
dma_addr_t ssi_srx_phys;
unsigned int ssi_fifo_depth;
struct ccsr_dma_channel __iomem *channel;
unsigned int irq;
bool assigned;
- char path[1];
};
/*
@@ -157,7 +154,7 @@ static void fsl_dma_abort_stream(struct snd_pcm_substream *substream)
/**
* fsl_dma_update_pointers - update LD pointers to point to the next period
*
- * As each period is completed, this function changes the the link
+ * As each period is completed, this function changes the link
* descriptor pointers for that period to point to the next period.
*/
static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private)
@@ -203,8 +200,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
{
struct fsl_dma_private *dma_private = dev_id;
struct snd_pcm_substream *substream = dma_private->substream;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct device *dev = rtd->platform->dev;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct device *dev = rtd->dev;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
irqreturn_t ret = IRQ_NONE;
u32 sr, sr2 = 0;
@@ -282,7 +279,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* Regardless of where the memory is actually allocated, since the device can
* technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
*/
-static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int fsl_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -292,32 +290,9 @@ static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
if (ret)
return ret;
- /* Some codecs have separate DAIs for playback and capture, so we
- * should allocate a DMA buffer only for the streams that are valid.
- */
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev,
- fsl_dma_hardware.buffer_bytes_max,
- &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer);
- if (ret) {
- dev_err(card->dev, "can't alloc playback dma buffer\n");
- return ret;
- }
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev,
- fsl_dma_hardware.buffer_bytes_max,
- &pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->dma_buffer);
- if (ret) {
- dev_err(card->dev, "can't alloc capture dma buffer\n");
- snd_dma_free_pages(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer);
- return ret;
- }
- }
-
- return 0;
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev,
+ fsl_dma_hardware.buffer_bytes_max);
}
/**
@@ -382,19 +357,18 @@ static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
* buffer, which is what ALSA expects. We're just dividing it into
* contiguous parts, and creating a link descriptor for each one.
*/
-static int fsl_dma_open(struct snd_pcm_substream *substream)
+static int fsl_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct device *dev = rtd->platform->dev;
+ struct device *dev = component->dev;
struct dma_object *dma =
- container_of(rtd->platform->driver, struct dma_object, dai);
+ container_of(component->driver, struct dma_object, dai);
struct fsl_dma_private *dma_private;
struct ccsr_dma_channel __iomem *dma_channel;
dma_addr_t ld_buf_phys;
u64 temp_link; /* Pointer to next link descriptor */
u32 mr;
- unsigned int channel;
int ret = 0;
unsigned int i;
@@ -410,8 +384,6 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
return ret;
}
- channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-
if (dma->assigned) {
dev_err(dev, "dma channel already assigned\n");
return -EBUSY;
@@ -447,7 +419,6 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
dma->assigned = true;
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware);
runtime->private_data = dma_private;
@@ -534,13 +505,13 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
* and 8 bytes at a time). So we do not support packed 24-bit samples.
* 24-bit data must be padded to 32 bits.
*/
-static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int fsl_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct device *dev = rtd->platform->dev;
+ struct device *dev = component->dev;
/* Number of bits per sample */
unsigned int sample_bits =
@@ -698,12 +669,12 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* The base address of the buffer is stored in the source_addr field of the
* first link descriptor.
*/
-static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t fsl_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct device *dev = rtd->platform->dev;
+ struct device *dev = component->dev;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
dma_addr_t position;
snd_pcm_uframes_t frames;
@@ -762,7 +733,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
*
* This function can be called multiple times.
*/
-static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
+static int fsl_dma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
@@ -795,14 +767,14 @@ static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
/**
* fsl_dma_close: close the stream.
*/
-static int fsl_dma_close(struct snd_pcm_substream *substream)
+static int fsl_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct device *dev = rtd->platform->dev;
+ struct device *dev = component->dev;
struct dma_object *dma =
- container_of(rtd->platform->driver, struct dma_object, dai);
+ container_of(component->driver, struct dma_object, dai);
if (dma_private) {
if (dma_private->irq)
@@ -819,24 +791,6 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
return 0;
}
-/*
- * Remove this PCM driver.
- */
-static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
- substream = pcm->streams[i].substream;
- if (substream) {
- snd_dma_free_pages(&substream->dma_buffer);
- substream->dma_buffer.area = NULL;
- substream->dma_buffer.addr = 0;
- }
- }
-}
-
/**
* find_ssi_node -- returns the SSI node that points to its DMA channel node
*
@@ -870,17 +824,8 @@ static struct device_node *find_ssi_node(struct device_node *dma_channel_np)
return NULL;
}
-static struct snd_pcm_ops fsl_dma_ops = {
- .open = fsl_dma_open,
- .close = fsl_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fsl_dma_hw_params,
- .hw_free = fsl_dma_hw_free,
- .pointer = fsl_dma_pointer,
-};
-
static int fsl_soc_dma_probe(struct platform_device *pdev)
- {
+{
struct dma_object *dma;
struct device_node *np = pdev->dev.of_node;
struct device_node *ssi_np;
@@ -897,27 +842,29 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
ret = of_address_to_resource(ssi_np, 0, &res);
if (ret) {
- dev_err(&pdev->dev, "could not determine resources for %s\n",
- ssi_np->full_name);
+ dev_err(&pdev->dev, "could not determine resources for %pOF\n",
+ ssi_np);
of_node_put(ssi_np);
return ret;
}
- dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL);
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
if (!dma) {
- dev_err(&pdev->dev, "could not allocate dma object\n");
of_node_put(ssi_np);
return -ENOMEM;
}
- strcpy(dma->path, np->full_name);
- dma->dai.ops = &fsl_dma_ops;
- dma->dai.pcm_new = fsl_dma_new;
- dma->dai.pcm_free = fsl_dma_free_dma_buffers;
+ dma->dai.name = DRV_NAME;
+ dma->dai.open = fsl_dma_open;
+ dma->dai.close = fsl_dma_close;
+ dma->dai.hw_params = fsl_dma_hw_params;
+ dma->dai.hw_free = fsl_dma_hw_free;
+ dma->dai.pointer = fsl_dma_pointer;
+ dma->dai.pcm_construct = fsl_dma_new;
/* Store the SSI-specific information that we need */
- dma->ssi_stx_phys = res.start + CCSR_SSI_STX0;
- dma->ssi_srx_phys = res.start + CCSR_SSI_SRX0;
+ dma->ssi_stx_phys = res.start + REG_SSI_STX0;
+ dma->ssi_srx_phys = res.start + REG_SSI_SRX0;
iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
if (iprop)
@@ -928,7 +875,7 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
of_node_put(ssi_np);
- ret = snd_soc_register_platform(&pdev->dev, &dma->dai);
+ ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "could not register platform\n");
kfree(dma);
@@ -943,16 +890,13 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
return 0;
}
-static int fsl_soc_dma_remove(struct platform_device *pdev)
+static void fsl_soc_dma_remove(struct platform_device *pdev)
{
struct dma_object *dma = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
iounmap(dma->channel);
irq_dispose_mapping(dma->irq);
kfree(dma);
-
- return 0;
}
static const struct of_device_id fsl_soc_dma_ids[] = {
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
index 78fee97e8036..f19ae765b656 100644
--- a/sound/soc/fsl/fsl_dma.h
+++ b/sound/soc/fsl/fsl_dma.h
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC
- *
- * This program is free software; you can 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 _MPC8610_PCM_H
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
new file mode 100644
index 000000000000..f404a39009e1
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -0,0 +1,2363 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2019 NXP
+
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/sched/signal.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/gcd.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+
+#include "fsl_easrc.h"
+#include "imx-pcm.h"
+
+#define FSL_EASRC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_U24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_U20_3LE | \
+ SNDRV_PCM_FMTBIT_FLOAT_LE)
+
+static int fsl_easrc_iec958_put_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_asrc *easrc = snd_soc_component_get_drvdata(comp);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regval = ucontrol->value.integer.value[0];
+
+ easrc_priv->bps_iec958[mc->regbase] = regval;
+
+ return 0;
+}
+
+static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_asrc *easrc = snd_soc_component_get_drvdata(comp);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+
+ ucontrol->value.enumerated.item[0] = easrc_priv->bps_iec958[mc->regbase];
+
+ return 0;
+}
+
+static int fsl_easrc_get_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regval;
+
+ regval = snd_soc_component_read(component, mc->regbase);
+
+ ucontrol->value.integer.value[0] = regval;
+
+ return 0;
+}
+
+static int fsl_easrc_set_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regval = ucontrol->value.integer.value[0];
+ int ret;
+
+ ret = snd_soc_component_write(component, mc->regbase, regval);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#define SOC_SINGLE_REG_RW(xname, xreg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_soc_info_xr_sx, .get = fsl_easrc_get_reg, \
+ .put = fsl_easrc_set_reg, \
+ .private_value = (unsigned long)&(struct soc_mreg_control) \
+ { .regbase = xreg, .regcount = 1, .nbits = 32, \
+ .invert = 0, .min = 0, .max = 0xffffffff, } }
+
+#define SOC_SINGLE_VAL_RW(xname, xreg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_soc_info_xr_sx, .get = fsl_easrc_iec958_get_bits, \
+ .put = fsl_easrc_iec958_put_bits, \
+ .private_value = (unsigned long)&(struct soc_mreg_control) \
+ { .regbase = xreg, .regcount = 1, .nbits = 32, \
+ .invert = 0, .min = 0, .max = 2, } }
+
+static const struct snd_kcontrol_new fsl_easrc_snd_controls[] = {
+ SOC_SINGLE("Context 0 Dither Switch", REG_EASRC_COC(0), 0, 1, 0),
+ SOC_SINGLE("Context 1 Dither Switch", REG_EASRC_COC(1), 0, 1, 0),
+ SOC_SINGLE("Context 2 Dither Switch", REG_EASRC_COC(2), 0, 1, 0),
+ SOC_SINGLE("Context 3 Dither Switch", REG_EASRC_COC(3), 0, 1, 0),
+
+ SOC_SINGLE("Context 0 IEC958 Validity", REG_EASRC_COC(0), 2, 1, 0),
+ SOC_SINGLE("Context 1 IEC958 Validity", REG_EASRC_COC(1), 2, 1, 0),
+ SOC_SINGLE("Context 2 IEC958 Validity", REG_EASRC_COC(2), 2, 1, 0),
+ SOC_SINGLE("Context 3 IEC958 Validity", REG_EASRC_COC(3), 2, 1, 0),
+
+ SOC_SINGLE_VAL_RW("Context 0 IEC958 Bits Per Sample", 0),
+ SOC_SINGLE_VAL_RW("Context 1 IEC958 Bits Per Sample", 1),
+ SOC_SINGLE_VAL_RW("Context 2 IEC958 Bits Per Sample", 2),
+ SOC_SINGLE_VAL_RW("Context 3 IEC958 Bits Per Sample", 3),
+
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS0", REG_EASRC_CS0(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS0", REG_EASRC_CS0(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS0", REG_EASRC_CS0(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS0", REG_EASRC_CS0(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS1", REG_EASRC_CS1(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS1", REG_EASRC_CS1(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS1", REG_EASRC_CS1(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS1", REG_EASRC_CS1(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS2", REG_EASRC_CS2(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS2", REG_EASRC_CS2(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS2", REG_EASRC_CS2(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS2", REG_EASRC_CS2(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS3", REG_EASRC_CS3(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS3", REG_EASRC_CS3(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS3", REG_EASRC_CS3(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS3", REG_EASRC_CS3(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS4", REG_EASRC_CS4(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS4", REG_EASRC_CS4(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS4", REG_EASRC_CS4(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS4", REG_EASRC_CS4(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS5", REG_EASRC_CS5(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS5", REG_EASRC_CS5(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS5", REG_EASRC_CS5(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS5", REG_EASRC_CS5(3)),
+};
+
+/*
+ * fsl_easrc_set_rs_ratio
+ *
+ * According to the resample taps, calculate the resample ratio
+ * ratio = in_rate / out_rate
+ */
+static int fsl_easrc_set_rs_ratio(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ unsigned int in_rate = ctx_priv->in_params.norm_rate;
+ unsigned int out_rate = ctx_priv->out_params.norm_rate;
+ unsigned int frac_bits;
+ u64 val;
+ u32 *r;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (u64)in_rate << frac_bits;
+ do_div(val, out_rate);
+ r = (uint32_t *)&val;
+
+ if (r[1] & 0xFFFFF000) {
+ dev_err(&easrc->pdev->dev, "ratio exceed range\n");
+ return -EINVAL;
+ }
+
+ regmap_write(easrc->regmap, REG_EASRC_RRL(ctx->index),
+ EASRC_RRL_RS_RL(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_RRH(ctx->index),
+ EASRC_RRH_RS_RH(r[1]));
+
+ return 0;
+}
+
+/* Normalize input and output sample rates */
+static void fsl_easrc_normalize_rates(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ int a, b;
+
+ if (!ctx)
+ return;
+
+ ctx_priv = ctx->private;
+
+ a = ctx_priv->in_params.sample_rate;
+ b = ctx_priv->out_params.sample_rate;
+
+ a = gcd(a, b);
+
+ /* Divide by gcd to normalize the rate */
+ ctx_priv->in_params.norm_rate = ctx_priv->in_params.sample_rate / a;
+ ctx_priv->out_params.norm_rate = ctx_priv->out_params.sample_rate / a;
+}
+
+/* Resets the pointer of the coeff memory pointers */
+static int fsl_easrc_coeff_mem_ptr_reset(struct fsl_asrc *easrc,
+ unsigned int ctx_id, int mem_type)
+{
+ struct device *dev;
+ u32 reg, mask, val;
+
+ if (!easrc)
+ return -ENODEV;
+
+ dev = &easrc->pdev->dev;
+
+ switch (mem_type) {
+ case EASRC_PF_COEFF_MEM:
+ /* This resets the prefilter memory pointer addr */
+ if (ctx_id >= EASRC_CTX_MAX_NUM) {
+ dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+ return -EINVAL;
+ }
+
+ reg = REG_EASRC_CCE1(ctx_id);
+ mask = EASRC_CCE1_COEF_MEM_RST_MASK;
+ val = EASRC_CCE1_COEF_MEM_RST;
+ break;
+ case EASRC_RS_COEFF_MEM:
+ /* This resets the resampling memory pointer addr */
+ reg = REG_EASRC_CRCC;
+ mask = EASRC_CRCC_RS_CPR_MASK;
+ val = EASRC_CRCC_RS_CPR;
+ break;
+ default:
+ dev_err(dev, "Unknown memory type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * To reset the write pointer back to zero, the register field
+ * ASRC_CTX_CTRL_EXT1x[PF_COEFF_MEM_RST] can be toggled from
+ * 0x0 to 0x1 to 0x0.
+ */
+ regmap_update_bits(easrc->regmap, reg, mask, 0);
+ regmap_update_bits(easrc->regmap, reg, mask, val);
+ regmap_update_bits(easrc->regmap, reg, mask, 0);
+
+ return 0;
+}
+
+static inline uint32_t bits_taps_to_val(unsigned int t)
+{
+ switch (t) {
+ case EASRC_RS_32_TAPS:
+ return 32;
+ case EASRC_RS_64_TAPS:
+ return 64;
+ case EASRC_RS_128_TAPS:
+ return 128;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_resampler_config(struct fsl_asrc *easrc)
+{
+ struct device *dev = &easrc->pdev->dev;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct asrc_firmware_hdr *hdr = easrc_priv->firmware_hdr;
+ struct interp_params *interp = easrc_priv->interp;
+ struct interp_params *selected_interp = NULL;
+ unsigned int num_coeff;
+ unsigned int i;
+ u64 *coef;
+ u32 *r;
+ int ret;
+
+ if (!hdr) {
+ dev_err(dev, "firmware not loaded!\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < hdr->interp_scen; i++) {
+ if ((interp[i].num_taps - 1) !=
+ bits_taps_to_val(easrc_priv->rs_num_taps))
+ continue;
+
+ coef = interp[i].coeff;
+ selected_interp = &interp[i];
+ dev_dbg(dev, "Selected interp_filter: %u taps - %u phases\n",
+ selected_interp->num_taps,
+ selected_interp->num_phases);
+ break;
+ }
+
+ if (!selected_interp) {
+ dev_err(dev, "failed to get interpreter configuration\n");
+ return -EINVAL;
+ }
+
+ /*
+ * RS_LOW - first half of center tap of the sinc function
+ * RS_HIGH - second half of center tap of the sinc function
+ * This is due to the fact the resampling function must be
+ * symetrical - i.e. odd number of taps
+ */
+ r = (uint32_t *)&selected_interp->center_tap;
+ regmap_write(easrc->regmap, REG_EASRC_RCTCL, EASRC_RCTCL_RS_CL(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_RCTCH, EASRC_RCTCH_RS_CH(r[1]));
+
+ /*
+ * Write Number of Resampling Coefficient Taps
+ * 00b - 32-Tap Resampling Filter
+ * 01b - 64-Tap Resampling Filter
+ * 10b - 128-Tap Resampling Filter
+ * 11b - N/A
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CRCC,
+ EASRC_CRCC_RS_TAPS_MASK,
+ EASRC_CRCC_RS_TAPS(easrc_priv->rs_num_taps));
+
+ /* Reset prefilter coefficient pointer back to 0 */
+ ret = fsl_easrc_coeff_mem_ptr_reset(easrc, 0, EASRC_RS_COEFF_MEM);
+ if (ret)
+ return ret;
+
+ /*
+ * When the filter is programmed to run in:
+ * 32-tap mode, 16-taps, 128-phases 4-coefficients per phase
+ * 64-tap mode, 32-taps, 64-phases 4-coefficients per phase
+ * 128-tap mode, 64-taps, 32-phases 4-coefficients per phase
+ * This means the number of writes is constant no matter
+ * the mode we are using
+ */
+ num_coeff = 16 * 128 * 4;
+
+ for (i = 0; i < num_coeff; i++) {
+ r = (uint32_t *)&coef[i];
+ regmap_write(easrc->regmap, REG_EASRC_CRCM,
+ EASRC_CRCM_RS_CWD(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_CRCM,
+ EASRC_CRCM_RS_CWD(r[1]));
+ }
+
+ return 0;
+}
+
+/**
+ * fsl_easrc_normalize_filter - Scale filter coefficients (64 bits float)
+ * For input float32 normalized range (1.0,-1.0) -> output int[16,24,32]:
+ * scale it by multiplying filter coefficients by 2^31
+ * For input int[16, 24, 32] -> output float32
+ * scale it by multiplying filter coefficients by 2^-15, 2^-23, 2^-31
+ * input:
+ * @easrc: Structure pointer of fsl_asrc
+ * @infilter : Pointer to non-scaled input filter
+ * @shift: The multiply factor
+ * output:
+ * @outfilter: scaled filter
+ */
+static int fsl_easrc_normalize_filter(struct fsl_asrc *easrc,
+ u64 *infilter,
+ u64 *outfilter,
+ int shift)
+{
+ struct device *dev = &easrc->pdev->dev;
+ u64 coef = *infilter;
+ s64 exp = (coef & 0x7ff0000000000000ll) >> 52;
+ u64 outcoef;
+
+ /*
+ * If exponent is zero (value == 0), or 7ff (value == NaNs)
+ * dont touch the content
+ */
+ if (exp == 0 || exp == 0x7ff) {
+ *outfilter = coef;
+ return 0;
+ }
+
+ /* coef * 2^shift ==> exp + shift */
+ exp += shift;
+
+ if ((shift > 0 && exp >= 0x7ff) || (shift < 0 && exp <= 0)) {
+ dev_err(dev, "coef out of range\n");
+ return -EINVAL;
+ }
+
+ outcoef = (u64)(coef & 0x800FFFFFFFFFFFFFll) + ((u64)exp << 52);
+ *outfilter = outcoef;
+
+ return 0;
+}
+
+static int fsl_easrc_write_pf_coeff_mem(struct fsl_asrc *easrc, int ctx_id,
+ u64 *coef, int n_taps, int shift)
+{
+ struct device *dev = &easrc->pdev->dev;
+ int ret = 0;
+ int i;
+ u32 *r;
+ u64 tmp;
+
+ /* If STx_NUM_TAPS is set to 0x0 then return */
+ if (!n_taps)
+ return 0;
+
+ if (!coef) {
+ dev_err(dev, "coef table is NULL\n");
+ return -EINVAL;
+ }
+
+ /*
+ * When switching between stages, the address pointer
+ * should be reset back to 0x0 before performing a write
+ */
+ ret = fsl_easrc_coeff_mem_ptr_reset(easrc, ctx_id, EASRC_PF_COEFF_MEM);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < (n_taps + 1) / 2; i++) {
+ ret = fsl_easrc_normalize_filter(easrc, &coef[i], &tmp, shift);
+ if (ret)
+ return ret;
+
+ r = (uint32_t *)&tmp;
+ regmap_write(easrc->regmap, REG_EASRC_PCF(ctx_id),
+ EASRC_PCF_CD(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_PCF(ctx_id),
+ EASRC_PCF_CD(r[1]));
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_prefilter_config(struct fsl_asrc *easrc,
+ unsigned int ctx_id)
+{
+ struct prefil_params *prefil, *selected_prefil = NULL;
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_easrc_priv *easrc_priv;
+ struct asrc_firmware_hdr *hdr;
+ struct fsl_asrc_pair *ctx;
+ struct device *dev;
+ u32 inrate, outrate, offset = 0;
+ u32 in_s_rate, out_s_rate;
+ snd_pcm_format_t in_s_fmt, out_s_fmt;
+ int ret, i;
+
+ if (!easrc)
+ return -ENODEV;
+
+ dev = &easrc->pdev->dev;
+
+ if (ctx_id >= EASRC_CTX_MAX_NUM) {
+ dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+ return -EINVAL;
+ }
+
+ easrc_priv = easrc->private;
+
+ ctx = easrc->pair[ctx_id];
+ ctx_priv = ctx->private;
+
+ in_s_rate = ctx_priv->in_params.sample_rate;
+ out_s_rate = ctx_priv->out_params.sample_rate;
+ in_s_fmt = ctx_priv->in_params.sample_format;
+ out_s_fmt = ctx_priv->out_params.sample_format;
+
+ ctx_priv->in_filled_sample = bits_taps_to_val(easrc_priv->rs_num_taps) / 2;
+ ctx_priv->out_missed_sample = ctx_priv->in_filled_sample * out_s_rate / in_s_rate;
+
+ ctx_priv->st1_num_taps = 0;
+ ctx_priv->st2_num_taps = 0;
+
+ regmap_write(easrc->regmap, REG_EASRC_CCE1(ctx_id), 0);
+ regmap_write(easrc->regmap, REG_EASRC_CCE2(ctx_id), 0);
+
+ /*
+ * The audio float point data range is (-1, 1), the asrc would output
+ * all zero for float point input and integer output case, that is to
+ * drop the fractional part of the data directly.
+ *
+ * In order to support float to int conversion or int to float
+ * conversion we need to do special operation on the coefficient to
+ * enlarge/reduce the data to the expected range.
+ *
+ * For float to int case:
+ * Up sampling:
+ * 1. Create a 1 tap filter with center tap (only tap) of 2^31
+ * in 64 bits floating point.
+ * double value = (double)(((uint64_t)1) << 31)
+ * 2. Program 1 tap prefilter with center tap above.
+ *
+ * Down sampling,
+ * 1. If the filter is single stage filter, add "shift" to the exponent
+ * of stage 1 coefficients.
+ * 2. If the filter is two stage filter , add "shift" to the exponent
+ * of stage 2 coefficients.
+ *
+ * The "shift" is 31, same for int16, int24, int32 case.
+ *
+ * For int to float case:
+ * Up sampling:
+ * 1. Create a 1 tap filter with center tap (only tap) of 2^-31
+ * in 64 bits floating point.
+ * 2. Program 1 tap prefilter with center tap above.
+ *
+ * Down sampling,
+ * 1. If the filter is single stage filter, subtract "shift" to the
+ * exponent of stage 1 coefficients.
+ * 2. If the filter is two stage filter , subtract "shift" to the
+ * exponent of stage 2 coefficients.
+ *
+ * The "shift" is 15,23,31, different for int16, int24, int32 case.
+ *
+ */
+ if (out_s_rate >= in_s_rate) {
+ if (out_s_rate == in_s_rate)
+ regmap_update_bits(easrc->regmap,
+ REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_RS_BYPASS_MASK,
+ EASRC_CCE1_RS_BYPASS);
+
+ ctx_priv->st1_num_taps = 1;
+ ctx_priv->st1_coeff = &easrc_priv->const_coeff;
+ ctx_priv->st1_num_exp = 1;
+ ctx_priv->st2_num_taps = 0;
+
+ if (in_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE)
+ ctx_priv->st1_addexp = 31;
+ else if (in_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE)
+ ctx_priv->st1_addexp -= ctx_priv->in_params.fmt.addexp;
+ } else {
+ inrate = ctx_priv->in_params.norm_rate;
+ outrate = ctx_priv->out_params.norm_rate;
+
+ hdr = easrc_priv->firmware_hdr;
+ prefil = easrc_priv->prefil;
+
+ for (i = 0; i < hdr->prefil_scen; i++) {
+ if (inrate == prefil[i].insr &&
+ outrate == prefil[i].outsr) {
+ selected_prefil = &prefil[i];
+ dev_dbg(dev, "Selected prefilter: %u insr, %u outsr, %u st1_taps, %u st2_taps\n",
+ selected_prefil->insr,
+ selected_prefil->outsr,
+ selected_prefil->st1_taps,
+ selected_prefil->st2_taps);
+ break;
+ }
+ }
+
+ if (!selected_prefil) {
+ dev_err(dev, "Conversion from in ratio %u(%u) to out ratio %u(%u) is not supported\n",
+ in_s_rate, inrate,
+ out_s_rate, outrate);
+ return -EINVAL;
+ }
+
+ /*
+ * In prefilter coeff array, first st1_num_taps represent the
+ * stage1 prefilter coefficients followed by next st2_num_taps
+ * representing stage 2 coefficients
+ */
+ ctx_priv->st1_num_taps = selected_prefil->st1_taps;
+ ctx_priv->st1_coeff = selected_prefil->coeff;
+ ctx_priv->st1_num_exp = selected_prefil->st1_exp;
+
+ offset = ((selected_prefil->st1_taps + 1) / 2);
+ ctx_priv->st2_num_taps = selected_prefil->st2_taps;
+ ctx_priv->st2_coeff = selected_prefil->coeff + offset;
+
+ if (in_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE) {
+ /* only change stage2 coefficient for 2 stage case */
+ if (ctx_priv->st2_num_taps > 0)
+ ctx_priv->st2_addexp = 31;
+ else
+ ctx_priv->st1_addexp = 31;
+ } else if (in_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE) {
+ if (ctx_priv->st2_num_taps > 0)
+ ctx_priv->st2_addexp -= ctx_priv->in_params.fmt.addexp;
+ else
+ ctx_priv->st1_addexp -= ctx_priv->in_params.fmt.addexp;
+ }
+ }
+
+ ctx_priv->in_filled_sample += (ctx_priv->st1_num_taps / 2) * ctx_priv->st1_num_exp +
+ ctx_priv->st2_num_taps / 2;
+ ctx_priv->out_missed_sample = ctx_priv->in_filled_sample * out_s_rate / in_s_rate;
+
+ if (ctx_priv->in_filled_sample * out_s_rate % in_s_rate != 0)
+ ctx_priv->out_missed_sample += 1;
+ /*
+ * To modify the value of a prefilter coefficient, the user must
+ * perform a write to the register ASRC_PRE_COEFF_FIFOn[COEFF_DATA]
+ * while the respective context RUN_EN bit is set to 0b0
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+ EASRC_CC_EN_MASK, 0);
+
+ if (ctx_priv->st1_num_taps > EASRC_MAX_PF_TAPS) {
+ dev_err(dev, "ST1 taps [%d] mus be lower than %d\n",
+ ctx_priv->st1_num_taps, EASRC_MAX_PF_TAPS);
+ ret = -EINVAL;
+ goto ctx_error;
+ }
+
+ /* Update ctx ST1_NUM_TAPS in Context Control Extended 2 register */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE2(ctx_id),
+ EASRC_CCE2_ST1_TAPS_MASK,
+ EASRC_CCE2_ST1_TAPS(ctx_priv->st1_num_taps - 1));
+
+ /* Prefilter Coefficient Write Select to write in ST1 coeff */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_COEF_WS_MASK,
+ EASRC_PF_ST1_COEFF_WR << EASRC_CCE1_COEF_WS_SHIFT);
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, ctx_id,
+ ctx_priv->st1_coeff,
+ ctx_priv->st1_num_taps,
+ ctx_priv->st1_addexp);
+ if (ret)
+ goto ctx_error;
+
+ if (ctx_priv->st2_num_taps > 0) {
+ if (ctx_priv->st2_num_taps + ctx_priv->st1_num_taps > EASRC_MAX_PF_TAPS) {
+ dev_err(dev, "ST2 taps [%d] mus be lower than %d\n",
+ ctx_priv->st2_num_taps, EASRC_MAX_PF_TAPS);
+ ret = -EINVAL;
+ goto ctx_error;
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_TSEN_MASK,
+ EASRC_CCE1_PF_TSEN);
+ /*
+ * Enable prefilter stage1 writeback floating point
+ * which is used for FLOAT_LE case
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_ST1_WBFP_MASK,
+ EASRC_CCE1_PF_ST1_WBFP);
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_EXP_MASK,
+ EASRC_CCE1_PF_EXP(ctx_priv->st1_num_exp - 1));
+
+ /* Update ctx ST2_NUM_TAPS in Context Control Extended 2 reg */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE2(ctx_id),
+ EASRC_CCE2_ST2_TAPS_MASK,
+ EASRC_CCE2_ST2_TAPS(ctx_priv->st2_num_taps - 1));
+
+ /* Prefilter Coefficient Write Select to write in ST2 coeff */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_COEF_WS_MASK,
+ EASRC_PF_ST2_COEFF_WR << EASRC_CCE1_COEF_WS_SHIFT);
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, ctx_id,
+ ctx_priv->st2_coeff,
+ ctx_priv->st2_num_taps,
+ ctx_priv->st2_addexp);
+ if (ret)
+ goto ctx_error;
+ }
+
+ return 0;
+
+ctx_error:
+ return ret;
+}
+
+static int fsl_easrc_max_ch_for_slot(struct fsl_asrc_pair *ctx,
+ struct fsl_easrc_slot *slot)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ int st1_mem_alloc = 0, st2_mem_alloc = 0;
+ int pf_mem_alloc = 0;
+ int max_channels = 8 - slot->num_channel;
+ int channels = 0;
+
+ if (ctx_priv->st1_num_taps > 0) {
+ if (ctx_priv->st2_num_taps > 0)
+ st1_mem_alloc =
+ (ctx_priv->st1_num_taps - 1) * ctx_priv->st1_num_exp + 1;
+ else
+ st1_mem_alloc = ctx_priv->st1_num_taps;
+ }
+
+ if (ctx_priv->st2_num_taps > 0)
+ st2_mem_alloc = ctx_priv->st2_num_taps;
+
+ pf_mem_alloc = st1_mem_alloc + st2_mem_alloc;
+
+ if (pf_mem_alloc != 0)
+ channels = (6144 - slot->pf_mem_used) / pf_mem_alloc;
+ else
+ channels = 8;
+
+ if (channels < max_channels)
+ max_channels = channels;
+
+ return max_channels;
+}
+
+static int fsl_easrc_config_one_slot(struct fsl_asrc_pair *ctx,
+ struct fsl_easrc_slot *slot,
+ unsigned int slot_ctx_idx,
+ unsigned int *req_channels,
+ unsigned int *start_channel,
+ unsigned int *avail_channel)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ int st1_chanxexp, st1_mem_alloc = 0, st2_mem_alloc;
+ unsigned int reg0, reg1, reg2, reg3;
+ unsigned int addr;
+
+ if (slot->slot_index == 0) {
+ reg0 = REG_EASRC_DPCS0R0(slot_ctx_idx);
+ reg1 = REG_EASRC_DPCS0R1(slot_ctx_idx);
+ reg2 = REG_EASRC_DPCS0R2(slot_ctx_idx);
+ reg3 = REG_EASRC_DPCS0R3(slot_ctx_idx);
+ } else {
+ reg0 = REG_EASRC_DPCS1R0(slot_ctx_idx);
+ reg1 = REG_EASRC_DPCS1R1(slot_ctx_idx);
+ reg2 = REG_EASRC_DPCS1R2(slot_ctx_idx);
+ reg3 = REG_EASRC_DPCS1R3(slot_ctx_idx);
+ }
+
+ if (*req_channels <= *avail_channel) {
+ slot->num_channel = *req_channels;
+ *req_channels = 0;
+ } else {
+ slot->num_channel = *avail_channel;
+ *req_channels -= *avail_channel;
+ }
+
+ slot->min_channel = *start_channel;
+ slot->max_channel = *start_channel + slot->num_channel - 1;
+ slot->ctx_index = ctx->index;
+ slot->busy = true;
+ *start_channel += slot->num_channel;
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_MAXCH_MASK,
+ EASRC_DPCS0R0_MAXCH(slot->max_channel));
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_MINCH_MASK,
+ EASRC_DPCS0R0_MINCH(slot->min_channel));
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_NUMCH_MASK,
+ EASRC_DPCS0R0_NUMCH(slot->num_channel - 1));
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_CTXNUM_MASK,
+ EASRC_DPCS0R0_CTXNUM(slot->ctx_index));
+
+ if (ctx_priv->st1_num_taps > 0) {
+ if (ctx_priv->st2_num_taps > 0)
+ st1_mem_alloc =
+ (ctx_priv->st1_num_taps - 1) * slot->num_channel *
+ ctx_priv->st1_num_exp + slot->num_channel;
+ else
+ st1_mem_alloc = ctx_priv->st1_num_taps * slot->num_channel;
+
+ slot->pf_mem_used = st1_mem_alloc;
+ regmap_update_bits(easrc->regmap, reg2,
+ EASRC_DPCS0R2_ST1_MA_MASK,
+ EASRC_DPCS0R2_ST1_MA(st1_mem_alloc));
+
+ if (slot->slot_index == 1)
+ addr = PREFILTER_MEM_LEN - st1_mem_alloc;
+ else
+ addr = 0;
+
+ regmap_update_bits(easrc->regmap, reg2,
+ EASRC_DPCS0R2_ST1_SA_MASK,
+ EASRC_DPCS0R2_ST1_SA(addr));
+ }
+
+ if (ctx_priv->st2_num_taps > 0) {
+ st1_chanxexp = slot->num_channel * (ctx_priv->st1_num_exp - 1);
+
+ regmap_update_bits(easrc->regmap, reg1,
+ EASRC_DPCS0R1_ST1_EXP_MASK,
+ EASRC_DPCS0R1_ST1_EXP(st1_chanxexp));
+
+ st2_mem_alloc = slot->num_channel * ctx_priv->st2_num_taps;
+ slot->pf_mem_used += st2_mem_alloc;
+ regmap_update_bits(easrc->regmap, reg3,
+ EASRC_DPCS0R3_ST2_MA_MASK,
+ EASRC_DPCS0R3_ST2_MA(st2_mem_alloc));
+
+ if (slot->slot_index == 1)
+ addr = PREFILTER_MEM_LEN - st1_mem_alloc - st2_mem_alloc;
+ else
+ addr = st1_mem_alloc;
+
+ regmap_update_bits(easrc->regmap, reg3,
+ EASRC_DPCS0R3_ST2_SA_MASK,
+ EASRC_DPCS0R3_ST2_SA(addr));
+ }
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_EN_MASK, EASRC_DPCS0R0_EN);
+
+ return 0;
+}
+
+/*
+ * fsl_easrc_config_slot
+ *
+ * A single context can be split amongst any of the 4 context processing pipes
+ * in the design.
+ * The total number of channels consumed within the context processor must be
+ * less than or equal to 8. if a single context is configured to contain more
+ * than 8 channels then it must be distributed across multiple context
+ * processing pipe slots.
+ *
+ */
+static int fsl_easrc_config_slot(struct fsl_asrc *easrc, unsigned int ctx_id)
+{
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_asrc_pair *ctx = easrc->pair[ctx_id];
+ int req_channels = ctx->channels;
+ int start_channel = 0, avail_channel;
+ struct fsl_easrc_slot *slot0, *slot1;
+ struct fsl_easrc_slot *slota, *slotb;
+ int i, ret;
+
+ if (req_channels <= 0)
+ return -EINVAL;
+
+ for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
+ slot0 = &easrc_priv->slot[i][0];
+ slot1 = &easrc_priv->slot[i][1];
+
+ if (slot0->busy && slot1->busy) {
+ continue;
+ } else if ((slot0->busy && slot0->ctx_index == ctx->index) ||
+ (slot1->busy && slot1->ctx_index == ctx->index)) {
+ continue;
+ } else if (!slot0->busy) {
+ slota = slot0;
+ slotb = slot1;
+ slota->slot_index = 0;
+ } else if (!slot1->busy) {
+ slota = slot1;
+ slotb = slot0;
+ slota->slot_index = 1;
+ }
+
+ if (!slota || !slotb)
+ continue;
+
+ avail_channel = fsl_easrc_max_ch_for_slot(ctx, slotb);
+ if (avail_channel <= 0)
+ continue;
+
+ ret = fsl_easrc_config_one_slot(ctx, slota, i, &req_channels,
+ &start_channel, &avail_channel);
+ if (ret)
+ return ret;
+
+ if (req_channels > 0)
+ continue;
+ else
+ break;
+ }
+
+ if (req_channels > 0) {
+ dev_err(&easrc->pdev->dev, "no avail slot.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * fsl_easrc_release_slot
+ *
+ * Clear the slot configuration
+ */
+static int fsl_easrc_release_slot(struct fsl_asrc *easrc, unsigned int ctx_id)
+{
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_asrc_pair *ctx = easrc->pair[ctx_id];
+ int i;
+
+ for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
+ if (easrc_priv->slot[i][0].busy &&
+ easrc_priv->slot[i][0].ctx_index == ctx->index) {
+ easrc_priv->slot[i][0].busy = false;
+ easrc_priv->slot[i][0].num_channel = 0;
+ easrc_priv->slot[i][0].pf_mem_used = 0;
+ /* set registers */
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R0(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R1(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R2(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R3(i), 0);
+ }
+
+ if (easrc_priv->slot[i][1].busy &&
+ easrc_priv->slot[i][1].ctx_index == ctx->index) {
+ easrc_priv->slot[i][1].busy = false;
+ easrc_priv->slot[i][1].num_channel = 0;
+ easrc_priv->slot[i][1].pf_mem_used = 0;
+ /* set registers */
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R0(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R1(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R2(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R3(i), 0);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * fsl_easrc_config_context
+ *
+ * Configure the register relate with context.
+ */
+static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_asrc_pair *ctx;
+ struct device *dev;
+ unsigned long lock_flags;
+ int ret;
+
+ if (!easrc)
+ return -ENODEV;
+
+ dev = &easrc->pdev->dev;
+
+ if (ctx_id >= EASRC_CTX_MAX_NUM) {
+ dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+ return -EINVAL;
+ }
+
+ ctx = easrc->pair[ctx_id];
+
+ ctx_priv = ctx->private;
+
+ fsl_easrc_normalize_rates(ctx);
+
+ ret = fsl_easrc_set_rs_ratio(ctx);
+ if (ret)
+ return ret;
+
+ /* Initialize the context coeficients */
+ ret = fsl_easrc_prefilter_config(easrc, ctx->index);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+ ret = fsl_easrc_config_slot(easrc, ctx->index);
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+ if (ret)
+ return ret;
+
+ /*
+ * Both prefilter and resampling filters can use following
+ * initialization modes:
+ * 2 - zero-fil mode
+ * 1 - replication mode
+ * 0 - software control
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_RS_INIT_MASK,
+ EASRC_CCE1_RS_INIT(ctx_priv->rs_init_mode));
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_INIT_MASK,
+ EASRC_CCE1_PF_INIT(ctx_priv->pf_init_mode));
+
+ /*
+ * Context Input FIFO Watermark
+ * DMA request is generated when input FIFO < FIFO_WTMK
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+ EASRC_CC_FIFO_WTMK_MASK,
+ EASRC_CC_FIFO_WTMK(ctx_priv->in_params.fifo_wtmk));
+
+ /*
+ * Context Output FIFO Watermark
+ * DMA request is generated when output FIFO > FIFO_WTMK
+ * So we set fifo_wtmk -1 to register.
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx_id),
+ EASRC_COC_FIFO_WTMK_MASK,
+ EASRC_COC_FIFO_WTMK(ctx_priv->out_params.fifo_wtmk - 1));
+
+ /* Number of channels */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+ EASRC_CC_CHEN_MASK,
+ EASRC_CC_CHEN(ctx->channels - 1));
+ return 0;
+}
+
+static int fsl_easrc_process_format(struct fsl_asrc_pair *ctx,
+ struct fsl_easrc_data_fmt *fmt,
+ snd_pcm_format_t raw_fmt)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ int ret;
+
+ if (!fmt)
+ return -EINVAL;
+
+ /*
+ * Context Input Floating Point Format
+ * 0 - Integer Format
+ * 1 - Single Precision FP Format
+ */
+ fmt->floating_point = !snd_pcm_format_linear(raw_fmt);
+ fmt->sample_pos = 0;
+ fmt->iec958 = 0;
+
+ /* Get the data width */
+ switch (snd_pcm_format_width(raw_fmt)) {
+ case 16:
+ fmt->width = EASRC_WIDTH_16_BIT;
+ fmt->addexp = 15;
+ break;
+ case 20:
+ fmt->width = EASRC_WIDTH_20_BIT;
+ fmt->addexp = 19;
+ break;
+ case 24:
+ fmt->width = EASRC_WIDTH_24_BIT;
+ fmt->addexp = 23;
+ break;
+ case 32:
+ fmt->width = EASRC_WIDTH_32_BIT;
+ fmt->addexp = 31;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (raw_fmt) {
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ fmt->width = easrc_priv->bps_iec958[ctx->index];
+ fmt->iec958 = 1;
+ fmt->floating_point = 0;
+ if (fmt->width == EASRC_WIDTH_16_BIT) {
+ fmt->sample_pos = 12;
+ fmt->addexp = 15;
+ } else if (fmt->width == EASRC_WIDTH_20_BIT) {
+ fmt->sample_pos = 8;
+ fmt->addexp = 19;
+ } else if (fmt->width == EASRC_WIDTH_24_BIT) {
+ fmt->sample_pos = 4;
+ fmt->addexp = 23;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Data Endianness
+ * 0 - Little-Endian
+ * 1 - Big-Endian
+ */
+ ret = snd_pcm_format_big_endian(raw_fmt);
+ if (ret < 0)
+ return ret;
+
+ fmt->endianness = ret;
+
+ /*
+ * Input Data sign
+ * 0b - Signed Format
+ * 1b - Unsigned Format
+ */
+ fmt->unsign = snd_pcm_format_unsigned(raw_fmt) > 0 ? 1 : 0;
+
+ return 0;
+}
+
+static int fsl_easrc_set_ctx_format(struct fsl_asrc_pair *ctx,
+ snd_pcm_format_t *in_raw_format,
+ snd_pcm_format_t *out_raw_format)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ struct fsl_easrc_data_fmt *in_fmt = &ctx_priv->in_params.fmt;
+ struct fsl_easrc_data_fmt *out_fmt = &ctx_priv->out_params.fmt;
+ int ret = 0;
+
+ /* Get the bitfield values for input data format */
+ if (in_raw_format && out_raw_format) {
+ ret = fsl_easrc_process_format(ctx, in_fmt, *in_raw_format);
+ if (ret)
+ return ret;
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_BPS_MASK,
+ EASRC_CC_BPS(in_fmt->width));
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_ENDIANNESS_MASK,
+ in_fmt->endianness << EASRC_CC_ENDIANNESS_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_FMT_MASK,
+ in_fmt->floating_point << EASRC_CC_FMT_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_INSIGN_MASK,
+ in_fmt->unsign << EASRC_CC_INSIGN_SHIFT);
+
+ /* In Sample Position */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_SAMPLE_POS_MASK,
+ EASRC_CC_SAMPLE_POS(in_fmt->sample_pos));
+
+ /* Get the bitfield values for input data format */
+ if (in_raw_format && out_raw_format) {
+ ret = fsl_easrc_process_format(ctx, out_fmt, *out_raw_format);
+ if (ret)
+ return ret;
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_BPS_MASK,
+ EASRC_COC_BPS(out_fmt->width));
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_ENDIANNESS_MASK,
+ out_fmt->endianness << EASRC_COC_ENDIANNESS_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_FMT_MASK,
+ out_fmt->floating_point << EASRC_COC_FMT_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_OUTSIGN_MASK,
+ out_fmt->unsign << EASRC_COC_OUTSIGN_SHIFT);
+
+ /* Out Sample Position */
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_SAMPLE_POS_MASK,
+ EASRC_COC_SAMPLE_POS(out_fmt->sample_pos));
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_IEC_EN_MASK,
+ out_fmt->iec958 << EASRC_COC_IEC_EN_SHIFT);
+
+ return ret;
+}
+
+/*
+ * The ASRC provides interleaving support in hardware to ensure that a
+ * variety of sample sources can be internally combined
+ * to conform with this format. Interleaving parameters are accessed
+ * through the ASRC_CTRL_IN_ACCESSa and ASRC_CTRL_OUT_ACCESSa registers
+ */
+static int fsl_easrc_set_ctx_organziation(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_asrc *easrc;
+
+ if (!ctx)
+ return -ENODEV;
+
+ easrc = ctx->asrc;
+ ctx_priv = ctx->private;
+
+ /* input interleaving parameters */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+ EASRC_CIA_ITER_MASK,
+ EASRC_CIA_ITER(ctx_priv->in_params.iterations));
+ regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+ EASRC_CIA_GRLEN_MASK,
+ EASRC_CIA_GRLEN(ctx_priv->in_params.group_len));
+ regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+ EASRC_CIA_ACCLEN_MASK,
+ EASRC_CIA_ACCLEN(ctx_priv->in_params.access_len));
+
+ /* output interleaving parameters */
+ regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+ EASRC_COA_ITER_MASK,
+ EASRC_COA_ITER(ctx_priv->out_params.iterations));
+ regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+ EASRC_COA_GRLEN_MASK,
+ EASRC_COA_GRLEN(ctx_priv->out_params.group_len));
+ regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+ EASRC_COA_ACCLEN_MASK,
+ EASRC_COA_ACCLEN(ctx_priv->out_params.access_len));
+
+ return 0;
+}
+
+/*
+ * Request one of the available contexts
+ *
+ * Returns a negative number on error and >=0 as context id
+ * on success
+ */
+static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx)
+{
+ enum asrc_pair_index index = ASRC_INVALID_PAIR;
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct device *dev;
+ unsigned long lock_flags;
+ int ret = 0;
+ int i;
+
+ dev = &easrc->pdev->dev;
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+
+ for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
+ if (easrc->pair[i])
+ continue;
+
+ index = i;
+ break;
+ }
+
+ if (index == ASRC_INVALID_PAIR) {
+ dev_err(dev, "all contexts are busy\n");
+ ret = -EBUSY;
+ } else if (channels > easrc->channel_avail) {
+ dev_err(dev, "can't give the required channels: %d\n",
+ channels);
+ ret = -EINVAL;
+ } else {
+ ctx->index = index;
+ ctx->channels = channels;
+ easrc->pair[index] = ctx;
+ easrc->channel_avail -= channels;
+ }
+
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+ return ret;
+}
+
+/*
+ * Release the context
+ *
+ * This funciton is mainly doing the revert thing in request context
+ */
+static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx)
+{
+ unsigned long lock_flags;
+ struct fsl_asrc *easrc;
+
+ if (!ctx)
+ return;
+
+ easrc = ctx->asrc;
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+
+ fsl_easrc_release_slot(easrc, ctx->index);
+
+ easrc->channel_avail += ctx->channels;
+ easrc->pair[ctx->index] = NULL;
+
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+}
+
+/*
+ * Start the context
+ *
+ * Enable the DMA request and context
+ */
+static int fsl_easrc_start_context(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_FWMDE_MASK, EASRC_CC_FWMDE);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_FWMDE_MASK, EASRC_COC_FWMDE);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_EN_MASK, EASRC_CC_EN);
+ return 0;
+}
+
+/*
+ * Stop the context
+ *
+ * Disable the DMA request and context
+ */
+static int fsl_easrc_stop_context(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ int val, i;
+ int size;
+ int retry = 200;
+
+ regmap_read(easrc->regmap, REG_EASRC_CC(ctx->index), &val);
+
+ if (val & EASRC_CC_EN_MASK) {
+ regmap_update_bits(easrc->regmap,
+ REG_EASRC_CC(ctx->index),
+ EASRC_CC_STOP_MASK, EASRC_CC_STOP);
+ do {
+ regmap_read(easrc->regmap, REG_EASRC_SFS(ctx->index), &val);
+ val &= EASRC_SFS_NSGO_MASK;
+ size = val >> EASRC_SFS_NSGO_SHIFT;
+
+ /* Read FIFO, drop the data */
+ for (i = 0; i < size * ctx->channels; i++)
+ regmap_read(easrc->regmap, REG_EASRC_RDFIFO(ctx->index), &val);
+ /* Check RUN_STOP_DONE */
+ regmap_read(easrc->regmap, REG_EASRC_IRQF, &val);
+ if (val & EASRC_IRQF_RSD(1 << ctx->index)) {
+ /*Clear RUN_STOP_DONE*/
+ regmap_write_bits(easrc->regmap,
+ REG_EASRC_IRQF,
+ EASRC_IRQF_RSD(1 << ctx->index),
+ EASRC_IRQF_RSD(1 << ctx->index));
+ break;
+ }
+ udelay(100);
+ } while (--retry);
+
+ if (retry == 0)
+ dev_warn(&easrc->pdev->dev, "RUN STOP fail\n");
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_EN_MASK | EASRC_CC_STOP_MASK, 0);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_FWMDE_MASK, 0);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_FWMDE_MASK, 0);
+ return 0;
+}
+
+static struct dma_chan *fsl_easrc_get_dma_channel(struct fsl_asrc_pair *ctx,
+ bool dir)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ enum asrc_pair_index index = ctx->index;
+ char name[8];
+
+ /* Example of dma name: ctx0_rx */
+ sprintf(name, "ctx%c_%cx", index + '0', dir == IN ? 'r' : 't');
+
+ return dma_request_slave_channel(&easrc->pdev->dev, name);
+};
+
+static const unsigned int easrc_rates[] = {
+ 8000, 11025, 12000, 16000,
+ 22050, 24000, 32000, 44100,
+ 48000, 64000, 88200, 96000,
+ 128000, 176400, 192000, 256000,
+ 352800, 384000, 705600, 768000,
+};
+
+static const struct snd_pcm_hw_constraint_list easrc_rate_constraints = {
+ .count = ARRAY_SIZE(easrc_rates),
+ .list = easrc_rates,
+};
+
+static int fsl_easrc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &easrc_rate_constraints);
+}
+
+static int fsl_easrc_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *ctx = runtime->private_data;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = fsl_easrc_start_context(ctx);
+ if (ret)
+ return ret;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = fsl_easrc_stop_context(ctx);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *easrc = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = &easrc->pdev->dev;
+ struct fsl_asrc_pair *ctx = runtime->private_data;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ snd_pcm_format_t format = params_format(params);
+ int ret;
+
+ ret = fsl_easrc_request_context(channels, ctx);
+ if (ret) {
+ dev_err(dev, "failed to request context\n");
+ return ret;
+ }
+
+ ctx_priv->ctx_streams |= BIT(substream->stream);
+
+ /*
+ * Set the input and output ratio so we can compute
+ * the resampling ratio in RS_LOW/HIGH
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ctx_priv->in_params.sample_rate = rate;
+ ctx_priv->in_params.sample_format = format;
+ ctx_priv->out_params.sample_rate = easrc->asrc_rate;
+ ctx_priv->out_params.sample_format = easrc->asrc_format;
+ } else {
+ ctx_priv->out_params.sample_rate = rate;
+ ctx_priv->out_params.sample_format = format;
+ ctx_priv->in_params.sample_rate = easrc->asrc_rate;
+ ctx_priv->in_params.sample_format = easrc->asrc_format;
+ }
+
+ ctx->channels = channels;
+ ctx_priv->in_params.fifo_wtmk = 0x20;
+ ctx_priv->out_params.fifo_wtmk = 0x20;
+
+ /*
+ * Do only rate conversion and keep the same format for input
+ * and output data
+ */
+ ret = fsl_easrc_set_ctx_format(ctx,
+ &ctx_priv->in_params.sample_format,
+ &ctx_priv->out_params.sample_format);
+ if (ret) {
+ dev_err(dev, "failed to set format %d", ret);
+ return ret;
+ }
+
+ ret = fsl_easrc_config_context(easrc, ctx->index);
+ if (ret) {
+ dev_err(dev, "failed to config context\n");
+ return ret;
+ }
+
+ ctx_priv->in_params.iterations = 1;
+ ctx_priv->in_params.group_len = ctx->channels;
+ ctx_priv->in_params.access_len = ctx->channels;
+ ctx_priv->out_params.iterations = 1;
+ ctx_priv->out_params.group_len = ctx->channels;
+ ctx_priv->out_params.access_len = ctx->channels;
+
+ ret = fsl_easrc_set_ctx_organziation(ctx);
+ if (ret) {
+ dev_err(dev, "failed to set fifo organization\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *ctx = runtime->private_data;
+ struct fsl_easrc_ctx_priv *ctx_priv;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ctx_priv = ctx->private;
+
+ if (ctx_priv->ctx_streams & BIT(substream->stream)) {
+ ctx_priv->ctx_streams &= ~BIT(substream->stream);
+ fsl_easrc_release_context(ctx);
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(cpu_dai->dev);
+
+ snd_soc_dai_init_dma_data(cpu_dai,
+ &easrc->dma_params_tx,
+ &easrc->dma_params_rx);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_easrc_dai_ops = {
+ .probe = fsl_easrc_dai_probe,
+ .startup = fsl_easrc_startup,
+ .trigger = fsl_easrc_trigger,
+ .hw_params = fsl_easrc_hw_params,
+ .hw_free = fsl_easrc_hw_free,
+};
+
+static struct snd_soc_dai_driver fsl_easrc_dai = {
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_easrc_component = {
+ .name = "fsl-easrc-dai",
+ .controls = fsl_easrc_snd_controls,
+ .num_controls = ARRAY_SIZE(fsl_easrc_snd_controls),
+ .legacy_dai_naming = 1,
+};
+
+static const struct reg_default fsl_easrc_reg_defaults[] = {
+ {REG_EASRC_WRFIFO(0), 0x00000000},
+ {REG_EASRC_WRFIFO(1), 0x00000000},
+ {REG_EASRC_WRFIFO(2), 0x00000000},
+ {REG_EASRC_WRFIFO(3), 0x00000000},
+ {REG_EASRC_RDFIFO(0), 0x00000000},
+ {REG_EASRC_RDFIFO(1), 0x00000000},
+ {REG_EASRC_RDFIFO(2), 0x00000000},
+ {REG_EASRC_RDFIFO(3), 0x00000000},
+ {REG_EASRC_CC(0), 0x00000000},
+ {REG_EASRC_CC(1), 0x00000000},
+ {REG_EASRC_CC(2), 0x00000000},
+ {REG_EASRC_CC(3), 0x00000000},
+ {REG_EASRC_CCE1(0), 0x00000000},
+ {REG_EASRC_CCE1(1), 0x00000000},
+ {REG_EASRC_CCE1(2), 0x00000000},
+ {REG_EASRC_CCE1(3), 0x00000000},
+ {REG_EASRC_CCE2(0), 0x00000000},
+ {REG_EASRC_CCE2(1), 0x00000000},
+ {REG_EASRC_CCE2(2), 0x00000000},
+ {REG_EASRC_CCE2(3), 0x00000000},
+ {REG_EASRC_CIA(0), 0x00000000},
+ {REG_EASRC_CIA(1), 0x00000000},
+ {REG_EASRC_CIA(2), 0x00000000},
+ {REG_EASRC_CIA(3), 0x00000000},
+ {REG_EASRC_DPCS0R0(0), 0x00000000},
+ {REG_EASRC_DPCS0R0(1), 0x00000000},
+ {REG_EASRC_DPCS0R0(2), 0x00000000},
+ {REG_EASRC_DPCS0R0(3), 0x00000000},
+ {REG_EASRC_DPCS0R1(0), 0x00000000},
+ {REG_EASRC_DPCS0R1(1), 0x00000000},
+ {REG_EASRC_DPCS0R1(2), 0x00000000},
+ {REG_EASRC_DPCS0R1(3), 0x00000000},
+ {REG_EASRC_DPCS0R2(0), 0x00000000},
+ {REG_EASRC_DPCS0R2(1), 0x00000000},
+ {REG_EASRC_DPCS0R2(2), 0x00000000},
+ {REG_EASRC_DPCS0R2(3), 0x00000000},
+ {REG_EASRC_DPCS0R3(0), 0x00000000},
+ {REG_EASRC_DPCS0R3(1), 0x00000000},
+ {REG_EASRC_DPCS0R3(2), 0x00000000},
+ {REG_EASRC_DPCS0R3(3), 0x00000000},
+ {REG_EASRC_DPCS1R0(0), 0x00000000},
+ {REG_EASRC_DPCS1R0(1), 0x00000000},
+ {REG_EASRC_DPCS1R0(2), 0x00000000},
+ {REG_EASRC_DPCS1R0(3), 0x00000000},
+ {REG_EASRC_DPCS1R1(0), 0x00000000},
+ {REG_EASRC_DPCS1R1(1), 0x00000000},
+ {REG_EASRC_DPCS1R1(2), 0x00000000},
+ {REG_EASRC_DPCS1R1(3), 0x00000000},
+ {REG_EASRC_DPCS1R2(0), 0x00000000},
+ {REG_EASRC_DPCS1R2(1), 0x00000000},
+ {REG_EASRC_DPCS1R2(2), 0x00000000},
+ {REG_EASRC_DPCS1R2(3), 0x00000000},
+ {REG_EASRC_DPCS1R3(0), 0x00000000},
+ {REG_EASRC_DPCS1R3(1), 0x00000000},
+ {REG_EASRC_DPCS1R3(2), 0x00000000},
+ {REG_EASRC_DPCS1R3(3), 0x00000000},
+ {REG_EASRC_COC(0), 0x00000000},
+ {REG_EASRC_COC(1), 0x00000000},
+ {REG_EASRC_COC(2), 0x00000000},
+ {REG_EASRC_COC(3), 0x00000000},
+ {REG_EASRC_COA(0), 0x00000000},
+ {REG_EASRC_COA(1), 0x00000000},
+ {REG_EASRC_COA(2), 0x00000000},
+ {REG_EASRC_COA(3), 0x00000000},
+ {REG_EASRC_SFS(0), 0x00000000},
+ {REG_EASRC_SFS(1), 0x00000000},
+ {REG_EASRC_SFS(2), 0x00000000},
+ {REG_EASRC_SFS(3), 0x00000000},
+ {REG_EASRC_RRL(0), 0x00000000},
+ {REG_EASRC_RRL(1), 0x00000000},
+ {REG_EASRC_RRL(2), 0x00000000},
+ {REG_EASRC_RRL(3), 0x00000000},
+ {REG_EASRC_RRH(0), 0x00000000},
+ {REG_EASRC_RRH(1), 0x00000000},
+ {REG_EASRC_RRH(2), 0x00000000},
+ {REG_EASRC_RRH(3), 0x00000000},
+ {REG_EASRC_RUC(0), 0x00000000},
+ {REG_EASRC_RUC(1), 0x00000000},
+ {REG_EASRC_RUC(2), 0x00000000},
+ {REG_EASRC_RUC(3), 0x00000000},
+ {REG_EASRC_RUR(0), 0x7FFFFFFF},
+ {REG_EASRC_RUR(1), 0x7FFFFFFF},
+ {REG_EASRC_RUR(2), 0x7FFFFFFF},
+ {REG_EASRC_RUR(3), 0x7FFFFFFF},
+ {REG_EASRC_RCTCL, 0x00000000},
+ {REG_EASRC_RCTCH, 0x00000000},
+ {REG_EASRC_PCF(0), 0x00000000},
+ {REG_EASRC_PCF(1), 0x00000000},
+ {REG_EASRC_PCF(2), 0x00000000},
+ {REG_EASRC_PCF(3), 0x00000000},
+ {REG_EASRC_CRCM, 0x00000000},
+ {REG_EASRC_CRCC, 0x00000000},
+ {REG_EASRC_IRQC, 0x00000FFF},
+ {REG_EASRC_IRQF, 0x00000000},
+ {REG_EASRC_CS0(0), 0x00000000},
+ {REG_EASRC_CS0(1), 0x00000000},
+ {REG_EASRC_CS0(2), 0x00000000},
+ {REG_EASRC_CS0(3), 0x00000000},
+ {REG_EASRC_CS1(0), 0x00000000},
+ {REG_EASRC_CS1(1), 0x00000000},
+ {REG_EASRC_CS1(2), 0x00000000},
+ {REG_EASRC_CS1(3), 0x00000000},
+ {REG_EASRC_CS2(0), 0x00000000},
+ {REG_EASRC_CS2(1), 0x00000000},
+ {REG_EASRC_CS2(2), 0x00000000},
+ {REG_EASRC_CS2(3), 0x00000000},
+ {REG_EASRC_CS3(0), 0x00000000},
+ {REG_EASRC_CS3(1), 0x00000000},
+ {REG_EASRC_CS3(2), 0x00000000},
+ {REG_EASRC_CS3(3), 0x00000000},
+ {REG_EASRC_CS4(0), 0x00000000},
+ {REG_EASRC_CS4(1), 0x00000000},
+ {REG_EASRC_CS4(2), 0x00000000},
+ {REG_EASRC_CS4(3), 0x00000000},
+ {REG_EASRC_CS5(0), 0x00000000},
+ {REG_EASRC_CS5(1), 0x00000000},
+ {REG_EASRC_CS5(2), 0x00000000},
+ {REG_EASRC_CS5(3), 0x00000000},
+ {REG_EASRC_DBGC, 0x00000000},
+ {REG_EASRC_DBGS, 0x00000000},
+};
+
+static const struct regmap_range fsl_easrc_readable_ranges[] = {
+ regmap_reg_range(REG_EASRC_RDFIFO(0), REG_EASRC_RCTCH),
+ regmap_reg_range(REG_EASRC_PCF(0), REG_EASRC_PCF(3)),
+ regmap_reg_range(REG_EASRC_CRCC, REG_EASRC_DBGS),
+};
+
+static const struct regmap_access_table fsl_easrc_readable_table = {
+ .yes_ranges = fsl_easrc_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fsl_easrc_readable_ranges),
+};
+
+static const struct regmap_range fsl_easrc_writeable_ranges[] = {
+ regmap_reg_range(REG_EASRC_WRFIFO(0), REG_EASRC_WRFIFO(3)),
+ regmap_reg_range(REG_EASRC_CC(0), REG_EASRC_COA(3)),
+ regmap_reg_range(REG_EASRC_RRL(0), REG_EASRC_RCTCH),
+ regmap_reg_range(REG_EASRC_PCF(0), REG_EASRC_DBGC),
+};
+
+static const struct regmap_access_table fsl_easrc_writeable_table = {
+ .yes_ranges = fsl_easrc_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fsl_easrc_writeable_ranges),
+};
+
+static const struct regmap_range fsl_easrc_volatileable_ranges[] = {
+ regmap_reg_range(REG_EASRC_RDFIFO(0), REG_EASRC_RDFIFO(3)),
+ regmap_reg_range(REG_EASRC_SFS(0), REG_EASRC_SFS(3)),
+ regmap_reg_range(REG_EASRC_IRQF, REG_EASRC_IRQF),
+ regmap_reg_range(REG_EASRC_DBGS, REG_EASRC_DBGS),
+};
+
+static const struct regmap_access_table fsl_easrc_volatileable_table = {
+ .yes_ranges = fsl_easrc_volatileable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fsl_easrc_volatileable_ranges),
+};
+
+static const struct regmap_config fsl_easrc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_EASRC_DBGS,
+ .reg_defaults = fsl_easrc_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_easrc_reg_defaults),
+ .rd_table = &fsl_easrc_readable_table,
+ .wr_table = &fsl_easrc_writeable_table,
+ .volatile_table = &fsl_easrc_volatileable_table,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+#ifdef DEBUG
+static void fsl_easrc_dump_firmware(struct fsl_asrc *easrc)
+{
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct asrc_firmware_hdr *firm = easrc_priv->firmware_hdr;
+ struct interp_params *interp = easrc_priv->interp;
+ struct prefil_params *prefil = easrc_priv->prefil;
+ struct device *dev = &easrc->pdev->dev;
+ int i;
+
+ if (firm->magic != FIRMWARE_MAGIC) {
+ dev_err(dev, "Wrong magic. Something went wrong!");
+ return;
+ }
+
+ dev_dbg(dev, "Firmware v%u dump:\n", firm->firmware_version);
+ dev_dbg(dev, "Num prefilter scenarios: %u\n", firm->prefil_scen);
+ dev_dbg(dev, "Num interpolation scenarios: %u\n", firm->interp_scen);
+ dev_dbg(dev, "\nInterpolation scenarios:\n");
+
+ for (i = 0; i < firm->interp_scen; i++) {
+ if (interp[i].magic != FIRMWARE_MAGIC) {
+ dev_dbg(dev, "%d. wrong interp magic: %x\n",
+ i, interp[i].magic);
+ continue;
+ }
+ dev_dbg(dev, "%d. taps: %u, phases: %u, center: %llu\n", i,
+ interp[i].num_taps, interp[i].num_phases,
+ interp[i].center_tap);
+ }
+
+ for (i = 0; i < firm->prefil_scen; i++) {
+ if (prefil[i].magic != FIRMWARE_MAGIC) {
+ dev_dbg(dev, "%d. wrong prefil magic: %x\n",
+ i, prefil[i].magic);
+ continue;
+ }
+ dev_dbg(dev, "%d. insr: %u, outsr: %u, st1: %u, st2: %u\n", i,
+ prefil[i].insr, prefil[i].outsr,
+ prefil[i].st1_taps, prefil[i].st2_taps);
+ }
+
+ dev_dbg(dev, "end of firmware dump\n");
+}
+#endif
+
+static int fsl_easrc_get_firmware(struct fsl_asrc *easrc)
+{
+ struct fsl_easrc_priv *easrc_priv;
+ const struct firmware **fw_p;
+ u32 pnum, inum, offset;
+ const u8 *data;
+ int ret;
+
+ if (!easrc)
+ return -EINVAL;
+
+ easrc_priv = easrc->private;
+ fw_p = &easrc_priv->fw;
+
+ ret = request_firmware(fw_p, easrc_priv->fw_name, &easrc->pdev->dev);
+ if (ret)
+ return ret;
+
+ data = easrc_priv->fw->data;
+
+ easrc_priv->firmware_hdr = (struct asrc_firmware_hdr *)data;
+ pnum = easrc_priv->firmware_hdr->prefil_scen;
+ inum = easrc_priv->firmware_hdr->interp_scen;
+
+ if (inum) {
+ offset = sizeof(struct asrc_firmware_hdr);
+ easrc_priv->interp = (struct interp_params *)(data + offset);
+ }
+
+ if (pnum) {
+ offset = sizeof(struct asrc_firmware_hdr) +
+ inum * sizeof(struct interp_params);
+ easrc_priv->prefil = (struct prefil_params *)(data + offset);
+ }
+
+#ifdef DEBUG
+ fsl_easrc_dump_firmware(easrc);
+#endif
+
+ return 0;
+}
+
+static irqreturn_t fsl_easrc_isr(int irq, void *dev_id)
+{
+ struct fsl_asrc *easrc = (struct fsl_asrc *)dev_id;
+ struct device *dev = &easrc->pdev->dev;
+ int val;
+
+ regmap_read(easrc->regmap, REG_EASRC_IRQF, &val);
+
+ if (val & EASRC_IRQF_OER_MASK)
+ dev_dbg(dev, "output FIFO underflow\n");
+
+ if (val & EASRC_IRQF_IFO_MASK)
+ dev_dbg(dev, "input FIFO overflow\n");
+
+ return IRQ_HANDLED;
+}
+
+static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
+{
+ return REG_EASRC_FIFO(dir, index);
+}
+
+/* Get sample numbers in FIFO */
+static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+
+ regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val);
+ val &= EASRC_SFS_NSGO_MASK;
+
+ return val >> EASRC_SFS_NSGO_SHIFT;
+}
+
+static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ int ret;
+
+ ctx_priv->in_params.sample_rate = pair->rate[IN];
+ ctx_priv->in_params.sample_format = pair->sample_format[IN];
+ ctx_priv->out_params.sample_rate = pair->rate[OUT];
+ ctx_priv->out_params.sample_format = pair->sample_format[OUT];
+
+ ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
+ ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
+ /* Fill the right half of the re-sampler with zeros */
+ ctx_priv->rs_init_mode = 0x2;
+ /* Zero fill the right half of the prefilter */
+ ctx_priv->pf_init_mode = 0x2;
+
+ ret = fsl_easrc_set_ctx_format(pair,
+ &ctx_priv->in_params.sample_format,
+ &ctx_priv->out_params.sample_format);
+ if (ret) {
+ dev_err(dev, "failed to set context format: %d\n", ret);
+ return ret;
+ }
+
+ ret = fsl_easrc_config_context(asrc, pair->index);
+ if (ret) {
+ dev_err(dev, "failed to config context %d\n", ret);
+ return ret;
+ }
+
+ ctx_priv->in_params.iterations = 1;
+ ctx_priv->in_params.group_len = pair->channels;
+ ctx_priv->in_params.access_len = pair->channels;
+ ctx_priv->out_params.iterations = 1;
+ ctx_priv->out_params.group_len = pair->channels;
+ ctx_priv->out_params.access_len = pair->channels;
+
+ ret = fsl_easrc_set_ctx_organziation(pair);
+ if (ret) {
+ dev_err(dev, "failed to set fifo organization\n");
+ return ret;
+ }
+
+ /* The context start flag */
+ pair->first_convert = 1;
+ return 0;
+}
+
+static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair)
+{
+ /* start context once */
+ if (pair->first_convert) {
+ fsl_easrc_start_context(pair);
+ pair->first_convert = 0;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair)
+{
+ /* Stop pair/context */
+ if (!pair->first_convert) {
+ fsl_easrc_stop_context(pair);
+ pair->first_convert = 1;
+ }
+
+ return 0;
+}
+
+/* calculate capture data length according to output data length and sample rate */
+static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
+{
+ struct fsl_asrc *easrc = pair->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ unsigned int in_rate = ctx_priv->in_params.norm_rate;
+ unsigned int out_rate = ctx_priv->out_params.norm_rate;
+ unsigned int channels = pair->channels;
+ unsigned int in_samples, out_samples;
+ unsigned int in_width, out_width;
+ unsigned int out_length;
+ unsigned int frac_bits;
+ u64 val1, val2;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val1 = (u64)in_rate << frac_bits;
+ do_div(val1, out_rate);
+ val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31);
+
+ in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8;
+ out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8;
+
+ ctx_priv->in_filled_len += input_buffer_length;
+ if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) {
+ out_length = 0;
+ } else {
+ in_samples = ctx_priv->in_filled_len / (in_width * channels) -
+ ctx_priv->in_filled_sample;
+
+ /* right shift 12 bit to make ratio in 32bit space */
+ val2 = (u64)in_samples << (frac_bits - 12);
+ val1 = val1 >> 12;
+ do_div(val2, val1);
+ out_samples = val2;
+
+ out_length = out_samples * out_width * channels;
+ ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels;
+ }
+
+ return out_length;
+}
+
+static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+
+ if (dir == IN)
+ return ctx_priv->in_params.fifo_wtmk * pair->channels;
+ else
+ return ctx_priv->out_params.fifo_wtmk * pair->channels;
+}
+
+static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair)
+{
+ fsl_easrc_stop_context(pair);
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+
+ pair->first_convert = 1;
+ ctx_priv->in_filled_len = 0;
+
+ return 0;
+}
+
+/* val is Q31 */
+static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ struct fsl_asrc *easrc = pair->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ unsigned int frac_bits;
+
+ ctx_priv->ratio_mod += val;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= (frac_bits - 31);
+ regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val));
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
+{
+ cap->fmt_in = FSL_EASRC_FORMATS;
+ cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ cap->rate_in = easrc_rates;
+ cap->rate_in_count = ARRAY_SIZE(easrc_rates);
+ cap->rate_out = easrc_rates;
+ cap->rate_out_count = ARRAY_SIZE(easrc_rates);
+ cap->chan_min = 1;
+ cap->chan_max = 32;
+ return 0;
+}
+
+static const struct of_device_id fsl_easrc_dt_ids[] = {
+ { .compatible = "fsl,imx8mn-easrc",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_easrc_dt_ids);
+
+static int fsl_easrc_probe(struct platform_device *pdev)
+{
+ struct fsl_easrc_priv *easrc_priv;
+ struct device *dev = &pdev->dev;
+ struct fsl_asrc *easrc;
+ struct resource *res;
+ struct device_node *np;
+ void __iomem *regs;
+ u32 asrc_fmt = 0;
+ int ret, irq;
+
+ easrc = devm_kzalloc(dev, sizeof(*easrc), GFP_KERNEL);
+ if (!easrc)
+ return -ENOMEM;
+
+ easrc_priv = devm_kzalloc(dev, sizeof(*easrc_priv), GFP_KERNEL);
+ if (!easrc_priv)
+ return -ENOMEM;
+
+ easrc->pdev = pdev;
+ easrc->private = easrc_priv;
+ np = dev->of_node;
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ easrc->paddr = res->start;
+
+ easrc->regmap = devm_regmap_init_mmio(dev, regs, &fsl_easrc_regmap_config);
+ if (IS_ERR(easrc->regmap)) {
+ dev_err(dev, "failed to init regmap");
+ return PTR_ERR(easrc->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(&pdev->dev, irq, fsl_easrc_isr, 0,
+ dev_name(dev), easrc);
+ if (ret) {
+ dev_err(dev, "failed to claim irq %u: %d\n", irq, ret);
+ return ret;
+ }
+
+ easrc->mem_clk = devm_clk_get(dev, "mem");
+ if (IS_ERR(easrc->mem_clk)) {
+ dev_err(dev, "failed to get mem clock\n");
+ return PTR_ERR(easrc->mem_clk);
+ }
+
+ /* Set default value */
+ easrc->channel_avail = 32;
+ easrc->get_dma_channel = fsl_easrc_get_dma_channel;
+ easrc->request_pair = fsl_easrc_request_context;
+ easrc->release_pair = fsl_easrc_release_context;
+ easrc->get_fifo_addr = fsl_easrc_get_fifo_addr;
+ easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv);
+ easrc->m2m_prepare = fsl_easrc_m2m_prepare;
+ easrc->m2m_start = fsl_easrc_m2m_start;
+ easrc->m2m_stop = fsl_easrc_m2m_stop;
+ easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size;
+ easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len;
+ easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst;
+ easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend;
+ easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume;
+ easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod;
+ easrc->m2m_get_cap = fsl_easrc_m2m_get_cap;
+
+ easrc_priv->rs_num_taps = EASRC_RS_32_TAPS;
+ easrc_priv->const_coeff = 0x3FF0000000000000;
+
+ ret = of_property_read_u32(np, "fsl,asrc-rate", &easrc->asrc_rate);
+ if (ret) {
+ dev_err(dev, "failed to asrc rate\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
+ easrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
+ if (ret) {
+ dev_err(dev, "failed to asrc format\n");
+ return ret;
+ }
+
+ if (!(FSL_EASRC_FORMATS & (pcm_format_to_bits(easrc->asrc_format)))) {
+ dev_warn(dev, "unsupported format, switching to S24_LE\n");
+ easrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ }
+
+ ret = of_property_read_string(np, "firmware-name",
+ &easrc_priv->fw_name);
+ if (ret) {
+ dev_err(dev, "failed to get firmware name\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, easrc);
+ pm_runtime_enable(dev);
+
+ spin_lock_init(&easrc->lock);
+
+ regcache_cache_only(easrc->regmap, true);
+
+ ret = devm_snd_soc_register_component(dev, &fsl_easrc_component,
+ &fsl_easrc_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register ASoC DAI\n");
+ goto err_pm_disable;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &fsl_asrc_component,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC platform\n");
+ goto err_pm_disable;
+ }
+
+ ret = fsl_asrc_m2m_init(easrc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void fsl_easrc_remove(struct platform_device *pdev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev);
+
+ fsl_asrc_m2m_exit(easrc);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int fsl_easrc_runtime_suspend(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ unsigned long lock_flags;
+
+ regcache_cache_only(easrc->regmap, true);
+
+ clk_disable_unprepare(easrc->mem_clk);
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+ easrc_priv->firmware_loaded = 0;
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+ return 0;
+}
+
+static int fsl_easrc_runtime_resume(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_asrc_pair *ctx;
+ unsigned long lock_flags;
+ int ret;
+ int i;
+
+ ret = clk_prepare_enable(easrc->mem_clk);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(easrc->regmap, false);
+ regcache_mark_dirty(easrc->regmap);
+ regcache_sync(easrc->regmap);
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+ if (easrc_priv->firmware_loaded) {
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+ goto skip_load;
+ }
+ easrc_priv->firmware_loaded = 1;
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+ ret = fsl_easrc_get_firmware(easrc);
+ if (ret) {
+ dev_err(dev, "failed to get firmware\n");
+ goto disable_mem_clk;
+ }
+
+ /*
+ * Write Resampling Coefficients
+ * The coefficient RAM must be configured prior to beginning of
+ * any context processing within the ASRC
+ */
+ ret = fsl_easrc_resampler_config(easrc);
+ if (ret) {
+ dev_err(dev, "resampler config failed\n");
+ goto disable_mem_clk;
+ }
+
+ for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
+ ctx = easrc->pair[i];
+ if (!ctx)
+ continue;
+
+ ctx_priv = ctx->private;
+ fsl_easrc_set_rs_ratio(ctx);
+ ctx_priv->out_missed_sample = ctx_priv->in_filled_sample *
+ ctx_priv->out_params.sample_rate /
+ ctx_priv->in_params.sample_rate;
+ if (ctx_priv->in_filled_sample * ctx_priv->out_params.sample_rate
+ % ctx_priv->in_params.sample_rate != 0)
+ ctx_priv->out_missed_sample += 1;
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, i,
+ ctx_priv->st1_coeff,
+ ctx_priv->st1_num_taps,
+ ctx_priv->st1_addexp);
+ if (ret)
+ goto disable_mem_clk;
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, i,
+ ctx_priv->st2_coeff,
+ ctx_priv->st2_num_taps,
+ ctx_priv->st2_addexp);
+ if (ret)
+ goto disable_mem_clk;
+ }
+
+skip_load:
+ return 0;
+
+disable_mem_clk:
+ clk_disable_unprepare(easrc->mem_clk);
+ return ret;
+}
+
+static int fsl_easrc_suspend(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ int ret;
+
+ fsl_asrc_m2m_suspend(easrc);
+ ret = pm_runtime_force_suspend(dev);
+ return ret;
+}
+
+static int fsl_easrc_resume(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ fsl_asrc_m2m_resume(easrc);
+ return ret;
+}
+
+static const struct dev_pm_ops fsl_easrc_pm_ops = {
+ RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume)
+};
+
+static struct platform_driver fsl_easrc_driver = {
+ .probe = fsl_easrc_probe,
+ .remove = fsl_easrc_remove,
+ .driver = {
+ .name = "fsl-easrc",
+ .pm = pm_ptr(&fsl_easrc_pm_ops),
+ .of_match_table = fsl_easrc_dt_ids,
+ },
+};
+module_platform_driver(fsl_easrc_driver);
+
+MODULE_DESCRIPTION("NXP Enhanced Asynchronous Sample Rate (eASRC) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h
new file mode 100644
index 000000000000..c9f770862662
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc.h
@@ -0,0 +1,655 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 NXP
+ */
+
+#ifndef _FSL_EASRC_H
+#define _FSL_EASRC_H
+
+#include <sound/asound.h>
+#include <linux/dma/imx-dma.h>
+
+#include "fsl_asrc_common.h"
+
+/* EASRC Register Map */
+
+/* ASRC Input Write FIFO */
+#define REG_EASRC_WRFIFO(ctx) (0x000 + 4 * (ctx))
+/* ASRC Output Read FIFO */
+#define REG_EASRC_RDFIFO(ctx) (0x010 + 4 * (ctx))
+/* ASRC Context Control */
+#define REG_EASRC_CC(ctx) (0x020 + 4 * (ctx))
+/* ASRC Context Control Extended 1 */
+#define REG_EASRC_CCE1(ctx) (0x030 + 4 * (ctx))
+/* ASRC Context Control Extended 2 */
+#define REG_EASRC_CCE2(ctx) (0x040 + 4 * (ctx))
+/* ASRC Control Input Access */
+#define REG_EASRC_CIA(ctx) (0x050 + 4 * (ctx))
+/* ASRC Datapath Processor Control Slot0 */
+#define REG_EASRC_DPCS0R0(ctx) (0x060 + 4 * (ctx))
+#define REG_EASRC_DPCS0R1(ctx) (0x070 + 4 * (ctx))
+#define REG_EASRC_DPCS0R2(ctx) (0x080 + 4 * (ctx))
+#define REG_EASRC_DPCS0R3(ctx) (0x090 + 4 * (ctx))
+/* ASRC Datapath Processor Control Slot1 */
+#define REG_EASRC_DPCS1R0(ctx) (0x0A0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R1(ctx) (0x0B0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R2(ctx) (0x0C0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R3(ctx) (0x0D0 + 4 * (ctx))
+/* ASRC Context Output Control */
+#define REG_EASRC_COC(ctx) (0x0E0 + 4 * (ctx))
+/* ASRC Control Output Access */
+#define REG_EASRC_COA(ctx) (0x0F0 + 4 * (ctx))
+/* ASRC Sample FIFO Status */
+#define REG_EASRC_SFS(ctx) (0x100 + 4 * (ctx))
+/* ASRC Resampling Ratio Low */
+#define REG_EASRC_RRL(ctx) (0x110 + 8 * (ctx))
+/* ASRC Resampling Ratio High */
+#define REG_EASRC_RRH(ctx) (0x114 + 8 * (ctx))
+/* ASRC Resampling Ratio Update Control */
+#define REG_EASRC_RUC(ctx) (0x130 + 4 * (ctx))
+/* ASRC Resampling Ratio Update Rate */
+#define REG_EASRC_RUR(ctx) (0x140 + 4 * (ctx))
+/* ASRC Resampling Center Tap Coefficient Low */
+#define REG_EASRC_RCTCL (0x150)
+/* ASRC Resampling Center Tap Coefficient High */
+#define REG_EASRC_RCTCH (0x154)
+/* ASRC Prefilter Coefficient FIFO */
+#define REG_EASRC_PCF(ctx) (0x160 + 4 * (ctx))
+/* ASRC Context Resampling Coefficient Memory */
+#define REG_EASRC_CRCM 0x170
+/* ASRC Context Resampling Coefficient Control*/
+#define REG_EASRC_CRCC 0x174
+/* ASRC Interrupt Control */
+#define REG_EASRC_IRQC 0x178
+/* ASRC Interrupt Status Flags */
+#define REG_EASRC_IRQF 0x17C
+/* ASRC Channel Status 0 */
+#define REG_EASRC_CS0(ctx) (0x180 + 4 * (ctx))
+/* ASRC Channel Status 1 */
+#define REG_EASRC_CS1(ctx) (0x190 + 4 * (ctx))
+/* ASRC Channel Status 2 */
+#define REG_EASRC_CS2(ctx) (0x1A0 + 4 * (ctx))
+/* ASRC Channel Status 3 */
+#define REG_EASRC_CS3(ctx) (0x1B0 + 4 * (ctx))
+/* ASRC Channel Status 4 */
+#define REG_EASRC_CS4(ctx) (0x1C0 + 4 * (ctx))
+/* ASRC Channel Status 5 */
+#define REG_EASRC_CS5(ctx) (0x1D0 + 4 * (ctx))
+/* ASRC Debug Control Register */
+#define REG_EASRC_DBGC 0x1E0
+/* ASRC Debug Status Register */
+#define REG_EASRC_DBGS 0x1E4
+
+#define REG_EASRC_FIFO(x, ctx) (x == IN ? REG_EASRC_WRFIFO(ctx) \
+ : REG_EASRC_RDFIFO(ctx))
+
+/* ASRC Context Control (CC) */
+#define EASRC_CC_EN_SHIFT 31
+#define EASRC_CC_EN_MASK BIT(EASRC_CC_EN_SHIFT)
+#define EASRC_CC_EN BIT(EASRC_CC_EN_SHIFT)
+#define EASRC_CC_STOP_SHIFT 29
+#define EASRC_CC_STOP_MASK BIT(EASRC_CC_STOP_SHIFT)
+#define EASRC_CC_STOP BIT(EASRC_CC_STOP_SHIFT)
+#define EASRC_CC_FWMDE_SHIFT 28
+#define EASRC_CC_FWMDE_MASK BIT(EASRC_CC_FWMDE_SHIFT)
+#define EASRC_CC_FWMDE BIT(EASRC_CC_FWMDE_SHIFT)
+#define EASRC_CC_FIFO_WTMK_SHIFT 16
+#define EASRC_CC_FIFO_WTMK_WIDTH 7
+#define EASRC_CC_FIFO_WTMK_MASK ((BIT(EASRC_CC_FIFO_WTMK_WIDTH) - 1) \
+ << EASRC_CC_FIFO_WTMK_SHIFT)
+#define EASRC_CC_FIFO_WTMK(v) (((v) << EASRC_CC_FIFO_WTMK_SHIFT) \
+ & EASRC_CC_FIFO_WTMK_MASK)
+#define EASRC_CC_SAMPLE_POS_SHIFT 11
+#define EASRC_CC_SAMPLE_POS_WIDTH 5
+#define EASRC_CC_SAMPLE_POS_MASK ((BIT(EASRC_CC_SAMPLE_POS_WIDTH) - 1) \
+ << EASRC_CC_SAMPLE_POS_SHIFT)
+#define EASRC_CC_SAMPLE_POS(v) (((v) << EASRC_CC_SAMPLE_POS_SHIFT) \
+ & EASRC_CC_SAMPLE_POS_MASK)
+#define EASRC_CC_ENDIANNESS_SHIFT 10
+#define EASRC_CC_ENDIANNESS_MASK BIT(EASRC_CC_ENDIANNESS_SHIFT)
+#define EASRC_CC_ENDIANNESS BIT(EASRC_CC_ENDIANNESS_SHIFT)
+#define EASRC_CC_BPS_SHIFT 8
+#define EASRC_CC_BPS_WIDTH 2
+#define EASRC_CC_BPS_MASK ((BIT(EASRC_CC_BPS_WIDTH) - 1) \
+ << EASRC_CC_BPS_SHIFT)
+#define EASRC_CC_BPS(v) (((v) << EASRC_CC_BPS_SHIFT) \
+ & EASRC_CC_BPS_MASK)
+#define EASRC_CC_FMT_SHIFT 7
+#define EASRC_CC_FMT_MASK BIT(EASRC_CC_FMT_SHIFT)
+#define EASRC_CC_FMT BIT(EASRC_CC_FMT_SHIFT)
+#define EASRC_CC_INSIGN_SHIFT 6
+#define EASRC_CC_INSIGN_MASK BIT(EASRC_CC_INSIGN_SHIFT)
+#define EASRC_CC_INSIGN BIT(EASRC_CC_INSIGN_SHIFT)
+#define EASRC_CC_CHEN_SHIFT 0
+#define EASRC_CC_CHEN_WIDTH 5
+#define EASRC_CC_CHEN_MASK ((BIT(EASRC_CC_CHEN_WIDTH) - 1) \
+ << EASRC_CC_CHEN_SHIFT)
+#define EASRC_CC_CHEN(v) (((v) << EASRC_CC_CHEN_SHIFT) \
+ & EASRC_CC_CHEN_MASK)
+
+/* ASRC Context Control Extended 1 (CCE1) */
+#define EASRC_CCE1_COEF_WS_SHIFT 25
+#define EASRC_CCE1_COEF_WS_MASK BIT(EASRC_CCE1_COEF_WS_SHIFT)
+#define EASRC_CCE1_COEF_WS BIT(EASRC_CCE1_COEF_WS_SHIFT)
+#define EASRC_CCE1_COEF_MEM_RST_SHIFT 24
+#define EASRC_CCE1_COEF_MEM_RST_MASK BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
+#define EASRC_CCE1_COEF_MEM_RST BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
+#define EASRC_CCE1_PF_EXP_SHIFT 16
+#define EASRC_CCE1_PF_EXP_WIDTH 8
+#define EASRC_CCE1_PF_EXP_MASK ((BIT(EASRC_CCE1_PF_EXP_WIDTH) - 1) \
+ << EASRC_CCE1_PF_EXP_SHIFT)
+#define EASRC_CCE1_PF_EXP(v) (((v) << EASRC_CCE1_PF_EXP_SHIFT) \
+ & EASRC_CCE1_PF_EXP_MASK)
+#define EASRC_CCE1_PF_ST1_WBFP_SHIFT 9
+#define EASRC_CCE1_PF_ST1_WBFP_MASK BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
+#define EASRC_CCE1_PF_ST1_WBFP BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
+#define EASRC_CCE1_PF_TSEN_SHIFT 8
+#define EASRC_CCE1_PF_TSEN_MASK BIT(EASRC_CCE1_PF_TSEN_SHIFT)
+#define EASRC_CCE1_PF_TSEN BIT(EASRC_CCE1_PF_TSEN_SHIFT)
+#define EASRC_CCE1_RS_BYPASS_SHIFT 7
+#define EASRC_CCE1_RS_BYPASS_MASK BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
+#define EASRC_CCE1_RS_BYPASS BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
+#define EASRC_CCE1_PF_BYPASS_SHIFT 6
+#define EASRC_CCE1_PF_BYPASS_MASK BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
+#define EASRC_CCE1_PF_BYPASS BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
+#define EASRC_CCE1_RS_STOP_SHIFT 5
+#define EASRC_CCE1_RS_STOP_MASK BIT(EASRC_CCE1_RS_STOP_SHIFT)
+#define EASRC_CCE1_RS_STOP BIT(EASRC_CCE1_RS_STOP_SHIFT)
+#define EASRC_CCE1_PF_STOP_SHIFT 4
+#define EASRC_CCE1_PF_STOP_MASK BIT(EASRC_CCE1_PF_STOP_SHIFT)
+#define EASRC_CCE1_PF_STOP BIT(EASRC_CCE1_PF_STOP_SHIFT)
+#define EASRC_CCE1_RS_INIT_SHIFT 2
+#define EASRC_CCE1_RS_INIT_WIDTH 2
+#define EASRC_CCE1_RS_INIT_MASK ((BIT(EASRC_CCE1_RS_INIT_WIDTH) - 1) \
+ << EASRC_CCE1_RS_INIT_SHIFT)
+#define EASRC_CCE1_RS_INIT(v) (((v) << EASRC_CCE1_RS_INIT_SHIFT) \
+ & EASRC_CCE1_RS_INIT_MASK)
+#define EASRC_CCE1_PF_INIT_SHIFT 0
+#define EASRC_CCE1_PF_INIT_WIDTH 2
+#define EASRC_CCE1_PF_INIT_MASK ((BIT(EASRC_CCE1_PF_INIT_WIDTH) - 1) \
+ << EASRC_CCE1_PF_INIT_SHIFT)
+#define EASRC_CCE1_PF_INIT(v) (((v) << EASRC_CCE1_PF_INIT_SHIFT) \
+ & EASRC_CCE1_PF_INIT_MASK)
+
+/* ASRC Context Control Extended 2 (CCE2) */
+#define EASRC_CCE2_ST2_TAPS_SHIFT 16
+#define EASRC_CCE2_ST2_TAPS_WIDTH 9
+#define EASRC_CCE2_ST2_TAPS_MASK ((BIT(EASRC_CCE2_ST2_TAPS_WIDTH) - 1) \
+ << EASRC_CCE2_ST2_TAPS_SHIFT)
+#define EASRC_CCE2_ST2_TAPS(v) (((v) << EASRC_CCE2_ST2_TAPS_SHIFT) \
+ & EASRC_CCE2_ST2_TAPS_MASK)
+#define EASRC_CCE2_ST1_TAPS_SHIFT 0
+#define EASRC_CCE2_ST1_TAPS_WIDTH 9
+#define EASRC_CCE2_ST1_TAPS_MASK ((BIT(EASRC_CCE2_ST1_TAPS_WIDTH) - 1) \
+ << EASRC_CCE2_ST1_TAPS_SHIFT)
+#define EASRC_CCE2_ST1_TAPS(v) (((v) << EASRC_CCE2_ST1_TAPS_SHIFT) \
+ & EASRC_CCE2_ST1_TAPS_MASK)
+
+/* ASRC Control Input Access (CIA) */
+#define EASRC_CIA_ITER_SHIFT 16
+#define EASRC_CIA_ITER_WIDTH 6
+#define EASRC_CIA_ITER_MASK ((BIT(EASRC_CIA_ITER_WIDTH) - 1) \
+ << EASRC_CIA_ITER_SHIFT)
+#define EASRC_CIA_ITER(v) (((v) << EASRC_CIA_ITER_SHIFT) \
+ & EASRC_CIA_ITER_MASK)
+#define EASRC_CIA_GRLEN_SHIFT 8
+#define EASRC_CIA_GRLEN_WIDTH 6
+#define EASRC_CIA_GRLEN_MASK ((BIT(EASRC_CIA_GRLEN_WIDTH) - 1) \
+ << EASRC_CIA_GRLEN_SHIFT)
+#define EASRC_CIA_GRLEN(v) (((v) << EASRC_CIA_GRLEN_SHIFT) \
+ & EASRC_CIA_GRLEN_MASK)
+#define EASRC_CIA_ACCLEN_SHIFT 0
+#define EASRC_CIA_ACCLEN_WIDTH 6
+#define EASRC_CIA_ACCLEN_MASK ((BIT(EASRC_CIA_ACCLEN_WIDTH) - 1) \
+ << EASRC_CIA_ACCLEN_SHIFT)
+#define EASRC_CIA_ACCLEN(v) (((v) << EASRC_CIA_ACCLEN_SHIFT) \
+ & EASRC_CIA_ACCLEN_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register0 (DPCS0R0) */
+#define EASRC_DPCS0R0_MAXCH_SHIFT 24
+#define EASRC_DPCS0R0_MAXCH_WIDTH 5
+#define EASRC_DPCS0R0_MAXCH_MASK ((BIT(EASRC_DPCS0R0_MAXCH_WIDTH) - 1) \
+ << EASRC_DPCS0R0_MAXCH_SHIFT)
+#define EASRC_DPCS0R0_MAXCH(v) (((v) << EASRC_DPCS0R0_MAXCH_SHIFT) \
+ & EASRC_DPCS0R0_MAXCH_MASK)
+#define EASRC_DPCS0R0_MINCH_SHIFT 16
+#define EASRC_DPCS0R0_MINCH_WIDTH 5
+#define EASRC_DPCS0R0_MINCH_MASK ((BIT(EASRC_DPCS0R0_MINCH_WIDTH) - 1) \
+ << EASRC_DPCS0R0_MINCH_SHIFT)
+#define EASRC_DPCS0R0_MINCH(v) (((v) << EASRC_DPCS0R0_MINCH_SHIFT) \
+ & EASRC_DPCS0R0_MINCH_MASK)
+#define EASRC_DPCS0R0_NUMCH_SHIFT 8
+#define EASRC_DPCS0R0_NUMCH_WIDTH 5
+#define EASRC_DPCS0R0_NUMCH_MASK ((BIT(EASRC_DPCS0R0_NUMCH_WIDTH) - 1) \
+ << EASRC_DPCS0R0_NUMCH_SHIFT)
+#define EASRC_DPCS0R0_NUMCH(v) (((v) << EASRC_DPCS0R0_NUMCH_SHIFT) \
+ & EASRC_DPCS0R0_NUMCH_MASK)
+#define EASRC_DPCS0R0_CTXNUM_SHIFT 1
+#define EASRC_DPCS0R0_CTXNUM_WIDTH 2
+#define EASRC_DPCS0R0_CTXNUM_MASK ((BIT(EASRC_DPCS0R0_CTXNUM_WIDTH) - 1) \
+ << EASRC_DPCS0R0_CTXNUM_SHIFT)
+#define EASRC_DPCS0R0_CTXNUM(v) (((v) << EASRC_DPCS0R0_CTXNUM_SHIFT) \
+ & EASRC_DPCS0R0_CTXNUM_MASK)
+#define EASRC_DPCS0R0_EN_SHIFT 0
+#define EASRC_DPCS0R0_EN_MASK BIT(EASRC_DPCS0R0_EN_SHIFT)
+#define EASRC_DPCS0R0_EN BIT(EASRC_DPCS0R0_EN_SHIFT)
+
+/* ASRC Datapath Processor Control Slot0 Register1 (DPCS0R1) */
+#define EASRC_DPCS0R1_ST1_EXP_SHIFT 0
+#define EASRC_DPCS0R1_ST1_EXP_WIDTH 13
+#define EASRC_DPCS0R1_ST1_EXP_MASK ((BIT(EASRC_DPCS0R1_ST1_EXP_WIDTH) - 1) \
+ << EASRC_DPCS0R1_ST1_EXP_SHIFT)
+#define EASRC_DPCS0R1_ST1_EXP(v) (((v) << EASRC_DPCS0R1_ST1_EXP_SHIFT) \
+ & EASRC_DPCS0R1_ST1_EXP_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register2 (DPCS0R2) */
+#define EASRC_DPCS0R2_ST1_MA_SHIFT 16
+#define EASRC_DPCS0R2_ST1_MA_WIDTH 13
+#define EASRC_DPCS0R2_ST1_MA_MASK ((BIT(EASRC_DPCS0R2_ST1_MA_WIDTH) - 1) \
+ << EASRC_DPCS0R2_ST1_MA_SHIFT)
+#define EASRC_DPCS0R2_ST1_MA(v) (((v) << EASRC_DPCS0R2_ST1_MA_SHIFT) \
+ & EASRC_DPCS0R2_ST1_MA_MASK)
+#define EASRC_DPCS0R2_ST1_SA_SHIFT 0
+#define EASRC_DPCS0R2_ST1_SA_WIDTH 13
+#define EASRC_DPCS0R2_ST1_SA_MASK ((BIT(EASRC_DPCS0R2_ST1_SA_WIDTH) - 1) \
+ << EASRC_DPCS0R2_ST1_SA_SHIFT)
+#define EASRC_DPCS0R2_ST1_SA(v) (((v) << EASRC_DPCS0R2_ST1_SA_SHIFT) \
+ & EASRC_DPCS0R2_ST1_SA_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register3 (DPCS0R3) */
+#define EASRC_DPCS0R3_ST2_MA_SHIFT 16
+#define EASRC_DPCS0R3_ST2_MA_WIDTH 13
+#define EASRC_DPCS0R3_ST2_MA_MASK ((BIT(EASRC_DPCS0R3_ST2_MA_WIDTH) - 1) \
+ << EASRC_DPCS0R3_ST2_MA_SHIFT)
+#define EASRC_DPCS0R3_ST2_MA(v) (((v) << EASRC_DPCS0R3_ST2_MA_SHIFT) \
+ & EASRC_DPCS0R3_ST2_MA_MASK)
+#define EASRC_DPCS0R3_ST2_SA_SHIFT 0
+#define EASRC_DPCS0R3_ST2_SA_WIDTH 13
+#define EASRC_DPCS0R3_ST2_SA_MASK ((BIT(EASRC_DPCS0R3_ST2_SA_WIDTH) - 1) \
+ << EASRC_DPCS0R3_ST2_SA_SHIFT)
+#define EASRC_DPCS0R3_ST2_SA(v) (((v) << EASRC_DPCS0R3_ST2_SA_SHIFT) \
+ & EASRC_DPCS0R3_ST2_SA_MASK)
+
+/* ASRC Context Output Control (COC) */
+#define EASRC_COC_FWMDE_SHIFT 28
+#define EASRC_COC_FWMDE_MASK BIT(EASRC_COC_FWMDE_SHIFT)
+#define EASRC_COC_FWMDE BIT(EASRC_COC_FWMDE_SHIFT)
+#define EASRC_COC_FIFO_WTMK_SHIFT 16
+#define EASRC_COC_FIFO_WTMK_WIDTH 7
+#define EASRC_COC_FIFO_WTMK_MASK ((BIT(EASRC_COC_FIFO_WTMK_WIDTH) - 1) \
+ << EASRC_COC_FIFO_WTMK_SHIFT)
+#define EASRC_COC_FIFO_WTMK(v) (((v) << EASRC_COC_FIFO_WTMK_SHIFT) \
+ & EASRC_COC_FIFO_WTMK_MASK)
+#define EASRC_COC_SAMPLE_POS_SHIFT 11
+#define EASRC_COC_SAMPLE_POS_WIDTH 5
+#define EASRC_COC_SAMPLE_POS_MASK ((BIT(EASRC_COC_SAMPLE_POS_WIDTH) - 1) \
+ << EASRC_COC_SAMPLE_POS_SHIFT)
+#define EASRC_COC_SAMPLE_POS(v) (((v) << EASRC_COC_SAMPLE_POS_SHIFT) \
+ & EASRC_COC_SAMPLE_POS_MASK)
+#define EASRC_COC_ENDIANNESS_SHIFT 10
+#define EASRC_COC_ENDIANNESS_MASK BIT(EASRC_COC_ENDIANNESS_SHIFT)
+#define EASRC_COC_ENDIANNESS BIT(EASRC_COC_ENDIANNESS_SHIFT)
+#define EASRC_COC_BPS_SHIFT 8
+#define EASRC_COC_BPS_WIDTH 2
+#define EASRC_COC_BPS_MASK ((BIT(EASRC_COC_BPS_WIDTH) - 1) \
+ << EASRC_COC_BPS_SHIFT)
+#define EASRC_COC_BPS(v) (((v) << EASRC_COC_BPS_SHIFT) \
+ & EASRC_COC_BPS_MASK)
+#define EASRC_COC_FMT_SHIFT 7
+#define EASRC_COC_FMT_MASK BIT(EASRC_COC_FMT_SHIFT)
+#define EASRC_COC_FMT BIT(EASRC_COC_FMT_SHIFT)
+#define EASRC_COC_OUTSIGN_SHIFT 6
+#define EASRC_COC_OUTSIGN_MASK BIT(EASRC_COC_OUTSIGN_SHIFT)
+#define EASRC_COC_OUTSIGN_OUT BIT(EASRC_COC_OUTSIGN_SHIFT)
+#define EASRC_COC_IEC_VDATA_SHIFT 2
+#define EASRC_COC_IEC_VDATA_MASK BIT(EASRC_COC_IEC_VDATA_SHIFT)
+#define EASRC_COC_IEC_VDATA BIT(EASRC_COC_IEC_VDATA_SHIFT)
+#define EASRC_COC_IEC_EN_SHIFT 1
+#define EASRC_COC_IEC_EN_MASK BIT(EASRC_COC_IEC_EN_SHIFT)
+#define EASRC_COC_IEC_EN BIT(EASRC_COC_IEC_EN_SHIFT)
+#define EASRC_COC_DITHER_EN_SHIFT 0
+#define EASRC_COC_DITHER_EN_MASK BIT(EASRC_COC_DITHER_EN_SHIFT)
+#define EASRC_COC_DITHER_EN BIT(EASRC_COC_DITHER_EN_SHIFT)
+
+/* ASRC Control Output Access (COA) */
+#define EASRC_COA_ITER_SHIFT 16
+#define EASRC_COA_ITER_WIDTH 6
+#define EASRC_COA_ITER_MASK ((BIT(EASRC_COA_ITER_WIDTH) - 1) \
+ << EASRC_COA_ITER_SHIFT)
+#define EASRC_COA_ITER(v) (((v) << EASRC_COA_ITER_SHIFT) \
+ & EASRC_COA_ITER_MASK)
+#define EASRC_COA_GRLEN_SHIFT 8
+#define EASRC_COA_GRLEN_WIDTH 6
+#define EASRC_COA_GRLEN_MASK ((BIT(EASRC_COA_GRLEN_WIDTH) - 1) \
+ << EASRC_COA_GRLEN_SHIFT)
+#define EASRC_COA_GRLEN(v) (((v) << EASRC_COA_GRLEN_SHIFT) \
+ & EASRC_COA_GRLEN_MASK)
+#define EASRC_COA_ACCLEN_SHIFT 0
+#define EASRC_COA_ACCLEN_WIDTH 6
+#define EASRC_COA_ACCLEN_MASK ((BIT(EASRC_COA_ACCLEN_WIDTH) - 1) \
+ << EASRC_COA_ACCLEN_SHIFT)
+#define EASRC_COA_ACCLEN(v) (((v) << EASRC_COA_ACCLEN_SHIFT) \
+ & EASRC_COA_ACCLEN_MASK)
+
+/* ASRC Sample FIFO Status (SFS) */
+#define EASRC_SFS_IWTMK_SHIFT 23
+#define EASRC_SFS_IWTMK_MASK BIT(EASRC_SFS_IWTMK_SHIFT)
+#define EASRC_SFS_IWTMK BIT(EASRC_SFS_IWTMK_SHIFT)
+#define EASRC_SFS_NSGI_SHIFT 16
+#define EASRC_SFS_NSGI_WIDTH 7
+#define EASRC_SFS_NSGI_MASK ((BIT(EASRC_SFS_NSGI_WIDTH) - 1) \
+ << EASRC_SFS_NSGI_SHIFT)
+#define EASRC_SFS_NSGI(v) (((v) << EASRC_SFS_NSGI_SHIFT) \
+ & EASRC_SFS_NSGI_MASK)
+#define EASRC_SFS_OWTMK_SHIFT 7
+#define EASRC_SFS_OWTMK_MASK BIT(EASRC_SFS_OWTMK_SHIFT)
+#define EASRC_SFS_OWTMK BIT(EASRC_SFS_OWTMK_SHIFT)
+#define EASRC_SFS_NSGO_SHIFT 0
+#define EASRC_SFS_NSGO_WIDTH 7
+#define EASRC_SFS_NSGO_MASK ((BIT(EASRC_SFS_NSGO_WIDTH) - 1) \
+ << EASRC_SFS_NSGO_SHIFT)
+#define EASRC_SFS_NSGO(v) (((v) << EASRC_SFS_NSGO_SHIFT) \
+ & EASRC_SFS_NSGO_MASK)
+
+/* ASRC Resampling Ratio Low (RRL) */
+#define EASRC_RRL_RS_RL_SHIFT 0
+#define EASRC_RRL_RS_RL_WIDTH 32
+#define EASRC_RRL_RS_RL(v) ((v) << EASRC_RRL_RS_RL_SHIFT)
+
+/* ASRC Resampling Ratio High (RRH) */
+#define EASRC_RRH_RS_VLD_SHIFT 31
+#define EASRC_RRH_RS_VLD_MASK BIT(EASRC_RRH_RS_VLD_SHIFT)
+#define EASRC_RRH_RS_VLD BIT(EASRC_RRH_RS_VLD_SHIFT)
+#define EASRC_RRH_RS_RH_SHIFT 0
+#define EASRC_RRH_RS_RH_WIDTH 12
+#define EASRC_RRH_RS_RH_MASK ((BIT(EASRC_RRH_RS_RH_WIDTH) - 1) \
+ << EASRC_RRH_RS_RH_SHIFT)
+#define EASRC_RRH_RS_RH(v) (((v) << EASRC_RRH_RS_RH_SHIFT) \
+ & EASRC_RRH_RS_RH_MASK)
+
+/* ASRC Resampling Ratio Update Control (RSUC) */
+#define EASRC_RSUC_RS_RM_SHIFT 0
+#define EASRC_RSUC_RS_RM_WIDTH 32
+#define EASRC_RSUC_RS_RM(v) ((v) << EASRC_RSUC_RS_RM_SHIFT)
+
+/* ASRC Resampling Ratio Update Rate (RRUR) */
+#define EASRC_RRUR_RRR_SHIFT 0
+#define EASRC_RRUR_RRR_WIDTH 31
+#define EASRC_RRUR_RRR_MASK ((BIT(EASRC_RRUR_RRR_WIDTH) - 1) \
+ << EASRC_RRUR_RRR_SHIFT)
+#define EASRC_RRUR_RRR(v) (((v) << EASRC_RRUR_RRR_SHIFT) \
+ & EASRC_RRUR_RRR_MASK)
+
+/* ASRC Resampling Center Tap Coefficient Low (RCTCL) */
+#define EASRC_RCTCL_RS_CL_SHIFT 0
+#define EASRC_RCTCL_RS_CL_WIDTH 32
+#define EASRC_RCTCL_RS_CL(v) ((v) << EASRC_RCTCL_RS_CL_SHIFT)
+
+/* ASRC Resampling Center Tap Coefficient High (RCTCH) */
+#define EASRC_RCTCH_RS_CH_SHIFT 0
+#define EASRC_RCTCH_RS_CH_WIDTH 32
+#define EASRC_RCTCH_RS_CH(v) ((v) << EASRC_RCTCH_RS_CH_SHIFT)
+
+/* ASRC Prefilter Coefficient FIFO (PCF) */
+#define EASRC_PCF_CD_SHIFT 0
+#define EASRC_PCF_CD_WIDTH 32
+#define EASRC_PCF_CD(v) ((v) << EASRC_PCF_CD_SHIFT)
+
+/* ASRC Context Resampling Coefficient Memory (CRCM) */
+#define EASRC_CRCM_RS_CWD_SHIFT 0
+#define EASRC_CRCM_RS_CWD_WIDTH 32
+#define EASRC_CRCM_RS_CWD(v) ((v) << EASRC_CRCM_RS_CWD_SHIFT)
+
+/* ASRC Context Resampling Coefficient Control (CRCC) */
+#define EASRC_CRCC_RS_CA_SHIFT 16
+#define EASRC_CRCC_RS_CA_WIDTH 11
+#define EASRC_CRCC_RS_CA_MASK ((BIT(EASRC_CRCC_RS_CA_WIDTH) - 1) \
+ << EASRC_CRCC_RS_CA_SHIFT)
+#define EASRC_CRCC_RS_CA(v) (((v) << EASRC_CRCC_RS_CA_SHIFT) \
+ & EASRC_CRCC_RS_CA_MASK)
+#define EASRC_CRCC_RS_TAPS_SHIFT 1
+#define EASRC_CRCC_RS_TAPS_WIDTH 2
+#define EASRC_CRCC_RS_TAPS_MASK ((BIT(EASRC_CRCC_RS_TAPS_WIDTH) - 1) \
+ << EASRC_CRCC_RS_TAPS_SHIFT)
+#define EASRC_CRCC_RS_TAPS(v) (((v) << EASRC_CRCC_RS_TAPS_SHIFT) \
+ & EASRC_CRCC_RS_TAPS_MASK)
+#define EASRC_CRCC_RS_CPR_SHIFT 0
+#define EASRC_CRCC_RS_CPR_MASK BIT(EASRC_CRCC_RS_CPR_SHIFT)
+#define EASRC_CRCC_RS_CPR BIT(EASRC_CRCC_RS_CPR_SHIFT)
+
+/* ASRC Interrupt_Control (IC) */
+#define EASRC_IRQC_RSDM_SHIFT 8
+#define EASRC_IRQC_RSDM_WIDTH 4
+#define EASRC_IRQC_RSDM_MASK ((BIT(EASRC_IRQC_RSDM_WIDTH) - 1) \
+ << EASRC_IRQC_RSDM_SHIFT)
+#define EASRC_IRQC_RSDM(v) (((v) << EASRC_IRQC_RSDM_SHIFT) \
+ & EASRC_IRQC_RSDM_MASK)
+#define EASRC_IRQC_OERM_SHIFT 4
+#define EASRC_IRQC_OERM_WIDTH 4
+#define EASRC_IRQC_OERM_MASK ((BIT(EASRC_IRQC_OERM_WIDTH) - 1) \
+ << EASRC_IRQC_OERM_SHIFT)
+#define EASRC_IRQC_OERM(v) (((v) << EASRC_IRQC_OERM_SHIFT) \
+ & EASRC_IEQC_OERM_MASK)
+#define EASRC_IRQC_IOM_SHIFT 0
+#define EASRC_IRQC_IOM_WIDTH 4
+#define EASRC_IRQC_IOM_MASK ((BIT(EASRC_IRQC_IOM_WIDTH) - 1) \
+ << EASRC_IRQC_IOM_SHIFT)
+#define EASRC_IRQC_IOM(v) (((v) << EASRC_IRQC_IOM_SHIFT) \
+ & EASRC_IRQC_IOM_MASK)
+
+/* ASRC Interrupt Status Flags (ISF) */
+#define EASRC_IRQF_RSD_SHIFT 8
+#define EASRC_IRQF_RSD_WIDTH 4
+#define EASRC_IRQF_RSD_MASK ((BIT(EASRC_IRQF_RSD_WIDTH) - 1) \
+ << EASRC_IRQF_RSD_SHIFT)
+#define EASRC_IRQF_RSD(v) (((v) << EASRC_IRQF_RSD_SHIFT) \
+ & EASRC_IRQF_RSD_MASK)
+#define EASRC_IRQF_OER_SHIFT 4
+#define EASRC_IRQF_OER_WIDTH 4
+#define EASRC_IRQF_OER_MASK ((BIT(EASRC_IRQF_OER_WIDTH) - 1) \
+ << EASRC_IRQF_OER_SHIFT)
+#define EASRC_IRQF_OER(v) (((v) << EASRC_IRQF_OER_SHIFT) \
+ & EASRC_IRQF_OER_MASK)
+#define EASRC_IRQF_IFO_SHIFT 0
+#define EASRC_IRQF_IFO_WIDTH 4
+#define EASRC_IRQF_IFO_MASK ((BIT(EASRC_IRQF_IFO_WIDTH) - 1) \
+ << EASRC_IRQF_IFO_SHIFT)
+#define EASRC_IRQF_IFO(v) (((v) << EASRC_IRQF_IFO_SHIFT) \
+ & EASRC_IRQF_IFO_MASK)
+
+/* ASRC Context Channel STAT */
+#define EASRC_CSx_CSx_SHIFT 0
+#define EASRC_CSx_CSx_WIDTH 32
+#define EASRC_CSx_CSx(v) ((v) << EASRC_CSx_CSx_SHIFT)
+
+/* ASRC Debug Control Register */
+#define EASRC_DBGC_DMS_SHIFT 0
+#define EASRC_DBGC_DMS_WIDTH 6
+#define EASRC_DBGC_DMS_MASK ((BIT(EASRC_DBGC_DMS_WIDTH) - 1) \
+ << EASRC_DBGC_DMS_SHIFT)
+#define EASRC_DBGC_DMS(v) (((v) << EASRC_DBGC_DMS_SHIFT) \
+ & EASRC_DBGC_DMS_MASK)
+
+/* ASRC Debug Status Register */
+#define EASRC_DBGS_DS_SHIFT 0
+#define EASRC_DBGS_DS_WIDTH 32
+#define EASRC_DBGS_DS(v) ((v) << EASRC_DBGS_DS_SHIFT)
+
+/* General Constants */
+#define EASRC_CTX_MAX_NUM 4
+#define EASRC_RS_COEFF_MEM 0
+#define EASRC_PF_COEFF_MEM 1
+
+/* Prefilter constants */
+#define EASRC_PF_ST1_ONLY 0
+#define EASRC_PF_TWO_STAGE_MODE 1
+#define EASRC_PF_ST1_COEFF_WR 0
+#define EASRC_PF_ST2_COEFF_WR 1
+#define EASRC_MAX_PF_TAPS 384
+
+/* Resampling constants */
+#define EASRC_RS_32_TAPS 0
+#define EASRC_RS_64_TAPS 1
+#define EASRC_RS_128_TAPS 2
+
+/* Initialization mode */
+#define EASRC_INIT_MODE_SW_CONTROL 0
+#define EASRC_INIT_MODE_REPLICATE 1
+#define EASRC_INIT_MODE_ZERO_FILL 2
+
+/* FIFO watermarks */
+#define FSL_EASRC_INPUTFIFO_WML 0x4
+#define FSL_EASRC_OUTPUTFIFO_WML 0x1
+
+#define EASRC_INPUTFIFO_THRESHOLD_MIN 0
+#define EASRC_INPUTFIFO_THRESHOLD_MAX 127
+#define EASRC_OUTPUTFIFO_THRESHOLD_MIN 0
+#define EASRC_OUTPUTFIFO_THRESHOLD_MAX 63
+
+#define EASRC_DMA_BUFFER_SIZE (1024 * 48 * 9)
+#define EASRC_MAX_BUFFER_SIZE (1024 * 48)
+
+#define FIRMWARE_MAGIC 0xDEAD
+#define FIRMWARE_VERSION 1
+
+#define PREFILTER_MEM_LEN 0x1800
+
+enum easrc_word_width {
+ EASRC_WIDTH_16_BIT = 0,
+ EASRC_WIDTH_20_BIT = 1,
+ EASRC_WIDTH_24_BIT = 2,
+ EASRC_WIDTH_32_BIT = 3,
+};
+
+struct __attribute__((__packed__)) asrc_firmware_hdr {
+ u32 magic;
+ u32 interp_scen;
+ u32 prefil_scen;
+ u32 firmware_version;
+};
+
+struct __attribute__((__packed__)) interp_params {
+ u32 magic;
+ u32 num_taps;
+ u32 num_phases;
+ u64 center_tap;
+ u64 coeff[8192];
+};
+
+struct __attribute__((__packed__)) prefil_params {
+ u32 magic;
+ u32 insr;
+ u32 outsr;
+ u32 st1_taps;
+ u32 st2_taps;
+ u32 st1_exp;
+ u64 coeff[256];
+};
+
+struct dma_block {
+ void *dma_vaddr;
+ unsigned int length;
+ unsigned int max_buf_size;
+};
+
+struct fsl_easrc_data_fmt {
+ unsigned int width : 2;
+ unsigned int endianness : 1;
+ unsigned int unsign : 1;
+ unsigned int floating_point : 1;
+ unsigned int iec958: 1;
+ unsigned int sample_pos: 5;
+ unsigned int addexp;
+};
+
+struct fsl_easrc_io_params {
+ struct fsl_easrc_data_fmt fmt;
+ unsigned int group_len;
+ unsigned int iterations;
+ unsigned int access_len;
+ unsigned int fifo_wtmk;
+ unsigned int sample_rate;
+ snd_pcm_format_t sample_format;
+ unsigned int norm_rate;
+};
+
+struct fsl_easrc_slot {
+ bool busy;
+ int ctx_index;
+ int slot_index;
+ int num_channel; /* maximum is 8 */
+ int min_channel;
+ int max_channel;
+ int pf_mem_used;
+};
+
+/**
+ * fsl_easrc_ctx_priv: EASRC context private data
+ *
+ * @in_params: input parameter
+ * @out_params: output parameter
+ * @st1_num_taps: tap number of stage 1
+ * @st2_num_taps: tap number of stage 2
+ * @st1_num_exp: exponent number of stage 1
+ * @pf_init_mode: prefilter init mode
+ * @rs_init_mode: resample filter init mode
+ * @ctx_streams: stream flag of ctx
+ * @rs_ratio: resampler ratio
+ * @st1_coeff: pointer of stage 1 coeff
+ * @st2_coeff: pointer of stage 2 coeff
+ * @in_filled_sample: input filled sample
+ * @out_missed_sample: sample missed in output
+ * @st1_addexp: exponent added for stage1
+ * @st2_addexp: exponent added for stage2
+ * @ratio_mod: update ratio
+ * @in_filled_len: input filled length
+ */
+struct fsl_easrc_ctx_priv {
+ struct fsl_easrc_io_params in_params;
+ struct fsl_easrc_io_params out_params;
+ unsigned int st1_num_taps;
+ unsigned int st2_num_taps;
+ unsigned int st1_num_exp;
+ unsigned int pf_init_mode;
+ unsigned int rs_init_mode;
+ unsigned int ctx_streams;
+ u64 rs_ratio;
+ u64 *st1_coeff;
+ u64 *st2_coeff;
+ int in_filled_sample;
+ int out_missed_sample;
+ int st1_addexp;
+ int st2_addexp;
+ int ratio_mod;
+ unsigned int in_filled_len;
+};
+
+/**
+ * fsl_easrc_priv: EASRC private data
+ *
+ * @slot: slot setting
+ * @firmware_hdr: the header of firmware
+ * @interp: pointer to interpolation filter coeff
+ * @prefil: pointer to prefilter coeff
+ * @fw: firmware of coeff table
+ * @fw_name: firmware name
+ * @rs_num_taps: resample filter taps, 32, 64, or 128
+ * @bps_iec958: bits per sample of iec958
+ * @rs_coeff: resampler coefficient
+ * @const_coeff: one tap prefilter coefficient
+ * @firmware_loaded: firmware is loaded
+ */
+struct fsl_easrc_priv {
+ struct fsl_easrc_slot slot[EASRC_CTX_MAX_NUM][2];
+ struct asrc_firmware_hdr *firmware_hdr;
+ struct interp_params *interp;
+ struct prefil_params *prefil;
+ const struct firmware *fw;
+ const char *fw_name;
+ unsigned int rs_num_taps;
+ unsigned int bps_iec958[EASRC_CTX_MAX_NUM];
+ u64 *rs_coeff;
+ u64 const_coeff;
+ int firmware_loaded;
+};
+#endif /* _FSL_EASRC_H */
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 26a90e12ede4..cde0b0c6c1ef 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -1,33 +1,36 @@
-/*
- * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
- *
- * Copyright (C) 2014 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
+//
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "fsl_esai.h"
#include "imx-pcm.h"
-#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000
#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
/**
- * fsl_esai: ESAI private data
- *
+ * struct fsl_esai_soc_data - soc specific data
+ * @reset_at_xrun: flags for enable reset operaton
+ */
+struct fsl_esai_soc_data {
+ bool reset_at_xrun;
+};
+
+/**
+ * struct fsl_esai - ESAI private data
* @dma_params_rx: DMA parameters for receive channel
* @dma_params_tx: DMA parameters for transmit channel
* @pdev: platform device pointer
@@ -36,14 +39,20 @@
* @extalclk: esai clock source to derive HCK, SCK and FS
* @fsysclk: system clock source to derive HCK, SCK and FS
* @spbaclk: SPBA clock (optional, depending on SoC design)
+ * @work: work to handle the reset operation
+ * @soc: soc specific data
+ * @lock: spin lock between hw_reset() and trigger()
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
+ * @tx_mask: slot mask for TX
+ * @rx_mask: slot mask for RX
+ * @channels: channel num for tx or rx
* @hck_rate: clock rate of desired HCKx clock
* @sck_rate: clock rate of desired SCKx clock
* @hck_dir: the direction of HCKx pads
* @sck_div: if using PSR/PM dividers for SCKx clock
- * @slave_mode: if fully using DAI slave mode
+ * @consumer_mode: if fully using DAI clock consumer mode
* @synchronous: if using tx/rx synchronous mode
* @name: driver name
*/
@@ -56,40 +65,70 @@ struct fsl_esai {
struct clk *extalclk;
struct clk *fsysclk;
struct clk *spbaclk;
+ struct work_struct work;
+ const struct fsl_esai_soc_data *soc;
+ spinlock_t lock; /* Protect hw_reset and trigger */
u32 fifo_depth;
u32 slot_width;
u32 slots;
+ u32 tx_mask;
+ u32 rx_mask;
+ u32 channels[2];
u32 hck_rate[2];
u32 sck_rate[2];
bool hck_dir[2];
bool sck_div[2];
- bool slave_mode;
+ bool consumer_mode;
bool synchronous;
char name[32];
};
+static struct fsl_esai_soc_data fsl_esai_vf610 = {
+ .reset_at_xrun = true,
+};
+
+static struct fsl_esai_soc_data fsl_esai_imx35 = {
+ .reset_at_xrun = true,
+};
+
+static struct fsl_esai_soc_data fsl_esai_imx6ull = {
+ .reset_at_xrun = false,
+};
+
static irqreturn_t esai_isr(int irq, void *devid)
{
struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
struct platform_device *pdev = esai_priv->pdev;
u32 esr;
+ u32 saisr;
regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
+ regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
+
+ if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) &&
+ esai_priv->soc->reset_at_xrun) {
+ dev_dbg(&pdev->dev, "reset module for xrun\n");
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
+ ESAI_xCR_xEIE_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
+ ESAI_xCR_xEIE_MASK, 0);
+ schedule_work(&esai_priv->work);
+ }
if (esr & ESAI_ESR_TINIT_MASK)
- dev_dbg(&pdev->dev, "isr: Transmition Initialized\n");
+ dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");
if (esr & ESAI_ESR_RFF_MASK)
- dev_warn(&pdev->dev, "isr: Receiving overrun\n");
+ dev_dbg(&pdev->dev, "isr: Receiving overrun\n");
if (esr & ESAI_ESR_TFE_MASK)
- dev_warn(&pdev->dev, "isr: Transmition underrun\n");
+ dev_dbg(&pdev->dev, "isr: Transmission underrun\n");
if (esr & ESAI_ESR_TLS_MASK)
dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
if (esr & ESAI_ESR_TDE_MASK)
- dev_dbg(&pdev->dev, "isr: Transmition data exception\n");
+ dev_dbg(&pdev->dev, "isr: Transmission data exception\n");
if (esr & ESAI_ESR_TED_MASK)
dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");
@@ -113,13 +152,15 @@ static irqreturn_t esai_isr(int irq, void *devid)
}
/**
- * This function is used to calculate the divisors of psr, pm, fp and it is
- * supposed to be called in set_dai_sysclk() and set_bclk().
+ * fsl_esai_divisor_cal - This function is used to calculate the
+ * divisors of psr, pm, fp and it is supposed to be called in
+ * set_dai_sysclk() and set_bclk().
*
+ * @dai: pointer to DAI
+ * @tx: current setting is for playback or capture
* @ratio: desired overall ratio for the paticipating dividers
* @usefp: for HCK setting, there is no need to set fp divider
* @fp: bypass other dividers by setting fp directly if fp != 0
- * @tx: current setting is for playback or capture
*/
static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
bool usefp, u32 fp)
@@ -145,6 +186,13 @@ static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;
+ /* Do not loop-search if PM (1 ~ 256) alone can serve the ratio */
+ if (ratio <= 256) {
+ pm = ratio;
+ fp = 1;
+ goto out;
+ }
+
/* Set the max fluctuation -- 0.1% of the max devisor */
savesub = (psr ? 1 : 8) * 256 * maxfp / 1000;
@@ -199,13 +247,12 @@ out_fp:
}
/**
- * This function mainly configures the clock frequency of MCLK (HCKT/HCKR)
- *
- * @Parameters:
- * clk_id: The clock source of HCKT/HCKR
+ * fsl_esai_set_dai_sysclk - configure the clock frequency of MCLK (HCKT/HCKR)
+ * @dai: pointer to DAI
+ * @clk_id: The clock source of HCKT/HCKR
* (Input from outside; output from inside, FSYS or EXTAL)
- * freq: The required clock rate of HCKT/HCKR
- * dir: The clock direction of HCKT/HCKR
+ * @freq: The required clock rate of HCKT/HCKR
+ * @dir: The clock direction of HCKT/HCKR
*
* Note: If the direction is input, we do not care about clk_id.
*/
@@ -214,12 +261,18 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
struct clk *clksrc = esai_priv->extalclk;
- bool tx = clk_id <= ESAI_HCKT_EXTAL;
+ bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous);
bool in = dir == SND_SOC_CLOCK_IN;
u32 ratio, ecr = 0;
unsigned long clk_rate;
int ret;
+ if (freq == 0) {
+ dev_err(dai->dev, "%sput freq of HCK%c should not be 0Hz\n",
+ in ? "in" : "out", tx ? 'T' : 'R');
+ return -EINVAL;
+ }
+
/* Bypass divider settings if the requirement doesn't change */
if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
return 0;
@@ -241,8 +294,9 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
break;
case ESAI_HCKT_EXTAL:
ecr |= ESAI_ECR_ETI;
+ break;
case ESAI_HCKR_EXTAL:
- ecr |= ESAI_ECR_ERI;
+ ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI;
break;
default:
return -EINVAL;
@@ -250,7 +304,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
if (IS_ERR(clksrc)) {
dev_err(dai->dev, "no assigned %s clock\n",
- clk_id % 2 ? "extal" : "fsys");
+ (clk_id % 2) ? "extal" : "fsys");
return PTR_ERR(clksrc);
}
clk_rate = clk_get_rate(clksrc);
@@ -300,7 +354,10 @@ out:
}
/**
- * This function configures the related dividers according to the bclk rate
+ * fsl_esai_set_bclk - configure the related dividers according to the bclk rate
+ * @dai: pointer to DAI
+ * @tx: direction boolean
+ * @freq: bclk freq
*/
static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
@@ -309,8 +366,8 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
u32 sub, ratio = hck_rate / freq;
int ret;
- /* Don't apply for fully slave mode or unchanged bclk */
- if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
+ /* Don't apply for fully consumer mode or unchanged bclk */
+ if (esai_priv->consumer_mode || esai_priv->sck_rate[tx] == freq)
return 0;
if (ratio * freq > hck_rate)
@@ -352,21 +409,13 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
- regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA,
- ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask));
- regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB,
- ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(tx_mask));
-
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
- regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA,
- ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask));
- regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB,
- ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask));
-
esai_priv->slot_width = slot_width;
esai_priv->slots = slots;
+ esai_priv->tx_mask = tx_mask;
+ esai_priv->rx_mask = rx_mask;
return 0;
}
@@ -389,7 +438,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_RIGHT_J:
/* Data on rising edge of bclk, frame high, right aligned */
- xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA;
+ xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
+ xcr |= ESAI_xCR_xWA;
break;
case SND_SOC_DAIFMT_DSP_A:
/* Data on rising edge of bclk, frame high, 1clk before data */
@@ -426,32 +476,32 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- esai_priv->slave_mode = false;
+ esai_priv->consumer_mode = false;
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- esai_priv->slave_mode = true;
+ /* DAI clock provider masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ esai_priv->consumer_mode = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_BP_FC:
xccr |= ESAI_xCCR_xCKD;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_BC_FP:
xccr |= ESAI_xCCR_xFSD;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_BP_FP:
xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
break;
default:
return -EINVAL;
}
- mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR;
+ mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR | ESAI_xCR_xWA;
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);
mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
- ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA;
+ ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);
@@ -462,56 +512,24 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
- int ret;
- /*
- * Some platforms might use the same bit to gate all three or two of
- * clocks, so keep all clocks open/close at the same time for safety
- */
- ret = clk_prepare_enable(esai_priv->coreclk);
- if (ret)
- return ret;
- if (!IS_ERR(esai_priv->spbaclk)) {
- ret = clk_prepare_enable(esai_priv->spbaclk);
- if (ret)
- goto err_spbaclk;
- }
- if (!IS_ERR(esai_priv->extalclk)) {
- ret = clk_prepare_enable(esai_priv->extalclk);
- if (ret)
- goto err_extalck;
- }
- if (!IS_ERR(esai_priv->fsysclk)) {
- ret = clk_prepare_enable(esai_priv->fsysclk);
- if (ret)
- goto err_fsysclk;
- }
-
- if (!dai->active) {
+ if (!snd_soc_dai_active(dai)) {
/* Set synchronous mode */
regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
ESAI_SAICR_SYNC, esai_priv->synchronous ?
ESAI_SAICR_SYNC : 0);
- /* Set a default slot number -- 2 */
+ /* Set slots count */
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
- ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
+ ESAI_xCCR_xDC_MASK,
+ ESAI_xCCR_xDC(esai_priv->slots));
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
- ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
+ ESAI_xCCR_xDC_MASK,
+ ESAI_xCCR_xDC(esai_priv->slots));
}
return 0;
-err_fsysclk:
- if (!IS_ERR(esai_priv->extalclk))
- clk_disable_unprepare(esai_priv->extalclk);
-err_extalck:
- if (!IS_ERR(esai_priv->spbaclk))
- clk_disable_unprepare(esai_priv->spbaclk);
-err_spbaclk:
- clk_disable_unprepare(esai_priv->coreclk);
-
- return ret;
}
static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
@@ -533,10 +551,18 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
bclk = params_rate(params) * slot_width * esai_priv->slots;
- ret = fsl_esai_set_bclk(dai, tx, bclk);
+ ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk);
if (ret)
return ret;
+ mask = ESAI_xCR_xSWS_MASK;
+ val = ESAI_xCR_xSWS(slot_width, width);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
+ /* Recording in synchronous mode needs to set TCR also */
+ if (!tx && esai_priv->synchronous)
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val);
+
/* Use Normal mode to support monaural audio */
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?
@@ -552,10 +578,9 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
- mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
- val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
-
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
+ if (tx)
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
+ ESAI_xCR_PADC, ESAI_xCR_PADC);
/* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */
regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
@@ -565,18 +590,168 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int fsl_esai_hw_init(struct fsl_esai *esai_priv)
{
- struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ struct platform_device *pdev = esai_priv->pdev;
+ int ret;
+
+ /* Reset ESAI unit */
+ ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+ ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
+ ESAI_ECR_ESAIEN | ESAI_ECR_ERST);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * We need to enable ESAI so as to access some of its registers.
+ * Otherwise, we would fail to dump regmap from user space.
+ */
+ ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+ ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
+ ESAI_ECR_ESAIEN);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+ ESAI_PRRC_PDC_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+ ESAI_PCRC_PC_MASK, 0);
- if (!IS_ERR(esai_priv->fsysclk))
- clk_disable_unprepare(esai_priv->fsysclk);
- if (!IS_ERR(esai_priv->extalclk))
- clk_disable_unprepare(esai_priv->extalclk);
- if (!IS_ERR(esai_priv->spbaclk))
- clk_disable_unprepare(esai_priv->spbaclk);
- clk_disable_unprepare(esai_priv->coreclk);
+ return 0;
+}
+
+static int fsl_esai_register_restore(struct fsl_esai *esai_priv)
+{
+ int ret;
+
+ /* FIFO reset for safety */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+ regcache_mark_dirty(esai_priv->regmap);
+ ret = regcache_sync(esai_priv->regmap);
+ if (ret)
+ return ret;
+
+ /* FIFO reset done */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+ return 0;
+}
+
+static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx)
+{
+ u8 i, channels = esai_priv->channels[tx];
+ u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
+ u32 mask;
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
+
+ /* Write initial words reqiured by ESAI as normal procedure */
+ for (i = 0; tx && i < channels; i++)
+ regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
+
+ /*
+ * When set the TE/RE in the end of enablement flow, there
+ * will be channel swap issue for multi data line case.
+ * In order to workaround this issue, we switch the bit
+ * enablement sequence to below sequence
+ * 1) clear the xSMB & xSMA: which is done in probe and
+ * stop state.
+ * 2) set TE/RE
+ * 3) set xSMB
+ * 4) set xSMA: xSMA is the last one in this flow, which
+ * will trigger esai to start.
+ */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
+ tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
+ mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask;
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
+ ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
+ ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));
+
+ /* Enable Exception interrupt */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE);
+}
+
+static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)
+{
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ ESAI_xCR_xEIE_MASK, 0);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
+ ESAI_xSMA_xS_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
+ ESAI_xSMB_xS_MASK, 0);
+
+ /* Disable and reset FIFO */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR, 0);
+}
+
+static void fsl_esai_hw_reset(struct work_struct *work)
+{
+ struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work);
+ bool tx = true, rx = false, enabled[2];
+ unsigned long lock_flags;
+ u32 tfcr, rfcr;
+
+ spin_lock_irqsave(&esai_priv->lock, lock_flags);
+ /* Save the registers */
+ regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
+ regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
+ enabled[tx] = tfcr & ESAI_xFCR_xFEN;
+ enabled[rx] = rfcr & ESAI_xFCR_xFEN;
+
+ /* Stop the tx & rx */
+ fsl_esai_trigger_stop(esai_priv, tx);
+ fsl_esai_trigger_stop(esai_priv, rx);
+
+ /* Reset the esai, and ignore return value */
+ fsl_esai_hw_init(esai_priv);
+
+ /* Enforce ESAI personal resets for both TX and RX */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
+ ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
+ ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
+
+ /* Restore registers by regcache_sync, and ignore return value */
+ fsl_esai_register_restore(esai_priv);
+
+ /* Remove ESAI personal resets by configuring PCRC and PRRC also */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
+ ESAI_xCR_xPR_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
+ ESAI_xCR_xPR_MASK, 0);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+ ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+ ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
+
+ /* Restart tx / rx, if they already enabled */
+ if (enabled[tx])
+ fsl_esai_trigger_start(esai_priv, tx);
+ if (enabled[rx])
+ fsl_esai_trigger_start(esai_priv, rx);
+
+ spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
}
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -584,35 +759,24 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u8 i, channels = substream->runtime->channels;
- u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
+ unsigned long lock_flags;
+
+ esai_priv->channels[tx] = substream->runtime->channels;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
- ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
-
- /* Write initial words reqiured by ESAI as normal procedure */
- for (i = 0; tx && i < channels; i++)
- regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
-
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
- tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
- tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
+ spin_lock_irqsave(&esai_priv->lock, lock_flags);
+ fsl_esai_trigger_start(esai_priv, tx);
+ spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
- tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
-
- /* Disable and reset FIFO */
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
- ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
- regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
- ESAI_xFCR_xFR, 0);
+ spin_lock_irqsave(&esai_priv->lock, lock_flags);
+ fsl_esai_trigger_stop(esai_priv, tx);
+ spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
break;
default:
return -EINVAL;
@@ -621,16 +785,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static struct snd_soc_dai_ops fsl_esai_dai_ops = {
- .startup = fsl_esai_startup,
- .shutdown = fsl_esai_shutdown,
- .trigger = fsl_esai_trigger,
- .hw_params = fsl_esai_hw_params,
- .set_sysclk = fsl_esai_set_dai_sysclk,
- .set_fmt = fsl_esai_set_dai_fmt,
- .set_tdm_slot = fsl_esai_set_dai_tdm_slot,
-};
-
static int fsl_esai_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
@@ -641,27 +795,37 @@ static int fsl_esai_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops fsl_esai_dai_ops = {
+ .probe = fsl_esai_dai_probe,
+ .startup = fsl_esai_startup,
+ .trigger = fsl_esai_trigger,
+ .hw_params = fsl_esai_hw_params,
+ .set_sysclk = fsl_esai_set_dai_sysclk,
+ .set_fmt = fsl_esai_set_dai_fmt,
+ .set_tdm_slot = fsl_esai_set_dai_tdm_slot,
+};
+
static struct snd_soc_dai_driver fsl_esai_dai = {
- .probe = fsl_esai_dai_probe,
.playback = {
.stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 12,
- .rates = FSL_ESAI_RATES,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = FSL_ESAI_FORMATS,
},
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 8,
- .rates = FSL_ESAI_RATES,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = FSL_ESAI_FORMATS,
},
.ops = &fsl_esai_dai_ops,
};
static const struct snd_soc_component_driver fsl_esai_component = {
- .name = "fsl-esai",
+ .name = "fsl-esai",
+ .legacy_dai_naming = 1,
};
static const struct reg_default fsl_esai_reg_defaults[] = {
@@ -781,15 +945,18 @@ static const struct regmap_config fsl_esai_regmap_config = {
.readable_reg = fsl_esai_readable_reg,
.volatile_reg = fsl_esai_volatile_reg,
.writeable_reg = fsl_esai_writeable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_FLAT,
};
+static int fsl_esai_runtime_resume(struct device *dev);
+static int fsl_esai_runtime_suspend(struct device *dev);
+
static int fsl_esai_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct fsl_esai *esai_priv;
struct resource *res;
- const uint32_t *iprop;
+ const __be32 *iprop;
void __iomem *regs;
int irq, ret;
@@ -798,16 +965,16 @@ static int fsl_esai_probe(struct platform_device *pdev)
return -ENOMEM;
esai_priv->pdev = pdev;
- strncpy(esai_priv->name, np->name, sizeof(esai_priv->name) - 1);
+ snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np);
+
+ esai_priv->soc = of_device_get_match_data(&pdev->dev);
/* Get the addresses and IRQ */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
- esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
- "core", regs, &fsl_esai_regmap_config);
+ esai_priv->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_esai_regmap_config);
if (IS_ERR(esai_priv->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
PTR_ERR(esai_priv->regmap));
@@ -837,12 +1004,10 @@ static int fsl_esai_probe(struct platform_device *pdev)
PTR_ERR(esai_priv->spbaclk));
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0,
+ ret = devm_request_irq(&pdev->dev, irq, esai_isr, IRQF_SHARED,
esai_priv->name, esai_priv);
if (ret) {
dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
@@ -852,8 +1017,8 @@ static int fsl_esai_probe(struct platform_device *pdev)
/* Set a default slot number */
esai_priv->slots = 2;
- /* Set a default master/slave state */
- esai_priv->slave_mode = true;
+ /* Set a default clock provider state */
+ esai_priv->consumer_mode = true;
/* Determine the FIFO depth */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
@@ -872,96 +1037,168 @@ static int fsl_esai_probe(struct platform_device *pdev)
/* Implement full symmetry for synchronous mode */
if (esai_priv->synchronous) {
- fsl_esai_dai.symmetric_rates = 1;
+ fsl_esai_dai.symmetric_rate = 1;
fsl_esai_dai.symmetric_channels = 1;
- fsl_esai_dai.symmetric_samplebits = 1;
+ fsl_esai_dai.symmetric_sample_bits = 1;
}
dev_set_drvdata(&pdev->dev, esai_priv);
-
- /* Reset ESAI unit */
- ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST);
- if (ret) {
- dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
- return ret;
+ spin_lock_init(&esai_priv->lock);
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = fsl_esai_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
}
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_pm_get_sync;
+
+ ret = fsl_esai_hw_init(esai_priv);
+ if (ret)
+ goto err_pm_get_sync;
+
+ esai_priv->tx_mask = 0xFFFFFFFF;
+ esai_priv->rx_mask = 0xFFFFFFFF;
+
+ /* Clear the TSMA, TSMB, RSMA, RSMB */
+ regmap_write(esai_priv->regmap, REG_ESAI_TSMA, 0);
+ regmap_write(esai_priv->regmap, REG_ESAI_TSMB, 0);
+ regmap_write(esai_priv->regmap, REG_ESAI_RSMA, 0);
+ regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0);
+
+ ret = pm_runtime_put_sync(&pdev->dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto err_pm_get_sync;
+
/*
- * We need to enable ESAI so as to access some of its registers.
- * Otherwise, we would fail to dump regmap from user space.
+ * Register platform component before registering cpu dai for there
+ * is not defer probe for platform component in snd_soc_add_pcm_runtime().
*/
- ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN);
+ ret = imx_pcm_dma_init(pdev);
if (ret) {
- dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
- return ret;
+ dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
+ goto err_pm_get_sync;
}
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,
&fsl_esai_dai, 1);
if (ret) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
- return ret;
+ goto err_pm_get_sync;
}
- ret = imx_pcm_dma_init(pdev, IMX_ESAI_DMABUF_SIZE);
- if (ret)
- dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
+ INIT_WORK(&esai_priv->work, fsl_esai_hw_reset);
return ret;
+
+err_pm_get_sync:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_esai_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void fsl_esai_remove(struct platform_device *pdev)
+{
+ struct fsl_esai *esai_priv = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_esai_runtime_suspend(&pdev->dev);
+
+ cancel_work_sync(&esai_priv->work);
}
static const struct of_device_id fsl_esai_dt_ids[] = {
- { .compatible = "fsl,imx35-esai", },
- { .compatible = "fsl,vf610-esai", },
+ { .compatible = "fsl,imx35-esai", .data = &fsl_esai_imx35 },
+ { .compatible = "fsl,vf610-esai", .data = &fsl_esai_vf610 },
+ { .compatible = "fsl,imx6ull-esai", .data = &fsl_esai_imx6ull },
{}
};
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
-#ifdef CONFIG_PM_SLEEP
-static int fsl_esai_suspend(struct device *dev)
+static int fsl_esai_runtime_resume(struct device *dev)
{
struct fsl_esai *esai = dev_get_drvdata(dev);
+ int ret;
- regcache_cache_only(esai->regmap, true);
- regcache_mark_dirty(esai->regmap);
+ /*
+ * Some platforms might use the same bit to gate all three or two of
+ * clocks, so keep all clocks open/close at the same time for safety
+ */
+ ret = clk_prepare_enable(esai->coreclk);
+ if (ret)
+ return ret;
+ if (!IS_ERR(esai->spbaclk)) {
+ ret = clk_prepare_enable(esai->spbaclk);
+ if (ret)
+ goto err_spbaclk;
+ }
+ if (!IS_ERR(esai->extalclk)) {
+ ret = clk_prepare_enable(esai->extalclk);
+ if (ret)
+ goto err_extalclk;
+ }
+ if (!IS_ERR(esai->fsysclk)) {
+ ret = clk_prepare_enable(esai->fsysclk);
+ if (ret)
+ goto err_fsysclk;
+ }
+
+ regcache_cache_only(esai->regmap, false);
+
+ ret = fsl_esai_register_restore(esai);
+ if (ret)
+ goto err_regcache_sync;
return 0;
+
+err_regcache_sync:
+ if (!IS_ERR(esai->fsysclk))
+ clk_disable_unprepare(esai->fsysclk);
+err_fsysclk:
+ if (!IS_ERR(esai->extalclk))
+ clk_disable_unprepare(esai->extalclk);
+err_extalclk:
+ if (!IS_ERR(esai->spbaclk))
+ clk_disable_unprepare(esai->spbaclk);
+err_spbaclk:
+ clk_disable_unprepare(esai->coreclk);
+
+ return ret;
}
-static int fsl_esai_resume(struct device *dev)
+static int fsl_esai_runtime_suspend(struct device *dev)
{
struct fsl_esai *esai = dev_get_drvdata(dev);
- int ret;
-
- regcache_cache_only(esai->regmap, false);
-
- /* FIFO reset for safety */
- regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
- ESAI_xFCR_xFR, ESAI_xFCR_xFR);
- regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
- ESAI_xFCR_xFR, ESAI_xFCR_xFR);
- ret = regcache_sync(esai->regmap);
- if (ret)
- return ret;
+ regcache_cache_only(esai->regmap, true);
- /* FIFO reset done */
- regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
- regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+ if (!IS_ERR(esai->fsysclk))
+ clk_disable_unprepare(esai->fsysclk);
+ if (!IS_ERR(esai->extalclk))
+ clk_disable_unprepare(esai->extalclk);
+ if (!IS_ERR(esai->spbaclk))
+ clk_disable_unprepare(esai->spbaclk);
+ clk_disable_unprepare(esai->coreclk);
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops fsl_esai_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume)
+ RUNTIME_PM_OPS(fsl_esai_runtime_suspend, fsl_esai_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_esai_driver = {
.probe = fsl_esai_probe,
+ .remove = fsl_esai_remove,
.driver = {
.name = "fsl-esai-dai",
- .pm = &fsl_esai_pm_ops,
+ .pm = pm_ptr(&fsl_esai_pm_ops),
.of_match_table = fsl_esai_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h
index 5e793bbb6b02..f873588d9045 100644
--- a/sound/soc/fsl/fsl_esai.h
+++ b/sound/soc/fsl/fsl_esai.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* fsl_esai.h - ALSA ESAI interface for the Freescale i.MX SoC
*
* 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 _FSL_ESAI_DAI_H
diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c
new file mode 100644
index 000000000000..d6cde2757c6d
--- /dev/null
+++ b/sound/soc/fsl/fsl_micfil.c
@@ -0,0 +1,1618 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// Copyright 2018 NXP
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/dma/imx-dma.h>
+#include <linux/log2.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+
+#include "fsl_micfil.h"
+#include "fsl_utils.h"
+
+#define MICFIL_OSR_DEFAULT 16
+
+#define MICFIL_NUM_RATES 7
+#define MICFIL_CLK_SRC_NUM 3
+/* clock source ids */
+#define MICFIL_AUDIO_PLL1 0
+#define MICFIL_AUDIO_PLL2 1
+#define MICFIL_CLK_EXT3 2
+
+static const unsigned int fsl_micfil_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list fsl_micfil_rate_constraints = {
+ .count = ARRAY_SIZE(fsl_micfil_rates),
+ .list = fsl_micfil_rates,
+};
+
+enum quality {
+ QUALITY_HIGH,
+ QUALITY_MEDIUM,
+ QUALITY_LOW,
+ QUALITY_VLOW0,
+ QUALITY_VLOW1,
+ QUALITY_VLOW2,
+};
+
+struct fsl_micfil {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ const struct fsl_micfil_soc_data *soc;
+ struct clk *busclk;
+ struct clk *mclk;
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
+ struct clk *clk_src[MICFIL_CLK_SRC_NUM];
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct sdma_peripheral_config sdmacfg;
+ struct snd_soc_card *card;
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ unsigned int constraint_rates_list[MICFIL_NUM_RATES];
+ unsigned int dataline;
+ char name[32];
+ int irq[MICFIL_IRQ_LINES];
+ enum quality quality;
+ int dc_remover;
+ int vad_init_mode;
+ int vad_enabled;
+ int vad_detected;
+ struct fsl_micfil_verid verid;
+ struct fsl_micfil_param param;
+ bool mclk_flag; /* mclk enable flag */
+ bool dec_bypass;
+};
+
+struct fsl_micfil_soc_data {
+ unsigned int fifos;
+ unsigned int fifo_depth;
+ unsigned int dataline;
+ bool imx;
+ bool use_edma;
+ bool use_verid;
+ bool volume_sx;
+ u64 formats;
+ int fifo_offset;
+ enum quality default_quality;
+ /* stores const value in formula to calculate range */
+ int rangeadj_const[3][2];
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
+ .imx = true,
+ .fifos = 8,
+ .fifo_depth = 8,
+ .dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .volume_sx = true,
+ .fifo_offset = 0,
+ .default_quality = QUALITY_VLOW0,
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx8mp = {
+ .imx = true,
+ .fifos = 8,
+ .fifo_depth = 32,
+ .dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .volume_sx = false,
+ .fifo_offset = 0,
+ .default_quality = QUALITY_MEDIUM,
+ .rangeadj_const = {{27, 7}, {27, 7}, {26, 7}},
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx93 = {
+ .imx = true,
+ .fifos = 8,
+ .fifo_depth = 32,
+ .dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .use_edma = true,
+ .use_verid = true,
+ .volume_sx = false,
+ .fifo_offset = 0,
+ .default_quality = QUALITY_MEDIUM,
+ .rangeadj_const = {{30, 6}, {30, 6}, {29, 6}},
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx943 = {
+ .imx = true,
+ .fifos = 8,
+ .fifo_depth = 32,
+ .dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE,
+ .use_edma = true,
+ .use_verid = true,
+ .volume_sx = false,
+ .fifo_offset = -4,
+ .default_quality = QUALITY_MEDIUM,
+ .rangeadj_const = {{34, 6}, {34, 6}, {33, 6}},
+};
+
+static const struct of_device_id fsl_micfil_dt_ids[] = {
+ { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm },
+ { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp },
+ { .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 },
+ { .compatible = "fsl,imx943-micfil", .data = &fsl_micfil_imx943 },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids);
+
+static const char * const micfil_quality_select_texts[] = {
+ [QUALITY_HIGH] = "High",
+ [QUALITY_MEDIUM] = "Medium",
+ [QUALITY_LOW] = "Low",
+ [QUALITY_VLOW0] = "VLow0",
+ [QUALITY_VLOW1] = "Vlow1",
+ [QUALITY_VLOW2] = "Vlow2",
+};
+
+static const struct soc_enum fsl_micfil_quality_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts),
+ micfil_quality_select_texts);
+
+static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0);
+
+static int micfil_get_max_range(struct fsl_micfil *micfil)
+{
+ int max_range;
+
+ switch (micfil->quality) {
+ case QUALITY_HIGH:
+ case QUALITY_VLOW0:
+ max_range = micfil->soc->rangeadj_const[0][0] - micfil->soc->rangeadj_const[0][1] *
+ ilog2(2 * MICFIL_OSR_DEFAULT);
+ break;
+ case QUALITY_MEDIUM:
+ case QUALITY_VLOW1:
+ max_range = micfil->soc->rangeadj_const[1][0] - micfil->soc->rangeadj_const[1][1] *
+ ilog2(MICFIL_OSR_DEFAULT);
+ break;
+ case QUALITY_LOW:
+ case QUALITY_VLOW2:
+ max_range = micfil->soc->rangeadj_const[2][0] - micfil->soc->rangeadj_const[2][1] *
+ ilog2(MICFIL_OSR_DEFAULT);
+ break;
+ default:
+ return 0;
+ }
+ max_range = max_range < 0 ? 0 : max_range;
+
+ return max_range;
+}
+
+static int micfil_range_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int shift = mc->shift;
+ int max_range, new_range;
+
+ new_range = ucontrol->value.integer.value[0];
+ max_range = micfil_get_max_range(micfil);
+ if (new_range > max_range)
+ dev_warn(&micfil->pdev->dev, "range makes channel %d data unreliable\n", shift / 4);
+
+ regmap_update_bits(micfil->regmap, REG_MICFIL_OUT_CTRL, 0xF << shift, new_range << shift);
+
+ return 0;
+}
+
+static int micfil_set_quality(struct fsl_micfil *micfil)
+{
+ int range, max_range;
+ u32 qsel, val;
+ int i;
+
+ if (!micfil->soc->volume_sx) {
+ regmap_read(micfil->regmap, REG_MICFIL_OUT_CTRL, &val);
+ max_range = micfil_get_max_range(micfil);
+ for (i = 0; i < micfil->soc->fifos; i++) {
+ range = (val >> MICFIL_OUTGAIN_CHX_SHIFT(i)) & 0xF;
+ if (range > max_range)
+ dev_warn(&micfil->pdev->dev, "please reset channel %d range\n", i);
+ }
+ }
+
+ switch (micfil->quality) {
+ case QUALITY_HIGH:
+ qsel = MICFIL_QSEL_HIGH_QUALITY;
+ break;
+ case QUALITY_MEDIUM:
+ qsel = MICFIL_QSEL_MEDIUM_QUALITY;
+ break;
+ case QUALITY_LOW:
+ qsel = MICFIL_QSEL_LOW_QUALITY;
+ break;
+ case QUALITY_VLOW0:
+ qsel = MICFIL_QSEL_VLOW0_QUALITY;
+ break;
+ case QUALITY_VLOW1:
+ qsel = MICFIL_QSEL_VLOW1_QUALITY;
+ break;
+ case QUALITY_VLOW2:
+ qsel = MICFIL_QSEL_VLOW2_QUALITY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_QSEL,
+ FIELD_PREP(MICFIL_CTRL2_QSEL, qsel));
+}
+
+static int micfil_quality_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = micfil->quality;
+
+ return 0;
+}
+
+static int micfil_quality_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+
+ micfil->quality = ucontrol->value.integer.value[0];
+
+ return micfil_set_quality(micfil);
+}
+
+static const char * const micfil_hwvad_enable[] = {
+ "Disable (Record only)",
+ "Enable (Record with Vad)",
+};
+
+static const char * const micfil_hwvad_init_mode[] = {
+ "Envelope mode", "Energy mode",
+};
+
+static const char * const micfil_hwvad_hpf_texts[] = {
+ "Filter bypass",
+ "Cut-off @1750Hz",
+ "Cut-off @215Hz",
+ "Cut-off @102Hz",
+};
+
+/*
+ * DC Remover Control
+ * Filter Bypassed 1 1
+ * Cut-off @21Hz 0 0
+ * Cut-off @83Hz 0 1
+ * Cut-off @152HZ 1 0
+ */
+static const char * const micfil_dc_remover_texts[] = {
+ "Cut-off @21Hz", "Cut-off @83Hz",
+ "Cut-off @152Hz", "Bypass",
+};
+
+static const struct soc_enum hwvad_enable_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable),
+ micfil_hwvad_enable);
+static const struct soc_enum hwvad_init_mode_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode),
+ micfil_hwvad_init_mode);
+static const struct soc_enum hwvad_hpf_enum =
+ SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0,
+ ARRAY_SIZE(micfil_hwvad_hpf_texts),
+ micfil_hwvad_hpf_texts);
+static const struct soc_enum fsl_micfil_dc_remover_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts),
+ micfil_dc_remover_texts);
+
+static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+ unsigned int *item = ucontrol->value.enumerated.item;
+ int val = snd_soc_enum_item_to_val(e, item[0]);
+ int i = 0, ret = 0;
+ u32 reg_val = 0;
+
+ if (val < 0 || val > 3)
+ return -EINVAL;
+
+ micfil->dc_remover = val;
+
+ /* Calculate total value for all channels */
+ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++)
+ reg_val |= val << MICFIL_DC_CHX_SHIFT(i);
+
+ /* Update DC Remover mode for all channels */
+ ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL,
+ MICFIL_DC_CTRL_CONFIG, reg_val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+
+ ucontrol->value.enumerated.item[0] = micfil->dc_remover;
+
+ return 0;
+}
+
+static int hwvad_put_enable(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+ int val = snd_soc_enum_item_to_val(e, item[0]);
+
+ micfil->vad_enabled = val;
+
+ return 0;
+}
+
+static int hwvad_get_enable(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+
+ ucontrol->value.enumerated.item[0] = micfil->vad_enabled;
+
+ return 0;
+}
+
+static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+ int val = snd_soc_enum_item_to_val(e, item[0]);
+
+ /* 0 - Envelope-based Mode
+ * 1 - Energy-based Mode
+ */
+ micfil->vad_init_mode = val;
+
+ return 0;
+}
+
+static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+
+ ucontrol->value.enumerated.item[0] = micfil->vad_init_mode;
+
+ return 0;
+}
+
+static int hwvad_detected(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
+
+ ucontrol->value.enumerated.item[0] = micfil->vad_detected;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new fsl_micfil_range_controls[] = {
+ SOC_SINGLE_EXT("CH0 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH1 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH2 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH3 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH4 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH5 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH6 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+ SOC_SINGLE_EXT("CH7 Range", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0,
+ snd_soc_get_volsw, micfil_range_set),
+};
+
+static const struct snd_kcontrol_new fsl_micfil_volume_sx_controls[] = {
+ SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(1), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(2), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(3), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(4), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(5), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv),
+ SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv),
+};
+
+static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
+ SOC_ENUM_EXT("MICFIL Quality Select",
+ fsl_micfil_quality_enum,
+ micfil_quality_get, micfil_quality_set),
+ SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum,
+ hwvad_get_enable, hwvad_put_enable),
+ SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum,
+ hwvad_get_init_mode, hwvad_put_init_mode),
+ SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum),
+ SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0),
+ SOC_SINGLE("HWVAD ZCD Auto Threshold Switch",
+ REG_MICFIL_VAD0_ZCD, 2, 1, 0),
+ SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum,
+ micfil_get_dc_remover_state, micfil_put_dc_remover_state),
+ SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0),
+ SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0),
+ SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0),
+ SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0),
+ SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0),
+ SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0),
+ SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0),
+ SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0),
+ SOC_SINGLE("HWVAD ZCD And Behavior Switch",
+ REG_MICFIL_VAD0_ZCD, 4, 1, 0),
+ SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL),
+};
+
+static int fsl_micfil_use_verid(struct device *dev)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (!micfil->soc->use_verid)
+ return 0;
+
+ ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "VERID: 0x%016X\n", val);
+
+ micfil->verid.version = val &
+ (MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK);
+ micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT;
+ micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK;
+
+ ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "PARAM: 0x%016X\n", val);
+
+ micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >>
+ MICFIL_PARAM_NUM_HWVAD_SHIFT;
+ micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD;
+ micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE;
+ micfil->param.hwvad = val & MICFIL_PARAM_HWVAD;
+ micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS;
+ micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS;
+ micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER;
+ micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH;
+ micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >>
+ MICFIL_PARAM_FIFO_PTRWID_SHIFT;
+ micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >>
+ MICFIL_PARAM_NPAIR_SHIFT;
+
+ return 0;
+}
+
+/* The SRES is a self-negated bit which provides the CPU with the
+ * capability to initialize the PDM Interface module through the
+ * slave-bus interface. This bit always reads as zero, and this
+ * bit is only effective when MDIS is cleared
+ */
+static int fsl_micfil_reset(struct device *dev)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_MDIS);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_SRES);
+ if (ret)
+ return ret;
+
+ /*
+ * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined
+ * as non-volatile register, so SRES still remain in regmap
+ * cache after set, that every update of REG_MICFIL_CTRL1,
+ * software reset happens. so clear it explicitly.
+ */
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_SRES);
+ if (ret)
+ return ret;
+
+ /*
+ * Set SRES should clear CHnF flags, But even add delay here
+ * the CHnF may not be cleared sometimes, so clear CHnF explicitly.
+ */
+ ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_micfil_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
+
+ if (!micfil) {
+ dev_err(dai->dev, "micfil dai priv_data not set\n");
+ return -EINVAL;
+ }
+
+ if (micfil->constraint_rates.count > 0)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &micfil->constraint_rates);
+
+ return 0;
+}
+
+/* Enable/disable hwvad interrupts */
+static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable)
+{
+ u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0;
+ u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0;
+
+ /* Voice Activity Detector Error Interruption */
+ regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_ERIE, vaderie_reg);
+
+ /* Voice Activity Detector Interruption */
+ regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_IE, vadie_reg);
+
+ return 0;
+}
+
+/* Configuration done only in energy-based initialization mode */
+static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil)
+{
+ /* Keep the VADFRENDIS bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
+ MICFIL_VAD0_CTRL2_FRENDIS);
+
+ /* Keep the VADPREFEN bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
+ MICFIL_VAD0_CTRL2_PREFEN);
+
+ /* Keep the VADSFILEN bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
+ MICFIL_VAD0_SCONFIG_SFILEN);
+
+ /* Keep the VADSMAXEN bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
+ MICFIL_VAD0_SCONFIG_SMAXEN);
+
+ /* Keep the VADNFILAUTO bitfield asserted. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NFILAUT);
+
+ /* Keep the VADNMINEN bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NMINEN);
+
+ /* Keep the VADNDECEN bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NDECEN);
+
+ /* Keep the VADNOREN bitfield cleared. */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NOREN);
+
+ return 0;
+}
+
+/* Configuration done only in envelope-based initialization mode */
+static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil)
+{
+ /* Assert the VADFRENDIS bitfield */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
+ MICFIL_VAD0_CTRL2_FRENDIS);
+
+ /* Assert the VADPREFEN bitfield. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
+ MICFIL_VAD0_CTRL2_PREFEN);
+
+ /* Assert the VADSFILEN bitfield. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
+ MICFIL_VAD0_SCONFIG_SFILEN);
+
+ /* Assert the VADSMAXEN bitfield. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
+ MICFIL_VAD0_SCONFIG_SMAXEN);
+
+ /* Clear the VADNFILAUTO bitfield */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NFILAUT);
+
+ /* Assert the VADNMINEN bitfield. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NMINEN);
+
+ /* Assert the VADNDECEN bitfield. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NDECEN);
+
+ /* Assert VADNOREN bitfield. */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
+ MICFIL_VAD0_NCONFIG_NOREN);
+
+ return 0;
+}
+
+/*
+ * Hardware Voice Active Detection: The HWVAD takes data from the input
+ * of a selected PDM microphone to detect if there is any
+ * voice activity. When a voice activity is detected, an interrupt could
+ * be delivered to the system. Initialization in section 8.4:
+ * Can work in two modes:
+ * -> Eneveope-based mode (section 8.4.1)
+ * -> Energy-based mode (section 8.4.2)
+ *
+ * It is important to remark that the HWVAD detector could be enabled
+ * or reset only when the MICFIL isn't running i.e. when the BSY_FIL
+ * bit in STAT register is cleared
+ */
+static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil)
+{
+ int ret;
+
+ micfil->vad_detected = 0;
+
+ /* envelope-based specific initialization */
+ if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE)
+ ret = fsl_micfil_init_hwvad_envelope_mode(micfil);
+ else
+ ret = fsl_micfil_init_hwvad_energy_mode(micfil);
+ if (ret)
+ return ret;
+
+ /* Voice Activity Detector Internal Filters Initialization*/
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_ST10);
+
+ /* Voice Activity Detector Internal Filter */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_ST10);
+
+ /* Enable Interrupts */
+ ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1);
+ if (ret)
+ return ret;
+
+ /* Voice Activity Detector Reset */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_RST);
+
+ /* Voice Activity Detector Enabled */
+ regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_EN);
+
+ return 0;
+}
+
+static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil)
+{
+ struct device *dev = &micfil->pdev->dev;
+ int ret = 0;
+
+ /* Disable HWVAD */
+ regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_EN);
+
+ /* Disable hwvad interrupts */
+ ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0);
+ if (ret)
+ dev_err(dev, "Failed to disable interrupts\n");
+
+ return ret;
+}
+
+static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = &micfil->pdev->dev;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = fsl_micfil_reset(dev);
+ if (ret) {
+ dev_err(dev, "failed to soft reset\n");
+ return ret;
+ }
+
+ /* DMA Interrupt Selection - DISEL bits
+ * 00 - DMA and IRQ disabled
+ * 01 - DMA req enabled
+ * 10 - IRQ enabled
+ * 11 - reserved
+ */
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_DISEL,
+ FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA));
+ if (ret)
+ return ret;
+
+ /* Enable the module */
+ ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_PDMIEN | MICFIL_CTRL1_ERREN);
+ if (ret)
+ return ret;
+
+ if (micfil->vad_enabled && !micfil->dec_bypass)
+ fsl_micfil_hwvad_enable(micfil);
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (micfil->vad_enabled && !micfil->dec_bypass)
+ fsl_micfil_hwvad_disable(micfil);
+
+ /* Disable the module */
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_PDMIEN | MICFIL_CTRL1_ERREN);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_DISEL,
+ FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE));
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate)
+{
+ struct device *dev = &micfil->pdev->dev;
+ u64 ratio = sample_rate;
+ struct clk *clk;
+ int ret;
+
+ /* Get root clock */
+ clk = micfil->mclk;
+
+ /* Disable clock first, for it was enabled by pm_runtime */
+ fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk,
+ micfil->pll11k_clk, ratio);
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int rate = params_rate(params);
+ int clk_div = 8, mclk_rate, div_multiply_k;
+ int osr = MICFIL_OSR_DEFAULT;
+ int ret;
+
+ /* 1. Disable the module */
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_PDMIEN);
+ if (ret)
+ return ret;
+
+ /* enable channels */
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ 0xFF, ((1 << channels) - 1));
+ if (ret)
+ return ret;
+
+ ret = fsl_micfil_reparent_rootclk(micfil, rate);
+ if (ret)
+ return ret;
+
+ micfil->mclk_flag = true;
+
+ /* floor(K * CLKDIV) */
+ switch (micfil->quality) {
+ case QUALITY_HIGH:
+ div_multiply_k = clk_div >> 1;
+ break;
+ case QUALITY_LOW:
+ case QUALITY_VLOW1:
+ div_multiply_k = clk_div << 1;
+ break;
+ case QUALITY_VLOW2:
+ div_multiply_k = clk_div << 2;
+ break;
+ case QUALITY_MEDIUM:
+ case QUALITY_VLOW0:
+ default:
+ div_multiply_k = clk_div;
+ break;
+ }
+
+ if (format == SNDRV_PCM_FORMAT_DSD_U32_LE) {
+ micfil->dec_bypass = true;
+ /*
+ * According to equation 29 in RM:
+ * MCLK_CLK_ROOT = PDM CLK rate * 2 * floor(K * CLKDIV)
+ * PDM CLK rate = rate * physical bit width (32)
+ */
+ mclk_rate = rate * div_multiply_k * 32 * 2;
+ } else {
+ micfil->dec_bypass = false;
+ mclk_rate = rate * clk_div * osr * 8;
+ }
+
+ ret = clk_set_rate(micfil->mclk, mclk_rate);
+ if (ret)
+ return ret;
+
+ ret = micfil_set_quality(micfil);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_DEC_BYPASS,
+ micfil->dec_bypass ? MICFIL_CTRL2_DEC_BYPASS : 0);
+
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR,
+ FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) |
+ FIELD_PREP(MICFIL_CTRL2_CICOSR, 32 - osr));
+
+ /* Configure CIC OSR in VADCICOSR */
+ regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_CICOSR,
+ FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr));
+
+ /* Configure source channel in VADCHSEL */
+ regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
+ MICFIL_VAD0_CTRL1_CHSEL,
+ FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1)));
+
+ micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg;
+ micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg);
+ micfil->sdmacfg.n_fifos_src = channels;
+ micfil->sdmacfg.sw_done = true;
+ micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX;
+ if (micfil->soc->use_edma)
+ micfil->dma_params_rx.maxburst = channels;
+
+ return 0;
+}
+
+static int fsl_micfil_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(micfil->mclk);
+ micfil->mclk_flag = false;
+
+ return 0;
+}
+
+static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev);
+ struct device *dev = cpu_dai->dev;
+ unsigned int val = 0;
+ int ret, i, max_range;
+
+ micfil->quality = micfil->soc->default_quality;
+ micfil->card = cpu_dai->component->card;
+
+ /* set default gain to 2 */
+ if (micfil->soc->volume_sx) {
+ regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222);
+ } else {
+ max_range = micfil_get_max_range(micfil);
+ for (i = 1; i < micfil->soc->fifos; i++)
+ max_range |= max_range << 4;
+ regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, max_range);
+ }
+
+ /* set DC Remover in bypass mode*/
+ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++)
+ val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i);
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL,
+ MICFIL_DC_CTRL_CONFIG, val);
+ if (ret) {
+ dev_err(dev, "failed to set DC Remover mode bits\n");
+ return ret;
+ }
+ micfil->dc_remover = MICFIL_DC_BYPASS;
+
+ snd_soc_dai_init_dma_data(cpu_dai, NULL,
+ &micfil->dma_params_rx);
+
+ /* FIFO Watermark Control - FIFOWMK*/
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL,
+ MICFIL_FIFO_CTRL_FIFOWMK,
+ FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_micfil_component_probe(struct snd_soc_component *component)
+{
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(component);
+
+ if (micfil->soc->volume_sx)
+ snd_soc_add_component_controls(component, fsl_micfil_volume_sx_controls,
+ ARRAY_SIZE(fsl_micfil_volume_sx_controls));
+ else
+ snd_soc_add_component_controls(component, fsl_micfil_range_controls,
+ ARRAY_SIZE(fsl_micfil_range_controls));
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_micfil_dai_ops = {
+ .probe = fsl_micfil_dai_probe,
+ .startup = fsl_micfil_startup,
+ .trigger = fsl_micfil_trigger,
+ .hw_params = fsl_micfil_hw_params,
+ .hw_free = fsl_micfil_hw_free,
+};
+
+static struct snd_soc_dai_driver fsl_micfil_dai = {
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &fsl_micfil_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_micfil_component = {
+ .name = "fsl-micfil-dai",
+ .probe = fsl_micfil_component_probe,
+ .controls = fsl_micfil_snd_controls,
+ .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls),
+ .legacy_dai_naming = 1,
+};
+
+/* REGMAP */
+static const struct reg_default fsl_micfil_reg_defaults[] = {
+ {REG_MICFIL_CTRL1, 0x00000000},
+ {REG_MICFIL_CTRL2, 0x00000000},
+ {REG_MICFIL_STAT, 0x00000000},
+ {REG_MICFIL_FIFO_CTRL, 0x0000001F},
+ {REG_MICFIL_FIFO_STAT, 0x00000000},
+ {REG_MICFIL_DATACH0, 0x00000000},
+ {REG_MICFIL_DATACH1, 0x00000000},
+ {REG_MICFIL_DATACH2, 0x00000000},
+ {REG_MICFIL_DATACH3, 0x00000000},
+ {REG_MICFIL_DATACH4, 0x00000000},
+ {REG_MICFIL_DATACH5, 0x00000000},
+ {REG_MICFIL_DATACH6, 0x00000000},
+ {REG_MICFIL_DATACH7, 0x00000000},
+ {REG_MICFIL_DC_CTRL, 0x00000000},
+ {REG_MICFIL_OUT_CTRL, 0x00000000},
+ {REG_MICFIL_OUT_STAT, 0x00000000},
+ {REG_MICFIL_VAD0_CTRL1, 0x00000000},
+ {REG_MICFIL_VAD0_CTRL2, 0x000A0000},
+ {REG_MICFIL_VAD0_STAT, 0x00000000},
+ {REG_MICFIL_VAD0_SCONFIG, 0x00000000},
+ {REG_MICFIL_VAD0_NCONFIG, 0x80000000},
+ {REG_MICFIL_VAD0_NDATA, 0x00000000},
+ {REG_MICFIL_VAD0_ZCD, 0x00000004},
+};
+
+static const struct reg_default fsl_micfil_reg_defaults_v2[] = {
+ {REG_MICFIL_CTRL1, 0x00000000},
+ {REG_MICFIL_CTRL2, 0x00000000},
+ {REG_MICFIL_STAT, 0x00000000},
+ {REG_MICFIL_FIFO_CTRL, 0x0000001F},
+ {REG_MICFIL_FIFO_STAT, 0x00000000},
+ {REG_MICFIL_DATACH0 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH1 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH2 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH3 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH4 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH5 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH6 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH7 - 0x4, 0x00000000},
+ {REG_MICFIL_DC_CTRL, 0x00000000},
+ {REG_MICFIL_OUT_CTRL, 0x00000000},
+ {REG_MICFIL_OUT_STAT, 0x00000000},
+ {REG_MICFIL_VAD0_CTRL1, 0x00000000},
+ {REG_MICFIL_VAD0_CTRL2, 0x000A0000},
+ {REG_MICFIL_VAD0_STAT, 0x00000000},
+ {REG_MICFIL_VAD0_SCONFIG, 0x00000000},
+ {REG_MICFIL_VAD0_NCONFIG, 0x80000000},
+ {REG_MICFIL_VAD0_NDATA, 0x00000000},
+ {REG_MICFIL_VAD0_ZCD, 0x00000004},
+};
+
+static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ int ofs = micfil->soc->fifo_offset;
+
+ if (reg >= (REG_MICFIL_DATACH0 + ofs) && reg <= (REG_MICFIL_DATACH7 + ofs))
+ return true;
+
+ switch (reg) {
+ case REG_MICFIL_CTRL1:
+ case REG_MICFIL_CTRL2:
+ case REG_MICFIL_STAT:
+ case REG_MICFIL_FIFO_CTRL:
+ case REG_MICFIL_FIFO_STAT:
+ case REG_MICFIL_DC_CTRL:
+ case REG_MICFIL_OUT_CTRL:
+ case REG_MICFIL_OUT_STAT:
+ case REG_MICFIL_VAD0_CTRL1:
+ case REG_MICFIL_VAD0_CTRL2:
+ case REG_MICFIL_VAD0_STAT:
+ case REG_MICFIL_VAD0_SCONFIG:
+ case REG_MICFIL_VAD0_NCONFIG:
+ case REG_MICFIL_VAD0_NDATA:
+ case REG_MICFIL_VAD0_ZCD:
+ return true;
+ case REG_MICFIL_FSYNC_CTRL:
+ case REG_MICFIL_VERID:
+ case REG_MICFIL_PARAM:
+ if (micfil->soc->use_verid)
+ return true;
+ fallthrough;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+
+ switch (reg) {
+ case REG_MICFIL_CTRL1:
+ case REG_MICFIL_CTRL2:
+ case REG_MICFIL_STAT: /* Write 1 to Clear */
+ case REG_MICFIL_FIFO_CTRL:
+ case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */
+ case REG_MICFIL_DC_CTRL:
+ case REG_MICFIL_OUT_CTRL:
+ case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */
+ case REG_MICFIL_VAD0_CTRL1:
+ case REG_MICFIL_VAD0_CTRL2:
+ case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */
+ case REG_MICFIL_VAD0_SCONFIG:
+ case REG_MICFIL_VAD0_NCONFIG:
+ case REG_MICFIL_VAD0_ZCD:
+ return true;
+ case REG_MICFIL_FSYNC_CTRL:
+ if (micfil->soc->use_verid)
+ return true;
+ fallthrough;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ int ofs = micfil->soc->fifo_offset;
+
+ if (reg >= (REG_MICFIL_DATACH0 + ofs) && reg <= (REG_MICFIL_DATACH7 + ofs))
+ return true;
+
+ switch (reg) {
+ case REG_MICFIL_STAT:
+ case REG_MICFIL_FIFO_STAT:
+ case REG_MICFIL_OUT_STAT:
+ case REG_MICFIL_VERID:
+ case REG_MICFIL_PARAM:
+ case REG_MICFIL_VAD0_STAT:
+ case REG_MICFIL_VAD0_NDATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config fsl_micfil_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_MICFIL_VAD0_ZCD,
+ .reg_defaults = fsl_micfil_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults),
+ .readable_reg = fsl_micfil_readable_reg,
+ .volatile_reg = fsl_micfil_volatile_reg,
+ .writeable_reg = fsl_micfil_writeable_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config fsl_micfil_regmap_config_v2 = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_MICFIL_VAD0_ZCD,
+ .reg_defaults = fsl_micfil_reg_defaults_v2,
+ .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults_v2),
+ .readable_reg = fsl_micfil_readable_reg,
+ .volatile_reg = fsl_micfil_volatile_reg,
+ .writeable_reg = fsl_micfil_writeable_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+/* END OF REGMAP */
+
+static irqreturn_t micfil_isr(int irq, void *devid)
+{
+ struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
+ struct platform_device *pdev = micfil->pdev;
+ u32 stat_reg;
+ u32 fifo_stat_reg;
+ u32 ctrl1_reg;
+ bool dma_enabled;
+ int i;
+
+ regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
+ regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg);
+ regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg);
+
+ dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA;
+
+ /* Channel 0-7 Output Data Flags */
+ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) {
+ if (stat_reg & MICFIL_STAT_CHXF(i))
+ dev_dbg(&pdev->dev,
+ "Data available in Data Channel %d\n", i);
+ /* if DMA is not enabled, field must be written with 1
+ * to clear
+ */
+ if (!dma_enabled)
+ regmap_write_bits(micfil->regmap,
+ REG_MICFIL_STAT,
+ MICFIL_STAT_CHXF(i),
+ MICFIL_STAT_CHXF(i));
+ }
+
+ for (i = 0; i < MICFIL_FIFO_NUM; i++) {
+ if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i))
+ dev_dbg(&pdev->dev,
+ "FIFO Overflow Exception flag for channel %d\n",
+ i);
+
+ if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i))
+ dev_dbg(&pdev->dev,
+ "FIFO Underflow Exception flag for channel %d\n",
+ i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t micfil_err_isr(int irq, void *devid)
+{
+ struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
+ struct platform_device *pdev = micfil->pdev;
+ u32 fifo_stat_reg;
+ u32 out_stat_reg;
+ u32 stat_reg;
+
+ regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
+
+ if (stat_reg & MICFIL_STAT_BSY_FIL)
+ dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n");
+
+ if (stat_reg & MICFIL_STAT_FIR_RDY)
+ dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n");
+
+ if (stat_reg & MICFIL_STAT_LOWFREQF) {
+ dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n");
+ regmap_write_bits(micfil->regmap, REG_MICFIL_STAT,
+ MICFIL_STAT_LOWFREQF, MICFIL_STAT_LOWFREQF);
+ }
+
+ regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg);
+ regmap_write_bits(micfil->regmap, REG_MICFIL_FIFO_STAT,
+ fifo_stat_reg, fifo_stat_reg);
+
+ regmap_read(micfil->regmap, REG_MICFIL_OUT_STAT, &out_stat_reg);
+ regmap_write_bits(micfil->regmap, REG_MICFIL_OUT_STAT,
+ out_stat_reg, out_stat_reg);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t voice_detected_fn(int irq, void *devid)
+{
+ struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
+ struct snd_kcontrol *kctl;
+
+ if (!micfil->card)
+ return IRQ_HANDLED;
+
+ kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected");
+ if (!kctl)
+ return IRQ_HANDLED;
+
+ if (micfil->vad_detected)
+ snd_ctl_notify(micfil->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &kctl->id);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t hwvad_isr(int irq, void *devid)
+{
+ struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
+ struct device *dev = &micfil->pdev->dev;
+ u32 vad0_reg;
+ int ret;
+
+ regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg);
+
+ /*
+ * The only difference between MICFIL_VAD0_STAT_EF and
+ * MICFIL_VAD0_STAT_IF is that the former requires Write
+ * 1 to Clear. Since both flags are set, it is enough
+ * to only read one of them
+ */
+ if (vad0_reg & MICFIL_VAD0_STAT_IF) {
+ /* Write 1 to clear */
+ regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT,
+ MICFIL_VAD0_STAT_IF,
+ MICFIL_VAD0_STAT_IF);
+
+ micfil->vad_detected = 1;
+ }
+
+ ret = fsl_micfil_hwvad_disable(micfil);
+ if (ret)
+ dev_err(dev, "Failed to disable hwvad\n");
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t hwvad_err_isr(int irq, void *devid)
+{
+ struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
+ struct device *dev = &micfil->pdev->dev;
+ u32 vad0_reg;
+
+ regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg);
+
+ if (vad0_reg & MICFIL_VAD0_STAT_INSATF)
+ dev_dbg(dev, "voice activity input overflow/underflow detected\n");
+
+ return IRQ_HANDLED;
+}
+
+static int fsl_micfil_runtime_suspend(struct device *dev);
+static int fsl_micfil_runtime_resume(struct device *dev);
+
+static int fsl_micfil_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_micfil *micfil;
+ struct resource *res;
+ void __iomem *regs;
+ int ret, i;
+
+ micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL);
+ if (!micfil)
+ return -ENOMEM;
+
+ micfil->pdev = pdev;
+ strscpy(micfil->name, np->name, sizeof(micfil->name));
+
+ micfil->soc = of_device_get_match_data(&pdev->dev);
+
+ /* ipg_clk is used to control the registers
+ * ipg_clk_app is used to operate the filter
+ */
+ micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app");
+ if (IS_ERR(micfil->mclk)) {
+ dev_err(&pdev->dev, "failed to get core clock: %ld\n",
+ PTR_ERR(micfil->mclk));
+ return PTR_ERR(micfil->mclk);
+ }
+
+ micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk");
+ if (IS_ERR(micfil->busclk)) {
+ dev_err(&pdev->dev, "failed to get ipg clock: %ld\n",
+ PTR_ERR(micfil->busclk));
+ return PTR_ERR(micfil->busclk);
+ }
+
+ fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk,
+ &micfil->pll11k_clk);
+
+ micfil->clk_src[MICFIL_AUDIO_PLL1] = micfil->pll8k_clk;
+ micfil->clk_src[MICFIL_AUDIO_PLL2] = micfil->pll11k_clk;
+ micfil->clk_src[MICFIL_CLK_EXT3] = devm_clk_get(&pdev->dev, "clkext3");
+ if (IS_ERR(micfil->clk_src[MICFIL_CLK_EXT3]))
+ micfil->clk_src[MICFIL_CLK_EXT3] = NULL;
+
+ fsl_asoc_constrain_rates(&micfil->constraint_rates,
+ &fsl_micfil_rate_constraints,
+ micfil->clk_src[MICFIL_AUDIO_PLL1],
+ micfil->clk_src[MICFIL_AUDIO_PLL2],
+ micfil->clk_src[MICFIL_CLK_EXT3],
+ micfil->constraint_rates_list);
+
+ /* init regmap */
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ if (of_device_is_compatible(np, "fsl,imx943-micfil"))
+ micfil->regmap = devm_regmap_init_mmio(&pdev->dev,
+ regs,
+ &fsl_micfil_regmap_config_v2);
+ else
+ micfil->regmap = devm_regmap_init_mmio(&pdev->dev,
+ regs,
+ &fsl_micfil_regmap_config);
+ if (IS_ERR(micfil->regmap)) {
+ dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n",
+ PTR_ERR(micfil->regmap));
+ return PTR_ERR(micfil->regmap);
+ }
+
+ /* dataline mask for RX */
+ ret = of_property_read_u32_index(np,
+ "fsl,dataline",
+ 0,
+ &micfil->dataline);
+ if (ret)
+ micfil->dataline = 1;
+
+ if (micfil->dataline & ~micfil->soc->dataline) {
+ dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n",
+ micfil->soc->dataline);
+ return -EINVAL;
+ }
+
+ /* get IRQs */
+ for (i = 0; i < MICFIL_IRQ_LINES; i++) {
+ micfil->irq[i] = platform_get_irq(pdev, i);
+ if (micfil->irq[i] < 0)
+ return micfil->irq[i];
+ }
+
+ /* Digital Microphone interface interrupt */
+ ret = devm_request_irq(&pdev->dev, micfil->irq[0],
+ micfil_isr, IRQF_SHARED,
+ micfil->name, micfil);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim mic interface irq %u\n",
+ micfil->irq[0]);
+ return ret;
+ }
+
+ /* Digital Microphone interface error interrupt */
+ ret = devm_request_irq(&pdev->dev, micfil->irq[1],
+ micfil_err_isr, IRQF_SHARED,
+ micfil->name, micfil);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n",
+ micfil->irq[1]);
+ return ret;
+ }
+
+ /* Digital Microphone interface voice activity detector event */
+ ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2],
+ hwvad_isr, voice_detected_fn,
+ IRQF_SHARED, micfil->name, micfil);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n",
+ micfil->irq[0]);
+ return ret;
+ }
+
+ /* Digital Microphone interface voice activity detector error */
+ ret = devm_request_irq(&pdev->dev, micfil->irq[3],
+ hwvad_err_isr, IRQF_SHARED,
+ micfil->name, micfil);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n",
+ micfil->irq[1]);
+ return ret;
+ }
+
+ micfil->dma_params_rx.chan_name = "rx";
+ micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0 + micfil->soc->fifo_offset;
+ micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX;
+
+ platform_set_drvdata(pdev, micfil);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = fsl_micfil_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_pm_get_sync;
+
+ /* Get micfil version */
+ ret = fsl_micfil_use_verid(&pdev->dev);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret);
+
+ ret = pm_runtime_put_sync(&pdev->dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto err_pm_get_sync;
+
+ regcache_cache_only(micfil->regmap, true);
+
+ /*
+ * Register platform component before registering cpu dai for there
+ * is not defer probe for platform component in snd_soc_add_pcm_runtime().
+ */
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to pcm register\n");
+ goto err_pm_disable;
+ }
+
+ fsl_micfil_dai.capture.formats = micfil->soc->formats;
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component,
+ &fsl_micfil_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register component %s\n",
+ fsl_micfil_component.name);
+ goto err_pm_disable;
+ }
+
+ return ret;
+
+err_pm_get_sync:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_micfil_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void fsl_micfil_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int fsl_micfil_runtime_suspend(struct device *dev)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+
+ regcache_cache_only(micfil->regmap, true);
+
+ if (micfil->mclk_flag)
+ clk_disable_unprepare(micfil->mclk);
+ clk_disable_unprepare(micfil->busclk);
+
+ return 0;
+}
+
+static int fsl_micfil_runtime_resume(struct device *dev)
+{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(micfil->busclk);
+ if (ret < 0)
+ return ret;
+
+ if (micfil->mclk_flag) {
+ ret = clk_prepare_enable(micfil->mclk);
+ if (ret < 0) {
+ clk_disable_unprepare(micfil->busclk);
+ return ret;
+ }
+ }
+
+ regcache_cache_only(micfil->regmap, false);
+ regcache_mark_dirty(micfil->regmap);
+ regcache_sync(micfil->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops fsl_micfil_pm_ops = {
+ RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, fsl_micfil_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_micfil_driver = {
+ .probe = fsl_micfil_probe,
+ .remove = fsl_micfil_remove,
+ .driver = {
+ .name = "fsl-micfil-dai",
+ .pm = pm_ptr(&fsl_micfil_pm_ops),
+ .of_match_table = fsl_micfil_dt_ids,
+ },
+};
+module_platform_driver(fsl_micfil_driver);
+
+MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>");
+MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h
new file mode 100644
index 000000000000..fdfe4e7125bc
--- /dev/null
+++ b/sound/soc/fsl/fsl_micfil.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * PDM Microphone Interface for the NXP i.MX SoC
+ * Copyright 2018 NXP
+ */
+
+#ifndef _FSL_MICFIL_H
+#define _FSL_MICFIL_H
+
+/* MICFIL Register Map */
+#define REG_MICFIL_CTRL1 0x00
+#define REG_MICFIL_CTRL2 0x04
+#define REG_MICFIL_STAT 0x08
+#define REG_MICFIL_FIFO_CTRL 0x10
+#define REG_MICFIL_FIFO_STAT 0x14
+#define REG_MICFIL_DATACH0 0x24
+#define REG_MICFIL_DATACH1 0x28
+#define REG_MICFIL_DATACH2 0x2C
+#define REG_MICFIL_DATACH3 0x30
+#define REG_MICFIL_DATACH4 0x34
+#define REG_MICFIL_DATACH5 0x38
+#define REG_MICFIL_DATACH6 0x3C
+#define REG_MICFIL_DATACH7 0x40
+#define REG_MICFIL_DC_CTRL 0x64
+#define REG_MICFIL_OUT_CTRL 0x74
+#define REG_MICFIL_OUT_STAT 0x7C
+#define REG_MICFIL_FSYNC_CTRL 0x80
+#define REG_MICFIL_VERID 0x84
+#define REG_MICFIL_PARAM 0x88
+#define REG_MICFIL_VAD0_CTRL1 0x90
+#define REG_MICFIL_VAD0_CTRL2 0x94
+#define REG_MICFIL_VAD0_STAT 0x98
+#define REG_MICFIL_VAD0_SCONFIG 0x9C
+#define REG_MICFIL_VAD0_NCONFIG 0xA0
+#define REG_MICFIL_VAD0_NDATA 0xA4
+#define REG_MICFIL_VAD0_ZCD 0xA8
+
+/* MICFIL Control Register 1 -- REG_MICFILL_CTRL1 0x00 */
+#define MICFIL_CTRL1_MDIS BIT(31)
+#define MICFIL_CTRL1_DOZEN BIT(30)
+#define MICFIL_CTRL1_PDMIEN BIT(29)
+#define MICFIL_CTRL1_DBG BIT(28)
+#define MICFIL_CTRL1_SRES BIT(27)
+#define MICFIL_CTRL1_DBGE BIT(26)
+#define MICFIL_CTRL1_DECFILS BIT(20)
+#define MICFIL_CTRL1_FSYNCEN BIT(16)
+
+#define MICFIL_CTRL1_DISEL_DISABLE 0
+#define MICFIL_CTRL1_DISEL_DMA 1
+#define MICFIL_CTRL1_DISEL_IRQ 2
+#define MICFIL_CTRL1_DISEL GENMASK(25, 24)
+#define MICFIL_CTRL1_ERREN BIT(23)
+#define MICFIL_CTRL1_CHEN(ch) BIT(ch)
+
+/* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */
+#define MICFIL_CTRL2_DEC_BYPASS BIT(31)
+#define MICFIL_CTRL2_QSEL_SHIFT 25
+#define MICFIL_CTRL2_QSEL GENMASK(27, 25)
+#define MICFIL_QSEL_MEDIUM_QUALITY 0
+#define MICFIL_QSEL_HIGH_QUALITY 1
+#define MICFIL_QSEL_LOW_QUALITY 7
+#define MICFIL_QSEL_VLOW0_QUALITY 6
+#define MICFIL_QSEL_VLOW1_QUALITY 5
+#define MICFIL_QSEL_VLOW2_QUALITY 4
+
+#define MICFIL_CTRL2_CICOSR GENMASK(20, 16)
+#define MICFIL_CTRL2_CLKDIV GENMASK(7, 0)
+
+/* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */
+#define MICFIL_STAT_BSY_FIL BIT(31)
+#define MICFIL_STAT_FIR_RDY BIT(30)
+#define MICFIL_STAT_LOWFREQF BIT(29)
+#define MICFIL_STAT_CHXF(ch) BIT(ch)
+
+/* MICFIL FIFO Control Register -- REG_MICFIL_FIFO_CTRL 0x10 */
+#define MICFIL_FIFO_CTRL_FIFOWMK GENMASK(4, 0)
+
+/* MICFIL FIFO Status Register -- REG_MICFIL_FIFO_STAT 0x14 */
+#define MICFIL_FIFO_STAT_FIFOX_OVER(ch) BIT(ch)
+#define MICFIL_FIFO_STAT_FIFOX_UNDER(ch) BIT((ch) + 8)
+
+/* MICFIL DC Remover Control Register -- REG_MICFIL_DC_CTRL */
+#define MICFIL_DC_CTRL_CONFIG GENMASK(15, 0)
+#define MICFIL_DC_CHX_SHIFT(ch) ((ch) << 1)
+#define MICFIL_DC_CHX(ch) GENMASK((((ch) << 1) + 1), ((ch) << 1))
+#define MICFIL_DC_CUTOFF_21HZ 0
+#define MICFIL_DC_CUTOFF_83HZ 1
+#define MICFIL_DC_CUTOFF_152Hz 2
+#define MICFIL_DC_BYPASS 3
+
+/* MICFIL VERID Register -- REG_MICFIL_VERID */
+#define MICFIL_VERID_MAJOR_SHIFT 24
+#define MICFIL_VERID_MAJOR_MASK GENMASK(31, 24)
+#define MICFIL_VERID_MINOR_SHIFT 16
+#define MICFIL_VERID_MINOR_MASK GENMASK(23, 16)
+#define MICFIL_VERID_FEATURE_SHIFT 0
+#define MICFIL_VERID_FEATURE_MASK GENMASK(15, 0)
+
+/* MICFIL PARAM Register -- REG_MICFIL_PARAM */
+#define MICFIL_PARAM_NUM_HWVAD_SHIFT 24
+#define MICFIL_PARAM_NUM_HWVAD_MASK GENMASK(27, 24)
+#define MICFIL_PARAM_HWVAD_ZCD BIT(19)
+#define MICFIL_PARAM_HWVAD_ENERGY_MODE BIT(17)
+#define MICFIL_PARAM_HWVAD BIT(16)
+#define MICFIL_PARAM_DC_OUT_BYPASS BIT(11)
+#define MICFIL_PARAM_DC_IN_BYPASS BIT(10)
+#define MICFIL_PARAM_LOW_POWER BIT(9)
+#define MICFIL_PARAM_FIL_OUT_WIDTH BIT(8)
+#define MICFIL_PARAM_FIFO_PTRWID_SHIFT 4
+#define MICFIL_PARAM_FIFO_PTRWID_MASK GENMASK(7, 4)
+#define MICFIL_PARAM_NPAIR_SHIFT 0
+#define MICFIL_PARAM_NPAIR_MASK GENMASK(3, 0)
+
+/* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/
+#define MICFIL_VAD0_CTRL1_CHSEL GENMASK(26, 24)
+#define MICFIL_VAD0_CTRL1_CICOSR GENMASK(19, 16)
+#define MICFIL_VAD0_CTRL1_INITT GENMASK(12, 8)
+#define MICFIL_VAD0_CTRL1_ST10 BIT(4)
+#define MICFIL_VAD0_CTRL1_ERIE BIT(3)
+#define MICFIL_VAD0_CTRL1_IE BIT(2)
+#define MICFIL_VAD0_CTRL1_RST BIT(1)
+#define MICFIL_VAD0_CTRL1_EN BIT(0)
+
+/* MICFIL HWVAD0 Control 2 Register -- REG_MICFIL_VAD0_CTRL2*/
+#define MICFIL_VAD0_CTRL2_FRENDIS BIT(31)
+#define MICFIL_VAD0_CTRL2_PREFEN BIT(30)
+#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(28)
+#define MICFIL_VAD0_CTRL2_FRAMET GENMASK(21, 16)
+#define MICFIL_VAD0_CTRL2_INPGAIN GENMASK(11, 8)
+#define MICFIL_VAD0_CTRL2_HPF GENMASK(1, 0)
+
+/* MICFIL HWVAD0 Signal CONFIG Register -- REG_MICFIL_VAD0_SCONFIG */
+#define MICFIL_VAD0_SCONFIG_SFILEN BIT(31)
+#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(30)
+#define MICFIL_VAD0_SCONFIG_SGAIN GENMASK(3, 0)
+
+/* MICFIL HWVAD0 Noise CONFIG Register -- REG_MICFIL_VAD0_NCONFIG */
+#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(31)
+#define MICFIL_VAD0_NCONFIG_NMINEN BIT(30)
+#define MICFIL_VAD0_NCONFIG_NDECEN BIT(29)
+#define MICFIL_VAD0_NCONFIG_NOREN BIT(28)
+#define MICFIL_VAD0_NCONFIG_NFILADJ GENMASK(12, 8)
+#define MICFIL_VAD0_NCONFIG_NGAIN GENMASK(3, 0)
+
+/* MICFIL HWVAD0 Zero-Crossing Detector - REG_MICFIL_VAD0_ZCD */
+#define MICFIL_VAD0_ZCD_ZCDTH GENMASK(25, 16)
+#define MICFIL_VAD0_ZCD_ZCDADJ GENMASK(11, 8)
+#define MICFIL_VAD0_ZCD_ZCDAND BIT(4)
+#define MICFIL_VAD0_ZCD_ZCDAUT BIT(2)
+#define MICFIL_VAD0_ZCD_ZCDEN BIT(0)
+
+/* MICFIL HWVAD0 Status Register - REG_MICFIL_VAD0_STAT */
+#define MICFIL_VAD0_STAT_INITF BIT(31)
+#define MICFIL_VAD0_STAT_INSATF BIT(16)
+#define MICFIL_VAD0_STAT_EF BIT(15)
+#define MICFIL_VAD0_STAT_IF BIT(0)
+
+/* MICFIL Output Control Register */
+#define MICFIL_OUTGAIN_CHX_SHIFT(v) (4 * (v))
+
+/* Constants */
+#define MICFIL_OUTPUT_CHANNELS 8
+#define MICFIL_FIFO_NUM 8
+
+#define FIFO_PTRWID 3
+#define FIFO_LEN BIT(FIFO_PTRWID)
+
+#define MICFIL_IRQ_LINES 4
+#define MICFIL_MAX_RETRY 25
+#define MICFIL_SLEEP_MIN 90000 /* in us */
+#define MICFIL_SLEEP_MAX 100000 /* in us */
+#define MICFIL_DMA_MAXBURST_RX 6
+
+/* HWVAD Constants */
+#define MICFIL_HWVAD_ENVELOPE_MODE 0
+#define MICFIL_HWVAD_ENERGY_MODE 1
+
+/**
+ * struct fsl_micfil_verid - version id data
+ * @version: version number
+ * @feature: feature specification number
+ */
+struct fsl_micfil_verid {
+ u32 version;
+ u32 feature;
+};
+
+/**
+ * struct fsl_micfil_param - parameter data
+ * @hwvad_num: the number of HWVADs
+ * @hwvad_zcd: HWVAD zero-cross detector is active
+ * @hwvad_energy_mode: HWVAD energy mode is active
+ * @hwvad: HWVAD is active
+ * @dc_out_bypass: points out if the output DC remover is disabled
+ * @dc_in_bypass: points out if the input DC remover is disabled
+ * @low_power: low power decimation filter
+ * @fil_out_width: filter output width
+ * @fifo_ptrwid: FIFO pointer width
+ * @npair: number of microphone pairs
+ */
+struct fsl_micfil_param {
+ u32 hwvad_num;
+ bool hwvad_zcd;
+ bool hwvad_energy_mode;
+ bool hwvad;
+ bool dc_out_bypass;
+ bool dc_in_bypass;
+ bool low_power;
+ bool fil_out_width;
+ u32 fifo_ptrwid;
+ u32 npair;
+};
+
+#endif /* _FSL_MICFIL_H */
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
new file mode 100644
index 000000000000..901f840df904
--- /dev/null
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC IMX MQS driver
+//
+// Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
+// Copyright 2019 NXP
+
+#include <linux/clk.h>
+#include <linux/firmware/imx/sm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#define REG_MQS_CTRL 0x00
+
+#define MQS_EN_MASK (0x1 << 28)
+#define MQS_EN_SHIFT (28)
+#define MQS_SW_RST_MASK (0x1 << 24)
+#define MQS_SW_RST_SHIFT (24)
+#define MQS_OVERSAMPLE_MASK (0x1 << 20)
+#define MQS_OVERSAMPLE_SHIFT (20)
+#define MQS_CLK_DIV_MASK (0xFF << 0)
+#define MQS_CLK_DIV_SHIFT (0)
+
+enum reg_type {
+ TYPE_REG_OWN, /* module own register space */
+ TYPE_REG_GPR, /* register in GPR space */
+ TYPE_REG_SM, /* System Manager controls the register */
+};
+
+/**
+ * struct fsl_mqs_soc_data - soc specific data
+ *
+ * @type: control register space type
+ * @sm_index: index from definition in system manager
+ * @ctrl_off: control register offset
+ * @en_mask: enable bit mask
+ * @en_shift: enable bit shift
+ * @rst_mask: reset bit mask
+ * @rst_shift: reset bit shift
+ * @osr_mask: oversample bit mask
+ * @osr_shift: oversample bit shift
+ * @div_mask: clock divider mask
+ * @div_shift: clock divider bit shift
+ */
+struct fsl_mqs_soc_data {
+ enum reg_type type;
+ int sm_index;
+ int ctrl_off;
+ int en_mask;
+ int en_shift;
+ int rst_mask;
+ int rst_shift;
+ int osr_mask;
+ int osr_shift;
+ int div_mask;
+ int div_shift;
+};
+
+/* codec private data */
+struct fsl_mqs {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *ipg;
+ const struct fsl_mqs_soc_data *soc;
+
+ unsigned int reg_mqs_ctrl;
+};
+
+#define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static int fsl_mqs_sm_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsl_mqs *mqs_priv = context;
+ int num = 1;
+
+ if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
+ mqs_priv->soc->ctrl_off == reg)
+ return scmi_imx_misc_ctrl_get(mqs_priv->soc->sm_index, &num, val);
+
+ return -EINVAL;
+};
+
+static int fsl_mqs_sm_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsl_mqs *mqs_priv = context;
+
+ if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
+ mqs_priv->soc->ctrl_off == reg)
+ return scmi_imx_misc_ctrl_set(mqs_priv->soc->sm_index, val);
+
+ return -EINVAL;
+};
+
+static int fsl_mqs_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
+ unsigned long mclk_rate;
+ int div, res;
+ int lrclk;
+
+ mclk_rate = clk_get_rate(mqs_priv->mclk);
+ lrclk = params_rate(params);
+
+ /*
+ * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
+ * if repeat_rate is 8, mqs can achieve better quality.
+ * oversample rate is fix to 32 currently.
+ */
+ div = mclk_rate / (32 * lrclk * 2 * 8);
+ res = mclk_rate % (32 * lrclk * 2 * 8);
+
+ if (res == 0 && div > 0 && div <= 256) {
+ regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
+ mqs_priv->soc->div_mask,
+ (div - 1) << mqs_priv->soc->div_shift);
+ regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
+ mqs_priv->soc->osr_mask, 0);
+ } else {
+ dev_err(component->dev, "can't get proper divider\n");
+ }
+
+ return 0;
+}
+
+static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ /* Only LEFT_J & SLAVE mode is supported. */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_mqs_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
+ mqs_priv->soc->en_mask,
+ 1 << mqs_priv->soc->en_shift);
+ return 0;
+}
+
+static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
+ mqs_priv->soc->en_mask, 0);
+}
+
+static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
+ .idle_bias_on = 1,
+};
+
+static const struct snd_soc_dai_ops fsl_mqs_dai_ops = {
+ .startup = fsl_mqs_startup,
+ .shutdown = fsl_mqs_shutdown,
+ .hw_params = fsl_mqs_hw_params,
+ .set_fmt = fsl_mqs_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver fsl_mqs_dai = {
+ .name = "fsl-mqs-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_MQS_RATES,
+ .formats = FSL_MQS_FORMATS,
+ },
+ .ops = &fsl_mqs_dai_ops,
+};
+
+static const struct regmap_config fsl_mqs_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = REG_MQS_CTRL,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config fsl_mqs_sm_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_read = fsl_mqs_sm_read,
+ .reg_write = fsl_mqs_sm_write,
+};
+
+static int fsl_mqs_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *gpr_np = NULL;
+ struct fsl_mqs *mqs_priv;
+ void __iomem *regs;
+ int ret;
+
+ mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL);
+ if (!mqs_priv)
+ return -ENOMEM;
+
+ /* On i.MX6sx the MQS control register is in GPR domain
+ * But in i.MX8QM/i.MX8QXP the control register is moved
+ * to its own domain.
+ */
+ mqs_priv->soc = of_device_get_match_data(&pdev->dev);
+
+ if (mqs_priv->soc->type == TYPE_REG_GPR) {
+ gpr_np = of_parse_phandle(np, "gpr", 0);
+ if (!gpr_np) {
+ dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
+ return -EINVAL;
+ }
+
+ mqs_priv->regmap = syscon_node_to_regmap(gpr_np);
+ of_node_put(gpr_np);
+ if (IS_ERR(mqs_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to get gpr regmap\n");
+ return PTR_ERR(mqs_priv->regmap);
+ }
+ } else if (mqs_priv->soc->type == TYPE_REG_SM) {
+ mqs_priv->regmap = devm_regmap_init(&pdev->dev,
+ NULL,
+ mqs_priv,
+ &fsl_mqs_sm_regmap);
+ if (IS_ERR(mqs_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap: %ld\n",
+ PTR_ERR(mqs_priv->regmap));
+ return PTR_ERR(mqs_priv->regmap);
+ }
+ } else {
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "core",
+ regs,
+ &fsl_mqs_regmap_config);
+ if (IS_ERR(mqs_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap: %ld\n",
+ PTR_ERR(mqs_priv->regmap));
+ return PTR_ERR(mqs_priv->regmap);
+ }
+
+ mqs_priv->ipg = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(mqs_priv->ipg)) {
+ dev_err(&pdev->dev, "failed to get the clock: %ld\n",
+ PTR_ERR(mqs_priv->ipg));
+ return PTR_ERR(mqs_priv->ipg);
+ }
+ }
+
+ mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(mqs_priv->mclk)) {
+ dev_err(&pdev->dev, "failed to get the clock: %ld\n",
+ PTR_ERR(mqs_priv->mclk));
+ return PTR_ERR(mqs_priv->mclk);
+ }
+
+ dev_set_drvdata(&pdev->dev, mqs_priv);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
+ &fsl_mqs_dai, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void fsl_mqs_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int fsl_mqs_runtime_resume(struct device *dev)
+{
+ struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(mqs_priv->ipg);
+ if (ret) {
+ dev_err(dev, "failed to enable ipg clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(mqs_priv->mclk);
+ if (ret) {
+ dev_err(dev, "failed to enable mclk clock\n");
+ clk_disable_unprepare(mqs_priv->ipg);
+ return ret;
+ }
+
+ regmap_write(mqs_priv->regmap, mqs_priv->soc->ctrl_off, mqs_priv->reg_mqs_ctrl);
+ return 0;
+}
+
+static int fsl_mqs_runtime_suspend(struct device *dev)
+{
+ struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
+
+ regmap_read(mqs_priv->regmap, mqs_priv->soc->ctrl_off, &mqs_priv->reg_mqs_ctrl);
+
+ clk_disable_unprepare(mqs_priv->mclk);
+ clk_disable_unprepare(mqs_priv->ipg);
+
+ return 0;
+}
+
+static const struct dev_pm_ops fsl_mqs_pm_ops = {
+ RUNTIME_PM_OPS(fsl_mqs_runtime_suspend, fsl_mqs_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = {
+ .type = TYPE_REG_OWN,
+ .ctrl_off = REG_MQS_CTRL,
+ .en_mask = MQS_EN_MASK,
+ .en_shift = MQS_EN_SHIFT,
+ .rst_mask = MQS_SW_RST_MASK,
+ .rst_shift = MQS_SW_RST_SHIFT,
+ .osr_mask = MQS_OVERSAMPLE_MASK,
+ .osr_shift = MQS_OVERSAMPLE_SHIFT,
+ .div_mask = MQS_CLK_DIV_MASK,
+ .div_shift = MQS_CLK_DIV_SHIFT,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = {
+ .type = TYPE_REG_GPR,
+ .ctrl_off = IOMUXC_GPR2,
+ .en_mask = IMX6SX_GPR2_MQS_EN_MASK,
+ .en_shift = IMX6SX_GPR2_MQS_EN_SHIFT,
+ .rst_mask = IMX6SX_GPR2_MQS_SW_RST_MASK,
+ .rst_shift = IMX6SX_GPR2_MQS_SW_RST_SHIFT,
+ .osr_mask = IMX6SX_GPR2_MQS_OVERSAMPLE_MASK,
+ .osr_shift = IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT,
+ .div_mask = IMX6SX_GPR2_MQS_CLK_DIV_MASK,
+ .div_shift = IMX6SX_GPR2_MQS_CLK_DIV_SHIFT,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = {
+ .type = TYPE_REG_GPR,
+ .ctrl_off = 0x20,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = {
+ .type = TYPE_REG_SM,
+ .sm_index = SCMI_IMX95_CTRL_MQS1_SETTINGS,
+ .ctrl_off = 0x88,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = {
+ .type = TYPE_REG_GPR,
+ .ctrl_off = 0x0,
+ .en_mask = BIT(2),
+ .en_shift = 2,
+ .rst_mask = BIT(3),
+ .rst_shift = 3,
+ .osr_mask = BIT(4),
+ .osr_shift = 4,
+ .div_mask = GENMASK(16, 9),
+ .div_shift = 9,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = {
+ .type = TYPE_REG_SM,
+ .sm_index = SCMI_IMX94_CTRL_MQS1_SETTINGS,
+ .ctrl_off = 0x88,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = {
+ .type = TYPE_REG_SM,
+ .sm_index = SCMI_IMX94_CTRL_MQS2_SETTINGS,
+ .ctrl_off = 0x10,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
+static const struct of_device_id fsl_mqs_dt_ids[] = {
+ { .compatible = "fsl,imx8qm-mqs", .data = &fsl_mqs_imx8qm_data },
+ { .compatible = "fsl,imx6sx-mqs", .data = &fsl_mqs_imx6sx_data },
+ { .compatible = "fsl,imx93-mqs", .data = &fsl_mqs_imx93_data },
+ { .compatible = "fsl,imx95-aonmix-mqs", .data = &fsl_mqs_imx95_aon_data },
+ { .compatible = "fsl,imx95-netcmix-mqs", .data = &fsl_mqs_imx95_netc_data },
+ { .compatible = "fsl,imx943-aonmix-mqs", .data = &fsl_mqs_imx943_aon_data },
+ { .compatible = "fsl,imx943-wakeupmix-mqs", .data = &fsl_mqs_imx943_wakeup_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
+
+static struct platform_driver fsl_mqs_driver = {
+ .probe = fsl_mqs_probe,
+ .remove = fsl_mqs_remove,
+ .driver = {
+ .name = "fsl-mqs",
+ .of_match_table = fsl_mqs_dt_ids,
+ .pm = pm_ptr(&fsl_mqs_pm_ops),
+ },
+};
+
+module_platform_driver(fsl_mqs_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
+MODULE_DESCRIPTION("MQS codec driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl-mqs");
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
new file mode 100644
index 000000000000..3de448ef724c
--- /dev/null
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC using the QUICC Multichannel Controller (QMC)
+ *
+ * Copyright 2022 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <soc/fsl/qe/qmc.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct qmc_dai {
+ char *name;
+ int id;
+ struct device *dev;
+ unsigned int nb_tx_ts;
+ unsigned int nb_rx_ts;
+
+ unsigned int nb_chans_avail;
+ unsigned int nb_chans_used_tx;
+ unsigned int nb_chans_used_rx;
+ struct qmc_chan **qmc_chans;
+};
+
+struct qmc_audio {
+ struct device *dev;
+ unsigned int num_dais;
+ struct qmc_dai *dais;
+ struct snd_soc_dai_driver *dai_drivers;
+};
+
+struct qmc_dai_prtd {
+ struct qmc_dai *qmc_dai;
+
+ snd_pcm_uframes_t buffer_ended;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
+
+ dma_addr_t ch_dma_addr_start;
+ dma_addr_t ch_dma_addr_current;
+ dma_addr_t ch_dma_addr_end;
+ size_t ch_dma_size;
+ size_t ch_dma_offset;
+
+ unsigned int channels;
+ struct snd_pcm_substream *substream;
+};
+
+static int qmc_audio_pcm_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ 64 * 1024, 64 * 1024);
+ return 0;
+}
+
+static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access)
+{
+ switch (access) {
+ case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+ case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+
+ /*
+ * In interleaved mode, the driver uses one QMC channel for all audio
+ * channels whereas in non-interleaved mode, it uses one QMC channel per
+ * audio channel.
+ */
+ prtd->channels = qmc_audio_access_is_interleaved(params_access(params)) ?
+ 1 : params_channels(params);
+
+ prtd->substream = substream;
+
+ prtd->buffer_ended = 0;
+ prtd->buffer_size = params_buffer_size(params);
+ prtd->period_size = params_period_size(params);
+
+ prtd->ch_dma_addr_start = runtime->dma_addr;
+ prtd->ch_dma_offset = params_buffer_bytes(params) / prtd->channels;
+ prtd->ch_dma_addr_end = runtime->dma_addr + prtd->ch_dma_offset;
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+ prtd->ch_dma_size = params_period_bytes(params) / prtd->channels;
+
+ return 0;
+}
+
+static void qmc_audio_pcm_write_complete(void *context);
+
+static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prtd->channels; i++) {
+ ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chans[i],
+ prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+ prtd->ch_dma_size,
+ i == prtd->channels - 1 ? qmc_audio_pcm_write_complete :
+ NULL, prtd);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void qmc_audio_pcm_write_complete(void *context)
+{
+ struct qmc_dai_prtd *prtd = context;
+
+ prtd->buffer_ended += prtd->period_size;
+ if (prtd->buffer_ended >= prtd->buffer_size)
+ prtd->buffer_ended = 0;
+
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ qmc_audio_pcm_write_submit(prtd);
+
+ snd_pcm_period_elapsed(prtd->substream);
+}
+
+static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags);
+
+static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prtd->channels; i++) {
+ ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chans[i],
+ prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+ prtd->ch_dma_size,
+ i == prtd->channels - 1 ? qmc_audio_pcm_read_complete :
+ NULL, prtd);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
+{
+ struct qmc_dai_prtd *prtd = context;
+
+ if (length != prtd->ch_dma_size) {
+ dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
+ length, prtd->ch_dma_size);
+ }
+
+ prtd->buffer_ended += prtd->period_size;
+ if (prtd->buffer_ended >= prtd->buffer_size)
+ prtd->buffer_ended = 0;
+
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ qmc_audio_pcm_read_submit(prtd);
+
+ snd_pcm_period_elapsed(prtd->substream);
+}
+
+static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ int ret;
+
+ if (!prtd->qmc_dai) {
+ dev_err(component->dev, "qmc_dai is not set\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->buffer_ended = 0;
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Submit first chunk ... */
+ ret = qmc_audio_pcm_write_submit(prtd);
+ if (ret)
+ return ret;
+
+ /* ... prepare next one ... */
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ /* ... and send it */
+ ret = qmc_audio_pcm_write_submit(prtd);
+ if (ret)
+ return ret;
+ } else {
+ /* Submit first chunk ... */
+ ret = qmc_audio_pcm_read_submit(prtd);
+ if (ret)
+ return ret;
+
+ /* ... prepare next one ... */
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ /* ... and send it */
+ ret = qmc_audio_pcm_read_submit(prtd);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+
+ return prtd->buffer_ended;
+}
+
+static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev);
+ struct snd_soc_dai_driver *dai_driver;
+ int id = args->args[0];
+ int i;
+
+ for (i = 0; i < qmc_audio->num_dais; i++) {
+ dai_driver = qmc_audio->dai_drivers + i;
+ if (dai_driver->id == id) {
+ *dai_name = dai_driver->name;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 64 * 1024,
+ .periods_min = 2,
+ .periods_max = 2 * 1024,
+ .buffer_bytes_max = 64 * 1024,
+};
+
+static int qmc_audio_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct qmc_dai_prtd *prtd;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &qmc_audio_pcm_hardware);
+
+ /* ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int qmc_audio_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static const struct snd_soc_component_driver qmc_audio_soc_platform = {
+ .open = qmc_audio_pcm_open,
+ .close = qmc_audio_pcm_close,
+ .hw_params = qmc_audio_pcm_hw_params,
+ .trigger = qmc_audio_pcm_trigger,
+ .pointer = qmc_audio_pcm_pointer,
+ .pcm_construct = qmc_audio_pcm_construct,
+ .of_xlate_dai_name = qmc_audio_of_xlate_dai_name,
+};
+
+static unsigned int qmc_dai_get_index(struct snd_soc_dai *dai)
+{
+ struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
+
+ return dai->driver - qmc_audio->dai_drivers;
+}
+
+static struct qmc_dai *qmc_dai_get_data(struct snd_soc_dai *dai)
+{
+ struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
+ unsigned int index;
+
+ index = qmc_dai_get_index(dai);
+ if (index > qmc_audio->num_dais)
+ return NULL;
+
+ return qmc_audio->dais + index;
+}
+
+/*
+ * The constraints for format/channel is to match with the number of 8bit
+ * time-slots available.
+ */
+static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_pcm_format_t format = params_format(params);
+ struct snd_interval ch = {0};
+
+ switch (snd_pcm_format_physical_width(format)) {
+ case 8:
+ ch.max = nb_ts;
+ break;
+ case 16:
+ ch.max = nb_ts / 2;
+ break;
+ case 32:
+ ch.max = nb_ts / 4;
+ break;
+ case 64:
+ ch.max = nb_ts / 8;
+ break;
+ default:
+ dev_err(qmc_dai->dev, "format physical width %u not supported\n",
+ snd_pcm_format_physical_width(format));
+ return -EINVAL;
+ }
+
+ ch.min = ch.max ? 1 : 0;
+
+ return snd_interval_refine(c, &ch);
+}
+
+static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct qmc_dai *qmc_dai = rule->private;
+
+ return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts);
+}
+
+static int qmc_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct qmc_dai *qmc_dai = rule->private;
+
+ return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_rx_ts);
+}
+
+static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ unsigned int channels = params_channels(params);
+ unsigned int slot_width;
+ snd_pcm_format_t format;
+ struct snd_mask f_new;
+
+ if (!channels || channels > nb_ts) {
+ dev_err(qmc_dai->dev, "channels %u not supported\n",
+ nb_ts);
+ return -EINVAL;
+ }
+
+ slot_width = (nb_ts / channels) * 8;
+
+ snd_mask_none(&f_new);
+ pcm_for_each_format(format) {
+ if (snd_mask_test_format(f_old, format)) {
+ if (snd_pcm_format_physical_width(format) <= slot_width)
+ snd_mask_set_format(&f_new, format);
+ }
+ }
+
+ return snd_mask_refine(f_old, &f_new);
+}
+
+static int qmc_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct qmc_dai *qmc_dai = rule->private;
+
+ return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts);
+}
+
+static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct qmc_dai *qmc_dai = rule->private;
+
+ return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts);
+}
+
+static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream,
+ struct qmc_dai *qmc_dai)
+{
+ snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
+ snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
+ unsigned int frame_bits;
+ u64 access;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format;
+ hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels;
+ frame_bits = qmc_dai->nb_rx_ts * 8;
+ } else {
+ hw_rule_channels_by_format = qmc_dai_hw_rule_playback_channels_by_format;
+ hw_rule_format_by_channels = qmc_dai_hw_rule_playback_format_by_channels;
+ frame_bits = qmc_dai->nb_tx_ts * 8;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels_by_format, qmc_dai,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add channels rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_format_by_channels, qmc_dai,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add format rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED |
+ 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+ access);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substream,
+ struct qmc_dai *qmc_dai)
+{
+ unsigned int frame_bits;
+ u64 access;
+ int ret;
+
+ frame_bits = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+ qmc_dai->nb_rx_ts * 8 : qmc_dai->nb_tx_ts * 8;
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED |
+ 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+ access);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmc_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ struct qmc_dai *qmc_dai;
+
+ qmc_dai = qmc_dai_get_data(dai);
+ if (!qmc_dai) {
+ dev_err(dai->dev, "Invalid dai\n");
+ return -EINVAL;
+ }
+
+ prtd->qmc_dai = qmc_dai;
+
+ return qmc_dai->nb_chans_avail > 1 ?
+ qmc_dai_constraints_noninterleaved(substream, qmc_dai) :
+ qmc_dai_constraints_interleaved(substream, qmc_dai);
+}
+
+static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct qmc_chan_param chan_param = {0};
+ unsigned int nb_chans_used;
+ struct qmc_dai *qmc_dai;
+ unsigned int i;
+ int ret;
+
+ qmc_dai = qmc_dai_get_data(dai);
+ if (!qmc_dai) {
+ dev_err(dai->dev, "Invalid dai\n");
+ return -EINVAL;
+ }
+
+ /*
+ * In interleaved mode, the driver uses one QMC channel for all audio
+ * channels whereas in non-interleaved mode, it uses one QMC channel per
+ * audio channel.
+ */
+ nb_chans_used = qmc_audio_access_is_interleaved(params_access(params)) ?
+ 1 : params_channels(params);
+
+ if (nb_chans_used > qmc_dai->nb_chans_avail) {
+ dev_err(dai->dev, "Not enough qmc_chans. Need %u, avail %u\n",
+ nb_chans_used, qmc_dai->nb_chans_avail);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ chan_param.mode = QMC_TRANSPARENT;
+ chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used;
+ for (i = 0; i < nb_chans_used; i++) {
+ ret = qmc_chan_set_param(qmc_dai->qmc_chans[i], &chan_param);
+ if (ret) {
+ dev_err(dai->dev, "qmc_chans[%u], set param failed %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+ qmc_dai->nb_chans_used_rx = nb_chans_used;
+ } else {
+ qmc_dai->nb_chans_used_tx = nb_chans_used;
+ }
+
+ return 0;
+}
+
+static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ unsigned int nb_chans_used;
+ struct qmc_dai *qmc_dai;
+ unsigned int i;
+ int direction;
+ int ret = 0;
+ int ret_tmp;
+
+ qmc_dai = qmc_dai_get_data(dai);
+ if (!qmc_dai) {
+ dev_err(dai->dev, "Invalid dai\n");
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = QMC_CHAN_WRITE;
+ nb_chans_used = qmc_dai->nb_chans_used_tx;
+ } else {
+ direction = QMC_CHAN_READ;
+ nb_chans_used = qmc_dai->nb_chans_used_rx;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ for (i = 0; i < nb_chans_used; i++) {
+ ret = qmc_chan_start(qmc_dai->qmc_chans[i], direction);
+ if (ret)
+ goto err_stop;
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* Stop and reset all QMC channels and return the first error encountered */
+ for (i = 0; i < nb_chans_used; i++) {
+ ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction);
+ if (!ret)
+ ret = ret_tmp;
+ if (ret_tmp)
+ continue;
+
+ ret_tmp = qmc_chan_reset(qmc_dai->qmc_chans[i], direction);
+ if (!ret)
+ ret = ret_tmp;
+ }
+ if (ret)
+ return ret;
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* Stop all QMC channels and return the first error encountered */
+ for (i = 0; i < nb_chans_used; i++) {
+ ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction);
+ if (!ret)
+ ret = ret_tmp;
+ }
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+err_stop:
+ while (i--) {
+ qmc_chan_stop(qmc_dai->qmc_chans[i], direction);
+ qmc_chan_reset(qmc_dai->qmc_chans[i], direction);
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops qmc_dai_ops = {
+ .startup = qmc_dai_startup,
+ .trigger = qmc_dai_trigger,
+ .hw_params = qmc_dai_hw_params,
+};
+
+static u64 qmc_audio_formats(u8 nb_ts, bool is_noninterleaved)
+{
+ unsigned int format_width;
+ unsigned int chan_width;
+ snd_pcm_format_t format;
+ u64 formats_mask;
+
+ if (!nb_ts)
+ return 0;
+
+ formats_mask = 0;
+ chan_width = nb_ts * 8;
+ pcm_for_each_format(format) {
+ /*
+ * Support format other than little-endian (ie big-endian or
+ * without endianness such as 8bit formats)
+ */
+ if (snd_pcm_format_little_endian(format) == 1)
+ continue;
+
+ /* Support physical width multiple of 8bit */
+ format_width = snd_pcm_format_physical_width(format);
+ if (format_width == 0 || format_width % 8)
+ continue;
+
+ /*
+ * And support physical width that can fit N times in the
+ * channel
+ */
+ if (format_width > chan_width || chan_width % format_width)
+ continue;
+
+ /*
+ * In non interleaved mode, we can only support formats that
+ * can fit only 1 time in the channel
+ */
+ if (is_noninterleaved && format_width != chan_width)
+ continue;
+
+ formats_mask |= pcm_format_to_bits(format);
+ }
+ return formats_mask;
+}
+
+static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np,
+ struct qmc_dai *qmc_dai,
+ struct snd_soc_dai_driver *qmc_soc_dai_driver)
+{
+ struct qmc_chan_ts_info ts_info;
+ struct qmc_chan_info info;
+ unsigned long rx_fs_rate;
+ unsigned long tx_fs_rate;
+ int prev_last_rx_ts = 0;
+ int prev_last_tx_ts = 0;
+ unsigned int nb_tx_ts;
+ unsigned int nb_rx_ts;
+ unsigned int i;
+ int last_rx_ts;
+ int last_tx_ts;
+ int count;
+ u32 val;
+ int ret;
+
+ qmc_dai->dev = qmc_audio->dev;
+
+ ret = of_property_read_u32(np, "reg", &val);
+ if (ret) {
+ dev_err(qmc_audio->dev, "%pOF: failed to read reg\n", np);
+ return ret;
+ }
+ qmc_dai->id = val;
+
+ qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d",
+ np->parent->name, qmc_dai->id);
+ if (!qmc_dai->name)
+ return -ENOMEM;
+
+ count = qmc_chan_count_phandles(np, "fsl,qmc-chan");
+ if (count < 0)
+ return dev_err_probe(qmc_audio->dev, count,
+ "dai %d get number of QMC channel failed\n", qmc_dai->id);
+ if (!count)
+ return dev_err_probe(qmc_audio->dev, -EINVAL,
+ "dai %d no QMC channel defined\n", qmc_dai->id);
+
+ qmc_dai->qmc_chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->qmc_chans),
+ GFP_KERNEL);
+ if (!qmc_dai->qmc_chans)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ qmc_dai->qmc_chans[i] = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np,
+ "fsl,qmc-chan", i);
+ if (IS_ERR(qmc_dai->qmc_chans[i])) {
+ return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->qmc_chans[i]),
+ "dai %d get QMC channel %d failed\n", qmc_dai->id, i);
+ }
+
+ ret = qmc_chan_get_info(qmc_dai->qmc_chans[i], &info);
+ if (ret) {
+ dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n",
+ qmc_dai->id, i, ret);
+ return ret;
+ }
+
+ if (info.mode != QMC_TRANSPARENT) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d mode %d is not QMC_TRANSPARENT\n",
+ qmc_dai->id, i, info.mode);
+ return -EINVAL;
+ }
+
+ /*
+ * All channels must have the same number of Tx slots and the
+ * same numbers of Rx slots.
+ */
+ if (i == 0) {
+ nb_tx_ts = info.nb_tx_ts;
+ nb_rx_ts = info.nb_rx_ts;
+ tx_fs_rate = info.tx_fs_rate;
+ rx_fs_rate = info.rx_fs_rate;
+ } else {
+ if (nb_tx_ts != info.nb_tx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Tx timeslots (%u instead of %u)\n",
+ qmc_dai->id, i, info.nb_tx_ts, nb_tx_ts);
+ return -EINVAL;
+ }
+ if (nb_rx_ts != info.nb_rx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Rx timeslots (%u instead of %u)\n",
+ qmc_dai->id, i, info.nb_rx_ts, nb_rx_ts);
+ return -EINVAL;
+ }
+ if (tx_fs_rate != info.tx_fs_rate) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Tx frame sample rate (%lu instead of %lu)\n",
+ qmc_dai->id, i, info.tx_fs_rate, tx_fs_rate);
+ return -EINVAL;
+ }
+ if (rx_fs_rate != info.rx_fs_rate) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Rx frame sample rate (%lu instead of %lu)\n",
+ qmc_dai->id, i, info.rx_fs_rate, rx_fs_rate);
+ return -EINVAL;
+ }
+ }
+
+ ret = qmc_chan_get_ts_info(qmc_dai->qmc_chans[i], &ts_info);
+ if (ret) {
+ dev_err(qmc_audio->dev, "dai %d get QMC %d channel TS info failed %d\n",
+ qmc_dai->id, i, ret);
+ return ret;
+ }
+
+ last_rx_ts = fls64(ts_info.rx_ts_mask);
+ last_tx_ts = fls64(ts_info.tx_ts_mask);
+
+ if (prev_last_rx_ts > last_rx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (RX timeslot %d before %d)\n",
+ qmc_dai->id, i, prev_last_rx_ts, last_rx_ts);
+ return -EINVAL;
+ }
+ if (prev_last_tx_ts > last_tx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (TX timeslot %d before %d)\n",
+ qmc_dai->id, i, prev_last_tx_ts, last_tx_ts);
+ return -EINVAL;
+ }
+
+ prev_last_rx_ts = last_rx_ts;
+ prev_last_tx_ts = last_tx_ts;
+ }
+
+ qmc_dai->nb_chans_avail = count;
+ qmc_dai->nb_tx_ts = nb_tx_ts * count;
+ qmc_dai->nb_rx_ts = nb_rx_ts * count;
+
+ qmc_soc_dai_driver->id = qmc_dai->id;
+ qmc_soc_dai_driver->name = qmc_dai->name;
+
+ qmc_soc_dai_driver->playback.channels_min = 0;
+ qmc_soc_dai_driver->playback.channels_max = 0;
+ if (nb_tx_ts) {
+ qmc_soc_dai_driver->playback.channels_min = 1;
+ qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts;
+ }
+ qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts,
+ count > 1);
+
+ qmc_soc_dai_driver->capture.channels_min = 0;
+ qmc_soc_dai_driver->capture.channels_max = 0;
+ if (nb_rx_ts) {
+ qmc_soc_dai_driver->capture.channels_min = 1;
+ qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts;
+ }
+ qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts,
+ count > 1);
+
+ qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate);
+ qmc_soc_dai_driver->playback.rate_min = tx_fs_rate;
+ qmc_soc_dai_driver->playback.rate_max = tx_fs_rate;
+ qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rx_fs_rate);
+ qmc_soc_dai_driver->capture.rate_min = rx_fs_rate;
+ qmc_soc_dai_driver->capture.rate_max = rx_fs_rate;
+
+ qmc_soc_dai_driver->ops = &qmc_dai_ops;
+
+ return 0;
+}
+
+static int qmc_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct qmc_audio *qmc_audio;
+ struct device_node *child;
+ unsigned int i;
+ int ret;
+
+ qmc_audio = devm_kzalloc(&pdev->dev, sizeof(*qmc_audio), GFP_KERNEL);
+ if (!qmc_audio)
+ return -ENOMEM;
+
+ qmc_audio->dev = &pdev->dev;
+
+ qmc_audio->num_dais = of_get_available_child_count(np);
+ if (qmc_audio->num_dais) {
+ qmc_audio->dais = devm_kcalloc(&pdev->dev, qmc_audio->num_dais,
+ sizeof(*qmc_audio->dais),
+ GFP_KERNEL);
+ if (!qmc_audio->dais)
+ return -ENOMEM;
+
+ qmc_audio->dai_drivers = devm_kcalloc(&pdev->dev, qmc_audio->num_dais,
+ sizeof(*qmc_audio->dai_drivers),
+ GFP_KERNEL);
+ if (!qmc_audio->dai_drivers)
+ return -ENOMEM;
+ }
+
+ i = 0;
+ for_each_available_child_of_node(np, child) {
+ ret = qmc_audio_dai_parse(qmc_audio, child,
+ qmc_audio->dais + i,
+ qmc_audio->dai_drivers + i);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ i++;
+ }
+
+ platform_set_drvdata(pdev, qmc_audio);
+
+ ret = devm_snd_soc_register_component(qmc_audio->dev,
+ &qmc_audio_soc_platform,
+ qmc_audio->dai_drivers,
+ qmc_audio->num_dais);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id qmc_audio_id_table[] = {
+ { .compatible = "fsl,qmc-audio" },
+ {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, qmc_audio_id_table);
+
+static struct platform_driver qmc_audio_driver = {
+ .driver = {
+ .name = "fsl-qmc-audio",
+ .of_match_table = of_match_ptr(qmc_audio_id_table),
+ },
+ .probe = qmc_audio_probe,
+};
+module_platform_driver(qmc_audio_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("CPM/QE QMC audio driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
new file mode 100644
index 000000000000..5708b3a9878d
--- /dev/null
+++ b/sound/soc/fsl/fsl_rpmsg.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018-2021 NXP
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_rpmsg.h"
+#include "imx-pcm.h"
+
+#define FSL_RPMSG_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_48000)
+#define FSL_RPMSG_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+/* 192kHz/32bit/2ch/60s size is 0x574e00 */
+#define LPA_LARGE_BUFFER_SIZE (0x6000000)
+/* 16kHz/32bit/8ch/1s size is 0x7D000 */
+#define LPA_CAPTURE_BUFFER_SIZE (0x100000)
+
+static const unsigned int fsl_rpmsg_rates[] = {
+ 8000, 11025, 16000, 22050, 44100,
+ 32000, 48000, 96000, 88200, 176400, 192000,
+ 352800, 384000, 705600, 768000, 1411200, 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = {
+ .count = ARRAY_SIZE(fsl_rpmsg_rates),
+ .list = fsl_rpmsg_rates,
+};
+
+static int fsl_rpmsg_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
+ struct clk *p = rpmsg->mclk, *pll = NULL, *npll = NULL;
+ u64 rate = params_rate(params);
+ int ret = 0;
+
+ /* Get current pll parent */
+ while (p && rpmsg->pll8k && rpmsg->pll11k) {
+ struct clk *pp = clk_get_parent(p);
+
+ if (clk_is_match(pp, rpmsg->pll8k) ||
+ clk_is_match(pp, rpmsg->pll11k)) {
+ pll = pp;
+ break;
+ }
+ p = pp;
+ }
+
+ /* Switch to another pll parent if needed. */
+ if (pll) {
+ npll = (do_div(rate, 8000) ? rpmsg->pll11k : rpmsg->pll8k);
+ if (!clk_is_match(pll, npll)) {
+ ret = clk_set_parent(p, npll);
+ if (ret < 0)
+ dev_warn(dai->dev, "failed to set parent %s: %d\n",
+ __clk_get_name(npll), ret);
+ }
+ }
+
+ if (!(rpmsg->mclk_streams & BIT(substream->stream))) {
+ ret = clk_prepare_enable(rpmsg->mclk);
+ if (ret) {
+ dev_err(dai->dev, "failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ rpmsg->mclk_streams |= BIT(substream->stream);
+ }
+
+ return ret;
+}
+
+static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
+
+ if (rpmsg->mclk_streams & BIT(substream->stream)) {
+ clk_disable_unprepare(rpmsg->mclk);
+ rpmsg->mclk_streams &= ~BIT(substream->stream);
+ }
+
+ return 0;
+}
+
+static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &fsl_rpmsg_rate_constraints);
+}
+
+static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
+ .startup = fsl_rpmsg_startup,
+ .hw_params = fsl_rpmsg_hw_params,
+ .hw_free = fsl_rpmsg_hw_free,
+};
+
+static struct snd_soc_dai_driver fsl_rpmsg_dai = {
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_RPMSG_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_RPMSG_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &fsl_rpmsg_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_component = {
+ .name = "fsl-rpmsg",
+};
+
+static const struct fsl_rpmsg_soc_data imx7ulp_data = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+};
+
+static const struct fsl_rpmsg_soc_data imx8mm_data = {
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U8 |
+ SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE,
+};
+
+static const struct fsl_rpmsg_soc_data imx8mn_data = {
+ .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,
+};
+
+static const struct fsl_rpmsg_soc_data imx8mp_data = {
+ .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,
+};
+
+static const struct fsl_rpmsg_soc_data imx93_data = {
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+};
+
+static const struct fsl_rpmsg_soc_data imx95_data = {
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+};
+
+static const struct of_device_id fsl_rpmsg_ids[] = {
+ { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data},
+ { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data},
+ { .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data},
+ { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data},
+ { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data},
+ { .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data},
+ { .compatible = "fsl,imx95-rpmsg-audio", .data = &imx95_data},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
+
+static int fsl_rpmsg_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_dai_driver *dai_drv;
+ const char *dai_name;
+ struct fsl_rpmsg *rpmsg;
+ int ret;
+
+ dai_drv = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+ memcpy(dai_drv, &fsl_rpmsg_dai, sizeof(fsl_rpmsg_dai));
+
+ rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
+ if (!rpmsg)
+ return -ENOMEM;
+
+ rpmsg->soc_data = of_device_get_match_data(&pdev->dev);
+
+ if (rpmsg->soc_data) {
+ dai_drv->playback.rates = rpmsg->soc_data->rates;
+ dai_drv->capture.rates = rpmsg->soc_data->rates;
+ dai_drv->playback.formats = rpmsg->soc_data->formats;
+ dai_drv->capture.formats = rpmsg->soc_data->formats;
+ }
+
+ /* Use rpmsg channel name as cpu dai name */
+ ret = of_property_read_string(np, "fsl,rpmsg-channel-name", &dai_name);
+ if (ret) {
+ if (ret == -EINVAL) {
+ dai_name = "rpmsg-audio-channel";
+ } else {
+ dev_err(&pdev->dev, "Failed to get rpmsg channel name: %d!\n", ret);
+ return ret;
+ }
+ }
+ dai_drv->name = dai_name;
+
+ /* Setup cpu dai for sound card that sits on rpmsg-micfil-channel */
+ if (!strcmp(dai_name, "rpmsg-micfil-channel")) {
+ dai_drv->capture.channels_min = 1;
+ dai_drv->capture.channels_max = 8;
+ dai_drv->capture.rates = SNDRV_PCM_RATE_8000_48000;
+ dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ if (of_device_is_compatible(np, "fsl,imx8mm-rpmsg-audio"))
+ dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ }
+
+ if (of_property_read_bool(np, "fsl,enable-lpa")) {
+ rpmsg->enable_lpa = 1;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = LPA_LARGE_BUFFER_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = LPA_CAPTURE_BUFFER_SIZE;
+ } else {
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = IMX_DEFAULT_DMABUF_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = IMX_DEFAULT_DMABUF_SIZE;
+ }
+
+ /* Get the optional clocks */
+ rpmsg->ipg = devm_clk_get_optional(&pdev->dev, "ipg");
+ if (IS_ERR(rpmsg->ipg))
+ return PTR_ERR(rpmsg->ipg);
+
+ rpmsg->mclk = devm_clk_get_optional(&pdev->dev, "mclk");
+ if (IS_ERR(rpmsg->mclk))
+ return PTR_ERR(rpmsg->mclk);
+
+ rpmsg->dma = devm_clk_get_optional(&pdev->dev, "dma");
+ if (IS_ERR(rpmsg->dma))
+ return PTR_ERR(rpmsg->dma);
+
+ rpmsg->pll8k = devm_clk_get_optional(&pdev->dev, "pll8k");
+ if (IS_ERR(rpmsg->pll8k))
+ return PTR_ERR(rpmsg->pll8k);
+
+ rpmsg->pll11k = devm_clk_get_optional(&pdev->dev, "pll11k");
+ if (IS_ERR(rpmsg->pll11k))
+ return PTR_ERR(rpmsg->pll11k);
+
+ platform_set_drvdata(pdev, rpmsg);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
+ dai_drv, 1);
+ if (ret)
+ goto err_pm_disable;
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void fsl_rpmsg_remove(struct platform_device *pdev)
+{
+ struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ if (rpmsg->card_pdev)
+ platform_device_unregister(rpmsg->card_pdev);
+}
+
+static int fsl_rpmsg_runtime_resume(struct device *dev)
+{
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(rpmsg->ipg);
+ if (ret) {
+ dev_err(dev, "failed to enable ipg clock: %d\n", ret);
+ goto ipg_err;
+ }
+
+ ret = clk_prepare_enable(rpmsg->dma);
+ if (ret) {
+ dev_err(dev, "Failed to enable dma clock %d\n", ret);
+ goto dma_err;
+ }
+
+ return 0;
+
+dma_err:
+ clk_disable_unprepare(rpmsg->ipg);
+ipg_err:
+ return ret;
+}
+
+static int fsl_rpmsg_runtime_suspend(struct device *dev)
+{
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(rpmsg->dma);
+ clk_disable_unprepare(rpmsg->ipg);
+
+ return 0;
+}
+
+static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
+ RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend, fsl_rpmsg_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver fsl_rpmsg_driver = {
+ .probe = fsl_rpmsg_probe,
+ .remove = fsl_rpmsg_remove,
+ .driver = {
+ .name = "fsl_rpmsg",
+ .pm = pm_ptr(&fsl_rpmsg_pm_ops),
+ .of_match_table = fsl_rpmsg_ids,
+ },
+};
+module_platform_driver(fsl_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:fsl_rpmsg");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_rpmsg.h b/sound/soc/fsl/fsl_rpmsg.h
new file mode 100644
index 000000000000..1b1683808507
--- /dev/null
+++ b/sound/soc/fsl/fsl_rpmsg.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2017-2021 NXP
+ */
+
+#ifndef __FSL_RPMSG_H
+#define __FSL_RPMSG_H
+
+/*
+ * struct fsl_rpmsg_soc_data
+ * @rates: supported rates
+ * @formats: supported formats
+ */
+struct fsl_rpmsg_soc_data {
+ int rates;
+ u64 formats;
+};
+
+/*
+ * struct fsl_rpmsg - rpmsg private data
+ *
+ * @ipg: ipg clock for cpu dai (SAI)
+ * @mclk: master clock for cpu dai (SAI)
+ * @dma: clock for dma device
+ * @pll8k: parent clock for multiple of 8kHz frequency
+ * @pll11k: parent clock for multiple of 11kHz frequency
+ * @card_pdev: Platform_device pointer to register a sound card
+ * @soc_data: soc specific data
+ * @mclk_streams: Active streams that are using baudclk
+ * @force_lpa: force enable low power audio routine if condition satisfy
+ * @enable_lpa: enable low power audio routine according to dts setting
+ * @buffer_size: pre allocated dma buffer size
+ */
+struct fsl_rpmsg {
+ struct clk *ipg;
+ struct clk *mclk;
+ struct clk *dma;
+ struct clk *pll8k;
+ struct clk *pll11k;
+ struct platform_device *card_pdev;
+ const struct fsl_rpmsg_soc_data *soc_data;
+ unsigned int mclk_streams;
+ int force_lpa;
+ int enable_lpa;
+ int buffer_size[2];
+};
+#endif /* __FSL_RPMSG_H */
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index fef264d27fd3..72bfc91e21b9 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1,27 +1,28 @@
-/*
- * Freescale ALSA SoC Digital Audio Interface (SAI) driver.
- *
- * Copyright 2012-2015 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 as published by the
- * Free Software Foundation, either version 2 of the License, or(at your
- * option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Freescale ALSA SoC Digital Audio Interface (SAI) driver.
+//
+// Copyright 2012-2015 Freescale Semiconductor, Inc.
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
-#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/time.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include "fsl_sai.h"
+#include "fsl_utils.h"
#include "imx-pcm.h"
#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
@@ -30,7 +31,8 @@
static const unsigned int fsl_sai_rates[] = {
8000, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000, 64000,
- 88200, 96000, 176400, 192000
+ 88200, 96000, 176400, 192000, 352800,
+ 384000, 705600, 768000, 1411200, 2822400,
};
static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
@@ -38,12 +40,56 @@ static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
.list = fsl_sai_rates,
};
+/**
+ * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream
+ *
+ * SAI supports synchronous mode using bit/frame clocks of either Transmitter's
+ * or Receiver's for both streams. This function is used to check if clocks of
+ * the stream's are synced by the opposite stream.
+ *
+ * @sai: SAI context
+ * @dir: stream direction
+ */
+static inline bool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir)
+{
+ int adir = (dir == TX) ? RX : TX;
+
+ /* current dir in async mode while opposite dir in sync mode */
+ return !sai->synchronous[dir] && sai->synchronous[adir];
+}
+
+static struct pinctrl_state *fsl_sai_get_pins_state(struct fsl_sai *sai, u32 bclk)
+{
+ struct pinctrl_state *state = NULL;
+
+ if (sai->is_pdm_mode) {
+ /* DSD512@44.1kHz, DSD512@48kHz */
+ if (bclk >= 22579200)
+ state = pinctrl_lookup_state(sai->pinctrl, "dsd512");
+
+ /* Get default DSD state */
+ if (IS_ERR_OR_NULL(state))
+ state = pinctrl_lookup_state(sai->pinctrl, "dsd");
+ } else {
+ /* 706k32b2c, 768k32b2c, etc */
+ if (bclk >= 45158400)
+ state = pinctrl_lookup_state(sai->pinctrl, "pcm_b2m");
+ }
+
+ /* Get default state */
+ if (IS_ERR_OR_NULL(state))
+ state = pinctrl_lookup_state(sai->pinctrl, "default");
+
+ return state;
+}
+
static irqreturn_t fsl_sai_isr(int irq, void *devid)
{
struct fsl_sai *sai = (struct fsl_sai *)devid;
+ unsigned int ofs = sai->soc_data->reg_offset;
struct device *dev = &sai->pdev->dev;
u32 flags, xcsr, mask;
- bool irq_none = true;
+ irqreturn_t iret = IRQ_NONE;
/*
* Both IRQ status bits and IRQ mask bits are in the xCSR but
@@ -53,11 +99,11 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
/* Tx IRQ */
- regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
+ regmap_read(sai->regmap, FSL_SAI_TCSR(ofs), &xcsr);
flags = xcsr & mask;
if (flags)
- irq_none = false;
+ iret = IRQ_HANDLED;
else
goto irq_rx;
@@ -65,13 +111,10 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
dev_dbg(dev, "isr: Start of Tx word detected\n");
if (flags & FSL_SAI_CSR_SEF)
- dev_warn(dev, "isr: Tx Frame sync error detected\n");
+ dev_dbg(dev, "isr: Tx Frame sync error detected\n");
- if (flags & FSL_SAI_CSR_FEF) {
- dev_warn(dev, "isr: Transmit underrun detected\n");
- /* FIFO reset for safety */
- xcsr |= FSL_SAI_CSR_FR;
- }
+ if (flags & FSL_SAI_CSR_FEF)
+ dev_dbg(dev, "isr: Transmit underrun detected\n");
if (flags & FSL_SAI_CSR_FWF)
dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
@@ -83,15 +126,15 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
xcsr &= ~FSL_SAI_CSR_xF_MASK;
if (flags)
- regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
+ regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), flags | xcsr);
irq_rx:
/* Rx IRQ */
- regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
+ regmap_read(sai->regmap, FSL_SAI_RCSR(ofs), &xcsr);
flags = xcsr & mask;
if (flags)
- irq_none = false;
+ iret = IRQ_HANDLED;
else
goto out;
@@ -99,13 +142,10 @@ irq_rx:
dev_dbg(dev, "isr: Start of Rx word detected\n");
if (flags & FSL_SAI_CSR_SEF)
- dev_warn(dev, "isr: Rx Frame sync error detected\n");
+ dev_dbg(dev, "isr: Rx Frame sync error detected\n");
- if (flags & FSL_SAI_CSR_FEF) {
- dev_warn(dev, "isr: Receive overflow detected\n");
- /* FIFO reset for safety */
- xcsr |= FSL_SAI_CSR_FR;
- }
+ if (flags & FSL_SAI_CSR_FEF)
+ dev_dbg(dev, "isr: Receive overflow detected\n");
if (flags & FSL_SAI_CSR_FWF)
dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
@@ -117,31 +157,70 @@ irq_rx:
xcsr &= ~FSL_SAI_CSR_xF_MASK;
if (flags)
- regmap_write(sai->regmap, FSL_SAI_RCSR, flags | xcsr);
+ regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr);
out:
- if (irq_none)
- return IRQ_NONE;
- else
- return IRQ_HANDLED;
+ return iret;
}
-static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
- u32 rx_mask, int slots, int slot_width)
+static int fsl_sai_set_dai_tdm_slot_tx(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = true;
+
+ sai->slots[tx] = slots;
+ sai->slot_width[tx] = slot_width;
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_tdm_slot_rx(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = false;
+
+ sai->slots[tx] = slots;
+ sai->slot_width[tx] = slot_width;
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ int ret;
+
+ ret = fsl_sai_set_dai_tdm_slot_tx(cpu_dai, tx_mask, rx_mask, slots, slot_width);
+ if (ret)
+ return ret;
- sai->slots = slots;
- sai->slot_width = slot_width;
+ return fsl_sai_set_dai_tdm_slot_rx(cpu_dai, tx_mask, rx_mask, slots, slot_width);
+}
+
+static int fsl_sai_xlate_tdm_slot_mask(unsigned int slots,
+ unsigned int *tx_mask, unsigned int *rx_mask)
+{
+ /* Leave it empty, don't change the value of tx_mask and rx_mask */
+ return 0;
+}
+
+static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+
+ sai->bclk_ratio = ratio;
return 0;
}
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int fsl_dir)
+ int clk_id, unsigned int freq, bool tx)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+ unsigned int ofs = sai->soc_data->reg_offset;
u32 val_cr2 = 0;
switch (clk_id) {
@@ -161,29 +240,61 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_MSEL_MASK, val_cr2);
return 0;
}
+static int fsl_sai_set_mclk_rate(struct snd_soc_dai *dai, int clk_id, unsigned int freq)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ fsl_asoc_reparent_pll_clocks(dai->dev, sai->mclk_clk[clk_id],
+ sai->pll8k_clk, sai->pll11k_clk, freq);
+
+ ret = clk_set_rate(sai->mclk_clk[clk_id], freq);
+ if (ret < 0)
+ dev_err(dai->dev, "failed to set clock rate (%u): %d\n", freq, ret);
+
+ return ret;
+}
+
static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
int ret;
if (dir == SND_SOC_CLOCK_IN)
return 0;
- ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
- FSL_FMT_TRANSMITTER);
+ if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) {
+ dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) {
+ dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ if (sai->mclk_streams == 0 && freq > 0) {
+ ret = fsl_sai_set_mclk_rate(cpu_dai,
+ clk_id ? clk_id : FSL_SAI_CLK_MAST1,
+ freq);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
if (ret) {
dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
return ret;
}
- ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
- FSL_FMT_RECEIVER);
+ ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false);
if (ret)
dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
@@ -191,15 +302,17 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
}
static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
- unsigned int fmt, int fsl_dir)
+ unsigned int fmt, bool tx)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+ unsigned int ofs = sai->soc_data->reg_offset;
u32 val_cr2 = 0, val_cr4 = 0;
if (!sai->is_lsb_first)
val_cr4 |= FSL_SAI_CR4_MF;
+ sai->is_pdm_mode = false;
+ sai->is_dsp_mode[tx] = false;
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -228,7 +341,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
*/
val_cr2 |= FSL_SAI_CR2_BCP;
val_cr4 |= FSL_SAI_CR4_FSE;
- sai->is_dsp_mode = true;
+ sai->is_dsp_mode[tx] = true;
break;
case SND_SOC_DAIFMT_DSP_B:
/*
@@ -236,7 +349,11 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
* frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
- sai->is_dsp_mode = true;
+ sai->is_dsp_mode[tx] = true;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ val_cr2 |= FSL_SAI_CR2_BCP;
+ sai->is_pdm_mode = true;
break;
case SND_SOC_DAIFMT_RIGHT_J:
/* To be done */
@@ -266,29 +383,31 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ /* DAI clock provider masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
+ sai->is_consumer_mode[tx] = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- sai->is_slave_mode = true;
+ case SND_SOC_DAIFMT_BC_FC:
+ sai->is_consumer_mode[tx] = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_BP_FC:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
+ sai->is_consumer_mode[tx] = false;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_BC_FP:
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
- sai->is_slave_mode = true;
+ sai->is_consumer_mode[tx] = true;
break;
default:
return -EINVAL;
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
- regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
@@ -299,63 +418,87 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
int ret;
- ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER);
+ ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true);
if (ret) {
dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
return ret;
}
- ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
+ ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false);
if (ret)
dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
return ret;
}
+static int fsl_sai_set_dai_fmt_tx(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true);
+}
+
+static int fsl_sai_set_dai_fmt_rx(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false);
+}
+
static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg, ofs = sai->soc_data->reg_offset;
unsigned long clk_rate;
- u32 savediv = 0, ratio, savesub = freq;
+ u32 savediv = 0, ratio, bestdiff = freq;
+ int adir = tx ? RX : TX;
+ int dir = tx ? TX : RX;
u32 id;
- int ret = 0;
+ bool support_1_1_ratio = sai->verid.version >= 0x0301;
- /* Don't apply to slave mode */
- if (sai->is_slave_mode)
+ /* Don't apply to consumer mode */
+ if (sai->is_consumer_mode[tx])
return 0;
- for (id = 0; id < FSL_SAI_MCLK_MAX; id++) {
+ /*
+ * There is no point in polling MCLK0 if it is identical to MCLK1.
+ * And given that MQS use case has to use MCLK1 though two clocks
+ * are the same, we simply skip MCLK0 and start to find from MCLK1.
+ */
+ id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0;
+
+ for (; id < FSL_SAI_MCLK_MAX; id++) {
+ int diff;
+
clk_rate = clk_get_rate(sai->mclk_clk[id]);
if (!clk_rate)
continue;
- ratio = clk_rate / freq;
+ ratio = DIV_ROUND_CLOSEST(clk_rate, freq);
+ if (!ratio || ratio > 512)
+ continue;
+ if (ratio == 1 && !support_1_1_ratio)
+ continue;
+ if ((ratio & 1) && ratio > 1)
+ continue;
- ret = clk_rate - ratio * freq;
+ diff = abs((long)clk_rate - ratio * freq);
/*
* Drop the source that can not be
* divided into the required rate.
*/
- if (ret != 0 && clk_rate / ret < 1000)
+ if (diff != 0 && clk_rate / diff < 1000)
continue;
dev_dbg(dai->dev,
"ratio %d for freq %dHz based on clock %ldHz\n",
ratio, freq, clk_rate);
- if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512)
- ratio /= 2;
- else
- continue;
- if (ret < savesub) {
+ if (diff < bestdiff) {
savediv = ratio;
sai->mclk_id[tx] = id;
- savesub = ret;
+ bestdiff = diff;
}
- if (ret == 0)
+ if (diff == 0)
break;
}
@@ -365,6 +508,9 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
return -EINVAL;
}
+ dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
+ sai->mclk_id[tx], savediv, bestdiff);
+
/*
* 1) For Asynchronous mode, we must set RCR2 register for capture, and
* set TCR2 register for playback.
@@ -375,24 +521,31 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
* 4) For Tx and Rx are both Synchronous with another SAI, we just
* ignore it.
*/
- if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
- (!tx && !sai->synchronous[RX])) {
- regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
- FSL_SAI_CR2_MSEL_MASK,
- FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
- regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
- FSL_SAI_CR2_DIV_MASK, savediv - 1);
- } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
- (tx && !sai->synchronous[TX])) {
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
- FSL_SAI_CR2_MSEL_MASK,
- FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
- FSL_SAI_CR2_DIV_MASK, savediv - 1);
- }
+ if (fsl_sai_dir_is_synced(sai, adir))
+ reg = FSL_SAI_xCR2(!tx, ofs);
+ else if (!sai->synchronous[dir])
+ reg = FSL_SAI_xCR2(tx, ofs);
+ else
+ return 0;
- dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
- sai->mclk_id[tx], savediv, savesub);
+ regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
+ FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
+
+ if (savediv == 1) {
+ regmap_update_bits(sai->regmap, reg,
+ FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
+ FSL_SAI_CR2_BYP);
+ if (fsl_sai_dir_is_synced(sai, adir))
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+ FSL_SAI_CR2_BCI, FSL_SAI_CR2_BCI);
+ else
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+ FSL_SAI_CR2_BCI, 0);
+ } else {
+ regmap_update_bits(sai->regmap, reg,
+ FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
+ savediv / 2 - 1);
+ }
return 0;
}
@@ -402,23 +555,69 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int channels = params_channels(params);
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct fsl_sai_dl_cfg *dl_cfg = sai->dl_cfg;
u32 word_width = params_width(params);
+ int trce_mask = 0, dl_cfg_idx = 0;
+ int dl_cfg_cnt = sai->dl_cfg_cnt;
+ u32 dl_type = FSL_SAI_DL_I2S;
u32 val_cr4 = 0, val_cr5 = 0;
u32 slots = (channels == 1) ? 2 : channels;
u32 slot_width = word_width;
- int ret;
+ int adir = tx ? RX : TX;
+ u32 pins, bclk;
+ u32 watermark;
+ int ret, i;
- if (sai->slots)
- slots = sai->slots;
+ if (sai->slot_width[tx])
+ slot_width = sai->slot_width[tx];
- if (sai->slot_width)
- slot_width = sai->slot_width;
+ if (sai->slots[tx])
+ slots = sai->slots[tx];
+ else if (sai->bclk_ratio)
+ slots = sai->bclk_ratio / slot_width;
- if (!sai->is_slave_mode) {
- ret = fsl_sai_set_bclk(cpu_dai, tx,
- slots * slot_width * params_rate(params));
+ pins = DIV_ROUND_UP(channels, slots);
+
+ /*
+ * PDM mode, channels are independent
+ * each channels are on one dataline/FIFO.
+ */
+ if (sai->is_pdm_mode) {
+ pins = channels;
+ dl_type = FSL_SAI_DL_PDM;
+ }
+
+ for (i = 0; i < dl_cfg_cnt; i++) {
+ if (dl_cfg[i].type == dl_type && dl_cfg[i].pins[tx] == pins) {
+ dl_cfg_idx = i;
+ break;
+ }
+ }
+
+ if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) < pins) {
+ dev_err(cpu_dai->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+
+ bclk = params_rate(params) * (sai->bclk_ratio ? sai->bclk_ratio : slots * slot_width);
+
+ if (!IS_ERR_OR_NULL(sai->pinctrl)) {
+ sai->pins_state = fsl_sai_get_pins_state(sai, bclk);
+ if (!IS_ERR_OR_NULL(sai->pins_state)) {
+ ret = pinctrl_select_state(sai->pinctrl, sai->pins_state);
+ if (ret) {
+ dev_err(cpu_dai->dev, "failed to set proper pins state: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ if (!sai->is_consumer_mode[tx]) {
+ ret = fsl_sai_set_bclk(cpu_dai, tx, bclk);
if (ret)
return ret;
@@ -432,7 +631,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
}
}
- if (!sai->is_dsp_mode)
+ if (!sai->is_dsp_mode[tx] && !sai->is_pdm_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
@@ -445,42 +644,109 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
+ /* Set to avoid channel swap */
+ val_cr4 |= FSL_SAI_CR4_FCONT;
+
+ /* Set to output mode to avoid tri-stated data pins */
+ if (tx)
+ val_cr4 |= FSL_SAI_CR4_CHMOD;
+
/*
- * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
- * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
- * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
- * error.
+ * When Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will provide bclk and
+ * frame clock for Tx(Rx). We should set RCR4(TCR4), RCR5(TCR5)
+ * for playback(capture), or there will be sync error.
*/
- if (!sai->is_slave_mode) {
- if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
- regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
- FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
- val_cr4);
- regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
- FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
- FSL_SAI_CR5_FBT_MASK, val_cr5);
- regmap_write(sai->regmap, FSL_SAI_TMR,
- ~0UL - ((1 << channels) - 1));
- } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
- regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
- FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
- val_cr4);
- regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
- FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
- FSL_SAI_CR5_FBT_MASK, val_cr5);
- regmap_write(sai->regmap, FSL_SAI_RMR,
- ~0UL - ((1 << channels) - 1));
+ if (fsl_sai_dir_is_synced(sai, adir)) {
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
+ FSL_SAI_CR4_CHMOD_MASK,
+ val_cr4);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs),
+ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+ FSL_SAI_CR5_FBT_MASK, val_cr5);
+ }
+
+ /*
+ * Combine mode has limation:
+ * - Can't used for singel dataline/FIFO case except the FIFO0
+ * - Can't used for multi dataline/FIFO case except the enabled FIFOs
+ * are successive and start from FIFO0
+ *
+ * So for common usage, all multi fifo case disable the combine mode.
+ */
+ if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) <= 1 || sai->is_multi_fifo_dma)
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
+ FSL_SAI_CR4_FCOMB_MASK, 0);
+ else
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
+ FSL_SAI_CR4_FCOMB_MASK, FSL_SAI_CR4_FCOMB_SOFT);
+
+ dma_params = tx ? &sai->dma_params_tx : &sai->dma_params_rx;
+ dma_params->addr = sai->res->start + FSL_SAI_xDR0(tx) +
+ dl_cfg[dl_cfg_idx].start_off[tx] * 0x4;
+
+ if (sai->is_multi_fifo_dma) {
+ sai->audio_config[tx].words_per_fifo = min(slots, channels);
+ if (tx) {
+ sai->audio_config[tx].n_fifos_dst = pins;
+ sai->audio_config[tx].stride_fifos_dst = dl_cfg[dl_cfg_idx].next_off[tx];
+ } else {
+ sai->audio_config[tx].n_fifos_src = pins;
+ sai->audio_config[tx].stride_fifos_src = dl_cfg[dl_cfg_idx].next_off[tx];
}
+ dma_params->maxburst = sai->audio_config[tx].words_per_fifo * pins;
+ dma_params->peripheral_config = &sai->audio_config[tx];
+ dma_params->peripheral_size = sizeof(sai->audio_config[tx]);
+
+ watermark = tx ? (sai->soc_data->fifo_depth - dma_params->maxburst) :
+ (dma_params->maxburst - 1);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR1(tx, ofs),
+ FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
+ watermark);
+ }
+
+ /* Find a proper tcre setting */
+ for (i = 0; i < sai->soc_data->pins; i++) {
+ trce_mask = (1 << (i + 1)) - 1;
+ if (hweight8(dl_cfg[dl_cfg_idx].mask[tx] & trce_mask) == pins)
+ break;
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
- FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
+ FSL_SAI_CR3_TRCE_MASK,
+ FSL_SAI_CR3_TRCE((dl_cfg[dl_cfg_idx].mask[tx] & trce_mask)));
+
+ /*
+ * When the TERE and FSD_MSTR enabled before configuring the word width
+ * There will be no frame sync clock issue, because word width impact
+ * the generation of frame sync clock.
+ *
+ * TERE enabled earlier only for i.MX8MP case for the hardware limitation,
+ * We need to disable FSD_MSTR before configuring word width, then enable
+ * FSD_MSTR bit for this specific case.
+ */
+ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
+ !sai->is_consumer_mode[tx])
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
+ FSL_SAI_CR4_FSD_MSTR, 0);
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
+ FSL_SAI_CR4_CHMOD_MASK | FSL_SAI_CR4_FCONT_MASK,
val_cr4);
- regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs),
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
- regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1));
+
+ /* Enable FSD_MSTR after configuring word width */
+ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
+ !sai->is_consumer_mode[tx])
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
+ FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR);
+
+ regmap_write(sai->regmap, FSL_SAI_xMR(tx),
+ ~0UL - ((1 << min(channels, slots)) - 1));
return 0;
}
@@ -490,9 +756,16 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int ofs = sai->soc_data->reg_offset;
- if (!sai->is_slave_mode &&
- sai->mclk_streams & BIT(substream->stream)) {
+ /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */
+ regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0);
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
+ FSL_SAI_CR3_TRCE_MASK, 0);
+
+ if (!sai->is_consumer_mode[tx] &&
+ sai->mclk_streams & BIT(substream->stream)) {
clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
sai->mclk_streams &= ~BIT(substream->stream);
}
@@ -500,22 +773,65 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
return 0;
}
+static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
+{
+ unsigned int ofs = sai->soc_data->reg_offset;
+ bool tx = dir == TX;
+ u32 xcsr, count = 100, mask;
+
+ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output)
+ mask = FSL_SAI_CSR_TERE;
+ else
+ mask = FSL_SAI_CSR_TERE | FSL_SAI_CSR_BCE;
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
+ mask, 0);
+
+ /* TERE will remain set till the end of current frame */
+ do {
+ udelay(10);
+ regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
+ } while (--count && xcsr & FSL_SAI_CSR_TERE);
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
+ FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+
+ /*
+ * For sai master mode, after several open/close sai,
+ * there will be no frame clock, and can't recover
+ * anymore. Add software reset to fix this issue.
+ * This is a hardware bug, and will be fix in the
+ * next sai version.
+ *
+ * In consumer mode, this can happen even after a
+ * single open/close, especially if both tx and rx
+ * are running concurrently.
+ */
+ /* Software Reset */
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR, 0);
+}
static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u32 xcsr, count = 100;
+ int adir = tx ? RX : TX;
+ int dir = tx ? TX : RX;
+ u32 xcsr;
/*
* Asynchronous mode: Clear SYNC for both Tx and Rx.
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
- sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
- regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), FSL_SAI_CR2_SYNC,
+ sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
/*
@@ -526,63 +842,56 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+ /*
+ * Enable the opposite direction for synchronous mode
+ * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx
+ * 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx
+ *
+ * RM recommends to enable RE after TE for case 1 and to enable
+ * TE after RE for case 2, but we here may not always guarantee
+ * that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables
+ * TE after RE, which is against what RM recommends but should
+ * be safe to do, judging by years of testing results.
+ */
+ if (fsl_sai_dir_is_synced(sai, adir))
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs),
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, 0);
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, 0);
/* Check if the opposite FRDE is also disabled */
- regmap_read(sai->regmap, FSL_SAI_xCSR(!tx), &xcsr);
- if (!(xcsr & FSL_SAI_CSR_FRDE)) {
- /* Disable both directions and reset their FIFOs */
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_TERE, 0);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_TERE, 0);
-
- /* TERE will remain set till the end of current frame */
- do {
- udelay(10);
- regmap_read(sai->regmap, FSL_SAI_xCSR(tx), &xcsr);
- } while (--count && xcsr & FSL_SAI_CSR_TERE);
-
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
-
- /*
- * For sai master mode, after several open/close sai,
- * there will be no frame clock, and can't recover
- * anymore. Add software reset to fix this issue.
- * This is a hardware bug, and will be fix in the
- * next sai version.
- */
- if (!sai->is_slave_mode) {
- /* Software Reset for both Tx and Rx */
- regmap_write(sai->regmap,
- FSL_SAI_TCSR, FSL_SAI_CSR_SR);
- regmap_write(sai->regmap,
- FSL_SAI_RCSR, FSL_SAI_CSR_SR);
- /* Clear SR bit to finish the reset */
- regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
- regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
- }
- }
+ regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr);
+
+ /*
+ * If opposite stream provides clocks for synchronous mode and
+ * it is inactive, disable it before disabling the current one
+ */
+ if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE))
+ fsl_sai_config_disable(sai, adir);
+
+ /*
+ * Disable current stream if either of:
+ * 1. current stream doesn't provide clocks for synchronous mode
+ * 2. current stream provides clocks for synchronous mode but no
+ * more stream is active.
+ */
+ if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE))
+ fsl_sai_config_disable(sai, dir);
+
break;
default:
return -EINVAL;
@@ -596,36 +905,52 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct device *dev = &sai->pdev->dev;
int ret;
- ret = clk_prepare_enable(sai->bus_clk);
- if (ret) {
- dev_err(dev, "failed to enable bus clock: %d\n", ret);
- return ret;
- }
-
- regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
- FSL_SAI_CR3_TRCE);
+ /*
+ * EDMA controller needs period size to be a multiple of
+ * tx/rx maxburst
+ */
+ if (sai->soc_data->use_edma)
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ tx ? sai->dma_params_tx.maxburst :
+ sai->dma_params_rx.maxburst);
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
+ SNDRV_PCM_HW_PARAM_RATE, &sai->constraint_rates);
return ret;
}
-static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
+static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
{
- struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ /* Software Reset for both Tx and Rx */
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, 0);
- regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR1(ofs),
+ FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
+ sai->soc_data->fifo_depth - sai->dma_params_tx.maxburst);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR1(ofs),
+ FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
+ sai->dma_params_rx.maxburst - 1);
- clk_disable_unprepare(sai->bus_clk);
+ snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
+ &sai->dma_params_rx);
+
+ return 0;
}
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
+ .probe = fsl_sai_dai_probe,
+ .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt,
.set_tdm_slot = fsl_sai_set_dai_tdm_slot,
@@ -633,96 +958,206 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
.startup = fsl_sai_startup,
- .shutdown = fsl_sai_shutdown,
};
-static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
-{
- struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
-
- /* Software Reset for both Tx and Rx */
- regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
- regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
- /* Clear SR bit to finish the reset */
- regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
- regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+static const struct snd_soc_dai_ops fsl_sai_pcm_dai_tx_ops = {
+ .probe = fsl_sai_dai_probe,
+ .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
+ .set_sysclk = fsl_sai_set_dai_sysclk,
+ .set_fmt = fsl_sai_set_dai_fmt_tx,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot_tx,
+ .xlate_tdm_slot_mask = fsl_sai_xlate_tdm_slot_mask,
+ .hw_params = fsl_sai_hw_params,
+ .hw_free = fsl_sai_hw_free,
+ .trigger = fsl_sai_trigger,
+ .startup = fsl_sai_startup,
+};
- regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
- FSL_SAI_MAXBURST_TX * 2);
- regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
- FSL_SAI_MAXBURST_RX - 1);
+static const struct snd_soc_dai_ops fsl_sai_pcm_dai_rx_ops = {
+ .probe = fsl_sai_dai_probe,
+ .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
+ .set_sysclk = fsl_sai_set_dai_sysclk,
+ .set_fmt = fsl_sai_set_dai_fmt_rx,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot_rx,
+ .xlate_tdm_slot_mask = fsl_sai_xlate_tdm_slot_mask,
+ .hw_params = fsl_sai_hw_params,
+ .hw_free = fsl_sai_hw_free,
+ .trigger = fsl_sai_trigger,
+ .startup = fsl_sai_startup,
+};
- snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
- &sai->dma_params_rx);
+static int fsl_sai_dai_resume(struct snd_soc_component *component)
+{
+ struct fsl_sai *sai = snd_soc_component_get_drvdata(component);
+ struct device *dev = &sai->pdev->dev;
+ int ret;
- snd_soc_dai_set_drvdata(cpu_dai, sai);
+ if (!IS_ERR_OR_NULL(sai->pinctrl) && !IS_ERR_OR_NULL(sai->pins_state)) {
+ ret = pinctrl_select_state(sai->pinctrl, sai->pins_state);
+ if (ret) {
+ dev_err(dev, "failed to set proper pins state: %d\n", ret);
+ return ret;
+ }
+ }
return 0;
}
-static struct snd_soc_dai_driver fsl_sai_dai = {
- .probe = fsl_sai_dai_probe,
- .playback = {
- .stream_name = "CPU-Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_SAI_FORMATS,
+static struct snd_soc_dai_driver fsl_sai_dai_template[] = {
+ {
+ .name = "sai-tx-rx",
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_ops,
+ },
+ {
+ .name = "sai-tx",
+ .playback = {
+ .stream_name = "SAI-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_tx_ops,
},
- .capture = {
- .stream_name = "CPU-Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rate_min = 8000,
- .rate_max = 192000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_SAI_FORMATS,
+ {
+ .name = "sai-rx",
+ .capture = {
+ .stream_name = "SAI-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_rx_ops,
},
- .ops = &fsl_sai_pcm_dai_ops,
};
static const struct snd_soc_component_driver fsl_component = {
- .name = "fsl-sai",
+ .name = "fsl-sai",
+ .resume = fsl_sai_dai_resume,
+ .legacy_dai_naming = 1,
+};
+
+static const struct reg_default fsl_sai_reg_defaults_ofs0[] = {
+ {FSL_SAI_TCR1(0), 0},
+ {FSL_SAI_TCR2(0), 0},
+ {FSL_SAI_TCR3(0), 0},
+ {FSL_SAI_TCR4(0), 0},
+ {FSL_SAI_TCR5(0), 0},
+ {FSL_SAI_TDR0, 0},
+ {FSL_SAI_TDR1, 0},
+ {FSL_SAI_TDR2, 0},
+ {FSL_SAI_TDR3, 0},
+ {FSL_SAI_TDR4, 0},
+ {FSL_SAI_TDR5, 0},
+ {FSL_SAI_TDR6, 0},
+ {FSL_SAI_TDR7, 0},
+ {FSL_SAI_TMR, 0},
+ {FSL_SAI_RCR1(0), 0},
+ {FSL_SAI_RCR2(0), 0},
+ {FSL_SAI_RCR3(0), 0},
+ {FSL_SAI_RCR4(0), 0},
+ {FSL_SAI_RCR5(0), 0},
+ {FSL_SAI_RMR, 0},
};
-static struct reg_default fsl_sai_reg_defaults[] = {
- {FSL_SAI_TCR1, 0},
- {FSL_SAI_TCR2, 0},
- {FSL_SAI_TCR3, 0},
- {FSL_SAI_TCR4, 0},
- {FSL_SAI_TCR5, 0},
- {FSL_SAI_TDR, 0},
- {FSL_SAI_TMR, 0},
- {FSL_SAI_RCR1, 0},
- {FSL_SAI_RCR2, 0},
- {FSL_SAI_RCR3, 0},
- {FSL_SAI_RCR4, 0},
- {FSL_SAI_RCR5, 0},
- {FSL_SAI_RMR, 0},
+static const struct reg_default fsl_sai_reg_defaults_ofs8[] = {
+ {FSL_SAI_TCR1(8), 0},
+ {FSL_SAI_TCR2(8), 0},
+ {FSL_SAI_TCR3(8), 0},
+ {FSL_SAI_TCR4(8), 0},
+ {FSL_SAI_TCR5(8), 0},
+ {FSL_SAI_TDR0, 0},
+ {FSL_SAI_TDR1, 0},
+ {FSL_SAI_TDR2, 0},
+ {FSL_SAI_TDR3, 0},
+ {FSL_SAI_TDR4, 0},
+ {FSL_SAI_TDR5, 0},
+ {FSL_SAI_TDR6, 0},
+ {FSL_SAI_TDR7, 0},
+ {FSL_SAI_TMR, 0},
+ {FSL_SAI_RCR1(8), 0},
+ {FSL_SAI_RCR2(8), 0},
+ {FSL_SAI_RCR3(8), 0},
+ {FSL_SAI_RCR4(8), 0},
+ {FSL_SAI_RCR5(8), 0},
+ {FSL_SAI_RMR, 0},
+ {FSL_SAI_MCTL, 0},
+ {FSL_SAI_MDIV, 0},
};
static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs))
+ return true;
+
+ if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs))
+ return true;
+
switch (reg) {
- case FSL_SAI_TCSR:
- case FSL_SAI_TCR1:
- case FSL_SAI_TCR2:
- case FSL_SAI_TCR3:
- case FSL_SAI_TCR4:
- case FSL_SAI_TCR5:
- case FSL_SAI_TFR:
+ case FSL_SAI_TFR0:
+ case FSL_SAI_TFR1:
+ case FSL_SAI_TFR2:
+ case FSL_SAI_TFR3:
+ case FSL_SAI_TFR4:
+ case FSL_SAI_TFR5:
+ case FSL_SAI_TFR6:
+ case FSL_SAI_TFR7:
case FSL_SAI_TMR:
- case FSL_SAI_RCSR:
- case FSL_SAI_RCR1:
- case FSL_SAI_RCR2:
- case FSL_SAI_RCR3:
- case FSL_SAI_RCR4:
- case FSL_SAI_RCR5:
- case FSL_SAI_RDR:
- case FSL_SAI_RFR:
+ case FSL_SAI_RDR0:
+ case FSL_SAI_RDR1:
+ case FSL_SAI_RDR2:
+ case FSL_SAI_RDR3:
+ case FSL_SAI_RDR4:
+ case FSL_SAI_RDR5:
+ case FSL_SAI_RDR6:
+ case FSL_SAI_RDR7:
+ case FSL_SAI_RFR0:
+ case FSL_SAI_RFR1:
+ case FSL_SAI_RFR2:
+ case FSL_SAI_RFR3:
+ case FSL_SAI_RFR4:
+ case FSL_SAI_RFR5:
+ case FSL_SAI_RFR6:
+ case FSL_SAI_RFR7:
case FSL_SAI_RMR:
+ case FSL_SAI_MCTL:
+ case FSL_SAI_MDIV:
+ case FSL_SAI_VERID:
+ case FSL_SAI_PARAM:
+ case FSL_SAI_TTCTN:
+ case FSL_SAI_RTCTN:
+ case FSL_SAI_TTCTL:
+ case FSL_SAI_TBCTN:
+ case FSL_SAI_TTCAP:
+ case FSL_SAI_RTCTL:
+ case FSL_SAI_RBCTN:
+ case FSL_SAI_RTCAP:
return true;
default:
return false;
@@ -731,12 +1166,41 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ if (reg == FSL_SAI_TCSR(ofs) || reg == FSL_SAI_RCSR(ofs))
+ return true;
+
+ /* Set VERID and PARAM be volatile for reading value in probe */
+ if (ofs == 8 && (reg == FSL_SAI_VERID || reg == FSL_SAI_PARAM))
+ return true;
+
switch (reg) {
- case FSL_SAI_TCSR:
- case FSL_SAI_RCSR:
- case FSL_SAI_TFR:
- case FSL_SAI_RFR:
- case FSL_SAI_RDR:
+ case FSL_SAI_TFR0:
+ case FSL_SAI_TFR1:
+ case FSL_SAI_TFR2:
+ case FSL_SAI_TFR3:
+ case FSL_SAI_TFR4:
+ case FSL_SAI_TFR5:
+ case FSL_SAI_TFR6:
+ case FSL_SAI_TFR7:
+ case FSL_SAI_RFR0:
+ case FSL_SAI_RFR1:
+ case FSL_SAI_RFR2:
+ case FSL_SAI_RFR3:
+ case FSL_SAI_RFR4:
+ case FSL_SAI_RFR5:
+ case FSL_SAI_RFR6:
+ case FSL_SAI_RFR7:
+ case FSL_SAI_RDR0:
+ case FSL_SAI_RDR1:
+ case FSL_SAI_RDR2:
+ case FSL_SAI_RDR3:
+ case FSL_SAI_RDR4:
+ case FSL_SAI_RDR5:
+ case FSL_SAI_RDR6:
+ case FSL_SAI_RDR7:
return true;
default:
return false;
@@ -745,196 +1209,658 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs))
+ return true;
+
+ if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs))
+ return true;
+
switch (reg) {
- case FSL_SAI_TCSR:
- case FSL_SAI_TCR1:
- case FSL_SAI_TCR2:
- case FSL_SAI_TCR3:
- case FSL_SAI_TCR4:
- case FSL_SAI_TCR5:
- case FSL_SAI_TDR:
+ case FSL_SAI_TDR0:
+ case FSL_SAI_TDR1:
+ case FSL_SAI_TDR2:
+ case FSL_SAI_TDR3:
+ case FSL_SAI_TDR4:
+ case FSL_SAI_TDR5:
+ case FSL_SAI_TDR6:
+ case FSL_SAI_TDR7:
case FSL_SAI_TMR:
- case FSL_SAI_RCSR:
- case FSL_SAI_RCR1:
- case FSL_SAI_RCR2:
- case FSL_SAI_RCR3:
- case FSL_SAI_RCR4:
- case FSL_SAI_RCR5:
case FSL_SAI_RMR:
+ case FSL_SAI_MCTL:
+ case FSL_SAI_MDIV:
+ case FSL_SAI_TTCTL:
+ case FSL_SAI_RTCTL:
return true;
default:
return false;
}
}
-static const struct regmap_config fsl_sai_regmap_config = {
+static struct regmap_config fsl_sai_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = FSL_SAI_RMR,
- .reg_defaults = fsl_sai_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
+ .reg_defaults = fsl_sai_reg_defaults_ofs0,
+ .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults_ofs0),
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,
.cache_type = REGCACHE_FLAT,
};
+static int fsl_sai_check_version(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned char ofs = sai->soc_data->reg_offset;
+ unsigned int val;
+ int ret;
+
+ if (FSL_SAI_TCSR(ofs) == FSL_SAI_VERID)
+ return 0;
+
+ ret = regmap_read(sai->regmap, FSL_SAI_VERID, &val);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "VERID: 0x%016X\n", val);
+
+ sai->verid.version = val &
+ (FSL_SAI_VERID_MAJOR_MASK | FSL_SAI_VERID_MINOR_MASK);
+ sai->verid.version >>= FSL_SAI_VERID_MINOR_SHIFT;
+ sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK;
+
+ ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "PARAM: 0x%016X\n", val);
+
+ /* Max slots per frame, power of 2 */
+ sai->param.slot_num = 1 <<
+ ((val & FSL_SAI_PARAM_SPF_MASK) >> FSL_SAI_PARAM_SPF_SHIFT);
+
+ /* Words per fifo, power of 2 */
+ sai->param.fifo_depth = 1 <<
+ ((val & FSL_SAI_PARAM_WPF_MASK) >> FSL_SAI_PARAM_WPF_SHIFT);
+
+ /* Number of datalines implemented */
+ sai->param.dataline = val & FSL_SAI_PARAM_DLN_MASK;
+
+ return 0;
+}
+
+/*
+ * Calculate the offset between first two datalines, don't
+ * different offset in one case.
+ */
+static unsigned int fsl_sai_calc_dl_off(unsigned long dl_mask)
+{
+ int fbidx, nbidx, offset;
+
+ fbidx = find_first_bit(&dl_mask, FSL_SAI_DL_NUM);
+ nbidx = find_next_bit(&dl_mask, FSL_SAI_DL_NUM, fbidx + 1);
+ offset = nbidx - fbidx - 1;
+
+ return (offset < 0 || offset >= (FSL_SAI_DL_NUM - 1) ? 0 : offset);
+}
+
+/*
+ * read the fsl,dataline property from dts file.
+ * It has 3 value for each configuration, first one means the type:
+ * I2S(1) or PDM(2), second one is dataline mask for 'rx', third one is
+ * dataline mask for 'tx'. for example
+ *
+ * fsl,dataline = <1 0xff 0xff 2 0xff 0x11>,
+ *
+ * It means I2S type rx mask is 0xff, tx mask is 0xff, PDM type
+ * rx mask is 0xff, tx mask is 0x11 (dataline 1 and 4 enabled).
+ *
+ */
+static int fsl_sai_read_dlcfg(struct fsl_sai *sai)
+{
+ struct platform_device *pdev = sai->pdev;
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret, elems, i, index, num_cfg;
+ char *propname = "fsl,dataline";
+ struct fsl_sai_dl_cfg *cfg;
+ unsigned long dl_mask;
+ unsigned int soc_dl;
+ u32 rx, tx, type;
+
+ elems = of_property_count_u32_elems(np, propname);
+
+ if (elems <= 0) {
+ elems = 0;
+ } else if (elems % 3) {
+ dev_err(dev, "Number of elements must be divisible to 3.\n");
+ return -EINVAL;
+ }
+
+ num_cfg = elems / 3;
+ /* Add one more for default value */
+ cfg = devm_kcalloc(&pdev->dev, num_cfg + 1, sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ /* Consider default value "0 0xFF 0xFF" if property is missing */
+ soc_dl = BIT(sai->soc_data->pins) - 1;
+ cfg[0].type = FSL_SAI_DL_DEFAULT;
+ cfg[0].pins[0] = sai->soc_data->pins;
+ cfg[0].mask[0] = soc_dl;
+ cfg[0].start_off[0] = 0;
+ cfg[0].next_off[0] = 0;
+
+ cfg[0].pins[1] = sai->soc_data->pins;
+ cfg[0].mask[1] = soc_dl;
+ cfg[0].start_off[1] = 0;
+ cfg[0].next_off[1] = 0;
+ for (i = 1, index = 0; i < num_cfg + 1; i++) {
+ /*
+ * type of dataline
+ * 0 means default mode
+ * 1 means I2S mode
+ * 2 means PDM mode
+ */
+ ret = of_property_read_u32_index(np, propname, index++, &type);
+ if (ret)
+ return -EINVAL;
+
+ ret = of_property_read_u32_index(np, propname, index++, &rx);
+ if (ret)
+ return -EINVAL;
+
+ ret = of_property_read_u32_index(np, propname, index++, &tx);
+ if (ret)
+ return -EINVAL;
+
+ if ((rx & ~soc_dl) || (tx & ~soc_dl)) {
+ dev_err(dev, "dataline cfg[%d] setting error, mask is 0x%x\n", i, soc_dl);
+ return -EINVAL;
+ }
+
+ rx = rx & soc_dl;
+ tx = tx & soc_dl;
+
+ cfg[i].type = type;
+ cfg[i].pins[0] = hweight8(rx);
+ cfg[i].mask[0] = rx;
+ dl_mask = rx;
+ cfg[i].start_off[0] = find_first_bit(&dl_mask, FSL_SAI_DL_NUM);
+ cfg[i].next_off[0] = fsl_sai_calc_dl_off(rx);
+
+ cfg[i].pins[1] = hweight8(tx);
+ cfg[i].mask[1] = tx;
+ dl_mask = tx;
+ cfg[i].start_off[1] = find_first_bit(&dl_mask, FSL_SAI_DL_NUM);
+ cfg[i].next_off[1] = fsl_sai_calc_dl_off(tx);
+ }
+
+ sai->dl_cfg = cfg;
+ sai->dl_cfg_cnt = num_cfg + 1;
+ return 0;
+}
+
+static int fsl_sai_runtime_suspend(struct device *dev);
+static int fsl_sai_runtime_resume(struct device *dev);
+
static int fsl_sai_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
struct fsl_sai *sai;
- struct resource *res;
+ struct regmap *gpr;
void __iomem *base;
char tmp[8];
int irq, ret, i;
+ int index;
+ u32 dmas[4];
- sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
return -ENOMEM;
sai->pdev = pdev;
-
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
- sai->sai_on_imx = true;
+ sai->soc_data = of_device_get_match_data(dev);
sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &sai->res);
if (IS_ERR(base))
return PTR_ERR(base);
- sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
- "bus", base, &fsl_sai_regmap_config);
+ if (sai->soc_data->reg_offset == 8) {
+ fsl_sai_regmap_config.reg_defaults = fsl_sai_reg_defaults_ofs8;
+ fsl_sai_regmap_config.max_register = FSL_SAI_MDIV;
+ fsl_sai_regmap_config.num_reg_defaults =
+ ARRAY_SIZE(fsl_sai_reg_defaults_ofs8);
+ }
- /* Compatible with old DTB cases */
- if (IS_ERR(sai->regmap))
- sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
- "sai", base, &fsl_sai_regmap_config);
+ sai->regmap = devm_regmap_init_mmio(dev, base, &fsl_sai_regmap_config);
if (IS_ERR(sai->regmap)) {
- dev_err(&pdev->dev, "regmap init failed\n");
+ dev_err(dev, "regmap init failed\n");
return PTR_ERR(sai->regmap);
}
- /* No error out for old DTB cases but only mark the clock NULL */
- sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ sai->bus_clk = devm_clk_get(dev, "bus");
+ /* Compatible with old DTB cases */
+ if (IS_ERR(sai->bus_clk) && PTR_ERR(sai->bus_clk) != -EPROBE_DEFER)
+ sai->bus_clk = devm_clk_get(dev, "sai");
if (IS_ERR(sai->bus_clk)) {
- dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
+ dev_err(dev, "failed to get bus clock: %ld\n",
PTR_ERR(sai->bus_clk));
- sai->bus_clk = NULL;
+ /* -EPROBE_DEFER */
+ return PTR_ERR(sai->bus_clk);
}
- sai->mclk_clk[0] = sai->bus_clk;
for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
sprintf(tmp, "mclk%d", i);
- sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
+ sai->mclk_clk[i] = devm_clk_get(dev, tmp);
if (IS_ERR(sai->mclk_clk[i])) {
- dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
- i + 1, PTR_ERR(sai->mclk_clk[i]));
+ dev_err(dev, "failed to get mclk%d clock: %ld\n",
+ i, PTR_ERR(sai->mclk_clk[i]));
sai->mclk_clk[i] = NULL;
}
}
+ if (sai->soc_data->mclk0_is_mclk1)
+ sai->mclk_clk[0] = sai->mclk_clk[1];
+ else
+ sai->mclk_clk[0] = sai->bus_clk;
+
+ fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
+ &sai->pll11k_clk);
+
+ fsl_asoc_constrain_rates(&sai->constraint_rates,
+ &fsl_sai_rate_constraints,
+ sai->pll8k_clk, sai->pll11k_clk, NULL,
+ sai->constraint_rates_list);
+
+ /* Use Multi FIFO mode depending on the support from SDMA script */
+ ret = of_property_read_u32_array(np, "dmas", dmas, 4);
+ if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
+ sai->is_multi_fifo_dma = true;
+
+ /* read dataline mask for rx and tx*/
+ ret = fsl_sai_read_dlcfg(sai);
+ if (ret < 0) {
+ dev_err(dev, "failed to read dlcfg %d\n", ret);
+ return ret;
+ }
+
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, 0, np->name, sai);
+ ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED,
+ np->name, sai);
if (ret) {
- dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
+ dev_err(dev, "failed to claim irq %u\n", irq);
return ret;
}
+ memcpy(&sai->cpu_dai_drv, fsl_sai_dai_template,
+ sizeof(*fsl_sai_dai_template) * ARRAY_SIZE(fsl_sai_dai_template));
+
/* Sync Tx with Rx as default by following old DT binding */
sai->synchronous[RX] = true;
sai->synchronous[TX] = false;
- fsl_sai_dai.symmetric_rates = 1;
- fsl_sai_dai.symmetric_channels = 1;
- fsl_sai_dai.symmetric_samplebits = 1;
+ sai->cpu_dai_drv[0].symmetric_rate = 1;
+ sai->cpu_dai_drv[0].symmetric_channels = 1;
+ sai->cpu_dai_drv[0].symmetric_sample_bits = 1;
- if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) &&
- of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+ if (of_property_read_bool(np, "fsl,sai-synchronous-rx") &&
+ of_property_read_bool(np, "fsl,sai-asynchronous")) {
/* error out if both synchronous and asynchronous are present */
- dev_err(&pdev->dev, "invalid binding for synchronous mode\n");
+ dev_err(dev, "invalid binding for synchronous mode\n");
return -EINVAL;
}
- if (of_find_property(np, "fsl,sai-synchronous-rx", NULL)) {
+ if (of_property_read_bool(np, "fsl,sai-synchronous-rx")) {
/* Sync Rx with Tx */
sai->synchronous[RX] = false;
sai->synchronous[TX] = true;
- } else if (of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+ } else if (of_property_read_bool(np, "fsl,sai-asynchronous")) {
/* Discard all settings for asynchronous mode */
sai->synchronous[RX] = false;
sai->synchronous[TX] = false;
- fsl_sai_dai.symmetric_rates = 0;
- fsl_sai_dai.symmetric_channels = 0;
- fsl_sai_dai.symmetric_samplebits = 0;
+ sai->cpu_dai_drv[0].symmetric_rate = 0;
+ sai->cpu_dai_drv[0].symmetric_channels = 0;
+ sai->cpu_dai_drv[0].symmetric_sample_bits = 0;
+ }
+
+ sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output");
+
+ if (sai->mclk_direction_output &&
+ of_device_is_compatible(np, "fsl,imx6ul-sai")) {
+ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
+ if (IS_ERR(gpr)) {
+ dev_err(dev, "cannot find iomuxc registers\n");
+ return PTR_ERR(gpr);
+ }
+
+ index = of_alias_get_id(np, "sai");
+ if (index < 0)
+ return index;
+
+ regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index),
+ MCLK_DIR(index));
}
- sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
- sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
- sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
- sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
+ sai->dma_params_rx.addr = sai->res->start + FSL_SAI_RDR0;
+ sai->dma_params_tx.addr = sai->res->start + FSL_SAI_TDR0;
+ sai->dma_params_rx.maxburst =
+ sai->soc_data->max_burst[RX] ? sai->soc_data->max_burst[RX] : FSL_SAI_MAXBURST_RX;
+ sai->dma_params_tx.maxburst =
+ sai->soc_data->max_burst[TX] ? sai->soc_data->max_burst[TX] : FSL_SAI_MAXBURST_TX;
+
+ sai->pinctrl = devm_pinctrl_get(&pdev->dev);
platform_set_drvdata(pdev, sai);
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = fsl_sai_runtime_resume(dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ goto err_pm_get_sync;
+
+ /* Get sai version */
+ ret = fsl_sai_check_version(dev);
+ if (ret < 0)
+ dev_warn(dev, "Error reading SAI version: %d\n", ret);
- ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
- &fsl_sai_dai, 1);
+ /* Select MCLK direction */
+ if (sai->mclk_direction_output &&
+ sai->soc_data->max_register >= FSL_SAI_MCTL) {
+ regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
+ FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
+ }
+
+ ret = pm_runtime_put_sync(dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto err_pm_get_sync;
+
+ /*
+ * Register platform component before registering cpu dai for there
+ * is not defer probe for platform component in snd_soc_add_pcm_runtime().
+ */
+ if (sai->soc_data->use_imx_pcm) {
+ ret = imx_pcm_dma_init(pdev);
+ if (ret) {
+ dev_err_probe(dev, ret, "PCM DMA init failed\n");
+ if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA))
+ dev_err(dev, "Error: You must enable the imx-pcm-dma support!\n");
+ goto err_pm_get_sync;
+ }
+ } else {
+ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
+ if (ret) {
+ dev_err_probe(dev, ret, "Registering PCM dmaengine failed\n");
+ goto err_pm_get_sync;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(dev, &fsl_component,
+ sai->cpu_dai_drv, ARRAY_SIZE(fsl_sai_dai_template));
if (ret)
- return ret;
+ goto err_pm_get_sync;
- if (sai->sai_on_imx)
- return imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE);
- else
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ return ret;
+
+err_pm_get_sync:
+ if (!pm_runtime_status_suspended(dev))
+ fsl_sai_runtime_suspend(dev);
+err_pm_disable:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static void fsl_sai_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ fsl_sai_runtime_suspend(&pdev->dev);
}
+static const struct fsl_sai_soc_data fsl_sai_vf610_data = {
+ .use_imx_pcm = false,
+ .use_edma = false,
+ .fifo_depth = 32,
+ .pins = 1,
+ .reg_offset = 0,
+ .mclk0_is_mclk1 = false,
+ .flags = 0,
+ .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 32,
+ .pins = 1,
+ .reg_offset = 0,
+ .mclk0_is_mclk1 = true,
+ .flags = 0,
+ .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 16,
+ .pins = 2,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .flags = PMQOS_CPU_LATENCY,
+ .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 128,
+ .pins = 8,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .flags = 0,
+ .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 64,
+ .pins = 4,
+ .reg_offset = 0,
+ .mclk0_is_mclk1 = false,
+ .flags = 0,
+ .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mm_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 8,
+ .flags = 0,
+ .max_register = FSL_SAI_MCTL,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mn_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 8,
+ .flags = 0,
+ .max_register = FSL_SAI_MDIV,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 8,
+ .flags = 0,
+ .max_register = FSL_SAI_MDIV,
+ .mclk_with_tere = true,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 16,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 4,
+ .flags = PMQOS_CPU_LATENCY,
+ .max_register = FSL_SAI_RTCAP,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx93_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 4,
+ .flags = 0,
+ .max_register = FSL_SAI_MCTL,
+ .max_burst = {8, 8},
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx95_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 8,
+ .flags = 0,
+ .max_register = FSL_SAI_MCTL,
+ .max_burst = {8, 8},
+};
+
static const struct of_device_id fsl_sai_ids[] = {
- { .compatible = "fsl,vf610-sai", },
- { .compatible = "fsl,imx6sx-sai", },
+ { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data },
+ { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data },
+ { .compatible = "fsl,imx6ul-sai", .data = &fsl_sai_imx6sx_data },
+ { .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data },
+ { .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data },
+ { .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data },
+ { .compatible = "fsl,imx8mm-sai", .data = &fsl_sai_imx8mm_data },
+ { .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data },
+ { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data },
+ { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data },
+ { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data },
+ { .compatible = "fsl,imx95-sai", .data = &fsl_sai_imx95_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
-#ifdef CONFIG_PM_SLEEP
-static int fsl_sai_suspend(struct device *dev)
+static int fsl_sai_runtime_suspend(struct device *dev)
{
struct fsl_sai *sai = dev_get_drvdata(dev);
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]);
+
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]);
+
+ clk_disable_unprepare(sai->bus_clk);
+
+ if (sai->soc_data->flags & PMQOS_CPU_LATENCY)
+ cpu_latency_qos_remove_request(&sai->pm_qos_req);
+
regcache_cache_only(sai->regmap, true);
- regcache_mark_dirty(sai->regmap);
return 0;
}
-static int fsl_sai_resume(struct device *dev)
+static int fsl_sai_runtime_resume(struct device *dev)
{
struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+ int ret;
+
+ ret = clk_prepare_enable(sai->bus_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable bus clock: %d\n", ret);
+ return ret;
+ }
+
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK)) {
+ ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[1]]);
+ if (ret)
+ goto disable_bus_clk;
+ }
+
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) {
+ ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[0]]);
+ if (ret)
+ goto disable_tx_clk;
+ }
+
+ if (sai->soc_data->flags & PMQOS_CPU_LATENCY)
+ cpu_latency_qos_add_request(&sai->pm_qos_req, 0);
regcache_cache_only(sai->regmap, false);
- regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
- regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
- msleep(1);
- regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
- regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
- return regcache_sync(sai->regmap);
+ regcache_mark_dirty(sai->regmap);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR);
+ usleep_range(1000, 2000);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, 0);
+
+ ret = regcache_sync(sai->regmap);
+ if (ret)
+ goto disable_rx_clk;
+
+ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output)
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs),
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+
+ return 0;
+
+disable_rx_clk:
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]);
+disable_tx_clk:
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]);
+disable_bus_clk:
+ clk_disable_unprepare(sai->bus_clk);
+
+ return ret;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops fsl_sai_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+ RUNTIME_PM_OPS(fsl_sai_runtime_suspend, fsl_sai_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
+ .remove = fsl_sai_remove,
.driver = {
.name = "fsl-sai",
- .pm = &fsl_sai_pm_ops,
+ .pm = pm_ptr(&fsl_sai_pm_ops),
.of_match_table = fsl_sai_ids,
},
};
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index d9ed7be8cb34..6c917f79c6b0 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -1,53 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2012-2013 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 __FSL_SAI_H
#define __FSL_SAI_H
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
+#define FAL_SAI_NUM_RATES 20
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_DSD_U8 |\
+ SNDRV_PCM_FMTBIT_DSD_U16_LE |\
+ SNDRV_PCM_FMTBIT_DSD_U32_LE)
/* SAI Register Map Register */
-#define FSL_SAI_TCSR 0x00 /* SAI Transmit Control */
-#define FSL_SAI_TCR1 0x04 /* SAI Transmit Configuration 1 */
-#define FSL_SAI_TCR2 0x08 /* SAI Transmit Configuration 2 */
-#define FSL_SAI_TCR3 0x0c /* SAI Transmit Configuration 3 */
-#define FSL_SAI_TCR4 0x10 /* SAI Transmit Configuration 4 */
-#define FSL_SAI_TCR5 0x14 /* SAI Transmit Configuration 5 */
-#define FSL_SAI_TDR 0x20 /* SAI Transmit Data */
-#define FSL_SAI_TFR 0x40 /* SAI Transmit FIFO */
+#define FSL_SAI_VERID 0x00 /* SAI Version ID Register */
+#define FSL_SAI_PARAM 0x04 /* SAI Parameter Register */
+#define FSL_SAI_TCSR(ofs) (0x00 + ofs) /* SAI Transmit Control */
+#define FSL_SAI_TCR1(ofs) (0x04 + ofs) /* SAI Transmit Configuration 1 */
+#define FSL_SAI_TCR2(ofs) (0x08 + ofs) /* SAI Transmit Configuration 2 */
+#define FSL_SAI_TCR3(ofs) (0x0c + ofs) /* SAI Transmit Configuration 3 */
+#define FSL_SAI_TCR4(ofs) (0x10 + ofs) /* SAI Transmit Configuration 4 */
+#define FSL_SAI_TCR5(ofs) (0x14 + ofs) /* SAI Transmit Configuration 5 */
+#define FSL_SAI_TDR0 0x20 /* SAI Transmit Data 0 */
+#define FSL_SAI_TDR1 0x24 /* SAI Transmit Data 1 */
+#define FSL_SAI_TDR2 0x28 /* SAI Transmit Data 2 */
+#define FSL_SAI_TDR3 0x2C /* SAI Transmit Data 3 */
+#define FSL_SAI_TDR4 0x30 /* SAI Transmit Data 4 */
+#define FSL_SAI_TDR5 0x34 /* SAI Transmit Data 5 */
+#define FSL_SAI_TDR6 0x38 /* SAI Transmit Data 6 */
+#define FSL_SAI_TDR7 0x3C /* SAI Transmit Data 7 */
+#define FSL_SAI_TFR0 0x40 /* SAI Transmit FIFO 0 */
+#define FSL_SAI_TFR1 0x44 /* SAI Transmit FIFO 1 */
+#define FSL_SAI_TFR2 0x48 /* SAI Transmit FIFO 2 */
+#define FSL_SAI_TFR3 0x4C /* SAI Transmit FIFO 3 */
+#define FSL_SAI_TFR4 0x50 /* SAI Transmit FIFO 4 */
+#define FSL_SAI_TFR5 0x54 /* SAI Transmit FIFO 5 */
+#define FSL_SAI_TFR6 0x58 /* SAI Transmit FIFO 6 */
+#define FSL_SAI_TFR7 0x5C /* SAI Transmit FIFO 7 */
#define FSL_SAI_TMR 0x60 /* SAI Transmit Mask */
-#define FSL_SAI_RCSR 0x80 /* SAI Receive Control */
-#define FSL_SAI_RCR1 0x84 /* SAI Receive Configuration 1 */
-#define FSL_SAI_RCR2 0x88 /* SAI Receive Configuration 2 */
-#define FSL_SAI_RCR3 0x8c /* SAI Receive Configuration 3 */
-#define FSL_SAI_RCR4 0x90 /* SAI Receive Configuration 4 */
-#define FSL_SAI_RCR5 0x94 /* SAI Receive Configuration 5 */
-#define FSL_SAI_RDR 0xa0 /* SAI Receive Data */
-#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */
+#define FSL_SAI_TTCTL 0x70 /* SAI Transmit Timestamp Control Register */
+#define FSL_SAI_TTCTN 0x74 /* SAI Transmit Timestamp Counter Register */
+#define FSL_SAI_TBCTN 0x78 /* SAI Transmit Bit Counter Register */
+#define FSL_SAI_TTCAP 0x7C /* SAI Transmit Timestamp Capture */
+#define FSL_SAI_RCSR(ofs) (0x80 + ofs) /* SAI Receive Control */
+#define FSL_SAI_RCR1(ofs) (0x84 + ofs)/* SAI Receive Configuration 1 */
+#define FSL_SAI_RCR2(ofs) (0x88 + ofs) /* SAI Receive Configuration 2 */
+#define FSL_SAI_RCR3(ofs) (0x8c + ofs) /* SAI Receive Configuration 3 */
+#define FSL_SAI_RCR4(ofs) (0x90 + ofs) /* SAI Receive Configuration 4 */
+#define FSL_SAI_RCR5(ofs) (0x94 + ofs) /* SAI Receive Configuration 5 */
+#define FSL_SAI_RDR0 0xa0 /* SAI Receive Data 0 */
+#define FSL_SAI_RDR1 0xa4 /* SAI Receive Data 1 */
+#define FSL_SAI_RDR2 0xa8 /* SAI Receive Data 2 */
+#define FSL_SAI_RDR3 0xac /* SAI Receive Data 3 */
+#define FSL_SAI_RDR4 0xb0 /* SAI Receive Data 4 */
+#define FSL_SAI_RDR5 0xb4 /* SAI Receive Data 5 */
+#define FSL_SAI_RDR6 0xb8 /* SAI Receive Data 6 */
+#define FSL_SAI_RDR7 0xbc /* SAI Receive Data 7 */
+#define FSL_SAI_RFR0 0xc0 /* SAI Receive FIFO 0 */
+#define FSL_SAI_RFR1 0xc4 /* SAI Receive FIFO 1 */
+#define FSL_SAI_RFR2 0xc8 /* SAI Receive FIFO 2 */
+#define FSL_SAI_RFR3 0xcc /* SAI Receive FIFO 3 */
+#define FSL_SAI_RFR4 0xd0 /* SAI Receive FIFO 4 */
+#define FSL_SAI_RFR5 0xd4 /* SAI Receive FIFO 5 */
+#define FSL_SAI_RFR6 0xd8 /* SAI Receive FIFO 6 */
+#define FSL_SAI_RFR7 0xdc /* SAI Receive FIFO 7 */
#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */
+#define FSL_SAI_RTCTL 0xf0 /* SAI Receive Timestamp Control Register */
+#define FSL_SAI_RTCTN 0xf4 /* SAI Receive Timestamp Counter Register */
+#define FSL_SAI_RBCTN 0xf8 /* SAI Receive Bit Counter Register */
+#define FSL_SAI_RTCAP 0xfc /* SAI Receive Timestamp Capture */
-#define FSL_SAI_xCSR(tx) (tx ? FSL_SAI_TCSR : FSL_SAI_RCSR)
-#define FSL_SAI_xCR1(tx) (tx ? FSL_SAI_TCR1 : FSL_SAI_RCR1)
-#define FSL_SAI_xCR2(tx) (tx ? FSL_SAI_TCR2 : FSL_SAI_RCR2)
-#define FSL_SAI_xCR3(tx) (tx ? FSL_SAI_TCR3 : FSL_SAI_RCR3)
-#define FSL_SAI_xCR4(tx) (tx ? FSL_SAI_TCR4 : FSL_SAI_RCR4)
-#define FSL_SAI_xCR5(tx) (tx ? FSL_SAI_TCR5 : FSL_SAI_RCR5)
-#define FSL_SAI_xDR(tx) (tx ? FSL_SAI_TDR : FSL_SAI_RDR)
-#define FSL_SAI_xFR(tx) (tx ? FSL_SAI_TFR : FSL_SAI_RFR)
+#define FSL_SAI_MCTL 0x100 /* SAI MCLK Control Register */
+#define FSL_SAI_MDIV 0x104 /* SAI MCLK Divide Register */
+
+#define FSL_SAI_xCSR(tx, ofs) (tx ? FSL_SAI_TCSR(ofs) : FSL_SAI_RCSR(ofs))
+#define FSL_SAI_xCR1(tx, ofs) (tx ? FSL_SAI_TCR1(ofs) : FSL_SAI_RCR1(ofs))
+#define FSL_SAI_xCR2(tx, ofs) (tx ? FSL_SAI_TCR2(ofs) : FSL_SAI_RCR2(ofs))
+#define FSL_SAI_xCR3(tx, ofs) (tx ? FSL_SAI_TCR3(ofs) : FSL_SAI_RCR3(ofs))
+#define FSL_SAI_xCR4(tx, ofs) (tx ? FSL_SAI_TCR4(ofs) : FSL_SAI_RCR4(ofs))
+#define FSL_SAI_xCR5(tx, ofs) (tx ? FSL_SAI_TCR5(ofs) : FSL_SAI_RCR5(ofs))
+#define FSL_SAI_xDR0(tx) (tx ? FSL_SAI_TDR0 : FSL_SAI_RDR0)
+#define FSL_SAI_xFR0(tx) (tx ? FSL_SAI_TFR0 : FSL_SAI_RFR0)
#define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
/* SAI Transmit/Receive Control Register */
#define FSL_SAI_CSR_TERE BIT(31)
+#define FSL_SAI_CSR_SE BIT(30)
+#define FSL_SAI_CSR_BCE BIT(28)
#define FSL_SAI_CSR_FR BIT(25)
#define FSL_SAI_CSR_SR BIT(24)
#define FSL_SAI_CSR_xF_SHIFT 16
@@ -69,10 +114,11 @@
#define FSL_SAI_CSR_FRDE BIT(0)
/* SAI Transmit and Receive Configuration 1 Register */
-#define FSL_SAI_CR1_RFW_MASK 0x1f
+#define FSL_SAI_CR1_RFW_MASK(x) ((x) - 1)
/* SAI Transmit and Receive Configuration 2 Register */
#define FSL_SAI_CR2_SYNC BIT(30)
+#define FSL_SAI_CR2_BCI BIT(28)
#define FSL_SAI_CR2_MSEL_MASK (0x3 << 26)
#define FSL_SAI_CR2_MSEL_BUS 0
#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26)
@@ -81,18 +127,30 @@
#define FSL_SAI_CR2_MSEL(ID) ((ID) << 26)
#define FSL_SAI_CR2_BCP BIT(25)
#define FSL_SAI_CR2_BCD_MSTR BIT(24)
+#define FSL_SAI_CR2_BYP BIT(23) /* BCLK bypass */
#define FSL_SAI_CR2_DIV_MASK 0xff
/* SAI Transmit and Receive Configuration 3 Register */
-#define FSL_SAI_CR3_TRCE BIT(16)
+#define FSL_SAI_CR3_TRCE(x) ((x) << 16)
+#define FSL_SAI_CR3_TRCE_MASK GENMASK(23, 16)
#define FSL_SAI_CR3_WDFL(x) (x)
#define FSL_SAI_CR3_WDFL_MASK 0x1f
/* SAI Transmit and Receive Configuration 4 Register */
+
+#define FSL_SAI_CR4_FCONT_MASK BIT(28)
+#define FSL_SAI_CR4_FCONT BIT(28)
+#define FSL_SAI_CR4_FCOMB_SHIFT BIT(26)
+#define FSL_SAI_CR4_FCOMB_SOFT BIT(27)
+#define FSL_SAI_CR4_FCOMB_MASK (0x3 << 26)
+#define FSL_SAI_CR4_FPACK_8 (0x2 << 24)
+#define FSL_SAI_CR4_FPACK_16 (0x3 << 24)
#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16)
#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16)
#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8)
#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8)
+#define FSL_SAI_CR4_CHMOD BIT(5)
+#define FSL_SAI_CR4_CHMOD_MASK BIT(5)
#define FSL_SAI_CR4_MF BIT(4)
#define FSL_SAI_CR4_FSE BIT(3)
#define FSL_SAI_CR4_FSP BIT(1)
@@ -106,6 +164,43 @@
#define FSL_SAI_CR5_FBT(x) ((x) << 8)
#define FSL_SAI_CR5_FBT_MASK (0x1f << 8)
+/* SAI MCLK Control Register */
+#define FSL_SAI_MCTL_MCLK_EN BIT(30) /* MCLK Enable */
+#define FSL_SAI_MCTL_MSEL_MASK (0x3 << 24)
+#define FSL_SAI_MCTL_MSEL(ID) ((ID) << 24)
+#define FSL_SAI_MCTL_MSEL_BUS 0
+#define FSL_SAI_MCTL_MSEL_MCLK1 BIT(24)
+#define FSL_SAI_MCTL_MSEL_MCLK2 BIT(25)
+#define FSL_SAI_MCTL_MSEL_MCLK3 (BIT(24) | BIT(25))
+#define FSL_SAI_MCTL_DIV_EN BIT(23)
+#define FSL_SAI_MCTL_DIV_MASK 0xFF
+
+/* SAI VERID Register */
+#define FSL_SAI_VERID_MAJOR_SHIFT 24
+#define FSL_SAI_VERID_MAJOR_MASK GENMASK(31, 24)
+#define FSL_SAI_VERID_MINOR_SHIFT 16
+#define FSL_SAI_VERID_MINOR_MASK GENMASK(23, 16)
+#define FSL_SAI_VERID_FEATURE_SHIFT 0
+#define FSL_SAI_VERID_FEATURE_MASK GENMASK(15, 0)
+#define FSL_SAI_VERID_EFIFO_EN BIT(0)
+#define FSL_SAI_VERID_TSTMP_EN BIT(1)
+
+/* SAI PARAM Register */
+#define FSL_SAI_PARAM_SPF_SHIFT 16
+#define FSL_SAI_PARAM_SPF_MASK GENMASK(19, 16)
+#define FSL_SAI_PARAM_WPF_SHIFT 8
+#define FSL_SAI_PARAM_WPF_MASK GENMASK(11, 8)
+#define FSL_SAI_PARAM_DLN_MASK GENMASK(3, 0)
+
+/* SAI MCLK Divide Register */
+#define FSL_SAI_MDIV_MASK 0xFFFFF
+
+/* SAI timestamp and bitcounter */
+#define FSL_SAI_xTCTL_TSEN BIT(0)
+#define FSL_SAI_xTCTL_TSINC BIT(1)
+#define FSL_SAI_xTCTL_RTSC BIT(8)
+#define FSL_SAI_xTCTL_RBC BIT(9)
+
/* SAI type */
#define FSL_SAI_DMA BIT(0)
#define FSL_SAI_USE_AC97 BIT(1)
@@ -114,9 +209,6 @@
#define FSL_SAI_REC_SYN BIT(4)
#define FSL_SAI_USE_I2S_SLAVE BIT(5)
-#define FSL_FMT_TRANSMITTER 0
-#define FSL_FMT_RECEIVER 1
-
/* SAI clock sources */
#define FSL_SAI_CLK_BUS 0
#define FSL_SAI_CLK_MAST1 1
@@ -129,25 +221,97 @@
#define FSL_SAI_MAXBURST_TX 6
#define FSL_SAI_MAXBURST_RX 6
+#define PMQOS_CPU_LATENCY BIT(0)
+
+/* Max number of dataline */
+#define FSL_SAI_DL_NUM (8)
+/* default dataline type is zero */
+#define FSL_SAI_DL_DEFAULT (0)
+#define FSL_SAI_DL_I2S BIT(0)
+#define FSL_SAI_DL_PDM BIT(1)
+
+struct fsl_sai_soc_data {
+ bool use_imx_pcm;
+ bool use_edma;
+ bool mclk0_is_mclk1;
+ bool mclk_with_tere;
+ unsigned int fifo_depth;
+ unsigned int pins;
+ unsigned int reg_offset;
+ unsigned int flags;
+ unsigned int max_register;
+ unsigned int max_burst[2];
+};
+
+/**
+ * struct fsl_sai_verid - version id data
+ * @version: version number
+ * @feature: feature specification number
+ * 0000000000000000b - Standard feature set
+ * 0000000000000000b - Standard feature set
+ */
+struct fsl_sai_verid {
+ u32 version;
+ u32 feature;
+};
+
+/**
+ * struct fsl_sai_param - parameter data
+ * @slot_num: The maximum number of slots per frame
+ * @fifo_depth: The number of words in each FIFO (depth)
+ * @dataline: The number of datalines implemented
+ */
+struct fsl_sai_param {
+ u32 slot_num;
+ u32 fifo_depth;
+ u32 dataline;
+};
+
+struct fsl_sai_dl_cfg {
+ unsigned int type;
+ unsigned int pins[2];
+ unsigned int mask[2];
+ unsigned int start_off[2];
+ unsigned int next_off[2];
+};
+
struct fsl_sai {
struct platform_device *pdev;
struct regmap *regmap;
struct clk *bus_clk;
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
+ struct resource *res;
- bool is_slave_mode;
+ bool is_consumer_mode[2];
bool is_lsb_first;
- bool is_dsp_mode;
- bool sai_on_imx;
+ bool is_dsp_mode[2];
+ bool is_pdm_mode;
+ bool is_multi_fifo_dma;
bool synchronous[2];
+ struct fsl_sai_dl_cfg *dl_cfg;
+ unsigned int dl_cfg_cnt;
+ bool mclk_direction_output;
unsigned int mclk_id[2];
unsigned int mclk_streams;
- unsigned int slots;
- unsigned int slot_width;
+ unsigned int slots[2];
+ unsigned int slot_width[2];
+ unsigned int bclk_ratio;
+ const struct fsl_sai_soc_data *soc_data;
+ struct snd_soc_dai_driver cpu_dai_drv[3];
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct fsl_sai_verid verid;
+ struct fsl_sai_param param;
+ struct pm_qos_request pm_qos_req;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_state;
+ struct sdma_peripheral_config audio_config[2];
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ unsigned int constraint_rates_list[FAL_SAI_NUM_RATES];
};
#define TX 1
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 151849f79863..1b9be85b34c2 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -1,31 +1,27 @@
-/*
- * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
- *
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
- *
- * Based on stmp3xxx_spdif_dai.c
- * Vladimir Barinov <vbarinov@embeddedalley.com>
- * Copyright 2008 SigmaTel, Inc
- * Copyright 2008 Embedded Alley Solutions, 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+//
+// Copyright (C) 2013 Freescale Semiconductor, Inc.
+//
+// Based on stmp3xxx_spdif_dai.c
+// Vladimir Barinov <vbarinov@embeddedalley.com>
+// Copyright 2008 SigmaTel, Inc
+// Copyright 2008 Embedded Alley Solutions, Inc
#include <linux/bitrev.h>
#include <linux/clk.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
#include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h>
#include <sound/soc.h>
#include "fsl_spdif.h"
+#include "fsl_utils.h"
#include "imx-pcm.h"
#define FSL_SPDIF_TXFIFO_WML 0x8
@@ -46,6 +42,32 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
#define DEFAULT_RXCLK_SRC 1
+#define RX_SAMPLE_RATE_KCONTROL "RX Sample Rate"
+
+/**
+ * struct fsl_spdif_soc_data: soc specific data
+ *
+ * @imx: for imx platform
+ * @shared_root_clock: flag of sharing a clock source with others;
+ * so the driver shouldn't set root clock rate
+ * @raw_capture_mode: if raw capture mode support
+ * @cchannel_192b: if there are registers for 192bits C channel data
+ * @interrupts: interrupt number
+ * @tx_burst: tx maxburst size
+ * @rx_burst: rx maxburst size
+ * @tx_formats: tx supported data format
+ */
+struct fsl_spdif_soc_data {
+ bool imx;
+ bool shared_root_clock;
+ bool raw_capture_mode;
+ bool cchannel_192b;
+ u32 interrupts;
+ u32 tx_burst;
+ u32 rx_burst;
+ u64 tx_formats;
+};
+
/*
* SPDIF control structure
* Defines channel status, subcode and Q sub
@@ -72,10 +94,12 @@ struct spdif_mixer_control {
};
/**
- * fsl_spdif_priv: Freescale SPDIF private data
- *
+ * struct fsl_spdif_priv - Freescale SPDIF private data
+ * @soc: SPDIF soc data
* @fsl_spdif_control: SPDIF control data
* @cpu_dai_drv: cpu dai driver
+ * @snd_card: sound card pointer
+ * @rxrate_kcontrol: kcontrol for RX Sample Rate
* @pdev: platform device pointer
* @regmap: regmap handler
* @dpll_locked: dpll lock flag
@@ -91,19 +115,26 @@ struct spdif_mixer_control {
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @dma_params_tx: DMA parameters for transmit channel
* @dma_params_rx: DMA parameters for receive channel
+ * @regcache_srpc: regcache for SRPC
+ * @bypass: status of bypass input to output
+ * @pll8k_clk: PLL clock for the rate of multiply of 8kHz
+ * @pll11k_clk: PLL clock for the rate of multiply of 11kHz
*/
struct fsl_spdif_priv {
+ const struct fsl_spdif_soc_data *soc;
struct spdif_mixer_control fsl_spdif_control;
struct snd_soc_dai_driver cpu_dai_drv;
+ struct snd_card *snd_card;
+ struct snd_kcontrol *rxrate_kcontrol;
struct platform_device *pdev;
struct regmap *regmap;
bool dpll_locked;
u32 txrate[SPDIF_TXRATE_MAX];
u8 txclk_df[SPDIF_TXRATE_MAX];
- u8 sysclk_df[SPDIF_TXRATE_MAX];
+ u16 sysclk_df[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src;
- struct clk *txclk[SPDIF_TXRATE_MAX];
+ struct clk *txclk[STC_TXCLK_SRC_MAX];
struct clk *rxclk;
struct clk *coreclk;
struct clk *sysclk;
@@ -112,8 +143,79 @@ struct fsl_spdif_priv {
struct snd_dmaengine_dai_dma_data dma_params_rx;
/* regcache for SRPC */
u32 regcache_srpc;
+ bool bypass;
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
+};
+
+static const struct fsl_spdif_soc_data fsl_spdif_vf610 = {
+ .imx = false,
+ .shared_root_clock = false,
+ .raw_capture_mode = false,
+ .interrupts = 1,
+ .tx_burst = FSL_SPDIF_TXFIFO_WML,
+ .rx_burst = FSL_SPDIF_RXFIFO_WML,
+ .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
+};
+
+static const struct fsl_spdif_soc_data fsl_spdif_imx35 = {
+ .imx = true,
+ .shared_root_clock = false,
+ .raw_capture_mode = false,
+ .interrupts = 1,
+ .tx_burst = FSL_SPDIF_TXFIFO_WML,
+ .rx_burst = FSL_SPDIF_RXFIFO_WML,
+ .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
};
+static const struct fsl_spdif_soc_data fsl_spdif_imx6sx = {
+ .imx = true,
+ .shared_root_clock = true,
+ .raw_capture_mode = false,
+ .interrupts = 1,
+ .tx_burst = FSL_SPDIF_TXFIFO_WML,
+ .rx_burst = FSL_SPDIF_RXFIFO_WML,
+ .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
+
+};
+
+static const struct fsl_spdif_soc_data fsl_spdif_imx8qm = {
+ .imx = true,
+ .shared_root_clock = true,
+ .raw_capture_mode = false,
+ .interrupts = 2,
+ .tx_burst = 2, /* Applied for EDMA */
+ .rx_burst = 2, /* Applied for EDMA */
+ .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */
+};
+
+static const struct fsl_spdif_soc_data fsl_spdif_imx8mm = {
+ .imx = true,
+ .shared_root_clock = false,
+ .raw_capture_mode = true,
+ .interrupts = 1,
+ .tx_burst = FSL_SPDIF_TXFIFO_WML,
+ .rx_burst = FSL_SPDIF_RXFIFO_WML,
+ .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
+};
+
+static const struct fsl_spdif_soc_data fsl_spdif_imx8ulp = {
+ .imx = true,
+ .shared_root_clock = true,
+ .raw_capture_mode = false,
+ .interrupts = 1,
+ .tx_burst = 2, /* Applied for EDMA */
+ .rx_burst = 2, /* Applied for EDMA */
+ .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */
+ .cchannel_192b = true,
+};
+
+/* Check if clk is a root clock that does not share clock source with others */
+static inline bool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk)
+{
+ return (clk == STC_TXCLK_SPDIF_ROOT) && !spdif->soc->shared_root_clock;
+}
+
/* DPLL locked and lock loss interrupt handler */
static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
{
@@ -128,6 +230,12 @@ static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
locked ? "locked" : "loss lock");
spdif_priv->dpll_locked = locked ? true : false;
+
+ if (spdif_priv->snd_card && spdif_priv->rxrate_kcontrol) {
+ snd_ctl_notify(spdif_priv->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &spdif_priv->rxrate_kcontrol->id);
+ }
}
/* Receiver found illegal symbol interrupt handler */
@@ -172,7 +280,7 @@ static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
if (*pos >= size * 2) {
*pos = 0;
} else if (unlikely((*pos % size) + 3 > size)) {
- dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+ dev_err(&pdev->dev, "User bit receive buffer overflow\n");
return;
}
@@ -351,6 +459,23 @@ static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+
+ if (spdif_priv->soc->cchannel_192b) {
+ ch_status = (bitrev8(ctrl->ch_status[0]) << 24) |
+ (bitrev8(ctrl->ch_status[1]) << 16) |
+ (bitrev8(ctrl->ch_status[2]) << 8) |
+ bitrev8(ctrl->ch_status[3]);
+
+ regmap_update_bits(regmap, REG_SPDIF_SCR, 0x1000000, 0x1000000);
+
+ /*
+ * The first 32bit should be in REG_SPDIF_STCCA_31_0 register,
+ * but here we need to set REG_SPDIF_STCCA_191_160 on 8ULP
+ * then can get correct result with HDMI analyzer capture.
+ * There is a hardware bug here.
+ */
+ regmap_write(regmap, REG_SPDIF_STCCA_191_160, ch_status);
+ }
}
/* Set SPDIF PhaseConfig register for rx clock */
@@ -370,20 +495,27 @@ static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
return 0;
}
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index);
+
static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
int sample_rate)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
unsigned long csfs = 0;
u32 stc, mask, rate;
- u8 clk, txclk_df, sysclk_df;
+ u16 sysclk_df;
+ u8 clk, txclk_df;
int ret;
switch (sample_rate) {
+ case 22050:
+ rate = SPDIF_TXRATE_22050;
+ csfs = IEC958_AES3_CON_FS_22050;
+ break;
case 32000:
rate = SPDIF_TXRATE_32000;
csfs = IEC958_AES3_CON_FS_32000;
@@ -396,10 +528,18 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
rate = SPDIF_TXRATE_48000;
csfs = IEC958_AES3_CON_FS_48000;
break;
+ case 88200:
+ rate = SPDIF_TXRATE_88200;
+ csfs = IEC958_AES3_CON_FS_88200;
+ break;
case 96000:
rate = SPDIF_TXRATE_96000;
csfs = IEC958_AES3_CON_FS_96000;
break;
+ case 176400:
+ rate = SPDIF_TXRATE_176400;
+ csfs = IEC958_AES3_CON_FS_176400;
+ break;
case 192000:
rate = SPDIF_TXRATE_192000;
csfs = IEC958_AES3_CON_FS_192000;
@@ -409,6 +549,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ ret = fsl_spdif_probe_txclk(spdif_priv, rate);
+ if (ret)
+ return ret;
+
clk = spdif_priv->txclk_src[rate];
if (clk >= STC_TXCLK_SRC_MAX) {
dev_err(&pdev->dev, "tx clock source is out of range\n");
@@ -423,12 +567,11 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
sysclk_df = spdif_priv->sysclk_df[rate];
- /* Don't mess up the clocks from other modules */
- if (clk != STC_TXCLK_SPDIF_ROOT)
+ if (!fsl_spdif_can_set_clk_rate(spdif_priv, clk))
goto clk_set_bypass;
/* The S/PDIF block needs a clock of 64 * fs * txclk_df */
- ret = clk_set_rate(spdif_priv->txclk[rate],
+ ret = clk_set_rate(spdif_priv->txclk[clk],
64 * sample_rate * txclk_df);
if (ret) {
dev_err(&pdev->dev, "failed to set tx clock rate\n");
@@ -439,7 +582,7 @@ clk_set_bypass:
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
(64 * sample_rate * txclk_df * sysclk_df));
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
- clk_get_rate(spdif_priv->txclk[rate]));
+ clk_get_rate(spdif_priv->txclk[clk]));
/* set fs field in consumer channel status */
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
@@ -460,34 +603,19 @@ clk_set_bypass:
static int fsl_spdif_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct platform_device *pdev = spdif_priv->pdev;
struct regmap *regmap = spdif_priv->regmap;
u32 scr, mask;
- int i;
int ret;
/* Reset module and interrupts only for first initialization */
- if (!cpu_dai->active) {
- ret = clk_prepare_enable(spdif_priv->coreclk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable core clock\n");
- return ret;
- }
-
- if (!IS_ERR(spdif_priv->spbaclk)) {
- ret = clk_prepare_enable(spdif_priv->spbaclk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable spba clock\n");
- goto err_spbaclk;
- }
- }
-
+ if (!snd_soc_dai_active(cpu_dai)) {
ret = spdif_softreset(spdif_priv);
if (ret) {
dev_err(&pdev->dev, "failed to soft reset\n");
- goto err;
+ return ret;
}
/* Disable all the interrupts */
@@ -501,18 +629,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
SCR_TXFIFO_FSEL_MASK;
- for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
- ret = clk_prepare_enable(spdif_priv->txclk[i]);
- if (ret)
- goto disable_txclk;
- }
} else {
scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
- ret = clk_prepare_enable(spdif_priv->rxclk);
- if (ret)
- goto err;
}
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
@@ -520,65 +640,80 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
return 0;
-
-disable_txclk:
- for (i--; i >= 0; i--)
- clk_disable_unprepare(spdif_priv->txclk[i]);
-err:
- if (!IS_ERR(spdif_priv->spbaclk))
- clk_disable_unprepare(spdif_priv->spbaclk);
-err_spbaclk:
- clk_disable_unprepare(spdif_priv->coreclk);
-
- return ret;
}
static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct regmap *regmap = spdif_priv->regmap;
- u32 scr, mask, i;
+ u32 scr, mask;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
scr = 0;
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
SCR_TXFIFO_FSEL_MASK;
- for (i = 0; i < SPDIF_TXRATE_MAX; i++)
- clk_disable_unprepare(spdif_priv->txclk[i]);
+ /* Disable TX clock */
+ regmap_update_bits(regmap, REG_SPDIF_STC, STC_TXCLK_ALL_EN_MASK, 0);
} else {
scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
- clk_disable_unprepare(spdif_priv->rxclk);
}
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
/* Power down SPDIF module only if tx&rx are both inactive */
- if (!cpu_dai->active) {
+ if (!snd_soc_dai_active(cpu_dai)) {
spdif_intr_status_clear(spdif_priv);
regmap_update_bits(regmap, REG_SPDIF_SCR,
SCR_LOW_POWER, SCR_LOW_POWER);
- if (!IS_ERR(spdif_priv->spbaclk))
- clk_disable_unprepare(spdif_priv->spbaclk);
- clk_disable_unprepare(spdif_priv->coreclk);
}
}
+static int spdif_reparent_rootclk(struct fsl_spdif_priv *spdif_priv, unsigned int sample_rate)
+{
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct clk *clk;
+ int ret;
+
+ /* Reparent clock if required condition is true */
+ if (!fsl_spdif_can_set_clk_rate(spdif_priv, STC_TXCLK_SPDIF_ROOT))
+ return 0;
+
+ /* Get root clock */
+ clk = spdif_priv->txclk[STC_TXCLK_SPDIF_ROOT];
+
+ /* Disable clock first, for it was enabled by pm_runtime */
+ clk_disable_unprepare(clk);
+ fsl_asoc_reparent_pll_clocks(&pdev->dev, clk, spdif_priv->pll8k_clk,
+ spdif_priv->pll11k_clk, sample_rate);
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
struct platform_device *pdev = spdif_priv->pdev;
u32 sample_rate = params_rate(params);
int ret = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = spdif_reparent_rootclk(spdif_priv, sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: reparent root clk failed: %d\n",
+ __func__, sample_rate);
+ return ret;
+ }
+
ret = spdif_set_sample_rate(substream, sample_rate);
if (ret) {
dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
@@ -599,8 +734,8 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct regmap *regmap = spdif_priv->regmap;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u32 intr = SIE_INTR_FOR(tx);
@@ -618,6 +753,8 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+ regmap_write(regmap, REG_SPDIF_STL, 0x0);
+ regmap_write(regmap, REG_SPDIF_STR, 0x0);
break;
default:
return -EINVAL;
@@ -626,14 +763,6 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_dai_ops fsl_spdif_dai_ops = {
- .startup = fsl_spdif_startup,
- .hw_params = fsl_spdif_hw_params,
- .trigger = fsl_spdif_trigger,
- .shutdown = fsl_spdif_shutdown,
-};
-
-
/*
* FSL SPDIF IEC958 controller(mixer) functions
*
@@ -771,20 +900,52 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
return ret;
}
-/* Valid bit information */
-static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_rx_vbit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+ regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
return 0;
}
-/* Get valid good bit from interrupt status register */
-static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+static int fsl_spdif_tx_vbit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ regmap_read(regmap, REG_SPDIF_SCR, &val);
+ val = (val & SCR_VAL_MASK) >> SCR_VAL_OFFSET;
+ val = 1 - val;
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int fsl_spdif_tx_vbit_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val = (1 - ucontrol->value.integer.value[0]) << SCR_VAL_OFFSET;
+
+ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, val);
+
+ return 0;
+}
+
+static int fsl_spdif_rx_rcm_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
@@ -792,9 +953,90 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
struct regmap *regmap = spdif_priv->regmap;
u32 val;
- regmap_read(regmap, REG_SPDIF_SIS, &val);
- ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
- regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+ regmap_read(regmap, REG_SPDIF_SCR, &val);
+ val = (val & SCR_RAW_CAPTURE_MODE) ? 1 : 0;
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val = (ucontrol->value.integer.value[0] ? SCR_RAW_CAPTURE_MODE : 0);
+
+ if (val)
+ cpu_dai->driver->capture.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ else
+ cpu_dai->driver->capture.formats &= ~SNDRV_PCM_FMTBIT_S32_LE;
+
+ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_RAW_CAPTURE_MODE, val);
+
+ return 0;
+}
+
+static int fsl_spdif_bypass_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ ucontrol->value.integer.value[0] = priv->bypass ? 1 : 0;
+
+ return 0;
+}
+
+static int fsl_spdif_bypass_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_card *card = dai->component->card;
+ bool set = (ucontrol->value.integer.value[0] != 0);
+ struct regmap *regmap = priv->regmap;
+ struct snd_soc_pcm_runtime *rtd;
+ u32 scr, mask;
+ int stream;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link);
+
+ if (priv->bypass == set)
+ return 0; /* nothing to do */
+
+ if (snd_soc_dai_active(dai)) {
+ dev_err(dai->dev, "Cannot change BYPASS mode while stream is running.\n");
+ return -EBUSY;
+ }
+
+ pm_runtime_get_sync(dai->dev);
+
+ if (set) {
+ /* Disable interrupts */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+
+ /* Configure BYPASS mode */
+ scr = SCR_TXSEL_RX | SCR_RXFIFO_OFF;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK |
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK | SCR_TXSEL_MASK;
+ /* Power up SPDIF module */
+ mask |= SCR_LOW_POWER;
+ } else {
+ /* Power down SPDIF module, disable TX */
+ scr = SCR_LOW_POWER | SCR_TXSEL_OFF;
+ mask = SCR_LOW_POWER | SCR_TXSEL_MASK;
+ }
+
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Disable playback & capture if BYPASS mode is enabled, enable otherwise */
+ for_each_pcm_streams(stream)
+ rtd->pcm->streams[stream].substream_count = (set ? 0 : 1);
+
+ priv->bypass = set;
+ pm_runtime_put_sync(dai->dev);
return 0;
}
@@ -806,7 +1048,7 @@ static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 16000;
- uinfo->value.integer.max = 96000;
+ uinfo->value.integer.max = 192000;
return 0;
}
@@ -866,18 +1108,6 @@ static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
return 0;
}
-/* User bit sync mode info */
-static int fsl_spdif_usync_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;
-}
-
/*
* User bit sync mode:
* 1 CD User channel subcode
@@ -916,7 +1146,7 @@ static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
}
/* FSL SPDIF IEC958 controller defines */
-static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+static const struct snd_kcontrol_new fsl_spdif_ctrls[] = {
/* Status cchanel controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -956,21 +1186,40 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
/* Valid bit error controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 V-Bit Errors",
+ .name = "IEC958 RX V-Bit Errors",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
- .info = fsl_spdif_vbit_info,
- .get = fsl_spdif_vbit_get,
+ .info = snd_ctl_boolean_mono_info,
+ .get = fsl_spdif_rx_vbit_get,
},
- /* DPLL lock info get controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "RX Sample Rate",
+ .name = "IEC958 TX V-Bit",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = fsl_spdif_tx_vbit_get,
+ .put = fsl_spdif_tx_vbit_put,
+ },
+ /* DPLL lock info get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = RX_SAMPLE_RATE_KCONTROL,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = fsl_spdif_rxrate_info,
.get = fsl_spdif_rxrate_get,
},
+ /* RX bypass controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Bypass Mode",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = fsl_spdif_bypass_get,
+ .put = fsl_spdif_bypass_put,
+ },
/* User bit sync mode set/get controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -978,12 +1227,25 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_WRITE |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
- .info = fsl_spdif_usync_info,
+ .info = snd_ctl_boolean_mono_info,
.get = fsl_spdif_usync_get,
.put = fsl_spdif_usync_put,
},
};
+static const struct snd_kcontrol_new fsl_spdif_ctrls_rcm[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Raw Capture Mode",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = fsl_spdif_rx_rcm_get,
+ .put = fsl_spdif_rx_rcm_put,
+ },
+};
+
static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
@@ -993,11 +1255,33 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+ if (spdif_private->soc->raw_capture_mode)
+ snd_soc_add_dai_controls(dai, fsl_spdif_ctrls_rcm,
+ ARRAY_SIZE(fsl_spdif_ctrls_rcm));
+
+ spdif_private->snd_card = dai->component->card->snd_card;
+ spdif_private->rxrate_kcontrol = snd_soc_card_get_kcontrol(dai->component->card,
+ RX_SAMPLE_RATE_KCONTROL);
+ if (!spdif_private->rxrate_kcontrol)
+ dev_err(&spdif_private->pdev->dev, "failed to get %s kcontrol\n",
+ RX_SAMPLE_RATE_KCONTROL);
+
+ /*Clear the val bit for Tx*/
+ regmap_update_bits(spdif_private->regmap, REG_SPDIF_SCR,
+ SCR_VAL_MASK, SCR_VAL_CLEAR);
+
return 0;
}
+static const struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+ .probe = fsl_spdif_dai_probe,
+ .startup = fsl_spdif_startup,
+ .hw_params = fsl_spdif_hw_params,
+ .trigger = fsl_spdif_trigger,
+ .shutdown = fsl_spdif_shutdown,
+};
+
static struct snd_soc_dai_driver fsl_spdif_dai = {
- .probe = &fsl_spdif_dai_probe,
.playback = {
.stream_name = "CPU-Playback",
.channels_min = 2,
@@ -1016,7 +1300,8 @@ static struct snd_soc_dai_driver fsl_spdif_dai = {
};
static const struct snd_soc_component_driver fsl_spdif_component = {
- .name = "fsl-spdif",
+ .name = "fsl-spdif",
+ .legacy_dai_naming = 1,
};
/* FSL SPDIF REGMAP */
@@ -1028,6 +1313,8 @@ static const struct reg_default fsl_spdif_reg_defaults[] = {
{REG_SPDIF_STR, 0x00000000},
{REG_SPDIF_STCSCH, 0x00000000},
{REG_SPDIF_STCSCL, 0x00000000},
+ {REG_SPDIF_STCSPH, 0x00000000},
+ {REG_SPDIF_STCSPL, 0x00000000},
{REG_SPDIF_STC, 0x00020f00},
};
@@ -1047,8 +1334,22 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
case REG_SPDIF_SRQ:
case REG_SPDIF_STCSCH:
case REG_SPDIF_STCSCL:
+ case REG_SPDIF_STCSPH:
+ case REG_SPDIF_STCSPL:
case REG_SPDIF_SRFM:
case REG_SPDIF_STC:
+ case REG_SPDIF_SRCCA_31_0:
+ case REG_SPDIF_SRCCA_63_32:
+ case REG_SPDIF_SRCCA_95_64:
+ case REG_SPDIF_SRCCA_127_96:
+ case REG_SPDIF_SRCCA_159_128:
+ case REG_SPDIF_SRCCA_191_160:
+ case REG_SPDIF_STCCA_31_0:
+ case REG_SPDIF_STCCA_63_32:
+ case REG_SPDIF_STCCA_95_64:
+ case REG_SPDIF_STCCA_127_96:
+ case REG_SPDIF_STCCA_159_128:
+ case REG_SPDIF_STCCA_191_160:
return true;
default:
return false;
@@ -1067,6 +1368,12 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
case REG_SPDIF_SRU:
case REG_SPDIF_SRQ:
case REG_SPDIF_SRFM:
+ case REG_SPDIF_SRCCA_31_0:
+ case REG_SPDIF_SRCCA_63_32:
+ case REG_SPDIF_SRCCA_95_64:
+ case REG_SPDIF_SRCCA_127_96:
+ case REG_SPDIF_SRCCA_159_128:
+ case REG_SPDIF_SRCCA_191_160:
return true;
default:
return false;
@@ -1085,7 +1392,15 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
case REG_SPDIF_STR:
case REG_SPDIF_STCSCH:
case REG_SPDIF_STCSCL:
+ case REG_SPDIF_STCSPH:
+ case REG_SPDIF_STCSPL:
case REG_SPDIF_STC:
+ case REG_SPDIF_STCCA_31_0:
+ case REG_SPDIF_STCCA_63_32:
+ case REG_SPDIF_STCCA_95_64:
+ case REG_SPDIF_STCCA_127_96:
+ case REG_SPDIF_STCCA_159_128:
+ case REG_SPDIF_STCCA_191_160:
return true;
default:
return false;
@@ -1097,24 +1412,26 @@ static const struct regmap_config fsl_spdif_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
- .max_register = REG_SPDIF_STC,
+ .max_register = REG_SPDIF_STCCA_191_160,
.reg_defaults = fsl_spdif_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults),
.readable_reg = fsl_spdif_readable_reg,
.volatile_reg = fsl_spdif_volatile_reg,
.writeable_reg = fsl_spdif_writeable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_FLAT,
};
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
struct clk *clk, u64 savesub,
enum spdif_txrate index, bool round)
{
- const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
+ static const u32 rate[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400,
+ 192000, };
bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
u64 rate_ideal, rate_actual, sub;
- u32 sysclk_dfmin, sysclk_dfmax;
- u32 txclk_df, sysclk_df, arate;
+ u32 arate;
+ u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
+ u8 txclk_df;
/* The sysclk has an extra divisor [2, 512] */
sysclk_dfmin = is_sysclk ? 2 : 1;
@@ -1122,7 +1439,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
- rate_ideal = rate[index] * txclk_df * 64;
+ rate_ideal = rate[index] * txclk_df * 64ULL;
if (round)
rate_actual = clk_round_rate(clk, rate_ideal);
else
@@ -1169,17 +1486,16 @@ out:
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
enum spdif_txrate index)
{
- const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
+ static const u32 rate[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400,
+ 192000, };
struct platform_device *pdev = spdif_priv->pdev;
struct device *dev = &pdev->dev;
u64 savesub = 100000, ret;
struct clk *clk;
- char tmp[16];
int i;
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
- sprintf(tmp, "rxtx%d", i);
- clk = devm_clk_get(&pdev->dev, tmp);
+ clk = spdif_priv->txclk[i];
if (IS_ERR(clk)) {
dev_err(dev, "no rxtx%d clock in devicetree\n", i);
return PTR_ERR(clk);
@@ -1188,12 +1504,11 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
continue;
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
- i == STC_TXCLK_SPDIF_ROOT);
+ fsl_spdif_can_set_clk_rate(spdif_priv, i));
if (savesub == ret)
continue;
savesub = ret;
- spdif_priv->txclk[index] = clk;
spdif_priv->txclk_src[index] = i;
/* To quick catch a divisor, we allow a 0.1% deviation */
@@ -1201,14 +1516,14 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
break;
}
- dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
+ dev_dbg(dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
spdif_priv->txclk_src[index], rate[index]);
- dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
+ dev_dbg(dev, "use txclk df %d for %dHz sample rate\n",
spdif_priv->txclk_df[index], rate[index]);
- if (clk_is_match(spdif_priv->txclk[index], spdif_priv->sysclk))
- dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
+ if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[index]], spdif_priv->sysclk))
+ dev_dbg(dev, "use sysclk df %d for %dHz sample rate\n",
spdif_priv->sysclk_df[index], rate[index]);
- dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
+ dev_dbg(dev, "the best rate for %dHz sample rate is %dHz\n",
rate[index], spdif_priv->txrate[index]);
return 0;
@@ -1216,15 +1531,12 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
static int fsl_spdif_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct fsl_spdif_priv *spdif_priv;
struct spdif_mixer_control *ctrl;
struct resource *res;
void __iomem *regs;
int irq, ret, i;
-
- if (!np)
- return -ENODEV;
+ char tmp[16];
spdif_priv = devm_kzalloc(&pdev->dev, sizeof(*spdif_priv), GFP_KERNEL);
if (!spdif_priv)
@@ -1232,38 +1544,49 @@ static int fsl_spdif_probe(struct platform_device *pdev)
spdif_priv->pdev = pdev;
+ spdif_priv->soc = of_device_get_match_data(&pdev->dev);
+
/* Initialize this copy of the CPU DAI driver structure */
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev);
+ spdif_priv->cpu_dai_drv.playback.formats =
+ spdif_priv->soc->tx_formats;
/* Get the addresses and IRQ */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
- spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
- "core", regs, &fsl_spdif_regmap_config);
+ spdif_priv->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_spdif_regmap_config);
if (IS_ERR(spdif_priv->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(spdif_priv->regmap);
}
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
- return irq;
+ for (i = 0; i < spdif_priv->soc->interrupts; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
+ dev_name(&pdev->dev), spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "could not claim irq %u\n", irq);
+ return ret;
+ }
}
- ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
- dev_name(&pdev->dev), spdif_priv);
- if (ret) {
- dev_err(&pdev->dev, "could not claim irq %u\n", irq);
- return ret;
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+ sprintf(tmp, "rxtx%d", i);
+ spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(spdif_priv->txclk[i])) {
+ dev_err(&pdev->dev, "no rxtx%d clock in devicetree\n", i);
+ return PTR_ERR(spdif_priv->txclk[i]);
+ }
}
/* Get system clock for rx clock rate calculation */
- spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
+ spdif_priv->sysclk = spdif_priv->txclk[5];
if (IS_ERR(spdif_priv->sysclk)) {
dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
return PTR_ERR(spdif_priv->sysclk);
@@ -1281,18 +1604,15 @@ static int fsl_spdif_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
/* Select clock source for rx/tx clock */
- spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
+ spdif_priv->rxclk = spdif_priv->txclk[1];
if (IS_ERR(spdif_priv->rxclk)) {
dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n");
return PTR_ERR(spdif_priv->rxclk);
}
spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
- for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
- ret = fsl_spdif_probe_txclk(spdif_priv, i);
- if (ret)
- return ret;
- }
+ fsl_asoc_get_pll_clocks(&pdev->dev, &spdif_priv->pll8k_clk,
+ &spdif_priv->pll11k_clk);
/* Initial spinlock for control data */
ctrl = &spdif_priv->fsl_spdif_control;
@@ -1308,63 +1628,130 @@ static int fsl_spdif_probe(struct platform_device *pdev)
spdif_priv->dpll_locked = false;
- spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
- spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+ spdif_priv->dma_params_tx.maxburst = spdif_priv->soc->tx_burst;
+ spdif_priv->dma_params_rx.maxburst = spdif_priv->soc->rx_burst;
spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
/* Register with ASoC */
dev_set_drvdata(&pdev->dev, spdif_priv);
+ pm_runtime_enable(&pdev->dev);
+ regcache_cache_only(spdif_priv->regmap, true);
+
+ /*
+ * Register platform component before registering cpu dai for there
+ * is not defer probe for platform component in snd_soc_add_pcm_runtime().
+ */
+ ret = imx_pcm_dma_init(pdev);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "imx_pcm_dma_init failed\n");
+ goto err_pm_disable;
+ }
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
&spdif_priv->cpu_dai_drv, 1);
if (ret) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
- return ret;
+ goto err_pm_disable;
}
- ret = imx_pcm_dma_init(pdev, IMX_SPDIF_DMABUF_SIZE);
- if (ret)
- dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+ return ret;
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
return ret;
}
-#ifdef CONFIG_PM_SLEEP
-static int fsl_spdif_suspend(struct device *dev)
+static void fsl_spdif_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int fsl_spdif_runtime_suspend(struct device *dev)
{
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+ int i;
+
+ /* Disable all the interrupts */
+ regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SIE, 0xffffff, 0);
regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
&spdif_priv->regcache_srpc);
-
regcache_cache_only(spdif_priv->regmap, true);
- regcache_mark_dirty(spdif_priv->regmap);
+
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++)
+ clk_disable_unprepare(spdif_priv->txclk[i]);
+
+ if (!IS_ERR(spdif_priv->spbaclk))
+ clk_disable_unprepare(spdif_priv->spbaclk);
+ clk_disable_unprepare(spdif_priv->coreclk);
return 0;
}
-static int fsl_spdif_resume(struct device *dev)
+static int fsl_spdif_runtime_resume(struct device *dev)
{
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+ int ret;
+ int i;
+
+ ret = clk_prepare_enable(spdif_priv->coreclk);
+ if (ret) {
+ dev_err(dev, "failed to enable core clock\n");
+ return ret;
+ }
+
+ if (!IS_ERR(spdif_priv->spbaclk)) {
+ ret = clk_prepare_enable(spdif_priv->spbaclk);
+ if (ret) {
+ dev_err(dev, "failed to enable spba clock\n");
+ goto disable_core_clk;
+ }
+ }
+
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+ ret = clk_prepare_enable(spdif_priv->txclk[i]);
+ if (ret)
+ goto disable_tx_clk;
+ }
regcache_cache_only(spdif_priv->regmap, false);
+ regcache_mark_dirty(spdif_priv->regmap);
regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
spdif_priv->regcache_srpc);
- return regcache_sync(spdif_priv->regmap);
+ ret = regcache_sync(spdif_priv->regmap);
+ if (ret)
+ goto disable_tx_clk;
+
+ return 0;
+
+disable_tx_clk:
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(spdif_priv->txclk[i]);
+ if (!IS_ERR(spdif_priv->spbaclk))
+ clk_disable_unprepare(spdif_priv->spbaclk);
+disable_core_clk:
+ clk_disable_unprepare(spdif_priv->coreclk);
+
+ return ret;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops fsl_spdif_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume,
+ NULL)
};
static const struct of_device_id fsl_spdif_dt_ids[] = {
- { .compatible = "fsl,imx35-spdif", },
- { .compatible = "fsl,vf610-spdif", },
+ { .compatible = "fsl,imx35-spdif", .data = &fsl_spdif_imx35, },
+ { .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, },
+ { .compatible = "fsl,imx6sx-spdif", .data = &fsl_spdif_imx6sx, },
+ { .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, },
+ { .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, },
+ { .compatible = "fsl,imx8ulp-spdif", .data = &fsl_spdif_imx8ulp, },
{}
};
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
@@ -1373,9 +1760,10 @@ static struct platform_driver fsl_spdif_driver = {
.driver = {
.name = "fsl-spdif-dai",
.of_match_table = fsl_spdif_dt_ids,
- .pm = &fsl_spdif_pm,
+ .pm = pm_ptr(&fsl_spdif_pm),
},
.probe = fsl_spdif_probe,
+ .remove = fsl_spdif_remove,
};
module_platform_driver(fsl_spdif_driver);
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
index 00bd3514c610..2bc1b10c17d4 100644
--- a/sound/soc/fsl/fsl_spdif.h
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
*
@@ -8,10 +9,6 @@
* Based on fsl_ssi.h
* Author: Timur Tabi <timur@freescale.com>
* Copyright 2007-2008 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.
*/
#ifndef _FSL_SPDIF_DAI_H
@@ -34,9 +31,23 @@
#define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */
#define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */
#define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_STCSPH 0x3C /* SPDIFTxCChannel_Prof_h Register */
+#define REG_SPDIF_STCSPL 0x40 /* SPDIFTxCChannel_Prof_l Register */
#define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */
#define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */
+#define REG_SPDIF_SRCCA_31_0 0x60 /* SPDIF receive C channel register, bits 31-0 */
+#define REG_SPDIF_SRCCA_63_32 0x64 /* SPDIF receive C channel register, bits 63-32 */
+#define REG_SPDIF_SRCCA_95_64 0x68 /* SPDIF receive C channel register, bits 95-64 */
+#define REG_SPDIF_SRCCA_127_96 0x6C /* SPDIF receive C channel register, bits 127-96 */
+#define REG_SPDIF_SRCCA_159_128 0x70 /* SPDIF receive C channel register, bits 159-128 */
+#define REG_SPDIF_SRCCA_191_160 0x74 /* SPDIF receive C channel register, bits 191-160 */
+#define REG_SPDIF_STCCA_31_0 0x78 /* SPDIF transmit C channel register, bits 31-0 */
+#define REG_SPDIF_STCCA_63_32 0x7C /* SPDIF transmit C channel register, bits 63-32 */
+#define REG_SPDIF_STCCA_95_64 0x80 /* SPDIF transmit C channel register, bits 95-64 */
+#define REG_SPDIF_STCCA_127_96 0x84 /* SPDIF transmit C channel register, bits 127-96 */
+#define REG_SPDIF_STCCA_159_128 0x88 /* SPDIF transmit C channel register, bits 159-128 */
+#define REG_SPDIF_STCCA_191_160 0x8C /* SPDIF transmit C channel register, bits 191-160 */
/* SPDIF Configuration register */
#define SCR_RXFIFO_CTL_OFFSET 23
@@ -66,6 +77,7 @@
#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_RAW_CAPTURE_MODE BIT(14)
#define SCR_LOW_POWER (1 << 13)
#define SCR_SOFT_RESET (1 << 12)
#define SCR_TXFIFO_CTRL_OFFSET 10
@@ -155,7 +167,7 @@ enum spdif_gainsel {
#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET)
#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
#define STC_TXCLK_DF_OFFSET 0
-#define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET)
+#define STC_TXCLK_DF_MASK (0x7f << STC_TXCLK_DF_OFFSET)
#define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK)
#define STC_TXCLK_SRC_MAX 8
@@ -163,10 +175,13 @@ enum spdif_gainsel {
/* SPDIF tx rate */
enum spdif_txrate {
- SPDIF_TXRATE_32000 = 0,
+ SPDIF_TXRATE_22050 = 0,
+ SPDIF_TXRATE_32000,
SPDIF_TXRATE_44100,
SPDIF_TXRATE_48000,
+ SPDIF_TXRATE_88200,
SPDIF_TXRATE_96000,
+ SPDIF_TXRATE_176400,
SPDIF_TXRATE_192000,
};
#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_192000 + 1)
@@ -177,18 +192,24 @@ enum spdif_txrate {
#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8)
-#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
+#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000)
#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_64000 | \
- SNDRV_PCM_RATE_96000)
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
#define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 40dfd8a36484..320108bebf30 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1,48 +1,46 @@
-/*
- * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
- *
- * Author: Timur Tabi <timur@freescale.com>
- *
- * Copyright 2007-2010 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.
- *
- *
- * Some notes why imx-pcm-fiq is used instead of DMA on some boards:
- *
- * The i.MX SSI core has some nasty limitations in AC97 mode. While most
- * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
- * one FIFO which combines all valid receive slots. We cannot even select
- * which slots we want to receive. The WM9712 with which this driver
- * was developed with always sends GPIO status data in slot 12 which
- * we receive in our (PCM-) data stream. The only chance we have is to
- * manually skip this data in the FIQ handler. With sampling rates different
- * from 48000Hz not every frame has valid receive data, so the ratio
- * between pcm data and GPIO status data changes. Our FIQ handler is not
- * able to handle this, hence this driver only works with 48000Hz sampling
- * rate.
- * Reading and writing AC97 registers is another challenge. The core
- * provides us status bits when the read register is updated with *another*
- * value. When we read the same register two times (and the register still
- * contains the same value) these status bits are not set. We work
- * around this by not polling these bits but only wait a fixed delay.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
+//
+// Author: Timur Tabi <timur@freescale.com>
+//
+// Copyright 2007-2010 Freescale Semiconductor, Inc.
+//
+// Some notes why imx-pcm-fiq is used instead of DMA on some boards:
+//
+// The i.MX SSI core has some nasty limitations in AC97 mode. While most
+// sane processor vendors have a FIFO per AC97 slot, the i.MX has only
+// one FIFO which combines all valid receive slots. We cannot even select
+// which slots we want to receive. The WM9712 with which this driver
+// was developed with always sends GPIO status data in slot 12 which
+// we receive in our (PCM-) data stream. The only chance we have is to
+// manually skip this data in the FIQ handler. With sampling rates different
+// from 48000Hz not every frame has valid receive data, so the ratio
+// between pcm data and GPIO status data changes. Our FIQ handler is not
+// able to handle this, hence this driver only works with 48000Hz sampling
+// rate.
+// Reading and writing AC97 registers is another challenge. The core
+// provides us status bits when the read register is updated with *another*
+// value. When we read the same register two times (and the register still
+// contains the same value) these status bits are not set. We work
+// around this by not polling these bits but only wait a fixed delay.
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
+#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/dma/imx-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -54,15 +52,9 @@
#include "fsl_ssi.h"
#include "imx-pcm.h"
-/**
- * FSLSSI_I2S_RATES: sample rates supported by the I2S
- *
- * This driver currently only supports the SSI running in I2S slave mode,
- * which means the codec determines the sample rate. Therefore, we tell
- * ALSA that we support all rates and let the codec driver decide what rates
- * are really supported.
- */
-#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
+/* Define RX and TX to index ssi->regvals array; Can be 0 or 1 only */
+#define RX 0
+#define TX 1
/**
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI
@@ -77,21 +69,45 @@
* samples will be written to STX properly.
*/
#ifdef __BIG_ENDIAN
-#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
+#define FSLSSI_I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S18_3BE | \
+ SNDRV_PCM_FMTBIT_S20_3BE | \
+ SNDRV_PCM_FMTBIT_S24_3BE | \
+ SNDRV_PCM_FMTBIT_S24_BE)
#else
-#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define FSLSSI_I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
#endif
-#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \
- CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \
- CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN)
-#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
- CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
- CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN)
+/*
+ * In AC97 mode, TXDIR bit is forced to 0 and TFDIR bit is forced to 1:
+ * - SSI inputs external bit clock and outputs frame sync clock -- CBM_CFS
+ * - Also have NB_NF to mark these two clocks will not be inverted
+ */
+#define FSLSSI_AC97_DAIFMT \
+ (SND_SOC_DAIFMT_AC97 | \
+ SND_SOC_DAIFMT_BC_FP | \
+ SND_SOC_DAIFMT_NB_NF)
+
+#define FSLSSI_SIER_DBG_RX_FLAGS \
+ (SSI_SIER_RFF0_EN | \
+ SSI_SIER_RLS_EN | \
+ SSI_SIER_RFS_EN | \
+ SSI_SIER_ROE0_EN | \
+ SSI_SIER_RFRC_EN)
+#define FSLSSI_SIER_DBG_TX_FLAGS \
+ (SSI_SIER_TFE0_EN | \
+ SSI_SIER_TLS_EN | \
+ SSI_SIER_TFS_EN | \
+ SSI_SIER_TUE0_EN | \
+ SSI_SIER_TFRC_EN)
enum fsl_ssi_type {
FSL_SSI_MCP8610,
@@ -100,37 +116,18 @@ enum fsl_ssi_type {
FSL_SSI_MX51,
};
-struct fsl_ssi_reg_val {
+struct fsl_ssi_regvals {
u32 sier;
u32 srcr;
u32 stcr;
u32 scr;
};
-struct fsl_ssi_rxtx_reg_val {
- struct fsl_ssi_reg_val rx;
- struct fsl_ssi_reg_val tx;
-};
-
-static const struct reg_default fsl_ssi_reg_defaults[] = {
- {CCSR_SSI_SCR, 0x00000000},
- {CCSR_SSI_SIER, 0x00003003},
- {CCSR_SSI_STCR, 0x00000200},
- {CCSR_SSI_SRCR, 0x00000200},
- {CCSR_SSI_STCCR, 0x00040000},
- {CCSR_SSI_SRCCR, 0x00040000},
- {CCSR_SSI_SACNT, 0x00000000},
- {CCSR_SSI_STMSK, 0x00000000},
- {CCSR_SSI_SRMSK, 0x00000000},
- {CCSR_SSI_SACCEN, 0x00000000},
- {CCSR_SSI_SACCDIS, 0x00000000},
-};
-
static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CCSR_SSI_SACCEN:
- case CCSR_SSI_SACCDIS:
+ case REG_SSI_SACCEN:
+ case REG_SSI_SACCDIS:
return false;
default:
return true;
@@ -140,17 +137,18 @@ static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CCSR_SSI_STX0:
- case CCSR_SSI_STX1:
- case CCSR_SSI_SRX0:
- case CCSR_SSI_SRX1:
- case CCSR_SSI_SISR:
- case CCSR_SSI_SFCSR:
- case CCSR_SSI_SACNT:
- case CCSR_SSI_SACADD:
- case CCSR_SSI_SACDAT:
- case CCSR_SSI_SATAG:
- case CCSR_SSI_SACCST:
+ case REG_SSI_STX0:
+ case REG_SSI_STX1:
+ case REG_SSI_SRX0:
+ case REG_SSI_SRX1:
+ case REG_SSI_SISR:
+ case REG_SSI_SFCSR:
+ case REG_SSI_SACNT:
+ case REG_SSI_SACADD:
+ case REG_SSI_SACDAT:
+ case REG_SSI_SATAG:
+ case REG_SSI_SACCST:
+ case REG_SSI_SOR:
return true;
default:
return false;
@@ -160,12 +158,12 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CCSR_SSI_SRX0:
- case CCSR_SSI_SRX1:
- case CCSR_SSI_SISR:
- case CCSR_SSI_SACADD:
- case CCSR_SSI_SACDAT:
- case CCSR_SSI_SATAG:
+ case REG_SSI_SRX0:
+ case REG_SSI_SRX1:
+ case REG_SSI_SISR:
+ case REG_SSI_SACADD:
+ case REG_SSI_SACDAT:
+ case REG_SSI_SATAG:
return true;
default:
return false;
@@ -175,9 +173,9 @@ static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg)
static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CCSR_SSI_SRX0:
- case CCSR_SSI_SRX1:
- case CCSR_SSI_SACCST:
+ case REG_SSI_SRX0:
+ case REG_SSI_SRX1:
+ case REG_SSI_SACCST:
return false;
default:
return true;
@@ -185,124 +183,144 @@ static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
}
static const struct regmap_config fsl_ssi_regconfig = {
- .max_register = CCSR_SSI_SACCDIS,
+ .max_register = REG_SSI_SACCDIS,
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
- .reg_defaults = fsl_ssi_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+ .num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1,
.readable_reg = fsl_ssi_readable_reg,
.volatile_reg = fsl_ssi_volatile_reg,
.precious_reg = fsl_ssi_precious_reg,
.writeable_reg = fsl_ssi_writeable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_FLAT,
};
struct fsl_ssi_soc_data {
bool imx;
+ bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */
bool offline_config;
u32 sisr_write_mask;
};
/**
- * fsl_ssi_private: per-SSI private data
- *
- * @reg: Pointer to the regmap registers
+ * struct fsl_ssi - per-SSI private data
+ * @regs: Pointer to the regmap registers
* @irq: IRQ of this SSI
* @cpu_dai_drv: CPU DAI driver for this device
- *
* @dai_fmt: DAI configuration this device is currently used with
- * @i2s_mode: i2s and network mode configuration of the device. Is used to
- * switch between normal and i2s/network mode
- * mode depending on the number of channels
+ * @streams: Mask of current active streams: BIT(TX) and BIT(RX)
+ * @i2s_net: I2S and Network mode configurations of SCR register
+ * (this is the initial settings based on the DAI format)
+ * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
* @use_dma: DMA is used or FIQ with stream filter
- * @use_dual_fifo: DMA with support for both FIFOs used
- * @fifo_deph: Depth of the SSI FIFOs
- * @rxtx_reg_val: Specific register settings for receive/transmit configuration
- *
- * @clk: SSI clock
- * @baudclk: SSI baud clock for master mode
+ * @use_dual_fifo: DMA with support for dual FIFO mode
+ * @use_dyna_fifo: DMA with support for multi FIFO script
+ * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
+ * @fifo_depth: Depth of the SSI FIFOs
+ * @slot_width: Width of each DAI slot
+ * @slots: Number of slots
+ * @regvals: Specific RX/TX register settings
+ * @clk: Clock source to access register
+ * @baudclk: Clock source to generate bit and frame-sync clocks
* @baudclk_streams: Active streams that are using baudclk
- * @bitclk_freq: bitclock frequency set by .set_dai_sysclk
- *
+ * @regcache_sfcsr: Cache sfcsr register value during suspend and resume
+ * @regcache_sacnt: Cache sacnt register value during suspend and resume
* @dma_params_tx: DMA transmit parameters
* @dma_params_rx: DMA receive parameters
* @ssi_phys: physical address of the SSI registers
- *
* @fiq_params: FIQ stream filtering parameters
- *
- * @pdev: Pointer to pdev used for deprecated fsl-ssi sound card
- *
+ * @card_pdev: Platform_device pointer to register a sound card for PowerPC or
+ * to register a CODEC platform device for AC97
+ * @card_name: Platform_device name to register a sound card for PowerPC or
+ * to register a CODEC platform device for AC97
+ * @card_idx: The index of SSI to register a sound card for PowerPC or
+ * to register a CODEC platform device for AC97
* @dbg_stats: Debugging statistics
- *
* @soc: SoC specific data
+ * @dev: Pointer to &pdev->dev
+ * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are
+ * @fifo_watermark or fewer words in TX fifo or
+ * @fifo_watermark or more empty words in RX fifo.
+ * @dma_maxburst: Max number of words to transfer in one go. So far,
+ * this is always the same as fifo_watermark.
+ * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
+ * @audio_config: configure for dma multi fifo script
*/
-struct fsl_ssi_private {
+struct fsl_ssi {
struct regmap *regs;
int irq;
struct snd_soc_dai_driver cpu_dai_drv;
unsigned int dai_fmt;
- u8 i2s_mode;
+ u8 streams;
+ u8 i2s_net;
+ bool synchronous;
bool use_dma;
bool use_dual_fifo;
+ bool use_dyna_fifo;
bool has_ipg_clk_name;
unsigned int fifo_depth;
- struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
+ unsigned int slot_width;
+ unsigned int slots;
+ struct fsl_ssi_regvals regvals[2];
struct clk *clk;
struct clk *baudclk;
unsigned int baudclk_streams;
- unsigned int bitclk_freq;
- /* regcache for volatile regs */
u32 regcache_sfcsr;
u32 regcache_sacnt;
- /* DMA params */
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
dma_addr_t ssi_phys;
- /* params for non-dma FIQ stream filtered mode */
struct imx_pcm_fiq_params fiq_params;
- /* Used when using fsl-ssi as sound-card. This is only used by ppc and
- * should be replaced with simple-sound-card. */
- struct platform_device *pdev;
+ struct platform_device *card_pdev;
+ char card_name[32];
+ u32 card_idx;
struct fsl_ssi_dbg dbg_stats;
const struct fsl_ssi_soc_data *soc;
+ struct device *dev;
+
+ u32 fifo_watermark;
+ u32 dma_maxburst;
+
+ struct mutex ac97_reg_lock;
+ struct sdma_peripheral_config audio_config[2];
};
/*
- * imx51 and later SoCs have a slightly different IP that allows the
- * SSI configuration while the SSI unit is running.
- *
- * More important, it is necessary on those SoCs to configure the
- * sperate TX/RX DMA bits just before starting the stream
- * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
- * sends any DMA requests to the SDMA unit, otherwise it is not defined
- * how the SDMA unit handles the DMA request.
+ * SoC specific data
*
- * SDMA units are present on devices starting at imx35 but the imx35
- * reference manual states that the DMA bits should not be changed
- * while the SSI unit is running (SSIEN). So we support the necessary
- * online configuration of fsl-ssi starting at imx51.
+ * Notes:
+ * 1) SSI in earlier SoCS has critical bits in control registers that
+ * cannot be changed after SSI starts running -- a software reset
+ * (set SSIEN to 0) is required to change their values. So adding
+ * an offline_config flag for these SoCs.
+ * 2) SDMA is available since imx35. However, imx35 does not support
+ * DMA bits changing when SSI is running, so set offline_config.
+ * 3) imx51 and later versions support register configurations when
+ * SSI is running (SSIEN); For these versions, DMA needs to be
+ * configured before SSI sends DMA request to avoid an undefined
+ * DMA request on the SDMA side.
*/
static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
.imx = false,
.offline_config = true,
- .sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
- CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
- CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1,
+ .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
+ SSI_SISR_ROE0 | SSI_SISR_ROE1 |
+ SSI_SISR_TUE0 | SSI_SISR_TUE1,
};
static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
.imx = true,
+ .imx21regs = true,
.offline_config = true,
.sisr_write_mask = 0,
};
@@ -310,16 +328,16 @@ static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
static struct fsl_ssi_soc_data fsl_ssi_imx35 = {
.imx = true,
.offline_config = true,
- .sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
- CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
- CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1,
+ .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
+ SSI_SISR_ROE0 | SSI_SISR_ROE1 |
+ SSI_SISR_TUE0 | SSI_SISR_TUE1,
};
static struct fsl_ssi_soc_data fsl_ssi_imx51 = {
.imx = true,
.offline_config = false,
- .sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
- CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1,
+ .sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 |
+ SSI_SISR_TUE0 | SSI_SISR_TUE1,
};
static const struct of_device_id fsl_ssi_ids[] = {
@@ -331,355 +349,378 @@ static const struct of_device_id fsl_ssi_ids[] = {
};
MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
-static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
+static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi)
{
- return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+ return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
SND_SOC_DAIFMT_AC97;
}
-static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
+static bool fsl_ssi_is_i2s_clock_provider(struct fsl_ssi *ssi)
{
- return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
- SND_SOC_DAIFMT_CBS_CFS;
+ return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_BP_FP;
}
-static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi_private *ssi_private)
+static bool fsl_ssi_is_i2s_bc_fp(struct fsl_ssi *ssi)
{
- return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
- SND_SOC_DAIFMT_CBM_CFS;
+ return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_BC_FP;
}
+
/**
- * fsl_ssi_isr: SSI interrupt handler
- *
- * Although it's possible to use the interrupt handler to send and receive
- * data to/from the SSI, we use the DMA instead. Programming is more
- * complicated, but the performance is much better.
- *
- * This interrupt handler is used only to gather statistics.
- *
- * @irq: IRQ of the SSI device
- * @dev_id: pointer to the ssi_private structure for this SSI device
+ * fsl_ssi_isr - Interrupt handler to gather states
+ * @irq: irq number
+ * @dev_id: context
*/
static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
{
- struct fsl_ssi_private *ssi_private = dev_id;
- struct regmap *regs = ssi_private->regs;
- __be32 sisr;
- __be32 sisr2;
-
- /* We got an interrupt, so read the status register to see what we
- were interrupted for. We mask it with the Interrupt Enable register
- so that we only check for events that we're interested in.
- */
- regmap_read(regs, CCSR_SSI_SISR, &sisr);
+ struct fsl_ssi *ssi = dev_id;
+ struct regmap *regs = ssi->regs;
+ u32 sisr, sisr2;
- sisr2 = sisr & ssi_private->soc->sisr_write_mask;
+ regmap_read(regs, REG_SSI_SISR, &sisr);
+
+ sisr2 = sisr & ssi->soc->sisr_write_mask;
/* Clear the bits that we set */
if (sisr2)
- regmap_write(regs, CCSR_SSI_SISR, sisr2);
+ regmap_write(regs, REG_SSI_SISR, sisr2);
- fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr);
+ fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr);
return IRQ_HANDLED;
}
-/*
- * Enable/Disable all rx/tx config flags at once.
+/**
+ * fsl_ssi_config_enable - Set SCR, SIER, STCR and SRCR registers with
+ * cached values in regvals
+ * @ssi: SSI context
+ * @tx: direction
+ *
+ * Notes:
+ * 1) For offline_config SoCs, enable all necessary bits of both streams
+ * when 1st stream starts, even if the opposite stream will not start
+ * 2) It also clears FIFO before setting regvals; SOR is safe to set online
*/
-static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
- bool enable)
+static void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx)
{
- struct regmap *regs = ssi_private->regs;
- struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val;
-
- if (enable) {
- regmap_update_bits(regs, CCSR_SSI_SIER,
- vals->rx.sier | vals->tx.sier,
- vals->rx.sier | vals->tx.sier);
- regmap_update_bits(regs, CCSR_SSI_SRCR,
- vals->rx.srcr | vals->tx.srcr,
- vals->rx.srcr | vals->tx.srcr);
- regmap_update_bits(regs, CCSR_SSI_STCR,
- vals->rx.stcr | vals->tx.stcr,
- vals->rx.stcr | vals->tx.stcr);
+ struct fsl_ssi_regvals *vals = ssi->regvals;
+ int dir = tx ? TX : RX;
+ u32 sier, srcr, stcr;
+
+ /* Clear dirty data in the FIFO; It also prevents channel slipping */
+ regmap_update_bits(ssi->regs, REG_SSI_SOR,
+ SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
+
+ /*
+ * On offline_config SoCs, SxCR and SIER are already configured when
+ * the previous stream started. So skip all SxCR and SIER settings
+ * to prevent online reconfigurations, then jump to set SCR directly
+ */
+ if (ssi->soc->offline_config && ssi->streams)
+ goto enable_scr;
+
+ if (ssi->soc->offline_config) {
+ /*
+ * Online reconfiguration not supported, so enable all bits for
+ * both streams at once to avoid necessity of reconfigurations
+ */
+ srcr = vals[RX].srcr | vals[TX].srcr;
+ stcr = vals[RX].stcr | vals[TX].stcr;
+ sier = vals[RX].sier | vals[TX].sier;
} else {
- regmap_update_bits(regs, CCSR_SSI_SRCR,
- vals->rx.srcr | vals->tx.srcr, 0);
- regmap_update_bits(regs, CCSR_SSI_STCR,
- vals->rx.stcr | vals->tx.stcr, 0);
- regmap_update_bits(regs, CCSR_SSI_SIER,
- vals->rx.sier | vals->tx.sier, 0);
+ /* Otherwise, only set bits for the current stream */
+ srcr = vals[dir].srcr;
+ stcr = vals[dir].stcr;
+ sier = vals[dir].sier;
+ }
+
+ /* Configure SRCR, STCR and SIER at once */
+ regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr);
+ regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
+ regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
+
+enable_scr:
+ /*
+ * Start DMA before setting TE to avoid FIFO underrun
+ * which may cause a channel slip or a channel swap
+ *
+ * TODO: FIQ cases might also need this upon testing
+ */
+ if (ssi->use_dma && tx) {
+ int try = 100;
+ u32 sfcsr;
+
+ /* Enable SSI first to send TX DMA request */
+ regmap_update_bits(ssi->regs, REG_SSI_SCR,
+ SSI_SCR_SSIEN, SSI_SCR_SSIEN);
+
+ /* Busy wait until TX FIFO not empty -- DMA working */
+ do {
+ regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr);
+ if (SSI_SFCSR_TFCNT0(sfcsr))
+ break;
+ } while (--try);
+
+ /* FIFO still empty -- something might be wrong */
+ if (!SSI_SFCSR_TFCNT0(sfcsr))
+ dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
}
+ /* Enable all remaining bits in SCR */
+ regmap_update_bits(ssi->regs, REG_SSI_SCR,
+ vals[dir].scr, vals[dir].scr);
+
+ /* Log the enabled stream to the mask */
+ ssi->streams |= BIT(dir);
}
/*
- * Calculate the bits that have to be disabled for the current stream that is
- * getting disabled. This keeps the bits enabled that are necessary for the
- * second stream to work if 'stream_active' is true.
+ * Exclude bits that are used by the opposite stream
*
- * Detailed calculation:
- * These are the values that need to be active after disabling. For non-active
- * second stream, this is 0:
- * vals_stream * !!stream_active
+ * When both streams are active, disabling some bits for the current stream
+ * might break the other stream if these bits are used by it.
*
- * The following computes the overall differences between the setup for the
- * to-disable stream and the active stream, a simple XOR:
- * vals_disable ^ (vals_stream * !!(stream_active))
+ * @vals : regvals of the current stream
+ * @avals: regvals of the opposite stream
+ * @aactive: active state of the opposite stream
*
- * The full expression adds a mask on all values we care about
+ * 1) XOR vals and avals to get the differences if the other stream is active;
+ * Otherwise, return current vals if the other stream is not active
+ * 2) AND the result of 1) with the current vals
*/
-#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \
- ((vals_disable) & \
- ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
+#define _ssi_xor_shared_bits(vals, avals, aactive) \
+ ((vals) ^ ((avals) * (aactive)))
-/*
- * Enable/Disable a ssi configuration. You have to pass either
- * ssi_private->rxtx_reg_val.rx or tx as vals parameter.
+#define ssi_excl_shared_bits(vals, avals, aactive) \
+ ((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
+
+/**
+ * fsl_ssi_config_disable - Unset SCR, SIER, STCR and SRCR registers
+ * with cached values in regvals
+ * @ssi: SSI context
+ * @tx: direction
+ *
+ * Notes:
+ * 1) For offline_config SoCs, to avoid online reconfigurations, disable all
+ * bits of both streams at once when the last stream is abort to end
+ * 2) It also clears FIFO after unsetting regvals; SOR is safe to set online
*/
-static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
- struct fsl_ssi_reg_val *vals)
+static void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx)
{
- struct regmap *regs = ssi_private->regs;
- struct fsl_ssi_reg_val *avals;
- int nr_active_streams;
- u32 scr_val;
- int keep_active;
+ struct fsl_ssi_regvals *vals, *avals;
+ u32 sier, srcr, stcr, scr;
+ int adir = tx ? RX : TX;
+ int dir = tx ? TX : RX;
+ bool aactive;
- regmap_read(regs, CCSR_SSI_SCR, &scr_val);
+ /* Check if the opposite stream is active */
+ aactive = ssi->streams & BIT(adir);
- nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
- !!(scr_val & CCSR_SSI_SCR_RE);
+ vals = &ssi->regvals[dir];
- if (nr_active_streams - 1 > 0)
- keep_active = 1;
- else
- keep_active = 0;
-
- /* Find the other direction values rx or tx which we do not want to
- * modify */
- if (&ssi_private->rxtx_reg_val.rx == vals)
- avals = &ssi_private->rxtx_reg_val.tx;
- else
- avals = &ssi_private->rxtx_reg_val.rx;
-
- /* If vals should be disabled, start with disabling the unit */
- if (!enable) {
- u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
- keep_active);
- regmap_update_bits(regs, CCSR_SSI_SCR, scr, 0);
- }
+ /* Get regvals of the opposite stream to keep opposite stream safe */
+ avals = &ssi->regvals[adir];
/*
- * We are running on a SoC which does not support online SSI
- * reconfiguration, so we have to enable all necessary flags at once
- * even if we do not use them later (capture and playback configuration)
+ * To keep the other stream safe, exclude shared bits between
+ * both streams, and get safe bits to disable current stream
*/
- if (ssi_private->soc->offline_config) {
- if ((enable && !nr_active_streams) ||
- (!enable && !keep_active))
- fsl_ssi_rxtx_config(ssi_private, enable);
+ scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
- goto config_done;
- }
+ /* Disable safe bits of SCR register for the current stream */
+ regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0);
+
+ /* Log the disabled stream to the mask */
+ ssi->streams &= ~BIT(dir);
/*
- * Configure single direction units while the SSI unit is running
- * (online configuration)
+ * On offline_config SoCs, if the other stream is active, skip
+ * SxCR and SIER settings to prevent online reconfigurations
*/
- if (enable) {
- regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
- regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
- regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
+ if (ssi->soc->offline_config && aactive)
+ goto fifo_clear;
+
+ if (ssi->soc->offline_config) {
+ /* Now there is only current stream active, disable all bits */
+ srcr = vals->srcr | avals->srcr;
+ stcr = vals->stcr | avals->stcr;
+ sier = vals->sier | avals->sier;
} else {
- u32 sier;
- u32 srcr;
- u32 stcr;
-
/*
- * Disabling the necessary flags for one of rx/tx while the
- * other stream is active is a little bit more difficult. We
- * have to disable only those flags that differ between both
- * streams (rx XOR tx) and that are set in the stream that is
- * disabled now. Otherwise we could alter flags of the other
- * stream
+ * To keep the other stream safe, exclude shared bits between
+ * both streams, and get safe bits to disable current stream
*/
-
- /* These assignments are simply vals without bits set in avals*/
- sier = fsl_ssi_disable_val(vals->sier, avals->sier,
- keep_active);
- srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
- keep_active);
- stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
- keep_active);
-
- regmap_update_bits(regs, CCSR_SSI_SRCR, srcr, 0);
- regmap_update_bits(regs, CCSR_SSI_STCR, stcr, 0);
- regmap_update_bits(regs, CCSR_SSI_SIER, sier, 0);
+ sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive);
+ srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive);
+ stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive);
}
-config_done:
- /* Enabling of subunits is done after configuration */
- if (enable)
- regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
-}
-
+ /* Clear configurations of SRCR, STCR and SIER at once */
+ regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0);
+ regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0);
+ regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0);
-static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable)
-{
- fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx);
+fifo_clear:
+ /* Clear remaining data in the FIFO */
+ regmap_update_bits(ssi->regs, REG_SSI_SOR,
+ SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
}
-static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable)
+static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
{
- fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx);
+ struct regmap *regs = ssi->regs;
+
+ /* no SACC{ST,EN,DIS} regs on imx21-class SSI */
+ if (!ssi->soc->imx21regs) {
+ /* Disable all channel slots */
+ regmap_write(regs, REG_SSI_SACCDIS, 0xff);
+ /* Enable slots 3 & 4 -- PCM Playback Left & Right channels */
+ regmap_write(regs, REG_SSI_SACCEN, 0x300);
+ }
}
-/*
- * Setup rx/tx register values used to enable/disable the streams. These will
- * be used later in fsl_ssi_config to setup the streams without the need to
- * check for all different SSI modes.
+/**
+ * fsl_ssi_setup_regvals - Cache critical bits of SIER, SRCR, STCR and
+ * SCR to later set them safely
+ * @ssi: SSI context
*/
-static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
+static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi)
{
- struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val;
-
- reg->rx.sier = CCSR_SSI_SIER_RFF0_EN;
- reg->rx.srcr = CCSR_SSI_SRCR_RFEN0;
- reg->rx.scr = 0;
- reg->tx.sier = CCSR_SSI_SIER_TFE0_EN;
- reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
- reg->tx.scr = 0;
-
- if (!fsl_ssi_is_ac97(ssi_private)) {
- reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
- reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
- reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
- reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN;
+ struct fsl_ssi_regvals *vals = ssi->regvals;
+
+ vals[RX].sier = SSI_SIER_RFF0_EN | FSLSSI_SIER_DBG_RX_FLAGS;
+ vals[RX].srcr = SSI_SRCR_RFEN0;
+ vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE;
+ vals[TX].sier = SSI_SIER_TFE0_EN | FSLSSI_SIER_DBG_TX_FLAGS;
+ vals[TX].stcr = SSI_STCR_TFEN0;
+ vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
+
+ /* AC97 has already enabled SSIEN, RE and TE, so ignore them */
+ if (fsl_ssi_is_ac97(ssi))
+ vals[RX].scr = vals[TX].scr = 0;
+
+ if (ssi->use_dual_fifo) {
+ vals[RX].srcr |= SSI_SRCR_RFEN1;
+ vals[TX].stcr |= SSI_STCR_TFEN1;
}
- if (ssi_private->use_dma) {
- reg->rx.sier |= CCSR_SSI_SIER_RDMAE;
- reg->tx.sier |= CCSR_SSI_SIER_TDMAE;
+ if (ssi->use_dma) {
+ vals[RX].sier |= SSI_SIER_RDMAE;
+ vals[TX].sier |= SSI_SIER_TDMAE;
} else {
- reg->rx.sier |= CCSR_SSI_SIER_RIE;
- reg->tx.sier |= CCSR_SSI_SIER_TIE;
+ vals[RX].sier |= SSI_SIER_RIE;
+ vals[TX].sier |= SSI_SIER_TIE;
}
-
- reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS;
- reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS;
}
-static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
+static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
{
- struct regmap *regs = ssi_private->regs;
+ struct regmap *regs = ssi->regs;
- /*
- * Setup the clock control register
- */
- regmap_write(regs, CCSR_SSI_STCCR,
- CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
- regmap_write(regs, CCSR_SSI_SRCCR,
- CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
+ /* Setup the clock control register */
+ regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
+ regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
- /*
- * Enable AC97 mode and startup the SSI
- */
- regmap_write(regs, CCSR_SSI_SACNT,
- CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
- regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
- regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+ /* Enable AC97 mode and startup the SSI */
+ regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV);
- /*
- * Enable SSI, Transmit and Receive. AC97 has to communicate with the
- * codec before a stream is started.
- */
- regmap_update_bits(regs, CCSR_SSI_SCR,
- CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE,
- CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
+ /* AC97 has to communicate with codec before starting a stream */
+ regmap_update_bits(regs, REG_SSI_SCR,
+ SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE,
+ SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE);
- regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_WAIT(3));
+ regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3));
}
-/**
- * fsl_ssi_startup: create a new substream
- *
- * This is the first function called when a stream is opened.
- *
- * If this is the first stream open, then grab the IRQ and program most of
- * the SSI registers.
- */
static int fsl_ssi_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
int ret;
- ret = clk_prepare_enable(ssi_private->clk);
+ ret = clk_prepare_enable(ssi->clk);
if (ret)
return ret;
- /* When using dual fifo mode, it is safer to ensure an even period
+ /*
+ * When using dual fifo mode, it is safer to ensure an even period
* size. If appearing to an odd number while DMA always starts its
* task from fifo0, fifo1 would be neglected at the end of each
* period. But SSI would still access fifo1 with an invalid data.
*/
- if (ssi_private->use_dual_fifo)
+ if (ssi->use_dual_fifo || ssi->use_dyna_fifo)
snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
return 0;
}
-/**
- * fsl_ssi_shutdown: shutdown the SSI
- *
- */
static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+ struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
-
- clk_disable_unprepare(ssi_private->clk);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ clk_disable_unprepare(ssi->clk);
}
/**
- * fsl_ssi_set_bclk - configure Digital Audio Interface bit clock
+ * fsl_ssi_set_bclk - Configure Digital Audio Interface bit clock
+ * @substream: ASoC substream
+ * @dai: pointer to DAI
+ * @hw_params: pointers to hw_params
*
- * Note: This function can be only called when using SSI as DAI master
+ * Notes: This function can be only called when using SSI as DAI master
*
* Quick instruction for parameters:
- * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
- * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
+ * freq: Output BCLK frequency = samplerate * slots * slot_width
+ * (In 2-channel I2S Master mode, slot_width is fixed 32)
*/
static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai,
- struct snd_pcm_hw_params *hw_params)
+ struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *hw_params)
{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
- struct regmap *regs = ssi_private->regs;
- int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
+ bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
+ struct regmap *regs = ssi->regs;
u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
unsigned long clkrate, baudrate, tmprate;
+ unsigned int channels = params_channels(hw_params);
+ unsigned int slot_width = params_width(hw_params);
+ unsigned int slots = 2;
u64 sub, savesub = 100000;
unsigned int freq;
bool baudclk_is_used;
+ int ret;
- /* Prefer the explicitly set bitclock frequency */
- if (ssi_private->bitclk_freq)
- freq = ssi_private->bitclk_freq;
- else
- freq = params_channels(hw_params) * 32 * params_rate(hw_params);
+ /* Override slots and slot_width if being specifically set... */
+ if (ssi->slots)
+ slots = ssi->slots;
+ if (ssi->slot_width)
+ slot_width = ssi->slot_width;
+
+ /* ...but force 32 bits for stereo audio using I2S Master Mode */
+ if (channels == 2 &&
+ (ssi->i2s_net & SSI_SCR_I2S_MODE_MASK) == SSI_SCR_I2S_MODE_MASTER)
+ slot_width = 32;
+
+ /* Generate bit clock based on the slot number and slot width */
+ freq = slots * slot_width * params_rate(hw_params);
/* Don't apply it to any non-baudclk circumstance */
- if (IS_ERR(ssi_private->baudclk))
+ if (IS_ERR(ssi->baudclk))
return -EINVAL;
- baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
+ /*
+ * Hardware limitation: The bclk rate must be
+ * never greater than 1/5 IPG clock rate
+ */
+ if (freq * 5 > clk_get_rate(ssi->clk)) {
+ dev_err(dai->dev, "bitclk > ipgclk / 5\n");
+ return -EINVAL;
+ }
+
+ baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream));
/* It should be already enough to divide clock by setting pm alone */
psr = 0;
@@ -691,16 +732,9 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
tmprate = freq * factor * (i + 1);
if (baudclk_is_used)
- clkrate = clk_get_rate(ssi_private->baudclk);
+ clkrate = clk_get_rate(ssi->baudclk);
else
- clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
-
- /*
- * Hardware limitation: The bclk rate must be
- * never greater than 1/5 IPG clock rate
- */
- if (clkrate * 5 > clk_get_rate(ssi_private->clk))
- continue;
+ clkrate = clk_round_rate(ssi->baudclk, tmprate);
clkrate /= factor;
afreq = clkrate / (i + 1);
@@ -718,7 +752,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
sub *= 100000;
do_div(sub, freq);
- if (sub < savesub && !(i == 0 && psr == 0 && div2 == 0)) {
+ if (sub < savesub && !(i == 0)) {
baudrate = tmprate;
savesub = sub;
pm = i;
@@ -731,24 +765,21 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
/* No proper pm found if it is still remaining the initial value */
if (pm == 999) {
- dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
+ dev_err(dai->dev, "failed to handle the required sysclk\n");
return -EINVAL;
}
- stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
- (psr ? CCSR_SSI_SxCCR_PSR : 0);
- mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 |
- CCSR_SSI_SxCCR_PSR;
+ stccr = SSI_SxCCR_PM(pm + 1);
+ mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || synchronous)
- regmap_update_bits(regs, CCSR_SSI_STCCR, mask, stccr);
- else
- regmap_update_bits(regs, CCSR_SSI_SRCCR, mask, stccr);
+ /* STCCR is used for RX in synchronous mode */
+ tx2 = tx || ssi->synchronous;
+ regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
if (!baudclk_is_used) {
- ret = clk_set_rate(ssi_private->baudclk, baudrate);
+ ret = clk_set_rate(ssi->baudclk, baudrate);
if (ret) {
- dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
+ dev_err(dai->dev, "failed to set baudclk rate\n");
return -EINVAL;
}
}
@@ -756,196 +787,186 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
return 0;
}
-static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
-
- ssi_private->bitclk_freq = freq;
-
- return 0;
-}
-
/**
- * fsl_ssi_hw_params - program the sample size
+ * fsl_ssi_hw_params - Configure SSI based on PCM hardware parameters
+ * @substream: ASoC substream
+ * @hw_params: pointers to hw_params
+ * @dai: pointer to DAI
*
- * Most of the SSI registers have been programmed in the startup function,
- * but the word length must be programmed here. Unfortunately, programming
- * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
- * cause a problem with supporting simultaneous playback and capture. If
- * the SSI is already playing a stream, then that stream may be temporarily
- * stopped when you start capture.
- *
- * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
- * clock master.
+ * Notes:
+ * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily
+ * disabled on offline_config SoCs. Even for online configurable SoCs
+ * running in synchronous mode (both TX and RX use STCCR), it is not
+ * safe to re-configure them when both two streams start running.
+ * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the
+ * fsl_ssi_set_bclk() if SSI is the DAI clock master.
*/
static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
- struct regmap *regs = ssi_private->regs;
+ bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
+ struct fsl_ssi_regvals *vals = ssi->regvals;
+ struct regmap *regs = ssi->regs;
unsigned int channels = params_channels(hw_params);
unsigned int sample_size = params_width(hw_params);
- u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
+ u32 wl = SSI_SxCCR_WL(sample_size);
int ret;
- u32 scr_val;
- int enabled;
-
- regmap_read(regs, CCSR_SSI_SCR, &scr_val);
- enabled = scr_val & CCSR_SSI_SCR_SSIEN;
- /*
- * If we're in synchronous mode, and the SSI is already enabled,
- * then STCCR is already set properly.
- */
- if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
- return 0;
-
- if (fsl_ssi_is_i2s_master(ssi_private)) {
- ret = fsl_ssi_set_bclk(substream, cpu_dai, hw_params);
+ if (fsl_ssi_is_i2s_clock_provider(ssi)) {
+ ret = fsl_ssi_set_bclk(substream, dai, hw_params);
if (ret)
return ret;
/* Do not enable the clock if it is already enabled */
- if (!(ssi_private->baudclk_streams & BIT(substream->stream))) {
- ret = clk_prepare_enable(ssi_private->baudclk);
+ if (!(ssi->baudclk_streams & BIT(substream->stream))) {
+ ret = clk_prepare_enable(ssi->baudclk);
if (ret)
return ret;
- ssi_private->baudclk_streams |= BIT(substream->stream);
+ ssi->baudclk_streams |= BIT(substream->stream);
}
}
- if (!fsl_ssi_is_ac97(ssi_private)) {
- u8 i2smode;
+ /*
+ * SSI is properly configured if it is enabled and running in
+ * the synchronous mode; Note that AC97 mode is an exception
+ * that should set separate configurations for STCCR and SRCCR
+ * despite running in the synchronous mode.
+ */
+ if (ssi->streams && ssi->synchronous)
+ return 0;
+
+ if (!fsl_ssi_is_ac97(ssi)) {
/*
- * Switch to normal net mode in order to have a frame sync
- * signal every 32 bits instead of 16 bits
+ * Keep the ssi->i2s_net intact while having a local variable
+ * to override settings for special use cases. Otherwise, the
+ * ssi->i2s_net will lose the settings for regular use cases.
*/
- if (fsl_ssi_is_i2s_cbm_cfs(ssi_private) && sample_size == 16)
- i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL |
- CCSR_SSI_SCR_NET;
- else
- i2smode = ssi_private->i2s_mode;
+ u8 i2s_net = ssi->i2s_net;
- regmap_update_bits(regs, CCSR_SSI_SCR,
- CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
- channels == 1 ? 0 : i2smode);
- }
+ /* Normal + Network mode to send 16-bit data in 32-bit frames */
+ if (fsl_ssi_is_i2s_bc_fp(ssi) && sample_size == 16)
+ i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
- /*
- * FIXME: The documentation says that SxCCR[WL] should not be
- * modified while the SSI is enabled. The only time this can
- * happen is if we're trying to do simultaneous playback and
- * capture in asynchronous mode. Unfortunately, I have been enable
- * to get that to work at all on the P1022DS. Therefore, we don't
- * bother to disable/enable the SSI when setting SxCCR[WL], because
- * the SSI will stop anyway. Maybe one day, this will get fixed.
- */
+ /* Use Normal mode to send mono data at 1st slot of 2 slots */
+ if (channels == 1)
+ i2s_net = SSI_SCR_I2S_MODE_NORMAL;
+
+ regmap_update_bits(regs, REG_SSI_SCR,
+ SSI_SCR_I2S_NET_MASK, i2s_net);
+ }
/* In synchronous mode, the SSI uses STCCR for capture */
- if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
- ssi_private->cpu_dai_drv.symmetric_rates)
- regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_WL_MASK,
- wl);
- else
- regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK,
- wl);
+ tx2 = tx || ssi->synchronous;
+ regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
+
+ if (ssi->use_dyna_fifo) {
+ if (channels == 1) {
+ ssi->audio_config[0].n_fifos_dst = 1;
+ ssi->audio_config[1].n_fifos_src = 1;
+ vals[RX].srcr &= ~SSI_SRCR_RFEN1;
+ vals[TX].stcr &= ~SSI_STCR_TFEN1;
+ vals[RX].scr &= ~SSI_SCR_TCH_EN;
+ vals[TX].scr &= ~SSI_SCR_TCH_EN;
+ } else {
+ ssi->audio_config[0].n_fifos_dst = 2;
+ ssi->audio_config[1].n_fifos_src = 2;
+ vals[RX].srcr |= SSI_SRCR_RFEN1;
+ vals[TX].stcr |= SSI_STCR_TFEN1;
+ vals[RX].scr |= SSI_SCR_TCH_EN;
+ vals[TX].scr |= SSI_SCR_TCH_EN;
+ }
+ ssi->dma_params_tx.peripheral_config = &ssi->audio_config[0];
+ ssi->dma_params_tx.peripheral_size = sizeof(ssi->audio_config[0]);
+ ssi->dma_params_rx.peripheral_config = &ssi->audio_config[1];
+ ssi->dma_params_rx.peripheral_size = sizeof(ssi->audio_config[1]);
+ }
return 0;
}
static int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
+ struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
-
- if (fsl_ssi_is_i2s_master(ssi_private) &&
- ssi_private->baudclk_streams & BIT(substream->stream)) {
- clk_disable_unprepare(ssi_private->baudclk);
- ssi_private->baudclk_streams &= ~BIT(substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+
+ if (fsl_ssi_is_i2s_clock_provider(ssi) &&
+ ssi->baudclk_streams & BIT(substream->stream)) {
+ clk_disable_unprepare(ssi->baudclk);
+ ssi->baudclk_streams &= ~BIT(substream->stream);
}
return 0;
}
-static int _fsl_ssi_set_dai_fmt(struct device *dev,
- struct fsl_ssi_private *ssi_private,
- unsigned int fmt)
+static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt)
{
- struct regmap *regs = ssi_private->regs;
- u32 strcr = 0, stcr, srcr, scr, mask;
- u8 wm;
-
- ssi_private->dai_fmt = fmt;
-
- if (fsl_ssi_is_i2s_master(ssi_private) && IS_ERR(ssi_private->baudclk)) {
- dev_err(dev, "baudclk is missing which is necessary for master mode\n");
- return -EINVAL;
- }
+ u32 strcr = 0, scr = 0, stcr, srcr, mask;
+ unsigned int slots;
- fsl_ssi_setup_reg_vals(ssi_private);
+ ssi->dai_fmt = fmt;
- regmap_read(regs, CCSR_SSI_SCR, &scr);
- scr &= ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
- scr |= CCSR_SSI_SCR_SYNC_TX_FS;
+ /* Synchronize frame sync clock for TE to avoid data slipping */
+ scr |= SSI_SCR_SYNC_TX_FS;
- mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR |
- CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL |
- CCSR_SSI_STCR_TEFS;
- regmap_read(regs, CCSR_SSI_STCR, &stcr);
- regmap_read(regs, CCSR_SSI_SRCR, &srcr);
- stcr &= ~mask;
- srcr &= ~mask;
+ /* Set to default shifting settings: LSB_ALIGNED */
+ strcr |= SSI_STCR_TXBIT0;
- ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
+ /* Use Network mode as default */
+ ssi->i2s_net = SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFS:
- ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
- regmap_update_bits(regs, CCSR_SSI_STCCR,
- CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
- regmap_update_bits(regs, CCSR_SSI_SRCCR,
- CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ if (IS_ERR(ssi->baudclk)) {
+ dev_err(ssi->dev,
+ "missing baudclk for master mode\n");
+ return -EINVAL;
+ }
+ fallthrough;
+ case SND_SOC_DAIFMT_BC_FP:
+ ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
+ case SND_SOC_DAIFMT_BC_FC:
+ ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE;
break;
default:
return -EINVAL;
}
+ slots = ssi->slots ? : 2;
+ regmap_update_bits(ssi->regs, REG_SSI_STCCR,
+ SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
+ regmap_update_bits(ssi->regs, REG_SSI_SRCCR,
+ SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
+
/* Data on rising edge of bclk, frame low, 1clk before data */
- strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP |
- CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
+ strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS;
break;
case SND_SOC_DAIFMT_LEFT_J:
/* Data on rising edge of bclk, frame high */
- strcr |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP;
+ strcr |= SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_DSP_A:
/* Data on rising edge of bclk, frame high, 1clk before data */
- strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
- CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
+ strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS;
break;
case SND_SOC_DAIFMT_DSP_B:
/* Data on rising edge of bclk, frame high */
- strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
- CCSR_SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_AC97:
- ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
+ /* Data on falling edge of bclk, frame high, 1clk before data */
+ strcr |= SSI_STCR_TEFS;
break;
default:
return -EINVAL;
}
- scr |= ssi_private->i2s_mode;
+
+ scr |= ssi->i2s_net;
/* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -954,146 +975,131 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
break;
case SND_SOC_DAIFMT_IB_NF:
/* Invert bit clock */
- strcr ^= CCSR_SSI_STCR_TSCKP;
+ strcr ^= SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_NB_IF:
/* Invert frame clock */
- strcr ^= CCSR_SSI_STCR_TFSI;
+ strcr ^= SSI_STCR_TFSI;
break;
case SND_SOC_DAIFMT_IB_IF:
/* Invert both clocks */
- strcr ^= CCSR_SSI_STCR_TSCKP;
- strcr ^= CCSR_SSI_STCR_TFSI;
+ strcr ^= SSI_STCR_TSCKP;
+ strcr ^= SSI_STCR_TFSI;
break;
default:
return -EINVAL;
}
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR;
- scr |= CCSR_SSI_SCR_SYS_CLK_EN;
+ /* DAI clock provider masks */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* Output bit and frame sync clocks */
+ strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
+ scr |= SSI_SCR_SYS_CLK_EN;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
+ case SND_SOC_DAIFMT_BC_FC:
+ /* Input bit or frame sync clocks */
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- strcr &= ~CCSR_SSI_STCR_TXDIR;
- strcr |= CCSR_SSI_STCR_TFDIR;
- scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
+ case SND_SOC_DAIFMT_BC_FP:
+ /* Input bit clock but output frame sync clock */
+ strcr |= SSI_STCR_TFDIR;
break;
default:
- if (!fsl_ssi_is_ac97(ssi_private))
- return -EINVAL;
+ return -EINVAL;
}
- stcr |= strcr;
- srcr |= strcr;
+ stcr = strcr;
+ srcr = strcr;
- if (ssi_private->cpu_dai_drv.symmetric_rates
- || fsl_ssi_is_ac97(ssi_private)) {
- /* Need to clear RXDIR when using SYNC or AC97 mode */
- srcr &= ~CCSR_SSI_SRCR_RXDIR;
- scr |= CCSR_SSI_SCR_SYN;
+ /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
+ if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) {
+ srcr &= ~SSI_SRCR_RXDIR;
+ scr |= SSI_SCR_SYN;
}
- regmap_write(regs, CCSR_SSI_STCR, stcr);
- regmap_write(regs, CCSR_SSI_SRCR, srcr);
- regmap_write(regs, CCSR_SSI_SCR, scr);
+ mask = SSI_STCR_TFDIR | SSI_STCR_TXDIR | SSI_STCR_TSCKP |
+ SSI_STCR_TFSL | SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
- /*
- * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
- * use FIFO 1. We program the transmit water to signal a DMA transfer
- * if there are only two (or fewer) elements left in the FIFO. Two
- * elements equals one frame (left channel, right channel). This value,
- * however, depends on the depth of the transmit buffer.
- *
- * We set the watermark on the same level as the DMA burstsize. For
- * fiq it is probably better to use the biggest possible watermark
- * size.
- */
- if (ssi_private->use_dma)
- wm = ssi_private->fifo_depth - 2;
- else
- wm = ssi_private->fifo_depth;
-
- regmap_write(regs, CCSR_SSI_SFCSR,
- CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
- CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm));
-
- if (ssi_private->use_dual_fifo) {
- regmap_update_bits(regs, CCSR_SSI_SRCR, CCSR_SSI_SRCR_RFEN1,
- CCSR_SSI_SRCR_RFEN1);
- regmap_update_bits(regs, CCSR_SSI_STCR, CCSR_SSI_STCR_TFEN1,
- CCSR_SSI_STCR_TFEN1);
- regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_TCH_EN,
- CCSR_SSI_SCR_TCH_EN);
- }
+ regmap_update_bits(ssi->regs, REG_SSI_STCR, mask, stcr);
+ regmap_update_bits(ssi->regs, REG_SSI_SRCR, mask, srcr);
- if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97)
- fsl_ssi_setup_ac97(ssi_private);
+ mask = SSI_SCR_SYNC_TX_FS | SSI_SCR_I2S_MODE_MASK |
+ SSI_SCR_SYS_CLK_EN | SSI_SCR_SYN;
+ regmap_update_bits(ssi->regs, REG_SSI_SCR, mask, scr);
return 0;
-
}
/**
- * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format.
+ * fsl_ssi_set_dai_fmt - Configure Digital Audio Interface (DAI) Format
+ * @dai: pointer to DAI
+ * @fmt: format mask
*/
-static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
+
+ /* AC97 configured DAIFMT earlier in the probe() */
+ if (fsl_ssi_is_ac97(ssi))
+ return 0;
- return _fsl_ssi_set_dai_fmt(cpu_dai->dev, ssi_private, fmt);
+ return _fsl_ssi_set_dai_fmt(ssi, fmt);
}
/**
- * fsl_ssi_set_dai_tdm_slot - set TDM slot number
- *
- * Note: This function can be only called when using SSI as DAI master
+ * fsl_ssi_set_dai_tdm_slot - Set TDM slot number and slot width
+ * @dai: pointer to DAI
+ * @tx_mask: mask for TX
+ * @rx_mask: mask for RX
+ * @slots: number of slots
+ * @slot_width: number of bits per slot
*/
-static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
- u32 rx_mask, int slots, int slot_width)
+static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
- struct regmap *regs = ssi_private->regs;
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
+ struct regmap *regs = ssi->regs;
u32 val;
+ /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */
+ if (slot_width & 1 || slot_width < 8 || slot_width > 24) {
+ dev_err(dai->dev, "invalid slot width: %d\n", slot_width);
+ return -EINVAL;
+ }
+
/* The slot number should be >= 2 if using Network mode or I2S mode */
- regmap_read(regs, CCSR_SSI_SCR, &val);
- val &= CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET;
- if (val && slots < 2) {
- dev_err(cpu_dai->dev, "slot number should be >= 2 in I2S or NET\n");
+ if (ssi->i2s_net && slots < 2) {
+ dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n");
return -EINVAL;
}
- regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(slots));
- regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(slots));
+ regmap_update_bits(regs, REG_SSI_STCCR,
+ SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
+ regmap_update_bits(regs, REG_SSI_SRCCR,
+ SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
- /* The register SxMSKs needs SSI to provide essential clock due to
- * hardware design. So we here temporarily enable SSI to set them.
- */
- regmap_read(regs, CCSR_SSI_SCR, &val);
- val &= CCSR_SSI_SCR_SSIEN;
- regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN,
- CCSR_SSI_SCR_SSIEN);
+ /* Save the SCR register value */
+ regmap_read(regs, REG_SSI_SCR, &val);
+ /* Temporarily enable SSI to allow SxMSKs to be configurable */
+ regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
+
+ regmap_write(regs, REG_SSI_STMSK, ~tx_mask);
+ regmap_write(regs, REG_SSI_SRMSK, ~rx_mask);
- regmap_write(regs, CCSR_SSI_STMSK, ~tx_mask);
- regmap_write(regs, CCSR_SSI_SRMSK, ~rx_mask);
+ /* Restore the value of SSIEN bit */
+ regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val);
- regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val);
+ ssi->slot_width = slot_width;
+ ssi->slots = slots;
return 0;
}
/**
- * fsl_ssi_trigger: start and stop the DMA transfer.
- *
- * This function is called by ALSA to start, stop, pause, and resume the DMA
- * transfer of data.
+ * fsl_ssi_trigger - Start or stop SSI and corresponding DMA transaction.
+ * @substream: ASoC substream
+ * @cmd: trigger command
+ * @dai: pointer to DAI
*
* The DMA channel is in external master start and pause mode, which
* means the SSI completely controls the flow of data.
@@ -1101,115 +1107,108 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct regmap *regs = ssi_private->regs;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- fsl_ssi_tx_config(ssi_private, true);
- else
- fsl_ssi_rx_config(ssi_private, true);
+ /*
+ * SACCST might be modified via AC Link by a CODEC if it sends
+ * extra bits in their SLOTREQ requests, which'll accidentally
+ * send valid data to slots other than normal playback slots.
+ *
+ * To be safe, configure SACCST right before TX starts.
+ */
+ if (tx && fsl_ssi_is_ac97(ssi))
+ fsl_ssi_tx_ac97_saccst_setup(ssi);
+ fsl_ssi_config_enable(ssi, tx);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- fsl_ssi_tx_config(ssi_private, false);
- else
- fsl_ssi_rx_config(ssi_private, false);
+ fsl_ssi_config_disable(ssi, tx);
break;
default:
return -EINVAL;
}
- if (fsl_ssi_is_ac97(ssi_private)) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_TX_CLR);
- else
- regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_RX_CLR);
- }
-
return 0;
}
static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
{
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
+ struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
- if (ssi_private->soc->imx && ssi_private->use_dma) {
- dai->playback_dma_data = &ssi_private->dma_params_tx;
- dai->capture_dma_data = &ssi_private->dma_params_rx;
- }
+ if (ssi->soc->imx && ssi->use_dma)
+ snd_soc_dai_init_dma_data(dai, &ssi->dma_params_tx,
+ &ssi->dma_params_rx);
return 0;
}
static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
- .startup = fsl_ssi_startup,
- .shutdown = fsl_ssi_shutdown,
- .hw_params = fsl_ssi_hw_params,
- .hw_free = fsl_ssi_hw_free,
- .set_fmt = fsl_ssi_set_dai_fmt,
- .set_sysclk = fsl_ssi_set_dai_sysclk,
- .set_tdm_slot = fsl_ssi_set_dai_tdm_slot,
- .trigger = fsl_ssi_trigger,
+ .probe = fsl_ssi_dai_probe,
+ .startup = fsl_ssi_startup,
+ .shutdown = fsl_ssi_shutdown,
+ .hw_params = fsl_ssi_hw_params,
+ .hw_free = fsl_ssi_hw_free,
+ .set_fmt = fsl_ssi_set_dai_fmt,
+ .set_tdm_slot = fsl_ssi_set_dai_tdm_slot,
+ .trigger = fsl_ssi_trigger,
};
-/* Template for the CPU dai driver structure */
static struct snd_soc_dai_driver fsl_ssi_dai_template = {
- .probe = fsl_ssi_dai_probe,
.playback = {
.stream_name = "CPU-Playback",
.channels_min = 1,
- .channels_max = 2,
- .rates = FSLSSI_I2S_RATES,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = FSLSSI_I2S_FORMATS,
},
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
- .channels_max = 2,
- .rates = FSLSSI_I2S_RATES,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = FSLSSI_I2S_FORMATS,
},
.ops = &fsl_ssi_dai_ops,
};
static const struct snd_soc_component_driver fsl_ssi_component = {
- .name = "fsl-ssi",
+ .name = "fsl-ssi",
+ .legacy_dai_naming = 1,
};
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
- .bus_control = true,
- .probe = fsl_ssi_dai_probe,
+ .symmetric_channels = 1,
.playback = {
- .stream_name = "AC97 Playback",
+ .stream_name = "CPU AC97 Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20,
},
.capture = {
- .stream_name = "AC97 Capture",
+ .stream_name = "CPU AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ /* 16-bit capture is broken (errata ERR003778) */
+ .formats = SNDRV_PCM_FMTBIT_S20,
},
.ops = &fsl_ssi_dai_ops,
};
-
-static struct fsl_ssi_private *fsl_ac97_data;
+static struct fsl_ssi *fsl_ac97_data;
static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
+ unsigned short val)
{
struct regmap *regs = fsl_ac97_data->regs;
unsigned int lreg;
@@ -1219,350 +1218,439 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
if (reg > 0x7f)
return;
+ mutex_lock(&fsl_ac97_data->ac97_reg_lock);
+
ret = clk_prepare_enable(fsl_ac97_data->clk);
if (ret) {
pr_err("ac97 write clk_prepare_enable failed: %d\n",
ret);
- return;
+ goto ret_unlock;
}
lreg = reg << 12;
- regmap_write(regs, CCSR_SSI_SACADD, lreg);
+ regmap_write(regs, REG_SSI_SACADD, lreg);
lval = val << 4;
- regmap_write(regs, CCSR_SSI_SACDAT, lval);
+ regmap_write(regs, REG_SSI_SACDAT, lval);
- regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK,
- CCSR_SSI_SACNT_WR);
+ regmap_update_bits(regs, REG_SSI_SACNT,
+ SSI_SACNT_RDWR_MASK, SSI_SACNT_WR);
udelay(100);
clk_disable_unprepare(fsl_ac97_data->clk);
+
+ret_unlock:
+ mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
}
static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
+ unsigned short reg)
{
struct regmap *regs = fsl_ac97_data->regs;
-
- unsigned short val = -1;
+ unsigned short val = 0;
u32 reg_val;
unsigned int lreg;
int ret;
+ mutex_lock(&fsl_ac97_data->ac97_reg_lock);
+
ret = clk_prepare_enable(fsl_ac97_data->clk);
if (ret) {
- pr_err("ac97 read clk_prepare_enable failed: %d\n",
- ret);
- return -1;
+ pr_err("ac97 read clk_prepare_enable failed: %d\n", ret);
+ goto ret_unlock;
}
lreg = (reg & 0x7f) << 12;
- regmap_write(regs, CCSR_SSI_SACADD, lreg);
- regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK,
- CCSR_SSI_SACNT_RD);
+ regmap_write(regs, REG_SSI_SACADD, lreg);
+ regmap_update_bits(regs, REG_SSI_SACNT,
+ SSI_SACNT_RDWR_MASK, SSI_SACNT_RD);
udelay(100);
- regmap_read(regs, CCSR_SSI_SACDAT, &reg_val);
+ regmap_read(regs, REG_SSI_SACDAT, &reg_val);
val = (reg_val >> 4) & 0xffff;
clk_disable_unprepare(fsl_ac97_data->clk);
+ret_unlock:
+ mutex_unlock(&fsl_ac97_data->ac97_reg_lock);
return val;
}
static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
- .read = fsl_ssi_ac97_read,
- .write = fsl_ssi_ac97_write,
+ .read = fsl_ssi_ac97_read,
+ .write = fsl_ssi_ac97_write,
};
/**
- * Make every character in a string lower-case
+ * fsl_ssi_hw_init - Initialize SSI registers
+ * @ssi: SSI context
*/
-static void make_lowercase(char *s)
+static int fsl_ssi_hw_init(struct fsl_ssi *ssi)
{
- char *p = s;
- char c;
+ u32 wm = ssi->fifo_watermark;
- while ((c = *p)) {
- if ((c >= 'A') && (c <= 'Z'))
- *p = c + ('a' - 'A');
- p++;
+ /* Initialize regvals */
+ fsl_ssi_setup_regvals(ssi);
+
+ /* Set watermarks */
+ regmap_write(ssi->regs, REG_SSI_SFCSR,
+ SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) |
+ SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm));
+
+ /* Enable Dual FIFO mode */
+ if (ssi->use_dual_fifo)
+ regmap_update_bits(ssi->regs, REG_SSI_SCR,
+ SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
+
+ /* AC97 should start earlier to communicate with CODECs */
+ if (fsl_ssi_is_ac97(ssi)) {
+ _fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt);
+ fsl_ssi_setup_ac97(ssi);
}
+
+ return 0;
+}
+
+/**
+ * fsl_ssi_hw_clean - Clear SSI registers
+ * @ssi: SSI context
+ */
+static void fsl_ssi_hw_clean(struct fsl_ssi *ssi)
+{
+ /* Disable registers for AC97 */
+ if (fsl_ssi_is_ac97(ssi)) {
+ /* Disable TE and RE bits first */
+ regmap_update_bits(ssi->regs, REG_SSI_SCR,
+ SSI_SCR_TE | SSI_SCR_RE, 0);
+ /* Disable AC97 mode */
+ regmap_write(ssi->regs, REG_SSI_SACNT, 0);
+ /* Unset WAIT bits */
+ regmap_write(ssi->regs, REG_SSI_SOR, 0);
+ /* Disable SSI -- software reset */
+ regmap_update_bits(ssi->regs, REG_SSI_SCR, SSI_SCR_SSIEN, 0);
+ }
+}
+
+/*
+ * Make every character in a string lower-case
+ */
+static void make_lowercase(char *s)
+{
+ if (!s)
+ return;
+ for (; *s; s++)
+ *s = tolower(*s);
}
static int fsl_ssi_imx_probe(struct platform_device *pdev,
- struct fsl_ssi_private *ssi_private, void __iomem *iomem)
+ struct fsl_ssi *ssi, void __iomem *iomem)
{
- struct device_node *np = pdev->dev.of_node;
- u32 dmas[4];
+ struct device *dev = &pdev->dev;
int ret;
- if (ssi_private->has_ipg_clk_name)
- ssi_private->clk = devm_clk_get(&pdev->dev, "ipg");
+ /* Backward compatible for a DT without ipg clock name assigned */
+ if (ssi->has_ipg_clk_name)
+ ssi->clk = devm_clk_get(dev, "ipg");
else
- ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(ssi_private->clk)) {
- ret = PTR_ERR(ssi_private->clk);
- dev_err(&pdev->dev, "could not get clock: %d\n", ret);
+ ssi->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ssi->clk)) {
+ ret = PTR_ERR(ssi->clk);
+ dev_err(dev, "failed to get clock: %d\n", ret);
return ret;
}
- if (!ssi_private->has_ipg_clk_name) {
- ret = clk_prepare_enable(ssi_private->clk);
+ /* Enable the clock since regmap will not handle it in this case */
+ if (!ssi->has_ipg_clk_name) {
+ ret = clk_prepare_enable(ssi->clk);
if (ret) {
- dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+ dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
return ret;
}
}
- /* For those SLAVE implementations, we ignore non-baudclk cases
- * and, instead, abandon MASTER mode that needs baud clock.
- */
- ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
- if (IS_ERR(ssi_private->baudclk))
- dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
- PTR_ERR(ssi_private->baudclk));
-
- /*
- * We have burstsize be "fifo_depth - 2" to match the SSI
- * watermark setting in fsl_ssi_startup().
- */
- ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2;
- ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2;
- ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0;
- ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0;
-
- ret = of_property_read_u32_array(np, "dmas", dmas, 4);
- if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
- ssi_private->use_dual_fifo = true;
- /* When using dual fifo mode, we need to keep watermark
- * as even numbers due to dma script limitation.
- */
- ssi_private->dma_params_tx.maxburst &= ~0x1;
- ssi_private->dma_params_rx.maxburst &= ~0x1;
+ /* Do not error out for consumer cases that live without a baud clock */
+ ssi->baudclk = devm_clk_get(dev, "baud");
+ if (IS_ERR(ssi->baudclk))
+ dev_dbg(dev, "failed to get baud clock: %ld\n",
+ PTR_ERR(ssi->baudclk));
+
+ ssi->dma_params_tx.maxburst = ssi->dma_maxburst;
+ ssi->dma_params_rx.maxburst = ssi->dma_maxburst;
+ ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0;
+ ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
+
+ /* Use even numbers to avoid channel swap due to SDMA script design */
+ if (ssi->use_dual_fifo || ssi->use_dyna_fifo) {
+ ssi->dma_params_tx.maxburst &= ~0x1;
+ ssi->dma_params_rx.maxburst &= ~0x1;
}
- if (!ssi_private->use_dma) {
-
+ if (!ssi->use_dma) {
/*
- * Some boards use an incompatible codec. To get it
- * working, we are using imx-fiq-pcm-audio, that
- * can handle those codecs. DMA is not possible in this
- * situation.
+ * Some boards use an incompatible codec. Use imx-fiq-pcm-audio
+ * to get it working, as DMA is not possible in this situation.
*/
+ ssi->fiq_params.irq = ssi->irq;
+ ssi->fiq_params.base = iomem;
+ ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
+ ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
- ssi_private->fiq_params.irq = ssi_private->irq;
- ssi_private->fiq_params.base = iomem;
- ssi_private->fiq_params.dma_params_rx =
- &ssi_private->dma_params_rx;
- ssi_private->fiq_params.dma_params_tx =
- &ssi_private->dma_params_tx;
-
- ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
+ ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
if (ret)
goto error_pcm;
} else {
- ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
- if (ret)
+ ret = imx_pcm_dma_init(pdev);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to init PCM DMA\n");
goto error_pcm;
+ }
}
return 0;
error_pcm:
+ if (!ssi->has_ipg_clk_name)
+ clk_disable_unprepare(ssi->clk);
- if (!ssi_private->has_ipg_clk_name)
- clk_disable_unprepare(ssi_private->clk);
return ret;
}
-static void fsl_ssi_imx_clean(struct platform_device *pdev,
- struct fsl_ssi_private *ssi_private)
+static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
{
- if (!ssi_private->use_dma)
+ if (!ssi->use_dma)
imx_pcm_fiq_exit(pdev);
- if (!ssi_private->has_ipg_clk_name)
- clk_disable_unprepare(ssi_private->clk);
+ if (!ssi->has_ipg_clk_name)
+ clk_disable_unprepare(ssi->clk);
}
-static int fsl_ssi_probe(struct platform_device *pdev)
+static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
{
- struct fsl_ssi_private *ssi_private;
- int ret = 0;
- struct device_node *np = pdev->dev.of_node;
- const struct of_device_id *of_id;
+ struct device *dev = ssi->dev;
+ struct device_node *np = dev->of_node;
const char *p, *sprop;
- const uint32_t *iprop;
- struct resource *res;
- void __iomem *iomem;
- char name[64];
+ const __be32 *iprop;
+ u32 dmas[4];
+ int ret;
- of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
- if (!of_id || !of_id->data)
- return -EINVAL;
+ ret = of_property_match_string(np, "clock-names", "ipg");
+ /* Get error code if not found */
+ ssi->has_ipg_clk_name = ret >= 0;
- ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private),
- GFP_KERNEL);
- if (!ssi_private) {
- dev_err(&pdev->dev, "could not allocate DAI object\n");
- return -ENOMEM;
+ /* Check if being used in AC97 mode */
+ sprop = of_get_property(np, "fsl,mode", NULL);
+ if (sprop && !strcmp(sprop, "ac97-slave")) {
+ ssi->dai_fmt = FSLSSI_AC97_DAIFMT;
+
+ ret = of_property_read_u32(np, "cell-index", &ssi->card_idx);
+ if (ret) {
+ dev_err(dev, "failed to get SSI index property\n");
+ return -EINVAL;
+ }
+ strcpy(ssi->card_name, "ac97-codec");
+ } else if (!of_property_read_bool(np, "fsl,ssi-asynchronous")) {
+ /*
+ * In synchronous mode, STCK and STFS ports are used by RX
+ * as well. So the software should limit the sample rates,
+ * sample bits and channels to be symmetric.
+ *
+ * This is exclusive with FSLSSI_AC97_FORMATS as AC97 runs
+ * in the SSI synchronous mode however it does not have to
+ * limit symmetric sample rates and sample bits.
+ */
+ ssi->synchronous = true;
}
- ssi_private->soc = of_id->data;
+ /* Select DMA or FIQ */
+ ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
- sprop = of_get_property(np, "fsl,mode", NULL);
- if (sprop) {
- if (!strcmp(sprop, "ac97-slave"))
- ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97;
+ /* Fetch FIFO depth; Set to 8 for older DT without this property */
+ iprop = of_get_property(np, "fsl,fifo-depth", NULL);
+ if (iprop)
+ ssi->fifo_depth = be32_to_cpup(iprop);
+ else
+ ssi->fifo_depth = 8;
+
+ /* Use dual FIFO mode depending on the support from SDMA script */
+ ret = of_property_read_u32_array(np, "dmas", dmas, 4);
+ if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
+ ssi->use_dual_fifo = true;
+
+ if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
+ ssi->use_dyna_fifo = true;
+ /*
+ * Backward compatible for older bindings by manually triggering the
+ * machine driver's probe(). Use /compatible property, including the
+ * address of CPU DAI driver structure, as the name of machine driver
+ *
+ * If card_name is set by AC97 earlier, bypass here since it uses a
+ * different name to register the device.
+ */
+ if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) {
+ struct device_node *root = of_find_node_by_path("/");
+
+ sprop = of_get_property(root, "compatible", NULL);
+ of_node_put(root);
+ /* Strip "fsl," in the compatible name if applicable */
+ p = strrchr(sprop, ',');
+ if (p)
+ sprop = p + 1;
+ snprintf(ssi->card_name, sizeof(ssi->card_name),
+ "snd-soc-%s", sprop);
+ make_lowercase(ssi->card_name);
+ ssi->card_idx = 0;
}
- ssi_private->use_dma = !of_property_read_bool(np,
- "fsl,fiq-stream-filter");
+ return 0;
+}
+
+static int fsl_ssi_probe(struct platform_device *pdev)
+{
+ struct regmap_config regconfig = fsl_ssi_regconfig;
+ struct device *dev = &pdev->dev;
+ struct fsl_ssi *ssi;
+ struct resource *res;
+ void __iomem *iomem;
+ int ret = 0;
+
+ ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
+ if (!ssi)
+ return -ENOMEM;
- if (fsl_ssi_is_ac97(ssi_private)) {
- memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
- sizeof(fsl_ssi_ac97_dai));
+ ssi->dev = dev;
+ ssi->soc = of_device_get_match_data(&pdev->dev);
- fsl_ac97_data = ssi_private;
+ /* Probe from DT */
+ ret = fsl_ssi_probe_from_dt(ssi);
+ if (ret)
+ return ret;
- ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
- if (ret) {
- dev_err(&pdev->dev, "could not set AC'97 ops\n");
- return ret;
- }
+ if (fsl_ssi_is_ac97(ssi)) {
+ memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai,
+ sizeof(fsl_ssi_ac97_dai));
+ fsl_ac97_data = ssi;
} else {
- /* Initialize this copy of the CPU DAI driver structure */
- memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
+ memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template,
sizeof(fsl_ssi_dai_template));
}
- ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev);
+ ssi->cpu_dai_drv.name = dev_name(dev);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iomem = devm_ioremap_resource(&pdev->dev, res);
+ iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(iomem))
return PTR_ERR(iomem);
- ssi_private->ssi_phys = res->start;
+ ssi->ssi_phys = res->start;
- ret = of_property_match_string(np, "clock-names", "ipg");
- if (ret < 0) {
- ssi_private->has_ipg_clk_name = false;
- ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
- &fsl_ssi_regconfig);
- } else {
- ssi_private->has_ipg_clk_name = true;
- ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev,
- "ipg", iomem, &fsl_ssi_regconfig);
- }
- if (IS_ERR(ssi_private->regs)) {
- dev_err(&pdev->dev, "Failed to init register map\n");
- return PTR_ERR(ssi_private->regs);
+ if (ssi->soc->imx21regs) {
+ /* No SACC{ST,EN,DIS} regs in imx21-class SSI */
+ regconfig.max_register = REG_SSI_SRMSK;
+ regconfig.num_reg_defaults_raw =
+ REG_SSI_SRMSK / sizeof(uint32_t) + 1;
}
- ssi_private->irq = platform_get_irq(pdev, 0);
- if (ssi_private->irq < 0) {
- dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
- return ssi_private->irq;
+ if (ssi->has_ipg_clk_name)
+ ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem,
+ &regconfig);
+ else
+ ssi->regs = devm_regmap_init_mmio(dev, iomem, &regconfig);
+ if (IS_ERR(ssi->regs)) {
+ dev_err(dev, "failed to init register map\n");
+ return PTR_ERR(ssi->regs);
}
- /* Are the RX and the TX clocks locked? */
- if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
- if (!fsl_ssi_is_ac97(ssi_private))
- ssi_private->cpu_dai_drv.symmetric_rates = 1;
+ ssi->irq = platform_get_irq(pdev, 0);
+ if (ssi->irq < 0)
+ return ssi->irq;
- ssi_private->cpu_dai_drv.symmetric_channels = 1;
- ssi_private->cpu_dai_drv.symmetric_samplebits = 1;
+ /* Set software limitations for synchronous mode except AC97 */
+ if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) {
+ ssi->cpu_dai_drv.symmetric_rate = 1;
+ ssi->cpu_dai_drv.symmetric_channels = 1;
+ ssi->cpu_dai_drv.symmetric_sample_bits = 1;
}
- /* Determine the FIFO depth. */
- iprop = of_get_property(np, "fsl,fifo-depth", NULL);
- if (iprop)
- ssi_private->fifo_depth = be32_to_cpup(iprop);
- else
- /* Older 8610 DTs didn't have the fifo-depth property */
- ssi_private->fifo_depth = 8;
+ /*
+ * Configure TX and RX DMA watermarks -- when to send a DMA request
+ *
+ * Values should be tested to avoid FIFO under/over run. Set maxburst
+ * to fifo_watermark to maxiumize DMA transaction to reduce overhead.
+ */
+ switch (ssi->fifo_depth) {
+ case 15:
+ /*
+ * Set to 8 as a balanced configuration -- When TX FIFO has 8
+ * empty slots, send a DMA request to fill these 8 slots. The
+ * remaining 7 slots should be able to allow DMA to finish the
+ * transaction before TX FIFO underruns; Same applies to RX.
+ *
+ * Tested with cases running at 48kHz @ 16 bits x 16 channels
+ */
+ ssi->fifo_watermark = 8;
+ ssi->dma_maxburst = 8;
+ break;
+ case 8:
+ default:
+ /* Safely use old watermark configurations for older chips */
+ ssi->fifo_watermark = ssi->fifo_depth - 2;
+ ssi->dma_maxburst = ssi->fifo_depth - 2;
+ break;
+ }
- dev_set_drvdata(&pdev->dev, ssi_private);
+ dev_set_drvdata(dev, ssi);
- if (ssi_private->soc->imx) {
- ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem);
+ if (ssi->soc->imx) {
+ ret = fsl_ssi_imx_probe(pdev, ssi, iomem);
if (ret)
return ret;
}
- ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
- &ssi_private->cpu_dai_drv, 1);
+ if (fsl_ssi_is_ac97(ssi)) {
+ mutex_init(&ssi->ac97_reg_lock);
+ ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+ if (ret) {
+ dev_err(dev, "failed to set AC'97 ops\n");
+ goto error_ac97_ops;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(dev, &fsl_ssi_component,
+ &ssi->cpu_dai_drv, 1);
if (ret) {
- dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+ dev_err(dev, "failed to register DAI: %d\n", ret);
goto error_asoc_register;
}
- if (ssi_private->use_dma) {
- ret = devm_request_irq(&pdev->dev, ssi_private->irq,
- fsl_ssi_isr, 0, dev_name(&pdev->dev),
- ssi_private);
+ if (ssi->use_dma) {
+ ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0,
+ dev_name(dev), ssi);
if (ret < 0) {
- dev_err(&pdev->dev, "could not claim irq %u\n",
- ssi_private->irq);
+ dev_err(dev, "failed to claim irq %u\n", ssi->irq);
goto error_asoc_register;
}
}
- ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
- if (ret)
- goto error_asoc_register;
-
- /*
- * If codec-handle property is missing from SSI node, we assume
- * that the machine driver uses new binding which does not require
- * SSI driver to trigger machine driver's probe.
- */
- if (!of_get_property(np, "codec-handle", NULL))
- goto done;
-
- /* Trigger the machine driver's probe function. The platform driver
- * name of the machine driver is taken from /compatible property of the
- * device tree. We also pass the address of the CPU DAI driver
- * structure.
- */
- sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
- /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
- p = strrchr(sprop, ',');
- if (p)
- sprop = p + 1;
- snprintf(name, sizeof(name), "snd-soc-%s", sprop);
- make_lowercase(name);
-
- ssi_private->pdev =
- platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
- if (IS_ERR(ssi_private->pdev)) {
- ret = PTR_ERR(ssi_private->pdev);
- dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
- goto error_sound_card;
- }
-
-done:
- if (ssi_private->dai_fmt)
- _fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private,
- ssi_private->dai_fmt);
+ fsl_ssi_debugfs_create(&ssi->dbg_stats, dev);
- if (fsl_ssi_is_ac97(ssi_private)) {
- u32 ssi_idx;
-
- ret = of_property_read_u32(np, "cell-index", &ssi_idx);
- if (ret) {
- dev_err(&pdev->dev, "cannot get SSI index property\n");
- goto error_sound_card;
- }
+ /* Initially configures SSI registers */
+ fsl_ssi_hw_init(ssi);
- ssi_private->pdev =
- platform_device_register_data(NULL,
- "ac97-codec", ssi_idx, NULL, 0);
- if (IS_ERR(ssi_private->pdev)) {
- ret = PTR_ERR(ssi_private->pdev);
- dev_err(&pdev->dev,
- "failed to register AC97 codec platform: %d\n",
- ret);
+ /* Register a platform device for older bindings or AC97 */
+ if (ssi->card_name[0]) {
+ struct device *parent = dev;
+ /*
+ * Do not set SSI dev as the parent of AC97 CODEC device since
+ * it does not have a DT node. Otherwise ASoC core will assume
+ * CODEC has the same DT node as the SSI, so it may bypass the
+ * dai_probe() of SSI and then cause NULL DMA data pointers.
+ */
+ if (fsl_ssi_is_ac97(ssi))
+ parent = NULL;
+
+ ssi->card_pdev = platform_device_register_data(parent,
+ ssi->card_name, ssi->card_idx, NULL, 0);
+ if (IS_ERR(ssi->card_pdev)) {
+ ret = PTR_ERR(ssi->card_pdev);
+ dev_err(dev, "failed to register %s: %d\n",
+ ssi->card_name, ret);
goto error_sound_card;
}
}
@@ -1570,43 +1658,48 @@ done:
return 0;
error_sound_card:
- fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
-
+ fsl_ssi_debugfs_remove(&ssi->dbg_stats);
error_asoc_register:
- if (ssi_private->soc->imx)
- fsl_ssi_imx_clean(pdev, ssi_private);
+ if (fsl_ssi_is_ac97(ssi))
+ snd_soc_set_ac97_ops(NULL);
+error_ac97_ops:
+ if (fsl_ssi_is_ac97(ssi))
+ mutex_destroy(&ssi->ac97_reg_lock);
+
+ if (ssi->soc->imx)
+ fsl_ssi_imx_clean(pdev, ssi);
return ret;
}
-static int fsl_ssi_remove(struct platform_device *pdev)
+static void fsl_ssi_remove(struct platform_device *pdev)
{
- struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
+ struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev);
- fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
+ fsl_ssi_debugfs_remove(&ssi->dbg_stats);
- if (ssi_private->pdev)
- platform_device_unregister(ssi_private->pdev);
+ if (ssi->card_pdev)
+ platform_device_unregister(ssi->card_pdev);
- if (ssi_private->soc->imx)
- fsl_ssi_imx_clean(pdev, ssi_private);
+ /* Clean up SSI registers */
+ fsl_ssi_hw_clean(ssi);
- if (fsl_ssi_is_ac97(ssi_private))
- snd_soc_set_ac97_ops(NULL);
+ if (ssi->soc->imx)
+ fsl_ssi_imx_clean(pdev, ssi);
- return 0;
+ if (fsl_ssi_is_ac97(ssi)) {
+ snd_soc_set_ac97_ops(NULL);
+ mutex_destroy(&ssi->ac97_reg_lock);
+ }
}
-#ifdef CONFIG_PM_SLEEP
static int fsl_ssi_suspend(struct device *dev)
{
- struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
- struct regmap *regs = ssi_private->regs;
+ struct fsl_ssi *ssi = dev_get_drvdata(dev);
+ struct regmap *regs = ssi->regs;
- regmap_read(regs, CCSR_SSI_SFCSR,
- &ssi_private->regcache_sfcsr);
- regmap_read(regs, CCSR_SSI_SACNT,
- &ssi_private->regcache_sacnt);
+ regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr);
+ regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt);
regcache_cache_only(regs, true);
regcache_mark_dirty(regs);
@@ -1616,31 +1709,29 @@ static int fsl_ssi_suspend(struct device *dev)
static int fsl_ssi_resume(struct device *dev)
{
- struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
- struct regmap *regs = ssi_private->regs;
+ struct fsl_ssi *ssi = dev_get_drvdata(dev);
+ struct regmap *regs = ssi->regs;
regcache_cache_only(regs, false);
- regmap_update_bits(regs, CCSR_SSI_SFCSR,
- CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK |
- CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK,
- ssi_private->regcache_sfcsr);
- regmap_write(regs, CCSR_SSI_SACNT,
- ssi_private->regcache_sacnt);
+ regmap_update_bits(regs, REG_SSI_SFCSR,
+ SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK |
+ SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK,
+ ssi->regcache_sfcsr);
+ regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt);
return regcache_sync(regs);
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops fsl_ssi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+ SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
};
static struct platform_driver fsl_ssi_driver = {
.driver = {
.name = "fsl-ssi-dai",
.of_match_table = fsl_ssi_ids,
- .pm = &fsl_ssi_pm,
+ .pm = pm_sleep_ptr(&fsl_ssi_pm),
},
.probe = fsl_ssi_probe,
.remove = fsl_ssi_remove,
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index 506510540d0a..db57cad80449 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -1,209 +1,267 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC
+ * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 and i.MX SoC
*
* Author: Timur Tabi <timur@freescale.com>
*
- * Copyright 2007-2008 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.
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
*/
#ifndef _MPC8610_I2S_H
#define _MPC8610_I2S_H
-/* SSI registers */
-#define CCSR_SSI_STX0 0x00
-#define CCSR_SSI_STX1 0x04
-#define CCSR_SSI_SRX0 0x08
-#define CCSR_SSI_SRX1 0x0c
-#define CCSR_SSI_SCR 0x10
-#define CCSR_SSI_SISR 0x14
-#define CCSR_SSI_SIER 0x18
-#define CCSR_SSI_STCR 0x1c
-#define CCSR_SSI_SRCR 0x20
-#define CCSR_SSI_STCCR 0x24
-#define CCSR_SSI_SRCCR 0x28
-#define CCSR_SSI_SFCSR 0x2c
-#define CCSR_SSI_STR 0x30
-#define CCSR_SSI_SOR 0x34
-#define CCSR_SSI_SACNT 0x38
-#define CCSR_SSI_SACADD 0x3c
-#define CCSR_SSI_SACDAT 0x40
-#define CCSR_SSI_SATAG 0x44
-#define CCSR_SSI_STMSK 0x48
-#define CCSR_SSI_SRMSK 0x4c
-#define CCSR_SSI_SACCST 0x50
-#define CCSR_SSI_SACCEN 0x54
-#define CCSR_SSI_SACCDIS 0x58
+/* -- SSI Register Map -- */
-#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000
-#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800
-#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400
-#define CCSR_SSI_SCR_TCH_EN 0x00000100
-#define CCSR_SSI_SCR_SYS_CLK_EN 0x00000080
-#define CCSR_SSI_SCR_I2S_MODE_MASK 0x00000060
-#define CCSR_SSI_SCR_I2S_MODE_NORMAL 0x00000000
-#define CCSR_SSI_SCR_I2S_MODE_MASTER 0x00000020
-#define CCSR_SSI_SCR_I2S_MODE_SLAVE 0x00000040
-#define CCSR_SSI_SCR_SYN 0x00000010
-#define CCSR_SSI_SCR_NET 0x00000008
-#define CCSR_SSI_SCR_RE 0x00000004
-#define CCSR_SSI_SCR_TE 0x00000002
-#define CCSR_SSI_SCR_SSIEN 0x00000001
+/* SSI Transmit Data Register 0 */
+#define REG_SSI_STX0 0x00
+/* SSI Transmit Data Register 1 */
+#define REG_SSI_STX1 0x04
+/* SSI Receive Data Register 0 */
+#define REG_SSI_SRX0 0x08
+/* SSI Receive Data Register 1 */
+#define REG_SSI_SRX1 0x0c
+/* SSI Control Register */
+#define REG_SSI_SCR 0x10
+/* SSI Interrupt Status Register */
+#define REG_SSI_SISR 0x14
+/* SSI Interrupt Enable Register */
+#define REG_SSI_SIER 0x18
+/* SSI Transmit Configuration Register */
+#define REG_SSI_STCR 0x1c
+/* SSI Receive Configuration Register */
+#define REG_SSI_SRCR 0x20
+#define REG_SSI_SxCR(tx) ((tx) ? REG_SSI_STCR : REG_SSI_SRCR)
+/* SSI Transmit Clock Control Register */
+#define REG_SSI_STCCR 0x24
+/* SSI Receive Clock Control Register */
+#define REG_SSI_SRCCR 0x28
+#define REG_SSI_SxCCR(tx) ((tx) ? REG_SSI_STCCR : REG_SSI_SRCCR)
+/* SSI FIFO Control/Status Register */
+#define REG_SSI_SFCSR 0x2c
+/*
+ * SSI Test Register (Intended for debugging purposes only)
+ *
+ * Note: STR is not documented in recent IMX datasheet, but
+ * is described in IMX51 reference manual at section 56.3.3.14
+ */
+#define REG_SSI_STR 0x30
+/*
+ * SSI Option Register (Intended for internal use only)
+ *
+ * Note: SOR is not documented in recent IMX datasheet, but
+ * is described in IMX51 reference manual at section 56.3.3.15
+ */
+#define REG_SSI_SOR 0x34
+/* SSI AC97 Control Register */
+#define REG_SSI_SACNT 0x38
+/* SSI AC97 Command Address Register */
+#define REG_SSI_SACADD 0x3c
+/* SSI AC97 Command Data Register */
+#define REG_SSI_SACDAT 0x40
+/* SSI AC97 Tag Register */
+#define REG_SSI_SATAG 0x44
+/* SSI Transmit Time Slot Mask Register */
+#define REG_SSI_STMSK 0x48
+/* SSI Receive Time Slot Mask Register */
+#define REG_SSI_SRMSK 0x4c
+#define REG_SSI_SxMSK(tx) ((tx) ? REG_SSI_STMSK : REG_SSI_SRMSK)
+/*
+ * SSI AC97 Channel Status Register
+ *
+ * The status could be changed by:
+ * 1) Writing a '1' bit at some position in SACCEN sets relevant bit in SACCST
+ * 2) Writing a '1' bit at some position in SACCDIS unsets the relevant bit
+ * 3) Receivng a '1' in SLOTREQ bit from external CODEC via AC Link
+ */
+#define REG_SSI_SACCST 0x50
+/* SSI AC97 Channel Enable Register -- Set bits in SACCST */
+#define REG_SSI_SACCEN 0x54
+/* SSI AC97 Channel Disable Register -- Clear bits in SACCST */
+#define REG_SSI_SACCDIS 0x58
-#define CCSR_SSI_SISR_RFRC 0x01000000
-#define CCSR_SSI_SISR_TFRC 0x00800000
-#define CCSR_SSI_SISR_CMDAU 0x00040000
-#define CCSR_SSI_SISR_CMDDU 0x00020000
-#define CCSR_SSI_SISR_RXT 0x00010000
-#define CCSR_SSI_SISR_RDR1 0x00008000
-#define CCSR_SSI_SISR_RDR0 0x00004000
-#define CCSR_SSI_SISR_TDE1 0x00002000
-#define CCSR_SSI_SISR_TDE0 0x00001000
-#define CCSR_SSI_SISR_ROE1 0x00000800
-#define CCSR_SSI_SISR_ROE0 0x00000400
-#define CCSR_SSI_SISR_TUE1 0x00000200
-#define CCSR_SSI_SISR_TUE0 0x00000100
-#define CCSR_SSI_SISR_TFS 0x00000080
-#define CCSR_SSI_SISR_RFS 0x00000040
-#define CCSR_SSI_SISR_TLS 0x00000020
-#define CCSR_SSI_SISR_RLS 0x00000010
-#define CCSR_SSI_SISR_RFF1 0x00000008
-#define CCSR_SSI_SISR_RFF0 0x00000004
-#define CCSR_SSI_SISR_TFE1 0x00000002
-#define CCSR_SSI_SISR_TFE0 0x00000001
+/* -- SSI Register Field Maps -- */
-#define CCSR_SSI_SIER_RFRC_EN 0x01000000
-#define CCSR_SSI_SIER_TFRC_EN 0x00800000
-#define CCSR_SSI_SIER_RDMAE 0x00400000
-#define CCSR_SSI_SIER_RIE 0x00200000
-#define CCSR_SSI_SIER_TDMAE 0x00100000
-#define CCSR_SSI_SIER_TIE 0x00080000
-#define CCSR_SSI_SIER_CMDAU_EN 0x00040000
-#define CCSR_SSI_SIER_CMDDU_EN 0x00020000
-#define CCSR_SSI_SIER_RXT_EN 0x00010000
-#define CCSR_SSI_SIER_RDR1_EN 0x00008000
-#define CCSR_SSI_SIER_RDR0_EN 0x00004000
-#define CCSR_SSI_SIER_TDE1_EN 0x00002000
-#define CCSR_SSI_SIER_TDE0_EN 0x00001000
-#define CCSR_SSI_SIER_ROE1_EN 0x00000800
-#define CCSR_SSI_SIER_ROE0_EN 0x00000400
-#define CCSR_SSI_SIER_TUE1_EN 0x00000200
-#define CCSR_SSI_SIER_TUE0_EN 0x00000100
-#define CCSR_SSI_SIER_TFS_EN 0x00000080
-#define CCSR_SSI_SIER_RFS_EN 0x00000040
-#define CCSR_SSI_SIER_TLS_EN 0x00000020
-#define CCSR_SSI_SIER_RLS_EN 0x00000010
-#define CCSR_SSI_SIER_RFF1_EN 0x00000008
-#define CCSR_SSI_SIER_RFF0_EN 0x00000004
-#define CCSR_SSI_SIER_TFE1_EN 0x00000002
-#define CCSR_SSI_SIER_TFE0_EN 0x00000001
+/* SSI Control Register -- REG_SSI_SCR 0x10 */
+#define SSI_SCR_SYNC_TX_FS 0x00001000
+#define SSI_SCR_RFR_CLK_DIS 0x00000800
+#define SSI_SCR_TFR_CLK_DIS 0x00000400
+#define SSI_SCR_TCH_EN 0x00000100
+#define SSI_SCR_SYS_CLK_EN 0x00000080
+#define SSI_SCR_I2S_MODE_MASK 0x00000060
+#define SSI_SCR_I2S_MODE_NORMAL 0x00000000
+#define SSI_SCR_I2S_MODE_MASTER 0x00000020
+#define SSI_SCR_I2S_MODE_SLAVE 0x00000040
+#define SSI_SCR_SYN 0x00000010
+#define SSI_SCR_NET 0x00000008
+#define SSI_SCR_I2S_NET_MASK (SSI_SCR_NET | SSI_SCR_I2S_MODE_MASK)
+#define SSI_SCR_RE 0x00000004
+#define SSI_SCR_TE 0x00000002
+#define SSI_SCR_SSIEN 0x00000001
-#define CCSR_SSI_STCR_TXBIT0 0x00000200
-#define CCSR_SSI_STCR_TFEN1 0x00000100
-#define CCSR_SSI_STCR_TFEN0 0x00000080
-#define CCSR_SSI_STCR_TFDIR 0x00000040
-#define CCSR_SSI_STCR_TXDIR 0x00000020
-#define CCSR_SSI_STCR_TSHFD 0x00000010
-#define CCSR_SSI_STCR_TSCKP 0x00000008
-#define CCSR_SSI_STCR_TFSI 0x00000004
-#define CCSR_SSI_STCR_TFSL 0x00000002
-#define CCSR_SSI_STCR_TEFS 0x00000001
+/* SSI Interrupt Status Register -- REG_SSI_SISR 0x14 */
+#define SSI_SISR_RFRC 0x01000000
+#define SSI_SISR_TFRC 0x00800000
+#define SSI_SISR_CMDAU 0x00040000
+#define SSI_SISR_CMDDU 0x00020000
+#define SSI_SISR_RXT 0x00010000
+#define SSI_SISR_RDR1 0x00008000
+#define SSI_SISR_RDR0 0x00004000
+#define SSI_SISR_TDE1 0x00002000
+#define SSI_SISR_TDE0 0x00001000
+#define SSI_SISR_ROE1 0x00000800
+#define SSI_SISR_ROE0 0x00000400
+#define SSI_SISR_TUE1 0x00000200
+#define SSI_SISR_TUE0 0x00000100
+#define SSI_SISR_TFS 0x00000080
+#define SSI_SISR_RFS 0x00000040
+#define SSI_SISR_TLS 0x00000020
+#define SSI_SISR_RLS 0x00000010
+#define SSI_SISR_RFF1 0x00000008
+#define SSI_SISR_RFF0 0x00000004
+#define SSI_SISR_TFE1 0x00000002
+#define SSI_SISR_TFE0 0x00000001
-#define CCSR_SSI_SRCR_RXEXT 0x00000400
-#define CCSR_SSI_SRCR_RXBIT0 0x00000200
-#define CCSR_SSI_SRCR_RFEN1 0x00000100
-#define CCSR_SSI_SRCR_RFEN0 0x00000080
-#define CCSR_SSI_SRCR_RFDIR 0x00000040
-#define CCSR_SSI_SRCR_RXDIR 0x00000020
-#define CCSR_SSI_SRCR_RSHFD 0x00000010
-#define CCSR_SSI_SRCR_RSCKP 0x00000008
-#define CCSR_SSI_SRCR_RFSI 0x00000004
-#define CCSR_SSI_SRCR_RFSL 0x00000002
-#define CCSR_SSI_SRCR_REFS 0x00000001
+/* SSI Interrupt Enable Register -- REG_SSI_SIER 0x18 */
+#define SSI_SIER_RFRC_EN 0x01000000
+#define SSI_SIER_TFRC_EN 0x00800000
+#define SSI_SIER_RDMAE 0x00400000
+#define SSI_SIER_RIE 0x00200000
+#define SSI_SIER_TDMAE 0x00100000
+#define SSI_SIER_TIE 0x00080000
+#define SSI_SIER_CMDAU_EN 0x00040000
+#define SSI_SIER_CMDDU_EN 0x00020000
+#define SSI_SIER_RXT_EN 0x00010000
+#define SSI_SIER_RDR1_EN 0x00008000
+#define SSI_SIER_RDR0_EN 0x00004000
+#define SSI_SIER_TDE1_EN 0x00002000
+#define SSI_SIER_TDE0_EN 0x00001000
+#define SSI_SIER_ROE1_EN 0x00000800
+#define SSI_SIER_ROE0_EN 0x00000400
+#define SSI_SIER_TUE1_EN 0x00000200
+#define SSI_SIER_TUE0_EN 0x00000100
+#define SSI_SIER_TFS_EN 0x00000080
+#define SSI_SIER_RFS_EN 0x00000040
+#define SSI_SIER_TLS_EN 0x00000020
+#define SSI_SIER_RLS_EN 0x00000010
+#define SSI_SIER_RFF1_EN 0x00000008
+#define SSI_SIER_RFF0_EN 0x00000004
+#define SSI_SIER_TFE1_EN 0x00000002
+#define SSI_SIER_TFE0_EN 0x00000001
-/* STCCR and SRCCR */
-#define CCSR_SSI_SxCCR_DIV2_SHIFT 18
-#define CCSR_SSI_SxCCR_DIV2 0x00040000
-#define CCSR_SSI_SxCCR_PSR_SHIFT 17
-#define CCSR_SSI_SxCCR_PSR 0x00020000
-#define CCSR_SSI_SxCCR_WL_SHIFT 13
-#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000
-#define CCSR_SSI_SxCCR_WL(x) \
- (((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK)
-#define CCSR_SSI_SxCCR_DC_SHIFT 8
-#define CCSR_SSI_SxCCR_DC_MASK 0x00001F00
-#define CCSR_SSI_SxCCR_DC(x) \
- ((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK)
-#define CCSR_SSI_SxCCR_PM_SHIFT 0
-#define CCSR_SSI_SxCCR_PM_MASK 0x000000FF
-#define CCSR_SSI_SxCCR_PM(x) \
- ((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK)
+/* SSI Transmit Configuration Register -- REG_SSI_STCR 0x1C */
+#define SSI_STCR_TXBIT0 0x00000200
+#define SSI_STCR_TFEN1 0x00000100
+#define SSI_STCR_TFEN0 0x00000080
+#define SSI_STCR_TFDIR 0x00000040
+#define SSI_STCR_TXDIR 0x00000020
+#define SSI_STCR_TSHFD 0x00000010
+#define SSI_STCR_TSCKP 0x00000008
+#define SSI_STCR_TFSI 0x00000004
+#define SSI_STCR_TFSL 0x00000002
+#define SSI_STCR_TEFS 0x00000001
+
+/* SSI Receive Configuration Register -- REG_SSI_SRCR 0x20 */
+#define SSI_SRCR_RXEXT 0x00000400
+#define SSI_SRCR_RXBIT0 0x00000200
+#define SSI_SRCR_RFEN1 0x00000100
+#define SSI_SRCR_RFEN0 0x00000080
+#define SSI_SRCR_RFDIR 0x00000040
+#define SSI_SRCR_RXDIR 0x00000020
+#define SSI_SRCR_RSHFD 0x00000010
+#define SSI_SRCR_RSCKP 0x00000008
+#define SSI_SRCR_RFSI 0x00000004
+#define SSI_SRCR_RFSL 0x00000002
+#define SSI_SRCR_REFS 0x00000001
/*
- * The xFCNT bits are read-only, and the xFWM bits are read/write. Use the
- * CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the
- * CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks.
+ * SSI Transmit Clock Control Register -- REG_SSI_STCCR 0x24
+ * SSI Receive Clock Control Register -- REG_SSI_SRCCR 0x28
+ */
+#define SSI_SxCCR_DIV2_SHIFT 18
+#define SSI_SxCCR_DIV2 0x00040000
+#define SSI_SxCCR_PSR_SHIFT 17
+#define SSI_SxCCR_PSR 0x00020000
+#define SSI_SxCCR_WL_SHIFT 13
+#define SSI_SxCCR_WL_MASK 0x0001E000
+#define SSI_SxCCR_WL(x) \
+ (((((x) / 2) - 1) << SSI_SxCCR_WL_SHIFT) & SSI_SxCCR_WL_MASK)
+#define SSI_SxCCR_DC_SHIFT 8
+#define SSI_SxCCR_DC_MASK 0x00001F00
+#define SSI_SxCCR_DC(x) \
+ ((((x) - 1) << SSI_SxCCR_DC_SHIFT) & SSI_SxCCR_DC_MASK)
+#define SSI_SxCCR_PM_SHIFT 0
+#define SSI_SxCCR_PM_MASK 0x000000FF
+#define SSI_SxCCR_PM(x) \
+ ((((x) - 1) << SSI_SxCCR_PM_SHIFT) & SSI_SxCCR_PM_MASK)
+
+/*
+ * SSI FIFO Control/Status Register -- REG_SSI_SFCSR 0x2c
+ *
+ * Tx or Rx FIFO Counter -- SSI_SFCSR_xFCNTy Read-Only
+ * Tx or Rx FIFO Watermarks -- SSI_SFCSR_xFWMy Read/Write
*/
-#define CCSR_SSI_SFCSR_RFCNT1_SHIFT 28
-#define CCSR_SSI_SFCSR_RFCNT1_MASK 0xF0000000
-#define CCSR_SSI_SFCSR_RFCNT1(x) \
- (((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT)
-#define CCSR_SSI_SFCSR_TFCNT1_SHIFT 24
-#define CCSR_SSI_SFCSR_TFCNT1_MASK 0x0F000000
-#define CCSR_SSI_SFCSR_TFCNT1(x) \
- (((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT)
-#define CCSR_SSI_SFCSR_RFWM1_SHIFT 20
-#define CCSR_SSI_SFCSR_RFWM1_MASK 0x00F00000
-#define CCSR_SSI_SFCSR_RFWM1(x) \
- (((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK)
-#define CCSR_SSI_SFCSR_TFWM1_SHIFT 16
-#define CCSR_SSI_SFCSR_TFWM1_MASK 0x000F0000
-#define CCSR_SSI_SFCSR_TFWM1(x) \
- (((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK)
-#define CCSR_SSI_SFCSR_RFCNT0_SHIFT 12
-#define CCSR_SSI_SFCSR_RFCNT0_MASK 0x0000F000
-#define CCSR_SSI_SFCSR_RFCNT0(x) \
- (((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT)
-#define CCSR_SSI_SFCSR_TFCNT0_SHIFT 8
-#define CCSR_SSI_SFCSR_TFCNT0_MASK 0x00000F00
-#define CCSR_SSI_SFCSR_TFCNT0(x) \
- (((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT)
-#define CCSR_SSI_SFCSR_RFWM0_SHIFT 4
-#define CCSR_SSI_SFCSR_RFWM0_MASK 0x000000F0
-#define CCSR_SSI_SFCSR_RFWM0(x) \
- (((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK)
-#define CCSR_SSI_SFCSR_TFWM0_SHIFT 0
-#define CCSR_SSI_SFCSR_TFWM0_MASK 0x0000000F
-#define CCSR_SSI_SFCSR_TFWM0(x) \
- (((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK)
+#define SSI_SFCSR_RFCNT1_SHIFT 28
+#define SSI_SFCSR_RFCNT1_MASK 0xF0000000
+#define SSI_SFCSR_RFCNT1(x) \
+ (((x) & SSI_SFCSR_RFCNT1_MASK) >> SSI_SFCSR_RFCNT1_SHIFT)
+#define SSI_SFCSR_TFCNT1_SHIFT 24
+#define SSI_SFCSR_TFCNT1_MASK 0x0F000000
+#define SSI_SFCSR_TFCNT1(x) \
+ (((x) & SSI_SFCSR_TFCNT1_MASK) >> SSI_SFCSR_TFCNT1_SHIFT)
+#define SSI_SFCSR_RFWM1_SHIFT 20
+#define SSI_SFCSR_RFWM1_MASK 0x00F00000
+#define SSI_SFCSR_RFWM1(x) \
+ (((x) << SSI_SFCSR_RFWM1_SHIFT) & SSI_SFCSR_RFWM1_MASK)
+#define SSI_SFCSR_TFWM1_SHIFT 16
+#define SSI_SFCSR_TFWM1_MASK 0x000F0000
+#define SSI_SFCSR_TFWM1(x) \
+ (((x) << SSI_SFCSR_TFWM1_SHIFT) & SSI_SFCSR_TFWM1_MASK)
+#define SSI_SFCSR_RFCNT0_SHIFT 12
+#define SSI_SFCSR_RFCNT0_MASK 0x0000F000
+#define SSI_SFCSR_RFCNT0(x) \
+ (((x) & SSI_SFCSR_RFCNT0_MASK) >> SSI_SFCSR_RFCNT0_SHIFT)
+#define SSI_SFCSR_TFCNT0_SHIFT 8
+#define SSI_SFCSR_TFCNT0_MASK 0x00000F00
+#define SSI_SFCSR_TFCNT0(x) \
+ (((x) & SSI_SFCSR_TFCNT0_MASK) >> SSI_SFCSR_TFCNT0_SHIFT)
+#define SSI_SFCSR_RFWM0_SHIFT 4
+#define SSI_SFCSR_RFWM0_MASK 0x000000F0
+#define SSI_SFCSR_RFWM0(x) \
+ (((x) << SSI_SFCSR_RFWM0_SHIFT) & SSI_SFCSR_RFWM0_MASK)
+#define SSI_SFCSR_TFWM0_SHIFT 0
+#define SSI_SFCSR_TFWM0_MASK 0x0000000F
+#define SSI_SFCSR_TFWM0(x) \
+ (((x) << SSI_SFCSR_TFWM0_SHIFT) & SSI_SFCSR_TFWM0_MASK)
-#define CCSR_SSI_STR_TEST 0x00008000
-#define CCSR_SSI_STR_RCK2TCK 0x00004000
-#define CCSR_SSI_STR_RFS2TFS 0x00002000
-#define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F)
-#define CCSR_SSI_STR_TXD2RXD 0x00000080
-#define CCSR_SSI_STR_TCK2RCK 0x00000040
-#define CCSR_SSI_STR_TFS2RFS 0x00000020
-#define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F)
+/* SSI Test Register -- REG_SSI_STR 0x30 */
+#define SSI_STR_TEST 0x00008000
+#define SSI_STR_RCK2TCK 0x00004000
+#define SSI_STR_RFS2TFS 0x00002000
+#define SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F)
+#define SSI_STR_TXD2RXD 0x00000080
+#define SSI_STR_TCK2RCK 0x00000040
+#define SSI_STR_TFS2RFS 0x00000020
+#define SSI_STR_TXSTATE(x) ((x) & 0x1F)
-#define CCSR_SSI_SOR_CLKOFF 0x00000040
-#define CCSR_SSI_SOR_RX_CLR 0x00000020
-#define CCSR_SSI_SOR_TX_CLR 0x00000010
-#define CCSR_SSI_SOR_INIT 0x00000008
-#define CCSR_SSI_SOR_WAIT_SHIFT 1
-#define CCSR_SSI_SOR_WAIT_MASK 0x00000006
-#define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT)
-#define CCSR_SSI_SOR_SYNRST 0x00000001
+/* SSI Option Register -- REG_SSI_SOR 0x34 */
+#define SSI_SOR_CLKOFF 0x00000040
+#define SSI_SOR_RX_CLR 0x00000020
+#define SSI_SOR_TX_CLR 0x00000010
+#define SSI_SOR_xX_CLR(tx) ((tx) ? SSI_SOR_TX_CLR : SSI_SOR_RX_CLR)
+#define SSI_SOR_INIT 0x00000008
+#define SSI_SOR_WAIT_SHIFT 1
+#define SSI_SOR_WAIT_MASK 0x00000006
+#define SSI_SOR_WAIT(x) (((x) & 3) << SSI_SOR_WAIT_SHIFT)
+#define SSI_SOR_SYNRST 0x00000001
-#define CCSR_SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
-#define CCSR_SSI_SACNT_WR 0x00000010
-#define CCSR_SSI_SACNT_RD 0x00000008
-#define CCSR_SSI_SACNT_RDWR_MASK 0x00000018
-#define CCSR_SSI_SACNT_TIF 0x00000004
-#define CCSR_SSI_SACNT_FV 0x00000002
-#define CCSR_SSI_SACNT_AC97EN 0x00000001
+/* SSI AC97 Control Register -- REG_SSI_SACNT 0x38 */
+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
+#define SSI_SACNT_WR 0x00000010
+#define SSI_SACNT_RD 0x00000008
+#define SSI_SACNT_RDWR_MASK 0x00000018
+#define SSI_SACNT_TIF 0x00000004
+#define SSI_SACNT_FV 0x00000002
+#define SSI_SACNT_AC97EN 0x00000001
struct device;
@@ -212,7 +270,6 @@ struct device;
struct fsl_ssi_dbg {
struct dentry *dbg_dir;
- struct dentry *dbg_stats;
struct {
unsigned int rfrc;
@@ -241,7 +298,7 @@ struct fsl_ssi_dbg {
void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *ssi_dbg, u32 sisr);
-int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev);
+void fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev);
void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg);
@@ -254,10 +311,9 @@ static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr)
{
}
-static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
- struct device *dev)
+static inline void fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
+ struct device *dev)
{
- return 0;
}
static inline void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c
index 5469ffbc0253..2c46c55f0a88 100644
--- a/sound/soc/fsl/fsl_ssi_dbg.c
+++ b/sound/soc/fsl/fsl_ssi_dbg.c
@@ -1,14 +1,10 @@
-/*
- * Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions
- *
- * Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
- *
- * Splitted from fsl_ssi.c
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions
+//
+// Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
+//
+// Split from fsl_ssi.c
#include <linux/debugfs.h>
#include <linux/device.h>
@@ -18,86 +14,86 @@
void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr)
{
- if (sisr & CCSR_SSI_SISR_RFRC)
+ if (sisr & SSI_SISR_RFRC)
dbg->stats.rfrc++;
- if (sisr & CCSR_SSI_SISR_TFRC)
+ if (sisr & SSI_SISR_TFRC)
dbg->stats.tfrc++;
- if (sisr & CCSR_SSI_SISR_CMDAU)
+ if (sisr & SSI_SISR_CMDAU)
dbg->stats.cmdau++;
- if (sisr & CCSR_SSI_SISR_CMDDU)
+ if (sisr & SSI_SISR_CMDDU)
dbg->stats.cmddu++;
- if (sisr & CCSR_SSI_SISR_RXT)
+ if (sisr & SSI_SISR_RXT)
dbg->stats.rxt++;
- if (sisr & CCSR_SSI_SISR_RDR1)
+ if (sisr & SSI_SISR_RDR1)
dbg->stats.rdr1++;
- if (sisr & CCSR_SSI_SISR_RDR0)
+ if (sisr & SSI_SISR_RDR0)
dbg->stats.rdr0++;
- if (sisr & CCSR_SSI_SISR_TDE1)
+ if (sisr & SSI_SISR_TDE1)
dbg->stats.tde1++;
- if (sisr & CCSR_SSI_SISR_TDE0)
+ if (sisr & SSI_SISR_TDE0)
dbg->stats.tde0++;
- if (sisr & CCSR_SSI_SISR_ROE1)
+ if (sisr & SSI_SISR_ROE1)
dbg->stats.roe1++;
- if (sisr & CCSR_SSI_SISR_ROE0)
+ if (sisr & SSI_SISR_ROE0)
dbg->stats.roe0++;
- if (sisr & CCSR_SSI_SISR_TUE1)
+ if (sisr & SSI_SISR_TUE1)
dbg->stats.tue1++;
- if (sisr & CCSR_SSI_SISR_TUE0)
+ if (sisr & SSI_SISR_TUE0)
dbg->stats.tue0++;
- if (sisr & CCSR_SSI_SISR_TFS)
+ if (sisr & SSI_SISR_TFS)
dbg->stats.tfs++;
- if (sisr & CCSR_SSI_SISR_RFS)
+ if (sisr & SSI_SISR_RFS)
dbg->stats.rfs++;
- if (sisr & CCSR_SSI_SISR_TLS)
+ if (sisr & SSI_SISR_TLS)
dbg->stats.tls++;
- if (sisr & CCSR_SSI_SISR_RLS)
+ if (sisr & SSI_SISR_RLS)
dbg->stats.rls++;
- if (sisr & CCSR_SSI_SISR_RFF1)
+ if (sisr & SSI_SISR_RFF1)
dbg->stats.rff1++;
- if (sisr & CCSR_SSI_SISR_RFF0)
+ if (sisr & SSI_SISR_RFF0)
dbg->stats.rff0++;
- if (sisr & CCSR_SSI_SISR_TFE1)
+ if (sisr & SSI_SISR_TFE1)
dbg->stats.tfe1++;
- if (sisr & CCSR_SSI_SISR_TFE0)
+ if (sisr & SSI_SISR_TFE0)
dbg->stats.tfe0++;
}
-/* Show the statistics of a flag only if its interrupt is enabled. The
- * compiler will optimze this code to a no-op if the interrupt is not
- * enabled.
+/*
+ * Show the statistics of a flag only if its interrupt is enabled
+ *
+ * Compilers will optimize it to a no-op if the interrupt is disabled
*/
#define SIER_SHOW(flag, name) \
do { \
- if (CCSR_SSI_SIER_##flag) \
+ if (SSI_SIER_##flag) \
seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \
} while (0)
-/**
- * fsl_sysfs_ssi_show: display SSI statistics
+/*
+ * Display the statistics for the current SSI device
*
- * Display the statistics for the current SSI device. To avoid confusion,
- * we only show those counts that are enabled.
+ * To avoid confusion, only show those counts that are enabled
*/
static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
{
@@ -128,36 +124,17 @@ static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
return 0;
}
-static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fsl_ssi_stats_show, inode->i_private);
-}
-
-static const struct file_operations fsl_ssi_stats_ops = {
- .open = fsl_ssi_stats_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fsl_ssi_stats);
-int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
+void fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
{
ssi_dbg->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
- if (!ssi_dbg->dbg_dir)
- return -ENOMEM;
-
- ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
- ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops);
- if (!ssi_dbg->dbg_stats) {
- debugfs_remove(ssi_dbg->dbg_dir);
- return -ENOMEM;
- }
- return 0;
+ debugfs_create_file("stats", 0444, ssi_dbg->dbg_dir, ssi_dbg,
+ &fsl_ssi_stats_fops);
}
void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
{
- debugfs_remove(ssi_dbg->dbg_stats);
- debugfs_remove(ssi_dbg->dbg_dir);
+ debugfs_remove_recursive(ssi_dbg->dbg_dir);
}
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
index b9e42b503a37..d69a6b9795bf 100644
--- a/sound/soc/fsl/fsl_utils.c
+++ b/sound/soc/fsl/fsl_utils.c
@@ -1,15 +1,13 @@
-/**
- * Freescale ALSA SoC Machine driver utility
- *
- * Author: Timur Tabi <timur@freescale.com>
- *
- * Copyright 2010 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale ALSA SoC Machine driver utility
+//
+// Author: Timur Tabi <timur@freescale.com>
+//
+// Copyright 2010 Freescale Semiconductor, Inc.
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <sound/soc.h>
@@ -36,7 +34,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
{
struct resource res;
struct device_node *dma_channel_np, *dma_np;
- const u32 *iprop;
+ const __be32 *iprop;
int ret;
dma_channel_np = of_parse_phandle(ssi_np, name, 0);
@@ -61,8 +59,8 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
of_node_put(dma_channel_np);
return ret;
}
- snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
- (unsigned long long) res.start, dma_channel_np->name);
+ snprintf((char *)dai->platforms->name, DAI_NAME_SIZE, "%llx.%pOFn",
+ (unsigned long long) res.start, dma_channel_np);
iprop = of_get_property(dma_channel_np, "cell-index", NULL);
if (!iprop) {
@@ -75,6 +73,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
iprop = of_get_property(dma_np, "cell-index", NULL);
if (!iprop) {
of_node_put(dma_np);
+ of_node_put(dma_channel_np);
return -EINVAL;
}
*dma_id = be32_to_cpup(iprop);
@@ -86,6 +85,118 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
}
EXPORT_SYMBOL(fsl_asoc_get_dma_channel);
+/**
+ * fsl_asoc_get_pll_clocks - get two PLL clock source
+ *
+ * @dev: device pointer
+ * @pll8k_clk: PLL clock pointer for 8kHz
+ * @pll11k_clk: PLL clock pointer for 11kHz
+ *
+ * This function get two PLL clock source
+ */
+void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk,
+ struct clk **pll11k_clk)
+{
+ *pll8k_clk = devm_clk_get(dev, "pll8k");
+ if (IS_ERR(*pll8k_clk))
+ *pll8k_clk = NULL;
+
+ *pll11k_clk = devm_clk_get(dev, "pll11k");
+ if (IS_ERR(*pll11k_clk))
+ *pll11k_clk = NULL;
+}
+EXPORT_SYMBOL(fsl_asoc_get_pll_clocks);
+
+/**
+ * fsl_asoc_reparent_pll_clocks - set clock parent if necessary
+ *
+ * @dev: device pointer
+ * @clk: root clock pointer
+ * @pll8k_clk: PLL clock pointer for 8kHz
+ * @pll11k_clk: PLL clock pointer for 11kHz
+ * @ratio: target requency for root clock
+ *
+ * This function set root clock parent according to the target ratio
+ */
+void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
+ struct clk *pll8k_clk,
+ struct clk *pll11k_clk, u64 ratio)
+{
+ struct clk *p, *pll = NULL, *npll = NULL;
+ bool reparent = false;
+ int ret;
+
+ if (!clk || !pll8k_clk || !pll11k_clk)
+ return;
+
+ p = clk;
+ while (p && pll8k_clk && pll11k_clk) {
+ struct clk *pp = clk_get_parent(p);
+
+ if (clk_is_match(pp, pll8k_clk) ||
+ clk_is_match(pp, pll11k_clk)) {
+ pll = pp;
+ break;
+ }
+ p = pp;
+ }
+
+ npll = (do_div(ratio, 8000) ? pll11k_clk : pll8k_clk);
+ reparent = (pll && !clk_is_match(pll, npll));
+
+ if (reparent) {
+ ret = clk_set_parent(p, npll);
+ if (ret < 0)
+ dev_warn(dev, "failed to set parent:%d\n", ret);
+ }
+}
+EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
+
+/**
+ * fsl_asoc_constrain_rates - constrain rates according to clocks
+ *
+ * @target_constr: target constraint
+ * @original_constr: original constraint
+ * @pll8k_clk: PLL clock pointer for 8kHz
+ * @pll11k_clk: PLL clock pointer for 11kHz
+ * @ext_clk: External clock pointer
+ * @target_rates: target rates array
+ *
+ * This function constrain rates according to clocks
+ */
+void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
+ const struct snd_pcm_hw_constraint_list *original_constr,
+ struct clk *pll8k_clk, struct clk *pll11k_clk,
+ struct clk *ext_clk, int *target_rates)
+{
+ int i, j, k = 0;
+ u64 clk_rate[3];
+
+ *target_constr = *original_constr;
+ if (pll8k_clk || pll11k_clk || ext_clk) {
+ target_constr->list = target_rates;
+ target_constr->count = 0;
+ for (i = 0; i < original_constr->count; i++) {
+ clk_rate[0] = clk_get_rate(pll8k_clk);
+ clk_rate[1] = clk_get_rate(pll11k_clk);
+ clk_rate[2] = clk_get_rate(ext_clk);
+ for (j = 0; j < 3; j++) {
+ if (clk_rate[j] != 0 &&
+ do_div(clk_rate[j], original_constr->list[i]) == 0) {
+ target_rates[k++] = original_constr->list[i];
+ target_constr->count++;
+ break;
+ }
+ }
+ }
+
+ /* protection for if there is no proper rate found*/
+ if (!target_constr->count)
+ *target_constr = *original_constr;
+ }
+}
+EXPORT_SYMBOL(fsl_asoc_constrain_rates);
+
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale ASoC utility code");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h
index 1687b66ef18e..21b25a11ecda 100644
--- a/sound/soc/fsl/fsl_utils.h
+++ b/sound/soc/fsl/fsl_utils.h
@@ -1,13 +1,10 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
* Freescale ALSA SoC Machine driver utility
*
* Author: Timur Tabi <timur@freescale.com>
*
* Copyright 2010 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.
*/
#ifndef _FSL_UTILS_H
@@ -22,4 +19,16 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name,
struct snd_soc_dai_link *dai,
unsigned int *dma_channel_id,
unsigned int *dma_id);
+
+void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk,
+ struct clk **pll11k_clk);
+
+void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
+ struct clk *pll8k_clk,
+ struct clk *pll11k_clk, u64 ratio);
+
+void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
+ const struct snd_pcm_hw_constraint_list *original_constr,
+ struct clk *pll8k_clk, struct clk *pll11k_clk,
+ struct clk *ext_clk, int *target_rates);
#endif /* _FSL_UTILS_H */
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
new file mode 100644
index 000000000000..06434b2c9a0f
--- /dev/null
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -0,0 +1,1843 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2019 NXP
+
+#include <linux/bitrev.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_iec958.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_xcvr.h"
+#include "fsl_utils.h"
+#include "imx-pcm.h"
+
+#define FSL_XCVR_CAPDS_SIZE 256
+#define SPDIF_NUM_RATES 7
+
+enum fsl_xcvr_pll_verison {
+ PLL_MX8MP,
+ PLL_MX95,
+};
+
+struct fsl_xcvr_soc_data {
+ const char *fw_name;
+ bool spdif_only;
+ bool use_edma;
+ bool use_phy;
+ enum fsl_xcvr_pll_verison pll_ver;
+};
+
+struct fsl_xcvr {
+ const struct fsl_xcvr_soc_data *soc_data;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct regmap *regmap_phy;
+ struct regmap *regmap_pll;
+ struct clk *ipg_clk;
+ struct clk *pll_ipg_clk;
+ struct clk *phy_clk;
+ struct clk *spba_clk;
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
+ struct reset_control *reset;
+ u8 streams;
+ u32 mode;
+ u32 arc_mode;
+ void __iomem *ram_addr;
+ struct snd_dmaengine_dai_dma_data dma_prms_rx;
+ struct snd_dmaengine_dai_dma_data dma_prms_tx;
+ struct snd_aes_iec958 rx_iec958;
+ struct snd_aes_iec958 tx_iec958;
+ u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
+ struct work_struct work_rst;
+ spinlock_t lock; /* Protect hw_reset and trigger */
+ struct snd_pcm_hw_constraint_list spdif_constr_rates;
+ u32 spdif_constr_rates_list[SPDIF_NUM_RATES];
+};
+
+static const struct fsl_xcvr_pll_conf {
+ u8 mfi; /* min=0x18, max=0x38 */
+ u32 mfn; /* signed int, 2's compl., min=0x3FFF0000, max=0x00010000 */
+ u32 mfd; /* unsigned int */
+ u32 fout; /* Fout = Fref*(MFI + MFN/MFD), Fref is 24MHz */
+} fsl_xcvr_pll_cfg[] = {
+ { .mfi = 54, .mfn = 1, .mfd = 6, .fout = 1300000000, }, /* 1.3 GHz */
+ { .mfi = 32, .mfn = 96, .mfd = 125, .fout = 786432000, }, /* 8000 Hz */
+ { .mfi = 30, .mfn = 66, .mfd = 625, .fout = 722534400, }, /* 11025 Hz */
+ { .mfi = 29, .mfn = 1, .mfd = 6, .fout = 700000000, }, /* 700 MHz */
+};
+
+/*
+ * HDMI2.1 spec defines 6- and 12-channels layout for one bit audio
+ * stream. Todo: to check how this case can be considered below
+ */
+static const u32 fsl_xcvr_earc_channels[] = { 1, 2, 8, 16, 32, };
+static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_channels_constr = {
+ .count = ARRAY_SIZE(fsl_xcvr_earc_channels),
+ .list = fsl_xcvr_earc_channels,
+};
+
+static const u32 fsl_xcvr_earc_rates[] = {
+ 32000, 44100, 48000, 64000, 88200, 96000,
+ 128000, 176400, 192000, 256000, 352800, 384000,
+ 512000, 705600, 768000, 1024000, 1411200, 1536000,
+};
+static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_rates_constr = {
+ .count = ARRAY_SIZE(fsl_xcvr_earc_rates),
+ .list = fsl_xcvr_earc_rates,
+};
+
+static const u32 fsl_xcvr_spdif_channels[] = { 2, };
+static const struct snd_pcm_hw_constraint_list fsl_xcvr_spdif_channels_constr = {
+ .count = ARRAY_SIZE(fsl_xcvr_spdif_channels),
+ .list = fsl_xcvr_spdif_channels,
+};
+
+static const u32 fsl_xcvr_spdif_rates[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000,
+};
+static const struct snd_pcm_hw_constraint_list fsl_xcvr_spdif_rates_constr = {
+ .count = ARRAY_SIZE(fsl_xcvr_spdif_rates),
+ .list = fsl_xcvr_spdif_rates,
+};
+
+static int fsl_xcvr_arc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+
+ xcvr->arc_mode = snd_soc_enum_item_to_val(e, item[0]);
+
+ return 0;
+}
+
+static int fsl_xcvr_arc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ ucontrol->value.enumerated.item[0] = xcvr->arc_mode;
+
+ return 0;
+}
+
+static const u32 fsl_xcvr_phy_arc_cfg[] = {
+ FSL_XCVR_PHY_CTRL_ARC_MODE_SE_EN, FSL_XCVR_PHY_CTRL_ARC_MODE_CM_EN,
+};
+
+static const char * const fsl_xcvr_arc_mode[] = { "Single Ended", "Common", };
+static const struct soc_enum fsl_xcvr_arc_mode_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fsl_xcvr_arc_mode), fsl_xcvr_arc_mode);
+static struct snd_kcontrol_new fsl_xcvr_arc_mode_kctl =
+ SOC_ENUM_EXT("ARC Mode", fsl_xcvr_arc_mode_enum,
+ fsl_xcvr_arc_mode_get, fsl_xcvr_arc_mode_put);
+
+/* Capabilities data structure, bytes */
+static int fsl_xcvr_type_capds_bytes_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = FSL_XCVR_CAPDS_SIZE;
+
+ return 0;
+}
+
+static int fsl_xcvr_capds_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ memcpy(ucontrol->value.bytes.data, xcvr->cap_ds, FSL_XCVR_CAPDS_SIZE);
+
+ return 0;
+}
+
+static int fsl_xcvr_capds_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ memcpy(xcvr->cap_ds, ucontrol->value.bytes.data, FSL_XCVR_CAPDS_SIZE);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new fsl_xcvr_earc_capds_kctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capabilities Data Structure",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = fsl_xcvr_type_capds_bytes_info,
+ .get = fsl_xcvr_capds_get,
+ .put = fsl_xcvr_capds_put,
+};
+
+static int fsl_xcvr_activate_ctl(struct snd_soc_dai *dai, const char *name,
+ bool active)
+{
+ struct snd_soc_card *card = dai->component->card;
+ struct snd_kcontrol *kctl;
+ bool enabled;
+
+ lockdep_assert_held(&card->snd_card->controls_rwsem);
+
+ kctl = snd_soc_card_get_kcontrol(card, name);
+ if (kctl == NULL)
+ return -ENOENT;
+
+ enabled = ((kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_WRITE) != 0);
+ if (active == enabled)
+ return 0; /* nothing to do */
+
+ if (active)
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+
+ return 1;
+}
+
+static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ struct snd_soc_card *card = dai->component->card;
+ struct snd_soc_pcm_runtime *rtd;
+
+ xcvr->mode = snd_soc_enum_item_to_val(e, item[0]);
+
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name,
+ (xcvr->mode == FSL_XCVR_MODE_ARC));
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name,
+ (xcvr->mode == FSL_XCVR_MODE_EARC));
+ /* Allow playback for SPDIF only */
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link);
+ rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count =
+ (xcvr->mode == FSL_XCVR_MODE_SPDIF ? 1 : 0);
+ return 0;
+}
+
+static int fsl_xcvr_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ ucontrol->value.enumerated.item[0] = xcvr->mode;
+
+ return 0;
+}
+
+static const char * const fsl_xcvr_mode[] = { "SPDIF", "ARC RX", "eARC", };
+static const struct soc_enum fsl_xcvr_mode_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fsl_xcvr_mode), fsl_xcvr_mode);
+static struct snd_kcontrol_new fsl_xcvr_mode_kctl =
+ SOC_ENUM_EXT("XCVR Mode", fsl_xcvr_mode_enum,
+ fsl_xcvr_mode_get, fsl_xcvr_mode_put);
+
+/** phy: true => phy, false => pll */
+static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ u32 val, idx, tidx;
+ int ret;
+
+ idx = BIT(phy ? 26 : 24);
+ tidx = BIT(phy ? 27 : 25);
+
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_WDATA, data);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
+
+ ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val,
+ (val & idx) == ((val & tidx) >> 1),
+ 10, 10000);
+ if (ret)
+ dev_err(dev, "AI timeout: failed to set %s reg 0x%02x=0x%08x\n",
+ phy ? "PHY" : "PLL", reg, data);
+ return ret;
+}
+
+static int fsl_xcvr_ai_read(struct fsl_xcvr *xcvr, u8 reg, u32 *data, bool phy)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ u32 val, idx, tidx;
+ int ret;
+
+ idx = BIT(phy ? 26 : 24);
+ tidx = BIT(phy ? 27 : 25);
+
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
+
+ ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val,
+ (val & idx) == ((val & tidx) >> 1),
+ 10, 10000);
+ if (ret)
+ dev_err(dev, "AI timeout: failed to read %s reg 0x%02x\n",
+ phy ? "PHY" : "PLL", reg);
+
+ regmap_read(xcvr->regmap, FSL_XCVR_PHY_AI_RDATA, data);
+
+ return ret;
+}
+
+static int fsl_xcvr_phy_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_read(xcvr, reg, val, 1);
+}
+
+static int fsl_xcvr_phy_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_write(xcvr, reg, val, 1);
+}
+
+static int fsl_xcvr_pll_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_read(xcvr, reg, val, 0);
+}
+
+static int fsl_xcvr_pll_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_write(xcvr, reg, val, 0);
+}
+
+static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ u32 i, div = 0, log2, val;
+ int ret;
+
+ if (!xcvr->soc_data->use_phy)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) {
+ if (fsl_xcvr_pll_cfg[i].fout % freq == 0) {
+ div = fsl_xcvr_pll_cfg[i].fout / freq;
+ break;
+ }
+ }
+
+ if (!div || i >= ARRAY_SIZE(fsl_xcvr_pll_cfg))
+ return -EINVAL;
+
+ log2 = ilog2(div);
+
+ /* Release AI interface from reset */
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
+ FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
+ if (ret < 0) {
+ dev_err(dev, "Error while setting IER0: %d\n", ret);
+ return ret;
+ }
+
+ switch (xcvr->soc_data->pll_ver) {
+ case PLL_MX8MP:
+ /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_BANDGAP,
+ FSL_XCVR_PLL_BANDGAP_EN_VBG);
+
+ /* PLL: CTRL0: DIV_INTEGER */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi);
+ /* PLL: NUMERATOR: MFN */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn);
+ /* PLL: DENOMINATOR: MFD */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd);
+ /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP);
+ udelay(25);
+ /* PLL: CTRL0: Clear Hold Ring Off */
+ regmap_clear_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_HROFF);
+ udelay(100);
+ if (tx) { /* TX is enabled for SPDIF only */
+ /* PLL: POSTDIV: PDIV0 */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
+ FSL_XCVR_PLL_PDIVx(log2, 0));
+ /* PLL: CTRL_SET: CLKMUX0_EN */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_CM0_EN);
+ } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */
+ /* PLL: POSTDIV: PDIV1 */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
+ FSL_XCVR_PLL_PDIVx(log2, 1));
+ /* PLL: CTRL_SET: CLKMUX1_EN */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_CM1_EN);
+ } else { /* SPDIF / ARC RX */
+ /* PLL: POSTDIV: PDIV2 */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
+ FSL_XCVR_PLL_PDIVx(log2, 2));
+ /* PLL: CTRL_SET: CLKMUX2_EN */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_CM2_EN);
+ }
+ break;
+ case PLL_MX95:
+ val = fsl_xcvr_pll_cfg[i].mfi << FSL_XCVR_GP_PLL_DIV_MFI_SHIFT | div;
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DIV, val);
+ val = fsl_xcvr_pll_cfg[i].mfn << FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT;
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_NUMERATOR, val);
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DENOMINATOR,
+ fsl_xcvr_pll_cfg[i].mfd);
+ val = FSL_XCVR_GP_PLL_CTRL_POWERUP | FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN;
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_CTRL, val);
+ break;
+ default:
+ dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver);
+ return -EINVAL;
+ }
+
+ if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */
+ /* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_TSDIFF_OE |
+ FSL_XCVR_PHY_CTRL_PHY_EN);
+ /* PHY: CTRL2_SET: EARC_TX_MODE */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2,
+ FSL_XCVR_PHY_CTRL2_EARC_TXMS);
+ } else if (!tx) { /* SPDIF / ARC RX mode */
+ if (xcvr->mode == FSL_XCVR_MODE_SPDIF)
+ /* PHY: CTRL_SET: SPDIF_EN */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_SPDIF_EN);
+ else /* PHY: CTRL_SET: ARC RX setup */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_PHY_EN |
+ FSL_XCVR_PHY_CTRL_RX_CM_EN |
+ fsl_xcvr_phy_arc_cfg[xcvr->arc_mode]);
+ }
+
+ dev_dbg(dev, "PLL Fexp: %u, Fout: %u, mfi: %u, mfn: %u, mfd: %d, div: %u, pdiv0: %u\n",
+ freq, fsl_xcvr_pll_cfg[i].fout, fsl_xcvr_pll_cfg[i].mfi,
+ fsl_xcvr_pll_cfg[i].mfn, fsl_xcvr_pll_cfg[i].mfd, div, log2);
+ return 0;
+}
+
+static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ int ret;
+
+ freq = xcvr->soc_data->spdif_only ? freq / 5 : freq;
+ clk_disable_unprepare(xcvr->phy_clk);
+ fsl_asoc_reparent_pll_clocks(dev, xcvr->phy_clk,
+ xcvr->pll8k_clk, xcvr->pll11k_clk, freq);
+ ret = clk_set_rate(xcvr->phy_clk, freq);
+ if (ret < 0) {
+ dev_err(dev, "Error while setting AUD PLL rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(xcvr->phy_clk);
+ if (ret) {
+ dev_err(dev, "failed to start PHY clock: %d\n", ret);
+ return ret;
+ }
+
+ if (!xcvr->soc_data->use_phy)
+ return 0;
+ /* Release AI interface from reset */
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
+ FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
+ if (ret < 0) {
+ dev_err(dev, "Error while setting IER0: %d\n", ret);
+ return ret;
+ }
+
+ if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */
+ /* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_TSDIFF_OE |
+ FSL_XCVR_PHY_CTRL_PHY_EN);
+ /* PHY: CTRL2_SET: EARC_TX_MODE */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2,
+ FSL_XCVR_PHY_CTRL2_EARC_TXMS);
+ } else { /* SPDIF mode */
+ /* PHY: CTRL_SET: TX_CLK_AUD_SS | SPDIF_EN */
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS |
+ FSL_XCVR_PHY_CTRL_SPDIF_EN);
+ }
+
+ dev_dbg(dev, "PLL Fexp: %u\n", freq);
+
+ return 0;
+}
+
+#define FSL_XCVR_SPDIF_RX_FREQ 175000000
+static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u32 m_ctl = 0, v_ctl = 0;
+ u32 r = substream->runtime->rate, ch = substream->runtime->channels;
+ u32 fout = 32 * r * ch * 10;
+ int ret = 0;
+
+ switch (xcvr->mode) {
+ case FSL_XCVR_MODE_SPDIF:
+ if (xcvr->soc_data->spdif_only && tx) {
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM,
+ FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set bypass fem: %d\n", ret);
+ return ret;
+ }
+ }
+ fallthrough;
+ case FSL_XCVR_MODE_ARC:
+ if (tx) {
+ ret = fsl_xcvr_en_aud_pll(xcvr, fout);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set TX freq %u: %d\n",
+ fout, ret);
+ return ret;
+ }
+
+ ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_FRM_FMT);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set TX_DPTH: %d\n", ret);
+ return ret;
+ }
+
+ /**
+ * set SPDIF MODE - this flag is used to gate
+ * SPDIF output, useless for SPDIF RX
+ */
+ m_ctl |= FSL_XCVR_EXT_CTRL_SPDIF_MODE;
+ v_ctl |= FSL_XCVR_EXT_CTRL_SPDIF_MODE;
+ } else {
+ /**
+ * Clear RX FIFO, flip RX FIFO bits,
+ * disable eARC related HW mode detects
+ */
+ ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
+ FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO |
+ FSL_XCVR_RX_DPTH_CTRL_COMP |
+ FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret);
+ return ret;
+ }
+
+ ret = fsl_xcvr_en_phy_pll(xcvr, FSL_XCVR_SPDIF_RX_FREQ, tx);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set RX freq %u: %d\n",
+ FSL_XCVR_SPDIF_RX_FREQ, ret);
+ return ret;
+ }
+ }
+ break;
+ case FSL_XCVR_MODE_EARC:
+ if (!tx) {
+ /** Clear RX FIFO, flip RX FIFO bits */
+ ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
+ FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret);
+ return ret;
+ }
+
+ /** Enable eARC related HW mode detects */
+ ret = regmap_clear_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_COMP |
+ FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to clr TX_DPTH: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* clear CMDC RESET */
+ m_ctl |= FSL_XCVR_EXT_CTRL_CMDC_RESET(tx);
+ /* set TX_RX_MODE */
+ m_ctl |= FSL_XCVR_EXT_CTRL_TX_RX_MODE;
+ v_ctl |= (tx ? FSL_XCVR_EXT_CTRL_TX_RX_MODE : 0);
+ break;
+ }
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, m_ctl, v_ctl);
+ if (ret < 0) {
+ dev_err(dai->dev, "Error while setting EXT_CTRL: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_xcvr_constr(const struct snd_pcm_substream *substream,
+ const struct snd_pcm_hw_constraint_list *channels,
+ const struct snd_pcm_hw_constraint_list *rates)
+{
+ struct snd_pcm_runtime *rt = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ channels);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
+ rates);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int ret = 0;
+
+ if (xcvr->streams & BIT(substream->stream)) {
+ dev_err(dai->dev, "%sX busy\n", tx ? "T" : "R");
+ return -EBUSY;
+ }
+
+ /*
+ * EDMA controller needs period size to be a multiple of
+ * tx/rx maxburst
+ */
+ if (xcvr->soc_data->use_edma)
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ tx ? xcvr->dma_prms_tx.maxburst :
+ xcvr->dma_prms_rx.maxburst);
+
+ switch (xcvr->mode) {
+ case FSL_XCVR_MODE_SPDIF:
+ case FSL_XCVR_MODE_ARC:
+ if (xcvr->soc_data->spdif_only && tx)
+ ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
+ &xcvr->spdif_constr_rates);
+ else
+ ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
+ &fsl_xcvr_spdif_rates_constr);
+ break;
+ case FSL_XCVR_MODE_EARC:
+ ret = fsl_xcvr_constr(substream, &fsl_xcvr_earc_channels_constr,
+ &fsl_xcvr_earc_rates_constr);
+ break;
+ }
+ if (ret < 0)
+ return ret;
+
+ xcvr->streams |= BIT(substream->stream);
+
+ if (!xcvr->soc_data->spdif_only) {
+ struct snd_soc_card *card = dai->component->card;
+
+ /* Disable XCVR controls if there is stream started */
+ down_read(&card->snd_card->controls_rwsem);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false);
+ up_read(&card->snd_card->controls_rwsem);
+ }
+
+ return 0;
+}
+
+static void fsl_xcvr_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u32 mask = 0, val = 0;
+ int ret;
+
+ xcvr->streams &= ~BIT(substream->stream);
+
+ /* Enable XCVR controls if there is no stream started */
+ if (!xcvr->streams) {
+ if (!xcvr->soc_data->spdif_only) {
+ struct snd_soc_card *card = dai->component->card;
+
+ down_read(&card->snd_card->controls_rwsem);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name,
+ (xcvr->mode == FSL_XCVR_MODE_ARC));
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name,
+ (xcvr->mode == FSL_XCVR_MODE_EARC));
+ up_read(&card->snd_card->controls_rwsem);
+ }
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
+ FSL_XCVR_IRQ_EARC_ALL, 0);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set IER0: %d\n", ret);
+ return;
+ }
+
+ /* clear SPDIF MODE */
+ if (xcvr->mode == FSL_XCVR_MODE_SPDIF)
+ mask |= FSL_XCVR_EXT_CTRL_SPDIF_MODE;
+ }
+
+ if (xcvr->mode == FSL_XCVR_MODE_EARC) {
+ /* set CMDC RESET */
+ mask |= FSL_XCVR_EXT_CTRL_CMDC_RESET(tx);
+ val |= FSL_XCVR_EXT_CTRL_CMDC_RESET(tx);
+ }
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, mask, val);
+ if (ret < 0) {
+ dev_err(dai->dev, "Err setting DPATH RESET: %d\n", ret);
+ return;
+ }
+}
+
+static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned long lock_flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&xcvr->lock, lock_flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* set DPATH RESET */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DPTH_RESET(tx),
+ FSL_XCVR_EXT_CTRL_DPTH_RESET(tx));
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret);
+ goto release_lock;
+ }
+
+ if (tx) {
+ switch (xcvr->mode) {
+ case FSL_XCVR_MODE_EARC:
+ /* set isr_cmdc_tx_en, w1c */
+ ret = regmap_write(xcvr->regmap,
+ FSL_XCVR_ISR_SET,
+ FSL_XCVR_ISR_CMDC_TX_EN);
+ if (ret < 0) {
+ dev_err(dai->dev, "err updating isr %d\n", ret);
+ goto release_lock;
+ }
+ fallthrough;
+ case FSL_XCVR_MODE_SPDIF:
+ ret = regmap_set_bits(xcvr->regmap,
+ FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret);
+ goto release_lock;
+ }
+ break;
+ }
+ }
+
+ /* enable DMA RD/WR */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to enable DMA: %d\n", ret);
+ goto release_lock;
+ }
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
+ FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL);
+ if (ret < 0) {
+ dev_err(dai->dev, "Error while setting IER0: %d\n", ret);
+ goto release_lock;
+ }
+
+ /* clear DPATH RESET */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DPTH_RESET(tx),
+ 0);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret);
+ goto release_lock;
+ }
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /* disable DMA RD/WR */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DMA_DIS(tx),
+ FSL_XCVR_EXT_CTRL_DMA_DIS(tx));
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to disable DMA: %d\n", ret);
+ goto release_lock;
+ }
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
+ FSL_XCVR_IRQ_EARC_ALL, 0);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to clear IER0: %d\n", ret);
+ goto release_lock;
+ }
+
+ if (tx) {
+ switch (xcvr->mode) {
+ case FSL_XCVR_MODE_SPDIF:
+ ret = regmap_clear_bits(xcvr->regmap,
+ FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
+ goto release_lock;
+ }
+ if (xcvr->soc_data->spdif_only)
+ break;
+ else
+ fallthrough;
+ case FSL_XCVR_MODE_EARC:
+ /* clear ISR_CMDC_TX_EN, W1C */
+ ret = regmap_write(xcvr->regmap,
+ FSL_XCVR_ISR_CLR,
+ FSL_XCVR_ISR_CMDC_TX_EN);
+ if (ret < 0) {
+ dev_err(dai->dev,
+ "Err updating ISR %d\n", ret);
+ goto release_lock;
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+release_lock:
+ spin_unlock_irqrestore(&xcvr->lock, lock_flags);
+ return ret;
+}
+
+static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ const struct firmware *fw;
+ int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET;
+ u32 mask, val;
+
+ ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev);
+ if (ret) {
+ dev_err(dev, "failed to request firmware.\n");
+ return ret;
+ }
+
+ rem = fw->size;
+
+ /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */
+ if (rem > 16384) {
+ dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem);
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ for (page = 0; page < 10; page++) {
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_PAGE_MASK,
+ FSL_XCVR_EXT_CTRL_PAGE(page));
+ if (ret < 0) {
+ dev_err(dev, "FW: failed to set page %d, err=%d\n",
+ page, ret);
+ goto err_firmware;
+ }
+
+ off = page * size;
+ out = min(rem, size);
+ /* IPG clock is assumed to be running, otherwise it will hang */
+ if (out > 0) {
+ /* write firmware into code memory */
+ memcpy_toio(xcvr->ram_addr, fw->data + off, out);
+ rem -= out;
+ if (rem == 0) {
+ /* last part of firmware written */
+ /* clean remaining part of code memory page */
+ memset_io(xcvr->ram_addr + out, 0, size - out);
+ }
+ } else {
+ /* clean current page, including data memory */
+ memset_io(xcvr->ram_addr, 0, size);
+ }
+ }
+
+err_firmware:
+ release_firmware(fw);
+ if (ret < 0)
+ return ret;
+
+ /* configure watermarks */
+ mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK;
+ val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX);
+ val |= FSL_XCVR_EXT_CTRL_TX_FWM(FSL_XCVR_FIFO_WMK_TX);
+ /* disable DMA RD/WR */
+ mask |= FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS;
+ val |= FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS;
+ /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
+ mask |= FSL_XCVR_EXT_CTRL_PAGE_MASK;
+ val |= FSL_XCVR_EXT_CTRL_PAGE(8);
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, mask, val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set watermarks: %d\n", ret);
+ return ret;
+ }
+
+ /* Store Capabilities Data Structure into Data RAM */
+ memcpy_toio(xcvr->ram_addr + FSL_XCVR_CAP_DATA_STR, xcvr->cap_ds,
+ FSL_XCVR_CAPDS_SIZE);
+ return 0;
+}
+
+static int fsl_xcvr_type_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int fsl_xcvr_type_iec958_bytes_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof_field(struct snd_aes_iec958, status);
+
+ return 0;
+}
+
+static int fsl_xcvr_rx_cs_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ memcpy(ucontrol->value.iec958.status, xcvr->rx_iec958.status, 24);
+
+ return 0;
+}
+
+static int fsl_xcvr_tx_cs_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ memcpy(ucontrol->value.iec958.status, xcvr->tx_iec958.status, 24);
+
+ return 0;
+}
+
+static int fsl_xcvr_tx_cs_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status, 24);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new fsl_xcvr_rx_ctls[] = {
+ /* Channel status controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = fsl_xcvr_type_iec958_info,
+ .get = fsl_xcvr_rx_cs_get,
+ },
+ /* Capture channel status, bytes */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Capture Channel Status",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = fsl_xcvr_type_iec958_bytes_info,
+ .get = fsl_xcvr_rx_cs_get,
+ },
+};
+
+static struct snd_kcontrol_new fsl_xcvr_tx_ctls[] = {
+ /* Channel status controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = fsl_xcvr_type_iec958_info,
+ .get = fsl_xcvr_tx_cs_get,
+ .put = fsl_xcvr_tx_cs_put,
+ },
+ /* Playback channel status, bytes */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Playback Channel Status",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = fsl_xcvr_type_iec958_bytes_info,
+ .get = fsl_xcvr_tx_cs_get,
+ .put = fsl_xcvr_tx_cs_put,
+ },
+};
+
+static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &xcvr->dma_prms_tx, &xcvr->dma_prms_rx);
+
+ if (xcvr->soc_data->spdif_only)
+ xcvr->mode = FSL_XCVR_MODE_SPDIF;
+ else {
+ snd_soc_add_dai_controls(dai, &fsl_xcvr_mode_kctl, 1);
+ snd_soc_add_dai_controls(dai, &fsl_xcvr_arc_mode_kctl, 1);
+ snd_soc_add_dai_controls(dai, &fsl_xcvr_earc_capds_kctl, 1);
+ }
+ snd_soc_add_dai_controls(dai, fsl_xcvr_tx_ctls,
+ ARRAY_SIZE(fsl_xcvr_tx_ctls));
+ snd_soc_add_dai_controls(dai, fsl_xcvr_rx_ctls,
+ ARRAY_SIZE(fsl_xcvr_rx_ctls));
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_xcvr_dai_ops = {
+ .probe = fsl_xcvr_dai_probe,
+ .prepare = fsl_xcvr_prepare,
+ .startup = fsl_xcvr_startup,
+ .shutdown = fsl_xcvr_shutdown,
+ .trigger = fsl_xcvr_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_xcvr_dai = {
+ .ops = &fsl_xcvr_dai_ops,
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 32000,
+ .rate_max = 1536000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 32000,
+ .rate_max = 1536000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+};
+
+static const struct snd_soc_component_driver fsl_xcvr_comp = {
+ .name = "fsl-xcvr-dai",
+ .legacy_dai_naming = 1,
+};
+
+static const struct reg_default fsl_xcvr_reg_defaults[] = {
+ { FSL_XCVR_VERSION, 0x00000000 },
+ { FSL_XCVR_EXT_CTRL, 0xF8204040 },
+ { FSL_XCVR_EXT_STATUS, 0x00000000 },
+ { FSL_XCVR_EXT_IER0, 0x00000000 },
+ { FSL_XCVR_EXT_IER1, 0x00000000 },
+ { FSL_XCVR_EXT_ISR, 0x00000000 },
+ { FSL_XCVR_EXT_ISR_SET, 0x00000000 },
+ { FSL_XCVR_EXT_ISR_CLR, 0x00000000 },
+ { FSL_XCVR_EXT_ISR_TOG, 0x00000000 },
+ { FSL_XCVR_IER, 0x00000000 },
+ { FSL_XCVR_ISR, 0x00000000 },
+ { FSL_XCVR_ISR_SET, 0x00000000 },
+ { FSL_XCVR_ISR_CLR, 0x00000000 },
+ { FSL_XCVR_ISR_TOG, 0x00000000 },
+ { FSL_XCVR_CLK_CTRL, 0x0000018F },
+ { FSL_XCVR_RX_DPTH_CTRL, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CTRL_SET, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL_SET, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_TSCR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_BCR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_BCTR, 0x00000000 },
+ { FSL_XCVR_RX_DPTH_BCRR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CTRL, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CTRL_SET, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CTRL_CLR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CTRL_TOG, 0x00000000 },
+ { FSL_XCVR_TX_CS_DATA_0, 0x00000000 },
+ { FSL_XCVR_TX_CS_DATA_1, 0x00000000 },
+ { FSL_XCVR_TX_CS_DATA_2, 0x00000000 },
+ { FSL_XCVR_TX_CS_DATA_3, 0x00000000 },
+ { FSL_XCVR_TX_CS_DATA_4, 0x00000000 },
+ { FSL_XCVR_TX_CS_DATA_5, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL_SET, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_TSCR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_BCR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_BCTR, 0x00000000 },
+ { FSL_XCVR_TX_DPTH_BCRR, 0x00000000 },
+ { FSL_XCVR_DEBUG_REG_0, 0x00000000 },
+ { FSL_XCVR_DEBUG_REG_1, 0x00000000 },
+};
+
+static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
+{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
+
+ if (!xcvr->soc_data->use_phy)
+ if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) ||
+ reg > FSL_XCVR_TX_DPTH_BCRR)
+ return false;
+ switch (reg) {
+ case FSL_XCVR_VERSION:
+ case FSL_XCVR_EXT_CTRL:
+ case FSL_XCVR_EXT_STATUS:
+ case FSL_XCVR_EXT_IER0:
+ case FSL_XCVR_EXT_IER1:
+ case FSL_XCVR_EXT_ISR:
+ case FSL_XCVR_EXT_ISR_SET:
+ case FSL_XCVR_EXT_ISR_CLR:
+ case FSL_XCVR_EXT_ISR_TOG:
+ case FSL_XCVR_IER:
+ case FSL_XCVR_ISR:
+ case FSL_XCVR_ISR_SET:
+ case FSL_XCVR_ISR_CLR:
+ case FSL_XCVR_ISR_TOG:
+ case FSL_XCVR_PHY_AI_CTRL:
+ case FSL_XCVR_PHY_AI_CTRL_SET:
+ case FSL_XCVR_PHY_AI_CTRL_CLR:
+ case FSL_XCVR_PHY_AI_CTRL_TOG:
+ case FSL_XCVR_PHY_AI_RDATA:
+ case FSL_XCVR_CLK_CTRL:
+ case FSL_XCVR_RX_DPTH_CTRL:
+ case FSL_XCVR_RX_DPTH_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CTRL_TOG:
+ case FSL_XCVR_RX_CS_DATA_0:
+ case FSL_XCVR_RX_CS_DATA_1:
+ case FSL_XCVR_RX_CS_DATA_2:
+ case FSL_XCVR_RX_CS_DATA_3:
+ case FSL_XCVR_RX_CS_DATA_4:
+ case FSL_XCVR_RX_CS_DATA_5:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_TSCR:
+ case FSL_XCVR_RX_DPTH_BCR:
+ case FSL_XCVR_RX_DPTH_BCTR:
+ case FSL_XCVR_RX_DPTH_BCRR:
+ case FSL_XCVR_TX_DPTH_CTRL:
+ case FSL_XCVR_TX_DPTH_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CTRL_TOG:
+ case FSL_XCVR_TX_CS_DATA_0:
+ case FSL_XCVR_TX_CS_DATA_1:
+ case FSL_XCVR_TX_CS_DATA_2:
+ case FSL_XCVR_TX_CS_DATA_3:
+ case FSL_XCVR_TX_CS_DATA_4:
+ case FSL_XCVR_TX_CS_DATA_5:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_TX_DPTH_TSCR:
+ case FSL_XCVR_TX_DPTH_BCR:
+ case FSL_XCVR_TX_DPTH_BCTR:
+ case FSL_XCVR_TX_DPTH_BCRR:
+ case FSL_XCVR_DEBUG_REG_0:
+ case FSL_XCVR_DEBUG_REG_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
+{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
+
+ if (!xcvr->soc_data->use_phy)
+ if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA)
+ return false;
+ switch (reg) {
+ case FSL_XCVR_EXT_CTRL:
+ case FSL_XCVR_EXT_IER0:
+ case FSL_XCVR_EXT_IER1:
+ case FSL_XCVR_EXT_ISR:
+ case FSL_XCVR_EXT_ISR_SET:
+ case FSL_XCVR_EXT_ISR_CLR:
+ case FSL_XCVR_EXT_ISR_TOG:
+ case FSL_XCVR_IER:
+ case FSL_XCVR_ISR_SET:
+ case FSL_XCVR_ISR_CLR:
+ case FSL_XCVR_ISR_TOG:
+ case FSL_XCVR_PHY_AI_CTRL:
+ case FSL_XCVR_PHY_AI_CTRL_SET:
+ case FSL_XCVR_PHY_AI_CTRL_CLR:
+ case FSL_XCVR_PHY_AI_CTRL_TOG:
+ case FSL_XCVR_PHY_AI_WDATA:
+ case FSL_XCVR_CLK_CTRL:
+ case FSL_XCVR_RX_DPTH_CTRL:
+ case FSL_XCVR_RX_DPTH_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_TX_DPTH_CTRL:
+ case FSL_XCVR_TX_DPTH_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CTRL_TOG:
+ case FSL_XCVR_TX_CS_DATA_0:
+ case FSL_XCVR_TX_CS_DATA_1:
+ case FSL_XCVR_TX_CS_DATA_2:
+ case FSL_XCVR_TX_CS_DATA_3:
+ case FSL_XCVR_TX_CS_DATA_4:
+ case FSL_XCVR_TX_CS_DATA_5:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_xcvr_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_XCVR_EXT_STATUS:
+ case FSL_XCVR_EXT_ISR:
+ case FSL_XCVR_EXT_ISR_SET:
+ case FSL_XCVR_EXT_ISR_CLR:
+ case FSL_XCVR_EXT_ISR_TOG:
+ case FSL_XCVR_ISR:
+ case FSL_XCVR_ISR_SET:
+ case FSL_XCVR_ISR_CLR:
+ case FSL_XCVR_ISR_TOG:
+ case FSL_XCVR_PHY_AI_CTRL:
+ case FSL_XCVR_PHY_AI_CTRL_SET:
+ case FSL_XCVR_PHY_AI_CTRL_CLR:
+ case FSL_XCVR_PHY_AI_CTRL_TOG:
+ case FSL_XCVR_PHY_AI_RDATA:
+ case FSL_XCVR_RX_CS_DATA_0:
+ case FSL_XCVR_RX_CS_DATA_1:
+ case FSL_XCVR_RX_CS_DATA_2:
+ case FSL_XCVR_RX_CS_DATA_3:
+ case FSL_XCVR_RX_CS_DATA_4:
+ case FSL_XCVR_RX_CS_DATA_5:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_TSCR:
+ case FSL_XCVR_RX_DPTH_BCR:
+ case FSL_XCVR_RX_DPTH_BCTR:
+ case FSL_XCVR_RX_DPTH_BCRR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_TX_DPTH_TSCR:
+ case FSL_XCVR_TX_DPTH_BCR:
+ case FSL_XCVR_TX_DPTH_BCTR:
+ case FSL_XCVR_TX_DPTH_BCRR:
+ case FSL_XCVR_DEBUG_REG_0:
+ case FSL_XCVR_DEBUG_REG_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config fsl_xcvr_regmap_cfg = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_MAX_REG,
+ .reg_defaults = fsl_xcvr_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_xcvr_reg_defaults),
+ .readable_reg = fsl_xcvr_readable_reg,
+ .volatile_reg = fsl_xcvr_volatile_reg,
+ .writeable_reg = fsl_xcvr_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct reg_default fsl_xcvr_phy_reg_defaults[] = {
+ { FSL_XCVR_PHY_CTRL, 0x58200804 },
+ { FSL_XCVR_PHY_STATUS, 0x00000000 },
+ { FSL_XCVR_PHY_ANALOG_TRIM, 0x00260F13 },
+ { FSL_XCVR_PHY_SLEW_RATE_TRIM, 0x00000411 },
+ { FSL_XCVR_PHY_DATA_TEST_DELAY, 0x00990000 },
+ { FSL_XCVR_PHY_TEST_CTRL, 0x00000000 },
+ { FSL_XCVR_PHY_DIFF_CDR_CTRL, 0x016D0009 },
+ { FSL_XCVR_PHY_CTRL2, 0x80000000 },
+};
+
+static const struct regmap_config fsl_xcvr_regmap_phy_cfg = {
+ .reg_bits = 8,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_PHY_CTRL2_TOG,
+ .reg_defaults = fsl_xcvr_phy_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_xcvr_phy_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+ .reg_read = fsl_xcvr_phy_reg_read,
+ .reg_write = fsl_xcvr_phy_reg_write,
+};
+
+static const struct regmap_config fsl_xcvr_regmap_pllv0_cfg = {
+ .reg_bits = 8,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_PLL_STAT0_TOG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_read = fsl_xcvr_pll_reg_read,
+ .reg_write = fsl_xcvr_pll_reg_write,
+};
+
+static const struct regmap_config fsl_xcvr_regmap_pllv1_cfg = {
+ .reg_bits = 8,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_GP_PLL_STATUS_TOG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_read = fsl_xcvr_pll_reg_read,
+ .reg_write = fsl_xcvr_pll_reg_write,
+};
+
+static void reset_rx_work(struct work_struct *work)
+{
+ struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst);
+ struct device *dev = &xcvr->pdev->dev;
+ unsigned long lock_flags;
+ u32 ext_ctrl;
+
+ dev_dbg(dev, "reset rx path\n");
+ spin_lock_irqsave(&xcvr->lock, lock_flags);
+ regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl);
+
+ if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) {
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DMA_RD_DIS,
+ FSL_XCVR_EXT_CTRL_DMA_RD_DIS);
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
+ FSL_XCVR_EXT_CTRL_RX_DPTH_RESET);
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DMA_RD_DIS,
+ 0);
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
+ 0);
+ }
+ spin_unlock_irqrestore(&xcvr->lock, lock_flags);
+}
+
+static irqreturn_t irq0_isr(int irq, void *devid)
+{
+ struct fsl_xcvr *xcvr = (struct fsl_xcvr *)devid;
+ struct device *dev = &xcvr->pdev->dev;
+ struct regmap *regmap = xcvr->regmap;
+ void __iomem *reg_ctrl, *reg_buff;
+ u32 isr, isr_clr = 0, val, i;
+
+ regmap_read(regmap, FSL_XCVR_EXT_ISR, &isr);
+
+ if (isr & FSL_XCVR_IRQ_NEW_CS) {
+ dev_dbg(dev, "Received new CS block\n");
+ isr_clr |= FSL_XCVR_IRQ_NEW_CS;
+ if (xcvr->soc_data->fw_name) {
+ /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_PAGE_MASK,
+ FSL_XCVR_EXT_CTRL_PAGE(8));
+
+ /* Find updated CS buffer */
+ reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_0;
+ reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_0;
+ memcpy_fromio(&val, reg_ctrl, sizeof(val));
+ if (!val) {
+ reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_1;
+ reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_1;
+ memcpy_fromio(&val, reg_ctrl, sizeof(val));
+ }
+
+ if (val) {
+ /* copy CS buffer */
+ memcpy_fromio(&xcvr->rx_iec958.status, reg_buff,
+ sizeof(xcvr->rx_iec958.status));
+ for (i = 0; i < 6; i++) {
+ val = *(u32 *)(xcvr->rx_iec958.status + i*4);
+ *(u32 *)(xcvr->rx_iec958.status + i*4) =
+ bitrev32(val);
+ }
+ /* clear CS control register */
+ writel_relaxed(0, reg_ctrl);
+ }
+ } else {
+ regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_0,
+ (u32 *)&xcvr->rx_iec958.status[0]);
+ regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_1,
+ (u32 *)&xcvr->rx_iec958.status[4]);
+ regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_2,
+ (u32 *)&xcvr->rx_iec958.status[8]);
+ regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_3,
+ (u32 *)&xcvr->rx_iec958.status[12]);
+ regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_4,
+ (u32 *)&xcvr->rx_iec958.status[16]);
+ regmap_read(xcvr->regmap, FSL_XCVR_RX_CS_DATA_5,
+ (u32 *)&xcvr->rx_iec958.status[20]);
+ for (i = 0; i < 6; i++) {
+ val = *(u32 *)(xcvr->rx_iec958.status + i * 4);
+ *(u32 *)(xcvr->rx_iec958.status + i * 4) =
+ bitrev32(val);
+ }
+ regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_CSA);
+ }
+ }
+ if (isr & FSL_XCVR_IRQ_NEW_UD) {
+ dev_dbg(dev, "Received new UD block\n");
+ isr_clr |= FSL_XCVR_IRQ_NEW_UD;
+ }
+ if (isr & FSL_XCVR_IRQ_MUTE) {
+ dev_dbg(dev, "HW mute bit detected\n");
+ isr_clr |= FSL_XCVR_IRQ_MUTE;
+ }
+ if (isr & FSL_XCVR_IRQ_FIFO_UOFL_ERR) {
+ dev_dbg(dev, "RX/TX FIFO full/empty\n");
+ isr_clr |= FSL_XCVR_IRQ_FIFO_UOFL_ERR;
+ }
+ if (isr & FSL_XCVR_IRQ_ARC_MODE) {
+ dev_dbg(dev, "CMDC SM falls out of eARC mode\n");
+ isr_clr |= FSL_XCVR_IRQ_ARC_MODE;
+ }
+ if (isr & FSL_XCVR_IRQ_DMA_RD_REQ) {
+ dev_dbg(dev, "DMA read request\n");
+ isr_clr |= FSL_XCVR_IRQ_DMA_RD_REQ;
+ }
+ if (isr & FSL_XCVR_IRQ_DMA_WR_REQ) {
+ dev_dbg(dev, "DMA write request\n");
+ isr_clr |= FSL_XCVR_IRQ_DMA_WR_REQ;
+ }
+ if (isr & FSL_XCVR_IRQ_CMDC_STATUS_UPD) {
+ dev_dbg(dev, "CMDC status update\n");
+ isr_clr |= FSL_XCVR_IRQ_CMDC_STATUS_UPD;
+ }
+ if (isr & FSL_XCVR_IRQ_PREAMBLE_MISMATCH) {
+ dev_dbg(dev, "Preamble mismatch\n");
+ isr_clr |= FSL_XCVR_IRQ_PREAMBLE_MISMATCH;
+ }
+ if (isr & FSL_XCVR_IRQ_UNEXP_PRE_REC) {
+ dev_dbg(dev, "Unexpected preamble received\n");
+ isr_clr |= FSL_XCVR_IRQ_UNEXP_PRE_REC;
+ }
+ if (isr & FSL_XCVR_IRQ_M_W_PRE_MISMATCH) {
+ dev_dbg(dev, "M/W preamble mismatch\n");
+ isr_clr |= FSL_XCVR_IRQ_M_W_PRE_MISMATCH;
+ }
+ if (isr & FSL_XCVR_IRQ_B_PRE_MISMATCH) {
+ dev_dbg(dev, "B preamble mismatch\n");
+ isr_clr |= FSL_XCVR_IRQ_B_PRE_MISMATCH;
+ }
+
+ if (isr & (FSL_XCVR_IRQ_PREAMBLE_MISMATCH |
+ FSL_XCVR_IRQ_UNEXP_PRE_REC |
+ FSL_XCVR_IRQ_M_W_PRE_MISMATCH |
+ FSL_XCVR_IRQ_B_PRE_MISMATCH)) {
+ schedule_work(&xcvr->work_rst);
+ }
+
+ if (isr_clr) {
+ regmap_write(regmap, FSL_XCVR_EXT_ISR_CLR, isr_clr);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = {
+ .fw_name = "imx/xcvr/xcvr-imx8mp.bin",
+ .use_phy = true,
+ .pll_ver = PLL_MX8MP,
+};
+
+static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = {
+ .spdif_only = true,
+ .use_edma = true,
+};
+
+static const struct fsl_xcvr_soc_data fsl_xcvr_imx95_data = {
+ .fw_name = "imx/xcvr/xcvr-imx95.bin",
+ .spdif_only = true,
+ .use_phy = true,
+ .use_edma = true,
+ .pll_ver = PLL_MX95,
+};
+
+static const struct of_device_id fsl_xcvr_dt_ids[] = {
+ { .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data },
+ { .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data},
+ { .compatible = "fsl,imx95-xcvr", .data = &fsl_xcvr_imx95_data},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids);
+
+static int fsl_xcvr_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fsl_xcvr *xcvr;
+ struct resource *rx_res, *tx_res;
+ void __iomem *regs;
+ int ret, irq;
+
+ xcvr = devm_kzalloc(dev, sizeof(*xcvr), GFP_KERNEL);
+ if (!xcvr)
+ return -ENOMEM;
+
+ xcvr->pdev = pdev;
+ xcvr->soc_data = of_device_get_match_data(&pdev->dev);
+
+ xcvr->ipg_clk = devm_clk_get(dev, "ipg");
+ if (IS_ERR(xcvr->ipg_clk))
+ return dev_err_probe(dev, PTR_ERR(xcvr->ipg_clk),
+ "failed to get ipg clock\n");
+
+ xcvr->phy_clk = devm_clk_get(dev, "phy");
+ if (IS_ERR(xcvr->phy_clk))
+ return dev_err_probe(dev, PTR_ERR(xcvr->phy_clk),
+ "failed to get phy clock\n");
+
+ xcvr->spba_clk = devm_clk_get(dev, "spba");
+ if (IS_ERR(xcvr->spba_clk))
+ return dev_err_probe(dev, PTR_ERR(xcvr->spba_clk),
+ "failed to get spba clock\n");
+
+ xcvr->pll_ipg_clk = devm_clk_get(dev, "pll_ipg");
+ if (IS_ERR(xcvr->pll_ipg_clk))
+ return dev_err_probe(dev, PTR_ERR(xcvr->pll_ipg_clk),
+ "failed to get pll_ipg clock\n");
+
+ fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk,
+ &xcvr->pll11k_clk);
+
+ if (xcvr->soc_data->spdif_only) {
+ if (!(xcvr->pll8k_clk || xcvr->pll11k_clk))
+ xcvr->pll8k_clk = xcvr->phy_clk;
+ fsl_asoc_constrain_rates(&xcvr->spdif_constr_rates,
+ &fsl_xcvr_spdif_rates_constr,
+ xcvr->pll8k_clk, xcvr->pll11k_clk, NULL,
+ xcvr->spdif_constr_rates_list);
+ }
+
+ xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram");
+ if (IS_ERR(xcvr->ram_addr))
+ return PTR_ERR(xcvr->ram_addr);
+
+ regs = devm_platform_ioremap_resource_byname(pdev, "regs");
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ xcvr->regmap = devm_regmap_init_mmio_clk(dev, NULL, regs,
+ &fsl_xcvr_regmap_cfg);
+ if (IS_ERR(xcvr->regmap))
+ return dev_err_probe(dev, PTR_ERR(xcvr->regmap), "failed to init XCVR regmap\n");
+
+ if (xcvr->soc_data->use_phy) {
+ xcvr->regmap_phy = devm_regmap_init(dev, NULL, xcvr,
+ &fsl_xcvr_regmap_phy_cfg);
+ if (IS_ERR(xcvr->regmap_phy))
+ return dev_err_probe(dev, PTR_ERR(xcvr->regmap_phy),
+ "failed to init XCVR PHY regmap\n");
+
+ switch (xcvr->soc_data->pll_ver) {
+ case PLL_MX8MP:
+ xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
+ &fsl_xcvr_regmap_pllv0_cfg);
+ if (IS_ERR(xcvr->regmap_pll))
+ return dev_err_probe(dev, PTR_ERR(xcvr->regmap_pll),
+ "failed to init XCVR PLL regmap\n");
+ break;
+ case PLL_MX95:
+ xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
+ &fsl_xcvr_regmap_pllv1_cfg);
+ if (IS_ERR(xcvr->regmap_pll))
+ return dev_err_probe(dev, PTR_ERR(xcvr->regmap_pll),
+ "failed to init XCVR PLL regmap\n");
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Error for PLL version %d\n",
+ xcvr->soc_data->pll_ver);
+ }
+ }
+
+ xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(xcvr->reset))
+ return dev_err_probe(dev, PTR_ERR(xcvr->reset),
+ "failed to get XCVR reset control\n");
+
+ /* get IRQs */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, irq0_isr, 0, pdev->name, xcvr);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to claim IRQ0\n");
+
+ rx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxfifo");
+ tx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txfifo");
+ if (!rx_res || !tx_res)
+ return dev_err_probe(dev, -EINVAL, "could not find rxfifo or txfifo resource\n");
+ xcvr->dma_prms_rx.chan_name = "rx";
+ xcvr->dma_prms_tx.chan_name = "tx";
+ xcvr->dma_prms_rx.addr = rx_res->start;
+ xcvr->dma_prms_tx.addr = tx_res->start;
+ xcvr->dma_prms_rx.maxburst = FSL_XCVR_MAXBURST_RX;
+ xcvr->dma_prms_tx.maxburst = FSL_XCVR_MAXBURST_TX;
+
+ platform_set_drvdata(pdev, xcvr);
+ pm_runtime_enable(dev);
+ regcache_cache_only(xcvr->regmap, true);
+ if (xcvr->soc_data->use_phy) {
+ regcache_cache_only(xcvr->regmap_phy, true);
+ regcache_cache_only(xcvr->regmap_pll, true);
+ }
+
+ /*
+ * Register platform component before registering cpu dai for there
+ * is not defer probe for platform component in snd_soc_add_pcm_runtime().
+ */
+ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
+ if (ret) {
+ pm_runtime_disable(dev);
+ return dev_err_probe(dev, ret, "failed to pcm register\n");
+ }
+
+ ret = devm_snd_soc_register_component(dev, &fsl_xcvr_comp,
+ &fsl_xcvr_dai, 1);
+ if (ret) {
+ pm_runtime_disable(dev);
+ dev_err(dev, "failed to register component %s\n",
+ fsl_xcvr_comp.name);
+ }
+
+ INIT_WORK(&xcvr->work_rst, reset_rx_work);
+ spin_lock_init(&xcvr->lock);
+ return ret;
+}
+
+static void fsl_xcvr_remove(struct platform_device *pdev)
+{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(&pdev->dev);
+
+ cancel_work_sync(&xcvr->work_rst);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int fsl_xcvr_runtime_suspend(struct device *dev)
+{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
+ int ret;
+
+ if (!xcvr->soc_data->spdif_only &&
+ xcvr->mode == FSL_XCVR_MODE_EARC) {
+ /* Assert M0+ reset */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_CORE_RESET,
+ FSL_XCVR_EXT_CTRL_CORE_RESET);
+ if (ret < 0)
+ dev_err(dev, "Failed to assert M0+ core: %d\n", ret);
+ }
+
+ regcache_cache_only(xcvr->regmap, true);
+ if (xcvr->soc_data->use_phy) {
+ regcache_cache_only(xcvr->regmap_phy, true);
+ regcache_cache_only(xcvr->regmap_pll, true);
+ }
+
+ clk_disable_unprepare(xcvr->spba_clk);
+ clk_disable_unprepare(xcvr->phy_clk);
+ clk_disable_unprepare(xcvr->pll_ipg_clk);
+ clk_disable_unprepare(xcvr->ipg_clk);
+
+ return 0;
+}
+
+static int fsl_xcvr_runtime_resume(struct device *dev)
+{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
+ int ret;
+
+ ret = reset_control_assert(xcvr->reset);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert M0+ reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(xcvr->ipg_clk);
+ if (ret) {
+ dev_err(dev, "failed to start IPG clock.\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(xcvr->pll_ipg_clk);
+ if (ret) {
+ dev_err(dev, "failed to start PLL IPG clock.\n");
+ goto stop_ipg_clk;
+ }
+
+ ret = clk_prepare_enable(xcvr->phy_clk);
+ if (ret) {
+ dev_err(dev, "failed to start PHY clock: %d\n", ret);
+ goto stop_pll_ipg_clk;
+ }
+
+ ret = clk_prepare_enable(xcvr->spba_clk);
+ if (ret) {
+ dev_err(dev, "failed to start SPBA clock.\n");
+ goto stop_phy_clk;
+ }
+
+ ret = reset_control_deassert(xcvr->reset);
+ if (ret) {
+ dev_err(dev, "failed to deassert M0+ reset.\n");
+ goto stop_spba_clk;
+ }
+
+ regcache_cache_only(xcvr->regmap, false);
+ regcache_mark_dirty(xcvr->regmap);
+ ret = regcache_sync(xcvr->regmap);
+
+ if (ret) {
+ dev_err(dev, "failed to sync regcache.\n");
+ goto stop_spba_clk;
+ }
+
+ if (xcvr->soc_data->use_phy) {
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
+ FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
+ if (ret < 0) {
+ dev_err(dev, "Error while release PHY reset: %d\n", ret);
+ goto stop_spba_clk;
+ }
+
+ regcache_cache_only(xcvr->regmap_phy, false);
+ regcache_mark_dirty(xcvr->regmap_phy);
+ ret = regcache_sync(xcvr->regmap_phy);
+ if (ret) {
+ dev_err(dev, "failed to sync phy regcache.\n");
+ goto stop_spba_clk;
+ }
+
+ regcache_cache_only(xcvr->regmap_pll, false);
+ regcache_mark_dirty(xcvr->regmap_pll);
+ ret = regcache_sync(xcvr->regmap_pll);
+ if (ret) {
+ dev_err(dev, "failed to sync pll regcache.\n");
+ goto stop_spba_clk;
+ }
+ }
+
+ if (xcvr->soc_data->fw_name) {
+ ret = fsl_xcvr_load_firmware(xcvr);
+ if (ret) {
+ dev_err(dev, "failed to load firmware.\n");
+ goto stop_spba_clk;
+ }
+
+ /* Release M0+ reset */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
+ if (ret < 0) {
+ dev_err(dev, "M0+ core release failed: %d\n", ret);
+ goto stop_spba_clk;
+ }
+
+ /* Let M0+ core complete firmware initialization */
+ msleep(50);
+ }
+
+ return 0;
+
+stop_spba_clk:
+ clk_disable_unprepare(xcvr->spba_clk);
+stop_phy_clk:
+ clk_disable_unprepare(xcvr->phy_clk);
+stop_pll_ipg_clk:
+ clk_disable_unprepare(xcvr->pll_ipg_clk);
+stop_ipg_clk:
+ clk_disable_unprepare(xcvr->ipg_clk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops fsl_xcvr_pm_ops = {
+ RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, fsl_xcvr_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_xcvr_driver = {
+ .probe = fsl_xcvr_probe,
+ .driver = {
+ .name = "fsl-xcvr",
+ .pm = pm_ptr(&fsl_xcvr_pm_ops),
+ .of_match_table = fsl_xcvr_dt_ids,
+ },
+ .remove = fsl_xcvr_remove,
+};
+module_platform_driver(fsl_xcvr_driver);
+
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_DESCRIPTION("NXP Audio Transceiver (XCVR) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h
new file mode 100644
index 000000000000..dade3945cc0c
--- /dev/null
+++ b/sound/soc/fsl/fsl_xcvr.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * NXP XCVR ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2019 NXP
+ */
+
+#ifndef __FSL_XCVR_H
+#define __FSL_XCVR_H
+
+#define FSL_XCVR_MODE_SPDIF 0
+#define FSL_XCVR_MODE_ARC 1
+#define FSL_XCVR_MODE_EARC 2
+
+/* XCVR Registers */
+#define FSL_XCVR_REG_OFFSET 0x800 /* regs offset */
+#define FSL_XCVR_FIFO_SIZE 0x80 /* 128 */
+#define FSL_XCVR_FIFO_WMK_RX (FSL_XCVR_FIFO_SIZE >> 1) /* 64 */
+#define FSL_XCVR_FIFO_WMK_TX (FSL_XCVR_FIFO_SIZE >> 1) /* 64 */
+#define FSL_XCVR_MAXBURST_RX (FSL_XCVR_FIFO_WMK_RX >> 2) /* 16 */
+#define FSL_XCVR_MAXBURST_TX (FSL_XCVR_FIFO_WMK_TX >> 2) /* 16 */
+
+#define FSL_XCVR_RX_FIFO_ADDR 0x0C00
+#define FSL_XCVR_TX_FIFO_ADDR 0x0E00
+
+#define FSL_XCVR_VERSION 0x00 /* Version */
+#define FSL_XCVR_EXT_CTRL 0x10 /* Control */
+#define FSL_XCVR_EXT_STATUS 0x20 /* Status */
+#define FSL_XCVR_EXT_IER0 0x30 /* Interrupt en 0 */
+#define FSL_XCVR_EXT_IER1 0x40 /* Interrupt en 1 */
+#define FSL_XCVR_EXT_ISR 0x50 /* Interrupt status */
+#define FSL_XCVR_EXT_ISR_SET 0x54 /* Interrupt status */
+#define FSL_XCVR_EXT_ISR_CLR 0x58 /* Interrupt status */
+#define FSL_XCVR_EXT_ISR_TOG 0x5C /* Interrupt status */
+#define FSL_XCVR_IER 0x70 /* Interrupt en for M0+ */
+#define FSL_XCVR_ISR 0x80 /* Interrupt status */
+#define FSL_XCVR_ISR_SET 0x84 /* Interrupt status set */
+#define FSL_XCVR_ISR_CLR 0x88 /* Interrupt status clear */
+#define FSL_XCVR_ISR_TOG 0x8C /* Interrupt status toggle */
+#define FSL_XCVR_PHY_AI_CTRL 0x90
+#define FSL_XCVR_PHY_AI_CTRL_SET 0x94
+#define FSL_XCVR_PHY_AI_CTRL_CLR 0x98
+#define FSL_XCVR_PHY_AI_CTRL_TOG 0x9C
+#define FSL_XCVR_PHY_AI_WDATA 0xA0
+#define FSL_XCVR_PHY_AI_RDATA 0xA4
+#define FSL_XCVR_CLK_CTRL 0xB0
+#define FSL_XCVR_RX_DPTH_CTRL 0x180 /* RX datapath ctrl reg */
+#define FSL_XCVR_RX_DPTH_CTRL_SET 0x184
+#define FSL_XCVR_RX_DPTH_CTRL_CLR 0x188
+#define FSL_XCVR_RX_DPTH_CTRL_TOG 0x18c
+
+#define FSL_XCVR_RX_CS_DATA_0 0x190
+#define FSL_XCVR_RX_CS_DATA_1 0x194
+#define FSL_XCVR_RX_CS_DATA_2 0x198
+#define FSL_XCVR_RX_CS_DATA_3 0x19C
+#define FSL_XCVR_RX_CS_DATA_4 0x1A0
+#define FSL_XCVR_RX_CS_DATA_5 0x1A4
+
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL 0x1C0
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL_SET 0x1C4
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR 0x1C8
+#define FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG 0x1CC
+
+#define FSL_XCVR_RX_DPTH_TSCR 0x1D0
+#define FSL_XCVR_RX_DPTH_BCR 0x1D4
+#define FSL_XCVR_RX_DPTH_BCTR 0x1D8
+#define FSL_XCVR_RX_DPTH_BCRR 0x1DC
+
+#define FSL_XCVR_TX_DPTH_CTRL 0x220 /* TX datapath ctrl reg */
+#define FSL_XCVR_TX_DPTH_CTRL_SET 0x224
+#define FSL_XCVR_TX_DPTH_CTRL_CLR 0x228
+#define FSL_XCVR_TX_DPTH_CTRL_TOG 0x22C
+#define FSL_XCVR_TX_CS_DATA_0 0x230 /* TX channel status bits regs */
+#define FSL_XCVR_TX_CS_DATA_1 0x234
+#define FSL_XCVR_TX_CS_DATA_2 0x238
+#define FSL_XCVR_TX_CS_DATA_3 0x23C
+#define FSL_XCVR_TX_CS_DATA_4 0x240
+#define FSL_XCVR_TX_CS_DATA_5 0x244
+
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL 0x260
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL_SET 0x264
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR 0x268
+#define FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG 0x26C
+
+#define FSL_XCVR_TX_DPTH_TSCR 0x270
+#define FSL_XCVR_TX_DPTH_BCR 0x274
+#define FSL_XCVR_TX_DPTH_BCTR 0x278
+#define FSL_XCVR_TX_DPTH_BCRR 0x27C
+
+#define FSL_XCVR_DEBUG_REG_0 0x2E0
+#define FSL_XCVR_DEBUG_REG_1 0x2F0
+
+#define FSL_XCVR_MAX_REG FSL_XCVR_DEBUG_REG_1
+
+#define FSL_XCVR_EXT_CTRL_CORE_RESET BIT(31)
+
+#define FSL_XCVR_EXT_CTRL_RX_CMDC_RESET BIT(30)
+#define FSL_XCVR_EXT_CTRL_TX_CMDC_RESET BIT(29)
+#define FSL_XCVR_EXT_CTRL_CMDC_RESET(t) (t ? BIT(29) : BIT(30))
+
+#define FSL_XCVR_EXT_CTRL_RX_DPTH_RESET BIT(28)
+#define FSL_XCVR_EXT_CTRL_TX_DPTH_RESET BIT(27)
+#define FSL_XCVR_EXT_CTRL_DPTH_RESET(t) (t ? BIT(27) : BIT(28))
+
+#define FSL_XCVR_EXT_CTRL_TX_RX_MODE BIT(26)
+#define FSL_XCVR_EXT_CTRL_DMA_RD_DIS BIT(25)
+#define FSL_XCVR_EXT_CTRL_DMA_WR_DIS BIT(24)
+#define FSL_XCVR_EXT_CTRL_DMA_DIS(t) (t ? BIT(24) : BIT(25))
+#define FSL_XCVR_EXT_CTRL_SPDIF_MODE BIT(23)
+#define FSL_XCVR_EXT_CTRL_SLEEP_MODE BIT(21)
+
+#define FSL_XCVR_EXT_CTRL_TX_FWM_SHFT 0
+#define FSL_XCVR_EXT_CTRL_TX_FWM_MASK GENMASK(6, 0)
+#define FSL_XCVR_EXT_CTRL_TX_FWM(i) (((i) << FSL_XCVR_EXT_CTRL_TX_FWM_SHFT) \
+ & FSL_XCVR_EXT_CTRL_TX_FWM_MASK)
+#define FSL_XCVR_EXT_CTRL_RX_FWM_SHFT 8
+#define FSL_XCVR_EXT_CTRL_RX_FWM_MASK GENMASK(14, 8)
+#define FSL_XCVR_EXT_CTRL_RX_FWM(i) (((i) << FSL_XCVR_EXT_CTRL_RX_FWM_SHFT) \
+ & FSL_XCVR_EXT_CTRL_RX_FWM_MASK)
+#define FSL_XCVR_EXT_CTRL_PAGE_SHFT 16
+#define FSL_XCVR_EXT_CTRL_PAGE_MASK GENMASK(19, 16)
+#define FSL_XCVR_EXT_CTRL_PAGE(i) (((i) << FSL_XCVR_EXT_CTRL_PAGE_SHFT) \
+ & FSL_XCVR_EXT_CTRL_PAGE_MASK)
+
+#define FSL_XCVR_EXT_STUS_NT_FIFO_ENTR GENMASK(7, 0)
+#define FSL_XCVR_EXT_STUS_NR_FIFO_ENTR GENMASK(15, 8)
+#define FSL_XCVR_EXT_STUS_CM0_SLEEPING BIT(16)
+#define FSL_XCVR_EXT_STUS_CM0_DEEP_SLP BIT(17)
+#define FSL_XCVR_EXT_STUS_CM0_SLP_HACK BIT(18)
+#define FSL_XCVR_EXT_STUS_RX_CMDC_RSTO BIT(23)
+#define FSL_XCVR_EXT_STUS_TX_CMDC_RSTO BIT(24)
+#define FSL_XCVR_EXT_STUS_RX_CMDC_COTO BIT(25)
+#define FSL_XCVR_EXT_STUS_TX_CMDC_COTO BIT(26)
+#define FSL_XCVR_EXT_STUS_HB_STATUS BIT(27)
+#define FSL_XCVR_EXT_STUS_NEW_UD4_REC BIT(28)
+#define FSL_XCVR_EXT_STUS_NEW_UD5_REC BIT(29)
+#define FSL_XCVR_EXT_STUS_NEW_UD6_REC BIT(30)
+#define FSL_XCVR_EXT_STUS_HPD_INPUT BIT(31)
+
+#define FSL_XCVR_IRQ_NEW_CS BIT(0)
+#define FSL_XCVR_IRQ_NEW_UD BIT(1)
+#define FSL_XCVR_IRQ_MUTE BIT(2)
+#define FSL_XCVR_IRQ_CMDC_RESP_TO BIT(3)
+#define FSL_XCVR_IRQ_ECC_ERR BIT(4)
+#define FSL_XCVR_IRQ_PREAMBLE_MISMATCH BIT(5)
+#define FSL_XCVR_IRQ_FIFO_UOFL_ERR BIT(6)
+#define FSL_XCVR_IRQ_HOST_WAKEUP BIT(7)
+#define FSL_XCVR_IRQ_HOST_OHPD BIT(8)
+#define FSL_XCVR_IRQ_DMAC_NO_DATA_REC BIT(9)
+#define FSL_XCVR_IRQ_DMAC_FMT_CHG_DET BIT(10)
+#define FSL_XCVR_IRQ_HB_STATE_CHG BIT(11)
+#define FSL_XCVR_IRQ_CMDC_STATUS_UPD BIT(12)
+#define FSL_XCVR_IRQ_TEMP_UPD BIT(13)
+#define FSL_XCVR_IRQ_DMA_RD_REQ BIT(14)
+#define FSL_XCVR_IRQ_DMA_WR_REQ BIT(15)
+#define FSL_XCVR_IRQ_DMAC_BME_BIT_ERR BIT(16)
+#define FSL_XCVR_IRQ_PREAMBLE_MATCH BIT(17)
+#define FSL_XCVR_IRQ_M_W_PRE_MISMATCH BIT(18)
+#define FSL_XCVR_IRQ_B_PRE_MISMATCH BIT(19)
+#define FSL_XCVR_IRQ_UNEXP_PRE_REC BIT(20)
+#define FSL_XCVR_IRQ_ARC_MODE BIT(21)
+#define FSL_XCVR_IRQ_CH_UD_OFLOW BIT(22)
+#define FSL_XCVR_IRQ_EARC_ALL (FSL_XCVR_IRQ_NEW_CS | \
+ FSL_XCVR_IRQ_NEW_UD | \
+ FSL_XCVR_IRQ_MUTE | \
+ FSL_XCVR_IRQ_FIFO_UOFL_ERR | \
+ FSL_XCVR_IRQ_HOST_WAKEUP | \
+ FSL_XCVR_IRQ_CMDC_STATUS_UPD |\
+ FSL_XCVR_IRQ_B_PRE_MISMATCH |\
+ FSL_XCVR_IRQ_M_W_PRE_MISMATCH |\
+ FSL_XCVR_IRQ_PREAMBLE_MISMATCH |\
+ FSL_XCVR_IRQ_UNEXP_PRE_REC |\
+ FSL_XCVR_IRQ_ARC_MODE)
+
+#define FSL_XCVR_ISR_CMDC_TX_EN BIT(3)
+#define FSL_XCVR_ISR_HPD_TGL BIT(15)
+#define FSL_XCVR_ISR_DMAC_SPARE_INT BIT(19)
+#define FSL_XCVR_ISR_SET_SPDIF_RX_INT BIT(20)
+#define FSL_XCVR_ISR_SET_SPDIF_TX_INT BIT(21)
+#define FSL_XCVR_ISR_SET_SPDIF_MODE(t) (t ? BIT(21) : BIT(20))
+#define FSL_XCVR_ISR_SET_ARC_CM_INT BIT(22)
+#define FSL_XCVR_ISR_SET_ARC_SE_INT BIT(23)
+
+#define FSL_XCVR_PHY_AI_ADDR_MASK GENMASK(7, 0)
+#define FSL_XCVR_PHY_AI_RESETN BIT(15)
+#define FSL_XCVR_PHY_AI_TOG_PLL BIT(24)
+#define FSL_XCVR_PHY_AI_TOG_DONE_PLL BIT(25)
+#define FSL_XCVR_PHY_AI_TOG_PHY BIT(26)
+#define FSL_XCVR_PHY_AI_TOG_DONE_PHY BIT(27)
+#define FSL_XCVR_PHY_AI_RW_MASK BIT(31)
+
+#define FSL_XCVR_RX_DPTH_CTRL_PAPB_FIFO_STATUS BIT(0)
+#define FSL_XCVR_RX_DPTH_CTRL_DIS_PRE_ERR_CHK BIT(1)
+#define FSL_XCVR_RX_DPTH_CTRL_DIS_NOD_REC_CHK BIT(2)
+#define FSL_XCVR_RX_DPTH_CTRL_ECC_VUC_BIT_CHK BIT(3)
+#define FSL_XCVR_RX_DPTH_CTRL_EN_CMP_PAR_CALC BIT(4)
+#define FSL_XCVR_RX_DPTH_CTRL_RST_PKT_CNT_FIFO BIT(5)
+#define FSL_XCVR_RX_DPTH_CTRL_STORE_FMT BIT(6)
+#define FSL_XCVR_RX_DPTH_CTRL_EN_PAR_CALC BIT(7)
+#define FSL_XCVR_RX_DPTH_CTRL_UDR BIT(8)
+#define FSL_XCVR_RX_DPTH_CTRL_CSR BIT(9)
+#define FSL_XCVR_RX_DPTH_CTRL_UDA BIT(10)
+#define FSL_XCVR_RX_DPTH_CTRL_CSA BIT(11)
+#define FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO BIT(12)
+#define FSL_XCVR_RX_DPTH_CTRL_DIS_B_PRE_ERR_CHK BIT(13)
+#define FSL_XCVR_RX_DPTH_CTRL_PABS BIT(19)
+#define FSL_XCVR_RX_DPTH_CTRL_DTS_CDS BIT(20)
+#define FSL_XCVR_RX_DPTH_CTRL_BLKC BIT(21)
+#define FSL_XCVR_RX_DPTH_CTRL_MUTE_CTRL BIT(22)
+#define FSL_XCVR_RX_DPTH_CTRL_MUTE_MODE BIT(23)
+#define FSL_XCVR_RX_DPTH_CTRL_FMT_CHG_CTRL BIT(24)
+#define FSL_XCVR_RX_DPTH_CTRL_FMT_CHG_MODE BIT(25)
+#define FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL BIT(26)
+#define FSL_XCVR_RX_DPTH_CTRL_LAYB_MODE BIT(27)
+#define FSL_XCVR_RX_DPTH_CTRL_PRC BIT(28)
+#define FSL_XCVR_RX_DPTH_CTRL_COMP BIT(29)
+#define FSL_XCVR_RX_DPTH_CTRL_FSM GENMASK(31, 30)
+
+#define FSL_XCVR_TX_DPTH_CTRL_CS_ACK BIT(0)
+#define FSL_XCVR_TX_DPTH_CTRL_UD_ACK BIT(1)
+#define FSL_XCVR_TX_DPTH_CTRL_CS_MOD BIT(2)
+#define FSL_XCVR_TX_DPTH_CTRL_UD_MOD BIT(3)
+#define FSL_XCVR_TX_DPTH_CTRL_VLD_MOD BIT(4)
+#define FSL_XCVR_TX_DPTH_CTRL_FRM_VLD BIT(5)
+#define FSL_XCVR_TX_DPTH_CTRL_EN_PARITY BIT(6)
+#define FSL_XCVR_TX_DPTH_CTRL_EN_PREAMBLE BIT(7)
+#define FSL_XCVR_TX_DPTH_CTRL_EN_ECC_INTER BIT(8)
+#define FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM BIT(10)
+#define FSL_XCVR_TX_DPTH_CTRL_FRM_FMT BIT(11)
+#define FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX BIT(14)
+#define FSL_XCVR_TX_DPTH_CTRL_ADD_CYC_TX_OE_STR BIT(15)
+#define FSL_XCVR_TX_DPTH_CTRL_ADD_CYC_TX_OE_END BIT(16)
+#define FSL_XCVR_TX_DPTH_CTRL_CLK_RATIO BIT(29)
+#define FSL_XCVR_TX_DPTH_CTRL_TM_NO_PRE_BME GENMASK(31, 30)
+
+#define FSL_XCVR_PHY_AI_CTRL_AI_RESETN BIT(15)
+#define FSL_XCVR_PHY_AI_CTRL_AI_RWB BIT(31)
+
+#define FSL_XCVR_PLL_CTRL0 0x00
+#define FSL_XCVR_PLL_CTRL0_SET 0x04
+#define FSL_XCVR_PLL_CTRL0_CLR 0x08
+#define FSL_XCVR_PLL_NUM 0x20
+#define FSL_XCVR_PLL_DEN 0x30
+#define FSL_XCVR_PLL_PDIV 0x40
+#define FSL_XCVR_PLL_BANDGAP 0x50
+#define FSL_XCVR_PLL_BANDGAP_SET 0x54
+#define FSL_XCVR_PLL_STAT0 0x60
+#define FSL_XCVR_PLL_STAT0_TOG 0x6c
+
+#define FSL_XCVR_PHY_CTRL 0x00
+#define FSL_XCVR_PHY_CTRL_SET 0x04
+#define FSL_XCVR_PHY_CTRL_CLR 0x08
+#define FSL_XCVR_PHY_CTRL_TOG 0x0c
+#define FSL_XCVR_PHY_STATUS 0x10
+#define FSL_XCVR_PHY_ANALOG_TRIM 0x20
+#define FSL_XCVR_PHY_SLEW_RATE_TRIM 0x30
+#define FSL_XCVR_PHY_DATA_TEST_DELAY 0x40
+#define FSL_XCVR_PHY_TEST_CTRL 0x50
+#define FSL_XCVR_PHY_DIFF_CDR_CTRL 0x60
+#define FSL_XCVR_PHY_CTRL2 0x70
+#define FSL_XCVR_PHY_CTRL2_SET 0x74
+#define FSL_XCVR_PHY_CTRL2_CLR 0x78
+#define FSL_XCVR_PHY_CTRL2_TOG 0x7c
+
+#define FSL_XCVR_PLL_BANDGAP_EN_VBG BIT(0)
+#define FSL_XCVR_PLL_CTRL0_HROFF BIT(13)
+#define FSL_XCVR_PLL_CTRL0_PWP BIT(14)
+#define FSL_XCVR_PLL_CTRL0_CM0_EN BIT(24)
+#define FSL_XCVR_PLL_CTRL0_CM1_EN BIT(25)
+#define FSL_XCVR_PLL_CTRL0_CM2_EN BIT(26)
+#define FSL_XCVR_PLL_PDIVx(v, i) ((v & 0x7) << (4 * i))
+
+#define FSL_XCVR_PHY_CTRL_PHY_EN BIT(0)
+#define FSL_XCVR_PHY_CTRL_RX_CM_EN BIT(1)
+#define FSL_XCVR_PHY_CTRL_TSDIFF_OE BIT(5)
+#define FSL_XCVR_PHY_CTRL_SPDIF_EN BIT(8)
+#define FSL_XCVR_PHY_CTRL_ARC_MODE_SE_EN BIT(9)
+#define FSL_XCVR_PHY_CTRL_ARC_MODE_CM_EN BIT(10)
+#define FSL_XCVR_PHY_CTRL_TX_CLK_MASK GENMASK(26, 25)
+#define FSL_XCVR_PHY_CTRL_TX_CLK_HDMI_SS BIT(25)
+#define FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS BIT(26)
+#define FSL_XCVR_PHY_CTRL2_EARC_TXMS BIT(14)
+
+#define FSL_XCVR_CS_DATA_0_FS_MASK GENMASK(31, 24)
+#define FSL_XCVR_CS_DATA_0_FS_32000 0x3000000
+#define FSL_XCVR_CS_DATA_0_FS_44100 0x0000000
+#define FSL_XCVR_CS_DATA_0_FS_48000 0x2000000
+#define FSL_XCVR_CS_DATA_0_FS_64000 0xB000000
+#define FSL_XCVR_CS_DATA_0_FS_88200 0x8000000
+#define FSL_XCVR_CS_DATA_0_FS_96000 0xA000000
+#define FSL_XCVR_CS_DATA_0_FS_176400 0xC000000
+#define FSL_XCVR_CS_DATA_0_FS_192000 0xE000000
+
+#define FSL_XCVR_CS_DATA_0_CH_MASK 0x3A
+#define FSL_XCVR_CS_DATA_0_CH_U2LPCM 0x00
+#define FSL_XCVR_CS_DATA_0_CH_UMLPCM 0x20
+#define FSL_XCVR_CS_DATA_0_CH_U1BAUD 0x30
+
+#define FSL_XCVR_CS_DATA_1_CH_MASK 0xF000
+#define FSL_XCVR_CS_DATA_1_CH_2 0x0000
+#define FSL_XCVR_CS_DATA_1_CH_8 0x7000
+#define FSL_XCVR_CS_DATA_1_CH_16 0xB000
+#define FSL_XCVR_CS_DATA_1_CH_32 0x3000
+
+/* Data memory structures */
+#define FSL_XCVR_RX_CS_CTRL_0 0x20 /* First RX CS control register */
+#define FSL_XCVR_RX_CS_CTRL_1 0x24 /* Second RX CS control register */
+#define FSL_XCVR_RX_CS_BUFF_0 0x80 /* First RX CS buffer */
+#define FSL_XCVR_RX_CS_BUFF_1 0xA0 /* Second RX CS buffer */
+#define FSL_XCVR_CAP_DATA_STR 0x300 /* Capabilities data structure */
+
+/* GP PLL Registers */
+#define FSL_XCVR_GP_PLL_CTRL 0x00
+#define FSL_XCVR_GP_PLL_CTRL_SET 0x04
+#define FSL_XCVR_GP_PLL_CTRL_CLR 0x08
+#define FSL_XCVR_GP_PLL_CTRL_TOG 0x0C
+#define FSL_XCVR_GP_PLL_ANA_PRG 0x10
+#define FSL_XCVR_GP_PLL_ANA_PRG_SET 0x14
+#define FSL_XCVR_GP_PLL_ANA_PRG_CLR 0x18
+#define FSL_XCVR_GP_PLL_ANA_PRG_TOG 0x1C
+#define FSL_XCVR_GP_PLL_TEST 0x20
+#define FSL_XCVR_GP_PLL_TEST_SET 0x24
+#define FSL_XCVR_GP_PLL_TEST_CLR 0x28
+#define FSL_XCVR_GP_PLL_TEST_TOG 0x2C
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM 0x30
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_SET 0x34
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_CLR 0x38
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_TOG 0x3C
+#define FSL_XCVR_GP_PLL_NUMERATOR 0x40
+#define FSL_XCVR_GP_PLL_NUMERATOR_SET 0x44
+#define FSL_XCVR_GP_PLL_NUMERATOR_CLR 0x48
+#define FSL_XCVR_GP_PLL_NUMERATOR_TOG 0x4C
+#define FSL_XCVR_GP_PLL_DENOMINATOR 0x50
+#define FSL_XCVR_GP_PLL_DENOMINATOR_SET 0x54
+#define FSL_XCVR_GP_PLL_DENOMINATOR_CLR 0x58
+#define FSL_XCVR_GP_PLL_DENOMINATOR_TOG 0x5C
+#define FSL_XCVR_GP_PLL_DIV 0x60
+#define FSL_XCVR_GP_PLL_DIV_SET 0x64
+#define FSL_XCVR_GP_PLL_DIV_CLR 0x68
+#define FSL_XCVR_GP_PLL_DIV_TOG 0x6C
+#define FSL_XCVR_GP_PLL_DFS_CTRL0 0x70
+#define FSL_XCVR_GP_PLL_DFS_CTRL0_SET 0x74
+#define FSL_XCVR_GP_PLL_DFS_CTRL0_CLR 0x78
+#define FSL_XCVR_GP_PLL_DFS_CTRL0_TOG 0x7C
+#define FSL_XCVR_GP_PLL_DFS_DIV0 0x80
+#define FSL_XCVR_GP_PLL_DFS_DIV0_SET 0x84
+#define FSL_XCVR_GP_PLL_DFS_DIV0_CLR 0x88
+#define FSL_XCVR_GP_PLL_DFS_DIV0_TOG 0x8C
+#define FSL_XCVR_GP_PLL_DFS_CTRL1 0x90
+#define FSL_XCVR_GP_PLL_DFS_CTRL1_SET 0x94
+#define FSL_XCVR_GP_PLL_DFS_CTRL1_CLR 0x98
+#define FSL_XCVR_GP_PLL_DFS_CTRL1_TOG 0x9C
+#define FSL_XCVR_GP_PLL_DFS_DIV1 0xA0
+#define FSL_XCVR_GP_PLL_DFS_DIV1_SET 0xA4
+#define FSL_XCVR_GP_PLL_DFS_DIV1_CLR 0xA8
+#define FSL_XCVR_GP_PLL_DFS_DIV1_TOG 0xAC
+#define FSL_XCVR_GP_PLL_DFS_CTRL2 0xB0
+#define FSL_XCVR_GP_PLL_DFS_CTRL2_SET 0xB4
+#define FSL_XCVR_GP_PLL_DFS_CTRL2_CLR 0xB8
+#define FSL_XCVR_GP_PLL_DFS_CTRL2_TOG 0xBC
+#define FSL_XCVR_GP_PLL_DFS_DIV2 0xC0
+#define FSL_XCVR_GP_PLL_DFS_DIV2_SET 0xC4
+#define FSL_XCVR_GP_PLL_DFS_DIV2_CLR 0xC8
+#define FSL_XCVR_GP_PLL_DFS_DIV2_TOG 0xCC
+#define FSL_XCVR_GP_PLL_DFS_CTRL3 0xD0
+#define FSL_XCVR_GP_PLL_DFS_CTRL3_SET 0xD4
+#define FSL_XCVR_GP_PLL_DFS_CTRL3_CLR 0xD8
+#define FSL_XCVR_GP_PLL_DFS_CTRL3_TOG 0xDC
+#define FSL_XCVR_GP_PLL_DFS_DIV3 0xE0
+#define FSL_XCVR_GP_PLL_DFS_DIV3_SET 0xE4
+#define FSL_XCVR_GP_PLL_DFS_DIV3_CLR 0xE8
+#define FSL_XCVR_GP_PLL_DFS_DIV3_TOG 0xEC
+#define FSL_XCVR_GP_PLL_STATUS 0xF0
+#define FSL_XCVR_GP_PLL_STATUS_SET 0xF4
+#define FSL_XCVR_GP_PLL_STATUS_CLR 0xF8
+#define FSL_XCVR_GP_PLL_STATUS_TOG 0xFC
+
+/* GP PLL Control Register */
+#define FSL_XCVR_GP_PLL_CTRL_LBYPASS BIT(31)
+#define FSL_XCVR_GP_PLL_CTRL_HCS BIT(16)
+#define FSL_XCVR_GP_PLL_CTRL_MSD BIT(12)
+#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN3 BIT(11)
+#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN2 BIT(10)
+#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN1 BIT(9)
+#define FSL_XCVR_GP_PLL_CTRL_SPREADCTL BIT(8)
+#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_BYPASS BIT(2)
+#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN BIT(1)
+#define FSL_XCVR_GP_PLL_CTRL_POWERUP BIT(0)
+
+/* GP PLL Numerator Register */
+#define FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT 2
+#define FSL_XCVR_GP_PLL_NUMERATOR_MFN GENMASK(31, 2)
+
+/* GP PLL Denominator Register */
+#define FSL_XCVR_GP_PLL_DENOMINATOR_MFD GENMASK(29, 0)
+
+/* GP PLL Dividers Register */
+#define FSL_XCVR_GP_PLL_DIV_MFI_SHIFT 16
+#define FSL_XCVR_GP_PLL_DIV_MFI GENMASK(24, 16)
+#define FSL_XCVR_GP_PLL_DIV_RDIV GENMASK(15, 13)
+#define FSL_XCVR_GP_PLL_DIV_ODIV GENMASK(7, 0)
+
+#endif /* __FSL_XCVR_H */
diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c
new file mode 100644
index 000000000000..38aafb8954c7
--- /dev/null
+++ b/sound/soc/fsl/imx-audio-rpmsg.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2020 NXP
+
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+#include "imx-pcm-rpmsg.h"
+
+/*
+ * struct imx_audio_rpmsg: private data
+ *
+ * @rpmsg_pdev: pointer of platform device
+ */
+struct imx_audio_rpmsg {
+ struct platform_device *rpmsg_pdev;
+ struct platform_device *card_pdev;
+};
+
+static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct imx_audio_rpmsg *rpmsg = dev_get_drvdata(&rpdev->dev);
+ struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data;
+ struct rpmsg_info *info;
+ struct rpmsg_msg *msg;
+ unsigned long flags;
+
+ if (!rpmsg->rpmsg_pdev)
+ return 0;
+
+ info = platform_get_drvdata(rpmsg->rpmsg_pdev);
+
+ dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n",
+ src, r_msg->header.cmd, r_msg->param.resp);
+
+ switch (r_msg->header.type) {
+ case MSG_TYPE_C:
+ /* TYPE C is notification from M core */
+ switch (r_msg->header.cmd) {
+ case TX_PERIOD_DONE:
+ spin_lock_irqsave(&info->lock[TX], flags);
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->r_msg.param.buffer_tail =
+ r_msg->param.buffer_tail;
+ msg->r_msg.param.buffer_tail %= info->num_period[TX];
+ spin_unlock_irqrestore(&info->lock[TX], flags);
+ info->callback[TX](info->callback_param[TX]);
+ break;
+ case RX_PERIOD_DONE:
+ spin_lock_irqsave(&info->lock[RX], flags);
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->r_msg.param.buffer_tail =
+ r_msg->param.buffer_tail;
+ msg->r_msg.param.buffer_tail %= info->num_period[1];
+ spin_unlock_irqrestore(&info->lock[RX], flags);
+ info->callback[RX](info->callback_param[RX]);
+ break;
+ default:
+ dev_warn(&rpdev->dev, "unknown msg command\n");
+ break;
+ }
+ break;
+ case MSG_TYPE_B:
+ /* TYPE B is response msg */
+ memcpy(&info->r_msg, r_msg, sizeof(struct rpmsg_r_msg));
+ complete(&info->cmd_complete);
+ break;
+ default:
+ dev_warn(&rpdev->dev, "unknown msg type\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int imx_audio_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+ struct imx_audio_rpmsg *data;
+ int ret = 0;
+
+ dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+ rpdev->src, rpdev->dst);
+
+ data = devm_kzalloc(&rpdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&rpdev->dev, data);
+
+ /* Register platform driver for rpmsg routine */
+ data->rpmsg_pdev = platform_device_register_data(&rpdev->dev,
+ rpdev->id.name,
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+ if (IS_ERR(data->rpmsg_pdev)) {
+ dev_err(&rpdev->dev, "failed to register rpmsg platform.\n");
+ ret = PTR_ERR(data->rpmsg_pdev);
+ }
+
+ data->card_pdev = platform_device_register_data(&rpdev->dev,
+ "imx-audio-rpmsg",
+ PLATFORM_DEVID_AUTO,
+ rpdev->id.name,
+ strlen(rpdev->id.name) + 1);
+ if (IS_ERR(data->card_pdev)) {
+ dev_err(&rpdev->dev, "failed to register rpmsg card.\n");
+ ret = PTR_ERR(data->card_pdev);
+ }
+
+ return ret;
+}
+
+static void imx_audio_rpmsg_remove(struct rpmsg_device *rpdev)
+{
+ struct imx_audio_rpmsg *data = dev_get_drvdata(&rpdev->dev);
+
+ if (data->rpmsg_pdev)
+ platform_device_unregister(data->rpmsg_pdev);
+
+ if (data->card_pdev)
+ platform_device_unregister(data->card_pdev);
+
+ dev_info(&rpdev->dev, "audio rpmsg driver is removed\n");
+}
+
+static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = {
+ { .name = "rpmsg-audio-channel" },
+ { .name = "rpmsg-micfil-channel" },
+ { },
+};
+MODULE_DEVICE_TABLE(rpmsg, imx_audio_rpmsg_id_table);
+
+static struct rpmsg_driver imx_audio_rpmsg_driver = {
+ .drv.name = "imx_audio_rpmsg",
+ .id_table = imx_audio_rpmsg_id_table,
+ .probe = imx_audio_rpmsg_probe,
+ .callback = imx_audio_rpmsg_cb,
+ .remove = imx_audio_rpmsg_remove,
+};
+
+module_rpmsg_driver(imx_audio_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("rpmsg:imx_audio_rpmsg");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
new file mode 100644
index 000000000000..dac5d4ddacd6
--- /dev/null
+++ b/sound/soc/fsl/imx-audmix.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 NXP
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * https://www.opensource.org/licenses/gpl-license.html
+ * https://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "fsl_sai.h"
+#include "fsl_audmix.h"
+
+struct imx_audmix {
+ struct platform_device *pdev;
+ struct snd_soc_card card;
+ struct platform_device *audmix_pdev;
+ struct platform_device *out_pdev;
+ int num_dai;
+ struct snd_soc_dai_link *dai;
+ int num_dai_conf;
+ struct snd_soc_codec_conf *dai_conf;
+ int num_dapm_routes;
+ struct snd_soc_dapm_route *dapm_routes;
+};
+
+static int imx_audmix_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, 8);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ FSL_AUDMIX_FORMATS);
+}
+
+static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct device *dev = rtd->card->dev;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
+ u32 channels = params_channels(params);
+ int ret, dir;
+
+ /* For playback the AUDMIX is consumer, and for record is provider */
+ fmt |= tx ? SND_SOC_DAIFMT_BP_FP : SND_SOC_DAIFMT_BC_FC;
+ dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN;
+
+ /* set DAI configuration */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), fmt);
+ if (ret) {
+ dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), FSL_SAI_CLK_MAST1, 0, dir);
+ if (ret) {
+ dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Per datasheet, AUDMIX expects 8 slots and 32 bits
+ * for every slot in TDM mode.
+ */
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), BIT(channels) - 1,
+ BIT(channels) - 1, 8, 32);
+ if (ret)
+ dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+
+ return ret;
+}
+
+static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct device *dev = rtd->card->dev;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
+ int ret;
+
+ if (!tx)
+ return 0;
+
+ /* For playback the AUDMIX is consumer */
+ fmt |= SND_SOC_DAIFMT_BC_FC;
+
+ /* set AUDMIX DAI configuration */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), fmt);
+ if (ret)
+ dev_err(dev, "failed to set AUDMIX DAI fmt: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops imx_audmix_fe_ops = {
+ .startup = imx_audmix_fe_startup,
+ .hw_params = imx_audmix_fe_hw_params,
+};
+
+static const struct snd_soc_ops imx_audmix_be_ops = {
+ .hw_params = imx_audmix_be_hw_params,
+};
+
+static const char *name[][3] = {
+ {"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"},
+ {"sai-tx", "sai-tx", "sai-rx"},
+ {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "SAI-Capture"},
+ {"SAI-Playback", "SAI-Playback", "AUDMIX-Capture-0"},
+};
+
+static int imx_audmix_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *audmix_np = NULL, *out_cpu_np = NULL;
+ struct platform_device *audmix_pdev = NULL;
+ struct platform_device *cpu_pdev;
+ struct of_phandle_args args;
+ struct imx_audmix *priv;
+ int i, num_dai, ret;
+ const char *fe_name_pref = "HiFi-AUDMIX-FE-";
+ char *be_name, *dai_name;
+
+ if (pdev->dev.parent) {
+ audmix_np = pdev->dev.parent->of_node;
+ } else {
+ dev_err(&pdev->dev, "Missing parent device.\n");
+ return -EINVAL;
+ }
+
+ if (!audmix_np) {
+ dev_err(&pdev->dev, "Missing DT node for parent device.\n");
+ return -EINVAL;
+ }
+
+ audmix_pdev = of_find_device_by_node(audmix_np);
+ if (!audmix_pdev) {
+ dev_err(&pdev->dev, "Missing AUDMIX platform device for %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+ put_device(&audmix_pdev->dev);
+
+ num_dai = of_count_phandle_with_args(audmix_np, "dais", NULL);
+ if (num_dai != FSL_AUDMIX_MAX_DAIS) {
+ dev_err(&pdev->dev, "Need 2 dais to be provided for %s\n",
+ audmix_np->full_name);
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ num_dai += 1;
+ priv->num_dai = 2 * num_dai;
+ priv->dai = devm_kcalloc(&pdev->dev, priv->num_dai,
+ sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!priv->dai)
+ return -ENOMEM;
+
+ priv->num_dai_conf = num_dai;
+ priv->dai_conf = devm_kcalloc(&pdev->dev, priv->num_dai_conf,
+ sizeof(struct snd_soc_codec_conf),
+ GFP_KERNEL);
+ if (!priv->dai_conf)
+ return -ENOMEM;
+
+ priv->num_dapm_routes = num_dai;
+ priv->dapm_routes = devm_kcalloc(&pdev->dev, priv->num_dapm_routes,
+ sizeof(struct snd_soc_dapm_route),
+ GFP_KERNEL);
+ if (!priv->dapm_routes)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dai; i++) {
+ struct snd_soc_dai_link_component *dlc;
+
+ /* for CPU x 2 */
+ dlc = devm_kcalloc(&pdev->dev, 2, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ if (i == num_dai - 1)
+ ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, 0,
+ &args);
+ else
+ ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
+ &args);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n");
+ return ret;
+ }
+
+ cpu_pdev = of_find_device_by_node(args.np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ return -EINVAL;
+ }
+ put_device(&cpu_pdev->dev);
+
+ dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
+ fe_name_pref, args.np->full_name);
+ if (!dai_name)
+ return -ENOMEM;
+
+ dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
+
+ if (i == num_dai - 1)
+ out_cpu_np = args.np;
+
+ /*
+ * CPU == Platform
+ * platform is using soc-generic-dmaengine-pcm
+ */
+ priv->dai[i].cpus =
+ priv->dai[i].platforms = &dlc[0];
+ priv->dai[i].codecs = &snd_soc_dummy_dlc;
+
+ priv->dai[i].num_cpus = 1;
+ priv->dai[i].num_codecs = 1;
+ priv->dai[i].num_platforms = 1;
+ priv->dai[i].name = name[0][i];
+ priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
+ priv->dai[i].cpus->of_node = args.np;
+ priv->dai[i].cpus->dai_name = name[1][i];
+
+ priv->dai[i].dynamic = 1;
+ if (i == num_dai - 1)
+ priv->dai[i].capture_only = 1;
+ else
+ priv->dai[i].playback_only = 1;
+ priv->dai[i].ignore_pmdown_time = 1;
+ priv->dai[i].ops = &imx_audmix_fe_ops;
+
+ /* Add AUDMIX Backend */
+ be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "audmix-%d", i);
+ if (!be_name)
+ return -ENOMEM;
+
+ priv->dai[num_dai + i].cpus = &dlc[1];
+ priv->dai[num_dai + i].codecs = &snd_soc_dummy_dlc;
+
+ priv->dai[num_dai + i].num_cpus = 1;
+ priv->dai[num_dai + i].num_codecs = 1;
+
+ priv->dai[num_dai + i].name = be_name;
+ priv->dai[num_dai + i].cpus->of_node = audmix_np;
+ priv->dai[num_dai + i].cpus->dai_name = be_name;
+ priv->dai[num_dai + i].no_pcm = 1;
+ if (i == num_dai - 1)
+ priv->dai[num_dai + i].capture_only = 1;
+ else
+ priv->dai[num_dai + i].playback_only = 1;
+ priv->dai[num_dai + i].ignore_pmdown_time = 1;
+ priv->dai[num_dai + i].ops = &imx_audmix_be_ops;
+
+ priv->dai_conf[i].dlc.of_node = args.np;
+ priv->dai_conf[i].name_prefix = dai_name;
+
+ if (i == num_dai - 1) {
+ priv->dapm_routes[i].sink =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, name[2][i]);
+ if (!priv->dapm_routes[i].sink)
+ return -ENOMEM;
+
+ priv->dapm_routes[i].source = name[3][i];
+ } else {
+ priv->dapm_routes[i].source =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, name[3][i]);
+ if (!priv->dapm_routes[i].source)
+ return -ENOMEM;
+
+ priv->dapm_routes[i].sink = name[2][i];
+ }
+ }
+
+ cpu_pdev = of_find_device_by_node(out_cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ return -EINVAL;
+ }
+ put_device(&cpu_pdev->dev);
+
+ priv->audmix_pdev = audmix_pdev;
+ priv->out_pdev = cpu_pdev;
+
+ priv->card.dai_link = priv->dai;
+ priv->card.num_links = priv->num_dai;
+ priv->card.codec_conf = priv->dai_conf;
+ priv->card.num_configs = priv->num_dai_conf;
+ priv->card.dapm_routes = priv->dapm_routes;
+ priv->card.num_dapm_routes = priv->num_dapm_routes;
+ priv->card.dev = &pdev->dev;
+ priv->card.owner = THIS_MODULE;
+ priv->card.name = "imx-audmix";
+
+ platform_set_drvdata(pdev, &priv->card);
+ snd_soc_card_set_drvdata(&priv->card, priv);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct platform_driver imx_audmix_driver = {
+ .probe = imx_audmix_probe,
+ .driver = {
+ .name = "imx-audmix",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(imx_audmix_driver);
+
+MODULE_DESCRIPTION("NXP AUDMIX ASoC machine driver");
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_ALIAS("platform:imx-audmix");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index fc57da341d61..f8335a04595a 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -1,21 +1,11 @@
-/*
- * Copyright 2012 Freescale Semiconductor, Inc.
- * Copyright 2012 Linaro Ltd.
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright 2012 Freescale Semiconductor, Inc.
+// Copyright 2012 Linaro Ltd.
+// Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+//
+// Initial development of this code was funded by
+// Phytec Messtechnik GmbH, https://www.phytec.de
#include <linux/clk.h>
#include <linux/debugfs.h>
@@ -23,7 +13,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -33,6 +22,8 @@
static struct clk *audmux_clk;
static void __iomem *audmux_base;
+static u32 *regcache;
+static u32 reg_max;
#define IMX_AUDMUX_V2_PTCR(x) ((x) * 8)
#define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4)
@@ -70,65 +61,61 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
uintptr_t port = (uintptr_t)file->private_data;
u32 pdcr, ptcr;
- if (audmux_clk) {
- ret = clk_prepare_enable(audmux_clk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(audmux_clk);
+ if (ret)
+ return ret;
ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
- if (audmux_clk)
- clk_disable_unprepare(audmux_clk);
+ clk_disable_unprepare(audmux_clk);
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
- pdcr, ptcr);
+ ret = sysfs_emit(buf, "PDCR: %08x\nPTCR: %08x\n", pdcr, ptcr);
if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"TxFS output from %s, ",
audmux_port_string((ptcr >> 27) & 0x7));
else
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"TxFS input, ");
if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR)
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"TxClk output from %s",
audmux_port_string((ptcr >> 22) & 0x7));
else
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"TxClk input");
- ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) {
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"Port is symmetric");
} else {
if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR)
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"RxFS output from %s, ",
audmux_port_string((ptcr >> 17) & 0x7));
else
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"RxFS input, ");
if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR)
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"RxClk output from %s",
audmux_port_string((ptcr >> 12) & 0x7));
else
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"RxClk input");
}
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"\nData received from %s\n",
audmux_port_string((pdcr >> 13) & 0x7));
@@ -151,17 +138,11 @@ static void audmux_debugfs_init(void)
char buf[20];
audmux_debugfs_root = debugfs_create_dir("audmux", NULL);
- if (!audmux_debugfs_root) {
- pr_warning("Failed to create AUDMUX debugfs root\n");
- return;
- }
for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) {
snprintf(buf, sizeof(buf), "ssi%lu", i);
- if (!debugfs_create_file(buf, 0444, audmux_debugfs_root,
- (void *)i, &audmux_debugfs_fops))
- pr_warning("Failed to create AUDMUX port %lu debugfs file\n",
- i);
+ debugfs_create_file(buf, 0444, audmux_debugfs_root,
+ (void *)i, &audmux_debugfs_fops);
}
}
@@ -184,22 +165,9 @@ static enum imx_audmux_type {
IMX31_AUDMUX,
} audmux_type;
-static const struct platform_device_id imx_audmux_ids[] = {
- {
- .name = "imx21-audmux",
- .driver_data = IMX21_AUDMUX,
- }, {
- .name = "imx31-audmux",
- .driver_data = IMX31_AUDMUX,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(platform, imx_audmux_ids);
-
static const struct of_device_id imx_audmux_dt_ids[] = {
- { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], },
- { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], },
+ { .compatible = "fsl,imx21-audmux", .data = (void *)IMX21_AUDMUX, },
+ { .compatible = "fsl,imx31-audmux", .data = (void *)IMX31_AUDMUX, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids);
@@ -236,17 +204,14 @@ int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
if (!audmux_base)
return -ENOSYS;
- if (audmux_clk) {
- ret = clk_prepare_enable(audmux_clk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(audmux_clk);
+ if (ret)
+ return ret;
writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
- if (audmux_clk)
- clk_disable_unprepare(audmux_clk);
+ clk_disable_unprepare(audmux_clk);
return 0;
}
@@ -268,13 +233,13 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
ret = of_property_read_u32(child, "fsl,audmux-port", &port);
if (ret) {
- dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%s\"\n",
- child->full_name);
+ dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%pOF\"\n",
+ child);
continue;
}
- if (!of_property_read_bool(child, "fsl,port-config")) {
- dev_warn(&pdev->dev, "child node \"%s\" does not have property fsl,port-config\n",
- child->full_name);
+ if (!of_property_present(child, "fsl,port-config")) {
+ dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n",
+ child);
continue;
}
@@ -292,15 +257,15 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
}
if (ret != -EOVERFLOW) {
- dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n",
- i, child->full_name);
+ dev_err(&pdev->dev, "Failed to read u32 at index %d of child %pOF\n",
+ i, child);
continue;
}
if (audmux_type == IMX31_AUDMUX) {
if (i % 2) {
- dev_err(&pdev->dev, "One pdcr value is missing in child node %s\n",
- child->full_name);
+ dev_err(&pdev->dev, "One pdcr value is missing in child node %pOF\n",
+ child);
continue;
}
imx_audmux_v2_configure_port(port, ptcr, pdcr);
@@ -314,12 +279,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
static int imx_audmux_probe(struct platform_device *pdev)
{
- struct resource *res;
- const struct of_device_id *of_id =
- of_match_device(imx_audmux_dt_ids, &pdev->dev);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- audmux_base = devm_ioremap_resource(&pdev->dev, res);
+ audmux_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(audmux_base))
return PTR_ERR(audmux_base);
@@ -330,32 +290,74 @@ static int imx_audmux_probe(struct platform_device *pdev)
audmux_clk = NULL;
}
- if (of_id)
- pdev->id_entry = of_id->data;
- audmux_type = pdev->id_entry->driver_data;
- if (audmux_type == IMX31_AUDMUX)
+ audmux_type = (uintptr_t)of_device_get_match_data(&pdev->dev);
+
+ switch (audmux_type) {
+ case IMX31_AUDMUX:
audmux_debugfs_init();
+ reg_max = 14;
+ break;
+ case IMX21_AUDMUX:
+ reg_max = 6;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported version!\n");
+ return -EINVAL;
+ }
- if (of_id)
- imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
+ regcache = devm_kcalloc(&pdev->dev, reg_max, sizeof(u32), GFP_KERNEL);
+ if (!regcache)
+ return -ENOMEM;
+
+ imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
return 0;
}
-static int imx_audmux_remove(struct platform_device *pdev)
+static void imx_audmux_remove(struct platform_device *pdev)
{
if (audmux_type == IMX31_AUDMUX)
audmux_debugfs_remove();
+}
+
+static int imx_audmux_suspend(struct device *dev)
+{
+ int i;
+
+ clk_prepare_enable(audmux_clk);
+
+ for (i = 0; i < reg_max; i++)
+ regcache[i] = readl(audmux_base + i * 4);
+
+ clk_disable_unprepare(audmux_clk);
return 0;
}
+static int imx_audmux_resume(struct device *dev)
+{
+ int i;
+
+ clk_prepare_enable(audmux_clk);
+
+ for (i = 0; i < reg_max; i++)
+ writel(regcache[i], audmux_base + i * 4);
+
+ clk_disable_unprepare(audmux_clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx_audmux_pm = {
+ SYSTEM_SLEEP_PM_OPS(imx_audmux_suspend, imx_audmux_resume)
+};
+
static struct platform_driver imx_audmux_driver = {
.probe = imx_audmux_probe,
.remove = imx_audmux_remove,
- .id_table = imx_audmux_ids,
.driver = {
.name = DRIVER_NAME,
+ .pm = pm_sleep_ptr(&imx_audmux_pm),
.of_match_table = imx_audmux_dt_ids,
}
};
diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h
index 38a4209af7c6..f75b4d3aeacc 100644
--- a/sound/soc/fsl/imx-audmux.h
+++ b/sound/soc/fsl/imx-audmux.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __IMX_AUDMUX_H
#define __IMX_AUDMUX_H
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
new file mode 100644
index 000000000000..28699d7b75ca
--- /dev/null
+++ b/sound/soc/fsl/imx-card.c
@@ -0,0 +1,943 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2021 NXP
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/simple_card_utils.h>
+
+#include "fsl_sai.h"
+
+#define IMX_CARD_MCLK_22P5792MHZ 22579200
+#define IMX_CARD_MCLK_24P576MHZ 24576000
+
+enum codec_type {
+ CODEC_DUMMY = 0,
+ CODEC_AK5558 = 1,
+ CODEC_AK4458,
+ CODEC_AK4497,
+ CODEC_AK5552,
+ CODEC_CS42888,
+ CODEC_WM8524,
+};
+
+/*
+ * Mapping LRCK fs and frame width, table 3 & 4 in datasheet
+ * @rmin: min rate
+ * @rmax: max rate
+ * @wmin: min frame ratio
+ * @wmax: max frame ratio
+ */
+struct imx_akcodec_fs_mul {
+ unsigned int rmin;
+ unsigned int rmax;
+ unsigned int wmin;
+ unsigned int wmax;
+};
+
+/*
+ * Mapping TDM mode and frame width
+ */
+struct imx_akcodec_tdm_fs_mul {
+ unsigned int min;
+ unsigned int max;
+ unsigned int mul;
+};
+
+/*
+ * struct imx_card_plat_data - specific info for codecs
+ *
+ * @fs_mul: ratio of mclk/fs for normal mode
+ * @tdm_fs_mul: ratio of mclk/fs for tdm mode
+ * @support_rates: supported sample rate
+ * @support_tdm_rates: supported sample rate for tdm mode
+ * @support_channels: supported channels
+ * @support_tdm_channels: supported channels for tdm mode
+ * @num_fs_mul: ARRAY_SIZE of fs_mul
+ * @num_tdm_fs_mul: ARRAY_SIZE of tdm_fs_mul
+ * @num_rates: ARRAY_SIZE of support_rates
+ * @num_tdm_rates: ARRAY_SIZE of support_tdm_rates
+ * @num_channels: ARRAY_SIZE of support_channels
+ * @num_tdm_channels: ARRAY_SIZE of support_tdm_channels
+ * @type: codec type
+ */
+struct imx_card_plat_data {
+ struct imx_akcodec_fs_mul *fs_mul;
+ struct imx_akcodec_tdm_fs_mul *tdm_fs_mul;
+ const u32 *support_rates;
+ const u32 *support_tdm_rates;
+ const u32 *support_channels;
+ const u32 *support_tdm_channels;
+ unsigned int num_fs_mul;
+ unsigned int num_tdm_fs_mul;
+ unsigned int num_rates;
+ unsigned int num_tdm_rates;
+ unsigned int num_channels;
+ unsigned int num_tdm_channels;
+ unsigned int num_codecs;
+ enum codec_type type;
+};
+
+/*
+ * struct dai_link_data - specific info for dai link
+ *
+ * @slots: slot number
+ * @slot_width: slot width value
+ * @cpu_sysclk_id: sysclk id for cpu dai
+ * @one2one_ratio: true if mclk equal to bclk
+ */
+struct dai_link_data {
+ unsigned int slots;
+ unsigned int slot_width;
+ unsigned int cpu_sysclk_id;
+ bool one2one_ratio;
+};
+
+/*
+ * struct imx_card_data - platform device data
+ *
+ * @plat_data: pointer of imx_card_plat_data
+ * @dapm_routes: pointer of dapm_routes
+ * @link_data: private data for dai link
+ * @card: card instance
+ * @num_dapm_routes: number of dapm_routes
+ * @asrc_rate: asrc rates
+ * @asrc_format: asrc format
+ */
+struct imx_card_data {
+ struct imx_card_plat_data *plat_data;
+ struct snd_soc_dapm_route *dapm_routes;
+ struct dai_link_data *link_data;
+ struct snd_soc_card card;
+ int num_dapm_routes;
+ u32 asrc_rate;
+ snd_pcm_format_t asrc_format;
+};
+
+static struct imx_akcodec_fs_mul ak4458_fs_mul[] = {
+ /* Normal, < 32kHz */
+ { .rmin = 8000, .rmax = 24000, .wmin = 256, .wmax = 1024, },
+ /* Normal, 32kHz */
+ { .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, },
+ /* Normal */
+ { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, },
+ /* Double */
+ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 512, },
+ /* Quad */
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 256, },
+ /* Oct */
+ { .rmin = 352800, .rmax = 384000, .wmin = 32, .wmax = 128, },
+ /* Hex */
+ { .rmin = 705600, .rmax = 768000, .wmin = 16, .wmax = 64, },
+};
+
+static struct imx_akcodec_tdm_fs_mul ak4458_tdm_fs_mul[] = {
+ /*
+ * Table 13 - Audio Interface Format
+ * For TDM mode, MCLK should is set to
+ * obtained from 2 * slots * slot_width
+ */
+ { .min = 128, .max = 128, .mul = 256 }, /* TDM128 */
+ { .min = 256, .max = 256, .mul = 512 }, /* TDM256 */
+ { .min = 512, .max = 512, .mul = 1024 }, /* TDM512 */
+};
+
+static struct imx_akcodec_fs_mul ak4497_fs_mul[] = {
+ /**
+ * Table 7 - mapping multiplier and speed mode
+ * Tables 8 & 9 - mapping speed mode and LRCK fs
+ */
+ { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal, <= 32kHz */
+ { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 512, }, /* Normal */
+ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */
+ { .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */
+ { .rmin = 705600, .rmax = 768000, .wmin = 64, .wmax = 64, }, /* Hex */
+};
+
+/*
+ * Auto MCLK selection based on LRCK for Normal Mode
+ * (Table 4 from datasheet)
+ */
+static struct imx_akcodec_fs_mul ak5558_fs_mul[] = {
+ { .rmin = 8000, .rmax = 32000, .wmin = 512, .wmax = 1024, },
+ { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, },
+ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, },
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, },
+ { .rmin = 352800, .rmax = 384000, .wmin = 64, .wmax = 64, },
+ { .rmin = 705600, .rmax = 768000, .wmin = 32, .wmax = 32, },
+};
+
+/*
+ * MCLK and BCLK selection based on TDM mode
+ * because of SAI we also add the restriction: MCLK >= 2 * BCLK
+ * (Table 9 from datasheet)
+ */
+static struct imx_akcodec_tdm_fs_mul ak5558_tdm_fs_mul[] = {
+ { .min = 128, .max = 128, .mul = 256 },
+ { .min = 256, .max = 256, .mul = 512 },
+ { .min = 512, .max = 512, .mul = 1024 },
+};
+
+static struct imx_akcodec_fs_mul cs42888_fs_mul[] = {
+ { .rmin = 8000, .rmax = 48000, .wmin = 256, .wmax = 1024, },
+ { .rmin = 64000, .rmax = 96000, .wmin = 128, .wmax = 512, },
+ { .rmin = 176400, .rmax = 192000, .wmin = 64, .wmax = 256, },
+};
+
+static struct imx_akcodec_tdm_fs_mul cs42888_tdm_fs_mul[] = {
+ { .min = 256, .max = 256, .mul = 256 },
+};
+
+static struct imx_akcodec_fs_mul wm8524_fs_mul[] = {
+ { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1152, },
+ { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, },
+ { .rmin = 88200, .rmax = 96000, .wmin = 128, .wmax = 384, },
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 192, },
+};
+
+static const u32 akcodec_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800, 384000, 705600, 768000,
+};
+
+static const u32 akcodec_tdm_rates[] = {
+ 8000, 16000, 32000, 48000, 96000,
+};
+
+static const u32 ak4458_channels[] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16,
+};
+
+static const u32 ak4458_tdm_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 16,
+};
+
+static const u32 ak5558_channels[] = {
+ 1, 2, 4, 6, 8,
+};
+
+static const u32 ak5558_tdm_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+};
+
+static const u32 cs42888_channels[] = {
+ 1, 2, 4, 6, 8,
+};
+
+static const u32 cs42888_tdm_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+};
+
+static const u32 wm8524_channels[] = {
+ 2,
+};
+
+static bool format_is_dsd(struct snd_pcm_hw_params *params)
+{
+ snd_pcm_format_t format = params_format(params);
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool format_is_tdm(struct dai_link_data *link_data)
+{
+ if (link_data->slots > 2)
+ return true;
+ else
+ return false;
+}
+
+static bool codec_is_akcodec(unsigned int type)
+{
+ switch (type) {
+ case CODEC_AK4458:
+ case CODEC_AK4497:
+ case CODEC_AK5558:
+ case CODEC_AK5552:
+ case CODEC_CS42888:
+ case CODEC_WM8524:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ int slots, int slot_width)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card);
+ const struct imx_card_plat_data *plat_data = data->plat_data;
+ struct dai_link_data *link_data = &data->link_data[rtd->id];
+ unsigned int width = slots * slot_width;
+ unsigned int rate = params_rate(params);
+ int i;
+
+ if (format_is_tdm(link_data)) {
+ for (i = 0; i < plat_data->num_tdm_fs_mul; i++) {
+ /* min = max = slots * slots_width */
+ if (width != plat_data->tdm_fs_mul[i].min)
+ continue;
+ return rate * plat_data->tdm_fs_mul[i].mul;
+ }
+ } else {
+ for (i = 0; i < plat_data->num_fs_mul; i++) {
+ if (rate >= plat_data->fs_mul[i].rmin &&
+ rate <= plat_data->fs_mul[i].rmax) {
+ width = max(width, plat_data->fs_mul[i].wmin);
+ width = min(width, plat_data->fs_mul[i].wmax);
+
+ /* Adjust SAI bclk:mclk ratio */
+ width *= link_data->one2one_ratio ? 1 : 2;
+
+ return rate * width;
+ }
+ }
+ }
+
+ /* Let DAI manage clk frequency by default */
+ return 0;
+}
+
+static int imx_aif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(card);
+ struct dai_link_data *link_data = &data->link_data[rtd->id];
+ struct imx_card_plat_data *plat_data = data->plat_data;
+ struct device *dev = card->dev;
+ struct snd_soc_dai *codec_dai;
+ unsigned long mclk_freq;
+ unsigned int fmt = rtd->dai_link->dai_fmt;
+ unsigned int slots, slot_width;
+ int ret, i;
+
+ slots = link_data->slots;
+ slot_width = link_data->slot_width;
+
+ if (!format_is_tdm(link_data)) {
+ if (format_is_dsd(params)) {
+ slots = 1;
+ slot_width = params_width(params);
+ fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) |
+ SND_SOC_DAIFMT_PDM;
+ } else {
+ slots = 2;
+ slot_width = params_physical_width(params);
+ fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) |
+ SND_SOC_DAIFMT_I2S;
+ }
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, snd_soc_daifmt_clock_provider_flipped(fmt));
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+ BIT(slots) - 1,
+ BIT(slots) - 1,
+ slots, slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+ return ret;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai[%d] fmt: %d\n", i, ret);
+ return ret;
+ }
+
+ if (format_is_tdm(link_data)) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ BIT(slots) - 1,
+ BIT(slots) - 1,
+ slots, slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n", i, ret);
+ return ret;
+ }
+ }
+ }
+
+ /* Set MCLK freq */
+ if (codec_is_akcodec(plat_data->type))
+ mclk_freq = akcodec_get_mclk_rate(substream, params, slots, slot_width);
+ else
+ mclk_freq = params_rate(params) * slots * slot_width;
+
+ if (format_is_dsd(params)) {
+ /* Use the maximum freq from DSD512 (512*44100 = 22579200) */
+ if (!(params_rate(params) % 11025))
+ mclk_freq = IMX_CARD_MCLK_22P5792MHZ;
+ else
+ mclk_freq = IMX_CARD_MCLK_24P576MHZ;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, link_data->cpu_sysclk_id, mclk_freq,
+ SND_SOC_CLOCK_OUT);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n", mclk_freq, ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai mclk rate (%lu): %d\n", mclk_freq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ak5558_hw_rule_rate(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *r)
+{
+ struct dai_link_data *link_data = r->private;
+ struct snd_interval t = { .min = 8000, .max = 8000, };
+ unsigned long mclk_freq;
+ unsigned int fs;
+ int i;
+
+ fs = hw_param_interval(p, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+ fs *= link_data->slots;
+
+ /* Identify maximum supported rate */
+ for (i = 0; i < ARRAY_SIZE(akcodec_rates); i++) {
+ mclk_freq = fs * akcodec_rates[i];
+ /* Adjust SAI bclk:mclk ratio */
+ mclk_freq *= link_data->one2one_ratio ? 1 : 2;
+
+ /* Skip rates for which MCLK is beyond supported value */
+ if (mclk_freq > 36864000)
+ continue;
+
+ if (t.max < akcodec_rates[i])
+ t.max = akcodec_rates[i];
+ }
+
+ return snd_interval_refine(hw_param_interval(p, r->var), &t);
+}
+
+static int imx_aif_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(card);
+ struct dai_link_data *link_data = &data->link_data[rtd->id];
+ static struct snd_pcm_hw_constraint_list constraint_rates;
+ static struct snd_pcm_hw_constraint_list constraint_channels;
+ int ret = 0;
+
+ if (format_is_tdm(link_data)) {
+ constraint_channels.list = data->plat_data->support_tdm_channels;
+ constraint_channels.count = data->plat_data->num_tdm_channels;
+ constraint_rates.list = data->plat_data->support_tdm_rates;
+ constraint_rates.count = data->plat_data->num_tdm_rates;
+ } else {
+ constraint_channels.list = data->plat_data->support_channels;
+ constraint_channels.count = data->plat_data->num_channels;
+ constraint_rates.list = data->plat_data->support_rates;
+ constraint_rates.count = data->plat_data->num_rates;
+ }
+
+ if (constraint_channels.count) {
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraint_channels);
+ if (ret)
+ return ret;
+ }
+
+ if (constraint_rates.count) {
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraint_rates);
+ if (ret)
+ return ret;
+ }
+
+ if (data->plat_data->type == CODEC_AK5558)
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ ak5558_hw_rule_rate, link_data,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+
+ return ret;
+}
+
+static void imx_aif_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ int i;
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
+ snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN);
+}
+
+static const struct snd_soc_ops imx_aif_ops = {
+ .hw_params = imx_aif_hw_params,
+ .startup = imx_aif_startup,
+ .shutdown = imx_aif_shutdown,
+};
+
+static const struct snd_soc_ops imx_aif_ops_be = {
+ .hw_params = imx_aif_hw_params,
+};
+
+static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_interval *rate;
+ struct snd_mask *mask;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ rate->max = data->asrc_rate;
+ rate->min = data->asrc_rate;
+
+ mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(mask);
+ snd_mask_set(mask, (__force unsigned int)data->asrc_format);
+
+ return 0;
+}
+
+static int imx_card_parse_of(struct imx_card_data *data)
+{
+ struct imx_card_plat_data *plat_data = data->plat_data;
+ struct snd_soc_card *card = &data->card;
+ struct snd_soc_dai_link_component *dlc;
+ struct device_node *platform = NULL;
+ struct device_node *codec = NULL;
+ struct device_node *cpu = NULL;
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *link;
+ struct dai_link_data *link_data;
+ struct of_phandle_args args;
+ bool playback_only, capture_only;
+ int ret, num_links;
+ u32 asrc_fmt = 0;
+ u32 width;
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret) {
+ dev_err(dev, "Error parsing card name: %d\n", ret);
+ return ret;
+ }
+
+ /* DAPM routes */
+ if (of_property_present(dev->of_node, "audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret)
+ return ret;
+ }
+
+ /* Populate links */
+ num_links = of_get_child_count(dev->of_node);
+
+ /* Allocate the DAI link array */
+ card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
+
+ data->link_data = devm_kcalloc(dev, num_links, sizeof(*link_data), GFP_KERNEL);
+ if (!data->link_data)
+ return -ENOMEM;
+
+ card->num_links = num_links;
+ link = card->dai_link;
+ link_data = data->link_data;
+
+ for_each_child_of_node_scoped(dev->of_node, np) {
+ dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
+ if (!dlc) {
+ return -ENOMEM;
+ }
+
+ link->cpus = &dlc[0];
+ link->platforms = &dlc[1];
+
+ link->num_cpus = 1;
+ link->num_platforms = 1;
+
+ ret = of_property_read_string(np, "link-name", &link->name);
+ if (ret) {
+ return dev_err_probe(card->dev, ret,
+ "error getting codec dai_link name\n");
+ }
+
+ cpu = of_get_child_by_name(np, "cpu");
+ if (!cpu) {
+ dev_err(dev, "%s: Can't find cpu DT node\n", link->name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0);
+ if (ret) {
+ dev_err_probe(card->dev, ret,
+ "%s: error getting cpu dai info\n", link->name);
+ goto err;
+ }
+
+ if (of_node_name_eq(args.np, "sai")) {
+ /* sai sysclk id */
+ link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1;
+
+ /* sai may support mclk/bclk = 1 */
+ if (of_property_read_bool(np, "fsl,mclk-equal-bclk")) {
+ link_data->one2one_ratio = true;
+ } else {
+ int i;
+
+ /*
+ * i.MX8MQ don't support one2one ratio, then
+ * with ak4497 only 16bit case is supported.
+ */
+ for (i = 0; i < ARRAY_SIZE(ak4497_fs_mul); i++) {
+ if (ak4497_fs_mul[i].rmin == 705600 &&
+ ak4497_fs_mul[i].rmax == 768000) {
+ ak4497_fs_mul[i].wmin = 32;
+ ak4497_fs_mul[i].wmax = 32;
+ }
+ }
+ }
+ }
+
+ link->platforms->of_node = link->cpus->of_node;
+ link->id = args.args[0];
+
+ codec = of_get_child_by_name(np, "codec");
+ if (codec) {
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "%s: codec dai not found\n",
+ link->name);
+ goto err;
+ }
+
+ plat_data->num_codecs = link->num_codecs;
+
+ /* Check the akcodec type */
+ if (!strcmp(link->codecs->dai_name, "ak4458-aif"))
+ plat_data->type = CODEC_AK4458;
+ else if (!strcmp(link->codecs->dai_name, "ak4497-aif"))
+ plat_data->type = CODEC_AK4497;
+ else if (!strcmp(link->codecs->dai_name, "ak5558-aif"))
+ plat_data->type = CODEC_AK5558;
+ else if (!strcmp(link->codecs->dai_name, "ak5552-aif"))
+ plat_data->type = CODEC_AK5552;
+ else if (!strcmp(link->codecs->dai_name, "cs42888"))
+ plat_data->type = CODEC_CS42888;
+ else if (!strcmp(link->codecs->dai_name, "wm8524-hifi"))
+ plat_data->type = CODEC_WM8524;
+
+ } else {
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+ }
+
+ if (!strncmp(link->name, "HiFi-ASRC-FE", 12)) {
+ /* DPCM frontend */
+ link->dynamic = 1;
+ link->dpcm_merged_chan = 1;
+
+ ret = of_property_read_u32(args.np, "fsl,asrc-rate", &data->asrc_rate);
+ if (ret) {
+ dev_err(dev, "failed to get output rate\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = of_property_read_u32(args.np, "fsl,asrc-format", &asrc_fmt);
+ data->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
+ if (ret) {
+ /* Fallback to old binding; translate to asrc_format */
+ ret = of_property_read_u32(args.np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(dev,
+ "failed to decide output format\n");
+ goto err;
+ }
+
+ if (width == 24)
+ data->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ else
+ data->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ }
+ } else if (!strncmp(link->name, "HiFi-ASRC-BE", 12)) {
+ /* DPCM backend */
+ /*
+ * No need to have link->platforms. alloced dlc[1] will be just wasted,
+ * but it won't leak.
+ */
+ link->no_pcm = 1;
+ link->platforms = NULL;
+
+ link->be_hw_params_fixup = be_hw_params_fixup;
+ link->ops = &imx_aif_ops_be;
+ } else {
+ link->ops = &imx_aif_ops;
+ }
+
+ graph_util_parse_link_direction(np, &playback_only, &capture_only);
+ link->playback_only = playback_only;
+ link->capture_only = capture_only;
+
+ /* Get dai fmt */
+ ret = simple_util_parse_daifmt(dev, np, codec,
+ NULL, &link->dai_fmt);
+ if (ret)
+ link->dai_fmt = SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC |
+ SND_SOC_DAIFMT_I2S;
+
+ /* Get tdm slot */
+ snd_soc_of_parse_tdm_slot(np, NULL, NULL,
+ &link_data->slots,
+ &link_data->slot_width);
+ /* default value */
+ if (!link_data->slots)
+ link_data->slots = 2;
+
+ if (!link_data->slot_width)
+ link_data->slot_width = 32;
+
+ link->ignore_pmdown_time = 1;
+ link->stream_name = link->name;
+ link++;
+ link_data++;
+
+ of_node_put(cpu);
+ of_node_put(codec);
+ of_node_put(platform);
+
+ cpu = NULL;
+ codec = NULL;
+ platform = NULL;
+ }
+
+ return 0;
+err:
+ of_node_put(cpu);
+ of_node_put(codec);
+ of_node_put(platform);
+
+ return ret;
+}
+
+static int imx_card_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *link_be = NULL, *link;
+ struct imx_card_plat_data *plat_data;
+ struct imx_card_data *data;
+ int ret, i;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ plat_data = devm_kzalloc(&pdev->dev, sizeof(*plat_data), GFP_KERNEL);
+ if (!plat_data)
+ return -ENOMEM;
+
+ data->plat_data = plat_data;
+ data->card.dev = &pdev->dev;
+ data->card.owner = THIS_MODULE;
+
+ dev_set_drvdata(&pdev->dev, &data->card);
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = imx_card_parse_of(data);
+ if (ret)
+ return ret;
+
+ data->num_dapm_routes = plat_data->num_codecs + 1;
+ data->dapm_routes = devm_kcalloc(&pdev->dev, data->num_dapm_routes,
+ sizeof(struct snd_soc_dapm_route),
+ GFP_KERNEL);
+ if (!data->dapm_routes)
+ return -ENOMEM;
+
+ /* configure the dapm routes */
+ switch (plat_data->type) {
+ case CODEC_AK4458:
+ case CODEC_AK4497:
+ if (plat_data->num_codecs == 1) {
+ data->dapm_routes[0].sink = "Playback";
+ data->dapm_routes[0].source = "CPU-Playback";
+ i = 1;
+ } else {
+ for (i = 0; i < plat_data->num_codecs; i++) {
+ data->dapm_routes[i].sink =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
+ i + 1, "Playback");
+ if (!data->dapm_routes[i].sink)
+ return -ENOMEM;
+ data->dapm_routes[i].source = "CPU-Playback";
+ }
+ }
+ data->dapm_routes[i].sink = "CPU-Playback";
+ data->dapm_routes[i].source = "ASRC-Playback";
+ break;
+ case CODEC_AK5558:
+ case CODEC_AK5552:
+ if (plat_data->num_codecs == 1) {
+ data->dapm_routes[0].sink = "CPU-Capture";
+ data->dapm_routes[0].source = "Capture";
+ i = 1;
+ } else {
+ for (i = 0; i < plat_data->num_codecs; i++) {
+ data->dapm_routes[i].source =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
+ i + 1, "Capture");
+ if (!data->dapm_routes[i].source)
+ return -ENOMEM;
+ data->dapm_routes[i].sink = "CPU-Capture";
+ }
+ }
+ data->dapm_routes[i].sink = "ASRC-Capture";
+ data->dapm_routes[i].source = "CPU-Capture";
+ break;
+ case CODEC_CS42888:
+ data->dapm_routes[0].sink = "Playback";
+ data->dapm_routes[0].source = "CPU-Playback";
+ data->dapm_routes[1].sink = "CPU-Capture";
+ data->dapm_routes[1].source = "Capture";
+ break;
+ case CODEC_WM8524:
+ data->dapm_routes[0].sink = "Playback";
+ data->dapm_routes[0].source = "CPU-Playback";
+ break;
+ default:
+ break;
+ }
+
+ /* default platform data for akcodecs */
+ if (codec_is_akcodec(plat_data->type)) {
+ plat_data->support_rates = akcodec_rates;
+ plat_data->num_rates = ARRAY_SIZE(akcodec_rates);
+ plat_data->support_tdm_rates = akcodec_tdm_rates;
+ plat_data->num_tdm_rates = ARRAY_SIZE(akcodec_tdm_rates);
+
+ switch (plat_data->type) {
+ case CODEC_AK4458:
+ plat_data->fs_mul = ak4458_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(ak4458_fs_mul);
+ plat_data->tdm_fs_mul = ak4458_tdm_fs_mul;
+ plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak4458_tdm_fs_mul);
+ plat_data->support_channels = ak4458_channels;
+ plat_data->num_channels = ARRAY_SIZE(ak4458_channels);
+ plat_data->support_tdm_channels = ak4458_tdm_channels;
+ plat_data->num_tdm_channels = ARRAY_SIZE(ak4458_tdm_channels);
+ break;
+ case CODEC_AK4497:
+ plat_data->fs_mul = ak4497_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(ak4497_fs_mul);
+ plat_data->support_channels = ak4458_channels;
+ plat_data->num_channels = ARRAY_SIZE(ak4458_channels);
+ break;
+ case CODEC_AK5558:
+ case CODEC_AK5552:
+ plat_data->fs_mul = ak5558_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(ak5558_fs_mul);
+ plat_data->tdm_fs_mul = ak5558_tdm_fs_mul;
+ plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak5558_tdm_fs_mul);
+ plat_data->support_channels = ak5558_channels;
+ plat_data->num_channels = ARRAY_SIZE(ak5558_channels);
+ plat_data->support_tdm_channels = ak5558_tdm_channels;
+ plat_data->num_tdm_channels = ARRAY_SIZE(ak5558_tdm_channels);
+ break;
+ case CODEC_CS42888:
+ plat_data->fs_mul = cs42888_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(cs42888_fs_mul);
+ plat_data->tdm_fs_mul = cs42888_tdm_fs_mul;
+ plat_data->num_tdm_fs_mul = ARRAY_SIZE(cs42888_tdm_fs_mul);
+ plat_data->support_channels = cs42888_channels;
+ plat_data->num_channels = ARRAY_SIZE(cs42888_channels);
+ plat_data->support_tdm_channels = cs42888_tdm_channels;
+ plat_data->num_tdm_channels = ARRAY_SIZE(cs42888_tdm_channels);
+ break;
+ case CODEC_WM8524:
+ plat_data->fs_mul = wm8524_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(wm8524_fs_mul);
+ plat_data->support_channels = wm8524_channels;
+ plat_data->num_channels = ARRAY_SIZE(wm8524_channels);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* with asrc as front end */
+ if (data->card.num_links == 3) {
+ data->card.dapm_routes = data->dapm_routes;
+ data->card.num_dapm_routes = data->num_dapm_routes;
+ for_each_card_prelinks(&data->card, i, link) {
+ if (link->no_pcm == 1)
+ link_be = link;
+ }
+ for_each_card_prelinks(&data->card, i, link) {
+ if (link->dynamic == 1 && link_be) {
+ link->playback_only = link_be->playback_only;
+ link->capture_only = link_be->capture_only;
+ }
+ }
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+
+ return 0;
+}
+
+static const struct of_device_id imx_card_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-card", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, imx_card_dt_ids);
+
+static struct platform_driver imx_card_driver = {
+ .driver = {
+ .name = "imx-card",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = imx_card_dt_ids,
+ },
+ .probe = imx_card_probe,
+};
+module_platform_driver(imx_card_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-card");
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
index 20e7400e2611..3ef92f6dfc6b 100644
--- a/sound/soc/fsl/imx-es8328.c
+++ b/sound/soc/fsl/imx-es8328.c
@@ -1,21 +1,13 @@
-/*
- * Copyright 2012 Freescale Semiconductor, Inc.
- * Copyright 2012 Linaro Ltd.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/gpio.h>
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright 2012 Freescale Semiconductor, Inc.
+// Copyright 2012 Linaro Ltd.
+
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/i2c.h>
-#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
@@ -30,12 +22,11 @@ struct imx_es8328_data {
struct snd_soc_card card;
char codec_dai_name[DAI_NAME_SIZE];
char platform_name[DAI_NAME_SIZE];
- int jack_gpio;
+ struct gpio_desc *jack_gpiod;
};
static struct snd_soc_jack_gpio headset_jack_gpios[] = {
{
- .gpio = -1,
.name = "headset-gpio",
.report = SND_JACK_HEADSET,
.invert = 0,
@@ -44,6 +35,16 @@ static struct snd_soc_jack_gpio headset_jack_gpios[] = {
};
static struct snd_soc_jack headset_jack;
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -51,15 +52,17 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
struct imx_es8328_data, card);
int ret = 0;
- /* Headphone jack detection */
- if (gpio_is_valid(data->jack_gpio)) {
- ret = snd_soc_card_jack_new(rtd->card, "Headphone",
- SND_JACK_HEADPHONE | SND_JACK_BTN_0,
- &headset_jack, NULL, 0);
+ if (data->jack_gpiod) {
+ /* Headphone jack detection */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headphone",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
if (ret)
return ret;
- headset_jack_gpios[0].gpio = data->jack_gpio;
+ headset_jack_gpios[0].desc = data->jack_gpiod;
ret = snd_soc_jack_add_gpios(&headset_jack,
ARRAY_SIZE(headset_jack_gpios),
headset_jack_gpios);
@@ -75,12 +78,18 @@ static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
};
+static const struct snd_kcontrol_new imx_es8328_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
static int imx_es8328_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *ssi_np = NULL, *codec_np = NULL;
struct platform_device *ssi_pdev;
struct imx_es8328_data *data;
+ struct snd_soc_dai_link_component *comp;
u32 int_port, ext_port;
int ret;
struct device *dev = &pdev->dev;
@@ -93,6 +102,7 @@ static int imx_es8328_probe(struct platform_device *pdev)
if (int_port > MUX_PORT_MAX || int_port == 0) {
dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
MUX_PORT_MAX);
+ ret = -EINVAL;
goto fail;
}
@@ -151,47 +161,72 @@ static int imx_es8328_probe(struct platform_device *pdev)
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
- goto fail;
+ goto put_device;
+ }
+
+ comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
+ if (!comp) {
+ ret = -ENOMEM;
+ goto put_device;
}
data->dev = dev;
- data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
+ data->jack_gpiod = devm_gpiod_get_optional(dev, "jack", GPIOD_IN);
+ if (IS_ERR(data->jack_gpiod)) {
+ ret = PTR_ERR(data->jack_gpiod);
+ goto put_device;
+ }
+
+ /*
+ * CPU == Platform
+ * platform is using soc-generic-dmaengine-pcm
+ */
+ data->dai.cpus =
+ data->dai.platforms = &comp[0];
+ data->dai.codecs = &comp[1];
+
+ data->dai.num_cpus = 1;
+ data->dai.num_codecs = 1;
+ data->dai.num_platforms = 1;
data->dai.name = "hifi";
data->dai.stream_name = "hifi";
- data->dai.codec_dai_name = "es8328-hifi-analog";
- data->dai.codec_of_node = codec_np;
- data->dai.cpu_of_node = ssi_np;
- data->dai.platform_of_node = ssi_np;
+ data->dai.codecs->dai_name = "es8328-hifi-analog";
+ data->dai.codecs->of_node = codec_np;
+ data->dai.cpus->of_node = ssi_np;
data->dai.init = &imx_es8328_dai_init;
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_CBP_CFP;
data->card.dev = dev;
data->card.dapm_widgets = imx_es8328_dapm_widgets;
data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
+ data->card.controls = imx_es8328_controls;
+ data->card.num_controls = ARRAY_SIZE(imx_es8328_controls);
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret) {
dev_err(dev, "Unable to parse card name\n");
- goto fail;
+ goto put_device;
}
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret) {
dev_err(dev, "Unable to parse routing: %d\n", ret);
- goto fail;
+ goto put_device;
}
data->card.num_links = 1;
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
- ret = snd_soc_register_card(&data->card);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
dev_err(dev, "Unable to register: %d\n", ret);
- goto fail;
+ goto put_device;
}
platform_set_drvdata(pdev, data);
+put_device:
+ put_device(&ssi_pdev->dev);
fail:
of_node_put(ssi_np);
of_node_put(codec_np);
@@ -199,18 +234,6 @@ fail:
return ret;
}
-static int imx_es8328_remove(struct platform_device *pdev)
-{
- struct imx_es8328_data *data = platform_get_drvdata(pdev);
-
- snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
- headset_jack_gpios);
-
- snd_soc_unregister_card(&data->card);
-
- return 0;
-}
-
static const struct of_device_id imx_es8328_dt_ids[] = {
{ .compatible = "fsl,imx-audio-es8328", },
{ /* sentinel */ }
@@ -223,7 +246,6 @@ static struct platform_driver imx_es8328_driver = {
.of_match_table = imx_es8328_dt_ids,
},
.probe = imx_es8328_probe,
- .remove = imx_es8328_remove,
};
module_platform_driver(imx_es8328_driver);
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
new file mode 100644
index 000000000000..1115189cc640
--- /dev/null
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2017-2020 NXP
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/hdmi-codec.h>
+#include "fsl_sai.h"
+
+/**
+ * struct cpu_priv - CPU private data
+ * @sysclk_id: SYSCLK ids for set_sysclk()
+ * @slot_width: Slot width of each frame
+ *
+ * Note: [1] for tx and [0] for rx
+ */
+struct cpu_priv {
+ u32 sysclk_id[2];
+ u32 slot_width;
+};
+
+struct imx_hdmi_data {
+ struct snd_soc_dai_link dai;
+ struct snd_soc_card card;
+ struct snd_soc_jack hdmi_jack;
+ struct snd_soc_jack_pin hdmi_jack_pin;
+ struct cpu_priv cpu_priv;
+ u32 dai_fmt;
+};
+
+static int imx_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct imx_hdmi_data *data = snd_soc_card_get_drvdata(rtd->card);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ struct device *dev = card->dev;
+ u32 slot_width = data->cpu_priv.slot_width;
+ int ret;
+
+ /* MCLK always is (256 or 192) * rate. */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, data->cpu_priv.sysclk_id[tx],
+ 8 * slot_width * params_rate(params),
+ tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0, 2, slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops imx_hdmi_ops = {
+ .hw_params = imx_hdmi_hw_params,
+};
+
+static const struct snd_soc_dapm_widget imx_hdmi_widgets[] = {
+ SND_SOC_DAPM_LINE("HDMI Jack", NULL),
+};
+
+static int imx_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct imx_hdmi_data *data = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ data->hdmi_jack_pin.pin = "HDMI Jack";
+ data->hdmi_jack_pin.mask = SND_JACK_LINEOUT;
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new_pins(card, "HDMI Jack", SND_JACK_LINEOUT,
+ &data->hdmi_jack,
+ &data->hdmi_jack_pin, 1);
+ if (ret) {
+ dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, &data->hdmi_jack, NULL);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(card->dev, "Can't set HDMI Jack %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static int imx_hdmi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ bool hdmi_out = of_property_read_bool(np, "hdmi-out");
+ bool hdmi_in = of_property_read_bool(np, "hdmi-in");
+ struct snd_soc_dai_link_component *dlc;
+ struct device_node *cpu_np;
+ struct imx_hdmi_data *data;
+ int ret;
+
+ dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ cpu_np = of_parse_phandle(np, "audio-cpu", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ data->dai.cpus = &dlc[0];
+ data->dai.num_cpus = 1;
+ data->dai.platforms = &dlc[1];
+ data->dai.num_platforms = 1;
+ data->dai.codecs = &dlc[2];
+ data->dai.num_codecs = 1;
+
+ data->dai.name = "i.MX HDMI";
+ data->dai.stream_name = "i.MX HDMI";
+ data->dai.cpus->of_node = cpu_np;
+ data->dai.platforms->of_node = cpu_np;
+ data->dai.ops = &imx_hdmi_ops;
+ data->dai.playback_only = true;
+ data->dai.capture_only = false;
+ data->dai.init = imx_hdmi_init;
+
+ if (of_node_name_eq(cpu_np, "sai")) {
+ data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
+ data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
+ }
+
+ if (of_device_is_compatible(np, "fsl,imx-audio-sii902x")) {
+ data->dai_fmt = SND_SOC_DAIFMT_LEFT_J;
+ data->cpu_priv.slot_width = 24;
+ } else {
+ data->dai_fmt = SND_SOC_DAIFMT_I2S;
+ data->cpu_priv.slot_width = 32;
+ }
+
+ if ((hdmi_out && hdmi_in) || (!hdmi_out && !hdmi_in)) {
+ dev_err(&pdev->dev, "Invalid HDMI DAI link\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (hdmi_out) {
+ data->dai.playback_only = true;
+ data->dai.capture_only = false;
+ data->dai.codecs->dai_name = "i2s-hifi";
+ data->dai.codecs->name = "hdmi-audio-codec.1";
+ data->dai.dai_fmt = data->dai_fmt |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC;
+ }
+
+ if (hdmi_in) {
+ data->dai.playback_only = false;
+ data->dai.capture_only = true;
+ data->dai.codecs->dai_name = "i2s-hifi";
+ data->dai.codecs->name = "hdmi-audio-codec.2";
+ data->dai.dai_fmt = data->dai_fmt |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP;
+ }
+
+ data->card.dapm_widgets = imx_hdmi_widgets;
+ data->card.num_dapm_widgets = ARRAY_SIZE(imx_hdmi_widgets);
+ data->card.dev = &pdev->dev;
+ data->card.owner = THIS_MODULE;
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+
+ data->card.num_links = 1;
+ data->card.dai_link = &data->dai;
+
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+ goto fail;
+ }
+
+fail:
+ of_node_put(cpu_np);
+
+ return ret;
+}
+
+static const struct of_device_id imx_hdmi_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-hdmi", },
+ { .compatible = "fsl,imx-audio-sii902x", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
+
+static struct platform_driver imx_hdmi_driver = {
+ .driver = {
+ .name = "imx-hdmi",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = imx_hdmi_dt_ids,
+ },
+ .probe = imx_hdmi_probe,
+};
+module_platform_driver(imx_hdmi_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX hdmi audio ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-hdmi");
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
deleted file mode 100644
index bb0459018b45..000000000000
--- a/sound/soc/fsl/imx-mc13783.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec
- *
- * Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch>
- *
- * Heavly based on phycore-mc13783:
- * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <asm/mach-types.h>
-
-#include "../codecs/mc13783.h"
-#include "imx-ssi.h"
-#include "imx-audmux.h"
-
-#define FMT_SSI (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 4, 16);
- if (ret)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, MC13783_CLK_CLIA, 26000000, 0);
- if (ret)
- return ret;
-
- return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
-}
-
-static struct snd_soc_ops imx_mc13783_hifi_ops = {
- .hw_params = imx_mc13783_hifi_hw_params,
-};
-
-static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = {
- {
- .name = "MC13783",
- .stream_name = "Sound",
- .codec_dai_name = "mc13783-hifi",
- .codec_name = "mc13783-codec",
- .cpu_dai_name = "imx-ssi.0",
- .platform_name = "imx-ssi.0",
- .ops = &imx_mc13783_hifi_ops,
- .symmetric_rates = 1,
- .dai_fmt = FMT_SSI,
- },
-};
-
-static const struct snd_soc_dapm_widget imx_mc13783_widget[] = {
- SND_SOC_DAPM_MIC("Mic", NULL),
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-static const struct snd_soc_dapm_route imx_mc13783_routes[] = {
- {"Speaker", NULL, "LSP"},
- {"Headphone", NULL, "HSL"},
- {"Headphone", NULL, "HSR"},
-
- {"MC1LIN", NULL, "MC1 Bias"},
- {"MC2IN", NULL, "MC2 Bias"},
- {"MC1 Bias", NULL, "Mic"},
- {"MC2 Bias", NULL, "Mic"},
-};
-
-static struct snd_soc_card imx_mc13783 = {
- .name = "imx_mc13783",
- .owner = THIS_MODULE,
- .dai_link = imx_mc13783_dai_mc13783,
- .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783),
- .dapm_widgets = imx_mc13783_widget,
- .num_dapm_widgets = ARRAY_SIZE(imx_mc13783_widget),
- .dapm_routes = imx_mc13783_routes,
- .num_dapm_routes = ARRAY_SIZE(imx_mc13783_routes),
-};
-
-static int imx_mc13783_probe(struct platform_device *pdev)
-{
- int ret;
-
- imx_mc13783.dev = &pdev->dev;
-
- ret = snd_soc_register_card(&imx_mc13783);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- return ret;
- }
-
- if (machine_is_mx31_3ds() || machine_is_mx31moboard()) {
- imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
- IMX_AUDMUX_V2_PTCR_SYN,
- IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
- IMX_AUDMUX_V2_PDCR_MODE(1) |
- IMX_AUDMUX_V2_PDCR_INMMASK(0xfc));
- imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0,
- IMX_AUDMUX_V2_PTCR_SYN |
- IMX_AUDMUX_V2_PTCR_TFSDIR |
- IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
- IMX_AUDMUX_V2_PTCR_TCLKDIR |
- IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
- IMX_AUDMUX_V2_PTCR_RFSDIR |
- IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
- IMX_AUDMUX_V2_PTCR_RCLKDIR |
- IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4),
- IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4));
- } else if (machine_is_mx27_3ds()) {
- imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
- IMX_AUDMUX_V1_PCR_SYN |
- IMX_AUDMUX_V1_PCR_TFSDIR |
- IMX_AUDMUX_V1_PCR_TCLKDIR |
- IMX_AUDMUX_V1_PCR_RFSDIR |
- IMX_AUDMUX_V1_PCR_RCLKDIR |
- IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
- IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
- IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4)
- );
- imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4,
- IMX_AUDMUX_V1_PCR_SYN |
- IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
- );
- }
-
- return ret;
-}
-
-static int imx_mc13783_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&imx_mc13783);
-
- return 0;
-}
-
-static struct platform_driver imx_mc13783_audio_driver = {
- .driver = {
- .name = "imx_mc13783",
- },
- .probe = imx_mc13783_probe,
- .remove = imx_mc13783_remove
-};
-
-module_platform_driver(imx_mc13783_audio_driver);
-
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch");
-MODULE_DESCRIPTION("imx with mc13783 codec ALSA SoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx_mc13783");
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index f3d3d1ffa84e..4fa208d6a032 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer
*
@@ -5,11 +6,6 @@
*
* This code is based on code copyrighted by Freescale,
* Liam Girdwood, Javier Martin and probably others.
- *
- * 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/platform_device.h>
#include <linux/dmaengine.h>
@@ -33,48 +29,20 @@ static bool filter(struct dma_chan *chan, void *param)
return true;
}
-static const struct snd_pcm_hardware imx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = 65535, /* Limited by SDMA engine */
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
- .pcm_hardware = &imx_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.compat_filter_fn = filter,
- .prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
};
-int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
+int imx_pcm_dma_init(struct platform_device *pdev)
{
struct snd_dmaengine_pcm_config *config;
- struct snd_pcm_hardware *pcm_hardware;
config = devm_kzalloc(&pdev->dev,
sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
if (!config)
return -ENOMEM;
*config = imx_dmaengine_pcm_config;
- if (size)
- config->prealloc_buffer_size = size;
-
- pcm_hardware = devm_kzalloc(&pdev->dev,
- sizeof(struct snd_pcm_hardware), GFP_KERNEL);
- *pcm_hardware = imx_pcm_hardware;
- if (size)
- pcm_hardware->buffer_bytes_max = size;
-
- config->pcm_hardware = pcm_hardware;
return devm_snd_dmaengine_pcm_register(&pdev->dev,
config,
@@ -82,4 +50,5 @@ int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
+MODULE_DESCRIPTION("Freescale i.MX PCM DMA interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 49d7513f429e..83de3ae33691 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -1,16 +1,11 @@
-/*
- * imx-pcm-fiq.c -- ALSA Soc Audio Layer
- *
- * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This code is based on code copyrighted by Freescale,
- * Liam Girdwood, Javier Martin and probably others.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+// imx-pcm-fiq.c -- ALSA Soc Audio Layer
+//
+// Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+//
+// This code is based on code copyrighted by Freescale,
+// Liam Girdwood, Javier Martin and probably others.
+
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -74,8 +69,9 @@ static struct fiq_handler fh = {
.name = DRV_NAME,
};
-static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int snd_imx_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -85,12 +81,12 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
iprtd->offset = 0;
iprtd->poll_time_ns = 1000000000 / params_rate(params) *
params_period_size(params);
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
-static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
+static int snd_imx_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -109,7 +105,8 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
static int imx_pcm_fiq;
-static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int snd_imx_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -146,7 +143,9 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+snd_imx_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -154,7 +153,7 @@ static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream
return bytes_to_frames(substream->runtime, iprtd->offset);
}
-static struct snd_pcm_hardware snd_imx_hardware = {
+static const struct snd_pcm_hardware snd_imx_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
@@ -170,7 +169,8 @@ static struct snd_pcm_hardware snd_imx_hardware = {
.fifo_size = 0,
};
-static int snd_imx_open(struct snd_pcm_substream *substream)
+static int snd_imx_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd;
@@ -185,8 +185,7 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
atomic_set(&iprtd->playing, 0);
atomic_set(&iprtd->capturing, 0);
- hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- iprtd->hrt.function = snd_hrtimer_callback;
+ hrtimer_setup(&iprtd->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@@ -199,7 +198,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_imx_close(struct snd_pcm_substream *substream)
+static int snd_imx_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@@ -211,51 +211,6 @@ static int snd_imx_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-
- pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret,
- runtime->dma_area,
- &runtime->dma_addr,
- runtime->dma_bytes);
- return ret;
-}
-
-static struct snd_pcm_ops imx_pcm_ops = {
- .open = snd_imx_open,
- .close = snd_imx_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_imx_pcm_hw_params,
- .prepare = snd_imx_pcm_prepare,
- .trigger = snd_imx_pcm_trigger,
- .pointer = snd_imx_pcm_pointer,
- .mmap = snd_imx_pcm_mmap,
-};
-
-static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = IMX_SSI_DMABUF_SIZE;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
-
- return 0;
-}
-
static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
@@ -266,26 +221,15 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (ret)
return ret;
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = imx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- return ret;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = imx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- return ret;
- }
-
- return 0;
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
+ pcm->card->dev,
+ IMX_SSI_DMABUF_SIZE);
}
-static int ssi_irq = 0;
+static int ssi_irq;
-static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
+static int snd_imx_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct snd_pcm_substream *substream;
@@ -315,38 +259,22 @@ static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static void imx_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-static void imx_pcm_fiq_free(struct snd_pcm *pcm)
+static void snd_imx_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
mxc_set_irq_fiq(ssi_irq, 0);
release_fiq(&fh);
- imx_pcm_free(pcm);
}
-static struct snd_soc_platform_driver imx_soc_platform_fiq = {
- .ops = &imx_pcm_ops,
- .pcm_new = imx_pcm_fiq_new,
- .pcm_free = imx_pcm_fiq_free,
+static const struct snd_soc_component_driver imx_soc_component_fiq = {
+ .open = snd_imx_open,
+ .close = snd_imx_close,
+ .hw_params = snd_imx_pcm_hw_params,
+ .prepare = snd_imx_pcm_prepare,
+ .trigger = snd_imx_pcm_trigger,
+ .pointer = snd_imx_pcm_pointer,
+ .pcm_construct = snd_imx_pcm_new,
+ .pcm_destruct = snd_imx_pcm_free,
};
int imx_pcm_fiq_init(struct platform_device *pdev,
@@ -370,7 +298,8 @@ int imx_pcm_fiq_init(struct platform_device *pdev,
params->dma_params_tx->maxburst = 4;
params->dma_params_rx->maxburst = 6;
- ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq);
+ ret = devm_snd_soc_register_component(&pdev->dev, &imx_soc_component_fiq,
+ NULL, 0);
if (ret)
goto failed_register;
@@ -386,8 +315,8 @@ EXPORT_SYMBOL_GPL(imx_pcm_fiq_init);
void imx_pcm_fiq_exit(struct platform_device *pdev)
{
- snd_soc_unregister_platform(&pdev->dev);
}
EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit);
+MODULE_DESCRIPTION("Freescale i.MX PCM FIQ handler");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
new file mode 100644
index 000000000000..edab68ae8366
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -0,0 +1,852 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2021 NXP
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/rpmsg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+#include "imx-pcm.h"
+#include "fsl_rpmsg.h"
+#include "imx-pcm-rpmsg.h"
+
+static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
+ .period_bytes_min = 512,
+ .period_bytes_max = 65536,
+ .periods_min = 2,
+ .periods_max = 6000,
+ .fifo_size = 0,
+};
+
+static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
+ struct rpmsg_info *info)
+{
+ struct rpmsg_device *rpdev = info->rpdev;
+ int ret = 0;
+
+ mutex_lock(&info->msg_lock);
+ if (!rpdev) {
+ dev_err(info->dev, "rpmsg channel not ready\n");
+ mutex_unlock(&info->msg_lock);
+ return -EINVAL;
+ }
+
+ dev_dbg(&rpdev->dev, "send cmd %d\n", msg->s_msg.header.cmd);
+
+ if (!(msg->s_msg.header.type == MSG_TYPE_C))
+ reinit_completion(&info->cmd_complete);
+
+ ret = rpmsg_send(rpdev->ept, (void *)&msg->s_msg,
+ sizeof(struct rpmsg_s_msg));
+ if (ret) {
+ dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+ mutex_unlock(&info->msg_lock);
+ return ret;
+ }
+
+ /* No receive msg for TYPE_C command */
+ if (msg->s_msg.header.type == MSG_TYPE_C) {
+ mutex_unlock(&info->msg_lock);
+ return 0;
+ }
+
+ /* wait response from rpmsg */
+ ret = wait_for_completion_timeout(&info->cmd_complete,
+ msecs_to_jiffies(RPMSG_TIMEOUT));
+ if (!ret) {
+ dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n",
+ msg->s_msg.header.cmd);
+ mutex_unlock(&info->msg_lock);
+ return -ETIMEDOUT;
+ }
+
+ memcpy(&msg->r_msg, &info->r_msg, sizeof(struct rpmsg_r_msg));
+ memcpy(&info->msg[msg->r_msg.header.cmd].r_msg,
+ &msg->r_msg, sizeof(struct rpmsg_r_msg));
+
+ /*
+ * Reset the buffer pointer to be zero, actully we have
+ * set the buffer pointer to be zero in imx_rpmsg_terminate_all
+ * But if there is timer task queued in queue, after it is
+ * executed the buffer pointer will be changed, so need to
+ * reset it again with TERMINATE command.
+ */
+ switch (msg->s_msg.header.cmd) {
+ case TX_TERMINATE:
+ info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
+ break;
+ case RX_TERMINATE:
+ info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd,
+ info->r_msg.param.resp);
+
+ mutex_unlock(&info->msg_lock);
+
+ return 0;
+}
+
+static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream,
+ struct rpmsg_msg *msg,
+ struct rpmsg_info *info)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ /*
+ * Queue the work to workqueue.
+ * If the queue is full, drop the message.
+ */
+ spin_lock_irqsave(&info->wq_lock, flags);
+ if (info->work_write_index != info->work_read_index) {
+ int index = info->work_write_index;
+
+ memcpy(&info->work_list[index].msg, msg,
+ sizeof(struct rpmsg_s_msg));
+
+ queue_work(info->rpmsg_wq, &info->work_list[index].work);
+ info->work_write_index++;
+ info->work_write_index %= WORK_MAX_NUM;
+ } else {
+ info->msg_drop_count[substream->stream]++;
+ ret = -EPIPE;
+ }
+ spin_unlock_irqrestore(&info->wq_lock, flags);
+
+ return ret;
+}
+
+static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_HW_PARAM];
+ msg->s_msg.header.cmd = TX_HW_PARAM;
+ } else {
+ msg = &info->msg[RX_HW_PARAM];
+ msg->s_msg.header.cmd = RX_HW_PARAM;
+ }
+
+ msg->s_msg.param.rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ msg->s_msg.param.format = RPMSG_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ msg->s_msg.param.format = RPMSG_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ msg->s_msg.param.format = RPMSG_DSD_U16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ msg->s_msg.param.format = RPMSG_DSD_U32_LE;
+ break;
+ default:
+ msg->s_msg.param.format = RPMSG_S32_LE;
+ break;
+ }
+
+ switch (params_channels(params)) {
+ case 1:
+ msg->s_msg.param.channels = RPMSG_CH_LEFT;
+ break;
+ case 2:
+ msg->s_msg.param.channels = RPMSG_CH_STEREO;
+ break;
+ default:
+ msg->s_msg.param.channels = params_channels(params);
+ break;
+ }
+
+ info->send_message(msg, info);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+ unsigned int pos = 0;
+ int buffer_tail = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ else
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+
+ buffer_tail = msg->r_msg.param.buffer_tail;
+ pos = buffer_tail * snd_pcm_lib_period_bytes(substream);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static void imx_rpmsg_timer_callback(struct timer_list *t)
+{
+ struct stream_timer *stream_timer =
+ timer_container_of(stream_timer, t, timer);
+ struct snd_pcm_substream *substream = stream_timer->substream;
+ struct rpmsg_info *info = stream_timer->info;
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = TX_PERIOD_DONE;
+ } else {
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = RX_PERIOD_DONE;
+ }
+
+ imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct snd_pcm_hardware pcm_hardware;
+ struct rpmsg_msg *msg;
+ int ret = 0;
+ int cmd;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_OPEN];
+ msg->s_msg.header.cmd = TX_OPEN;
+
+ /* reinitialize buffer counter*/
+ cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
+
+ } else {
+ msg = &info->msg[RX_OPEN];
+ msg->s_msg.header.cmd = RX_OPEN;
+
+ /* reinitialize buffer counter*/
+ cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
+ }
+
+ info->send_message(msg, info);
+
+ pcm_hardware = imx_rpmsg_pcm_hardware;
+ pcm_hardware.buffer_bytes_max = rpmsg->buffer_size[substream->stream];
+ pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2;
+
+ snd_soc_set_runtime_hwparams(substream, &pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ info->msg_drop_count[substream->stream] = 0;
+
+ /* Create timer*/
+ info->stream_timer[substream->stream].info = info;
+ info->stream_timer[substream->stream].substream = substream;
+ timer_setup(&info->stream_timer[substream->stream].timer,
+ imx_rpmsg_timer_callback, 0);
+ return ret;
+}
+
+static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ /* Flush work in workqueue to make TX_CLOSE is the last message */
+ flush_workqueue(info->rpmsg_wq);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_CLOSE];
+ msg->s_msg.header.cmd = TX_CLOSE;
+ } else {
+ msg = &info->msg[RX_CLOSE];
+ msg->s_msg.header.cmd = RX_CLOSE;
+ }
+
+ info->send_message(msg, info);
+
+ timer_delete(&info->stream_timer[substream->stream].timer);
+
+ rtd->dai_link->ignore_suspend = 0;
+
+ if (info->msg_drop_count[substream->stream])
+ dev_warn(rtd->dev, "Msg is dropped!, number is %d\n",
+ info->msg_drop_count[substream->stream]);
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+
+ /*
+ * NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts
+ * four conditions to determine the lpa is enabled.
+ */
+ if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) &&
+ rpmsg->enable_lpa) {
+ /*
+ * Ignore suspend operation in low power mode
+ * M core will continue playback music on A core suspend.
+ */
+ rtd->dai_link->ignore_suspend = 1;
+ rpmsg->force_lpa = 1;
+ } else {
+ rpmsg->force_lpa = 0;
+ }
+
+ return 0;
+}
+
+static void imx_rpmsg_pcm_dma_complete(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int imx_rpmsg_prepare_and_submit(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_BUFFER];
+ msg->s_msg.header.cmd = TX_BUFFER;
+ } else {
+ msg = &info->msg[RX_BUFFER];
+ msg->s_msg.header.cmd = RX_BUFFER;
+ }
+
+ /* Send buffer address and buffer size */
+ msg->s_msg.param.buffer_addr = substream->runtime->dma_addr;
+ msg->s_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ msg->s_msg.param.period_size = snd_pcm_lib_period_bytes(substream);
+ msg->s_msg.param.buffer_tail = 0;
+
+ info->num_period[substream->stream] = msg->s_msg.param.buffer_size /
+ msg->s_msg.param.period_size;
+
+ info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete;
+ info->callback_param[substream->stream] = substream;
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_async_issue_pending(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_START];
+ msg->s_msg.header.cmd = TX_START;
+ } else {
+ msg = &info->msg[RX_START];
+ msg->s_msg.header.cmd = RX_START;
+ }
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_restart(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_RESTART];
+ msg->s_msg.header.cmd = TX_RESTART;
+ } else {
+ msg = &info->msg[RX_RESTART];
+ msg->s_msg.header.cmd = RX_RESTART;
+ }
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_pause(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_PAUSE];
+ msg->s_msg.header.cmd = TX_PAUSE;
+ } else {
+ msg = &info->msg[RX_PAUSE];
+ msg->s_msg.header.cmd = RX_PAUSE;
+ }
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_terminate_all(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+ int cmd;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_TERMINATE];
+ msg->s_msg.header.cmd = TX_TERMINATE;
+ /* Clear buffer count*/
+ cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
+ } else {
+ msg = &info->msg[RX_TERMINATE];
+ msg->s_msg.header.cmd = RX_TERMINATE;
+ /* Clear buffer count*/
+ cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
+ }
+
+ timer_delete(&info->stream_timer[substream->stream].timer);
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = imx_rpmsg_prepare_and_submit(component, substream);
+ if (ret)
+ return ret;
+ ret = imx_rpmsg_async_issue_pending(component, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rpmsg->force_lpa)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = imx_rpmsg_restart(component, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (!rpmsg->force_lpa) {
+ if (runtime->info & SNDRV_PCM_INFO_PAUSE)
+ ret = imx_rpmsg_pause(component, substream);
+ else
+ ret = imx_rpmsg_terminate_all(component, substream);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = imx_rpmsg_pause(component, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = imx_rpmsg_terminate_all(component, substream);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * imx_rpmsg_pcm_ack
+ *
+ * Send the period index to M core through rpmsg, but not send
+ * all the period index to M core, reduce some unnessesary msg
+ * to reduce the pressure of rpmsg bandwidth.
+ */
+static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ snd_pcm_uframes_t period_size = runtime->period_size;
+ snd_pcm_sframes_t avail;
+ struct timer_list *timer;
+ struct rpmsg_msg *msg;
+ unsigned long flags;
+ int buffer_tail = 0;
+ int written_num;
+
+ if (!rpmsg->force_lpa)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = TX_PERIOD_DONE;
+ } else {
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = RX_PERIOD_DONE;
+ }
+
+ msg->s_msg.header.type = MSG_TYPE_C;
+
+ buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) %
+ snd_pcm_lib_buffer_bytes(substream));
+ buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream);
+
+ /* There is update for period index */
+ if (buffer_tail != msg->s_msg.param.buffer_tail) {
+ written_num = buffer_tail - msg->s_msg.param.buffer_tail;
+ if (written_num < 0)
+ written_num += runtime->periods;
+
+ msg->s_msg.param.buffer_tail = buffer_tail;
+
+ /* The notification message is updated to latest */
+ spin_lock_irqsave(&info->lock[substream->stream], flags);
+ memcpy(&info->notify[substream->stream], msg,
+ sizeof(struct rpmsg_s_msg));
+ info->notify_updated[substream->stream] = true;
+ spin_unlock_irqrestore(&info->lock[substream->stream], flags);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ avail = snd_pcm_playback_hw_avail(runtime);
+ else
+ avail = snd_pcm_capture_hw_avail(runtime);
+
+ timer = &info->stream_timer[substream->stream].timer;
+ /*
+ * If the data in the buffer is less than one period before
+ * this fill, which means the data may not enough on M
+ * core side, we need to send message immediately to let
+ * M core know the pointer is updated.
+ * if there is more than one period data in the buffer before
+ * this fill, which means the data is enough on M core side,
+ * we can delay one period (using timer) to send the message
+ * for reduce the message number in workqueue, because the
+ * pointer may be updated by ack function later, we can
+ * send latest pointer to M core side.
+ */
+ if ((avail - written_num * period_size) <= period_size) {
+ imx_rpmsg_insert_workqueue(substream, msg, info);
+ } else if (rpmsg->force_lpa && !timer_pending(timer)) {
+ int time_msec;
+
+ time_msec = (int)(runtime->period_size * 1000 / runtime->rate);
+ mod_timer(timer, jiffies + msecs_to_jiffies(time_msec));
+ }
+ }
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct snd_pcm_substream *substream;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (substream) {
+ ret = snd_pcm_set_fixed_buffer(substream, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev,
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK]);
+ if (ret < 0)
+ return ret;
+ }
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (substream) {
+ ret = snd_pcm_set_fixed_buffer(substream, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev,
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver imx_rpmsg_soc_component = {
+ .name = IMX_PCM_DRV_NAME,
+ .pcm_construct = imx_rpmsg_pcm_new,
+ .open = imx_rpmsg_pcm_open,
+ .close = imx_rpmsg_pcm_close,
+ .hw_params = imx_rpmsg_pcm_hw_params,
+ .trigger = imx_rpmsg_pcm_trigger,
+ .pointer = imx_rpmsg_pcm_pointer,
+ .ack = imx_rpmsg_pcm_ack,
+ .prepare = imx_rpmsg_pcm_prepare,
+};
+
+static void imx_rpmsg_pcm_work(struct work_struct *work)
+{
+ struct work_of_rpmsg *work_of_rpmsg;
+ bool is_notification = false;
+ struct rpmsg_info *info;
+ struct rpmsg_msg msg;
+ unsigned long flags;
+
+ work_of_rpmsg = container_of(work, struct work_of_rpmsg, work);
+ info = work_of_rpmsg->info;
+
+ /*
+ * Every work in the work queue, first we check if there
+ * is update for period is filled, because there may be not
+ * enough data in M core side, need to let M core know
+ * data is updated immediately.
+ */
+ spin_lock_irqsave(&info->lock[TX], flags);
+ if (info->notify_updated[TX]) {
+ memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg));
+ info->notify_updated[TX] = false;
+ spin_unlock_irqrestore(&info->lock[TX], flags);
+ info->send_message(&msg, info);
+ } else {
+ spin_unlock_irqrestore(&info->lock[TX], flags);
+ }
+
+ spin_lock_irqsave(&info->lock[RX], flags);
+ if (info->notify_updated[RX]) {
+ memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg));
+ info->notify_updated[RX] = false;
+ spin_unlock_irqrestore(&info->lock[RX], flags);
+ info->send_message(&msg, info);
+ } else {
+ spin_unlock_irqrestore(&info->lock[RX], flags);
+ }
+
+ /* Skip the notification message for it has been processed above */
+ if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C &&
+ (work_of_rpmsg->msg.s_msg.header.cmd == TX_PERIOD_DONE ||
+ work_of_rpmsg->msg.s_msg.header.cmd == RX_PERIOD_DONE))
+ is_notification = true;
+
+ if (!is_notification)
+ info->send_message(&work_of_rpmsg->msg, info);
+
+ /* update read index */
+ spin_lock_irqsave(&info->wq_lock, flags);
+ info->work_read_index++;
+ info->work_read_index %= WORK_MAX_NUM;
+ spin_unlock_irqrestore(&info->wq_lock, flags);
+}
+
+static int imx_rpmsg_pcm_probe(struct platform_device *pdev)
+{
+ struct snd_soc_component *component;
+ struct rpmsg_info *info;
+ int ret, i;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, info);
+
+ info->rpdev = container_of(pdev->dev.parent, struct rpmsg_device, dev);
+ info->dev = &pdev->dev;
+ /* Setup work queue */
+ info->rpmsg_wq = alloc_ordered_workqueue(info->rpdev->id.name,
+ WQ_HIGHPRI |
+ WQ_UNBOUND |
+ WQ_FREEZABLE);
+ if (!info->rpmsg_wq) {
+ dev_err(&pdev->dev, "workqueue create failed\n");
+ return -ENOMEM;
+ }
+
+ /* Write index initialize 1, make it differ with the read index */
+ info->work_write_index = 1;
+ info->send_message = imx_rpmsg_pcm_send_message;
+
+ for (i = 0; i < WORK_MAX_NUM; i++) {
+ INIT_WORK(&info->work_list[i].work, imx_rpmsg_pcm_work);
+ info->work_list[i].info = info;
+ }
+
+ /* Initialize msg */
+ for (i = 0; i < MSG_MAX_NUM; i++) {
+ info->msg[i].s_msg.header.cate = IMX_RPMSG_AUDIO;
+ info->msg[i].s_msg.header.major = IMX_RMPSG_MAJOR;
+ info->msg[i].s_msg.header.minor = IMX_RMPSG_MINOR;
+ info->msg[i].s_msg.header.type = MSG_TYPE_A;
+ info->msg[i].s_msg.param.audioindex = 0;
+ }
+
+ init_completion(&info->cmd_complete);
+ mutex_init(&info->msg_lock);
+ spin_lock_init(&info->lock[TX]);
+ spin_lock_init(&info->lock[RX]);
+ spin_lock_init(&info->wq_lock);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &imx_rpmsg_soc_component,
+ NULL, 0);
+ if (ret)
+ goto fail;
+
+ component = snd_soc_lookup_component(&pdev->dev, NULL);
+ if (!component) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ component->debugfs_prefix = "rpmsg";
+#endif
+
+ return 0;
+
+fail:
+ if (info->rpmsg_wq)
+ destroy_workqueue(info->rpmsg_wq);
+
+ return ret;
+}
+
+static void imx_rpmsg_pcm_remove(struct platform_device *pdev)
+{
+ struct rpmsg_info *info = platform_get_drvdata(pdev);
+
+ if (info->rpmsg_wq)
+ destroy_workqueue(info->rpmsg_wq);
+}
+
+static int imx_rpmsg_pcm_runtime_resume(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+
+ cpu_latency_qos_add_request(&info->pm_qos_req, 0);
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_runtime_suspend(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+
+ cpu_latency_qos_remove_request(&info->pm_qos_req);
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_suspend(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+ struct rpmsg_msg *rpmsg_tx;
+ struct rpmsg_msg *rpmsg_rx;
+
+ rpmsg_tx = &info->msg[TX_SUSPEND];
+ rpmsg_rx = &info->msg[RX_SUSPEND];
+
+ rpmsg_tx->s_msg.header.cmd = TX_SUSPEND;
+ info->send_message(rpmsg_tx, info);
+
+ rpmsg_rx->s_msg.header.cmd = RX_SUSPEND;
+ info->send_message(rpmsg_rx, info);
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_resume(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+ struct rpmsg_msg *rpmsg_tx;
+ struct rpmsg_msg *rpmsg_rx;
+
+ rpmsg_tx = &info->msg[TX_RESUME];
+ rpmsg_rx = &info->msg[RX_RESUME];
+
+ rpmsg_tx->s_msg.header.cmd = TX_RESUME;
+ info->send_message(rpmsg_tx, info);
+
+ rpmsg_rx->s_msg.header.cmd = RX_RESUME;
+ info->send_message(rpmsg_rx, info);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = {
+ RUNTIME_PM_OPS(imx_rpmsg_pcm_runtime_suspend,
+ imx_rpmsg_pcm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(imx_rpmsg_pcm_suspend, imx_rpmsg_pcm_resume)
+};
+
+static const struct platform_device_id imx_rpmsg_pcm_id_table[] = {
+ { .name = "rpmsg-audio-channel" },
+ { .name = "rpmsg-micfil-channel" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table);
+
+static struct platform_driver imx_pcm_rpmsg_driver = {
+ .probe = imx_rpmsg_pcm_probe,
+ .remove = imx_rpmsg_pcm_remove,
+ .id_table = imx_rpmsg_pcm_id_table,
+ .driver = {
+ .name = IMX_PCM_DRV_NAME,
+ .pm = pm_ptr(&imx_rpmsg_pcm_pm_ops),
+ },
+};
+module_platform_driver(imx_pcm_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio RPMSG PCM interface");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:" IMX_PCM_DRV_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.h b/sound/soc/fsl/imx-pcm-rpmsg.h
new file mode 100644
index 000000000000..8286b55f00ae
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm-rpmsg.h
@@ -0,0 +1,512 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2017-2021 NXP
+ *
+ ******************************************************************************
+ * Communication stack of audio with rpmsg
+ ******************************************************************************
+ * Packet structure:
+ * A SRTM message consists of a 10 bytes header followed by 0~N bytes of data
+ *
+ * +---------------+-------------------------------+
+ * | | Content |
+ * +---------------+-------------------------------+
+ * | Byte Offset | 7 6 5 4 3 2 1 0 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 0 | Category |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 1 ~ 2 | Version |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 3 | Type |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 4 | Command |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 5 | Reserved0 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 6 | Reserved1 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 7 | Reserved2 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 8 | Reserved3 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 9 | Reserved4 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 10 | DATA 0 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * : : : : : : : : : : : : :
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | N + 10 - 1 | DATA N-1 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ *
+ * +----------+------------+------------------------------------------------+
+ * | Field | Byte | |
+ * +----------+------------+------------------------------------------------+
+ * | Category | 0 | The destination category. |
+ * +----------+------------+------------------------------------------------+
+ * | Version | 1 ~ 2 | The category version of the sender of the |
+ * | | | packet. |
+ * | | | The first byte represent the major version of |
+ * | | | the packet.The second byte represent the minor |
+ * | | | version of the packet. |
+ * +----------+------------+------------------------------------------------+
+ * | Type | 3 | The message type of current message packet. |
+ * +----------+------------+------------------------------------------------+
+ * | Command | 4 | The command byte sent to remote processor/SoC. |
+ * +----------+------------+------------------------------------------------+
+ * | Reserved | 5 ~ 9 | Reserved field for future extension. |
+ * +----------+------------+------------------------------------------------+
+ * | Data | N | The data payload of the message packet. |
+ * +----------+------------+------------------------------------------------+
+ *
+ * Audio control:
+ * SRTM Audio Control Category Request Command Table:
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | Category | Version | Type | Command | Data | Function |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x00 | Data[0]: Audio Device Index | Open a TX Instance. |
+ * | | | | | Data[1]: format | |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x01 | Data[0]: Audio Device Index | Start a TX Instance. |
+ * | | | | | Same as above command | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x02 | Data[0]: Audio Device Index | Pause a TX Instance. |
+ * | | | | | Same as above command | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x03 | Data[0]: Audio Device Index | Resume a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x04 | Data[0]: Audio Device Index | Stop a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x05 | Data[0]: Audio Device Index | Close a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x06 | Data[0]: Audio Device Index | Set Parameters for |
+ * | | | | | Data[1]: format | a TX Instance. |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x07 | Data[0]: Audio Device Index | Set TX Buffer. |
+ * | | | | | Data[1-6]: reserved | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x08 | Data[0]: Audio Device Index | Suspend a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x09 | Data[0]: Audio Device Index | Resume a TX Instance. |
+ * | | | | | Data[1]: format | |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0A | Data[0]: Audio Device Index | Open a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0B | Data[0]: Audio Device Index | Start a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0C | Data[0]: Audio Device Index | Pause a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0D | Data[0]: Audio Device Index | Resume a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0E | Data[0]: Audio Device Index | Stop a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0F | Data[0]: Audio Device Index | Close a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x10 | Data[0]: Audio Device Index | Set Parameters for |
+ * | | | | | Data[1]: format | a RX Instance. |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x11 | Data[0]: Audio Device Index | Set RX Buffer. |
+ * | | | | | Data[1-6]: reserved | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x12 | Data[0]: Audio Device Index | Suspend a RX Instance.|
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x13 | Data[0]: Audio Device Index | Resume a RX Instance. |
+ * | | | | | Data[1]: format | |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x14 | Data[0]: Audio Device Index | Set register value |
+ * | | | | | Data[1-6]: reserved | to codec |
+ * | | | | | Data[7-10]: register | |
+ * | | | | | Data[11-14]: value | |
+ * | | | | | Data[15-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x15 | Data[0]: Audio Device Index | Get register value |
+ * | | | | | Data[1-6]: reserved | from codec |
+ * | | | | | Data[7-10]: register | |
+ * | | | | | Data[11-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * Note 1: See <List of Sample Format> for available value of
+ * Sample Format;
+ * Note 2: See <List of Audio Channels> for available value of Channels;
+ * Note 3: Sample Rate of Set Parameters for an Audio TX Instance
+ * Command and Set Parameters for an Audio RX Instance Command is
+ * in little-endian format.
+ *
+ * SRTM Audio Control Category Response Command Table:
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | Category | Version | Type | Command | Data | Function |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x00 | Data[0]: Audio Device Index | Reply for Open |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x01 | Data[0]: Audio Device Index | Reply for Start |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x02 | Data[0]: Audio Device Index | Reply for Pause |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x03 | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x04 | Data[0]: Audio Device Index | Reply for Stop |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x05 | Data[0]: Audio Device Index | Reply for Close |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x06 | Data[0]: Audio Device Index | Reply for Set Param |
+ * | | | | | Data[1]: Return code | for a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x07 | Data[0]: Audio Device Index | Reply for Set |
+ * | | | | | Data[1]: Return code | TX Buffer |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x08 | Data[0]: Audio Device Index | Reply for Suspend |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x09 | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0A | Data[0]: Audio Device Index | Reply for Open |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0B | Data[0]: Audio Device Index | Reply for Start |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0C | Data[0]: Audio Device Index | Reply for Pause |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0D | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0E | Data[0]: Audio Device Index | Reply for Stop |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0F | Data[0]: Audio Device Index | Reply for Close |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x10 | Data[0]: Audio Device Index | Reply for Set Param |
+ * | | | | | Data[1]: Return code | for a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x11 | Data[0]: Audio Device Index | Reply for Set |
+ * | | | | | Data[1]: Return code | RX Buffer |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x12 | Data[0]: Audio Device Index | Reply for Suspend |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x13 | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x14 | Data[0]: Audio Device Index | Reply for Set codec |
+ * | | | | | Data[1]: Return code | register value |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x15 | Data[0]: Audio Device Index | Reply for Get codec |
+ * | | | | | Data[1]: Return code | register value |
+ * | | | | | Data[2-6]: reserved | |
+ * | | | | | Data[7-10]: register | |
+ * | | | | | Data[11-14]: value | |
+ * | | | | | Data[15-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ *
+ * SRTM Audio Control Category Notification Command Table:
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | Category | Version | Type | Command | Data | Function |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x02 | 0x00 | Data[0]: Audio Device Index | Notify one TX period |
+ * | | | | | | is finished |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x02 | 0x01 | Data[0]: Audio Device Index | Notify one RX period |
+ * | | | | | | is finished |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ *
+ * List of Sample Format:
+ * +------------------+-----------------------+
+ * | Sample Format | Description |
+ * +------------------+-----------------------+
+ * | 0x0 | S16_LE |
+ * +------------------+-----------------------+
+ * | 0x1 | S24_LE |
+ * +------------------+-----------------------+
+ *
+ * List of Audio Channels
+ * +------------------+-----------------------+
+ * | Audio Channel | Description |
+ * +------------------+-----------------------+
+ * | 0x0 | Left Channel |
+ * +------------------+-----------------------+
+ * | 0x1 | Right Channel |
+ * +------------------+---------------- ------+
+ * | 0x2 | Left & Right Channel |
+ * +------------------+-----------------------+
+ *
+ */
+
+#ifndef _IMX_PCM_RPMSG_H
+#define _IMX_PCM_RPMSG_H
+
+#include <linux/pm_qos.h>
+#include <linux/interrupt.h>
+#include <sound/dmaengine_pcm.h>
+
+#define RPMSG_TIMEOUT 1000
+
+/* RPMSG Command (TYPE A)*/
+#define TX_OPEN 0x0
+#define TX_START 0x1
+#define TX_PAUSE 0x2
+#define TX_RESTART 0x3
+#define TX_TERMINATE 0x4
+#define TX_CLOSE 0x5
+#define TX_HW_PARAM 0x6
+#define TX_BUFFER 0x7
+#define TX_SUSPEND 0x8
+#define TX_RESUME 0x9
+
+#define RX_OPEN 0xA
+#define RX_START 0xB
+#define RX_PAUSE 0xC
+#define RX_RESTART 0xD
+#define RX_TERMINATE 0xE
+#define RX_CLOSE 0xF
+#define RX_HW_PARAM 0x10
+#define RX_BUFFER 0x11
+#define RX_SUSPEND 0x12
+#define RX_RESUME 0x13
+#define SET_CODEC_VALUE 0x14
+#define GET_CODEC_VALUE 0x15
+#define TX_POINTER 0x16
+#define RX_POINTER 0x17
+/* Total msg numver for type A */
+#define MSG_TYPE_A_NUM 0x18
+
+/* RPMSG Command (TYPE C)*/
+#define TX_PERIOD_DONE 0x0
+#define RX_PERIOD_DONE 0x1
+/* Total msg numver for type C */
+#define MSG_TYPE_C_NUM 0x2
+
+#define MSG_MAX_NUM (MSG_TYPE_A_NUM + MSG_TYPE_C_NUM)
+
+#define MSG_TYPE_A 0x0
+#define MSG_TYPE_B 0x1
+#define MSG_TYPE_C 0x2
+
+#define RESP_NONE 0x0
+#define RESP_NOT_ALLOWED 0x1
+#define RESP_SUCCESS 0x2
+#define RESP_FAILED 0x3
+
+#define RPMSG_S16_LE 0x0
+#define RPMSG_S24_LE 0x1
+#define RPMSG_S32_LE 0x2
+#define RPMSG_DSD_U16_LE 49 /* SNDRV_PCM_FORMAT_DSD_U16_LE */
+#define RPMSG_DSD_U24_LE 0x4
+#define RPMSG_DSD_U32_LE 50 /* SNDRV_PCM_FORMAT_DSD_U32_LE */
+
+#define RPMSG_CH_LEFT 0x0
+#define RPMSG_CH_RIGHT 0x1
+#define RPMSG_CH_STEREO 0x2
+
+#define WORK_MAX_NUM 0x30
+
+/* Category define */
+#define IMX_RMPSG_LIFECYCLE 1
+#define IMX_RPMSG_PMIC 2
+#define IMX_RPMSG_AUDIO 3
+#define IMX_RPMSG_KEY 4
+#define IMX_RPMSG_GPIO 5
+#define IMX_RPMSG_RTC 6
+#define IMX_RPMSG_SENSOR 7
+
+/* rpmsg version */
+#define IMX_RMPSG_MAJOR 1
+#define IMX_RMPSG_MINOR 0
+
+#define TX SNDRV_PCM_STREAM_PLAYBACK
+#define RX SNDRV_PCM_STREAM_CAPTURE
+
+/**
+ * struct rpmsg_head: rpmsg header structure
+ *
+ * @cate: category
+ * @major: major version
+ * @minor: minor version
+ * @type: message type (A/B/C)
+ * @cmd: message command
+ * @reserved: reserved space
+ */
+struct rpmsg_head {
+ u8 cate;
+ u8 major;
+ u8 minor;
+ u8 type;
+ u8 cmd;
+ u8 reserved[5];
+} __packed;
+
+/**
+ * struct param_s: sent rpmsg parameter
+ *
+ * @audioindex: audio instance index
+ * @format: audio format
+ * @channels: audio channel number
+ * @rate: sample rate
+ * @buffer_addr: dma buffer physical address or register for SET_CODEC_VALUE
+ * @buffer_size: dma buffer size or register value for SET_CODEC_VALUE
+ * @period_size: period size
+ * @buffer_tail: current period index
+ */
+struct param_s {
+ unsigned char audioindex;
+ unsigned char format;
+ unsigned char channels;
+ unsigned int rate;
+ unsigned int buffer_addr;
+ unsigned int buffer_size;
+ unsigned int period_size;
+ unsigned int buffer_tail;
+} __packed;
+
+/**
+ * struct param_s: send rpmsg parameter
+ *
+ * @audioindex: audio instance index
+ * @resp: response value
+ * @reserved1: reserved space
+ * @buffer_offset: the consumed offset of buffer
+ * @reg_addr: register addr of codec
+ * @reg_data: register value of codec
+ * @reserved2: reserved space
+ * @buffer_tail: current period index
+ */
+struct param_r {
+ unsigned char audioindex;
+ unsigned char resp;
+ unsigned char reserved1[1];
+ unsigned int buffer_offset;
+ unsigned int reg_addr;
+ unsigned int reg_data;
+ unsigned char reserved2[4];
+ unsigned int buffer_tail;
+} __packed;
+
+/* Struct of sent message */
+struct rpmsg_s_msg {
+ struct rpmsg_head header;
+ struct param_s param;
+};
+
+/* Struct of received message */
+struct rpmsg_r_msg {
+ struct rpmsg_head header;
+ struct param_r param;
+};
+
+/* Struct of rpmsg */
+struct rpmsg_msg {
+ struct rpmsg_s_msg s_msg;
+ struct rpmsg_r_msg r_msg;
+};
+
+/* Struct of rpmsg for workqueue */
+struct work_of_rpmsg {
+ struct rpmsg_info *info;
+ /* Sent msg for each work */
+ struct rpmsg_msg msg;
+ struct work_struct work;
+};
+
+/* Struct of timer */
+struct stream_timer {
+ struct timer_list timer;
+ struct rpmsg_info *info;
+ struct snd_pcm_substream *substream;
+};
+
+typedef void (*dma_callback)(void *arg);
+
+/**
+ * struct rpmsg_info: rpmsg audio information
+ *
+ * @rpdev: pointer of rpmsg_device
+ * @dev: pointer for imx_pcm_rpmsg device
+ * @cmd_complete: command is finished
+ * @pm_qos_req: request of pm qos
+ * @r_msg: received rpmsg
+ * @msg: array of rpmsg
+ * @notify: notification msg (type C) for TX & RX
+ * @notify_updated: notification flag for TX & RX
+ * @rpmsg_wq: rpmsg workqueue
+ * @work_list: array of work list for workqueue
+ * @work_write_index: write index of work list
+ * @work_read_index: read index of work list
+ * @msg_drop_count: counter of dropped msg for TX & RX
+ * @num_period: period number for TX & RX
+ * @callback_param: parameter for period elapse callback for TX & RX
+ * @callback: period elapse callback for TX & RX
+ * @send_message: function pointer for send message
+ * @lock: spin lock for TX & RX
+ * @wq_lock: lock for work queue
+ * @msg_lock: lock for send message
+ * @stream_timer: timer for tigger workqueue
+ */
+struct rpmsg_info {
+ struct rpmsg_device *rpdev;
+ struct device *dev;
+ struct completion cmd_complete;
+ struct pm_qos_request pm_qos_req;
+
+ /* Received msg (global) */
+ struct rpmsg_r_msg r_msg;
+ struct rpmsg_msg msg[MSG_MAX_NUM];
+ /* period done */
+ struct rpmsg_msg notify[2];
+ bool notify_updated[2];
+
+ struct workqueue_struct *rpmsg_wq;
+ struct work_of_rpmsg work_list[WORK_MAX_NUM];
+ int work_write_index;
+ int work_read_index;
+ int msg_drop_count[2];
+ int num_period[2];
+ void *callback_param[2];
+ dma_callback callback[2];
+ int (*send_message)(struct rpmsg_msg *msg, struct rpmsg_info *info);
+ spinlock_t lock[2]; /* spin lock for resource protection */
+ spinlock_t wq_lock; /* spin lock for resource protection */
+ struct mutex msg_lock; /* mutex for resource protection */
+ struct stream_timer stream_timer[2];
+};
+
+#define IMX_PCM_DRV_NAME "imx_pcm_rpmsg"
+
+#endif /* IMX_PCM_RPMSG_H */
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index 133c4470acad..ac5f57c3cc55 100644
--- a/sound/soc/fsl/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -1,19 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
*
* This code is based on code copyrighted by Freescale,
* Liam Girdwood, Javier Martin and probably others.
- *
- * 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 _IMX_PCM_H
#define _IMX_PCM_H
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
/*
* Do not change this as the FIQ handler depends on this size
@@ -21,18 +17,6 @@
#define IMX_SSI_DMABUF_SIZE (64 * 1024)
#define IMX_DEFAULT_DMABUF_SIZE (64 * 1024)
-#define IMX_SAI_DMABUF_SIZE (64 * 1024)
-#define IMX_SPDIF_DMABUF_SIZE (64 * 1024)
-#define IMX_ESAI_DMABUF_SIZE (256 * 1024)
-
-static inline void
-imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data,
- int dma, enum sdma_peripheral_type peripheral_type)
-{
- dma_data->dma_request = dma;
- dma_data->priority = DMA_PRIO_HIGH;
- dma_data->peripheral_type = peripheral_type;
-}
struct imx_pcm_fiq_params {
int irq;
@@ -44,9 +28,9 @@ struct imx_pcm_fiq_params {
};
#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA)
-int imx_pcm_dma_init(struct platform_device *pdev, size_t size);
+int imx_pcm_dma_init(struct platform_device *pdev);
#else
-static inline int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
+static inline int imx_pcm_dma_init(struct platform_device *pdev)
{
return -ENODEV;
}
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
new file mode 100644
index 000000000000..53f04d1f3280
--- /dev/null
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2020 NXP
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/control.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include "imx-pcm-rpmsg.h"
+
+struct imx_rpmsg {
+ struct snd_soc_dai_link dai;
+ struct snd_soc_card card;
+ unsigned long sysclk;
+ bool lpa;
+};
+
+static struct dev_pm_ops lpa_pm;
+
+static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("Main MIC", NULL),
+};
+
+static int imx_rpmsg_late_probe(struct snd_soc_card *card)
+{
+ struct imx_rpmsg *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_pcm_runtime *rtd = list_first_entry(&card->rtd_list,
+ struct snd_soc_pcm_runtime, list);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct device *dev = card->dev;
+ int ret;
+
+ if (data->lpa) {
+ struct snd_soc_component *codec_comp;
+ struct device_node *codec_np;
+ struct device_driver *codec_drv;
+ struct device *codec_dev = NULL;
+
+ codec_np = data->dai.codecs->of_node;
+ if (codec_np) {
+ struct platform_device *codec_pdev;
+ struct i2c_client *codec_i2c;
+
+ codec_i2c = of_find_i2c_device_by_node(codec_np);
+ if (codec_i2c)
+ codec_dev = &codec_i2c->dev;
+ if (!codec_dev) {
+ codec_pdev = of_find_device_by_node(codec_np);
+ if (codec_pdev)
+ codec_dev = &codec_pdev->dev;
+ }
+ }
+ if (codec_dev) {
+ codec_comp = snd_soc_lookup_component_nolocked(codec_dev, NULL);
+ if (codec_comp) {
+ int i, num_widgets;
+ const char *widgets;
+ struct snd_soc_dapm_context *dapm;
+
+ num_widgets = of_property_count_strings(data->card.dev->of_node,
+ "ignore-suspend-widgets");
+ for (i = 0; i < num_widgets; i++) {
+ of_property_read_string_index(data->card.dev->of_node,
+ "ignore-suspend-widgets",
+ i, &widgets);
+ dapm = snd_soc_component_to_dapm(codec_comp);
+ snd_soc_dapm_ignore_suspend(dapm, widgets);
+ }
+ }
+ codec_drv = codec_dev->driver;
+ if (codec_drv->pm) {
+ memcpy(&lpa_pm, codec_drv->pm, sizeof(lpa_pm));
+ lpa_pm.suspend = NULL;
+ lpa_pm.resume = NULL;
+ lpa_pm.freeze = NULL;
+ lpa_pm.thaw = NULL;
+ lpa_pm.poweroff = NULL;
+ lpa_pm.restore = NULL;
+ codec_drv->pm = &lpa_pm;
+ }
+ put_device(codec_dev);
+ }
+ }
+
+ if (!data->sysclk)
+ return 0;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, data->sysclk, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_rpmsg_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link_component *dlc;
+ struct snd_soc_dai *cpu_dai;
+ struct device_node *np = NULL;
+ struct of_phandle_args args;
+ const char *platform_name;
+ struct imx_rpmsg *data;
+ int ret = 0;
+
+ dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ data->dai.cpus = &dlc[0];
+ data->dai.num_cpus = 1;
+ data->dai.platforms = &dlc[1];
+ data->dai.num_platforms = 1;
+ data->dai.codecs = &dlc[2];
+ data->dai.num_codecs = 1;
+
+ data->dai.name = "rpmsg hifi";
+ data->dai.stream_name = "rpmsg hifi";
+ data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC;
+
+ /*
+ * i.MX rpmsg sound cards work on codec slave mode. MCLK will be
+ * disabled by CPU DAI driver in hw_free(). Some codec requires MCLK
+ * present at power up/down sequence. So need to set ignore_pmdown_time
+ * to power down codec immediately before MCLK is turned off.
+ */
+ data->dai.ignore_pmdown_time = 1;
+
+ data->dai.cpus->dai_name = pdev->dev.platform_data;
+ cpu_dai = snd_soc_find_dai(data->dai.cpus);
+ if (!cpu_dai) {
+ ret = -EPROBE_DEFER;
+ goto fail;
+ }
+ np = cpu_dai->dev->of_node;
+ if (!np) {
+ dev_err(&pdev->dev, "failed to parse CPU DAI device node\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0);
+ if (ret)
+ dev_warn(&pdev->dev, "no reserved DMA memory\n");
+
+ /* Optional codec node */
+ ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args);
+ if (ret) {
+ *data->dai.codecs = snd_soc_dummy_dlc;
+ } else {
+ struct clk *clk;
+
+ ret = snd_soc_get_dlc(&args, data->dai.codecs);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
+ goto fail;
+ }
+
+ clk = devm_get_clk_from_child(&pdev->dev, args.np, NULL);
+ if (!IS_ERR(clk))
+ data->sysclk = clk_get_rate(clk);
+ }
+
+ if (!of_property_read_string(np, "fsl,rpmsg-channel-name", &platform_name))
+ data->dai.platforms->name = platform_name;
+ else
+ data->dai.platforms->name = "rpmsg-audio-channel";
+ data->dai.playback_only = true;
+ data->dai.capture_only = true;
+ data->card.num_links = 1;
+ data->card.dai_link = &data->dai;
+
+ if (of_property_read_bool(np, "fsl,rpmsg-out"))
+ data->dai.capture_only = false;
+
+ if (of_property_read_bool(np, "fsl,rpmsg-in"))
+ data->dai.playback_only = false;
+
+ if (data->dai.playback_only && data->dai.capture_only) {
+ dev_err(&pdev->dev, "no enabled rpmsg DAI link\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (of_property_read_bool(np, "fsl,enable-lpa"))
+ data->lpa = true;
+
+ data->card.dev = &pdev->dev;
+ data->card.owner = THIS_MODULE;
+ data->card.dapm_widgets = imx_rpmsg_dapm_widgets;
+ data->card.num_dapm_widgets = ARRAY_SIZE(imx_rpmsg_dapm_widgets);
+ data->card.late_probe = imx_rpmsg_late_probe;
+ /*
+ * Inoder to use common api to get card name and audio routing.
+ * Use parent of_node for this device, revert it after finishing using
+ */
+ data->card.dev->of_node = np;
+
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+
+ if (of_property_present(np, "audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+ goto fail;
+ }
+ }
+
+ platform_set_drvdata(pdev, &data->card);
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+ goto fail;
+ }
+
+fail:
+ pdev->dev.of_node = NULL;
+ return ret;
+}
+
+static struct platform_driver imx_rpmsg_driver = {
+ .driver = {
+ .name = "imx-audio-rpmsg",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = imx_rpmsg_probe,
+};
+module_platform_driver(imx_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio RPMSG Machine Driver");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:imx-audio-rpmsg");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index b99e0b5e00e9..8bcf54ef709e 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -1,14 +1,7 @@
-/*
- * Copyright 2012 Freescale Semiconductor, Inc.
- * Copyright 2012 Linaro Ltd.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright 2012 Freescale Semiconductor, Inc.
+// Copyright 2012 Linaro Ltd.
#include <linux/module.h>
#include <linux/of.h>
@@ -37,7 +30,7 @@ static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd)
struct device *dev = rtd->card->dev;
int ret;
- ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK,
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), SGTL5000_SYSCLK,
data->clk_frequency, SND_SOC_CLOCK_IN);
if (ret) {
dev_err(dev, "could not set codec driver clock params\n");
@@ -62,6 +55,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
struct platform_device *ssi_pdev;
struct i2c_client *codec_dev;
struct imx_sgtl5000_data *data = NULL;
+ struct snd_soc_dai_link_component *comp;
int int_port, ext_port;
int ret;
@@ -111,47 +105,63 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
ssi_pdev = of_find_device_by_node(ssi_np);
if (!ssi_pdev) {
- dev_err(&pdev->dev, "failed to find SSI platform device\n");
+ dev_dbg(&pdev->dev, "failed to find SSI platform device\n");
ret = -EPROBE_DEFER;
goto fail;
}
+ put_device(&ssi_pdev->dev);
codec_dev = of_find_i2c_device_by_node(codec_np);
if (!codec_dev) {
- dev_err(&pdev->dev, "failed to find codec platform device\n");
- return -EPROBE_DEFER;
+ dev_dbg(&pdev->dev, "failed to find codec platform device\n");
+ ret = -EPROBE_DEFER;
+ goto fail;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
- goto fail;
+ goto put_device;
+ }
+
+ comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
+ if (!comp) {
+ ret = -ENOMEM;
+ goto put_device;
}
data->codec_clk = clk_get(&codec_dev->dev, NULL);
if (IS_ERR(data->codec_clk)) {
ret = PTR_ERR(data->codec_clk);
- goto fail;
+ goto put_device;
}
data->clk_frequency = clk_get_rate(data->codec_clk);
+ data->dai.cpus = &comp[0];
+ data->dai.codecs = &comp[1];
+ data->dai.platforms = &comp[2];
+
+ data->dai.num_cpus = 1;
+ data->dai.num_codecs = 1;
+ data->dai.num_platforms = 1;
+
data->dai.name = "HiFi";
data->dai.stream_name = "HiFi";
- data->dai.codec_dai_name = "sgtl5000";
- data->dai.codec_of_node = codec_np;
- data->dai.cpu_of_node = ssi_np;
- data->dai.platform_of_node = ssi_np;
+ data->dai.codecs->dai_name = "sgtl5000";
+ data->dai.codecs->of_node = codec_np;
+ data->dai.cpus->of_node = ssi_np;
+ data->dai.platforms->of_node = ssi_np;
data->dai.init = &imx_sgtl5000_dai_init;
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_CBP_CFP;
data->card.dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret)
- goto fail;
+ goto put_device;
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret)
- goto fail;
+ goto put_device;
data->card.num_links = 1;
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
@@ -163,8 +173,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- goto fail;
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+ goto put_device;
}
of_node_put(ssi_np);
@@ -172,6 +182,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
return 0;
+put_device:
+ put_device(&codec_dev->dev);
fail:
if (data && !IS_ERR(data->codec_clk))
clk_put(data->codec_clk);
@@ -181,14 +193,12 @@ fail:
return ret;
}
-static int imx_sgtl5000_remove(struct platform_device *pdev)
+static void imx_sgtl5000_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(card);
clk_put(data->codec_clk);
-
- return 0;
}
static const struct of_device_id imx_sgtl5000_dt_ids[] = {
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
deleted file mode 100644
index a407e833c612..000000000000
--- a/sound/soc/fsl/imx-spdif.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <sound/soc.h>
-
-struct imx_spdif_data {
- struct snd_soc_dai_link dai;
- struct snd_soc_card card;
-};
-
-static int imx_spdif_audio_probe(struct platform_device *pdev)
-{
- struct device_node *spdif_np, *np = pdev->dev.of_node;
- struct imx_spdif_data *data;
- int ret = 0;
-
- spdif_np = of_parse_phandle(np, "spdif-controller", 0);
- if (!spdif_np) {
- dev_err(&pdev->dev, "failed to find spdif-controller\n");
- ret = -EINVAL;
- goto end;
- }
-
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto end;
- }
-
- data->dai.name = "S/PDIF PCM";
- data->dai.stream_name = "S/PDIF PCM";
- data->dai.codec_dai_name = "snd-soc-dummy-dai";
- data->dai.codec_name = "snd-soc-dummy";
- data->dai.cpu_of_node = spdif_np;
- data->dai.platform_of_node = spdif_np;
- data->dai.playback_only = true;
- data->dai.capture_only = true;
-
- if (of_property_read_bool(np, "spdif-out"))
- data->dai.capture_only = false;
-
- if (of_property_read_bool(np, "spdif-in"))
- data->dai.playback_only = false;
-
- if (data->dai.playback_only && data->dai.capture_only) {
- dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
- goto end;
- }
-
- data->card.dev = &pdev->dev;
- data->card.dai_link = &data->dai;
- data->card.num_links = 1;
- data->card.owner = THIS_MODULE;
-
- ret = snd_soc_of_parse_card_name(&data->card, "model");
- if (ret)
- goto end;
-
- ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
- goto end;
- }
-
- platform_set_drvdata(pdev, data);
-
-end:
- of_node_put(spdif_np);
-
- return ret;
-}
-
-static const struct of_device_id imx_spdif_dt_ids[] = {
- { .compatible = "fsl,imx-audio-spdif", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
-
-static struct platform_driver imx_spdif_driver = {
- .driver = {
- .name = "imx-spdif",
- .pm = &snd_soc_pm_ops,
- .of_match_table = imx_spdif_dt_ids,
- },
- .probe = imx_spdif_audio_probe,
-};
-
-module_platform_driver(imx_spdif_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:imx-spdif");
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
deleted file mode 100644
index b95132e2f9dc..000000000000
--- a/sound/soc/fsl/imx-ssi.c
+++ /dev/null
@@ -1,657 +0,0 @@
-/*
- * imx-ssi.c -- ALSA Soc Audio Layer
- *
- * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This code is based on code copyrighted by Freescale,
- * Liam Girdwood, Javier Martin and probably others.
- *
- * 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.
- *
- *
- * The i.MX SSI core has some nasty limitations in AC97 mode. While most
- * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
- * one FIFO which combines all valid receive slots. We cannot even select
- * which slots we want to receive. The WM9712 with which this driver
- * was developed with always sends GPIO status data in slot 12 which
- * we receive in our (PCM-) data stream. The only chance we have is to
- * manually skip this data in the FIQ handler. With sampling rates different
- * from 48000Hz not every frame has valid receive data, so the ratio
- * between pcm data and GPIO status data changes. Our FIQ handler is not
- * able to handle this, hence this driver only works with 48000Hz sampling
- * rate.
- * Reading and writing AC97 registers is another challenge. The core
- * provides us status bits when the read register is updated with *another*
- * value. When we read the same register two times (and the register still
- * contains the same value) these status bits are not set. We work
- * around this by not polling these bits but only wait a fixed delay.
- *
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.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 <linux/platform_data/asoc-imx-ssi.h>
-
-#include "imx-ssi.h"
-#include "fsl_utils.h"
-
-#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV)
-
-/*
- * SSI Network Mode or TDM slots configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
- unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
-{
- struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
- u32 sccr;
-
- sccr = readl(ssi->base + SSI_STCCR);
- sccr &= ~SSI_STCCR_DC_MASK;
- sccr |= SSI_STCCR_DC(slots - 1);
- writel(sccr, ssi->base + SSI_STCCR);
-
- sccr = readl(ssi->base + SSI_SRCCR);
- sccr &= ~SSI_STCCR_DC_MASK;
- sccr |= SSI_STCCR_DC(slots - 1);
- writel(sccr, ssi->base + SSI_SRCCR);
-
- writel(~tx_mask, ssi->base + SSI_STMSK);
- writel(~rx_mask, ssi->base + SSI_SRMSK);
-
- return 0;
-}
-
-/*
- * SSI DAI format configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
-{
- struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
- u32 strcr = 0, scr;
-
- scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
-
- /* DAI mode */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- /* data on rising edge of bclk, frame low 1clk before data */
- strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI |
- SSI_STCR_TEFS;
- scr |= SSI_SCR_NET;
- if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
- scr &= ~SSI_I2S_MODE_MASK;
- scr |= SSI_SCR_I2S_MODE_SLAVE;
- }
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- /* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- /* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- /* data on rising edge of bclk, frame high 1clk before data */
- strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL |
- SSI_STCR_TEFS;
- break;
- }
-
- /* DAI clock inversion */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_IB_IF:
- strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- strcr ^= SSI_STCR_TSCKP;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- strcr ^= SSI_STCR_TFSI;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- break;
- }
-
- /* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- default:
- /* Master mode not implemented, needs handling of clocks. */
- return -EINVAL;
- }
-
- strcr |= SSI_STCR_TFEN0;
-
- if (ssi->flags & IMX_SSI_NET)
- scr |= SSI_SCR_NET;
- if (ssi->flags & IMX_SSI_SYN)
- scr |= SSI_SCR_SYN;
-
- writel(strcr, ssi->base + SSI_STCR);
- writel(strcr, ssi->base + SSI_SRCR);
- writel(scr, ssi->base + SSI_SCR);
-
- return 0;
-}
-
-/*
- * SSI system clock configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
- u32 scr;
-
- scr = readl(ssi->base + SSI_SCR);
-
- switch (clk_id) {
- case IMX_SSP_SYS_CLK:
- if (dir == SND_SOC_CLOCK_OUT)
- scr |= SSI_SCR_SYS_CLK_EN;
- else
- scr &= ~SSI_SCR_SYS_CLK_EN;
- break;
- default:
- return -EINVAL;
- }
-
- writel(scr, ssi->base + SSI_SCR);
-
- return 0;
-}
-
-/*
- * SSI Clock dividers
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
- u32 stccr, srccr;
-
- stccr = readl(ssi->base + SSI_STCCR);
- srccr = readl(ssi->base + SSI_SRCCR);
-
- switch (div_id) {
- case IMX_SSI_TX_DIV_2:
- stccr &= ~SSI_STCCR_DIV2;
- stccr |= div;
- break;
- case IMX_SSI_TX_DIV_PSR:
- stccr &= ~SSI_STCCR_PSR;
- stccr |= div;
- break;
- case IMX_SSI_TX_DIV_PM:
- stccr &= ~0xff;
- stccr |= SSI_STCCR_PM(div);
- break;
- case IMX_SSI_RX_DIV_2:
- stccr &= ~SSI_STCCR_DIV2;
- stccr |= div;
- break;
- case IMX_SSI_RX_DIV_PSR:
- stccr &= ~SSI_STCCR_PSR;
- stccr |= div;
- break;
- case IMX_SSI_RX_DIV_PM:
- stccr &= ~0xff;
- stccr |= SSI_STCCR_PM(div);
- break;
- default:
- return -EINVAL;
- }
-
- writel(stccr, ssi->base + SSI_STCCR);
- writel(srccr, ssi->base + SSI_SRCCR);
-
- return 0;
-}
-
-/*
- * Should only be called when port is inactive (i.e. SSIEN = 0),
- * although can be called multiple times by upper layers.
- */
-static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
-{
- struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
- u32 reg, sccr;
-
- /* Tx/Rx config */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- reg = SSI_STCCR;
- else
- reg = SSI_SRCCR;
-
- if (ssi->flags & IMX_SSI_SYN)
- reg = SSI_STCCR;
-
- sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK;
-
- /* DAI data (word) size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- sccr |= SSI_SRCCR_WL(16);
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- sccr |= SSI_SRCCR_WL(20);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- sccr |= SSI_SRCCR_WL(24);
- break;
- }
-
- writel(sccr, ssi->base + reg);
-
- return 0;
-}
-
-static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai);
- unsigned int sier_bits, sier;
- unsigned int scr;
-
- scr = readl(ssi->base + SSI_SCR);
- sier = readl(ssi->base + SSI_SIER);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (ssi->flags & IMX_SSI_DMA)
- sier_bits = SSI_SIER_TDMAE;
- else
- sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN;
- } else {
- if (ssi->flags & IMX_SSI_DMA)
- sier_bits = SSI_SIER_RDMAE;
- else
- sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN;
- }
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- scr |= SSI_SCR_TE;
- else
- scr |= SSI_SCR_RE;
- sier |= sier_bits;
-
- scr |= SSI_SCR_SSIEN;
-
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- scr &= ~SSI_SCR_TE;
- else
- scr &= ~SSI_SCR_RE;
- sier &= ~sier_bits;
-
- if (!(scr & (SSI_SCR_TE | SSI_SCR_RE)))
- scr &= ~SSI_SCR_SSIEN;
-
- break;
- default:
- return -EINVAL;
- }
-
- if (!(ssi->flags & IMX_SSI_USE_AC97))
- /* rx/tx are always enabled to access ac97 registers */
- writel(scr, ssi->base + SSI_SCR);
-
- writel(sier, ssi->base + SSI_SIER);
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
- .hw_params = imx_ssi_hw_params,
- .set_fmt = imx_ssi_set_dai_fmt,
- .set_clkdiv = imx_ssi_set_dai_clkdiv,
- .set_sysclk = imx_ssi_set_dai_sysclk,
- .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
- .trigger = imx_ssi_trigger,
-};
-
-static int imx_ssi_dai_probe(struct snd_soc_dai *dai)
-{
- struct imx_ssi *ssi = dev_get_drvdata(dai->dev);
- uint32_t val;
-
- snd_soc_dai_set_drvdata(dai, ssi);
-
- val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.maxburst) |
- SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst);
- writel(val, ssi->base + SSI_SFCSR);
-
- /* Tx/Rx config */
- dai->playback_dma_data = &ssi->dma_params_tx;
- dai->capture_dma_data = &ssi->dma_params_rx;
-
- return 0;
-}
-
-static struct snd_soc_dai_driver imx_ssi_dai = {
- .probe = imx_ssi_dai_probe,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &imx_ssi_pcm_dai_ops,
-};
-
-static struct snd_soc_dai_driver imx_ac97_dai = {
- .probe = imx_ssi_dai_probe,
- .bus_control = true,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &imx_ssi_pcm_dai_ops,
-};
-
-static const struct snd_soc_component_driver imx_component = {
- .name = DRV_NAME,
-};
-
-static void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
-{
- void __iomem *base = imx_ssi->base;
-
- writel(0x0, base + SSI_SCR);
- writel(0x0, base + SSI_STCR);
- writel(0x0, base + SSI_SRCR);
-
- writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR);
-
- writel(SSI_SFCSR_RFWM0(8) |
- SSI_SFCSR_TFWM0(8) |
- SSI_SFCSR_RFWM1(8) |
- SSI_SFCSR_TFWM1(8), base + SSI_SFCSR);
-
- writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR);
- writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR);
-
- writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR);
- writel(SSI_SOR_WAIT(3), base + SSI_SOR);
-
- writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN |
- SSI_SCR_TE | SSI_SCR_RE,
- base + SSI_SCR);
-
- writel(SSI_SACNT_DEFAULT, base + SSI_SACNT);
- writel(0xff, base + SSI_SACCDIS);
- writel(0x300, base + SSI_SACCEN);
-}
-
-static struct imx_ssi *ac97_ssi;
-
-static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- struct imx_ssi *imx_ssi = ac97_ssi;
- void __iomem *base = imx_ssi->base;
- unsigned int lreg;
- unsigned int lval;
-
- if (reg > 0x7f)
- return;
-
- pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
-
- lreg = reg << 12;
- writel(lreg, base + SSI_SACADD);
-
- lval = val << 4;
- writel(lval , base + SSI_SACDAT);
-
- writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT);
- udelay(100);
-}
-
-static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct imx_ssi *imx_ssi = ac97_ssi;
- void __iomem *base = imx_ssi->base;
-
- unsigned short val = -1;
- unsigned int lreg;
-
- lreg = (reg & 0x7f) << 12 ;
- writel(lreg, base + SSI_SACADD);
- writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT);
-
- udelay(100);
-
- val = (readl(base + SSI_SACDAT) >> 4) & 0xffff;
-
- pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
-
- return val;
-}
-
-static void imx_ssi_ac97_reset(struct snd_ac97 *ac97)
-{
- struct imx_ssi *imx_ssi = ac97_ssi;
-
- if (imx_ssi->ac97_reset)
- imx_ssi->ac97_reset(ac97);
- /* First read sometimes fails, do a dummy read */
- imx_ssi_ac97_read(ac97, 0);
-}
-
-static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct imx_ssi *imx_ssi = ac97_ssi;
-
- if (imx_ssi->ac97_warm_reset)
- imx_ssi->ac97_warm_reset(ac97);
-
- /* First read sometimes fails, do a dummy read */
- imx_ssi_ac97_read(ac97, 0);
-}
-
-static struct snd_ac97_bus_ops imx_ssi_ac97_ops = {
- .read = imx_ssi_ac97_read,
- .write = imx_ssi_ac97_write,
- .reset = imx_ssi_ac97_reset,
- .warm_reset = imx_ssi_ac97_warm_reset
-};
-
-static int imx_ssi_probe(struct platform_device *pdev)
-{
- struct resource *res;
- struct imx_ssi *ssi;
- struct imx_ssi_platform_data *pdata = pdev->dev.platform_data;
- int ret = 0;
- struct snd_soc_dai_driver *dai;
-
- ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL);
- if (!ssi)
- return -ENOMEM;
- dev_set_drvdata(&pdev->dev, ssi);
-
- if (pdata) {
- ssi->ac97_reset = pdata->ac97_reset;
- ssi->ac97_warm_reset = pdata->ac97_warm_reset;
- ssi->flags = pdata->flags;
- }
-
- ssi->irq = platform_get_irq(pdev, 0);
-
- ssi->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(ssi->clk)) {
- ret = PTR_ERR(ssi->clk);
- dev_err(&pdev->dev, "Cannot get the clock: %d\n",
- ret);
- goto failed_clk;
- }
- ret = clk_prepare_enable(ssi->clk);
- if (ret)
- goto failed_clk;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ssi->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ssi->base)) {
- ret = PTR_ERR(ssi->base);
- goto failed_register;
- }
-
- if (ssi->flags & IMX_SSI_USE_AC97) {
- if (ac97_ssi) {
- dev_err(&pdev->dev, "AC'97 SSI already registered\n");
- ret = -EBUSY;
- goto failed_register;
- }
- ac97_ssi = ssi;
- setup_channel_to_ac97(ssi);
- dai = &imx_ac97_dai;
- } else
- dai = &imx_ssi_dai;
-
- writel(0x0, ssi->base + SSI_SIER);
-
- ssi->dma_params_rx.addr = res->start + SSI_SRX0;
- ssi->dma_params_tx.addr = res->start + SSI_STX0;
-
- ssi->dma_params_tx.maxburst = 6;
- ssi->dma_params_rx.maxburst = 4;
-
- ssi->dma_params_tx.filter_data = &ssi->filter_data_tx;
- ssi->dma_params_rx.filter_data = &ssi->filter_data_rx;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
- if (res) {
- imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start,
- IMX_DMATYPE_SSI);
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
- if (res) {
- imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start,
- IMX_DMATYPE_SSI);
- }
-
- platform_set_drvdata(pdev, ssi);
-
- ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
- goto failed_register;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &imx_component,
- dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "register DAI failed\n");
- goto failed_register;
- }
-
- ssi->fiq_params.irq = ssi->irq;
- ssi->fiq_params.base = ssi->base;
- ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
- ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
-
- ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
- ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
-
- if (ssi->fiq_init && ssi->dma_init) {
- ret = ssi->fiq_init;
- goto failed_pcm;
- }
-
- return 0;
-
-failed_pcm:
- snd_soc_unregister_component(&pdev->dev);
-failed_register:
- clk_disable_unprepare(ssi->clk);
-failed_clk:
- snd_soc_set_ac97_ops(NULL);
-
- return ret;
-}
-
-static int imx_ssi_remove(struct platform_device *pdev)
-{
- struct imx_ssi *ssi = platform_get_drvdata(pdev);
-
- if (!ssi->fiq_init)
- imx_pcm_fiq_exit(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
-
- if (ssi->flags & IMX_SSI_USE_AC97)
- ac97_ssi = NULL;
-
- clk_disable_unprepare(ssi->clk);
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver imx_ssi_driver = {
- .probe = imx_ssi_probe,
- .remove = imx_ssi_remove,
-
- .driver = {
- .name = "imx-ssi",
- },
-};
-
-module_platform_driver(imx_ssi_driver);
-
-/* Module information */
-MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>");
-MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx-ssi");
diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
index be6562365b6a..2d30d822451a 100644
--- a/sound/soc/fsl/imx-ssi.h
+++ b/sound/soc/fsl/imx-ssi.h
@@ -1,8 +1,4 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _IMX_SSI_H
#define _IMX_SSI_H
@@ -186,7 +182,7 @@
#define DRV_NAME "imx-ssi"
#include <linux/dmaengine.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include "imx-pcm.h"
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
deleted file mode 100644
index 201a70d1027a..000000000000
--- a/sound/soc/fsl/imx-wm8962.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2013 Freescale Semiconductor, Inc.
- *
- * Based on imx-sgtl5000.c
- * Copyright 2012 Freescale Semiconductor, Inc.
- * Copyright 2012 Linaro Ltd.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#include <sound/soc-dapm.h>
-#include <linux/pinctrl/consumer.h>
-
-#include "../codecs/wm8962.h"
-#include "imx-audmux.h"
-
-#define DAI_NAME_SIZE 32
-
-struct imx_wm8962_data {
- struct snd_soc_dai_link dai;
- struct snd_soc_card card;
- char codec_dai_name[DAI_NAME_SIZE];
- char platform_name[DAI_NAME_SIZE];
- struct clk *codec_clk;
- unsigned int clk_frequency;
-};
-
-struct imx_priv {
- struct platform_device *pdev;
-};
-static struct imx_priv card_priv;
-
-static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_MIC("AMIC", NULL),
- SND_SOC_DAPM_MIC("DMIC", NULL),
-};
-
-static int sample_rate = 44100;
-static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
-
-static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- sample_rate = params_rate(params);
- sample_format = params_format(params);
-
- return 0;
-}
-
-static struct snd_soc_ops imx_hifi_ops = {
- .hw_params = imx_hifi_hw_params,
-};
-
-static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
- struct snd_soc_dapm_context *dapm,
- enum snd_soc_bias_level level)
-{
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai *codec_dai;
- struct imx_priv *priv = &card_priv;
- struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
- struct device *dev = &priv->pdev->dev;
- unsigned int pll_out;
- int ret;
-
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
- if (dapm->dev != codec_dai->dev)
- return 0;
-
- switch (level) {
- case SND_SOC_BIAS_PREPARE:
- if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
- if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
- pll_out = sample_rate * 384;
- else
- pll_out = sample_rate * 256;
-
- ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
- WM8962_FLL_MCLK, data->clk_frequency,
- pll_out);
- if (ret < 0) {
- dev_err(dev, "failed to start FLL: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai,
- WM8962_SYSCLK_FLL, pll_out,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(dev, "failed to set SYSCLK: %d\n", ret);
- return ret;
- }
- }
- break;
-
- case SND_SOC_BIAS_STANDBY:
- if (dapm->bias_level == SND_SOC_BIAS_PREPARE) {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- WM8962_SYSCLK_MCLK, data->clk_frequency,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(dev,
- "failed to switch away from FLL: %d\n",
- ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
- 0, 0, 0);
- if (ret < 0) {
- dev_err(dev, "failed to stop FLL: %d\n", ret);
- return ret;
- }
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-static int imx_wm8962_late_probe(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai *codec_dai;
- struct imx_priv *priv = &card_priv;
- struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
- struct device *dev = &priv->pdev->dev;
- int ret;
-
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
- data->clk_frequency, SND_SOC_CLOCK_IN);
- if (ret < 0)
- dev_err(dev, "failed to set sysclk in %s\n", __func__);
-
- return ret;
-}
-
-static int imx_wm8962_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *ssi_np, *codec_np;
- struct platform_device *ssi_pdev;
- struct imx_priv *priv = &card_priv;
- struct i2c_client *codec_dev;
- struct imx_wm8962_data *data;
- int int_port, ext_port;
- int ret;
-
- priv->pdev = pdev;
-
- ret = of_property_read_u32(np, "mux-int-port", &int_port);
- if (ret) {
- dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
- return ret;
- }
- ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
- if (ret) {
- dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
- return ret;
- }
-
- /*
- * The port numbering in the hardware manual starts at 1, while
- * the audmux API expects it starts at 0.
- */
- int_port--;
- ext_port--;
- ret = imx_audmux_v2_configure_port(int_port,
- IMX_AUDMUX_V2_PTCR_SYN |
- IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
- IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
- IMX_AUDMUX_V2_PTCR_TFSDIR |
- IMX_AUDMUX_V2_PTCR_TCLKDIR,
- IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
- if (ret) {
- dev_err(&pdev->dev, "audmux internal port setup failed\n");
- return ret;
- }
- ret = imx_audmux_v2_configure_port(ext_port,
- IMX_AUDMUX_V2_PTCR_SYN,
- IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
- if (ret) {
- dev_err(&pdev->dev, "audmux external port setup failed\n");
- return ret;
- }
-
- ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
- codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
- if (!ssi_np || !codec_np) {
- dev_err(&pdev->dev, "phandle missing or invalid\n");
- ret = -EINVAL;
- goto fail;
- }
-
- ssi_pdev = of_find_device_by_node(ssi_np);
- if (!ssi_pdev) {
- dev_err(&pdev->dev, "failed to find SSI platform device\n");
- ret = -EINVAL;
- goto fail;
- }
- codec_dev = of_find_i2c_device_by_node(codec_np);
- if (!codec_dev || !codec_dev->dev.driver) {
- dev_err(&pdev->dev, "failed to find codec platform device\n");
- ret = -EINVAL;
- goto fail;
- }
-
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto fail;
- }
-
- data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
- if (IS_ERR(data->codec_clk)) {
- ret = PTR_ERR(data->codec_clk);
- dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
- goto fail;
- }
-
- data->clk_frequency = clk_get_rate(data->codec_clk);
- ret = clk_prepare_enable(data->codec_clk);
- if (ret) {
- dev_err(&codec_dev->dev, "failed to enable codec clk: %d\n", ret);
- goto fail;
- }
-
- data->dai.name = "HiFi";
- data->dai.stream_name = "HiFi";
- data->dai.codec_dai_name = "wm8962";
- data->dai.codec_of_node = codec_np;
- data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev);
- data->dai.platform_of_node = ssi_np;
- data->dai.ops = &imx_hifi_ops;
- data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
-
- data->card.dev = &pdev->dev;
- ret = snd_soc_of_parse_card_name(&data->card, "model");
- if (ret)
- goto clk_fail;
- ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
- if (ret)
- goto clk_fail;
- data->card.num_links = 1;
- data->card.owner = THIS_MODULE;
- data->card.dai_link = &data->dai;
- data->card.dapm_widgets = imx_wm8962_dapm_widgets;
- data->card.num_dapm_widgets = ARRAY_SIZE(imx_wm8962_dapm_widgets);
-
- data->card.late_probe = imx_wm8962_late_probe;
- data->card.set_bias_level = imx_wm8962_set_bias_level;
-
- platform_set_drvdata(pdev, &data->card);
- snd_soc_card_set_drvdata(&data->card, data);
-
- ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- goto clk_fail;
- }
-
- of_node_put(ssi_np);
- of_node_put(codec_np);
-
- return 0;
-
-clk_fail:
- clk_disable_unprepare(data->codec_clk);
-fail:
- of_node_put(ssi_np);
- of_node_put(codec_np);
-
- return ret;
-}
-
-static int imx_wm8962_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
-
- if (!IS_ERR(data->codec_clk))
- clk_disable_unprepare(data->codec_clk);
-
- return 0;
-}
-
-static const struct of_device_id imx_wm8962_dt_ids[] = {
- { .compatible = "fsl,imx-audio-wm8962", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_wm8962_dt_ids);
-
-static struct platform_driver imx_wm8962_driver = {
- .driver = {
- .name = "imx-wm8962",
- .pm = &snd_soc_pm_ops,
- .of_match_table = imx_wm8962_dt_ids,
- },
- .probe = imx_wm8962_probe,
- .remove = imx_wm8962_remove,
-};
-module_platform_driver(imx_wm8962_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("Freescale i.MX WM8962 ASoC machine driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:imx-wm8962");
diff --git a/sound/soc/fsl/lpc3xxx-i2s.c b/sound/soc/fsl/lpc3xxx-i2s.c
new file mode 100644
index 000000000000..c65c17dfa174
--- /dev/null
+++ b/sound/soc/fsl/lpc3xxx-i2s.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Author: Kevin Wells <kevin.wells@nxp.com>
+//
+// Copyright (C) 2008 NXP Semiconductors
+// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "lpc3xxx-i2s.h"
+
+#define I2S_PLAYBACK_FLAG 0x1
+#define I2S_CAPTURE_FLAG 0x2
+
+#define LPC3XXX_I2S_RATES ( \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define LPC3XXX_I2S_FORMATS ( \
+ SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate)
+{
+ u32 i2srate;
+ u32 idxx, idyy;
+ u32 diff, trate, baseclk;
+
+ /* Adjust rate for sample size (bits) and 2 channels and offset for
+ * divider in clock output
+ */
+ i2srate = (freq / 100) * 2 * (8 * xbytes);
+ i2srate = i2srate << 1;
+ clkrate = clkrate / 100;
+ baseclk = clkrate;
+ *clkx = 1;
+ *clky = 1;
+
+ /* Find the best divider */
+ *clkx = *clky = 0;
+ diff = ~0;
+ for (idxx = 1; idxx < 0xFF; idxx++) {
+ for (idyy = 1; idyy < 0xFF; idyy++) {
+ trate = (baseclk * idxx) / idyy;
+ if (abs(trate - i2srate) < diff) {
+ diff = abs(trate - i2srate);
+ *clkx = idxx;
+ *clky = idyy;
+ }
+ }
+ }
+}
+
+static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = i2s_info_p->dev;
+ u32 flag;
+ int ret = 0;
+
+ guard(mutex)(&i2s_info_p->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ flag = I2S_PLAYBACK_FLAG;
+ else
+ flag = I2S_CAPTURE_FLAG;
+
+ if (flag & i2s_info_p->streams_in_use) {
+ dev_warn(dev, "I2S channel is busy\n");
+ ret = -EBUSY;
+ return ret;
+ }
+
+ if (i2s_info_p->streams_in_use == 0) {
+ ret = clk_prepare_enable(i2s_info_p->clk);
+ if (ret) {
+ dev_err(dev, "Can't enable clock, err=%d\n", ret);
+ return ret;
+ }
+ }
+
+ i2s_info_p->streams_in_use |= flag;
+ return 0;
+}
+
+static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regs = i2s_info_p->regs;
+ const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP);
+ u32 flag;
+
+ guard(mutex)(&i2s_info_p->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ flag = I2S_PLAYBACK_FLAG;
+ regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0);
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits);
+ } else {
+ flag = I2S_CAPTURE_FLAG;
+ regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0);
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits);
+ }
+ i2s_info_p->streams_in_use &= ~flag;
+
+ if (i2s_info_p->streams_in_use == 0)
+ clk_disable_unprepare(i2s_info_p->clk);
+}
+
+static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* Will use in HW params later */
+ i2s_info_p->freq = freq;
+
+ return 0;
+}
+
+static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = i2s_info_p->dev;
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
+ dev_warn(dev, "unsupported bus format %d\n", fmt);
+ return -EINVAL;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ dev_warn(dev, "unsupported clock provider %d\n", fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = i2s_info_p->dev;
+ struct regmap *regs = i2s_info_p->regs;
+ int xfersize;
+ u32 tmp, clkx, clky;
+
+ tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP);
+ xfersize = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP);
+ xfersize = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP);
+ xfersize = 4;
+ break;
+
+ default:
+ dev_warn(dev, "Unsupported audio data format %d\n", params_format(params));
+ return -EINVAL;
+ }
+
+ if (params_channels(params) == 1)
+ tmp |= LPC3XXX_I2S_MONO;
+
+ __lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate);
+
+ dev_dbg(dev, "Stream : %s\n", snd_pcm_direction_name(substream->stream));
+ dev_dbg(dev, "Desired clock rate : %d\n", i2s_info_p->freq);
+ dev_dbg(dev, "Base clock rate : %d\n", i2s_info_p->clkrate);
+ dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize);
+ dev_dbg(dev, "Clock divider (x) : %d\n", clkx);
+ dev_dbg(dev, "Clock divider (y) : %d\n", clky);
+ dev_dbg(dev, "Channels : %d\n", params_channels(params));
+ dev_dbg(dev, "Data format : %s\n", "I2S");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(regs, LPC3XXX_REG_I2S_DMA1,
+ LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4));
+ regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky);
+ regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp);
+ } else {
+ regmap_write(regs, LPC3XXX_REG_I2S_DMA0,
+ LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4));
+ regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky);
+ regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp);
+ }
+
+ return 0;
+}
+
+static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regs = i2s_info_p->regs;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO,
+ LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP);
+ else
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI,
+ LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP);
+ break;
+
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO,
+ (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0);
+ else
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI,
+ (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config,
+ &i2s_info_p->capture_dma_config);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = {
+ .probe = lpc3xxx_i2s_dai_probe,
+ .startup = lpc3xxx_i2s_startup,
+ .shutdown = lpc3xxx_i2s_shutdown,
+ .trigger = lpc3xxx_i2s_trigger,
+ .hw_params = lpc3xxx_i2s_hw_params,
+ .set_sysclk = lpc3xxx_i2s_set_dai_sysclk,
+ .set_fmt = lpc3xxx_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = LPC3XXX_I2S_RATES,
+ .formats = LPC3XXX_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = LPC3XXX_I2S_RATES,
+ .formats = LPC3XXX_I2S_FORMATS,
+ },
+ .ops = &lpc3xxx_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct snd_soc_component_driver lpc32xx_i2s_component = {
+ .name = "lpc32xx-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config lpc32xx_i2s_regconfig = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LPC3XXX_REG_I2S_RX_RATE,
+};
+
+static int lpc32xx_i2s_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lpc3xxx_i2s_info *i2s_info_p;
+ struct resource *res;
+ void __iomem *iomem;
+ int ret;
+
+ i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL);
+ if (!i2s_info_p)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, i2s_info_p);
+ i2s_info_p->dev = dev;
+
+ iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(iomem))
+ return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n");
+
+ i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig);
+ if (IS_ERR(i2s_info_p->regs))
+ return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs),
+ "failed to init register map: %pe\n", i2s_info_p->regs);
+
+ i2s_info_p->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(i2s_info_p->clk))
+ return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n");
+
+ i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk);
+ if (i2s_info_p->clkrate == 0)
+ return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n");
+
+ mutex_init(&i2s_info_p->lock);
+
+ ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component,
+ &lpc3xxx_i2s_dai_driver, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't register cpu_dai component\n");
+
+ i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO);
+ i2s_info_p->playback_dma_config.maxburst = 4;
+
+ i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO);
+ i2s_info_p->capture_dma_config.maxburst = 4;
+
+ ret = lpc3xxx_pcm_register(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't register pcm component\n");
+
+ return 0;
+}
+
+static const struct of_device_id lpc32xx_i2s_match[] = {
+ { .compatible = "nxp,lpc3220-i2s" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match);
+
+static struct platform_driver lpc32xx_i2s_driver = {
+ .probe = lpc32xx_i2s_probe,
+ .driver = {
+ .name = "lpc3xxx-i2s",
+ .of_match_table = lpc32xx_i2s_match,
+ },
+};
+
+module_platform_driver(lpc32xx_i2s_driver);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_AUTHOR("Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>");
+MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/lpc3xxx-i2s.h b/sound/soc/fsl/lpc3xxx-i2s.h
new file mode 100644
index 000000000000..b6657853017a
--- /dev/null
+++ b/sound/soc/fsl/lpc3xxx-i2s.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2008 NXP Semiconductors
+ * Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
+ */
+
+#ifndef __SOUND_SOC_LPC3XXX_I2S_H
+#define __SOUND_SOC_LPC3XXX_I2S_H
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/regmap.h>
+
+struct lpc3xxx_i2s_info {
+ struct device *dev;
+ struct clk *clk;
+ struct mutex lock; /* To serialize user-space access */
+ struct regmap *regs;
+ u32 streams_in_use;
+ u32 clkrate;
+ int freq;
+ struct snd_dmaengine_dai_dma_data playback_dma_config;
+ struct snd_dmaengine_dai_dma_data capture_dma_config;
+};
+
+int lpc3xxx_pcm_register(struct platform_device *pdev);
+
+/* I2S controller register offsets */
+#define LPC3XXX_REG_I2S_DAO 0x00
+#define LPC3XXX_REG_I2S_DAI 0x04
+#define LPC3XXX_REG_I2S_TX_FIFO 0x08
+#define LPC3XXX_REG_I2S_RX_FIFO 0x0C
+#define LPC3XXX_REG_I2S_STAT 0x10
+#define LPC3XXX_REG_I2S_DMA0 0x14
+#define LPC3XXX_REG_I2S_DMA1 0x18
+#define LPC3XXX_REG_I2S_IRQ 0x1C
+#define LPC3XXX_REG_I2S_TX_RATE 0x20
+#define LPC3XXX_REG_I2S_RX_RATE 0x24
+
+/* i2s_daO i2s_dai register definitions */
+#define LPC3XXX_I2S_WW8 FIELD_PREP(0x3, 0) /* Word width is 8bit */
+#define LPC3XXX_I2S_WW16 FIELD_PREP(0x3, 1) /* Word width is 16bit */
+#define LPC3XXX_I2S_WW32 FIELD_PREP(0x3, 3) /* Word width is 32bit */
+#define LPC3XXX_I2S_MONO BIT(2) /* Mono */
+#define LPC3XXX_I2S_STOP BIT(3) /* Stop, diables the access to FIFO, mutes the channel */
+#define LPC3XXX_I2S_RESET BIT(4) /* Reset the channel */
+#define LPC3XXX_I2S_WS_SEL BIT(5) /* Channel Master(0) or slave(1) mode select */
+#define LPC3XXX_I2S_WS_HP(s) FIELD_PREP(0x7FC0, s) /* Word select half period - 1 */
+#define LPC3XXX_I2S_MUTE BIT(15) /* Mute the channel, Transmit channel only */
+
+#define LPC3XXX_I2S_WW32_HP 0x1f /* Word select half period for 32bit word width */
+#define LPC3XXX_I2S_WW16_HP 0x0f /* Word select half period for 16bit word width */
+#define LPC3XXX_I2S_WW8_HP 0x7 /* Word select half period for 8bit word width */
+
+/* i2s_stat register definitions */
+#define LPC3XXX_I2S_IRQ_STAT BIT(0)
+#define LPC3XXX_I2S_DMA0_REQ BIT(1)
+#define LPC3XXX_I2S_DMA1_REQ BIT(2)
+
+/* i2s_dma0 Configuration register definitions */
+#define LPC3XXX_I2S_DMA0_RX_EN BIT(0) /* Enable RX DMA1 */
+#define LPC3XXX_I2S_DMA0_TX_EN BIT(1) /* Enable TX DMA1 */
+#define LPC3XXX_I2S_DMA0_RX_DEPTH(s) FIELD_PREP(0xF00, s) /* Set the DMA1 RX Request level */
+#define LPC3XXX_I2S_DMA0_TX_DEPTH(s) FIELD_PREP(0xF0000, s) /* Set the DMA1 TX Request level */
+
+/* i2s_dma1 Configuration register definitions */
+#define LPC3XXX_I2S_DMA1_RX_EN BIT(0) /* Enable RX DMA1 */
+#define LPC3XXX_I2S_DMA1_TX_EN BIT(1) /* Enable TX DMA1 */
+#define LPC3XXX_I2S_DMA1_RX_DEPTH(s) FIELD_PREP(0x700, s) /* Set the DMA1 RX Request level */
+#define LPC3XXX_I2S_DMA1_TX_DEPTH(s) FIELD_PREP(0x70000, s) /* Set the DMA1 TX Request level */
+
+/* i2s_irq register definitions */
+#define LPC3XXX_I2S_RX_IRQ_EN BIT(0) /* Enable RX IRQ */
+#define LPC3XXX_I2S_TX_IRQ_EN BIT(1) /* Enable TX IRQ */
+#define LPC3XXX_I2S_IRQ_RX_DEPTH(s) FIELD_PREP(0xFF00, s) /* valid values ar 0 to 7 */
+#define LPC3XXX_I2S_IRQ_TX_DEPTH(s) FIELD_PREP(0xFF0000, s) /* valid values ar 0 to 7 */
+
+#endif
diff --git a/sound/soc/fsl/lpc3xxx-pcm.c b/sound/soc/fsl/lpc3xxx-pcm.c
new file mode 100644
index 000000000000..e6abaf63895a
--- /dev/null
+++ b/sound/soc/fsl/lpc3xxx-pcm.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Author: Kevin Wells <kevin.wells@nxp.com>
+//
+// Copyright (C) 2008 NXP Semiconductors
+// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/pl08x.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+#include "lpc3xxx-i2s.h"
+
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE | \
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+static const struct snd_pcm_hardware lpc3xxx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = STUB_FORMATS,
+ .period_bytes_min = 128,
+ .period_bytes_max = 2048,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 128 * 1024
+};
+
+static const struct snd_dmaengine_pcm_config lpc3xxx_dmaengine_pcm_config = {
+ .pcm_hardware = &lpc3xxx_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .compat_filter_fn = pl08x_filter_id,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+static const struct snd_soc_component_driver lpc3xxx_soc_platform_driver = {
+ .name = "lpc32xx-pcm",
+};
+
+int lpc3xxx_pcm_register(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &lpc3xxx_dmaengine_pcm_config, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dmaengine: %d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev, &lpc3xxx_soc_platform_driver,
+ NULL, 0);
+}
+EXPORT_SYMBOL(lpc3xxx_pcm_register);
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index 0b82e209b6e3..345f338251ac 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -1,18 +1,18 @@
-/*
- * Freescale MPC5200 PSC DMA
- * ALSA SoC Platform driver
- *
- * Copyright (C) 2008 Secret Lab Technologies Ltd.
- * Copyright (C) 2009 Jon Smirl, Digispeaker
- */
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Freescale MPC5200 PSC DMA
+// ALSA SoC Platform driver
+//
+// Copyright (C) 2008 Secret Lab Technologies Ltd.
+// Copyright (C) 2009 Jon Smirl, Digispeaker
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <sound/soc.h>
@@ -22,6 +22,8 @@
#include "mpc5200_dma.h"
+#define DRV_NAME "mpc5200_dma"
+
/*
* Interrupt handlers
*/
@@ -96,22 +98,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
return IRQ_HANDLED;
}
-static int psc_dma_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
/**
* psc_dma_trigger: start and stop the DMA transfer.
+ * @component: triggered component
+ * @substream: triggered substream
+ * @cmd: triggered command
*
* This function is called by ALSA to start, stop, pause, and resume the DMA
* transfer of data.
*/
-static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int psc_dma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct snd_pcm_runtime *runtime = substream->runtime;
struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
@@ -208,11 +208,12 @@ static const struct snd_pcm_hardware psc_dma_hardware = {
.fifo_size = 512,
};
-static int psc_dma_open(struct snd_pcm_substream *substream)
+static int psc_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct psc_dma_stream *s;
int rc;
@@ -236,10 +237,11 @@ static int psc_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int psc_dma_close(struct snd_pcm_substream *substream)
+static int psc_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct psc_dma_stream *s;
dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream);
@@ -261,10 +263,11 @@ static int psc_dma_close(struct snd_pcm_substream *substream)
}
static snd_pcm_uframes_t
-psc_dma_pointer(struct snd_pcm_substream *substream)
+psc_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct psc_dma_stream *s;
dma_addr_t count;
@@ -278,89 +281,33 @@ psc_dma_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, count);
}
-static int
-psc_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
- return 0;
-}
-
-static struct snd_pcm_ops psc_dma_ops = {
- .open = psc_dma_open,
- .close = psc_dma_close,
- .hw_free = psc_dma_hw_free,
- .ioctl = snd_pcm_lib_ioctl,
- .pointer = psc_dma_pointer,
- .trigger = psc_dma_trigger,
- .hw_params = psc_dma_hw_params,
-};
-
-static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
+static int psc_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_pcm *pcm = rtd->pcm;
- struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
size_t size = psc_dma_hardware.buffer_bytes_max;
int rc;
- dev_dbg(rtd->platform->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n",
+ dev_dbg(component->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n",
card, dai, pcm);
rc = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (rc)
return rc;
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
- size, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer);
- if (rc)
- goto playback_alloc_err;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
- size, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->dma_buffer);
- if (rc)
- goto capture_alloc_err;
- }
-
- return 0;
-
- capture_alloc_err:
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
- snd_dma_free_pages(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer);
-
- playback_alloc_err:
- dev_err(card->dev, "Cannot allocate buffer(s)\n");
-
- return -ENOMEM;
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ size);
}
-static void psc_dma_free(struct snd_pcm *pcm)
-{
- struct snd_soc_pcm_runtime *rtd = pcm->private_data;
- struct snd_pcm_substream *substream;
- int stream;
-
- dev_dbg(rtd->platform->dev, "psc_dma_free(pcm=%p)\n", pcm);
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (substream) {
- snd_dma_free_pages(&substream->dma_buffer);
- substream->dma_buffer.area = NULL;
- substream->dma_buffer.addr = 0;
- }
- }
-}
-
-static struct snd_soc_platform_driver mpc5200_audio_dma_platform = {
- .ops = &psc_dma_ops,
- .pcm_new = &psc_dma_new,
- .pcm_free = &psc_dma_free,
+static const struct snd_soc_component_driver mpc5200_audio_dma_component = {
+ .name = DRV_NAME,
+ .open = psc_dma_open,
+ .close = psc_dma_close,
+ .pointer = psc_dma_pointer,
+ .trigger = psc_dma_trigger,
+ .pcm_construct = psc_dma_new,
};
int mpc5200_audio_dma_create(struct platform_device *op)
@@ -408,7 +355,7 @@ int mpc5200_audio_dma_create(struct platform_device *op)
psc_dma->dev = &op->dev;
psc_dma->playback.psc_dma = psc_dma;
psc_dma->capture.psc_dma = psc_dma;
- snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id);
+ snprintf(psc_dma->name, sizeof(psc_dma->name), "PSC%d", psc_dma->id);
/* Find the address of the fifo data registers and setup the
* DMA tasks */
@@ -469,7 +416,8 @@ int mpc5200_audio_dma_create(struct platform_device *op)
dev_set_drvdata(&op->dev, psc_dma);
/* Tell the ASoC OF helpers about it */
- return snd_soc_register_platform(&op->dev, &mpc5200_audio_dma_platform);
+ return devm_snd_soc_register_component(&op->dev,
+ &mpc5200_audio_dma_component, NULL, 0);
out_irq:
free_irq(psc_dma->irq, psc_dma);
free_irq(psc_dma->capture.irq, &psc_dma->capture);
@@ -488,8 +436,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op)
dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n");
- snd_soc_unregister_platform(&op->dev);
-
bcom_gen_bd_rx_release(psc_dma->capture.bcom_task);
bcom_gen_bd_tx_release(psc_dma->playback.bcom_task);
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h
index dff253fde29a..d7ee33b5b9a8 100644
--- a/sound/soc/fsl/mpc5200_dma.h
+++ b/sound/soc/fsl/mpc5200_dma.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Freescale MPC5200 Audio DMA driver
*/
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 0bab76051fd8..8554fb690772 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -1,18 +1,14 @@
-/*
- * linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip.
- *
- * Copyright (C) 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 version 2 as
- * published by the Free Software Foundation.
- */
-
+// SPDX-License-Identifier: GPL-2.0
+//
+// linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip.
+//
+// Copyright (C) 2009 Jon Smirl, Digispeaker
+// Author: Jon Smirl <jonsmirl@gmail.com>
+
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
#include <linux/delay.h>
+#include <linux/time.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -24,7 +20,6 @@
#include <asm/mpc52xx_psc.h>
#include "mpc5200_dma.h"
-#include "mpc5200_psc_ac97.h"
#define DRV_NAME "mpc5200-psc-ac97"
@@ -127,7 +122,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
mutex_unlock(&psc_dma->mutex);
- msleep(1);
+ usleep_range(1000, 2000);
psc_ac97_warm_reset(ac97);
}
@@ -226,6 +221,7 @@ static int psc_ac97_probe(struct snd_soc_dai *cpu_dai)
* psc_ac97_dai_template: template CPU Digital Audio Interface
*/
static const struct snd_soc_dai_ops psc_ac97_analog_ops = {
+ .probe = psc_ac97_probe,
.hw_params = psc_ac97_hw_analog_params,
.trigger = psc_ac97_trigger,
};
@@ -237,8 +233,6 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
static struct snd_soc_dai_driver psc_ac97_dai[] = {
{
.name = "mpc5200-psc-ac97.0",
- .bus_control = true,
- .probe = psc_ac97_probe,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 1,
@@ -257,7 +251,6 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
},
{
.name = "mpc5200-psc-ac97.1",
- .bus_control = true,
.playback = {
.stream_name = "AC97 SPDIF",
.channels_min = 1,
@@ -317,12 +310,11 @@ static int psc_ac97_of_probe(struct platform_device *op)
return 0;
}
-static int psc_ac97_of_remove(struct platform_device *op)
+static void psc_ac97_of_remove(struct platform_device *op)
{
mpc5200_audio_dma_destroy(op);
snd_soc_unregister_component(&op->dev);
snd_soc_set_ac97_ops(NULL);
- return 0;
}
/* Match table for of_platform binding */
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h
deleted file mode 100644
index e881e784b270..000000000000
--- a/sound/soc/fsl/mpc5200_psc_ac97.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Freescale MPC5200 PSC in AC97 mode
- * ALSA SoC Digital Audio Interface (DAI) driver
- *
- */
-
-#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
-#define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
-
-#define MPC5200_AC97_NORMAL 0
-#define MPC5200_AC97_SPDIF 1
-
-#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index d8232943ccb6..9ad44eeed6ad 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -1,14 +1,13 @@
-/*
- * Freescale MPC5200 PSC in I2S mode
- * ALSA SoC Digital Audio Interface (DAI) driver
- *
- * Copyright (C) 2008 Secret Lab Technologies Ltd.
- * Copyright (C) 2009 Jon Smirl, Digispeaker
- */
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Freescale MPC5200 PSC in I2S mode
+// ALSA SoC Digital Audio Interface (DAI) driver
+//
+// Copyright (C) 2008 Secret Lab Technologies Ltd.
+// Copyright (C) 2009 Jon Smirl, Digispeaker
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -38,8 +37,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
u32 mode;
dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
@@ -148,7 +147,8 @@ static struct snd_soc_dai_driver psc_i2s_dai[] = {{
} };
static const struct snd_soc_component_driver psc_i2s_component = {
- .name = "mpc5200-i2s",
+ .name = "mpc5200-i2s",
+ .legacy_dai_naming = 1,
};
/* ---------------------------------------------------------------------
@@ -184,7 +184,7 @@ static int psc_i2s_of_probe(struct platform_device *op)
/* Check for the codec handle. If it is not present then we
* are done */
- if (!of_get_property(op->dev.of_node, "codec-handle", NULL))
+ if (!of_property_present(op->dev.of_node, "codec-handle"))
return 0;
/* Due to errata in the dma mode; need to line up enabling
@@ -209,11 +209,10 @@ static int psc_i2s_of_probe(struct platform_device *op)
}
-static int psc_i2s_of_remove(struct platform_device *op)
+static void psc_i2s_of_remove(struct platform_device *op)
{
mpc5200_audio_dma_destroy(op);
snd_soc_unregister_component(&op->dev);
- return 0;
}
/* Match table for of_platform binding */
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
deleted file mode 100644
index ddf49f30b23f..000000000000
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ /dev/null
@@ -1,432 +0,0 @@
-/**
- * Freescale MPC8610HPCD ALSA SoC Machine driver
- *
- * Author: Timur Tabi <timur@freescale.com>
- *
- * Copyright 2007-2010 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.
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/fsl/guts.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/slab.h>
-#include <sound/soc.h>
-
-#include "fsl_dma.h"
-#include "fsl_ssi.h"
-#include "fsl_utils.h"
-
-/* There's only one global utilities register */
-static phys_addr_t guts_phys;
-
-/**
- * mpc8610_hpcd_data: machine-specific ASoC device data
- *
- * This structure contains data for a single sound platform device on an
- * MPC8610 HPCD. Some of the data is taken from the device tree.
- */
-struct mpc8610_hpcd_data {
- struct snd_soc_dai_link dai[2];
- struct snd_soc_card card;
- unsigned int dai_format;
- unsigned int codec_clk_direction;
- unsigned int cpu_clk_direction;
- unsigned int clk_frequency;
- unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
- unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
- unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
- char codec_dai_name[DAI_NAME_SIZE];
- char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
-};
-
-/**
- * mpc8610_hpcd_machine_probe: initialize the board
- *
- * This function is used to initialize the board-specific hardware.
- *
- * Here we program the DMACR and PMUXCR registers.
- */
-static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
-{
- struct mpc8610_hpcd_data *machine_data =
- container_of(card, struct mpc8610_hpcd_data, card);
- struct ccsr_guts __iomem *guts;
-
- guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
- if (!guts) {
- dev_err(card->dev, "could not map global utilities\n");
- return -ENOMEM;
- }
-
- /* Program the signal routing between the SSI and the DMA */
- guts_set_dmacr(guts, machine_data->dma_id[0],
- machine_data->dma_channel_id[0],
- CCSR_GUTS_DMACR_DEV_SSI);
- guts_set_dmacr(guts, machine_data->dma_id[1],
- machine_data->dma_channel_id[1],
- CCSR_GUTS_DMACR_DEV_SSI);
-
- guts_set_pmuxcr_dma(guts, machine_data->dma_id[0],
- machine_data->dma_channel_id[0], 0);
- guts_set_pmuxcr_dma(guts, machine_data->dma_id[1],
- machine_data->dma_channel_id[1], 0);
-
- switch (machine_data->ssi_id) {
- case 0:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
- break;
- case 1:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
- break;
- }
-
- iounmap(guts);
-
- return 0;
-}
-
-/**
- * mpc8610_hpcd_startup: program the board with various hardware parameters
- *
- * This function takes board-specific information, like clock frequencies
- * and serial data formats, and passes that information to the codec and
- * transport drivers.
- */
-static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mpc8610_hpcd_data *machine_data =
- container_of(rtd->card, struct mpc8610_hpcd_data, card);
- struct device *dev = rtd->card->dev;
- int ret = 0;
-
- /* Tell the codec driver what the serial protocol is. */
- ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format);
- if (ret < 0) {
- dev_err(dev, "could not set codec driver audio format\n");
- return ret;
- }
-
- /*
- * Tell the codec driver what the MCLK frequency is, and whether it's
- * a slave or master.
- */
- ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0,
- machine_data->clk_frequency,
- machine_data->codec_clk_direction);
- if (ret < 0) {
- dev_err(dev, "could not set codec driver clock params\n");
- return ret;
- }
-
- return 0;
-}
-
-/**
- * mpc8610_hpcd_machine_remove: Remove the sound device
- *
- * This function is called to remove the sound device for one SSI. We
- * de-program the DMACR and PMUXCR register.
- */
-static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
-{
- struct mpc8610_hpcd_data *machine_data =
- container_of(card, struct mpc8610_hpcd_data, card);
- struct ccsr_guts __iomem *guts;
-
- guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
- if (!guts) {
- dev_err(card->dev, "could not map global utilities\n");
- return -ENOMEM;
- }
-
- /* Restore the signal routing */
-
- guts_set_dmacr(guts, machine_data->dma_id[0],
- machine_data->dma_channel_id[0], 0);
- guts_set_dmacr(guts, machine_data->dma_id[1],
- machine_data->dma_channel_id[1], 0);
-
- switch (machine_data->ssi_id) {
- case 0:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
- break;
- case 1:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
- break;
- }
-
- iounmap(guts);
-
- return 0;
-}
-
-/**
- * mpc8610_hpcd_ops: ASoC machine driver operations
- */
-static struct snd_soc_ops mpc8610_hpcd_ops = {
- .startup = mpc8610_hpcd_startup,
-};
-
-/**
- * mpc8610_hpcd_probe: platform probe function for the machine driver
- *
- * Although this is a machine driver, the SSI node is the "master" node with
- * respect to audio hardware connections. Therefore, we create a new ASoC
- * device for each new SSI node that has a codec attached.
- */
-static int mpc8610_hpcd_probe(struct platform_device *pdev)
-{
- struct device *dev = pdev->dev.parent;
- /* ssi_pdev is the platform device for the SSI node that probed us */
- struct platform_device *ssi_pdev = to_platform_device(dev);
- struct device_node *np = ssi_pdev->dev.of_node;
- struct device_node *codec_np = NULL;
- struct mpc8610_hpcd_data *machine_data;
- int ret = -ENODEV;
- const char *sprop;
- const u32 *iprop;
-
- /* Find the codec node for this SSI. */
- codec_np = of_parse_phandle(np, "codec-handle", 0);
- if (!codec_np) {
- dev_err(dev, "invalid codec node\n");
- return -EINVAL;
- }
-
- machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
- if (!machine_data) {
- ret = -ENOMEM;
- goto error_alloc;
- }
-
- machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
- machine_data->dai[0].ops = &mpc8610_hpcd_ops;
-
- /* ASoC core can match codec with device node */
- machine_data->dai[0].codec_of_node = codec_np;
-
- /* The DAI name from the codec (snd_soc_dai_driver.name) */
- machine_data->dai[0].codec_dai_name = "cs4270-hifi";
-
- /* We register two DAIs per SSI, one for playback and the other for
- * capture. Currently, we only support codecs that have one DAI for
- * both playback and capture.
- */
- memcpy(&machine_data->dai[1], &machine_data->dai[0],
- sizeof(struct snd_soc_dai_link));
-
- /* Get the device ID */
- iprop = of_get_property(np, "cell-index", NULL);
- if (!iprop) {
- dev_err(&pdev->dev, "cell-index property not found\n");
- ret = -EINVAL;
- goto error;
- }
- machine_data->ssi_id = be32_to_cpup(iprop);
-
- /* Get the serial format and clock direction. */
- sprop = of_get_property(np, "fsl,mode", NULL);
- if (!sprop) {
- dev_err(&pdev->dev, "fsl,mode property not found\n");
- ret = -EINVAL;
- goto error;
- }
-
- if (strcasecmp(sprop, "i2s-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
-
- /* In i2s-slave mode, the codec has its own clock source, so we
- * need to get the frequency from the device tree and pass it to
- * the codec driver.
- */
- iprop = of_get_property(codec_np, "clock-frequency", NULL);
- if (!iprop || !*iprop) {
- dev_err(&pdev->dev, "codec bus-frequency "
- "property is missing or invalid\n");
- ret = -EINVAL;
- goto error;
- }
- machine_data->clk_frequency = be32_to_cpup(iprop);
- } else if (strcasecmp(sprop, "i2s-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else if (strcasecmp(sprop, "lj-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
- } else if (strcasecmp(sprop, "lj-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else if (strcasecmp(sprop, "rj-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBM_CFM;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
- } else if (strcasecmp(sprop, "rj-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBS_CFS;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else if (strcasecmp(sprop, "ac97-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBM_CFM;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
- } else if (strcasecmp(sprop, "ac97-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBS_CFS;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else {
- dev_err(&pdev->dev,
- "unrecognized fsl,mode property '%s'\n", sprop);
- ret = -EINVAL;
- goto error;
- }
-
- if (!machine_data->clk_frequency) {
- dev_err(&pdev->dev, "unknown clock frequency\n");
- ret = -EINVAL;
- goto error;
- }
-
- /* Find the playback DMA channel to use. */
- machine_data->dai[0].platform_name = machine_data->platform_name[0];
- ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma",
- &machine_data->dai[0],
- &machine_data->dma_channel_id[0],
- &machine_data->dma_id[0]);
- if (ret) {
- dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
- goto error;
- }
-
- /* Find the capture DMA channel to use. */
- machine_data->dai[1].platform_name = machine_data->platform_name[1];
- ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma",
- &machine_data->dai[1],
- &machine_data->dma_channel_id[1],
- &machine_data->dma_id[1]);
- if (ret) {
- dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
- goto error;
- }
-
- /* Initialize our DAI data structure. */
- machine_data->dai[0].stream_name = "playback";
- machine_data->dai[1].stream_name = "capture";
- machine_data->dai[0].name = machine_data->dai[0].stream_name;
- machine_data->dai[1].name = machine_data->dai[1].stream_name;
-
- machine_data->card.probe = mpc8610_hpcd_machine_probe;
- machine_data->card.remove = mpc8610_hpcd_machine_remove;
- machine_data->card.name = pdev->name; /* The platform driver name */
- machine_data->card.owner = THIS_MODULE;
- machine_data->card.dev = &pdev->dev;
- machine_data->card.num_links = 2;
- machine_data->card.dai_link = machine_data->dai;
-
- /* Register with ASoC */
- ret = snd_soc_register_card(&machine_data->card);
- if (ret) {
- dev_err(&pdev->dev, "could not register card\n");
- goto error;
- }
-
- of_node_put(codec_np);
-
- return 0;
-
-error:
- kfree(machine_data);
-error_alloc:
- of_node_put(codec_np);
- return ret;
-}
-
-/**
- * mpc8610_hpcd_remove: remove the platform device
- *
- * This function is called when the platform device is removed.
- */
-static int mpc8610_hpcd_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct mpc8610_hpcd_data *machine_data =
- container_of(card, struct mpc8610_hpcd_data, card);
-
- snd_soc_unregister_card(card);
- kfree(machine_data);
-
- return 0;
-}
-
-static struct platform_driver mpc8610_hpcd_driver = {
- .probe = mpc8610_hpcd_probe,
- .remove = mpc8610_hpcd_remove,
- .driver = {
- /* The name must match 'compatible' property in the device tree,
- * in lowercase letters.
- */
- .name = "snd-soc-mpc8610hpcd",
- },
-};
-
-/**
- * mpc8610_hpcd_init: machine driver initialization.
- *
- * This function is called when this module is loaded.
- */
-static int __init mpc8610_hpcd_init(void)
-{
- struct device_node *guts_np;
- struct resource res;
-
- pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n");
-
- /* Get the physical address of the global utilities registers */
- guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
- if (of_address_to_resource(guts_np, 0, &res)) {
- pr_err("mpc8610-hpcd: missing/invalid global utilities node\n");
- return -EINVAL;
- }
- guts_phys = res.start;
-
- return platform_driver_register(&mpc8610_hpcd_driver);
-}
-
-/**
- * mpc8610_hpcd_exit: machine driver exit
- *
- * This function is called when this driver is unloaded.
- */
-static void __exit mpc8610_hpcd_exit(void)
-{
- platform_driver_unregister(&mpc8610_hpcd_driver);
-}
-
-module_init(mpc8610_hpcd_init);
-module_exit(mpc8610_hpcd_exit);
-
-MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
-MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
deleted file mode 100644
index 198eeb3f3f7a..000000000000
--- a/sound/soc/fsl/mx27vis-aic32x4.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * mx27vis-aic32x4.c
- *
- * Copyright 2011 Vista Silicon S.L.
- *
- * Author: Javier Martin <javier.martin@vista-silicon.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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/gpio.h>
-#include <linux/platform_data/asoc-mx27vis.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/tlv.h>
-#include <asm/mach-types.h>
-
-#include "../codecs/tlv320aic32x4.h"
-#include "imx-ssi.h"
-#include "imx-audmux.h"
-
-#define MX27VIS_AMP_GAIN 0
-#define MX27VIS_AMP_MUTE 1
-
-static int mx27vis_amp_gain;
-static int mx27vis_amp_mute;
-static int mx27vis_amp_gain0_gpio;
-static int mx27vis_amp_gain1_gpio;
-static int mx27vis_amp_mutel_gpio;
-static int mx27vis_amp_muter_gpio;
-
-static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0,
- 25000000, SND_SOC_CLOCK_OUT);
- if (ret) {
- pr_err("%s: failed setting codec sysclk\n", __func__);
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
- SND_SOC_CLOCK_IN);
- if (ret) {
- pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
- .hw_params = mx27vis_aic32x4_hw_params,
-};
-
-static int mx27vis_amp_set(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int value = ucontrol->value.integer.value[0];
- unsigned int reg = mc->reg;
- int max = mc->max;
-
- if (value > max)
- return -EINVAL;
-
- switch (reg) {
- case MX27VIS_AMP_GAIN:
- gpio_set_value(mx27vis_amp_gain0_gpio, value & 1);
- gpio_set_value(mx27vis_amp_gain1_gpio, value >> 1);
- mx27vis_amp_gain = value;
- break;
- case MX27VIS_AMP_MUTE:
- gpio_set_value(mx27vis_amp_mutel_gpio, value & 1);
- gpio_set_value(mx27vis_amp_muter_gpio, value >> 1);
- mx27vis_amp_mute = value;
- break;
- }
- return 0;
-}
-
-static int mx27vis_amp_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
-
- switch (reg) {
- case MX27VIS_AMP_GAIN:
- ucontrol->value.integer.value[0] = mx27vis_amp_gain;
- break;
- case MX27VIS_AMP_MUTE:
- ucontrol->value.integer.value[0] = mx27vis_amp_mute;
- break;
- }
- return 0;
-}
-
-/* From 6dB to 24dB in steps of 6dB */
-static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0);
-
-static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = {
- SOC_DAPM_PIN_SWITCH("External Mic"),
- SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0,
- mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv),
- SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0,
- mx27vis_amp_get, mx27vis_amp_set),
-};
-
-static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("External Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
- {"Mic Bias", NULL, "External Mic"},
- {"IN1_R", NULL, "Mic Bias"},
- {"IN2_R", NULL, "Mic Bias"},
- {"IN3_R", NULL, "Mic Bias"},
- {"IN1_L", NULL, "Mic Bias"},
- {"IN2_L", NULL, "Mic Bias"},
- {"IN3_L", NULL, "Mic Bias"},
-};
-
-static struct snd_soc_dai_link mx27vis_aic32x4_dai = {
- .name = "tlv320aic32x4",
- .stream_name = "TLV320AIC32X4",
- .codec_dai_name = "tlv320aic32x4-hifi",
- .platform_name = "imx-ssi.0",
- .codec_name = "tlv320aic32x4.0-0018",
- .cpu_dai_name = "imx-ssi.0",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &mx27vis_aic32x4_snd_ops,
-};
-
-static struct snd_soc_card mx27vis_aic32x4 = {
- .name = "visstrim_m10-audio",
- .owner = THIS_MODULE,
- .dai_link = &mx27vis_aic32x4_dai,
- .num_links = 1,
- .controls = mx27vis_aic32x4_controls,
- .num_controls = ARRAY_SIZE(mx27vis_aic32x4_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 mx27vis_aic32x4_probe(struct platform_device *pdev)
-{
- struct snd_mx27vis_platform_data *pdata = pdev->dev.platform_data;
- int ret;
-
- if (!pdata) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
-
- mx27vis_amp_gain0_gpio = pdata->amp_gain0_gpio;
- mx27vis_amp_gain1_gpio = pdata->amp_gain1_gpio;
- mx27vis_amp_mutel_gpio = pdata->amp_mutel_gpio;
- mx27vis_amp_muter_gpio = pdata->amp_muter_gpio;
-
- mx27vis_aic32x4.dev = &pdev->dev;
- ret = snd_soc_register_card(&mx27vis_aic32x4);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- return ret;
- }
-
- /* Connect SSI0 as clock slave to SSI1 external pins */
- imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
- IMX_AUDMUX_V1_PCR_SYN |
- IMX_AUDMUX_V1_PCR_TFSDIR |
- IMX_AUDMUX_V1_PCR_TCLKDIR |
- IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) |
- IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1)
- );
- imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1,
- IMX_AUDMUX_V1_PCR_SYN |
- IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
- );
-
- return ret;
-}
-
-static int mx27vis_aic32x4_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&mx27vis_aic32x4);
-
- return 0;
-}
-
-static struct platform_driver mx27vis_aic32x4_audio_driver = {
- .driver = {
- .name = "mx27vis",
- },
- .probe = mx27vis_aic32x4_probe,
- .remove = mx27vis_aic32x4_remove,
-};
-
-module_platform_driver(mx27vis_aic32x4_audio_driver);
-
-MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
-MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:mx27vis");
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index a1f780ecadf5..66db05970d82 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -1,20 +1,16 @@
-/**
- * Freescale P1022DS ALSA SoC Machine driver
- *
- * Author: Timur Tabi <timur@freescale.com>
- *
- * Copyright 2010 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale P1022DS ALSA SoC Machine driver
+//
+// Author: Timur Tabi <timur@freescale.com>
+//
+// Copyright 2010 Freescale Semiconductor, Inc.
#include <linux/module.h>
#include <linux/fsl/guts.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
@@ -125,14 +121,14 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card)
*/
static int p1022_ds_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct machine_data *mdata =
container_of(rtd->card, struct machine_data, card);
struct device *dev = rtd->card->dev;
int ret = 0;
/* Tell the codec driver what the serial protocol is. */
- ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format);
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0), mdata->dai_format);
if (ret < 0) {
dev_err(dev, "could not set codec driver audio format\n");
return ret;
@@ -142,7 +138,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream)
* Tell the codec driver what the MCLK frequency is, and whether it's
* a slave or master.
*/
- ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, mdata->clk_frequency,
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), 0, mdata->clk_frequency,
mdata->codec_clk_direction);
if (ret < 0) {
dev_err(dev, "could not set codec driver clock params\n");
@@ -184,7 +180,7 @@ static int p1022_ds_machine_remove(struct snd_soc_card *card)
/**
* p1022_ds_ops: ASoC machine driver operations
*/
-static struct snd_soc_ops p1022_ds_ops = {
+static const struct snd_soc_ops p1022_ds_ops = {
.startup = p1022_ds_startup,
};
@@ -203,7 +199,8 @@ static int p1022_ds_probe(struct platform_device *pdev)
struct device_node *np = ssi_pdev->dev.of_node;
struct device_node *codec_np = NULL;
struct machine_data *mdata;
- int ret = -ENODEV;
+ struct snd_soc_dai_link_component *comp;
+ int ret;
const char *sprop;
const u32 *iprop;
@@ -220,11 +217,34 @@ static int p1022_ds_probe(struct platform_device *pdev)
goto error_put;
}
- mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
+ comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL);
+ if (!comp) {
+ ret = -ENOMEM;
+ goto error_put;
+ }
+
+ mdata->dai[0].cpus = &comp[0];
+ mdata->dai[0].codecs = &comp[1];
+ mdata->dai[0].platforms = &comp[2];
+
+ mdata->dai[0].num_cpus = 1;
+ mdata->dai[0].num_codecs = 1;
+ mdata->dai[0].num_platforms = 1;
+
+ mdata->dai[1].cpus = &comp[3];
+ mdata->dai[1].codecs = &comp[4];
+ mdata->dai[1].platforms = &comp[5];
+
+ mdata->dai[1].num_cpus = 1;
+ mdata->dai[1].num_codecs = 1;
+ mdata->dai[1].num_platforms = 1;
+
+
+ mdata->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev);
mdata->dai[0].ops = &p1022_ds_ops;
/* ASoC core can match codec with device node */
- mdata->dai[0].codec_of_node = codec_np;
+ mdata->dai[0].codecs->of_node = codec_np;
/* We register two DAIs per SSI, one for playback and the other for
* capture. We support codecs that have separate DAIs for both playback
@@ -233,8 +253,8 @@ static int p1022_ds_probe(struct platform_device *pdev)
memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link));
/* The DAI names from the codec (snd_soc_dai_driver.name) */
- mdata->dai[0].codec_dai_name = "wm8776-hifi-playback";
- mdata->dai[1].codec_dai_name = "wm8776-hifi-capture";
+ mdata->dai[0].codecs->dai_name = "wm8776-hifi-playback";
+ mdata->dai[1].codecs->dai_name = "wm8776-hifi-capture";
/* Get the device ID */
iprop = of_get_property(np, "cell-index", NULL);
@@ -255,7 +275,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
if (strcasecmp(sprop, "i2s-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
@@ -273,37 +293,37 @@ static int p1022_ds_probe(struct platform_device *pdev)
mdata->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "lj-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "lj-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "rj-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "rj-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else if (strcasecmp(sprop, "ac97-slave") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
} else if (strcasecmp(sprop, "ac97-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBS_CFS;
+ SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
} else {
@@ -320,7 +340,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
}
/* Find the playback DMA channel to use. */
- mdata->dai[0].platform_name = mdata->platform_name[0];
+ mdata->dai[0].platforms->name = mdata->platform_name[0];
ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
&mdata->dma_channel_id[0],
&mdata->dma_id[0]);
@@ -330,7 +350,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
}
/* Find the capture DMA channel to use. */
- mdata->dai[1].platform_name = mdata->platform_name[1];
+ mdata->dai[1].platforms->name = mdata->platform_name[1];
ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
&mdata->dma_channel_id[1],
&mdata->dma_id[1]);
@@ -376,7 +396,7 @@ error_put:
*
* This function is called when the platform device is removed.
*/
-static int p1022_ds_remove(struct platform_device *pdev)
+static void p1022_ds_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct machine_data *mdata =
@@ -384,8 +404,6 @@ static int p1022_ds_remove(struct platform_device *pdev)
snd_soc_unregister_card(card);
kfree(mdata);
-
- return 0;
}
static struct platform_driver p1022_ds_driver = {
diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c
index d4d88a8cb9c0..d4568346714f 100644
--- a/sound/soc/fsl/p1022_rdk.c
+++ b/sound/soc/fsl/p1022_rdk.c
@@ -1,27 +1,23 @@
-/**
- * Freescale P1022RDK ALSA SoC Machine driver
- *
- * Author: Timur Tabi <timur@freescale.com>
- *
- * Copyright 2012 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.
- *
- * Note: in order for audio to work correctly, the output controls need
- * to be enabled, because they control the clock. So for playback, for
- * example:
- *
- * amixer sset 'Left Output Mixer PCM' on
- * amixer sset 'Right Output Mixer PCM' on
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Freescale P1022RDK ALSA SoC Machine driver
+//
+// Author: Timur Tabi <timur@freescale.com>
+//
+// Copyright 2012 Freescale Semiconductor, Inc.
+//
+// Note: in order for audio to work correctly, the output controls need
+// to be enabled, because they control the clock. So for playback, for
+// example:
+//
+// amixer sset 'Left Output Mixer PCM' on
+// amixer sset 'Right Output Mixer PCM' on
#include <linux/module.h>
#include <linux/fsl/guts.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
@@ -65,7 +61,7 @@ static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
/* There's only one global utilities register */
static phys_addr_t guts_phys;
-/**
+/*
* machine_data: machine-specific ASoC device data
*
* This structure contains data for a single sound platform device on an
@@ -84,11 +80,14 @@ struct machine_data {
};
/**
- * p1022_rdk_machine_probe: initialize the board
+ * p1022_rdk_machine_probe - initialize the board
+ * @card: ASoC card instance
*
* This function is used to initialize the board-specific hardware.
*
* Here we program the DMACR and PMUXCR registers.
+ *
+ * Returns: %0 on success or negative errno value on error
*/
static int p1022_rdk_machine_probe(struct snd_soc_card *card)
{
@@ -123,29 +122,32 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card)
}
/**
- * p1022_rdk_startup: program the board with various hardware parameters
+ * p1022_rdk_startup - program the board with various hardware parameters
+ * @substream: ASoC substream object
*
* This function takes board-specific information, like clock frequencies
* and serial data formats, and passes that information to the codec and
* transport drivers.
+ *
+ * Returns: %0 on success or negative errno value on error
*/
static int p1022_rdk_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct machine_data *mdata =
container_of(rtd->card, struct machine_data, card);
struct device *dev = rtd->card->dev;
int ret = 0;
/* Tell the codec driver what the serial protocol is. */
- ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format);
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0), mdata->dai_format);
if (ret < 0) {
dev_err(dev, "could not set codec driver audio format (ret=%i)\n",
ret);
return ret;
}
- ret = snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, mdata->clk_frequency,
+ ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), 0, 0, mdata->clk_frequency,
mdata->clk_frequency);
if (ret < 0) {
dev_err(dev, "could not set codec PLL frequency (ret=%i)\n",
@@ -157,10 +159,13 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream)
}
/**
- * p1022_rdk_machine_remove: Remove the sound device
+ * p1022_rdk_machine_remove - Remove the sound device
+ * @card: ASoC card instance
*
* This function is called to remove the sound device for one SSI. We
* de-program the DMACR and PMUXCR register.
+ *
+ * Returns: %0 on success or negative errno value on error
*/
static int p1022_rdk_machine_remove(struct snd_soc_card *card)
{
@@ -185,19 +190,22 @@ static int p1022_rdk_machine_remove(struct snd_soc_card *card)
return 0;
}
-/**
+/*
* p1022_rdk_ops: ASoC machine driver operations
*/
-static struct snd_soc_ops p1022_rdk_ops = {
+static const struct snd_soc_ops p1022_rdk_ops = {
.startup = p1022_rdk_startup,
};
/**
- * p1022_rdk_probe: platform probe function for the machine driver
+ * p1022_rdk_probe - platform probe function for the machine driver
+ * @pdev: platform device pointer
*
* Although this is a machine driver, the SSI node is the "master" node with
* respect to audio hardware connections. Therefore, we create a new ASoC
* device for each new SSI node that has a codec attached.
+ *
+ * Returns: %0 on success or negative errno value on error
*/
static int p1022_rdk_probe(struct platform_device *pdev)
{
@@ -207,6 +215,7 @@ static int p1022_rdk_probe(struct platform_device *pdev)
struct device_node *np = ssi_pdev->dev.of_node;
struct device_node *codec_np = NULL;
struct machine_data *mdata;
+ struct snd_soc_dai_link_component *comp;
const u32 *iprop;
int ret;
@@ -223,11 +232,33 @@ static int p1022_rdk_probe(struct platform_device *pdev)
goto error_put;
}
- mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
+ comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL);
+ if (!comp) {
+ ret = -ENOMEM;
+ goto error_put;
+ }
+
+ mdata->dai[0].cpus = &comp[0];
+ mdata->dai[0].codecs = &comp[1];
+ mdata->dai[0].platforms = &comp[2];
+
+ mdata->dai[0].num_cpus = 1;
+ mdata->dai[0].num_codecs = 1;
+ mdata->dai[0].num_platforms = 1;
+
+ mdata->dai[1].cpus = &comp[3];
+ mdata->dai[1].codecs = &comp[4];
+ mdata->dai[1].platforms = &comp[5];
+
+ mdata->dai[1].num_cpus = 1;
+ mdata->dai[1].num_codecs = 1;
+ mdata->dai[1].num_platforms = 1;
+
+ mdata->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev);
mdata->dai[0].ops = &p1022_rdk_ops;
/* ASoC core can match codec with device node */
- mdata->dai[0].codec_of_node = codec_np;
+ mdata->dai[0].codecs->of_node = codec_np;
/*
* We register two DAIs per SSI, one for playback and the other for
@@ -237,8 +268,8 @@ static int p1022_rdk_probe(struct platform_device *pdev)
memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link));
/* The DAI names from the codec (snd_soc_dai_driver.name) */
- mdata->dai[0].codec_dai_name = "wm8960-hifi";
- mdata->dai[1].codec_dai_name = mdata->dai[0].codec_dai_name;
+ mdata->dai[0].codecs->dai_name = "wm8960-hifi";
+ mdata->dai[1].codecs->dai_name = mdata->dai[0].codecs->dai_name;
/*
* Configure the SSI for I2S slave mode. Older device trees have
@@ -246,7 +277,7 @@ static int p1022_rdk_probe(struct platform_device *pdev)
* only one way to configure the SSI.
*/
mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
@@ -270,7 +301,7 @@ static int p1022_rdk_probe(struct platform_device *pdev)
}
/* Find the playback DMA channel to use. */
- mdata->dai[0].platform_name = mdata->platform_name[0];
+ mdata->dai[0].platforms->name = mdata->platform_name[0];
ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
&mdata->dma_channel_id[0],
&mdata->dma_id[0]);
@@ -281,7 +312,7 @@ static int p1022_rdk_probe(struct platform_device *pdev)
}
/* Find the capture DMA channel to use. */
- mdata->dai[1].platform_name = mdata->platform_name[1];
+ mdata->dai[1].platforms->name = mdata->platform_name[1];
ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
&mdata->dma_channel_id[1],
&mdata->dma_id[1]);
@@ -322,11 +353,12 @@ error_put:
}
/**
- * p1022_rdk_remove: remove the platform device
+ * p1022_rdk_remove - remove the platform device
+ * @pdev: platform device pointer
*
* This function is called when the platform device is removed.
*/
-static int p1022_rdk_remove(struct platform_device *pdev)
+static void p1022_rdk_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct machine_data *mdata =
@@ -334,8 +366,6 @@ static int p1022_rdk_remove(struct platform_device *pdev)
snd_soc_unregister_card(card);
kfree(mdata);
-
- return 0;
}
static struct platform_driver p1022_rdk_driver = {
@@ -351,9 +381,11 @@ static struct platform_driver p1022_rdk_driver = {
};
/**
- * p1022_rdk_init: machine driver initialization.
+ * p1022_rdk_init - machine driver initialization.
*
* This function is called when this module is loaded.
+ *
+ * Returns: %0 on success or negative errno value on error
*/
static int __init p1022_rdk_init(void)
{
@@ -374,7 +406,7 @@ static int __init p1022_rdk_init(void)
}
/**
- * p1022_rdk_exit: machine driver exit
+ * p1022_rdk_exit - machine driver exit
*
* This function is called when this driver is unloaded.
*/
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
index ec731223cab3..5542c4ee6d12 100644
--- a/sound/soc/fsl/pcm030-audio-fabric.c
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -1,20 +1,15 @@
-/*
- * Phytec pcm030 driver for the PSC of the Freescale MPC52xx
- * configured as AC97 interface
- *
- * Copyright 2008 Jon Smirl, Digispeaker
- * Author: Jon Smirl <jonsmirl@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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Phytec pcm030 driver for the PSC of the Freescale MPC52xx
+// configured as AC97 interface
+//
+// Copyright 2008 Jon Smirl, Digispeaker
+// Author: Jon Smirl <jonsmirl@gmail.com>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <sound/soc.h>
@@ -27,20 +22,26 @@ struct pcm030_audio_data {
struct platform_device *codec_device;
};
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_CPU("mpc5200-psc-ac97.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(iec958,
+ DAILINK_COMP_ARRAY(COMP_CPU("mpc5200-psc-ac97.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
static struct snd_soc_dai_link pcm030_fabric_dai[] = {
{
.name = "AC97.0",
.stream_name = "AC97 Analog",
- .codec_dai_name = "wm9712-hifi",
- .cpu_dai_name = "mpc5200-psc-ac97.0",
- .codec_name = "wm9712-codec",
+ SND_SOC_DAILINK_REG(analog),
},
{
.name = "AC97.1",
.stream_name = "AC97 IEC958",
- .codec_dai_name = "wm9712-aux",
- .cpu_dai_name = "mpc5200-psc-ac97.1",
- .codec_name = "wm9712-codec",
+ SND_SOC_DAILINK_REG(iec958),
},
};
@@ -57,6 +58,7 @@ static int pcm030_fabric_probe(struct platform_device *op)
struct device_node *platform_np;
struct snd_soc_card *card = &pcm030_card;
struct pcm030_audio_data *pdata;
+ struct snd_soc_dai_link *dai_link;
int ret;
int i;
@@ -78,8 +80,8 @@ static int pcm030_fabric_probe(struct platform_device *op)
return -ENODEV;
}
- for (i = 0; i < card->num_links; i++)
- card->dai_link[i].platform_of_node = platform_np;
+ for_each_card_prelinks(card, i, dai_link)
+ dai_link->platforms->of_node = platform_np;
ret = request_module("snd-soc-wm9712");
if (ret)
@@ -90,27 +92,28 @@ static int pcm030_fabric_probe(struct platform_device *op)
dev_err(&op->dev, "platform_device_alloc() failed\n");
ret = platform_device_add(pdata->codec_device);
- if (ret)
+ if (ret) {
dev_err(&op->dev, "platform_device_add() failed: %d\n", ret);
+ platform_device_put(pdata->codec_device);
+ }
ret = snd_soc_register_card(card);
- if (ret)
+ if (ret) {
dev_err(&op->dev, "snd_soc_register_card() failed: %d\n", ret);
+ platform_device_unregister(pdata->codec_device);
+ }
platform_set_drvdata(op, pdata);
-
return ret;
+
}
-static int pcm030_fabric_remove(struct platform_device *op)
+static void pcm030_fabric_remove(struct platform_device *op)
{
struct pcm030_audio_data *pdata = platform_get_drvdata(op);
- int ret;
- ret = snd_soc_unregister_card(pdata->card);
+ snd_soc_unregister_card(pdata->card);
platform_device_unregister(pdata->codec_device);
-
- return ret;
}
static const struct of_device_id pcm030_audio_match[] = {
diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
deleted file mode 100644
index ae403c29688f..000000000000
--- a/sound/soc/fsl/phycore-ac97.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode
- *
- * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <asm/mach-types.h>
-
-#include "imx-audmux.h"
-
-static struct snd_soc_card imx_phycore;
-
-static struct snd_soc_ops imx_phycore_hifi_ops = {
-};
-
-static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
- {
- .name = "HiFi",
- .stream_name = "HiFi",
- .codec_dai_name = "wm9712-hifi",
- .codec_name = "wm9712-codec",
- .cpu_dai_name = "imx-ssi.0",
- .platform_name = "imx-ssi.0",
- .ops = &imx_phycore_hifi_ops,
- },
-};
-
-static struct snd_soc_card imx_phycore = {
- .name = "PhyCORE-ac97-audio",
- .owner = THIS_MODULE,
- .dai_link = imx_phycore_dai_ac97,
- .num_links = ARRAY_SIZE(imx_phycore_dai_ac97),
-};
-
-static struct platform_device *imx_phycore_snd_ac97_device;
-static struct platform_device *imx_phycore_snd_device;
-
-static int __init imx_phycore_init(void)
-{
- int ret;
-
- if (machine_is_pca100()) {
- imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
- IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */
- IMX_AUDMUX_V1_PCR_TFCSEL(3) |
- IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */
- IMX_AUDMUX_V1_PCR_RXDSEL(3));
- imx_audmux_v1_configure_port(3,
- IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */
- IMX_AUDMUX_V1_PCR_TFCSEL(0) |
- IMX_AUDMUX_V1_PCR_TFSDIR |
- IMX_AUDMUX_V1_PCR_RXDSEL(0));
- } else if (machine_is_pcm043()) {
- imx_audmux_v2_configure_port(3,
- IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */
- IMX_AUDMUX_V2_PTCR_TFSEL(0) |
- IMX_AUDMUX_V2_PTCR_TFSDIR,
- IMX_AUDMUX_V2_PDCR_RXDSEL(0));
- imx_audmux_v2_configure_port(0,
- IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */
- IMX_AUDMUX_V2_PTCR_TCSEL(3) |
- IMX_AUDMUX_V2_PTCR_TCLKDIR, /* clock is output */
- IMX_AUDMUX_V2_PDCR_RXDSEL(3));
- } else {
- /* return happy. We might run on a totally different machine */
- return 0;
- }
-
- imx_phycore_snd_ac97_device = platform_device_alloc("soc-audio", -1);
- if (!imx_phycore_snd_ac97_device)
- return -ENOMEM;
-
- platform_set_drvdata(imx_phycore_snd_ac97_device, &imx_phycore);
- ret = platform_device_add(imx_phycore_snd_ac97_device);
- if (ret)
- goto fail1;
-
- imx_phycore_snd_device = platform_device_alloc("wm9712-codec", -1);
- if (!imx_phycore_snd_device) {
- ret = -ENOMEM;
- goto fail2;
- }
- ret = platform_device_add(imx_phycore_snd_device);
-
- if (ret) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- goto fail3;
- }
-
- return 0;
-
-fail3:
- platform_device_put(imx_phycore_snd_device);
-fail2:
- platform_device_del(imx_phycore_snd_ac97_device);
-fail1:
- platform_device_put(imx_phycore_snd_ac97_device);
- return ret;
-}
-
-static void __exit imx_phycore_exit(void)
-{
- platform_device_unregister(imx_phycore_snd_device);
- platform_device_unregister(imx_phycore_snd_ac97_device);
-}
-
-late_initcall(imx_phycore_init);
-module_exit(imx_phycore_exit);
-
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
deleted file mode 100644
index b454972dce35..000000000000
--- a/sound/soc/fsl/wm1133-ev1.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS
- *
- * Copyright (c) 2010 Wolfson Microelectronics plc
- * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * Based on an earlier driver for the same hardware 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/platform_device.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "imx-ssi.h"
-#include "../codecs/wm8350.h"
-#include "imx-audmux.h"
-
-/* There is a silicon mic on the board optionally connected via a solder pad
- * SP1. Define this to enable it.
- */
-#undef USE_SIMIC
-
-struct _wm8350_audio {
- unsigned int channels;
- snd_pcm_format_t format;
- unsigned int rate;
- unsigned int sysclk;
- unsigned int bclkdiv;
- unsigned int clkdiv;
- unsigned int lr_rate;
-};
-
-/* in order of power consumption per rate (lowest first) */
-static const struct _wm8350_audio wm8350_audio[] = {
- /* 16bit mono modes */
- {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1,
- WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,},
-
- /* 16 bit stereo modes */
- {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
- WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
- WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
- WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
- WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
- WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
- WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
- WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
- WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
- {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
- WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
-
- /* 24bit stereo modes */
- {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000,
- WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
- {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000,
- WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
- {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600,
- WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
- {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200,
- WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
-};
-
-static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int i, found = 0;
- snd_pcm_format_t format = params_format(params);
- unsigned int rate = params_rate(params);
- unsigned int channels = params_channels(params);
-
- /* find the correct audio parameters */
- for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) {
- if (rate == wm8350_audio[i].rate &&
- format == wm8350_audio[i].format &&
- channels == wm8350_audio[i].channels) {
- found = 1;
- break;
- }
- }
- if (!found)
- return -EINVAL;
-
- /* codec FLL input is 14.75 MHz from MCLK */
- snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk);
-
- /* TODO: The SSI driver should figure this out for us */
- switch (channels) {
- case 2:
- snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 0);
- break;
- case 1:
- snd_soc_dai_set_tdm_slot(cpu_dai, 0x1, 0x1, 1, 0);
- break;
- default:
- return -EINVAL;
- }
-
- /* set MCLK as the codec system clock for DAC and ADC */
- snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK,
- wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
-
- /* set codec BCLK division for sample rate */
- snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV,
- wm8350_audio[i].bclkdiv);
-
- /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */
- snd_soc_dai_set_clkdiv(codec_dai,
- WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate);
- snd_soc_dai_set_clkdiv(codec_dai,
- WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate);
-
- /* now configure DAC and ADC clocks */
- snd_soc_dai_set_clkdiv(codec_dai,
- WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv);
-
- snd_soc_dai_set_clkdiv(codec_dai,
- WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv);
-
- return 0;
-}
-
-static struct snd_soc_ops wm1133_ev1_ops = {
- .hw_params = wm1133_ev1_hw_params,
-};
-
-static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = {
-#ifdef USE_SIMIC
- SND_SOC_DAPM_MIC("SiMIC", NULL),
-#endif
- SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
- SND_SOC_DAPM_MIC("Mic2 Jack", NULL),
- SND_SOC_DAPM_LINE("Line In Jack", NULL),
- SND_SOC_DAPM_LINE("Line Out Jack", NULL),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
-};
-
-/* imx32ads soc_card audio map */
-static const struct snd_soc_dapm_route wm1133_ev1_map[] = {
-
-#ifdef USE_SIMIC
- /* SiMIC --> IN1LN (with automatic bias) via SP1 */
- { "IN1LN", NULL, "Mic Bias" },
- { "Mic Bias", NULL, "SiMIC" },
-#endif
-
- /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */
- { "IN1LN", NULL, "Mic Bias" },
- { "IN1LP", NULL, "Mic1 Jack" },
- { "Mic Bias", NULL, "Mic1 Jack" },
-
- /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */
- { "IN1RN", NULL, "Mic Bias" },
- { "IN1RP", NULL, "Mic2 Jack" },
- { "Mic Bias", NULL, "Mic2 Jack" },
-
- /* Line in Jack --> AUX (L+R) */
- { "IN3R", NULL, "Line In Jack" },
- { "IN3L", NULL, "Line In Jack" },
-
- /* Out1 --> Headphone Jack */
- { "Headphone Jack", NULL, "OUT1R" },
- { "Headphone Jack", NULL, "OUT1L" },
-
- /* Out1 --> Line Out Jack */
- { "Line Out Jack", NULL, "OUT2R" },
- { "Line Out Jack", NULL, "OUT2L" },
-};
-
-static struct snd_soc_jack hp_jack;
-
-static struct snd_soc_jack_pin hp_jack_pins[] = {
- { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE },
-};
-
-static struct snd_soc_jack mic_jack;
-
-static struct snd_soc_jack_pin mic_jack_pins[] = {
- { .pin = "Mic1 Jack", .mask = SND_JACK_MICROPHONE },
- { .pin = "Mic2 Jack", .mask = SND_JACK_MICROPHONE },
-};
-
-static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
-
- /* Headphone jack detection */
- snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE,
- &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
- wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
-
- /* Microphone jack detection */
- snd_soc_card_jack_new(rtd->card, "Microphone",
- SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack,
- mic_jack_pins, ARRAY_SIZE(mic_jack_pins));
- wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE,
- SND_JACK_BTN_0);
-
- snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias");
-
- return 0;
-}
-
-
-static struct snd_soc_dai_link wm1133_ev1_dai = {
- .name = "WM1133-EV1",
- .stream_name = "Audio",
- .cpu_dai_name = "imx-ssi.0",
- .codec_dai_name = "wm8350-hifi",
- .platform_name = "imx-ssi.0",
- .codec_name = "wm8350-codec.0-0x1a",
- .init = wm1133_ev1_init,
- .ops = &wm1133_ev1_ops,
- .symmetric_rates = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card wm1133_ev1 = {
- .name = "WM1133-EV1",
- .owner = THIS_MODULE,
- .dai_link = &wm1133_ev1_dai,
- .num_links = 1,
-
- .dapm_widgets = wm1133_ev1_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm1133_ev1_widgets),
- .dapm_routes = wm1133_ev1_map,
- .num_dapm_routes = ARRAY_SIZE(wm1133_ev1_map),
-};
-
-static struct platform_device *wm1133_ev1_snd_device;
-
-static int __init wm1133_ev1_audio_init(void)
-{
- int ret;
- unsigned int ptcr, pdcr;
-
- /* SSI0 mastered by port 5 */
- ptcr = IMX_AUDMUX_V2_PTCR_SYN |
- IMX_AUDMUX_V2_PTCR_TFSDIR |
- IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) |
- IMX_AUDMUX_V2_PTCR_TCLKDIR |
- IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
- pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
- imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr);
-
- ptcr = IMX_AUDMUX_V2_PTCR_SYN;
- pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0);
- imx_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr);
-
- wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1);
- if (!wm1133_ev1_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1);
- ret = platform_device_add(wm1133_ev1_snd_device);
-
- if (ret)
- platform_device_put(wm1133_ev1_snd_device);
-
- return ret;
-}
-module_init(wm1133_ev1_audio_init);
-
-static void __exit wm1133_ev1_audio_exit(void)
-{
- platform_device_unregister(wm1133_ev1_snd_device);
-}
-module_exit(wm1133_ev1_audio_exit);
-
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index 610f61251640..64b0817e2955 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -1,4 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Generic drivers"
+
+config SND_SIMPLE_CARD_UTILS
+ tristate
+
config SND_SIMPLE_CARD
tristate "ASoC Simple sound card support"
+ select SND_SIMPLE_CARD_UTILS
help
This option enables generic simple sound card support
+ It also support DPCM of multi CPU single Codec ststem.
+
+config SND_AUDIO_GRAPH_CARD
+ tristate "ASoC Audio Graph sound card support"
+ depends on OF
+ select SND_SIMPLE_CARD_UTILS
+ help
+ This option enables generic simple sound card support
+ with OF-graph DT bindings.
+ It also support DPCM of multi CPU single Codec ststem.
+
+config SND_AUDIO_GRAPH_CARD2
+ tristate "ASoC Audio Graph sound card2 support"
+ depends on OF
+ select SND_SIMPLE_CARD_UTILS
+ help
+ This option enables generic simple sound card2 support
+ with OF-graph DT bindings.
+
+config SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
+ tristate "ASoC Audio Graph Card2 base custom sample support"
+ depends on SND_AUDIO_GRAPH_CARD2
+ help
+ This option enables Audio Graph Card2 base custom sample
+
+config SND_TEST_COMPONENT
+ tristate "ASoC Test component sound support"
+ depends on OF
+ help
+ This option enables test component sound driver support.
+
+endmenu
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index 9c3b246792bf..d5abb3eed3df 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -1,3 +1,14 @@
-snd-soc-simple-card-objs := simple-card.o
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-simple-card-utils-y := simple-card-utils.o
+snd-soc-simple-card-y := simple-card.o
+snd-soc-audio-graph-card-y := audio-graph-card.o
+snd-soc-audio-graph-card2-y := audio-graph-card2.o
+snd-soc-audio-graph-card2-custom-sample-y := audio-graph-card2-custom-sample.o
+snd-soc-test-component-y := test-component.o
-obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
+obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
+obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
+obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
+obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2) += snd-soc-audio-graph-card2.o
+obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE) += snd-soc-audio-graph-card2-custom-sample.o
+obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
new file mode 100644
index 000000000000..7720cf1fd6e1
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ASoC audio graph sound card support
+//
+// Copyright (C) 2016 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// based on ${LINUX}/sound/soc/generic/simple-card.c
+
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/graph_card.h>
+
+#define DPCM_SELECTABLE 1
+
+#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret)
+static inline int _graph_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
+#define ep_to_port(ep) of_get_parent(ep)
+static struct device_node *port_to_ports(struct device_node *port)
+{
+ struct device_node *ports = of_get_parent(port);
+
+ if (!of_node_name_eq(ports, "ports")) {
+ of_node_put(ports);
+ return NULL;
+ }
+ return ports;
+}
+
+static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ gpiod_set_value_cansleep(priv->pa_gpio, 1);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ gpiod_set_value_cansleep(priv->pa_gpio, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
+ SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
+ 0, 0, NULL, 0, graph_outdrv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_ops graph_ops = {
+ .startup = simple_util_startup,
+ .shutdown = simple_util_shutdown,
+ .hw_params = simple_util_hw_params,
+};
+
+static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
+{
+ struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
+
+ if (dai && (dai->component->driver->pcm_construct ||
+ (dai->driver->ops && dai->driver->ops->pcm_new)))
+ return true;
+
+ return false;
+}
+
+static void graph_parse_convert(struct device *dev,
+ struct device_node *ep,
+ struct simple_util_data *adata)
+{
+ struct device_node *top = dev->of_node;
+ struct device_node *port __free(device_node) = ep_to_port(ep);
+ struct device_node *ports __free(device_node) = port_to_ports(port);
+ struct device_node *node __free(device_node) = of_graph_get_port_parent(ep);
+
+ simple_util_parse_convert(top, NULL, adata);
+ simple_util_parse_convert(ports, NULL, adata);
+ simple_util_parse_convert(port, NULL, adata);
+ simple_util_parse_convert(ep, NULL, adata);
+}
+
+static int graph_parse_node(struct simple_util_priv *priv,
+ struct device_node *ep,
+ struct link_info *li,
+ int *cpu)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct snd_soc_dai_link_component *dlc;
+ struct simple_util_dai *dai;
+ int ret;
+
+ if (cpu) {
+ dlc = snd_soc_link_to_cpu(dai_link, 0);
+ dai = simple_props_to_dai_cpu(dai_props, 0);
+ } else {
+ dlc = snd_soc_link_to_codec(dai_link, 0);
+ dai = simple_props_to_dai_codec(dai_props, 0);
+ }
+
+ ret = graph_util_parse_dai(priv, ep, dlc, cpu);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_tdm(ep, dai);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_clk(dev, ep, dai, dlc);
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_link_init(struct simple_util_priv *priv,
+ struct device_node *ep_cpu,
+ struct device_node *ep_codec,
+ struct link_info *li,
+ char *name)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *top = dev->of_node;
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct device_node *port_cpu __free(device_node) = ep_to_port(ep_cpu);
+ struct device_node *port_codec __free(device_node) = ep_to_port(ep_codec);
+ struct device_node *ports_cpu __free(device_node) = port_to_ports(port_cpu);
+ struct device_node *ports_codec __free(device_node) = port_to_ports(port_codec);
+ enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;
+ enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT;
+ bool playback_only = 0, capture_only = 0;
+ int ret;
+
+ ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec,
+ NULL, &dai_link->dai_fmt);
+ if (ret < 0)
+ goto end;
+
+ graph_util_parse_link_direction(top, &playback_only, &capture_only);
+ graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only);
+ graph_util_parse_link_direction(port_codec, &playback_only, &capture_only);
+ graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only);
+ graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only);
+
+ of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs);
+
+ graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop);
+
+ dai_link->playback_only = playback_only;
+ dai_link->capture_only = capture_only;
+
+ dai_link->trigger_start = trigger_start;
+ dai_link->trigger_stop = trigger_stop;
+
+ dai_link->init = simple_util_dai_init;
+ dai_link->ops = &graph_ops;
+ if (priv->ops)
+ dai_link->ops = priv->ops;
+
+ ret = simple_util_set_dailink_name(priv, dai_link, name);
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct device_node *top = dev->of_node;
+ struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
+ char dai_name[64];
+ int ret;
+
+ dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
+
+ if (li->cpu) {
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
+ struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
+ int is_single_links = 0;
+
+ /* Codec is dummy */
+
+ /* FE settings */
+ dai_link->dynamic = 1;
+ dai_link->dpcm_merged_format = 1;
+
+ ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
+ if (ret)
+ goto end;
+
+ snprintf(dai_name, sizeof(dai_name),
+ "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
+ /*
+ * In BE<->BE connections it is not required to create
+ * PCM devices at CPU end of the dai link and thus 'no_pcm'
+ * flag needs to be set. It is useful when there are many
+ * BE components and some of these have to be connected to
+ * form a valid audio path.
+ *
+ * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
+ * there are 'n' BE components in the path.
+ */
+ if (card->component_chaining && !soc_component_is_pcm(cpus)) {
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
+ }
+
+ simple_util_canonicalize_cpu(cpus, is_single_links);
+ simple_util_canonicalize_platform(platforms, cpus);
+ } else {
+ struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
+ struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
+
+ /* CPU is dummy */
+
+ /* BE settings */
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
+
+ ret = graph_parse_node(priv, codec_ep, li, NULL);
+ if (ret < 0)
+ goto end;
+
+ snprintf(dai_name, sizeof(dai_name),
+ "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
+
+ /* check "prefix" from top node */
+ struct device_node *port __free(device_node) = ep_to_port(ep);
+ struct device_node *ports __free(device_node) = port_to_ports(port);
+
+ snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix");
+ snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
+ snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, "prefix");
+ }
+
+ graph_parse_convert(dev, ep, &dai_props->adata);
+
+ ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
+
+ li->link++;
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_dai_link_of(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
+ struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
+ struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
+ char dai_name[64];
+ int ret, is_single_links = 0;
+
+ dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
+
+ ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
+ if (ret < 0)
+ goto end;
+
+ ret = graph_parse_node(priv, codec_ep, li, NULL);
+ if (ret < 0)
+ goto end;
+
+ snprintf(dai_name, sizeof(dai_name),
+ "%s-%s", cpus->dai_name, codecs->dai_name);
+
+ simple_util_canonicalize_cpu(cpus, is_single_links);
+ simple_util_canonicalize_platform(platforms, cpus);
+
+ ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
+ if (ret < 0)
+ goto end;
+
+ li->link++;
+end:
+ return graph_ret(priv, ret);
+}
+
+static inline bool parse_as_dpcm_link(struct simple_util_priv *priv,
+ struct device_node *codec_port,
+ struct simple_util_data *adata)
+{
+ if (priv->force_dpcm)
+ return true;
+
+ if (!priv->dpcm_selectable)
+ return false;
+
+ /*
+ * It is DPCM
+ * if Codec port has many endpoints,
+ * or has convert-xxx property
+ */
+ if ((of_get_child_count(codec_port) > 1) ||
+ simple_util_is_convert_required(adata))
+ return true;
+
+ return false;
+}
+
+static int __graph_for_each_link(struct simple_util_priv *priv,
+ struct link_info *li,
+ int (*func_noml)(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li),
+ int (*func_dpcm)(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li))
+{
+ struct of_phandle_iterator it;
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *node = dev->of_node;
+ struct device_node *cpu_port;
+ struct device_node *codec_port_old = NULL;
+ struct simple_util_data adata;
+ int rc, ret = 0;
+
+ /* loop for all listed CPU port */
+ of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
+ cpu_port = it.node;
+
+ /* loop for all CPU endpoint */
+ for_each_of_graph_port_endpoint(cpu_port, cpu_ep) {
+ /* get codec */
+ struct device_node *codec_ep __free(device_node) = of_graph_get_remote_endpoint(cpu_ep);
+ struct device_node *codec_port __free(device_node) = ep_to_port(codec_ep);
+
+ /* get convert-xxx property */
+ memset(&adata, 0, sizeof(adata));
+ graph_parse_convert(dev, codec_ep, &adata);
+ graph_parse_convert(dev, cpu_ep, &adata);
+
+ /* check if link requires DPCM parsing */
+ if (parse_as_dpcm_link(priv, codec_port, &adata)) {
+ /*
+ * Codec endpoint can be NULL for pluggable audio HW.
+ * Platform DT can populate the Codec endpoint depending on the
+ * plugged HW.
+ */
+ /* Do it all CPU endpoint, and 1st Codec endpoint */
+ if (li->cpu ||
+ ((codec_port_old != codec_port) && codec_ep))
+ ret = func_dpcm(priv, cpu_ep, codec_ep, li);
+ /* else normal sound */
+ } else {
+ if (li->cpu)
+ ret = func_noml(priv, cpu_ep, codec_ep, li);
+ }
+
+ if (ret < 0)
+ goto end;
+
+ codec_port_old = codec_port;
+ }
+ }
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_for_each_link(struct simple_util_priv *priv,
+ struct link_info *li,
+ int (*func_noml)(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li),
+ int (*func_dpcm)(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li))
+{
+ int ret;
+ /*
+ * Detect all CPU first, and Detect all Codec 2nd.
+ *
+ * In Normal sound case, all DAIs are detected
+ * as "CPU-Codec".
+ *
+ * In DPCM sound case,
+ * all CPUs are detected as "CPU-dummy", and
+ * all Codecs are detected as "dummy-Codec".
+ * To avoid random sub-device numbering,
+ * detect "dummy-Codec" in last;
+ */
+ for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
+ ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
+ if (ret < 0)
+ break;
+ }
+
+ return graph_ret(priv, ret);
+}
+
+static int graph_count_noml(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
+
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
+
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
+ li->num[li->link].cpus = 1;
+ li->num[li->link].platforms = 1;
+
+ li->num[li->link].codecs = 1;
+
+ li->link += 1; /* 1xCPU-Codec */
+
+ dev_dbg(dev, "Count As Normal\n");
+ ret = 0;
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_count_dpcm(struct simple_util_priv *priv,
+ struct device_node *cpu_ep,
+ struct device_node *codec_ep,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
+
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
+
+ if (li->cpu) {
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
+ li->num[li->link].cpus = 1;
+ li->num[li->link].platforms = 1;
+
+ li->link++; /* 1xCPU-dummy */
+ } else {
+ li->num[li->link].codecs = 1;
+
+ li->link++; /* 1xdummy-Codec */
+ }
+
+ dev_dbg(dev, "Count As DPCM\n");
+ ret = 0;
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_get_dais_count(struct simple_util_priv *priv,
+ struct link_info *li)
+{
+ /*
+ * link_num : number of links.
+ * CPU-Codec / CPU-dummy / dummy-Codec
+ * dais_num : number of DAIs
+ * ccnf_num : number of codec_conf
+ * same number for "dummy-Codec"
+ *
+ * ex1)
+ * CPU0 --- Codec0 link : 5
+ * CPU1 --- Codec1 dais : 7
+ * CPU2 -/ ccnf : 1
+ * CPU3 --- Codec2
+ *
+ * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
+ * => 7 DAIs = 4xCPU + 3xCodec
+ * => 1 ccnf = 1xdummy-Codec
+ *
+ * ex2)
+ * CPU0 --- Codec0 link : 5
+ * CPU1 --- Codec1 dais : 6
+ * CPU2 -/ ccnf : 1
+ * CPU3 -/
+ *
+ * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
+ * => 6 DAIs = 4xCPU + 2xCodec
+ * => 1 ccnf = 1xdummy-Codec
+ *
+ * ex3)
+ * CPU0 --- Codec0 link : 6
+ * CPU1 -/ dais : 6
+ * CPU2 --- Codec1 ccnf : 2
+ * CPU3 -/
+ *
+ * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
+ * => 6 DAIs = 4xCPU + 2xCodec
+ * => 2 ccnf = 2xdummy-Codec
+ *
+ * ex4)
+ * CPU0 --- Codec0 (convert-rate) link : 3
+ * CPU1 --- Codec1 dais : 4
+ * ccnf : 1
+ *
+ * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
+ * => 4 DAIs = 2xCPU + 2xCodec
+ * => 1 ccnf = 1xdummy-Codec
+ */
+ return graph_for_each_link(priv, li,
+ graph_count_noml,
+ graph_count_dpcm);
+}
+
+int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ int ret = -ENOMEM;
+
+ struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
+ if (!li)
+ goto end;
+
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+
+ ret = graph_get_dais_count(priv, li);
+ if (ret < 0)
+ goto end;
+
+ ret = -EINVAL;
+ if (!li->link)
+ goto end;
+
+ ret = simple_util_init_priv(priv, li);
+ if (ret < 0)
+ goto end;
+
+ priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->pa_gpio)) {
+ ret = PTR_ERR(priv->pa_gpio);
+ dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
+ goto end;
+ }
+
+ ret = simple_util_parse_widgets(card, NULL);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_routing(card, NULL);
+ if (ret < 0)
+ goto end;
+
+ memset(li, 0, sizeof(*li));
+ ret = graph_for_each_link(priv, li,
+ graph_dai_link_of,
+ graph_dai_link_of_dpcm);
+ if (ret < 0)
+ goto err;
+
+ ret = simple_util_parse_card_name(priv, NULL);
+ if (ret < 0)
+ goto err;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ simple_util_debug_info(priv);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ simple_util_clean_reference(card);
+end:
+ return dev_err_probe(dev, ret, "parse error\n");
+}
+EXPORT_SYMBOL_GPL(audio_graph_parse_of);
+
+static int graph_probe(struct platform_device *pdev)
+{
+ struct simple_util_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+
+ /* Allocate the private data and the DAI link array */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ card = simple_priv_to_card(priv);
+ card->dapm_widgets = graph_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets);
+ card->probe = graph_util_card_probe;
+
+ if (of_device_get_match_data(dev))
+ priv->dpcm_selectable = 1;
+
+ return audio_graph_parse_of(priv, dev);
+}
+
+static const struct of_device_id graph_of_match[] = {
+ { .compatible = "audio-graph-card", },
+ { .compatible = "audio-graph-scu-card",
+ .data = (void *)DPCM_SELECTABLE },
+ {},
+};
+MODULE_DEVICE_TABLE(of, graph_of_match);
+
+static struct platform_driver graph_card = {
+ .driver = {
+ .name = "asoc-audio-graph-card",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = graph_of_match,
+ },
+ .probe = graph_probe,
+ .remove = simple_util_remove,
+};
+module_platform_driver(graph_card);
+
+MODULE_ALIAS("platform:asoc-audio-graph-card");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.c b/sound/soc/generic/audio-graph-card2-custom-sample.c
new file mode 100644
index 000000000000..7151d426bee9
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2-custom-sample.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// audio-graph-card2-custom-sample.c
+//
+// Copyright (C) 2020 Renesas Electronics Corp.
+// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/graph_card.h>
+
+/*
+ * Custom driver can have own priv
+ * which includes simple_util_priv.
+ */
+struct custom_priv {
+ struct simple_util_priv simple_priv;
+
+ /* custom driver's own params */
+ int custom_params;
+};
+
+/* You can get custom_priv from simple_priv */
+#define simple_to_custom(simple) container_of((simple), struct custom_priv, simple_priv)
+
+static int custom_card_probe(struct snd_soc_card *card)
+{
+ struct simple_util_priv *simple_priv = snd_soc_card_get_drvdata(card);
+ struct custom_priv *custom_priv = simple_to_custom(simple_priv);
+ struct device *dev = simple_priv_to_dev(simple_priv);
+
+ dev_info(dev, "custom probe\n");
+
+ custom_priv->custom_params = 1;
+
+ /* you can use generic probe function */
+ return graph_util_card_probe(card);
+}
+
+static int custom_hook_pre(struct simple_util_priv *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /* You can custom before parsing */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return 0;
+}
+
+static int custom_hook_post(struct simple_util_priv *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_card *card;
+
+ /* You can custom after parsing */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ /* overwrite .probe sample */
+ card = simple_priv_to_card(priv);
+ card->probe = custom_card_probe;
+
+ return 0;
+}
+
+static int custom_normal(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /*
+ * You can custom Normal parsing
+ * before/affter audio_graph2_link_normal()
+ */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return audio_graph2_link_normal(priv, lnk, li);
+}
+
+static int custom_dpcm(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /*
+ * You can custom DPCM parsing
+ * before/affter audio_graph2_link_dpcm()
+ */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return audio_graph2_link_dpcm(priv, lnk, li);
+}
+
+static int custom_c2c(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ /*
+ * You can custom Codec2Codec parsing
+ * before/affter audio_graph2_link_c2c()
+ */
+ dev_info(dev, "hook : %s\n", __func__);
+
+ return audio_graph2_link_c2c(priv, lnk, li);
+}
+
+/*
+ * audio-graph-card2 has many hooks for your customizing.
+ */
+static struct graph2_custom_hooks custom_hooks = {
+ .hook_pre = custom_hook_pre,
+ .hook_post = custom_hook_post,
+ .custom_normal = custom_normal,
+ .custom_dpcm = custom_dpcm,
+ .custom_c2c = custom_c2c,
+};
+
+static int custom_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct device *dev = simple_priv_to_dev(priv);
+
+ dev_info(dev, "custom startup\n");
+
+ return simple_util_startup(substream);
+}
+
+/* You can use custom ops */
+static const struct snd_soc_ops custom_ops = {
+ .startup = custom_startup,
+ .shutdown = simple_util_shutdown,
+ .hw_params = simple_util_hw_params,
+};
+
+static int custom_probe(struct platform_device *pdev)
+{
+ struct custom_priv *custom_priv;
+ struct simple_util_priv *simple_priv;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ custom_priv = devm_kzalloc(dev, sizeof(*custom_priv), GFP_KERNEL);
+ if (!custom_priv)
+ return -ENOMEM;
+
+ simple_priv = &custom_priv->simple_priv;
+ simple_priv->ops = &custom_ops; /* customize dai_link ops */
+
+ /* "audio-graph-card2-custom-sample" is too long */
+ simple_priv->snd_card.name = "card2-custom";
+
+ /* use audio-graph-card2 parsing with own custom hooks */
+ ret = audio_graph2_parse_of(simple_priv, dev, &custom_hooks);
+ if (ret < 0)
+ return ret;
+
+ /* customize more if needed */
+
+ return 0;
+}
+
+static const struct of_device_id custom_of_match[] = {
+ { .compatible = "audio-graph-card2-custom-sample", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, custom_of_match);
+
+static struct platform_driver custom_card = {
+ .driver = {
+ .name = "audio-graph-card2-custom-sample",
+ .of_match_table = custom_of_match,
+ },
+ .probe = custom_probe,
+ .remove = simple_util_remove,
+};
+module_platform_driver(custom_card);
+
+MODULE_ALIAS("platform:asoc-audio-graph-card2-custom-sample");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Audio Graph Card2 Custom Sample");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample1.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample1.dtsi
new file mode 100644
index 000000000000..12d40e05de46
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2-custom-sample1.dtsi
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * audio-graph-card2-custom-sample1.dtsi
+ *
+ * Copyright (C) 2020 Renesas Electronics Corp.
+ * Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This sample indicates how to use audio-graph-card2 and its
+ * custom driver. "audio-graph-card2-custom-sample" is the custome driver
+ * which is using audio-graph-card2.
+ *
+ * You can easily use this sample by adding below line on your DT file,
+ * and add new CONFIG to your .config.
+ *
+ * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample1.dtsi"
+ *
+ * CONFIG_SND_AUDIO_GRAPH_CARD2
+ * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
+ * CONFIG_SND_TEST_COMPONENT
+ *
+ *
+ * You can indicate more detail each device behavior as debug if you modify
+ * "compatible" on each test-component. see below
+ *
+ * test_cpu {
+ * - compatible = "test-cpu";
+ * + compatible = "test-cpu-verbose";
+ * ...
+ * };
+ *
+ * test_codec {
+ * - compatible = "test-codec";
+ * + compatible = "test-codec-verbose";
+ * ...
+ * };
+ *
+ *
+ * Below sample doesn't use "format" property,
+ * because test-component driver (test-cpu/test-codec) is supporting
+ * snd_soc_dai_ops :: .auto_selectable_formats.
+ * see
+ * snd_soc_runtime_get_dai_fmt()
+ * linux/sound/soc/generic/test-component.c :: test_dai_formats
+ */
+/ {
+ audio-graph-card2-custom-sample-1 {
+ /*
+ * You can use audio-graph-card2 directly by using
+ *
+ * compatible = "audio-graph-card2";
+ */
+ compatible = "audio-graph-card2-custom-sample";
+ label = "card2-custom-sample-1";
+
+ /*
+ * @ : used at links
+ */
+ links = <
+ /*
+ *
+ * [Normal]
+ *
+ * <cpu1_0>
+ * cpu1_0 <-@-----> codec1_0
+ */
+ &cpu1_0 /* CPU side only */
+
+ /*
+ * [Semi-Multi]
+ *
+ * CPU:Codec = 1:N
+ *
+ * <sm> +-+
+ * cpu1_1 <--@---->| |-> codec1_1
+ * | |-> codec1_2
+ * +-+
+ */
+ &sm /* CPU side only */
+
+ /*
+ * [Multi-CPU/Codec-A]
+ *
+ * +-+ <mcpuA> +-+
+ * cpu1_2 <-| |<---@------>| |-> codec1_3
+ * cpu1_3 <-| | | |-> codec1_4
+ * +-+ +-+
+ */
+ &mcpuA /* CPU side only */
+
+ /*
+ * [Multi-CPU/Codec-B]
+ *
+ * +-+ <mcpuB> +-+
+ * | |<---@------>| |
+ * | | | |
+ * cpu1_4 <-| |<---------->| |-> codec1_5
+ * cpu1_5 <-| |<---+------>| |-> codec1_6
+ * +-+ \----->| |-> codec1_7
+ * +-+
+ */
+ &mcpuB /* CPU side only */
+
+ /*
+ * [Multi-CPU/Codec-C]
+ *
+ * +-+ <mcpuC> +-+
+ * | |<---@------>| |
+ * | | | |
+ * cpu1_6 <-| |<---------->| |-> codec1_8
+ * cpu1_7 <-| |<-----+---->| |-> codec1_9
+ * cpu1_8 <-| |<----/ +-+
+ * +-+
+ */
+ &mcpuC /* CPU side only */
+ >;
+
+ multi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /*
+ * [Semi-Multi]
+ *
+ * <sm> +---+
+ * cpu1_1 <---@--->|X A|-> codec1_1
+ * | B|-> codec1_2
+ * +---+
+ */
+ ports@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; smcodec_ep: endpoint { remote-endpoint = <&cpu1_1_ep>; };};/* (X) to pair */
+ port@1 { reg = <1>; smcodec_A_ep: endpoint { remote-endpoint = <&codec1_1_ep>; };};/* (A) Multi Element */
+ port@2 { reg = <2>; smcodec_B_ep: endpoint { remote-endpoint = <&codec1_2_ep>; };};/* (B) Multi Element */
+ };
+
+ /*
+ * [Multi-CPU-A]
+ *
+ * +---+ <mcpuA> +---+
+ * cpu1_2 <-|A X|<---@---->|x a|-> codec1_3
+ * cpu1_3 <-|B | | b|-> codec1_4
+ * +---+ +---+
+ */
+ ports@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ mcpuA: port@0 { reg = <0>; mcpu_A_ep: endpoint { remote-endpoint = <&mcodec_A_ep>; };}; /* (X) to pair */
+ port@1 { reg = <1>; mcpu_AA_ep: endpoint { remote-endpoint = <&cpu1_2_ep>; };}; /* (A) Multi Element */
+ port@2 { reg = <2>; mcpu_AB_ep: endpoint { remote-endpoint = <&cpu1_3_ep>; };}; /* (B) Multi Element */
+ };
+
+ /*
+ * [Multi-Codec-A]
+ *
+ * +---+ <mcpuA> +---+
+ * cpu1_2 <-|A X|<-@------>|x a|-> codec1_3
+ * cpu1_3 <-|B | | b|-> codec1_4
+ * +---+ +---+
+ */
+ ports@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mcodec_A_ep: endpoint { remote-endpoint = <&mcpu_A_ep>; };}; /* (x) to pair */
+ port@1 { reg = <1>; mcodec_Aa_ep: endpoint { remote-endpoint = <&codec1_3_ep>; };}; /* (a) Multi Element */
+ port@2 { reg = <2>; mcodec_Ab_ep: endpoint { remote-endpoint = <&codec1_4_ep>; };}; /* (b) Multi Element */
+ };
+
+ /*
+ * [Multi-CPU-B]
+ *
+ * +---+ <mcpuB> +---+
+ * | X|<---@---->|x |
+ * | | | |
+ * cpu1_4 <-|A 1|<-------->|3 a|-> codec1_5
+ * cpu1_5 <-|B 2|<---+---->|4 b|-> codec1_6
+ * +---+ \--->|5 c|-> codec1_7
+ * +---+
+ */
+ ports@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ mcpuB: port@0 {
+ reg = <0>;
+ mcpu_BX_ep: endpoint { remote-endpoint = <&mcodec_Bx_ep>; }; /* (X) to pair */
+ };
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcpu_BA_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_4_ep>; }; /* (A) Multi Element */
+ mcpu_B1_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_B3_ep>; }; /* (1) connected Codec */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcpu_BB_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_5_ep>; }; /* (B) Multi Element */
+ mcpu_B2_0_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_B4_ep>; }; /* (2) connected Codec */
+ mcpu_B2_1_ep: endpoint@2 { reg = <2>; remote-endpoint = <&mcodec_B5_ep>; }; /* (2) connected Codec */
+ };
+ };
+
+ /*
+ * [Multi-Codec-B]
+ *
+ * +---+ <mcpuB> +---+
+ * | X|<-@------>|x |
+ * | | | |
+ * cpu1_4 <-|A 1|<-------->|3 a|-> codec1_5
+ * cpu1_5 <-|B 2|<---+---->|4 b|-> codec1_6
+ * +---+ \--->|5 c|-> codec1_7
+ * +---+
+ */
+ ports@4 {
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ mcodec_Bx_ep: endpoint { remote-endpoint = <&mcpu_BX_ep>; }; /* (x) to pair */
+ };
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcodec_Ba_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_5_ep>;}; /* (a) Multi Element */
+ mcodec_B3_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_B1_ep>; }; /* (3) connected CPU */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcodec_Bb_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_6_ep>; }; /* (b) Multi Element */
+ mcodec_B4_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_B2_0_ep>;}; /* (4) connected CPU */
+ };
+ port@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ mcodec_Bc_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_7_ep>; }; /* (c) Multi Element */
+ mcodec_B5_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_B2_1_ep>;}; /* (5) connected CPU */
+ };
+ };
+
+ /*
+ * [Multi-CPU-C]
+ *
+ * +---+ <mcpuC> +---+
+ * | X|<-@------>|x |
+ * | | | |
+ * cpu1_6 <-|A 1|<-------->|4 a|-> codec1_8
+ * cpu1_7 <-|B 2|<-----+-->|5 b|-> codec1_9
+ * cpu1_8 <-|C 3|<----/ +---+
+ * +---+
+ */
+ ports@5 {
+ reg = <5>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ mcpuC: port@0 {
+ reg = <0>;
+ mcpu_CX_ep: endpoint { remote-endpoint = <&mcodec_Cx_ep>; }; /* (X) to pair */
+ };
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcpu_CA_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_6_ep>; }; /* (A) Multi Element */
+ mcpu_C1_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_C4_ep>; }; /* (1) connected Codec */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcpu_CB_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_7_ep>; }; /* (B) Multi Element */
+ mcpu_C2_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_C5_0_ep>; }; /* (2) connected Codec */
+ };
+ port@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ mcpu_CC_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_8_ep>; }; /* (C) Multi Element */
+ mcpu_C3_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_C5_1_ep>; }; /* (3) connected Codec */
+ };
+ };
+
+ /*
+ * [Multi-Codec-C]
+ *
+ * +---+ <mcpuC> +---+
+ * | X|<-@------>|x |
+ * | | | |
+ * cpu1_6 <-|A 1|<-------->|4 a|-> codec1_8
+ * cpu1_7 <-|B 2|<-----+-->|5 b|-> codec1_9
+ * cpu1_8 <-|C 3|<----/ +---+
+ * +---+
+ */
+ ports@6 {
+ reg = <6>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ mcodec_Cx_ep: endpoint { remote-endpoint = <&mcpu_CX_ep>; }; /* (x) to pair */
+ };
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcodec_Ca_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_8_ep>;}; /* (a) Multi Element */
+ mcodec_C4_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_C1_ep>; }; /* (4) connected CPU */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcodec_Cb_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_9_ep>;}; /* (b) Multi Element */
+ mcodec_C5_0_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_C2_ep>; }; /* (5) connected CPU */
+ mcodec_C5_1_ep: endpoint@2 { reg = <2>; remote-endpoint = <&mcpu_C3_ep>; }; /* (5) connected CPU */
+ };
+ };
+ };
+ };
+
+ test_cpu_1 {
+ /*
+ * update compatible to indicate more detail behaviour
+ * if you want. see test-compatible for more detail.
+ *
+ * ex)
+ * - compatible = "test-cpu";
+ * + compatible = "test-cpu-verbose";
+ */
+ compatible = "test-cpu";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bitclock-master;
+ frame-master;
+
+ /* [Normal] */
+ cpu1_0: port@0 { reg = <0>; cpu1_0_ep: endpoint { remote-endpoint = <&codec1_0_ep>;}; };
+ /* [Semi-Multi] */
+ sm: port@1 { reg = <1>; cpu1_1_ep: endpoint { remote-endpoint = <&smcodec_ep>; }; };
+ /* [Multi-CPU-A] */
+ port@2 { reg = <2>; cpu1_2_ep: endpoint { remote-endpoint = <&mcpu_AA_ep>; }; };
+ port@3 { reg = <3>; cpu1_3_ep: endpoint { remote-endpoint = <&mcpu_AB_ep>; }; };
+ /* [Multi-CPU-B] */
+ port@4 { reg = <4>; cpu1_4_ep: endpoint { remote-endpoint = <&mcpu_BA_ep>; }; };
+ port@5 { reg = <5>; cpu1_5_ep: endpoint { remote-endpoint = <&mcpu_BB_ep>; }; };
+ /* [Multi-CPU-C] */
+ port@6 { reg = <6>; cpu1_6_ep: endpoint { remote-endpoint = <&mcpu_CA_ep>; }; };
+ port@7 { reg = <7>; cpu1_7_ep: endpoint { remote-endpoint = <&mcpu_CB_ep>; }; };
+ port@8 { reg = <8>; cpu1_8_ep: endpoint { remote-endpoint = <&mcpu_CC_ep>; }; };
+ };
+ };
+
+ test_codec_1 {
+ /*
+ * update compatible to indicate more detail behaviour
+ * if you want. see test-compatible for more detail.
+ *
+ * ex)
+ * - compatible = "test-codec";
+ * + compatible = "test-codec-verbose";
+ */
+ compatible = "test-codec";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* [Normal] */
+ port@0 { reg = <0>; codec1_0_ep: endpoint { remote-endpoint = <&cpu1_0_ep>; }; };
+ /* [Semi-Multi] */
+ port@1 { reg = <1>; codec1_1_ep: endpoint { remote-endpoint = <&smcodec_A_ep>; }; };
+ port@2 { reg = <2>; codec1_2_ep: endpoint { remote-endpoint = <&smcodec_B_ep>; }; };
+ /* [Multi-Codec-0] */
+ port@3 { reg = <3>; codec1_3_ep: endpoint { remote-endpoint = <&mcodec_Aa_ep>; }; };
+ port@4 { reg = <4>; codec1_4_ep: endpoint { remote-endpoint = <&mcodec_Ab_ep>; }; };
+ /* [Multi-Codec-1] */
+ port@5 { reg = <5>; codec1_5_ep: endpoint { remote-endpoint = <&mcodec_Ba_ep>; }; };
+ port@6 { reg = <6>; codec1_6_ep: endpoint { remote-endpoint = <&mcodec_Bb_ep>; }; };
+ port@7 { reg = <7>; codec1_7_ep: endpoint { remote-endpoint = <&mcodec_Bc_ep>; }; };
+ /* [Multi-Codec-2] */
+ port@8 { reg = <8>; codec1_8_ep: endpoint { remote-endpoint = <&mcodec_Ca_ep>; }; };
+ port@9 { reg = <9>; codec1_9_ep: endpoint { remote-endpoint = <&mcodec_Cb_ep>; }; };
+ };
+ };
+};
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample2.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample2.dtsi
new file mode 100644
index 000000000000..1fb061a10ab1
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2-custom-sample2.dtsi
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * audio-graph-card2-custom-sample2.dtsi
+ *
+ * Copyright (C) 2020 Renesas Electronics Corp.
+ * Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This sample indicates how to use audio-graph-card2 and its
+ * custom driver. "audio-graph-card2-custom-sample" is the custome driver
+ * which is using audio-graph-card2.
+ *
+ * You can easily use this sample by adding below line on your DT file,
+ * and add new CONFIG to your .config.
+ *
+ * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample2.dtsi"
+ *
+ * CONFIG_SND_AUDIO_GRAPH_CARD2
+ * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
+ * CONFIG_SND_TEST_COMPONENT
+ *
+ *
+ * You can indicate more detail each device behavior as debug if you modify
+ * "compatible" on each test-component. see below
+ *
+ * test_cpu {
+ * - compatible = "test-cpu";
+ * + compatible = "test-cpu-verbose";
+ * ...
+ * };
+ *
+ * test_codec {
+ * - compatible = "test-codec";
+ * + compatible = "test-codec-verbose";
+ * ...
+ * };
+ *
+ *
+ * Below sample doesn't use "format" property,
+ * because test-component driver (test-cpu/test-codec) is supporting
+ * snd_soc_dai_ops :: .auto_selectable_formats.
+ * see
+ * snd_soc_runtime_get_dai_fmt()
+ * linux/sound/soc/generic/test-component.c :: test_dai_formats
+ */
+/ {
+ audio-graph-card2-custom-sample-2 {
+ /*
+ * You can use audio-graph-card2 directly by using
+ *
+ * compatible = "audio-graph-card2";
+ */
+ compatible = "audio-graph-card2-custom-sample";
+ label = "card2-custom-sample-2";
+
+ /* for [DPCM] */
+ /* BE FE */
+ routing = "TC DAI0 Playback", "DAI0 Playback",
+ "TC DAI0 Playback", "DAI1 Playback",
+ "DAI0 Capture", "TC DAI0 Capture",
+ "DAI1 Capture", "TC DAI0 Capture",
+ /* for [DPCM-Multi] */
+ /* BE FE */
+ "TC DAI1 Playback", "DAI2 Playback",
+ "TC DAI2 Playback", "DAI2 Playback",
+ "TC DAI1 Playback", "DAI3 Playback",
+ "TC DAI2 Playback", "DAI3 Playback",
+ "DAI2 Capture", "TC DAI1 Capture",
+ "DAI2 Capture", "TC DAI2 Capture",
+ "DAI3 Capture", "TC DAI1 Capture",
+ "DAI3 Capture", "TC DAI2 Capture",
+ /* for [Codec2Codec] */
+ "TC OUT", "TC DAI4 Playback",
+ "TC DAI3 Capture", "TC IN",
+ /* for [Codec2Codec-Multi] */
+ "TC OUT", "TC DAI7 Playback",
+ "TC DAI5 Capture", "TC IN",
+ "TC OUT", "TC DAI8 Playback",
+ "TC DAI6 Capture", "TC IN";
+
+ /*
+ * @ : used at links
+ */
+ links = <
+ /*
+ * [DPCM]
+ *
+ * cpu20/cpu21 are converting rate to 44.1kHz
+ *
+ * FE BE
+ * <feA> **** <beA>
+ * cpu2_0 <----@---* *------@---> codec2_0 (44.1kHz)
+ * cpu2_1 <----@---* *
+ * <feB> ****
+ */
+ &feA &feB &beA /* both FE / BE */
+
+ /*
+ * [DPCM-Multi]
+ *
+ * FE BE
+ * <feC> **** <beB> +-+
+ * cpu2_2 <----@---* *------@---> | | -> codec2_1
+ * cpu2_3 <----@---* * | | -> codec2_2
+ * <feD> **** +-+
+ */
+ &feC &feD &beB /* both FE / BE*/
+
+ /*
+ * [Codec2Codec]
+ *
+ * <c2c>
+ * +-@-> codec2_3
+ * |
+ * +---> codec2_4
+ */
+ &c2c /* CPU side only */
+
+ /*
+ * [Codec2Codec-Multi]
+ *
+ * --NOTE--
+ * Multi connect N:M is not supported by ASoC.
+ *
+ * <c2c_m> +-+
+ * +---@-->| |-> codec2_5
+ * | | |-> codec2_6
+ * | +-+
+ * | +-+
+ * +------>| |-> codec2_7
+ * | |-> codec2_8
+ * +-+
+ */
+ &c2c_m /* CPU side only */
+ >;
+
+ multi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /*
+ * [DPCM-Multi]::BE
+ *
+ * FE BE
+ * <feC> **** <beB> +---+
+ * cpu2_2 <----@---* *------@---> |x a| -> codec2_1
+ * cpu2_3 <----@---* * | b| -> codec2_2
+ * <feD> **** +---+
+ */
+ ports@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mbe_x_ep: endpoint { remote-endpoint = <&beB_ep>; };};/* (x) to pair */
+ port@1 { reg = <1>; mbe_a_ep: endpoint { remote-endpoint = <&codec2_1_ep>; };};/* (a) Multi Element */
+ port@2 { reg = <2>; mbe_b_ep: endpoint { remote-endpoint = <&codec2_2_ep>; };};/* (b) Multi Element */
+ };
+
+ /*
+ * [Codec2Codec-Multi]::CPU
+ *
+ * <c2c_m> c2cmf +---+
+ * +---@---------->|X A|-> codec2_5
+ * | | B|-> codec2_6
+ * | +---+
+ * | c2cmb +---+
+ * +-------------->|x a|-> codec2_7
+ * | b|-> codec2_8
+ * +---+
+ */
+ ports@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mc2c0X_ep: endpoint { remote-endpoint = <&c2cmf_ep>; };};/* (X) to pair */
+ port@1 { reg = <1>; mc2c0A_ep: endpoint { remote-endpoint = <&codec2_5_ep>; };};/* (A) Multi Element */
+ port@2 { reg = <2>; mc2c0B_ep: endpoint { remote-endpoint = <&codec2_6_ep>; };};/* (B) Multi Element */
+ };
+
+ /*
+ * [Codec2Codec-Multi]::Codec
+ *
+ * <c2c_m> c2cmf +---+
+ * +---@---------->|X A|-> codec2_5
+ * | | B|-> codec2_6
+ * | +---+
+ * | c2cmb +---+
+ * +-------------->|x a|-> codec2_7
+ * | b|-> codec2_8
+ * +---+
+ */
+ ports@4 {
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mc2c1x_ep: endpoint { remote-endpoint = <&c2cmb_ep>; };};/* (x) to pair */
+ port@1 { reg = <1>; mc2c1a_ep: endpoint { remote-endpoint = <&codec2_7_ep>; };};/* (a) Multi Element */
+ port@2 { reg = <2>; mc2c1b_ep: endpoint { remote-endpoint = <&codec2_8_ep>; };};/* (b) Multi Element */
+ };
+ };
+
+ dpcm {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* FE part */
+ ports@0 {
+ reg = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ /*
+ * [DPCM]::FE
+ *
+ * FE BE
+ * <feA> **** <beA>
+ * cpu2_0 <----@---* *------@---> codec2_0 (44.1kHz)
+ * cpu2_1 <----@---* *
+ * <feB> ****
+ */
+ feA: port@0 { reg = <0>; feA_ep: endpoint { remote-endpoint = <&cpu2_0_ep>; }; };
+ feB: port@1 { reg = <1>; feB_ep: endpoint { remote-endpoint = <&cpu2_1_ep>; }; };
+
+ /*
+ * [DPCM-Multi]::FE
+ *
+ * FE BE
+ * <feC> **** <beB> +-+
+ * cpu2_2 <----@---* *------@---> | | -> codec2_1
+ * cpu2_3 <----@---* * | | -> codec2_2
+ * <feD> **** +-+
+ */
+ feC: port@2 { reg = <2>; feC_ep: endpoint { remote-endpoint = <&cpu2_2_ep>; }; };
+ feD: port@3 { reg = <3>; feD_ep: endpoint { remote-endpoint = <&cpu2_3_ep>; }; };
+ };
+
+ /* BE part */
+ ports@1 {
+ reg = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ /*
+ * [DPCM]::BE
+ *
+ * FE BE
+ * <feA> **** <beA>
+ * cpu2_0 <----@---* *------@---> codec2_0 (44.1kHz)
+ * cpu2_1 <----@---* *
+ * <feB> ****
+ */
+ beA: port@0 { reg = <0>; beA_ep: endpoint { remote-endpoint = <&codec2_0_ep>; }; };
+
+ /*
+ * [DPCM-Multi]::BE
+ *
+ * FE BE
+ * <feC> **** <beB> +-------+
+ * cpu2_2 <----@---* *------@---> |mbe_x | -> codec2_1
+ * cpu2_3 <----@---* * | | -> codec2_2
+ * <feD> **** +-------+
+ */
+ beB: port@1 { reg = <1>; beB_ep: endpoint { remote-endpoint = <&mbe_x_ep>; }; };
+ };
+ };
+
+ codec2codec {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ /*
+ * [Codec2Codec]
+ *
+ * <c2c>
+ * +-@--> codec2_3
+ * |
+ * +----> codec2_4
+ */
+ ports@0 {
+ reg = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* use default settings */
+ c2c: port@0 { reg = <0>; c2cf_ep: endpoint { remote-endpoint = <&codec2_3_ep>; }; };
+ port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec2_4_ep>; }; };
+ };
+
+ /*
+ * [Codec2Codec-Multi]
+ *
+ * <c2c_m> c2cmf +--------+
+ * +---@---------->|mc2c0X |-> codec2_5
+ * | | |-> codec2_6
+ * | +--------+
+ * | c2cmb +--------+
+ * +-------------->|mc2c1x |-> codec2_7
+ * | |-> codec2_8
+ * +--------+
+ */
+ ports@1 {
+ reg = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* use original settings */
+ rate = <48000>;
+ c2c_m: port@0 { reg = <0>; c2cmf_ep: endpoint { remote-endpoint = <&mc2c0X_ep>; }; };
+ port@1 { reg = <1>; c2cmb_ep: endpoint { remote-endpoint = <&mc2c1x_ep>; }; };
+ };
+ };
+ };
+
+ test_cpu_2 {
+ /*
+ * update compatible to indicate more detail behaviour
+ * if you want. see test-compatible for more detail.
+ *
+ * ex)
+ * - compatible = "test-cpu";
+ * + compatible = "test-cpu-verbose";
+ */
+ compatible = "test-cpu";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bitclock-master;
+ frame-master;
+
+ /* [DPCM]::FE */
+ port@0 { reg = <0>; cpu2_0_ep: endpoint { remote-endpoint = <&feA_ep>; };};
+ port@1 { reg = <1>; cpu2_1_ep: endpoint { remote-endpoint = <&feB_ep>; };};
+ /* [DPCM-Multi]::FE */
+ port@2 { reg = <2>; cpu2_2_ep: endpoint { remote-endpoint = <&feC_ep>; };};
+ port@3 { reg = <3>; cpu2_3_ep: endpoint { remote-endpoint = <&feD_ep>; };};
+ };
+ };
+
+ test_codec_2 {
+ /*
+ * update compatible to indicate more detail behaviour
+ * if you want. see test-compatible for more detail.
+ *
+ * ex)
+ * - compatible = "test-codec";
+ * + compatible = "test-codec-verbose";
+ */
+ compatible = "test-codec";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /*
+ * prefix can be added to *component*,
+ * see audio-graph-card2::routing
+ */
+ prefix = "TC";
+
+ /* [DPCM]::BE */
+ port@0 {
+ convert-rate = <44100>;
+ reg = <0>; codec2_0_ep: endpoint { remote-endpoint = <&beA_ep>; };
+ };
+ /* [DPCM-Multi]::BE */
+ port@1 { reg = <1>; codec2_1_ep: endpoint { remote-endpoint = <&mbe_a_ep>; };};
+ port@2 { reg = <2>; codec2_2_ep: endpoint { remote-endpoint = <&mbe_b_ep>; };};
+ /* [Codec2Codec] */
+ port@3 { bitclock-master;
+ frame-master;
+ reg = <3>; codec2_3_ep: endpoint { remote-endpoint = <&c2cf_ep>; };};
+ port@4 { reg = <4>; codec2_4_ep: endpoint { remote-endpoint = <&c2cb_ep>; };};
+ /* [Codec2Codec-Multi] */
+ port@5 { bitclock-master;
+ frame-master;
+ reg = <5>; codec2_5_ep: endpoint { remote-endpoint = <&mc2c0A_ep>; };};
+ port@6 { reg = <6>; codec2_6_ep: endpoint { remote-endpoint = <&mc2c0B_ep>; };};
+ port@7 { reg = <7>; codec2_7_ep: endpoint { remote-endpoint = <&mc2c1a_ep>; };};
+ port@8 { reg = <8>; codec2_8_ep: endpoint { remote-endpoint = <&mc2c1b_ep>; };};
+ };
+ };
+};
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
new file mode 100644
index 000000000000..5dcc78c551a2
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -0,0 +1,1412 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ASoC Audio Graph Card2 support
+//
+// Copyright (C) 2020 Renesas Electronics Corp.
+// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// based on ${LINUX}/sound/soc/generic/audio-graph-card.c
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/graph_card.h>
+
+/************************************
+ daifmt
+ ************************************
+ ports {
+ format = "left_j";
+ port@0 {
+ bitclock-master;
+ sample0: endpoint@0 {
+ frame-master;
+ };
+ sample1: endpoint@1 {
+ format = "i2s";
+ };
+ };
+ ...
+ };
+
+ You can set daifmt at ports/port/endpoint.
+ It uses *latest* format, and *share* master settings.
+ In above case,
+ sample0: left_j, bitclock-master, frame-master
+ sample1: i2s, bitclock-master
+
+ If there was no settings, *Codec* will be
+ bitclock/frame provider as default.
+ see
+ graph_parse_daifmt().
+
+ "format" property is no longer needed on DT if both CPU/Codec drivers are
+ supporting snd_soc_dai_ops :: .auto_selectable_formats.
+ see
+ snd_soc_runtime_get_dai_fmt()
+
+ sample driver
+ linux/sound/soc/renesas/rcar/core.c
+ linux/sound/soc/codecs/ak4613.c
+ linux/sound/soc/codecs/pcm3168a.c
+ linux/sound/soc/soc-utils.c
+ linux/sound/soc/generic/test-component.c
+
+ ************************************
+ Normal Audio-Graph
+ ************************************
+
+ CPU <---> Codec
+
+ sound {
+ compatible = "audio-graph-card2";
+ links = <&cpu>;
+ };
+
+ CPU {
+ cpu: port {
+ bitclock-master;
+ frame-master;
+ cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; };
+ };
+
+ Codec {
+ port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };
+ };
+
+ ************************************
+ Multi-CPU/Codec
+ ************************************
+
+It has link connection part (= X,x) and list part (= A,B,a,b).
+"links" is connection part of CPU side (= @).
+
+ +----+ +---+
+ CPU1 --|A X| <-@----> |x a|-- Codec1
+ CPU2 --|B | | b|-- Codec2
+ +----+ +---+
+
+ sound {
+ compatible = "audio-graph-card2";
+
+(@) links = <&mcpu>;
+
+ multi {
+ ports@0 {
+(@) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; // (X) to pair
+ port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; // (A) Multi Element
+ port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; // (B) Multi Element
+ };
+ ports@1 {
+ port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; // (x) to pair
+ port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; // (a) Multi Element
+ port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; // (b) Multi Element
+ };
+ };
+ };
+
+ CPU {
+ ports {
+ bitclock-master;
+ frame-master;
+ port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
+ port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
+ port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
+ };
+ };
+
+ ************************************
+ DPCM
+ ************************************
+
+ DSP
+ ************
+ PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset
+ PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers
+ PCM2 <--> * fe2 be2 * <--> DAI2: MODEM
+ PCM3 <--> * fe3 be3 * <--> DAI3: BT
+ * be4 * <--> DAI4: DMIC
+ * be5 * <--> DAI5: FM
+ ************
+
+ sound {
+ compatible = "audio-graph-card2";
+
+ // indicate routing
+ routing = "xxx Playback", "xxx Playback",
+ "xxx Playback", "xxx Playback",
+ "xxx Playback", "xxx Playback";
+
+ // indicate all Front-End, Back-End
+ links = <&fe0, &fe1, ...,
+ &be0, &be1, ...>;
+
+ dpcm {
+ // Front-End
+ ports@0 {
+ fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; };
+ fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; };
+ ...
+ };
+ // Back-End
+ ports@1 {
+ be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; };
+ be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; };
+ ...
+ };
+ };
+ };
+
+ CPU {
+ ports {
+ bitclock-master;
+ frame-master;
+ port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; };
+ port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; };
+ ...
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; };
+ port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; };
+ ...
+ };
+ };
+
+ ************************************
+ Codec to Codec
+ ************************************
+
+ +--+
+ | |<-- Codec0 <- IN
+ | |--> Codec1 -> OUT
+ +--+
+
+ sound {
+ compatible = "audio-graph-card2";
+
+ routing = "OUT" ,"DAI1 Playback",
+ "DAI0 Capture", "IN";
+
+ links = <&c2c>;
+
+ codec2codec {
+ ports {
+ rate = <48000>;
+ c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+ port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 {
+ bitclock-master;
+ frame-master;
+ codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
+ port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
+ };
+ };
+
+*/
+
+enum graph_type {
+ GRAPH_NORMAL,
+ GRAPH_DPCM,
+ GRAPH_C2C,
+
+ GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */
+};
+
+#define GRAPH_NODENAME_MULTI "multi"
+#define GRAPH_NODENAME_DPCM "dpcm"
+#define GRAPH_NODENAME_C2C "codec2codec"
+
+#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret)
+static inline int _graph_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
+#define ep_to_port(ep) of_get_parent(ep)
+static struct device_node *port_to_ports(struct device_node *port)
+{
+ struct device_node *ports = of_get_parent(port);
+
+ if (!of_node_name_eq(ports, "ports")) {
+ of_node_put(ports);
+ return NULL;
+ }
+ return ports;
+}
+
+static enum graph_type __graph_get_type(struct device_node *lnk)
+{
+ struct device_node *np, *parent_np;
+ enum graph_type ret;
+
+ /*
+ * target {
+ * ports {
+ * => lnk: port@0 { ... };
+ * port@1 { ... };
+ * };
+ * };
+ */
+ np = of_get_parent(lnk);
+ if (of_node_name_eq(np, "ports")) {
+ parent_np = of_get_parent(np);
+ of_node_put(np);
+ np = parent_np;
+ }
+
+ if (of_node_name_eq(np, GRAPH_NODENAME_MULTI)) {
+ ret = GRAPH_MULTI;
+ fw_devlink_purge_absent_suppliers(&np->fwnode);
+ goto out_put;
+ }
+
+ if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) {
+ ret = GRAPH_DPCM;
+ fw_devlink_purge_absent_suppliers(&np->fwnode);
+ goto out_put;
+ }
+
+ if (of_node_name_eq(np, GRAPH_NODENAME_C2C)) {
+ ret = GRAPH_C2C;
+ fw_devlink_purge_absent_suppliers(&np->fwnode);
+ goto out_put;
+ }
+
+ ret = GRAPH_NORMAL;
+
+out_put:
+ of_node_put(np);
+ return ret;
+
+}
+
+static enum graph_type graph_get_type(struct simple_util_priv *priv,
+ struct device_node *lnk)
+{
+ enum graph_type type = __graph_get_type(lnk);
+
+ /* GRAPH_MULTI here means GRAPH_NORMAL */
+ if (type == GRAPH_MULTI)
+ type = GRAPH_NORMAL;
+
+#ifdef DEBUG
+ {
+ struct device *dev = simple_priv_to_dev(priv);
+ const char *str = "Normal";
+
+ switch (type) {
+ case GRAPH_DPCM:
+ if (graph_util_is_ports0(lnk))
+ str = "DPCM Front-End";
+ else
+ str = "DPCM Back-End";
+ break;
+ case GRAPH_C2C:
+ str = "Codec2Codec";
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%pOF (%s)", lnk, str);
+ }
+#endif
+ return type;
+}
+
+static int graph_lnk_is_multi(struct device_node *lnk)
+{
+ return __graph_get_type(lnk) == GRAPH_MULTI;
+}
+
+static struct device_node *graph_get_next_multi_ep(struct device_node **port, int idx)
+{
+ struct device_node *ports __free(device_node) = port_to_ports(*port);
+ struct device_node *rep = NULL;
+
+ /*
+ * multi {
+ * ports {
+ * => lnk: port@0 { ... }; // to pair
+ * port@1 { ep { ... = rep0 } }; // Multi Element
+ * port@2 { ep { ... = rep1 } }; // Multi Element
+ * ...
+ * };
+ * };
+ *
+ * xxx {
+ * port@0 { rep0 };
+ * port@1 { rep1 };
+ * };
+ */
+
+ /*
+ * Don't use of_graph_get_next_port() here
+ *
+ * In overlay case, "port" are not necessarily in order. So we need to use
+ * of_graph_get_port_by_id() instead
+ */
+ of_node_put(*port);
+
+ *port = of_graph_get_port_by_id(ports, idx);
+ if (*port) {
+ struct device_node *ep __free(device_node) = of_graph_get_next_port_endpoint(*port, NULL);
+
+ rep = of_graph_get_remote_endpoint(ep);
+ }
+
+ return rep;
+}
+
+static const struct snd_soc_ops graph_ops = {
+ .startup = simple_util_startup,
+ .shutdown = simple_util_shutdown,
+ .hw_params = simple_util_hw_params,
+};
+
+static void graph_parse_convert(struct device_node *ep,
+ struct simple_dai_props *props)
+{
+ struct device_node *port __free(device_node) = ep_to_port(ep);
+ struct device_node *ports __free(device_node) = port_to_ports(port);
+ struct simple_util_data *adata = &props->adata;
+
+ simple_util_parse_convert(ports, NULL, adata);
+ simple_util_parse_convert(port, NULL, adata);
+ simple_util_parse_convert(ep, NULL, adata);
+}
+
+static int __graph_parse_node(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *ep,
+ struct link_info *li,
+ int is_cpu, int idx)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct snd_soc_dai_link_component *dlc;
+ struct simple_util_dai *dai;
+ int ret, is_single_links = 0;
+
+ if (is_cpu) {
+ dlc = snd_soc_link_to_cpu(dai_link, idx);
+ dai = simple_props_to_dai_cpu(dai_props, idx);
+ } else {
+ dlc = snd_soc_link_to_codec(dai_link, idx);
+ dai = simple_props_to_dai_codec(dai_props, idx);
+ }
+
+ ret = graph_util_parse_dai(priv, ep, dlc, &is_single_links);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_tdm(ep, dai);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_tdm_width_map(priv, ep, dai);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_clk(dev, ep, dai, dlc);
+ if (ret < 0)
+ goto end;
+
+ /*
+ * set DAI Name
+ */
+ if (!dai_link->name) {
+ struct snd_soc_dai_link_component *cpus = dlc;
+ struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx);
+ char *cpu_multi = "";
+ char *codec_multi = "";
+
+ if (dai_link->num_cpus > 1)
+ cpu_multi = "_multi";
+ if (dai_link->num_codecs > 1)
+ codec_multi = "_multi";
+
+ switch (gtype) {
+ case GRAPH_NORMAL:
+ /* run is_cpu only. see audio_graph2_link_normal() */
+ if (is_cpu)
+ simple_util_set_dailink_name(priv, dai_link, "%s%s-%s%s",
+ cpus->dai_name, cpu_multi,
+ codecs->dai_name, codec_multi);
+ break;
+ case GRAPH_DPCM:
+ if (is_cpu)
+ simple_util_set_dailink_name(priv, dai_link, "fe.%pOFP.%s%s",
+ cpus->of_node, cpus->dai_name, cpu_multi);
+ else
+ simple_util_set_dailink_name(priv, dai_link, "be.%pOFP.%s%s",
+ codecs->of_node, codecs->dai_name, codec_multi);
+ break;
+ case GRAPH_C2C:
+ /* run is_cpu only. see audio_graph2_link_c2c() */
+ if (is_cpu)
+ simple_util_set_dailink_name(priv, dai_link, "c2c.%s%s-%s%s",
+ cpus->dai_name, cpu_multi,
+ codecs->dai_name, codec_multi);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Check "prefix" from top node
+ * if DPCM-BE case
+ */
+ if (!is_cpu && gtype == GRAPH_DPCM) {
+ struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx);
+ struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx);
+ struct device_node *rport __free(device_node) = ep_to_port(ep);
+ struct device_node *rports __free(device_node) = port_to_ports(rport);
+
+ snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix");
+ snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix");
+ }
+
+ if (is_cpu) {
+ struct snd_soc_dai_link_component *cpus = dlc;
+ struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, idx);
+
+ simple_util_canonicalize_cpu(cpus, is_single_links);
+ simple_util_canonicalize_platform(platforms, cpus);
+ }
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_parse_node_multi_nm(struct simple_util_priv *priv,
+ struct snd_soc_dai_link *dai_link,
+ int *nm_idx, int cpu_idx,
+ struct device_node *mcpu_port)
+{
+ /*
+ * +---+ +---+
+ * | X|<-@------->|x |
+ * | | | |
+ * cpu0 <--|A 1|<--------->|4 a|-> codec0
+ * cpu1 <--|B 2|<-----+--->|5 b|-> codec1
+ * cpu2 <--|C 3|<----/ +---+
+ * +---+
+ *
+ * multi {
+ * ports {
+ * port@0 { mcpu_top_ep {... = mcodec_ep; }; }; // (X) to pair
+ * <mcpu_port> port@1 { mcpu0_ep { ... = cpu0_ep; }; // (A) Multi Element
+ * mcpu0_ep_0 { ... = mcodec0_ep_0; }; }; // (1) connected Codec
+ * port@2 { mcpu1_ep { ... = cpu1_ep; }; // (B) Multi Element
+ * mcpu1_ep_0 { ... = mcodec1_ep_0; }; }; // (2) connected Codec
+ * port@3 { mcpu2_ep { ... = cpu2_ep; }; // (C) Multi Element
+ * mcpu2_ep_0 { ... = mcodec1_ep_1; }; }; // (3) connected Codec
+ * };
+ *
+ * ports {
+ * port@0 { mcodec_top_ep {... = mcpu_ep; }; }; // (x) to pair
+ * <mcodec_port>port@1 { mcodec0_ep { ... = codec0_ep; }; // (a) Multi Element
+ * mcodec0_ep_0 { ... = mcpu0_ep_0; }; }; // (4) connected CPU
+ * port@2 { mcodec1_ep { ... = codec1_ep; }; // (b) Multi Element
+ * mcodec1_ep_0 { ... = mcpu1_ep_0; }; // (5) connected CPU
+ * mcodec1_ep_1 { ... = mcpu2_ep_0; }; }; // (5) connected CPU
+ * };
+ * };
+ */
+ struct device_node *mcpu_ep __free(device_node) = of_graph_get_next_port_endpoint(mcpu_port, NULL);
+ struct device_node *mcpu_ports __free(device_node) = port_to_ports(mcpu_port);
+ struct device_node *mcpu_port_top __free(device_node) = of_graph_get_next_port(mcpu_ports, NULL);
+ struct device_node *mcpu_ep_top __free(device_node) = of_graph_get_next_port_endpoint(mcpu_port_top, NULL);
+ struct device_node *mcodec_ep_top __free(device_node) = of_graph_get_remote_endpoint(mcpu_ep_top);
+ struct device_node *mcodec_port_top __free(device_node) = ep_to_port(mcodec_ep_top);
+ struct device_node *mcodec_ports __free(device_node) = port_to_ports(mcodec_port_top);
+ int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
+ int ret = -EINVAL;
+
+ if (cpu_idx > dai_link->num_cpus)
+ goto end;
+
+ for_each_of_graph_port_endpoint(mcpu_port, mcpu_ep_n) {
+ int codec_idx = 0;
+
+ /* ignore 1st ep which is for element */
+ if (mcpu_ep_n == mcpu_ep)
+ continue;
+
+ if (*nm_idx > nm_max)
+ break;
+
+ struct device_node *mcodec_ep_n __free(device_node) = of_graph_get_remote_endpoint(mcpu_ep_n);
+ struct device_node *mcodec_port __free(device_node) = ep_to_port(mcodec_ep_n);
+
+ ret = -EINVAL;
+ if (mcodec_ports != port_to_ports(mcodec_port))
+ break;
+
+ for_each_of_graph_port(mcodec_ports, mcodec_port_i) {
+
+ /* ignore 1st port which is for pair connection */
+ if (mcodec_port_top == mcodec_port_i)
+ continue;
+
+ if (codec_idx > dai_link->num_codecs)
+ break;
+
+ if (mcodec_port_i == mcodec_port) {
+ dai_link->ch_maps[*nm_idx].cpu = cpu_idx;
+ dai_link->ch_maps[*nm_idx].codec = codec_idx;
+
+ (*nm_idx)++;
+ ret = 0;
+ break;
+ }
+ codec_idx++;
+ }
+ if (ret < 0)
+ break;
+ }
+end:
+ return graph_ret(priv, ret);
+}
+
+static int graph_parse_node_multi(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *port,
+ struct link_info *li, int is_cpu)
+{
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct device *dev = simple_priv_to_dev(priv);
+ int ret = -ENOMEM;
+ int nm_idx = 0;
+ int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
+
+ /*
+ * create ch_maps if CPU:Codec = N:M
+ * DPCM is out of scope
+ */
+ if (gtype != GRAPH_DPCM && !dai_link->ch_maps &&
+ dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
+ dai_link->num_cpus != dai_link->num_codecs) {
+
+ dai_link->ch_maps = devm_kcalloc(dev, nm_max,
+ sizeof(struct snd_soc_dai_link_ch_map), GFP_KERNEL);
+ if (!dai_link->ch_maps)
+ goto multi_err;
+ }
+
+ for (int idx = 0;; idx++) {
+ /*
+ * multi {
+ * ports {
+ * <port> port@0 { ... }; // to pair
+ * port@1 { mcpu1_ep { ... = cpu1_ep };}; // Multi Element
+ * port@2 { mcpu2_ep { ... = cpu2_ep };}; // Multi Element
+ * };
+ * };
+ *
+ * cpu {
+ * ports {
+ * <ep> port@0 { cpu1_ep { ... = mcpu1_ep };};
+ * };
+ * };
+ */
+ struct device_node *ep __free(device_node) = graph_get_next_multi_ep(&port, idx + 1);
+ if (!ep)
+ break;
+
+ ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, idx);
+ if (ret < 0)
+ goto multi_err;
+
+ /* CPU:Codec = N:M */
+ if (is_cpu && dai_link->ch_maps) {
+ ret = graph_parse_node_multi_nm(priv, dai_link, &nm_idx, idx, port);
+ if (ret < 0)
+ goto multi_err;
+ }
+ }
+
+ if (is_cpu && dai_link->ch_maps && (nm_idx != nm_max))
+ ret = -EINVAL;
+
+multi_err:
+ return graph_ret(priv, ret);
+}
+
+static int graph_parse_node_single(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *ep,
+ struct link_info *li, int is_cpu)
+{
+ return graph_ret(priv, __graph_parse_node(priv, gtype, ep, li, is_cpu, 0));
+}
+
+static int graph_parse_node(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *ep,
+ struct link_info *li, int is_cpu)
+{
+ struct device_node *port __free(device_node) = ep_to_port(ep);
+ int ret;
+
+ if (graph_lnk_is_multi(port))
+ ret = graph_parse_node_multi(priv, gtype, port, li, is_cpu);
+ else
+ ret = graph_parse_node_single(priv, gtype, ep, li, is_cpu);
+
+ return graph_ret(priv, ret);
+}
+
+static void graph_parse_daifmt(struct device_node *node, unsigned int *daifmt)
+{
+ unsigned int fmt;
+
+ if (!node)
+ return;
+
+ /*
+ * see also above "daifmt" explanation
+ * and samples.
+ */
+
+ /*
+ * ports {
+ * (A)
+ * port {
+ * (B)
+ * endpoint {
+ * (C)
+ * };
+ * };
+ * };
+ * };
+ */
+
+#define update_daifmt(name) \
+ if (!(*daifmt & SND_SOC_DAIFMT_##name##_MASK) && \
+ (fmt & SND_SOC_DAIFMT_##name##_MASK)) \
+ *daifmt |= fmt & SND_SOC_DAIFMT_##name##_MASK
+
+ /*
+ * format
+ *
+ * This function is called by (C) -> (B) -> (A) order.
+ * Set if applicable part was not yet set.
+ */
+ fmt = snd_soc_daifmt_parse_format(node, NULL);
+ update_daifmt(FORMAT);
+ update_daifmt(CLOCK);
+ update_daifmt(INV);
+}
+
+static unsigned int graph_parse_bitframe(struct device_node *ep)
+{
+ struct device_node *port __free(device_node) = ep_to_port(ep);
+ struct device_node *ports __free(device_node) = port_to_ports(port);
+
+ return snd_soc_daifmt_clock_provider_from_bitmap(
+ snd_soc_daifmt_parse_clock_provider_as_bitmap(ep, NULL) |
+ snd_soc_daifmt_parse_clock_provider_as_bitmap(port, NULL) |
+ snd_soc_daifmt_parse_clock_provider_as_bitmap(ports, NULL));
+}
+
+static void graph_link_init(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct device_node *ep_cpu,
+ struct device_node *ep_codec,
+ struct link_info *li,
+ int is_cpu_node)
+{
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct device_node *port_cpu = ep_to_port(ep_cpu);
+ struct device_node *port_codec = ep_to_port(ep_codec);
+ struct device_node *multi_cpu_port = NULL, *multi_codec_port = NULL;
+ struct snd_soc_dai_link_component *dlc;
+ unsigned int daifmt = 0;
+ bool playback_only = 0, capture_only = 0;
+ enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;
+ enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT;
+ int multi_cpu_port_idx = 1, multi_codec_port_idx = 1;
+ int i;
+
+ if (graph_lnk_is_multi(port_cpu)) {
+ multi_cpu_port = port_cpu;
+ ep_cpu = graph_get_next_multi_ep(&multi_cpu_port, multi_cpu_port_idx++);
+ of_node_put(port_cpu);
+ port_cpu = ep_to_port(ep_cpu);
+ } else {
+ of_node_get(ep_cpu);
+ }
+ struct device_node *ports_cpu __free(device_node) = port_to_ports(port_cpu);
+
+ if (graph_lnk_is_multi(port_codec)) {
+ multi_codec_port = port_codec;
+ ep_codec = graph_get_next_multi_ep(&multi_codec_port, multi_codec_port_idx++);
+ of_node_put(port_codec);
+ port_codec = ep_to_port(ep_codec);
+ } else {
+ of_node_get(ep_codec);
+ }
+ struct device_node *ports_codec __free(device_node) = port_to_ports(port_codec);
+
+ graph_parse_daifmt(ep_cpu, &daifmt);
+ graph_parse_daifmt(ep_codec, &daifmt);
+ graph_parse_daifmt(port_cpu, &daifmt);
+ graph_parse_daifmt(port_codec, &daifmt);
+ graph_parse_daifmt(ports_cpu, &daifmt);
+ graph_parse_daifmt(ports_codec, &daifmt);
+ graph_parse_daifmt(lnk, &daifmt);
+
+ graph_util_parse_link_direction(lnk, &playback_only, &capture_only);
+ graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only);
+ graph_util_parse_link_direction(ports_codec, &playback_only, &capture_only);
+ graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only);
+ graph_util_parse_link_direction(port_codec, &playback_only, &capture_only);
+ graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only);
+ graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only);
+
+ of_property_read_u32(lnk, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs);
+
+ graph_util_parse_trigger_order(priv, lnk, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop);
+
+ for_each_link_cpus(dai_link, i, dlc) {
+ dlc->ext_fmt = graph_parse_bitframe(ep_cpu);
+
+ if (multi_cpu_port)
+ ep_cpu = graph_get_next_multi_ep(&multi_cpu_port, multi_cpu_port_idx++);
+ }
+
+ for_each_link_codecs(dai_link, i, dlc) {
+ dlc->ext_fmt = graph_parse_bitframe(ep_codec);
+
+ if (multi_codec_port)
+ ep_codec = graph_get_next_multi_ep(&multi_codec_port, multi_codec_port_idx++);
+ }
+
+ /*** Don't use port_cpu / port_codec after here ***/
+
+ dai_link->playback_only = playback_only;
+ dai_link->capture_only = capture_only;
+
+ dai_link->trigger_start = trigger_start;
+ dai_link->trigger_stop = trigger_stop;
+
+ dai_link->dai_fmt = daifmt;
+ dai_link->init = simple_util_dai_init;
+ dai_link->ops = &graph_ops;
+ if (priv->ops)
+ dai_link->ops = priv->ops;
+
+ of_node_put(port_cpu);
+ of_node_put(port_codec);
+ of_node_put(ep_cpu);
+ of_node_put(ep_codec);
+}
+
+int audio_graph2_link_normal(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *cpu_port = lnk;
+ struct device_node *cpu_ep __free(device_node) = of_graph_get_next_port_endpoint(cpu_port, NULL);
+ struct device_node *codec_ep __free(device_node) = of_graph_get_remote_endpoint(cpu_ep);
+ int ret;
+
+ /*
+ * call Codec first.
+ * see
+ * __graph_parse_node() :: DAI Naming
+ */
+ ret = graph_parse_node(priv, GRAPH_NORMAL, codec_ep, li, 0);
+ if (ret < 0)
+ goto end;
+
+ /*
+ * call CPU, and set DAI Name
+ */
+ ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_ep, li, 1);
+ if (ret < 0)
+ goto end;
+
+ graph_link_init(priv, lnk, cpu_ep, codec_ep, li, 1);
+
+end:
+ return graph_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_normal);
+
+int audio_graph2_link_dpcm(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ep __free(device_node) = of_graph_get_next_port_endpoint(lnk, NULL);
+ struct device_node *rep __free(device_node) = of_graph_get_remote_endpoint(ep);
+ struct device_node *cpu_ep = NULL;
+ struct device_node *codec_ep = NULL;
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ int is_cpu = graph_util_is_ports0(lnk);
+ int ret;
+
+ if (is_cpu) {
+ cpu_ep = rep;
+
+ /*
+ * dpcm {
+ * // Front-End
+ * ports@0 {
+ * => lnk: port@0 { ep: { ... = rep }; };
+ * ...
+ * };
+ * // Back-End
+ * ports@0 {
+ * ...
+ * };
+ * };
+ *
+ * CPU {
+ * rports: ports {
+ * rport: port@0 { rep: { ... = ep } };
+ * }
+ * }
+ */
+ /*
+ * setup CPU here, Codec is already set as dummy.
+ * see
+ * simple_util_init_priv()
+ */
+ dai_link->dynamic = 1;
+ dai_link->dpcm_merged_format = 1;
+
+ ret = graph_parse_node(priv, GRAPH_DPCM, cpu_ep, li, 1);
+ if (ret)
+ return ret;
+
+ } else {
+ codec_ep = rep;
+
+ /*
+ * dpcm {
+ * // Front-End
+ * ports@0 {
+ * ...
+ * };
+ * // Back-End
+ * ports@0 {
+ * => lnk: port@0 { ep: { ... = rep; }; };
+ * ...
+ * };
+ * };
+ *
+ * Codec {
+ * rports: ports {
+ * rport: port@0 { rep: { ... = ep; }; };
+ * }
+ * }
+ */
+ /*
+ * setup Codec here, CPU is already set as dummy.
+ * see
+ * simple_util_init_priv()
+ */
+
+ /* BE settings */
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
+
+ ret = graph_parse_node(priv, GRAPH_DPCM, codec_ep, li, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ graph_parse_convert(ep, dai_props); /* at node of <dpcm> */
+ graph_parse_convert(rep, dai_props); /* at node of <CPU/Codec> */
+
+ graph_link_init(priv, lnk, cpu_ep, codec_ep, li, is_cpu);
+
+ return graph_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);
+
+int audio_graph2_link_c2c(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct device_node *port0 = lnk;
+ struct device_node *ports __free(device_node) = port_to_ports(port0);
+ struct device_node *port1 __free(device_node) = of_graph_get_next_port(ports, port0);
+ u32 val = 0;
+ int ret = -EINVAL;
+
+ /*
+ * codec2codec {
+ * ports {
+ * rate = <48000>;
+ * => lnk: port@0 { c2c0_ep: { ... = codec0_ep; }; };
+ * port@1 { c2c1_ep: { ... = codec1_ep; }; };
+ * };
+ * };
+ *
+ * Codec {
+ * ports {
+ * port@0 { codec0_ep: ... }; };
+ * port@1 { codec1_ep: ... }; };
+ * };
+ * };
+ */
+
+ /*
+ * Card2 can use original Codec2Codec settings if DT has.
+ * It will use default settings if no settings on DT.
+ * see
+ * simple_util_init_for_codec2codec()
+ *
+ * Add more settings here if needed
+ */
+ of_property_read_u32(ports, "rate", &val);
+ if (val) {
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_pcm_stream *c2c_conf;
+
+ c2c_conf = devm_kzalloc(dev, sizeof(*c2c_conf), GFP_KERNEL);
+ if (!c2c_conf) {
+ /*
+ * Clang doesn't allow to use "goto end" before calling __free(),
+ * because it bypasses the initialization. Use graph_ret() directly.
+ */
+ return graph_ret(priv, -ENOMEM);
+ }
+
+ c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */
+ c2c_conf->rates = SNDRV_PCM_RATE_8000_384000;
+ c2c_conf->rate_min =
+ c2c_conf->rate_max = val;
+ c2c_conf->channels_min =
+ c2c_conf->channels_max = 2; /* update ME */
+
+ dai_link->c2c_params = c2c_conf;
+ dai_link->num_c2c_params = 1;
+ }
+
+ struct device_node *ep0 __free(device_node) = of_graph_get_next_port_endpoint(port0, NULL);
+ struct device_node *ep1 __free(device_node) = of_graph_get_next_port_endpoint(port1, NULL);
+
+ struct device_node *codec0_ep __free(device_node) = of_graph_get_remote_endpoint(ep0);
+ struct device_node *codec1_ep __free(device_node) = of_graph_get_remote_endpoint(ep1);
+
+ /*
+ * call Codec first.
+ * see
+ * __graph_parse_node() :: DAI Naming
+ */
+ ret = graph_parse_node(priv, GRAPH_C2C, codec1_ep, li, 0);
+ if (ret < 0)
+ goto end;
+
+ /*
+ * call CPU, and set DAI Name
+ */
+ ret = graph_parse_node(priv, GRAPH_C2C, codec0_ep, li, 1);
+ if (ret < 0)
+ goto end;
+
+ graph_link_init(priv, lnk, codec0_ep, codec1_ep, li, 1);
+end:
+ return graph_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_c2c);
+
+static int graph_link(struct simple_util_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ enum graph_type gtype,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ GRAPH2_CUSTOM func = NULL;
+ int ret = -EINVAL;
+
+ switch (gtype) {
+ case GRAPH_NORMAL:
+ if (hooks && hooks->custom_normal)
+ func = hooks->custom_normal;
+ else
+ func = audio_graph2_link_normal;
+ break;
+ case GRAPH_DPCM:
+ if (hooks && hooks->custom_dpcm)
+ func = hooks->custom_dpcm;
+ else
+ func = audio_graph2_link_dpcm;
+ break;
+ case GRAPH_C2C:
+ if (hooks && hooks->custom_c2c)
+ func = hooks->custom_c2c;
+ else
+ func = audio_graph2_link_c2c;
+ break;
+ default:
+ break;
+ }
+
+ if (!func) {
+ dev_err(dev, "non supported gtype (%d)\n", gtype);
+ goto err;
+ }
+
+ ret = func(priv, lnk, li);
+ if (ret < 0)
+ goto err;
+
+ li->link++;
+err:
+ return graph_ret(priv, ret);
+}
+
+static int graph_counter(struct device_node *lnk)
+{
+ /*
+ * Multi CPU / Codec
+ *
+ * multi {
+ * ports {
+ * => lnk: port@0 { ... }; // to pair
+ * port@1 { ... }; // Multi Element
+ * port@2 { ... }; // Multi Element
+ * ...
+ * };
+ * };
+ *
+ * ignore first lnk part
+ */
+ if (graph_lnk_is_multi(lnk)) {
+ struct device_node *ports = port_to_ports(lnk);
+
+ /*
+ * CPU/Codec = N:M case has many endpoints.
+ * We can't use of_graph_get_endpoint_count() here
+ */
+ return of_graph_get_port_count(ports) - 1;
+ }
+ /*
+ * Single CPU / Codec
+ */
+ else
+ return 1;
+}
+
+static int graph_count_normal(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *cpu_port = lnk;
+ struct device_node *cpu_ep __free(device_node) = of_graph_get_next_port_endpoint(cpu_port, NULL);
+ struct device_node *codec_port __free(device_node) = of_graph_get_remote_port(cpu_ep);
+
+ /*
+ * CPU {
+ * => lnk: port { endpoint { .. }; };
+ * };
+ */
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
+ li->num[li->link].cpus =
+ li->num[li->link].platforms = graph_counter(cpu_port);
+
+ li->num[li->link].codecs = graph_counter(codec_port);
+
+ return 0;
+}
+
+static int graph_count_dpcm(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ep __free(device_node) = of_graph_get_next_port_endpoint(lnk, NULL);
+ struct device_node *rport __free(device_node) = of_graph_get_remote_port(ep);
+
+ /*
+ * dpcm {
+ * // Front-End
+ * ports@0 {
+ * => lnk: port@0 { endpoint { ... }; };
+ * ...
+ * };
+ * // Back-End
+ * ports@1 {
+ * => lnk: port@0 { endpoint { ... }; };
+ * ...
+ * };
+ * };
+ */
+
+ if (graph_util_is_ports0(lnk)) {
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
+ li->num[li->link].cpus = graph_counter(rport); /* FE */
+ li->num[li->link].platforms = graph_counter(rport);
+ } else {
+ li->num[li->link].codecs = graph_counter(rport); /* BE */
+ }
+
+ return 0;
+}
+
+static int graph_count_c2c(struct simple_util_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ports __free(device_node) = port_to_ports(lnk);
+ struct device_node *port0 = of_node_get(lnk);
+ struct device_node *port1 = of_node_get(of_graph_get_next_port(ports, of_node_get(port0)));
+ struct device_node *ep0 __free(device_node) = of_graph_get_next_port_endpoint(port0, NULL);
+ struct device_node *ep1 __free(device_node) = of_graph_get_next_port_endpoint(port1, NULL);
+ struct device_node *codec0 __free(device_node) = of_graph_get_remote_port(ep0);
+ struct device_node *codec1 __free(device_node) = of_graph_get_remote_port(ep1);
+
+ /*
+ * codec2codec {
+ * ports {
+ * => lnk: port@0 { endpoint { ... }; };
+ * port@1 { endpoint { ... }; };
+ * };
+ * };
+ */
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
+ li->num[li->link].cpus =
+ li->num[li->link].platforms = graph_counter(codec0);
+
+ li->num[li->link].codecs = graph_counter(codec1);
+
+ return 0;
+}
+
+static int graph_count(struct simple_util_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ enum graph_type gtype,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ GRAPH2_CUSTOM func = NULL;
+ int ret = -EINVAL;
+
+ if (li->link >= SNDRV_MAX_LINKS) {
+ dev_err(dev, "too many links\n");
+ return ret;
+ }
+
+ switch (gtype) {
+ case GRAPH_NORMAL:
+ func = graph_count_normal;
+ break;
+ case GRAPH_DPCM:
+ func = graph_count_dpcm;
+ break;
+ case GRAPH_C2C:
+ func = graph_count_c2c;
+ break;
+ default:
+ break;
+ }
+
+ if (!func) {
+ dev_err(dev, "non supported gtype (%d)\n", gtype);
+ goto err;
+ }
+
+ ret = func(priv, lnk, li);
+ if (ret < 0)
+ goto err;
+
+ li->link++;
+err:
+ return graph_ret(priv, ret);
+}
+
+static int graph_for_each_link(struct simple_util_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ struct link_info *li,
+ int (*func)(struct simple_util_priv *priv,
+ struct graph2_custom_hooks *hooks,
+ enum graph_type gtype,
+ struct device_node *lnk,
+ struct link_info *li))
+{
+ struct of_phandle_iterator it;
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *node = dev->of_node;
+ struct device_node *lnk;
+ enum graph_type gtype;
+ int rc, ret = 0;
+
+ /* loop for all listed CPU port */
+ of_for_each_phandle(&it, rc, node, "links", NULL, 0) {
+ lnk = it.node;
+
+ gtype = graph_get_type(priv, lnk);
+
+ ret = func(priv, hooks, gtype, lnk, li);
+ if (ret < 0)
+ break;
+ }
+
+ return graph_ret(priv, ret);
+}
+
+int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev,
+ struct graph2_custom_hooks *hooks)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ int ret;
+
+ struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
+ if (!li)
+ return -ENOMEM;
+
+ card->probe = graph_util_card_probe;
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+
+ if ((hooks) && (hooks)->hook_pre) {
+ ret = (hooks)->hook_pre(priv);
+ if (ret < 0)
+ goto err;
+ }
+
+ ret = graph_for_each_link(priv, hooks, li, graph_count);
+ if (!li->link)
+ ret = -EINVAL;
+ if (ret < 0)
+ goto err;
+
+ ret = simple_util_init_priv(priv, li);
+ if (ret < 0)
+ goto err;
+
+ priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->pa_gpio)) {
+ ret = PTR_ERR(priv->pa_gpio);
+ dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
+ goto err;
+ }
+
+ ret = simple_util_parse_widgets(card, NULL);
+ if (ret < 0)
+ goto err;
+
+ ret = simple_util_parse_routing(card, NULL);
+ if (ret < 0)
+ goto err;
+
+ memset(li, 0, sizeof(*li));
+ ret = graph_for_each_link(priv, hooks, li, graph_link);
+ if (ret < 0)
+ goto err;
+
+ ret = simple_util_parse_card_name(priv, NULL);
+ if (ret < 0)
+ goto err;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ if ((hooks) && (hooks)->hook_post) {
+ ret = (hooks)->hook_post(priv);
+ if (ret < 0)
+ goto err;
+ }
+
+ simple_util_debug_info(priv);
+
+ ret = snd_soc_of_parse_aux_devs(card, "aux-devs");
+ if (ret < 0)
+ goto err;
+
+ ret = devm_snd_soc_register_card(dev, card);
+err:
+ if (ret < 0)
+ dev_err_probe(dev, ret, "parse error\n");
+
+ return graph_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(audio_graph2_parse_of);
+
+static int graph_probe(struct platform_device *pdev)
+{
+ struct simple_util_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ /* Allocate the private data and the DAI link array */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ return audio_graph2_parse_of(priv, dev, NULL);
+}
+
+static const struct of_device_id graph_of_match[] = {
+ { .compatible = "audio-graph-card2", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, graph_of_match);
+
+static struct platform_driver graph_card = {
+ .driver = {
+ .name = "asoc-audio-graph-card2",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = graph_of_match,
+ },
+ .probe = graph_probe,
+ .remove = simple_util_remove,
+};
+module_platform_driver(graph_card);
+
+MODULE_ALIAS("platform:asoc-audio-graph-card2");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Audio Graph Card2");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
new file mode 100644
index 000000000000..355f7ec8943c
--- /dev/null
+++ b/sound/soc/generic/simple-card-utils.c
@@ -0,0 +1,1262 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// simple-card-utils.c
+//
+// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <dt-bindings/sound/audio-graph.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/simple_card_utils.h>
+
+#define simple_ret(priv, ret) _simple_ret(priv, __func__, ret)
+static inline int _simple_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
+int simple_util_get_sample_fmt(struct simple_util_data *data)
+{
+ int i;
+ int val = -EINVAL;
+
+ struct {
+ char *fmt;
+ u32 val;
+ } of_sample_fmt_table[] = {
+ { "s8", SNDRV_PCM_FORMAT_S8},
+ { "s16_le", SNDRV_PCM_FORMAT_S16_LE},
+ { "s24_le", SNDRV_PCM_FORMAT_S24_LE},
+ { "s24_3le", SNDRV_PCM_FORMAT_S24_3LE},
+ { "s32_le", SNDRV_PCM_FORMAT_S32_LE},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {
+ if (!strcmp(data->convert_sample_format,
+ of_sample_fmt_table[i].fmt)) {
+ val = of_sample_fmt_table[i].val;
+ break;
+ }
+ }
+ return val;
+}
+EXPORT_SYMBOL_GPL(simple_util_get_sample_fmt);
+
+static void simple_fixup_sample_fmt(struct simple_util_data *data,
+ struct snd_pcm_hw_params *params)
+{
+ int val;
+ struct snd_mask *mask = hw_param_mask(params,
+ SNDRV_PCM_HW_PARAM_FORMAT);
+
+ val = simple_util_get_sample_fmt(data);
+ if (val >= 0) {
+ snd_mask_none(mask);
+ snd_mask_set(mask, val);
+ }
+}
+
+void simple_util_parse_convert(struct device_node *np,
+ char *prefix,
+ struct simple_util_data *data)
+{
+ char prop[128];
+
+ if (!np)
+ return;
+
+ if (!prefix)
+ prefix = "";
+
+ /* sampling rate convert */
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
+ of_property_read_u32(np, prop, &data->convert_rate);
+
+ /* channels transfer */
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
+ of_property_read_u32(np, prop, &data->convert_channels);
+
+ /* convert sample format */
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format");
+ of_property_read_string(np, prop, &data->convert_sample_format);
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_convert);
+
+/**
+ * simple_util_is_convert_required() - Query if HW param conversion was requested
+ * @data: Link data.
+ *
+ * Returns true if any HW param conversion was requested for this DAI link with
+ * any "convert-xxx" properties.
+ */
+bool simple_util_is_convert_required(const struct simple_util_data *data)
+{
+ return data->convert_rate ||
+ data->convert_channels ||
+ data->convert_sample_format;
+}
+EXPORT_SYMBOL_GPL(simple_util_is_convert_required);
+
+int simple_util_parse_daifmt(struct device *dev,
+ struct device_node *node,
+ struct device_node *codec,
+ char *prefix,
+ unsigned int *retfmt)
+{
+ struct device_node *bitclkmaster = NULL;
+ struct device_node *framemaster = NULL;
+ unsigned int daifmt;
+
+ daifmt = snd_soc_daifmt_parse_format(node, prefix);
+
+ snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
+ if (!bitclkmaster && !framemaster) {
+ /*
+ * No dai-link level and master setting was not found from
+ * sound node level, revert back to legacy DT parsing and
+ * take the settings from codec node.
+ */
+ dev_dbg(dev, "Revert to legacy daifmt parsing\n");
+
+ daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
+ } else {
+ daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
+ ((codec == bitclkmaster) << 4) | (codec == framemaster));
+ }
+
+ of_node_put(bitclkmaster);
+ of_node_put(framemaster);
+
+ *retfmt = daifmt;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_daifmt);
+
+int simple_util_parse_tdm_width_map(struct simple_util_priv *priv, struct device_node *np,
+ struct simple_util_dai *dai)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ int n, i, ret;
+ u32 *p;
+
+ /*
+ * NOTE
+ *
+ * Clang doesn't allow to use "goto end" before calling __free(),
+ * because it bypasses the initialization. Use simple_ret() directly.
+ */
+
+ n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
+ if (n <= 0)
+ return 0;
+
+ if (n % 3) {
+ dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
+ return simple_ret(priv, -EINVAL); /* see NOTE */
+ }
+
+ ret = -ENOMEM;
+ dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
+ if (!dai->tdm_width_map)
+ return simple_ret(priv, ret); /* see NOTE */
+
+ u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
+ if (!array_values)
+ goto end;
+
+ ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
+ if (ret < 0) {
+ dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
+ goto end;
+ }
+
+ p = array_values;
+ for (i = 0; i < n / 3; ++i) {
+ dai->tdm_width_map[i].sample_bits = *p++;
+ dai->tdm_width_map[i].slot_width = *p++;
+ dai->tdm_width_map[i].slot_count = *p++;
+ }
+
+ dai->n_tdm_widths = i;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map);
+
+int simple_util_set_dailink_name(struct simple_util_priv *priv,
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ va_list ap;
+ char *name = NULL;
+ int ret = -ENOMEM;
+
+ va_start(ap, fmt);
+ name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
+ va_end(ap);
+
+ if (name) {
+ ret = 0;
+
+ dai_link->name = name;
+ dai_link->stream_name = name;
+ }
+
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(simple_util_set_dailink_name);
+
+int simple_util_parse_card_name(struct simple_util_priv *priv,
+ char *prefix)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ int ret;
+
+ if (!prefix)
+ prefix = "";
+
+ /* Parse the card name from DT */
+ ret = snd_soc_of_parse_card_name(card, "label");
+ if (ret < 0 || !card->name) {
+ char prop[128];
+
+ snprintf(prop, sizeof(prop), "%sname", prefix);
+ ret = snd_soc_of_parse_card_name(card, prop);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (!card->name && card->dai_link)
+ card->name = card->dai_link->name;
+end:
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_card_name);
+
+static int simple_clk_enable(struct simple_util_dai *dai)
+{
+ if (dai)
+ return clk_prepare_enable(dai->clk);
+
+ return 0;
+}
+
+static void simple_clk_disable(struct simple_util_dai *dai)
+{
+ if (dai)
+ clk_disable_unprepare(dai->clk);
+}
+
+int simple_util_parse_clk(struct device *dev,
+ struct device_node *node,
+ struct simple_util_dai *simple_dai,
+ struct snd_soc_dai_link_component *dlc)
+{
+ struct clk *clk;
+ u32 val;
+
+ /*
+ * Parse dai->sysclk come from "clocks = <&xxx>"
+ * (if system has common clock)
+ * or "system-clock-frequency = <xxx>"
+ * or device's module clock.
+ */
+ clk = devm_get_clk_from_child(dev, node, NULL);
+ simple_dai->clk_fixed = of_property_read_bool(
+ node, "system-clock-fixed");
+ if (!IS_ERR(clk)) {
+ simple_dai->sysclk = clk_get_rate(clk);
+
+ simple_dai->clk = clk;
+ } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
+ simple_dai->sysclk = val;
+ simple_dai->clk_fixed = true;
+ } else {
+ clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
+ if (!IS_ERR(clk))
+ simple_dai->sysclk = clk_get_rate(clk);
+ }
+
+ if (of_property_read_bool(node, "system-clock-direction-out"))
+ simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_clk);
+
+static int simple_check_fixed_sysclk(struct device *dev,
+ struct simple_util_dai *dai,
+ unsigned int *fixed_sysclk)
+{
+ if (dai->clk_fixed) {
+ if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
+ dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
+ *fixed_sysclk, dai->sysclk);
+ return -EINVAL;
+ }
+ *fixed_sysclk = dai->sysclk;
+ }
+
+ return 0;
+}
+
+int simple_util_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd);
+ struct simple_util_dai *dai;
+ unsigned int fixed_sysclk = 0;
+ int i1, i2, i;
+ int ret;
+
+ for_each_prop_dai_cpu(props, i1, dai) {
+ ret = simple_clk_enable(dai);
+ if (ret)
+ goto cpu_err;
+ ret = simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
+ if (ret)
+ goto cpu_err;
+ }
+
+ for_each_prop_dai_codec(props, i2, dai) {
+ ret = simple_clk_enable(dai);
+ if (ret)
+ goto codec_err;
+ ret = simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
+ if (ret)
+ goto codec_err;
+ }
+
+ if (fixed_sysclk && props->mclk_fs) {
+ unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
+
+ if (fixed_sysclk % props->mclk_fs) {
+ dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
+ fixed_sysclk, props->mclk_fs);
+ ret = -EINVAL;
+ goto codec_err;
+ }
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
+ fixed_rate, fixed_rate);
+ if (ret < 0)
+ goto codec_err;
+ }
+
+ return 0;
+
+codec_err:
+ for_each_prop_dai_codec(props, i, dai) {
+ if (i >= i2)
+ break;
+ simple_clk_disable(dai);
+ }
+cpu_err:
+ for_each_prop_dai_cpu(props, i, dai) {
+ if (i >= i1)
+ break;
+ simple_clk_disable(dai);
+ }
+
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(simple_util_startup);
+
+void simple_util_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd);
+ struct simple_util_dai *dai;
+ int i;
+
+ for_each_prop_dai_cpu(props, i, dai) {
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, i);
+
+ if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, dai->clk_direction);
+
+ simple_clk_disable(dai);
+ }
+ for_each_prop_dai_codec(props, i, dai) {
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, i);
+
+ if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
+ snd_soc_dai_set_sysclk(codec_dai, 0, 0, dai->clk_direction);
+
+ simple_clk_disable(dai);
+ }
+}
+EXPORT_SYMBOL_GPL(simple_util_shutdown);
+
+static int simple_set_clk_rate(struct simple_util_priv *priv,
+ struct simple_util_dai *simple_dai,
+ unsigned long rate)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
+
+ if (!simple_dai)
+ return 0;
+
+ if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
+ dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
+ goto end;
+ }
+
+ if (!simple_dai->clk)
+ return 0;
+
+ if (clk_get_rate(simple_dai->clk) == rate)
+ return 0;
+
+ ret = clk_set_rate(simple_dai->clk, rate);
+end:
+ return simple_ret(priv, ret);
+}
+
+static int simple_set_tdm(struct simple_util_priv *priv,
+ struct snd_soc_dai *dai,
+ struct simple_util_dai *simple_dai,
+ struct snd_pcm_hw_params *params)
+{
+ int sample_bits = params_width(params);
+ int slot_width, slot_count;
+ int i, ret;
+
+ if (!simple_dai || !simple_dai->tdm_width_map)
+ return 0;
+
+ slot_width = simple_dai->slot_width;
+ slot_count = simple_dai->slots;
+
+ if (slot_width == 0)
+ slot_width = sample_bits;
+
+ for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
+ if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
+ slot_width = simple_dai->tdm_width_map[i].slot_width;
+ slot_count = simple_dai->tdm_width_map[i].slot_count;
+ break;
+ }
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(dai,
+ simple_dai->tx_slot_mask,
+ simple_dai->rx_slot_mask,
+ slot_count,
+ slot_width);
+
+ return simple_ret(priv, ret);
+}
+
+int simple_util_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct simple_util_dai *pdai;
+ struct snd_soc_dai *sdai;
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd);
+ unsigned int mclk, mclk_fs = 0;
+ int i, ret;
+
+ if (props->mclk_fs)
+ mclk_fs = props->mclk_fs;
+
+ if (mclk_fs) {
+ struct snd_soc_component *component;
+ mclk = params_rate(params) * mclk_fs;
+
+ for_each_prop_dai_codec(props, i, pdai) {
+ ret = simple_set_clk_rate(priv, pdai, mclk);
+ if (ret < 0)
+ goto end;
+ }
+
+ for_each_prop_dai_cpu(props, i, pdai) {
+ ret = simple_set_clk_rate(priv, pdai, mclk);
+ if (ret < 0)
+ goto end;
+ }
+
+ /* Ensure sysclk is set on all components in case any
+ * (such as platform components) are missed by calls to
+ * snd_soc_dai_set_sysclk.
+ */
+ for_each_rtd_components(rtd, i, component) {
+ ret = snd_soc_component_set_sysclk(component, 0, 0,
+ mclk, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP)
+ goto end;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, sdai) {
+ pdai = simple_props_to_dai_codec(props, i);
+ ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction);
+ if (ret && ret != -ENOTSUPP)
+ goto end;
+ }
+
+ for_each_rtd_cpu_dais(rtd, i, sdai) {
+ pdai = simple_props_to_dai_cpu(props, i);
+ ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction);
+ if (ret && ret != -ENOTSUPP)
+ goto end;
+ }
+ }
+
+ for_each_prop_dai_codec(props, i, pdai) {
+ sdai = snd_soc_rtd_to_codec(rtd, i);
+ ret = simple_set_tdm(priv, sdai, pdai, params);
+ if (ret < 0)
+ goto end;
+ }
+
+ for_each_prop_dai_cpu(props, i, pdai) {
+ sdai = snd_soc_rtd_to_cpu(rtd, i);
+ ret = simple_set_tdm(priv, sdai, pdai, params);
+ if (ret < 0)
+ goto end;
+ }
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(simple_util_hw_params);
+
+int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = runtime_simple_priv_to_props(priv, rtd);
+ struct simple_util_data *data = &dai_props->adata;
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ if (data->convert_rate)
+ rate->min =
+ rate->max = data->convert_rate;
+
+ if (data->convert_channels)
+ channels->min =
+ channels->max = data->convert_channels;
+
+ if (data->convert_sample_format)
+ simple_fixup_sample_fmt(data, params);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_be_hw_params_fixup);
+
+static int simple_init_dai(struct simple_util_priv *priv,
+ struct snd_soc_dai *dai, struct simple_util_dai *simple_dai)
+{
+ int ret;
+
+ if (!simple_dai)
+ return 0;
+
+ if (simple_dai->sysclk) {
+ ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
+ simple_dai->clk_direction);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dai->dev, "simple-card: set_sysclk error\n");
+ goto end;
+ }
+ }
+
+ if (simple_dai->slots) {
+ ret = snd_soc_dai_set_tdm_slot(dai,
+ simple_dai->tx_slot_mask,
+ simple_dai->rx_slot_mask,
+ simple_dai->slots,
+ simple_dai->slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
+ goto end;
+ }
+ }
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
+
+static inline int simple_component_is_codec(struct snd_soc_component *component)
+{
+ return component->driver->endianness;
+}
+
+static int simple_init_for_codec2codec(struct simple_util_priv *priv,
+ struct snd_soc_pcm_runtime *rtd,
+ struct simple_dai_props *dai_props)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_component *component;
+ struct snd_soc_pcm_stream *c2c_params;
+ struct snd_pcm_hardware hw;
+ int i, ret, stream;
+
+ /* Do nothing if it already has Codec2Codec settings */
+ if (dai_link->c2c_params)
+ return 0;
+
+ /* Do nothing if it was DPCM :: BE */
+ if (dai_link->no_pcm)
+ return 0;
+
+ /* Only Codecs */
+ for_each_rtd_components(rtd, i, component) {
+ if (!simple_component_is_codec(component))
+ return 0;
+ }
+
+ /* Assumes the capabilities are the same for all supported streams */
+ for_each_pcm_streams(stream) {
+ ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
+ if (ret == 0)
+ break;
+ }
+
+ if (ret < 0) {
+ dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
+ goto end;
+ }
+
+ ret = -ENOMEM;
+ c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);
+ if (!c2c_params)
+ goto end;
+
+ c2c_params->formats = hw.formats;
+ c2c_params->rates = hw.rates;
+ c2c_params->rate_min = hw.rate_min;
+ c2c_params->rate_max = hw.rate_max;
+ c2c_params->channels_min = hw.channels_min;
+ c2c_params->channels_max = hw.channels_max;
+
+ dai_link->c2c_params = c2c_params;
+ dai_link->num_c2c_params = 1;
+
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
+
+int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd);
+ struct simple_util_dai *dai;
+ int i, ret;
+
+ for_each_prop_dai_codec(props, i, dai) {
+ ret = simple_init_dai(priv, snd_soc_rtd_to_codec(rtd, i), dai);
+ if (ret < 0)
+ goto end;
+ }
+ for_each_prop_dai_cpu(props, i, dai) {
+ ret = simple_init_dai(priv, snd_soc_rtd_to_cpu(rtd, i), dai);
+ if (ret < 0)
+ goto end;
+ }
+
+ ret = simple_init_for_codec2codec(priv, rtd, props);
+end:
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(simple_util_dai_init);
+
+void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
+ struct snd_soc_dai_link_component *cpus)
+{
+ /*
+ * Assumes Platform == CPU
+ *
+ * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
+ * are different Component, but are sharing same component->dev.
+ *
+ * Let's assume Platform is same as CPU if it doesn't identify Platform on DT.
+ * see
+ * simple-card.c :: simple_count_noml()
+ */
+ if (!platforms->of_node)
+ snd_soc_dlc_use_cpu_as_platform(platforms, cpus);
+}
+EXPORT_SYMBOL_GPL(simple_util_canonicalize_platform);
+
+void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
+ int is_single_links)
+{
+ /*
+ * In soc_bind_dai_link() will check cpu name after
+ * of_node matching if dai_link has cpu_dai_name.
+ * but, it will never match if name was created by
+ * fmt_single_name() remove cpu_dai_name if cpu_args
+ * was 0. See:
+ * fmt_single_name()
+ * fmt_multiple_name()
+ */
+ if (is_single_links)
+ cpus->dai_name = NULL;
+}
+EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu);
+
+void simple_util_clean_reference(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_dai_link_component *cpu;
+ struct snd_soc_dai_link_component *codec;
+ int i, j;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ for_each_link_cpus(dai_link, j, cpu)
+ of_node_put(cpu->of_node);
+ for_each_link_codecs(dai_link, j, codec)
+ of_node_put(codec->of_node);
+ }
+}
+EXPORT_SYMBOL_GPL(simple_util_clean_reference);
+
+int simple_util_parse_routing(struct snd_soc_card *card,
+ char *prefix)
+{
+ struct device_node *node = card->dev->of_node;
+ char prop[128];
+
+ if (!prefix)
+ prefix = "";
+
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
+
+ if (!of_property_present(node, prop))
+ return 0;
+
+ return snd_soc_of_parse_audio_routing(card, prop);
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_routing);
+
+int simple_util_parse_widgets(struct snd_soc_card *card,
+ char *prefix)
+{
+ struct device_node *node = card->dev->of_node;
+ char prop[128];
+
+ if (!prefix)
+ prefix = "";
+
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
+
+ if (of_property_present(node, prop))
+ return snd_soc_of_parse_audio_simple_widgets(card, prop);
+
+ /* no widgets is not error */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_widgets);
+
+int simple_util_parse_pin_switches(struct snd_soc_card *card,
+ char *prefix)
+{
+ char prop[128];
+
+ if (!prefix)
+ prefix = "";
+
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
+
+ return snd_soc_of_parse_pin_switches(card, prop);
+}
+EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches);
+
+int simple_util_init_jack(struct snd_soc_card *card,
+ struct simple_util_jack *sjack,
+ int is_hp, char *prefix,
+ char *pin)
+{
+ struct device *dev = card->dev;
+ struct gpio_desc *desc;
+ char prop[128];
+ char *pin_name;
+ char *gpio_name;
+ int mask;
+ int error;
+
+ if (!prefix)
+ prefix = "";
+
+ if (is_hp) {
+ snprintf(prop, sizeof(prop), "%shp-det", prefix);
+ pin_name = pin ? pin : "Headphones";
+ gpio_name = "Headphone detection";
+ mask = SND_JACK_HEADPHONE;
+ } else {
+ snprintf(prop, sizeof(prop), "%smic-det", prefix);
+ pin_name = pin ? pin : "Mic Jack";
+ gpio_name = "Mic detection";
+ mask = SND_JACK_MICROPHONE;
+ }
+
+ desc = gpiod_get_optional(dev, prop, GPIOD_IN);
+ error = PTR_ERR_OR_ZERO(desc);
+ if (error)
+ return error;
+
+ if (desc) {
+ error = gpiod_set_consumer_name(desc, gpio_name);
+ if (error)
+ return error;
+
+ sjack->pin.pin = pin_name;
+ sjack->pin.mask = mask;
+
+ sjack->gpio.name = gpio_name;
+ sjack->gpio.report = mask;
+ sjack->gpio.desc = desc;
+ sjack->gpio.debounce_time = 150;
+
+ snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
+ &sjack->pin, 1);
+
+ snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_init_jack);
+
+int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct snd_soc_component *component;
+ int found_jack_index = 0;
+ int type = 0;
+ int num = 0;
+ int ret;
+
+ if (priv->aux_jacks)
+ return 0;
+
+ for_each_card_auxs(card, component) {
+ type = snd_soc_component_get_jack_type(component);
+ if (type > 0)
+ num++;
+ }
+ if (num < 1)
+ return 0;
+
+ priv->aux_jacks = devm_kcalloc(card->dev, num,
+ sizeof(struct snd_soc_jack), GFP_KERNEL);
+ if (!priv->aux_jacks)
+ return simple_ret(priv, -ENOMEM);
+
+ for_each_card_auxs(card, component) {
+ char id[128];
+ struct snd_soc_jack *jack;
+
+ if (found_jack_index >= num)
+ break;
+
+ type = snd_soc_component_get_jack_type(component);
+ if (type <= 0)
+ continue;
+
+ /* create jack */
+ jack = &(priv->aux_jacks[found_jack_index++]);
+ snprintf(id, sizeof(id), "%s-jack", component->name);
+ ret = snd_soc_card_jack_new(card, id, type, jack);
+ if (ret)
+ continue;
+
+ (void)snd_soc_component_set_jack(component, jack, NULL);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_init_aux_jacks);
+
+static struct simple_util_dai dummy_util_dais = {
+ .name = "dummy_util_dais",
+};
+
+int simple_util_init_priv(struct simple_util_priv *priv,
+ struct link_info *li)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link;
+ struct simple_dai_props *dai_props;
+ struct simple_util_dai *dais;
+ struct snd_soc_dai_link_component *dlcs;
+ struct snd_soc_codec_conf *cconf = NULL;
+ int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
+
+ dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
+ dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_props || !dai_link)
+ return -ENOMEM;
+
+ /*
+ * dais (= CPU+Codec)
+ * dlcs (= CPU+Codec+Platform)
+ */
+ for (i = 0; i < li->link; i++) {
+ int cc = li->num[i].cpus + li->num[i].codecs;
+
+ dai_num += cc;
+ dlc_num += cc + li->num[i].platforms;
+
+ if (!li->num[i].cpus)
+ cnf_num += li->num[i].codecs;
+ }
+
+ dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
+ dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
+ if (!dais || !dlcs)
+ return -ENOMEM;
+
+ if (cnf_num) {
+ cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
+ if (!cconf)
+ return -ENOMEM;
+ }
+
+ dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
+ li->link, dai_num, cnf_num);
+
+ priv->dai_props = dai_props;
+ priv->dai_link = dai_link;
+ priv->dais = dais;
+ priv->dlcs = dlcs;
+ priv->codec_conf = cconf;
+
+ card->dai_link = priv->dai_link;
+ card->num_links = li->link;
+ card->codec_conf = cconf;
+ card->num_configs = cnf_num;
+
+ for (i = 0; i < li->link; i++) {
+ if (li->num[i].cpus) {
+ /* Normal CPU */
+ dai_link[i].cpus = dlcs;
+ dai_props[i].num.cpus =
+ dai_link[i].num_cpus = li->num[i].cpus;
+ dai_props[i].cpu_dai = dais;
+
+ dlcs += li->num[i].cpus;
+ dais += li->num[i].cpus;
+ } else {
+ /* DPCM Be's CPU = dummy */
+ dai_link[i].cpus = &snd_soc_dummy_dlc;
+ dai_props[i].num.cpus =
+ dai_link[i].num_cpus = 1;
+ dai_props[i].cpu_dai = &dummy_util_dais;
+ }
+
+ if (li->num[i].codecs) {
+ /* Normal Codec */
+ dai_link[i].codecs = dlcs;
+ dai_props[i].num.codecs =
+ dai_link[i].num_codecs = li->num[i].codecs;
+ dai_props[i].codec_dai = dais;
+
+ dlcs += li->num[i].codecs;
+ dais += li->num[i].codecs;
+
+ if (!li->num[i].cpus) {
+ /* DPCM Be's Codec */
+ dai_props[i].codec_conf = cconf;
+ cconf += li->num[i].codecs;
+ }
+ } else {
+ /* DPCM Fe's Codec = dummy */
+ dai_link[i].codecs = &snd_soc_dummy_dlc;
+ dai_props[i].num.codecs =
+ dai_link[i].num_codecs = 1;
+ dai_props[i].codec_dai = &dummy_util_dais;
+ }
+
+ if (li->num[i].platforms) {
+ /* Have Platform */
+ dai_link[i].platforms = dlcs;
+ dai_props[i].num.platforms =
+ dai_link[i].num_platforms = li->num[i].platforms;
+
+ dlcs += li->num[i].platforms;
+ } else {
+ /* Doesn't have Platform */
+ dai_link[i].platforms = NULL;
+ dai_props[i].num.platforms =
+ dai_link[i].num_platforms = 0;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_util_init_priv);
+
+void simple_util_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ simple_util_clean_reference(card);
+}
+EXPORT_SYMBOL_GPL(simple_util_remove);
+
+int graph_util_card_probe(struct snd_soc_card *card)
+{
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = simple_util_init_hp(card, &priv->hp_jack, NULL);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_init_mic(card, &priv->mic_jack, NULL);
+end:
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(graph_util_card_probe);
+
+int graph_util_is_ports0(struct device_node *np)
+{
+ struct device_node *parent __free(device_node) = of_get_parent(np);
+ struct device_node *port;
+
+ /* np is "endpoint" or "port" */
+ if (of_node_name_eq(np, "endpoint"))
+ port = parent;
+ else
+ port = np;
+
+ struct device_node *ports __free(device_node) = of_get_parent(port);
+ struct device_node *top __free(device_node) = of_get_parent(ports);
+ struct device_node *ports0 __free(device_node) = of_get_child_by_name(top, "ports");
+
+ return ports0 == ports;
+}
+EXPORT_SYMBOL_GPL(graph_util_is_ports0);
+
+static int graph_get_dai_id(struct device_node *ep)
+{
+ struct device_node *node __free(device_node) = of_graph_get_port_parent(ep);
+ struct device_node *port __free(device_node) = of_get_parent(ep);
+ struct of_endpoint info;
+ int i, id;
+ int ret;
+
+ /* use driver specified DAI ID if exist */
+ ret = snd_soc_get_dai_id(ep);
+ if (ret != -ENOTSUPP)
+ return ret;
+
+ /* use endpoint/port reg if exist */
+ ret = of_graph_parse_endpoint(ep, &info);
+ if (ret == 0) {
+ /*
+ * Because it will count port/endpoint if it doesn't have "reg".
+ * But, we can't judge whether it has "no reg", or "reg = <0>"
+ * only of_graph_parse_endpoint().
+ * We need to check "reg" property
+ */
+
+ /* check port first */
+ ret = of_property_present(port, "reg");
+ if (ret)
+ return info.port;
+
+ /* check endpoint 2nd as backup */
+ if (of_property_present(ep, "reg"))
+ return info.id;
+ }
+
+ /*
+ * Non HDMI sound case, counting port/endpoint on its DT
+ * is enough. Let's count it.
+ */
+ i = 0;
+ id = -1;
+ for_each_of_graph_port(node, p) {
+ if (port == p) {
+ id = i;
+ break;
+ }
+ i++;
+ }
+
+ if (id < 0)
+ return -ENODEV;
+
+ return id;
+}
+
+int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep,
+ struct snd_soc_dai_link_component *dlc, int *is_single_link)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *node;
+ struct of_phandle_args args = {};
+ struct snd_soc_dai *dai;
+ int ret;
+
+ if (!ep)
+ return 0;
+
+ node = of_graph_get_port_parent(ep);
+
+ /*
+ * Try to find from DAI node
+ */
+ args.np = ep;
+ dai = snd_soc_get_dai_via_args(&args);
+ if (dai) {
+ const char *dai_name = snd_soc_dai_name_get(dai);
+ const struct of_phandle_args *dai_args = snd_soc_copy_dai_args(dev, &args);
+
+ ret = -ENOMEM;
+ if (!dai_args)
+ goto err;
+
+ dlc->of_node = node;
+ dlc->dai_name = dai_name;
+ dlc->dai_args = dai_args;
+
+ goto parse_dai_end;
+ }
+
+ /* Get dai->name */
+ args.np = node;
+ args.args[0] = graph_get_dai_id(ep);
+ args.args_count = (of_graph_get_endpoint_count(node) > 1);
+
+ /*
+ * FIXME
+ *
+ * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
+ * If user unbinded CPU or Codec driver, but not for Sound Card,
+ * dlc->dai_name is keeping unbinded CPU or Codec
+ * driver's pointer.
+ *
+ * If user re-bind CPU or Codec driver again, ALSA SoC will try
+ * to rebind Card via snd_soc_try_rebind_card(), but because of
+ * above reason, it might can't bind Sound Card.
+ * Because Sound Card is pointing to released dai_name pointer.
+ *
+ * To avoid this rebind Card issue,
+ * 1) It needs to alloc memory to keep dai_name eventhough
+ * CPU or Codec driver was unbinded, or
+ * 2) user need to rebind Sound Card everytime
+ * if he unbinded CPU or Codec.
+ */
+ ret = snd_soc_get_dlc(&args, dlc);
+ if (ret < 0)
+ goto err;
+
+parse_dai_end:
+ if (is_single_link)
+ *is_single_link = of_graph_get_endpoint_count(node) == 1;
+ ret = 0;
+err:
+ if (ret < 0)
+ of_node_put(node);
+
+ return simple_ret(priv, ret);
+}
+EXPORT_SYMBOL_GPL(graph_util_parse_dai);
+
+void graph_util_parse_link_direction(struct device_node *np,
+ bool *playback_only, bool *capture_only)
+{
+ bool is_playback_only = of_property_read_bool(np, "playback-only");
+ bool is_capture_only = of_property_read_bool(np, "capture-only");
+
+ if (playback_only)
+ *playback_only = is_playback_only;
+ if (capture_only)
+ *capture_only = is_capture_only;
+}
+EXPORT_SYMBOL_GPL(graph_util_parse_link_direction);
+
+static enum snd_soc_trigger_order
+__graph_util_parse_trigger_order(struct simple_util_priv *priv,
+ struct device_node *np,
+ const char *prop)
+{
+ u32 val[SND_SOC_TRIGGER_SIZE];
+ int ret;
+
+ ret = of_property_read_u32_array(np, prop, val, SND_SOC_TRIGGER_SIZE);
+ if (ret == 0) {
+ struct device *dev = simple_priv_to_dev(priv);
+ u32 order = (val[0] << 8) +
+ (val[1] << 4) +
+ (val[2]);
+
+ switch (order) {
+ case (SND_SOC_TRIGGER_LINK << 8) +
+ (SND_SOC_TRIGGER_COMPONENT << 4) +
+ (SND_SOC_TRIGGER_DAI):
+ return SND_SOC_TRIGGER_ORDER_DEFAULT;
+
+ case (SND_SOC_TRIGGER_LINK << 8) +
+ (SND_SOC_TRIGGER_DAI << 4) +
+ (SND_SOC_TRIGGER_COMPONENT):
+ return SND_SOC_TRIGGER_ORDER_LDC;
+
+ default:
+ dev_err(dev, "unsupported trigger order [0x%x]\n", order);
+ }
+ }
+
+ /* SND_SOC_TRIGGER_ORDER_MAX means error */
+ return SND_SOC_TRIGGER_ORDER_MAX;
+}
+
+void graph_util_parse_trigger_order(struct simple_util_priv *priv,
+ struct device_node *np,
+ enum snd_soc_trigger_order *trigger_start,
+ enum snd_soc_trigger_order *trigger_stop)
+{
+ static enum snd_soc_trigger_order order;
+
+ /*
+ * We can use it like below
+ *
+ * #include <dt-bindings/sound/audio-graph.h>
+ *
+ * link-trigger-order = <SND_SOC_TRIGGER_LINK
+ * SND_SOC_TRIGGER_COMPONENT
+ * SND_SOC_TRIGGER_DAI>;
+ */
+
+ order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order");
+ if (order < SND_SOC_TRIGGER_ORDER_MAX) {
+ *trigger_start = order;
+ *trigger_stop = order;
+ }
+
+ order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-start");
+ if (order < SND_SOC_TRIGGER_ORDER_MAX)
+ *trigger_start = order;
+
+ order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-stop");
+ if (order < SND_SOC_TRIGGER_ORDER_MAX)
+ *trigger_stop = order;
+
+ return;
+}
+EXPORT_SYMBOL_GPL(graph_util_parse_trigger_order);
+
+/* Module information */
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 1ded8811598e..5af6d1b308f2 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -1,586 +1,769 @@
-/*
- * ASoC simple sound card support
- *
- * Copyright (C) 2012 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ASoC simple sound card support
+//
+// Copyright (C) 2012 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/device.h>
-#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/string.h>
-#include <sound/jack.h>
#include <sound/simple_card.h>
#include <sound/soc-dai.h>
#include <sound/soc.h>
-struct simple_card_data {
- struct snd_soc_card snd_card;
- struct simple_dai_props {
- struct asoc_simple_dai cpu_dai;
- struct asoc_simple_dai codec_dai;
- unsigned int mclk_fs;
- } *dai_props;
- unsigned int mclk_fs;
- int gpio_hp_det;
- int gpio_hp_det_invert;
- int gpio_mic_det;
- int gpio_mic_det_invert;
- struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
+#define DPCM_SELECTABLE 1
+
+#define DAI "sound-dai"
+#define CELL "#sound-dai-cells"
+#define PREFIX "simple-audio-card,"
+
+static const struct snd_soc_ops simple_ops = {
+ .startup = simple_util_startup,
+ .shutdown = simple_util_shutdown,
+ .hw_params = simple_util_hw_params,
};
-#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
-#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
-#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
+#define simple_ret(priv, ret) _simple_ret(priv, __func__, ret)
+static inline int _simple_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
-static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
+static int simple_parse_platform(struct simple_util_priv *priv,
+ struct device_node *node,
+ struct snd_soc_dai_link_component *dlc)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props =
- &priv->dai_props[rtd->num];
+ struct of_phandle_args args;
int ret;
- ret = clk_prepare_enable(dai_props->cpu_dai.clk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(dai_props->codec_dai.clk);
- if (ret)
- clk_disable_unprepare(dai_props->cpu_dai.clk);
+ if (!node)
+ return 0;
- return ret;
-}
+ /*
+ * Get node via "sound-dai = <&phandle port>"
+ * it will be used as xxx_of_node on soc_bind_dai_link()
+ */
+ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
+ if (ret)
+ return simple_ret(priv, ret);
-static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props =
- &priv->dai_props[rtd->num];
+ /* dai_name is not required and may not exist for plat component */
- clk_disable_unprepare(dai_props->cpu_dai.clk);
+ dlc->of_node = args.np;
- clk_disable_unprepare(dai_props->codec_dai.clk);
+ return 0;
}
-static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int simple_parse_dai(struct simple_util_priv *priv,
+ struct device_node *node,
+ struct snd_soc_dai_link_component *dlc,
+ int *is_single_link)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props = &priv->dai_props[rtd->num];
- unsigned int mclk, mclk_fs = 0;
- int ret = 0;
+ struct device *dev = simple_priv_to_dev(priv);
+ struct of_phandle_args args;
+ struct snd_soc_dai *dai;
+ int ret;
- if (priv->mclk_fs)
- mclk_fs = priv->mclk_fs;
- else if (dai_props->mclk_fs)
- mclk_fs = dai_props->mclk_fs;
+ if (!node)
+ return 0;
- if (mclk_fs) {
- mclk = params_rate(params) * mclk_fs;
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
- SND_SOC_CLOCK_IN);
- if (ret && ret != -ENOTSUPP)
- goto err;
+ /*
+ * Get node via "sound-dai = <&phandle port>"
+ * it will be used as xxx_of_node on soc_bind_dai_link()
+ */
+ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
+ if (ret)
+ goto end;
- ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
- SND_SOC_CLOCK_OUT);
- if (ret && ret != -ENOTSUPP)
- goto err;
+ /*
+ * Try to find from DAI args
+ */
+ dai = snd_soc_get_dai_via_args(&args);
+ if (dai) {
+ ret = -ENOMEM;
+ dlc->dai_name = snd_soc_dai_name_get(dai);
+ dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
+ if (!dlc->dai_args)
+ goto end;
+
+ goto parse_dai_end;
}
-err:
- return ret;
-}
+ /*
+ * FIXME
+ *
+ * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
+ * If user unbinded CPU or Codec driver, but not for Sound Card,
+ * dlc->dai_name is keeping unbinded CPU or Codec
+ * driver's pointer.
+ *
+ * If user re-bind CPU or Codec driver again, ALSA SoC will try
+ * to rebind Card via snd_soc_try_rebind_card(), but because of
+ * above reason, it might can't bind Sound Card.
+ * Because Sound Card is pointing to released dai_name pointer.
+ *
+ * To avoid this rebind Card issue,
+ * 1) It needs to alloc memory to keep dai_name eventhough
+ * CPU or Codec driver was unbinded, or
+ * 2) user need to rebind Sound Card everytime
+ * if he unbinded CPU or Codec.
+ */
+ ret = snd_soc_get_dlc(&args, dlc);
+ if (ret < 0)
+ goto end;
-static struct snd_soc_ops asoc_simple_card_ops = {
- .startup = asoc_simple_card_startup,
- .shutdown = asoc_simple_card_shutdown,
- .hw_params = asoc_simple_card_hw_params,
-};
+parse_dai_end:
+ if (is_single_link)
+ *is_single_link = !args.args_count;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
-static struct snd_soc_jack simple_card_hp_jack;
-static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
- {
- .pin = "Headphones",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
- .name = "Headphone detection",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 150,
-};
+static void simple_parse_convert(struct device *dev,
+ struct device_node *np,
+ struct simple_util_data *adata)
+{
+ struct device_node *top = dev->of_node;
+ struct device_node *node __free(device_node) = of_get_parent(np);
-static struct snd_soc_jack simple_card_mic_jack;
-static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
- .name = "Mic detection",
- .report = SND_JACK_MICROPHONE,
- .debounce_time = 150,
-};
+ simple_util_parse_convert(top, PREFIX, adata);
+ simple_util_parse_convert(node, PREFIX, adata);
+ simple_util_parse_convert(node, NULL, adata);
+ simple_util_parse_convert(np, NULL, adata);
+}
-static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
- struct asoc_simple_dai *set)
+static int simple_parse_node(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct link_info *li,
+ char *prefix,
+ int *cpu)
{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct snd_soc_dai_link_component *dlc;
+ struct simple_util_dai *dai;
int ret;
- if (set->sysclk) {
- ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dai->dev, "simple-card: set_sysclk error\n");
- goto err;
- }
+ if (cpu) {
+ dlc = snd_soc_link_to_cpu(dai_link, 0);
+ dai = simple_props_to_dai_cpu(dai_props, 0);
+ } else {
+ dlc = snd_soc_link_to_codec(dai_link, 0);
+ dai = simple_props_to_dai_codec(dai_props, 0);
}
- if (set->slots) {
- ret = snd_soc_dai_set_tdm_slot(dai,
- set->tx_slot_mask,
- set->rx_slot_mask,
- set->slots,
- set->slot_width);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
- goto err;
- }
- }
+ ret = simple_parse_dai(priv, np, dlc, cpu);
+ if (ret)
+ goto end;
- ret = 0;
+ ret = simple_util_parse_clk(dev, np, dai, dlc);
+ if (ret)
+ goto end;
-err:
- return ret;
+ ret = simple_util_parse_tdm(np, dai);
+end:
+ return simple_ret(priv, ret);
}
-static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+static int simple_link_init(struct simple_util_priv *priv,
+ struct device_node *cpu,
+ struct device_node *codec,
+ struct link_info *li,
+ char *prefix, char *name)
{
- struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_dai *codec = rtd->codec_dai;
- struct snd_soc_dai *cpu = rtd->cpu_dai;
- struct simple_dai_props *dai_props;
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *top = dev->of_node;
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct device_node *node __free(device_node) = of_get_parent(cpu);
+ enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;
+ enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT;
+ bool playback_only = 0, capture_only = 0;
int ret;
- dai_props = &priv->dai_props[rtd->num];
- ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
+ ret = simple_util_parse_daifmt(dev, node, codec,
+ prefix, &dai_link->dai_fmt);
if (ret < 0)
- return ret;
-
- ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai);
- if (ret < 0)
- return ret;
-
- if (gpio_is_valid(priv->gpio_hp_det)) {
- snd_soc_card_jack_new(rtd->card, "Headphones",
- SND_JACK_HEADPHONE,
- &simple_card_hp_jack,
- simple_card_hp_jack_pins,
- ARRAY_SIZE(simple_card_hp_jack_pins));
-
- simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
- simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
- snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
- &simple_card_hp_jack_gpio);
- }
-
- if (gpio_is_valid(priv->gpio_mic_det)) {
- snd_soc_card_jack_new(rtd->card, "Mic Jack",
- SND_JACK_MICROPHONE,
- &simple_card_mic_jack,
- simple_card_mic_jack_pins,
- ARRAY_SIZE(simple_card_mic_jack_pins));
- simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
- simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
- snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
- &simple_card_mic_jack_gpio);
- }
- return 0;
+ goto end;
+
+ graph_util_parse_link_direction(top, &playback_only, &capture_only);
+ graph_util_parse_link_direction(node, &playback_only, &capture_only);
+ graph_util_parse_link_direction(cpu, &playback_only, &capture_only);
+ graph_util_parse_link_direction(codec, &playback_only, &capture_only);
+
+ of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(node, PREFIX "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(cpu, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(cpu, PREFIX "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(codec, "mclk-fs", &dai_props->mclk_fs);
+ of_property_read_u32(codec, PREFIX "mclk-fs", &dai_props->mclk_fs);
+
+ graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, node, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, cpu, &trigger_start, &trigger_stop);
+ graph_util_parse_trigger_order(priv, codec, &trigger_start, &trigger_stop);
+
+ dai_link->playback_only = playback_only;
+ dai_link->capture_only = capture_only;
+
+ dai_link->trigger_start = trigger_start;
+ dai_link->trigger_stop = trigger_stop;
+
+ dai_link->init = simple_util_dai_init;
+ dai_link->ops = &simple_ops;
+
+ ret = simple_util_set_dailink_name(priv, dai_link, name);
+end:
+ return simple_ret(priv, ret);
}
-static int
-asoc_simple_card_sub_parse_of(struct device_node *np,
- struct asoc_simple_dai *dai,
- struct device_node **p_node,
- const char **name,
- int *args_count)
+static int simple_dai_link_of_dpcm(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li,
+ bool is_top)
{
- struct of_phandle_args args;
- struct clk *clk;
- u32 val;
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct device_node *top = dev->of_node;
+ struct device_node *node __free(device_node) = of_get_parent(np);
+ char *prefix = "";
+ char dai_name[64];
int ret;
- /*
- * Get node via "sound-dai = <&phandle port>"
- * it will be used as xxx_of_node on soc_bind_dai_link()
- */
- ret = of_parse_phandle_with_args(np, "sound-dai",
- "#sound-dai-cells", 0, &args);
- if (ret)
- return ret;
+ dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
- *p_node = args.np;
+ /* For single DAI link & old style of DT node */
+ if (is_top)
+ prefix = PREFIX;
- if (args_count)
- *args_count = args.args_count;
+ if (li->cpu) {
+ struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
+ struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
+ int is_single_links = 0;
- /* Get dai->name */
- ret = snd_soc_of_get_dai_name(np, name);
- if (ret < 0)
- return ret;
+ /* Codec is dummy */
- /* Parse TDM slot */
- ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
- &dai->rx_slot_mask,
- &dai->slots, &dai->slot_width);
- if (ret)
- return ret;
+ /* FE settings */
+ dai_link->dynamic = 1;
+ dai_link->dpcm_merged_format = 1;
- /*
- * Parse dai->sysclk come from "clocks = <&xxx>"
- * (if system has common clock)
- * or "system-clock-frequency = <xxx>"
- * or device's module clock.
- */
- if (of_property_read_bool(np, "clocks")) {
- clk = of_clk_get(np, 0);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- return ret;
- }
+ ret = simple_parse_node(priv, np, li, prefix, &is_single_links);
+ if (ret < 0)
+ goto out_put_node;
- dai->sysclk = clk_get_rate(clk);
- dai->clk = clk;
- } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
- dai->sysclk = val;
+ snprintf(dai_name, sizeof(dai_name), "fe.%s", cpus->dai_name);
+
+ simple_util_canonicalize_cpu(cpus, is_single_links);
+ simple_util_canonicalize_platform(platforms, cpus);
} else {
- clk = of_clk_get(args.np, 0);
- if (!IS_ERR(clk))
- dai->sysclk = clk_get_rate(clk);
- }
+ struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
+ struct snd_soc_codec_conf *cconf;
- return 0;
-}
+ /* CPU is dummy */
-static int asoc_simple_card_parse_daifmt(struct device_node *node,
- struct simple_card_data *priv,
- struct device_node *codec,
- char *prefix, int idx)
-{
- struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
- struct device *dev = simple_priv_to_dev(priv);
- struct device_node *bitclkmaster = NULL;
- struct device_node *framemaster = NULL;
- unsigned int daifmt;
+ /* BE settings */
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
- daifmt = snd_soc_of_parse_daifmt(node, prefix,
- &bitclkmaster, &framemaster);
- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+ cconf = simple_props_to_codec_conf(dai_props, 0);
- if (strlen(prefix) && !bitclkmaster && !framemaster) {
- /*
- * No dai-link level and master setting was not found from
- * sound node level, revert back to legacy DT parsing and
- * take the settings from codec node.
- */
- dev_dbg(dev, "Revert to legacy daifmt parsing\n");
+ ret = simple_parse_node(priv, np, li, prefix, NULL);
+ if (ret < 0)
+ goto out_put_node;
- daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
- (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
- } else {
- if (codec == bitclkmaster)
- daifmt |= (codec == framemaster) ?
- SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
- else
- daifmt |= (codec == framemaster) ?
- SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+ snprintf(dai_name, sizeof(dai_name), "be.%s", codecs->dai_name);
+
+ /* check "prefix" from top node */
+ snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
+ PREFIX "prefix");
+ snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node,
+ "prefix");
+ snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node,
+ "prefix");
}
- dai_link->dai_fmt = daifmt;
+ simple_parse_convert(dev, np, &dai_props->adata);
- of_node_put(bitclkmaster);
- of_node_put(framemaster);
+ ret = simple_link_init(priv, np, codec, li, prefix, dai_name);
- return 0;
+out_put_node:
+ li->link++;
+
+ return simple_ret(priv, ret);
}
-static int asoc_simple_card_dai_link_of(struct device_node *node,
- struct simple_card_data *priv,
- int idx,
- bool is_top_level_node)
+static int simple_dai_link_of(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li,
+ bool is_top)
{
struct device *dev = simple_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
+ struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
+ struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
struct device_node *cpu = NULL;
- struct device_node *plat = NULL;
- struct device_node *codec = NULL;
- char *name;
+ char dai_name[64];
char prop[128];
char *prefix = "";
- int ret, cpu_args;
- u32 val;
+ int ret, single_cpu = 0;
- /* For single DAI link & old style of DT node */
- if (is_top_level_node)
- prefix = "simple-audio-card,";
-
- snprintf(prop, sizeof(prop), "%scpu", prefix);
- cpu = of_get_child_by_name(node, prop);
+ cpu = np;
+ struct device_node *node __free(device_node) = of_get_parent(np);
- snprintf(prop, sizeof(prop), "%splat", prefix);
- plat = of_get_child_by_name(node, prop);
+ dev_dbg(dev, "link_of (%pOF)\n", node);
- snprintf(prop, sizeof(prop), "%scodec", prefix);
- codec = of_get_child_by_name(node, prop);
+ /* For single DAI link & old style of DT node */
+ if (is_top)
+ prefix = PREFIX;
- if (!cpu || !codec) {
- ret = -EINVAL;
- dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
- goto dai_link_of_err;
- }
+ snprintf(prop, sizeof(prop), "%splat", prefix);
+ struct device_node *plat __free(device_node) = of_get_child_by_name(node, prop);
- ret = asoc_simple_card_parse_daifmt(node, priv,
- codec, prefix, idx);
+ ret = simple_parse_node(priv, cpu, li, prefix, &single_cpu);
if (ret < 0)
goto dai_link_of_err;
- if (!of_property_read_u32(node, "mclk-fs", &val))
- dai_props->mclk_fs = val;
-
- ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
- &dai_link->cpu_of_node,
- &dai_link->cpu_dai_name,
- &cpu_args);
+ ret = simple_parse_node(priv, codec, li, prefix, NULL);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
- &dai_link->codec_of_node,
- &dai_link->codec_dai_name, NULL);
+ ret = simple_parse_platform(priv, plat, platforms);
if (ret < 0)
goto dai_link_of_err;
- if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
- ret = -EINVAL;
- goto dai_link_of_err;
- }
+ snprintf(dai_name, sizeof(dai_name),
+ "%s-%s", cpus->dai_name, codecs->dai_name);
- if (plat) {
- struct of_phandle_args args;
+ simple_util_canonicalize_cpu(cpus, single_cpu);
+ simple_util_canonicalize_platform(platforms, cpus);
- ret = of_parse_phandle_with_args(plat, "sound-dai",
- "#sound-dai-cells", 0, &args);
- dai_link->platform_of_node = args.np;
- } else {
- /* Assumes platform == cpu */
- dai_link->platform_of_node = dai_link->cpu_of_node;
- }
+ ret = simple_link_init(priv, cpu, codec, li, prefix, dai_name);
- /* DAI link name is created from CPU/CODEC dai name */
- name = devm_kzalloc(dev,
- strlen(dai_link->cpu_dai_name) +
- strlen(dai_link->codec_dai_name) + 2,
- GFP_KERNEL);
- if (!name) {
- ret = -ENOMEM;
- goto dai_link_of_err;
+dai_link_of_err:
+ li->link++;
+
+ return simple_ret(priv, ret);
+}
+
+static int __simple_for_each_link(struct simple_util_priv *priv,
+ struct link_info *li,
+ int (*func_noml)(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li, bool is_top),
+ int (*func_dpcm)(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li, bool is_top))
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *top = dev->of_node;
+ struct device_node *node;
+ uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev);
+ bool is_top = 0;
+ int ret = 0;
+
+ /* Check if it has dai-link */
+ node = of_get_child_by_name(top, PREFIX "dai-link");
+ if (!node) {
+ node = of_node_get(top);
+ is_top = 1;
}
- sprintf(name, "%s-%s", dai_link->cpu_dai_name,
- dai_link->codec_dai_name);
- dai_link->name = dai_link->stream_name = name;
- dai_link->ops = &asoc_simple_card_ops;
- dai_link->init = asoc_simple_card_dai_init;
-
- dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
- dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
- dev_dbg(dev, "\tcpu : %s / %d\n",
- dai_link->cpu_dai_name,
- dai_props->cpu_dai.sysclk);
- dev_dbg(dev, "\tcodec : %s / %d\n",
- dai_link->codec_dai_name,
- dai_props->codec_dai.sysclk);
+ struct device_node *add_devs __free(device_node) = of_get_child_by_name(top, PREFIX "additional-devs");
+
+ /* loop for all dai-link */
+ do {
+ struct simple_util_data adata;
+ int num = of_get_child_count(node);
+
+ /* Skip additional-devs node */
+ if (node == add_devs) {
+ node = of_get_next_child(top, node);
+ continue;
+ }
+
+ /* get codec */
+ struct device_node *codec __free(device_node) =
+ of_get_child_by_name(node, is_top ? PREFIX "codec" : "codec");
+ if (!codec) {
+ ret = -ENODEV;
+ goto error;
+ }
+ /* get platform */
+ struct device_node *plat __free(device_node) =
+ of_get_child_by_name(node, is_top ? PREFIX "plat" : "plat");
+
+ /* get convert-xxx property */
+ memset(&adata, 0, sizeof(adata));
+ for_each_child_of_node_scoped(node, np) {
+ if (np == add_devs)
+ continue;
+ simple_parse_convert(dev, np, &adata);
+ }
+
+ /* loop for all CPU/Codec node */
+ for_each_child_of_node_scoped(node, np) {
+ if (plat == np || add_devs == np)
+ continue;
+ /*
+ * It is DPCM
+ * if it has many CPUs,
+ * or has convert-xxx property
+ */
+ if (dpcm_selectable &&
+ (num > 2 || simple_util_is_convert_required(&adata))) {
+ /*
+ * np
+ * |1(CPU)|0(Codec) li->cpu
+ * CPU |Pass |return
+ * Codec |return|Pass
+ */
+ if (li->cpu != (np == codec))
+ ret = func_dpcm(priv, np, codec, li, is_top);
+ /* else normal sound */
+ } else {
+ /*
+ * np
+ * |1(CPU)|0(Codec) li->cpu
+ * CPU |Pass |return
+ * Codec |return|return
+ */
+ if (li->cpu && (np != codec))
+ ret = func_noml(priv, np, codec, li, is_top);
+ }
+
+ if (ret < 0)
+ goto error;
+ }
+
+ node = of_get_next_child(top, node);
+ } while (!is_top && node);
+error:
+ of_node_put(node);
+
+ return simple_ret(priv, ret);
+}
+
+static int simple_for_each_link(struct simple_util_priv *priv,
+ struct link_info *li,
+ int (*func_noml)(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li, bool is_top),
+ int (*func_dpcm)(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li, bool is_top))
+{
+ int ret;
/*
- * In soc_bind_dai_link() will check cpu name after
- * of_node matching if dai_link has cpu_dai_name.
- * but, it will never match if name was created by
- * fmt_single_name() remove cpu_dai_name if cpu_args
- * was 0. See:
- * fmt_single_name()
- * fmt_multiple_name()
+ * Detect all CPU first, and Detect all Codec 2nd.
+ *
+ * In Normal sound case, all DAIs are detected
+ * as "CPU-Codec".
+ *
+ * In DPCM sound case,
+ * all CPUs are detected as "CPU-dummy", and
+ * all Codecs are detected as "dummy-Codec".
+ * To avoid random sub-device numbering,
+ * detect "dummy-Codec" in last;
*/
- if (!cpu_args)
- dai_link->cpu_dai_name = NULL;
+ for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
+ ret = __simple_for_each_link(priv, li, func_noml, func_dpcm);
+ if (ret < 0)
+ break;
+ }
-dai_link_of_err:
- of_node_put(cpu);
- of_node_put(codec);
+ return simple_ret(priv, ret);
+}
+
+static void simple_depopulate_aux(void *data)
+{
+ struct simple_util_priv *priv = data;
- return ret;
+ of_platform_depopulate(simple_priv_to_dev(priv));
}
-static int asoc_simple_card_parse_of(struct device_node *node,
- struct simple_card_data *priv)
+static int simple_populate_aux(struct simple_util_priv *priv)
{
struct device *dev = simple_priv_to_dev(priv);
- enum of_gpio_flags flags;
- u32 val;
+ struct device_node *node __free(device_node) = of_get_child_by_name(dev->of_node, PREFIX "additional-devs");
int ret;
if (!node)
- return -EINVAL;
+ return 0;
- /* Parse the card name from DT */
- snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret)
+ goto end;
- /* The off-codec widgets */
- if (of_property_read_bool(node, "simple-audio-card,widgets")) {
- ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
- "simple-audio-card,widgets");
- if (ret)
- return ret;
- }
+ ret = devm_add_action_or_reset(dev, simple_depopulate_aux, priv);
+end:
+ return simple_ret(priv, ret);
+}
- /* DAPM routes */
- if (of_property_read_bool(node, "simple-audio-card,routing")) {
- ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
- "simple-audio-card,routing");
- if (ret)
- return ret;
- }
+static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ int ret;
- /* Factor to mclk, used in hw_params() */
- ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
- if (ret == 0)
- priv->mclk_fs = val;
+ ret = simple_util_parse_widgets(card, PREFIX);
+ if (ret < 0)
+ goto end;
- dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
- priv->snd_card.name : "");
+ ret = simple_util_parse_routing(card, PREFIX);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_pin_switches(card, PREFIX);
+ if (ret < 0)
+ goto end;
/* Single/Muti DAI link(s) & New style of DT node */
- if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
- struct device_node *np = NULL;
- int i = 0;
-
- for_each_child_of_node(node, np) {
- dev_dbg(dev, "\tlink %d:\n", i);
- ret = asoc_simple_card_dai_link_of(np, priv,
- i, false);
- if (ret < 0) {
- of_node_put(np);
- return ret;
- }
- i++;
- }
+ memset(li, 0, sizeof(*li));
+ ret = simple_for_each_link(priv, li,
+ simple_dai_link_of,
+ simple_dai_link_of_dpcm);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_parse_card_name(priv, PREFIX);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_populate_aux(priv);
+ if (ret < 0)
+ goto end;
+
+ ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs");
+end:
+ return simple_ret(priv, ret);
+}
+
+static int simple_count_noml(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li, bool is_top)
+{
+ int ret = -EINVAL;
+
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
+
+ /*
+ * DON'T REMOVE platforms
+ *
+ * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
+ * are different Component, but are sharing same component->dev.
+ * Simple Card had been supported it without special Platform selection.
+ * We need platforms here.
+ *
+ * In case of no Platform, it will be Platform == CPU, but Platform will be
+ * ignored by snd_soc_rtd_add_component().
+ *
+ * see
+ * simple-card-utils.c :: simple_util_canonicalize_platform()
+ */
+ li->num[li->link].cpus = 1;
+ li->num[li->link].platforms = 1;
+
+ li->num[li->link].codecs = 1;
+
+ li->link += 1;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
+
+static int simple_count_dpcm(struct simple_util_priv *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_info *li, bool is_top)
+{
+ int ret = -EINVAL;
+
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
+
+ if (li->cpu) {
+ /*
+ * DON'T REMOVE platforms
+ * see
+ * simple_count_noml()
+ */
+ li->num[li->link].cpus = 1;
+ li->num[li->link].platforms = 1;
+
+ li->link++; /* CPU-dummy */
} else {
- /* For single DAI link & old style of DT node */
- ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
- if (ret < 0)
- return ret;
+ li->num[li->link].codecs = 1;
+
+ li->link++; /* dummy-Codec */
}
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
+}
- priv->gpio_hp_det = of_get_named_gpio_flags(node,
- "simple-audio-card,hp-det-gpio", 0, &flags);
- priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
- if (priv->gpio_hp_det == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+static int simple_get_dais_count(struct simple_util_priv *priv,
+ struct link_info *li)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *top = dev->of_node;
- priv->gpio_mic_det = of_get_named_gpio_flags(node,
- "simple-audio-card,mic-det-gpio", 0, &flags);
- priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
- if (priv->gpio_mic_det == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ /*
+ * link_num : number of links.
+ * CPU-Codec / CPU-dummy / dummy-Codec
+ * dais_num : number of DAIs
+ * ccnf_num : number of codec_conf
+ * same number for "dummy-Codec"
+ *
+ * ex1)
+ * CPU0 --- Codec0 link : 5
+ * CPU1 --- Codec1 dais : 7
+ * CPU2 -/ ccnf : 1
+ * CPU3 --- Codec2
+ *
+ * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
+ * => 7 DAIs = 4xCPU + 3xCodec
+ * => 1 ccnf = 1xdummy-Codec
+ *
+ * ex2)
+ * CPU0 --- Codec0 link : 5
+ * CPU1 --- Codec1 dais : 6
+ * CPU2 -/ ccnf : 1
+ * CPU3 -/
+ *
+ * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
+ * => 6 DAIs = 4xCPU + 2xCodec
+ * => 1 ccnf = 1xdummy-Codec
+ *
+ * ex3)
+ * CPU0 --- Codec0 link : 6
+ * CPU1 -/ dais : 6
+ * CPU2 --- Codec1 ccnf : 2
+ * CPU3 -/
+ *
+ * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
+ * => 6 DAIs = 4xCPU + 2xCodec
+ * => 2 ccnf = 2xdummy-Codec
+ *
+ * ex4)
+ * CPU0 --- Codec0 (convert-rate) link : 3
+ * CPU1 --- Codec1 dais : 4
+ * ccnf : 1
+ *
+ * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
+ * => 4 DAIs = 2xCPU + 2xCodec
+ * => 1 ccnf = 1xdummy-Codec
+ */
+ if (!top) {
+ li->num[0].cpus = 1;
+ li->num[0].codecs = 1;
+ li->num[0].platforms = 1;
- if (!priv->snd_card.name)
- priv->snd_card.name = priv->snd_card.dai_link->name;
+ li->link = 1;
+ return 0;
+ }
- return 0;
+ return simple_for_each_link(priv, li,
+ simple_count_noml,
+ simple_count_dpcm);
}
-/* Decrease the reference count of the device nodes */
-static int asoc_simple_card_unref(struct snd_soc_card *card)
+static int simple_soc_probe(struct snd_soc_card *card)
{
- struct snd_soc_dai_link *dai_link;
- int num_links;
-
- for (num_links = 0, dai_link = card->dai_link;
- num_links < card->num_links;
- num_links++, dai_link++) {
- of_node_put(dai_link->cpu_of_node);
- of_node_put(dai_link->codec_of_node);
- }
- return 0;
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = simple_util_init_hp(card, &priv->hp_jack, PREFIX);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_init_mic(card, &priv->mic_jack, PREFIX);
+ if (ret < 0)
+ goto end;
+
+ ret = simple_util_init_aux_jacks(priv, PREFIX);
+end:
+ return simple_ret(priv, ret);
}
-static int asoc_simple_card_probe(struct platform_device *pdev)
+static int simple_probe(struct platform_device *pdev)
{
- struct simple_card_data *priv;
- struct snd_soc_dai_link *dai_link;
- struct device_node *np = pdev->dev.of_node;
+ struct simple_util_priv *priv;
struct device *dev = &pdev->dev;
- int num_links, ret;
-
- /* Get the number of DAI links */
- if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
- num_links = of_get_child_count(np);
- else
- num_links = 1;
+ struct device_node *np = dev->of_node;
+ struct snd_soc_card *card;
+ int ret;
/* Allocate the private data and the DAI link array */
- priv = devm_kzalloc(dev,
- sizeof(*priv) + sizeof(*dai_link) * num_links,
- GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- /* Init snd_soc_card */
- priv->snd_card.owner = THIS_MODULE;
- priv->snd_card.dev = dev;
- dai_link = priv->dai_link;
- priv->snd_card.dai_link = dai_link;
- priv->snd_card.num_links = num_links;
-
- priv->gpio_hp_det = -ENOENT;
- priv->gpio_mic_det = -ENOENT;
-
- /* Get room for the other properties */
- priv->dai_props = devm_kzalloc(dev,
- sizeof(*priv->dai_props) * num_links,
- GFP_KERNEL);
- if (!priv->dai_props)
- return -ENOMEM;
+ card = simple_priv_to_card(priv);
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+ card->probe = simple_soc_probe;
+ card->driver_name = "simple-card";
+
+ ret = -ENOMEM;
+ struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
+ if (!li)
+ goto end;
+
+ ret = simple_get_dais_count(priv, li);
+ if (ret < 0)
+ goto end;
+
+ ret = -EINVAL;
+ if (!li->link)
+ goto end;
+
+ ret = simple_util_init_priv(priv, li);
+ if (ret < 0)
+ goto end;
if (np && of_device_is_available(np)) {
- ret = asoc_simple_card_parse_of(np, priv);
+ ret = simple_parse_of(priv, li);
if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "parse error %d\n", ret);
+ dev_err_probe(dev, ret, "parse error\n");
goto err;
}
} else {
- struct asoc_simple_card_info *cinfo;
+ struct simple_util_info *cinfo;
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dai_link = priv->dai_link;
+ struct simple_dai_props *dai_props = priv->dai_props;
+
+ ret = -EINVAL;
cinfo = dev->platform_data;
if (!cinfo) {
dev_err(dev, "no info for asoc-simple-card\n");
- return -EINVAL;
+ goto err;
}
if (!cinfo->name ||
@@ -588,70 +771,67 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
!cinfo->codec ||
!cinfo->platform ||
!cinfo->cpu_dai.name) {
- dev_err(dev, "insufficient asoc_simple_card_info settings\n");
- return -EINVAL;
+ dev_err(dev, "insufficient simple_util_info settings\n");
+ goto err;
}
- priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name;
+ cpus = dai_link->cpus;
+ cpus->dai_name = cinfo->cpu_dai.name;
+
+ codecs = dai_link->codecs;
+ codecs->name = cinfo->codec;
+ codecs->dai_name = cinfo->codec_dai.name;
+
+ platform = dai_link->platforms;
+ platform->name = cinfo->platform;
+
+ card->name = (cinfo->card) ? cinfo->card : cinfo->name;
dai_link->name = cinfo->name;
dai_link->stream_name = cinfo->name;
- dai_link->platform_name = cinfo->platform;
- dai_link->codec_name = cinfo->codec;
- dai_link->cpu_dai_name = cinfo->cpu_dai.name;
- dai_link->codec_dai_name = cinfo->codec_dai.name;
dai_link->dai_fmt = cinfo->daifmt;
- dai_link->init = asoc_simple_card_dai_init;
- memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
- sizeof(priv->dai_props->cpu_dai));
- memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
- sizeof(priv->dai_props->codec_dai));
-
+ dai_link->init = simple_util_dai_init;
+ memcpy(dai_props->cpu_dai, &cinfo->cpu_dai,
+ sizeof(*dai_props->cpu_dai));
+ memcpy(dai_props->codec_dai, &cinfo->codec_dai,
+ sizeof(*dai_props->codec_dai));
}
- snd_soc_card_set_drvdata(&priv->snd_card, priv);
+ snd_soc_card_set_drvdata(card, priv);
- ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
- if (ret >= 0)
- return ret;
+ simple_util_debug_info(priv);
-err:
- asoc_simple_card_unref(&priv->snd_card);
- return ret;
-}
-
-static int asoc_simple_card_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
-
- if (gpio_is_valid(priv->gpio_hp_det))
- snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
- &simple_card_hp_jack_gpio);
- if (gpio_is_valid(priv->gpio_mic_det))
- snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
- &simple_card_mic_jack_gpio);
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0)
+ goto err;
- return asoc_simple_card_unref(card);
+ return 0;
+err:
+ simple_util_clean_reference(card);
+end:
+ return dev_err_probe(dev, ret, "parse error\n");
}
-static const struct of_device_id asoc_simple_of_match[] = {
+static const struct of_device_id simple_of_match[] = {
{ .compatible = "simple-audio-card", },
+ { .compatible = "simple-scu-audio-card",
+ .data = (void *)DPCM_SELECTABLE },
{},
};
-MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
+MODULE_DEVICE_TABLE(of, simple_of_match);
-static struct platform_driver asoc_simple_card = {
+static struct platform_driver simple_card = {
.driver = {
.name = "asoc-simple-card",
- .of_match_table = asoc_simple_of_match,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = simple_of_match,
},
- .probe = asoc_simple_card_probe,
- .remove = asoc_simple_card_remove,
+ .probe = simple_probe,
+ .remove = simple_util_remove,
};
-module_platform_driver(asoc_simple_card);
+module_platform_driver(simple_card);
MODULE_ALIAS("platform:asoc-simple-card");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c
new file mode 100644
index 000000000000..2e49066dedd4
--- /dev/null
+++ b/sound/soc/generic/test-component.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// test-component.c -- Test Audio Component driver
+//
+// Copyright (C) 2020 Renesas Electronics Corporation
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define TEST_NAME_LEN 32
+struct test_dai_name {
+ char name[TEST_NAME_LEN];
+ char name_playback[TEST_NAME_LEN];
+ char name_capture[TEST_NAME_LEN];
+};
+
+struct test_priv {
+ struct device *dev;
+ struct snd_pcm_substream *substream;
+ struct delayed_work dwork;
+ struct snd_soc_component_driver *component_driver;
+ struct snd_soc_dai_driver *dai_driver;
+ struct test_dai_name *name;
+};
+
+struct test_adata {
+ u32 is_cpu:1;
+ u32 cmp_v:1;
+ u32 dai_v:1;
+};
+
+#define mile_stone(d) dev_info((d)->dev, "%s() : %s", __func__, (d)->driver->name)
+#define mile_stone_x(dev) dev_info(dev, "%s()", __func__)
+
+static int test_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ unsigned int clock = fmt & SND_SOC_DAIFMT_CLOCK_MASK;
+ unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+ unsigned int master = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ char *str;
+
+ dev_info(dai->dev, "name : %s", dai->name);
+
+ str = "unknown";
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ str = "i2s";
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ str = "right_j";
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ str = "left_j";
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ str = "dsp_a";
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ str = "dsp_b";
+ break;
+ case SND_SOC_DAIFMT_AC97:
+ str = "ac97";
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ str = "pdm";
+ break;
+ }
+ dev_info(dai->dev, "format : %s", str);
+
+ if (clock == SND_SOC_DAIFMT_CONT)
+ str = "continuous";
+ else
+ str = "gated";
+ dev_info(dai->dev, "clock : %s", str);
+
+ str = "unknown";
+ switch (master) {
+ case SND_SOC_DAIFMT_BP_FP:
+ str = "clk provider, frame provider";
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ str = "clk consumer, frame provider";
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ str = "clk provider, frame consumer";
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ str = "clk consumer, frame consumer";
+ break;
+ }
+ dev_info(dai->dev, "clock : codec is %s", str);
+
+ str = "unknown";
+ switch (inv) {
+ case SND_SOC_DAIFMT_NB_NF:
+ str = "normal bit, normal frame";
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ str = "normal bit, invert frame";
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ str = "invert bit, normal frame";
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ str = "invert bit, invert frame";
+ break;
+ }
+ dev_info(dai->dev, "signal : %s", str);
+
+ return 0;
+}
+
+static int test_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ dev_info(dai->dev, "set tdm slot: tx_mask=0x%08X, rx_mask=0x%08X, slots=%d, slot_width=%d\n",
+ tx_mask, rx_mask, slots, slot_width);
+ return 0;
+}
+
+static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static void test_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+}
+
+static int test_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ mile_stone(dai);
+
+ return 0;
+}
+
+static const u64 test_dai_formats =
+ /*
+ * Select below from Sound Card, not auto
+ * SND_SOC_POSSIBLE_DAIFMT_BP_FP
+ * SND_SOC_POSSIBLE_DAIFMT_BC_FP
+ * SND_SOC_POSSIBLE_DAIFMT_BP_FC
+ * SND_SOC_POSSIBLE_DAIFMT_BC_FC
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_AC97 |
+ SND_SOC_POSSIBLE_DAIFMT_PDM |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
+static const struct snd_soc_dai_ops test_ops = {
+ .set_fmt = test_dai_set_fmt,
+ .set_tdm_slot = test_dai_set_tdm_slot,
+ .startup = test_dai_startup,
+ .shutdown = test_dai_shutdown,
+ .auto_selectable_formats = &test_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static const struct snd_soc_dai_ops test_verbose_ops = {
+ .set_sysclk = test_dai_set_sysclk,
+ .set_pll = test_dai_set_pll,
+ .set_clkdiv = test_dai_set_clkdiv,
+ .set_fmt = test_dai_set_fmt,
+ .set_tdm_slot = test_dai_set_tdm_slot,
+ .mute_stream = test_dai_mute_stream,
+ .startup = test_dai_startup,
+ .shutdown = test_dai_shutdown,
+ .hw_params = test_dai_hw_params,
+ .hw_free = test_dai_hw_free,
+ .trigger = test_dai_trigger,
+ .auto_selectable_formats = &test_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_CONTINUOUS
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static int test_component_probe(struct snd_soc_component *component)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static void test_component_remove(struct snd_soc_component *component)
+{
+ mile_stone(component);
+}
+
+static int test_component_suspend(struct snd_soc_component *component)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_resume(struct snd_soc_component *component)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+#define PREALLOC_BUFFER (32 * 1024)
+static int test_component_pcm_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ mile_stone(component);
+
+ snd_pcm_set_managed_buffer_all(
+ rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER);
+
+ return 0;
+}
+
+static void test_component_pcm_destruct(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ mile_stone(component);
+}
+
+static int test_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static void test_component_seq_notifier(struct snd_soc_component *component,
+ enum snd_soc_dapm_type type, int subseq)
+{
+ mile_stone(component);
+}
+
+static int test_component_stream_event(struct snd_soc_component *component, int event)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static const struct snd_pcm_hardware test_component_hardware = {
+ /* Random values to keep userspace happy when checking constraints */
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .buffer_bytes_max = 32 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 128,
+ .fifo_size = 256,
+};
+
+static int test_component_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ mile_stone(component);
+
+ /* BE's dont need dummy params */
+ if (!rtd->dai_link->no_pcm)
+ snd_soc_set_runtime_hwparams(substream, &test_component_hardware);
+
+ return 0;
+}
+
+static int test_component_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_ioctl(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static void test_component_timer_stop(struct test_priv *priv)
+{
+ cancel_delayed_work(&priv->dwork);
+}
+
+static void test_component_timer_start(struct test_priv *priv)
+{
+ schedule_delayed_work(&priv->dwork, msecs_to_jiffies(10));
+}
+
+static void test_component_dwork(struct work_struct *work)
+{
+ struct test_priv *priv = container_of(work, struct test_priv, dwork.work);
+
+ if (priv->substream)
+ snd_pcm_period_elapsed(priv->substream);
+
+ test_component_timer_start(priv);
+}
+
+static int test_component_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct test_priv *priv = dev_get_drvdata(component->dev);
+
+ mile_stone(component);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ test_component_timer_start(priv);
+ priv->substream = substream; /* set substream later */
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ priv->substream = NULL;
+ test_component_timer_stop(priv);
+ }
+
+ return 0;
+}
+
+static int test_component_sync_stop(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t test_component_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ static int pointer;
+
+ if (!runtime)
+ return 0;
+
+ pointer += 10;
+ if (pointer > PREALLOC_BUFFER)
+ pointer = 0;
+
+ /* mile_stone(component); */
+
+ return bytes_to_frames(runtime, pointer);
+}
+
+static int test_component_get_time_info(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct timespec64 *system_ts,
+ struct timespec64 *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+ mile_stone(component);
+
+ return 0;
+}
+
+static int test_component_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ mile_stone_x(rtd->dev);
+
+ return 0;
+}
+
+/* CPU */
+static const struct test_adata test_cpu = { .is_cpu = 1, .cmp_v = 0, .dai_v = 0, };
+static const struct test_adata test_cpu_vv = { .is_cpu = 1, .cmp_v = 1, .dai_v = 1, };
+static const struct test_adata test_cpu_nv = { .is_cpu = 1, .cmp_v = 0, .dai_v = 1, };
+static const struct test_adata test_cpu_vn = { .is_cpu = 1, .cmp_v = 1, .dai_v = 0, };
+/* Codec */
+static const struct test_adata test_codec = { .is_cpu = 0, .cmp_v = 0, .dai_v = 0, };
+static const struct test_adata test_codec_vv = { .is_cpu = 0, .cmp_v = 1, .dai_v = 1, };
+static const struct test_adata test_codec_nv = { .is_cpu = 0, .cmp_v = 0, .dai_v = 1, };
+static const struct test_adata test_codec_vn = { .is_cpu = 0, .cmp_v = 1, .dai_v = 0, };
+
+static const struct of_device_id test_of_match[] = {
+ { .compatible = "test-cpu", .data = (void *)&test_cpu, },
+ { .compatible = "test-cpu-verbose", .data = (void *)&test_cpu_vv, },
+ { .compatible = "test-cpu-verbose-dai", .data = (void *)&test_cpu_nv, },
+ { .compatible = "test-cpu-verbose-component", .data = (void *)&test_cpu_vn, },
+ { .compatible = "test-codec", .data = (void *)&test_codec, },
+ { .compatible = "test-codec-verbose", .data = (void *)&test_codec_vv, },
+ { .compatible = "test-codec-verbose-dai", .data = (void *)&test_codec_nv, },
+ { .compatible = "test-codec-verbose-component", .data = (void *)&test_codec_vn, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, test_of_match);
+
+static const struct snd_soc_dapm_widget widgets[] = {
+ /*
+ * FIXME
+ *
+ * Just IN/OUT is OK for now,
+ * but need to be updated ?
+ */
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static int test_driver_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ const struct test_adata *adata = of_device_get_match_data(&pdev->dev);
+ struct snd_soc_component_driver *cdriv;
+ struct snd_soc_dai_driver *ddriv;
+ struct test_dai_name *dname;
+ struct test_priv *priv;
+ int num, ret, i;
+
+ num = of_graph_get_endpoint_count(node);
+ if (!num) {
+ dev_err(dev, "no port exits\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL);
+ ddriv = devm_kcalloc(dev, num, sizeof(*ddriv), GFP_KERNEL);
+ dname = devm_kcalloc(dev, num, sizeof(*dname), GFP_KERNEL);
+ if (!priv || !cdriv || !ddriv || !dname || !adata)
+ return -EINVAL;
+
+ priv->dev = dev;
+ priv->component_driver = cdriv;
+ priv->dai_driver = ddriv;
+ priv->name = dname;
+
+ INIT_DELAYED_WORK(&priv->dwork, test_component_dwork);
+ dev_set_drvdata(dev, priv);
+
+ if (adata->is_cpu) {
+ cdriv->name = "test_cpu";
+ cdriv->pcm_construct = test_component_pcm_construct;
+ cdriv->pointer = test_component_pointer;
+ cdriv->trigger = test_component_trigger;
+ cdriv->legacy_dai_naming = 1;
+ } else {
+ cdriv->name = "test_codec";
+ cdriv->idle_bias_on = 1;
+ cdriv->endianness = 1;
+ }
+
+ cdriv->open = test_component_open;
+ cdriv->dapm_widgets = widgets;
+ cdriv->num_dapm_widgets = ARRAY_SIZE(widgets);
+
+ if (adata->cmp_v) {
+ cdriv->probe = test_component_probe;
+ cdriv->remove = test_component_remove;
+ cdriv->suspend = test_component_suspend;
+ cdriv->resume = test_component_resume;
+ cdriv->set_sysclk = test_component_set_sysclk;
+ cdriv->set_pll = test_component_set_pll;
+ cdriv->set_jack = test_component_set_jack;
+ cdriv->seq_notifier = test_component_seq_notifier;
+ cdriv->stream_event = test_component_stream_event;
+ cdriv->set_bias_level = test_component_set_bias_level;
+ cdriv->close = test_component_close;
+ cdriv->ioctl = test_component_ioctl;
+ cdriv->hw_params = test_component_hw_params;
+ cdriv->hw_free = test_component_hw_free;
+ cdriv->prepare = test_component_prepare;
+ cdriv->sync_stop = test_component_sync_stop;
+ cdriv->get_time_info = test_component_get_time_info;
+ cdriv->be_hw_params_fixup = test_component_be_hw_params_fixup;
+
+ if (adata->is_cpu)
+ cdriv->pcm_destruct = test_component_pcm_destruct;
+ }
+
+ i = 0;
+ for_each_of_graph_port(node, port) {
+ snprintf(dname[i].name, TEST_NAME_LEN, "%s.%d", node->name, i);
+ ddriv[i].name = dname[i].name;
+
+ snprintf(dname[i].name_playback, TEST_NAME_LEN, "DAI%d Playback", i);
+ ddriv[i].playback.stream_name = dname[i].name_playback;
+ ddriv[i].playback.channels_min = 1;
+ ddriv[i].playback.channels_max = 384;
+ ddriv[i].playback.rates = STUB_RATES;
+ ddriv[i].playback.formats = STUB_FORMATS;
+
+ snprintf(dname[i].name_capture, TEST_NAME_LEN, "DAI%d Capture", i);
+ ddriv[i].capture.stream_name = dname[i].name_capture;
+ ddriv[i].capture.channels_min = 1;
+ ddriv[i].capture.channels_max = 384;
+ ddriv[i].capture.rates = STUB_RATES;
+ ddriv[i].capture.formats = STUB_FORMATS;
+
+ if (adata->dai_v)
+ ddriv[i].ops = &test_verbose_ops;
+ else
+ ddriv[i].ops = &test_ops;
+
+ i++;
+ }
+
+ ret = devm_snd_soc_register_component(dev, cdriv, ddriv, num);
+ if (ret < 0)
+ return ret;
+
+ mile_stone_x(dev);
+
+ return 0;
+}
+
+static void test_driver_remove(struct platform_device *pdev)
+{
+ mile_stone_x(&pdev->dev);
+}
+
+static struct platform_driver test_driver = {
+ .driver = {
+ .name = "test-component",
+ .of_match_table = test_of_match,
+ },
+ .probe = test_driver_probe,
+ .remove = test_driver_remove,
+};
+module_platform_driver(test_driver);
+
+MODULE_ALIAS("platform:asoc-test-component");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_DESCRIPTION("ASoC Test Component");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/google/Kconfig b/sound/soc/google/Kconfig
new file mode 100644
index 000000000000..6005653170a8
--- /dev/null
+++ b/sound/soc/google/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Google"
+
+config SND_SOC_CHV3_I2S
+ tristate "Google Chameleon v3 I2S device"
+ help
+ Enable support for the Google Chameleon v3 I2S device.
+
+endmenu
diff --git a/sound/soc/google/Makefile b/sound/soc/google/Makefile
new file mode 100644
index 000000000000..862496af1ae1
--- /dev/null
+++ b/sound/soc/google/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SND_SOC_CHV3_I2S) += chv3-i2s.o
diff --git a/sound/soc/google/chv3-i2s.c b/sound/soc/google/chv3-i2s.c
new file mode 100644
index 000000000000..0ff24653d49f
--- /dev/null
+++ b/sound/soc/google/chv3-i2s.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+/*
+ * The I2S interface consists of two ring buffers - one for RX and one for
+ * TX. A ring buffer has a producer index and a consumer index. Depending
+ * on which way the data is flowing, either the software or the hardware
+ * writes data and updates the producer index, and the other end reads data
+ * and updates the consumer index.
+ *
+ * The pointer managed by software is updated using the .ack callback
+ * (see chv3_dma_ack). This seems to be the only way to reliably obtain
+ * the appl_ptr from within the driver and pass it to hardware.
+ *
+ * Because of the two pointer design, the ring buffer can never be full. With
+ * capture this isn't a problem, because the hardware being the producer
+ * will wait for the consumer index to move out of the way. With playback,
+ * however, this is problematic, because ALSA wants to fill up the buffer
+ * completely when waiting for hardware. In the .ack callback, the driver
+ * would have to wait for the consumer index to move out of the way by
+ * busy-waiting, which would keep stalling the kernel for quite a long time.
+ *
+ * The workaround to this problem is to "lie" to ALSA that the hw_pointer
+ * is one frame behind what it actually is (see chv3_dma_pointer). This
+ * way, ALSA will not try to fill up the entire buffer, and all callbacks
+ * are wait-free.
+ */
+
+#define I2S_TX_ENABLE 0x00
+#define I2S_TX_BASE_ADDR 0x04
+#define I2S_TX_BUFFER_SIZE 0x08
+#define I2S_TX_PRODUCER_IDX 0x0c
+#define I2S_TX_CONSUMER_IDX 0x10
+#define I2S_RX_ENABLE 0x14
+#define I2S_RX_BASE_ADDR 0x18
+#define I2S_RX_BUFFER_SIZE 0x1c
+#define I2S_RX_PRODUCER_IDX 0x20
+#define I2S_RX_CONSUMER_IDX 0x24
+
+#define I2S_SOFT_RESET 0x2c
+#define I2S_SOFT_RESET_RX_BIT 0x1
+#define I2S_SOFT_RESET_TX_BIT 0x2
+
+#define I2S_RX_IRQ 0x4c
+#define I2S_RX_IRQ_CONST 0x50
+#define I2S_TX_IRQ 0x54
+#define I2S_TX_IRQ_CONST 0x58
+
+#define I2S_IRQ_MASK 0x8
+#define I2S_IRQ_CLR 0xc
+#define I2S_IRQ_RX_BIT 0x1
+#define I2S_IRQ_TX_BIT 0x2
+
+#define I2S_MAX_BUFFER_SIZE 0x200000
+
+struct chv3_i2s_dev {
+ struct device *dev;
+ void __iomem *iobase;
+ void __iomem *iobase_irq;
+ struct snd_pcm_substream *rx_substream;
+ struct snd_pcm_substream *tx_substream;
+ int tx_bytes_to_fetch;
+};
+
+static struct snd_soc_dai_driver chv3_i2s_dai = {
+ .name = "chv3-i2s",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 128,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 128,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+static const struct snd_pcm_hardware chv3_dma_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .buffer_bytes_max = I2S_MAX_BUFFER_SIZE,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 4,
+ .periods_max = 256,
+};
+
+static inline void chv3_i2s_wr(struct chv3_i2s_dev *i2s, int offset, u32 val)
+{
+ writel(val, i2s->iobase + offset);
+}
+
+static inline u32 chv3_i2s_rd(struct chv3_i2s_dev *i2s, int offset)
+{
+ return readl(i2s->iobase + offset);
+}
+
+static irqreturn_t chv3_i2s_isr(int irq, void *data)
+{
+ struct chv3_i2s_dev *i2s = data;
+ u32 reg;
+
+ reg = readl(i2s->iobase_irq + I2S_IRQ_CLR);
+ if (!reg)
+ return IRQ_NONE;
+
+ if (reg & I2S_IRQ_RX_BIT)
+ snd_pcm_period_elapsed(i2s->rx_substream);
+
+ if (reg & I2S_IRQ_TX_BIT)
+ snd_pcm_period_elapsed(i2s->tx_substream);
+
+ writel(reg, i2s->iobase_irq + I2S_IRQ_CLR);
+
+ return IRQ_HANDLED;
+}
+
+static int chv3_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ int res;
+
+ snd_soc_set_runtime_hwparams(substream, &chv3_dma_hw);
+
+ res = snd_pcm_hw_constraint_pow2(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+ if (res)
+ return res;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ i2s->rx_substream = substream;
+ else
+ i2s->tx_substream = substream;
+
+ return 0;
+}
+static int chv3_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ chv3_i2s_wr(i2s, I2S_RX_ENABLE, 0);
+ else
+ chv3_i2s_wr(i2s, I2S_TX_ENABLE, 0);
+
+ return 0;
+}
+
+static int chv3_dma_pcm_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ struct snd_pcm_substream *substream;
+ int res;
+
+ substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (substream) {
+ res = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, i2s->dev,
+ I2S_MAX_BUFFER_SIZE, &substream->dma_buffer);
+ if (res)
+ return res;
+ }
+
+ substream = rtd->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (substream) {
+ res = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, i2s->dev,
+ I2S_MAX_BUFFER_SIZE, &substream->dma_buffer);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int chv3_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int chv3_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ unsigned int buffer_bytes, period_bytes, period_size;
+
+ buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ period_size = substream->runtime->period_size;
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ chv3_i2s_wr(i2s, I2S_SOFT_RESET, I2S_SOFT_RESET_RX_BIT);
+ chv3_i2s_wr(i2s, I2S_RX_BASE_ADDR, substream->dma_buffer.addr);
+ chv3_i2s_wr(i2s, I2S_RX_BUFFER_SIZE, buffer_bytes);
+ chv3_i2s_wr(i2s, I2S_RX_IRQ, (period_size << 8) | 1);
+ chv3_i2s_wr(i2s, I2S_RX_ENABLE, 1);
+ } else {
+ chv3_i2s_wr(i2s, I2S_SOFT_RESET, I2S_SOFT_RESET_TX_BIT);
+ chv3_i2s_wr(i2s, I2S_TX_BASE_ADDR, substream->dma_buffer.addr);
+ chv3_i2s_wr(i2s, I2S_TX_BUFFER_SIZE, buffer_bytes);
+ chv3_i2s_wr(i2s, I2S_TX_IRQ, ((period_bytes / i2s->tx_bytes_to_fetch) << 8) | 1);
+ chv3_i2s_wr(i2s, I2S_TX_ENABLE, 1);
+ }
+ writel(I2S_IRQ_RX_BIT | I2S_IRQ_TX_BIT, i2s->iobase_irq + I2S_IRQ_MASK);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t chv3_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ u32 frame_bytes, buffer_bytes;
+ u32 idx_bytes;
+
+ frame_bytes = substream->runtime->frame_bits * 8;
+ buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ idx_bytes = chv3_i2s_rd(i2s, I2S_RX_PRODUCER_IDX);
+ } else {
+ idx_bytes = chv3_i2s_rd(i2s, I2S_TX_CONSUMER_IDX);
+ /* lag the pointer by one frame */
+ idx_bytes = (idx_bytes - frame_bytes) & (buffer_bytes - 1);
+ }
+
+ return bytes_to_frames(substream->runtime, idx_bytes);
+}
+
+static int chv3_dma_ack(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ unsigned int bytes, idx;
+
+ bytes = frames_to_bytes(runtime, runtime->control->appl_ptr);
+ idx = bytes & (snd_pcm_lib_buffer_bytes(substream) - 1);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ chv3_i2s_wr(i2s, I2S_RX_CONSUMER_IDX, idx);
+ else
+ chv3_i2s_wr(i2s, I2S_TX_PRODUCER_IDX, idx);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver chv3_i2s_comp = {
+ .name = "chv3-i2s-comp",
+ .open = chv3_dma_open,
+ .close = chv3_dma_close,
+ .pcm_construct = chv3_dma_pcm_construct,
+ .hw_params = chv3_dma_hw_params,
+ .prepare = chv3_dma_prepare,
+ .pointer = chv3_dma_pointer,
+ .ack = chv3_dma_ack,
+};
+
+static int chv3_i2s_probe(struct platform_device *pdev)
+{
+ struct chv3_i2s_dev *i2s;
+ int res;
+ int irq;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->iobase = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(i2s->iobase))
+ return PTR_ERR(i2s->iobase);
+
+ i2s->iobase_irq = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(i2s->iobase_irq))
+ return PTR_ERR(i2s->iobase_irq);
+
+ i2s->tx_bytes_to_fetch = (chv3_i2s_rd(i2s, I2S_TX_IRQ_CONST) >> 8) & 0xffff;
+
+ i2s->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENXIO;
+ res = devm_request_irq(i2s->dev, irq, chv3_i2s_isr, 0, "chv3-i2s", i2s);
+ if (res)
+ return res;
+
+ res = devm_snd_soc_register_component(&pdev->dev, &chv3_i2s_comp,
+ &chv3_i2s_dai, 1);
+ if (res) {
+ dev_err(&pdev->dev, "couldn't register component: %d\n", res);
+ return res;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id chv3_i2s_of_match[] = {
+ { .compatible = "google,chv3-i2s" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, chv3_i2s_of_match);
+
+static struct platform_driver chv3_i2s_driver = {
+ .probe = chv3_i2s_probe,
+ .driver = {
+ .name = "chv3-i2s",
+ .of_match_table = chv3_i2s_of_match,
+ },
+};
+
+module_platform_driver(chv3_i2s_driver);
+
+MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>");
+MODULE_DESCRIPTION("Chameleon v3 I2S interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..d95da932f352
--- /dev/null
+++ b/sound/soc/hisilicon/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Hisilicon"
+
+config SND_I2S_HI6210_I2S
+ tristate "Hisilicon I2S controller"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Hisilicon I2S
+
+endmenu
diff --git a/sound/soc/hisilicon/Makefile b/sound/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..02e766378bca
--- /dev/null
+++ b/sound/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SND_I2S_HI6210_I2S) += hi6210-i2s.o
diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c
new file mode 100644
index 000000000000..250ae3781d14
--- /dev/null
+++ b/sound/soc/hisilicon/hi6210-i2s.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/sound/soc/m8m/hi6210_i2s.c - I2S IP driver
+ *
+ * Copyright (C) 2015 Linaro, Ltd
+ * Author: Andy Green <andy.green@linaro.org>
+ *
+ * This driver only deals with S2 interface (BT)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset-controller.h>
+
+#include "hi6210-i2s.h"
+
+struct hi6210_i2s {
+ struct device *dev;
+ struct reset_control *rc;
+ struct clk *clk[8];
+ int clocks;
+ struct snd_soc_dai_driver dai;
+ void __iomem *base;
+ struct regmap *sysctrl;
+ phys_addr_t base_phys;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ int clk_rate;
+ spinlock_t lock;
+ int rate;
+ int format;
+ u8 bits;
+ u8 channels;
+ u8 id;
+ u8 channel_length;
+ u8 use;
+ u32 master:1;
+ u32 status:1;
+};
+
+#define SC_PERIPH_CLKEN1 0x210
+#define SC_PERIPH_CLKDIS1 0x214
+
+#define SC_PERIPH_CLKEN3 0x230
+#define SC_PERIPH_CLKDIS3 0x234
+
+#define SC_PERIPH_CLKEN12 0x270
+#define SC_PERIPH_CLKDIS12 0x274
+
+#define SC_PERIPH_RSTEN1 0x310
+#define SC_PERIPH_RSTDIS1 0x314
+#define SC_PERIPH_RSTSTAT1 0x318
+
+#define SC_PERIPH_RSTEN2 0x320
+#define SC_PERIPH_RSTDIS2 0x324
+#define SC_PERIPH_RSTSTAT2 0x328
+
+#define SOC_PMCTRL_BBPPLLALIAS 0x48
+
+enum {
+ CLK_DACODEC,
+ CLK_I2S_BASE,
+};
+
+static inline void hi6210_write_reg(struct hi6210_i2s *i2s, int reg, u32 val)
+{
+ writel(val, i2s->base + reg);
+}
+
+static inline u32 hi6210_read_reg(struct hi6210_i2s *i2s, int reg)
+{
+ return readl(i2s->base + reg);
+}
+
+static int hi6210_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ int ret, n;
+ u32 val;
+
+ /* deassert reset on ABB */
+ regmap_read(i2s->sysctrl, SC_PERIPH_RSTSTAT2, &val);
+ if (val & BIT(4))
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS2, BIT(4));
+
+ for (n = 0; n < i2s->clocks; n++) {
+ ret = clk_prepare_enable(i2s->clk[n]);
+ if (ret)
+ goto err_unprepare_clk;
+ }
+
+ ret = clk_set_rate(i2s->clk[CLK_I2S_BASE], 49152000);
+ if (ret) {
+ dev_err(i2s->dev, "%s: setting 49.152MHz base rate failed %d\n",
+ __func__, ret);
+ goto err_unprepare_clk;
+ }
+
+ /* enable clock before frequency division */
+ regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN12, BIT(9));
+
+ /* enable codec working clock / == "codec bus clock" */
+ regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN1, BIT(5));
+
+ /* deassert reset on codec / interface clock / working clock */
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS1, BIT(5));
+
+ /* not interested in i2s irqs */
+ val = hi6210_read_reg(i2s, HII2S_CODEC_IRQ_MASK);
+ val |= 0x3f;
+ hi6210_write_reg(i2s, HII2S_CODEC_IRQ_MASK, val);
+
+
+ /* reset the stereo downlink fifo */
+ val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
+ val |= (BIT(5) | BIT(4));
+ hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
+
+ val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
+ val &= ~(BIT(5) | BIT(4));
+ hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
+ val &= ~(HII2S_SW_RST_N__ST_DL_WORDLEN_MASK <<
+ HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
+ val |= (HII2S_BITS_16 << HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
+ hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
+
+ val = hi6210_read_reg(i2s, HII2S_MISC_CFG);
+ /* mux 11/12 = APB not i2s */
+ val &= ~HII2S_MISC_CFG__ST_DL_TEST_SEL;
+ /* BT R ch 0 = mixer op of DACR ch */
+ val &= ~HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
+ val &= ~HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
+
+ val |= HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
+ /* BT L ch = 1 = mux 7 = "mixer output of DACL */
+ val |= HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
+ hi6210_write_reg(i2s, HII2S_MISC_CFG, val);
+
+ val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
+ val |= HII2S_SW_RST_N__SW_RST_N;
+ hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
+
+ return 0;
+
+err_unprepare_clk:
+ while (n--)
+ clk_disable_unprepare(i2s->clk[n]);
+ return ret;
+}
+
+static void hi6210_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ int n;
+
+ for (n = 0; n < i2s->clocks; n++)
+ clk_disable_unprepare(i2s->clk[n]);
+
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
+}
+
+static void hi6210_i2s_txctrl(struct snd_soc_dai *cpu_dai, int on)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ u32 val;
+
+ spin_lock(&i2s->lock);
+ if (on) {
+ /* enable S2 TX */
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_IF_TX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ } else {
+ /* disable S2 TX */
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_IF_TX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ }
+ spin_unlock(&i2s->lock);
+}
+
+static void hi6210_i2s_rxctrl(struct snd_soc_dai *cpu_dai, int on)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ u32 val;
+
+ spin_lock(&i2s->lock);
+ if (on) {
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_IF_RX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ } else {
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_IF_RX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ }
+ spin_unlock(&i2s->lock);
+}
+
+static int hi6210_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+
+ /*
+ * We don't actually set the hardware until the hw_params
+ * call, but we need to validate the user input here.
+ */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ case SND_SOC_DAIFMT_BP_FP:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->format = fmt;
+ i2s->master = (i2s->format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_BP_FP;
+
+ return 0;
+}
+
+static int hi6210_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ u32 bits = 0, rate = 0, signed_data = 0, fmt = 0;
+ u32 val;
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U16_LE:
+ signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits = HII2S_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_U24_LE:
+ signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits = HII2S_BITS_24;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Bad format\n");
+ return -EINVAL;
+ }
+
+
+ switch (params_rate(params)) {
+ case 8000:
+ rate = HII2S_FS_RATE_8KHZ;
+ break;
+ case 16000:
+ rate = HII2S_FS_RATE_16KHZ;
+ break;
+ case 32000:
+ rate = HII2S_FS_RATE_32KHZ;
+ break;
+ case 48000:
+ rate = HII2S_FS_RATE_48KHZ;
+ break;
+ case 96000:
+ rate = HII2S_FS_RATE_96KHZ;
+ break;
+ case 192000:
+ rate = HII2S_FS_RATE_192KHZ;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Bad rate: %d\n", params_rate(params));
+ return -EINVAL;
+ }
+
+ if (!(params_channels(params))) {
+ dev_err(cpu_dai->dev, "Bad channels\n");
+ return -EINVAL;
+ }
+
+ dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (bits) {
+ case HII2S_BITS_24:
+ i2s->bits = 32;
+ dma_data->addr_width = 3;
+ break;
+ default:
+ i2s->bits = 16;
+ dma_data->addr_width = 2;
+ break;
+ }
+ i2s->rate = params_rate(params);
+ i2s->channels = params_channels(params);
+ i2s->channel_length = i2s->channels * i2s->bits;
+
+ val = hi6210_read_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG);
+ val &= ~((HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) |
+ (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) |
+ (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) |
+ (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT));
+ val |= ((16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) |
+ (30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) |
+ (16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) |
+ (30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT));
+ hi6210_write_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_IF_CLK_EN_CFG);
+ val |= (BIT(19) | BIT(18) | BIT(17) |
+ HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN |
+ HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN |
+ HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN |
+ HII2S_IF_CLK_EN_CFG__ST_DL_R_EN |
+ HII2S_IF_CLK_EN_CFG__ST_DL_L_EN);
+ hi6210_write_reg(i2s, HII2S_IF_CLK_EN_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG);
+ val &= ~(HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN);
+ val |= (HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN);
+ hi6210_write_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG);
+ val &= ~(HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE |
+ HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE);
+ hi6210_write_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG, val);
+
+ val = hi6210_read_reg(i2s, HII2S_MUX_TOP_MODULE_CFG);
+ val &= ~(HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE |
+ HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE |
+ HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE |
+ HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE);
+ hi6210_write_reg(i2s, HII2S_MUX_TOP_MODULE_CFG, val);
+
+
+ switch (i2s->format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ i2s->master = false;
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_MST_SLV;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ i2s->master = true;
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_MST_SLV;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ default:
+ WARN_ONCE(1, "Invalid i2s->fmt CLOCK_PROVIDER_MASK. This shouldn't happen\n");
+ return -EINVAL;
+ }
+
+ switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt = HII2S_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = HII2S_FORMAT_LEFT_JUST;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ fmt = HII2S_FORMAT_RIGHT_JUST;
+ break;
+ default:
+ WARN_ONCE(1, "Invalid i2s->fmt FORMAT_MASK. This shouldn't happen\n");
+ return -EINVAL;
+ }
+
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~(HII2S_I2S_CFG__S2_FUNC_MODE_MASK <<
+ HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT);
+ val |= fmt << HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_CLK_SEL);
+ val &= ~(HII2S_CLK_SEL__I2S_BT_FM_SEL | /* BT gets the I2S */
+ HII2S_CLK_SEL__EXT_12_288MHZ_SEL);
+ hi6210_write_reg(i2s, HII2S_CLK_SEL, val);
+
+ dma_data->maxburst = 2;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data->addr = i2s->base_phys + HII2S_ST_DL_CHANNEL;
+ else
+ dma_data->addr = i2s->base_phys + HII2S_STEREO_UPLINK_CHANNEL;
+
+ switch (i2s->channels) {
+ case 1:
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_FRAME_MODE;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ default:
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_FRAME_MODE;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ }
+
+ /* clear loopback, set signed type and word length */
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+ val &= ~(HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK <<
+ HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
+ val &= ~(HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK <<
+ HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT);
+ val |= signed_data;
+ val |= (bits << HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+
+
+ if (!i2s->master)
+ return 0;
+
+ /* set DAC and related units to correct rate */
+ val = hi6210_read_reg(i2s, HII2S_FS_CFG);
+ val &= ~(HII2S_FS_CFG__FS_S2_MASK << HII2S_FS_CFG__FS_S2_SHIFT);
+ val &= ~(HII2S_FS_CFG__FS_DACLR_MASK << HII2S_FS_CFG__FS_DACLR_SHIFT);
+ val &= ~(HII2S_FS_CFG__FS_ST_DL_R_MASK <<
+ HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
+ val &= ~(HII2S_FS_CFG__FS_ST_DL_L_MASK <<
+ HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_S2_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_DACLR_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
+ hi6210_write_reg(i2s, HII2S_FS_CFG, val);
+
+ return 0;
+}
+
+static int hi6210_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hi6210_i2s_rxctrl(cpu_dai, 1);
+ else
+ hi6210_i2s_txctrl(cpu_dai, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hi6210_i2s_rxctrl(cpu_dai, 0);
+ else
+ hi6210_i2s_txctrl(cpu_dai, 0);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "unknown cmd\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct hi6210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai,
+ &i2s->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &i2s->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+ return 0;
+}
+
+
+static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = {
+ .probe = hi6210_i2s_dai_probe,
+ .trigger = hi6210_i2s_trigger,
+ .hw_params = hi6210_i2s_hw_params,
+ .set_fmt = hi6210_i2s_set_fmt,
+ .startup = hi6210_i2s_startup,
+ .shutdown = hi6210_i2s_shutdown,
+};
+
+static const struct snd_soc_dai_driver hi6210_i2s_dai_init = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ },
+ .ops = &hi6210_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver hi6210_i2s_i2s_comp = {
+ .name = "hi6210_i2s-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static int hi6210_i2s_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct hi6210_i2s *i2s;
+ struct resource *res;
+ int ret;
+
+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->dev = dev;
+ spin_lock_init(&i2s->lock);
+
+ i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(i2s->base))
+ return PTR_ERR(i2s->base);
+
+ i2s->base_phys = (phys_addr_t)res->start;
+ i2s->dai = hi6210_i2s_dai_init;
+
+ dev_set_drvdata(dev, i2s);
+
+ i2s->sysctrl = syscon_regmap_lookup_by_phandle(node,
+ "hisilicon,sysctrl-syscon");
+ if (IS_ERR(i2s->sysctrl))
+ return PTR_ERR(i2s->sysctrl);
+
+ i2s->clk[CLK_DACODEC] = devm_clk_get(dev, "dacodec");
+ if (IS_ERR(i2s->clk[CLK_DACODEC]))
+ return PTR_ERR(i2s->clk[CLK_DACODEC]);
+ i2s->clocks++;
+
+ i2s->clk[CLK_I2S_BASE] = devm_clk_get(dev, "i2s-base");
+ if (IS_ERR(i2s->clk[CLK_I2S_BASE]))
+ return PTR_ERR(i2s->clk[CLK_I2S_BASE]);
+ i2s->clocks++;
+
+ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(dev, &hi6210_i2s_i2s_comp,
+ &i2s->dai, 1);
+ return ret;
+}
+
+static const struct of_device_id hi6210_i2s_dt_ids[] = {
+ { .compatible = "hisilicon,hi6210-i2s" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, hi6210_i2s_dt_ids);
+
+static struct platform_driver hi6210_i2s_driver = {
+ .probe = hi6210_i2s_probe,
+ .driver = {
+ .name = "hi6210_i2s",
+ .of_match_table = hi6210_i2s_dt_ids,
+ },
+};
+
+module_platform_driver(hi6210_i2s_driver);
+
+MODULE_DESCRIPTION("Hisilicon HI6210 I2S driver");
+MODULE_AUTHOR("Andy Green <andy.green@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/hisilicon/hi6210-i2s.h b/sound/soc/hisilicon/hi6210-i2s.h
new file mode 100644
index 000000000000..e816a9b63fdb
--- /dev/null
+++ b/sound/soc/hisilicon/hi6210-i2s.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * linux/sound/soc/hisilicon/hi6210-i2s.h
+ *
+ * Copyright (C) 2015 Linaro, Ltd
+ * Author: Andy Green <andy.green@linaro.org>
+ *
+ * Note at least on 6220, S2 == BT, S1 == Digital FM Radio IF
+ */
+
+#ifndef _HI6210_I2S_H
+#define _HI6210_I2S_H
+
+#define HII2S_SW_RST_N 0
+
+#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_SHIFT 28
+#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_SHIFT 26
+#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_SHIFT 24
+#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT 20
+#define HII2S_SW_RST_N__ST_DL_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_SHIFT 18
+#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_SHIFT 16
+#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_MASK 3
+
+#define HII2S_SW_RST_N__SW_RST_N BIT(0)
+
+enum hi6210_bits {
+ HII2S_BITS_16,
+ HII2S_BITS_18,
+ HII2S_BITS_20,
+ HII2S_BITS_24,
+};
+
+
+#define HII2S_IF_CLK_EN_CFG 4
+
+#define HII2S_IF_CLK_EN_CFG__THIRDMD_UPLINK_EN BIT(25)
+#define HII2S_IF_CLK_EN_CFG__THIRDMD_DLINK_EN BIT(24)
+#define HII2S_IF_CLK_EN_CFG__S3_IF_CLK_EN BIT(20)
+#define HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN BIT(16)
+#define HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN BIT(15)
+#define HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN BIT(14)
+#define HII2S_IF_CLK_EN_CFG__S2_IR_PGA_EN BIT(13)
+#define HII2S_IF_CLK_EN_CFG__S2_IL_PGA_EN BIT(12)
+#define HII2S_IF_CLK_EN_CFG__S1_IR_PGA_EN BIT(10)
+#define HII2S_IF_CLK_EN_CFG__S1_IL_PGA_EN BIT(9)
+#define HII2S_IF_CLK_EN_CFG__S1_IF_CLK_EN BIT(8)
+#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_SRC_EN BIT(7)
+#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_EN BIT(6)
+#define HII2S_IF_CLK_EN_CFG__ST_DL_R_EN BIT(5)
+#define HII2S_IF_CLK_EN_CFG__ST_DL_L_EN BIT(4)
+#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_R_EN BIT(3)
+#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_L_EN BIT(2)
+#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_R_EN BIT(1)
+#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_L_EN BIT(0)
+
+#define HII2S_DIG_FILTER_CLK_EN_CFG 8
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN BIT(30)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN BIT(28)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN BIT(25)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN BIT(24)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN BIT(22)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN BIT(20)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN BIT(17)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN BIT(16)
+
+#define HII2S_FS_CFG 0xc
+
+#define HII2S_FS_CFG__FS_S2_SHIFT 28
+#define HII2S_FS_CFG__FS_S2_MASK 7
+#define HII2S_FS_CFG__FS_S1_SHIFT 24
+#define HII2S_FS_CFG__FS_S1_MASK 7
+#define HII2S_FS_CFG__FS_ADCLR_SHIFT 20
+#define HII2S_FS_CFG__FS_ADCLR_MASK 7
+#define HII2S_FS_CFG__FS_DACLR_SHIFT 16
+#define HII2S_FS_CFG__FS_DACLR_MASK 7
+#define HII2S_FS_CFG__FS_ST_DL_R_SHIFT 8
+#define HII2S_FS_CFG__FS_ST_DL_R_MASK 7
+#define HII2S_FS_CFG__FS_ST_DL_L_SHIFT 4
+#define HII2S_FS_CFG__FS_ST_DL_L_MASK 7
+#define HII2S_FS_CFG__FS_VOICE_DLINK_SHIFT 0
+#define HII2S_FS_CFG__FS_VOICE_DLINK_MASK 7
+
+enum hi6210_i2s_rates {
+ HII2S_FS_RATE_8KHZ = 0,
+ HII2S_FS_RATE_16KHZ = 1,
+ HII2S_FS_RATE_32KHZ = 2,
+ HII2S_FS_RATE_48KHZ = 4,
+ HII2S_FS_RATE_96KHZ = 5,
+ HII2S_FS_RATE_192KHZ = 6,
+};
+
+#define HII2S_I2S_CFG 0x10
+
+#define HII2S_I2S_CFG__S2_IF_TX_EN BIT(31)
+#define HII2S_I2S_CFG__S2_IF_RX_EN BIT(30)
+#define HII2S_I2S_CFG__S2_FRAME_MODE BIT(29)
+#define HII2S_I2S_CFG__S2_MST_SLV BIT(28)
+#define HII2S_I2S_CFG__S2_LRCK_MODE BIT(27)
+#define HII2S_I2S_CFG__S2_CHNNL_MODE BIT(26)
+#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT 24
+#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK 3
+#define HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT 22
+#define HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK 3
+#define HII2S_I2S_CFG__S2_TX_CLK_SEL BIT(21)
+#define HII2S_I2S_CFG__S2_RX_CLK_SEL BIT(20)
+#define HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT BIT(19)
+#define HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT 16
+#define HII2S_I2S_CFG__S2_FUNC_MODE_MASK 7
+#define HII2S_I2S_CFG__S1_IF_TX_EN BIT(15)
+#define HII2S_I2S_CFG__S1_IF_RX_EN BIT(14)
+#define HII2S_I2S_CFG__S1_FRAME_MODE BIT(13)
+#define HII2S_I2S_CFG__S1_MST_SLV BIT(12)
+#define HII2S_I2S_CFG__S1_LRCK_MODE BIT(11)
+#define HII2S_I2S_CFG__S1_CHNNL_MODE BIT(10)
+#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_SHIFT 8
+#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_MASK 3
+#define HII2S_I2S_CFG__S1_DIRECT_LOOP_SHIFT 6
+#define HII2S_I2S_CFG__S1_DIRECT_LOOP_MASK 3
+#define HII2S_I2S_CFG__S1_TX_CLK_SEL BIT(5)
+#define HII2S_I2S_CFG__S1_RX_CLK_SEL BIT(4)
+#define HII2S_I2S_CFG__S1_CODEC_DATA_FORMAT BIT(3)
+#define HII2S_I2S_CFG__S1_FUNC_MODE_SHIFT 0
+#define HII2S_I2S_CFG__S1_FUNC_MODE_MASK 7
+
+enum hi6210_i2s_formats {
+ HII2S_FORMAT_I2S,
+ HII2S_FORMAT_PCM_STD,
+ HII2S_FORMAT_PCM_USER,
+ HII2S_FORMAT_LEFT_JUST,
+ HII2S_FORMAT_RIGHT_JUST,
+};
+
+#define HII2S_DIG_FILTER_MODULE_CFG 0x14
+
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_SHIFT 28
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_MASK 3
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN4_MUTE BIT(27)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN3_MUTE BIT(26)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE BIT(25)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN1_MUTE BIT(24)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_SHIFT 20
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_MASK 3
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN4_MUTE BIT(19)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN3_MUTE BIT(18)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE BIT(17)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN1_MUTE BIT(16)
+#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACR_SDM_DITHER BIT(9)
+#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACL_SDM_DITHER BIT(8)
+#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_SHIFT 4
+#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_MASK 7
+#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_SHIFT 0
+#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_MASK 7
+
+enum hi6210_gains {
+ HII2S_GAIN_100PC,
+ HII2S_GAIN_50PC,
+ HII2S_GAIN_25PC,
+};
+
+#define HII2S_MUX_TOP_MODULE_CFG 0x18
+
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_SHIFT 14
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_MASK 3
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE BIT(13)
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE BIT(12)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_SHIFT 10
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_MASK 3
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE BIT(9)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE BIT(8)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_RDY BIT(6)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_SHIFT 4
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_MASK 3
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_RDY BIT(3)
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_SHIFT 0
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_MASK 7
+
+enum hi6210_s2_src_mode {
+ HII2S_S2_SRC_MODE_3,
+ HII2S_S2_SRC_MODE_12,
+ HII2S_S2_SRC_MODE_6,
+ HII2S_S2_SRC_MODE_2,
+};
+
+enum hi6210_voice_dlink_src_mode {
+ HII2S_VOICE_DL_SRC_MODE_12 = 1,
+ HII2S_VOICE_DL_SRC_MODE_6,
+ HII2S_VOICE_DL_SRC_MODE_2,
+ HII2S_VOICE_DL_SRC_MODE_3,
+};
+
+#define HII2S_ADC_PGA_CFG 0x1c
+#define HII2S_S1_INPUT_PGA_CFG 0x20
+#define HII2S_S2_INPUT_PGA_CFG 0x24
+#define HII2S_ST_DL_PGA_CFG 0x28
+#define HII2S_VOICE_SIDETONE_DLINK_PGA_CFG 0x2c
+#define HII2S_APB_AFIFO_CFG_1 0x30
+#define HII2S_APB_AFIFO_CFG_2 0x34
+#define HII2S_ST_DL_FIFO_TH_CFG 0x38
+
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT 24
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK 0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT 16
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK 0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT 8
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK 0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT 0
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK 0x1f
+
+#define HII2S_STEREO_UPLINK_FIFO_TH_CFG 0x3c
+#define HII2S_VOICE_UPLINK_FIFO_TH_CFG 0x40
+#define HII2S_CODEC_IRQ_MASK 0x44
+#define HII2S_CODEC_IRQ 0x48
+#define HII2S_DACL_AGC_CFG_1 0x4c
+#define HII2S_DACL_AGC_CFG_2 0x50
+#define HII2S_DACR_AGC_CFG_1 0x54
+#define HII2S_DACR_AGC_CFG_2 0x58
+#define HII2S_DMIC_SIF_CFG 0x5c
+#define HII2S_MISC_CFG 0x60
+
+#define HII2S_MISC_CFG__THIRDMD_DLINK_TEST_SEL BIT(17)
+#define HII2S_MISC_CFG__THIRDMD_DLINK_DIN_SEL BIT(16)
+#define HII2S_MISC_CFG__S3_DOUT_RIGHT_SEL BIT(14)
+#define HII2S_MISC_CFG__S3_DOUT_LEFT_SEL BIT(13)
+#define HII2S_MISC_CFG__S3_DIN_TEST_SEL BIT(12)
+#define HII2S_MISC_CFG__VOICE_DLINK_SRC_UP_DOUT_VLD_SEL BIT(8)
+#define HII2S_MISC_CFG__VOICE_DLINK_TEST_SEL BIT(7)
+#define HII2S_MISC_CFG__VOICE_DLINK_DIN_SEL BIT(6)
+#define HII2S_MISC_CFG__ST_DL_TEST_SEL BIT(4)
+#define HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL BIT(3)
+#define HII2S_MISC_CFG__S2_DOUT_TEST_SEL BIT(2)
+#define HII2S_MISC_CFG__S1_DOUT_TEST_SEL BIT(1)
+#define HII2S_MISC_CFG__S2_DOUT_LEFT_SEL BIT(0)
+
+#define HII2S_S2_SRC_CFG 0x64
+#define HII2S_MEM_CFG 0x68
+#define HII2S_THIRDMD_PCM_PGA_CFG 0x6c
+#define HII2S_THIRD_MODEM_FIFO_TH 0x70
+#define HII2S_S3_ANTI_FREQ_JITTER_TX_INC_CNT 0x74
+#define HII2S_S3_ANTI_FREQ_JITTER_TX_DEC_CNT 0x78
+#define HII2S_S3_ANTI_FREQ_JITTER_RX_INC_CNT 0x7c
+#define HII2S_S3_ANTI_FREQ_JITTER_RX_DEC_CNT 0x80
+#define HII2S_ANTI_FREQ_JITTER_EN 0x84
+#define HII2S_CLK_SEL 0x88
+
+/* 0 = BT owns the i2s */
+#define HII2S_CLK_SEL__I2S_BT_FM_SEL BIT(0)
+/* 0 = internal source, 1 = ext */
+#define HII2S_CLK_SEL__EXT_12_288MHZ_SEL BIT(1)
+
+
+#define HII2S_THIRDMD_DLINK_CHANNEL 0xe8
+#define HII2S_THIRDMD_ULINK_CHANNEL 0xec
+#define HII2S_VOICE_DLINK_CHANNEL 0xf0
+
+/* shovel data in here for playback */
+#define HII2S_ST_DL_CHANNEL 0xf4
+#define HII2S_STEREO_UPLINK_CHANNEL 0xf8
+#define HII2S_VOICE_UPLINK_CHANNEL 0xfc
+
+#endif/* _HI6210_I2S_H */
diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig
index 857a9510ee1c..22b75a8144a1 100644
--- a/sound/soc/img/Kconfig
+++ b/sound/soc/img/Kconfig
@@ -1,11 +1,9 @@
-config SND_SOC_IMG
- bool "Audio support for Imagination Technologies designs"
- help
- Audio support for Imagination Technologies audio hardware
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Imagination Technologies"
+ depends on MIPS || COMPILE_TEST
config SND_SOC_IMG_I2S_IN
tristate "Imagination I2S Input Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S in driver for
@@ -13,7 +11,6 @@ config SND_SOC_IMG_I2S_IN
config SND_SOC_IMG_I2S_OUT
tristate "Imagination I2S Output Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S out driver for
@@ -21,7 +18,6 @@ config SND_SOC_IMG_I2S_OUT
config SND_SOC_IMG_PARALLEL_OUT
tristate "Imagination Parallel Output Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for parallel out driver for
@@ -29,7 +25,6 @@ config SND_SOC_IMG_PARALLEL_OUT
config SND_SOC_IMG_SPDIF_IN
tristate "Imagination SPDIF Input Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF input driver for
@@ -37,7 +32,6 @@ config SND_SOC_IMG_SPDIF_IN
config SND_SOC_IMG_SPDIF_OUT
tristate "Imagination SPDIF Output Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF out driver for
@@ -46,7 +40,8 @@ config SND_SOC_IMG_SPDIF_OUT
config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC
tristate "Support for Pistachio SoC Internal DAC Driver"
- depends on SND_SOC_IMG
help
Say Y or M if you want to add support for Pistachio internal DAC
driver for Imagination Technologies Pistachio internal DAC device.
+
+endmenu
diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile
index 0508c1ced636..3e7b0fd4fcbf 100644
--- a/sound/soc/img/Makefile
+++ b/sound/soc/img/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o
obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o
obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c
index 0389203f8560..98d2ab68883a 100644
--- a/sound/soc/img/img-i2s-in.c
+++ b/sound/soc/img/img-i2s-in.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IMG I2S input controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -16,6 +13,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/core.h>
@@ -60,8 +58,33 @@ struct img_i2s_in {
void __iomem *channel_base;
unsigned int active_channels;
struct snd_soc_dai_driver dai_driver;
+ u32 suspend_ctl;
+ u32 *suspend_ch_ctl;
};
+static int img_i2s_in_runtime_suspend(struct device *dev)
+{
+ struct img_i2s_in *i2s = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(i2s->clk_sys);
+
+ return 0;
+}
+
+static int img_i2s_in_runtime_resume(struct device *dev)
+{
+ struct img_i2s_in *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2s->clk_sys);
+ if (ret) {
+ dev_err(dev, "Unable to enable sys clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
{
writel(val, i2s->base + reg);
@@ -279,7 +302,7 @@ static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
- int i;
+ int i, ret;
u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
u32 reg;
@@ -310,8 +333,8 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
break;
default:
return -EINVAL;
@@ -319,6 +342,10 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
+ ret = pm_runtime_resume_and_get(i2s->dev);
+ if (ret < 0)
+ return ret;
+
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_disable(i2s, i);
@@ -338,15 +365,11 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_enable(i2s, i);
+ pm_runtime_put(i2s->dev);
+
return 0;
}
-static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
- .trigger = img_i2s_in_trigger,
- .hw_params = img_i2s_in_hw_params,
- .set_fmt = img_i2s_in_set_fmt
-};
-
static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
{
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
@@ -356,19 +379,27 @@ static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
+ .probe = img_i2s_in_dai_probe,
+ .trigger = img_i2s_in_trigger,
+ .hw_params = img_i2s_in_hw_params,
+ .set_fmt = img_i2s_in_set_fmt
+};
+
static const struct snd_soc_component_driver img_i2s_in_component = {
- .name = "img-i2s-in"
+ .name = "img-i2s-in",
+ .legacy_dai_naming = 1,
};
static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
{
unsigned int i2s_channels = params_channels(params) / 2;
- struct snd_soc_pcm_runtime *rtd = st->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(st);
struct snd_dmaengine_dai_dma_data *dma_data;
int ret;
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
+ dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), st);
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
if (ret)
@@ -403,8 +434,7 @@ static int img_i2s_in_probe(struct platform_device *pdev)
i2s->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -421,21 +451,24 @@ static int img_i2s_in_probe(struct platform_device *pdev)
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
i2s->clk_sys = devm_clk_get(dev, "sys");
- if (IS_ERR(i2s->clk_sys)) {
- if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(i2s->clk_sys);
+ if (IS_ERR(i2s->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk_sys),
+ "Failed to acquire clock 'sys'\n");
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = img_i2s_in_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
}
-
- ret = clk_prepare_enable(i2s->clk_sys);
- if (ret)
- return ret;
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_suspend;
i2s->active_channels = 1;
i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
i2s->dma_data.addr_width = 4;
- i2s->dai_driver.probe = img_i2s_in_dai_probe;
i2s->dai_driver.capture.channels_min = 2;
i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
@@ -443,11 +476,12 @@ static int img_i2s_in_probe(struct platform_device *pdev)
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
i2s->dai_driver.ops = &img_i2s_in_dai_ops;
- rst = devm_reset_control_get(dev, "rst");
+ rst = devm_reset_control_get_exclusive(dev, "rst");
if (IS_ERR(rst)) {
if (PTR_ERR(rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
- goto err_clk_disable;
+ pm_runtime_put(&pdev->dev);
+ goto err_suspend;
}
dev_dbg(dev, "No top level reset found\n");
@@ -469,28 +503,85 @@ static int img_i2s_in_probe(struct platform_device *pdev)
IMG_I2S_IN_CH_CTL_JUST_MASK |
IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
+ pm_runtime_put(&pdev->dev);
+
+ i2s->suspend_ch_ctl = devm_kcalloc(dev,
+ i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
+ if (!i2s->suspend_ch_ctl) {
+ ret = -ENOMEM;
+ goto err_suspend;
+ }
+
ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
&i2s->dai_driver, 1);
if (ret)
- goto err_clk_disable;
+ goto err_suspend;
ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
if (ret)
- goto err_clk_disable;
+ goto err_suspend;
return 0;
-err_clk_disable:
- clk_disable_unprepare(i2s->clk_sys);
+err_suspend:
+ if (!pm_runtime_enabled(&pdev->dev))
+ img_i2s_in_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
return ret;
}
-static int img_i2s_in_dev_remove(struct platform_device *pdev)
+static void img_i2s_in_dev_remove(struct platform_device *pdev)
{
- struct img_i2s_in *i2s = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ img_i2s_in_runtime_suspend(&pdev->dev);
+}
- clk_disable_unprepare(i2s->clk_sys);
+static int img_i2s_in_suspend(struct device *dev)
+{
+ struct img_i2s_in *i2s = dev_get_drvdata(dev);
+ int i, ret;
+ u32 reg;
+
+ if (pm_runtime_status_suspended(dev)) {
+ ret = img_i2s_in_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < i2s->max_i2s_chan; i++) {
+ reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
+ i2s->suspend_ch_ctl[i] = reg;
+ }
+
+ i2s->suspend_ctl = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
+
+ img_i2s_in_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int img_i2s_in_resume(struct device *dev)
+{
+ struct img_i2s_in *i2s = dev_get_drvdata(dev);
+ int i, ret;
+ u32 reg;
+
+ ret = img_i2s_in_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < i2s->max_i2s_chan; i++) {
+ reg = i2s->suspend_ch_ctl[i];
+ img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
+ }
+
+ img_i2s_in_writel(i2s, i2s->suspend_ctl, IMG_I2S_IN_CTL);
+
+ if (pm_runtime_status_suspended(dev))
+ img_i2s_in_runtime_suspend(dev);
return 0;
}
@@ -501,10 +592,16 @@ static const struct of_device_id img_i2s_in_of_match[] = {
};
MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
+static const struct dev_pm_ops img_i2s_in_pm_ops = {
+ RUNTIME_PM_OPS(img_i2s_in_runtime_suspend, img_i2s_in_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume)
+};
+
static struct platform_driver img_i2s_in_driver = {
.driver = {
.name = "img-i2s-in",
- .of_match_table = img_i2s_in_of_match
+ .of_match_table = img_i2s_in_of_match,
+ .pm = pm_ptr(&img_i2s_in_pm_ops)
},
.probe = img_i2s_in_probe,
.remove = img_i2s_in_dev_remove
diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c
index 5f997135a8ae..aedcf56a5359 100644
--- a/sound/soc/img/img-i2s-out.c
+++ b/sound/soc/img/img-i2s-out.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IMG I2S output controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -63,29 +60,36 @@ struct img_i2s_out {
unsigned int active_channels;
struct reset_control *rst;
struct snd_soc_dai_driver dai_driver;
+ u32 suspend_ctl;
+ u32 *suspend_ch_ctl;
};
-static int img_i2s_out_suspend(struct device *dev)
+static int img_i2s_out_runtime_suspend(struct device *dev)
{
struct img_i2s_out *i2s = dev_get_drvdata(dev);
- if (!i2s->force_clk_active)
- clk_disable_unprepare(i2s->clk_ref);
+ clk_disable_unprepare(i2s->clk_ref);
+ clk_disable_unprepare(i2s->clk_sys);
return 0;
}
-static int img_i2s_out_resume(struct device *dev)
+static int img_i2s_out_runtime_resume(struct device *dev)
{
struct img_i2s_out *i2s = dev_get_drvdata(dev);
int ret;
- if (!i2s->force_clk_active) {
- ret = clk_prepare_enable(i2s->clk_ref);
- if (ret) {
- dev_err(dev, "clk_enable failed: %d\n", ret);
- return ret;
- }
+ ret = clk_prepare_enable(i2s->clk_sys);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(i2s->clk_ref);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ clk_disable_unprepare(i2s->clk_sys);
+ return ret;
}
return 0;
@@ -287,7 +291,7 @@ static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
- int i;
+ int i, ret;
bool force_clk_active;
u32 chan_control_mask, control_mask, chan_control_set = 0;
u32 reg, control_set = 0;
@@ -298,10 +302,10 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
if (force_clk_active)
control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_BP_FP:
control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
break;
default:
@@ -342,6 +346,10 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
+ ret = pm_runtime_resume_and_get(i2s->dev);
+ if (ret < 0)
+ return ret;
+
img_i2s_out_disable(i2s);
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
@@ -361,18 +369,13 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
img_i2s_out_ch_enable(i2s, i);
img_i2s_out_enable(i2s);
+ pm_runtime_put(i2s->dev);
i2s->force_clk_active = force_clk_active;
return 0;
}
-static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
- .trigger = img_i2s_out_trigger,
- .hw_params = img_i2s_out_hw_params,
- .set_fmt = img_i2s_out_set_fmt
-};
-
static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
{
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
@@ -382,19 +385,27 @@ static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
+ .probe = img_i2s_out_dai_probe,
+ .trigger = img_i2s_out_trigger,
+ .hw_params = img_i2s_out_hw_params,
+ .set_fmt = img_i2s_out_set_fmt
+};
+
static const struct snd_soc_component_driver img_i2s_out_component = {
- .name = "img-i2s-out"
+ .name = "img-i2s-out",
+ .legacy_dai_naming = 1,
};
static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
{
unsigned int i2s_channels = params_channels(params) / 2;
- struct snd_soc_pcm_runtime *rtd = st->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(st);
struct snd_dmaengine_dai_dma_data *dma_data;
int ret;
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
+ dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), st);
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
if (ret)
@@ -429,8 +440,7 @@ static int img_i2s_out_probe(struct platform_device *pdev)
i2s->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -446,30 +456,35 @@ static int img_i2s_out_probe(struct platform_device *pdev)
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
- i2s->rst = devm_reset_control_get(&pdev->dev, "rst");
- if (IS_ERR(i2s->rst)) {
- if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No top level reset found\n");
- return PTR_ERR(i2s->rst);
- }
+ i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
+ if (IS_ERR(i2s->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst),
+ "No top level reset found\n");
i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
- if (IS_ERR(i2s->clk_sys)) {
- if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(i2s->clk_sys);
- }
+ if (IS_ERR(i2s->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk_sys),
+ "Failed to acquire clock 'sys'\n");
i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
- if (IS_ERR(i2s->clk_ref)) {
- if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'ref'\n");
- return PTR_ERR(i2s->clk_ref);
- }
+ if (IS_ERR(i2s->clk_ref))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk_ref),
+ "Failed to acquire clock 'ref'\n");
- ret = clk_prepare_enable(i2s->clk_sys);
- if (ret)
- return ret;
+ i2s->suspend_ch_ctl = devm_kcalloc(dev,
+ i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
+ if (!i2s->suspend_ch_ctl)
+ return -ENOMEM;
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = img_i2s_out_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_suspend;
reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
@@ -483,20 +498,13 @@ static int img_i2s_out_probe(struct platform_device *pdev)
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
img_i2s_out_reset(i2s);
-
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- ret = img_i2s_out_resume(&pdev->dev);
- if (ret)
- goto err_pm_disable;
- }
+ pm_runtime_put(&pdev->dev);
i2s->active_channels = 1;
i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
i2s->dma_data.addr_width = 4;
i2s->dma_data.maxburst = 4;
- i2s->dai_driver.probe = img_i2s_out_dai_probe;
i2s->dai_driver.playback.channels_min = 2;
i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
@@ -517,23 +525,63 @@ static int img_i2s_out_probe(struct platform_device *pdev)
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
- img_i2s_out_suspend(&pdev->dev);
+ img_i2s_out_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(i2s->clk_sys);
return ret;
}
-static int img_i2s_out_dev_remove(struct platform_device *pdev)
+static void img_i2s_out_dev_remove(struct platform_device *pdev)
{
- struct img_i2s_out *i2s = platform_get_drvdata(pdev);
-
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
- img_i2s_out_suspend(&pdev->dev);
+ img_i2s_out_runtime_suspend(&pdev->dev);
+}
- clk_disable_unprepare(i2s->clk_sys);
+static int img_i2s_out_suspend(struct device *dev)
+{
+ struct img_i2s_out *i2s = dev_get_drvdata(dev);
+ int i, ret;
+ u32 reg;
+
+ if (pm_runtime_status_suspended(dev)) {
+ ret = img_i2s_out_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < i2s->max_i2s_chan; i++) {
+ reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
+ i2s->suspend_ch_ctl[i] = reg;
+ }
+
+ i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
+
+ img_i2s_out_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int img_i2s_out_resume(struct device *dev)
+{
+ struct img_i2s_out *i2s = dev_get_drvdata(dev);
+ int i, ret;
+ u32 reg;
+
+ ret = img_i2s_out_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < i2s->max_i2s_chan; i++) {
+ reg = i2s->suspend_ch_ctl[i];
+ img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
+ }
+
+ img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL);
+
+ if (pm_runtime_status_suspended(dev))
+ img_i2s_out_runtime_suspend(dev);
return 0;
}
@@ -545,15 +593,15 @@ static const struct of_device_id img_i2s_out_of_match[] = {
MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
static const struct dev_pm_ops img_i2s_out_pm_ops = {
- SET_RUNTIME_PM_OPS(img_i2s_out_suspend,
- img_i2s_out_resume, NULL)
+ RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, img_i2s_out_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume)
};
static struct platform_driver img_i2s_out_driver = {
.driver = {
.name = "img-i2s-out",
.of_match_table = img_i2s_out_of_match,
- .pm = &img_i2s_out_pm_ops
+ .pm = pm_ptr(&img_i2s_out_pm_ops)
},
.probe = img_i2s_out_probe,
.remove = img_i2s_out_dev_remove
diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c
index c1610a054d65..86b60e6dab38 100644
--- a/sound/soc/img/img-parallel-out.c
+++ b/sound/soc/img/img-parallel-out.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IMG parallel output controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -123,10 +120,8 @@ static int img_prl_out_hw_params(struct snd_pcm_substream *substream,
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
unsigned int rate, channels;
u32 reg, control_set = 0;
- snd_pcm_format_t format;
rate = params_rate(params);
- format = params_format(params);
channels = params_channels(params);
switch (params_format(params)) {
@@ -155,6 +150,7 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
u32 reg, control_set = 0;
+ int ret;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
@@ -166,19 +162,18 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
+ ret = pm_runtime_resume_and_get(prl->dev);
+ if (ret < 0)
+ return ret;
+
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
+ pm_runtime_put(prl->dev);
return 0;
}
-static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
- .trigger = img_prl_out_trigger,
- .hw_params = img_prl_out_hw_params,
- .set_fmt = img_prl_out_set_fmt
-};
-
static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
{
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
@@ -188,8 +183,14 @@ static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
+ .probe = img_prl_out_dai_probe,
+ .trigger = img_prl_out_trigger,
+ .hw_params = img_prl_out_hw_params,
+ .set_fmt = img_prl_out_set_fmt
+};
+
static struct snd_soc_dai_driver img_prl_out_dai = {
- .probe = img_prl_out_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -200,7 +201,8 @@ static struct snd_soc_dai_driver img_prl_out_dai = {
};
static const struct snd_soc_component_driver img_prl_out_component = {
- .name = "img-prl-out"
+ .name = "img-prl-out",
+ .legacy_dai_naming = 1,
};
static int img_prl_out_probe(struct platform_device *pdev)
@@ -219,33 +221,26 @@ static int img_prl_out_probe(struct platform_device *pdev)
prl->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
prl->base = base;
- prl->rst = devm_reset_control_get(&pdev->dev, "rst");
- if (IS_ERR(prl->rst)) {
- if (PTR_ERR(prl->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No top level reset found\n");
- return PTR_ERR(prl->rst);
- }
+ prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
+ if (IS_ERR(prl->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst),
+ "No top level reset found\n");
prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
- if (IS_ERR(prl->clk_sys)) {
- if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(prl->clk_sys);
- }
+ if (IS_ERR(prl->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(prl->clk_sys),
+ "Failed to acquire clock 'sys'\n");
prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
- if (IS_ERR(prl->clk_ref)) {
- if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'ref'\n");
- return PTR_ERR(prl->clk_ref);
- }
+ if (IS_ERR(prl->clk_ref))
+ return dev_err_probe(dev, PTR_ERR(prl->clk_ref),
+ "Failed to acquire clock 'ref'\n");
ret = clk_prepare_enable(prl->clk_sys);
if (ret)
@@ -287,7 +282,7 @@ err_pm_disable:
return ret;
}
-static int img_prl_out_dev_remove(struct platform_device *pdev)
+static void img_prl_out_dev_remove(struct platform_device *pdev)
{
struct img_prl_out *prl = platform_get_drvdata(pdev);
@@ -296,8 +291,6 @@ static int img_prl_out_dev_remove(struct platform_device *pdev)
img_prl_out_suspend(&pdev->dev);
clk_disable_unprepare(prl->clk_sys);
-
- return 0;
}
static const struct of_device_id img_prl_out_of_match[] = {
@@ -307,15 +300,14 @@ static const struct of_device_id img_prl_out_of_match[] = {
MODULE_DEVICE_TABLE(of, img_prl_out_of_match);
static const struct dev_pm_ops img_prl_out_pm_ops = {
- SET_RUNTIME_PM_OPS(img_prl_out_suspend,
- img_prl_out_resume, NULL)
+ RUNTIME_PM_OPS(img_prl_out_suspend, img_prl_out_resume, NULL)
};
static struct platform_driver img_prl_out_driver = {
.driver = {
.name = "img-parallel-out",
.of_match_table = img_prl_out_of_match,
- .pm = &img_prl_out_pm_ops
+ .pm = pm_ptr(&img_prl_out_pm_ops)
},
.probe = img_prl_out_probe,
.remove = img_prl_out_dev_remove
diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c
index 4d9953d318af..82295e2508fa 100644
--- a/sound/soc/img/img-spdif-in.c
+++ b/sound/soc/img/img-spdif-in.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IMG SPDIF input controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -16,6 +13,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/core.h>
@@ -82,11 +80,36 @@ struct img_spdif_in {
unsigned int single_freq;
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
bool active;
+ u32 suspend_clkgen;
+ u32 suspend_ctl;
/* Write-only registers */
unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
};
+static int img_spdif_in_runtime_suspend(struct device *dev)
+{
+ struct img_spdif_in *spdif = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(spdif->clk_sys);
+
+ return 0;
+}
+
+static int img_spdif_in_runtime_resume(struct device *dev)
+{
+ struct img_spdif_in *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdif->clk_sys);
+ if (ret) {
+ dev_err(dev, "Unable to enable sys clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static inline void img_spdif_in_writel(struct img_spdif_in *spdif,
u32 val, u32 reg)
{
@@ -659,11 +682,6 @@ static int img_spdif_in_hw_params(struct snd_pcm_substream *substream,
return img_spdif_in_do_clkgen_single(spdif, rate);
}
-static const struct snd_soc_dai_ops img_spdif_in_dai_ops = {
- .trigger = img_spdif_in_trigger,
- .hw_params = img_spdif_in_hw_params
-};
-
static int img_spdif_in_dai_probe(struct snd_soc_dai *dai)
{
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
@@ -676,8 +694,13 @@ static int img_spdif_in_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops img_spdif_in_dai_ops = {
+ .probe = img_spdif_in_dai_probe,
+ .trigger = img_spdif_in_trigger,
+ .hw_params = img_spdif_in_hw_params
+};
+
static struct snd_soc_dai_driver img_spdif_in_dai = {
- .probe = img_spdif_in_dai_probe,
.capture = {
.channels_min = 2,
.channels_max = 2,
@@ -688,7 +711,8 @@ static struct snd_soc_dai_driver img_spdif_in_dai = {
};
static const struct snd_soc_component_driver img_spdif_in_component = {
- .name = "img-spdif-in"
+ .name = "img-spdif-in",
+ .legacy_dai_naming = 1,
};
static int img_spdif_in_probe(struct platform_device *pdev)
@@ -709,29 +733,32 @@ static int img_spdif_in_probe(struct platform_device *pdev)
spdif->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
spdif->base = base;
spdif->clk_sys = devm_clk_get(dev, "sys");
- if (IS_ERR(spdif->clk_sys)) {
- if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(spdif->clk_sys);
- }
+ if (IS_ERR(spdif->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(spdif->clk_sys),
+ "Failed to acquire clock 'sys'\n");
- ret = clk_prepare_enable(spdif->clk_sys);
- if (ret)
- return ret;
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = img_spdif_in_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_suspend;
- rst = devm_reset_control_get(&pdev->dev, "rst");
+ rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
if (IS_ERR(rst)) {
if (PTR_ERR(rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
- goto err_clk_disable;
+ goto err_pm_put;
}
dev_dbg(dev, "No top level reset found\n");
img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK,
@@ -759,28 +786,73 @@ static int img_spdif_in_probe(struct platform_device *pdev)
IMG_SPDIF_IN_CTL_TRK_MASK;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
+ pm_runtime_put(&pdev->dev);
+
ret = devm_snd_soc_register_component(&pdev->dev,
&img_spdif_in_component, &img_spdif_in_dai, 1);
if (ret)
- goto err_clk_disable;
+ goto err_suspend;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret)
- goto err_clk_disable;
+ goto err_suspend;
return 0;
-err_clk_disable:
- clk_disable_unprepare(spdif->clk_sys);
+err_pm_put:
+ pm_runtime_put(&pdev->dev);
+err_suspend:
+ if (!pm_runtime_enabled(&pdev->dev))
+ img_spdif_in_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
return ret;
}
-static int img_spdif_in_dev_remove(struct platform_device *pdev)
+static void img_spdif_in_dev_remove(struct platform_device *pdev)
{
- struct img_spdif_in *spdif = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ img_spdif_in_runtime_suspend(&pdev->dev);
+}
- clk_disable_unprepare(spdif->clk_sys);
+static int img_spdif_in_suspend(struct device *dev)
+{
+ struct img_spdif_in *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ if (pm_runtime_status_suspended(dev)) {
+ ret = img_spdif_in_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ spdif->suspend_clkgen = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CLKGEN);
+ spdif->suspend_ctl = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
+
+ img_spdif_in_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int img_spdif_in_resume(struct device *dev)
+{
+ struct img_spdif_in *spdif = dev_get_drvdata(dev);
+ int i, ret;
+
+ ret = img_spdif_in_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++)
+ img_spdif_in_aclkgen_writel(spdif, i);
+
+ img_spdif_in_writel(spdif, spdif->suspend_clkgen, IMG_SPDIF_IN_CLKGEN);
+ img_spdif_in_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_IN_CTL);
+
+ if (pm_runtime_status_suspended(dev))
+ img_spdif_in_runtime_suspend(dev);
return 0;
}
@@ -791,10 +863,16 @@ static const struct of_device_id img_spdif_in_of_match[] = {
};
MODULE_DEVICE_TABLE(of, img_spdif_in_of_match);
+static const struct dev_pm_ops img_spdif_in_pm_ops = {
+ RUNTIME_PM_OPS(img_spdif_in_runtime_suspend, img_spdif_in_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume)
+};
+
static struct platform_driver img_spdif_in_driver = {
.driver = {
.name = "img-spdif-in",
- .of_match_table = img_spdif_in_of_match
+ .of_match_table = img_spdif_in_of_match,
+ .pm = pm_ptr(&img_spdif_in_pm_ops)
},
.probe = img_spdif_in_probe,
.remove = img_spdif_in_dev_remove
diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c
index 08f93a5dadfe..52f696219ef4 100644
--- a/sound/soc/img/img-spdif-out.c
+++ b/sound/soc/img/img-spdif-out.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IMG SPDIF output controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -47,25 +44,36 @@ struct img_spdif_out {
struct snd_dmaengine_dai_dma_data dma_data;
struct device *dev;
struct reset_control *rst;
+ u32 suspend_ctl;
+ u32 suspend_csl;
+ u32 suspend_csh;
};
-static int img_spdif_out_suspend(struct device *dev)
+static int img_spdif_out_runtime_suspend(struct device *dev)
{
struct img_spdif_out *spdif = dev_get_drvdata(dev);
clk_disable_unprepare(spdif->clk_ref);
+ clk_disable_unprepare(spdif->clk_sys);
return 0;
}
-static int img_spdif_out_resume(struct device *dev)
+static int img_spdif_out_runtime_resume(struct device *dev)
{
struct img_spdif_out *spdif = dev_get_drvdata(dev);
int ret;
+ ret = clk_prepare_enable(spdif->clk_sys);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
ret = clk_prepare_enable(spdif->clk_ref);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
+ clk_disable_unprepare(spdif->clk_sys);
return ret;
}
@@ -279,11 +287,6 @@ static int img_spdif_out_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_soc_dai_ops img_spdif_out_dai_ops = {
- .trigger = img_spdif_out_trigger,
- .hw_params = img_spdif_out_hw_params
-};
-
static int img_spdif_out_dai_probe(struct snd_soc_dai *dai)
{
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
@@ -296,8 +299,13 @@ static int img_spdif_out_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops img_spdif_out_dai_ops = {
+ .probe = img_spdif_out_dai_probe,
+ .trigger = img_spdif_out_trigger,
+ .hw_params = img_spdif_out_hw_params
+};
+
static struct snd_soc_dai_driver img_spdif_out_dai = {
- .probe = img_spdif_out_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -308,7 +316,8 @@ static struct snd_soc_dai_driver img_spdif_out_dai = {
};
static const struct snd_soc_component_driver img_spdif_out_component = {
- .name = "img-spdif-out"
+ .name = "img-spdif-out",
+ .legacy_dai_naming = 1,
};
static int img_spdif_out_probe(struct platform_device *pdev)
@@ -327,49 +336,42 @@ static int img_spdif_out_probe(struct platform_device *pdev)
spdif->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
spdif->base = base;
- spdif->rst = devm_reset_control_get(&pdev->dev, "rst");
- if (IS_ERR(spdif->rst)) {
- if (PTR_ERR(spdif->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No top level reset found\n");
- return PTR_ERR(spdif->rst);
- }
+ spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
+ if (IS_ERR(spdif->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(spdif->rst),
+ "No top level reset found\n");
spdif->clk_sys = devm_clk_get(&pdev->dev, "sys");
- if (IS_ERR(spdif->clk_sys)) {
- if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'sys'\n");
- return PTR_ERR(spdif->clk_sys);
- }
+ if (IS_ERR(spdif->clk_sys))
+ return dev_err_probe(dev, PTR_ERR(spdif->clk_sys),
+ "Failed to acquire clock 'sys'\n");
spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
- if (IS_ERR(spdif->clk_ref)) {
- if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER)
- dev_err(dev, "Failed to acquire clock 'ref'\n");
- return PTR_ERR(spdif->clk_ref);
- }
-
- ret = clk_prepare_enable(spdif->clk_sys);
- if (ret)
- return ret;
-
- img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
- IMG_SPDIF_OUT_CTL);
-
- img_spdif_out_reset(spdif);
+ if (IS_ERR(spdif->clk_ref))
+ return dev_err_probe(dev, PTR_ERR(spdif->clk_ref),
+ "Failed to acquire clock 'ref'\n");
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
- ret = img_spdif_out_resume(&pdev->dev);
+ ret = img_spdif_out_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ goto err_suspend;
+
+ img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
+ IMG_SPDIF_OUT_CTL);
+
+ img_spdif_out_reset(spdif);
+ pm_runtime_put(&pdev->dev);
spin_lock_init(&spdif->lock);
@@ -393,23 +395,55 @@ static int img_spdif_out_probe(struct platform_device *pdev)
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
- img_spdif_out_suspend(&pdev->dev);
+ img_spdif_out_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(spdif->clk_sys);
return ret;
}
-static int img_spdif_out_dev_remove(struct platform_device *pdev)
+static void img_spdif_out_dev_remove(struct platform_device *pdev)
{
- struct img_spdif_out *spdif = platform_get_drvdata(pdev);
-
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
- img_spdif_out_suspend(&pdev->dev);
+ img_spdif_out_runtime_suspend(&pdev->dev);
+}
- clk_disable_unprepare(spdif->clk_sys);
+static int img_spdif_out_suspend(struct device *dev)
+{
+ struct img_spdif_out *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ if (pm_runtime_status_suspended(dev)) {
+ ret = img_spdif_out_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ spdif->suspend_ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
+ spdif->suspend_csl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
+ spdif->suspend_csh = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
+
+ img_spdif_out_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int img_spdif_out_resume(struct device *dev)
+{
+ struct img_spdif_out *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = img_spdif_out_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ img_spdif_out_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_OUT_CTL);
+ img_spdif_out_writel(spdif, spdif->suspend_csl, IMG_SPDIF_OUT_CSL);
+ img_spdif_out_writel(spdif, spdif->suspend_csh, IMG_SPDIF_OUT_CSH_UV);
+
+ if (pm_runtime_status_suspended(dev))
+ img_spdif_out_runtime_suspend(dev);
return 0;
}
@@ -421,15 +455,15 @@ static const struct of_device_id img_spdif_out_of_match[] = {
MODULE_DEVICE_TABLE(of, img_spdif_out_of_match);
static const struct dev_pm_ops img_spdif_out_pm_ops = {
- SET_RUNTIME_PM_OPS(img_spdif_out_suspend,
- img_spdif_out_resume, NULL)
+ RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, img_spdif_out_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume)
};
static struct platform_driver img_spdif_out_driver = {
.driver = {
.name = "img-spdif-out",
.of_match_table = img_spdif_out_of_match,
- .pm = &img_spdif_out_pm_ops
+ .pm = pm_ptr(&img_spdif_out_pm_ops)
},
.probe = img_spdif_out_probe,
.remove = img_spdif_out_dev_remove
diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c
index 162a0fd68c7b..ec4f891295c9 100644
--- a/sound/soc/img/pistachio-internal-dac.c
+++ b/sound/soc/img/pistachio-internal-dac.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Pistachio internal dac driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -122,24 +119,25 @@ static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = {
},
};
-static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec)
+static int pistachio_internal_dac_codec_probe(struct snd_soc_component *component)
{
- struct pistachio_internal_dac *dac = snd_soc_codec_get_drvdata(codec);
+ struct pistachio_internal_dac *dac = snd_soc_component_get_drvdata(component);
- snd_soc_codec_init_regmap(codec, dac->regmap);
+ snd_soc_component_init_regmap(component, dac->regmap);
return 0;
}
-static const struct snd_soc_codec_driver pistachio_internal_dac_driver = {
- .probe = pistachio_internal_dac_codec_probe,
- .idle_bias_off = true,
- .controls = pistachio_internal_dac_snd_controls,
- .num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
- .dapm_widgets = pistachio_internal_dac_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets),
- .dapm_routes = pistachio_internal_dac_routes,
- .num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes),
+static const struct snd_soc_component_driver pistachio_internal_dac_driver = {
+ .probe = pistachio_internal_dac_codec_probe,
+ .controls = pistachio_internal_dac_snd_controls,
+ .num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
+ .dapm_widgets = pistachio_internal_dac_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets),
+ .dapm_routes = pistachio_internal_dac_routes,
+ .num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static int pistachio_internal_dac_probe(struct platform_device *pdev)
@@ -162,12 +160,9 @@ static int pistachio_internal_dac_probe(struct platform_device *pdev)
return PTR_ERR(dac->regmap);
dac->supply = devm_regulator_get(dev, "VDD");
- if (IS_ERR(dac->supply)) {
- ret = PTR_ERR(dac->supply);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
- return ret;
- }
+ if (IS_ERR(dac->supply))
+ return dev_err_probe(dev, PTR_ERR(dac->supply),
+ "failed to acquire supply 'VDD-supply'\n");
ret = regulator_enable(dac->supply);
if (ret) {
@@ -200,11 +195,12 @@ static int pistachio_internal_dac_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
pm_runtime_idle(dev);
- ret = snd_soc_register_codec(dev, &pistachio_internal_dac_driver,
+ ret = devm_snd_soc_register_component(dev,
+ &pistachio_internal_dac_driver,
pistachio_internal_dac_dais,
ARRAY_SIZE(pistachio_internal_dac_dais));
if (ret) {
- dev_err(dev, "failed to register codec: %d\n", ret);
+ dev_err(dev, "failed to register component: %d\n", ret);
goto err_pwr;
}
@@ -219,19 +215,15 @@ err_regulator:
return ret;
}
-static int pistachio_internal_dac_remove(struct platform_device *pdev)
+static void pistachio_internal_dac_remove(struct platform_device *pdev)
{
struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pistachio_internal_dac_pwr_off(dac);
regulator_disable(dac->supply);
-
- return 0;
}
-#ifdef CONFIG_PM
static int pistachio_internal_dac_rt_resume(struct device *dev)
{
struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
@@ -258,11 +250,10 @@ static int pistachio_internal_dac_rt_suspend(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
- SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
- pistachio_internal_dac_rt_resume, NULL)
+ RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
+ pistachio_internal_dac_rt_resume, NULL)
};
static const struct of_device_id pistachio_internal_dac_of_match[] = {
@@ -275,7 +266,7 @@ static struct platform_driver pistachio_internal_dac_plat_driver = {
.driver = {
.name = "img-pistachio-internal-dac",
.of_match_table = pistachio_internal_dac_of_match,
- .pm = &pistachio_internal_dac_pm_ops
+ .pm = pm_ptr(&pistachio_internal_dac_pm_ops)
},
.probe = pistachio_internal_dac_probe,
.remove = pistachio_internal_dac_remove
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 803f95e40679..412555e626b8 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -1,198 +1,121 @@
-config SND_MFLD_MACHINE
- tristate "SOC Machine Audio driver for Intel Medfield MID platform"
- depends on INTEL_SCU_IPC
- select SND_SOC_SN95031
- select SND_SST_MFLD_PLATFORM
- select SND_SST_IPC_PCI
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Intel"
+
+config SND_SOC_INTEL_SST_TOPLEVEL
+ bool "Intel ASoC SST drivers"
+ default y
+ depends on X86 || COMPILE_TEST
+ select SND_SOC_INTEL_MACH
help
- This adds support for ASoC machine driver for Intel(R) MID Medfield platform
- used as alsa device in audio substem in Intel(R) MID devices
- Say Y if you have such a device
- If unsure select "N".
+ Intel ASoC SST Platform Drivers. If you have a Intel machine that
+ has an audio controller with a DSP and I2S or DMIC port, then
+ enable this option by saying Y
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Intel SST drivers.
+
+if SND_SOC_INTEL_SST_TOPLEVEL
+
+config SND_SOC_INTEL_CATPT
+ tristate "Haswell and Broadwell"
+ depends on ACPI || COMPILE_TEST
+ depends on DMADEVICES && SND_DMA_SGBUF
+ select DW_DMAC_CORE
+ select SND_SOC_ACPI if ACPI
+ select WANT_DEV_COREDUMP
+ select SND_INTEL_DSP_CONFIG
+ help
+ Enable support for Intel(R) Haswell and Broadwell platforms
+ with I2S codec present. This is a recommended option.
+ Say Y or m if you have such device.
+ If unsure, say N.
-config SND_SST_MFLD_PLATFORM
+config SND_SOC_INTEL_HASWELL
tristate
- select SND_SOC_COMPRESS
+ select SND_SOC_INTEL_CATPT
-config SND_SST_IPC
+config SND_SST_ATOM_HIFI2_PLATFORM
tristate
+ select SND_SOC_COMPRESS
-config SND_SST_IPC_PCI
- tristate
- select SND_SST_IPC
+config SND_SST_ATOM_HIFI2_PLATFORM_PCI
+ tristate "PCI HiFi2 (Merrifield) Platforms"
+ depends on X86 && PCI
+ select SND_SST_ATOM_HIFI2_PLATFORM
+ help
+ If you have a Intel Merrifield/Edison platform, then
+ enable this option by saying Y or m. Distros will typically not
+ enable this option: while Merrifield/Edison can run a mainline
+ kernel with limited functionality it will require a firmware file
+ which is not in the standard firmware tree
+
+config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
+ tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms"
+ default ACPI
+ depends on X86 && ACPI && PCI
+ select SND_SST_ATOM_HIFI2_PLATFORM
+ select SND_SOC_ACPI_INTEL_MATCH
+ select SND_INTEL_DSP_CONFIG
+ select IOSF_MBI
+ help
+ If you have a Intel Baytrail or Cherrytrail platform with an I2S
+ codec, then enable this option by saying Y or m. This is a
+ recommended option
+ This option is mutually exclusive with the SOF support on
+ Baytrail/Cherrytrail. If you want to enable SOF on
+ Baytrail/Cherrytrail, you need to deselect this option first.
-config SND_SST_IPC_ACPI
- tristate
- select SND_SST_IPC
- select SND_SOC_INTEL_SST
- depends on ACPI
+endif ## SND_SOC_INTEL_SST_TOPLEVEL
-config SND_SOC_INTEL_SST
- tristate
- select SND_SOC_INTEL_SST_ACPI if ACPI
- depends on (X86 || COMPILE_TEST)
+if SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL
-config SND_SOC_INTEL_SST_ACPI
+config SND_SOC_ACPI_INTEL_MATCH
tristate
+ select SND_SOC_ACPI if ACPI
+ select SND_SOC_ACPI_INTEL_SDCA_QUIRKS
+ # this option controls the compilation of ACPI matching tables and
+ # helpers and is not meant to be selected by the user.
-config SND_SOC_INTEL_HASWELL
+config SND_SOC_ACPI_INTEL_SDCA_QUIRKS
tristate
+ select SND_SOC_SDCA if ACPI
-config SND_SOC_INTEL_BAYTRAIL
- tristate
+endif ## SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL
-config SND_SOC_INTEL_HASWELL_MACH
- tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
- depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
- depends on DW_DMAC_CORE=y
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_HASWELL
- select SND_SOC_RT5640
- help
- This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell
- Ultrabook platforms.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_BYT_RT5640_MACH
- tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
- depends on X86_INTEL_LPSS && I2C
- depends on DW_DMAC_CORE=y && (SND_SOC_INTEL_BYTCR_RT5640_MACH = n)
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_BAYTRAIL
- select SND_SOC_RT5640
- help
- This adds audio driver for Intel Baytrail platform based boards
- with the RT5640 audio codec. This driver is deprecated, use
- SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality
-
-config SND_SOC_INTEL_BYT_MAX98090_MACH
- tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
- depends on X86_INTEL_LPSS && I2C
- depends on DW_DMAC_CORE=y
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_BAYTRAIL
- select SND_SOC_MAX98090
- help
- This adds audio driver for Intel Baytrail platform based boards
- with the MAX98090 audio codec.
-
-config SND_SOC_INTEL_BROADWELL_MACH
- tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
- depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
- I2C_DESIGNWARE_PLATFORM
- depends on DW_DMAC_CORE=y
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_HASWELL
- select SND_SOC_RT286
+config SND_SOC_INTEL_KEEMBAY
+ tristate "Keembay Platforms"
+ depends on ARCH_KEEMBAY || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_DMAENGINE_PCM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
- This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
- Ultrabook platforms.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_BYTCR_RT5640_MACH
- tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
- depends on X86 && I2C
- select SND_SOC_RT5640
- select SND_SST_MFLD_PLATFORM
- select SND_SST_IPC_ACPI
- help
- This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
- platforms with RT5640 audio codec.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_BYTCR_RT5651_MACH
- tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"
- depends on X86 && I2C
- select SND_SOC_RT5651
- select SND_SST_MFLD_PLATFORM
- select SND_SST_IPC_ACPI
- help
- This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
- platforms with RT5651 audio codec.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
- tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
- depends on X86_INTEL_LPSS && I2C
- select SND_SOC_RT5670
- select SND_SST_MFLD_PLATFORM
- select SND_SST_IPC_ACPI
- help
- This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
- platforms with RT5672 audio codec.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
- tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
- depends on X86_INTEL_LPSS && I2C
- select SND_SOC_RT5645
- select SND_SST_MFLD_PLATFORM
- select SND_SST_IPC_ACPI
- help
- This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
- platforms with RT5645/5650 audio codec.
- If unsure select "N".
-
-config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
- tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
- depends on X86_INTEL_LPSS && I2C
- select SND_SOC_MAX98090
- select SND_SOC_TS3A227E
- select SND_SST_MFLD_PLATFORM
- select SND_SST_IPC_ACPI
- help
- This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
- platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
- If unsure select "N".
-
-config SND_SOC_INTEL_SKYLAKE
- tristate
- select SND_HDA_EXT_CORE
+ If you have a Intel Keembay platform then enable this option
+ by saying Y or m.
+
+config SND_SOC_INTEL_AVS
+ tristate "Intel AVS driver"
+ depends on X86 || COMPILE_TEST
+ depends on PCI
+ depends on COMMON_CLK
+ select ACPI_NHLT if ACPI
+ select SND_SOC_ACPI if ACPI
select SND_SOC_TOPOLOGY
- select SND_SOC_INTEL_SST
-
-config SND_SOC_INTEL_SKL_RT286_MACH
- tristate "ASoC Audio driver for SKL with RT286 I2S mode"
- depends on X86 && ACPI && I2C
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_SKYLAKE
- select SND_SOC_RT286
- select SND_SOC_DMIC
- help
- This adds support for ASoC machine driver for Skylake platforms
- with RT286 I2S audio codec.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
- tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode"
- depends on X86_INTEL_LPSS && I2C
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_SKYLAKE
- select SND_SOC_NAU8825
- select SND_SOC_SSM4567
- select SND_SOC_DMIC
- help
- This adds support for ASoC Onboard Codec I2S machine driver. This will
- create an alsa sound card for NAU88L25 + SSM4567.
- Say Y if you have such a device
- If unsure select "N".
-
-config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
- tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode"
- depends on X86_INTEL_LPSS && I2C
- select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_SKYLAKE
- select SND_SOC_NAU8825
- select SND_SOC_MAX98357A
- select SND_SOC_DMIC
+ select SND_SOC_HDA
+ select SND_SOC_COMPRESS if DEBUG_FS
+ select SND_HDA_EXT_CORE
+ select SND_HDA_DSP_LOADER
+ select SND_INTEL_DSP_CONFIG
+ select WANT_DEV_COREDUMP
help
- This adds support for ASoC Onboard Codec I2S machine driver. This will
- create an alsa sound card for NAU88L25 + MAX98357A.
- Say Y if you have such a device
- If unsure select "N".
+ Enable support for Intel(R) cAVS 1.5 platforms with DSP
+ capabilities. This includes Skylake, Kabylake, Amberlake and
+ Apollolake.
+
+# Machine board drivers
+source "sound/soc/intel/avs/boards/Kconfig"
+
+# ASoC codec drivers
+source "sound/soc/intel/boards/Kconfig"
+
+endmenu
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index 2b45435e6245..8ecc7047d700 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -1,11 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Core support
-obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
+obj-$(CONFIG_SND_SOC) += common/
# Platform Support
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
-obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
-obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/
-obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/
+obj-$(CONFIG_SND_SOC_INTEL_CATPT) += catpt/
+obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += keembay/
+obj-$(CONFIG_SND_SOC_INTEL_AVS) += avs/
# Machine support
obj-$(CONFIG_SND_SOC) += boards/
diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile
index ce8074fa6d66..38e4876025c7 100644
--- a/sound/soc/intel/atom/Makefile
+++ b/sound/soc/intel/atom/Makefile
@@ -1,7 +1,9 @@
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
- sst-mfld-platform-compress.o sst-atom-controls.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-sst-atom-hifi2-platform-y := sst-mfld-platform-pcm.o \
+ sst-mfld-platform-compress.o \
+ sst-atom-controls.o
-obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-soc-sst-atom-hifi2-platform.o
# DSP driver
-obj-$(CONFIG_SND_SST_IPC) += sst/
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += sst/
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index b97e6adcf1b2..3629ceaaac17 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -1,4 +1,5 @@
-/*
+// SPDX-License-Identifier: GPL-2.0-only
+ /*
* sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
*
* Copyright (C) 2013-14 Intel Corp
@@ -6,15 +7,6 @@
* Vinod Koul <vinod.koul@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.
- *
* In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
* we forward the settings and parameters, rest we keep the values in
* driver and forward when DAPM enables them
@@ -58,6 +50,8 @@ static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
{
int ret = 0;
+ WARN_ON(!mutex_is_locked(&drv->lock));
+
ret = sst_fill_byte_control(drv, ipc_msg,
block, task_id, pipe_id, len, cmd_data);
if (ret < 0)
@@ -67,8 +61,13 @@ static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
/**
* sst_fill_and_send_cmd - generate the IPC message and send it to the FW
- * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)
- * @cmd_data: the IPC payload
+ * @drv: sst_data
+ * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)
+ * @block: block index
+ * @task_id: task index
+ * @pipe_id: pipe index
+ * @cmd_data: the IPC payload
+ * @len: length of data to be sent
*/
static int sst_fill_and_send_cmd(struct sst_data *drv,
u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
@@ -84,7 +83,7 @@ static int sst_fill_and_send_cmd(struct sst_data *drv,
return ret;
}
-/**
+/*
* tx map value is a bitfield where each bit represents a FW channel
*
* 3 2 1 0 # 0 = codec0, 1 = codec1
@@ -96,7 +95,7 @@ static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
};
-/**
+/*
* rx map value is a bitfield where each bit represents a slot
*
* 76543210 # 0 = slot 0, 1 = slot 1
@@ -107,7 +106,7 @@ static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
};
-/**
+/*
* NOTE: this is invoked with lock held
*/
static int sst_send_slot_map(struct sst_data *drv)
@@ -143,7 +142,7 @@ static int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item > e->max - 1)
uinfo->value.enumerated.item = e->max - 1;
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
e->texts[uinfo->value.enumerated.item]);
return 0;
@@ -151,7 +150,8 @@ static int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
/**
* sst_slot_get - get the status of the interleaver/deinterleaver control
- *
+ * @kcontrol: control pointer
+ * @ucontrol: User data
* Searches the map where the control status is stored, and gets the
* channel/slot which is currently set for this enumerated control. Since it is
* an enumerated control, there is only one possible value.
@@ -195,7 +195,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol
if (e->w && e->w->power)
ret = sst_send_slot_map(drv);
- else
+ else if (!e->w)
dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
kcontrol->id.name);
return ret;
@@ -203,7 +203,8 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol
/**
* sst_slot_put - set the status of interleaver/deinterleaver control
- *
+ * @kcontrol: control pointer
+ * @ucontrol: User data
* (de)interleaver controls are defined in opposite sense to be user-friendly
*
* Instead of the enum value being the value written to the register, it is the
@@ -217,7 +218,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol
static int sst_slot_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
struct sst_data *drv = snd_soc_component_get_drvdata(c);
struct sst_enum *e = (void *)kcontrol->private_value;
int i, ret = 0;
@@ -286,7 +287,9 @@ static int sst_send_algo_cmd(struct sst_data *drv,
/**
* sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
- *
+ * @drv: sst_data
+ * @pipe: string identifier
+ * @ids: list of algorithms
* The algos which are in each pipeline are sent to the firmware one by one
*
* Called with lock held
@@ -296,7 +299,7 @@ static int sst_find_and_send_pipe_algo(struct sst_data *drv,
{
int ret = 0;
struct sst_algo_control *bc;
- struct sst_module *algo = NULL;
+ struct sst_module *algo;
dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
@@ -346,7 +349,7 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = 0;
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
struct sst_algo_control *bc = (void *)kcontrol->private_value;
@@ -385,11 +388,15 @@ static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
/**
* sst_send_gain_cmd - send the gain algorithm IPC to the FW
- * @gv: the stored value of gain (also contains rampduration)
- * @mute: flag that indicates whether this was called from the
- * digital_mute callback or directly. If called from the
- * digital_mute callback, module will be muted/unmuted based on this
- * flag. The flag is always 0 if called directly.
+ * @drv: sst_data
+ * @gv:the stored value of gain (also contains rampduration)
+ * @task_id: task index
+ * @loc_id: location/position index
+ * @module_id: module index
+ * @mute: flag that indicates whether this was called from the
+ * digital_mute callback or directly. If called from the
+ * digital_mute callback, module will be muted/unmuted based on this
+ * flag. The flag is always 0 if called directly.
*
* Called with sst_data.lock held
*
@@ -463,7 +470,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = 0;
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
struct sst_gain_value *gv = mc->gain_val;
@@ -534,6 +541,7 @@ static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10,
/* Look up table to convert MIXER SW bit regs to SWM inputs */
static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+ [SST_IP_MODEM] = SST_SWM_IN_MODEM,
[SST_IP_CODEC0] = SST_SWM_IN_CODEC0,
[SST_IP_CODEC1] = SST_SWM_IN_CODEC1,
[SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP,
@@ -549,9 +557,12 @@ static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
/**
* fill_swm_input - fill in the SWM input ids given the register
+ * @cmpnt: ASoC component
+ * @swm_input: array of swm_input_ids
+ * @reg: the register value is a bit-field inicated which mixer inputs are ON.
*
- * The register value is a bit-field inicated which mixer inputs are ON. Use the
- * lookup table to get the input-id and fill it in the structure.
+ * Use the lookup table to get the input-id and fill it in the
+ * structure.
*/
static int fill_swm_input(struct snd_soc_component *cmpnt,
struct swm_input_ids *swm_input, unsigned int reg)
@@ -582,7 +593,7 @@ static int fill_swm_input(struct snd_soc_component *cmpnt,
}
-/**
+/*
* called with lock held
*/
static int sst_set_pipe_gain(struct sst_ids *ids,
@@ -591,7 +602,7 @@ static int sst_set_pipe_gain(struct sst_ids *ids,
int ret = 0;
struct sst_gain_mixer_control *mc;
struct sst_gain_value *gv;
- struct sst_module *gain = NULL;
+ struct sst_module *gain;
list_for_each_entry(gain, &ids->gain_list, node) {
struct snd_kcontrol *kctl = gain->kctl;
@@ -626,7 +637,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
* inputs as an IPC to the DSP.
*/
for (i = 0; i < w->num_kcontrols; i++) {
- if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+ if (snd_soc_dapm_kcontrol_get_value(w->kcontrols[i])) {
mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
val |= 1 << mc->shift;
}
@@ -646,7 +657,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
set_mixer = false;
}
- if (set_mixer == false)
+ if (!set_mixer)
return 0;
if (SND_SOC_DAPM_EVENT_ON(event) ||
@@ -674,6 +685,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
/* SBA mixers - 16 inputs */
#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \
static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0), \
SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \
SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \
SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \
@@ -684,6 +696,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
}
#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \
+ { mix_name, "modem_in Switch", "modem_in" }, \
{ mix_name, "codec_in0 Switch", "codec_in0" }, \
{ mix_name, "codec_in1 Switch", "codec_in1" }, \
{ mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \
@@ -710,9 +723,10 @@ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(__maybe_unused sst_mix_voip_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls);
/*
* sst_handle_vb_timer - Start/Stop the DSP scheduler
@@ -797,13 +811,11 @@ static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,
switch (format) {
case SND_SOC_DAIFMT_NB_NF:
- return SSP_FS_ACTIVE_LOW;
- case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
return SSP_FS_ACTIVE_HIGH;
+ case SND_SOC_DAIFMT_NB_IF:
case SND_SOC_DAIFMT_IB_IF:
return SSP_FS_ACTIVE_LOW;
- case SND_SOC_DAIFMT_IB_NF:
- return SSP_FS_ACTIVE_HIGH;
default:
dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);
}
@@ -815,14 +827,14 @@ static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt)
{
int format;
- format = (fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ format = (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
switch (format) {
- case SND_SOC_DAIFMT_CBS_CFS:
- return SSP_MODE_MASTER;
- case SND_SOC_DAIFMT_CBM_CFM:
- return SSP_MODE_SLAVE;
+ case SND_SOC_DAIFMT_BP_FP:
+ return SSP_MODE_PROVIDER;
+ case SND_SOC_DAIFMT_BC_FC:
+ return SSP_MODE_CONSUMER;
default:
dev_err(dai->dev, "Invalid ssp protocol: %d\n", format);
}
@@ -885,7 +897,7 @@ int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-/**
+/*
* sst_ssp_config - contains SSP configuration for media UC
* this can be overwritten by set_dai_xxx APIs
*/
@@ -893,7 +905,7 @@ static const struct sst_ssp_config sst_ssp_configs = {
.ssp_id = SSP_CODEC,
.bits_per_slot = 24,
.slots = 4,
- .ssp_mode = SSP_MODE_MASTER,
+ .ssp_mode = SSP_MODE_PROVIDER,
.pcm_mode = SSP_PCM_MODE_NETWORK,
.duplex = SSP_DUPLEX,
.ssp_protocol = SSP_MODE_PCM,
@@ -931,17 +943,26 @@ void sst_fill_ssp_defaults(struct snd_soc_dai *dai)
int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
{
struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
- const struct sst_ssp_config *config;
+ int ssp_id;
+
+ dev_dbg(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
- dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+ if (strcmp(id, "ssp0-port") == 0)
+ ssp_id = SSP_MODEM;
+ else if (strcmp(id, "ssp2-port") == 0)
+ ssp_id = SSP_CODEC;
+ else {
+ dev_dbg(dai->dev, "port %s is not supported\n", id);
+ return -1;
+ }
SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);
drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;
drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
- sizeof(struct sst_dsp_header);
- config = &sst_ssp_configs;
- dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+ drv->ssp_cmd.selection = ssp_id;
+ dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id);
if (enable)
drv->ssp_cmd.switch_state = SST_SWITCH_ON;
@@ -963,7 +984,9 @@ static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
if (SND_SOC_DAPM_EVENT_ON(event)) {
+ mutex_lock(&drv->lock);
ret = sst_send_slot_map(drv);
+ mutex_unlock(&drv->lock);
if (ret)
return ret;
ret = sst_send_pipe_module_params(w, k);
@@ -1047,8 +1070,10 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
}
static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+ SST_AIF_IN("modem_in", sst_set_be_modules),
SST_AIF_IN("codec_in0", sst_set_be_modules),
SST_AIF_IN("codec_in1", sst_set_be_modules),
+ SST_AIF_OUT("modem_out", sst_set_be_modules),
SST_AIF_OUT("codec_out0", sst_set_be_modules),
SST_AIF_OUT("codec_out1", sst_set_be_modules),
@@ -1072,8 +1097,8 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
- SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
- SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_STEREO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_STEREO, sst_set_media_loop),
SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
/* Media Mixers */
@@ -1103,6 +1128,9 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
sst_mix_codec0_controls, sst_swm_mixer_event),
SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
sst_mix_codec1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM,
+ sst_mix_modem_controls, sst_swm_mixer_event),
+
};
static const struct snd_soc_dapm_route intercon[] = {
@@ -1148,6 +1176,9 @@ static const struct snd_soc_dapm_route intercon[] = {
SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
{"codec_out1", NULL, "codec_out1 mix 0"},
SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+ {"modem_out", NULL, "modem_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"),
+
};
static const char * const slot_names[] = {
@@ -1217,6 +1248,9 @@ static const struct snd_kcontrol_new sst_gain_controls[] = {
SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+ SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]),
+ SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]),
+
};
#define SST_GAIN_NUM_CONTROLS 3
@@ -1282,6 +1316,9 @@ static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
/**
* sst_send_pipe_gains - send gains for the front-end DAIs
+ * @dai: front-end dai
+ * @stream: direction
+ * @mute: boolean indicating mute status
*
* The gains in the pipes connected to the front-ends are muted/unmuted
* automatically via the digital_mute() DAPM callback. This function sends the
@@ -1290,15 +1327,13 @@ static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
{
struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_dapm_widget *w;
- struct snd_soc_dapm_path *p = NULL;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);
+ struct snd_soc_dapm_path *p;
dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+ dev_dbg(dai->dev, "Stream name=%s\n", w->name);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev_dbg(dai->dev, "Stream name=%s\n",
- dai->playback_widget->name);
- w = dai->playback_widget;
snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->connected && !p->connected(w, p->sink))
continue;
@@ -1315,11 +1350,8 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
}
}
} else {
- dev_dbg(dai->dev, "Stream name=%s\n",
- dai->capture_widget->name);
- w = dai->capture_widget;
snd_soc_dapm_widget_for_each_source_path(w, p) {
- if (p->connected && !p->connected(w, p->sink))
+ if (p->connected && !p->connected(w, p->source))
continue;
if (p->connect && p->source->power &&
@@ -1339,7 +1371,9 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
/**
* sst_fill_module_list - populate the list of modules/gains for a pipe
- *
+ * @kctl: kcontrol pointer
+ * @w: dapm widget
+ * @type: widget type
*
* Fills the widget pointer in the kcontrol private data, and also fills the
* kcontrol pointer in the widget private data.
@@ -1353,7 +1387,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
static int sst_fill_module_list(struct snd_kcontrol *kctl,
struct snd_soc_dapm_widget *w, int type)
{
- struct sst_module *module = NULL;
+ struct sst_module *module;
struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
struct sst_ids *ids = w->priv;
int ret = 0;
@@ -1385,18 +1419,19 @@ static int sst_fill_module_list(struct snd_kcontrol *kctl,
/**
* sst_fill_widget_module_info - fill list of gains/algos for the pipe
- * @widget: pipe modelled as a DAPM widget
+ * @w: pipe modeled as a DAPM widget
+ * @component: ASoC component
*
* Fill the list of gains/algos for the widget by looking at all the card
* controls and comparing the name of the widget with the first part of control
* name. First part of control name contains the pipe name (widget name).
*/
static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
- struct snd_soc_platform *platform)
+ struct snd_soc_component *component)
{
struct snd_kcontrol *kctl;
int index, ret = 0;
- struct snd_card *card = platform->component.card->snd_card;
+ struct snd_card *card = component->card->snd_card;
char *idx;
down_read(&card->controls_rwsem);
@@ -1445,14 +1480,16 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
/**
* sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ * @component: ASoC component
+ * @ids: sst_ids array
*/
-static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+static void sst_fill_linked_widgets(struct snd_soc_component *component,
struct sst_ids *ids)
{
struct snd_soc_dapm_widget *w;
unsigned int len = strlen(ids->parent_wname);
- list_for_each_entry(w, &platform->component.card->widgets, list) {
+ list_for_each_entry(w, &component->card->widgets, list) {
if (!strncmp(ids->parent_wname, w->name, len)) {
ids->parent_w = w;
break;
@@ -1462,42 +1499,42 @@ static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
/**
* sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ * @component: ASoC component
*/
-static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+static int sst_map_modules_to_pipe(struct snd_soc_component *component)
{
struct snd_soc_dapm_widget *w;
int ret = 0;
- list_for_each_entry(w, &platform->component.card->widgets, list) {
+ list_for_each_entry(w, &component->card->widgets, list) {
if (is_sst_dapm_widget(w) && (w->priv)) {
struct sst_ids *ids = w->priv;
- dev_dbg(platform->dev, "widget type=%d name=%s\n",
+ dev_dbg(component->dev, "widget type=%d name=%s\n",
w->id, w->name);
INIT_LIST_HEAD(&ids->algo_list);
INIT_LIST_HEAD(&ids->gain_list);
- ret = sst_fill_widget_module_info(w, platform);
+ ret = sst_fill_widget_module_info(w, component);
if (ret < 0)
return ret;
/* fill linked widgets */
if (ids->parent_wname != NULL)
- sst_fill_linked_widgets(platform, ids);
+ sst_fill_linked_widgets(component, ids);
}
}
return 0;
}
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+int sst_dsp_init_v2_dpcm(struct snd_soc_component *component)
{
int i, ret = 0;
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(&platform->component);
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct sst_data *drv = snd_soc_component_get_drvdata(component);
unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
- drv->byte_stream = devm_kzalloc(platform->dev,
+ drv->byte_stream = devm_kzalloc(component->dev,
SST_MAX_BIN_BYTES, GFP_KERNEL);
if (!drv->byte_stream)
return -ENOMEM;
@@ -1506,7 +1543,7 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
ARRAY_SIZE(sst_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon,
ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(dapm->card);
+ snd_soc_dapm_new_widgets(component->card);
for (i = 0; i < gains; i++) {
sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
@@ -1515,26 +1552,26 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
}
- ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+ ret = snd_soc_add_component_controls(component, sst_gain_controls,
ARRAY_SIZE(sst_gain_controls));
if (ret)
return ret;
/* Initialize algo control params */
- ret = sst_algo_control_init(platform->dev);
+ ret = sst_algo_control_init(component->dev);
if (ret)
return ret;
- ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
+ ret = snd_soc_add_component_controls(component, sst_algo_controls,
ARRAY_SIZE(sst_algo_controls));
if (ret)
return ret;
- ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+ ret = snd_soc_add_component_controls(component, sst_slot_controls,
ARRAY_SIZE(sst_slot_controls));
if (ret)
return ret;
- ret = sst_map_modules_to_pipe(platform);
+ ret = sst_map_modules_to_pipe(component);
return ret;
}
diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h
index e0113112f668..23bf37544a8d 100644
--- a/sound/soc/intel/atom/sst-atom-controls.h
+++ b/sound/soc/intel/atom/sst-atom-controls.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* sst-atom-controls.h - Intel MID Platform driver header file
*
@@ -7,17 +8,7 @@
* Samreen Nilofer <samreen.nilofer@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
*/
#ifndef __SST_ATOM_CONTROLS_H__
@@ -35,6 +26,8 @@ enum {
/* define a bit for each mixer input */
#define SST_MIX_IP(x) (x)
+#define SST_IP_MODEM SST_MIX_IP(0)
+#define SST_IP_BT SST_MIX_IP(1)
#define SST_IP_CODEC0 SST_MIX_IP(2)
#define SST_IP_CODEC1 SST_MIX_IP(3)
#define SST_IP_LOOP0 SST_MIX_IP(4)
@@ -63,6 +56,7 @@ enum {
* Audio DSP Path Ids. Specified by the audio DSP FW
*/
enum sst_path_index {
+ SST_PATH_INDEX_MODEM_OUT = (0x00 << SST_PATH_ID_SHIFT),
SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT),
SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT),
@@ -80,6 +74,7 @@ enum sst_path_index {
/* Start of input paths */
+ SST_PATH_INDEX_MODEM_IN = (0x80 << SST_PATH_ID_SHIFT),
SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT),
SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT),
@@ -105,6 +100,7 @@ enum sst_path_index {
* path IDs
*/
enum sst_swm_inputs {
+ SST_SWM_IN_MODEM = (SST_PATH_INDEX_MODEM_IN | SST_DEFAULT_CELL_NBR),
SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR),
SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR),
SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR),
@@ -124,6 +120,7 @@ enum sst_swm_inputs {
* path IDs
*/
enum sst_swm_outputs {
+ SST_SWM_OUT_MODEM = (SST_PATH_INDEX_MODEM_OUT | SST_DEFAULT_CELL_NBR),
SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR),
SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR),
SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR),
@@ -413,7 +410,7 @@ struct sst_cmd_set_gain_dual {
struct sst_cmd_set_params {
struct sst_destination_id dst;
u16 command_id;
- char params[0];
+ char params[];
} __packed;
@@ -442,8 +439,8 @@ struct sst_cmd_tone_stop {
} __packed;
enum sst_ssp_mode {
- SSP_MODE_MASTER = 0,
- SSP_MODE_SLAVE = 1,
+ SSP_MODE_PROVIDER = 0,
+ SSP_MODE_CONSUMER = 1,
};
enum sst_ssp_pcm_mode {
diff --git a/sound/soc/intel/atom/sst-mfld-dsp.h b/sound/soc/intel/atom/sst-mfld-dsp.h
index 4257263157cd..c8f0816edb53 100644
--- a/sound/soc/intel/atom/sst-mfld-dsp.h
+++ b/sound/soc/intel/atom/sst-mfld-dsp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SST_MFLD_DSP_H__
#define __SST_MFLD_DSP_H__
/*
@@ -7,15 +8,6 @@
* Authors: Vinod Koul <vinod.koul@linux.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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
@@ -264,7 +256,7 @@ struct snd_sst_tstamp {
u32 channel_peak[8];
} __packed;
-/* Stream type params struture for Alloc stream */
+/* Stream type params structure for Alloc stream */
struct snd_sst_str_type {
u8 codec_type; /* Codec type */
u8 str_type; /* 1 = voice 2 = music */
@@ -366,7 +358,7 @@ struct snd_wma_params {
u8 reserved; /* reserved */
} __packed;
-/* Codec params struture */
+/* Codec params structure */
union snd_sst_codec_params {
struct snd_pcm_params pcm_params;
struct snd_mp3_params mp3_params;
@@ -435,7 +427,7 @@ struct snd_sst_drop_response {
struct snd_sst_async_msg {
u32 msg_id; /* Async msg id */
- u32 payload[0];
+ u32 payload[];
};
struct snd_sst_async_err_msg {
@@ -522,7 +514,7 @@ struct snd_sst_bytes_v2 {
u8 pipe_id;
u8 rsvd;
u16 len;
- char bytes[0];
+ char bytes[];
};
#define MAX_VTSV_FILES 2
diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c
index 395168986462..9dfb0a814b94 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_mfld_platform.c - Intel MID Platform driver
*
@@ -5,15 +6,6 @@
* Author: Vinod Koul <vinod.koul@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -26,6 +18,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
+#include <asm/div64.h>
#include "sst-mfld-platform.h"
/* compress stream operations */
@@ -47,10 +40,10 @@ static void sst_drain_notify(void *arg)
snd_compr_drain_notify(cstream);
}
-static int sst_platform_compr_open(struct snd_compr_stream *cstream)
+static int sst_platform_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
{
-
- int ret_val = 0;
+ int ret_val;
struct snd_compr_runtime *runtime = cstream->runtime;
struct sst_runtime_stream *stream;
@@ -80,7 +73,8 @@ out_ops:
return ret_val;
}
-static int sst_platform_compr_free(struct snd_compr_stream *cstream)
+static int sst_platform_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
@@ -99,16 +93,15 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
return 0;
}
-static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params)
+static int sst_platform_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
{
struct sst_runtime_stream *stream;
int retval;
struct snd_sst_params str_params;
struct sst_compress_cb cb;
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
+ struct sst_data *ctx = snd_soc_component_get_drvdata(component);
stream = cstream->runtime->private_data;
/* construct fw structure for this*/
@@ -174,7 +167,8 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
return 0;
}
-static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+static int sst_platform_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream, int cmd)
{
struct sst_runtime_stream *stream = cstream->runtime->private_data;
@@ -182,41 +176,49 @@ static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
case SNDRV_PCM_TRIGGER_START:
if (stream->compr_ops->stream_start)
return stream->compr_ops->stream_start(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_STOP:
if (stream->compr_ops->stream_drop)
return stream->compr_ops->stream_drop(sst->dev, stream->id);
+ break;
case SND_COMPR_TRIGGER_DRAIN:
if (stream->compr_ops->stream_drain)
return stream->compr_ops->stream_drain(sst->dev, stream->id);
+ break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
if (stream->compr_ops->stream_partial_drain)
return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (stream->compr_ops->stream_pause)
return stream->compr_ops->stream_pause(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (stream->compr_ops->stream_pause_release)
return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
- default:
- return -EINVAL;
+ break;
}
+ return -EINVAL;
}
-static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp)
+static int sst_platform_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp64 *tstamp)
{
struct sst_runtime_stream *stream;
+ u64 temp_copied_total = tstamp->copied_total;
- stream = cstream->runtime->private_data;
+ stream = cstream->runtime->private_data;
stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
- tstamp->byte_offset = tstamp->copied_total %
- (u32)cstream->runtime->buffer_size;
- pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
+ tstamp->byte_offset =
+ do_div(temp_copied_total, cstream->runtime->buffer_size);
+ pr_debug("calc bytes offset/copied bytes as %u\n", tstamp->byte_offset);
return 0;
}
-static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
- size_t bytes)
+static int sst_platform_compr_ack(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ size_t bytes)
{
struct sst_runtime_stream *stream;
@@ -227,8 +229,9 @@ static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
return 0;
}
-static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
- struct snd_compr_caps *caps)
+static int sst_platform_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
@@ -236,8 +239,9 @@ static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
return stream->compr_ops->get_caps(caps);
}
-static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
- struct snd_compr_codec_caps *codec)
+static int sst_platform_compr_get_codec_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
@@ -245,8 +249,9 @@ static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
return stream->compr_ops->get_codec_caps(codec);
}
-static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
- struct snd_compr_metadata *metadata)
+static int sst_platform_compr_set_metadata(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
@@ -254,7 +259,7 @@ static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
}
-struct snd_compr_ops sst_platform_compr_ops = {
+const struct snd_compress_ops sst_platform_compress_ops = {
.open = sst_platform_compr_open,
.free = sst_platform_compr_free,
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 55c33dc76ce4..373d68b4cf88 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_mfld_platform.c - Intel MID Platform driver
*
@@ -6,15 +7,6 @@
* 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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -76,7 +68,7 @@ int sst_unregister_dsp(struct sst_device *dev)
}
EXPORT_SYMBOL_GPL(sst_unregister_dsp);
-static struct snd_pcm_hardware sst_platform_pcm_hw = {
+static const struct snd_pcm_hardware sst_platform_pcm_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_PAUSE |
@@ -135,7 +127,7 @@ static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
snd_pcm_uframes_t period_size;
ssize_t periodbytes;
ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);
+ u32 buffer_addr = substream->runtime->dma_addr;
channels = substream->runtime->channels;
period_size = substream->runtime->period_size;
@@ -190,7 +182,7 @@ int sst_fill_stream_params(void *substream,
map = ctx->pdata->pdev_strm_map;
map_size = ctx->pdata->strm_map_size;
- if (is_compress == true)
+ if (is_compress)
cstream = (struct snd_compr_stream *)substream;
else
pstream = (struct snd_pcm_substream *)substream;
@@ -241,7 +233,6 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
/* set codec params and inform SST driver the same */
sst_fill_pcm_params(substream, &param);
sst_fill_alloc_params(substream, &alloc_params);
- substream->runtime->dma_area = substream->dma_buffer.area;
str_params.sparams = param;
str_params.aparams = alloc_params;
str_params.codec = SST_CODEC_TYPE_PCM;
@@ -282,7 +273,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream =
substream->runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
int ret_val;
dev_dbg(rtd->dev, "setting buffer ptr param\n");
@@ -339,7 +330,18 @@ static int sst_media_open(struct snd_pcm_substream *substream,
ret_val = power_up_sst(stream);
if (ret_val < 0)
- return ret_val;
+ goto out_power_up;
+
+ /*
+ * Make sure the period to be multiple of 1ms to align the
+ * design of firmware. Apply same rule to buffer size to make
+ * sure alsa could always find a value for period size
+ * regardless the buffer size given by user space.
+ */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 48);
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 48);
/* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step(substream->runtime, 0,
@@ -348,8 +350,9 @@ static int sst_media_open(struct snd_pcm_substream *substream,
return snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
out_ops:
- kfree(stream);
mutex_unlock(&sst_lock);
+out_power_up:
+ kfree(stream);
return ret_val;
}
@@ -357,14 +360,14 @@ static void sst_media_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sst_runtime_stream *stream;
- int ret_val = 0, str_id;
+ int str_id;
stream = substream->runtime->private_data;
power_down_sst(stream);
str_id = stream->stream_info.str_id;
if (str_id)
- ret_val = stream->ops->close(sst->dev, str_id);
+ stream->ops->close(sst->dev, str_id);
module_put(sst->dev->driver->owner);
kfree(stream);
}
@@ -373,7 +376,7 @@ static int sst_media_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sst_runtime_stream *stream;
- int ret_val = 0, str_id;
+ int ret_val, str_id;
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
@@ -392,30 +395,15 @@ static int sst_media_prepare(struct snd_pcm_substream *substream,
if (ret_val)
return ret_val;
substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
- return ret_val;
-}
-
-static int sst_media_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
return 0;
}
-static int sst_media_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int sst_enable_ssp(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret = 0;
- if (!dai->active) {
+ if (!snd_soc_dai_active(dai)) {
ret = sst_handle_vb_timer(dai, true);
sst_fill_ssp_defaults(dai);
}
@@ -428,7 +416,7 @@ static int sst_be_hw_params(struct snd_pcm_substream *substream,
{
int ret = 0;
- if (dai->active == 1)
+ if (snd_soc_dai_active(dai) == 1)
ret = send_ssp_cmd(dai, dai->name, 1);
return ret;
}
@@ -437,7 +425,7 @@ static int sst_set_format(struct snd_soc_dai *dai, unsigned int fmt)
{
int ret = 0;
- if (!dai->active)
+ if (!snd_soc_dai_active(dai))
return 0;
ret = sst_fill_ssp_config(dai, fmt);
@@ -452,7 +440,7 @@ static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai,
int slots, int slot_width) {
int ret = 0;
- if (!dai->active)
+ if (!snd_soc_dai_active(dai))
return ret;
ret = sst_fill_ssp_slot(dai, tx_mask, rx_mask, slots, slot_width);
@@ -465,26 +453,25 @@ static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai,
static void sst_disable_ssp(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- if (!dai->active) {
+ if (!snd_soc_dai_active(dai)) {
send_ssp_cmd(dai, dai->name, 0);
sst_handle_vb_timer(dai, false);
}
}
-static struct snd_soc_dai_ops sst_media_dai_ops = {
+static const struct snd_soc_dai_ops sst_media_dai_ops = {
.startup = sst_media_open,
.shutdown = sst_media_close,
.prepare = sst_media_prepare,
- .hw_params = sst_media_hw_params,
- .hw_free = sst_media_hw_free,
.mute_stream = sst_media_digital_mute,
};
-static struct snd_soc_dai_ops sst_compr_dai_ops = {
+static const struct snd_soc_dai_ops sst_compr_dai_ops = {
+ .compress_new = snd_soc_new_compress,
.mute_stream = sst_media_digital_mute,
};
-static struct snd_soc_dai_ops sst_be_dai_ops = {
+static const struct snd_soc_dai_ops sst_be_dai_ops = {
.startup = sst_enable_ssp,
.hw_params = sst_be_hw_params,
.set_fmt = sst_set_format,
@@ -500,15 +487,15 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
.stream_name = "Headset Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Headset Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
@@ -518,16 +505,16 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
.stream_name = "Deepbuffer Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = "compress-cpu-dai",
- .compress_new = snd_soc_new_compress,
.ops = &sst_compr_dai_ops,
.playback = {
.stream_name = "Compress Playback",
+ .channels_min = 1,
},
},
/* BE CPU Dais */
@@ -587,7 +574,8 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
};
-static int sst_platform_open(struct snd_pcm_substream *substream)
+static int sst_soc_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
@@ -599,15 +587,15 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
return 0;
}
-static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int sst_soc_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
int ret_val = 0, str_id;
struct sst_runtime_stream *stream;
int status;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n");
+ dev_dbg(rtd->dev, "%s called\n", __func__);
if (substream->pcm->internal)
return 0;
stream = substream->runtime->private_data;
@@ -647,13 +635,13 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
}
-static snd_pcm_uframes_t sst_platform_pcm_pointer
- (struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream;
int ret_val, status;
struct pcm_stream_info *str_info;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
stream = substream->runtime->private_data;
status = sst_get_stream_status(stream);
@@ -665,56 +653,62 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
return ret_val;
}
- substream->runtime->delay = str_info->pcm_delay;
return str_info->buffer_ptr;
}
-static struct snd_pcm_ops sst_platform_ops = {
- .open = sst_platform_open,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = sst_platform_pcm_trigger,
- .pointer = sst_platform_pcm_pointer,
-};
+static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream = substream->runtime->private_data;
+ struct pcm_stream_info *str_info = &stream->stream_info;
+
+ if (sst_get_stream_status(stream) == SST_PLATFORM_INIT)
+ return 0;
+
+ return str_info->pcm_delay;
+}
-static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int sst_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_pcm *pcm = rtd->pcm;
- int retval = 0;
if (dai->driver->playback.channels_min ||
dai->driver->capture.channels_min) {
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_DMA),
- SST_MIN_BUFFER, SST_MAX_BUFFER);
- if (retval) {
- dev_err(rtd->dev, "dma buffer allocationf fail\n");
- return retval;
- }
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ pcm->card->dev,
+ SST_MIN_BUFFER, SST_MAX_BUFFER);
}
- return retval;
+ return 0;
}
-static int sst_soc_probe(struct snd_soc_platform *platform)
+static int sst_soc_probe(struct snd_soc_component *component)
{
- struct sst_data *drv = dev_get_drvdata(platform->dev);
+ struct sst_data *drv = dev_get_drvdata(component->dev);
- drv->soc_card = platform->component.card;
- return sst_dsp_init_v2_dpcm(platform);
+ drv->soc_card = component->card;
+ return sst_dsp_init_v2_dpcm(component);
}
-static struct snd_soc_platform_driver sst_soc_platform_drv = {
- .probe = sst_soc_probe,
- .ops = &sst_platform_ops,
- .compr_ops = &sst_platform_compr_ops,
- .pcm_new = sst_pcm_new,
-};
+static void sst_soc_remove(struct snd_soc_component *component)
+{
+ struct sst_data *drv = dev_get_drvdata(component->dev);
-static const struct snd_soc_component_driver sst_component = {
- .name = "sst",
-};
+ drv->soc_card = NULL;
+}
+static const struct snd_soc_component_driver sst_soc_platform_drv = {
+ .name = DRV_NAME,
+ .probe = sst_soc_probe,
+ .remove = sst_soc_remove,
+ .open = sst_soc_open,
+ .trigger = sst_soc_trigger,
+ .pointer = sst_soc_pointer,
+ .delay = sst_soc_delay,
+ .compress_ops = &sst_platform_compress_ops,
+ .pcm_construct = sst_soc_pcm_new,
+};
static int sst_platform_probe(struct platform_device *pdev)
{
@@ -739,28 +733,17 @@ static int sst_platform_probe(struct platform_device *pdev)
mutex_init(&drv->lock);
dev_set_drvdata(&pdev->dev, drv);
- ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
- if (ret) {
- dev_err(&pdev->dev, "registering soc platform failed\n");
- return ret;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &sst_component,
+ ret = devm_snd_soc_register_component(&pdev->dev, &sst_soc_platform_drv,
sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "registering cpu dais failed\n");
- snd_soc_unregister_platform(&pdev->dev);
- }
+
return ret;
}
-static int sst_platform_remove(struct platform_device *pdev)
+static void sst_platform_remove(struct platform_device *pdev)
{
-
- snd_soc_unregister_component(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
dev_dbg(&pdev->dev, "sst_platform_remove success\n");
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -770,15 +753,18 @@ static int sst_soc_prepare(struct device *dev)
struct sst_data *drv = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *rtd;
+ if (!drv->soc_card)
+ return 0;
+
/* suspend all pcms first */
snd_soc_suspend(drv->soc_card->dev);
snd_soc_poweroff(drv->soc_card->dev);
/* set the SSPs to idle */
- list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
- struct snd_soc_dai *dai = rtd->cpu_dai;
+ for_each_card_rtds(drv->soc_card, rtd) {
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
- if (dai->active) {
+ if (snd_soc_dai_active(dai)) {
send_ssp_cmd(dai, dai->name, 0);
sst_handle_vb_timer(dai, false);
}
@@ -792,11 +778,14 @@ static void sst_soc_complete(struct device *dev)
struct sst_data *drv = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *rtd;
+ if (!drv->soc_card)
+ return;
+
/* restart SSPs */
- list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
- struct snd_soc_dai *dai = rtd->cpu_dai;
+ for_each_card_rtds(drv->soc_card, rtd) {
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
- if (dai->active) {
+ if (snd_soc_dai_active(dai)) {
sst_handle_vb_timer(dai, true);
send_ssp_cmd(dai, dai->name, 1);
}
@@ -832,4 +821,5 @@ MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-atom-hifi2-platform");
MODULE_ALIAS("platform:sst-mfld-platform");
diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h
index cb32cc7e5ec1..a0e33f7f01c5 100644
--- a/sound/soc/intel/atom/sst-mfld-platform.h
+++ b/sound/soc/intel/atom/sst-mfld-platform.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* sst_mfld_platform.h - Intel MID Platform driver header file
*
@@ -6,15 +7,6 @@
* 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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
@@ -25,7 +17,9 @@
#include "sst-atom-controls.h"
extern struct sst_device *sst;
-extern struct snd_compr_ops sst_platform_compr_ops;
+extern const struct snd_compress_ops sst_platform_compress_ops;
+
+#define DRV_NAME "sst"
#define SST_MONO 1
#define SST_STEREO 2
@@ -111,7 +105,7 @@ struct compress_sst_ops {
int (*stream_pause_release)(struct device *dev, unsigned int str_id);
int (*tstamp)(struct device *dev, unsigned int str_id,
- struct snd_compr_tstamp *tstamp);
+ struct snd_compr_tstamp64 *tstamp);
int (*ack)(struct device *dev, unsigned int str_id,
unsigned long bytes);
int (*close)(struct device *dev, unsigned int str_id);
@@ -155,7 +149,7 @@ struct sst_device {
struct sst_data;
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_dsp_init_v2_dpcm(struct snd_soc_component *component);
int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
@@ -179,6 +173,6 @@ struct sst_data {
struct snd_soc_card *soc_card;
struct sst_cmd_sba_hw_set_ssp ssp_cmd;
};
-int sst_register_dsp(struct sst_device *sst);
-int sst_unregister_dsp(struct sst_device *sst);
+int sst_register_dsp(struct sst_device *dev);
+int sst_unregister_dsp(struct sst_device *dev);
#endif
diff --git a/sound/soc/intel/atom/sst/Makefile b/sound/soc/intel/atom/sst/Makefile
index fd21726361b5..16be0463424d 100644
--- a/sound/soc/intel/atom/sst/Makefile
+++ b/sound/soc/intel/atom/sst/Makefile
@@ -1,7 +1,8 @@
-snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
-snd-intel-sst-pci-objs += sst_pci.o
-snd-intel-sst-acpi-objs += sst_acpi.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-intel-sst-core-y := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-y += sst_pci.o
+snd-intel-sst-acpi-y += sst_acpi.o
-obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
-obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
-obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index a4b458e77089..3c47c8de04b7 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst.c - Intel SST Driver for audio engine
*
@@ -8,31 +9,24 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/firmware.h>
+#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/async.h>
#include <linux/acpi.h>
+#include <linux/sysfs.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h"
#include "sst.h"
-#include "../../common/sst-dsp.h"
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
@@ -55,7 +49,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
union ipc_header_mrfld header;
union sst_imr_reg_mrfld imr;
struct ipc_post *msg = NULL;
- unsigned int size = 0;
+ unsigned int size;
struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
irqreturn_t retval = IRQ_HANDLED;
@@ -70,7 +64,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
header.p.header_high.part.done = 0;
sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
- /* write 1 to clear status register */;
+ /* write 1 to clear status register */
isr.part.done_interrupt = 1;
sst_shim_write64(drv->shim, SST_ISRX, isr.full);
spin_unlock(&drv->ipc_spin_lock);
@@ -121,7 +115,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
{
struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
- struct ipc_post *__msg, *msg = NULL;
+ struct ipc_post *__msg, *msg;
unsigned long irq_flags;
spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
@@ -181,18 +175,19 @@ int sst_driver_ops(struct intel_sst_drv *sst)
{
switch (sst->dev_id) {
- case SST_MRFLD_PCI_ID:
- case SST_BYT_ACPI_ID:
- case SST_CHV_ACPI_ID:
+ case PCI_DEVICE_ID_INTEL_SST_TNG:
+ case PCI_DEVICE_ID_INTEL_SST_BYT:
+ case PCI_DEVICE_ID_INTEL_SST_BSW:
sst->tstamp = SST_TIME_STAMP_MRFLD;
sst->ops = &mrfld_ops;
return 0;
default:
dev_err(sst->dev,
- "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+ "SST Driver capabilities missing for dev_id: %x",
+ sst->dev_id);
return -EINVAL;
- };
+ }
}
void sst_process_pending_msg(struct work_struct *work)
@@ -227,8 +222,13 @@ static void sst_init_locks(struct intel_sst_drv *ctx)
spin_lock_init(&ctx->block_lock);
}
+/*
+ * Driver handles PCI IDs in ACPI - sst_acpi_probe() - and we are using only
+ * device ID part. If real ACPI ID appears, the kstrtouint() returns error, so
+ * we are fine with using unsigned short as dev_id type.
+ */
int sst_alloc_drv_context(struct intel_sst_drv **ctx,
- struct device *dev, unsigned int dev_id)
+ struct device *dev, unsigned short dev_id)
{
*ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
if (!(*ctx))
@@ -241,6 +241,32 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx,
}
EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+static ssize_t firmware_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 &&
+ ctx->fw_version.minor == 0 && ctx->fw_version.build == 0)
+ return sysfs_emit(buf, "FW not yet loaded\n");
+ else
+ return sysfs_emit(buf, "v%02x.%02x.%02x.%02x\n",
+ ctx->fw_version.type, ctx->fw_version.major,
+ ctx->fw_version.minor, ctx->fw_version.build);
+
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+
+static const struct attribute *sst_fw_version_attrs[] = {
+ &dev_attr_firmware_version.attr,
+ NULL,
+};
+
+static const struct attribute_group sst_fw_version_attr_group = {
+ .attrs = (struct attribute **)sst_fw_version_attrs,
+};
+
int sst_context_init(struct intel_sst_drv *ctx)
{
int ret = 0, i;
@@ -304,8 +330,7 @@ int sst_context_init(struct intel_sst_drv *ctx)
ret = -ENOMEM;
goto do_free_mem;
}
- pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
+ cpu_latency_qos_add_request(ctx->qos, PM_QOS_DEFAULT_VALUE);
dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
@@ -314,8 +339,19 @@ int sst_context_init(struct intel_sst_drv *ctx)
dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
goto do_free_mem;
}
+
+ ret = sysfs_create_group(&ctx->dev->kobj,
+ &sst_fw_version_attr_group);
+ if (ret) {
+ dev_err(ctx->dev,
+ "Unable to create sysfs\n");
+ goto err_sysfs;
+ }
+
sst_register(ctx->dev);
return 0;
+err_sysfs:
+ sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
do_free_mem:
destroy_workqueue(ctx->post_msg_wq);
@@ -329,50 +365,18 @@ void sst_context_cleanup(struct intel_sst_drv *ctx)
pm_runtime_disable(ctx->dev);
sst_unregister(ctx->dev);
sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
- flush_scheduled_work();
+ sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
destroy_workqueue(ctx->post_msg_wq);
- pm_qos_remove_request(ctx->qos);
+ cpu_latency_qos_remove_request(ctx->qos);
kfree(ctx->fw_sg_list.src);
kfree(ctx->fw_sg_list.dst);
ctx->fw_sg_list.list_len = 0;
kfree(ctx->fw_in_mem);
ctx->fw_in_mem = NULL;
sst_memcpy_free_resources(ctx);
- ctx = NULL;
}
EXPORT_SYMBOL_GPL(sst_context_cleanup);
-static inline void sst_save_shim64(struct intel_sst_drv *ctx,
- void __iomem *shim,
- struct sst_shim_regs64 *shim_regs)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
-
- shim_regs->imrx = sst_shim_read64(shim, SST_IMRX);
- shim_regs->csr = sst_shim_read64(shim, SST_CSR);
-
-
- spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
-}
-
-static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
- void __iomem *shim,
- struct sst_shim_regs64 *shim_regs)
-{
- unsigned long irq_flags;
-
- /*
- * we only need to restore IMRX for this case, rest will be
- * initialize by FW or driver when firmware is loaded
- */
- spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
- sst_shim_write64(shim, SST_IMRX, shim_regs->imrx);
- sst_shim_write64(shim, SST_CSR, shim_regs->csr);
- spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
-}
-
void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
{
pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
@@ -392,8 +396,6 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
pm_runtime_set_active(ctx->dev);
else
pm_runtime_put_noidle(ctx->dev);
-
- sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
}
EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
@@ -417,8 +419,6 @@ static int intel_sst_runtime_suspend(struct device *dev)
flush_workqueue(ctx->post_msg_wq);
ctx->ops->reset(ctx);
- /* save the shim registers because PMC doesn't save state */
- sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
return ret;
}
@@ -427,7 +427,7 @@ static int intel_sst_suspend(struct device *dev)
{
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
struct sst_fw_save *fw_save;
- int i, ret = 0;
+ int i, ret;
/* check first if we are already in SW reset */
if (ctx->sst_state == SST_RESET)
@@ -441,9 +441,16 @@ static int intel_sst_suspend(struct device *dev)
struct stream_info *stream = &ctx->streams[i];
if (stream->status == STREAM_RUNNING) {
- dev_err(dev, "stream %d is running, cant susupend, abort\n", i);
+ dev_err(dev, "stream %d is running, can't suspend, abort\n", i);
return -EBUSY;
}
+
+ if (ctx->pdata->streams_lost_on_suspend) {
+ stream->resume_status = stream->status;
+ stream->resume_prev = stream->prev;
+ if (stream->status != STREAM_UN_INIT)
+ sst_free_stream(ctx, i);
+ }
}
synchronize_irq(ctx->irq_num);
flush_workqueue(ctx->post_msg_wq);
@@ -459,23 +466,23 @@ static int intel_sst_suspend(struct device *dev)
fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
if (!fw_save)
return -ENOMEM;
- fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
+ fw_save->iram = kvzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
if (!fw_save->iram) {
ret = -ENOMEM;
goto iram;
}
- fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
+ fw_save->dram = kvzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
if (!fw_save->dram) {
ret = -ENOMEM;
goto dram;
}
- fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
+ fw_save->sram = kvzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
if (!fw_save->sram) {
ret = -ENOMEM;
goto sram;
}
- fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
+ fw_save->ddr = kvzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
if (!fw_save->ddr) {
ret = -ENOMEM;
goto ddr;
@@ -490,11 +497,11 @@ static int intel_sst_suspend(struct device *dev)
ctx->ops->reset(ctx);
return 0;
ddr:
- kfree(fw_save->sram);
+ kvfree(fw_save->sram);
sram:
- kfree(fw_save->dram);
+ kvfree(fw_save->dram);
dram:
- kfree(fw_save->iram);
+ kvfree(fw_save->iram);
iram:
kfree(fw_save);
return ret;
@@ -504,8 +511,8 @@ static int intel_sst_resume(struct device *dev)
{
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
struct sst_fw_save *fw_save = ctx->fw_save;
- int ret = 0;
struct sst_block *block;
+ int i, ret = 0;
if (!fw_save)
return 0;
@@ -522,10 +529,10 @@ static int intel_sst_resume(struct device *dev)
memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
- kfree(fw_save->sram);
- kfree(fw_save->dram);
- kfree(fw_save->iram);
- kfree(fw_save->ddr);
+ kvfree(fw_save->sram);
+ kvfree(fw_save->dram);
+ kvfree(fw_save->iram);
+ kvfree(fw_save->ddr);
kfree(fw_save);
block = sst_create_block(ctx, 0, FW_DWNL_ID);
@@ -545,6 +552,21 @@ static int intel_sst_resume(struct device *dev)
sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
}
+ if (ctx->pdata->streams_lost_on_suspend) {
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ if (stream->resume_status != STREAM_UN_INIT) {
+ dev_dbg(ctx->dev, "Re-allocing stream %d status %d prev %d\n",
+ i, stream->resume_status,
+ stream->resume_prev);
+ sst_realloc_stream(ctx, i);
+ stream->status = stream->resume_status;
+ stream->prev = stream->resume_prev;
+ }
+ }
+ }
+
sst_free_block(ctx, block);
return ret;
}
diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h
index 3f493862e98d..c43946c5ecee 100644
--- a/sound/soc/intel/atom/sst/sst.h
+++ b/sound/soc/intel/atom/sst/sst.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* sst.h - Intel SST Driver for audio engine
*
@@ -8,15 +9,6 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Common private declarations for SST
@@ -28,9 +20,6 @@
/* driver names */
#define SST_DRV_NAME "intel_sst_driver"
-#define SST_MRFLD_PCI_ID 0x119A
-#define SST_BYT_ACPI_ID 0x80860F28
-#define SST_CHV_ACPI_ID 0x808622A8
#define SST_SUSPEND_DELAY 2000
#define FW_CONTEXT_MEM (64*1024)
@@ -42,6 +31,13 @@
#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
#define MRFLD_FW_BSS_RESET_BIT 0
+/* SST Shim register map */
+#define SST_CSR 0x00
+#define SST_ISRX 0x18
+#define SST_IMRX 0x28
+#define SST_IPCX 0x38 /* IPC IA -> SST */
+#define SST_IPCD 0x40 /* IPC SST -> IA */
+
extern const struct dev_pm_ops intel_sst_pm;
enum sst_states {
SST_FW_LOADING = 1,
@@ -65,9 +61,7 @@ enum sst_stream_states {
STREAM_UN_INIT = 0, /* Freed/Not used stream */
STREAM_RUNNING = 1, /* Running */
STREAM_PAUSED = 2, /* Paused stream */
- STREAM_DECODE = 3, /* stream is in decoding only state */
- STREAM_INIT = 4, /* stream init, waiting for data */
- STREAM_RESET = 5, /* force reset on recovery */
+ STREAM_INIT = 3, /* stream init, waiting for data */
};
enum sst_ram_type {
@@ -181,22 +175,22 @@ struct sst_block {
*
* @status : stream current state
* @prev : stream prev state
- * @ops : stream operation pb/cp/drm...
- * @bufs: stream buffer list
+ * @resume_status : stream current state to restore on resume
+ * @resume_prev : stream prev state to restore on resume
* @lock : stream mutex for protecting state
+ * @alloc_param : parameters used for stream (re-)allocation
* @pcm_substream : PCM substream
* @period_elapsed : PCM period elapsed callback
* @sfreq : stream sampling freq
- * @str_type : stream type
* @cumm_bytes : cummulative bytes decoded
- * @str_type : stream type
- * @src : stream source
*/
struct stream_info {
unsigned int status;
unsigned int prev;
- unsigned int ops;
+ unsigned int resume_status;
+ unsigned int resume_prev;
struct mutex lock;
+ struct snd_sst_alloc_mrfld alloc_param;
void *pcm_substream;
void (*period_elapsed)(void *pcm_substream);
@@ -212,7 +206,6 @@ struct stream_info {
unsigned int num_ch;
unsigned int pipe_id;
- unsigned int str_id;
unsigned int task_id;
};
@@ -317,31 +310,11 @@ struct sst_ipc_reg {
int ipcd;
};
-struct sst_shim_regs64 {
- u64 csr;
- u64 pisr;
- u64 pimr;
- u64 isrx;
- u64 isrd;
- u64 imrx;
- u64 imrd;
- u64 ipcx;
- u64 ipcd;
- u64 isrsc;
- u64 isrlpesc;
- u64 imrsc;
- u64 imrlpesc;
- u64 ipcsc;
- u64 ipclpesc;
- u64 clkctl;
- u64 csr2;
-};
-
struct sst_fw_save {
- void *iram;
- void *dram;
- void *sram;
- void *ddr;
+ void *iram; /* allocated via kvmalloc() */
+ void *dram; /* allocated via kvmalloc() */
+ void *sram; /* allocated via kvmalloc() */
+ void *ddr; /* allocated via kvmalloc() */
};
/**
@@ -356,7 +329,6 @@ struct sst_fw_save {
* @dram : SST DRAM pointer
* @pdata : SST info passed as a part of pci platform data
* @shim_phy_add : SST shim phy addr
- * @shim_regs64: Struct to save shim registers
* @ipc_dispatch_list : ipc messages dispatched
* @rx_list : to copy the process_reply/process_msg from DSP
* @ipc_post_msg_wq : wq to post IPC messages context
@@ -383,7 +355,7 @@ struct sst_fw_save {
struct intel_sst_drv {
int sst_state;
int irq_num;
- unsigned int dev_id;
+ unsigned short dev_id;
void __iomem *ddr;
void __iomem *shim;
void __iomem *mailbox;
@@ -398,7 +370,6 @@ struct intel_sst_drv {
unsigned int ddr_end;
unsigned int ddr_base;
unsigned int mailbox_recv_offset;
- struct sst_shim_regs64 *shim_regs64;
struct list_head block_list;
struct list_head ipc_dispatch_list;
struct sst_platform_info *pdata;
@@ -436,6 +407,7 @@ struct intel_sst_drv {
*/
char firmware_name[FW_NAME_SIZE];
+ struct snd_sst_fw_version fw_version;
struct sst_fw_save *fw_save;
};
@@ -459,37 +431,33 @@ struct intel_sst_ops {
void (*post_download)(struct intel_sst_drv *sst);
};
-int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
-int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
- struct snd_sst_bytes_v2 *sbytes);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_bytes_v2 *bytes);
int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
int sst_set_metadata(int str_id, char *params);
-int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+int sst_get_stream(struct intel_sst_drv *ctx,
struct snd_sst_params *str_param);
-int sst_get_stream_allocated(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param,
- struct snd_sst_lib_download **lib_dnld);
int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
int str_id, bool partial_drain);
-int sst_post_message_mrfld(struct intel_sst_drv *ctx,
- struct ipc_post *msg, bool sync);
-void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
-int sst_start_mrfld(struct intel_sst_drv *ctx);
-int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
-void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
-
-int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *ipc_msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx);
+
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx);
int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
void sst_post_download_mrfld(struct intel_sst_drv *ctx);
int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
-void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx);
-int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block);
int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
struct sst_block *block);
int sst_create_ipc_msg(struct ipc_post **arg, bool large);
@@ -497,7 +465,6 @@ int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
void sst_clean_stream(struct stream_info *stream);
int intel_sst_register_compress(struct intel_sst_drv *sst);
int intel_sst_remove_compress(struct intel_sst_drv *sst);
-void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
int sst_send_sync_msg(int ipc, int str_id);
int sst_get_num_channel(struct snd_sst_params *str_param);
int sst_get_sfreq(struct snd_sst_params *str_param);
@@ -521,9 +488,7 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
bool large, bool fill_dsp, bool sync, bool response);
void sst_process_pending_msg(struct work_struct *work);
-int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
-void sst_init_stream(struct stream_info *stream,
- int codec, int sst_id, int ops, u8 slot);
+int sst_assign_pvt_id(struct intel_sst_drv *drv);
int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
int str_id);
@@ -549,7 +514,7 @@ int sst_register(struct device *);
int sst_unregister(struct device *);
int sst_alloc_drv_context(struct intel_sst_drv **ctx,
- struct device *dev, unsigned int dev_id);
+ struct device *dev, unsigned short dev_id);
int sst_context_init(struct intel_sst_drv *ctx);
void sst_context_cleanup(struct intel_sst_drv *ctx);
void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 4fce03fc1870..73624e1b138a 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
*
@@ -5,32 +6,22 @@
*
* Authors: Ramesh Babu K V <Ramesh.Babu@intel.com>
* Authors: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/fs.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/io.h>
-#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
+#include <linux/dmi.h>
#include <linux/acpi.h>
#include <asm/platform_sst_audio.h>
#include <sound/core.h>
+#include <sound/intel-dsp-config.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
#include <acpi/acbuffer.h>
@@ -38,9 +29,10 @@
#include <acpi/platform/aclinux.h>
#include <acpi/actypes.h>
#include <acpi/acpi_bus.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
#include "../sst-mfld-platform.h"
-#include "../../common/sst-dsp.h"
-#include "../../common/sst-acpi.h"
+#include "../../common/soc-intel-quirks.h"
#include "sst.h"
/* LPE viewpoint addresses */
@@ -112,16 +104,61 @@ static const struct sst_res_info byt_rvp_res_info = {
.acpi_ipc_irq_index = 5,
};
+/* BYTCR has different BIOS from BYT */
+static const struct sst_res_info bytcr_res_info = {
+ .shim_offset = 0x140000,
+ .shim_size = 0x000100,
+ .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+ .ssp0_offset = 0xa0000,
+ .ssp0_size = 0x1000,
+ .dma0_offset = 0x98000,
+ .dma0_size = 0x4000,
+ .dma1_offset = 0x9c000,
+ .dma1_size = 0x4000,
+ .iram_offset = 0x0c0000,
+ .iram_size = 0x14000,
+ .dram_offset = 0x100000,
+ .dram_size = 0x28000,
+ .mbox_offset = 0x144000,
+ .mbox_size = 0x1000,
+ .acpi_lpe_res_index = 0,
+ .acpi_ddr_index = 2,
+ .acpi_ipc_irq_index = 0
+};
+
+/* For "LPE0F28" ACPI device found on some Android factory OS models */
+static const struct sst_res_info lpe8086_res_info = {
+ .shim_offset = 0x140000,
+ .shim_size = 0x000100,
+ .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+ .ssp0_offset = 0xa0000,
+ .ssp0_size = 0x1000,
+ .dma0_offset = 0x98000,
+ .dma0_size = 0x4000,
+ .dma1_offset = 0x9c000,
+ .dma1_size = 0x4000,
+ .iram_offset = 0x0c0000,
+ .iram_size = 0x14000,
+ .dram_offset = 0x100000,
+ .dram_size = 0x28000,
+ .mbox_offset = 0x144000,
+ .mbox_size = 0x1000,
+ .acpi_lpe_res_index = 1,
+ .acpi_ddr_index = 0,
+ .acpi_ipc_irq_index = 0
+};
+
static struct sst_platform_info byt_rvp_platform_data = {
.probe_data = &byt_fwparse_info,
.ipc_info = &byt_ipc_info,
.lib_info = &byt_lib_dnld_info,
.res_info = &byt_rvp_res_info,
.platform = "sst-mfld-platform",
+ .streams_lost_on_suspend = true,
};
/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
- * so pdata is same as Baytrail.
+ * so pdata is same as Baytrail, minus the streams_lost_on_suspend quirk.
*/
static struct sst_platform_info chv_platform_data = {
.probe_data = &byt_fwparse_info,
@@ -141,7 +178,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
ctx->pdata->res_info->acpi_lpe_res_index);
if (!rsrc) {
- dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+ dev_err(ctx->dev, "Invalid SHIM base from IFWI\n");
return -EIO;
}
dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
@@ -150,29 +187,29 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
- ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+ ctx->iram = devm_ioremap(ctx->dev, ctx->iram_base,
ctx->pdata->res_info->iram_size);
if (!ctx->iram) {
- dev_err(ctx->dev, "unable to map IRAM");
+ dev_err(ctx->dev, "unable to map IRAM\n");
return -EIO;
}
ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
- ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+ ctx->dram = devm_ioremap(ctx->dev, ctx->dram_base,
ctx->pdata->res_info->dram_size);
if (!ctx->dram) {
- dev_err(ctx->dev, "unable to map DRAM");
+ dev_err(ctx->dev, "unable to map DRAM\n");
return -EIO;
}
ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
- ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+ ctx->shim = devm_ioremap(ctx->dev, ctx->shim_phy_add,
ctx->pdata->res_info->shim_size);
if (!ctx->shim) {
- dev_err(ctx->dev, "unable to map SHIM");
+ dev_err(ctx->dev, "unable to map SHIM\n");
return -EIO;
}
@@ -182,10 +219,10 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
/* Get mailbox addr */
ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
- ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+ ctx->mailbox = devm_ioremap(ctx->dev, ctx->mailbox_add,
ctx->pdata->res_info->mbox_size);
if (!ctx->mailbox) {
- dev_err(ctx->dev, "unable to map mailbox");
+ dev_err(ctx->dev, "unable to map mailbox\n");
return -EIO;
}
@@ -195,22 +232,25 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
ctx->pdata->res_info->acpi_ddr_index);
if (!rsrc) {
- dev_err(ctx->dev, "Invalid DDR base from IFWI");
+ dev_err(ctx->dev, "Invalid DDR base from IFWI\n");
return -EIO;
}
ctx->ddr_base = rsrc->start;
ctx->ddr_end = rsrc->end;
dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
- ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+ ctx->ddr = devm_ioremap(ctx->dev, ctx->ddr_base,
resource_size(rsrc));
if (!ctx->ddr) {
- dev_err(ctx->dev, "unable to map DDR");
+ dev_err(ctx->dev, "unable to map DDR\n");
return -EIO;
}
/* Find the IRQ */
ctx->irq_num = platform_get_irq(pdev,
ctx->pdata->res_info->acpi_ipc_irq_index);
+ if (ctx->irq_num <= 0)
+ return ctx->irq_num < 0 ? ctx->irq_num : -EIO;
+
return 0;
}
@@ -220,7 +260,7 @@ static int sst_acpi_probe(struct platform_device *pdev)
int ret = 0;
struct intel_sst_drv *ctx;
const struct acpi_device_id *id;
- struct sst_acpi_mach *mach;
+ struct snd_soc_acpi_mach *mach;
struct platform_device *mdev;
struct platform_device *plat_dev;
struct sst_platform_info *pdata;
@@ -229,24 +269,72 @@ static int sst_acpi_probe(struct platform_device *pdev)
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return -ENODEV;
- dev_dbg(dev, "for %s", id->id);
- mach = (struct sst_acpi_mach *)id->driver_data;
- mach = sst_acpi_find_machine(mach);
+ ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
+ dev_dbg(dev, "SST ACPI driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(dev, "for %s\n", id->id);
+
+ mach = (struct snd_soc_acpi_mach *)id->driver_data;
+ mach = snd_soc_acpi_find_machine(mach);
if (mach == NULL) {
dev_err(dev, "No matching machine driver found\n");
return -ENODEV;
}
+
+ if (soc_intel_is_byt())
+ mach->pdata = &byt_rvp_platform_data;
+ else
+ mach->pdata = &chv_platform_data;
pdata = mach->pdata;
- ret = kstrtouint(id->id, 16, &dev_id);
- if (ret < 0) {
- dev_err(dev, "Unique device id conversion error: %d\n", ret);
- return ret;
+ if (!strcmp(id->id, "LPE0F28")) {
+ struct resource *rsrc;
+
+ /* Use regular BYT SST PCI VID:PID */
+ dev_id = 0x80860F28;
+ byt_rvp_platform_data.res_info = &lpe8086_res_info;
+
+ /*
+ * The "LPE0F28" ACPI device has separate IO-mem resources for:
+ * DDR, SHIM, MBOX, IRAM, DRAM, CFG
+ * None of which covers the entire LPE base address range.
+ * lpe8086_res_info.acpi_lpe_res_index points to the SHIM.
+ * Patch this to cover the entire base address range as expected
+ * by sst_platform_get_resources().
+ */
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ pdata->res_info->acpi_lpe_res_index);
+ if (!rsrc) {
+ dev_err(dev, "Invalid SHIM base\n");
+ return -EIO;
+ }
+ rsrc->start -= pdata->res_info->shim_offset;
+ rsrc->end = rsrc->start + 0x200000 - 1;
+ } else {
+ ret = kstrtouint(id->id, 16, &dev_id);
+ if (ret < 0) {
+ dev_err(dev, "Unique device id conversion error: %d\n", ret);
+ return ret;
+ }
+
+ if (soc_intel_is_byt_cr(pdev))
+ byt_rvp_platform_data.res_info = &bytcr_res_info;
}
dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+ ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+ if (ret < 0)
+ return ret;
+
+ /* update machine parameters */
+ mach->mach_params.acpi_ipc_irq_index =
+ pdata->res_info->acpi_ipc_irq_index;
+
plat_dev = platform_device_register_data(dev, pdata->platform, -1,
NULL, 0);
if (IS_ERR(plat_dev)) {
@@ -267,13 +355,9 @@ static int sst_acpi_probe(struct platform_device *pdev)
return PTR_ERR(mdev);
}
- ret = sst_alloc_drv_context(&ctx, dev, dev_id);
- if (ret < 0)
- return ret;
-
/* Fill sst platform data */
ctx->pdata = pdata;
- strcpy(ctx->firmware_name, mach->fw_filename);
+ strscpy(ctx->firmware_name, mach->fw_filename);
ret = sst_platform_get_resources(ctx);
if (ret)
@@ -283,71 +367,32 @@ static int sst_acpi_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- /* need to save shim registers in BYT */
- ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
- GFP_KERNEL);
- if (!ctx->shim_regs64) {
- ret = -ENOMEM;
- goto do_sst_cleanup;
- }
-
sst_configure_runtime_pm(ctx);
platform_set_drvdata(pdev, ctx);
return ret;
-
-do_sst_cleanup:
- sst_context_cleanup(ctx);
- platform_set_drvdata(pdev, NULL);
- dev_err(ctx->dev, "failed with %d\n", ret);
- return ret;
}
/**
-* intel_sst_remove - remove function
+* sst_acpi_remove - remove function
*
* @pdev: platform device structure
*
* This function is called by OS when a device is unloaded
* This frees the interrupt etc
*/
-static int sst_acpi_remove(struct platform_device *pdev)
+static void sst_acpi_remove(struct platform_device *pdev)
{
struct intel_sst_drv *ctx;
ctx = platform_get_drvdata(pdev);
sst_context_cleanup(ctx);
platform_set_drvdata(pdev, NULL);
- return 0;
}
-static struct sst_acpi_mach sst_acpi_bytcr[] = {
- {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
- &byt_rvp_platform_data },
- {"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
- &byt_rvp_platform_data },
- {"INTCCFFD", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
- &byt_rvp_platform_data },
- {"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
- &byt_rvp_platform_data },
- {},
-};
-
-/* Cherryview-based platforms: CherryTrail and Braswell */
-static struct sst_acpi_mach sst_acpi_chv[] = {
- {"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
- &chv_platform_data },
- {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
- &chv_platform_data },
- {"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
- &chv_platform_data },
- {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
- &chv_platform_data },
- {},
-};
-
static const struct acpi_device_id sst_acpi_ids[] = {
- { "80860F28", (unsigned long)&sst_acpi_bytcr},
- { "808622A8", (unsigned long) &sst_acpi_chv},
+ { "LPE0F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines},
+ { "80860F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines},
+ { "808622A8", (unsigned long)&snd_soc_acpi_intel_cherrytrail_machines},
{ },
};
@@ -360,7 +405,7 @@ static struct platform_driver sst_acpi_driver = {
.pm = &intel_sst_pm,
},
.probe = sst_acpi_probe,
- .remove = sst_acpi_remove,
+ .remove = sst_acpi_remove,
};
module_platform_driver(sst_acpi_driver);
diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c
index ce689c5af5ab..2646c4632ca1 100644
--- a/sound/soc/intel/atom/sst/sst_drv_interface.c
+++ b/sound/soc/intel/atom/sst/sst_drv_interface.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_drv_interface.c - Intel SST Driver for audio engine
*
@@ -7,15 +8,6 @@
* Dharageswari R <dharageswari.r@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/delay.h>
@@ -32,9 +24,6 @@
#include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h"
#include "sst.h"
-#include "../../common/sst-dsp.h"
-
-
#define NUM_CODEC 2
#define MIN_FRAGMENT 2
@@ -66,19 +55,6 @@ int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
return ret;
}
-int sst_get_stream_allocated(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param,
- struct snd_sst_lib_download **lib_dnld)
-{
- int retval;
-
- retval = ctx->ops->alloc_stream(ctx, str_param);
- if (retval > 0)
- dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
- return retval;
-
-}
-
/*
* sst_get_sfreq - this function returns the frequency of the stream
*
@@ -146,12 +122,11 @@ static int sst_power_control(struct device *dev, bool state)
int ret = 0;
int usage_count = 0;
- if (state == true) {
- ret = pm_runtime_get_sync(dev);
+ if (state) {
+ ret = pm_runtime_resume_and_get(dev);
usage_count = GET_USAGE_COUNT(dev);
dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
if (ret < 0) {
- pm_runtime_put_sync(dev);
dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
return ret;
}
@@ -204,11 +179,9 @@ static int sst_cdev_open(struct device *dev,
struct stream_info *stream;
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
- retval = pm_runtime_get_sync(ctx->dev);
- if (retval < 0) {
- pm_runtime_put_sync(ctx->dev);
+ retval = pm_runtime_resume_and_get(ctx->dev);
+ if (retval < 0)
return retval;
- }
str_id = sst_get_stream(ctx, str_params);
if (str_id > 0) {
@@ -238,16 +211,7 @@ static int sst_cdev_close(struct device *dev, unsigned int str_id)
return -EINVAL;
}
- if (stream->status == STREAM_RESET) {
- dev_dbg(dev, "stream in reset state...\n");
- stream->status = STREAM_UN_INIT;
-
- retval = 0;
- goto put;
- }
-
retval = sst_free_stream(ctx, str_id);
-put:
stream->compr_cb_param = NULL;
stream->compr_cb = NULL;
@@ -256,7 +220,6 @@ put:
dev_dbg(dev, "End\n");
return retval;
-
}
static int sst_cdev_ack(struct device *dev, unsigned int str_id,
@@ -276,17 +239,15 @@ static int sst_cdev_ack(struct device *dev, unsigned int str_id,
stream->cumm_bytes += bytes;
dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
- memcpy_fromio(&fw_tstamp,
- ((void *)(ctx->mailbox + ctx->tstamp)
- +(str_id * sizeof(fw_tstamp))),
- sizeof(fw_tstamp));
+ addr = ((void __iomem *)(ctx->mailbox + ctx->tstamp)) +
+ (str_id * sizeof(fw_tstamp));
+
+ memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
fw_tstamp.bytes_copied = stream->cumm_bytes;
dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
fw_tstamp.bytes_copied, bytes);
- addr = ((void *)(ctx->mailbox + ctx->tstamp)) +
- (str_id * sizeof(fw_tstamp));
offset = offsetof(struct snd_sst_tstamp, bytes_copied);
sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
return 0;
@@ -365,16 +326,17 @@ static int sst_cdev_stream_partial_drain(struct device *dev,
}
static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
- struct snd_compr_tstamp *tstamp)
+ struct snd_compr_tstamp64 *tstamp)
{
struct snd_sst_tstamp fw_tstamp = {0,};
struct stream_info *stream;
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ void __iomem *addr;
+
+ addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) +
+ (str_id * sizeof(fw_tstamp));
- memcpy_fromio(&fw_tstamp,
- ((void *)(ctx->mailbox + ctx->tstamp)
- +(str_id * sizeof(fw_tstamp))),
- sizeof(fw_tstamp));
+ memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
stream = get_stream_info(ctx, str_id);
if (!stream)
@@ -387,10 +349,11 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
(u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24));
tstamp->sampling_rate = fw_tstamp.sampling_frequency;
- dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
- dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n",
+ dev_dbg(dev, "PCM = %llu\n", tstamp->pcm_io_frames);
+ dev_dbg(dev,
+ "Ptr Query on strid = %d copied_total %llu, decodec %llu\n",
str_id, tstamp->copied_total, tstamp->pcm_frames);
- dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+ dev_dbg(dev, "rendered %llu\n", tstamp->pcm_io_frames);
return 0;
}
@@ -407,7 +370,7 @@ static int sst_cdev_caps(struct snd_compr_caps *caps)
return 0;
}
-static struct snd_compr_codec_caps caps_mp3 = {
+static const struct snd_compr_codec_caps caps_mp3 = {
.num_descriptors = 1,
.descriptor[0].max_ch = 2,
.descriptor[0].sample_rates[0] = 48000,
@@ -424,7 +387,7 @@ static struct snd_compr_codec_caps caps_mp3 = {
.descriptor[0].formats = 0,
};
-static struct snd_compr_codec_caps caps_aac = {
+static const struct snd_compr_codec_caps caps_aac = {
.num_descriptors = 2,
.descriptor[1].max_ch = 2,
.descriptor[0].sample_rates[0] = 48000,
@@ -455,17 +418,6 @@ static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
return 0;
}
-void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
-{
- struct stream_info *stream;
-
- dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
- str_id);
- stream = &ctx->streams[str_id];
- if (stream->compr_cb)
- stream->compr_cb(stream->compr_cb_param);
-}
-
/*
* sst_close_pcm_stream - Close PCM interface
*
@@ -486,16 +438,7 @@ static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
return -EINVAL;
}
- if (stream->status == STREAM_RESET) {
- /* silently fail here as we have cleaned the stream earlier */
- dev_dbg(ctx->dev, "stream in reset state...\n");
-
- retval = 0;
- goto put;
- }
-
retval = free_stream_context(ctx, str_id);
-put:
stream->pcm_substream = NULL;
stream->status = STREAM_UN_INIT;
stream->period_elapsed = NULL;
@@ -549,6 +492,7 @@ static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
struct snd_sst_tstamp fw_tstamp;
unsigned int str_id;
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ void __iomem *addr;
str_id = info->str_id;
stream = get_stream_info(ctx, str_id);
@@ -559,10 +503,11 @@ static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
return -EINVAL;
substream = stream->pcm_substream;
- memcpy_fromio(&fw_tstamp,
- ((void *)(ctx->mailbox + ctx->tstamp)
- + (str_id * sizeof(fw_tstamp))),
- sizeof(fw_tstamp));
+ addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) +
+ (str_id * sizeof(fw_tstamp));
+
+ memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
+
return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
}
@@ -674,11 +619,9 @@ static int sst_send_byte_stream(struct device *dev,
if (NULL == bytes)
return -EINVAL;
- ret_val = pm_runtime_get_sync(ctx->dev);
- if (ret_val < 0) {
- pm_runtime_put_sync(ctx->dev);
+ ret_val = pm_runtime_resume_and_get(ctx->dev);
+ if (ret_val < 0)
return ret_val;
- }
ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
sst_pm_runtime_put(ctx);
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index 3dc7358828b3..0630e58b9d6b 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_ipc.c - Intel SST Driver for audio engine
*
@@ -8,36 +9,26 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
-#include <asm/intel-mid.h>
+
#include <asm/platform_sst_audio.h>
+
#include "../sst-mfld-platform.h"
#include "sst.h"
-#include "../../common/sst-dsp.h"
struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
u32 msg_id, u32 drv_id)
{
- struct sst_block *msg = NULL;
+ struct sst_block *msg;
dev_dbg(ctx->dev, "Enter\n");
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
@@ -72,7 +63,7 @@ struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
u32 drv_id, u32 ipc, void *data, u32 size)
{
- struct sst_block *block = NULL;
+ struct sst_block *block;
dev_dbg(ctx->dev, "Enter\n");
@@ -100,7 +91,7 @@ int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
{
- struct sst_block *block = NULL, *__block;
+ struct sst_block *block, *__block;
dev_dbg(ctx->dev, "Enter\n");
spin_lock_bh(&ctx->block_lock);
@@ -137,7 +128,7 @@ int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
while (header.p.header_high.part.busy) {
if (loop_count > 25) {
dev_err(sst_drv_ctx->dev,
- "sst: Busy wait failed, cant send this msg\n");
+ "sst: Busy wait failed, can't send this msg\n");
retval = -EBUSY;
goto out;
}
@@ -236,6 +227,19 @@ static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
retval = init->result;
goto ret;
}
+ if (memcmp(&sst_drv_ctx->fw_version, &init->fw_version,
+ sizeof(init->fw_version)))
+ dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
+ init->fw_version.type, init->fw_version.major,
+ init->fw_version.minor, init->fw_version.build);
+ dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n",
+ init->build_info.date, init->build_info.time);
+
+ /* Save FW version */
+ sst_drv_ctx->fw_version.type = init->fw_version.type;
+ sst_drv_ctx->fw_version.major = init->fw_version.major;
+ sst_drv_ctx->fw_version.minor = init->fw_version.minor;
+ sst_drv_ctx->fw_version.build = init->fw_version.build;
ret:
sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
@@ -249,10 +253,8 @@ static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
u32 data_size, i;
void *data_offset;
struct stream_info *stream;
- union ipc_header_high msg_high;
u32 msg_low, pipe_id;
- msg_high = msg->mrfld_header.p.header_high;
msg_low = msg->mrfld_header.p.header_low_payload;
msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
@@ -267,6 +269,9 @@ static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
"Period elapsed rcvd for pipe id 0x%x\n",
pipe_id);
stream = &sst_drv_ctx->streams[str_id];
+ /* If stream is dropped, skip processing this message*/
+ if (stream->status == STREAM_INIT)
+ break;
if (stream->period_elapsed)
stream->period_elapsed(stream->pcm_substream);
if (stream->compr_cb)
@@ -318,7 +323,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
union ipc_header_high msg_high;
u32 msg_low;
struct ipc_dsp_hdr *dsp_hdr;
- unsigned int cmd_id;
msg_high = msg->mrfld_header.p.header_high;
msg_low = msg->mrfld_header.p.header_low_payload;
@@ -337,7 +341,7 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
}
/* FW sent short error response for an IPC */
- if (msg_high.part.result && drv_id && !msg_high.part.large) {
+ if (msg_high.part.result && !msg_high.part.large) {
/* 32-bit FW error code in msg_low */
dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
@@ -357,7 +361,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
return;
/* Copy command id so that we can use to put sst to reset */
dsp_hdr = (struct ipc_dsp_hdr *)data;
- cmd_id = dsp_hdr->cmd_id;
dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
msg_high.part.drv_id,
diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c
index 33917146d9c4..bf4ba6bcc429 100644
--- a/sound/soc/intel/atom/sst/sst_loader.c
+++ b/sound/soc/intel/atom/sst/sst_loader.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_dsp.c - Intel SST Driver for audio engine
*
@@ -8,15 +9,6 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This file contains all dsp controlling functions like firmware download,
@@ -28,7 +20,6 @@
#include <linux/sched.h>
#include <linux/firmware.h>
#include <linux/dmaengine.h>
-#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -37,26 +28,26 @@
#include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h"
#include "sst.h"
-#include "../../common/sst-dsp.h"
void memcpy32_toio(void __iomem *dst, const void *src, int count)
{
/* __iowrite32_copy uses 32-bit count values so divide by 4 for
* right count in words
*/
- __iowrite32_copy(dst, src, count/4);
+ __iowrite32_copy(dst, src, count / 4);
}
void memcpy32_fromio(void *dst, const void __iomem *src, int count)
{
- /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+ /* __ioread32_copy uses 32-bit count values so divide by 4 for
* right count in words
*/
- __iowrite32_copy(dst, src, count/4);
+ __ioread32_copy(dst, src, count / 4);
}
/**
* intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ * @sst_drv_ctx: intel_sst_drv context pointer
*
* This resets DSP in case of MRFLD platfroms
*/
@@ -84,7 +75,8 @@ int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
}
/**
- * sst_start_merrifield - Start the SST DSP processor
+ * sst_start_mrfld - Start the SST DSP processor
+ * @sst_drv_ctx: intel_sst_drv context pointer
*
* This starts the DSP in MERRIFIELD platfroms
*/
@@ -269,7 +261,7 @@ static void sst_do_memcpy(struct list_head *memcpy_list)
struct sst_memcpy_list *listnode;
list_for_each_entry(listnode, memcpy_list, memcpylist) {
- if (listnode->is_io == true)
+ if (listnode->is_io)
memcpy32_toio((void __iomem *)listnode->dstn,
listnode->src, listnode->size);
else
@@ -282,12 +274,10 @@ void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
struct sst_memcpy_list *listnode, *tmplistnode;
/* Free the list */
- if (!list_empty(&sst_drv_ctx->memcpy_list)) {
- list_for_each_entry_safe(listnode, tmplistnode,
- &sst_drv_ctx->memcpy_list, memcpylist) {
- list_del(&listnode->memcpylist);
- kfree(listnode);
- }
+ list_for_each_entry_safe(listnode, tmplistnode,
+ &sst_drv_ctx->memcpy_list, memcpylist) {
+ list_del(&listnode->memcpylist);
+ kfree(listnode);
}
}
@@ -354,14 +344,14 @@ static int sst_request_fw(struct intel_sst_drv *sst)
const struct firmware *fw;
retval = request_firmware(&fw, sst->firmware_name, sst->dev);
- if (fw == NULL) {
- dev_err(sst->dev, "fw is returning as null\n");
- return -EINVAL;
- }
if (retval) {
dev_err(sst->dev, "request fw failed %d\n", retval);
return retval;
}
+ if (fw == NULL) {
+ dev_err(sst->dev, "fw is returning as null\n");
+ return -EINVAL;
+ }
mutex_lock(&sst->sst_lock);
retval = sst_cache_and_parse_fw(sst, fw);
mutex_unlock(&sst->sst_lock);
@@ -395,6 +385,8 @@ void sst_post_download_mrfld(struct intel_sst_drv *ctx)
/**
* sst_load_fw - function to load FW into DSP
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ *
* Transfers the FW to DSP using dma/memcpy
*/
int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
@@ -404,8 +396,7 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
- if (sst_drv_ctx->sst_state != SST_RESET ||
- sst_drv_ctx->sst_state == SST_SHUTDOWN)
+ if (sst_drv_ctx->sst_state != SST_RESET)
return -EAGAIN;
if (!sst_drv_ctx->fw_in_mem) {
@@ -415,13 +406,12 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
return ret_val;
}
- BUG_ON(!sst_drv_ctx->fw_in_mem);
block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
if (block == NULL)
return -ENOMEM;
/* Prevent C-states beyond C6 */
- pm_qos_update_request(sst_drv_ctx->qos, 0);
+ cpu_latency_qos_update_request(sst_drv_ctx->qos, 0);
sst_drv_ctx->sst_state = SST_FW_LOADING;
@@ -451,7 +441,7 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
restore:
/* Re-enable Deeper C-states beyond C6 */
- pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+ cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
sst_free_block(sst_drv_ctx, block);
dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c
index 3a0b3bf0af97..22ae2d22f121 100644
--- a/sound/soc/intel/atom/sst/sst_pci.c
+++ b/sound/soc/intel/atom/sst/sst_pci.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_pci.c - SST (LPE) driver init file for pci enumeration.
*
@@ -8,22 +9,12 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <asm/platform_sst_audio.h>
@@ -35,78 +26,68 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
int ddr_base, ret = 0;
struct pci_dev *pci = ctx->pci;
- ret = pci_request_regions(pci, SST_DRV_NAME);
+ ret = pcim_request_all_regions(pci, SST_DRV_NAME);
if (ret)
return ret;
/* map registers */
/* DDR base */
- if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+ if (ctx->dev_id == PCI_DEVICE_ID_INTEL_SST_TNG) {
ctx->ddr_base = pci_resource_start(pci, 0);
/* check that the relocated IMR base matches with FW Binary */
ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
if (!ctx->pdata->lib_info) {
dev_err(ctx->dev, "lib_info pointer NULL\n");
- ret = -EINVAL;
- goto do_release_regions;
+ return -EINVAL;
}
if (ddr_base != ctx->pdata->lib_info->mod_base) {
dev_err(ctx->dev,
"FW LSP DDR BASE does not match with IFWI\n");
- ret = -EINVAL;
- goto do_release_regions;
+ return -EINVAL;
}
ctx->ddr_end = pci_resource_end(pci, 0);
- ctx->ddr = pcim_iomap(pci, 0,
- pci_resource_len(pci, 0));
- if (!ctx->ddr) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->ddr = pcim_iomap(pci, 0, 0);
+ if (!ctx->ddr)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
} else {
ctx->ddr = NULL;
}
/* SHIM */
ctx->shim_phy_add = pci_resource_start(pci, 1);
- ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
- if (!ctx->shim) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->shim = pcim_iomap(pci, 1, 0);
+ if (!ctx->shim)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
/* Shared SRAM */
ctx->mailbox_add = pci_resource_start(pci, 2);
- ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
- if (!ctx->mailbox) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->mailbox = pcim_iomap(pci, 2, 0);
+ if (!ctx->mailbox)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
/* IRAM */
ctx->iram_end = pci_resource_end(pci, 3);
ctx->iram_base = pci_resource_start(pci, 3);
- ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
- if (!ctx->iram) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->iram = pcim_iomap(pci, 3, 0);
+ if (!ctx->iram)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
/* DRAM */
ctx->dram_end = pci_resource_end(pci, 4);
ctx->dram_base = pci_resource_start(pci, 4);
- ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
- if (!ctx->dram) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->dram = pcim_iomap(pci, 4, 0);
+ if (!ctx->dram)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
-do_release_regions:
- pci_release_regions(pci);
return 0;
}
@@ -176,13 +157,12 @@ static void intel_sst_remove(struct pci_dev *pci)
sst_context_cleanup(sst_drv_ctx);
pci_dev_put(sst_drv_ctx->pci);
- pci_release_regions(pci);
pci_set_drvdata(pci, NULL);
}
/* PCI Routines */
-static struct pci_device_id intel_sst_ids[] = {
- { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+static const struct pci_device_id intel_sst_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, SST_TNG, 0) },
{ 0, }
};
diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c
index adb32fefd693..c01b29616ebc 100644
--- a/sound/soc/intel/atom/sst/sst_pvt.c
+++ b/sound/soc/intel/atom/sst/sst_pvt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_pvt.c - Intel SST Driver for audio engine
*
@@ -8,15 +9,6 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/kobject.h>
@@ -34,7 +26,6 @@
#include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h"
#include "sst.h"
-#include "../../common/sst-dsp.h"
int sst_shim_write(void __iomem *addr, int offset, int value)
{
@@ -79,39 +70,6 @@ void sst_set_fw_state_locked(
}
/*
- * sst_wait_interruptible - wait on event
- *
- * @sst_drv_ctx: Driver context
- * @block: Driver block to wait on
- *
- * This function waits without a timeout (and is interruptable) for a
- * given block event
- */
-int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block)
-{
- int retval = 0;
-
- if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
- block->condition)) {
- /* event wake */
- if (block->ret_code < 0) {
- dev_err(sst_drv_ctx->dev,
- "stream failed %d\n", block->ret_code);
- retval = -EBUSY;
- } else {
- dev_dbg(sst_drv_ctx->dev, "event up\n");
- retval = 0;
- }
- } else {
- dev_err(sst_drv_ctx->dev, "signal interrupted\n");
- retval = -EINTR;
- }
- return retval;
-
-}
-
-/*
* sst_wait_timeout - wait on event for timeout
*
* @sst_drv_ctx: Driver context
@@ -166,7 +124,7 @@ int sst_create_ipc_msg(struct ipc_post **arg, bool large)
{
struct ipc_post *msg;
- msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+ msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
if (!msg)
return -ENOMEM;
if (large) {
@@ -196,7 +154,7 @@ int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
u32 msg_id, u32 drv_id)
{
- int retval = 0;
+ int retval;
retval = sst_create_ipc_msg(arg, large);
if (retval)
@@ -206,7 +164,7 @@ int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
kfree(*arg);
return -ENOMEM;
}
- return retval;
+ return 0;
}
/*
@@ -231,9 +189,9 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
size_t mbox_data_len, const void *mbox_data, void **data,
bool large, bool fill_dsp, bool sync, bool response)
{
+ struct sst_block *block = NULL;
struct ipc_post *msg = NULL;
struct ipc_dsp_hdr dsp_hdr;
- struct sst_block *block;
int ret = 0, pvt_id;
pvt_id = sst_assign_pvt_id(sst);
@@ -279,17 +237,15 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
if (response) {
ret = sst_wait_timeout(sst, block);
- if (ret < 0) {
+ if (ret < 0)
goto out;
- } else if(block->data) {
- if (!data)
- goto out;
- *data = kzalloc(block->size, GFP_KERNEL);
- if (!(*data)) {
+
+ if (data && block->data) {
+ *data = kmemdup(block->data, block->size, GFP_KERNEL);
+ if (!*data) {
ret = -ENOMEM;
goto out;
- } else
- memcpy(data, (void *) block->data, block->size);
+ }
}
}
out:
@@ -303,7 +259,6 @@ int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
{
int ret;
- pm_runtime_mark_last_busy(sst_drv->dev);
ret = pm_runtime_put_autosuspend(sst_drv->dev);
if (ret < 0)
return ret;
@@ -362,14 +317,6 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv)
return local;
}
-void sst_init_stream(struct stream_info *stream,
- int codec, int sst_id, int ops, u8 slot)
-{
- stream->status = STREAM_INIT;
- stream->prev = STREAM_UN_INIT;
- stream->ops = ops;
-}
-
int sst_validate_strid(
struct intel_sst_drv *sst_drv_ctx, int str_id)
{
diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c
index 4ccc80e5e8cc..288221db7323 100644
--- a/sound/soc/intel/atom/sst/sst_stream.c
+++ b/sound/soc/intel/atom/sst/sst_stream.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sst_stream.c - Intel SST Driver for audio engine
*
@@ -8,22 +9,12 @@
* KP Jeeja <jeeja.kp@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -31,34 +22,34 @@
#include <asm/platform_sst_audio.h>
#include "../sst-mfld-platform.h"
#include "sst.h"
-#include "../../common/sst-dsp.h"
int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
{
- struct snd_sst_alloc_mrfld alloc_param;
+ struct snd_pcm_params *pcm_params;
struct snd_sst_params *str_params;
struct snd_sst_tstamp fw_tstamp;
struct stream_info *str_info;
- struct snd_sst_alloc_response *response;
- unsigned int str_id, pipe_id, task_id;
- int i, num_ch, ret = 0;
- void *data = NULL;
+ int i, num_ch, str_id;
dev_dbg(sst_drv_ctx->dev, "Enter\n");
- BUG_ON(!params);
str_params = (struct snd_sst_params *)params;
- memset(&alloc_param, 0, sizeof(alloc_param));
- alloc_param.operation = str_params->ops;
- alloc_param.codec_type = str_params->codec;
- alloc_param.sg_count = str_params->aparams.sg_count;
- alloc_param.ring_buf_info[0].addr =
+ str_id = str_params->stream_id;
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ memset(&str_info->alloc_param, 0, sizeof(str_info->alloc_param));
+ str_info->alloc_param.operation = str_params->ops;
+ str_info->alloc_param.codec_type = str_params->codec;
+ str_info->alloc_param.sg_count = str_params->aparams.sg_count;
+ str_info->alloc_param.ring_buf_info[0].addr =
str_params->aparams.ring_buf_info[0].addr;
- alloc_param.ring_buf_info[0].size =
+ str_info->alloc_param.ring_buf_info[0].size =
str_params->aparams.ring_buf_info[0].size;
- alloc_param.frag_size = str_params->aparams.frag_size;
+ str_info->alloc_param.frag_size = str_params->aparams.frag_size;
- memcpy(&alloc_param.codec_params, &str_params->sparams,
+ memcpy(&str_info->alloc_param.codec_params, &str_params->sparams,
sizeof(struct snd_sst_stream_params));
/*
@@ -68,47 +59,62 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
* Currently hardcoding as per FW reqm.
*/
num_ch = sst_get_num_channel(str_params);
+ pcm_params = &str_info->alloc_param.codec_params.uc.pcm_params;
for (i = 0; i < 8; i++) {
if (i < num_ch)
- alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+ pcm_params->channel_map[i] = i;
else
- alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
- }
-
- str_id = str_params->stream_id;
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (str_info == NULL) {
- dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
- return -EINVAL;
+ pcm_params->channel_map[i] = 0xff;
}
- pipe_id = str_params->device_type;
- task_id = str_params->task;
- sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
- sst_drv_ctx->streams[str_id].task_id = task_id;
+ sst_drv_ctx->streams[str_id].status = STREAM_INIT;
+ sst_drv_ctx->streams[str_id].prev = STREAM_UN_INIT;
+ sst_drv_ctx->streams[str_id].pipe_id = str_params->device_type;
+ sst_drv_ctx->streams[str_id].task_id = str_params->task;
sst_drv_ctx->streams[str_id].num_ch = num_ch;
if (sst_drv_ctx->info.lpe_viewpt_rqd)
- alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+ str_info->alloc_param.ts = sst_drv_ctx->info.mailbox_start +
sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
else
- alloc_param.ts = sst_drv_ctx->mailbox_add +
+ str_info->alloc_param.ts = sst_drv_ctx->mailbox_add +
sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
- alloc_param.ts);
+ str_info->alloc_param.ts);
dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
- pipe_id, task_id);
+ str_info->pipe_id, str_info->task_id);
+
+ return sst_realloc_stream(sst_drv_ctx, str_id);
+}
+
+/**
+ * sst_realloc_stream - Send msg for (re-)allocating a stream using the
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * Send a msg for (re-)allocating a stream using the parameters previously
+ * passed to sst_alloc_stream_mrfld() for the same stream ID.
+ * Return: 0 or negative errno value.
+ */
+int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ struct snd_sst_alloc_response *response;
+ struct stream_info *str_info;
+ void *data = NULL;
+ int ret;
+
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
- /* allocate device type context */
- sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
- str_id, alloc_param.operation, 0);
+ dev_dbg(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+ str_id, str_info->pipe_id);
- dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
- str_id, pipe_id);
- ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
- IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
- &alloc_param, &data, true, true, false, true);
+ ret = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_ALLOC_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(str_info->alloc_param), &str_info->alloc_param,
+ &data, true, true, false, true);
if (ret < 0) {
dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
@@ -134,12 +140,13 @@ out:
}
/**
-* sst_start_stream - Send msg for a starting stream
-* @str_id: stream ID
-*
-* This function is called by any function which wants to start
-* a stream.
-*/
+ * sst_start_stream - Send msg for a starting stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to start
+ * a stream.
+ */
int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
{
int retval = 0;
@@ -166,10 +173,11 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
u32 length;
int pvt_id, ret = 0;
struct sst_block *block = NULL;
+ u8 bytes_block = bytes->block;
dev_dbg(sst_drv_ctx->dev,
"type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
- bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+ bytes->type, bytes->ipc_msg, bytes_block, bytes->task_id,
bytes->pipe_id, bytes->len);
if (sst_create_ipc_msg(&msg, true))
@@ -178,12 +186,12 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
pvt_id = sst_assign_pvt_id(sst_drv_ctx);
sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
bytes->task_id, 1, pvt_id);
- msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+ msg->mrfld_header.p.header_high.part.res_rqd = bytes_block;
length = bytes->len;
msg->mrfld_header.p.header_low_payload = length;
dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
- if (bytes->block) {
+ if (bytes_block) {
block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
if (block == NULL) {
kfree(msg);
@@ -196,7 +204,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
msg->mrfld_header.p.header_low_payload);
- if (bytes->block) {
+ if (bytes_block) {
ret = sst_wait_timeout(sst_drv_ctx, block);
if (ret) {
dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
@@ -209,7 +217,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
* copy the reply and send back
* we need to update only sz and payload
*/
- if (bytes->block) {
+ if (bytes_block) {
unsigned char *r = block->data;
dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
@@ -217,16 +225,17 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
memcpy(bytes->bytes, r, bytes->len);
}
}
- if (bytes->block)
+ if (bytes_block)
sst_free_block(sst_drv_ctx, block);
out:
test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
- return 0;
+ return ret;
}
-/*
+/**
* sst_pause_stream - Send msg for a pausing stream
- * @str_id: stream ID
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
*
* This function is called by any function which wants to pause
* an already running stream.
@@ -254,7 +263,7 @@ int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
if (retval == 0) {
str_info->prev = str_info->status;
str_info->status = STREAM_PAUSED;
- } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
retval = -EINVAL;
mutex_lock(&sst_drv_ctx->sst_lock);
sst_clean_stream(str_info);
@@ -262,7 +271,7 @@ int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
}
} else {
retval = -EBADRQC;
- dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n");
}
return retval;
@@ -270,7 +279,8 @@ int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
/**
* sst_resume_stream - Send msg for resuming stream
- * @str_id: stream ID
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
*
* This function is called by any function which wants to resume
* an already paused stream.
@@ -285,8 +295,30 @@ int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
if (!str_info)
return -EINVAL;
if (str_info->status == STREAM_RUNNING)
- return 0;
- if (str_info->status == STREAM_PAUSED) {
+ return 0;
+
+ if (str_info->resume_status == STREAM_PAUSED &&
+ str_info->resume_prev == STREAM_RUNNING) {
+ /*
+ * Stream was running before suspend and re-created on resume,
+ * start it to get back to running state.
+ */
+ dev_dbg(sst_drv_ctx->dev, "restart recreated stream after resume\n");
+ str_info->status = STREAM_RUNNING;
+ str_info->prev = STREAM_PAUSED;
+ retval = sst_start_stream(sst_drv_ctx, str_id);
+ str_info->resume_status = STREAM_UN_INIT;
+ } else if (str_info->resume_status == STREAM_PAUSED &&
+ str_info->resume_prev == STREAM_INIT) {
+ /*
+ * Stream was idle before suspend and re-created on resume,
+ * keep it as is.
+ */
+ dev_dbg(sst_drv_ctx->dev, "leaving recreated stream idle after resume\n");
+ str_info->status = STREAM_INIT;
+ str_info->prev = STREAM_PAUSED;
+ str_info->resume_status = STREAM_UN_INIT;
+ } else if (str_info->status == STREAM_PAUSED) {
retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
str_info->pipe_id, 0, NULL, NULL,
@@ -315,7 +347,8 @@ int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
/**
* sst_drop_stream - Send msg for stopping stream
- * @str_id: stream ID
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
*
* This function is called by any function which wants to stop
* a stream.
@@ -347,12 +380,14 @@ int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
}
/**
-* sst_drain_stream - Send msg for draining stream
-* @str_id: stream ID
-*
-* This function is called by any function which wants to drain
-* a stream.
-*/
+ * sst_drain_stream - Send msg for draining stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ * @partial_drain: boolean indicating if a gapless transition is taking place
+ *
+ * This function is called by any function which wants to drain
+ * a stream.
+ */
int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
int str_id, bool partial_drain)
{
@@ -385,7 +420,8 @@ int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
/**
* sst_free_stream - Frees a stream
- * @str_id: stream ID
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
*
* This function is called by any function which wants to free
* a stream.
@@ -394,7 +430,6 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
{
int retval = 0;
struct stream_info *str_info;
- struct intel_sst_ops *ops;
dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
@@ -407,7 +442,6 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
str_info = get_stream_info(sst_drv_ctx, str_id);
if (!str_info)
return -EINVAL;
- ops = sst_drv_ctx->ops;
mutex_lock(&str_info->lock);
if (str_info->status != STREAM_UN_INIT) {
@@ -415,7 +449,7 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
str_info->status = STREAM_UN_INIT;
mutex_unlock(&str_info->lock);
- dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+ dev_dbg(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
str_id, str_info->pipe_id);
retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
new file mode 100644
index 000000000000..576dc0da381d
--- /dev/null
+++ b/sound/soc/intel/avs/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+snd-soc-avs-y := dsp.o ipc.o messages.o utils.o core.o loader.o \
+ topology.o path.o pcm.o board_selection.o control.o \
+ sysfs.o
+snd-soc-avs-y += cldma.o
+snd-soc-avs-y += skl.o apl.o cnl.o icl.o tgl.o mtl.o lnl.o ptl.o
+
+snd-soc-avs-y += trace.o
+# tell define_trace.h where to find the trace header
+CFLAGS_trace.o := -I$(src)
+
+ifneq ($(CONFIG_DEBUG_FS),)
+snd-soc-avs-y += probes.o debugfs.o
+endif
+
+obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
+
+# Machine support
+obj-$(CONFIG_SND_SOC) += boards/
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
new file mode 100644
index 000000000000..b922eeaba843
--- /dev/null
+++ b/sound/soc/intel/avs/apl.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+#include "path.h"
+#include "registers.h"
+#include "topology.h"
+
+static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev)
+{
+ u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (adspis == UINT_MAX)
+ return ret;
+
+ if (adspis & AVS_ADSP_ADSPIS_IPC) {
+ avs_skl_ipc_interrupt(adev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct avs_apl_log_state_info *info;
+ u32 size, num_cores = adev->hw_cfg.dsp_cores;
+ int ret, i;
+
+ if (fls_long(resource_mask) > num_cores)
+ return -EINVAL;
+ size = struct_size(info, logs_core, num_cores);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->aging_timer_period = aging_period;
+ info->fifo_full_timer_period = fifo_full_period;
+ info->core_mask = resource_mask;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, num_cores) {
+ info->logs_core[i].enable = enable;
+ info->logs_core[i].min_priority = *priorities++;
+ }
+ else
+ for_each_set_bit(i, &resource_mask, num_cores)
+ info->logs_core[i].enable = enable;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+#endif
+
+int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct avs_apl_log_buffer_layout layout;
+ void __iomem *addr, *buf;
+
+ addr = avs_log_buffer_addr(adev, msg->log.core);
+ if (!addr)
+ return -ENXIO;
+
+ memcpy_fromio(&layout, addr, sizeof(layout));
+
+ if (!avs_logging_fw(adev))
+ /* consume the logs regardless of consumer presence */
+ goto update_read_ptr;
+
+ buf = avs_apl_log_payload_addr(addr);
+
+ if (layout.read_ptr > layout.write_ptr) {
+ avs_dump_fw_log(adev, buf + layout.read_ptr,
+ avs_apl_log_payload_size(adev) - layout.read_ptr);
+ layout.read_ptr = 0;
+ }
+ avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr);
+
+update_read_ptr:
+ writel(layout.write_ptr, addr);
+ return 0;
+}
+
+static int avs_apl_wait_log_entry(struct avs_dev *adev, u32 core,
+ struct avs_apl_log_buffer_layout *layout)
+{
+ unsigned long timeout;
+ void __iomem *addr;
+
+ addr = avs_log_buffer_addr(adev, core);
+ if (!addr)
+ return -ENXIO;
+
+ timeout = jiffies + msecs_to_jiffies(10);
+
+ do {
+ memcpy_fromio(layout, addr, sizeof(*layout));
+ if (layout->read_ptr != layout->write_ptr)
+ return 0;
+ usleep_range(500, 1000);
+ } while (!time_after(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+/* reads log header and tests its type */
+#define avs_apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
+
+int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct avs_apl_log_buffer_layout layout;
+ void __iomem *addr, *buf;
+ size_t dump_size;
+ u32 offset = 0;
+ u8 *dump, *pos;
+
+ dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size;
+ dump = vzalloc(dump_size);
+ if (!dump)
+ return -ENOMEM;
+
+ memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+
+ if (!msg->ext.coredump.stack_dump_size)
+ goto exit;
+
+ /* Dump the registers even if an external error prevents gathering the stack. */
+ addr = avs_log_buffer_addr(adev, msg->ext.coredump.core_id);
+ if (!addr)
+ goto exit;
+
+ buf = avs_apl_log_payload_addr(addr);
+ memcpy_fromio(&layout, addr, sizeof(layout));
+ if (!avs_apl_is_entry_stackdump(buf + layout.read_ptr)) {
+ union avs_notify_msg lbs_msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
+
+ /*
+ * DSP awaits the remaining logs to be
+ * gathered before dumping stack
+ */
+ lbs_msg.log.core = msg->ext.coredump.core_id;
+ avs_log_buffer_status_locked(adev, &lbs_msg);
+ }
+
+ pos = dump + AVS_FW_REGS_SIZE;
+ /* gather the stack */
+ do {
+ u32 count;
+
+ if (avs_apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
+ break;
+
+ if (layout.read_ptr > layout.write_ptr) {
+ count = avs_apl_log_payload_size(adev) - layout.read_ptr;
+ memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+ layout.read_ptr = 0;
+ offset += count;
+ }
+ count = layout.write_ptr - layout.read_ptr;
+ memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+ offset += count;
+
+ /* update read pointer */
+ writel(layout.write_ptr, addr);
+ } while (offset < msg->ext.coredump.stack_dump_size);
+
+exit:
+ dev_coredumpv(adev->dev, dump, dump_size, GFP_KERNEL);
+
+ return 0;
+}
+
+static bool avs_apl_lp_streaming(struct avs_dev *adev)
+{
+ struct avs_path *path;
+
+ spin_lock(&adev->path_list_lock);
+ /* Any gateway without buffer allocated in LP area disqualifies D0IX. */
+ list_for_each_entry(path, &adev->path_list, node) {
+ struct avs_path_pipeline *ppl;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node) {
+ struct avs_tplg_modcfg_ext *cfg;
+
+ cfg = mod->template->cfg_ext;
+
+ /* only copiers have gateway attributes */
+ if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID))
+ continue;
+ /* non-gateway copiers do not prevent PG */
+ if (cfg->copier.dma_type == INVALID_OBJECT_ID)
+ continue;
+
+ if (!mod->gtw_attrs.lp_buffer_alloc) {
+ spin_unlock(&adev->path_list_lock);
+ return false;
+ }
+ }
+ }
+ }
+ spin_unlock(&adev->path_list_lock);
+
+ return true;
+}
+
+bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+ /* wake in all cases */
+ if (wake)
+ return true;
+
+ /*
+ * If no pipelines are running, allow for d0ix schedule.
+ * If all gateways have lp=1, allow for d0ix schedule.
+ * If any gateway with lp=0 is allocated, abort scheduling d0ix.
+ *
+ * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition,
+ * not the power-gating mechanism known from cAVS 2.0.
+ */
+ return avs_apl_lp_streaming(adev);
+}
+
+int avs_apl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ bool streaming = false;
+ int ret;
+
+ if (enable)
+ /* Either idle or all gateways with lp=1. */
+ streaming = !list_empty(&adev->path_list);
+
+ ret = avs_ipc_set_d0ix(adev, enable, streaming);
+ return AVS_IPC_RET(ret);
+}
+
+const struct avs_dsp_ops avs_apl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .dsp_interrupt = avs_apl_dsp_interrupt,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .log_buffer_offset = avs_skl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_apl_d0ix_toggle,
+ .set_d0ix = avs_apl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(apl)
+};
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
new file mode 100644
index 000000000000..0f8ddd0e9e5f
--- /dev/null
+++ b/sound/soc/intel/avs/avs.h
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_H
+#define __SOUND_SOC_INTEL_AVS_H
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/kfifo.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include <sound/soc-component.h>
+#include "messages.h"
+#include "registers.h"
+
+struct avs_dev;
+struct avs_tplg;
+struct avs_tplg_library;
+struct avs_ipc_msg;
+
+#ifdef CONFIG_ACPI
+#define AVS_S0IX_SUPPORTED \
+ (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
+#else
+#define AVS_S0IX_SUPPORTED false
+#endif
+
+/*
+ * struct avs_dsp_ops - Platform-specific DSP operations
+ *
+ * @power: Power on or off DSP cores
+ * @reset: Enter or exit reset state on DSP cores
+ * @stall: Stall or run DSP cores
+ * @irq_handler: Top half of IPC servicing
+ * @irq_thread: Bottom half of IPC servicing
+ * @int_control: Enable or disable IPC interrupts
+ */
+struct avs_dsp_ops {
+ int (* const power)(struct avs_dev *, u32, bool);
+ int (* const reset)(struct avs_dev *, u32, bool);
+ int (* const stall)(struct avs_dev *, u32, bool);
+ irqreturn_t (* const dsp_interrupt)(struct avs_dev *);
+ void (* const int_control)(struct avs_dev *, bool);
+ int (* const load_basefw)(struct avs_dev *, struct firmware *);
+ int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
+ int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32);
+ int (* const config_basefw)(struct avs_dev *);
+ int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long,
+ u32 *);
+ int (* const log_buffer_offset)(struct avs_dev *, u32);
+ int (* const log_buffer_status)(struct avs_dev *, union avs_notify_msg *);
+ int (* const coredump)(struct avs_dev *, union avs_notify_msg *);
+ bool (* const d0ix_toggle)(struct avs_dev *, struct avs_ipc_msg *, bool);
+ int (* const set_d0ix)(struct avs_dev *, bool);
+};
+
+#define avs_dsp_op(adev, op, ...) \
+ ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
+
+extern const struct avs_dsp_ops avs_skl_dsp_ops;
+extern const struct avs_dsp_ops avs_apl_dsp_ops;
+extern const struct avs_dsp_ops avs_cnl_dsp_ops;
+extern const struct avs_dsp_ops avs_icl_dsp_ops;
+extern const struct avs_dsp_ops avs_tgl_dsp_ops;
+extern const struct avs_dsp_ops avs_ptl_dsp_ops;
+
+#define AVS_PLATATTR_CLDMA BIT_ULL(0)
+#define AVS_PLATATTR_IMR BIT_ULL(1)
+#define AVS_PLATATTR_ACE BIT_ULL(2)
+#define AVS_PLATATTR_ALTHDA BIT_ULL(3)
+
+#define avs_platattr_test(adev, attr) \
+ ((adev)->spec->attributes & AVS_PLATATTR_##attr)
+
+struct avs_sram_spec {
+ const u32 base_offset;
+ const u32 window_size;
+};
+
+struct avs_hipc_spec {
+ const u32 req_offset;
+ const u32 req_ext_offset;
+ const u32 req_busy_mask;
+ const u32 ack_offset;
+ const u32 ack_done_mask;
+ const u32 rsp_offset;
+ const u32 rsp_busy_mask;
+ const u32 ctl_offset;
+ const u32 sts_offset;
+};
+
+/* Platform specific descriptor */
+struct avs_spec {
+ const char *name;
+
+ const struct avs_dsp_ops *const dsp_ops;
+ struct avs_fw_version min_fw_version; /* anything below is rejected */
+
+ const u32 core_init_mask; /* used during DSP boot */
+ const u64 attributes; /* bitmask of AVS_PLATATTR_* */
+ const struct avs_sram_spec *sram;
+ const struct avs_hipc_spec *hipc;
+};
+
+struct avs_fw_entry {
+ const char *name;
+ const struct firmware *fw;
+
+ struct list_head node;
+};
+
+/*
+ * struct avs_dev - Intel HD-Audio driver data
+ *
+ * @dev: PCI device
+ * @dsp_ba: DSP bar address
+ * @spec: platform-specific descriptor
+ * @fw_cfg: Firmware configuration, obtained through FW_CONFIG message
+ * @hw_cfg: Hardware configuration, obtained through HW_CONFIG message
+ * @mods_info: Available module-types, obtained through MODULES_INFO message
+ * @mod_idas: Module instance ID pool, one per module-type
+ * @modres_mutex: For synchronizing any @mods_info updates
+ * @ppl_ida: Pipeline instance ID pool
+ * @fw_list: List of libraries loaded, including base firmware
+ */
+struct avs_dev {
+ struct hda_bus base;
+ struct device *dev;
+
+ void __iomem *dsp_ba;
+ const struct avs_spec *spec;
+ struct avs_ipc *ipc;
+
+ struct avs_fw_cfg fw_cfg;
+ struct avs_hw_cfg hw_cfg;
+ struct avs_mods_info *mods_info;
+ struct ida **mod_idas;
+ struct mutex modres_mutex;
+ void *modcfg_buf; /* module configuration buffer */
+ struct ida ppl_ida;
+ struct list_head fw_list;
+ int *core_refs; /* reference count per core */
+ char **lib_names;
+ int num_lp_paths;
+ atomic_t l1sen_counter; /* controls whether L1SEN should be disabled */
+
+ struct completion fw_ready;
+ struct work_struct probe_work;
+
+ struct list_head comp_list;
+ struct mutex comp_list_mutex;
+ struct list_head path_list;
+ spinlock_t path_list_lock;
+ struct mutex path_mutex;
+
+ spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
+#ifdef CONFIG_DEBUG_FS
+ struct kfifo trace_fifo;
+ wait_queue_head_t trace_waitq;
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 logged_resources; /* context dependent: core or library */
+ struct dentry *debugfs_root;
+ /* probes */
+ struct hdac_ext_stream *extractor;
+ unsigned int num_probe_streams;
+#endif
+};
+
+/* from hda_bus to avs_dev */
+#define hda_to_avs(hda) container_of(hda, struct avs_dev, base)
+/* from hdac_bus to avs_dev */
+#define hdac_to_avs(hdac) hda_to_avs(to_hda_bus(hdac))
+/* from device to avs_dev */
+#define to_avs_dev(dev) \
+({ \
+ struct hdac_bus *__bus = dev_get_drvdata(dev); \
+ hdac_to_avs(__bus); \
+})
+
+int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power);
+int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset);
+int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
+int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
+int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);
+
+/* Inter Process Communication */
+
+struct avs_ipc_msg {
+ union {
+ u64 header;
+ union avs_global_msg glb;
+ union avs_reply_msg rsp;
+ };
+ void *data;
+ size_t size;
+};
+
+/*
+ * struct avs_ipc - DSP IPC context
+ *
+ * @dev: PCI device
+ * @rx: Reply message cache
+ * @default_timeout_ms: default message timeout in MS
+ * @ready: whether firmware is ready and communication is open
+ * @rx_completed: whether RX for previously sent TX has been received
+ * @rx_lock: for serializing manipulation of rx_* fields
+ * @msg_lock: for synchronizing request handling
+ * @done_completion: DONE-part of IPC i.e. ROM and ACKs from FW
+ * @busy_completion: BUSY-part of IPC i.e. receiving responses from FW
+ */
+struct avs_ipc {
+ struct device *dev;
+
+ struct avs_ipc_msg rx;
+ u32 default_timeout_ms;
+ bool ready;
+ atomic_t recovering;
+
+ bool rx_completed;
+ spinlock_t rx_lock;
+ struct mutex msg_mutex;
+ struct completion done_completion;
+ struct completion busy_completion;
+
+ struct work_struct recovery_work;
+ struct delayed_work d0ix_work;
+ atomic_t d0ix_disable_depth;
+ bool in_d0ix;
+};
+
+#define AVS_EIPC EREMOTEIO
+/*
+ * IPC handlers may return positive value (firmware error code) what denotes
+ * successful HOST <-> DSP communication yet failure to process specific request.
+ *
+ * Below macro converts returned value to linux kernel error code.
+ * All IPC callers MUST use it as soon as firmware error code is consumed.
+ */
+#define AVS_IPC_RET(ret) \
+ (((ret) <= 0) ? (ret) : -AVS_EIPC)
+
+void avs_dsp_process_response(struct avs_dev *adev, u64 header);
+int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, const char *name);
+int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, const char *name);
+/* Two variants below are for messages that control DSP power states. */
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+ const char *name);
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, bool wake_d0i0, const char *name);
+int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout,
+ const char *name);
+int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, const char *name);
+void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable);
+int avs_ipc_init(struct avs_ipc *ipc, struct device *dev);
+void avs_ipc_block(struct avs_ipc *ipc);
+
+int avs_dsp_disable_d0ix(struct avs_dev *adev);
+int avs_dsp_enable_d0ix(struct avs_dev *adev);
+
+int avs_mtl_core_power(struct avs_dev *adev, u32 core_mask, bool power);
+int avs_mtl_core_reset(struct avs_dev *adev, u32 core_mask, bool power);
+int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
+int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
+void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable);
+void avs_skl_ipc_interrupt(struct avs_dev *adev);
+irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev);
+irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev);
+int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
+int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
+int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core);
+int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg);
+int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg);
+bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake);
+bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake);
+int avs_apl_set_d0ix(struct avs_dev *adev, bool enable);
+int avs_icl_set_d0ix(struct avs_dev *adev, bool enable);
+
+/* Firmware resources management */
+
+int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
+int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry);
+int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid);
+bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id);
+
+int avs_module_info_init(struct avs_dev *adev, bool purge);
+void avs_module_info_free(struct avs_dev *adev);
+int avs_module_id_alloc(struct avs_dev *adev, u16 module_id);
+void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id);
+int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name);
+void avs_release_last_firmware(struct avs_dev *adev);
+void avs_release_firmwares(struct avs_dev *adev);
+
+int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
+ u8 core_id, u8 domain, void *param, u32 param_size,
+ u8 *instance_id);
+void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 ppl_instance_id, u8 core_id);
+int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+ bool lp, u16 attributes, u8 *instance_id);
+int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id);
+
+/* Firmware loading */
+
+void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable);
+void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable);
+void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
+
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs);
+int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
+int avs_dsp_first_boot_firmware(struct avs_dev *adev);
+
+int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw);
+int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
+int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
+ struct avs_module_entry *mods, u32 num_mods);
+int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw);
+int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
+int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
+ struct avs_module_entry *mods, u32 num_mods);
+
+int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw);
+
+/* Soc component members */
+
+struct avs_soc_component {
+ struct snd_soc_component base;
+ struct avs_tplg *tplg;
+
+ struct list_head node;
+};
+
+#define to_avs_soc_component(comp) \
+ container_of(comp, struct avs_soc_component, base)
+
+extern const struct snd_soc_dai_ops avs_dai_fe_ops;
+
+int avs_register_dmic_component(struct avs_dev *adev, const char *name);
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms);
+int avs_register_hda_component(struct avs_dev *adev, const char *name);
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
+
+int avs_register_all_boards(struct avs_dev *adev);
+void avs_unregister_all_boards(struct avs_dev *adev);
+
+int avs_parse_sched_cfg(struct avs_dev *adev, const char *buf, size_t len);
+
+/* Filesystems integration */
+
+extern const struct attribute_group *avs_attr_groups[];
+
+#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
new file mode 100644
index 000000000000..52e6266a7cb8
--- /dev/null
+++ b/sound/soc/intel/avs/board_selection.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <acpi/nhlt.h>
+#include <linux/platform_device.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+#include "debug.h"
+#include "pcm.h"
+#include "utils.h"
+
+static char *i2s_test;
+module_param(i2s_test, charp, 0444);
+MODULE_PARM_DESC(i2s_test, "Use I2S test-board instead of ACPI, i2s_test=ssp0tdm,ssp1tdm,... 0 to ignore port");
+
+bool obsolete_card_names = IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE);
+module_param_named(obsolete_card_names, obsolete_card_names, bool, 0444);
+MODULE_PARM_DESC(obsolete_card_names, "Use obsolete card names 0=no, 1=yes");
+
+static const struct dmi_system_id kbl_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "AmberLake Y"),
+ },
+ },
+ {}
+};
+
+static const struct dmi_system_id kblr_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
+ },
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+ struct dmi_system_id *dmi_table;
+
+ dmi_table = (struct dmi_system_id *)mach->quirk_data;
+
+ if (!dmi_table || dmi_first_match(dmi_table))
+ return mach;
+ return NULL;
+}
+
+#define AVS_SSP(x) (BIT(x))
+#define AVS_SSP_RANGE(a, b) (GENMASK(b, a))
+
+/* supported I2S board codec configurations */
+static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "avs_nau8825",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "nau8825-tplg.bin",
+ },
+ {
+ .id = "INT343B",
+ .drv_name = "avs_ssm4567",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "ssm4567-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kbl_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kblr_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "MX98927",
+ .drv_name = "avs_max98927",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98927-tplg.bin",
+ },
+ {
+ .id = "10EC5514",
+ .drv_name = "avs_rt5514",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .pdata = (struct avs_mach_pdata[]){ { .tdms = (unsigned long[]){ 0x2 } } },
+ .tplg_filename = "rt5514-tplg.bin",
+ },
+ {
+ .id = "10EC5663",
+ .drv_name = "avs_rt5663",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt5663-tplg.bin",
+ },
+ {
+ .id = "MX98373",
+ .drv_name = "avs_max98373",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98373-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {
+ .id = "ESSX8336",
+ .drv_name = "avs_es8336",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "es8336-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "INT34C3",
+ .drv_name = "avs_tdf8532",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP_RANGE(0, 5),
+ },
+ .pdata = (struct avs_mach_pdata[]){ {
+ .tdms = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }
+ } },
+ .tplg_filename = "tdf8532-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = {
+ {
+ .id = "INT34C2",
+ .drv_name = "avs_rt274",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt274-tplg.bin",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "avs_rt5682",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt5682-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "INT34C2",
+ .drv_name = "avs_rt274",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt274-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
+ {
+ .id = "INT34C2",
+ .drv_name = "avs_rt274",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt274-tplg.bin",
+ },
+ {
+ .id = "10EC0298",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "10EC1308",
+ .drv_name = "avs_rt1308",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt1308-tplg.bin",
+ },
+ {
+ .id = "10EC5640",
+ .uid = "1",
+ .drv_name = "avs_rt5640",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt5640-tplg.bin",
+ },
+ {
+ .id = "10EC5640",
+ .uid = "3",
+ .drv_name = "avs_rt5640",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt5640-tplg.bin",
+ },
+ {
+ .id = "10EC5640",
+ .uid = "2",
+ .drv_name = "avs_rt5640",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "rt5640-tplg.bin",
+ },
+ {
+ .id = "ESSX8336",
+ .drv_name = "avs_es8336",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "es8336-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
+ {
+ .id = "PCM3168A",
+ .drv_name = "avs_pcm3168a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
+ },
+ .tplg_filename = "pcm3168a-tplg.bin",
+ },
+ {}
+};
+
+struct avs_acpi_boards {
+ int id;
+ struct snd_soc_acpi_mach *machs;
+};
+
+#define AVS_MACH_ENTRY(_id, _mach) \
+ { .id = PCI_DEVICE_ID_INTEL_##_id, .machs = (_mach), }
+
+/* supported I2S boards per platform */
+static const struct avs_acpi_boards i2s_boards[] = {
+ AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
+ AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_ADL_N, avs_mbl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_FCL, avs_tgl_i2s_machines),
+ { },
+};
+
+static struct snd_soc_acpi_mach *avs_get_i2s_machines(struct avs_dev *adev)
+{
+ int id, i;
+
+ id = adev->base.pci->device;
+ for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
+ if (i2s_boards[i].id == id)
+ return i2s_boards[i].machs;
+ return NULL;
+}
+
+/* Platform devices spawned by AVS driver are removed with this hook. */
+static void avs_unregister_board(void *pdev)
+{
+ platform_device_unregister(pdev);
+}
+
+static struct platform_device *avs_register_board(struct avs_dev *adev, const char *name,
+ const void *data, size_t size)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_register_data(NULL, name, PLATFORM_DEVID_AUTO, data, size);
+ if (IS_ERR(pdev))
+ return pdev;
+
+ ret = devm_add_action_or_reset(adev->dev, avs_unregister_board, pdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return pdev;
+}
+
+static struct platform_device *avs_register_board_pdata(struct avs_dev *adev, const char *name,
+ struct snd_soc_acpi_mach *mach,
+ struct hda_codec *codec,
+ unsigned long *tdms, char *codec_name)
+{
+ struct avs_mach_pdata *pdata;
+
+ pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->codec = codec;
+ pdata->tdms = tdms;
+ pdata->codec_name = codec_name;
+ pdata->obsolete_card_names = obsolete_card_names;
+ mach->pdata = pdata;
+
+ return avs_register_board(adev, name, mach, sizeof(*mach));
+}
+
+static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
+{
+ struct platform_device *pdev;
+
+ pdev = avs_register_board(adev, "avs_probe_mb", NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_probe_component(adev, dev_name(&pdev->dev));
+}
+
+static int avs_register_dmic_board(struct avs_dev *adev)
+{
+ static struct snd_soc_acpi_mach mach = {
+ .tplg_filename = "dmic-tplg.bin",
+ };
+ struct platform_device *pdev;
+ char *codec_name;
+
+ if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) {
+ dev_dbg(adev->dev, "no DMIC endpoints present\n");
+ return 0;
+ }
+
+ /* DMIC present in Intel PCH is enumerated statically. */
+ pdev = avs_register_board(adev, "dmic-codec", NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ codec_name = devm_kstrdup(adev->dev, dev_name(&pdev->dev), GFP_KERNEL);
+ if (!codec_name)
+ return -ENOMEM;
+
+ pdev = avs_register_board_pdata(adev, "avs_dmic", &mach, NULL, NULL, codec_name);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_dmic_component(adev, dev_name(&pdev->dev));
+}
+
+static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot)
+{
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
+ unsigned long *tdms;
+
+ tdms = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(*tdms), GFP_KERNEL);
+ mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+ AVS_STRING_FMT("i2s", "-test-tplg.bin",
+ ssp_port, tdm_slot));
+ if (!tdms || !mach.tplg_filename)
+ return -ENOMEM;
+
+ tdms[ssp_port] = BIT(tdm_slot);
+ mach.drv_name = "avs_i2s_test";
+ mach.mach_params.i2s_link_mask = AVS_SSP(ssp_port);
+
+ pdev = avs_register_board_pdata(adev, mach.drv_name, &mach, NULL, tdms, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_i2s_component(adev, dev_name(&pdev->dev), AVS_SSP(ssp_port), tdms);
+}
+
+static int avs_register_i2s_test_boards(struct avs_dev *adev)
+{
+ int max_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ int ssp_port, tdm_slot, ret;
+ unsigned long tdm_slots;
+ u32 *array, num_elems;
+
+ if (!i2s_test)
+ return 0;
+
+ ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array);
+ if (ret) {
+ dev_err(adev->dev, "failed to parse i2s_test parameter\n");
+ return ret;
+ }
+
+ num_elems = *array;
+ if (num_elems > max_ssps) {
+ dev_err(adev->dev, "board supports only %d SSP, %d specified\n",
+ max_ssps, num_elems);
+ return -EINVAL;
+ }
+
+ for (ssp_port = 0; ssp_port < num_elems; ssp_port++) {
+ tdm_slots = array[1 + ssp_port];
+ for_each_set_bit(tdm_slot, &tdm_slots, 16) {
+ ret = avs_register_i2s_test_board(adev, ssp_port, tdm_slot);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+ u32 i2s_mask = mach->mach_params.i2s_link_mask;
+ struct platform_device *pdev;
+ unsigned long *tdms = NULL;
+
+ if (mach->pdata)
+ tdms = ((struct avs_mach_pdata *)mach->pdata)->tdms;
+
+ pdev = avs_register_board_pdata(adev, mach->drv_name, mach, NULL, tdms, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_i2s_component(adev, dev_name(&pdev->dev), i2s_mask, tdms);
+}
+
+static int avs_register_i2s_boards(struct avs_dev *adev)
+{
+ int num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ struct snd_soc_acpi_mach *machs;
+ struct snd_soc_acpi_mach *mach;
+ int ret;
+
+ if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) {
+ dev_dbg(adev->dev, "no I2S endpoints present\n");
+ return 0;
+ }
+
+ machs = avs_get_i2s_machines(adev);
+ if (!machs) {
+ dev_dbg(adev->dev, "no I2S endpoints supported\n");
+ return 0;
+ }
+
+ for (mach = machs; mach->id[0]; mach++) {
+ if (!acpi_dev_present(mach->id, mach->uid, -1))
+ continue;
+
+ if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+ dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+ num_ssps, mach->drv_name,
+ (unsigned long)__fls(mach->mach_params.i2s_link_mask));
+ continue;
+ }
+ if (mach->machine_quirk)
+ if (!mach->machine_quirk(mach))
+ continue;
+
+ ret = avs_register_i2s_board(adev, mach);
+ if (ret < 0)
+ dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
+ }
+
+ return 0;
+}
+
+static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
+{
+ struct hdac_device *hdev = &codec->core;
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
+
+ mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
+ hdev->vendor_id);
+ if (!mach.tplg_filename)
+ return -ENOMEM;
+
+ pdev = avs_register_board_pdata(adev, "avs_hdaudio", &mach, codec, NULL, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_hda_component(adev, dev_name(&pdev->dev));
+}
+
+static int avs_register_hda_boards(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_device *hdev;
+ int ret;
+
+ if (!bus->num_codecs) {
+ dev_dbg(adev->dev, "no HDA endpoints present\n");
+ return 0;
+ }
+
+ list_for_each_entry(hdev, &bus->codec_list, list) {
+ struct hda_codec *codec;
+
+ codec = dev_to_hda_codec(&hdev->dev);
+
+ ret = avs_register_hda_board(adev, codec);
+ if (ret < 0)
+ dev_warn(adev->dev, "register hda-%08x failed: %d\n",
+ codec->core.vendor_id, ret);
+ }
+
+ return 0;
+}
+
+int avs_register_all_boards(struct avs_dev *adev)
+{
+ int ret;
+
+#ifdef CONFIG_DEBUG_FS
+ ret = avs_register_probe_board(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
+#endif
+
+ ret = avs_register_dmic_board(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
+ ret);
+
+ ret = avs_register_i2s_test_boards(adev);
+ if (ret)
+ dev_dbg(adev->dev, "enumerate I2S TEST endpoints failed: %d\n", ret);
+
+ ret = avs_register_i2s_boards(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
+ ret);
+
+ ret = avs_register_hda_boards(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
+ ret);
+
+ return 0;
+}
+
+void avs_unregister_all_boards(struct avs_dev *adev)
+{
+ snd_soc_unregister_component(adev->dev);
+}
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
new file mode 100644
index 000000000000..82f50207bb2f
--- /dev/null
+++ b/sound/soc/intel/avs/boards/Kconfig
@@ -0,0 +1,199 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Intel AVS Machine drivers"
+ depends on SND_SOC_INTEL_AVS
+
+comment "Available DSP configurations"
+
+config SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE
+ bool "Use obsolete card names"
+ default n
+ help
+ Use obsolete names for some of avs cards. This option should be
+ used if your system depends on old card names, for example having
+ not up to date UCM files.
+
+config SND_SOC_INTEL_AVS_MACH_DA7219
+ tristate "da7219 I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_DA7219
+ help
+ This adds support for AVS with DA7219 I2S codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_DMIC
+ tristate "DMIC generic board"
+ select SND_SOC_DMIC
+ help
+ This adds support for AVS with Digital Mic array configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_ES8336
+ tristate "es8336 I2S board"
+ depends on X86 && I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_ES8316
+ help
+ This adds support for AVS with ES8336 I2S codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_HDAUDIO
+ tristate "HD-Audio generic board"
+ select SND_SOC_HDA
+ help
+ This adds support for AVS with HDAudio codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_I2S_TEST
+ tristate "I2S test board"
+ help
+ This adds support for I2S test-board which can be used to verify
+ transfer over I2S interface with SSP loopback scenarios.
+
+config SND_SOC_INTEL_AVS_MACH_MAX98927
+ tristate "max98927 I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_MAX98927
+ help
+ This adds support for AVS with MAX98927 I2S codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_MAX98357A
+ tristate "max98357A I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_MAX98357A
+ help
+ This adds support for AVS with MAX98357A I2S codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_MAX98373
+ tristate "max98373 I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_MAX98373
+ help
+ This adds support for AVS with MAX98373 I2S codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_NAU8825
+ tristate "nau8825 I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_NAU8825
+ help
+ This adds support for ASoC machine driver with NAU8825 I2S audio codec.
+ It is meant to be used with AVS driver.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_PCM3168A
+ tristate "pcm3168a I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_PCM3168A_I2C
+ help
+ This adds support for AVS with PCM3168A I2C codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_PROBE
+ tristate "Probing (data) board"
+ depends on DEBUG_FS
+ select SND_HWDEP
+ help
+ This adds support for data probing board which can be used to
+ gather data from runtime stream over compress operations.
+
+config SND_SOC_INTEL_AVS_MACH_RT274
+ tristate "rt274 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT274
+ help
+ This adds support for ASoC machine driver with RT274 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_RT286
+ tristate "rt286 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT286
+ help
+ This adds support for ASoC machine driver with RT286 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_RT298
+ tristate "rt298 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT298
+ help
+ This adds support for ASoC machine driver with RT298 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_RT5514
+ tristate "rt5514 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5514
+ help
+ This adds support for ASoC machine driver with RT5514 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_RT5640
+ tristate "rt5640 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5640
+ help
+ This adds support for ASoC machine board connecting AVS with RT5640,
+ components representing Intel AudioDSP and Realtek 5640 codec respectively.
+ The codec chip is present on I2C bus and the streaming occurs over I2S
+ interface.
+ Say Y or m if you have such a device.
+
+config SND_SOC_INTEL_AVS_MACH_RT5663
+ tristate "rt5663 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5663
+ help
+ This adds support for ASoC machine driver with RT5663 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_RT5682
+ tristate "rt5682 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5682_I2C
+ help
+ This adds support for ASoC machine driver with RT5682 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_AVS_MACH_SSM4567
+ tristate "ssm4567 I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_SSM4567
+ help
+ This adds support for ASoC machine driver with SSM4567 I2S audio codec.
+ It is meant to be used with AVS driver.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+endmenu
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
new file mode 100644
index 000000000000..46ef1babda34
--- /dev/null
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+snd-soc-avs-da7219-y := da7219.o
+snd-soc-avs-dmic-y := dmic.o
+snd-soc-avs-es8336-y := es8336.o
+snd-soc-avs-hdaudio-y := hdaudio.o
+snd-soc-avs-i2s-test-y := i2s_test.o
+snd-soc-avs-max98927-y := max98927.o
+snd-soc-avs-max98357a-y := max98357a.o
+snd-soc-avs-max98373-y := max98373.o
+snd-soc-avs-nau8825-y := nau8825.o
+snd-soc-avs-pcm3168a-y := pcm3168a.o
+snd-soc-avs-probe-y := probe.o
+snd-soc-avs-rt274-y := rt274.o
+snd-soc-avs-rt286-y := rt286.o
+snd-soc-avs-rt298-y := rt298.o
+snd-soc-avs-rt5514-y := rt5514.o
+snd-soc-avs-rt5640-y := rt5640.o
+snd-soc-avs-rt5663-y := rt5663.o
+snd-soc-avs-rt5682-y := rt5682.o
+snd-soc-avs-ssm4567-y := ssm4567.o
+
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_ES8336) += snd-soc-avs-es8336.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_HDAUDIO) += snd-soc-avs-hdaudio.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_I2S_TEST) += snd-soc-avs-i2s-test.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5514) += snd-soc-avs-rt5514.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5640) += snd-soc-avs-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
new file mode 100644
index 000000000000..2b17abcbd2bc
--- /dev/null
+++ b/sound/soc/intel/avs/boards/da7219.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_data/x86/soc.h>
+#include <linux/platform_device.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <uapi/linux/input-event-codes.h>
+#include "../../../codecs/da7219.h"
+#include "../utils.h"
+
+#define DA7219_DAI_NAME "da7219-hifi"
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, DA7219_DAI_NAME);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found. Unable to set/unset codec pll\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret)
+ dev_err(card->dev, "failed to stop PLL: %d\n", ret);
+ } else if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM,
+ 0, DA7219_PLL_FREQ_OUT_98304);
+ if (ret)
+ dev_err(card->dev, "failed to start PLL: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ {"MIC", NULL, "Headset Mic"},
+
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Line Out", NULL, "Platform Clock" },
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins;
+ int clk_freq;
+ int ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ if (soc_intel_is_apl())
+ clk_freq = 19200000;
+ else /* kbl */
+ clk_freq = 24576000;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, clk_freq, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+
+ num_pins = ARRAY_SIZE(card_headset_pins);
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3 | SND_JACK_LINEOUT,
+ jack, pins, num_pins);
+ if (ret) {
+ dev_err(card->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ return snd_soc_component_set_jack(component, jack, NULL);
+}
+
+static void avs_da7219_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+}
+
+static int
+avs_da7219_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-DLGS7219:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, DA7219_DAI_NAME);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->be_hw_params_fixup = avs_da7219_be_fixup;
+ dl->init = avs_da7219_codec_init;
+ dl->exit = avs_da7219_codec_exit;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_da7219_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_da7219";
+ } else {
+ card->driver_name = "avs_da7219";
+ card->long_name = card->name = "AVS I2S DA7219";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_da7219_driver_ids[] = {
+ {
+ .name = "avs_da7219",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids);
+
+static struct platform_driver avs_da7219_driver = {
+ .probe = avs_da7219_probe,
+ .driver = {
+ .name = "avs_da7219",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_da7219_driver_ids,
+};
+
+module_platform_driver(avs_da7219_driver);
+
+MODULE_DESCRIPTION("Intel da7219 machine driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
new file mode 100644
index 000000000000..bf6f580a5164
--- /dev/null
+++ b/sound/soc/intel/avs/boards/dmic.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../utils.h"
+
+SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC Pin")));
+SND_SOC_DAILINK_DEF(dmic_wov_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC WoV Pin")));
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static int avs_create_dai_links(struct device *dev, const char *codec_name,
+ struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ const int num_dl = 2;
+
+ dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->codecs)
+ return -ENOMEM;
+
+ dl->codecs->name = devm_kstrdup(dev, codec_name, GFP_KERNEL);
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "dmic-hifi");
+ if (!dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl[0].num_cpus = 1;
+ dl[0].num_codecs = 1;
+ dl[0].platforms = platform;
+ dl[0].num_platforms = 1;
+ dl[0].nonatomic = 1;
+ dl[0].no_pcm = 1;
+ dl[0].capture_only = 1;
+ memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+ dl[0].name = "DMIC";
+ dl[0].cpus = dmic_pin;
+ dl[0].id = 0;
+ dl[1].name = "DMIC WoV";
+ dl[1].cpus = dmic_wov_pin;
+ dl[1].id = 1;
+ dl[1].ignore_suspend = 1;
+
+ *links = dl;
+ *num_links = num_dl;
+ return 0;
+}
+
+static int avs_dmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ int ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ ret = avs_create_dai_links(dev, pdata->codec_name, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_dmic";
+ } else {
+ card->driver_name = "avs_dmic";
+ card->long_name = card->name = "AVS DMIC";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_dmic_driver_ids[] = {
+ {
+ .name = "avs_dmic",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids);
+
+static struct platform_driver avs_dmic_driver = {
+ .probe = avs_dmic_probe,
+ .driver = {
+ .name = "avs_dmic",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_dmic_driver_ids,
+};
+
+module_platform_driver(avs_dmic_driver);
+
+MODULE_DESCRIPTION("Intel DMIC machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
new file mode 100644
index 000000000000..301cfb3cf15b
--- /dev/null
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2023 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/processor.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <asm/cpu_device_id.h>
+#include "../utils.h"
+
+#define ES8336_CODEC_DAI "ES8316 HiFi"
+
+struct avs_card_drvdata {
+ struct snd_soc_jack jack;
+ struct gpio_desc *gpiod;
+};
+
+static const struct acpi_gpio_params enable_gpio = { 0, 0, true };
+
+static const struct acpi_gpio_mapping speaker_gpios[] = {
+ { "speaker-enable-gpios", &enable_gpio, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { }
+};
+
+static int avs_es8336_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct avs_card_drvdata *data;
+ bool speaker_en;
+
+ data = snd_soc_card_get_drvdata(card);
+ /* As enable_gpio has active_low=true, logic is inverted. */
+ speaker_en = !SND_SOC_DAPM_EVENT_ON(event);
+
+ gpiod_set_value_cansleep(data->gpiod, speaker_en);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ avs_es8336_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled by the "Speaker Power" widget.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+
+ /* Mic route map */
+ {"MIC1", NULL, "Internal Mic"},
+ {"MIC2", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_jack_pin *pins;
+ struct avs_card_drvdata *data;
+ struct gpio_desc *gpiod;
+ int num_pins, ret;
+
+ data = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &data->jack, pins, num_pins);
+ if (ret)
+ return ret;
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dai->dev, speaker_gpios);
+ if (ret)
+ dev_warn(codec_dai->dev, "Unable to add GPIO mapping table\n");
+
+ gpiod = gpiod_get_optional(codec_dai->dev, "speaker-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod))
+ return dev_err_probe(codec_dai->dev, PTR_ERR(gpiod), "Get gpiod failed: %ld\n",
+ PTR_ERR(gpiod));
+
+ data->gpiod = gpiod;
+ snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_soc_component_set_jack(component, &data->jack, NULL);
+
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ return 0;
+}
+
+static void avs_es8336_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct avs_card_drvdata *data = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+
+ snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+ gpiod_put(data->gpiod);
+}
+
+static int avs_es8336_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ int clk_freq;
+ int ret;
+
+ switch (boot_cpu_data.x86_vfm) {
+ case INTEL_KABYLAKE_L:
+ case INTEL_KABYLAKE:
+ clk_freq = 24000000;
+ break;
+ default:
+ clk_freq = 19200000;
+ break;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1, clk_freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_es8336_ops = {
+ .hw_params = avs_es8336_hw_params,
+};
+
+static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSPN to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_3LE);
+
+ return 0;
+}
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-ESSX8336:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, ES8336_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_es8336_codec_init;
+ dl->exit = avs_es8336_codec_exit;
+ dl->be_hw_params_fixup = avs_es8336_be_fixup;
+ dl->ops = &avs_es8336_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI);
+ struct avs_card_drvdata *data = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, &data->jack, NULL);
+}
+
+static int avs_es8336_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct avs_card_drvdata *data;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!data || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_es8336";
+ } else {
+ card->driver_name = "avs_es8336";
+ card->long_name = card->name = "AVS I2S ES8336";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, data);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_es8336_driver_ids[] = {
+ {
+ .name = "avs_es8336",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids);
+
+static struct platform_driver avs_es8336_driver = {
+ .probe = avs_es8336_probe,
+ .driver = {
+ .name = "avs_es8336",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_es8336_driver_ids,
+};
+
+module_platform_driver(avs_es8336_driver);
+
+MODULE_DESCRIPTION("Intel es8336 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
new file mode 100644
index 000000000000..aec769e2396c
--- /dev/null
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/hda.h"
+#include "../utils.h"
+
+static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
+ struct snd_soc_dai_link **links)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ struct hda_pcm *pcm;
+ const char *cname = dev_name(&codec->core.dev);
+ int i;
+
+ dl = devm_kcalloc(dev, pcm_count, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+ for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+ dl[i].name = devm_kasprintf(dev, GFP_KERNEL, "%s link%d", cname, i);
+ if (!dl[i].name)
+ return -ENOMEM;
+
+ dl[i].id = i;
+ dl[i].nonatomic = 1;
+ dl[i].no_pcm = 1;
+ dl[i].platforms = platform;
+ dl[i].num_platforms = 1;
+ dl[i].ignore_pmdown_time = 1;
+
+ dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ if (!dl[i].codecs || !dl[i].cpus)
+ return -ENOMEM;
+
+ dl[i].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d", cname, i);
+ if (!dl[i].cpus->dai_name)
+ return -ENOMEM;
+
+ dl[i].codecs->name = devm_kstrdup_const(dev, cname, GFP_KERNEL);
+ if (!dl[i].codecs->name)
+ return -ENOMEM;
+
+ dl[i].codecs->dai_name = pcm->name;
+ dl[i].num_codecs = 1;
+ dl[i].num_cpus = 1;
+ }
+
+ *links = dl;
+ return 0;
+}
+
+/* Should be aligned with SectionPCM's name from topology */
+#define FEDAI_NAME_PREFIX "HDMI"
+
+static struct snd_pcm *
+avs_card_hdmi_pcm_at(struct snd_soc_card *card, int hdmi_idx)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ int dir = SNDRV_PCM_STREAM_PLAYBACK;
+
+ for_each_card_rtds(card, rtd) {
+ struct snd_pcm *spcm;
+ int ret, n;
+
+ spcm = rtd->pcm ? rtd->pcm->streams[dir].pcm : NULL;
+ if (!spcm || !strstr(spcm->id, FEDAI_NAME_PREFIX))
+ continue;
+
+ ret = sscanf(spcm->id, FEDAI_NAME_PREFIX "%d", &n);
+ if (ret != 1)
+ continue;
+ if (n == hdmi_idx)
+ return rtd->pcm;
+ }
+
+ return NULL;
+}
+
+static int avs_card_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ struct avs_mach_pdata *pdata = mach->pdata;
+ struct hda_codec *codec = pdata->codec;
+ struct hda_pcm *hpcm;
+ /* Topology pcm indexing is 1-based */
+ int i = 1;
+
+ list_for_each_entry(hpcm, &codec->pcm_list_head, list) {
+ struct snd_pcm *spcm;
+
+ spcm = avs_card_hdmi_pcm_at(card, i);
+ if (spcm) {
+ hpcm->pcm = spcm;
+ hpcm->device = spcm->device;
+ dev_info(card->dev, "%s: mapping HDMI converter %d to PCM %d (%p)\n",
+ __func__, i, hpcm->device, spcm);
+ } else {
+ hpcm->pcm = NULL;
+ hpcm->device = SNDRV_PCM_INVALID_DEVICE;
+ dev_warn(card->dev, "%s: no PCM in topology for HDMI converter %d\n",
+ __func__, i);
+ }
+ i++;
+ }
+
+ return hda_codec_probe_complete(codec);
+}
+
+static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_dai_link *links = NULL;
+ struct snd_soc_card *card = rtm->card;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ int ret, pcm_count = 0;
+
+ mach = dev_get_platdata(card->dev);
+ pdata = mach->pdata;
+ codec = pdata->codec;
+
+ if (list_empty(&codec->pcm_list_head))
+ return -EINVAL;
+ list_for_each_entry(pcm, &codec->pcm_list_head, list)
+ pcm_count++;
+
+ ret = avs_create_dai_links(card->dev, codec, pcm_count, &links);
+ if (ret < 0) {
+ dev_err(card->dev, "create links failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_pcm_runtimes(card, links, pcm_count);
+ if (ret < 0) {
+ dev_err(card->dev, "add links failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_link probing_link = {
+ .name = "probing-LINK",
+ .id = -1,
+ .nonatomic = 1,
+ .no_pcm = 1,
+ .cpus = &snd_soc_dummy_dlc,
+ .num_cpus = 1,
+ .init = avs_probing_link_init,
+};
+
+static int avs_hdaudio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *binder;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ struct hda_codec *codec;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+ codec = pdata->codec;
+
+ /* codec may be unloaded before card's probe() fires */
+ if (!device_is_registered(&codec->core.dev))
+ return -ENODEV;
+
+ binder = devm_kmemdup(dev, &probing_link, sizeof(probing_link), GFP_KERNEL);
+ if (!binder)
+ return -ENOMEM;
+
+ binder->platforms = devm_kzalloc(dev, sizeof(*binder->platforms), GFP_KERNEL);
+ binder->codecs = devm_kzalloc(dev, sizeof(*binder->codecs), GFP_KERNEL);
+ if (!binder->platforms || !binder->codecs)
+ return -ENOMEM;
+
+ binder->codecs->name = devm_kstrdup_const(dev, dev_name(&codec->core.dev), GFP_KERNEL);
+ if (!binder->codecs->name)
+ return -ENOMEM;
+
+ binder->platforms->name = dev_name(dev);
+ binder->num_platforms = 1;
+ binder->codecs->dai_name = "codec-probing-DAI";
+ binder->num_codecs = 1;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = devm_kasprintf(dev, GFP_KERNEL, "hdaudioB%dD%d", codec->bus->core.idx,
+ codec->core.addr);
+ if (!card->name)
+ return -ENOMEM;
+ } else {
+ card->driver_name = "avs_hdaudio";
+ if (hda_codec_is_display(codec))
+ card->long_name = card->name = "AVS HDMI";
+ else
+ card->long_name = card->name = "AVS HD-Audio";
+ }
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = binder;
+ card->num_links = 1;
+ card->fully_routed = true;
+ if (hda_codec_is_display(codec))
+ card->late_probe = avs_card_late_probe;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_hdaudio_driver_ids[] = {
+ {
+ .name = "avs_hdaudio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids);
+
+static struct platform_driver avs_hdaudio_driver = {
+ .probe = avs_hdaudio_probe,
+ .driver = {
+ .name = "avs_hdaudio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_hdaudio_driver_ids,
+};
+
+module_platform_driver(avs_hdaudio_driver)
+
+MODULE_DESCRIPTION("Intel HD-Audio machine driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
new file mode 100644
index 000000000000..9a6b89ffdf14
--- /dev/null
+++ b/sound/soc/intel/avs/boards/i2s_test.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include "../utils.h"
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ if (!dl->name || !dl->cpus)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs = &snd_soc_dummy_dlc;
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_i2s_test_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ if (!avs_mach_singular_ssp(mach)) {
+ dev_err(dev, "Invalid SSP configuration\n");
+ return -EINVAL;
+ }
+ ssp_port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, ssp_port)) {
+ dev_err(dev, "Invalid TDM configuration\n");
+ return -EINVAL;
+ }
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("ssp", "-loopback", ssp_port, tdm_slot));
+ } else {
+ card->driver_name = "avs_i2s_test";
+ card->long_name = card->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("AVS I2S TEST-", "",
+ ssp_port, tdm_slot));
+ }
+ if (!card->name)
+ return -ENOMEM;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d\n", ret);
+ return ret;
+ }
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_i2s_test_driver_ids[] = {
+ {
+ .name = "avs_i2s_test",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids);
+
+static struct platform_driver avs_i2s_test_driver = {
+ .probe = avs_i2s_test_probe,
+ .driver = {
+ .name = "avs_i2s_test",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_i2s_test_driver_ids,
+};
+
+module_platform_driver(avs_i2s_test_driver);
+
+MODULE_DESCRIPTION("Intel i2s test machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
new file mode 100644
index 000000000000..e9a87804f918
--- /dev/null
+++ b/sound/soc/intel/avs/boards/max98357a.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include "../utils.h"
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ { "Spk", NULL, "Speaker" },
+};
+
+static int
+avs_max98357a_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 16 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "MX98357A:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "HiFi");
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->be_hw_params_fixup = avs_max98357a_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->playback_only = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_max98357a_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_max98357a";
+ } else {
+ card->driver_name = "avs_max98357a";
+ card->long_name = card->name = "AVS I2S MAX98357A";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_max98357a_driver_ids[] = {
+ {
+ .name = "avs_max98357a",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids);
+
+static struct platform_driver avs_max98357a_driver = {
+ .probe = avs_max98357a_probe,
+ .driver = {
+ .name = "avs_max98357a",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_max98357a_driver_ids,
+};
+
+module_platform_driver(avs_max98357a_driver)
+
+MODULE_DESCRIPTION("Intel max98357a machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
new file mode 100644
index 000000000000..8b45b643ca29
--- /dev/null
+++ b/sound/soc/intel/avs/boards/max98373.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include "../utils.h"
+
+#define MAX98373_DEV0_NAME "i2c-MX98373:00"
+#define MAX98373_DEV1_NAME "i2c-MX98373:01"
+#define MAX98373_CODEC_NAME "max98373-aif1"
+
+static struct snd_soc_codec_conf card_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX98373_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX98373_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static int
+avs_max98373_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 16 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int avs_max98373_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int ret, i;
+
+ for_each_rtd_codec_dais(runtime, i, codec_dai) {
+ if (!strcmp(codec_dai->component->name, MAX98373_DEV0_NAME)) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16);
+ if (ret < 0) {
+ dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ if (!strcmp(codec_dai->component->name, MAX98373_DEV1_NAME)) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16);
+ if (ret < 0) {
+ dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops avs_max98373_ops = {
+ .hw_params = avs_max98373_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_DEV0_NAME);
+ dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_CODEC_NAME);
+ dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_DEV1_NAME);
+ dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_CODEC_NAME);
+ if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name ||
+ !dl->codecs[1].name || !dl->codecs[1].dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 2;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->be_hw_params_fixup = avs_max98373_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->ignore_pmdown_time = 1;
+ dl->ops = &avs_max98373_ops;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_max98373_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_max98373";
+ } else {
+ card->driver_name = "avs_max98373";
+ card->long_name = card->name = "AVS I2S MAX98373";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->codec_conf = card_codec_conf;
+ card->num_configs = ARRAY_SIZE(card_codec_conf);
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_max98373_driver_ids[] = {
+ {
+ .name = "avs_max98373",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids);
+
+static struct platform_driver avs_max98373_driver = {
+ .probe = avs_max98373_probe,
+ .driver = {
+ .name = "avs_max98373",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_max98373_driver_ids,
+};
+
+module_platform_driver(avs_max98373_driver)
+
+MODULE_DESCRIPTION("Intel max98373 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
new file mode 100644
index 000000000000..db073125fa4d
--- /dev/null
+++ b/sound/soc/intel/avs/boards/max98927.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include "../utils.h"
+
+#define MAX98927_DEV0_NAME "i2c-MX98927:00"
+#define MAX98927_DEV1_NAME "i2c-MX98927:01"
+#define MAX98927_CODEC_NAME "max98927-aif1"
+
+static struct snd_soc_codec_conf card_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX98927_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX98927_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static int
+avs_max98927_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 16 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int avs_max98927_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0;
+ int i;
+
+ for_each_rtd_codec_dais(runtime, i, codec_dai) {
+ if (!strcmp(codec_dai->component->name, MAX98927_DEV0_NAME))
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16);
+ else if (!strcmp(codec_dai->component->name, MAX98927_DEV1_NAME))
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16);
+
+ if (ret < 0) {
+ dev_err(runtime->dev, "hw_params for %s failed: %d\n",
+ codec_dai->component->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops avs_max98927_ops = {
+ .hw_params = avs_max98927_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV0_NAME);
+ dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME);
+ dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV1_NAME);
+ dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME);
+ if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name ||
+ !dl->codecs[1].name || !dl->codecs[1].dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 2;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->be_hw_params_fixup = avs_max98927_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->ignore_pmdown_time = 1;
+ dl->ops = &avs_max98927_ops;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_max98927_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_max98927";
+ } else {
+ card->driver_name = "avs_max98927";
+ card->long_name = card->name = "AVS I2S MAX98927";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->codec_conf = card_codec_conf;
+ card->num_configs = ARRAY_SIZE(card_codec_conf);
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_max98927_driver_ids[] = {
+ {
+ .name = "avs_max98927",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids);
+
+static struct platform_driver avs_max98927_driver = {
+ .probe = avs_max98927_probe,
+ .driver = {
+ .name = "avs_max98927",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_max98927_driver_ids,
+};
+
+module_platform_driver(avs_max98927_driver)
+
+MODULE_DESCRIPTION("Intel max98927 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
new file mode 100644
index 000000000000..d44edacbfc9a
--- /dev/null
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/nau8825.h"
+#include "../utils.h"
+
+#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
+
+static int
+avs_nau8825_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EINVAL;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_MCLK, 24000000,
+ SND_SOC_CLOCK_IN);
+ else
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "Set sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, avs_nau8825_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ { "MIC", NULL, "Headset Mic" },
+
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ /*
+ * 4 buttons here map to the google Reference headset.
+ * The use of these buttons can be decided by the user space.
+ */
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ jack, pins, num_pins);
+ if (ret)
+ return ret;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+}
+
+static void avs_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+}
+
+static int
+avs_nau8825_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_nau8825_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ break;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate, runtime->rate * 256);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate, runtime->rate * 256);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ break;
+ }
+
+ return ret;
+}
+
+
+static const struct snd_soc_ops avs_nau8825_ops = {
+ .trigger = avs_nau8825_trigger,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10508825:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, SKL_NUVOTON_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_nau8825_codec_init;
+ dl->exit = avs_nau8825_codec_exit;
+ dl->be_hw_params_fixup = avs_nau8825_be_fixup;
+ dl->ops = &avs_nau8825_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+ int stream = SNDRV_PCM_STREAM_PLAYBACK;
+
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EINVAL;
+ }
+
+ if (snd_soc_dai_stream_active(codec_dai, stream) &&
+ snd_soc_dai_get_widget(codec_dai, stream)->active)
+ snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_nau8825_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_nau8825";
+ } else {
+ card->driver_name = "avs_nau8825";
+ card->long_name = card->name = "AVS I2S NAU8825";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_nau8825_driver_ids[] = {
+ {
+ .name = "avs_nau8825",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids);
+
+static struct platform_driver avs_nau8825_driver = {
+ .probe = avs_nau8825_probe,
+ .driver = {
+ .name = "avs_nau8825",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_nau8825_driver_ids,
+};
+
+module_platform_driver(avs_nau8825_driver)
+
+MODULE_DESCRIPTION("Intel nau8825 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c
new file mode 100644
index 000000000000..b5bebadbbcb2
--- /dev/null
+++ b/sound/soc/intel/avs/boards/pcm3168a.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2024-2025 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.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-acpi.h>
+#include "../utils.h"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),
+ SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL),
+ SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL),
+ SND_SOC_DAPM_LINE("CPB Line Out", NULL),
+ SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL),
+ SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL),
+ SND_SOC_DAPM_LINE("CPB Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ { "CPB Stereo HP 1", NULL, "AOUT1L" },
+ { "CPB Stereo HP 1", NULL, "AOUT1R" },
+ { "CPB Stereo HP 2", NULL, "AOUT2L" },
+ { "CPB Stereo HP 2", NULL, "AOUT2R" },
+ { "CPB Stereo HP 3", NULL, "AOUT3L" },
+ { "CPB Stereo HP 3", NULL, "AOUT3R" },
+ { "CPB Line Out", NULL, "AOUT4L" },
+ { "CPB Line Out", NULL, "AOUT4R" },
+
+ { "AIN1L", NULL, "CPB Stereo Mic 1" },
+ { "AIN1R", NULL, "CPB Stereo Mic 1" },
+ { "AIN2L", NULL, "CPB Stereo Mic 2" },
+ { "AIN2R", NULL, "CPB Stereo Mic 2" },
+ { "AIN3L", NULL, "CPB Line In" },
+ { "AIN3R", NULL, "CPB Line In" },
+};
+
+static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* Set SSP to 24 bit. */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+SND_SOC_DAILINK_DEF(pcm3168a_dac,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac")));
+SND_SOC_DAILINK_DEF(pcm3168a_adc,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc")));
+SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
+SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin")));
+
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ const int num_dl = 2;
+
+ dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl[0].num_cpus = 1;
+ dl[0].num_codecs = 1;
+ dl[0].platforms = platform;
+ dl[0].num_platforms = 1;
+ dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+ dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup;
+ dl[0].nonatomic = 1;
+ dl[0].no_pcm = 1;
+ memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+ dl[0].name = "SSP0-Codec-dac";
+ dl[0].cpus = cpu_ssp0;
+ dl[0].codecs = pcm3168a_dac;
+ dl[1].name = "SSP2-Codec-adc";
+ dl[1].cpus = cpu_ssp2;
+ dl[1].codecs = pcm3168a_adc;
+
+ *links = dl;
+ *num_links = num_dl;
+ return 0;
+}
+
+static int avs_pcm3168a_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_pcm3168a";
+ } else {
+ card->driver_name = "avs_pcm3168a";
+ card->long_name = card->name = "AVS I2S PCM3168A";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_pcm3168a_driver_ids[] = {
+ {
+ .name = "avs_pcm3168a",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids);
+
+static struct platform_driver avs_pcm3168a_driver = {
+ .probe = avs_pcm3168a_probe,
+ .driver = {
+ .name = "avs_pcm3168a",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_pcm3168a_driver_ids,
+};
+
+module_platform_driver(avs_pcm3168a_driver);
+
+MODULE_DESCRIPTION("Intel pcm3168a machine driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c
new file mode 100644
index 000000000000..73884f8a535c
--- /dev/null
+++ b/sound/soc/intel/avs/boards/probe.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ if (!dl)
+ return -ENOMEM;
+
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->platforms = devm_kzalloc(dev, sizeof(*dl->platforms), GFP_KERNEL);
+ if (!dl->cpus || !dl->platforms)
+ return -ENOMEM;
+
+ dl->name = "Compress Probe Capture";
+ dl->cpus->dai_name = "Probe Extraction CPU DAI";
+ dl->num_cpus = 1;
+ dl->codecs = &snd_soc_dummy_dlc;
+ dl->num_codecs = 1;
+ dl->platforms->name = dev_name(dev);
+ dl->num_platforms = 1;
+ dl->nonatomic = 1;
+
+ *links = dl;
+ *num_links = 1;
+ return 0;
+}
+
+static int avs_probe_mb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ card->driver_name = "avs_probe_mb";
+ card->long_name = card->name = "AVS PROBE";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_probe_mb_driver_ids[] = {
+ {
+ .name = "avs_probe_mb",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids);
+
+static struct platform_driver avs_probe_mb_driver = {
+ .probe = avs_probe_mb_probe,
+ .driver = {
+ .name = "avs_probe_mb",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_probe_mb_driver_ids,
+};
+
+module_platform_driver(avs_probe_mb_driver);
+
+MODULE_DESCRIPTION("Intel probe machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
new file mode 100644
index 000000000000..a689f4c80867
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt274.h"
+#include "../utils.h"
+
+#define AVS_RT274_FREQ_OUT 24000000
+#define AVS_RT274_BE_FIXUP_RATE 48000
+#define RT274_CODEC_DAI "rt274-aif1"
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static int
+avs_rt274_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
+ if (!codec_dai)
+ return -EINVAL;
+
+ /* Codec needs clock for Jack detection and button press */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, AVS_RT274_FREQ_OUT,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret);
+ return ret;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ int ratio = 100;
+
+ snd_soc_dai_set_bclk_ratio(codec_dai, ratio);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK,
+ AVS_RT274_BE_FIXUP_RATE * ratio, AVS_RT274_FREQ_OUT);
+ if (ret) {
+ dev_err(codec_dai->dev, "failed to enable PLL2: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, avs_rt274_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ {"Headphone Jack", NULL, "HPO Pin"},
+ {"MIC", NULL, "Mic Jack"},
+
+ {"Headphone Jack", NULL, "Platform Clock"},
+ {"MIC", NULL, "Platform Clock"},
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
+ num_pins);
+ if (ret)
+ return ret;
+
+ snd_soc_component_set_jack(component, jack, NULL);
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec pcm format %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ return 0;
+}
+
+static void avs_rt274_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+}
+
+static int avs_rt274_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = AVS_RT274_BE_FIXUP_RATE;
+ channels->min = channels->max = 2;
+
+ /* set SSPN to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT34C2:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT274_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt274_codec_init;
+ dl->exit = avs_rt274_codec_exit;
+ dl->be_hw_params_fixup = avs_rt274_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt274_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt274";
+ } else {
+ card->driver_name = "avs_rt274";
+ card->long_name = card->name = "AVS I2S ALC274";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt274_driver_ids[] = {
+ {
+ .name = "avs_rt274",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids);
+
+static struct platform_driver avs_rt274_driver = {
+ .probe = avs_rt274_probe,
+ .driver = {
+ .name = "avs_rt274",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt274_driver_ids,
+};
+
+module_platform_driver(avs_rt274_driver);
+
+MODULE_DESCRIPTION("Intel rt274 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
new file mode 100644
index 000000000000..4c9ac545555a
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt286.h"
+#include "../utils.h"
+
+#define RT286_CODEC_DAI "rt286-aif1"
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ /* HP jack connectors - unknown if we have jack detect */
+ {"Headphone Jack", NULL, "HPO Pin"},
+ {"MIC1", NULL, "Mic Jack"},
+
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt286_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ jack, pins, num_pins);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+}
+
+static void avs_rt286_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+}
+
+static int avs_rt286_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int
+avs_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt286_ops = {
+ .hw_params = avs_rt286_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343A:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT286_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt286_codec_init;
+ dl->exit = avs_rt286_codec_exit;
+ dl->be_hw_params_fixup = avs_rt286_be_fixup;
+ dl->ops = &avs_rt286_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT286_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT286_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt286_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt286";
+ } else {
+ card->driver_name = "avs_rt286";
+ card->long_name = card->name = "AVS I2S ALC286";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt286_driver_ids[] = {
+ {
+ .name = "avs_rt286",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids);
+
+static struct platform_driver avs_rt286_driver = {
+ .probe = avs_rt286_probe,
+ .driver = {
+ .name = "avs_rt286",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt286_driver_ids,
+};
+
+module_platform_driver(avs_rt286_driver);
+
+MODULE_DESCRIPTION("Intel rt286 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
new file mode 100644
index 000000000000..2d7a7748d577
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt298.h"
+#include "../utils.h"
+
+#define RT298_CODEC_DAI "rt298-aif1"
+
+static const struct dmi_system_id kblr_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
+ },
+ },
+ {}
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ /* HP jack connectors - unknown if we have jack detect */
+ {"Headphone Jack", NULL, "HPO Pin"},
+ {"MIC1", NULL, "Mic Jack"},
+
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt298_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ jack, pins, num_pins);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+}
+
+static void avs_rt298_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+}
+
+static int avs_rt298_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int
+avs_rt298_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int clk_freq;
+ int ret;
+
+ if (dmi_first_match(kblr_dmi_table))
+ clk_freq = 24000000;
+ else
+ clk_freq = 19200000;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "Set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt298_ops = {
+ .hw_params = avs_rt298_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343A:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT298_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ if (dmi_first_match(kblr_dmi_table))
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt298_codec_init;
+ dl->exit = avs_rt298_codec_exit;
+ dl->be_hw_params_fixup = avs_rt298_be_fixup;
+ dl->ops = &avs_rt298_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT298_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT298_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt298_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt298";
+ } else {
+ card->driver_name = "avs_rt298";
+ card->long_name = card->name = "AVS I2S ALC298";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt298_driver_ids[] = {
+ {
+ .name = "avs_rt298",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids);
+
+static struct platform_driver avs_rt298_driver = {
+ .probe = avs_rt298_probe,
+ .driver = {
+ .name = "avs_rt298",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt298_driver_ids,
+};
+
+module_platform_driver(avs_rt298_driver);
+
+MODULE_DESCRIPTION("Intel rt298 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c
new file mode 100644
index 000000000000..22139eaad83a
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5514.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2023 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5514.h"
+#include "../utils.h"
+
+#define RT5514_CODEC_DAI "rt5514-aif1"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ /* DMIC */
+ { "DMIC1L", NULL, "DMIC" },
+ { "DMIC1R", NULL, "DMIC" },
+ { "DMIC2L", NULL, "DMIC" },
+ { "DMIC2R", NULL, "DMIC" },
+};
+
+static int avs_rt5514_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(runtime->card);
+ int ret = snd_soc_dapm_ignore_suspend(dapm, "DMIC");
+
+ if (ret)
+ dev_err(runtime->dev, "DMIC - Ignore suspend failed = %d\n", ret);
+
+ return ret;
+}
+
+static int avs_rt5514_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 4;
+
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int avs_rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0, 8, 16);
+ if (ret < 0) {
+ dev_err(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "set sysclk err: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5514_ops = {
+ .hw_params = avs_rt5514_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5514:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5514_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt5514_codec_init;
+ dl->be_hw_params_fixup = avs_rt5514_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->capture_only = 1;
+ dl->ops = &avs_rt5514_ops;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_rt5514_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt5514";
+ } else {
+ card->driver_name = "avs_rt5514";
+ card->long_name = card->name = "AVS I2S ALC5514";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5514_driver_ids[] = {
+ {
+ .name = "avs_rt5514",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids);
+
+static struct platform_driver avs_rt5514_driver = {
+ .probe = avs_rt5514_probe,
+ .driver = {
+ .name = "avs_rt5514",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5514_driver_ids,
+};
+
+module_platform_driver(avs_rt5514_driver);
+
+MODULE_DESCRIPTION("Intel rt5514 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c
new file mode 100644
index 000000000000..2990d32f2301
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5640.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022-2025 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5640.h"
+#include "../utils.h"
+
+#define AVS_RT5640_MCLK_HZ 19200000
+#define RT5640_CODEC_DAI "rt5640-aif1"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ { "Headphone Jack", NULL, "HPOR" },
+ { "Headphone Jack", NULL, "HPOL" },
+ { "IN2P", NULL, "Mic Jack" },
+ { "IN2P", NULL, "MICBIAS1" },
+ { "Speaker", NULL, "SPOLP" },
+ { "Speaker", NULL, "SPOLN" },
+ { "Speaker", NULL, "SPORP" },
+ { "Speaker", NULL, "SPORN" },
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
+ num_pins);
+ if (ret)
+ return ret;
+
+ snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ return 0;
+}
+
+static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+
+ snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* Format 24/32 is MSB-aligned for HDAudio and LSB-aligned for I2S. */
+ if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+ snd_mask_set_format(fmask, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, AVS_RT5640_MCLK_HZ,
+ params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(runtime->dev, "Set codec PLL failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(runtime->dev, "Set codec SCLK failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = rt5640_sel_asrc_clk_src(codec_dai->component,
+ RT5640_DA_STEREO_FILTER | RT5640_AD_STEREO_FILTER |
+ RT5640_DA_MONO_L_FILTER | RT5640_DA_MONO_R_FILTER |
+ RT5640_AD_MONO_L_FILTER | RT5640_AD_MONO_R_FILTER,
+ RT5640_CLK_SEL_ASRC);
+ if (ret)
+ dev_err(runtime->dev, "Set codec ASRC failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5640_ops = {
+ .hw_params = avs_rt5640_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_acpi_mach *mach,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ u32 uid = 0;
+ int ret;
+
+ if (mach->uid) {
+ ret = kstrtou32(mach->uid, 0, &uid);
+ if (ret)
+ return ret;
+ uid--; /* 0-based indexing. */
+ }
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%d", uid);
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt5640_codec_init;
+ dl->exit = avs_rt5640_codec_exit;
+ dl->be_hw_params_fixup = avs_rt5640_be_fixup;
+ dl->ops = &avs_rt5640_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt5640_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, mach, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (mach->uid) {
+ card->name = devm_kasprintf(dev, GFP_KERNEL, "AVS I2S ALC5640.%s", mach->uid);
+ if (!card->name)
+ return -ENOMEM;
+ } else {
+ card->name = "AVS I2S ALC5640";
+ }
+ card->driver_name = "avs_rt5640";
+ card->long_name = card->name;
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5640_driver_ids[] = {
+ {
+ .name = "avs_rt5640",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids);
+
+static struct platform_driver avs_rt5640_driver = {
+ .probe = avs_rt5640_probe,
+ .driver = {
+ .name = "avs_rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5640_driver_ids,
+};
+
+module_platform_driver(avs_rt5640_driver);
+
+MODULE_DESCRIPTION("Intel rt5640 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
new file mode 100644
index 000000000000..68fea325376a
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5663.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022-2023 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5663.h"
+#include "../utils.h"
+
+#define RT5663_CODEC_DAI "rt5663-aif"
+
+struct rt5663_private {
+ struct snd_soc_jack jack;
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ /* HP jack connectors */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* Mic jacks */
+ { "IN1P", NULL, "Headset Mic" },
+ { "IN1N", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct rt5663_private *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = &priv->jack;
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
+ sizeof(card_headset_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack,
+ pins, num_pins);
+ if (ret)
+ return ret;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+
+ return 0;
+}
+
+static void avs_rt5663_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, NULL, NULL);
+}
+
+static int
+avs_rt5663_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSPN to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_rt5663_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */
+ rt5663_sel_asrc_clk_src(codec_dai->component,
+ RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER,
+ RT5663_CLK_SEL_I2S1_ASRC);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5663_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5663_ops = {
+ .hw_params = avs_rt5663_hw_params,
+};
+
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5663:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5663_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt5663_codec_init;
+ dl->exit = avs_rt5663_codec_exit;
+ dl->be_hw_params_fixup = avs_rt5663_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->ops = &avs_rt5663_ops;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt5663_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct rt5663_private *priv;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!priv || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt5663";
+ } else {
+ card->driver_name = "avs_rt5663";
+ card->long_name = card->name = "AVS I2S ALC5663";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, priv);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5663_driver_ids[] = {
+ {
+ .name = "avs_rt5663",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids);
+
+static struct platform_driver avs_rt5663_driver = {
+ .probe = avs_rt5663_probe,
+ .driver = {
+ .name = "avs_rt5663",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5663_driver_ids,
+};
+
+module_platform_driver(avs_rt5663_driver);
+
+MODULE_DESCRIPTION("Intel rt5663 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
new file mode 100644
index 000000000000..81863728da1d
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/clk.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rt5682.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../common/soc-intel-quirks.h"
+#include "../../../codecs/rt5682.h"
+#include "../utils.h"
+
+#define AVS_RT5682_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0))
+#define AVS_RT5682_SSP_CODEC_MASK (GENMASK(2, 0))
+#define AVS_RT5682_MCLK_EN BIT(3)
+#define AVS_RT5682_MCLK_24MHZ BIT(4)
+#define AVS_RT5682_CODEC_DAI_NAME "rt5682-aif1"
+
+/* Default: MCLK on, MCLK 19.2M, SSP0 */
+static unsigned long avs_rt5682_quirk = AVS_RT5682_MCLK_EN | AVS_RT5682_SSP_CODEC(0);
+
+static int avs_rt5682_quirk_cb(const struct dmi_system_id *id)
+{
+ avs_rt5682_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id avs_rt5682_quirk_table[] = {
+ {
+ .callback = avs_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WhiskeyLake Client"),
+ },
+ .driver_data = (void *)(AVS_RT5682_MCLK_EN |
+ AVS_RT5682_MCLK_24MHZ |
+ AVS_RT5682_SSP_CODEC(1)),
+ },
+ {
+ .callback = avs_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
+ },
+ .driver_data = (void *)(AVS_RT5682_MCLK_EN |
+ AVS_RT5682_SSP_CODEC(0)),
+ },
+ {}
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ /* HP jack connectors - unknown if we have jack detect */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* other jacks */
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_jack_pin card_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_jack_pins);
+
+ pins = devm_kmemdup_array(card->dev, card_jack_pins, num_pins,
+ sizeof(card_jack_pins[0]), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ /* Need to enable ASRC function for 24MHz mclk rate */
+ if ((avs_rt5682_quirk & AVS_RT5682_MCLK_EN) &&
+ (avs_rt5682_quirk & AVS_RT5682_MCLK_24MHZ)) {
+ rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER |
+ RT5682_AD_STEREO1_FILTER, RT5682_CLK_SEL_I2S1_ASRC);
+ }
+
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack,
+ pins, num_pins);
+ if (ret) {
+ dev_err(card->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(card->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static void avs_rt5682_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
+}
+
+static int
+avs_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ int pll_source, freq_in, freq_out;
+ int ret;
+
+ if (avs_rt5682_quirk & AVS_RT5682_MCLK_EN) {
+ pll_source = RT5682_PLL1_S_MCLK;
+ if (avs_rt5682_quirk & AVS_RT5682_MCLK_24MHZ)
+ freq_in = 24000000;
+ else
+ freq_in = 19200000;
+ } else {
+ pll_source = RT5682_PLL1_S_BCLK1;
+ freq_in = params_rate(params) * 50;
+ }
+
+ freq_out = params_rate(params) * 512;
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, pll_source, freq_in, freq_out);
+ if (ret < 0)
+ dev_err(runtime->dev, "Set PLL failed: %d\n", ret);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, freq_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(runtime->dev, "Set sysclk failed: %d\n", ret);
+
+ /* slot_width should be equal or larger than data length. */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, params_width(params));
+ if (ret < 0)
+ dev_err(runtime->dev, "Set TDM slot failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5682_ops = {
+ .hw_params = avs_rt5682_hw_params,
+};
+
+static int
+avs_rt5682_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSPN to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5682:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, AVS_RT5682_CODEC_DAI_NAME);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt5682_codec_init;
+ dl->exit = avs_rt5682_codec_exit;
+ dl->be_hw_params_fixup = avs_rt5682_be_fixup;
+ dl->ops = &avs_rt5682_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, AVS_RT5682_CODEC_DAI_NAME);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, AVS_RT5682_CODEC_DAI_NAME);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt5682_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ avs_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ dmi_check_system(avs_rt5682_quirk_table);
+ dev_dbg(dev, "avs_rt5682_quirk = %lx\n", avs_rt5682_quirk);
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt5682";
+ } else {
+ card->driver_name = "avs_rt5682";
+ card->long_name = card->name = "AVS I2S ALC5682";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5682_driver_ids[] = {
+ {
+ .name = "avs_rt5682",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids);
+
+static struct platform_driver avs_rt5682_driver = {
+ .probe = avs_rt5682_probe,
+ .driver = {
+ .name = "avs_rt5682",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5682_driver_ids,
+};
+
+module_platform_driver(avs_rt5682_driver)
+
+MODULE_DESCRIPTION("Intel rt5682 machine driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
new file mode 100644
index 000000000000..ae0e6e27a8b8
--- /dev/null
+++ b/sound/soc/intel/avs/boards/ssm4567.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.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-acpi.h>
+#include "../../../codecs/nau8825.h"
+#include "../utils.h"
+
+#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
+
+static struct snd_soc_codec_conf card_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-INT343B:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-INT343B:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Speaker"),
+ SOC_DAPM_PIN_SWITCH("Right Speaker"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Speaker", NULL),
+ SND_SOC_DAPM_SPK("Right Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route card_base_routes[] = {
+ {"Left Speaker", NULL, "Left OUT"},
+ {"Right Speaker", NULL, "Right OUT"},
+};
+
+static int avs_ssm4567_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+
+ /* Slot 1 for left */
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(runtime, 0), 0x01, 0x01, 2, 48);
+ if (ret < 0)
+ return ret;
+
+ /* Slot 2 for right */
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(runtime, 1), 0x02, 0x02, 2, 48);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+avs_ssm4567_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343B:00");
+ dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssm4567-hifi");
+ dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343B:01");
+ dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssm4567-hifi");
+ if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name ||
+ !dl->codecs[1].name || !dl->codecs[1].dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 2;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_ssm4567_codec_init;
+ dl->be_hw_params_fixup = avs_ssm4567_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->ignore_pmdown_time = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_ssm4567_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_ssm4567";
+ } else {
+ card->driver_name = "avs_ssm4567";
+ card->long_name = card->name = "AVS I2S SSM4567";
+ }
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->codec_conf = card_codec_conf;
+ card->num_configs = ARRAY_SIZE(card_codec_conf);
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_ssm4567_driver_ids[] = {
+ {
+ .name = "avs_ssm4567",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids);
+
+static struct platform_driver avs_ssm4567_driver = {
+ .probe = avs_ssm4567_probe,
+ .driver = {
+ .name = "avs_ssm4567",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_ssm4567_driver_ids,
+};
+
+module_platform_driver(avs_ssm4567_driver)
+
+MODULE_DESCRIPTION("Intel ssm4567 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c
new file mode 100644
index 000000000000..61326d7059b1
--- /dev/null
+++ b/sound/soc/intel/avs/cldma.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/pci.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include "cldma.h"
+#include "registers.h"
+
+/* Stream Registers */
+#define AZX_CL_SD_BASE 0x80
+#define AZX_SD_CTL_STRM_MASK GENMASK(23, 20)
+#define AZX_SD_CTL_STRM(s) (((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK)
+#define AZX_SD_BDLPL_BDLPLBA_MASK GENMASK(31, 7)
+#define AZX_SD_BDLPL_BDLPLBA(lb) ((lb) & AZX_SD_BDLPL_BDLPLBA_MASK)
+
+/* Software Position Based FIFO Capability Registers */
+#define AZX_CL_SPBFCS 0x20
+#define AZX_REG_CL_SPBFCTL (AZX_CL_SPBFCS + 0x4)
+#define AZX_REG_CL_SD_SPIB (AZX_CL_SPBFCS + 0x8)
+
+#define AVS_CL_OP_INTERVAL_US 3
+#define AVS_CL_OP_TIMEOUT_US 300
+#define AVS_CL_IOC_TIMEOUT_MS 300
+#define AVS_CL_STREAM_INDEX 0
+
+struct hda_cldma {
+ struct device *dev;
+ struct hdac_bus *bus;
+ void __iomem *dsp_ba;
+
+ unsigned int buffer_size;
+ unsigned int num_periods;
+ unsigned char stream_tag;
+ void __iomem *sd_addr;
+
+ struct snd_dma_buffer dmab_data;
+ struct snd_dma_buffer dmab_bdl;
+ struct delayed_work memcpy_work;
+ struct completion completion;
+
+ /* runtime */
+ void *position;
+ unsigned int remaining;
+ unsigned int sd_status;
+};
+
+static void cldma_memcpy_work(struct work_struct *work);
+
+struct hda_cldma code_loader = {
+ .stream_tag = AVS_CL_STREAM_INDEX + 1,
+ .memcpy_work = __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0),
+ .completion = COMPLETION_INITIALIZER(code_loader.completion),
+};
+
+void hda_cldma_fill(struct hda_cldma *cl)
+{
+ unsigned int size, offset;
+
+ if (cl->remaining > cl->buffer_size)
+ size = cl->buffer_size;
+ else
+ size = cl->remaining;
+
+ offset = snd_hdac_stream_readl(cl, CL_SD_SPIB);
+ if (offset + size > cl->buffer_size) {
+ unsigned int ss;
+
+ ss = cl->buffer_size - offset;
+ memcpy(cl->dmab_data.area + offset, cl->position, ss);
+ offset = 0;
+ size -= ss;
+ cl->position += ss;
+ cl->remaining -= ss;
+ }
+
+ memcpy(cl->dmab_data.area + offset, cl->position, size);
+ cl->position += size;
+ cl->remaining -= size;
+
+ snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size);
+}
+
+static void cldma_memcpy_work(struct work_struct *work)
+{
+ struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work);
+ int ret;
+
+ ret = hda_cldma_start(cl);
+ if (ret < 0) {
+ dev_err(cl->dev, "cldma set RUN failed: %d\n", ret);
+ return;
+ }
+
+ while (true) {
+ ret = wait_for_completion_timeout(&cl->completion,
+ msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS));
+ if (!ret) {
+ dev_err(cl->dev, "cldma IOC timeout\n");
+ break;
+ }
+
+ if (!(cl->sd_status & SD_INT_COMPLETE)) {
+ dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n",
+ cl->sd_status);
+ break;
+ }
+
+ if (!cl->remaining)
+ break;
+
+ reinit_completion(&cl->completion);
+ hda_cldma_fill(cl);
+ /* enable CLDMA interrupt */
+ snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
+ AVS_ADSP_ADSPIC_CLDMA);
+ }
+}
+
+void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay)
+{
+ if (!cl->remaining)
+ return;
+
+ reinit_completion(&cl->completion);
+ /* fill buffer with the first chunk before scheduling run */
+ hda_cldma_fill(cl);
+
+ schedule_delayed_work(&cl->memcpy_work, start_delay);
+}
+
+int hda_cldma_start(struct hda_cldma *cl)
+{
+ unsigned int reg;
+
+ /* enable interrupts */
+ snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
+ AVS_ADSP_ADSPIC_CLDMA);
+ snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START,
+ SD_INT_MASK | SD_CTL_DMA_START);
+
+ /* await DMA engine start */
+ return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START,
+ AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+}
+
+int hda_cldma_stop(struct hda_cldma *cl)
+{
+ unsigned int reg;
+ int ret;
+
+ /* disable interrupts */
+ snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);
+ snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);
+
+ /* await DMA engine stop */
+ ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START),
+ AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+ cancel_delayed_work_sync(&cl->memcpy_work);
+
+ return ret;
+}
+
+int hda_cldma_reset(struct hda_cldma *cl)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = hda_cldma_stop(cl);
+ if (ret < 0) {
+ dev_err(cl->dev, "cldma stop failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, SD_CTL_STREAM_RESET);
+ ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & SD_CTL_STREAM_RESET),
+ AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(cl->dev, "cldma set SRST failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, 0);
+ ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_STREAM_RESET),
+ AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size)
+{
+ /* setup runtime */
+ cl->position = data;
+ cl->remaining = size;
+}
+
+static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size)
+{
+ struct snd_dma_buffer *dmab = &cl->dmab_data;
+ __le32 *bdl = (__le32 *)cl->dmab_bdl.area;
+ int remaining = cl->buffer_size;
+ int offset = 0;
+
+ cl->num_periods = 0;
+
+ while (remaining > 0) {
+ phys_addr_t addr;
+ int chunk;
+
+ addr = snd_sgbuf_get_addr(dmab, offset);
+ bdl[0] = cpu_to_le32(lower_32_bits(addr));
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size);
+ bdl[2] = cpu_to_le32(chunk);
+
+ remaining -= chunk;
+ /* set IOC only for the last entry */
+ bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);
+
+ bdl += 4;
+ offset += chunk;
+ cl->num_periods++;
+ }
+}
+
+void hda_cldma_setup(struct hda_cldma *cl)
+{
+ dma_addr_t bdl_addr = cl->dmab_bdl.addr;
+
+ cldma_setup_bdle(cl, cl->buffer_size / 2);
+
+ snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr)));
+ snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr));
+
+ snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size);
+ snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1);
+
+ snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl));
+ /* enable spib */
+ snd_hdac_stream_writel(cl, CL_SPBFCTL, 1);
+}
+
+void hda_cldma_interrupt(struct hda_cldma *cl)
+{
+ /* disable CLDMA interrupt */
+ snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);
+
+ cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);
+ dev_dbg(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);
+
+ complete(&cl->completion);
+}
+
+int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,
+ unsigned int buffer_size)
+{
+ int ret;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl);
+ if (ret < 0) {
+ snd_dma_free_pages(&cl->dmab_data);
+ return ret;
+ }
+
+ cl->dev = bus->dev;
+ cl->bus = bus;
+ cl->dsp_ba = dsp_ba;
+ cl->buffer_size = buffer_size;
+ cl->sd_addr = dsp_ba + AZX_CL_SD_BASE;
+
+ return 0;
+}
+
+void hda_cldma_free(struct hda_cldma *cl)
+{
+ snd_dma_free_pages(&cl->dmab_data);
+ snd_dma_free_pages(&cl->dmab_bdl);
+}
diff --git a/sound/soc/intel/avs/cldma.h b/sound/soc/intel/avs/cldma.h
new file mode 100644
index 000000000000..7f9b2b1c566e
--- /dev/null
+++ b/sound/soc/intel/avs/cldma.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_CLDMA_H
+#define __SOUND_SOC_INTEL_AVS_CLDMA_H
+
+#include <linux/sizes.h>
+
+#define AVS_CL_DEFAULT_BUFFER_SIZE SZ_128K
+
+struct hda_cldma;
+extern struct hda_cldma code_loader;
+
+void hda_cldma_fill(struct hda_cldma *cl);
+void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay);
+
+int hda_cldma_start(struct hda_cldma *cl);
+int hda_cldma_stop(struct hda_cldma *cl);
+int hda_cldma_reset(struct hda_cldma *cl);
+
+void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size);
+void hda_cldma_setup(struct hda_cldma *cl);
+void hda_cldma_interrupt(struct hda_cldma *cl);
+int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,
+ unsigned int buffer_size);
+void hda_cldma_free(struct hda_cldma *cl);
+
+#endif
diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c
new file mode 100644
index 000000000000..5b5359e9128b
--- /dev/null
+++ b/sound/soc/intel/avs/cnl.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+#include "registers.h"
+
+static void avs_cnl_ipc_interrupt(struct avs_dev *adev)
+{
+ const struct avs_spec *spec = adev->spec;
+ u32 hipc_ack, hipc_rsp;
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0);
+
+ hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset);
+ hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+
+ /* DSP acked host's request. */
+ if (hipc_ack & spec->hipc->ack_done_mask) {
+ complete(&adev->ipc->done_completion);
+
+ /* Tell DSP it has our attention. */
+ snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask,
+ spec->hipc->ack_done_mask);
+ }
+
+ /* DSP sent new response to process. */
+ if (hipc_rsp & spec->hipc->rsp_busy_mask) {
+ union avs_reply_msg msg;
+ u32 hipctda;
+
+ msg.primary = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR);
+ msg.ext.val = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD);
+
+ avs_dsp_process_response(adev, msg.val);
+
+ /* Tell DSP we accepted its message. */
+ snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDR,
+ CNL_ADSP_HIPCTDR_BUSY, CNL_ADSP_HIPCTDR_BUSY);
+ /* Ack this response. */
+ snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDA,
+ CNL_ADSP_HIPCTDA_DONE, CNL_ADSP_HIPCTDA_DONE);
+ /* HW might have been clock gated, give some time for change to propagate. */
+ snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda,
+ !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000);
+ }
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
+}
+
+irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev)
+{
+ u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (adspis == UINT_MAX)
+ return ret;
+
+ if (adspis & AVS_ADSP_ADSPIS_IPC) {
+ avs_cnl_ipc_interrupt(adev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+const struct avs_dsp_ops avs_cnl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .dsp_interrupt = avs_cnl_dsp_interrupt,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .log_buffer_offset = avs_skl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_apl_d0ix_toggle,
+ .set_d0ix = avs_apl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(apl)
+};
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
new file mode 100644
index 000000000000..a8f05de338e0
--- /dev/null
+++ b/sound/soc/intel/avs/control.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+// Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/cleanup.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "control.h"
+#include "messages.h"
+#include "path.h"
+
+static struct avs_dev *avs_get_kcontrol_adev(struct snd_kcontrol *kcontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct device *dev = snd_soc_dapm_to_dev(dapm);
+
+ return to_avs_dev(dev);
+}
+
+static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 id)
+{
+ struct avs_path *path;
+ struct avs_path_pipeline *ppl;
+ struct avs_path_module *mod;
+
+ spin_lock(&adev->path_list_lock);
+ list_for_each_entry(path, &adev->path_list, node) {
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ list_for_each_entry(mod, &ppl->mod_list, node) {
+ guid_t *type = &mod->template->cfg_ext->type;
+
+ if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) ||
+ guid_equal(type, &AVS_GAIN_MOD_UUID)) &&
+ mod->template->ctl_id == id) {
+ spin_unlock(&adev->path_list_lock);
+ return mod;
+ }
+ }
+ }
+ }
+ spin_unlock(&adev->path_list_lock);
+
+ return NULL;
+}
+
+int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
+ struct avs_control_data *ctl_data = mc->dobj.private;
+ struct avs_path_module *active_module;
+ struct avs_volume_cfg *dspvols;
+ struct avs_dev *adev;
+ size_t num_dspvols;
+ int ret, i;
+
+ adev = avs_get_kcontrol_adev(kctl);
+
+ /* Prevent access to modules while path is being constructed. */
+ guard(mutex)(&adev->path_mutex);
+
+ active_module = avs_get_volume_module(adev, ctl_data->id);
+ if (active_module) {
+ ret = avs_ipc_peakvol_get_volume(adev, active_module->module_id,
+ active_module->instance_id, &dspvols,
+ &num_dspvols);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ /* Do not copy more than the control can store. */
+ num_dspvols = min_t(u32, num_dspvols, SND_SOC_TPLG_MAX_CHAN);
+ for (i = 0; i < num_dspvols; i++)
+ ctl_data->values[i] = dspvols[i].target_volume;
+ kfree(dspvols);
+ }
+
+ memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values));
+ return 0;
+}
+
+int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
+{
+ struct avs_path_module *active_module;
+ struct avs_control_data *ctl_data;
+ struct soc_mixer_control *mc;
+ struct avs_dev *adev;
+ long *input;
+ int ret, i;
+
+ mc = (struct soc_mixer_control *)kctl->private_value;
+ ctl_data = mc->dobj.private;
+ adev = avs_get_kcontrol_adev(kctl);
+ input = uctl->value.integer.value;
+ i = 0;
+
+ /* mc->num_channels can be 0. */
+ do {
+ if (input[i] < mc->min || input[i] > mc->max)
+ return -EINVAL;
+ } while (++i < mc->num_channels);
+
+ if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values)))
+ return 0;
+
+ /* Prevent access to modules while path is being constructed. */
+ guard(mutex)(&adev->path_mutex);
+
+ active_module = avs_get_volume_module(adev, ctl_data->id);
+ if (active_module) {
+ ret = avs_peakvol_set_volume(adev, active_module, mc, input);
+ if (ret)
+ return ret;
+ }
+
+ memcpy(ctl_data->values, input, sizeof(ctl_data->values));
+ return 1;
+}
+
+int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = max_t(u32, 1, mc->num_channels);
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mc->max;
+ return 0;
+}
+
+int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
+ struct avs_control_data *ctl_data = mc->dobj.private;
+ struct avs_path_module *active_module;
+ struct avs_mute_cfg *dspmutes;
+ struct avs_dev *adev;
+ size_t num_dspmutes;
+ int ret, i;
+
+ adev = avs_get_kcontrol_adev(kctl);
+
+ /* Prevent access to modules while path is being constructed. */
+ guard(mutex)(&adev->path_mutex);
+
+ active_module = avs_get_volume_module(adev, ctl_data->id);
+ if (active_module) {
+ ret = avs_ipc_peakvol_get_mute(adev, active_module->module_id,
+ active_module->instance_id, &dspmutes,
+ &num_dspmutes);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ /* Do not copy more than the control can store. */
+ num_dspmutes = min_t(u32, num_dspmutes, SND_SOC_TPLG_MAX_CHAN);
+ for (i = 0; i < num_dspmutes; i++)
+ ctl_data->values[i] = !dspmutes[i].mute;
+ kfree(dspmutes);
+ }
+
+ memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values));
+ return 0;
+}
+
+int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
+{
+ struct avs_path_module *active_module;
+ struct avs_control_data *ctl_data;
+ struct soc_mixer_control *mc;
+ struct avs_dev *adev;
+ long *input;
+ int ret, i;
+
+ mc = (struct soc_mixer_control *)kctl->private_value;
+ ctl_data = mc->dobj.private;
+ adev = avs_get_kcontrol_adev(kctl);
+ input = uctl->value.integer.value;
+ i = 0;
+
+ /* mc->num_channels can be 0. */
+ do {
+ if (input[i] < mc->min || input[i] > mc->max)
+ return -EINVAL;
+ } while (++i < mc->num_channels);
+
+ if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values)))
+ return 0;
+
+ /* Prevent access to modules while path is being constructed. */
+ guard(mutex)(&adev->path_mutex);
+
+ active_module = avs_get_volume_module(adev, ctl_data->id);
+ if (active_module) {
+ ret = avs_peakvol_set_mute(adev, active_module, mc, input);
+ if (ret)
+ return ret;
+ }
+
+ memcpy(ctl_data->values, input, sizeof(ctl_data->values));
+ return 1;
+}
+
+int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = max_t(u32, 1, mc->num_channels);
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mc->max;
+ return 0;
+}
diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h
new file mode 100644
index 000000000000..08b2919e4629
--- /dev/null
+++ b/sound/soc/intel/avs/control.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ * Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_CTRL_H
+#define __SOUND_SOC_INTEL_AVS_CTRL_H
+
+#include <sound/control.h>
+#include <uapi/sound/asoc.h>
+
+struct avs_control_data {
+ u32 id;
+ long values[SND_SOC_TPLG_MAX_CHAN];
+};
+
+int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo);
+int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo);
+
+#endif
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
new file mode 100644
index 000000000000..6e0e65584c7f
--- /dev/null
+++ b/sound/soc/intel/avs/core.c
@@ -0,0 +1,959 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+// Special thanks to:
+// Krzysztof Hejmowski <krzysztof.hejmowski@intel.com>
+// Michal Sienkiewicz <michal.sienkiewicz@intel.com>
+// Filip Proborszcz
+//
+// for sharing Intel AudioDSP expertise and helping shape the very
+// foundation of this driver
+//
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <acpi/nhlt.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/intel-dsp-config.h>
+#include "../../codecs/hda.h"
+#include "avs.h"
+#include "cldma.h"
+#include "debug.h"
+#include "messages.h"
+#include "pcm.h"
+
+static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK;
+module_param(pgctl_mask, uint, 0444);
+MODULE_PARM_DESC(pgctl_mask, "PCI PGCTL policy override");
+
+static u32 cgctl_mask = AZX_CGCTL_MISCBDCGE_MASK;
+module_param(cgctl_mask, uint, 0444);
+MODULE_PARM_DESC(cgctl_mask, "PCI CGCTL policy override");
+
+static void
+avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
+{
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ u32 data;
+
+ pci_read_config_dword(pci, reg, &data);
+ data &= ~mask;
+ data |= (value & mask);
+ pci_write_config_dword(pci, reg, data);
+}
+
+void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable)
+{
+ u32 value = enable ? 0 : pgctl_mask;
+
+ if (!avs_platattr_test(adev, ACE))
+ avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value);
+}
+
+static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable)
+{
+ struct avs_dev *adev = hdac_to_avs(bus);
+ u32 value = enable ? cgctl_mask : 0;
+
+ if (!avs_platattr_test(adev, ACE))
+ avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value);
+}
+
+void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
+{
+ avs_hdac_clock_gating_enable(&adev->base.core, enable);
+}
+
+void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
+{
+ if (avs_platattr_test(adev, ACE))
+ return;
+ if (enable) {
+ if (atomic_inc_and_test(&adev->l1sen_counter))
+ snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN,
+ AZX_VS_EM2_L1SEN);
+ } else {
+ if (atomic_dec_return(&adev->l1sen_counter) == -1)
+ snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, 0);
+ }
+}
+
+static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
+{
+ unsigned int cp_streams, pb_streams;
+ unsigned int gcap;
+
+ gcap = snd_hdac_chip_readw(bus, GCAP);
+ cp_streams = (gcap >> 8) & 0x0F;
+ pb_streams = (gcap >> 12) & 0x0F;
+ bus->num_streams = cp_streams + pb_streams;
+
+ snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
+ snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
+
+ return snd_hdac_bus_alloc_stream_pages(bus);
+}
+
+static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+ struct avs_dev *adev = hdac_to_avs(bus);
+ struct hdac_ext_link *hlink;
+ bool ret;
+
+ avs_hdac_clock_gating_enable(bus, false);
+ ret = snd_hdac_bus_init_chip(bus, full_reset);
+
+ /* Reset stream-to-link mapping */
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
+
+ avs_hdac_clock_gating_enable(bus, true);
+
+ /* Set DUM bit to address incorrect position reporting for capture
+ * streams. In order to do so, CTRL needs to be out of reset state
+ */
+ if (!avs_platattr_test(adev, ACE))
+ snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM);
+
+ return ret;
+}
+
+static int probe_codec(struct hdac_bus *bus, int addr)
+{
+ struct hda_codec *codec;
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res = -1;
+ int ret;
+
+ mutex_lock(&bus->cmd_mutex);
+ snd_hdac_bus_send_cmd(bus, cmd);
+ snd_hdac_bus_get_response(bus, addr, &res);
+ mutex_unlock(&bus->cmd_mutex);
+ if (res == -1)
+ return -EIO;
+
+ dev_dbg(bus->dev, "codec #%d probed OK: 0x%x\n", addr, res);
+
+ codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "hdaudioB%dD%d", bus->idx, addr);
+ if (IS_ERR(codec)) {
+ dev_err(bus->dev, "init codec failed: %ld\n", PTR_ERR(codec));
+ return PTR_ERR(codec);
+ }
+ /*
+ * Allow avs_core suspend by forcing suspended state on all
+ * of its codec child devices. Component interested in
+ * dealing with hda codecs directly takes pm responsibilities
+ */
+ pm_runtime_set_suspended(hda_codec_dev(codec));
+
+ /* configure effectively creates new ASoC component */
+ ret = snd_hda_codec_configure(codec);
+ if (ret < 0) {
+ dev_warn(bus->dev, "failed to config codec #%d: %d\n", addr, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus)
+{
+ int ret, c;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < HDA_MAX_CODECS; c++) {
+ if (!(bus->codec_mask & BIT(c)))
+ continue;
+
+ ret = probe_codec(bus, c);
+ /* Ignore codecs with no supporting driver. */
+ if (!ret || ret == -ENODEV)
+ continue;
+
+ /*
+ * Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(bus->dev, "Codec #%d probe error; disabling it...\n", c);
+ bus->codec_mask &= ~BIT(c);
+ /*
+ * More badly, accessing to a non-existing
+ * codec often screws up the controller bus,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller bus to get
+ * back to the sanity state.
+ */
+ snd_hdac_bus_stop_chip(bus);
+ avs_hdac_bus_init_chip(bus, true);
+ }
+}
+
+static void avs_hda_probe_work(struct work_struct *work)
+{
+ struct avs_dev *adev = container_of(work, struct avs_dev, probe_work);
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ pm_runtime_set_active(bus->dev); /* clear runtime_error flag */
+
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+ avs_hdac_bus_init_chip(bus, true);
+ avs_hdac_bus_probe_codecs(bus);
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ /* with all codecs probed, links can be powered down */
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+
+ snd_hdac_ext_bus_ppcap_enable(bus, true);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+ avs_debugfs_init(adev);
+
+ ret = avs_dsp_first_boot_firmware(adev);
+ if (ret < 0)
+ return;
+
+ acpi_nhlt_get_gbl_table();
+
+ avs_register_all_boards(adev);
+
+ /* configure PM */
+ pm_runtime_set_autosuspend_delay(bus->dev, 2000);
+ pm_runtime_use_autosuspend(bus->dev);
+ pm_runtime_put_autosuspend(bus->dev);
+ pm_runtime_allow(bus->dev);
+}
+
+static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size)
+{
+ u64 prev_pos, pos, num_bytes;
+
+ div64_u64_rem(stream->curr_pos, buffer_size, &prev_pos);
+ pos = snd_hdac_stream_get_pos_posbuf(stream);
+
+ if (pos < prev_pos)
+ num_bytes = (buffer_size - prev_pos) + pos;
+ else
+ num_bytes = pos - prev_pos;
+
+ stream->curr_pos += num_bytes;
+}
+
+/* called from IRQ */
+static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream)
+{
+ if (stream->substream) {
+ avs_period_elapsed(stream->substream);
+ } else if (stream->cstream) {
+ u64 buffer_size = stream->cstream->runtime->buffer_size;
+
+ hdac_stream_update_pos(stream, buffer_size);
+ snd_compr_fragment_elapsed(stream->cstream);
+ }
+}
+
+static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus)
+{
+ irqreturn_t ret = IRQ_NONE;
+ u32 status;
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream))
+ ret = IRQ_HANDLED;
+
+ spin_lock_irq(&bus->reg_lock);
+ /* Clear RIRB interrupt. */
+ status = snd_hdac_chip_readb(bus, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ if (status & RIRB_INT_RESPONSE)
+ snd_hdac_bus_update_rirb(bus);
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock_irq(&bus->reg_lock);
+ return ret;
+}
+
+static irqreturn_t avs_hda_irq_handler(int irq, void *dev_id)
+{
+ struct hdac_bus *bus = dev_id;
+ u32 intsts;
+
+ intsts = snd_hdac_chip_readl(bus, INTSTS);
+ if (intsts == UINT_MAX || !(intsts & AZX_INT_GLOBAL_EN))
+ return IRQ_NONE;
+
+ /* Mask GIE, unmasked in irq_thread(). */
+ snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, 0);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t avs_hda_irq_thread(int irq, void *dev_id)
+{
+ struct hdac_bus *bus = dev_id;
+ u32 status;
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ if (status & ~AZX_INT_GLOBAL_EN)
+ avs_hda_interrupt(bus);
+
+ /* Unmask GIE, masked in irq_handler(). */
+ snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
+{
+ struct avs_dev *adev = dev_id;
+
+ return avs_hda_irq_handler(irq, &adev->base.core);
+}
+
+static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id)
+{
+ struct avs_dev *adev = dev_id;
+ struct hdac_bus *bus = &adev->base.core;
+ u32 status;
+
+ status = readl(bus->ppcap + AZX_REG_PP_PPSTS);
+ if (status & AZX_PPCTL_PIE)
+ avs_dsp_op(adev, dsp_interrupt);
+
+ /* Unmask GIE, masked in irq_handler(). */
+ snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN);
+
+ return IRQ_HANDLED;
+}
+
+static int avs_hdac_acquire_irq(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ int ret;
+
+ /* request one and check that we only got one interrupt */
+ ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_INTX);
+ if (ret != 1) {
+ dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret);
+ return ret;
+ }
+
+ ret = pci_request_irq(pci, 0, avs_hda_irq_handler, avs_hda_irq_thread, bus,
+ KBUILD_MODNAME);
+ if (ret < 0) {
+ dev_err(adev->dev, "Failed to request stream IRQ handler: %d\n", ret);
+ goto free_vector;
+ }
+
+ ret = pci_request_irq(pci, 0, avs_dsp_irq_handler, avs_dsp_irq_thread, adev,
+ KBUILD_MODNAME);
+ if (ret < 0) {
+ dev_err(adev->dev, "Failed to request IPC IRQ handler: %d\n", ret);
+ goto free_stream_irq;
+ }
+
+ return 0;
+
+free_stream_irq:
+ pci_free_irq(pci, 0, bus);
+free_vector:
+ pci_free_irq_vectors(pci);
+ return ret;
+}
+
+static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct hda_bus *bus = &adev->base;
+ struct avs_ipc *ipc;
+ struct device *dev = &pci->dev;
+ int ret;
+
+ ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, &soc_hda_ext_bus_ops);
+ if (ret < 0)
+ return ret;
+
+ bus->core.use_posbuf = 1;
+ bus->core.bdl_pos_adj = 0;
+ bus->core.sync_write = 1;
+ bus->pci = pci;
+ bus->mixer_assigned = -1;
+ mutex_init(&bus->prepare_mutex);
+
+ ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
+ if (!ipc)
+ return -ENOMEM;
+ ret = avs_ipc_init(ipc, dev);
+ if (ret < 0)
+ return ret;
+
+ adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!adev->modcfg_buf)
+ return -ENOMEM;
+
+ adev->dev = dev;
+ adev->spec = (const struct avs_spec *)id->driver_data;
+ adev->ipc = ipc;
+ adev->hw_cfg.dsp_cores = hweight_long(AVS_MAIN_CORE_MASK);
+ INIT_WORK(&adev->probe_work, avs_hda_probe_work);
+ INIT_LIST_HEAD(&adev->comp_list);
+ INIT_LIST_HEAD(&adev->path_list);
+ INIT_LIST_HEAD(&adev->fw_list);
+ init_completion(&adev->fw_ready);
+ spin_lock_init(&adev->path_list_lock);
+ mutex_init(&adev->modres_mutex);
+ mutex_init(&adev->comp_list_mutex);
+ mutex_init(&adev->path_mutex);
+
+ return 0;
+}
+
+static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct hdac_bus *bus;
+ struct avs_dev *adev;
+ struct device *dev = &pci->dev;
+ int ret;
+
+ ret = snd_intel_dsp_driver_probe(pci);
+ switch (ret) {
+ case SND_INTEL_DSP_DRIVER_ANY:
+ case SND_INTEL_DSP_DRIVER_SST:
+ case SND_INTEL_DSP_DRIVER_AVS:
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ret = pcim_enable_device(pci);
+ if (ret < 0)
+ return ret;
+
+ adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+ bus = &adev->base.core;
+
+ ret = avs_bus_init(adev, pci, id);
+ if (ret < 0) {
+ dev_err(dev, "failed to init avs bus: %d\n", ret);
+ return ret;
+ }
+
+ ret = pcim_request_all_regions(pci, "AVS HDAudio");
+ if (ret < 0)
+ return ret;
+
+ bus->addr = pci_resource_start(pci, 0);
+ bus->remap_addr = pci_ioremap_bar(pci, 0);
+ if (!bus->remap_addr) {
+ dev_err(bus->dev, "ioremap error\n");
+ return -ENXIO;
+ }
+
+ adev->dsp_ba = pci_ioremap_bar(pci, 4);
+ if (!adev->dsp_ba) {
+ dev_err(bus->dev, "ioremap error\n");
+ ret = -ENXIO;
+ goto err_remap_bar4;
+ }
+
+ snd_hdac_bus_parse_capabilities(bus);
+ if (bus->mlcap)
+ snd_hdac_ext_bus_get_ml_capabilities(bus);
+
+ if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ dma_set_max_seg_size(dev, UINT_MAX);
+
+ ret = avs_hdac_bus_init_streams(bus);
+ if (ret < 0) {
+ dev_err(dev, "failed to init streams: %d\n", ret);
+ goto err_init_streams;
+ }
+
+ ret = avs_hdac_acquire_irq(adev);
+ if (ret < 0) {
+ dev_err(bus->dev, "failed to acquire irq: %d\n", ret);
+ goto err_acquire_irq;
+ }
+
+ pci_set_master(pci);
+ pci_set_drvdata(pci, bus);
+ device_disable_async_suspend(dev);
+
+ ret = snd_hdac_i915_init(bus);
+ if (ret == -EPROBE_DEFER)
+ goto err_i915_init;
+ else if (ret < 0)
+ dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret);
+
+ schedule_work(&adev->probe_work);
+
+ return 0;
+
+err_i915_init:
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
+ pci_clear_master(pci);
+ pci_set_drvdata(pci, NULL);
+err_acquire_irq:
+ snd_hdac_bus_free_stream_pages(bus);
+ snd_hdac_ext_stream_free_all(bus);
+err_init_streams:
+ iounmap(adev->dsp_ba);
+err_remap_bar4:
+ iounmap(bus->remap_addr);
+ return ret;
+}
+
+static void avs_pci_shutdown(struct pci_dev *pci)
+{
+ struct hdac_bus *bus = pci_get_drvdata(pci);
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ cancel_work_sync(&adev->probe_work);
+ avs_ipc_block(adev->ipc);
+
+ snd_hdac_stop_streams(bus);
+ avs_dsp_op(adev, int_control, false);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+ snd_hdac_ext_bus_link_power_down_all(bus);
+
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
+}
+
+static void avs_pci_remove(struct pci_dev *pci)
+{
+ struct hdac_device *hdev, *save;
+ struct hdac_bus *bus = pci_get_drvdata(pci);
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ cancel_work_sync(&adev->probe_work);
+ avs_ipc_block(adev->ipc);
+
+ avs_unregister_all_boards(adev);
+
+ acpi_nhlt_put_gbl_table();
+ avs_debugfs_exit(adev);
+
+ if (avs_platattr_test(adev, CLDMA))
+ hda_cldma_free(&code_loader);
+
+ snd_hdac_stop_streams_and_chip(bus);
+ avs_dsp_op(adev, int_control, false);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+ /* it is safe to remove all codecs from the system now */
+ list_for_each_entry_safe(hdev, save, &bus->codec_list, list)
+ snd_hda_codec_unregister(hdac_to_hda_codec(hdev));
+
+ snd_hdac_bus_free_stream_pages(bus);
+ snd_hdac_ext_stream_free_all(bus);
+ /* reverse ml_capabilities */
+ snd_hdac_ext_link_free_all(bus);
+ snd_hdac_ext_bus_exit(bus);
+
+ avs_dsp_core_disable(adev, GENMASK(adev->hw_cfg.dsp_cores - 1, 0));
+ snd_hdac_ext_bus_ppcap_enable(bus, false);
+
+ /* snd_hdac_stop_streams_and_chip does that already? */
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+ if (bus->audio_component)
+ snd_hdac_i915_exit(bus);
+
+ avs_module_info_free(adev);
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
+ iounmap(bus->remap_addr);
+ iounmap(adev->dsp_ba);
+
+ /* Firmware is not needed anymore */
+ avs_release_firmwares(adev);
+
+ /* pm_runtime_forbid() can rpm_resume() which we do not want */
+ pm_runtime_disable(&pci->dev);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_enable(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+}
+
+static int avs_suspend_standby(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct pci_dev *pci = adev->base.pci;
+
+ if (bus->cmd_dma_state)
+ snd_hdac_bus_stop_cmd_io(bus);
+
+ snd_hdac_ext_bus_link_power_down_all(bus);
+
+ enable_irq_wake(pci->irq);
+ pci_save_state(pci);
+
+ return 0;
+}
+
+static int avs_suspend_common(struct avs_dev *adev, bool low_power)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ int ret;
+
+ flush_work(&adev->probe_work);
+ if (low_power && adev->num_lp_paths)
+ return avs_suspend_standby(adev);
+
+ snd_hdac_ext_bus_link_power_down_all(bus);
+
+ ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false);
+ /*
+ * pm_runtime is blocked on DSP failure but system-wide suspend is not.
+ * Do not block entire system from suspending if that's the case.
+ */
+ if (ret && ret != -EPERM) {
+ dev_err(adev->dev, "set dx failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+
+ avs_ipc_block(adev->ipc);
+ avs_dsp_op(adev, int_control, false);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+ ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ if (ret < 0) {
+ dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret);
+ return ret;
+ }
+
+ snd_hdac_ext_bus_ppcap_enable(bus, false);
+ /* disable LP SRAM retention */
+ avs_hda_power_gating_enable(adev, false);
+ snd_hdac_bus_stop_chip(bus);
+ /* disable CG when putting controller to reset */
+ avs_hdac_clock_gating_enable(bus, false);
+ snd_hdac_bus_enter_link_reset(bus);
+ avs_hdac_clock_gating_enable(bus, true);
+
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ return 0;
+}
+
+static int avs_resume_standby(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct pci_dev *pci = adev->base.pci;
+
+ pci_restore_state(pci);
+ disable_irq_wake(pci->irq);
+
+ snd_hdac_ext_bus_link_power_up_all(bus);
+
+ if (bus->cmd_dma_state)
+ snd_hdac_bus_init_cmd_io(bus);
+
+ return 0;
+}
+
+static int avs_resume_common(struct avs_dev *adev, bool low_power, bool purge)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ int ret;
+
+ if (low_power && adev->num_lp_paths)
+ return avs_resume_standby(adev);
+
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+ avs_hdac_bus_init_chip(bus, true);
+
+ snd_hdac_ext_bus_ppcap_enable(bus, true);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+ ret = avs_dsp_boot_firmware(adev, purge);
+ if (ret < 0) {
+ dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_suspend(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev), true);
+}
+
+static int avs_resume(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), true, true);
+}
+
+static int avs_runtime_suspend(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev), true);
+}
+
+static int avs_runtime_resume(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), true, false);
+}
+
+static int avs_freeze(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev), false);
+}
+static int avs_thaw(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), false, true);
+}
+
+static int avs_poweroff(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev), false);
+}
+
+static int avs_restore(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), false, true);
+}
+
+static const struct dev_pm_ops avs_dev_pm = {
+ .suspend = avs_suspend,
+ .resume = avs_resume,
+ .freeze = avs_freeze,
+ .thaw = avs_thaw,
+ .poweroff = avs_poweroff,
+ .restore = avs_restore,
+ RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
+};
+
+static const struct avs_sram_spec skl_sram_spec = {
+ .base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+ .window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
+};
+
+static const struct avs_sram_spec apl_sram_spec = {
+ .base_offset = APL_ADSP_SRAM_BASE_OFFSET,
+ .window_size = APL_ADSP_SRAM_WINDOW_SIZE,
+};
+
+static const struct avs_sram_spec mtl_sram_spec = {
+ .base_offset = MTL_ADSP_SRAM_BASE_OFFSET,
+ .window_size = MTL_ADSP_SRAM_WINDOW_SIZE,
+};
+
+static const struct avs_hipc_spec skl_hipc_spec = {
+ .req_offset = SKL_ADSP_REG_HIPCI,
+ .req_ext_offset = SKL_ADSP_REG_HIPCIE,
+ .req_busy_mask = SKL_ADSP_HIPCI_BUSY,
+ .ack_offset = SKL_ADSP_REG_HIPCIE,
+ .ack_done_mask = SKL_ADSP_HIPCIE_DONE,
+ .rsp_offset = SKL_ADSP_REG_HIPCT,
+ .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY,
+ .ctl_offset = SKL_ADSP_REG_HIPCCTL,
+ .sts_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_hipc_spec apl_hipc_spec = {
+ .req_offset = SKL_ADSP_REG_HIPCI,
+ .req_ext_offset = SKL_ADSP_REG_HIPCIE,
+ .req_busy_mask = SKL_ADSP_HIPCI_BUSY,
+ .ack_offset = SKL_ADSP_REG_HIPCIE,
+ .ack_done_mask = SKL_ADSP_HIPCIE_DONE,
+ .rsp_offset = SKL_ADSP_REG_HIPCT,
+ .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY,
+ .ctl_offset = SKL_ADSP_REG_HIPCCTL,
+ .sts_offset = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_hipc_spec cnl_hipc_spec = {
+ .req_offset = CNL_ADSP_REG_HIPCIDR,
+ .req_ext_offset = CNL_ADSP_REG_HIPCIDD,
+ .req_busy_mask = CNL_ADSP_HIPCIDR_BUSY,
+ .ack_offset = CNL_ADSP_REG_HIPCIDA,
+ .ack_done_mask = CNL_ADSP_HIPCIDA_DONE,
+ .rsp_offset = CNL_ADSP_REG_HIPCTDR,
+ .rsp_busy_mask = CNL_ADSP_HIPCTDR_BUSY,
+ .ctl_offset = CNL_ADSP_REG_HIPCCTL,
+ .sts_offset = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_hipc_spec lnl_hipc_spec = {
+ .req_offset = MTL_REG_HfIPCxIDR,
+ .req_ext_offset = MTL_REG_HfIPCxIDD,
+ .req_busy_mask = MTL_HfIPCxIDR_BUSY,
+ .ack_offset = MTL_REG_HfIPCxIDA,
+ .ack_done_mask = MTL_HfIPCxIDA_DONE,
+ .rsp_offset = MTL_REG_HfIPCxTDR,
+ .rsp_busy_mask = MTL_HfIPCxTDR_BUSY,
+ .ctl_offset = MTL_REG_HfIPCxCTL,
+ .sts_offset = LNL_REG_HfDFR(0),
+};
+
+static const struct avs_spec skl_desc = {
+ .name = "skl",
+ .min_fw_version = { 9, 21, 0, 4732 },
+ .dsp_ops = &avs_skl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_CLDMA,
+ .sram = &skl_sram_spec,
+ .hipc = &skl_hipc_spec,
+};
+
+static const struct avs_spec apl_desc = {
+ .name = "apl",
+ .min_fw_version = { 9, 22, 1, 4323 },
+ .dsp_ops = &avs_apl_dsp_ops,
+ .core_init_mask = 3,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &apl_hipc_spec,
+};
+
+static const struct avs_spec cnl_desc = {
+ .name = "cnl",
+ .min_fw_version = { 10, 23, 0, 5314 },
+ .dsp_ops = &avs_cnl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &cnl_hipc_spec,
+};
+
+static const struct avs_spec icl_desc = {
+ .name = "icl",
+ .min_fw_version = { 10, 23, 0, 5040 },
+ .dsp_ops = &avs_icl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &cnl_hipc_spec,
+};
+
+static const struct avs_spec jsl_desc = {
+ .name = "jsl",
+ .min_fw_version = { 10, 26, 0, 5872 },
+ .dsp_ops = &avs_icl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram = &apl_sram_spec,
+ .hipc = &cnl_hipc_spec,
+};
+
+#define AVS_TGL_BASED_SPEC(sname, min) \
+static const struct avs_spec sname##_desc = { \
+ .name = #sname, \
+ .min_fw_version = { 10, min, 0, 5646 }, \
+ .dsp_ops = &avs_tgl_dsp_ops, \
+ .core_init_mask = 1, \
+ .attributes = AVS_PLATATTR_IMR, \
+ .sram = &apl_sram_spec, \
+ .hipc = &cnl_hipc_spec, \
+}
+
+AVS_TGL_BASED_SPEC(lkf, 28);
+AVS_TGL_BASED_SPEC(tgl, 29);
+AVS_TGL_BASED_SPEC(ehl, 30);
+AVS_TGL_BASED_SPEC(adl, 35);
+AVS_TGL_BASED_SPEC(adl_n, 35);
+
+static const struct avs_spec fcl_desc = {
+ .name = "fcl",
+ .min_fw_version = { 0 },
+ .dsp_ops = &avs_ptl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR | AVS_PLATATTR_ACE | AVS_PLATATTR_ALTHDA,
+ .sram = &mtl_sram_spec,
+ .hipc = &lnl_hipc_spec,
+};
+
+static const struct pci_device_id avs_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, &cnl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_LKF, &lkf_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_R, &tgl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, &ehl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, &ehl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_FCL, &fcl_desc) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, avs_ids);
+
+static struct pci_driver avs_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = avs_ids,
+ .probe = avs_pci_probe,
+ .remove = avs_pci_remove,
+ .shutdown = avs_pci_shutdown,
+ .dev_groups = avs_attr_groups,
+ .driver = {
+ .pm = pm_ptr(&avs_dev_pm),
+ },
+};
+module_pci_driver(avs_pci_driver);
+
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>");
+MODULE_DESCRIPTION("Intel cAVS sound driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("intel/avs/skl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/apl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/cnl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/icl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/jsl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/lkf/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/tgl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/ehl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/adl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/adl_n/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/fcl/dsp_basefw.bin");
diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h
new file mode 100644
index 000000000000..94fe8729a5c1
--- /dev/null
+++ b/sound/soc/intel/avs/debug.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2024-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H
+#define __SOUND_SOC_INTEL_AVS_DEBUG_H
+
+#include "messages.h"
+#include "registers.h"
+
+struct avs_dev;
+
+#define avs_log_buffer_size(adev) \
+ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({ \
+ s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
+ (__offset < 0) ? NULL : \
+ (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
+})
+
+static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&adev->trace_lock, flags);
+ ret = avs_dsp_op(adev, log_buffer_status, msg);
+ spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+ return ret;
+}
+
+struct avs_apl_log_buffer_layout {
+ u32 read_ptr;
+ u32 write_ptr;
+ u8 buffer[];
+} __packed;
+static_assert(sizeof(struct avs_apl_log_buffer_layout) == 8);
+
+#define avs_apl_log_payload_size(adev) \
+ (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout))
+
+#define avs_apl_log_payload_addr(addr) \
+ (addr + sizeof(struct avs_apl_log_buffer_layout))
+
+#ifdef CONFIG_DEBUG_FS
+int avs_register_probe_component(struct avs_dev *adev, const char *name);
+
+#define AVS_SET_ENABLE_LOGS_OP(name) \
+ .enable_logs = avs_##name##_enable_logs
+
+bool avs_logging_fw(struct avs_dev *adev);
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+
+void avs_debugfs_init(struct avs_dev *adev);
+void avs_debugfs_exit(struct avs_dev *adev);
+
+#else
+static inline int avs_register_probe_component(struct avs_dev *adev, const char *name)
+{
+ return -EOPNOTSUPP;
+}
+
+#define AVS_SET_ENABLE_LOGS_OP(name)
+
+static inline bool avs_logging_fw(struct avs_dev *adev)
+{
+ return false;
+}
+
+static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+}
+
+static inline void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src,
+ unsigned int len)
+{
+}
+
+static inline void avs_debugfs_init(struct avs_dev *adev) { }
+static inline void avs_debugfs_exit(struct avs_dev *adev) { }
+#endif
+
+#endif
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
new file mode 100644
index 000000000000..701c247227bf
--- /dev/null
+++ b/sound/soc/intel/avs/debugfs.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/kfifo.h>
+#include <linux/wait.h>
+#include <linux/sched/signal.h>
+#include <linux/string_helpers.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+
+static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
+{
+ struct __kfifo *__fifo = &fifo->kfifo;
+ unsigned int l, off;
+
+ len = min(len, kfifo_avail(fifo));
+ off = __fifo->in & __fifo->mask;
+ l = min(len, kfifo_size(fifo) - off);
+
+ memcpy_fromio(__fifo->data + off, src, l);
+ memcpy_fromio(__fifo->data, src + l, len - l);
+ /* Make sure data copied from SRAM is visible to all CPUs. */
+ smp_mb();
+ __fifo->in += len;
+
+ return len;
+}
+
+bool avs_logging_fw(struct avs_dev *adev)
+{
+ return kfifo_initialized(&adev->trace_fifo);
+}
+
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+ __kfifo_fromio(&adev->trace_fifo, src, len);
+}
+
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+ avs_dump_fw_log(adev, src, len);
+ wake_up(&adev->trace_waitq);
+}
+
+static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ char *buf;
+ int ret;
+
+ buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations fw_regs_fops = {
+ .open = simple_open,
+ .read = fw_regs_read,
+};
+
+static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ size_t size;
+ char *buf;
+ int ret;
+
+ size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE;
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size);
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, size);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations debug_window_fops = {
+ .open = simple_open,
+ .read = debug_window_read,
+};
+
+static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ struct avs_probe_point_desc *desc;
+ size_t num_desc, len = 0;
+ char *buf;
+ int i, ret;
+
+ /* Prevent chaining, send and dump IPC value just once. */
+ if (*ppos)
+ return 0;
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
+ if (ret) {
+ ret = AVS_IPC_RET(ret);
+ goto exit;
+ }
+
+ for (i = 0; i < num_desc; i++) {
+ ret = scnprintf(buf + len, PAGE_SIZE - len,
+ "Id: %#010x Purpose: %d Node id: %#x\n",
+ desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
+ len += ret;
+ }
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+ kfree(desc);
+exit:
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ struct avs_probe_point_desc *desc;
+ u32 *array, num_elems;
+ size_t bytes;
+ int ret;
+
+ ret = parse_int_array_user(from, count, (int **)&array);
+ if (ret)
+ return ret;
+
+ num_elems = *array;
+ bytes = sizeof(*array) * num_elems;
+ if (bytes % sizeof(*desc)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ desc = (struct avs_probe_point_desc *)&array[1];
+ ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc));
+ if (ret)
+ ret = AVS_IPC_RET(ret);
+ else
+ ret = count;
+exit:
+ kfree(array);
+ return ret;
+}
+
+static const struct file_operations probe_points_fops = {
+ .open = simple_open,
+ .read = probe_points_read,
+ .write = probe_points_write,
+};
+
+static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ union avs_probe_point_id *id;
+ u32 *array, num_elems;
+ size_t bytes;
+ int ret;
+
+ ret = parse_int_array_user(from, count, (int **)&array);
+ if (ret)
+ return ret;
+
+ num_elems = *array;
+ bytes = sizeof(*array) * num_elems;
+ if (bytes % sizeof(*id)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ id = (union avs_probe_point_id *)&array[1];
+ ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id));
+ if (ret)
+ ret = AVS_IPC_RET(ret);
+ else
+ ret = count;
+exit:
+ kfree(array);
+ return ret;
+}
+
+static const struct file_operations probe_points_disconnect_fops = {
+ .open = simple_open,
+ .write = probe_points_disconnect_write,
+ .llseek = default_llseek,
+};
+
+static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ struct kfifo *fifo = &adev->trace_fifo;
+ unsigned int copied;
+
+ if (kfifo_is_empty(fifo)) {
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE);
+ if (!signal_pending(current))
+ schedule();
+ finish_wait(&adev->trace_waitq, &wait);
+ }
+
+ if (kfifo_to_user(fifo, to, count, &copied))
+ return -EFAULT;
+ *ppos += copied;
+ return copied;
+}
+
+static int strace_open(struct inode *inode, struct file *file)
+{
+ struct avs_dev *adev = inode->i_private;
+ int ret;
+
+ if (!try_module_get(adev->dev->driver->owner))
+ return -ENODEV;
+
+ if (kfifo_initialized(&adev->trace_fifo))
+ return -EBUSY;
+
+ ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ file->private_data = adev;
+ return 0;
+}
+
+static int strace_release(struct inode *inode, struct file *file)
+{
+ union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
+ struct avs_dev *adev = file->private_data;
+ unsigned long resource_mask;
+ unsigned long flags, i;
+ u32 num_cores;
+
+ resource_mask = adev->logged_resources;
+ num_cores = adev->hw_cfg.dsp_cores;
+
+ spin_lock_irqsave(&adev->trace_lock, flags);
+
+ /* Gather any remaining logs. */
+ for_each_set_bit(i, &resource_mask, num_cores) {
+ msg.log.core = i;
+ avs_dsp_op(adev, log_buffer_status, &msg);
+ }
+
+ kfifo_free(&adev->trace_fifo);
+
+ spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+ module_put(adev->dev->driver->owner);
+ return 0;
+}
+
+static const struct file_operations strace_fops = {
+ .llseek = default_llseek,
+ .read = strace_read,
+ .open = strace_open,
+ .release = strace_release,
+};
+
+#define DISABLE_TIMERS UINT_MAX
+
+static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities)
+{
+ int ret;
+
+ /* Logging demands D0i0 state from DSP. */
+ if (!adev->logged_resources) {
+ pm_runtime_get_sync(adev->dev);
+
+ ret = avs_dsp_disable_d0ix(adev);
+ if (ret)
+ goto err_d0ix;
+ }
+
+ ret = avs_ipc_set_system_time(adev);
+ if (ret && ret != AVS_IPC_NOT_SUPPORTED) {
+ ret = AVS_IPC_RET(ret);
+ goto err_ipc;
+ }
+
+ ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period,
+ adev->fifo_full_timer_period, resource_mask, priorities);
+ if (ret)
+ goto err_ipc;
+
+ adev->logged_resources |= resource_mask;
+ return 0;
+
+err_ipc:
+ if (!adev->logged_resources) {
+ avs_dsp_enable_d0ix(adev);
+err_d0ix:
+ pm_runtime_put_autosuspend(adev->dev);
+ }
+
+ return ret;
+}
+
+static int disable_logs(struct avs_dev *adev, u32 resource_mask)
+{
+ int ret;
+
+ /* Check if there's anything to do. */
+ if (!adev->logged_resources)
+ return 0;
+
+ ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS,
+ resource_mask, NULL);
+
+ /*
+ * If IPC fails causing recovery, logged_resources is already zero
+ * so unsetting bits is still safe.
+ */
+ adev->logged_resources &= ~resource_mask;
+
+ /* If that's the last resource, allow for D3. */
+ if (!adev->logged_resources) {
+ avs_dsp_enable_d0ix(adev);
+ pm_runtime_put_autosuspend(adev->dev);
+ }
+
+ return ret;
+}
+
+static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ char buf[64];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources);
+
+ return simple_read_from_buffer(to, count, ppos, buf, len);
+}
+
+static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ u32 *array, num_elems;
+ u32 resource_mask;
+ int ret;
+
+ ret = parse_int_array_user(from, count, (int **)&array);
+ if (ret)
+ return ret;
+
+ num_elems = *array;
+ if (!num_elems) {
+ ret = -EINVAL;
+ goto free_array;
+ }
+
+ /*
+ * Disable if just resource mask is provided - no log priority flags.
+ *
+ * Enable input format: mask, prio1, .., prioN
+ * Where 'N' equals number of bits set in the 'mask'.
+ */
+ resource_mask = array[1];
+ if (num_elems == 1) {
+ ret = disable_logs(adev, resource_mask);
+ } else {
+ if (num_elems != (hweight_long(resource_mask) + 1)) {
+ ret = -EINVAL;
+ goto free_array;
+ }
+
+ ret = enable_logs(adev, resource_mask, &array[2]);
+ }
+
+ if (!ret)
+ ret = count;
+free_array:
+ kfree(array);
+ return ret;
+}
+
+static const struct file_operations trace_control_fops = {
+ .llseek = default_llseek,
+ .read = trace_control_read,
+ .write = trace_control_write,
+ .open = simple_open,
+};
+
+void avs_debugfs_init(struct avs_dev *adev)
+{
+ init_waitqueue_head(&adev->trace_waitq);
+ spin_lock_init(&adev->trace_lock);
+
+ adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root);
+
+ /* Initialize timer periods with recommended defaults. */
+ adev->aging_timer_period = 10;
+ adev->fifo_full_timer_period = 10;
+
+ debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops);
+ debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops);
+ debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops);
+ debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops);
+
+ debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root,
+ &adev->aging_timer_period);
+ debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root,
+ &adev->fifo_full_timer_period);
+
+ debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops);
+ debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev,
+ &probe_points_disconnect_fops);
+}
+
+void avs_debugfs_exit(struct avs_dev *adev)
+{
+ debugfs_remove_recursive(adev->debugfs_root);
+}
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
new file mode 100644
index 000000000000..464bd6859182
--- /dev/null
+++ b/sound/soc/intel/avs/dsp.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/string_choices.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "registers.h"
+#include "trace.h"
+
+#define AVS_ADSPCS_DELAY_US 1000
+
+int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ u32 value, mask, reg;
+ int ret;
+
+ value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+ trace_avs_dsp_core_op(value, core_mask, "power", power);
+
+ mask = AVS_ADSPCS_SPA_MASK(core_mask);
+ value = power ? mask : 0;
+
+ snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
+ /* Delay the polling to avoid false positives. */
+ usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);
+
+ mask = AVS_ADSPCS_CPA_MASK(core_mask);
+ value = power ? mask : 0;
+
+ ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
+ reg, (reg & mask) == value,
+ AVS_ADSPCS_INTERVAL_US,
+ AVS_ADSPCS_TIMEOUT_US);
+ if (ret)
+ dev_err(adev->dev, "core_mask %d power %s failed: %d\n",
+ core_mask, str_on_off(power), ret);
+
+ return ret;
+}
+
+int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
+{
+ u32 value, mask, reg;
+ int ret;
+
+ value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+ trace_avs_dsp_core_op(value, core_mask, "reset", reset);
+
+ mask = AVS_ADSPCS_CRST_MASK(core_mask);
+ value = reset ? mask : 0;
+
+ snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
+
+ ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
+ reg, (reg & mask) == value,
+ AVS_ADSPCS_INTERVAL_US,
+ AVS_ADSPCS_TIMEOUT_US);
+ if (ret)
+ dev_err(adev->dev, "core_mask %d %s reset failed: %d\n",
+ core_mask, reset ? "enter" : "exit", ret);
+
+ return ret;
+}
+
+int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ u32 value, mask, reg;
+ int ret;
+
+ value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+ trace_avs_dsp_core_op(value, core_mask, "stall", stall);
+
+ mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
+ value = stall ? mask : 0;
+
+ snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
+
+ ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
+ reg, (reg & mask) == value,
+ AVS_ADSPCS_INTERVAL_US,
+ AVS_ADSPCS_TIMEOUT_US);
+ if (ret) {
+ dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
+ core_mask, stall ? "" : "un", ret);
+ return ret;
+ }
+
+ /* Give HW time to propagate the change. */
+ usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);
+ return 0;
+}
+
+int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
+{
+ int ret;
+
+ ret = avs_dsp_op(adev, power, core_mask, true);
+ if (ret)
+ return ret;
+
+ ret = avs_dsp_op(adev, reset, core_mask, false);
+ if (ret)
+ return ret;
+
+ return avs_dsp_op(adev, stall, core_mask, false);
+}
+
+int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
+{
+ /* No error checks to allow for complete DSP shutdown. */
+ avs_dsp_op(adev, stall, core_mask, true);
+ avs_dsp_op(adev, reset, core_mask, true);
+
+ return avs_dsp_op(adev, power, core_mask, false);
+}
+
+static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
+{
+ u32 mask;
+ int ret;
+
+ ret = avs_dsp_core_enable(adev, core_mask);
+ if (ret < 0)
+ return ret;
+
+ mask = core_mask & ~AVS_MAIN_CORE_MASK;
+ if (!mask)
+ /*
+ * without main core, fw is dead anyway
+ * so setting D0 for it is futile.
+ */
+ return 0;
+
+ ret = avs_ipc_set_dx(adev, mask, true);
+ return AVS_IPC_RET(ret);
+}
+
+static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
+{
+ int ret;
+
+ ret = avs_ipc_set_dx(adev, core_mask, false);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return avs_dsp_core_disable(adev, core_mask);
+}
+
+static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
+{
+ u32 mask;
+ int ret;
+
+ mask = BIT_MASK(core_id);
+ if (mask == AVS_MAIN_CORE_MASK)
+ /* nothing to do for main core */
+ return 0;
+ if (core_id >= adev->hw_cfg.dsp_cores) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ adev->core_refs[core_id]++;
+ if (adev->core_refs[core_id] == 1) {
+ /*
+ * No cores other than main-core can be running for DSP
+ * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
+ * simply d0ix power state will no longer be attempted.
+ */
+ ret = avs_dsp_disable_d0ix(adev);
+ if (ret && ret != -AVS_EIPC)
+ goto err_disable_d0ix;
+
+ ret = avs_dsp_enable(adev, mask);
+ if (ret)
+ goto err_enable_dsp;
+ }
+
+ return 0;
+
+err_enable_dsp:
+ avs_dsp_enable_d0ix(adev);
+err_disable_d0ix:
+ adev->core_refs[core_id]--;
+err:
+ dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
+ return ret;
+}
+
+static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
+{
+ u32 mask;
+ int ret;
+
+ mask = BIT_MASK(core_id);
+ if (mask == AVS_MAIN_CORE_MASK)
+ /* nothing to do for main core */
+ return 0;
+ if (core_id >= adev->hw_cfg.dsp_cores) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ adev->core_refs[core_id]--;
+ if (!adev->core_refs[core_id]) {
+ ret = avs_dsp_disable(adev, mask);
+ if (ret)
+ goto err;
+
+ /* Match disable_d0ix in avs_dsp_get_core(). */
+ avs_dsp_enable_d0ix(adev);
+ }
+
+ return 0;
+err:
+ dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);
+ return ret;
+}
+
+int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
+ u8 core_id, u8 domain, void *param, u32 param_size,
+ u8 *instance_id)
+{
+ struct avs_module_entry mentry;
+ bool was_loaded = false;
+ int ret, id;
+
+ id = avs_module_id_alloc(adev, module_id);
+ if (id < 0)
+ return id;
+
+ ret = avs_get_module_id_entry(adev, module_id, &mentry);
+ if (ret)
+ goto err_mod_entry;
+
+ ret = avs_dsp_get_core(adev, core_id);
+ if (ret)
+ goto err_mod_entry;
+
+ /* Load code into memory if this is the first instance. */
+ if (!id && !avs_module_entry_is_loaded(&mentry)) {
+ ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);
+ if (ret) {
+ dev_err(adev->dev, "load modules failed: %d\n", ret);
+ goto err_mod_entry;
+ }
+ was_loaded = true;
+ }
+
+ ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,
+ core_id, domain, param, param_size);
+ if (ret) {
+ ret = AVS_IPC_RET(ret);
+ goto err_ipc;
+ }
+
+ *instance_id = id;
+ return 0;
+
+err_ipc:
+ if (was_loaded)
+ avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
+ avs_dsp_put_core(adev, core_id);
+err_mod_entry:
+ avs_module_id_free(adev, module_id, id);
+ return ret;
+}
+
+void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 ppl_instance_id, u8 core_id)
+{
+ struct avs_module_entry mentry;
+ int ret;
+
+ /* Modules not owned by any pipeline need to be freed explicitly. */
+ if (ppl_instance_id == INVALID_PIPELINE_ID)
+ avs_ipc_delete_instance(adev, module_id, instance_id);
+
+ avs_module_id_free(adev, module_id, instance_id);
+
+ ret = avs_get_module_id_entry(adev, module_id, &mentry);
+ /* Unload occupied memory if this was the last instance. */
+ if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {
+ if (avs_is_module_ida_empty(adev, module_id)) {
+ ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
+ if (ret)
+ dev_err(adev->dev, "unload modules failed: %d\n", ret);
+ }
+ }
+
+ avs_dsp_put_core(adev, core_id);
+}
+
+int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+ bool lp, u16 attributes, u8 *instance_id)
+{
+ struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;
+ int ret, id;
+
+ id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes);
+ if (ret) {
+ ida_free(&adev->ppl_ida, id);
+ return AVS_IPC_RET(ret);
+ }
+
+ *instance_id = id;
+ return 0;
+}
+
+int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
+{
+ int ret;
+
+ ret = avs_ipc_delete_pipeline(adev, instance_id);
+ if (ret)
+ ret = AVS_IPC_RET(ret);
+
+ ida_free(&adev->ppl_ida, instance_id);
+ return ret;
+}
diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c
new file mode 100644
index 000000000000..d655e727bebd
--- /dev/null
+++ b/sound/soc/intel/avs/icl.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/slab.h>
+#include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+
+#define ICL_VS_LTRP_GB_ICCMAX 95
+
+#ifdef CONFIG_DEBUG_FS
+int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct avs_icl_log_state_info *info;
+ u32 size, num_libs = adev->fw_cfg.max_libs_count;
+ int i, ret;
+
+ if (fls_long(resource_mask) > num_libs)
+ return -EINVAL;
+ size = struct_size(info, logs_priorities_mask, num_libs);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->aging_timer_period = aging_period;
+ info->fifo_full_timer_period = fifo_full_period;
+ info->enable = enable;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, num_libs)
+ info->logs_priorities_mask[i] = *priorities++;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+#endif
+
+union avs_icl_memwnd2_slot_type {
+ u32 val;
+ struct {
+ u32 resource_id:8;
+ u32 type:24;
+ };
+} __packed;
+static_assert(sizeof(union avs_icl_memwnd2_slot_type) == 4);
+
+struct avs_icl_memwnd2_desc {
+ u32 resource_id;
+ union avs_icl_memwnd2_slot_type slot_id;
+ u32 vma;
+} __packed;
+static_assert(sizeof(struct avs_icl_memwnd2_desc) == 12);
+
+#define AVS_ICL_MEMWND2_SLOTS_COUNT 15
+
+struct avs_icl_memwnd2 {
+ union {
+ struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT];
+ u8 rsvd[SZ_4K];
+ };
+ u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][SZ_4K];
+} __packed;
+static_assert(sizeof(struct avs_icl_memwnd2) == 65536);
+
+#define AVS_ICL_SLOT_UNUSED \
+ ((union avs_icl_memwnd2_slot_type) { 0x00000000U })
+#define AVS_ICL_SLOT_CRITICAL_LOG \
+ ((union avs_icl_memwnd2_slot_type) { 0x54524300U })
+#define AVS_ICL_SLOT_DEBUG_LOG \
+ ((union avs_icl_memwnd2_slot_type) { 0x474f4c00U })
+#define AVS_ICL_SLOT_GDB_STUB \
+ ((union avs_icl_memwnd2_slot_type) { 0x42444700U })
+#define AVS_ICL_SLOT_BROKEN \
+ ((union avs_icl_memwnd2_slot_type) { 0x44414544U })
+
+static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_type slot_type)
+{
+ struct avs_icl_memwnd2_desc desc[AVS_ICL_MEMWND2_SLOTS_COUNT];
+ int i;
+
+ memcpy_fromio(&desc, avs_sram_addr(adev, AVS_DEBUG_WINDOW), sizeof(desc));
+
+ for (i = 0; i < AVS_ICL_MEMWND2_SLOTS_COUNT; i++)
+ if (desc[i].slot_id.val == slot_type.val)
+ return offsetof(struct avs_icl_memwnd2, slot_array) + i * SZ_4K;
+ return -ENXIO;
+}
+
+int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+ union avs_icl_memwnd2_slot_type slot_type = AVS_ICL_SLOT_DEBUG_LOG;
+ int ret;
+
+ slot_type.resource_id = core;
+ ret = avs_icl_slot_offset(adev, slot_type);
+ if (ret < 0)
+ dev_dbg(adev->dev, "No slot offset found for: %x\n",
+ slot_type.val);
+
+ return ret;
+}
+
+bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+ /* Full-power when starting DMA engines. */
+ if (tx->glb.set_ppl_state.state == AVS_PPL_STATE_RUNNING)
+ return true;
+
+ /* Payload-less IPCs do not take part in d0ix toggling. */
+ return tx->size;
+}
+
+int avs_icl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ int ret;
+
+ ret = avs_ipc_set_d0ix(adev, enable, false);
+ return AVS_IPC_RET(ret);
+}
+
+int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_stream *host_stream;
+ struct snd_pcm_substream substream;
+ struct snd_dma_buffer dmab;
+ unsigned int sd_fmt;
+ u8 ltrp_gb;
+ int ret;
+
+ /*
+ * ICCMAX:
+ *
+ * For ICL+ platforms, as per HW recommendation LTRP_GB is set to 95us
+ * during FW load. Its original value shall be restored once load completes.
+ *
+ * To avoid DMI/OPIO L1 entry during the load procedure, additional CAPTURE
+ * stream is allocated and set to run.
+ */
+
+ memset(&substream, 0, sizeof(substream));
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+
+ host_stream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST);
+ if (!host_stream)
+ return -EBUSY;
+
+ ltrp_gb = snd_hdac_chip_readb(bus, VS_LTRP) & AZX_REG_VS_LTRP_GB_MASK;
+ /* Carries no real data, use default format. */
+ sd_fmt = snd_hdac_stream_format(1, 32, 48000);
+
+ ret = snd_hdac_dsp_prepare(hdac_stream(host_stream), sd_fmt, fw->size, &dmab);
+ if (ret < 0)
+ goto release_stream;
+
+ snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ICL_VS_LTRP_GB_ICCMAX);
+
+ spin_lock(&bus->reg_lock);
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ spin_unlock(&bus->reg_lock);
+
+ ret = avs_hda_load_basefw(adev, fw);
+
+ spin_lock(&bus->reg_lock);
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ spin_unlock(&bus->reg_lock);
+
+ snd_hdac_dsp_cleanup(hdac_stream(host_stream), &dmab);
+
+release_stream:
+ snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+ snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ltrp_gb);
+
+ return ret;
+}
+
+const struct avs_dsp_ops avs_icl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .dsp_interrupt = avs_cnl_dsp_interrupt,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_icl_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .log_buffer_offset = avs_icl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_icl_d0ix_toggle,
+ .set_d0ix = avs_icl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(icl)
+};
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
new file mode 100644
index 000000000000..c0feb9edd7f6
--- /dev/null
+++ b/sound/soc/intel/avs/ipc.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+#include "registers.h"
+#include "trace.h"
+
+#define AVS_IPC_TIMEOUT_MS 300
+#define AVS_D0IX_DELAY_MS 300
+
+static int
+avs_dsp_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ struct avs_ipc *ipc = adev->ipc;
+ int ret;
+
+ /* Is transition required? */
+ if (ipc->in_d0ix == enable)
+ return 0;
+
+ ret = avs_dsp_op(adev, set_d0ix, enable);
+ if (ret) {
+ /* Prevent further d0ix attempts on conscious IPC failure. */
+ if (ret == -AVS_EIPC)
+ atomic_inc(&ipc->d0ix_disable_depth);
+
+ ipc->in_d0ix = false;
+ return ret;
+ }
+
+ ipc->in_d0ix = enable;
+ return 0;
+}
+
+static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+ if (atomic_read(&adev->ipc->d0ix_disable_depth))
+ return;
+
+ mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work,
+ msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+}
+
+static void avs_dsp_d0ix_work(struct work_struct *work)
+{
+ struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work);
+
+ avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true);
+}
+
+static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ if (!atomic_read(&ipc->d0ix_disable_depth)) {
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ return avs_dsp_set_d0ix(adev, false);
+ }
+
+ return 0;
+}
+
+int avs_dsp_disable_d0ix(struct avs_dev *adev)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ /* Prevent PG only on the first disable. */
+ if (atomic_inc_return(&ipc->d0ix_disable_depth) == 1) {
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ return avs_dsp_set_d0ix(adev, false);
+ }
+
+ return 0;
+}
+
+int avs_dsp_enable_d0ix(struct avs_dev *adev)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ if (atomic_dec_and_test(&ipc->d0ix_disable_depth))
+ queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work,
+ msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+ return 0;
+}
+
+static void avs_dsp_recovery(struct avs_dev *adev)
+{
+ struct avs_soc_component *acomp;
+ unsigned int core_mask;
+ int ret;
+
+ mutex_lock(&adev->comp_list_mutex);
+ /* disconnect all running streams */
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_card *card;
+
+ card = acomp->base.card;
+ if (!card)
+ continue;
+
+ for_each_card_rtds(card, rtd) {
+ struct snd_pcm *pcm;
+ int dir;
+
+ pcm = rtd->pcm;
+ if (!pcm || rtd->dai_link->no_pcm)
+ continue;
+
+ for_each_pcm_streams(dir) {
+ struct snd_pcm_substream *substream;
+
+ substream = pcm->streams[dir].substream;
+ if (!substream || !substream->runtime)
+ continue;
+
+ /* No need for _irq() as we are in nonatomic context. */
+ snd_pcm_stream_lock(substream);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+ snd_pcm_stream_unlock(substream);
+ }
+ }
+ }
+ mutex_unlock(&adev->comp_list_mutex);
+
+ /* forcibly shutdown all cores */
+ core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
+ avs_dsp_core_disable(adev, core_mask);
+
+ /* attempt dsp reboot */
+ ret = avs_dsp_boot_firmware(adev, true);
+ if (ret < 0)
+ dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
+
+ pm_runtime_enable(adev->dev);
+ pm_request_autosuspend(adev->dev);
+
+ atomic_set(&adev->ipc->recovering, 0);
+}
+
+static void avs_dsp_recovery_work(struct work_struct *work)
+{
+ struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work);
+
+ avs_dsp_recovery(to_avs_dev(ipc->dev));
+}
+
+static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ /* Account for the double-exception case. */
+ ipc->ready = false;
+
+ if (!atomic_add_unless(&ipc->recovering, 1, 1)) {
+ dev_err(adev->dev, "dsp recovery is already in progress\n");
+ return;
+ }
+
+ dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
+
+ /* Avoid deadlock as the exception may be the response to SET_D0IX. */
+ if (current_work() != &ipc->d0ix_work.work)
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ ipc->in_d0ix = false;
+ /* Re-enabled on recovery completion. */
+ pm_runtime_disable(adev->dev);
+
+ /* Process received notification. */
+ avs_dsp_op(adev, coredump, msg);
+
+ schedule_work(&ipc->recovery_work);
+}
+
+static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
+{
+ struct avs_ipc *ipc = adev->ipc;
+ union avs_reply_msg msg = AVS_MSG(header);
+ u32 sts, lec;
+
+ sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev));
+ lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev));
+ trace_avs_ipc_reply_msg(header, sts, lec);
+
+ ipc->rx.header = header;
+ /* Abort copying payload if request processing was unsuccessful. */
+ if (!msg.status) {
+ /* update size in case of LARGE_CONFIG_GET */
+ if (msg.msg_target == AVS_MOD_MSG &&
+ msg.global_msg_type == AVS_MOD_LARGE_CONFIG_GET)
+ ipc->rx.size = min_t(u32, AVS_MAILBOX_SIZE,
+ msg.ext.large_config.data_off_size);
+
+ memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size);
+ trace_avs_msg_payload(ipc->rx.data, ipc->rx.size);
+ }
+}
+
+static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
+{
+ struct avs_notify_mod_data mod_data;
+ union avs_notify_msg msg = AVS_MSG(header);
+ size_t data_size = 0;
+ void *data = NULL;
+ u32 sts, lec;
+
+ sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev));
+ lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev));
+ trace_avs_ipc_notify_msg(header, sts, lec);
+
+ /* Ignore spurious notifications until handshake is established. */
+ if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
+ dev_dbg(adev->dev, "FW not ready, skip notification: 0x%08x\n", msg.primary);
+ return;
+ }
+
+ /* Calculate notification payload size. */
+ switch (msg.notify_msg_type) {
+ case AVS_NOTIFY_FW_READY:
+ break;
+
+ case AVS_NOTIFY_PHRASE_DETECTED:
+ data_size = sizeof(struct avs_notify_voice_data);
+ break;
+
+ case AVS_NOTIFY_RESOURCE_EVENT:
+ data_size = sizeof(struct avs_notify_res_data);
+ break;
+
+ case AVS_NOTIFY_LOG_BUFFER_STATUS:
+ case AVS_NOTIFY_EXCEPTION_CAUGHT:
+ break;
+
+ case AVS_NOTIFY_MODULE_EVENT:
+ /* To know the total payload size, header needs to be read first. */
+ memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
+ data_size = sizeof(mod_data) + mod_data.data_size;
+ break;
+
+ default:
+ dev_info(adev->dev, "unknown notification: 0x%08x\n", msg.primary);
+ break;
+ }
+
+ if (data_size) {
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (!data)
+ return;
+
+ memcpy_fromio(data, avs_uplink_addr(adev), data_size);
+ trace_avs_msg_payload(data, data_size);
+ }
+
+ /* Perform notification-specific operations. */
+ switch (msg.notify_msg_type) {
+ case AVS_NOTIFY_FW_READY:
+ dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary);
+ adev->ipc->ready = true;
+ complete(&adev->fw_ready);
+ break;
+
+ case AVS_NOTIFY_LOG_BUFFER_STATUS:
+ avs_log_buffer_status_locked(adev, &msg);
+ break;
+
+ case AVS_NOTIFY_EXCEPTION_CAUGHT:
+ avs_dsp_exception_caught(adev, &msg);
+ break;
+
+ default:
+ break;
+ }
+
+ kfree(data);
+}
+
+void avs_dsp_process_response(struct avs_dev *adev, u64 header)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ /*
+ * Response may either be solicited - a reply for a request that has
+ * been sent beforehand - or unsolicited (notification).
+ */
+ if (avs_msg_is_reply(header)) {
+ /* Response processing is invoked from IRQ thread. */
+ spin_lock_irq(&ipc->rx_lock);
+ avs_dsp_receive_rx(adev, header);
+ ipc->rx_completed = true;
+ spin_unlock_irq(&ipc->rx_lock);
+ } else {
+ avs_dsp_process_notification(adev, header);
+ }
+
+ complete(&ipc->busy_completion);
+}
+
+static bool avs_ipc_is_busy(struct avs_ipc *ipc)
+{
+ struct avs_dev *adev = to_avs_dev(ipc->dev);
+ const struct avs_spec *const spec = adev->spec;
+ u32 hipc_rsp;
+
+ hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+ return hipc_rsp & spec->hipc->rsp_busy_mask;
+}
+
+static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout)
+{
+ u32 repeats_left = 128; /* to avoid infinite looping */
+ int ret;
+
+again:
+ ret = wait_for_completion_timeout(&ipc->busy_completion, msecs_to_jiffies(timeout));
+
+ /* DSP could be unresponsive at this point. */
+ if (!ipc->ready)
+ return -EPERM;
+
+ if (!ret) {
+ if (!avs_ipc_is_busy(ipc))
+ return -ETIMEDOUT;
+ /*
+ * Firmware did its job, either notification or reply
+ * has been received - now wait until it's processed.
+ */
+ wait_for_completion_killable(&ipc->busy_completion);
+ }
+
+ /* Ongoing notification's bottom-half may cause early wakeup */
+ spin_lock(&ipc->rx_lock);
+ if (!ipc->rx_completed) {
+ if (repeats_left) {
+ /* Reply delayed due to notification. */
+ repeats_left--;
+ reinit_completion(&ipc->busy_completion);
+ spin_unlock(&ipc->rx_lock);
+ goto again;
+ }
+
+ spin_unlock(&ipc->rx_lock);
+ return -ETIMEDOUT;
+ }
+
+ spin_unlock(&ipc->rx_lock);
+ return 0;
+}
+
+static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply)
+{
+ lockdep_assert_held(&ipc->rx_lock);
+
+ ipc->rx.header = 0;
+ ipc->rx.size = reply ? reply->size : 0;
+ ipc->rx_completed = false;
+
+ reinit_completion(&ipc->done_completion);
+ reinit_completion(&ipc->busy_completion);
+}
+
+static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs)
+{
+ const struct avs_spec *const spec = adev->spec;
+ u32 sts = UINT_MAX;
+ u32 lec = UINT_MAX;
+
+ tx->header |= spec->hipc->req_busy_mask;
+ if (read_fwregs) {
+ sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev));
+ lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev));
+ }
+
+ trace_avs_request(tx, sts, lec);
+
+ if (tx->size)
+ memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size);
+ snd_hdac_adsp_writel(adev, spec->hipc->req_ext_offset, tx->header >> 32);
+ snd_hdac_adsp_writel(adev, spec->hipc->req_offset, tx->header & UINT_MAX);
+}
+
+static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, const char *name)
+{
+ struct avs_ipc *ipc = adev->ipc;
+ int ret;
+
+ if (!ipc->ready)
+ return -EPERM;
+
+ mutex_lock(&ipc->msg_mutex);
+
+ spin_lock(&ipc->rx_lock);
+ avs_ipc_msg_init(ipc, reply);
+ avs_dsp_send_tx(adev, request, true);
+ spin_unlock(&ipc->rx_lock);
+
+ ret = avs_ipc_wait_busy_completion(ipc, timeout);
+ if (ret) {
+ if (ret == -ETIMEDOUT) {
+ union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT);
+
+ /* Same treatment as on exception, just stack_dump=0. */
+ avs_dsp_exception_caught(adev, &msg);
+ }
+ goto exit;
+ }
+
+ ret = ipc->rx.rsp.status;
+ /*
+ * If IPC channel is blocked e.g.: due to ongoing recovery,
+ * -EPERM error code is expected and thus it's not an actual error.
+ *
+ * Unsupported IPCs are of no harm either.
+ */
+ if (ret == -EPERM || ret == AVS_IPC_NOT_SUPPORTED)
+ dev_dbg(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n",
+ name, request->glb.primary, request->glb.ext.val, ret);
+ else if (ret)
+ dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n",
+ name, request->glb.primary, request->glb.ext.val, ret);
+
+ if (reply) {
+ reply->header = ipc->rx.header;
+ reply->size = ipc->rx.size;
+ if (reply->data && ipc->rx.size)
+ memcpy(reply->data, ipc->rx.data, reply->size);
+ }
+
+exit:
+ mutex_unlock(&ipc->msg_mutex);
+ return ret;
+}
+
+static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+ bool schedule_d0ix, const char *name)
+{
+ int ret;
+
+ trace_avs_d0ix("wake", wake_d0i0, request->header);
+ if (wake_d0i0) {
+ ret = avs_dsp_wake_d0i0(adev, request);
+ if (ret)
+ return ret;
+ }
+
+ ret = avs_dsp_do_send_msg(adev, request, reply, timeout, name);
+ if (ret)
+ return ret;
+
+ trace_avs_d0ix("schedule", schedule_d0ix, request->header);
+ if (schedule_d0ix)
+ avs_dsp_schedule_d0ix(adev, request);
+
+ return 0;
+}
+
+int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, const char *name)
+{
+ bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true);
+ bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false);
+
+ return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix,
+ name);
+}
+
+int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, const char *name)
+{
+ return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms, name);
+}
+
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+ const char *name)
+{
+ return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false, name);
+}
+
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, bool wake_d0i0, const char *name)
+{
+ return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms,
+ wake_d0i0, name);
+}
+
+static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout,
+ const char *name)
+{
+ struct avs_ipc *ipc = adev->ipc;
+ int ret;
+
+ mutex_lock(&ipc->msg_mutex);
+
+ spin_lock(&ipc->rx_lock);
+ avs_ipc_msg_init(ipc, NULL);
+ /*
+ * with hw still stalled, memory windows may not be
+ * configured properly so avoid accessing SRAM
+ */
+ avs_dsp_send_tx(adev, request, false);
+ spin_unlock(&ipc->rx_lock);
+
+ /* ROM messages must be sent before main core is unstalled */
+ ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false);
+ if (!ret) {
+ ret = wait_for_completion_timeout(&ipc->done_completion, msecs_to_jiffies(timeout));
+ ret = ret ? 0 : -ETIMEDOUT;
+ }
+ if (ret)
+ dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n",
+ name, request->glb.primary, request->glb.ext.val, ret);
+
+ mutex_unlock(&ipc->msg_mutex);
+
+ return ret;
+}
+
+int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout,
+ const char *name)
+{
+ return avs_dsp_do_send_rom_msg(adev, request, timeout, name);
+}
+
+int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, const char *name)
+{
+ return avs_dsp_send_rom_msg_timeout(adev, request, adev->ipc->default_timeout_ms, name);
+}
+
+void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
+{
+ const struct avs_spec *const spec = adev->spec;
+ u32 value, mask;
+
+ /*
+ * No particular bit setting order. All of these are required
+ * to have a functional SW <-> FW communication.
+ */
+ value = enable ? AVS_ADSP_ADSPIC_IPC : 0;
+ snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_IPC, value);
+
+ mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY;
+ value = enable ? mask : 0;
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, mask, value);
+}
+
+int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
+{
+ ipc->rx.data = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!ipc->rx.data)
+ return -ENOMEM;
+
+ ipc->dev = dev;
+ ipc->ready = false;
+ ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
+ INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
+ INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work);
+ init_completion(&ipc->done_completion);
+ init_completion(&ipc->busy_completion);
+ spin_lock_init(&ipc->rx_lock);
+ mutex_init(&ipc->msg_mutex);
+
+ return 0;
+}
+
+void avs_ipc_block(struct avs_ipc *ipc)
+{
+ ipc->ready = false;
+ cancel_work_sync(&ipc->recovery_work);
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ ipc->in_d0ix = false;
+}
diff --git a/sound/soc/intel/avs/lnl.c b/sound/soc/intel/avs/lnl.c
new file mode 100644
index 000000000000..4fbc62bfd6c5
--- /dev/null
+++ b/sound/soc/intel/avs/lnl.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2021-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "registers.h"
+
+int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ ret = avs_mtl_core_stall(adev, core_mask, stall);
+
+ /* On unstall, route interrupts from the links to the DSP firmware. */
+ if (!ret && !stall)
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_OFLEN,
+ AZX_ML_LCTL_OFLEN);
+ return ret;
+}
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
new file mode 100644
index 000000000000..353e343b1d28
--- /dev/null
+++ b/sound/soc/intel/avs/loader.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "cldma.h"
+#include "messages.h"
+#include "registers.h"
+#include "topology.h"
+
+#define AVS_ROM_STS_MASK 0xFF
+#define AVS_ROM_INIT_DONE 0x1
+#define SKL_ROM_BASEFW_ENTERED 0xF
+#define APL_ROM_FW_ENTERED 0x5
+#define AVS_ROM_INIT_POLLING_US 5
+#define SKL_ROM_INIT_TIMEOUT_US 1000000
+#define APL_ROM_INIT_TIMEOUT_US 300000
+#define APL_ROM_INIT_RETRIES 3
+
+#define AVS_FW_INIT_POLLING_US 500
+#define AVS_FW_INIT_TIMEOUT_MS 3000
+#define AVS_FW_INIT_TIMEOUT_US (AVS_FW_INIT_TIMEOUT_MS * 1000)
+
+#define AVS_CLDMA_START_DELAY_MS 100
+
+#define AVS_ROOT_DIR "intel/avs"
+#define AVS_BASEFW_FILENAME "dsp_basefw.bin"
+#define AVS_EXT_MANIFEST_MAGIC 0x31454124
+#define SKL_MANIFEST_MAGIC 0x00000006
+#define SKL_ADSPFW_OFFSET 0x284
+#define APL_MANIFEST_MAGIC 0x44504324
+#define APL_ADSPFW_OFFSET 0x2000
+
+/* Occasionally, engineering (release candidate) firmware is provided for testing. */
+static bool debug_ignore_fw_version;
+module_param_named(ignore_fw_version, debug_ignore_fw_version, bool, 0444);
+MODULE_PARM_DESC(ignore_fw_version, "Ignore firmware version check 0=no (default), 1=yes");
+
+#define AVS_LIB_NAME_SIZE 8
+
+struct avs_fw_manifest {
+ u32 id;
+ u32 len;
+ char name[AVS_LIB_NAME_SIZE];
+ u32 preload_page_count;
+ u32 img_flags;
+ u32 feature_mask;
+ struct avs_fw_version version;
+} __packed;
+static_assert(sizeof(struct avs_fw_manifest) == 36);
+
+struct avs_fw_ext_manifest {
+ u32 id;
+ u32 len;
+ u16 version_major;
+ u16 version_minor;
+ u32 entries;
+} __packed;
+static_assert(sizeof(struct avs_fw_ext_manifest) == 16);
+
+static int avs_fw_ext_manifest_strip(struct firmware *fw)
+{
+ struct avs_fw_ext_manifest *man;
+
+ if (fw->size < sizeof(*man))
+ return -EINVAL;
+
+ man = (struct avs_fw_ext_manifest *)fw->data;
+ if (man->id == AVS_EXT_MANIFEST_MAGIC) {
+ fw->data += man->len;
+ fw->size -= man->len;
+ }
+
+ return 0;
+}
+
+static int avs_fw_manifest_offset(struct firmware *fw)
+{
+ /* Header type found in first DWORD of fw binary. */
+ u32 magic = *(u32 *)fw->data;
+
+ switch (magic) {
+ case SKL_MANIFEST_MAGIC:
+ return SKL_ADSPFW_OFFSET;
+ case APL_MANIFEST_MAGIC:
+ return APL_ADSPFW_OFFSET;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *fw,
+ const struct avs_fw_version *min)
+{
+ struct avs_fw_manifest *man;
+ int offset, ret;
+
+ ret = avs_fw_ext_manifest_strip(fw);
+ if (ret)
+ return ret;
+
+ offset = avs_fw_manifest_offset(fw);
+ if (offset < 0)
+ return offset;
+
+ if (fw->size < offset + sizeof(*man))
+ return -EINVAL;
+ if (!min)
+ return 0;
+
+ man = (struct avs_fw_manifest *)(fw->data + offset);
+ if (man->version.major != min->major ||
+ man->version.minor != min->minor ||
+ man->version.hotfix != min->hotfix ||
+ man->version.build < min->build) {
+ dev_warn(adev->dev, "bad FW version %d.%d.%d.%d, expected %d.%d.%d.%d or newer\n",
+ man->version.major, man->version.minor,
+ man->version.hotfix, man->version.build,
+ min->major, min->minor, min->hotfix, min->build);
+
+ if (!debug_ignore_fw_version)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+ struct hda_cldma *cl = &code_loader;
+ unsigned int reg;
+ int ret;
+
+ ret = avs_dsp_op(adev, power, AVS_MAIN_CORE_MASK, true);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false);
+ if (ret < 0)
+ return ret;
+
+ ret = hda_cldma_reset(cl);
+ if (ret < 0) {
+ dev_err(adev->dev, "cldma reset failed: %d\n", ret);
+ return ret;
+ }
+ hda_cldma_setup(cl);
+
+ ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false);
+ if (ret < 0)
+ return ret;
+
+ reinit_completion(&adev->fw_ready);
+ avs_dsp_op(adev, int_control, true);
+
+ /* await ROM init */
+ ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
+ (reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE,
+ AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(adev->dev, "rom init failed: %d, status: 0x%08x, lec: 0x%08x\n",
+ ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)));
+ avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ return ret;
+ }
+
+ hda_cldma_set_data(cl, (void *)fw->data, fw->size);
+ /* transfer firmware */
+ hda_cldma_transfer(cl, 0);
+ ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
+ (reg & AVS_ROM_STS_MASK) == SKL_ROM_BASEFW_ENTERED,
+ AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US);
+ hda_cldma_stop(cl);
+ if (ret < 0) {
+ dev_err(adev->dev, "transfer fw failed: %d, status: 0x%08x, lec: 0x%08x\n",
+ ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)));
+ avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ return ret;
+ }
+
+ return 0;
+}
+
+int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
+{
+ struct hda_cldma *cl = &code_loader;
+ int ret;
+
+ hda_cldma_set_data(cl, (void *)lib->data, lib->size);
+ /* transfer modules manifest */
+ hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
+
+ /* DMA id ignored as there is only ever one code-loader DMA */
+ ret = avs_ipc_load_library(adev, 0, id);
+ hda_cldma_stop(cl);
+
+ if (ret) {
+ ret = AVS_IPC_RET(ret);
+ dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
+ }
+
+ return ret;
+}
+
+static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry)
+{
+ struct hda_cldma *cl = &code_loader;
+ const struct firmware *mod;
+ char *mod_name;
+ int ret;
+
+ mod_name = kasprintf(GFP_KERNEL, "%s/%s/dsp_mod_%pUL.bin", AVS_ROOT_DIR,
+ adev->spec->name, mentry->uuid.b);
+ if (!mod_name)
+ return -ENOMEM;
+
+ ret = avs_request_firmware(adev, &mod, mod_name);
+ kfree(mod_name);
+ if (ret < 0)
+ return ret;
+
+ avs_hda_power_gating_enable(adev, false);
+ avs_hda_clock_gating_enable(adev, false);
+ avs_hda_l1sen_enable(adev, false);
+
+ hda_cldma_set_data(cl, (void *)mod->data, mod->size);
+ hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
+ ret = avs_ipc_load_modules(adev, &mentry->module_id, 1);
+ hda_cldma_stop(cl);
+
+ avs_hda_l1sen_enable(adev, true);
+ avs_hda_clock_gating_enable(adev, true);
+ avs_hda_power_gating_enable(adev, true);
+
+ if (ret) {
+ dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret);
+ avs_release_last_firmware(adev);
+ return AVS_IPC_RET(ret);
+ }
+
+ return 0;
+}
+
+int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
+ struct avs_module_entry *mods, u32 num_mods)
+{
+ u16 *mod_ids;
+ int ret, i;
+
+ /* Either load to DSP or unload them to free space. */
+ if (load) {
+ for (i = 0; i < num_mods; i++) {
+ ret = avs_cldma_load_module(adev, &mods[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+ }
+
+ mod_ids = kcalloc(num_mods, sizeof(u16), GFP_KERNEL);
+ if (!mod_ids)
+ return -ENOMEM;
+
+ for (i = 0; i < num_mods; i++)
+ mod_ids[i] = mods[i].module_id;
+
+ ret = avs_ipc_unload_modules(adev, mod_ids, num_mods);
+ kfree(mod_ids);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
+static int
+avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge)
+{
+ const struct avs_spec *const spec = adev->spec;
+ unsigned int corex_mask, reg;
+ int ret;
+
+ corex_mask = spec->core_init_mask & ~AVS_MAIN_CORE_MASK;
+
+ ret = avs_dsp_op(adev, power, spec->core_init_mask, true);
+ if (ret < 0)
+ goto err;
+
+ ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false);
+ if (ret < 0)
+ goto err;
+
+ reinit_completion(&adev->fw_ready);
+ avs_dsp_op(adev, int_control, true);
+
+ /* set boot config */
+ ret = avs_ipc_set_boot_config(adev, dma_id, purge);
+ if (ret) {
+ ret = AVS_IPC_RET(ret);
+ goto err;
+ }
+
+ /* await ROM init */
+ ret = snd_hdac_adsp_readl_poll(adev, spec->hipc->sts_offset, reg,
+ (reg & 0xF) == AVS_ROM_INIT_DONE ||
+ (reg & 0xF) == APL_ROM_FW_ENTERED,
+ AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(adev->dev, "rom init failed: %d, status: 0x%08x, lec: 0x%08x\n",
+ ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)));
+ goto err;
+ }
+
+ /* power down non-main cores */
+ if (corex_mask) {
+ ret = avs_dsp_op(adev, power, corex_mask, false);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ avs_dsp_core_disable(adev, spec->core_init_mask);
+ return ret;
+}
+
+static int avs_imr_load_basefw(struct avs_dev *adev)
+{
+ int ret;
+
+ /* DMA id ignored when flashing from IMR as no transfer occurs. */
+ ret = avs_hda_init_rom(adev, 0, false);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&adev->fw_ready,
+ msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
+ if (!ret) {
+ dev_err(adev->dev, "firmware ready timeout, status: 0x%08x, lec: 0x%08x\n",
+ snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)),
+ snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)));
+ avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+ struct snd_pcm_substream substream;
+ struct snd_dma_buffer dmab;
+ struct hdac_ext_stream *estream;
+ struct hdac_stream *hstream;
+ struct hdac_bus *bus = &adev->base.core;
+ unsigned int sdfmt, reg;
+ int ret, i;
+
+ /* configure hda dma */
+ memset(&substream, 0, sizeof(substream));
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ estream = snd_hdac_ext_stream_assign(bus, &substream,
+ HDAC_EXT_STREAM_TYPE_HOST);
+ if (!estream)
+ return -ENODEV;
+ hstream = hdac_stream(estream);
+
+ /* code loading performed with default format */
+ sdfmt = snd_hdac_stream_format(1, 32, 48000);
+ ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab);
+ if (ret < 0)
+ goto release_stream;
+
+ /* enable SPIB for hda stream */
+ snd_hdac_stream_spbcap_enable(bus, true, hstream->index);
+ ret = snd_hdac_stream_set_spib(bus, hstream, fw->size);
+ if (ret)
+ goto cleanup_resources;
+
+ memcpy(dmab.area, fw->data, fw->size);
+
+ for (i = 0; i < APL_ROM_INIT_RETRIES; i++) {
+ unsigned int dma_id = hstream->stream_tag - 1;
+
+ ret = avs_hda_init_rom(adev, dma_id, true);
+ if (!ret)
+ break;
+ dev_info(adev->dev, "#%d rom init failed: %d\n", i + 1, ret);
+ }
+ if (ret < 0)
+ goto cleanup_resources;
+
+ /* transfer firmware */
+ snd_hdac_dsp_trigger(hstream, true);
+ ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
+ (reg & AVS_ROM_STS_MASK) == APL_ROM_FW_ENTERED,
+ AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US);
+ snd_hdac_dsp_trigger(hstream, false);
+ if (ret < 0) {
+ dev_err(adev->dev, "transfer fw failed: %d, status: 0x%08x, lec: 0x%08x\n",
+ ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)));
+ avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ }
+
+cleanup_resources:
+ /* disable SPIB for hda stream */
+ snd_hdac_stream_spbcap_enable(bus, false, hstream->index);
+ snd_hdac_stream_set_spib(bus, hstream, 0);
+
+ snd_hdac_dsp_cleanup(hstream, &dmab);
+release_stream:
+ snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
+
+ return ret;
+}
+
+int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
+{
+ struct snd_pcm_substream substream;
+ struct snd_dma_buffer dmab;
+ struct hdac_ext_stream *estream;
+ struct hdac_stream *stream;
+ struct hdac_bus *bus = &adev->base.core;
+ unsigned int sdfmt;
+ int ret;
+
+ /* configure hda dma */
+ memset(&substream, 0, sizeof(substream));
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ estream = snd_hdac_ext_stream_assign(bus, &substream,
+ HDAC_EXT_STREAM_TYPE_HOST);
+ if (!estream)
+ return -ENODEV;
+ stream = hdac_stream(estream);
+
+ /* code loading performed with default format */
+ sdfmt = snd_hdac_stream_format(1, 32, 48000);
+ ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab);
+ if (ret < 0)
+ goto release_stream;
+
+ /* enable SPIB for hda stream */
+ snd_hdac_stream_spbcap_enable(bus, true, stream->index);
+ snd_hdac_stream_set_spib(bus, stream, lib->size);
+
+ memcpy(dmab.area, lib->data, lib->size);
+
+ /* transfer firmware */
+ snd_hdac_dsp_trigger(stream, true);
+ ret = avs_ipc_load_library(adev, stream->stream_tag - 1, id);
+ snd_hdac_dsp_trigger(stream, false);
+ if (ret) {
+ dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
+ ret = AVS_IPC_RET(ret);
+ }
+
+ /* disable SPIB for hda stream */
+ snd_hdac_stream_spbcap_enable(bus, false, stream->index);
+ snd_hdac_stream_set_spib(bus, stream, 0);
+
+ snd_hdac_dsp_cleanup(stream, &dmab);
+release_stream:
+ snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
+
+ return ret;
+}
+
+int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
+ struct avs_module_entry *mods, u32 num_mods)
+{
+ /*
+ * All platforms without CLDMA are equipped with IMR,
+ * and thus the module transferring is offloaded to DSP.
+ */
+ return 0;
+}
+
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs)
+{
+ int start, id, i = 0;
+ int ret;
+
+ /* Calculate the id to assign for the next lib. */
+ for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
+ if (adev->lib_names[id][0] == '\0')
+ break;
+ if (id + num_libs >= adev->fw_cfg.max_libs_count)
+ return -EINVAL;
+
+ start = id;
+ while (i < num_libs) {
+ struct avs_fw_manifest *man;
+ const struct firmware *fw;
+ struct firmware stripped_fw;
+ char *filename;
+ int j;
+
+ filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name,
+ libs[i].name);
+ if (!filename)
+ return -ENOMEM;
+
+ /*
+ * If any call after this one fails, requested firmware is not released with
+ * avs_release_last_firmware() as failing to load code results in need for reload
+ * of entire driver module. And then avs_release_firmwares() is in place already.
+ */
+ ret = avs_request_firmware(adev, &fw, filename);
+ kfree(filename);
+ if (ret < 0)
+ return ret;
+
+ stripped_fw = *fw;
+ ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
+ if (ret) {
+ dev_err(adev->dev, "invalid library data: %d\n", ret);
+ return ret;
+ }
+
+ ret = avs_fw_manifest_offset(&stripped_fw);
+ if (ret < 0)
+ return ret;
+ man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
+
+ /* Don't load anything that's already in DSP memory. */
+ for (j = 0; j < id; j++)
+ if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE))
+ goto next_lib;
+
+ ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
+ if (ret)
+ return ret;
+
+ strscpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
+ id++;
+next_lib:
+ i++;
+ }
+
+ return start == id ? 1 : 0;
+}
+
+static int avs_dsp_load_basefw(struct avs_dev *adev)
+{
+ const struct avs_fw_version *min_req;
+ const struct avs_spec *const spec = adev->spec;
+ const struct firmware *fw;
+ struct firmware stripped_fw;
+ char *filename;
+ int ret;
+
+ filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, spec->name, AVS_BASEFW_FILENAME);
+ if (!filename)
+ return -ENOMEM;
+
+ ret = avs_request_firmware(adev, &fw, filename);
+ kfree(filename);
+ if (ret < 0) {
+ dev_err(adev->dev, "request firmware failed: %d\n", ret);
+ return ret;
+ }
+
+ stripped_fw = *fw;
+ min_req = &adev->spec->min_fw_version;
+
+ ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, min_req);
+ if (ret < 0) {
+ dev_err(adev->dev, "invalid firmware data: %d\n", ret);
+ goto release_fw;
+ }
+
+ ret = avs_dsp_op(adev, load_basefw, &stripped_fw);
+ if (ret < 0) {
+ dev_err(adev->dev, "basefw load failed: %d\n", ret);
+ goto release_fw;
+ }
+
+ ret = wait_for_completion_timeout(&adev->fw_ready,
+ msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
+ if (!ret) {
+ dev_err(adev->dev, "firmware ready timeout, status: 0x%08x, lec: 0x%08x\n",
+ snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)),
+ snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)));
+ avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ ret = -ETIMEDOUT;
+ goto release_fw;
+ }
+
+ return 0;
+
+release_fw:
+ avs_release_last_firmware(adev);
+ return ret;
+}
+
+static int avs_load_firmware(struct avs_dev *adev, bool purge)
+{
+ struct avs_soc_component *acomp;
+ int ret, i;
+
+ /* Forgo full boot if flash from IMR succeeds. */
+ if (!purge && avs_platattr_test(adev, IMR)) {
+ ret = avs_imr_load_basefw(adev);
+ if (!ret)
+ return 0;
+
+ dev_dbg(adev->dev, "firmware flash from imr failed: %d\n", ret);
+ }
+
+ /* Full boot, clear cached data except for basefw (slot 0). */
+ for (i = 1; i < adev->fw_cfg.max_libs_count; i++)
+ memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE);
+
+ avs_hda_power_gating_enable(adev, false);
+ avs_hda_clock_gating_enable(adev, false);
+ avs_hda_l1sen_enable(adev, false);
+
+ ret = avs_dsp_load_basefw(adev);
+ if (ret)
+ goto reenable_gating;
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ struct avs_tplg *tplg = acomp->tplg;
+
+ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+ if (ret < 0)
+ break;
+ }
+ mutex_unlock(&adev->comp_list_mutex);
+
+reenable_gating:
+ avs_hda_l1sen_enable(adev, true);
+ avs_hda_clock_gating_enable(adev, true);
+ avs_hda_power_gating_enable(adev, true);
+
+ if (ret < 0)
+ return ret;
+
+ /* With all code loaded, refresh module information. */
+ ret = avs_module_info_init(adev, true);
+ if (ret) {
+ dev_err(adev->dev, "init module info failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_config_basefw(struct avs_dev *adev)
+{
+ int ret;
+
+ if (adev->spec->dsp_ops->config_basefw) {
+ ret = avs_dsp_op(adev, config_basefw);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
+{
+ int ret;
+
+ ret = avs_load_firmware(adev, purge);
+ if (ret)
+ return ret;
+
+ return avs_config_basefw(adev);
+}
+
+static int avs_dsp_alloc_resources(struct avs_dev *adev)
+{
+ struct hdac_ext_link *link;
+ int ret, i;
+
+ ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ ret = avs_ipc_get_fw_config(adev, &adev->fw_cfg);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ /* If hw allows, read capabilities directly from it. */
+ if (avs_platattr_test(adev, ALTHDA)) {
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core,
+ AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (link)
+ adev->hw_cfg.i2s_caps.ctrl_count = link->slcount;
+ }
+
+ adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores,
+ sizeof(*adev->core_refs), GFP_KERNEL);
+ adev->lib_names = devm_kcalloc(adev->dev, adev->fw_cfg.max_libs_count,
+ sizeof(*adev->lib_names), GFP_KERNEL);
+ if (!adev->core_refs || !adev->lib_names)
+ return -ENOMEM;
+
+ for (i = 0; i < adev->fw_cfg.max_libs_count; i++) {
+ adev->lib_names[i] = devm_kzalloc(adev->dev, AVS_LIB_NAME_SIZE, GFP_KERNEL);
+ if (!adev->lib_names[i])
+ return -ENOMEM;
+ }
+
+ /* basefw always occupies slot 0 */
+ strscpy(adev->lib_names[0], "BASEFW", AVS_LIB_NAME_SIZE);
+
+ ida_init(&adev->ppl_ida);
+ return 0;
+}
+
+int avs_dsp_first_boot_firmware(struct avs_dev *adev)
+{
+ int ret;
+
+ if (avs_platattr_test(adev, CLDMA)) {
+ ret = hda_cldma_init(&code_loader, &adev->base.core,
+ adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
+ if (ret < 0) {
+ dev_err(adev->dev, "cldma init failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_dsp_boot_firmware(adev, true);
+ if (ret < 0) {
+ dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+ return ret;
+ }
+
+ return avs_dsp_alloc_resources(adev);
+}
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
new file mode 100644
index 000000000000..a5ba27983091
--- /dev/null
+++ b/sound/soc/intel/avs/messages.c
@@ -0,0 +1,904 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+
+#define AVS_CL_TIMEOUT_MS 5000
+
+int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(ROM_CONTROL);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.boot_cfg.rom_ctrl_msg_type = AVS_ROM_SET_BOOT_CONFIG;
+ msg.boot_cfg.dma_id = dma_id;
+ msg.boot_cfg.purge_request = purge;
+ request.header = msg.val;
+
+ return avs_dsp_send_rom_msg(adev, &request, "set boot config");
+}
+
+int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES);
+ struct avs_ipc_msg request;
+
+ msg.load_multi_mods.mod_cnt = num_mod_ids;
+ request.header = msg.val;
+ request.data = mod_ids;
+ request.size = sizeof(*mod_ids) * num_mod_ids;
+
+ return avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS,
+ "load multiple modules");
+}
+
+int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(UNLOAD_MULTIPLE_MODULES);
+ struct avs_ipc_msg request;
+
+ msg.load_multi_mods.mod_cnt = num_mod_ids;
+ request.header = msg.val;
+ request.data = mod_ids;
+ request.size = sizeof(*mod_ids) * num_mod_ids;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "unload multiple modules");
+}
+
+int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.load_lib.dma_id = dma_id;
+ msg.load_lib.lib_id = lib_id;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS, "load library");
+}
+
+int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+ u8 instance_id, bool lp, u16 attributes)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.create_ppl.ppl_mem_size = req_size;
+ msg.create_ppl.ppl_priority = priority;
+ msg.create_ppl.instance_id = instance_id;
+ msg.ext.create_ppl.lp = lp;
+ msg.ext.create_ppl.attributes = attributes;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "create pipeline");
+}
+
+int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(DELETE_PIPELINE);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.ppl.instance_id = instance_id;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "delete pipeline");
+}
+
+int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
+ enum avs_pipeline_state state)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(SET_PIPELINE_STATE);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.set_ppl_state.ppl_id = instance_id;
+ msg.set_ppl_state.state = state;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "set pipeline state");
+}
+
+int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
+ enum avs_pipeline_state *state)
+{
+ union avs_global_msg msg = AVS_GLOBAL_REQUEST(GET_PIPELINE_STATE);
+ struct avs_ipc_msg request = {{0}};
+ struct avs_ipc_msg reply = {{0}};
+ int ret;
+
+ msg.get_ppl_state.ppl_id = instance_id;
+ request.header = msg.val;
+
+ ret = avs_dsp_send_msg(adev, &request, &reply, "get pipeline state");
+ if (!ret)
+ *state = reply.rsp.ext.get_ppl_state.state;
+ return ret;
+}
+
+/*
+ * avs_ipc_init_instance - Initialize module instance
+ *
+ * @adev: Driver context
+ * @module_id: Module-type id
+ * @instance_id: Unique module instance id
+ * @ppl_id: Parent pipeline id
+ * @core_id: DSP core to allocate module on
+ * @domain: Processing domain (low latency or data processing)
+ * @param: Module-type specific configuration
+ * @param_size: Size of @param in bytes
+ *
+ * Argument verification, as well as pipeline state checks are done by the
+ * firmware.
+ *
+ * Note: @ppl_id and @core_id are independent of each other as single pipeline
+ * can be composed of module instances located on different DSP cores.
+ */
+int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 ppl_id, u8 core_id, u8 domain,
+ void *param, u32 param_size)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
+ struct avs_ipc_msg request;
+
+ msg.module_id = module_id;
+ msg.instance_id = instance_id;
+ /* firmware expects size provided in dwords */
+ msg.ext.init_instance.param_block_size = DIV_ROUND_UP(param_size, sizeof(u32));
+ msg.ext.init_instance.ppl_instance_id = ppl_id;
+ msg.ext.init_instance.core_id = core_id;
+ msg.ext.init_instance.proc_domain = domain;
+
+ request.header = msg.val;
+ request.data = param;
+ request.size = param_size;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "init instance");
+}
+
+/*
+ * avs_ipc_delete_instance - Delete module instance
+ *
+ * @adev: Driver context
+ * @module_id: Module-type id
+ * @instance_id: Unique module instance id
+ *
+ * Argument verification, as well as pipeline state checks are done by the
+ * firmware.
+ *
+ * Note: only standalone modules i.e. without a parent pipeline shall be
+ * deleted using this IPC message. In all other cases, pipeline owning the
+ * modules performs cleanup automatically when it is deleted.
+ */
+int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.module_id = module_id;
+ msg.instance_id = instance_id;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "delete instance");
+}
+
+/*
+ * avs_ipc_bind - Bind two module instances
+ *
+ * @adev: Driver context
+ * @module_id: Source module-type id
+ * @instance_id: Source module instance id
+ * @dst_module_id: Sink module-type id
+ * @dst_instance_id: Sink module instance id
+ * @dst_queue: Sink module pin to bind @src_queue with
+ * @src_queue: Source module pin to bind @dst_queue with
+ */
+int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u16 dst_module_id, u8 dst_instance_id,
+ u8 dst_queue, u8 src_queue)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.module_id = module_id;
+ msg.instance_id = instance_id;
+ msg.ext.bind_unbind.dst_module_id = dst_module_id;
+ msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
+ msg.ext.bind_unbind.dst_queue = dst_queue;
+ msg.ext.bind_unbind.src_queue = src_queue;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "bind modules");
+}
+
+/*
+ * avs_ipc_unbind - Unbind two module instances
+ *
+ * @adev: Driver context
+ * @module_id: Source module-type id
+ * @instance_id: Source module instance id
+ * @dst_module_id: Sink module-type id
+ * @dst_instance_id: Sink module instance id
+ * @dst_queue: Sink module pin to unbind @src_queue from
+ * @src_queue: Source module pin to unbind @dst_queue from
+ */
+int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u16 dst_module_id, u8 dst_instance_id,
+ u8 dst_queue, u8 src_queue)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.module_id = module_id;
+ msg.instance_id = instance_id;
+ msg.ext.bind_unbind.dst_module_id = dst_module_id;
+ msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
+ msg.ext.bind_unbind.dst_queue = dst_queue;
+ msg.ext.bind_unbind.src_queue = src_queue;
+ request.header = msg.val;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "unbind modules");
+}
+
+static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 param_id, bool init_block, bool final_block,
+ u8 *request_data, size_t request_size, size_t off_size)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_SET);
+ struct avs_ipc_msg request;
+
+ msg.module_id = module_id;
+ msg.instance_id = instance_id;
+ msg.ext.large_config.data_off_size = off_size;
+ msg.ext.large_config.large_param_id = param_id;
+ msg.ext.large_config.final_block = final_block;
+ msg.ext.large_config.init_block = init_block;
+
+ request.header = msg.val;
+ request.data = request_data;
+ request.size = request_size;
+
+ return avs_dsp_send_msg(adev, &request, NULL, "large config set");
+}
+
+int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
+ u8 instance_id, u8 param_id,
+ u8 *request, size_t request_size)
+{
+ size_t remaining, tx_size;
+ bool final;
+ int ret;
+
+ remaining = request_size;
+ tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining);
+ final = (tx_size == remaining);
+
+ /* Initial request states total payload size. */
+ ret = __avs_ipc_set_large_config(adev, module_id, instance_id,
+ param_id, 1, final, request, tx_size,
+ request_size);
+ if (ret)
+ return ret;
+
+ remaining -= tx_size;
+
+ /* Loop the rest only when payload exceeds mailbox's size. */
+ while (remaining) {
+ size_t offset;
+
+ offset = request_size - remaining;
+ tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining);
+ final = (tx_size == remaining);
+
+ ret = __avs_ipc_set_large_config(adev, module_id, instance_id,
+ param_id, 0, final,
+ request + offset, tx_size,
+ offset);
+ if (ret)
+ return ret;
+
+ remaining -= tx_size;
+ }
+
+ return 0;
+}
+
+int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 param_id, u8 *request_data, size_t request_size,
+ u8 **reply_data, size_t *reply_size)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_GET);
+ struct avs_ipc_msg request;
+ struct avs_ipc_msg reply = {{0}};
+ void *buf;
+ int ret;
+
+ reply.data = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!reply.data)
+ return -ENOMEM;
+
+ msg.module_id = module_id;
+ msg.instance_id = instance_id;
+ msg.ext.large_config.data_off_size = request_size;
+ msg.ext.large_config.large_param_id = param_id;
+ /* final_block is always 0 on request. Updated by fw on reply. */
+ msg.ext.large_config.final_block = 0;
+ msg.ext.large_config.init_block = 1;
+
+ request.header = msg.val;
+ request.data = request_data;
+ request.size = request_size;
+ reply.size = AVS_MAILBOX_SIZE;
+
+ ret = avs_dsp_send_msg(adev, &request, &reply, "large config get");
+ if (ret) {
+ kfree(reply.data);
+ return ret;
+ }
+
+ buf = krealloc(reply.data, reply.size, GFP_KERNEL);
+ if (!buf) {
+ kfree(reply.data);
+ return -ENOMEM;
+ }
+
+ *reply_data = buf;
+ *reply_size = reply.size;
+
+ return 0;
+}
+
+int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX);
+ struct avs_ipc_msg request;
+ struct avs_dxstate_info dx;
+
+ dx.core_mask = core_mask;
+ dx.dx_mask = powerup ? core_mask : 0;
+ request.header = msg.val;
+ request.data = &dx;
+ request.size = sizeof(dx);
+
+ return avs_dsp_send_pm_msg(adev, &request, NULL, true, "set dx");
+}
+
+/*
+ * avs_ipc_set_d0ix - Set power gating policy (entering D0IX substates)
+ *
+ * @enable_pg: Whether to enable or disable power gating
+ * @streaming: Whether a stream is running when transitioning
+ */
+int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
+{
+ union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX);
+ struct avs_ipc_msg request = {{0}};
+
+ msg.ext.set_d0ix.wake = enable_pg;
+ msg.ext.set_d0ix.streaming = streaming;
+ msg.ext.set_d0ix.prevent_pg = !enable_pg;
+
+ request.header = msg.val;
+
+ return avs_dsp_send_pm_msg(adev, &request, NULL, false, "set d0ix");
+}
+
+int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg)
+{
+ struct avs_tlv *tlv;
+ size_t payload_size;
+ size_t offset = 0;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_FIRMWARE_CONFIG, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ goto err;
+ /* Non-zero payload expected for FIRMWARE_CONFIG. */
+ if (!payload_size) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ while (offset < payload_size) {
+ tlv = (struct avs_tlv *)(payload + offset);
+
+ switch (tlv->type) {
+ case AVS_FW_CFG_FW_VERSION:
+ memcpy(&cfg->fw_version, tlv->value, sizeof(cfg->fw_version));
+ break;
+
+ case AVS_FW_CFG_MEMORY_RECLAIMED:
+ cfg->memory_reclaimed = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ:
+ cfg->slow_clock_freq_hz = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_FAST_CLOCK_FREQ_HZ:
+ cfg->fast_clock_freq_hz = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_ALH_SUPPORT_LEVEL:
+ cfg->alh_support = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_IPC_DL_MAILBOX_BYTES:
+ cfg->ipc_dl_mailbox_bytes = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_IPC_UL_MAILBOX_BYTES:
+ cfg->ipc_ul_mailbox_bytes = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_TRACE_LOG_BYTES:
+ cfg->trace_log_bytes = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_PPL_COUNT:
+ cfg->max_ppl_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_ASTATE_COUNT:
+ cfg->max_astate_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_MODULE_PIN_COUNT:
+ cfg->max_module_pin_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MODULES_COUNT:
+ cfg->modules_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_MOD_INST_COUNT:
+ cfg->max_mod_inst_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT:
+ cfg->max_ll_tasks_per_pri_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_LL_PRI_COUNT:
+ cfg->ll_pri_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_DP_TASKS_COUNT:
+ cfg->max_dp_tasks_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_MAX_LIBS_COUNT:
+ cfg->max_libs_count = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_XTAL_FREQ_HZ:
+ cfg->xtal_freq_hz = *tlv->value;
+ break;
+
+ case AVS_FW_CFG_POWER_GATING_POLICY:
+ cfg->power_gating_policy = *tlv->value;
+ break;
+
+ /* Known but not useful to us. */
+ case AVS_FW_CFG_DMA_BUFFER_CONFIG:
+ case AVS_FW_CFG_SCHEDULER_CONFIG:
+ case AVS_FW_CFG_CLOCKS_CONFIG:
+ case AVS_FW_CFG_RESERVED:
+ break;
+
+ default:
+ dev_info(adev->dev, "Unrecognized fw param: %d\n", tlv->type);
+ break;
+ }
+
+ offset += sizeof(*tlv) + tlv->length;
+ }
+
+ /* No longer needed, free it as it's owned by the get_large_config() caller. */
+ kfree(payload);
+err:
+ if (ret)
+ dev_err(adev->dev, "get fw cfg failed: %d\n", ret);
+ return ret;
+}
+
+int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...)
+{
+ struct avs_tlv *tlv;
+ void *payload;
+ size_t offset;
+ va_list args;
+ int ret, i;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ va_start(args, num_tlvs);
+ for (offset = i = 0; i < num_tlvs && offset < AVS_MAILBOX_SIZE - sizeof(*tlv); i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+ tlv->type = va_arg(args, u32);
+ tlv->length = va_arg(args, u32);
+
+ offset += sizeof(*tlv) + tlv->length;
+ if (offset > AVS_MAILBOX_SIZE)
+ break;
+
+ memcpy(tlv->value, va_arg(args, u8*), tlv->length);
+ }
+
+ if (i == num_tlvs)
+ ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_FIRMWARE_CONFIG, payload, offset);
+ else
+ ret = -ERANGE;
+
+ va_end(args);
+ kfree(payload);
+ if (ret)
+ dev_err(adev->dev, "set fw cfg failed: %d\n", ret);
+ return ret;
+}
+
+int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg)
+{
+ struct avs_tlv *tlv;
+ size_t payload_size;
+ size_t size, offset = 0;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_HARDWARE_CONFIG, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ goto err;
+ /* Non-zero payload expected for HARDWARE_CONFIG. */
+ if (!payload_size) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ while (offset < payload_size) {
+ tlv = (struct avs_tlv *)(payload + offset);
+
+ switch (tlv->type) {
+ case AVS_HW_CFG_AVS_VER:
+ cfg->avs_version = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_DSP_CORES:
+ cfg->dsp_cores = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_MEM_PAGE_BYTES:
+ cfg->mem_page_bytes = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES:
+ cfg->total_phys_mem_pages = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_I2S_CAPS:
+ cfg->i2s_caps.i2s_version = tlv->value[0];
+ size = tlv->value[1];
+ cfg->i2s_caps.ctrl_count = size;
+ if (!size)
+ break;
+
+ /* Multiply to get entire array size. */
+ size *= sizeof(*cfg->i2s_caps.ctrl_base_addr);
+ cfg->i2s_caps.ctrl_base_addr = devm_kmemdup(adev->dev,
+ &tlv->value[2],
+ size, GFP_KERNEL);
+ if (!cfg->i2s_caps.ctrl_base_addr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ break;
+
+ case AVS_HW_CFG_GATEWAY_COUNT:
+ cfg->gateway_count = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_HP_EBB_COUNT:
+ cfg->hp_ebb_count = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_LP_EBB_COUNT:
+ cfg->lp_ebb_count = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_EBB_SIZE_BYTES:
+ cfg->ebb_size_bytes = *tlv->value;
+ break;
+
+ case AVS_HW_CFG_GPDMA_CAPS:
+ break;
+
+ default:
+ dev_info(adev->dev, "Unrecognized hw config: %d\n", tlv->type);
+ break;
+ }
+
+ offset += sizeof(*tlv) + tlv->length;
+ }
+
+exit:
+ /* No longer needed, free it as it's owned by the get_large_config() caller. */
+ kfree(payload);
+err:
+ if (ret)
+ dev_err(adev->dev, "get hw cfg failed: %d\n", ret);
+ return ret;
+}
+
+int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
+{
+ size_t payload_size;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_MODULES_INFO, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ return ret;
+ /* Non-zero payload expected for MODULES_INFO. */
+ if (!payload_size)
+ return -EREMOTEIO;
+
+ *info = (struct avs_mods_info *)payload;
+ return 0;
+}
+
+int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
+ u8 instance_id, u32 sink_id,
+ const struct avs_audio_format *src_fmt,
+ const struct avs_audio_format *sink_fmt)
+{
+ struct avs_copier_sink_format cpr_fmt;
+
+ cpr_fmt.sink_id = sink_id;
+ /* Firmware expects driver to resend copier's input format. */
+ cpr_fmt.src_fmt = *src_fmt;
+ cpr_fmt.sink_fmt = *sink_fmt;
+
+ return avs_ipc_set_large_config(adev, module_id, instance_id,
+ AVS_COPIER_SET_SINK_FORMAT,
+ (u8 *)&cpr_fmt, sizeof(cpr_fmt));
+}
+
+int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg **vols, size_t *num_vols)
+{
+ size_t payload_size;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ /* Non-zero payload expected for PEAKVOL_VOLUME. */
+ if (!payload_size)
+ return -EREMOTEIO;
+
+ *vols = (struct avs_volume_cfg *)payload;
+ *num_vols = payload_size / sizeof(**vols);
+
+ return 0;
+}
+
+int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vol)
+{
+ return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME,
+ (u8 *)vol, sizeof(*vol));
+}
+
+int avs_ipc_peakvol_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vols, size_t num_vols)
+{
+ struct avs_tlv *tlv;
+ size_t offset;
+ size_t size;
+ u8 *payload;
+ int ret, i;
+
+ size = num_vols * sizeof(*vols);
+ size += num_vols * sizeof(*tlv);
+ if (size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ for (offset = i = 0; i < num_vols; i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+
+ tlv->type = AVS_PEAKVOL_VOLUME;
+ tlv->length = sizeof(*vols);
+ memcpy(tlv->value, &vols[i], tlv->length);
+
+ offset += sizeof(*tlv) + tlv->length;
+ }
+
+ ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload,
+ size);
+ kfree(payload);
+ return ret;
+}
+
+int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg **mutes, size_t *num_mutes)
+{
+ size_t payload_size;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ /* Non-zero payload expected for PEAKVOL_MUTE. */
+ if (!payload_size)
+ return -EREMOTEIO;
+
+ *mutes = (struct avs_mute_cfg *)payload;
+ *num_mutes = payload_size / sizeof(**mutes);
+
+ return 0;
+}
+
+int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mute)
+{
+ return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE,
+ (u8 *)mute, sizeof(*mute));
+}
+
+int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mutes, size_t num_mutes)
+{
+ struct avs_tlv *tlv;
+ size_t offset;
+ size_t size;
+ u8 *payload;
+ int ret, i;
+
+ size = num_mutes * sizeof(*mutes);
+ size += num_mutes * sizeof(*tlv);
+ if (size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ for (offset = i = 0; i < num_mutes; i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+
+ tlv->type = AVS_PEAKVOL_MUTE;
+ tlv->length = sizeof(*mutes);
+ memcpy(tlv->value, &mutes[i], tlv->length);
+
+ offset += sizeof(*tlv) + tlv->length;
+ }
+
+ ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload,
+ size);
+ kfree(payload);
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
+{
+ return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_ENABLE_LOGS, log_info, size);
+}
+
+int avs_ipc_set_system_time(struct avs_dev *adev)
+{
+ struct avs_sys_time sys_time;
+ u64 us;
+
+ /* firmware expects UTC time in micro seconds */
+ us = ktime_to_us(ktime_get());
+ sys_time.val_l = us & UINT_MAX;
+ sys_time.val_u = us >> 32;
+
+ return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
+}
+
+int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas)
+{
+ size_t payload_size;
+ u32 module_id;
+ u8 *payload;
+ int ret;
+
+ module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
+ NULL, 0, &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ *dmas = (struct avs_probe_dma *)payload;
+ *num_dmas = payload_size / sizeof(**dmas);
+
+ return 0;
+}
+
+int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
+ (u8 *)dmas, array_size(sizeof(*dmas), num_dmas));
+}
+
+int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids,
+ size_t num_node_ids)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID,
+ AVS_PROBE_INJECTION_DMA_DETACH, (u8 *)node_ids,
+ array_size(sizeof(*node_ids), num_node_ids));
+}
+
+int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs,
+ size_t *num_descs)
+{
+ size_t payload_size;
+ u32 module_id;
+ u8 *payload;
+ int ret;
+
+ module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, NULL,
+ 0, &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ *descs = (struct avs_probe_point_desc *)payload;
+ *num_descs = payload_size / sizeof(**descs);
+
+ return 0;
+}
+
+int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs,
+ size_t num_descs)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS,
+ (u8 *)descs, array_size(sizeof(*descs), num_descs));
+}
+
+int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids,
+ size_t num_ids)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID,
+ AVS_PROBE_POINTS_DISCONNECT, (u8 *)ids,
+ array_size(sizeof(*ids), num_ids));
+}
+#endif
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
new file mode 100644
index 000000000000..55c04b0142ae
--- /dev/null
+++ b/sound/soc/intel/avs/messages.h
@@ -0,0 +1,1035 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_MSGS_H
+#define __SOUND_SOC_INTEL_AVS_MSGS_H
+
+#include <linux/sizes.h>
+
+struct avs_dev;
+
+#define AVS_MAILBOX_SIZE SZ_4K
+
+enum avs_msg_target {
+ AVS_FW_GEN_MSG = 0,
+ AVS_MOD_MSG = 1
+};
+
+enum avs_msg_direction {
+ AVS_MSG_REQUEST = 0,
+ AVS_MSG_REPLY = 1
+};
+
+enum avs_global_msg_type {
+ AVS_GLB_ROM_CONTROL = 1,
+ AVS_GLB_LOAD_MULTIPLE_MODULES = 15,
+ AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16,
+ AVS_GLB_CREATE_PIPELINE = 17,
+ AVS_GLB_DELETE_PIPELINE = 18,
+ AVS_GLB_SET_PIPELINE_STATE = 19,
+ AVS_GLB_GET_PIPELINE_STATE = 20,
+ AVS_GLB_LOAD_LIBRARY = 24,
+ AVS_GLB_NOTIFICATION = 27,
+};
+
+union avs_global_msg {
+ u64 val;
+ struct {
+ union {
+ u32 primary;
+ struct {
+ u32 rsvd:24;
+ u32 global_msg_type:5;
+ u32 msg_direction:1;
+ u32 msg_target:1;
+ };
+ /* set boot config */
+ struct {
+ u32 rom_ctrl_msg_type:9;
+ u32 dma_id:5;
+ u32 purge_request:1;
+ } boot_cfg;
+ /* module loading */
+ struct {
+ u32 mod_cnt:8;
+ } load_multi_mods;
+ /* pipeline management */
+ struct {
+ u32 ppl_mem_size:11;
+ u32 ppl_priority:5;
+ u32 instance_id:8;
+ } create_ppl;
+ struct {
+ u32 rsvd:16;
+ u32 instance_id:8;
+ } ppl; /* generic ppl request */
+ struct {
+ u32 state:16;
+ u32 ppl_id:8;
+ } set_ppl_state;
+ struct {
+ u32 ppl_id:8;
+ } get_ppl_state;
+ /* library loading */
+ struct {
+ u32 dma_id:5;
+ u32 rsvd:11;
+ u32 lib_id:4;
+ } load_lib;
+ };
+ union {
+ u32 val;
+ /* pipeline management */
+ struct {
+ u32 lp:1; /* low power flag */
+ u32 rsvd:3;
+ u32 attributes:16; /* additional scheduling flags */
+ } create_ppl;
+ } ext;
+ };
+} __packed;
+static_assert(sizeof(union avs_global_msg) == 8);
+
+struct avs_tlv {
+ u32 type;
+ u32 length;
+ u32 value[];
+} __packed;
+static_assert(sizeof(struct avs_tlv) == 8);
+
+#define avs_tlv_size(tlv) struct_size(tlv, value, (tlv)->length / 4)
+
+enum avs_module_msg_type {
+ AVS_MOD_INIT_INSTANCE = 0,
+ AVS_MOD_LARGE_CONFIG_GET = 3,
+ AVS_MOD_LARGE_CONFIG_SET = 4,
+ AVS_MOD_BIND = 5,
+ AVS_MOD_UNBIND = 6,
+ AVS_MOD_SET_DX = 7,
+ AVS_MOD_SET_D0IX = 8,
+ AVS_MOD_DELETE_INSTANCE = 11,
+};
+
+union avs_module_msg {
+ u64 val;
+ struct {
+ union {
+ u32 primary;
+ struct {
+ u32 module_id:16;
+ u32 instance_id:8;
+ u32 module_msg_type:5;
+ u32 msg_direction:1;
+ u32 msg_target:1;
+ };
+ };
+ union {
+ u32 val;
+ struct {
+ u32 param_block_size:16;
+ u32 ppl_instance_id:8;
+ u32 core_id:4;
+ u32 proc_domain:1;
+ } init_instance;
+ struct {
+ u32 data_off_size:20;
+ u32 large_param_id:8;
+ u32 final_block:1;
+ u32 init_block:1;
+ } large_config;
+ struct {
+ u32 dst_module_id:16;
+ u32 dst_instance_id:8;
+ u32 dst_queue:3;
+ u32 src_queue:3;
+ } bind_unbind;
+ struct {
+ /* pre-IceLake */
+ u32 wake:1;
+ u32 streaming:1;
+ /* IceLake and onwards */
+ u32 prevent_pg:1;
+ u32 prevent_local_cg:1;
+ } set_d0ix;
+ } ext;
+ };
+} __packed;
+static_assert(sizeof(union avs_module_msg) == 8);
+
+#define AVS_IPC_NOT_SUPPORTED 15
+
+union avs_reply_msg {
+ u64 val;
+ struct {
+ union {
+ u32 primary;
+ struct {
+ u32 status:24;
+ u32 global_msg_type:5;
+ u32 msg_direction:1;
+ u32 msg_target:1;
+ };
+ };
+ union {
+ u32 val;
+ /* module loading */
+ struct {
+ u32 err_mod_id:16;
+ } load_multi_mods;
+ /* pipeline management */
+ struct {
+ u32 state:5;
+ } get_ppl_state;
+ /* module management */
+ struct {
+ u32 data_off_size:20;
+ u32 large_param_id:8;
+ u32 final_block:1;
+ u32 init_block:1;
+ } large_config;
+ } ext;
+ };
+} __packed;
+static_assert(sizeof(union avs_reply_msg) == 8);
+
+enum avs_notify_msg_type {
+ AVS_NOTIFY_PHRASE_DETECTED = 4,
+ AVS_NOTIFY_RESOURCE_EVENT = 5,
+ AVS_NOTIFY_LOG_BUFFER_STATUS = 6,
+ AVS_NOTIFY_FW_READY = 8,
+ AVS_NOTIFY_EXCEPTION_CAUGHT = 10,
+ AVS_NOTIFY_MODULE_EVENT = 12,
+};
+
+union avs_notify_msg {
+ u64 val;
+ struct {
+ union {
+ u32 primary;
+ struct {
+ u32 rsvd:16;
+ u32 notify_msg_type:8;
+ u32 global_msg_type:5;
+ u32 msg_direction:1;
+ u32 msg_target:1;
+ };
+ struct {
+ u16 rsvd:12;
+ u16 core:4;
+ } log;
+ };
+ union {
+ u32 val;
+ struct {
+ u32 core_id:2;
+ u32 stack_dump_size:16;
+ } coredump;
+ } ext;
+ };
+} __packed;
+static_assert(sizeof(union avs_notify_msg) == 8);
+
+#define AVS_MSG(hdr) { .val = hdr }
+
+#define AVS_GLOBAL_REQUEST(msg_type) \
+{ \
+ .global_msg_type = AVS_GLB_##msg_type, \
+ .msg_direction = AVS_MSG_REQUEST, \
+ .msg_target = AVS_FW_GEN_MSG, \
+}
+
+#define AVS_MODULE_REQUEST(msg_type) \
+{ \
+ .module_msg_type = AVS_MOD_##msg_type, \
+ .msg_direction = AVS_MSG_REQUEST, \
+ .msg_target = AVS_MOD_MSG, \
+}
+
+#define AVS_NOTIFICATION(msg_type) \
+{ \
+ .notify_msg_type = AVS_NOTIFY_##msg_type,\
+ .global_msg_type = AVS_GLB_NOTIFICATION,\
+ .msg_direction = AVS_MSG_REPLY, \
+ .msg_target = AVS_FW_GEN_MSG, \
+}
+
+#define avs_msg_is_reply(hdr) \
+({ \
+ union avs_reply_msg __msg = AVS_MSG(hdr); \
+ __msg.msg_direction == AVS_MSG_REPLY && \
+ __msg.global_msg_type != AVS_GLB_NOTIFICATION; \
+})
+
+/* Notification types */
+
+struct avs_notify_voice_data {
+ u16 kpd_score;
+ u16 reserved;
+} __packed;
+static_assert(sizeof(struct avs_notify_voice_data) == 4);
+
+struct avs_notify_res_data {
+ u32 resource_type;
+ u32 resource_id;
+ u32 event_type;
+ u32 reserved;
+ u32 data[6];
+} __packed;
+static_assert(sizeof(struct avs_notify_res_data) == 40);
+
+struct avs_notify_mod_data {
+ u32 module_instance_id;
+ u32 event_id;
+ u32 data_size;
+ u32 data[];
+} __packed;
+static_assert(sizeof(struct avs_notify_mod_data) == 12);
+
+/* ROM messages */
+enum avs_rom_control_msg_type {
+ AVS_ROM_SET_BOOT_CONFIG = 0,
+};
+
+int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge);
+
+/* Code loading messages */
+int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
+int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
+int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id);
+
+/* Pipeline management messages */
+enum avs_pipeline_state {
+ AVS_PPL_STATE_INVALID,
+ AVS_PPL_STATE_UNINITIALIZED,
+ AVS_PPL_STATE_RESET,
+ AVS_PPL_STATE_PAUSED,
+ AVS_PPL_STATE_RUNNING,
+};
+
+int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+ u8 instance_id, bool lp, u16 attributes);
+int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id);
+int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
+ enum avs_pipeline_state state);
+int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
+ enum avs_pipeline_state *state);
+
+/* Module management messages */
+int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 ppl_id, u8 core_id, u8 domain,
+ void *param, u32 param_size);
+int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id);
+int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u16 dst_module_id, u8 dst_instance_id,
+ u8 dst_queue, u8 src_queue);
+int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u16 dst_module_id, u8 dst_instance_id,
+ u8 dst_queue, u8 src_queue);
+int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
+ u8 instance_id, u8 param_id,
+ u8 *request, size_t request_size);
+int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ u8 param_id, u8 *request_data, size_t request_size,
+ u8 **reply_data, size_t *reply_size);
+
+/* DSP cores and domains power management messages */
+struct avs_dxstate_info {
+ u32 core_mask; /* which cores are subject for power transition */
+ u32 dx_mask; /* bit[n]=1 core n goes to D0, bit[n]=0 it goes to D3 */
+} __packed;
+static_assert(sizeof(struct avs_dxstate_info) == 8);
+
+int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup);
+int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
+
+/* Base-firmware runtime parameters */
+
+#define AVS_BASEFW_MOD_ID 0
+#define AVS_BASEFW_INST_ID 0
+
+enum avs_basefw_runtime_param {
+ AVS_BASEFW_ENABLE_LOGS = 6,
+ AVS_BASEFW_FIRMWARE_CONFIG = 7,
+ AVS_BASEFW_HARDWARE_CONFIG = 8,
+ AVS_BASEFW_MODULES_INFO = 9,
+ AVS_BASEFW_LIBRARIES_INFO = 16,
+ AVS_BASEFW_SYSTEM_TIME = 20,
+};
+
+enum avs_log_enable {
+ AVS_LOG_DISABLE = 0,
+ AVS_LOG_ENABLE = 1
+};
+
+enum avs_skl_log_priority {
+ AVS_SKL_LOG_CRITICAL = 1,
+ AVS_SKL_LOG_HIGH,
+ AVS_SKL_LOG_MEDIUM,
+ AVS_SKL_LOG_LOW,
+ AVS_SKL_LOG_VERBOSE,
+};
+
+struct avs_skl_log_state {
+ u32 enable;
+ u32 min_priority;
+} __packed;
+static_assert(sizeof(struct avs_skl_log_state) == 8);
+
+struct avs_skl_log_state_info {
+ u32 core_mask;
+ struct avs_skl_log_state logs_core[];
+} __packed;
+static_assert(sizeof(struct avs_skl_log_state_info) == 4);
+
+struct avs_apl_log_state_info {
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 core_mask;
+ struct avs_skl_log_state logs_core[];
+} __packed;
+static_assert(sizeof(struct avs_apl_log_state_info) == 12);
+
+enum avs_icl_log_priority {
+ AVS_ICL_LOG_CRITICAL = 0,
+ AVS_ICL_LOG_HIGH,
+ AVS_ICL_LOG_MEDIUM,
+ AVS_ICL_LOG_LOW,
+ AVS_ICL_LOG_VERBOSE,
+};
+
+enum avs_icl_log_source {
+ AVS_ICL_LOG_INFRA = 0,
+ AVS_ICL_LOG_HAL,
+ AVS_ICL_LOG_MODULE,
+ AVS_ICL_LOG_AUDIO,
+ AVS_ICL_LOG_SENSING,
+ AVS_ICL_LOG_ULP_INFRA,
+};
+
+struct avs_icl_log_state_info {
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 enable;
+ u32 logs_priorities_mask[];
+} __packed;
+static_assert(sizeof(struct avs_icl_log_state_info) == 12);
+
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
+
+struct avs_fw_version {
+ u16 major;
+ u16 minor;
+ u16 hotfix;
+ u16 build;
+};
+
+enum avs_fw_cfg_params {
+ AVS_FW_CFG_FW_VERSION = 0,
+ AVS_FW_CFG_MEMORY_RECLAIMED,
+ AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ,
+ AVS_FW_CFG_FAST_CLOCK_FREQ_HZ,
+ AVS_FW_CFG_DMA_BUFFER_CONFIG,
+ AVS_FW_CFG_ALH_SUPPORT_LEVEL,
+ AVS_FW_CFG_IPC_DL_MAILBOX_BYTES,
+ AVS_FW_CFG_IPC_UL_MAILBOX_BYTES,
+ AVS_FW_CFG_TRACE_LOG_BYTES,
+ AVS_FW_CFG_MAX_PPL_COUNT,
+ AVS_FW_CFG_MAX_ASTATE_COUNT,
+ AVS_FW_CFG_MAX_MODULE_PIN_COUNT,
+ AVS_FW_CFG_MODULES_COUNT,
+ AVS_FW_CFG_MAX_MOD_INST_COUNT,
+ AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT,
+ AVS_FW_CFG_LL_PRI_COUNT,
+ AVS_FW_CFG_MAX_DP_TASKS_COUNT,
+ AVS_FW_CFG_MAX_LIBS_COUNT,
+ AVS_FW_CFG_SCHEDULER_CONFIG,
+ AVS_FW_CFG_XTAL_FREQ_HZ,
+ AVS_FW_CFG_CLOCKS_CONFIG,
+ AVS_FW_CFG_RESERVED,
+ AVS_FW_CFG_POWER_GATING_POLICY,
+ AVS_FW_CFG_ASSERT_MODE,
+ AVS_FW_CFG_RESERVED2,
+ AVS_FW_CFG_BUS_HARDWARE_ID,
+};
+
+struct avs_fw_cfg {
+ struct avs_fw_version fw_version;
+ u32 memory_reclaimed;
+ u32 slow_clock_freq_hz;
+ u32 fast_clock_freq_hz;
+ u32 alh_support;
+ u32 ipc_dl_mailbox_bytes;
+ u32 ipc_ul_mailbox_bytes;
+ u32 trace_log_bytes;
+ u32 max_ppl_count;
+ u32 max_astate_count;
+ u32 max_module_pin_count;
+ u32 modules_count;
+ u32 max_mod_inst_count;
+ u32 max_ll_tasks_per_pri_count;
+ u32 ll_pri_count;
+ u32 max_dp_tasks_count;
+ u32 max_libs_count;
+ u32 xtal_freq_hz;
+ u32 power_gating_policy;
+};
+
+struct avs_bus_hwid {
+ u32 device;
+ u32 subsystem;
+ u8 revision;
+};
+
+int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg);
+int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...);
+
+enum avs_hw_cfg_params {
+ AVS_HW_CFG_AVS_VER,
+ AVS_HW_CFG_DSP_CORES,
+ AVS_HW_CFG_MEM_PAGE_BYTES,
+ AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES,
+ AVS_HW_CFG_I2S_CAPS,
+ AVS_HW_CFG_GPDMA_CAPS,
+ AVS_HW_CFG_GATEWAY_COUNT,
+ AVS_HW_CFG_HP_EBB_COUNT,
+ AVS_HW_CFG_LP_EBB_COUNT,
+ AVS_HW_CFG_EBB_SIZE_BYTES,
+};
+
+enum avs_iface_version {
+ AVS_AVS_VER_1_5 = 0x10005,
+ AVS_AVS_VER_1_8 = 0x10008,
+};
+
+enum avs_i2s_version {
+ AVS_I2S_VER_15_SKYLAKE = 0x00000,
+ AVS_I2S_VER_15_BROXTON = 0x10000,
+ AVS_I2S_VER_15_BROXTON_P = 0x20000,
+ AVS_I2S_VER_18_KBL_CNL = 0x30000,
+};
+
+struct avs_i2s_caps {
+ u32 i2s_version;
+ u32 ctrl_count;
+ u32 *ctrl_base_addr;
+};
+
+struct avs_hw_cfg {
+ u32 avs_version;
+ u32 dsp_cores;
+ u32 mem_page_bytes;
+ u32 total_phys_mem_pages;
+ struct avs_i2s_caps i2s_caps;
+ u32 gateway_count;
+ u32 hp_ebb_count;
+ u32 lp_ebb_count;
+ u32 ebb_size_bytes;
+};
+
+int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg);
+
+#define AVS_MODULE_LOAD_TYPE_BUILTIN 0
+#define AVS_MODULE_LOAD_TYPE_LOADABLE 1
+#define AVS_MODULE_STATE_LOADED BIT(0)
+
+struct avs_module_type {
+ u32 load_type:4;
+ u32 auto_start:1;
+ u32 domain_ll:1;
+ u32 domain_dp:1;
+ u32 lib_code:1;
+ u32 rsvd:24;
+} __packed;
+static_assert(sizeof(struct avs_module_type) == 4);
+
+union avs_segment_flags {
+ u32 ul;
+ struct {
+ u32 contents:1;
+ u32 alloc:1;
+ u32 load:1;
+ u32 readonly:1;
+ u32 code:1;
+ u32 data:1;
+ u32 rsvd_1:2;
+ u32 type:4;
+ u32 rsvd_2:4;
+ u32 length:16;
+ };
+} __packed;
+static_assert(sizeof(union avs_segment_flags) == 4);
+
+struct avs_segment_desc {
+ union avs_segment_flags flags;
+ u32 v_base_addr;
+ u32 file_offset;
+} __packed;
+static_assert(sizeof(struct avs_segment_desc) == 12);
+
+struct avs_module_entry {
+ u16 module_id;
+ u16 state_flags;
+ u8 name[8];
+ guid_t uuid;
+ struct avs_module_type type;
+ u8 hash[32];
+ u32 entry_point;
+ u16 cfg_offset;
+ u16 cfg_count;
+ u32 affinity_mask;
+ u16 instance_max_count;
+ u16 instance_bss_size;
+ struct avs_segment_desc segments[3];
+} __packed;
+static_assert(sizeof(struct avs_module_entry) == 116);
+
+struct avs_mods_info {
+ u32 count;
+ struct avs_module_entry entries[];
+} __packed;
+static_assert(sizeof(struct avs_mods_info) == 4);
+
+static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry)
+{
+ return mentry->type.load_type == AVS_MODULE_LOAD_TYPE_BUILTIN ||
+ mentry->state_flags & AVS_MODULE_STATE_LOADED;
+}
+
+int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info);
+
+struct avs_sys_time {
+ u32 val_l;
+ u32 val_u;
+} __packed;
+static_assert(sizeof(struct avs_sys_time) == 8);
+
+int avs_ipc_set_system_time(struct avs_dev *adev);
+
+/* Module configuration */
+
+#define AVS_MIXIN_MOD_UUID \
+ GUID_INIT(0x39656EB2, 0x3B71, 0x4049, 0x8D, 0x3F, 0xF9, 0x2C, 0xD5, 0xC4, 0x3C, 0x09)
+
+#define AVS_MIXOUT_MOD_UUID \
+ GUID_INIT(0x3C56505A, 0x24D7, 0x418F, 0xBD, 0xDC, 0xC1, 0xF5, 0xA3, 0xAC, 0x2A, 0xE0)
+
+#define AVS_COPIER_MOD_UUID \
+ GUID_INIT(0x9BA00C83, 0xCA12, 0x4A83, 0x94, 0x3C, 0x1F, 0xA2, 0xE8, 0x2F, 0x9D, 0xDA)
+
+#define AVS_PEAKVOL_MOD_UUID \
+ GUID_INIT(0x8A171323, 0x94A3, 0x4E1D, 0xAF, 0xE9, 0xFE, 0x5D, 0xBA, 0xa4, 0xC3, 0x93)
+
+#define AVS_GAIN_MOD_UUID \
+ GUID_INIT(0x61BCA9A8, 0x18D0, 0x4A18, 0x8E, 0x7B, 0x26, 0x39, 0x21, 0x98, 0x04, 0xB7)
+
+#define AVS_KPBUFF_MOD_UUID \
+ GUID_INIT(0xA8A0CB32, 0x4A77, 0x4DB1, 0x85, 0xC7, 0x53, 0xD7, 0xEE, 0x07, 0xBC, 0xE6)
+
+#define AVS_MICSEL_MOD_UUID \
+ GUID_INIT(0x32FE92C1, 0x1E17, 0x4FC2, 0x97, 0x58, 0xC7, 0xF3, 0x54, 0x2E, 0x98, 0x0A)
+
+#define AVS_MUX_MOD_UUID \
+ GUID_INIT(0x64CE6E35, 0x857A, 0x4878, 0xAC, 0xE8, 0xE2, 0xA2, 0xF4, 0x2e, 0x30, 0x69)
+
+#define AVS_UPDWMIX_MOD_UUID \
+ GUID_INIT(0x42F8060C, 0x832F, 0x4DBF, 0xB2, 0x47, 0x51, 0xE9, 0x61, 0x99, 0x7b, 0x35)
+
+#define AVS_SRCINTC_MOD_UUID \
+ GUID_INIT(0xE61BB28D, 0x149A, 0x4C1F, 0xB7, 0x09, 0x46, 0x82, 0x3E, 0xF5, 0xF5, 0xAE)
+
+#define AVS_PROBE_MOD_UUID \
+ GUID_INIT(0x7CAD0808, 0xAB10, 0xCD23, 0xEF, 0x45, 0x12, 0xAB, 0x34, 0xCD, 0x56, 0xEF)
+
+#define AVS_AEC_MOD_UUID \
+ GUID_INIT(0x46CB87FB, 0xD2C9, 0x4970, 0x96, 0xD2, 0x6D, 0x7E, 0x61, 0x4B, 0xB6, 0x05)
+
+#define AVS_ASRC_MOD_UUID \
+ GUID_INIT(0x66B4402D, 0xB468, 0x42F2, 0x81, 0xA7, 0xB3, 0x71, 0x21, 0x86, 0x3D, 0xD4)
+
+#define AVS_INTELWOV_MOD_UUID \
+ GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7)
+
+#define AVS_WOVHOSTM_MOD_UUID \
+ GUID_INIT(0xF9ED62B7, 0x092E, 0x4A90, 0x8F, 0x4D, 0x82, 0xDA, 0xA8, 0xB3, 0x8F, 0x3B)
+
+/* channel map */
+enum avs_channel_index {
+ AVS_CHANNEL_LEFT = 0,
+ AVS_CHANNEL_RIGHT = 1,
+ AVS_CHANNEL_CENTER = 2,
+ AVS_CHANNEL_LEFT_SURROUND = 3,
+ AVS_CHANNEL_CENTER_SURROUND = 3,
+ AVS_CHANNEL_RIGHT_SURROUND = 4,
+ AVS_CHANNEL_LFE = 7,
+ AVS_CHANNEL_INVALID = 0xF,
+};
+
+enum avs_channel_config {
+ AVS_CHANNEL_CONFIG_MONO = 0,
+ AVS_CHANNEL_CONFIG_STEREO = 1,
+ AVS_CHANNEL_CONFIG_2_1 = 2,
+ AVS_CHANNEL_CONFIG_3_0 = 3,
+ AVS_CHANNEL_CONFIG_3_1 = 4,
+ AVS_CHANNEL_CONFIG_QUATRO = 5,
+ AVS_CHANNEL_CONFIG_4_0 = 6,
+ AVS_CHANNEL_CONFIG_5_0 = 7,
+ AVS_CHANNEL_CONFIG_5_1 = 8,
+ AVS_CHANNEL_CONFIG_DUAL_MONO = 9,
+ AVS_CHANNEL_CONFIG_I2S_DUAL_STEREO_0 = 10,
+ AVS_CHANNEL_CONFIG_I2S_DUAL_STEREO_1 = 11,
+ AVS_CHANNEL_CONFIG_7_1 = 12,
+ AVS_CHANNEL_CONFIG_INVALID
+};
+
+enum avs_interleaving {
+ AVS_INTERLEAVING_PER_CHANNEL = 0,
+ AVS_INTERLEAVING_PER_SAMPLE = 1,
+};
+
+enum avs_sample_type {
+ AVS_SAMPLE_TYPE_INT_MSB = 0,
+ AVS_SAMPLE_TYPE_INT_LSB = 1,
+ AVS_SAMPLE_TYPE_INT_SIGNED = 2,
+ AVS_SAMPLE_TYPE_INT_UNSIGNED = 3,
+ AVS_SAMPLE_TYPE_FLOAT = 4,
+};
+
+#define AVS_COEFF_CHANNELS_MAX 8
+#define AVS_ALL_CHANNELS_MASK UINT_MAX
+#define AVS_CHANNELS_MAX 16
+
+struct avs_audio_format {
+ u32 sampling_freq;
+ u32 bit_depth;
+ u32 channel_map;
+ u32 channel_config;
+ u32 interleaving;
+ u32 num_channels:8;
+ u32 valid_bit_depth:8;
+ u32 sample_type:8;
+ u32 reserved:8;
+} __packed;
+static_assert(sizeof(struct avs_audio_format) == 24);
+
+struct avs_modcfg_base {
+ u32 cpc;
+ u32 ibs;
+ u32 obs;
+ u32 is_pages;
+ struct avs_audio_format audio_fmt;
+} __packed;
+static_assert(sizeof(struct avs_modcfg_base) == 40);
+
+struct avs_pin_format {
+ u32 pin_index;
+ u32 iobs;
+ struct avs_audio_format audio_fmt;
+} __packed;
+static_assert(sizeof(struct avs_pin_format) == 32);
+
+struct avs_modcfg_ext {
+ struct avs_modcfg_base base;
+ u16 num_input_pins;
+ u16 num_output_pins;
+ u8 reserved[12];
+ /* input pin formats followed by output ones */
+ struct avs_pin_format pin_fmts[];
+} __packed;
+static_assert(sizeof(struct avs_modcfg_ext) == 56);
+
+enum avs_dma_type {
+ AVS_DMA_HDA_HOST_OUTPUT = 0,
+ AVS_DMA_HDA_HOST_INPUT = 1,
+ AVS_DMA_HDA_LINK_OUTPUT = 8,
+ AVS_DMA_HDA_LINK_INPUT = 9,
+ AVS_DMA_DMIC_LINK_INPUT = 11,
+ AVS_DMA_I2S_LINK_OUTPUT = 12,
+ AVS_DMA_I2S_LINK_INPUT = 13,
+};
+
+union avs_virtual_index {
+ u8 val;
+ struct {
+ u8 time_slot:4;
+ u8 instance:4;
+ } i2s;
+ struct {
+ u8 queue_id:3;
+ u8 time_slot:2;
+ u8 instance:3;
+ } dmic;
+} __packed;
+static_assert(sizeof(union avs_virtual_index) == 1);
+
+union avs_connector_node_id {
+ u32 val;
+ struct {
+ u32 vindex:8;
+ u32 dma_type:5;
+ u32 rsvd:19;
+ };
+} __packed;
+static_assert(sizeof(union avs_connector_node_id) == 4);
+
+#define INVALID_PIPELINE_ID 0xFF
+#define INVALID_NODE_ID \
+ ((union avs_connector_node_id) { UINT_MAX })
+
+union avs_gtw_attributes {
+ u32 val;
+ struct {
+ u32 lp_buffer_alloc:1;
+ u32 rsvd:31;
+ };
+} __packed;
+static_assert(sizeof(union avs_gtw_attributes) == 4);
+
+#define AVS_GTW_DMA_CONFIG_ID 0x1000
+#define AVS_DMA_METHOD_HDA 1
+
+struct avs_dma_device_stream_channel_map {
+ u32 device_address;
+ u32 channel_map;
+} __packed;
+static_assert(sizeof(struct avs_dma_device_stream_channel_map) == 8);
+
+struct avs_dma_stream_channel_map {
+ u32 device_count;
+ struct avs_dma_device_stream_channel_map map[16];
+} __packed;
+static_assert(sizeof(struct avs_dma_stream_channel_map) == 132);
+
+struct avs_dma_cfg {
+ u8 dma_method;
+ u8 pre_allocated;
+ u16 rsvd;
+ u32 dma_channel_id;
+ u32 stream_id;
+ struct avs_dma_stream_channel_map map;
+ u32 config_size;
+ u8 config[] __counted_by(config_size);
+} __packed;
+static_assert(sizeof(struct avs_dma_cfg) == 148);
+
+struct avs_copier_gtw_cfg {
+ union avs_connector_node_id node_id;
+ u32 dma_buffer_size;
+ u32 config_length;
+ union {
+ union avs_gtw_attributes attrs;
+ DECLARE_FLEX_ARRAY(u32, blob);
+ } config;
+} __packed;
+static_assert(sizeof(struct avs_copier_gtw_cfg) == 16);
+
+struct avs_copier_cfg {
+ struct avs_modcfg_base base;
+ struct avs_audio_format out_fmt;
+ u32 feature_mask;
+ struct avs_copier_gtw_cfg gtw_cfg;
+} __packed;
+static_assert(sizeof(struct avs_copier_cfg) == 84);
+
+struct avs_volume_cfg {
+ u32 channel_id;
+ u32 target_volume;
+ u32 curve_type;
+ u32 reserved; /* alignment */
+ u64 curve_duration;
+} __packed;
+static_assert(sizeof(struct avs_volume_cfg) == 24);
+
+struct avs_mute_cfg {
+ u32 channel_id;
+ u32 mute;
+ u32 curve_type;
+ u32 reserved; /* alignment */
+ u64 curve_duration;
+} __packed;
+static_assert(sizeof(struct avs_mute_cfg) == 24);
+
+struct avs_peakvol_cfg {
+ struct avs_modcfg_base base;
+ struct avs_volume_cfg vols[];
+} __packed;
+static_assert(sizeof(struct avs_peakvol_cfg) == 40);
+
+struct avs_micsel_cfg {
+ struct avs_modcfg_base base;
+ struct avs_audio_format out_fmt;
+} __packed;
+static_assert(sizeof(struct avs_micsel_cfg) == 64);
+
+struct avs_mux_cfg {
+ struct avs_modcfg_base base;
+ struct avs_audio_format ref_fmt;
+ struct avs_audio_format out_fmt;
+} __packed;
+static_assert(sizeof(struct avs_mux_cfg) == 88);
+
+struct avs_updown_mixer_cfg {
+ struct avs_modcfg_base base;
+ u32 out_channel_config;
+ u32 coefficients_select;
+ s32 coefficients[AVS_COEFF_CHANNELS_MAX];
+ u32 channel_map;
+} __packed;
+static_assert(sizeof(struct avs_updown_mixer_cfg) == 84);
+
+struct avs_src_cfg {
+ struct avs_modcfg_base base;
+ u32 out_freq;
+} __packed;
+static_assert(sizeof(struct avs_src_cfg) == 44);
+
+struct avs_probe_gtw_cfg {
+ union avs_connector_node_id node_id;
+ u32 dma_buffer_size;
+} __packed;
+static_assert(sizeof(struct avs_probe_gtw_cfg) == 8);
+
+struct avs_probe_cfg {
+ struct avs_modcfg_base base;
+ struct avs_probe_gtw_cfg gtw_cfg;
+} __packed;
+static_assert(sizeof(struct avs_probe_cfg) == 48);
+
+struct avs_aec_cfg {
+ struct avs_modcfg_base base;
+ struct avs_audio_format ref_fmt;
+ struct avs_audio_format out_fmt;
+ u32 cpc_lp_mode;
+} __packed;
+static_assert(sizeof(struct avs_aec_cfg) == 92);
+
+struct avs_asrc_cfg {
+ struct avs_modcfg_base base;
+ u32 out_freq;
+ u32 mode:2;
+ u32 rsvd2:2;
+ u32 disable_jitter_buffer:1;
+ u32 rsvd3:27;
+} __packed;
+static_assert(sizeof(struct avs_asrc_cfg) == 48);
+
+struct avs_wov_cfg {
+ struct avs_modcfg_base base;
+ u32 cpc_lp_mode;
+} __packed;
+static_assert(sizeof(struct avs_wov_cfg) == 44);
+
+struct avs_whm_cfg {
+ struct avs_modcfg_base base;
+ /* Audio format for output pin 0 */
+ struct avs_audio_format ref_fmt;
+ struct avs_audio_format out_fmt;
+ u32 wake_tick_period;
+ struct avs_copier_gtw_cfg gtw_cfg;
+} __packed;
+static_assert(sizeof(struct avs_whm_cfg) == 108);
+
+/* Module runtime parameters */
+
+#define AVS_VENDOR_CONFIG 0xFF
+
+enum avs_copier_runtime_param {
+ AVS_COPIER_SET_SINK_FORMAT = 2,
+};
+
+struct avs_copier_sink_format {
+ u32 sink_id;
+ struct avs_audio_format src_fmt;
+ struct avs_audio_format sink_fmt;
+} __packed;
+static_assert(sizeof(struct avs_copier_sink_format) == 52);
+
+int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
+ u8 instance_id, u32 sink_id,
+ const struct avs_audio_format *src_fmt,
+ const struct avs_audio_format *sink_fmt);
+
+enum avs_peakvol_runtime_param {
+ AVS_PEAKVOL_VOLUME = 0,
+ AVS_PEAKVOL_MUTE = 3,
+};
+
+enum avs_audio_curve_type {
+ AVS_AUDIO_CURVE_NONE = 0,
+ AVS_AUDIO_CURVE_WINDOWS_FADE = 1,
+};
+
+int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg **vols, size_t *num_vols);
+int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vol);
+int avs_ipc_peakvol_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vols, size_t num_vols);
+int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg **mutes, size_t *num_mutes);
+int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mute);
+int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mutes, size_t num_mutes);
+
+#define AVS_PROBE_INST_ID 0
+
+enum avs_probe_runtime_param {
+ AVS_PROBE_INJECTION_DMA = 1,
+ AVS_PROBE_INJECTION_DMA_DETACH,
+ AVS_PROBE_POINTS,
+ AVS_PROBE_POINTS_DISCONNECT,
+};
+
+struct avs_probe_dma {
+ union avs_connector_node_id node_id;
+ u32 dma_buffer_size;
+} __packed;
+static_assert(sizeof(struct avs_probe_dma) == 8);
+
+enum avs_probe_type {
+ AVS_PROBE_TYPE_INPUT = 0,
+ AVS_PROBE_TYPE_OUTPUT,
+ AVS_PROBE_TYPE_INTERNAL
+};
+
+union avs_probe_point_id {
+ u32 value;
+ struct {
+ u32 module_id:16;
+ u32 instance_id:8;
+ u32 type:2;
+ u32 index:6;
+ } id;
+} __packed;
+static_assert(sizeof(union avs_probe_point_id) == 4);
+
+enum avs_connection_purpose {
+ AVS_CONNECTION_PURPOSE_EXTRACT = 0,
+ AVS_CONNECTION_PURPOSE_INJECT,
+ AVS_CONNECTION_PURPOSE_INJECT_REEXTRACT,
+};
+
+struct avs_probe_point_desc {
+ union avs_probe_point_id id;
+ u32 purpose;
+ union avs_connector_node_id node_id;
+} __packed;
+static_assert(sizeof(struct avs_probe_point_desc) == 12);
+
+int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas);
+int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas);
+int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids,
+ size_t num_node_ids);
+int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs,
+ size_t *num_descs);
+int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs,
+ size_t num_descs);
+int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids,
+ size_t num_ids);
+
+#endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
diff --git a/sound/soc/intel/avs/mtl.c b/sound/soc/intel/avs/mtl.c
new file mode 100644
index 000000000000..d8bdd03275d7
--- /dev/null
+++ b/sound/soc/intel/avs/mtl.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2021-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "registers.h"
+#include "trace.h"
+
+#define MTL_HfDSSGBL_BASE 0x1000
+#define MTL_REG_HfDSSCS (MTL_HfDSSGBL_BASE + 0x0)
+#define MTL_HfDSSCS_SPA BIT(16)
+#define MTL_HfDSSCS_CPA BIT(24)
+
+#define MTL_DSPCS_BASE 0x178D00
+#define MTL_REG_DSPCCTL (MTL_DSPCS_BASE + 0x4)
+#define MTL_DSPCCTL_SPA BIT(0)
+#define MTL_DSPCCTL_CPA BIT(8)
+#define MTL_DSPCCTL_OSEL GENMASK(25, 24)
+#define MTL_DSPCCTL_OSEL_HOST BIT(25)
+
+#define MTL_HfINT_BASE 0x1100
+#define MTL_REG_HfINTIPPTR (MTL_HfINT_BASE + 0x8)
+#define MTL_REG_HfHIPCIE (MTL_HfINT_BASE + 0x40)
+#define MTL_HfINTIPPTR_PTR GENMASK(20, 0)
+#define MTL_HfHIPCIE_IE BIT(0)
+
+#define MTL_DWICTL_INTENL_IE BIT(0)
+#define MTL_DWICTL_FINALSTATUSL_IPC BIT(0) /* same as ADSPIS_IPC */
+
+static int avs_mtl_core_power_on(struct avs_dev *adev)
+{
+ u32 reg;
+ int ret;
+
+ /* Power up DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, MTL_HfDSSCS_SPA);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "power dsp", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == MTL_HfDSSCS_CPA,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+ if (ret) {
+ dev_err(adev->dev, "power on domain dsp failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Prevent power gating of DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL, MTL_HfPWRCTL_WPDSPHPxPG,
+ MTL_HfPWRCTL_WPDSPHPxPG);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "prevent dsp PG", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfPWRSTS, reg,
+ (reg & MTL_HfPWRSTS_DSPHPxPGS) == MTL_HfPWRSTS_DSPHPxPGS,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+
+ /* Set ownership to HOST. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_OSEL, MTL_DSPCCTL_OSEL_HOST);
+ return ret;
+}
+
+static int avs_mtl_core_power_off(struct avs_dev *adev)
+{
+ u32 reg;
+
+ /* Allow power gating of DSP domain. No STS polling as HOST is only one of its users. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL, MTL_HfPWRCTL_WPDSPHPxPG, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "allow dsp pg", false);
+
+ /* Power down DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "power dsp", false);
+
+ return snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == 0,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+}
+
+int avs_mtl_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+ if (!core_mask)
+ return 0;
+
+ if (power)
+ return avs_mtl_core_power_on(adev);
+ return avs_mtl_core_power_off(adev);
+}
+
+int avs_mtl_core_reset(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ /* No logical equivalent on ACE 1.x. */
+ return 0;
+}
+
+int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ u32 value, reg;
+ int ret;
+
+ core_mask &= AVS_MAIN_CORE_MASK;
+ if (!core_mask)
+ return 0;
+
+ value = snd_hdac_adsp_readl(adev, MTL_REG_DSPCCTL);
+ trace_avs_dsp_core_op(value, core_mask, "stall", stall);
+ if (value == UINT_MAX)
+ return 0;
+
+ value = stall ? 0 : MTL_DSPCCTL_SPA;
+ snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_SPA, value);
+
+ value = stall ? 0 : MTL_DSPCCTL_CPA;
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_DSPCCTL,
+ reg, (reg & MTL_DSPCCTL_CPA) == value,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+ if (ret)
+ dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
+ core_mask, stall ? "" : "un", ret);
+ return ret;
+}
+
+static void avs_mtl_ipc_interrupt(struct avs_dev *adev)
+{
+ const struct avs_spec *spec = adev->spec;
+ u32 hipc_ack, hipc_rsp;
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0);
+
+ hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset);
+ hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+
+ /* DSP acked host's request. */
+ if (hipc_ack & spec->hipc->ack_done_mask) {
+ complete(&adev->ipc->done_completion);
+
+ /* Tell DSP it has our attention. */
+ snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask,
+ spec->hipc->ack_done_mask);
+ }
+
+ /* DSP sent new response to process. */
+ if (hipc_rsp & spec->hipc->rsp_busy_mask) {
+ union avs_reply_msg msg;
+
+ msg.primary = snd_hdac_adsp_readl(adev, MTL_REG_HfIPCxTDR);
+ msg.ext.val = snd_hdac_adsp_readl(adev, MTL_REG_HfIPCxTDD);
+
+ avs_dsp_process_response(adev, msg.val);
+
+ /* Tell DSP we accepted its message. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxTDR,
+ MTL_HfIPCxTDR_BUSY, MTL_HfIPCxTDR_BUSY);
+ /* Ack this response. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxTDA, MTL_HfIPCxTDA_BUSY, 0);
+ }
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
+}
+
+irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev)
+{
+ u32 adspis = snd_hdac_adsp_readl(adev, MTL_DWICTL_REG_FINALSTATUSL);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (adspis == UINT_MAX)
+ return ret;
+
+ if (adspis & MTL_DWICTL_FINALSTATUSL_IPC) {
+ avs_mtl_ipc_interrupt(adev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable)
+{
+ if (enable) {
+ snd_hdac_adsp_updatel(adev, MTL_DWICTL_REG_INTENL, MTL_DWICTL_INTENL_IE,
+ MTL_DWICTL_INTENL_IE);
+ snd_hdac_adsp_updatew(adev, MTL_REG_HfHIPCIE, MTL_HfHIPCIE_IE, MTL_HfHIPCIE_IE);
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_DONE,
+ AVS_ADSP_HIPCCTL_DONE);
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_BUSY,
+ AVS_ADSP_HIPCCTL_BUSY);
+ } else {
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_BUSY, 0);
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_DONE, 0);
+ snd_hdac_adsp_updatew(adev, MTL_REG_HfHIPCIE, MTL_HfHIPCIE_IE, 0);
+ snd_hdac_adsp_updatel(adev, MTL_DWICTL_REG_INTENL, MTL_DWICTL_INTENL_IE, 0);
+ }
+}
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
new file mode 100644
index 000000000000..c8b586aced20
--- /dev/null
+++ b/sound/soc/intel/avs/path.c
@@ -0,0 +1,1608 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/acpi.h>
+#include <acpi/nhlt.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "control.h"
+#include "path.h"
+#include "topology.h"
+
+/* Must be called with adev->comp_list_mutex held. */
+static struct avs_tplg *
+avs_path_find_tplg(struct avs_dev *adev, const char *name)
+{
+ struct avs_soc_component *acomp;
+
+ list_for_each_entry(acomp, &adev->comp_list, node)
+ if (!strcmp(acomp->tplg->name, name))
+ return acomp->tplg;
+ return NULL;
+}
+
+static struct avs_path_module *
+avs_path_find_module(struct avs_path_pipeline *ppl, u32 template_id)
+{
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node)
+ if (mod->template->id == template_id)
+ return mod;
+ return NULL;
+}
+
+static struct avs_path_pipeline *
+avs_path_find_pipeline(struct avs_path *path, u32 template_id)
+{
+ struct avs_path_pipeline *ppl;
+
+ list_for_each_entry(ppl, &path->ppl_list, node)
+ if (ppl->template->id == template_id)
+ return ppl;
+ return NULL;
+}
+
+static struct avs_path *
+avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id)
+{
+ struct avs_tplg_path_template *pos, *template = NULL;
+ struct avs_tplg *tplg;
+ struct avs_path *path;
+
+ tplg = avs_path_find_tplg(adev, name);
+ if (!tplg)
+ return NULL;
+
+ list_for_each_entry(pos, &tplg->path_tmpl_list, node) {
+ if (pos->id == template_id) {
+ template = pos;
+ break;
+ }
+ }
+ if (!template)
+ return NULL;
+
+ spin_lock(&adev->path_list_lock);
+ /* Only one variant of given path template may be instantiated at a time. */
+ list_for_each_entry(path, &adev->path_list, node) {
+ if (path->template->owner == template) {
+ spin_unlock(&adev->path_list_lock);
+ return path;
+ }
+ }
+
+ spin_unlock(&adev->path_list_lock);
+ return NULL;
+}
+
+static bool avs_test_hw_params(struct snd_pcm_hw_params *params,
+ struct avs_audio_format *fmt)
+{
+ return (params_rate(params) == fmt->sampling_freq &&
+ params_channels(params) == fmt->num_channels &&
+ params_physical_width(params) == fmt->bit_depth &&
+ snd_pcm_hw_params_bits(params) == fmt->valid_bit_depth);
+}
+
+static struct avs_tplg_path *
+avs_path_find_variant(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params)
+{
+ struct avs_tplg_path *variant;
+
+ list_for_each_entry(variant, &template->path_list, node) {
+ dev_dbg(adev->dev, "check FE rate %d chn %d vbd %d bd %d\n",
+ variant->fe_fmt->sampling_freq, variant->fe_fmt->num_channels,
+ variant->fe_fmt->valid_bit_depth, variant->fe_fmt->bit_depth);
+ dev_dbg(adev->dev, "check BE rate %d chn %d vbd %d bd %d\n",
+ variant->be_fmt->sampling_freq, variant->be_fmt->num_channels,
+ variant->be_fmt->valid_bit_depth, variant->be_fmt->bit_depth);
+
+ if (variant->fe_fmt && avs_test_hw_params(fe_params, variant->fe_fmt) &&
+ variant->be_fmt && avs_test_hw_params(be_params, variant->be_fmt))
+ return variant;
+ }
+
+ return NULL;
+}
+
+static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *source,
+ struct avs_path *sink)
+{
+ struct avs_tplg_path *variant;
+
+ list_for_each_entry(variant, &template->path_list, node) {
+ if (variant->source_path_id == source->template->id &&
+ variant->sink_path_id == sink->template->id)
+ return variant;
+ }
+
+ return NULL;
+}
+
+static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
+ struct avs_tplg_path_template_id *id2)
+{
+ return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
+}
+
+static struct avs_path *avs_condpath_find_match(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *path, int dir)
+{
+ struct avs_tplg_path_template_id *id, *id2;
+
+ if (dir) {
+ id = &template->source;
+ id2 = &template->sink;
+ } else {
+ id = &template->sink;
+ id2 = &template->source;
+ }
+
+ /* Check whether this path is either source or sink of condpath template. */
+ if (id->id != path->template->owner->id ||
+ strcmp(id->tplg_name, path->template->owner->owner->name))
+ return NULL;
+
+ /* Unidirectional condpaths are allowed. */
+ if (avs_tplg_path_template_id_equal(id, id2))
+ return path;
+
+ /* Now find the counterpart. */
+ return avs_path_find_path(adev, id2->tplg_name, id2->id);
+}
+
+static struct acpi_nhlt_config *
+avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t);
+
+int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_constraint_list *rate_list,
+ struct snd_pcm_hw_constraint_list *channels_list,
+ struct snd_pcm_hw_constraint_list *sample_bits_list)
+{
+ struct avs_tplg_path *path_template;
+ unsigned int *rlist, *clist, *slist;
+ size_t i;
+
+ i = 0;
+ list_for_each_entry(path_template, &template->path_list, node)
+ i++;
+
+ rlist = kcalloc(i, sizeof(*rlist), GFP_KERNEL);
+ clist = kcalloc(i, sizeof(*clist), GFP_KERNEL);
+ slist = kcalloc(i, sizeof(*slist), GFP_KERNEL);
+ if (!rlist || !clist || !slist)
+ return -ENOMEM;
+
+ i = 0;
+ list_for_each_entry(path_template, &template->path_list, node) {
+ struct avs_tplg_pipeline *pipeline_template;
+
+ list_for_each_entry(pipeline_template, &path_template->ppl_list, node) {
+ struct avs_tplg_module *module_template;
+
+ list_for_each_entry(module_template, &pipeline_template->mod_list, node) {
+ const guid_t *type = &module_template->cfg_ext->type;
+ struct acpi_nhlt_config *blob;
+
+ if (!guid_equal(type, &AVS_COPIER_MOD_UUID) &&
+ !guid_equal(type, &AVS_WOVHOSTM_MOD_UUID))
+ continue;
+
+ switch (module_template->cfg_ext->copier.dma_type) {
+ case AVS_DMA_DMIC_LINK_INPUT:
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ case AVS_DMA_I2S_LINK_INPUT:
+ break;
+ default:
+ continue;
+ }
+
+ if (!module_template->nhlt_config) {
+ blob = avs_nhlt_config_or_default(adev, module_template);
+ if (IS_ERR(blob))
+ continue;
+ }
+
+ rlist[i] = path_template->fe_fmt->sampling_freq;
+ clist[i] = path_template->fe_fmt->num_channels;
+ slist[i] = path_template->fe_fmt->bit_depth;
+ i++;
+ }
+ }
+ }
+
+ if (i) {
+ rate_list->count = i;
+ rate_list->list = rlist;
+ channels_list->count = i;
+ channels_list->list = clist;
+ sample_bits_list->count = i;
+ sample_bits_list->list = slist;
+ } else {
+ kfree(rlist);
+ kfree(clist);
+ kfree(slist);
+ }
+
+ return i;
+}
+
+static void avs_init_node_id(union avs_connector_node_id *node_id,
+ struct avs_tplg_modcfg_ext *te, u32 dma_id)
+{
+ node_id->val = 0;
+ node_id->dma_type = te->copier.dma_type;
+
+ switch (node_id->dma_type) {
+ case AVS_DMA_DMIC_LINK_INPUT:
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ case AVS_DMA_I2S_LINK_INPUT:
+ /* Gateway's virtual index is statically assigned in the topology. */
+ node_id->vindex = te->copier.vindex.val;
+ break;
+
+ case AVS_DMA_HDA_HOST_OUTPUT:
+ case AVS_DMA_HDA_HOST_INPUT:
+ /* Gateway's virtual index is dynamically assigned with DMA ID */
+ node_id->vindex = dma_id;
+ break;
+
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ node_id->vindex = te->copier.vindex.val | dma_id;
+ break;
+
+ default:
+ *node_id = INVALID_NODE_ID;
+ break;
+ }
+}
+
+/* Every BLOB contains at least gateway attributes. */
+static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4};
+
+static struct acpi_nhlt_config *
+avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t)
+{
+ struct acpi_nhlt_format_config *fmtcfg;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_audio_format *fmt;
+ int link_type, dev_type;
+ int bus_id, dir;
+
+ te = t->cfg_ext;
+
+ switch (te->copier.dma_type) {
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ link_type = ACPI_NHLT_LINKTYPE_SSP;
+ dev_type = ACPI_NHLT_DEVICETYPE_CODEC;
+ bus_id = te->copier.vindex.i2s.instance;
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ fmt = te->copier.out_fmt;
+ break;
+
+ case AVS_DMA_I2S_LINK_INPUT:
+ link_type = ACPI_NHLT_LINKTYPE_SSP;
+ dev_type = ACPI_NHLT_DEVICETYPE_CODEC;
+ bus_id = te->copier.vindex.i2s.instance;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
+ break;
+
+ case AVS_DMA_DMIC_LINK_INPUT:
+ link_type = ACPI_NHLT_LINKTYPE_PDM;
+ dev_type = -1; /* ignored */
+ bus_id = 0;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
+ break;
+
+ default:
+ return default_blob;
+ }
+
+ /* Override format selection if necessary. */
+ if (te->copier.blob_fmt)
+ fmt = te->copier.blob_fmt;
+
+ fmtcfg = acpi_nhlt_find_fmtcfg(link_type, dev_type, dir, bus_id,
+ fmt->num_channels, fmt->sampling_freq, fmt->valid_bit_depth,
+ fmt->bit_depth);
+ if (!fmtcfg) {
+ dev_warn(adev->dev, "Endpoint format configuration not found.\n");
+ return ERR_PTR(-ENOENT);
+ }
+
+ if (fmtcfg->config.capabilities_size < default_blob->capabilities_size)
+ return ERR_PTR(-ETOOSMALL);
+ /* The firmware expects the payload to be DWORD-aligned. */
+ if (fmtcfg->config.capabilities_size % sizeof(u32))
+ return ERR_PTR(-EINVAL);
+
+ return &fmtcfg->config;
+}
+
+static int avs_append_dma_cfg(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw,
+ struct avs_tplg_module *t, u32 dma_id, size_t *cfg_size)
+{
+ u32 dma_type = t->cfg_ext->copier.dma_type;
+ struct avs_dma_cfg *dma;
+ struct avs_tlv *tlv;
+ size_t tlv_size;
+
+ if (!avs_platattr_test(adev, ALTHDA))
+ return 0;
+
+ switch (dma_type) {
+ case AVS_DMA_HDA_HOST_OUTPUT:
+ case AVS_DMA_HDA_HOST_INPUT:
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ return 0;
+ default:
+ break;
+ }
+
+ tlv_size = sizeof(*tlv) + sizeof(*dma);
+ if (*cfg_size + tlv_size > AVS_MAILBOX_SIZE)
+ return -E2BIG;
+
+ /* DMA config is a TLV tailing the existing payload. */
+ tlv = (struct avs_tlv *)&gtw->config.blob[gtw->config_length];
+ tlv->type = AVS_GTW_DMA_CONFIG_ID;
+ tlv->length = sizeof(*dma);
+
+ dma = (struct avs_dma_cfg *)tlv->value;
+ memset(dma, 0, sizeof(*dma));
+ dma->dma_method = AVS_DMA_METHOD_HDA;
+ dma->pre_allocated = true;
+ dma->dma_channel_id = dma_id;
+ dma->stream_id = dma_id + 1;
+
+ gtw->config_length += tlv_size / sizeof(u32);
+ *cfg_size += tlv_size;
+
+ return 0;
+}
+
+static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw,
+ struct avs_tplg_module *t, u32 dma_id, size_t *cfg_size)
+{
+ struct acpi_nhlt_config *blob;
+ size_t gtw_size;
+
+ if (t->nhlt_config)
+ blob = t->nhlt_config->blob;
+ else
+ blob = avs_nhlt_config_or_default(adev, t);
+ if (IS_ERR(blob))
+ return PTR_ERR(blob);
+
+ gtw_size = blob->capabilities_size;
+ if (*cfg_size + gtw_size > AVS_MAILBOX_SIZE)
+ return -E2BIG;
+
+ gtw->config_length = gtw_size / sizeof(u32);
+ memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size);
+ *cfg_size += gtw_size;
+
+ return avs_append_dma_cfg(adev, gtw, t, dma_id, cfg_size);
+}
+
+static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_copier_cfg *cfg;
+ size_t cfg_size;
+ u32 dma_id;
+ int ret;
+
+ te = t->cfg_ext;
+ cfg = adev->modcfg_buf;
+ dma_id = mod->owner->owner->dma_id;
+ cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config);
+
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, dma_id, &cfg_size);
+ if (ret)
+ return ret;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->out_fmt = *te->copier.out_fmt;
+ cfg->feature_mask = te->copier.feature_mask;
+ avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id);
+ cfg->gtw_cfg.dma_buffer_size = te->copier.dma_buffer_size;
+ mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
+ return ret;
+}
+
+static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_whm_cfg *cfg;
+ size_t cfg_size;
+ u32 dma_id;
+ int ret;
+
+ te = t->cfg_ext;
+ cfg = adev->modcfg_buf;
+ dma_id = mod->owner->owner->dma_id;
+ cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config);
+
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, dma_id, &cfg_size);
+ if (ret)
+ return ret;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->ref_fmt = *te->whm.ref_fmt;
+ cfg->out_fmt = *te->whm.out_fmt;
+ cfg->wake_tick_period = te->whm.wake_tick_period;
+ avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id);
+ cfg->gtw_cfg.dma_buffer_size = te->whm.dma_buffer_size;
+ mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
+ return ret;
+}
+
+static struct soc_mixer_control *avs_get_module_control(struct avs_path_module *mod,
+ const char *name)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_path_template *path_tmpl;
+ struct snd_soc_dapm_widget *w;
+ int i;
+
+ path_tmpl = t->owner->owner->owner;
+ w = path_tmpl->w;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ struct avs_control_data *ctl_data;
+ struct soc_mixer_control *mc;
+
+ mc = (struct soc_mixer_control *)w->kcontrols[i]->private_value;
+ ctl_data = (struct avs_control_data *)mc->dobj.private;
+ if (ctl_data->id == t->ctl_id && strstr(w->kcontrols[i]->id.name, name))
+ return mc;
+ }
+
+ return NULL;
+}
+
+int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input)
+{
+ struct avs_volume_cfg vols[SND_SOC_TPLG_MAX_CHAN] = {{0}};
+ struct avs_control_data *ctl_data;
+ struct avs_tplg_module *t;
+ int ret, i;
+
+ ctl_data = mc->dobj.private;
+ t = mod->template;
+ if (!input)
+ input = ctl_data->values;
+
+ if (mc->num_channels) {
+ for (i = 0; i < mc->num_channels; i++) {
+ vols[i].channel_id = i;
+ vols[i].target_volume = input[i];
+ vols[i].curve_type = t->cfg_ext->peakvol.curve_type;
+ vols[i].curve_duration = t->cfg_ext->peakvol.curve_duration;
+ }
+
+ ret = avs_ipc_peakvol_set_volumes(adev, mod->module_id, mod->instance_id, vols,
+ mc->num_channels);
+ return AVS_IPC_RET(ret);
+ }
+
+ /* Target all channels if no individual selected. */
+ vols[0].channel_id = AVS_ALL_CHANNELS_MASK;
+ vols[0].target_volume = input[0];
+ vols[0].curve_type = t->cfg_ext->peakvol.curve_type;
+ vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
+
+ ret = avs_ipc_peakvol_set_volume(adev, mod->module_id, mod->instance_id, &vols[0]);
+ return AVS_IPC_RET(ret);
+}
+
+int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input)
+{
+ struct avs_mute_cfg mutes[SND_SOC_TPLG_MAX_CHAN] = {{0}};
+ struct avs_control_data *ctl_data;
+ struct avs_tplg_module *t;
+ int ret, i;
+
+ ctl_data = mc->dobj.private;
+ t = mod->template;
+ if (!input)
+ input = ctl_data->values;
+
+ if (mc->num_channels) {
+ for (i = 0; i < mc->num_channels; i++) {
+ mutes[i].channel_id = i;
+ mutes[i].mute = !input[i];
+ mutes[i].curve_type = t->cfg_ext->peakvol.curve_type;
+ mutes[i].curve_duration = t->cfg_ext->peakvol.curve_duration;
+ }
+
+ ret = avs_ipc_peakvol_set_mutes(adev, mod->module_id, mod->instance_id, mutes,
+ mc->num_channels);
+ return AVS_IPC_RET(ret);
+ }
+
+ /* Target all channels if no individual selected. */
+ mutes[0].channel_id = AVS_ALL_CHANNELS_MASK;
+ mutes[0].mute = !input[0];
+ mutes[0].curve_type = t->cfg_ext->peakvol.curve_type;
+ mutes[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
+
+ ret = avs_ipc_peakvol_set_mute(adev, mod->module_id, mod->instance_id, &mutes[0]);
+ return AVS_IPC_RET(ret);
+}
+
+static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct soc_mixer_control *mc;
+ struct avs_peakvol_cfg *cfg;
+ size_t cfg_size;
+ int ret;
+
+ cfg_size = struct_size(cfg, vols, 1);
+ if (cfg_size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+
+ cfg = adev->modcfg_buf;
+ memset(cfg, 0, cfg_size);
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->vols[0].channel_id = AVS_ALL_CHANNELS_MASK;
+ cfg->vols[0].target_volume = S32_MAX;
+ cfg->vols[0].curve_type = t->cfg_ext->peakvol.curve_type;
+ cfg->vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
+ if (ret)
+ return ret;
+
+ /* Now configure both VOLUME and MUTE parameters. */
+ mc = avs_get_module_control(mod, "Volume");
+ if (mc) {
+ ret = avs_peakvol_set_volume(adev, mod, mc, NULL);
+ if (ret)
+ return ret;
+ }
+
+ mc = avs_get_module_control(mod, "Switch");
+ if (mc)
+ return avs_peakvol_set_mute(adev, mod, mc, NULL);
+ return 0;
+}
+
+static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_updown_mixer_cfg cfg;
+ int i;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_channel_config = t->cfg_ext->updown_mix.out_channel_config;
+ cfg.coefficients_select = t->cfg_ext->updown_mix.coefficients_select;
+ for (i = 0; i < AVS_COEFF_CHANNELS_MAX; i++)
+ cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i];
+ cfg.channel_map = t->cfg_ext->updown_mix.channel_map;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_src_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_src_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_freq = t->cfg_ext->src.out_freq;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_asrc_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_asrc_cfg cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_freq = t->cfg_ext->asrc.out_freq;
+ cfg.mode = t->cfg_ext->asrc.mode;
+ cfg.disable_jitter_buffer = t->cfg_ext->asrc.disable_jitter_buffer;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_aec_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_aec_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.ref_fmt = *t->cfg_ext->aec.ref_fmt;
+ cfg.out_fmt = *t->cfg_ext->aec.out_fmt;
+ cfg.cpc_lp_mode = t->cfg_ext->aec.cpc_lp_mode;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_mux_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_mux_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.ref_fmt = *t->cfg_ext->mux.ref_fmt;
+ cfg.out_fmt = *t->cfg_ext->mux.out_fmt;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_wov_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_wov_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.cpc_lp_mode = t->cfg_ext->wov.cpc_lp_mode;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_micsel_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_micsel_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_fmt = *t->cfg_ext->micsel.out_fmt;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_modbase_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_modcfg_base cfg;
+
+ cfg.cpc = t->cfg_base->cpc;
+ cfg.ibs = t->cfg_base->ibs;
+ cfg.obs = t->cfg_base->obs;
+ cfg.is_pages = t->cfg_base->is_pages;
+ cfg.audio_fmt = *t->in_fmt;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *tcfg = t->cfg_ext;
+ struct avs_modcfg_ext *cfg;
+ size_t cfg_size, num_pins;
+ int ret, i;
+
+ num_pins = tcfg->generic.num_input_pins + tcfg->generic.num_output_pins;
+ cfg_size = struct_size(cfg, pin_fmts, num_pins);
+
+ if (cfg_size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+
+ cfg = adev->modcfg_buf;
+ memset(cfg, 0, cfg_size);
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->num_input_pins = tcfg->generic.num_input_pins;
+ cfg->num_output_pins = tcfg->generic.num_output_pins;
+
+ /* configure pin formats */
+ for (i = 0; i < num_pins; i++) {
+ struct avs_tplg_pin_format *tpin = &tcfg->generic.pin_fmts[i];
+ struct avs_pin_format *pin = &cfg->pin_fmts[i];
+
+ pin->pin_index = tpin->pin_index;
+ pin->iobs = tpin->iobs;
+ pin->audio_fmt = *tpin->fmt;
+ }
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, cfg, cfg_size,
+ &mod->instance_id);
+ return ret;
+}
+
+static int avs_probe_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ dev_err(adev->dev, "Probe module can't be instantiated by topology");
+ return -EINVAL;
+}
+
+struct avs_module_create {
+ guid_t *guid;
+ int (*create)(struct avs_dev *adev, struct avs_path_module *mod);
+};
+
+static struct avs_module_create avs_module_create[] = {
+ { &AVS_MIXIN_MOD_UUID, avs_modbase_create },
+ { &AVS_MIXOUT_MOD_UUID, avs_modbase_create },
+ { &AVS_KPBUFF_MOD_UUID, avs_modbase_create },
+ { &AVS_COPIER_MOD_UUID, avs_copier_create },
+ { &AVS_PEAKVOL_MOD_UUID, avs_peakvol_create },
+ { &AVS_GAIN_MOD_UUID, avs_peakvol_create },
+ { &AVS_MICSEL_MOD_UUID, avs_micsel_create },
+ { &AVS_MUX_MOD_UUID, avs_mux_create },
+ { &AVS_UPDWMIX_MOD_UUID, avs_updown_mix_create },
+ { &AVS_SRCINTC_MOD_UUID, avs_src_create },
+ { &AVS_AEC_MOD_UUID, avs_aec_create },
+ { &AVS_ASRC_MOD_UUID, avs_asrc_create },
+ { &AVS_INTELWOV_MOD_UUID, avs_wov_create },
+ { &AVS_PROBE_MOD_UUID, avs_probe_create },
+ { &AVS_WOVHOSTM_MOD_UUID, avs_whm_create },
+};
+
+static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ const guid_t *type = &mod->template->cfg_ext->type;
+
+ for (int i = 0; i < ARRAY_SIZE(avs_module_create); i++)
+ if (guid_equal(type, avs_module_create[i].guid))
+ return avs_module_create[i].create(adev, mod);
+
+ return avs_modext_create(adev, mod);
+}
+
+static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_soc_component *acomp;
+
+ acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp);
+
+ u32 num_ids = mod->template->num_config_ids;
+ u32 *ids = mod->template->config_ids;
+
+ for (int i = 0; i < num_ids; i++) {
+ struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]];
+ size_t len = config->length;
+ void *data = config->data;
+ u32 param = config->param;
+ int ret;
+
+ ret = avs_ipc_set_large_config(adev, mod->module_id, mod->instance_id,
+ param, data, len);
+ if (ret) {
+ dev_err(adev->dev, "send initial module config failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ return 0;
+}
+
+static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ kfree(mod);
+}
+
+static struct avs_path_module *
+avs_path_module_create(struct avs_dev *adev,
+ struct avs_path_pipeline *owner,
+ struct avs_tplg_module *template)
+{
+ struct avs_path_module *mod;
+ int module_id, ret;
+
+ module_id = avs_get_module_id(adev, &template->cfg_ext->type);
+ if (module_id < 0)
+ return ERR_PTR(module_id);
+
+ mod = kzalloc(sizeof(*mod), GFP_KERNEL);
+ if (!mod)
+ return ERR_PTR(-ENOMEM);
+
+ mod->template = template;
+ mod->module_id = module_id;
+ mod->owner = owner;
+ INIT_LIST_HEAD(&mod->node);
+
+ ret = avs_path_module_type_create(adev, mod);
+ if (ret) {
+ dev_err(adev->dev, "module-type create failed: %d\n", ret);
+ kfree(mod);
+ return ERR_PTR(ret);
+ }
+
+ ret = avs_path_module_send_init_configs(adev, mod);
+ if (ret) {
+ kfree(mod);
+ return ERR_PTR(ret);
+ }
+
+ return mod;
+}
+
+static int avs_path_binding_arm(struct avs_dev *adev, struct avs_path_binding *binding)
+{
+ struct avs_path_module *this_mod, *target_mod;
+ struct avs_path_pipeline *target_ppl;
+ struct avs_path *target_path;
+ struct avs_tplg_binding *t;
+
+ t = binding->template;
+ this_mod = avs_path_find_module(binding->owner,
+ t->mod_id);
+ if (!this_mod) {
+ dev_err(adev->dev, "path mod %d not found\n", t->mod_id);
+ return -EINVAL;
+ }
+
+ /* update with target_tplg_name too */
+ target_path = avs_path_find_path(adev, t->target_tplg_name,
+ t->target_path_tmpl_id);
+ if (!target_path) {
+ dev_err(adev->dev, "target path %s:%d not found\n",
+ t->target_tplg_name, t->target_path_tmpl_id);
+ return -EINVAL;
+ }
+
+ target_ppl = avs_path_find_pipeline(target_path,
+ t->target_ppl_id);
+ if (!target_ppl) {
+ dev_err(adev->dev, "target ppl %d not found\n", t->target_ppl_id);
+ return -EINVAL;
+ }
+
+ target_mod = avs_path_find_module(target_ppl, t->target_mod_id);
+ if (!target_mod) {
+ dev_err(adev->dev, "target mod %d not found\n", t->target_mod_id);
+ return -EINVAL;
+ }
+
+ if (t->is_sink) {
+ binding->sink = this_mod;
+ binding->sink_pin = t->mod_pin;
+ binding->source = target_mod;
+ binding->source_pin = t->target_mod_pin;
+ } else {
+ binding->sink = target_mod;
+ binding->sink_pin = t->target_mod_pin;
+ binding->source = this_mod;
+ binding->source_pin = t->mod_pin;
+ }
+
+ return 0;
+}
+
+static void avs_path_binding_free(struct avs_dev *adev, struct avs_path_binding *binding)
+{
+ kfree(binding);
+}
+
+static struct avs_path_binding *avs_path_binding_create(struct avs_dev *adev,
+ struct avs_path_pipeline *owner,
+ struct avs_tplg_binding *t)
+{
+ struct avs_path_binding *binding;
+
+ binding = kzalloc(sizeof(*binding), GFP_KERNEL);
+ if (!binding)
+ return ERR_PTR(-ENOMEM);
+
+ binding->template = t;
+ binding->owner = owner;
+ INIT_LIST_HEAD(&binding->node);
+
+ return binding;
+}
+
+static int avs_path_pipeline_arm(struct avs_dev *adev,
+ struct avs_path_pipeline *ppl)
+{
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node) {
+ struct avs_path_module *source, *sink;
+ int ret;
+
+ /*
+ * Only one module (so it's implicitly last) or it is the last
+ * one, either way we don't have next module to bind it to.
+ */
+ if (mod == list_last_entry(&ppl->mod_list,
+ struct avs_path_module, node))
+ break;
+
+ /* bind current module to next module on list */
+ source = mod;
+ sink = list_next_entry(mod, node);
+
+ ret = avs_ipc_bind(adev, source->module_id, source->instance_id,
+ sink->module_id, sink->instance_id, 0, 0);
+ if (ret)
+ return AVS_IPC_RET(ret);
+ }
+
+ return 0;
+}
+
+static void avs_path_pipeline_free(struct avs_dev *adev,
+ struct avs_path_pipeline *ppl)
+{
+ struct avs_path_binding *binding, *bsave;
+ struct avs_path_module *mod, *save;
+
+ list_for_each_entry_safe(binding, bsave, &ppl->binding_list, node) {
+ list_del(&binding->node);
+ avs_path_binding_free(adev, binding);
+ }
+
+ avs_dsp_delete_pipeline(adev, ppl->instance_id);
+
+ /* Unload resources occupied by owned modules */
+ list_for_each_entry_safe(mod, save, &ppl->mod_list, node) {
+ avs_dsp_delete_module(adev, mod->module_id, mod->instance_id,
+ mod->owner->instance_id,
+ mod->template->core_id);
+ avs_path_module_free(adev, mod);
+ }
+
+ list_del(&ppl->node);
+ kfree(ppl);
+}
+
+static struct avs_path_pipeline *
+avs_path_pipeline_create(struct avs_dev *adev, struct avs_path *owner,
+ struct avs_tplg_pipeline *template)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_tplg_pplcfg *cfg = template->cfg;
+ struct avs_tplg_module *tmod;
+ int ret, i;
+
+ ppl = kzalloc(sizeof(*ppl), GFP_KERNEL);
+ if (!ppl)
+ return ERR_PTR(-ENOMEM);
+
+ ppl->template = template;
+ ppl->owner = owner;
+ INIT_LIST_HEAD(&ppl->binding_list);
+ INIT_LIST_HEAD(&ppl->mod_list);
+ INIT_LIST_HEAD(&ppl->node);
+
+ ret = avs_dsp_create_pipeline(adev, cfg->req_size, cfg->priority,
+ cfg->lp, cfg->attributes,
+ &ppl->instance_id);
+ if (ret) {
+ dev_err(adev->dev, "error creating pipeline %d\n", ret);
+ kfree(ppl);
+ return ERR_PTR(ret);
+ }
+
+ list_for_each_entry(tmod, &template->mod_list, node) {
+ struct avs_path_module *mod;
+
+ mod = avs_path_module_create(adev, ppl, tmod);
+ if (IS_ERR(mod)) {
+ ret = PTR_ERR(mod);
+ dev_err(adev->dev, "error creating module %d\n", ret);
+ goto init_err;
+ }
+
+ list_add_tail(&mod->node, &ppl->mod_list);
+ }
+
+ for (i = 0; i < template->num_bindings; i++) {
+ struct avs_path_binding *binding;
+
+ binding = avs_path_binding_create(adev, ppl, template->bindings[i]);
+ if (IS_ERR(binding)) {
+ ret = PTR_ERR(binding);
+ dev_err(adev->dev, "error creating binding %d\n", ret);
+ goto init_err;
+ }
+
+ list_add_tail(&binding->node, &ppl->binding_list);
+ }
+
+ return ppl;
+
+init_err:
+ avs_path_pipeline_free(adev, ppl);
+ return ERR_PTR(ret);
+}
+
+static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
+ struct avs_tplg_path *template, u32 dma_id)
+{
+ struct avs_tplg_pipeline *tppl;
+
+ path->owner = adev;
+ path->template = template;
+ path->dma_id = dma_id;
+ INIT_LIST_HEAD(&path->ppl_list);
+ INIT_LIST_HEAD(&path->node);
+ INIT_LIST_HEAD(&path->source_list);
+ INIT_LIST_HEAD(&path->sink_list);
+ INIT_LIST_HEAD(&path->source_node);
+ INIT_LIST_HEAD(&path->sink_node);
+
+ /* create all the pipelines */
+ list_for_each_entry(tppl, &template->ppl_list, node) {
+ struct avs_path_pipeline *ppl;
+
+ ppl = avs_path_pipeline_create(adev, path, tppl);
+ if (IS_ERR(ppl))
+ return PTR_ERR(ppl);
+
+ list_add_tail(&ppl->node, &path->ppl_list);
+ }
+
+ spin_lock(&adev->path_list_lock);
+ list_add_tail(&path->node, &adev->path_list);
+ spin_unlock(&adev->path_list_lock);
+
+ return 0;
+}
+
+static int avs_path_arm(struct avs_dev *adev, struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_path_binding *binding;
+ int ret;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ /*
+ * Arm all ppl bindings before binding internal modules
+ * as it costs no IPCs which isn't true for the latter.
+ */
+ list_for_each_entry(binding, &ppl->binding_list, node) {
+ ret = avs_path_binding_arm(adev, binding);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = avs_path_pipeline_arm(adev, ppl);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void avs_path_free_unlocked(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl, *save;
+
+ spin_lock(&path->owner->path_list_lock);
+ list_del(&path->node);
+ spin_unlock(&path->owner->path_list_lock);
+
+ list_for_each_entry_safe(ppl, save, &path->ppl_list, node)
+ avs_path_pipeline_free(path->owner, ppl);
+
+ kfree(path);
+}
+
+static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path *template)
+{
+ struct avs_path *path;
+ int ret;
+
+ path = kzalloc(sizeof(*path), GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ ret = avs_path_init(adev, path, template, dma_id);
+ if (ret < 0)
+ goto err;
+
+ ret = avs_path_arm(adev, path);
+ if (ret < 0)
+ goto err;
+
+ path->state = AVS_PPL_STATE_INVALID;
+ return path;
+err:
+ avs_path_free_unlocked(path);
+ return ERR_PTR(ret);
+}
+
+static void avs_condpath_free(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ list_del(&path->source_node);
+ list_del(&path->sink_node);
+
+ ret = avs_path_reset(path);
+ if (ret < 0)
+ dev_err(adev->dev, "reset condpath failed: %d\n", ret);
+
+ ret = avs_path_unbind(path);
+ if (ret < 0)
+ dev_err(adev->dev, "unbind condpath failed: %d\n", ret);
+
+ avs_path_free_unlocked(path);
+}
+
+static struct avs_path *avs_condpath_create(struct avs_dev *adev,
+ struct avs_tplg_path *template,
+ struct avs_path *source,
+ struct avs_path *sink)
+{
+ struct avs_path *path;
+ int ret;
+
+ path = avs_path_create_unlocked(adev, 0, template);
+ if (IS_ERR(path))
+ return path;
+
+ ret = avs_path_bind(path);
+ if (ret)
+ goto err_bind;
+
+ ret = avs_path_reset(path);
+ if (ret)
+ goto err_reset;
+
+ path->source = source;
+ path->sink = sink;
+ list_add_tail(&path->source_node, &source->source_list);
+ list_add_tail(&path->sink_node, &sink->sink_list);
+
+ return path;
+
+err_reset:
+ avs_path_unbind(path);
+err_bind:
+ avs_path_free_unlocked(path);
+ return ERR_PTR(ret);
+}
+
+static int avs_condpaths_walk(struct avs_dev *adev, struct avs_path *path, int dir)
+{
+ struct avs_soc_component *acomp;
+ struct avs_path *source, *sink;
+ struct avs_path **other;
+
+ if (dir) {
+ source = path;
+ other = &sink;
+ } else {
+ sink = path;
+ other = &source;
+ }
+
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ for (int i = 0; i < acomp->tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ struct avs_tplg_path *variant;
+ struct avs_path *cpath;
+
+ template = &acomp->tplg->condpath_tmpls[i];
+
+ /* Do not create unidirectional condpaths twice. */
+ if (avs_tplg_path_template_id_equal(&template->source,
+ &template->sink) && dir)
+ continue;
+
+ *other = avs_condpath_find_match(adev, template, path, dir);
+ if (!*other)
+ continue;
+
+ variant = avs_condpath_find_variant(adev, template, source, sink);
+ if (!variant)
+ continue;
+
+ cpath = avs_condpath_create(adev, variant, source, sink);
+ if (IS_ERR(cpath))
+ return PTR_ERR(cpath);
+ }
+ }
+
+ return 0;
+}
+
+/* Caller responsible for holding adev->path_mutex. */
+static int avs_condpaths_walk_all(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ ret = avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+
+ return avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+void avs_path_free(struct avs_path *path)
+{
+ struct avs_path *cpath, *csave;
+ struct avs_dev *adev = path->owner;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* Free all condpaths this path spawned. */
+ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node)
+ avs_condpath_free(path->owner, cpath);
+ list_for_each_entry_safe(cpath, csave, &path->sink_list, sink_node)
+ avs_condpath_free(path->owner, cpath);
+
+ avs_path_free_unlocked(path);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
+struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params)
+{
+ struct avs_tplg_path *variant;
+ struct avs_path *path;
+ int ret;
+
+ variant = avs_path_find_variant(adev, template, fe_params, be_params);
+ if (!variant) {
+ dev_err(adev->dev, "no matching variant found\n");
+ return ERR_PTR(-ENOENT);
+ }
+
+ /* Serialize path and its components creation. */
+ mutex_lock(&adev->path_mutex);
+ /* Satisfy needs of avs_path_find_tplg(). */
+ mutex_lock(&adev->comp_list_mutex);
+
+ path = avs_path_create_unlocked(adev, dma_id, variant);
+ if (IS_ERR(path))
+ goto exit;
+
+ ret = avs_condpaths_walk_all(adev, path);
+ if (ret) {
+ avs_path_free_unlocked(path);
+ path = ERR_PTR(ret);
+ }
+
+exit:
+ mutex_unlock(&adev->comp_list_mutex);
+ mutex_unlock(&adev->path_mutex);
+
+ return path;
+}
+
+static int avs_path_bind_prepare(struct avs_dev *adev,
+ struct avs_path_binding *binding)
+{
+ const struct avs_audio_format *src_fmt, *sink_fmt;
+ struct avs_tplg_module *tsource = binding->source->template;
+ struct avs_path_module *source = binding->source;
+ int ret;
+
+ /*
+ * only copier modules about to be bound
+ * to output pin other than 0 need preparation
+ */
+ if (!binding->source_pin)
+ return 0;
+ if (!guid_equal(&tsource->cfg_ext->type, &AVS_COPIER_MOD_UUID))
+ return 0;
+
+ src_fmt = tsource->in_fmt;
+ sink_fmt = binding->sink->template->in_fmt;
+
+ ret = avs_ipc_copier_set_sink_format(adev, source->module_id,
+ source->instance_id, binding->source_pin,
+ src_fmt, sink_fmt);
+ if (ret) {
+ dev_err(adev->dev, "config copier failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+
+ return 0;
+}
+
+int avs_path_bind(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_binding *binding;
+
+ list_for_each_entry(binding, &ppl->binding_list, node) {
+ struct avs_path_module *source, *sink;
+
+ source = binding->source;
+ sink = binding->sink;
+
+ ret = avs_path_bind_prepare(adev, binding);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_ipc_bind(adev, source->module_id,
+ source->instance_id, sink->module_id,
+ sink->instance_id, binding->sink_pin,
+ binding->source_pin);
+ if (ret) {
+ dev_err(adev->dev, "bind path failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int avs_path_unbind(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_binding *binding;
+
+ list_for_each_entry(binding, &ppl->binding_list, node) {
+ struct avs_path_module *source, *sink;
+
+ source = binding->source;
+ sink = binding->sink;
+
+ ret = avs_ipc_unbind(adev, source->module_id,
+ source->instance_id, sink->module_id,
+ sink->instance_id, binding->sink_pin,
+ binding->source_pin);
+ if (ret) {
+ dev_err(adev->dev, "unbind path failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int avs_path_reset(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ if (path->state == AVS_PPL_STATE_RESET)
+ return 0;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+ AVS_PPL_STATE_RESET);
+ if (ret) {
+ dev_err(adev->dev, "reset path failed: %d\n", ret);
+ path->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ path->state = AVS_PPL_STATE_RESET;
+ return 0;
+}
+
+static int avs_condpath_pause(struct avs_dev *adev, struct avs_path *cpath)
+{
+ struct avs_path_pipeline *ppl;
+ int ret;
+
+ if (cpath->state == AVS_PPL_STATE_PAUSED)
+ return 0;
+
+ list_for_each_entry_reverse(ppl, &cpath->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_PAUSED);
+ if (ret) {
+ dev_err(adev->dev, "pause cpath failed: %d\n", ret);
+ cpath->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ cpath->state = AVS_PPL_STATE_PAUSED;
+ return 0;
+}
+
+static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path)
+{
+ struct avs_path *cpath;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* If either source or sink stops, so do the attached conditional paths. */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ avs_condpath_pause(adev, cpath);
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ avs_condpath_pause(adev, cpath);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
+int avs_path_pause(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ if (path->state == AVS_PPL_STATE_PAUSED)
+ return 0;
+
+ avs_condpaths_pause(adev, path);
+
+ list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+ AVS_PPL_STATE_PAUSED);
+ if (ret) {
+ dev_err(adev->dev, "pause path failed: %d\n", ret);
+ path->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ path->state = AVS_PPL_STATE_PAUSED;
+ return 0;
+}
+
+static int avs_condpath_run(struct avs_dev *adev, struct avs_path *cpath, int trigger)
+{
+ struct avs_path_pipeline *ppl;
+ int ret;
+
+ if (cpath->state == AVS_PPL_STATE_RUNNING)
+ return 0;
+
+ list_for_each_entry(ppl, &cpath->ppl_list, node) {
+ if (ppl->template->cfg->trigger != trigger)
+ continue;
+
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_RUNNING);
+ if (ret) {
+ dev_err(adev->dev, "run cpath failed: %d\n", ret);
+ cpath->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ cpath->state = AVS_PPL_STATE_RUNNING;
+ return 0;
+}
+
+static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int trigger)
+{
+ struct avs_path *cpath;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* Run conditional paths only if source and sink are both running. */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_condpath_run(adev, cpath, trigger);
+
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_condpath_run(adev, cpath, trigger);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
+int avs_path_run(struct avs_path *path, int trigger)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ if (path->state == AVS_PPL_STATE_RUNNING && trigger == AVS_TPLG_TRIGGER_AUTO)
+ return 0;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ if (ppl->template->cfg->trigger != trigger)
+ continue;
+
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+ AVS_PPL_STATE_RUNNING);
+ if (ret) {
+ dev_err(adev->dev, "run path failed: %d\n", ret);
+ path->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ path->state = AVS_PPL_STATE_RUNNING;
+
+ /* Granular pipeline triggering not intended for conditional paths. */
+ if (trigger == AVS_TPLG_TRIGGER_AUTO)
+ avs_condpaths_run(adev, path, trigger);
+
+ return 0;
+}
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
new file mode 100644
index 000000000000..ceb89971a902
--- /dev/null
+++ b/sound/soc/intel/avs/path.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_PATH_H
+#define __SOUND_SOC_INTEL_AVS_PATH_H
+
+#include <linux/list.h>
+#include "avs.h"
+#include "topology.h"
+
+#define AVS_COND_TYPE_NONE 0
+#define AVS_COND_TYPE_AECREF 1
+
+struct avs_path {
+ u32 dma_id;
+ struct list_head ppl_list;
+ u32 state;
+
+ /* condpath navigation for standard paths */
+ struct list_head source_list;
+ struct list_head sink_list;
+
+ /* conditional path fields */
+ struct avs_path *source;
+ struct avs_path *sink;
+ struct list_head source_node;
+ struct list_head sink_node;
+
+ struct avs_tplg_path *template;
+ struct avs_dev *owner;
+ /* device path management */
+ struct list_head node;
+};
+
+struct avs_path_pipeline {
+ u8 instance_id;
+ struct list_head mod_list;
+ struct list_head binding_list;
+
+ struct avs_tplg_pipeline *template;
+ struct avs_path *owner;
+ /* path pipelines management */
+ struct list_head node;
+};
+
+struct avs_path_module {
+ u16 module_id;
+ u8 instance_id;
+ union avs_gtw_attributes gtw_attrs;
+
+ struct avs_tplg_module *template;
+ struct avs_path_pipeline *owner;
+ /* pipeline modules management */
+ struct list_head node;
+};
+
+struct avs_path_binding {
+ struct avs_path_module *source;
+ u8 source_pin;
+ struct avs_path_module *sink;
+ u8 sink_pin;
+
+ struct avs_tplg_binding *template;
+ struct avs_path_pipeline *owner;
+ /* pipeline bindings management */
+ struct list_head node;
+};
+
+void avs_path_free(struct avs_path *path);
+struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params);
+int avs_path_bind(struct avs_path *path);
+int avs_path_unbind(struct avs_path *path);
+int avs_path_reset(struct avs_path *path);
+int avs_path_pause(struct avs_path *path);
+int avs_path_run(struct avs_path *path, int trigger);
+
+int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_constraint_list *rate_list,
+ struct snd_pcm_hw_constraint_list *channels_list,
+ struct snd_pcm_hw_constraint_list *sample_bits_list);
+
+int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input);
+int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input);
+
+#endif
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
new file mode 100644
index 000000000000..4a6deb599c88
--- /dev/null
+++ b/sound/soc/intel/avs/pcm.c
@@ -0,0 +1,1773 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+#include "path.h"
+#include "pcm.h"
+#include "topology.h"
+#include "utils.h"
+#include "../../codecs/hda.h"
+
+struct avs_dma_data {
+ struct avs_tplg_path_template *template;
+ struct avs_path *path;
+ struct avs_dev *adev;
+
+ /* LINK-stream utilized in BE operations while HOST in FE ones. */
+ union {
+ struct hdac_ext_stream *link_stream;
+ struct hdac_ext_stream *host_stream;
+ };
+
+ struct snd_pcm_hw_constraint_list rate_list;
+ struct snd_pcm_hw_constraint_list channels_list;
+ struct snd_pcm_hw_constraint_list sample_bits_list;
+
+ struct work_struct period_elapsed_work;
+ struct hdac_ext_link *link;
+ struct snd_pcm_substream *substream;
+};
+
+static struct avs_tplg_path_template *
+avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
+{
+ struct snd_soc_dapm_widget *dw = snd_soc_dai_get_widget(dai, direction);
+ struct snd_soc_dapm_path *dp;
+ enum snd_soc_dapm_direction dir;
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN;
+ } else {
+ dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT;
+ }
+
+ dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]);
+ if (!dp)
+ return NULL;
+
+ /* Get the other widget, with actual path template data */
+ dw = (dp->source == dw) ? dp->sink : dp->source;
+
+ return dw->priv;
+}
+
+static void avs_period_elapsed_work(struct work_struct *work)
+{
+ struct avs_dma_data *data = container_of(work, struct avs_dma_data, period_elapsed_work);
+
+ snd_pcm_period_elapsed(data->substream);
+}
+
+void avs_period_elapsed(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
+
+ schedule_work(&data->period_elapsed_work);
+}
+
+static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule);
+static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hw_constraint_list *r, *c, *s;
+ struct avs_dma_data *data;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ r = &(data->rate_list);
+ c = &(data->channels_list);
+ s = &(data->sample_bits_list);
+
+ ret = avs_path_set_constraint(data->adev, data->template, r, c, s);
+ if (ret <= 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, r);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, c);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct avs_tplg_path_template *template;
+ struct avs_dma_data *data;
+
+ template = avs_dai_find_path_template(dai, !rtd->dai_link->no_pcm, substream->stream);
+ if (!template) {
+ dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n",
+ snd_pcm_stream_str(substream), dai->name);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->substream = substream;
+ data->template = template;
+ data->adev = adev;
+ INIT_WORK(&data->period_elapsed_work, avs_period_elapsed_work);
+ snd_soc_dai_set_dma_data(dai, substream, data);
+
+ if (rtd->dai_link->ignore_suspend)
+ adev->num_lp_paths++;
+
+ return avs_hw_constraints_init(substream, dai);
+}
+
+static void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (rtd->dai_link->ignore_suspend)
+ data->adev->num_lp_paths--;
+
+ kfree(data->rate_list.list);
+ kfree(data->channels_list.list);
+ kfree(data->sample_bits_list.list);
+
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ kfree(data);
+}
+
+static int avs_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *fe_hw_params,
+ struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+ int dma_id)
+{
+ struct avs_dma_data *data;
+ struct avs_path *path;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p",
+ __func__, substream, substream->runtime);
+ dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+ params_rate(fe_hw_params), params_channels(fe_hw_params),
+ params_width(fe_hw_params), params_physical_width(fe_hw_params));
+
+ dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p",
+ __func__, substream, substream->runtime);
+ dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+ params_rate(be_hw_params), params_channels(be_hw_params),
+ params_width(be_hw_params), params_physical_width(be_hw_params));
+
+ path = avs_path_create(data->adev, dma_id, data->template, fe_hw_params, be_hw_params);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ dev_err(dai->dev, "create path failed: %d\n", ret);
+ return ret;
+ }
+
+ data->path = path;
+ return 0;
+}
+
+static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+ int dma_id)
+{
+ struct snd_pcm_hw_params *fe_hw_params = NULL;
+ struct snd_soc_pcm_runtime *fe, *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = snd_soc_substream_to_rtd(substream);
+ /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */
+ for_each_dpcm_fe(be, substream->stream, dpcm) {
+ fe = dpcm->fe;
+ fe_hw_params = &fe->dpcm[substream->stream].hw_params;
+ }
+
+ return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id);
+}
+
+static int avs_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ ret = avs_path_reset(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "reset path failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause path failed: %d\n", ret);
+ return ret;
+}
+
+static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ /* Actual port-id comes from topology. */
+ return avs_dai_be_hw_params(substream, hw_params, dai, 0);
+}
+
+static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path) {
+ avs_path_free(data->path);
+ data->path = NULL;
+ }
+
+ return 0;
+}
+
+static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dma_data *data;
+ int ret = 0;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = avs_path_pause(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+ break;
+ }
+
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret < 0)
+ dev_err(dai->dev, "run BE path failed: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
+ .startup = avs_dai_startup,
+ .shutdown = avs_dai_shutdown,
+ .hw_params = avs_dai_nonhda_be_hw_params,
+ .hw_free = avs_dai_nonhda_be_hw_free,
+ .prepare = avs_dai_prepare,
+ .trigger = avs_dai_nonhda_be_trigger,
+};
+
+static int __avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
+ struct hdac_ext_link *link)
+{
+ struct hdac_ext_stream *link_stream;
+ struct avs_dma_data *data;
+ int ret;
+
+ ret = avs_dai_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ link_stream = snd_hdac_ext_stream_assign(&data->adev->base.core, substream,
+ HDAC_EXT_STREAM_TYPE_LINK);
+ if (!link_stream) {
+ avs_dai_shutdown(substream, dai);
+ return -EBUSY;
+ }
+
+ data->link_stream = link_stream;
+ data->link = link;
+ return 0;
+}
+
+static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct hdac_ext_link *link;
+ struct avs_dma_data *data;
+ struct hda_codec *codec;
+ int ret;
+
+ codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
+
+ link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr);
+ if (!link)
+ return -EINVAL;
+
+ ret = __avs_dai_hda_be_startup(substream, dai, link);
+ if (!ret) {
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ substream->runtime->private_data = data->link_stream;
+ }
+
+ return ret;
+}
+
+static int avs_dai_i2shda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct hdac_ext_link *link;
+
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (!link)
+ return -EINVAL;
+ return __avs_dai_hda_be_startup(substream, dai, link);
+}
+
+static int avs_dai_dmichda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct hdac_ext_link *link;
+
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+ if (!link)
+ return -EINVAL;
+ return __avs_dai_hda_be_startup(substream, dai, link);
+}
+
+static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_hdac_ext_stream_release(data->link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ substream->runtime->private_data = NULL;
+ avs_dai_shutdown(substream, dai);
+}
+
+static void avs_dai_althda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_hdac_ext_stream_release(data->link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ avs_dai_shutdown(substream, dai);
+}
+
+static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ return avs_dai_be_hw_params(substream, hw_params, dai,
+ hdac_stream(data->link_stream)->stream_tag - 1);
+}
+
+static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *link_stream;
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ link_stream = data->link_stream;
+ link_stream->link_prepared = false;
+ avs_path_free(data->path);
+ data->path = NULL;
+
+ /* clear link <-> stream mapping */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_bus_link_clear_stream_id(data->link,
+ hdac_stream(link_stream)->stream_tag);
+
+ return 0;
+}
+
+static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream);
+ const struct snd_soc_pcm_stream *stream_info;
+ struct hdac_ext_stream *link_stream;
+ const struct snd_pcm_hw_params *p;
+ struct avs_dma_data *data;
+ unsigned int format_val;
+ unsigned int bits;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ link_stream = data->link_stream;
+ p = &be->dpcm[substream->stream].hw_params;
+
+ if (link_stream->link_prepared)
+ return 0;
+
+ stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
+ bits = snd_hdac_stream_format_bits(params_format(p), params_subformat(p),
+ stream_info->sig_bits);
+ format_val = snd_hdac_stream_format(params_channels(p), bits, params_rate(p));
+
+ snd_hdac_ext_stream_decouple(&data->adev->base.core, link_stream, true);
+ snd_hdac_ext_stream_reset(link_stream);
+ snd_hdac_ext_stream_setup(link_stream, format_val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_bus_link_set_stream_id(data->link,
+ hdac_stream(link_stream)->stream_tag);
+
+ ret = avs_dai_prepare(substream, dai);
+ if (ret)
+ return ret;
+
+ link_stream->link_prepared = true;
+ return 0;
+}
+
+static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dma_data *data;
+ int ret = 0;
+
+ dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_stream_start(data->link_stream);
+
+ ret = avs_path_pause(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+ break;
+ }
+
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret < 0)
+ dev_err(dai->dev, "run BE path failed: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+ snd_hdac_ext_stream_clear(data->link_stream);
+
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
+ .startup = avs_dai_hda_be_startup,
+ .shutdown = avs_dai_hda_be_shutdown,
+ .hw_params = avs_dai_hda_be_hw_params,
+ .hw_free = avs_dai_hda_be_hw_free,
+ .prepare = avs_dai_hda_be_prepare,
+ .trigger = avs_dai_hda_be_trigger,
+};
+
+static const struct snd_soc_dai_ops avs_dai_i2shda_be_ops = {
+ .startup = avs_dai_i2shda_be_startup,
+ .shutdown = avs_dai_althda_be_shutdown,
+ .hw_params = avs_dai_hda_be_hw_params,
+ .hw_free = avs_dai_hda_be_hw_free,
+ .prepare = avs_dai_hda_be_prepare,
+ .trigger = avs_dai_hda_be_trigger,
+};
+
+static const struct snd_soc_dai_ops avs_dai_dmichda_be_ops = {
+ .startup = avs_dai_dmichda_be_startup,
+ .shutdown = avs_dai_althda_be_shutdown,
+ .hw_params = avs_dai_hda_be_hw_params,
+ .hw_free = avs_dai_hda_be_hw_free,
+ .prepare = avs_dai_hda_be_prepare,
+ .trigger = avs_dai_hda_be_trigger,
+};
+
+static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *interval = hw_param_interval(params, rule->var);
+ struct snd_interval to;
+
+ snd_interval_any(&to);
+ to.integer = interval->integer;
+ to.max = interval->max;
+ /*
+ * Commonly 2ms buffer size is used in HDA scenarios whereas 4ms is used
+ * when streaming through GPDMA. Align to the latter to account for both.
+ */
+ to.min = params_rate(params) / 1000 * 4;
+
+ if (rule->var == SNDRV_PCM_HW_PARAM_PERIOD_SIZE)
+ to.min /= params_periods(params);
+
+ return snd_interval_refine(interval, &to);
+}
+
+static int avs_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ /* Avoid wrap-around with wall-clock. */
+ ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000);
+ if (ret < 0)
+ return ret;
+
+ /* Adjust buffer and period size based on the audio format. */
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, hw_rule_param_size, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, hw_rule_param_size, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return 0;
+}
+
+static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream;
+ struct avs_dma_data *data;
+ struct hdac_bus *bus;
+ int ret;
+
+ ret = avs_pcm_hw_constraints_init(substream);
+ if (ret)
+ return ret;
+
+ ret = avs_dai_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ bus = &data->adev->base.core;
+
+ host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST);
+ if (!host_stream) {
+ avs_dai_shutdown(substream, dai);
+ return -EBUSY;
+ }
+
+ data->host_stream = host_stream;
+ snd_pcm_set_sync(substream);
+
+ dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p",
+ __func__, hdac_stream(host_stream)->stream_tag, substream);
+
+ return 0;
+}
+
+static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ disable_work_sync(&data->period_elapsed_work);
+ snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+ avs_dai_shutdown(substream, dai);
+}
+
+static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_hw_params *be_hw_params = NULL;
+ struct snd_soc_pcm_runtime *fe, *be;
+ struct snd_soc_dpcm *dpcm;
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ host_stream = data->host_stream;
+
+ hdac_stream(host_stream)->bufsize = 0;
+ hdac_stream(host_stream)->period_bytes = 0;
+ hdac_stream(host_stream)->format_val = 0;
+
+ fe = snd_soc_substream_to_rtd(substream);
+ /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */
+ for_each_dpcm_be(fe, substream->stream, dpcm) {
+ be = dpcm->be;
+ be_hw_params = &be->dpcm[substream->stream].hw_params;
+ }
+
+ ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai,
+ hdac_stream(host_stream)->stream_tag - 1);
+ if (ret)
+ goto create_err;
+
+ ret = avs_path_bind(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret);
+ goto bind_err;
+ }
+
+ return 0;
+
+bind_err:
+ avs_path_free(data->path);
+ data->path = NULL;
+create_err:
+ snd_pcm_lib_free_pages(substream);
+ return ret;
+}
+
+static int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p",
+ __func__, substream, substream->runtime);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ host_stream = data->host_stream;
+
+ ret = avs_path_unbind(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret);
+
+ avs_path_free(data->path);
+ data->path = NULL;
+ snd_hdac_stream_cleanup(hdac_stream(host_stream));
+ hdac_stream(host_stream)->prepared = false;
+
+ return ret;
+}
+
+static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = __avs_dai_fe_hw_free(substream, dai);
+ snd_pcm_lib_free_pages(substream);
+
+ return ret;
+}
+
+static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ const struct snd_soc_pcm_stream *stream_info;
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ unsigned int format_val;
+ struct hdac_bus *bus;
+ unsigned int bits;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ host_stream = data->host_stream;
+
+ if (runtime->state == SNDRV_PCM_STATE_XRUN)
+ hdac_stream(host_stream)->prepared = false;
+ if (hdac_stream(host_stream)->prepared)
+ return 0;
+
+ bus = hdac_stream(host_stream)->bus;
+ snd_hdac_ext_stream_decouple(bus, data->host_stream, true);
+ snd_hdac_stream_reset(hdac_stream(host_stream));
+
+ stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->sig_bits);
+ format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
+
+ ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_hdac_ext_host_stream_setup(host_stream, false);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_dai_prepare(substream, dai);
+ if (ret)
+ return ret;
+
+ hdac_stream(host_stream)->prepared = true;
+ return 0;
+}
+
+static void avs_hda_stream_start(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+ struct hdac_stream *first_running = NULL;
+ struct hdac_stream *pos;
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ list_for_each_entry(pos, &bus->stream_list, list) {
+ if (pos->running) {
+ if (first_running)
+ break; /* more than one running */
+ first_running = pos;
+ }
+ }
+
+ /*
+ * If host_stream is a CAPTURE stream and will be the only one running,
+ * disable L1SEN to avoid sound clipping.
+ */
+ if (!first_running) {
+ if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, false);
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ return;
+ }
+
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ /*
+ * If host_stream is the first stream to break the rule above,
+ * re-enable L1SEN.
+ */
+ if (list_entry_is_head(pos, &bus->stream_list, list) &&
+ first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, true);
+}
+
+static void avs_hda_stream_stop(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+ struct hdac_stream *first_running = NULL;
+ struct hdac_stream *pos;
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ list_for_each_entry(pos, &bus->stream_list, list) {
+ if (pos == hdac_stream(host_stream))
+ continue; /* ignore stream that is about to be stopped */
+ if (pos->running) {
+ if (first_running)
+ break; /* more than one running */
+ first_running = pos;
+ }
+ }
+
+ /*
+ * If host_stream is a CAPTURE stream and is the only one running,
+ * re-enable L1SEN.
+ */
+ if (!first_running) {
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, true);
+ return;
+ }
+
+ /*
+ * If by stopping host_stream there is only a single, CAPTURE stream running
+ * left, disable L1SEN to avoid sound clipping.
+ */
+ if (list_entry_is_head(pos, &bus->stream_list, list) &&
+ first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, false);
+
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+}
+
+static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ struct hdac_bus *bus;
+ unsigned long flags;
+ int ret = 0;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ host_stream = data->host_stream;
+ bus = hdac_stream(host_stream)->bus;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ avs_hda_stream_start(bus, host_stream);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+ /* Timeout on DRSM poll shall not stop the resume so ignore the result. */
+ if (cmd == SNDRV_PCM_TRIGGER_RESUME)
+ snd_hdac_stream_wait_drsm(hdac_stream(host_stream));
+
+ ret = avs_path_pause(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "pause FE path failed: %d\n", ret);
+ break;
+ }
+
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret < 0)
+ dev_err(dai->dev, "run FE path failed: %d\n", ret);
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause FE path failed: %d\n", ret);
+
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ avs_hda_stream_stop(bus, host_stream);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset FE path failed: %d\n", ret);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+const struct snd_soc_dai_ops avs_dai_fe_ops = {
+ .startup = avs_dai_fe_startup,
+ .shutdown = avs_dai_fe_shutdown,
+ .hw_params = avs_dai_fe_hw_params,
+ .hw_free = avs_dai_fe_hw_free,
+ .prepare = avs_dai_fe_prepare,
+ .trigger = avs_dai_fe_trigger,
+};
+
+static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct snd_soc_component *component = file->private_data;
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ char buf[64];
+ size_t len;
+
+ len = scnprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
+ mach->tplg_filename);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations topology_name_fops = {
+ .open = simple_open,
+ .read = topology_name_read,
+ .llseek = default_llseek,
+};
+
+static int avs_component_load_libraries(struct avs_soc_component *acomp)
+{
+ struct avs_tplg *tplg = acomp->tplg;
+ struct avs_dev *adev = to_avs_dev(acomp->base.dev);
+ int ret;
+
+ if (!tplg->num_libs)
+ return 0;
+
+ /* Parent device may be asleep and library loading involves IPCs. */
+ ret = pm_runtime_resume_and_get(adev->dev);
+ if (ret < 0)
+ return ret;
+
+ avs_hda_power_gating_enable(adev, false);
+ avs_hda_clock_gating_enable(adev, false);
+ avs_hda_l1sen_enable(adev, false);
+
+ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+
+ avs_hda_l1sen_enable(adev, true);
+ avs_hda_clock_gating_enable(adev, true);
+ avs_hda_power_gating_enable(adev, true);
+
+ if (!ret)
+ ret = avs_module_info_init(adev, false);
+
+ pm_runtime_put_autosuspend(adev->dev);
+
+ return ret;
+}
+
+static int avs_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_soc_component *acomp;
+ struct avs_dev *adev;
+ char *filename;
+ int ret;
+
+ dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
+ mach = dev_get_platdata(card->dev);
+ acomp = to_avs_soc_component(component);
+ adev = to_avs_dev(component->dev);
+
+ acomp->tplg = avs_tplg_new(component);
+ if (!acomp->tplg)
+ return -ENOMEM;
+
+ if (!mach->tplg_filename)
+ goto finalize;
+
+ /* Load specified topology and create debugfs for it. */
+ filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
+ mach->tplg_filename);
+ if (!filename)
+ return -ENOMEM;
+
+ ret = avs_load_topology(component, filename);
+ kfree(filename);
+ if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) {
+ unsigned int vendor_id;
+
+ if (sscanf(mach->tplg_filename, "hda-%08x-tplg.bin", &vendor_id) != 1)
+ return ret;
+
+ if (((vendor_id >> 16) & 0xFFFF) == 0x8086)
+ mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+ "hda-8086-generic-tplg.bin");
+ else
+ mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+ "hda-generic-tplg.bin");
+ if (!mach->tplg_filename)
+ return -ENOMEM;
+ filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
+ mach->tplg_filename);
+ if (!filename)
+ return -ENOMEM;
+
+ dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename);
+ ret = avs_load_topology(component, filename);
+ kfree(filename);
+ }
+ if (ret < 0)
+ return ret;
+
+ ret = avs_component_load_libraries(acomp);
+ if (ret < 0) {
+ dev_err(card->dev, "libraries loading failed: %d\n", ret);
+ goto err_load_libs;
+ }
+
+finalize:
+ debugfs_create_file("topology_name", 0444, component->debugfs_root, component,
+ &topology_name_fops);
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_add_tail(&acomp->node, &adev->comp_list);
+ mutex_unlock(&adev->comp_list_mutex);
+
+ return 0;
+
+err_load_libs:
+ avs_remove_topology(component);
+ return ret;
+}
+
+static void avs_component_remove(struct snd_soc_component *component)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(component);
+ struct snd_soc_acpi_mach *mach;
+ struct avs_dev *adev = to_avs_dev(component->dev);
+ int ret;
+
+ mach = dev_get_platdata(component->card->dev);
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_del(&acomp->node);
+ mutex_unlock(&adev->comp_list_mutex);
+
+ if (mach->tplg_filename) {
+ ret = avs_remove_topology(component);
+ if (ret < 0)
+ dev_err(component->dev, "unload topology failed: %d\n", ret);
+ }
+}
+
+static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret;
+
+ substream = data->substream;
+ rtd = snd_soc_substream_to_rtd(substream);
+
+ ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai);
+ if (ret)
+ dev_err(dai->dev, "hw_params on resume failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ struct hdac_ext_stream *host_stream;
+ struct hdac_stream *hstream;
+ struct hdac_bus *bus;
+ int ret;
+
+ host_stream = data->host_stream;
+ hstream = hdac_stream(host_stream);
+ bus = hdac_stream(host_stream)->bus;
+
+ /* Set DRSM before programming stream and position registers. */
+ snd_hdac_stream_drsm_enable(bus, true, hstream->index);
+
+ ret = dai->driver->ops->prepare(data->substream, dai);
+ if (ret) {
+ dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret);
+ return ret;
+ }
+
+ writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL);
+ writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU);
+ writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL);
+ writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU);
+
+ /* As per HW spec recommendation, program LPIB and DPIB to the same value. */
+ snd_hdac_stream_set_lpib(hstream, hstream->lpib);
+ snd_hdac_stream_set_dpibr(bus, hstream, hstream->lpib);
+
+ return 0;
+}
+
+static int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ int ret;
+
+ ret = dai->driver->ops->prepare(data->substream, dai);
+ if (ret)
+ dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ host_stream = data->host_stream;
+
+ /* Store position addresses so we can resume from them later on. */
+ hdac_stream(host_stream)->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream));
+ host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL);
+ host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU);
+ host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL);
+ host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU);
+
+ ret = __avs_dai_fe_hw_free(data->substream, dai);
+ if (ret < 0)
+ dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data)
+{
+ int ret;
+
+ ret = dai->driver->ops->hw_free(data->substream, dai);
+ if (ret < 0)
+ dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret);
+
+ return ret;
+}
+
+static int avs_component_pm_op(struct snd_soc_component *component, bool be,
+ int (*op)(struct snd_soc_dai *, struct avs_dma_data *))
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct avs_dma_data *data;
+ struct snd_soc_dai *dai;
+ int ret;
+
+ for_each_component_dais(component, dai) {
+ data = snd_soc_dai_dma_data_get_playback(dai);
+ if (data) {
+ rtd = snd_soc_substream_to_rtd(data->substream);
+ if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
+ ret = op(dai, data);
+ if (ret < 0) {
+ __snd_pcm_set_state(data->substream->runtime,
+ SNDRV_PCM_STATE_DISCONNECTED);
+ return ret;
+ }
+ }
+ }
+
+ data = snd_soc_dai_dma_data_get_capture(dai);
+ if (data) {
+ rtd = snd_soc_substream_to_rtd(data->substream);
+ if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
+ ret = op(dai, data);
+ if (ret < 0) {
+ __snd_pcm_set_state(data->substream->runtime,
+ SNDRV_PCM_STATE_DISCONNECTED);
+ return ret;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int avs_component_resume_hw_params(struct snd_soc_component *component, bool be)
+{
+ return avs_component_pm_op(component, be, &avs_dai_resume_hw_params);
+}
+
+static int avs_component_resume_prepare(struct snd_soc_component *component, bool be)
+{
+ int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data);
+
+ if (be)
+ prepare_cb = &avs_dai_resume_be_prepare;
+ else
+ prepare_cb = &avs_dai_resume_fe_prepare;
+
+ return avs_component_pm_op(component, be, prepare_cb);
+}
+
+static int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be)
+{
+ int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data);
+
+ if (be)
+ hw_free_cb = &avs_dai_suspend_be_hw_free;
+ else
+ hw_free_cb = &avs_dai_suspend_fe_hw_free;
+
+ return avs_component_pm_op(component, be, hw_free_cb);
+}
+
+static int avs_component_suspend(struct snd_soc_component *component)
+{
+ int ret;
+
+ /*
+ * When freeing paths, FEs need to be first as they perform
+ * path unbinding.
+ */
+ ret = avs_component_suspend_hw_free(component, false);
+ if (ret)
+ return ret;
+
+ return avs_component_suspend_hw_free(component, true);
+}
+
+static int avs_component_resume(struct snd_soc_component *component)
+{
+ int ret;
+
+ /*
+ * When creating paths, FEs need to be last as they perform
+ * path binding.
+ */
+ ret = avs_component_resume_hw_params(component, true);
+ if (ret)
+ return ret;
+
+ ret = avs_component_resume_hw_params(component, false);
+ if (ret)
+ return ret;
+
+ /* It is expected that the LINK stream is prepared first. */
+ ret = avs_component_resume_prepare(component, true);
+ if (ret)
+ return ret;
+
+ return avs_component_resume_prepare(component, false);
+}
+
+static const struct snd_pcm_hardware avs_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+static int avs_component_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ /* only FE DAI links are handled here */
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware);
+}
+
+static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream)
+{
+ return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index));
+}
+
+static snd_pcm_uframes_t
+avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ unsigned int pos;
+
+ data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ if (!data->host_stream)
+ return 0;
+
+ host_stream = data->host_stream;
+ pos = avs_hda_stream_dpib_read(host_stream);
+
+ if (pos >= hdac_stream(host_stream)->bufsize)
+ pos = 0;
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int avs_component_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+static int avs_component_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_pcm *pcm = rtd->pcm;
+
+ if (dai->driver->playback.channels_min)
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+ MAX_PREALLOC_SIZE);
+
+ if (dai->driver->capture.channels_min)
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+ MAX_PREALLOC_SIZE);
+
+ return 0;
+}
+
+static struct snd_soc_component_driver avs_component_driver = {
+ .name = "avs-pcm",
+ .probe = avs_component_probe,
+ .remove = avs_component_remove,
+ .suspend = avs_component_suspend,
+ .resume = avs_component_resume,
+ .open = avs_component_open,
+ .pointer = avs_component_pointer,
+ .mmap = avs_component_mmap,
+ .pcm_construct = avs_component_construct,
+ .module_get_upon_open = 1, /* increment refcount when a pcm is opened */
+ .topology_name_prefix = "intel/avs",
+};
+
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+{
+ struct avs_soc_component *acomp;
+ int ret;
+
+ acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL);
+ if (!acomp)
+ return -ENOMEM;
+
+ acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL);
+ if (!acomp->base.name)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&acomp->node);
+
+ drv->use_dai_pcm_id = !obsolete_card_names;
+
+ ret = snd_soc_component_initialize(&acomp->base, drv, dev);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
+}
+
+static struct snd_soc_dai_driver dmic_cpu_dais[] = {
+{
+ .name = "DMIC Pin",
+ .capture = {
+ .stream_name = "DMIC Rx",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "DMIC WoV Pin",
+ .capture = {
+ .stream_name = "DMIC WoV Rx",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+int avs_register_dmic_component(struct avs_dev *adev, const char *name)
+{
+ const struct snd_soc_dai_ops *ops;
+
+ if (avs_platattr_test(adev, ALTHDA))
+ ops = &avs_dai_dmichda_be_ops;
+ else
+ ops = &avs_dai_nonhda_be_ops;
+
+ dmic_cpu_dais[0].ops = ops;
+ dmic_cpu_dais[1].ops = ops;
+ return avs_register_component(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
+ ARRAY_SIZE(dmic_cpu_dais));
+}
+
+static const struct snd_soc_dai_driver i2s_dai_template = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = AVS_CHANNELS_MAX,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_12000 |
+ SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_128000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = AVS_CHANNELS_MAX,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_12000 |
+ SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_128000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+};
+
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms)
+{
+ struct snd_soc_dai_driver *cpus, *dai;
+ const struct snd_soc_dai_ops *ops;
+ size_t ssp_count, cpu_count;
+ int i, j;
+
+ ssp_count = adev->hw_cfg.i2s_caps.ctrl_count;
+ if (avs_platattr_test(adev, ALTHDA))
+ ops = &avs_dai_i2shda_be_ops;
+ else
+ ops = &avs_dai_nonhda_be_ops;
+
+ cpu_count = 0;
+ for_each_set_bit(i, &port_mask, ssp_count)
+ if (!tdms || test_bit(0, &tdms[i]))
+ cpu_count++;
+ if (tdms)
+ for_each_set_bit(i, &port_mask, ssp_count)
+ cpu_count += hweight_long(tdms[i]);
+
+ cpus = devm_kcalloc(adev->dev, cpu_count, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ dai = cpus;
+ for_each_set_bit(i, &port_mask, ssp_count) {
+ if (!tdms || test_bit(0, &tdms[i])) {
+ memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
+
+ if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+ return -ENOMEM;
+ dai->ops = ops;
+ dai++;
+ }
+ }
+
+ if (!tdms)
+ goto plat_register;
+
+ for_each_set_bit(i, &port_mask, ssp_count) {
+ for_each_set_bit(j, &tdms[i], AVS_CHANNELS_MAX) {
+ memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j);
+
+ if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+ return -ENOMEM;
+ dai->ops = ops;
+ dai++;
+ }
+ }
+
+plat_register:
+ return avs_register_component(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+}
+
+/* HD-Audio CPU DAI template */
+static const struct snd_soc_dai_driver hda_cpu_dai = {
+ .ops = &avs_dai_hda_be_ops,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = AVS_CHANNELS_MAX,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = AVS_CHANNELS_MAX,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+};
+
+static void avs_component_hda_unregister_dais(struct snd_soc_component *component)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_dai *dai, *save;
+ struct avs_mach_pdata *pdata;
+ struct hda_codec *codec;
+ char name[32];
+
+ mach = dev_get_platdata(component->card->dev);
+ pdata = mach->pdata;
+ codec = pdata->codec;
+ snprintf(name, sizeof(name), "%s-cpu", dev_name(&codec->core.dev));
+
+ for_each_component_dais_safe(component, dai, save) {
+ int stream;
+
+ if (!strstr(dai->driver->name, name))
+ continue;
+
+ for_each_pcm_streams(stream)
+ snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream));
+
+ snd_soc_unregister_dai(dai);
+ }
+}
+
+static int avs_component_hda_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_dai_driver *dais;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ const char *cname;
+ int pcm_count = 0, ret, i;
+
+ mach = dev_get_platdata(component->card->dev);
+ if (!mach)
+ return -EINVAL;
+
+ pdata = mach->pdata;
+ codec = pdata->codec;
+ if (list_empty(&codec->pcm_list_head))
+ return -EINVAL;
+ list_for_each_entry(pcm, &codec->pcm_list_head, list)
+ pcm_count++;
+
+ dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais),
+ GFP_KERNEL);
+ if (!dais)
+ return -ENOMEM;
+
+ cname = dev_name(&codec->core.dev);
+ dapm = snd_soc_component_to_dapm(component);
+ pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+ for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+ struct snd_soc_dai *dai;
+
+ memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais));
+ dais[i].id = i;
+ dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL,
+ "%s-cpu%d", cname, i);
+ if (!dais[i].name) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (pcm->stream[0].substreams) {
+ dais[i].playback.stream_name =
+ devm_kasprintf(component->dev, GFP_KERNEL,
+ "%s-cpu%d Tx", cname, i);
+ if (!dais[i].playback.stream_name) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (!hda_codec_is_display(codec)) {
+ dais[i].playback.formats = pcm->stream[0].formats;
+ dais[i].playback.subformats = pcm->stream[0].subformats;
+ dais[i].playback.rates = pcm->stream[0].rates;
+ dais[i].playback.channels_min = pcm->stream[0].channels_min;
+ dais[i].playback.channels_max = pcm->stream[0].channels_max;
+ dais[i].playback.sig_bits = pcm->stream[0].maxbps;
+ }
+ }
+
+ if (pcm->stream[1].substreams) {
+ dais[i].capture.stream_name =
+ devm_kasprintf(component->dev, GFP_KERNEL,
+ "%s-cpu%d Rx", cname, i);
+ if (!dais[i].capture.stream_name) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (!hda_codec_is_display(codec)) {
+ dais[i].capture.formats = pcm->stream[1].formats;
+ dais[i].capture.subformats = pcm->stream[1].subformats;
+ dais[i].capture.rates = pcm->stream[1].rates;
+ dais[i].capture.channels_min = pcm->stream[1].channels_min;
+ dais[i].capture.channels_max = pcm->stream[1].channels_max;
+ dais[i].capture.sig_bits = pcm->stream[1].maxbps;
+ }
+ }
+
+ dai = snd_soc_register_dai(component, &dais[i], false);
+ if (!dai) {
+ dev_err(component->dev, "register dai for %s failed\n",
+ pcm->name);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+ if (ret < 0) {
+ dev_err(component->dev, "create widgets failed: %d\n",
+ ret);
+ snd_soc_unregister_dai(dai);
+ goto exit;
+ }
+ }
+
+ ret = avs_component_probe(component);
+exit:
+ if (ret)
+ avs_component_hda_unregister_dais(component);
+
+ return ret;
+}
+
+static void avs_component_hda_remove(struct snd_soc_component *component)
+{
+ avs_component_remove(component);
+ avs_component_hda_unregister_dais(component);
+}
+
+static int avs_component_hda_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ if (!rtd->dai_link->no_pcm) {
+ struct snd_pcm_hardware hwparams = avs_pcm_hardware;
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+ int dir = substream->stream;
+
+ /*
+ * Support the DPCM reparenting while still fulfilling expectations of HDAudio
+ * common code - a valid stream pointer at substream->runtime->private_data -
+ * by having all FEs point to the same private data.
+ */
+ for_each_dpcm_be(rtd, dir, dpcm) {
+ struct snd_pcm_substream *be_substream;
+
+ be = dpcm->be;
+ if (be->dpcm[dir].users == 1)
+ break;
+
+ be_substream = snd_soc_dpcm_get_substream(be, dir);
+ substream->runtime->private_data = be_substream->runtime->private_data;
+ break;
+ }
+
+ /* RESUME unsupported for de-coupled HD-Audio capture. */
+ if (dir == SNDRV_PCM_STREAM_CAPTURE)
+ hwparams.info &= ~SNDRV_PCM_INFO_RESUME;
+
+ return snd_soc_set_runtime_hwparams(substream, &hwparams);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_component_driver avs_hda_component_driver = {
+ .name = "avs-hda-pcm",
+ .probe = avs_component_hda_probe,
+ .remove = avs_component_hda_remove,
+ .suspend = avs_component_suspend,
+ .resume = avs_component_resume,
+ .open = avs_component_hda_open,
+ .pointer = avs_component_pointer,
+ .mmap = avs_component_mmap,
+ .pcm_construct = avs_component_construct,
+ /*
+ * hda platform component's probe() is dependent on
+ * codec->pcm_list_head, it needs to be initialized after codec
+ * component. remove_order is here for completeness sake
+ */
+ .probe_order = SND_SOC_COMP_ORDER_LATE,
+ .remove_order = SND_SOC_COMP_ORDER_EARLY,
+ .module_get_upon_open = 1,
+ .topology_name_prefix = "intel/avs",
+};
+
+int avs_register_hda_component(struct avs_dev *adev, const char *name)
+{
+ return avs_register_component(adev->dev, name, &avs_hda_component_driver, NULL, 0);
+}
diff --git a/sound/soc/intel/avs/pcm.h b/sound/soc/intel/avs/pcm.h
new file mode 100644
index 000000000000..0f3615c90398
--- /dev/null
+++ b/sound/soc/intel/avs/pcm.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2024 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_PCM_H
+#define __SOUND_SOC_INTEL_AVS_PCM_H
+
+#include <sound/pcm.h>
+
+void avs_period_elapsed(struct snd_pcm_substream *substream);
+
+#endif
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
new file mode 100644
index 000000000000..74096236984a
--- /dev/null
+++ b/sound/soc/intel/avs/probes.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <sound/compress_driver.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hdaudio.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+
+static int avs_dsp_init_probe(struct avs_dev *adev, struct snd_compr_params *params, int bps,
+ union avs_connector_node_id node_id, size_t buffer_size)
+{
+ struct avs_probe_cfg cfg = {{0}};
+ struct avs_module_entry mentry;
+ u8 dummy;
+ int ret;
+
+ ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
+ if (ret)
+ return ret;
+
+ /*
+ * Probe module uses no cycles, input and output frame sizes are unused.
+ * It is also not owned by any pipeline.
+ */
+ cfg.base.ibs = 1;
+ /* BSS module descriptor is always segment of index=2. */
+ cfg.base.is_pages = mentry.segments[2].flags.length;
+ cfg.base.audio_fmt.sampling_freq = params->codec.sample_rate;
+ cfg.base.audio_fmt.bit_depth = bps;
+ cfg.base.audio_fmt.num_channels = params->codec.ch_out;
+ cfg.base.audio_fmt.valid_bit_depth = bps;
+ cfg.gtw_cfg.node_id = node_id;
+ cfg.gtw_cfg.dma_buffer_size = buffer_size;
+
+ return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg,
+ sizeof(cfg), &dummy);
+}
+
+static void avs_dsp_delete_probe(struct avs_dev *adev)
+{
+ struct avs_module_entry mentry;
+ int ret;
+
+ ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
+ if (!ret)
+ /* There is only ever one probe module instance. */
+ avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0);
+}
+
+static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream)
+{
+ return cstream->runtime->private_data;
+}
+
+static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_stream *host_stream;
+
+ if (adev->extractor) {
+ dev_err(dai->dev, "Cannot open more than one extractor stream\n");
+ return -EEXIST;
+ }
+
+ host_stream = snd_hdac_ext_cstream_assign(bus, cstream);
+ if (!host_stream) {
+ dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n");
+ return -EBUSY;
+ }
+
+ adev->extractor = host_stream;
+ hdac_stream(host_stream)->curr_pos = 0;
+ cstream->runtime->private_data = host_stream;
+
+ return 0;
+}
+
+static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct avs_probe_point_desc *desc;
+ /* Extractor node identifier. */
+ unsigned int vindex = INVALID_NODE_ID.vindex;
+ size_t num_desc;
+ int i, ret;
+
+ /* Disconnect all probe points. */
+ ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
+ if (ret) {
+ dev_err(dai->dev, "get probe points failed: %d\n", ret);
+ ret = AVS_IPC_RET(ret);
+ goto exit;
+ }
+
+ for (i = 0; i < num_desc; i++)
+ if (desc[i].node_id.vindex == vindex)
+ avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1);
+ kfree(desc);
+
+exit:
+ if (adev->num_probe_streams) {
+ adev->num_probe_streams--;
+ if (!adev->num_probe_streams) {
+ avs_dsp_delete_probe(adev);
+ avs_dsp_enable_d0ix(adev);
+ }
+ }
+
+ snd_hdac_stream_cleanup(hdac_stream(host_stream));
+ hdac_stream(host_stream)->prepared = 0;
+ snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+
+ snd_compr_free_pages(cstream);
+ adev->extractor = NULL;
+
+ return ret;
+}
+
+static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ unsigned int format_val;
+ int bps, ret;
+
+ hdac_stream(host_stream)->bufsize = 0;
+ hdac_stream(host_stream)->period_bytes = 0;
+ hdac_stream(host_stream)->format_val = 0;
+ cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
+ cstream->dma_buffer.dev.dev = adev->dev;
+
+ ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
+ if (ret < 0)
+ return ret;
+ bps = snd_pcm_format_physical_width(params->codec.format);
+ if (bps < 0)
+ return bps;
+ format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate);
+ ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
+ if (ret < 0)
+ return ret;
+ ret = snd_hdac_stream_setup(hdac_stream(host_stream), false);
+ if (ret < 0)
+ return ret;
+
+ hdac_stream(host_stream)->prepared = 1;
+
+ if (!adev->num_probe_streams) {
+ union avs_connector_node_id node_id;
+
+ /* D0ix not allowed during probing. */
+ ret = avs_dsp_disable_d0ix(adev);
+ if (ret)
+ return ret;
+
+ node_id.vindex = hdac_stream(host_stream)->stream_tag - 1;
+ node_id.dma_type = AVS_DMA_HDA_HOST_INPUT;
+
+ ret = avs_dsp_init_probe(adev, params, bps, node_id, rtd->dma_bytes);
+ if (ret < 0) {
+ dev_err(dai->dev, "probe init failed: %d\n", ret);
+ avs_dsp_enable_d0ix(adev);
+ return ret;
+ }
+ }
+
+ adev->num_probe_streams++;
+ return 0;
+}
+
+static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct hdac_bus *bus = &adev->base.core;
+ unsigned long cookie;
+
+ if (!hdac_stream(host_stream)->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int avs_probe_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp64 *tstamp, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct snd_soc_pcm_stream *pstream;
+
+ pstream = &dai->driver->capture;
+ tstamp->copied_total = hdac_stream(host_stream)->curr_pos;
+ tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
+
+ return 0;
+}
+
+static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ unsigned int offset, n;
+ void *ptr;
+ int ret;
+
+ if (count > rtd->buffer_size)
+ count = rtd->buffer_size;
+
+ div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
+ ptr = rtd->dma_area + offset;
+ n = rtd->buffer_size - offset;
+
+ if (count < n) {
+ ret = copy_to_user(buf, ptr, count);
+ } else {
+ ret = copy_to_user(buf, ptr, n);
+ ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+ }
+
+ if (ret)
+ return count - ret;
+ return count;
+}
+
+static const struct snd_soc_cdai_ops avs_probe_cdai_ops = {
+ .startup = avs_probe_compr_open,
+ .shutdown = avs_probe_compr_free,
+ .set_params = avs_probe_compr_set_params,
+ .trigger = avs_probe_compr_trigger,
+ .pointer = avs_probe_compr_pointer,
+};
+
+static const struct snd_soc_dai_ops avs_probe_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static const struct snd_compress_ops avs_probe_compress_ops = {
+ .copy = avs_probe_compr_copy,
+};
+
+static struct snd_soc_dai_driver probe_cpu_dais[] = {
+{
+ .name = "Probe Extraction CPU DAI",
+ .cops = &avs_probe_cdai_ops,
+ .ops = &avs_probe_dai_ops,
+ .capture = {
+ .stream_name = "Probe Extraction",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+},
+};
+
+static const struct snd_soc_component_driver avs_probe_component_driver = {
+ .name = "avs-probe-compr",
+ .compress_ops = &avs_probe_compress_ops,
+ .module_get_upon_open = 1, /* increment refcount when a stream is opened */
+};
+
+int avs_register_probe_component(struct avs_dev *adev, const char *name)
+{
+ struct snd_soc_component *component;
+ int ret;
+
+ component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+
+ component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL);
+ if (!component->name)
+ return -ENOMEM;
+
+ ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev);
+ if (ret)
+ return ret;
+
+ return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
+}
diff --git a/sound/soc/intel/avs/ptl.c b/sound/soc/intel/avs/ptl.c
new file mode 100644
index 000000000000..07da9b0aa2b8
--- /dev/null
+++ b/sound/soc/intel/avs/ptl.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2024-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "debug.h"
+#include "registers.h"
+#include "trace.h"
+
+#define MTL_HfDSSGBL_BASE 0x1000
+#define MTL_REG_HfDSSCS (MTL_HfDSSGBL_BASE + 0x0)
+#define MTL_HfDSSCS_SPA BIT(16)
+#define MTL_HfDSSCS_CPA BIT(24)
+
+#define MTL_DSPCS_BASE 0x178D00
+#define MTL_REG_DSPCCTL (MTL_DSPCS_BASE + 0x4)
+#define MTL_DSPCCTL_OSEL GENMASK(25, 24)
+#define MTL_DSPCCTL_OSEL_HOST BIT(25)
+
+static int avs_ptl_core_power_on(struct avs_dev *adev)
+{
+ u32 reg;
+ int ret;
+
+ /* Power up DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, MTL_HfDSSCS_SPA);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "power dsp", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == MTL_HfDSSCS_CPA,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+ if (ret) {
+ dev_err(adev->dev, "power on domain dsp failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Prevent power gating of DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL2, MTL_HfPWRCTL2_WPDSPHPxPG,
+ MTL_HfPWRCTL2_WPDSPHPxPG);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "prevent dsp PG", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfPWRSTS2, reg,
+ (reg & MTL_HfPWRSTS2_DSPHPxPGS) == MTL_HfPWRSTS2_DSPHPxPGS,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+
+ /* Set ownership to HOST. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_OSEL, MTL_DSPCCTL_OSEL_HOST);
+ return ret;
+}
+
+static int avs_ptl_core_power_off(struct avs_dev *adev)
+{
+ u32 reg;
+
+ /* Allow power gating of DSP domain. No STS polling as HOST is only one of its users. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL2, MTL_HfPWRCTL2_WPDSPHPxPG, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "allow dsp pg", false);
+
+ /* Power down DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "power dsp", false);
+
+ return snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == 0,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+}
+
+static int avs_ptl_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+ if (!core_mask)
+ return 0;
+
+ if (power)
+ return avs_ptl_core_power_on(adev);
+ return avs_ptl_core_power_off(adev);
+}
+
+const struct avs_dsp_ops avs_ptl_dsp_ops = {
+ .power = avs_ptl_core_power,
+ .reset = avs_mtl_core_reset,
+ .stall = avs_lnl_core_stall,
+ .dsp_interrupt = avs_mtl_dsp_interrupt,
+ .int_control = avs_mtl_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .log_buffer_offset = avs_icl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_icl_d0ix_toggle,
+ .set_d0ix = avs_icl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(icl)
+};
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
new file mode 100644
index 000000000000..97767882ffa1
--- /dev/null
+++ b/sound/soc/intel/avs/registers.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_REGS_H
+#define __SOUND_SOC_INTEL_AVS_REGS_H
+
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+
+#define AZX_PCIREG_PGCTL 0x44
+#define AZX_PCIREG_CGCTL 0x48
+#define AZX_PGCTL_LSRMD_MASK BIT(4)
+#define AZX_CGCTL_MISCBDCGE_MASK BIT(6)
+#define AZX_VS_EM2_L1SEN BIT(13)
+#define AZX_VS_EM2_DUM BIT(23)
+
+/* Intel HD Audio General DSP Registers */
+#define AVS_ADSP_GEN_BASE 0x0
+#define AVS_ADSP_REG_ADSPCS (AVS_ADSP_GEN_BASE + 0x04)
+#define AVS_ADSP_REG_ADSPIC (AVS_ADSP_GEN_BASE + 0x08)
+#define AVS_ADSP_REG_ADSPIS (AVS_ADSP_GEN_BASE + 0x0C)
+
+#define AVS_ADSP_ADSPIC_IPC BIT(0)
+#define AVS_ADSP_ADSPIC_CLDMA BIT(1)
+#define AVS_ADSP_ADSPIS_IPC BIT(0)
+#define AVS_ADSP_ADSPIS_CLDMA BIT(1)
+
+#define AVS_ADSPCS_CRST_MASK(cm) (cm)
+#define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8)
+#define AVS_ADSPCS_SPA_MASK(cm) ((cm) << 16)
+#define AVS_ADSPCS_CPA_MASK(cm) ((cm) << 24)
+#define AVS_ADSPCS_INTERVAL_US 500
+#define AVS_ADSPCS_TIMEOUT_US 10000
+#define AVS_MAIN_CORE_MASK BIT(0)
+
+#define AVS_ADSP_HIPCCTL_BUSY BIT(0)
+#define AVS_ADSP_HIPCCTL_DONE BIT(1)
+
+/* SKL Intel HD Audio Inter-Processor Communication Registers */
+#define SKL_ADSP_IPC_BASE 0x40
+#define SKL_ADSP_REG_HIPCT (SKL_ADSP_IPC_BASE + 0x00)
+#define SKL_ADSP_REG_HIPCTE (SKL_ADSP_IPC_BASE + 0x04)
+#define SKL_ADSP_REG_HIPCI (SKL_ADSP_IPC_BASE + 0x08)
+#define SKL_ADSP_REG_HIPCIE (SKL_ADSP_IPC_BASE + 0x0C)
+#define SKL_ADSP_REG_HIPCCTL (SKL_ADSP_IPC_BASE + 0x10)
+
+#define SKL_ADSP_HIPCI_BUSY BIT(31)
+#define SKL_ADSP_HIPCIE_DONE BIT(30)
+#define SKL_ADSP_HIPCT_BUSY BIT(31)
+
+/* CNL Intel HD Audio Inter-Processor Communication Registers */
+#define CNL_ADSP_IPC_BASE 0xC0
+#define CNL_ADSP_REG_HIPCTDR (CNL_ADSP_IPC_BASE + 0x00)
+#define CNL_ADSP_REG_HIPCTDA (CNL_ADSP_IPC_BASE + 0x04)
+#define CNL_ADSP_REG_HIPCTDD (CNL_ADSP_IPC_BASE + 0x08)
+#define CNL_ADSP_REG_HIPCIDR (CNL_ADSP_IPC_BASE + 0x10)
+#define CNL_ADSP_REG_HIPCIDA (CNL_ADSP_IPC_BASE + 0x14)
+#define CNL_ADSP_REG_HIPCIDD (CNL_ADSP_IPC_BASE + 0x18)
+#define CNL_ADSP_REG_HIPCCTL (CNL_ADSP_IPC_BASE + 0x28)
+
+#define CNL_ADSP_HIPCTDR_BUSY BIT(31)
+#define CNL_ADSP_HIPCTDA_DONE BIT(31)
+#define CNL_ADSP_HIPCIDR_BUSY BIT(31)
+#define CNL_ADSP_HIPCIDA_DONE BIT(31)
+
+/* MTL Intel HOST Inter-Processor Communication Registers */
+#define MTL_HfIPC_BASE 0x73000
+#define MTL_REG_HfIPCxTDR (MTL_HfIPC_BASE + 0x200)
+#define MTL_REG_HfIPCxTDA (MTL_HfIPC_BASE + 0x204)
+#define MTL_REG_HfIPCxIDR (MTL_HfIPC_BASE + 0x210)
+#define MTL_REG_HfIPCxIDA (MTL_HfIPC_BASE + 0x214)
+#define MTL_REG_HfIPCxCTL (MTL_HfIPC_BASE + 0x228)
+#define MTL_REG_HfIPCxTDD (MTL_HfIPC_BASE + 0x300)
+#define MTL_REG_HfIPCxIDD (MTL_HfIPC_BASE + 0x380)
+
+#define MTL_HfIPCxTDR_BUSY BIT(31)
+#define MTL_HfIPCxTDA_BUSY BIT(31)
+#define MTL_HfIPCxIDR_BUSY BIT(31)
+#define MTL_HfIPCxIDA_DONE BIT(31)
+
+#define MTL_HfFLV_BASE 0x162000
+#define MTL_REG_HfFLGP(x, y) (MTL_HfFLV_BASE + 0x1200 + (x) * 0x20 + (y) * 0x08)
+#define LNL_REG_HfDFR(x) (0x160200 + (x) * 0x8)
+
+#define MTL_DWICTL_BASE 0x1800
+#define MTL_DWICTL_REG_INTENL (MTL_DWICTL_BASE + 0x0)
+#define MTL_DWICTL_REG_FINALSTATUSL (MTL_DWICTL_BASE + 0x30)
+
+#define MTL_HfPMCCU_BASE 0x1D00
+#define MTL_REG_HfCLKCTL (MTL_HfPMCCU_BASE + 0x10)
+#define MTL_REG_HfPWRCTL (MTL_HfPMCCU_BASE + 0x18)
+#define MTL_REG_HfPWRSTS (MTL_HfPMCCU_BASE + 0x1C)
+#define MTL_REG_HfPWRCTL2 (MTL_HfPMCCU_BASE + 0x20)
+#define MTL_REG_HfPWRSTS2 (MTL_HfPMCCU_BASE + 0x24)
+#define MTL_HfPWRCTL_WPDSPHPxPG BIT(0)
+#define MTL_HfPWRSTS_DSPHPxPGS BIT(0)
+#define MTL_HfPWRCTL2_WPDSPHPxPG BIT(0)
+#define MTL_HfPWRSTS2_DSPHPxPGS BIT(0)
+
+/* Intel HD Audio SRAM windows base addresses */
+#define SKL_ADSP_SRAM_BASE_OFFSET 0x8000
+#define SKL_ADSP_SRAM_WINDOW_SIZE 0x2000
+#define APL_ADSP_SRAM_BASE_OFFSET 0x80000
+#define APL_ADSP_SRAM_WINDOW_SIZE 0x20000
+#define MTL_ADSP_SRAM_BASE_OFFSET 0x180000
+#define MTL_ADSP_SRAM_WINDOW_SIZE 0x8000
+
+/* Constants used when accessing SRAM, space shared with firmware */
+#define AVS_FW_REG_BASE(adev) ((adev)->spec->hipc->sts_offset)
+#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0)
+#define AVS_FW_REG_ERROR(adev) (AVS_FW_REG_BASE(adev) + 0x4)
+
+#define AVS_WINDOW_CHUNK_SIZE SZ_4K
+#define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE
+#define AVS_FW_REGS_WINDOW 0
+/* DSP -> HOST communication window */
+#define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW
+/* HOST -> DSP communication window */
+#define AVS_DOWNLINK_WINDOW 1
+#define AVS_DEBUG_WINDOW 2
+
+/* registry I/O helpers */
+#define avs_sram_offset(adev, window_idx) \
+ ((adev)->spec->sram->base_offset + \
+ (adev)->spec->sram->window_size * (window_idx))
+
+#define avs_sram_addr(adev, window_idx) \
+ ((adev)->dsp_ba + avs_sram_offset(adev, window_idx))
+
+#define avs_uplink_addr(adev) \
+ (avs_sram_addr(adev, AVS_UPLINK_WINDOW) + AVS_FW_REGS_SIZE)
+#define avs_downlink_addr(adev) \
+ avs_sram_addr(adev, AVS_DOWNLINK_WINDOW)
+
+#define snd_hdac_adsp_writeb(adev, reg, value) \
+ snd_hdac_reg_writeb(&(adev)->base.core, (adev)->dsp_ba + (reg), value)
+#define snd_hdac_adsp_readb(adev, reg) \
+ snd_hdac_reg_readb(&(adev)->base.core, (adev)->dsp_ba + (reg))
+#define snd_hdac_adsp_writew(adev, reg, value) \
+ snd_hdac_reg_writew(&(adev)->base.core, (adev)->dsp_ba + (reg), value)
+#define snd_hdac_adsp_readw(adev, reg) \
+ snd_hdac_reg_readw(&(adev)->base.core, (adev)->dsp_ba + (reg))
+#define snd_hdac_adsp_writel(adev, reg, value) \
+ snd_hdac_reg_writel(&(adev)->base.core, (adev)->dsp_ba + (reg), value)
+#define snd_hdac_adsp_readl(adev, reg) \
+ snd_hdac_reg_readl(&(adev)->base.core, (adev)->dsp_ba + (reg))
+#define snd_hdac_adsp_writeq(adev, reg, value) \
+ snd_hdac_reg_writeq(&(adev)->base.core, (adev)->dsp_ba + (reg), value)
+#define snd_hdac_adsp_readq(adev, reg) \
+ snd_hdac_reg_readq(&(adev)->base.core, (adev)->dsp_ba + (reg))
+
+#define snd_hdac_adsp_updateb(adev, reg, mask, val) \
+ snd_hdac_adsp_writeb(adev, reg, \
+ (snd_hdac_adsp_readb(adev, reg) & ~(mask)) | (val))
+#define snd_hdac_adsp_updatew(adev, reg, mask, val) \
+ snd_hdac_adsp_writew(adev, reg, \
+ (snd_hdac_adsp_readw(adev, reg) & ~(mask)) | (val))
+#define snd_hdac_adsp_updatel(adev, reg, mask, val) \
+ snd_hdac_adsp_writel(adev, reg, \
+ (snd_hdac_adsp_readl(adev, reg) & ~(mask)) | (val))
+#define snd_hdac_adsp_updateq(adev, reg, mask, val) \
+ snd_hdac_adsp_writeq(adev, reg, \
+ (snd_hdac_adsp_readq(adev, reg) & ~(mask)) | (val))
+
+#define snd_hdac_adsp_readb_poll(adev, reg, val, cond, delay_us, timeout_us) \
+ readb_poll_timeout((adev)->dsp_ba + (reg), val, cond, \
+ delay_us, timeout_us)
+#define snd_hdac_adsp_readw_poll(adev, reg, val, cond, delay_us, timeout_us) \
+ readw_poll_timeout((adev)->dsp_ba + (reg), val, cond, \
+ delay_us, timeout_us)
+#define snd_hdac_adsp_readl_poll(adev, reg, val, cond, delay_us, timeout_us) \
+ readl_poll_timeout((adev)->dsp_ba + (reg), val, cond, \
+ delay_us, timeout_us)
+#define snd_hdac_adsp_readq_poll(adev, reg, val, cond, delay_us, timeout_us) \
+ readq_poll_timeout((adev)->dsp_ba + (reg), val, cond, \
+ delay_us, timeout_us)
+
+#endif /* __SOUND_SOC_INTEL_AVS_REGS_H */
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
new file mode 100644
index 000000000000..8fb86f364ff3
--- /dev/null
+++ b/sound/soc/intel/avs/skl.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "cldma.h"
+#include "debug.h"
+#include "messages.h"
+#include "registers.h"
+
+void avs_skl_ipc_interrupt(struct avs_dev *adev)
+{
+ const struct avs_spec *spec = adev->spec;
+ u32 hipc_ack, hipc_rsp;
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0);
+
+ hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset);
+ hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+
+ /* DSP acked host's request. */
+ if (hipc_ack & spec->hipc->ack_done_mask) {
+ complete(&adev->ipc->done_completion);
+
+ /* Tell DSP it has our attention. */
+ snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask,
+ spec->hipc->ack_done_mask);
+ }
+
+ /* DSP sent new response to process */
+ if (hipc_rsp & spec->hipc->rsp_busy_mask) {
+ union avs_reply_msg msg;
+
+ msg.primary = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
+ msg.ext.val = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE);
+
+ avs_dsp_process_response(adev, msg.val);
+
+ /* Tell DSP we accepted its message. */
+ snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY,
+ SKL_ADSP_HIPCT_BUSY);
+ }
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
+}
+
+static irqreturn_t avs_skl_dsp_interrupt(struct avs_dev *adev)
+{
+ u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (adspis == UINT_MAX)
+ return ret;
+
+ if (adspis & AVS_ADSP_ADSPIS_CLDMA) {
+ hda_cldma_interrupt(&code_loader);
+ ret = IRQ_HANDLED;
+ }
+
+ if (adspis & AVS_ADSP_ADSPIS_IPC) {
+ avs_skl_ipc_interrupt(adev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int __maybe_unused
+avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct avs_skl_log_state_info *info;
+ u32 size, num_cores = adev->hw_cfg.dsp_cores;
+ int ret, i;
+
+ if (fls_long(resource_mask) > num_cores)
+ return -EINVAL;
+ size = struct_size(info, logs_core, num_cores);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->core_mask = resource_mask;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, num_cores) {
+ info->logs_core[i].enable = enable;
+ info->logs_core[i].min_priority = *priorities++;
+ }
+ else
+ for_each_set_bit(i, &resource_mask, num_cores)
+ info->logs_core[i].enable = enable;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
+int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+ return core * avs_log_buffer_size(adev);
+}
+
+/* fw DbgLogWp registers */
+#define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core)
+
+static int avs_skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ void __iomem *buf;
+ u16 size, write, offset;
+
+ if (!avs_logging_fw(adev))
+ return 0;
+
+ size = avs_log_buffer_size(adev) / 2;
+ write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
+ /* determine buffer half */
+ offset = (write < size) ? size : 0;
+
+ /* Address is guaranteed to exist in SRAM2. */
+ buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
+ avs_dump_fw_log_wakeup(adev, buf, size);
+
+ return 0;
+}
+
+static int avs_skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ u8 *dump;
+
+ dump = vzalloc(AVS_FW_REGS_SIZE);
+ if (!dump)
+ return -ENOMEM;
+
+ memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+ dev_coredumpv(adev->dev, dump, AVS_FW_REGS_SIZE, GFP_KERNEL);
+
+ return 0;
+}
+
+static bool avs_skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+ /* unsupported on cAVS 1.5 hw */
+ return false;
+}
+
+static int avs_skl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ /* unsupported on cAVS 1.5 hw */
+ return 0;
+}
+
+const struct avs_dsp_ops avs_skl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .dsp_interrupt = avs_skl_dsp_interrupt,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_cldma_load_basefw,
+ .load_lib = avs_cldma_load_library,
+ .transfer_mods = avs_cldma_transfer_modules,
+ .log_buffer_offset = avs_skl_log_buffer_offset,
+ .log_buffer_status = avs_skl_log_buffer_status,
+ .coredump = avs_skl_coredump,
+ .d0ix_toggle = avs_skl_d0ix_toggle,
+ .set_d0ix = avs_skl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(skl)
+};
diff --git a/sound/soc/intel/avs/sysfs.c b/sound/soc/intel/avs/sysfs.c
new file mode 100644
index 000000000000..74b2e6f38d76
--- /dev/null
+++ b/sound/soc/intel/avs/sysfs.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/sysfs.h>
+#include "avs.h"
+
+static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct avs_dev *adev = to_avs_dev(dev);
+ struct avs_fw_version *fw_version = &adev->fw_cfg.fw_version;
+
+ return sysfs_emit(buf, "%d.%d.%d.%d\n", fw_version->major, fw_version->minor,
+ fw_version->hotfix, fw_version->build);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static struct attribute *avs_fw_attrs[] = {
+ &dev_attr_fw_version.attr,
+ NULL
+};
+
+static const struct attribute_group avs_attr_group = {
+ .name = "avs",
+ .attrs = avs_fw_attrs,
+};
+
+const struct attribute_group *avs_attr_groups[] = {
+ &avs_attr_group,
+ NULL
+};
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
new file mode 100644
index 000000000000..afb066516101
--- /dev/null
+++ b/sound/soc/intel/avs/tgl.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2024 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/pci.h>
+#include "avs.h"
+#include "debug.h"
+#include "messages.h"
+
+#define CPUID_TSC_LEAF 0x15
+
+static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+
+ if (!core_mask)
+ return 0;
+ return avs_dsp_core_power(adev, core_mask, power);
+}
+
+static int avs_tgl_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+
+ if (!core_mask)
+ return 0;
+ return avs_dsp_core_reset(adev, core_mask, reset);
+}
+
+static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+
+ if (!core_mask)
+ return 0;
+ return avs_dsp_core_stall(adev, core_mask, stall);
+}
+
+static int avs_tgl_config_basefw(struct avs_dev *adev)
+{
+ struct pci_dev *pci = adev->base.pci;
+ struct avs_bus_hwid hwid;
+ int ret;
+#ifdef CONFIG_X86
+ unsigned int ecx;
+
+#include <asm/cpuid/api.h>
+ ecx = cpuid_ecx(CPUID_TSC_LEAF);
+ if (ecx) {
+ ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_XTAL_FREQ_HZ, sizeof(ecx), &ecx);
+ if (ret)
+ return AVS_IPC_RET(ret);
+ }
+#endif
+
+ hwid.device = pci->device;
+ hwid.subsystem = pci->subsystem_vendor | (pci->subsystem_device << 16);
+ hwid.revision = pci->revision;
+
+ ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_BUS_HARDWARE_ID, sizeof(hwid), &hwid);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
+const struct avs_dsp_ops avs_tgl_dsp_ops = {
+ .power = avs_tgl_dsp_core_power,
+ .reset = avs_tgl_dsp_core_reset,
+ .stall = avs_tgl_dsp_core_stall,
+ .dsp_interrupt = avs_cnl_dsp_interrupt,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_icl_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .config_basefw = avs_tgl_config_basefw,
+ .log_buffer_offset = avs_icl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_icl_d0ix_toggle,
+ .set_d0ix = avs_icl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(icl)
+};
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
new file mode 100644
index 000000000000..9033f683393c
--- /dev/null
+++ b/sound/soc/intel/avs/topology.c
@@ -0,0 +1,2247 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/uuid.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-topology.h>
+#include <uapi/sound/intel/avs/tokens.h>
+#include "avs.h"
+#include "control.h"
+#include "topology.h"
+#include "utils.h"
+
+/* Get pointer to vendor array at the specified offset. */
+#define avs_tplg_vendor_array_at(array, offset) \
+ ((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset))
+
+/* Get pointer to vendor array that is next in line. */
+#define avs_tplg_vendor_array_next(array) \
+ (avs_tplg_vendor_array_at(array, le32_to_cpu((array)->size)))
+
+/*
+ * Scan provided block of tuples for the specified token. If found,
+ * @offset is updated with position at which first matching token is
+ * located.
+ *
+ * Returns 0 on success, -ENOENT if not found and error code otherwise.
+ */
+static int
+avs_tplg_vendor_array_lookup(struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 token, u32 *offset)
+{
+ u32 pos = 0;
+
+ while (block_size > 0) {
+ struct snd_soc_tplg_vendor_value_elem *tuple;
+ u32 tuples_size = le32_to_cpu(tuples->size);
+
+ if (tuples_size > block_size)
+ return -EINVAL;
+
+ tuple = tuples->value;
+ if (le32_to_cpu(tuple->token) == token) {
+ *offset = pos;
+ return 0;
+ }
+
+ block_size -= tuples_size;
+ pos += tuples_size;
+ tuples = avs_tplg_vendor_array_next(tuples);
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * See avs_tplg_vendor_array_lookup() for description.
+ *
+ * Behaves exactly like avs_tplg_vendor_lookup() but starts from the
+ * next vendor array in line. Useful when searching for the finish line
+ * of an arbitrary entry in a list of entries where each is composed of
+ * several vendor tuples and a specific token marks the beginning of
+ * a new entry block.
+ */
+static int
+avs_tplg_vendor_array_lookup_next(struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 token, u32 *offset)
+{
+ u32 tuples_size = le32_to_cpu(tuples->size);
+ int ret;
+
+ if (tuples_size > block_size)
+ return -EINVAL;
+
+ tuples = avs_tplg_vendor_array_next(tuples);
+ block_size -= tuples_size;
+
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size, token, offset);
+ if (!ret)
+ *offset += tuples_size;
+ return ret;
+}
+
+/*
+ * Scan provided block of tuples for the specified token which marks
+ * the border of an entry block. Behavior is similar to
+ * avs_tplg_vendor_array_lookup() except 0 is also returned if no
+ * matching token has been found. In such case, returned @size is
+ * assigned to @block_size as the entire block belongs to the current
+ * entry.
+ *
+ * Returns 0 on success, error code otherwise.
+ */
+static int
+avs_tplg_vendor_entry_size(struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 entry_id_token, u32 *size)
+{
+ int ret;
+
+ ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, entry_id_token, size);
+ if (ret == -ENOENT) {
+ *size = block_size;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Vendor tuple parsing descriptor.
+ *
+ * @token: vendor specific token that identifies tuple
+ * @type: tuple type, one of SND_SOC_TPLG_TUPLE_TYPE_XXX
+ * @offset: offset of a struct's field to initialize
+ * @parse: parsing function, extracts and assigns value to object's field
+ */
+struct avs_tplg_token_parser {
+ enum avs_tplg_token token;
+ u32 type;
+ u32 offset;
+ int (*parse)(struct snd_soc_component *comp, void *elem, void *object, u32 offset);
+};
+
+static int
+avs_parse_uuid_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_uuid_elem *tuple = elem;
+ guid_t *val = (guid_t *)((u8 *)object + offset);
+
+ guid_copy((guid_t *)val, (const guid_t *)&tuple->uuid);
+
+ return 0;
+}
+
+static int
+avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ bool *val = (bool *)((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ u8 *val = ((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_short_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ u16 *val = (u16 *)((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_word_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ u32 *val = (u32 *)((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_string_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_string_elem *tuple = elem;
+ char *val = (char *)((u8 *)object + offset);
+
+ snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", tuple->string);
+
+ return 0;
+}
+
+static int avs_parse_uuid_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, int count,
+ struct snd_soc_tplg_vendor_array *tuples)
+{
+ struct snd_soc_tplg_vendor_uuid_elem *tuple;
+ int ret, i, j;
+
+ /* Parse element by element. */
+ for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+ tuple = &tuples->uuid[i];
+
+ for (j = 0; j < count; j++) {
+ /* Ignore non-UUID tokens. */
+ if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID ||
+ parsers[j].token != le32_to_cpu(tuple->token))
+ continue;
+
+ ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_parse_string_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, int count,
+ struct snd_soc_tplg_vendor_array *tuples)
+{
+ struct snd_soc_tplg_vendor_string_elem *tuple;
+ int ret, i, j;
+
+ /* Parse element by element. */
+ for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+ tuple = &tuples->string[i];
+
+ for (j = 0; j < count; j++) {
+ /* Ignore non-string tokens. */
+ if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING ||
+ parsers[j].token != le32_to_cpu(tuple->token))
+ continue;
+
+ ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_parse_word_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, int count,
+ struct snd_soc_tplg_vendor_array *tuples)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple;
+ int ret, i, j;
+
+ /* Parse element by element. */
+ for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+ tuple = &tuples->value[i];
+
+ for (j = 0; j < count; j++) {
+ /* Ignore non-integer tokens. */
+ if (!(parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
+ parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
+ parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE ||
+ parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL))
+ continue;
+
+ if (parsers[j].token != le32_to_cpu(tuple->token))
+ continue;
+
+ ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_parse_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, size_t count,
+ struct snd_soc_tplg_vendor_array *tuples, int priv_size)
+{
+ int array_size, ret;
+
+ while (priv_size > 0) {
+ array_size = le32_to_cpu(tuples->size);
+
+ if (array_size <= 0) {
+ dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
+ return -EINVAL;
+ }
+
+ /* Make sure there is enough data before parsing. */
+ priv_size -= array_size;
+ if (priv_size < 0) {
+ dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
+ return -EINVAL;
+ }
+
+ switch (le32_to_cpu(tuples->type)) {
+ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+ ret = avs_parse_uuid_tokens(comp, object, parsers, count, tuples);
+ break;
+ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+ ret = avs_parse_string_tokens(comp, object, parsers, count, tuples);
+ break;
+ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
+ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
+ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
+ ret = avs_parse_word_tokens(comp, object, parsers, count, tuples);
+ break;
+ default:
+ dev_err(comp->dev, "unknown token type %d\n", tuples->type);
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(comp->dev, "parsing %zu tokens of %d type failed: %d\n",
+ count, tuples->type, ret);
+ return ret;
+ }
+
+ tuples = avs_tplg_vendor_array_next(tuples);
+ }
+
+ return 0;
+}
+
+#define AVS_DEFINE_PTR_PARSER(name, type, member) \
+static int \
+avs_parse_##name##_ptr(struct snd_soc_component *comp, void *elem, void *object, u32 offset) \
+{ \
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem; \
+ struct avs_soc_component *acomp = to_avs_soc_component(comp); \
+ type **val = (type **)(object + offset); \
+ u32 idx; \
+ \
+ idx = le32_to_cpu(tuple->value); \
+ if (idx >= acomp->tplg->num_##member) \
+ return -EINVAL; \
+ \
+ *val = &acomp->tplg->member[idx]; \
+ \
+ return 0; \
+}
+
+AVS_DEFINE_PTR_PARSER(audio_format, struct avs_audio_format, fmts);
+AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base);
+AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext);
+AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs);
+AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings);
+AVS_DEFINE_PTR_PARSER(nhlt_config, struct avs_tplg_nhlt_config, nhlt_configs);
+
+static int
+parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *velem = elem;
+ struct avs_audio_format *audio_format = object;
+
+ switch (offset) {
+ case AVS_TKN_AFMT_NUM_CHANNELS_U32:
+ audio_format->num_channels = le32_to_cpu(velem->value);
+ break;
+ case AVS_TKN_AFMT_VALID_BIT_DEPTH_U32:
+ audio_format->valid_bit_depth = le32_to_cpu(velem->value);
+ break;
+ case AVS_TKN_AFMT_SAMPLE_TYPE_U32:
+ audio_format->sample_type = le32_to_cpu(velem->value);
+ break;
+ }
+
+ return 0;
+}
+
+static int avs_ssp_sprint(char *buf, size_t size, const char *fmt, int port, int tdm)
+{
+ char *needle = strstr(fmt, "%d");
+ int retsize;
+
+ /*
+ * If there is %d present in fmt string it should be replaced by either
+ * SSP or SSP:TDM, where SSP and TDM are numbers, all other formatting
+ * will be ignored.
+ */
+ if (needle) {
+ retsize = scnprintf(buf, min_t(size_t, size, needle - fmt + 1), "%s", fmt);
+ retsize += scnprintf(buf + retsize, size - retsize, "%d", port);
+ if (tdm)
+ retsize += scnprintf(buf + retsize, size - retsize, ":%d", tdm);
+ retsize += scnprintf(buf + retsize, size - retsize, "%s", needle + 2);
+ return retsize;
+ }
+
+ return snprintf(buf, size, "%s", fmt);
+}
+
+static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem,
+ void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_string_elem *tuple = elem;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
+ char *val = (char *)((u8 *)object + offset);
+ int ssp_port, tdm_slot;
+
+ /*
+ * Dynamic naming - string formats, e.g.: ssp%d - supported only for
+ * topologies describing single device e.g.: an I2S codec on SSP0.
+ */
+ if (!avs_mach_singular_ssp(mach))
+ return avs_parse_string_token(comp, elem, object, offset);
+
+ ssp_port = avs_mach_ssp_port(mach);
+ if (!avs_mach_singular_tdm(mach, ssp_port))
+ return avs_parse_string_token(comp, elem, object, offset);
+
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+
+ avs_ssp_sprint(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string, ssp_port, tdm_slot);
+
+ return 0;
+}
+
+static int avs_parse_nhlt_config_size(struct snd_soc_component *comp, void *elem, void *object,
+ u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ struct acpi_nhlt_config **blob = (struct acpi_nhlt_config **)((u8 *)object + offset);
+ u32 size;
+
+ size = le32_to_cpu(tuple->value);
+ *blob = devm_kzalloc(comp->card->dev, struct_size(*blob, capabilities, size), GFP_KERNEL);
+ if (!*blob)
+ return -ENOMEM;
+
+ (*blob)->capabilities_size = size;
+ return 0;
+}
+
+static int
+parse_dictionary_header(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ void **dict, u32 *num_entries, size_t entry_size,
+ u32 num_entries_token)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple;
+
+ /* Dictionary header consists of single tuple - entry count. */
+ tuple = tuples->value;
+ if (le32_to_cpu(tuple->token) != num_entries_token) {
+ dev_err(comp->dev, "invalid dictionary header, expected: %d\n",
+ num_entries_token);
+ return -EINVAL;
+ }
+
+ *num_entries = le32_to_cpu(tuple->value);
+ *dict = devm_kcalloc(comp->card->dev, *num_entries, entry_size, GFP_KERNEL);
+ if (!*dict)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+parse_dictionary_entries(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ void *dict, u32 num_entries, size_t entry_size,
+ u32 entry_id_token,
+ const struct avs_tplg_token_parser *parsers, size_t num_parsers)
+{
+ void *pos = dict;
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ u32 esize;
+ int ret;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ entry_id_token, &esize);
+ if (ret)
+ return ret;
+
+ ret = avs_parse_tokens(comp, pos, parsers, num_parsers, tuples, esize);
+ if (ret < 0) {
+ dev_err(comp->dev, "parse entry: %d of type: %d failed: %d\n",
+ i, entry_id_token, ret);
+ return ret;
+ }
+
+ pos += entry_size;
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static int parse_dictionary(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ void **dict, u32 *num_entries, size_t entry_size,
+ u32 num_entries_token, u32 entry_id_token,
+ const struct avs_tplg_token_parser *parsers, size_t num_parsers)
+{
+ int ret;
+
+ ret = parse_dictionary_header(comp, tuples, dict, num_entries,
+ entry_size, num_entries_token);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ return parse_dictionary_entries(comp, tuples, block_size, *dict,
+ *num_entries, entry_size,
+ entry_id_token, parsers, num_parsers);
+}
+
+static const struct avs_tplg_token_parser library_parsers[] = {
+ {
+ .token = AVS_TKN_LIBRARY_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_library, name),
+ .parse = avs_parse_string_token,
+ },
+};
+
+static int avs_tplg_parse_libraries(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->libs,
+ &tplg->num_libs, sizeof(*tplg->libs),
+ AVS_TKN_MANIFEST_NUM_LIBRARIES_U32,
+ AVS_TKN_LIBRARY_ID_U32,
+ library_parsers, ARRAY_SIZE(library_parsers));
+}
+
+static const struct avs_tplg_token_parser audio_format_parsers[] = {
+ {
+ .token = AVS_TKN_AFMT_SAMPLE_RATE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, sampling_freq),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_BIT_DEPTH_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, bit_depth),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_CHANNEL_MAP_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, channel_map),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_CHANNEL_CFG_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, channel_config),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_INTERLEAVING_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, interleaving),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_NUM_CHANNELS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = AVS_TKN_AFMT_NUM_CHANNELS_U32,
+ .parse = parse_audio_format_bitfield,
+ },
+ {
+ .token = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32,
+ .parse = parse_audio_format_bitfield,
+ },
+ {
+ .token = AVS_TKN_AFMT_SAMPLE_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = AVS_TKN_AFMT_SAMPLE_TYPE_U32,
+ .parse = parse_audio_format_bitfield,
+ },
+};
+
+static int avs_tplg_parse_audio_formats(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->fmts,
+ &tplg->num_fmts, sizeof(*tplg->fmts),
+ AVS_TKN_MANIFEST_NUM_AFMTS_U32,
+ AVS_TKN_AFMT_ID_U32,
+ audio_format_parsers, ARRAY_SIZE(audio_format_parsers));
+}
+
+static const struct avs_tplg_token_parser modcfg_base_parsers[] = {
+ {
+ .token = AVS_TKN_MODCFG_BASE_CPC_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, cpc),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_BASE_IBS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, ibs),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_BASE_OBS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, obs),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_BASE_PAGES_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, is_pages),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_tplg_parse_modcfgs_base(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->modcfgs_base,
+ &tplg->num_modcfgs_base, sizeof(*tplg->modcfgs_base),
+ AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32,
+ AVS_TKN_MODCFG_BASE_ID_U32,
+ modcfg_base_parsers, ARRAY_SIZE(modcfg_base_parsers));
+}
+
+static const struct avs_tplg_token_parser modcfg_ext_parsers[] = {
+ {
+ .token = AVS_TKN_MODCFG_EXT_TYPE_UUID,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_UUID,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, type),
+ .parse = avs_parse_uuid_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.feature_mask),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_VINDEX_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.vindex),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_DMA_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_buffer_size),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.blob_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, micsel.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, wov.cpc_lp_mode),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_SRC_OUT_FREQ_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, src.out_freq),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, mux.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, mux.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, aec.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, aec.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, aec.cpc_lp_mode),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.out_freq),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_ASRC_MODE_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.mode),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.disable_jitter_buffer),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.out_channel_config),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients_select),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[0]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[1]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[2]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[3]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[4]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[5]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[6]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[7]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.channel_map),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_input_pins),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.wake_tick_period),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_VINDEX_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.vindex),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_DMA_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_buffer_size),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_PEAKVOL_VOLUME_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.target_volume),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_DURATION_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_duration),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static const struct avs_tplg_token_parser pin_format_parsers[] = {
+ {
+ .token = AVS_TKN_PIN_FMT_INDEX_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pin_format, pin_index),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PIN_FMT_IOBS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pin_format, iobs),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PIN_FMT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pin_format, fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+};
+
+static void
+assign_copier_gtw_instance(struct snd_soc_component *comp, struct avs_tplg_modcfg_ext *cfg)
+{
+ struct snd_soc_acpi_mach *mach;
+ int ssp_port, tdm_slot;
+
+ if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID))
+ return;
+
+ /* Only I2S boards assign port instance in ->i2s_link_mask. */
+ switch (cfg->copier.dma_type) {
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ case AVS_DMA_I2S_LINK_INPUT:
+ break;
+ default:
+ return;
+ }
+
+ /* If topology sets value don't overwrite it */
+ if (cfg->copier.vindex.val)
+ return;
+
+ mach = dev_get_platdata(comp->card->dev);
+
+ if (!avs_mach_singular_ssp(mach))
+ return;
+ ssp_port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, ssp_port))
+ return;
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+
+ cfg->copier.vindex.i2s.instance = ssp_port;
+ cfg->copier.vindex.i2s.time_slot = tdm_slot;
+}
+
+static int avs_tplg_parse_modcfg_ext(struct snd_soc_component *comp,
+ struct avs_tplg_modcfg_ext *cfg,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ u32 esize;
+ int ret;
+
+ /* See where pin block starts. */
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_PIN_FMT_INDEX_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = avs_parse_tokens(comp, cfg, modcfg_ext_parsers,
+ ARRAY_SIZE(modcfg_ext_parsers), tuples, esize);
+ if (ret)
+ return ret;
+
+ /* Update copier gateway based on board's i2s_link_mask. */
+ assign_copier_gtw_instance(comp, cfg);
+
+ block_size -= esize;
+ /* Parse trailing in/out pin formats if any. */
+ if (block_size) {
+ struct avs_tplg_pin_format *pins;
+ u32 num_pins;
+
+ num_pins = cfg->generic.num_input_pins + cfg->generic.num_output_pins;
+ if (!num_pins)
+ return -EINVAL;
+
+ pins = devm_kcalloc(comp->card->dev, num_pins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ ret = parse_dictionary_entries(comp, tuples, block_size,
+ pins, num_pins, sizeof(*pins),
+ AVS_TKN_PIN_FMT_INDEX_U32,
+ pin_format_parsers,
+ ARRAY_SIZE(pin_format_parsers));
+ if (ret)
+ return ret;
+ cfg->generic.pin_fmts = pins;
+ }
+
+ return 0;
+}
+
+static int avs_tplg_parse_modcfgs_ext(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->modcfgs_ext,
+ &tplg->num_modcfgs_ext,
+ sizeof(*tplg->modcfgs_ext),
+ AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_modcfgs_ext; i++) {
+ struct avs_tplg_modcfg_ext *cfg = &tplg->modcfgs_ext[i];
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_MODCFG_EXT_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = avs_tplg_parse_modcfg_ext(comp, cfg, tuples, esize);
+ if (ret)
+ return ret;
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser pplcfg_parsers[] = {
+ {
+ .token = AVS_TKN_PPLCFG_REQ_SIZE_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_pplcfg, req_size),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_PRIORITY_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_pplcfg, priority),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_LOW_POWER_BOOL,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL,
+ .offset = offsetof(struct avs_tplg_pplcfg, lp),
+ .parse = avs_parse_bool_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_ATTRIBUTES_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_pplcfg, attributes),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_TRIGGER_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pplcfg, trigger),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_tplg_parse_pplcfgs(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->pplcfgs,
+ &tplg->num_pplcfgs, sizeof(*tplg->pplcfgs),
+ AVS_TKN_MANIFEST_NUM_PPLCFGS_U32,
+ AVS_TKN_PPLCFG_ID_U32,
+ pplcfg_parsers, ARRAY_SIZE(pplcfg_parsers));
+}
+
+static const struct avs_tplg_token_parser binding_parsers[] = {
+ {
+ .token = AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_binding, target_tplg_name),
+ .parse = parse_link_formatted_string,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, target_path_tmpl_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_PPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, target_ppl_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_MOD_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, target_mod_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_MOD_PIN_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_binding, target_mod_pin),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_MOD_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, mod_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_MOD_PIN_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_binding, mod_pin),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_IS_SINK_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_binding, is_sink),
+ .parse = avs_parse_byte_token,
+ },
+};
+
+static int avs_tplg_parse_bindings(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->bindings,
+ &tplg->num_bindings, sizeof(*tplg->bindings),
+ AVS_TKN_MANIFEST_NUM_BINDINGS_U32,
+ AVS_TKN_BINDING_ID_U32,
+ binding_parsers, ARRAY_SIZE(binding_parsers));
+}
+
+static const struct avs_tplg_token_parser module_parsers[] = {
+ {
+ .token = AVS_TKN_MOD_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MOD_MODCFG_BASE_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, cfg_base),
+ .parse = avs_parse_modcfg_base_ptr,
+ },
+ {
+ .token = AVS_TKN_MOD_IN_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, in_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MOD_CORE_ID_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_module, core_id),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_PROC_DOMAIN_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_module, domain),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_MODCFG_EXT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, cfg_ext),
+ .parse = avs_parse_modcfg_ext_ptr,
+ },
+ {
+ .token = AVS_TKN_MOD_KCONTROL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, ctl_id),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, num_config_ids),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_NHLT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, nhlt_config),
+ .parse = avs_parse_nhlt_config_ptr,
+ },
+};
+
+static const struct avs_tplg_token_parser init_config_parsers[] = {
+ {
+ .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = 0,
+ .parse = avs_parse_word_token,
+ },
+};
+
+static struct avs_tplg_module *
+avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_tplg_module *module;
+ u32 esize;
+ int ret;
+
+ /* See where config id block starts. */
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_MOD_INIT_CONFIG_ID_U32, &esize);
+ if (ret)
+ return ERR_PTR(ret);
+
+ module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return ERR_PTR(-ENOMEM);
+
+ ret = avs_parse_tokens(comp, module, module_parsers,
+ ARRAY_SIZE(module_parsers), tuples, esize);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ block_size -= esize;
+ /* Parse trailing config ids if any. */
+ if (block_size) {
+ u32 num_config_ids = module->num_config_ids;
+ u32 *config_ids;
+
+ if (!num_config_ids)
+ return ERR_PTR(-EINVAL);
+
+ config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids),
+ GFP_KERNEL);
+ if (!config_ids)
+ return ERR_PTR(-ENOMEM);
+
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ ret = parse_dictionary_entries(comp, tuples, block_size,
+ config_ids, num_config_ids, sizeof(*config_ids),
+ AVS_TKN_MOD_INIT_CONFIG_ID_U32,
+ init_config_parsers,
+ ARRAY_SIZE(init_config_parsers));
+ if (ret)
+ return ERR_PTR(ret);
+
+ module->config_ids = config_ids;
+ }
+
+ module->owner = owner;
+ INIT_LIST_HEAD(&module->node);
+
+ return module;
+}
+
+static const struct avs_tplg_token_parser pipeline_parsers[] = {
+ {
+ .token = AVS_TKN_PPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pipeline, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PPL_PPLCFG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pipeline, cfg),
+ .parse = avs_parse_pplcfg_ptr,
+ },
+ {
+ .token = AVS_TKN_PPL_NUM_BINDING_IDS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pipeline, num_bindings),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static const struct avs_tplg_token_parser bindings_parsers[] = {
+ {
+ .token = AVS_TKN_PPL_BINDING_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = 0, /* to treat pipeline->bindings as dictionary */
+ .parse = avs_parse_binding_ptr,
+ },
+};
+
+static struct avs_tplg_pipeline *
+avs_tplg_pipeline_create(struct snd_soc_component *comp, struct avs_tplg_path *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_tplg_pipeline *pipeline;
+ u32 modblk_size, offset;
+ int ret;
+
+ pipeline = devm_kzalloc(comp->card->dev, sizeof(*pipeline), GFP_KERNEL);
+ if (!pipeline)
+ return ERR_PTR(-ENOMEM);
+
+ pipeline->owner = owner;
+ INIT_LIST_HEAD(&pipeline->mod_list);
+
+ /* Pipeline header MUST be followed by at least one module. */
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+ AVS_TKN_MOD_ID_U32, &offset);
+ if (!ret && !offset)
+ ret = -EINVAL;
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Process header which precedes module sections. */
+ ret = avs_parse_tokens(comp, pipeline, pipeline_parsers,
+ ARRAY_SIZE(pipeline_parsers), tuples, offset);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ block_size -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* Optionally, binding sections follow module ones. */
+ ret = avs_tplg_vendor_array_lookup_next(tuples, block_size,
+ AVS_TKN_PPL_BINDING_ID_U32, &offset);
+ if (ret) {
+ if (ret != -ENOENT)
+ return ERR_PTR(ret);
+
+ /* Does header information match actual block layout? */
+ if (pipeline->num_bindings)
+ return ERR_PTR(-EINVAL);
+
+ modblk_size = block_size;
+ } else {
+ pipeline->bindings = devm_kcalloc(comp->card->dev, pipeline->num_bindings,
+ sizeof(*pipeline->bindings), GFP_KERNEL);
+ if (!pipeline->bindings)
+ return ERR_PTR(-ENOMEM);
+
+ modblk_size = offset;
+ }
+
+ block_size -= modblk_size;
+ do {
+ struct avs_tplg_module *module;
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, modblk_size,
+ AVS_TKN_MOD_ID_U32, &esize);
+ if (ret)
+ return ERR_PTR(ret);
+
+ module = avs_tplg_module_create(comp, pipeline, tuples, esize);
+ if (IS_ERR(module)) {
+ dev_err(comp->dev, "parse module failed: %ld\n",
+ PTR_ERR(module));
+ return ERR_CAST(module);
+ }
+
+ list_add_tail(&module->node, &pipeline->mod_list);
+ modblk_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ } while (modblk_size > 0);
+
+ /* What's left is optional range of bindings. */
+ ret = parse_dictionary_entries(comp, tuples, block_size, pipeline->bindings,
+ pipeline->num_bindings, sizeof(*pipeline->bindings),
+ AVS_TKN_PPL_BINDING_ID_U32,
+ bindings_parsers, ARRAY_SIZE(bindings_parsers));
+ if (ret)
+ return ERR_PTR(ret);
+
+ return pipeline;
+}
+
+static const struct avs_tplg_token_parser path_parsers[] = {
+ {
+ .token = AVS_TKN_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PATH_FE_FMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, fe_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_PATH_BE_FMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, be_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+};
+
+static const struct avs_tplg_token_parser condpath_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, source_path_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SINK_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, sink_path_id),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static struct avs_tplg_path *
+avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ const struct avs_tplg_token_parser *parsers, u32 num_parsers)
+{
+ struct avs_tplg_pipeline *pipeline;
+ struct avs_tplg_path *path;
+ u32 offset;
+ int ret;
+
+ path = devm_kzalloc(comp->card->dev, sizeof(*path), GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ path->owner = owner;
+ INIT_LIST_HEAD(&path->ppl_list);
+ INIT_LIST_HEAD(&path->node);
+
+ /* Path header MAY be followed by one or more pipelines. */
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+ AVS_TKN_PPL_ID_U32, &offset);
+ if (ret == -ENOENT)
+ offset = block_size;
+ else if (ret)
+ return ERR_PTR(ret);
+ else if (!offset)
+ return ERR_PTR(-EINVAL);
+
+ /* Process header which precedes pipeline sections. */
+ ret = avs_parse_tokens(comp, path, parsers, num_parsers, tuples, offset);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ block_size -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+ while (block_size > 0) {
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_PPL_ID_U32, &esize);
+ if (ret)
+ return ERR_PTR(ret);
+
+ pipeline = avs_tplg_pipeline_create(comp, path, tuples, esize);
+ if (IS_ERR(pipeline)) {
+ dev_err(comp->dev, "parse pipeline failed: %ld\n",
+ PTR_ERR(pipeline));
+ return ERR_CAST(pipeline);
+ }
+
+ list_add_tail(&pipeline->node, &path->ppl_list);
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return path;
+}
+
+static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
+ {
+ .token = AVS_TKN_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, id),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static const struct avs_tplg_token_parser condpath_tmpl_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, source.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, source.id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, sink.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, sink.id),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int parse_path_template(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ struct avs_tplg_path_template *template,
+ const struct avs_tplg_token_parser *tmpl_tokens, u32 num_tmpl_tokens,
+ const struct avs_tplg_token_parser *path_tokens, u32 num_path_tokens)
+{
+ struct avs_tplg_path *path;
+ u32 offset;
+ int ret;
+
+ /* Path template header MUST be followed by at least one path variant. */
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+ AVS_TKN_PATH_ID_U32, &offset);
+ if (ret)
+ return ret;
+
+ /* Process header which precedes path variants sections. */
+ ret = avs_parse_tokens(comp, template, tmpl_tokens, num_tmpl_tokens, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ block_size -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+ do {
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_PATH_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ path = avs_tplg_path_create(comp, template, tuples, esize, path_tokens,
+ num_path_tokens);
+ if (IS_ERR(path)) {
+ dev_err(comp->dev, "parse path failed: %ld\n", PTR_ERR(path));
+ return PTR_ERR(path);
+ }
+
+ list_add_tail(&path->node, &template->path_list);
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ } while (block_size > 0);
+
+ return 0;
+}
+
+static struct avs_tplg_path_template *
+avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_tplg_path_template *template;
+ int ret;
+
+ template = devm_kzalloc(comp->card->dev, sizeof(*template), GFP_KERNEL);
+ if (!template)
+ return ERR_PTR(-ENOMEM);
+
+ template->owner = owner; /* Used to access component tplg is assigned to. */
+ INIT_LIST_HEAD(&template->path_list);
+ INIT_LIST_HEAD(&template->node);
+
+ ret = parse_path_template(comp, tuples, block_size, template, path_tmpl_parsers,
+ ARRAY_SIZE(path_tmpl_parsers), path_parsers,
+ ARRAY_SIZE(path_parsers));
+ if (ret)
+ return ERR_PTR(ret);
+
+ return template;
+}
+
+static int avs_tplg_parse_condpath_templates(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->condpath_tmpls,
+ &tplg->num_condpath_tmpls,
+ sizeof(*tplg->condpath_tmpls),
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ u32 esize;
+
+ template = &tplg->condpath_tmpls[i];
+ template->owner = tplg; /* Used when building sysfs hierarchy. */
+ INIT_LIST_HEAD(&template->path_list);
+ INIT_LIST_HEAD(&template->node);
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_CONDPATH_TMPL_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = parse_path_template(comp, tuples, esize, template,
+ condpath_tmpl_parsers,
+ ARRAY_SIZE(condpath_tmpl_parsers),
+ condpath_parsers,
+ ARRAY_SIZE(condpath_parsers));
+ if (ret < 0) {
+ dev_err(comp->dev, "parse condpath_tmpl: %d failed: %d\n", i, ret);
+ return ret;
+ }
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
+ {
+ .token = AVS_TKN_INIT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_init_config, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_INIT_CONFIG_PARAM_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_init_config, param),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_INIT_CONFIG_LENGTH_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_init_config, length),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 *offset)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ *offset = 0;
+
+ /* Parse tuple section telling how many init configs there are. */
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs,
+ &tplg->num_init_configs,
+ sizeof(*tplg->init_configs),
+ AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ *offset += le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) {
+ struct avs_tplg_init_config *config = &tplg->init_configs[i];
+ struct snd_soc_tplg_vendor_array *tmp;
+ void *init_config_data;
+ u32 esize;
+
+ /*
+ * Usually to get section length we search for first token of next group of data,
+ * but in this case we can't as tuples are followed by raw data.
+ */
+ tmp = avs_tplg_vendor_array_next(tuples);
+ esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size);
+ *offset += esize;
+
+ ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
+ AVS_TKN_INIT_CONFIG_ID_U32,
+ mod_init_config_parsers,
+ ARRAY_SIZE(mod_init_config_parsers));
+
+ block_size -= esize;
+
+ /* handle raw data section */
+ init_config_data = (void *)tuples + esize;
+ esize = config->length;
+ *offset += esize;
+
+ config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL);
+ if (!config->data)
+ return -ENOMEM;
+
+ tuples = init_config_data + esize;
+ block_size -= esize;
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser mod_nhlt_config_parsers[] = {
+ {
+ .token = AVS_TKN_NHLT_CONFIG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_nhlt_config, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_NHLT_CONFIG_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_nhlt_config, blob),
+ .parse = avs_parse_nhlt_config_size,
+ },
+};
+
+static int avs_tplg_parse_nhlt_configs(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ /* Parse the header section to know how many entries there are. */
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->nhlt_configs,
+ &tplg->num_nhlt_configs,
+ sizeof(*tplg->nhlt_configs),
+ AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With the header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_nhlt_configs && block_size > 0; i++) {
+ struct avs_tplg_nhlt_config *config;
+ u32 esize;
+
+ config = &tplg->nhlt_configs[i];
+ esize = le32_to_cpu(tuples->size);
+
+ ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
+ AVS_TKN_NHLT_CONFIG_ID_U32,
+ mod_nhlt_config_parsers,
+ ARRAY_SIZE(mod_nhlt_config_parsers));
+ if (ret)
+ return ret;
+ /* With tuples parsed, the blob shall be allocated. */
+ if (!config->blob)
+ return -EINVAL;
+
+ /* Consume the raw data and move to the next entry. */
+ memcpy(config->blob->capabilities, (u8 *)tuples + esize,
+ config->blob->capabilities_size);
+ esize += config->blob->capabilities_size;
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static int avs_route_load(struct snd_soc_component *comp, int index,
+ struct snd_soc_dapm_route *route)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
+ size_t len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
+ int ssp_port, tdm_slot;
+ char *buf;
+
+ /* See parse_link_formatted_string() for dynamic naming when(s). */
+ if (!avs_mach_singular_ssp(mach))
+ return 0;
+ ssp_port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, ssp_port))
+ return 0;
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+
+ buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ avs_ssp_sprint(buf, len, route->source, ssp_port, tdm_slot);
+ route->source = buf;
+
+ buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ avs_ssp_sprint(buf, len, route->sink, ssp_port, tdm_slot);
+ route->sink = buf;
+
+ if (route->control) {
+ buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ avs_ssp_sprint(buf, len, route->control, ssp_port, tdm_slot);
+ route->control = buf;
+ }
+
+ return 0;
+}
+
+static int avs_widget_load(struct snd_soc_component *comp, int index,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *dw)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct avs_tplg_path_template *template;
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg;
+ int ssp_port, tdm_slot;
+
+ if (!le32_to_cpu(dw->priv.size))
+ return 0;
+
+ w->no_wname_in_kcontrol_name = true;
+
+ if (w->ignore_suspend && !AVS_S0IX_SUPPORTED) {
+ dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n");
+ w->ignore_suspend = false;
+ }
+
+ tplg = acomp->tplg;
+ mach = dev_get_platdata(comp->card->dev);
+ if (!avs_mach_singular_ssp(mach))
+ goto static_name;
+ ssp_port = avs_mach_ssp_port(mach);
+
+ /* See parse_link_formatted_string() for dynamic naming when(s). */
+ if (avs_mach_singular_tdm(mach, ssp_port)) {
+ /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 16 + '\0' */
+ size_t size = strlen(dw->name) + 3;
+ char *buf;
+
+ tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ avs_ssp_sprint(buf, size, dw->name, ssp_port, tdm_slot);
+ kfree(w->name);
+ /* w->name is freed later by soc_tplg_dapm_widget_create() */
+ w->name = buf;
+ }
+
+static_name:
+ template = avs_tplg_path_template_create(comp, tplg, dw->priv.array,
+ le32_to_cpu(dw->priv.size));
+ if (IS_ERR(template)) {
+ dev_err(comp->dev, "widget %s load failed: %ld\n", dw->name,
+ PTR_ERR(template));
+ return PTR_ERR(template);
+ }
+
+ w->priv = template; /* link path information to widget */
+ list_add_tail(&template->node, &tplg->path_tmpl_list);
+ return 0;
+}
+
+static int avs_widget_ready(struct snd_soc_component *comp, int index,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *dw)
+{
+ struct avs_tplg_path_template *template = w->priv;
+
+ template->w = w;
+ return 0;
+}
+
+static int avs_dai_load(struct snd_soc_component *comp, int index,
+ struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm,
+ struct snd_soc_dai *dai)
+{
+ u32 fe_subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
+
+ if (pcm) {
+ dai_drv->ops = &avs_dai_fe_ops;
+ dai_drv->capture.subformats = fe_subformats;
+ dai_drv->playback.subformats = fe_subformats;
+ }
+
+ return 0;
+}
+
+static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg)
+{
+ if (link->ignore_suspend && !AVS_S0IX_SUPPORTED) {
+ dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n");
+ link->ignore_suspend = false;
+ }
+
+ if (!link->no_pcm) {
+ /* Stream control handled by IPCs. */
+ link->nonatomic = true;
+
+ /* Open LINK (BE) pipes last and close them first to prevent xruns. */
+ link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE;
+ link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE;
+ } else {
+ /* Do not ignore codec capabilities. */
+ link->dpcm_merged_format = 1;
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser manifest_parsers[] = {
+ {
+ .token = AVS_TKN_MANIFEST_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg, name),
+ .parse = parse_link_formatted_string,
+ },
+ {
+ .token = AVS_TKN_MANIFEST_VERSION_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg, version),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_manifest(struct snd_soc_component *comp, int index,
+ struct snd_soc_tplg_manifest *manifest)
+{
+ struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array;
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ size_t remaining = le32_to_cpu(manifest->priv.size);
+ bool has_init_config = true;
+ u32 offset;
+ int ret;
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, &offset);
+ /* Manifest MUST begin with a header. */
+ if (!ret && !offset)
+ ret = -EINVAL;
+ if (ret) {
+ dev_err(comp->dev, "incorrect manifest format: %d\n", ret);
+ return ret;
+ }
+
+ /* Process header which precedes any of the dictionaries. */
+ ret = avs_parse_tokens(comp, acomp->tplg, manifest_parsers,
+ ARRAY_SIZE(manifest_parsers), tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_AFMTS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "audio formats lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Libraries dictionary. */
+ ret = avs_tplg_parse_libraries(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "modcfgs_base lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Audio formats dictionary. */
+ ret = avs_tplg_parse_audio_formats(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "modcfgs_ext lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Module configs-base dictionary. */
+ ret = avs_tplg_parse_modcfgs_base(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "pplcfgs lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Module configs-ext dictionary. */
+ ret = avs_tplg_parse_modcfgs_ext(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_BINDINGS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "bindings lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Pipeline configs dictionary. */
+ ret = avs_tplg_parse_pplcfgs(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "condpath lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Bindings dictionary. */
+ ret = avs_tplg_parse_bindings(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, &offset);
+ if (ret == -ENOENT) {
+ dev_dbg(comp->dev, "init config lookup failed: %d\n", ret);
+ has_init_config = false;
+ } else if (ret) {
+ dev_err(comp->dev, "init config lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Condpaths dictionary. */
+ ret = avs_tplg_parse_condpath_templates(comp, tuples,
+ has_init_config ? offset : remaining);
+ if (ret < 0)
+ return ret;
+
+ if (!has_init_config)
+ return 0;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* Initial configs dictionary. */
+ ret = avs_tplg_parse_initial_configs(comp, tuples, remaining, &offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32, &offset);
+ if (ret == -ENOENT)
+ return 0;
+ if (ret) {
+ dev_err(comp->dev, "NHLT config lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* NHLT configs dictionary. */
+ return avs_tplg_parse_nhlt_configs(comp, tuples, remaining);
+}
+
+enum {
+ AVS_CONTROL_OPS_VOLUME = 257,
+ AVS_CONTROL_OPS_MUTE,
+};
+
+static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = {
+ {
+ .id = AVS_CONTROL_OPS_VOLUME,
+ .get = avs_control_volume_get,
+ .put = avs_control_volume_put,
+ .info = avs_control_volume_info,
+ },
+ {
+ .id = AVS_CONTROL_OPS_MUTE,
+ .get = avs_control_mute_get,
+ .put = avs_control_mute_put,
+ .info = avs_control_mute_info,
+ },
+};
+
+static const struct avs_tplg_token_parser control_parsers[] = {
+ {
+ .token = AVS_TKN_KCONTROL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_control_data, id),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int
+avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_new *ctmpl,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_soc_tplg_vendor_array *tuples;
+ struct snd_soc_tplg_mixer_control *tmc;
+ struct avs_control_data *ctl_data;
+ struct soc_mixer_control *mc;
+ size_t block_size;
+ int ret, i;
+
+ switch (le32_to_cpu(hdr->type)) {
+ case SND_SOC_TPLG_TYPE_MIXER:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mc = (struct soc_mixer_control *)ctmpl->private_value;
+ tmc = container_of(hdr, typeof(*tmc), hdr);
+ tuples = tmc->priv.array;
+ block_size = le32_to_cpu(tmc->priv.size);
+
+ ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL);
+ if (!ctl_data)
+ return -ENOMEM;
+
+ ret = parse_dictionary_entries(comp, tuples, block_size, ctl_data, 1, sizeof(*ctl_data),
+ AVS_TKN_KCONTROL_ID_U32, control_parsers,
+ ARRAY_SIZE(control_parsers));
+ if (ret)
+ return ret;
+
+ mc->dobj.private = ctl_data;
+ if (tmc->invert) {
+ ctl_data->values[0] = mc->max;
+ for (i = 1; i < mc->num_channels; i++)
+ ctl_data->values[i] = mc->max;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_ops avs_tplg_ops = {
+ .io_ops = avs_control_ops,
+ .io_ops_count = ARRAY_SIZE(avs_control_ops),
+ .control_load = avs_control_load,
+ .dapm_route_load = avs_route_load,
+ .widget_load = avs_widget_load,
+ .widget_ready = avs_widget_ready,
+ .dai_load = avs_dai_load,
+ .link_load = avs_link_load,
+ .manifest = avs_manifest,
+};
+
+struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp)
+{
+ struct avs_tplg *tplg;
+
+ tplg = devm_kzalloc(comp->card->dev, sizeof(*tplg), GFP_KERNEL);
+ if (!tplg)
+ return NULL;
+
+ tplg->comp = comp;
+ INIT_LIST_HEAD(&tplg->path_tmpl_list);
+
+ return tplg;
+}
+
+int avs_load_topology(struct snd_soc_component *comp, const char *filename)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, filename, comp->dev);
+ if (ret < 0) {
+ dev_err(comp->dev, "request topology \"%s\" failed: %d\n", filename, ret);
+ return ret;
+ }
+
+ ret = snd_soc_tplg_component_load(comp, &avs_tplg_ops, fw);
+ if (ret < 0)
+ dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret);
+
+ release_firmware(fw);
+ return ret;
+}
+
+int avs_remove_topology(struct snd_soc_component *comp)
+{
+ snd_soc_tplg_component_remove(comp);
+
+ return 0;
+}
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
new file mode 100644
index 000000000000..1cf7455b6c01
--- /dev/null
+++ b/sound/soc/intel/avs/topology.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_TPLG_H
+#define __SOUND_SOC_INTEL_AVS_TPLG_H
+
+#include <linux/list.h>
+#include "messages.h"
+
+#define INVALID_OBJECT_ID UINT_MAX
+
+struct snd_soc_component;
+
+struct avs_tplg {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ u32 version;
+ struct snd_soc_component *comp;
+
+ struct avs_tplg_library *libs;
+ u32 num_libs;
+ struct avs_audio_format *fmts;
+ u32 num_fmts;
+ struct avs_tplg_modcfg_base *modcfgs_base;
+ u32 num_modcfgs_base;
+ struct avs_tplg_modcfg_ext *modcfgs_ext;
+ u32 num_modcfgs_ext;
+ struct avs_tplg_pplcfg *pplcfgs;
+ u32 num_pplcfgs;
+ struct avs_tplg_binding *bindings;
+ u32 num_bindings;
+ struct avs_tplg_path_template *condpath_tmpls;
+ u32 num_condpath_tmpls;
+ struct avs_tplg_init_config *init_configs;
+ u32 num_init_configs;
+ struct avs_tplg_nhlt_config *nhlt_configs;
+ u32 num_nhlt_configs;
+
+ struct list_head path_tmpl_list;
+};
+
+struct avs_tplg_library {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+};
+
+/* Matches header of struct avs_mod_cfg_base. */
+struct avs_tplg_modcfg_base {
+ u32 cpc;
+ u32 ibs;
+ u32 obs;
+ u32 is_pages;
+};
+
+struct avs_tplg_pin_format {
+ u32 pin_index;
+ u32 iobs;
+ struct avs_audio_format *fmt;
+};
+
+struct avs_tplg_modcfg_ext {
+ guid_t type;
+
+ union {
+ struct {
+ u16 num_input_pins;
+ u16 num_output_pins;
+ struct avs_tplg_pin_format *pin_fmts;
+ } generic;
+ struct {
+ struct avs_audio_format *out_fmt;
+ struct avs_audio_format *blob_fmt; /* optional override */
+ u32 feature_mask;
+ union avs_virtual_index vindex;
+ u32 dma_type;
+ u32 dma_buffer_size;
+ } copier;
+ struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ u32 wake_tick_period;
+ union avs_virtual_index vindex;
+ u32 dma_type;
+ u32 dma_buffer_size;
+ struct avs_audio_format *blob_fmt; /* optional override */
+ } whm;
+ struct {
+ u32 out_channel_config;
+ u32 coefficients_select;
+ s32 coefficients[AVS_COEFF_CHANNELS_MAX];
+ u32 channel_map;
+ } updown_mix;
+ struct {
+ u32 out_freq;
+ } src;
+ struct {
+ u32 out_freq;
+ u8 mode;
+ u8 disable_jitter_buffer;
+ } asrc;
+ struct {
+ u32 cpc_lp_mode;
+ } wov;
+ struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ u32 cpc_lp_mode;
+ } aec;
+ struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ } mux;
+ struct {
+ struct avs_audio_format *out_fmt;
+ } micsel;
+ struct {
+ u32 target_volume;
+ u32 curve_type;
+ u32 curve_duration;
+ } peakvol;
+ };
+};
+
+/* Specifies path behaviour during PCM ->trigger(START) command. */
+enum avs_tplg_trigger {
+ AVS_TPLG_TRIGGER_AUTO = 0,
+};
+
+struct avs_tplg_pplcfg {
+ u16 req_size;
+ u8 priority;
+ bool lp;
+ u16 attributes;
+ enum avs_tplg_trigger trigger;
+};
+
+struct avs_tplg_binding {
+ char target_tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ u32 target_path_tmpl_id;
+ u32 target_ppl_id;
+ u32 target_mod_id;
+ u8 target_mod_pin;
+ u32 mod_id;
+ u8 mod_pin;
+ u8 is_sink;
+};
+
+struct avs_tplg_path_template_id {
+ u32 id;
+ char tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+};
+
+struct avs_tplg_path_template {
+ u32 id;
+
+ struct snd_soc_dapm_widget *w;
+
+ /* Conditional path. */
+ struct avs_tplg_path_template_id source;
+ struct avs_tplg_path_template_id sink;
+
+ struct list_head path_list;
+
+ struct avs_tplg *owner;
+ /* Driver path templates management. */
+ struct list_head node;
+};
+
+struct avs_tplg_init_config {
+ u32 id;
+
+ u8 param;
+ size_t length;
+ void *data;
+};
+
+struct avs_tplg_nhlt_config {
+ u32 id;
+ struct acpi_nhlt_config *blob;
+};
+
+struct avs_tplg_path {
+ u32 id;
+
+ /* Path format requirements. */
+ struct avs_audio_format *fe_fmt;
+ struct avs_audio_format *be_fmt;
+ /* Condpath path-variant requirements. */
+ u32 source_path_id;
+ u32 sink_path_id;
+
+ struct list_head ppl_list;
+
+ struct avs_tplg_path_template *owner;
+ /* Path template path-variants management. */
+ struct list_head node;
+};
+
+struct avs_tplg_pipeline {
+ u32 id;
+
+ struct avs_tplg_pplcfg *cfg;
+ struct avs_tplg_binding **bindings;
+ u32 num_bindings;
+ struct list_head mod_list;
+
+ struct avs_tplg_path *owner;
+ /* Path pipelines management. */
+ struct list_head node;
+};
+
+struct avs_tplg_module {
+ u32 id;
+
+ struct avs_tplg_modcfg_base *cfg_base;
+ struct avs_audio_format *in_fmt;
+ u8 core_id;
+ u8 domain;
+ struct avs_tplg_modcfg_ext *cfg_ext;
+ u32 ctl_id;
+ u32 num_config_ids;
+ u32 *config_ids;
+ struct avs_tplg_nhlt_config *nhlt_config;
+
+ struct avs_tplg_pipeline *owner;
+ /* Pipeline modules management. */
+ struct list_head node;
+};
+
+struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp);
+
+int avs_load_topology(struct snd_soc_component *comp, const char *filename);
+int avs_remove_topology(struct snd_soc_component *comp);
+
+#endif
diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c
new file mode 100644
index 000000000000..a98da521db0f
--- /dev/null
+++ b/sound/soc/intel/avs/trace.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/types.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#define BYTES_PER_LINE 16
+#define MAX_CHUNK_SIZE ((PAGE_SIZE - 150) /* Place for trace header */ \
+ / (2 * BYTES_PER_LINE + 4) /* chars per line */ \
+ * BYTES_PER_LINE)
+
+void trace_avs_msg_payload(const void *data, size_t size)
+{
+ size_t remaining = size;
+ size_t offset = 0;
+
+ while (remaining > 0) {
+ u32 chunk;
+
+ chunk = min_t(size_t, remaining, MAX_CHUNK_SIZE);
+ trace_avs_ipc_msg_payload(data, chunk, offset, size);
+
+ remaining -= chunk;
+ offset += chunk;
+ }
+}
diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h
new file mode 100644
index 000000000000..f4288d0ad5ef
--- /dev/null
+++ b/sound/soc/intel/avs/trace.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM intel_avs
+
+#if !defined(_TRACE_INTEL_AVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_INTEL_AVS_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(avs_dsp_core_op,
+
+ TP_PROTO(unsigned int reg, unsigned int mask, const char *op, bool flag),
+
+ TP_ARGS(reg, mask, op, flag),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, reg )
+ __field(unsigned int, mask )
+ __string(op, op )
+ __field(bool, flag )
+ ),
+
+ TP_fast_assign(
+ __entry->reg = reg;
+ __entry->mask = mask;
+ __assign_str(op);
+ __entry->flag = flag;
+ ),
+
+ TP_printk("%s: %d, core mask: 0x%X, prev state: 0x%08X",
+ __get_str(op), __entry->flag, __entry->mask, __entry->reg)
+);
+
+#ifndef __TRACE_INTEL_AVS_TRACE_HELPER
+#define __TRACE_INTEL_AVS_TRACE_HELPER
+
+void trace_avs_msg_payload(const void *data, size_t size);
+
+#define trace_avs_request(msg, sts, lec) \
+({ \
+ trace_avs_ipc_request_msg((msg)->header, sts, lec); \
+ trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_reply(msg, sts, lec) \
+({ \
+ trace_avs_ipc_reply_msg((msg)->header, sts, lec); \
+ trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_notify(msg, sts, lec) \
+({ \
+ trace_avs_ipc_notify_msg((msg)->header, sts, lec); \
+ trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+#endif
+
+DECLARE_EVENT_CLASS(avs_ipc_msg_hdr,
+
+ TP_PROTO(u64 header, u32 sts, u32 lec),
+
+ TP_ARGS(header, sts, lec),
+
+ TP_STRUCT__entry(
+ __field(u64, header)
+ __field(u32, sts)
+ __field(u32, lec)
+ ),
+
+ TP_fast_assign(
+ __entry->header = header;
+ __entry->sts = sts;
+ __entry->lec = lec;
+ ),
+
+ TP_printk("primary: 0x%08X, extension: 0x%08X,\n"
+ "status: 0x%08X, error: 0x%08X",
+ lower_32_bits(__entry->header), upper_32_bits(__entry->header),
+ __entry->sts, __entry->lec)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg,
+ TP_PROTO(u64 header, u32 sts, u32 lec),
+ TP_ARGS(header, sts, lec)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg,
+ TP_PROTO(u64 header, u32 sts, u32 lec),
+ TP_ARGS(header, sts, lec)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg,
+ TP_PROTO(u64 header, u32 sts, u32 lec),
+ TP_ARGS(header, sts, lec)
+);
+
+TRACE_EVENT_CONDITION(avs_ipc_msg_payload,
+
+ TP_PROTO(const u8 *data, size_t size, size_t offset, size_t total),
+
+ TP_ARGS(data, size, offset, total),
+
+ TP_CONDITION(data && size),
+
+ TP_STRUCT__entry(
+ __dynamic_array(u8, buf, size )
+ __field(size_t, offset )
+ __field(size_t, pos )
+ __field(size_t, total )
+ ),
+
+ TP_fast_assign(
+ memcpy(__get_dynamic_array(buf), data + offset, size);
+ __entry->offset = offset;
+ __entry->pos = offset + size;
+ __entry->total = total;
+ ),
+
+ TP_printk("range %zu-%zu out of %zu bytes%s",
+ __entry->offset, __entry->pos, __entry->total,
+ __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4,
+ __get_dynamic_array(buf),
+ __get_dynamic_array_len(buf), false))
+);
+
+TRACE_EVENT(avs_d0ix,
+
+ TP_PROTO(const char *op, bool proceed, u64 header),
+
+ TP_ARGS(op, proceed, header),
+
+ TP_STRUCT__entry(
+ __string(op, op )
+ __field(bool, proceed )
+ __field(u64, header )
+ ),
+
+ TP_fast_assign(
+ __assign_str(op);
+ __entry->proceed = proceed;
+ __entry->header = header;
+ ),
+
+ TP_printk("%s%s for request: 0x%08X 0x%08X",
+ __entry->proceed ? "" : "ignore ", __get_str(op),
+ lower_32_bits(__entry->header), upper_32_bits(__entry->header))
+);
+
+#endif /* _TRACE_INTEL_AVS_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c
new file mode 100644
index 000000000000..81f9b67d8e29
--- /dev/null
+++ b/sound/soc/intel/avs/utils.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+
+/* Caller responsible for holding adev->modres_mutex. */
+static int avs_module_entry_index(struct avs_dev *adev, const guid_t *uuid)
+{
+ int i;
+
+ for (i = 0; i < adev->mods_info->count; i++) {
+ struct avs_module_entry *module;
+
+ module = &adev->mods_info->entries[i];
+ if (guid_equal(&module->uuid, uuid))
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+/* Caller responsible for holding adev->modres_mutex. */
+static int avs_module_id_entry_index(struct avs_dev *adev, u32 module_id)
+{
+ int i;
+
+ for (i = 0; i < adev->mods_info->count; i++) {
+ struct avs_module_entry *module;
+
+ module = &adev->mods_info->entries[i];
+ if (module->module_id == module_id)
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry)
+{
+ int idx;
+
+ mutex_lock(&adev->modres_mutex);
+
+ idx = avs_module_entry_index(adev, uuid);
+ if (idx >= 0)
+ memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry));
+
+ mutex_unlock(&adev->modres_mutex);
+ return (idx < 0) ? idx : 0;
+}
+
+int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry)
+{
+ int idx;
+
+ mutex_lock(&adev->modres_mutex);
+
+ idx = avs_module_id_entry_index(adev, module_id);
+ if (idx >= 0)
+ memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry));
+
+ mutex_unlock(&adev->modres_mutex);
+ return (idx < 0) ? idx : 0;
+}
+
+int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid)
+{
+ struct avs_module_entry module;
+ int ret;
+
+ ret = avs_get_module_entry(adev, uuid, &module);
+ return !ret ? module.module_id : -ENOENT;
+}
+
+bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id)
+{
+ bool ret = false;
+ int idx;
+
+ mutex_lock(&adev->modres_mutex);
+
+ idx = avs_module_id_entry_index(adev, module_id);
+ if (idx >= 0)
+ ret = ida_is_empty(adev->mod_idas[idx]);
+
+ mutex_unlock(&adev->modres_mutex);
+ return ret;
+}
+
+/* Caller responsible for holding adev->modres_mutex. */
+static void avs_module_ida_destroy(struct avs_dev *adev)
+{
+ int i = adev->mods_info ? adev->mods_info->count : 0;
+
+ while (i--) {
+ ida_destroy(adev->mod_idas[i]);
+ kfree(adev->mod_idas[i]);
+ }
+ kfree(adev->mod_idas);
+}
+
+/* Caller responsible for holding adev->modres_mutex. */
+static int
+avs_module_ida_alloc(struct avs_dev *adev, struct avs_mods_info *newinfo, bool purge)
+{
+ struct avs_mods_info *oldinfo = adev->mods_info;
+ struct ida **ida_ptrs;
+ u32 tocopy_count = 0;
+ int i;
+
+ if (!purge && oldinfo) {
+ if (oldinfo->count >= newinfo->count)
+ dev_warn(adev->dev, "refreshing %d modules info with %d\n",
+ oldinfo->count, newinfo->count);
+ tocopy_count = oldinfo->count;
+ }
+
+ ida_ptrs = kcalloc(newinfo->count, sizeof(*ida_ptrs), GFP_KERNEL);
+ if (!ida_ptrs)
+ return -ENOMEM;
+
+ if (tocopy_count)
+ memcpy(ida_ptrs, adev->mod_idas, tocopy_count * sizeof(*ida_ptrs));
+
+ for (i = tocopy_count; i < newinfo->count; i++) {
+ ida_ptrs[i] = kzalloc(sizeof(**ida_ptrs), GFP_KERNEL);
+ if (!ida_ptrs[i]) {
+ while (i--)
+ kfree(ida_ptrs[i]);
+
+ kfree(ida_ptrs);
+ return -ENOMEM;
+ }
+
+ ida_init(ida_ptrs[i]);
+ }
+
+ /* If old elements have been reused, don't wipe them. */
+ if (tocopy_count)
+ kfree(adev->mod_idas);
+ else
+ avs_module_ida_destroy(adev);
+
+ adev->mod_idas = ida_ptrs;
+ return 0;
+}
+
+int avs_module_info_init(struct avs_dev *adev, bool purge)
+{
+ struct avs_mods_info *info;
+ int ret;
+
+ ret = avs_ipc_get_modules_info(adev, &info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ mutex_lock(&adev->modres_mutex);
+
+ ret = avs_module_ida_alloc(adev, info, purge);
+ if (ret < 0) {
+ dev_err(adev->dev, "initialize module idas failed: %d\n", ret);
+ goto exit;
+ }
+
+ /* Refresh current information with newly received table. */
+ kfree(adev->mods_info);
+ adev->mods_info = info;
+
+exit:
+ mutex_unlock(&adev->modres_mutex);
+ return ret;
+}
+
+void avs_module_info_free(struct avs_dev *adev)
+{
+ mutex_lock(&adev->modres_mutex);
+
+ avs_module_ida_destroy(adev);
+ kfree(adev->mods_info);
+ adev->mods_info = NULL;
+
+ mutex_unlock(&adev->modres_mutex);
+}
+
+int avs_module_id_alloc(struct avs_dev *adev, u16 module_id)
+{
+ int ret, idx, max_id;
+
+ mutex_lock(&adev->modres_mutex);
+
+ idx = avs_module_id_entry_index(adev, module_id);
+ if (idx == -ENOENT) {
+ dev_err(adev->dev, "invalid module id: %d", module_id);
+ ret = -EINVAL;
+ goto exit;
+ }
+ max_id = adev->mods_info->entries[idx].instance_max_count - 1;
+ ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL);
+exit:
+ mutex_unlock(&adev->modres_mutex);
+ return ret;
+}
+
+void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id)
+{
+ int idx;
+
+ mutex_lock(&adev->modres_mutex);
+
+ idx = avs_module_id_entry_index(adev, module_id);
+ if (idx == -ENOENT) {
+ dev_err(adev->dev, "invalid module id: %d", module_id);
+ goto exit;
+ }
+
+ ida_free(adev->mod_idas[idx], instance_id);
+exit:
+ mutex_unlock(&adev->modres_mutex);
+}
+
+/*
+ * Once driver loads FW it should keep it in memory, so we are not affected
+ * by FW removal from filesystem or even worse by loading different FW at
+ * runtime suspend/resume.
+ */
+int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name)
+{
+ struct avs_fw_entry *entry;
+ int ret;
+
+ /* first check in list if it is not already loaded */
+ list_for_each_entry(entry, &adev->fw_list, node) {
+ if (!strcmp(name, entry->name)) {
+ *fw_p = entry->fw;
+ return 0;
+ }
+ }
+
+ /* FW is not loaded, let's load it now and add to the list */
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->name = kstrdup_const(name, GFP_KERNEL);
+ if (!entry->name) {
+ kfree(entry);
+ return -ENOMEM;
+ }
+
+ ret = request_firmware(&entry->fw, name, adev->dev);
+ if (ret < 0) {
+ kfree_const(entry->name);
+ kfree(entry);
+ return ret;
+ }
+
+ *fw_p = entry->fw;
+
+ list_add_tail(&entry->node, &adev->fw_list);
+
+ return 0;
+}
+
+/*
+ * Release single FW entry, used to handle errors in functions calling
+ * avs_request_firmware()
+ */
+void avs_release_last_firmware(struct avs_dev *adev)
+{
+ struct avs_fw_entry *entry;
+
+ entry = list_last_entry(&adev->fw_list, typeof(*entry), node);
+
+ list_del(&entry->node);
+ release_firmware(entry->fw);
+ kfree_const(entry->name);
+ kfree(entry);
+}
+
+/*
+ * Release all FW entries, used on driver removal
+ */
+void avs_release_firmwares(struct avs_dev *adev)
+{
+ struct avs_fw_entry *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &adev->fw_list, node) {
+ list_del(&entry->node);
+ release_firmware(entry->fw);
+ kfree_const(entry->name);
+ kfree(entry);
+ }
+}
diff --git a/sound/soc/intel/avs/utils.h b/sound/soc/intel/avs/utils.h
new file mode 100644
index 000000000000..955a40d2c30c
--- /dev/null
+++ b/sound/soc/intel/avs/utils.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2023 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_UTILS_H
+#define __SOUND_SOC_INTEL_AVS_UTILS_H
+
+#include <sound/soc-acpi.h>
+
+extern bool obsolete_card_names;
+
+struct avs_mach_pdata {
+ struct hda_codec *codec;
+ unsigned long *tdms;
+ char *codec_name; /* DMIC only */
+
+ bool obsolete_card_names;
+};
+
+static inline bool avs_mach_singular_ssp(struct snd_soc_acpi_mach *mach)
+{
+ return hweight_long(mach->mach_params.i2s_link_mask) == 1;
+}
+
+static inline u32 avs_mach_ssp_port(struct snd_soc_acpi_mach *mach)
+{
+ return __ffs(mach->mach_params.i2s_link_mask);
+}
+
+static inline bool avs_mach_singular_tdm(struct snd_soc_acpi_mach *mach, u32 port)
+{
+ struct avs_mach_pdata *pdata = mach->pdata;
+ unsigned long *tdms = pdata->tdms;
+
+ return !tdms || (hweight_long(tdms[port]) == 1);
+}
+
+static inline u32 avs_mach_ssp_tdm(struct snd_soc_acpi_mach *mach, u32 port)
+{
+ struct avs_mach_pdata *pdata = mach->pdata;
+ unsigned long *tdms = pdata->tdms;
+
+ return tdms ? __ffs(tdms[port]) : 0;
+}
+
+static inline int avs_mach_get_ssp_tdm(struct device *dev, struct snd_soc_acpi_mach *mach,
+ int *ssp_port, int *tdm_slot)
+{
+ int port;
+
+ if (!avs_mach_singular_ssp(mach)) {
+ dev_err(dev, "Invalid SSP configuration\n");
+ return -EINVAL;
+ }
+ port = avs_mach_ssp_port(mach);
+
+ if (!avs_mach_singular_tdm(mach, port)) {
+ dev_err(dev, "Invalid TDM configuration\n");
+ return -EINVAL;
+ }
+ *ssp_port = port;
+ *tdm_slot = avs_mach_ssp_tdm(mach, *ssp_port);
+
+ return 0;
+}
+
+/*
+ * Macro to easily generate format strings
+ */
+#define AVS_STRING_FMT(prefix, suffix, ssp, tdm) \
+ (tdm) ? prefix "%d:%d" suffix : prefix "%d" suffix, (ssp), (tdm)
+
+#endif
diff --git a/sound/soc/intel/baytrail/Makefile b/sound/soc/intel/baytrail/Makefile
deleted file mode 100644
index 488408cadf6d..000000000000
--- a/sound/soc/intel/baytrail/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-snd-soc-sst-baytrail-pcm-objs := \
- sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
-
-obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
diff --git a/sound/soc/intel/baytrail/sst-baytrail-dsp.c b/sound/soc/intel/baytrail/sst-baytrail-dsp.c
deleted file mode 100644
index 01d023cc05dd..000000000000
--- a/sound/soc/intel/baytrail/sst-baytrail-dsp.c
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Intel Baytrail SST DSP driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/delay.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/firmware.h>
-
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "sst-baytrail-ipc.h"
-
-#define SST_BYT_FW_SIGNATURE_SIZE 4
-#define SST_BYT_FW_SIGN "$SST"
-
-#define SST_BYT_IRAM_OFFSET 0xC0000
-#define SST_BYT_DRAM_OFFSET 0x100000
-#define SST_BYT_SHIM_OFFSET 0x140000
-
-enum sst_ram_type {
- SST_BYT_IRAM = 1,
- SST_BYT_DRAM = 2,
- SST_BYT_CACHE = 3,
-};
-
-struct dma_block_info {
- enum sst_ram_type type; /* IRAM/DRAM */
- u32 size; /* Bytes */
- u32 ram_offset; /* Offset in I/DRAM */
- u32 rsvd; /* Reserved field */
-};
-
-struct fw_header {
- unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
- u32 file_size; /* size of fw minus this header */
- u32 modules; /* # of modules */
- u32 file_format; /* version of header format */
- u32 reserved[4];
-};
-
-struct sst_byt_fw_module_header {
- unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
- u32 mod_size; /* size of module */
- u32 blocks; /* # of blocks */
- u32 type; /* codec type, pp lib */
- u32 entry_point;
-};
-
-static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
- struct sst_byt_fw_module_header *module)
-{
- struct dma_block_info *block;
- struct sst_module *mod;
- struct sst_module_template template;
- int count;
-
- memset(&template, 0, sizeof(template));
- template.id = module->type;
- template.entry = module->entry_point;
-
- mod = sst_module_new(fw, &template, NULL);
- if (mod == NULL)
- return -ENOMEM;
-
- block = (void *)module + sizeof(*module);
-
- for (count = 0; count < module->blocks; count++) {
-
- if (block->size <= 0) {
- dev_err(dsp->dev, "block %d size invalid\n", count);
- return -EINVAL;
- }
-
- switch (block->type) {
- case SST_BYT_IRAM:
- mod->offset = block->ram_offset +
- dsp->addr.iram_offset;
- mod->type = SST_MEM_IRAM;
- break;
- case SST_BYT_DRAM:
- mod->offset = block->ram_offset +
- dsp->addr.dram_offset;
- mod->type = SST_MEM_DRAM;
- break;
- case SST_BYT_CACHE:
- mod->offset = block->ram_offset +
- (dsp->addr.fw_ext - dsp->addr.lpe);
- mod->type = SST_MEM_CACHE;
- break;
- default:
- dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
- block->type, count);
- return -EINVAL;
- }
-
- mod->size = block->size;
- mod->data = (void *)block + sizeof(*block);
-
- sst_module_alloc_blocks(mod);
-
- block = (void *)block + sizeof(*block) + block->size;
- }
- return 0;
-}
-
-static int sst_byt_parse_fw_image(struct sst_fw *sst_fw)
-{
- struct fw_header *header;
- struct sst_byt_fw_module_header *module;
- struct sst_dsp *dsp = sst_fw->dsp;
- int ret, count;
-
- /* Read the header information from the data pointer */
- header = (struct fw_header *)sst_fw->dma_buf;
-
- /* verify FW */
- if ((strncmp(header->signature, SST_BYT_FW_SIGN, 4) != 0) ||
- (sst_fw->size != header->file_size + sizeof(*header))) {
- /* Invalid FW signature */
- dev_err(dsp->dev, "Invalid FW sign/filesize mismatch\n");
- return -EINVAL;
- }
-
- dev_dbg(dsp->dev,
- "header sign=%4s size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
- header->signature, header->file_size, header->modules,
- header->file_format, sizeof(*header));
-
- module = (void *)sst_fw->dma_buf + sizeof(*header);
- for (count = 0; count < header->modules; count++) {
- /* module */
- ret = sst_byt_parse_module(dsp, sst_fw, module);
- if (ret < 0) {
- dev_err(dsp->dev, "invalid module %d\n", count);
- return ret;
- }
- module = (void *)module + sizeof(*module) + module->mod_size;
- }
-
- return 0;
-}
-
-static void sst_byt_dump_shim(struct sst_dsp *sst)
-{
- int i;
- u64 reg;
-
- for (i = 0; i <= 0xF0; i += 8) {
- reg = sst_dsp_shim_read64_unlocked(sst, i);
- if (reg)
- dev_dbg(sst->dev, "shim 0x%2.2x value 0x%16.16llx\n",
- i, reg);
- }
-
- for (i = 0x00; i <= 0xff; i += 4) {
- reg = readl(sst->addr.pci_cfg + i);
- if (reg)
- dev_dbg(sst->dev, "pci 0x%2.2x value 0x%8.8x\n",
- i, (u32)reg);
- }
-}
-
-static irqreturn_t sst_byt_irq(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- u64 isrx;
- irqreturn_t ret = IRQ_NONE;
-
- spin_lock(&sst->spinlock);
-
- isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
- if (isrx & SST_ISRX_DONE) {
- /* ADSP has processed the message request from IA */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX,
- SST_BYT_IPCX_DONE, 0);
- ret = IRQ_WAKE_THREAD;
- }
- if (isrx & SST_BYT_ISRX_REQUEST) {
- /* mask message request from ADSP and do processing later */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
- SST_BYT_IMRX_REQUEST,
- SST_BYT_IMRX_REQUEST);
- ret = IRQ_WAKE_THREAD;
- }
-
- spin_unlock(&sst->spinlock);
-
- return ret;
-}
-
-static void sst_byt_boot(struct sst_dsp *sst)
-{
- int tries = 10;
-
- /*
- * save the physical address of extended firmware block in the first
- * 4 bytes of the mailbox
- */
- memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
- &sst->pdata->fw_base, sizeof(u32));
-
- /* release stall and wait to unstall */
- sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
- while (tries--) {
- if (!(sst_dsp_shim_read64(sst, SST_CSR) &
- SST_BYT_CSR_PWAITMODE))
- break;
- msleep(100);
- }
- if (tries < 0) {
- dev_err(sst->dev, "unable to start DSP\n");
- sst_byt_dump_shim(sst);
- }
-}
-
-static void sst_byt_reset(struct sst_dsp *sst)
-{
- /* put DSP into reset, set reset vector and stall */
- sst_dsp_shim_update_bits64(sst, SST_CSR,
- SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL,
- SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL);
-
- udelay(10);
-
- /* take DSP out of reset and keep stalled for FW loading */
- sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_RST, 0);
-}
-
-struct sst_adsp_memregion {
- u32 start;
- u32 end;
- int blocks;
- enum sst_mem_type type;
-};
-
-/* BYT test stuff */
-static const struct sst_adsp_memregion byt_region[] = {
- {0xC0000, 0x100000, 8, SST_MEM_IRAM}, /* I-SRAM - 8 * 32kB */
- {0x100000, 0x140000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
-};
-
-static int sst_byt_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- sst->addr.lpe_base = pdata->lpe_base;
- sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
- if (!sst->addr.lpe)
- return -ENODEV;
-
- /* ADSP PCI MMIO config space */
- sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
- if (!sst->addr.pci_cfg) {
- iounmap(sst->addr.lpe);
- return -ENODEV;
- }
-
- /* SST Extended FW allocation */
- sst->addr.fw_ext = ioremap(pdata->fw_base, pdata->fw_size);
- if (!sst->addr.fw_ext) {
- iounmap(sst->addr.pci_cfg);
- iounmap(sst->addr.lpe);
- return -ENODEV;
- }
-
- /* SST Shim */
- sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
-
- sst_dsp_mailbox_init(sst, SST_BYT_MAILBOX_OFFSET + 0x204,
- SST_BYT_IPC_MAX_PAYLOAD_SIZE,
- SST_BYT_MAILBOX_OFFSET,
- SST_BYT_IPC_MAX_PAYLOAD_SIZE);
-
- sst->irq = pdata->irq;
-
- return 0;
-}
-
-static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- const struct sst_adsp_memregion *region;
- struct device *dev;
- int ret = -ENODEV, i, j, region_count;
- u32 offset, size;
-
- dev = sst->dev;
-
- switch (sst->id) {
- case SST_DEV_ID_BYT:
- region = byt_region;
- region_count = ARRAY_SIZE(byt_region);
- sst->addr.iram_offset = SST_BYT_IRAM_OFFSET;
- sst->addr.dram_offset = SST_BYT_DRAM_OFFSET;
- sst->addr.shim_offset = SST_BYT_SHIM_OFFSET;
- break;
- default:
- dev_err(dev, "failed to get mem resources\n");
- return ret;
- }
-
- ret = sst_byt_resource_map(sst, pdata);
- if (ret < 0) {
- dev_err(dev, "failed to map resources\n");
- return ret;
- }
-
- ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- /* enable Interrupt from both sides */
- sst_dsp_shim_update_bits64(sst, SST_IMRX, 0x3, 0x0);
- sst_dsp_shim_update_bits64(sst, SST_IMRD, 0x3, 0x0);
-
- /* register DSP memory blocks - ideally we should get this from ACPI */
- for (i = 0; i < region_count; i++) {
- offset = region[i].start;
- size = (region[i].end - region[i].start) / region[i].blocks;
-
- /* register individual memory blocks */
- for (j = 0; j < region[i].blocks; j++) {
- sst_mem_block_register(sst, offset, size,
- region[i].type, NULL, j, sst);
- offset += size;
- }
- }
-
- return 0;
-}
-
-static void sst_byt_free(struct sst_dsp *sst)
-{
- sst_mem_block_unregister_all(sst);
- iounmap(sst->addr.lpe);
- iounmap(sst->addr.pci_cfg);
- iounmap(sst->addr.fw_ext);
-}
-
-struct sst_ops sst_byt_ops = {
- .reset = sst_byt_reset,
- .boot = sst_byt_boot,
- .write = sst_shim32_write,
- .read = sst_shim32_read,
- .write64 = sst_shim32_write64,
- .read64 = sst_shim32_read64,
- .ram_read = sst_memcpy_fromio_32,
- .ram_write = sst_memcpy_toio_32,
- .irq_handler = sst_byt_irq,
- .init = sst_byt_init,
- .free = sst_byt_free,
- .parse_fw = sst_byt_parse_fw_image,
-};
diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
deleted file mode 100644
index 5bbaa667bec1..000000000000
--- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c
+++ /dev/null
@@ -1,789 +0,0 @@
-/*
- * Intel Baytrail SST IPC Support
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/types.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/kthread.h>
-#include <linux/firmware.h>
-#include <linux/io.h>
-#include <asm/div64.h>
-
-#include "sst-baytrail-ipc.h"
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "../common/sst-ipc.h"
-
-/* IPC message timeout */
-#define IPC_TIMEOUT_MSECS 300
-#define IPC_BOOT_MSECS 200
-
-#define IPC_EMPTY_LIST_SIZE 8
-
-/* IPC header bits */
-#define IPC_HEADER_MSG_ID_MASK 0xff
-#define IPC_HEADER_MSG_ID(x) ((x) & IPC_HEADER_MSG_ID_MASK)
-#define IPC_HEADER_STR_ID_SHIFT 8
-#define IPC_HEADER_STR_ID_MASK 0x1f
-#define IPC_HEADER_STR_ID(x) (((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT)
-#define IPC_HEADER_LARGE_SHIFT 13
-#define IPC_HEADER_LARGE(x) (((x) & 0x1) << IPC_HEADER_LARGE_SHIFT)
-#define IPC_HEADER_DATA_SHIFT 16
-#define IPC_HEADER_DATA_MASK 0x3fff
-#define IPC_HEADER_DATA(x) (((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT)
-
-/* mask for differentiating between notification and reply message */
-#define IPC_NOTIFICATION (0x1 << 7)
-
-/* I2L Stream config/control msgs */
-#define IPC_IA_ALLOC_STREAM 0x20
-#define IPC_IA_FREE_STREAM 0x21
-#define IPC_IA_PAUSE_STREAM 0x24
-#define IPC_IA_RESUME_STREAM 0x25
-#define IPC_IA_DROP_STREAM 0x26
-#define IPC_IA_START_STREAM 0x30
-
-/* notification messages */
-#define IPC_IA_FW_INIT_CMPLT 0x81
-#define IPC_SST_PERIOD_ELAPSED 0x97
-
-/* IPC messages between host and ADSP */
-struct sst_byt_address_info {
- u32 addr;
- u32 size;
-} __packed;
-
-struct sst_byt_str_type {
- u8 codec_type;
- u8 str_type;
- u8 operation;
- u8 protected_str;
- u8 time_slots;
- u8 reserved;
- u16 result;
-} __packed;
-
-struct sst_byt_pcm_params {
- u8 num_chan;
- u8 pcm_wd_sz;
- u8 use_offload_path;
- u8 reserved;
- u32 sfreq;
- u8 channel_map[8];
-} __packed;
-
-struct sst_byt_frames_info {
- u16 num_entries;
- u16 rsrvd;
- u32 frag_size;
- struct sst_byt_address_info ring_buf_info[8];
-} __packed;
-
-struct sst_byt_alloc_params {
- struct sst_byt_str_type str_type;
- struct sst_byt_pcm_params pcm_params;
- struct sst_byt_frames_info frame_info;
-} __packed;
-
-struct sst_byt_alloc_response {
- struct sst_byt_str_type str_type;
- u8 reserved[88];
-} __packed;
-
-struct sst_byt_start_stream_params {
- u32 byte_offset;
-} __packed;
-
-struct sst_byt_tstamp {
- u64 ring_buffer_counter;
- u64 hardware_counter;
- u64 frames_decoded;
- u64 bytes_decoded;
- u64 bytes_copied;
- u32 sampling_frequency;
- u32 channel_peak[8];
-} __packed;
-
-struct sst_byt_fw_version {
- u8 build;
- u8 minor;
- u8 major;
- u8 type;
-} __packed;
-
-struct sst_byt_fw_build_info {
- u8 date[16];
- u8 time[16];
-} __packed;
-
-struct sst_byt_fw_init {
- struct sst_byt_fw_version fw_version;
- struct sst_byt_fw_build_info build_info;
- u16 result;
- u8 module_id;
- u8 debug_info;
-} __packed;
-
-struct sst_byt_stream;
-struct sst_byt;
-
-/* stream infomation */
-struct sst_byt_stream {
- struct list_head node;
-
- /* configuration */
- struct sst_byt_alloc_params request;
- struct sst_byt_alloc_response reply;
-
- /* runtime info */
- struct sst_byt *byt;
- int str_id;
- bool commited;
- bool running;
-
- /* driver callback */
- u32 (*notify_position)(struct sst_byt_stream *stream, void *data);
- void *pdata;
-};
-
-/* SST Baytrail IPC data */
-struct sst_byt {
- struct device *dev;
- struct sst_dsp *dsp;
-
- /* stream */
- struct list_head stream_list;
-
- /* boot */
- wait_queue_head_t boot_wait;
- bool boot_complete;
- struct sst_fw *fw;
-
- /* IPC messaging */
- struct sst_generic_ipc ipc;
-};
-
-static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id)
-{
- u64 header;
-
- header = IPC_HEADER_MSG_ID(msg_id) |
- IPC_HEADER_STR_ID(str_id) |
- IPC_HEADER_LARGE(large) |
- IPC_HEADER_DATA(data) |
- SST_BYT_IPCX_BUSY;
-
- return header;
-}
-
-static inline u16 sst_byt_header_msg_id(u64 header)
-{
- return header & IPC_HEADER_MSG_ID_MASK;
-}
-
-static inline u8 sst_byt_header_str_id(u64 header)
-{
- return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK;
-}
-
-static inline u16 sst_byt_header_data(u64 header)
-{
- return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK;
-}
-
-static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt,
- int stream_id)
-{
- struct sst_byt_stream *stream;
-
- list_for_each_entry(stream, &byt->stream_list, node) {
- if (stream->str_id == stream_id)
- return stream;
- }
-
- return NULL;
-}
-
-static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg)
-{
- struct sst_byt_stream *stream;
- u64 header = msg->header;
- u8 stream_id = sst_byt_header_str_id(header);
- u8 stream_msg = sst_byt_header_msg_id(header);
-
- stream = sst_byt_get_stream(byt, stream_id);
- if (stream == NULL)
- return;
-
- switch (stream_msg) {
- case IPC_IA_DROP_STREAM:
- case IPC_IA_PAUSE_STREAM:
- case IPC_IA_FREE_STREAM:
- stream->running = false;
- break;
- case IPC_IA_START_STREAM:
- case IPC_IA_RESUME_STREAM:
- stream->running = true;
- break;
- }
-}
-
-static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
-{
- struct ipc_message *msg;
-
- msg = sst_ipc_reply_find_msg(&byt->ipc, header);
- if (msg == NULL)
- return 1;
-
- if (header & IPC_HEADER_LARGE(true)) {
- msg->rx_size = sst_byt_header_data(header);
- sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size);
- }
-
- /* update any stream states */
- sst_byt_stream_update(byt, msg);
-
- list_del(&msg->list);
- /* wake up */
- sst_ipc_tx_msg_reply_complete(&byt->ipc, msg);
-
- return 1;
-}
-
-static void sst_byt_fw_ready(struct sst_byt *byt, u64 header)
-{
- dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header);
-
- byt->boot_complete = true;
- wake_up(&byt->boot_wait);
-}
-
-static int sst_byt_process_notification(struct sst_byt *byt,
- unsigned long *flags)
-{
- struct sst_dsp *sst = byt->dsp;
- struct sst_byt_stream *stream;
- u64 header;
- u8 msg_id, stream_id;
- int handled = 1;
-
- header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
- msg_id = sst_byt_header_msg_id(header);
-
- switch (msg_id) {
- case IPC_SST_PERIOD_ELAPSED:
- stream_id = sst_byt_header_str_id(header);
- stream = sst_byt_get_stream(byt, stream_id);
- if (stream && stream->running && stream->notify_position) {
- spin_unlock_irqrestore(&sst->spinlock, *flags);
- stream->notify_position(stream, stream->pdata);
- spin_lock_irqsave(&sst->spinlock, *flags);
- }
- break;
- case IPC_IA_FW_INIT_CMPLT:
- sst_byt_fw_ready(byt, header);
- break;
- }
-
- return handled;
-}
-
-static irqreturn_t sst_byt_irq_thread(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- struct sst_byt *byt = sst_dsp_get_thread_context(sst);
- struct sst_generic_ipc *ipc = &byt->ipc;
- u64 header;
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
-
- header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
- if (header & SST_BYT_IPCD_BUSY) {
- if (header & IPC_NOTIFICATION) {
- /* message from ADSP */
- sst_byt_process_notification(byt, &flags);
- } else {
- /* reply from ADSP */
- sst_byt_process_reply(byt, header);
- }
- /*
- * clear IPCD BUSY bit and set DONE bit. Tell DSP we have
- * processed the message and can accept new. Clear data part
- * of the header
- */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD,
- SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY |
- IPC_HEADER_DATA(IPC_HEADER_DATA_MASK),
- SST_BYT_IPCD_DONE);
- /* unmask message request interrupts */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
- SST_BYT_IMRX_REQUEST, 0);
- }
-
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- /* continue to send any remaining messages... */
- queue_kthread_work(&ipc->kworker, &ipc->kwork);
-
- return IRQ_HANDLED;
-}
-
-/* stream API */
-struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
- u32 (*notify_position)(struct sst_byt_stream *stream, void *data),
- void *data)
-{
- struct sst_byt_stream *stream;
- struct sst_dsp *sst = byt->dsp;
- unsigned long flags;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (stream == NULL)
- return NULL;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- list_add(&stream->node, &byt->stream_list);
- stream->notify_position = notify_position;
- stream->pdata = data;
- stream->byt = byt;
- stream->str_id = id;
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return stream;
-}
-
-int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
- int bits)
-{
- stream->request.pcm_params.pcm_wd_sz = bits;
- return 0;
-}
-
-int sst_byt_stream_set_channels(struct sst_byt *byt,
- struct sst_byt_stream *stream, u8 channels)
-{
- stream->request.pcm_params.num_chan = channels;
- return 0;
-}
-
-int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
- unsigned int rate)
-{
- stream->request.pcm_params.sfreq = rate;
- return 0;
-}
-
-/* stream sonfiguration */
-int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
- int codec_type, int stream_type, int operation)
-{
- stream->request.str_type.codec_type = codec_type;
- stream->request.str_type.str_type = stream_type;
- stream->request.str_type.operation = operation;
- stream->request.str_type.time_slots = 0xc;
-
- return 0;
-}
-
-int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
- uint32_t buffer_addr, uint32_t buffer_size)
-{
- stream->request.frame_info.num_entries = 1;
- stream->request.frame_info.ring_buf_info[0].addr = buffer_addr;
- stream->request.frame_info.ring_buf_info[0].size = buffer_size;
- /* calculate bytes per 4 ms fragment */
- stream->request.frame_info.frag_size =
- stream->request.pcm_params.sfreq *
- stream->request.pcm_params.num_chan *
- stream->request.pcm_params.pcm_wd_sz / 8 *
- 4 / 1000;
- return 0;
-}
-
-int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- struct sst_byt_alloc_params *str_req = &stream->request;
- struct sst_byt_alloc_response *reply = &stream->reply;
- u64 header;
- int ret;
-
- header = sst_byt_header(IPC_IA_ALLOC_STREAM,
- sizeof(*str_req) + sizeof(u32),
- true, stream->str_id);
- ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req,
- sizeof(*str_req),
- reply, sizeof(*reply));
- if (ret < 0) {
- dev_err(byt->dev, "ipc: error stream commit failed\n");
- return ret;
- }
-
- stream->commited = true;
-
- return 0;
-}
-
-int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- u64 header;
- int ret = 0;
- struct sst_dsp *sst = byt->dsp;
- unsigned long flags;
-
- if (!stream->commited)
- goto out;
-
- header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id);
- ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(byt->dev, "ipc: free stream %d failed\n",
- stream->str_id);
- return -EAGAIN;
- }
-
- stream->commited = false;
-out:
- spin_lock_irqsave(&sst->spinlock, flags);
- list_del(&stream->node);
- kfree(stream);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return ret;
-}
-
-static int sst_byt_stream_operations(struct sst_byt *byt, int type,
- int stream_id, int wait)
-{
- u64 header;
-
- header = sst_byt_header(type, 0, false, stream_id);
- if (wait)
- return sst_ipc_tx_message_wait(&byt->ipc, header, NULL,
- 0, NULL, 0);
- else
- return sst_ipc_tx_message_nowait(&byt->ipc, header,
- NULL, 0);
-}
-
-/* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
- u32 start_offset)
-{
- struct sst_byt_start_stream_params start_stream;
- void *tx_msg;
- size_t size;
- u64 header;
- int ret;
-
- start_stream.byte_offset = start_offset;
- header = sst_byt_header(IPC_IA_START_STREAM,
- sizeof(start_stream) + sizeof(u32),
- true, stream->str_id);
- tx_msg = &start_stream;
- size = sizeof(start_stream);
-
- ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to start stream %d\n",
- stream->str_id);
-
- return ret;
-}
-
-int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- int ret;
-
- /* don't stop streams that are not commited */
- if (!stream->commited)
- return 0;
-
- ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM,
- stream->str_id, 0);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to stop stream %d\n",
- stream->str_id);
- return ret;
-}
-
-int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- int ret;
-
- ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM,
- stream->str_id, 0);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to pause stream %d\n",
- stream->str_id);
-
- return ret;
-}
-
-int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- int ret;
-
- ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM,
- stream->str_id, 0);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to resume stream %d\n",
- stream->str_id);
-
- return ret;
-}
-
-int sst_byt_get_dsp_position(struct sst_byt *byt,
- struct sst_byt_stream *stream, int buffer_size)
-{
- struct sst_dsp *sst = byt->dsp;
- struct sst_byt_tstamp fw_tstamp;
- u8 str_id = stream->str_id;
- u32 tstamp_offset;
-
- tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp);
- memcpy_fromio(&fw_tstamp,
- sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp));
-
- return do_div(fw_tstamp.ring_buffer_counter, buffer_size);
-}
-
-struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt)
-{
- return byt->dsp;
-}
-
-static struct sst_dsp_device byt_dev = {
- .thread = sst_byt_irq_thread,
- .ops = &sst_byt_ops,
-};
-
-int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
-
- dev_dbg(byt->dev, "dsp reset\n");
- sst_dsp_reset(byt->dsp);
- sst_ipc_drop_all(&byt->ipc);
- dev_dbg(byt->dev, "dsp in reset\n");
-
- dev_dbg(byt->dev, "free all blocks and unload fw\n");
- sst_fw_unload(byt->fw);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late);
-
-int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
- int ret;
-
- dev_dbg(byt->dev, "reload dsp fw\n");
-
- sst_dsp_reset(byt->dsp);
-
- ret = sst_fw_reload(byt->fw);
- if (ret < 0) {
- dev_err(dev, "error: failed to reload firmware\n");
- return ret;
- }
-
- /* wait for DSP boot completion */
- byt->boot_complete = false;
- sst_dsp_boot(byt->dsp);
- dev_dbg(byt->dev, "dsp booting...\n");
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_boot);
-
-int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
- int err;
-
- dev_dbg(byt->dev, "wait for dsp reboot\n");
-
- err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (err == 0) {
- dev_err(byt->dev, "ipc: error DSP boot timeout\n");
- return -EIO;
- }
-
- dev_dbg(byt->dev, "dsp rebooted\n");
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
-
-static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
-{
- if (msg->header & IPC_HEADER_LARGE(true))
- sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
-
- sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header);
-}
-
-static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
-{
- struct sst_dsp *sst = ipc->dsp;
- u64 isr, ipcd, imrx, ipcx;
-
- ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
- isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
- ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
- imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
-
- dev_err(ipc->dev,
- "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
- text, ipcx, isr, ipcd, imrx);
-}
-
-static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data,
- size_t tx_size)
-{
- /* msg content = lower 32-bit of the header + data */
- *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1);
- memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size);
- msg->tx_size += sizeof(u32);
-}
-
-static u64 byt_reply_msg_match(u64 header, u64 *mask)
-{
- /* match reply to message sent based on msg and stream IDs */
- *mask = IPC_HEADER_MSG_ID_MASK |
- IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
- header &= *mask;
-
- return header;
-}
-
-static bool byt_is_dsp_busy(struct sst_dsp *dsp)
-{
- u64 ipcx;
-
- ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
- return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
-}
-
-int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt;
- struct sst_generic_ipc *ipc;
- struct sst_fw *byt_sst_fw;
- struct sst_byt_fw_init init;
- int err;
-
- dev_dbg(dev, "initialising Byt DSP IPC\n");
-
- byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL);
- if (byt == NULL)
- return -ENOMEM;
-
- byt->dev = dev;
-
- ipc = &byt->ipc;
- ipc->dev = dev;
- ipc->ops.tx_msg = byt_tx_msg;
- ipc->ops.shim_dbg = byt_shim_dbg;
- ipc->ops.tx_data_copy = byt_tx_data_copy;
- ipc->ops.reply_msg_match = byt_reply_msg_match;
- ipc->ops.is_dsp_busy = byt_is_dsp_busy;
- ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
- ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
-
- err = sst_ipc_init(ipc);
- if (err != 0)
- goto ipc_init_err;
-
- INIT_LIST_HEAD(&byt->stream_list);
- init_waitqueue_head(&byt->boot_wait);
- byt_dev.thread_context = byt;
-
- /* init SST shim */
- byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
- if (byt->dsp == NULL) {
- err = -ENODEV;
- goto dsp_new_err;
- }
-
- ipc->dsp = byt->dsp;
-
- /* keep the DSP in reset state for base FW loading */
- sst_dsp_reset(byt->dsp);
-
- byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt);
- if (byt_sst_fw == NULL) {
- err = -ENODEV;
- dev_err(dev, "error: failed to load firmware\n");
- goto fw_err;
- }
-
- /* wait for DSP boot completion */
- sst_dsp_boot(byt->dsp);
- err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (err == 0) {
- err = -EIO;
- dev_err(byt->dev, "ipc: error DSP boot timeout\n");
- goto boot_err;
- }
-
- /* show firmware information */
- sst_dsp_inbox_read(byt->dsp, &init, sizeof(init));
- dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n",
- init.fw_version.major, init.fw_version.minor,
- init.fw_version.build, init.fw_version.type);
- dev_info(byt->dev, "Build type: %x\n", init.fw_version.type);
- dev_info(byt->dev, "Build date: %s %s\n",
- init.build_info.date, init.build_info.time);
-
- pdata->dsp = byt;
- byt->fw = byt_sst_fw;
-
- return 0;
-
-boot_err:
- sst_dsp_reset(byt->dsp);
- sst_fw_free(byt_sst_fw);
-fw_err:
- sst_dsp_free(byt->dsp);
-dsp_new_err:
- sst_ipc_fini(ipc);
-ipc_init_err:
-
- return err;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_init);
-
-void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
-
- sst_dsp_reset(byt->dsp);
- sst_fw_free_all(byt->dsp);
- sst_dsp_free(byt->dsp);
- sst_ipc_fini(&byt->ipc);
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.h b/sound/soc/intel/baytrail/sst-baytrail-ipc.h
deleted file mode 100644
index 8faff6dcf25d..000000000000
--- a/sound/soc/intel/baytrail/sst-baytrail-ipc.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Intel Baytrail SST IPC Support
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 __SST_BYT_IPC_H
-#define __SST_BYT_IPC_H
-
-#include <linux/types.h>
-
-struct sst_byt;
-struct sst_byt_stream;
-struct sst_pdata;
-extern struct sst_ops sst_byt_ops;
-
-
-#define SST_BYT_MAILBOX_OFFSET 0x144000
-#define SST_BYT_TIMESTAMP_OFFSET (SST_BYT_MAILBOX_OFFSET + 0x800)
-
-/**
- * Upfront defined maximum message size that is
- * expected by the in/out communication pipes in FW.
- */
-#define SST_BYT_IPC_MAX_PAYLOAD_SIZE 200
-
-/* stream API */
-struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
- uint32_t (*get_write_position)(struct sst_byt_stream *stream,
- void *data),
- void *data);
-
-/* stream configuration */
-int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
- int bits);
-int sst_byt_stream_set_channels(struct sst_byt *byt,
- struct sst_byt_stream *stream, u8 channels);
-int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
- unsigned int rate);
-int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
- int codec_type, int stream_type, int operation);
-int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
- uint32_t buffer_addr, uint32_t buffer_size);
-int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
-int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
-
-/* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
- u32 start_offset);
-int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
-int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
-int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
-
-int sst_byt_get_dsp_position(struct sst_byt *byt,
- struct sst_byt_stream *stream, int buffer_size);
-
-/* init */
-int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
-void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
-struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
-int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata);
-int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata);
-int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata);
-
-#endif
diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c
deleted file mode 100644
index 4765ad474544..000000000000
--- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Intel Baytrail SST PCM Support
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/dma-mapping.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "sst-baytrail-ipc.h"
-#include "../common/sst-dsp-priv.h"
-#include "../common/sst-dsp.h"
-
-#define BYT_PCM_COUNT 2
-
-static const struct snd_pcm_hardware sst_byt_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .period_bytes_min = 384,
- .period_bytes_max = 48000,
- .periods_min = 2,
- .periods_max = 250,
- .buffer_bytes_max = 96000,
-};
-
-/* private data for each PCM DSP stream */
-struct sst_byt_pcm_data {
- struct sst_byt_stream *stream;
- struct snd_pcm_substream *substream;
- struct mutex mutex;
-
- /* latest DSP DMA hw pointer */
- u32 hw_ptr;
-
- struct work_struct work;
-};
-
-/* private data for the driver */
-struct sst_byt_priv_data {
- /* runtime DSP */
- struct sst_byt *byt;
-
- /* DAI data */
- struct sst_byt_pcm_data pcm[BYT_PCM_COUNT];
-
- /* flag indicating is stream context restore needed after suspend */
- bool restore_stream;
-};
-
-/* this may get called several times by oss emulation */
-static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
- u32 rate, bits;
- u8 channels;
- int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
-
- dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data);
-
- ret = sst_byt_stream_type(byt, pcm_data->stream,
- 1, 1, !playback);
- if (ret < 0) {
- dev_err(rtd->dev, "failed to set stream format %d\n", ret);
- return ret;
- }
-
- rate = params_rate(params);
- ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate);
- if (ret < 0) {
- dev_err(rtd->dev, "could not set rate %d\n", rate);
- return ret;
- }
-
- bits = snd_pcm_format_width(params_format(params));
- ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits);
- if (ret < 0) {
- dev_err(rtd->dev, "could not set formats %d\n",
- params_rate(params));
- return ret;
- }
-
- channels = (u8)(params_channels(params) & 0xF);
- ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels);
- if (ret < 0) {
- dev_err(rtd->dev, "could not set channels %d\n",
- params_rate(params));
- return ret;
- }
-
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-
- ret = sst_byt_stream_buffer(byt, pcm_data->stream,
- substream->dma_buffer.addr,
- params_buffer_bytes(params));
- if (ret < 0) {
- dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret);
- return ret;
- }
-
- ret = sst_byt_stream_commit(byt, pcm_data->stream);
- if (ret < 0) {
- dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- dev_dbg(rtd->dev, "PCM: hw_free\n");
- snd_pcm_lib_free_pages(substream);
-
- return 0;
-}
-
-static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
- int ret;
-
- /* commit stream using existing stream params */
- ret = sst_byt_stream_commit(byt, pcm_data->stream);
- if (ret < 0) {
- dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
- return ret;
- }
-
- sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
-
- dev_dbg(rtd->dev, "stream context restored at offset %d\n",
- pcm_data->hw_ptr);
-
- return 0;
-}
-
-static void sst_byt_pcm_work(struct work_struct *work)
-{
- struct sst_byt_pcm_data *pcm_data =
- container_of(work, struct sst_byt_pcm_data, work);
-
- if (snd_pcm_running(pcm_data->substream))
- sst_byt_pcm_restore_stream_context(pcm_data->substream);
-}
-
-static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
-
- dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- pcm_data->hw_ptr = 0;
- sst_byt_stream_start(byt, pcm_data->stream, 0);
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- if (pdata->restore_stream == true)
- schedule_work(&pcm_data->work);
- else
- sst_byt_stream_resume(byt, pcm_data->stream);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- sst_byt_stream_resume(byt, pcm_data->stream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- sst_byt_stream_stop(byt, pcm_data->stream);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- pdata->restore_stream = false;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- sst_byt_stream_pause(byt, pcm_data->stream);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
-{
- struct sst_byt_pcm_data *pcm_data = data;
- struct snd_pcm_substream *substream = pcm_data->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt *byt = pdata->byt;
- u32 pos, hw_pos;
-
- hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
- snd_pcm_lib_buffer_bytes(substream));
- pcm_data->hw_ptr = hw_pos;
- pos = frames_to_bytes(runtime,
- (runtime->control->appl_ptr %
- runtime->buffer_size));
-
- dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
-
- snd_pcm_period_elapsed(substream);
- return pos;
-}
-
-static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
-
- dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
-
- return bytes_to_frames(runtime, pcm_data->hw_ptr);
-}
-
-static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
-
- dev_dbg(rtd->dev, "PCM: open\n");
-
- mutex_lock(&pcm_data->mutex);
-
- pcm_data->substream = substream;
-
- snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
-
- pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
- byt_notify_pointer, pcm_data);
- if (pcm_data->stream == NULL) {
- dev_err(rtd->dev, "failed to create stream\n");
- mutex_unlock(&pcm_data->mutex);
- return -EINVAL;
- }
-
- mutex_unlock(&pcm_data->mutex);
- return 0;
-}
-
-static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
- int ret;
-
- dev_dbg(rtd->dev, "PCM: close\n");
-
- cancel_work_sync(&pcm_data->work);
- mutex_lock(&pcm_data->mutex);
- ret = sst_byt_stream_free(byt, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "Free stream fail\n");
- goto out;
- }
- pcm_data->stream = NULL;
-
-out:
- mutex_unlock(&pcm_data->mutex);
- return ret;
-}
-
-static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- dev_dbg(rtd->dev, "PCM: mmap\n");
- return snd_pcm_lib_default_mmap(substream, vma);
-}
-
-static struct snd_pcm_ops sst_byt_pcm_ops = {
- .open = sst_byt_pcm_open,
- .close = sst_byt_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = sst_byt_pcm_hw_params,
- .hw_free = sst_byt_pcm_hw_free,
- .trigger = sst_byt_pcm_trigger,
- .pointer = sst_byt_pcm_pointer,
- .mmap = sst_byt_pcm_mmap,
-};
-
-static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_pcm *pcm = rtd->pcm;
- size_t size;
- struct snd_soc_platform *platform = rtd->platform;
- struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- int ret = 0;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
- pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- size = sst_byt_pcm_hardware.buffer_bytes_max;
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV,
- pdata->dma_dev,
- size, size);
- if (ret) {
- dev_err(rtd->dev, "dma buffer allocation failed %d\n",
- ret);
- return ret;
- }
- }
-
- return ret;
-}
-
-static struct snd_soc_dai_driver byt_dais[] = {
- {
- .name = "Baytrail PCM",
- .playback = {
- .stream_name = "System Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_3LE |
- SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Analog Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
-};
-
-static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
-{
- struct sst_pdata *plat_data = dev_get_platdata(platform->dev);
- struct sst_byt_priv_data *priv_data;
- int i;
-
- if (!plat_data)
- return -ENODEV;
-
- priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
- GFP_KERNEL);
- if (!priv_data)
- return -ENOMEM;
- priv_data->byt = plat_data->dsp;
- snd_soc_platform_set_drvdata(platform, priv_data);
-
- for (i = 0; i < BYT_PCM_COUNT; i++) {
- mutex_init(&priv_data->pcm[i].mutex);
- INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
- }
-
- return 0;
-}
-
-static int sst_byt_pcm_remove(struct snd_soc_platform *platform)
-{
- return 0;
-}
-
-static struct snd_soc_platform_driver byt_soc_platform = {
- .probe = sst_byt_pcm_probe,
- .remove = sst_byt_pcm_remove,
- .ops = &sst_byt_pcm_ops,
- .pcm_new = sst_byt_pcm_new,
-};
-
-static const struct snd_soc_component_driver byt_dai_component = {
- .name = "byt-dai",
-};
-
-#ifdef CONFIG_PM
-static int sst_byt_pcm_dev_suspend_late(struct device *dev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(dev);
- struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev);
- int ret;
-
- dev_dbg(dev, "suspending late\n");
-
- ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
- if (ret < 0) {
- dev_err(dev, "failed to suspend %d\n", ret);
- return ret;
- }
-
- priv_data->restore_stream = true;
-
- return ret;
-}
-
-static int sst_byt_pcm_dev_resume_early(struct device *dev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(dev);
- int ret;
-
- dev_dbg(dev, "resume early\n");
-
- /* load fw and boot DSP */
- ret = sst_byt_dsp_boot(dev, sst_pdata);
- if (ret)
- return ret;
-
- /* wait for FW to finish booting */
- return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
-}
-
-static const struct dev_pm_ops sst_byt_pm_ops = {
- .suspend_late = sst_byt_pcm_dev_suspend_late,
- .resume_early = sst_byt_pcm_dev_resume_early,
-};
-
-#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
-#else
-#define SST_BYT_PM_OPS NULL
-#endif
-
-static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
- int ret;
-
- ret = sst_byt_dsp_init(&pdev->dev, sst_pdata);
- if (ret < 0)
- return -ENODEV;
-
- ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform);
- if (ret < 0)
- goto err_plat;
-
- ret = snd_soc_register_component(&pdev->dev, &byt_dai_component,
- byt_dais, ARRAY_SIZE(byt_dais));
- if (ret < 0)
- goto err_comp;
-
- return 0;
-
-err_comp:
- snd_soc_unregister_platform(&pdev->dev);
-err_plat:
- sst_byt_dsp_free(&pdev->dev, sst_pdata);
- return ret;
-}
-
-static int sst_byt_pcm_dev_remove(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
- sst_byt_dsp_free(&pdev->dev, sst_pdata);
-
- return 0;
-}
-
-static struct platform_driver sst_byt_pcm_driver = {
- .driver = {
- .name = "baytrail-pcm-audio",
- .pm = SST_BYT_PM_OPS,
- },
-
- .probe = sst_byt_pcm_dev_probe,
- .remove = sst_byt_pcm_dev_remove,
-};
-module_platform_driver(sst_byt_pcm_driver);
-
-MODULE_AUTHOR("Jarkko Nikula");
-MODULE_DESCRIPTION("Baytrail PCM");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:baytrail-pcm-audio");
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
new file mode 100644
index 000000000000..c23fdb6aad4c
--- /dev/null
+++ b/sound/soc/intel/boards/Kconfig
@@ -0,0 +1,542 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig SND_SOC_INTEL_MACH
+ bool "Intel Machine drivers"
+ depends on SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL
+ help
+ Intel ASoC Machine Drivers. If you have a Intel machine that
+ has an audio controller with a DSP and I2S or DMIC port, then
+ enable this option by saying Y
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Intel ASoC machine drivers.
+
+if SND_SOC_INTEL_MACH && (SND_SOC_SOF_INTEL_COMMON || !SND_SOC_SOF_INTEL_COMMON)
+
+config SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES
+ bool "Use more user friendly long card names"
+ help
+ Some drivers report the I/O configuration to userspace through the
+ soundcard's long card name in the control user space AP. An unfortunate
+ side effect is that this long name may also be used by the GUI,
+ confusing users with information they don't need.
+ This option prevents the long name from being modified, and the I/O
+ configuration will be provided through a different component interface.
+ Select Y if userspace like UCM (Use Case Manager) uses the component
+ interface.
+ If unsure select N.
+
+config SND_SOC_INTEL_HDA_DSP_COMMON
+ tristate
+
+config SND_SOC_INTEL_SOF_MAXIM_COMMON
+ tristate
+
+config SND_SOC_INTEL_SOF_REALTEK_COMMON
+ tristate
+
+config SND_SOC_INTEL_SOF_CIRRUS_COMMON
+ tristate
+
+config SND_SOC_INTEL_SOF_NUVOTON_COMMON
+ tristate
+
+config SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_ACPI_INTEL_MATCH
+ tristate
+
+if SND_SOC_INTEL_CATPT
+
+config SND_SOC_INTEL_HASWELL_MACH
+ tristate "Haswell with RT5640 I2S codec"
+ depends on I2C
+ depends on I2C_DESIGNWARE_PLATFORM || COMPILE_TEST
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5640
+ help
+ This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell
+ Ultrabook platforms. This is a recommended option.
+ Say Y or m if you have such a device.
+ If unsure select "N".
+
+endif ## SND_SOC_INTEL_CATPT
+
+if SND_SOC_INTEL_CATPT || SND_SOC_SOF_BROADWELL
+
+config SND_SOC_INTEL_BDW_RT5650_MACH
+ tristate "Broadwell with RT5650 codec"
+ depends on I2C
+ depends on I2C_DESIGNWARE_PLATFORM || COMPILE_TEST
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5645
+ help
+ This adds the ASoC machine driver for Intel Broadwell platforms with
+ the RT5650 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BDW_RT5677_MACH
+ tristate "Broadwell with RT5677 codec"
+ depends on I2C
+ depends on I2C_DESIGNWARE_PLATFORM || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on SPI_MASTER
+ select SPI_PXA2XX
+ select SND_SOC_RT5677_SPI
+ select SND_SOC_RT5677
+ help
+ This adds support for Intel Broadwell platform based boards with
+ the RT5677 audio codec. This is a recommended option.
+ Say Y or m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BROADWELL_MACH
+ tristate "Broadwell with RT286 I2S codec"
+ depends on I2C
+ depends on I2C_DESIGNWARE_PLATFORM || COMPILE_TEST
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT286
+ help
+ This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
+ Ultrabook platforms.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+endif ## SND_SOC_INTEL_CATPT || SND_SOC_SOF_BROADWELL
+
+if SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL
+
+config SND_SOC_INTEL_BYTCR_RT5640_MACH
+ tristate "Baytrail and Baytrail-CR with RT5640 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_RT5640
+ help
+ This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
+ platforms with RT5640 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_RT5651_MACH
+ tristate "Baytrail and Baytrail-CR with RT5651 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_RT5651
+ help
+ This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
+ platforms with RT5651 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_WM5102_MACH
+ tristate "Baytrail and Baytrail-CR with WM5102 codec"
+ depends on MFD_ARIZONA && MFD_WM5102 && SPI_MASTER && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_WM5102
+ help
+ This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
+ platforms with WM5102 audio codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+ tristate "Cherrytrail & Braswell with RT5672 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_RT5670
+ help
+ This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+ platforms with RT5672 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
+ tristate "Cherrytrail & Braswell with RT5645/5650 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_RT5645
+ help
+ This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+ platforms with RT5645/5650 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
+ tristate "Cherrytrail & Braswell with MAX98090 & TI codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_MAX98090
+ select SND_SOC_TS3A227E
+ help
+ This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+ platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_NAU8824_MACH
+ tristate "Cherrytrail & Braswell with NAU88L24 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_NAU8824
+ help
+ This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+ platforms with NAU88L24 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BYT_CHT_CX2072X_MACH
+ tristate "Baytrail & Cherrytrail with CX2072X codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_CX2072X
+ help
+ This adds support for ASoC machine driver for Intel(R) Baytrail &
+ Cherrytrail platforms with Conexant CX2072X audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
+ tristate "Baytrail & Cherrytrail with DA7212/7213 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_DA7213
+ help
+ This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail
+ platforms with DA7212/7213 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BYT_CHT_ES8316_MACH
+ tristate "Baytrail & Cherrytrail with ES8316 codec"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_ACPI
+ select SND_SOC_ES8316
+ select SND_SOC_ES83XX_DSM_COMMON
+ help
+ This adds support for ASoC machine driver for Intel(R) Baytrail &
+ Cherrytrail platforms with ES8316 audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+endif ## SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL
+
+if SND_SST_ATOM_HIFI2_PLATFORM
+
+config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH
+ tristate "Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)"
+ depends on I2C && ACPI
+ depends on X86_INTEL_LPSS || COMPILE_TEST
+ help
+ This adds support for ASoC machine driver for the MinnowBoard Max or
+ Up boards and provides access to I2S signals on the Low-Speed
+ connector. This is not a recommended option outside of these cases.
+ It is not intended to be enabled by distros by default.
+ Say Y or m if you have such a device.
+
+ If unsure select "N".
+
+endif ## SND_SST_ATOM_HIFI2_PLATFORM
+
+if SND_SOC_SOF_APOLLOLAKE
+
+config SND_SOC_INTEL_SOF_WM8804_MACH
+ tristate "SOF with Wolfson/Cirrus WM8804 codec"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_WM8804_I2C
+ help
+ This adds support for ASoC machine driver for Intel platforms
+ with the Wolfson/Cirrus WM8804 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+endif ## SND_SOC_SOF_APOLLOLAKE
+
+if SND_SOC_SOF_GEMINILAKE
+
+config SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH
+ tristate "GLK with DA7219 and MAX98357A in I2S Mode"
+ imply SND_SOC_INTEL_SOF_DA7219_MACH
+ help
+ This adds support for ASoC machine driver for Geminilake platforms
+ with DA7219 + MAX98357A I2S audio codec. This option is deprecated
+ and please use SND_SOC_INTEL_SOF_DA7219_MACH instead.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
+ tristate "GLK with RT5682 and MAX98357A in I2S Mode"
+ imply SND_SOC_INTEL_SOF_RT5682_MACH
+ help
+ This adds support for ASoC machine driver for Geminilake platforms
+ with RT5682 + MAX98357A I2S audio codec. This option is deprecated
+ and please use SND_SOC_INTEL_SOF_RT5682_MACH instead.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+endif ## SND_SOC_SOF_GEMINILAKE
+
+if SND_SOC_SOF_HDA_AUDIO_CODEC
+
+config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH
+ tristate "Skylake+ with HDA Codecs"
+ depends on SND_HDA_CODEC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_DMIC
+ # SND_SOC_HDAC_HDA is already selected
+ help
+ This adds support for ASoC machine driver for Intel Skylake+
+ platforms with display (HDMI/DP) and HDA audio codecs, and
+ Smart Sound Technology (SST) integrated audio DSP.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+endif ## SND_SOC_SOF_HDA_AUDIO_CODEC
+
+if SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL
+config SND_SOC_INTEL_SOF_RT5682_MACH
+ tristate "SOF with rt5650/rt5682 codec in I2S Mode"
+ depends on I2C && ACPI
+ depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\
+ (MFD_INTEL_LPSS || COMPILE_TEST)) ||\
+ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST))
+ select SND_SOC_MAX98357A
+ select SND_SOC_MAX98373_I2C
+ select SND_SOC_MAX98390
+ select SND_SOC_RT1011
+ select SND_SOC_RT1015
+ select SND_SOC_RT1015P
+ select SND_SOC_RT5645
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ select SND_SOC_INTEL_SOF_REALTEK_COMMON
+ select SND_SOC_ACPI_INTEL_MATCH
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with rt5650 or rt5682 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_SOF_CS42L42_MACH
+ tristate "SOF with cs42l42 codec in I2S Mode"
+ depends on I2C && ACPI
+ depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\
+ (MFD_INTEL_LPSS || COMPILE_TEST))
+ select SND_SOC_CS42L42
+ select SND_SOC_MAX98357A
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ select SND_SOC_ACPI_INTEL_MATCH
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with cs42l42 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_SOF_PCM512x_MACH
+ tristate "SOF with TI PCM512x codec"
+ depends on I2C && ACPI
+ depends on (SND_SOC_SOF_HDA_AUDIO_CODEC && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\
+ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST))
+ depends on SND_HDA_CODEC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_PCM512x_I2C
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with TI PCM512x I2S audio codec.
+ Say Y or m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_SOF_ES8336_MACH
+ tristate "SOF with ES8336 or ES8326 codec in I2S mode"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
+ depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
+ select SND_SOC_ES8316
+ select SND_SOC_ES8326
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with es8336 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_INTEL_SOF_NAU8825_MACH
+ tristate "SOF with nau8825 codec in I2S Mode"
+ depends on I2C && ACPI
+ depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\
+ (MFD_INTEL_LPSS || COMPILE_TEST))
+ select SND_SOC_NAU8825
+ select SND_SOC_RT1015P
+ select SND_SOC_MAX98373_I2C
+ select SND_SOC_MAX98357A
+ select SND_SOC_NAU8315
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ select SND_SOC_INTEL_SOF_NUVOTON_COMMON
+ select SND_SOC_INTEL_SOF_REALTEK_COMMON
+ select SND_SOC_ACPI_INTEL_MATCH
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with nau8825 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL
+
+if (SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK)
+
+config SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH
+ tristate "CML_LP with DA7219 and MAX98357A in I2S Mode"
+ imply SND_SOC_INTEL_SOF_DA7219_MACH
+ help
+ This adds support for ASoC machine driver for Cometlake platforms
+ with DA7219 + MAX98357A I2S audio codec. This option is deprecated
+ and please use SND_SOC_INTEL_SOF_DA7219_MACH instead.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
+config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH
+ tristate "CML with RT1011 and RT5682 in I2S Mode"
+ imply SND_SOC_INTEL_SOF_RT5682_MACH
+ help
+ This adds support for ASoC machine driver for SOF platform with
+ RT1011 + RT5682 I2S codec. This option is deprecated and please used
+ SND_SOC_INTEL_SOF_RT5682_MACH instead.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+endif ## SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK
+
+config SND_SOC_INTEL_SOF_DA7219_MACH
+ tristate "SOF with DA7219 codec in I2S Mode"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_DA7219
+ select SND_SOC_MAX98357A
+ select SND_SOC_MAX98373_I2C
+ select SND_SOC_MAX98390
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ select SND_SOC_ACPI_INTEL_MATCH
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with Dialog DA7219 I2S audio codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+if SND_SOC_SOF_HDA_LINK
+
+config SND_SOC_INTEL_SOF_SSP_AMP_MACH
+ tristate "SOF with amplifiers in I2S Mode"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT1308
+ select SND_SOC_CS35L41_I2C
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_INTEL_SOF_REALTEK_COMMON
+ select SND_SOC_INTEL_SOF_CIRRUS_COMMON
+ select SND_SOC_ACPI_INTEL_MATCH
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with RT1308/CS35L41 I2S audio codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+endif ## SND_SOC_SOF_HDA_LINK
+
+if SND_SOC_SOF_ELKHARTLAKE
+
+config SND_SOC_INTEL_EHL_RT5660_MACH
+ tristate "EHL with RT5660 in I2S mode"
+ depends on I2C && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
+ select SND_SOC_RT5660
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ help
+ This adds support for ASoC machine driver for Elkhart Lake
+ platform with RT5660 I2S audio codec.
+
+endif ## SND_SOC_SOF_ELKHARTLAKE
+
+if SND_SOC_SOF_INTEL_SOUNDWIRE
+
+config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
+ tristate "SoundWire generic machine driver"
+ depends on I2C && SPI_MASTER && ACPI
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST
+ depends on SOUNDWIRE
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_MAX98363
+ select SND_SOC_MAX98373_I2C
+ select SND_SOC_MAX98373_SDW
+ select SND_SOC_RT700_SDW
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT712_SDCA_SDW
+ select SND_SOC_RT712_SDCA_DMIC_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT721_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
+ select SND_SOC_RT1308_SDW
+ select SND_SOC_RT1308
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT1318_SDW
+ select SND_SOC_RT1320_SDW
+ select SND_SOC_RT5682_SDW
+ select SND_SOC_CS42L42_SDW
+ select SND_SOC_CS42L43
+ select SND_SOC_CS42L43_SDW
+ select MFD_CS42L43
+ select MFD_CS42L43_SDW
+ select PINCTRL_CS42L43
+ select SPI_CS42L43
+ select SND_SOC_CS35L56_SPI
+ select SND_SOC_CS35L56_SDW
+ select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ imply SND_SOC_SDW_MOCKUP
+ help
+ Add support for Intel SoundWire-based platforms connected to
+ MAX98373, RT700, RT711, RT1308 and RT715
+ If unsure select "N".
+
+endif
+
+endif ## SND_SOC_INTEL_MACH
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 3310c0f9c356..fcd517d6c279 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -1,25 +1,73 @@
-snd-soc-sst-haswell-objs := haswell.o
-snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
-snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
-snd-soc-sst-broadwell-objs := broadwell.o
-snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
-snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
-snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
-snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
-snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
-snd-soc-skl_rt286-objs := skl_rt286.o
-snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
-snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
-
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
-obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
-obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
-obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-hsw-rt5640-y := hsw_rt5640.o
+snd-soc-sst-bdw-rt5650-mach-y := bdw-rt5650.o
+snd-soc-sst-bdw-rt5677-mach-y := bdw-rt5677.o
+snd-soc-bdw-rt286-y := bdw_rt286.o
+snd-soc-sst-sof-pcm512x-y := sof_pcm512x.o
+snd-soc-sst-sof-wm8804-y := sof_wm8804.o
+snd-soc-sst-bytcr-rt5640-y := bytcr_rt5640.o
+snd-soc-sst-bytcr-rt5651-y := bytcr_rt5651.o
+snd-soc-sst-bytcr-wm5102-y := bytcr_wm5102.o
+snd-soc-sst-cht-bsw-rt5672-y := cht_bsw_rt5672.o
+snd-soc-sst-cht-bsw-rt5645-y := cht_bsw_rt5645.o
+snd-soc-sst-cht-bsw-max98090_ti-y := cht_bsw_max98090_ti.o
+snd-soc-sst-cht-bsw-nau8824-y := cht_bsw_nau8824.o
+snd-soc-sst-byt-cht-cx2072x-y := bytcht_cx2072x.o
+snd-soc-sst-byt-cht-da7213-y := bytcht_da7213.o
+snd-soc-sst-byt-cht-es8316-y := bytcht_es8316.o
+snd-soc-sst-byt-cht-nocodec-y := bytcht_nocodec.o
+snd-soc-sof_rt5682-y := sof_rt5682.o
+snd-soc-sof_cs42l42-y := sof_cs42l42.o
+snd-soc-sof_es8336-y := sof_es8336.o
+snd-soc-sof_nau8825-y := sof_nau8825.o
+snd-soc-sof_da7219-y := sof_da7219.o
+snd-soc-skl_hda_dsp-y := skl_hda_dsp_generic.o
+snd-soc-ehl-rt5660-y := ehl_rt5660.o
+snd-soc-sof-ssp-amp-y := sof_ssp_amp.o
+snd-soc-sof-sdw-y += sof_sdw.o \
+ sof_sdw_hdmi.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_NAU8825_MACH) += snd-soc-sof_nau8825.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MACH) += snd-soc-sof_da7219.o
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-hsw-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_PCM512x_MACH) += snd-soc-sst-sof-pcm512x.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_WM8804_MACH) += snd-soc-sst-sof-wm8804.o
+obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-bdw-rt286.o
+obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5650_MACH) += snd-soc-sst-bdw-rt5650-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_WM5102_MACH) += snd-soc-sst-bytcr-wm5102.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
-obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
-obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o
-obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH) += snd-soc-sst-cht-bsw-nau8824.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH) += snd-soc-sst-byt-cht-cx2072x.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
+obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o
+obj-$(CONFIG_SND_SOC_INTEL_EHL_RT5660_MACH) += snd-soc-ehl-rt5660.o
+obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_SOF_MACH) += snd-soc-sof-sdw.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_SSP_AMP_MACH) += snd-soc-sof-ssp-amp.o
+
+# common modules
+snd-soc-intel-hda-dsp-common-y := hda_dsp_common.o
+obj-$(CONFIG_SND_SOC_INTEL_HDA_DSP_COMMON) += snd-soc-intel-hda-dsp-common.o
+
+snd-soc-intel-sof-maxim-common-y += sof_maxim_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_MAXIM_COMMON) += snd-soc-intel-sof-maxim-common.o
+
+snd-soc-intel-sof-realtek-common-y += sof_realtek_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_REALTEK_COMMON) += snd-soc-intel-sof-realtek-common.o
+
+snd-soc-intel-sof-cirrus-common-y += sof_cirrus_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_CIRRUS_COMMON) += snd-soc-intel-sof-cirrus-common.o
+
+snd-soc-intel-sof-nuvoton-common-y += sof_nuvoton_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_NUVOTON_COMMON) += snd-soc-intel-sof-nuvoton-common.o
+
+snd-soc-intel-sof-board-helpers-y += sof_board_helpers.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_BOARD_HELPERS) += snd-soc-intel-sof-board-helpers.o
diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c
new file mode 100644
index 000000000000..d25a7188f603
--- /dev/null
+++ b/sound/soc/intel/boards/bdw-rt5650.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASoC machine driver for Intel Broadwell platforms with RT5650 codec
+ *
+ * Copyright 2019, The Chromium OS Authors. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+
+#include "../../codecs/rt5645.h"
+
+struct bdw_rt5650_priv {
+ struct gpio_desc *gpio_hp_en;
+ struct snd_soc_component *component;
+};
+
+static const struct snd_soc_dapm_widget bdw_rt5650_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("DMIC Pair1", NULL),
+ SND_SOC_DAPM_MIC("DMIC Pair2", NULL),
+};
+
+static const struct snd_soc_dapm_route bdw_rt5650_map[] = {
+ /* Speakers */
+ {"Speaker", NULL, "SPOL"},
+ {"Speaker", NULL, "SPOR"},
+
+ /* Headset jack connectors */
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+
+ /* Digital MICs
+ * DMIC Pair1 are the two DMICs connected on the DMICN1 connector.
+ * DMIC Pair2 are the two DMICs connected on the DMICN2 connector.
+ * Facing the camera, DMIC Pair1 are on the left side, DMIC Pair2
+ * are on the right side.
+ */
+ {"DMIC L1", NULL, "DMIC Pair1"},
+ {"DMIC R1", NULL, "DMIC Pair1"},
+ {"DMIC L2", NULL, "DMIC Pair2"},
+ {"DMIC R2", NULL, "DMIC Pair2"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static const struct snd_kcontrol_new bdw_rt5650_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("DMIC Pair1"),
+ SOC_DAPM_PIN_SWITCH("DMIC Pair2"),
+};
+
+
+static struct snd_soc_jack headphone_jack;
+static struct snd_soc_jack mic_jack;
+
+static struct snd_soc_jack_pin headphone_jack_pin = {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+};
+
+static struct snd_soc_jack_pin mic_jack_pin = {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+};
+
+static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *chan = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The ADSP will convert the FE rate to 48k, max 4-channels */
+ rate->min = rate->max = 48000;
+ chan->min = 2;
+ chan->max = 4;
+
+ /* set SSP0 to 24 bit */
+ snd_mask_set_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int bdw_rt5650_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ /* Workaround: set codec PLL to 19.2MHz that PLL source is
+ * from MCLK(24MHz) to conform 2.4MHz DMIC clock.
+ */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
+ 24000000, 19200000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ /* The actual MCLK freq is 24MHz. The codec is told that MCLK is
+ * 24.576MHz to satisfy the requirement of rl6231_get_clk_info.
+ * ASRC is enabled on AD and DA filters to ensure good audio quality.
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, 24576000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops bdw_rt5650_ops = {
+ .hw_params = bdw_rt5650_hw_params,
+};
+
+static const unsigned int channels[] = {
+ 2, 4,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int bdw_rt5650_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* Board supports stereo and quad configurations for capture */
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ runtime->hw.channels_max = 4;
+ return snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+}
+
+static const struct snd_soc_ops bdw_rt5650_fe_ops = {
+ .startup = bdw_rt5650_fe_startup,
+};
+
+static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct bdw_rt5650_priv *bdw_rt5650 =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
+ * The ASRC clock source is clk_i2s1_asrc.
+ */
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_DA_MONO_L_FILTER |
+ RT5645_DA_MONO_R_FILTER |
+ RT5645_AD_STEREO_FILTER |
+ RT5645_AD_MONO_L_FILTER |
+ RT5645_AD_MONO_R_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret);
+ return ret;
+ }
+
+ /* Create and initialize headphone jack */
+ if (snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &headphone_jack,
+ &headphone_jack_pin, 1)) {
+ dev_err(component->dev, "Can't create headphone jack\n");
+ }
+
+ /* Create and initialize mic jack */
+ if (snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE, &mic_jack, &mic_jack_pin, 1)) {
+ dev_err(component->dev, "Can't create mic jack\n");
+ }
+
+ rt5645_set_jack_detect(component, &headphone_jack, &mic_jack, NULL);
+
+ bdw_rt5650->component = component;
+
+ return 0;
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(fe,
+ DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
+
+SND_SOC_DAILINK_DEF(be,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
+
+SND_SOC_DAILINK_DEF(ssp0_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
+
+static struct snd_soc_dai_link bdw_rt5650_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "System PCM",
+ .stream_name = "System Playback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .ops = &bdw_rt5650_fe_ops,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ SND_SOC_DAILINK_REG(fe, dummy, platform),
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .id = 0,
+ .nonatomic = 1,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = broadwell_ssp0_fixup,
+ .ops = &bdw_rt5650_ops,
+ .init = bdw_rt5650_init,
+ SND_SOC_DAILINK_REG(ssp0_port, be, platform),
+ },
+};
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bdw rt5650" /* card name will be 'sof-bdw rt5650' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bdw-rt5650"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+/* ASoC machine driver for Broadwell DSP + RT5650 */
+static struct snd_soc_card bdw_rt5650_card = {
+ .name = CARD_NAME,
+ .driver_name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .dai_link = bdw_rt5650_dais,
+ .num_links = ARRAY_SIZE(bdw_rt5650_dais),
+ .dapm_widgets = bdw_rt5650_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bdw_rt5650_widgets),
+ .dapm_routes = bdw_rt5650_map,
+ .num_dapm_routes = ARRAY_SIZE(bdw_rt5650_map),
+ .controls = bdw_rt5650_controls,
+ .num_controls = ARRAY_SIZE(bdw_rt5650_controls),
+ .fully_routed = true,
+};
+
+static int bdw_rt5650_probe(struct platform_device *pdev)
+{
+ struct bdw_rt5650_priv *bdw_rt5650;
+ struct snd_soc_acpi_mach *mach;
+ int ret;
+
+ bdw_rt5650_card.dev = &pdev->dev;
+
+ /* Allocate driver private struct */
+ bdw_rt5650 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5650_priv),
+ GFP_KERNEL);
+ if (!bdw_rt5650)
+ return -ENOMEM;
+
+ /* override platform name, if required */
+ mach = pdev->dev.platform_data;
+ ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5650_card,
+ mach->mach_params.platform);
+
+ if (ret)
+ return ret;
+
+ /* set card and driver name */
+ if (snd_soc_acpi_sof_parent(&pdev->dev)) {
+ bdw_rt5650_card.name = SOF_CARD_NAME;
+ bdw_rt5650_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ bdw_rt5650_card.name = CARD_NAME;
+ bdw_rt5650_card.driver_name = DRIVER_NAME;
+ }
+
+ snd_soc_card_set_drvdata(&bdw_rt5650_card, bdw_rt5650);
+
+ return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5650_card);
+}
+
+static struct platform_driver bdw_rt5650_audio = {
+ .probe = bdw_rt5650_probe,
+ .driver = {
+ .name = "bdw-rt5650",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(bdw_rt5650_audio)
+
+/* Module information */
+MODULE_AUTHOR("Ben Zhang <benzh@chromium.org>");
+MODULE_DESCRIPTION("Intel Broadwell RT5650 machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bdw-rt5650");
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
new file mode 100644
index 000000000000..408d20ed8cee
--- /dev/null
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ASoC machine driver for Intel Broadwell platforms with RT5677 codec
+ *
+ * Copyright (c) 2014, The Chromium OS Authors. All rights reserved.
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc-acpi.h>
+
+#include "../../codecs/rt5677.h"
+
+struct bdw_rt5677_priv {
+ struct gpio_desc *gpio_hp_en;
+ struct snd_soc_component *component;
+};
+
+static int bdw_rt5677_event_hp(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ msleep(70);
+
+ gpiod_set_value_cansleep(bdw_rt5677->gpio_hp_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget bdw_rt5677_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", bdw_rt5677_event_hp),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Local DMICs", NULL),
+ SND_SOC_DAPM_MIC("Remote DMICs", NULL),
+};
+
+static const struct snd_soc_dapm_route bdw_rt5677_map[] = {
+ /* Speakers */
+ {"Speaker", NULL, "PDM1L"},
+ {"Speaker", NULL, "PDM1R"},
+
+ /* Headset jack connectors */
+ {"Headphone", NULL, "LOUT1"},
+ {"Headphone", NULL, "LOUT2"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+
+ /* Digital MICs
+ * Local DMICs: the two DMICs on the mainboard
+ * Remote DMICs: the two DMICs on the camera module
+ */
+ {"DMIC L1", NULL, "Remote DMICs"},
+ {"DMIC R1", NULL, "Remote DMICs"},
+ {"DMIC L2", NULL, "Local DMICs"},
+ {"DMIC R2", NULL, "Local DMICs"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+ {"DSP Capture", NULL, "DSP Buffer"},
+
+ /* DSP Clock Connections */
+ { "DSP Buffer", NULL, "SSP0 CODEC IN" },
+ { "SSP0 CODEC IN", NULL, "DSPTX" },
+};
+
+static const struct snd_kcontrol_new bdw_rt5677_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Local DMICs"),
+ SOC_DAPM_PIN_SWITCH("Remote DMICs"),
+};
+
+
+static struct snd_soc_jack headphone_jack;
+static struct snd_soc_jack mic_jack;
+
+static struct snd_soc_jack_pin headphone_jack_pin = {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+};
+
+static struct snd_soc_jack_pin mic_jack_pin = {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+};
+
+static struct snd_soc_jack_gpio headphone_jack_gpio = {
+ .name = "plug-det",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+};
+
+static struct snd_soc_jack_gpio mic_jack_gpio = {
+ .name = "mic-present",
+ .report = SND_JACK_MICROPHONE,
+ .debounce_time = 200,
+ .invert = 1,
+};
+
+/* GPIO indexes defined by ACPI */
+enum {
+ RT5677_GPIO_PLUG_DET = 0,
+ RT5677_GPIO_MIC_PRESENT_L = 1,
+ RT5677_GPIO_HOTWORD_DET_L = 2,
+ RT5677_GPIO_DSP_INT = 3,
+ RT5677_GPIO_HP_AMP_SHDN_L = 4,
+};
+
+static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false };
+static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false };
+static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false };
+
+static const struct acpi_gpio_mapping bdw_rt5677_gpios[] = {
+ { "plug-det-gpios", &plug_det_gpio, 1 },
+ { "mic-present-gpios", &mic_present_gpio, 1 },
+ { "headphone-enable-gpios", &headphone_enable_gpio, 1 },
+ { NULL },
+};
+
+static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *chan = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ chan->min = chan->max = 2;
+
+ /* set SSP0 to 16 bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, 24576000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int bdw_rt5677_dsp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_PLL1, 24576000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5677_PLL1_S_MCLK,
+ 24000000, 24576000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll configuration\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops bdw_rt5677_ops = {
+ .hw_params = bdw_rt5677_hw_params,
+};
+
+static const struct snd_soc_ops bdw_rt5677_dsp_ops = {
+ .hw_params = bdw_rt5677_dsp_hw_params,
+};
+
+static const unsigned int channels[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int bdw_rt5677_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* Board supports stereo configuration only */
+ runtime->hw.channels_max = 2;
+ return snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+}
+
+static const struct snd_soc_ops bdw_rt5677_fe_ops = {
+ .startup = bdw_rt5677_fe_startup,
+};
+
+static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct bdw_rt5677_priv *bdw_rt5677 =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ ret = devm_acpi_dev_add_driver_gpios(component->dev, bdw_rt5677_gpios);
+ if (ret)
+ dev_warn(component->dev, "Failed to add driver gpios\n");
+
+ /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
+ * The ASRC clock source is clk_i2s1_asrc.
+ */
+ rt5677_sel_asrc_clk_src(component, RT5677_DA_STEREO_FILTER |
+ RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE,
+ RT5677_CLK_SEL_I2S1_ASRC);
+ /* Enable codec ASRC function for Mono ADC L.
+ * The ASRC clock source is clk_sys2_asrc.
+ */
+ rt5677_sel_asrc_clk_src(component, RT5677_AD_MONO_L_FILTER,
+ RT5677_CLK_SEL_SYS2);
+
+ /* Request rt5677 GPIO for headphone amp control */
+ bdw_rt5677->gpio_hp_en = gpiod_get(component->dev, "headphone-enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(bdw_rt5677->gpio_hp_en)) {
+ dev_err(component->dev, "Can't find HP_AMP_SHDN_L gpio\n");
+ return PTR_ERR(bdw_rt5677->gpio_hp_en);
+ }
+
+ /* Create and initialize headphone jack */
+ if (!snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &headphone_jack,
+ &headphone_jack_pin, 1)) {
+ headphone_jack_gpio.gpiod_dev = component->dev;
+ if (snd_soc_jack_add_gpios(&headphone_jack, 1,
+ &headphone_jack_gpio))
+ dev_err(component->dev, "Can't add headphone jack gpio\n");
+ } else {
+ dev_err(component->dev, "Can't create headphone jack\n");
+ }
+
+ /* Create and initialize mic jack */
+ if (!snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE, &mic_jack,
+ &mic_jack_pin, 1)) {
+ mic_jack_gpio.gpiod_dev = component->dev;
+ if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio))
+ dev_err(component->dev, "Can't add mic jack gpio\n");
+ } else {
+ dev_err(component->dev, "Can't create mic jack\n");
+ }
+ bdw_rt5677->component = component;
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+ return 0;
+}
+
+static void bdw_rt5677_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct bdw_rt5677_priv *bdw_rt5677 =
+ snd_soc_card_get_drvdata(rtd->card);
+
+ /*
+ * The .exit() can be reached without going through the .init()
+ * so explicitly test if the gpiod is valid
+ */
+ if (!IS_ERR_OR_NULL(bdw_rt5677->gpio_hp_en))
+ gpiod_put(bdw_rt5677->gpio_hp_en);
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(fe,
+ DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
+
+SND_SOC_DAILINK_DEF(be,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-aif1")));
+
+SND_SOC_DAILINK_DEF(ssp0_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
+
+/* Wake on voice interface */
+SND_SOC_DAILINK_DEFS(dsp,
+ DAILINK_COMP_ARRAY(COMP_CPU("spi-RT5677AA:00")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-dspbuffer")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("spi-RT5677AA:00")));
+
+static struct snd_soc_dai_link bdw_rt5677_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "System PCM",
+ .stream_name = "System Playback/Capture",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .ops = &bdw_rt5677_fe_ops,
+ SND_SOC_DAILINK_REG(fe, dummy, platform),
+ },
+
+ /* Non-DPCM links */
+ {
+ .name = "Codec DSP",
+ .stream_name = "Wake on Voice",
+ .capture_only = 1,
+ .ops = &bdw_rt5677_dsp_ops,
+ SND_SOC_DAILINK_REG(dsp),
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .id = 0,
+ .nonatomic = 1,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = broadwell_ssp0_fixup,
+ .ops = &bdw_rt5677_ops,
+ .init = bdw_rt5677_init,
+ .exit = bdw_rt5677_exit,
+ SND_SOC_DAILINK_REG(ssp0_port, be, platform),
+ },
+};
+
+static int bdw_rt5677_suspend_pre(struct snd_soc_card *card)
+{
+ struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dapm_context *dapm;
+
+ if (bdw_rt5677->component) {
+ dapm = snd_soc_component_to_dapm(bdw_rt5677->component);
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
+ }
+ return 0;
+}
+
+static int bdw_rt5677_resume_post(struct snd_soc_card *card)
+{
+ struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dapm_context *dapm;
+
+ if (bdw_rt5677->component) {
+ dapm = snd_soc_component_to_dapm(bdw_rt5677->component);
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+ }
+ return 0;
+}
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bdw rt5677" /* card name will be 'sof-bdw rt5677' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bdw-rt5677"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+/* ASoC machine driver for Broadwell DSP + RT5677 */
+static struct snd_soc_card bdw_rt5677_card = {
+ .name = CARD_NAME,
+ .driver_name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .dai_link = bdw_rt5677_dais,
+ .num_links = ARRAY_SIZE(bdw_rt5677_dais),
+ .dapm_widgets = bdw_rt5677_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bdw_rt5677_widgets),
+ .dapm_routes = bdw_rt5677_map,
+ .num_dapm_routes = ARRAY_SIZE(bdw_rt5677_map),
+ .controls = bdw_rt5677_controls,
+ .num_controls = ARRAY_SIZE(bdw_rt5677_controls),
+ .fully_routed = true,
+ .suspend_pre = bdw_rt5677_suspend_pre,
+ .resume_post = bdw_rt5677_resume_post,
+};
+
+static int bdw_rt5677_probe(struct platform_device *pdev)
+{
+ struct bdw_rt5677_priv *bdw_rt5677;
+ struct snd_soc_acpi_mach *mach;
+ int ret;
+
+ bdw_rt5677_card.dev = &pdev->dev;
+
+ /* Allocate driver private struct */
+ bdw_rt5677 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5677_priv),
+ GFP_KERNEL);
+ if (!bdw_rt5677)
+ return -ENOMEM;
+
+ /* override platform name, if required */
+ mach = pdev->dev.platform_data;
+ ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5677_card,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ /* set card and driver name */
+ if (snd_soc_acpi_sof_parent(&pdev->dev)) {
+ bdw_rt5677_card.name = SOF_CARD_NAME;
+ bdw_rt5677_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ bdw_rt5677_card.name = CARD_NAME;
+ bdw_rt5677_card.driver_name = DRIVER_NAME;
+ }
+
+ snd_soc_card_set_drvdata(&bdw_rt5677_card, bdw_rt5677);
+
+ return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5677_card);
+}
+
+static struct platform_driver bdw_rt5677_audio = {
+ .probe = bdw_rt5677_probe,
+ .driver = {
+ .name = "bdw-rt5677",
+ .pm = &snd_soc_pm_ops
+ },
+};
+
+module_platform_driver(bdw_rt5677_audio)
+
+/* Module information */
+MODULE_AUTHOR("Ben Zhang");
+MODULE_DESCRIPTION("Intel Broadwell RT5677 machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bdw-rt5677");
diff --git a/sound/soc/intel/boards/bdw_rt286.c b/sound/soc/intel/boards/bdw_rt286.c
new file mode 100644
index 000000000000..523ade9f31ab
--- /dev/null
+++ b/sound/soc/intel/boards/bdw_rt286.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Sound card driver for Intel Broadwell Wildcat Point with Realtek 286
+ *
+ * Copyright (C) 2013, Intel Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack card_headset;
+
+static struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("DMIC1", NULL),
+ SND_SOC_DAPM_MIC("DMIC2", NULL),
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+
+ {"Headphone Jack", NULL, "HPO Pin"},
+
+ {"MIC1", NULL, "Mic Jack"},
+ {"LINE1", NULL, "Line Jack"},
+
+ {"DMIC1 Pin", NULL, "DMIC1"},
+ {"DMIC2 Pin", NULL, "DMIC2"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static int codec_link_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &card_headset, card_headset_pins,
+ ARRAY_SIZE(card_headset_pins));
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(codec, &card_headset, NULL);
+}
+
+static void codec_link_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(codec, NULL, NULL);
+}
+
+static int codec_link_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ /* The ADSP will convert the FE rate to 48kHz, stereo. */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ /* Set SSP0 to 16 bit. */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int codec_link_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops codec_link_ops = {
+ .hw_params = codec_link_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(system, DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
+SND_SOC_DAILINK_DEF(offload0, DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
+SND_SOC_DAILINK_DEF(offload1, DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
+SND_SOC_DAILINK_DEF(loopback, DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
+
+SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
+SND_SOC_DAILINK_DEF(codec, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1")));
+SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
+
+static struct snd_soc_dai_link card_dai_links[] = {
+ /* Front End DAI links */
+ {
+ .name = "System PCM",
+ .stream_name = "System Playback/Capture",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ SND_SOC_DAILINK_REG(system, dummy, platform),
+ },
+ {
+ .name = "Offload0",
+ .stream_name = "Offload0 Playback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(offload0, dummy, platform),
+ },
+ {
+ .name = "Offload1",
+ .stream_name = "Offload1 Playback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(offload1, dummy, platform),
+ },
+ {
+ .name = "Loopback PCM",
+ .stream_name = "Loopback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(loopback, dummy, platform),
+ },
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .id = 0,
+ .nonatomic = 1,
+ .no_pcm = 1,
+ .init = codec_link_init,
+ .exit = codec_link_exit,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = codec_link_hw_params_fixup,
+ .ops = &codec_link_ops,
+ SND_SOC_DAILINK_REG(ssp0_port, codec, platform),
+ },
+};
+
+static int card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, "rt286-aif1");
+
+ if (!codec_dai)
+ return 0;
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, "rt286-aif1");
+
+ if (!codec_dai)
+ return 0;
+
+ return snd_soc_component_set_jack(codec_dai->component, &card_headset, NULL);
+}
+
+static struct snd_soc_card bdw_rt286_card = {
+ .owner = THIS_MODULE,
+ .suspend_pre = card_suspend_pre,
+ .resume_post = card_resume_post,
+ .dai_link = card_dai_links,
+ .num_links = ARRAY_SIZE(card_dai_links),
+ .controls = card_controls,
+ .num_controls = ARRAY_SIZE(card_controls),
+ .dapm_widgets = card_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(card_widgets),
+ .dapm_routes = card_routes,
+ .num_dapm_routes = ARRAY_SIZE(card_routes),
+ .fully_routed = true,
+};
+
+/* Use space before codec name to simplify card ID, and simplify driver name. */
+#define SOF_CARD_NAME "bdw rt286" /* card name will be 'sof-bdw rt286' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "broadwell-rt286"
+
+static int bdw_rt286_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ bdw_rt286_card.dev = dev;
+ mach = dev_get_platdata(dev);
+
+ ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt286_card, mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ if (snd_soc_acpi_sof_parent(dev)) {
+ bdw_rt286_card.name = SOF_CARD_NAME;
+ bdw_rt286_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ bdw_rt286_card.name = CARD_NAME;
+ }
+
+ return devm_snd_soc_register_card(dev, &bdw_rt286_card);
+}
+
+static struct platform_driver bdw_rt286_driver = {
+ .probe = bdw_rt286_probe,
+ .driver = {
+ .name = "bdw_rt286",
+ .pm = &snd_soc_pm_ops
+ },
+};
+
+module_platform_driver(bdw_rt286_driver)
+
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Sound card driver for Intel Broadwell Wildcat Point with Realtek 286");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bdw_rt286");
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
deleted file mode 100644
index 3f8a1e10bed0..000000000000
--- a/sound/soc/intel/boards/broadwell.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Intel Broadwell Wildcatpoint SST Audio
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <sound/pcm_params.h>
-
-#include "../common/sst-dsp.h"
-#include "../haswell/sst-haswell-ipc.h"
-
-#include "../../codecs/rt286.h"
-
-static struct snd_soc_jack broadwell_headset;
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin broadwell_headset_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static const struct snd_kcontrol_new broadwell_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speaker"),
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
-};
-
-static const struct snd_soc_dapm_widget broadwell_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_MIC("DMIC1", NULL),
- SND_SOC_DAPM_MIC("DMIC2", NULL),
- SND_SOC_DAPM_LINE("Line Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
-
- /* speaker */
- {"Speaker", NULL, "SPOR"},
- {"Speaker", NULL, "SPOL"},
-
- /* HP jack connectors - unknown if we have jack deteck */
- {"Headphone Jack", NULL, "HPO Pin"},
-
- /* other jacks */
- {"MIC1", NULL, "Mic Jack"},
- {"LINE1", NULL, "Line Jack"},
-
- /* digital mics */
- {"DMIC1 Pin", NULL, "DMIC1"},
- {"DMIC2 Pin", NULL, "DMIC2"},
-
- /* CODEC BE connections */
- {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
- {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
-};
-
-static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- int ret = 0;
- ret = snd_soc_card_jack_new(rtd->card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
- broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
- if (ret)
- return ret;
-
- rt286_mic_detect(codec, &broadwell_headset);
- return 0;
-}
-
-
-static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The ADSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 16 bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
- return 0;
-}
-
-static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
- SND_SOC_CLOCK_IN);
-
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec sysclk configuration\n");
- return ret;
- }
-
- return ret;
-}
-
-static struct snd_soc_ops broadwell_rt286_ops = {
- .hw_params = broadwell_rt286_hw_params,
-};
-
-static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
- struct sst_hsw *broadwell = pdata->dsp;
- int ret;
-
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to set device config\n");
- return ret;
- }
-
- return 0;
-}
-
-/* broadwell digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link broadwell_rt286_dais[] = {
- /* Front End DAI links */
- {
- .name = "System PCM",
- .stream_name = "System Playback/Capture",
- .cpu_dai_name = "System Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .init = broadwell_rtd_init,
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "Offload0",
- .stream_name = "Offload0 Playback",
- .cpu_dai_name = "Offload0 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Offload1",
- .stream_name = "Offload1 Playback",
- .cpu_dai_name = "Offload1 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Loopback PCM",
- .stream_name = "Loopback",
- .cpu_dai_name = "Loopback Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 0,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_capture = 1,
- },
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "Codec",
- .be_id = 0,
- .cpu_dai_name = "snd-soc-dummy-dai",
- .platform_name = "snd-soc-dummy",
- .no_pcm = 1,
- .codec_name = "i2c-INT343A:00",
- .codec_dai_name = "rt286-aif1",
- .init = broadwell_rt286_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_suspend = 1,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = broadwell_ssp0_fixup,
- .ops = &broadwell_rt286_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
-};
-
-static int broadwell_suspend(struct snd_soc_card *card){
- struct snd_soc_codec *codec;
-
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
- dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
- rt286_mic_detect(codec, NULL);
- break;
- }
- }
- return 0;
-}
-
-static int broadwell_resume(struct snd_soc_card *card){
- struct snd_soc_codec *codec;
-
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
- dev_dbg(codec->dev, "enabling jack detect for resume.\n");
- rt286_mic_detect(codec, &broadwell_headset);
- break;
- }
- }
- return 0;
-}
-
-/* broadwell audio machine driver for WPT + RT286S */
-static struct snd_soc_card broadwell_rt286 = {
- .name = "broadwell-rt286",
- .owner = THIS_MODULE,
- .dai_link = broadwell_rt286_dais,
- .num_links = ARRAY_SIZE(broadwell_rt286_dais),
- .controls = broadwell_controls,
- .num_controls = ARRAY_SIZE(broadwell_controls),
- .dapm_widgets = broadwell_widgets,
- .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
- .dapm_routes = broadwell_rt286_map,
- .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
- .fully_routed = true,
- .suspend_pre = broadwell_suspend,
- .resume_post = broadwell_resume,
-};
-
-static int broadwell_audio_probe(struct platform_device *pdev)
-{
- broadwell_rt286.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
-}
-
-static struct platform_driver broadwell_audio = {
- .probe = broadwell_audio_probe,
- .driver = {
- .name = "broadwell-audio",
- },
-};
-
-module_platform_driver(broadwell_audio)
-
-/* Module information */
-MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
-MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:broadwell-audio");
diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c
deleted file mode 100644
index d9f81b8d915d..000000000000
--- a/sound/soc/intel/boards/byt-max98090.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Intel Baytrail SST MAX98090 machine driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/platform_device.h>
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../../codecs/max98090.h"
-
-struct byt_max98090_private {
- struct snd_soc_jack jack;
-};
-
-static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-};
-
-static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
- {"IN34", NULL, "Headset Mic"},
- {"Headset Mic", NULL, "MICBIAS"},
- {"DMICL", NULL, "Int Mic"},
- {"Headphone", NULL, "HPL"},
- {"Headphone", NULL, "HPR"},
- {"Ext Spk", NULL, "SPKL"},
- {"Ext Spk", NULL, "SPKR"},
-};
-
-static const struct snd_kcontrol_new byt_max98090_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Ext Spk"),
-};
-
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Headphone",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "Headset Mic",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
- {
- .name = "hp-gpio",
- .idx = 0,
- .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
- .debounce_time = 200,
- },
- {
- .name = "mic-gpio",
- .idx = 1,
- .invert = 1,
- .report = SND_JACK_MICROPHONE,
- .debounce_time = 200,
- },
-};
-
-static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
-{
- int ret;
- struct snd_soc_card *card = runtime->card;
- struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card);
- struct snd_soc_jack *jack = &drv->jack;
-
- card->dapm.idle_bias_off = true;
-
- ret = snd_soc_dai_set_sysclk(runtime->codec_dai,
- M98090_REG_SYSTEM_CLOCK,
- 25000000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "Can't set codec clock %d\n", ret);
- return ret;
- }
-
- /* Enable jack detection */
- ret = snd_soc_card_jack_new(runtime->card, "Headset",
- SND_JACK_LINEOUT | SND_JACK_HEADSET, jack,
- hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
- if (ret)
- return ret;
-
- return snd_soc_jack_add_gpiods(card->dev->parent, jack,
- ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
-}
-
-static struct snd_soc_dai_link byt_max98090_dais[] = {
- {
- .name = "Baytrail Audio",
- .stream_name = "Audio",
- .cpu_dai_name = "baytrail-pcm-audio",
- .codec_dai_name = "HiFi",
- .codec_name = "i2c-193C9890:00",
- .platform_name = "baytrail-pcm-audio",
- .init = byt_max98090_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- },
-};
-
-static struct snd_soc_card byt_max98090_card = {
- .name = "byt-max98090",
- .owner = THIS_MODULE,
- .dai_link = byt_max98090_dais,
- .num_links = ARRAY_SIZE(byt_max98090_dais),
- .dapm_widgets = byt_max98090_widgets,
- .num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets),
- .dapm_routes = byt_max98090_audio_map,
- .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
- .controls = byt_max98090_controls,
- .num_controls = ARRAY_SIZE(byt_max98090_controls),
- .fully_routed = true,
-};
-
-static int byt_max98090_probe(struct platform_device *pdev)
-{
- int ret_val = 0;
- struct byt_max98090_private *priv;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
- if (!priv) {
- dev_err(&pdev->dev, "allocation failed\n");
- return -ENOMEM;
- }
-
- byt_max98090_card.dev = &pdev->dev;
- snd_soc_card_set_drvdata(&byt_max98090_card, priv);
- ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card);
- if (ret_val) {
- dev_err(&pdev->dev,
- "snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
-
- return ret_val;
-}
-
-static int byt_max98090_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card);
-
- snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
-
- return 0;
-}
-
-static struct platform_driver byt_max98090_driver = {
- .probe = byt_max98090_probe,
- .remove = byt_max98090_remove,
- .driver = {
- .name = "byt-max98090",
- .pm = &snd_soc_pm_ops,
- },
-};
-module_platform_driver(byt_max98090_driver)
-
-MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
-MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:byt-max98090");
diff --git a/sound/soc/intel/boards/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c
deleted file mode 100644
index de9788a3fd06..000000000000
--- a/sound/soc/intel/boards/byt-rt5640.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Intel Baytrail SST RT5640 machine driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/platform_device.h>
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/dmi.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../../codecs/rt5640.h"
-
-#include "../common/sst-dsp.h"
-
-static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Internal Mic", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
- {"Headset Mic", NULL, "MICBIAS1"},
- {"IN2P", NULL, "Headset Mic"},
- {"Headphone", NULL, "HPOL"},
- {"Headphone", NULL, "HPOR"},
- {"Speaker", NULL, "SPOLP"},
- {"Speaker", NULL, "SPOLN"},
- {"Speaker", NULL, "SPORP"},
- {"Speaker", NULL, "SPORN"},
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
- {"DMIC1", NULL, "Internal Mic"},
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
- {"DMIC2", NULL, "Internal Mic"},
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
- {"Internal Mic", NULL, "MICBIAS1"},
- {"IN1P", NULL, "Internal Mic"},
-};
-
-enum {
- BYT_RT5640_DMIC1_MAP,
- BYT_RT5640_DMIC2_MAP,
- BYT_RT5640_IN1_MAP,
-};
-
-#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
-#define BYT_RT5640_DMIC_EN BIT(16)
-
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN;
-
-static const struct snd_kcontrol_new byt_rt5640_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Internal Mic"),
- SOC_DAPM_PIN_SWITCH("Speaker"),
-};
-
-static int byt_rt5640_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
- params_rate(params) * 256,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(codec_dai->dev, "can't set codec clock %d\n", ret);
- return ret;
- }
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
- params_rate(params) * 64,
- params_rate(params) * 256);
- if (ret < 0) {
- dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
-{
- byt_rt5640_quirk = (unsigned long)id->driver_data;
- return 1;
-}
-
-static const struct dmi_system_id byt_rt5640_quirk_table[] = {
- {
- .callback = byt_rt5640_quirk_cb,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
- },
- .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
- },
- {
- .callback = byt_rt5640_quirk_cb,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
- },
- .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
- BYT_RT5640_DMIC_EN),
- },
- {}
-};
-
-static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
-{
- int ret;
- struct snd_soc_codec *codec = runtime->codec;
- struct snd_soc_card *card = runtime->card;
- const struct snd_soc_dapm_route *custom_map;
- int num_routes;
-
- card->dapm.idle_bias_off = true;
-
- ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
- ARRAY_SIZE(byt_rt5640_controls));
- if (ret) {
- dev_err(card->dev, "unable to add card controls\n");
- return ret;
- }
-
- dmi_check_system(byt_rt5640_quirk_table);
- switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
- case BYT_RT5640_IN1_MAP:
- custom_map = byt_rt5640_intmic_in1_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
- break;
- case BYT_RT5640_DMIC2_MAP:
- custom_map = byt_rt5640_intmic_dmic2_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
- break;
- default:
- custom_map = byt_rt5640_intmic_dmic1_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
- }
-
- ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
- if (ret)
- return ret;
-
- if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
- ret = rt5640_dmic_enable(codec, 0, 0);
- if (ret)
- return ret;
- }
-
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
-
- return ret;
-}
-
-static struct snd_soc_ops byt_rt5640_ops = {
- .hw_params = byt_rt5640_hw_params,
-};
-
-static struct snd_soc_dai_link byt_rt5640_dais[] = {
- {
- .name = "Baytrail Audio",
- .stream_name = "Audio",
- .cpu_dai_name = "baytrail-pcm-audio",
- .codec_dai_name = "rt5640-aif1",
- .codec_name = "i2c-10EC5640:00",
- .platform_name = "baytrail-pcm-audio",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .init = byt_rt5640_init,
- .ops = &byt_rt5640_ops,
- },
-};
-
-static struct snd_soc_card byt_rt5640_card = {
- .name = "byt-rt5640",
- .owner = THIS_MODULE,
- .dai_link = byt_rt5640_dais,
- .num_links = ARRAY_SIZE(byt_rt5640_dais),
- .dapm_widgets = byt_rt5640_widgets,
- .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
- .dapm_routes = byt_rt5640_audio_map,
- .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
- .fully_routed = true,
-};
-
-static int byt_rt5640_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &byt_rt5640_card;
-
- card->dev = &pdev->dev;
- return devm_snd_soc_register_card(&pdev->dev, card);
-}
-
-static struct platform_driver byt_rt5640_audio = {
- .probe = byt_rt5640_probe,
- .driver = {
- .name = "byt-rt5640",
- .pm = &snd_soc_pm_ops,
- },
-};
-module_platform_driver(byt_rt5640_audio)
-
-MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
-MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:byt-rt5640");
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
new file mode 100644
index 000000000000..0a7e6d2e37cb
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ASoC DPCM Machine driver for Baytrail / Cherrytrail platforms with
+// CX2072X codec
+//
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/cx2072x.h"
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_cht_cx2072x_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_cht_cx2072x_audio_map[] = {
+ /* External Speakers: HFL, HFR */
+ {"Headphone", NULL, "PORTA"},
+ {"Ext Spk", NULL, "PORTG"},
+ {"PORTC", NULL, "Int Mic"},
+ {"PORTD", NULL, "Headset Mic"},
+
+ {"Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+ {"ssp2 Rx", NULL, "Capture"},
+};
+
+static const struct snd_kcontrol_new byt_cht_cx2072x_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack byt_cht_cx2072x_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin byt_cht_cx2072x_headset_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct acpi_gpio_params byt_cht_cx2072x_headset_gpios;
+static const struct acpi_gpio_mapping byt_cht_cx2072x_acpi_gpios[] = {
+ { "headset-gpios", &byt_cht_cx2072x_headset_gpios, 1 },
+ {},
+};
+
+static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ if (devm_acpi_dev_add_driver_gpios(codec->dev,
+ byt_cht_cx2072x_acpi_gpios))
+ dev_warn(rtd->dev, "Unable to add GPIO mapping table\n");
+
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ /* set the default PLL rate, the clock is handled by the codec driver */
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), CX2072X_MCLK_EXTERNAL_PLL,
+ 19200000, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(rtd->dev, "Could not set sysclk\n");
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &byt_cht_cx2072x_headset,
+ byt_cht_cx2072x_headset_pins,
+ ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
+ if (ret)
+ return ret;
+
+ snd_soc_component_set_jack(codec, &byt_cht_cx2072x_headset, NULL);
+
+ snd_soc_dai_set_bclk_ratio(snd_soc_rtd_to_codec(rtd, 0), 50);
+
+ return 0;
+}
+
+static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret;
+
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static const struct snd_soc_ops byt_cht_cx2072x_aif1_ops = {
+ .startup = byt_cht_cx2072x_aif1_startup,
+};
+
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+
+SND_SOC_DAILINK_DEF(cx2072x,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-14F10720:00", "cx2072x-hifi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
+static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .ops = &byt_cht_cx2072x_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &byt_cht_cx2072x_aif1_ops,
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
+ },
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .init = byt_cht_cx2072x_init,
+ .be_hw_params_fixup = byt_cht_cx2072x_fixup,
+ SND_SOC_DAILINK_REG(ssp2, cx2072x, platform),
+ },
+};
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht cx2072x" /* card name will be 'sof-bytcht cx2072x' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bytcht-cx2072x"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+/* SoC card */
+static struct snd_soc_card byt_cht_cx2072x_card = {
+ .name = CARD_NAME,
+ .driver_name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .dai_link = byt_cht_cx2072x_dais,
+ .num_links = ARRAY_SIZE(byt_cht_cx2072x_dais),
+ .dapm_widgets = byt_cht_cx2072x_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets),
+ .dapm_routes = byt_cht_cx2072x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map),
+ .controls = byt_cht_cx2072x_controls,
+ .num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls),
+};
+
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct acpi_device *adev;
+ int dai_index = 0;
+ bool sof_parent;
+ int i, ret;
+
+ byt_cht_cx2072x_card.dev = &pdev->dev;
+ mach = dev_get_platdata(&pdev->dev);
+
+ /* fix index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) {
+ if (byt_cht_cx2072x_dais[i].num_codecs &&
+ !strcmp(byt_cht_cx2072x_dais[i].codecs->name,
+ "i2c-14F10720:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name), "i2c-%s",
+ acpi_dev_name(adev));
+ byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name;
+ } else {
+ dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ acpi_dev_put(adev);
+
+ /* override platform name, if required */
+ ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ byt_cht_cx2072x_card.name = SOF_CARD_NAME;
+ byt_cht_cx2072x_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ byt_cht_cx2072x_card.name = CARD_NAME;
+ byt_cht_cx2072x_card.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ pdev->dev.driver->pm = &snd_soc_pm_ops;
+
+ return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card);
+}
+
+static struct platform_driver snd_byt_cht_cx2072x_driver = {
+ .driver = {
+ .name = "bytcht_cx2072x",
+ },
+ .probe = snd_byt_cht_cx2072x_probe,
+};
+module_platform_driver(snd_byt_cht_cx2072x_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_cx2072x");
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
new file mode 100644
index 000000000000..31141d4b6b25
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and
+ * Cherrytrail-based platforms, with Dialog DA7213 codec
+ *
+ * Copyright (C) 2017 Intel Corporation
+ * Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/da7213.h"
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Mic"),
+ SOC_DAPM_PIN_SWITCH("Aux In"),
+};
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_LINE("Aux In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ {"AUXL", NULL, "Aux In"},
+ {"AUXR", NULL, "Aux In"},
+
+ /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */
+ {"MIC1", NULL, "Headset Mic"},
+ {"MIC2", NULL, "Mic"},
+
+ /* SOC-codec link */
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Rx", NULL, "Capture"},
+};
+
+static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static int aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK,
+ 19200000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static int aif1_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7213_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops aif1_ops = {
+ .startup = aif1_startup,
+};
+
+static const struct snd_soc_ops ssp2_ops = {
+ .hw_params = aif1_hw_params,
+ .hw_free = aif1_hw_free,
+
+};
+
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7213:00",
+ "da7213-hifi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
+static struct snd_soc_dai_link dailink[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .ops = &aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &aif1_ops,
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
+ },
+ /* CODEC<->CODEC link */
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .be_hw_params_fixup = codec_fixup,
+ .ops = &ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
+ },
+};
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht da7213" /* card name will be 'sof-bytcht da7213' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bytcht-da7213"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+/* SoC card */
+static struct snd_soc_card bytcht_da7213_card = {
+ .name = CARD_NAME,
+ .driver_name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .dai_link = dailink,
+ .num_links = ARRAY_SIZE(dailink),
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .dapm_widgets = dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+static int bytcht_da7213_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct snd_soc_acpi_mach *mach;
+ const char *platform_name;
+ struct acpi_device *adev;
+ bool sof_parent;
+ int dai_index = 0;
+ int ret_val = 0;
+ int i;
+
+ mach = pdev->dev.platform_data;
+ card = &bytcht_da7213_card;
+ card->dev = &pdev->dev;
+
+ /* fix index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(dailink); i++) {
+ if (dailink[i].num_codecs &&
+ !strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ dailink[dai_index].codecs->name = codec_name;
+ } else {
+ dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ acpi_dev_put(adev);
+
+ /* override platform name, if required */
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name);
+ if (ret_val)
+ return ret_val;
+
+ sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ bytcht_da7213_card.name = SOF_CARD_NAME;
+ bytcht_da7213_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ bytcht_da7213_card.name = CARD_NAME;
+ bytcht_da7213_card.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ pdev->dev.driver->pm = &snd_soc_pm_ops;
+
+ ret_val = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, card);
+ return ret_val;
+}
+
+static struct platform_driver bytcht_da7213_driver = {
+ .driver = {
+ .name = "bytcht_da7213",
+ },
+ .probe = bytcht_da7213_probe,
+};
+module_platform_driver(bytcht_da7213_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_da7213");
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
new file mode 100644
index 000000000000..192e2a394ff3
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail
+ * platforms with Everest ES8316 SoC
+ *
+ * Copyright (C) 2017 Endless Mobile, Inc.
+ * Authors: David Yang <yangxiaohua@everest-semi.com>,
+ * Daniel Drake <drake@endlessm.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/es83xx-dsm-common.h"
+#include "../atom/sst-atom-controls.h"
+#include "../common/soc-intel-quirks.h"
+
+/* jd-inv + terminating entry */
+#define MAX_NO_PROPS 2
+
+struct byt_cht_es8316_private {
+ struct clk *mclk;
+ struct snd_soc_jack jack;
+ struct gpio_desc *speaker_en_gpio;
+ struct device *codec_dev;
+ bool speaker_en;
+};
+
+enum {
+ BYT_CHT_ES8316_INTMIC_IN1_MAP,
+ BYT_CHT_ES8316_INTMIC_IN2_MAP,
+};
+
+#define BYT_CHT_ES8316_MAP_MASK GENMASK(3, 0)
+#define BYT_CHT_ES8316_MAP(quirk) ((quirk) & BYT_CHT_ES8316_MAP_MASK)
+#define BYT_CHT_ES8316_SSP0 BIT(16)
+#define BYT_CHT_ES8316_MONO_SPEAKER BIT(17)
+#define BYT_CHT_ES8316_JD_INVERTED BIT(18)
+
+static unsigned long quirk;
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ int map;
+
+ map = BYT_CHT_ES8316_MAP(quirk);
+ switch (map) {
+ case BYT_CHT_ES8316_INTMIC_IN1_MAP:
+ dev_info(dev, "quirk IN1_MAP enabled");
+ break;
+ case BYT_CHT_ES8316_INTMIC_IN2_MAP:
+ dev_info(dev, "quirk IN2_MAP enabled");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to INTMIC_IN1_MAP\n", map);
+ quirk &= ~BYT_CHT_ES8316_MAP_MASK;
+ quirk |= BYT_CHT_ES8316_INTMIC_IN1_MAP;
+ break;
+ }
+
+ if (quirk & BYT_CHT_ES8316_SSP0)
+ dev_info(dev, "quirk SSP0 enabled");
+ if (quirk & BYT_CHT_ES8316_MONO_SPEAKER)
+ dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+ if (quirk & BYT_CHT_ES8316_JD_INVERTED)
+ dev_info(dev, "quirk JD_INVERTED enabled\n");
+}
+
+static int byt_cht_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->speaker_en = true;
+ else
+ priv->speaker_en = false;
+
+ gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ byt_cht_es8316_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled by the "Speaker Power" supply.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in1_map[] = {
+ {"MIC1", NULL, "Internal Mic"},
+ {"MIC2", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in2_map[] = {
+ {"MIC2", NULL, "Internal Mic"},
+ {"MIC1", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_ssp0_map[] = {
+ {"Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
+ {"ssp0 Rx", NULL, "Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_ssp2_map[] = {
+ {"Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx" },
+ {"codec_in1", NULL, "ssp2 Rx" },
+ {"ssp2 Rx", NULL, "Capture"},
+};
+
+static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+ const struct snd_soc_dapm_route *custom_map;
+ int num_routes;
+ int ret;
+
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ switch (BYT_CHT_ES8316_MAP(quirk)) {
+ case BYT_CHT_ES8316_INTMIC_IN1_MAP:
+ default:
+ custom_map = byt_cht_es8316_intmic_in1_map;
+ num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in1_map);
+ break;
+ case BYT_CHT_ES8316_INTMIC_IN2_MAP:
+ custom_map = byt_cht_es8316_intmic_in2_map;
+ num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in2_map);
+ break;
+ }
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ if (quirk & BYT_CHT_ES8316_SSP0) {
+ custom_map = byt_cht_es8316_ssp0_map;
+ num_routes = ARRAY_SIZE(byt_cht_es8316_ssp0_map);
+ } else {
+ custom_map = byt_cht_es8316_ssp2_map;
+ num_routes = ARRAY_SIZE(byt_cht_es8316_ssp2_map);
+ }
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ /*
+ * The firmware might enable the clock at boot (this information
+ * may or may not be reflected in the enable clock register).
+ * To change the rate we must disable the clock first to cover these
+ * cases. Due to common clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled, we need to enable
+ * the clock first.
+ */
+ ret = clk_prepare_enable(priv->mclk);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
+
+ ret = clk_set_rate(priv->mclk, 19200000);
+ if (ret)
+ dev_err(card->dev, "unable to set MCLK rate\n");
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret)
+ dev_err(card->dev, "unable to enable MCLK\n");
+
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(runtime, 0), 0, 19200000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, byt_cht_es8316_jack_pins,
+ ARRAY_SIZE(byt_cht_es8316_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_soc_component_set_jack(codec, &priv->jack, NULL);
+
+ return 0;
+}
+
+static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret, bits;
+
+ /* The DSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ if (quirk & BYT_CHT_ES8316_SSP0) {
+ /* set SSP0 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ bits = 16;
+ } else {
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ bits = 24;
+ }
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP
+ );
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static const struct snd_soc_ops byt_cht_es8316_aif1_ops = {
+ .startup = byt_cht_es8316_aif1_startup,
+};
+
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8316:00", "ES8316 HiFi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
+static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .ops = &byt_cht_es8316_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
+ },
+
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &byt_cht_es8316_aif1_ops,
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
+ },
+
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .be_hw_params_fixup = byt_cht_es8316_codec_fixup,
+ .init = byt_cht_es8316_init,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
+ },
+};
+
+
+/* SoC card */
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
+static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */
+#endif
+static char components_string[32]; /* = "cfg-spk:* cfg-mic:* */
+
+static int byt_cht_es8316_suspend(struct snd_soc_card *card)
+{
+ struct snd_soc_component *component;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, codec_name)) {
+ dev_dbg(component->dev, "disabling jack detect before suspend\n");
+ snd_soc_component_set_jack(component, NULL, NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int byt_cht_es8316_resume(struct snd_soc_card *card)
+{
+ struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, codec_name)) {
+ dev_dbg(component->dev, "re-enabling jack detect after resume\n");
+ snd_soc_component_set_jack(component, &priv->jack, NULL);
+ break;
+ }
+ }
+
+ /*
+ * Some Cherry Trail boards with an ES8316 codec have a bug in their
+ * ACPI tables where the MSSL1680 touchscreen's _PS0 and _PS3 methods
+ * wrongly also set the speaker-enable GPIO to 1/0. Testing has shown
+ * that this really is a bug and the GPIO has no influence on the
+ * touchscreen at all.
+ *
+ * The silead.c touchscreen driver does not support runtime suspend, so
+ * the GPIO can only be changed underneath us during a system suspend.
+ * This resume() function runs from a pm complete() callback, and thus
+ * is guaranteed to run after the touchscreen driver/ACPI-subsys has
+ * brought the touchscreen back up again (and thus changed the GPIO).
+ *
+ * So to work around this we pass GPIOD_FLAGS_BIT_NONEXCLUSIVE when
+ * requesting the GPIO and we set its value here to undo any changes
+ * done by the touchscreen's broken _PS0 ACPI method.
+ */
+ gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en);
+
+ return 0;
+}
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht es8316" /* card name will be 'sof-bytcht es8316' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bytcht-es8316"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+static struct snd_soc_card byt_cht_es8316_card = {
+ .owner = THIS_MODULE,
+ .dai_link = byt_cht_es8316_dais,
+ .num_links = ARRAY_SIZE(byt_cht_es8316_dais),
+ .dapm_widgets = byt_cht_es8316_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets),
+ .dapm_routes = byt_cht_es8316_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map),
+ .controls = byt_cht_es8316_controls,
+ .num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
+ .fully_routed = true,
+ .suspend_pre = byt_cht_es8316_suspend,
+ .resume_post = byt_cht_es8316_resume,
+};
+
+static const struct acpi_gpio_params first_gpio = { 0, 0, false };
+
+static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = {
+ { "speaker-enable-gpios", &first_gpio, 1 },
+ { },
+};
+
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id byt_cht_es8316_quirk_table[] = {
+ { /* Irbis NB41 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "IRBIS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NB41"),
+ },
+ .driver_data = (void *)(BYT_CHT_ES8316_SSP0
+ | BYT_CHT_ES8316_INTMIC_IN2_MAP
+ | BYT_CHT_ES8316_JD_INVERTED),
+ },
+ { /* Nanote UMPC-01 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UMPC-01"),
+ },
+ .driver_data = (void *)BYT_CHT_ES8316_INTMIC_IN1_MAP,
+ },
+ { /* Teclast X98 Plus II */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"),
+ },
+ .driver_data = (void *)(BYT_CHT_ES8316_INTMIC_IN1_MAP
+ | BYT_CHT_ES8316_JD_INVERTED),
+ },
+ {}
+};
+
+static int byt_cht_es8316_get_quirks_from_dsm(struct byt_cht_es8316_private *priv,
+ bool is_bytcr)
+{
+ int ret, val1, val2, dsm_quirk = 0;
+
+ if (is_bytcr)
+ dsm_quirk |= BYT_CHT_ES8316_SSP0;
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_MAINMIC_TYPE_ARG, &val1);
+ if (ret < 0)
+ return ret;
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_HPMIC_TYPE_ARG, &val2);
+ if (ret < 0)
+ return ret;
+
+ if (val1 == PLATFORM_MIC_AMIC_LIN1RIN1 && val2 == PLATFORM_MIC_AMIC_LIN2RIN2) {
+ dsm_quirk |= BYT_CHT_ES8316_INTMIC_IN1_MAP;
+ } else if (val1 == PLATFORM_MIC_AMIC_LIN2RIN2 && val2 == PLATFORM_MIC_AMIC_LIN1RIN1) {
+ dsm_quirk |= BYT_CHT_ES8316_INTMIC_IN2_MAP;
+ } else {
+ dev_warn(priv->codec_dev, "Unknown mic settings mainmic 0x%02x hpmic 0x%02x\n",
+ val1, val2);
+ return -EINVAL;
+ }
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_SPK_TYPE_ARG, &val1);
+ if (ret < 0)
+ return ret;
+
+ switch (val1) {
+ case PLATFORM_SPK_MONO:
+ dsm_quirk |= BYT_CHT_ES8316_MONO_SPEAKER;
+ break;
+ case PLATFORM_SPK_STEREO:
+ break;
+ default:
+ dev_warn(priv->codec_dev, "Unknown speaker setting 0x%02x\n", val1);
+ return -EINVAL;
+ }
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_HPDET_INV_ARG, &val1);
+ if (ret < 0)
+ return ret;
+
+ switch (val1) {
+ case PLATFORM_HPDET_NORMAL:
+ break;
+ case PLATFORM_HPDET_INVERTED:
+ dsm_quirk |= BYT_CHT_ES8316_JD_INVERTED;
+ break;
+ default:
+ dev_warn(priv->codec_dev, "Unknown hpdet-inv setting 0x%02x\n", val1);
+ return -EINVAL;
+ }
+
+ quirk = dsm_quirk;
+ return 0;
+}
+
+static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ static const char * const mic_name[] = { "in1", "in2" };
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
+ struct property_entry props[MAX_NO_PROPS] = {};
+ struct byt_cht_es8316_private *priv;
+ const struct dmi_system_id *dmi_id;
+ struct fwnode_handle *fwnode;
+ bool sof_parent, is_bytcr;
+ const char *platform_name;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+ unsigned int cnt = 0;
+ int dai_index = 0;
+ int i;
+ int ret = 0;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* fix index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
+ if (byt_cht_es8316_dais[i].num_codecs &&
+ !strcmp(byt_cht_es8316_dais[i].codecs->name,
+ "i2c-ESSX8316:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ byt_cht_es8316_dais[dai_index].codecs->name = codec_name;
+ } else {
+ dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+ priv->codec_dev = get_device(codec_dev);
+
+ /* override platform name, if required */
+ byt_cht_es8316_card.dev = dev;
+ platform_name = mach->mach_params.platform;
+
+ ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_es8316_card,
+ platform_name);
+ if (ret) {
+ put_device(codec_dev);
+ return ret;
+ }
+
+ es83xx_dsm_dump(priv->codec_dev);
+
+ /* Check for BYTCR or other platform and setup quirks */
+ is_bytcr = soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0;
+ dmi_id = dmi_first_match(byt_cht_es8316_quirk_table);
+ if (dmi_id) {
+ quirk = (unsigned long)dmi_id->driver_data;
+ } else if (!byt_cht_es8316_get_quirks_from_dsm(priv, is_bytcr)) {
+ dev_info(dev, "Using ACPI DSM info for quirks\n");
+ } else if (is_bytcr) {
+ /* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */
+ quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP |
+ BYT_CHT_ES8316_MONO_SPEAKER;
+ } else {
+ /* Others default to internal-mic-in1-map, mono-speaker */
+ quirk = BYT_CHT_ES8316_INTMIC_IN1_MAP |
+ BYT_CHT_ES8316_MONO_SPEAKER;
+ }
+ if (quirk_override != -1) {
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
+ quirk, quirk_override);
+ quirk = quirk_override;
+ }
+ log_quirks(dev);
+
+ if (quirk & BYT_CHT_ES8316_SSP0)
+ byt_cht_es8316_dais[dai_index].cpus->dai_name = "ssp0-port";
+
+ /* get the clock */
+ priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3");
+ if (IS_ERR(priv->mclk)) {
+ put_device(codec_dev);
+ return dev_err_probe(dev, PTR_ERR(priv->mclk), "clk_get pmc_plt_clk_3 failed\n");
+ }
+
+ if (quirk & BYT_CHT_ES8316_JD_INVERTED)
+ props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted");
+
+ if (cnt) {
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode)) {
+ put_device(codec_dev);
+ return PTR_ERR(fwnode);
+ }
+
+ ret = device_add_software_node(codec_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ if (ret) {
+ put_device(codec_dev);
+ return ret;
+ }
+ }
+
+ /* get speaker enable GPIO */
+ devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios);
+ priv->speaker_en_gpio =
+ gpiod_get_optional(codec_dev, "speaker-enable",
+ /* see comment in byt_cht_es8316_resume() */
+ GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(priv->speaker_en_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(priv->speaker_en_gpio),
+ "get speaker GPIO failed\n");
+ goto err_put_codec;
+ }
+
+ snprintf(components_string, sizeof(components_string),
+ "cfg-spk:%s cfg-mic:%s",
+ (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "1" : "2",
+ mic_name[BYT_CHT_ES8316_MAP(quirk)]);
+ byt_cht_es8316_card.components = components_string;
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
+ snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic",
+ (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo",
+ mic_name[BYT_CHT_ES8316_MAP(quirk)]);
+ byt_cht_es8316_card.long_name = long_name;
+#endif
+
+ sof_parent = snd_soc_acpi_sof_parent(dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ byt_cht_es8316_card.name = SOF_CARD_NAME;
+ byt_cht_es8316_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ byt_cht_es8316_card.name = CARD_NAME;
+ byt_cht_es8316_card.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ dev->driver->pm = &snd_soc_pm_ops;
+
+ /* register the soc card */
+ snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
+
+ ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card);
+ if (ret) {
+ gpiod_put(priv->speaker_en_gpio);
+ dev_err(dev, "snd_soc_register_card failed: %d\n", ret);
+ goto err_put_codec;
+ }
+ platform_set_drvdata(pdev, &byt_cht_es8316_card);
+ return 0;
+
+err_put_codec:
+ device_remove_software_node(priv->codec_dev);
+ put_device(priv->codec_dev);
+ return ret;
+}
+
+static void snd_byt_cht_es8316_mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+
+ gpiod_put(priv->speaker_en_gpio);
+ device_remove_software_node(priv->codec_dev);
+ put_device(priv->codec_dev);
+}
+
+static struct platform_driver snd_byt_cht_es8316_mc_driver = {
+ .driver = {
+ .name = "bytcht_es8316",
+ },
+ .probe = snd_byt_cht_es8316_mc_probe,
+ .remove = snd_byt_cht_es8316_mc_remove,
+};
+
+module_platform_driver(snd_byt_cht_es8316_mc_driver);
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_es8316");
diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c
new file mode 100644
index 000000000000..fec23bda9e64
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_nocodec.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bytcht_nocodec.c - ASoc Machine driver for MinnowBoard Max and Up
+ * to make I2S signals observable on the Low-Speed connector. Audio codec
+ * is not managed by ASoC/DAPM
+ *
+ * Copyright (C) 2015-2017 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Mic"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"ssp2 Rx", NULL, "Mic"},
+ {"Speaker", NULL, "ssp2 Tx"},
+};
+
+static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret;
+
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP);
+
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const unsigned int rates_48000[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static const struct snd_soc_ops aif1_ops = {
+ .startup = aif1_startup,
+};
+
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
+static struct snd_soc_dai_link dais[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .ignore_suspend = 1,
+ .nonatomic = true,
+ .dynamic = 1,
+ .ops = &aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .ignore_suspend = 1,
+ .nonatomic = true,
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &aif1_ops,
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
+ },
+ /* CODEC<->CODEC link */
+ /* back ends */
+ {
+ .name = "SSP2-LowSpeed Connector",
+ .id = 0,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .be_hw_params_fixup = codec_fixup,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ssp2_port, dummy, platform),
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card bytcht_nocodec_card = {
+ .name = "bytcht-nocodec",
+ .owner = THIS_MODULE,
+ .dai_link = dais,
+ .num_links = ARRAY_SIZE(dais),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .fully_routed = true,
+};
+
+static int snd_bytcht_nocodec_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+
+ /* register the soc card */
+ bytcht_nocodec_card.dev = &pdev->dev;
+
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &bytcht_nocodec_card);
+
+ if (ret_val) {
+ dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
+ ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &bytcht_nocodec_card);
+ return ret_val;
+}
+
+static struct platform_driver snd_bytcht_nocodec_mc_driver = {
+ .driver = {
+ .name = "bytcht_nocodec",
+ },
+ .probe = snd_bytcht_nocodec_mc_probe,
+};
+module_platform_driver(snd_bytcht_nocodec_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Nocodec Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_nocodec");
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 9a1752df45a9..103e0b445603 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
*
@@ -5,166 +6,1340 @@
* Author: Subhransu S. Prusty <subhransu.s.prusty@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.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <linux/device.h>
+#include <linux/device/bus.h>
#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/input.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/soc-acpi.h>
+#include <dt-bindings/sound/rt5640.h>
#include "../../codecs/rt5640.h"
#include "../atom/sst-atom-controls.h"
-#include "../common/sst-acpi.h"
+#include "../common/soc-intel-quirks.h"
+
+#define BYT_RT5640_FALLBACK_CODEC_DEV_NAME "i2c-rt5640"
+
+enum {
+ BYT_RT5640_DMIC1_MAP,
+ BYT_RT5640_DMIC2_MAP,
+ BYT_RT5640_IN1_MAP,
+ BYT_RT5640_IN3_MAP,
+ BYT_RT5640_NO_INTERNAL_MIC_MAP,
+};
+
+#define RT5640_JD_SRC_EXT_GPIO 0x0f
+
+enum {
+ BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4),
+ BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4),
+ BYT_RT5640_JD_SRC_JD2_IN4N = (RT5640_JD_SRC_JD2_IN4N << 4),
+ BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4),
+ BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4),
+ BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4),
+ BYT_RT5640_JD_SRC_EXT_GPIO = (RT5640_JD_SRC_EXT_GPIO << 4)
+};
+
+enum {
+ BYT_RT5640_OVCD_TH_600UA = (6 << 8),
+ BYT_RT5640_OVCD_TH_1500UA = (15 << 8),
+ BYT_RT5640_OVCD_TH_2000UA = (20 << 8),
+};
+
+enum {
+ BYT_RT5640_OVCD_SF_0P5 = (RT5640_OVCD_SF_0P5 << 13),
+ BYT_RT5640_OVCD_SF_0P75 = (RT5640_OVCD_SF_0P75 << 13),
+ BYT_RT5640_OVCD_SF_1P0 = (RT5640_OVCD_SF_1P0 << 13),
+ BYT_RT5640_OVCD_SF_1P5 = (RT5640_OVCD_SF_1P5 << 13),
+};
+
+#define BYT_RT5640_MAP_MASK GENMASK(3, 0)
+#define BYT_RT5640_MAP(quirk) ((quirk) & BYT_RT5640_MAP_MASK)
+#define BYT_RT5640_JDSRC(quirk) (((quirk) & GENMASK(7, 4)) >> 4)
+#define BYT_RT5640_OVCD_TH(quirk) (((quirk) & GENMASK(12, 8)) >> 8)
+#define BYT_RT5640_OVCD_SF(quirk) (((quirk) & GENMASK(14, 13)) >> 13)
+#define BYT_RT5640_JD_NOT_INV BIT(16)
+#define BYT_RT5640_MONO_SPEAKER BIT(17)
+#define BYT_RT5640_DIFF_MIC BIT(18) /* default is single-ended */
+#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */
+#define BYT_RT5640_SSP0_AIF1 BIT(20)
+#define BYT_RT5640_SSP0_AIF2 BIT(21)
+#define BYT_RT5640_MCLK_EN BIT(22)
+#define BYT_RT5640_MCLK_25MHZ BIT(23)
+#define BYT_RT5640_NO_SPEAKERS BIT(24)
+#define BYT_RT5640_LINEOUT BIT(25)
+#define BYT_RT5640_LINEOUT_AS_HP2 BIT(26)
+#define BYT_RT5640_HSMIC2_ON_IN1 BIT(27)
+#define BYT_RT5640_JD_HP_ELITEP_1000G2 BIT(28)
+#define BYT_RT5640_USE_AMCR0F28 BIT(29)
+#define BYT_RT5640_SWAPPED_SPEAKERS BIT(30)
+
+#define BYTCR_INPUT_DEFAULTS \
+ (BYT_RT5640_IN3_MAP | \
+ BYT_RT5640_JD_SRC_JD1_IN4P | \
+ BYT_RT5640_OVCD_TH_2000UA | \
+ BYT_RT5640_OVCD_SF_0P75 | \
+ BYT_RT5640_DIFF_MIC)
+
+/* in-diff or dmic-pin + jdsrc + ovcd-th + -sf + jd-inv + terminating entry */
+#define MAX_NO_PROPS 6
+
+struct byt_rt5640_private {
+ struct snd_soc_jack jack;
+ struct snd_soc_jack jack2;
+ struct rt5640_set_jack_data jack_data;
+ struct gpio_desc *hsmic_detect;
+ struct clk *mclk;
+ struct device *codec_dev;
+};
+static bool is_bytcr;
+
+static unsigned long byt_rt5640_quirk = BYT_RT5640_MCLK_EN;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ int map;
+ bool has_mclk = false;
+ bool has_ssp0 = false;
+ bool has_ssp0_aif1 = false;
+ bool has_ssp0_aif2 = false;
+ bool has_ssp2_aif2 = false;
+
+ map = BYT_RT5640_MAP(byt_rt5640_quirk);
+ switch (map) {
+ case BYT_RT5640_DMIC1_MAP:
+ dev_info(dev, "quirk DMIC1_MAP enabled\n");
+ break;
+ case BYT_RT5640_DMIC2_MAP:
+ dev_info(dev, "quirk DMIC2_MAP enabled\n");
+ break;
+ case BYT_RT5640_IN1_MAP:
+ dev_info(dev, "quirk IN1_MAP enabled\n");
+ break;
+ case BYT_RT5640_IN3_MAP:
+ dev_info(dev, "quirk IN3_MAP enabled\n");
+ break;
+ case BYT_RT5640_NO_INTERNAL_MIC_MAP:
+ dev_info(dev, "quirk NO_INTERNAL_MIC_MAP enabled\n");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to DMIC1_MAP\n", map);
+ byt_rt5640_quirk &= ~BYT_RT5640_MAP_MASK;
+ byt_rt5640_quirk |= BYT_RT5640_DMIC1_MAP;
+ break;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_HSMIC2_ON_IN1)
+ dev_info(dev, "quirk HSMIC2_ON_IN1 enabled\n");
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
+ dev_info(dev, "quirk realtek,jack-detect-source %ld\n",
+ BYT_RT5640_JDSRC(byt_rt5640_quirk));
+ dev_info(dev, "quirk realtek,over-current-threshold-microamp %ld\n",
+ BYT_RT5640_OVCD_TH(byt_rt5640_quirk) * 100);
+ dev_info(dev, "quirk realtek,over-current-scale-factor %ld\n",
+ BYT_RT5640_OVCD_SF(byt_rt5640_quirk));
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_JD_NOT_INV)
+ dev_info(dev, "quirk JD_NOT_INV enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2)
+ dev_info(dev, "quirk JD_HP_ELITEPAD_1000G2 enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER)
+ dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_NO_SPEAKERS)
+ dev_info(dev, "quirk NO_SPEAKERS enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_SWAPPED_SPEAKERS)
+ dev_info(dev, "quirk SWAPPED_SPEAKERS enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_LINEOUT)
+ dev_info(dev, "quirk LINEOUT enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_LINEOUT_AS_HP2)
+ dev_info(dev, "quirk LINEOUT_AS_HP2 enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
+ dev_info(dev, "quirk DIFF_MIC enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) {
+ dev_info(dev, "quirk SSP0_AIF1 enabled\n");
+ has_ssp0 = true;
+ has_ssp0_aif1 = true;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) {
+ dev_info(dev, "quirk SSP0_AIF2 enabled\n");
+ has_ssp0 = true;
+ has_ssp0_aif2 = true;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
+ dev_info(dev, "quirk SSP2_AIF2 enabled\n");
+ has_ssp2_aif2 = true;
+ }
+ if (is_bytcr && !has_ssp0)
+ dev_err(dev, "Invalid routing, bytcr detected but no SSP0-based quirk, audio cannot work with SSP2 on bytcr\n");
+ if (has_ssp0_aif1 && has_ssp0_aif2)
+ dev_err(dev, "Invalid routing, SSP0 cannot be connected to both AIF1 and AIF2\n");
+ if (has_ssp0 && has_ssp2_aif2)
+ dev_err(dev, "Invalid routing, cannot have both SSP0 and SSP2 connected to codec\n");
+
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
+ dev_info(dev, "quirk MCLK_EN enabled\n");
+ has_mclk = true;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
+ if (has_mclk)
+ dev_info(dev, "quirk MCLK_25MHZ enabled\n");
+ else
+ dev_err(dev, "quirk MCLK_25MHZ enabled but quirk MCLK not selected, will be ignored\n");
+ }
+}
+
+static int byt_rt5640_prepare_and_enable_pll1(struct snd_soc_dai *codec_dai,
+ int rate)
+{
+ int ret;
+
+ /* Configure the PLL before selecting it */
+ if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) {
+ /* use bitclock as PLL input */
+ if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+ (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+ /* 2x16 bit slots on SSP0 */
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_BCLK1,
+ rate * 32, rate * 512);
+ } else {
+ /* 2x15 bit slots on SSP2 */
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_BCLK1,
+ rate * 50, rate * 512);
+ }
+ } else {
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_MCLK,
+ 25000000, rate * 512);
+ } else {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_MCLK,
+ 19200000, rate * 512);
+ }
+ }
+
+ if (ret < 0) {
+ dev_err(codec_dai->component->dev, "can't set pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->component->dev, "can't set clock %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define BYT_CODEC_DAI1 "rt5640-aif1"
+#define BYT_CODEC_DAI2 "rt5640-aif2"
+
+static struct snd_soc_dai *byt_rt5640_get_codec_dai(struct snd_soc_dapm_context *dapm)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct snd_soc_dai *codec_dai;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1);
+ if (!codec_dai)
+ codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI2);
+ if (!codec_dai)
+ dev_err(card->dev, "Error codec dai not found\n");
+
+ return codec_dai;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct snd_soc_dai *codec_dai;
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ codec_dai = byt_rt5640_get_codec_dai(dapm);
+ if (!codec_dai)
+ return -EIO;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(card->dev, "could not configure MCLK state\n");
+ return ret;
+ }
+ ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000);
+ } else {
+ /*
+ * Set codec clock source to internal clock before
+ * turning off the platform clock. Codec needs clock
+ * for Jack detection and button press
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK,
+ 48000 * 512,
+ SND_SOC_CLOCK_IN);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
+ }
+
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int byt_rt5640_event_lineout(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ unsigned int gpio_ctrl3_val = RT5640_GP1_PF_OUT;
+ struct snd_soc_dai *codec_dai;
+
+ if (!(byt_rt5640_quirk & BYT_RT5640_LINEOUT_AS_HP2))
+ return 0;
+
+ /*
+ * On devices which use line-out as a second headphones output,
+ * the codec's GPIO1 pin is used to enable an external HP-amp.
+ */
+
+ codec_dai = byt_rt5640_get_codec_dai(w->dapm);
+ if (!codec_dai)
+ return -EIO;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpio_ctrl3_val |= RT5640_GP1_OUT_HI;
+
+ snd_soc_component_update_bits(codec_dai->component, RT5640_GPIO_CTRL3,
+ RT5640_GP1_PF_MASK | RT5640_GP1_OUT_MASK, gpio_ctrl3_val);
+
+ return 0;
+}
static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic 2", NULL),
SND_SOC_DAPM_MIC("Internal Mic", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_LINE("Line Out", byt_rt5640_event_lineout),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
- {"AIF1 Playback", NULL, "ssp2 Tx"},
- {"ssp2 Tx", NULL, "codec_out0"},
- {"ssp2 Tx", NULL, "codec_out1"},
- {"codec_in0", NULL, "ssp2 Rx"},
- {"codec_in1", NULL, "ssp2 Rx"},
- {"ssp2 Rx", NULL, "AIF1 Capture"},
-
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
{"Headset Mic", NULL, "MICBIAS1"},
{"IN2P", NULL, "Headset Mic"},
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
- {"Speaker", NULL, "SPOLP"},
- {"Speaker", NULL, "SPOLN"},
- {"Speaker", NULL, "SPORP"},
- {"Speaker", NULL, "SPORN"},
};
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
+ {"Internal Mic", NULL, "Platform Clock"},
{"DMIC1", NULL, "Internal Mic"},
};
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
+ {"Internal Mic", NULL, "Platform Clock"},
{"DMIC2", NULL, "Internal Mic"},
};
static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
+ {"Internal Mic", NULL, "Platform Clock"},
{"Internal Mic", NULL, "MICBIAS1"},
{"IN1P", NULL, "Internal Mic"},
};
-enum {
- BYT_RT5640_DMIC1_MAP,
- BYT_RT5640_DMIC2_MAP,
- BYT_RT5640_IN1_MAP,
+static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = {
+ {"Internal Mic", NULL, "Platform Clock"},
+ {"Internal Mic", NULL, "MICBIAS1"},
+ {"IN3P", NULL, "Internal Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_hsmic2_in1_map[] = {
+ {"Headset Mic 2", NULL, "Platform Clock"},
+ {"Headset Mic 2", NULL, "MICBIAS1"},
+ {"IN1P", NULL, "Headset Mic 2"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif1_map[] = {
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif2_map[] = {
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"AIF2 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Rx", NULL, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif1_map[] = {
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
+
+ {"AIF1 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Rx", NULL, "AIF1 Capture"},
};
-#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
-#define BYT_RT5640_DMIC_EN BIT(16)
+static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif2_map[] = {
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN;
+ {"AIF2 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Rx", NULL, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_stereo_spk_map[] = {
+ {"Speaker", NULL, "Platform Clock"},
+ {"Speaker", NULL, "SPOLP"},
+ {"Speaker", NULL, "SPOLN"},
+ {"Speaker", NULL, "SPORP"},
+ {"Speaker", NULL, "SPORN"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_mono_spk_map[] = {
+ {"Speaker", NULL, "Platform Clock"},
+ {"Speaker", NULL, "SPOLP"},
+ {"Speaker", NULL, "SPOLN"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_lineout_map[] = {
+ {"Line Out", NULL, "Platform Clock"},
+ {"Line Out", NULL, "LOUTR"},
+ {"Line Out", NULL, "LOUTL"},
+};
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic 2"),
SOC_DAPM_PIN_SWITCH("Internal Mic"),
SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
};
-static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static struct snd_soc_jack_pin rt5640_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_pin rt5640_pins2[] = {
+ {
+ /* The 2nd headset jack uses lineout with an external HP-amp */
+ .pin = "Line Out",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic 2",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio rt5640_jack_gpio = {
+ .name = "hp-detect",
+ .report = SND_JACK_HEADSET,
+ .invert = true,
+ .debounce_time = 200,
+};
+
+static struct snd_soc_jack_gpio rt5640_jack2_gpio = {
+ .name = "hp2-detect",
+ .report = SND_JACK_HEADSET,
+ .invert = true,
+ .debounce_time = 200,
+};
+
+static const struct acpi_gpio_params acpi_gpio0 = { 0, 0, false };
+static const struct acpi_gpio_params acpi_gpio1 = { 1, 0, false };
+static const struct acpi_gpio_params acpi_gpio2 = { 2, 0, false };
+
+static const struct acpi_gpio_mapping byt_rt5640_hp_elitepad_1000g2_gpios[] = {
+ { "hp-detect-gpios", &acpi_gpio0, 1, },
+ { "headset-mic-detect-gpios", &acpi_gpio1, 1, },
+ { "hp2-detect-gpios", &acpi_gpio2, 1, },
+ { },
+};
+
+static int byt_rt5640_hp_elitepad_1000g2_jack1_check(void *data)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
+ struct byt_rt5640_private *priv = data;
+ int jack_status, mic_status;
- snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+ jack_status = gpiod_get_value_cansleep(rt5640_jack_gpio.desc);
+ if (jack_status)
+ return 0;
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
- params_rate(params) * 512,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec clock %d\n", ret);
- return ret;
- }
+ mic_status = gpiod_get_value_cansleep(priv->hsmic_detect);
+ if (mic_status)
+ return SND_JACK_HEADPHONE;
+ else
+ return SND_JACK_HEADSET;
+}
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
- params_rate(params) * 50,
- params_rate(params) * 512);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
+static int byt_rt5640_hp_elitepad_1000g2_jack2_check(void *data)
+{
+ struct snd_soc_component *component = data;
+ int jack_status, report;
- return 0;
+ jack_status = gpiod_get_value_cansleep(rt5640_jack2_gpio.desc);
+ if (jack_status)
+ return 0;
+
+ rt5640_enable_micbias1_for_ovcd(component);
+ report = rt5640_detect_headset(component, rt5640_jack2_gpio.desc);
+ rt5640_disable_micbias1_for_ovcd(component);
+
+ return report;
}
-static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
+static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- byt_rt5640_quirk = (unsigned long)id->driver_data;
- return 1;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ return byt_rt5640_prepare_and_enable_pll1(dai, params_rate(params));
}
+/* Please keep this list alphabetically sorted */
static const struct dmi_system_id byt_rt5640_quirk_table[] = {
+ { /* Acer Iconia One 7 B1-750 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "VESPA2"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Acer Iconia Tab 8 W1-810 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Iconia W1-810"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Acer One 10 S1002 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "One S1002"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Acer Aspire SW3-013 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW3-013"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{
- .callback = byt_rt5640_quirk_cb,
+ /* Advantech MICA-071 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"),
+ },
+ /* OVCD Th = 1500uA to reliable detect head-phones vs -set */
+ .driver_data = (void *)(BYT_RT5640_IN3_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 80 Cesium"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 101 CESIUM"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 140 CESIUM"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_USE_AMCR0F28),
+ },
+ {
+ /* Asus T100TAF, unlike other T100TA* models this one has a mono speaker */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ /* Asus T100TA and T100TAM, must come after T100TAF (mono spk) match */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
},
- .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_EXT_GPIO |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_USE_AMCR0F28),
+ },
+ { /* Chuwi Vi8 (CWI506) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "i86"),
+ /* The above are too generic, also match BIOS info */
+ DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Chuwi Vi8 dual-boot (CWI506) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "i86"),
+ /* The above are too generic, also match BIOS info */
+ DMI_MATCH(DMI_BIOS_VERSION, "CHUWI2.D86JHBNR02"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
},
{
- .callback = byt_rt5640_quirk_cb,
+ /* Chuwi Vi10 (CWI505) */
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_BOARD_NAME, "BYT-PF02"),
+ DMI_MATCH(DMI_SYS_VENDOR, "ilife"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "S165"),
},
- .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
- BYT_RT5640_DMIC_EN),
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
},
{
- .callback = byt_rt5640_quirk_cb,
+ /* Chuwi Hi8 (CWI509) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"),
+ DMI_MATCH(DMI_SYS_VENDOR, "ilife"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "S806"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP),
+ },
+ { /* Connect Tablet 9 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Connect"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Tablet 9"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Estar Beauty HD MID 7316R */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Estar"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Glavey TM800A550L */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS version */
+ DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC2_MAP |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_LINEOUT |
+ BYT_RT5640_LINEOUT_AS_HP2 |
+ BYT_RT5640_HSMIC2_ON_IN1 |
+ BYT_RT5640_JD_HP_ELITEP_1000G2),
+ },
+ { /* HP Pavilion x2 10-k0XX, 10-n0XX */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* HP Pavilion x2 10-p0XX */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* HP Pro Tablet 408 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pro Tablet 408"),
},
- .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* HP Stream 7 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 7 Tablet"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* HP Stream 8 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 8 Tablet"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* I.T.Works TW891 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"),
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Lamina I8270 / T701BR.SE */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Lamina"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "T701BR.SE"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Lenovo Miix 2 8 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "20326"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Hiking"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Lenovo Miix 3-830 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 3-830"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Linx Linx7 tablet */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LINX"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LINX7"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ /* Medion Lifetab S10346 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are much too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_SWAPPED_SPEAKERS |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Mele PCG03 Mini PC */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Mini PC"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Mini PC"),
+ },
+ .driver_data = (void *)(BYT_RT5640_NO_INTERNAL_MIC_MAP |
+ BYT_RT5640_NO_SPEAKERS |
+ BYT_RT5640_SSP0_AIF1),
+ },
+ { /* MPMAN Converter 9, similar hw as the I.T.Works TW891 2-in-1 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MPMAN"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Converter9"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ /* MPMAN MPWIN895CL */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MPMAN"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MPWIN8900CL"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* MSI S100 tablet */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "S100"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Nuvison/TMax TM800W560 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TMAX"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TM800W560L"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Onda v975w */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* The above are too generic, also match BIOS info */
+ DMI_EXACT_MATCH(DMI_BIOS_VERSION, "5.6.5"),
+ DMI_EXACT_MATCH(DMI_BIOS_DATE, "07/25/2014"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Pipo W4 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* The above are too generic, also match BIOS info */
+ DMI_MATCH(DMI_BIOS_VERSION, "V8L_WIN32_CHIPHD"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Point of View Mobii TAB-P800W (V2.0) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* The above are too generic, also match BIOS info */
+ DMI_EXACT_MATCH(DMI_BIOS_VERSION, "3BAIR1014"),
+ DMI_EXACT_MATCH(DMI_BIOS_DATE, "10/24/2014"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Point of View Mobii TAB-P800W (V2.1) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* The above are too generic, also match BIOS info */
+ DMI_EXACT_MATCH(DMI_BIOS_VERSION, "3BAIR1013"),
+ DMI_EXACT_MATCH(DMI_BIOS_DATE, "08/22/2014"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Point of View Mobii TAB-P1005W-232 (V2.0) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "POV"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "I102A"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ /* Prowise PT301 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Prowise"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PT301"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ /* Teclast X89 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
+ DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN3_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_1P0 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Toshiba Satellite Click Mini L9W-B */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SATELLITE Click Mini L9W-B"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Toshiba Encore WT8-A */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TOSHIBA WT8-A"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Toshiba Encore WT10-A */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TOSHIBA WT10-A-103"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ /* Vexia Edu Atla 10 tablet 5V version */
+ .matches = {
+ /* Having all 3 of these not set is somewhat unique */
+ DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_BOARD_NAME, "To be filled by O.E.M."),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "05/14/2015"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Vexia Edu Atla 10 tablet 9V version */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Voyo Winpad A15 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "11/20/2014"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Catch-all for generic Insyde tablets, must be last */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_SSP0_AIF1),
+
},
{}
};
-static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
+/*
+ * Note this MUST be called before snd_soc_register_card(), so that the props
+ * are in place before the codec component driver's probe function parses them.
+ */
+static int byt_rt5640_add_codec_device_props(struct device *i2c_dev,
+ struct byt_rt5640_private *priv)
{
+ struct property_entry props[MAX_NO_PROPS] = {};
+ struct fwnode_handle *fwnode;
+ int cnt = 0;
int ret;
- struct snd_soc_codec *codec = runtime->codec;
+
+ switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
+ case BYT_RT5640_DMIC1_MAP:
+ props[cnt++] = PROPERTY_ENTRY_U32("realtek,dmic1-data-pin",
+ RT5640_DMIC1_DATA_PIN_IN1P);
+ break;
+ case BYT_RT5640_DMIC2_MAP:
+ props[cnt++] = PROPERTY_ENTRY_U32("realtek,dmic2-data-pin",
+ RT5640_DMIC2_DATA_PIN_IN1N);
+ break;
+ case BYT_RT5640_IN1_MAP:
+ if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
+ props[cnt++] =
+ PROPERTY_ENTRY_BOOL("realtek,in1-differential");
+ break;
+ case BYT_RT5640_IN3_MAP:
+ if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
+ props[cnt++] =
+ PROPERTY_ENTRY_BOOL("realtek,in3-differential");
+ break;
+ }
+
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk) != RT5640_JD_SRC_EXT_GPIO) {
+ props[cnt++] = PROPERTY_ENTRY_U32(
+ "realtek,jack-detect-source",
+ BYT_RT5640_JDSRC(byt_rt5640_quirk));
+ }
+
+ props[cnt++] = PROPERTY_ENTRY_U32(
+ "realtek,over-current-threshold-microamp",
+ BYT_RT5640_OVCD_TH(byt_rt5640_quirk) * 100);
+
+ props[cnt++] = PROPERTY_ENTRY_U32(
+ "realtek,over-current-scale-factor",
+ BYT_RT5640_OVCD_SF(byt_rt5640_quirk));
+ }
+
+ if (byt_rt5640_quirk & BYT_RT5640_JD_NOT_INV)
+ props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,jack-detect-not-inverted");
+
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode)) {
+ /* put_device() is handled in caller */
+ return PTR_ERR(fwnode);
+ }
+
+ ret = device_add_software_node(i2c_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+/* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */
+static const struct acpi_gpio_params amcr0f28_jd_gpio = { 1, 0, false };
+
+static const struct acpi_gpio_mapping amcr0f28_gpios[] = {
+ { "rt5640-jd-gpios", &amcr0f28_jd_gpio, 1 },
+ { }
+};
+
+static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card)
+{
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+ struct rt5640_set_jack_data *data = &priv->jack_data;
+ struct acpi_device *adev;
+ int ret = 0;
+
+ adev = acpi_dev_get_first_match_dev("AMCR0F28", "1", -1);
+ if (!adev) {
+ dev_err(card->dev, "error cannot find AMCR0F28 adev\n");
+ return -ENOENT;
+ }
+
+ data->codec_irq_override = acpi_dev_gpio_irq_get(adev, 0);
+ if (data->codec_irq_override < 0) {
+ ret = data->codec_irq_override;
+ dev_err(card->dev, "error %d getting codec IRQ\n", ret);
+ goto put_adev;
+ }
+
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk) == RT5640_JD_SRC_EXT_GPIO) {
+ acpi_dev_add_driver_gpios(adev, amcr0f28_gpios);
+ data->jd_gpio = devm_fwnode_gpiod_get(card->dev, acpi_fwnode_handle(adev),
+ "rt5640-jd", GPIOD_IN, "rt5640-jd");
+ acpi_dev_remove_driver_gpios(adev);
+
+ if (IS_ERR(data->jd_gpio)) {
+ ret = PTR_ERR(data->jd_gpio);
+ dev_err(card->dev, "error %d getting jd GPIO\n", ret);
+ }
+ }
+
+put_adev:
+ acpi_dev_put(adev);
+ return ret;
+}
+
+static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
+{
struct snd_soc_card *card = runtime->card;
- const struct snd_soc_dapm_route *custom_map;
- int num_routes;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+ struct rt5640_set_jack_data *jack_data = &priv->jack_data;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ const struct snd_soc_dapm_route *custom_map = NULL;
+ int num_routes = 0;
+ int ret;
- card->dapm.idle_bias_off = true;
+ snd_soc_dapm_set_idle_bias(dapm, false);
+ jack_data->use_platform_clock = true;
- rt5640_sel_asrc_clk_src(codec,
+ /* Start with RC clk for jack-detect (we disable MCLK below) */
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN)
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_RCCLK);
+
+ rt5640_sel_asrc_clk_src(component,
RT5640_DA_STEREO_FILTER |
- RT5640_AD_STEREO_FILTER,
+ RT5640_DA_MONO_L_FILTER |
+ RT5640_DA_MONO_R_FILTER |
+ RT5640_AD_STEREO_FILTER |
+ RT5640_AD_MONO_L_FILTER |
+ RT5640_AD_MONO_R_FILTER,
RT5640_CLK_SEL_ASRC);
ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
@@ -174,44 +1349,165 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- dmi_check_system(byt_rt5640_quirk_table);
switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
case BYT_RT5640_IN1_MAP:
custom_map = byt_rt5640_intmic_in1_map;
num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
break;
+ case BYT_RT5640_IN3_MAP:
+ custom_map = byt_rt5640_intmic_in3_map;
+ num_routes = ARRAY_SIZE(byt_rt5640_intmic_in3_map);
+ break;
+ case BYT_RT5640_DMIC1_MAP:
+ custom_map = byt_rt5640_intmic_dmic1_map;
+ num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
+ break;
case BYT_RT5640_DMIC2_MAP:
custom_map = byt_rt5640_intmic_dmic2_map;
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
break;
- default:
- custom_map = byt_rt5640_intmic_dmic1_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
}
- ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
if (ret)
return ret;
- if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
- ret = rt5640_dmic_enable(codec, 0, 0);
+ if (byt_rt5640_quirk & BYT_RT5640_HSMIC2_ON_IN1) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_hsmic2_in1_map,
+ ARRAY_SIZE(byt_rt5640_hsmic2_in1_map));
if (ret)
return ret;
}
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_ssp2_aif2_map,
+ ARRAY_SIZE(byt_rt5640_ssp2_aif2_map));
+ } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_ssp0_aif1_map,
+ ARRAY_SIZE(byt_rt5640_ssp0_aif1_map));
+ } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_ssp0_aif2_map,
+ ARRAY_SIZE(byt_rt5640_ssp0_aif2_map));
+ } else {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_ssp2_aif1_map,
+ ARRAY_SIZE(byt_rt5640_ssp2_aif1_map));
+ }
+ if (ret)
+ return ret;
- return ret;
+ if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_mono_spk_map,
+ ARRAY_SIZE(byt_rt5640_mono_spk_map));
+ } else if (!(byt_rt5640_quirk & BYT_RT5640_NO_SPEAKERS)) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_stereo_spk_map,
+ ARRAY_SIZE(byt_rt5640_stereo_spk_map));
+ }
+ if (ret)
+ return ret;
+
+ if (byt_rt5640_quirk & BYT_RT5640_LINEOUT) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5640_lineout_map,
+ ARRAY_SIZE(byt_rt5640_lineout_map));
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * The firmware might enable the clock at boot (this information
+ * may or may not be reflected in the enable clock register).
+ * To change the rate we must disable the clock first to cover
+ * these cases. Due to common clock framework restrictions that
+ * do not allow to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(priv->mclk);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
+
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
+ ret = clk_set_rate(priv->mclk, 25000000);
+ else
+ ret = clk_set_rate(priv->mclk, 19200000);
+ if (ret) {
+ dev_err(card->dev, "unable to set MCLK rate\n");
+ return ret;
+ }
+
+ if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, rt5640_pins,
+ ARRAY_SIZE(rt5640_pins));
+ if (ret) {
+ dev_err(card->dev, "Jack creation failed %d\n", ret);
+ return ret;
+ }
+ snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0,
+ KEY_PLAYPAUSE);
+
+ if (byt_rt5640_quirk & BYT_RT5640_USE_AMCR0F28) {
+ ret = byt_rt5640_get_amcr0f28_settings(card);
+ if (ret)
+ return ret;
+ }
+
+ snd_soc_component_set_jack(component, &priv->jack, &priv->jack_data);
+ }
+
+ if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET,
+ &priv->jack, rt5640_pins,
+ ARRAY_SIZE(rt5640_pins));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset 2",
+ SND_JACK_HEADSET,
+ &priv->jack2, rt5640_pins2,
+ ARRAY_SIZE(rt5640_pins2));
+ if (ret)
+ return ret;
+
+ rt5640_jack_gpio.data = priv;
+ rt5640_jack_gpio.gpiod_dev = priv->codec_dev;
+ rt5640_jack_gpio.jack_status_check = byt_rt5640_hp_elitepad_1000g2_jack1_check;
+ ret = snd_soc_jack_add_gpios(&priv->jack, 1, &rt5640_jack_gpio);
+ if (ret)
+ return ret;
+
+ rt5640_set_ovcd_params(component);
+ rt5640_jack2_gpio.data = component;
+ rt5640_jack2_gpio.gpiod_dev = priv->codec_dev;
+ rt5640_jack2_gpio.jack_status_check = byt_rt5640_hp_elitepad_1000g2_jack2_check;
+ ret = snd_soc_jack_add_gpios(&priv->jack2, 1, &rt5640_jack2_gpio);
+ if (ret) {
+ snd_soc_jack_free_gpios(&priv->jack, 1, &rt5640_jack_gpio);
+ return ret;
+ }
+ }
+
+ return 0;
}
-static const struct snd_soc_pcm_stream byt_rt5640_dai_params = {
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
-};
+static void byt_rt5640_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
+ snd_soc_jack_free_gpios(&priv->jack2, 1, &rt5640_jack2_gpio);
+ snd_soc_jack_free_gpios(&priv->jack, 1, &rt5640_jack_gpio);
+ }
+}
static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
@@ -220,31 +1516,38 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- int ret;
+ int ret, bits;
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ /* The DSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
- /* set SSP2 to 24-bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+ (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+ /* set SSP0 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ bits = 16;
+ } else {
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ bits = 24;
+ }
/*
* Default mode for SSP configuration is TDM 4 slot, override config
- * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * with explicit setting to I2S 2ch. The word length is set with
* dai_set_tdm_slot() since there is no other API exposed
*/
- ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS
- );
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP);
if (ret < 0) {
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
return ret;
}
- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits);
if (ret < 0) {
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
return ret;
@@ -259,72 +1562,120 @@ static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_RATE, 48000);
}
-static struct snd_soc_ops byt_rt5640_aif1_ops = {
+static const struct snd_soc_ops byt_rt5640_aif1_ops = {
.startup = byt_rt5640_aif1_startup,
};
-static struct snd_soc_ops byt_rt5640_be_ssp2_ops = {
+static const struct snd_soc_ops byt_rt5640_be_ssp2_ops = {
.hw_params = byt_rt5640_aif1_hw_params,
};
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ /* overwritten for ssp0 routing */
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC(
+ /* overwritten with HID */ "i2c-10EC5640:00",
+ /* changed w/ quirk */ "rt5640-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
static struct snd_soc_dai_link byt_rt5640_dais[] = {
[MERR_DPCM_AUDIO] = {
.name = "Baytrail Audio Port",
.stream_name = "Baytrail Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
+ .nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &byt_rt5640_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
},
[MERR_DPCM_DEEP_BUFFER] = {
.name = "Deep-Buffer Audio Port",
.stream_name = "Deep-Buffer Audio",
- .cpu_dai_name = "deepbuffer-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
.ops = &byt_rt5640_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Baytrail Compressed Port",
- .stream_name = "Baytrail Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
},
/* back ends */
{
.name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
+ .id = 0,
.no_pcm = 1,
- .codec_dai_name = "rt5640-aif1",
- .codec_name = "i2c-10EC5640:00", /* overwritten with HID */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_rt5640_codec_fixup,
- .ignore_suspend = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.init = byt_rt5640_init,
+ .exit = byt_rt5640_exit,
.ops = &byt_rt5640_be_ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
},
};
/* SoC card */
+static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN];
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
+static char byt_rt5640_long_name[40]; /* = "bytcr-rt5640-*-spk-*-mic" */
+#endif
+static char byt_rt5640_components[64]; /* = "cfg-spk:* cfg-mic:* ..." */
+
+static int byt_rt5640_suspend(struct snd_soc_card *card)
+{
+ struct snd_soc_component *component;
+
+ if (!BYT_RT5640_JDSRC(byt_rt5640_quirk))
+ return 0;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, byt_rt5640_codec_name)) {
+ dev_dbg(component->dev, "disabling jack detect before suspend\n");
+ snd_soc_component_set_jack(component, NULL, NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int byt_rt5640_resume(struct snd_soc_card *card)
+{
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+
+ if (!BYT_RT5640_JDSRC(byt_rt5640_quirk))
+ return 0;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, byt_rt5640_codec_name)) {
+ dev_dbg(component->dev, "re-enabling jack detect after resume\n");
+ snd_soc_component_set_jack(component, &priv->jack,
+ &priv->jack_data);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht rt5640" /* card name will be 'sof-bytcht rt5640' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bytcr-rt5640"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
static struct snd_soc_card byt_rt5640_card = {
- .name = "bytcr-rt5640",
.owner = THIS_MODULE,
.dai_link = byt_rt5640_dais,
.num_links = ARRAY_SIZE(byt_rt5640_dais),
@@ -333,41 +1684,311 @@ static struct snd_soc_card byt_rt5640_card = {
.dapm_routes = byt_rt5640_audio_map,
.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
.fully_routed = true,
+ .suspend_pre = byt_rt5640_suspend,
+ .resume_post = byt_rt5640_resume,
};
-static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */
+ u64 aif_value; /* 1: AIF1, 2: AIF2 */
+ u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */
+};
static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ static const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3", "none" };
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
+ __maybe_unused const char *spk_type;
+ const struct dmi_system_id *dmi_id;
+ const char *headset2_string = "";
+ const char *lineout_string = "";
+ struct byt_rt5640_private *priv;
+ const char *platform_name;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+ const char *cfg_spk;
+ bool sof_parent;
int ret_val = 0;
- struct sst_acpi_mach *mach;
+ int dai_index = 0;
+ int i, aif;
+
+ is_bytcr = false;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
/* register the soc card */
- byt_rt5640_card.dev = &pdev->dev;
- mach = byt_rt5640_card.dev->platform_data;
+ byt_rt5640_card.dev = dev;
+ snd_soc_card_set_drvdata(&byt_rt5640_card, priv);
+
+ /* fix index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) {
+ if (byt_rt5640_dais[i].num_codecs &&
+ !strcmp(byt_rt5640_dais[i].codecs->name,
+ "i2c-10EC5640:00")) {
+ dai_index = i;
+ break;
+ }
+ }
/* fixup codec name based on HID */
- snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
- "%s%s%s", "i2c-", mach->id, ":00");
- byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name;
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ byt_rt5640_dais[dai_index].codecs->name = byt_rt5640_codec_name;
+ } else {
+ dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
- ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
+ if (codec_dev) {
+ priv->codec_dev = get_device(codec_dev);
+ } else {
+ /*
+ * Special case for Android tablets where the codec i2c_client
+ * has been manually instantiated by x86_android_tablets.ko due
+ * to a broken DSDT.
+ */
+ codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL,
+ BYT_RT5640_FALLBACK_CODEC_DEV_NAME);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+ if (!i2c_verify_client(codec_dev)) {
+ dev_err(dev, "Error '%s' is not an i2c_client\n",
+ BYT_RT5640_FALLBACK_CODEC_DEV_NAME);
+ put_device(codec_dev);
+ }
+
+ /* fixup codec name */
+ strscpy(byt_rt5640_codec_name, BYT_RT5640_FALLBACK_CODEC_DEV_NAME,
+ sizeof(byt_rt5640_codec_name));
+
+ /* bus_find_device() returns a reference no need to get() */
+ priv->codec_dev = codec_dev;
+ }
+
+ /*
+ * swap SSP0 if bytcr is detected
+ * (will be overridden if DMI quirk is detected)
+ */
+ if (soc_intel_is_byt()) {
+ if (mach->mach_params.acpi_ipc_irq_index == 0)
+ is_bytcr = true;
+ }
+
+ if (is_bytcr) {
+ /*
+ * Baytrail CR platforms may have CHAN package in BIOS, try
+ * to find relevant routing quirk based as done on Windows
+ * platforms. We have to read the information directly from the
+ * BIOS, at this stage the card is not created and the links
+ * with the codec driver/pdata are non-existent
+ */
+
+ struct acpi_chan_package chan_package = { 0 };
+
+ /* format specified: 2 64-bit integers */
+ struct acpi_buffer format = {sizeof("NN"), "NN"};
+ struct acpi_buffer state = {0, NULL};
+ struct snd_soc_acpi_package_context pkg_ctx;
+ bool pkg_found = false;
+
+ state.length = sizeof(chan_package);
+ state.pointer = &chan_package;
+
+ pkg_ctx.name = "CHAN";
+ pkg_ctx.length = 2;
+ pkg_ctx.format = &format;
+ pkg_ctx.state = &state;
+ pkg_ctx.data_valid = false;
+
+ pkg_found = snd_soc_acpi_find_package_from_hid(mach->id,
+ &pkg_ctx);
+ if (pkg_found) {
+ if (chan_package.aif_value == 1) {
+ dev_info(dev, "BIOS Routing: AIF1 connected\n");
+ byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1;
+ } else if (chan_package.aif_value == 2) {
+ dev_info(dev, "BIOS Routing: AIF2 connected\n");
+ byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+ } else {
+ dev_info(dev, "BIOS Routing isn't valid, ignored\n");
+ pkg_found = false;
+ }
+ }
+
+ if (!pkg_found) {
+ /* no BIOS indications, assume SSP0-AIF2 connection */
+ byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+ }
+
+ /* change defaults for Baytrail-CR capture */
+ byt_rt5640_quirk |= BYTCR_INPUT_DEFAULTS;
+ } else {
+ byt_rt5640_quirk |= BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75;
+ }
+
+ /* check quirks before creating card */
+ dmi_id = dmi_first_match(byt_rt5640_quirk_table);
+ if (dmi_id)
+ byt_rt5640_quirk = (unsigned long)dmi_id->driver_data;
+ if (quirk_override != -1) {
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
+ byt_rt5640_quirk, quirk_override);
+ byt_rt5640_quirk = quirk_override;
+ }
+
+ if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
+ acpi_dev_add_driver_gpios(ACPI_COMPANION(priv->codec_dev),
+ byt_rt5640_hp_elitepad_1000g2_gpios);
+
+ priv->hsmic_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode,
+ "headset-mic-detect", GPIOD_IN,
+ "headset-mic-detect");
+ if (IS_ERR(priv->hsmic_detect)) {
+ ret_val = dev_err_probe(dev, PTR_ERR(priv->hsmic_detect),
+ "getting hsmic-detect GPIO\n");
+ goto err_device;
+ }
+ }
+
+ /* Must be called before register_card, also see declaration comment. */
+ ret_val = byt_rt5640_add_codec_device_props(codec_dev, priv);
+ if (ret_val)
+ goto err_remove_gpios;
+
+ log_quirks(dev);
+
+ if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
+ (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+ byt_rt5640_dais[dai_index].codecs->dai_name = "rt5640-aif2";
+ aif = 2;
+ } else {
+ aif = 1;
+ }
+
+ if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+ (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2))
+ byt_rt5640_dais[dai_index].cpus->dai_name = "ssp0-port";
+
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
+ priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3");
+ if (IS_ERR(priv->mclk)) {
+ ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk),
+ "Failed to get MCLK from pmc_plt_clk_3\n");
+ goto err;
+ }
+ /*
+ * Fall back to bit clock usage when clock is not
+ * available likely due to missing dependencies.
+ */
+ if (!priv->mclk)
+ byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN;
+ }
+
+ if (byt_rt5640_quirk & BYT_RT5640_NO_SPEAKERS) {
+ cfg_spk = "0";
+ spk_type = "none";
+ } else if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) {
+ cfg_spk = "1";
+ spk_type = "mono";
+ } else if (byt_rt5640_quirk & BYT_RT5640_SWAPPED_SPEAKERS) {
+ cfg_spk = "swapped";
+ spk_type = "swapped";
+ } else {
+ cfg_spk = "2";
+ spk_type = "stereo";
+ }
+
+ if (byt_rt5640_quirk & BYT_RT5640_LINEOUT) {
+ if (byt_rt5640_quirk & BYT_RT5640_LINEOUT_AS_HP2)
+ lineout_string = " cfg-hp2:lineout";
+ else
+ lineout_string = " cfg-lineout:2";
+ }
+
+ if (byt_rt5640_quirk & BYT_RT5640_HSMIC2_ON_IN1)
+ headset2_string = " cfg-hs2:in1";
+
+ snprintf(byt_rt5640_components, sizeof(byt_rt5640_components),
+ "cfg-spk:%s cfg-mic:%s aif:%d%s%s", cfg_spk,
+ map_name[BYT_RT5640_MAP(byt_rt5640_quirk)], aif,
+ lineout_string, headset2_string);
+ byt_rt5640_card.components = byt_rt5640_components;
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
+ snprintf(byt_rt5640_long_name, sizeof(byt_rt5640_long_name),
+ "bytcr-rt5640-%s-spk-%s-mic", spk_type,
+ map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]);
+ byt_rt5640_card.long_name = byt_rt5640_long_name;
+#endif
+
+ /* override platform name, if required */
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5640_card,
+ platform_name);
+ if (ret_val)
+ goto err;
+
+ sof_parent = snd_soc_acpi_sof_parent(dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ byt_rt5640_card.name = SOF_CARD_NAME;
+ byt_rt5640_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ byt_rt5640_card.name = CARD_NAME;
+ byt_rt5640_card.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ dev->driver->pm = &snd_soc_pm_ops;
+
+ ret_val = devm_snd_soc_register_card(dev, &byt_rt5640_card);
if (ret_val) {
- dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
- ret_val);
- return ret_val;
+ dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+ goto err;
}
platform_set_drvdata(pdev, &byt_rt5640_card);
return ret_val;
+
+err:
+ device_remove_software_node(priv->codec_dev);
+err_remove_gpios:
+ if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2)
+ acpi_dev_remove_driver_gpios(ACPI_COMPANION(priv->codec_dev));
+err_device:
+ put_device(priv->codec_dev);
+ return ret_val;
+}
+
+static void snd_byt_rt5640_mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2)
+ acpi_dev_remove_driver_gpios(ACPI_COMPANION(priv->codec_dev));
+
+ device_remove_software_node(priv->codec_dev);
+ put_device(priv->codec_dev);
}
static struct platform_driver snd_byt_rt5640_mc_driver = {
.driver = {
.name = "bytcr_rt5640",
- .pm = &snd_soc_pm_ops,
},
.probe = snd_byt_rt5640_mc_probe,
+ .remove = snd_byt_rt5640_mc_remove,
};
module_platform_driver(snd_byt_rt5640_mc_driver);
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 1c95ccc886c4..68cf463f1d50 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bytcr_rt5651.c - ASoc Machine driver for Intel Byt CR platform
* (derived from bytcr_rt5640.c)
@@ -5,142 +6,633 @@
* Copyright (C) 2015 Intel Corp
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
- * 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/init.h>
+#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/soc-acpi.h>
#include "../../codecs/rt5651.h"
#include "../atom/sst-atom-controls.h"
+#include "../common/soc-intel-quirks.h"
+
+enum {
+ BYT_RT5651_DMIC_MAP,
+ BYT_RT5651_IN1_MAP,
+ BYT_RT5651_IN2_MAP,
+ BYT_RT5651_IN1_IN2_MAP,
+};
+
+enum {
+ BYT_RT5651_JD_NULL = (RT5651_JD_NULL << 4),
+ BYT_RT5651_JD1_1 = (RT5651_JD1_1 << 4),
+ BYT_RT5651_JD1_2 = (RT5651_JD1_2 << 4),
+ BYT_RT5651_JD2 = (RT5651_JD2 << 4),
+};
+
+enum {
+ BYT_RT5651_OVCD_TH_600UA = (6 << 8),
+ BYT_RT5651_OVCD_TH_1500UA = (15 << 8),
+ BYT_RT5651_OVCD_TH_2000UA = (20 << 8),
+};
+
+enum {
+ BYT_RT5651_OVCD_SF_0P5 = (RT5651_OVCD_SF_0P5 << 13),
+ BYT_RT5651_OVCD_SF_0P75 = (RT5651_OVCD_SF_0P75 << 13),
+ BYT_RT5651_OVCD_SF_1P0 = (RT5651_OVCD_SF_1P0 << 13),
+ BYT_RT5651_OVCD_SF_1P5 = (RT5651_OVCD_SF_1P5 << 13),
+};
+
+#define BYT_RT5651_MAP_MASK GENMASK(3, 0)
+#define BYT_RT5651_MAP(quirk) ((quirk) & BYT_RT5651_MAP_MASK)
+#define BYT_RT5651_JDSRC(quirk) (((quirk) & GENMASK(7, 4)) >> 4)
+#define BYT_RT5651_OVCD_TH(quirk) (((quirk) & GENMASK(12, 8)) >> 8)
+#define BYT_RT5651_OVCD_SF(quirk) (((quirk) & GENMASK(14, 13)) >> 13)
+#define BYT_RT5651_DMIC_EN BIT(16)
+#define BYT_RT5651_MCLK_EN BIT(17)
+#define BYT_RT5651_MCLK_25MHZ BIT(18)
+#define BYT_RT5651_SSP2_AIF2 BIT(19) /* default is using AIF1 */
+#define BYT_RT5651_SSP0_AIF1 BIT(20)
+#define BYT_RT5651_SSP0_AIF2 BIT(21)
+#define BYT_RT5651_HP_LR_SWAPPED BIT(22)
+#define BYT_RT5651_MONO_SPEAKER BIT(23)
+#define BYT_RT5651_JD_NOT_INV BIT(24)
+
+#define BYT_RT5651_DEFAULT_QUIRKS (BYT_RT5651_MCLK_EN | \
+ BYT_RT5651_JD1_1 | \
+ BYT_RT5651_OVCD_TH_2000UA | \
+ BYT_RT5651_OVCD_SF_0P75)
+
+/* jack-detect-source + inv + dmic-en + ovcd-th + -sf + terminating entry */
+#define MAX_NO_PROPS 6
+
+struct byt_rt5651_private {
+ struct clk *mclk;
+ struct gpio_desc *ext_amp_gpio;
+ struct gpio_desc *hp_detect;
+ struct snd_soc_jack jack;
+ struct device *codec_dev;
+};
+
+static const struct acpi_gpio_mapping *byt_rt5651_gpios;
+
+/* Default: jack-detect on JD1_1, internal mic on in2, headsetmic on in3 */
+static unsigned long byt_rt5651_quirk = BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP;
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ int map;
+
+ map = BYT_RT5651_MAP(byt_rt5651_quirk);
+ switch (map) {
+ case BYT_RT5651_DMIC_MAP:
+ dev_info(dev, "quirk DMIC_MAP enabled");
+ break;
+ case BYT_RT5651_IN1_MAP:
+ dev_info(dev, "quirk IN1_MAP enabled");
+ break;
+ case BYT_RT5651_IN2_MAP:
+ dev_info(dev, "quirk IN2_MAP enabled");
+ break;
+ case BYT_RT5651_IN1_IN2_MAP:
+ dev_info(dev, "quirk IN1_IN2_MAP enabled");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to DMIC_MAP\n", map);
+ byt_rt5651_quirk &= ~BYT_RT5651_MAP_MASK;
+ byt_rt5651_quirk |= BYT_RT5651_DMIC_MAP;
+ break;
+ }
+
+ if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) {
+ dev_info(dev, "quirk realtek,jack-detect-source %ld\n",
+ BYT_RT5651_JDSRC(byt_rt5651_quirk));
+ dev_info(dev, "quirk realtek,over-current-threshold-microamp %ld\n",
+ BYT_RT5651_OVCD_TH(byt_rt5651_quirk) * 100);
+ dev_info(dev, "quirk realtek,over-current-scale-factor %ld\n",
+ BYT_RT5651_OVCD_SF(byt_rt5651_quirk));
+ }
+ if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN)
+ dev_info(dev, "quirk DMIC enabled");
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
+ dev_info(dev, "quirk MCLK_EN enabled");
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
+ dev_info(dev, "quirk MCLK_25MHZ enabled");
+ if (byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2)
+ dev_info(dev, "quirk SSP2_AIF2 enabled\n");
+ if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1)
+ dev_info(dev, "quirk SSP0_AIF1 enabled\n");
+ if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)
+ dev_info(dev, "quirk SSP0_AIF2 enabled\n");
+ if (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER)
+ dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+ if (byt_rt5651_quirk & BYT_RT5651_JD_NOT_INV)
+ dev_info(dev, "quirk JD_NOT_INV enabled\n");
+}
+
+#define BYT_CODEC_DAI1 "rt5651-aif1"
+#define BYT_CODEC_DAI2 "rt5651-aif2"
+
+static int byt_rt5651_prepare_and_enable_pll1(struct snd_soc_dai *codec_dai,
+ int rate, int bclk_ratio)
+{
+ int clk_id, clk_freq, ret;
+
+ /* Configure the PLL before selecting it */
+ if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) {
+ clk_id = RT5651_PLL1_S_BCLK1;
+ clk_freq = rate * bclk_ratio;
+ } else {
+ clk_id = RT5651_PLL1_S_MCLK;
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
+ clk_freq = 25000000;
+ else
+ clk_freq = 19200000;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, rate * 512);
+ if (ret < 0) {
+ dev_err(codec_dai->component->dev, "can't set pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->component->dev, "can't set clock %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1);
+ if (!codec_dai)
+ codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI2);
+ if (!codec_dai) {
+ dev_err(card->dev,
+ "Codec dai not found; Unable to set platform clock\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(card->dev, "could not configure MCLK state");
+ return ret;
+ }
+ ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50);
+ } else {
+ /*
+ * Set codec clock source to internal clock before
+ * turning off the platform clock. Codec needs clock
+ * for Jack detection and button press
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK,
+ 48000 * 512,
+ SND_SOC_CLOCK_IN);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
+ }
+
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rt5651_ext_amp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpiod_set_value_cansleep(priv->ext_amp_gpio, 1);
+ else
+ gpiod_set_value_cansleep(priv->ext_amp_gpio, 0);
+
+ return 0;
+}
static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Internal Mic", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Ext Amp Power", SND_SOC_NOPM, 0, 0,
+ rt5651_ext_amp_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
};
static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = {
- {"AIF1 Playback", NULL, "ssp2 Tx"},
- {"ssp2 Tx", NULL, "codec_out0"},
- {"ssp2 Tx", NULL, "codec_out1"},
- {"codec_in0", NULL, "ssp2 Rx"},
- {"codec_in1", NULL, "ssp2 Rx"},
- {"ssp2 Rx", NULL, "AIF1 Capture"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Internal Mic", NULL, "Platform Clock"},
+ {"Speaker", NULL, "Platform Clock"},
+ {"Speaker", NULL, "Ext Amp Power"},
+ {"Line In", NULL, "Platform Clock"},
{"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */
- {"IN2P", NULL, "Headset Mic"},
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
{"Speaker", NULL, "LOUTL"},
{"Speaker", NULL, "LOUTR"},
-};
+ {"IN2P", NULL, "Line In"},
+ {"IN2N", NULL, "Line In"},
-static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic1_map[] = {
- {"DMIC1", NULL, "Internal Mic"},
};
-static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic2_map[] = {
- {"DMIC2", NULL, "Internal Mic"},
+static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = {
+ {"DMIC L1", NULL, "Internal Mic"},
+ {"DMIC R1", NULL, "Internal Mic"},
+ {"IN2P", NULL, "Headset Mic"},
};
static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = {
{"Internal Mic", NULL, "micbias1"},
{"IN1P", NULL, "Internal Mic"},
+ {"IN3P", NULL, "Headset Mic"},
};
-enum {
- BYT_RT5651_DMIC1_MAP,
- BYT_RT5651_DMIC2_MAP,
- BYT_RT5651_IN1_MAP,
+static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = {
+ {"Internal Mic", NULL, "micbias1"},
+ {"IN2P", NULL, "Internal Mic"},
+ {"IN3P", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = {
+ {"Internal Mic", NULL, "micbias1"},
+ {"IN1P", NULL, "Internal Mic"},
+ {"IN2P", NULL, "Internal Mic"},
+ {"IN3P", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif1_map[] = {
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
+
+ {"AIF1 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif2_map[] = {
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
+
+ {"AIF2 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Rx", NULL, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = {
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
};
-#define BYT_RT5651_MAP(quirk) ((quirk) & 0xff)
-#define BYT_RT5651_DMIC_EN BIT(16)
+static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif2_map[] = {
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
-static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP |
- BYT_RT5651_DMIC_EN;
+ {"AIF2 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Rx", NULL, "AIF2 Capture"},
+};
static const struct snd_kcontrol_new byt_rt5651_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Internal Mic"),
SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Line In"),
+};
+
+static struct snd_soc_jack_pin bytcr_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
};
static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ snd_pcm_format_t format = params_format(params);
+ int rate = params_rate(params);
+ int bclk_ratio;
- snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ bclk_ratio = 32;
+ else
+ bclk_ratio = 50;
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
- params_rate(params) * 512,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec clock %d\n", ret);
- return ret;
- }
+ return byt_rt5651_prepare_and_enable_pll1(codec_dai, rate, bclk_ratio);
+}
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5651_PLL1_S_BCLK1,
- params_rate(params) * 50,
- params_rate(params) * 512);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
+static const struct acpi_gpio_params pov_p1006w_hp_detect = { 1, 0, false };
+static const struct acpi_gpio_params pov_p1006w_ext_amp_en = { 2, 0, true };
- return 0;
+static const struct acpi_gpio_mapping byt_rt5651_pov_p1006w_gpios[] = {
+ { "hp-detect-gpios", &pov_p1006w_hp_detect, 1, },
+ { "ext-amp-enable-gpios", &pov_p1006w_ext_amp_en, 1, },
+ { },
+};
+
+static int byt_rt5651_pov_p1006w_quirk_cb(const struct dmi_system_id *id)
+{
+ byt_rt5651_quirk = (unsigned long)id->driver_data;
+ byt_rt5651_gpios = byt_rt5651_pov_p1006w_gpios;
+ return 1;
+}
+
+static int byt_rt5651_quirk_cb(const struct dmi_system_id *id)
+{
+ byt_rt5651_quirk = (unsigned long)id->driver_data;
+ return 1;
}
static const struct dmi_system_id byt_rt5651_quirk_table[] = {
+ {
+ /* Chuwi Hi8 Pro (CWI513) */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_HP_LR_SWAPPED |
+ BYT_RT5651_MONO_SPEAKER),
+ },
+ {
+ /* Chuwi Vi8 Plus (CWI519) */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_HP_LR_SWAPPED |
+ BYT_RT5651_MONO_SPEAKER),
+ },
+ {
+ /* Complet Electro Serv MY8307 */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Complet Electro Serv"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MY8307"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_MONO_SPEAKER |
+ BYT_RT5651_JD_NOT_INV),
+ },
+ {
+ /* I.T.Works TW701, Ployer Momo7w and Trekstor ST70416-6
+ * (these all use the same mainboard) */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."),
+ /* Partial match for all of itWORKS.G.WI71C.JGBMRBA,
+ * TREK.G.WI71C.JGBMRBA0x and MOMO.G.WI71C.MABMRBA02 */
+ DMI_MATCH(DMI_BIOS_VERSION, ".G.WI71C."),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_SSP0_AIF1 |
+ BYT_RT5651_MONO_SPEAKER),
+ },
+ {
+ /* Jumper EZpad 7 */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Jumper"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "EZpad"),
+ /* Jumper12x.WJ2012.bsBKRCP05 with the version dropped */
+ DMI_MATCH(DMI_BIOS_VERSION, "Jumper12x.WJ2012.bsBKRCP"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_JD_NOT_INV),
+ },
+ {
+ /* KIANO SlimNote 14.2 */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "KIANO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN1_IN2_MAP),
+ },
+ {
+ /* Minnowboard Max B3 */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
+ },
+ .driver_data = (void *)(BYT_RT5651_IN1_MAP),
+ },
+ {
+ /* Minnowboard Turbot */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ADI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"),
+ },
+ .driver_data = (void *)(BYT_RT5651_MCLK_EN |
+ BYT_RT5651_IN1_MAP),
+ },
+ {
+ /* Point of View mobii wintab p1006w (v1.0) */
+ .callback = byt_rt5651_pov_p1006w_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+ /* Note 105b is Foxcon's USB/PCI vendor id */
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DMIC_MAP |
+ BYT_RT5651_OVCD_TH_2000UA |
+ BYT_RT5651_OVCD_SF_0P75 |
+ BYT_RT5651_DMIC_EN |
+ BYT_RT5651_MCLK_EN |
+ BYT_RT5651_SSP0_AIF1),
+ },
+ {
+ /* VIOS LTH17 */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "VIOS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LTH17"),
+ },
+ .driver_data = (void *)(BYT_RT5651_IN1_IN2_MAP |
+ BYT_RT5651_JD1_1 |
+ BYT_RT5651_OVCD_TH_2000UA |
+ BYT_RT5651_OVCD_SF_1P0 |
+ BYT_RT5651_MCLK_EN),
+ },
+ {
+ /* Yours Y8W81 (and others using the same mainboard) */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."),
+ /* Partial match for all devs with a W86C mainboard */
+ DMI_MATCH(DMI_BIOS_VERSION, ".F.W86C."),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_SSP0_AIF1 |
+ BYT_RT5651_MONO_SPEAKER),
+ },
{}
};
-static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+/*
+ * Note this MUST be called before snd_soc_register_card(), so that the props
+ * are in place before the codec component driver's probe function parses them.
+ */
+static int byt_rt5651_add_codec_device_props(struct device *i2c_dev,
+ struct byt_rt5651_private *priv)
{
+ struct property_entry props[MAX_NO_PROPS] = {};
+ struct fwnode_handle *fwnode;
+ int cnt = 0;
int ret;
+
+ props[cnt++] = PROPERTY_ENTRY_U32("realtek,jack-detect-source",
+ BYT_RT5651_JDSRC(byt_rt5651_quirk));
+
+ props[cnt++] = PROPERTY_ENTRY_U32("realtek,over-current-threshold-microamp",
+ BYT_RT5651_OVCD_TH(byt_rt5651_quirk) * 100);
+
+ props[cnt++] = PROPERTY_ENTRY_U32("realtek,over-current-scale-factor",
+ BYT_RT5651_OVCD_SF(byt_rt5651_quirk));
+
+ if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN)
+ props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,dmic-en");
+
+ if (byt_rt5651_quirk & BYT_RT5651_JD_NOT_INV)
+ props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,jack-detect-not-inverted");
+
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode)) {
+ /* put_device(i2c_dev) is handled in caller */
+ return PTR_ERR(fwnode);
+ }
+
+ ret = device_add_software_node(i2c_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+{
struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
const struct snd_soc_dapm_route *custom_map;
int num_routes;
+ int report;
+ int ret;
- card->dapm.idle_bias_off = true;
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ /* Start with RC clk for jack-detect (we disable MCLK below) */
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
+ snd_soc_component_update_bits(codec, RT5651_GLB_CLK,
+ RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_RCCLK);
- dmi_check_system(byt_rt5651_quirk_table);
switch (BYT_RT5651_MAP(byt_rt5651_quirk)) {
case BYT_RT5651_IN1_MAP:
custom_map = byt_rt5651_intmic_in1_map;
num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_map);
break;
- case BYT_RT5651_DMIC2_MAP:
- custom_map = byt_rt5651_intmic_dmic2_map;
- num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic2_map);
+ case BYT_RT5651_IN2_MAP:
+ custom_map = byt_rt5651_intmic_in2_map;
+ num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_map);
+ break;
+ case BYT_RT5651_IN1_IN2_MAP:
+ custom_map = byt_rt5651_intmic_in1_in2_map;
+ num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_in2_map);
break;
default:
- custom_map = byt_rt5651_intmic_dmic1_map;
- num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic1_map);
+ custom_map = byt_rt5651_intmic_dmic_map;
+ num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map);
}
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ if (byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5651_ssp2_aif2_map,
+ ARRAY_SIZE(byt_rt5651_ssp2_aif2_map));
+ } else if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5651_ssp0_aif1_map,
+ ARRAY_SIZE(byt_rt5651_ssp0_aif1_map));
+ } else if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5651_ssp0_aif2_map,
+ ARRAY_SIZE(byt_rt5651_ssp0_aif2_map));
+ } else {
+ ret = snd_soc_dapm_add_routes(dapm,
+ byt_rt5651_ssp2_aif1_map,
+ ARRAY_SIZE(byt_rt5651_ssp2_aif1_map));
+ }
+ if (ret)
+ return ret;
ret = snd_soc_add_card_controls(card, byt_rt5651_controls,
ARRAY_SIZE(byt_rt5651_controls));
@@ -148,19 +640,55 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
dev_err(card->dev, "unable to add card controls\n");
return ret;
}
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
- return ret;
-}
+ /*
+ * The firmware might enable the clock at boot (this information
+ * may or may not be reflected in the enable clock register).
+ * To change the rate we must disable the clock first to cover
+ * these cases. Due to common clock framework restrictions that
+ * do not allow to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(priv->mclk);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
-static const struct snd_soc_pcm_stream byt_rt5651_dai_params = {
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
-};
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
+ ret = clk_set_rate(priv->mclk, 25000000);
+ else
+ ret = clk_set_rate(priv->mclk, 19200000);
+
+ if (ret)
+ dev_err(card->dev, "unable to set MCLK rate\n");
+
+ report = 0;
+ if (BYT_RT5651_JDSRC(byt_rt5651_quirk))
+ report = SND_JACK_HEADSET | SND_JACK_BTN_0;
+ else if (priv->hp_detect)
+ report = SND_JACK_HEADSET;
+
+ if (report) {
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",
+ report, &priv->jack,
+ bytcr_jack_pins,
+ ARRAY_SIZE(bytcr_jack_pins));
+ if (ret) {
+ dev_err(runtime->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ if (report & SND_JACK_BTN_0)
+ snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0,
+ KEY_PLAYPAUSE);
+
+ ret = snd_soc_component_set_jack(codec, &priv->jack,
+ priv->hp_detect);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
@@ -169,24 +697,32 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- int ret;
+ int ret, bits;
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ /* The DSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
- /* set SSP2 to 24-bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ if ((byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) ||
+ (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) {
+ /* set SSP0 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ bits = 16;
+ } else {
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ bits = 24;
+ }
/*
* Default mode for SSP configuration is TDM 4 slot, override config
- * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * with explicit setting to I2S 2ch. The word length is set with
* dai_set_tdm_slot() since there is no other API exposed
*/
- ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP
);
if (ret < 0) {
@@ -194,7 +730,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return ret;
}
- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits);
if (ret < 0) {
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
return ret;
@@ -203,11 +739,11 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
+static const unsigned int rates_48000[] = {
48000,
};
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
+static const struct snd_pcm_hw_constraint_list constraints_48000 = {
.count = ARRAY_SIZE(rates_48000),
.list = rates_48000,
};
@@ -219,75 +755,119 @@ static int byt_rt5651_aif1_startup(struct snd_pcm_substream *substream)
&constraints_48000);
}
-static struct snd_soc_ops byt_rt5651_aif1_ops = {
+static const struct snd_soc_ops byt_rt5651_aif1_ops = {
.startup = byt_rt5651_aif1_startup,
};
-static struct snd_soc_ops byt_rt5651_be_ssp2_ops = {
+static const struct snd_soc_ops byt_rt5651_be_ssp2_ops = {
.hw_params = byt_rt5651_aif1_hw_params,
};
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5651:00", "rt5651-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
static struct snd_soc_dai_link byt_rt5651_dais[] = {
[MERR_DPCM_AUDIO] = {
.name = "Audio Port",
.stream_name = "Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &byt_rt5651_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
},
[MERR_DPCM_DEEP_BUFFER] = {
.name = "Deep-Buffer Audio Port",
.stream_name = "Deep-Buffer Audio",
- .cpu_dai_name = "deepbuffer-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
.ops = &byt_rt5651_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Compressed Port",
- .stream_name = "Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
},
/* CODEC<->CODEC link */
/* back ends */
{
.name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
+ .id = 0,
.no_pcm = 1,
- .codec_dai_name = "rt5651-aif1",
- .codec_name = "i2c-10EC5651:00",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_rt5651_codec_fixup,
- .ignore_suspend = 1,
- .nonatomic = true,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.init = byt_rt5651_init,
.ops = &byt_rt5651_be_ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
},
};
/* SoC card */
+static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN];
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
+static char byt_rt5651_long_name[50]; /* = "bytcr-rt5651-*-spk-*-mic[-swapped-hp]" */
+#endif
+static char byt_rt5651_components[50]; /* = "cfg-spk:* cfg-mic:*" */
+
+static int byt_rt5651_suspend(struct snd_soc_card *card)
+{
+ struct snd_soc_component *component;
+
+ if (!BYT_RT5651_JDSRC(byt_rt5651_quirk))
+ return 0;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, byt_rt5651_codec_name)) {
+ dev_dbg(component->dev, "disabling jack detect before suspend\n");
+ snd_soc_component_set_jack(component, NULL, NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int byt_rt5651_resume(struct snd_soc_card *card)
+{
+ struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+
+ if (!BYT_RT5651_JDSRC(byt_rt5651_quirk))
+ return 0;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, byt_rt5651_codec_name)) {
+ dev_dbg(component->dev, "re-enabling jack detect after resume\n");
+ snd_soc_component_set_jack(component, &priv->jack,
+ priv->hp_detect);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht rt5651" /* card name will be 'sof-bytcht rt5651' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bytcr-rt5651"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
static struct snd_soc_card byt_rt5651_card = {
- .name = "bytcr-rt5651",
+ .name = CARD_NAME,
+ .driver_name = DRIVER_NAME,
.owner = THIS_MODULE,
.dai_link = byt_rt5651_dais,
.num_links = ARRAY_SIZE(byt_rt5651_dais),
@@ -296,32 +876,285 @@ static struct snd_soc_card byt_rt5651_card = {
.dapm_routes = byt_rt5651_audio_map,
.num_dapm_routes = ARRAY_SIZE(byt_rt5651_audio_map),
.fully_routed = true,
+ .suspend_pre = byt_rt5651_suspend,
+ .resume_post = byt_rt5651_resume,
+};
+
+static const struct acpi_gpio_params ext_amp_enable_gpios = { 0, 0, false };
+
+static const struct acpi_gpio_mapping cht_rt5651_gpios[] = {
+ /*
+ * Some boards have I2cSerialBusV2, GpioIo, GpioInt as ACPI resources,
+ * other boards may have I2cSerialBusV2, GpioInt, GpioIo instead.
+ * We want the GpioIo one for the ext-amp-enable-gpio.
+ */
+ { "ext-amp-enable-gpios", &ext_amp_enable_gpios, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { },
+};
+
+struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */
+ u64 aif_value; /* 1: AIF1, 2: AIF2 */
+ u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */
};
static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ static const char * const mic_name[] = { "dmic", "in1", "in2", "in12" };
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
+ struct byt_rt5651_private *priv;
+ const char *platform_name;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+ bool sof_parent;
+ bool is_bytcr = false;
int ret_val = 0;
+ int dai_index = 0;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
/* register the soc card */
- byt_rt5651_card.dev = &pdev->dev;
+ byt_rt5651_card.dev = dev;
+ snd_soc_card_set_drvdata(&byt_rt5651_card, priv);
+
+ /* fix index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) {
+ if (byt_rt5651_dais[i].num_codecs &&
+ !strcmp(byt_rt5651_dais[i].codecs->name,
+ "i2c-10EC5651:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ byt_rt5651_dais[dai_index].codecs->name = byt_rt5651_codec_name;
+ } else {
+ dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+ priv->codec_dev = get_device(codec_dev);
+
+ /*
+ * swap SSP0 if bytcr is detected
+ * (will be overridden if DMI quirk is detected)
+ */
+ if (soc_intel_is_byt()) {
+ if (mach->mach_params.acpi_ipc_irq_index == 0)
+ is_bytcr = true;
+ }
- ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card);
+ if (is_bytcr) {
+ /*
+ * Baytrail CR platforms may have CHAN package in BIOS, try
+ * to find relevant routing quirk based as done on Windows
+ * platforms. We have to read the information directly from the
+ * BIOS, at this stage the card is not created and the links
+ * with the codec driver/pdata are non-existent
+ */
+ struct acpi_chan_package chan_package = { 0 };
+
+ /* format specified: 2 64-bit integers */
+ struct acpi_buffer format = {sizeof("NN"), "NN"};
+ struct acpi_buffer state = {0, NULL};
+ struct snd_soc_acpi_package_context pkg_ctx;
+ bool pkg_found = false;
+
+ state.length = sizeof(chan_package);
+ state.pointer = &chan_package;
+
+ pkg_ctx.name = "CHAN";
+ pkg_ctx.length = 2;
+ pkg_ctx.format = &format;
+ pkg_ctx.state = &state;
+ pkg_ctx.data_valid = false;
+
+ pkg_found = snd_soc_acpi_find_package_from_hid(mach->id,
+ &pkg_ctx);
+ if (pkg_found) {
+ if (chan_package.aif_value == 1) {
+ dev_info(dev, "BIOS Routing: AIF1 connected\n");
+ byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF1;
+ } else if (chan_package.aif_value == 2) {
+ dev_info(dev, "BIOS Routing: AIF2 connected\n");
+ byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2;
+ } else {
+ dev_info(dev, "BIOS Routing isn't valid, ignored\n");
+ pkg_found = false;
+ }
+ }
+
+ if (!pkg_found) {
+ /* no BIOS indications, assume SSP0-AIF2 connection */
+ byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2;
+ }
+ }
+
+ /* check quirks before creating card */
+ dmi_check_system(byt_rt5651_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
+ byt_rt5651_quirk, quirk_override);
+ byt_rt5651_quirk = quirk_override;
+ }
+
+ /* Must be called before register_card, also see declaration comment. */
+ ret_val = byt_rt5651_add_codec_device_props(codec_dev, priv);
+ if (ret_val)
+ goto err_device;
+
+ /* Cherry Trail devices use an external amplifier enable gpio */
+ if (soc_intel_is_cht() && !byt_rt5651_gpios)
+ byt_rt5651_gpios = cht_rt5651_gpios;
+
+ if (byt_rt5651_gpios) {
+ devm_acpi_dev_add_driver_gpios(codec_dev, byt_rt5651_gpios);
+ priv->ext_amp_gpio = devm_fwnode_gpiod_get(dev, codec_dev->fwnode,
+ "ext-amp-enable",
+ GPIOD_OUT_LOW,
+ "speaker-amp");
+ if (IS_ERR(priv->ext_amp_gpio)) {
+ ret_val = PTR_ERR(priv->ext_amp_gpio);
+ switch (ret_val) {
+ case -ENOENT:
+ priv->ext_amp_gpio = NULL;
+ break;
+ default:
+ dev_err(dev, "Failed to get ext-amp-enable GPIO: %d\n", ret_val);
+ fallthrough;
+ case -EPROBE_DEFER:
+ goto err;
+ }
+ }
+ priv->hp_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode,
+ "hp-detect",
+ GPIOD_IN,
+ "hp-detect");
+ if (IS_ERR(priv->hp_detect)) {
+ ret_val = PTR_ERR(priv->hp_detect);
+ switch (ret_val) {
+ case -ENOENT:
+ priv->hp_detect = NULL;
+ break;
+ default:
+ dev_err(dev, "Failed to get hp-detect GPIO: %d\n", ret_val);
+ fallthrough;
+ case -EPROBE_DEFER:
+ goto err;
+ }
+ }
+ }
+
+ log_quirks(dev);
+
+ if ((byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) ||
+ (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2))
+ byt_rt5651_dais[dai_index].codecs->dai_name = "rt5651-aif2";
+
+ if ((byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) ||
+ (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2))
+ byt_rt5651_dais[dai_index].cpus->dai_name = "ssp0-port";
+
+ if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) {
+ priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3");
+ if (IS_ERR(priv->mclk)) {
+ ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk),
+ "Failed to get MCLK from pmc_plt_clk_3\n");
+ goto err;
+ }
+ /*
+ * Fall back to bit clock usage when clock is not
+ * available likely due to missing dependencies.
+ */
+ if (!priv->mclk)
+ byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN;
+ }
+
+ snprintf(byt_rt5651_components, sizeof(byt_rt5651_components),
+ "cfg-spk:%s cfg-mic:%s%s",
+ (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ? "1" : "2",
+ mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)],
+ (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) ?
+ " cfg-hp:lrswap" : "");
+ byt_rt5651_card.components = byt_rt5651_components;
+#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
+ snprintf(byt_rt5651_long_name, sizeof(byt_rt5651_long_name),
+ "bytcr-rt5651-%s-spk-%s-mic%s",
+ (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ?
+ "mono" : "stereo",
+ mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)],
+ (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) ?
+ "-hp-swapped" : "");
+ byt_rt5651_card.long_name = byt_rt5651_long_name;
+#endif
+
+ /* override platform name, if required */
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5651_card,
+ platform_name);
+ if (ret_val)
+ goto err;
+
+ sof_parent = snd_soc_acpi_sof_parent(dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ byt_rt5651_card.name = SOF_CARD_NAME;
+ byt_rt5651_card.driver_name = SOF_DRIVER_NAME;
+ } else {
+ byt_rt5651_card.name = CARD_NAME;
+ byt_rt5651_card.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ dev->driver->pm = &snd_soc_pm_ops;
+
+ ret_val = devm_snd_soc_register_card(dev, &byt_rt5651_card);
if (ret_val) {
- dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
- ret_val);
- return ret_val;
+ dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+ goto err;
}
platform_set_drvdata(pdev, &byt_rt5651_card);
return ret_val;
+
+err:
+ device_remove_software_node(priv->codec_dev);
+err_device:
+ put_device(priv->codec_dev);
+ return ret_val;
+}
+
+static void snd_byt_rt5651_mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
+
+ device_remove_software_node(priv->codec_dev);
+ put_device(priv->codec_dev);
}
static struct platform_driver snd_byt_rt5651_mc_driver = {
.driver = {
.name = "bytcr_rt5651",
- .pm = &snd_soc_pm_ops,
},
.probe = snd_byt_rt5651_mc_probe,
+ .remove = snd_byt_rt5651_mc_remove,
};
module_platform_driver(snd_byt_rt5651_mc_driver);
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
new file mode 100644
index 000000000000..4879f79aef29
--- /dev/null
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bytcr_wm5102.c - ASoc Machine driver for Intel Baytrail platforms with a
+ * Wolfson Microelectronics WM5102 codec
+ *
+ * Copyright (C) 2020 Hans de Goede <hdegoede@redhat.com>
+ * Loosely based on bytcr_rt5640.c which is:
+ * Copyright (C) 2014-2020 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_data/x86/soc.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/wm5102.h"
+#include "../atom/sst-atom-controls.h"
+
+#define WM5102_MAX_SYSCLK_4K 49152000 /* max sysclk for 4K family */
+#define WM5102_MAX_SYSCLK_11025 45158400 /* max sysclk for 11.025K family */
+
+struct byt_wm5102_private {
+ struct snd_soc_jack jack;
+ struct clk *mclk;
+ struct gpio_desc *spkvdd_en_gpio;
+ int mclk_freq;
+};
+
+#define BYT_WM5102_IN_MAP GENMASK(3, 0)
+#define BYT_WM5102_OUT_MAP GENMASK(7, 4)
+#define BYT_WM5102_SSP2 BIT(16)
+#define BYT_WM5102_MCLK_19_2MHZ BIT(17)
+
+enum {
+ BYT_WM5102_INTMIC_IN3L_HSMIC_IN1L,
+ BYT_WM5102_INTMIC_IN1L_HSMIC_IN2L,
+};
+
+/* Note these values are pre-shifted for easy use of setting quirks */
+enum {
+ BYT_WM5102_SPK_SPK_MAP = FIELD_PREP_CONST(BYT_WM5102_OUT_MAP, 0),
+ BYT_WM5102_SPK_HPOUT2_MAP = FIELD_PREP_CONST(BYT_WM5102_OUT_MAP, 1),
+};
+
+static unsigned long quirk;
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ switch (quirk & BYT_WM5102_IN_MAP) {
+ case BYT_WM5102_INTMIC_IN3L_HSMIC_IN1L:
+ dev_info_once(dev, "quirk INTMIC_IN3L_HSMIC_IN1L enabled\n");
+ break;
+ case BYT_WM5102_INTMIC_IN1L_HSMIC_IN2L:
+ dev_info_once(dev, "quirk INTMIC_IN1L_HSMIC_IN2L enabled\n");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%lx, defaulting to INTMIC_IN3L_HSMIC_IN1L\n",
+ quirk & BYT_WM5102_IN_MAP);
+ quirk &= ~BYT_WM5102_IN_MAP;
+ quirk |= BYT_WM5102_INTMIC_IN3L_HSMIC_IN1L;
+ break;
+ }
+ switch (quirk & BYT_WM5102_OUT_MAP) {
+ case BYT_WM5102_SPK_SPK_MAP:
+ dev_info_once(dev, "quirk SPK_SPK_MAP enabled\n");
+ break;
+ case BYT_WM5102_SPK_HPOUT2_MAP:
+ dev_info_once(dev, "quirk SPK_HPOUT2_MAP enabled\n");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid output map: 0x%lx, defaulting to SPK_SPK_MAP\n",
+ quirk & BYT_WM5102_OUT_MAP);
+ quirk &= ~BYT_WM5102_OUT_MAP;
+ quirk |= BYT_WM5102_SPK_SPK_MAP;
+ break;
+ }
+ if (quirk & BYT_WM5102_SSP2)
+ dev_info_once(dev, "quirk SSP2 enabled");
+ if (quirk & BYT_WM5102_MCLK_19_2MHZ)
+ dev_info_once(dev, "quirk MCLK 19.2MHz enabled");
+}
+
+static int byt_wm5102_spkvdd_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
+
+ gpiod_set_value_cansleep(priv->spkvdd_en_gpio,
+ !!SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int byt_wm5102_prepare_and_enable_pll1(struct snd_soc_dai *codec_dai, int rate)
+{
+ struct snd_soc_component *codec_component = codec_dai->component;
+ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(codec_component->card);
+ int sr_mult = ((rate % 4000) == 0) ?
+ (WM5102_MAX_SYSCLK_4K / rate) :
+ (WM5102_MAX_SYSCLK_11025 / rate);
+ int ret;
+
+ /* Reset FLL1 */
+ snd_soc_dai_set_pll(codec_dai, WM5102_FLL1_REFCLK, ARIZONA_FLL_SRC_NONE, 0, 0);
+ snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0);
+
+ /* Configure the FLL1 PLL before selecting it */
+ ret = snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_CLK_SRC_MCLK1,
+ priv->mclk_freq, rate * sr_mult);
+ if (ret) {
+ dev_err(codec_component->dev, "Error setting PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_sysclk(codec_component, ARIZONA_CLK_SYSCLK,
+ ARIZONA_CLK_SRC_FLL1, rate * sr_mult,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(codec_component->dev, "Error setting SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, ARIZONA_CLK_SYSCLK,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(codec_component->dev, "Error setting clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, "wm5102-aif1");
+ if (!codec_dai) {
+ dev_err(card->dev, "Error codec DAI not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(card->dev, "Error enabling MCLK: %d\n", ret);
+ return ret;
+ }
+ ret = byt_wm5102_prepare_and_enable_pll1(codec_dai, 48000);
+ if (ret) {
+ dev_err(card->dev, "Error setting codec sysclk: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /*
+ * The WM5102 has a separate 32KHz clock for jack-detect
+ * so we can disable the PLL, followed by disabling the
+ * platform clock which is the source-clock for the PLL.
+ */
+ snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0);
+ clk_disable_unprepare(priv->mclk);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget byt_wm5102_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Speaker VDD", SND_SOC_NOPM, 0, 0,
+ byt_wm5102_spkvdd_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route byt_wm5102_audio_map[] = {
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Internal Mic", NULL, "Platform Clock"},
+ {"Speaker", NULL, "Platform Clock"},
+ {"Speaker", NULL, "Speaker VDD"},
+
+ {"Headphone", NULL, "HPOUT1L"},
+ {"Headphone", NULL, "HPOUT1R"},
+
+ /*
+ * The Headset Mix uses MICBIAS1 or 2 depending on if a CTIA/OMTP Headset
+ * is connected, as the MICBIAS is applied after the CTIA/OMTP cross-switch.
+ */
+ {"Headset Mic", NULL, "MICBIAS1"},
+ {"Headset Mic", NULL, "MICBIAS2"},
+ {"Internal Mic", NULL, "MICBIAS3"},
+};
+
+static const struct snd_soc_dapm_route bytcr_wm5102_ssp0_map[] = {
+ {"AIF1 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
+ {"ssp0 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route bytcr_wm5102_ssp2_map[] = {
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_wm5102_spk_spk_map[] = {
+ {"Speaker", NULL, "SPKOUTLP"},
+ {"Speaker", NULL, "SPKOUTLN"},
+ {"Speaker", NULL, "SPKOUTRP"},
+ {"Speaker", NULL, "SPKOUTRN"},
+};
+
+static const struct snd_soc_dapm_route byt_wm5102_spk_hpout2_map[] = {
+ {"Speaker", NULL, "HPOUT2L"},
+ {"Speaker", NULL, "HPOUT2R"},
+};
+
+static const struct snd_soc_dapm_route byt_wm5102_intmic_in3l_hsmic_in1l_map[] = {
+ {"IN3L", NULL, "Internal Mic"},
+ {"IN1L", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_wm5102_intmic_in1l_hsmic_in2l_map[] = {
+ {"IN1L", NULL, "Internal Mic"},
+ {"IN2L", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new byt_wm5102_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static struct snd_soc_jack_pin byt_wm5102_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ const struct snd_soc_dapm_route *custom_map = NULL;
+ int ret, jack_type, num_routes = 0;
+
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ ret = snd_soc_add_card_controls(card, byt_wm5102_controls,
+ ARRAY_SIZE(byt_wm5102_controls));
+ if (ret) {
+ dev_err(card->dev, "Error adding card controls: %d\n", ret);
+ return ret;
+ }
+
+ switch (quirk & BYT_WM5102_IN_MAP) {
+ case BYT_WM5102_INTMIC_IN3L_HSMIC_IN1L:
+ custom_map = byt_wm5102_intmic_in3l_hsmic_in1l_map;
+ num_routes = ARRAY_SIZE(byt_wm5102_intmic_in3l_hsmic_in1l_map);
+ break;
+ case BYT_WM5102_INTMIC_IN1L_HSMIC_IN2L:
+ custom_map = byt_wm5102_intmic_in1l_hsmic_in2l_map;
+ num_routes = ARRAY_SIZE(byt_wm5102_intmic_in1l_hsmic_in2l_map);
+ break;
+ }
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ switch (quirk & BYT_WM5102_OUT_MAP) {
+ case BYT_WM5102_SPK_SPK_MAP:
+ custom_map = byt_wm5102_spk_spk_map;
+ num_routes = ARRAY_SIZE(byt_wm5102_spk_spk_map);
+ break;
+ case BYT_WM5102_SPK_HPOUT2_MAP:
+ custom_map = byt_wm5102_spk_hpout2_map;
+ num_routes = ARRAY_SIZE(byt_wm5102_spk_hpout2_map);
+ break;
+ }
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ if (quirk & BYT_WM5102_SSP2) {
+ custom_map = bytcr_wm5102_ssp2_map;
+ num_routes = ARRAY_SIZE(bytcr_wm5102_ssp2_map);
+ } else {
+ custom_map = bytcr_wm5102_ssp0_map;
+ num_routes = ARRAY_SIZE(bytcr_wm5102_ssp0_map);
+ }
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ if (quirk & BYT_WM5102_MCLK_19_2MHZ)
+ priv->mclk_freq = 19200000;
+ else
+ priv->mclk_freq = 25000000;
+
+ /*
+ * The firmware might enable the clock at boot (this information
+ * may or may not be reflected in the enable clock register).
+ * To change the rate we must disable the clock first to cover these
+ * cases. Due to common clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled, we need to enable
+ * the clock first.
+ */
+ ret = clk_prepare_enable(priv->mclk);
+ if (!ret)
+ clk_disable_unprepare(priv->mclk);
+
+ ret = clk_set_rate(priv->mclk, priv->mclk_freq);
+ if (ret) {
+ dev_err(card->dev, "Error setting MCLK rate: %d\n", ret);
+ return ret;
+ }
+
+ jack_type = ARIZONA_JACK_MASK | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ ret = snd_soc_card_jack_new_pins(card, "Headset", jack_type,
+ &priv->jack, byt_wm5102_pins,
+ ARRAY_SIZE(byt_wm5102_pins));
+ if (ret) {
+ dev_err(card->dev, "Error creating jack: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_component_set_jack(component, &priv->jack, NULL);
+
+ return 0;
+}
+
+static int byt_wm5102_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret, bits;
+
+ /* The DSP will convert the FE rate to 48k, stereo */
+ rate->min = 48000;
+ rate->max = 48000;
+ channels->min = 2;
+ channels->max = 2;
+
+ if (quirk & BYT_WM5102_SSP2) {
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ bits = 24;
+ } else {
+ /* set SSP0 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ bits = 16;
+ }
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 16-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting format to I2S: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting I2S config: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int byt_wm5102_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static const struct snd_soc_ops byt_wm5102_aif1_ops = {
+ .startup = byt_wm5102_aif1_startup,
+};
+
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp0_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
+
+SND_SOC_DAILINK_DEF(ssp0_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC(
+ /*
+ * Note there is no need to overwrite the codec-name as is done in
+ * other bytcr machine drivers, because the codec is a MFD child-dev.
+ */
+ "wm5102-codec",
+ "wm5102-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
+static struct snd_soc_dai_link byt_wm5102_dais[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Baytrail Audio Port",
+ .stream_name = "Baytrail Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .ops = &byt_wm5102_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
+
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &byt_wm5102_aif1_ops,
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
+ },
+ /* back ends */
+ {
+ /*
+ * This dailink is updated dynamically to point to SSP0 or SSP2.
+ * Yet its name is always kept as "SSP2-Codec" because the SOF
+ * tplg files hardcode "SSP2-Codec" even in byt-foo-ssp0.tplg.
+ */
+ .name = "SSP2-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .be_hw_params_fixup = byt_wm5102_codec_fixup,
+ .init = byt_wm5102_init,
+ SND_SOC_DAILINK_REG(ssp0_port, ssp0_codec, platform),
+ },
+};
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht wm5102" /* card name will be 'sof-bytcht wm5102' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "bytcr-wm5102"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+/* SoC card */
+static struct snd_soc_card byt_wm5102_card = {
+ .owner = THIS_MODULE,
+ .dai_link = byt_wm5102_dais,
+ .num_links = ARRAY_SIZE(byt_wm5102_dais),
+ .dapm_widgets = byt_wm5102_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_wm5102_widgets),
+ .dapm_routes = byt_wm5102_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_wm5102_audio_map),
+ .fully_routed = true,
+};
+
+static char byt_wm5102_components[64]; /* = "cfg-spk:* cfg-int-mic:* cfg-hs-mic:* ..." */
+
+static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
+{
+ static const char * const out_map_name[] = { "spk", "hpout2" };
+ static const char * const intmic_map_name[] = { "in3l", "in1l" };
+ static const char * const hsmic_map_name[] = { "in1l", "in2l" };
+ char codec_name[SND_ACPI_I2C_ID_LEN];
+ struct device *dev = &pdev->dev;
+ struct byt_wm5102_private *priv;
+ struct snd_soc_acpi_mach *mach;
+ const char *platform_name;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+ int dai_index = 0;
+ bool sof_parent;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get MCLK */
+ priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3");
+ if (IS_ERR(priv->mclk))
+ return dev_err_probe(dev, PTR_ERR(priv->mclk), "getting pmc_plt_clk_3\n");
+
+ /*
+ * Get speaker VDD enable GPIO:
+ * 1. Get codec-device-name
+ * 2. Get codec-device
+ * 3. Get GPIO from codec-device
+ */
+ mach = dev->platform_data;
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name), "spi-%s", acpi_dev_name(adev));
+ acpi_dev_put(adev);
+ } else {
+ /* Special case for when the codec is missing from the DSTD */
+ strscpy(codec_name, "spi-wm5102", sizeof(codec_name));
+ }
+
+ codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, codec_name);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+
+ /* Note no devm_ here since we call gpiod_get on codec_dev rather then dev */
+ priv->spkvdd_en_gpio = gpiod_get(codec_dev, "wlf,spkvdd-ena", GPIOD_OUT_LOW);
+ put_device(codec_dev);
+
+ if (IS_ERR(priv->spkvdd_en_gpio)) {
+ ret = PTR_ERR(priv->spkvdd_en_gpio);
+ /*
+ * The spkvdd gpio-lookup is registered by: drivers/mfd/arizona-spi.c,
+ * so -ENOENT means that arizona-spi hasn't probed yet.
+ */
+ if (ret == -ENOENT)
+ ret = -EPROBE_DEFER;
+
+ return dev_err_probe(dev, ret, "getting spkvdd-GPIO\n");
+ }
+
+ if (soc_intel_is_cht()) {
+ /*
+ * CHT always uses SSP2 and 19.2 MHz; and
+ * the one currently supported CHT design uses HPOUT2 as
+ * speaker output and has the intmic on IN1L + hsmic on IN2L.
+ */
+ quirk = BYT_WM5102_SSP2 | BYT_WM5102_MCLK_19_2MHZ |
+ BYT_WM5102_INTMIC_IN1L_HSMIC_IN2L |
+ BYT_WM5102_SPK_HPOUT2_MAP;
+ }
+ if (quirk_override != -1) {
+ dev_info_once(dev, "Overriding quirk 0x%lx => 0x%x\n",
+ quirk, quirk_override);
+ quirk = quirk_override;
+ }
+ log_quirks(dev);
+
+ snprintf(byt_wm5102_components, sizeof(byt_wm5102_components),
+ "cfg-spk:%s cfg-intmic:%s cfg-hsmic:%s",
+ out_map_name[FIELD_GET(BYT_WM5102_OUT_MAP, quirk)],
+ intmic_map_name[FIELD_GET(BYT_WM5102_IN_MAP, quirk)],
+ hsmic_map_name[FIELD_GET(BYT_WM5102_IN_MAP, quirk)]);
+ byt_wm5102_card.components = byt_wm5102_components;
+
+ /* find index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(byt_wm5102_dais); i++) {
+ if (byt_wm5102_dais[i].num_codecs &&
+ !strcmp(byt_wm5102_dais[i].codecs->name,
+ "wm5102-codec")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* override platform name, if required */
+ byt_wm5102_card.dev = dev;
+ platform_name = mach->mach_params.platform;
+ ret = snd_soc_fixup_dai_links_platform_name(&byt_wm5102_card, platform_name);
+ if (ret)
+ goto out_put_gpio;
+
+ /* override SSP port, if required */
+ if (quirk & BYT_WM5102_SSP2)
+ byt_wm5102_dais[dai_index].cpus->dai_name = "ssp2-port";
+
+ /* set card and driver name and pm-ops */
+ sof_parent = snd_soc_acpi_sof_parent(dev);
+ if (sof_parent) {
+ byt_wm5102_card.name = SOF_CARD_NAME;
+ byt_wm5102_card.driver_name = SOF_DRIVER_NAME;
+ dev->driver->pm = &snd_soc_pm_ops;
+ } else {
+ byt_wm5102_card.name = CARD_NAME;
+ byt_wm5102_card.driver_name = DRIVER_NAME;
+ }
+
+ snd_soc_card_set_drvdata(&byt_wm5102_card, priv);
+ ret = devm_snd_soc_register_card(dev, &byt_wm5102_card);
+ if (ret) {
+ dev_err_probe(dev, ret, "registering card\n");
+ goto out_put_gpio;
+ }
+
+ platform_set_drvdata(pdev, &byt_wm5102_card);
+ return 0;
+
+out_put_gpio:
+ gpiod_put(priv->spkvdd_en_gpio);
+ return ret;
+}
+
+static void snd_byt_wm5102_mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
+
+ gpiod_put(priv->spkvdd_en_gpio);
+}
+
+static struct platform_driver snd_byt_wm5102_mc_driver = {
+ .driver = {
+ .name = "bytcr_wm5102",
+ },
+ .probe = snd_byt_wm5102_mc_probe,
+ .remove = snd_byt_wm5102_mc_remove,
+};
+
+module_platform_driver(snd_byt_wm5102_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Baytrail with WM5102 codec machine driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcr_wm5102");
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 90588d6e64fc..ad45b79d3e4b 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based
* platforms Cherrytrail and Braswell, with max98090 & TI codec.
@@ -7,25 +8,20 @@
* This file is modified from cht_bsw_rt5645.c
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
- * 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/dmi.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-acpi.h>
#include <sound/jack.h>
#include "../../codecs/max98090.h"
#include "../atom/sst-atom-controls.h"
@@ -34,21 +30,45 @@
#define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "HiFi"
+#define QUIRK_PMC_PLT_CLK_0 0x01
+
struct cht_mc_private {
+ struct clk *mclk;
struct snd_soc_jack jack;
bool ts3a227e_present;
+ int quirks;
};
-static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
{
- struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_dai *codec_dai;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ /* See the comment in snd_cht_mc_probe() */
+ if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
+ return 0;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+ return -EIO;
+ }
- list_for_each_entry(rtd, &card->rtd_list, list) {
- if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
- strlen(CHT_CODEC_DAI)))
- return rtd->codec_dai;
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = clk_prepare_enable(ctx->mclk);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ } else {
+ clk_disable_unprepare(ctx->mclk);
}
- return NULL;
+
+ return 0;
}
static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
@@ -56,6 +76,9 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route cht_audio_map[] = {
@@ -72,6 +95,10 @@ static const struct snd_soc_dapm_route cht_audio_map[] = {
{"codec_in0", NULL, "ssp2 Rx" },
{"codec_in1", NULL, "ssp2 Rx" },
{"ssp2 Rx", NULL, "HiFi Capture"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
};
static const struct snd_kcontrol_new cht_mc_controls[] = {
@@ -84,8 +111,8 @@ static const struct snd_kcontrol_new cht_mc_controls[] = {
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK,
@@ -102,7 +129,7 @@ static int cht_ti_jack_event(struct notifier_block *nb,
unsigned long event, void *data)
{
struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
- struct snd_soc_dapm_context *dapm = &jack->card->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(jack->card);
if (event & SND_JACK_MICROPHONE) {
snd_soc_dapm_force_enable_pin(dapm, "SHDN");
@@ -121,6 +148,40 @@ static struct notifier_block cht_jack_nb = {
.notifier_call = cht_ti_jack_event,
};
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ .name = "hp",
+ .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
+ .debounce_time = 200,
+ },
+ {
+ .name = "mic",
+ .invert = 1,
+ .report = SND_JACK_MICROPHONE,
+ .debounce_time = 200,
+ },
+};
+
+static const struct acpi_gpio_params hp_gpios = { 0, 0, false };
+static const struct acpi_gpio_params mic_gpios = { 1, 0, false };
+
+static const struct acpi_gpio_mapping acpi_max98090_gpios[] = {
+ { "hp-gpios", &hp_gpios, 1 },
+ { "mic-gpios", &mic_gpios, 1 },
+ {},
+};
+
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
@@ -128,30 +189,60 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
struct snd_soc_jack *jack = &ctx->jack;
- /**
- * TI supports 4 butons headset detection
- * KEY_MEDIA
- * KEY_VOICECOMMAND
- * KEY_VOLUMEUP
- * KEY_VOLUMEDOWN
- */
- if (ctx->ts3a227e_present)
- jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3;
- else
- jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
+ if (ctx->ts3a227e_present) {
+ /*
+ * The jack has already been created in the
+ * cht_max98090_headset_init() function.
+ */
+ snd_soc_jack_notifier_register(jack, &cht_jack_nb);
+ return 0;
+ }
- ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
- jack_type, jack, NULL, 0);
+ jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset Jack",
+ jack_type, jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret) {
dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
return ret;
}
- if (ctx->ts3a227e_present)
- snd_soc_jack_notifier_register(jack, &cht_jack_nb);
+ ret = snd_soc_jack_add_gpiods(runtime->card->dev->parent, jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ if (ret) {
+ /*
+ * flag error but don't bail if jack detect is broken
+ * due to platform issues or bad BIOS/configuration
+ */
+ dev_err(runtime->dev,
+ "jack detection gpios not added, error %d\n", ret);
+ }
+
+ /* See the comment in snd_cht_mc_probe() */
+ if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
+ return 0;
+
+ /*
+ * The firmware might enable the clock at
+ * boot (this information may or may not
+ * be reflected in the enable clock register).
+ * To change the rate we must disable the clock
+ * first to cover these cases. Due to common
+ * clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(ctx->mclk);
+ if (!ret)
+ clk_disable_unprepare(ctx->mclk);
+
+ ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
+
+ if (ret)
+ dev_err(runtime->dev, "unable to set MCLK rate\n");
return ret;
}
@@ -166,27 +257,26 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
int ret = 0;
unsigned int fmt = 0;
- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16);
if (ret < 0) {
dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret);
return ret;
}
- fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS;
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_BP_FP;
- ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), fmt);
if (ret < 0) {
dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret);
return ret;
}
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
- /* set SSP2 to 24-bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ /* set SSP2 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
@@ -200,80 +290,101 @@ static int cht_max98090_headset_init(struct snd_soc_component *component)
{
struct snd_soc_card *card = component->card;
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack *jack = &ctx->jack;
+ int jack_type;
+ int ret;
- return ts3a227e_enable_jack_detect(component, &ctx->jack);
+ /*
+ * TI supports 4 buttons headset detection
+ * KEY_MEDIA
+ * KEY_VOICECOMMAND
+ * KEY_VOLUMEUP
+ * KEY_VOLUMEDOWN
+ */
+ jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3;
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, jack);
+ if (ret) {
+ dev_err(card->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ return ts3a227e_enable_jack_detect(component, jack);
}
-static struct snd_soc_ops cht_aif1_ops = {
+static const struct snd_soc_ops cht_aif1_ops = {
.startup = cht_aif1_startup,
};
-static struct snd_soc_ops cht_be_ssp2_ops = {
+static const struct snd_soc_ops cht_be_ssp2_ops = {
.hw_params = cht_aif1_hw_params,
};
static struct snd_soc_aux_dev cht_max98090_headset_dev = {
- .name = "Headset Chip",
+ .dlc = COMP_AUX("i2c-104C227E:00"),
.init = cht_max98090_headset_init,
- .codec_name = "i2c-104C227E:00",
};
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-193C9890:00", "HiFi")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
static struct snd_soc_dai_link cht_dailink[] = {
[MERR_DPCM_AUDIO] = {
.name = "Audio Port",
.stream_name = "Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &cht_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
},
[MERR_DPCM_DEEP_BUFFER] = {
.name = "Deep-Buffer Audio Port",
.stream_name = "Deep-Buffer Audio",
- .cpu_dai_name = "deepbuffer-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
.ops = &cht_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Compressed Port",
- .stream_name = "Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
},
/* back ends */
{
.name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
+ .id = 0,
.no_pcm = 1,
- .codec_dai_name = "HiFi",
- .codec_name = "i2c-193C9890:00",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &cht_be_ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
},
};
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht max98090" /* card name will be 'sof-bytcht max98090 */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "chtmax98090"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
/* SoC card */
static struct snd_soc_card snd_soc_card_cht = {
- .name = "chtmax98090",
.owner = THIS_MODULE,
.dai_link = cht_dailink,
.num_links = ARRAY_SIZE(cht_dailink),
@@ -287,41 +398,219 @@ static struct snd_soc_card snd_soc_card_cht = {
.num_controls = ARRAY_SIZE(cht_mc_controls),
};
-static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
+static const struct dmi_system_id cht_max98090_quirk_table[] = {
+ {
+ /* Banjo model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Banjo"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Candy model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Candy"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Clapper model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Clapper"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Cyan model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Cyan"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Enguarde model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Enguarde"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Glimmer model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Gnawty model Chromebook (Acer Chromebook CB3-111) */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Gnawty"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Heli model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Heli"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Kip model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kip"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Ninja model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ninja"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Orco model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Orco"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Quawks model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Quawks"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Rambi model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Rambi"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Squawks model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Squawks"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Sumo model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Sumo"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Swanky model Chromebook (Toshiba Chromebook 2) */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Swanky"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {
+ /* Winky model Chromebook */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Winky"),
+ },
+ .driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
+ },
+ {}
+};
static int snd_cht_mc_probe(struct platform_device *pdev)
{
+ const struct dmi_system_id *dmi_id;
+ struct device *dev = &pdev->dev;
int ret_val = 0;
- bool found = false;
struct cht_mc_private *drv;
+ const char *mclk_name;
+ struct snd_soc_acpi_mach *mach;
+ const char *platform_name;
+ bool sof_parent;
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
- if (ACPI_SUCCESS(acpi_get_devices(
- "104C227E",
- snd_acpi_codec_match,
- &found, NULL)) && found) {
- drv->ts3a227e_present = true;
- } else {
+ dmi_id = dmi_first_match(cht_max98090_quirk_table);
+ if (dmi_id)
+ drv->quirks = (unsigned long)dmi_id->driver_data;
+
+ drv->ts3a227e_present = acpi_dev_found("104C227E");
+ if (!drv->ts3a227e_present) {
/* no need probe TI jack detection chip */
snd_soc_card_cht.aux_dev = NULL;
snd_soc_card_cht.num_aux_devs = 0;
- drv->ts3a227e_present = false;
+
+ ret_val = devm_acpi_dev_add_driver_gpios(dev->parent,
+ acpi_max98090_gpios);
+ if (ret_val)
+ dev_dbg(dev, "Unable to add GPIO mapping table\n");
}
+ /* override platform name, if required */
+ snd_soc_card_cht.dev = dev;
+ mach = dev->platform_data;
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
+ platform_name);
+ if (ret_val)
+ return ret_val;
+
/* register the soc card */
- snd_soc_card_cht.dev = &pdev->dev;
snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+
+ if (drv->quirks & QUIRK_PMC_PLT_CLK_0)
+ mclk_name = "pmc_plt_clk_0";
+ else
+ mclk_name = "pmc_plt_clk_3";
+
+ drv->mclk = devm_clk_get(dev, mclk_name);
+ if (IS_ERR(drv->mclk)) {
+ dev_err(dev,
+ "Failed to get MCLK from %s: %ld\n",
+ mclk_name, PTR_ERR(drv->mclk));
+ return PTR_ERR(drv->mclk);
+ }
+
+ /*
+ * Boards which have the MAX98090's clk connected to clk_0 do not seem
+ * to like it if we muck with the clock. If we disable the clock when
+ * it is unused we get "max98090 i2c-193C9890:00: PLL unlocked" errors
+ * and the PLL never seems to lock again.
+ * So for these boards we enable it here once and leave it at that.
+ */
+ if (drv->quirks & QUIRK_PMC_PLT_CLK_0) {
+ ret_val = clk_prepare_enable(drv->mclk);
+ if (ret_val < 0) {
+ dev_err(dev, "MCLK enable error: %d\n", ret_val);
+ return ret_val;
+ }
+ }
+
+ sof_parent = snd_soc_acpi_sof_parent(dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ snd_soc_card_cht.name = SOF_CARD_NAME;
+ snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;
+ } else {
+ snd_soc_card_cht.name = CARD_NAME;
+ snd_soc_card_cht.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ dev->driver->pm = &snd_soc_pm_ops;
+
+ ret_val = devm_snd_soc_register_card(dev, &snd_soc_card_cht);
if (ret_val) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"snd_soc_register_card failed %d\n", ret_val);
return ret_val;
}
@@ -329,11 +618,21 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
return ret_val;
}
+static void snd_cht_mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
+ clk_disable_unprepare(ctx->mclk);
+}
+
static struct platform_driver snd_cht_mc_driver = {
.driver = {
.name = "cht-bsw-max98090",
},
.probe = snd_cht_mc_probe,
+ .remove = snd_cht_mc_remove,
};
module_platform_driver(snd_cht_mc_driver)
diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c
new file mode 100644
index 000000000000..4afb292d4f13
--- /dev/null
+++ b/sound/soc/intel/boards/cht_bsw_nau8824.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cht-bsw-nau8824.c - ASoc Machine driver for Intel Cherryview-based
+ * platforms Cherrytrail and Braswell, with nau8824 codec.
+ *
+ * Copyright (C) 2018 Intel Corp
+ * Copyright (C) 2018 Nuvoton Technology Corp
+ *
+ * Author: Wang, Joseph C <joequant@gmail.com>
+ * Co-author: John Hsu <KCHSU0@nuvoton.com>
+ * This file is based on cht_bsw_rt5672.c and cht-bsw-max98090.c
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/jack.h>
+#include <linux/input.h>
+#include "../atom/sst-atom-controls.h"
+#include "../../codecs/nau8824.h"
+
+struct cht_mc_private {
+ struct snd_soc_jack jack;
+};
+
+static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+ {"Ext Spk", NULL, "SPKOUTL"},
+ {"Ext Spk", NULL, "SPKOUTR"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"MIC1", NULL, "Int Mic"},
+ {"MIC2", NULL, "Int Mic"},
+ {"HSMIC1", NULL, "Headset Mic"},
+ {"HSMIC2", NULL, "Headset Mic"},
+ {"Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx" },
+ {"codec_in1", NULL, "ssp2 Rx" },
+ {"ssp2 Rx", NULL, "Capture"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8824_CLK_FLL_FS, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params),
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_jack *jack = &ctx->jack;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret, jack_type;
+
+ /* NAU88L24 supports 4 buttons headset detection
+ * KEY_PLAYPAUSE
+ * KEY_VOICECOMMAND
+ * KEY_VOLUMEUP
+ * KEY_VOLUMEDOWN
+ */
+ jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,
+ jack, cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
+ if (ret) {
+ dev_err(runtime->dev,
+ "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ nau8824_enable_jack_detect(component, jack);
+
+ return ret;
+}
+
+static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt =
+ hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ int ret;
+
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ snd_mask_none(fmt);
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), 0xf, 0x1, 4, 24);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static const struct snd_soc_ops cht_aif1_ops = {
+ .startup = cht_aif1_startup,
+};
+
+static const struct snd_soc_ops cht_be_ssp2_ops = {
+ .hw_params = cht_aif1_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508824:00",
+ NAU8824_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
+static struct snd_soc_dai_link cht_dailink[] = {
+ /* Front End DAI links */
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .ops = &cht_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .nonatomic = true,
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &cht_aif1_ops,
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
+ },
+ /* Back End DAI links */
+ {
+ /* SSP2 - Codec */
+ .name = "SSP2-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .init = cht_codec_init,
+ .be_hw_params_fixup = cht_codec_fixup,
+ .ops = &cht_be_ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
+ },
+};
+
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht nau8824" /* card name will be 'sof-bytcht nau8824 */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "chtnau8824"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+ .owner = THIS_MODULE,
+ .dai_link = cht_dailink,
+ .num_links = ARRAY_SIZE(cht_dailink),
+ .dapm_widgets = cht_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+ .dapm_routes = cht_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+ .controls = cht_mc_controls,
+ .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+ struct cht_mc_private *drv;
+ struct snd_soc_acpi_mach *mach;
+ const char *platform_name;
+ bool sof_parent;
+ int ret_val;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
+
+ /* override platform name, if required */
+ snd_soc_card_cht.dev = &pdev->dev;
+ mach = pdev->dev.platform_data;
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
+ platform_name);
+ if (ret_val)
+ return ret_val;
+
+ sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ snd_soc_card_cht.name = SOF_CARD_NAME;
+ snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;
+ } else {
+ snd_soc_card_cht.name = CARD_NAME;
+ snd_soc_card_cht.driver_name = DRIVER_NAME;
+ }
+
+ snd_soc_card_cht.components = nau8824_components();
+
+ /* set pm ops */
+ if (sof_parent)
+ pdev->dev.driver->pm = &snd_soc_pm_ops;
+
+ /* register the soc card */
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_cht);
+
+ return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+ .driver = {
+ .name = "cht-bsw-nau8824",
+ },
+ .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Wang, Joseph C <joequant@gmail.com>");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-nau8824");
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 2d3afddb0a2e..249be121be15 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
* Cherrytrail and Braswell, with RT5645 codec.
@@ -8,31 +9,27 @@
* This file is modified from cht_bsw_rt5672.c
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
- * 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/acpi.h>
#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/dmi.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/soc-acpi.h>
#include "../../codecs/rt5645.h"
#include "../atom/sst-atom-controls.h"
+#include "../common/soc-intel-quirks.h"
#define CHT_PLAT_CLK_3_HZ 19200000
-#define CHT_CODEC_DAI "rt5645-aif1"
+#define CHT_CODEC_DAI1 "rt5645-aif1"
+#define CHT_CODEC_DAI2 "rt5645-aif2"
struct cht_acpi_card {
char *codec_id;
@@ -43,47 +40,67 @@ struct cht_acpi_card {
struct cht_mc_private {
struct snd_soc_jack jack;
struct cht_acpi_card *acpi_card;
+ struct clk *mclk;
};
-static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
+#define CHT_RT5645_MAP(quirk) ((quirk) & GENMASK(7, 0))
+#define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */
+#define CHT_RT5645_SSP0_AIF1 BIT(17)
+#define CHT_RT5645_SSP0_AIF2 BIT(18)
+#define CHT_RT5645_PMC_PLT_CLK_0 BIT(19)
- list_for_each_entry(rtd, &card->rtd_list, list) {
- if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
- strlen(CHT_CODEC_DAI)))
- return rtd->codec_dai;
- }
- return NULL;
+static unsigned long cht_rt5645_quirk = 0;
+
+static void log_quirks(struct device *dev)
+{
+ if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2)
+ dev_info(dev, "quirk SSP2_AIF2 enabled");
+ if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1)
+ dev_info(dev, "quirk SSP0_AIF1 enabled");
+ if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)
+ dev_info(dev, "quirk SSP0_AIF2 enabled");
+ if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0)
+ dev_info(dev, "quirk PMC_PLT_CLK_0 enabled");
}
static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
int ret;
- codec_dai = cht_get_codec_dai(card);
+ codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI1);
+ if (!codec_dai)
+ codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI2);
+
if (!codec_dai) {
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
return -EIO;
}
- if (!SND_SOC_DAPM_EVENT_OFF(event))
- return 0;
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = clk_prepare_enable(ctx->mclk);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ } else {
+ /* Set codec sysclk source to its internal clock because codec PLL will
+ * be off when idle and MCLK will also be off when codec is
+ * runtime suspended. Codec needs clock for jack detection and button
+ * press. MCLK is turned off with clock framework or ACPI.
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
+ 48000 * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
- /* Set codec sysclk source to its internal clock because codec PLL will
- * be off when idle and MCLK will also be off by ACPI when codec is
- * runtime suspended. Codec needs clock for jack detection and button
- * press.
- */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
- 0, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
- return ret;
+ clk_disable_unprepare(ctx->mclk);
}
return 0;
@@ -93,9 +110,10 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Analog Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
- platform_clock_control, SND_SOC_DAPM_POST_PMD),
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
@@ -103,19 +121,18 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
{"IN1N", NULL, "Headset Mic"},
{"DMIC L1", NULL, "Int Mic"},
{"DMIC R1", NULL, "Int Mic"},
+ {"IN2P", NULL, "Int Analog Mic"},
+ {"IN2N", NULL, "Int Analog Mic"},
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
{"Ext Spk", NULL, "SPOL"},
{"Ext Spk", NULL, "SPOR"},
- {"AIF1 Playback", NULL, "ssp2 Tx"},
- {"ssp2 Tx", NULL, "codec_out0"},
- {"ssp2 Tx", NULL, "codec_out1"},
- {"codec_in0", NULL, "ssp2 Rx" },
- {"codec_in1", NULL, "ssp2 Rx" },
- {"ssp2 Rx", NULL, "AIF1 Capture"},
{"Headphone", NULL, "Platform Clock"},
{"Headset Mic", NULL, "Platform Clock"},
{"Int Mic", NULL, "Platform Clock"},
+ {"Int Analog Mic", NULL, "Platform Clock"},
+ {"Int Analog Mic", NULL, "micbias1"},
+ {"Int Analog Mic", NULL, "micbias2"},
{"Ext Spk", NULL, "Platform Clock"},
};
@@ -128,30 +145,68 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
{"Headphone", NULL, "HPOR"},
{"Ext Spk", NULL, "SPOL"},
{"Ext Spk", NULL, "SPOR"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = {
{"AIF1 Playback", NULL, "ssp2 Tx"},
{"ssp2 Tx", NULL, "codec_out0"},
{"ssp2 Tx", NULL, "codec_out1"},
{"codec_in0", NULL, "ssp2 Rx" },
{"codec_in1", NULL, "ssp2 Rx" },
{"ssp2 Rx", NULL, "AIF1 Capture"},
- {"Headphone", NULL, "Platform Clock"},
- {"Headset Mic", NULL, "Platform Clock"},
- {"Int Mic", NULL, "Platform Clock"},
- {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = {
+ {"AIF2 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx" },
+ {"codec_in1", NULL, "ssp2 Rx" },
+ {"ssp2 Rx", NULL, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = {
+ {"AIF1 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx" },
+ {"ssp0 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = {
+ {"AIF2 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx" },
+ {"ssp0 Rx", NULL, "AIF2 Capture"},
};
static const struct snd_kcontrol_new cht_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Analog Mic"),
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
+static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
@@ -172,28 +227,72 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int cht_rt5645_quirk_cb(const struct dmi_system_id *id)
+{
+ cht_rt5645_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id cht_rt5645_quirk_table[] = {
+ {
+ /* Strago family Chromebooks */
+ .callback = cht_rt5645_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_Strago"),
+ },
+ .driver_data = (void *)CHT_RT5645_PMC_PLT_CLK_0,
+ },
+ {
+ },
+};
+
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
{
- int ret;
- int jack_type;
- struct snd_soc_codec *codec = runtime->codec;
- struct snd_soc_dai *codec_dai = runtime->codec_dai;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ int jack_type;
+ int ret;
- /* Select clk_i2s1_asrc as ASRC clock source */
- rt5645_sel_asrc_clk_src(codec,
- RT5645_DA_STEREO_FILTER |
- RT5645_DA_MONO_L_FILTER |
- RT5645_DA_MONO_R_FILTER |
- RT5645_AD_STEREO_FILTER,
- RT5645_CLK_SEL_I2S1_ASRC);
+ if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) ||
+ (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
+ /* Select clk_i2s2_asrc as ASRC clock source */
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_DA_MONO_L_FILTER |
+ RT5645_DA_MONO_R_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S2_ASRC);
+ } else {
+ /* Select clk_i2s1_asrc as ASRC clock source */
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_DA_MONO_L_FILTER |
+ RT5645_DA_MONO_R_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ }
- /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
- if (ret < 0) {
- dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
- return ret;
+ if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ cht_rt5645_ssp2_aif2_map,
+ ARRAY_SIZE(cht_rt5645_ssp2_aif2_map));
+ } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ cht_rt5645_ssp0_aif1_map,
+ ARRAY_SIZE(cht_rt5645_ssp0_aif1_map));
+ } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ cht_rt5645_ssp0_aif2_map,
+ ARRAY_SIZE(cht_rt5645_ssp0_aif2_map));
+ } else {
+ ret = snd_soc_dapm_add_routes(dapm,
+ cht_rt5645_ssp2_aif1_map,
+ ARRAY_SIZE(cht_rt5645_ssp2_aif1_map));
}
+ if (ret)
+ return ret;
if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
@@ -202,15 +301,35 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
else
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
- ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
- jack_type, &ctx->jack,
- NULL, 0);
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,
+ &ctx->jack, cht_bsw_jack_pins,
+ ARRAY_SIZE(cht_bsw_jack_pins));
if (ret) {
dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
return ret;
}
- rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
+ rt5645_set_jack_detect(component, &ctx->jack, &ctx->jack, &ctx->jack);
+
+
+ /*
+ * The firmware might enable the clock at
+ * boot (this information may or may not
+ * be reflected in the enable clock register).
+ * To change the rate we must disable the clock
+ * first to cover these cases. Due to common
+ * clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(ctx->mclk);
+ if (!ret)
+ clk_disable_unprepare(ctx->mclk);
+
+ ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
+
+ if (ret)
+ dev_err(runtime->dev, "unable to set MCLK rate\n");
return ret;
}
@@ -218,17 +337,77 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
+ int ret;
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
- /* set SSP2 to 24-bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
+ (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
+
+ /* set SSP0 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 16-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP
+ );
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BC_FC
+ );
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+
+ } else {
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0),
+ SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_BC_FC);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to TDM %d\n", ret);
+ return ret;
+ }
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), 0xF, 0xF, 4, 24);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret);
+ return ret;
+ }
+ }
return 0;
}
@@ -238,72 +417,73 @@ static int cht_aif1_startup(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_RATE, 48000);
}
-static struct snd_soc_ops cht_aif1_ops = {
+static const struct snd_soc_ops cht_aif1_ops = {
.startup = cht_aif1_startup,
};
-static struct snd_soc_ops cht_be_ssp2_ops = {
+static const struct snd_soc_ops cht_be_ssp2_ops = {
.hw_params = cht_aif1_hw_params,
};
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5645:00", "rt5645-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
static struct snd_soc_dai_link cht_dailink[] = {
[MERR_DPCM_AUDIO] = {
.name = "Audio Port",
.stream_name = "Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &cht_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
},
[MERR_DPCM_DEEP_BUFFER] = {
.name = "Deep-Buffer Audio Port",
.stream_name = "Deep-Buffer Audio",
- .cpu_dai_name = "deepbuffer-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
.ops = &cht_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Compressed Port",
- .stream_name = "Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
},
/* CODEC<->CODEC link */
/* back ends */
{
.name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
+ .id = 0,
.no_pcm = 1,
- .codec_dai_name = "rt5645-aif1",
- .codec_name = "i2c-10EC5645:00",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
- .nonatomic = true,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &cht_be_ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
},
};
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_RT5645_NAME "bytcht rt5645" /* card name 'sof-bytcht rt5645' */
+#define SOF_CARD_RT5650_NAME "bytcht rt5650" /* card name 'sof-bytcht rt5650' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_RT5645_NAME "chtrt5645"
+#define CARD_RT5650_NAME "chtrt5650"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
/* SoC card */
static struct snd_soc_card snd_soc_card_chtrt5645 = {
- .name = "chtrt5645",
.owner = THIS_MODULE,
.dai_link = cht_dailink,
.num_links = ARRAY_SIZE(cht_dailink),
@@ -316,7 +496,6 @@ static struct snd_soc_card snd_soc_card_chtrt5645 = {
};
static struct snd_soc_card snd_soc_card_chtrt5650 = {
- .name = "chtrt5650",
.owner = THIS_MODULE,
.dai_link = cht_dailink,
.num_links = ARRAY_SIZE(cht_dailink),
@@ -329,47 +508,199 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
};
static struct cht_acpi_card snd_soc_cards[] = {
+ {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ {"10EC5648", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ {"10EC3270", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
};
-static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
+static char cht_rt5645_codec_name[SND_ACPI_I2C_ID_LEN];
+
+struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */
+ u64 aif_value; /* 1: AIF1, 2: AIF2 */
+ u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */
+};
static int snd_cht_mc_probe(struct platform_device *pdev)
{
- int ret_val = 0;
- int i;
- struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
+ struct snd_soc_acpi_mach *mach;
+ const char *platform_name;
+ struct cht_mc_private *drv;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+ bool sof_parent;
bool found = false;
- char codec_name[16];
+ bool is_bytcr = false;
+ int dai_index = 0;
+ int ret_val = 0;
+ int i;
+ const char *mclk_name;
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
+ mach = pdev->dev.platform_data;
+
for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
- if (ACPI_SUCCESS(acpi_get_devices(
- snd_soc_cards[i].codec_id,
- snd_acpi_codec_match,
- &found, NULL)) && found) {
+ if (acpi_dev_found(snd_soc_cards[i].codec_id) &&
+ (!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) {
dev_dbg(&pdev->dev,
"found codec %s\n", snd_soc_cards[i].codec_id);
card = snd_soc_cards[i].soc_card;
drv->acpi_card = &snd_soc_cards[i];
+ found = true;
break;
}
}
+
+ if (!found) {
+ dev_err(&pdev->dev, "No matching HID found in supported list\n");
+ return -ENODEV;
+ }
+
card->dev = &pdev->dev;
- sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+
/* set correct codec name */
- strcpy((char *)card->dai_link[2].codec_name, codec_name);
+ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
+ if (cht_dailink[i].num_codecs &&
+ !strcmp(cht_dailink[i].codecs->name,
+ "i2c-10EC5645:00")) {
+ dai_index = i;
+ break;
+ }
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ cht_dailink[dai_index].codecs->name = cht_rt5645_codec_name;
+ } else {
+ dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ /* acpi_get_first_physical_node() returns a borrowed ref, no need to deref */
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+
+ snd_soc_card_chtrt5645.components = rt5645_components(codec_dev);
+ snd_soc_card_chtrt5650.components = rt5645_components(codec_dev);
+
+ /*
+ * swap SSP0 if bytcr is detected
+ * (will be overridden if DMI quirk is detected)
+ */
+ if (soc_intel_is_byt()) {
+ if (mach->mach_params.acpi_ipc_irq_index == 0)
+ is_bytcr = true;
+ }
+
+ if (is_bytcr) {
+ /*
+ * Baytrail CR platforms may have CHAN package in BIOS, try
+ * to find relevant routing quirk based as done on Windows
+ * platforms. We have to read the information directly from the
+ * BIOS, at this stage the card is not created and the links
+ * with the codec driver/pdata are non-existent
+ */
+
+ struct acpi_chan_package chan_package = { 0 };
+
+ /* format specified: 2 64-bit integers */
+ struct acpi_buffer format = {sizeof("NN"), "NN"};
+ struct acpi_buffer state = {0, NULL};
+ struct snd_soc_acpi_package_context pkg_ctx;
+ bool pkg_found = false;
+
+ state.length = sizeof(chan_package);
+ state.pointer = &chan_package;
+
+ pkg_ctx.name = "CHAN";
+ pkg_ctx.length = 2;
+ pkg_ctx.format = &format;
+ pkg_ctx.state = &state;
+ pkg_ctx.data_valid = false;
+
+ pkg_found = snd_soc_acpi_find_package_from_hid(mach->id,
+ &pkg_ctx);
+ if (pkg_found) {
+ if (chan_package.aif_value == 1) {
+ dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
+ cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1;
+ } else if (chan_package.aif_value == 2) {
+ dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
+ cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
+ } else {
+ dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
+ pkg_found = false;
+ }
+ }
+
+ if (!pkg_found) {
+ /* no BIOS indications, assume SSP0-AIF2 connection */
+ cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
+ }
+ }
+
+ /* check quirks before creating card */
+ dmi_check_system(cht_rt5645_quirk_table);
+ log_quirks(&pdev->dev);
+
+ if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) ||
+ (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2))
+ cht_dailink[dai_index].codecs->dai_name = "rt5645-aif2";
+
+ if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
+ (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2))
+ cht_dailink[dai_index].cpus->dai_name = "ssp0-port";
+
+ /* override platform name, if required */
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(card,
+ platform_name);
+ if (ret_val)
+ return ret_val;
+
+ if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0)
+ mclk_name = "pmc_plt_clk_0";
+ else
+ mclk_name = "pmc_plt_clk_3";
+
+ drv->mclk = devm_clk_get(&pdev->dev, mclk_name);
+ if (IS_ERR(drv->mclk)) {
+ dev_err(&pdev->dev, "Failed to get MCLK from %s: %ld\n",
+ mclk_name, PTR_ERR(drv->mclk));
+ return PTR_ERR(drv->mclk);
+ }
+
snd_soc_card_set_drvdata(card, drv);
+
+ sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ snd_soc_card_chtrt5645.name = SOF_CARD_RT5645_NAME;
+ snd_soc_card_chtrt5645.driver_name = SOF_DRIVER_NAME;
+ snd_soc_card_chtrt5650.name = SOF_CARD_RT5650_NAME;
+ snd_soc_card_chtrt5650.driver_name = SOF_DRIVER_NAME;
+ } else {
+ snd_soc_card_chtrt5645.name = CARD_RT5645_NAME;
+ snd_soc_card_chtrt5645.driver_name = DRIVER_NAME;
+ snd_soc_card_chtrt5650.name = CARD_RT5650_NAME;
+ snd_soc_card_chtrt5650.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ pdev->dev.driver->pm = &snd_soc_pm_ops;
+
ret_val = devm_snd_soc_register_card(&pdev->dev, card);
if (ret_val) {
dev_err(&pdev->dev,
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 2e5347f8f96c..359723f2700e 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
* Cherrytrail and Braswell, with RT5672 codec.
@@ -5,32 +6,35 @@
* Copyright (C) 2014 Intel Corp
* Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
* Mengdong Lin <mengdong.lin@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.
*/
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/string.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/soc-acpi.h>
#include "../../codecs/rt5670.h"
#include "../atom/sst-atom-controls.h"
+#include "../common/soc-intel-quirks.h"
+
/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
#define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5670-aif1"
-static struct snd_soc_jack cht_bsw_headset;
+struct cht_mc_private {
+ struct snd_soc_jack headset;
+ char codec_name[SND_ACPI_I2C_ID_LEN];
+ struct clk *mclk;
+ bool use_ssp0;
+};
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
@@ -44,33 +48,30 @@ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
},
};
-static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
-
- list_for_each_entry(rtd, &card->rtd_list, list) {
- if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
- strlen(CHT_CODEC_DAI)))
- return rtd->codec_dai;
- }
- return NULL;
-}
-
static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
int ret;
- codec_dai = cht_get_codec_dai(card);
+ codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);
if (!codec_dai) {
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
return -EIO;
}
if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (ctx->mclk) {
+ ret = clk_prepare_enable(ctx->mclk);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ }
+
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
CHT_PLAT_CLK_3_HZ, 48000 * 512);
@@ -92,8 +93,15 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
* when codec is runtime suspended. Codec needs clock for jack
* detection and button press.
*/
- snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
- 48000 * 512, SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+ 48000 * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "failed to set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ if (ctx->mclk)
+ clk_disable_unprepare(ctx->mclk);
}
return 0;
}
@@ -119,16 +127,26 @@ static const struct snd_soc_dapm_route cht_audio_map[] = {
{"Ext Spk", NULL, "SPOLN"},
{"Ext Spk", NULL, "SPORP"},
{"Ext Spk", NULL, "SPORN"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_soc_dapm_route cht_audio_ssp0_map[] = {
+ {"AIF1 Playback", NULL, "ssp0 Tx"},
+ {"ssp0 Tx", NULL, "modem_out"},
+ {"modem_in", NULL, "ssp0 Rx"},
+ {"ssp0 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route cht_audio_ssp2_map[] = {
{"AIF1 Playback", NULL, "ssp2 Tx"},
{"ssp2 Tx", NULL, "codec_out0"},
{"ssp2 Tx", NULL, "codec_out1"},
{"codec_in0", NULL, "ssp2 Rx"},
{"codec_in1", NULL, "ssp2 Rx"},
{"ssp2 Rx", NULL, "AIF1 Capture"},
- {"Headphone", NULL, "Platform Clock"},
- {"Headset Mic", NULL, "Platform Clock"},
- {"Int Mic", NULL, "Platform Clock"},
- {"Ext Spk", NULL, "Platform Clock"},
};
static const struct snd_kcontrol_new cht_mc_controls[] = {
@@ -141,8 +159,8 @@ static const struct snd_kcontrol_new cht_mc_controls[] = {
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
@@ -164,25 +182,30 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const struct acpi_gpio_params headset_gpios = { 0, 0, false };
+
+static const struct acpi_gpio_mapping cht_rt5672_gpios[] = {
+ { "headset-gpios", &headset_gpios, 1 },
+ {},
+};
+
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
- struct snd_soc_dai *codec_dai = runtime->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(runtime->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
- /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
- if (ret < 0) {
- dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
- return ret;
- }
+ if (devm_acpi_dev_add_driver_gpios(component->dev, cht_rt5672_gpios))
+ dev_warn(runtime->dev, "Unable to add GPIO mapping table\n");
/* Select codec ASRC clock source to track I2S1 clock, because codec
* is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
* be supported by RT5672. Otherwise, ASRC will be disabled and cause
* noise.
*/
- rt5670_sel_asrc_clk_src(codec,
+ rt5670_sel_asrc_clk_src(component,
RT5670_DA_STEREO_FILTER
| RT5670_DA_MONO_L_FILTER
| RT5670_DA_MONO_R_FILTER
@@ -191,31 +214,109 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
| RT5670_AD_MONO_R_FILTER,
RT5670_CLK_SEL_I2S1_ASRC);
- ret = snd_soc_card_jack_new(runtime->card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset,
- cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins));
+ if (ctx->use_ssp0) {
+ ret = snd_soc_dapm_add_routes(dapm,
+ cht_audio_ssp0_map,
+ ARRAY_SIZE(cht_audio_ssp0_map));
+ } else {
+ ret = snd_soc_dapm_add_routes(dapm,
+ cht_audio_ssp2_map,
+ ARRAY_SIZE(cht_audio_ssp2_map));
+ }
+ if (ret)
+ return ret;
+
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &ctx->headset,
+ cht_bsw_headset_pins,
+ ARRAY_SIZE(cht_bsw_headset_pins));
if (ret)
return ret;
- rt5670_set_jack_detect(codec, &cht_bsw_headset);
+ snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+
+ rt5670_set_jack_detect(component, &ctx->headset);
+ if (ctx->mclk) {
+ /*
+ * The firmware might enable the clock at
+ * boot (this information may or may not
+ * be reflected in the enable clock register).
+ * To change the rate we must disable the clock
+ * first to cover these cases. Due to common
+ * clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(ctx->mclk);
+ if (!ret)
+ clk_disable_unprepare(ctx->mclk);
+
+ ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
+
+ if (ret) {
+ dev_err(runtime->dev, "unable to set MCLK rate\n");
+ return ret;
+ }
+ }
return 0;
}
static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret, bits;
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
- /* set SSP2 to 24-bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ if (ctx->use_ssp0) {
+ /* set SSP0 to 16-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ bits = 16;
+ } else {
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ bits = 24;
+ }
+
+ /*
+ * The default mode for the cpu-dai is TDM 4 slot. The default mode
+ * for the codec-dai is I2S. So we need to either set the cpu-dai to
+ * I2S mode to match the codec-dai, or set the codec-dai to TDM 4 slot
+ * (or program both to yet another mode).
+ * One board, the Lenovo Miix 2 10, uses not 1 but 2 codecs connected
+ * to SSP2. The second piggy-backed, output-only codec is inside the
+ * keyboard-dock (which has extra speakers). Unlike the main rt5672
+ * codec, we cannot configure this codec, it is hard coded to use
+ * 2 channel 24 bit I2S. For this to work we must use I2S mode on this
+ * board. Since we only support 2 channels anyways, there is no need
+ * for TDM on any cht-bsw-rt5672 designs. So we use I2S 2ch everywhere.
+ */
+ ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BP_FP);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+
return 0;
}
@@ -225,79 +326,76 @@ static int cht_aif1_startup(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_RATE, 48000);
}
-static struct snd_soc_ops cht_aif1_ops = {
+static const struct snd_soc_ops cht_aif1_ops = {
.startup = cht_aif1_startup,
};
-static struct snd_soc_ops cht_be_ssp2_ops = {
+static const struct snd_soc_ops cht_be_ssp2_ops = {
.hw_params = cht_aif1_hw_params,
};
+SND_SOC_DAILINK_DEF(dummy,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+SND_SOC_DAILINK_DEF(media,
+ DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(deepbuffer,
+ DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
+
+SND_SOC_DAILINK_DEF(ssp2_port,
+ DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
+SND_SOC_DAILINK_DEF(ssp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5670:00",
+ "rt5670-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
+
static struct snd_soc_dai_link cht_dailink[] = {
/* Front End DAI links */
[MERR_DPCM_AUDIO] = {
.name = "Audio Port",
.stream_name = "Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &cht_aif1_ops,
+ SND_SOC_DAILINK_REG(media, dummy, platform),
},
[MERR_DPCM_DEEP_BUFFER] = {
.name = "Deep-Buffer Audio Port",
.stream_name = "Deep-Buffer Audio",
- .cpu_dai_name = "deepbuffer-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
.nonatomic = true,
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
.ops = &cht_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Compressed Port",
- .stream_name = "Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
+ SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
},
/* Back End DAI links */
{
/* SSP2 - Codec */
.name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
+ .id = 0,
.no_pcm = 1,
- .nonatomic = true,
- .codec_dai_name = "rt5670-aif1",
- .codec_name = "i2c-10EC5670:00",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &cht_be_ssp2_ops,
+ SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
},
};
static int cht_suspend_pre(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
- dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
- rt5670_jack_suspend(codec);
+ for_each_card_components(card, component) {
+ if (!strncmp(component->name,
+ ctx->codec_name, sizeof(ctx->codec_name))) {
+
+ dev_dbg(component->dev, "disabling jack detect before going to suspend.\n");
+ rt5670_jack_suspend(component);
break;
}
}
@@ -306,12 +404,15 @@ static int cht_suspend_pre(struct snd_soc_card *card)
static int cht_resume_post(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ for_each_card_components(card, component) {
+ if (!strncmp(component->name,
+ ctx->codec_name, sizeof(ctx->codec_name))) {
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
- dev_dbg(codec->dev, "enabling jack detect for resume.\n");
- rt5670_jack_resume(codec);
+ dev_dbg(component->dev, "enabling jack detect for resume.\n");
+ rt5670_jack_resume(component);
break;
}
}
@@ -319,9 +420,15 @@ static int cht_resume_post(struct snd_soc_card *card)
return 0;
}
+/* use space before codec name to simplify card ID, and simplify driver name */
+#define SOF_CARD_NAME "bytcht rt5672" /* card name will be 'sof-bytcht rt5672' */
+#define SOF_DRIVER_NAME "SOF"
+
+#define CARD_NAME "cht-bsw-rt5672"
+#define DRIVER_NAME NULL /* card name will be used for driver name */
+
/* SoC card */
static struct snd_soc_card snd_soc_card_cht = {
- .name = "cherrytrailcraudio",
.owner = THIS_MODULE,
.dai_link = cht_dailink,
.num_links = ARRAY_SIZE(cht_dailink),
@@ -335,12 +442,89 @@ static struct snd_soc_card snd_soc_card_cht = {
.resume_post = cht_resume_post,
};
+#define RT5672_I2C_DEFAULT "i2c-10EC5670:00"
+
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
+ struct cht_mc_private *drv;
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ const char *platform_name;
+ struct acpi_device *adev;
+ bool sof_parent;
+ int dai_index = 0;
+ int i;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ strscpy(drv->codec_name, RT5672_I2C_DEFAULT, sizeof(drv->codec_name));
+
+ /* find index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
+ if (cht_dailink[i].num_codecs &&
+ !strcmp(cht_dailink[i].codecs->name, RT5672_I2C_DEFAULT)) {
+ dai_index = i;
+ break;
+ }
+ }
- /* register the soc card */
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(drv->codec_name, sizeof(drv->codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ cht_dailink[dai_index].codecs->name = drv->codec_name;
+ } else {
+ dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ acpi_dev_put(adev);
+
+ /* Use SSP0 on Bay Trail CR devices */
+ if (soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0) {
+ cht_dailink[dai_index].cpus->dai_name = "ssp0-port";
+ drv->use_ssp0 = true;
+ }
+
+ /* override platform name, if required */
snd_soc_card_cht.dev = &pdev->dev;
+ platform_name = mach->mach_params.platform;
+
+ ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
+ platform_name);
+ if (ret_val)
+ return ret_val;
+
+ snd_soc_card_cht.components = rt5670_components();
+
+ drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ if (IS_ERR(drv->mclk)) {
+ dev_err(&pdev->dev,
+ "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+ PTR_ERR(drv->mclk));
+ return PTR_ERR(drv->mclk);
+ }
+ snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
+
+ sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+
+ /* set card and driver name */
+ if (sof_parent) {
+ snd_soc_card_cht.name = SOF_CARD_NAME;
+ snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;
+ } else {
+ snd_soc_card_cht.name = CARD_NAME;
+ snd_soc_card_cht.driver_name = DRIVER_NAME;
+ }
+
+ /* set pm ops */
+ if (sof_parent)
+ pdev->dev.driver->pm = &snd_soc_pm_ops;
+
+ /* register the soc card */
ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
if (ret_val) {
dev_err(&pdev->dev,
diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c
new file mode 100644
index 000000000000..5c7b218f22b7
--- /dev/null
+++ b/sound/soc/intel/boards/ehl_rt5660.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020 Intel Corporation
+
+/*
+ * ehl_rt5660 - ASOC Machine driver for Elkhart Lake platforms
+ * with rt5660 codec
+ */
+
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+
+#include "hda_dsp_common.h"
+#include "../../codecs/rt5660.h"
+
+#define DUAL_CHANNEL 2
+#define HDMI_LINK_START 3
+#define HDMI_LINE_END 6
+#define NAME_SIZE 32
+#define IDISP_CODEC_MASK 0x4
+
+struct sof_card_private {
+ struct list_head hdmi_pcm_list;
+ bool idisp_codec;
+};
+
+static const struct snd_kcontrol_new rt5660_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ /* There are two MICBIAS in rt5660, each for one MIC */
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic2"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static const struct snd_soc_dapm_widget rt5660_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic2", NULL),
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+};
+
+static const struct snd_soc_dapm_route rt5660_map[] = {
+ {"Speaker", NULL, "SPO"},
+
+ {"Headset Mic", NULL, "MICBIAS1"},
+ {"Headset Mic2", NULL, "MICBIAS2"},
+
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN2P", NULL, "Headset Mic2"},
+
+ {"Line Out", NULL, "LOUTL"},
+ {"Line Out", NULL, "LOUTR"},
+
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+struct sof_hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ int device;
+};
+
+static int hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct sof_hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ /* dai_link id is 1:1 mapped to the PCM device */
+ pcm->device = rtd->dai_link->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+ return 0;
+}
+
+static int card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct sof_hdmi_pcm *pcm;
+
+ if (list_empty(&ctx->hdmi_pcm_list))
+ return -ENOENT;
+
+ if (!ctx->idisp_codec)
+ return 0;
+
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
+
+ return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
+}
+
+static int rt5660_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT5660_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5660_PLL1_S_BCLK,
+ params_rate(params) * 50,
+ params_rate(params) * 512);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops rt5660_ops = {
+ .hw_params = rt5660_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(ssp0_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
+
+SND_SOC_DAILINK_DEF(rt5660_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5660:00", "rt5660-aif1")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));
+
+SND_SOC_DAILINK_DEF(dmic_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+SND_SOC_DAILINK_DEF(dmic16k,
+ DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));
+
+SND_SOC_DAILINK_DEF(idisp1_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));
+SND_SOC_DAILINK_DEF(idisp1_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));
+
+SND_SOC_DAILINK_DEF(idisp2_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));
+SND_SOC_DAILINK_DEF(idisp2_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));
+
+SND_SOC_DAILINK_DEF(idisp3_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));
+SND_SOC_DAILINK_DEF(idisp3_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));
+
+SND_SOC_DAILINK_DEF(idisp4_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin")));
+SND_SOC_DAILINK_DEF(idisp4_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4")));
+
+static struct snd_soc_dai_link ehl_rt5660_dailink[] = {
+ /* back ends */
+ {
+ .name = "SSP0-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .ops = &rt5660_ops,
+ SND_SOC_DAILINK_REG(ssp0_pin, rt5660_codec, platform),
+ },
+ {
+ .name = "dmic48k",
+ .id = 1,
+ .ignore_suspend = 1,
+ .capture_only = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
+ },
+ {
+ .name = "dmic16k",
+ .id = 2,
+ .ignore_suspend = 1,
+ .capture_only = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(dmic16k, dmic_codec, platform),
+ },
+ {
+ .name = "iDisp1",
+ .id = 5,
+ .init = hdmi_init,
+ .playback_only = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform),
+ },
+ {
+ .name = "iDisp2",
+ .id = 6,
+ .init = hdmi_init,
+ .playback_only = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform),
+ },
+ {
+ .name = "iDisp3",
+ .id = 7,
+ .init = hdmi_init,
+ .playback_only = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform),
+ },
+ {
+ .name = "iDisp4",
+ .id = 8,
+ .init = hdmi_init,
+ .playback_only = 1,
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform),
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_ehl_rt5660 = {
+ .name = "ehl-rt5660",
+ .owner = THIS_MODULE,
+ .dai_link = ehl_rt5660_dailink,
+ .num_links = ARRAY_SIZE(ehl_rt5660_dailink),
+ .dapm_widgets = rt5660_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5660_widgets),
+ .dapm_routes = rt5660_map,
+ .num_dapm_routes = ARRAY_SIZE(rt5660_map),
+ .controls = rt5660_controls,
+ .num_controls = ARRAY_SIZE(rt5660_controls),
+ .fully_routed = true,
+ .late_probe = card_late_probe,
+};
+
+/* If hdmi codec is not supported, switch to use dummy codec */
+static void hdmi_link_init(struct snd_soc_card *card,
+ struct sof_card_private *ctx,
+ struct snd_soc_acpi_mach *mach)
+{
+ int i;
+
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) {
+ ctx->idisp_codec = true;
+ return;
+ }
+
+ /*
+ * if HDMI is not enabled in kernel config, or
+ * hdmi codec is not supported
+ */
+ for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++)
+ card->dai_link[i].codecs[0] = snd_soc_dummy_dlc;
+}
+
+static int snd_ehl_rt5660_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_card *card = &snd_soc_card_ehl_rt5660;
+ struct sof_card_private *ctx;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+ snd_soc_card_set_drvdata(card, ctx);
+
+ mach = pdev->dev.platform_data;
+ ret = snd_soc_fixup_dai_links_platform_name(card,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ hdmi_link_init(card, ctx, mach);
+
+ return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static const struct platform_device_id ehl_board_ids[] = {
+ { .name = "ehl_rt5660" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ehl_board_ids);
+
+static struct platform_driver snd_ehl_rt5660_driver = {
+ .driver = {
+ .name = "ehl_rt5660",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = snd_ehl_rt5660_probe,
+ .id_table = ehl_board_ids,
+};
+
+module_platform_driver(snd_ehl_rt5660_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Elkhartlake + rt5660 Machine driver");
+MODULE_AUTHOR("libin.yang@intel.com");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("SND_SOC_INTEL_HDA_DSP_COMMON");
diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c
deleted file mode 100644
index 22558572cb9c..000000000000
--- a/sound/soc/intel/boards/haswell.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Intel Haswell Lynxpoint SST Audio
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "../common/sst-dsp.h"
-#include "../haswell/sst-haswell-ipc.h"
-
-#include "../../codecs/rt5640.h"
-
-/* Haswell ULT platforms have a Headphone and Mic jack */
-static const struct snd_soc_dapm_widget haswell_widgets[] = {
- SND_SOC_DAPM_HP("Headphones", NULL),
- SND_SOC_DAPM_MIC("Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route haswell_rt5640_map[] = {
-
- {"Headphones", NULL, "HPOR"},
- {"Headphones", NULL, "HPOL"},
- {"IN2P", NULL, "Mic"},
-
- /* CODEC BE connections */
- {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
- {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
-};
-
-static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The ADSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 16 bit */
- params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
- return 0;
-}
-
-static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
- SND_SOC_CLOCK_IN);
-
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec sysclk configuration\n");
- return ret;
- }
-
- /* set correct codec filter for DAI format and clock config */
- snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000);
-
- return ret;
-}
-
-static struct snd_soc_ops haswell_rt5640_ops = {
- .hw_params = haswell_rt5640_hw_params,
-};
-
-static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
- struct sst_hsw *haswell = pdata->dsp;
- int ret;
-
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
- if (ret < 0) {
- dev_err(rtd->dev, "failed to set device config\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_dai_link haswell_rt5640_dais[] = {
- /* Front End DAI links */
- {
- .name = "System",
- .stream_name = "System Playback/Capture",
- .cpu_dai_name = "System Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .init = haswell_rtd_init,
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "Offload0",
- .stream_name = "Offload0 Playback",
- .cpu_dai_name = "Offload0 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Offload1",
- .stream_name = "Offload1 Playback",
- .cpu_dai_name = "Offload1 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Loopback",
- .stream_name = "Loopback",
- .cpu_dai_name = "Loopback Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 0,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_capture = 1,
- },
-
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "Codec",
- .be_id = 0,
- .cpu_dai_name = "snd-soc-dummy-dai",
- .platform_name = "snd-soc-dummy",
- .no_pcm = 1,
- .codec_name = "i2c-INT33CA:00",
- .codec_dai_name = "rt5640-aif1",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_suspend = 1,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = haswell_ssp0_fixup,
- .ops = &haswell_rt5640_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
-};
-
-/* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
-static struct snd_soc_card haswell_rt5640 = {
- .name = "haswell-rt5640",
- .owner = THIS_MODULE,
- .dai_link = haswell_rt5640_dais,
- .num_links = ARRAY_SIZE(haswell_rt5640_dais),
- .dapm_widgets = haswell_widgets,
- .num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
- .dapm_routes = haswell_rt5640_map,
- .num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
- .fully_routed = true,
-};
-
-static int haswell_audio_probe(struct platform_device *pdev)
-{
- haswell_rt5640.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
-}
-
-static struct platform_driver haswell_audio = {
- .probe = haswell_audio_probe,
- .driver = {
- .name = "haswell-audio",
- },
-};
-
-module_platform_driver(haswell_audio)
-
-/* Module information */
-MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
-MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:haswell-audio");
diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c
new file mode 100644
index 000000000000..328ffff336a8
--- /dev/null
+++ b/sound/soc/intel/boards/hda_dsp_common.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2019 Intel Corporation
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include "../../codecs/hdac_hda.h"
+
+#include "hda_dsp_common.h"
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+
+/*
+ * Search card topology and return PCM device number
+ * matching Nth playback HDMI device (zero-based index).
+ */
+static struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card,
+ int hdmi_idx)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_pcm *spcm;
+ int i = 0;
+
+ for_each_card_rtds(card, rtd) {
+ /* ignore BE PCMs */
+ if (rtd->dai_link && rtd->dai_link->no_pcm)
+ continue;
+
+ spcm = rtd->pcm;
+
+ /* ignore PCMs with no playback streams */
+ if (!spcm || !spcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+ continue;
+
+ /* look for FE PCMs with name "HDMI x" */
+ if (spcm && strstr(spcm->id, "HDMI")) {
+ if (i == hdmi_idx)
+ return rtd->pcm;
+ ++i;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Search card topology and register HDMI PCM related controls
+ * to codec driver.
+ */
+int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
+ struct snd_soc_component *comp)
+{
+ struct hdac_hda_priv *hda_pvt;
+ struct hda_codec *hcodec;
+ struct snd_pcm *spcm;
+ struct hda_pcm *hpcm;
+ int err = 0, i = 0;
+
+ if (!comp)
+ return -EINVAL;
+
+ hda_pvt = snd_soc_component_get_drvdata(comp);
+ hcodec = hda_pvt->codec;
+
+ list_for_each_entry(hpcm, &hcodec->pcm_list_head, list) {
+ spcm = hda_dsp_hdmi_pcm_handle(card, i);
+ if (spcm) {
+ hpcm->pcm = spcm;
+ hpcm->device = spcm->device;
+ dev_dbg(card->dev,
+ "mapping HDMI converter %d to PCM %d (%p)\n",
+ i, hpcm->device, spcm);
+ } else {
+ hpcm->pcm = NULL;
+ hpcm->device = SNDRV_PCM_INVALID_DEVICE;
+ dev_warn(card->dev,
+ "%s: no PCM in topology for HDMI converter %d\n",
+ __func__, i);
+ }
+ i++;
+ }
+ snd_hdac_display_power(hcodec->core.bus,
+ HDA_CODEC_IDX_CONTROLLER, true);
+ err = snd_hda_codec_build_controls(hcodec);
+ if (err < 0)
+ dev_err(card->dev, "unable to create controls %d\n", err);
+ snd_hdac_display_power(hcodec->core.bus,
+ HDA_CODEC_IDX_CONTROLLER, false);
+
+ return err;
+}
+EXPORT_SYMBOL_NS(hda_dsp_hdmi_build_controls, "SND_SOC_INTEL_HDA_DSP_COMMON");
+
+#endif
+
+MODULE_DESCRIPTION("ASoC Intel HDMI helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/hda_dsp_common.h b/sound/soc/intel/boards/hda_dsp_common.h
new file mode 100644
index 000000000000..ea4ae9285cf0
--- /dev/null
+++ b/sound/soc/intel/boards/hda_dsp_common.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2019 Intel Corporation.
+ */
+
+/*
+ * This file defines helper functions used by multiple
+ * Intel HDA based machine drivers.
+ */
+
+#ifndef __HDA_DSP_COMMON_H
+#define __HDA_DSP_COMMON_H
+
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include "../../codecs/hdac_hda.h"
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
+ struct snd_soc_component *comp);
+#else
+static inline int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
+ struct snd_soc_component *comp)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* __HDA_DSP_COMMON_H */
diff --git a/sound/soc/intel/boards/hsw_rt5640.c b/sound/soc/intel/boards/hsw_rt5640.c
new file mode 100644
index 000000000000..9bb2822ba63e
--- /dev/null
+++ b/sound/soc/intel/boards/hsw_rt5640.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Sound card driver for Intel Haswell Lynx Point with Realtek 5640
+ *
+ * Copyright (C) 2013, Intel Corporation
+ */
+
+#include <linux/module.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-acpi.h>
+#include "../../codecs/rt5640.h"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ {"Headphones", NULL, "HPOR"},
+ {"Headphones", NULL, "HPOL"},
+ {"IN2P", NULL, "Mic"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static int codec_link_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ /* The ADSP will convert the FE rate to 48k, stereo. */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ /* Set SSP0 to 16 bit. */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int codec_link_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Set correct codec filter for DAI format and clock config. */
+ snd_soc_component_update_bits(codec_dai->component, 0x83, 0xffff, 0x8000);
+
+ return ret;
+}
+
+static const struct snd_soc_ops codec_link_ops = {
+ .hw_params = codec_link_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(system, DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
+SND_SOC_DAILINK_DEF(offload0, DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
+SND_SOC_DAILINK_DEF(offload1, DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
+SND_SOC_DAILINK_DEF(loopback, DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
+
+SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
+SND_SOC_DAILINK_DEF(codec, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT33CA:00", "rt5640-aif1")));
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
+SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
+
+static struct snd_soc_dai_link card_dai_links[] = {
+ /* Front End DAI links */
+ {
+ .name = "System",
+ .stream_name = "System Playback/Capture",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ SND_SOC_DAILINK_REG(system, dummy, platform),
+ },
+ {
+ .name = "Offload0",
+ .stream_name = "Offload0 Playback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(offload0, dummy, platform),
+ },
+ {
+ .name = "Offload1",
+ .stream_name = "Offload1 Playback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(offload1, dummy, platform),
+ },
+ {
+ .name = "Loopback",
+ .stream_name = "Loopback",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(loopback, dummy, platform),
+ },
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .id = 0,
+ .nonatomic = 1,
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = codec_link_hw_params_fixup,
+ .ops = &codec_link_ops,
+ SND_SOC_DAILINK_REG(ssp0_port, codec, platform),
+ },
+};
+
+static struct snd_soc_card hsw_rt5640_card = {
+ .name = "haswell-rt5640",
+ .owner = THIS_MODULE,
+ .dai_link = card_dai_links,
+ .num_links = ARRAY_SIZE(card_dai_links),
+ .dapm_widgets = card_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(card_widgets),
+ .dapm_routes = card_routes,
+ .num_dapm_routes = ARRAY_SIZE(card_routes),
+ .fully_routed = true,
+};
+
+static int hsw_rt5640_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ hsw_rt5640_card.dev = dev;
+ mach = dev_get_platdata(dev);
+
+ ret = snd_soc_fixup_dai_links_platform_name(&hsw_rt5640_card, mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_card(dev, &hsw_rt5640_card);
+}
+
+static struct platform_driver hsw_rt5640_driver = {
+ .probe = hsw_rt5640_probe,
+ .driver = {
+ .name = "hsw_rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(hsw_rt5640_driver)
+
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Sound card driver for Intel Haswell Lynx Point with Realtek 5640");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hsw_rt5640");
diff --git a/sound/soc/intel/boards/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c
deleted file mode 100644
index 49c09a0add79..000000000000
--- a/sound/soc/intel/boards/mfld_machine.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
- *
- * 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/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../codecs/sn95031.h"
-
-#define MID_MONO 1
-#define MID_STEREO 2
-#define MID_MAX_CAP 5
-#define MFLD_JACK_INSERT 0x04
-
-enum soc_mic_bias_zones {
- MFLD_MV_START = 0,
- /* mic bias volutage range for Headphones*/
- MFLD_MV_HP = 400,
- /* mic bias volutage range for American Headset*/
- MFLD_MV_AM_HS = 650,
- /* mic bias volutage range for Headset*/
- MFLD_MV_HS = 2000,
- MFLD_MV_UNDEFINED,
-};
-
-static unsigned int hs_switch;
-static unsigned int lo_dac;
-static struct snd_soc_codec *mfld_codec;
-
-struct mfld_mc_private {
- void __iomem *int_base;
- u8 interrupt_status;
-};
-
-struct snd_soc_jack mfld_jack;
-
-/*Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin mfld_jack_pins[] = {
- {
- .pin = "Headphones",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "AMIC1",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-/* jack detection voltage zones */
-static struct snd_soc_jack_zone mfld_zones[] = {
- {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
- {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
-};
-
-/* sound card controls */
-static const char *headset_switch_text[] = {"Earpiece", "Headset"};
-
-static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
-
-static const struct soc_enum headset_enum =
- SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
-
-static const struct soc_enum lo_enum =
- SOC_ENUM_SINGLE_EXT(4, lo_text);
-
-static int headset_get_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = hs_switch;
- return 0;
-}
-
-static int headset_set_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_context *dapm = &card->dapm;
-
- if (ucontrol->value.integer.value[0] == hs_switch)
- return 0;
-
- snd_soc_dapm_mutex_lock(dapm);
-
- if (ucontrol->value.integer.value[0]) {
- pr_debug("hs_set HS path\n");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
- } else {
- pr_debug("hs_set EP path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
- }
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-
- hs_switch = ucontrol->value.integer.value[0];
-
- return 0;
-}
-
-static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm)
-{
- snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL");
- snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR");
- snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL");
- snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR");
- snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT");
- snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT");
- if (hs_switch) {
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
- } else {
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
- }
-}
-
-static int lo_get_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = lo_dac;
- return 0;
-}
-
-static int lo_set_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_context *dapm = &card->dapm;
-
- if (ucontrol->value.integer.value[0] == lo_dac)
- return 0;
-
- snd_soc_dapm_mutex_lock(dapm);
-
- /* we dont want to work with last state of lineout so just enable all
- * pins and then disable pins not required
- */
- lo_enable_out_pins(dapm);
-
- switch (ucontrol->value.integer.value[0]) {
- case 0:
- pr_debug("set vibra path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT");
- snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0);
- break;
-
- case 1:
- pr_debug("set hs path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22);
- break;
-
- case 2:
- pr_debug("set spkr path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL");
- snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44);
- break;
-
- case 3:
- pr_debug("set null path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL");
- snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66);
- break;
- }
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-
- lo_dac = ucontrol->value.integer.value[0];
- return 0;
-}
-
-static const struct snd_kcontrol_new mfld_snd_controls[] = {
- SOC_ENUM_EXT("Playback Switch", headset_enum,
- headset_get_switch, headset_set_switch),
- SOC_ENUM_EXT("Lineout Mux", lo_enum,
- lo_get_switch, lo_set_switch),
-};
-
-static const struct snd_soc_dapm_widget mfld_widgets[] = {
- SND_SOC_DAPM_HP("Headphones", NULL),
- SND_SOC_DAPM_MIC("Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route mfld_map[] = {
- {"Headphones", NULL, "HPOUTR"},
- {"Headphones", NULL, "HPOUTL"},
- {"Mic", NULL, "AMIC1"},
-};
-
-static void mfld_jack_check(unsigned int intr_status)
-{
- struct mfld_jack_data jack_data;
-
- if (!mfld_codec)
- return;
-
- jack_data.mfld_jack = &mfld_jack;
- jack_data.intr_id = intr_status;
-
- sn95031_jack_detection(mfld_codec, &jack_data);
- /* TODO: add american headset detection post gpiolib support */
-}
-
-static int mfld_init(struct snd_soc_pcm_runtime *runtime)
-{
- struct snd_soc_dapm_context *dapm = &runtime->card->dapm;
- int ret_val;
-
- /* default is earpiece pin, userspace sets it explcitly */
- snd_soc_dapm_disable_pin(dapm, "Headphones");
- /* default is lineout NC, userspace sets it explcitly */
- snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
- snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
- lo_dac = 3;
- hs_switch = 0;
- /* we dont use linein in this so set to NC */
- snd_soc_dapm_disable_pin(dapm, "LINEINL");
- snd_soc_dapm_disable_pin(dapm, "LINEINR");
-
- /* Headset and button jack detection */
- ret_val = snd_soc_card_jack_new(runtime->card,
- "Intel(R) MID Audio Jack", SND_JACK_HEADSET |
- SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack,
- mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins));
- if (ret_val) {
- pr_err("jack creation failed\n");
- return ret_val;
- }
-
- ret_val = snd_soc_jack_add_zones(&mfld_jack,
- ARRAY_SIZE(mfld_zones), mfld_zones);
- if (ret_val) {
- pr_err("adding jack zones failed\n");
- return ret_val;
- }
-
- mfld_codec = runtime->codec;
-
- /* we want to check if anything is inserted at boot,
- * so send a fake event to codec and it will read adc
- * to find if anything is there or not */
- mfld_jack_check(MFLD_JACK_INSERT);
- return ret_val;
-}
-
-static struct snd_soc_dai_link mfld_msic_dailink[] = {
- {
- .name = "Medfield Headset",
- .stream_name = "Headset",
- .cpu_dai_name = "Headset-cpu-dai",
- .codec_dai_name = "SN95031 Headset",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = mfld_init,
- },
- {
- .name = "Medfield Speaker",
- .stream_name = "Speaker",
- .cpu_dai_name = "Speaker-cpu-dai",
- .codec_dai_name = "SN95031 Speaker",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
- {
- .name = "Medfield Vibra",
- .stream_name = "Vibra1",
- .cpu_dai_name = "Vibra1-cpu-dai",
- .codec_dai_name = "SN95031 Vibra1",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
- {
- .name = "Medfield Haptics",
- .stream_name = "Vibra2",
- .cpu_dai_name = "Vibra2-cpu-dai",
- .codec_dai_name = "SN95031 Vibra2",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
- {
- .name = "Medfield Compress",
- .stream_name = "Speaker",
- .cpu_dai_name = "Compress-cpu-dai",
- .codec_dai_name = "SN95031 Speaker",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
-};
-
-/* SoC card */
-static struct snd_soc_card snd_soc_card_mfld = {
- .name = "medfield_audio",
- .owner = THIS_MODULE,
- .dai_link = mfld_msic_dailink,
- .num_links = ARRAY_SIZE(mfld_msic_dailink),
-
- .controls = mfld_snd_controls,
- .num_controls = ARRAY_SIZE(mfld_snd_controls),
- .dapm_widgets = mfld_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mfld_widgets),
- .dapm_routes = mfld_map,
- .num_dapm_routes = ARRAY_SIZE(mfld_map),
-};
-
-static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
-{
- struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
-
- memcpy_fromio(&mc_private->interrupt_status,
- ((void *)(mc_private->int_base)),
- sizeof(u8));
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
-{
- struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
-
- mfld_jack_check(mc_drv_ctx->interrupt_status);
-
- return IRQ_HANDLED;
-}
-
-static int snd_mfld_mc_probe(struct platform_device *pdev)
-{
- int ret_val = 0, irq;
- struct mfld_mc_private *mc_drv_ctx;
- struct resource *irq_mem;
-
- pr_debug("snd_mfld_mc_probe called\n");
-
- /* retrive the irq number */
- irq = platform_get_irq(pdev, 0);
-
- /* audio interrupt base of SRAM location where
- * interrupts are stored by System FW */
- mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC);
- if (!mc_drv_ctx) {
- pr_err("allocation failed\n");
- return -ENOMEM;
- }
-
- irq_mem = platform_get_resource_byname(
- pdev, IORESOURCE_MEM, "IRQ_BASE");
- if (!irq_mem) {
- pr_err("no mem resource given\n");
- return -ENODEV;
- }
- mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start,
- resource_size(irq_mem));
- if (!mc_drv_ctx->int_base) {
- pr_err("Mapping of cache failed\n");
- return -ENOMEM;
- }
- /* register for interrupt */
- ret_val = devm_request_threaded_irq(&pdev->dev, irq,
- snd_mfld_jack_intr_handler,
- snd_mfld_jack_detection,
- IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
- if (ret_val) {
- pr_err("cannot register IRQ\n");
- return ret_val;
- }
- /* register the soc card */
- snd_soc_card_mfld.dev = &pdev->dev;
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld);
- if (ret_val) {
- pr_debug("snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
- platform_set_drvdata(pdev, mc_drv_ctx);
- pr_debug("successfully exited probe\n");
- return 0;
-}
-
-static struct platform_driver snd_mfld_mc_driver = {
- .driver = {
- .name = "msic_audio",
- },
- .probe = snd_mfld_mc_probe,
-};
-
-module_platform_driver(snd_mfld_mc_driver);
-
-MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
-MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
-MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:msic-audio");
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c
new file mode 100644
index 000000000000..519218385fdf
--- /dev/null
+++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2015-18 Intel Corporation.
+
+/*
+ * Machine Driver for SKL+ platforms with DSP and iDisp, HDA Codecs
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/hdac_hda.h"
+#include "../../sof/intel/hda.h"
+#include "sof_board_helpers.h"
+
+static int skl_hda_card_late_probe(struct snd_soc_card *card)
+{
+ return sof_intel_board_card_late_probe(card);
+}
+
+#define HDA_CODEC_AUTOSUSPEND_DELAY_MS 1000
+
+static void skl_set_hda_codec_autosuspend_delay(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_hda_priv *hda_pvt;
+ struct snd_soc_dai *dai;
+
+ for_each_card_rtds(card, rtd) {
+ if (!strstr(rtd->dai_link->codecs->name, "ehdaudio0D0"))
+ continue;
+ dai = snd_soc_rtd_to_codec(rtd, 0);
+ hda_pvt = snd_soc_component_get_drvdata(dai->component);
+ if (hda_pvt) {
+ /*
+ * all codecs are on the same bus, so it's sufficient
+ * to look up only the first one
+ */
+ snd_hda_set_power_save(hda_pvt->codec->bus,
+ HDA_CODEC_AUTOSUSPEND_DELAY_MS);
+ break;
+ }
+ }
+}
+
+#define IDISP_HDMI_BE_ID 1
+#define HDA_BE_ID 4
+#define DMIC01_BE_ID 6
+#define DMIC16K_BE_ID 7
+#define BT_OFFLOAD_BE_ID 8
+
+#define HDA_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_HDA, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_DMIC16K, \
+ SOF_LINK_BT_OFFLOAD, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE)
+
+#define HDA_LINK_IDS SOF_LINK_ORDER(IDISP_HDMI_BE_ID, \
+ HDA_BE_ID, \
+ DMIC01_BE_ID, \
+ DMIC16K_BE_ID, \
+ BT_OFFLOAD_BE_ID, \
+ 0, \
+ 0)
+
+static unsigned long
+skl_hda_get_board_quirk(struct snd_soc_acpi_mach_params *mach_params)
+{
+ unsigned long board_quirk = 0;
+ int ssp_bt;
+
+ if (hweight_long(mach_params->bt_link_mask) == 1) {
+ ssp_bt = fls(mach_params->bt_link_mask) - 1;
+ board_quirk |= SOF_SSP_PORT_BT_OFFLOAD(ssp_bt) |
+ SOF_BT_OFFLOAD_PRESENT;
+ }
+
+ return board_quirk;
+}
+
+static int skl_hda_add_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+
+ /* Ignore the HDMI PCM link if iDisp is not present */
+ if (strstr(link->stream_name, "HDMI") && !ctx->hdmi.idisp_codec)
+ link->ignore = true;
+
+ return 0;
+}
+
+static int skl_hda_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_card_private *ctx;
+ struct snd_soc_card *card;
+ unsigned long board_quirk = skl_hda_get_board_quirk(&mach->mach_params);
+ int ret;
+
+ card = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->name = "hda-dsp";
+ card->owner = THIS_MODULE;
+ card->fully_routed = true;
+ card->late_probe = skl_hda_card_late_probe;
+ card->add_dai_link = skl_hda_add_dai_link;
+
+ dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk);
+
+ /* initialize ctx with board quirk */
+ ctx = sof_intel_board_get_ctx(&pdev->dev, board_quirk);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (HDA_EXT_CODEC(mach->mach_params.codec_mask))
+ ctx->hda_codec_present = true;
+
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->hdmi.idisp_codec = true;
+
+ ctx->link_order_overwrite = HDA_LINK_ORDER;
+ ctx->link_id_overwrite = HDA_LINK_IDS;
+
+ /* update dai_link */
+ ret = sof_intel_board_set_dai_link(&pdev->dev, card, ctx);
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ if (mach->mach_params.dmic_num > 0) {
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "cfg-dmics:%d",
+ mach->mach_params.dmic_num);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ ret = snd_soc_fixup_dai_links_platform_name(card,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (!ret)
+ skl_set_hda_codec_autosuspend_delay(card);
+
+ return ret;
+}
+
+static struct platform_driver skl_hda_audio = {
+ .probe = skl_hda_audio_probe,
+ .driver = {
+ .name = "skl_hda_dsp_generic",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(skl_hda_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver");
+MODULE_AUTHOR("Rakesh Ughreja <rakesh.a.ughreja@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl_hda_dsp_generic");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
deleted file mode 100644
index ab7da9c304b2..000000000000
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Intel Skylake I2S Machine Driver with MAXIM98357A
- * and NAU88L25
- *
- * Copyright (C) 2015, Intel 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.
- *
- * 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/platform_device.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "../../codecs/nau8825.h"
-
-#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
-#define SKL_MAXIM_CODEC_DAI "HiFi"
-
-static struct snd_soc_jack skylake_headset;
-static struct snd_soc_card skylake_audio_card;
-
-static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
-
- list_for_each_entry(rtd, &card->rtd_list, list) {
-
- if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI,
- strlen(SKL_NUVOTON_CODEC_DAI)))
- return rtd->codec_dai;
- }
-
- return NULL;
-}
-
-static int platform_clock_control(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct snd_soc_dai *codec_dai;
- int ret;
-
- codec_dai = skl_get_codec_dai(card);
- if (!codec_dai) {
- dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
- return -EIO;
- }
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "set sysclk err = %d\n", ret);
- return -EIO;
- }
- } else {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "set sysclk err = %d\n", ret);
- return -EIO;
- }
- }
-
- return ret;
-}
-
-static const struct snd_kcontrol_new skylake_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Spk"),
-};
-
-static const struct snd_soc_dapm_widget skylake_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_SPK("Spk", NULL),
- SND_SOC_DAPM_MIC("SoC DMIC", NULL),
- SND_SOC_DAPM_SINK("WoV Sink"),
- SND_SOC_DAPM_SPK("DP", NULL),
- SND_SOC_DAPM_SPK("HDMI", NULL),
- SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
- platform_clock_control, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route skylake_map[] = {
- /* HP jack connectors - unknown if we have jack detection */
- { "Headphone Jack", NULL, "HPOL" },
- { "Headphone Jack", NULL, "HPOR" },
-
- /* speaker */
- { "Spk", NULL, "Speaker" },
-
- /* other jacks */
- { "MIC", NULL, "Headset Mic" },
- { "DMic", NULL, "SoC DMIC" },
-
- {"WoV Sink", NULL, "hwd_in sink"},
- {"HDMI", NULL, "hif5 Output"},
- {"DP", NULL, "hif6 Output"},
-
- /* CODEC BE connections */
- { "HiFi Playback", NULL, "ssp0 Tx" },
- { "ssp0 Tx", NULL, "codec0_out" },
-
- { "Playback", NULL, "ssp1 Tx" },
- { "ssp1 Tx", NULL, "codec1_out" },
-
- { "codec0_in", NULL, "ssp1 Rx" },
- { "ssp1 Rx", NULL, "Capture" },
-
- /* DMIC */
- { "dmic01_hifi", NULL, "DMIC01 Rx" },
- { "DMIC01 Rx", NULL, "DMIC AIF" },
- { "hifi1", NULL, "iDisp Tx"},
- { "iDisp Tx", NULL, "iDisp_out"},
- { "Headphone Jack", NULL, "Platform Clock" },
- { "Headset Mic", NULL, "Platform Clock" },
-};
-
-static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-
- /* The ADSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 24 bit */
- snd_mask_none(fmt);
- snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
-
- return 0;
-}
-
-static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- int ret;
- struct snd_soc_codec *codec = rtd->codec;
-
- /*
- * Headset buttons map to the google Reference headset.
- * These can be configured by userspace.
- */
- ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
- NULL, 0);
- if (ret) {
- dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
- return ret;
- }
-
- nau8825_enable_jack_detect(codec, &skylake_headset);
-
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
-
- return ret;
-}
-
-static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm;
- struct snd_soc_component *component = rtd->cpu_dai->component;
-
- dapm = snd_soc_component_get_dapm(component);
- snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
-
- return 0;
-}
-
-static unsigned int rates[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static unsigned int channels[] = {
- 2,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_channels = {
- .count = ARRAY_SIZE(channels),
- .list = channels,
- .mask = 0,
-};
-
-static int skl_fe_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- /*
- * On this platform for PCM device we support,
- * 48Khz
- * stereo
- * 16 bit audio
- */
-
- runtime->hw.channels_max = 2;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
-
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
- snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
-
- snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
-
- return 0;
-}
-
-static const struct snd_soc_ops skylake_nau8825_fe_ops = {
- .startup = skl_fe_startup,
-};
-
-static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
-
- if (ret < 0)
- dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
-
- return ret;
-}
-
-static struct snd_soc_ops skylake_nau8825_ops = {
- .hw_params = skylake_nau8825_hw_params,
-};
-
-static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- if (params_channels(params) == 2)
- channels->min = channels->max = 2;
- else
- channels->min = channels->max = 4;
-
- return 0;
-}
-
-static unsigned int channels_dmic[] = {
- 2, 4,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
- .count = ARRAY_SIZE(channels_dmic),
- .list = channels_dmic,
- .mask = 0,
-};
-
-static int skylake_dmic_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->hw.channels_max = 4;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
-
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
-}
-
-static struct snd_soc_ops skylake_dmic_ops = {
- .startup = skylake_dmic_startup,
-};
-
-static unsigned int rates_16000[] = {
- 16000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_16000 = {
- .count = ARRAY_SIZE(rates_16000),
- .list = rates_16000,
-};
-
-static int skylake_refcap_startup(struct snd_pcm_substream *substream)
-{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_16000);
-}
-
-static struct snd_soc_ops skylaye_refcap_ops = {
- .startup = skylake_refcap_startup,
-};
-
-/* skylake digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link skylake_dais[] = {
- /* Front End DAI links */
- {
- .name = "Skl Audio Port",
- .stream_name = "Audio",
- .cpu_dai_name = "System Pin",
- .platform_name = "0000:00:1f.3",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .nonatomic = 1,
- .init = skylake_nau8825_fe_init,
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- .ops = &skylake_nau8825_fe_ops,
- },
- {
- .name = "Skl Audio Capture Port",
- .stream_name = "Audio Record",
- .cpu_dai_name = "System Pin",
- .platform_name = "0000:00:1f.3",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .nonatomic = 1,
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_capture = 1,
- .ops = &skylake_nau8825_fe_ops,
- },
- {
- .name = "Skl Audio Reference cap",
- .stream_name = "Wake on Voice",
- .cpu_dai_name = "Reference Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .init = NULL,
- .dpcm_capture = 1,
- .ignore_suspend = 1,
- .nonatomic = 1,
- .dynamic = 1,
- .ops = &skylaye_refcap_ops,
- },
- {
- .name = "Skl Audio DMIC cap",
- .stream_name = "dmiccap",
- .cpu_dai_name = "DMIC Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .init = NULL,
- .dpcm_capture = 1,
- .nonatomic = 1,
- .dynamic = 1,
- .ops = &skylake_dmic_ops,
- },
- {
- .name = "Skl HDMI Port",
- .stream_name = "Hdmi",
- .cpu_dai_name = "HDMI Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .dpcm_playback = 1,
- .init = NULL,
- .nonatomic = 1,
- .dynamic = 1,
- },
-
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "SSP0-Codec",
- .be_id = 0,
- .cpu_dai_name = "SSP0 Pin",
- .platform_name = "0000:00:1f.3",
- .no_pcm = 1,
- .codec_name = "MX98357A:00",
- .codec_dai_name = SKL_MAXIM_CODEC_DAI,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = skylake_ssp_fixup,
- .dpcm_playback = 1,
- },
- {
- /* SSP1 - Codec */
- .name = "SSP1-Codec",
- .be_id = 0,
- .cpu_dai_name = "SSP1 Pin",
- .platform_name = "0000:00:1f.3",
- .no_pcm = 1,
- .codec_name = "i2c-10508825:00",
- .codec_dai_name = SKL_NUVOTON_CODEC_DAI,
- .init = skylake_nau8825_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = skylake_ssp_fixup,
- .ops = &skylake_nau8825_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "dmic01",
- .be_id = 1,
- .cpu_dai_name = "DMIC01 Pin",
- .codec_name = "dmic-codec",
- .codec_dai_name = "dmic-hifi",
- .platform_name = "0000:00:1f.3",
- .be_hw_params_fixup = skylake_dmic_fixup,
- .ignore_suspend = 1,
- .dpcm_capture = 1,
- .no_pcm = 1,
- },
- {
- .name = "iDisp",
- .be_id = 3,
- .cpu_dai_name = "iDisp Pin",
- .codec_name = "ehdaudio0D2",
- .codec_dai_name = "intel-hdmi-hifi1",
- .platform_name = "0000:00:1f.3",
- .dpcm_playback = 1,
- .no_pcm = 1,
- },
-};
-
-/* skylake audio machine driver for SPT + NAU88L25 */
-static struct snd_soc_card skylake_audio_card = {
- .name = "sklnau8825max",
- .owner = THIS_MODULE,
- .dai_link = skylake_dais,
- .num_links = ARRAY_SIZE(skylake_dais),
- .controls = skylake_controls,
- .num_controls = ARRAY_SIZE(skylake_controls),
- .dapm_widgets = skylake_widgets,
- .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
- .dapm_routes = skylake_map,
- .num_dapm_routes = ARRAY_SIZE(skylake_map),
- .fully_routed = true,
-};
-
-static int skylake_audio_probe(struct platform_device *pdev)
-{
- skylake_audio_card.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
-}
-
-static struct platform_driver skylake_audio = {
- .probe = skylake_audio_probe,
- .driver = {
- .name = "skl_nau88l25_max98357a_i2s",
- .pm = &snd_soc_pm_ops,
- },
-};
-
-module_platform_driver(skylake_audio)
-
-/* Module information */
-MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode");
-MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s");
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
deleted file mode 100644
index c071812f31e5..000000000000
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * Intel Skylake I2S Machine Driver for NAU88L25+SSM4567
- *
- * Copyright (C) 2015, Intel Corporation. All rights reserved.
- *
- * Modified from:
- * Intel Skylake I2S Machine Driver for NAU88L25 and SSM4567
- *
- * Copyright (C) 2015, Intel 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.
- *
- * 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/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <sound/pcm_params.h>
-#include "../../codecs/nau8825.h"
-
-#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
-#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
-
-static struct snd_soc_jack skylake_headset;
-static struct snd_soc_card skylake_audio_card;
-
-static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
-
- list_for_each_entry(rtd, &card->rtd_list, list) {
-
- if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI,
- strlen(SKL_NUVOTON_CODEC_DAI)))
- return rtd->codec_dai;
- }
-
- return NULL;
-}
-
-static const struct snd_kcontrol_new skylake_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Left Speaker"),
- SOC_DAPM_PIN_SWITCH("Right Speaker"),
-};
-
-static int platform_clock_control(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct snd_soc_dai *codec_dai;
- int ret;
-
- codec_dai = skl_get_codec_dai(card);
- if (!codec_dai) {
- dev_err(card->dev, "Codec dai not found\n");
- return -EIO;
- }
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "set sysclk err = %d\n", ret);
- return -EIO;
- }
- } else {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "set sysclk err = %d\n", ret);
- return -EIO;
- }
- }
- return ret;
-}
-
-static const struct snd_soc_dapm_widget skylake_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_SPK("Left Speaker", NULL),
- SND_SOC_DAPM_SPK("Right Speaker", NULL),
- SND_SOC_DAPM_MIC("SoC DMIC", NULL),
- SND_SOC_DAPM_SINK("WoV Sink"),
- SND_SOC_DAPM_SPK("DP", NULL),
- SND_SOC_DAPM_SPK("HDMI", NULL),
- SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
- platform_clock_control, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route skylake_map[] = {
- /* HP jack connectors - unknown if we have jack detection */
- {"Headphone Jack", NULL, "HPOL"},
- {"Headphone Jack", NULL, "HPOR"},
-
- /* speaker */
- {"Left Speaker", NULL, "Left OUT"},
- {"Right Speaker", NULL, "Right OUT"},
-
- /* other jacks */
- {"MIC", NULL, "Headset Mic"},
- {"DMic", NULL, "SoC DMIC"},
-
- {"WoV Sink", NULL, "hwd_in sink"},
-
- {"HDMI", NULL, "hif5 Output"},
- {"DP", NULL, "hif6 Output"},
- /* CODEC BE connections */
- { "Left Playback", NULL, "ssp0 Tx"},
- { "Right Playback", NULL, "ssp0 Tx"},
- { "ssp0 Tx", NULL, "codec0_out"},
-
- { "Playback", NULL, "ssp1 Tx"},
- { "ssp1 Tx", NULL, "codec1_out"},
-
- { "codec0_in", NULL, "ssp1 Rx" },
- { "ssp1 Rx", NULL, "Capture" },
-
- /* DMIC */
- { "dmic01_hifi", NULL, "DMIC01 Rx" },
- { "DMIC01 Rx", NULL, "DMIC AIF" },
- { "hifi1", NULL, "iDisp Tx"},
- { "iDisp Tx", NULL, "iDisp_out"},
- { "Headphone Jack", NULL, "Platform Clock" },
- { "Headset Mic", NULL, "Platform Clock" },
-};
-
-static struct snd_soc_codec_conf ssm4567_codec_conf[] = {
- {
- .dev_name = "i2c-INT343B:00",
- .name_prefix = "Left",
- },
- {
- .dev_name = "i2c-INT343B:01",
- .name_prefix = "Right",
- },
-};
-
-static struct snd_soc_dai_link_component ssm4567_codec_components[] = {
- { /* Left */
- .name = "i2c-INT343B:00",
- .dai_name = SKL_SSM_CODEC_DAI,
- },
- { /* Right */
- .name = "i2c-INT343B:01",
- .dai_name = SKL_SSM_CODEC_DAI,
- },
-};
-
-static int skylake_ssm4567_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- int ret;
-
- /* Slot 1 for left */
- ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x01, 0x01, 2, 48);
- if (ret < 0)
- return ret;
-
- /* Slot 2 for right */
- ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x02, 0x02, 2, 48);
- if (ret < 0)
- return ret;
-
- return ret;
-}
-
-static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- int ret;
- struct snd_soc_codec *codec = rtd->codec;
-
- /*
- * 4 buttons here map to the google Reference headset
- * The use of these buttons can be decided by the user space.
- */
- ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
- NULL, 0);
- if (ret) {
- dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
- return ret;
- }
-
- nau8825_enable_jack_detect(codec, &skylake_headset);
-
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
-
- return ret;
-}
-
-static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm;
- struct snd_soc_component *component = rtd->cpu_dai->component;
-
- dapm = snd_soc_component_get_dapm(component);
- snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
-
- return 0;
-}
-
-static unsigned int rates[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static unsigned int channels[] = {
- 2,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_channels = {
- .count = ARRAY_SIZE(channels),
- .list = channels,
- .mask = 0,
-};
-
-static int skl_fe_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- /*
- * on this platform for PCM device we support,
- * 48Khz
- * stereo
- * 16 bit audio
- */
-
- runtime->hw.channels_max = 2;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
-
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
- snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
-
- snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
-
- return 0;
-}
-
-static const struct snd_soc_ops skylake_nau8825_fe_ops = {
- .startup = skl_fe_startup,
-};
-
-static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-
- /* The ADSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 24 bit */
- snd_mask_none(fmt);
- snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
- return 0;
-}
-
-static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- if (params_channels(params) == 2)
- channels->min = channels->max = 2;
- else
- channels->min = channels->max = 4;
-
- return 0;
-}
-
-static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
-
- if (ret < 0)
- dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
-
- return ret;
-}
-
-static struct snd_soc_ops skylake_nau8825_ops = {
- .hw_params = skylake_nau8825_hw_params,
-};
-
-static unsigned int channels_dmic[] = {
- 2, 4,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
- .count = ARRAY_SIZE(channels_dmic),
- .list = channels_dmic,
- .mask = 0,
-};
-
-static int skylake_dmic_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->hw.channels_max = 4;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
-
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
-}
-
-static struct snd_soc_ops skylake_dmic_ops = {
- .startup = skylake_dmic_startup,
-};
-
-static unsigned int rates_16000[] = {
- 16000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_16000 = {
- .count = ARRAY_SIZE(rates_16000),
- .list = rates_16000,
-};
-
-static int skylake_refcap_startup(struct snd_pcm_substream *substream)
-{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_16000);
-}
-
-static struct snd_soc_ops skylaye_refcap_ops = {
- .startup = skylake_refcap_startup,
-};
-
-/* skylake digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link skylake_dais[] = {
- /* Front End DAI links */
- {
- .name = "Skl Audio Port",
- .stream_name = "Audio",
- .cpu_dai_name = "System Pin",
- .platform_name = "0000:00:1f.3",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .nonatomic = 1,
- .init = skylake_nau8825_fe_init,
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- .ops = &skylake_nau8825_fe_ops,
- },
- {
- .name = "Skl Audio Capture Port",
- .stream_name = "Audio Record",
- .cpu_dai_name = "System Pin",
- .platform_name = "0000:00:1f.3",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .nonatomic = 1,
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_capture = 1,
- .ops = &skylake_nau8825_fe_ops,
- },
- {
- .name = "Skl Audio Reference cap",
- .stream_name = "Wake on Voice",
- .cpu_dai_name = "Reference Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .init = NULL,
- .dpcm_capture = 1,
- .ignore_suspend = 1,
- .nonatomic = 1,
- .dynamic = 1,
- .ops = &skylaye_refcap_ops,
- },
- {
- .name = "Skl Audio DMIC cap",
- .stream_name = "dmiccap",
- .cpu_dai_name = "DMIC Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .init = NULL,
- .dpcm_capture = 1,
- .nonatomic = 1,
- .dynamic = 1,
- .ops = &skylake_dmic_ops,
- },
- {
- .name = "Skl HDMI Port",
- .stream_name = "Hdmi",
- .cpu_dai_name = "HDMI Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .dpcm_playback = 1,
- .init = NULL,
- .nonatomic = 1,
- .dynamic = 1,
- },
-
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "SSP0-Codec",
- .be_id = 0,
- .cpu_dai_name = "SSP0 Pin",
- .platform_name = "0000:00:1f.3",
- .no_pcm = 1,
- .codecs = ssm4567_codec_components,
- .num_codecs = ARRAY_SIZE(ssm4567_codec_components),
- .dai_fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .init = skylake_ssm4567_codec_init,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = skylake_ssp_fixup,
- .dpcm_playback = 1,
- },
- {
- /* SSP1 - Codec */
- .name = "SSP1-Codec",
- .be_id = 0,
- .cpu_dai_name = "SSP1 Pin",
- .platform_name = "0000:00:1f.3",
- .no_pcm = 1,
- .codec_name = "i2c-10508825:00",
- .codec_dai_name = SKL_NUVOTON_CODEC_DAI,
- .init = skylake_nau8825_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = skylake_ssp_fixup,
- .ops = &skylake_nau8825_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "dmic01",
- .be_id = 1,
- .cpu_dai_name = "DMIC01 Pin",
- .codec_name = "dmic-codec",
- .codec_dai_name = "dmic-hifi",
- .platform_name = "0000:00:1f.3",
- .ignore_suspend = 1,
- .be_hw_params_fixup = skylake_dmic_fixup,
- .dpcm_capture = 1,
- .no_pcm = 1,
- },
- {
- .name = "iDisp",
- .be_id = 3,
- .cpu_dai_name = "iDisp Pin",
- .codec_name = "ehdaudio0D2",
- .codec_dai_name = "intel-hdmi-hifi1",
- .platform_name = "0000:00:1f.3",
- .dpcm_playback = 1,
- .no_pcm = 1,
- },
-};
-
-/* skylake audio machine driver for SPT + NAU88L25 */
-static struct snd_soc_card skylake_audio_card = {
- .name = "sklnau8825adi",
- .owner = THIS_MODULE,
- .dai_link = skylake_dais,
- .num_links = ARRAY_SIZE(skylake_dais),
- .controls = skylake_controls,
- .num_controls = ARRAY_SIZE(skylake_controls),
- .dapm_widgets = skylake_widgets,
- .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
- .dapm_routes = skylake_map,
- .num_dapm_routes = ARRAY_SIZE(skylake_map),
- .codec_conf = ssm4567_codec_conf,
- .num_configs = ARRAY_SIZE(ssm4567_codec_conf),
- .fully_routed = true,
-};
-
-static int skylake_audio_probe(struct platform_device *pdev)
-{
- skylake_audio_card.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
-}
-
-static struct platform_driver skylake_audio = {
- .probe = skylake_audio_probe,
- .driver = {
- .name = "skl_nau88l25_ssm4567_i2s",
- .pm = &snd_soc_pm_ops,
- },
-};
-
-module_platform_driver(skylake_audio)
-
-/* Module information */
-MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
-MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
-MODULE_AUTHOR("Naveen M <naveen.m@intel.com>");
-MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
-MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
-MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s");
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
deleted file mode 100644
index 7396ddb427d8..000000000000
--- a/sound/soc/intel/boards/skl_rt286.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Intel Skylake I2S Machine Driver
- *
- * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
- *
- * Modified from:
- * Intel Broadwell Wildcatpoint SST Audio
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <sound/pcm_params.h>
-#include "../../codecs/rt286.h"
-
-static struct snd_soc_jack skylake_headset;
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin skylake_headset_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static const struct snd_kcontrol_new skylake_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speaker"),
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
- SOC_DAPM_PIN_SWITCH("Mic Jack"),
-};
-
-static const struct snd_soc_dapm_widget skylake_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_MIC("DMIC2", NULL),
- SND_SOC_DAPM_MIC("SoC DMIC", NULL),
- SND_SOC_DAPM_SINK("WoV Sink"),
-};
-
-static const struct snd_soc_dapm_route skylake_rt286_map[] = {
- /* speaker */
- {"Speaker", NULL, "SPOR"},
- {"Speaker", NULL, "SPOL"},
-
- /* HP jack connectors - unknown if we have jack deteck */
- {"Headphone Jack", NULL, "HPO Pin"},
-
- /* other jacks */
- {"MIC1", NULL, "Mic Jack"},
-
- /* digital mics */
- {"DMIC1 Pin", NULL, "DMIC2"},
- {"DMic", NULL, "SoC DMIC"},
-
- {"WoV Sink", NULL, "hwd_in sink"},
-
- /* CODEC BE connections */
- { "AIF1 Playback", NULL, "ssp0 Tx"},
- { "ssp0 Tx", NULL, "codec0_out"},
- { "ssp0 Tx", NULL, "codec1_out"},
-
- { "codec0_in", NULL, "ssp0 Rx" },
- { "codec1_in", NULL, "ssp0 Rx" },
- { "ssp0 Rx", NULL, "AIF1 Capture" },
-
- { "dmic01_hifi", NULL, "DMIC01 Rx" },
- { "DMIC01 Rx", NULL, "DMIC AIF" },
-
- { "hif1", NULL, "iDisp Tx"},
- { "iDisp Tx", NULL, "iDisp_out"},
-
-};
-
-static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm;
- struct snd_soc_component *component = rtd->cpu_dai->component;
-
- dapm = snd_soc_component_get_dapm(component);
- snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
-
- return 0;
-}
-
-static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- int ret;
-
- ret = snd_soc_card_jack_new(rtd->card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &skylake_headset,
- skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
-
- if (ret)
- return ret;
-
- rt286_mic_detect(codec, &skylake_headset);
-
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
-
- return 0;
-}
-
-static unsigned int rates[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static unsigned int channels[] = {
- 2,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_channels = {
- .count = ARRAY_SIZE(channels),
- .list = channels,
- .mask = 0,
-};
-
-static int skl_fe_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- /*
- * on this platform for PCM device we support,
- * 48Khz
- * stereo
- * 16 bit audio
- */
-
- runtime->hw.channels_max = 2;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
-
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
- snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
-
- snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
-
- return 0;
-}
-
-static const struct snd_soc_ops skylake_rt286_fe_ops = {
- .startup = skl_fe_startup,
-};
-
-static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-
- /* The output is 48KHz, stereo, 16bits */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 24 bit */
- snd_mask_none(fmt);
- snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
- return 0;
-}
-
-static int skylake_rt286_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
-
- return ret;
-}
-
-static struct snd_soc_ops skylake_rt286_ops = {
- .hw_params = skylake_rt286_hw_params,
-};
-
-static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- channels->min = channels->max = 4;
-
- return 0;
-}
-
-static unsigned int channels_dmic[] = {
- 2, 4,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
- .count = ARRAY_SIZE(channels_dmic),
- .list = channels_dmic,
- .mask = 0,
-};
-
-static int skylake_dmic_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->hw.channels_max = 4;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
-
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
-}
-
-static struct snd_soc_ops skylake_dmic_ops = {
- .startup = skylake_dmic_startup,
-};
-
-/* skylake digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link skylake_rt286_dais[] = {
- /* Front End DAI links */
- {
- .name = "Skl Audio Port",
- .stream_name = "Audio",
- .cpu_dai_name = "System Pin",
- .platform_name = "0000:00:1f.3",
- .nonatomic = 1,
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .init = skylake_rt286_fe_init,
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST
- },
- .dpcm_playback = 1,
- .ops = &skylake_rt286_fe_ops,
- },
- {
- .name = "Skl Audio Capture Port",
- .stream_name = "Audio Record",
- .cpu_dai_name = "System Pin",
- .platform_name = "0000:00:1f.3",
- .nonatomic = 1,
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST
- },
- .dpcm_capture = 1,
- .ops = &skylake_rt286_fe_ops,
- },
- {
- .name = "Skl Audio Reference cap",
- .stream_name = "refcap",
- .cpu_dai_name = "Reference Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .init = NULL,
- .dpcm_capture = 1,
- .ignore_suspend = 1,
- .nonatomic = 1,
- .dynamic = 1,
- },
- {
- .name = "Skl Audio DMIC cap",
- .stream_name = "dmiccap",
- .cpu_dai_name = "DMIC Pin",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .platform_name = "0000:00:1f.3",
- .init = NULL,
- .dpcm_capture = 1,
- .nonatomic = 1,
- .dynamic = 1,
- .ops = &skylake_dmic_ops,
- },
-
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "SSP0-Codec",
- .be_id = 0,
- .cpu_dai_name = "SSP0 Pin",
- .platform_name = "0000:00:1f.3",
- .no_pcm = 1,
- .codec_name = "i2c-INT343A:00",
- .codec_dai_name = "rt286-aif1",
- .init = skylake_rt286_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = skylake_ssp0_fixup,
- .ops = &skylake_rt286_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "dmic01",
- .be_id = 1,
- .cpu_dai_name = "DMIC01 Pin",
- .codec_name = "dmic-codec",
- .codec_dai_name = "dmic-hifi",
- .platform_name = "0000:00:1f.3",
- .be_hw_params_fixup = skylake_dmic_fixup,
- .ignore_suspend = 1,
- .dpcm_capture = 1,
- .no_pcm = 1,
- },
-};
-
-/* skylake audio machine driver for SPT + RT286S */
-static struct snd_soc_card skylake_rt286 = {
- .name = "skylake-rt286",
- .owner = THIS_MODULE,
- .dai_link = skylake_rt286_dais,
- .num_links = ARRAY_SIZE(skylake_rt286_dais),
- .controls = skylake_controls,
- .num_controls = ARRAY_SIZE(skylake_controls),
- .dapm_widgets = skylake_widgets,
- .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
- .dapm_routes = skylake_rt286_map,
- .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
- .fully_routed = true,
-};
-
-static int skylake_audio_probe(struct platform_device *pdev)
-{
- skylake_rt286.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
-}
-
-static struct platform_driver skylake_audio = {
- .probe = skylake_audio_probe,
- .driver = {
- .name = "skl_alc286s_i2s",
- .pm = &snd_soc_pm_ops,
- },
-};
-
-module_platform_driver(skylake_audio)
-
-/* Module information */
-MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
-MODULE_DESCRIPTION("Intel SST Audio for Skylake");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_alc286s_i2s");
diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c
new file mode 100644
index 000000000000..f741a1e142be
--- /dev/null
+++ b/sound/soc/intel/boards/sof_board_helpers.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2023 Intel Corporation
+
+#include <sound/soc.h>
+#include "../common/soc-intel-quirks.h"
+#include "hda_dsp_common.h"
+#include "sof_board_helpers.h"
+
+/*
+ * Intel HDMI DAI Link
+ */
+static int hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ ctx->hdmi.hdmi_comp = dai->component;
+
+ return 0;
+}
+
+int sof_intel_board_card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (!ctx->hdmi_num)
+ return 0;
+
+ if (!ctx->hdmi.idisp_codec)
+ return 0;
+
+ if (!ctx->hdmi.hdmi_comp)
+ return -EINVAL;
+
+ return hda_dsp_hdmi_build_controls(card, ctx->hdmi.hdmi_comp);
+}
+EXPORT_SYMBOL_NS(sof_intel_board_card_late_probe, "SND_SOC_INTEL_SOF_BOARD_HELPERS");
+
+/*
+ * DMIC DAI Link
+ */
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route dmic_routes[] = {
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static int dmic_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add dmic widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, dmic_routes,
+ ARRAY_SIZE(dmic_routes));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add dmic routes, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * HDA External Codec DAI Link
+ */
+static const struct snd_soc_dapm_widget hda_widgets[] = {
+ SND_SOC_DAPM_MIC("Analog In", NULL),
+ SND_SOC_DAPM_MIC("Digital In", NULL),
+ SND_SOC_DAPM_MIC("Alt Analog In", NULL),
+
+ SND_SOC_DAPM_HP("Analog Out", NULL),
+ SND_SOC_DAPM_SPK("Digital Out", NULL),
+ SND_SOC_DAPM_HP("Alt Analog Out", NULL),
+};
+
+static const struct snd_soc_dapm_route hda_routes[] = {
+ { "Codec Input Pin1", NULL, "Analog In" },
+ { "Codec Input Pin2", NULL, "Digital In" },
+ { "Codec Input Pin3", NULL, "Alt Analog In" },
+
+ { "Analog Out", NULL, "Codec Output Pin1" },
+ { "Digital Out", NULL, "Codec Output Pin2" },
+ { "Alt Analog Out", NULL, "Codec Output Pin3" },
+
+ /* CODEC BE connections */
+ { "codec0_in", NULL, "Analog CPU Capture" },
+ { "Analog CPU Capture", NULL, "Analog Codec Capture" },
+ { "codec1_in", NULL, "Digital CPU Capture" },
+ { "Digital CPU Capture", NULL, "Digital Codec Capture" },
+ { "codec2_in", NULL, "Alt Analog CPU Capture" },
+ { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" },
+
+ { "Analog Codec Playback", NULL, "Analog CPU Playback" },
+ { "Analog CPU Playback", NULL, "codec0_out" },
+ { "Digital Codec Playback", NULL, "Digital CPU Playback" },
+ { "Digital CPU Playback", NULL, "codec1_out" },
+ { "Alt Analog Codec Playback", NULL, "Alt Analog CPU Playback" },
+ { "Alt Analog CPU Playback", NULL, "codec2_out" },
+};
+
+static int hda_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, hda_widgets,
+ ARRAY_SIZE(hda_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add hda widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, hda_routes,
+ ARRAY_SIZE(hda_routes));
+ if (ret)
+ dev_err(rtd->dev, "fail to add hda routes, ret %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * DAI Link Helpers
+ */
+
+enum sof_dmic_be_type {
+ SOF_DMIC_01,
+ SOF_DMIC_16K,
+};
+
+enum sof_hda_be_type {
+ SOF_HDA_ANALOG,
+ SOF_HDA_DIGITAL,
+};
+
+/* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */
+#define DEFAULT_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_CODEC, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_DMIC16K, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_AMP, \
+ SOF_LINK_BT_OFFLOAD, \
+ SOF_LINK_HDMI_IN)
+
+static struct snd_soc_dai_link_component dmic_component[] = {
+ {
+ .name = "dmic-codec",
+ .dai_name = "dmic-hifi",
+ }
+};
+
+SND_SOC_DAILINK_DEF(hda_analog_cpus,
+ DAILINK_COMP_ARRAY(COMP_CPU("Analog CPU DAI")));
+SND_SOC_DAILINK_DEF(hda_analog_codecs,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D0", "Analog Codec DAI")));
+
+SND_SOC_DAILINK_DEF(hda_digital_cpus,
+ DAILINK_COMP_ARRAY(COMP_CPU("Digital CPU DAI")));
+SND_SOC_DAILINK_DEF(hda_digital_codecs,
+ DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D0", "Digital Codec DAI")));
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:00:1f.3"
+ }
+};
+
+static int set_ssp_codec_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, enum snd_soc_acpi_intel_codec codec_type,
+ int ssp_codec)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: ssp codec %s, ssp %d\n", be_id,
+ snd_soc_acpi_intel_get_codec_name(codec_type), ssp_codec);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ if (soc_intel_is_byt() || soc_intel_is_cht()) {
+ /* backward-compatibility for BYT/CHT boards */
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port",
+ ssp_codec);
+ } else {
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin",
+ ssp_codec);
+ }
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs - caller to handle */
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+
+ return 0;
+}
+
+static int set_dmic_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, enum sof_dmic_be_type be_type)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ switch (be_type) {
+ case SOF_DMIC_01:
+ dev_dbg(dev, "link %d: dmic01\n", be_id);
+
+ link->name = "dmic01";
+ cpus->dai_name = "DMIC01 Pin";
+ break;
+ case SOF_DMIC_16K:
+ dev_dbg(dev, "link %d: dmic16k\n", be_id);
+
+ link->name = "dmic16k";
+ cpus->dai_name = "DMIC16k Pin";
+ break;
+ default:
+ dev_err(dev, "invalid be type %d\n", be_type);
+ return -EINVAL;
+ }
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs */
+ link->codecs = dmic_component;
+ link->num_codecs = ARRAY_SIZE(dmic_component);
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ if (be_type == SOF_DMIC_01)
+ link->init = dmic_init;
+ link->ignore_suspend = 1;
+ link->no_pcm = 1;
+ link->capture_only = 1;
+
+ return 0;
+}
+
+static int set_idisp_hdmi_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, int hdmi_id, bool idisp_codec)
+{
+ struct snd_soc_dai_link_component *cpus, *codecs;
+
+ dev_dbg(dev, "link %d: idisp hdmi %d, idisp codec %d\n", be_id, hdmi_id,
+ idisp_codec);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", hdmi_id);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", hdmi_id);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs */
+ if (idisp_codec) {
+ codecs = devm_kzalloc(dev,
+ sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codecs->name = "ehdaudio0D2";
+ codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "intel-hdmi-hifi%d", hdmi_id);
+ if (!codecs->dai_name)
+ return -ENOMEM;
+
+ link->codecs = codecs;
+ } else {
+ link->codecs = &snd_soc_dummy_dlc;
+ }
+ link->num_codecs = 1;
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->init = (hdmi_id == 1) ? hdmi_init : NULL;
+ link->no_pcm = 1;
+ link->playback_only = 1;
+
+ return 0;
+}
+
+static int set_ssp_amp_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, enum snd_soc_acpi_intel_codec amp_type,
+ int ssp_amp)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: ssp amp %s, ssp %d\n", be_id,
+ snd_soc_acpi_intel_get_codec_name(amp_type), ssp_amp);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_amp);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_amp);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs - caller to handle */
+
+ /* platforms */
+ /* feedback stream or firmware-generated echo reference */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+
+ return 0;
+}
+
+static int set_bt_offload_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, int ssp_bt)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs */
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+
+ return 0;
+}
+
+static int set_hdmi_in_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, int ssp_hdmi)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs */
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+ link->capture_only = 1;
+
+ return 0;
+}
+
+static int set_hda_codec_link(struct device *dev, struct snd_soc_dai_link *link,
+ int be_id, enum sof_hda_be_type be_type)
+{
+ switch (be_type) {
+ case SOF_HDA_ANALOG:
+ dev_dbg(dev, "link %d: hda analog\n", be_id);
+
+ link->name = "Analog Playback and Capture";
+
+ /* cpus */
+ link->cpus = hda_analog_cpus;
+ link->num_cpus = ARRAY_SIZE(hda_analog_cpus);
+
+ /* codecs */
+ link->codecs = hda_analog_codecs;
+ link->num_codecs = ARRAY_SIZE(hda_analog_codecs);
+ break;
+ case SOF_HDA_DIGITAL:
+ dev_dbg(dev, "link %d: hda digital\n", be_id);
+
+ link->name = "Digital Playback and Capture";
+
+ /* cpus */
+ link->cpus = hda_digital_cpus;
+ link->num_cpus = ARRAY_SIZE(hda_digital_cpus);
+
+ /* codecs */
+ link->codecs = hda_digital_codecs;
+ link->num_codecs = ARRAY_SIZE(hda_digital_codecs);
+ break;
+ default:
+ dev_err(dev, "invalid be type %d\n", be_type);
+ return -EINVAL;
+ }
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ if (be_type == SOF_HDA_ANALOG)
+ link->init = hda_init;
+ link->no_pcm = 1;
+
+ return 0;
+}
+
+static int calculate_num_links(struct sof_card_private *ctx)
+{
+ int num_links = 0;
+
+ /* headphone codec */
+ if (ctx->codec_type != CODEC_NONE)
+ num_links++;
+
+ /* dmic01 and dmic16k */
+ if (ctx->dmic_be_num > 0)
+ num_links++;
+
+ if (ctx->dmic_be_num > 1)
+ num_links++;
+
+ /* idisp HDMI */
+ num_links += ctx->hdmi_num;
+
+ /* speaker amp */
+ if (ctx->amp_type != CODEC_NONE)
+ num_links++;
+
+ /* BT audio offload */
+ if (ctx->bt_offload_present)
+ num_links++;
+
+ /* HDMI-In */
+ num_links += hweight32(ctx->ssp_mask_hdmi_in);
+
+ /* HDA external codec */
+ if (ctx->hda_codec_present)
+ num_links += 2;
+
+ return num_links;
+}
+
+int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ struct snd_soc_dai_link *links;
+ int num_links;
+ int i;
+ int idx = 0;
+ int ret;
+ int ssp_hdmi_in = 0;
+ unsigned long link_order, link;
+ unsigned long link_ids, be_id;
+
+ num_links = calculate_num_links(ctx);
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link),
+ GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (ctx->link_order_overwrite)
+ link_order = ctx->link_order_overwrite;
+ else
+ link_order = DEFAULT_LINK_ORDER;
+
+ if (ctx->link_id_overwrite)
+ link_ids = ctx->link_id_overwrite;
+ else
+ link_ids = 0;
+
+ dev_dbg(dev, "create dai links, link_order 0x%lx, id_overwrite 0x%lx\n",
+ link_order, link_ids);
+
+ while (link_order) {
+ link = link_order & SOF_LINK_ORDER_MASK;
+ link_order >>= SOF_LINK_ORDER_SHIFT;
+
+ if (ctx->link_id_overwrite) {
+ be_id = link_ids & SOF_LINK_IDS_MASK;
+ link_ids >>= SOF_LINK_IDS_SHIFT;
+ } else {
+ /* use array index as link id */
+ be_id = idx;
+ }
+
+ switch (link) {
+ case SOF_LINK_CODEC:
+ /* headphone codec */
+ if (ctx->codec_type == CODEC_NONE)
+ continue;
+
+ ret = set_ssp_codec_link(dev, &links[idx], be_id,
+ ctx->codec_type, ctx->ssp_codec);
+ if (ret) {
+ dev_err(dev, "fail to set codec link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ctx->codec_link = &links[idx];
+ idx++;
+ break;
+ case SOF_LINK_DMIC01:
+ /* dmic01 */
+ if (ctx->dmic_be_num == 0)
+ continue;
+
+ /* at least we have dmic01 */
+ ret = set_dmic_link(dev, &links[idx], be_id, SOF_DMIC_01);
+ if (ret) {
+ dev_err(dev, "fail to set dmic01 link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ break;
+ case SOF_LINK_DMIC16K:
+ /* dmic16k */
+ if (ctx->dmic_be_num <= 1)
+ continue;
+
+ /* set up 2 BE links at most */
+ ret = set_dmic_link(dev, &links[idx], be_id,
+ SOF_DMIC_16K);
+ if (ret) {
+ dev_err(dev, "fail to set dmic16k link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ break;
+ case SOF_LINK_IDISP_HDMI:
+ /* idisp HDMI */
+ for (i = 1; i <= ctx->hdmi_num; i++) {
+ ret = set_idisp_hdmi_link(dev, &links[idx],
+ be_id, i,
+ ctx->hdmi.idisp_codec);
+ if (ret) {
+ dev_err(dev, "fail to set hdmi link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ be_id++;
+ }
+ break;
+ case SOF_LINK_AMP:
+ /* speaker amp */
+ if (ctx->amp_type == CODEC_NONE)
+ continue;
+
+ ret = set_ssp_amp_link(dev, &links[idx], be_id,
+ ctx->amp_type, ctx->ssp_amp);
+ if (ret) {
+ dev_err(dev, "fail to set amp link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ctx->amp_link = &links[idx];
+ idx++;
+ break;
+ case SOF_LINK_BT_OFFLOAD:
+ /* BT audio offload */
+ if (!ctx->bt_offload_present)
+ continue;
+
+ ret = set_bt_offload_link(dev, &links[idx], be_id,
+ ctx->ssp_bt);
+ if (ret) {
+ dev_err(dev, "fail to set bt link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ break;
+ case SOF_LINK_HDMI_IN:
+ /* HDMI-In */
+ for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) {
+ ret = set_hdmi_in_link(dev, &links[idx], be_id,
+ ssp_hdmi_in);
+ if (ret) {
+ dev_err(dev, "fail to set hdmi-in link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ be_id++;
+ }
+ break;
+ case SOF_LINK_HDA:
+ /* HDA external codec */
+ if (!ctx->hda_codec_present)
+ continue;
+
+ ret = set_hda_codec_link(dev, &links[idx], be_id,
+ SOF_HDA_ANALOG);
+ if (ret) {
+ dev_err(dev, "fail to set hda analog link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ be_id++;
+
+ ret = set_hda_codec_link(dev, &links[idx], be_id,
+ SOF_HDA_DIGITAL);
+ if (ret) {
+ dev_err(dev, "fail to set hda digital link, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ idx++;
+ break;
+ case SOF_LINK_NONE:
+ /* caught here if it's not used as terminator in macro */
+ fallthrough;
+ default:
+ dev_err(dev, "invalid link type %ld\n", link);
+ return -EINVAL;
+ }
+ }
+
+ if (idx != num_links) {
+ dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx,
+ num_links);
+ return -EINVAL;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, "SND_SOC_INTEL_SOF_BOARD_HELPERS");
+
+struct sof_card_private *
+sof_intel_board_get_ctx(struct device *dev, unsigned long board_quirk)
+{
+ struct sof_card_private *ctx;
+
+ dev_dbg(dev, "create ctx, board_quirk 0x%lx\n", board_quirk);
+
+ ctx = devm_kzalloc(dev, sizeof(struct sof_card_private), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ ctx->codec_type = snd_soc_acpi_intel_detect_codec_type(dev);
+ ctx->amp_type = snd_soc_acpi_intel_detect_amp_type(dev);
+
+ ctx->dmic_be_num = 2;
+ ctx->hdmi_num = (board_quirk & SOF_NUM_IDISP_HDMI_MASK) >>
+ SOF_NUM_IDISP_HDMI_SHIFT;
+ /* default number of HDMI DAI's */
+ if (!ctx->hdmi_num)
+ ctx->hdmi_num = 3;
+
+ /* port number/mask of peripherals attached to ssp interface */
+ if (ctx->codec_type != CODEC_NONE)
+ ctx->ssp_codec = (board_quirk & SOF_SSP_PORT_CODEC_MASK) >>
+ SOF_SSP_PORT_CODEC_SHIFT;
+
+ if (ctx->amp_type != CODEC_NONE)
+ ctx->ssp_amp = (board_quirk & SOF_SSP_PORT_AMP_MASK) >>
+ SOF_SSP_PORT_AMP_SHIFT;
+
+ if (board_quirk & SOF_BT_OFFLOAD_PRESENT) {
+ ctx->bt_offload_present = true;
+ ctx->ssp_bt = (board_quirk & SOF_SSP_PORT_BT_OFFLOAD_MASK) >>
+ SOF_SSP_PORT_BT_OFFLOAD_SHIFT;
+ }
+
+ ctx->ssp_mask_hdmi_in = (board_quirk & SOF_SSP_MASK_HDMI_CAPTURE_MASK) >>
+ SOF_SSP_MASK_HDMI_CAPTURE_SHIFT;
+
+ return ctx;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_get_ctx, "SND_SOC_INTEL_SOF_BOARD_HELPERS");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_INTEL_HDA_DSP_COMMON");
+MODULE_IMPORT_NS("SND_SOC_ACPI_INTEL_MATCH");
diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h
new file mode 100644
index 000000000000..33a9601b770c
--- /dev/null
+++ b/sound/soc/intel/boards/sof_board_helpers.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2023 Intel Corporation.
+ */
+
+#ifndef __SOF_INTEL_BOARD_HELPERS_H
+#define __SOF_INTEL_BOARD_HELPERS_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+#include "sof_hdmi_common.h"
+
+/*
+ * Common board quirks: from bit 8 to 31, LSB 8 bits reserved for machine
+ * drivers
+ */
+
+/* SSP port number for headphone codec: 3 bits */
+#define SOF_SSP_PORT_CODEC_SHIFT 8
+#define SOF_SSP_PORT_CODEC_MASK (GENMASK(10, 8))
+#define SOF_SSP_PORT_CODEC(quirk) \
+ (((quirk) << SOF_SSP_PORT_CODEC_SHIFT) & SOF_SSP_PORT_CODEC_MASK)
+
+/* SSP port number for speaker amplifier: 3 bits */
+#define SOF_SSP_PORT_AMP_SHIFT 11
+#define SOF_SSP_PORT_AMP_MASK (GENMASK(13, 11))
+#define SOF_SSP_PORT_AMP(quirk) \
+ (((quirk) << SOF_SSP_PORT_AMP_SHIFT) & SOF_SSP_PORT_AMP_MASK)
+
+/* SSP port number for BT audio offload: 3 bits */
+#define SOF_SSP_PORT_BT_OFFLOAD_SHIFT 14
+#define SOF_SSP_PORT_BT_OFFLOAD_MASK (GENMASK(16, 14))
+#define SOF_SSP_PORT_BT_OFFLOAD(quirk) \
+ (((quirk) << SOF_SSP_PORT_BT_OFFLOAD_SHIFT) & SOF_SSP_PORT_BT_OFFLOAD_MASK)
+
+/* SSP port mask for HDMI capture: 6 bits */
+#define SOF_SSP_MASK_HDMI_CAPTURE_SHIFT 17
+#define SOF_SSP_MASK_HDMI_CAPTURE_MASK (GENMASK(22, 17))
+#define SOF_SSP_MASK_HDMI_CAPTURE(quirk) \
+ (((quirk) << SOF_SSP_MASK_HDMI_CAPTURE_SHIFT) & SOF_SSP_MASK_HDMI_CAPTURE_MASK)
+
+/* Number of idisp HDMI BE link: 3 bits */
+#define SOF_NUM_IDISP_HDMI_SHIFT 23
+#define SOF_NUM_IDISP_HDMI_MASK (GENMASK(25, 23))
+#define SOF_NUM_IDISP_HDMI(quirk) \
+ (((quirk) << SOF_NUM_IDISP_HDMI_SHIFT) & SOF_NUM_IDISP_HDMI_MASK)
+
+/* Board uses BT audio offload */
+#define SOF_BT_OFFLOAD_PRESENT BIT(26)
+
+enum {
+ SOF_LINK_NONE = 0,
+ SOF_LINK_CODEC,
+ SOF_LINK_DMIC01,
+ SOF_LINK_DMIC16K,
+ SOF_LINK_IDISP_HDMI,
+ SOF_LINK_AMP,
+ SOF_LINK_BT_OFFLOAD,
+ SOF_LINK_HDMI_IN,
+ SOF_LINK_HDA,
+};
+
+#define SOF_LINK_ORDER_MASK (0xF)
+#define SOF_LINK_ORDER_SHIFT (4)
+
+#define SOF_LINK_ORDER(k1, k2, k3, k4, k5, k6, k7) \
+ ((((k1) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 0)) | \
+ (((k2) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 1)) | \
+ (((k3) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 2)) | \
+ (((k4) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 3)) | \
+ (((k5) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 4)) | \
+ (((k6) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 5)) | \
+ (((k7) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 6)))
+
+#define SOF_LINK_IDS_MASK (0xF)
+#define SOF_LINK_IDS_SHIFT (4)
+
+#define SOF_LINK_IDS(k1, k2, k3, k4, k5, k6, k7) \
+ ((((k1) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 0)) | \
+ (((k2) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 1)) | \
+ (((k3) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 2)) | \
+ (((k4) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 3)) | \
+ (((k5) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 4)) | \
+ (((k6) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 5)) | \
+ (((k7) & SOF_LINK_IDS_MASK) << (SOF_LINK_IDS_SHIFT * 6)))
+
+/*
+ * sof_da7219_private: private data for da7219 machine driver
+ *
+ * @mclk_en: true for mclk pin is connected
+ * @pll_bypass: true for PLL bypass mode
+ */
+struct sof_da7219_private {
+ bool mclk_en;
+ bool pll_bypass;
+};
+
+/*
+ * sof_rt5682_private: private data for rt5682 machine driver
+ *
+ * @mclk: mclk clock data
+ * @is_legacy_cpu: true for BYT/CHT boards
+ * @mclk_en: true for mclk pin is connected
+ */
+struct sof_rt5682_private {
+ struct clk *mclk;
+ bool is_legacy_cpu;
+ bool mclk_en;
+};
+
+/*
+ * sof_card_private: common data for machine drivers
+ *
+ * @headset_jack: headset jack data
+ * @hdmi: init data for hdmi dai link
+ * @codec_type: type of headset codec
+ * @amp_type: type of speaker amplifier
+ * @dmic_be_num: number of Intel PCH DMIC BE link
+ * @hdmi_num: number of Intel HDMI BE link
+ * @ssp_codec: ssp port number of headphone BE link
+ * @ssp_amp: ssp port number of speaker BE link
+ * @ssp_bt: ssp port number of BT offload BE link
+ * @ssp_mask_hdmi_in: ssp port mask of HDMI-IN BE link
+ * @bt_offload_present: true to create BT offload BE link
+ * @hda_codec_present: true to create HDA codec BE links
+ * @codec_link: pointer to headset codec dai link
+ * @amp_link: pointer to speaker amplifier dai link
+ * @link_order_overwrite: custom DAI link order
+ * @link_id_overwrite: custom DAI link ID
+ * @da7219: private data for da7219 machine driver
+ * @rt5682: private data for rt5682 machine driver
+ */
+struct sof_card_private {
+ struct snd_soc_jack headset_jack;
+ struct sof_hdmi_private hdmi;
+
+ enum snd_soc_acpi_intel_codec codec_type;
+ enum snd_soc_acpi_intel_codec amp_type;
+
+ int dmic_be_num;
+ int hdmi_num;
+
+ int ssp_codec;
+ int ssp_amp;
+ int ssp_bt;
+ unsigned long ssp_mask_hdmi_in;
+
+ bool bt_offload_present;
+ bool hda_codec_present;
+
+ struct snd_soc_dai_link *codec_link;
+ struct snd_soc_dai_link *amp_link;
+
+ unsigned long link_order_overwrite;
+ /*
+ * A variable stores id for all BE DAI links, use SOF_LINK_IDS macro to
+ * build the value; use DAI link array index as id if zero.
+ */
+ unsigned long link_id_overwrite;
+
+ union {
+ struct sof_da7219_private da7219;
+ struct sof_rt5682_private rt5682;
+ };
+};
+
+int sof_intel_board_card_late_probe(struct snd_soc_card *card);
+int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx);
+struct sof_card_private *
+sof_intel_board_get_ctx(struct device *dev, unsigned long board_quirk);
+
+#endif /* __SOF_INTEL_BOARD_HELPERS_H */
diff --git a/sound/soc/intel/boards/sof_cirrus_common.c b/sound/soc/intel/boards/sof_cirrus_common.c
new file mode 100644
index 000000000000..88fc6cb2bfd4
--- /dev/null
+++ b/sound/soc/intel/boards/sof_cirrus_common.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file defines data structures and functions used in Machine
+ * Driver for Intel platforms with Cirrus Logic Codecs.
+ *
+ * Copyright 2022 Intel Corporation.
+ */
+#include <linux/module.h>
+#include <sound/sof.h>
+#include "../../codecs/cs35l41.h"
+#include "sof_cirrus_common.h"
+
+#define CS35L41_HID "CSC3541"
+#define CS35L41_MAX_AMPS 4
+
+/*
+ * Cirrus Logic CS35L41/CS35L53
+ */
+static const struct snd_kcontrol_new cs35l41_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("WL Spk"),
+ SOC_DAPM_PIN_SWITCH("WR Spk"),
+ SOC_DAPM_PIN_SWITCH("TL Spk"),
+ SOC_DAPM_PIN_SWITCH("TR Spk"),
+};
+
+static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("WL Spk", NULL),
+ SND_SOC_DAPM_SPK("WR Spk", NULL),
+ SND_SOC_DAPM_SPK("TL Spk", NULL),
+ SND_SOC_DAPM_SPK("TR Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route cs35l41_dapm_routes[] = {
+ /* speaker */
+ {"WL Spk", NULL, "WL SPK"},
+ {"WR Spk", NULL, "WR SPK"},
+ {"TL Spk", NULL, "TL SPK"},
+ {"TR Spk", NULL, "TR SPK"},
+};
+
+static struct snd_soc_dai_link_component cs35l41_components[CS35L41_MAX_AMPS];
+
+/*
+ * Mapping between ACPI instance id and speaker position.
+ */
+static struct snd_soc_codec_conf cs35l41_codec_conf[CS35L41_MAX_AMPS];
+
+static int cs35l41_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, cs35l41_dapm_widgets,
+ ARRAY_SIZE(cs35l41_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, cs35l41_kcontrols,
+ ARRAY_SIZE(cs35l41_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, cs35l41_dapm_routes,
+ ARRAY_SIZE(cs35l41_dapm_routes));
+
+ if (ret)
+ dev_err(rtd->dev, "fail to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Channel map:
+ *
+ * TL/WL: ASPRX1 on slot 0, ASPRX2 on slot 1 (default)
+ * TR/WR: ASPRX1 on slot 1, ASPRX2 on slot 0
+ */
+static const struct {
+ unsigned int rx[2];
+} cs35l41_channel_map[] = {
+ {.rx = {0, 1}}, /* WL */
+ {.rx = {1, 0}}, /* WR */
+ {.rx = {0, 1}}, /* TL */
+ {.rx = {1, 0}}, /* TR */
+};
+
+static int cs35l41_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int clk_freq, i, ret;
+
+ clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
+
+ if (clk_freq <= 0) {
+ dev_err(rtd->dev, "fail to get bclk freq, ret %d\n", clk_freq);
+ return -EINVAL;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ /* call dai driver's set_sysclk() callback */
+ ret = snd_soc_dai_set_sysclk(codec_dai, CS35L41_CLKID_SCLK,
+ clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set sysclk, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ /* call component driver's set_sysclk() callback */
+ ret = snd_soc_component_set_sysclk(codec_dai->component,
+ CS35L41_CLKID_SCLK, 0,
+ clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set component sysclk, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ /* setup channel map */
+ ret = snd_soc_dai_set_channel_map(codec_dai, 0, NULL,
+ ARRAY_SIZE(cs35l41_channel_map[i].rx),
+ (unsigned int *)cs35l41_channel_map[i].rx);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set channel map, ret %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops cs35l41_ops = {
+ .hw_params = cs35l41_hw_params,
+};
+
+static const char * const cs35l41_name_prefixes[] = { "WL", "WR", "TL", "TR" };
+
+/*
+ * Expected UIDs are integers (stored as strings).
+ * UID Mapping is fixed:
+ * UID 0x0 -> WL
+ * UID 0x1 -> WR
+ * UID 0x2 -> TL
+ * UID 0x3 -> TR
+ * Note: If there are less than 4 Amps, UIDs still map to WL/WR/TL/TR. Dynamic code will only create
+ * dai links for UIDs which exist, and ignore non-existant ones. Only 2 or 4 amps are expected.
+ * Return number of codecs found.
+ */
+static int cs35l41_compute_codec_conf(void)
+{
+ static const char * const uid_strings[] = { "0", "1", "2", "3" };
+ unsigned int uid, sz = 0;
+ struct acpi_device *adev;
+ struct device *physdev;
+
+ for (uid = 0; uid < CS35L41_MAX_AMPS; uid++) {
+ adev = acpi_dev_get_first_match_dev(CS35L41_HID, uid_strings[uid], -1);
+ if (!adev) {
+ pr_devel("Cannot find match for HID %s UID %u (%s)\n", CS35L41_HID, uid,
+ cs35l41_name_prefixes[uid]);
+ continue;
+ }
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+ if (!physdev) {
+ pr_devel("Cannot find physical node for HID %s UID %u (%s)\n", CS35L41_HID,
+ uid, cs35l41_name_prefixes[uid]);
+ return 0;
+ }
+ cs35l41_components[sz].name = dev_name(physdev);
+ cs35l41_components[sz].dai_name = CS35L41_CODEC_DAI;
+ cs35l41_codec_conf[sz].dlc.name = dev_name(physdev);
+ cs35l41_codec_conf[sz].name_prefix = cs35l41_name_prefixes[uid];
+ sz++;
+ }
+
+ if (sz != 2 && sz != 4)
+ pr_warn("Invalid number of cs35l41 amps found: %d, expected 2 or 4\n", sz);
+ return sz;
+}
+
+void cs35l41_set_dai_link(struct snd_soc_dai_link *link)
+{
+ link->num_codecs = cs35l41_compute_codec_conf();
+ link->codecs = cs35l41_components;
+ link->init = cs35l41_init;
+ link->ops = &cs35l41_ops;
+}
+EXPORT_SYMBOL_NS(cs35l41_set_dai_link, "SND_SOC_INTEL_SOF_CIRRUS_COMMON");
+
+void cs35l41_set_codec_conf(struct snd_soc_card *card)
+{
+ card->codec_conf = cs35l41_codec_conf;
+ card->num_configs = ARRAY_SIZE(cs35l41_codec_conf);
+}
+EXPORT_SYMBOL_NS(cs35l41_set_codec_conf, "SND_SOC_INTEL_SOF_CIRRUS_COMMON");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Cirrus Logic helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_cirrus_common.h b/sound/soc/intel/boards/sof_cirrus_common.h
new file mode 100644
index 000000000000..1c87637b9ef7
--- /dev/null
+++ b/sound/soc/intel/boards/sof_cirrus_common.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file defines data structures used in Machine Driver for Intel
+ * platforms with Cirrus Logic Codecs.
+ *
+ * Copyright 2022 Intel Corporation.
+ */
+#ifndef __SOF_CIRRUS_COMMON_H
+#define __SOF_CIRRUS_COMMON_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Cirrus Logic CS35L41/CS35L53
+ */
+#define CS35L41_CODEC_DAI "cs35l41-pcm"
+#define CS35L41_DEV0_NAME "i2c-" CS35L41_ACPI_HID ":00"
+#define CS35L41_DEV1_NAME "i2c-" CS35L41_ACPI_HID ":01"
+#define CS35L41_DEV2_NAME "i2c-" CS35L41_ACPI_HID ":02"
+#define CS35L41_DEV3_NAME "i2c-" CS35L41_ACPI_HID ":03"
+
+void cs35l41_set_dai_link(struct snd_soc_dai_link *link);
+void cs35l41_set_codec_conf(struct snd_soc_card *card);
+
+#endif /* __SOF_CIRRUS_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
new file mode 100644
index 000000000000..455c5bc8c634
--- /dev/null
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2021 Intel Corporation.
+
+/*
+ * Intel SOF Machine Driver with Cirrus Logic CS42L42 Codec
+ * and speaker codec MAX98357A
+ */
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dmi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/sof.h>
+#include <sound/soc-acpi.h>
+#include <dt-bindings/sound/cs42l42.h>
+#include "../common/soc-intel-quirks.h"
+#include "sof_board_helpers.h"
+#include "sof_maxim_common.h"
+
+static struct snd_soc_jack_pin jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+/* Default: SSP2 */
+static unsigned long sof_cs42l42_quirk = SOF_SSP_PORT_CODEC(2);
+
+static int sof_cs42l42_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_jack *jack = &ctx->headset_jack;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ jack_pins,
+ ARRAY_SIZE(jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+};
+
+static void sof_cs42l42_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sof_cs42l42_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int clk_freq, ret;
+
+ clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
+
+ if (clk_freq <= 0) {
+ dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq);
+ return -EINVAL;
+ }
+
+ /* Configure sysclk for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops sof_cs42l42_ops = {
+ .hw_params = sof_cs42l42_hw_params,
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ return sof_intel_board_card_late_probe(card);
+}
+
+static const struct snd_kcontrol_new sof_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone Jack", NULL, "HP"},
+
+ /* other jacks */
+ {"HS", NULL, "Headset Mic"},
+};
+
+/* sof audio machine driver for cs42l42 codec */
+static struct snd_soc_card sof_audio_card_cs42l42 = {
+ .name = "cs42l42", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE,
+ .controls = sof_controls,
+ .num_controls = ARRAY_SIZE(sof_controls),
+ .dapm_widgets = sof_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
+ .dapm_routes = sof_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_map),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+static struct snd_soc_dai_link_component cs42l42_component[] = {
+ {
+ .name = "i2c-10134242:00",
+ .dai_name = "cs42l42",
+ }
+};
+
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ int ret;
+
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
+
+ if (!ctx->codec_link) {
+ dev_err(dev, "codec link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for headphone codec */
+ ctx->codec_link->codecs = cs42l42_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(cs42l42_component);
+ ctx->codec_link->init = sof_cs42l42_init;
+ ctx->codec_link->exit = sof_cs42l42_exit;
+ ctx->codec_link->ops = &sof_cs42l42_ops;
+
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
+
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ max_98357a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(ctx->amp_link);
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define GLK_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \
+ SOF_LINK_CODEC, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE)
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_card_private *ctx;
+ int ret;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ sof_cs42l42_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk);
+
+ /* initialize ctx with board quirk */
+ ctx = sof_intel_board_get_ctx(&pdev->dev, sof_cs42l42_quirk);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (soc_intel_is_glk()) {
+ ctx->dmic_be_num = 1;
+
+ /* overwrite the DAI link order for GLK boards */
+ ctx->link_order_overwrite = GLK_LINK_ORDER;
+ }
+
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->hdmi.idisp_codec = true;
+
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_cs42l42, ctx);
+ if (ret)
+ return ret;
+
+ sof_audio_card_cs42l42.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_cs42l42,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&sof_audio_card_cs42l42, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev,
+ &sof_audio_card_cs42l42);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "glk_cs4242_mx98357a",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "jsl_cs4242_mx98360a",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "adl_cs42l42_def",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_BT_OFFLOAD_PRESENT |
+ SOF_SSP_PORT_BT_OFFLOAD(2)),
+ },
+ {
+ .name = "rpl_cs42l42_def",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_BT_OFFLOAD_PRESENT |
+ SOF_SSP_PORT_BT_OFFLOAD(2)),
+ },
+ {
+ .name = "mtl_cs42l42_def",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(0) |
+ SOF_BT_OFFLOAD_PRESENT |
+ SOF_SSP_PORT_BT_OFFLOAD(1)),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver sof_audio = {
+ .probe = sof_audio_probe,
+ .driver = {
+ .name = "sof_cs42l42",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(sof_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("SOF Audio Machine driver for CS42L42");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
diff --git a/sound/soc/intel/boards/sof_da7219.c b/sound/soc/intel/boards/sof_da7219.c
new file mode 100644
index 000000000000..ad845d32f642
--- /dev/null
+++ b/sound/soc/intel/boards/sof_da7219.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2019 Intel Corporation.
+
+/*
+ * Intel SOF Machine driver for Dialog headphone codec
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/sof.h>
+#include "../../codecs/da7219.h"
+#include "sof_board_helpers.h"
+#include "sof_maxim_common.h"
+
+/* Driver-specific board quirks: from bit 0 to 7 */
+#define SOF_DA7219_GLK_BOARD BIT(0)
+#define SOF_DA7219_CML_BOARD BIT(1)
+#define SOF_DA7219_JSL_BOARD BIT(2)
+#define SOF_DA7219_MCLK_EN BIT(3)
+
+#define DIALOG_CODEC_DAI "da7219-hifi"
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0;
+
+ if (ctx->da7219.pll_bypass)
+ return ret;
+
+ /* PLL SRM mode */
+ codec_dai = snd_soc_card_get_codec_dai(card, DIALOG_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK,
+ 0, 0);
+ if (ret)
+ dev_err(card->dev, "failed to stop PLL: %d\n", ret);
+ } else if (SND_SOC_DAPM_EVENT_ON(event)) {
+ dev_dbg(card->dev, "pll srm mode\n");
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM,
+ 0, DA7219_PLL_FREQ_OUT_98304);
+ if (ret)
+ dev_err(card->dev, "failed to start PLL: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static const struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ { "Headphone Jack", NULL, "HPL" },
+ { "Headphone Jack", NULL, "HPR" },
+
+ { "MIC", NULL, "Headset Mic" },
+
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Line Out", NULL, "Platform Clock" },
+};
+
+static struct snd_soc_jack_pin jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_jack *jack = &ctx->headset_jack;
+ int mclk_rate, ret;
+
+ mclk_rate = sof_dai_get_mclk(rtd);
+ if (mclk_rate <= 0) {
+ dev_err(rtd->dev, "invalid mclk freq %d\n", mclk_rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, mclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(rtd->dev, "fail to set sysclk, ret %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Use PLL bypass mode if MCLK is available, be sure to set the
+ * frequency of MCLK to 12.288 or 24.576MHz on topology side.
+ */
+ if (ctx->da7219.mclk_en &&
+ (mclk_rate == 12288000 || mclk_rate == 24576000)) {
+ /* PLL bypass mode */
+ dev_dbg(rtd->dev, "pll bypass mode, mclk rate %d\n", mclk_rate);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret) {
+ dev_err(rtd->dev, "fail to set pll, ret %d\n", ret);
+ return ret;
+ }
+
+ ctx->da7219.pll_bypass = true;
+ }
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3 | SND_JACK_LINEOUT,
+ jack, jack_pins, ARRAY_SIZE(jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "fail to set component jack, ret %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void da7219_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int err;
+
+ if (ctx->amp_type == CODEC_MAX98373) {
+ /* Disable Left and Right Spk pin after boot */
+ snd_soc_dapm_disable_pin(dapm, "Left Spk");
+ snd_soc_dapm_disable_pin(dapm, "Right Spk");
+ err = snd_soc_dapm_sync(dapm);
+ if (err < 0)
+ return err;
+ }
+
+ return sof_intel_board_card_late_probe(card);
+}
+
+static struct snd_soc_card card_da7219 = {
+ .name = "da7219", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE,
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+ .late_probe = card_late_probe,
+};
+
+static struct snd_soc_dai_link_component da7219_component[] = {
+ {
+ .name = "i2c-DLGS7219:00",
+ .dai_name = DIALOG_CODEC_DAI,
+ }
+};
+
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ int ret;
+
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
+
+ if (!ctx->codec_link) {
+ dev_err(dev, "codec link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for headphone codec */
+ ctx->codec_link->codecs = da7219_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(da7219_component);
+ ctx->codec_link->init = da7219_codec_init;
+ ctx->codec_link->exit = da7219_codec_exit;
+
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
+
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ max_98357a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98373:
+ max_98373_dai_link(dev, ctx->amp_link);
+ break;
+ case CODEC_MAX98390:
+ max_98390_dai_link(dev, ctx->amp_link);
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define GLK_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \
+ SOF_LINK_CODEC, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE)
+
+#define CML_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \
+ SOF_LINK_CODEC, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_DMIC16K, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE)
+
+#define JSL_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \
+ SOF_LINK_CODEC, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_DMIC16K, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE)
+
+static int audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_card_private *ctx;
+ char *card_name;
+ unsigned long board_quirk = 0;
+ int ret;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ board_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk);
+
+ /* initialize ctx with board quirk */
+ ctx = sof_intel_board_get_ctx(&pdev->dev, board_quirk);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->hdmi.idisp_codec = true;
+
+ if (board_quirk & SOF_DA7219_GLK_BOARD) {
+ /* dmic16k not support */
+ ctx->dmic_be_num = 1;
+
+ /* overwrite the DAI link order for GLK boards */
+ ctx->link_order_overwrite = GLK_LINK_ORDER;
+
+ /* backward-compatible with existing devices */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ card_name = devm_kstrdup(&pdev->dev, "glkda7219max",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ card_da7219.name = card_name;
+ break;
+ default:
+ break;
+ }
+ } else if (board_quirk & SOF_DA7219_CML_BOARD) {
+ /* overwrite the DAI link order for CML boards */
+ ctx->link_order_overwrite = CML_LINK_ORDER;
+
+ /* backward-compatible with existing devices */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ card_name = devm_kstrdup(&pdev->dev, "cmlda7219max",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ card_da7219.name = card_name;
+ break;
+ case CODEC_MAX98390:
+ card_name = devm_kstrdup(&pdev->dev,
+ "cml_max98390_da7219",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ card_da7219.name = card_name;
+ break;
+ default:
+ break;
+ }
+ } else if (board_quirk & SOF_DA7219_JSL_BOARD) {
+ /* overwrite the DAI link order for JSL boards */
+ ctx->link_order_overwrite = JSL_LINK_ORDER;
+
+ /* backward-compatible with existing devices */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98360A:
+ card_name = devm_kstrdup(&pdev->dev, "da7219max98360a",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ card_da7219.name = card_name;
+ break;
+ case CODEC_MAX98373:
+ card_name = devm_kstrdup(&pdev->dev, "da7219max",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ card_da7219.name = card_name;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (board_quirk & SOF_DA7219_MCLK_EN)
+ ctx->da7219.mclk_en = true;
+
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &card_da7219, ctx);
+ if (ret)
+ return ret;
+
+ /* update codec_conf */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98373:
+ max_98373_set_codec_conf(&card_da7219);
+ break;
+ case CODEC_MAX98390:
+ max_98390_set_codec_conf(&pdev->dev, &card_da7219);
+ break;
+ case CODEC_MAX98357A:
+ case CODEC_MAX98360A:
+ case CODEC_NONE:
+ /* no codec conf required */
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ card_da7219.dev = &pdev->dev;
+
+ ret = snd_soc_fixup_dai_links_platform_name(&card_da7219,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&card_da7219, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev, &card_da7219);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "glk_da7219_def",
+ .driver_data = (kernel_ulong_t)(SOF_DA7219_GLK_BOARD |
+ SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "cml_da7219_def",
+ .driver_data = (kernel_ulong_t)(SOF_DA7219_CML_BOARD |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "jsl_da7219_def",
+ .driver_data = (kernel_ulong_t)(SOF_DA7219_JSL_BOARD |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "adl_da7219_def",
+ .driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "rpl_da7219_def",
+ .driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "mtl_da7219_def",
+ .driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN |
+ SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(0) |
+ SOF_SSP_PORT_BT_OFFLOAD(1) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver audio = {
+ .probe = audio_probe,
+ .driver = {
+ .name = "sof_da7219",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(audio)
+
+/* Module information */
+MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver for Dialog codec");
+MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
new file mode 100644
index 000000000000..774fff58d51b
--- /dev/null
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -0,0 +1,858 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2021 Intel Corporation.
+
+/*
+ * Intel SOF Machine Driver with es8336 Codec
+ */
+
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "hda_dsp_common.h"
+
+/* jd-inv + terminating entry */
+#define MAX_NO_PROPS 2
+
+#define SOF_ES8336_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0))
+#define SOF_ES8336_SSP_CODEC_MASK (GENMASK(3, 0))
+
+#define SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK BIT(4)
+
+/* HDMI capture*/
+#define SOF_SSP_HDMI_CAPTURE_PRESENT BIT(14)
+#define SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT 15
+#define SOF_NO_OF_HDMI_CAPTURE_SSP_MASK (GENMASK(16, 15))
+#define SOF_NO_OF_HDMI_CAPTURE_SSP(quirk) \
+ (((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK)
+
+#define SOF_HDMI_CAPTURE_1_SSP_SHIFT 7
+#define SOF_HDMI_CAPTURE_1_SSP_MASK (GENMASK(9, 7))
+#define SOF_HDMI_CAPTURE_1_SSP(quirk) \
+ (((quirk) << SOF_HDMI_CAPTURE_1_SSP_SHIFT) & SOF_HDMI_CAPTURE_1_SSP_MASK)
+
+#define SOF_HDMI_CAPTURE_2_SSP_SHIFT 10
+#define SOF_HDMI_CAPTURE_2_SSP_MASK (GENMASK(12, 10))
+#define SOF_HDMI_CAPTURE_2_SSP(quirk) \
+ (((quirk) << SOF_HDMI_CAPTURE_2_SSP_SHIFT) & SOF_HDMI_CAPTURE_2_SSP_MASK)
+
+#define SOF_ES8336_ENABLE_DMIC BIT(5)
+#define SOF_ES8336_JD_INVERTED BIT(6)
+#define SOF_ES8336_HEADPHONE_GPIO BIT(7)
+#define SOC_ES8336_HEADSET_MIC1 BIT(8)
+
+static unsigned long quirk;
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+struct sof_es8336_private {
+ struct device *codec_dev;
+ struct gpio_desc *gpio_speakers, *gpio_headphone;
+ struct snd_soc_jack jack;
+ struct list_head hdmi_pcm_list;
+ bool speaker_en;
+ struct delayed_work pcm_pop_work;
+};
+
+struct sof_hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ int device;
+};
+
+static const struct acpi_gpio_params enable_gpio0 = { 0, 0, true };
+static const struct acpi_gpio_params enable_gpio1 = { 1, 0, true };
+
+static const struct acpi_gpio_mapping acpi_speakers_enable_gpio0[] = {
+ { "speakers-enable-gpios", &enable_gpio0, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { }
+};
+
+static const struct acpi_gpio_mapping acpi_speakers_enable_gpio1[] = {
+ { "speakers-enable-gpios", &enable_gpio1, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+};
+
+static const struct acpi_gpio_mapping acpi_enable_both_gpios[] = {
+ { "speakers-enable-gpios", &enable_gpio0, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { "headphone-enable-gpios", &enable_gpio1, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { }
+};
+
+static const struct acpi_gpio_mapping acpi_enable_both_gpios_rev_order[] = {
+ { "speakers-enable-gpios", &enable_gpio1, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { "headphone-enable-gpios", &enable_gpio0, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { }
+};
+
+static void log_quirks(struct device *dev)
+{
+ dev_info(dev, "quirk mask %#lx\n", quirk);
+ dev_info(dev, "quirk SSP%ld\n", SOF_ES8336_SSP_CODEC(quirk));
+ if (quirk & SOF_ES8336_ENABLE_DMIC)
+ dev_info(dev, "quirk DMIC enabled\n");
+ if (quirk & SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK)
+ dev_info(dev, "Speakers GPIO1 quirk enabled\n");
+ if (quirk & SOF_ES8336_HEADPHONE_GPIO)
+ dev_info(dev, "quirk headphone GPIO enabled\n");
+ if (quirk & SOF_ES8336_JD_INVERTED)
+ dev_info(dev, "quirk JD inverted enabled\n");
+ if (quirk & SOC_ES8336_HEADSET_MIC1)
+ dev_info(dev, "quirk headset at mic1 port enabled\n");
+}
+
+static void pcm_pop_work_events(struct work_struct *work)
+{
+ struct sof_es8336_private *priv =
+ container_of(work, struct sof_es8336_private, pcm_pop_work.work);
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_en);
+
+ if (quirk & SOF_ES8336_HEADPHONE_GPIO)
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->speaker_en);
+
+}
+
+static int sof_8336_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (priv->speaker_en == false)
+ if (substream->stream == 0) {
+ cancel_delayed_work(&priv->pcm_pop_work);
+ gpiod_set_value_cansleep(priv->gpio_speakers, true);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (priv->speaker_en == !SND_SOC_DAPM_EVENT_ON(event))
+ return 0;
+
+ priv->speaker_en = !SND_SOC_DAPM_EVENT_ON(event);
+
+ queue_delayed_work(system_dfl_wq, &priv->pcm_pop_work, msecs_to_jiffies(70));
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget sof_es8316_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ sof_es8316_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_es8316_audio_map[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled Speaker and/or headphone switch.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_soc_dapm_route sof_es8316_headset_mic2_map[] = {
+ {"MIC1", NULL, "Internal Mic"},
+ {"MIC2", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route sof_es8316_headset_mic1_map[] = {
+ {"MIC2", NULL, "Internal Mic"},
+ {"MIC1", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static const struct snd_kcontrol_new sof_es8316_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static struct snd_soc_jack_pin sof_es8316_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int dmic_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, dmic_map,
+ ARRAY_SIZE(dmic_map));
+ if (ret)
+ dev_err(card->dev, "DMic map addition failed: %d\n", ret);
+
+ return ret;
+}
+
+static int sof_hdmi_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct sof_hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(runtime->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ /* dai_link id is 1:1 mapped to the PCM device */
+ pcm->device = runtime->dai_link->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &priv->hdmi_pcm_list);
+
+ return 0;
+}
+
+static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+ const struct snd_soc_dapm_route *custom_map;
+ int num_routes;
+ int ret;
+
+ snd_soc_dapm_set_idle_bias(dapm, false);
+
+ if (quirk & SOC_ES8336_HEADSET_MIC1) {
+ custom_map = sof_es8316_headset_mic1_map;
+ num_routes = ARRAY_SIZE(sof_es8316_headset_mic1_map);
+ } else {
+ custom_map = sof_es8316_headset_mic2_map;
+ num_routes = ARRAY_SIZE(sof_es8316_headset_mic2_map);
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, sof_es8316_jack_pins,
+ ARRAY_SIZE(sof_es8316_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+
+ snd_soc_component_set_jack(codec, &priv->jack, NULL);
+
+ return 0;
+}
+
+static void sof_es8316_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sof_es8336_quirk_cb(const struct dmi_system_id *id)
+{
+ quirk = (unsigned long)id->driver_data;
+
+ return 1;
+}
+
+/*
+ * this table should only be used to add GPIO or jack-detection quirks
+ * that cannot be detected from ACPI tables. The SSP and DMIC
+ * information are providing by the platform driver and are aligned
+ * with the topology used.
+ *
+ * If the GPIO support is missing, the quirk parameter can be used to
+ * enable speakers. In that case it's recommended to keep the SSP and DMIC
+ * information consistent, overriding the SSP and DMIC can only be done
+ * if the topology file is modified as well.
+ */
+static const struct dmi_system_id sof_es8336_quirk_table[] = {
+ {
+ .callback = sof_es8336_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "IP3 tech"),
+ DMI_MATCH(DMI_BOARD_NAME, "WN1"),
+ },
+ .driver_data = (void *)(SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK)
+ },
+ {
+ .callback = sof_es8336_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+ DMI_MATCH(DMI_BOARD_NAME, "BOHB-WAX9-PCB-B2"),
+ },
+ .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO |
+ SOC_ES8336_HEADSET_MIC1)
+ },
+ {}
+};
+
+static int sof_es8336_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ const int sysclk = 19200000;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1, sysclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "%s, Failed to set ES8336 SYSCLK: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* machine stream operations */
+static const struct snd_soc_ops sof_es8336_ops = {
+ .hw_params = sof_es8336_hw_params,
+ .trigger = sof_8336_trigger,
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:00:1f.3"
+ }
+};
+
+SND_SOC_DAILINK_DEF(es8336_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+
+static struct snd_soc_dai_link_component dmic_component[] = {
+ {
+ .name = "dmic-codec",
+ .dai_name = "dmic-hifi",
+ }
+};
+
+static int sof_es8336_late_probe(struct snd_soc_card *card)
+{
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+ struct sof_hdmi_pcm *pcm;
+
+ if (list_empty(&priv->hdmi_pcm_list))
+ return -ENOENT;
+
+ pcm = list_first_entry(&priv->hdmi_pcm_list, struct sof_hdmi_pcm, head);
+
+ return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
+}
+
+/* SoC card */
+static struct snd_soc_card sof_es8336_card = {
+ .name = "essx8336", /* sof- prefix added automatically */
+ .owner = THIS_MODULE,
+ .dapm_widgets = sof_es8316_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_es8316_widgets),
+ .dapm_routes = sof_es8316_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_es8316_audio_map),
+ .controls = sof_es8316_controls,
+ .num_controls = ARRAY_SIZE(sof_es8316_controls),
+ .fully_routed = true,
+ .late_probe = sof_es8336_late_probe,
+ .num_links = 1,
+};
+
+static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
+ int ssp_codec,
+ int dmic_be_num,
+ int hdmi_num)
+{
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link *links;
+ struct snd_soc_dai_link_component *idisp_components;
+ int hdmi_id_offset = 0;
+ int id = 0;
+ int i;
+
+ links = devm_kcalloc(dev, sof_es8336_card.num_links,
+ sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ cpus = devm_kcalloc(dev, sof_es8336_card.num_links,
+ sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!links || !cpus)
+ goto devm_err;
+
+ /* codec SSP */
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d-Codec", ssp_codec);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+ links[id].codecs = es8336_codec;
+ links[id].num_codecs = ARRAY_SIZE(es8336_codec);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_es8316_init;
+ links[id].exit = sof_es8316_exit;
+ links[id].ops = &sof_es8336_ops;
+ links[id].nonatomic = true;
+ links[id].no_pcm = 1;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d Pin",
+ ssp_codec);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+
+ id++;
+
+ /* dmic */
+ if (dmic_be_num > 0) {
+ /* at least we have dmic01 */
+ links[id].name = "dmic01";
+ links[id].cpus = &cpus[id];
+ links[id].cpus->dai_name = "DMIC01 Pin";
+ links[id].init = dmic_init;
+ if (dmic_be_num > 1) {
+ /* set up 2 BE links at most */
+ links[id + 1].name = "dmic16k";
+ links[id + 1].cpus = &cpus[id + 1];
+ links[id + 1].cpus->dai_name = "DMIC16k Pin";
+ dmic_be_num = 2;
+ }
+ } else {
+ /* HDMI dai link starts at 3 according to current topology settings */
+ hdmi_id_offset = 2;
+ }
+
+ for (i = 0; i < dmic_be_num; i++) {
+ links[id].id = id;
+ links[id].num_cpus = 1;
+ links[id].codecs = dmic_component;
+ links[id].num_codecs = ARRAY_SIZE(dmic_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].ignore_suspend = 1;
+ links[id].capture_only = 1;
+ links[id].no_pcm = 1;
+
+ id++;
+ }
+
+ /* HDMI */
+ if (hdmi_num > 0) {
+ idisp_components = devm_kcalloc(dev,
+ hdmi_num,
+ sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!idisp_components)
+ goto devm_err;
+ }
+
+ for (i = 1; i <= hdmi_num; i++) {
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d", i);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id + hdmi_id_offset;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d Pin", i);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+
+ idisp_components[i - 1].name = "ehdaudio0D2";
+ idisp_components[i - 1].dai_name = devm_kasprintf(dev,
+ GFP_KERNEL,
+ "intel-hdmi-hifi%d",
+ i);
+ if (!idisp_components[i - 1].dai_name)
+ goto devm_err;
+
+ links[id].codecs = &idisp_components[i - 1];
+ links[id].num_codecs = 1;
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_hdmi_init;
+ links[id].playback_only = 1;
+ links[id].no_pcm = 1;
+
+ id++;
+ }
+
+ /* HDMI-In SSP */
+ if (quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) {
+ int num_of_hdmi_ssp = (quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >>
+ SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
+
+ for (i = 1; i <= num_of_hdmi_ssp; i++) {
+ int port = (i == 1 ? (quirk & SOF_HDMI_CAPTURE_1_SSP_MASK) >>
+ SOF_HDMI_CAPTURE_1_SSP_SHIFT :
+ (quirk & SOF_HDMI_CAPTURE_2_SSP_MASK) >>
+ SOF_HDMI_CAPTURE_2_SSP_SHIFT);
+
+ links[id].cpus = &cpus[id];
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d Pin", port);
+ if (!links[id].cpus->dai_name)
+ return NULL;
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port);
+ if (!links[id].name)
+ return NULL;
+ links[id].id = id + hdmi_id_offset;
+ links[id].codecs = &snd_soc_dummy_dlc;
+ links[id].num_codecs = 1;
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].capture_only = 1;
+ links[id].no_pcm = 1;
+ links[id].num_cpus = 1;
+ id++;
+ }
+ }
+
+ return links;
+
+devm_err:
+ return NULL;
+}
+
+static char soc_components[30];
+
+ /* i2c-<HID>:00 with HID being 8 chars */
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+static int sof_es8336_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct property_entry props[MAX_NO_PROPS] = {};
+ struct sof_es8336_private *priv;
+ struct fwnode_handle *fwnode;
+ struct acpi_device *adev;
+ struct snd_soc_dai_link *dai_links;
+ struct device *codec_dev;
+ const struct acpi_gpio_mapping *gpio_mapping;
+ unsigned int cnt = 0;
+ int dmic_be_num = 0;
+ int hdmi_num = 3;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ card = &sof_es8336_card;
+ card->dev = dev;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ /* check GPIO DMI quirks */
+ dmi_check_system(sof_es8336_quirk_table);
+
+ /* Use NHLT configuration only for Non-HDMI capture use case.
+ * Because more than one SSP will be enabled for HDMI capture hence wrong codec
+ * SSP will be set.
+ */
+ if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER) {
+ if (!mach->mach_params.i2s_link_mask) {
+ dev_warn(dev, "No I2S link information provided, using SSP0. This may need to be modified with the quirk module parameter\n");
+ } else {
+ /*
+ * Set configuration based on platform NHLT.
+ * In this machine driver, we can only support one SSP for the
+ * ES8336 link.
+ * In some cases multiple SSPs can be reported by NHLT, starting MSB-first
+ * seems to pick the right connection.
+ */
+ unsigned long ssp;
+
+ /* fls returns 1-based results, SSPs indices are 0-based */
+ ssp = fls(mach->mach_params.i2s_link_mask) - 1;
+
+ quirk |= ssp;
+ }
+ }
+
+ if (mach->mach_params.dmic_num)
+ quirk |= SOF_ES8336_ENABLE_DMIC;
+
+ if (quirk_override != -1) {
+ dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n",
+ quirk, quirk_override);
+ quirk = quirk_override;
+ }
+ log_quirks(dev);
+
+ if (quirk & SOF_ES8336_ENABLE_DMIC)
+ dmic_be_num = 2;
+
+ /* compute number of dai links */
+ sof_es8336_card.num_links = 1 + dmic_be_num + hdmi_num;
+
+ if (quirk & SOF_SSP_HDMI_CAPTURE_PRESENT)
+ sof_es8336_card.num_links += (quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >>
+ SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
+
+ dai_links = sof_card_dai_links_create(dev,
+ SOF_ES8336_SSP_CODEC(quirk),
+ dmic_be_num, hdmi_num);
+ if (!dai_links)
+ return -ENOMEM;
+
+ sof_es8336_card.dai_link = dai_links;
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+ dai_links[0].codecs->name = codec_name;
+
+ /* also fixup codec dai name if relevant */
+ if (!strncmp(mach->id, "ESSX8326", SND_ACPI_I2C_ID_LEN))
+ dai_links[0].codecs->dai_name = "ES8326 HiFi";
+ } else {
+ dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+ priv->codec_dev = get_device(codec_dev);
+
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_es8336_card,
+ mach->mach_params.platform);
+ if (ret) {
+ put_device(codec_dev);
+ return ret;
+ }
+
+ if (quirk & SOF_ES8336_JD_INVERTED)
+ props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted");
+
+ if (cnt) {
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode)) {
+ put_device(codec_dev);
+ return PTR_ERR(fwnode);
+ }
+
+ ret = device_add_software_node(codec_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ if (ret) {
+ put_device(codec_dev);
+ return ret;
+ }
+ }
+
+ /* get speaker enable GPIO */
+ if (quirk & SOF_ES8336_HEADPHONE_GPIO) {
+ if (quirk & SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK)
+ gpio_mapping = acpi_enable_both_gpios;
+ else
+ gpio_mapping = acpi_enable_both_gpios_rev_order;
+ } else if (quirk & SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK) {
+ gpio_mapping = acpi_speakers_enable_gpio1;
+ } else {
+ gpio_mapping = acpi_speakers_enable_gpio0;
+ }
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dev, gpio_mapping);
+ if (ret)
+ dev_warn(codec_dev, "unable to add GPIO mapping table\n");
+
+ priv->gpio_speakers = gpiod_get_optional(codec_dev, "speakers-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpio_speakers)) {
+ ret = dev_err_probe(dev, PTR_ERR(priv->gpio_speakers),
+ "could not get speakers-enable GPIO\n");
+ goto err_put_codec;
+ }
+
+ priv->gpio_headphone = gpiod_get_optional(codec_dev, "headphone-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpio_headphone)) {
+ ret = dev_err_probe(dev, PTR_ERR(priv->gpio_headphone),
+ "could not get headphone-enable GPIO\n");
+ goto err_put_codec;
+ }
+
+ INIT_LIST_HEAD(&priv->hdmi_pcm_list);
+ INIT_DELAYED_WORK(&priv->pcm_pop_work,
+ pcm_pop_work_events);
+ snd_soc_card_set_drvdata(card, priv);
+
+ if (mach->mach_params.dmic_num > 0) {
+ snprintf(soc_components, sizeof(soc_components),
+ "cfg-dmics:%d", mach->mach_params.dmic_num);
+ card->components = soc_components;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ gpiod_put(priv->gpio_speakers);
+ dev_err(dev, "snd_soc_register_card failed: %d\n", ret);
+ goto err_put_codec;
+ }
+ platform_set_drvdata(pdev, &sof_es8336_card);
+ return 0;
+
+err_put_codec:
+ device_remove_software_node(priv->codec_dev);
+ put_device(codec_dev);
+ return ret;
+}
+
+static void sof_es8336_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
+
+ cancel_delayed_work_sync(&priv->pcm_pop_work);
+ gpiod_put(priv->gpio_speakers);
+ device_remove_software_node(priv->codec_dev);
+ put_device(priv->codec_dev);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "sof-essx8336", /* default quirk == 0 */
+ },
+ {
+ .name = "adl_es83x6_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
+ SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
+ SOF_ES8336_JD_INVERTED),
+ },
+ {
+ .name = "rpl_es83x6_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
+ SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
+ SOF_ES8336_JD_INVERTED),
+ },
+ {
+ .name = "mtl_es83x6_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
+ SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
+ SOF_ES8336_JD_INVERTED),
+ },
+ {
+ .name = "arl_es83x6_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
+ SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
+ SOF_ES8336_JD_INVERTED),
+ },
+ {
+ .name = "ptl_es83x6_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
+ SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
+ SOF_ES8336_JD_INVERTED),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver sof_es8336_driver = {
+ .driver = {
+ .name = "sof-essx8336",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = sof_es8336_probe,
+ .remove = sof_es8336_remove,
+ .id_table = board_ids,
+};
+module_platform_driver(sof_es8336_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) SOF + ES8336 Machine driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_INTEL_HDA_DSP_COMMON");
diff --git a/sound/soc/intel/boards/sof_hdmi_common.h b/sound/soc/intel/boards/sof_hdmi_common.h
new file mode 100644
index 000000000000..1573e089c0e5
--- /dev/null
+++ b/sound/soc/intel/boards/sof_hdmi_common.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2023 Intel Corporation.
+ */
+
+#ifndef __SOF_HDMI_COMMON_H
+#define __SOF_HDMI_COMMON_H
+
+#include <sound/soc.h>
+
+#define IDISP_CODEC_MASK 0x4
+
+/*
+ * sof_hdmi_private: data for Intel HDMI dai link (idisp) initialization
+ *
+ * @hdmi_comp: ASoC component of idisp codec
+ * @idisp_codec: true to indicate idisp codec is present
+ */
+struct sof_hdmi_private {
+ struct snd_soc_component *hdmi_comp;
+ bool idisp_codec;
+};
+
+#endif /* __SOF_HDMI_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c
new file mode 100644
index 000000000000..c3d0f697ff8d
--- /dev/null
+++ b/sound/soc/intel/boards/sof_maxim_common.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/sof.h>
+#include <uapi/sound/asound.h>
+#include "../common/soc-intel-quirks.h"
+#include "sof_maxim_common.h"
+
+/*
+ * Common structures and functions
+ */
+static const struct snd_kcontrol_new maxim_2spk_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+
+};
+
+static const struct snd_soc_dapm_widget maxim_2spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+/* helper function to get the number of specific codec */
+static unsigned int get_num_codecs(const char *hid)
+{
+ struct acpi_device *adev;
+ unsigned int dev_num = 0;
+
+ for_each_acpi_dev_match(adev, hid, NULL, -1)
+ dev_num++;
+
+ return dev_num;
+}
+
+/*
+ * Maxim MAX98373
+ */
+#define MAX_98373_PIN_NAME 16
+
+static const struct snd_soc_dapm_route max_98373_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max_98373_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98373_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98373_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static struct snd_soc_dai_link_component max_98373_components[] = {
+ { /* For Right */
+ .name = MAX_98373_DEV0_NAME,
+ .dai_name = MAX_98373_CODEC_DAI,
+ },
+ { /* For Left */
+ .name = MAX_98373_DEV1_NAME,
+ .dai_name = MAX_98373_CODEC_DAI,
+ },
+};
+
+/*
+ * According to the definition of 'DAI Sel Mux' mixer in max98373.c, rx mask
+ * should choose two channels from TDM slots, the LSB of rx mask is left channel
+ * and the other one is right channel.
+ */
+static const struct {
+ unsigned int rx;
+} max_98373_tdm_mask[] = {
+ {.rx = 0x3},
+ {.rx = 0x3},
+};
+
+/*
+ * The tx mask indicates which channel(s) contains output IV-sense data and
+ * others should set to Hi-Z. Here we get the channel number from codec's ACPI
+ * device property "maxim,vmon-slot-no" and "maxim,imon-slot-no" to generate the
+ * mask. Refer to the max98373_slot_config() function in max98373.c codec driver.
+ */
+static unsigned int max_98373_get_tx_mask(struct device *dev)
+{
+ int vmon_slot;
+ int imon_slot;
+
+ if (device_property_read_u32(dev, "maxim,vmon-slot-no", &vmon_slot))
+ vmon_slot = 0;
+
+ if (device_property_read_u32(dev, "maxim,imon-slot-no", &imon_slot))
+ imon_slot = 1;
+
+ dev_dbg(dev, "vmon_slot %d imon_slot %d\n", vmon_slot, imon_slot);
+
+ return (0x1 << vmon_slot) | (0x1 << imon_slot);
+}
+
+static int max_98373_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_dai *codec_dai;
+ int i;
+ int tdm_slots;
+ unsigned int tx_mask;
+ unsigned int tx_mask_used = 0x0;
+ int ret = 0;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (i >= ARRAY_SIZE(max_98373_tdm_mask)) {
+ dev_err(codec_dai->dev, "only 2 amps are supported\n");
+ return -EINVAL;
+ }
+
+ switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* get the tplg configured tdm slot number */
+ tdm_slots = sof_dai_get_tdm_slots(rtd);
+ if (tdm_slots <= 0) {
+ dev_err(rtd->dev, "invalid tdm slots %d\n",
+ tdm_slots);
+ return -EINVAL;
+ }
+
+ /* get the tx mask from ACPI device properties */
+ tx_mask = max_98373_get_tx_mask(codec_dai->dev);
+ if (!tx_mask)
+ return -EINVAL;
+
+ if (tx_mask & tx_mask_used) {
+ dev_err(codec_dai->dev, "invalid tx mask 0x%x, used 0x%x\n",
+ tx_mask, tx_mask_used);
+ return -EINVAL;
+ }
+
+ tx_mask_used |= tx_mask;
+
+ /*
+ * check if tdm slot number is too small for channel
+ * allocation
+ */
+ if (fls(tx_mask) > tdm_slots) {
+ dev_err(codec_dai->dev, "slot mismatch, tx %d slots %d\n",
+ fls(tx_mask), tdm_slots);
+ return -EINVAL;
+ }
+
+ if (fls(max_98373_tdm_mask[i].rx) > tdm_slots) {
+ dev_err(codec_dai->dev, "slot mismatch, rx %d slots %d\n",
+ fls(max_98373_tdm_mask[i].rx), tdm_slots);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec_dai->dev, "set tdm slot: tx 0x%x rx 0x%x slots %d width %d\n",
+ tx_mask, max_98373_tdm_mask[i].rx,
+ tdm_slots, params_width(params));
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask,
+ max_98373_tdm_mask[i].rx,
+ tdm_slots,
+ params_width(params));
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_dbg(codec_dai->dev, "codec is in I2S mode\n");
+ break;
+ }
+ }
+ return 0;
+}
+
+static int max_98373_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
+ int j;
+ int ret = 0;
+
+ /* set spk pin by playback only */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cpu_dai->component);
+ char pin_name[MAX_98373_PIN_NAME];
+
+ snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk",
+ codec_dai->component->name_prefix);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = snd_soc_dapm_enable_pin(dapm, pin_name);
+ if (!ret)
+ snd_soc_dapm_sync(dapm);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = snd_soc_dapm_disable_pin(dapm, pin_name);
+ if (!ret)
+ snd_soc_dapm_sync(dapm);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops max_98373_ops = {
+ .hw_params = max_98373_hw_params,
+ .trigger = max_98373_trigger,
+};
+
+static int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ unsigned int num_codecs = get_num_codecs(MAX_98373_ACPI_HID);
+ int ret;
+
+ switch (num_codecs) {
+ case 2:
+ ret = snd_soc_dapm_new_controls(dapm, maxim_2spk_widgets,
+ ARRAY_SIZE(maxim_2spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add max98373 widgets, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, maxim_2spk_kcontrols,
+ ARRAY_SIZE(maxim_2spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add max98373 kcontrols, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, max_98373_dapm_routes,
+ ARRAY_SIZE(max_98373_dapm_routes));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add max98373 routes, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(rtd->dev, "max98373: invalid num_codecs %d\n", num_codecs);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void max_98373_dai_link(struct device *dev, struct snd_soc_dai_link *link)
+{
+ link->codecs = max_98373_components;
+ link->num_codecs = ARRAY_SIZE(max_98373_components);
+ link->init = max_98373_spk_codec_init;
+ link->ops = &max_98373_ops;
+}
+EXPORT_SYMBOL_NS(max_98373_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
+
+void max_98373_set_codec_conf(struct snd_soc_card *card)
+{
+ card->codec_conf = max_98373_codec_conf;
+ card->num_configs = ARRAY_SIZE(max_98373_codec_conf);
+}
+EXPORT_SYMBOL_NS(max_98373_set_codec_conf, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
+
+/*
+ * Maxim MAX98390
+ */
+static const struct snd_soc_dapm_route max_98390_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("TL Spk"),
+ SOC_DAPM_PIN_SWITCH("TR Spk"),
+};
+
+static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("TL Spk", NULL),
+ SND_SOC_DAPM_SPK("TR Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = {
+ /* Tweeter speaker */
+ { "TL Spk", NULL, "Tweeter Left BE_OUT" },
+ { "TR Spk", NULL, "Tweeter Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max_98390_cml_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_codec_conf max_98390_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME),
+ .name_prefix = "Tweeter Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME),
+ .name_prefix = "Tweeter Left",
+ },
+};
+
+static struct snd_soc_dai_link_component max_98390_components[] = {
+ {
+ .name = MAX_98390_DEV0_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV1_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV2_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+ {
+ .name = MAX_98390_DEV3_NAME,
+ .dai_name = MAX_98390_CODEC_DAI,
+ },
+};
+
+static const struct {
+ unsigned int tx;
+ unsigned int rx;
+} max_98390_tdm_mask[] = {
+ {.tx = 0x01, .rx = 0x3},
+ {.tx = 0x02, .rx = 0x3},
+ {.tx = 0x04, .rx = 0x3},
+ {.tx = 0x08, .rx = 0x3},
+};
+
+static int max_98390_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (i >= ARRAY_SIZE(max_98390_tdm_mask)) {
+ dev_err(codec_dai->dev, "invalid codec index %d\n", i);
+ return -ENODEV;
+ }
+
+ switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* 4-slot TDM */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ max_98390_tdm_mask[i].tx,
+ max_98390_tdm_mask[i].rx,
+ 4,
+ params_width(params));
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_dbg(codec_dai->dev, "codec is in I2S mode\n");
+ break;
+ }
+ }
+ return 0;
+}
+
+static int max_98390_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);
+ int ret;
+
+ switch (num_codecs) {
+ case 4:
+ /* add widgets/controls/dapm for tweeter speakers */
+ ret = snd_soc_dapm_new_controls(dapm, max_98390_tt_dapm_widgets,
+ ARRAY_SIZE(max_98390_tt_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add tweeter dapm widgets, ret %d\n",
+ ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols,
+ ARRAY_SIZE(max_98390_tt_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add tweeter controls, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, max_98390_tt_dapm_routes,
+ ARRAY_SIZE(max_98390_tt_dapm_routes));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add tweeter dapm routes, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ fallthrough;
+ case 2:
+ /* add regular speakers dapm route */
+ ret = snd_soc_dapm_new_controls(dapm, maxim_2spk_widgets,
+ ARRAY_SIZE(maxim_2spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add max98390 woofer widgets, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, maxim_2spk_kcontrols,
+ ARRAY_SIZE(maxim_2spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add max98390 woofer kcontrols, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, max_98390_dapm_routes,
+ ARRAY_SIZE(max_98390_dapm_routes));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(rtd->dev, "invalid codec number %d\n", num_codecs);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops max_98390_ops = {
+ .hw_params = max_98390_hw_params,
+};
+
+void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link)
+{
+ unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);
+
+ link->codecs = max_98390_components;
+
+ switch (num_codecs) {
+ case 2:
+ case 4:
+ link->num_codecs = num_codecs;
+ break;
+ default:
+ dev_err(dev, "invalid codec number %d for %s\n", num_codecs,
+ MAX_98390_ACPI_HID);
+ break;
+ }
+
+ link->init = max_98390_init;
+ link->ops = &max_98390_ops;
+}
+EXPORT_SYMBOL_NS(max_98390_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
+
+void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card)
+{
+ unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);
+
+ card->codec_conf = max_98390_codec_conf;
+
+ switch (num_codecs) {
+ case 2:
+ if (soc_intel_is_cml())
+ card->codec_conf = max_98390_cml_codec_conf;
+
+ fallthrough;
+ case 4:
+ card->num_configs = num_codecs;
+ break;
+ default:
+ dev_err(dev, "invalid codec number %d for %s\n", num_codecs,
+ MAX_98390_ACPI_HID);
+ break;
+ }
+}
+EXPORT_SYMBOL_NS(max_98390_set_codec_conf, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
+
+/*
+ * Maxim MAX98357A/MAX98360A
+ */
+static const struct snd_kcontrol_new max_98357a_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = {
+ /* speaker */
+ {"Spk", NULL, "Speaker"},
+};
+
+static struct snd_soc_dai_link_component max_98357a_components[] = {
+ {
+ .name = MAX_98357A_DEV0_NAME,
+ .dai_name = MAX_98357A_CODEC_DAI,
+ }
+};
+
+static struct snd_soc_dai_link_component max_98360a_components[] = {
+ {
+ .name = MAX_98360A_DEV0_NAME,
+ .dai_name = MAX_98357A_CODEC_DAI,
+ }
+};
+
+static int max_98357a_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, max_98357a_dapm_widgets,
+ ARRAY_SIZE(max_98357a_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max_98357a_kcontrols,
+ ARRAY_SIZE(max_98357a_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, max_98357a_dapm_routes,
+ ARRAY_SIZE(max_98357a_dapm_routes));
+
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+void max_98357a_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = max_98357a_components;
+ link->num_codecs = ARRAY_SIZE(max_98357a_components);
+ link->init = max_98357a_init;
+}
+EXPORT_SYMBOL_NS(max_98357a_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
+
+void max_98360a_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = max_98360a_components;
+ link->num_codecs = ARRAY_SIZE(max_98360a_components);
+ link->init = max_98357a_init;
+}
+EXPORT_SYMBOL_NS(max_98360a_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h
new file mode 100644
index 000000000000..3d34c7dae6f5
--- /dev/null
+++ b/sound/soc/intel/boards/sof_maxim_common.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2020 Intel Corporation.
+ */
+
+/*
+ * This file defines data structures used in Machine Driver for Intel
+ * platforms with Maxim Codecs.
+ */
+#ifndef __SOF_MAXIM_COMMON_H
+#define __SOF_MAXIM_COMMON_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Maxim MAX98373
+ */
+#define MAX_98373_CODEC_DAI "max98373-aif1"
+#define MAX_98373_DEV0_NAME "i2c-" MAX_98373_ACPI_HID ":00"
+#define MAX_98373_DEV1_NAME "i2c-" MAX_98373_ACPI_HID ":01"
+
+void max_98373_dai_link(struct device *dev, struct snd_soc_dai_link *link);
+void max_98373_set_codec_conf(struct snd_soc_card *card);
+
+/*
+ * Maxim MAX98390
+ */
+#define MAX_98390_CODEC_DAI "max98390-aif1"
+#define MAX_98390_DEV0_NAME "i2c-" MAX_98390_ACPI_HID ":00"
+#define MAX_98390_DEV1_NAME "i2c-" MAX_98390_ACPI_HID ":01"
+#define MAX_98390_DEV2_NAME "i2c-" MAX_98390_ACPI_HID ":02"
+#define MAX_98390_DEV3_NAME "i2c-" MAX_98390_ACPI_HID ":03"
+
+void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link);
+void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card);
+
+/*
+ * Maxim MAX98357A/MAX98360A
+ */
+#define MAX_98357A_CODEC_DAI "HiFi"
+#define MAX_98357A_DEV0_NAME MAX_98357A_ACPI_HID ":00"
+#define MAX_98360A_DEV0_NAME MAX_98360A_ACPI_HID ":00"
+
+void max_98357a_dai_link(struct snd_soc_dai_link *link);
+void max_98360a_dai_link(struct snd_soc_dai_link *link);
+
+#endif /* __SOF_MAXIM_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c
new file mode 100644
index 000000000000..15ba6f5c697c
--- /dev/null
+++ b/sound/soc/intel/boards/sof_nau8825.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2021 Intel Corporation.
+// Copyright(c) 2021 Nuvoton Corporation.
+
+/*
+ * Intel SOF Machine Driver with Nuvoton headphone codec NAU8825
+ * and speaker codec RT1019P MAX98360a or MAX98373
+ */
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/sof.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/nau8825.h"
+#include "../common/soc-intel-quirks.h"
+#include "sof_board_helpers.h"
+#include "sof_realtek_common.h"
+#include "sof_maxim_common.h"
+#include "sof_nuvoton_common.h"
+
+static unsigned long sof_nau8825_quirk = SOF_SSP_PORT_CODEC(0);
+
+static struct snd_soc_jack_pin jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_jack *jack = &ctx->headset_jack;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ jack_pins,
+ ARRAY_SIZE(jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+};
+
+static void sof_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sof_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int clk_freq, ret;
+
+ clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
+
+ if (clk_freq <= 0) {
+ dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq);
+ return -EINVAL;
+ }
+
+ /* Configure clock for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
+ return ret;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops sof_nau8825_ops = {
+ .hw_params = sof_nau8825_hw_params,
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int err;
+
+ if (ctx->amp_type == CODEC_MAX98373) {
+ /* Disable Left and Right Spk pin after boot */
+ snd_soc_dapm_disable_pin(dapm, "Left Spk");
+ snd_soc_dapm_disable_pin(dapm, "Right Spk");
+ err = snd_soc_dapm_sync(dapm);
+ if (err < 0)
+ return err;
+ }
+
+ return sof_intel_board_card_late_probe(card);
+}
+
+static const struct snd_kcontrol_new sof_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* other jacks */
+ { "MIC", NULL, "Headset Mic" },
+};
+
+/* sof audio machine driver for nau8825 codec */
+static struct snd_soc_card sof_audio_card_nau8825 = {
+ .name = "nau8825", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE,
+ .controls = sof_controls,
+ .num_controls = ARRAY_SIZE(sof_controls),
+ .dapm_widgets = sof_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
+ .dapm_routes = sof_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_map),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+static struct snd_soc_dai_link_component nau8825_component[] = {
+ {
+ .name = "i2c-10508825:00",
+ .dai_name = "nau8825-hifi",
+ }
+};
+
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ int ret;
+
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
+
+ if (!ctx->codec_link) {
+ dev_err(dev, "codec link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for headphone codec */
+ ctx->codec_link->codecs = nau8825_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(nau8825_component);
+ ctx->codec_link->init = sof_nau8825_codec_init;
+ ctx->codec_link->exit = sof_nau8825_codec_exit;
+ ctx->codec_link->ops = &sof_nau8825_ops;
+
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
+
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98373:
+ max_98373_dai_link(dev, ctx->amp_link);
+ break;
+ case CODEC_NAU8318:
+ nau8318_set_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1015P:
+ sof_rt1015p_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1019P:
+ sof_rt1019p_dai_link(ctx->amp_link);
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_card_private *ctx;
+ int ret;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk);
+
+ /* initialize ctx with board quirk */
+ ctx = sof_intel_board_get_ctx(&pdev->dev, sof_nau8825_quirk);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->hdmi.idisp_codec = true;
+
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_nau8825, ctx);
+ if (ret)
+ return ret;
+
+ /* update codec_conf */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98373:
+ max_98373_set_codec_conf(&sof_audio_card_nau8825);
+ break;
+ case CODEC_RT1015P:
+ sof_rt1015p_codec_conf(&sof_audio_card_nau8825);
+ break;
+ case CODEC_MAX98360A:
+ case CODEC_NAU8318:
+ case CODEC_RT1019P:
+ case CODEC_NONE:
+ /* no codec conf required */
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ sof_audio_card_nau8825.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev,
+ &sof_audio_card_nau8825);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "adl_rt1019p_8825",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .name = "adl_nau8825_def",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "rpl_nau8825_def",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "mtl_nau8825_def",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(0) |
+ SOF_SSP_PORT_BT_OFFLOAD(1) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver sof_audio = {
+ .probe = sof_audio_probe,
+ .driver = {
+ .name = "sof_nau8825",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(sof_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_NUVOTON_COMMON");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON");
diff --git a/sound/soc/intel/boards/sof_nuvoton_common.c b/sound/soc/intel/boards/sof_nuvoton_common.c
new file mode 100644
index 000000000000..b09ecbab2fc9
--- /dev/null
+++ b/sound/soc/intel/boards/sof_nuvoton_common.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file defines data structures and functions used in Machine
+ * Driver for Intel platforms with Nuvoton Codecs.
+ *
+ * Copyright 2023 Intel Corporation.
+ */
+#include <linux/module.h>
+#include <sound/sof.h>
+#include "sof_nuvoton_common.h"
+
+/*
+ * Nuvoton NAU8318
+ */
+static const struct snd_kcontrol_new nau8318_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget nau8318_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route nau8318_routes[] = {
+ { "Spk", NULL, "Speaker" },
+};
+
+static struct snd_soc_dai_link_component nau8318_components[] = {
+ {
+ .name = NAU8318_DEV0_NAME,
+ .dai_name = NAU8318_CODEC_DAI,
+ }
+};
+
+static int nau8318_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, nau8318_widgets,
+ ARRAY_SIZE(nau8318_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add nau8318 widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8318_kcontrols,
+ ARRAY_SIZE(nau8318_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add nau8318 kcontrols, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, nau8318_routes,
+ ARRAY_SIZE(nau8318_routes));
+
+ if (ret) {
+ dev_err(rtd->dev, "fail to add nau8318 routes, ret %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+void nau8318_set_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = nau8318_components;
+ link->num_codecs = ARRAY_SIZE(nau8318_components);
+ link->init = nau8318_init;
+}
+EXPORT_SYMBOL_NS(nau8318_set_dai_link, "SND_SOC_INTEL_SOF_NUVOTON_COMMON");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Nuvoton helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_nuvoton_common.h b/sound/soc/intel/boards/sof_nuvoton_common.h
new file mode 100644
index 000000000000..8a0f283260e7
--- /dev/null
+++ b/sound/soc/intel/boards/sof_nuvoton_common.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This file defines data structures used in Machine Driver for Intel
+ * platforms with Nuvoton Codecs.
+ *
+ * Copyright 2023 Intel Corporation.
+ */
+#ifndef __SOF_NUVOTON_COMMON_H
+#define __SOF_NUVOTON_COMMON_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Nuvoton NAU8318
+ */
+#define NAU8318_CODEC_DAI "nau8315-hifi"
+#define NAU8318_DEV0_NAME "i2c-" NAU8318_ACPI_HID ":00"
+
+void nau8318_set_dai_link(struct snd_soc_dai_link *link);
+
+#endif /* __SOF_NUVOTON_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_pcm512x.c b/sound/soc/intel/boards/sof_pcm512x.c
new file mode 100644
index 000000000000..359559b6175b
--- /dev/null
+++ b/sound/soc/intel/boards/sof_pcm512x.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2018-2020 Intel Corporation.
+
+/*
+ * Intel SOF Machine Driver for Intel platforms with TI PCM512x codec,
+ * e.g. Up or Up2 with Hifiberry DAC+ HAT
+ */
+#include <linux/clk.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/pcm512x.h"
+#include "../common/soc-intel-quirks.h"
+#include "hda_dsp_common.h"
+
+#define NAME_SIZE 32
+
+#define SOF_PCM512X_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0))
+#define SOF_PCM512X_SSP_CODEC_MASK (GENMASK(3, 0))
+#define SOF_PCM512X_ENABLE_SSP_CAPTURE BIT(4)
+#define SOF_PCM512X_ENABLE_DMIC BIT(5)
+
+#define IDISP_CODEC_MASK 0x4
+
+/* Default: SSP5 */
+static unsigned long sof_pcm512x_quirk =
+ SOF_PCM512X_SSP_CODEC(5) |
+ SOF_PCM512X_ENABLE_SSP_CAPTURE |
+ SOF_PCM512X_ENABLE_DMIC;
+
+static bool is_legacy_cpu;
+
+struct sof_hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ int device;
+};
+
+struct sof_card_private {
+ struct list_head hdmi_pcm_list;
+ bool idisp_codec;
+};
+
+static int sof_pcm512x_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_pcm512x_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_pcm512x_quirk_table[] = {
+ {
+ .callback = sof_pcm512x_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"),
+ },
+ .driver_data = (void *)(SOF_PCM512X_SSP_CODEC(2)),
+ },
+ {}
+};
+
+static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct sof_hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ /* dai_link id is 1:1 mapped to the PCM device */
+ pcm->device = rtd->dai_link->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+ return 0;
+}
+
+static int sof_pcm512x_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08);
+ snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
+ snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
+ 0x08, 0x08);
+
+ return 0;
+}
+
+static int aif1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
+ 0x08, 0x08);
+
+ return 0;
+}
+
+static void aif1_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
+ 0x08, 0x00);
+}
+
+static const struct snd_soc_ops sof_pcm512x_ops = {
+ .startup = aif1_startup,
+ .shutdown = aif1_shutdown,
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:00:1f.3"
+ }
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct sof_hdmi_pcm *pcm;
+
+ /* HDMI is not supported by SOF on Baytrail/CherryTrail */
+ if (is_legacy_cpu)
+ return 0;
+
+ if (list_empty(&ctx->hdmi_pcm_list))
+ return -EINVAL;
+
+ if (!ctx->idisp_codec)
+ return 0;
+
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
+
+ return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
+}
+
+static const struct snd_kcontrol_new sof_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_map[] = {
+ /* Speaker */
+ {"Ext Spk", NULL, "OUTR"},
+ {"Ext Spk", NULL, "OUTL"},
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static int dmic_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, dmic_map,
+ ARRAY_SIZE(dmic_map));
+
+ if (ret)
+ dev_err(card->dev, "DMic map addition failed: %d\n", ret);
+
+ return ret;
+}
+
+/* sof audio machine driver for pcm512x codec */
+static struct snd_soc_card sof_audio_card_pcm512x = {
+ .name = "pcm512x",
+ .owner = THIS_MODULE,
+ .controls = sof_controls,
+ .num_controls = ARRAY_SIZE(sof_controls),
+ .dapm_widgets = sof_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
+ .dapm_routes = sof_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_map),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+SND_SOC_DAILINK_DEF(pcm512x_component,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-104C5122:00", "pcm512x-hifi")));
+SND_SOC_DAILINK_DEF(dmic_component,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+
+static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
+ int ssp_codec,
+ int dmic_be_num,
+ int hdmi_num,
+ bool idisp_codec)
+{
+ struct snd_soc_dai_link_component *idisp_components;
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link *links;
+ int i, id = 0;
+
+ links = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,
+ sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ cpus = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,
+ sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!links || !cpus)
+ goto devm_err;
+
+ /* codec SSP */
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d-Codec", ssp_codec);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+ links[id].codecs = pcm512x_component;
+ links[id].num_codecs = ARRAY_SIZE(pcm512x_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_pcm512x_codec_init;
+ links[id].ops = &sof_pcm512x_ops;
+ /*
+ * capture only supported with specific versions of the Hifiberry DAC+
+ */
+ if (!(sof_pcm512x_quirk & SOF_PCM512X_ENABLE_SSP_CAPTURE))
+ links[id].playback_only = 1;
+ links[id].no_pcm = 1;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+ if (is_legacy_cpu) {
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "ssp%d-port",
+ ssp_codec);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+ } else {
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d Pin",
+ ssp_codec);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+ }
+ id++;
+
+ /* dmic */
+ if (dmic_be_num > 0) {
+ /* at least we have dmic01 */
+ links[id].name = "dmic01";
+ links[id].cpus = &cpus[id];
+ links[id].cpus->dai_name = "DMIC01 Pin";
+ links[id].init = dmic_init;
+ if (dmic_be_num > 1) {
+ /* set up 2 BE links at most */
+ links[id + 1].name = "dmic16k";
+ links[id + 1].cpus = &cpus[id + 1];
+ links[id + 1].cpus->dai_name = "DMIC16k Pin";
+ dmic_be_num = 2;
+ }
+ }
+
+ for (i = 0; i < dmic_be_num; i++) {
+ links[id].id = id;
+ links[id].num_cpus = 1;
+ links[id].codecs = dmic_component;
+ links[id].num_codecs = ARRAY_SIZE(dmic_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].ignore_suspend = 1;
+ links[id].capture_only = 1;
+ links[id].no_pcm = 1;
+ id++;
+ }
+
+ /* HDMI */
+ if (hdmi_num > 0) {
+ idisp_components = devm_kcalloc(dev, hdmi_num,
+ sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!idisp_components)
+ goto devm_err;
+ }
+ for (i = 1; i <= hdmi_num; i++) {
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d", i);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d Pin", i);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+
+ /*
+ * topology cannot be loaded if codec is missing, so
+ * use the dummy codec if needed
+ */
+ if (idisp_codec) {
+ idisp_components[i - 1].name = "ehdaudio0D2";
+ idisp_components[i - 1].dai_name =
+ devm_kasprintf(dev, GFP_KERNEL,
+ "intel-hdmi-hifi%d", i);
+ } else {
+ idisp_components[i - 1] = snd_soc_dummy_dlc;
+ }
+ if (!idisp_components[i - 1].dai_name)
+ goto devm_err;
+
+ links[id].codecs = &idisp_components[i - 1];
+ links[id].num_codecs = 1;
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_hdmi_init;
+ links[id].playback_only = 1;
+ links[id].no_pcm = 1;
+ id++;
+ }
+
+ return links;
+devm_err:
+ return NULL;
+}
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct snd_soc_dai_link *dai_links;
+ struct sof_card_private *ctx;
+ int dmic_be_num, hdmi_num;
+ int ret, ssp_codec;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ hdmi_num = 0;
+ if (soc_intel_is_byt() || soc_intel_is_cht()) {
+ is_legacy_cpu = true;
+ dmic_be_num = 0;
+ /* default quirk for legacy cpu */
+ sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(2);
+ } else {
+ dmic_be_num = 2;
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->idisp_codec = true;
+
+ /* links are always present in topology */
+ hdmi_num = 3;
+ }
+
+ dmi_check_system(sof_pcm512x_quirk_table);
+
+ dev_dbg(&pdev->dev, "sof_pcm512x_quirk = %lx\n", sof_pcm512x_quirk);
+
+ ssp_codec = sof_pcm512x_quirk & SOF_PCM512X_SSP_CODEC_MASK;
+
+ if (!(sof_pcm512x_quirk & SOF_PCM512X_ENABLE_DMIC))
+ dmic_be_num = 0;
+
+ /* compute number of dai links */
+ sof_audio_card_pcm512x.num_links = 1 + dmic_be_num + hdmi_num;
+
+ dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec,
+ dmic_be_num, hdmi_num,
+ ctx->idisp_codec);
+ if (!dai_links)
+ return -ENOMEM;
+
+ sof_audio_card_pcm512x.dai_link = dai_links;
+
+ INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
+ sof_audio_card_pcm512x.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_pcm512x,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&sof_audio_card_pcm512x, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev,
+ &sof_audio_card_pcm512x);
+}
+
+static void sof_pcm512x_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct snd_soc_component *component;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, pcm512x_component[0].name)) {
+ snd_soc_component_set_jack(component, NULL, NULL);
+ break;
+ }
+ }
+}
+
+static struct platform_driver sof_audio = {
+ .probe = sof_audio_probe,
+ .remove = sof_pcm512x_remove,
+ .driver = {
+ .name = "sof_pcm512x",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(sof_audio)
+
+MODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sof_pcm512x");
+MODULE_IMPORT_NS("SND_SOC_INTEL_HDA_DSP_COMMON");
diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c
new file mode 100644
index 000000000000..835186cf04d0
--- /dev/null
+++ b/sound/soc/intel/boards/sof_realtek_common.c
@@ -0,0 +1,689 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/sof.h>
+#include <uapi/sound/asound.h>
+#include "../../codecs/rt1011.h"
+#include "../../codecs/rt1015.h"
+#include "../../codecs/rt1308.h"
+#include "../common/soc-intel-quirks.h"
+#include "sof_realtek_common.h"
+
+/*
+ * Common structures and functions
+ */
+static const struct snd_kcontrol_new realtek_2spk_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+
+};
+
+static const struct snd_soc_dapm_widget realtek_2spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_kcontrol_new realtek_4spk_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("WL Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("WR Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("TL Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("TR Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget realtek_4spk_widgets[] = {
+ SND_SOC_DAPM_SPK("WL Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("WR Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("TL Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("TR Ext Spk", NULL),
+};
+
+/* helper function to get the number of specific codec */
+static unsigned int get_num_codecs(const char *hid)
+{
+ struct acpi_device *adev;
+ unsigned int dev_num = 0;
+
+ for_each_acpi_dev_match(adev, hid, NULL, -1)
+ dev_num++;
+
+ return dev_num;
+}
+
+/*
+ * Realtek ALC1011
+ */
+static const struct snd_soc_dapm_route speaker_map_lr[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
+
+static const struct snd_soc_dapm_route rt1011_4spk_routes[] = {
+ {"WL Ext Spk", NULL, "WL SPO" },
+ {"WR Ext Spk", NULL, "WR SPO" },
+ {"TL Ext Spk", NULL, "TL SPO" },
+ {"TR Ext Spk", NULL, "TR SPO" },
+};
+
+static struct snd_soc_codec_conf rt1011_2spk_codec_confs[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_codec_conf rt1011_4spk_codec_confs[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
+ .name_prefix = "WL",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
+ .name_prefix = "WR",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV2_NAME),
+ .name_prefix = "TL",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV3_NAME),
+ .name_prefix = "TR",
+ },
+};
+
+static struct snd_soc_dai_link_component rt1011_dai_link_components[] = {
+ {
+ .name = RT1011_DEV0_NAME,
+ .dai_name = RT1011_CODEC_DAI,
+ },
+ {
+ .name = RT1011_DEV1_NAME,
+ .dai_name = RT1011_CODEC_DAI,
+ },
+ {
+ .name = RT1011_DEV2_NAME,
+ .dai_name = RT1011_CODEC_DAI,
+ },
+ {
+ .name = RT1011_DEV3_NAME,
+ .dai_name = RT1011_CODEC_DAI,
+ },
+};
+
+static const struct {
+ unsigned int tx;
+ unsigned int rx;
+} rt1011_tdm_mask[] = {
+ {.tx = 0x4, .rx = 0x1},
+ {.tx = 0x8, .rx = 0x2},
+ {.tx = 0x1, .rx = 0x1},
+ {.tx = 0x2, .rx = 0x2},
+};
+
+static int rt1011_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int srate, i, ret = 0;
+
+ srate = params_rate(params);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ /* 100 Fs to drive 24 bit data */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
+ 100 * srate, 256 * srate);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set pll, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1011_FS_SYS_PRE_S_PLL1,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set sysclk, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ if (i >= ARRAY_SIZE(rt1011_tdm_mask)) {
+ dev_err(codec_dai->dev, "invalid codec index %d\n",
+ i);
+ return -ENODEV;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, rt1011_tdm_mask[i].tx,
+ rt1011_tdm_mask[i].rx, 4,
+ params_width(params));
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops rt1011_ops = {
+ .hw_params = rt1011_hw_params,
+};
+
+static int rt1011_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ unsigned int num_codecs = get_num_codecs(RT1011_ACPI_HID);
+ int ret;
+
+ switch (num_codecs) {
+ case 2:
+ if (!soc_intel_is_cml()) {
+ ret = snd_soc_dapm_new_controls(dapm, realtek_2spk_widgets,
+ ARRAY_SIZE(realtek_2spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1011 widgets, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, realtek_2spk_kcontrols,
+ ARRAY_SIZE(realtek_2spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1011 kcontrols, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, speaker_map_lr,
+ ARRAY_SIZE(speaker_map_lr));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1011 routes, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ break;
+ }
+
+ /*
+ * register speaker widgets "WL Ext Spk" and "WR Ext Spk" to
+ * keep backward compatible with cml devices
+ */
+ fallthrough;
+ case 4:
+ ret = snd_soc_dapm_new_controls(dapm, realtek_4spk_widgets, num_codecs);
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1011 widgets, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, realtek_4spk_kcontrols, num_codecs);
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1011 controls, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, rt1011_4spk_routes, num_codecs);
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1011 routes, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(rtd->dev, "rt1011: invalid num_codecs %d\n", num_codecs);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void sof_rt1011_dai_link(struct device *dev, struct snd_soc_dai_link *link)
+{
+ unsigned int num_codecs = get_num_codecs(RT1011_ACPI_HID);
+
+ link->codecs = rt1011_dai_link_components;
+
+ switch (num_codecs) {
+ case 2:
+ case 4:
+ link->num_codecs = num_codecs;
+ break;
+ default:
+ dev_err(dev, "rt1011: invalid num_codecs %d\n", num_codecs);
+ break;
+ }
+
+ link->init = rt1011_init;
+ link->ops = &rt1011_ops;
+}
+EXPORT_SYMBOL_NS(sof_rt1011_dai_link, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+void sof_rt1011_codec_conf(struct device *dev, struct snd_soc_card *card)
+{
+ unsigned int num_codecs = get_num_codecs(RT1011_ACPI_HID);
+
+ switch (num_codecs) {
+ case 2:
+ if (soc_intel_is_cml()) {
+ /*
+ * use name prefix 'WL' and 'WR' for speaker widgets to
+ * keep backward compatible with cml devices
+ */
+ card->codec_conf = rt1011_4spk_codec_confs;
+ } else {
+ card->codec_conf = rt1011_2spk_codec_confs;
+ }
+
+ card->num_configs = num_codecs;
+ break;
+ case 4:
+ card->codec_conf = rt1011_4spk_codec_confs;
+ card->num_configs = ARRAY_SIZE(rt1011_4spk_codec_confs);
+ break;
+ default:
+ dev_err(dev, "rt1011: invalid num_codecs %d\n", num_codecs);
+ break;
+ }
+
+}
+EXPORT_SYMBOL_NS(sof_rt1011_codec_conf, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+/*
+ * rt1015: i2c mode driver for ALC1015 and ALC1015Q
+ * rt1015p: auto-mode driver for ALC1015, ALC1015Q, and ALC1015Q-VB
+ *
+ * For stereo output, there are always two amplifiers on the board.
+ * However, the ACPI implements only one device instance (UID=0) if they
+ * are sharing the same enable pin. This is the case of rt1015p.
+ */
+static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Speaker" },
+ { "Right Spk", NULL, "Speaker" },
+};
+
+static struct snd_soc_dai_link_component rt1015p_dai_link_components[] = {
+ {
+ .name = RT1015P_DEV0_NAME,
+ .dai_name = RT1015P_CODEC_DAI,
+ },
+};
+
+static int rt1015p_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* reserved for debugging purpose */
+
+ return 0;
+}
+
+static const struct snd_soc_ops rt1015p_ops = {
+ .hw_params = rt1015p_hw_params,
+};
+
+static int rt1015p_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, realtek_2spk_widgets,
+ ARRAY_SIZE(realtek_2spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1015p widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, realtek_2spk_kcontrols,
+ ARRAY_SIZE(realtek_2spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1015p kcontrols, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, rt1015p_dapm_routes,
+ ARRAY_SIZE(rt1015p_dapm_routes));
+ if (ret)
+ dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
+ return ret;
+}
+
+void sof_rt1015p_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = rt1015p_dai_link_components;
+ link->num_codecs = ARRAY_SIZE(rt1015p_dai_link_components);
+ link->init = rt1015p_init;
+ link->ops = &rt1015p_ops;
+}
+EXPORT_SYMBOL_NS(sof_rt1015p_dai_link, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+void sof_rt1015p_codec_conf(struct snd_soc_card *card)
+{
+}
+EXPORT_SYMBOL_NS(sof_rt1015p_codec_conf, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+/*
+ * RT1015 audio amplifier
+ */
+
+static const struct {
+ unsigned int tx;
+ unsigned int rx;
+} rt1015_tdm_mask[] = {
+ {.tx = 0x0, .rx = 0x1},
+ {.tx = 0x0, .rx = 0x2},
+};
+
+static int rt1015_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_dai *codec_dai;
+ int i, clk_freq;
+ int ret = 0;
+
+ clk_freq = sof_dai_get_bclk(rtd);
+
+ if (clk_freq <= 0) {
+ dev_err(rtd->dev, "fail to get bclk freq, ret %d\n", clk_freq);
+ return -EINVAL;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
+ clk_freq,
+ params_rate(params) * 256);
+ if (ret) {
+ dev_err(codec_dai->dev, "fail to set pll, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
+ params_rate(params) * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(codec_dai->dev, "fail to set sysclk, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* 4-slot TDM */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ rt1015_tdm_mask[i].tx,
+ rt1015_tdm_mask[i].rx,
+ 4,
+ params_width(params));
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_dbg(codec_dai->dev, "codec is in I2S mode\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops rt1015_ops = {
+ .hw_params = rt1015_hw_params,
+};
+
+static struct snd_soc_codec_conf rt1015_amp_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_dai_link_component rt1015_components[] = {
+ {
+ .name = RT1015_DEV0_NAME,
+ .dai_name = RT1015_CODEC_DAI,
+ },
+ {
+ .name = RT1015_DEV1_NAME,
+ .dai_name = RT1015_CODEC_DAI,
+ },
+};
+
+static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ unsigned int num_codecs = get_num_codecs(RT1015_ACPI_HID);
+ int ret;
+
+ switch (num_codecs) {
+ case 2:
+ ret = snd_soc_dapm_new_controls(dapm, realtek_2spk_widgets,
+ ARRAY_SIZE(realtek_2spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1015 widgets, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, realtek_2spk_kcontrols,
+ ARRAY_SIZE(realtek_2spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1015 kcontrols, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, speaker_map_lr,
+ ARRAY_SIZE(speaker_map_lr));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1015 routes, ret %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(rtd->dev, "rt1015: invalid num_codecs %d\n", num_codecs);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void sof_rt1015_codec_conf(struct snd_soc_card *card)
+{
+ card->codec_conf = rt1015_amp_conf;
+ card->num_configs = ARRAY_SIZE(rt1015_amp_conf);
+}
+EXPORT_SYMBOL_NS(sof_rt1015_codec_conf, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+void sof_rt1015_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = rt1015_components;
+ link->num_codecs = ARRAY_SIZE(rt1015_components);
+ link->init = speaker_codec_init_lr;
+ link->ops = &rt1015_ops;
+}
+EXPORT_SYMBOL_NS(sof_rt1015_dai_link, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+/*
+ * RT1308 audio amplifier
+ */
+static const struct snd_kcontrol_new rt1308_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+};
+
+static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
+static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
+ /* speaker */
+ {"Speakers", NULL, "SPOL"},
+ {"Speakers", NULL, "SPOR"},
+};
+
+static struct snd_soc_dai_link_component rt1308_components[] = {
+ {
+ .name = RT1308_DEV0_NAME,
+ .dai_name = RT1308_CODEC_DAI,
+ }
+};
+
+static int rt1308_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, rt1308_dapm_widgets,
+ ARRAY_SIZE(rt1308_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt1308_kcontrols,
+ ARRAY_SIZE(rt1308_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, rt1308_dapm_routes,
+ ARRAY_SIZE(rt1308_dapm_routes));
+
+ if (ret)
+ dev_err(rtd->dev, "fail to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int rt1308_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int clk_id, clk_freq, pll_out;
+ int ret;
+
+ clk_id = RT1308_PLL_S_MCLK;
+ /* get the tplg configured mclk. */
+ clk_freq = sof_dai_get_mclk(rtd);
+
+ pll_out = params_rate(params) * 512;
+
+ /* Set rt1308 pll */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
+ if (ret < 0) {
+ dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", ret);
+ return ret;
+ }
+
+ /* Set rt1308 sysclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops rt1308_ops = {
+ .hw_params = rt1308_hw_params,
+};
+
+void sof_rt1308_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = rt1308_components;
+ link->num_codecs = ARRAY_SIZE(rt1308_components);
+ link->init = rt1308_init;
+ link->ops = &rt1308_ops;
+}
+EXPORT_SYMBOL_NS(sof_rt1308_dai_link, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+/*
+ * 2-amp Configuration for RT1019
+ */
+
+static const struct snd_soc_dapm_route rt1019p_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Speaker" },
+ { "Right Spk", NULL, "Speaker" },
+};
+
+static struct snd_soc_dai_link_component rt1019p_components[] = {
+ {
+ .name = RT1019P_DEV0_NAME,
+ .dai_name = RT1019P_CODEC_DAI,
+ },
+};
+
+static int rt1019p_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, realtek_2spk_widgets,
+ ARRAY_SIZE(realtek_2spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1019p widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, realtek_2spk_kcontrols,
+ ARRAY_SIZE(realtek_2spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt1019p kcontrols, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, rt1019p_dapm_routes,
+ ARRAY_SIZE(rt1019p_dapm_routes));
+ if (ret) {
+ dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+void sof_rt1019p_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = rt1019p_components;
+ link->num_codecs = ARRAY_SIZE(rt1019p_components);
+ link->init = rt1019p_init;
+}
+EXPORT_SYMBOL_NS(sof_rt1019p_dai_link, "SND_SOC_INTEL_SOF_REALTEK_COMMON");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Realtek helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h
new file mode 100644
index 000000000000..876290555c22
--- /dev/null
+++ b/sound/soc/intel/boards/sof_realtek_common.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2020 Intel Corporation.
+ */
+
+/*
+ * This file defines data structures used in Machine Driver for Intel
+ * platforms with Realtek Codecs.
+ */
+#ifndef __SOF_REALTEK_COMMON_H
+#define __SOF_REALTEK_COMMON_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Realtek ALC1011
+ */
+
+#define RT1011_CODEC_DAI "rt1011-aif"
+#define RT1011_DEV0_NAME "i2c-" RT1011_ACPI_HID ":00"
+#define RT1011_DEV1_NAME "i2c-" RT1011_ACPI_HID ":01"
+#define RT1011_DEV2_NAME "i2c-" RT1011_ACPI_HID ":02"
+#define RT1011_DEV3_NAME "i2c-" RT1011_ACPI_HID ":03"
+
+void sof_rt1011_dai_link(struct device *dev, struct snd_soc_dai_link *link);
+void sof_rt1011_codec_conf(struct device *dev, struct snd_soc_card *card);
+
+/*
+ * Realtek ALC1015 (AUTO)
+ */
+#define RT1015P_CODEC_DAI "HiFi"
+#define RT1015P_DEV0_NAME RT1015P_ACPI_HID ":00"
+
+void sof_rt1015p_dai_link(struct snd_soc_dai_link *link);
+void sof_rt1015p_codec_conf(struct snd_soc_card *card);
+
+/*
+ * Realtek ALC1015 (I2C)
+ */
+#define RT1015_CODEC_DAI "rt1015-aif"
+#define RT1015_DEV0_NAME "i2c-" RT1015_ACPI_HID ":00"
+#define RT1015_DEV1_NAME "i2c-" RT1015_ACPI_HID ":01"
+
+void sof_rt1015_dai_link(struct snd_soc_dai_link *link);
+void sof_rt1015_codec_conf(struct snd_soc_card *card);
+
+/*
+ * Realtek ALC1308
+ */
+#define RT1308_CODEC_DAI "rt1308-aif"
+#define RT1308_DEV0_NAME "i2c-" RT1308_ACPI_HID ":00"
+void sof_rt1308_dai_link(struct snd_soc_dai_link *link);
+
+/*
+ * Realtek ALC1019
+ */
+#define RT1019P_CODEC_DAI "HiFi"
+#define RT1019P_DEV0_NAME RT1019P_ACPI_HID ":00"
+
+void sof_rt1019p_dai_link(struct snd_soc_dai_link *link);
+
+#endif /* __SOF_REALTEK_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
new file mode 100644
index 000000000000..3d9d8a97d153
--- /dev/null
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -0,0 +1,936 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2019-2020 Intel Corporation.
+
+/*
+ * Intel SOF Machine Driver with Realtek rt5682 Codec
+ * and speaker codec MAX98357A or RT1015.
+ */
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/dmi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/sof.h>
+#include <sound/rt5682.h>
+#include <sound/rt5682s.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/rt5682.h"
+#include "../../codecs/rt5682s.h"
+#include "../../codecs/rt5645.h"
+#include "../common/soc-intel-quirks.h"
+#include "sof_board_helpers.h"
+#include "sof_maxim_common.h"
+#include "sof_realtek_common.h"
+
+/* Driver-specific board quirks: from bit 0 to 7 */
+#define SOF_RT5682_MCLK_EN BIT(0)
+
+/* Default: MCLK on, MCLK 19.2M, SSP0 */
+static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0);
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static int sof_rt5682_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_rt5682_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_rt5682_quirk_table[] = {
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max"),
+ },
+ .driver_data = (void *)(SOF_SSP_PORT_CODEC(2)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"),
+ },
+ .driver_data = (void *)(SOF_SSP_PORT_CODEC(2)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WhiskeyLake Client"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98373_ALC5682I_I2S_UP4"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-ADL_MAX98373_ALC5682I_I2S"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S_AMP_SSP2"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(0) |
+ SOF_SSP_PORT_BT_OFFLOAD(1) |
+ SOF_BT_OFFLOAD_PRESENT
+ ),
+ },
+ {}
+};
+
+static struct snd_soc_jack_pin jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_jack *jack = &ctx->headset_jack;
+ int extra_jack_data;
+ int ret, mclk_freq;
+
+ if (ctx->rt5682.mclk_en) {
+ mclk_freq = sof_dai_get_mclk(rtd);
+ if (mclk_freq <= 0) {
+ dev_err(rtd->dev, "invalid mclk freq %d\n", mclk_freq);
+ return -EINVAL;
+ }
+
+ /* need to enable ASRC function for 24MHz mclk rate */
+ if (mclk_freq == 24000000) {
+ dev_info(rtd->dev, "enable ASRC\n");
+
+ switch (ctx->codec_type) {
+ case CODEC_RT5650:
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_MONO_L_FILTER |
+ RT5645_DA_MONO_R_FILTER,
+ RT5645_CLK_SEL_I2S2_ASRC);
+ break;
+ case CODEC_RT5682:
+ rt5682_sel_asrc_clk_src(component,
+ RT5682_DA_STEREO1_FILTER |
+ RT5682_AD_STEREO1_FILTER,
+ RT5682_CLK_SEL_I2S1_ASRC);
+ break;
+ case CODEC_RT5682S:
+ rt5682s_sel_asrc_clk_src(component,
+ RT5682S_DA_STEREO1_FILTER |
+ RT5682S_AD_STEREO1_FILTER,
+ RT5682S_CLK_SEL_I2S1_ASRC);
+ break;
+ default:
+ dev_err(rtd->dev, "invalid codec type %d\n",
+ ctx->codec_type);
+ return -EINVAL;
+ }
+ }
+
+ if (ctx->rt5682.is_legacy_cpu) {
+ /*
+ * The firmware might enable the clock at
+ * boot (this information may or may not
+ * be reflected in the enable clock register).
+ * To change the rate we must disable the clock
+ * first to cover these cases. Due to common
+ * clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(ctx->rt5682.mclk);
+ if (!ret)
+ clk_disable_unprepare(ctx->rt5682.mclk);
+
+ ret = clk_set_rate(ctx->rt5682.mclk, 19200000);
+
+ if (ret)
+ dev_err(rtd->dev, "unable to set MCLK rate\n");
+ }
+ }
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ jack_pins,
+ ARRAY_SIZE(jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ if (ctx->codec_type == CODEC_RT5650) {
+ extra_jack_data = SND_JACK_MICROPHONE | SND_JACK_BTN_0;
+ ret = snd_soc_component_set_jack(component, jack, &extra_jack_data);
+ } else
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+};
+
+static void sof_rt5682_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
+
+ if (ctx->rt5682.mclk_en) {
+ if (ctx->rt5682.is_legacy_cpu) {
+ ret = clk_prepare_enable(ctx->rt5682.mclk);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ }
+
+ switch (ctx->codec_type) {
+ case CODEC_RT5650:
+ pll_source = RT5645_PLL1_S_MCLK;
+ break;
+ case CODEC_RT5682:
+ pll_source = RT5682_PLL1_S_MCLK;
+ break;
+ case CODEC_RT5682S:
+ pll_source = RT5682S_PLL_S_MCLK;
+ break;
+ default:
+ dev_err(rtd->dev, "invalid codec type %d\n",
+ ctx->codec_type);
+ return -EINVAL;
+ }
+
+ /* get the tplg configured mclk. */
+ pll_in = sof_dai_get_mclk(rtd);
+ if (pll_in <= 0) {
+ dev_err(rtd->dev, "invalid mclk freq %d\n", pll_in);
+ return -EINVAL;
+ }
+ } else {
+ switch (ctx->codec_type) {
+ case CODEC_RT5650:
+ pll_source = RT5645_PLL1_S_BCLK1;
+ break;
+ case CODEC_RT5682:
+ pll_source = RT5682_PLL1_S_BCLK1;
+ break;
+ case CODEC_RT5682S:
+ pll_source = RT5682S_PLL_S_BCLK1;
+ break;
+ default:
+ dev_err(rtd->dev, "invalid codec type %d\n",
+ ctx->codec_type);
+ return -EINVAL;
+ }
+
+ /* get the tplg configured bclk. */
+ pll_in = sof_dai_get_bclk(rtd);
+ if (pll_in <= 0) {
+ dev_err(rtd->dev, "invalid bclk freq %d\n", pll_in);
+ return -EINVAL;
+ }
+ }
+
+ pll_out = params_rate(params) * 512;
+
+ /* when MCLK is 512FS, no need to set PLL configuration additionally. */
+ if (pll_in == pll_out) {
+ switch (ctx->codec_type) {
+ case CODEC_RT5650:
+ clk_id = RT5645_SCLK_S_MCLK;
+ break;
+ case CODEC_RT5682:
+ clk_id = RT5682_SCLK_S_MCLK;
+ break;
+ case CODEC_RT5682S:
+ clk_id = RT5682S_SCLK_S_MCLK;
+ break;
+ default:
+ dev_err(rtd->dev, "invalid codec type %d\n",
+ ctx->codec_type);
+ return -EINVAL;
+ }
+ } else {
+ switch (ctx->codec_type) {
+ case CODEC_RT5650:
+ pll_id = 0; /* not used in codec driver */
+ clk_id = RT5645_SCLK_S_PLL1;
+ break;
+ case CODEC_RT5682:
+ pll_id = RT5682_PLL1;
+ clk_id = RT5682_SCLK_S_PLL1;
+ break;
+ case CODEC_RT5682S:
+ /* check plla_table and pllb_table in rt5682s.c */
+ switch (pll_in) {
+ case 3072000:
+ case 24576000:
+ /*
+ * For MCLK = 24.576MHz and sample rate = 96KHz case, use PLL1 We don't test
+ * pll_out or params_rate() here since rt5682s PLL2 doesn't support 24.576MHz
+ * input, so we have no choice but to use PLL1. Besides, we will not use PLL at
+ * all if pll_in == pll_out. ex, MCLK = 24.576Mhz and sample rate = 48KHz
+ */
+ pll_id = RT5682S_PLL1;
+ clk_id = RT5682S_SCLK_S_PLL1;
+ break;
+ default:
+ pll_id = RT5682S_PLL2;
+ clk_id = RT5682S_SCLK_S_PLL2;
+ break;
+ }
+ break;
+ default:
+ dev_err(rtd->dev, "invalid codec type %d\n", ctx->codec_type);
+ return -EINVAL;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, pll_in,
+ pll_out);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret);
+ }
+
+ /* Configure sysclk for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ /*
+ * slot_width should equal or large than data length, set them
+ * be the same
+ */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2,
+ params_width(params));
+ if (ret < 0) {
+ dev_err(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops sof_rt5682_ops = {
+ .hw_params = sof_rt5682_hw_params,
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int err;
+
+ if (ctx->amp_type == CODEC_MAX98373) {
+ /* Disable Left and Right Spk pin after boot */
+ snd_soc_dapm_disable_pin(dapm, "Left Spk");
+ snd_soc_dapm_disable_pin(dapm, "Right Spk");
+ err = snd_soc_dapm_sync(dapm);
+ if (err < 0)
+ return err;
+ }
+
+ return sof_intel_board_card_late_probe(card);
+}
+
+static const struct snd_kcontrol_new sof_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* other jacks */
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static const struct snd_kcontrol_new rt5650_spk_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+
+};
+
+static const struct snd_soc_dapm_widget rt5650_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route rt5650_spk_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "SPOL" },
+ { "Right Spk", NULL, "SPOR" },
+};
+
+static int rt5650_spk_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, rt5650_spk_widgets,
+ ARRAY_SIZE(rt5650_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt5650 spk widgets, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5650_spk_kcontrols,
+ ARRAY_SIZE(rt5650_spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "fail to add rt5650 spk kcontrols, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, rt5650_spk_dapm_routes,
+ ARRAY_SIZE(rt5650_spk_dapm_routes));
+ if (ret)
+ dev_err(rtd->dev, "fail to add dapm routes, ret=%d\n", ret);
+
+ return ret;
+}
+
+/* sof audio machine driver for rt5682 codec */
+static struct snd_soc_card sof_audio_card_rt5682 = {
+ .name = "rt5682", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE,
+ .controls = sof_controls,
+ .num_controls = ARRAY_SIZE(sof_controls),
+ .dapm_widgets = sof_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
+ .dapm_routes = sof_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_map),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+static struct snd_soc_dai_link_component rt5682_component[] = {
+ {
+ .name = "i2c-10EC5682:00",
+ .dai_name = "rt5682-aif1",
+ }
+};
+
+static struct snd_soc_dai_link_component rt5682s_component[] = {
+ {
+ .name = "i2c-RTL5682:00",
+ .dai_name = "rt5682s-aif1",
+ }
+};
+
+static struct snd_soc_dai_link_component rt5650_components[] = {
+ {
+ .name = "i2c-10EC5650:00",
+ .dai_name = "rt5645-aif1",
+ },
+ {
+ .name = "i2c-10EC5650:00",
+ .dai_name = "rt5645-aif2",
+ }
+};
+
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ int ret;
+
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
+
+ if (!ctx->codec_link) {
+ dev_err(dev, "codec link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for headphone codec */
+ switch (ctx->codec_type) {
+ case CODEC_RT5650:
+ ctx->codec_link->codecs = &rt5650_components[0];
+ ctx->codec_link->num_codecs = 1;
+ break;
+ case CODEC_RT5682:
+ ctx->codec_link->codecs = rt5682_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(rt5682_component);
+ break;
+ case CODEC_RT5682S:
+ ctx->codec_link->codecs = rt5682s_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(rt5682s_component);
+ break;
+ default:
+ dev_err(dev, "invalid codec type %d\n", ctx->codec_type);
+ return -EINVAL;
+ }
+
+ ctx->codec_link->init = sof_rt5682_codec_init;
+ ctx->codec_link->exit = sof_rt5682_codec_exit;
+ ctx->codec_link->ops = &sof_rt5682_ops;
+
+ if (!ctx->rt5682.is_legacy_cpu) {
+ /*
+ * Currently, On SKL+ platforms MCLK will be turned off in sof
+ * runtime suspended, and it will go into runtime suspended
+ * right after playback is stop. However, rt5682 will output
+ * static noise if sysclk turns off during playback. Set
+ * ignore_pmdown_time to power down rt5682 immediately and
+ * avoid the noise.
+ * It can be removed once we can control MCLK by driver.
+ */
+ ctx->codec_link->ignore_pmdown_time = 1;
+ }
+
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
+
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ max_98357a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98373:
+ max_98373_dai_link(dev, ctx->amp_link);
+ break;
+ case CODEC_MAX98390:
+ max_98390_dai_link(dev, ctx->amp_link);
+ break;
+ case CODEC_RT1011:
+ sof_rt1011_dai_link(dev, ctx->amp_link);
+ break;
+ case CODEC_RT1015:
+ sof_rt1015_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1015P:
+ sof_rt1015p_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1019P:
+ sof_rt1019p_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT5650:
+ /* use AIF2 to support speaker pipeline */
+ ctx->amp_link->codecs = &rt5650_components[1];
+ ctx->amp_link->num_codecs = 1;
+ ctx->amp_link->init = rt5650_spk_init;
+ ctx->amp_link->ops = &sof_rt5682_ops;
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define GLK_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \
+ SOF_LINK_CODEC, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE, \
+ SOF_LINK_NONE)
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_card_private *ctx;
+ char *card_name;
+ int ret;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ sof_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ dmi_check_system(sof_rt5682_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ sof_rt5682_quirk, quirk_override);
+ sof_rt5682_quirk = quirk_override;
+ }
+
+ dev_dbg(&pdev->dev, "sof_rt5682_quirk = %lx\n", sof_rt5682_quirk);
+
+ /* initialize ctx with board quirk */
+ ctx = sof_intel_board_get_ctx(&pdev->dev, sof_rt5682_quirk);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (ctx->codec_type == CODEC_RT5650) {
+ card_name = devm_kstrdup(&pdev->dev, "rt5650", GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ sof_audio_card_rt5682.name = card_name;
+
+ /* create speaker dai link also */
+ if (ctx->amp_type == CODEC_NONE)
+ ctx->amp_type = CODEC_RT5650;
+ }
+
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->hdmi.idisp_codec = true;
+
+ if (soc_intel_is_byt() || soc_intel_is_cht()) {
+ ctx->rt5682.is_legacy_cpu = true;
+ ctx->dmic_be_num = 0;
+ /* HDMI is not supported by SOF on Baytrail/CherryTrail */
+ ctx->hdmi_num = 0;
+ } else if (soc_intel_is_glk()) {
+ /* dmic16k not support */
+ ctx->dmic_be_num = 1;
+
+ /* overwrite the DAI link order for GLK boards */
+ ctx->link_order_overwrite = GLK_LINK_ORDER;
+
+ /* backward-compatible with existing devices */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ card_name = devm_kstrdup(&pdev->dev, "glkrt5682max",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ sof_audio_card_rt5682.name = card_name;
+ break;
+ default:
+ break;
+ }
+ } else if (soc_intel_is_cml()) {
+ /* backward-compatible with existing devices */
+ switch (ctx->amp_type) {
+ case CODEC_RT1011:
+ card_name = devm_kstrdup(&pdev->dev, "cml_rt1011_rt5682",
+ GFP_KERNEL);
+ if (!card_name)
+ return -ENOMEM;
+
+ sof_audio_card_rt5682.name = card_name;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (sof_rt5682_quirk & SOF_RT5682_MCLK_EN) {
+ ctx->rt5682.mclk_en = true;
+
+ /* need to get main clock from pmc */
+ if (ctx->rt5682.is_legacy_cpu) {
+ ctx->rt5682.mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ if (IS_ERR(ctx->rt5682.mclk)) {
+ ret = PTR_ERR(ctx->rt5682.mclk);
+
+ dev_err(&pdev->dev,
+ "Failed to get MCLK from pmc_plt_clk_3: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ctx->rt5682.mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ }
+ }
+
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_rt5682, ctx);
+ if (ret)
+ return ret;
+
+ /* update codec_conf */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98373:
+ max_98373_set_codec_conf(&sof_audio_card_rt5682);
+ break;
+ case CODEC_MAX98390:
+ max_98390_set_codec_conf(&pdev->dev, &sof_audio_card_rt5682);
+ break;
+ case CODEC_RT1011:
+ sof_rt1011_codec_conf(&pdev->dev, &sof_audio_card_rt5682);
+ break;
+ case CODEC_RT1015:
+ sof_rt1015_codec_conf(&sof_audio_card_rt5682);
+ break;
+ case CODEC_RT1015P:
+ sof_rt1015p_codec_conf(&sof_audio_card_rt5682);
+ break;
+ case CODEC_MAX98357A:
+ case CODEC_MAX98360A:
+ case CODEC_RT1019P:
+ case CODEC_RT5650:
+ case CODEC_NONE:
+ /* no codec conf required */
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ sof_audio_card_rt5682.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_rt5682,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&sof_audio_card_rt5682, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev,
+ &sof_audio_card_rt5682);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "sof_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(2)),
+ },
+ {
+ .name = "glk_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(2) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "icl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0)),
+ },
+ {
+ .name = "cml_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "jsl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1)),
+ },
+ {
+ .name = "tgl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "adl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "adl_mx98357_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .name = "adl_rt5682_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_SSP_MASK_HDMI_CAPTURE(0x5)),
+ },
+ {
+ .name = "rpl_mx98357_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(2) |
+ SOF_NUM_IDISP_HDMI(4)),
+ },
+ {
+ .name = "rpl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "rpl_rt5682_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_SSP_MASK_HDMI_CAPTURE(0x5)),
+ },
+ {
+ .name = "mtl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "mtl_rt5682_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_SSP_MASK_HDMI_CAPTURE(0x5)),
+ },
+ {
+ .name = "arl_rt5682_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_SSP_MASK_HDMI_CAPTURE(0x5)),
+ },
+ {
+ .name = "ptl_rt5682_def",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(0) |
+ SOF_SSP_PORT_AMP(1) |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "ptl_rt5682_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_SSP_MASK_HDMI_CAPTURE(0x5)),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver sof_audio = {
+ .probe = sof_audio_probe,
+ .driver = {
+ .name = "sof_rt5682",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(sof_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("SOF Audio Machine driver");
+MODULE_AUTHOR("Bard Liao <bard.liao@intel.com>");
+MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON");
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
new file mode 100644
index 000000000000..2c1001148d54
--- /dev/null
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -0,0 +1,1500 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020 Intel Corporation
+
+/*
+ * sof_sdw - ASOC Machine driver for Intel SoundWire platforms
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/core.h>
+#include <sound/soc-acpi.h>
+#include "sof_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long sof_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+#define DMIC_DEFAULT_CHANNELS 2
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_SDW_JACK_JDSRC(sof_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_SDW_JACK_JDSRC(sof_sdw_quirk));
+ if (sof_sdw_quirk & SOC_SDW_FOUR_SPK)
+ dev_err(dev, "quirk SOC_SDW_FOUR_SPK enabled but no longer supported\n");
+ if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
+ dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n");
+ if (sof_sdw_quirk & SOC_SDW_PCH_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_PCH_DMIC enabled\n");
+ if (SOF_SSP_GET_PORT(sof_sdw_quirk))
+ dev_dbg(dev, "SSP port %ld\n",
+ SOF_SSP_GET_PORT(sof_sdw_quirk));
+ if (sof_sdw_quirk & SOC_SDW_NO_AGGREGATION)
+ dev_err(dev, "quirk SOC_SDW_NO_AGGREGATION enabled but no longer supported\n");
+ if (sof_sdw_quirk & SOC_SDW_CODEC_SPKR)
+ dev_dbg(dev, "quirk SOC_SDW_CODEC_SPKR enabled\n");
+ if (sof_sdw_quirk & SOC_SDW_SIDECAR_AMPS)
+ dev_dbg(dev, "quirk SOC_SDW_SIDECAR_AMPS enabled\n");
+ if (sof_sdw_quirk & SOC_SDW_CODEC_MIC)
+ dev_dbg(dev, "quirk SOC_SDW_CODEC_MIC enabled\n");
+}
+
+static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_sdw_quirk_table[] = {
+ /* CometLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"),
+ },
+ .driver_data = (void *)SOC_SDW_PCH_DMIC,
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {
+ /* early version of SKU 09C6 */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
+ },
+ .driver_data = (void *)(RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
+ },
+ .driver_data = (void *)(RT711_JD2),
+ },
+ /* IceLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
+ },
+ .driver_data = (void *)SOC_SDW_PCH_DMIC,
+ },
+ /* TigerLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "Tiger Lake Client Platform"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD1 |
+ SOC_SDW_PCH_DMIC |
+ SOF_SSP_PORT(SOF_I2S_SSP2)),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ /* another SKU of Dell Latitude 9520 */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ /* Dell XPS 9710 */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC),
+ },
+ {
+ /*
+ * this entry covers multiple HP SKUs. The family name
+ * does not seem robust enough, so we use a partial
+ * match that ignores the product name suffix
+ * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx)
+ */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD1),
+ },
+ {
+ /*
+ * this entry covers HP Spectre x360 where the DMI information
+ * changed somehow
+ */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8709"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD1),
+ },
+ {
+ /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LAPBC"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD1),
+ },
+ {
+ /* NUC15 LAPBC710 skews */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "LAPBC710"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD1),
+ },
+ {
+ /*
+ * Avell B.ON (OEM rebrand of NUC15 'Bishop County' LAPBC510 and
+ * LAPBC710)
+ */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Avell High Performance"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "B.ON"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD1),
+ },
+ {
+ /* NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD2_100K),
+ },
+ {
+ /* NUC15 LAPRC710 skews */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ RT711_JD2_100K),
+ },
+ /* TigerLake-SDCA devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ /* AlderLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),
+ },
+ .driver_data = (void *)(RT711_JD2_100K |
+ SOF_SDW_TGL_HDMI |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_SKU, "0000000000070000"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2_100K),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Brya"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF0")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"),
+ },
+ /* No Jack */
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"),
+ },
+ /* No Jack */
+ .driver_data = (void *)SOF_SDW_TGL_HDMI,
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B14"),
+ },
+ /* No Jack */
+ .driver_data = (void *)SOF_SDW_TGL_HDMI,
+ },
+
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"),
+ },
+ /* No Jack */
+ .driver_data = (void *)SOF_SDW_TGL_HDMI,
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B8C"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ /* RaptorLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C0F")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"),
+ },
+ /* No Jack */
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C11")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C40")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C4F")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF6")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF9")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CFA")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ /* MeteorLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"),
+ },
+ .driver_data = (void *)(RT711_JD1),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"),
+ },
+ .driver_data = (void *)(RT711_JD2_100K),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Rex"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(1) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OMEN Transcend Gaming Laptop"),
+ },
+ .driver_data = (void *)(RT711_JD2),
+ },
+
+ /* LunarLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"),
+ },
+ .driver_data = (void *)(RT711_JD2),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE3")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE4")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CDB")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CDC")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CDD")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D36")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF8")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83JX")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83LC")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83MC")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC),
+ }, {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83NM")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83HM")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS |
+ SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21QB")
+ },
+ /* Note this quirk excludes the CODEC mic */
+ .driver_data = (void *)(SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21QA")
+ },
+ /* Note this quirk excludes the CODEC mic */
+ .driver_data = (void *)(SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21Q6")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21Q7")
+ },
+ .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC),
+ },
+
+ /* ArrowLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE8")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF1")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF7")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF0")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF3")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF4")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF5")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CCC")
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR),
+ },
+ /* Pantherlake devices*/
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_ptlrvp"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Lapis"),
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR |
+ SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Francka"),
+ },
+ .driver_data = (void *)(SOC_SDW_CODEC_SPKR |
+ SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Fatcat"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ /* Wildcatlake devices*/
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_wclrvp"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ocelot"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {}
+};
+
+static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = {
+ SND_PCI_QUIRK(0x1043, 0x1e13, "ASUS Zenbook S14", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1f43, "ASUS Zenbook S16", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x2347, "Lenovo P16", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x2348, "Lenovo P16", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x2349, "Lenovo P1", SOC_SDW_CODEC_MIC),
+ {}
+};
+
+static void sof_sdw_check_ssid_quirk(const struct snd_soc_acpi_mach *mach)
+{
+ const struct snd_pci_quirk *quirk_entry;
+
+ quirk_entry = snd_pci_quirk_lookup_id(mach->mach_params.subsystem_vendor,
+ mach->mach_params.subsystem_device,
+ sof_sdw_ssid_quirk_table);
+
+ if (quirk_entry)
+ sof_sdw_quirk = quirk_entry->value;
+}
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *sof_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *sof_end;
+ int stream;
+ int ret;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->name_prefix) {
+ (*codec_conf)->dlc.name = sof_end->codec_name;
+ (*codec_conf)->name_prefix = sof_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (sof_end->include_sidecar && sof_end->codec_info->add_sidecar) {
+ ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-Playback",
+ "SDW%d-Capture",
+ "SDW%d-Playback-%s",
+ "SDW%d-Capture-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link_component *platform;
+ int num_cpus = hweight32(sof_dai->link_mask[stream]);
+ int num_codecs = sof_dai->num_devs[stream];
+ int playback, capture;
+ int cur_link = 0;
+ int i = 0, j = 0;
+ char *name;
+
+ if (!sof_dai->num_devs[stream])
+ continue;
+
+ sof_end = list_first_entry(&sof_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = sof_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type)
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(sof_end->link_mask) - 1,
+ type_strings[sof_end->dai_info->dai_type]);
+ else
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(sof_end->link_mask) - 1);
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!platform)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (!sof_end->dai_info->direction[stream])
+ continue;
+
+ if (cur_link != sof_end->link_mask) {
+ int link_num = ffs(sof_end->link_mask) - 1;
+ int pin_num = intel_ctx->sdw_pin_index[link_num]++;
+
+ cur_link = sof_end->link_mask;
+
+ cpus[i].dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, pin_num);
+ if (!cpus[i].dai_name)
+ return -ENOMEM;
+ i++;
+ }
+
+ codec_maps[j].cpu = i - 1;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = sof_end->codec_name;
+ codecs[j].dai_name = sof_end->dai_info->dai_name;
+ if (sof_end->dai_info->dai_type == SOC_SDW_DAI_TYPE_MIC &&
+ mach_params->dmic_num > 0) {
+ dev_warn(dev,
+ "Both SDW DMIC and PCH DMIC are present, if incorrect, please set kernel params snd_sof_intel_hda_generic dmic_num=0 to disable PCH DMIC\n");
+ }
+ j++;
+ }
+
+ WARN_ON(i != num_cpus || j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, platform, 1, codecs, num_codecs,
+ 1, asoc_sdw_rtd_init, &sdw_ops);
+
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->dai_info->init)
+ sof_end->dai_info->init(card, *dai_links,
+ sof_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *sof_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+ int ret, i;
+
+ for (i = 0; i < SDW_INTEL_MAX_LINKS; i++)
+ intel_ctx->sdw_pin_index[i] = SOC_SDW_INTEL_BIDIR_PDI_BASE;
+
+ /* generate DAI links by each sdw link */
+ while (sof_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, sof_dais, dai_links,
+ &current_be_id, codec_conf);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ sof_dais++;
+ }
+
+ return 0;
+}
+
+static int create_ssp_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_codec_info *ssp_info,
+ unsigned long ssp_mask)
+{
+ struct device *dev = card->dev;
+ int i, j = 0;
+ int ret;
+
+ for_each_set_bit(i, &ssp_mask, BITS_PER_TYPE(ssp_mask)) {
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", i);
+ char *cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i);
+ char *codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d",
+ ssp_info->acpi_id, j++);
+ if (!name || !cpu_dai_name || !codec_name)
+ return -ENOMEM;
+
+ int playback = ssp_info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK];
+ int capture = ssp_info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE];
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
+ playback, capture, cpu_dai_name,
+ "dummy", codec_name,
+ ssp_info->dais[0].dai_name, 1, NULL,
+ ssp_info->ops);
+ if (ret)
+ return ret;
+
+ ret = ssp_info->dais[0].init(card, *dai_links, ssp_info, 0);
+ if (ret < 0)
+ return ret;
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id)
+{
+ struct device *dev = card->dev;
+ int ret;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "dmic01",
+ 0, 1, // DMIC only supports capture
+ "DMIC01 Pin", "dummy",
+ "dmic-codec", "dmic-hifi", 1,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "dmic16k",
+ 0, 1, // DMIC only supports capture
+ "DMIC16k Pin", "dummy",
+ "dmic-codec", "dmic-hifi", 1,
+ /* don't call asoc_sdw_dmic_init() twice */
+ NULL, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int create_hdmi_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ int hdmi_num)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+ int i, ret;
+
+ for (i = 0; i < hdmi_num; i++) {
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", i + 1);
+ char *cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", i + 1);
+ if (!name || !cpu_dai_name)
+ return -ENOMEM;
+
+ char *codec_name, *codec_dai_name;
+
+ if (intel_ctx->hdmi.idisp_codec) {
+ codec_name = "ehdaudio0D2";
+ codec_dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "intel-hdmi-hifi%d", i + 1);
+ } else {
+ codec_name = "snd-soc-dummy";
+ codec_dai_name = "snd-soc-dummy-dai";
+ }
+
+ if (!codec_dai_name)
+ return -ENOMEM;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
+ 1, 0, // HDMI only supports playback
+ cpu_dai_name, "dummy",
+ codec_name, codec_dai_name, 1,
+ i == 0 ? sof_sdw_hdmi_init : NULL, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_bt_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
+ char *cpu_dai_name;
+ char *name;
+ int port;
+ int ret;
+
+ if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
+ port = (sof_sdw_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> SOF_BT_OFFLOAD_SSP_SHIFT;
+ else
+ port = fls(mach->mach_params.bt_link_mask) - 1;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
+ cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port);
+ if (!name || !cpu_dai_name)
+ return -ENOMEM;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
+ 1, 1, cpu_dai_name, "dummy",
+ snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name,
+ 1, NULL, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int sof_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, bt_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct snd_soc_codec_conf *codec_conf;
+ struct asoc_sdw_codec_info *ssp_info;
+ struct asoc_sdw_endpoint *sof_ends;
+ struct asoc_sdw_dailink *sof_dais;
+ struct snd_soc_aux_dev *sof_aux;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_aux = 0;
+ int num_confs;
+ struct snd_soc_dai_link *dai_links;
+ int num_links;
+ int be_id = 0;
+ int hdmi_num;
+ unsigned long ssp_mask;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ num_confs = num_ends;
+
+ /*
+ * One per DAI link, worst case is a DAI link for every endpoint, also
+ * add one additional to act as a terminator such that code can iterate
+ * until it hits an uninitialised DAI.
+ */
+ sof_dais = kcalloc(num_ends + 1, sizeof(*sof_dais), GFP_KERNEL);
+ if (!sof_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
+ if (!sof_ends) {
+ ret = -ENOMEM;
+ goto err_dai;
+ }
+
+ sof_aux = devm_kcalloc(dev, num_aux, sizeof(*sof_aux), GFP_KERNEL);
+ if (!sof_aux) {
+ ret = -ENOMEM;
+ goto err_dai;
+ }
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs);
+ if (ret < 0)
+ goto err_end;
+
+ sdw_be_num = ret;
+
+ /*
+ * on generic tgl platform, I2S or sdw mode is supported
+ * based on board rework. A ACPI device is registered in
+ * system only when I2S mode is supported, not sdw mode.
+ * Here check ACPI ID to confirm I2S is supported.
+ */
+ ssp_info = asoc_sdw_find_codec_info_acpi(mach->id);
+ if (ssp_info) {
+ ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk);
+ ssp_num = hweight_long(ssp_mask);
+ }
+
+ if (mach_params->codec_mask & IDISP_CODEC_MASK)
+ intel_ctx->hdmi.idisp_codec = true;
+
+ if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
+ hdmi_num = SOF_TGL_HDMI_COUNT;
+ else
+ hdmi_num = SOF_PRE_TGL_HDMI_COUNT;
+
+ /* enable dmic01 & dmic16k */
+ if (ctx->ignore_internal_dmic) {
+ dev_dbg(dev, "SoundWire DMIC is used, ignoring internal DMIC\n");
+ mach_params->dmic_num = 0;
+ } else if (mach_params->dmic_num) {
+ dmic_num = 2;
+ } else if (sof_sdw_quirk & SOC_SDW_PCH_DMIC) {
+ dmic_num = 2;
+ /*
+ * mach_params->dmic_num will be used to set the cfg-mics value of
+ * card->components string. Set it to the default value.
+ */
+ mach_params->dmic_num = DMIC_DEFAULT_CHANNELS;
+ }
+
+ if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT || mach_params->bt_link_mask)
+ bt_num = 1;
+
+ dev_dbg(dev, "DAI link numbers: sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n",
+ sdw_be_num, ssp_num, dmic_num,
+ intel_ctx->hdmi.idisp_codec ? hdmi_num : 0, bt_num);
+
+ codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf) {
+ ret = -ENOMEM;
+ goto err_end;
+ }
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links) {
+ ret = -ENOMEM;
+ goto err_end;
+ }
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_confs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+ card->aux_dev = sof_aux;
+ card->num_aux_devs = num_aux;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ sof_dais, &codec_conf);
+ if (ret)
+ goto err_end;
+ }
+
+ /* SSP */
+ if (ssp_num) {
+ ret = create_ssp_dailinks(card, &dai_links, &be_id,
+ ssp_info, ssp_mask);
+ if (ret)
+ goto err_end;
+ }
+
+ /* dmic */
+ if (dmic_num) {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id);
+ if (ret)
+ goto err_end;
+ }
+
+ /* HDMI */
+ ret = create_hdmi_dailinks(card, &dai_links, &be_id, hdmi_num);
+ if (ret)
+ goto err_end;
+
+ /* BT */
+ if (bt_num) {
+ ret = create_bt_dailinks(card, &dai_links, &be_id);
+ if (ret)
+ goto err_end;
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+err_end:
+ kfree(sof_ends);
+err_dai:
+ kfree(sof_dais);
+
+ return ret;
+}
+
+static int sof_sdw_card_late_probe(struct snd_soc_card *card)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+ int ret = 0;
+
+ ret = asoc_sdw_card_late_probe(card);
+ if (ret < 0)
+ return ret;
+
+ if (intel_ctx->hdmi.idisp_codec)
+ ret = sof_sdw_hdmi_card_late_probe(card);
+
+ return ret;
+}
+
+static int sof_sdw_add_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+
+ /* Ignore the HDMI PCM link if iDisp is not present */
+ if (strstr(link->stream_name, "HDMI") && !intel_ctx->hdmi.idisp_codec)
+ link->ignore = true;
+
+ return 0;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct asoc_sdw_mc_private *ctx;
+ struct intel_mc_ctx *intel_ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ dev_dbg(&pdev->dev, "Entry\n");
+
+ intel_ctx = devm_kzalloc(&pdev->dev, sizeof(*intel_ctx), GFP_KERNEL);
+ if (!intel_ctx)
+ return -ENOMEM;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->private = intel_ctx;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = sof_sdw_card_late_probe;
+ card->add_dai_link = sof_sdw_add_dai_link;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ if (mach->mach_params.subsystem_id_set) {
+ snd_soc_card_set_pci_ssid(card,
+ mach->mach_params.subsystem_vendor,
+ mach->mach_params.subsystem_device);
+ sof_sdw_check_ssid_quirk(mach);
+ }
+
+ dmi_check_system(sof_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ sof_sdw_quirk, quirk_override);
+ sof_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = sof_sdw_quirk;
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = sof_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+
+ if (mach->mach_params.dmic_num) {
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:dmic cfg-mics:%d",
+ card->components,
+ mach->mach_params.dmic_num);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "sof_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver sof_sdw_driver = {
+ .driver = {
+ .name = "sof_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(sof_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver");
+MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>");
+MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>");
+MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("SND_SOC_INTEL_HDA_DSP_COMMON");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h
new file mode 100644
index 000000000000..3aa1dcec5172
--- /dev/null
+++ b/sound/soc/intel/boards/sof_sdw_common.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+/*
+ * sof_sdw_common.h - prototypes for common helpers
+ */
+
+#ifndef SND_SOC_SOF_SDW_COMMON_H
+#define SND_SOC_SOF_SDW_COMMON_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+#include <sound/soc_sdw_utils.h>
+#include "sof_hdmi_common.h"
+
+#define MAX_HDMI_NUM 4
+#define SOC_SDW_MAX_CPU_DAIS 16
+#define SOC_SDW_INTEL_BIDIR_PDI_BASE 2
+
+/* 8 combinations with 4 links + unused group 0 */
+#define SDW_MAX_GROUPS 9
+
+enum {
+ SOF_PRE_TGL_HDMI_COUNT = 3,
+ SOF_TGL_HDMI_COUNT = 4,
+};
+
+enum {
+ SOF_I2S_SSP0 = BIT(0),
+ SOF_I2S_SSP1 = BIT(1),
+ SOF_I2S_SSP2 = BIT(2),
+ SOF_I2S_SSP3 = BIT(3),
+ SOF_I2S_SSP4 = BIT(4),
+ SOF_I2S_SSP5 = BIT(5),
+};
+
+/* Deprecated and no longer supported by the code */
+#define SOC_SDW_FOUR_SPK BIT(4)
+#define SOF_SDW_TGL_HDMI BIT(5)
+#define SOC_SDW_PCH_DMIC BIT(6)
+#define SOF_SSP_PORT(x) (((x) & GENMASK(5, 0)) << 7)
+#define SOF_SSP_GET_PORT(quirk) (((quirk) >> 7) & GENMASK(5, 0))
+/* Deprecated and no longer supported by the code */
+#define SOC_SDW_NO_AGGREGATION BIT(14)
+
+/* BT audio offload: reserve 3 bits for future */
+#define SOF_BT_OFFLOAD_SSP_SHIFT 15
+#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(17, 15))
+#define SOF_BT_OFFLOAD_SSP(quirk) \
+ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
+#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18)
+
+struct intel_mc_ctx {
+ struct sof_hdmi_private hdmi;
+ /* To store SDW Pin index for each SoundWire link */
+ unsigned int sdw_pin_index[SDW_INTEL_MAX_LINKS];
+};
+
+/* generic HDMI support */
+int sof_sdw_hdmi_init(struct snd_soc_pcm_runtime *rtd);
+
+int sof_sdw_hdmi_card_late_probe(struct snd_soc_card *card);
+
+#endif
diff --git a/sound/soc/intel/boards/sof_sdw_hdmi.c b/sound/soc/intel/boards/sof_sdw_hdmi.c
new file mode 100644
index 000000000000..f92867deb029
--- /dev/null
+++ b/sound/soc/intel/boards/sof_sdw_hdmi.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020 Intel Corporation
+
+/*
+ * sof_sdw_hdmi - Helpers to handle HDMI from generic machine driver
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/jack.h>
+#include "sof_sdw_common.h"
+#include "hda_dsp_common.h"
+
+int sof_sdw_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ intel_ctx->hdmi.hdmi_comp = dai->component;
+
+ return 0;
+}
+
+int sof_sdw_hdmi_card_late_probe(struct snd_soc_card *card)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+
+ if (!intel_ctx->hdmi.idisp_codec)
+ return 0;
+
+ if (!intel_ctx->hdmi.hdmi_comp)
+ return -EINVAL;
+
+ return hda_dsp_hdmi_build_controls(card, intel_ctx->hdmi.hdmi_comp);
+}
diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c
new file mode 100644
index 000000000000..729c0cd7c19c
--- /dev/null
+++ b/sound/soc/intel/boards/sof_ssp_amp.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation
+
+/*
+ * sof_ssp_amp.c - ASoc Machine driver for Intel platforms
+ * with RT1308/CS35L41 codec.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sof.h>
+#include "sof_board_helpers.h"
+#include "sof_realtek_common.h"
+#include "sof_cirrus_common.h"
+
+/* Driver-specific board quirks: from bit 0 to 7 */
+#define SOF_HDMI_PLAYBACK_PRESENT BIT(0)
+
+/* Default: SSP2 */
+static unsigned long sof_ssp_amp_quirk = SOF_SSP_PORT_AMP(2);
+
+static const struct dmi_system_id chromebook_platforms[] = {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {},
+};
+
+static int sof_card_late_probe(struct snd_soc_card *card)
+{
+ return sof_intel_board_card_late_probe(card);
+}
+
+static struct snd_soc_card sof_ssp_amp_card = {
+ .name = "ssp_amp",
+ .owner = THIS_MODULE,
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+/* BE ID defined in sof-tgl-rt1308-hdmi-ssp.m4 */
+#define HDMI_IN_BE_ID 0
+#define SPK_BE_ID 2
+#define DMIC01_BE_ID 3
+#define INTEL_HDMI_BE_ID 5
+/* extra BE links to support no-hdmi-in boards */
+#define DMIC16K_BE_ID 4
+#define BT_OFFLOAD_BE_ID 8
+
+#define SSP_AMP_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_HDMI_IN, \
+ SOF_LINK_AMP, \
+ SOF_LINK_DMIC01, \
+ SOF_LINK_DMIC16K, \
+ SOF_LINK_IDISP_HDMI, \
+ SOF_LINK_BT_OFFLOAD, \
+ SOF_LINK_NONE)
+
+#define SSP_AMP_LINK_IDS SOF_LINK_ORDER(HDMI_IN_BE_ID, \
+ SPK_BE_ID, \
+ DMIC01_BE_ID, \
+ DMIC16K_BE_ID, \
+ INTEL_HDMI_BE_ID, \
+ BT_OFFLOAD_BE_ID, \
+ 0)
+
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ int ret;
+
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
+
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
+
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
+ }
+
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_CS35L41:
+ cs35l41_set_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1308:
+ sof_rt1308_dai_link(ctx->amp_link);
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sof_ssp_amp_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
+ struct sof_card_private *ctx;
+ int ret;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ sof_ssp_amp_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ dev_dbg(&pdev->dev, "sof_ssp_amp_quirk = %lx\n", sof_ssp_amp_quirk);
+
+ /* initialize ctx with board quirk */
+ ctx = sof_intel_board_get_ctx(&pdev->dev, sof_ssp_amp_quirk);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (!dmi_check_system(chromebook_platforms) &&
+ (mach->mach_params.dmic_num == 0))
+ ctx->dmic_be_num = 0;
+
+ if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) {
+ if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
+ ctx->hdmi.idisp_codec = true;
+ } else {
+ ctx->hdmi_num = 0;
+ }
+
+ ctx->link_order_overwrite = SSP_AMP_LINK_ORDER;
+
+ if (ctx->ssp_mask_hdmi_in) {
+ /* the topology supports HDMI-IN uses fixed BE ID for DAI links */
+ ctx->link_id_overwrite = SSP_AMP_LINK_IDS;
+ }
+
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &sof_ssp_amp_card, ctx);
+ if (ret)
+ return ret;
+
+ /* update codec_conf */
+ switch (ctx->amp_type) {
+ case CODEC_CS35L41:
+ cs35l41_set_codec_conf(&sof_ssp_amp_card);
+ break;
+ case CODEC_RT1308:
+ case CODEC_NONE:
+ /* no codec conf required */
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
+ }
+
+ sof_ssp_amp_card.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_ssp_amp_card,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ snd_soc_card_set_drvdata(&sof_ssp_amp_card, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev, &sof_ssp_amp_card);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "sof_ssp_amp",
+ },
+ {
+ .name = "tgl_rt1308_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_AMP(2) |
+ SOF_SSP_MASK_HDMI_CAPTURE(0x22)),
+ /* SSP 1 and SSP 5 are used for HDMI IN */
+ },
+ {
+ .name = "adl_cs35l41",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_AMP(1) |
+ SOF_NUM_IDISP_HDMI(4) |
+ SOF_HDMI_PLAYBACK_PRESENT |
+ SOF_SSP_PORT_BT_OFFLOAD(2) |
+ SOF_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "adl_lt6911_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_HDMI_PLAYBACK_PRESENT),
+ },
+ {
+ .name = "rpl_lt6911_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_HDMI_PLAYBACK_PRESENT),
+ },
+ {
+ .name = "mtl_lt6911_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_HDMI_PLAYBACK_PRESENT),
+ },
+ {
+ .name = "arl_lt6911_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_HDMI_PLAYBACK_PRESENT),
+ },
+ {
+ .name = "ptl_lt6911_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_HDMI_PLAYBACK_PRESENT),
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver sof_ssp_amp_driver = {
+ .probe = sof_ssp_amp_probe,
+ .driver = {
+ .name = "sof_ssp_amp",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(sof_ssp_amp_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
+MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_CIRRUS_COMMON");
diff --git a/sound/soc/intel/boards/sof_wm8804.c b/sound/soc/intel/boards/sof_wm8804.c
new file mode 100644
index 000000000000..51922347409f
--- /dev/null
+++ b/sound/soc/intel/boards/sof_wm8804.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, Intel Corporation
+//
+// sof-wm8804.c - ASoC machine driver for Up and Up2 board
+// based on WM8804/Hifiberry Digi+
+
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/wm8804.h"
+
+struct sof_card_private {
+ struct gpio_desc *gpio_44;
+ struct gpio_desc *gpio_48;
+ int sample_rate;
+};
+
+#define SOF_WM8804_UP2_QUIRK BIT(0)
+
+static unsigned long sof_wm8804_quirk;
+
+static int sof_wm8804_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_wm8804_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_wm8804_quirk_table[] = {
+ {
+ .callback = sof_wm8804_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UP-APL01"),
+ },
+ .driver_data = (void *)SOF_WM8804_UP2_QUIRK,
+ },
+ {}
+};
+
+static int sof_wm8804_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *codec = codec_dai->component;
+ const int sysclk = 27000000; /* This is fixed on this board */
+ int samplerate;
+ long mclk_freq;
+ int mclk_div;
+ int sampling_freq;
+ bool clk_44;
+ int ret;
+
+ samplerate = params_rate(params);
+ if (samplerate == ctx->sample_rate)
+ return 0;
+
+ ctx->sample_rate = 0;
+
+ if (samplerate <= 96000) {
+ mclk_freq = samplerate * 256;
+ mclk_div = WM8804_MCLKDIV_256FS;
+ } else {
+ mclk_freq = samplerate * 128;
+ mclk_div = WM8804_MCLKDIV_128FS;
+ }
+
+ switch (samplerate) {
+ case 32000:
+ sampling_freq = 0x03;
+ break;
+ case 44100:
+ sampling_freq = 0x00;
+ break;
+ case 48000:
+ sampling_freq = 0x02;
+ break;
+ case 88200:
+ sampling_freq = 0x08;
+ break;
+ case 96000:
+ sampling_freq = 0x0a;
+ break;
+ case 176400:
+ sampling_freq = 0x0c;
+ break;
+ case 192000:
+ sampling_freq = 0x0e;
+ break;
+ default:
+ dev_err(rtd->card->dev,
+ "unsupported samplerate %d\n", samplerate);
+ return -EINVAL;
+ }
+
+ if (samplerate % 16000)
+ clk_44 = true; /* use 44.1 kHz root frequency */
+ else
+ clk_44 = false;
+
+ if (!(IS_ERR_OR_NULL(ctx->gpio_44) ||
+ IS_ERR_OR_NULL(ctx->gpio_48))) {
+ /*
+ * ensure both GPIOs are LOW first, then drive the
+ * relevant one to HIGH
+ */
+ if (clk_44) {
+ gpiod_set_value_cansleep(ctx->gpio_48, !clk_44);
+ gpiod_set_value_cansleep(ctx->gpio_44, clk_44);
+ } else {
+ gpiod_set_value_cansleep(ctx->gpio_44, clk_44);
+ gpiod_set_value_cansleep(ctx->gpio_48, !clk_44);
+ }
+ }
+
+ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set WM8804 PLL\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
+ sysclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "Failed to set WM8804 SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* set sampling frequency status bits */
+ snd_soc_component_update_bits(codec, WM8804_SPDTX4, 0x0f,
+ sampling_freq);
+
+ ctx->sample_rate = samplerate;
+
+ return 0;
+}
+
+/* machine stream operations */
+static const struct snd_soc_ops sof_wm8804_ops = {
+ .hw_params = sof_wm8804_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(ssp5_pin,
+ DAILINK_COMP_ARRAY(COMP_CPU("SSP5 Pin")));
+
+SND_SOC_DAILINK_DEF(ssp5_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-1AEC8804:00", "wm8804-spdif")));
+
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:0e.0")));
+
+static struct snd_soc_dai_link dailink[] = {
+ /* back ends */
+ {
+ .name = "SSP5-Codec",
+ .id = 0,
+ .no_pcm = 1,
+ .ops = &sof_wm8804_ops,
+ SND_SOC_DAILINK_REG(ssp5_pin, ssp5_codec, platform),
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card sof_wm8804_card = {
+ .name = "wm8804", /* sof- prefix added automatically */
+ .owner = THIS_MODULE,
+ .dai_link = dailink,
+ .num_links = ARRAY_SIZE(dailink),
+};
+
+ /* i2c-<HID>:00 with HID being 8 chars */
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+/*
+ * to control the HifiBerry Digi+ PRO, it's required to toggle GPIO to
+ * select the clock source. On the Up2 board, this means
+ * Pin29/BCM5/Linux GPIO 430 and Pin 31/BCM6/ Linux GPIO 404.
+ *
+ * Using the ACPI device name is not very nice, but since we only use
+ * the value for the Up2 board there is no risk of conflict with other
+ * platforms.
+ */
+
+static struct gpiod_lookup_table up2_gpios_table = {
+ /* .dev_id is set during probe */
+ .table = {
+ GPIO_LOOKUP("INT3452:01", 73, "BCM-GPIO5", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("INT3452:01", 74, "BCM-GPIO6", GPIO_ACTIVE_HIGH),
+ { },
+ },
+};
+
+static int sof_wm8804_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct snd_soc_acpi_mach *mach;
+ struct sof_card_private *ctx;
+ struct acpi_device *adev;
+ int dai_index = 0;
+ int ret;
+ int i;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mach = pdev->dev.platform_data;
+ card = &sof_wm8804_card;
+ card->dev = &pdev->dev;
+
+ dmi_check_system(sof_wm8804_quirk_table);
+
+ if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK) {
+ up2_gpios_table.dev_id = dev_name(&pdev->dev);
+ gpiod_add_lookup_table(&up2_gpios_table);
+
+ /*
+ * The gpios are required for specific boards with
+ * local oscillators, and optional in other cases.
+ * Since we can't identify when they are needed, use
+ * the GPIO as non-optional
+ */
+
+ ctx->gpio_44 = devm_gpiod_get(&pdev->dev, "BCM-GPIO5",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->gpio_44)) {
+ ret = PTR_ERR(ctx->gpio_44);
+ dev_err(&pdev->dev,
+ "could not get BCM-GPIO5: %d\n",
+ ret);
+ return ret;
+ }
+
+ ctx->gpio_48 = devm_gpiod_get(&pdev->dev, "BCM-GPIO6",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->gpio_48)) {
+ ret = PTR_ERR(ctx->gpio_48);
+ dev_err(&pdev->dev,
+ "could not get BCM-GPIO6: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* fix index of codec dai */
+ for (i = 0; i < ARRAY_SIZE(dailink); i++) {
+ if (!strcmp(dailink[i].codecs->name, "i2c-1AEC8804:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* fixup codec name based on HID */
+ adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+ if (adev) {
+ snprintf(codec_name, sizeof(codec_name),
+ "%s%s", "i2c-", acpi_dev_name(adev));
+ dailink[dai_index].codecs->name = codec_name;
+ } else {
+ dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+ return -ENOENT;
+ }
+
+ acpi_dev_put(adev);
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static void sof_wm8804_remove(struct platform_device *pdev)
+{
+ if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK)
+ gpiod_remove_lookup_table(&up2_gpios_table);
+}
+
+static struct platform_driver sof_wm8804_driver = {
+ .driver = {
+ .name = "sof-wm8804",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = sof_wm8804_probe,
+ .remove = sof_wm8804_remove,
+};
+module_platform_driver(sof_wm8804_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) SOF + WM8804 Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sof-wm8804");
diff --git a/sound/soc/intel/catpt/Makefile b/sound/soc/intel/catpt/Makefile
new file mode 100644
index 000000000000..f5f6a7e956ce
--- /dev/null
+++ b/sound/soc/intel/catpt/Makefile
@@ -0,0 +1,6 @@
+snd-soc-catpt-y := device.o dsp.o loader.o ipc.o messages.o pcm.o sysfs.o
+
+# tell define_trace.h where to find the trace header
+CFLAGS_device.o := -I$(src)
+
+obj-$(CONFIG_SND_SOC_INTEL_CATPT) += snd-soc-catpt.o
diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h
new file mode 100644
index 000000000000..c01d27e9fd88
--- /dev/null
+++ b/sound/soc/intel/catpt/core.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2020 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SND_SOC_INTEL_CATPT_CORE_H
+#define __SND_SOC_INTEL_CATPT_CORE_H
+
+#include <linux/dma/dw.h>
+#include <linux/irqreturn.h>
+#include "messages.h"
+#include "registers.h"
+
+struct catpt_dev;
+
+extern const struct attribute_group *catpt_attr_groups[];
+
+void catpt_sram_init(struct resource *sram, u32 start, u32 size);
+void catpt_sram_free(struct resource *sram);
+struct resource *
+catpt_request_region(struct resource *root, resource_size_t size);
+
+struct catpt_ipc_msg {
+ union {
+ u32 header;
+ union catpt_global_msg rsp;
+ };
+ void *data;
+ size_t size;
+};
+
+struct catpt_ipc {
+ struct device *dev;
+
+ struct catpt_ipc_msg rx;
+ struct catpt_fw_ready config;
+ u32 default_timeout;
+ bool ready;
+
+ spinlock_t lock;
+ struct mutex mutex;
+ struct completion done_completion;
+ struct completion busy_completion;
+};
+
+void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev);
+
+struct catpt_module_type {
+ bool loaded;
+ u32 entry_point;
+ u32 persistent_size;
+ u32 scratch_size;
+ /* DRAM, initial module state */
+ u32 state_offset;
+ u32 state_size;
+
+ struct list_head node;
+};
+
+struct catpt_spec {
+ struct snd_soc_acpi_mach *machines;
+ u8 core_id;
+ u32 host_dram_offset;
+ u32 host_iram_offset;
+ u32 host_shim_offset;
+ u32 host_dma_offset[CATPT_DMA_COUNT];
+ u32 host_ssp_offset[CATPT_SSP_COUNT];
+ u32 dram_mask;
+ u32 iram_mask;
+ u32 d3srampgd_bit;
+ u32 d3pgd_bit;
+ void (*pll_shutdown)(struct catpt_dev *cdev, bool enable);
+};
+
+struct catpt_dev {
+ struct device *dev;
+ struct dw_dma_chip *dmac;
+ struct catpt_ipc ipc;
+
+ void __iomem *pci_ba;
+ void __iomem *lpe_ba;
+ u32 lpe_base;
+ int irq;
+
+ const struct catpt_spec *spec;
+ struct completion fw_ready;
+
+ struct resource dram;
+ struct resource iram;
+ struct resource *scratch;
+
+ struct catpt_mixer_stream_info mixer;
+ struct catpt_module_type modules[CATPT_MODULE_COUNT];
+ struct catpt_ssp_device_format devfmt[CATPT_SSP_COUNT];
+ struct list_head stream_list;
+ spinlock_t list_lock;
+ struct mutex clk_mutex;
+
+ struct catpt_dx_context dx_ctx;
+ void *dxbuf_vaddr;
+ dma_addr_t dxbuf_paddr;
+};
+
+int catpt_dmac_probe(struct catpt_dev *cdev);
+void catpt_dmac_remove(struct catpt_dev *cdev);
+struct dma_chan *catpt_dma_request_config_chan(struct catpt_dev *cdev);
+int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan,
+ dma_addr_t dst_addr, dma_addr_t src_addr,
+ size_t size);
+int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan,
+ dma_addr_t dst_addr, dma_addr_t src_addr,
+ size_t size);
+
+void lpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable);
+void wpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable);
+int catpt_dsp_power_up(struct catpt_dev *cdev);
+int catpt_dsp_power_down(struct catpt_dev *cdev);
+int catpt_dsp_stall(struct catpt_dev *cdev, bool stall);
+void catpt_dsp_update_srampge(struct catpt_dev *cdev, struct resource *sram,
+ unsigned long mask);
+int catpt_dsp_update_lpclock(struct catpt_dev *cdev);
+irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id);
+irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id);
+
+/*
+ * IPC handlers may return positive values which denote successful
+ * HOST <-> DSP communication yet failure to process specific request.
+ * Use below macro to convert returned non-zero values appropriately
+ */
+#define CATPT_IPC_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO)
+
+int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
+ struct catpt_ipc_msg request,
+ struct catpt_ipc_msg *reply, int timeout);
+int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
+ struct catpt_ipc_msg *reply);
+
+int catpt_first_boot_firmware(struct catpt_dev *cdev);
+int catpt_boot_firmware(struct catpt_dev *cdev, bool restore);
+int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan);
+int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan);
+int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan);
+int catpt_coredump(struct catpt_dev *cdev);
+
+#include <sound/memalloc.h>
+#include <uapi/sound/asound.h>
+
+struct snd_pcm_substream;
+struct catpt_stream_template;
+
+struct catpt_stream_runtime {
+ struct snd_pcm_substream *substream;
+
+ struct catpt_stream_template *template;
+ struct catpt_stream_info info;
+ struct resource *persistent;
+ struct snd_dma_buffer pgtbl;
+
+ bool allocated;
+ bool prepared;
+
+ struct list_head node;
+};
+
+int catpt_register_plat_component(struct catpt_dev *cdev);
+void catpt_stream_update_position(struct catpt_dev *cdev,
+ struct catpt_stream_runtime *stream,
+ struct catpt_notify_position *pos);
+struct catpt_stream_runtime *
+catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id);
+int catpt_arm_stream_templates(struct catpt_dev *cdev);
+
+#endif
diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c
new file mode 100644
index 000000000000..d13062c8e907
--- /dev/null
+++ b/sound/soc/intel/catpt/device.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+// Special thanks to:
+// Marcin Barlik <marcin.barlik@intel.com>
+// Piotr Papierkowski <piotr.papierkowski@intel.com>
+//
+// for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and
+// helping backtrack its historical background
+//
+
+#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "core.h"
+#include "registers.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+static int catpt_do_suspend(struct device *dev)
+{
+ struct catpt_dev *cdev = dev_get_drvdata(dev);
+ struct dma_chan *chan;
+ int ret;
+
+ chan = catpt_dma_request_config_chan(cdev);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
+ ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
+ if (ret) {
+ ret = CATPT_IPC_ERROR(ret);
+ goto release_dma_chan;
+ }
+
+ ret = catpt_dsp_stall(cdev, true);
+ if (ret)
+ goto release_dma_chan;
+
+ ret = catpt_store_memdumps(cdev, chan);
+ if (ret) {
+ dev_err(cdev->dev, "store memdumps failed: %d\n", ret);
+ goto release_dma_chan;
+ }
+
+ ret = catpt_store_module_states(cdev, chan);
+ if (ret) {
+ dev_err(cdev->dev, "store module states failed: %d\n", ret);
+ goto release_dma_chan;
+ }
+
+ ret = catpt_store_streams_context(cdev, chan);
+ if (ret)
+ dev_err(cdev->dev, "store streams ctx failed: %d\n", ret);
+
+release_dma_chan:
+ dma_release_channel(chan);
+ if (ret)
+ return ret;
+ return catpt_dsp_power_down(cdev);
+}
+
+/* Do not block the system from suspending, recover on resume() if needed. */
+static int catpt_suspend(struct device *dev)
+{
+ catpt_do_suspend(dev);
+ return 0;
+}
+
+static int catpt_resume(struct device *dev)
+{
+ struct catpt_dev *cdev = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = catpt_dsp_power_up(cdev);
+ if (ret)
+ return ret;
+
+ if (!try_module_get(dev->driver->owner)) {
+ dev_info(dev, "module unloading, skipping fw boot\n");
+ return 0;
+ }
+ module_put(dev->driver->owner);
+
+ ret = catpt_boot_firmware(cdev, true);
+ if (ret) {
+ dev_err(cdev->dev, "boot firmware failed: %d\n", ret);
+ return ret;
+ }
+
+ /* reconfigure SSP devices after Dx transition */
+ for (i = 0; i < CATPT_SSP_COUNT; i++) {
+ if (cdev->devfmt[i].iface == UINT_MAX)
+ continue;
+
+ ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+ }
+
+ return 0;
+}
+
+static int catpt_runtime_suspend(struct device *dev)
+{
+ if (!try_module_get(dev->driver->owner)) {
+ dev_info(dev, "module unloading, skipping suspend\n");
+ return 0;
+ }
+ module_put(dev->driver->owner);
+
+ return catpt_do_suspend(dev);
+}
+
+static int catpt_runtime_resume(struct device *dev)
+{
+ return catpt_resume(dev);
+}
+
+static const struct dev_pm_ops catpt_dev_pm = {
+ SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume)
+ RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL)
+};
+
+/* machine board owned by CATPT is removed with this hook */
+static void board_pdev_unregister(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int catpt_register_board(struct catpt_dev *cdev)
+{
+ const struct catpt_spec *spec = cdev->spec;
+ struct snd_soc_acpi_mach *mach;
+ struct platform_device *board;
+
+ mach = snd_soc_acpi_find_machine(spec->machines);
+ if (!mach) {
+ dev_info(cdev->dev, "no machines present\n");
+ return 0;
+ }
+
+ mach->mach_params.platform = "catpt-platform";
+ board = platform_device_register_data(NULL, mach->drv_name,
+ PLATFORM_DEVID_NONE,
+ (const void *)mach, sizeof(*mach));
+ if (IS_ERR(board)) {
+ dev_err(cdev->dev, "board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ return devm_add_action_or_reset(cdev->dev, board_pdev_unregister,
+ board);
+}
+
+static int catpt_probe_components(struct catpt_dev *cdev)
+{
+ int ret;
+
+ ret = catpt_dsp_power_up(cdev);
+ if (ret)
+ return ret;
+
+ ret = catpt_dmac_probe(cdev);
+ if (ret) {
+ dev_err(cdev->dev, "DMAC probe failed: %d\n", ret);
+ goto err_dmac_probe;
+ }
+
+ ret = catpt_first_boot_firmware(cdev);
+ if (ret) {
+ dev_err(cdev->dev, "first fw boot failed: %d\n", ret);
+ goto err_boot_fw;
+ }
+
+ ret = catpt_register_plat_component(cdev);
+ if (ret) {
+ dev_err(cdev->dev, "register plat comp failed: %d\n", ret);
+ goto err_boot_fw;
+ }
+
+ /* reflect actual ADSP state in pm_runtime */
+ pm_runtime_set_active(cdev->dev);
+
+ pm_runtime_set_autosuspend_delay(cdev->dev, 2000);
+ pm_runtime_use_autosuspend(cdev->dev);
+ pm_runtime_mark_last_busy(cdev->dev);
+ /* Enable PM before spawning child device. See catpt_dai_pcm_new(). */
+ pm_runtime_enable(cdev->dev);
+
+ ret = catpt_register_board(cdev);
+ if (ret) {
+ dev_err(cdev->dev, "register board failed: %d\n", ret);
+ goto err_reg_board;
+ }
+
+ return 0;
+
+err_reg_board:
+ pm_runtime_disable(cdev->dev);
+ snd_soc_unregister_component(cdev->dev);
+err_boot_fw:
+ catpt_dmac_remove(cdev);
+err_dmac_probe:
+ catpt_dsp_power_down(cdev);
+
+ return ret;
+}
+
+static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev,
+ const struct catpt_spec *spec)
+{
+ cdev->dev = dev;
+ cdev->spec = spec;
+ init_completion(&cdev->fw_ready);
+ INIT_LIST_HEAD(&cdev->stream_list);
+ spin_lock_init(&cdev->list_lock);
+ mutex_init(&cdev->clk_mutex);
+
+ /*
+ * Mark both device formats as uninitialized. Once corresponding
+ * cpu_dai's pcm is created, proper values are assigned.
+ */
+ cdev->devfmt[CATPT_SSP_IFACE_0].iface = UINT_MAX;
+ cdev->devfmt[CATPT_SSP_IFACE_1].iface = UINT_MAX;
+
+ catpt_ipc_init(&cdev->ipc, dev);
+
+ catpt_sram_init(&cdev->dram, spec->host_dram_offset,
+ catpt_dram_size(cdev));
+ catpt_sram_init(&cdev->iram, spec->host_iram_offset,
+ catpt_iram_size(cdev));
+}
+
+static int catpt_acpi_probe(struct platform_device *pdev)
+{
+ const struct catpt_spec *spec;
+ struct catpt_dev *cdev;
+ struct device *dev = &pdev->dev;
+ const struct acpi_device_id *id;
+ struct resource *res;
+ int ret;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
+ dev_dbg(dev, "CATPT ACPI driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+
+ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ spec = (const struct catpt_spec *)id->driver_data;
+ catpt_dev_init(cdev, dev, spec);
+
+ /* map DSP bar address */
+ cdev->lpe_ba = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(cdev->lpe_ba))
+ return PTR_ERR(cdev->lpe_ba);
+ cdev->lpe_base = res->start;
+
+ /* map PCI bar address */
+ cdev->pci_ba = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(cdev->pci_ba))
+ return PTR_ERR(cdev->pci_ba);
+
+ /* alloc buffer for storing DRAM context during dx transitions */
+ cdev->dxbuf_vaddr = dmam_alloc_coherent(dev, catpt_dram_size(cdev),
+ &cdev->dxbuf_paddr, GFP_KERNEL);
+ if (!cdev->dxbuf_vaddr)
+ return -ENOMEM;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ cdev->irq = ret;
+
+ platform_set_drvdata(pdev, cdev);
+
+ ret = devm_request_threaded_irq(dev, cdev->irq, catpt_dsp_irq_handler,
+ catpt_dsp_irq_thread,
+ IRQF_SHARED, "AudioDSP", cdev);
+ if (ret)
+ return ret;
+
+ return catpt_probe_components(cdev);
+}
+
+static void catpt_acpi_remove(struct platform_device *pdev)
+{
+ struct catpt_dev *cdev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(cdev->dev);
+
+ snd_soc_unregister_component(cdev->dev);
+ catpt_dmac_remove(cdev);
+ catpt_dsp_power_down(cdev);
+
+ catpt_sram_free(&cdev->iram);
+ catpt_sram_free(&cdev->dram);
+}
+
+static struct snd_soc_acpi_mach lpt_machines[] = {
+ {
+ .id = "INT33CA",
+ .drv_name = "hsw_rt5640",
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach wpt_machines[] = {
+ {
+ .id = "INT33CA",
+ .drv_name = "hsw_rt5640",
+ },
+ {
+ .id = "INT343A",
+ .drv_name = "bdw_rt286",
+ },
+ {
+ .id = "10EC5650",
+ .drv_name = "bdw-rt5650",
+ },
+ {
+ .id = "RT5677CE",
+ .drv_name = "bdw-rt5677",
+ },
+ {}
+};
+
+static struct catpt_spec lpt_desc = {
+ .machines = lpt_machines,
+ .core_id = 0x01,
+ .host_dram_offset = 0x000000,
+ .host_iram_offset = 0x080000,
+ .host_shim_offset = 0x0E7000,
+ .host_dma_offset = { 0x0F0000, 0x0F8000 },
+ .host_ssp_offset = { 0x0E8000, 0x0E9000 },
+ .dram_mask = LPT_VDRTCTL0_DSRAMPGE_MASK,
+ .iram_mask = LPT_VDRTCTL0_ISRAMPGE_MASK,
+ .d3srampgd_bit = LPT_VDRTCTL0_D3SRAMPGD,
+ .d3pgd_bit = LPT_VDRTCTL0_D3PGD,
+ .pll_shutdown = lpt_dsp_pll_shutdown,
+};
+
+static struct catpt_spec wpt_desc = {
+ .machines = wpt_machines,
+ .core_id = 0x02,
+ .host_dram_offset = 0x000000,
+ .host_iram_offset = 0x0A0000,
+ .host_shim_offset = 0x0FB000,
+ .host_dma_offset = { 0x0FE000, 0x0FF000 },
+ .host_ssp_offset = { 0x0FC000, 0x0FD000 },
+ .dram_mask = WPT_VDRTCTL0_DSRAMPGE_MASK,
+ .iram_mask = WPT_VDRTCTL0_ISRAMPGE_MASK,
+ .d3srampgd_bit = WPT_VDRTCTL0_D3SRAMPGD,
+ .d3pgd_bit = WPT_VDRTCTL0_D3PGD,
+ .pll_shutdown = wpt_dsp_pll_shutdown,
+};
+
+static const struct acpi_device_id catpt_ids[] = {
+ { "INT33C8", (unsigned long)&lpt_desc },
+ { "INT3438", (unsigned long)&wpt_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, catpt_ids);
+
+static struct platform_driver catpt_acpi_driver = {
+ .probe = catpt_acpi_probe,
+ .remove = catpt_acpi_remove,
+ .driver = {
+ .name = "intel_catpt",
+ .acpi_match_table = catpt_ids,
+ .pm = pm_ptr(&catpt_dev_pm),
+ .dev_groups = catpt_attr_groups,
+ },
+};
+module_platform_driver(catpt_acpi_driver);
+
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c
new file mode 100644
index 000000000000..008a20a2acbd
--- /dev/null
+++ b/sound/soc/intel/catpt/dsp.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/pxa2xx_ssp.h>
+#include "core.h"
+#include "messages.h"
+#include "registers.h"
+
+static bool catpt_dma_filter(struct dma_chan *chan, void *param)
+{
+ return param == chan->device->dev;
+}
+
+/*
+ * Either engine 0 or 1 can be used for image loading.
+ * Align with Windows driver equivalent and stick to engine 1.
+ */
+#define CATPT_DMA_DEVID 1
+#define CATPT_DMA_DSP_ADDR_MASK GENMASK(31, 20)
+
+struct dma_chan *catpt_dma_request_config_chan(struct catpt_dev *cdev)
+{
+ struct dma_slave_config config;
+ struct dma_chan *chan;
+ dma_cap_mask_t mask;
+ int ret;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ chan = dma_request_channel(mask, catpt_dma_filter, cdev->dev);
+ if (!chan) {
+ dev_err(cdev->dev, "request channel failed\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.direction = DMA_MEM_TO_DEV;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.src_maxburst = 16;
+ config.dst_maxburst = 16;
+
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret) {
+ dev_err(cdev->dev, "slave config failed: %d\n", ret);
+ dma_release_channel(chan);
+ return ERR_PTR(ret);
+ }
+
+ return chan;
+}
+
+static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan,
+ dma_addr_t dst_addr, dma_addr_t src_addr,
+ size_t size)
+{
+ struct dma_async_tx_descriptor *desc;
+ enum dma_status status;
+ int ret;
+
+ desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, size,
+ DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(cdev->dev, "prep dma memcpy failed\n");
+ return -EIO;
+ }
+
+ /* enable demand mode for dma channel */
+ catpt_updatel_shim(cdev, HMDC,
+ CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id),
+ CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id));
+
+ ret = dma_submit_error(dmaengine_submit(desc));
+ if (ret) {
+ dev_err(cdev->dev, "submit tx failed: %d\n", ret);
+ goto clear_hdda;
+ }
+
+ status = dma_wait_for_async_tx(desc);
+ ret = (status == DMA_COMPLETE) ? 0 : -EPROTO;
+
+clear_hdda:
+ /* regardless of status, disable access to HOST memory in demand mode */
+ catpt_updatel_shim(cdev, HMDC,
+ CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), 0);
+
+ return ret;
+}
+
+int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan,
+ dma_addr_t dst_addr, dma_addr_t src_addr,
+ size_t size)
+{
+ return catpt_dma_memcpy(cdev, chan, dst_addr | CATPT_DMA_DSP_ADDR_MASK,
+ src_addr, size);
+}
+
+int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan,
+ dma_addr_t dst_addr, dma_addr_t src_addr,
+ size_t size)
+{
+ return catpt_dma_memcpy(cdev, chan, dst_addr,
+ src_addr | CATPT_DMA_DSP_ADDR_MASK, size);
+}
+
+int catpt_dmac_probe(struct catpt_dev *cdev)
+{
+ struct dw_dma_chip *dmac;
+ int ret;
+
+ dmac = devm_kzalloc(cdev->dev, sizeof(*dmac), GFP_KERNEL);
+ if (!dmac)
+ return -ENOMEM;
+
+ dmac->regs = cdev->lpe_ba + cdev->spec->host_dma_offset[CATPT_DMA_DEVID];
+ dmac->dev = cdev->dev;
+ dmac->irq = cdev->irq;
+
+ ret = dma_coerce_mask_and_coherent(cdev->dev, DMA_BIT_MASK(31));
+ if (ret)
+ return ret;
+ /*
+ * Caller is responsible for putting device in D0 to allow
+ * for I/O and memory access before probing DW.
+ */
+ ret = dw_dma_probe(dmac);
+ if (ret)
+ return ret;
+
+ cdev->dmac = dmac;
+ return 0;
+}
+
+void catpt_dmac_remove(struct catpt_dev *cdev)
+{
+ /*
+ * As do_dma_remove() juggles with pm_runtime_get_xxx() and
+ * pm_runtime_put_xxx() while both ADSP and DW 'devices' are part of
+ * the same module, caller makes sure pm_runtime_disable() is invoked
+ * before removing DW to prevent postmortem resume and suspend.
+ */
+ dw_dma_remove(cdev->dmac);
+}
+
+static void catpt_dsp_set_srampge(struct catpt_dev *cdev, struct resource *sram,
+ unsigned long mask, unsigned long new)
+{
+ unsigned long old;
+ u32 off = sram->start;
+ unsigned long b = __ffs(mask);
+
+ old = catpt_readl_pci(cdev, VDRTCTL0) & mask;
+ dev_dbg(cdev->dev, "SRAMPGE [0x%08lx] 0x%08lx -> 0x%08lx",
+ mask, old, new);
+
+ if (old == new)
+ return;
+
+ catpt_updatel_pci(cdev, VDRTCTL0, mask, new);
+ /* wait for SRAM power gating to propagate */
+ udelay(60);
+
+ /*
+ * Dummy read as the very first access after block enable
+ * to prevent byte loss in future operations.
+ */
+ for_each_clear_bit_from(b, &new, fls_long(mask)) {
+ u8 buf[4];
+
+ /* newly enabled: new bit=0 while old bit=1 */
+ if (test_bit(b, &old)) {
+ dev_dbg(cdev->dev, "sanitize block %ld: off 0x%08x\n",
+ b - __ffs(mask), off);
+ memcpy_fromio(buf, cdev->lpe_ba + off, sizeof(buf));
+ }
+ off += CATPT_MEMBLOCK_SIZE;
+ }
+}
+
+void catpt_dsp_update_srampge(struct catpt_dev *cdev, struct resource *sram,
+ unsigned long mask)
+{
+ struct resource *res;
+ unsigned long new = 0;
+
+ /* flag all busy blocks */
+ for (res = sram->child; res; res = res->sibling) {
+ u32 h, l;
+
+ h = (res->end - sram->start) / CATPT_MEMBLOCK_SIZE;
+ l = (res->start - sram->start) / CATPT_MEMBLOCK_SIZE;
+ new |= GENMASK(h, l);
+ }
+
+ /* offset value given mask's start and invert it as ON=b0 */
+ new = ~(new << __ffs(mask)) & mask;
+
+ /* disable core clock gating */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0);
+
+ catpt_dsp_set_srampge(cdev, sram, mask, new);
+
+ /* enable core clock gating */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE,
+ CATPT_VDRTCTL2_DCLCGE);
+}
+
+int catpt_dsp_stall(struct catpt_dev *cdev, bool stall)
+{
+ u32 reg, val;
+
+ val = stall ? CATPT_CS_STALL : 0;
+ catpt_updatel_shim(cdev, CS1, CATPT_CS_STALL, val);
+
+ return catpt_readl_poll_shim(cdev, CS1,
+ reg, (reg & CATPT_CS_STALL) == val,
+ 500, 10000);
+}
+
+static int catpt_dsp_reset(struct catpt_dev *cdev, bool reset)
+{
+ u32 reg, val;
+
+ val = reset ? CATPT_CS_RST : 0;
+ catpt_updatel_shim(cdev, CS1, CATPT_CS_RST, val);
+
+ return catpt_readl_poll_shim(cdev, CS1,
+ reg, (reg & CATPT_CS_RST) == val,
+ 500, 10000);
+}
+
+void lpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable)
+{
+ u32 val;
+
+ val = enable ? LPT_VDRTCTL0_APLLSE : 0;
+ catpt_updatel_pci(cdev, VDRTCTL0, LPT_VDRTCTL0_APLLSE, val);
+}
+
+void wpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable)
+{
+ u32 val;
+
+ val = enable ? WPT_VDRTCTL2_APLLSE : 0;
+ catpt_updatel_pci(cdev, VDRTCTL2, WPT_VDRTCTL2_APLLSE, val);
+}
+
+static int catpt_dsp_select_lpclock(struct catpt_dev *cdev, bool lp, bool waiti)
+{
+ u32 mask, reg, val;
+ int ret;
+
+ mutex_lock(&cdev->clk_mutex);
+
+ val = lp ? CATPT_CS_LPCS : 0;
+ reg = catpt_readl_shim(cdev, CS1) & CATPT_CS_LPCS;
+ dev_dbg(cdev->dev, "LPCS [0x%08lx] 0x%08x -> 0x%08x",
+ CATPT_CS_LPCS, reg, val);
+
+ if (reg == val) {
+ mutex_unlock(&cdev->clk_mutex);
+ return 0;
+ }
+
+ if (waiti) {
+ /* wait for DSP to signal WAIT state */
+ ret = catpt_readl_poll_shim(cdev, ISD,
+ reg, (reg & CATPT_ISD_DCPWM),
+ 500, 10000);
+ if (ret) {
+ dev_warn(cdev->dev, "await WAITI timeout\n");
+ /* no signal - only high clock selection allowed */
+ if (lp) {
+ mutex_unlock(&cdev->clk_mutex);
+ return 0;
+ }
+ }
+ }
+
+ ret = catpt_readl_poll_shim(cdev, CLKCTL,
+ reg, !(reg & CATPT_CLKCTL_CFCIP),
+ 500, 10000);
+ if (ret)
+ dev_warn(cdev->dev, "clock change still in progress\n");
+
+ /* default to DSP core & audio fabric high clock */
+ val |= CATPT_CS_DCS_HIGH;
+ mask = CATPT_CS_LPCS | CATPT_CS_DCS;
+ catpt_updatel_shim(cdev, CS1, mask, val);
+
+ ret = catpt_readl_poll_shim(cdev, CLKCTL,
+ reg, !(reg & CATPT_CLKCTL_CFCIP),
+ 500, 10000);
+ if (ret)
+ dev_warn(cdev->dev, "clock change still in progress\n");
+
+ /* update PLL accordingly */
+ cdev->spec->pll_shutdown(cdev, lp);
+
+ mutex_unlock(&cdev->clk_mutex);
+ return 0;
+}
+
+int catpt_dsp_update_lpclock(struct catpt_dev *cdev)
+{
+ struct catpt_stream_runtime *stream;
+
+ list_for_each_entry(stream, &cdev->stream_list, node)
+ if (stream->prepared)
+ return catpt_dsp_select_lpclock(cdev, false, true);
+
+ return catpt_dsp_select_lpclock(cdev, true, true);
+}
+
+/* bring registers to their defaults as HW won't reset itself */
+static void catpt_dsp_set_regs_defaults(struct catpt_dev *cdev)
+{
+ int i;
+
+ catpt_writel_shim(cdev, CS1, CATPT_CS_DEFAULT);
+ catpt_writel_shim(cdev, ISC, CATPT_ISC_DEFAULT);
+ catpt_writel_shim(cdev, ISD, CATPT_ISD_DEFAULT);
+ catpt_writel_shim(cdev, IMC, CATPT_IMC_DEFAULT);
+ catpt_writel_shim(cdev, IMD, CATPT_IMD_DEFAULT);
+ catpt_writel_shim(cdev, IPCC, CATPT_IPCC_DEFAULT);
+ catpt_writel_shim(cdev, IPCD, CATPT_IPCD_DEFAULT);
+ catpt_writel_shim(cdev, CLKCTL, CATPT_CLKCTL_DEFAULT);
+ catpt_writel_shim(cdev, CS2, CATPT_CS2_DEFAULT);
+ catpt_writel_shim(cdev, LTRC, CATPT_LTRC_DEFAULT);
+ catpt_writel_shim(cdev, HMDC, CATPT_HMDC_DEFAULT);
+
+ for (i = 0; i < CATPT_SSP_COUNT; i++) {
+ catpt_writel_ssp(cdev, i, SSCR0, CATPT_SSC0_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSCR1, CATPT_SSC1_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSSR, CATPT_SSS_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSITR, CATPT_SSIT_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSDR, CATPT_SSD_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSTO, CATPT_SSTO_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSPSP, CATPT_SSPSP_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSTSA, CATPT_SSTSA_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSRSA, CATPT_SSRSA_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSTSS, CATPT_SSTSS_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSCR2, CATPT_SSCR2_DEFAULT);
+ catpt_writel_ssp(cdev, i, SSPSP2, CATPT_SSPSP2_DEFAULT);
+ }
+}
+
+int catpt_dsp_power_down(struct catpt_dev *cdev)
+{
+ u32 mask, val;
+
+ /* disable core clock gating */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0);
+
+ catpt_dsp_reset(cdev, true);
+ /* set 24Mhz clock for both SSPs */
+ catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1),
+ CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1));
+ catpt_dsp_select_lpclock(cdev, true, false);
+ /* disable MCLK */
+ catpt_updatel_shim(cdev, CLKCTL, CATPT_CLKCTL_SMOS, 0);
+
+ catpt_dsp_set_regs_defaults(cdev);
+
+ /* switch clock gating */
+ mask = CATPT_VDRTCTL2_CGEALL & (~CATPT_VDRTCTL2_DCLCGE);
+ val = mask & (~CATPT_VDRTCTL2_DTCGE);
+ catpt_updatel_pci(cdev, VDRTCTL2, mask, val);
+ /* enable DTCGE separatelly */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DTCGE,
+ CATPT_VDRTCTL2_DTCGE);
+
+ /* SRAM power gating all */
+ catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask,
+ cdev->spec->dram_mask);
+ catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask,
+ cdev->spec->iram_mask);
+ mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit;
+ catpt_updatel_pci(cdev, VDRTCTL0, mask, cdev->spec->d3pgd_bit);
+
+ catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot);
+ /* give hw time to drop off */
+ udelay(50);
+
+ /* enable core clock gating */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE,
+ CATPT_VDRTCTL2_DCLCGE);
+ udelay(50);
+
+ return 0;
+}
+
+int catpt_dsp_power_up(struct catpt_dev *cdev)
+{
+ u32 mask, val;
+
+ /* disable core clock gating */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0);
+
+ /* switch clock gating */
+ mask = CATPT_VDRTCTL2_CGEALL & (~CATPT_VDRTCTL2_DCLCGE);
+ val = mask & (~CATPT_VDRTCTL2_DTCGE);
+ catpt_updatel_pci(cdev, VDRTCTL2, mask, val);
+
+ catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0);
+
+ /* SRAM power gating none */
+ mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit;
+ catpt_updatel_pci(cdev, VDRTCTL0, mask, mask);
+ catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask, 0);
+ catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask, 0);
+
+ catpt_dsp_set_regs_defaults(cdev);
+
+ /* restore MCLK */
+ catpt_updatel_shim(cdev, CLKCTL, CATPT_CLKCTL_SMOS, CATPT_CLKCTL_SMOS);
+ catpt_dsp_select_lpclock(cdev, false, false);
+ /* set 24Mhz clock for both SSPs */
+ catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1),
+ CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1));
+ catpt_dsp_reset(cdev, false);
+
+ /* enable core clock gating */
+ catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE,
+ CATPT_VDRTCTL2_DCLCGE);
+
+ /* generate int deassert msg to fix inversed int logic */
+ catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB | CATPT_IMC_IPCCD, 0);
+
+ return 0;
+}
+
+#define CATPT_DUMP_MAGIC 0xcd42
+#define CATPT_DUMP_SECTION_ID_FILE 0x00
+#define CATPT_DUMP_SECTION_ID_IRAM 0x01
+#define CATPT_DUMP_SECTION_ID_DRAM 0x02
+#define CATPT_DUMP_SECTION_ID_REGS 0x03
+#define CATPT_DUMP_HASH_SIZE 20
+
+struct catpt_dump_section_hdr {
+ u16 magic;
+ u8 core_id;
+ u8 section_id;
+ u32 size;
+};
+
+int catpt_coredump(struct catpt_dev *cdev)
+{
+ struct catpt_dump_section_hdr *hdr;
+ size_t dump_size, regs_size;
+ u8 *dump, *pos;
+ const char *eof;
+ char *info;
+ int i;
+
+ regs_size = CATPT_SHIM_REGS_SIZE;
+ regs_size += CATPT_DMA_COUNT * CATPT_DMA_REGS_SIZE;
+ regs_size += CATPT_SSP_COUNT * CATPT_SSP_REGS_SIZE;
+ dump_size = resource_size(&cdev->dram);
+ dump_size += resource_size(&cdev->iram);
+ dump_size += regs_size;
+ /* account for header of each section and hash chunk */
+ dump_size += 4 * sizeof(*hdr) + CATPT_DUMP_HASH_SIZE;
+
+ dump = vzalloc(dump_size);
+ if (!dump)
+ return -ENOMEM;
+
+ pos = dump;
+
+ hdr = (struct catpt_dump_section_hdr *)pos;
+ hdr->magic = CATPT_DUMP_MAGIC;
+ hdr->core_id = cdev->spec->core_id;
+ hdr->section_id = CATPT_DUMP_SECTION_ID_FILE;
+ hdr->size = dump_size - sizeof(*hdr);
+ pos += sizeof(*hdr);
+
+ info = cdev->ipc.config.fw_info;
+ eof = info + FW_INFO_SIZE_MAX;
+ /* navigate to fifth info segment (fw hash) */
+ for (i = 0; i < 4 && info < eof; i++, info++) {
+ /* info segments are separated by space each */
+ info = strnchr(info, eof - info, ' ');
+ if (!info)
+ break;
+ }
+
+ if (i == 4 && info)
+ memcpy(pos, info, min_t(u32, eof - info, CATPT_DUMP_HASH_SIZE));
+ pos += CATPT_DUMP_HASH_SIZE;
+
+ hdr = (struct catpt_dump_section_hdr *)pos;
+ hdr->magic = CATPT_DUMP_MAGIC;
+ hdr->core_id = cdev->spec->core_id;
+ hdr->section_id = CATPT_DUMP_SECTION_ID_IRAM;
+ hdr->size = resource_size(&cdev->iram);
+ pos += sizeof(*hdr);
+
+ memcpy_fromio(pos, cdev->lpe_ba + cdev->iram.start, hdr->size);
+ pos += hdr->size;
+
+ hdr = (struct catpt_dump_section_hdr *)pos;
+ hdr->magic = CATPT_DUMP_MAGIC;
+ hdr->core_id = cdev->spec->core_id;
+ hdr->section_id = CATPT_DUMP_SECTION_ID_DRAM;
+ hdr->size = resource_size(&cdev->dram);
+ pos += sizeof(*hdr);
+
+ memcpy_fromio(pos, cdev->lpe_ba + cdev->dram.start, hdr->size);
+ pos += hdr->size;
+
+ hdr = (struct catpt_dump_section_hdr *)pos;
+ hdr->magic = CATPT_DUMP_MAGIC;
+ hdr->core_id = cdev->spec->core_id;
+ hdr->section_id = CATPT_DUMP_SECTION_ID_REGS;
+ hdr->size = regs_size;
+ pos += sizeof(*hdr);
+
+ memcpy_fromio(pos, catpt_shim_addr(cdev), CATPT_SHIM_REGS_SIZE);
+ pos += CATPT_SHIM_REGS_SIZE;
+
+ for (i = 0; i < CATPT_SSP_COUNT; i++) {
+ memcpy_fromio(pos, catpt_ssp_addr(cdev, i),
+ CATPT_SSP_REGS_SIZE);
+ pos += CATPT_SSP_REGS_SIZE;
+ }
+ for (i = 0; i < CATPT_DMA_COUNT; i++) {
+ memcpy_fromio(pos, catpt_dma_addr(cdev, i),
+ CATPT_DMA_REGS_SIZE);
+ pos += CATPT_DMA_REGS_SIZE;
+ }
+
+ dev_coredumpv(cdev->dev, dump, dump_size, GFP_KERNEL);
+
+ return 0;
+}
diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c
new file mode 100644
index 000000000000..d26863249097
--- /dev/null
+++ b/sound/soc/intel/catpt/ipc.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/irqreturn.h>
+#include "core.h"
+#include "messages.h"
+#include "registers.h"
+#include "trace.h"
+
+#define CATPT_IPC_TIMEOUT_MS 300
+
+void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev)
+{
+ ipc->dev = dev;
+ ipc->ready = false;
+ ipc->default_timeout = CATPT_IPC_TIMEOUT_MS;
+ init_completion(&ipc->done_completion);
+ init_completion(&ipc->busy_completion);
+ spin_lock_init(&ipc->lock);
+ mutex_init(&ipc->mutex);
+}
+
+static int catpt_ipc_arm(struct catpt_ipc *ipc, struct catpt_fw_ready *config)
+{
+ /*
+ * Both tx and rx are put into and received from outbox. Inbox is
+ * only used for notifications where payload size is known upfront,
+ * thus no separate buffer is allocated for it.
+ */
+ ipc->rx.data = devm_kzalloc(ipc->dev, config->outbox_size, GFP_KERNEL);
+ if (!ipc->rx.data)
+ return -ENOMEM;
+
+ memcpy(&ipc->config, config, sizeof(*config));
+ ipc->ready = true;
+
+ return 0;
+}
+
+static void catpt_ipc_msg_init(struct catpt_ipc *ipc,
+ struct catpt_ipc_msg *reply)
+{
+ lockdep_assert_held(&ipc->lock);
+
+ ipc->rx.header = 0;
+ ipc->rx.size = reply ? reply->size : 0;
+ reinit_completion(&ipc->done_completion);
+ reinit_completion(&ipc->busy_completion);
+}
+
+static void catpt_dsp_send_tx(struct catpt_dev *cdev,
+ const struct catpt_ipc_msg *tx)
+{
+ u32 header = tx->header | CATPT_IPCC_BUSY;
+
+ trace_catpt_ipc_request(header);
+ trace_catpt_ipc_payload(tx->data, tx->size);
+
+ memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size);
+ catpt_writel_shim(cdev, IPCC, header);
+}
+
+static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
+{
+ struct catpt_ipc *ipc = &cdev->ipc;
+ int ret;
+
+ ret = wait_for_completion_timeout(&ipc->done_completion,
+ msecs_to_jiffies(timeout));
+ if (!ret)
+ return -ETIMEDOUT;
+ if (ipc->rx.rsp.status != CATPT_REPLY_PENDING)
+ return 0;
+
+ /* wait for delayed reply */
+ ret = wait_for_completion_timeout(&ipc->busy_completion,
+ msecs_to_jiffies(timeout));
+ return ret ? 0 : -ETIMEDOUT;
+}
+
+static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
+ struct catpt_ipc_msg request,
+ struct catpt_ipc_msg *reply, int timeout)
+{
+ struct catpt_ipc *ipc = &cdev->ipc;
+ unsigned long flags;
+ int ret;
+
+ if (!ipc->ready)
+ return -EPERM;
+ if (request.size > ipc->config.outbox_size ||
+ (reply && reply->size > ipc->config.outbox_size))
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipc->lock, flags);
+ catpt_ipc_msg_init(ipc, reply);
+ catpt_dsp_send_tx(cdev, &request);
+ spin_unlock_irqrestore(&ipc->lock, flags);
+
+ ret = catpt_wait_msg_completion(cdev, timeout);
+ if (ret) {
+ dev_crit(cdev->dev, "communication severed: %d, rebooting dsp..\n",
+ ret);
+ ipc->ready = false;
+ /* TODO: attempt recovery */
+ return ret;
+ }
+
+ ret = ipc->rx.rsp.status;
+ if (reply) {
+ reply->header = ipc->rx.header;
+
+ if (!ret && reply->data)
+ memcpy(reply->data, ipc->rx.data, reply->size);
+ }
+
+ return ret;
+}
+
+int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
+ struct catpt_ipc_msg request,
+ struct catpt_ipc_msg *reply, int timeout)
+{
+ struct catpt_ipc *ipc = &cdev->ipc;
+ int ret;
+
+ mutex_lock(&ipc->mutex);
+ ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout);
+ mutex_unlock(&ipc->mutex);
+
+ return ret;
+}
+
+int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
+ struct catpt_ipc_msg *reply)
+{
+ return catpt_dsp_send_msg_timeout(cdev, request, reply,
+ cdev->ipc.default_timeout);
+}
+
+static void
+catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg)
+{
+ struct catpt_stream_runtime *stream;
+ struct catpt_notify_position pos;
+ struct catpt_notify_glitch glitch;
+
+ stream = catpt_stream_find(cdev, msg.stream_hw_id);
+ if (!stream) {
+ dev_warn(cdev->dev, "notify %d for non-existent stream %d\n",
+ msg.notify_reason, msg.stream_hw_id);
+ return;
+ }
+
+ switch (msg.notify_reason) {
+ case CATPT_NOTIFY_POSITION_CHANGED:
+ memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos));
+ trace_catpt_ipc_payload((u8 *)&pos, sizeof(pos));
+
+ catpt_stream_update_position(cdev, stream, &pos);
+ break;
+
+ case CATPT_NOTIFY_GLITCH_OCCURRED:
+ memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch));
+ trace_catpt_ipc_payload((u8 *)&glitch, sizeof(glitch));
+
+ dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n",
+ glitch.type, glitch.presentation_pos,
+ glitch.write_pos);
+ break;
+
+ default:
+ dev_warn(cdev->dev, "unknown notification: %d received\n",
+ msg.notify_reason);
+ break;
+ }
+}
+
+static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header)
+{
+ struct catpt_ipc *ipc = &cdev->ipc;
+
+ ipc->rx.header = header;
+ if (ipc->rx.rsp.status != CATPT_REPLY_SUCCESS)
+ return;
+
+ memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size);
+ trace_catpt_ipc_payload(ipc->rx.data, ipc->rx.size);
+}
+
+static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header)
+{
+ union catpt_notify_msg msg = CATPT_MSG(header);
+ struct catpt_ipc *ipc = &cdev->ipc;
+
+ if (msg.fw_ready) {
+ struct catpt_fw_ready config;
+ /* to fit 32b header original address is shifted right by 3 */
+ u32 off = msg.mailbox_address << 3;
+
+ memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config));
+ trace_catpt_ipc_payload((u8 *)&config, sizeof(config));
+
+ catpt_ipc_arm(ipc, &config);
+ complete(&cdev->fw_ready);
+ return;
+ }
+
+ switch (msg.global_msg_type) {
+ case CATPT_GLB_REQUEST_CORE_DUMP:
+ dev_err(cdev->dev, "ADSP device coredump received\n");
+ ipc->ready = false;
+ catpt_coredump(cdev);
+ /* TODO: attempt recovery */
+ break;
+
+ case CATPT_GLB_STREAM_MESSAGE:
+ switch (msg.stream_msg_type) {
+ case CATPT_STRM_NOTIFICATION:
+ catpt_dsp_notify_stream(cdev, msg);
+ break;
+ default:
+ catpt_dsp_copy_rx(cdev, header);
+ /* signal completion of delayed reply */
+ complete(&ipc->busy_completion);
+ break;
+ }
+ break;
+
+ default:
+ dev_warn(cdev->dev, "unknown response: %d received\n",
+ msg.global_msg_type);
+ break;
+ }
+}
+
+irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id)
+{
+ struct catpt_dev *cdev = dev_id;
+ u32 ipcd;
+
+ ipcd = catpt_readl_shim(cdev, IPCD);
+ trace_catpt_ipc_notify(ipcd);
+
+ /* ensure there is delayed reply or notification to process */
+ if (!(ipcd & CATPT_IPCD_BUSY))
+ return IRQ_NONE;
+
+ catpt_dsp_process_response(cdev, ipcd);
+
+ /* tell DSP processing is completed */
+ catpt_updatel_shim(cdev, IPCD, CATPT_IPCD_BUSY | CATPT_IPCD_DONE,
+ CATPT_IPCD_DONE);
+ /* unmask dsp BUSY interrupt */
+ catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, 0);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id)
+{
+ struct catpt_dev *cdev = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ u32 isc, ipcc;
+
+ isc = catpt_readl_shim(cdev, ISC);
+ trace_catpt_irq(isc);
+
+ /* immediate reply */
+ if (isc & CATPT_ISC_IPCCD) {
+ /* mask host DONE interrupt */
+ catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD);
+
+ ipcc = catpt_readl_shim(cdev, IPCC);
+ trace_catpt_ipc_reply(ipcc);
+ catpt_dsp_copy_rx(cdev, ipcc);
+ complete(&cdev->ipc.done_completion);
+
+ /* tell DSP processing is completed */
+ catpt_updatel_shim(cdev, IPCC, CATPT_IPCC_DONE, 0);
+ /* unmask host DONE interrupt */
+ catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, 0);
+ ret = IRQ_HANDLED;
+ }
+
+ /* delayed reply or notification */
+ if (isc & CATPT_ISC_IPCDB) {
+ /* mask dsp BUSY interrupt */
+ catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, CATPT_IMC_IPCDB);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c
new file mode 100644
index 000000000000..f5705cd2c1e1
--- /dev/null
+++ b/sound/soc/intel/catpt/loader.c
@@ -0,0 +1,671 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include "core.h"
+#include "registers.h"
+
+/* FW load (200ms) plus operational delays */
+#define FW_READY_TIMEOUT_MS 250
+
+#define FW_SIGNATURE "$SST"
+#define FW_SIGNATURE_SIZE 4
+
+struct catpt_fw_hdr {
+ char signature[FW_SIGNATURE_SIZE];
+ u32 file_size;
+ u32 modules;
+ u32 file_format;
+ u32 reserved[4];
+} __packed;
+
+struct catpt_fw_mod_hdr {
+ char signature[FW_SIGNATURE_SIZE];
+ u32 mod_size;
+ u32 blocks;
+ u16 slot;
+ u16 module_id;
+ u32 entry_point;
+ u32 persistent_size;
+ u32 scratch_size;
+} __packed;
+
+enum catpt_ram_type {
+ CATPT_RAM_TYPE_IRAM = 1,
+ CATPT_RAM_TYPE_DRAM = 2,
+ /* DRAM with module's initial state */
+ CATPT_RAM_TYPE_INSTANCE = 3,
+};
+
+struct catpt_fw_block_hdr {
+ u32 ram_type;
+ u32 size;
+ u32 ram_offset;
+ u32 rsvd;
+} __packed;
+
+void catpt_sram_init(struct resource *sram, u32 start, u32 size)
+{
+ sram->start = start;
+ sram->end = start + size - 1;
+}
+
+void catpt_sram_free(struct resource *sram)
+{
+ struct resource *res, *save;
+
+ for (res = sram->child; res;) {
+ save = res->sibling;
+ release_resource(res);
+ kfree(res);
+ res = save;
+ }
+}
+
+struct resource *
+catpt_request_region(struct resource *root, resource_size_t size)
+{
+ struct resource *res = root->child;
+ resource_size_t addr = root->start;
+
+ for (;;) {
+ if (res->start - addr >= size)
+ break;
+ addr = res->end + 1;
+ res = res->sibling;
+ if (!res)
+ return NULL;
+ }
+
+ return __request_region(root, addr, size, NULL, 0);
+}
+
+int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
+{
+ struct catpt_stream_runtime *stream;
+
+ list_for_each_entry(stream, &cdev->stream_list, node) {
+ u32 off, size;
+ int ret;
+
+ off = stream->persistent->start;
+ size = resource_size(stream->persistent);
+ dev_dbg(cdev->dev, "storing stream %d ctx: off 0x%08x size %d\n",
+ stream->info.stream_hw_id, off, size);
+
+ ret = catpt_dma_memcpy_fromdsp(cdev, chan,
+ cdev->dxbuf_paddr + off,
+ cdev->lpe_base + off,
+ ALIGN(size, 4));
+ if (ret) {
+ dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cdev->modules); i++) {
+ struct catpt_module_type *type;
+ u32 off;
+ int ret;
+
+ type = &cdev->modules[i];
+ if (!type->loaded || !type->state_size)
+ continue;
+
+ off = type->state_offset;
+ dev_dbg(cdev->dev, "storing mod %d state: off 0x%08x size %d\n",
+ i, off, type->state_size);
+
+ ret = catpt_dma_memcpy_fromdsp(cdev, chan,
+ cdev->dxbuf_paddr + off,
+ cdev->lpe_base + off,
+ ALIGN(type->state_size, 4));
+ if (ret) {
+ dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
+{
+ int i;
+
+ for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
+ struct catpt_save_meminfo *info;
+ u32 off;
+ int ret;
+
+ info = &cdev->dx_ctx.meminfo[i];
+ if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
+ continue;
+
+ off = catpt_to_host_offset(info->offset);
+ if (off < cdev->dram.start || off > cdev->dram.end)
+ continue;
+
+ dev_dbg(cdev->dev, "storing memdump: off 0x%08x size %d\n",
+ off, info->size);
+
+ ret = catpt_dma_memcpy_fromdsp(cdev, chan,
+ cdev->dxbuf_paddr + off,
+ cdev->lpe_base + off,
+ ALIGN(info->size, 4));
+ if (ret) {
+ dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int
+catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
+{
+ struct catpt_stream_runtime *stream;
+
+ list_for_each_entry(stream, &cdev->stream_list, node) {
+ u32 off, size;
+ int ret;
+
+ off = stream->persistent->start;
+ size = resource_size(stream->persistent);
+ dev_dbg(cdev->dev, "restoring stream %d ctx: off 0x%08x size %d\n",
+ stream->info.stream_hw_id, off, size);
+
+ ret = catpt_dma_memcpy_todsp(cdev, chan,
+ cdev->lpe_base + off,
+ cdev->dxbuf_paddr + off,
+ ALIGN(size, 4));
+ if (ret) {
+ dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
+{
+ int i;
+
+ for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
+ struct catpt_save_meminfo *info;
+ struct resource r = {};
+ u32 off;
+ int ret;
+
+ info = &cdev->dx_ctx.meminfo[i];
+ if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
+ continue;
+
+ off = catpt_to_host_offset(info->offset);
+ resource_set_range(&r, off, info->size);
+ if (!resource_contains(&cdev->dram, &r))
+ continue;
+
+ dev_dbg(cdev->dev, "restoring memdump: off 0x%08x size %d\n",
+ off, info->size);
+
+ ret = catpt_dma_memcpy_todsp(cdev, chan,
+ cdev->lpe_base + off,
+ cdev->dxbuf_paddr + off,
+ ALIGN(info->size, 4));
+ if (ret) {
+ dev_err(cdev->dev, "restore block failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int catpt_restore_fwimage(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_block_hdr *blk)
+{
+ struct resource r1 = {};
+ int i;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ blk, sizeof(*blk), false);
+
+ resource_set_range(&r1, cdev->dram.start + blk->ram_offset, blk->size);
+ /* advance to data area */
+ paddr += sizeof(*blk);
+
+ for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
+ struct catpt_save_meminfo *info;
+ struct resource common = {};
+ struct resource r2 = {};
+ u32 off;
+ int ret;
+
+ info = &cdev->dx_ctx.meminfo[i];
+ if (info->source != CATPT_DX_TYPE_FW_IMAGE)
+ continue;
+
+ off = catpt_to_host_offset(info->offset);
+ resource_set_range(&r2, off, info->size);
+ if (!resource_contains(&cdev->dram, &r2))
+ continue;
+
+ if (!resource_intersection(&r2, &r1, &common))
+ continue;
+ /* calculate start offset of common data area */
+ off = common.start - r1.start;
+
+ dev_dbg(cdev->dev, "restoring fwimage: %pr\n", &common);
+
+ ret = catpt_dma_memcpy_todsp(cdev, chan, common.start,
+ paddr + off,
+ resource_size(&common));
+ if (ret) {
+ dev_err(cdev->dev, "memcpy todsp failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int catpt_load_block(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_block_hdr *blk, bool alloc)
+{
+ struct resource *sram, *res;
+ dma_addr_t dst_addr;
+ int ret;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ blk, sizeof(*blk), false);
+
+ switch (blk->ram_type) {
+ case CATPT_RAM_TYPE_IRAM:
+ sram = &cdev->iram;
+ break;
+ default:
+ sram = &cdev->dram;
+ break;
+ }
+
+ dst_addr = sram->start + blk->ram_offset;
+ if (alloc) {
+ res = __request_region(sram, dst_addr, blk->size, NULL, 0);
+ if (!res)
+ return -EBUSY;
+ }
+
+ /* advance to data area */
+ paddr += sizeof(*blk);
+
+ ret = catpt_dma_memcpy_todsp(cdev, chan, dst_addr, paddr, blk->size);
+ if (ret) {
+ dev_err(cdev->dev, "memcpy error: %d\n", ret);
+ __release_region(sram, dst_addr, blk->size);
+ }
+
+ return ret;
+}
+
+static int catpt_restore_basefw(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_mod_hdr *basefw)
+{
+ u32 offset = sizeof(*basefw);
+ int ret, i;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ basefw, sizeof(*basefw), false);
+
+ /* restore basefw image */
+ for (i = 0; i < basefw->blocks; i++) {
+ struct catpt_fw_block_hdr *blk;
+
+ blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset);
+
+ switch (blk->ram_type) {
+ case CATPT_RAM_TYPE_IRAM:
+ ret = catpt_load_block(cdev, chan, paddr + offset,
+ blk, false);
+ break;
+ default:
+ ret = catpt_restore_fwimage(cdev, chan, paddr + offset,
+ blk);
+ break;
+ }
+
+ if (ret) {
+ dev_err(cdev->dev, "restore block failed: %d\n", ret);
+ return ret;
+ }
+
+ offset += sizeof(*blk) + blk->size;
+ }
+
+ /* then proceed with memory dumps */
+ ret = catpt_restore_memdumps(cdev, chan);
+ if (ret)
+ dev_err(cdev->dev, "restore memdumps failed: %d\n", ret);
+
+ return ret;
+}
+
+static int catpt_restore_module(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_mod_hdr *mod)
+{
+ u32 offset = sizeof(*mod);
+ int i;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ mod, sizeof(*mod), false);
+
+ for (i = 0; i < mod->blocks; i++) {
+ struct catpt_fw_block_hdr *blk;
+ int ret;
+
+ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
+
+ switch (blk->ram_type) {
+ case CATPT_RAM_TYPE_INSTANCE:
+ /* restore module state */
+ ret = catpt_dma_memcpy_todsp(cdev, chan,
+ cdev->lpe_base + blk->ram_offset,
+ cdev->dxbuf_paddr + blk->ram_offset,
+ ALIGN(blk->size, 4));
+ break;
+ default:
+ ret = catpt_load_block(cdev, chan, paddr + offset,
+ blk, false);
+ break;
+ }
+
+ if (ret) {
+ dev_err(cdev->dev, "restore block failed: %d\n", ret);
+ return ret;
+ }
+
+ offset += sizeof(*blk) + blk->size;
+ }
+
+ return 0;
+}
+
+static int catpt_load_module(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_mod_hdr *mod)
+{
+ struct catpt_module_type *type;
+ u32 offset = sizeof(*mod);
+ int i;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ mod, sizeof(*mod), false);
+
+ type = &cdev->modules[mod->module_id];
+
+ for (i = 0; i < mod->blocks; i++) {
+ struct catpt_fw_block_hdr *blk;
+ int ret;
+
+ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
+
+ ret = catpt_load_block(cdev, chan, paddr + offset, blk, true);
+ if (ret) {
+ dev_err(cdev->dev, "load block failed: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Save state window coordinates - these will be
+ * used to capture module state on D0 exit.
+ */
+ if (blk->ram_type == CATPT_RAM_TYPE_INSTANCE) {
+ type->state_offset = blk->ram_offset;
+ type->state_size = blk->size;
+ }
+
+ offset += sizeof(*blk) + blk->size;
+ }
+
+ /* init module type static info */
+ type->loaded = true;
+ /* DSP expects address from module header substracted by 4 */
+ type->entry_point = mod->entry_point - 4;
+ type->persistent_size = mod->persistent_size;
+ type->scratch_size = mod->scratch_size;
+
+ return 0;
+}
+
+static int catpt_restore_firmware(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_hdr *fw)
+{
+ u32 offset = sizeof(*fw);
+ int i;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ fw, sizeof(*fw), false);
+
+ for (i = 0; i < fw->modules; i++) {
+ struct catpt_fw_mod_hdr *mod;
+ int ret;
+
+ mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
+ if (strncmp(fw->signature, mod->signature,
+ FW_SIGNATURE_SIZE)) {
+ dev_err(cdev->dev, "module signature mismatch\n");
+ return -EINVAL;
+ }
+
+ if (mod->module_id > CATPT_MODID_LAST)
+ return -EINVAL;
+
+ switch (mod->module_id) {
+ case CATPT_MODID_BASE_FW:
+ ret = catpt_restore_basefw(cdev, chan, paddr + offset,
+ mod);
+ break;
+ default:
+ ret = catpt_restore_module(cdev, chan, paddr + offset,
+ mod);
+ break;
+ }
+
+ if (ret) {
+ dev_err(cdev->dev, "restore module failed: %d\n", ret);
+ return ret;
+ }
+
+ offset += sizeof(*mod) + mod->mod_size;
+ }
+
+ return 0;
+}
+
+static int catpt_load_firmware(struct catpt_dev *cdev,
+ struct dma_chan *chan, dma_addr_t paddr,
+ struct catpt_fw_hdr *fw)
+{
+ u32 offset = sizeof(*fw);
+ int i;
+
+ print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
+ fw, sizeof(*fw), false);
+
+ for (i = 0; i < fw->modules; i++) {
+ struct catpt_fw_mod_hdr *mod;
+ int ret;
+
+ mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
+ if (strncmp(fw->signature, mod->signature,
+ FW_SIGNATURE_SIZE)) {
+ dev_err(cdev->dev, "module signature mismatch\n");
+ return -EINVAL;
+ }
+
+ if (mod->module_id > CATPT_MODID_LAST)
+ return -EINVAL;
+
+ ret = catpt_load_module(cdev, chan, paddr + offset, mod);
+ if (ret) {
+ dev_err(cdev->dev, "load module failed: %d\n", ret);
+ return ret;
+ }
+
+ offset += sizeof(*mod) + mod->mod_size;
+ }
+
+ return 0;
+}
+
+static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan,
+ const char *name, const char *signature,
+ bool restore)
+{
+ struct catpt_fw_hdr *fw;
+ struct firmware *img;
+ dma_addr_t paddr;
+ void *vaddr;
+ int ret;
+
+ ret = request_firmware((const struct firmware **)&img, name, cdev->dev);
+ if (ret)
+ return ret;
+
+ fw = (struct catpt_fw_hdr *)img->data;
+ if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) {
+ dev_err(cdev->dev, "firmware signature mismatch\n");
+ ret = -EINVAL;
+ goto release_fw;
+ }
+
+ vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL);
+ if (!vaddr) {
+ ret = -ENOMEM;
+ goto release_fw;
+ }
+
+ memcpy(vaddr, img->data, img->size);
+ fw = (struct catpt_fw_hdr *)vaddr;
+ if (restore)
+ ret = catpt_restore_firmware(cdev, chan, paddr, fw);
+ else
+ ret = catpt_load_firmware(cdev, chan, paddr, fw);
+
+ dma_free_coherent(cdev->dev, img->size, vaddr, paddr);
+release_fw:
+ release_firmware(img);
+ return ret;
+}
+
+static int catpt_load_images(struct catpt_dev *cdev, bool restore)
+{
+ static const char *const names[] = {
+ "intel/IntcSST1.bin",
+ "intel/IntcSST2.bin",
+ };
+ struct dma_chan *chan;
+ int ret;
+
+ chan = catpt_dma_request_config_chan(cdev);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1],
+ FW_SIGNATURE, restore);
+ if (ret)
+ goto release_dma_chan;
+
+ if (!restore)
+ goto release_dma_chan;
+ ret = catpt_restore_streams_context(cdev, chan);
+ if (ret)
+ dev_err(cdev->dev, "restore streams ctx failed: %d\n", ret);
+release_dma_chan:
+ dma_release_channel(chan);
+ return ret;
+}
+
+int catpt_boot_firmware(struct catpt_dev *cdev, bool restore)
+{
+ int ret;
+
+ catpt_dsp_stall(cdev, true);
+
+ ret = catpt_load_images(cdev, restore);
+ if (ret) {
+ dev_err(cdev->dev, "load binaries failed: %d\n", ret);
+ return ret;
+ }
+
+ reinit_completion(&cdev->fw_ready);
+ catpt_dsp_stall(cdev, false);
+
+ ret = wait_for_completion_timeout(&cdev->fw_ready,
+ msecs_to_jiffies(FW_READY_TIMEOUT_MS));
+ if (!ret) {
+ dev_err(cdev->dev, "firmware ready timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* update sram pg & clock once done booting */
+ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
+ catpt_dsp_update_srampge(cdev, &cdev->iram, cdev->spec->iram_mask);
+
+ return catpt_dsp_update_lpclock(cdev);
+}
+
+int catpt_first_boot_firmware(struct catpt_dev *cdev)
+{
+ struct resource *res;
+ int ret;
+
+ ret = catpt_boot_firmware(cdev, false);
+ if (ret) {
+ dev_err(cdev->dev, "basefw boot failed: %d\n", ret);
+ return ret;
+ }
+
+ /* restrict FW Core dump area */
+ __request_region(&cdev->dram, 0, 0x200, NULL, 0);
+ /* restrict entire area following BASE_FW - highest offset in DRAM */
+ for (res = cdev->dram.child; res->sibling; res = res->sibling)
+ ;
+ __request_region(&cdev->dram, res->end + 1,
+ cdev->dram.end - res->end, NULL, 0);
+
+ ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ ret = catpt_arm_stream_templates(cdev);
+ if (ret) {
+ dev_err(cdev->dev, "arm templates failed: %d\n", ret);
+ return ret;
+ }
+
+ /* update dram pg for scratch and restricted regions */
+ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
+
+ return 0;
+}
diff --git a/sound/soc/intel/catpt/messages.c b/sound/soc/intel/catpt/messages.c
new file mode 100644
index 000000000000..30eec2de4dc1
--- /dev/null
+++ b/sound/soc/intel/catpt/messages.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/slab.h>
+#include "core.h"
+#include "messages.h"
+#include "registers.h"
+
+int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
+ struct catpt_fw_version *version)
+{
+ union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_FW_VERSION);
+ struct catpt_ipc_msg request = {{0}}, reply;
+ int ret;
+
+ request.header = msg.val;
+ reply.size = sizeof(*version);
+ reply.data = version;
+
+ ret = catpt_dsp_send_msg(cdev, request, &reply);
+ if (ret)
+ dev_err(cdev->dev, "get fw version failed: %d\n", ret);
+
+ return ret;
+}
+
+struct catpt_alloc_stream_input {
+ enum catpt_path_id path_id:8;
+ enum catpt_stream_type stream_type:8;
+ enum catpt_format_id format_id:8;
+ u8 reserved;
+ struct catpt_audio_format input_format;
+ struct catpt_ring_info ring_info;
+ u8 num_entries;
+ /* flex array with entries here */
+ struct catpt_memory_info persistent_mem;
+ struct catpt_memory_info scratch_mem;
+ u32 num_notifications; /* obsolete */
+} __packed;
+
+int catpt_ipc_alloc_stream(struct catpt_dev *cdev,
+ enum catpt_path_id path_id,
+ enum catpt_stream_type type,
+ struct catpt_audio_format *afmt,
+ struct catpt_ring_info *rinfo,
+ u8 num_modules,
+ struct catpt_module_entry *modules,
+ struct resource *persistent,
+ struct resource *scratch,
+ struct catpt_stream_info *sinfo)
+{
+ union catpt_global_msg msg = CATPT_GLOBAL_MSG(ALLOCATE_STREAM);
+ struct catpt_alloc_stream_input input;
+ struct catpt_ipc_msg request, reply;
+ size_t size, arrsz;
+ u8 *payload;
+ off_t off;
+ int ret;
+
+ off = offsetof(struct catpt_alloc_stream_input, persistent_mem);
+ arrsz = sizeof(*modules) * num_modules;
+ size = sizeof(input) + arrsz;
+
+ payload = kzalloc(size, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ memset(&input, 0, sizeof(input));
+ input.path_id = path_id;
+ input.stream_type = type;
+ input.format_id = CATPT_FORMAT_PCM;
+ input.input_format = *afmt;
+ input.ring_info = *rinfo;
+ input.num_entries = num_modules;
+ input.persistent_mem.offset = catpt_to_dsp_offset(persistent->start);
+ input.persistent_mem.size = resource_size(persistent);
+ if (scratch) {
+ input.scratch_mem.offset = catpt_to_dsp_offset(scratch->start);
+ input.scratch_mem.size = resource_size(scratch);
+ }
+
+ /* re-arrange the input: account for flex array 'entries' */
+ memcpy(payload, &input, sizeof(input));
+ memmove(payload + off + arrsz, payload + off, sizeof(input) - off);
+ memcpy(payload + off, modules, arrsz);
+
+ request.header = msg.val;
+ request.size = size;
+ request.data = payload;
+ reply.size = sizeof(*sinfo);
+ reply.data = sinfo;
+
+ ret = catpt_dsp_send_msg(cdev, request, &reply);
+ if (ret)
+ dev_err(cdev->dev, "alloc stream type %d failed: %d\n",
+ type, ret);
+
+ kfree(payload);
+ return ret;
+}
+
+int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id)
+{
+ union catpt_global_msg msg = CATPT_GLOBAL_MSG(FREE_STREAM);
+ struct catpt_ipc_msg request;
+ int ret;
+
+ request.header = msg.val;
+ request.size = sizeof(stream_hw_id);
+ request.data = &stream_hw_id;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "free stream %d failed: %d\n",
+ stream_hw_id, ret);
+
+ return ret;
+}
+
+int catpt_ipc_set_device_format(struct catpt_dev *cdev,
+ struct catpt_ssp_device_format *devfmt)
+{
+ union catpt_global_msg msg = CATPT_GLOBAL_MSG(SET_DEVICE_FORMATS);
+ struct catpt_ipc_msg request;
+ int ret;
+
+ request.header = msg.val;
+ request.size = sizeof(*devfmt);
+ request.data = devfmt;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "set device format failed: %d\n", ret);
+
+ return ret;
+}
+
+int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
+ struct catpt_dx_context *context)
+{
+ union catpt_global_msg msg = CATPT_GLOBAL_MSG(ENTER_DX_STATE);
+ struct catpt_ipc_msg request, reply;
+ int ret;
+
+ request.header = msg.val;
+ request.size = sizeof(state);
+ request.data = &state;
+ reply.size = sizeof(*context);
+ reply.data = context;
+
+ ret = catpt_dsp_send_msg(cdev, request, &reply);
+ if (ret)
+ dev_err(cdev->dev, "enter dx state failed: %d\n", ret);
+
+ return ret;
+}
+
+int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
+ struct catpt_mixer_stream_info *info)
+{
+ union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_MIXER_STREAM_INFO);
+ struct catpt_ipc_msg request = {{0}}, reply;
+ int ret;
+
+ request.header = msg.val;
+ reply.size = sizeof(*info);
+ reply.data = info;
+
+ ret = catpt_dsp_send_msg(cdev, request, &reply);
+ if (ret)
+ dev_err(cdev->dev, "get mixer info failed: %d\n", ret);
+
+ return ret;
+}
+
+int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id)
+{
+ union catpt_stream_msg msg = CATPT_STREAM_MSG(RESET_STREAM);
+ struct catpt_ipc_msg request = {{0}};
+ int ret;
+
+ msg.stream_hw_id = stream_hw_id;
+ request.header = msg.val;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "reset stream %d failed: %d\n",
+ stream_hw_id, ret);
+
+ return ret;
+}
+
+int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id)
+{
+ union catpt_stream_msg msg = CATPT_STREAM_MSG(PAUSE_STREAM);
+ struct catpt_ipc_msg request = {{0}};
+ int ret;
+
+ msg.stream_hw_id = stream_hw_id;
+ request.header = msg.val;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "pause stream %d failed: %d\n",
+ stream_hw_id, ret);
+
+ return ret;
+}
+
+int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id)
+{
+ union catpt_stream_msg msg = CATPT_STREAM_MSG(RESUME_STREAM);
+ struct catpt_ipc_msg request = {{0}};
+ int ret;
+
+ msg.stream_hw_id = stream_hw_id;
+ request.header = msg.val;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "resume stream %d failed: %d\n",
+ stream_hw_id, ret);
+
+ return ret;
+}
+
+struct catpt_set_volume_input {
+ u32 channel;
+ u32 target_volume;
+ u64 curve_duration;
+ u32 curve_type;
+} __packed;
+
+int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
+ u32 channel, u32 volume,
+ u32 curve_duration,
+ enum catpt_audio_curve_type curve_type)
+{
+ union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_VOLUME);
+ struct catpt_ipc_msg request;
+ struct catpt_set_volume_input input;
+ int ret;
+
+ msg.stream_hw_id = stream_hw_id;
+ input.channel = channel;
+ input.target_volume = volume;
+ input.curve_duration = curve_duration;
+ input.curve_type = curve_type;
+
+ request.header = msg.val;
+ request.size = sizeof(input);
+ request.data = &input;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "set stream %d volume failed: %d\n",
+ stream_hw_id, ret);
+
+ return ret;
+}
+
+struct catpt_set_write_pos_input {
+ u32 new_write_pos;
+ bool end_of_buffer;
+ bool low_latency;
+} __packed;
+
+int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
+ u32 pos, bool eob, bool ll)
+{
+ union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_WRITE_POSITION);
+ struct catpt_ipc_msg request;
+ struct catpt_set_write_pos_input input;
+ int ret;
+
+ msg.stream_hw_id = stream_hw_id;
+ input.new_write_pos = pos;
+ input.end_of_buffer = eob;
+ input.low_latency = ll;
+
+ request.header = msg.val;
+ request.size = sizeof(input);
+ request.data = &input;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "set stream %d write pos failed: %d\n",
+ stream_hw_id, ret);
+
+ return ret;
+}
+
+int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute)
+{
+ union catpt_stream_msg msg = CATPT_STAGE_MSG(MUTE_LOOPBACK);
+ struct catpt_ipc_msg request;
+ int ret;
+
+ msg.stream_hw_id = stream_hw_id;
+ request.header = msg.val;
+ request.size = sizeof(mute);
+ request.data = &mute;
+
+ ret = catpt_dsp_send_msg(cdev, request, NULL);
+ if (ret)
+ dev_err(cdev->dev, "mute loopback failed: %d\n", ret);
+
+ return ret;
+}
diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h
new file mode 100644
index 000000000000..a634943eb669
--- /dev/null
+++ b/sound/soc/intel/catpt/messages.h
@@ -0,0 +1,399 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2020 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SND_SOC_INTEL_CATPT_MSG_H
+#define __SND_SOC_INTEL_CATPT_MSG_H
+
+struct catpt_dev;
+
+/* IPC messages base types */
+
+enum catpt_reply_status {
+ CATPT_REPLY_SUCCESS = 0,
+ CATPT_REPLY_ERROR_INVALID_PARAM = 1,
+ CATPT_REPLY_UNKNOWN_MESSAGE_TYPE = 2,
+ CATPT_REPLY_OUT_OF_RESOURCES = 3,
+ CATPT_REPLY_BUSY = 4,
+ CATPT_REPLY_PENDING = 5,
+ CATPT_REPLY_FAILURE = 6,
+ CATPT_REPLY_INVALID_REQUEST = 7,
+ CATPT_REPLY_UNINITIALIZED = 8,
+ CATPT_REPLY_NOT_FOUND = 9,
+ CATPT_REPLY_SOURCE_NOT_STARTED = 10,
+};
+
+/* GLOBAL messages */
+
+enum catpt_global_msg_type {
+ CATPT_GLB_GET_FW_VERSION = 0,
+ CATPT_GLB_ALLOCATE_STREAM = 3,
+ CATPT_GLB_FREE_STREAM = 4,
+ CATPT_GLB_STREAM_MESSAGE = 6,
+ CATPT_GLB_REQUEST_CORE_DUMP = 7,
+ CATPT_GLB_SET_DEVICE_FORMATS = 10,
+ CATPT_GLB_ENTER_DX_STATE = 12,
+ CATPT_GLB_GET_MIXER_STREAM_INFO = 13,
+};
+
+union catpt_global_msg {
+ u32 val;
+ struct {
+ u32 status:5;
+ u32 context:19; /* stream or module specific */
+ u32 global_msg_type:5;
+ u32 fw_ready:1;
+ u32 done:1;
+ u32 busy:1;
+ };
+} __packed;
+
+#define CATPT_MSG(hdr) { .val = hdr }
+#define CATPT_GLOBAL_MSG(msg_type) \
+ { .global_msg_type = CATPT_GLB_##msg_type }
+
+#define BUILD_HASH_SIZE 40
+
+struct catpt_fw_version {
+ u8 build;
+ u8 minor;
+ u8 major;
+ u8 type;
+ u8 build_hash[BUILD_HASH_SIZE];
+ u32 log_providers_hash;
+} __packed;
+
+int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
+ struct catpt_fw_version *version);
+
+enum catpt_pin_id {
+ CATPT_PIN_ID_SYSTEM = 0,
+ CATPT_PIN_ID_REFERENCE = 1,
+ CATPT_PIN_ID_CAPTURE1 = 2,
+ CATPT_PIN_ID_CAPTURE2 = 3,
+ CATPT_PIN_ID_OFFLOAD1 = 4,
+ CATPT_PIN_ID_OFFLOAD2 = 5,
+ CATPT_PIN_ID_MIXER = 7,
+ CATPT_PIN_ID_BLUETOOTH_CAPTURE = 8,
+ CATPT_PIN_ID_BLUETOOTH_RENDER = 9,
+};
+
+enum catpt_path_id {
+ CATPT_PATH_SSP0_OUT = 0,
+ CATPT_PATH_SSP0_IN = 1,
+ CATPT_PATH_SSP1_OUT = 2,
+ CATPT_PATH_SSP1_IN = 3,
+ /* duplicated audio in capture path */
+ CATPT_PATH_SSP0_IN_DUP = 4,
+};
+
+enum catpt_stream_type {
+ CATPT_STRM_TYPE_RENDER = 0, /* offload */
+ CATPT_STRM_TYPE_SYSTEM = 1,
+ CATPT_STRM_TYPE_CAPTURE = 2,
+ CATPT_STRM_TYPE_LOOPBACK = 3,
+ CATPT_STRM_TYPE_BLUETOOTH_RENDER = 4,
+ CATPT_STRM_TYPE_BLUETOOTH_CAPTURE = 5,
+};
+
+enum catpt_format_id {
+ CATPT_FORMAT_PCM = 0,
+ CATPT_FORMAT_MP3 = 1,
+ CATPT_FORMAT_AAC = 2,
+ CATPT_FORMAT_WMA = 3,
+};
+
+enum catpt_channel_index {
+ CATPT_CHANNEL_LEFT = 0x0,
+ CATPT_CHANNEL_CENTER = 0x1,
+ CATPT_CHANNEL_RIGHT = 0x2,
+ CATPT_CHANNEL_LEFT_SURROUND = 0x3,
+ CATPT_CHANNEL_CENTER_SURROUND = 0x3,
+ CATPT_CHANNEL_RIGHT_SURROUND = 0x4,
+ CATPT_CHANNEL_LFE = 0x7,
+ CATPT_CHANNEL_INVALID = 0xF,
+};
+
+enum catpt_channel_config {
+ CATPT_CHANNEL_CONFIG_MONO = 0, /* One channel only */
+ CATPT_CHANNEL_CONFIG_STEREO = 1, /* L & R */
+ CATPT_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only */
+ CATPT_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only */
+ CATPT_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only */
+ CATPT_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only */
+ CATPT_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only */
+ CATPT_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs */
+ CATPT_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE */
+ CATPT_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two */
+ CATPT_CHANNEL_CONFIG_INVALID = 10,
+};
+
+enum catpt_interleaving_style {
+ CATPT_INTERLEAVING_PER_CHANNEL = 0,
+ CATPT_INTERLEAVING_PER_SAMPLE = 1,
+};
+
+struct catpt_audio_format {
+ u32 sample_rate;
+ u32 bit_depth;
+ u32 channel_map;
+ u32 channel_config;
+ u32 interleaving;
+ u8 num_channels;
+ u8 valid_bit_depth;
+ u8 reserved[2];
+} __packed;
+
+struct catpt_ring_info {
+ u32 page_table_addr;
+ u32 num_pages;
+ u32 size;
+ u32 offset;
+ u32 ring_first_page_pfn;
+} __packed;
+
+#define CATPT_MODULE_COUNT (CATPT_MODID_LAST + 1)
+
+enum catpt_module_id {
+ CATPT_MODID_BASE_FW = 0x0,
+ CATPT_MODID_MP3 = 0x1,
+ CATPT_MODID_AAC_5_1 = 0x2,
+ CATPT_MODID_AAC_2_0 = 0x3,
+ CATPT_MODID_SRC = 0x4,
+ CATPT_MODID_WAVES = 0x5,
+ CATPT_MODID_DOLBY = 0x6,
+ CATPT_MODID_BOOST = 0x7,
+ CATPT_MODID_LPAL = 0x8,
+ CATPT_MODID_DTS = 0x9,
+ CATPT_MODID_PCM_CAPTURE = 0xA,
+ CATPT_MODID_PCM_SYSTEM = 0xB,
+ CATPT_MODID_PCM_REFERENCE = 0xC,
+ CATPT_MODID_PCM = 0xD, /* offload */
+ CATPT_MODID_BLUETOOTH_RENDER = 0xE,
+ CATPT_MODID_BLUETOOTH_CAPTURE = 0xF,
+ CATPT_MODID_LAST = CATPT_MODID_BLUETOOTH_CAPTURE,
+};
+
+struct catpt_module_entry {
+ u32 module_id;
+ u32 entry_point;
+} __packed;
+
+struct catpt_module_map {
+ u8 num_entries;
+ struct catpt_module_entry entries[];
+} __packed;
+
+struct catpt_memory_info {
+ u32 offset;
+ u32 size;
+} __packed;
+
+#define CATPT_CHANNELS_MAX 4
+#define CATPT_ALL_CHANNELS_MASK UINT_MAX
+
+struct catpt_stream_info {
+ u32 stream_hw_id;
+ u32 reserved;
+ u32 read_pos_regaddr;
+ u32 pres_pos_regaddr;
+ u32 peak_meter_regaddr[CATPT_CHANNELS_MAX];
+ u32 volume_regaddr[CATPT_CHANNELS_MAX];
+} __packed;
+
+int catpt_ipc_alloc_stream(struct catpt_dev *cdev,
+ enum catpt_path_id path_id,
+ enum catpt_stream_type type,
+ struct catpt_audio_format *afmt,
+ struct catpt_ring_info *rinfo,
+ u8 num_modules,
+ struct catpt_module_entry *modules,
+ struct resource *persistent,
+ struct resource *scratch,
+ struct catpt_stream_info *sinfo);
+int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id);
+
+enum catpt_ssp_iface {
+ CATPT_SSP_IFACE_0 = 0,
+ CATPT_SSP_IFACE_1 = 1,
+ CATPT_SSP_COUNT,
+};
+
+enum catpt_mclk_frequency {
+ CATPT_MCLK_OFF = 0,
+ CATPT_MCLK_FREQ_6_MHZ = 1,
+ CATPT_MCLK_FREQ_21_MHZ = 2,
+ CATPT_MCLK_FREQ_24_MHZ = 3,
+};
+
+enum catpt_ssp_mode {
+ CATPT_SSP_MODE_I2S_CONSUMER = 0,
+ CATPT_SSP_MODE_I2S_PROVIDER = 1,
+ CATPT_SSP_MODE_TDM_PROVIDER = 2,
+};
+
+struct catpt_ssp_device_format {
+ u32 iface;
+ u32 mclk;
+ u32 mode;
+ u16 clock_divider;
+ u8 channels;
+} __packed;
+
+int catpt_ipc_set_device_format(struct catpt_dev *cdev,
+ struct catpt_ssp_device_format *devfmt);
+
+enum catpt_dx_state {
+ CATPT_DX_STATE_D3 = 3,
+};
+
+enum catpt_dx_type {
+ CATPT_DX_TYPE_FW_IMAGE = 0,
+ CATPT_DX_TYPE_MEMORY_DUMP = 1,
+};
+
+struct catpt_save_meminfo {
+ u32 offset;
+ u32 size;
+ u32 source;
+} __packed;
+
+#define SAVE_MEMINFO_MAX 14
+
+struct catpt_dx_context {
+ u32 num_meminfo;
+ struct catpt_save_meminfo meminfo[SAVE_MEMINFO_MAX];
+} __packed;
+
+int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
+ struct catpt_dx_context *context);
+
+struct catpt_mixer_stream_info {
+ u32 mixer_hw_id;
+ u32 peak_meter_regaddr[CATPT_CHANNELS_MAX];
+ u32 volume_regaddr[CATPT_CHANNELS_MAX];
+} __packed;
+
+int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
+ struct catpt_mixer_stream_info *info);
+
+/* STREAM messages */
+
+enum catpt_stream_msg_type {
+ CATPT_STRM_RESET_STREAM = 0,
+ CATPT_STRM_PAUSE_STREAM = 1,
+ CATPT_STRM_RESUME_STREAM = 2,
+ CATPT_STRM_STAGE_MESSAGE = 3,
+ CATPT_STRM_NOTIFICATION = 4,
+};
+
+enum catpt_stage_action {
+ CATPT_STG_SET_VOLUME = 1,
+ CATPT_STG_SET_WRITE_POSITION = 2,
+ CATPT_STG_MUTE_LOOPBACK = 3,
+};
+
+union catpt_stream_msg {
+ u32 val;
+ struct {
+ u32 status:5;
+ u32 reserved:7;
+ u32 stage_action:4;
+ u32 stream_hw_id:4;
+ u32 stream_msg_type:4;
+ u32 global_msg_type:5;
+ u32 fw_ready:1;
+ u32 done:1;
+ u32 busy:1;
+ };
+} __packed;
+
+#define CATPT_STREAM_MSG(msg_type) \
+{ \
+ .stream_msg_type = CATPT_STRM_##msg_type, \
+ .global_msg_type = CATPT_GLB_STREAM_MESSAGE }
+#define CATPT_STAGE_MSG(msg_type) \
+{ \
+ .stage_action = CATPT_STG_##msg_type, \
+ .stream_msg_type = CATPT_STRM_STAGE_MESSAGE, \
+ .global_msg_type = CATPT_GLB_STREAM_MESSAGE }
+
+int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id);
+int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id);
+int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id);
+
+/* STREAM messages - STAGE subtype */
+
+enum catpt_audio_curve_type {
+ CATPT_AUDIO_CURVE_NONE = 0,
+ CATPT_AUDIO_CURVE_WINDOWS_FADE = 1,
+};
+
+int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
+ u32 channel, u32 volume,
+ u32 curve_duration,
+ enum catpt_audio_curve_type curve_type);
+
+int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
+ u32 pos, bool eob, bool ll);
+
+int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute);
+
+/* NOTIFICATION messages */
+
+enum catpt_notify_reason {
+ CATPT_NOTIFY_POSITION_CHANGED = 0,
+ CATPT_NOTIFY_GLITCH_OCCURRED = 1,
+};
+
+union catpt_notify_msg {
+ u32 val;
+ struct {
+ u32 mailbox_address:29;
+ u32 fw_ready:1;
+ u32 done:1;
+ u32 busy:1;
+ };
+ struct {
+ u32 status:5;
+ u32 reserved:7;
+ u32 notify_reason:4;
+ u32 stream_hw_id:4;
+ u32 stream_msg_type:4;
+ u32 global_msg_type:5;
+ u32 hdr:3; /* fw_ready, done, busy */
+ };
+} __packed;
+
+#define FW_INFO_SIZE_MAX 100
+
+struct catpt_fw_ready {
+ u32 inbox_offset;
+ u32 outbox_offset;
+ u32 inbox_size;
+ u32 outbox_size;
+ u32 fw_info_size;
+ char fw_info[FW_INFO_SIZE_MAX];
+} __packed;
+
+struct catpt_notify_position {
+ u32 stream_position;
+ u32 fw_cycle_count;
+} __packed;
+
+enum catpt_glitch_type {
+ CATPT_GLITCH_UNDERRUN = 1,
+ CATPT_GLITCH_DECODER_ERROR = 2,
+ CATPT_GLITCH_DOUBLED_WRITE_POS = 3,
+};
+
+struct catpt_notify_glitch {
+ u32 type;
+ u64 presentation_pos;
+ u32 write_pos;
+} __packed;
+
+#endif
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
new file mode 100644
index 000000000000..abd1cb07c60c
--- /dev/null
+++ b/sound/soc/intel/catpt/pcm.c
@@ -0,0 +1,1201 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <uapi/sound/tlv.h>
+#include "core.h"
+#include "messages.h"
+
+struct catpt_stream_template {
+ enum catpt_path_id path_id;
+ enum catpt_stream_type type;
+ u32 persistent_size;
+ u8 num_entries;
+ struct catpt_module_entry entries[];
+};
+
+static struct catpt_stream_template system_pb = {
+ .path_id = CATPT_PATH_SSP0_OUT,
+ .type = CATPT_STRM_TYPE_SYSTEM,
+ .num_entries = 1,
+ .entries = {{ CATPT_MODID_PCM_SYSTEM, 0 }},
+};
+
+static struct catpt_stream_template system_cp = {
+ .path_id = CATPT_PATH_SSP0_IN,
+ .type = CATPT_STRM_TYPE_CAPTURE,
+ .num_entries = 1,
+ .entries = {{ CATPT_MODID_PCM_CAPTURE, 0 }},
+};
+
+static struct catpt_stream_template offload_pb = {
+ .path_id = CATPT_PATH_SSP0_OUT,
+ .type = CATPT_STRM_TYPE_RENDER,
+ .num_entries = 1,
+ .entries = {{ CATPT_MODID_PCM, 0 }},
+};
+
+static struct catpt_stream_template loopback_cp = {
+ .path_id = CATPT_PATH_SSP0_OUT,
+ .type = CATPT_STRM_TYPE_LOOPBACK,
+ .num_entries = 1,
+ .entries = {{ CATPT_MODID_PCM_REFERENCE, 0 }},
+};
+
+static struct catpt_stream_template bluetooth_pb = {
+ .path_id = CATPT_PATH_SSP1_OUT,
+ .type = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
+ .num_entries = 1,
+ .entries = {{ CATPT_MODID_BLUETOOTH_RENDER, 0 }},
+};
+
+static struct catpt_stream_template bluetooth_cp = {
+ .path_id = CATPT_PATH_SSP1_IN,
+ .type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE,
+ .num_entries = 1,
+ .entries = {{ CATPT_MODID_BLUETOOTH_CAPTURE, 0 }},
+};
+
+static struct catpt_stream_template *catpt_topology[] = {
+ [CATPT_STRM_TYPE_RENDER] = &offload_pb,
+ [CATPT_STRM_TYPE_SYSTEM] = &system_pb,
+ [CATPT_STRM_TYPE_CAPTURE] = &system_cp,
+ [CATPT_STRM_TYPE_LOOPBACK] = &loopback_cp,
+ [CATPT_STRM_TYPE_BLUETOOTH_RENDER] = &bluetooth_pb,
+ [CATPT_STRM_TYPE_BLUETOOTH_CAPTURE] = &bluetooth_cp,
+};
+
+static struct catpt_stream_template *
+catpt_get_stream_template(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);
+ enum catpt_stream_type type;
+
+ type = cpu_dai->driver->id;
+
+ /* account for capture in bidirectional dais */
+ switch (type) {
+ case CATPT_STRM_TYPE_SYSTEM:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ type = CATPT_STRM_TYPE_CAPTURE;
+ break;
+ case CATPT_STRM_TYPE_BLUETOOTH_RENDER:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE;
+ break;
+ default:
+ break;
+ }
+
+ return catpt_topology[type];
+}
+
+struct catpt_stream_runtime *
+catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
+{
+ struct catpt_stream_runtime *pos, *result = NULL;
+
+ spin_lock(&cdev->list_lock);
+ list_for_each_entry(pos, &cdev->stream_list, node) {
+ if (pos->info.stream_hw_id == stream_hw_id) {
+ result = pos;
+ break;
+ }
+ }
+
+ spin_unlock(&cdev->list_lock);
+ return result;
+}
+
+static u32 catpt_stream_read_position(struct catpt_dev *cdev,
+ struct catpt_stream_runtime *stream)
+{
+ u32 pos;
+
+ memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr,
+ sizeof(pos));
+ return pos;
+}
+
+static u32 catpt_stream_volume(struct catpt_dev *cdev,
+ struct catpt_stream_runtime *stream, u32 channel)
+{
+ u32 volume, offset;
+
+ if (channel >= CATPT_CHANNELS_MAX)
+ channel = 0;
+
+ offset = stream->info.volume_regaddr[channel];
+ memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));
+ return volume;
+}
+
+static u32 catpt_mixer_volume(struct catpt_dev *cdev,
+ struct catpt_mixer_stream_info *info, u32 channel)
+{
+ u32 volume, offset;
+
+ if (channel >= CATPT_CHANNELS_MAX)
+ channel = 0;
+
+ offset = info->volume_regaddr[channel];
+ memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));
+ return volume;
+}
+
+static void catpt_arrange_page_table(struct snd_pcm_substream *substream,
+ struct snd_dma_buffer *pgtbl)
+{
+ struct snd_pcm_runtime *rtm = substream->runtime;
+ struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream);
+ int i, pages;
+
+ pages = snd_sgbuf_aligned_pages(rtm->dma_bytes);
+
+ for (i = 0; i < pages; i++) {
+ u32 pfn, offset;
+ u32 *page_table;
+
+ pfn = PFN_DOWN(snd_sgbuf_get_addr(databuf, i * PAGE_SIZE));
+ /* incrementing by 2 on even and 3 on odd */
+ offset = ((i << 2) + i) >> 1;
+ page_table = (u32 *)(pgtbl->area + offset);
+
+ if (i & 1)
+ *page_table |= (pfn << 4);
+ else
+ *page_table |= pfn;
+ }
+}
+
+static u32 catpt_get_channel_map(enum catpt_channel_config config)
+{
+ switch (config) {
+ case CATPT_CHANNEL_CONFIG_MONO:
+ return GENMASK(31, 4) | CATPT_CHANNEL_CENTER;
+
+ case CATPT_CHANNEL_CONFIG_STEREO:
+ return GENMASK(31, 8) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_RIGHT << 4);
+
+ case CATPT_CHANNEL_CONFIG_2_POINT_1:
+ return GENMASK(31, 12) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_RIGHT << 4)
+ | (CATPT_CHANNEL_LFE << 8);
+
+ case CATPT_CHANNEL_CONFIG_3_POINT_0:
+ return GENMASK(31, 12) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_CENTER << 4)
+ | (CATPT_CHANNEL_RIGHT << 8);
+
+ case CATPT_CHANNEL_CONFIG_3_POINT_1:
+ return GENMASK(31, 16) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_CENTER << 4)
+ | (CATPT_CHANNEL_RIGHT << 8)
+ | (CATPT_CHANNEL_LFE << 12);
+
+ case CATPT_CHANNEL_CONFIG_QUATRO:
+ return GENMASK(31, 16) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_RIGHT << 4)
+ | (CATPT_CHANNEL_LEFT_SURROUND << 8)
+ | (CATPT_CHANNEL_RIGHT_SURROUND << 12);
+
+ case CATPT_CHANNEL_CONFIG_4_POINT_0:
+ return GENMASK(31, 16) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_CENTER << 4)
+ | (CATPT_CHANNEL_RIGHT << 8)
+ | (CATPT_CHANNEL_CENTER_SURROUND << 12);
+
+ case CATPT_CHANNEL_CONFIG_5_POINT_0:
+ return GENMASK(31, 20) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_CENTER << 4)
+ | (CATPT_CHANNEL_RIGHT << 8)
+ | (CATPT_CHANNEL_LEFT_SURROUND << 12)
+ | (CATPT_CHANNEL_RIGHT_SURROUND << 16);
+
+ case CATPT_CHANNEL_CONFIG_5_POINT_1:
+ return GENMASK(31, 24) | CATPT_CHANNEL_CENTER
+ | (CATPT_CHANNEL_LEFT << 4)
+ | (CATPT_CHANNEL_RIGHT << 8)
+ | (CATPT_CHANNEL_LEFT_SURROUND << 12)
+ | (CATPT_CHANNEL_RIGHT_SURROUND << 16)
+ | (CATPT_CHANNEL_LFE << 20);
+
+ case CATPT_CHANNEL_CONFIG_DUAL_MONO:
+ return GENMASK(31, 8) | CATPT_CHANNEL_LEFT
+ | (CATPT_CHANNEL_LEFT << 4);
+
+ default:
+ return U32_MAX;
+ }
+}
+
+static enum catpt_channel_config catpt_get_channel_config(u32 num_channels)
+{
+ switch (num_channels) {
+ case 6:
+ return CATPT_CHANNEL_CONFIG_5_POINT_1;
+ case 5:
+ return CATPT_CHANNEL_CONFIG_5_POINT_0;
+ case 4:
+ return CATPT_CHANNEL_CONFIG_QUATRO;
+ case 3:
+ return CATPT_CHANNEL_CONFIG_2_POINT_1;
+ case 1:
+ return CATPT_CHANNEL_CONFIG_MONO;
+ case 2:
+ default:
+ return CATPT_CHANNEL_CONFIG_STEREO;
+ }
+}
+
+static int catpt_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct catpt_stream_template *template;
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ struct resource *res;
+ int ret;
+
+ template = catpt_get_stream_template(substream);
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, cdev->dev, PAGE_SIZE,
+ &stream->pgtbl);
+ if (ret)
+ goto err_pgtbl;
+
+ res = catpt_request_region(&cdev->dram, template->persistent_size);
+ if (!res) {
+ ret = -EBUSY;
+ goto err_request;
+ }
+
+ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
+
+ stream->template = template;
+ stream->persistent = res;
+ stream->substream = substream;
+ INIT_LIST_HEAD(&stream->node);
+ snd_soc_dai_set_dma_data(dai, substream, stream);
+
+ spin_lock(&cdev->list_lock);
+ list_add_tail(&stream->node, &cdev->stream_list);
+ spin_unlock(&cdev->list_lock);
+
+ return 0;
+
+err_request:
+ snd_dma_free_pages(&stream->pgtbl);
+err_pgtbl:
+ kfree(stream);
+ return ret;
+}
+
+static void catpt_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ spin_lock(&cdev->list_lock);
+ list_del(&stream->node);
+ spin_unlock(&cdev->list_lock);
+
+ release_resource(stream->persistent);
+ kfree(stream->persistent);
+ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
+
+ snd_dma_free_pages(&stream->pgtbl);
+ kfree(stream);
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);
+
+static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
+ struct catpt_stream_runtime *stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_kcontrol *pos;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ const char *name;
+ int ret;
+ u32 id = stream->info.stream_hw_id;
+
+ /* only selected streams have individual controls */
+ switch (id) {
+ case CATPT_PIN_ID_OFFLOAD1:
+ name = "Media0 Playback Volume";
+ break;
+ case CATPT_PIN_ID_OFFLOAD2:
+ name = "Media1 Playback Volume";
+ break;
+ case CATPT_PIN_ID_CAPTURE1:
+ name = "Mic Capture Volume";
+ break;
+ case CATPT_PIN_ID_REFERENCE:
+ name = "Loopback Mute";
+ break;
+ default:
+ return 0;
+ }
+
+ list_for_each_entry(pos, &component->card->snd_card->controls, list) {
+ if (pos->private_data == component &&
+ !strncmp(name, pos->id.name, sizeof(pos->id.name)))
+ break;
+ }
+ if (list_entry_is_head(pos, &component->card->snd_card->controls, list))
+ return -ENOENT;
+
+ if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)
+ return catpt_set_dspvol(cdev, id, (long *)pos->private_value);
+ ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+ return 0;
+}
+
+static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *rtm = substream->runtime;
+ struct snd_dma_buffer *dmab;
+ struct catpt_stream_runtime *stream;
+ struct catpt_audio_format afmt;
+ struct catpt_ring_info rinfo;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ int ret;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (stream->allocated)
+ return 0;
+
+ memset(&afmt, 0, sizeof(afmt));
+ afmt.sample_rate = params_rate(params);
+ afmt.bit_depth = params_physical_width(params);
+ afmt.valid_bit_depth = params_width(params);
+ afmt.num_channels = params_channels(params);
+ afmt.channel_config = catpt_get_channel_config(afmt.num_channels);
+ afmt.channel_map = catpt_get_channel_map(afmt.channel_config);
+ afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL;
+
+ dmab = snd_pcm_get_dma_buf(substream);
+ catpt_arrange_page_table(substream, &stream->pgtbl);
+
+ memset(&rinfo, 0, sizeof(rinfo));
+ rinfo.page_table_addr = stream->pgtbl.addr;
+ rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE);
+ rinfo.size = rtm->dma_bytes;
+ rinfo.offset = 0;
+ rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0));
+
+ ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id,
+ stream->template->type,
+ &afmt, &rinfo,
+ stream->template->num_entries,
+ stream->template->entries,
+ stream->persistent,
+ cdev->scratch,
+ &stream->info);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ ret = catpt_dai_apply_usettings(dai, stream);
+ if (ret) {
+ catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
+ return ret;
+ }
+
+ stream->allocated = true;
+ return 0;
+}
+
+static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!stream->allocated)
+ return 0;
+
+ catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
+ catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
+
+ stream->allocated = false;
+ return 0;
+}
+
+static int catpt_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ int ret;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (stream->prepared)
+ return 0;
+
+ ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ stream->prepared = true;
+ return 0;
+}
+
+static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ snd_pcm_uframes_t pos;
+ int ret;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* only offload is set_write_pos driven */
+ if (stream->template->type != CATPT_STRM_TYPE_RENDER)
+ goto resume_stream;
+
+ pos = frames_to_bytes(runtime, runtime->start_threshold);
+ /*
+ * Dsp operates on buffer halves, thus max 2x set_write_pos
+ * (entire buffer filled) prior to stream start.
+ */
+ ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
+ pos, false, false);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ resume_stream:
+ catpt_dsp_update_lpclock(cdev);
+ ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ stream->prepared = false;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
+ catpt_dsp_update_lpclock(cdev);
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void catpt_stream_update_position(struct catpt_dev *cdev,
+ struct catpt_stream_runtime *stream,
+ struct catpt_notify_position *pos)
+{
+ struct snd_pcm_substream *substream = stream->substream;
+ struct snd_pcm_runtime *r = substream->runtime;
+ snd_pcm_uframes_t dsppos, newpos;
+ int ret;
+
+ dsppos = bytes_to_frames(r, pos->stream_position);
+
+ if (!stream->prepared)
+ goto exit;
+ /* only offload is set_write_pos driven */
+ if (stream->template->type != CATPT_STRM_TYPE_RENDER)
+ goto exit;
+
+ if (dsppos >= r->buffer_size / 2)
+ newpos = r->buffer_size / 2;
+ else
+ newpos = 0;
+ /*
+ * Dsp operates on buffer halves, thus on every notify position
+ * (buffer half consumed) update wp to allow stream progression.
+ */
+ ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
+ frames_to_bytes(r, newpos),
+ false, false);
+ if (ret) {
+ dev_err(cdev->dev, "update position for stream %d failed: %d\n",
+ stream->info.stream_hw_id, ret);
+ return;
+ }
+exit:
+ snd_pcm_period_elapsed(substream);
+}
+
+/* 200 ms for 2 32-bit channels at 48kHz (native format) */
+#define CATPT_BUFFER_MAX_SIZE 76800
+#define CATPT_PCM_PERIODS_MAX 4
+#define CATPT_PCM_PERIODS_MIN 2
+
+static const struct snd_pcm_hardware catpt_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN,
+ .periods_min = CATPT_PCM_PERIODS_MIN,
+ .periods_max = CATPT_PCM_PERIODS_MAX,
+ .buffer_bytes_max = CATPT_BUFFER_MAX_SIZE,
+};
+
+static int catpt_component_pcm_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtm)
+{
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+
+ snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG,
+ cdev->dev,
+ catpt_pcm_hardware.buffer_bytes_max,
+ catpt_pcm_hardware.buffer_bytes_max);
+
+ return 0;
+}
+
+static int catpt_component_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
+
+ if (!rtm->dai_link->no_pcm)
+ snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);
+ return 0;
+}
+
+static snd_pcm_uframes_t
+catpt_component_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+ u32 pos;
+
+ if (rtm->dai_link->no_pcm)
+ return 0;
+
+ stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ pos = catpt_stream_read_position(cdev, stream);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static const struct snd_soc_dai_ops catpt_fe_dai_ops = {
+ .startup = catpt_dai_startup,
+ .shutdown = catpt_dai_shutdown,
+ .hw_params = catpt_dai_hw_params,
+ .hw_free = catpt_dai_hw_free,
+ .prepare = catpt_dai_prepare,
+ .trigger = catpt_dai_trigger,
+};
+
+static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);
+ struct catpt_ssp_device_format devfmt;
+ struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
+ int ret;
+
+ devfmt.iface = dai->driver->id;
+ devfmt.channels = codec_dai->driver->capture.channels_max;
+
+ switch (devfmt.iface) {
+ case CATPT_SSP_IFACE_0:
+ devfmt.mclk = CATPT_MCLK_FREQ_24_MHZ;
+
+ switch (devfmt.channels) {
+ case 4:
+ devfmt.mode = CATPT_SSP_MODE_TDM_PROVIDER;
+ devfmt.clock_divider = 4;
+ break;
+ case 2:
+ default:
+ devfmt.mode = CATPT_SSP_MODE_I2S_PROVIDER;
+ devfmt.clock_divider = 9;
+ break;
+ }
+ break;
+
+ case CATPT_SSP_IFACE_1:
+ devfmt.mclk = CATPT_MCLK_OFF;
+ devfmt.mode = CATPT_SSP_MODE_I2S_CONSUMER;
+ devfmt.clock_divider = 0;
+ break;
+ }
+
+ /* see if this is a new configuration */
+ if (!memcmp(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)))
+ return 0;
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ ret = catpt_ipc_set_device_format(cdev, &devfmt);
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ /* store device format set for given SSP */
+ memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
+ return 0;
+}
+
+static const struct snd_soc_dai_ops catpt_dai_ops = {
+ .pcm_new = catpt_dai_pcm_new,
+};
+
+static struct snd_soc_dai_driver dai_drivers[] = {
+/* FE DAIs */
+{
+ .name = "System Pin",
+ .id = CATPT_STRM_TYPE_SYSTEM,
+ .ops = &catpt_fe_dai_ops,
+ .playback = {
+ .stream_name = "System Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+ .capture = {
+ .stream_name = "Analog Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+},
+{
+ .name = "Offload0 Pin",
+ .id = CATPT_STRM_TYPE_RENDER,
+ .ops = &catpt_fe_dai_ops,
+ .playback = {
+ .stream_name = "Offload0 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+},
+{
+ .name = "Offload1 Pin",
+ .id = CATPT_STRM_TYPE_RENDER,
+ .ops = &catpt_fe_dai_ops,
+ .playback = {
+ .stream_name = "Offload1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+},
+{
+ .name = "Loopback Pin",
+ .id = CATPT_STRM_TYPE_LOOPBACK,
+ .ops = &catpt_fe_dai_ops,
+ .capture = {
+ .stream_name = "Loopback Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+},
+{
+ .name = "Bluetooth Pin",
+ .id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
+ .ops = &catpt_fe_dai_ops,
+ .playback = {
+ .stream_name = "Bluetooth Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Bluetooth Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+/* BE DAIs */
+{
+ .name = "ssp0-port",
+ .id = CATPT_SSP_IFACE_0,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &catpt_dai_ops,
+},
+{
+ .name = "ssp1-port",
+ .id = CATPT_SSP_IFACE_1,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &catpt_dai_ops,
+},
+};
+
+#define DSP_VOLUME_MAX S32_MAX /* 0db */
+#define DSP_VOLUME_STEP_MAX 30
+
+static u32 ctlvol_to_dspvol(u32 value)
+{
+ if (value > DSP_VOLUME_STEP_MAX)
+ value = 0;
+ return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);
+}
+
+static u32 dspvol_to_ctlvol(u32 volume)
+{
+ if (volume > DSP_VOLUME_MAX)
+ return DSP_VOLUME_STEP_MAX;
+ return volume ? __fls(volume) : 0;
+}
+
+static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
+{
+ u32 dspvol;
+ int ret, i;
+
+ for (i = 1; i < CATPT_CHANNELS_MAX; i++)
+ if (ctlvol[i] != ctlvol[0])
+ break;
+
+ if (i == CATPT_CHANNELS_MAX) {
+ dspvol = ctlvol_to_dspvol(ctlvol[0]);
+
+ ret = catpt_ipc_set_volume(cdev, stream_id,
+ CATPT_ALL_CHANNELS_MASK, dspvol,
+ 0, CATPT_AUDIO_CURVE_NONE);
+ } else {
+ for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
+ dspvol = ctlvol_to_dspvol(ctlvol[i]);
+
+ ret = catpt_ipc_set_volume(cdev, stream_id,
+ i, dspvol,
+ 0, CATPT_AUDIO_CURVE_NONE);
+ if (ret)
+ break;
+ }
+ }
+
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+ return 0;
+}
+
+static int catpt_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = CATPT_CHANNELS_MAX;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;
+ return 0;
+}
+
+static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+ u32 dspvol;
+ int ret;
+ int i;
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
+ dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);
+ ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
+ }
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ return 0;
+}
+
+static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,
+ ucontrol->value.integer.value);
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ return ret;
+}
+
+static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ enum catpt_pin_id pin_id)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+ long *ctlvol = (long *)kcontrol->private_value;
+ u32 dspvol;
+ int ret;
+ int i;
+
+ stream = catpt_stream_find(cdev, pin_id);
+ if (!stream) {
+ for (i = 0; i < CATPT_CHANNELS_MAX; i++)
+ ucontrol->value.integer.value[i] = ctlvol[i];
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
+ dspvol = catpt_stream_volume(cdev, stream, i);
+ ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
+ }
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ return 0;
+}
+
+static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ enum catpt_pin_id pin_id)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+ long *ctlvol = (long *)kcontrol->private_value;
+ int ret, i;
+
+ stream = catpt_stream_find(cdev, pin_id);
+ if (!stream) {
+ for (i = 0; i < CATPT_CHANNELS_MAX; i++)
+ ctlvol[i] = ucontrol->value.integer.value[i];
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,
+ ucontrol->value.integer.value);
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ if (ret)
+ return ret;
+
+ for (i = 0; i < CATPT_CHANNELS_MAX; i++)
+ ctlvol[i] = ucontrol->value.integer.value[i];
+ return 0;
+}
+
+static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
+}
+
+static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
+}
+
+static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
+}
+
+static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
+}
+
+static int catpt_capture_volume_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
+}
+
+static int catpt_capture_volume_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
+}
+
+static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;
+ return 0;
+}
+
+static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct catpt_stream_runtime *stream;
+ struct catpt_dev *cdev = dev_get_drvdata(component->dev);
+ bool mute;
+ int ret;
+
+ mute = (bool)ucontrol->value.integer.value[0];
+ stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);
+ if (!stream) {
+ *(bool *)kcontrol->private_value = mute;
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ *(bool *)kcontrol->private_value = mute;
+ return 0;
+}
+
+static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,
+ unsigned int __user *bytes,
+ unsigned int size)
+{
+ return 0;
+}
+
+static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
+ const unsigned int __user *bytes,
+ unsigned int size)
+{
+ return 0;
+}
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
+
+#define CATPT_VOLUME_CTL(kname, sname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = (kname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = catpt_volume_info, \
+ .get = catpt_##sname##_volume_get, \
+ .put = catpt_##sname##_volume_put, \
+ .tlv.p = catpt_volume_tlv, \
+ .private_value = (unsigned long) \
+ &(long[CATPT_CHANNELS_MAX]) {0} }
+
+static const struct snd_kcontrol_new component_kcontrols[] = {
+/* Master volume (mixer stream) */
+CATPT_VOLUME_CTL("Master Playback Volume", mixer),
+/* Individual volume controls for offload and capture */
+CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),
+CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),
+CATPT_VOLUME_CTL("Mic Capture Volume", capture),
+SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},
+ catpt_loopback_switch_get, catpt_loopback_switch_put),
+/* Enable or disable WAVES module */
+SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
+ catpt_waves_switch_get, catpt_waves_switch_put),
+/* WAVES module parameter control */
+SND_SOC_BYTES_TLV("Waves Set Param", 128,
+ catpt_waves_param_get, catpt_waves_param_put),
+};
+
+static const struct snd_soc_dapm_widget component_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route component_routes[] = {
+ {"Playback VMixer", NULL, "System Playback"},
+ {"Playback VMixer", NULL, "Offload0 Playback"},
+ {"Playback VMixer", NULL, "Offload1 Playback"},
+
+ {"SSP0 CODEC OUT", NULL, "Playback VMixer"},
+
+ {"Analog Capture", NULL, "SSP0 CODEC IN"},
+ {"Loopback Capture", NULL, "SSP0 CODEC IN"},
+
+ {"SSP1 BT OUT", NULL, "Bluetooth Playback"},
+ {"Bluetooth Capture", NULL, "SSP1 BT IN"},
+};
+
+static const struct snd_soc_component_driver catpt_comp_driver = {
+ .name = "catpt-platform",
+
+ .pcm_construct = catpt_component_pcm_construct,
+ .open = catpt_component_open,
+ .pointer = catpt_component_pointer,
+
+ .controls = component_kcontrols,
+ .num_controls = ARRAY_SIZE(component_kcontrols),
+ .dapm_widgets = component_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(component_widgets),
+ .dapm_routes = component_routes,
+ .num_dapm_routes = ARRAY_SIZE(component_routes),
+};
+
+int catpt_arm_stream_templates(struct catpt_dev *cdev)
+{
+ struct resource *res;
+ u32 scratch_size = 0;
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {
+ struct catpt_stream_template *template;
+ struct catpt_module_entry *entry;
+ struct catpt_module_type *type;
+
+ template = catpt_topology[i];
+ template->persistent_size = 0;
+
+ for (j = 0; j < template->num_entries; j++) {
+ entry = &template->entries[j];
+ type = &cdev->modules[entry->module_id];
+
+ if (!type->loaded)
+ return -ENOENT;
+
+ entry->entry_point = type->entry_point;
+ template->persistent_size += type->persistent_size;
+ if (type->scratch_size > scratch_size)
+ scratch_size = type->scratch_size;
+ }
+ }
+
+ if (scratch_size) {
+ /* allocate single scratch area for all modules */
+ res = catpt_request_region(&cdev->dram, scratch_size);
+ if (!res)
+ return -EBUSY;
+ cdev->scratch = res;
+ }
+
+ return 0;
+}
+
+int catpt_register_plat_component(struct catpt_dev *cdev)
+{
+ struct snd_soc_component *component;
+ int ret;
+
+ component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+
+ ret = snd_soc_component_initialize(component, &catpt_comp_driver,
+ cdev->dev);
+ if (ret)
+ return ret;
+
+ component->name = catpt_comp_driver.name;
+ return snd_soc_add_component(component, dai_drivers,
+ ARRAY_SIZE(dai_drivers));
+}
diff --git a/sound/soc/intel/catpt/registers.h b/sound/soc/intel/catpt/registers.h
new file mode 100644
index 000000000000..6c1ad28c6d69
--- /dev/null
+++ b/sound/soc/intel/catpt/registers.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2020 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SND_SOC_INTEL_CATPT_REGS_H
+#define __SND_SOC_INTEL_CATPT_REGS_H
+
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <uapi/linux/pci_regs.h>
+
+#define CATPT_SHIM_REGS_SIZE 4096
+#define CATPT_DMA_REGS_SIZE 1024
+#define CATPT_DMA_COUNT 2
+#define CATPT_SSP_REGS_SIZE 512
+
+/* DSP Shim registers */
+
+#define CATPT_SHIM_CS1 0x00
+#define CATPT_SHIM_ISC 0x18
+#define CATPT_SHIM_ISD 0x20
+#define CATPT_SHIM_IMC 0x28
+#define CATPT_SHIM_IMD 0x30
+#define CATPT_SHIM_IPCC 0x38
+#define CATPT_SHIM_IPCD 0x40
+#define CATPT_SHIM_CLKCTL 0x78
+#define CATPT_SHIM_CS2 0x80
+#define CATPT_SHIM_LTRC 0xE0
+#define CATPT_SHIM_HMDC 0xE8
+
+#define CATPT_CS_LPCS BIT(31)
+#define CATPT_CS_SFCR(ssp) BIT(27 + (ssp))
+#define CATPT_CS_S1IOCS BIT(23)
+#define CATPT_CS_S0IOCS BIT(21)
+#define CATPT_CS_PCE BIT(15)
+#define CATPT_CS_SDPM(ssp) BIT(11 + (ssp))
+#define CATPT_CS_STALL BIT(10)
+#define CATPT_CS_DCS GENMASK(6, 4)
+/* b100 DSP core & audio fabric high clock */
+#define CATPT_CS_DCS_HIGH (0x4 << 4)
+#define CATPT_CS_SBCS(ssp) BIT(2 + (ssp))
+#define CATPT_CS_RST BIT(1)
+
+#define CATPT_ISC_IPCDB BIT(1)
+#define CATPT_ISC_IPCCD BIT(0)
+#define CATPT_ISD_DCPWM BIT(31)
+#define CATPT_ISD_IPCCB BIT(1)
+#define CATPT_ISD_IPCDD BIT(0)
+
+#define CATPT_IMC_IPCDB BIT(1)
+#define CATPT_IMC_IPCCD BIT(0)
+#define CATPT_IMD_IPCCB BIT(1)
+#define CATPT_IMD_IPCDD BIT(0)
+
+#define CATPT_IPCC_BUSY BIT(31)
+#define CATPT_IPCC_DONE BIT(30)
+#define CATPT_IPCD_BUSY BIT(31)
+#define CATPT_IPCD_DONE BIT(30)
+
+#define CATPT_CLKCTL_CFCIP BIT(31)
+#define CATPT_CLKCTL_SMOS GENMASK(25, 24)
+
+#define CATPT_HMDC_HDDA(e, ch) BIT(8 * (e) + (ch))
+
+/* defaults to reset SHIM registers to after each power cycle */
+#define CATPT_CS_DEFAULT 0x8480040E
+#define CATPT_ISC_DEFAULT 0x0
+#define CATPT_ISD_DEFAULT 0x0
+#define CATPT_IMC_DEFAULT 0x7FFF0003
+#define CATPT_IMD_DEFAULT 0x7FFF0003
+#define CATPT_IPCC_DEFAULT 0x0
+#define CATPT_IPCD_DEFAULT 0x0
+#define CATPT_CLKCTL_DEFAULT 0x7FF
+#define CATPT_CS2_DEFAULT 0x0
+#define CATPT_LTRC_DEFAULT 0x0
+#define CATPT_HMDC_DEFAULT 0x0
+
+/* PCI Configuration registers */
+
+#define CATPT_PCI_PMCAPID 0x80
+#define CATPT_PCI_PMCS (CATPT_PCI_PMCAPID + PCI_PM_CTRL)
+#define CATPT_PCI_VDRTCTL0 0xA0
+#define CATPT_PCI_VDRTCTL2 0xA8
+
+#define CATPT_VDRTCTL2_DTCGE BIT(10)
+#define CATPT_VDRTCTL2_DCLCGE BIT(1)
+#define CATPT_VDRTCTL2_CGEALL 0xF7F
+
+/* LPT PCI Configuration bits */
+
+#define LPT_VDRTCTL0_DSRAMPGE(b) BIT(16 + (b))
+#define LPT_VDRTCTL0_DSRAMPGE_MASK GENMASK(31, 16)
+#define LPT_VDRTCTL0_ISRAMPGE(b) BIT(6 + (b))
+#define LPT_VDRTCTL0_ISRAMPGE_MASK GENMASK(15, 6)
+#define LPT_VDRTCTL0_D3SRAMPGD BIT(2)
+#define LPT_VDRTCTL0_D3PGD BIT(1)
+#define LPT_VDRTCTL0_APLLSE BIT(0)
+
+/* WPT PCI Configuration bits */
+
+#define WPT_VDRTCTL0_DSRAMPGE(b) BIT(12 + (b))
+#define WPT_VDRTCTL0_DSRAMPGE_MASK GENMASK(31, 12)
+#define WPT_VDRTCTL0_ISRAMPGE(b) BIT(2 + (b))
+#define WPT_VDRTCTL0_ISRAMPGE_MASK GENMASK(11, 2)
+#define WPT_VDRTCTL0_D3SRAMPGD BIT(1)
+#define WPT_VDRTCTL0_D3PGD BIT(0)
+
+#define WPT_VDRTCTL2_APLLSE BIT(31)
+
+/* defaults to reset SSP registers to after each power cycle */
+#define CATPT_SSC0_DEFAULT 0x0
+#define CATPT_SSC1_DEFAULT 0x0
+#define CATPT_SSS_DEFAULT 0xF004
+#define CATPT_SSIT_DEFAULT 0x0
+#define CATPT_SSD_DEFAULT 0xC43893A3
+#define CATPT_SSTO_DEFAULT 0x0
+#define CATPT_SSPSP_DEFAULT 0x0
+#define CATPT_SSTSA_DEFAULT 0x0
+#define CATPT_SSRSA_DEFAULT 0x0
+#define CATPT_SSTSS_DEFAULT 0x0
+#define CATPT_SSCR2_DEFAULT 0x0
+#define CATPT_SSPSP2_DEFAULT 0x0
+
+/* Physically the same block, access address differs between host and dsp */
+#define CATPT_DSP_DRAM_OFFSET 0x400000
+#define catpt_to_host_offset(offset) ((offset) & ~(CATPT_DSP_DRAM_OFFSET))
+#define catpt_to_dsp_offset(offset) ((offset) | CATPT_DSP_DRAM_OFFSET)
+
+#define CATPT_MEMBLOCK_SIZE 0x8000
+#define catpt_num_dram(cdev) (hweight_long((cdev)->spec->dram_mask))
+#define catpt_num_iram(cdev) (hweight_long((cdev)->spec->iram_mask))
+#define catpt_dram_size(cdev) (catpt_num_dram(cdev) * CATPT_MEMBLOCK_SIZE)
+#define catpt_iram_size(cdev) (catpt_num_iram(cdev) * CATPT_MEMBLOCK_SIZE)
+
+/* registry I/O helpers */
+
+#define catpt_shim_addr(cdev) \
+ ((cdev)->lpe_ba + (cdev)->spec->host_shim_offset)
+#define catpt_dma_addr(cdev, dma) \
+ ((cdev)->lpe_ba + (cdev)->spec->host_dma_offset[dma])
+#define catpt_ssp_addr(cdev, ssp) \
+ ((cdev)->lpe_ba + (cdev)->spec->host_ssp_offset[ssp])
+#define catpt_inbox_addr(cdev) \
+ ((cdev)->lpe_ba + (cdev)->ipc.config.inbox_offset)
+#define catpt_outbox_addr(cdev) \
+ ((cdev)->lpe_ba + (cdev)->ipc.config.outbox_offset)
+
+#define catpt_writel_ssp(cdev, ssp, reg, val) \
+ writel(val, catpt_ssp_addr(cdev, ssp) + (reg))
+
+#define catpt_readl_shim(cdev, reg) \
+ readl(catpt_shim_addr(cdev) + CATPT_SHIM_##reg)
+#define catpt_writel_shim(cdev, reg, val) \
+ writel(val, catpt_shim_addr(cdev) + CATPT_SHIM_##reg)
+#define catpt_updatel_shim(cdev, reg, mask, val) \
+ catpt_writel_shim(cdev, reg, \
+ (catpt_readl_shim(cdev, reg) & ~(mask)) | (val))
+
+#define catpt_readl_poll_shim(cdev, reg, val, cond, delay_us, timeout_us) \
+ readl_poll_timeout(catpt_shim_addr(cdev) + CATPT_SHIM_##reg, \
+ val, cond, delay_us, timeout_us)
+
+#define catpt_readl_pci(cdev, reg) \
+ readl(cdev->pci_ba + CATPT_PCI_##reg)
+#define catpt_writel_pci(cdev, reg, val) \
+ writel(val, cdev->pci_ba + CATPT_PCI_##reg)
+#define catpt_updatel_pci(cdev, reg, mask, val) \
+ catpt_writel_pci(cdev, reg, \
+ (catpt_readl_pci(cdev, reg) & ~(mask)) | (val))
+
+#define catpt_readl_poll_pci(cdev, reg, val, cond, delay_us, timeout_us) \
+ readl_poll_timeout((cdev)->pci_ba + CATPT_PCI_##reg, \
+ val, cond, delay_us, timeout_us)
+
+#endif
diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c
new file mode 100644
index 000000000000..e961e172f9b7
--- /dev/null
+++ b/sound/soc/intel/catpt/sysfs.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/pm_runtime.h>
+#include "core.h"
+
+static ssize_t fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct catpt_dev *cdev = dev_get_drvdata(dev);
+ struct catpt_fw_version version;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(cdev->dev);
+ if (ret)
+ return ret;
+
+ ret = catpt_ipc_get_fw_version(cdev, &version);
+
+ pm_runtime_put_autosuspend(cdev->dev);
+
+ if (ret)
+ return CATPT_IPC_ERROR(ret);
+
+ return sysfs_emit(buf, "%d.%d.%d.%d\n", version.type, version.major,
+ version.minor, version.build);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static ssize_t fw_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct catpt_dev *cdev = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", cdev->ipc.config.fw_info);
+}
+static DEVICE_ATTR_RO(fw_info);
+
+static struct attribute *catpt_attrs[] = {
+ &dev_attr_fw_version.attr,
+ &dev_attr_fw_info.attr,
+ NULL
+};
+
+static const struct attribute_group catpt_attr_group = {
+ .attrs = catpt_attrs,
+};
+
+const struct attribute_group *catpt_attr_groups[] = {
+ &catpt_attr_group,
+ NULL
+};
diff --git a/sound/soc/intel/catpt/trace.h b/sound/soc/intel/catpt/trace.h
new file mode 100644
index 000000000000..010f57b6a7a8
--- /dev/null
+++ b/sound/soc/intel/catpt/trace.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2020 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM intel_catpt
+
+#if !defined(__SND_SOC_INTEL_CATPT_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __SND_SOC_INTEL_CATPT_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(catpt_ipc_msg,
+
+ TP_PROTO(u32 header),
+
+ TP_ARGS(header),
+
+ TP_STRUCT__entry(
+ __field(u32, header)
+ ),
+
+ TP_fast_assign(
+ __entry->header = header;
+ ),
+
+ TP_printk("0x%08x", __entry->header)
+);
+
+DEFINE_EVENT(catpt_ipc_msg, catpt_irq,
+ TP_PROTO(u32 header),
+ TP_ARGS(header)
+);
+
+DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_request,
+ TP_PROTO(u32 header),
+ TP_ARGS(header)
+);
+
+DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_reply,
+ TP_PROTO(u32 header),
+ TP_ARGS(header)
+);
+
+DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_notify,
+ TP_PROTO(u32 header),
+ TP_ARGS(header)
+);
+
+TRACE_EVENT_CONDITION(catpt_ipc_payload,
+
+ TP_PROTO(const u8 *data, size_t size),
+
+ TP_ARGS(data, size),
+
+ TP_CONDITION(data && size),
+
+ TP_STRUCT__entry(
+ __dynamic_array(u8, buf, size)
+ ),
+
+ TP_fast_assign(
+ memcpy(__get_dynamic_array(buf), data, size);
+ ),
+
+ TP_printk("%u byte(s)%s",
+ __get_dynamic_array_len(buf),
+ __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4,
+ __get_dynamic_array(buf),
+ __get_dynamic_array_len(buf), false))
+);
+
+#endif /* __SND_SOC_INTEL_CATPT_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index 668fdeee195e..dbfd9e2ac015 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -1,13 +1,23 @@
-snd-soc-sst-dsp-objs := sst-dsp.o
-ifneq ($(CONFIG_SND_SST_IPC_ACPI),)
-snd-soc-sst-acpi-objs := sst-match-acpi.o
-else
-snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o
-endif
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-acpi-intel-match-y := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \
+ soc-acpi-intel-hsw-bdw-match.o \
+ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \
+ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \
+ soc-acpi-intel-cnl-match.o soc-acpi-intel-cfl-match.o \
+ soc-acpi-intel-cml-match.o soc-acpi-intel-icl-match.o \
+ soc-acpi-intel-tgl-match.o soc-acpi-intel-ehl-match.o \
+ soc-acpi-intel-jsl-match.o soc-acpi-intel-adl-match.o \
+ soc-acpi-intel-rpl-match.o soc-acpi-intel-mtl-match.o \
+ soc-acpi-intel-arl-match.o \
+ soc-acpi-intel-lnl-match.o \
+ soc-acpi-intel-ptl-match.o \
+ soc-acpi-intel-nvl-match.o \
+ soc-acpi-intel-hda-match.o \
+ soc-acpi-intel-sdw-mockup-match.o sof-function-topology-lib.o
-snd-soc-sst-ipc-objs := sst-ipc.o
+snd-soc-acpi-intel-match-y += soc-acpi-intel-ssp-common.o
-snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
+snd-soc-acpi-intel-sdca-quirks-y += soc-acpi-intel-sdca-quirks.o
-obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
-obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
+obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o
+obj-$(CONFIG_SND_SOC_ACPI_INTEL_SDCA_QUIRKS) += snd-soc-acpi-intel-sdca-quirks.o
diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
new file mode 100644
index 000000000000..a68efbe98948
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -0,0 +1,783 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-apci-intel-adl-match.c - tables and support for ADL ACPI enumeration.
+ *
+ * Copyright (c) 2020, Intel Corporation.
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = {
+ {
+ .adr = 0x00023201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00023301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = {
+ {
+ .adr = 0x00033001fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00033101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+ {
+ .adr = 0x00003001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = {
+ {
+ .adr = 0x000220025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1308-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt715_3_adr[] = {
+ {
+ .adr = 0x000320025D071500ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt715"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_2_adr[] = {
+ {
+ .adr = 0x000230025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
+ {
+ .adr = 0x000131025D131601ull, /* unique ID is set for some reason */
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
+ {
+ .adr = 0x000330025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_0_group2_adr[] = {
+ {
+ .adr = 0x000031025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
+ {
+ .adr = 0x000130025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group2_adr[] = {
+ {
+ .adr = 0x000232025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = {
+ {
+ .adr = 0x000130025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_single_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = {
+ {
+ .adr = 0x000330025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_0_adr[] = {
+ {
+ .adr = 0x000030025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_2_adr[] = {
+ {
+ .adr = 0x000230025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_3_adr[] = {
+ {
+ .adr = 0x000330025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr adl_default[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_group1_adr),
+ .adr_d = rt1308_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1308_2_group1_adr),
+ .adr_d = rt1308_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdca_default[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdca_3_in_1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt714_2_adr),
+ .adr_d = rt714_2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_group1_adr),
+ .adr_d = rt1316_3_group1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link2_rt1316_link01_rt714_link3[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+ .adr_d = rt711_sdca_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
+ .adr_d = rt1316_0_group2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link2_rt1316_link01[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+ .adr_d = rt711_sdca_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
+ .adr_d = rt1316_0_group2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link12_rt714_link0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link1_rt714_link0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_single_adr),
+ .adr_d = rt1316_1_single_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link3[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_single_adr),
+ .adr_d = rt1316_2_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link0[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_single_adr),
+ .adr_d = rt1316_2_single_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link0_rt1316_link3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_single_adr),
+ .adr_d = rt1316_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link0_rt1316_link2[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_single_adr),
+ .adr_d = rt1316_2_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_adr_device mx8373_2_adr[] = {
+ {
+ .adr = 0x000223019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "Left"
+ },
+ {
+ .adr = 0x000227019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "Right"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt5682_0_adr[] = {
+ {
+ .adr = 0x000021025D568200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt5682"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr adl_cs42l43_l0_cs35l56_l23[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_r_adr),
+ .adr_d = cs35l56_2_r_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_l_adr),
+ .adr_d = cs35l56_3_l_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adlps_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_chromebook_base[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt5682_0_adr),
+ .adr_d = rt5682_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(mx8373_2_adr),
+ .adr_d = mx8373_2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link02[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
+ .adr_d = rt1316_0_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group2_adr),
+ .adr_d = rt1316_2_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_codecs adl_max98357a_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+static const struct snd_soc_acpi_codecs adl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
+static const struct snd_soc_acpi_codecs adl_rt1019p_amp = {
+ .num_codecs = 1,
+ .codecs = {"RTL1019"}
+};
+
+static const struct snd_soc_acpi_codecs adl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
+ {
+ .comp_ids = &adl_rt5682_rt5682s_hp,
+ .drv_name = "adl_mx98357_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98357a_amp,
+ .sof_tplg_filename = "sof-adl-max98357a-rt5682.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "adl_rt1019p_8825",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_rt1019p_amp,
+ .sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg",
+ },
+ {
+ .comp_ids = &adl_rt5682_rt5682s_hp,
+ .drv_name = "adl_rt5682_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-adl-rt5682-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "adl_es83x6_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-adl-es83x6-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-adl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ /* place boards for each headphone codec: sof driver will complete the
+ * tplg name and machine driver will detect the amp type
+ */
+ {
+ .id = CS42L42_ACPI_HID,
+ .drv_name = "adl_cs42l42_def",
+ .sof_tplg_filename = "sof-adl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = DA7219_ACPI_HID,
+ .drv_name = "adl_da7219_def",
+ .sof_tplg_filename = "sof-adl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = NAU8825_ACPI_HID,
+ .drv_name = "adl_nau8825_def",
+ .sof_tplg_filename = "sof-adl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = RT5650_ACPI_HID,
+ .drv_name = "adl_rt5682_def",
+ .sof_tplg_filename = "sof-adl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .comp_ids = &adl_rt5682_rt5682s_hp,
+ .drv_name = "adl_rt5682_def",
+ .sof_tplg_filename = "sof-adl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ /* place amp-only boards in the end of table */
+ {
+ .id = "CSC3541",
+ .drv_name = "adl_cs35l41",
+ .sof_tplg_filename = "sof-adl-cs35l41.tplg",
+ },
+ {
+ .id = "INTC10B0",
+ .drv_name = "adl_lt6911_hdmi_ssp",
+ .sof_tplg_filename = "sof-adl-nocodec-hdmi-ssp02.tplg"
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines);
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = adl_cs42l43_l0_cs35l56_l23,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-cs42l43-l0-cs35l56-l23.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = adl_default,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l0-rt1308-l12-rt715-l3.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = adl_sdca_default,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l12-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = adl_sdca_3_in_1,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l13-rt714-l2.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = adl_sdw_rt711_link2_rt1316_link01_rt714_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0x7, /* rt1316 on link0 and link1 & rt711 on link2*/
+ .links = adl_sdw_rt711_link2_rt1316_link01,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01.tplg",
+ },
+ {
+ .link_mask = 0xC, /* rt1316 on link2 & rt714 on link3 */
+ .links = adl_sdw_rt1316_link2_rt714_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */
+ .links = adl_sdw_rt1316_link12_rt714_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt1316-l12-rt714-l0.tplg",
+ },
+ {
+ .link_mask = 0x3, /* rt1316 on link1 & rt714 on link0 */
+ .links = adl_sdw_rt1316_link1_rt714_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt1316-l1-mono-rt714-l0.tplg",
+ },
+ {
+ .link_mask = 0x5, /* 2 active links required */
+ .links = adl_sdw_rt1316_link2_rt714_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l0.tplg",
+ },
+ {
+ .link_mask = 0x9, /* 2 active links required */
+ .links = adl_sdw_rt711_link0_rt1316_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l3.tplg",
+ },
+ {
+ .link_mask = 0x5, /* 2 active links required */
+ .links = adl_sdw_rt711_link0_rt1316_link2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l2.tplg",
+ },
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = adl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711.tplg",
+ },
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = adlps_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt711.tplg",
+ },
+ {
+ .link_mask = 0x5, /* rt5682 on link0 & 2xmax98373 on link 2 */
+ .links = adl_chromebook_base,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-sdw-max98373-rt5682.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(2),
+ .links = adl_sdw_rt1316_link02,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt1316-l02.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
new file mode 100644
index 000000000000..6bf7a6250ddc
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-apci-intel-arl-match.c - tables and support for ARL ACPI enumeration.
+ *
+ * Copyright (c) 2023 Intel Corporation.
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+#include "sof-function-topology-lib.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
+/*
+ * RT722 is a multi-function codec, three endpoints are created for
+ * its headset, amp and dmic functions.
+ */
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_lr_adr[] = {
+ {
+ .adr = 0x00023001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00023101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_lr_adr[] = {
+ {
+ .adr = 0x00033001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00033401FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = {
+ {
+ .adr = 0x00023201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00023301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = {
+ {
+ .adr = 0x00033001fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00033101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_r1_adr[] = {
+ {
+ .adr = 0x00023101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_l3_adr[] = {
+ {
+ .adr = 0x00033301fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_r3_adr[] = {
+ {
+ .adr = 0x00023301fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = {
+ {
+ .adr = 0x00033101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+ {
+ .adr = 0x00003001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_2_adr[] = {
+ {
+ .adr = 0x00023001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025D072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = {
+ {
+ .adr = 0x000330025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_2_single_adr[] = {
+ {
+ .adr = 0x000230025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l2[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs42l43_2_adr),
+ .adr_d = cs42l43_2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l2_cs35l56_l3[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs42l43_2_adr),
+ .adr_d = cs42l43_2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_lr_adr),
+ .adr_d = cs35l56_3_lr_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_l2[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_lr_adr),
+ .adr_d = cs35l56_2_lr_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_l23[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_r_adr),
+ .adr_d = cs35l56_2_r_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_l_adr),
+ .adr_d = cs35l56_3_l_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_2_l23[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_r1_adr),
+ .adr_d = cs35l56_2_r1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_l3_adr),
+ .adr_d = cs35l56_3_l3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_3_l23[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_r3_adr),
+ .adr_d = cs35l56_2_r3_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_l1_adr),
+ .adr_d = cs35l56_3_l1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_sdca_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_single_adr),
+ .adr_d = rt1316_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1320_2_single_adr),
+ .adr_d = rt1320_2_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_codecs arl_essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs arl_rt5682_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
+static const struct snd_soc_acpi_codecs arl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_machines[] = {
+ {
+ .comp_ids = &arl_essx_83x6,
+ .drv_name = "arl_es83x6_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &arl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-arl-es83x6-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &arl_essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-arl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {
+ .comp_ids = &arl_rt5682_hp,
+ .drv_name = "arl_rt5682_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &arl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-arl-rt5682-ssp1-hdmi-ssp02.tplg",
+ },
+ /* place amp-only boards in the end of table */
+ {
+ .id = "INTC10B0",
+ .drv_name = "arl_lt6911_hdmi_ssp",
+ .sof_tplg_filename = "sof-arl-hdmi-ssp02.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_arl_machines);
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = arl_cs42l43_l0_cs35l56_l23,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = arl_cs42l43_l0_cs35l56_2_l23,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = arl_cs42l43_l0_cs35l56_3_l23,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0) | BIT(2),
+ .links = arl_cs42l43_l0_cs35l56_l2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = arl_cs42l43_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(2) | BIT(3),
+ .links = arl_cs42l43_l2_cs35l56_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(2),
+ .links = arl_cs42l43_l2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0) | BIT(3),
+ .links = arl_rt711_l0_rt1316_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg",
+ },
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = arl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-rt711.tplg",
+ },
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = arl_sdca_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-rt711-l0.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(2),
+ .links = arl_rt722_l0_rt1320_l2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-rt722-l0_rt1320-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_arl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
new file mode 100644
index 000000000000..f99cf6c794dc
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-bxt-match.c - tables and support for BXT ACPI enumeration.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ */
+
+#include <linux/dmi.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+enum {
+ APL_RVP,
+};
+
+static const struct dmi_system_id apl_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
+ DMI_MATCH(DMI_BOARD_NAME, "Apollolake RVP1A"),
+ },
+ .driver_data = (void *)(APL_RVP),
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach *apl_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+ const struct dmi_system_id *dmi_id;
+ unsigned long apl_machine_id;
+
+ dmi_id = dmi_first_match(apl_table);
+ if (dmi_id) {
+ apl_machine_id = (unsigned long)dmi_id->driver_data;
+ if (apl_machine_id == APL_RVP)
+ return NULL;
+ }
+
+ return mach;
+}
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs bxt_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "bxt_alc298s_i2s",
+ .fw_filename = "intel/dsp_fw_bxtn.bin",
+ .sof_tplg_filename = "sof-apl-rt298.tplg",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "bxt_da7219_mx98357a",
+ .fw_filename = "intel/dsp_fw_bxtn.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &bxt_codecs,
+ .sof_tplg_filename = "sof-apl-da7219.tplg",
+ },
+ {
+ .id = "104C5122",
+ .drv_name = "sof_pcm512x",
+ .sof_tplg_filename = "sof-apl-pcm512x.tplg",
+ },
+ {
+ .id = "1AEC8804",
+ .drv_name = "sof-wm8804",
+ .sof_tplg_filename = "sof-apl-wm8804.tplg",
+ },
+ {
+ .id = "INT34C3",
+ .drv_name = "bxt_tdf8532",
+ .machine_quirk = apl_quirk,
+ .sof_tplg_filename = "sof-apl-tdf8532.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-apl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c
new file mode 100644
index 000000000000..87c44f284971
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-byt-match.c - tables and support for BYT ACPI enumeration.
+ *
+ * Copyright (c) 2017, Intel Corporation.
+ */
+
+#include <linux/dmi.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static unsigned long byt_machine_id;
+
+#define BYT_RT5672 1
+#define BYT_POV_P1006W 2
+
+static int byt_rt5672_quirk_cb(const struct dmi_system_id *id)
+{
+ byt_machine_id = BYT_RT5672;
+ return 1;
+}
+
+static int byt_pov_p1006w_quirk_cb(const struct dmi_system_id *id)
+{
+ byt_machine_id = BYT_POV_P1006W;
+ return 1;
+}
+
+static const struct dmi_system_id byt_table[] = {
+ {
+ .callback = byt_rt5672_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
+ },
+ },
+ {
+ .callback = byt_rt5672_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ },
+ {
+ .callback = byt_rt5672_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ },
+ {
+ .callback = byt_rt5672_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
+ },
+ },
+ {
+ /* Point of View mobii wintab p1006w (v1.0) */
+ .callback = byt_pov_p1006w_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+ /* Note 105b is Foxcon's USB/PCI vendor id */
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
+ },
+ },
+ {
+ /* Aegex 10 tablet (RU2) */
+ .callback = byt_rt5672_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AEGEX"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "RU2"),
+ },
+ },
+ {
+ /* Dell Venue 10 Pro 5055 */
+ .callback = byt_rt5672_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"),
+ },
+ },
+ { }
+};
+
+/* Various devices use an ACPI id of 10EC5640 while using a rt5672 codec */
+static struct snd_soc_acpi_mach byt_rt5672 = {
+ .id = "10EC5640",
+ .drv_name = "cht-bsw-rt5672",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-byt-rt5670.tplg",
+};
+
+static struct snd_soc_acpi_mach byt_pov_p1006w = {
+ .id = "10EC5640",
+ .drv_name = "bytcr_rt5651",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcr_rt5651",
+ .sof_tplg_filename = "sof-byt-rt5651.tplg",
+};
+
+static struct snd_soc_acpi_mach *byt_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+
+ dmi_check_system(byt_table);
+
+ switch (byt_machine_id) {
+ case BYT_RT5672:
+ return &byt_rt5672;
+ case BYT_POV_P1006W:
+ return &byt_pov_p1006w;
+ default:
+ return mach;
+ }
+}
+
+static const struct snd_soc_acpi_codecs rt5640_comp_ids = {
+ .num_codecs = 3,
+ .codecs = { "10EC5640", "10EC5642", "INTCCFFD"},
+};
+
+static const struct snd_soc_acpi_codecs wm5102_comp_ids = {
+ .num_codecs = 3,
+ .codecs = { "10WM5102", "WM510204", "WM510205"},
+};
+
+static const struct snd_soc_acpi_codecs da7213_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "DGLS7212", "DGLS7213"},
+};
+
+static const struct snd_soc_acpi_codecs rt5645_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "10EC5645", "10EC5648"},
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = {
+ {
+ .comp_ids = &rt5640_comp_ids,
+ .drv_name = "bytcr_rt5640",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcr_rt5640",
+ .machine_quirk = byt_quirk,
+ .sof_tplg_filename = "sof-byt-rt5640.tplg",
+ },
+ {
+ .id = "10EC5651",
+ .drv_name = "bytcr_rt5651",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcr_rt5651",
+ .sof_tplg_filename = "sof-byt-rt5651.tplg",
+ },
+ {
+ .comp_ids = &wm5102_comp_ids,
+ .drv_name = "bytcr_wm5102",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcr_wm5102",
+ .sof_tplg_filename = "sof-byt-wm5102.tplg",
+ },
+ {
+ .comp_ids = &da7213_comp_ids,
+ .drv_name = "bytcht_da7213",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcht_da7213",
+ .sof_tplg_filename = "sof-byt-da7213.tplg",
+ },
+ {
+ .id = "ESSX8316",
+ .drv_name = "bytcht_es8316",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcht_es8316",
+ .sof_tplg_filename = "sof-byt-es8316.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "sof_rt5682",
+ .sof_tplg_filename = "sof-byt-rt5682.tplg",
+ },
+ /* some Baytrail platforms rely on RT5645, use CHT machine driver */
+ {
+ .comp_ids = &rt5645_comp_ids,
+ .drv_name = "cht-bsw-rt5645",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-byt-rt5645.tplg",
+ },
+ /* use CHT driver to Baytrail Chromebooks */
+ {
+ .id = "193C9890",
+ .drv_name = "cht-bsw-max98090",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-byt-max98090.tplg",
+ },
+ {
+ .id = "14F10720",
+ .drv_name = "bytcht_cx2072x",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcht_cx2072x",
+ .sof_tplg_filename = "sof-byt-cx2072x.tplg",
+ },
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
+ /*
+ * This is always last in the table so that it is selected only when
+ * enabled explicitly and there is no codec-related information in SSDT
+ */
+ {
+ .id = "80860F28",
+ .drv_name = "bytcht_nocodec",
+ .fw_filename = "intel/fw_sst_0f28.bin",
+ .board = "bytcht_nocodec",
+ },
+#endif
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c
new file mode 100644
index 000000000000..1733dfb23e79
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-apci-intel-cfl-match.c - tables and support for CFL ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[] = {
+ {}
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
new file mode 100644
index 000000000000..e4c3492a0c28
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-cht-match.c - tables and support for CHT ACPI enumeration.
+ *
+ * Copyright (c) 2017, Intel Corporation.
+ */
+
+#include <linux/dmi.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static unsigned long cht_machine_id;
+
+#define CHT_SURFACE_MACH 1
+
+static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+{
+ cht_machine_id = CHT_SURFACE_MACH;
+ return 1;
+}
+
+static const struct dmi_system_id cht_table[] = {
+ {
+ .callback = cht_surface_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ },
+ },
+ { }
+};
+
+static struct snd_soc_acpi_mach cht_surface_mach = {
+ .id = "10EC5640",
+ .drv_name = "cht-bsw-rt5645",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-cht-rt5645.tplg",
+};
+
+static struct snd_soc_acpi_mach *cht_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+
+ dmi_check_system(cht_table);
+
+ if (cht_machine_id == CHT_SURFACE_MACH)
+ return &cht_surface_mach;
+ else
+ return mach;
+}
+
+/*
+ * Some tablets with Android factory OS have buggy DSDTs with an ESSX8316 device
+ * in the ACPI tables. While they are not using an ESS8316 codec. These DSDTs
+ * also have an ACPI device for the correct codec, ignore the ESSX8316.
+ */
+static const struct dmi_system_id cht_ess8316_not_present_table[] = {
+ {
+ /* Nextbook Ares 8A */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+ DMI_MATCH(DMI_BIOS_VERSION, "M882"),
+ },
+ },
+ { }
+};
+
+static struct snd_soc_acpi_mach *cht_ess8316_quirk(void *arg)
+{
+ if (dmi_check_system(cht_ess8316_not_present_table))
+ return NULL;
+
+ return arg;
+}
+
+/*
+ * The Lenovo Yoga Tab 3 Pro YT3-X90, with Android factory OS has a buggy DSDT
+ * with the coded not being listed at all.
+ */
+static const struct dmi_system_id lenovo_yoga_tab3_x90[] = {
+ {
+ /* Lenovo Yoga Tab 3 Pro YT3-X90, codec missing from DSDT */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
+ },
+ },
+ { }
+};
+
+static struct snd_soc_acpi_mach cht_lenovo_yoga_tab3_x90_mach = {
+ .id = "10WM5102",
+ .drv_name = "bytcr_wm5102",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcr_wm5102",
+ .sof_tplg_filename = "sof-cht-wm5102.tplg",
+};
+
+static struct snd_soc_acpi_mach *lenovo_yt3_x90_quirk(void *arg)
+{
+ if (dmi_check_system(lenovo_yoga_tab3_x90))
+ return &cht_lenovo_yoga_tab3_x90_mach;
+
+ /* Skip wildcard match snd_soc_acpi_intel_cherrytrail_machines[] entry */
+ return NULL;
+}
+
+static const struct snd_soc_acpi_codecs rt5640_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "10EC5640", "10EC3276" },
+};
+
+static const struct snd_soc_acpi_codecs rt5670_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "10EC5670", "10EC5672" },
+};
+
+static const struct snd_soc_acpi_codecs rt5645_comp_ids = {
+ .num_codecs = 3,
+ .codecs = { "10EC5645", "10EC5650", "10EC3270" },
+};
+
+static const struct snd_soc_acpi_codecs da7213_comp_ids = {
+ .num_codecs = 2,
+ .codecs = { "DGLS7212", "DGLS7213"},
+
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = {
+ {
+ .comp_ids = &rt5670_comp_ids,
+ .drv_name = "cht-bsw-rt5672",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-cht-rt5670.tplg",
+ },
+ {
+ .comp_ids = &rt5645_comp_ids,
+ .drv_name = "cht-bsw-rt5645",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-cht-rt5645.tplg",
+ },
+ {
+ .id = "193C9890",
+ .drv_name = "cht-bsw-max98090",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-cht-max98090.tplg",
+ },
+ {
+ .id = "10508824",
+ .drv_name = "cht-bsw-nau8824",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "cht-bsw",
+ .sof_tplg_filename = "sof-cht-nau8824.tplg",
+ },
+ {
+ .comp_ids = &da7213_comp_ids,
+ .drv_name = "bytcht_da7213",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcht_da7213",
+ .sof_tplg_filename = "sof-cht-da7213.tplg",
+ },
+ {
+ .id = "ESSX8316",
+ .drv_name = "bytcht_es8316",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcht_es8316",
+ .machine_quirk = cht_ess8316_quirk,
+ .sof_tplg_filename = "sof-cht-es8316.tplg",
+ },
+ /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
+ {
+ .comp_ids = &rt5640_comp_ids,
+ .drv_name = "bytcr_rt5640",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcr_rt5640",
+ .machine_quirk = cht_quirk,
+ .sof_tplg_filename = "sof-cht-rt5640.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "sof_rt5682",
+ .sof_tplg_filename = "sof-cht-rt5682.tplg",
+ },
+ /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */
+ {
+ .id = "10EC5651",
+ .drv_name = "bytcr_rt5651",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcr_rt5651",
+ .sof_tplg_filename = "sof-cht-rt5651.tplg",
+ },
+ {
+ .id = "14F10720",
+ .drv_name = "bytcht_cx2072x",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcht_cx2072x",
+ .sof_tplg_filename = "sof-cht-cx2072x.tplg",
+ },
+ {
+ .id = "104C5122",
+ .drv_name = "sof_pcm512x",
+ .sof_tplg_filename = "sof-cht-src-50khz-pcm512x.tplg",
+ },
+ /*
+ * Special case for the Lenovo Yoga Tab 3 Pro YT3-X90 where the DSDT
+ * misses the codec. Match on the SST id instead, lenovo_yt3_x90_quirk()
+ * will return a YT3 specific mach or NULL when called on other hw,
+ * skipping this entry.
+ */
+ {
+ .id = "808622A8",
+ .machine_quirk = lenovo_yt3_x90_quirk,
+ },
+
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
+ /*
+ * This is always last in the table so that it is selected only when
+ * enabled explicitly and there is no codec-related information in SSDT
+ */
+ {
+ .id = "808622A8",
+ .drv_name = "bytcht_nocodec",
+ .fw_filename = "intel/fw_sst_22a8.bin",
+ .board = "bytcht_nocodec",
+ },
+#endif
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cherrytrail_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
new file mode 100644
index 000000000000..f79d7558174a
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-cml-match.c - tables and support for CML ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs rt1011_spk_codecs = {
+ .num_codecs = 1,
+ .codecs = {"10EC1011"}
+};
+
+static const struct snd_soc_acpi_codecs rt1015_spk_codecs = {
+ .num_codecs = 1,
+ .codecs = {"10EC1015"}
+};
+
+static const struct snd_soc_acpi_codecs max98357a_spk_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+static const struct snd_soc_acpi_codecs max98390_spk_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98390"}
+};
+
+/*
+ * The order of the three entries with .id = "10EC5682" matters
+ * here, because DSDT tables expose an ACPI HID for the MAX98357A
+ * speaker amplifier which is not populated on the board.
+ */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "cml_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rt1011_spk_codecs,
+ .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "cml_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rt1015_spk_codecs,
+ .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "cml_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &max98357a_spk_codecs,
+ .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "cml_rt5682_def",
+ .sof_tplg_filename = "sof-cml-rt5682.tplg",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "cml_da7219_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &max98357a_spk_codecs,
+ .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "cml_da7219_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &max98390_spk_codecs,
+ .sof_tplg_filename = "sof-cml-da7219-max98390.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-cml-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines);
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_adr_device rt700_1_adr[] = {
+ {
+ .adr = 0x000110025D070000ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt700"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr cml_rvp[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt700_1_adr),
+ .adr_d = rt700_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = {
+ {
+ .adr = 0x000220025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1308-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt715_3_adr[] = {
+ {
+ .adr = 0x000320025D071500ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt715"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
+ {
+ .adr = 0x000131025D131601ull, /* unique ID is set for some reason */
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_3_adr[] = {
+ {
+ .adr = 0x000330025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr cml_3_in_1_default[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_group1_adr),
+ .adr_d = rt1308_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1308_2_group1_adr),
+ .adr_d = rt1308_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr cml_3_in_1_mono_amp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_single_adr),
+ .adr_d = rt1308_1_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr cml_3_in_1_sdca[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = {
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = cml_3_in_1_default,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = cml_3_in_1_sdca,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cml-rt711-rt1316-rt714.tplg",
+ },
+ {
+ /*
+ * link_mask should be 0xB, but all links are enabled by BIOS.
+ * This entry will be selected if there is no rt1308 exposed
+ * on link2 since it will fail to match the above entry.
+ */
+ .link_mask = 0xF,
+ .links = cml_3_in_1_mono_amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = 0x2, /* RT700 connected on Link1 */
+ .links = cml_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cml-rt700.tplg",
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c
new file mode 100644
index 000000000000..8bbb1052faf2
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-cnl-match.c - tables and support for CNL ACPI enumeration.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = {
+ {
+ .id = "INT34C2",
+ .drv_name = "cnl_rt274",
+ .fw_filename = "intel/dsp_fw_cnl.bin",
+ .sof_tplg_filename = "sof-cnl-rt274.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ /* cnl and cml are identical */
+ .sof_tplg_filename = "sof-cml-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines);
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_adr_device rt5682_2_adr[] = {
+ {
+ .adr = 0x000220025D568200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt5682"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr up_extreme_rt5682_2[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt5682_2_adr),
+ .adr_d = rt5682_2_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[] = {
+ {
+ .link_mask = BIT(2),
+ .links = up_extreme_rt5682_2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cnl-rt5682-sdw2.tplg"
+ },
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg",
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-ehl-match.c b/sound/soc/intel/common/soc-acpi-intel-ehl-match.c
new file mode 100644
index 000000000000..78255d56b08c
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-ehl-match.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-ehl-match.c - tables and support for EHL ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[] = {
+ {
+ .id = "10EC5660",
+ .drv_name = "ehl_rt5660",
+ .sof_tplg_filename = "sof-ehl-rt5660.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ehl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
new file mode 100644
index 000000000000..c82c8c93d200
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-glk-match.c - tables and support for GLK ACPI enumeration.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs glk_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+static const struct snd_soc_acpi_codecs glk_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {"10EC5682", "RTL5682"},
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "glk_alc298s_i2s",
+ .fw_filename = "intel/dsp_fw_glk.bin",
+ .sof_tplg_filename = "sof-glk-alc298.tplg",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "glk_da7219_def",
+ .fw_filename = "intel/dsp_fw_glk.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &glk_codecs,
+ .sof_tplg_filename = "sof-glk-da7219.tplg",
+ },
+ {
+ .comp_ids = &glk_rt5682_rt5682s_hp,
+ .drv_name = "glk_rt5682_def",
+ .fw_filename = "intel/dsp_fw_glk.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &glk_codecs,
+ .sof_tplg_filename = "sof-glk-rt5682.tplg",
+ },
+ {
+ .id = "10134242",
+ .drv_name = "glk_cs4242_mx98357a",
+ .fw_filename = "intel/dsp_fw_glk.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &glk_codecs,
+ .sof_tplg_filename = "sof-glk-cs42l42.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-glk-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-hda-match.c b/sound/soc/intel/common/soc-acpi-intel-hda-match.c
new file mode 100644
index 000000000000..e93336e27beb
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-hda-match.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018, Intel Corporation.
+
+/*
+ * soc-acpi-intel-hda-match.c - tables and support for HDA+ACPI enumeration.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[] = {
+ {
+ /* .id is not used in this file */
+ .drv_name = "skl_hda_dsp_generic",
+ .sof_tplg_filename = "sof-hda-generic", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_hda_machines);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Common ACPI Match module");
diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
new file mode 100644
index 000000000000..6daf60b1edf1
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-hsw-bdw-match.c - tables and support for ACPI enumeration.
+ *
+ * Copyright (c) 2017, Intel Corporation.
+ */
+
+#include <linux/dmi.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "bdw_rt286",
+ .sof_tplg_filename = "sof-bdw-rt286.tplg",
+ },
+ {
+ .id = "10EC5650",
+ .drv_name = "bdw-rt5650",
+ .sof_tplg_filename = "sof-bdw-rt5650.tplg",
+ },
+ {
+ .id = "RT5677CE",
+ .drv_name = "bdw-rt5677",
+ .sof_tplg_filename = "sof-bdw-rt5677.tplg",
+ },
+ {
+ .id = "INT33CA",
+ .drv_name = "hsw_rt5640",
+ .sof_tplg_filename = "sof-bdw-rt5640.tplg",
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_broadwell_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c
new file mode 100644
index 000000000000..6ce75fbb842e
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-icl-match.c - tables and support for ICL ACPI enumeration.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = {
+ {
+ .id = "INT34C2",
+ .drv_name = "icl_rt274",
+ .fw_filename = "intel/dsp_fw_icl.bin",
+ .sof_tplg_filename = "sof-icl-rt274.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "icl_rt5682_def",
+ .sof_tplg_filename = "sof-icl-rt5682.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-icl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines);
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_adr_device rt700_0_adr[] = {
+ {
+ .adr = 0x000010025D070000ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt700"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr icl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt700_0_adr),
+ .adr_d = rt700_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = {
+ {
+ .adr = 0x000220025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1308-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt715_3_adr[] = {
+ {
+ .adr = 0x000320025D071500ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt715"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr icl_3_in_1_default[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_group1_adr),
+ .adr_d = rt1308_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1308_2_group1_adr),
+ .adr_d = rt1308_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr icl_3_in_1_mono_amp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_adr),
+ .adr_d = rt1308_1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[] = {
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = icl_3_in_1_default,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = 0xB, /* 3 active links required */
+ .links = icl_3_in_1_mono_amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715-mono.tplg",
+ },
+ {
+ .link_mask = 0x1, /* rt700 connected on link0 */
+ .links = icl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-icl-rt700.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
new file mode 100644
index 000000000000..d4b397c53bcc
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-apci-intel-jsl-match.c - tables and support for JSL ACPI enumeration.
+ *
+ * Copyright (c) 2019-2020, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs mx98373_spk = {
+ .num_codecs = 1,
+ .codecs = {"MX98373"}
+};
+
+static const struct snd_soc_acpi_codecs rt1015_spk = {
+ .num_codecs = 1,
+ .codecs = {"10EC1015"}
+};
+
+static const struct snd_soc_acpi_codecs rt1015p_spk = {
+ .num_codecs = 1,
+ .codecs = {"RTL1015"}
+};
+
+static const struct snd_soc_acpi_codecs mx98360a_spk = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+static struct snd_soc_acpi_codecs rt5650_spk = {
+ .num_codecs = 1,
+ .codecs = {"10EC5650"}
+};
+
+static const struct snd_soc_acpi_codecs rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {"10EC5682", "RTL5682"},
+};
+
+/*
+ * When adding new entry to the snd_soc_acpi_intel_jsl_machines array,
+ * use .quirk_data member to distinguish different machine driver,
+ * and keep ACPI .id field unchanged for the common codec.
+ */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
+ {
+ .id = "DLGS7219",
+ .drv_name = "jsl_da7219_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mx98373_spk,
+ .sof_tplg_filename = "sof-jsl-da7219.tplg",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "jsl_da7219_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mx98360a_spk,
+ .sof_tplg_filename = "sof-jsl-da7219-mx98360a.tplg",
+ },
+ {
+ .comp_ids = &rt5682_rt5682s_hp,
+ .drv_name = "jsl_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rt1015_spk,
+ .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg",
+ },
+ {
+ .comp_ids = &rt5682_rt5682s_hp,
+ .drv_name = "jsl_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rt1015p_spk,
+ .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg",
+ },
+ {
+ .comp_ids = &rt5682_rt5682s_hp,
+ .drv_name = "jsl_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mx98360a_spk,
+ .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg",
+ },
+ {
+ .comp_ids = &rt5682_rt5682s_hp,
+ .drv_name = "jsl_rt5682_def",
+ .sof_tplg_filename = "sof-jsl-rt5682.tplg",
+ },
+ {
+ .id = "10134242",
+ .drv_name = "jsl_cs4242_mx98360a",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mx98360a_spk,
+ .sof_tplg_filename = "sof-jsl-cs42l42-mx98360a.tplg",
+ },
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-jsl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {
+ .id = "10EC5650",
+ .drv_name = "jsl_rt5682_def",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rt5650_spk,
+ .sof_tplg_filename = "sof-jsl-rt5650.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
new file mode 100644
index 000000000000..d4c158d8441b
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-kbl-match.c - tables and support for KBL ACPI enumeration.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_codecs kbl_codecs = {
+ .num_codecs = 1,
+ .codecs = {"10508825"}
+};
+
+static const struct snd_soc_acpi_codecs kbl_poppy_codecs = {
+ .num_codecs = 1,
+ .codecs = {"10EC5663"}
+};
+
+static const struct snd_soc_acpi_codecs kbl_5663_5514_codecs = {
+ .num_codecs = 2,
+ .codecs = {"10EC5663", "10EC5514"}
+};
+
+static const struct snd_soc_acpi_codecs kbl_7219_98357_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+static const struct snd_soc_acpi_codecs kbl_7219_98927_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98927"}
+};
+
+static const struct snd_soc_acpi_codecs kbl_7219_98373_codecs = {
+ .num_codecs = 1,
+ .codecs = {"MX98373"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "kbl_alc286s_i2s",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ },
+ {
+ .id = "INT343B",
+ .drv_name = "kbl_n88l25_s4567",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_codecs,
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "kbl_n88l25_m98357a",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_codecs,
+ },
+ {
+ .id = "MX98927",
+ .drv_name = "kbl_r5514_5663_max",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_5663_5514_codecs,
+ },
+ {
+ .id = "MX98927",
+ .drv_name = "kbl_rt5663_m98927",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_poppy_codecs,
+ },
+ {
+ .id = "10EC5663",
+ .drv_name = "kbl_rt5663",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "kbl_da7219_mx98357a",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_7219_98357_codecs,
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "kbl_da7219_max98927",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_7219_98927_codecs,
+ },
+ {
+ .id = "10EC5660",
+ .drv_name = "kbl_rt5660",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ },
+ {
+ .id = "10EC3277",
+ .drv_name = "kbl_rt5660",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "kbl_da7219_mx98373",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &kbl_7219_98373_codecs,
+ },
+ {
+ .id = "MX98373",
+ .drv_name = "kbl_max98373",
+ .fw_filename = "intel/dsp_fw_kbl.bin",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
new file mode 100644
index 000000000000..937a74a5d523
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
@@ -0,0 +1,801 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-lnl-match.c - tables and support for LNL ACPI enumeration.
+ *
+ * Copyright (c) 2023, Intel Corporation
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "sof-function-topology-lib.h"
+#include "soc-acpi-intel-sdca-quirks.h"
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_machines);
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_1_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_4_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_5_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 5,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_6_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 6,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+/*
+ * RT722 is a multi-function codec, three endpoints are created for
+ * its headset, amp and dmic functions.
+ */
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp Endpoint, work as spk_l_endpoint */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_amp_spkagg_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_l_adr[] = {
+ {
+ .adr = 0x00023001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00023101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_r_adr[] = {
+ {
+ .adr = 0x00033201fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00033301fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_lr_adr[] = {
+ {
+ .adr = 0x00033001fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00033101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_1_3amp_adr[] = {
+ {
+ .adr = 0x00013001fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_1_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00013201fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP3"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_3amp_adr[] = {
+ {
+ .adr = 0x00033301fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_4_endpoint,
+ .name_prefix = "AMP4"
+ },
+ {
+ .adr = 0x00033401fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_5_endpoint,
+ .name_prefix = "AMP5"
+ },
+ {
+ .adr = 0x00033501fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_6_endpoint,
+ .name_prefix = "AMP6"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+ {
+ .adr = 0x00003001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_2_adr[] = {
+ {
+ .adr = 0x00023001fa424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_amp_spkagg_endpoints),
+ .endpoints = cs42l43_amp_spkagg_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_2_single_adr[] = {
+ {
+ .adr = 0x000230025D071201ull,
+ .num_endpoints = ARRAY_SIZE(rt712_endpoints),
+ .endpoints = rt712_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = {
+ {
+ .adr = 0x000330025D171201ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt712-dmic"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D071201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
+ {
+ .adr = 0x000331025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_1_adr[] = {
+ {
+ .adr = 0x000133025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1318-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = {
+ {
+ .adr = 0x000130025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1318-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_2_group1_adr[] = {
+ {
+ .adr = 0x000232025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1318-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_2_group2_adr[] = {
+ {
+ .adr = 0x000231025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_3_group2_adr[] = {
+ {
+ .adr = 0x000330025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt713_0_adr[] = {
+ {
+ .adr = 0x000031025D071301ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt713"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt713_vb_2_adr[] = {
+ {
+ .adr = 0x000230025d071301ull,
+ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
+ .endpoints = jack_dmic_endpoints,
+ .name_prefix = "rt713"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_0_adr[] = {
+ {
+ .adr = 0x000030025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_1_adr[] = {
+ {
+ .adr = 0x000130025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr lnl_cs42l43_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_cs42l43_l0_cs35l56_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_lr_adr),
+ .adr_d = cs35l56_3_lr_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_cs42l43_l0_cs35l56_l23[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_l_adr),
+ .adr_d = cs35l56_2_l_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_r_adr),
+ .adr_d = cs35l56_3_r_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_cs42l43_l2_cs35l56x6_l13[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs42l43_2_adr),
+ .adr_d = cs42l43_2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_1_3amp_adr),
+ .adr_d = cs35l56_1_3amp_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_3amp_adr),
+ .adr_d = cs35l56_3_3amp_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_712_only[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt712_2_single_adr),
+ .adr_d = rt712_2_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1712_3_single_adr),
+ .adr_d = rt1712_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_3_in_1_sdca[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_group1_adr),
+ .adr_d = rt1316_3_group1_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_1_adr),
+ .adr_d = rt714_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_sdw_rt1318_l12_rt714_l0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_group1_adr),
+ .adr_d = rt1318_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1318_2_group1_adr),
+ .adr_d = rt1318_2_group1_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_sdw_rt1320_l12_rt714_l0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
+ .adr_d = rt1320_1_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1320_2_group2_adr),
+ .adr_d = rt1320_2_group2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_l0_rt1318_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt713_0_adr),
+ .adr_d = rt713_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_adr),
+ .adr_d = rt1318_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_vb_l2_rt1320_l13[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt713_vb_2_adr),
+ .adr_d = rt713_vb_2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
+ .adr_d = rt1320_1_group2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1320_3_group2_adr),
+ .adr_d = rt1320_3_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr lnl_sdw_rt712_vb_l2_rt1320_l1[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt712_vb_2_group1_adr),
+ .adr_d = rt712_vb_2_group1_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group1_adr),
+ .adr_d = rt1320_1_group1_adr,
+ },
+ {}
+};
+
+/* this table is used when there is no I2S codec present */
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
+ /* mockup tests need to be first */
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = sdw_mockup_mic_headset_1amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = sdw_mockup_multi_func,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", /* Reuse the existing tplg file */
+ },
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = lnl_3_in_1_sdca,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = lnl_cs42l43_l0_cs35l56_l23,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg",
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = lnl_cs42l43_l2_cs35l56x6_l13,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(3),
+ .links = lnl_cs42l43_l0_cs35l56_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = lnl_cs42l43_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = lnl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711.tplg",
+ },
+ {
+ .link_mask = BIT(2) | BIT(3),
+ .links = lnl_712_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = lnl_rt722_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt722-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = lnl_sdw_rt1318_l12_rt714_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg"
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = lnl_sdw_rt1320_l12_rt714_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg"
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = lnl_sdw_rt713_l0_rt1318_l1,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg"
+ },
+ {
+ .link_mask = BIT(1) | BIT(2),
+ .links = lnl_sdw_rt712_vb_l2_rt1320_l1,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = lnl_sdw_rt713_vb_l2_rt1320_l13,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
new file mode 100644
index 000000000000..ec9fd8486c05
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -0,0 +1,1246 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-mtl-match.c - tables and support for MTL ACPI enumeration.
+ *
+ * Copyright (c) 2022, Intel Corporation.
+ *
+ */
+
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/sdca.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+#include "sof-function-topology-lib.h"
+#include "soc-acpi-intel-sdca-quirks.h"
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+static const struct snd_soc_acpi_codecs mtl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
+static const struct snd_soc_acpi_codecs mtl_essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs mtl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = {
+ {
+ .comp_ids = &mtl_essx_83x6,
+ .drv_name = "mtl_es83x6_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mtl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-mtl-es83x6-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &mtl_essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-mtl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ {
+ .comp_ids = &mtl_rt5682_rt5682s_hp,
+ .drv_name = "mtl_rt5682_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mtl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-mtl-rt5682-ssp1-hdmi-ssp02.tplg",
+ },
+ /* place boards for each headphone codec: sof driver will complete the
+ * tplg name and machine driver will detect the amp type
+ */
+ {
+ .id = CS42L42_ACPI_HID,
+ .drv_name = "mtl_cs42l42_def",
+ .sof_tplg_filename = "sof-mtl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = DA7219_ACPI_HID,
+ .drv_name = "mtl_da7219_def",
+ .sof_tplg_filename = "sof-mtl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = NAU8825_ACPI_HID,
+ .drv_name = "mtl_nau8825_def",
+ .sof_tplg_filename = "sof-mtl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = RT5650_ACPI_HID,
+ .drv_name = "mtl_rt5682_def",
+ .sof_tplg_filename = "sof-mtl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .comp_ids = &mtl_rt5682_rt5682s_hp,
+ .drv_name = "mtl_rt5682_def",
+ .sof_tplg_filename = "sof-mtl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ /* place amp-only boards in the end of table */
+ {
+ .id = "INTC10B0",
+ .drv_name = "mtl_lt6911_hdmi_ssp",
+ .sof_tplg_filename = "sof-mtl-hdmi-ssp02.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_machines);
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint rt712_vb_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+/*
+ * RT722 is a multi-function codec, three endpoints are created for
+ * its headset, amp and dmic functions.
+ */
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = {
+ {
+ .adr = 0x000030025D071201ull,
+ .num_endpoints = ARRAY_SIZE(rt712_endpoints),
+ .endpoints = rt712_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_vb_0_single_adr[] = {
+ {
+ .adr = 0x000030025D071201ull,
+ .num_endpoints = ARRAY_SIZE(rt712_vb_endpoints),
+ .endpoints = rt712_vb_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = {
+ {
+ .adr = 0x000330025D171201ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt712-dmic"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt713_0_single_adr[] = {
+ {
+ .adr = 0x000031025D071301ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt713"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1713_3_single_adr[] = {
+ {
+ .adr = 0x000331025D171301ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt713-dmic"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device mx8373_0_adr[] = {
+ {
+ .adr = 0x000023019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "Left"
+ },
+ {
+ .adr = 0x000027019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "Right"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt5682_2_adr[] = {
+ {
+ .adr = 0x000221025D568200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt5682"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
+ {
+ .adr = 0x000331025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
+ {
+ .adr = 0x000131025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group2_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = {
+ {
+ .adr = 0x000330025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_1_single_adr[] = {
+ {
+ .adr = 0x000130025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1318-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = {
+ {
+ .adr = 0x000130025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1318-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_2_group1_adr[] = {
+ {
+ .adr = 0x000232025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1318-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_0_adr[] = {
+ {
+ .adr = 0x000030025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_1_adr[] = {
+ {
+ .adr = 0x000130025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr mtl_712_l0_1712_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt712_0_single_adr),
+ .adr_d = rt712_0_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1712_3_single_adr),
+ .adr_d = rt1712_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_712_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt712_0_single_adr),
+ .adr_d = rt712_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_712_vb_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt712_vb_0_single_adr),
+ .adr_d = rt712_vb_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+ {
+ .adr = 0x00003001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+/* CS42L43 - speaker DAI aggregated with 4 amps */
+static const struct snd_soc_acpi_endpoint cs42l43_4amp_spkagg_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 1,
+ },
+};
+
+/* CS42L43 on link3 aggregated with 4 amps */
+static const struct snd_soc_acpi_adr_device cs42l43_l3_4amp_spkagg_adr[] = {
+ {
+ .adr = 0x00033001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_4amp_spkagg_endpoints),
+ .endpoints = cs42l43_4amp_spkagg_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_l_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_r_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_2_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_3_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_4_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_5_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 5,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 5,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_6_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 6,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 6,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 7,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 7,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_0_adr[] = {
+ {
+ .adr = 0x00003301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = {
+ {
+ .adr = 0x00013701FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00013601FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = {
+ {
+ .adr = 0x00023301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00023201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_0_fb_adr[] = {
+ {
+ .adr = 0x00003301FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
+ .endpoints = cs35l56_l_fb_endpoints,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
+ .endpoints = cs35l56_2_fb_endpoints,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00003101FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints),
+ .endpoints = cs35l56_4_fb_endpoints,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00003001FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints),
+ .endpoints = cs35l56_6_fb_endpoints,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_1_fb_adr[] = {
+ {
+ .adr = 0x00013701FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
+ .endpoints = cs35l56_r_fb_endpoints,
+ .name_prefix = "AMP8"
+ },
+ {
+ .adr = 0x00013601FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
+ .endpoints = cs35l56_3_fb_endpoints,
+ .name_prefix = "AMP7"
+ },
+ {
+ .adr = 0x00013501FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints),
+ .endpoints = cs35l56_5_fb_endpoints,
+ .name_prefix = "AMP6"
+ },
+ {
+ .adr = 0x00013401FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints),
+ .endpoints = cs35l56_7_fb_endpoints,
+ .name_prefix = "AMP5"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = {
+ {
+ .adr = 0x00023201FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
+ .endpoints = cs35l56_r_fb_endpoints,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00023301FA355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
+ .endpoints = cs35l56_3_fb_endpoints,
+ .name_prefix = "AMP4"
+ }
+
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = {
+ {
+ .adr = 0x00033001fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
+ .endpoints = cs35l56_l_fb_endpoints,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00033101fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
+ .endpoints = cs35l56_2_fb_endpoints,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63_1_fb_adr[] = {
+ {
+ .adr = 0x00013001FA356301ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
+ .endpoints = cs35l56_l_fb_endpoints,
+ .name_prefix = "AMP1"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63_3_fb_adr[] = {
+ {
+ .adr = 0x00033101FA356301ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
+ .endpoints = cs35l56_r_fb_endpoints,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = {
+ /* Expected order: jack -> amp */
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt5682_2_adr),
+ .adr_d = rt5682_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(mx8373_0_adr),
+ .adr_d = mx8373_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_group1_adr),
+ .adr_d = rt1316_3_group1_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_1_adr),
+ .adr_d = rt714_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_sdw_rt1318_l12_rt714_l0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_group1_adr),
+ .adr_d = rt1318_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1318_2_group1_adr),
+ .adr_d = rt1318_2_group1_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1316_l12_rt1713_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt713_0_single_adr),
+ .adr_d = rt713_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group2_adr),
+ .adr_d = rt1316_2_group2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1713_3_single_adr),
+ .adr_d = rt1713_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1318_l1_rt1713_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt713_0_single_adr),
+ .adr_d = rt713_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_single_adr),
+ .adr_d = rt1318_1_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1713_3_single_adr),
+ .adr_d = rt1713_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1318_l12_rt1713_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt713_0_single_adr),
+ .adr_d = rt713_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_group1_adr),
+ .adr_d = rt1318_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1318_2_group1_adr),
+ .adr_d = rt1318_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1713_3_single_adr),
+ .adr_d = rt1713_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1316_l12[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt713_0_single_adr),
+ .adr_d = rt713_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group2_adr),
+ .adr_d = rt1316_2_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_rt711_l0_rt1316_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_single_adr),
+ .adr_d = rt1316_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = {
+ {
+ .adr = 0x000230019F836300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "Left"
+ },
+ {
+ .adr = 0x000231019F836300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "Right"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = {
+ {
+ .adr = 0x00001001FA424200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "cs42l42"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = {
+ {
+ .adr = 0x0000380102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "tas2783-1"
+ },
+ {
+ .adr = 0x0000390102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "tas2783-2"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr tas2783_link0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(tas2783_0_adr),
+ .adr_d = tas2783_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr cs42l42_link0_max98363_link2[] = {
+ /* Expected order: jack -> amp */
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l42_0_adr),
+ .adr_d = cs42l42_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(mx8363_2_adr),
+ .adr_d = mx8363_2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_cs42l43_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_cs42l43_cs35l56[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_1_adr),
+ .adr_d = cs35l56_1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_adr),
+ .adr_d = cs35l56_2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr cs42l43_link0_cs35l56_link2_link3[] = {
+ /* Expected order: jack -> amp */
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_r_adr),
+ .adr_d = cs35l56_2_r_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_l_adr),
+ .adr_d = cs35l56_3_l_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr cs42l43_link3_cs35l56_x4_link0_link1_spkagg[] = {
+ /* Expected order: jack -> amp */
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs42l43_l3_4amp_spkagg_adr),
+ .adr_d = cs42l43_l3_4amp_spkagg_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = 2,
+ .adr_d = cs35l56_1_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = 2,
+ .adr_d = cs35l56_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_cs35l56_x8_link0_link1_fb[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_1_fb_adr),
+ .adr_d = cs35l56_1_fb_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l56_0_fb_adr),
+ .adr_d = cs35l56_0_fb_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr mtl_cs35l63_x2_link1_link3_fb[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l63_3_fb_adr),
+ .adr_d = cs35l63_3_fb_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l63_1_fb_adr),
+ .adr_d = cs35l63_1_fb_adr,
+ },
+ {}
+};
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
+ /* mockup tests need to be first */
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = sdw_mockup_mic_headset_1amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = tas2783_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-tas2783.tplg",
+ },
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = mtl_rt713_l0_rt1316_l12_rt1713_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg",
+ },
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = mtl_rt713_l0_rt1318_l12_rt1713_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = mtl_rt713_l0_rt1318_l1_rt1713_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg",
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = mtl_rt713_l0_rt1316_l12,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg",
+ },
+ {
+ .link_mask = BIT(3) | BIT(0),
+ .links = mtl_712_l0_1712_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = mtl_712_vb_l0,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-mtl-rt712-vb-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = mtl_712_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt712-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = mtl_sdw_rt1318_l12_rt714_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg"
+ },
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = cs42l43_link0_cs35l56_link2_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = mtl_cs42l43_cs35l56,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = mtl_cs35l56_x8_link0_link1_fb,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = mtl_cs42l43_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs42l43-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(3),
+ .links = mtl_cs35l63_x2_link1_link3_fb,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg",
+ },
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = mtl_3_in_1_sdca,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg",
+ },
+ {
+ .link_mask = 0x9, /* 2 active links required */
+ .links = mtl_rt711_l0_rt1316_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = mtl_rt722_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt722-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = mtl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt711.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(2),
+ .links = rt5682_link2_max98373_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(2),
+ .links = cs42l42_link0_max98363_link2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_sdw_machines);
+
+MODULE_IMPORT_NS("SND_SOC_ACPI_INTEL_SDCA_QUIRKS");
diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c
new file mode 100644
index 000000000000..2768dd10aaa0
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-nvl-match.c - tables and support for NVL ACPI enumeration.
+ *
+ * Copyright (c) 2025, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines);
+
+/*
+ * Multi-function codecs with three endpoints created for
+ * headset, amp and dmic functions.
+ */
+static const struct snd_soc_acpi_endpoint rt_mf_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_3_single_adr[] = {
+ {
+ .adr = 0x000330025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr nvl_rt722_l3[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt722_3_single_adr),
+ .adr_d = rt722_3_single_adr,
+ },
+ {}
+};
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_sdw_machines[] = {
+ /* mockup tests need to be first */
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-nvl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-nvl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = sdw_mockup_mic_headset_1amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-nvl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = BIT(3),
+ .links = nvl_rt722_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-nvl-rt722.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
new file mode 100644
index 000000000000..060955825fe0
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
@@ -0,0 +1,773 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-ptl-match.c - tables and support for PTL ACPI enumeration.
+ *
+ * Copyright (c) 2024, Intel Corporation.
+ *
+ * Order of entries in snd_soc_acpi_intel_ptl_sdw_machines[] matters.
+ * Check subset of link mask when matching the machine driver, rule is
+ * superset match should be ordered before subset matches.
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "sof-function-topology-lib.h"
+#include "soc-acpi-intel-sdca-quirks.h"
+#include "soc-acpi-intel-sdw-mockup-match.h"
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+static const struct snd_soc_acpi_codecs ptl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
+static const struct snd_soc_acpi_codecs ptl_essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs ptl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = {
+ {
+ .comp_ids = &ptl_rt5682_rt5682s_hp,
+ .drv_name = "ptl_rt5682_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &ptl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-ptl-rt5682-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &ptl_rt5682_rt5682s_hp,
+ .drv_name = "ptl_rt5682_def",
+ .sof_tplg_filename = "sof-ptl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .comp_ids = &ptl_essx_83x6,
+ .drv_name = "ptl_es83x6_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &ptl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-ptl-es83x6-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &ptl_essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-ptl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ /* place amp-only boards in the end of table */
+ {
+ .id = "INTC10B0",
+ .drv_name = "ptl_lt6911_hdmi_ssp",
+ .sof_tplg_filename = "sof-ptl-hdmi-ssp02.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_machines);
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_1_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_4_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_5_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 5,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_6_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 6,
+ .group_id = 1,
+};
+
+/*
+ * Multi-function codecs with three endpoints created for
+ * headset, amp and dmic functions.
+ */
+static const struct snd_soc_acpi_endpoint rt_mf_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp Endpoint, work as spk_l_endpoint */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_amp_spkagg_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_2_adr[] = {
+ {
+ .adr = 0x00023001fa424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_amp_spkagg_endpoints),
+ .endpoints = cs42l43_amp_spkagg_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_3_agg_adr[] = {
+ {
+ .adr = 0x00033001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_amp_spkagg_endpoints),
+ .endpoints = cs42l43_amp_spkagg_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_2_lr_adr[] = {
+ {
+ .adr = 0x00023001fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00023101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_1_3amp_adr[] = {
+ {
+ .adr = 0x00013001fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_1_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013101fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00013201fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP3"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_3_3amp_adr[] = {
+ {
+ .adr = 0x00033301fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_4_endpoint,
+ .name_prefix = "AMP4"
+ },
+ {
+ .adr = 0x00033401fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_5_endpoint,
+ .name_prefix = "AMP5"
+ },
+ {
+ .adr = 0x00033501fa355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_6_endpoint,
+ .name_prefix = "AMP6"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D071201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_vb_3_group1_adr[] = {
+ {
+ .adr = 0x000330025D071201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt713_vb_2_adr[] = {
+ {
+ .adr = 0x000230025d071301ull,
+ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
+ .endpoints = jack_dmic_endpoints,
+ .name_prefix = "rt713"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt713_vb_3_adr[] = {
+ {
+ .adr = 0x000330025D071301ull,
+ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
+ .endpoints = jack_dmic_endpoints,
+ .name_prefix = "rt713"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = {
+ {
+ .adr = 0x000330025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072101ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt721"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = {
+ {
+ .adr = 0x000330025d072101ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt721"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rt721_l3[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt721_3_single_adr),
+ .adr_d = rt721_3_single_adr,
+ },
+ {},
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt722_1_single_adr[] = {
+ {
+ .adr = 0x000130025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt722_3_single_adr[] = {
+ {
+ .adr = 0x000330025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_2_group2_adr[] = {
+ {
+ .adr = 0x000230025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_2_group2_l_adr[] = {
+ {
+ .adr = 0x000230025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_3_group2_adr[] = {
+ {
+ .adr = 0x000330025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-2"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr ptl_cs42l43_agg_l3_cs35l56_l2[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs42l43_3_agg_adr),
+ .adr_d = cs42l43_3_agg_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs35l56_2_lr_adr),
+ .adr_d = cs35l56_2_lr_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_cs42l43_l2_cs35l56x6_l13[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(cs42l43_2_adr),
+ .adr_d = cs42l43_2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_1_3amp_adr),
+ .adr_d = cs35l56_1_3amp_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l56_3_3amp_adr),
+ .adr_d = cs35l56_3_3amp_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt721_0_single_adr),
+ .adr_d = rt721_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rt722_l1[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt722_1_single_adr),
+ .adr_d = rt722_1_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rt722_l3[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt722_3_single_adr),
+ .adr_d = rt722_3_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rt722_l0_rt1320_l23[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1320_2_group2_l_adr),
+ .adr_d = rt1320_2_group2_l_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1320_3_group2_adr),
+ .adr_d = rt1320_3_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt713_vb_l2_rt1320_l13[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt713_vb_2_adr),
+ .adr_d = rt713_vb_2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
+ .adr_d = rt1320_1_group2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1320_3_group2_adr),
+ .adr_d = rt1320_3_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt713_vb_l3_rt1320_l12[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt713_vb_3_adr),
+ .adr_d = rt713_vb_3_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
+ .adr_d = rt1320_1_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1320_2_group2_adr),
+ .adr_d = rt1320_2_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l2_rt1320_l1[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt712_vb_2_group1_adr),
+ .adr_d = rt712_vb_2_group1_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group1_adr),
+ .adr_d = rt1320_1_group1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l3_rt1320_l2[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt712_vb_3_group1_adr),
+ .adr_d = rt712_vb_3_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1320_2_group1_adr),
+ .adr_d = rt1320_2_group1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l3_rt1320_l3[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt712_vb_3_group1_adr),
+ .adr_d = rt712_vb_3_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1320_3_group1_adr),
+ .adr_d = rt1320_3_group1_adr,
+ },
+ {}
+};
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
+/* Order Priority: mockup > most links > most bit link-mask > alphabetical */
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = sdw_mockup_mic_headset_1amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = sdw_mockup_multi_func,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt722.tplg", /* Reuse the existing tplg file */
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = ptl_sdw_rt713_vb_l2_rt1320_l13,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = ptl_sdw_rt713_vb_l3_rt1320_l12,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = ptl_cs42l43_l2_cs35l56x6_l13,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-cs42l43-l2-cs35l56x6-l13.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(2) | BIT(3),
+ .links = ptl_rt722_l0_rt1320_l23,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(2),
+ .links = ptl_sdw_rt712_vb_l2_rt1320_l1,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(2) | BIT(3),
+ .links = ptl_sdw_rt712_vb_l3_rt1320_l2,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(2) | BIT(3),
+ .links = ptl_cs42l43_agg_l3_cs35l56_l2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-cs42l43-agg-l3-cs35l56-l2.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = ptl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt711.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = ptl_rt721_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt721.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = ptl_rt722_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt722.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1),
+ .links = ptl_rt722_l1,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt722.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(3),
+ .links = ptl_sdw_rt712_vb_l3_rt1320_l3,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(3),
+ .links = ptl_rt721_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt721.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(3),
+ .links = ptl_rt722_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt722.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
new file mode 100644
index 000000000000..b83ac2e6337c
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
@@ -0,0 +1,568 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-apci-intel-rpl-match.c - tables and support for RPL ACPI enumeration.
+ *
+ * Copyright (c) 2022 Intel Corporation.
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+ {
+ .adr = 0x00003001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr rpl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_2_adr[] = {
+ {
+ .adr = 0x000230025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
+ {
+ .adr = 0x000131025D131601ull, /* unique ID is set for some reason */
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
+ {
+ .adr = 0x000330025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_0_group2_adr[] = {
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
+ {
+ .adr = 0x000131025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = {
+ {
+ .adr = 0x000132025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1318-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1318_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131801ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1318-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_0_adr[] = {
+ {
+ .adr = 0x000030025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_2_adr[] = {
+ {
+ .adr = 0x000230025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_3_adr[] = {
+ {
+ .adr = 0x000330025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr rpl_cs42l43_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_0_adr),
+ .adr_d = cs42l43_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdca_3_in_1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt714_2_adr),
+ .adr_d = rt714_2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_group1_adr),
+ .adr_d = rt1316_3_group1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1316_link12_rt714_link3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link2_rt1316_link01_rt714_link3[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+ .adr_d = rt711_sdca_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
+ .adr_d = rt1316_0_group2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link2_rt1316_link01[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+ .adr_d = rt711_sdca_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
+ .adr_d = rt1316_0_group2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1316_link12[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1318_link12_rt714_link3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_group1_adr),
+ .adr_d = rt1318_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1318_2_group1_adr),
+ .adr_d = rt1318_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1318_link12[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_group1_adr),
+ .adr_d = rt1318_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1318_2_group1_adr),
+ .adr_d = rt1318_2_group1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt1316_link12_rt714_link0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rpl_sdca_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr rplp_crb[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+ .adr_d = rt711_sdca_2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_codecs rpl_rt5682_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
+static const struct snd_soc_acpi_codecs rpl_essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs rpl_max98357a_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
+static const struct snd_soc_acpi_codecs rpl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
+ {
+ .comp_ids = &rpl_rt5682_hp,
+ .drv_name = "rpl_mx98357_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rpl_max98357a_amp,
+ .sof_tplg_filename = "sof-rpl-max98357a-rt5682.tplg",
+ },
+ {
+ .comp_ids = &rpl_rt5682_hp,
+ .drv_name = "rpl_rt5682_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rpl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-rpl-rt5682-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &rpl_essx_83x6,
+ .drv_name = "rpl_es83x6_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rpl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-rpl-es83x6-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &rpl_essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-rpl-es83x6", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ /* place boards for each headphone codec: sof driver will complete the
+ * tplg name and machine driver will detect the amp type
+ */
+ {
+ .id = CS42L42_ACPI_HID,
+ .drv_name = "rpl_cs42l42_def",
+ .sof_tplg_filename = "sof-rpl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = DA7219_ACPI_HID,
+ .drv_name = "rpl_da7219_def",
+ .sof_tplg_filename = "sof-rpl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = NAU8825_ACPI_HID,
+ .drv_name = "rpl_nau8825_def",
+ .sof_tplg_filename = "sof-rpl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .id = RT5650_ACPI_HID,
+ .drv_name = "rpl_rt5682_def",
+ .sof_tplg_filename = "sof-rpl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ {
+ .comp_ids = &rpl_rt5682_hp,
+ .drv_name = "rpl_rt5682_def",
+ .sof_tplg_filename = "sof-rpl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ /* place amp-only boards in the end of table */
+ {
+ .id = "INTC10B0",
+ .drv_name = "rpl_lt6911_hdmi_ssp",
+ .sof_tplg_filename = "sof-rpl-nocodec-hdmi-ssp02.tplg"
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines);
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = rpl_cs42l43_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-cs42l43-l0.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = rpl_sdca_3_in_1,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1316-l13-rt714-l2.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = rpl_sdw_rt711_link2_rt1316_link01_rt714_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l2-rt1316-l01-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = rpl_sdw_rt711_link0_rt1316_link12_rt714_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1316-l12-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = rpl_sdw_rt711_link0_rt1318_link12_rt714_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1318-l12-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0x7, /* rt711 on link0 & two rt1316s on link1 and link2 */
+ .links = rpl_sdw_rt711_link0_rt1316_link12,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1316-l12.tplg",
+ },
+ {
+ .link_mask = 0x7, /* rt711 on link0 & two rt1318s on link1 and link2 */
+ .links = rpl_sdw_rt711_link0_rt1318_link12,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1318-l12.tplg",
+ },
+ {
+ .link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */
+ .links = rpl_sdw_rt1316_link12_rt714_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt1316-l12-rt714-l0.tplg",
+ },
+ {
+ .link_mask = 0x7, /* rt711 on link2 & two rt1316s on link0 and link1 */
+ .links = rpl_sdw_rt711_link2_rt1316_link01,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l2-rt1316-l01.tplg",
+ },
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = rpl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0.tplg",
+ },
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = rpl_sdca_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0.tplg",
+ },
+ {
+ .link_mask = 0x4, /* link2 required */
+ .links = rplp_crb,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l2.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c
new file mode 100644
index 000000000000..3eaa058f8460
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-sdca-quirks.c - tables and support for SDCA quirks
+ *
+ * Copyright (c) 2024, Intel Corporation.
+ *
+ */
+
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/sdca.h>
+#include <sound/soc-acpi.h>
+#include "soc-acpi-intel-sdca-quirks.h"
+
+/*
+ * Pretend machine quirk. The argument type is not the traditional
+ * 'struct snd_soc_acpi_mach' pointer but instead the sdw_intel_ctx
+ * which contains the peripheral information required for the
+ * SoundWire/SDCA filter on the SMART_MIC setup and interface
+ * revision. When the return value is false, the entry in the
+ * 'snd_soc_acpi_mach' table needs to be skipped.
+ */
+bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg)
+{
+ struct sdw_intel_ctx *ctx = arg;
+ int i;
+
+ if (!ctx)
+ return false;
+
+ for (i = 0; i < ctx->peripherals->num_peripherals; i++) {
+ if (sdca_device_quirk_match(ctx->peripherals->array[i],
+ SDCA_QUIRKS_RT712_VB))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_sdca_is_device_rt712_vb, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS");
+
+MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h
new file mode 100644
index 000000000000..bead5ec6243f
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-acpi-intel-sdca-quirks.h - tables and support for SDCA quirks
+ *
+ * Copyright (c) 2024, Intel Corporation.
+ *
+ */
+
+#ifndef _SND_SOC_ACPI_INTEL_SDCA_QUIRKS
+#define _SND_SOC_ACPI_INTEL_SDCA_QUIRKS
+
+bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg);
+
+#endif
diff --git a/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c
new file mode 100644
index 000000000000..d122ce69fa4f
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// soc-acpi-intel-sdw-mockup-match.c - tables and support for SoundWire
+// mockup device ACPI enumeration.
+//
+// Copyright (c) 2021, Intel Corporation.
+//
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+static const struct snd_soc_acpi_endpoint sdw_mockup_single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint sdw_mockup_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint sdw_mockup_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp Endpoint, work as spk_l_endpoint */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_headset_0_adr[] = {
+ {
+ .adr = 0x0000000105AA5500ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_single_endpoint,
+ .name_prefix = "sdw_mockup_headset0"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_headset_1_adr[] = {
+ {
+ .adr = 0x0001000105AA5500ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_single_endpoint,
+ .name_prefix = "sdw_mockup_headset1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_amp_1_adr[] = {
+ {
+ .adr = 0x000100010555AA00ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_single_endpoint,
+ .name_prefix = "sdw_mockup_amp1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_amp_2_adr[] = {
+ {
+ .adr = 0x000200010555AA00ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_single_endpoint,
+ .name_prefix = "sdw_mockup_amp2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_mic_0_adr[] = {
+ {
+ .adr = 0x0000000105555500ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_single_endpoint,
+ .name_prefix = "sdw_mockup_mic0"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_mic_3_adr[] = {
+ {
+ .adr = 0x0003000105555500ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_single_endpoint,
+ .name_prefix = "sdw_mockup_mic3"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_amp_1_group1_adr[] = {
+ {
+ .adr = 0x000100010555AA00ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_l_endpoint,
+ .name_prefix = "sdw_mockup_amp1_l"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_amp_2_group1_adr[] = {
+ {
+ .adr = 0x000200010555AA00ull,
+ .num_endpoints = 1,
+ .endpoints = &sdw_mockup_r_endpoint,
+ .name_prefix = "sdw_mockup_amp2_r"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device sdw_mockup_multi_function_adr[] = {
+ {
+ .adr = 0x0000000105AAAA01ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "sdw_mockup_mmulti-function"
+ }
+};
+
+const struct snd_soc_acpi_link_adr sdw_mockup_headset_1amp_mic[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(sdw_mockup_headset_0_adr),
+ .adr_d = sdw_mockup_headset_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(sdw_mockup_amp_1_adr),
+ .adr_d = sdw_mockup_amp_1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(sdw_mockup_mic_3_adr),
+ .adr_d = sdw_mockup_mic_3_adr,
+ },
+ {}
+};
+
+const struct snd_soc_acpi_link_adr sdw_mockup_headset_2amps_mic[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(sdw_mockup_headset_0_adr),
+ .adr_d = sdw_mockup_headset_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(sdw_mockup_amp_1_group1_adr),
+ .adr_d = sdw_mockup_amp_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(sdw_mockup_amp_2_group1_adr),
+ .adr_d = sdw_mockup_amp_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(sdw_mockup_mic_3_adr),
+ .adr_d = sdw_mockup_mic_3_adr,
+ },
+ {}
+};
+
+const struct snd_soc_acpi_link_adr sdw_mockup_mic_headset_1amp[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(sdw_mockup_headset_1_adr),
+ .adr_d = sdw_mockup_headset_1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(sdw_mockup_amp_2_adr),
+ .adr_d = sdw_mockup_amp_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(sdw_mockup_mic_0_adr),
+ .adr_d = sdw_mockup_mic_0_adr,
+ },
+ {}
+};
+
+const struct snd_soc_acpi_link_adr sdw_mockup_multi_func[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(sdw_mockup_multi_function_adr),
+ .adr_d = sdw_mockup_multi_function_adr,
+ },
+ {}
+};
diff --git a/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h
new file mode 100644
index 000000000000..f7ed5beaca96
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-acpi-intel-sdw-mockup-match.h - tables and support for SoundWire
+ * mockup device ACPI enumeration.
+ *
+ * Copyright (c) 2021, Intel Corporation.
+ *
+ */
+
+#ifndef _SND_SOC_ACPI_INTEL_SDW_MOCKUP_MATCH
+#define _SND_SOC_ACPI_INTEL_SDW_MOCKUP_MATCH
+
+extern const struct snd_soc_acpi_link_adr sdw_mockup_headset_1amp_mic[];
+extern const struct snd_soc_acpi_link_adr sdw_mockup_headset_2amps_mic[];
+extern const struct snd_soc_acpi_link_adr sdw_mockup_mic_headset_1amp[];
+extern const struct snd_soc_acpi_link_adr sdw_mockup_multi_func[];
+
+#endif
diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c
new file mode 100644
index 000000000000..ee6463202918
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-skl-match.c - tables and support for SKL ACPI enumeration.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_codecs skl_codecs = {
+ .num_codecs = 1,
+ .codecs = {"10508825"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "skl_alc286s_i2s",
+ .fw_filename = "intel/dsp_fw_release.bin",
+ },
+ {
+ .id = "INT343B",
+ .drv_name = "skl_n88l25_s4567",
+ .fw_filename = "intel/dsp_fw_release.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &skl_codecs,
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "skl_n88l25_m98357a",
+ .fw_filename = "intel/dsp_fw_release.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &skl_codecs,
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
new file mode 100644
index 000000000000..f56f4bfa5187
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2023 Intel Corporation
+
+#include <linux/device.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Codec probe function
+ */
+#define CODEC_MAP_ENTRY(n, s, h, t) \
+ { \
+ .name = n, \
+ .tplg_suffix = s, \
+ .acpi_hid = h, \
+ .codec_type = t, \
+ }
+
+struct codec_map {
+ const char *name;
+ const char *tplg_suffix;
+ const char *acpi_hid;
+ enum snd_soc_acpi_intel_codec codec_type;
+};
+
+static const struct codec_map codecs[] = {
+ /* Cirrus Logic */
+ CODEC_MAP_ENTRY("CS42L42", "cs42l42", CS42L42_ACPI_HID, CODEC_CS42L42),
+
+ /* Dialog */
+ CODEC_MAP_ENTRY("DA7219", "da7219", DA7219_ACPI_HID, CODEC_DA7219),
+
+ /* Everest */
+ CODEC_MAP_ENTRY("ES8316", "es8336", ES8316_ACPI_HID, CODEC_ES8316),
+ CODEC_MAP_ENTRY("ES8326", "es8336", ES8326_ACPI_HID, CODEC_ES8326),
+ CODEC_MAP_ENTRY("ES8336", "es8336", ES8336_ACPI_HID, CODEC_ES8336),
+
+ /* Nuvoton */
+ CODEC_MAP_ENTRY("NAU8825", "nau8825", NAU8825_ACPI_HID, CODEC_NAU8825),
+
+ /* Realtek */
+ CODEC_MAP_ENTRY("RT5650", "rt5650", RT5650_ACPI_HID, CODEC_RT5650),
+ CODEC_MAP_ENTRY("RT5682", "rt5682", RT5682_ACPI_HID, CODEC_RT5682),
+ CODEC_MAP_ENTRY("RT5682S", "rt5682", RT5682S_ACPI_HID, CODEC_RT5682S),
+};
+
+static const struct codec_map amps[] = {
+ /* Cirrus Logic */
+ CODEC_MAP_ENTRY("CS35L41", "cs35l41", CS35L41_ACPI_HID, CODEC_CS35L41),
+
+ /* Maxim */
+ CODEC_MAP_ENTRY("MAX98357A", "max98357a", MAX_98357A_ACPI_HID, CODEC_MAX98357A),
+ CODEC_MAP_ENTRY("MAX98360A", "max98360a", MAX_98360A_ACPI_HID, CODEC_MAX98360A),
+ CODEC_MAP_ENTRY("MAX98373", "max98373", MAX_98373_ACPI_HID, CODEC_MAX98373),
+ CODEC_MAP_ENTRY("MAX98390", "max98390", MAX_98390_ACPI_HID, CODEC_MAX98390),
+
+ /* Nuvoton */
+ CODEC_MAP_ENTRY("NAU8318", "nau8318", NAU8318_ACPI_HID, CODEC_NAU8318),
+
+ /* Realtek */
+ CODEC_MAP_ENTRY("RT1011", "rt1011", RT1011_ACPI_HID, CODEC_RT1011),
+ CODEC_MAP_ENTRY("RT1015", "rt1015", RT1015_ACPI_HID, CODEC_RT1015),
+ CODEC_MAP_ENTRY("RT1015P", "rt1015", RT1015P_ACPI_HID, CODEC_RT1015P),
+ CODEC_MAP_ENTRY("RT1019P", "rt1019", RT1019P_ACPI_HID, CODEC_RT1019P),
+ CODEC_MAP_ENTRY("RT1308", "rt1308", RT1308_ACPI_HID, CODEC_RT1308),
+
+ /*
+ * Monolithic components
+ *
+ * Only put components that can serve as both the amp and the codec below this line.
+ * This will ensure that if the part is used just as a codec and there is an amp as well
+ * then the amp will be selected properly.
+ */
+ CODEC_MAP_ENTRY("RT5650", "rt5650", RT5650_ACPI_HID, CODEC_RT5650),
+};
+
+enum snd_soc_acpi_intel_codec
+snd_soc_acpi_intel_detect_codec_type(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codecs); i++) {
+ if (!acpi_dev_present(codecs[i].acpi_hid, NULL, -1))
+ continue;
+
+ dev_dbg(dev, "codec %s found\n", codecs[i].name);
+ return codecs[i].codec_type;
+ }
+
+ return CODEC_NONE;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_detect_codec_type, "SND_SOC_ACPI_INTEL_MATCH");
+
+enum snd_soc_acpi_intel_codec
+snd_soc_acpi_intel_detect_amp_type(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ if (!acpi_dev_present(amps[i].acpi_hid, NULL, -1))
+ continue;
+
+ dev_dbg(dev, "amp %s found\n", amps[i].name);
+ return amps[i].codec_type;
+ }
+
+ return CODEC_NONE;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_detect_amp_type, "SND_SOC_ACPI_INTEL_MATCH");
+
+const char *
+snd_soc_acpi_intel_get_codec_name(enum snd_soc_acpi_intel_codec codec_type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codecs); i++) {
+ if (codecs[i].codec_type != codec_type)
+ continue;
+
+ return codecs[i].name;
+ }
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ if (amps[i].codec_type != codec_type)
+ continue;
+
+ return amps[i].name;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_get_codec_name, "SND_SOC_ACPI_INTEL_MATCH");
+
+const char *
+snd_soc_acpi_intel_get_codec_tplg_suffix(enum snd_soc_acpi_intel_codec codec_type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codecs); i++) {
+ if (codecs[i].codec_type != codec_type)
+ continue;
+
+ return codecs[i].tplg_suffix;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_get_codec_tplg_suffix, "SND_SOC_ACPI_INTEL_MATCH");
+
+const char *
+snd_soc_acpi_intel_get_amp_tplg_suffix(enum snd_soc_acpi_intel_codec codec_type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ if (amps[i].codec_type != codec_type)
+ continue;
+
+ return amps[i].tplg_suffix;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_get_amp_tplg_suffix, "SND_SOC_ACPI_INTEL_MATCH");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Common Machine Driver Helpers");
+MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
new file mode 100644
index 000000000000..b77aafb0bfb6
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
@@ -0,0 +1,834 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-tgl-match.c - tables and support for TGL ACPI enumeration.
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+static const struct snd_soc_acpi_codecs essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_1_adr[] = {
+ {
+ .adr = 0x000120025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_dual_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1308-1"
+ },
+ {
+ .adr = 0x000122025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1308-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_2_single_adr[] = {
+ {
+ .adr = 0x000220025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
+ {
+ .adr = 0x000120025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1308-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = {
+ {
+ .adr = 0x000220025D130800ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1308-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt715_0_adr[] = {
+ {
+ .adr = 0x000021025D071500ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt715"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt715_3_adr[] = {
+ {
+ .adr = 0x000320025D071500ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt715"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device mx8373_1_adr[] = {
+ {
+ .adr = 0x000123019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "Right"
+ },
+ {
+ .adr = 0x000127019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "Left"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt5682_0_adr[] = {
+ {
+ .adr = 0x000021025D568200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt5682"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = {
+ {
+ .adr = 0x000131025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = {
+ {
+ .adr = 0x000030025D071201ull,
+ .num_endpoints = ARRAY_SIZE(rt712_endpoints),
+ .endpoints = rt712_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1712_1_single_adr[] = {
+ {
+ .adr = 0x000130025D171201ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt712-dmic"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
+ {
+ .adr = 0x000131025D131601ull, /* unique ID is set for some reason */
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_3_adr[] = {
+ {
+ .adr = 0x000330025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr tgl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_dual_adr),
+ .adr_d = rt1308_1_dual_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_rvp_headset_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_hp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_single_adr),
+ .adr_d = rt1308_1_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_chromebook_base[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt5682_0_adr),
+ .adr_d = rt5682_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(mx8373_1_adr),
+ .adr_d = mx8373_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_3_in_1_default[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_group1_adr),
+ .adr_d = rt1308_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1308_2_group1_adr),
+ .adr_d = rt1308_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_3_in_1_mono_amp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1308_1_single_adr),
+ .adr_d = rt1308_1_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt715_3_adr),
+ .adr_d = rt715_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_sdw_rt711_link1_rt1308_link2_rt715_link0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt711_1_adr),
+ .adr_d = rt711_1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1308_2_single_adr),
+ .adr_d = rt1308_2_single_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt715_0_adr),
+ .adr_d = rt715_0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca_mono[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_single_adr),
+ .adr_d = rt1316_1_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr tgl_712_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt712_0_single_adr),
+ .adr_d = rt712_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1712_1_single_adr),
+ .adr_d = rt1712_1_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
+ { /* Jack Playback Endpoint */
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* DMIC Capture Endpoint */
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Jack Capture Endpoint */
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ { /* Speaker Playback Endpoint */
+ .num = 3,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_3_adr[] = {
+ {
+ .adr = 0x00033001FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_0_adr[] = {
+ {
+ .adr = 0x00003301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = {
+ {
+ .adr = 0x00013701FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00013601FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP4"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr tgl_cs42l43_cs35l56[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs42l43_3_adr),
+ .adr_d = cs42l43_3_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l56_0_adr),
+ .adr_d = cs35l56_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_1_adr),
+ .adr_d = cs35l56_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_codecs tgl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
+static const struct snd_soc_acpi_codecs tgl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = {
+ {
+ .comp_ids = &essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-tgl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
+ /* place boards for each headphone codec: sof driver will complete the
+ * tplg name and machine driver will detect the amp type
+ */
+ {
+ .comp_ids = &tgl_rt5682_rt5682s_hp,
+ .drv_name = "tgl_rt5682_def",
+ .sof_tplg_filename = "sof-tgl", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
+ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
+ },
+ /* place amp-only boards in the end of table */
+ {
+ .id = "10EC1308",
+ .drv_name = "tgl_rt1308_hdmi_ssp",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &tgl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-tgl-rt1308-ssp2-hdmi-ssp15.tplg"
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines);
+
+static const struct snd_soc_acpi_endpoint cs35l56_l_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_r_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_2_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_3_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 2,
+ },
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_4_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 4,
+ .group_id = 2,
+ }
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_5_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 5,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 5,
+ .group_id = 2,
+ }
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_6_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 6,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 6,
+ .group_id = 2,
+ }
+};
+
+static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = {
+ { /* Speaker Playback Endpoint */
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 7,
+ .group_id = 1,
+ },
+ { /* Feedback Capture Endpoint */
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 7,
+ .group_id = 2,
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_1_4_fb_adr[] = {
+ {
+ .adr = 0x00003301fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
+ .endpoints = cs35l56_l_fb_endpoints,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
+ .endpoints = cs35l56_2_fb_endpoints,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00003101fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints),
+ .endpoints = cs35l56_4_fb_endpoints,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00003001fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints),
+ .endpoints = cs35l56_6_fb_endpoints,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_5_8_fb_adr[] = {
+ {
+ .adr = 0x00013701fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
+ .endpoints = cs35l56_r_fb_endpoints,
+ .name_prefix = "AMP8"
+ },
+ {
+ .adr = 0x00013601fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
+ .endpoints = cs35l56_3_fb_endpoints,
+ .name_prefix = "AMP7"
+ },
+ {
+ .adr = 0x00013501fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints),
+ .endpoints = cs35l56_5_fb_endpoints,
+ .name_prefix = "AMP6"
+ },
+ {
+ .adr = 0x00013401fa355601ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints),
+ .endpoints = cs35l56_7_fb_endpoints,
+ .name_prefix = "AMP5"
+ },
+};
+
+static const struct snd_soc_acpi_link_adr up_extreme_cs35l56_sdw_eight[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_sdw_eight_5_8_fb_adr),
+ .adr_d = cs35l56_sdw_eight_5_8_fb_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l56_sdw_eight_1_4_fb_adr),
+ .adr_d = cs35l56_sdw_eight_1_4_fb_adr,
+ },
+ {}
+};
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = {
+ /* mockup tests need to be first */
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(2),
+ .links = sdw_mockup_mic_headset_1amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = tgl_712_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt712.tplg",
+ },
+ {
+ .link_mask = 0x7,
+ .links = tgl_sdw_rt711_link1_rt1308_link2_rt715_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = 0xB,
+ .links = tgl_cs42l43_cs35l56,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-cs42l43-l3-cs35l56-l01.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = tgl_3_in_1_default,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ /*
+ * link_mask should be 0xB, but all links are enabled by BIOS.
+ * This entry will be selected if there is no rt1308 exposed
+ * on link2 since it will fail to match the above entry.
+ */
+ .link_mask = 0xF,
+ .links = tgl_3_in_1_mono_amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
+ .links = tgl_3_in_1_sdca,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1316-rt714.tplg",
+ },
+ {
+ /*
+ * link_mask should be 0xB, but all links are enabled by BIOS.
+ * This entry will be selected if there is no rt1316 amplifier exposed
+ * on link2 since it will fail to match the above entry.
+ */
+
+ .link_mask = 0xF, /* 4 active links required */
+ .links = tgl_3_in_1_sdca_mono,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-l0-rt1316-l1-mono-rt714-l3.tplg",
+ },
+
+ {
+ .link_mask = 0x3, /* rt711 on link 0 and 1 rt1308 on link 1 */
+ .links = tgl_hp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg",
+ },
+ {
+ .link_mask = 0x3, /* rt711 on link 0 and 2 rt1308s on link 1 */
+ .links = tgl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg",
+ },
+ {
+ .link_mask = 0x3, /* rt5682 on link0 & 2xmax98373 on link 1 */
+ .links = tgl_chromebook_base,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-sdw-max98373-rt5682.tplg",
+ },
+ {
+ .link_mask = 0x1, /* rt711 on link 0 */
+ .links = tgl_rvp_headset_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt711.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = up_extreme_cs35l56_sdw_eight,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-cs35l56-l01-fb8.tplg"
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-intel-quirks.h b/sound/soc/intel/common/soc-intel-quirks.h
new file mode 100644
index 000000000000..42bd51456b94
--- /dev/null
+++ b/sound/soc/intel/common/soc-intel-quirks.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-intel-quirks.h - prototypes for quirk autodetection
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#ifndef _SND_SOC_INTEL_QUIRKS_H
+#define _SND_SOC_INTEL_QUIRKS_H
+
+#include <linux/platform_data/x86/soc.h>
+
+#if IS_REACHABLE(CONFIG_IOSF_MBI)
+
+#include <linux/dmi.h>
+#include <asm/iosf_mbi.h>
+
+static inline bool soc_intel_is_byt_cr(struct platform_device *pdev)
+{
+ /*
+ * List of systems which:
+ * 1. Use a non CR version of the Bay Trail SoC
+ * 2. Contain at least 6 interrupt resources so that the
+ * platform_get_resource(pdev, IORESOURCE_IRQ, 5) check below
+ * succeeds
+ * 3. Despite 1. and 2. still have their IPC IRQ at index 0 rather then 5
+ *
+ * This needs to be here so that it can be shared between the SST and
+ * SOF drivers. We rely on the compiler to optimize this out in files
+ * where soc_intel_is_byt_cr is not used.
+ */
+ static const struct dmi_system_id force_bytcr_table[] = {
+ { /* Lenovo Yoga Tablet 2 series */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "YOGATablet2"),
+ },
+ },
+ {}
+ };
+ struct device *dev = &pdev->dev;
+ int status = 0;
+
+ if (!soc_intel_is_byt())
+ return false;
+
+ if (dmi_check_system(force_bytcr_table))
+ return true;
+
+ if (iosf_mbi_available()) {
+ u32 bios_status;
+
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
+ MBI_REG_READ, /* 0x10 */
+ 0x006, /* BIOS_CONFIG */
+ &bios_status);
+
+ if (status) {
+ dev_err(dev, "could not read PUNIT BIOS_CONFIG\n");
+ } else {
+ /* bits 26:27 mirror PMIC options */
+ bios_status = (bios_status >> 26) & 3;
+
+ if (bios_status == 1 || bios_status == 3) {
+ dev_info(dev, "Detected Baytrail-CR platform\n");
+ return true;
+ }
+
+ dev_info(dev, "BYT-CR not detected\n");
+ }
+ } else {
+ dev_info(dev, "IOSF_MBI not available, no BYT-CR detection\n");
+ }
+
+ if (!platform_get_resource(pdev, IORESOURCE_IRQ, 5)) {
+ /*
+ * Some devices detected as BYT-T have only a single IRQ listed,
+ * causing platform_get_irq with index 5 to return -ENXIO.
+ * The correct IRQ in this case is at index 0, as on BYT-CR.
+ */
+ dev_info(dev, "Falling back to Baytrail-CR platform\n");
+ return true;
+ }
+
+ return false;
+}
+
+#else
+
+static inline bool soc_intel_is_byt_cr(struct platform_device *pdev)
+{
+ return false;
+}
+
+#endif
+
+#endif /* _SND_SOC_INTEL_QUIRKS_H */
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
new file mode 100644
index 000000000000..b10d4794159a
--- /dev/null
+++ b/sound/soc/intel/common/sof-function-topology-lib.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2025 Intel Corporation.
+//
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "sof-function-topology-lib.h"
+
+enum tplg_device_id {
+ TPLG_DEVICE_SDCA_JACK,
+ TPLG_DEVICE_SDCA_AMP,
+ TPLG_DEVICE_SDCA_MIC,
+ TPLG_DEVICE_INTEL_PCH_DMIC,
+ TPLG_DEVICE_HDMI,
+ TPLG_DEVICE_MAX
+};
+
+#define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \
+ BIT(TPLG_DEVICE_SDCA_MIC))
+
+#define SOF_INTEL_PLATFORM_NAME_MAX 4
+
+int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
+ const char *prefix, const char ***tplg_files)
+{
+ struct snd_soc_acpi_mach_params mach_params = mach->mach_params;
+ struct snd_soc_dai_link *dai_link;
+ const struct firmware *fw;
+ char platform[SOF_INTEL_PLATFORM_NAME_MAX];
+ unsigned long tplg_mask = 0;
+ int tplg_num = 0;
+ int tplg_dev;
+ int ret;
+ int i;
+
+ ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform);
+ if (ret != 1) {
+ dev_err(card->dev, "Invalid platform name %s of tplg %s\n",
+ platform, mach->sof_tplg_filename);
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ char *tplg_dev_name;
+
+ dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id);
+ if (strstr(dai_link->name, "SimpleJack")) {
+ tplg_dev = TPLG_DEVICE_SDCA_JACK;
+ tplg_dev_name = "sdca-jack";
+ } else if (strstr(dai_link->name, "SmartAmp")) {
+ tplg_dev = TPLG_DEVICE_SDCA_AMP;
+ tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
+ "sdca-%damp", dai_link->num_cpus);
+ if (!tplg_dev_name)
+ return -ENOMEM;
+ } else if (strstr(dai_link->name, "SmartMic")) {
+ tplg_dev = TPLG_DEVICE_SDCA_MIC;
+ tplg_dev_name = "sdca-mic";
+ } else if (strstr(dai_link->name, "dmic")) {
+ switch (mach_params.dmic_num) {
+ case 2:
+ tplg_dev_name = "dmic-2ch";
+ break;
+ case 4:
+ tplg_dev_name = "dmic-4ch";
+ break;
+ default:
+ dev_warn(card->dev,
+ "unsupported number of dmics: %d\n",
+ mach_params.dmic_num);
+ continue;
+ }
+ tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC;
+ } else if (strstr(dai_link->name, "iDisp")) {
+ tplg_dev = TPLG_DEVICE_HDMI;
+ tplg_dev_name = "hdmi-pcm5";
+
+ } else {
+ /* The dai link is not supported by separated tplg yet */
+ dev_dbg(card->dev,
+ "dai_link %s is not supported by separated tplg yet\n",
+ dai_link->name);
+ return 0;
+ }
+ if (tplg_mask & BIT(tplg_dev))
+ continue;
+
+ tplg_mask |= BIT(tplg_dev);
+
+ /*
+ * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
+ * where <platform> is only required for the DMIC function as the nhlt blob
+ * is platform dependent.
+ */
+ switch (tplg_dev) {
+ case TPLG_DEVICE_INTEL_PCH_DMIC:
+ (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s/sof-%s-%s-id%d.tplg",
+ prefix, platform,
+ tplg_dev_name, dai_link->id);
+ break;
+ default:
+ (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s/sof-%s-id%d.tplg",
+ prefix, tplg_dev_name,
+ dai_link->id);
+ break;
+ }
+ if (!(*tplg_files)[tplg_num])
+ return -ENOMEM;
+ tplg_num++;
+ }
+
+ dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num);
+
+ /* Check presence of sub-topologies */
+ for (i = 0; i < tplg_num; i++) {
+ ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev);
+ if (!ret) {
+ release_firmware(fw);
+ } else {
+ dev_warn(card->dev,
+ "Failed to open topology file: %s, you might need to\n",
+ (*tplg_files)[i]);
+ dev_warn(card->dev,
+ "download it from https://github.com/thesofproject/sof-bin/\n");
+ return 0;
+ }
+ }
+
+ return tplg_num;
+}
+EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h
new file mode 100644
index 000000000000..e7d0c39d0788
--- /dev/null
+++ b/sound/soc/intel/common/sof-function-topology-lib.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-acpi-intel-get-tplg.h - get-tplg-files ops
+ *
+ * Copyright (c) 2025, Intel Corporation.
+ *
+ */
+
+#ifndef _SND_SOC_ACPI_INTEL_GET_TPLG_H
+#define _SND_SOC_ACPI_INTEL_GET_TPLG_H
+
+int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
+ const char *prefix, const char ***tplg_files);
+
+#endif
diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c
deleted file mode 100644
index 7a85c576dad3..000000000000
--- a/sound/soc/intel/common/sst-acpi.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Intel SST loader on ACPI systems
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/acpi.h>
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include "sst-dsp.h"
-#include "sst-acpi.h"
-
-#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000
-#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000
-#define SST_LPT_DSP_DMA_SIZE (1024 - 1)
-
-/* Descriptor for setting up SST platform data */
-struct sst_acpi_desc {
- const char *drv_name;
- struct sst_acpi_mach *machines;
- /* Platform resource indexes. Must set to -1 if not used */
- int resindex_lpe_base;
- int resindex_pcicfg_base;
- int resindex_fw_base;
- int irqindex_host_ipc;
- int resindex_dma_base;
- /* Unique number identifying the SST core on platform */
- int sst_id;
- /* DMA only valid when resindex_dma_base != -1*/
- int dma_engine;
- int dma_size;
-};
-
-struct sst_acpi_priv {
- struct platform_device *pdev_mach;
- struct platform_device *pdev_pcm;
- struct sst_pdata sst_pdata;
- struct sst_acpi_desc *desc;
- struct sst_acpi_mach *mach;
-};
-
-static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
-{
- struct platform_device *pdev = context;
- struct device *dev = &pdev->dev;
- struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
- struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
- struct sst_acpi_desc *desc = sst_acpi->desc;
- struct sst_acpi_mach *mach = sst_acpi->mach;
-
- sst_pdata->fw = fw;
- if (!fw) {
- dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename);
- return;
- }
-
- /* register PCM and DAI driver */
- sst_acpi->pdev_pcm =
- platform_device_register_data(dev, desc->drv_name, -1,
- sst_pdata, sizeof(*sst_pdata));
- if (IS_ERR(sst_acpi->pdev_pcm)) {
- dev_err(dev, "Cannot register device %s. Error %d\n",
- desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm));
- }
-
- return;
-}
-
-static int sst_acpi_probe(struct platform_device *pdev)
-{
- const struct acpi_device_id *id;
- struct device *dev = &pdev->dev;
- struct sst_acpi_priv *sst_acpi;
- struct sst_pdata *sst_pdata;
- struct sst_acpi_mach *mach;
- struct sst_acpi_desc *desc;
- struct resource *mmio;
- int ret = 0;
-
- sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL);
- if (sst_acpi == NULL)
- return -ENOMEM;
-
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!id)
- return -ENODEV;
-
- desc = (struct sst_acpi_desc *)id->driver_data;
- mach = sst_acpi_find_machine(desc->machines);
- if (mach == NULL) {
- dev_err(dev, "No matching ASoC machine driver found\n");
- return -ENODEV;
- }
-
- sst_pdata = &sst_acpi->sst_pdata;
- sst_pdata->id = desc->sst_id;
- sst_pdata->dma_dev = dev;
- sst_acpi->desc = desc;
- sst_acpi->mach = mach;
-
- sst_pdata->resindex_dma_base = desc->resindex_dma_base;
- if (desc->resindex_dma_base >= 0) {
- sst_pdata->dma_engine = desc->dma_engine;
- sst_pdata->dma_base = desc->resindex_dma_base;
- sst_pdata->dma_size = desc->dma_size;
- }
-
- if (desc->irqindex_host_ipc >= 0)
- sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
-
- if (desc->resindex_lpe_base >= 0) {
- mmio = platform_get_resource(pdev, IORESOURCE_MEM,
- desc->resindex_lpe_base);
- if (mmio) {
- sst_pdata->lpe_base = mmio->start;
- sst_pdata->lpe_size = resource_size(mmio);
- }
- }
-
- if (desc->resindex_pcicfg_base >= 0) {
- mmio = platform_get_resource(pdev, IORESOURCE_MEM,
- desc->resindex_pcicfg_base);
- if (mmio) {
- sst_pdata->pcicfg_base = mmio->start;
- sst_pdata->pcicfg_size = resource_size(mmio);
- }
- }
-
- if (desc->resindex_fw_base >= 0) {
- mmio = platform_get_resource(pdev, IORESOURCE_MEM,
- desc->resindex_fw_base);
- if (mmio) {
- sst_pdata->fw_base = mmio->start;
- sst_pdata->fw_size = resource_size(mmio);
- }
- }
-
- platform_set_drvdata(pdev, sst_acpi);
-
- /* register machine driver */
- sst_acpi->pdev_mach =
- platform_device_register_data(dev, mach->drv_name, -1,
- sst_pdata, sizeof(*sst_pdata));
- if (IS_ERR(sst_acpi->pdev_mach))
- return PTR_ERR(sst_acpi->pdev_mach);
-
- /* continue SST probing after firmware is loaded */
- ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename,
- dev, GFP_KERNEL, pdev, sst_acpi_fw_cb);
- if (ret)
- platform_device_unregister(sst_acpi->pdev_mach);
-
- return ret;
-}
-
-static int sst_acpi_remove(struct platform_device *pdev)
-{
- struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
- struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
-
- platform_device_unregister(sst_acpi->pdev_mach);
- if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm))
- platform_device_unregister(sst_acpi->pdev_pcm);
- release_firmware(sst_pdata->fw);
-
- return 0;
-}
-
-static struct sst_acpi_mach haswell_machines[] = {
- { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL },
- {}
-};
-
-static struct sst_acpi_desc sst_acpi_haswell_desc = {
- .drv_name = "haswell-pcm-audio",
- .machines = haswell_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = -1,
- .irqindex_host_ipc = 0,
- .sst_id = SST_DEV_ID_LYNX_POINT,
- .dma_engine = SST_DMA_TYPE_DW,
- .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET,
- .dma_size = SST_LPT_DSP_DMA_SIZE,
-};
-
-static struct sst_acpi_mach broadwell_machines[] = {
- { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
- {}
-};
-
-static struct sst_acpi_desc sst_acpi_broadwell_desc = {
- .drv_name = "haswell-pcm-audio",
- .machines = broadwell_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = -1,
- .irqindex_host_ipc = 0,
- .sst_id = SST_DEV_ID_WILDCAT_POINT,
- .dma_engine = SST_DMA_TYPE_DW,
- .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET,
- .dma_size = SST_LPT_DSP_DMA_SIZE,
-};
-
-static struct sst_acpi_mach baytrail_machines[] = {
- { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
- { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
- {}
-};
-
-static struct sst_acpi_desc sst_acpi_baytrail_desc = {
- .drv_name = "baytrail-pcm-audio",
- .machines = baytrail_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = 2,
- .irqindex_host_ipc = 5,
- .sst_id = SST_DEV_ID_BYT,
- .resindex_dma_base = -1,
-};
-
-static const struct acpi_device_id sst_acpi_match[] = {
- { "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
- { "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
- { "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, sst_acpi_match);
-
-static struct platform_driver sst_acpi_driver = {
- .probe = sst_acpi_probe,
- .remove = sst_acpi_remove,
- .driver = {
- .name = "sst-acpi",
- .acpi_match_table = ACPI_PTR(sst_acpi_match),
- },
-};
-module_platform_driver(sst_acpi_driver);
-
-MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
-MODULE_DESCRIPTION("Intel SST loader on ACPI systems");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
deleted file mode 100644
index 3ee3b7ab5d03..000000000000
--- a/sound/soc/intel/common/sst-acpi.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013-15, Intel 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.
- *
- * 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/acpi.h>
-
-/* acpi match */
-struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
-
-/* Descriptor for SST ASoC machine driver */
-struct sst_acpi_mach {
- /* ACPI ID for the matching machine driver. Audio codec for instance */
- const u8 id[ACPI_ID_LEN];
- /* machine driver name */
- const char *drv_name;
- /* firmware file name */
- const char *fw_filename;
-
- /* board name */
- const char *board;
- void (*machine_quirk)(void);
- void *pdata;
-};
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h
deleted file mode 100644
index 81aa1ed64201..000000000000
--- a/sound/soc/intel/common/sst-dsp-priv.h
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * Intel Smart Sound Technology
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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 __SOUND_SOC_SST_DSP_PRIV_H
-#define __SOUND_SOC_SST_DSP_PRIV_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/firmware.h>
-
-#include "../skylake/skl-sst-dsp.h"
-
-struct sst_mem_block;
-struct sst_module;
-struct sst_fw;
-
-/* do we need to remove or keep */
-#define DSP_DRAM_ADDR_OFFSET 0x400000
-
-/*
- * DSP Operations exported by platform Audio DSP driver.
- */
-struct sst_ops {
- /* DSP core boot / reset */
- void (*boot)(struct sst_dsp *);
- void (*reset)(struct sst_dsp *);
- int (*wake)(struct sst_dsp *);
- void (*sleep)(struct sst_dsp *);
- void (*stall)(struct sst_dsp *);
-
- /* Shim IO */
- void (*write)(void __iomem *addr, u32 offset, u32 value);
- u32 (*read)(void __iomem *addr, u32 offset);
- void (*write64)(void __iomem *addr, u32 offset, u64 value);
- u64 (*read64)(void __iomem *addr, u32 offset);
-
- /* DSP I/DRAM IO */
- void (*ram_read)(struct sst_dsp *sst, void *dest, void __iomem *src,
- size_t bytes);
- void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src,
- size_t bytes);
-
- void (*dump)(struct sst_dsp *);
-
- /* IRQ handlers */
- irqreturn_t (*irq_handler)(int irq, void *context);
-
- /* SST init and free */
- int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata);
- void (*free)(struct sst_dsp *sst);
-
- /* FW module parser/loader */
- int (*parse_fw)(struct sst_fw *sst_fw);
-};
-
-/*
- * Audio DSP memory offsets and addresses.
- */
-struct sst_addr {
- u32 lpe_base;
- u32 shim_offset;
- u32 iram_offset;
- u32 dram_offset;
- u32 dsp_iram_offset;
- u32 dsp_dram_offset;
- void __iomem *lpe;
- void __iomem *shim;
- void __iomem *pci_cfg;
- void __iomem *fw_ext;
-};
-
-/*
- * Audio DSP Mailbox configuration.
- */
-struct sst_mailbox {
- void __iomem *in_base;
- void __iomem *out_base;
- size_t in_size;
- size_t out_size;
-};
-
-/*
- * Audio DSP memory block types.
- */
-enum sst_mem_type {
- SST_MEM_IRAM = 0,
- SST_MEM_DRAM = 1,
- SST_MEM_ANY = 2,
- SST_MEM_CACHE= 3,
-};
-
-/*
- * Audio DSP Generic Firmware File.
- *
- * SST Firmware files can consist of 1..N modules. This generic structure is
- * used to manage each firmware file and it's modules regardless of SST firmware
- * type. A SST driver may load multiple FW files.
- */
-struct sst_fw {
- struct sst_dsp *dsp;
-
- /* base addresses of FW file data */
- dma_addr_t dmable_fw_paddr; /* physical address of fw data */
- void *dma_buf; /* virtual address of fw data */
- u32 size; /* size of fw data */
-
- /* lists */
- struct list_head list; /* DSP list of FW */
- struct list_head module_list; /* FW list of modules */
-
- void *private; /* core doesn't touch this */
-};
-
-/*
- * Audio DSP Generic Module Template.
- *
- * Used to define and register a new FW module. This data is extracted from
- * FW module header information.
- */
-struct sst_module_template {
- u32 id;
- u32 entry; /* entry point */
- u32 scratch_size;
- u32 persistent_size;
-};
-
-/*
- * Block Allocator - Used to allocate blocks of DSP memory.
- */
-struct sst_block_allocator {
- u32 id;
- u32 offset;
- int size;
- enum sst_mem_type type;
-};
-
-/*
- * Runtime Module Instance - A module object can be instanciated multiple
- * times within the DSP FW.
- */
-struct sst_module_runtime {
- struct sst_dsp *dsp;
- int id;
- struct sst_module *module; /* parent module we belong too */
-
- u32 persistent_offset; /* private memory offset */
- void *private;
-
- struct list_head list;
- struct list_head block_list; /* list of blocks used */
-};
-
-/*
- * Runtime Module Context - The runtime context must be manually stored by the
- * driver prior to enter S3 and restored after leaving S3. This should really be
- * part of the memory context saved by the enter D3 message IPC ???
- */
-struct sst_module_runtime_context {
- dma_addr_t dma_buffer;
- u32 *buffer;
-};
-
-/*
- * Audio DSP Module State
- */
-enum sst_module_state {
- SST_MODULE_STATE_UNLOADED = 0, /* default state */
- SST_MODULE_STATE_LOADED,
- SST_MODULE_STATE_INITIALIZED, /* and inactive */
- SST_MODULE_STATE_ACTIVE,
-};
-
-/*
- * Audio DSP Generic Module.
- *
- * Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
- * can be instanciated multiple times in the DSP.
- */
-struct sst_module {
- struct sst_dsp *dsp;
- struct sst_fw *sst_fw; /* parent FW we belong too */
-
- /* module configuration */
- u32 id;
- u32 entry; /* module entry point */
- s32 offset; /* module offset in firmware file */
- u32 size; /* module size */
- u32 scratch_size; /* global scratch memory required */
- u32 persistent_size; /* private memory required */
- enum sst_mem_type type; /* destination memory type */
- u32 data_offset; /* offset in ADSP memory space */
- void *data; /* module data */
-
- /* runtime */
- u32 usage_count; /* can be unloaded if count == 0 */
- void *private; /* core doesn't touch this */
-
- /* lists */
- struct list_head block_list; /* Module list of blocks in use */
- struct list_head list; /* DSP list of modules */
- struct list_head list_fw; /* FW list of modules */
- struct list_head runtime_list; /* list of runtime module objects*/
-
- /* state */
- enum sst_module_state state;
-};
-
-/*
- * SST Memory Block operations.
- */
-struct sst_block_ops {
- int (*enable)(struct sst_mem_block *block);
- int (*disable)(struct sst_mem_block *block);
-};
-
-/*
- * SST Generic Memory Block.
- *
- * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be
- * power gated.
- */
-struct sst_mem_block {
- struct sst_dsp *dsp;
- struct sst_module *module; /* module that uses this block */
-
- /* block config */
- u32 offset; /* offset from base */
- u32 size; /* block size */
- u32 index; /* block index 0..N */
- enum sst_mem_type type; /* block memory type IRAM/DRAM */
- const struct sst_block_ops *ops;/* block operations, if any */
-
- /* block status */
- u32 bytes_used; /* bytes in use by modules */
- void *private; /* generic core does not touch this */
- int users; /* number of modules using this block */
-
- /* block lists */
- struct list_head module_list; /* Module list of blocks */
- struct list_head list; /* Map list of free/used blocks */
-};
-
-/*
- * Generic SST Shim Interface.
- */
-struct sst_dsp {
-
- /* Shared for all platforms */
-
- /* runtime */
- struct sst_dsp_device *sst_dev;
- spinlock_t spinlock; /* IPC locking */
- struct mutex mutex; /* DSP FW lock */
- struct device *dev;
- struct device *dma_dev;
- void *thread_context;
- int irq;
- u32 id;
-
- /* operations */
- struct sst_ops *ops;
-
- /* debug FS */
- struct dentry *debugfs_root;
-
- /* base addresses */
- struct sst_addr addr;
-
- /* mailbox */
- struct sst_mailbox mailbox;
-
- /* HSW/Byt data */
-
- /* list of free and used ADSP memory blocks */
- struct list_head used_block_list;
- struct list_head free_block_list;
-
- /* SST FW files loaded and their modules */
- struct list_head module_list;
- struct list_head fw_list;
-
- /* scratch buffer */
- struct list_head scratch_block_list;
- u32 scratch_offset;
- u32 scratch_size;
-
- /* platform data */
- struct sst_pdata *pdata;
-
- /* DMA FW loading */
- struct sst_dma *dma;
- bool fw_use_dma;
-
- /* SKL data */
-
- const char *fw_name;
-
- /* To allocate CL dma buffers */
- struct skl_dsp_loader_ops dsp_ops;
- struct skl_dsp_fw_ops fw_ops;
- int sst_state;
- struct skl_cl_dev cl_dev;
- u32 intr_status;
- const struct firmware *fw;
-};
-
-/* Size optimised DRAM/IRAM memcpy */
-static inline void sst_dsp_write(struct sst_dsp *sst, void *src,
- u32 dest_offset, size_t bytes)
-{
- sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes);
-}
-
-static inline void sst_dsp_read(struct sst_dsp *sst, void *dest,
- u32 src_offset, size_t bytes)
-{
- sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes);
-}
-
-static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst)
-{
- return sst->thread_context;
-}
-
-/* Create/Free FW files - can contain multiple modules */
-struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private);
-void sst_fw_free(struct sst_fw *sst_fw);
-void sst_fw_free_all(struct sst_dsp *dsp);
-int sst_fw_reload(struct sst_fw *sst_fw);
-void sst_fw_unload(struct sst_fw *sst_fw);
-
-/* Create/Free firmware modules */
-struct sst_module *sst_module_new(struct sst_fw *sst_fw,
- struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *module);
-struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-int sst_module_alloc_blocks(struct sst_module *module);
-int sst_module_free_blocks(struct sst_module *module);
-
-/* Create/Free firmware module runtime instances */
-struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
- int id, void *private);
-void sst_module_runtime_free(struct sst_module_runtime *runtime);
-struct sst_module_runtime *sst_module_runtime_get_from_id(
- struct sst_module *module, u32 id);
-int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
- int offset);
-int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
-int sst_module_runtime_save(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context);
-int sst_module_runtime_restore(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context);
-
-/* generic block allocation */
-int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list);
-int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
-
-/* scratch allocation */
-int sst_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_block_free_scratch(struct sst_dsp *dsp);
-
-/* Register the DSPs memory blocks - would be nice to read from ACPI */
-struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
- u32 size, enum sst_mem_type type, const struct sst_block_ops *ops,
- u32 index, void *private);
-void sst_mem_block_unregister_all(struct sst_dsp *dsp);
-
-/* Create/Free DMA resources */
-int sst_dma_new(struct sst_dsp *sst);
-void sst_dma_free(struct sst_dma *dma);
-
-u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
- enum sst_mem_type type);
-#endif
diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c
deleted file mode 100644
index b5bbdf4fe93a..000000000000
--- a/sound/soc/intel/common/sst-dsp.c
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Intel Smart Sound Technology (SST) DSP Core Driver
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/slab.h>
-#include <linux/export.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/intel-sst.h>
-
-/* Internal generic low-level SST IO functions - can be overidden */
-void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
-{
- writel(value, addr + offset);
-}
-EXPORT_SYMBOL_GPL(sst_shim32_write);
-
-u32 sst_shim32_read(void __iomem *addr, u32 offset)
-{
- return readl(addr + offset);
-}
-EXPORT_SYMBOL_GPL(sst_shim32_read);
-
-void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
-{
- memcpy_toio(addr + offset, &value, sizeof(value));
-}
-EXPORT_SYMBOL_GPL(sst_shim32_write64);
-
-u64 sst_shim32_read64(void __iomem *addr, u32 offset)
-{
- u64 val;
-
- memcpy_fromio(&val, addr + offset, sizeof(val));
- return val;
-}
-EXPORT_SYMBOL_GPL(sst_shim32_read64);
-
-static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
- u32 *src, size_t bytes)
-{
- int i, words = bytes >> 2;
-
- for (i = 0; i < words; i++)
- writel(src[i], dest + i);
-}
-
-static inline void _sst_memcpy_fromio_32(u32 *dest,
- const volatile __iomem u32 *src, size_t bytes)
-{
- int i, words = bytes >> 2;
-
- for (i = 0; i < words; i++)
- dest[i] = readl(src + i);
-}
-
-void sst_memcpy_toio_32(struct sst_dsp *sst,
- void __iomem *dest, void *src, size_t bytes)
-{
- _sst_memcpy_toio_32(dest, src, bytes);
-}
-EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
-
-void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
- void __iomem *src, size_t bytes)
-{
- _sst_memcpy_fromio_32(dest, src, bytes);
-}
-EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
-
-/* Public API */
-void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write);
-
-u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset)
-{
- unsigned long flags;
- u32 val;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read);
-
-void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write64(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write64);
-
-u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset)
-{
- unsigned long flags;
- u64 val;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read64(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read64);
-
-void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value)
-{
- sst->ops->write(sst->addr.shim, offset, value);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked);
-
-u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset)
-{
- return sst->ops->read(sst->addr.shim, offset);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked);
-
-void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value)
-{
- sst->ops->write64(sst->addr.shim, offset, value);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked);
-
-u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset)
-{
- return sst->ops->read64(sst->addr.shim, offset);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked);
-
-int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
-{
- bool change;
- unsigned int old, new;
- u32 ret;
-
- ret = sst_dsp_shim_read_unlocked(sst, offset);
-
- old = ret;
- new = (old & (~mask)) | (value & mask);
-
- change = (old != new);
- if (change)
- sst_dsp_shim_write_unlocked(sst, offset, new);
-
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked);
-
-int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value)
-{
- bool change;
- u64 old, new;
-
- old = sst_dsp_shim_read64_unlocked(sst, offset);
-
- new = (old & (~mask)) | (value & mask);
-
- change = (old != new);
- if (change)
- sst_dsp_shim_write64_unlocked(sst, offset, new);
-
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
-
-/* This is for registers bits with attribute RWC */
-void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
-{
- unsigned int old, new;
- u32 ret;
-
- ret = sst_dsp_shim_read_unlocked(sst, offset);
-
- old = ret;
- new = (old & (~mask)) | (value & mask);
-
- sst_dsp_shim_write_unlocked(sst, offset, new);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced_unlocked);
-
-int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
-{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits);
-
-int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value)
-{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
-
-/* This is for registers bits with attribute RWC */
-void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- sst_dsp_shim_update_bits_forced_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced);
-
-int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
- u32 target, u32 timeout, char *operation)
-{
- int time, ret;
- u32 reg;
- bool done = false;
-
- /*
- * we will poll for couple of ms using mdelay, if not successful
- * then go to longer sleep using usleep_range
- */
-
- /* check if set state successful */
- for (time = 0; time < 5; time++) {
- if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) {
- done = true;
- break;
- }
- mdelay(1);
- }
-
- if (done == false) {
- /* sleeping in 10ms steps so adjust timeout value */
- timeout /= 10;
-
- for (time = 0; time < timeout; time++) {
- if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target)
- break;
-
- usleep_range(5000, 10000);
- }
- }
-
- reg = sst_dsp_shim_read_unlocked(ctx, offset);
- dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
- (time < timeout) ? "successful" : "timedout");
- ret = time < timeout ? 0 : -ETIME;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_register_poll);
-
-void sst_dsp_dump(struct sst_dsp *sst)
-{
- if (sst->ops->dump)
- sst->ops->dump(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dump);
-
-void sst_dsp_reset(struct sst_dsp *sst)
-{
- if (sst->ops->reset)
- sst->ops->reset(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_reset);
-
-int sst_dsp_boot(struct sst_dsp *sst)
-{
- if (sst->ops->boot)
- sst->ops->boot(sst);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_boot);
-
-int sst_dsp_wake(struct sst_dsp *sst)
-{
- if (sst->ops->wake)
- return sst->ops->wake(sst);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_wake);
-
-void sst_dsp_sleep(struct sst_dsp *sst)
-{
- if (sst->ops->sleep)
- sst->ops->sleep(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_sleep);
-
-void sst_dsp_stall(struct sst_dsp *sst)
-{
- if (sst->ops->stall)
- sst->ops->stall(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_stall);
-
-void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
-{
- sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
- trace_sst_ipc_msg_tx(msg);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
-
-u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
-{
- u32 msg;
-
- msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
- trace_sst_ipc_msg_rx(msg);
-
- return msg;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
-
-int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
- u32 outbox_offset, size_t outbox_size)
-{
- sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
- sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
- sst->mailbox.in_size = inbox_size;
- sst->mailbox.out_size = outbox_size;
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
-
-void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_outbox_write(bytes);
-
- memcpy_toio(sst->mailbox.out_base, message, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
-
-void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_outbox_read(bytes);
-
- memcpy_fromio(message, sst->mailbox.out_base, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
-
-void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_inbox_write(bytes);
-
- memcpy_toio(sst->mailbox.in_base, message, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
-
-void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_inbox_read(bytes);
-
- memcpy_fromio(message, sst->mailbox.in_base, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
-
-#ifdef CONFIG_DW_DMAC_CORE
-struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
-{
- struct sst_dsp *sst;
- int err;
-
- dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
-
- sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
- if (sst == NULL)
- return NULL;
-
- spin_lock_init(&sst->spinlock);
- mutex_init(&sst->mutex);
- sst->dev = dev;
- sst->dma_dev = pdata->dma_dev;
- sst->thread_context = sst_dev->thread_context;
- sst->sst_dev = sst_dev;
- sst->id = pdata->id;
- sst->irq = pdata->irq;
- sst->ops = sst_dev->ops;
- sst->pdata = pdata;
- INIT_LIST_HEAD(&sst->used_block_list);
- INIT_LIST_HEAD(&sst->free_block_list);
- INIT_LIST_HEAD(&sst->module_list);
- INIT_LIST_HEAD(&sst->fw_list);
- INIT_LIST_HEAD(&sst->scratch_block_list);
-
- /* Initialise SST Audio DSP */
- if (sst->ops->init) {
- err = sst->ops->init(sst, pdata);
- if (err < 0)
- return NULL;
- }
-
- /* Register the ISR */
- err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
- sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
- if (err)
- goto irq_err;
-
- err = sst_dma_new(sst);
- if (err)
- dev_warn(dev, "sst_dma_new failed %d\n", err);
-
- return sst;
-
-irq_err:
- if (sst->ops->free)
- sst->ops->free(sst);
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_new);
-
-void sst_dsp_free(struct sst_dsp *sst)
-{
- free_irq(sst->irq, sst);
- if (sst->ops->free)
- sst->ops->free(sst);
-
- sst_dma_free(sst->dma);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_free);
-#endif
-
-/* Module information */
-MODULE_AUTHOR("Liam Girdwood");
-MODULE_DESCRIPTION("Intel SST Core");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h
deleted file mode 100644
index 0b84c719ec48..000000000000
--- a/sound/soc/intel/common/sst-dsp.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Intel Smart Sound Technology (SST) Core
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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 __SOUND_SOC_SST_DSP_H
-#define __SOUND_SOC_SST_DSP_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-
-/* SST Device IDs */
-#define SST_DEV_ID_LYNX_POINT 0x33C8
-#define SST_DEV_ID_WILDCAT_POINT 0x3438
-#define SST_DEV_ID_BYT 0x0F28
-
-/* Supported SST DMA Devices */
-#define SST_DMA_TYPE_DW 1
-
-/* autosuspend delay 5s*/
-#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000)
-
-/* SST Shim register map
- * The register naming can differ between products. Some products also
- * contain extra functionality.
- */
-#define SST_CSR 0x00
-#define SST_PISR 0x08
-#define SST_PIMR 0x10
-#define SST_ISRX 0x18
-#define SST_ISRD 0x20
-#define SST_IMRX 0x28
-#define SST_IMRD 0x30
-#define SST_IPCX 0x38 /* IPC IA -> SST */
-#define SST_IPCD 0x40 /* IPC SST -> IA */
-#define SST_ISRSC 0x48
-#define SST_ISRLPESC 0x50
-#define SST_IMRSC 0x58
-#define SST_IMRLPESC 0x60
-#define SST_IPCSC 0x68
-#define SST_IPCLPESC 0x70
-#define SST_CLKCTL 0x78
-#define SST_CSR2 0x80
-#define SST_LTRC 0xE0
-#define SST_HMDC 0xE8
-
-#define SST_SHIM_BEGIN SST_CSR
-#define SST_SHIM_END SST_HDMC
-
-#define SST_DBGO 0xF0
-
-#define SST_SHIM_SIZE 0x100
-#define SST_PWMCTRL 0x1000
-
-/* SST Shim Register bits
- * The register bit naming can differ between products. Some products also
- * contain extra functionality.
- */
-
-/* CSR / CS */
-#define SST_CSR_RST (0x1 << 1)
-#define SST_CSR_SBCS0 (0x1 << 2)
-#define SST_CSR_SBCS1 (0x1 << 3)
-#define SST_CSR_DCS(x) (x << 4)
-#define SST_CSR_DCS_MASK (0x7 << 4)
-#define SST_CSR_STALL (0x1 << 10)
-#define SST_CSR_S0IOCS (0x1 << 21)
-#define SST_CSR_S1IOCS (0x1 << 23)
-#define SST_CSR_LPCS (0x1 << 31)
-#define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS)
-#define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1)
-#define SST_BYT_CSR_RST (0x1 << 0)
-#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
-#define SST_BYT_CSR_STALL (0x1 << 2)
-#define SST_BYT_CSR_PWAITMODE (0x1 << 3)
-
-/* ISRX / ISC */
-#define SST_ISRX_BUSY (0x1 << 1)
-#define SST_ISRX_DONE (0x1 << 0)
-#define SST_BYT_ISRX_REQUEST (0x1 << 1)
-
-/* ISRD / ISD */
-#define SST_ISRD_BUSY (0x1 << 1)
-#define SST_ISRD_DONE (0x1 << 0)
-
-/* IMRX / IMC */
-#define SST_IMRX_BUSY (0x1 << 1)
-#define SST_IMRX_DONE (0x1 << 0)
-#define SST_BYT_IMRX_REQUEST (0x1 << 1)
-
-/* IMRD / IMD */
-#define SST_IMRD_DONE (0x1 << 0)
-#define SST_IMRD_BUSY (0x1 << 1)
-#define SST_IMRD_SSP0 (0x1 << 16)
-#define SST_IMRD_DMAC0 (0x1 << 21)
-#define SST_IMRD_DMAC1 (0x1 << 22)
-#define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1)
-
-/* IPCX / IPCC */
-#define SST_IPCX_DONE (0x1 << 30)
-#define SST_IPCX_BUSY (0x1 << 31)
-#define SST_BYT_IPCX_DONE ((u64)0x1 << 62)
-#define SST_BYT_IPCX_BUSY ((u64)0x1 << 63)
-
-/* IPCD */
-#define SST_IPCD_DONE (0x1 << 30)
-#define SST_IPCD_BUSY (0x1 << 31)
-#define SST_BYT_IPCD_DONE ((u64)0x1 << 62)
-#define SST_BYT_IPCD_BUSY ((u64)0x1 << 63)
-
-/* CLKCTL */
-#define SST_CLKCTL_SMOS(x) (x << 24)
-#define SST_CLKCTL_MASK (3 << 24)
-#define SST_CLKCTL_DCPLCG (1 << 18)
-#define SST_CLKCTL_SCOE1 (1 << 17)
-#define SST_CLKCTL_SCOE0 (1 << 16)
-
-/* CSR2 / CS2 */
-#define SST_CSR2_SDFD_SSP0 (1 << 1)
-#define SST_CSR2_SDFD_SSP1 (1 << 2)
-
-/* LTRC */
-#define SST_LTRC_VAL(x) (x << 0)
-
-/* HMDC */
-#define SST_HMDC_HDDA0(x) (x << 0)
-#define SST_HMDC_HDDA1(x) (x << 7)
-#define SST_HMDC_HDDA_E0_CH0 1
-#define SST_HMDC_HDDA_E0_CH1 2
-#define SST_HMDC_HDDA_E0_CH2 4
-#define SST_HMDC_HDDA_E0_CH3 8
-#define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0)
-#define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1)
-#define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2)
-#define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3)
-#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \
- SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3)
-#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \
- SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3)
-
-
-/* SST Vendor Defined Registers and bits */
-#define SST_VDRTCTL0 0xa0
-#define SST_VDRTCTL1 0xa4
-#define SST_VDRTCTL2 0xa8
-#define SST_VDRTCTL3 0xaC
-
-/* VDRTCTL0 */
-#define SST_VDRTCL0_D3PGD (1 << 0)
-#define SST_VDRTCL0_D3SRAMPGD (1 << 1)
-#define SST_VDRTCL0_DSRAMPGE_SHIFT 12
-#define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
-#define SST_VDRTCL0_ISRAMPGE_SHIFT 2
-#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
-
-/* VDRTCTL2 */
-#define SST_VDRTCL2_DCLCGE (1 << 1)
-#define SST_VDRTCL2_DTCGE (1 << 10)
-#define SST_VDRTCL2_APLLSE_MASK (1 << 31)
-
-/* PMCS */
-#define SST_PMCS 0x84
-#define SST_PMCS_PS_MASK 0x3
-
-struct sst_dsp;
-
-/*
- * SST Device.
- *
- * This structure is populated by the SST core driver.
- */
-struct sst_dsp_device {
- /* Mandatory fields */
- struct sst_ops *ops;
- irqreturn_t (*thread)(int irq, void *context);
- void *thread_context;
-};
-
-/*
- * SST Platform Data.
- */
-struct sst_pdata {
- /* ACPI data */
- u32 lpe_base;
- u32 lpe_size;
- u32 pcicfg_base;
- u32 pcicfg_size;
- u32 fw_base;
- u32 fw_size;
- int irq;
-
- /* Firmware */
- const struct firmware *fw;
-
- /* DMA */
- int resindex_dma_base; /* other fields invalid if equals to -1 */
- u32 dma_base;
- u32 dma_size;
- int dma_engine;
- struct device *dma_dev;
-
- /* DSP */
- u32 id;
- void *dsp;
-};
-
-#ifdef CONFIG_DW_DMAC_CORE
-/* Initialization */
-struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
-void sst_dsp_free(struct sst_dsp *sst);
-#endif
-
-/* SHIM Read / Write */
-void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
-u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value);
-void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value);
-u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value);
-void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value);
-
-/* SHIM Read / Write Unlocked for callers already holding sst lock */
-void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value);
-u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value);
-void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value);
-u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value);
-void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value);
-
-/* Internal generic low-level SST IO functions - can be overidden */
-void sst_shim32_write(void __iomem *addr, u32 offset, u32 value);
-u32 sst_shim32_read(void __iomem *addr, u32 offset);
-void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value);
-u64 sst_shim32_read64(void __iomem *addr, u32 offset);
-void sst_memcpy_toio_32(struct sst_dsp *sst,
- void __iomem *dest, void *src, size_t bytes);
-void sst_memcpy_fromio_32(struct sst_dsp *sst,
- void *dest, void __iomem *src, size_t bytes);
-
-/* DSP reset & boot */
-void sst_dsp_reset(struct sst_dsp *sst);
-int sst_dsp_boot(struct sst_dsp *sst);
-int sst_dsp_wake(struct sst_dsp *sst);
-void sst_dsp_sleep(struct sst_dsp *sst);
-void sst_dsp_stall(struct sst_dsp *sst);
-
-/* DMA */
-int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
-void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
-int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size);
-int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size);
-
-/* Msg IO */
-void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
-u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp);
-
-/* Mailbox management */
-int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset,
- size_t inbox_size, u32 outbox_offset, size_t outbox_size);
-void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes);
-int sst_dsp_register_poll(struct sst_dsp *dsp, u32 offset, u32 mask,
- u32 expected_value, u32 timeout, char *operation);
-
-/* Debug */
-void sst_dsp_dump(struct sst_dsp *sst);
-
-#endif
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
deleted file mode 100644
index ef4881e7753a..000000000000
--- a/sound/soc/intel/common/sst-firmware.c
+++ /dev/null
@@ -1,1213 +0,0 @@
-/*
- * Intel SST Firmware Loader
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/kernel.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/firmware.h>
-#include <linux/export.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/pci.h>
-#include <linux/acpi.h>
-
-/* supported DMA engine drivers */
-#include <linux/dma/dw.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-
-#define SST_DMA_RESOURCES 2
-#define SST_DSP_DMA_MAX_BURST 0x3
-#define SST_HSW_BLOCK_ANY 0xffffffff
-
-#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
-
-struct sst_dma {
- struct sst_dsp *sst;
-
- struct dw_dma_chip *chip;
-
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *ch;
-};
-
-static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
-{
- u32 tmp = 0;
- int i, m, n;
- const u8 *src_byte = src;
-
- m = bytes / 4;
- n = bytes % 4;
-
- /* __iowrite32_copy use 32bit size values so divide by 4 */
- __iowrite32_copy((void *)dest, src, m);
-
- if (n) {
- for (i = 0; i < n; i++)
- tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8);
- __iowrite32_copy((void *)(dest + m * 4), &tmp, 1);
- }
-
-}
-
-static void sst_dma_transfer_complete(void *arg)
-{
- struct sst_dsp *sst = (struct sst_dsp *)arg;
-
- dev_dbg(sst->dev, "DMA: callback\n");
-}
-
-static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size)
-{
- struct dma_async_tx_descriptor *desc;
- struct sst_dma *dma = sst->dma;
-
- if (dma->ch == NULL) {
- dev_err(sst->dev, "error: no DMA channel\n");
- return -ENODEV;
- }
-
- dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
- (unsigned long)src_addr, (unsigned long)dest_addr, size);
-
- desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
- src_addr, size, DMA_CTRL_ACK);
- if (!desc){
- dev_err(sst->dev, "error: dma prep memcpy failed\n");
- return -EINVAL;
- }
-
- desc->callback = sst_dma_transfer_complete;
- desc->callback_param = sst;
-
- desc->tx_submit(desc);
- dma_wait_for_async_tx(desc);
-
- return 0;
-}
-
-/* copy to DSP */
-int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size)
-{
- return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
- src_addr, size);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
-
-/* copy from DSP */
-int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size)
-{
- return sst_dsp_dma_copy(sst, dest_addr,
- src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
-
-/* remove module from memory - callers hold locks */
-static void block_list_remove(struct sst_dsp *dsp,
- struct list_head *block_list)
-{
- struct sst_mem_block *block, *tmp;
- int err;
-
- /* disable each block */
- list_for_each_entry(block, block_list, module_list) {
-
- if (block->ops && block->ops->disable) {
- err = block->ops->disable(block);
- if (err < 0)
- dev_err(dsp->dev,
- "error: cant disable block %d:%d\n",
- block->type, block->index);
- }
- }
-
- /* mark each block as free */
- list_for_each_entry_safe(block, tmp, block_list, module_list) {
- list_del(&block->module_list);
- list_move(&block->list, &dsp->free_block_list);
- dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- }
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_list_prepare(struct sst_dsp *dsp,
- struct list_head *block_list)
-{
- struct sst_mem_block *block;
- int ret = 0;
-
- /* enable each block so that's it'e ready for data */
- list_for_each_entry(block, block_list, module_list) {
-
- if (block->ops && block->ops->enable && !block->users) {
- ret = block->ops->enable(block);
- if (ret < 0) {
- dev_err(dsp->dev,
- "error: cant disable block %d:%d\n",
- block->type, block->index);
- goto err;
- }
- }
- }
- return ret;
-
-err:
- list_for_each_entry(block, block_list, module_list) {
- if (block->ops && block->ops->disable)
- block->ops->disable(block);
- }
- return ret;
-}
-
-static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
- int irq)
-{
- struct dw_dma_chip *chip;
- int err;
-
- chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return ERR_PTR(-ENOMEM);
-
- chip->irq = irq;
- chip->regs = devm_ioremap_resource(dev, mem);
- if (IS_ERR(chip->regs))
- return ERR_CAST(chip->regs);
-
- err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
- if (err)
- return ERR_PTR(err);
-
- chip->dev = dev;
-
- err = dw_dma_probe(chip, NULL);
- if (err)
- return ERR_PTR(err);
-
- return chip;
-}
-
-static void dw_remove(struct dw_dma_chip *chip)
-{
- dw_dma_remove(chip);
-}
-
-static bool dma_chan_filter(struct dma_chan *chan, void *param)
-{
- struct sst_dsp *dsp = (struct sst_dsp *)param;
-
- return chan->device->dev == dsp->dma_dev;
-}
-
-int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
-{
- struct sst_dma *dma = dsp->dma;
- struct dma_slave_config slave;
- dma_cap_mask_t mask;
- int ret;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_cap_set(DMA_MEMCPY, mask);
-
- dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
- if (dma->ch == NULL) {
- dev_err(dsp->dev, "error: DMA request channel failed\n");
- return -EIO;
- }
-
- memset(&slave, 0, sizeof(slave));
- slave.direction = DMA_MEM_TO_DEV;
- slave.src_addr_width =
- slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
-
- ret = dmaengine_slave_config(dma->ch, &slave);
- if (ret) {
- dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
- ret);
- dma_release_channel(dma->ch);
- dma->ch = NULL;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
-
-void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
-{
- struct sst_dma *dma = dsp->dma;
-
- if (!dma->ch)
- return;
-
- dma_release_channel(dma->ch);
- dma->ch = NULL;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
-
-int sst_dma_new(struct sst_dsp *sst)
-{
- struct sst_pdata *sst_pdata = sst->pdata;
- struct sst_dma *dma;
- struct resource mem;
- const char *dma_dev_name;
- int ret = 0;
-
- if (sst->pdata->resindex_dma_base == -1)
- /* DMA is not used, return and squelsh error messages */
- return 0;
-
- /* configure the correct platform data for whatever DMA engine
- * is attached to the ADSP IP. */
- switch (sst->pdata->dma_engine) {
- case SST_DMA_TYPE_DW:
- dma_dev_name = "dw_dmac";
- break;
- default:
- dev_err(sst->dev, "error: invalid DMA engine %d\n",
- sst->pdata->dma_engine);
- return -EINVAL;
- }
-
- dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
- if (!dma)
- return -ENOMEM;
-
- dma->sst = sst;
-
- memset(&mem, 0, sizeof(mem));
-
- mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
- mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
- mem.flags = IORESOURCE_MEM;
-
- /* now register DMA engine device */
- dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
- if (IS_ERR(dma->chip)) {
- dev_err(sst->dev, "error: DMA device register failed\n");
- ret = PTR_ERR(dma->chip);
- goto err_dma_dev;
- }
-
- sst->dma = dma;
- sst->fw_use_dma = true;
- return 0;
-
-err_dma_dev:
- devm_kfree(sst->dev, dma);
- return ret;
-}
-EXPORT_SYMBOL(sst_dma_new);
-
-void sst_dma_free(struct sst_dma *dma)
-{
-
- if (dma == NULL)
- return;
-
- if (dma->ch)
- dma_release_channel(dma->ch);
-
- if (dma->chip)
- dw_remove(dma->chip);
-
-}
-EXPORT_SYMBOL(sst_dma_free);
-
-/* create new generic firmware object */
-struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private)
-{
- struct sst_fw *sst_fw;
- int err;
-
- if (!dsp->ops->parse_fw)
- return NULL;
-
- sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL);
- if (sst_fw == NULL)
- return NULL;
-
- sst_fw->dsp = dsp;
- sst_fw->private = private;
- sst_fw->size = fw->size;
-
- /* allocate DMA buffer to store FW data */
- sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size,
- &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL);
- if (!sst_fw->dma_buf) {
- dev_err(dsp->dev, "error: DMA alloc failed\n");
- kfree(sst_fw);
- return NULL;
- }
-
- /* copy FW data to DMA-able memory */
- memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
-
- if (dsp->fw_use_dma) {
- err = sst_dsp_dma_get_channel(dsp, 0);
- if (err < 0)
- goto chan_err;
- }
-
- /* call core specific FW paser to load FW data into DSP */
- err = dsp->ops->parse_fw(sst_fw);
- if (err < 0) {
- dev_err(dsp->dev, "error: parse fw failed %d\n", err);
- goto parse_err;
- }
-
- if (dsp->fw_use_dma)
- sst_dsp_dma_put_channel(dsp);
-
- mutex_lock(&dsp->mutex);
- list_add(&sst_fw->list, &dsp->fw_list);
- mutex_unlock(&dsp->mutex);
-
- return sst_fw;
-
-parse_err:
- if (dsp->fw_use_dma)
- sst_dsp_dma_put_channel(dsp);
-chan_err:
- dma_free_coherent(dsp->dma_dev, sst_fw->size,
- sst_fw->dma_buf,
- sst_fw->dmable_fw_paddr);
- sst_fw->dma_buf = NULL;
- kfree(sst_fw);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_fw_new);
-
-int sst_fw_reload(struct sst_fw *sst_fw)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
- int ret;
-
- dev_dbg(dsp->dev, "reloading firmware\n");
-
- /* call core specific FW paser to load FW data into DSP */
- ret = dsp->ops->parse_fw(sst_fw);
- if (ret < 0)
- dev_err(dsp->dev, "error: parse fw failed %d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_fw_reload);
-
-void sst_fw_unload(struct sst_fw *sst_fw)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_module *module, *mtmp;
- struct sst_module_runtime *runtime, *rtmp;
-
- dev_dbg(dsp->dev, "unloading firmware\n");
-
- mutex_lock(&dsp->mutex);
-
- /* check module by module */
- list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
- if (module->sst_fw == sst_fw) {
-
- /* remove runtime modules */
- list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
-
- block_list_remove(dsp, &runtime->block_list);
- list_del(&runtime->list);
- kfree(runtime);
- }
-
- /* now remove the module */
- block_list_remove(dsp, &module->block_list);
- list_del(&module->list);
- kfree(module);
- }
- }
-
- /* remove all scratch blocks */
- block_list_remove(dsp, &dsp->scratch_block_list);
-
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_fw_unload);
-
-/* free single firmware object */
-void sst_fw_free(struct sst_fw *sst_fw)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
-
- mutex_lock(&dsp->mutex);
- list_del(&sst_fw->list);
- mutex_unlock(&dsp->mutex);
-
- if (sst_fw->dma_buf)
- dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
- sst_fw->dmable_fw_paddr);
- kfree(sst_fw);
-}
-EXPORT_SYMBOL_GPL(sst_fw_free);
-
-/* free all firmware objects */
-void sst_fw_free_all(struct sst_dsp *dsp)
-{
- struct sst_fw *sst_fw, *t;
-
- mutex_lock(&dsp->mutex);
- list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
-
- list_del(&sst_fw->list);
- dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
- sst_fw->dmable_fw_paddr);
- kfree(sst_fw);
- }
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_fw_free_all);
-
-/* create a new SST generic module from FW template */
-struct sst_module *sst_module_new(struct sst_fw *sst_fw,
- struct sst_module_template *template, void *private)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_module *sst_module;
-
- sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL);
- if (sst_module == NULL)
- return NULL;
-
- sst_module->id = template->id;
- sst_module->dsp = dsp;
- sst_module->sst_fw = sst_fw;
- sst_module->scratch_size = template->scratch_size;
- sst_module->persistent_size = template->persistent_size;
- sst_module->entry = template->entry;
- sst_module->state = SST_MODULE_STATE_UNLOADED;
-
- INIT_LIST_HEAD(&sst_module->block_list);
- INIT_LIST_HEAD(&sst_module->runtime_list);
-
- mutex_lock(&dsp->mutex);
- list_add(&sst_module->list, &dsp->module_list);
- mutex_unlock(&dsp->mutex);
-
- return sst_module;
-}
-EXPORT_SYMBOL_GPL(sst_module_new);
-
-/* free firmware module and remove from available list */
-void sst_module_free(struct sst_module *sst_module)
-{
- struct sst_dsp *dsp = sst_module->dsp;
-
- mutex_lock(&dsp->mutex);
- list_del(&sst_module->list);
- mutex_unlock(&dsp->mutex);
-
- kfree(sst_module);
-}
-EXPORT_SYMBOL_GPL(sst_module_free);
-
-struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
- int id, void *private)
-{
- struct sst_dsp *dsp = module->dsp;
- struct sst_module_runtime *runtime;
-
- runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
- if (runtime == NULL)
- return NULL;
-
- runtime->id = id;
- runtime->dsp = dsp;
- runtime->module = module;
- INIT_LIST_HEAD(&runtime->block_list);
-
- mutex_lock(&dsp->mutex);
- list_add(&runtime->list, &module->runtime_list);
- mutex_unlock(&dsp->mutex);
-
- return runtime;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_new);
-
-void sst_module_runtime_free(struct sst_module_runtime *runtime)
-{
- struct sst_dsp *dsp = runtime->dsp;
-
- mutex_lock(&dsp->mutex);
- list_del(&runtime->list);
- mutex_unlock(&dsp->mutex);
-
- kfree(runtime);
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_free);
-
-static struct sst_mem_block *find_block(struct sst_dsp *dsp,
- struct sst_block_allocator *ba)
-{
- struct sst_mem_block *block;
-
- list_for_each_entry(block, &dsp->free_block_list, list) {
- if (block->type == ba->type && block->offset == ba->offset)
- return block;
- }
-
- return NULL;
-}
-
-/* Block allocator must be on block boundary */
-static int block_alloc_contiguous(struct sst_dsp *dsp,
- struct sst_block_allocator *ba, struct list_head *block_list)
-{
- struct list_head tmp = LIST_HEAD_INIT(tmp);
- struct sst_mem_block *block;
- u32 block_start = SST_HSW_BLOCK_ANY;
- int size = ba->size, offset = ba->offset;
-
- while (ba->size > 0) {
-
- block = find_block(dsp, ba);
- if (!block) {
- list_splice(&tmp, &dsp->free_block_list);
-
- ba->size = size;
- ba->offset = offset;
- return -ENOMEM;
- }
-
- list_move_tail(&block->list, &tmp);
- ba->offset += block->size;
- ba->size -= block->size;
- }
- ba->size = size;
- ba->offset = offset;
-
- list_for_each_entry(block, &tmp, list) {
-
- if (block->offset < block_start)
- block_start = block->offset;
-
- list_add(&block->module_list, block_list);
-
- dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- }
-
- list_splice(&tmp, &dsp->used_block_list);
- return 0;
-}
-
-/* allocate first free DSP blocks for data - callers hold locks */
-static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list)
-{
- struct sst_mem_block *block, *tmp;
- int ret = 0;
-
- if (ba->size == 0)
- return 0;
-
- /* find first free whole blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- if (ba->size > block->size)
- continue;
-
- ba->offset = block->offset;
- block->bytes_used = ba->size % block->size;
- list_add(&block->module_list, block_list);
- list_move(&block->list, &dsp->used_block_list);
- dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- return 0;
- }
-
- /* then find free multiple blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- /* do we span > 1 blocks */
- if (ba->size > block->size) {
-
- /* align ba to block boundary */
- ba->offset = block->offset;
-
- ret = block_alloc_contiguous(dsp, ba, block_list);
- if (ret == 0)
- return ret;
-
- }
- }
-
- /* not enough free block space */
- return -ENOMEM;
-}
-
-int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list)
-{
- int ret;
-
- dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
- ba->size, ba->offset, ba->type);
-
- mutex_lock(&dsp->mutex);
-
- ret = block_alloc(dsp, ba, block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
- goto out;
- }
-
- /* prepare DSP blocks for module usage */
- ret = block_list_prepare(dsp, block_list);
- if (ret < 0)
- dev_err(dsp->dev, "error: prepare failed\n");
-
-out:
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_alloc_blocks);
-
-int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
-{
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, block_list);
- mutex_unlock(&dsp->mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_free_blocks);
-
-/* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list)
-{
- struct sst_mem_block *block, *tmp;
- struct sst_block_allocator ba_tmp = *ba;
- u32 end = ba->offset + ba->size, block_end;
- int err;
-
- /* only IRAM/DRAM blocks are managed */
- if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
- return 0;
-
- /* are blocks already attached to this module */
- list_for_each_entry_safe(block, tmp, block_list, module_list) {
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- block_end = block->offset + block->size;
-
- /* find block that holds section */
- if (ba->offset >= block->offset && end <= block_end)
- return 0;
-
- /* does block span more than 1 section */
- if (ba->offset >= block->offset && ba->offset < block_end) {
-
- /* align ba to block boundary */
- ba_tmp.size -= block_end - ba->offset;
- ba_tmp.offset = block_end;
- err = block_alloc_contiguous(dsp, &ba_tmp, block_list);
- if (err < 0)
- return -ENOMEM;
-
- /* module already owns blocks */
- return 0;
- }
- }
-
- /* find first free blocks that can hold section in free list */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
- block_end = block->offset + block->size;
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- /* find block that holds section */
- if (ba->offset >= block->offset && end <= block_end) {
-
- /* add block */
- list_move(&block->list, &dsp->used_block_list);
- list_add(&block->module_list, block_list);
- dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- return 0;
- }
-
- /* does block span more than 1 section */
- if (ba->offset >= block->offset && ba->offset < block_end) {
-
- /* add block */
- list_move(&block->list, &dsp->used_block_list);
- list_add(&block->module_list, block_list);
- /* align ba to block boundary */
- ba_tmp.size -= block_end - ba->offset;
- ba_tmp.offset = block_end;
-
- err = block_alloc_contiguous(dsp, &ba_tmp, block_list);
- if (err < 0)
- return -ENOMEM;
-
- return 0;
- }
- }
-
- return -ENOMEM;
-}
-
-/* Load fixed module data into DSP memory blocks */
-int sst_module_alloc_blocks(struct sst_module *module)
-{
- struct sst_dsp *dsp = module->dsp;
- struct sst_fw *sst_fw = module->sst_fw;
- struct sst_block_allocator ba;
- int ret;
-
- memset(&ba, 0, sizeof(ba));
- ba.size = module->size;
- ba.type = module->type;
- ba.offset = module->offset;
-
- dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
- ba.size, ba.offset, ba.type);
-
- mutex_lock(&dsp->mutex);
-
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(dsp, &ba, &module->block_list);
- if (ret < 0) {
- dev_err(dsp->dev,
- "error: no free blocks for section at offset 0x%x size 0x%x\n",
- module->offset, module->size);
- mutex_unlock(&dsp->mutex);
- return -ENOMEM;
- }
-
- /* prepare DSP blocks for module copy */
- ret = block_list_prepare(dsp, &module->block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: fw module prepare failed\n");
- goto err;
- }
-
- /* copy partial module data to blocks */
- if (dsp->fw_use_dma) {
- ret = sst_dsp_dma_copyto(dsp,
- dsp->addr.lpe_base + module->offset,
- sst_fw->dmable_fw_paddr + module->data_offset,
- module->size);
- if (ret < 0) {
- dev_err(dsp->dev, "error: module copy failed\n");
- goto err;
- }
- } else
- sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
- module->size);
-
- mutex_unlock(&dsp->mutex);
- return ret;
-
-err:
- block_list_remove(dsp, &module->block_list);
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
-
-/* Unload entire module from DSP memory */
-int sst_module_free_blocks(struct sst_module *module)
-{
- struct sst_dsp *dsp = module->dsp;
-
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, &module->block_list);
- mutex_unlock(&dsp->mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_module_free_blocks);
-
-int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
- int offset)
-{
- struct sst_dsp *dsp = runtime->dsp;
- struct sst_module *module = runtime->module;
- struct sst_block_allocator ba;
- int ret;
-
- if (module->persistent_size == 0)
- return 0;
-
- memset(&ba, 0, sizeof(ba));
- ba.size = module->persistent_size;
- ba.type = SST_MEM_DRAM;
-
- mutex_lock(&dsp->mutex);
-
- /* do we need to allocate at a fixed address ? */
- if (offset != 0) {
-
- ba.offset = offset;
-
- dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
- ba.size, ba.type, ba.offset);
-
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
-
- } else {
- dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
- ba.size, ba.type);
-
- /* alloc blocks that includes this section */
- ret = block_alloc(dsp, &ba, &runtime->block_list);
- }
- if (ret < 0) {
- dev_err(dsp->dev,
- "error: no free blocks for runtime module size 0x%x\n",
- module->persistent_size);
- mutex_unlock(&dsp->mutex);
- return -ENOMEM;
- }
- runtime->persistent_offset = ba.offset;
-
- /* prepare DSP blocks for module copy */
- ret = block_list_prepare(dsp, &runtime->block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: runtime block prepare failed\n");
- goto err;
- }
-
- mutex_unlock(&dsp->mutex);
- return ret;
-
-err:
- block_list_remove(dsp, &module->block_list);
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
-
-int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
-{
- struct sst_dsp *dsp = runtime->dsp;
-
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, &runtime->block_list);
- mutex_unlock(&dsp->mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
-
-int sst_module_runtime_save(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context)
-{
- struct sst_dsp *dsp = runtime->dsp;
- struct sst_module *module = runtime->module;
- int ret = 0;
-
- dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
- runtime->id, runtime->persistent_offset,
- module->persistent_size);
-
- context->buffer = dma_alloc_coherent(dsp->dma_dev,
- module->persistent_size,
- &context->dma_buffer, GFP_DMA | GFP_KERNEL);
- if (!context->buffer) {
- dev_err(dsp->dev, "error: DMA context alloc failed\n");
- return -ENOMEM;
- }
-
- mutex_lock(&dsp->mutex);
-
- if (dsp->fw_use_dma) {
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0)
- goto err;
-
- ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
- dsp->addr.lpe_base + runtime->persistent_offset,
- module->persistent_size);
- sst_dsp_dma_put_channel(dsp);
- if (ret < 0) {
- dev_err(dsp->dev, "error: context copy failed\n");
- goto err;
- }
- } else
- sst_memcpy32(context->buffer, dsp->addr.lpe +
- runtime->persistent_offset,
- module->persistent_size);
-
-err:
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_save);
-
-int sst_module_runtime_restore(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context)
-{
- struct sst_dsp *dsp = runtime->dsp;
- struct sst_module *module = runtime->module;
- int ret = 0;
-
- dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
- runtime->id, runtime->persistent_offset,
- module->persistent_size);
-
- mutex_lock(&dsp->mutex);
-
- if (!context->buffer) {
- dev_info(dsp->dev, "no context buffer need to restore!\n");
- goto err;
- }
-
- if (dsp->fw_use_dma) {
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0)
- goto err;
-
- ret = sst_dsp_dma_copyto(dsp,
- dsp->addr.lpe_base + runtime->persistent_offset,
- context->dma_buffer, module->persistent_size);
- sst_dsp_dma_put_channel(dsp);
- if (ret < 0) {
- dev_err(dsp->dev, "error: module copy failed\n");
- goto err;
- }
- } else
- sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
- context->buffer, module->persistent_size);
-
- dma_free_coherent(dsp->dma_dev, module->persistent_size,
- context->buffer, context->dma_buffer);
- context->buffer = NULL;
-
-err:
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
-
-/* register a DSP memory block for use with FW based modules */
-struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
- u32 size, enum sst_mem_type type, const struct sst_block_ops *ops,
- u32 index, void *private)
-{
- struct sst_mem_block *block;
-
- block = kzalloc(sizeof(*block), GFP_KERNEL);
- if (block == NULL)
- return NULL;
-
- block->offset = offset;
- block->size = size;
- block->index = index;
- block->type = type;
- block->dsp = dsp;
- block->private = private;
- block->ops = ops;
-
- mutex_lock(&dsp->mutex);
- list_add(&block->list, &dsp->free_block_list);
- mutex_unlock(&dsp->mutex);
-
- return block;
-}
-EXPORT_SYMBOL_GPL(sst_mem_block_register);
-
-/* unregister all DSP memory blocks */
-void sst_mem_block_unregister_all(struct sst_dsp *dsp)
-{
- struct sst_mem_block *block, *tmp;
-
- mutex_lock(&dsp->mutex);
-
- /* unregister used blocks */
- list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) {
- list_del(&block->list);
- kfree(block);
- }
-
- /* unregister free blocks */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
- list_del(&block->list);
- kfree(block);
- }
-
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
-
-/* allocate scratch buffer blocks */
-int sst_block_alloc_scratch(struct sst_dsp *dsp)
-{
- struct sst_module *module;
- struct sst_block_allocator ba;
- int ret;
-
- mutex_lock(&dsp->mutex);
-
- /* calculate required scratch size */
- dsp->scratch_size = 0;
- list_for_each_entry(module, &dsp->module_list, list) {
- dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
- module->id, module->scratch_size);
- if (dsp->scratch_size < module->scratch_size)
- dsp->scratch_size = module->scratch_size;
- }
-
- dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
- dsp->scratch_size);
-
- if (dsp->scratch_size == 0) {
- dev_info(dsp->dev, "no modules need scratch buffer\n");
- mutex_unlock(&dsp->mutex);
- return 0;
- }
-
- /* allocate blocks for module scratch buffers */
- dev_dbg(dsp->dev, "allocating scratch blocks\n");
-
- ba.size = dsp->scratch_size;
- ba.type = SST_MEM_DRAM;
-
- /* do we need to allocate at fixed offset */
- if (dsp->scratch_offset != 0) {
-
- dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
- ba.size, ba.type, ba.offset);
-
- ba.offset = dsp->scratch_offset;
-
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
-
- } else {
- dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
- ba.size, ba.type);
-
- ba.offset = 0;
- ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
- }
- if (ret < 0) {
- dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
- mutex_unlock(&dsp->mutex);
- return ret;
- }
-
- ret = block_list_prepare(dsp, &dsp->scratch_block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: scratch block prepare failed\n");
- mutex_unlock(&dsp->mutex);
- return ret;
- }
-
- /* assign the same offset of scratch to each module */
- dsp->scratch_offset = ba.offset;
- mutex_unlock(&dsp->mutex);
- return dsp->scratch_size;
-}
-EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
-
-/* free all scratch blocks */
-void sst_block_free_scratch(struct sst_dsp *dsp)
-{
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, &dsp->scratch_block_list);
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_block_free_scratch);
-
-/* get a module from it's unique ID */
-struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
-{
- struct sst_module *module;
-
- mutex_lock(&dsp->mutex);
-
- list_for_each_entry(module, &dsp->module_list, list) {
- if (module->id == id) {
- mutex_unlock(&dsp->mutex);
- return module;
- }
- }
-
- mutex_unlock(&dsp->mutex);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_module_get_from_id);
-
-struct sst_module_runtime *sst_module_runtime_get_from_id(
- struct sst_module *module, u32 id)
-{
- struct sst_module_runtime *runtime;
- struct sst_dsp *dsp = module->dsp;
-
- mutex_lock(&dsp->mutex);
-
- list_for_each_entry(runtime, &module->runtime_list, list) {
- if (runtime->id == id) {
- mutex_unlock(&dsp->mutex);
- return runtime;
- }
- }
-
- mutex_unlock(&dsp->mutex);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
-
-/* returns block address in DSP address space */
-u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
- enum sst_mem_type type)
-{
- switch (type) {
- case SST_MEM_IRAM:
- return offset - dsp->addr.iram_offset +
- dsp->addr.dsp_iram_offset;
- case SST_MEM_DRAM:
- return offset - dsp->addr.dram_offset +
- dsp->addr.dsp_dram_offset;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c
deleted file mode 100644
index a12c7bb08d3b..000000000000
--- a/sound/soc/intel/common/sst-ipc.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Intel SST generic IPC Support
- *
- * Copyright (C) 2015, Intel 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.
- *
- * 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/types.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/wait.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/kthread.h>
-#include <sound/asound.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-#include "sst-ipc.h"
-
-/* IPC message timeout (msecs) */
-#define IPC_TIMEOUT_MSECS 300
-
-#define IPC_EMPTY_LIST_SIZE 8
-
-/* locks held by caller */
-static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc)
-{
- struct ipc_message *msg = NULL;
-
- if (!list_empty(&ipc->empty_list)) {
- msg = list_first_entry(&ipc->empty_list, struct ipc_message,
- list);
- list_del(&msg->list);
- }
-
- return msg;
-}
-
-static int tx_wait_done(struct sst_generic_ipc *ipc,
- struct ipc_message *msg, void *rx_data)
-{
- unsigned long flags;
- int ret;
-
- /* wait for DSP completion (in all cases atm inc pending) */
- ret = wait_event_timeout(msg->waitq, msg->complete,
- msecs_to_jiffies(IPC_TIMEOUT_MSECS));
-
- spin_lock_irqsave(&ipc->dsp->spinlock, flags);
- if (ret == 0) {
- if (ipc->ops.shim_dbg != NULL)
- ipc->ops.shim_dbg(ipc, "message timeout");
-
- list_del(&msg->list);
- ret = -ETIMEDOUT;
- } else {
-
- /* copy the data returned from DSP */
- if (msg->rx_size)
- memcpy(rx_data, msg->rx_data, msg->rx_size);
- ret = msg->errno;
- }
-
- list_add_tail(&msg->list, &ipc->empty_list);
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- return ret;
-}
-
-static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header,
- void *tx_data, size_t tx_bytes, void *rx_data,
- size_t rx_bytes, int wait)
-{
- struct ipc_message *msg;
- unsigned long flags;
-
- spin_lock_irqsave(&ipc->dsp->spinlock, flags);
-
- msg = msg_get_empty(ipc);
- if (msg == NULL) {
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- return -EBUSY;
- }
-
- msg->header = header;
- msg->tx_size = tx_bytes;
- msg->rx_size = rx_bytes;
- msg->wait = wait;
- msg->errno = 0;
- msg->pending = false;
- msg->complete = false;
-
- if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL))
- ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
-
- list_add_tail(&msg->list, &ipc->tx_list);
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
-
- queue_kthread_work(&ipc->kworker, &ipc->kwork);
-
- if (wait)
- return tx_wait_done(ipc, msg, rx_data);
- else
- return 0;
-}
-
-static int msg_empty_list_init(struct sst_generic_ipc *ipc)
-{
- int i;
-
- ipc->msg = kzalloc(sizeof(struct ipc_message) *
- IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
- if (ipc->msg == NULL)
- return -ENOMEM;
-
- for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
- ipc->msg[i].tx_data = kzalloc(ipc->tx_data_max_size, GFP_KERNEL);
- if (ipc->msg[i].tx_data == NULL)
- goto free_mem;
-
- ipc->msg[i].rx_data = kzalloc(ipc->rx_data_max_size, GFP_KERNEL);
- if (ipc->msg[i].rx_data == NULL) {
- kfree(ipc->msg[i].tx_data);
- goto free_mem;
- }
-
- init_waitqueue_head(&ipc->msg[i].waitq);
- list_add(&ipc->msg[i].list, &ipc->empty_list);
- }
-
- return 0;
-
-free_mem:
- while (i > 0) {
- kfree(ipc->msg[i-1].tx_data);
- kfree(ipc->msg[i-1].rx_data);
- --i;
- }
- kfree(ipc->msg);
-
- return -ENOMEM;
-}
-
-static void ipc_tx_msgs(struct kthread_work *work)
-{
- struct sst_generic_ipc *ipc =
- container_of(work, struct sst_generic_ipc, kwork);
- struct ipc_message *msg;
- unsigned long flags;
-
- spin_lock_irqsave(&ipc->dsp->spinlock, flags);
-
- if (list_empty(&ipc->tx_list) || ipc->pending) {
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- return;
- }
-
- /* if the DSP is busy, we will TX messages after IRQ.
- * also postpone if we are in the middle of procesing completion irq*/
- if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
- dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- return;
- }
-
- msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
- list_move(&msg->list, &ipc->rx_list);
-
- if (ipc->ops.tx_msg != NULL)
- ipc->ops.tx_msg(ipc, msg);
-
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
-}
-
-int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
- void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
-{
- return ipc_tx_message(ipc, header, tx_data, tx_bytes,
- rx_data, rx_bytes, 1);
-}
-EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
-
-int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
- void *tx_data, size_t tx_bytes)
-{
- return ipc_tx_message(ipc, header, tx_data, tx_bytes,
- NULL, 0, 0);
-}
-EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
-
-struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
- u64 header)
-{
- struct ipc_message *msg;
- u64 mask;
-
- if (ipc->ops.reply_msg_match != NULL)
- header = ipc->ops.reply_msg_match(header, &mask);
-
- if (list_empty(&ipc->rx_list)) {
- dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n",
- header);
- return NULL;
- }
-
- list_for_each_entry(msg, &ipc->rx_list, list) {
- if ((msg->header & mask) == header)
- return msg;
- }
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg);
-
-/* locks held by caller */
-void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
- struct ipc_message *msg)
-{
- msg->complete = true;
-
- if (!msg->wait)
- list_add_tail(&msg->list, &ipc->empty_list);
- else
- wake_up(&msg->waitq);
-}
-EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete);
-
-void sst_ipc_drop_all(struct sst_generic_ipc *ipc)
-{
- struct ipc_message *msg, *tmp;
- unsigned long flags;
- int tx_drop_cnt = 0, rx_drop_cnt = 0;
-
- /* drop all TX and Rx messages before we stall + reset DSP */
- spin_lock_irqsave(&ipc->dsp->spinlock, flags);
-
- list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) {
- list_move(&msg->list, &ipc->empty_list);
- tx_drop_cnt++;
- }
-
- list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) {
- list_move(&msg->list, &ipc->empty_list);
- rx_drop_cnt++;
- }
-
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
-
- if (tx_drop_cnt || rx_drop_cnt)
- dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n",
- tx_drop_cnt, rx_drop_cnt);
-}
-EXPORT_SYMBOL_GPL(sst_ipc_drop_all);
-
-int sst_ipc_init(struct sst_generic_ipc *ipc)
-{
- int ret;
-
- INIT_LIST_HEAD(&ipc->tx_list);
- INIT_LIST_HEAD(&ipc->rx_list);
- INIT_LIST_HEAD(&ipc->empty_list);
- init_waitqueue_head(&ipc->wait_txq);
-
- ret = msg_empty_list_init(ipc);
- if (ret < 0)
- return -ENOMEM;
-
- /* start the IPC message thread */
- init_kthread_worker(&ipc->kworker);
- ipc->tx_thread = kthread_run(kthread_worker_fn,
- &ipc->kworker, "%s",
- dev_name(ipc->dev));
- if (IS_ERR(ipc->tx_thread)) {
- dev_err(ipc->dev, "error: failed to create message TX task\n");
- ret = PTR_ERR(ipc->tx_thread);
- kfree(ipc->msg);
- return ret;
- }
-
- init_kthread_work(&ipc->kwork, ipc_tx_msgs);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_ipc_init);
-
-void sst_ipc_fini(struct sst_generic_ipc *ipc)
-{
- int i;
-
- if (ipc->tx_thread)
- kthread_stop(ipc->tx_thread);
-
- if (ipc->msg) {
- for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
- kfree(ipc->msg[i].tx_data);
- kfree(ipc->msg[i].rx_data);
- }
- kfree(ipc->msg);
- }
-}
-EXPORT_SYMBOL_GPL(sst_ipc_fini);
-
-/* Module information */
-MODULE_AUTHOR("Jin Yao");
-MODULE_DESCRIPTION("Intel SST IPC generic");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h
deleted file mode 100644
index ceb7e468a3fa..000000000000
--- a/sound/soc/intel/common/sst-ipc.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Intel SST generic IPC Support
- *
- * Copyright (C) 2015, Intel 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.
- *
- * 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 __SST_GENERIC_IPC_H
-#define __SST_GENERIC_IPC_H
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/workqueue.h>
-#include <linux/sched.h>
-#include <linux/kthread.h>
-
-#define IPC_MAX_MAILBOX_BYTES 256
-
-struct ipc_message {
- struct list_head list;
- u64 header;
-
- /* direction wrt host CPU */
- char *tx_data;
- size_t tx_size;
- char *rx_data;
- size_t rx_size;
-
- wait_queue_head_t waitq;
- bool pending;
- bool complete;
- bool wait;
- int errno;
-};
-
-struct sst_generic_ipc;
-
-struct sst_plat_ipc_ops {
- void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *);
- void (*shim_dbg)(struct sst_generic_ipc *, const char *);
- void (*tx_data_copy)(struct ipc_message *, char *, size_t);
- u64 (*reply_msg_match)(u64 header, u64 *mask);
- bool (*is_dsp_busy)(struct sst_dsp *dsp);
-};
-
-/* SST generic IPC data */
-struct sst_generic_ipc {
- struct device *dev;
- struct sst_dsp *dsp;
-
- /* IPC messaging */
- struct list_head tx_list;
- struct list_head rx_list;
- struct list_head empty_list;
- wait_queue_head_t wait_txq;
- struct task_struct *tx_thread;
- struct kthread_worker kworker;
- struct kthread_work kwork;
- bool pending;
- struct ipc_message *msg;
- int tx_data_max_size;
- int rx_data_max_size;
-
- struct sst_plat_ipc_ops ops;
-};
-
-int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
- void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
-
-int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
- void *tx_data, size_t tx_bytes);
-
-struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
- u64 header);
-
-void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
- struct ipc_message *msg);
-
-void sst_ipc_drop_all(struct sst_generic_ipc *ipc);
-int sst_ipc_init(struct sst_generic_ipc *ipc);
-void sst_ipc_fini(struct sst_generic_ipc *ipc);
-
-#endif
diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c
deleted file mode 100644
index dd077e116d25..000000000000
--- a/sound/soc/intel/common/sst-match-acpi.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * sst_match_apci.c - SST (LPE) match for ACPI enumeration.
- *
- * Copyright (c) 2013-15, Intel Corporation.
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/acpi.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include "sst-acpi.h"
-
-static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
-
-struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
-{
- struct sst_acpi_mach *mach;
- bool found = false;
-
- for (mach = machines; mach->id[0]; mach++)
- if (ACPI_SUCCESS(acpi_get_devices(mach->id,
- sst_acpi_mach_match,
- &found, NULL)) && found)
- return mach;
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
diff --git a/sound/soc/intel/haswell/Makefile b/sound/soc/intel/haswell/Makefile
deleted file mode 100644
index 9c1723112d22..000000000000
--- a/sound/soc/intel/haswell/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-snd-soc-sst-haswell-pcm-objs := \
- sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
-
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
diff --git a/sound/soc/intel/haswell/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c
deleted file mode 100644
index b2bec36d074c..000000000000
--- a/sound/soc/intel/haswell/sst-haswell-dsp.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/*
- * Intel Haswell SST DSP driver
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/delay.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/sched.h>
-#include <linux/export.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/pci.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "../haswell/sst-haswell-ipc.h"
-
-#include <trace/events/hswadsp.h>
-
-#define SST_HSW_FW_SIGNATURE_SIZE 4
-#define SST_HSW_FW_SIGN "$SST"
-#define SST_HSW_FW_LIB_SIGN "$LIB"
-
-#define SST_WPT_SHIM_OFFSET 0xFB000
-#define SST_LP_SHIM_OFFSET 0xE7000
-#define SST_WPT_IRAM_OFFSET 0xA0000
-#define SST_LP_IRAM_OFFSET 0x80000
-#define SST_WPT_DSP_DRAM_OFFSET 0x400000
-#define SST_WPT_DSP_IRAM_OFFSET 0x00000
-#define SST_LPT_DSP_DRAM_OFFSET 0x400000
-#define SST_LPT_DSP_IRAM_OFFSET 0x00000
-
-#define SST_SHIM_PM_REG 0x84
-
-#define SST_HSW_IRAM 1
-#define SST_HSW_DRAM 2
-#define SST_HSW_REGS 3
-
-struct dma_block_info {
- __le32 type; /* IRAM/DRAM */
- __le32 size; /* Bytes */
- __le32 ram_offset; /* Offset in I/DRAM */
- __le32 rsvd; /* Reserved field */
-} __attribute__((packed));
-
-struct fw_module_info {
- __le32 persistent_size;
- __le32 scratch_size;
-} __attribute__((packed));
-
-struct fw_header {
- unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */
- __le32 file_size; /* size of fw minus this header */
- __le32 modules; /* # of modules */
- __le32 file_format; /* version of header format */
- __le32 reserved[4];
-} __attribute__((packed));
-
-struct fw_module_header {
- unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */
- __le32 mod_size; /* size of module */
- __le32 blocks; /* # of blocks */
- __le16 padding;
- __le16 type; /* codec type, pp lib */
- __le32 entry_point;
- struct fw_module_info info;
-} __attribute__((packed));
-
-static void hsw_free(struct sst_dsp *sst);
-
-static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
- struct fw_module_header *module)
-{
- struct dma_block_info *block;
- struct sst_module *mod;
- struct sst_module_template template;
- int count, ret;
- void __iomem *ram;
-
- /* TODO: allowed module types need to be configurable */
- if (module->type != SST_HSW_MODULE_BASE_FW
- && module->type != SST_HSW_MODULE_PCM_SYSTEM
- && module->type != SST_HSW_MODULE_PCM
- && module->type != SST_HSW_MODULE_PCM_REFERENCE
- && module->type != SST_HSW_MODULE_PCM_CAPTURE
- && module->type != SST_HSW_MODULE_WAVES
- && module->type != SST_HSW_MODULE_LPAL)
- return 0;
-
- dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n",
- module->signature, module->mod_size,
- module->blocks, module->type);
- dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point);
- dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n",
- module->info.persistent_size, module->info.scratch_size);
-
- memset(&template, 0, sizeof(template));
- template.id = module->type;
- template.entry = module->entry_point - 4;
- template.persistent_size = module->info.persistent_size;
- template.scratch_size = module->info.scratch_size;
-
- mod = sst_module_new(fw, &template, NULL);
- if (mod == NULL)
- return -ENOMEM;
-
- block = (void *)module + sizeof(*module);
-
- for (count = 0; count < module->blocks; count++) {
-
- if (block->size <= 0) {
- dev_err(dsp->dev,
- "error: block %d size invalid\n", count);
- sst_module_free(mod);
- return -EINVAL;
- }
-
- switch (block->type) {
- case SST_HSW_IRAM:
- ram = dsp->addr.lpe;
- mod->offset =
- block->ram_offset + dsp->addr.iram_offset;
- mod->type = SST_MEM_IRAM;
- break;
- case SST_HSW_DRAM:
- case SST_HSW_REGS:
- ram = dsp->addr.lpe;
- mod->offset = block->ram_offset;
- mod->type = SST_MEM_DRAM;
- break;
- default:
- dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
- block->type, count);
- sst_module_free(mod);
- return -EINVAL;
- }
-
- mod->size = block->size;
- mod->data = (void *)block + sizeof(*block);
- mod->data_offset = mod->data - fw->dma_buf;
-
- dev_dbg(dsp->dev, "module block %d type 0x%x "
- "size 0x%x ==> ram %p offset 0x%x\n",
- count, mod->type, block->size, ram,
- block->ram_offset);
-
- ret = sst_module_alloc_blocks(mod);
- if (ret < 0) {
- dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
- count);
- sst_module_free(mod);
- return ret;
- }
-
- block = (void *)block + sizeof(*block) + block->size;
- }
- mod->state = SST_MODULE_STATE_LOADED;
-
- return 0;
-}
-
-static int hsw_parse_fw_image(struct sst_fw *sst_fw)
-{
- struct fw_header *header;
- struct fw_module_header *module;
- struct sst_dsp *dsp = sst_fw->dsp;
- int ret, count;
-
- /* Read the header information from the data pointer */
- header = (struct fw_header *)sst_fw->dma_buf;
-
- /* verify FW */
- if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) ||
- (sst_fw->size != header->file_size + sizeof(*header))) {
- dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n");
- return -EINVAL;
- }
-
- dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
- header->file_size, header->modules,
- header->file_format, sizeof(*header));
-
- /* parse each module */
- module = (void *)sst_fw->dma_buf + sizeof(*header);
- for (count = 0; count < header->modules; count++) {
-
- /* module */
- ret = hsw_parse_module(dsp, sst_fw, module);
- if (ret < 0) {
- dev_err(dsp->dev, "error: invalid module %d\n", count);
- return ret;
- }
- module = (void *)module + sizeof(*module) + module->mod_size;
- }
-
- return 0;
-}
-
-static irqreturn_t hsw_irq(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- u32 isr;
- int ret = IRQ_NONE;
-
- spin_lock(&sst->spinlock);
-
- /* Interrupt arrived, check src */
- isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
- if (isr & SST_ISRX_DONE) {
- trace_sst_irq_done(isr,
- sst_dsp_shim_read_unlocked(sst, SST_IMRX));
-
- /* Mask Done interrupt before return */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_DONE, SST_IMRX_DONE);
- ret = IRQ_WAKE_THREAD;
- }
-
- if (isr & SST_ISRX_BUSY) {
- trace_sst_irq_busy(isr,
- sst_dsp_shim_read_unlocked(sst, SST_IMRX));
-
- /* Mask Busy interrupt before return */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_BUSY, SST_IMRX_BUSY);
- ret = IRQ_WAKE_THREAD;
- }
-
- spin_unlock(&sst->spinlock);
- return ret;
-}
-
-static void hsw_set_dsp_D3(struct sst_dsp *sst)
-{
- u32 val;
- u32 reg;
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* enable power gating and switch off DRAM & IRAM blocks */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- val |= SST_VDRTCL0_DSRAMPGE_MASK |
- SST_VDRTCL0_ISRAMPGE_MASK;
- val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* switch off audio PLL */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val |= SST_VDRTCL2_APLLSE_MASK;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* disable MCLK(clkctl.smos = 0) */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
- SST_CLKCTL_MASK, 0);
-
- /* Set D3 state, delay 50 us */
- val = readl(sst->addr.pci_cfg + SST_PMCS);
- val |= SST_PMCS_PS_MASK;
- writel(val, sst->addr.pci_cfg + SST_PMCS);
- udelay(50);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
-}
-
-static void hsw_reset(struct sst_dsp *sst)
-{
- /* put DSP into reset and stall */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_RST | SST_CSR_STALL,
- SST_CSR_RST | SST_CSR_STALL);
-
- /* keep in reset for 10ms */
- mdelay(10);
-
- /* take DSP out of reset and keep stalled for FW loading */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
-}
-
-static int hsw_set_dsp_D0(struct sst_dsp *sst)
-{
- int tries = 10;
- u32 reg, fw_dump_bit;
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- reg |= SST_VDRTCL0_D3PGD;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* Set D0 state */
- reg = readl(sst->addr.pci_cfg + SST_PMCS);
- reg &= ~SST_PMCS_PS_MASK;
- writel(reg, sst->addr.pci_cfg + SST_PMCS);
-
- /* check that ADSP shim is enabled */
- while (tries--) {
- reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
- if (reg == 0)
- goto finish;
-
- msleep(1);
- }
-
- return -ENODEV;
-
-finish:
- /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
-
- /* stall DSP core, set clk to 192/96Mhz */
- sst_dsp_shim_update_bits_unlocked(sst,
- SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
- SST_CSR_STALL | SST_CSR_DCS(4));
-
- /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
- SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
- SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
-
- /* Stall and reset core, set CSR */
- hsw_reset(sst);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
- /* switch on audio PLL */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg &= ~SST_VDRTCL2_APLLSE_MASK;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* set default power gating control, enable power gating control for all blocks. that is,
- can't be accessed, please enable each block before accessing. */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
- /* for D0, always enable the block(DSRAM[0]) used for FW dump */
- fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
- writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
-
- /* disable DMA finish function for SSP0 & SSP1 */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
- SST_CSR2_SDFD_SSP1);
-
- /* set on-demond mode on engine 0,1 for all channels */
- sst_dsp_shim_update_bits(sst, SST_HMDC,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
-
- /* Enable Interrupt from both sides */
- sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
- 0x0);
- sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
- SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
-
- /* clear IPC registers */
- sst_dsp_shim_write(sst, SST_IPCX, 0x0);
- sst_dsp_shim_write(sst, SST_IPCD, 0x0);
- sst_dsp_shim_write(sst, 0x80, 0x6);
- sst_dsp_shim_write(sst, 0xe0, 0x300a);
-
- return 0;
-}
-
-static void hsw_boot(struct sst_dsp *sst)
-{
- /* set oportunistic mode on engine 0,1 for all channels */
- sst_dsp_shim_update_bits(sst, SST_HMDC,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
-
- /* set DSP to RUN */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
-}
-
-static void hsw_stall(struct sst_dsp *sst)
-{
- /* stall DSP */
- sst_dsp_shim_update_bits(sst, SST_CSR,
- SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
- SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
-}
-
-static void hsw_sleep(struct sst_dsp *sst)
-{
- dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
-
- /* put DSP into reset and stall */
- sst_dsp_shim_update_bits(sst, SST_CSR,
- SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
- SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
-
- hsw_set_dsp_D3(sst);
- dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
-}
-
-static int hsw_wake(struct sst_dsp *sst)
-{
- int ret;
-
- dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
-
- ret = hsw_set_dsp_D0(sst);
- if (ret < 0)
- return ret;
-
- dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
-
- return 0;
-}
-
-struct sst_adsp_memregion {
- u32 start;
- u32 end;
- int blocks;
- enum sst_mem_type type;
-};
-
-/* lynx point ADSP mem regions */
-static const struct sst_adsp_memregion lp_region[] = {
- {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
- {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
- {0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */
-};
-
-/* wild cat point ADSP mem regions */
-static const struct sst_adsp_memregion wpt_region[] = {
- {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
- {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
-};
-
-static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- /* ADSP DRAM & IRAM */
- sst->addr.lpe_base = pdata->lpe_base;
- sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
- if (!sst->addr.lpe)
- return -ENODEV;
-
- /* ADSP PCI MMIO config space */
- sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
- if (!sst->addr.pci_cfg) {
- iounmap(sst->addr.lpe);
- return -ENODEV;
- }
-
- /* SST Shim */
- sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
- return 0;
-}
-
-struct sst_sram_shift {
- u32 dev_id; /* SST Device IDs */
- u32 iram_shift;
- u32 dram_shift;
-};
-
-static const struct sst_sram_shift sram_shift[] = {
- {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
- {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
-};
-
-static u32 hsw_block_get_bit(struct sst_mem_block *block)
-{
- u32 bit = 0, shift = 0, index;
- struct sst_dsp *sst = block->dsp;
-
- for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
- if (sram_shift[index].dev_id == sst->id)
- break;
- }
-
- if (index < ARRAY_SIZE(sram_shift)) {
- switch (block->type) {
- case SST_MEM_DRAM:
- shift = sram_shift[index].dram_shift;
- break;
- case SST_MEM_IRAM:
- shift = sram_shift[index].iram_shift;
- break;
- default:
- shift = 0;
- }
- } else
- shift = 0;
-
- bit = 1 << (block->index + shift);
-
- return bit;
-}
-
-/*dummy read a SRAM block.*/
-static void sst_mem_block_dummy_read(struct sst_mem_block *block)
-{
- u32 size;
- u8 tmp_buf[4];
- struct sst_dsp *sst = block->dsp;
-
- size = block->size > 4 ? 4 : block->size;
- memcpy_fromio(tmp_buf, sst->addr.lpe + block->offset, size);
-}
-
-/* enable 32kB memory block - locks held by caller */
-static int hsw_block_enable(struct sst_mem_block *block)
-{
- struct sst_dsp *sst = block->dsp;
- u32 bit, val;
-
- if (block->users++ > 0)
- return 0;
-
- dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val &= ~SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- bit = hsw_block_get_bit(block);
- writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* wait 18 DSP clock ticks */
- udelay(10);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val |= SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
- /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
- sst_mem_block_dummy_read(block);
- return 0;
-}
-
-/* disable 32kB memory block - locks held by caller */
-static int hsw_block_disable(struct sst_mem_block *block)
-{
- struct sst_dsp *sst = block->dsp;
- u32 bit, val;
-
- if (--block->users > 0)
- return 0;
-
- dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val &= ~SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
-
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- bit = hsw_block_get_bit(block);
- /* don't disable DSRAM[0], keep it always enable for FW dump*/
- if (bit != (1 << SST_VDRTCL0_DSRAMPGE_SHIFT))
- writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* wait 18 DSP clock ticks */
- udelay(10);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val |= SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
- return 0;
-}
-
-static const struct sst_block_ops sst_hsw_ops = {
- .enable = hsw_block_enable,
- .disable = hsw_block_disable,
-};
-
-static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- const struct sst_adsp_memregion *region;
- struct device *dev;
- int ret = -ENODEV, i, j, region_count;
- u32 offset, size, fw_dump_bit;
-
- dev = sst->dma_dev;
-
- switch (sst->id) {
- case SST_DEV_ID_LYNX_POINT:
- region = lp_region;
- region_count = ARRAY_SIZE(lp_region);
- sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
- sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
- sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
- sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
- break;
- case SST_DEV_ID_WILDCAT_POINT:
- region = wpt_region;
- region_count = ARRAY_SIZE(wpt_region);
- sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
- sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
- sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
- sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
- break;
- default:
- dev_err(dev, "error: failed to get mem resources\n");
- return ret;
- }
-
- ret = hsw_acpi_resource_map(sst, pdata);
- if (ret < 0) {
- dev_err(dev, "error: failed to map resources\n");
- return ret;
- }
-
- /* enable the DSP SHIM */
- ret = hsw_set_dsp_D0(sst);
- if (ret < 0) {
- dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
- return ret;
- }
-
- ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
- if (ret)
- return ret;
-
-
- /* register DSP memory blocks - ideally we should get this from ACPI */
- for (i = 0; i < region_count; i++) {
- offset = region[i].start;
- size = (region[i].end - region[i].start) / region[i].blocks;
-
- /* register individual memory blocks */
- for (j = 0; j < region[i].blocks; j++) {
- sst_mem_block_register(sst, offset, size,
- region[i].type, &sst_hsw_ops, j, sst);
- offset += size;
- }
- }
-
- /* always enable the block(DSRAM[0]) used for FW dump */
- fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
- /* set default power gating control, enable power gating control for all blocks. that is,
- can't be accessed, please enable each block before accessing. */
- writel(0xffffffff & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- return 0;
-}
-
-static void hsw_free(struct sst_dsp *sst)
-{
- sst_mem_block_unregister_all(sst);
- iounmap(sst->addr.lpe);
- iounmap(sst->addr.pci_cfg);
-}
-
-struct sst_ops haswell_ops = {
- .reset = hsw_reset,
- .boot = hsw_boot,
- .stall = hsw_stall,
- .wake = hsw_wake,
- .sleep = hsw_sleep,
- .write = sst_shim32_write,
- .read = sst_shim32_read,
- .write64 = sst_shim32_write64,
- .read64 = sst_shim32_read64,
- .ram_read = sst_memcpy_fromio_32,
- .ram_write = sst_memcpy_toio_32,
- .irq_handler = hsw_irq,
- .init = hsw_init,
- .free = hsw_free,
- .parse_fw = hsw_parse_fw_image,
-};
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
deleted file mode 100644
index ac60f1301e21..000000000000
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ /dev/null
@@ -1,2232 +0,0 @@
-/*
- * Intel SST Haswell/Broadwell IPC Support
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/types.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/platform_device.h>
-#include <linux/kthread.h>
-#include <linux/firmware.h>
-#include <linux/dma-mapping.h>
-#include <linux/debugfs.h>
-#include <linux/pm_runtime.h>
-#include <sound/asound.h>
-
-#include "sst-haswell-ipc.h"
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "../common/sst-ipc.h"
-
-/* Global Message - Generic */
-#define IPC_GLB_TYPE_SHIFT 24
-#define IPC_GLB_TYPE_MASK (0x1f << IPC_GLB_TYPE_SHIFT)
-#define IPC_GLB_TYPE(x) (x << IPC_GLB_TYPE_SHIFT)
-
-/* Global Message - Reply */
-#define IPC_GLB_REPLY_SHIFT 0
-#define IPC_GLB_REPLY_MASK (0x1f << IPC_GLB_REPLY_SHIFT)
-#define IPC_GLB_REPLY_TYPE(x) (x << IPC_GLB_REPLY_TYPE_SHIFT)
-
-/* Stream Message - Generic */
-#define IPC_STR_TYPE_SHIFT 20
-#define IPC_STR_TYPE_MASK (0xf << IPC_STR_TYPE_SHIFT)
-#define IPC_STR_TYPE(x) (x << IPC_STR_TYPE_SHIFT)
-#define IPC_STR_ID_SHIFT 16
-#define IPC_STR_ID_MASK (0xf << IPC_STR_ID_SHIFT)
-#define IPC_STR_ID(x) (x << IPC_STR_ID_SHIFT)
-
-/* Stream Message - Reply */
-#define IPC_STR_REPLY_SHIFT 0
-#define IPC_STR_REPLY_MASK (0x1f << IPC_STR_REPLY_SHIFT)
-
-/* Stream Stage Message - Generic */
-#define IPC_STG_TYPE_SHIFT 12
-#define IPC_STG_TYPE_MASK (0xf << IPC_STG_TYPE_SHIFT)
-#define IPC_STG_TYPE(x) (x << IPC_STG_TYPE_SHIFT)
-#define IPC_STG_ID_SHIFT 10
-#define IPC_STG_ID_MASK (0x3 << IPC_STG_ID_SHIFT)
-#define IPC_STG_ID(x) (x << IPC_STG_ID_SHIFT)
-
-/* Stream Stage Message - Reply */
-#define IPC_STG_REPLY_SHIFT 0
-#define IPC_STG_REPLY_MASK (0x1f << IPC_STG_REPLY_SHIFT)
-
-/* Debug Log Message - Generic */
-#define IPC_LOG_OP_SHIFT 20
-#define IPC_LOG_OP_MASK (0xf << IPC_LOG_OP_SHIFT)
-#define IPC_LOG_OP_TYPE(x) (x << IPC_LOG_OP_SHIFT)
-#define IPC_LOG_ID_SHIFT 16
-#define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT)
-#define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT)
-
-/* Module Message */
-#define IPC_MODULE_OPERATION_SHIFT 20
-#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT)
-#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT)
-
-#define IPC_MODULE_ID_SHIFT 16
-#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT)
-#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT)
-
-/* IPC message timeout (msecs) */
-#define IPC_TIMEOUT_MSECS 300
-#define IPC_BOOT_MSECS 200
-#define IPC_MSG_WAIT 0
-#define IPC_MSG_NOWAIT 1
-
-/* Firmware Ready Message */
-#define IPC_FW_READY (0x1 << 29)
-#define IPC_STATUS_MASK (0x3 << 30)
-
-#define IPC_EMPTY_LIST_SIZE 8
-#define IPC_MAX_STREAMS 4
-
-/* Mailbox */
-#define IPC_MAX_MAILBOX_BYTES 256
-
-#define INVALID_STREAM_HW_ID 0xffffffff
-
-/* Global Message - Types and Replies */
-enum ipc_glb_type {
- IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */
- IPC_GLB_PERFORMANCE_MONITOR = 1, /* Performance monitoring actions */
- IPC_GLB_ALLOCATE_STREAM = 3, /* Request to allocate new stream */
- IPC_GLB_FREE_STREAM = 4, /* Request to free stream */
- IPC_GLB_GET_FW_CAPABILITIES = 5, /* Retrieves firmware capabilities */
- IPC_GLB_STREAM_MESSAGE = 6, /* Message directed to stream or its stages */
- /* Request to store firmware context during D0->D3 transition */
- IPC_GLB_REQUEST_DUMP = 7,
- /* Request to restore firmware context during D3->D0 transition */
- IPC_GLB_RESTORE_CONTEXT = 8,
- IPC_GLB_GET_DEVICE_FORMATS = 9, /* Set device format */
- IPC_GLB_SET_DEVICE_FORMATS = 10, /* Get device format */
- IPC_GLB_SHORT_REPLY = 11,
- IPC_GLB_ENTER_DX_STATE = 12,
- IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */
- IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */
- IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */
- IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */
- IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */
-};
-
-enum ipc_glb_reply {
- IPC_GLB_REPLY_SUCCESS = 0, /* The operation was successful. */
- IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1, /* Invalid parameter was passed. */
- IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2, /* Uknown message type was resceived. */
- IPC_GLB_REPLY_OUT_OF_RESOURCES = 3, /* No resources to satisfy the request. */
- IPC_GLB_REPLY_BUSY = 4, /* The system or resource is busy. */
- IPC_GLB_REPLY_PENDING = 5, /* The action was scheduled for processing. */
- IPC_GLB_REPLY_FAILURE = 6, /* Critical error happened. */
- IPC_GLB_REPLY_INVALID_REQUEST = 7, /* Request can not be completed. */
- IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8, /* Processing stage was uninitialized. */
- IPC_GLB_REPLY_NOT_FOUND = 9, /* Required resource can not be found. */
- IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */
-};
-
-enum ipc_module_operation {
- IPC_MODULE_NOTIFICATION = 0,
- IPC_MODULE_ENABLE = 1,
- IPC_MODULE_DISABLE = 2,
- IPC_MODULE_GET_PARAMETER = 3,
- IPC_MODULE_SET_PARAMETER = 4,
- IPC_MODULE_GET_INFO = 5,
- IPC_MODULE_MAX_MESSAGE
-};
-
-/* Stream Message - Types */
-enum ipc_str_operation {
- IPC_STR_RESET = 0,
- IPC_STR_PAUSE = 1,
- IPC_STR_RESUME = 2,
- IPC_STR_STAGE_MESSAGE = 3,
- IPC_STR_NOTIFICATION = 4,
- IPC_STR_MAX_MESSAGE
-};
-
-/* Stream Stage Message Types */
-enum ipc_stg_operation {
- IPC_STG_GET_VOLUME = 0,
- IPC_STG_SET_VOLUME,
- IPC_STG_SET_WRITE_POSITION,
- IPC_STG_SET_FX_ENABLE,
- IPC_STG_SET_FX_DISABLE,
- IPC_STG_SET_FX_GET_PARAM,
- IPC_STG_SET_FX_SET_PARAM,
- IPC_STG_SET_FX_GET_INFO,
- IPC_STG_MUTE_LOOPBACK,
- IPC_STG_MAX_MESSAGE
-};
-
-/* Stream Stage Message Types For Notification*/
-enum ipc_stg_operation_notify {
- IPC_POSITION_CHANGED = 0,
- IPC_STG_GLITCH,
- IPC_STG_MAX_NOTIFY
-};
-
-enum ipc_glitch_type {
- IPC_GLITCH_UNDERRUN = 1,
- IPC_GLITCH_DECODER_ERROR,
- IPC_GLITCH_DOUBLED_WRITE_POS,
- IPC_GLITCH_MAX
-};
-
-/* Debug Control */
-enum ipc_debug_operation {
- IPC_DEBUG_ENABLE_LOG = 0,
- IPC_DEBUG_DISABLE_LOG = 1,
- IPC_DEBUG_REQUEST_LOG_DUMP = 2,
- IPC_DEBUG_NOTIFY_LOG_DUMP = 3,
- IPC_DEBUG_MAX_DEBUG_LOG
-};
-
-/* Firmware Ready */
-struct sst_hsw_ipc_fw_ready {
- u32 inbox_offset;
- u32 outbox_offset;
- u32 inbox_size;
- u32 outbox_size;
- u32 fw_info_size;
- u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
-} __attribute__((packed));
-
-struct sst_hsw_stream;
-struct sst_hsw;
-
-/* Stream infomation */
-struct sst_hsw_stream {
- /* configuration */
- struct sst_hsw_ipc_stream_alloc_req request;
- struct sst_hsw_ipc_stream_alloc_reply reply;
- struct sst_hsw_ipc_stream_free_req free_req;
-
- /* Mixer info */
- u32 mute_volume[SST_HSW_NO_CHANNELS];
- u32 mute[SST_HSW_NO_CHANNELS];
-
- /* runtime info */
- struct sst_hsw *hsw;
- int host_id;
- bool commited;
- bool running;
-
- /* Notification work */
- struct work_struct notify_work;
- u32 header;
-
- /* Position info from DSP */
- struct sst_hsw_ipc_stream_set_position wpos;
- struct sst_hsw_ipc_stream_get_position rpos;
- struct sst_hsw_ipc_stream_glitch_position glitch;
-
- /* Volume info */
- struct sst_hsw_ipc_volume_req vol_req;
-
- /* driver callback */
- u32 (*notify_position)(struct sst_hsw_stream *stream, void *data);
- void *pdata;
-
- /* record the fw read position when playback */
- snd_pcm_uframes_t old_position;
- bool play_silence;
- struct list_head node;
-};
-
-/* FW log ring information */
-struct sst_hsw_log_stream {
- dma_addr_t dma_addr;
- unsigned char *dma_area;
- unsigned char *ring_descr;
- int pages;
- int size;
-
- /* Notification work */
- struct work_struct notify_work;
- wait_queue_head_t readers_wait_q;
- struct mutex rw_mutex;
-
- u32 last_pos;
- u32 curr_pos;
- u32 reader_pos;
-
- /* fw log config */
- u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
-
- struct sst_hsw *hsw;
-};
-
-/* SST Haswell IPC data */
-struct sst_hsw {
- struct device *dev;
- struct sst_dsp *dsp;
- struct platform_device *pdev_pcm;
-
- /* FW config */
- struct sst_hsw_ipc_fw_ready fw_ready;
- struct sst_hsw_ipc_fw_version version;
- bool fw_done;
- struct sst_fw *sst_fw;
-
- /* stream */
- struct list_head stream_list;
-
- /* global mixer */
- struct sst_hsw_ipc_stream_info_reply mixer_info;
- enum sst_hsw_volume_curve curve_type;
- u32 curve_duration;
- u32 mute[SST_HSW_NO_CHANNELS];
- u32 mute_volume[SST_HSW_NO_CHANNELS];
-
- /* DX */
- struct sst_hsw_ipc_dx_reply dx;
- void *dx_context;
- dma_addr_t dx_context_paddr;
- enum sst_hsw_device_id dx_dev;
- enum sst_hsw_device_mclk dx_mclk;
- enum sst_hsw_device_mode dx_mode;
- u32 dx_clock_divider;
-
- /* boot */
- wait_queue_head_t boot_wait;
- bool boot_complete;
- bool shutdown;
-
- /* IPC messaging */
- struct sst_generic_ipc ipc;
-
- /* FW log stream */
- struct sst_hsw_log_stream log_stream;
-
- /* flags bit field to track module state when resume from RTD3,
- * each bit represent state (enabled/disabled) of single module */
- u32 enabled_modules_rtd3;
-
- /* buffer to store parameter lines */
- u32 param_idx_w; /* write index */
- u32 param_idx_r; /* read index */
- u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT];
-};
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/hswadsp.h>
-
-static inline u32 msg_get_global_type(u32 msg)
-{
- return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_global_reply(u32 msg)
-{
- return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT;
-}
-
-static inline u32 msg_get_stream_type(u32 msg)
-{
- return (msg & IPC_STR_TYPE_MASK) >> IPC_STR_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_stage_type(u32 msg)
-{
- return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_stream_id(u32 msg)
-{
- return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT;
-}
-
-static inline u32 msg_get_notify_reason(u32 msg)
-{
- return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_module_operation(u32 msg)
-{
- return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT;
-}
-
-static inline u32 msg_get_module_id(u32 msg)
-{
- return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT;
-}
-
-u32 create_channel_map(enum sst_hsw_channel_config config)
-{
- switch (config) {
- case SST_HSW_CHANNEL_CONFIG_MONO:
- return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER);
- case SST_HSW_CHANNEL_CONFIG_STEREO:
- return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_RIGHT << 4));
- case SST_HSW_CHANNEL_CONFIG_2_POINT_1:
- return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_RIGHT << 4)
- | (SST_HSW_CHANNEL_LFE << 8 ));
- case SST_HSW_CHANNEL_CONFIG_3_POINT_0:
- return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8));
- case SST_HSW_CHANNEL_CONFIG_3_POINT_1:
- return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_LFE << 12));
- case SST_HSW_CHANNEL_CONFIG_QUATRO:
- return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_RIGHT << 4)
- | (SST_HSW_CHANNEL_LEFT_SURROUND << 8)
- | (SST_HSW_CHANNEL_RIGHT_SURROUND << 12));
- case SST_HSW_CHANNEL_CONFIG_4_POINT_0:
- return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_CENTER_SURROUND << 12));
- case SST_HSW_CHANNEL_CONFIG_5_POINT_0:
- return (0xFFF00000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
- | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16));
- case SST_HSW_CHANNEL_CONFIG_5_POINT_1:
- return (0xFF000000 | SST_HSW_CHANNEL_CENTER
- | (SST_HSW_CHANNEL_LEFT << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
- | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)
- | (SST_HSW_CHANNEL_LFE << 20));
- case SST_HSW_CHANNEL_CONFIG_DUAL_MONO:
- return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_LEFT << 4));
- default:
- return 0xFFFFFFFF;
- }
-}
-
-static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
- int stream_id)
-{
- struct sst_hsw_stream *stream;
-
- list_for_each_entry(stream, &hsw->stream_list, node) {
- if (stream->reply.stream_hw_id == stream_id)
- return stream;
- }
-
- return NULL;
-}
-
-static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
-{
- struct sst_hsw_ipc_fw_ready fw_ready;
- u32 offset;
- u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
- char *tmp[5], *pinfo;
- int i = 0;
-
- offset = (header & 0x1FFFFFFF) << 3;
-
- dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n",
- header, offset);
-
- /* copy data from the DSP FW ready offset */
- sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready));
-
- sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset,
- fw_ready.inbox_size, fw_ready.outbox_offset,
- fw_ready.outbox_size);
-
- hsw->boot_complete = true;
- wake_up(&hsw->boot_wait);
-
- dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n",
- fw_ready.inbox_offset, fw_ready.inbox_size);
- dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
- fw_ready.outbox_offset, fw_ready.outbox_size);
- if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
- fw_ready.fw_info[fw_ready.fw_info_size] = 0;
- dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
-
- /* log the FW version info got from the mailbox here. */
- memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
- pinfo = &fw_info[0];
- for (i = 0; i < ARRAY_SIZE(tmp); i++)
- tmp[i] = strsep(&pinfo, " ");
- dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
- "version: %s.%s, build %s, source commit id: %s\n",
- tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
- }
-}
-
-static void hsw_notification_work(struct work_struct *work)
-{
- struct sst_hsw_stream *stream = container_of(work,
- struct sst_hsw_stream, notify_work);
- struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch;
- struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos;
- struct sst_hsw *hsw = stream->hsw;
- u32 reason;
-
- reason = msg_get_notify_reason(stream->header);
-
- switch (reason) {
- case IPC_STG_GLITCH:
- trace_ipc_notification("DSP stream under/overrun",
- stream->reply.stream_hw_id);
- sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch));
-
- dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n",
- glitch->glitch_type, glitch->present_pos,
- glitch->write_pos);
- break;
-
- case IPC_POSITION_CHANGED:
- trace_ipc_notification("DSP stream position changed for",
- stream->reply.stream_hw_id);
- sst_dsp_inbox_read(hsw->dsp, pos, sizeof(*pos));
-
- if (stream->notify_position)
- stream->notify_position(stream, stream->pdata);
-
- break;
- default:
- dev_err(hsw->dev, "error: unknown notification 0x%x\n",
- stream->header);
- break;
- }
-
- /* tell DSP that notification has been handled */
- sst_dsp_shim_update_bits(hsw->dsp, SST_IPCD,
- SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
-
- /* unmask busy interrupt */
- sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
-}
-
-static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
-{
- struct sst_hsw_stream *stream;
- u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
- u32 stream_id = msg_get_stream_id(header);
- u32 stream_msg = msg_get_stream_type(header);
-
- stream = get_stream_by_id(hsw, stream_id);
- if (stream == NULL)
- return;
-
- switch (stream_msg) {
- case IPC_STR_STAGE_MESSAGE:
- case IPC_STR_NOTIFICATION:
- break;
- case IPC_STR_RESET:
- trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
- break;
- case IPC_STR_PAUSE:
- stream->running = false;
- trace_ipc_notification("stream paused",
- stream->reply.stream_hw_id);
- break;
- case IPC_STR_RESUME:
- stream->running = true;
- trace_ipc_notification("stream running",
- stream->reply.stream_hw_id);
- break;
- }
-}
-
-static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
-{
- struct ipc_message *msg;
- u32 reply = msg_get_global_reply(header);
-
- trace_ipc_reply("processing -->", header);
-
- msg = sst_ipc_reply_find_msg(&hsw->ipc, header);
- if (msg == NULL) {
- trace_ipc_error("error: can't find message header", header);
- return -EIO;
- }
-
- /* first process the header */
- switch (reply) {
- case IPC_GLB_REPLY_PENDING:
- trace_ipc_pending_reply("received", header);
- msg->pending = true;
- hsw->ipc.pending = true;
- return 1;
- case IPC_GLB_REPLY_SUCCESS:
- if (msg->pending) {
- trace_ipc_pending_reply("completed", header);
- sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
- msg->rx_size);
- hsw->ipc.pending = false;
- } else {
- /* copy data from the DSP */
- sst_dsp_outbox_read(hsw->dsp, msg->rx_data,
- msg->rx_size);
- }
- break;
- /* these will be rare - but useful for debug */
- case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE:
- trace_ipc_error("error: unknown message type", header);
- msg->errno = -EBADMSG;
- break;
- case IPC_GLB_REPLY_OUT_OF_RESOURCES:
- trace_ipc_error("error: out of resources", header);
- msg->errno = -ENOMEM;
- break;
- case IPC_GLB_REPLY_BUSY:
- trace_ipc_error("error: reply busy", header);
- msg->errno = -EBUSY;
- break;
- case IPC_GLB_REPLY_FAILURE:
- trace_ipc_error("error: reply failure", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_STAGE_UNINITIALIZED:
- trace_ipc_error("error: stage uninitialized", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_NOT_FOUND:
- trace_ipc_error("error: reply not found", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_SOURCE_NOT_STARTED:
- trace_ipc_error("error: source not started", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_INVALID_REQUEST:
- trace_ipc_error("error: invalid request", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_ERROR_INVALID_PARAM:
- trace_ipc_error("error: invalid parameter", header);
- msg->errno = -EINVAL;
- break;
- default:
- trace_ipc_error("error: unknown reply", header);
- msg->errno = -EINVAL;
- break;
- }
-
- /* update any stream states */
- if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
- hsw_stream_update(hsw, msg);
-
- /* wake up and return the error if we have waiters on this message ? */
- list_del(&msg->list);
- sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg);
-
- return 1;
-}
-
-static int hsw_module_message(struct sst_hsw *hsw, u32 header)
-{
- u32 operation, module_id;
- int handled = 0;
-
- operation = msg_get_module_operation(header);
- module_id = msg_get_module_id(header);
- dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n",
- header);
- dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n",
- operation, module_id);
-
- switch (operation) {
- case IPC_MODULE_NOTIFICATION:
- dev_dbg(hsw->dev, "module notification received");
- handled = 1;
- break;
- default:
- handled = hsw_process_reply(hsw, header);
- break;
- }
-
- return handled;
-}
-
-static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
-{
- u32 stream_msg, stream_id, stage_type;
- struct sst_hsw_stream *stream;
- int handled = 0;
-
- stream_msg = msg_get_stream_type(header);
- stream_id = msg_get_stream_id(header);
- stage_type = msg_get_stage_type(header);
-
- stream = get_stream_by_id(hsw, stream_id);
- if (stream == NULL)
- return handled;
-
- stream->header = header;
-
- switch (stream_msg) {
- case IPC_STR_STAGE_MESSAGE:
- dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n",
- header);
- break;
- case IPC_STR_NOTIFICATION:
- schedule_work(&stream->notify_work);
- break;
- default:
- /* handle pending message complete request */
- handled = hsw_process_reply(hsw, header);
- break;
- }
-
- return handled;
-}
-
-static int hsw_log_message(struct sst_hsw *hsw, u32 header)
-{
- u32 operation = (header & IPC_LOG_OP_MASK) >> IPC_LOG_OP_SHIFT;
- struct sst_hsw_log_stream *stream = &hsw->log_stream;
- int ret = 1;
-
- if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) {
- dev_err(hsw->dev,
- "error: log msg not implemented 0x%8.8x\n", header);
- return 0;
- }
-
- mutex_lock(&stream->rw_mutex);
- stream->last_pos = stream->curr_pos;
- sst_dsp_inbox_read(
- hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos));
- mutex_unlock(&stream->rw_mutex);
-
- schedule_work(&stream->notify_work);
-
- return ret;
-}
-
-static int hsw_process_notification(struct sst_hsw *hsw)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 type, header;
- int handled = 1;
-
- header = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
- type = msg_get_global_type(header);
-
- trace_ipc_request("processing -->", header);
-
- /* FW Ready is a special case */
- if (!hsw->boot_complete && header & IPC_FW_READY) {
- hsw_fw_ready(hsw, header);
- return handled;
- }
-
- switch (type) {
- case IPC_GLB_GET_FW_VERSION:
- case IPC_GLB_ALLOCATE_STREAM:
- case IPC_GLB_FREE_STREAM:
- case IPC_GLB_GET_FW_CAPABILITIES:
- case IPC_GLB_REQUEST_DUMP:
- case IPC_GLB_GET_DEVICE_FORMATS:
- case IPC_GLB_SET_DEVICE_FORMATS:
- case IPC_GLB_ENTER_DX_STATE:
- case IPC_GLB_GET_MIXER_STREAM_INFO:
- case IPC_GLB_MAX_IPC_MESSAGE_TYPE:
- case IPC_GLB_RESTORE_CONTEXT:
- case IPC_GLB_SHORT_REPLY:
- dev_err(hsw->dev, "error: message type %d header 0x%x\n",
- type, header);
- break;
- case IPC_GLB_STREAM_MESSAGE:
- handled = hsw_stream_message(hsw, header);
- break;
- case IPC_GLB_DEBUG_LOG_MESSAGE:
- handled = hsw_log_message(hsw, header);
- break;
- case IPC_GLB_MODULE_OPERATION:
- handled = hsw_module_message(hsw, header);
- break;
- default:
- dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n",
- type, header);
- break;
- }
-
- return handled;
-}
-
-static irqreturn_t hsw_irq_thread(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
- struct sst_generic_ipc *ipc = &hsw->ipc;
- u32 ipcx, ipcd;
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
-
- ipcx = sst_dsp_ipc_msg_rx(hsw->dsp);
- ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
-
- /* reply message from DSP */
- if (ipcx & SST_IPCX_DONE) {
-
- /* Handle Immediate reply from DSP Core */
- hsw_process_reply(hsw, ipcx);
-
- /* clear DONE bit - tell DSP we have completed */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
- SST_IPCX_DONE, 0);
-
- /* unmask Done interrupt */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_DONE, 0);
- }
-
- /* new message from DSP */
- if (ipcd & SST_IPCD_BUSY) {
-
- /* Handle Notification and Delayed reply from DSP Core */
- hsw_process_notification(hsw);
-
- /* clear BUSY bit and set DONE bit - accept new messages */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
- SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
-
- /* unmask busy interrupt */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_BUSY, 0);
- }
-
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- /* continue to send any remaining messages... */
- queue_kthread_work(&ipc->kworker, &ipc->kwork);
-
- return IRQ_HANDLED;
-}
-
-int sst_hsw_fw_get_version(struct sst_hsw *hsw,
- struct sst_hsw_ipc_fw_version *version)
-{
- int ret;
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc,
- IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
- NULL, 0, version, sizeof(*version));
- if (ret < 0)
- dev_err(hsw->dev, "error: get version failed\n");
-
- return ret;
-}
-
-/* Mixer Controls */
-int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 stage_id, u32 channel, u32 *volume)
-{
- if (channel > 1)
- return -EINVAL;
-
- sst_dsp_read(hsw->dsp, volume,
- stream->reply.volume_register_address[channel],
- sizeof(*volume));
-
- return 0;
-}
-
-/* stream volume */
-int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume)
-{
- struct sst_hsw_ipc_volume_req *req;
- u32 header;
- int ret;
-
- trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
-
- if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
- return -EINVAL;
-
- header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
- IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
- header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
- header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
- header |= (stage_id << IPC_STG_ID_SHIFT);
-
- req = &stream->vol_req;
- req->target_volume = volume;
-
- /* set both at same time ? */
- if (channel == SST_HSW_CHANNELS_ALL) {
- if (hsw->mute[0] && hsw->mute[1]) {
- hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
- return 0;
- } else if (hsw->mute[0])
- req->channel = 1;
- else if (hsw->mute[1])
- req->channel = 0;
- else
- req->channel = SST_HSW_CHANNELS_ALL;
- } else {
- /* set only 1 channel */
- if (hsw->mute[channel]) {
- hsw->mute_volume[channel] = volume;
- return 0;
- }
- req->channel = channel;
- }
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req,
- sizeof(*req), NULL, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: set stream volume failed\n");
- return ret;
- }
-
- return 0;
-}
-
-int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 *volume)
-{
- if (channel > 1)
- return -EINVAL;
-
- sst_dsp_read(hsw->dsp, volume,
- hsw->mixer_info.volume_register_address[channel],
- sizeof(*volume));
-
- return 0;
-}
-
-/* global mixer volume */
-int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 volume)
-{
- struct sst_hsw_ipc_volume_req req;
- u32 header;
- int ret;
-
- trace_ipc_request("set mixer volume", volume);
-
- if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
- return -EINVAL;
-
- /* set both at same time ? */
- if (channel == SST_HSW_CHANNELS_ALL) {
- if (hsw->mute[0] && hsw->mute[1]) {
- hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
- return 0;
- } else if (hsw->mute[0])
- req.channel = 1;
- else if (hsw->mute[1])
- req.channel = 0;
- else
- req.channel = SST_HSW_CHANNELS_ALL;
- } else {
- /* set only 1 channel */
- if (hsw->mute[channel]) {
- hsw->mute_volume[channel] = volume;
- return 0;
- }
- req.channel = channel;
- }
-
- header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
- IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
- header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT);
- header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
- header |= (stage_id << IPC_STG_ID_SHIFT);
-
- req.curve_duration = hsw->curve_duration;
- req.curve_type = hsw->curve_type;
- req.target_volume = volume;
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req,
- sizeof(req), NULL, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: set mixer volume failed\n");
- return ret;
- }
-
- return 0;
-}
-
-/* Stream API */
-struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
- u32 (*notify_position)(struct sst_hsw_stream *stream, void *data),
- void *data)
-{
- struct sst_hsw_stream *stream;
- struct sst_dsp *sst = hsw->dsp;
- unsigned long flags;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (stream == NULL)
- return NULL;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- stream->reply.stream_hw_id = INVALID_STREAM_HW_ID;
- list_add(&stream->node, &hsw->stream_list);
- stream->notify_position = notify_position;
- stream->pdata = data;
- stream->hsw = hsw;
- stream->host_id = id;
-
- /* work to process notification messages */
- INIT_WORK(&stream->notify_work, hsw_notification_work);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return stream;
-}
-
-int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
-{
- u32 header;
- int ret = 0;
- struct sst_dsp *sst = hsw->dsp;
- unsigned long flags;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to free, ignore it.\n");
- return 0;
- }
-
- /* dont free DSP streams that are not commited */
- if (!stream->commited)
- goto out;
-
- trace_ipc_request("stream free", stream->host_id);
-
- stream->free_req.stream_id = stream->reply.stream_hw_id;
- header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req,
- sizeof(stream->free_req), NULL, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: free stream %d failed\n",
- stream->free_req.stream_id);
- return -EAGAIN;
- }
-
- trace_hsw_stream_free_req(stream, &stream->free_req);
-
-out:
- cancel_work_sync(&stream->notify_work);
- spin_lock_irqsave(&sst->spinlock, flags);
- list_del(&stream->node);
- kfree(stream);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return ret;
-}
-
-int sst_hsw_stream_set_bits(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set bits\n");
- return -EINVAL;
- }
-
- stream->request.format.bitdepth = bits;
- return 0;
-}
-
-int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, int channels)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set channels\n");
- return -EINVAL;
- }
-
- stream->request.format.ch_num = channels;
- return 0;
-}
-
-int sst_hsw_stream_set_rate(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, int rate)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set rate\n");
- return -EINVAL;
- }
-
- stream->request.format.frequency = rate;
- return 0;
-}
-
-int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 map,
- enum sst_hsw_channel_config config)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set map\n");
- return -EINVAL;
- }
-
- stream->request.format.map = map;
- stream->request.format.config = config;
- return 0;
-}
-
-int sst_hsw_stream_set_style(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, enum sst_hsw_interleaving style)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set style\n");
- return -EINVAL;
- }
-
- stream->request.format.style = style;
- return 0;
-}
-
-int sst_hsw_stream_set_valid(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 bits)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set valid bits\n");
- return -EINVAL;
- }
-
- stream->request.format.valid_bit = bits;
- return 0;
-}
-
-/* Stream Configuration */
-int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_stream_path_id path_id,
- enum sst_hsw_stream_type stream_type,
- enum sst_hsw_stream_format format_id)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set format\n");
- return -EINVAL;
- }
-
- stream->request.path_id = path_id;
- stream->request.stream_type = stream_type;
- stream->request.format_id = format_id;
-
- trace_hsw_stream_alloc_request(stream, &stream->request);
-
- return 0;
-}
-
-int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 ring_pt_address, u32 num_pages,
- u32 ring_size, u32 ring_offset, u32 ring_first_pfn)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for buffer\n");
- return -EINVAL;
- }
-
- stream->request.ringinfo.ring_pt_address = ring_pt_address;
- stream->request.ringinfo.num_pages = num_pages;
- stream->request.ringinfo.ring_size = ring_size;
- stream->request.ringinfo.ring_offset = ring_offset;
- stream->request.ringinfo.ring_first_pfn = ring_first_pfn;
-
- trace_hsw_stream_buffer(stream);
-
- return 0;
-}
-
-int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
-{
- struct sst_hsw_module_map *map = &stream->request.map;
- struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
- struct sst_module *module = runtime->module;
-
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set module\n");
- return -EINVAL;
- }
-
- /* only support initial module atm */
- map->module_entries_count = 1;
- map->module_entries[0].module_id = module->id;
- map->module_entries[0].entry_point = module->entry;
-
- stream->request.persistent_mem.offset =
- sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
- stream->request.persistent_mem.size = module->persistent_size;
-
- stream->request.scratch_mem.offset =
- sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
- stream->request.scratch_mem.size = dsp->scratch_size;
-
- dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
- runtime->id);
- dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
- stream->request.persistent_mem.offset,
- stream->request.persistent_mem.size);
- dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
- stream->request.scratch_mem.offset,
- stream->request.scratch_mem.size);
-
- return 0;
-}
-
-int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
-{
- struct sst_hsw_ipc_stream_alloc_req *str_req = &stream->request;
- struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply;
- u32 header;
- int ret;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n");
- return 0;
- }
-
- if (stream->commited) {
- dev_warn(hsw->dev, "warning: stream is already committed, ignore it.\n");
- return 0;
- }
-
- trace_ipc_request("stream alloc", stream->host_id);
-
- header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req,
- sizeof(*str_req), reply, sizeof(*reply));
- if (ret < 0) {
- dev_err(hsw->dev, "error: stream commit failed\n");
- return ret;
- }
-
- stream->commited = 1;
- trace_hsw_stream_alloc_reply(stream);
-
- return 0;
-}
-
-snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- return stream->old_position;
-}
-
-void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, snd_pcm_uframes_t val)
-{
- stream->old_position = val;
-}
-
-bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- return stream->play_silence;
-}
-
-void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, bool val)
-{
- stream->play_silence = val;
-}
-
-/* Stream Information - these calls could be inline but we want the IPC
- ABI to be opaque to client PCM drivers to cope with any future ABI changes */
-int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
-{
- struct sst_hsw_ipc_stream_info_reply *reply;
- u32 header;
- int ret;
-
- reply = &hsw->mixer_info;
- header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO);
-
- trace_ipc_request("get global mixer info", 0);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0,
- reply, sizeof(*reply));
- if (ret < 0) {
- dev_err(hsw->dev, "error: get stream info failed\n");
- return ret;
- }
-
- trace_hsw_mixer_info_reply(reply);
-
- return 0;
-}
-
-/* Send stream command */
-static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type,
- int stream_id, int wait)
-{
- u32 header;
-
- header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type);
- header |= (stream_id << IPC_STR_ID_SHIFT);
-
- if (wait)
- return sst_ipc_tx_message_wait(&hsw->ipc, header,
- NULL, 0, NULL, 0);
- else
- return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0);
-}
-
-/* Stream ALSA trigger operations */
-int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait)
-{
- int ret;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to pause, ignore it.\n");
- return 0;
- }
-
- trace_ipc_request("stream pause", stream->reply.stream_hw_id);
-
- ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE,
- stream->reply.stream_hw_id, wait);
- if (ret < 0)
- dev_err(hsw->dev, "error: failed to pause stream %d\n",
- stream->reply.stream_hw_id);
-
- return ret;
-}
-
-int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait)
-{
- int ret;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to resume, ignore it.\n");
- return 0;
- }
-
- trace_ipc_request("stream resume", stream->reply.stream_hw_id);
-
- ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME,
- stream->reply.stream_hw_id, wait);
- if (ret < 0)
- dev_err(hsw->dev, "error: failed to resume stream %d\n",
- stream->reply.stream_hw_id);
-
- return ret;
-}
-
-int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
-{
- int ret, tries = 10;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to reset, ignore it.\n");
- return 0;
- }
-
- /* dont reset streams that are not commited */
- if (!stream->commited)
- return 0;
-
- /* wait for pause to complete before we reset the stream */
- while (stream->running && tries--)
- msleep(1);
- if (!tries) {
- dev_err(hsw->dev, "error: reset stream %d still running\n",
- stream->reply.stream_hw_id);
- return -EINVAL;
- }
-
- trace_ipc_request("stream reset", stream->reply.stream_hw_id);
-
- ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET,
- stream->reply.stream_hw_id, 1);
- if (ret < 0)
- dev_err(hsw->dev, "error: failed to reset stream %d\n",
- stream->reply.stream_hw_id);
- return ret;
-}
-
-/* Stream pointer positions */
-u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- u32 rpos;
-
- sst_dsp_read(hsw->dsp, &rpos,
- stream->reply.read_position_register_address, sizeof(rpos));
-
- return rpos;
-}
-
-/* Stream presentation (monotonic) positions */
-u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- u64 ppos;
-
- sst_dsp_read(hsw->dsp, &ppos,
- stream->reply.presentation_position_register_address,
- sizeof(ppos));
-
- return ppos;
-}
-
-/* physical BE config */
-int sst_hsw_device_set_config(struct sst_hsw *hsw,
- enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
- enum sst_hsw_device_mode mode, u32 clock_divider)
-{
- struct sst_hsw_ipc_device_config_req config;
- u32 header;
- int ret;
-
- trace_ipc_request("set device config", dev);
-
- hsw->dx_dev = config.ssp_interface = dev;
- hsw->dx_mclk = config.clock_frequency = mclk;
- hsw->dx_mode = config.mode = mode;
- hsw->dx_clock_divider = config.clock_divider = clock_divider;
- if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
- config.channels = 4;
- else
- config.channels = 2;
-
- trace_hsw_device_config_req(&config);
-
- header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config,
- sizeof(config), NULL, 0);
- if (ret < 0)
- dev_err(hsw->dev, "error: set device formats failed\n");
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_hsw_device_set_config);
-
-/* DX Config */
-int sst_hsw_dx_set_state(struct sst_hsw *hsw,
- enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
-{
- u32 header, state_;
- int ret, item;
-
- header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
- state_ = state;
-
- trace_ipc_request("PM enter Dx state", state);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_,
- sizeof(state_), dx, sizeof(*dx));
- if (ret < 0) {
- dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
- return ret;
- }
-
- for (item = 0; item < dx->entries_no; item++) {
- dev_dbg(hsw->dev,
- "Item[%d] offset[%x] - size[%x] - source[%x]\n",
- item, dx->mem_info[item].offset,
- dx->mem_info[item].size,
- dx->mem_info[item].source);
- }
- dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
- dx->entries_no, state);
-
- return ret;
-}
-
-struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
- int mod_id, int offset)
-{
- struct sst_dsp *dsp = hsw->dsp;
- struct sst_module *module;
- struct sst_module_runtime *runtime;
- int err;
-
- module = sst_module_get_from_id(dsp, mod_id);
- if (module == NULL) {
- dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
- mod_id);
- return NULL;
- }
-
- runtime = sst_module_runtime_new(module, mod_id, NULL);
- if (runtime == NULL) {
- dev_err(dsp->dev, "error: failed to create module %d runtime\n",
- mod_id);
- return NULL;
- }
-
- err = sst_module_runtime_alloc_blocks(runtime, offset);
- if (err < 0) {
- dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
- mod_id);
- sst_module_runtime_free(runtime);
- return NULL;
- }
-
- dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
- mod_id);
- return runtime;
-}
-
-void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
-{
- sst_module_runtime_free_blocks(runtime);
- sst_module_runtime_free(runtime);
-}
-
-#ifdef CONFIG_PM
-static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 item, offset, size;
- int ret = 0;
-
- trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
-
- if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
- dev_err(hsw->dev,
- "error: number of FW context regions greater than %d\n",
- SST_HSW_MAX_DX_REGIONS);
- memset(&hsw->dx, 0, sizeof(hsw->dx));
- return -EINVAL;
- }
-
- ret = sst_dsp_dma_get_channel(sst, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
- return ret;
- }
-
- /* set on-demond mode on engine 0 channel 3 */
- sst_dsp_shim_update_bits(sst, SST_HMDC,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
-
- for (item = 0; item < hsw->dx.entries_no; item++) {
- if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
- && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
- && hsw->dx.mem_info[item].offset <
- DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
-
- offset = hsw->dx.mem_info[item].offset
- - DSP_DRAM_ADDR_OFFSET;
- size = (hsw->dx.mem_info[item].size + 3) & (~3);
-
- ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
- sst->addr.lpe_base + offset, size);
- if (ret < 0) {
- dev_err(hsw->dev,
- "error: FW context dump failed\n");
- memset(&hsw->dx, 0, sizeof(hsw->dx));
- goto out;
- }
- }
- }
-
-out:
- sst_dsp_dma_put_channel(sst);
- return ret;
-}
-
-static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 item, offset, size;
- int ret;
-
- for (item = 0; item < hsw->dx.entries_no; item++) {
- if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
- && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
- && hsw->dx.mem_info[item].offset <
- DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
-
- offset = hsw->dx.mem_info[item].offset
- - DSP_DRAM_ADDR_OFFSET;
- size = (hsw->dx.mem_info[item].size + 3) & (~3);
-
- ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
- hsw->dx_context_paddr + offset, size);
- if (ret < 0) {
- dev_err(hsw->dev,
- "error: FW context restore failed\n");
- return ret;
- }
- }
- }
-
- return 0;
-}
-
-int sst_hsw_dsp_load(struct sst_hsw *hsw)
-{
- struct sst_dsp *dsp = hsw->dsp;
- struct sst_fw *sst_fw, *t;
- int ret;
-
- dev_dbg(hsw->dev, "loading audio DSP....");
-
- ret = sst_dsp_wake(dsp);
- if (ret < 0) {
- dev_err(hsw->dev, "error: failed to wake audio DSP\n");
- return -ENODEV;
- }
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
- return ret;
- }
-
- list_for_each_entry_safe_reverse(sst_fw, t, &dsp->fw_list, list) {
- ret = sst_fw_reload(sst_fw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: SST FW reload failed\n");
- sst_dsp_dma_put_channel(dsp);
- return -ENOMEM;
- }
- }
- ret = sst_block_alloc_scratch(hsw->dsp);
- if (ret < 0)
- return -EINVAL;
-
- sst_dsp_dma_put_channel(dsp);
- return 0;
-}
-
-static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
-{
- struct sst_dsp *dsp = hsw->dsp;
- int ret;
-
- dev_dbg(hsw->dev, "restoring audio DSP....");
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
- return ret;
- }
-
- ret = sst_hsw_dx_state_restore(hsw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: SST FW context restore failed\n");
- sst_dsp_dma_put_channel(dsp);
- return -ENOMEM;
- }
- sst_dsp_dma_put_channel(dsp);
-
- /* wait for DSP boot completion */
- sst_dsp_boot(dsp);
-
- return ret;
-}
-
-int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
-{
- int ret;
-
- dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
-
- ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
- if (ret < 0)
- return ret;
-
- sst_dsp_stall(hsw->dsp);
-
- ret = sst_hsw_dx_state_dump(hsw);
- if (ret < 0)
- return ret;
-
- sst_ipc_drop_all(&hsw->ipc);
-
- return 0;
-}
-
-int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
-{
- struct sst_fw *sst_fw, *t;
- struct sst_dsp *dsp = hsw->dsp;
-
- list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
- sst_fw_unload(sst_fw);
- }
- sst_block_free_scratch(dsp);
-
- hsw->boot_complete = false;
-
- sst_dsp_sleep(dsp);
-
- return 0;
-}
-
-int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
-{
- struct device *dev = hsw->dev;
- int ret;
-
- dev_dbg(dev, "audio dsp runtime resume\n");
-
- if (hsw->boot_complete)
- return 1; /* tell caller no action is required */
-
- ret = sst_hsw_dsp_restore(hsw);
- if (ret < 0)
- dev_err(dev, "error: audio DSP boot failure\n");
-
- sst_hsw_init_module_state(hsw);
-
- ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (ret == 0) {
- dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
- return -EIO;
- }
-
- /* Set ADSP SSP port settings - sadly the FW does not store SSP port
- settings as part of the PM context. */
- ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk,
- hsw->dx_mode, hsw->dx_clock_divider);
- if (ret < 0)
- dev_err(dev, "error: SSP re-initialization failed\n");
-
- return ret;
-}
-#endif
-
-struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
-{
- return hsw->dsp;
-}
-
-void sst_hsw_init_module_state(struct sst_hsw *hsw)
-{
- struct sst_module *module;
- enum sst_hsw_module_id id;
-
- /* the base fw contains several modules */
- for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) {
- module = sst_module_get_from_id(hsw->dsp, id);
- if (module) {
- /* module waves is active only after being enabled */
- if (id == SST_HSW_MODULE_WAVES)
- module->state = SST_MODULE_STATE_INITIALIZED;
- else
- module->state = SST_MODULE_STATE_ACTIVE;
- }
- }
-}
-
-bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id)
-{
- struct sst_module *module;
-
- module = sst_module_get_from_id(hsw->dsp, module_id);
- if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED)
- return false;
- else
- return true;
-}
-
-bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id)
-{
- struct sst_module *module;
-
- module = sst_module_get_from_id(hsw->dsp, module_id);
- if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE)
- return true;
- else
- return false;
-}
-
-void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
-{
- hsw->enabled_modules_rtd3 |= (1 << module_id);
-}
-
-void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id)
-{
- hsw->enabled_modules_rtd3 &= ~(1 << module_id);
-}
-
-bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
-{
- return hsw->enabled_modules_rtd3 & (1 << module_id);
-}
-
-void sst_hsw_reset_param_buf(struct sst_hsw *hsw)
-{
- hsw->param_idx_w = 0;
- hsw->param_idx_r = 0;
- memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf));
-}
-
-int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf)
-{
- /* save line to the first available position of param buffer */
- if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) {
- dev_warn(hsw->dev, "warning: param buffer overflow!\n");
- return -EPERM;
- }
- memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT);
- hsw->param_idx_w++;
- return 0;
-}
-
-int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf)
-{
- u8 id = 0;
-
- /* read the first matching line from param buffer */
- while (hsw->param_idx_r < WAVES_PARAM_LINES) {
- id = hsw->param_buf[hsw->param_idx_r][0];
- hsw->param_idx_r++;
- if (buf[0] == id) {
- memcpy(buf, hsw->param_buf[hsw->param_idx_r],
- WAVES_PARAM_COUNT);
- break;
- }
- }
- if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) {
- dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n");
- hsw->param_idx_r = 0;
- return 0;
- }
- return 0;
-}
-
-int sst_hsw_launch_param_buf(struct sst_hsw *hsw)
-{
- int ret, idx;
-
- if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
- dev_dbg(hsw->dev, "module waves is not active\n");
- return 0;
- }
-
- /* put all param lines to DSP through ipc */
- for (idx = 0; idx < hsw->param_idx_w; idx++) {
- ret = sst_hsw_module_set_param(hsw,
- SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0],
- WAVES_PARAM_COUNT, hsw->param_buf[idx]);
- if (ret < 0)
- return ret;
- }
- return 0;
-}
-
-int sst_hsw_module_load(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id, char *name)
-{
- int ret = 0;
- const struct firmware *fw = NULL;
- struct sst_fw *hsw_sst_fw;
- struct sst_module *module;
- struct device *dev = hsw->dev;
- struct sst_dsp *dsp = hsw->dsp;
-
- dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name);
-
- module = sst_module_get_from_id(dsp, module_id);
- if (module == NULL) {
- /* loading for the first time */
- if (module_id == SST_HSW_MODULE_BASE_FW) {
- /* for base module: use fw requested in acpi probe */
- fw = dsp->pdata->fw;
- if (!fw) {
- dev_err(dev, "request Base fw failed\n");
- return -ENODEV;
- }
- } else {
- /* try and load any other optional modules if they are
- * available. Use dev_info instead of dev_err in case
- * request firmware failed */
- ret = request_firmware(&fw, name, dev);
- if (ret) {
- dev_info(dev, "fw image %s not available(%d)\n",
- name, ret);
- return ret;
- }
- }
- hsw_sst_fw = sst_fw_new(dsp, fw, hsw);
- if (hsw_sst_fw == NULL) {
- dev_err(dev, "error: failed to load firmware\n");
- ret = -ENOMEM;
- goto out;
- }
- module = sst_module_get_from_id(dsp, module_id);
- if (module == NULL) {
- dev_err(dev, "error: no module %d in firmware %s\n",
- module_id, name);
- }
- } else
- dev_info(dev, "module %d (%s) already loaded\n",
- module_id, name);
-out:
- /* release fw, but base fw should be released by acpi driver */
- if (fw && module_id != SST_HSW_MODULE_BASE_FW)
- release_firmware(fw);
-
- return ret;
-}
-
-int sst_hsw_module_enable(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id)
-{
- int ret;
- u32 header = 0;
- struct sst_hsw_ipc_module_config config;
- struct sst_module *module;
- struct sst_module_runtime *runtime;
- struct device *dev = hsw->dev;
- struct sst_dsp *dsp = hsw->dsp;
-
- if (!sst_hsw_is_module_loaded(hsw, module_id)) {
- dev_dbg(dev, "module %d not loaded\n", module_id);
- return 0;
- }
-
- if (sst_hsw_is_module_active(hsw, module_id)) {
- dev_info(dev, "module %d already enabled\n", module_id);
- return 0;
- }
-
- module = sst_module_get_from_id(dsp, module_id);
- if (module == NULL) {
- dev_err(dev, "module %d not valid\n", module_id);
- return -ENXIO;
- }
-
- runtime = sst_module_runtime_get_from_id(module, module_id);
- if (runtime == NULL) {
- dev_err(dev, "runtime %d not valid", module_id);
- return -ENXIO;
- }
-
- header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
- IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) |
- IPC_MODULE_ID(module_id);
- dev_dbg(dev, "module enable header: %x\n", header);
-
- config.map.module_entries_count = 1;
- config.map.module_entries[0].module_id = module->id;
- config.map.module_entries[0].entry_point = module->entry;
-
- config.persistent_mem.offset =
- sst_dsp_get_offset(dsp,
- runtime->persistent_offset, SST_MEM_DRAM);
- config.persistent_mem.size = module->persistent_size;
-
- config.scratch_mem.offset =
- sst_dsp_get_offset(dsp,
- dsp->scratch_offset, SST_MEM_DRAM);
- config.scratch_mem.size = module->scratch_size;
- dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x",
- config.map.module_entries[0].module_id,
- config.persistent_mem.size,
- config.persistent_mem.offset,
- config.scratch_mem.size, config.scratch_mem.offset,
- config.map.module_entries[0].entry_point);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
- &config, sizeof(config), NULL, 0);
- if (ret < 0)
- dev_err(dev, "ipc: module enable failed - %d\n", ret);
- else
- module->state = SST_MODULE_STATE_ACTIVE;
-
- return ret;
-}
-
-int sst_hsw_module_disable(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id)
-{
- int ret;
- u32 header;
- struct sst_module *module;
- struct device *dev = hsw->dev;
- struct sst_dsp *dsp = hsw->dsp;
-
- if (!sst_hsw_is_module_loaded(hsw, module_id)) {
- dev_dbg(dev, "module %d not loaded\n", module_id);
- return 0;
- }
-
- if (!sst_hsw_is_module_active(hsw, module_id)) {
- dev_info(dev, "module %d already disabled\n", module_id);
- return 0;
- }
-
- module = sst_module_get_from_id(dsp, module_id);
- if (module == NULL) {
- dev_err(dev, "module %d not valid\n", module_id);
- return -ENXIO;
- }
-
- header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
- IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) |
- IPC_MODULE_ID(module_id);
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0);
- if (ret < 0)
- dev_err(dev, "module disable failed - %d\n", ret);
- else
- module->state = SST_MODULE_STATE_INITIALIZED;
-
- return ret;
-}
-
-int sst_hsw_module_set_param(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id, u32 parameter_id,
- u32 param_size, char *param)
-{
- int ret;
- unsigned char *data = NULL;
- u32 header = 0;
- u32 payload_size = 0, transfer_parameter_size = 0;
- dma_addr_t dma_addr = 0;
- struct sst_hsw_transfer_parameter *parameter;
- struct device *dev = hsw->dev;
-
- header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
- IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) |
- IPC_MODULE_ID(module_id);
- dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header);
-
- payload_size = param_size +
- sizeof(struct sst_hsw_transfer_parameter) -
- sizeof(struct sst_hsw_transfer_list);
- dev_dbg(dev, "parameter size : %d\n", param_size);
- dev_dbg(dev, "payload size : %d\n", payload_size);
-
- if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) {
- /* short parameter, mailbox can contain data */
- dev_dbg(dev, "transfer parameter size : %d\n",
- transfer_parameter_size);
-
- transfer_parameter_size = ALIGN(payload_size, 4);
- dev_dbg(dev, "transfer parameter aligned size : %d\n",
- transfer_parameter_size);
-
- parameter = kzalloc(transfer_parameter_size, GFP_KERNEL);
- if (parameter == NULL)
- return -ENOMEM;
-
- memcpy(parameter->data, param, param_size);
- } else {
- dev_warn(dev, "transfer parameter size too large!");
- return 0;
- }
-
- parameter->parameter_id = parameter_id;
- parameter->data_size = param_size;
-
- ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
- parameter, transfer_parameter_size , NULL, 0);
- if (ret < 0)
- dev_err(dev, "ipc: module set parameter failed - %d\n", ret);
-
- kfree(parameter);
-
- if (data)
- dma_free_coherent(hsw->dsp->dma_dev,
- param_size, (void *)data, dma_addr);
-
- return ret;
-}
-
-static struct sst_dsp_device hsw_dev = {
- .thread = hsw_irq_thread,
- .ops = &haswell_ops,
-};
-
-static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
-{
- /* send the message */
- sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
- sst_dsp_ipc_msg_tx(ipc->dsp, msg->header);
-}
-
-static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
-{
- struct sst_dsp *sst = ipc->dsp;
- u32 isr, ipcd, imrx, ipcx;
-
- ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
- isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
- ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
- imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
-
- dev_err(ipc->dev,
- "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
- text, ipcx, isr, ipcd, imrx);
-}
-
-static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data,
- size_t tx_size)
-{
- memcpy(msg->tx_data, tx_data, tx_size);
-}
-
-static u64 hsw_reply_msg_match(u64 header, u64 *mask)
-{
- /* clear reply bits & status bits */
- header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
- *mask = (u64)-1;
-
- return header;
-}
-
-static bool hsw_is_dsp_busy(struct sst_dsp *dsp)
-{
- u64 ipcx;
-
- ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
- return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
-}
-
-int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_hsw_ipc_fw_version version;
- struct sst_hsw *hsw;
- struct sst_generic_ipc *ipc;
- int ret;
-
- dev_dbg(dev, "initialising Audio DSP IPC\n");
-
- hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL);
- if (hsw == NULL)
- return -ENOMEM;
-
- hsw->dev = dev;
-
- ipc = &hsw->ipc;
- ipc->dev = dev;
- ipc->ops.tx_msg = hsw_tx_msg;
- ipc->ops.shim_dbg = hsw_shim_dbg;
- ipc->ops.tx_data_copy = hsw_tx_data_copy;
- ipc->ops.reply_msg_match = hsw_reply_msg_match;
- ipc->ops.is_dsp_busy = hsw_is_dsp_busy;
-
- ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
- ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
-
- ret = sst_ipc_init(ipc);
- if (ret != 0)
- goto ipc_init_err;
-
- INIT_LIST_HEAD(&hsw->stream_list);
- init_waitqueue_head(&hsw->boot_wait);
- hsw_dev.thread_context = hsw;
-
- /* init SST shim */
- hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
- if (hsw->dsp == NULL) {
- ret = -ENODEV;
- goto dsp_new_err;
- }
-
- ipc->dsp = hsw->dsp;
-
- /* allocate DMA buffer for context storage */
- hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
- SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
- if (hsw->dx_context == NULL) {
- ret = -ENOMEM;
- goto dma_err;
- }
-
- /* keep the DSP in reset state for base FW loading */
- sst_dsp_reset(hsw->dsp);
-
- /* load base module and other modules in base firmware image */
- ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base");
- if (ret < 0)
- goto fw_err;
-
- /* try to load module waves */
- sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin");
-
- /* allocate scratch mem regions */
- ret = sst_block_alloc_scratch(hsw->dsp);
- if (ret < 0)
- goto boot_err;
-
- /* init param buffer */
- sst_hsw_reset_param_buf(hsw);
-
- /* wait for DSP boot completion */
- sst_dsp_boot(hsw->dsp);
- ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (ret == 0) {
- ret = -EIO;
- dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
- goto boot_err;
- }
-
- /* init module state after boot */
- sst_hsw_init_module_state(hsw);
-
- /* get the FW version */
- sst_hsw_fw_get_version(hsw, &version);
-
- /* get the globalmixer */
- ret = sst_hsw_mixer_get_info(hsw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: failed to get stream info\n");
- goto boot_err;
- }
-
- pdata->dsp = hsw;
- return 0;
-
-boot_err:
- sst_dsp_reset(hsw->dsp);
- sst_fw_free_all(hsw->dsp);
-fw_err:
- dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
- hsw->dx_context, hsw->dx_context_paddr);
-dma_err:
- sst_dsp_free(hsw->dsp);
-dsp_new_err:
- sst_ipc_fini(ipc);
-ipc_init_err:
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
-
-void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_hsw *hsw = pdata->dsp;
-
- sst_dsp_reset(hsw->dsp);
- sst_fw_free_all(hsw->dsp);
- dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
- hsw->dx_context, hsw->dx_context_paddr);
- sst_dsp_free(hsw->dsp);
- sst_ipc_fini(&hsw->ipc);
-}
-EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.h b/sound/soc/intel/haswell/sst-haswell-ipc.h
deleted file mode 100644
index 06d71aefa1fe..000000000000
--- a/sound/soc/intel/haswell/sst-haswell-ipc.h
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Intel SST Haswell/Broadwell IPC Support
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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 __SST_HASWELL_IPC_H
-#define __SST_HASWELL_IPC_H
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <sound/asound.h>
-
-#define SST_HSW_NO_CHANNELS 4
-#define SST_HSW_MAX_DX_REGIONS 14
-#define SST_HSW_DX_CONTEXT_SIZE (640 * 1024)
-#define SST_HSW_CHANNELS_ALL 0xffffffff
-
-#define SST_HSW_FW_LOG_CONFIG_DWORDS 12
-#define SST_HSW_GLOBAL_LOG 15
-
-/**
- * Upfront defined maximum message size that is
- * expected by the in/out communication pipes in FW.
- */
-#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400
-#define SST_HSW_MAX_INFO_SIZE 64
-#define SST_HSW_BUILD_HASH_LENGTH 40
-#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500
-#define WAVES_PARAM_COUNT 128
-#define WAVES_PARAM_LINES 160
-
-struct sst_hsw;
-struct sst_hsw_stream;
-struct sst_hsw_log_stream;
-struct sst_pdata;
-struct sst_module;
-struct sst_module_runtime;
-extern struct sst_ops haswell_ops;
-
-/* Stream Allocate Path ID */
-enum sst_hsw_stream_path_id {
- SST_HSW_STREAM_PATH_SSP0_OUT = 0,
- SST_HSW_STREAM_PATH_SSP0_IN = 1,
- SST_HSW_STREAM_PATH_MAX_PATH_ID = 2,
-};
-
-/* Stream Allocate Stream Type */
-enum sst_hsw_stream_type {
- SST_HSW_STREAM_TYPE_RENDER = 0,
- SST_HSW_STREAM_TYPE_SYSTEM = 1,
- SST_HSW_STREAM_TYPE_CAPTURE = 2,
- SST_HSW_STREAM_TYPE_LOOPBACK = 3,
- SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4,
-};
-
-/* Stream Allocate Stream Format */
-enum sst_hsw_stream_format {
- SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0,
- SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1,
- SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2,
- SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3,
-};
-
-/* Device ID */
-enum sst_hsw_device_id {
- SST_HSW_DEVICE_SSP_0 = 0,
- SST_HSW_DEVICE_SSP_1 = 1,
-};
-
-/* Device Master Clock Frequency */
-enum sst_hsw_device_mclk {
- SST_HSW_DEVICE_MCLK_OFF = 0,
- SST_HSW_DEVICE_MCLK_FREQ_6_MHZ = 1,
- SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3,
-};
-
-/* Device Clock Master */
-enum sst_hsw_device_mode {
- SST_HSW_DEVICE_CLOCK_SLAVE = 0,
- SST_HSW_DEVICE_CLOCK_MASTER = 1,
- SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
-};
-
-/* DX Power State */
-enum sst_hsw_dx_state {
- SST_HSW_DX_STATE_D0 = 0,
- SST_HSW_DX_STATE_D1 = 1,
- SST_HSW_DX_STATE_D3 = 3,
- SST_HSW_DX_STATE_MAX = 3,
-};
-
-/* Audio stream stage IDs */
-enum sst_hsw_fx_stage_id {
- SST_HSW_STAGE_ID_WAVES = 0,
- SST_HSW_STAGE_ID_DTS = 1,
- SST_HSW_STAGE_ID_DOLBY = 2,
- SST_HSW_STAGE_ID_BOOST = 3,
- SST_HSW_STAGE_ID_MAX_FX_ID
-};
-
-/* DX State Type */
-enum sst_hsw_dx_type {
- SST_HSW_DX_TYPE_FW_IMAGE = 0,
- SST_HSW_DX_TYPE_MEMORY_DUMP = 1
-};
-
-/* Volume Curve Type*/
-enum sst_hsw_volume_curve {
- SST_HSW_VOLUME_CURVE_NONE = 0,
- SST_HSW_VOLUME_CURVE_FADE = 1
-};
-
-/* Sample ordering */
-enum sst_hsw_interleaving {
- SST_HSW_INTERLEAVING_PER_CHANNEL = 0,
- SST_HSW_INTERLEAVING_PER_SAMPLE = 1,
-};
-
-/* Channel indices */
-enum sst_hsw_channel_index {
- SST_HSW_CHANNEL_LEFT = 0,
- SST_HSW_CHANNEL_CENTER = 1,
- SST_HSW_CHANNEL_RIGHT = 2,
- SST_HSW_CHANNEL_LEFT_SURROUND = 3,
- SST_HSW_CHANNEL_CENTER_SURROUND = 3,
- SST_HSW_CHANNEL_RIGHT_SURROUND = 4,
- SST_HSW_CHANNEL_LFE = 7,
- SST_HSW_CHANNEL_INVALID = 0xF,
-};
-
-/* List of supported channel maps. */
-enum sst_hsw_channel_config {
- SST_HSW_CHANNEL_CONFIG_MONO = 0, /* mono only. */
- SST_HSW_CHANNEL_CONFIG_STEREO = 1, /* L & R. */
- SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */
- SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */
- SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */
- SST_HSW_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */
- SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */
- SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */
- SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */
- SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */
- SST_HSW_CHANNEL_CONFIG_INVALID,
-};
-
-/* List of supported bit depths. */
-enum sst_hsw_bitdepth {
- SST_HSW_DEPTH_8BIT = 8,
- SST_HSW_DEPTH_16BIT = 16,
- SST_HSW_DEPTH_24BIT = 24, /* Default. */
- SST_HSW_DEPTH_32BIT = 32,
- SST_HSW_DEPTH_INVALID = 33,
-};
-
-enum sst_hsw_module_id {
- SST_HSW_MODULE_BASE_FW = 0x0,
- SST_HSW_MODULE_MP3 = 0x1,
- SST_HSW_MODULE_AAC_5_1 = 0x2,
- SST_HSW_MODULE_AAC_2_0 = 0x3,
- SST_HSW_MODULE_SRC = 0x4,
- SST_HSW_MODULE_WAVES = 0x5,
- SST_HSW_MODULE_DOLBY = 0x6,
- SST_HSW_MODULE_BOOST = 0x7,
- SST_HSW_MODULE_LPAL = 0x8,
- SST_HSW_MODULE_DTS = 0x9,
- SST_HSW_MODULE_PCM_CAPTURE = 0xA,
- SST_HSW_MODULE_PCM_SYSTEM = 0xB,
- SST_HSW_MODULE_PCM_REFERENCE = 0xC,
- SST_HSW_MODULE_PCM = 0xD,
- SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE,
- SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF,
- SST_HSW_MAX_MODULE_ID,
-};
-
-enum sst_hsw_performance_action {
- SST_HSW_PERF_START = 0,
- SST_HSW_PERF_STOP = 1,
-};
-
-struct sst_hsw_transfer_info {
- uint32_t destination; /* destination address */
- uint32_t reverse:1; /* if 1 data flows from destination */
- uint32_t size:31; /* transfer size in bytes.*/
- uint16_t first_page_offset; /* offset to data in the first page. */
- uint8_t packed_pages; /* page addresses. Each occupies 20 bits */
-} __attribute__((packed));
-
-struct sst_hsw_transfer_list {
- uint32_t transfers_count;
- struct sst_hsw_transfer_info transfers;
-} __attribute__((packed));
-
-struct sst_hsw_transfer_parameter {
- uint32_t parameter_id;
- uint32_t data_size;
- union {
- uint8_t data[1];
- struct sst_hsw_transfer_list transfer_list;
- };
-} __attribute__((packed));
-
-/* SST firmware module info */
-struct sst_hsw_module_info {
- u8 name[SST_HSW_MAX_INFO_SIZE];
- u8 version[SST_HSW_MAX_INFO_SIZE];
-} __attribute__((packed));
-
-/* Module entry point */
-struct sst_hsw_module_entry {
- enum sst_hsw_module_id module_id;
- u32 entry_point;
-} __attribute__((packed));
-
-/* Module map - alignement matches DSP */
-struct sst_hsw_module_map {
- u8 module_entries_count;
- struct sst_hsw_module_entry module_entries[1];
-} __attribute__((packed));
-
-struct sst_hsw_memory_info {
- u32 offset;
- u32 size;
-} __attribute__((packed));
-
-struct sst_hsw_fx_enable {
- struct sst_hsw_module_map module_map;
- struct sst_hsw_memory_info persistent_mem;
-} __attribute__((packed));
-
-struct sst_hsw_ipc_module_config {
- struct sst_hsw_module_map map;
- struct sst_hsw_memory_info persistent_mem;
- struct sst_hsw_memory_info scratch_mem;
-} __attribute__((packed));
-
-struct sst_hsw_get_fx_param {
- u32 parameter_id;
- u32 param_size;
-} __attribute__((packed));
-
-struct sst_hsw_perf_action {
- u32 action;
-} __attribute__((packed));
-
-struct sst_hsw_perf_data {
- u64 timestamp;
- u64 cycles;
- u64 datatime;
-} __attribute__((packed));
-
-/* FW version */
-struct sst_hsw_ipc_fw_version {
- u8 build;
- u8 minor;
- u8 major;
- u8 type;
- u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH];
- u32 fw_log_providers_hash;
-} __attribute__((packed));
-
-/* Stream ring info */
-struct sst_hsw_ipc_stream_ring {
- u32 ring_pt_address;
- u32 num_pages;
- u32 ring_size;
- u32 ring_offset;
- u32 ring_first_pfn;
-} __attribute__((packed));
-
-/* Debug Dump Log Enable Request */
-struct sst_hsw_ipc_debug_log_enable_req {
- struct sst_hsw_ipc_stream_ring ringinfo;
- u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
-} __attribute__((packed));
-
-/* Debug Dump Log Reply */
-struct sst_hsw_ipc_debug_log_reply {
- u32 log_buffer_begining;
- u32 log_buffer_size;
-} __attribute__((packed));
-
-/* Stream glitch position */
-struct sst_hsw_ipc_stream_glitch_position {
- u32 glitch_type;
- u32 present_pos;
- u32 write_pos;
-} __attribute__((packed));
-
-/* Stream get position */
-struct sst_hsw_ipc_stream_get_position {
- u32 position;
- u32 fw_cycle_count;
-} __attribute__((packed));
-
-/* Stream set position */
-struct sst_hsw_ipc_stream_set_position {
- u32 position;
- u32 end_of_buffer;
-} __attribute__((packed));
-
-/* Stream Free Request */
-struct sst_hsw_ipc_stream_free_req {
- u8 stream_id;
- u8 reserved[3];
-} __attribute__((packed));
-
-/* Set Volume Request */
-struct sst_hsw_ipc_volume_req {
- u32 channel;
- u32 target_volume;
- u64 curve_duration;
- u32 curve_type;
-} __attribute__((packed));
-
-/* Device Configuration Request */
-struct sst_hsw_ipc_device_config_req {
- u32 ssp_interface;
- u32 clock_frequency;
- u32 mode;
- u16 clock_divider;
- u8 channels;
- u8 reserved;
-} __attribute__((packed));
-
-/* Audio Data formats */
-struct sst_hsw_audio_data_format_ipc {
- u32 frequency;
- u32 bitdepth;
- u32 map;
- u32 config;
- u32 style;
- u8 ch_num;
- u8 valid_bit;
- u8 reserved[2];
-} __attribute__((packed));
-
-/* Stream Allocate Request */
-struct sst_hsw_ipc_stream_alloc_req {
- u8 path_id;
- u8 stream_type;
- u8 format_id;
- u8 reserved;
- struct sst_hsw_audio_data_format_ipc format;
- struct sst_hsw_ipc_stream_ring ringinfo;
- struct sst_hsw_module_map map;
- struct sst_hsw_memory_info persistent_mem;
- struct sst_hsw_memory_info scratch_mem;
- u32 number_of_notifications;
-} __attribute__((packed));
-
-/* Stream Allocate Reply */
-struct sst_hsw_ipc_stream_alloc_reply {
- u32 stream_hw_id;
- u32 mixer_hw_id; // returns rate ????
- u32 read_position_register_address;
- u32 presentation_position_register_address;
- u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
- u32 volume_register_address[SST_HSW_NO_CHANNELS];
-} __attribute__((packed));
-
-/* Get Mixer Stream Info */
-struct sst_hsw_ipc_stream_info_reply {
- u32 mixer_hw_id;
- u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
- u32 volume_register_address[SST_HSW_NO_CHANNELS];
-} __attribute__((packed));
-
-/* DX State Request */
-struct sst_hsw_ipc_dx_req {
- u8 state;
- u8 reserved[3];
-} __attribute__((packed));
-
-/* DX State Reply Memory Info Item */
-struct sst_hsw_ipc_dx_memory_item {
- u32 offset;
- u32 size;
- u32 source;
-} __attribute__((packed));
-
-/* DX State Reply */
-struct sst_hsw_ipc_dx_reply {
- u32 entries_no;
- struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS];
-} __attribute__((packed));
-
-struct sst_hsw_ipc_fw_version;
-
-/* SST Init & Free */
-struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length,
- u32 fw_offset);
-void sst_hsw_free(struct sst_hsw *hsw);
-int sst_hsw_fw_get_version(struct sst_hsw *hsw,
- struct sst_hsw_ipc_fw_version *version);
-u32 create_channel_map(enum sst_hsw_channel_config config);
-
-/* Stream Mixer Controls - */
-int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume);
-int sst_hsw_stream_get_volume(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume);
-
-/* Global Mixer Controls - */
-int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 volume);
-int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 *volume);
-
-/* Stream API */
-struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
- u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data),
- void *data);
-
-int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
-
-/* Stream Configuration */
-int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_stream_path_id path_id,
- enum sst_hsw_stream_type stream_type,
- enum sst_hsw_stream_format format_id);
-
-int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 ring_pt_address, u32 num_pages,
- u32 ring_size, u32 ring_offset, u32 ring_first_pfn);
-
-int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
-
-int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 bits);
-int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int rate);
-int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_bitdepth bits);
-int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, int channels);
-int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 map,
- enum sst_hsw_channel_config config);
-int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_interleaving style);
-int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 offset, u32 size);
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 offset, u32 size);
-snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, snd_pcm_uframes_t val);
-bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, bool val);
-int sst_hsw_mixer_get_info(struct sst_hsw *hsw);
-
-/* Stream ALSA trigger operations */
-int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait);
-int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait);
-int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
-
-/* Stream pointer positions */
-int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 *position);
-int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 *position);
-u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-
-/* HW port config */
-int sst_hsw_device_set_config(struct sst_hsw *hsw,
- enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
- enum sst_hsw_device_mode mode, u32 clock_divider);
-
-/* DX Config */
-int sst_hsw_dx_set_state(struct sst_hsw *hsw,
- enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx);
-
-/* init */
-int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
-void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
-struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-
-/* fw module function */
-void sst_hsw_init_module_state(struct sst_hsw *hsw);
-bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id);
-bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id);
-void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
-void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id);
-bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
-void sst_hsw_reset_param_buf(struct sst_hsw *hsw);
-int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf);
-int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf);
-int sst_hsw_launch_param_buf(struct sst_hsw *hsw);
-
-int sst_hsw_module_load(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id, char *name);
-int sst_hsw_module_enable(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id);
-int sst_hsw_module_disable(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id);
-int sst_hsw_module_set_param(struct sst_hsw *hsw,
- u32 module_id, u32 instance_id, u32 parameter_id,
- u32 param_size, char *param);
-
-/* runtime module management */
-struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
- int mod_id, int offset);
-void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
-
-/* PM */
-int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
-int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
-int sst_hsw_dsp_load(struct sst_hsw *hsw);
-int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
-
-#endif
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
deleted file mode 100644
index 1aa819c7e09b..000000000000
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ /dev/null
@@ -1,1419 +0,0 @@
-/*
- * Intel SST Haswell/Broadwell PCM Support
- *
- * Copyright (C) 2013, Intel 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.
- *
- * 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/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/pm_runtime.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/dmaengine_pcm.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
-#include <sound/compress_driver.h>
-
-#include "../haswell/sst-haswell-ipc.h"
-#include "../common/sst-dsp-priv.h"
-#include "../common/sst-dsp.h"
-
-#define HSW_PCM_COUNT 6
-#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */
-
-#define SST_OLD_POSITION(d, r, o) ((d) + \
- frames_to_bytes(r, o))
-#define SST_SAMPLES(r, x) (bytes_to_samples(r, \
- frames_to_bytes(r, (x))))
-
-/* simple volume table */
-static const u32 volume_map[] = {
- HSW_VOLUME_MAX >> 30,
- HSW_VOLUME_MAX >> 29,
- HSW_VOLUME_MAX >> 28,
- HSW_VOLUME_MAX >> 27,
- HSW_VOLUME_MAX >> 26,
- HSW_VOLUME_MAX >> 25,
- HSW_VOLUME_MAX >> 24,
- HSW_VOLUME_MAX >> 23,
- HSW_VOLUME_MAX >> 22,
- HSW_VOLUME_MAX >> 21,
- HSW_VOLUME_MAX >> 20,
- HSW_VOLUME_MAX >> 19,
- HSW_VOLUME_MAX >> 18,
- HSW_VOLUME_MAX >> 17,
- HSW_VOLUME_MAX >> 16,
- HSW_VOLUME_MAX >> 15,
- HSW_VOLUME_MAX >> 14,
- HSW_VOLUME_MAX >> 13,
- HSW_VOLUME_MAX >> 12,
- HSW_VOLUME_MAX >> 11,
- HSW_VOLUME_MAX >> 10,
- HSW_VOLUME_MAX >> 9,
- HSW_VOLUME_MAX >> 8,
- HSW_VOLUME_MAX >> 7,
- HSW_VOLUME_MAX >> 6,
- HSW_VOLUME_MAX >> 5,
- HSW_VOLUME_MAX >> 4,
- HSW_VOLUME_MAX >> 3,
- HSW_VOLUME_MAX >> 2,
- HSW_VOLUME_MAX >> 1,
- HSW_VOLUME_MAX >> 0,
-};
-
-#define HSW_PCM_PERIODS_MAX 64
-#define HSW_PCM_PERIODS_MIN 2
-
-#define HSW_PCM_DAI_ID_SYSTEM 0
-#define HSW_PCM_DAI_ID_OFFLOAD0 1
-#define HSW_PCM_DAI_ID_OFFLOAD1 2
-#define HSW_PCM_DAI_ID_LOOPBACK 3
-
-
-static const struct snd_pcm_hardware hsw_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
- SNDRV_PCM_INFO_DRAIN_TRIGGER,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
- .period_bytes_min = PAGE_SIZE,
- .period_bytes_max = (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE,
- .periods_min = HSW_PCM_PERIODS_MIN,
- .periods_max = HSW_PCM_PERIODS_MAX,
- .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
-};
-
-struct hsw_pcm_module_map {
- int dai_id;
- int stream;
- enum sst_hsw_module_id mod_id;
-};
-
-/* private data for each PCM DSP stream */
-struct hsw_pcm_data {
- int dai_id;
- struct sst_hsw_stream *stream;
- struct sst_module_runtime *runtime;
- struct sst_module_runtime_context context;
- struct snd_pcm *hsw_pcm;
- u32 volume[2];
- struct snd_pcm_substream *substream;
- struct snd_compr_stream *cstream;
- unsigned int wpos;
- struct mutex mutex;
- bool allocated;
- int persistent_offset;
-};
-
-enum hsw_pm_state {
- HSW_PM_STATE_D0 = 0,
- HSW_PM_STATE_RTD3 = 1,
- HSW_PM_STATE_D3 = 2,
-};
-
-/* private data for the driver */
-struct hsw_priv_data {
- /* runtime DSP */
- struct sst_hsw *hsw;
- struct device *dev;
- enum hsw_pm_state pm_state;
- struct snd_soc_card *soc_card;
- struct sst_module_runtime *runtime_waves; /* sound effect module */
-
- /* page tables */
- struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
-
- /* DAI data */
- struct hsw_pcm_data pcm[HSW_PCM_COUNT][2];
-};
-
-
-/* static mappings between PCMs and modules - may be dynamic in future */
-static struct hsw_pcm_module_map mod_map[] = {
- {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM},
- {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM},
- {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM},
- {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE},
- {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE},
-};
-
-static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
-
-static inline u32 hsw_mixer_to_ipc(unsigned int value)
-{
- if (value >= ARRAY_SIZE(volume_map))
- return volume_map[0];
- else
- return volume_map[value];
-}
-
-static inline unsigned int hsw_ipc_to_mixer(u32 value)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(volume_map); i++) {
- if (volume_map[i] >= value)
- return i;
- }
-
- return i - 1;
-}
-
-static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- u32 volume;
- int dai, stream;
-
- dai = mod_map[mc->reg].dai_id;
- stream = mod_map[mc->reg].stream;
- pcm_data = &pdata->pcm[dai][stream];
-
- mutex_lock(&pcm_data->mutex);
- pm_runtime_get_sync(pdata->dev);
-
- if (!pcm_data->stream) {
- pcm_data->volume[0] =
- hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- pcm_data->volume[1] =
- hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return 0;
- }
-
- if (ucontrol->value.integer.value[0] ==
- ucontrol->value.integer.value[1]) {
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- /* apply volume value to all channels */
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
- } else {
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
- }
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return 0;
-}
-
-static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- u32 volume;
- int dai, stream;
-
- dai = mod_map[mc->reg].dai_id;
- stream = mod_map[mc->reg].stream;
- pcm_data = &pdata->pcm[dai][stream];
-
- mutex_lock(&pcm_data->mutex);
- pm_runtime_get_sync(pdata->dev);
-
- if (!pcm_data->stream) {
- ucontrol->value.integer.value[0] =
- hsw_ipc_to_mixer(pcm_data->volume[0]);
- ucontrol->value.integer.value[1] =
- hsw_ipc_to_mixer(pcm_data->volume[1]);
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return 0;
- }
-
- sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume);
- ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
- sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
- ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
-
- return 0;
-}
-
-static int hsw_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- u32 volume;
-
- pm_runtime_get_sync(pdata->dev);
-
- if (ucontrol->value.integer.value[0] ==
- ucontrol->value.integer.value[1]) {
-
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
-
- } else {
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_mixer_set_volume(hsw, 0, 0, volume);
-
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
- sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
- }
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- return 0;
-}
-
-static int hsw_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- unsigned int volume = 0;
-
- pm_runtime_get_sync(pdata->dev);
- sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
- ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
-
- sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
- ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- return 0;
-}
-
-static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
-
- ucontrol->value.integer.value[0] =
- (sst_hsw_is_module_active(hsw, id) ||
- sst_hsw_is_module_enabled_rtd3(hsw, id));
- return 0;
-}
-
-static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- int ret = 0;
- enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
- bool switch_on = (bool)ucontrol->value.integer.value[0];
-
- /* if module is in RAM on the DSP, apply user settings to module through
- * ipc. If module is not in RAM on the DSP, store user setting for
- * track */
- if (sst_hsw_is_module_loaded(hsw, id)) {
- if (switch_on == sst_hsw_is_module_active(hsw, id))
- return 0;
-
- if (switch_on)
- ret = sst_hsw_module_enable(hsw, id, 0);
- else
- ret = sst_hsw_module_disable(hsw, id, 0);
- } else {
- if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id))
- return 0;
-
- if (switch_on)
- sst_hsw_set_module_enabled_rtd3(hsw, id);
- else
- sst_hsw_set_module_disabled_rtd3(hsw, id);
- }
-
- return ret;
-}
-
-static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
-
- /* return a matching line from param buffer */
- return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data);
-}
-
-static int hsw_waves_param_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- int ret;
- enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
- int param_id = ucontrol->value.bytes.data[0];
- int param_size = WAVES_PARAM_COUNT;
-
- /* clear param buffer and reset buffer index */
- if (param_id == 0xFF) {
- sst_hsw_reset_param_buf(hsw);
- return 0;
- }
-
- /* store params into buffer */
- ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data);
- if (ret < 0)
- return ret;
-
- if (sst_hsw_is_module_active(hsw, id))
- ret = sst_hsw_module_set_param(hsw, id, 0, param_id,
- param_size, ucontrol->value.bytes.data);
- return ret;
-}
-
-/* TLV used by both global and stream volumes */
-static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
-
-/* System Pin has no volume control */
-static const struct snd_kcontrol_new hsw_volume_controls[] = {
- /* Global DSP volume */
- SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
- /* Offload 0 volume */
- SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
- /* Offload 1 volume */
- SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
- /* Mic Capture volume */
- SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
- /* enable/disable module waves */
- SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
- hsw_waves_switch_get, hsw_waves_switch_put),
- /* set parameters to module waves */
- SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT,
- hsw_waves_param_get, hsw_waves_param_put),
-};
-
-/* Create DMA buffer page table for DSP */
-static int create_adsp_page_table(struct snd_pcm_substream *substream,
- struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd,
- unsigned char *dma_area, size_t size, int pcm)
-{
- struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
- int i, pages, stream = substream->stream;
-
- pages = snd_sgbuf_aligned_pages(size);
-
- dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
- dma_area, size, pages);
-
- for (i = 0; i < pages; i++) {
- u32 idx = (((i << 2) + i)) >> 1;
- u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
- u32 *pg_table;
-
- dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
-
- pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx);
-
- if (i & 1)
- *pg_table |= (pfn << 4);
- else
- *pg_table |= pfn;
- }
-
- return 0;
-}
-
-/* this may get called several times by oss emulation */
-static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- struct sst_module *module_data;
- struct sst_dsp *dsp;
- struct snd_dma_buffer *dmab;
- enum sst_hsw_stream_type stream_type;
- enum sst_hsw_stream_path_id path_id;
- u32 rate, bits, map, pages, module_id;
- u8 channels;
- int ret, dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
-
- /* check if we are being called a subsequent time */
- if (pcm_data->allocated) {
- ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
- if (ret < 0)
- dev_dbg(rtd->dev, "error: reset stream failed %d\n",
- ret);
-
- ret = sst_hsw_stream_free(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "error: free stream failed %d\n",
- ret);
- return ret;
- }
- pcm_data->allocated = false;
-
- pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
- hsw_notify_pointer, pcm_data);
- if (pcm_data->stream == NULL) {
- dev_err(rtd->dev, "error: failed to create stream\n");
- return -EINVAL;
- }
- }
-
- /* stream direction */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
- else
- path_id = SST_HSW_STREAM_PATH_SSP0_IN;
-
- /* DSP stream type depends on DAI ID */
- switch (rtd->cpu_dai->id) {
- case 0:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
- module_id = SST_HSW_MODULE_PCM_SYSTEM;
- }
- else {
- stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
- module_id = SST_HSW_MODULE_PCM_CAPTURE;
- }
- break;
- case 1:
- case 2:
- stream_type = SST_HSW_STREAM_TYPE_RENDER;
- module_id = SST_HSW_MODULE_PCM;
- break;
- case 3:
- /* path ID needs to be OUT for loopback */
- stream_type = SST_HSW_STREAM_TYPE_LOOPBACK;
- path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
- module_id = SST_HSW_MODULE_PCM_REFERENCE;
- break;
- default:
- dev_err(rtd->dev, "error: invalid DAI ID %d\n",
- rtd->cpu_dai->id);
- return -EINVAL;
- };
-
- ret = sst_hsw_stream_format(hsw, pcm_data->stream,
- path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to set format %d\n", ret);
- return ret;
- }
-
- rate = params_rate(params);
- ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate);
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not set rate %d\n", rate);
- return ret;
- }
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- bits = SST_HSW_DEPTH_16BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- bits = SST_HSW_DEPTH_32BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 24);
- break;
- case SNDRV_PCM_FORMAT_S8:
- bits = SST_HSW_DEPTH_8BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 8);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- bits = SST_HSW_DEPTH_32BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32);
- break;
- default:
- dev_err(rtd->dev, "error: invalid format %d\n",
- params_format(params));
- return -EINVAL;
- }
-
- ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits);
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not set bits %d\n", bits);
- return ret;
- }
-
- channels = params_channels(params);
- map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
- sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
- map, SST_HSW_CHANNEL_CONFIG_STEREO);
-
- ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels);
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not set channels %d\n",
- channels);
- return ret;
- }
-
- ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n",
- params_buffer_bytes(params), ret);
- return ret;
- }
-
- dmab = snd_pcm_get_dma_buf(substream);
-
- ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area,
- runtime->dma_bytes, rtd->cpu_dai->id);
- if (ret < 0)
- return ret;
-
- sst_hsw_stream_set_style(hsw, pcm_data->stream,
- SST_HSW_INTERLEAVING_PER_CHANNEL);
-
- if (runtime->dma_bytes % PAGE_SIZE)
- pages = (runtime->dma_bytes / PAGE_SIZE) + 1;
- else
- pages = runtime->dma_bytes / PAGE_SIZE;
-
- ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
- pdata->dmab[rtd->cpu_dai->id][substream->stream].addr,
- pages, runtime->dma_bytes, 0,
- snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
- return ret;
- }
-
- dsp = sst_hsw_get_dsp(hsw);
-
- module_data = sst_module_get_from_id(dsp, module_id);
- if (module_data == NULL) {
- dev_err(rtd->dev, "error: failed to get module config\n");
- return -EINVAL;
- }
-
- sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
- pcm_data->runtime);
-
- ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
- return ret;
- }
-
- if (!pcm_data->allocated) {
- /* Set previous saved volume */
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
- 0, pcm_data->volume[0]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
- 1, pcm_data->volume[1]);
- pcm_data->allocated = true;
- }
-
- ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
- if (ret < 0)
- dev_err(rtd->dev, "error: failed to pause %d\n", ret);
-
- return 0;
-}
-
-static int hsw_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw_stream *sst_stream;
- struct sst_hsw *hsw = pdata->hsw;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t pos;
- int dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
- sst_stream = pcm_data->stream;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
- sst_hsw_stream_resume(hsw, pcm_data->stream, 0);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
- sst_hsw_stream_pause(hsw, pcm_data->stream, 0);
- break;
- case SNDRV_PCM_TRIGGER_DRAIN:
- pos = runtime->control->appl_ptr % runtime->buffer_size;
- sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos);
- sst_hsw_stream_set_silence_start(hsw, sst_stream, true);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
-{
- struct hsw_pcm_data *pcm_data = data;
- struct snd_pcm_substream *substream = pcm_data->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_hsw *hsw = pdata->hsw;
- u32 pos;
- snd_pcm_uframes_t position = bytes_to_frames(runtime,
- sst_hsw_get_dsp_position(hsw, pcm_data->stream));
- unsigned char *dma_area = runtime->dma_area;
- snd_pcm_uframes_t dma_frames =
- bytes_to_frames(runtime, runtime->dma_bytes);
- snd_pcm_uframes_t old_position;
- ssize_t samples;
-
- pos = frames_to_bytes(runtime,
- (runtime->control->appl_ptr % runtime->buffer_size));
-
- dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
-
- /* SST fw don't know where to stop dma
- * So, SST driver need to clean the data which has been consumed
- */
- if (dma_area == NULL || dma_frames <= 0
- || (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- || !sst_hsw_stream_get_silence_start(hsw, stream)) {
- snd_pcm_period_elapsed(substream);
- return pos;
- }
-
- old_position = sst_hsw_stream_get_old_position(hsw, stream);
- if (position > old_position) {
- if (position < dma_frames) {
- samples = SST_SAMPLES(runtime, position - old_position);
- snd_pcm_format_set_silence(runtime->format,
- SST_OLD_POSITION(dma_area,
- runtime, old_position),
- samples);
- } else
- dev_err(rtd->dev, "PCM: position is wrong\n");
- } else {
- if (old_position < dma_frames) {
- samples = SST_SAMPLES(runtime,
- dma_frames - old_position);
- snd_pcm_format_set_silence(runtime->format,
- SST_OLD_POSITION(dma_area,
- runtime, old_position),
- samples);
- } else
- dev_err(rtd->dev, "PCM: dma_bytes is wrong\n");
- if (position < dma_frames) {
- samples = SST_SAMPLES(runtime, position);
- snd_pcm_format_set_silence(runtime->format,
- dma_area, samples);
- } else
- dev_err(rtd->dev, "PCM: position is wrong\n");
- }
- sst_hsw_stream_set_old_position(hsw, stream, position);
-
- /* let alsa know we have play a period */
- snd_pcm_period_elapsed(substream);
- return pos;
-}
-
-static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- snd_pcm_uframes_t offset;
- uint64_t ppos;
- u32 position;
- int dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
- position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
-
- offset = bytes_to_frames(runtime, position);
- ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
-
- dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
- position, ppos);
- return offset;
-}
-
-static int hsw_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- int dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
-
- mutex_lock(&pcm_data->mutex);
- pm_runtime_get_sync(pdata->dev);
-
- snd_soc_pcm_set_drvdata(rtd, pcm_data);
- pcm_data->substream = substream;
-
- snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
-
- pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
- hsw_notify_pointer, pcm_data);
- if (pcm_data->stream == NULL) {
- dev_err(rtd->dev, "error: failed to create stream\n");
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return -EINVAL;
- }
-
- mutex_unlock(&pcm_data->mutex);
- return 0;
-}
-
-static int hsw_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- int ret, dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
-
- mutex_lock(&pcm_data->mutex);
- ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret);
- goto out;
- }
-
- ret = sst_hsw_stream_free(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
- goto out;
- }
- pcm_data->allocated = 0;
- pcm_data->stream = NULL;
-
-out:
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return ret;
-}
-
-static struct snd_pcm_ops hsw_pcm_ops = {
- .open = hsw_pcm_open,
- .close = hsw_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = hsw_pcm_hw_params,
- .hw_free = hsw_pcm_hw_free,
- .trigger = hsw_pcm_trigger,
- .pointer = hsw_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
-{
- struct sst_hsw *hsw = pdata->hsw;
- struct hsw_pcm_data *pcm_data;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- /* create new runtime module, use same offset if recreated */
- pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
- mod_map[i].mod_id, pcm_data->persistent_offset);
- if (pcm_data->runtime == NULL)
- goto err;
- pcm_data->persistent_offset =
- pcm_data->runtime->persistent_offset;
- }
-
- /* create runtime blocks for module waves */
- if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
- pdata->runtime_waves = sst_hsw_runtime_module_create(hsw,
- SST_HSW_MODULE_WAVES, 0);
- if (pdata->runtime_waves == NULL)
- goto err;
- }
-
- return 0;
-
-err:
- for (--i; i >= 0; i--) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
- sst_hsw_runtime_module_free(pcm_data->runtime);
- }
-
- return -ENODEV;
-}
-
-static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
-{
- struct sst_hsw *hsw = pdata->hsw;
- struct hsw_pcm_data *pcm_data;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
- if (pcm_data->runtime){
- sst_hsw_runtime_module_free(pcm_data->runtime);
- pcm_data->runtime = NULL;
- }
- }
- if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES) &&
- pdata->runtime_waves) {
- sst_hsw_runtime_module_free(pdata->runtime_waves);
- pdata->runtime_waves = NULL;
- }
-}
-
-static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_pcm *pcm = rtd->pcm;
- struct snd_soc_platform *platform = rtd->platform;
- struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
- struct device *dev = pdata->dma_dev;
- int ret = 0;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
- pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV_SG,
- dev,
- hsw_pcm_hardware.buffer_bytes_max,
- hsw_pcm_hardware.buffer_bytes_max);
- if (ret) {
- dev_err(rtd->dev, "dma buffer allocation failed %d\n",
- ret);
- return ret;
- }
- }
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
- priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm;
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
- priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm;
-
- return ret;
-}
-
-#define HSW_FORMATS \
- (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-
-static struct snd_soc_dai_driver hsw_dais[] = {
- {
- .name = "System Pin",
- .id = HSW_PCM_DAI_ID_SYSTEM,
- .playback = {
- .stream_name = "System Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Analog Capture",
- .channels_min = 2,
- .channels_max = 4,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
- {
- /* PCM */
- .name = "Offload0 Pin",
- .id = HSW_PCM_DAI_ID_OFFLOAD0,
- .playback = {
- .stream_name = "Offload0 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = HSW_FORMATS,
- },
- },
- {
- /* PCM */
- .name = "Offload1 Pin",
- .id = HSW_PCM_DAI_ID_OFFLOAD1,
- .playback = {
- .stream_name = "Offload1 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = HSW_FORMATS,
- },
- },
- {
- .name = "Loopback Pin",
- .id = HSW_PCM_DAI_ID_LOOPBACK,
- .capture = {
- .stream_name = "Loopback Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
-};
-
-static const struct snd_soc_dapm_widget widgets[] = {
-
- /* Backend DAIs */
- SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
-
- /* Global Playback Mixer */
- SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_route graph[] = {
-
- /* Playback Mixer */
- {"Playback VMixer", NULL, "System Playback"},
- {"Playback VMixer", NULL, "Offload0 Playback"},
- {"Playback VMixer", NULL, "Offload1 Playback"},
-
- {"SSP0 CODEC OUT", NULL, "Playback VMixer"},
-
- {"Analog Capture", NULL, "SSP0 CODEC IN"},
-};
-
-static int hsw_pcm_probe(struct snd_soc_platform *platform)
-{
- struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
- struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- struct device *dma_dev, *dev;
- int i, ret = 0;
-
- if (!pdata)
- return -ENODEV;
-
- dev = platform->dev;
- dma_dev = pdata->dma_dev;
-
- priv_data->hsw = pdata->dsp;
- priv_data->dev = platform->dev;
- priv_data->pm_state = HSW_PM_STATE_D0;
- priv_data->soc_card = platform->component.card;
-
- /* allocate DSP buffer page tables */
- for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
-
- /* playback */
- if (hsw_dais[i].playback.channels_min) {
- mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex);
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
- PAGE_SIZE, &priv_data->dmab[i][0]);
- if (ret < 0)
- goto err;
- }
-
- /* capture */
- if (hsw_dais[i].capture.channels_min) {
- mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex);
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
- PAGE_SIZE, &priv_data->dmab[i][1]);
- if (ret < 0)
- goto err;
- }
- }
-
- /* allocate runtime modules */
- ret = hsw_pcm_create_modules(priv_data);
- if (ret < 0)
- goto err;
-
- /* enable runtime PM with auto suspend */
- pm_runtime_set_autosuspend_delay(platform->dev,
- SST_RUNTIME_SUSPEND_DELAY);
- pm_runtime_use_autosuspend(platform->dev);
- pm_runtime_enable(platform->dev);
- pm_runtime_idle(platform->dev);
-
- return 0;
-
-err:
- for (--i; i >= 0; i--) {
- if (hsw_dais[i].playback.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][0]);
- if (hsw_dais[i].capture.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][1]);
- }
- return ret;
-}
-
-static int hsw_pcm_remove(struct snd_soc_platform *platform)
-{
- struct hsw_priv_data *priv_data =
- snd_soc_platform_get_drvdata(platform);
- int i;
-
- pm_runtime_disable(platform->dev);
- hsw_pcm_free_modules(priv_data);
-
- for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
- if (hsw_dais[i].playback.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][0]);
- if (hsw_dais[i].capture.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][1]);
- }
-
- return 0;
-}
-
-static struct snd_soc_platform_driver hsw_soc_platform = {
- .probe = hsw_pcm_probe,
- .remove = hsw_pcm_remove,
- .ops = &hsw_pcm_ops,
- .pcm_new = hsw_pcm_new,
-};
-
-static const struct snd_soc_component_driver hsw_dai_component = {
- .name = "haswell-dai",
- .controls = hsw_volume_controls,
- .num_controls = ARRAY_SIZE(hsw_volume_controls),
- .dapm_widgets = widgets,
- .num_dapm_widgets = ARRAY_SIZE(widgets),
- .dapm_routes = graph,
- .num_dapm_routes = ARRAY_SIZE(graph),
-};
-
-static int hsw_pcm_dev_probe(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
- struct hsw_priv_data *priv_data;
- int ret;
-
- if (!sst_pdata)
- return -EINVAL;
-
- priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL);
- if (!priv_data)
- return -ENOMEM;
-
- ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
- if (ret < 0)
- return -ENODEV;
-
- priv_data->hsw = sst_pdata->dsp;
- platform_set_drvdata(pdev, priv_data);
-
- ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
- if (ret < 0)
- goto err_plat;
-
- ret = snd_soc_register_component(&pdev->dev, &hsw_dai_component,
- hsw_dais, ARRAY_SIZE(hsw_dais));
- if (ret < 0)
- goto err_comp;
-
- return 0;
-
-err_comp:
- snd_soc_unregister_platform(&pdev->dev);
-err_plat:
- sst_hsw_dsp_free(&pdev->dev, sst_pdata);
- return 0;
-}
-
-static int hsw_pcm_dev_remove(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
- sst_hsw_dsp_free(&pdev->dev, sst_pdata);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int hsw_pcm_runtime_idle(struct device *dev)
-{
- return 0;
-}
-
-static int hsw_pcm_suspend(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
-
- /* enter D3 state and stall */
- sst_hsw_dsp_runtime_suspend(hsw);
- /* free all runtime modules */
- hsw_pcm_free_modules(pdata);
- /* put the DSP to sleep, fw unloaded after runtime modules freed */
- sst_hsw_dsp_runtime_sleep(hsw);
- return 0;
-}
-
-static int hsw_pcm_runtime_suspend(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
- int ret;
-
- if (pdata->pm_state >= HSW_PM_STATE_RTD3)
- return 0;
-
- /* fw modules will be unloaded on RTD3, set flag to track */
- if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
- ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0);
- if (ret < 0)
- return ret;
- sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
- }
- hsw_pcm_suspend(dev);
- pdata->pm_state = HSW_PM_STATE_RTD3;
-
- return 0;
-}
-
-static int hsw_pcm_runtime_resume(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
- int ret;
-
- if (pdata->pm_state != HSW_PM_STATE_RTD3)
- return 0;
-
- ret = sst_hsw_dsp_load(hsw);
- if (ret < 0) {
- dev_err(dev, "failed to reload %d\n", ret);
- return ret;
- }
-
- ret = hsw_pcm_create_modules(pdata);
- if (ret < 0) {
- dev_err(dev, "failed to create modules %d\n", ret);
- return ret;
- }
-
- ret = sst_hsw_dsp_runtime_resume(hsw);
- if (ret < 0)
- return ret;
- else if (ret == 1) /* no action required */
- return 0;
-
- /* check flag when resume */
- if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) {
- ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0);
- if (ret < 0)
- return ret;
- /* put parameters from buffer to dsp */
- ret = sst_hsw_launch_param_buf(hsw);
- if (ret < 0)
- return ret;
- /* unset flag */
- sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
- }
-
- pdata->pm_state = HSW_PM_STATE_D0;
- return ret;
-}
-
-#else
-#define hsw_pcm_runtime_idle NULL
-#define hsw_pcm_runtime_suspend NULL
-#define hsw_pcm_runtime_resume NULL
-#endif
-
-#ifdef CONFIG_PM
-
-static void hsw_pcm_complete(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
- struct hsw_pcm_data *pcm_data;
- int i, err;
-
- if (pdata->pm_state != HSW_PM_STATE_D3)
- return;
-
- err = sst_hsw_dsp_load(hsw);
- if (err < 0) {
- dev_err(dev, "failed to reload %d\n", err);
- return;
- }
-
- err = hsw_pcm_create_modules(pdata);
- if (err < 0) {
- dev_err(dev, "failed to create modules %d\n", err);
- return;
- }
-
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- if (!pcm_data->substream)
- continue;
-
- err = sst_module_runtime_restore(pcm_data->runtime,
- &pcm_data->context);
- if (err < 0)
- dev_err(dev, "failed to restore context for PCM %d\n", i);
- }
-
- snd_soc_resume(pdata->soc_card->dev);
-
- err = sst_hsw_dsp_runtime_resume(hsw);
- if (err < 0)
- return;
- else if (err == 1) /* no action required */
- return;
-
- pdata->pm_state = HSW_PM_STATE_D0;
- return;
-}
-
-static int hsw_pcm_prepare(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct hsw_pcm_data *pcm_data;
- int i, err;
-
- if (pdata->pm_state == HSW_PM_STATE_D3)
- return 0;
- else if (pdata->pm_state == HSW_PM_STATE_D0) {
- /* suspend all active streams */
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- if (!pcm_data->substream)
- continue;
- dev_dbg(dev, "suspending pcm %d\n", i);
- snd_pcm_suspend_all(pcm_data->hsw_pcm);
-
- /* We need to wait until the DSP FW stops the streams */
- msleep(2);
- }
-
- /* preserve persistent memory */
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- if (!pcm_data->substream)
- continue;
-
- dev_dbg(dev, "saving context pcm %d\n", i);
- err = sst_module_runtime_save(pcm_data->runtime,
- &pcm_data->context);
- if (err < 0)
- dev_err(dev, "failed to save context for PCM %d\n", i);
- }
- hsw_pcm_suspend(dev);
- }
-
- snd_soc_suspend(pdata->soc_card->dev);
- snd_soc_poweroff(pdata->soc_card->dev);
-
- pdata->pm_state = HSW_PM_STATE_D3;
-
- return 0;
-}
-
-#else
-#define hsw_pcm_prepare NULL
-#define hsw_pcm_complete NULL
-#endif
-
-static const struct dev_pm_ops hsw_pcm_pm = {
- .runtime_idle = hsw_pcm_runtime_idle,
- .runtime_suspend = hsw_pcm_runtime_suspend,
- .runtime_resume = hsw_pcm_runtime_resume,
- .prepare = hsw_pcm_prepare,
- .complete = hsw_pcm_complete,
-};
-
-static struct platform_driver hsw_pcm_driver = {
- .driver = {
- .name = "haswell-pcm-audio",
- .pm = &hsw_pcm_pm,
- },
-
- .probe = hsw_pcm_dev_probe,
- .remove = hsw_pcm_dev_remove,
-};
-module_platform_driver(hsw_pcm_driver);
-
-MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
-MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:haswell-pcm-audio");
diff --git a/sound/soc/intel/keembay/Makefile b/sound/soc/intel/keembay/Makefile
new file mode 100644
index 000000000000..3da9a6f9ba2a
--- /dev/null
+++ b/sound/soc/intel/keembay/Makefile
@@ -0,0 +1,4 @@
+snd-soc-kmb_platform-y := \
+ kmb_platform.o
+
+obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += snd-soc-kmb_platform.o
diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c
new file mode 100644
index 000000000000..4ed71d11ad77
--- /dev/null
+++ b/sound/soc/intel/keembay/kmb_platform.c
@@ -0,0 +1,928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (C) 2020 Intel Corporation.
+//
+// Intel KeemBay Platform driver.
+//
+
+#include <linux/bitrev.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "kmb_platform.h"
+
+#define PERIODS_MIN 2
+#define PERIODS_MAX 48
+#define PERIOD_BYTES_MIN 4096
+#define BUFFER_BYTES_MAX (PERIODS_MAX * PERIOD_BYTES_MIN)
+#define TDM_OPERATION 5
+#define I2S_OPERATION 0
+#define DATA_WIDTH_CONFIG_BIT 6
+#define TDM_CHANNEL_CONFIG_BIT 3
+
+static const struct snd_pcm_hardware kmb_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = BUFFER_BYTES_MAX,
+ .period_bytes_min = PERIOD_BYTES_MIN,
+ .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
+ .periods_min = PERIODS_MIN,
+ .periods_max = PERIODS_MAX,
+ .fifo_size = 16,
+};
+
+/*
+ * Convert to ADV7511 HDMI hardware format.
+ * ADV7511 HDMI chip need parity bit replaced by block start bit and
+ * with the preamble bits left out.
+ * ALSA IEC958 subframe format:
+ * bit 0-3 = preamble (0x8 = block start)
+ * 4-7 = AUX (=0)
+ * 8-27 = audio data (without AUX if 24bit sample)
+ * 28 = validity
+ * 29 = user data
+ * 30 = channel status
+ * 31 = parity
+ *
+ * ADV7511 IEC958 subframe format:
+ * bit 0-23 = audio data
+ * 24 = validity
+ * 25 = user data
+ * 26 = channel status
+ * 27 = block start
+ * 28-31 = 0
+ * MSB to LSB bit reverse by software as hardware not supporting it.
+ */
+static void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime,
+ struct kmb_i2s_info *kmb_i2s,
+ unsigned int tx_ptr)
+{
+ u32(*buf)[2] = (void *)runtime->dma_area;
+ unsigned long temp;
+ u32 i, j, sample;
+
+ for (i = 0; i < kmb_i2s->fifo_th; i++) {
+ j = 0;
+ do {
+ temp = buf[tx_ptr][j];
+ /* Replace parity with block start*/
+ assign_bit(31, &temp, (BIT(3) & temp));
+ sample = bitrev32(temp);
+ buf[tx_ptr][j] = sample << 4;
+ j++;
+ } while (j < 2);
+ tx_ptr++;
+ }
+}
+
+static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
+ struct snd_pcm_runtime *runtime,
+ unsigned int tx_ptr, bool *period_elapsed)
+{
+ unsigned int period_pos = tx_ptr % runtime->period_size;
+ void __iomem *i2s_base = kmb_i2s->i2s_base;
+ void *buf = runtime->dma_area;
+ int i;
+
+ if (kmb_i2s->iec958_fmt)
+ hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr);
+
+ /* KMB i2s uses two separate L/R FIFO */
+ for (i = 0; i < kmb_i2s->fifo_th; i++) {
+ if (kmb_i2s->config.data_width == 16) {
+ writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
+ writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
+ } else {
+ writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
+ writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
+ }
+
+ period_pos++;
+
+ if (++tx_ptr >= runtime->buffer_size)
+ tx_ptr = 0;
+ }
+
+ *period_elapsed = period_pos >= runtime->period_size;
+
+ return tx_ptr;
+}
+
+static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s,
+ struct snd_pcm_runtime *runtime,
+ unsigned int rx_ptr, bool *period_elapsed)
+{
+ unsigned int period_pos = rx_ptr % runtime->period_size;
+ void __iomem *i2s_base = kmb_i2s->i2s_base;
+ int chan = kmb_i2s->config.chan_nr;
+ void *buf = runtime->dma_area;
+ int i, j;
+
+ /* KMB i2s uses two separate L/R FIFO */
+ for (i = 0; i < kmb_i2s->fifo_th; i++) {
+ for (j = 0; j < chan / 2; j++) {
+ if (kmb_i2s->config.data_width == 16) {
+ ((u16 *)buf)[rx_ptr * chan + (j * 2)] =
+ readl(i2s_base + LRBR_LTHR(j));
+ ((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
+ readl(i2s_base + RRBR_RTHR(j));
+ } else {
+ ((u32 *)buf)[rx_ptr * chan + (j * 2)] =
+ readl(i2s_base + LRBR_LTHR(j));
+ ((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
+ readl(i2s_base + RRBR_RTHR(j));
+ }
+ }
+ period_pos++;
+
+ if (++rx_ptr >= runtime->buffer_size)
+ rx_ptr = 0;
+ }
+
+ *period_elapsed = period_pos >= runtime->period_size;
+
+ return rx_ptr;
+}
+
+static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s,
+ u32 stream)
+{
+ u32 i;
+
+ /* Disable all channels regardless of configuration*/
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < MAX_ISR; i++)
+ writel(0, kmb_i2s->i2s_base + TER(i));
+ } else {
+ for (i = 0; i < MAX_ISR; i++)
+ writel(0, kmb_i2s->i2s_base + RER(i));
+ }
+}
+
+static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream)
+{
+ struct i2s_clk_config_data *config = &kmb_i2s->config;
+ u32 i;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < config->chan_nr / 2; i++)
+ readl(kmb_i2s->i2s_base + TOR(i));
+ } else {
+ for (i = 0; i < config->chan_nr / 2; i++)
+ readl(kmb_i2s->i2s_base + ROR(i));
+ }
+}
+
+static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s,
+ u32 stream, int chan_nr, bool trigger)
+{
+ u32 i, irq;
+ u32 flag;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ flag = TX_INT_FLAG;
+ else
+ flag = RX_INT_FLAG;
+
+ for (i = 0; i < chan_nr / 2; i++) {
+ irq = readl(kmb_i2s->i2s_base + IMR(i));
+
+ if (trigger)
+ irq = irq & ~flag;
+ else
+ irq = irq | flag;
+
+ writel(irq, kmb_i2s->i2s_base + IMR(i));
+ }
+}
+
+static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback)
+{
+ struct snd_pcm_substream *substream;
+ bool period_elapsed;
+ unsigned int new_ptr;
+ unsigned int ptr;
+
+ if (playback)
+ substream = kmb_i2s->tx_substream;
+ else
+ substream = kmb_i2s->rx_substream;
+
+ if (!substream || !snd_pcm_running(substream))
+ return;
+
+ if (playback) {
+ ptr = kmb_i2s->tx_ptr;
+ new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime,
+ ptr, &period_elapsed);
+ cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr);
+ } else {
+ ptr = kmb_i2s->rx_ptr;
+ new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime,
+ ptr, &period_elapsed);
+ cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr);
+ }
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+}
+
+static int kmb_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct kmb_i2s_info *kmb_i2s;
+
+ kmb_i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
+ snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ runtime->private_data = kmb_i2s;
+
+ return 0;
+}
+
+static int kmb_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct kmb_i2s_info *kmb_i2s = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ kmb_i2s->tx_ptr = 0;
+ kmb_i2s->tx_substream = substream;
+ } else {
+ kmb_i2s->rx_ptr = 0;
+ kmb_i2s->rx_substream = substream;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ kmb_i2s->tx_substream = NULL;
+ else
+ kmb_i2s->rx_substream = NULL;
+ kmb_i2s->iec958_fmt = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id)
+{
+ struct kmb_i2s_info *kmb_i2s = dev_id;
+ struct i2s_clk_config_data *config = &kmb_i2s->config;
+ irqreturn_t ret = IRQ_NONE;
+ u32 tx_enabled = 0;
+ u32 isr[4];
+ int i;
+
+ for (i = 0; i < config->chan_nr / 2; i++)
+ isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
+
+ kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
+ kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
+ /* Only check TX interrupt if TX is active */
+ tx_enabled = readl(kmb_i2s->i2s_base + ITER);
+
+ /*
+ * Data available. Retrieve samples from FIFO
+ */
+
+ /*
+ * 8 channel audio will have isr[0..2] triggered,
+ * reading the specific isr based on the audio configuration,
+ * to avoid reading the buffers too early.
+ */
+ switch (config->chan_nr) {
+ case 2:
+ if (isr[0] & ISR_RXDA)
+ kmb_pcm_operation(kmb_i2s, false);
+ ret = IRQ_HANDLED;
+ break;
+ case 4:
+ if (isr[1] & ISR_RXDA)
+ kmb_pcm_operation(kmb_i2s, false);
+ ret = IRQ_HANDLED;
+ break;
+ case 8:
+ if (isr[3] & ISR_RXDA)
+ kmb_pcm_operation(kmb_i2s, false);
+ ret = IRQ_HANDLED;
+ break;
+ }
+
+ for (i = 0; i < config->chan_nr / 2; i++) {
+ /*
+ * Check if TX fifo is empty. If empty fill FIFO with samples
+ */
+ if ((isr[i] & ISR_TXFE) && tx_enabled) {
+ kmb_pcm_operation(kmb_i2s, true);
+ ret = IRQ_HANDLED;
+ }
+
+ /* Error Handling: TX */
+ if (isr[i] & ISR_TXFO) {
+ dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
+ ret = IRQ_HANDLED;
+ }
+ /* Error Handling: RX */
+ if (isr[i] & ISR_RXFO) {
+ dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ return ret;
+}
+
+static int kmb_platform_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *soc_runtime)
+{
+ size_t size = kmb_pcm_hardware.buffer_bytes_max;
+ /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
+ snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, size, size);
+ return 0;
+}
+
+static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct kmb_i2s_info *kmb_i2s = runtime->private_data;
+ snd_pcm_uframes_t pos;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pos = kmb_i2s->tx_ptr;
+ else
+ pos = kmb_i2s->rx_ptr;
+
+ return pos < runtime->buffer_size ? pos : 0;
+}
+
+static const struct snd_soc_component_driver kmb_component = {
+ .name = "kmb",
+ .pcm_construct = kmb_platform_pcm_new,
+ .open = kmb_pcm_open,
+ .trigger = kmb_pcm_trigger,
+ .pointer = kmb_pcm_pointer,
+ .legacy_dai_naming = 1,
+};
+
+static const struct snd_soc_component_driver kmb_component_dma = {
+ .name = "kmb",
+ .legacy_dai_naming = 1,
+};
+
+static int kmb_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (kmb_i2s->use_pio)
+ return 0;
+
+ snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data,
+ &kmb_i2s->capture_dma_data);
+
+ return 0;
+}
+
+static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
+{
+ u32 dma_reg;
+
+ dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
+ /* Enable DMA handshake for stream */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_reg |= I2S_DMAEN_TXBLOCK;
+ else
+ dma_reg |= I2S_DMAEN_RXBLOCK;
+
+ writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
+}
+
+static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
+{
+ u32 dma_reg;
+
+ dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
+ /* Disable DMA handshake for stream */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_reg &= ~I2S_DMAEN_TXBLOCK;
+ writel(1, kmb_i2s->i2s_base + I2S_RTXDMA);
+ } else {
+ dma_reg &= ~I2S_DMAEN_RXBLOCK;
+ writel(1, kmb_i2s->i2s_base + I2S_RRXDMA);
+ }
+ writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
+}
+
+static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_clk_config_data *config = &kmb_i2s->config;
+
+ /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
+ writel(1, kmb_i2s->i2s_base + IER);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ writel(1, kmb_i2s->i2s_base + ITER);
+ else
+ writel(1, kmb_i2s->i2s_base + IRER);
+
+ if (kmb_i2s->use_pio)
+ kmb_i2s_irq_trigger(kmb_i2s, substream->stream,
+ config->chan_nr, true);
+ else
+ kmb_i2s_enable_dma(kmb_i2s, substream->stream);
+
+ if (kmb_i2s->clock_provider)
+ writel(1, kmb_i2s->i2s_base + CER);
+ else
+ writel(0, kmb_i2s->i2s_base + CER);
+}
+
+static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s,
+ struct snd_pcm_substream *substream)
+{
+ /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
+ kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ writel(0, kmb_i2s->i2s_base + ITER);
+ else
+ writel(0, kmb_i2s->i2s_base + IRER);
+
+ kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
+
+ if (!kmb_i2s->active) {
+ writel(0, kmb_i2s->i2s_base + CER);
+ writel(0, kmb_i2s->i2s_base + IER);
+ }
+}
+
+static void kmb_disable_clk(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ kmb_i2s->clock_provider = false;
+ ret = 0;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0);
+
+ ret = clk_prepare_enable(kmb_i2s->clk_i2s);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk,
+ kmb_i2s->clk_i2s);
+ if (ret)
+ return ret;
+
+ kmb_i2s->clock_provider = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int kmb_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* Keep track of i2s activity before turn off
+ * the i2s interface
+ */
+ kmb_i2s->active++;
+ kmb_i2s_start(kmb_i2s, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ kmb_i2s->active--;
+ if (kmb_i2s->use_pio)
+ kmb_i2s_stop(kmb_i2s, substream);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream)
+{
+ struct i2s_clk_config_data *config = &kmb_i2s->config;
+ u32 ch_reg;
+
+ kmb_i2s_disable_channels(kmb_i2s, stream);
+
+ for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ writel(kmb_i2s->xfer_resolution,
+ kmb_i2s->i2s_base + TCR(ch_reg));
+
+ writel(kmb_i2s->fifo_th - 1,
+ kmb_i2s->i2s_base + TFCR(ch_reg));
+
+ writel(1, kmb_i2s->i2s_base + TER(ch_reg));
+ } else {
+ writel(kmb_i2s->xfer_resolution,
+ kmb_i2s->i2s_base + RCR(ch_reg));
+
+ writel(kmb_i2s->fifo_th - 1,
+ kmb_i2s->i2s_base + RFCR(ch_reg));
+
+ writel(1, kmb_i2s->i2s_base + RER(ch_reg));
+ }
+ }
+}
+
+static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ struct i2s_clk_config_data *config = &kmb_i2s->config;
+ u32 write_val;
+ int ret;
+
+ switch (params_format(hw_params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ config->data_width = 16;
+ kmb_i2s->ccr = 0x00;
+ kmb_i2s->xfer_resolution = 0x02;
+ kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ config->data_width = 32;
+ kmb_i2s->ccr = 0x14;
+ kmb_i2s->xfer_resolution = 0x05;
+ kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ kmb_i2s->iec958_fmt = true;
+ fallthrough;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ config->data_width = 32;
+ kmb_i2s->ccr = 0x10;
+ kmb_i2s->xfer_resolution = 0x05;
+ kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
+ return -EINVAL;
+ }
+
+ config->chan_nr = params_channels(hw_params);
+
+ switch (config->chan_nr) {
+ case 8:
+ case 4:
+ /*
+ * Platform is not capable of providing clocks for
+ * multi channel audio
+ */
+ if (kmb_i2s->clock_provider)
+ return -EINVAL;
+
+ write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
+ (config->data_width << DATA_WIDTH_CONFIG_BIT) |
+ TDM_OPERATION;
+
+ writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
+ break;
+ case 2:
+ /*
+ * Platform is only capable of providing clocks need for
+ * 2 channel master mode
+ */
+ if (!(kmb_i2s->clock_provider))
+ return -EINVAL;
+
+ write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
+ (config->data_width << DATA_WIDTH_CONFIG_BIT) |
+ CLOCK_PROVIDER_MODE | I2S_OPERATION;
+
+ writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
+ break;
+ default:
+ dev_dbg(kmb_i2s->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+
+ kmb_i2s_config(kmb_i2s, substream->stream);
+
+ writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR);
+
+ config->sample_rate = params_rate(hw_params);
+
+ if (kmb_i2s->clock_provider) {
+ /* Only 2 ch supported in Master mode */
+ u32 bitclk = config->sample_rate * config->data_width * 2;
+
+ ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk);
+ if (ret) {
+ dev_err(kmb_i2s->dev,
+ "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int kmb_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ writel(1, kmb_i2s->i2s_base + TXFFR);
+ else
+ writel(1, kmb_i2s->i2s_base + RXFFR);
+
+ return 0;
+}
+
+static int kmb_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ if (kmb_i2s->use_pio)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data = &kmb_i2s->play_dma_data;
+ else
+ dma_data = &kmb_i2s->capture_dma_data;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
+
+ return 0;
+}
+
+static int kmb_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
+ if (kmb_i2s->use_pio)
+ kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ writel(0, kmb_i2s->i2s_base + ITER);
+ else
+ writel(0, kmb_i2s->i2s_base + IRER);
+
+ if (kmb_i2s->use_pio)
+ kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
+ else
+ kmb_i2s_disable_dma(kmb_i2s, substream->stream);
+
+ if (!kmb_i2s->active) {
+ writel(0, kmb_i2s->i2s_base + CER);
+ writel(0, kmb_i2s->i2s_base + IER);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops kmb_dai_ops = {
+ .probe = kmb_probe,
+ .startup = kmb_dai_startup,
+ .trigger = kmb_dai_trigger,
+ .hw_params = kmb_dai_hw_params,
+ .hw_free = kmb_dai_hw_free,
+ .prepare = kmb_dai_prepare,
+ .set_fmt = kmb_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = {
+ {
+ .name = "intel_kmb_hdmi_i2s",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
+ },
+ .ops = &kmb_dai_ops,
+ },
+};
+
+static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
+ {
+ .name = "intel_kmb_i2s",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = (SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE),
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = (SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE),
+ },
+ .ops = &kmb_dai_ops,
+ },
+};
+
+static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
+ {
+ .name = "intel_kmb_tdm",
+ .capture = {
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = (SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE),
+ },
+ .ops = &kmb_dai_ops,
+ },
+};
+
+static const struct of_device_id kmb_plat_of_match[] = {
+ { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
+ { .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai},
+ { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
+ {}
+};
+MODULE_DEVICE_TABLE(of, kmb_plat_of_match);
+
+static int kmb_plat_dai_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_dai_driver *kmb_i2s_dai;
+ struct device *dev = &pdev->dev;
+ struct kmb_i2s_info *kmb_i2s;
+ struct resource *res;
+ int ret, irq;
+ u32 comp1_reg;
+
+ kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL);
+ if (!kmb_i2s)
+ return -ENOMEM;
+
+ kmb_i2s_dai = (struct snd_soc_dai_driver *)device_get_match_data(&pdev->dev);
+
+ /* Prepare the related clocks */
+ kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk");
+ if (IS_ERR(kmb_i2s->clk_apb)) {
+ dev_err(dev, "Failed to get apb clock\n");
+ return PTR_ERR(kmb_i2s->clk_apb);
+ }
+
+ ret = clk_prepare_enable(kmb_i2s->clk_apb);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb);
+ if (ret) {
+ dev_err(dev, "Failed to add clk_apb reset action\n");
+ return ret;
+ }
+
+ kmb_i2s->clk_i2s = devm_clk_get(dev, "osc");
+ if (IS_ERR(kmb_i2s->clk_i2s)) {
+ dev_err(dev, "Failed to get osc clock\n");
+ return PTR_ERR(kmb_i2s->clk_i2s);
+ }
+
+ kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(kmb_i2s->i2s_base))
+ return PTR_ERR(kmb_i2s->i2s_base);
+
+ kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(kmb_i2s->pss_base))
+ return PTR_ERR(kmb_i2s->pss_base);
+
+ kmb_i2s->dev = &pdev->dev;
+
+ comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
+
+ kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
+
+ kmb_i2s->use_pio = !of_property_present(np, "dmas");
+
+ if (kmb_i2s->use_pio) {
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq > 0) {
+ ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
+ pdev->name, kmb_i2s);
+ if (ret < 0) {
+ dev_err(dev, "failed to request irq\n");
+ return ret;
+ }
+ }
+ ret = devm_snd_soc_register_component(dev, &kmb_component,
+ kmb_i2s_dai, 1);
+ } else {
+ kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
+ kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
+ ret = snd_dmaengine_pcm_register(&pdev->dev,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register dmaengine: %d\n",
+ ret);
+ return ret;
+ }
+ ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
+ kmb_i2s_dai, 1);
+ }
+
+ if (ret) {
+ dev_err(dev, "not able to register dai\n");
+ return ret;
+ }
+
+ /* To ensure none of the channels are enabled at boot up */
+ kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
+ kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
+
+ dev_set_drvdata(dev, kmb_i2s);
+
+ return ret;
+}
+
+static struct platform_driver kmb_plat_dai_driver = {
+ .driver = {
+ .name = "kmb-plat-dai",
+ .of_match_table = kmb_plat_of_match,
+ },
+ .probe = kmb_plat_dai_probe,
+};
+
+module_platform_driver(kmb_plat_dai_driver);
+
+MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
+MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
+MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:kmb_platform");
diff --git a/sound/soc/intel/keembay/kmb_platform.h b/sound/soc/intel/keembay/kmb_platform.h
new file mode 100644
index 000000000000..29be2cd84ddb
--- /dev/null
+++ b/sound/soc/intel/keembay/kmb_platform.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Intel KeemBay Platform driver
+ *
+ * Copyright (C) 2020 Intel Corporation.
+ *
+ */
+
+#ifndef KMB_PLATFORM_H_
+#define KMB_PLATFORM_H_
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Register values with reference to KMB databook v1.1 */
+/* common register for all channel */
+#define IER 0x000
+#define IRER 0x004
+#define ITER 0x008
+#define CER 0x00C
+#define CCR 0x010
+#define RXFFR 0x014
+#define TXFFR 0x018
+
+/* Interrupt status register fields */
+#define ISR_TXFO BIT(5)
+#define ISR_TXFE BIT(4)
+#define ISR_RXFO BIT(1)
+#define ISR_RXDA BIT(0)
+
+/* I2S Tx Rx Registers for all channels */
+#define LRBR_LTHR(x) (0x40 * (x) + 0x020)
+#define RRBR_RTHR(x) (0x40 * (x) + 0x024)
+#define RER(x) (0x40 * (x) + 0x028)
+#define TER(x) (0x40 * (x) + 0x02C)
+#define RCR(x) (0x40 * (x) + 0x030)
+#define TCR(x) (0x40 * (x) + 0x034)
+#define ISR(x) (0x40 * (x) + 0x038)
+#define IMR(x) (0x40 * (x) + 0x03C)
+#define ROR(x) (0x40 * (x) + 0x040)
+#define TOR(x) (0x40 * (x) + 0x044)
+#define RFCR(x) (0x40 * (x) + 0x048)
+#define TFCR(x) (0x40 * (x) + 0x04C)
+#define RFF(x) (0x40 * (x) + 0x050)
+#define TFF(x) (0x40 * (x) + 0x054)
+
+/* I2S COMP Registers */
+#define I2S_COMP_PARAM_2 0x01F0
+#define I2S_COMP_PARAM_1 0x01F4
+#define I2S_COMP_VERSION 0x01F8
+#define I2S_COMP_TYPE 0x01FC
+
+/* PSS_GEN_CTRL_I2S_GEN_CFG_0 Registers */
+#define I2S_GEN_CFG_0 0x000
+#define PSS_CPR_RST_EN 0x010
+#define PSS_CPR_RST_SET 0x014
+#define PSS_CPR_CLK_CLR 0x000
+#define PSS_CPR_AUX_RST_EN 0x070
+
+#define CLOCK_PROVIDER_MODE BIT(13)
+
+/* Interrupt Flag */
+#define TX_INT_FLAG GENMASK(5, 4)
+#define RX_INT_FLAG GENMASK(1, 0)
+/*
+ * Component parameter register fields - define the I2S block's
+ * configuration.
+ */
+#define COMP1_TX_WORDSIZE_3(r) FIELD_GET(GENMASK(27, 25), (r))
+#define COMP1_TX_WORDSIZE_2(r) FIELD_GET(GENMASK(24, 22), (r))
+#define COMP1_TX_WORDSIZE_1(r) FIELD_GET(GENMASK(21, 19), (r))
+#define COMP1_TX_WORDSIZE_0(r) FIELD_GET(GENMASK(18, 16), (r))
+#define COMP1_RX_ENABLED(r) FIELD_GET(BIT(6), (r))
+#define COMP1_TX_ENABLED(r) FIELD_GET(BIT(5), (r))
+#define COMP1_MODE_EN(r) FIELD_GET(BIT(4), (r))
+#define COMP1_APB_DATA_WIDTH(r) FIELD_GET(GENMASK(1, 0), (r))
+#define COMP2_RX_WORDSIZE_3(r) FIELD_GET(GENMASK(12, 10), (r))
+#define COMP2_RX_WORDSIZE_2(r) FIELD_GET(GENMASK(9, 7), (r))
+#define COMP2_RX_WORDSIZE_1(r) FIELD_GET(GENMASK(5, 3), (r))
+#define COMP2_RX_WORDSIZE_0(r) FIELD_GET(GENMASK(2, 0), (r))
+
+/* Add 1 to the below registers to indicate the actual size */
+#define COMP1_TX_CHANNELS(r) (FIELD_GET(GENMASK(10, 9), (r)) + 1)
+#define COMP1_RX_CHANNELS(r) (FIELD_GET(GENMASK(8, 7), (r)) + 1)
+#define COMP1_FIFO_DEPTH(r) (FIELD_GET(GENMASK(3, 2), (r)) + 1)
+
+/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
+#define COMP_MAX_WORDSIZE 8 /* 3 bits register width */
+
+#define MAX_CHANNEL_NUM 8
+#define MIN_CHANNEL_NUM 2
+#define MAX_ISR 4
+
+#define TWO_CHANNEL_SUPPORT 2 /* up to 2.0 */
+#define FOUR_CHANNEL_SUPPORT 4 /* up to 3.1 */
+#define SIX_CHANNEL_SUPPORT 6 /* up to 5.1 */
+#define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */
+
+#define DWC_I2S_PLAY BIT(0)
+#define DWC_I2S_RECORD BIT(1)
+#define DW_I2S_CONSUMER BIT(2)
+#define DW_I2S_PROVIDER BIT(3)
+
+#define I2S_RXDMA 0x01C0
+#define I2S_RRXDMA 0x01C4
+#define I2S_TXDMA 0x01C8
+#define I2S_RTXDMA 0x01CC
+#define I2S_DMACR 0x0200
+#define I2S_DMAEN_RXBLOCK (1 << 16)
+#define I2S_DMAEN_TXBLOCK (1 << 17)
+
+/*
+ * struct i2s_clk_config_data - represent i2s clk configuration data
+ * @chan_nr: number of channel
+ * @data_width: number of bits per sample (8/16/24/32 bit)
+ * @sample_rate: sampling frequency (8Khz, 16Khz, 48Khz)
+ */
+struct i2s_clk_config_data {
+ int chan_nr;
+ u32 data_width;
+ u32 sample_rate;
+};
+
+struct kmb_i2s_info {
+ void __iomem *i2s_base;
+ void __iomem *pss_base;
+ struct clk *clk_i2s;
+ struct clk *clk_apb;
+ int active;
+ unsigned int capability;
+ unsigned int i2s_reg_comp1;
+ unsigned int i2s_reg_comp2;
+ struct device *dev;
+ u32 ccr;
+ u32 xfer_resolution;
+ u32 fifo_th;
+ bool clock_provider;
+ /* data related to DMA transfers b/w i2s and DMAC */
+ struct snd_dmaengine_dai_dma_data play_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+
+ struct i2s_clk_config_data config;
+ int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+
+ /* data related to PIO transfers */
+ bool use_pio;
+ struct snd_pcm_substream *tx_substream;
+ struct snd_pcm_substream *rx_substream;
+ unsigned int tx_ptr;
+ unsigned int rx_ptr;
+ bool iec958_fmt;
+};
+
+#endif /* KMB_PLATFORM_H_ */
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
deleted file mode 100644
index 914b6dab9bea..000000000000
--- a/sound/soc/intel/skylake/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \
-skl-topology.o
-
-obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
-
-# Skylake IPC Support
-snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
- skl-sst.o
-
-obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
deleted file mode 100644
index de6dac496a0d..000000000000
--- a/sound/soc/intel/skylake/skl-messages.c
+++ /dev/null
@@ -1,944 +0,0 @@
-/*
- * skl-message.c - HDA DSP interface for FW registration, Pipe and Module
- * configurations
- *
- * Copyright (C) 2015 Intel Corp
- * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
- * Jeeja KP <jeeja.kp@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 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/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include "skl-sst-dsp.h"
-#include "skl-sst-ipc.h"
-#include "skl.h"
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "skl-topology.h"
-#include "skl-tplg-interface.h"
-
-static int skl_alloc_dma_buf(struct device *dev,
- struct snd_dma_buffer *dmab, size_t size)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- if (!bus)
- return -ENODEV;
-
- return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, size, dmab);
-}
-
-static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- if (!bus)
- return -ENODEV;
-
- bus->io_ops->dma_free_pages(bus, dmab);
-
- return 0;
-}
-
-#define NOTIFICATION_PARAM_ID 3
-#define NOTIFICATION_MASK 0xf
-
-/* disable notfication for underruns/overruns from firmware module */
-static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
-{
- struct notification_mask mask;
- struct skl_ipc_large_config_msg msg = {0};
-
- mask.notify = NOTIFICATION_MASK;
- mask.enable = enable;
-
- msg.large_param_id = NOTIFICATION_PARAM_ID;
- msg.param_data_size = sizeof(mask);
-
- skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
-}
-
-int skl_init_dsp(struct skl *skl)
-{
- void __iomem *mmio_base;
- struct hdac_ext_bus *ebus = &skl->ebus;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- int irq = bus->irq;
- struct skl_dsp_loader_ops loader_ops;
- int ret;
-
- loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
- loader_ops.free_dma_buf = skl_free_dma_buf;
-
- /* enable ppcap interrupt */
- snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
- snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
-
- /* read the BAR of the ADSP MMIO */
- mmio_base = pci_ioremap_bar(skl->pci, 4);
- if (mmio_base == NULL) {
- dev_err(bus->dev, "ioremap error\n");
- return -ENXIO;
- }
-
- ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
- skl->fw_name, loader_ops, &skl->skl_sst);
- if (ret < 0)
- return ret;
-
- skl_dsp_enable_notification(skl->skl_sst, false);
- dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
-
- return ret;
-}
-
-void skl_free_dsp(struct skl *skl)
-{
- struct hdac_ext_bus *ebus = &skl->ebus;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl_sst *ctx = skl->skl_sst;
-
- /* disable ppcap interrupt */
- snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
-
- skl_sst_dsp_cleanup(bus->dev, ctx);
- if (ctx->dsp->addr.lpe)
- iounmap(ctx->dsp->addr.lpe);
-}
-
-int skl_suspend_dsp(struct skl *skl)
-{
- struct skl_sst *ctx = skl->skl_sst;
- int ret;
-
- /* if ppcap is not supported return 0 */
- if (!skl->ebus.ppcap)
- return 0;
-
- ret = skl_dsp_sleep(ctx->dsp);
- if (ret < 0)
- return ret;
-
- /* disable ppcap interrupt */
- snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
- snd_hdac_ext_bus_ppcap_enable(&skl->ebus, false);
-
- return 0;
-}
-
-int skl_resume_dsp(struct skl *skl)
-{
- struct skl_sst *ctx = skl->skl_sst;
- int ret;
-
- /* if ppcap is not supported return 0 */
- if (!skl->ebus.ppcap)
- return 0;
-
- /* enable ppcap interrupt */
- snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
- snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
-
- ret = skl_dsp_wake(ctx->dsp);
- if (ret < 0)
- return ret;
-
- skl_dsp_enable_notification(skl->skl_sst, false);
- return ret;
-}
-
-enum skl_bitdepth skl_get_bit_depth(int params)
-{
- switch (params) {
- case 8:
- return SKL_DEPTH_8BIT;
-
- case 16:
- return SKL_DEPTH_16BIT;
-
- case 24:
- return SKL_DEPTH_24BIT;
-
- case 32:
- return SKL_DEPTH_32BIT;
-
- default:
- return SKL_DEPTH_INVALID;
-
- }
-}
-
-/*
- * Each module in DSP expects a base module configuration, which consists of
- * PCM format information, which we calculate in driver and resource values
- * which are read from widget information passed through topology binary
- * This is send when we create a module with INIT_INSTANCE IPC msg
- */
-static void skl_set_base_module_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_base_cfg *base_cfg)
-{
- struct skl_module_fmt *format = &mconfig->in_fmt[0];
-
- base_cfg->audio_fmt.number_of_channels = (u8)format->channels;
-
- base_cfg->audio_fmt.s_freq = format->s_freq;
- base_cfg->audio_fmt.bit_depth = format->bit_depth;
- base_cfg->audio_fmt.valid_bit_depth = format->valid_bit_depth;
- base_cfg->audio_fmt.ch_cfg = format->ch_cfg;
-
- dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x\n",
- format->bit_depth, format->valid_bit_depth,
- format->ch_cfg);
-
- base_cfg->audio_fmt.channel_map = format->ch_map;
-
- base_cfg->audio_fmt.interleaving = format->interleaving_style;
-
- base_cfg->cps = mconfig->mcps;
- base_cfg->ibs = mconfig->ibs;
- base_cfg->obs = mconfig->obs;
- base_cfg->is_pages = mconfig->mem_pages;
-}
-
-/*
- * Copies copier capabilities into copier module and updates copier module
- * config size.
- */
-static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
- struct skl_cpr_cfg *cpr_mconfig)
-{
- if (mconfig->formats_config.caps_size == 0)
- return;
-
- memcpy(cpr_mconfig->gtw_cfg.config_data,
- mconfig->formats_config.caps,
- mconfig->formats_config.caps_size);
-
- cpr_mconfig->gtw_cfg.config_length =
- (mconfig->formats_config.caps_size) / 4;
-}
-
-#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF
-/*
- * Calculate the gatewat settings required for copier module, type of
- * gateway and index of gateway to use
- */
-static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_cpr_cfg *cpr_mconfig)
-{
- union skl_connector_node_id node_id = {0};
- union skl_ssp_dma_node ssp_node = {0};
- struct skl_pipe_params *params = mconfig->pipe->p_params;
-
- switch (mconfig->dev_type) {
- case SKL_DEVICE_BT:
- node_id.node.dma_type =
- (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
- SKL_DMA_I2S_LINK_OUTPUT_CLASS :
- SKL_DMA_I2S_LINK_INPUT_CLASS;
- node_id.node.vindex = params->host_dma_id +
- (mconfig->vbus_id << 3);
- break;
-
- case SKL_DEVICE_I2S:
- node_id.node.dma_type =
- (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
- SKL_DMA_I2S_LINK_OUTPUT_CLASS :
- SKL_DMA_I2S_LINK_INPUT_CLASS;
- ssp_node.dma_node.time_slot_index = mconfig->time_slot;
- ssp_node.dma_node.i2s_instance = mconfig->vbus_id;
- node_id.node.vindex = ssp_node.val;
- break;
-
- case SKL_DEVICE_DMIC:
- node_id.node.dma_type = SKL_DMA_DMIC_LINK_INPUT_CLASS;
- node_id.node.vindex = mconfig->vbus_id +
- (mconfig->time_slot);
- break;
-
- case SKL_DEVICE_HDALINK:
- node_id.node.dma_type =
- (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
- SKL_DMA_HDA_LINK_OUTPUT_CLASS :
- SKL_DMA_HDA_LINK_INPUT_CLASS;
- node_id.node.vindex = params->link_dma_id;
- break;
-
- case SKL_DEVICE_HDAHOST:
- node_id.node.dma_type =
- (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
- SKL_DMA_HDA_HOST_OUTPUT_CLASS :
- SKL_DMA_HDA_HOST_INPUT_CLASS;
- node_id.node.vindex = params->host_dma_id;
- break;
-
- default:
- cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
- cpr_mconfig->cpr_feature_mask = 0;
- return;
- }
-
- cpr_mconfig->gtw_cfg.node_id = node_id.val;
-
- if (SKL_CONN_SOURCE == mconfig->hw_conn_type)
- cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs;
- else
- cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->ibs;
-
- cpr_mconfig->cpr_feature_mask = 0;
- cpr_mconfig->gtw_cfg.config_length = 0;
-
- skl_copy_copier_caps(mconfig, cpr_mconfig);
-}
-
-static void skl_setup_out_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_audio_data_format *out_fmt)
-{
- struct skl_module_fmt *format = &mconfig->out_fmt[0];
-
- out_fmt->number_of_channels = (u8)format->channels;
- out_fmt->s_freq = format->s_freq;
- out_fmt->bit_depth = format->bit_depth;
- out_fmt->valid_bit_depth = format->valid_bit_depth;
- out_fmt->ch_cfg = format->ch_cfg;
-
- out_fmt->channel_map = format->ch_map;
- out_fmt->interleaving = format->interleaving_style;
- out_fmt->sample_type = format->sample_type;
-
- dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n",
- out_fmt->number_of_channels, format->s_freq, format->bit_depth);
-}
-
-/*
- * DSP needs SRC module for frequency conversion, SRC takes base module
- * configuration and the target frequency as extra parameter passed as src
- * config
- */
-static void skl_set_src_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_src_module_cfg *src_mconfig)
-{
- struct skl_module_fmt *fmt = &mconfig->out_fmt[0];
-
- skl_set_base_module_format(ctx, mconfig,
- (struct skl_base_cfg *)src_mconfig);
-
- src_mconfig->src_cfg = fmt->s_freq;
-}
-
-/*
- * DSP needs updown module to do channel conversion. updown module take base
- * module configuration and channel configuration
- * It also take coefficients and now we have defaults applied here
- */
-static void skl_set_updown_mixer_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_up_down_mixer_cfg *mixer_mconfig)
-{
- struct skl_module_fmt *fmt = &mconfig->out_fmt[0];
- int i = 0;
-
- skl_set_base_module_format(ctx, mconfig,
- (struct skl_base_cfg *)mixer_mconfig);
- mixer_mconfig->out_ch_cfg = fmt->ch_cfg;
-
- /* Select F/W default coefficient */
- mixer_mconfig->coeff_sel = 0x0;
-
- /* User coeff, don't care since we are selecting F/W defaults */
- for (i = 0; i < UP_DOWN_MIXER_MAX_COEFF; i++)
- mixer_mconfig->coeff[i] = 0xDEADBEEF;
-}
-
-/*
- * 'copier' is DSP internal module which copies data from Host DMA (HDA host
- * dma) or link (hda link, SSP, PDM)
- * Here we calculate the copier module parameters, like PCM format, output
- * format, gateway settings
- * copier_module_config is sent as input buffer with INIT_INSTANCE IPC msg
- */
-static void skl_set_copier_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_cpr_cfg *cpr_mconfig)
-{
- struct skl_audio_data_format *out_fmt = &cpr_mconfig->out_fmt;
- struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)cpr_mconfig;
-
- skl_set_base_module_format(ctx, mconfig, base_cfg);
-
- skl_setup_out_format(ctx, mconfig, out_fmt);
- skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig);
-}
-
-/*
- * Algo module are DSP pre processing modules. Algo module take base module
- * configuration and params
- */
-
-static void skl_set_algo_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_algo_cfg *algo_mcfg)
-{
- struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)algo_mcfg;
-
- skl_set_base_module_format(ctx, mconfig, base_cfg);
-
- if (mconfig->formats_config.caps_size == 0)
- return;
-
- memcpy(algo_mcfg->params,
- mconfig->formats_config.caps,
- mconfig->formats_config.caps_size);
-
-}
-
-/*
- * Mic select module allows selecting one or many input channels, thus
- * acting as a demux.
- *
- * Mic select module take base module configuration and out-format
- * configuration
- */
-static void skl_set_base_outfmt_format(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_base_outfmt_cfg *base_outfmt_mcfg)
-{
- struct skl_audio_data_format *out_fmt = &base_outfmt_mcfg->out_fmt;
- struct skl_base_cfg *base_cfg =
- (struct skl_base_cfg *)base_outfmt_mcfg;
-
- skl_set_base_module_format(ctx, mconfig, base_cfg);
- skl_setup_out_format(ctx, mconfig, out_fmt);
-}
-
-static u16 skl_get_module_param_size(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig)
-{
- u16 param_size;
-
- switch (mconfig->m_type) {
- case SKL_MODULE_TYPE_COPIER:
- param_size = sizeof(struct skl_cpr_cfg);
- param_size += mconfig->formats_config.caps_size;
- return param_size;
-
- case SKL_MODULE_TYPE_SRCINT:
- return sizeof(struct skl_src_module_cfg);
-
- case SKL_MODULE_TYPE_UPDWMIX:
- return sizeof(struct skl_up_down_mixer_cfg);
-
- case SKL_MODULE_TYPE_ALGO:
- param_size = sizeof(struct skl_base_cfg);
- param_size += mconfig->formats_config.caps_size;
- return param_size;
-
- case SKL_MODULE_TYPE_BASE_OUTFMT:
- return sizeof(struct skl_base_outfmt_cfg);
-
- default:
- /*
- * return only base cfg when no specific module type is
- * specified
- */
- return sizeof(struct skl_base_cfg);
- }
-
- return 0;
-}
-
-/*
- * DSP firmware supports various modules like copier, SRC, updown etc.
- * These modules required various parameters to be calculated and sent for
- * the module initialization to DSP. By default a generic module needs only
- * base module format configuration
- */
-
-static int skl_set_module_format(struct skl_sst *ctx,
- struct skl_module_cfg *module_config,
- u16 *module_config_size,
- void **param_data)
-{
- u16 param_size;
-
- param_size = skl_get_module_param_size(ctx, module_config);
-
- *param_data = kzalloc(param_size, GFP_KERNEL);
- if (NULL == *param_data)
- return -ENOMEM;
-
- *module_config_size = param_size;
-
- switch (module_config->m_type) {
- case SKL_MODULE_TYPE_COPIER:
- skl_set_copier_format(ctx, module_config, *param_data);
- break;
-
- case SKL_MODULE_TYPE_SRCINT:
- skl_set_src_format(ctx, module_config, *param_data);
- break;
-
- case SKL_MODULE_TYPE_UPDWMIX:
- skl_set_updown_mixer_format(ctx, module_config, *param_data);
- break;
-
- case SKL_MODULE_TYPE_ALGO:
- skl_set_algo_format(ctx, module_config, *param_data);
- break;
-
- case SKL_MODULE_TYPE_BASE_OUTFMT:
- skl_set_base_outfmt_format(ctx, module_config, *param_data);
- break;
-
- default:
- skl_set_base_module_format(ctx, module_config, *param_data);
- break;
-
- }
-
- dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n",
- module_config->id.module_id, param_size);
- print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4,
- *param_data, param_size, false);
- return 0;
-}
-
-static int skl_get_queue_index(struct skl_module_pin *mpin,
- struct skl_module_inst_id id, int max)
-{
- int i;
-
- for (i = 0; i < max; i++) {
- if (mpin[i].id.module_id == id.module_id &&
- mpin[i].id.instance_id == id.instance_id)
- return i;
- }
-
- return -EINVAL;
-}
-
-/*
- * Allocates queue for each module.
- * if dynamic, the pin_index is allocated 0 to max_pin.
- * In static, the pin_index is fixed based on module_id and instance id
- */
-static int skl_alloc_queue(struct skl_module_pin *mpin,
- struct skl_module_cfg *tgt_cfg, int max)
-{
- int i;
- struct skl_module_inst_id id = tgt_cfg->id;
- /*
- * if pin in dynamic, find first free pin
- * otherwise find match module and instance id pin as topology will
- * ensure a unique pin is assigned to this so no need to
- * allocate/free
- */
- for (i = 0; i < max; i++) {
- if (mpin[i].is_dynamic) {
- if (!mpin[i].in_use &&
- mpin[i].pin_state == SKL_PIN_UNBIND) {
-
- mpin[i].in_use = true;
- mpin[i].id.module_id = id.module_id;
- mpin[i].id.instance_id = id.instance_id;
- mpin[i].tgt_mcfg = tgt_cfg;
- return i;
- }
- } else {
- if (mpin[i].id.module_id == id.module_id &&
- mpin[i].id.instance_id == id.instance_id &&
- mpin[i].pin_state == SKL_PIN_UNBIND) {
-
- mpin[i].tgt_mcfg = tgt_cfg;
- return i;
- }
- }
- }
-
- return -EINVAL;
-}
-
-static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
-{
- if (mpin[q_index].is_dynamic) {
- mpin[q_index].in_use = false;
- mpin[q_index].id.module_id = 0;
- mpin[q_index].id.instance_id = 0;
- }
- mpin[q_index].pin_state = SKL_PIN_UNBIND;
- mpin[q_index].tgt_mcfg = NULL;
-}
-
-/* Module state will be set to unint, if all the out pin state is UNBIND */
-
-static void skl_clear_module_state(struct skl_module_pin *mpin, int max,
- struct skl_module_cfg *mcfg)
-{
- int i;
- bool found = false;
-
- for (i = 0; i < max; i++) {
- if (mpin[i].pin_state == SKL_PIN_UNBIND)
- continue;
- found = true;
- break;
- }
-
- if (!found)
- mcfg->m_state = SKL_MODULE_UNINIT;
- return;
-}
-
-/*
- * A module needs to be instanataited in DSP. A mdoule is present in a
- * collection of module referred as a PIPE.
- * We first calculate the module format, based on module type and then
- * invoke the DSP by sending IPC INIT_INSTANCE using ipc helper
- */
-int skl_init_module(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig)
-{
- u16 module_config_size = 0;
- void *param_data = NULL;
- int ret;
- struct skl_ipc_init_instance_msg msg;
-
- dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__,
- mconfig->id.module_id, mconfig->id.instance_id);
-
- if (mconfig->pipe->state != SKL_PIPE_CREATED) {
- dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n",
- mconfig->pipe->state, mconfig->pipe->ppl_id);
- return -EIO;
- }
-
- ret = skl_set_module_format(ctx, mconfig,
- &module_config_size, &param_data);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to set module format ret=%d\n", ret);
- return ret;
- }
-
- msg.module_id = mconfig->id.module_id;
- msg.instance_id = mconfig->id.instance_id;
- msg.ppl_instance_id = mconfig->pipe->ppl_id;
- msg.param_data_size = module_config_size;
- msg.core_id = mconfig->core_id;
-
- ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to init instance ret=%d\n", ret);
- kfree(param_data);
- return ret;
- }
- mconfig->m_state = SKL_MODULE_INIT_DONE;
-
- return ret;
-}
-
-static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg
- *src_module, struct skl_module_cfg *dst_module)
-{
- dev_dbg(ctx->dev, "%s: src module_id = %d src_instance=%d\n",
- __func__, src_module->id.module_id, src_module->id.instance_id);
- dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__,
- dst_module->id.module_id, dst_module->id.instance_id);
-
- dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n",
- src_module->m_state, dst_module->m_state);
-}
-
-/*
- * On module freeup, we need to unbind the module with modules
- * it is already bind.
- * Find the pin allocated and unbind then using bind_unbind IPC
- */
-int skl_unbind_modules(struct skl_sst *ctx,
- struct skl_module_cfg *src_mcfg,
- struct skl_module_cfg *dst_mcfg)
-{
- int ret;
- struct skl_ipc_bind_unbind_msg msg;
- struct skl_module_inst_id src_id = src_mcfg->id;
- struct skl_module_inst_id dst_id = dst_mcfg->id;
- int in_max = dst_mcfg->max_in_queue;
- int out_max = src_mcfg->max_out_queue;
- int src_index, dst_index, src_pin_state, dst_pin_state;
-
- skl_dump_bind_info(ctx, src_mcfg, dst_mcfg);
-
- /* get src queue index */
- src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max);
- if (src_index < 0)
- return -EINVAL;
-
- msg.src_queue = src_index;
-
- /* get dst queue index */
- dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max);
- if (dst_index < 0)
- return -EINVAL;
-
- msg.dst_queue = dst_index;
-
- src_pin_state = src_mcfg->m_out_pin[src_index].pin_state;
- dst_pin_state = dst_mcfg->m_in_pin[dst_index].pin_state;
-
- if (src_pin_state != SKL_PIN_BIND_DONE ||
- dst_pin_state != SKL_PIN_BIND_DONE)
- return 0;
-
- msg.module_id = src_mcfg->id.module_id;
- msg.instance_id = src_mcfg->id.instance_id;
- msg.dst_module_id = dst_mcfg->id.module_id;
- msg.dst_instance_id = dst_mcfg->id.instance_id;
- msg.bind = false;
-
- ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
- if (!ret) {
- /* free queue only if unbind is success */
- skl_free_queue(src_mcfg->m_out_pin, src_index);
- skl_free_queue(dst_mcfg->m_in_pin, dst_index);
-
- /*
- * check only if src module bind state, bind is
- * always from src -> sink
- */
- skl_clear_module_state(src_mcfg->m_out_pin, out_max, src_mcfg);
- }
-
- return ret;
-}
-
-/*
- * Once a module is instantiated it need to be 'bind' with other modules in
- * the pipeline. For binding we need to find the module pins which are bind
- * together
- * This function finds the pins and then sends bund_unbind IPC message to
- * DSP using IPC helper
- */
-int skl_bind_modules(struct skl_sst *ctx,
- struct skl_module_cfg *src_mcfg,
- struct skl_module_cfg *dst_mcfg)
-{
- int ret;
- struct skl_ipc_bind_unbind_msg msg;
- int in_max = dst_mcfg->max_in_queue;
- int out_max = src_mcfg->max_out_queue;
- int src_index, dst_index;
-
- skl_dump_bind_info(ctx, src_mcfg, dst_mcfg);
-
- if (src_mcfg->m_state < SKL_MODULE_INIT_DONE &&
- dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
- return 0;
-
- src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_mcfg, out_max);
- if (src_index < 0)
- return -EINVAL;
-
- msg.src_queue = src_index;
- dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_mcfg, in_max);
- if (dst_index < 0) {
- skl_free_queue(src_mcfg->m_out_pin, src_index);
- return -EINVAL;
- }
-
- msg.dst_queue = dst_index;
-
- dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n",
- msg.src_queue, msg.dst_queue);
-
- msg.module_id = src_mcfg->id.module_id;
- msg.instance_id = src_mcfg->id.instance_id;
- msg.dst_module_id = dst_mcfg->id.module_id;
- msg.dst_instance_id = dst_mcfg->id.instance_id;
- msg.bind = true;
-
- ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
-
- if (!ret) {
- src_mcfg->m_state = SKL_MODULE_BIND_DONE;
- src_mcfg->m_out_pin[src_index].pin_state = SKL_PIN_BIND_DONE;
- dst_mcfg->m_in_pin[dst_index].pin_state = SKL_PIN_BIND_DONE;
- } else {
- /* error case , if IPC fails, clear the queue index */
- skl_free_queue(src_mcfg->m_out_pin, src_index);
- skl_free_queue(dst_mcfg->m_in_pin, dst_index);
- }
-
- return ret;
-}
-
-static int skl_set_pipe_state(struct skl_sst *ctx, struct skl_pipe *pipe,
- enum skl_ipc_pipeline_state state)
-{
- dev_dbg(ctx->dev, "%s: pipe_satate = %d\n", __func__, state);
-
- return skl_ipc_set_pipeline_state(&ctx->ipc, pipe->ppl_id, state);
-}
-
-/*
- * A pipeline is a collection of modules. Before a module in instantiated a
- * pipeline needs to be created for it.
- * This function creates pipeline, by sending create pipeline IPC messages
- * to FW
- */
-int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe)
-{
- int ret;
-
- dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id);
-
- ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages,
- pipe->pipe_priority, pipe->ppl_id);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to create pipeline\n");
- return ret;
- }
-
- pipe->state = SKL_PIPE_CREATED;
-
- return 0;
-}
-
-/*
- * A pipeline needs to be deleted on cleanup. If a pipeline is running, then
- * pause the pipeline first and then delete it
- * The pipe delete is done by sending delete pipeline IPC. DSP will stop the
- * DMA engines and releases resources
- */
-int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
-{
- int ret;
-
- dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
-
- /* If pipe is not started, do not try to stop the pipe in FW. */
- if (pipe->state > SKL_PIPE_STARTED) {
- ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to stop pipeline\n");
- return ret;
- }
-
- pipe->state = SKL_PIPE_PAUSED;
- } else {
- /* If pipe was not created in FW, do not try to delete it */
- if (pipe->state < SKL_PIPE_CREATED)
- return 0;
-
- ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
- if (ret < 0)
- dev_err(ctx->dev, "Failed to delete pipeline\n");
-
- pipe->state = SKL_PIPE_INVALID;
- }
-
- return ret;
-}
-
-/*
- * A pipeline is also a scheduling entity in DSP which can be run, stopped
- * For processing data the pipe need to be run by sending IPC set pipe state
- * to DSP
- */
-int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
-{
- int ret;
-
- dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
-
- /* If pipe was not created in FW, do not try to pause or delete */
- if (pipe->state < SKL_PIPE_CREATED)
- return 0;
-
- /* Pipe has to be paused before it is started */
- ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to pause pipe\n");
- return ret;
- }
-
- pipe->state = SKL_PIPE_PAUSED;
-
- ret = skl_set_pipe_state(ctx, pipe, PPL_RUNNING);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to start pipe\n");
- return ret;
- }
-
- pipe->state = SKL_PIPE_STARTED;
-
- return 0;
-}
-
-/*
- * Stop the pipeline by sending set pipe state IPC
- * DSP doesnt implement stop so we always send pause message
- */
-int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
-{
- int ret;
-
- dev_dbg(ctx->dev, "In %s pipe=%d\n", __func__, pipe->ppl_id);
-
- /* If pipe was not created in FW, do not try to pause or delete */
- if (pipe->state < SKL_PIPE_PAUSED)
- return 0;
-
- ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
- if (ret < 0) {
- dev_dbg(ctx->dev, "Failed to stop pipe\n");
- return ret;
- }
-
- pipe->state = SKL_PIPE_CREATED;
-
- return 0;
-}
-
-/* Algo parameter set helper function */
-int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
- u32 param_id, struct skl_module_cfg *mcfg)
-{
- struct skl_ipc_large_config_msg msg;
-
- msg.module_id = mcfg->id.module_id;
- msg.instance_id = mcfg->id.instance_id;
- msg.param_data_size = size;
- msg.large_param_id = param_id;
-
- return skl_ipc_set_large_config(&ctx->ipc, &msg, params);
-}
-
-int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
- u32 param_id, struct skl_module_cfg *mcfg)
-{
- struct skl_ipc_large_config_msg msg;
-
- msg.module_id = mcfg->id.module_id;
- msg.instance_id = mcfg->id.instance_id;
- msg.param_data_size = size;
- msg.large_param_id = param_id;
-
- return skl_ipc_get_large_config(&ctx->ipc, &msg, params);
-}
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
deleted file mode 100644
index 6e4b21cdb1bd..000000000000
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * skl-nhlt.c - Intel SKL Platform NHLT parsing
- *
- * Copyright (C) 2015 Intel Corp
- * Author: Sanjiv Kumar <sanjiv.kumar@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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-#include "skl.h"
-
-/* Unique identification for getting NHLT blobs */
-static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
- 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53};
-
-#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
-
-void *skl_nhlt_init(struct device *dev)
-{
- acpi_handle handle;
- union acpi_object *obj;
- struct nhlt_resource_desc *nhlt_ptr = NULL;
-
- if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
- dev_err(dev, "Requested NHLT device not found\n");
- return NULL;
- }
-
- obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL);
- if (obj && obj->type == ACPI_TYPE_BUFFER) {
- nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
-
- return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
- MEMREMAP_WB);
- }
-
- dev_err(dev, "device specific method to extract NHLT blob failed\n");
- return NULL;
-}
-
-void skl_nhlt_free(void *addr)
-{
- memunmap(addr);
-}
-
-static struct nhlt_specific_cfg *skl_get_specific_cfg(
- struct device *dev, struct nhlt_fmt *fmt,
- u8 no_ch, u32 rate, u16 bps, u8 linktype)
-{
- struct nhlt_specific_cfg *sp_config;
- struct wav_fmt *wfmt;
- struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config;
- int i;
-
- dev_dbg(dev, "Format count =%d\n", fmt->fmt_count);
-
- for (i = 0; i < fmt->fmt_count; i++) {
- wfmt = &fmt_config->fmt_ext.fmt;
- dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels,
- wfmt->bits_per_sample, wfmt->samples_per_sec);
- if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) {
- /*
- * if link type is dmic ignore rate check as the blob is
- * generic for all rates
- */
- sp_config = &fmt_config->config;
- if (linktype == NHLT_LINK_DMIC)
- return sp_config;
-
- if (wfmt->samples_per_sec == rate)
- return sp_config;
- }
-
- fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps +
- fmt_config->config.size);
- }
-
- return NULL;
-}
-
-static void dump_config(struct device *dev, u32 instance_id, u8 linktype,
- u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps)
-{
- dev_dbg(dev, "Input configuration\n");
- dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate);
- dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype);
- dev_dbg(dev, "bits_per_sample=%d\n", bps);
-}
-
-static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
- u32 instance_id, u8 link_type, u8 dirn)
-{
- dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d\n",
- epnt->virtual_bus_id, epnt->linktype, epnt->direction);
-
- if ((epnt->virtual_bus_id == instance_id) &&
- (epnt->linktype == link_type) &&
- (epnt->direction == dirn))
- return true;
- else
- return false;
-}
-
-struct nhlt_specific_cfg
-*skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type,
- u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn)
-{
- struct nhlt_fmt *fmt;
- struct nhlt_endpoint *epnt;
- struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
- struct device *dev = bus->dev;
- struct nhlt_specific_cfg *sp_config;
- struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
- u16 bps = (s_fmt == 16) ? 16 : 32;
- u8 j;
-
- dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps);
-
- epnt = (struct nhlt_endpoint *)nhlt->desc;
-
- dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count);
-
- for (j = 0; j < nhlt->endpoint_count; j++) {
- if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) {
- fmt = (struct nhlt_fmt *)(epnt->config.caps +
- epnt->config.size);
- sp_config = skl_get_specific_cfg(dev, fmt, num_ch,
- s_rate, bps, link_type);
- if (sp_config)
- return sp_config;
- }
-
- epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
- }
-
- return NULL;
-}
diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h
deleted file mode 100644
index 3769f9fefe2b..000000000000
--- a/sound/soc/intel/skylake/skl-nhlt.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * skl-nhlt.h - Intel HDA Platform NHLT header
- *
- * Copyright (C) 2015 Intel Corp
- * Author: Sanjiv Kumar <sanjiv.kumar@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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-#ifndef __SKL_NHLT_H__
-#define __SKL_NHLT_H__
-
-#include <linux/acpi.h>
-
-struct wav_fmt {
- u16 fmt_tag;
- u16 channels;
- u32 samples_per_sec;
- u32 avg_bytes_per_sec;
- u16 block_align;
- u16 bits_per_sample;
- u16 cb_size;
-} __packed;
-
-struct wav_fmt_ext {
- struct wav_fmt fmt;
- union samples {
- u16 valid_bits_per_sample;
- u16 samples_per_block;
- u16 reserved;
- } sample;
- u32 channel_mask;
- u8 sub_fmt[16];
-} __packed;
-
-enum nhlt_link_type {
- NHLT_LINK_HDA = 0,
- NHLT_LINK_DSP = 1,
- NHLT_LINK_DMIC = 2,
- NHLT_LINK_SSP = 3,
- NHLT_LINK_INVALID
-};
-
-enum nhlt_device_type {
- NHLT_DEVICE_BT = 0,
- NHLT_DEVICE_DMIC = 1,
- NHLT_DEVICE_I2S = 4,
- NHLT_DEVICE_INVALID
-};
-
-struct nhlt_specific_cfg {
- u32 size;
- u8 caps[0];
-} __packed;
-
-struct nhlt_fmt_cfg {
- struct wav_fmt_ext fmt_ext;
- struct nhlt_specific_cfg config;
-} __packed;
-
-struct nhlt_fmt {
- u8 fmt_count;
- struct nhlt_fmt_cfg fmt_config[0];
-} __packed;
-
-struct nhlt_endpoint {
- u32 length;
- u8 linktype;
- u8 instance_id;
- u16 vendor_id;
- u16 device_id;
- u16 revision_id;
- u32 subsystem_id;
- u8 device_type;
- u8 direction;
- u8 virtual_bus_id;
- struct nhlt_specific_cfg config;
-} __packed;
-
-struct nhlt_acpi_table {
- struct acpi_table_header header;
- u8 endpoint_count;
- struct nhlt_endpoint desc[0];
-} __packed;
-
-struct nhlt_resource_desc {
- u32 extra;
- u16 flags;
- u64 addr_spc_gra;
- u64 min_addr;
- u64 max_addr;
- u64 addr_trans_offset;
- u64 length;
-} __packed;
-
-#endif
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
deleted file mode 100644
index f3553258091a..000000000000
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ /dev/null
@@ -1,1058 +0,0 @@
-/*
- * skl-pcm.c -ASoC HDA Platform driver file implementing PCM functionality
- *
- * Copyright (C) 2014-2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#include <linux/pci.h>
-#include <linux/pm_runtime.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "skl.h"
-#include "skl-topology.h"
-#include "skl-sst-dsp.h"
-#include "skl-sst-ipc.h"
-
-#define HDA_MONO 1
-#define HDA_STEREO 2
-#define HDA_QUAD 4
-
-static struct snd_pcm_hardware azx_pcm_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_SYNC_START |
- SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
- SNDRV_PCM_INFO_HAS_LINK_ATIME |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S32_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_8000,
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = HDA_QUAD,
- .buffer_bytes_max = AZX_MAX_BUF_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
- .periods_min = 2,
- .periods_max = AZX_MAX_FRAG,
- .fifo_size = 0,
-};
-
-static inline
-struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream)
-{
- return substream->runtime->private_data;
-}
-
-static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream)
-{
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
- struct hdac_stream *hstream = hdac_stream(stream);
- struct hdac_bus *bus = hstream->bus;
-
- return hbus_to_ebus(bus);
-}
-
-static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus,
- struct snd_pcm_substream *substream,
- size_t size)
-{
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
-
- hdac_stream(stream)->bufsize = 0;
- hdac_stream(stream)->period_bytes = 0;
- hdac_stream(stream)->format_val = 0;
-
- return snd_pcm_lib_malloc_pages(substream, size);
-}
-
-static int skl_substream_free_pages(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
- struct snd_pcm_runtime *runtime)
-{
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-
- /* avoid wrap-around with wall-clock */
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
- 20, 178000000);
-}
-
-static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus)
-{
- if (ebus->ppcap)
- return HDAC_EXT_STREAM_TYPE_HOST;
- else
- return HDAC_EXT_STREAM_TYPE_COUPLED;
-}
-
-/*
- * check if the stream opened is marked as ignore_suspend by machine, if so
- * then enable suspend_active refcount
- *
- * The count supend_active does not need lock as it is used in open/close
- * and suspend context
- */
-static void skl_set_suspend_active(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai, bool enable)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct snd_soc_dapm_widget *w;
- struct skl *skl = ebus_to_skl(ebus);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
-
- if (w->ignore_suspend && enable)
- skl->supend_active++;
- else if (w->ignore_suspend && !enable)
- skl->supend_active--;
-}
-
-static int skl_pcm_open(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct hdac_ext_stream *stream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct skl_dma_params *dma_params;
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
-
- stream = snd_hdac_ext_stream_assign(ebus, substream,
- skl_get_host_stream_type(ebus));
- if (stream == NULL)
- return -EBUSY;
-
- skl_set_pcm_constrains(ebus, runtime);
-
- /*
- * disable WALLCLOCK timestamps for capture streams
- * until we figure out how to handle digital inputs
- */
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
- runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
- }
-
- runtime->private_data = stream;
-
- dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL);
- if (!dma_params)
- return -ENOMEM;
-
- dma_params->stream_tag = hdac_stream(stream)->stream_tag;
- snd_soc_dai_set_dma_data(dai, substream, dma_params);
-
- dev_dbg(dai->dev, "stream tag set in dma params=%d\n",
- dma_params->stream_tag);
- skl_set_suspend_active(substream, dai, true);
- snd_pcm_set_sync(substream);
-
- return 0;
-}
-
-static int skl_get_format(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct skl_dma_params *dma_params;
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- int format_val = 0;
-
- if (ebus->ppcap) {
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- format_val = snd_hdac_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- 32, 0);
- } else {
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
- if (dma_params)
- format_val = dma_params->format;
- }
-
- return format_val;
-}
-
-static int skl_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
- unsigned int format_val;
- int err;
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
-
- format_val = skl_get_format(substream, dai);
- dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
- hdac_stream(stream)->stream_tag, format_val);
- snd_hdac_stream_reset(hdac_stream(stream));
-
- err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
- if (err < 0)
- return err;
-
- err = snd_hdac_stream_setup(hdac_stream(stream));
- if (err < 0)
- return err;
-
- hdac_stream(stream)->prepared = 1;
-
- return err;
-}
-
-static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct skl_pipe_params p_params = {0};
- struct skl_module_cfg *m_cfg;
- int ret, dma_id;
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
- ret = skl_substream_alloc_pages(ebus, substream,
- params_buffer_bytes(params));
- if (ret < 0)
- return ret;
-
- dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n",
- runtime->rate, runtime->channels, runtime->format);
-
- dma_id = hdac_stream(stream)->stream_tag - 1;
- dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
-
- p_params.s_fmt = snd_pcm_format_width(params_format(params));
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.host_dma_id = dma_id;
- p_params.stream = substream->stream;
-
- m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
- if (m_cfg)
- skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params);
-
- return 0;
-}
-
-static void skl_pcm_close(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct skl_dma_params *dma_params = NULL;
- struct skl *skl = ebus_to_skl(ebus);
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
-
- snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(ebus));
-
- dma_params = snd_soc_dai_get_dma_data(dai, substream);
- /*
- * now we should set this to NULL as we are freeing by the
- * dma_params
- */
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- skl_set_suspend_active(substream, dai, false);
-
- /*
- * check if close is for "Reference Pin" and set back the
- * CGCTL.MISCBDCGE if disabled by driver
- */
- if (!strncmp(dai->name, "Reference Pin", 13) &&
- skl->skl_sst->miscbdcg_disabled) {
- skl->skl_sst->enable_miscbdcge(dai->dev, true);
- skl->skl_sst->miscbdcg_disabled = false;
- }
-
- kfree(dma_params);
-}
-
-static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
-
- snd_hdac_stream_cleanup(hdac_stream(stream));
- hdac_stream(stream)->prepared = 0;
-
- return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
-}
-
-static int skl_be_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct skl_pipe_params p_params = {0};
-
- p_params.s_fmt = snd_pcm_format_width(params_format(params));
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.stream = substream->stream;
-
- return skl_tplg_be_update_params(dai, &p_params);
-}
-
-static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct hdac_ext_bus *ebus = get_bus_ctx(substream);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct hdac_ext_stream *stream;
- int start;
- unsigned long cookie;
- struct hdac_stream *hstr;
-
- stream = get_hdac_ext_stream(substream);
- hstr = hdac_stream(stream);
-
- if (!hstr->prepared)
- return -EPIPE;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- start = 1;
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- start = 0;
- break;
-
- default:
- return -EINVAL;
- }
-
- spin_lock_irqsave(&bus->reg_lock, cookie);
-
- if (start) {
- snd_hdac_stream_start(hdac_stream(stream), true);
- snd_hdac_stream_timecounter_init(hstr, 0);
- } else {
- snd_hdac_stream_stop(hdac_stream(stream));
- }
-
- spin_unlock_irqrestore(&bus->reg_lock, cookie);
-
- return 0;
-}
-
-static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct skl *skl = get_skl_ctx(dai->dev);
- struct skl_sst *ctx = skl->skl_sst;
- struct skl_module_cfg *mconfig;
- struct hdac_ext_bus *ebus = get_bus_ctx(substream);
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
- int ret;
-
- mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
- if (!mconfig)
- return -EIO;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- skl_pcm_prepare(substream, dai);
- /*
- * enable DMA Resume enable bit for the stream, set the dpib
- * & lpib position to resune before starting the DMA
- */
- snd_hdac_ext_stream_drsm_enable(ebus, true,
- hdac_stream(stream)->index);
- snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
- snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
-
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- /*
- * Start HOST DMA and Start FE Pipe.This is to make sure that
- * there are no underrun/overrun in the case when the FE
- * pipeline is started but there is a delay in starting the
- * DMA channel on the host.
- */
- snd_hdac_ext_stream_decouple(ebus, stream, true);
- ret = skl_decoupled_trigger(substream, cmd);
- if (ret < 0)
- return ret;
- return skl_run_pipe(ctx, mconfig->pipe);
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- /*
- * Stop FE Pipe first and stop DMA. This is to make sure that
- * there are no underrun/overrun in the case if there is a delay
- * between the two operations.
- */
- ret = skl_stop_pipe(ctx, mconfig->pipe);
- if (ret < 0)
- return ret;
-
- ret = skl_decoupled_trigger(substream, cmd);
- if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
- /* save the dpib and lpib positions */
- stream->dpib = readl(ebus->bus.remap_addr +
- AZX_REG_VS_SDXDPIB_XBASE +
- (AZX_REG_VS_SDXDPIB_XINTERVAL *
- hdac_stream(stream)->index));
-
- stream->lpib = snd_hdac_stream_get_pos_lpib(
- hdac_stream(stream));
- snd_hdac_ext_stream_decouple(ebus, stream, false);
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int skl_link_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct hdac_ext_stream *link_dev;
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct skl_dma_params *dma_params;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct skl_pipe_params p_params = {0};
-
- link_dev = snd_hdac_ext_stream_assign(ebus, substream,
- HDAC_EXT_STREAM_TYPE_LINK);
- if (!link_dev)
- return -EBUSY;
-
- snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
-
- /* set the stream tag in the codec dai dma params */
- dma_params = (struct skl_dma_params *)
- snd_soc_dai_get_dma_data(codec_dai, substream);
- if (dma_params)
- dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
-
- p_params.s_fmt = snd_pcm_format_width(params_format(params));
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.stream = substream->stream;
- p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
-
- return skl_tplg_be_update_params(dai, &p_params);
-}
-
-static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- unsigned int format_val = 0;
- struct skl_dma_params *dma_params;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct hdac_ext_link *link;
-
- dma_params = (struct skl_dma_params *)
- snd_soc_dai_get_dma_data(codec_dai, substream);
- if (dma_params)
- format_val = dma_params->format;
- dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n",
- hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name);
-
- link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
- if (!link)
- return -EINVAL;
-
- snd_hdac_ext_bus_link_power_up(link);
- snd_hdac_ext_link_stream_reset(link_dev);
-
- snd_hdac_ext_link_stream_setup(link_dev, format_val);
-
- snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
- link_dev->link_prepared = 1;
-
- return 0;
-}
-
-static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- struct hdac_ext_bus *ebus = get_bus_ctx(substream);
- struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
-
- dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- skl_link_pcm_prepare(substream, dai);
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_stream_decouple(ebus, stream, true);
- snd_hdac_ext_link_stream_start(link_dev);
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- snd_hdac_ext_link_stream_clear(link_dev);
- if (cmd == SNDRV_PCM_TRIGGER_SUSPEND)
- snd_hdac_ext_stream_decouple(ebus, stream, false);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int skl_link_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- struct hdac_ext_link *link;
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
-
- link_dev->link_prepared = 0;
-
- link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
- if (!link)
- return -EINVAL;
-
- snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag);
- snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
- return 0;
-}
-
-static struct snd_soc_dai_ops skl_pcm_dai_ops = {
- .startup = skl_pcm_open,
- .shutdown = skl_pcm_close,
- .prepare = skl_pcm_prepare,
- .hw_params = skl_pcm_hw_params,
- .hw_free = skl_pcm_hw_free,
- .trigger = skl_pcm_trigger,
-};
-
-static struct snd_soc_dai_ops skl_dmic_dai_ops = {
- .hw_params = skl_be_hw_params,
-};
-
-static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
- .hw_params = skl_be_hw_params,
-};
-
-static struct snd_soc_dai_ops skl_link_dai_ops = {
- .prepare = skl_link_pcm_prepare,
- .hw_params = skl_link_hw_params,
- .hw_free = skl_link_hw_free,
- .trigger = skl_link_pcm_trigger,
-};
-
-static struct snd_soc_dai_driver skl_platform_dai[] = {
-{
- .name = "System Pin",
- .ops = &skl_pcm_dai_ops,
- .playback = {
- .stream_name = "System Playback",
- .channels_min = HDA_MONO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .capture = {
- .stream_name = "System Capture",
- .channels_min = HDA_MONO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "Reference Pin",
- .ops = &skl_pcm_dai_ops,
- .capture = {
- .stream_name = "Reference Capture",
- .channels_min = HDA_MONO,
- .channels_max = HDA_QUAD,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "Deepbuffer Pin",
- .ops = &skl_pcm_dai_ops,
- .playback = {
- .stream_name = "Deepbuffer Playback",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "LowLatency Pin",
- .ops = &skl_pcm_dai_ops,
- .playback = {
- .stream_name = "Low Latency Playback",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "DMIC Pin",
- .ops = &skl_pcm_dai_ops,
- .capture = {
- .stream_name = "DMIC Capture",
- .channels_min = HDA_MONO,
- .channels_max = HDA_QUAD,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-
-/* BE CPU Dais */
-{
- .name = "SSP0 Pin",
- .ops = &skl_be_ssp_dai_ops,
- .playback = {
- .stream_name = "ssp0 Tx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "ssp0 Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "SSP1 Pin",
- .ops = &skl_be_ssp_dai_ops,
- .playback = {
- .stream_name = "ssp1 Tx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "ssp1 Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "iDisp Pin",
- .ops = &skl_link_dai_ops,
- .playback = {
- .stream_name = "iDisp Tx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "DMIC01 Pin",
- .ops = &skl_dmic_dai_ops,
- .capture = {
- .stream_name = "DMIC01 Rx",
- .channels_min = HDA_MONO,
- .channels_max = HDA_QUAD,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "HD-Codec Pin",
- .ops = &skl_link_dai_ops,
- .playback = {
- .stream_name = "HD-Codec Tx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "HD-Codec Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-};
-
-static int skl_platform_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
-
- dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__,
- dai_link->cpu_dai_name);
-
- runtime = substream->runtime;
- snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw);
-
- return 0;
-}
-
-static int skl_coupled_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct hdac_ext_bus *ebus = get_bus_ctx(substream);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct hdac_ext_stream *stream;
- struct snd_pcm_substream *s;
- bool start;
- int sbits = 0;
- unsigned long cookie;
- struct hdac_stream *hstr;
-
- stream = get_hdac_ext_stream(substream);
- hstr = hdac_stream(stream);
-
- dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd);
-
- if (!hstr->prepared)
- return -EPIPE;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- start = true;
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- start = false;
- break;
-
- default:
- return -EINVAL;
- }
-
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- stream = get_hdac_ext_stream(s);
- sbits |= 1 << hdac_stream(stream)->index;
- snd_pcm_trigger_done(s, substream);
- }
-
- spin_lock_irqsave(&bus->reg_lock, cookie);
-
- /* first, set SYNC bits of corresponding streams */
- snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC);
-
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- stream = get_hdac_ext_stream(s);
- if (start)
- snd_hdac_stream_start(hdac_stream(stream), true);
- else
- snd_hdac_stream_stop(hdac_stream(stream));
- }
- spin_unlock_irqrestore(&bus->reg_lock, cookie);
-
- snd_hdac_stream_sync(hstr, start, sbits);
-
- spin_lock_irqsave(&bus->reg_lock, cookie);
-
- /* reset SYNC bits */
- snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC);
- if (start)
- snd_hdac_stream_timecounter_init(hstr, sbits);
- spin_unlock_irqrestore(&bus->reg_lock, cookie);
-
- return 0;
-}
-
-static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct hdac_ext_bus *ebus = get_bus_ctx(substream);
-
- if (!ebus->ppcap)
- return skl_coupled_trigger(substream, cmd);
-
- return 0;
-}
-
-/* calculate runtime delay from LPIB */
-static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *sstream,
- unsigned int pos)
-{
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct hdac_stream *hstream = hdac_stream(sstream);
- struct snd_pcm_substream *substream = hstream->substream;
- int stream = substream->stream;
- unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream);
- int delay;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- delay = pos - lpib_pos;
- else
- delay = lpib_pos - pos;
-
- if (delay < 0) {
- if (delay >= hstream->delay_negative_threshold)
- delay = 0;
- else
- delay += hstream->bufsize;
- }
-
- if (delay >= hstream->period_bytes) {
- dev_info(bus->dev,
- "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
- delay, hstream->period_bytes);
- delay = 0;
- }
-
- return bytes_to_frames(substream->runtime, delay);
-}
-
-static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
- int codec_delay)
-{
- struct hdac_stream *hstr = hdac_stream(hstream);
- struct snd_pcm_substream *substream = hstr->substream;
- struct hdac_ext_bus *ebus;
- unsigned int pos;
- int delay;
-
- /* use the position buffer as default */
- pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
-
- if (pos >= hdac_stream(hstream)->bufsize)
- pos = 0;
-
- if (substream->runtime) {
- ebus = get_bus_ctx(substream);
- delay = skl_get_delay_from_lpib(ebus, hstream, pos)
- + codec_delay;
- substream->runtime->delay += delay;
- }
-
- return pos;
-}
-
-static snd_pcm_uframes_t skl_platform_pcm_pointer
- (struct snd_pcm_substream *substream)
-{
- struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
-
- return bytes_to_frames(substream->runtime,
- skl_get_position(hstream, 0));
-}
-
-static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
- u64 nsec)
-{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- u64 codec_frames, codec_nsecs;
-
- if (!codec_dai->driver->ops->delay)
- return nsec;
-
- codec_frames = codec_dai->driver->ops->delay(substream, codec_dai);
- codec_nsecs = div_u64(codec_frames * 1000000000LL,
- substream->runtime->rate);
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- return nsec + codec_nsecs;
-
- return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
-}
-
-static int skl_get_time_info(struct snd_pcm_substream *substream,
- struct timespec *system_ts, struct timespec *audio_ts,
- struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
- struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
-{
- struct hdac_ext_stream *sstream = get_hdac_ext_stream(substream);
- struct hdac_stream *hstr = hdac_stream(sstream);
- u64 nsec;
-
- if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
- (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
-
- snd_pcm_gettime(substream->runtime, system_ts);
-
- nsec = timecounter_read(&hstr->tc);
- nsec = div_u64(nsec, 3); /* can be optimized */
- if (audio_tstamp_config->report_delay)
- nsec = skl_adjust_codec_delay(substream, nsec);
-
- *audio_ts = ns_to_timespec(nsec);
-
- audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
- audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */
- audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */
-
- } else {
- audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
- }
-
- return 0;
-}
-
-static struct snd_pcm_ops skl_platform_ops = {
- .open = skl_platform_open,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = skl_platform_pcm_trigger,
- .pointer = skl_platform_pcm_pointer,
- .get_time_info = skl_get_time_info,
- .mmap = snd_pcm_lib_default_mmap,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static void skl_pcm_free(struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
-
-static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *dai = rtd->cpu_dai;
- struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
- struct snd_pcm *pcm = rtd->pcm;
- unsigned int size;
- int retval = 0;
- struct skl *skl = ebus_to_skl(ebus);
-
- if (dai->driver->playback.channels_min ||
- dai->driver->capture.channels_min) {
- /* buffer pre-allocation */
- size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
- if (size > MAX_PREALLOC_SIZE)
- size = MAX_PREALLOC_SIZE;
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(skl->pci),
- size, MAX_PREALLOC_SIZE);
- if (retval) {
- dev_err(dai->dev, "dma buffer allocationf fail\n");
- return retval;
- }
- }
-
- return retval;
-}
-
-static int skl_platform_soc_probe(struct snd_soc_platform *platform)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
-
- if (ebus->ppcap)
- return skl_tplg_init(platform, ebus);
-
- return 0;
-}
-static struct snd_soc_platform_driver skl_platform_drv = {
- .probe = skl_platform_soc_probe,
- .ops = &skl_platform_ops,
- .pcm_new = skl_pcm_new,
- .pcm_free = skl_pcm_free,
-};
-
-static const struct snd_soc_component_driver skl_component = {
- .name = "pcm",
-};
-
-int skl_platform_register(struct device *dev)
-{
- int ret;
- struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
- struct skl *skl = ebus_to_skl(ebus);
-
- INIT_LIST_HEAD(&skl->ppl_list);
-
- ret = snd_soc_register_platform(dev, &skl_platform_drv);
- if (ret) {
- dev_err(dev, "soc platform registration failed %d\n", ret);
- return ret;
- }
- ret = snd_soc_register_component(dev, &skl_component,
- skl_platform_dai,
- ARRAY_SIZE(skl_platform_dai));
- if (ret) {
- dev_err(dev, "soc component registration failed %d\n", ret);
- snd_soc_unregister_platform(dev);
- }
-
- return ret;
-
-}
-
-int skl_platform_unregister(struct device *dev)
-{
- snd_soc_unregister_component(dev);
- snd_soc_unregister_platform(dev);
- return 0;
-}
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c
deleted file mode 100644
index da2329d17f4d..000000000000
--- a/sound/soc/intel/skylake/skl-sst-cldma.c
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * skl-sst-cldma.c - Code Loader DMA handler
- *
- * Copyright (C) 2015, Intel Corporation.
- * Author: Subhransu S. Prusty <subhransu.s.prusty@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 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/device.h>
-#include <linux/mm.h>
-#include <linux/kthread.h>
-#include <linux/delay.h>
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-
-static void skl_cldma_int_enable(struct sst_dsp *ctx)
-{
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC,
- SKL_ADSPIC_CL_DMA, SKL_ADSPIC_CL_DMA);
-}
-
-void skl_cldma_int_disable(struct sst_dsp *ctx)
-{
- sst_dsp_shim_update_bits_unlocked(ctx,
- SKL_ADSP_REG_ADSPIC, SKL_ADSPIC_CL_DMA, 0);
-}
-
-static void skl_cldma_stream_run(struct sst_dsp *ctx, bool enable)
-{
- unsigned char val;
- int timeout;
-
- sst_dsp_shim_update_bits_unlocked(ctx,
- SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(enable));
-
- udelay(3);
- timeout = 300;
- do {
- /* waiting for hardware to report that the stream Run bit set */
- val = sst_dsp_shim_read(ctx, SKL_ADSP_REG_CL_SD_CTL) &
- CL_SD_CTL_RUN_MASK;
- if (enable && val)
- break;
- else if (!enable && !val)
- break;
- udelay(3);
- } while (--timeout);
-
- if (timeout == 0)
- dev_err(ctx->dev, "Failed to set Run bit=%d enable=%d\n", val, enable);
-}
-
-static void skl_cldma_stream_clear(struct sst_dsp *ctx)
-{
- /* make sure Run bit is cleared before setting stream register */
- skl_cldma_stream_run(ctx, 0);
-
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0));
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0));
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0));
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0));
-
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0));
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0);
-
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0);
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0);
-}
-
-/* Code loader helper APIs */
-static void skl_cldma_setup_bdle(struct sst_dsp *ctx,
- struct snd_dma_buffer *dmab_data,
- u32 **bdlp, int size, int with_ioc)
-{
- u32 *bdl = *bdlp;
-
- ctx->cl_dev.frags = 0;
- while (size > 0) {
- phys_addr_t addr = virt_to_phys(dmab_data->area +
- (ctx->cl_dev.frags * ctx->cl_dev.bufsize));
-
- bdl[0] = cpu_to_le32(lower_32_bits(addr));
- bdl[1] = cpu_to_le32(upper_32_bits(addr));
-
- bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize);
-
- size -= ctx->cl_dev.bufsize;
- bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
-
- bdl += 4;
- ctx->cl_dev.frags++;
- }
-}
-
-/*
- * Setup controller
- * Configure the registers to update the dma buffer address and
- * enable interrupts.
- * Note: Using the channel 1 for transfer
- */
-static void skl_cldma_setup_controller(struct sst_dsp *ctx,
- struct snd_dma_buffer *dmab_bdl, unsigned int max_size,
- u32 count)
-{
- skl_cldma_stream_clear(ctx);
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL,
- CL_SD_BDLPLBA(dmab_bdl->addr));
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU,
- CL_SD_BDLPUBA(dmab_bdl->addr));
-
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, max_size);
- sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, count - 1);
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(1));
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(1));
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(1));
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
- CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(FW_CL_STREAM_NUMBER));
-}
-
-static void skl_cldma_setup_spb(struct sst_dsp *ctx,
- unsigned int size, bool enable)
-{
- if (enable)
- sst_dsp_shim_update_bits_unlocked(ctx,
- SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL,
- CL_SPBFIFO_SPBFCCTL_SPIBE_MASK,
- CL_SPBFIFO_SPBFCCTL_SPIBE(1));
-
- sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, size);
-}
-
-static void skl_cldma_cleanup_spb(struct sst_dsp *ctx)
-{
- sst_dsp_shim_update_bits_unlocked(ctx,
- SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL,
- CL_SPBFIFO_SPBFCCTL_SPIBE_MASK,
- CL_SPBFIFO_SPBFCCTL_SPIBE(0));
-
- sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, 0);
-}
-
-static void skl_cldma_cleanup(struct sst_dsp *ctx)
-{
- skl_cldma_cleanup_spb(ctx);
- skl_cldma_stream_clear(ctx);
-
- ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
- ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl);
-}
-
-static int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
-{
- int ret = 0;
-
- if (!wait_event_timeout(ctx->cl_dev.wait_queue,
- ctx->cl_dev.wait_condition,
- msecs_to_jiffies(SKL_WAIT_TIMEOUT))) {
- dev_err(ctx->dev, "%s: Wait timeout\n", __func__);
- ret = -EIO;
- goto cleanup;
- }
-
- dev_dbg(ctx->dev, "%s: Event wake\n", __func__);
- if (ctx->cl_dev.wake_status != SKL_CL_DMA_BUF_COMPLETE) {
- dev_err(ctx->dev, "%s: DMA Error\n", __func__);
- ret = -EIO;
- }
-
-cleanup:
- ctx->cl_dev.wake_status = SKL_CL_DMA_STATUS_NONE;
- return ret;
-}
-
-static void skl_cldma_stop(struct sst_dsp *ctx)
-{
- skl_cldma_stream_run(ctx, false);
-}
-
-static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
- const void *curr_pos, bool intr_enable, bool trigger)
-{
- dev_dbg(ctx->dev, "Size: %x, intr_enable: %d\n", size, intr_enable);
- dev_dbg(ctx->dev, "buf_pos_index:%d, trigger:%d\n",
- ctx->cl_dev.dma_buffer_offset, trigger);
- dev_dbg(ctx->dev, "spib position: %d\n", ctx->cl_dev.curr_spib_pos);
-
- /*
- * Check if the size exceeds buffer boundary. If it exceeds
- * max_buffer size, then copy till buffer size and then copy
- * remaining buffer from the start of ring buffer.
- */
- if (ctx->cl_dev.dma_buffer_offset + size > ctx->cl_dev.bufsize) {
- unsigned int size_b = ctx->cl_dev.bufsize -
- ctx->cl_dev.dma_buffer_offset;
- memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
- curr_pos, size_b);
- size -= size_b;
- curr_pos += size_b;
- ctx->cl_dev.dma_buffer_offset = 0;
- }
-
- memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
- curr_pos, size);
-
- if (ctx->cl_dev.curr_spib_pos == ctx->cl_dev.bufsize)
- ctx->cl_dev.dma_buffer_offset = 0;
- else
- ctx->cl_dev.dma_buffer_offset = ctx->cl_dev.curr_spib_pos;
-
- ctx->cl_dev.wait_condition = false;
-
- if (intr_enable)
- skl_cldma_int_enable(ctx);
-
- ctx->cl_dev.ops.cl_setup_spb(ctx, ctx->cl_dev.curr_spib_pos, trigger);
- if (trigger)
- ctx->cl_dev.ops.cl_trigger(ctx, true);
-}
-
-/*
- * The CL dma doesn't have any way to update the transfer status until a BDL
- * buffer is fully transferred
- *
- * So Copying is divided in two parts.
- * 1. Interrupt on buffer done where the size to be transferred is more than
- * ring buffer size.
- * 2. Polling on fw register to identify if data left to transferred doesn't
- * fill the ring buffer. Caller takes care of polling the required status
- * register to identify the transfer status.
- */
-static int
-skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
-{
- int ret = 0;
- bool start = true;
- unsigned int excess_bytes;
- u32 size;
- unsigned int bytes_left = total_size;
- const void *curr_pos = bin;
-
- if (total_size <= 0)
- return -EINVAL;
-
- dev_dbg(ctx->dev, "%s: Total binary size: %u\n", __func__, bytes_left);
-
- while (bytes_left) {
- if (bytes_left > ctx->cl_dev.bufsize) {
-
- /*
- * dma transfers only till the write pointer as
- * updated in spib
- */
- if (ctx->cl_dev.curr_spib_pos == 0)
- ctx->cl_dev.curr_spib_pos = ctx->cl_dev.bufsize;
-
- size = ctx->cl_dev.bufsize;
- skl_cldma_fill_buffer(ctx, size, curr_pos, true, start);
-
- start = false;
- ret = skl_cldma_wait_interruptible(ctx);
- if (ret < 0) {
- skl_cldma_stop(ctx);
- return ret;
- }
-
- } else {
- skl_cldma_int_disable(ctx);
-
- if ((ctx->cl_dev.curr_spib_pos + bytes_left)
- <= ctx->cl_dev.bufsize) {
- ctx->cl_dev.curr_spib_pos += bytes_left;
- } else {
- excess_bytes = bytes_left -
- (ctx->cl_dev.bufsize -
- ctx->cl_dev.curr_spib_pos);
- ctx->cl_dev.curr_spib_pos = excess_bytes;
- }
-
- size = bytes_left;
- skl_cldma_fill_buffer(ctx, size,
- curr_pos, false, start);
- }
- bytes_left -= size;
- curr_pos = curr_pos + size;
- }
-
- return ret;
-}
-
-void skl_cldma_process_intr(struct sst_dsp *ctx)
-{
- u8 cl_dma_intr_status;
-
- cl_dma_intr_status =
- sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_CL_SD_STS);
-
- if (!(cl_dma_intr_status & SKL_CL_DMA_SD_INT_COMPLETE))
- ctx->cl_dev.wake_status = SKL_CL_DMA_ERR;
- else
- ctx->cl_dev.wake_status = SKL_CL_DMA_BUF_COMPLETE;
-
- ctx->cl_dev.wait_condition = true;
- wake_up(&ctx->cl_dev.wait_queue);
-}
-
-int skl_cldma_prepare(struct sst_dsp *ctx)
-{
- int ret;
- u32 *bdl;
-
- ctx->cl_dev.bufsize = SKL_MAX_BUFFER_SIZE;
-
- /* Allocate cl ops */
- ctx->cl_dev.ops.cl_setup_bdle = skl_cldma_setup_bdle;
- ctx->cl_dev.ops.cl_setup_controller = skl_cldma_setup_controller;
- ctx->cl_dev.ops.cl_setup_spb = skl_cldma_setup_spb;
- ctx->cl_dev.ops.cl_cleanup_spb = skl_cldma_cleanup_spb;
- ctx->cl_dev.ops.cl_trigger = skl_cldma_stream_run;
- ctx->cl_dev.ops.cl_cleanup_controller = skl_cldma_cleanup;
- ctx->cl_dev.ops.cl_copy_to_dmabuf = skl_cldma_copy_to_buf;
- ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop;
-
- /* Allocate buffer*/
- ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
- &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize);
- if (ret < 0) {
- dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret);
- return ret;
- }
- /* Setup Code loader BDL */
- ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
- &ctx->cl_dev.dmab_bdl, PAGE_SIZE);
- if (ret < 0) {
- dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret);
- ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
- return ret;
- }
- bdl = (u32 *)ctx->cl_dev.dmab_bdl.area;
-
- /* Allocate BDLs */
- ctx->cl_dev.ops.cl_setup_bdle(ctx, &ctx->cl_dev.dmab_data,
- &bdl, ctx->cl_dev.bufsize, 1);
- ctx->cl_dev.ops.cl_setup_controller(ctx, &ctx->cl_dev.dmab_bdl,
- ctx->cl_dev.bufsize, ctx->cl_dev.frags);
-
- ctx->cl_dev.curr_spib_pos = 0;
- ctx->cl_dev.dma_buffer_offset = 0;
- init_waitqueue_head(&ctx->cl_dev.wait_queue);
-
- return ret;
-}
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.h b/sound/soc/intel/skylake/skl-sst-cldma.h
deleted file mode 100644
index 99e4c86b6358..000000000000
--- a/sound/soc/intel/skylake/skl-sst-cldma.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Intel Code Loader DMA support
- *
- * Copyright (C) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as 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 SKL_SST_CLDMA_H_
-#define SKL_SST_CLDMA_H_
-
-#define FW_CL_STREAM_NUMBER 0x1
-
-#define DMA_ADDRESS_128_BITS_ALIGNMENT 7
-#define BDL_ALIGN(x) (x >> DMA_ADDRESS_128_BITS_ALIGNMENT)
-
-#define SKL_ADSPIC_CL_DMA 0x2
-#define SKL_ADSPIS_CL_DMA 0x2
-#define SKL_CL_DMA_SD_INT_DESC_ERR 0x10 /* Descriptor error interrupt */
-#define SKL_CL_DMA_SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
-#define SKL_CL_DMA_SD_INT_COMPLETE 0x04 /* Buffer completion interrupt */
-
-/* Intel HD Audio Code Loader DMA Registers */
-
-#define HDA_ADSP_LOADER_BASE 0x80
-
-/* Stream Registers */
-#define SKL_ADSP_REG_CL_SD_CTL (HDA_ADSP_LOADER_BASE + 0x00)
-#define SKL_ADSP_REG_CL_SD_STS (HDA_ADSP_LOADER_BASE + 0x03)
-#define SKL_ADSP_REG_CL_SD_LPIB (HDA_ADSP_LOADER_BASE + 0x04)
-#define SKL_ADSP_REG_CL_SD_CBL (HDA_ADSP_LOADER_BASE + 0x08)
-#define SKL_ADSP_REG_CL_SD_LVI (HDA_ADSP_LOADER_BASE + 0x0c)
-#define SKL_ADSP_REG_CL_SD_FIFOW (HDA_ADSP_LOADER_BASE + 0x0e)
-#define SKL_ADSP_REG_CL_SD_FIFOSIZE (HDA_ADSP_LOADER_BASE + 0x10)
-#define SKL_ADSP_REG_CL_SD_FORMAT (HDA_ADSP_LOADER_BASE + 0x12)
-#define SKL_ADSP_REG_CL_SD_FIFOL (HDA_ADSP_LOADER_BASE + 0x14)
-#define SKL_ADSP_REG_CL_SD_BDLPL (HDA_ADSP_LOADER_BASE + 0x18)
-#define SKL_ADSP_REG_CL_SD_BDLPU (HDA_ADSP_LOADER_BASE + 0x1c)
-
-/* CL: Software Position Based FIFO Capability Registers */
-#define SKL_ADSP_REG_CL_SPBFIFO (HDA_ADSP_LOADER_BASE + 0x20)
-#define SKL_ADSP_REG_CL_SPBFIFO_SPBFCH (SKL_ADSP_REG_CL_SPBFIFO + 0x0)
-#define SKL_ADSP_REG_CL_SPBFIFO_SPBFCCTL (SKL_ADSP_REG_CL_SPBFIFO + 0x4)
-#define SKL_ADSP_REG_CL_SPBFIFO_SPIB (SKL_ADSP_REG_CL_SPBFIFO + 0x8)
-#define SKL_ADSP_REG_CL_SPBFIFO_MAXFIFOS (SKL_ADSP_REG_CL_SPBFIFO + 0xc)
-
-/* CL: Stream Descriptor x Control */
-
-/* Stream Reset */
-#define CL_SD_CTL_SRST_SHIFT 0
-#define CL_SD_CTL_SRST_MASK (1 << CL_SD_CTL_SRST_SHIFT)
-#define CL_SD_CTL_SRST(x) \
- ((x << CL_SD_CTL_SRST_SHIFT) & CL_SD_CTL_SRST_MASK)
-
-/* Stream Run */
-#define CL_SD_CTL_RUN_SHIFT 1
-#define CL_SD_CTL_RUN_MASK (1 << CL_SD_CTL_RUN_SHIFT)
-#define CL_SD_CTL_RUN(x) \
- ((x << CL_SD_CTL_RUN_SHIFT) & CL_SD_CTL_RUN_MASK)
-
-/* Interrupt On Completion Enable */
-#define CL_SD_CTL_IOCE_SHIFT 2
-#define CL_SD_CTL_IOCE_MASK (1 << CL_SD_CTL_IOCE_SHIFT)
-#define CL_SD_CTL_IOCE(x) \
- ((x << CL_SD_CTL_IOCE_SHIFT) & CL_SD_CTL_IOCE_MASK)
-
-/* FIFO Error Interrupt Enable */
-#define CL_SD_CTL_FEIE_SHIFT 3
-#define CL_SD_CTL_FEIE_MASK (1 << CL_SD_CTL_FEIE_SHIFT)
-#define CL_SD_CTL_FEIE(x) \
- ((x << CL_SD_CTL_FEIE_SHIFT) & CL_SD_CTL_FEIE_MASK)
-
-/* Descriptor Error Interrupt Enable */
-#define CL_SD_CTL_DEIE_SHIFT 4
-#define CL_SD_CTL_DEIE_MASK (1 << CL_SD_CTL_DEIE_SHIFT)
-#define CL_SD_CTL_DEIE(x) \
- ((x << CL_SD_CTL_DEIE_SHIFT) & CL_SD_CTL_DEIE_MASK)
-
-/* FIFO Limit Change */
-#define CL_SD_CTL_FIFOLC_SHIFT 5
-#define CL_SD_CTL_FIFOLC_MASK (1 << CL_SD_CTL_FIFOLC_SHIFT)
-#define CL_SD_CTL_FIFOLC(x) \
- ((x << CL_SD_CTL_FIFOLC_SHIFT) & CL_SD_CTL_FIFOLC_MASK)
-
-/* Stripe Control */
-#define CL_SD_CTL_STRIPE_SHIFT 16
-#define CL_SD_CTL_STRIPE_MASK (0x3 << CL_SD_CTL_STRIPE_SHIFT)
-#define CL_SD_CTL_STRIPE(x) \
- ((x << CL_SD_CTL_STRIPE_SHIFT) & CL_SD_CTL_STRIPE_MASK)
-
-/* Traffic Priority */
-#define CL_SD_CTL_TP_SHIFT 18
-#define CL_SD_CTL_TP_MASK (1 << CL_SD_CTL_TP_SHIFT)
-#define CL_SD_CTL_TP(x) \
- ((x << CL_SD_CTL_TP_SHIFT) & CL_SD_CTL_TP_MASK)
-
-/* Bidirectional Direction Control */
-#define CL_SD_CTL_DIR_SHIFT 19
-#define CL_SD_CTL_DIR_MASK (1 << CL_SD_CTL_DIR_SHIFT)
-#define CL_SD_CTL_DIR(x) \
- ((x << CL_SD_CTL_DIR_SHIFT) & CL_SD_CTL_DIR_MASK)
-
-/* Stream Number */
-#define CL_SD_CTL_STRM_SHIFT 20
-#define CL_SD_CTL_STRM_MASK (0xf << CL_SD_CTL_STRM_SHIFT)
-#define CL_SD_CTL_STRM(x) \
- ((x << CL_SD_CTL_STRM_SHIFT) & CL_SD_CTL_STRM_MASK)
-
-/* CL: Stream Descriptor x Status */
-
-/* Buffer Completion Interrupt Status */
-#define CL_SD_STS_BCIS(x) CL_SD_CTL_IOCE(x)
-
-/* FIFO Error */
-#define CL_SD_STS_FIFOE(x) CL_SD_CTL_FEIE(x)
-
-/* Descriptor Error */
-#define CL_SD_STS_DESE(x) CL_SD_CTL_DEIE(x)
-
-/* FIFO Ready */
-#define CL_SD_STS_FIFORDY(x) CL_SD_CTL_FIFOLC(x)
-
-
-/* CL: Stream Descriptor x Last Valid Index */
-#define CL_SD_LVI_SHIFT 0
-#define CL_SD_LVI_MASK (0xff << CL_SD_LVI_SHIFT)
-#define CL_SD_LVI(x) ((x << CL_SD_LVI_SHIFT) & CL_SD_LVI_MASK)
-
-/* CL: Stream Descriptor x FIFO Eviction Watermark */
-#define CL_SD_FIFOW_SHIFT 0
-#define CL_SD_FIFOW_MASK (0x7 << CL_SD_FIFOW_SHIFT)
-#define CL_SD_FIFOW(x) \
- ((x << CL_SD_FIFOW_SHIFT) & CL_SD_FIFOW_MASK)
-
-/* CL: Stream Descriptor x Buffer Descriptor List Pointer Lower Base Address */
-
-/* Protect Bits */
-#define CL_SD_BDLPLBA_PROT_SHIFT 0
-#define CL_SD_BDLPLBA_PROT_MASK (1 << CL_SD_BDLPLBA_PROT_SHIFT)
-#define CL_SD_BDLPLBA_PROT(x) \
- ((x << CL_SD_BDLPLBA_PROT_SHIFT) & CL_SD_BDLPLBA_PROT_MASK)
-
-/* Buffer Descriptor List Lower Base Address */
-#define CL_SD_BDLPLBA_SHIFT 7
-#define CL_SD_BDLPLBA_MASK (0x1ffffff << CL_SD_BDLPLBA_SHIFT)
-#define CL_SD_BDLPLBA(x) \
- ((BDL_ALIGN(lower_32_bits(x)) << CL_SD_BDLPLBA_SHIFT) & CL_SD_BDLPLBA_MASK)
-
-/* Buffer Descriptor List Upper Base Address */
-#define CL_SD_BDLPUBA_SHIFT 0
-#define CL_SD_BDLPUBA_MASK (0xffffffff << CL_SD_BDLPUBA_SHIFT)
-#define CL_SD_BDLPUBA(x) \
- ((upper_32_bits(x) << CL_SD_BDLPUBA_SHIFT) & CL_SD_BDLPUBA_MASK)
-
-/*
- * Code Loader - Software Position Based FIFO
- * Capability Registers x Software Position Based FIFO Header
- */
-
-/* Next Capability Pointer */
-#define CL_SPBFIFO_SPBFCH_PTR_SHIFT 0
-#define CL_SPBFIFO_SPBFCH_PTR_MASK (0xff << CL_SPBFIFO_SPBFCH_PTR_SHIFT)
-#define CL_SPBFIFO_SPBFCH_PTR(x) \
- ((x << CL_SPBFIFO_SPBFCH_PTR_SHIFT) & CL_SPBFIFO_SPBFCH_PTR_MASK)
-
-/* Capability Identifier */
-#define CL_SPBFIFO_SPBFCH_ID_SHIFT 16
-#define CL_SPBFIFO_SPBFCH_ID_MASK (0xfff << CL_SPBFIFO_SPBFCH_ID_SHIFT)
-#define CL_SPBFIFO_SPBFCH_ID(x) \
- ((x << CL_SPBFIFO_SPBFCH_ID_SHIFT) & CL_SPBFIFO_SPBFCH_ID_MASK)
-
-/* Capability Version */
-#define CL_SPBFIFO_SPBFCH_VER_SHIFT 28
-#define CL_SPBFIFO_SPBFCH_VER_MASK (0xf << CL_SPBFIFO_SPBFCH_VER_SHIFT)
-#define CL_SPBFIFO_SPBFCH_VER(x) \
- ((x << CL_SPBFIFO_SPBFCH_VER_SHIFT) & CL_SPBFIFO_SPBFCH_VER_MASK)
-
-/* Software Position in Buffer Enable */
-#define CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0
-#define CL_SPBFIFO_SPBFCCTL_SPIBE_MASK (1 << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT)
-#define CL_SPBFIFO_SPBFCCTL_SPIBE(x) \
- ((x << CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & CL_SPBFIFO_SPBFCCTL_SPIBE_MASK)
-
-/* SST IPC SKL defines */
-#define SKL_WAIT_TIMEOUT 500 /* 500 msec */
-#define SKL_MAX_BUFFER_SIZE (32 * PAGE_SIZE)
-
-enum skl_cl_dma_wake_states {
- SKL_CL_DMA_STATUS_NONE = 0,
- SKL_CL_DMA_BUF_COMPLETE,
- SKL_CL_DMA_ERR, /* TODO: Expand the error states */
-};
-
-struct sst_dsp;
-
-struct skl_cl_dev_ops {
- void (*cl_setup_bdle)(struct sst_dsp *ctx,
- struct snd_dma_buffer *dmab_data,
- u32 **bdlp, int size, int with_ioc);
- void (*cl_setup_controller)(struct sst_dsp *ctx,
- struct snd_dma_buffer *dmab_bdl,
- unsigned int max_size, u32 page_count);
- void (*cl_setup_spb)(struct sst_dsp *ctx,
- unsigned int size, bool enable);
- void (*cl_cleanup_spb)(struct sst_dsp *ctx);
- void (*cl_trigger)(struct sst_dsp *ctx, bool enable);
- void (*cl_cleanup_controller)(struct sst_dsp *ctx);
- int (*cl_copy_to_dmabuf)(struct sst_dsp *ctx,
- const void *bin, u32 size);
- void (*cl_stop_dma)(struct sst_dsp *ctx);
-};
-
-/**
- * skl_cl_dev - holds information for code loader dma transfer
- *
- * @dmab_data: buffer pointer
- * @dmab_bdl: buffer descriptor list
- * @bufsize: ring buffer size
- * @frags: Last valid buffer descriptor index in the BDL
- * @curr_spib_pos: Current position in ring buffer
- * @dma_buffer_offset: dma buffer offset
- * @ops: operations supported on CL dma
- * @wait_queue: wait queue to wake for wake event
- * @wake_status: DMA wake status
- * @wait_condition: condition to wait on wait queue
- * @cl_dma_lock: for synchronized access to cldma
- */
-struct skl_cl_dev {
- struct snd_dma_buffer dmab_data;
- struct snd_dma_buffer dmab_bdl;
-
- unsigned int bufsize;
- unsigned int frags;
-
- unsigned int curr_spib_pos;
- unsigned int dma_buffer_offset;
- struct skl_cl_dev_ops ops;
-
- wait_queue_head_t wait_queue;
- int wake_status;
- bool wait_condition;
-};
-
-#endif /* SKL_SST_CLDMA_H_ */
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
deleted file mode 100644
index 1bfb7f63b572..000000000000
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * skl-sst-dsp.c - SKL SST library generic function
- *
- * Copyright (C) 2014-15, Intel Corporation.
- * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
- * Jeeja KP <jeeja.kp@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 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 <sound/pcm.h>
-
-#include "../common/sst-dsp.h"
-#include "../common/sst-ipc.h"
-#include "../common/sst-dsp-priv.h"
-#include "skl-sst-ipc.h"
-
-/* various timeout values */
-#define SKL_DSP_PU_TO 50
-#define SKL_DSP_PD_TO 50
-#define SKL_DSP_RESET_TO 50
-
-void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
-{
- mutex_lock(&ctx->mutex);
- ctx->sst_state = state;
- mutex_unlock(&ctx->mutex);
-}
-
-static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
-{
- int ret;
-
- /* update bits */
- sst_dsp_shim_update_bits_unlocked(ctx,
- SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK,
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK));
-
- /* poll with timeout to check if operation successful */
- ret = sst_dsp_register_poll(ctx,
- SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK,
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK),
- SKL_DSP_RESET_TO,
- "Set reset");
- if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) !=
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) {
- dev_err(ctx->dev, "Set reset state failed\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
-{
- int ret;
-
- dev_dbg(ctx->dev, "In %s\n", __func__);
-
- /* update bits */
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK, 0);
-
- /* poll with timeout to check if operation successful */
- ret = sst_dsp_register_poll(ctx,
- SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK,
- 0,
- SKL_DSP_RESET_TO,
- "Unset reset");
-
- if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) {
- dev_err(ctx->dev, "Unset reset state failed\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
-{
- int val;
- bool is_enable;
-
- val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
-
- is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) &&
- (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) &&
- !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) &&
- !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)));
-
- dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
- return is_enable;
-}
-
-static int skl_dsp_reset_core(struct sst_dsp *ctx)
-{
- /* stall core */
- sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
-
- /* set reset state */
- return skl_dsp_core_set_reset_state(ctx);
-}
-
-static int skl_dsp_start_core(struct sst_dsp *ctx)
-{
- int ret;
-
- /* unset reset state */
- ret = skl_dsp_core_unset_reset_state(ctx);
- if (ret < 0) {
- dev_dbg(ctx->dev, "dsp unset reset fails\n");
- return ret;
- }
-
- /* run core */
- dev_dbg(ctx->dev, "run core...\n");
- sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
-
- if (!is_skl_dsp_core_enable(ctx)) {
- skl_dsp_reset_core(ctx);
- dev_err(ctx->dev, "DSP core enable failed\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static int skl_dsp_core_power_up(struct sst_dsp *ctx)
-{
- int ret;
-
- /* update bits */
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK));
-
- /* poll with timeout to check if operation successful */
- ret = sst_dsp_register_poll(ctx,
- SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CPA_MASK,
- SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK),
- SKL_DSP_PU_TO,
- "Power up");
-
- if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) !=
- SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) {
- dev_err(ctx->dev, "DSP core power up failed\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static int skl_dsp_core_power_down(struct sst_dsp *ctx)
-{
- /* update bits */
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK, 0);
-
- /* poll with timeout to check if operation successful */
- return sst_dsp_register_poll(ctx,
- SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CPA_MASK,
- 0,
- SKL_DSP_PD_TO,
- "Power down");
-}
-
-static int skl_dsp_enable_core(struct sst_dsp *ctx)
-{
- int ret;
-
- /* power up */
- ret = skl_dsp_core_power_up(ctx);
- if (ret < 0) {
- dev_dbg(ctx->dev, "dsp core power up failed\n");
- return ret;
- }
-
- return skl_dsp_start_core(ctx);
-}
-
-int skl_dsp_disable_core(struct sst_dsp *ctx)
-{
- int ret;
-
- ret = skl_dsp_reset_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "dsp core reset failed\n");
- return ret;
- }
-
- /* power down core*/
- ret = skl_dsp_core_power_down(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "dsp core power down failed\n");
- return ret;
- }
-
- if (is_skl_dsp_core_enable(ctx)) {
- dev_err(ctx->dev, "DSP core disable failed\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-int skl_dsp_boot(struct sst_dsp *ctx)
-{
- int ret;
-
- if (is_skl_dsp_core_enable(ctx)) {
- dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
- ret = skl_dsp_reset_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "dsp reset failed\n");
- return ret;
- }
-
- ret = skl_dsp_start_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "dsp start failed\n");
- return ret;
- }
- } else {
- dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
- ret = skl_dsp_disable_core(ctx);
-
- if (ret < 0) {
- dev_err(ctx->dev, "dsp disable core failes\n");
- return ret;
- }
- ret = skl_dsp_enable_core(ctx);
- }
-
- return ret;
-}
-
-irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
-{
- struct sst_dsp *ctx = dev_id;
- u32 val;
- irqreturn_t result = IRQ_NONE;
-
- spin_lock(&ctx->spinlock);
-
- val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
- ctx->intr_status = val;
-
- if (val == 0xffffffff) {
- spin_unlock(&ctx->spinlock);
- return IRQ_NONE;
- }
-
- if (val & SKL_ADSPIS_IPC) {
- skl_ipc_int_disable(ctx);
- result = IRQ_WAKE_THREAD;
- }
-
- if (val & SKL_ADSPIS_CL_DMA) {
- skl_cldma_int_disable(ctx);
- result = IRQ_WAKE_THREAD;
- }
-
- spin_unlock(&ctx->spinlock);
-
- return result;
-}
-
-int skl_dsp_wake(struct sst_dsp *ctx)
-{
- return ctx->fw_ops.set_state_D0(ctx);
-}
-EXPORT_SYMBOL_GPL(skl_dsp_wake);
-
-int skl_dsp_sleep(struct sst_dsp *ctx)
-{
- return ctx->fw_ops.set_state_D3(ctx);
-}
-EXPORT_SYMBOL_GPL(skl_dsp_sleep);
-
-struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
- struct sst_dsp_device *sst_dev, int irq)
-{
- int ret;
- struct sst_dsp *sst;
-
- sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
- if (sst == NULL)
- return NULL;
-
- spin_lock_init(&sst->spinlock);
- mutex_init(&sst->mutex);
- sst->dev = dev;
- sst->sst_dev = sst_dev;
- sst->irq = irq;
- sst->ops = sst_dev->ops;
- sst->thread_context = sst_dev->thread_context;
-
- /* Initialise SST Audio DSP */
- if (sst->ops->init) {
- ret = sst->ops->init(sst, NULL);
- if (ret < 0)
- return NULL;
- }
-
- /* Register the ISR */
- ret = request_threaded_irq(sst->irq, sst->ops->irq_handler,
- sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
- if (ret) {
- dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n",
- sst->irq);
- return NULL;
- }
-
- return sst;
-}
-
-void skl_dsp_free(struct sst_dsp *dsp)
-{
- skl_ipc_int_disable(dsp);
-
- free_irq(dsp->irq, dsp);
- skl_dsp_disable_core(dsp);
-}
-EXPORT_SYMBOL_GPL(skl_dsp_free);
-
-bool is_skl_dsp_running(struct sst_dsp *ctx)
-{
- return (ctx->sst_state == SKL_DSP_RUNNING);
-}
-EXPORT_SYMBOL_GPL(is_skl_dsp_running);
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
deleted file mode 100644
index cbb40751c37e..000000000000
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Skylake SST DSP Support
- *
- * Copyright (C) 2014-15, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as 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 __SKL_SST_DSP_H__
-#define __SKL_SST_DSP_H__
-
-#include <linux/interrupt.h>
-#include <sound/memalloc.h>
-#include "skl-sst-cldma.h"
-
-struct sst_dsp;
-struct skl_sst;
-struct sst_dsp_device;
-
-/* Intel HD Audio General DSP Registers */
-#define SKL_ADSP_GEN_BASE 0x0
-#define SKL_ADSP_REG_ADSPCS (SKL_ADSP_GEN_BASE + 0x04)
-#define SKL_ADSP_REG_ADSPIC (SKL_ADSP_GEN_BASE + 0x08)
-#define SKL_ADSP_REG_ADSPIS (SKL_ADSP_GEN_BASE + 0x0C)
-#define SKL_ADSP_REG_ADSPIC2 (SKL_ADSP_GEN_BASE + 0x10)
-#define SKL_ADSP_REG_ADSPIS2 (SKL_ADSP_GEN_BASE + 0x14)
-
-/* Intel HD Audio Inter-Processor Communication Registers */
-#define SKL_ADSP_IPC_BASE 0x40
-#define SKL_ADSP_REG_HIPCT (SKL_ADSP_IPC_BASE + 0x00)
-#define SKL_ADSP_REG_HIPCTE (SKL_ADSP_IPC_BASE + 0x04)
-#define SKL_ADSP_REG_HIPCI (SKL_ADSP_IPC_BASE + 0x08)
-#define SKL_ADSP_REG_HIPCIE (SKL_ADSP_IPC_BASE + 0x0C)
-#define SKL_ADSP_REG_HIPCCTL (SKL_ADSP_IPC_BASE + 0x10)
-
-/* HIPCI */
-#define SKL_ADSP_REG_HIPCI_BUSY BIT(31)
-
-/* HIPCIE */
-#define SKL_ADSP_REG_HIPCIE_DONE BIT(30)
-
-/* HIPCCTL */
-#define SKL_ADSP_REG_HIPCCTL_DONE BIT(1)
-#define SKL_ADSP_REG_HIPCCTL_BUSY BIT(0)
-
-/* HIPCT */
-#define SKL_ADSP_REG_HIPCT_BUSY BIT(31)
-
-/* Intel HD Audio SRAM Window 1 */
-#define SKL_ADSP_SRAM1_BASE 0xA000
-
-#define SKL_ADSP_MMIO_LEN 0x10000
-
-#define SKL_ADSP_W0_STAT_SZ 0x1000
-
-#define SKL_ADSP_W0_UP_SZ 0x1000
-
-#define SKL_ADSP_W1_SZ 0x1000
-
-#define SKL_FW_STS_MASK 0xf
-
-#define SKL_FW_INIT 0x1
-#define SKL_FW_RFW_START 0xf
-
-#define SKL_ADSPIC_IPC 1
-#define SKL_ADSPIS_IPC 1
-
-/* ADSPCS - Audio DSP Control & Status */
-#define SKL_DSP_CORES 1
-#define SKL_DSP_CORE0_MASK 1
-#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1)
-
-/* Core Reset - asserted high */
-#define SKL_ADSPCS_CRST_SHIFT 0
-#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT)
-#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK)
-
-/* Core run/stall - when set to '1' core is stalled */
-#define SKL_ADSPCS_CSTALL_SHIFT 8
-#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \
- SKL_ADSPCS_CSTALL_SHIFT)
-#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \
- SKL_ADSPCS_CSTALL_MASK)
-
-/* Set Power Active - when set to '1' turn cores on */
-#define SKL_ADSPCS_SPA_SHIFT 16
-#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT)
-#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK)
-
-/* Current Power Active - power status of cores, set by hardware */
-#define SKL_ADSPCS_CPA_SHIFT 24
-#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT)
-#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK)
-
-#define SST_DSP_POWER_D0 0x0 /* full On */
-#define SST_DSP_POWER_D3 0x3 /* Off */
-
-enum skl_dsp_states {
- SKL_DSP_RUNNING = 1,
- SKL_DSP_RESET,
-};
-
-struct skl_dsp_fw_ops {
- int (*load_fw)(struct sst_dsp *ctx);
- /* FW module parser/loader */
- int (*parse_fw)(struct sst_dsp *ctx);
- int (*set_state_D0)(struct sst_dsp *ctx);
- int (*set_state_D3)(struct sst_dsp *ctx);
- unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
- int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name);
- int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
-
-};
-
-struct skl_dsp_loader_ops {
- int (*alloc_dma_buf)(struct device *dev,
- struct snd_dma_buffer *dmab, size_t size);
- int (*free_dma_buf)(struct device *dev,
- struct snd_dma_buffer *dmab);
-};
-
-struct skl_load_module_info {
- u16 mod_id;
- const struct firmware *fw;
-};
-
-struct skl_module_table {
- struct skl_load_module_info *mod_info;
- unsigned int usage_cnt;
- struct list_head list;
-};
-
-void skl_cldma_process_intr(struct sst_dsp *ctx);
-void skl_cldma_int_disable(struct sst_dsp *ctx);
-int skl_cldma_prepare(struct sst_dsp *ctx);
-
-void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
-struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
- struct sst_dsp_device *sst_dev, int irq);
-int skl_dsp_disable_core(struct sst_dsp *ctx);
-bool is_skl_dsp_running(struct sst_dsp *ctx);
-irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
-int skl_dsp_wake(struct sst_dsp *ctx);
-int skl_dsp_sleep(struct sst_dsp *ctx);
-void skl_dsp_free(struct sst_dsp *dsp);
-
-int skl_dsp_boot(struct sst_dsp *ctx);
-int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
- const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
- struct skl_sst **dsp);
-void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
-
-#endif /*__SKL_SST_DSP_H__*/
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c
deleted file mode 100644
index 543460293b00..000000000000
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ /dev/null
@@ -1,904 +0,0 @@
-/*
- * skl-sst-ipc.c - Intel skl IPC Support
- *
- * Copyright (C) 2014-15, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as 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/device.h>
-
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "skl.h"
-#include "skl-sst-dsp.h"
-#include "skl-sst-ipc.h"
-#include "sound/hdaudio_ext.h"
-
-
-#define IPC_IXC_STATUS_BITS 24
-
-/* Global Message - Generic */
-#define IPC_GLB_TYPE_SHIFT 24
-#define IPC_GLB_TYPE_MASK (0xf << IPC_GLB_TYPE_SHIFT)
-#define IPC_GLB_TYPE(x) ((x) << IPC_GLB_TYPE_SHIFT)
-
-/* Global Message - Reply */
-#define IPC_GLB_REPLY_STATUS_SHIFT 24
-#define IPC_GLB_REPLY_STATUS_MASK ((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1)
-#define IPC_GLB_REPLY_STATUS(x) ((x) << IPC_GLB_REPLY_STATUS_SHIFT)
-
-#define IPC_TIMEOUT_MSECS 3000
-
-#define IPC_EMPTY_LIST_SIZE 8
-
-#define IPC_MSG_TARGET_SHIFT 30
-#define IPC_MSG_TARGET_MASK 0x1
-#define IPC_MSG_TARGET(x) (((x) & IPC_MSG_TARGET_MASK) \
- << IPC_MSG_TARGET_SHIFT)
-
-#define IPC_MSG_DIR_SHIFT 29
-#define IPC_MSG_DIR_MASK 0x1
-#define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \
- << IPC_MSG_DIR_SHIFT)
-/* Global Notification Message */
-#define IPC_GLB_NOTIFY_TYPE_SHIFT 16
-#define IPC_GLB_NOTIFY_TYPE_MASK 0xFF
-#define IPC_GLB_NOTIFY_TYPE(x) (((x) >> IPC_GLB_NOTIFY_TYPE_SHIFT) \
- & IPC_GLB_NOTIFY_TYPE_MASK)
-
-#define IPC_GLB_NOTIFY_MSG_TYPE_SHIFT 24
-#define IPC_GLB_NOTIFY_MSG_TYPE_MASK 0x1F
-#define IPC_GLB_NOTIFY_MSG_TYPE(x) (((x) >> IPC_GLB_NOTIFY_MSG_TYPE_SHIFT) \
- & IPC_GLB_NOTIFY_MSG_TYPE_MASK)
-
-#define IPC_GLB_NOTIFY_RSP_SHIFT 29
-#define IPC_GLB_NOTIFY_RSP_MASK 0x1
-#define IPC_GLB_NOTIFY_RSP_TYPE(x) (((x) >> IPC_GLB_NOTIFY_RSP_SHIFT) \
- & IPC_GLB_NOTIFY_RSP_MASK)
-
-/* Pipeline operations */
-
-/* Create pipeline message */
-#define IPC_PPL_MEM_SIZE_SHIFT 0
-#define IPC_PPL_MEM_SIZE_MASK 0x7FF
-#define IPC_PPL_MEM_SIZE(x) (((x) & IPC_PPL_MEM_SIZE_MASK) \
- << IPC_PPL_MEM_SIZE_SHIFT)
-
-#define IPC_PPL_TYPE_SHIFT 11
-#define IPC_PPL_TYPE_MASK 0x1F
-#define IPC_PPL_TYPE(x) (((x) & IPC_PPL_TYPE_MASK) \
- << IPC_PPL_TYPE_SHIFT)
-
-#define IPC_INSTANCE_ID_SHIFT 16
-#define IPC_INSTANCE_ID_MASK 0xFF
-#define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \
- << IPC_INSTANCE_ID_SHIFT)
-
-/* Set pipeline state message */
-#define IPC_PPL_STATE_SHIFT 0
-#define IPC_PPL_STATE_MASK 0x1F
-#define IPC_PPL_STATE(x) (((x) & IPC_PPL_STATE_MASK) \
- << IPC_PPL_STATE_SHIFT)
-
-/* Module operations primary register */
-#define IPC_MOD_ID_SHIFT 0
-#define IPC_MOD_ID_MASK 0xFFFF
-#define IPC_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \
- << IPC_MOD_ID_SHIFT)
-
-#define IPC_MOD_INSTANCE_ID_SHIFT 16
-#define IPC_MOD_INSTANCE_ID_MASK 0xFF
-#define IPC_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \
- << IPC_MOD_INSTANCE_ID_SHIFT)
-
-/* Init instance message extension register */
-#define IPC_PARAM_BLOCK_SIZE_SHIFT 0
-#define IPC_PARAM_BLOCK_SIZE_MASK 0xFFFF
-#define IPC_PARAM_BLOCK_SIZE(x) (((x) & IPC_PARAM_BLOCK_SIZE_MASK) \
- << IPC_PARAM_BLOCK_SIZE_SHIFT)
-
-#define IPC_PPL_INSTANCE_ID_SHIFT 16
-#define IPC_PPL_INSTANCE_ID_MASK 0xFF
-#define IPC_PPL_INSTANCE_ID(x) (((x) & IPC_PPL_INSTANCE_ID_MASK) \
- << IPC_PPL_INSTANCE_ID_SHIFT)
-
-#define IPC_CORE_ID_SHIFT 24
-#define IPC_CORE_ID_MASK 0x1F
-#define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \
- << IPC_CORE_ID_SHIFT)
-
-/* Bind/Unbind message extension register */
-#define IPC_DST_MOD_ID_SHIFT 0
-#define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \
- << IPC_DST_MOD_ID_SHIFT)
-
-#define IPC_DST_MOD_INSTANCE_ID_SHIFT 16
-#define IPC_DST_MOD_INSTANCE_ID(x) (((x) & IPC_MOD_INSTANCE_ID_MASK) \
- << IPC_DST_MOD_INSTANCE_ID_SHIFT)
-
-#define IPC_DST_QUEUE_SHIFT 24
-#define IPC_DST_QUEUE_MASK 0x7
-#define IPC_DST_QUEUE(x) (((x) & IPC_DST_QUEUE_MASK) \
- << IPC_DST_QUEUE_SHIFT)
-
-#define IPC_SRC_QUEUE_SHIFT 27
-#define IPC_SRC_QUEUE_MASK 0x7
-#define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \
- << IPC_SRC_QUEUE_SHIFT)
-/* Load Module count */
-#define IPC_LOAD_MODULE_SHIFT 0
-#define IPC_LOAD_MODULE_MASK 0xFF
-#define IPC_LOAD_MODULE_CNT(x) (((x) & IPC_LOAD_MODULE_MASK) \
- << IPC_LOAD_MODULE_SHIFT)
-
-/* Save pipeline messgae extension register */
-#define IPC_DMA_ID_SHIFT 0
-#define IPC_DMA_ID_MASK 0x1F
-#define IPC_DMA_ID(x) (((x) & IPC_DMA_ID_MASK) \
- << IPC_DMA_ID_SHIFT)
-/* Large Config message extension register */
-#define IPC_DATA_OFFSET_SZ_SHIFT 0
-#define IPC_DATA_OFFSET_SZ_MASK 0xFFFFF
-#define IPC_DATA_OFFSET_SZ(x) (((x) & IPC_DATA_OFFSET_SZ_MASK) \
- << IPC_DATA_OFFSET_SZ_SHIFT)
-#define IPC_DATA_OFFSET_SZ_CLEAR ~(IPC_DATA_OFFSET_SZ_MASK \
- << IPC_DATA_OFFSET_SZ_SHIFT)
-
-#define IPC_LARGE_PARAM_ID_SHIFT 20
-#define IPC_LARGE_PARAM_ID_MASK 0xFF
-#define IPC_LARGE_PARAM_ID(x) (((x) & IPC_LARGE_PARAM_ID_MASK) \
- << IPC_LARGE_PARAM_ID_SHIFT)
-
-#define IPC_FINAL_BLOCK_SHIFT 28
-#define IPC_FINAL_BLOCK_MASK 0x1
-#define IPC_FINAL_BLOCK(x) (((x) & IPC_FINAL_BLOCK_MASK) \
- << IPC_FINAL_BLOCK_SHIFT)
-
-#define IPC_INITIAL_BLOCK_SHIFT 29
-#define IPC_INITIAL_BLOCK_MASK 0x1
-#define IPC_INITIAL_BLOCK(x) (((x) & IPC_INITIAL_BLOCK_MASK) \
- << IPC_INITIAL_BLOCK_SHIFT)
-#define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \
- << IPC_INITIAL_BLOCK_SHIFT)
-
-enum skl_ipc_msg_target {
- IPC_FW_GEN_MSG = 0,
- IPC_MOD_MSG = 1
-};
-
-enum skl_ipc_msg_direction {
- IPC_MSG_REQUEST = 0,
- IPC_MSG_REPLY = 1
-};
-
-/* Global Message Types */
-enum skl_ipc_glb_type {
- IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */
- IPC_GLB_LOAD_MULTIPLE_MODS = 15,
- IPC_GLB_UNLOAD_MULTIPLE_MODS = 16,
- IPC_GLB_CREATE_PPL = 17,
- IPC_GLB_DELETE_PPL = 18,
- IPC_GLB_SET_PPL_STATE = 19,
- IPC_GLB_GET_PPL_STATE = 20,
- IPC_GLB_GET_PPL_CONTEXT_SIZE = 21,
- IPC_GLB_SAVE_PPL = 22,
- IPC_GLB_RESTORE_PPL = 23,
- IPC_GLB_NOTIFY = 26,
- IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */
-};
-
-enum skl_ipc_glb_reply {
- IPC_GLB_REPLY_SUCCESS = 0,
-
- IPC_GLB_REPLY_UNKNOWN_MSG_TYPE = 1,
- IPC_GLB_REPLY_ERROR_INVALID_PARAM = 2,
-
- IPC_GLB_REPLY_BUSY = 3,
- IPC_GLB_REPLY_PENDING = 4,
- IPC_GLB_REPLY_FAILURE = 5,
- IPC_GLB_REPLY_INVALID_REQUEST = 6,
-
- IPC_GLB_REPLY_OUT_OF_MEMORY = 7,
- IPC_GLB_REPLY_OUT_OF_MIPS = 8,
-
- IPC_GLB_REPLY_INVALID_RESOURCE_ID = 9,
- IPC_GLB_REPLY_INVALID_RESOURCE_STATE = 10,
-
- IPC_GLB_REPLY_MOD_MGMT_ERROR = 100,
- IPC_GLB_REPLY_MOD_LOAD_CL_FAILED = 101,
- IPC_GLB_REPLY_MOD_LOAD_INVALID_HASH = 102,
-
- IPC_GLB_REPLY_MOD_UNLOAD_INST_EXIST = 103,
- IPC_GLB_REPLY_MOD_NOT_INITIALIZED = 104,
-
- IPC_GLB_REPLY_INVALID_CONFIG_PARAM_ID = 120,
- IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121,
- IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140,
- IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141,
-
- IPC_GLB_REPLY_PPL_NOT_INITIALIZED = 160,
- IPC_GLB_REPLY_PPL_NOT_EXIST = 161,
- IPC_GLB_REPLY_PPL_SAVE_FAILED = 162,
- IPC_GLB_REPLY_PPL_RESTORE_FAILED = 163,
-
- IPC_MAX_STATUS = ((1<<IPC_IXC_STATUS_BITS)-1)
-};
-
-enum skl_ipc_notification_type {
- IPC_GLB_NOTIFY_GLITCH = 0,
- IPC_GLB_NOTIFY_OVERRUN = 1,
- IPC_GLB_NOTIFY_UNDERRUN = 2,
- IPC_GLB_NOTIFY_END_STREAM = 3,
- IPC_GLB_NOTIFY_PHRASE_DETECTED = 4,
- IPC_GLB_NOTIFY_RESOURCE_EVENT = 5,
- IPC_GLB_NOTIFY_LOG_BUFFER_STATUS = 6,
- IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7,
- IPC_GLB_NOTIFY_FW_READY = 8
-};
-
-/* Module Message Types */
-enum skl_ipc_module_msg {
- IPC_MOD_INIT_INSTANCE = 0,
- IPC_MOD_CONFIG_GET = 1,
- IPC_MOD_CONFIG_SET = 2,
- IPC_MOD_LARGE_CONFIG_GET = 3,
- IPC_MOD_LARGE_CONFIG_SET = 4,
- IPC_MOD_BIND = 5,
- IPC_MOD_UNBIND = 6,
- IPC_MOD_SET_DX = 7
-};
-
-static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data,
- size_t tx_size)
-{
- if (tx_size)
- memcpy(msg->tx_data, tx_data, tx_size);
-}
-
-static bool skl_ipc_is_dsp_busy(struct sst_dsp *dsp)
-{
- u32 hipci;
-
- hipci = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCI);
- return (hipci & SKL_ADSP_REG_HIPCI_BUSY);
-}
-
-/* Lock to be held by caller */
-static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
-{
- struct skl_ipc_header *header = (struct skl_ipc_header *)(&msg->header);
-
- if (msg->tx_size)
- sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
- sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCIE,
- header->extension);
- sst_dsp_shim_write_unlocked(ipc->dsp, SKL_ADSP_REG_HIPCI,
- header->primary | SKL_ADSP_REG_HIPCI_BUSY);
-}
-
-static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc,
- u64 ipc_header)
-{
- struct ipc_message *msg = NULL;
- struct skl_ipc_header *header = (struct skl_ipc_header *)(&ipc_header);
-
- if (list_empty(&ipc->rx_list)) {
- dev_err(ipc->dev, "ipc: rx list is empty but received 0x%x\n",
- header->primary);
- goto out;
- }
-
- msg = list_first_entry(&ipc->rx_list, struct ipc_message, list);
-
-out:
- return msg;
-
-}
-
-static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
- struct skl_ipc_header header)
-{
- struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc);
-
- if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) {
- switch (IPC_GLB_NOTIFY_TYPE(header.primary)) {
-
- case IPC_GLB_NOTIFY_UNDERRUN:
- dev_err(ipc->dev, "FW Underrun %x\n", header.primary);
- break;
-
- case IPC_GLB_NOTIFY_RESOURCE_EVENT:
- dev_err(ipc->dev, "MCPS Budget Violation: %x\n",
- header.primary);
- break;
-
- case IPC_GLB_NOTIFY_FW_READY:
- skl->boot_complete = true;
- wake_up(&skl->boot_wait);
- break;
-
- case IPC_GLB_NOTIFY_PHRASE_DETECTED:
- dev_dbg(ipc->dev, "***** Phrase Detected **********\n");
-
- /*
- * Per HW recomendation, After phrase detection,
- * clear the CGCTL.MISCBDCGE.
- *
- * This will be set back on stream closure
- */
- skl->enable_miscbdcge(ipc->dev, false);
- skl->miscbdcg_disabled = true;
- break;
-
- default:
- dev_err(ipc->dev, "ipc: Unhandled error msg=%x",
- header.primary);
- break;
- }
- }
-
- return 0;
-}
-
-static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
- struct skl_ipc_header header)
-{
- struct ipc_message *msg;
- u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK;
- u64 *ipc_header = (u64 *)(&header);
-
- msg = skl_ipc_reply_get_msg(ipc, *ipc_header);
- if (msg == NULL) {
- dev_dbg(ipc->dev, "ipc: rx list is empty\n");
- return;
- }
-
- /* first process the header */
- switch (reply) {
- case IPC_GLB_REPLY_SUCCESS:
- dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
- /* copy the rx data from the mailbox */
- sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
- break;
-
- case IPC_GLB_REPLY_OUT_OF_MEMORY:
- dev_err(ipc->dev, "ipc fw reply: %x: no memory\n", header.primary);
- msg->errno = -ENOMEM;
- break;
-
- case IPC_GLB_REPLY_BUSY:
- dev_err(ipc->dev, "ipc fw reply: %x: Busy\n", header.primary);
- msg->errno = -EBUSY;
- break;
-
- default:
- dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply);
- msg->errno = -EINVAL;
- break;
- }
-
- if (reply != IPC_GLB_REPLY_SUCCESS) {
- dev_err(ipc->dev, "ipc FW reply: reply=%d", reply);
- dev_err(ipc->dev, "FW Error Code: %u\n",
- ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp));
- }
-
- list_del(&msg->list);
- sst_ipc_tx_msg_reply_complete(ipc, msg);
-}
-
-irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
-{
- struct sst_dsp *dsp = context;
- struct skl_sst *skl = sst_dsp_get_thread_context(dsp);
- struct sst_generic_ipc *ipc = &skl->ipc;
- struct skl_ipc_header header = {0};
- u32 hipcie, hipct, hipcte;
- int ipc_irq = 0;
-
- if (dsp->intr_status & SKL_ADSPIS_CL_DMA)
- skl_cldma_process_intr(dsp);
-
- /* Here we handle IPC interrupts only */
- if (!(dsp->intr_status & SKL_ADSPIS_IPC))
- return IRQ_NONE;
-
- hipcie = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCIE);
- hipct = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCT);
-
- /* reply message from DSP */
- if (hipcie & SKL_ADSP_REG_HIPCIE_DONE) {
- sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_DONE, 0);
-
- /* clear DONE bit - tell DSP we have completed the operation */
- sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCIE,
- SKL_ADSP_REG_HIPCIE_DONE, SKL_ADSP_REG_HIPCIE_DONE);
-
- ipc_irq = 1;
-
- /* unmask Done interrupt */
- sst_dsp_shim_update_bits(dsp, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE);
- }
-
- /* New message from DSP */
- if (hipct & SKL_ADSP_REG_HIPCT_BUSY) {
- hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE);
- header.primary = hipct;
- header.extension = hipcte;
- dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x",
- header.primary);
- dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x",
- header.extension);
-
- if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
- /* Handle Immediate reply from DSP Core */
- skl_ipc_process_reply(ipc, header);
- } else {
- dev_dbg(dsp->dev, "IPC irq: Notification from firmware\n");
- skl_ipc_process_notification(ipc, header);
- }
- /* clear busy interrupt */
- sst_dsp_shim_update_bits_forced(dsp, SKL_ADSP_REG_HIPCT,
- SKL_ADSP_REG_HIPCT_BUSY, SKL_ADSP_REG_HIPCT_BUSY);
- ipc_irq = 1;
- }
-
- if (ipc_irq == 0)
- return IRQ_NONE;
-
- skl_ipc_int_enable(dsp);
-
- /* continue to send any remaining messages... */
- queue_kthread_work(&ipc->kworker, &ipc->kwork);
-
- return IRQ_HANDLED;
-}
-
-void skl_ipc_int_enable(struct sst_dsp *ctx)
-{
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_ADSPIC,
- SKL_ADSPIC_IPC, SKL_ADSPIC_IPC);
-}
-
-void skl_ipc_int_disable(struct sst_dsp *ctx)
-{
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPIC,
- SKL_ADSPIC_IPC, 0);
-}
-
-void skl_ipc_op_int_enable(struct sst_dsp *ctx)
-{
- /* enable IPC DONE interrupt */
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_DONE, SKL_ADSP_REG_HIPCCTL_DONE);
-
- /* Enable IPC BUSY interrupt */
- sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY);
-}
-
-void skl_ipc_op_int_disable(struct sst_dsp *ctx)
-{
- /* disable IPC DONE interrupt */
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_DONE, 0);
-
- /* Disable IPC BUSY interrupt */
- sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_BUSY, 0);
-
-}
-
-bool skl_ipc_int_status(struct sst_dsp *ctx)
-{
- return sst_dsp_shim_read_unlocked(ctx,
- SKL_ADSP_REG_ADSPIS) & SKL_ADSPIS_IPC;
-}
-
-int skl_ipc_init(struct device *dev, struct skl_sst *skl)
-{
- struct sst_generic_ipc *ipc;
- int err;
-
- ipc = &skl->ipc;
- ipc->dsp = skl->dsp;
- ipc->dev = dev;
-
- ipc->tx_data_max_size = SKL_ADSP_W1_SZ;
- ipc->rx_data_max_size = SKL_ADSP_W0_UP_SZ;
-
- err = sst_ipc_init(ipc);
- if (err)
- return err;
-
- ipc->ops.tx_msg = skl_ipc_tx_msg;
- ipc->ops.tx_data_copy = skl_ipc_tx_data_copy;
- ipc->ops.is_dsp_busy = skl_ipc_is_dsp_busy;
-
- return 0;
-}
-
-void skl_ipc_free(struct sst_generic_ipc *ipc)
-{
- /* Disable IPC DONE interrupt */
- sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_DONE, 0);
-
- /* Disable IPC BUSY interrupt */
- sst_dsp_shim_update_bits(ipc->dsp, SKL_ADSP_REG_HIPCCTL,
- SKL_ADSP_REG_HIPCCTL_BUSY, 0);
-
- sst_ipc_fini(ipc);
-}
-
-int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc,
- u16 ppl_mem_size, u8 ppl_type, u8 instance_id)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_CREATE_PPL);
- header.primary |= IPC_INSTANCE_ID(instance_id);
- header.primary |= IPC_PPL_TYPE(ppl_type);
- header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size);
-
- dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: create pipeline fail, err: %d\n", ret);
- return ret;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_create_pipeline);
-
-int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_DELETE_PPL);
- header.primary |= IPC_INSTANCE_ID(instance_id);
-
- dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: delete pipeline failed, err %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_delete_pipeline);
-
-int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc,
- u8 instance_id, enum skl_ipc_pipeline_state state)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_SET_PPL_STATE);
- header.primary |= IPC_INSTANCE_ID(instance_id);
- header.primary |= IPC_PPL_STATE(state);
-
- dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: set pipeline state failed, err: %d\n", ret);
- return ret;
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_set_pipeline_state);
-
-int
-skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, u8 instance_id, int dma_id)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_SAVE_PPL);
- header.primary |= IPC_INSTANCE_ID(instance_id);
-
- header.extension = IPC_DMA_ID(dma_id);
- dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: save pipeline failed, err: %d\n", ret);
- return ret;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_save_pipeline);
-
-int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_RESTORE_PPL);
- header.primary |= IPC_INSTANCE_ID(instance_id);
-
- dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: restore pipeline failed, err: %d\n", ret);
- return ret;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_restore_pipeline);
-
-int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id,
- u16 module_id, struct skl_ipc_dxstate_info *dx)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_DX);
- header.primary |= IPC_MOD_INSTANCE_ID(instance_id);
- header.primary |= IPC_MOD_ID(module_id);
-
- dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
- header.primary, header.extension);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
- dx, sizeof(*dx), NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret);
- return ret;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_set_dx);
-
-int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
- struct skl_ipc_init_instance_msg *msg, void *param_data)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
- u32 *buffer = (u32 *)param_data;
- /* param_block_size must be in dwords */
- u16 param_block_size = msg->param_data_size / sizeof(u32);
-
- print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
- 16, 4, buffer, param_block_size, false);
-
- header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_MOD_INIT_INSTANCE);
- header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
- header.primary |= IPC_MOD_ID(msg->module_id);
-
- header.extension = IPC_CORE_ID(msg->core_id);
- header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id);
- header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size);
-
- dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
- header.primary, header.extension);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, param_data,
- msg->param_data_size, NULL, 0);
-
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: init instance failed\n");
- return ret;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_init_instance);
-
-int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
- struct skl_ipc_bind_unbind_msg *msg)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- u8 bind_unbind = msg->bind ? IPC_MOD_BIND : IPC_MOD_UNBIND;
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(bind_unbind);
- header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
- header.primary |= IPC_MOD_ID(msg->module_id);
-
- header.extension = IPC_DST_MOD_ID(msg->dst_module_id);
- header.extension |= IPC_DST_MOD_INSTANCE_ID(msg->dst_instance_id);
- header.extension |= IPC_DST_QUEUE(msg->dst_queue);
- header.extension |= IPC_SRC_QUEUE(msg->src_queue);
-
- dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary,
- header.extension);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev, "ipc: bind/unbind faileden");
- return ret;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind);
-
-/*
- * In order to load a module we need to send IPC to initiate that. DMA will
- * performed to load the module memory. The FW supports multiple module load
- * at single shot, so we can send IPC with N modules represented by
- * module_cnt
- */
-int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
- u8 module_cnt, void *data)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS);
- header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
-
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
- (sizeof(u16) * module_cnt), NULL, 0);
- if (ret < 0)
- dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_load_modules);
-
-int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt,
- void *data)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret;
-
- header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_GLB_UNLOAD_MULTIPLE_MODS);
- header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
-
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
- (sizeof(u16) * module_cnt), NULL, 0);
- if (ret < 0)
- dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_unload_modules);
-
-int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
- struct skl_ipc_large_config_msg *msg, u32 *param)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret = 0;
- size_t sz_remaining, tx_size, data_offset;
-
- header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_SET);
- header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
- header.primary |= IPC_MOD_ID(msg->module_id);
-
- header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size);
- header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id);
- header.extension |= IPC_FINAL_BLOCK(0);
- header.extension |= IPC_INITIAL_BLOCK(1);
-
- sz_remaining = msg->param_data_size;
- data_offset = 0;
- while (sz_remaining != 0) {
- tx_size = sz_remaining > SKL_ADSP_W1_SZ
- ? SKL_ADSP_W1_SZ : sz_remaining;
- if (tx_size == sz_remaining)
- header.extension |= IPC_FINAL_BLOCK(1);
-
- dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__,
- header.primary, header.extension);
- dev_dbg(ipc->dev, "transmitting offset: %#x, size: %#x\n",
- (unsigned)data_offset, (unsigned)tx_size);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
- ((char *)param) + data_offset,
- tx_size, NULL, 0);
- if (ret < 0) {
- dev_err(ipc->dev,
- "ipc: set large config fail, err: %d\n", ret);
- return ret;
- }
- sz_remaining -= tx_size;
- data_offset = msg->param_data_size - sz_remaining;
-
- /* clear the fields */
- header.extension &= IPC_INITIAL_BLOCK_CLEAR;
- header.extension &= IPC_DATA_OFFSET_SZ_CLEAR;
- /* fill the fields */
- header.extension |= IPC_INITIAL_BLOCK(0);
- header.extension |= IPC_DATA_OFFSET_SZ(data_offset);
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_set_large_config);
-
-int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
- struct skl_ipc_large_config_msg *msg, u32 *param)
-{
- struct skl_ipc_header header = {0};
- u64 *ipc_header = (u64 *)(&header);
- int ret = 0;
- size_t sz_remaining, rx_size, data_offset;
-
- header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
- header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
- header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_GET);
- header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
- header.primary |= IPC_MOD_ID(msg->module_id);
-
- header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size);
- header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id);
- header.extension |= IPC_FINAL_BLOCK(1);
- header.extension |= IPC_INITIAL_BLOCK(1);
-
- sz_remaining = msg->param_data_size;
- data_offset = 0;
-
- while (sz_remaining != 0) {
- rx_size = sz_remaining > SKL_ADSP_W1_SZ
- ? SKL_ADSP_W1_SZ : sz_remaining;
- if (rx_size == sz_remaining)
- header.extension |= IPC_FINAL_BLOCK(1);
-
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0,
- ((char *)param) + data_offset,
- msg->param_data_size);
- if (ret < 0) {
- dev_err(ipc->dev,
- "ipc: get large config fail, err: %d\n", ret);
- return ret;
- }
- sz_remaining -= rx_size;
- data_offset = msg->param_data_size - sz_remaining;
-
- /* clear the fields */
- header.extension &= IPC_INITIAL_BLOCK_CLEAR;
- header.extension &= IPC_DATA_OFFSET_SZ_CLEAR;
- /* fill the fields */
- header.extension |= IPC_INITIAL_BLOCK(1);
- header.extension |= IPC_DATA_OFFSET_SZ(data_offset);
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_ipc_get_large_config);
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
deleted file mode 100644
index d59d1ba62a43..000000000000
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Intel SKL IPC Support
- *
- * Copyright (C) 2014-15, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as 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 __SKL_IPC_H
-#define __SKL_IPC_H
-
-#include <linux/kthread.h>
-#include <linux/irqreturn.h>
-#include "../common/sst-ipc.h"
-
-struct sst_dsp;
-struct skl_sst;
-struct sst_generic_ipc;
-
-enum skl_ipc_pipeline_state {
- PPL_INVALID_STATE = 0,
- PPL_UNINITIALIZED = 1,
- PPL_RESET = 2,
- PPL_PAUSED = 3,
- PPL_RUNNING = 4,
- PPL_ERROR_STOP = 5,
- PPL_SAVED = 6,
- PPL_RESTORED = 7
-};
-
-struct skl_ipc_dxstate_info {
- u32 core_mask;
- u32 dx_mask;
-};
-
-struct skl_ipc_header {
- u32 primary;
- u32 extension;
-};
-
-struct skl_sst {
- struct device *dev;
- struct sst_dsp *dsp;
-
- /* boot */
- wait_queue_head_t boot_wait;
- bool boot_complete;
-
- /* IPC messaging */
- struct sst_generic_ipc ipc;
-
- /* callback for miscbdge */
- void (*enable_miscbdcge)(struct device *dev, bool enable);
- /*Is CGCTL.MISCBDCGE disabled*/
- bool miscbdcg_disabled;
-};
-
-struct skl_ipc_init_instance_msg {
- u32 module_id;
- u32 instance_id;
- u16 param_data_size;
- u8 ppl_instance_id;
- u8 core_id;
-};
-
-struct skl_ipc_bind_unbind_msg {
- u32 module_id;
- u32 instance_id;
- u32 dst_module_id;
- u32 dst_instance_id;
- u8 src_queue;
- u8 dst_queue;
- bool bind;
-};
-
-struct skl_ipc_large_config_msg {
- u32 module_id;
- u32 instance_id;
- u32 large_param_id;
- u32 param_data_size;
-};
-
-#define SKL_IPC_BOOT_MSECS 3000
-
-#define SKL_IPC_D3_MASK 0
-#define SKL_IPC_D0_MASK 3
-
-irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context);
-
-int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc,
- u16 ppl_mem_size, u8 ppl_type, u8 instance_id);
-
-int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id);
-
-int skl_ipc_set_pipeline_state(struct sst_generic_ipc *sst_ipc,
- u8 instance_id, enum skl_ipc_pipeline_state state);
-
-int skl_ipc_save_pipeline(struct sst_generic_ipc *ipc,
- u8 instance_id, int dma_id);
-
-int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id);
-
-int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc,
- struct skl_ipc_init_instance_msg *msg, void *param_data);
-
-int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc,
- struct skl_ipc_bind_unbind_msg *msg);
-
-int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
- u8 module_cnt, void *data);
-
-int skl_ipc_unload_modules(struct sst_generic_ipc *ipc,
- u8 module_cnt, void *data);
-
-int skl_ipc_set_dx(struct sst_generic_ipc *ipc,
- u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx);
-
-int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
- struct skl_ipc_large_config_msg *msg, u32 *param);
-
-int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
- struct skl_ipc_large_config_msg *msg, u32 *param);
-
-void skl_ipc_int_enable(struct sst_dsp *dsp);
-void skl_ipc_op_int_enable(struct sst_dsp *ctx);
-void skl_ipc_op_int_disable(struct sst_dsp *ctx);
-void skl_ipc_int_disable(struct sst_dsp *dsp);
-
-bool skl_ipc_int_status(struct sst_dsp *dsp);
-void skl_ipc_free(struct sst_generic_ipc *ipc);
-int skl_ipc_init(struct device *dev, struct skl_sst *skl);
-
-#endif /* __SKL_IPC_H */
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
deleted file mode 100644
index e26f4746afb7..000000000000
--- a/sound/soc/intel/skylake/skl-sst.c
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * skl-sst.c - HDA DSP library functions for SKL platform
- *
- * Copyright (C) 2014-15, Intel Corporation.
- * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
- * Jeeja KP <jeeja.kp@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 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/delay.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-#include "../common/sst-ipc.h"
-#include "skl-sst-ipc.h"
-
-#define SKL_BASEFW_TIMEOUT 300
-#define SKL_INIT_TIMEOUT 1000
-
-/* Intel HD Audio SRAM Window 0*/
-#define SKL_ADSP_SRAM0_BASE 0x8000
-
-/* Firmware status window */
-#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE
-#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4)
-
-#define SKL_INSTANCE_ID 0
-#define SKL_BASE_FW_MODULE_ID 0
-
-#define SKL_NUM_MODULES 1
-
-static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
-{
- u32 cur_sts;
-
- cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK;
-
- return (cur_sts == status);
-}
-
-static int skl_transfer_firmware(struct sst_dsp *ctx,
- const void *basefw, u32 base_fw_size)
-{
- int ret = 0;
-
- ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size);
- if (ret < 0)
- return ret;
-
- ret = sst_dsp_register_poll(ctx,
- SKL_ADSP_FW_STATUS,
- SKL_FW_STS_MASK,
- SKL_FW_RFW_START,
- SKL_BASEFW_TIMEOUT,
- "Firmware boot");
-
- ctx->cl_dev.ops.cl_stop_dma(ctx);
-
- return ret;
-}
-
-static int skl_load_base_firmware(struct sst_dsp *ctx)
-{
- int ret = 0, i;
- struct skl_sst *skl = ctx->thread_context;
- u32 reg;
-
- skl->boot_complete = false;
- init_waitqueue_head(&skl->boot_wait);
-
- if (ctx->fw == NULL) {
- ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
- if (ret < 0) {
- dev_err(ctx->dev, "Request firmware failed %d\n", ret);
- skl_dsp_disable_core(ctx);
- return -EIO;
- }
- }
-
- ret = skl_dsp_boot(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
- goto skl_load_base_firmware_failed;
- }
-
- ret = skl_cldma_prepare(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
- goto skl_load_base_firmware_failed;
- }
-
- /* enable Interrupt */
- skl_ipc_int_enable(ctx);
- skl_ipc_op_int_enable(ctx);
-
- /* check ROM Status */
- for (i = SKL_INIT_TIMEOUT; i > 0; --i) {
- if (skl_check_fw_status(ctx, SKL_FW_INIT)) {
- dev_dbg(ctx->dev,
- "ROM loaded, we can continue with FW loading\n");
- break;
- }
- mdelay(1);
- }
- if (!i) {
- reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS);
- dev_err(ctx->dev,
- "Timeout waiting for ROM init done, reg:0x%x\n", reg);
- ret = -EIO;
- goto transfer_firmware_failed;
- }
-
- ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
- if (ret < 0) {
- dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
- goto transfer_firmware_failed;
- } else {
- ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
- msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
- if (ret == 0) {
- dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n");
- ret = -EIO;
- goto transfer_firmware_failed;
- }
-
- dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
- }
- return 0;
-transfer_firmware_failed:
- ctx->cl_dev.ops.cl_cleanup_controller(ctx);
-skl_load_base_firmware_failed:
- skl_dsp_disable_core(ctx);
- release_firmware(ctx->fw);
- ctx->fw = NULL;
- return ret;
-}
-
-static int skl_set_dsp_D0(struct sst_dsp *ctx)
-{
- int ret;
-
- ret = skl_load_base_firmware(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "unable to load firmware\n");
- return ret;
- }
-
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
-
- return ret;
-}
-
-static int skl_set_dsp_D3(struct sst_dsp *ctx)
-{
- int ret;
- struct skl_ipc_dxstate_info dx;
- struct skl_sst *skl = ctx->thread_context;
-
- dev_dbg(ctx->dev, "In %s:\n", __func__);
- mutex_lock(&ctx->mutex);
- if (!is_skl_dsp_running(ctx)) {
- mutex_unlock(&ctx->mutex);
- return 0;
- }
- mutex_unlock(&ctx->mutex);
-
- dx.core_mask = SKL_DSP_CORE0_MASK;
- dx.dx_mask = SKL_IPC_D3_MASK;
- ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
- if (ret < 0)
- dev_err(ctx->dev,
- "D3 request to FW failed, continuing reset: %d", ret);
-
- /* disable Interrupt */
- ctx->cl_dev.ops.cl_cleanup_controller(ctx);
- skl_cldma_int_disable(ctx);
- skl_ipc_op_int_disable(ctx);
- skl_ipc_int_disable(ctx);
-
- ret = skl_dsp_disable_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret);
- ret = -EIO;
- }
- skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
-
- return ret;
-}
-
-static unsigned int skl_get_errorcode(struct sst_dsp *ctx)
-{
- return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE);
-}
-
-/*
- * since get/set_module are called from DAPM context,
- * we don't need lock for usage count
- */
-static int skl_get_module(struct sst_dsp *ctx, u16 mod_id)
-{
- struct skl_module_table *module;
-
- list_for_each_entry(module, &ctx->module_list, list) {
- if (module->mod_info->mod_id == mod_id)
- return ++module->usage_cnt;
- }
-
- return -EINVAL;
-}
-
-static int skl_put_module(struct sst_dsp *ctx, u16 mod_id)
-{
- struct skl_module_table *module;
-
- list_for_each_entry(module, &ctx->module_list, list) {
- if (module->mod_info->mod_id == mod_id)
- return --module->usage_cnt;
- }
-
- return -EINVAL;
-}
-
-static struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx,
- char *mod_name, int mod_id)
-{
- const struct firmware *fw;
- struct skl_module_table *skl_module;
- unsigned int size;
- int ret;
-
- ret = request_firmware(&fw, mod_name, ctx->dev);
- if (ret < 0) {
- dev_err(ctx->dev, "Request Module %s failed :%d\n",
- mod_name, ret);
- return NULL;
- }
-
- skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL);
- if (skl_module == NULL) {
- release_firmware(fw);
- return NULL;
- }
-
- size = sizeof(*skl_module->mod_info);
- skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
- if (skl_module->mod_info == NULL) {
- release_firmware(fw);
- return NULL;
- }
-
- skl_module->mod_info->mod_id = mod_id;
- skl_module->mod_info->fw = fw;
- list_add(&skl_module->list, &ctx->module_list);
-
- return skl_module;
-}
-
-/* get a module from it's unique ID */
-static struct skl_module_table *skl_module_get_from_id(
- struct sst_dsp *ctx, u16 mod_id)
-{
- struct skl_module_table *module;
-
- if (list_empty(&ctx->module_list)) {
- dev_err(ctx->dev, "Module list is empty\n");
- return NULL;
- }
-
- list_for_each_entry(module, &ctx->module_list, list) {
- if (module->mod_info->mod_id == mod_id)
- return module;
- }
-
- return NULL;
-}
-
-static int skl_transfer_module(struct sst_dsp *ctx,
- struct skl_load_module_info *module)
-{
- int ret;
- struct skl_sst *skl = ctx->thread_context;
-
- ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data,
- module->fw->size);
- if (ret < 0)
- return ret;
-
- ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES,
- (void *)&module->mod_id);
- if (ret < 0)
- dev_err(ctx->dev, "Failed to Load module: %d\n", ret);
-
- ctx->cl_dev.ops.cl_stop_dma(ctx);
-
- return ret;
-}
-
-static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid)
-{
- struct skl_module_table *module_entry = NULL;
- int ret = 0;
- char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
-
- snprintf(mod_name, sizeof(mod_name), "%s%s%s",
- "intel/dsp_fw_", guid, ".bin");
-
- module_entry = skl_module_get_from_id(ctx, mod_id);
- if (module_entry == NULL) {
- module_entry = skl_fill_module_table(ctx, mod_name, mod_id);
- if (module_entry == NULL) {
- dev_err(ctx->dev, "Failed to Load module\n");
- return -EINVAL;
- }
- }
-
- if (!module_entry->usage_cnt) {
- ret = skl_transfer_module(ctx, module_entry->mod_info);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to Load module\n");
- return ret;
- }
- }
-
- ret = skl_get_module(ctx, mod_id);
-
- return ret;
-}
-
-static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
-{
- int usage_cnt;
- struct skl_sst *skl = ctx->thread_context;
- int ret = 0;
-
- usage_cnt = skl_put_module(ctx, mod_id);
- if (usage_cnt < 0) {
- dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt);
- return -EIO;
- }
- ret = skl_ipc_unload_modules(&skl->ipc,
- SKL_NUM_MODULES, &mod_id);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to UnLoad module\n");
- skl_get_module(ctx, mod_id);
- return ret;
- }
-
- return ret;
-}
-
-static void skl_clear_module_table(struct sst_dsp *ctx)
-{
- struct skl_module_table *module, *tmp;
-
- if (list_empty(&ctx->module_list))
- return;
-
- list_for_each_entry_safe(module, tmp, &ctx->module_list, list) {
- list_del(&module->list);
- release_firmware(module->mod_info->fw);
- }
-}
-
-static struct skl_dsp_fw_ops skl_fw_ops = {
- .set_state_D0 = skl_set_dsp_D0,
- .set_state_D3 = skl_set_dsp_D3,
- .load_fw = skl_load_base_firmware,
- .get_fw_errcode = skl_get_errorcode,
- .load_mod = skl_load_module,
- .unload_mod = skl_unload_module,
-};
-
-static struct sst_ops skl_ops = {
- .irq_handler = skl_dsp_sst_interrupt,
- .write = sst_shim32_write,
- .read = sst_shim32_read,
- .ram_read = sst_memcpy_fromio_32,
- .ram_write = sst_memcpy_toio_32,
- .free = skl_dsp_free,
-};
-
-static struct sst_dsp_device skl_dev = {
- .thread = skl_dsp_irq_thread_handler,
- .ops = &skl_ops,
-};
-
-int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
- const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp)
-{
- struct skl_sst *skl;
- struct sst_dsp *sst;
- int ret;
-
- skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
- if (skl == NULL)
- return -ENOMEM;
-
- skl->dev = dev;
- skl_dev.thread_context = skl;
-
- skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
- if (!skl->dsp) {
- dev_err(skl->dev, "%s: no device\n", __func__);
- return -ENODEV;
- }
-
- sst = skl->dsp;
-
- sst->fw_name = fw_name;
- sst->addr.lpe = mmio_base;
- sst->addr.shim = mmio_base;
- sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
- SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
-
- INIT_LIST_HEAD(&sst->module_list);
- sst->dsp_ops = dsp_ops;
- sst->fw_ops = skl_fw_ops;
-
- ret = skl_ipc_init(dev, skl);
- if (ret)
- return ret;
-
- ret = sst->fw_ops.load_fw(sst);
- if (ret < 0) {
- dev_err(dev, "Load base fw failed : %d", ret);
- goto cleanup;
- }
-
- if (dsp)
- *dsp = skl;
-
- return ret;
-
-cleanup:
- skl_sst_dsp_cleanup(dev, skl);
- return ret;
-}
-EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
-
-void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
-{
- skl_clear_module_table(ctx->dsp);
- skl_ipc_free(&ctx->ipc);
- ctx->dsp->ops->free(ctx->dsp);
-}
-EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Intel Skylake IPC driver");
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
deleted file mode 100644
index 4624556f486d..000000000000
--- a/sound/soc/intel/skylake/skl-topology.c
+++ /dev/null
@@ -1,1522 +0,0 @@
-/*
- * skl-topology.c - Implements Platform component ALSA controls/widget
- * handlers.
- *
- * Copyright (C) 2014-2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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 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/slab.h>
-#include <linux/types.h>
-#include <linux/firmware.h>
-#include <sound/soc.h>
-#include <sound/soc-topology.h>
-#include "skl-sst-dsp.h"
-#include "skl-sst-ipc.h"
-#include "skl-topology.h"
-#include "skl.h"
-#include "skl-tplg-interface.h"
-#include "../common/sst-dsp.h"
-#include "../common/sst-dsp-priv.h"
-
-#define SKL_CH_FIXUP_MASK (1 << 0)
-#define SKL_RATE_FIXUP_MASK (1 << 1)
-#define SKL_FMT_FIXUP_MASK (1 << 2)
-
-/*
- * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
- * ignore. This helpers checks if the SKL driver handles this widget type
- */
-static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
-{
- switch (w->id) {
- case snd_soc_dapm_dai_link:
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_aif_in:
- case snd_soc_dapm_aif_out:
- case snd_soc_dapm_dai_out:
- case snd_soc_dapm_switch:
- return false;
- default:
- return true;
- }
-}
-
-/*
- * Each pipelines needs memory to be allocated. Check if we have free memory
- * from available pool. Then only add this to pool
- * This is freed when pipe is deleted
- * Note: DSP does actual memory management we only keep track for complete
- * pool
- */
-static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
- struct skl_module_cfg *mconfig)
-{
- struct skl_sst *ctx = skl->skl_sst;
-
- if (skl->resource.mem + mconfig->pipe->memory_pages >
- skl->resource.max_mem) {
- dev_err(ctx->dev,
- "%s: module_id %d instance %d\n", __func__,
- mconfig->id.module_id,
- mconfig->id.instance_id);
- dev_err(ctx->dev,
- "exceeds ppl memory available %d mem %d\n",
- skl->resource.max_mem, skl->resource.mem);
- return false;
- }
-
- skl->resource.mem += mconfig->pipe->memory_pages;
- return true;
-}
-
-/*
- * Pipeline needs needs DSP CPU resources for computation, this is
- * quantified in MCPS (Million Clocks Per Second) required for module/pipe
- *
- * Each pipelines needs mcps to be allocated. Check if we have mcps for this
- * pipe. This adds the mcps to driver counter
- * This is removed on pipeline delete
- */
-static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
- struct skl_module_cfg *mconfig)
-{
- struct skl_sst *ctx = skl->skl_sst;
-
- if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
- dev_err(ctx->dev,
- "%s: module_id %d instance %d\n", __func__,
- mconfig->id.module_id, mconfig->id.instance_id);
- dev_err(ctx->dev,
- "exceeds ppl memory available %d > mem %d\n",
- skl->resource.max_mcps, skl->resource.mcps);
- return false;
- }
-
- skl->resource.mcps += mconfig->mcps;
- return true;
-}
-
-/*
- * Free the mcps when tearing down
- */
-static void
-skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
-{
- skl->resource.mcps -= mconfig->mcps;
-}
-
-/*
- * Free the memory when tearing down
- */
-static void
-skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
-{
- skl->resource.mem -= mconfig->pipe->memory_pages;
-}
-
-
-static void skl_dump_mconfig(struct skl_sst *ctx,
- struct skl_module_cfg *mcfg)
-{
- dev_dbg(ctx->dev, "Dumping config\n");
- dev_dbg(ctx->dev, "Input Format:\n");
- dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt[0].channels);
- dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt[0].s_freq);
- dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt[0].ch_cfg);
- dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt[0].valid_bit_depth);
- dev_dbg(ctx->dev, "Output Format:\n");
- dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt[0].channels);
- dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt[0].s_freq);
- dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt[0].valid_bit_depth);
- dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
-}
-
-static void skl_tplg_update_params(struct skl_module_fmt *fmt,
- struct skl_pipe_params *params, int fixup)
-{
- if (fixup & SKL_RATE_FIXUP_MASK)
- fmt->s_freq = params->s_freq;
- if (fixup & SKL_CH_FIXUP_MASK)
- fmt->channels = params->ch;
- if (fixup & SKL_FMT_FIXUP_MASK) {
- fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
-
- /*
- * 16 bit is 16 bit container whereas 24 bit is in 32 bit
- * container so update bit depth accordingly
- */
- switch (fmt->valid_bit_depth) {
- case SKL_DEPTH_16BIT:
- fmt->bit_depth = fmt->valid_bit_depth;
- break;
-
- default:
- fmt->bit_depth = SKL_DEPTH_32BIT;
- break;
- }
- }
-
-}
-
-/*
- * A pipeline may have modules which impact the pcm parameters, like SRC,
- * channel converter, format converter.
- * We need to calculate the output params by applying the 'fixup'
- * Topology will tell driver which type of fixup is to be applied by
- * supplying the fixup mask, so based on that we calculate the output
- *
- * Now In FE the pcm hw_params is source/target format. Same is applicable
- * for BE with its hw_params invoked.
- * here based on FE, BE pipeline and direction we calculate the input and
- * outfix and then apply that for a module
- */
-static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
- struct skl_pipe_params *params, bool is_fe)
-{
- int in_fixup, out_fixup;
- struct skl_module_fmt *in_fmt, *out_fmt;
-
- /* Fixups will be applied to pin 0 only */
- in_fmt = &m_cfg->in_fmt[0];
- out_fmt = &m_cfg->out_fmt[0];
-
- if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (is_fe) {
- in_fixup = m_cfg->params_fixup;
- out_fixup = (~m_cfg->converter) &
- m_cfg->params_fixup;
- } else {
- out_fixup = m_cfg->params_fixup;
- in_fixup = (~m_cfg->converter) &
- m_cfg->params_fixup;
- }
- } else {
- if (is_fe) {
- out_fixup = m_cfg->params_fixup;
- in_fixup = (~m_cfg->converter) &
- m_cfg->params_fixup;
- } else {
- in_fixup = m_cfg->params_fixup;
- out_fixup = (~m_cfg->converter) &
- m_cfg->params_fixup;
- }
- }
-
- skl_tplg_update_params(in_fmt, params, in_fixup);
- skl_tplg_update_params(out_fmt, params, out_fixup);
-}
-
-/*
- * A module needs input and output buffers, which are dependent upon pcm
- * params, so once we have calculate params, we need buffer calculation as
- * well.
- */
-static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
- struct skl_module_cfg *mcfg)
-{
- int multiplier = 1;
- struct skl_module_fmt *in_fmt, *out_fmt;
-
-
- /* Since fixups is applied to pin 0 only, ibs, obs needs
- * change for pin 0 only
- */
- in_fmt = &mcfg->in_fmt[0];
- out_fmt = &mcfg->out_fmt[0];
-
- if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
- multiplier = 5;
- mcfg->ibs = (in_fmt->s_freq / 1000) *
- (mcfg->in_fmt->channels) *
- (mcfg->in_fmt->bit_depth >> 3) *
- multiplier;
-
- mcfg->obs = (mcfg->out_fmt->s_freq / 1000) *
- (mcfg->out_fmt->channels) *
- (mcfg->out_fmt->bit_depth >> 3) *
- multiplier;
-}
-
-static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
- struct skl_sst *ctx)
-{
- struct skl_module_cfg *m_cfg = w->priv;
- struct skl_pipe_params *params = m_cfg->pipe->p_params;
- int p_conn_type = m_cfg->pipe->conn_type;
- bool is_fe;
-
- if (!m_cfg->params_fixup)
- return;
-
- dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n",
- w->name);
-
- skl_dump_mconfig(ctx, m_cfg);
-
- if (p_conn_type == SKL_PIPE_CONN_TYPE_FE)
- is_fe = true;
- else
- is_fe = false;
-
- skl_tplg_update_params_fixup(m_cfg, params, is_fe);
- skl_tplg_update_buffer_size(ctx, m_cfg);
-
- dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n",
- w->name);
-
- skl_dump_mconfig(ctx, m_cfg);
-}
-
-/*
- * A pipe can have multiple modules, each of them will be a DAPM widget as
- * well. While managing a pipeline we need to get the list of all the
- * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
- * to get the SKL type widgets in that pipeline
- */
-static int skl_tplg_alloc_pipe_widget(struct device *dev,
- struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
-{
- struct skl_module_cfg *src_module = NULL;
- struct snd_soc_dapm_path *p = NULL;
- struct skl_pipe_module *p_module = NULL;
-
- p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
- if (!p_module)
- return -ENOMEM;
-
- p_module->w = w;
- list_add_tail(&p_module->node, &pipe->w_list);
-
- snd_soc_dapm_widget_for_each_sink_path(w, p) {
- if ((p->sink->priv == NULL)
- && (!is_skl_dsp_widget_type(w)))
- continue;
-
- if ((p->sink->priv != NULL) && p->connect
- && is_skl_dsp_widget_type(p->sink)) {
-
- src_module = p->sink->priv;
- if (pipe->ppl_id == src_module->pipe->ppl_id)
- skl_tplg_alloc_pipe_widget(dev,
- p->sink, pipe);
- }
- }
- return 0;
-}
-
-/*
- * some modules can have multiple params set from user control and
- * need to be set after module is initialized. If set_param flag is
- * set module params will be done after module is initialised.
- */
-static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
- struct skl_sst *ctx)
-{
- int i, ret;
- struct skl_module_cfg *mconfig = w->priv;
- const struct snd_kcontrol_new *k;
- struct soc_bytes_ext *sb;
- struct skl_algo_data *bc;
- struct skl_specific_cfg *sp_cfg;
-
- if (mconfig->formats_config.caps_size > 0 &&
- mconfig->formats_config.set_params == SKL_PARAM_SET) {
- sp_cfg = &mconfig->formats_config;
- ret = skl_set_module_params(ctx, sp_cfg->caps,
- sp_cfg->caps_size,
- sp_cfg->param_id, mconfig);
- if (ret < 0)
- return ret;
- }
-
- for (i = 0; i < w->num_kcontrols; i++) {
- k = &w->kcontrol_news[i];
- if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- sb = (void *) k->private_value;
- bc = (struct skl_algo_data *)sb->dobj.private;
-
- if (bc->set_params == SKL_PARAM_SET) {
- ret = skl_set_module_params(ctx,
- (u32 *)bc->params, bc->max,
- bc->param_id, mconfig);
- if (ret < 0)
- return ret;
- }
- }
- }
-
- return 0;
-}
-
-/*
- * some module param can set from user control and this is required as
- * when module is initailzed. if module param is required in init it is
- * identifed by set_param flag. if set_param flag is not set, then this
- * parameter needs to set as part of module init.
- */
-static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
-{
- const struct snd_kcontrol_new *k;
- struct soc_bytes_ext *sb;
- struct skl_algo_data *bc;
- struct skl_module_cfg *mconfig = w->priv;
- int i;
-
- for (i = 0; i < w->num_kcontrols; i++) {
- k = &w->kcontrol_news[i];
- if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- sb = (struct soc_bytes_ext *)k->private_value;
- bc = (struct skl_algo_data *)sb->dobj.private;
-
- if (bc->set_params != SKL_PARAM_INIT)
- continue;
-
- mconfig->formats_config.caps = (u32 *)&bc->params;
- mconfig->formats_config.caps_size = bc->max;
-
- break;
- }
- }
-
- return 0;
-}
-
-/*
- * Inside a pipe instance, we can have various modules. These modules need
- * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
- * skl_init_module() routine, so invoke that for all modules in a pipeline
- */
-static int
-skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
-{
- struct skl_pipe_module *w_module;
- struct snd_soc_dapm_widget *w;
- struct skl_module_cfg *mconfig;
- struct skl_sst *ctx = skl->skl_sst;
- int ret = 0;
-
- list_for_each_entry(w_module, &pipe->w_list, node) {
- w = w_module->w;
- mconfig = w->priv;
-
- /* check resource available */
- if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
- return -ENOMEM;
-
- if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
- ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
- mconfig->id.module_id, mconfig->guid);
- if (ret < 0)
- return ret;
- }
-
- /*
- * apply fix/conversion to module params based on
- * FE/BE params
- */
- skl_tplg_update_module_params(w, ctx);
-
- skl_tplg_set_module_init_data(w);
- ret = skl_init_module(ctx, mconfig);
- if (ret < 0)
- return ret;
-
- ret = skl_tplg_set_module_params(w, ctx);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
- struct skl_pipe *pipe)
-{
- struct skl_pipe_module *w_module = NULL;
- struct skl_module_cfg *mconfig = NULL;
-
- list_for_each_entry(w_module, &pipe->w_list, node) {
- mconfig = w_module->w->priv;
-
- if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod)
- return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
- mconfig->id.module_id);
- }
-
- /* no modules to unload in this path, so return */
- return 0;
-}
-
-/*
- * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
- * need create the pipeline. So we do following:
- * - check the resources
- * - Create the pipeline
- * - Initialize the modules in pipeline
- * - finally bind all modules together
- */
-static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
- struct skl *skl)
-{
- int ret;
- struct skl_module_cfg *mconfig = w->priv;
- struct skl_pipe_module *w_module;
- struct skl_pipe *s_pipe = mconfig->pipe;
- struct skl_module_cfg *src_module = NULL, *dst_module;
- struct skl_sst *ctx = skl->skl_sst;
-
- /* check resource available */
- if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
- return -EBUSY;
-
- if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
- return -ENOMEM;
-
- /*
- * Create a list of modules for pipe.
- * This list contains modules from source to sink
- */
- ret = skl_create_pipeline(ctx, mconfig->pipe);
- if (ret < 0)
- return ret;
-
- /*
- * we create a w_list of all widgets in that pipe. This list is not
- * freed on PMD event as widgets within a pipe are static. This
- * saves us cycles to get widgets in pipe every time.
- *
- * So if we have already initialized all the widgets of a pipeline
- * we skip, so check for list_empty and create the list if empty
- */
- if (list_empty(&s_pipe->w_list)) {
- ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
- if (ret < 0)
- return ret;
- }
-
- /* Init all pipe modules from source to sink */
- ret = skl_tplg_init_pipe_modules(skl, s_pipe);
- if (ret < 0)
- return ret;
-
- /* Bind modules from source to sink */
- list_for_each_entry(w_module, &s_pipe->w_list, node) {
- dst_module = w_module->w->priv;
-
- if (src_module == NULL) {
- src_module = dst_module;
- continue;
- }
-
- ret = skl_bind_modules(ctx, src_module, dst_module);
- if (ret < 0)
- return ret;
-
- src_module = dst_module;
- }
-
- return 0;
-}
-
-static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
- struct skl *skl,
- struct skl_module_cfg *src_mconfig)
-{
- struct snd_soc_dapm_path *p;
- struct snd_soc_dapm_widget *sink = NULL, *next_sink = NULL;
- struct skl_module_cfg *sink_mconfig;
- struct skl_sst *ctx = skl->skl_sst;
- int ret;
-
- snd_soc_dapm_widget_for_each_sink_path(w, p) {
- if (!p->connect)
- continue;
-
- dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
- dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
-
- next_sink = p->sink;
- /*
- * here we will check widgets in sink pipelines, so that
- * can be any widgets type and we are only interested if
- * they are ones used for SKL so check that first
- */
- if ((p->sink->priv != NULL) &&
- is_skl_dsp_widget_type(p->sink)) {
-
- sink = p->sink;
- sink_mconfig = sink->priv;
-
- /* Bind source to sink, mixin is always source */
- ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
- if (ret)
- return ret;
-
- /* Start sinks pipe first */
- if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
- if (sink_mconfig->pipe->conn_type !=
- SKL_PIPE_CONN_TYPE_FE)
- ret = skl_run_pipe(ctx,
- sink_mconfig->pipe);
- if (ret)
- return ret;
- }
- }
- }
-
- if (!sink)
- return skl_tplg_bind_sinks(next_sink, skl, src_mconfig);
-
- return 0;
-}
-
-/*
- * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
- * we need to do following:
- * - Bind to sink pipeline
- * Since the sink pipes can be running and we don't get mixer event on
- * connect for already running mixer, we need to find the sink pipes
- * here and bind to them. This way dynamic connect works.
- * - Start sink pipeline, if not running
- * - Then run current pipe
- */
-static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
- struct skl *skl)
-{
- struct skl_module_cfg *src_mconfig;
- struct skl_sst *ctx = skl->skl_sst;
- int ret = 0;
-
- src_mconfig = w->priv;
-
- /*
- * find which sink it is connected to, bind with the sink,
- * if sink is not started, start sink pipe first, then start
- * this pipe
- */
- ret = skl_tplg_bind_sinks(w, skl, src_mconfig);
- if (ret)
- return ret;
-
- /* Start source pipe last after starting all sinks */
- if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
- return skl_run_pipe(ctx, src_mconfig->pipe);
-
- return 0;
-}
-
-static struct snd_soc_dapm_widget *skl_get_src_dsp_widget(
- struct snd_soc_dapm_widget *w, struct skl *skl)
-{
- struct snd_soc_dapm_path *p;
- struct snd_soc_dapm_widget *src_w = NULL;
- struct skl_sst *ctx = skl->skl_sst;
-
- snd_soc_dapm_widget_for_each_source_path(w, p) {
- src_w = p->source;
- if (!p->connect)
- continue;
-
- dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
- dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
-
- /*
- * here we will check widgets in sink pipelines, so that can
- * be any widgets type and we are only interested if they are
- * ones used for SKL so check that first
- */
- if ((p->source->priv != NULL) &&
- is_skl_dsp_widget_type(p->source)) {
- return p->source;
- }
- }
-
- if (src_w != NULL)
- return skl_get_src_dsp_widget(src_w, skl);
-
- return NULL;
-}
-
-/*
- * in the Post-PMU event of mixer we need to do following:
- * - Check if this pipe is running
- * - if not, then
- * - bind this pipeline to its source pipeline
- * if source pipe is already running, this means it is a dynamic
- * connection and we need to bind only to that pipe
- * - start this pipeline
- */
-static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
- struct skl *skl)
-{
- int ret = 0;
- struct snd_soc_dapm_widget *source, *sink;
- struct skl_module_cfg *src_mconfig, *sink_mconfig;
- struct skl_sst *ctx = skl->skl_sst;
- int src_pipe_started = 0;
-
- sink = w;
- sink_mconfig = sink->priv;
-
- /*
- * If source pipe is already started, that means source is driving
- * one more sink before this sink got connected, Since source is
- * started, bind this sink to source and start this pipe.
- */
- source = skl_get_src_dsp_widget(w, skl);
- if (source != NULL) {
- src_mconfig = source->priv;
- sink_mconfig = sink->priv;
- src_pipe_started = 1;
-
- /*
- * check pipe state, then no need to bind or start the
- * pipe
- */
- if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
- src_pipe_started = 0;
- }
-
- if (src_pipe_started) {
- ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
- if (ret)
- return ret;
-
- if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
- ret = skl_run_pipe(ctx, sink_mconfig->pipe);
- }
-
- return ret;
-}
-
-/*
- * in the Pre-PMD event of mixer we need to do following:
- * - Stop the pipe
- * - find the source connections and remove that from dapm_path_list
- * - unbind with source pipelines if still connected
- */
-static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
- struct skl *skl)
-{
- struct skl_module_cfg *src_mconfig, *sink_mconfig;
- int ret = 0, i;
- struct skl_sst *ctx = skl->skl_sst;
-
- sink_mconfig = w->priv;
-
- /* Stop the pipe */
- ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
- if (ret)
- return ret;
-
- for (i = 0; i < sink_mconfig->max_in_queue; i++) {
- if (sink_mconfig->m_in_pin[i].pin_state == SKL_PIN_BIND_DONE) {
- src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg;
- if (!src_mconfig)
- continue;
- /*
- * If path_found == 1, that means pmd for source
- * pipe has not occurred, source is connected to
- * some other sink. so its responsibility of sink
- * to unbind itself from source.
- */
- ret = skl_stop_pipe(ctx, src_mconfig->pipe);
- if (ret < 0)
- return ret;
-
- ret = skl_unbind_modules(ctx,
- src_mconfig, sink_mconfig);
- }
- }
-
- return ret;
-}
-
-/*
- * in the Post-PMD event of mixer we need to do following:
- * - Free the mcps used
- * - Free the mem used
- * - Unbind the modules within the pipeline
- * - Delete the pipeline (modules are not required to be explicitly
- * deleted, pipeline delete is enough here
- */
-static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
- struct skl *skl)
-{
- struct skl_module_cfg *mconfig = w->priv;
- struct skl_pipe_module *w_module;
- struct skl_module_cfg *src_module = NULL, *dst_module;
- struct skl_sst *ctx = skl->skl_sst;
- struct skl_pipe *s_pipe = mconfig->pipe;
- int ret = 0;
-
- skl_tplg_free_pipe_mcps(skl, mconfig);
- skl_tplg_free_pipe_mem(skl, mconfig);
-
- list_for_each_entry(w_module, &s_pipe->w_list, node) {
- dst_module = w_module->w->priv;
-
- skl_tplg_free_pipe_mcps(skl, dst_module);
- if (src_module == NULL) {
- src_module = dst_module;
- continue;
- }
-
- ret = skl_unbind_modules(ctx, src_module, dst_module);
- if (ret < 0)
- return ret;
-
- src_module = dst_module;
- }
-
- ret = skl_delete_pipe(ctx, mconfig->pipe);
-
- return skl_tplg_unload_pipe_modules(ctx, s_pipe);
-}
-
-/*
- * in the Post-PMD event of PGA we need to do following:
- * - Free the mcps used
- * - Stop the pipeline
- * - In source pipe is connected, unbind with source pipelines
- */
-static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
- struct skl *skl)
-{
- struct skl_module_cfg *src_mconfig, *sink_mconfig;
- int ret = 0, i;
- struct skl_sst *ctx = skl->skl_sst;
-
- src_mconfig = w->priv;
-
- /* Stop the pipe since this is a mixin module */
- ret = skl_stop_pipe(ctx, src_mconfig->pipe);
- if (ret)
- return ret;
-
- for (i = 0; i < src_mconfig->max_out_queue; i++) {
- if (src_mconfig->m_out_pin[i].pin_state == SKL_PIN_BIND_DONE) {
- sink_mconfig = src_mconfig->m_out_pin[i].tgt_mcfg;
- if (!sink_mconfig)
- continue;
- /*
- * This is a connecter and if path is found that means
- * unbind between source and sink has not happened yet
- */
- ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
- if (ret < 0)
- return ret;
- ret = skl_unbind_modules(ctx, src_mconfig,
- sink_mconfig);
- }
- }
-
- return ret;
-}
-
-/*
- * In modelling, we assume there will be ONLY one mixer in a pipeline. If
- * mixer is not required then it is treated as static mixer aka vmixer with
- * a hard path to source module
- * So we don't need to check if source is started or not as hard path puts
- * dependency on each other
- */
-static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct skl *skl = get_skl_ctx(dapm->dev);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
-
- case SND_SOC_DAPM_POST_PMD:
- return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
- }
-
- return 0;
-}
-
-/*
- * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
- * second one is required that is created as another pipe entity.
- * The mixer is responsible for pipe management and represent a pipeline
- * instance
- */
-static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct skl *skl = get_skl_ctx(dapm->dev);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
-
- case SND_SOC_DAPM_POST_PMU:
- return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
-
- case SND_SOC_DAPM_PRE_PMD:
- return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
-
- case SND_SOC_DAPM_POST_PMD:
- return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
- }
-
- return 0;
-}
-
-/*
- * In modelling, we assumed rest of the modules in pipeline are PGA. But we
- * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
- * the sink when it is running (two FE to one BE or one FE to two BE)
- * scenarios
- */
-static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct skl *skl = get_skl_ctx(dapm->dev);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
-
- case SND_SOC_DAPM_POST_PMD:
- return skl_tplg_pga_dapm_post_pmd_event(w, skl);
- }
-
- return 0;
-}
-
-static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
- unsigned int __user *data, unsigned int size)
-{
- struct soc_bytes_ext *sb =
- (struct soc_bytes_ext *)kcontrol->private_value;
- struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private;
- struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
- struct skl_module_cfg *mconfig = w->priv;
- struct skl *skl = get_skl_ctx(w->dapm->dev);
-
- if (w->power)
- skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
- bc->max, bc->param_id, mconfig);
-
- if (bc->params) {
- if (copy_to_user(data, &bc->param_id, sizeof(u32)))
- return -EFAULT;
- if (copy_to_user(data + 1, &size, sizeof(u32)))
- return -EFAULT;
- if (copy_to_user(data + 2, bc->params, size))
- return -EFAULT;
- }
-
- return 0;
-}
-
-#define SKL_PARAM_VENDOR_ID 0xff
-
-static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
- const unsigned int __user *data, unsigned int size)
-{
- struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
- struct skl_module_cfg *mconfig = w->priv;
- struct soc_bytes_ext *sb =
- (struct soc_bytes_ext *)kcontrol->private_value;
- struct skl_algo_data *ac = (struct skl_algo_data *)sb->dobj.private;
- struct skl *skl = get_skl_ctx(w->dapm->dev);
-
- if (ac->params) {
- /*
- * if the param_is is of type Vendor, firmware expects actual
- * parameter id and size from the control.
- */
- if (ac->param_id == SKL_PARAM_VENDOR_ID) {
- if (copy_from_user(ac->params, data, size))
- return -EFAULT;
- } else {
- if (copy_from_user(ac->params,
- data + 2 * sizeof(u32), size))
- return -EFAULT;
- }
-
- if (w->power)
- return skl_set_module_params(skl->skl_sst,
- (u32 *)ac->params, ac->max,
- ac->param_id, mconfig);
- }
-
- return 0;
-}
-
-/*
- * The FE params are passed by hw_params of the DAI.
- * On hw_params, the params are stored in Gateway module of the FE and we
- * need to calculate the format in DSP module configuration, that
- * conversion is done here
- */
-int skl_tplg_update_pipe_params(struct device *dev,
- struct skl_module_cfg *mconfig,
- struct skl_pipe_params *params)
-{
- struct skl_pipe *pipe = mconfig->pipe;
- struct skl_module_fmt *format = NULL;
-
- memcpy(pipe->p_params, params, sizeof(*params));
-
- if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
- format = &mconfig->in_fmt[0];
- else
- format = &mconfig->out_fmt[0];
-
- /* set the hw_params */
- format->s_freq = params->s_freq;
- format->channels = params->ch;
- format->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
-
- /*
- * 16 bit is 16 bit container whereas 24 bit is in 32 bit
- * container so update bit depth accordingly
- */
- switch (format->valid_bit_depth) {
- case SKL_DEPTH_16BIT:
- format->bit_depth = format->valid_bit_depth;
- break;
-
- case SKL_DEPTH_24BIT:
- case SKL_DEPTH_32BIT:
- format->bit_depth = SKL_DEPTH_32BIT;
- break;
-
- default:
- dev_err(dev, "Invalid bit depth %x for pipe\n",
- format->valid_bit_depth);
- return -EINVAL;
- }
-
- if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- mconfig->ibs = (format->s_freq / 1000) *
- (format->channels) *
- (format->bit_depth >> 3);
- } else {
- mconfig->obs = (format->s_freq / 1000) *
- (format->channels) *
- (format->bit_depth >> 3);
- }
-
- return 0;
-}
-
-/*
- * Query the module config for the FE DAI
- * This is used to find the hw_params set for that DAI and apply to FE
- * pipeline
- */
-struct skl_module_cfg *
-skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
-{
- struct snd_soc_dapm_widget *w;
- struct snd_soc_dapm_path *p = NULL;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w = dai->playback_widget;
- snd_soc_dapm_widget_for_each_sink_path(w, p) {
- if (p->connect && p->sink->power &&
- !is_skl_dsp_widget_type(p->sink))
- continue;
-
- if (p->sink->priv) {
- dev_dbg(dai->dev, "set params for %s\n",
- p->sink->name);
- return p->sink->priv;
- }
- }
- } else {
- w = dai->capture_widget;
- snd_soc_dapm_widget_for_each_source_path(w, p) {
- if (p->connect && p->source->power &&
- !is_skl_dsp_widget_type(p->source))
- continue;
-
- if (p->source->priv) {
- dev_dbg(dai->dev, "set params for %s\n",
- p->source->name);
- return p->source->priv;
- }
- }
- }
-
- return NULL;
-}
-
-static u8 skl_tplg_be_link_type(int dev_type)
-{
- int ret;
-
- switch (dev_type) {
- case SKL_DEVICE_BT:
- ret = NHLT_LINK_SSP;
- break;
-
- case SKL_DEVICE_DMIC:
- ret = NHLT_LINK_DMIC;
- break;
-
- case SKL_DEVICE_I2S:
- ret = NHLT_LINK_SSP;
- break;
-
- case SKL_DEVICE_HDALINK:
- ret = NHLT_LINK_HDA;
- break;
-
- default:
- ret = NHLT_LINK_INVALID;
- break;
- }
-
- return ret;
-}
-
-/*
- * Fill the BE gateway parameters
- * The BE gateway expects a blob of parameters which are kept in the ACPI
- * NHLT blob, so query the blob for interface type (i2s/pdm) and instance.
- * The port can have multiple settings so pick based on the PCM
- * parameters
- */
-static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
- struct skl_module_cfg *mconfig,
- struct skl_pipe_params *params)
-{
- struct skl_pipe *pipe = mconfig->pipe;
- struct nhlt_specific_cfg *cfg;
- struct skl *skl = get_skl_ctx(dai->dev);
- int link_type = skl_tplg_be_link_type(mconfig->dev_type);
-
- memcpy(pipe->p_params, params, sizeof(*params));
-
- if (link_type == NHLT_LINK_HDA)
- return 0;
-
- /* update the blob based on virtual bus_id*/
- cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
- params->s_fmt, params->ch,
- params->s_freq, params->stream);
- if (cfg) {
- mconfig->formats_config.caps_size = cfg->size;
- mconfig->formats_config.caps = (u32 *) &cfg->caps;
- } else {
- dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n",
- mconfig->vbus_id, link_type,
- params->stream);
- dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n",
- params->ch, params->s_freq, params->s_fmt);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
- struct snd_soc_dapm_widget *w,
- struct skl_pipe_params *params)
-{
- struct snd_soc_dapm_path *p;
- int ret = -EIO;
-
- snd_soc_dapm_widget_for_each_source_path(w, p) {
- if (p->connect && is_skl_dsp_widget_type(p->source) &&
- p->source->priv) {
-
- ret = skl_tplg_be_fill_pipe_params(dai,
- p->source->priv, params);
- if (ret < 0)
- return ret;
- } else {
- ret = skl_tplg_be_set_src_pipe_params(dai,
- p->source, params);
- if (ret < 0)
- return ret;
- }
- }
-
- return ret;
-}
-
-static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
- struct snd_soc_dapm_widget *w, struct skl_pipe_params *params)
-{
- struct snd_soc_dapm_path *p = NULL;
- int ret = -EIO;
-
- snd_soc_dapm_widget_for_each_sink_path(w, p) {
- if (p->connect && is_skl_dsp_widget_type(p->sink) &&
- p->sink->priv) {
-
- ret = skl_tplg_be_fill_pipe_params(dai,
- p->sink->priv, params);
- if (ret < 0)
- return ret;
- } else {
- ret = skl_tplg_be_set_sink_pipe_params(
- dai, p->sink, params);
- if (ret < 0)
- return ret;
- }
- }
-
- return ret;
-}
-
-/*
- * BE hw_params can be a source parameters (capture) or sink parameters
- * (playback). Based on sink and source we need to either find the source
- * list or the sink list and set the pipeline parameters
- */
-int skl_tplg_be_update_params(struct snd_soc_dai *dai,
- struct skl_pipe_params *params)
-{
- struct snd_soc_dapm_widget *w;
-
- if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w = dai->playback_widget;
-
- return skl_tplg_be_set_src_pipe_params(dai, w, params);
-
- } else {
- w = dai->capture_widget;
-
- return skl_tplg_be_set_sink_pipe_params(dai, w, params);
- }
-
- return 0;
-}
-
-static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
- {SKL_MIXER_EVENT, skl_tplg_mixer_event},
- {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
- {SKL_PGA_EVENT, skl_tplg_pga_event},
-};
-
-static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
- {SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get,
- skl_tplg_tlv_control_set},
-};
-
-/*
- * The topology binary passes the pin info for a module so initialize the pin
- * info passed into module instance
- */
-static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
- struct skl_module_pin *m_pin,
- bool is_dynamic, int max_pin)
-{
- int i;
-
- for (i = 0; i < max_pin; i++) {
- m_pin[i].id.module_id = dfw_pin[i].module_id;
- m_pin[i].id.instance_id = dfw_pin[i].instance_id;
- m_pin[i].in_use = false;
- m_pin[i].is_dynamic = is_dynamic;
- m_pin[i].pin_state = SKL_PIN_UNBIND;
- }
-}
-
-/*
- * Add pipeline from topology binary into driver pipeline list
- *
- * If already added we return that instance
- * Otherwise we create a new instance and add into driver list
- */
-static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
- struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
-{
- struct skl_pipeline *ppl;
- struct skl_pipe *pipe;
- struct skl_pipe_params *params;
-
- list_for_each_entry(ppl, &skl->ppl_list, node) {
- if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
- return ppl->pipe;
- }
-
- ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
- if (!ppl)
- return NULL;
-
- pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
- if (!pipe)
- return NULL;
-
- params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
- if (!params)
- return NULL;
-
- pipe->ppl_id = dfw_pipe->pipe_id;
- pipe->memory_pages = dfw_pipe->memory_pages;
- pipe->pipe_priority = dfw_pipe->pipe_priority;
- pipe->conn_type = dfw_pipe->conn_type;
- pipe->state = SKL_PIPE_INVALID;
- pipe->p_params = params;
- INIT_LIST_HEAD(&pipe->w_list);
-
- ppl->pipe = pipe;
- list_add(&ppl->node, &skl->ppl_list);
-
- return ppl->pipe;
-}
-
-static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
- struct skl_dfw_module_fmt *src_fmt,
- int pins)
-{
- int i;
-
- for (i = 0; i < pins; i++) {
- dst_fmt[i].channels = src_fmt[i].channels;
- dst_fmt[i].s_freq = src_fmt[i].freq;
- dst_fmt[i].bit_depth = src_fmt[i].bit_depth;
- dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth;
- dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg;
- dst_fmt[i].ch_map = src_fmt[i].ch_map;
- dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style;
- dst_fmt[i].sample_type = src_fmt[i].sample_type;
- }
-}
-
-/*
- * Topology core widget load callback
- *
- * This is used to save the private data for each widget which gives
- * information to the driver about module and pipeline parameters which DSP
- * FW expects like ids, resource values, formats etc
- */
-static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
- struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
-{
- int ret;
- struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl_module_cfg *mconfig;
- struct skl_pipe *pipe;
- struct skl_dfw_module *dfw_config =
- (struct skl_dfw_module *)tplg_w->priv.data;
-
- if (!tplg_w->priv.size)
- goto bind_event;
-
- mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
-
- if (!mconfig)
- return -ENOMEM;
-
- w->priv = mconfig;
- mconfig->id.module_id = dfw_config->module_id;
- mconfig->id.instance_id = dfw_config->instance_id;
- mconfig->mcps = dfw_config->max_mcps;
- mconfig->ibs = dfw_config->ibs;
- mconfig->obs = dfw_config->obs;
- mconfig->core_id = dfw_config->core_id;
- mconfig->max_in_queue = dfw_config->max_in_queue;
- mconfig->max_out_queue = dfw_config->max_out_queue;
- mconfig->is_loadable = dfw_config->is_loadable;
- skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt,
- MODULE_MAX_IN_PINS);
- skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt,
- MODULE_MAX_OUT_PINS);
-
- mconfig->params_fixup = dfw_config->params_fixup;
- mconfig->converter = dfw_config->converter;
- mconfig->m_type = dfw_config->module_type;
- mconfig->vbus_id = dfw_config->vbus_id;
- mconfig->mem_pages = dfw_config->mem_pages;
-
- pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
- if (pipe)
- mconfig->pipe = pipe;
-
- mconfig->dev_type = dfw_config->dev_type;
- mconfig->hw_conn_type = dfw_config->hw_conn_type;
- mconfig->time_slot = dfw_config->time_slot;
- mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
-
- if (dfw_config->is_loadable)
- memcpy(mconfig->guid, dfw_config->uuid,
- ARRAY_SIZE(dfw_config->uuid));
-
- mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
- sizeof(*mconfig->m_in_pin),
- GFP_KERNEL);
- if (!mconfig->m_in_pin)
- return -ENOMEM;
-
- mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
- sizeof(*mconfig->m_out_pin),
- GFP_KERNEL);
- if (!mconfig->m_out_pin)
- return -ENOMEM;
-
- skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
- dfw_config->is_dynamic_in_pin,
- mconfig->max_in_queue);
-
- skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
- dfw_config->is_dynamic_out_pin,
- mconfig->max_out_queue);
-
-
- if (mconfig->formats_config.caps_size == 0)
- goto bind_event;
-
- mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
- mconfig->formats_config.caps_size, GFP_KERNEL);
-
- if (mconfig->formats_config.caps == NULL)
- return -ENOMEM;
-
- memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
- dfw_config->caps.caps_size);
- mconfig->formats_config.param_id = dfw_config->caps.param_id;
- mconfig->formats_config.set_params = dfw_config->caps.set_params;
-
-bind_event:
- if (tplg_w->event_type == 0) {
- dev_dbg(bus->dev, "ASoC: No event handler required\n");
- return 0;
- }
-
- ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
- ARRAY_SIZE(skl_tplg_widget_ops),
- tplg_w->event_type);
-
- if (ret) {
- dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
- __func__, tplg_w->event_type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
- struct snd_soc_tplg_bytes_control *bc)
-{
- struct skl_algo_data *ac;
- struct skl_dfw_algo_data *dfw_ac =
- (struct skl_dfw_algo_data *)bc->priv.data;
-
- ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL);
- if (!ac)
- return -ENOMEM;
-
- /* Fill private data */
- ac->max = dfw_ac->max;
- ac->param_id = dfw_ac->param_id;
- ac->set_params = dfw_ac->set_params;
-
- if (ac->max) {
- ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
- if (!ac->params)
- return -ENOMEM;
-
- if (dfw_ac->params)
- memcpy(ac->params, dfw_ac->params, ac->max);
- }
-
- be->dobj.private = ac;
- return 0;
-}
-
-static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
- struct snd_kcontrol_new *kctl,
- struct snd_soc_tplg_ctl_hdr *hdr)
-{
- struct soc_bytes_ext *sb;
- struct snd_soc_tplg_bytes_control *tplg_bc;
- struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- switch (hdr->ops.info) {
- case SND_SOC_TPLG_CTL_BYTES:
- tplg_bc = container_of(hdr,
- struct snd_soc_tplg_bytes_control, hdr);
- if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- sb = (struct soc_bytes_ext *)kctl->private_value;
- if (tplg_bc->priv.size)
- return skl_init_algo_data(
- bus->dev, sb, tplg_bc);
- }
- break;
-
- default:
- dev_warn(bus->dev, "Control load not supported %d:%d:%d\n",
- hdr->ops.get, hdr->ops.put, hdr->ops.info);
- break;
- }
-
- return 0;
-}
-
-static struct snd_soc_tplg_ops skl_tplg_ops = {
- .widget_load = skl_tplg_widget_load,
- .control_load = skl_tplg_control_load,
- .bytes_ext_ops = skl_tlv_ops,
- .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
-};
-
-/* This will be read from topology manifest, currently defined here */
-#define SKL_MAX_MCPS 30000000
-#define SKL_FW_MAX_MEM 1000000
-
-/*
- * SKL topology init routine
- */
-int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
-{
- int ret;
- const struct firmware *fw;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl *skl = ebus_to_skl(ebus);
-
- ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
- if (ret < 0) {
- dev_err(bus->dev, "tplg fw %s load failed with %d\n",
- "dfw_sst.bin", ret);
- return ret;
- }
-
- /*
- * The complete tplg for SKL is loaded as index 0, we don't use
- * any other index
- */
- ret = snd_soc_tplg_component_load(&platform->component,
- &skl_tplg_ops, fw, 0);
- if (ret < 0) {
- dev_err(bus->dev, "tplg component load failed%d\n", ret);
- return -EINVAL;
- }
-
- skl->resource.max_mcps = SKL_MAX_MCPS;
- skl->resource.max_mem = SKL_FW_MAX_MEM;
-
- skl->tplg = fw;
-
- return 0;
-}
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
deleted file mode 100644
index 9aa2a2b6598a..000000000000
--- a/sound/soc/intel/skylake/skl-topology.h
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * skl_topology.h - Intel HDA Platform topology header file
- *
- * Copyright (C) 2014-15 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#ifndef __SKL_TOPOLOGY_H__
-#define __SKL_TOPOLOGY_H__
-
-#include <linux/types.h>
-
-#include <sound/hdaudio_ext.h>
-#include <sound/soc.h>
-#include "skl.h"
-#include "skl-tplg-interface.h"
-
-#define BITS_PER_BYTE 8
-#define MAX_TS_GROUPS 8
-#define MAX_DMIC_TS_GROUPS 4
-#define MAX_FIXED_DMIC_PARAMS_SIZE 727
-
-/* Maximum number of coefficients up down mixer module */
-#define UP_DOWN_MIXER_MAX_COEFF 6
-
-#define MODULE_MAX_IN_PINS 8
-#define MODULE_MAX_OUT_PINS 8
-
-enum skl_channel_index {
- SKL_CHANNEL_LEFT = 0,
- SKL_CHANNEL_RIGHT = 1,
- SKL_CHANNEL_CENTER = 2,
- SKL_CHANNEL_LEFT_SURROUND = 3,
- SKL_CHANNEL_CENTER_SURROUND = 3,
- SKL_CHANNEL_RIGHT_SURROUND = 4,
- SKL_CHANNEL_LFE = 7,
- SKL_CHANNEL_INVALID = 0xF,
-};
-
-enum skl_bitdepth {
- SKL_DEPTH_8BIT = 8,
- SKL_DEPTH_16BIT = 16,
- SKL_DEPTH_24BIT = 24,
- SKL_DEPTH_32BIT = 32,
- SKL_DEPTH_INVALID
-};
-
-
-enum skl_s_freq {
- SKL_FS_8000 = 8000,
- SKL_FS_11025 = 11025,
- SKL_FS_12000 = 12000,
- SKL_FS_16000 = 16000,
- SKL_FS_22050 = 22050,
- SKL_FS_24000 = 24000,
- SKL_FS_32000 = 32000,
- SKL_FS_44100 = 44100,
- SKL_FS_48000 = 48000,
- SKL_FS_64000 = 64000,
- SKL_FS_88200 = 88200,
- SKL_FS_96000 = 96000,
- SKL_FS_128000 = 128000,
- SKL_FS_176400 = 176400,
- SKL_FS_192000 = 192000,
- SKL_FS_INVALID
-};
-
-enum skl_widget_type {
- SKL_WIDGET_VMIXER = 1,
- SKL_WIDGET_MIXER = 2,
- SKL_WIDGET_PGA = 3,
- SKL_WIDGET_MUX = 4
-};
-
-struct skl_audio_data_format {
- enum skl_s_freq s_freq;
- enum skl_bitdepth bit_depth;
- u32 channel_map;
- enum skl_ch_cfg ch_cfg;
- enum skl_interleaving interleaving;
- u8 number_of_channels;
- u8 valid_bit_depth;
- u8 sample_type;
- u8 reserved[1];
-} __packed;
-
-struct skl_base_cfg {
- u32 cps;
- u32 ibs;
- u32 obs;
- u32 is_pages;
- struct skl_audio_data_format audio_fmt;
-};
-
-struct skl_cpr_gtw_cfg {
- u32 node_id;
- u32 dma_buffer_size;
- u32 config_length;
- /* not mandatory; required only for DMIC/I2S */
- u32 config_data[1];
-} __packed;
-
-struct skl_cpr_cfg {
- struct skl_base_cfg base_cfg;
- struct skl_audio_data_format out_fmt;
- u32 cpr_feature_mask;
- struct skl_cpr_gtw_cfg gtw_cfg;
-} __packed;
-
-
-struct skl_src_module_cfg {
- struct skl_base_cfg base_cfg;
- enum skl_s_freq src_cfg;
-} __packed;
-
-struct notification_mask {
- u32 notify;
- u32 enable;
-} __packed;
-
-struct skl_up_down_mixer_cfg {
- struct skl_base_cfg base_cfg;
- enum skl_ch_cfg out_ch_cfg;
- /* This should be set to 1 if user coefficients are required */
- u32 coeff_sel;
- /* Pass the user coeff in this array */
- s32 coeff[UP_DOWN_MIXER_MAX_COEFF];
-} __packed;
-
-struct skl_algo_cfg {
- struct skl_base_cfg base_cfg;
- char params[0];
-} __packed;
-
-struct skl_base_outfmt_cfg {
- struct skl_base_cfg base_cfg;
- struct skl_audio_data_format out_fmt;
-} __packed;
-
-enum skl_dma_type {
- SKL_DMA_HDA_HOST_OUTPUT_CLASS = 0,
- SKL_DMA_HDA_HOST_INPUT_CLASS = 1,
- SKL_DMA_HDA_HOST_INOUT_CLASS = 2,
- SKL_DMA_HDA_LINK_OUTPUT_CLASS = 8,
- SKL_DMA_HDA_LINK_INPUT_CLASS = 9,
- SKL_DMA_HDA_LINK_INOUT_CLASS = 0xA,
- SKL_DMA_DMIC_LINK_INPUT_CLASS = 0xB,
- SKL_DMA_I2S_LINK_OUTPUT_CLASS = 0xC,
- SKL_DMA_I2S_LINK_INPUT_CLASS = 0xD,
-};
-
-union skl_ssp_dma_node {
- u8 val;
- struct {
- u8 time_slot_index:4;
- u8 i2s_instance:4;
- } dma_node;
-};
-
-union skl_connector_node_id {
- u32 val;
- struct {
- u32 vindex:8;
- u32 dma_type:4;
- u32 rsvd:20;
- } node;
-};
-
-struct skl_module_fmt {
- u32 channels;
- u32 s_freq;
- u32 bit_depth;
- u32 valid_bit_depth;
- u32 ch_cfg;
- u32 interleaving_style;
- u32 sample_type;
- u32 ch_map;
-};
-
-struct skl_module_cfg;
-
-struct skl_module_inst_id {
- u32 module_id;
- u32 instance_id;
-};
-
-enum skl_module_pin_state {
- SKL_PIN_UNBIND = 0,
- SKL_PIN_BIND_DONE = 1,
-};
-
-struct skl_module_pin {
- struct skl_module_inst_id id;
- bool is_dynamic;
- bool in_use;
- enum skl_module_pin_state pin_state;
- struct skl_module_cfg *tgt_mcfg;
-};
-
-struct skl_specific_cfg {
- u32 set_params;
- u32 param_id;
- u32 caps_size;
- u32 *caps;
-};
-
-enum skl_pipe_state {
- SKL_PIPE_INVALID = 0,
- SKL_PIPE_CREATED = 1,
- SKL_PIPE_PAUSED = 2,
- SKL_PIPE_STARTED = 3
-};
-
-struct skl_pipe_module {
- struct snd_soc_dapm_widget *w;
- struct list_head node;
-};
-
-struct skl_pipe_params {
- u8 host_dma_id;
- u8 link_dma_id;
- u32 ch;
- u32 s_freq;
- u32 s_fmt;
- u8 linktype;
- int stream;
-};
-
-struct skl_pipe {
- u8 ppl_id;
- u8 pipe_priority;
- u16 conn_type;
- u32 memory_pages;
- struct skl_pipe_params *p_params;
- enum skl_pipe_state state;
- struct list_head w_list;
-};
-
-enum skl_module_state {
- SKL_MODULE_UNINIT = 0,
- SKL_MODULE_INIT_DONE = 1,
- SKL_MODULE_LOADED = 2,
- SKL_MODULE_UNLOADED = 3,
- SKL_MODULE_BIND_DONE = 4
-};
-
-struct skl_module_cfg {
- char guid[SKL_UUID_STR_SZ];
- struct skl_module_inst_id id;
- u8 domain;
- bool homogenous_inputs;
- bool homogenous_outputs;
- struct skl_module_fmt in_fmt[MODULE_MAX_IN_PINS];
- struct skl_module_fmt out_fmt[MODULE_MAX_OUT_PINS];
- u8 max_in_queue;
- u8 max_out_queue;
- u8 in_queue_mask;
- u8 out_queue_mask;
- u8 in_queue;
- u8 out_queue;
- u32 mcps;
- u32 ibs;
- u32 obs;
- u8 is_loadable;
- u8 core_id;
- u8 dev_type;
- u8 dma_id;
- u8 time_slot;
- u32 params_fixup;
- u32 converter;
- u32 vbus_id;
- u32 mem_pages;
- struct skl_module_pin *m_in_pin;
- struct skl_module_pin *m_out_pin;
- enum skl_module_type m_type;
- enum skl_hw_conn_type hw_conn_type;
- enum skl_module_state m_state;
- struct skl_pipe *pipe;
- struct skl_specific_cfg formats_config;
-};
-
-struct skl_algo_data {
- u32 param_id;
- u32 set_params;
- u32 max;
- char *params;
-};
-
-struct skl_pipeline {
- struct skl_pipe *pipe;
- struct list_head node;
-};
-
-static inline struct skl *get_skl_ctx(struct device *dev)
-{
- struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
-
- return ebus_to_skl(ebus);
-}
-
-int skl_tplg_be_update_params(struct snd_soc_dai *dai,
- struct skl_pipe_params *params);
-void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
- struct skl_pipe_params *params, int stream);
-int skl_tplg_init(struct snd_soc_platform *platform,
- struct hdac_ext_bus *ebus);
-struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
- struct snd_soc_dai *dai, int stream);
-int skl_tplg_update_pipe_params(struct device *dev,
- struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
-
-int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
-
-int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
-
-int skl_pause_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
-
-int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
-
-int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
-
-int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config);
-
-int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
- *src_module, struct skl_module_cfg *dst_module);
-
-int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg
- *src_module, struct skl_module_cfg *dst_module);
-
-int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
- u32 param_id, struct skl_module_cfg *mcfg);
-int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
- u32 param_id, struct skl_module_cfg *mcfg);
-
-enum skl_bitdepth skl_get_bit_depth(int params);
-#endif
diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h
deleted file mode 100644
index c9ae010b3cc8..000000000000
--- a/sound/soc/intel/skylake/skl-tplg-interface.h
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * skl-tplg-interface.h - Intel DSP FW private data interface
- *
- * Copyright (C) 2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@intel.com>
- * Nilofer, Samreen <samreen.nilofer@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 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 __HDA_TPLG_INTERFACE_H__
-#define __HDA_TPLG_INTERFACE_H__
-
-/*
- * Default types range from 0~12. type can range from 0 to 0xff
- * SST types start at higher to avoid any overlapping in future
- */
-#define SKL_CONTROL_TYPE_BYTE_TLV 0x100
-
-#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
-#define MAX_IN_QUEUE 8
-#define MAX_OUT_QUEUE 8
-
-#define SKL_UUID_STR_SZ 40
-/* Event types goes here */
-/* Reserve event type 0 for no event handlers */
-enum skl_event_types {
- SKL_EVENT_NONE = 0,
- SKL_MIXER_EVENT,
- SKL_MUX_EVENT,
- SKL_VMIXER_EVENT,
- SKL_PGA_EVENT
-};
-
-/**
- * enum skl_ch_cfg - channel configuration
- *
- * @SKL_CH_CFG_MONO: One channel only
- * @SKL_CH_CFG_STEREO: L & R
- * @SKL_CH_CFG_2_1: L, R & LFE
- * @SKL_CH_CFG_3_0: L, C & R
- * @SKL_CH_CFG_3_1: L, C, R & LFE
- * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs
- * @SKL_CH_CFG_4_0: L, C, R & Cs
- * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs
- * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE
- * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two
- * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ]
- * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ]
- * @SKL_CH_CFG_INVALID: Invalid
- */
-enum skl_ch_cfg {
- SKL_CH_CFG_MONO = 0,
- SKL_CH_CFG_STEREO = 1,
- SKL_CH_CFG_2_1 = 2,
- SKL_CH_CFG_3_0 = 3,
- SKL_CH_CFG_3_1 = 4,
- SKL_CH_CFG_QUATRO = 5,
- SKL_CH_CFG_4_0 = 6,
- SKL_CH_CFG_5_0 = 7,
- SKL_CH_CFG_5_1 = 8,
- SKL_CH_CFG_DUAL_MONO = 9,
- SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10,
- SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11,
- SKL_CH_CFG_4_CHANNEL = 12,
- SKL_CH_CFG_INVALID
-};
-
-enum skl_module_type {
- SKL_MODULE_TYPE_MIXER = 0,
- SKL_MODULE_TYPE_COPIER,
- SKL_MODULE_TYPE_UPDWMIX,
- SKL_MODULE_TYPE_SRCINT,
- SKL_MODULE_TYPE_ALGO,
- SKL_MODULE_TYPE_BASE_OUTFMT
-};
-
-enum skl_core_affinity {
- SKL_AFFINITY_CORE_0 = 0,
- SKL_AFFINITY_CORE_1,
- SKL_AFFINITY_CORE_MAX
-};
-
-enum skl_pipe_conn_type {
- SKL_PIPE_CONN_TYPE_NONE = 0,
- SKL_PIPE_CONN_TYPE_FE,
- SKL_PIPE_CONN_TYPE_BE
-};
-
-enum skl_hw_conn_type {
- SKL_CONN_NONE = 0,
- SKL_CONN_SOURCE = 1,
- SKL_CONN_SINK = 2
-};
-
-enum skl_dev_type {
- SKL_DEVICE_BT = 0x0,
- SKL_DEVICE_DMIC = 0x1,
- SKL_DEVICE_I2S = 0x2,
- SKL_DEVICE_SLIMBUS = 0x3,
- SKL_DEVICE_HDALINK = 0x4,
- SKL_DEVICE_HDAHOST = 0x5,
- SKL_DEVICE_NONE
-};
-
-/**
- * enum skl_interleaving - interleaving style
- *
- * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN]
- * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN]
- */
-enum skl_interleaving {
- SKL_INTERLEAVING_PER_CHANNEL = 0,
- SKL_INTERLEAVING_PER_SAMPLE = 1,
-};
-
-enum skl_sample_type {
- SKL_SAMPLE_TYPE_INT_MSB = 0,
- SKL_SAMPLE_TYPE_INT_LSB = 1,
- SKL_SAMPLE_TYPE_INT_SIGNED = 2,
- SKL_SAMPLE_TYPE_INT_UNSIGNED = 3,
- SKL_SAMPLE_TYPE_FLOAT = 4
-};
-
-enum module_pin_type {
- /* All pins of the module takes same PCM inputs or outputs
- * e.g. mixout
- */
- SKL_PIN_TYPE_HOMOGENEOUS,
- /* All pins of the module takes different PCM inputs or outputs
- * e.g mux
- */
- SKL_PIN_TYPE_HETEROGENEOUS,
-};
-
-enum skl_module_param_type {
- SKL_PARAM_DEFAULT = 0,
- SKL_PARAM_INIT,
- SKL_PARAM_SET
-};
-
-struct skl_dfw_module_pin {
- u16 module_id;
- u16 instance_id;
-} __packed;
-
-struct skl_dfw_module_fmt {
- u32 channels;
- u32 freq;
- u32 bit_depth;
- u32 valid_bit_depth;
- u32 ch_cfg;
- u32 interleaving_style;
- u32 sample_type;
- u32 ch_map;
-} __packed;
-
-struct skl_dfw_module_caps {
- u32 set_params:2;
- u32 rsvd:30;
- u32 param_id;
- u32 caps_size;
- u32 caps[HDA_SST_CFG_MAX];
-};
-
-struct skl_dfw_pipe {
- u8 pipe_id;
- u8 pipe_priority;
- u16 conn_type:4;
- u16 rsvd:4;
- u16 memory_pages:8;
-} __packed;
-
-struct skl_dfw_module {
- char uuid[SKL_UUID_STR_SZ];
-
- u16 module_id;
- u16 instance_id;
- u32 max_mcps;
- u32 mem_pages;
- u32 obs;
- u32 ibs;
- u32 vbus_id;
-
- u32 max_in_queue:8;
- u32 max_out_queue:8;
- u32 time_slot:8;
- u32 core_id:4;
- u32 rsvd1:4;
-
- u32 module_type:8;
- u32 conn_type:4;
- u32 dev_type:4;
- u32 hw_conn_type:4;
- u32 rsvd2:12;
-
- u32 params_fixup:8;
- u32 converter:8;
- u32 input_pin_type:1;
- u32 output_pin_type:1;
- u32 is_dynamic_in_pin:1;
- u32 is_dynamic_out_pin:1;
- u32 is_loadable:1;
- u32 rsvd3:11;
-
- struct skl_dfw_pipe pipe;
- struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE];
- struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE];
- struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
- struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
- struct skl_dfw_module_caps caps;
-} __packed;
-
-struct skl_dfw_algo_data {
- u32 set_params:2;
- u32 rsvd:30;
- u32 param_id;
- u32 max;
- char params[0];
-} __packed;
-
-#endif
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
deleted file mode 100644
index 443a15de94b5..000000000000
--- a/sound/soc/intel/skylake/skl.c
+++ /dev/null
@@ -1,689 +0,0 @@
-/*
- * skl.c - Implementation of ASoC Intel SKL HD Audio driver
- *
- * Copyright (C) 2014-2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@intel.com>
- *
- * Derived mostly from Intel HDA driver with following copyrights:
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- * PeiSen Hou <pshou@realtek.com.tw>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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/pci.h>
-#include <linux/pm_runtime.h>
-#include <linux/platform_device.h>
-#include <linux/firmware.h>
-#include <sound/pcm.h>
-#include "../common/sst-acpi.h"
-#include "skl.h"
-#include "skl-sst-dsp.h"
-#include "skl-sst-ipc.h"
-
-/*
- * initialize the PCI registers
- */
-static void skl_update_pci_byte(struct pci_dev *pci, unsigned int reg,
- unsigned char mask, unsigned char val)
-{
- unsigned char data;
-
- pci_read_config_byte(pci, reg, &data);
- data &= ~mask;
- data |= (val & mask);
- pci_write_config_byte(pci, reg, data);
-}
-
-static void skl_init_pci(struct skl *skl)
-{
- struct hdac_ext_bus *ebus = &skl->ebus;
-
- /*
- * Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
- * TCSEL == Traffic Class Select Register, which sets PCI express QOS
- * Ensuring these bits are 0 clears playback static on some HD Audio
- * codecs.
- * The PCI register TCSEL is defined in the Intel manuals.
- */
- dev_dbg(ebus_to_hbus(ebus)->dev, "Clearing TCSEL\n");
- skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0);
-}
-
-static void update_pci_dword(struct pci_dev *pci,
- unsigned int reg, u32 mask, u32 val)
-{
- u32 data = 0;
-
- pci_read_config_dword(pci, reg, &data);
- data &= ~mask;
- data |= (val & mask);
- pci_write_config_dword(pci, reg, data);
-}
-
-/*
- * skl_enable_miscbdcge - enable/dsiable CGCTL.MISCBDCGE bits
- *
- * @dev: device pointer
- * @enable: enable/disable flag
- */
-static void skl_enable_miscbdcge(struct device *dev, bool enable)
-{
- struct pci_dev *pci = to_pci_dev(dev);
- u32 val;
-
- val = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0;
-
- update_pci_dword(pci, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, val);
-}
-
-/*
- * While performing reset, controller may not come back properly causing
- * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset
- * (init chip) and then again set CGCTL.MISCBDCGE to 1
- */
-static int skl_init_chip(struct hdac_bus *bus, bool full_reset)
-{
- int ret;
-
- skl_enable_miscbdcge(bus->dev, false);
- ret = snd_hdac_bus_init_chip(bus, full_reset);
- skl_enable_miscbdcge(bus->dev, true);
-
- return ret;
-}
-
-/* called from IRQ */
-static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
-{
- snd_pcm_period_elapsed(hstr->substream);
-}
-
-static irqreturn_t skl_interrupt(int irq, void *dev_id)
-{
- struct hdac_ext_bus *ebus = dev_id;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- u32 status;
-
- if (!pm_runtime_active(bus->dev))
- return IRQ_NONE;
-
- spin_lock(&bus->reg_lock);
-
- status = snd_hdac_chip_readl(bus, INTSTS);
- if (status == 0 || status == 0xffffffff) {
- spin_unlock(&bus->reg_lock);
- return IRQ_NONE;
- }
-
- /* clear rirb int */
- status = snd_hdac_chip_readb(bus, RIRBSTS);
- if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE)
- snd_hdac_bus_update_rirb(bus);
- snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
- }
-
- spin_unlock(&bus->reg_lock);
-
- return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
-}
-
-static irqreturn_t skl_threaded_handler(int irq, void *dev_id)
-{
- struct hdac_ext_bus *ebus = dev_id;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- u32 status;
-
- status = snd_hdac_chip_readl(bus, INTSTS);
-
- snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update);
-
- return IRQ_HANDLED;
-}
-
-static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
-{
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- int ret;
-
- ret = request_threaded_irq(skl->pci->irq, skl_interrupt,
- skl_threaded_handler,
- IRQF_SHARED,
- KBUILD_MODNAME, ebus);
- if (ret) {
- dev_err(bus->dev,
- "unable to grab IRQ %d, disabling device\n",
- skl->pci->irq);
- return ret;
- }
-
- bus->irq = skl->pci->irq;
- pci_intx(skl->pci, 1);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int _skl_suspend(struct hdac_ext_bus *ebus)
-{
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- int ret;
-
- snd_hdac_ext_bus_link_power_down_all(ebus);
-
- ret = skl_suspend_dsp(skl);
- if (ret < 0)
- return ret;
-
- snd_hdac_bus_stop_chip(bus);
- skl_enable_miscbdcge(bus->dev, false);
- snd_hdac_bus_enter_link_reset(bus);
- skl_enable_miscbdcge(bus->dev, true);
-
- return 0;
-}
-
-static int _skl_resume(struct hdac_ext_bus *ebus)
-{
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- skl_init_pci(skl);
- skl_init_chip(bus, true);
-
- return skl_resume_dsp(skl);
-}
-#endif
-
-#ifdef CONFIG_PM_SLEEP
-/*
- * power management
- */
-static int skl_suspend(struct device *dev)
-{
- struct pci_dev *pci = to_pci_dev(dev);
- struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- /*
- * Do not suspend if streams which are marked ignore suspend are
- * running, we need to save the state for these and continue
- */
- if (skl->supend_active) {
- snd_hdac_ext_bus_link_power_down_all(ebus);
- enable_irq_wake(bus->irq);
- pci_save_state(pci);
- pci_disable_device(pci);
- return 0;
- } else {
- return _skl_suspend(ebus);
- }
-}
-
-static int skl_resume(struct device *dev)
-{
- struct pci_dev *pci = to_pci_dev(dev);
- struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- int ret;
-
- /*
- * resume only when we are not in suspend active, otherwise need to
- * restore the device
- */
- if (skl->supend_active) {
- pci_restore_state(pci);
- ret = pci_enable_device(pci);
- snd_hdac_ext_bus_link_power_up_all(ebus);
- disable_irq_wake(bus->irq);
- } else {
- ret = _skl_resume(ebus);
- }
-
- return ret;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-#ifdef CONFIG_PM
-static int skl_runtime_suspend(struct device *dev)
-{
- struct pci_dev *pci = to_pci_dev(dev);
- struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- dev_dbg(bus->dev, "in %s\n", __func__);
-
- return _skl_suspend(ebus);
-}
-
-static int skl_runtime_resume(struct device *dev)
-{
- struct pci_dev *pci = to_pci_dev(dev);
- struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- dev_dbg(bus->dev, "in %s\n", __func__);
-
- return _skl_resume(ebus);
-}
-#endif /* CONFIG_PM */
-
-static const struct dev_pm_ops skl_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume)
- SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL)
-};
-
-/*
- * destructor
- */
-static int skl_free(struct hdac_ext_bus *ebus)
-{
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
-
- skl->init_failed = 1; /* to be sure */
-
- snd_hdac_ext_stop_streams(ebus);
-
- if (bus->irq >= 0)
- free_irq(bus->irq, (void *)bus);
- if (bus->remap_addr)
- iounmap(bus->remap_addr);
-
- snd_hdac_bus_free_stream_pages(bus);
- snd_hdac_stream_free_all(ebus);
- snd_hdac_link_free_all(ebus);
- pci_release_regions(skl->pci);
- pci_disable_device(skl->pci);
-
- snd_hdac_ext_bus_exit(ebus);
-
- return 0;
-}
-
-static int skl_machine_device_register(struct skl *skl, void *driver_data)
-{
- struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
- struct platform_device *pdev;
- struct sst_acpi_mach *mach = driver_data;
- int ret;
-
- mach = sst_acpi_find_machine(mach);
- if (mach == NULL) {
- dev_err(bus->dev, "No matching machine driver found\n");
- return -ENODEV;
- }
- skl->fw_name = mach->fw_filename;
-
- pdev = platform_device_alloc(mach->drv_name, -1);
- if (pdev == NULL) {
- dev_err(bus->dev, "platform device alloc failed\n");
- return -EIO;
- }
-
- ret = platform_device_add(pdev);
- if (ret) {
- dev_err(bus->dev, "failed to add machine device\n");
- platform_device_put(pdev);
- return -EIO;
- }
- skl->i2s_dev = pdev;
-
- return 0;
-}
-
-static void skl_machine_device_unregister(struct skl *skl)
-{
- if (skl->i2s_dev)
- platform_device_unregister(skl->i2s_dev);
-}
-
-static int skl_dmic_device_register(struct skl *skl)
-{
- struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
- struct platform_device *pdev;
- int ret;
-
- /* SKL has one dmic port, so allocate dmic device for this */
- pdev = platform_device_alloc("dmic-codec", -1);
- if (!pdev) {
- dev_err(bus->dev, "failed to allocate dmic device\n");
- return -ENOMEM;
- }
-
- ret = platform_device_add(pdev);
- if (ret) {
- dev_err(bus->dev, "failed to add dmic device: %d\n", ret);
- platform_device_put(pdev);
- return ret;
- }
- skl->dmic_dev = pdev;
-
- return 0;
-}
-
-static void skl_dmic_device_unregister(struct skl *skl)
-{
- if (skl->dmic_dev)
- platform_device_unregister(skl->dmic_dev);
-}
-
-/*
- * Probe the given codec address
- */
-static int probe_codec(struct hdac_ext_bus *ebus, int addr)
-{
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
- (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
-
- mutex_lock(&bus->cmd_mutex);
- snd_hdac_bus_send_cmd(bus, cmd);
- snd_hdac_bus_get_response(bus, addr, &res);
- mutex_unlock(&bus->cmd_mutex);
- if (res == -1)
- return -EIO;
- dev_dbg(bus->dev, "codec #%d probed OK\n", addr);
-
- return snd_hdac_ext_bus_device_init(ebus, addr);
-}
-
-/* Codec initialization */
-static int skl_codec_create(struct hdac_ext_bus *ebus)
-{
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- int c, max_slots;
-
- max_slots = HDA_MAX_CODECS;
-
- /* First try to probe all given codec slots */
- for (c = 0; c < max_slots; c++) {
- if ((bus->codec_mask & (1 << c))) {
- if (probe_codec(ebus, c) < 0) {
- /*
- * Some BIOSen give you wrong codec addresses
- * that don't exist
- */
- dev_warn(bus->dev,
- "Codec #%d probe error; disabling it...\n", c);
- bus->codec_mask &= ~(1 << c);
- /*
- * More badly, accessing to a non-existing
- * codec often screws up the controller bus,
- * and disturbs the further communications.
- * Thus if an error occurs during probing,
- * better to reset the controller bus to get
- * back to the sanity state.
- */
- snd_hdac_bus_stop_chip(bus);
- skl_init_chip(bus, true);
- }
- }
- }
-
- return 0;
-}
-
-static const struct hdac_bus_ops bus_core_ops = {
- .command = snd_hdac_bus_send_cmd,
- .get_response = snd_hdac_bus_get_response,
-};
-
-/*
- * constructor
- */
-static int skl_create(struct pci_dev *pci,
- const struct hdac_io_ops *io_ops,
- struct skl **rskl)
-{
- struct skl *skl;
- struct hdac_ext_bus *ebus;
-
- int err;
-
- *rskl = NULL;
-
- err = pci_enable_device(pci);
- if (err < 0)
- return err;
-
- skl = devm_kzalloc(&pci->dev, sizeof(*skl), GFP_KERNEL);
- if (!skl) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
- ebus = &skl->ebus;
- snd_hdac_ext_bus_init(ebus, &pci->dev, &bus_core_ops, io_ops);
- ebus->bus.use_posbuf = 1;
- skl->pci = pci;
-
- ebus->bus.bdl_pos_adj = 0;
-
- *rskl = skl;
-
- return 0;
-}
-
-static int skl_first_init(struct hdac_ext_bus *ebus)
-{
- struct skl *skl = ebus_to_skl(ebus);
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct pci_dev *pci = skl->pci;
- int err;
- unsigned short gcap;
- int cp_streams, pb_streams, start_idx;
-
- err = pci_request_regions(pci, "Skylake HD audio");
- if (err < 0)
- return err;
-
- bus->addr = pci_resource_start(pci, 0);
- bus->remap_addr = pci_ioremap_bar(pci, 0);
- if (bus->remap_addr == NULL) {
- dev_err(bus->dev, "ioremap error\n");
- return -ENXIO;
- }
-
- snd_hdac_ext_bus_parse_capabilities(ebus);
-
- if (skl_acquire_irq(ebus, 0) < 0)
- return -EBUSY;
-
- pci_set_master(pci);
- synchronize_irq(bus->irq);
-
- gcap = snd_hdac_chip_readw(bus, GCAP);
- dev_dbg(bus->dev, "chipset global capabilities = 0x%x\n", gcap);
-
- /* allow 64bit DMA address if supported by H/W */
- if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) {
- dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64));
- } else {
- dma_set_mask(bus->dev, DMA_BIT_MASK(32));
- dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32));
- }
-
- /* read number of streams from GCAP register */
- cp_streams = (gcap >> 8) & 0x0f;
- pb_streams = (gcap >> 12) & 0x0f;
-
- if (!pb_streams && !cp_streams)
- return -EIO;
-
- ebus->num_streams = cp_streams + pb_streams;
-
- /* initialize streams */
- snd_hdac_ext_stream_init_all
- (ebus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
- start_idx = cp_streams;
- snd_hdac_ext_stream_init_all
- (ebus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
-
- err = snd_hdac_bus_alloc_stream_pages(bus);
- if (err < 0)
- return err;
-
- /* initialize chip */
- skl_init_pci(skl);
-
- skl_init_chip(bus, true);
-
- /* codec detection */
- if (!bus->codec_mask) {
- dev_info(bus->dev, "no hda codecs found!\n");
- }
-
- return 0;
-}
-
-static int skl_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
-{
- struct skl *skl;
- struct hdac_ext_bus *ebus = NULL;
- struct hdac_bus *bus = NULL;
- int err;
-
- /* we use ext core ops, so provide NULL for ops here */
- err = skl_create(pci, NULL, &skl);
- if (err < 0)
- return err;
-
- ebus = &skl->ebus;
- bus = ebus_to_hbus(ebus);
-
- err = skl_first_init(ebus);
- if (err < 0)
- goto out_free;
-
- skl->nhlt = skl_nhlt_init(bus->dev);
-
- if (skl->nhlt == NULL)
- goto out_free;
-
- pci_set_drvdata(skl->pci, ebus);
-
- /* check if dsp is there */
- if (ebus->ppcap) {
- err = skl_machine_device_register(skl,
- (void *)pci_id->driver_data);
- if (err < 0)
- goto out_free;
-
- err = skl_init_dsp(skl);
- if (err < 0) {
- dev_dbg(bus->dev, "error failed to register dsp\n");
- goto out_mach_free;
- }
- skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
-
- }
- if (ebus->mlcap)
- snd_hdac_ext_bus_get_ml_capabilities(ebus);
-
- /* create device for soc dmic */
- err = skl_dmic_device_register(skl);
- if (err < 0)
- goto out_dsp_free;
-
- /* register platform dai and controls */
- err = skl_platform_register(bus->dev);
- if (err < 0)
- goto out_dmic_free;
-
- /* create codec instances */
- err = skl_codec_create(ebus);
- if (err < 0)
- goto out_unregister;
-
- /*configure PM */
- pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY);
- pm_runtime_use_autosuspend(bus->dev);
- pm_runtime_put_noidle(bus->dev);
- pm_runtime_allow(bus->dev);
-
- return 0;
-
-out_unregister:
- skl_platform_unregister(bus->dev);
-out_dmic_free:
- skl_dmic_device_unregister(skl);
-out_dsp_free:
- skl_free_dsp(skl);
-out_mach_free:
- skl_machine_device_unregister(skl);
-out_free:
- skl->init_failed = 1;
- skl_free(ebus);
-
- return err;
-}
-
-static void skl_remove(struct pci_dev *pci)
-{
- struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
- struct skl *skl = ebus_to_skl(ebus);
-
- if (skl->tplg)
- release_firmware(skl->tplg);
-
- if (pci_dev_run_wake(pci))
- pm_runtime_get_noresume(&pci->dev);
- pci_dev_put(pci);
- skl_platform_unregister(&pci->dev);
- skl_free_dsp(skl);
- skl_machine_device_unregister(skl);
- skl_dmic_device_unregister(skl);
- skl_free(ebus);
- dev_set_drvdata(&pci->dev, NULL);
-}
-
-static struct sst_acpi_mach sst_skl_devdata[] = {
- { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
- { "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
- NULL, NULL, NULL },
- { "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
- NULL, NULL, NULL },
- {}
-};
-
-/* PCI IDs */
-static const struct pci_device_id skl_ids[] = {
- /* Sunrise Point-LP */
- { PCI_DEVICE(0x8086, 0x9d70),
- .driver_data = (unsigned long)&sst_skl_devdata},
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, skl_ids);
-
-/* pci_driver definition */
-static struct pci_driver skl_driver = {
- .name = KBUILD_MODNAME,
- .id_table = skl_ids,
- .probe = skl_probe,
- .remove = skl_remove,
- .driver = {
- .pm = &skl_pm,
- },
-};
-module_pci_driver(skl_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Intel Skylake ASoC HDA driver");
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
deleted file mode 100644
index 4d18293b5537..000000000000
--- a/sound/soc/intel/skylake/skl.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * skl.h - HD Audio skylake defintions.
- *
- * Copyright (C) 2015 Intel Corp
- * Author: Jeeja KP <jeeja.kp@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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#ifndef __SOUND_SOC_SKL_H
-#define __SOUND_SOC_SKL_H
-
-#include <sound/hda_register.h>
-#include <sound/hdaudio_ext.h>
-#include "skl-nhlt.h"
-
-#define SKL_SUSPEND_DELAY 2000
-
-/* Vendor Specific Registers */
-#define AZX_REG_VS_EM1 0x1000
-#define AZX_REG_VS_INRC 0x1004
-#define AZX_REG_VS_OUTRC 0x1008
-#define AZX_REG_VS_FIFOTRK 0x100C
-#define AZX_REG_VS_FIFOTRK2 0x1010
-#define AZX_REG_VS_EM2 0x1030
-#define AZX_REG_VS_EM3L 0x1038
-#define AZX_REG_VS_EM3U 0x103C
-#define AZX_REG_VS_EM4L 0x1040
-#define AZX_REG_VS_EM4U 0x1044
-#define AZX_REG_VS_LTRC 0x1048
-#define AZX_REG_VS_D0I3C 0x104A
-#define AZX_REG_VS_PCE 0x104B
-#define AZX_REG_VS_L2MAGC 0x1050
-#define AZX_REG_VS_L2LAHPT 0x1054
-#define AZX_REG_VS_SDXDPIB_XBASE 0x1084
-#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20
-#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
-#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
-
-#define AZX_PCIREG_CGCTL 0x48
-#define AZX_CGCTL_MISCBDCGE_MASK (1 << 6)
-
-struct skl_dsp_resource {
- u32 max_mcps;
- u32 max_mem;
- u32 mcps;
- u32 mem;
-};
-
-struct skl {
- struct hdac_ext_bus ebus;
- struct pci_dev *pci;
-
- unsigned int init_failed:1; /* delayed init failed */
- struct platform_device *dmic_dev;
- struct platform_device *i2s_dev;
-
- void *nhlt; /* nhlt ptr */
- struct skl_sst *skl_sst; /* sst skl ctx */
-
- struct skl_dsp_resource resource;
- struct list_head ppl_list;
-
- const char *fw_name;
- const struct firmware *tplg;
-
- int supend_active;
-};
-
-#define skl_to_ebus(s) (&(s)->ebus)
-#define ebus_to_skl(sbus) \
- container_of(sbus, struct skl, sbus)
-
-/* to pass dai dma data */
-struct skl_dma_params {
- u32 format;
- u8 stream_tag;
-};
-
-int skl_platform_unregister(struct device *dev);
-int skl_platform_register(struct device *dev);
-
-void *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void *addr);
-struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
- u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
-
-int skl_init_dsp(struct skl *skl);
-void skl_free_dsp(struct skl *skl);
-int skl_suspend_dsp(struct skl *skl);
-int skl_resume_dsp(struct skl *skl);
-#endif /* __SOUND_SOC_SKL_H */
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 1a354a6b6e87..f3ff3fb49239 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -1,29 +1,14 @@
-config SND_JZ4740_SOC
- tristate "SoC Audio for Ingenic JZ4740 SoC"
- depends on MACH_JZ4740 || COMPILE_TEST
- select SND_SOC_GENERIC_DMAENGINE_PCM
- help
- Say Y or M if you want to add support for codecs attached to
- the JZ4740 I2S interface. You will also need to select the audio
- interfaces to support below.
-
-if SND_JZ4740_SOC
+# SPDX-License-Identifier: GPL-2.0-only
+menu "JZ4740"
config SND_JZ4740_SOC_I2S
tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
+ depends on MIPS || COMPILE_TEST
depends on HAS_IOMEM
+ select REGMAP_MMIO
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
based boards.
-config SND_JZ4740_SOC_QI_LB60
- tristate "SoC Audio support for Qi LB60"
- depends on HAS_IOMEM
- depends on JZ4740_QI_LB60 || COMPILE_TEST
- select SND_JZ4740_SOC_I2S
- select SND_SOC_JZ4740_CODEC
- help
- Say Y if you want to add support for ASoC audio on the Qi LB60 board
- a.k.a Qi Ben NanoNote.
-
-endif
+endmenu
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
index d32c540555c4..1c7f3f375318 100644
--- a/sound/soc/jz4740/Makefile
+++ b/sound/soc/jz4740/Makefile
@@ -1,11 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Jz4740 Platform Support
#
-snd-soc-jz4740-i2s-objs := jz4740-i2s.o
+snd-soc-jz4740-i2s-y := jz4740-i2s.o
obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
-
-# Jz4740 Machine Support
-snd-soc-qi-lb60-objs := qi_lb60.o
-
-obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 794a3499e567..517619531615 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -1,31 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * 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/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-
-#include <linux/dma-mapping.h>
-
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -33,11 +23,6 @@
#include <sound/initval.h>
#include <sound/dmaengine_pcm.h>
-#include "jz4740-i2s.h"
-
-#define JZ4740_DMA_TYPE_AIC_TRANSMIT 24
-#define JZ4740_DMA_TYPE_AIC_RECEIVE 25
-
#define JZ_REG_AIC_CONF 0x00
#define JZ_REG_AIC_CTRL 0x04
#define JZ_REG_AIC_I2S_FMT 0x10
@@ -46,66 +31,56 @@
#define JZ_REG_AIC_CLK_DIV 0x30
#define JZ_REG_AIC_FIFO 0x34
-#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
-#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
-#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
-#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
-#define JZ_AIC_CONF_I2S BIT(4)
-#define JZ_AIC_CONF_RESET BIT(3)
-#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
-#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
-#define JZ_AIC_CONF_ENABLE BIT(0)
-
-#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
-#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
-#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24
-#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16
-#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_MASK \
- (0xf << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET)
-#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_MASK \
- (0x1f << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET)
-
-#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
-#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
-#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
-#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
-#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
-#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
-#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
-#define JZ_AIC_CTRL_FLUSH BIT(8)
-#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
-#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
-#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
-#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
-#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
-#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
-#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
-
-#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
-#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
-
-#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
-#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13)
-#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
-#define JZ_AIC_I2S_FMT_MSB BIT(0)
-
-#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
-
-#define JZ_AIC_CLK_DIV_MASK 0xf
-#define I2SDIV_DV_SHIFT 8
-#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT)
-#define I2SDIV_IDV_SHIFT 8
-#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT)
-
-enum jz47xx_i2s_version {
- JZ_I2S_JZ4740,
- JZ_I2S_JZ4780,
+#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
+#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
+#define JZ_AIC_CONF_I2S BIT(4)
+#define JZ_AIC_CONF_RESET BIT(3)
+#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
+#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
+#define JZ_AIC_CONF_ENABLE BIT(0)
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE GENMASK(21, 19)
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE GENMASK(18, 16)
+#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
+#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
+#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
+#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
+#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
+#define JZ_AIC_CTRL_TFLUSH BIT(8)
+#define JZ_AIC_CTRL_RFLUSH BIT(7)
+#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
+#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
+#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
+#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
+#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
+#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
+#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
+
+#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
+#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13)
+#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
+#define JZ_AIC_I2S_FMT_MSB BIT(0)
+
+#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
+
+struct i2s_soc_info {
+ struct snd_soc_dai_driver *dai;
+
+ struct reg_field field_rx_fifo_thresh;
+ struct reg_field field_tx_fifo_thresh;
+ struct reg_field field_i2sdiv_capture;
+ struct reg_field field_i2sdiv_playback;
+
+ bool shared_fifo_flush;
};
struct jz4740_i2s {
- struct resource *mem;
- void __iomem *base;
- dma_addr_t phys_base;
+ struct regmap *regmap;
+
+ struct regmap_field *field_rx_fifo_thresh;
+ struct regmap_field *field_tx_fifo_thresh;
+ struct regmap_field *field_i2sdiv_capture;
+ struct regmap_field *field_i2sdiv_playback;
struct clk *clk_aic;
struct clk *clk_i2s;
@@ -113,40 +88,44 @@ struct jz4740_i2s {
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct snd_dmaengine_dai_dma_data capture_dma_data;
- enum jz47xx_i2s_version version;
+ const struct i2s_soc_info *soc_info;
};
-static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
- unsigned int reg)
-{
- return readl(i2s->base + reg);
-}
-
-static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
- unsigned int reg, uint32_t value)
-{
- writel(value, i2s->base + reg);
-}
-
static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- uint32_t conf, ctrl;
+ int ret;
- if (dai->active)
- return 0;
+ /*
+ * When we can flush FIFOs independently, only flush the FIFO
+ * that is starting up. We can do this when the DAI is active
+ * because it does not disturb other active substreams.
+ */
+ if (!i2s->soc_info->shared_fifo_flush) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH);
+ else
+ regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_RFLUSH);
+ }
- ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
- ctrl |= JZ_AIC_CTRL_FLUSH;
- jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+ if (snd_soc_dai_active(dai))
+ return 0;
- clk_prepare_enable(i2s->clk_i2s);
+ /*
+ * When there is a shared flush bit for both FIFOs, the TFLUSH
+ * bit flushes both FIFOs. Flushing while the DAI is active would
+ * cause FIFO underruns in other active substreams so we have to
+ * guard this behind the snd_soc_dai_active() check.
+ */
+ if (i2s->soc_info->shared_fifo_flush)
+ regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH);
- conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
- conf |= JZ_AIC_CONF_ENABLE;
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+ ret = clk_prepare_enable(i2s->clk_i2s);
+ if (ret)
+ return ret;
+ regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
return 0;
}
@@ -154,14 +133,11 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- uint32_t conf;
- if (dai->active)
+ if (snd_soc_dai_active(dai))
return;
- conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
- conf &= ~JZ_AIC_CONF_ENABLE;
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+ regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
clk_disable_unprepare(i2s->clk_i2s);
}
@@ -170,8 +146,6 @@ static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-
- uint32_t ctrl;
uint32_t mask;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -179,51 +153,43 @@ static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
else
mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
- ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ctrl |= mask;
+ regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ctrl &= ~mask;
+ regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask);
break;
default:
return -EINVAL;
}
- jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
-
return 0;
}
static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ const unsigned int conf_mask = JZ_AIC_CONF_BIT_CLK_MASTER |
+ JZ_AIC_CONF_SYNC_CLK_MASTER;
+ unsigned int conf = 0, format = 0;
- uint32_t format = 0;
- uint32_t conf;
-
- conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
-
- conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_BC_FP:
conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_BP_FC:
conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_BC_FC:
break;
default:
return -EINVAL;
@@ -246,291 +212,378 @@ static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
- jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
+ regmap_update_bits(i2s->regmap, JZ_REG_AIC_CONF, conf_mask, conf);
+ regmap_write(i2s->regmap, JZ_REG_AIC_I2S_FMT, format);
return 0;
}
+static int jz4740_i2s_get_i2sdiv(unsigned long mclk, unsigned long rate,
+ unsigned long i2sdiv_max)
+{
+ unsigned long div, rate1, rate2, err1, err2;
+
+ div = mclk / (64 * rate);
+ if (div == 0)
+ div = 1;
+
+ rate1 = mclk / (64 * div);
+ rate2 = mclk / (64 * (div + 1));
+
+ err1 = abs(rate1 - rate);
+ err2 = abs(rate2 - rate);
+
+ /*
+ * Choose the divider that produces the smallest error in the
+ * output rate and reject dividers with a 5% or higher error.
+ * In the event that both dividers are outside the acceptable
+ * error margin, reject the rate to prevent distorted audio.
+ * (The number 5% is arbitrary.)
+ */
+ if (div <= i2sdiv_max && err1 <= err2 && err1 < rate/20)
+ return div;
+ if (div < i2sdiv_max && err2 < rate/20)
+ return div + 1;
+
+ return -EINVAL;
+}
+
static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ struct regmap_field *div_field;
+ unsigned long i2sdiv_max;
unsigned int sample_size;
- uint32_t ctrl, div_reg;
- int div;
+ uint32_t ctrl, conf;
+ int div = 1;
- ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
-
- div_reg = jz4740_i2s_read(i2s, JZ_REG_AIC_CLK_DIV);
- div = clk_get_rate(i2s->clk_i2s) / (64 * params_rate(params));
+ regmap_read(i2s->regmap, JZ_REG_AIC_CTRL, &ctrl);
+ regmap_read(i2s->regmap, JZ_REG_AIC_CONF, &conf);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
sample_size = 0;
break;
- case SNDRV_PCM_FORMAT_S16:
+ case SNDRV_PCM_FORMAT_S16_LE:
sample_size = 1;
break;
+ case SNDRV_PCM_FORMAT_S20_LE:
+ sample_size = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ sample_size = 4;
+ break;
default:
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
- ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
+ ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE;
+ ctrl |= FIELD_PREP(JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE, sample_size);
+
if (params_channels(params) == 1)
ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
else
ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
- div_reg &= ~I2SDIV_DV_MASK;
- div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
+ div_field = i2s->field_i2sdiv_playback;
+ i2sdiv_max = GENMASK(i2s->soc_info->field_i2sdiv_playback.msb,
+ i2s->soc_info->field_i2sdiv_playback.lsb);
} else {
- ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
- ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
-
- if (i2s->version >= JZ_I2S_JZ4780) {
- div_reg &= ~I2SDIV_IDV_MASK;
- div_reg |= (div - 1) << I2SDIV_IDV_SHIFT;
- } else {
- div_reg &= ~I2SDIV_DV_MASK;
- div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
- }
- }
-
- jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
- jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg);
-
- return 0;
-}
+ ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE;
+ ctrl |= FIELD_PREP(JZ_AIC_CTRL_INPUT_SAMPLE_SIZE, sample_size);
-static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- struct clk *parent;
- int ret = 0;
-
- switch (clk_id) {
- case JZ4740_I2S_CLKSRC_EXT:
- parent = clk_get(NULL, "ext");
- clk_set_parent(i2s->clk_i2s, parent);
- break;
- case JZ4740_I2S_CLKSRC_PLL:
- parent = clk_get(NULL, "pll half");
- clk_set_parent(i2s->clk_i2s, parent);
- ret = clk_set_rate(i2s->clk_i2s, freq);
- break;
- default:
- return -EINVAL;
+ div_field = i2s->field_i2sdiv_capture;
+ i2sdiv_max = GENMASK(i2s->soc_info->field_i2sdiv_capture.msb,
+ i2s->soc_info->field_i2sdiv_capture.lsb);
}
- clk_put(parent);
-
- return ret;
-}
-
-static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- uint32_t conf;
-
- if (dai->active) {
- conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
- conf &= ~JZ_AIC_CONF_ENABLE;
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
- clk_disable_unprepare(i2s->clk_i2s);
+ /*
+ * Only calculate I2SDIV if we're supplying the bit or frame clock.
+ * If the codec is supplying both clocks then the divider output is
+ * unused, and we don't want it to limit the allowed sample rates.
+ */
+ if (conf & (JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER)) {
+ div = jz4740_i2s_get_i2sdiv(clk_get_rate(i2s->clk_i2s),
+ params_rate(params), i2sdiv_max);
+ if (div < 0)
+ return div;
}
- clk_disable_unprepare(i2s->clk_aic);
+ regmap_write(i2s->regmap, JZ_REG_AIC_CTRL, ctrl);
+ regmap_field_write(div_field, div - 1);
return 0;
}
-static int jz4740_i2s_resume(struct snd_soc_dai *dai)
-{
- struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- uint32_t conf;
-
- clk_prepare_enable(i2s->clk_aic);
-
- if (dai->active) {
- clk_prepare_enable(i2s->clk_i2s);
-
- conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
- conf |= JZ_AIC_CONF_ENABLE;
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
- }
-
- return 0;
-}
-
-static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
-{
- struct snd_dmaengine_dai_dma_data *dma_data;
-
- /* Playback */
- dma_data = &i2s->playback_dma_data;
- dma_data->maxburst = 16;
- dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT;
- dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
-
- /* Capture */
- dma_data = &i2s->capture_dma_data;
- dma_data->maxburst = 16;
- dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE;
- dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
-}
-
static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- uint32_t conf;
- clk_prepare_enable(i2s->clk_aic);
-
- jz4740_i2c_init_pcm_config(i2s);
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
&i2s->capture_dma_data);
- if (i2s->version >= JZ_I2S_JZ4780) {
- conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
- (8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
- JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
- JZ_AIC_CONF_I2S |
- JZ_AIC_CONF_INTERNAL_CODEC;
- } else {
- conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
- (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
- JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
- JZ_AIC_CONF_I2S |
- JZ_AIC_CONF_INTERNAL_CODEC;
- }
-
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
- jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
-
- return 0;
-}
-
-static int jz4740_i2s_dai_remove(struct snd_soc_dai *dai)
-{
- struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-
- clk_disable_unprepare(i2s->clk_aic);
return 0;
}
static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
+ .probe = jz4740_i2s_dai_probe,
.startup = jz4740_i2s_startup,
.shutdown = jz4740_i2s_shutdown,
.trigger = jz4740_i2s_trigger,
.hw_params = jz4740_i2s_hw_params,
.set_fmt = jz4740_i2s_set_fmt,
- .set_sysclk = jz4740_i2s_set_sysclk,
};
#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE)
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_driver jz4740_i2s_dai = {
- .probe = jz4740_i2s_dai_probe,
- .remove = jz4740_i2s_dai_remove,
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = JZ4740_I2S_FMTS,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = JZ4740_I2S_FMTS,
},
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
.ops = &jz4740_i2s_dai_ops,
- .suspend = jz4740_i2s_suspend,
- .resume = jz4740_i2s_resume,
};
-static struct snd_soc_dai_driver jz4780_i2s_dai = {
- .probe = jz4740_i2s_dai_probe,
- .remove = jz4740_i2s_dai_remove,
+static const struct i2s_soc_info jz4740_i2s_soc_info = {
+ .dai = &jz4740_i2s_dai,
+ .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 12, 15),
+ .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 8, 11),
+ .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
+ .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
+ .shared_fifo_flush = true,
+};
+
+static const struct i2s_soc_info jz4760_i2s_soc_info = {
+ .dai = &jz4740_i2s_dai,
+ .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
+ .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
+ .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
+ .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
+};
+
+static const struct i2s_soc_info x1000_i2s_soc_info = {
+ .dai = &jz4740_i2s_dai,
+ .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
+ .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
+ .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8),
+ .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8),
+};
+
+static struct snd_soc_dai_driver jz4770_i2s_dai = {
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = JZ4740_I2S_FMTS,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = JZ4740_I2S_FMTS,
},
.ops = &jz4740_i2s_dai_ops,
- .suspend = jz4740_i2s_suspend,
- .resume = jz4740_i2s_resume,
};
+static const struct i2s_soc_info jz4770_i2s_soc_info = {
+ .dai = &jz4770_i2s_dai,
+ .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
+ .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
+ .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11),
+ .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
+};
+
+static const struct i2s_soc_info jz4780_i2s_soc_info = {
+ .dai = &jz4770_i2s_dai,
+ .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
+ .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
+ .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11),
+ .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
+};
+
+static int jz4740_i2s_suspend(struct snd_soc_component *component)
+{
+ struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
+
+ if (snd_soc_component_active(component)) {
+ regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
+ clk_disable_unprepare(i2s->clk_i2s);
+ }
+
+ clk_disable_unprepare(i2s->clk_aic);
+
+ return 0;
+}
+
+static int jz4740_i2s_resume(struct snd_soc_component *component)
+{
+ struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = clk_prepare_enable(i2s->clk_aic);
+ if (ret)
+ return ret;
+
+ if (snd_soc_component_active(component)) {
+ ret = clk_prepare_enable(i2s->clk_i2s);
+ if (ret) {
+ clk_disable_unprepare(i2s->clk_aic);
+ return ret;
+ }
+
+ regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
+ }
+
+ return 0;
+}
+
+static int jz4740_i2s_probe(struct snd_soc_component *component)
+{
+ struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = clk_prepare_enable(i2s->clk_aic);
+ if (ret)
+ return ret;
+
+ regmap_write(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
+
+ regmap_write(i2s->regmap, JZ_REG_AIC_CONF,
+ JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
+ JZ_AIC_CONF_I2S | JZ_AIC_CONF_INTERNAL_CODEC);
+
+ regmap_field_write(i2s->field_rx_fifo_thresh, 7);
+ regmap_field_write(i2s->field_tx_fifo_thresh, 8);
+
+ return 0;
+}
+
+static void jz4740_i2s_remove(struct snd_soc_component *component)
+{
+ struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(i2s->clk_aic);
+}
+
static const struct snd_soc_component_driver jz4740_i2s_component = {
- .name = "jz4740-i2s",
+ .name = "jz4740-i2s",
+ .probe = jz4740_i2s_probe,
+ .remove = jz4740_i2s_remove,
+ .suspend = jz4740_i2s_suspend,
+ .resume = jz4740_i2s_resume,
+ .legacy_dai_naming = 1,
};
-#ifdef CONFIG_OF
static const struct of_device_id jz4740_of_matches[] = {
- { .compatible = "ingenic,jz4740-i2s", .data = (void *)JZ_I2S_JZ4740 },
- { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
+ { .compatible = "ingenic,jz4740-i2s", .data = &jz4740_i2s_soc_info },
+ { .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info },
+ { .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info },
+ { .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info },
+ { .compatible = "ingenic,x1000-i2s", .data = &x1000_i2s_soc_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, jz4740_of_matches);
-#endif
+
+static int jz4740_i2s_init_regmap_fields(struct device *dev,
+ struct jz4740_i2s *i2s)
+{
+ i2s->field_rx_fifo_thresh =
+ devm_regmap_field_alloc(dev, i2s->regmap,
+ i2s->soc_info->field_rx_fifo_thresh);
+ if (IS_ERR(i2s->field_rx_fifo_thresh))
+ return PTR_ERR(i2s->field_rx_fifo_thresh);
+
+ i2s->field_tx_fifo_thresh =
+ devm_regmap_field_alloc(dev, i2s->regmap,
+ i2s->soc_info->field_tx_fifo_thresh);
+ if (IS_ERR(i2s->field_tx_fifo_thresh))
+ return PTR_ERR(i2s->field_tx_fifo_thresh);
+
+ i2s->field_i2sdiv_capture =
+ devm_regmap_field_alloc(dev, i2s->regmap,
+ i2s->soc_info->field_i2sdiv_capture);
+ if (IS_ERR(i2s->field_i2sdiv_capture))
+ return PTR_ERR(i2s->field_i2sdiv_capture);
+
+ i2s->field_i2sdiv_playback =
+ devm_regmap_field_alloc(dev, i2s->regmap,
+ i2s->soc_info->field_i2sdiv_playback);
+ if (IS_ERR(i2s->field_i2sdiv_playback))
+ return PTR_ERR(i2s->field_i2sdiv_playback);
+
+ return 0;
+}
+
+static const struct regmap_config jz4740_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = JZ_REG_AIC_FIFO,
+};
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct jz4740_i2s *i2s;
struct resource *mem;
+ void __iomem *regs;
int ret;
- const struct of_device_id *match;
- i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
- match = of_match_device(jz4740_of_matches, &pdev->dev);
- if (match)
- i2s->version = (enum jz47xx_i2s_version)match->data;
+ i2s->soc_info = device_get_match_data(dev);
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2s->base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(i2s->base))
- return PTR_ERR(i2s->base);
+ i2s->playback_dma_data.maxburst = 16;
+ i2s->playback_dma_data.addr = mem->start + JZ_REG_AIC_FIFO;
- i2s->phys_base = mem->start;
+ i2s->capture_dma_data.maxburst = 16;
+ i2s->capture_dma_data.addr = mem->start + JZ_REG_AIC_FIFO;
- i2s->clk_aic = devm_clk_get(&pdev->dev, "aic");
+ i2s->clk_aic = devm_clk_get(dev, "aic");
if (IS_ERR(i2s->clk_aic))
return PTR_ERR(i2s->clk_aic);
- i2s->clk_i2s = devm_clk_get(&pdev->dev, "i2s");
+ i2s->clk_i2s = devm_clk_get(dev, "i2s");
if (IS_ERR(i2s->clk_i2s))
return PTR_ERR(i2s->clk_i2s);
- platform_set_drvdata(pdev, i2s);
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &jz4740_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap))
+ return PTR_ERR(i2s->regmap);
- if (i2s->version == JZ_I2S_JZ4780)
- ret = devm_snd_soc_register_component(&pdev->dev,
- &jz4740_i2s_component, &jz4780_i2s_dai, 1);
- else
- ret = devm_snd_soc_register_component(&pdev->dev,
- &jz4740_i2s_component, &jz4740_i2s_dai, 1);
+ ret = jz4740_i2s_init_regmap_fields(dev, i2s);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, i2s);
+ ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component,
+ i2s->soc_info->dai, 1);
if (ret)
return ret;
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ return devm_snd_dmaengine_pcm_register(dev, NULL,
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
@@ -538,7 +591,7 @@ static struct platform_driver jz4740_i2s_driver = {
.probe = jz4740_i2s_dev_probe,
.driver = {
.name = "jz4740-i2s",
- .of_match_table = of_match_ptr(jz4740_of_matches)
+ .of_match_table = jz4740_of_matches,
},
};
diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
deleted file mode 100644
index 5e49339d8b93..000000000000
--- a/sound/soc/jz4740/jz4740-i2s.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * This program is free software; you can 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 _JZ4740_I2S_H
-#define _JZ4740_I2S_H
-
-/* I2S clock source */
-#define JZ4740_I2S_CLKSRC_EXT 0
-#define JZ4740_I2S_CLKSRC_PLL 1
-
-#define JZ4740_I2S_BIT_CLK 0
-
-#endif
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
deleted file mode 100644
index 53586999fcaa..000000000000
--- a/sound/soc/jz4740/qi_lb60.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2009, 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/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <linux/gpio/consumer.h>
-
-struct qi_lb60 {
- struct gpio_desc *snd_gpio;
- struct gpio_desc *amp_gpio;
-};
-
-static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *ctrl, int event)
-{
- struct qi_lb60 *qi_lb60 = snd_soc_card_get_drvdata(widget->dapm->card);
- int on = !SND_SOC_DAPM_EVENT_OFF(event);
-
- gpiod_set_value_cansleep(qi_lb60->snd_gpio, on);
- gpiod_set_value_cansleep(qi_lb60->amp_gpio, on);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget qi_lb60_widgets[] = {
- SND_SOC_DAPM_SPK("Speaker", qi_lb60_spk_event),
- SND_SOC_DAPM_MIC("Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route qi_lb60_routes[] = {
- {"Mic", NULL, "MIC"},
- {"Speaker", NULL, "LOUT"},
- {"Speaker", NULL, "ROUT"},
-};
-
-static struct snd_soc_dai_link qi_lb60_dai = {
- .name = "jz4740",
- .stream_name = "jz4740",
- .cpu_dai_name = "jz4740-i2s",
- .platform_name = "jz4740-i2s",
- .codec_dai_name = "jz4740-hifi",
- .codec_name = "jz4740-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-static struct snd_soc_card qi_lb60_card = {
- .name = "QI LB60",
- .owner = THIS_MODULE,
- .dai_link = &qi_lb60_dai,
- .num_links = 1,
-
- .dapm_widgets = qi_lb60_widgets,
- .num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
- .dapm_routes = qi_lb60_routes,
- .num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
- .fully_routed = true,
-};
-
-static int qi_lb60_probe(struct platform_device *pdev)
-{
- struct qi_lb60 *qi_lb60;
- struct snd_soc_card *card = &qi_lb60_card;
-
- qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
- if (!qi_lb60)
- return -ENOMEM;
-
- qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW);
- if (IS_ERR(qi_lb60->snd_gpio))
- return PTR_ERR(qi_lb60->snd_gpio);
-
- qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW);
- if (IS_ERR(qi_lb60->amp_gpio))
- return PTR_ERR(qi_lb60->amp_gpio);
-
- card->dev = &pdev->dev;
-
- snd_soc_card_set_drvdata(card, qi_lb60);
-
- return devm_snd_soc_register_card(&pdev->dev, card);
-}
-
-static struct platform_driver qi_lb60_driver = {
- .driver = {
- .name = "qi-lb60-audio",
- },
- .probe = qi_lb60_probe,
-};
-
-module_platform_driver(qi_lb60_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:qi-lb60-audio");
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index 132bb83f8e99..924072e402c8 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,3 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Kirkwood"
+
config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
@@ -15,3 +18,4 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB
Say Y if you want to add support for SoC audio on
the Armada 370 Development Board.
+endmenu
diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile
index c36b03d8006c..9be1eb8203a1 100644
--- a/sound/soc/kirkwood/Makefile
+++ b/sound/soc/kirkwood/Makefile
@@ -1,7 +1,8 @@
-snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-kirkwood-y := kirkwood-dma.o kirkwood-i2s.o
obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
-snd-soc-armada-370-db-objs := armada-370-db.o
+snd-soc-armada-370-db-y := armada-370-db.o
obj-$(CONFIG_SND_KIRKWOOD_SOC_ARMADA370_DB) += snd-soc-armada-370-db.o
diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c
index e0304d544f26..77a5f440364e 100644
--- a/sound/soc/kirkwood/armada-370-db.c
+++ b/sound/soc/kirkwood/armada-370-db.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.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>
@@ -22,8 +18,8 @@
static int a370db_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int freq;
switch (params_rate(params)) {
@@ -42,7 +38,7 @@ static int a370db_hw_params(struct snd_pcm_substream *substream,
return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
}
-static struct snd_soc_ops a370db_ops = {
+static const struct snd_soc_ops a370db_ops = {
.hw_params = a370db_hw_params,
};
@@ -58,28 +54,40 @@ static const struct snd_soc_dapm_route a370db_route[] = {
{ "AIN1L", NULL, "In Jack" },
};
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_CPU("i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42l51-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(spdif_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("spdif")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "dit-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(spdif_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("spdif")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "dir-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
static struct snd_soc_dai_link a370db_dai[] = {
{
.name = "CS42L51",
.stream_name = "analog",
- .cpu_dai_name = "i2s",
- .codec_dai_name = "cs42l51-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC,
.ops = &a370db_ops,
+ SND_SOC_DAILINK_REG(analog),
},
{
.name = "S/PDIF out",
.stream_name = "spdif-out",
- .cpu_dai_name = "spdif",
- .codec_dai_name = "dit-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(spdif_out),
},
{
.name = "S/PDIF in",
.stream_name = "spdif-in",
- .cpu_dai_name = "spdif",
- .codec_dai_name = "dir-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(spdif_in),
},
};
@@ -100,33 +108,33 @@ static int a370db_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- a370db_dai[0].cpu_of_node =
+ a370db_dai[0].cpus->of_node =
of_parse_phandle(pdev->dev.of_node,
"marvell,audio-controller", 0);
- a370db_dai[0].platform_of_node = a370db_dai[0].cpu_of_node;
+ a370db_dai[0].platforms->of_node = a370db_dai[0].cpus->of_node;
- a370db_dai[0].codec_of_node =
+ a370db_dai[0].codecs->of_node =
of_parse_phandle(pdev->dev.of_node,
"marvell,audio-codec", 0);
- a370db_dai[1].cpu_of_node = a370db_dai[0].cpu_of_node;
- a370db_dai[1].platform_of_node = a370db_dai[0].cpu_of_node;
+ a370db_dai[1].cpus->of_node = a370db_dai[0].cpus->of_node;
+ a370db_dai[1].platforms->of_node = a370db_dai[0].cpus->of_node;
- a370db_dai[1].codec_of_node =
+ a370db_dai[1].codecs->of_node =
of_parse_phandle(pdev->dev.of_node,
"marvell,audio-codec", 1);
- a370db_dai[2].cpu_of_node = a370db_dai[0].cpu_of_node;
- a370db_dai[2].platform_of_node = a370db_dai[0].cpu_of_node;
+ a370db_dai[2].cpus->of_node = a370db_dai[0].cpus->of_node;
+ a370db_dai[2].platforms->of_node = a370db_dai[0].cpus->of_node;
- a370db_dai[2].codec_of_node =
+ a370db_dai[2].codecs->of_node =
of_parse_phandle(pdev->dev.of_node,
"marvell,audio-codec", 2);
return devm_snd_soc_register_card(card->dev, card);
}
-static const struct of_device_id a370db_dt_ids[] = {
+static const struct of_device_id a370db_dt_ids[] __maybe_unused = {
{ .compatible = "marvell,a370db-audio" },
{ },
};
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index dbfdfe99c69d..036b42058272 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* kirkwood-dma.c
*
* (c) 2010 Arnaud Patard <apatard@mandriva.com>
* (c) 2010 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/init.h>
@@ -23,11 +19,11 @@
static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
{
- struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
- return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai);
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(subs);
+ return snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(soc_runtime, 0));
}
-static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
+static const struct snd_pcm_hardware kirkwood_dma_snd_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -90,7 +86,7 @@ kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
/* try to find matching cs for current dma address */
for (i = 0; i < dram->num_cs; i++) {
- const struct mbus_dram_window *cs = dram->cs + i;
+ const struct mbus_dram_window *cs = &dram->cs[i];
if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
writel(cs->base & 0xffff0000,
base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
@@ -102,13 +98,12 @@ kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
}
}
-static int kirkwood_dma_open(struct snd_pcm_substream *substream)
+static int kirkwood_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
int err;
struct snd_pcm_runtime *runtime = substream->runtime;
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
- const struct mbus_dram_target_info *dram;
- unsigned long addr;
snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
@@ -136,7 +131,7 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
"kirkwood-i2s", priv);
if (err)
- return -EBUSY;
+ return err;
/*
* Enable Error interrupts. We're only ack'ing them but
@@ -145,26 +140,21 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
writel((unsigned int)-1, priv->io + KIRKWOOD_ERR_MASK);
}
- dram = mv_mbus_dram_info();
- addr = substream->dma_buffer.addr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (priv->substream_play)
return -EBUSY;
priv->substream_play = substream;
- kirkwood_dma_conf_mbus_windows(priv->io,
- KIRKWOOD_PLAYBACK_WIN, addr, dram);
} else {
if (priv->substream_rec)
return -EBUSY;
priv->substream_rec = substream;
- kirkwood_dma_conf_mbus_windows(priv->io,
- KIRKWOOD_RECORD_WIN, addr, dram);
}
return 0;
}
-static int kirkwood_dma_close(struct snd_pcm_substream *substream)
+static int kirkwood_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
@@ -184,24 +174,28 @@ static int kirkwood_dma_close(struct snd_pcm_substream *substream)
return 0;
}
-static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int kirkwood_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
+ struct kirkwood_dma_data *priv = kirkwood_priv(substream);
+ const struct mbus_dram_target_info *dram = mv_mbus_dram_info();
+ unsigned long addr = substream->runtime->dma_addr;
- return 0;
-}
+ if (!dram)
+ return 0;
-static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_set_runtime_buffer(substream, NULL);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ kirkwood_dma_conf_mbus_windows(priv->io,
+ KIRKWOOD_PLAYBACK_WIN, addr, dram);
+ else
+ kirkwood_dma_conf_mbus_windows(priv->io,
+ KIRKWOOD_RECORD_WIN, addr, dram);
return 0;
}
-static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
+static int kirkwood_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
@@ -226,8 +220,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
- *substream)
+static snd_pcm_uframes_t kirkwood_dma_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct kirkwood_dma_data *priv = kirkwood_priv(substream);
snd_pcm_uframes_t count;
@@ -242,84 +237,29 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
return count;
}
-static struct snd_pcm_ops kirkwood_dma_ops = {
- .open = kirkwood_dma_open,
- .close = kirkwood_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = kirkwood_dma_hw_params,
- .hw_free = kirkwood_dma_hw_free,
- .prepare = kirkwood_dma_prepare,
- .pointer = kirkwood_dma_pointer,
-};
-
-static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
- int stream)
+static int kirkwood_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->area = dma_alloc_coherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- buf->private_data = NULL;
-
- return 0;
-}
-
-static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
-{
struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = kirkwood_dma_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- return ret;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = kirkwood_dma_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- return ret;
- }
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
return 0;
}
-static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_coherent(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-struct snd_soc_platform_driver kirkwood_soc_platform = {
- .ops = &kirkwood_dma_ops,
- .pcm_new = kirkwood_dma_new,
- .pcm_free = kirkwood_dma_free_dma_buffers,
+const struct snd_soc_component_driver kirkwood_soc_component = {
+ .name = DRV_NAME,
+ .open = kirkwood_dma_open,
+ .close = kirkwood_dma_close,
+ .hw_params = kirkwood_dma_hw_params,
+ .prepare = kirkwood_dma_prepare,
+ .pointer = kirkwood_dma_pointer,
+ .pcm_construct = kirkwood_dma_new,
};
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 3a36d60e1785..99bd066c7309 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* kirkwood-i2s.c
*
* (c) 2010 Arnaud Patard <apatard@mandriva.com>
* (c) 2010 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/init.h>
@@ -26,8 +22,6 @@
#include "kirkwood.h"
-#define DRV_NAME "mvebu-audio"
-
#define KIRKWOOD_I2S_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
@@ -37,6 +31,122 @@
(SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+/* These registers are relative to the second register region -
+ * audio pll configuration.
+ */
+#define A38X_PLL_CONF_REG0 0x0
+#define A38X_PLL_FB_CLK_DIV_OFFSET 10
+#define A38X_PLL_FB_CLK_DIV_MASK 0x7fc00
+#define A38X_PLL_CONF_REG1 0x4
+#define A38X_PLL_FREQ_OFFSET_MASK 0xffff
+#define A38X_PLL_FREQ_OFFSET_VALID BIT(16)
+#define A38X_PLL_SW_RESET BIT(31)
+#define A38X_PLL_CONF_REG2 0x8
+#define A38X_PLL_AUDIO_POSTDIV_MASK 0x7f
+
+/* Bit below belongs to SoC control register corresponding to the third
+ * register region.
+ */
+#define A38X_SPDIF_MODE_ENABLE BIT(27)
+
+static int armada_38x_i2s_init_quirk(struct platform_device *pdev,
+ struct kirkwood_dma_data *priv,
+ struct snd_soc_dai_driver *dai_drv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 reg_val;
+ int i;
+
+ priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs");
+ if (IS_ERR(priv->pll_config))
+ return -ENOMEM;
+
+ priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl");
+ if (IS_ERR(priv->soc_control))
+ return -ENOMEM;
+
+ /* Select one of exceptive modes: I2S or S/PDIF */
+ reg_val = readl(priv->soc_control);
+ if (of_property_read_bool(np, "spdif-mode")) {
+ reg_val |= A38X_SPDIF_MODE_ENABLE;
+ dev_info(&pdev->dev, "using S/PDIF mode\n");
+ } else {
+ reg_val &= ~A38X_SPDIF_MODE_ENABLE;
+ dev_info(&pdev->dev, "using I2S mode\n");
+ }
+ writel(reg_val, priv->soc_control);
+
+ /* Update available rates of mclk's fs */
+ for (i = 0; i < 2; i++) {
+ dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000;
+ dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000;
+ }
+
+ return 0;
+}
+
+static inline void armada_38x_set_pll(void __iomem *base, unsigned long rate)
+{
+ u32 reg_val;
+ u16 freq_offset = 0x22b0;
+ u8 audio_postdiv, fb_clk_div = 0x1d;
+
+ /* Set frequency offset value to not valid and enable PLL reset */
+ reg_val = readl(base + A38X_PLL_CONF_REG1);
+ reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID;
+ reg_val &= ~A38X_PLL_SW_RESET;
+ writel(reg_val, base + A38X_PLL_CONF_REG1);
+
+ udelay(1);
+
+ /* Update PLL parameters */
+ switch (rate) {
+ default:
+ case 44100:
+ freq_offset = 0x735;
+ fb_clk_div = 0x1b;
+ audio_postdiv = 0xc;
+ break;
+ case 48000:
+ audio_postdiv = 0xc;
+ break;
+ case 96000:
+ audio_postdiv = 0x6;
+ break;
+ case 192000:
+ audio_postdiv = 0x3;
+ break;
+ }
+
+ reg_val = readl(base + A38X_PLL_CONF_REG0);
+ reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK;
+ reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET);
+ writel(reg_val, base + A38X_PLL_CONF_REG0);
+
+ reg_val = readl(base + A38X_PLL_CONF_REG2);
+ reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK;
+ reg_val |= audio_postdiv;
+ writel(reg_val, base + A38X_PLL_CONF_REG2);
+
+ reg_val = readl(base + A38X_PLL_CONF_REG1);
+ reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK;
+ reg_val |= freq_offset;
+ writel(reg_val, base + A38X_PLL_CONF_REG1);
+
+ udelay(1);
+
+ /* Disable reset */
+ reg_val |= A38X_PLL_SW_RESET;
+ writel(reg_val, base + A38X_PLL_CONF_REG1);
+
+ /* Wait 50us for PLL to lock */
+ udelay(50);
+
+ /* Restore frequency offset value validity */
+ reg_val |= A38X_PLL_FREQ_OFFSET_VALID;
+ writel(reg_val, base + A38X_PLL_CONF_REG1);
+}
+
static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
@@ -112,7 +222,10 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai,
* defined in kirkwood_i2s_dai */
dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
__func__, rate);
- kirkwood_set_dco(priv->io, rate);
+ if (priv->pll_config)
+ armada_38x_set_pll(priv->pll_config, rate);
+ else
+ kirkwood_set_dco(priv->io, rate);
clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
} else {
@@ -524,35 +637,37 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
},
};
-static const struct snd_soc_component_driver kirkwood_i2s_component = {
- .name = DRV_NAME,
-};
-
static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
{
struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
struct kirkwood_dma_data *priv;
- struct resource *mem;
struct device_node *np = pdev->dev.of_node;
int err;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "allocation failed\n");
+ if (!priv)
return -ENOMEM;
- }
+
dev_set_drvdata(&pdev->dev, priv);
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->io = devm_ioremap_resource(&pdev->dev, mem);
+ if (of_device_is_compatible(np, "marvell,armada-380-audio"))
+ priv->io = devm_platform_ioremap_resource_byname(pdev, "i2s_regs");
+ else
+ priv->io = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->io))
return PTR_ERR(priv->io);
priv->irq = platform_get_irq(pdev, 0);
- if (priv->irq <= 0) {
- dev_err(&pdev->dev, "platform_get_irq failed\n");
- return -ENXIO;
+ if (priv->irq < 0)
+ return priv->irq;
+
+ if (of_device_is_compatible(np, "marvell,armada-380-audio")) {
+ err = armada_38x_i2s_init_quirk(pdev, priv, soc_dai);
+ if (err < 0)
+ return err;
+ /* Set initial pll frequency */
+ armada_38x_set_pll(priv->pll_config, 44100);
}
if (np) {
@@ -570,10 +685,6 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
- err = clk_prepare_enable(priv->clk);
- if (err < 0)
- return err;
-
priv->extclk = devm_clk_get(&pdev->dev, "extclk");
if (IS_ERR(priv->extclk)) {
if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
@@ -589,6 +700,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
}
}
+ err = clk_prepare_enable(priv->clk);
+ if (err < 0)
+ return err;
+
/* Some sensible defaults - this reflects the powerup values */
priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
@@ -602,24 +717,17 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
}
- err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component,
+ err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
soc_dai, 2);
if (err) {
dev_err(&pdev->dev, "snd_soc_register_component failed\n");
goto err_component;
}
- err = snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
- if (err) {
- dev_err(&pdev->dev, "snd_soc_register_platform failed\n");
- goto err_platform;
- }
-
kirkwood_i2s_init(priv);
return 0;
- err_platform:
- snd_soc_unregister_component(&pdev->dev);
+
err_component:
if (!IS_ERR(priv->extclk))
clk_disable_unprepare(priv->extclk);
@@ -628,18 +736,14 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
return err;
}
-static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
+static void kirkwood_i2s_dev_remove(struct platform_device *pdev)
{
struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
-
if (!IS_ERR(priv->extclk))
clk_disable_unprepare(priv->extclk);
clk_disable_unprepare(priv->clk);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -647,6 +751,7 @@ static const struct of_device_id mvebu_audio_of_match[] = {
{ .compatible = "marvell,kirkwood-audio" },
{ .compatible = "marvell,dove-audio" },
{ .compatible = "marvell,armada370-audio" },
+ { .compatible = "marvell,armada-380-audio" },
{ }
};
MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
index 90e32a781424..79bb9aa7f086 100644
--- a/sound/soc/kirkwood/kirkwood.h
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -1,17 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* kirkwood.h
*
* (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.
*/
#ifndef _KIRKWOOD_AUDIO_H
#define _KIRKWOOD_AUDIO_H
+#define DRV_NAME "mvebu-audio"
+
#define KIRKWOOD_RECORD_WIN 0
#define KIRKWOOD_PLAYBACK_WIN 1
#define KIRKWOOD_MAX_AUDIO_WIN 2
@@ -133,6 +131,8 @@
struct kirkwood_dma_data {
void __iomem *io;
+ void __iomem *pll_config;
+ void __iomem *soc_control;
struct clk *clk;
struct clk *extclk;
uint32_t ctl_play;
@@ -143,6 +143,6 @@ struct kirkwood_dma_data {
int burst;
};
-extern struct snd_soc_platform_driver kirkwood_soc_platform;
+extern const struct snd_soc_component_driver kirkwood_soc_component;
#endif
diff --git a/sound/soc/loongson/Kconfig b/sound/soc/loongson/Kconfig
new file mode 100644
index 000000000000..2e06670e4d7e
--- /dev/null
+++ b/sound/soc/loongson/Kconfig
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0
+menu "Loongson"
+
+config SND_SOC_LOONGSON_CARD
+ tristate "Loongson Sound Card Driver"
+ depends on LOONGARCH || COMPILE_TEST
+ select SND_SOC_LOONGSON_I2S_PCI if PCI
+ select SND_SOC_LOONGSON_I2S_PLATFORM if OF
+ help
+ Say Y or M if you want to add support for SoC audio using
+ loongson I2S controller.
+
+ The driver add support for ALSA SoC Audio support using
+ loongson I2S controller.
+
+config SND_SOC_LOONGSON_I2S_PCI
+ tristate "Loongson I2S-PCI Device Driver"
+ depends on LOONGARCH || COMPILE_TEST
+ depends on PCI
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for I2S driver for
+ Loongson I2S controller.
+
+ The controller is found in loongson bridge chips or SoCs,
+ and work as a PCI device.
+
+config SND_SOC_LOONGSON_I2S_PLATFORM
+ tristate "Loongson I2S-PLAT Device Driver"
+ depends on LOONGARCH || COMPILE_TEST
+ select REGMAP_MMIO
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for I2S driver for
+ Loongson I2S controller.
+
+ The controller work as a platform device, we can found it in
+ Loongson-2K1000 SoCs.
+endmenu
+
+config SND_LOONGSON1_AC97
+ tristate "Loongson1 AC97 Support"
+ depends on LOONGSON1_APB_DMA
+ select SND_SOC_AC97_CODEC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Loongson1 AC97 controller.
diff --git a/sound/soc/loongson/Makefile b/sound/soc/loongson/Makefile
new file mode 100644
index 000000000000..4c6d3130bcee
--- /dev/null
+++ b/sound/soc/loongson/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+#Platform Support
+snd-soc-loongson-i2s-pci-y := loongson_i2s_pci.o loongson_dma.o
+obj-$(CONFIG_SND_SOC_LOONGSON_I2S_PCI) += snd-soc-loongson-i2s-pci.o snd-soc-loongson-i2s.o
+
+snd-soc-loongson-i2s-plat-y := loongson_i2s_plat.o
+obj-$(CONFIG_SND_SOC_LOONGSON_I2S_PLATFORM) += snd-soc-loongson-i2s-plat.o snd-soc-loongson-i2s.o
+
+snd-soc-loongson-i2s-y := loongson_i2s.o
+
+obj-$(CONFIG_SND_LOONGSON1_AC97) += loongson1_ac97.o
+
+#Machine Support
+snd-soc-loongson-card-y := loongson_card.o
+obj-$(CONFIG_SND_SOC_LOONGSON_CARD) += snd-soc-loongson-card.o
diff --git a/sound/soc/loongson/loongson1_ac97.c b/sound/soc/loongson/loongson1_ac97.c
new file mode 100644
index 000000000000..84901900ad43
--- /dev/null
+++ b/sound/soc/loongson/loongson1_ac97.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AC97 Controller Driver for Loongson-1 SoC
+ *
+ * Copyright (C) 2025 Keguang Zhang <keguang.zhang@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/* Loongson-1 AC97 Controller Registers */
+#define AC97_CSR 0x0
+#define AC97_OCC0 0x4
+#define AC97_ICC 0x10
+#define AC97_CRAC 0x18
+#define AC97_INTRAW 0x54
+#define AC97_INTM 0x58
+#define AC97_INT_CW_CLR 0x68
+#define AC97_INT_CR_CLR 0x6c
+
+/* Control Status Register Bits (CSR) */
+#define CSR_RESUME BIT(1)
+#define CSR_RST_FORCE BIT(0)
+
+/* MIC Channel Configuration Bits */
+#define M_DMA_EN BIT(22)
+#define M_FIFO_THRES GENMASK(21, 20)
+#define M_FIFO_THRES_FULL FIELD_PREP(M_FIFO_THRES, 3)
+#define M_FIFO_THRES_HALF FIELD_PREP(M_FIFO_THRES, 1)
+#define M_FIFO_THRES_QUARTER FIELD_PREP(M_FIFO_THRES, 0)
+#define M_SW GENMASK(19, 18)
+#define M_SW_16_BITS FIELD_PREP(M_SW, 2)
+#define M_SW_8_BITS FIELD_PREP(M_SW, 0)
+#define M_VSR BIT(17)
+#define M_CH_EN BIT(16)
+/* Right Channel Configuration Bits */
+#define R_DMA_EN BIT(14)
+#define R_FIFO_THRES GENMASK(13, 12)
+#define R_FIFO_THRES_EMPTY FIELD_PREP(R_FIFO_THRES, 3)
+#define R_FIFO_THRES_HALF FIELD_PREP(R_FIFO_THRES, 1)
+#define R_FIFO_THRES_QUARTER FIELD_PREP(R_FIFO_THRES, 0)
+#define R_SW GENMASK(11, 10)
+#define R_SW_16_BITS FIELD_PREP(R_SW, 2)
+#define R_SW_8_BITS FIELD_PREP(R_SW, 0)
+#define R_VSR BIT(9)
+#define R_CH_EN BIT(8)
+/* Left Channel Configuration Bits */
+#define L_DMA_EN BIT(6)
+#define L_FIFO_THRES GENMASK(5, 4)
+#define L_FIFO_THRES_EMPTY FIELD_PREP(L_FIFO_THRES, 3)
+#define L_FIFO_THRES_HALF FIELD_PREP(L_FIFO_THRES, 1)
+#define L_FIFO_THRES_QUARTER FIELD_PREP(L_FIFO_THRES, 0)
+#define L_SW GENMASK(3, 2)
+#define L_SW_16_BITS FIELD_PREP(L_SW, 2)
+#define L_SW_8_BITS FIELD_PREP(L_SW, 0)
+#define L_VSR BIT(1)
+#define L_CH_EN BIT(0)
+
+/* Codec Register Access Command Bits (CRAC) */
+#define CODEC_WR BIT(31)
+#define CODEC_ADR GENMASK(22, 16)
+#define CODEC_DAT GENMASK(15, 0)
+
+/* Interrupt Register (INTRAW) */
+#define CW_DONE BIT(1)
+#define CR_DONE BIT(0)
+
+#define LS1X_AC97_DMA_TX_EN BIT(31)
+#define LS1X_AC97_DMA_STEREO BIT(30)
+#define LS1X_AC97_DMA_TX_BYTES GENMASK(29, 28)
+#define LS1X_AC97_DMA_TX_4_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 2)
+#define LS1X_AC97_DMA_TX_2_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 1)
+#define LS1X_AC97_DMA_TX_1_BYTE FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 0)
+#define LS1X_AC97_DMA_DADDR_MASK GENMASK(27, 0)
+
+#define LS1X_AC97_DMA_FIFO_SIZE 128
+
+#define LS1X_AC97_TIMEOUT 3000
+
+struct ls1x_ac97 {
+ void __iomem *reg_base;
+ struct regmap *regmap;
+ dma_addr_t tx_dma_base;
+ dma_addr_t rx_dma_base;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+};
+
+static struct ls1x_ac97 *ls1x_ac97;
+
+static const struct regmap_config ls1x_ac97_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static void ls1x_ac97_reset(struct snd_ac97 *ac97)
+{
+ int val;
+
+ regmap_write(ls1x_ac97->regmap, AC97_CSR, CSR_RST_FORCE);
+ regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
+ !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
+}
+
+static void ls1x_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
+{
+ int tmp, ret;
+
+ tmp = FIELD_PREP(CODEC_ADR, reg) | FIELD_PREP(CODEC_DAT, val);
+ regmap_write(ls1x_ac97->regmap, AC97_CRAC, tmp);
+ ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, tmp,
+ (tmp & CW_DONE), 0, LS1X_AC97_TIMEOUT);
+ if (ret)
+ pr_err("timeout on AC97 write! %d\n", ret);
+
+ regmap_read(ls1x_ac97->regmap, AC97_INT_CW_CLR, &ret);
+}
+
+static unsigned short ls1x_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ int val, ret;
+
+ val = CODEC_WR | FIELD_PREP(CODEC_ADR, reg);
+ regmap_write(ls1x_ac97->regmap, AC97_CRAC, val);
+ ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, val,
+ (val & CR_DONE), 0, LS1X_AC97_TIMEOUT);
+ if (ret) {
+ pr_err("timeout on AC97 read! %d\n", ret);
+ return ret;
+ }
+
+ regmap_read(ls1x_ac97->regmap, AC97_INT_CR_CLR, &ret);
+ regmap_read(ls1x_ac97->regmap, AC97_CRAC, &ret);
+
+ return (ret & CODEC_DAT);
+}
+
+static void ls1x_ac97_init(struct snd_ac97 *ac97)
+{
+ writel(0, ls1x_ac97->reg_base + AC97_INTRAW);
+ writel(0, ls1x_ac97->reg_base + AC97_INTM);
+
+ /* Config output channels */
+ regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0,
+ R_DMA_EN | R_FIFO_THRES | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES | L_CH_EN,
+ R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
+
+ /* Config inputs channel */
+ regmap_update_bits(ls1x_ac97->regmap, AC97_ICC,
+ M_DMA_EN | M_FIFO_THRES | M_CH_EN |
+ R_DMA_EN | R_FIFO_THRES | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES | L_CH_EN,
+ M_DMA_EN | M_FIFO_THRES_FULL | M_CH_EN |
+ R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
+
+ if (ac97->ext_id & AC97_EI_VRA) {
+ regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, R_VSR | L_VSR, R_VSR | L_VSR);
+ regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, M_VSR, M_VSR);
+ }
+}
+
+static struct snd_ac97_bus_ops ls1x_ac97_ops = {
+ .reset = ls1x_ac97_reset,
+ .write = ls1x_ac97_write,
+ .read = ls1x_ac97_read,
+ .init = ls1x_ac97_init,
+};
+
+static int ls1x_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
+ struct snd_dmaengine_dai_dma_data *dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (params_channels(params)) {
+ case 1:
+ dma_data->addr &= ~LS1X_AC97_DMA_STEREO;
+ break;
+ case 2:
+ dma_data->addr |= LS1X_AC97_DMA_STEREO;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "unsupported channels! %d\n", params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(ac97->regmap, AC97_OCC0,
+ R_SW | L_SW,
+ R_SW_8_BITS | L_SW_8_BITS);
+ else
+ regmap_update_bits(ac97->regmap, AC97_ICC,
+ M_SW | R_SW | L_SW,
+ M_SW_8_BITS | R_SW_8_BITS | L_SW_8_BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(ac97->regmap, AC97_OCC0,
+ R_SW | L_SW,
+ R_SW_16_BITS | L_SW_16_BITS);
+ else
+ regmap_update_bits(ac97->regmap, AC97_ICC,
+ M_SW | R_SW | L_SW,
+ M_SW_16_BITS | R_SW_16_BITS | L_SW_16_BITS);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "unsupported format! %d\n", params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ls1x_ac97_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
+
+ ac97->capture_dma_data.addr = ac97->rx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
+ ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ac97->capture_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
+
+ ac97->playback_dma_data.addr = ac97->tx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
+ ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_4_BYTES;
+ ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_EN;
+ ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ac97->playback_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
+
+ snd_soc_dai_init_dma_data(cpu_dai, &ac97->playback_dma_data, &ac97->capture_dma_data);
+ snd_soc_dai_set_drvdata(cpu_dai, ac97);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ls1x_ac97_dai_ops = {
+ .probe = ls1x_ac97_dai_probe,
+ .hw_params = ls1x_ac97_hw_params,
+};
+
+#define LS1X_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE)
+
+static struct snd_soc_dai_driver ls1x_ac97_dai[] = {
+ {
+ .name = "ls1x-ac97",
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = LS1X_AC97_FMTS,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = LS1X_AC97_FMTS,
+ },
+ .ops = &ls1x_ac97_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver ls1x_ac97_component = {
+ .name = KBUILD_MODNAME,
+ .legacy_dai_naming = 1,
+};
+
+static int ls1x_ac97_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ls1x_ac97 *ac97;
+ struct resource *res;
+ int ret;
+
+ ac97 = devm_kzalloc(dev, sizeof(struct ls1x_ac97), GFP_KERNEL);
+ if (!ac97)
+ return -ENOMEM;
+ ls1x_ac97 = ac97;
+ platform_set_drvdata(pdev, ac97);
+
+ ac97->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ac97->reg_base))
+ return PTR_ERR(ac97->reg_base);
+
+ ac97->regmap = devm_regmap_init_mmio(dev, ac97->reg_base, &ls1x_ac97_regmap_config);
+ if (IS_ERR(ac97->regmap))
+ return dev_err_probe(dev, PTR_ERR(ac97->regmap), "devm_regmap_init_mmio failed\n");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-tx");
+ if (!res)
+ return dev_err_probe(dev, -EINVAL, "Missing 'audio-tx' in reg-names property\n");
+
+ ac97->tx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
+ DMA_TO_DEVICE, 0);
+ if (dma_mapping_error(dev, ac97->tx_dma_base))
+ return -ENXIO;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-rx");
+ if (!res)
+ return dev_err_probe(dev, -EINVAL, "Missing 'audio-rx' in reg-names property\n");
+
+ ac97->rx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
+ DMA_FROM_DEVICE, 0);
+ if (dma_mapping_error(dev, ac97->rx_dma_base))
+ return -ENXIO;
+
+ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
+ if (ret)
+ dev_err_probe(dev, ret, "failed to register PCM\n");
+
+ ret = devm_snd_soc_register_component(dev, &ls1x_ac97_component,
+ ls1x_ac97_dai, ARRAY_SIZE(ls1x_ac97_dai));
+ if (ret)
+ dev_err_probe(dev, ret, "failed to register DAI\n");
+
+ return snd_soc_set_ac97_ops(&ls1x_ac97_ops);
+}
+
+static void ls1x_ac97_remove(struct platform_device *pdev)
+{
+ ls1x_ac97 = NULL;
+ snd_soc_set_ac97_ops(NULL);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ls1x_ac97_suspend(struct device *dev)
+{
+ int val;
+
+ regmap_clear_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_clear_bits(ls1x_ac97->regmap, AC97_ICC,
+ M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
+
+ return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
+ (val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
+}
+
+static int ls1x_ac97_resume(struct device *dev)
+{
+ int val;
+
+ regmap_set_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_set_bits(ls1x_ac97->regmap, AC97_ICC,
+ M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
+
+ return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
+ !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
+}
+#endif
+
+static const struct dev_pm_ops ls1x_ac97_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ls1x_ac97_suspend, ls1x_ac97_resume)
+};
+
+static const struct of_device_id ls1x_ac97_match[] = {
+ { .compatible = "loongson,ls1b-ac97" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ls1x_ac97_match);
+
+static struct platform_driver ls1x_ac97_driver = {
+ .probe = ls1x_ac97_probe,
+ .remove = ls1x_ac97_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ls1x_ac97_match,
+ .pm = &ls1x_ac97_pm_ops,
+ },
+};
+
+module_platform_driver(ls1x_ac97_driver);
+
+MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
+MODULE_DESCRIPTION("Loongson-1 AC97 Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c
new file mode 100644
index 000000000000..7910d5d9ac4f
--- /dev/null
+++ b/sound/soc/loongson/loongson_card.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Loongson ASoC Audio Machine driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <sound/pcm_params.h>
+
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+struct loongson_card_data {
+ struct snd_soc_card snd_card;
+ unsigned int mclk_fs;
+};
+
+static int loongson_card_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct loongson_card_data *ls_card = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret, mclk;
+
+ if (!ls_card->mclk_fs)
+ return 0;
+
+ mclk = ls_card->mclk_fs * params_rate(params);
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "cpu_dai clock not set\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops loongson_ops = {
+ .hw_params = loongson_card_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_CPU("loongson-i2s")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link loongson_dai_links[] = {
+ {
+ .name = "Loongson Audio Port",
+ .stream_name = "Loongson Audio",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(analog),
+ .ops = &loongson_ops,
+ },
+};
+
+static struct acpi_device *loongson_card_acpi_find_device(struct snd_soc_card *card,
+ const char *name)
+{
+ struct fwnode_handle *fwnode = card->dev->fwnode;
+ struct fwnode_reference_args args;
+ int status;
+
+ memset(&args, 0, sizeof(args));
+ status = acpi_node_get_property_reference(fwnode, name, 0, &args);
+ if (status || !is_acpi_device_node(args.fwnode)) {
+ dev_err(card->dev, "No matching phy in ACPI table\n");
+ return NULL;
+ }
+
+ return to_acpi_device_node(args.fwnode);
+}
+
+static int loongson_card_parse_acpi(struct loongson_card_data *data)
+{
+ struct snd_soc_card *card = &data->snd_card;
+ const char *codec_dai_name;
+ struct acpi_device *adev;
+ struct device *phy_dev;
+ int i;
+
+ /* fixup platform name based on reference node */
+ adev = loongson_card_acpi_find_device(card, "cpu");
+ if (!adev)
+ return -ENOENT;
+
+ phy_dev = acpi_get_first_physical_node(adev);
+ if (!phy_dev)
+ return -EPROBE_DEFER;
+
+ /* fixup codec name based on reference node */
+ adev = loongson_card_acpi_find_device(card, "codec");
+ if (!adev)
+ return -ENOENT;
+ snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev));
+
+ device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name);
+
+ for (i = 0; i < card->num_links; i++) {
+ loongson_dai_links[i].platforms->name = dev_name(phy_dev);
+ loongson_dai_links[i].codecs->name = codec_name;
+ loongson_dai_links[i].codecs->dai_name = codec_dai_name;
+ }
+
+ return 0;
+}
+
+static int loongson_card_parse_of(struct loongson_card_data *data)
+{
+ struct device_node *cpu, *codec;
+ struct snd_soc_card *card = &data->snd_card;
+ struct device *dev = card->dev;
+ int ret, i;
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ if (!cpu) {
+ dev_err(dev, "platform property missing or invalid\n");
+ return -EINVAL;
+ }
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ if (!codec) {
+ dev_err(dev, "audio-codec property missing or invalid\n");
+ of_node_put(cpu);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ ret = snd_soc_of_get_dlc(cpu, NULL, loongson_dai_links[i].cpus, 0);
+ if (ret < 0) {
+ dev_err(dev, "getting cpu dlc error (%d)\n", ret);
+ goto err;
+ }
+ loongson_dai_links[i].platforms->of_node = loongson_dai_links[i].cpus->of_node;
+
+ ret = snd_soc_of_get_dlc(codec, NULL, loongson_dai_links[i].codecs, 0);
+ if (ret < 0) {
+ dev_err(dev, "getting codec dlc error (%d)\n", ret);
+ goto err;
+ }
+ }
+
+ of_node_put(cpu);
+ of_node_put(codec);
+
+ return 0;
+
+err:
+ of_node_put(cpu);
+ of_node_put(codec);
+ return ret;
+}
+
+static int loongson_asoc_card_probe(struct platform_device *pdev)
+{
+ struct loongson_card_data *ls_priv;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ ls_priv = devm_kzalloc(dev, sizeof(*ls_priv), GFP_KERNEL);
+ if (!ls_priv)
+ return -ENOMEM;
+
+ card = &ls_priv->snd_card;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = loongson_dai_links;
+ card->num_links = ARRAY_SIZE(loongson_dai_links);
+ snd_soc_card_set_drvdata(card, ls_priv);
+
+ ret = device_property_read_string(dev, "model", &card->name);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error parsing card name\n");
+
+ ret = device_property_read_u32(dev, "mclk-fs", &ls_priv->mclk_fs);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error parsing mclk-fs\n");
+
+ ret = has_acpi_companion(dev) ? loongson_card_parse_acpi(ls_priv)
+ : loongson_card_parse_of(ls_priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error parsing acpi/of properties\n");
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id loongson_asoc_dt_ids[] = {
+ { .compatible = "loongson,ls-audio-card" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids);
+
+static struct platform_driver loongson_audio_driver = {
+ .probe = loongson_asoc_card_probe,
+ .driver = {
+ .name = "loongson-asoc-card",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = loongson_asoc_dt_ids,
+ },
+};
+module_platform_driver(loongson_audio_driver);
+
+MODULE_DESCRIPTION("Loongson ASoc Sound Card driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/loongson/loongson_dma.c b/sound/soc/loongson/loongson_dma.c
new file mode 100644
index 000000000000..20e4a0641340
--- /dev/null
+++ b/sound/soc/loongson/loongson_dma.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Loongson ALSA SoC Platform (DMA) driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "loongson_i2s.h"
+
+/* DMA dma_order Register */
+#define DMA_ORDER_STOP BIT(4) /* DMA stop */
+#define DMA_ORDER_START BIT(3) /* DMA start */
+#define DMA_ORDER_ASK_VALID BIT(2) /* DMA ask valid flag */
+#define DMA_ORDER_AXI_UNCO BIT(1) /* Uncache access */
+#define DMA_ORDER_ADDR_64 BIT(0) /* 64bits address support */
+
+#define DMA_ORDER_ASK_MASK (~0x1fUL) /* Ask addr mask */
+#define DMA_ORDER_CTRL_MASK (0x0fUL) /* Control mask */
+
+/*
+ * DMA registers descriptor.
+ */
+struct loongson_dma_desc {
+ u32 order; /* Next descriptor address register */
+ u32 saddr; /* Source address register */
+ u32 daddr; /* Device address register */
+ u32 length; /* Total length register */
+ u32 step_length; /* Memory stride register */
+ u32 step_times; /* Repeat time register */
+ u32 cmd; /* Command register */
+ u32 stats; /* Status register */
+ u32 order_hi; /* Next descriptor high address register */
+ u32 saddr_hi; /* High source address register */
+ u32 res[6]; /* Reserved */
+} __packed;
+
+struct loongson_runtime_data {
+ struct loongson_dma_data *dma_data;
+
+ struct loongson_dma_desc *dma_desc_arr;
+ dma_addr_t dma_desc_arr_phy;
+ int dma_desc_arr_size;
+
+ struct loongson_dma_desc *dma_pos_desc;
+ dma_addr_t dma_pos_desc_phy;
+};
+
+static const struct snd_pcm_hardware ls_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .period_bytes_min = 128,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE / sizeof(struct loongson_dma_desc),
+ .buffer_bytes_max = 1024 * 1024,
+};
+
+static struct
+loongson_dma_desc *dma_desc_save(struct loongson_runtime_data *prtd)
+{
+ void __iomem *order_reg = prtd->dma_data->order_addr;
+ u64 val;
+
+ val = (u64)prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK;
+ val |= (readq(order_reg) & DMA_ORDER_CTRL_MASK);
+ val |= DMA_ORDER_ASK_VALID;
+ writeq(val, order_reg);
+
+ while (readl(order_reg) & DMA_ORDER_ASK_VALID)
+ udelay(2);
+
+ return prtd->dma_pos_desc;
+}
+
+static int loongson_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct loongson_runtime_data *prtd = substream->runtime->private_data;
+ struct device *dev = substream->pcm->card->dev;
+ void __iomem *order_reg = prtd->dma_data->order_addr;
+ u64 val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK;
+ if (dev->coherent_dma_mask == DMA_BIT_MASK(64))
+ val |= DMA_ORDER_ADDR_64;
+ else
+ val &= ~DMA_ORDER_ADDR_64;
+ val |= (readq(order_reg) & DMA_ORDER_CTRL_MASK);
+ val |= DMA_ORDER_START;
+ writeq(val, order_reg);
+
+ while ((readl(order_reg) & DMA_ORDER_START))
+ udelay(2);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dma_desc_save(prtd);
+
+ /* dma stop */
+ val = readq(order_reg) | DMA_ORDER_STOP;
+ writeq(val, order_reg);
+ udelay(1000);
+
+ break;
+ default:
+ dev_err(dev, "Invalid pcm trigger operation\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int loongson_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = substream->pcm->card->dev;
+ struct loongson_runtime_data *prtd = runtime->private_data;
+ size_t buf_len = params_buffer_bytes(params);
+ size_t period_len = params_period_bytes(params);
+ dma_addr_t order_addr, mem_addr;
+ struct loongson_dma_desc *desc;
+ u32 num_periods;
+ int i;
+
+ if (buf_len % period_len) {
+ dev_err(dev, "buf len not multiply of period len\n");
+ return -EINVAL;
+ }
+
+ num_periods = buf_len / period_len;
+ if (!num_periods || num_periods > prtd->dma_desc_arr_size) {
+ dev_err(dev, "dma data too small or too big\n");
+ return -EINVAL;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = buf_len;
+
+ /* initialize dma descriptor array */
+ mem_addr = runtime->dma_addr;
+ order_addr = prtd->dma_desc_arr_phy;
+ for (i = 0; i < num_periods; i++) {
+ desc = &prtd->dma_desc_arr[i];
+
+ /* next descriptor physical address */
+ order_addr += sizeof(*desc);
+ desc->order = lower_32_bits(order_addr | BIT(0));
+ desc->order_hi = upper_32_bits(order_addr);
+
+ desc->saddr = lower_32_bits(mem_addr);
+ desc->saddr_hi = upper_32_bits(mem_addr);
+ desc->daddr = prtd->dma_data->dev_addr;
+
+ desc->cmd = BIT(0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ desc->cmd |= BIT(12);
+
+ desc->length = period_len >> 2;
+ desc->step_length = 0;
+ desc->step_times = 1;
+
+ mem_addr += period_len;
+ }
+ desc = &prtd->dma_desc_arr[num_periods - 1];
+ desc->order = lower_32_bits(prtd->dma_desc_arr_phy | BIT(0));
+ desc->order_hi = upper_32_bits(prtd->dma_desc_arr_phy);
+
+ /* init position descriptor */
+ *prtd->dma_pos_desc = *prtd->dma_desc_arr;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+loongson_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loongson_runtime_data *prtd = runtime->private_data;
+ struct loongson_dma_desc *desc;
+ snd_pcm_uframes_t x;
+ u64 addr;
+
+ desc = dma_desc_save(prtd);
+ addr = ((u64)desc->saddr_hi << 32) | desc->saddr;
+
+ x = bytes_to_frames(runtime, addr - runtime->dma_addr);
+ if (x == runtime->buffer_size)
+ x = 0;
+ return x;
+}
+
+static irqreturn_t loongson_pcm_dma_irq(int irq, void *devid)
+{
+ struct snd_pcm_substream *substream = devid;
+
+ snd_pcm_period_elapsed(substream);
+ return IRQ_HANDLED;
+}
+
+static int loongson_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_card *card = substream->pcm->card;
+ struct loongson_runtime_data *prtd;
+ struct loongson_dma_data *dma_data;
+
+ /*
+ * For mysterious reasons (and despite what the manual says)
+ * playback samples are lost if the DMA count is not a multiple
+ * of the DMA burst size. Let's add a rule to enforce that.
+ */
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
+ snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ snd_soc_set_runtime_hwparams(substream, &ls_pcm_hardware);
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ prtd->dma_desc_arr = dma_alloc_coherent(card->dev, PAGE_SIZE,
+ &prtd->dma_desc_arr_phy,
+ GFP_KERNEL);
+ if (!prtd->dma_desc_arr)
+ goto desc_err;
+
+ prtd->dma_desc_arr_size = PAGE_SIZE / sizeof(*prtd->dma_desc_arr);
+
+ prtd->dma_pos_desc = dma_alloc_coherent(card->dev,
+ sizeof(*prtd->dma_pos_desc),
+ &prtd->dma_pos_desc_phy,
+ GFP_KERNEL);
+ if (!prtd->dma_pos_desc)
+ goto pos_err;
+
+ dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ prtd->dma_data = dma_data;
+
+ substream->runtime->private_data = prtd;
+
+ return 0;
+pos_err:
+ dma_free_coherent(card->dev, PAGE_SIZE, prtd->dma_desc_arr,
+ prtd->dma_desc_arr_phy);
+desc_err:
+ kfree(prtd);
+
+ return -ENOMEM;
+}
+
+static int loongson_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_card *card = substream->pcm->card;
+ struct loongson_runtime_data *prtd = substream->runtime->private_data;
+
+ dma_free_coherent(card->dev, PAGE_SIZE, prtd->dma_desc_arr,
+ prtd->dma_desc_arr_phy);
+
+ dma_free_coherent(card->dev, sizeof(*prtd->dma_pos_desc),
+ prtd->dma_pos_desc, prtd->dma_pos_desc_phy);
+
+ kfree(prtd);
+ return 0;
+}
+
+static int loongson_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static int loongson_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm_substream *substream;
+ struct loongson_dma_data *dma_data;
+ unsigned int i;
+ int ret;
+
+ for_each_pcm_streams(i) {
+ substream = rtd->pcm->streams[i].substream;
+ if (!substream)
+ continue;
+
+ dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0),
+ substream);
+ ret = devm_request_irq(card->dev, dma_data->irq,
+ loongson_pcm_dma_irq,
+ IRQF_TRIGGER_HIGH, LS_I2S_DRVNAME,
+ substream);
+ if (ret < 0) {
+ dev_err(card->dev, "request irq for DMA failed\n");
+ return ret;
+ }
+ }
+
+ return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev,
+ ls_pcm_hardware.buffer_bytes_max);
+}
+
+const struct snd_soc_component_driver loongson_i2s_component = {
+ .name = LS_I2S_DRVNAME,
+ .open = loongson_pcm_open,
+ .close = loongson_pcm_close,
+ .hw_params = loongson_pcm_hw_params,
+ .trigger = loongson_pcm_trigger,
+ .pointer = loongson_pcm_pointer,
+ .mmap = loongson_pcm_mmap,
+ .pcm_construct = loongson_pcm_new,
+};
diff --git a/sound/soc/loongson/loongson_dma.h b/sound/soc/loongson/loongson_dma.h
new file mode 100644
index 000000000000..073ee8c0c046
--- /dev/null
+++ b/sound/soc/loongson/loongson_dma.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA ASoC interface for the Loongson platform
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ * Author: Yingkun Meng <mengyingkun@loongson.cn>
+ */
+
+#ifndef _LOONGSON_DMA_H
+#define _LOONGSON_DMA_H
+
+#include <sound/soc.h>
+
+extern const struct snd_soc_component_driver loongson_i2s_component;
+
+#endif
diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c
new file mode 100644
index 000000000000..e336656e13eb
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Common functions for loongson I2S controller driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited.
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "loongson_i2s.h"
+
+#define LOONGSON_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define LOONGSON_I2S_TX_ENABLE (I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN)
+#define LOONGSON_I2S_RX_ENABLE (I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN)
+
+#define LOONGSON_I2S_DEF_DELAY 10
+#define LOONGSON_I2S_DEF_TIMEOUT 500000
+
+static int loongson_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mask = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ LOONGSON_I2S_TX_ENABLE : LOONGSON_I2S_RX_ENABLE;
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, mask, mask);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ mask = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ LOONGSON_I2S_TX_ENABLE : LOONGSON_I2S_RX_ENABLE;
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, mask, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int loongson_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 clk_rate = i2s->clk_rate;
+ u32 sysclk = i2s->sysclk;
+ u32 bits = params_width(params);
+ u32 chans = params_channels(params);
+ u32 fs = params_rate(params);
+ u32 bclk_ratio, mclk_ratio;
+ u32 mclk_ratio_frac;
+ u32 val = 0;
+
+ switch (i2s->rev_id) {
+ case 0:
+ bclk_ratio = DIV_ROUND_CLOSEST(clk_rate,
+ (bits * chans * fs * 2)) - 1;
+ mclk_ratio = DIV_ROUND_CLOSEST(clk_rate, (sysclk * 2)) - 1;
+
+ /* According to 2k1000LA user manual, set bits == depth */
+ val |= (bits << 24);
+ val |= (bits << 16);
+ val |= (bclk_ratio << 8);
+ val |= mclk_ratio;
+ regmap_write(i2s->regmap, LS_I2S_CFG, val);
+
+ break;
+ case 1:
+ bclk_ratio = DIV_ROUND_CLOSEST(sysclk,
+ (bits * chans * fs * 2)) - 1;
+ mclk_ratio = clk_rate / sysclk;
+ mclk_ratio_frac = DIV_ROUND_CLOSEST_ULL(((u64)clk_rate << 16),
+ sysclk) - (mclk_ratio << 16);
+
+ regmap_read(i2s->regmap, LS_I2S_CFG, &val);
+ val |= (bits << 24);
+ val |= (bclk_ratio << 8);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ val |= (bits << 16);
+ else
+ val |= bits;
+ regmap_write(i2s->regmap, LS_I2S_CFG, val);
+
+ val = (mclk_ratio_frac << 16) | mclk_ratio;
+ regmap_write(i2s->regmap, LS_I2S_CFG1, val);
+
+ break;
+ default:
+ dev_err(i2s->dev, "I2S revision invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int loongson_i2s_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ i2s->sysclk = freq;
+
+ return 0;
+}
+
+static int loongson_i2s_enable_mclk(struct loongson_i2s *i2s)
+{
+ u32 val;
+
+ if (i2s->rev_id == 0)
+ return 0;
+
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_MCLK_EN, I2S_CTRL_MCLK_EN);
+
+ return regmap_read_poll_timeout_atomic(i2s->regmap,
+ LS_I2S_CTRL, val,
+ val & I2S_CTRL_MCLK_READY,
+ LOONGSON_I2S_DEF_DELAY,
+ LOONGSON_I2S_DEF_TIMEOUT);
+}
+
+static int loongson_i2s_enable_bclk(struct loongson_i2s *i2s)
+{
+ u32 val;
+
+ if (i2s->rev_id == 0)
+ return 0;
+
+ return regmap_read_poll_timeout_atomic(i2s->regmap,
+ LS_I2S_CTRL, val,
+ val & I2S_CTRL_CLK_READY,
+ LOONGSON_I2S_DEF_DELAY,
+ LOONGSON_I2S_DEF_TIMEOUT);
+}
+
+static int loongson_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MSB,
+ I2S_CTRL_MSB);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ /* Enable master mode */
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER,
+ I2S_CTRL_MASTER);
+ ret = loongson_i2s_enable_bclk(i2s);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait BCLK ready timeout\n");
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ /* Enable MCLK */
+ ret = loongson_i2s_enable_mclk(i2s);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait MCLK ready timeout\n");
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ /* Enable MCLK */
+ ret = loongson_i2s_enable_mclk(i2s);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait MCLK ready timeout\n");
+
+ /* Enable master mode */
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER,
+ I2S_CTRL_MASTER);
+
+ ret = loongson_i2s_enable_bclk(i2s);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait BCLK ready timeout\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct loongson_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+
+ snd_soc_dai_init_dma_data(cpu_dai, &i2s->playback_dma_data,
+ &i2s->capture_dma_data);
+ snd_soc_dai_set_drvdata(cpu_dai, i2s);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops loongson_i2s_dai_ops = {
+ .probe = loongson_i2s_dai_probe,
+ .trigger = loongson_i2s_trigger,
+ .hw_params = loongson_i2s_hw_params,
+ .set_sysclk = loongson_i2s_set_dai_sysclk,
+ .set_fmt = loongson_i2s_set_fmt,
+};
+
+struct snd_soc_dai_driver loongson_i2s_dai = {
+ .name = "loongson-i2s",
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = LOONGSON_I2S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = LOONGSON_I2S_FORMATS,
+ },
+ .ops = &loongson_i2s_dai_ops,
+ .symmetric_rate = 1,
+};
+EXPORT_SYMBOL_GPL(loongson_i2s_dai);
+
+static int i2s_suspend(struct device *dev)
+{
+ struct loongson_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, true);
+
+ return 0;
+}
+
+static int i2s_resume(struct device *dev)
+{
+ struct loongson_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, false);
+ regcache_mark_dirty(i2s->regmap);
+ return regcache_sync(i2s->regmap);
+}
+
+const struct dev_pm_ops loongson_i2s_pm = {
+ SYSTEM_SLEEP_PM_OPS(i2s_suspend, i2s_resume)
+};
+EXPORT_SYMBOL_GPL(loongson_i2s_pm);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Common functions for loongson I2S controller driver");
diff --git a/sound/soc/loongson/loongson_i2s.h b/sound/soc/loongson/loongson_i2s.h
new file mode 100644
index 000000000000..c8052a762c1b
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA I2S interface for the Loongson platform
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ * Author: Yingkun Meng <mengyingkun@loongson.cn>
+ */
+
+#ifndef _LOONGSON_I2S_H
+#define _LOONGSON_I2S_H
+
+#include <linux/regmap.h>
+#include <sound/dmaengine_pcm.h>
+
+/* I2S Common Registers */
+#define LS_I2S_VER 0x00 /* I2S Version */
+#define LS_I2S_CFG 0x04 /* I2S Config */
+#define LS_I2S_CTRL 0x08 /* I2S Control */
+#define LS_I2S_RX_DATA 0x0C /* I2S DMA RX Address */
+#define LS_I2S_TX_DATA 0x10 /* I2S DMA TX Address */
+
+/* 2K2000 I2S Specify Registers */
+#define LS_I2S_CFG1 0x14 /* I2S Config1 */
+
+/* 7A2000 I2S Specify Registers */
+#define LS_I2S_TX_ORDER 0x100 /* TX DMA Order */
+#define LS_I2S_RX_ORDER 0x110 /* RX DMA Order */
+
+/* Loongson I2S Control Register */
+#define I2S_CTRL_MCLK_READY BIT(16) /* MCLK ready */
+#define I2S_CTRL_MASTER BIT(15) /* Master mode */
+#define I2S_CTRL_MSB BIT(14) /* MSB bit order */
+#define I2S_CTRL_RX_EN BIT(13) /* RX enable */
+#define I2S_CTRL_TX_EN BIT(12) /* TX enable */
+#define I2S_CTRL_RX_DMA_EN BIT(11) /* DMA RX enable */
+#define I2S_CTRL_CLK_READY BIT(8) /* BCLK ready */
+#define I2S_CTRL_TX_DMA_EN BIT(7) /* DMA TX enable */
+#define I2S_CTRL_RESET BIT(4) /* Controller soft reset */
+#define I2S_CTRL_MCLK_EN BIT(3) /* Enable MCLK */
+#define I2S_CTRL_RX_INT_EN BIT(1) /* RX interrupt enable */
+#define I2S_CTRL_TX_INT_EN BIT(0) /* TX interrupt enable */
+
+#define LS_I2S_DRVNAME "loongson-i2s"
+
+struct loongson_dma_data {
+ dma_addr_t dev_addr; /* device physical address for DMA */
+ void __iomem *order_addr; /* DMA order register */
+ int irq; /* DMA irq */
+};
+
+struct loongson_i2s {
+ struct device *dev;
+ union {
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct loongson_dma_data tx_dma_data;
+ };
+ union {
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct loongson_dma_data rx_dma_data;
+ };
+ struct regmap *regmap;
+ void __iomem *reg_base;
+ u32 rev_id;
+ u32 clk_rate;
+ u32 sysclk;
+};
+
+extern const struct dev_pm_ops loongson_i2s_pm;
+extern struct snd_soc_dai_driver loongson_i2s_dai;
+
+#endif
diff --git a/sound/soc/loongson/loongson_i2s_pci.c b/sound/soc/loongson/loongson_i2s_pci.c
new file mode 100644
index 000000000000..1ea5501a97f8
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s_pci.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// loongson_i2s_pci.c -- Loongson I2S controller driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <sound/soc.h>
+#include "loongson_i2s.h"
+#include "loongson_dma.h"
+
+#define DRIVER_NAME "loongson-i2s-pci"
+
+static bool loongson_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LS_I2S_CFG:
+ case LS_I2S_CTRL:
+ case LS_I2S_RX_DATA:
+ case LS_I2S_TX_DATA:
+ case LS_I2S_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool loongson_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LS_I2S_VER:
+ case LS_I2S_CFG:
+ case LS_I2S_CTRL:
+ case LS_I2S_RX_DATA:
+ case LS_I2S_TX_DATA:
+ case LS_I2S_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool loongson_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LS_I2S_CFG:
+ case LS_I2S_CTRL:
+ case LS_I2S_RX_DATA:
+ case LS_I2S_TX_DATA:
+ case LS_I2S_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config loongson_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LS_I2S_CFG1,
+ .writeable_reg = loongson_i2s_wr_reg,
+ .readable_reg = loongson_i2s_rd_reg,
+ .volatile_reg = loongson_i2s_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int loongson_i2s_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pid)
+{
+ const struct fwnode_handle *fwnode = pdev->dev.fwnode;
+ struct loongson_dma_data *tx_data, *rx_data;
+ struct device *dev = &pdev->dev;
+ struct loongson_i2s *i2s;
+ int ret;
+
+ if (pcim_enable_device(pdev)) {
+ dev_err(dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->rev_id = pdev->revision;
+ i2s->dev = dev;
+ pci_set_drvdata(pdev, i2s);
+
+ i2s->reg_base = pcim_iomap_region(pdev, 0, DRIVER_NAME);
+ if (IS_ERR(i2s->reg_base)) {
+ dev_err(dev, "iomap_region failed\n");
+ return PTR_ERR(i2s->reg_base);
+ }
+
+ i2s->regmap = devm_regmap_init_mmio(dev, i2s->reg_base,
+ &loongson_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap))
+ return dev_err_probe(dev, PTR_ERR(i2s->regmap), "regmap_init_mmio failed\n");
+
+ tx_data = &i2s->tx_dma_data;
+ rx_data = &i2s->rx_dma_data;
+
+ tx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_TX_DATA;
+ tx_data->order_addr = i2s->reg_base + LS_I2S_TX_ORDER;
+
+ rx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_RX_DATA;
+ rx_data->order_addr = i2s->reg_base + LS_I2S_RX_ORDER;
+
+ tx_data->irq = fwnode_irq_get_byname(fwnode, "tx");
+ if (tx_data->irq < 0)
+ return dev_err_probe(dev, tx_data->irq, "dma tx irq invalid\n");
+
+ rx_data->irq = fwnode_irq_get_byname(fwnode, "rx");
+ if (rx_data->irq < 0)
+ return dev_err_probe(dev, rx_data->irq, "dma rx irq invalid\n");
+
+ ret = device_property_read_u32(dev, "clock-frequency", &i2s->clk_rate);
+ if (ret)
+ return dev_err_probe(dev, ret, "clock-frequency property invalid\n");
+
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+
+ if (i2s->rev_id == 1) {
+ regmap_write(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET);
+ udelay(200);
+ }
+
+ ret = devm_snd_soc_register_component(dev, &loongson_i2s_component,
+ &loongson_i2s_dai, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "register DAI failed\n");
+
+ return 0;
+}
+
+static const struct pci_device_id loongson_i2s_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a27) },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, loongson_i2s_ids);
+
+static struct pci_driver loongson_i2s_driver = {
+ .name = DRIVER_NAME,
+ .id_table = loongson_i2s_ids,
+ .probe = loongson_i2s_pci_probe,
+ .driver = {
+ .pm = pm_sleep_ptr(&loongson_i2s_pm),
+ },
+};
+module_pci_driver(loongson_i2s_driver);
+
+MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c
new file mode 100644
index 000000000000..fa2e450ff618
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s_plat.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Loongson I2S controller master mode dirver(platform device)
+//
+// Copyright (C) 2023-2024 Loongson Technology Corporation Limited
+//
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+// Binbin Zhou <zhoubinbin@loongson.cn>
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "loongson_i2s.h"
+
+#define LOONGSON_I2S_RX_DMA_OFFSET 21
+#define LOONGSON_I2S_TX_DMA_OFFSET 18
+
+#define LOONGSON_DMA0_CONF 0x0
+#define LOONGSON_DMA1_CONF 0x1
+#define LOONGSON_DMA2_CONF 0x2
+#define LOONGSON_DMA3_CONF 0x3
+#define LOONGSON_DMA4_CONF 0x4
+
+/* periods_max = PAGE_SIZE / sizeof(struct ls_dma_chan_reg) */
+static const struct snd_pcm_hardware loongson_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .period_bytes_min = 128,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 1,
+ .periods_max = 64,
+ .buffer_bytes_max = 1024 * 1024,
+};
+
+static const struct snd_dmaengine_pcm_config loongson_dmaengine_pcm_config = {
+ .pcm_hardware = &loongson_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+static int loongson_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (substream->pcm->device & 1) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+ runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+ }
+
+ if (substream->pcm->device & 2)
+ runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID);
+ /*
+ * For mysterious reasons (and despite what the manual says)
+ * playback samples are lost if the DMA count is not a multiple
+ * of the DMA burst size. Let's add a rule to enforce that.
+ */
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
+ snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver loongson_i2s_component_driver = {
+ .name = LS_I2S_DRVNAME,
+ .open = loongson_pcm_open,
+};
+
+static const struct regmap_config loongson_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x14,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int loongson_i2s_apbdma_config(struct platform_device *pdev)
+{
+ int val;
+ void __iomem *regs;
+
+ regs = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ val = readl(regs);
+ val |= LOONGSON_DMA2_CONF << LOONGSON_I2S_TX_DMA_OFFSET;
+ val |= LOONGSON_DMA3_CONF << LOONGSON_I2S_RX_DMA_OFFSET;
+ writel(val, regs);
+
+ return 0;
+}
+
+static int loongson_i2s_plat_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct loongson_i2s *i2s;
+ struct resource *res;
+ struct clk *i2s_clk;
+ int ret;
+
+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ ret = loongson_i2s_apbdma_config(pdev);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2s->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2s->reg_base))
+ return dev_err_probe(dev, PTR_ERR(i2s->reg_base),
+ "devm_ioremap_resource failed\n");
+
+ i2s->regmap = devm_regmap_init_mmio(dev, i2s->reg_base,
+ &loongson_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap))
+ return dev_err_probe(dev, PTR_ERR(i2s->regmap),
+ "devm_regmap_init_mmio failed\n");
+
+ i2s->playback_dma_data.addr = res->start + LS_I2S_TX_DATA;
+ i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->playback_dma_data.maxburst = 4;
+
+ i2s->capture_dma_data.addr = res->start + LS_I2S_RX_DATA;
+ i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->capture_dma_data.maxburst = 4;
+
+ i2s_clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(i2s_clk))
+ return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n");
+ i2s->clk_rate = clk_get_rate(i2s_clk);
+
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ dev_set_name(dev, LS_I2S_DRVNAME);
+ dev_set_drvdata(dev, i2s);
+
+ ret = devm_snd_soc_register_component(dev, &loongson_i2s_component_driver,
+ &loongson_i2s_dai, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register DAI\n");
+
+ return devm_snd_dmaengine_pcm_register(dev, &loongson_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
+}
+
+static const struct of_device_id loongson_i2s_ids[] = {
+ { .compatible = "loongson,ls2k1000-i2s" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, loongson_i2s_ids);
+
+static struct platform_driver loongson_i2s_driver = {
+ .probe = loongson_i2s_plat_probe,
+ .driver = {
+ .name = "loongson-i2s-plat",
+ .pm = pm_sleep_ptr(&loongson_i2s_pm),
+ .of_match_table = loongson_i2s_ids,
+ },
+};
+module_platform_driver(loongson_i2s_driver);
+
+MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 15c04e2eae34..3a1e1fa3fe5c 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,15 +1,94 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Mediatek"
+
config SND_SOC_MEDIATEK
- tristate "ASoC support for Mediatek chip"
+ tristate
+ select REGMAP_MMIO
+
+config SND_SOC_MT2701
+ tristate "ASoC support for Mediatek MT2701 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC driver for Mediatek MT2701 boards
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT2701_CS42448
+ tristate "ASoc Audio driver for MT2701 with CS42448 codec"
+ depends on SND_SOC_MT2701 && I2C
+ select SND_SOC_CS42XX8_I2C
+ select SND_SOC_BT_SCO
+ help
+ This adds ASoC driver for Mediatek MT2701 boards
+ with the CS42448 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT2701_WM8960
+ tristate "ASoc Audio driver for MT2701 with WM8960 codec"
+ depends on SND_SOC_MT2701 && I2C
+ select SND_SOC_WM8960
+ help
+ This adds ASoC driver for Mediatek MT2701 boards
+ with the WM8960 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT6797
+ tristate "ASoC support for Mediatek MT6797 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC driver for Mediatek MT6797 boards
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT6797_MT6351
+ tristate "ASoc Audio driver for MT6797 with MT6351 codec"
+ depends on SND_SOC_MT6797 && MTK_PMIC_WRAP
+ select SND_SOC_MT6351
+ help
+ This adds ASoC driver for Mediatek MT6797 boards
+ with the MT6351 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT7986
+ tristate "ASoC support for Mediatek MT7986 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC platform driver support for MediaTek MT7986 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT7986_WM8960
+ tristate "ASoc Audio driver for MT7986 with WM8960 codec"
+ depends on SND_SOC_MT7986 && I2C
+ select SND_SOC_WM8960
+ help
+ This adds support for ASoC machine driver for MediaTek MT7986
+ boards with the WM8960 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8173
+ tristate "ASoC support for Mediatek MT8173 chip"
depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
help
- This adds ASoC platform driver support for Mediatek chip
+ This adds ASoC platform driver support for Mediatek MT8173 chip
that can be used with other codecs.
Select Y if you have such device.
Ex: MT8173
config SND_SOC_MT8173_MAX98090
tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
- depends on SND_SOC_MEDIATEK
+ depends on SND_SOC_MT8173 && I2C
select SND_SOC_MAX98090
help
This adds ASoC driver for Mediatek MT8173 boards
@@ -17,14 +96,261 @@ config SND_SOC_MT8173_MAX98090
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8173_RT5650
+ tristate "ASoC Audio driver for MT8173 with RT5650 codec"
+ depends on SND_SOC_MT8173 && I2C
+ select SND_SOC_RT5645
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds ASoC driver for Mediatek MT8173 boards
+ with the RT5650 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8173_RT5650_RT5514
+ tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
+ depends on SND_SOC_MT8173 && I2C
+ select SND_SOC_RT5645
+ select SND_SOC_RT5514
+ help
+ This adds ASoC driver for Mediatek MT8173 boards
+ with the RT5650 and RT5514 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
config SND_SOC_MT8173_RT5650_RT5676
tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
- depends on SND_SOC_MEDIATEK
+ depends on SND_SOC_MT8173 && I2C
select SND_SOC_RT5645
select SND_SOC_RT5677
+ select SND_SOC_HDMI_CODEC
help
This adds ASoC driver for Mediatek MT8173 boards
with the RT5650 and RT5676 codecs.
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8183
+ tristate "ASoC support for Mediatek MT8183 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC platform driver support for Mediatek MT8183 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A
+ tristate "ASoC Audio driver for MT8183 with MT6358 TS3A227E MAX98357A RT1015 codec"
+ depends on I2C && GPIOLIB
+ depends on SND_SOC_MT8183
+ select SND_SOC_MT6358
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT1015
+ select SND_SOC_RT1015P
+ select SND_SOC_BT_SCO
+ select SND_SOC_TS3A227E
+ select SND_SOC_CROS_EC_CODEC if CROS_EC
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds ASoC driver for Mediatek MT8183 boards
+ with the MT6358 TS3A227E MAX98357A RT1015 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8183_DA7219_MAX98357A
+ tristate "ASoC Audio driver for MT8183 with DA7219 MAX98357A RT1015 codec"
+ depends on SND_SOC_MT8183 && I2C && GPIOLIB
+ select SND_SOC_MT6358
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT1015
+ select SND_SOC_RT1015P
+ select SND_SOC_DA7219
+ select SND_SOC_BT_SCO
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds ASoC driver for Mediatek MT8183 boards
+ with the DA7219 MAX98357A RT1015 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8186
+ tristate "ASoC support for Mediatek MT8186 chip"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_SOC_MEDIATEK
+ select SND_SOC_MT6358
+ select MFD_SYSCON if SND_SOC_MT6358
+ help
+ This adds ASoC driver for Mediatek MT8186 boards
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8186_MT6366
+ tristate "ASoC Audio driver for MT8186 with MT6366 and I2S codecs"
+ depends on I2C && GPIOLIB
+ depends on SND_SOC_MT8186 && MTK_PMIC_WRAP
+ select SND_SOC_DA7219
+ select SND_SOC_MT6358
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT1015P
+ select SND_SOC_RT5682S
+ select SND_SOC_RT5645
+ select SND_SOC_BT_SCO
+ select SND_SOC_DMIC
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds the ASoC machine driver for Mediatek MT8186 boards
+ with the MT6366(MT6358) and other I2S audio codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MTK_BTCVSD
+ tristate "ALSA BT SCO CVSD/MSBC Driver"
+ help
+ This is for software BTCVSD. This enable
+ the function for transferring/receiving
+ BT encoded data to/from BT firmware.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8188
+ tristate "ASoC support for MediaTek MT8188 chip"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_SOC_MEDIATEK
+ select MFD_SYSCON if SND_SOC_MT6359
+ help
+ This adds ASoC platform driver support for MediaTek MT8188 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8188_MT6359
+ tristate "ASoC Audio driver for MT8188 with MT6359 and I2S codecs"
+ depends on SND_SOC_MT8188 && MTK_PMIC_WRAP
+ depends on SND_SOC_MT6359_ACCDET || !SND_SOC_MT6359_ACCDET
+ depends on I2C
+ select SND_SOC_MT6359
+ select SND_SOC_HDMI_CODEC
+ select SND_SOC_DMIC
+ select SND_SOC_MAX98390
+ select SND_SOC_NAU8315
+ select SND_SOC_NAU8825
+ select SND_SOC_RT5682S
+ select SND_SOC_ES8326
+ help
+ This adds support for ASoC machine driver for MediaTek MT8188
+ boards with the MT6359 and other I2S audio codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8189
+ tristate "ASoC support for Mediatek MT8189 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC driver for Mediatek MT8189 boards
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8189_NAU8825
+ tristate "ASoc Audio driver for MT8189 with NAU8825 and I2S codec"
+ depends on SND_SOC_MT8189
+ depends on I2C
+ select SND_SOC_DMIC
+ select SND_SOC_HDMI_CODEC
+ select SND_SOC_NAU8825
+ select SND_SOC_RT5645
+ select SND_SOC_RT9123P
+ select SND_SOC_RT1015P
+ select SND_SOC_RT5682S
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_CS35L41_I2C
+ select SND_SOC_AW88081
+ select SND_SOC_ES8326
+ help
+ This adds support for ASoC machine driver for MediaTek MT8189
+ boards with the NAU8828 and other I2S audio codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8192
+ tristate "ASoC support for Mediatek MT8192 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC platform driver support for Mediatek MT8192 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8192_MT6359_RT1015_RT5682
+ tristate "ASoC Audio driver for MT8192 with MT6359 RT1015 RT5682 codec"
+ depends on I2C && GPIOLIB
+ depends on SND_SOC_MT8192 && MTK_PMIC_WRAP
+ select SND_SOC_MT6359
+ select SND_SOC_RT1015
+ select SND_SOC_RT1015P
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
+ select SND_SOC_DMIC
+ help
+ This adds ASoC driver for Mediatek MT8192 boards
+ with the MT6359 RT1015 RT5682 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8195
+ tristate "ASoC support for Mediatek MT8195 chip"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_SOC_MEDIATEK
+ select MFD_SYSCON if SND_SOC_MT6359
+ help
+ This adds ASoC platform driver support for Mediatek MT8195 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8195_MT6359
+ tristate "ASoC Audio driver for MT8195 with MT6359 and I2S codecs"
+ depends on I2C && GPIOLIB
+ depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
+ select SND_SOC_MT6359
+ select SND_SOC_RT1011
+ select SND_SOC_RT1015P
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
+ select SND_SOC_MAX98390
+ select SND_SOC_DMIC
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds support for ASoC machine driver for Mediatek MT8195
+ boards with the MT6359 and other I2S audio codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8365
+ tristate "ASoC support for MediaTek MT8365 chip"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC platform driver support for MediaTek MT8365 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8365_MT6357
+ tristate "ASoC Audio driver for MT8365 with MT6357 codec"
+ depends on SND_SOC_MT8365 && MTK_PMIC_WRAP
+ select SND_SOC_MT6357
+ help
+ This adds support for ASoC machine driver for MediaTek MT8365
+ boards with the MT6357 PMIC codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+endmenu
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 75effbec438d..7cd67bce92e9 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,5 +1,13 @@
-# MTK Platform Support
-obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
-# Machine support
-obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
+obj-$(CONFIG_SND_SOC_MT6797) += mt6797/
+obj-$(CONFIG_SND_SOC_MT7986) += mt7986/
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
+obj-$(CONFIG_SND_SOC_MT8183) += mt8183/
+obj-$(CONFIG_SND_SOC_MT8186) += mt8186/
+obj-$(CONFIG_SND_SOC_MT8188) += mt8188/
+obj-$(CONFIG_SND_SOC_MT8192) += mt8192/
+obj-$(CONFIG_SND_SOC_MT8195) += mt8195/
+obj-$(CONFIG_SND_SOC_MT8365) += mt8365/
+obj-$(CONFIG_SND_SOC_MT8189) += mt8189/
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 000000000000..c90d276cf4ed
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# platform driver
+snd-soc-mtk-common-y := mtk-afe-platform-driver.o mtk-afe-fe-dai.o mtk-dsp-sof-common.o mtk-soundcard-driver.o
+snd-soc-mtk-common-y += mtk-dai-adda-common.o
+
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
+
+obj-$(CONFIG_SND_SOC_MTK_BTCVSD) += mtk-btcvsd.o
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644
index 000000000000..3809068f5620
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mtk-afe-fe-dais.c -- Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-platform-driver.h"
+#include <sound/pcm_params.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+static int mtk_regmap_update_bits(struct regmap *map, int reg,
+ unsigned int mask,
+ unsigned int val, int shift)
+{
+ if (reg < 0 || WARN_ON_ONCE(shift < 0))
+ return 0;
+ return regmap_update_bits(map, reg, mask << shift, val << shift);
+}
+
+static int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+ if (reg < 0)
+ return 0;
+ return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int memif_num = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+ int ret;
+
+ memif->substream = substream;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+ /* enable agent */
+ mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+ 1, 0, memif->data->agent_disable_shift);
+
+ snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+ /*
+ * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+ * smaller than period_size due to AFE's internal buffer.
+ * This easily leads to overrun when avail_min is period_size.
+ * One more period can hold the possible unread buffer.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ int periods_max = mtk_afe_hardware->periods_max;
+
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS,
+ 3, periods_max);
+ if (ret < 0) {
+ dev_err(afe->dev, "hw_constraint_minmax failed\n");
+ return ret;
+ }
+ }
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+ /* dynamic allocate irq to memif */
+ if (memif->irq_usage < 0) {
+ int irq_id = mtk_dynamic_irq_acquire(afe);
+
+ if (irq_id != afe->irqs_size) {
+ /* link */
+ memif->irq_usage = irq_id;
+ } else {
+ dev_err(afe->dev, "%s() error: no more asys irq\n",
+ __func__);
+ ret = -EBUSY;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mtk_base_afe_memif *memif = &afe->memif[snd_soc_rtd_to_cpu(rtd, 0)->id];
+ int irq_id;
+
+ irq_id = memif->irq_usage;
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+ 1, 1, memif->data->agent_disable_shift);
+
+ if (!memif->const_irq) {
+ mtk_dynamic_irq_release(afe, irq_id);
+ memif->irq_usage = -1;
+ memif->substream = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int ret;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ snd_pcm_format_t format = params_format(params);
+
+ if (afe->request_dram_resource)
+ afe->request_dram_resource(afe->dev);
+
+ dev_dbg(afe->dev, "%s(), %s, ch %d, rate %d, fmt %d, dma_addr %pad, dma_area %p, dma_bytes 0x%zx\n",
+ __func__, memif->data->name,
+ channels, rate, format,
+ &substream->runtime->dma_addr,
+ substream->runtime->dma_area,
+ substream->runtime->dma_bytes);
+
+ memset_io((void __force __iomem *)substream->runtime->dma_area, 0,
+ substream->runtime->dma_bytes);
+
+ /* set addr */
+ ret = mtk_memif_set_addr(afe, id,
+ substream->runtime->dma_area,
+ substream->runtime->dma_addr,
+ substream->runtime->dma_bytes);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, set addr, ret %d\n",
+ __func__, id, ret);
+ return ret;
+ }
+
+ /* set channel */
+ ret = mtk_memif_set_channel(afe, id, channels);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, set channel %d, ret %d\n",
+ __func__, id, channels, ret);
+ return ret;
+ }
+
+ /* set rate */
+ ret = mtk_memif_set_rate_substream(substream, id, rate);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, set rate %d, ret %d\n",
+ __func__, id, rate, ret);
+ return ret;
+ }
+
+ /* set format */
+ ret = mtk_memif_set_format(afe, id, format);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, set format %d, ret %d\n",
+ __func__, id, format, ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ if (afe->release_dram_resource)
+ afe->release_dram_resource(afe->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int counter = runtime->period_size;
+ int fs;
+ int ret;
+
+ dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = mtk_memif_set_enable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+ return ret;
+ }
+
+ /* set irq counter */
+ mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit, counter,
+ irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, runtime->rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit, fs,
+ irq_data->irq_fs_shift);
+
+ /* enable interrupt */
+ mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+ 1, 1, irq_data->irq_en_shift);
+
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = mtk_memif_set_disable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+ }
+
+ /* disable interrupt */
+ mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+ 1, 0, irq_data->irq_en_shift);
+ /* and clear pending IRQ */
+ mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+ 1 << irq_data->irq_clr_shift);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ int pbuf_size;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (afe->get_memif_pbuf_size) {
+ pbuf_size = afe->get_memif_pbuf_size(substream);
+ mtk_memif_set_pbuf_size(afe, id, pbuf_size);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+ .startup = mtk_afe_fe_startup,
+ .shutdown = mtk_afe_fe_shutdown,
+ .hw_params = mtk_afe_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+ int i;
+
+ mutex_lock(&afe->irq_alloc_lock);
+ for (i = 0; i < afe->irqs_size; ++i) {
+ if (afe->irqs[i].irq_occupyed == 0) {
+ afe->irqs[i].irq_occupyed = 1;
+ mutex_unlock(&afe->irq_alloc_lock);
+ return i;
+ }
+ }
+ mutex_unlock(&afe->irq_alloc_lock);
+ return afe->irqs_size;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+ mutex_lock(&afe->irq_alloc_lock);
+ if (irq_id >= 0 && irq_id < afe->irqs_size) {
+ afe->irqs[irq_id].irq_occupyed = 0;
+ mutex_unlock(&afe->irq_alloc_lock);
+ return 0;
+ }
+ mutex_unlock(&afe->irq_alloc_lock);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
+
+int mtk_afe_suspend(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct device *dev = afe->dev;
+ struct regmap *regmap = afe->regmap;
+ int i;
+
+ if (pm_runtime_status_suspended(dev) || afe->suspended)
+ return 0;
+
+ if (!afe->reg_back_up)
+ afe->reg_back_up =
+ devm_kcalloc(dev, afe->reg_back_up_list_num,
+ sizeof(unsigned int), GFP_KERNEL);
+
+ if (afe->reg_back_up) {
+ for (i = 0; i < afe->reg_back_up_list_num; i++)
+ regmap_read(regmap, afe->reg_back_up_list[i],
+ &afe->reg_back_up[i]);
+ }
+
+ afe->suspended = true;
+ afe->runtime_suspend(dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_suspend);
+
+int mtk_afe_resume(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct device *dev = afe->dev;
+ struct regmap *regmap = afe->regmap;
+ int i;
+
+ if (pm_runtime_status_suspended(dev) || !afe->suspended)
+ return 0;
+
+ afe->runtime_resume(dev);
+
+ if (!afe->reg_back_up) {
+ dev_dbg(dev, "%s no reg_backup\n", __func__);
+ } else {
+ for (i = 0; i < afe->reg_back_up_list_num; i++)
+ mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+ afe->reg_back_up[i]);
+ }
+
+ afe->suspended = false;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_resume);
+
+int mtk_memif_set_enable(struct mtk_base_afe *afe, int id)
+{
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+
+ if (memif->data->enable_shift < 0) {
+ dev_warn(afe->dev, "%s(), error, id %d, enable_shift < 0\n",
+ __func__, id);
+ return 0;
+ }
+ return mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+ 1, 1, memif->data->enable_shift);
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_enable);
+
+int mtk_memif_set_disable(struct mtk_base_afe *afe, int id)
+{
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+
+ if (memif->data->enable_shift < 0) {
+ dev_warn(afe->dev, "%s(), error, id %d, enable_shift < 0\n",
+ __func__, id);
+ return 0;
+ }
+ return mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+ 1, 0, memif->data->enable_shift);
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_disable);
+
+int mtk_memif_set_addr(struct mtk_base_afe *afe, int id,
+ unsigned char *dma_area,
+ dma_addr_t dma_addr,
+ size_t dma_bytes)
+{
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int msb_at_bit33 = upper_32_bits(dma_addr) ? 1 : 0;
+ unsigned int phys_buf_addr = lower_32_bits(dma_addr);
+ unsigned int phys_buf_addr_upper_32 = upper_32_bits(dma_addr);
+
+ memif->dma_area = dma_area;
+ memif->dma_addr = dma_addr;
+ memif->dma_bytes = dma_bytes;
+
+ /* start */
+ mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+ phys_buf_addr);
+ /* end */
+ if (memif->data->reg_ofs_end)
+ mtk_regmap_write(afe->regmap,
+ memif->data->reg_ofs_end,
+ phys_buf_addr + dma_bytes - 1);
+ else
+ mtk_regmap_write(afe->regmap,
+ memif->data->reg_ofs_base +
+ AFE_BASE_END_OFFSET,
+ phys_buf_addr + dma_bytes - 1);
+
+ /* set start, end, upper 32 bits */
+ if (memif->data->reg_ofs_base_msb) {
+ mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base_msb,
+ phys_buf_addr_upper_32);
+ mtk_regmap_write(afe->regmap,
+ memif->data->reg_ofs_end_msb,
+ phys_buf_addr_upper_32);
+ }
+
+ /*
+ * set MSB to 33-bit, for memif address
+ * only for memif base address, if msb_end_reg exists
+ */
+ if (memif->data->msb_reg)
+ mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+ 1, msb_at_bit33, memif->data->msb_shift);
+
+ /* set MSB to 33-bit, for memif end address */
+ if (memif->data->msb_end_reg)
+ mtk_regmap_update_bits(afe->regmap, memif->data->msb_end_reg,
+ 1, msb_at_bit33,
+ memif->data->msb_end_shift);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_addr);
+
+int mtk_memif_set_channel(struct mtk_base_afe *afe,
+ int id, unsigned int channel)
+{
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ unsigned int mono;
+
+ if (memif->data->mono_shift < 0)
+ return 0;
+
+ if (memif->data->quad_ch_mask) {
+ unsigned int quad_ch = (channel == 4) ? 1 : 0;
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->quad_ch_reg,
+ memif->data->quad_ch_mask,
+ quad_ch, memif->data->quad_ch_shift);
+ }
+
+ if (memif->data->mono_invert)
+ mono = (channel == 1) ? 0 : 1;
+ else
+ mono = (channel == 1) ? 1 : 0;
+
+ /* for specific configuration of memif mono mode */
+ if (memif->data->int_odd_flag_reg)
+ mtk_regmap_update_bits(afe->regmap,
+ memif->data->int_odd_flag_reg,
+ 1, mono,
+ memif->data->int_odd_flag_shift);
+
+ return mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+ 1, mono, memif->data->mono_shift);
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_channel);
+
+static int mtk_memif_set_rate_fs(struct mtk_base_afe *afe,
+ int id, int fs)
+{
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+
+ if (memif->data->fs_shift >= 0)
+ mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+ memif->data->fs_maskbit,
+ fs, memif->data->fs_shift);
+
+ return 0;
+}
+
+int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream,
+ int id, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ int fs = 0;
+
+ if (!afe->memif_fs) {
+ dev_err(afe->dev, "%s(), error, afe->memif_fs == NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ fs = afe->memif_fs(substream, rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ return mtk_memif_set_rate_fs(afe, id, fs);
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_rate_substream);
+
+int mtk_memif_set_format(struct mtk_base_afe *afe,
+ int id, snd_pcm_format_t format)
+{
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int hd_audio = 0;
+ int hd_align = 0;
+
+ /* set hd mode */
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ hd_audio = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ if (afe->memif_32bit_supported) {
+ hd_audio = 2;
+ hd_align = 0;
+ } else {
+ hd_audio = 1;
+ hd_align = 1;
+ }
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ hd_audio = 1;
+ break;
+ default:
+ dev_err(afe->dev, "%s() error: unsupported format %d\n",
+ __func__, format);
+ break;
+ }
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+ 0x3, hd_audio, memif->data->hd_shift);
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->hd_align_reg,
+ 0x1, hd_align, memif->data->hd_align_mshift);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_format);
+
+int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe,
+ int id, int pbuf_size)
+{
+ const struct mtk_base_memif_data *memif_data = afe->memif[id].data;
+
+ if (memif_data->pbuf_mask == 0 || memif_data->minlen_mask == 0)
+ return 0;
+
+ mtk_regmap_update_bits(afe->regmap, memif_data->pbuf_reg,
+ memif_data->pbuf_mask,
+ pbuf_size, memif_data->pbuf_shift);
+
+ mtk_regmap_update_bits(afe->regmap, memif_data->minlen_reg,
+ memif_data->minlen_mask,
+ pbuf_size, memif_data->minlen_shift);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_memif_set_pbuf_size);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644
index 000000000000..b6d0f2b27e86
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-afe-fe-dais.h -- Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_suspend(struct snd_soc_component *component);
+int mtk_afe_resume(struct snd_soc_component *component);
+
+int mtk_memif_set_enable(struct mtk_base_afe *afe, int id);
+int mtk_memif_set_disable(struct mtk_base_afe *afe, int id);
+int mtk_memif_set_addr(struct mtk_base_afe *afe, int id,
+ unsigned char *dma_area,
+ dma_addr_t dma_addr,
+ size_t dma_bytes);
+int mtk_memif_set_channel(struct mtk_base_afe *afe,
+ int id, unsigned int channel);
+int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream,
+ int id, unsigned int rate);
+int mtk_memif_set_format(struct mtk_base_afe *afe,
+ int id, snd_pcm_format_t format);
+int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe,
+ int id, int pbuf_size);
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644
index 000000000000..938dd3d46b00
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mtk-afe-platform-driver.c -- Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ size_t num_dai_drivers = 0, dai_idx = 0;
+
+ /* calcualte total dai driver size */
+ list_for_each_entry(dai, &afe->sub_dais, list) {
+ num_dai_drivers += dai->num_dai_drivers;
+ }
+
+ dev_info(afe->dev, "%s(), num of dai %zd\n", __func__, num_dai_drivers);
+
+ /* combine sub_dais */
+ afe->num_dai_drivers = num_dai_drivers;
+ afe->dai_drivers = devm_kcalloc(afe->dev,
+ num_dai_drivers,
+ sizeof(struct snd_soc_dai_driver),
+ GFP_KERNEL);
+ if (!afe->dai_drivers)
+ return -ENOMEM;
+
+ list_for_each_entry(dai, &afe->sub_dais, list) {
+ /* dai driver */
+ memcpy(&afe->dai_drivers[dai_idx],
+ dai->dai_drivers,
+ dai->num_dai_drivers *
+ sizeof(struct snd_soc_dai_driver));
+ dai_idx += dai->num_dai_drivers;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai);
+
+int mtk_afe_add_sub_dai_control(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mtk_base_afe_dai *dai;
+
+ list_for_each_entry(dai, &afe->sub_dais, list) {
+ if (dai->controls)
+ snd_soc_add_component_controls(component,
+ dai->controls,
+ dai->num_controls);
+
+ if (dai->dapm_widgets)
+ snd_soc_dapm_new_controls(dapm,
+ dai->dapm_widgets,
+ dai->num_dapm_widgets);
+ }
+ /* add routes after all widgets are added */
+ list_for_each_entry(dai, &afe->sub_dais, list) {
+ if (dai->dapm_routes)
+ snd_soc_dapm_add_routes(dapm,
+ dai->dapm_routes,
+ dai->num_dapm_routes);
+ }
+
+ snd_soc_dapm_new_widgets(component->card);
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(mtk_afe_add_sub_dai_control);
+
+snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mtk_base_afe_memif *memif = &afe->memif[snd_soc_rtd_to_cpu(rtd, 0)->id];
+ const struct mtk_base_memif_data *memif_data = memif->data;
+ struct regmap *regmap = afe->regmap;
+ struct device *dev = afe->dev;
+ int reg_ofs_base = memif_data->reg_ofs_base;
+ int reg_ofs_cur = memif_data->reg_ofs_cur;
+ unsigned int hw_ptr = 0, hw_base = 0;
+ int ret, pcm_ptr_bytes;
+
+ ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+ if (ret || hw_ptr == 0) {
+ dev_err(dev, "%s hw_ptr err\n", __func__);
+ pcm_ptr_bytes = 0;
+ goto POINTER_RETURN_FRAMES;
+ }
+
+ ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+ if (ret || hw_base == 0) {
+ dev_err(dev, "%s hw_ptr err\n", __func__);
+ pcm_ptr_bytes = 0;
+ goto POINTER_RETURN_FRAMES;
+ }
+
+ pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+ return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_pointer);
+
+int mtk_afe_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size;
+ struct snd_pcm *pcm = rtd->pcm;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ size = afe->mtk_afe_hardware->buffer_bytes_max;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev,
+ afe->preallocate_buffers ? size : 0,
+ size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_new);
+
+static int mtk_afe_component_probe(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ snd_soc_component_init_regmap(component, afe->regmap);
+
+ /* If the list was never initialized there are no sub-DAIs */
+ if (afe->sub_dais.next && afe->sub_dais.prev) {
+ ret = mtk_afe_add_sub_dai_control(component);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct snd_soc_component_driver mtk_afe_pcm_platform = {
+ .name = AFE_PCM_NAME,
+ .pointer = mtk_afe_pcm_pointer,
+ .pcm_construct = mtk_afe_pcm_new,
+ .probe = mtk_afe_component_probe,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644
index 000000000000..fcc923b88f12
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-afe-platform-driver.h -- Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+#define AFE_PCM_NAME "mtk-afe-pcm"
+extern const struct snd_soc_component_driver mtk_afe_pcm_platform;
+
+struct mtk_base_afe;
+struct snd_pcm;
+struct snd_soc_component;
+struct snd_soc_pcm_runtime;
+
+snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+int mtk_afe_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd);
+
+int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe);
+int mtk_afe_add_sub_dai_control(struct snd_soc_component *component);
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644
index 000000000000..a406f2e3e7a8
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-base-afe.h -- Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+
+#define MTK_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MTK_SIP_AUDIO_CONTROL MTK_SIP_SMC_CMD(0x517)
+
+/* SMC CALL Operations */
+enum mtk_audio_smc_call_op {
+ MTK_AUDIO_SMC_OP_INIT = 0,
+ MTK_AUDIO_SMC_OP_DRAM_REQUEST,
+ MTK_AUDIO_SMC_OP_DRAM_RELEASE,
+ MTK_AUDIO_SMC_OP_SRAM_REQUEST,
+ MTK_AUDIO_SMC_OP_SRAM_RELEASE,
+ MTK_AUDIO_SMC_OP_ADSP_REQUEST,
+ MTK_AUDIO_SMC_OP_ADSP_RELEASE,
+ MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS,
+ MTK_AUDIO_SMC_OP_BTCVSD_WRITE,
+ MTK_AUDIO_SMC_OP_BTCVSD_UPDATE_CTRL_CLEAR,
+ MTK_AUDIO_SMC_OP_BTCVSD_UPDATE_CTRL_UNDERFLOW,
+ MTK_AUDIO_SMC_OP_NUM
+};
+
+struct mtk_base_memif_data {
+ int id;
+ const char *name;
+ int reg_ofs_base;
+ int reg_ofs_cur;
+ int reg_ofs_end;
+ int reg_ofs_base_msb;
+ int reg_ofs_cur_msb;
+ int reg_ofs_end_msb;
+ int fs_reg;
+ int fs_shift;
+ int fs_maskbit;
+ int mono_reg;
+ int mono_shift;
+ int mono_invert;
+ int quad_ch_reg;
+ int quad_ch_mask;
+ int quad_ch_shift;
+ int int_odd_flag_reg;
+ int int_odd_flag_shift;
+ int enable_reg;
+ int enable_shift;
+ int hd_reg;
+ int hd_shift;
+ int hd_align_reg;
+ int hd_align_mshift;
+ int msb_reg;
+ int msb_shift;
+ int msb_end_reg;
+ int msb_end_shift;
+ int agent_disable_reg;
+ int agent_disable_shift;
+ int ch_num_reg;
+ int ch_num_shift;
+ int ch_num_maskbit;
+ /* playback memif only */
+ int pbuf_reg;
+ int pbuf_mask;
+ int pbuf_shift;
+ int minlen_reg;
+ int minlen_mask;
+ int minlen_shift;
+};
+
+struct mtk_base_irq_data {
+ int id;
+ int irq_cnt_reg;
+ int irq_cnt_shift;
+ int irq_cnt_maskbit;
+ int irq_fs_reg;
+ int irq_fs_shift;
+ int irq_fs_maskbit;
+ int irq_en_reg;
+ int irq_en_shift;
+ int irq_clr_reg;
+ int irq_clr_shift;
+ int irq_status_shift;
+};
+
+struct device;
+struct list_head;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct mtk_base_afe_dai;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+ void __iomem *base_addr;
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
+
+ unsigned int const *reg_back_up_list;
+ unsigned int *reg_back_up;
+ unsigned int reg_back_up_list_num;
+
+ int (*runtime_suspend)(struct device *dev);
+ int (*runtime_resume)(struct device *dev);
+ bool suspended;
+
+ struct mtk_base_afe_memif *memif;
+ int memif_size;
+ struct mtk_base_afe_irq *irqs;
+ int irqs_size;
+ int memif_32bit_supported;
+ bool preallocate_buffers;
+
+ struct list_head sub_dais;
+ struct snd_soc_dai_driver *dai_drivers;
+ unsigned int num_dai_drivers;
+
+ const struct snd_pcm_hardware *mtk_afe_hardware;
+ int (*memif_fs)(struct snd_pcm_substream *substream,
+ unsigned int rate);
+ int (*irq_fs)(struct snd_pcm_substream *substream,
+ unsigned int rate);
+ int (*get_dai_fs)(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate);
+ int (*get_memif_pbuf_size)(struct snd_pcm_substream *substream);
+
+ int (*request_dram_resource)(struct device *dev);
+ int (*release_dram_resource)(struct device *dev);
+
+ void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+ unsigned int phys_buf_addr;
+ int buffer_size;
+ struct snd_pcm_substream *substream;
+ const struct mtk_base_memif_data *data;
+ int irq_usage;
+ int const_irq;
+ unsigned char *dma_area;
+ dma_addr_t dma_addr;
+ size_t dma_bytes;
+};
+
+struct mtk_base_afe_irq {
+ const struct mtk_base_irq_data *irq_data;
+ int irq_occupyed;
+};
+
+struct mtk_base_afe_dai {
+ struct snd_soc_dai_driver *dai_drivers;
+ unsigned int num_dai_drivers;
+
+ const struct snd_kcontrol_new *controls;
+ unsigned int num_controls;
+ const struct snd_soc_dapm_widget *dapm_widgets;
+ unsigned int num_dapm_widgets;
+ const struct snd_soc_dapm_route *dapm_routes;
+ unsigned int num_dapm_routes;
+
+ struct list_head list;
+};
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c
new file mode 100644
index 000000000000..5e7e85b4c98a
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-btcvsd.c
@@ -0,0 +1,1410 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Mediatek ALSA BT SCO CVSD/MSBC Driver
+//
+// Copyright (c) 2019 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/sched/clock.h>
+
+#include <sound/soc.h>
+
+#define BTCVSD_SND_NAME "mtk-btcvsd-snd"
+
+#define BT_CVSD_TX_NREADY BIT(21)
+#define BT_CVSD_RX_READY BIT(22)
+#define BT_CVSD_TX_UNDERFLOW BIT(23)
+#define BT_CVSD_RX_OVERFLOW BIT(24)
+#define BT_CVSD_INTERRUPT BIT(31)
+
+#define BT_CVSD_CLEAR \
+ (BT_CVSD_TX_NREADY | BT_CVSD_RX_READY | BT_CVSD_TX_UNDERFLOW |\
+ BT_CVSD_RX_OVERFLOW | BT_CVSD_INTERRUPT)
+
+/* TX */
+#define SCO_TX_ENCODE_SIZE (60)
+/* 18 = 6 * 180 / SCO_TX_ENCODE_SIZE */
+#define SCO_TX_PACKER_BUF_NUM (18)
+
+/* RX */
+#define SCO_RX_PLC_SIZE (30)
+#define SCO_RX_PACKER_BUF_NUM (64)
+#define SCO_RX_PACKET_MASK (0x3F)
+
+#define SCO_CVSD_PACKET_VALID_SIZE 2
+
+#define SCO_PACKET_120 120
+#define SCO_PACKET_180 180
+
+#define BTCVSD_RX_PACKET_SIZE (SCO_RX_PLC_SIZE + SCO_CVSD_PACKET_VALID_SIZE)
+#define BTCVSD_TX_PACKET_SIZE (SCO_TX_ENCODE_SIZE)
+
+#define BTCVSD_RX_BUF_SIZE (BTCVSD_RX_PACKET_SIZE * SCO_RX_PACKER_BUF_NUM)
+#define BTCVSD_TX_BUF_SIZE (BTCVSD_TX_PACKET_SIZE * SCO_TX_PACKER_BUF_NUM)
+
+enum bt_sco_state {
+ BT_SCO_STATE_IDLE,
+ BT_SCO_STATE_RUNNING,
+ BT_SCO_STATE_ENDING,
+ BT_SCO_STATE_LOOPBACK,
+};
+
+enum bt_sco_direct {
+ BT_SCO_DIRECT_BT2ARM,
+ BT_SCO_DIRECT_ARM2BT,
+};
+
+enum bt_sco_packet_len {
+ BT_SCO_CVSD_30 = 0,
+ BT_SCO_CVSD_60,
+ BT_SCO_CVSD_90,
+ BT_SCO_CVSD_120,
+ BT_SCO_CVSD_10,
+ BT_SCO_CVSD_20,
+ BT_SCO_CVSD_MAX,
+};
+
+enum BT_SCO_BAND {
+ BT_SCO_NB,
+ BT_SCO_WB,
+};
+
+struct mtk_btcvsd_snd_hw_info {
+ unsigned int num_valid_addr;
+ unsigned long bt_sram_addr[20];
+ unsigned int packet_length;
+ unsigned int packet_num;
+};
+
+struct mtk_btcvsd_snd_stream {
+ struct snd_pcm_substream *substream;
+ int stream;
+
+ enum bt_sco_state state;
+
+ unsigned int packet_size;
+ unsigned int buf_size;
+ u8 temp_packet_buf[SCO_PACKET_180];
+
+ int packet_w;
+ int packet_r;
+ snd_pcm_uframes_t prev_frame;
+ int prev_packet_idx;
+
+ unsigned int xrun:1;
+ unsigned int timeout:1;
+ unsigned int mute:1;
+ unsigned int trigger_start:1;
+ unsigned int wait_flag:1;
+ unsigned int rw_cnt;
+
+ unsigned long long time_stamp;
+ unsigned long long buf_data_equivalent_time;
+
+ struct mtk_btcvsd_snd_hw_info buffer_info;
+};
+
+struct mtk_btcvsd_snd {
+ struct device *dev;
+ int irq_id;
+
+ struct regmap *infra;
+ void __iomem *bt_pkv_base;
+ void __iomem *bt_sram_bank2_base;
+
+ unsigned int infra_misc_offset;
+ unsigned int conn_bt_cvsd_mask;
+ unsigned int cvsd_mcu_read_offset;
+ unsigned int cvsd_mcu_write_offset;
+ unsigned int cvsd_packet_indicator;
+
+ u32 *bt_reg_pkt_r;
+ u32 *bt_reg_pkt_w;
+ u32 *bt_reg_ctl;
+
+ unsigned int irq_disabled:1;
+
+ spinlock_t tx_lock; /* spinlock for bt tx stream control */
+ spinlock_t rx_lock; /* spinlock for bt rx stream control */
+ wait_queue_head_t tx_wait;
+ wait_queue_head_t rx_wait;
+
+ struct mtk_btcvsd_snd_stream *tx;
+ struct mtk_btcvsd_snd_stream *rx;
+ u8 tx_packet_buf[BTCVSD_TX_BUF_SIZE];
+ u8 rx_packet_buf[BTCVSD_RX_BUF_SIZE];
+
+ enum BT_SCO_BAND band;
+};
+
+struct mtk_btcvsd_snd_time_buffer_info {
+ unsigned long long data_count_equi_time;
+ unsigned long long time_stamp_us;
+};
+
+static const unsigned int btsco_packet_valid_mask[BT_SCO_CVSD_MAX][6] = {
+ {0x1, 0x1 << 1, 0x1 << 2, 0x1 << 3, 0x1 << 4, 0x1 << 5},
+ {0x1, 0x1, 0x2, 0x2, 0x4, 0x4},
+ {0x1, 0x1, 0x1, 0x2, 0x2, 0x2},
+ {0x1, 0x1, 0x1, 0x1, 0x0, 0x0},
+ {0x7, 0x7 << 3, 0x7 << 6, 0x7 << 9, 0x7 << 12, 0x7 << 15},
+ {0x3, 0x3 << 1, 0x3 << 3, 0x3 << 4, 0x3 << 6, 0x3 << 7},
+};
+
+static const unsigned int btsco_packet_info[BT_SCO_CVSD_MAX][4] = {
+ {30, 6, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
+ SCO_PACKET_180 / SCO_RX_PLC_SIZE},
+ {60, 3, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
+ SCO_PACKET_180 / SCO_RX_PLC_SIZE},
+ {90, 2, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
+ SCO_PACKET_180 / SCO_RX_PLC_SIZE},
+ {120, 1, SCO_PACKET_120 / SCO_TX_ENCODE_SIZE,
+ SCO_PACKET_120 / SCO_RX_PLC_SIZE},
+ {10, 18, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
+ SCO_PACKET_180 / SCO_RX_PLC_SIZE},
+ {20, 9, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
+ SCO_PACKET_180 / SCO_RX_PLC_SIZE},
+};
+
+static const u8 table_msbc_silence[SCO_PACKET_180] = {
+ 0x01, 0x38, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00,
+ 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d,
+ 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
+ 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd,
+ 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
+ 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00,
+ 0x01, 0xc8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00,
+ 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d,
+ 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
+ 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd,
+ 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
+ 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00,
+ 0x01, 0xf8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00,
+ 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d,
+ 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
+ 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd,
+ 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
+ 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00
+};
+
+static void mtk_btcvsd_snd_irq_enable(struct mtk_btcvsd_snd *bt)
+{
+ regmap_update_bits(bt->infra, bt->infra_misc_offset,
+ bt->conn_bt_cvsd_mask, 0);
+}
+
+static void mtk_btcvsd_snd_irq_disable(struct mtk_btcvsd_snd *bt)
+{
+ regmap_update_bits(bt->infra, bt->infra_misc_offset,
+ bt->conn_bt_cvsd_mask, bt->conn_bt_cvsd_mask);
+}
+
+static void mtk_btcvsd_snd_set_state(struct mtk_btcvsd_snd *bt,
+ struct mtk_btcvsd_snd_stream *bt_stream,
+ int state)
+{
+ dev_dbg(bt->dev, "%s(), stream %d, state %d, tx->state %d, rx->state %d, irq_disabled %d\n",
+ __func__,
+ bt_stream->stream, state,
+ bt->tx->state, bt->rx->state, bt->irq_disabled);
+
+ bt_stream->state = state;
+
+ if (bt->tx->state == BT_SCO_STATE_IDLE &&
+ bt->rx->state == BT_SCO_STATE_IDLE) {
+ if (!bt->irq_disabled) {
+ disable_irq(bt->irq_id);
+ mtk_btcvsd_snd_irq_disable(bt);
+ bt->irq_disabled = 1;
+ }
+ } else {
+ if (bt->irq_disabled) {
+ enable_irq(bt->irq_id);
+ mtk_btcvsd_snd_irq_enable(bt);
+ bt->irq_disabled = 0;
+ }
+ }
+}
+
+static int mtk_btcvsd_snd_tx_init(struct mtk_btcvsd_snd *bt)
+{
+ memset(bt->tx, 0, sizeof(*bt->tx));
+ memset(bt->tx_packet_buf, 0, sizeof(bt->tx_packet_buf));
+
+ bt->tx->packet_size = BTCVSD_TX_PACKET_SIZE;
+ bt->tx->buf_size = BTCVSD_TX_BUF_SIZE;
+ bt->tx->timeout = 0;
+ bt->tx->rw_cnt = 0;
+ bt->tx->stream = SNDRV_PCM_STREAM_PLAYBACK;
+ return 0;
+}
+
+static int mtk_btcvsd_snd_rx_init(struct mtk_btcvsd_snd *bt)
+{
+ memset(bt->rx, 0, sizeof(*bt->rx));
+ memset(bt->rx_packet_buf, 0, sizeof(bt->rx_packet_buf));
+
+ bt->rx->packet_size = BTCVSD_RX_PACKET_SIZE;
+ bt->rx->buf_size = BTCVSD_RX_BUF_SIZE;
+ bt->rx->timeout = 0;
+ bt->rx->rw_cnt = 0;
+ bt->rx->stream = SNDRV_PCM_STREAM_CAPTURE;
+ return 0;
+}
+
+static void get_tx_time_stamp(struct mtk_btcvsd_snd *bt,
+ struct mtk_btcvsd_snd_time_buffer_info *ts)
+{
+ ts->time_stamp_us = bt->tx->time_stamp;
+ ts->data_count_equi_time = bt->tx->buf_data_equivalent_time;
+}
+
+static void get_rx_time_stamp(struct mtk_btcvsd_snd *bt,
+ struct mtk_btcvsd_snd_time_buffer_info *ts)
+{
+ ts->time_stamp_us = bt->rx->time_stamp;
+ ts->data_count_equi_time = bt->rx->buf_data_equivalent_time;
+}
+
+static int btcvsd_bytes_to_frame(struct snd_pcm_substream *substream,
+ int bytes)
+{
+ int count = bytes;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (runtime->format == SNDRV_PCM_FORMAT_S32_LE ||
+ runtime->format == SNDRV_PCM_FORMAT_U32_LE)
+ count = count >> 2;
+ else
+ count = count >> 1;
+
+ count = count / runtime->channels;
+ return count;
+}
+
+static void mtk_btcvsd_snd_data_transfer(enum bt_sco_direct dir,
+ u8 *src, u8 *dst,
+ unsigned int blk_size,
+ unsigned int blk_num)
+{
+ unsigned int i, j;
+
+ if (blk_size == 60 || blk_size == 120 || blk_size == 20) {
+ u32 *src_32 = (u32 *)src;
+ u32 *dst_32 = (u32 *)dst;
+
+ for (i = 0; i < (blk_size * blk_num / 4); i++)
+ *dst_32++ = *src_32++;
+ } else {
+ u16 *src_16 = (u16 *)src;
+ u16 *dst_16 = (u16 *)dst;
+
+ for (j = 0; j < blk_num; j++) {
+ for (i = 0; i < (blk_size / 2); i++)
+ *dst_16++ = *src_16++;
+
+ if (dir == BT_SCO_DIRECT_BT2ARM)
+ src_16++;
+ else
+ dst_16++;
+ }
+ }
+}
+
+/* write encoded mute data to bt sram */
+static int btcvsd_tx_clean_buffer(struct mtk_btcvsd_snd *bt)
+{
+ unsigned int i;
+ unsigned int num_valid_addr;
+ unsigned long flags;
+ enum BT_SCO_BAND band = bt->band;
+
+ /* prepare encoded mute data */
+ if (band == BT_SCO_NB)
+ memset(bt->tx->temp_packet_buf, 170, SCO_PACKET_180);
+ else
+ memcpy(bt->tx->temp_packet_buf,
+ table_msbc_silence, SCO_PACKET_180);
+
+ /* write mute data to bt tx sram buffer */
+ spin_lock_irqsave(&bt->tx_lock, flags);
+ num_valid_addr = bt->tx->buffer_info.num_valid_addr;
+
+ dev_info(bt->dev, "%s(), band %d, num_valid_addr %u\n",
+ __func__, band, num_valid_addr);
+
+ for (i = 0; i < num_valid_addr; i++) {
+ void *dst;
+
+ dev_info(bt->dev, "%s(), clean addr 0x%lx\n", __func__,
+ bt->tx->buffer_info.bt_sram_addr[i]);
+
+ dst = (void *)bt->tx->buffer_info.bt_sram_addr[i];
+
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
+ bt->tx->temp_packet_buf, dst,
+ bt->tx->buffer_info.packet_length,
+ bt->tx->buffer_info.packet_num);
+ }
+ spin_unlock_irqrestore(&bt->tx_lock, flags);
+
+ return 0;
+}
+
+static int mtk_btcvsd_read_from_bt(struct mtk_btcvsd_snd *bt,
+ enum bt_sco_packet_len packet_type,
+ unsigned int packet_length,
+ unsigned int packet_num,
+ unsigned int blk_size,
+ unsigned int control)
+{
+ unsigned int i;
+ int pv;
+ u8 *src;
+ unsigned int packet_buf_ofs;
+ unsigned long flags;
+ unsigned long connsys_addr_rx, ap_addr_rx;
+
+ connsys_addr_rx = *bt->bt_reg_pkt_r;
+ ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base +
+ (connsys_addr_rx & 0xFFFF);
+
+ if (connsys_addr_rx == 0xdeadfeed) {
+ /* bt return 0xdeadfeed if read register during bt sleep */
+ dev_warn(bt->dev, "%s(), connsys_addr_rx == 0xdeadfeed",
+ __func__);
+ return -EIO;
+ }
+
+ src = (u8 *)ap_addr_rx;
+
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src,
+ bt->rx->temp_packet_buf, packet_length,
+ packet_num);
+
+ spin_lock_irqsave(&bt->rx_lock, flags);
+ for (i = 0; i < blk_size; i++) {
+ packet_buf_ofs = (bt->rx->packet_w & SCO_RX_PACKET_MASK) *
+ bt->rx->packet_size;
+ memcpy(bt->rx_packet_buf + packet_buf_ofs,
+ bt->rx->temp_packet_buf + (SCO_RX_PLC_SIZE * i),
+ SCO_RX_PLC_SIZE);
+ if ((control & btsco_packet_valid_mask[packet_type][i]) ==
+ btsco_packet_valid_mask[packet_type][i])
+ pv = 1;
+ else
+ pv = 0;
+
+ packet_buf_ofs += SCO_RX_PLC_SIZE;
+ memcpy(bt->rx_packet_buf + packet_buf_ofs, (void *)&pv,
+ SCO_CVSD_PACKET_VALID_SIZE);
+ bt->rx->packet_w++;
+ }
+ spin_unlock_irqrestore(&bt->rx_lock, flags);
+ return 0;
+}
+
+static int mtk_btcvsd_write_to_bt(struct mtk_btcvsd_snd *bt,
+ enum bt_sco_packet_len packet_type,
+ unsigned int packet_length,
+ unsigned int packet_num,
+ unsigned int blk_size)
+{
+ unsigned int i;
+ unsigned long flags;
+ u8 *dst;
+ unsigned long connsys_addr_tx, ap_addr_tx;
+ bool new_ap_addr_tx = true;
+
+ connsys_addr_tx = *bt->bt_reg_pkt_w;
+ ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base +
+ (connsys_addr_tx & 0xFFFF);
+
+ if (connsys_addr_tx == 0xdeadfeed) {
+ /* bt return 0xdeadfeed if read register during bt sleep */
+ dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n",
+ __func__);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&bt->tx_lock, flags);
+ for (i = 0; i < blk_size; i++) {
+ memcpy(bt->tx->temp_packet_buf + (bt->tx->packet_size * i),
+ (bt->tx_packet_buf +
+ (bt->tx->packet_r % SCO_TX_PACKER_BUF_NUM) *
+ bt->tx->packet_size),
+ bt->tx->packet_size);
+
+ bt->tx->packet_r++;
+ }
+ spin_unlock_irqrestore(&bt->tx_lock, flags);
+
+ dst = (u8 *)ap_addr_tx;
+
+ if (!bt->tx->mute) {
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
+ bt->tx->temp_packet_buf, dst,
+ packet_length, packet_num);
+ }
+
+ /* store bt tx buffer sram info */
+ bt->tx->buffer_info.packet_length = packet_length;
+ bt->tx->buffer_info.packet_num = packet_num;
+ for (i = 0; i < bt->tx->buffer_info.num_valid_addr; i++) {
+ if (bt->tx->buffer_info.bt_sram_addr[i] == ap_addr_tx) {
+ new_ap_addr_tx = false;
+ break;
+ }
+ }
+ if (new_ap_addr_tx) {
+ unsigned int next_idx;
+
+ spin_lock_irqsave(&bt->tx_lock, flags);
+ bt->tx->buffer_info.num_valid_addr++;
+ next_idx = bt->tx->buffer_info.num_valid_addr - 1;
+ bt->tx->buffer_info.bt_sram_addr[next_idx] = ap_addr_tx;
+ spin_unlock_irqrestore(&bt->tx_lock, flags);
+ dev_info(bt->dev, "%s(), new ap_addr_tx = 0x%lx, num_valid_addr %d\n",
+ __func__, ap_addr_tx,
+ bt->tx->buffer_info.num_valid_addr);
+ }
+
+ if (bt->tx->mute)
+ btcvsd_tx_clean_buffer(bt);
+
+ return 0;
+}
+
+static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_btcvsd_snd *bt = dev;
+ unsigned int packet_type, packet_num, packet_length;
+ unsigned int buf_cnt_tx, buf_cnt_rx, control;
+
+ if (bt->rx->state != BT_SCO_STATE_RUNNING &&
+ bt->rx->state != BT_SCO_STATE_ENDING &&
+ bt->tx->state != BT_SCO_STATE_RUNNING &&
+ bt->tx->state != BT_SCO_STATE_ENDING &&
+ bt->tx->state != BT_SCO_STATE_LOOPBACK) {
+ dev_warn(bt->dev, "%s(), in idle state: rx->state: %d, tx->state: %d\n",
+ __func__, bt->rx->state, bt->tx->state);
+ goto irq_handler_exit;
+ }
+
+ control = *bt->bt_reg_ctl;
+ packet_type = (control >> 18) & 0x7;
+
+ if (((control >> 31) & 1) == 0) {
+ dev_warn(bt->dev, "%s(), ((control >> 31) & 1) == 0, control 0x%x\n",
+ __func__, control);
+ goto irq_handler_exit;
+ }
+
+ if (packet_type >= BT_SCO_CVSD_MAX) {
+ dev_warn(bt->dev, "%s(), invalid packet_type %u, exit\n",
+ __func__, packet_type);
+ goto irq_handler_exit;
+ }
+
+ packet_length = btsco_packet_info[packet_type][0];
+ packet_num = btsco_packet_info[packet_type][1];
+ buf_cnt_tx = btsco_packet_info[packet_type][2];
+ buf_cnt_rx = btsco_packet_info[packet_type][3];
+
+ if (bt->tx->state == BT_SCO_STATE_LOOPBACK) {
+ u8 *src, *dst;
+ unsigned long connsys_addr_rx, ap_addr_rx;
+ unsigned long connsys_addr_tx, ap_addr_tx;
+
+ connsys_addr_rx = *bt->bt_reg_pkt_r;
+ ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base +
+ (connsys_addr_rx & 0xFFFF);
+
+ connsys_addr_tx = *bt->bt_reg_pkt_w;
+ ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base +
+ (connsys_addr_tx & 0xFFFF);
+
+ if (connsys_addr_tx == 0xdeadfeed ||
+ connsys_addr_rx == 0xdeadfeed) {
+ /* bt return 0xdeadfeed if read reg during bt sleep */
+ dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n",
+ __func__);
+ goto irq_handler_exit;
+ }
+
+ src = (u8 *)ap_addr_rx;
+ dst = (u8 *)ap_addr_tx;
+
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src,
+ bt->tx->temp_packet_buf,
+ packet_length,
+ packet_num);
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
+ bt->tx->temp_packet_buf, dst,
+ packet_length,
+ packet_num);
+ bt->rx->rw_cnt++;
+ bt->tx->rw_cnt++;
+ }
+
+ if (bt->rx->state == BT_SCO_STATE_RUNNING ||
+ bt->rx->state == BT_SCO_STATE_ENDING) {
+ if (bt->rx->xrun) {
+ if (bt->rx->packet_w - bt->rx->packet_r <=
+ SCO_RX_PACKER_BUF_NUM - 2 * buf_cnt_rx) {
+ /*
+ * free space is larger then
+ * twice interrupt rx data size
+ */
+ bt->rx->xrun = 0;
+ dev_warn(bt->dev, "%s(), rx->xrun 0!\n",
+ __func__);
+ }
+ }
+
+ if (!bt->rx->xrun &&
+ (bt->rx->packet_w - bt->rx->packet_r <=
+ SCO_RX_PACKER_BUF_NUM - buf_cnt_rx)) {
+ mtk_btcvsd_read_from_bt(bt,
+ packet_type,
+ packet_length,
+ packet_num,
+ buf_cnt_rx,
+ control);
+ bt->rx->rw_cnt++;
+ } else {
+ bt->rx->xrun = 1;
+ dev_warn(bt->dev, "%s(), rx->xrun 1\n", __func__);
+ }
+ }
+
+ /* tx */
+ bt->tx->timeout = 0;
+ if ((bt->tx->state == BT_SCO_STATE_RUNNING ||
+ bt->tx->state == BT_SCO_STATE_ENDING) &&
+ bt->tx->trigger_start) {
+ if (bt->tx->xrun) {
+ /* prepared data is larger then twice
+ * interrupt tx data size
+ */
+ if (bt->tx->packet_w - bt->tx->packet_r >=
+ 2 * buf_cnt_tx) {
+ bt->tx->xrun = 0;
+ dev_warn(bt->dev, "%s(), tx->xrun 0\n",
+ __func__);
+ }
+ }
+
+ if ((!bt->tx->xrun &&
+ (bt->tx->packet_w - bt->tx->packet_r >= buf_cnt_tx)) ||
+ bt->tx->state == BT_SCO_STATE_ENDING) {
+ mtk_btcvsd_write_to_bt(bt,
+ packet_type,
+ packet_length,
+ packet_num,
+ buf_cnt_tx);
+ bt->tx->rw_cnt++;
+ } else {
+ bt->tx->xrun = 1;
+ dev_warn(bt->dev, "%s(), tx->xrun 1\n", __func__);
+ }
+ }
+
+ *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR;
+
+ if (bt->rx->state == BT_SCO_STATE_RUNNING ||
+ bt->rx->state == BT_SCO_STATE_ENDING) {
+ bt->rx->wait_flag = 1;
+ wake_up_interruptible(&bt->rx_wait);
+ snd_pcm_period_elapsed(bt->rx->substream);
+ }
+ if (bt->tx->state == BT_SCO_STATE_RUNNING ||
+ bt->tx->state == BT_SCO_STATE_ENDING) {
+ bt->tx->wait_flag = 1;
+ wake_up_interruptible(&bt->tx_wait);
+ snd_pcm_period_elapsed(bt->tx->substream);
+ }
+
+ return IRQ_HANDLED;
+irq_handler_exit:
+ *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR;
+ return IRQ_HANDLED;
+}
+
+static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt,
+ struct mtk_btcvsd_snd_stream *bt_stream)
+{
+ unsigned long long t1, t2;
+ /* one interrupt period = 22.5ms */
+ unsigned long long timeout_limit = 22500000;
+ int max_timeout_trial = 2;
+ int ret;
+
+ bt_stream->wait_flag = 0;
+
+ while (max_timeout_trial && !bt_stream->wait_flag) {
+ t1 = sched_clock();
+ if (bt_stream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = wait_event_interruptible_timeout(bt->tx_wait,
+ bt_stream->wait_flag,
+ nsecs_to_jiffies(timeout_limit));
+ } else {
+ ret = wait_event_interruptible_timeout(bt->rx_wait,
+ bt_stream->wait_flag,
+ nsecs_to_jiffies(timeout_limit));
+ }
+
+ t2 = sched_clock();
+ t2 = t2 - t1; /* in ns (10^9) */
+
+ if (t2 > timeout_limit) {
+ dev_warn(bt->dev, "%s(), stream %d, timeout %llu, limit %llu, ret %d, flag %d\n",
+ __func__, bt_stream->stream,
+ t2, timeout_limit, ret,
+ bt_stream->wait_flag);
+ }
+
+ if (ret < 0) {
+ /*
+ * error, -ERESTARTSYS if it was interrupted by
+ * a signal
+ */
+ dev_warn(bt->dev, "%s(), stream %d, error, trial left %d\n",
+ __func__,
+ bt_stream->stream, max_timeout_trial);
+
+ bt_stream->timeout = 1;
+ return ret;
+ } else if (ret == 0) {
+ /* conidtion is false after timeout */
+ max_timeout_trial--;
+ dev_warn(bt->dev, "%s(), stream %d, error, timeout, condition is false, trial left %d\n",
+ __func__,
+ bt_stream->stream, max_timeout_trial);
+
+ if (max_timeout_trial <= 0) {
+ bt_stream->timeout = 1;
+ return -ETIME;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt,
+ struct iov_iter *buf,
+ size_t count)
+{
+ ssize_t read_size = 0, read_count = 0, cur_read_idx, cont;
+ unsigned long avail;
+ unsigned long flags;
+ unsigned int packet_size = bt->rx->packet_size;
+
+ while (count) {
+ spin_lock_irqsave(&bt->rx_lock, flags);
+ /* available data in RX packet buffer */
+ avail = (bt->rx->packet_w - bt->rx->packet_r) * packet_size;
+
+ cur_read_idx = (bt->rx->packet_r & SCO_RX_PACKET_MASK) *
+ packet_size;
+ spin_unlock_irqrestore(&bt->rx_lock, flags);
+
+ if (!avail) {
+ int ret = wait_for_bt_irq(bt, bt->rx);
+
+ if (ret)
+ return read_count;
+
+ continue;
+ }
+
+ /* count must be multiple of packet_size */
+ if (count % packet_size != 0 ||
+ avail % packet_size != 0) {
+ dev_warn(bt->dev, "%s(), count %zu or d %lu is not multiple of packet_size %dd\n",
+ __func__, count, avail, packet_size);
+
+ count -= count % packet_size;
+ avail -= avail % packet_size;
+ }
+
+ if (count > avail)
+ read_size = avail;
+ else
+ read_size = count;
+
+ /* calculate continue space */
+ cont = bt->rx->buf_size - cur_read_idx;
+ if (read_size > cont)
+ read_size = cont;
+
+ if (copy_to_iter(bt->rx_packet_buf + cur_read_idx,
+ read_size, buf) != read_size) {
+ dev_warn(bt->dev, "%s(), copy_to_iter fail\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&bt->rx_lock, flags);
+ bt->rx->packet_r += read_size / packet_size;
+ spin_unlock_irqrestore(&bt->rx_lock, flags);
+
+ read_count += read_size;
+ count -= read_size;
+ }
+
+ /*
+ * save current timestamp & buffer time in times_tamp and
+ * buf_data_equivalent_time
+ */
+ bt->rx->time_stamp = sched_clock();
+ bt->rx->buf_data_equivalent_time =
+ (unsigned long long)(bt->rx->packet_w - bt->rx->packet_r) *
+ SCO_RX_PLC_SIZE * 16 * 1000 / 2 / 64;
+ bt->rx->buf_data_equivalent_time += read_count * SCO_RX_PLC_SIZE *
+ 16 * 1000 / packet_size / 2 / 64;
+ /* return equivalent time(us) to data count */
+ bt->rx->buf_data_equivalent_time *= 1000;
+
+ return read_count;
+}
+
+static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt,
+ struct iov_iter *buf,
+ size_t count)
+{
+ int written_size = count, avail, cur_write_idx, write_size, cont;
+ unsigned long flags;
+ unsigned int packet_size = bt->tx->packet_size;
+
+ /*
+ * save current timestamp & buffer time in time_stamp and
+ * buf_data_equivalent_time
+ */
+ bt->tx->time_stamp = sched_clock();
+ bt->tx->buf_data_equivalent_time =
+ (unsigned long long)(bt->tx->packet_w - bt->tx->packet_r) *
+ packet_size * 16 * 1000 / 2 / 64;
+
+ /* return equivalent time(us) to data count */
+ bt->tx->buf_data_equivalent_time *= 1000;
+
+ while (count) {
+ spin_lock_irqsave(&bt->tx_lock, flags);
+ /* free space of TX packet buffer */
+ avail = bt->tx->buf_size -
+ (bt->tx->packet_w - bt->tx->packet_r) * packet_size;
+
+ cur_write_idx = (bt->tx->packet_w % SCO_TX_PACKER_BUF_NUM) *
+ packet_size;
+ spin_unlock_irqrestore(&bt->tx_lock, flags);
+
+ if (!avail) {
+ int ret = wait_for_bt_irq(bt, bt->tx);
+
+ if (ret)
+ return written_size;
+
+ continue;
+ }
+
+ /* count must be multiple of bt->tx->packet_size */
+ if (count % packet_size != 0 ||
+ avail % packet_size != 0) {
+ dev_warn(bt->dev, "%s(), count %zu or avail %d is not multiple of packet_size %d\n",
+ __func__, count, avail, packet_size);
+ count -= count % packet_size;
+ avail -= avail % packet_size;
+ }
+
+ if (count > avail)
+ write_size = avail;
+ else
+ write_size = count;
+
+ /* calculate continue space */
+ cont = bt->tx->buf_size - cur_write_idx;
+ if (write_size > cont)
+ write_size = cont;
+
+ if (copy_from_iter(bt->tx_packet_buf + cur_write_idx,
+ write_size, buf) != write_size) {
+ dev_warn(bt->dev, "%s(), copy_from_iter fail\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&bt->tx_lock, flags);
+ bt->tx->packet_w += write_size / packet_size;
+ spin_unlock_irqrestore(&bt->tx_lock, flags);
+ count -= write_size;
+ }
+
+ return written_size;
+}
+
+static struct mtk_btcvsd_snd_stream *get_bt_stream
+ (struct mtk_btcvsd_snd *bt, struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return bt->tx;
+ else
+ return bt->rx;
+}
+
+/* pcm ops */
+static const struct snd_pcm_hardware mtk_btcvsd_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .buffer_bytes_max = 24 * 1024,
+ .period_bytes_max = 24 * 1024,
+ .periods_min = 2,
+ .periods_max = 16,
+ .fifo_size = 0,
+};
+
+static int mtk_pcm_btcvsd_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ dev_dbg(bt->dev, "%s(), stream %d, substream %p\n",
+ __func__, substream->stream, substream);
+
+ snd_soc_set_runtime_hwparams(substream, &mtk_btcvsd_hardware);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = mtk_btcvsd_snd_tx_init(bt);
+ bt->tx->substream = substream;
+ } else {
+ ret = mtk_btcvsd_snd_rx_init(bt);
+ bt->rx->substream = substream;
+ }
+
+ return ret;
+}
+
+static int mtk_pcm_btcvsd_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+ struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
+
+ dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream);
+
+ mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_IDLE);
+ bt_stream->substream = NULL;
+ return 0;
+}
+
+static int mtk_pcm_btcvsd_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ params_buffer_bytes(hw_params) % bt->tx->packet_size != 0) {
+ dev_warn(bt->dev, "%s(), error, buffer size %d not valid\n",
+ __func__,
+ params_buffer_bytes(hw_params));
+ return -EINVAL;
+ }
+
+ substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+ return 0;
+}
+
+static int mtk_pcm_btcvsd_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ btcvsd_tx_clean_buffer(bt);
+
+ return 0;
+}
+
+static int mtk_pcm_btcvsd_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+ struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
+
+ dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream);
+
+ mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_RUNNING);
+ return 0;
+}
+
+static int mtk_pcm_btcvsd_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+ struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
+ int stream = substream->stream;
+ int hw_packet_ptr;
+
+ dev_dbg(bt->dev, "%s(), stream %d, cmd %d\n",
+ __func__, substream->stream, cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ hw_packet_ptr = stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ bt_stream->packet_r : bt_stream->packet_w;
+ bt_stream->prev_packet_idx = hw_packet_ptr;
+ bt_stream->prev_frame = 0;
+ bt_stream->trigger_start = 1;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ bt_stream->trigger_start = 0;
+ mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_ENDING);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer(
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+ struct mtk_btcvsd_snd_stream *bt_stream;
+ snd_pcm_uframes_t frame = 0;
+ int byte = 0;
+ int hw_packet_ptr;
+ int packet_diff;
+ spinlock_t *lock; /* spinlock for bt stream control */
+ unsigned long flags;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ lock = &bt->tx_lock;
+ bt_stream = bt->tx;
+ } else {
+ lock = &bt->rx_lock;
+ bt_stream = bt->rx;
+ }
+
+ spin_lock_irqsave(lock, flags);
+ hw_packet_ptr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ bt->tx->packet_r : bt->rx->packet_w;
+
+ /* get packet diff from last time */
+ if (hw_packet_ptr >= bt_stream->prev_packet_idx) {
+ packet_diff = hw_packet_ptr - bt_stream->prev_packet_idx;
+ } else {
+ /* integer overflow */
+ packet_diff = (INT_MAX - bt_stream->prev_packet_idx) +
+ (hw_packet_ptr - INT_MIN) + 1;
+ }
+ bt_stream->prev_packet_idx = hw_packet_ptr;
+
+ /* increased bytes */
+ byte = packet_diff * bt_stream->packet_size;
+
+ frame = btcvsd_bytes_to_frame(substream, byte);
+ frame += bt_stream->prev_frame;
+ frame %= substream->runtime->buffer_size;
+
+ bt_stream->prev_frame = frame;
+
+ spin_unlock_irqrestore(lock, flags);
+
+ return frame;
+}
+
+static int mtk_pcm_btcvsd_copy(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ struct iov_iter *buf, unsigned long count)
+{
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return mtk_btcvsd_snd_write(bt, buf, count);
+ else
+ return mtk_btcvsd_snd_read(bt, buf, count);
+}
+
+/* kcontrol */
+static const char *const btsco_band_str[] = {"NB", "WB"};
+
+static const struct soc_enum btcvsd_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_band_str), btsco_band_str),
+};
+
+static int btcvsd_band_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = bt->band;
+ return 0;
+}
+
+static int btcvsd_band_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ bt->band = ucontrol->value.integer.value[0];
+ dev_dbg(bt->dev, "%s(), band %d\n", __func__, bt->band);
+ return 0;
+}
+
+static int btcvsd_loopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+ bool lpbk_en = bt->tx->state == BT_SCO_STATE_LOOPBACK;
+
+ ucontrol->value.integer.value[0] = lpbk_en;
+ return 0;
+}
+
+static int btcvsd_loopback_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (ucontrol->value.integer.value[0]) {
+ mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_LOOPBACK);
+ mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_LOOPBACK);
+ } else {
+ mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_RUNNING);
+ mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_RUNNING);
+ }
+ return 0;
+}
+
+static int btcvsd_tx_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (!bt->tx) {
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+ }
+
+ ucontrol->value.integer.value[0] = bt->tx->mute;
+ return 0;
+}
+
+static int btcvsd_tx_mute_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (!bt->tx)
+ return 0;
+
+ bt->tx->mute = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int btcvsd_rx_irq_received_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (!bt->rx)
+ return 0;
+
+ ucontrol->value.integer.value[0] = bt->rx->rw_cnt ? 1 : 0;
+ return 0;
+}
+
+static int btcvsd_rx_timeout_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (!bt->rx)
+ return 0;
+
+ ucontrol->value.integer.value[0] = bt->rx->timeout;
+ bt->rx->timeout = 0;
+ return 0;
+}
+
+static int btcvsd_rx_timestamp_get(struct snd_kcontrol *kcontrol,
+ unsigned int __user *data, unsigned int size)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+ int ret = 0;
+ struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_rx;
+
+ if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info))
+ return -EINVAL;
+
+ get_rx_time_stamp(bt, &time_buffer_info_rx);
+
+ dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu",
+ __func__,
+ time_buffer_info_rx.time_stamp_us,
+ time_buffer_info_rx.data_count_equi_time);
+
+ if (copy_to_user(data, &time_buffer_info_rx,
+ sizeof(struct mtk_btcvsd_snd_time_buffer_info))) {
+ dev_warn(bt->dev, "%s(), copy_to_user fail", __func__);
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static int btcvsd_tx_irq_received_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (!bt->tx)
+ return 0;
+
+ ucontrol->value.integer.value[0] = bt->tx->rw_cnt ? 1 : 0;
+ return 0;
+}
+
+static int btcvsd_tx_timeout_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = bt->tx->timeout;
+ return 0;
+}
+
+static int btcvsd_tx_timestamp_get(struct snd_kcontrol *kcontrol,
+ unsigned int __user *data, unsigned int size)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+ int ret = 0;
+ struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_tx;
+
+ if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info))
+ return -EINVAL;
+
+ get_tx_time_stamp(bt, &time_buffer_info_tx);
+
+ dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu",
+ __func__,
+ time_buffer_info_tx.time_stamp_us,
+ time_buffer_info_tx.data_count_equi_time);
+
+ if (copy_to_user(data, &time_buffer_info_tx,
+ sizeof(struct mtk_btcvsd_snd_time_buffer_info))) {
+ dev_warn(bt->dev, "%s(), copy_to_user fail", __func__);
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new mtk_btcvsd_snd_controls[] = {
+ SOC_ENUM_EXT("BTCVSD Band", btcvsd_enum[0],
+ btcvsd_band_get, btcvsd_band_set),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Loopback Switch", 0,
+ btcvsd_loopback_get, btcvsd_loopback_set),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Tx Mute Switch", 0,
+ btcvsd_tx_mute_get, btcvsd_tx_mute_set),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Tx Irq Received Switch", 0,
+ btcvsd_tx_irq_received_get, NULL),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Tx Timeout Switch", 0,
+ btcvsd_tx_timeout_get, NULL),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Rx Irq Received Switch", 0,
+ btcvsd_rx_irq_received_get, NULL),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Rx Timeout Switch", 0,
+ btcvsd_rx_timeout_get, NULL),
+ SND_SOC_BYTES_TLV("BTCVSD Rx Timestamp",
+ sizeof(struct mtk_btcvsd_snd_time_buffer_info),
+ btcvsd_rx_timestamp_get, NULL),
+ SND_SOC_BYTES_TLV("BTCVSD Tx Timestamp",
+ sizeof(struct mtk_btcvsd_snd_time_buffer_info),
+ btcvsd_tx_timestamp_get, NULL),
+};
+
+static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component)
+{
+ return snd_soc_add_component_controls(component,
+ mtk_btcvsd_snd_controls,
+ ARRAY_SIZE(mtk_btcvsd_snd_controls));
+}
+
+static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = {
+ .name = BTCVSD_SND_NAME,
+ .probe = mtk_btcvsd_snd_component_probe,
+ .open = mtk_pcm_btcvsd_open,
+ .close = mtk_pcm_btcvsd_close,
+ .hw_params = mtk_pcm_btcvsd_hw_params,
+ .hw_free = mtk_pcm_btcvsd_hw_free,
+ .prepare = mtk_pcm_btcvsd_prepare,
+ .trigger = mtk_pcm_btcvsd_trigger,
+ .pointer = mtk_pcm_btcvsd_pointer,
+ .copy = mtk_pcm_btcvsd_copy,
+};
+
+static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq_id;
+ u32 offset[5] = {0, 0, 0, 0, 0};
+ struct mtk_btcvsd_snd *btcvsd;
+ struct device *dev = &pdev->dev;
+
+ /* init btcvsd private data */
+ btcvsd = devm_kzalloc(dev, sizeof(*btcvsd), GFP_KERNEL);
+ if (!btcvsd)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, btcvsd);
+ btcvsd->dev = dev;
+
+ /* init tx/rx */
+ btcvsd->rx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->rx), GFP_KERNEL);
+ if (!btcvsd->rx)
+ return -ENOMEM;
+
+ btcvsd->tx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->tx), GFP_KERNEL);
+ if (!btcvsd->tx)
+ return -ENOMEM;
+
+ spin_lock_init(&btcvsd->tx_lock);
+ spin_lock_init(&btcvsd->rx_lock);
+
+ init_waitqueue_head(&btcvsd->tx_wait);
+ init_waitqueue_head(&btcvsd->rx_wait);
+
+ mtk_btcvsd_snd_tx_init(btcvsd);
+ mtk_btcvsd_snd_rx_init(btcvsd);
+
+ /* irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id <= 0)
+ return irq_id < 0 ? irq_id : -ENXIO;
+
+ ret = devm_request_irq(dev, irq_id, mtk_btcvsd_snd_irq_handler,
+ IRQF_TRIGGER_LOW, "BTCVSD_ISR_Handle",
+ (void *)btcvsd);
+ if (ret) {
+ dev_err(dev, "could not request_irq for BTCVSD_ISR_Handle\n");
+ return ret;
+ }
+
+ btcvsd->irq_id = irq_id;
+
+ /* iomap */
+ btcvsd->bt_pkv_base = of_iomap(dev->of_node, 0);
+ if (!btcvsd->bt_pkv_base) {
+ dev_err(dev, "iomap bt_pkv_base fail\n");
+ return -EIO;
+ }
+
+ btcvsd->bt_sram_bank2_base = of_iomap(dev->of_node, 1);
+ if (!btcvsd->bt_sram_bank2_base) {
+ dev_err(dev, "iomap bt_sram_bank2_base fail\n");
+ ret = -EIO;
+ goto unmap_pkv_err;
+ }
+
+ btcvsd->infra = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "mediatek,infracfg");
+ if (IS_ERR(btcvsd->infra)) {
+ dev_err(dev, "cannot find infra controller: %ld\n",
+ PTR_ERR(btcvsd->infra));
+ ret = PTR_ERR(btcvsd->infra);
+ goto unmap_bank2_err;
+ }
+
+ /* get offset */
+ ret = of_property_read_u32_array(dev->of_node, "mediatek,offset",
+ offset,
+ ARRAY_SIZE(offset));
+ if (ret) {
+ dev_warn(dev, "%s(), get offset fail, ret %d\n", __func__, ret);
+ goto unmap_bank2_err;
+ }
+ btcvsd->infra_misc_offset = offset[0];
+ btcvsd->conn_bt_cvsd_mask = offset[1];
+ btcvsd->cvsd_mcu_read_offset = offset[2];
+ btcvsd->cvsd_mcu_write_offset = offset[3];
+ btcvsd->cvsd_packet_indicator = offset[4];
+
+ btcvsd->bt_reg_pkt_r = btcvsd->bt_pkv_base +
+ btcvsd->cvsd_mcu_read_offset;
+ btcvsd->bt_reg_pkt_w = btcvsd->bt_pkv_base +
+ btcvsd->cvsd_mcu_write_offset;
+ btcvsd->bt_reg_ctl = btcvsd->bt_pkv_base +
+ btcvsd->cvsd_packet_indicator;
+
+ /* init state */
+ mtk_btcvsd_snd_set_state(btcvsd, btcvsd->tx, BT_SCO_STATE_IDLE);
+ mtk_btcvsd_snd_set_state(btcvsd, btcvsd->rx, BT_SCO_STATE_IDLE);
+
+ ret = devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform,
+ NULL, 0);
+ if (ret)
+ goto unmap_bank2_err;
+
+ return 0;
+
+unmap_bank2_err:
+ iounmap(btcvsd->bt_sram_bank2_base);
+unmap_pkv_err:
+ iounmap(btcvsd->bt_pkv_base);
+ return ret;
+}
+
+static void mtk_btcvsd_snd_remove(struct platform_device *pdev)
+{
+ struct mtk_btcvsd_snd *btcvsd = dev_get_drvdata(&pdev->dev);
+
+ iounmap(btcvsd->bt_pkv_base);
+ iounmap(btcvsd->bt_sram_bank2_base);
+}
+
+static const struct of_device_id mtk_btcvsd_snd_dt_match[] = {
+ { .compatible = "mediatek,mtk-btcvsd-snd", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_btcvsd_snd_dt_match);
+
+static struct platform_driver mtk_btcvsd_snd_driver = {
+ .driver = {
+ .name = "mtk-btcvsd-snd",
+ .of_match_table = mtk_btcvsd_snd_dt_match,
+ },
+ .probe = mtk_btcvsd_snd_probe,
+ .remove = mtk_btcvsd_snd_remove,
+};
+
+module_platform_driver(mtk_btcvsd_snd_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA BT SCO CVSD/MSBC Driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/common/mtk-dai-adda-common.c b/sound/soc/mediatek/common/mtk-dai-adda-common.c
new file mode 100644
index 000000000000..4dc1412489d6
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-dai-adda-common.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI ADDA Common
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2024 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+
+#include "mtk-base-afe.h"
+#include "mtk-dai-adda-common.h"
+
+unsigned int mtk_adda_dl_rate_transform(struct mtk_base_afe *afe, u32 rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_DL_RATE_8K;
+ case 11025:
+ return MTK_AFE_ADDA_DL_RATE_11K;
+ case 12000:
+ return MTK_AFE_ADDA_DL_RATE_12K;
+ case 16000:
+ return MTK_AFE_ADDA_DL_RATE_16K;
+ case 22050:
+ return MTK_AFE_ADDA_DL_RATE_22K;
+ case 24000:
+ return MTK_AFE_ADDA_DL_RATE_24K;
+ case 32000:
+ return MTK_AFE_ADDA_DL_RATE_32K;
+ case 44100:
+ return MTK_AFE_ADDA_DL_RATE_44K;
+ case 48000:
+ return MTK_AFE_ADDA_DL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_DL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_DL_RATE_192K;
+ default:
+ dev_info(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
+ __func__, rate);
+ return MTK_AFE_ADDA_DL_RATE_48K;
+ }
+}
+EXPORT_SYMBOL_GPL(mtk_adda_dl_rate_transform);
+
+unsigned int mtk_adda_ul_rate_transform(struct mtk_base_afe *afe, u32 rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_UL_RATE_8K;
+ case 16000:
+ return MTK_AFE_ADDA_UL_RATE_16K;
+ case 32000:
+ return MTK_AFE_ADDA_UL_RATE_32K;
+ case 48000:
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_UL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_UL_RATE_192K;
+ default:
+ dev_info(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
+ __func__, rate);
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ }
+}
+EXPORT_SYMBOL_GPL(mtk_adda_ul_rate_transform);
diff --git a/sound/soc/mediatek/common/mtk-dai-adda-common.h b/sound/soc/mediatek/common/mtk-dai-adda-common.h
new file mode 100644
index 000000000000..208b0dd89f57
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-dai-adda-common.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2024 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#ifndef _MTK_DAI_ADDA_COMMON_H_
+#define _MTK_DAI_ADDA_COMMON_H_
+
+struct mtk_base_afe;
+
+enum adda_input_mode_rate {
+ MTK_AFE_ADDA_DL_RATE_8K = 0,
+ MTK_AFE_ADDA_DL_RATE_11K = 1,
+ MTK_AFE_ADDA_DL_RATE_12K = 2,
+ MTK_AFE_ADDA_DL_RATE_16K = 3,
+ MTK_AFE_ADDA_DL_RATE_22K = 4,
+ MTK_AFE_ADDA_DL_RATE_24K = 5,
+ MTK_AFE_ADDA_DL_RATE_32K = 6,
+ MTK_AFE_ADDA_DL_RATE_44K = 7,
+ MTK_AFE_ADDA_DL_RATE_48K = 8,
+ MTK_AFE_ADDA_DL_RATE_96K = 9,
+ MTK_AFE_ADDA_DL_RATE_192K = 10,
+};
+
+enum adda_voice_mode_rate {
+ MTK_AFE_ADDA_UL_RATE_8K = 0,
+ MTK_AFE_ADDA_UL_RATE_16K = 1,
+ MTK_AFE_ADDA_UL_RATE_32K = 2,
+ MTK_AFE_ADDA_UL_RATE_48K = 3,
+ MTK_AFE_ADDA_UL_RATE_96K = 4,
+ MTK_AFE_ADDA_UL_RATE_192K = 5,
+ MTK_AFE_ADDA_UL_RATE_48K_HD = 6,
+};
+
+enum adda_rxif_delay_data {
+ DELAY_DATA_MISO1 = 0,
+ DELAY_DATA_MISO0 = 1,
+ DELAY_DATA_MISO2 = 1,
+};
+
+unsigned int mtk_adda_dl_rate_transform(struct mtk_base_afe *afe, u32 rate);
+unsigned int mtk_adda_ul_rate_transform(struct mtk_base_afe *afe, u32 rate);
+#endif
diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c
new file mode 100644
index 000000000000..fd10616a08a0
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mtk-dsp-sof-common.c -- MediaTek dsp sof common ctrl
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chunxu Li <chunxu.li@mediatek.com>
+ */
+
+#include "mtk-dsp-sof-common.h"
+#include "mtk-soc-card.h"
+
+/* fixup the BE DAI link to match any values from topology */
+int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
+ int i, j, ret = 0;
+
+ for (i = 0; i < sof_priv->num_streams; i++) {
+ struct snd_soc_dai *cpu_dai;
+ struct snd_soc_pcm_runtime *runtime;
+ struct snd_soc_dai_link *sof_dai_link = NULL;
+ const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
+
+ if (conn->normal_link && strcmp(rtd->dai_link->name, conn->normal_link))
+ continue;
+
+ for_each_card_rtds(card, runtime) {
+ if (strcmp(runtime->dai_link->name, conn->sof_link))
+ continue;
+
+ for_each_rtd_cpu_dais(runtime, j, cpu_dai) {
+ if (snd_soc_dai_stream_active(cpu_dai, conn->stream_dir) > 0) {
+ sof_dai_link = runtime->dai_link;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (sof_dai_link && sof_dai_link->be_hw_params_fixup)
+ ret = sof_dai_link->be_hw_params_fixup(runtime, params);
+
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_sof_dai_link_fixup);
+
+int mtk_sof_card_probe(struct snd_soc_card *card)
+{
+ int i;
+ struct snd_soc_dai_link *dai_link;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+
+ /* Set stream_name to help sof bind widgets */
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->no_pcm && !dai_link->stream_name && dai_link->name)
+ dai_link->stream_name = dai_link->name;
+ }
+
+ INIT_LIST_HEAD(&soc_card_data->sof_dai_link_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_sof_card_probe);
+
+static struct snd_soc_pcm_runtime *mtk_sof_find_tplg_be(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
+ struct snd_soc_pcm_runtime *fe;
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+ int i, stream;
+
+ for_each_pcm_streams(stream) {
+ fe = NULL;
+ for_each_dpcm_fe(rtd, stream, dpcm) {
+ fe = dpcm->fe;
+ if (fe)
+ break;
+ }
+
+ if (!fe)
+ continue;
+
+ for_each_dpcm_be(fe, stream, dpcm) {
+ be = dpcm->be;
+ if (be == rtd)
+ continue;
+
+ for (i = 0; i < sof_priv->num_streams; i++) {
+ const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
+
+ if (!strcmp(be->dai_link->name, conn->sof_link))
+ return be;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* fixup the BE DAI link to match any values from topology */
+static int mtk_sof_check_tplg_be_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
+ struct snd_soc_pcm_runtime *sof_be;
+ struct mtk_dai_link *dai_link;
+ int ret = 0;
+
+ sof_be = mtk_sof_find_tplg_be(rtd);
+ if (sof_be) {
+ if (sof_priv->sof_dai_link_fixup)
+ ret = sof_priv->sof_dai_link_fixup(rtd, params);
+ else if (sof_be->dai_link->be_hw_params_fixup)
+ ret = sof_be->dai_link->be_hw_params_fixup(sof_be, params);
+ } else {
+ list_for_each_entry(dai_link, &soc_card_data->sof_dai_link_list, list) {
+ if (strcmp(dai_link->name, rtd->dai_link->name) == 0) {
+ if (dai_link->be_hw_params_fixup)
+ ret = dai_link->be_hw_params_fixup(rtd, params);
+
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int mtk_sof_card_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_component *sof_comp = NULL;
+ struct mtk_soc_card_data *soc_card_data =
+ snd_soc_card_get_drvdata(card);
+ const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
+ struct snd_soc_dai_link *dai_link;
+ struct mtk_dai_link *mtk_dai_link;
+ int i;
+
+ /* 1. find sof component */
+ for_each_card_rtds(card, rtd) {
+ sof_comp = snd_soc_rtdcom_lookup(rtd, "sof-audio-component");
+ if (sof_comp)
+ break;
+ }
+
+ if (!sof_comp) {
+ dev_info(card->dev, "probe without sof-audio-component\n");
+ return 0;
+ }
+
+ /* 2. overwrite all BE fixups, and backup the existing fixup */
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->be_hw_params_fixup) {
+ mtk_dai_link = devm_kzalloc(card->dev,
+ sizeof(*mtk_dai_link),
+ GFP_KERNEL);
+ if (!mtk_dai_link)
+ return -ENOMEM;
+
+ mtk_dai_link->be_hw_params_fixup = dai_link->be_hw_params_fixup;
+ mtk_dai_link->name = dai_link->name;
+
+ list_add(&mtk_dai_link->list, &soc_card_data->sof_dai_link_list);
+ }
+
+ if (dai_link->no_pcm)
+ dai_link->be_hw_params_fixup = mtk_sof_check_tplg_be_dai_link_fixup;
+ }
+
+ /* 3. add route path and SOF_BE fixup callback */
+ for (i = 0; i < sof_priv->num_streams; i++) {
+ const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
+ struct snd_soc_pcm_runtime *sof_rtd = NULL;
+
+ for_each_card_rtds(card, rtd) {
+ if (!strcmp(rtd->dai_link->name, conn->sof_link)) {
+ sof_rtd = rtd;
+ break;
+ }
+ }
+ if (sof_rtd) {
+ int j;
+ struct snd_soc_dai *cpu_dai;
+
+ for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) {
+ struct snd_soc_dapm_route route;
+ struct snd_soc_dapm_path *p = NULL;
+ struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(cpu_dai, conn->stream_dir);
+
+ memset(&route, 0, sizeof(route));
+ if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && widget) {
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ route.source = conn->sof_dma;
+ route.sink = p->sink->name;
+ snd_soc_dapm_add_routes(dapm, &route, 1);
+ }
+ } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && widget) {
+ snd_soc_dapm_widget_for_each_source_path(widget, p) {
+ route.source = p->source->name;
+ route.sink = conn->sof_dma;
+ snd_soc_dapm_add_routes(dapm, &route, 1);
+ }
+ } else {
+ dev_err(cpu_dai->dev, "stream dir and widget not pair\n");
+ }
+ }
+
+ /* overwrite SOF BE fixup */
+ sof_rtd->dai_link->be_hw_params_fixup =
+ sof_comp->driver->be_hw_params_fixup;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_sof_card_late_probe);
+
+int mtk_sof_dailink_parse_of(struct snd_soc_card *card, struct device_node *np,
+ const char *propname, struct snd_soc_dai_link *pre_dai_links,
+ int pre_num_links)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *parsed_dai_link;
+ const char *dai_name = NULL;
+ int i, j, ret, num_links, parsed_num_links = 0;
+
+ num_links = of_property_count_strings(np, "mediatek,dai-link");
+ if (num_links < 0 || num_links > card->num_links) {
+ dev_dbg(dev, "number of dai-link is invalid\n");
+ return -EINVAL;
+ }
+
+ parsed_dai_link = devm_kcalloc(dev, num_links, sizeof(*parsed_dai_link), GFP_KERNEL);
+ if (!parsed_dai_link)
+ return -ENOMEM;
+
+ for (i = 0; i < num_links; i++) {
+ ret = of_property_read_string_index(np, propname, i, &dai_name);
+ if (ret) {
+ dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n",
+ propname, i, ret);
+ return ret;
+ }
+ dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name);
+ for (j = 0; j < pre_num_links; j++) {
+ if (!strcmp(dai_name, pre_dai_links[j].name)) {
+ memcpy(&parsed_dai_link[parsed_num_links++], &pre_dai_links[j],
+ sizeof(struct snd_soc_dai_link));
+ break;
+ }
+ }
+ }
+
+ if (parsed_num_links != num_links)
+ return -EINVAL;
+
+ card->dai_link = parsed_dai_link;
+ card->num_links = parsed_num_links;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_sof_dailink_parse_of);
diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.h b/sound/soc/mediatek/common/mtk-dsp-sof-common.h
new file mode 100644
index 000000000000..8784ee471132
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-dsp-sof-common.h -- MediaTek dsp sof common definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chunxu Li <chunxu.li@mediatek.com>
+ */
+
+#ifndef _MTK_DSP_SOF_COMMON_H_
+#define _MTK_DSP_SOF_COMMON_H_
+
+#include <sound/soc.h>
+
+struct sof_conn_stream {
+ const char *normal_link;
+ const char *sof_link;
+ const char *sof_dma;
+ int stream_dir;
+};
+
+struct mtk_dai_link {
+ const char *name;
+ int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+ struct list_head list;
+};
+
+struct mtk_sof_priv {
+ const struct sof_conn_stream *conn_streams;
+ int num_streams;
+ int (*sof_dai_link_fixup)(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+};
+
+int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+int mtk_sof_card_probe(struct snd_soc_card *card);
+int mtk_sof_card_late_probe(struct snd_soc_card *card);
+int mtk_sof_dailink_parse_of(struct snd_soc_card *card, struct device_node *np,
+ const char *propname, struct snd_soc_dai_link *pre_dai_links,
+ int pre_num_links);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-soc-card.h b/sound/soc/mediatek/common/mtk-soc-card.h
new file mode 100644
index 000000000000..a1d2794ac1f7
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-soc-card.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-soc-card.h -- MediaTek soc card data definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chunxu Li <chunxu.li@mediatek.com>
+ */
+
+#ifndef _MTK_SOC_CARD_H_
+#define _MTK_SOC_CARD_H_
+
+struct mtk_platform_card_data;
+struct mtk_sof_priv;
+
+struct mtk_soc_card_data {
+ const struct mtk_sof_priv *sof_priv;
+ struct list_head sof_dai_link_list;
+ struct mtk_platform_card_data *card_data;
+ struct snd_soc_component *accdet;
+ void *mach_priv;
+};
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c
new file mode 100644
index 000000000000..a2a30a87a359
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mtk-soundcard-driver.c -- MediaTek soundcard driver common
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+#include "mtk-dsp-sof-common.h"
+#include "mtk-soc-card.h"
+#include "mtk-soundcard-driver.h"
+
+static int set_card_codec_info(struct snd_soc_card *card,
+ struct device_node *sub_node,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct device *dev = card->dev;
+ struct device_node *codec_node;
+ int ret;
+
+ codec_node = of_get_child_by_name(sub_node, "codec");
+ if (!codec_node) {
+ dev_dbg(dev, "%s no specified codec: setting dummy.\n", dai_link->name);
+
+ dai_link->codecs = &snd_soc_dummy_dlc;
+ dai_link->num_codecs = 1;
+ dai_link->dynamic = 1;
+ return 0;
+ }
+
+ /* set card codec info */
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);
+
+ of_node_put(codec_node);
+
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%s: codec dai not found\n",
+ dai_link->name);
+
+ return 0;
+}
+
+static int set_dailink_daifmt(struct snd_soc_card *card,
+ struct device_node *sub_node,
+ struct snd_soc_dai_link *dai_link)
+{
+ unsigned int daifmt;
+ const char *str;
+ int ret;
+ struct {
+ char *name;
+ unsigned int val;
+ } of_clk_table[] = {
+ { "cpu", SND_SOC_DAIFMT_CBC_CFC },
+ { "codec", SND_SOC_DAIFMT_CBP_CFP },
+ };
+
+ daifmt = snd_soc_daifmt_parse_format(sub_node, NULL);
+ if (daifmt) {
+ dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ dai_link->dai_fmt |= daifmt;
+ }
+
+ /*
+ * check "mediatek,clk-provider = xxx"
+ * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area
+ */
+ ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str);
+ if (ret == 0) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) {
+ if (strcmp(str, of_clk_table[i].name) == 0) {
+ dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ dai_link->dai_fmt |= of_clk_table[i].val;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int parse_dai_link_info(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *dai_link;
+ const char *dai_link_name;
+ int ret, i;
+
+ /* Loop over all the dai link sub nodes */
+ for_each_available_child_of_node_scoped(dev->of_node, sub_node) {
+ if (of_property_read_string(sub_node, "link-name",
+ &dai_link_name))
+ return -EINVAL;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (!strcmp(dai_link_name, dai_link->name))
+ break;
+ }
+
+ if (i >= card->num_links)
+ return -EINVAL;
+
+ ret = set_card_codec_info(card, sub_node, dai_link);
+ if (ret < 0)
+ return ret;
+
+ ret = set_dailink_daifmt(card, sub_node, dai_link);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(parse_dai_link_info);
+
+void clean_card_reference(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ int i;
+
+ /* release codec reference gotten by set_card_codec_info */
+ for_each_card_prelinks(card, i, dai_link)
+ snd_soc_of_put_dai_link_codecs(dai_link);
+}
+EXPORT_SYMBOL_GPL(clean_card_reference);
+
+int mtk_soundcard_startup(struct snd_pcm_substream *substream,
+ enum mtk_pcm_constraint_type ctype)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_soc_card_data *soc_card = snd_soc_card_get_drvdata(rtd->card);
+ const struct mtk_pcm_constraints_data *mpc = &soc_card->card_data->pcm_constraints[ctype];
+ int ret;
+
+ if (unlikely(!mpc))
+ return -EINVAL;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ mpc->rates);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list rate failed\n");
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ mpc->channels);
+ if (ret < 0) {
+ dev_err(rtd->dev, "hw_constraint_list channel failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_soundcard_startup);
+
+static int mtk_soundcard_playback_startup(struct snd_pcm_substream *substream)
+{
+ return mtk_soundcard_startup(substream, MTK_CONSTRAINT_PLAYBACK);
+}
+
+const struct snd_soc_ops mtk_soundcard_common_playback_ops = {
+ .startup = mtk_soundcard_playback_startup,
+};
+EXPORT_SYMBOL_GPL(mtk_soundcard_common_playback_ops);
+
+static int mtk_soundcard_capture_startup(struct snd_pcm_substream *substream)
+{
+ return mtk_soundcard_startup(substream, MTK_CONSTRAINT_CAPTURE);
+}
+
+const struct snd_soc_ops mtk_soundcard_common_capture_ops = {
+ .startup = mtk_soundcard_capture_startup,
+};
+EXPORT_SYMBOL_GPL(mtk_soundcard_common_capture_ops);
+
+int mtk_soundcard_common_probe(struct platform_device *pdev)
+{
+ struct device_node *platform_node, *adsp_node, *accdet_node;
+ struct snd_soc_component *accdet_comp;
+ struct platform_device *accdet_pdev;
+ const struct mtk_soundcard_pdata *pdata;
+ struct mtk_soc_card_data *soc_card_data;
+ struct snd_soc_dai_link *orig_dai_link, *dai_link;
+ struct snd_soc_jack *jacks;
+ struct snd_soc_card *card;
+ int i, orig_num_links, ret;
+ bool needs_legacy_probe;
+
+ pdata = device_get_match_data(&pdev->dev);
+ if (!pdata)
+ return -EINVAL;
+
+ card = pdata->card_data->card;
+ card->dev = &pdev->dev;
+ orig_dai_link = card->dai_link;
+ orig_num_links = card->num_links;
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret)
+ return ret;
+
+ if (!card->name) {
+ if (!pdata->card_name)
+ return -EINVAL;
+
+ card->name = pdata->card_name;
+ }
+
+ needs_legacy_probe = !of_property_present(pdev->dev.of_node, "audio-routing");
+ if (needs_legacy_probe) {
+ /*
+ * If we have no .soc_probe() callback there's no way of using
+ * any legacy probe mechanism, as that cannot not be generic.
+ */
+ if (!pdata->soc_probe)
+ return -EINVAL;
+
+ dev_info_once(&pdev->dev, "audio-routing not found: using legacy probe\n");
+ } else {
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret)
+ return ret;
+ }
+
+ soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*soc_card_data), GFP_KERNEL);
+ if (!soc_card_data)
+ return -ENOMEM;
+
+ soc_card_data->card_data = pdata->card_data;
+
+ jacks = devm_kcalloc(card->dev, soc_card_data->card_data->num_jacks,
+ sizeof(*jacks), GFP_KERNEL);
+ if (!jacks)
+ return -ENOMEM;
+
+ soc_card_data->card_data->jacks = jacks;
+
+ accdet_node = of_parse_phandle(pdev->dev.of_node, "mediatek,accdet", 0);
+ if (accdet_node) {
+ accdet_pdev = of_find_device_by_node(accdet_node);
+ if (accdet_pdev) {
+ accdet_comp = snd_soc_lookup_component(&accdet_pdev->dev, NULL);
+ if (accdet_comp)
+ soc_card_data->accdet = accdet_comp;
+ else
+ dev_err(&pdev->dev, "No sound component found from mediatek,accdet property\n");
+
+ put_device(&accdet_pdev->dev);
+ } else {
+ dev_err(&pdev->dev, "No device found from mediatek,accdet property\n");
+ }
+
+ of_node_put(accdet_node);
+ }
+
+ platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
+ if (!platform_node)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Property mediatek,platform missing or invalid\n");
+
+ /* Check if this SoC has an Audio DSP */
+ if (pdata->sof_priv)
+ adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0);
+ else
+ adsp_node = NULL;
+
+ if (adsp_node) {
+ if (of_property_present(pdev->dev.of_node, "mediatek,dai-link")) {
+ ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node,
+ "mediatek,dai-link",
+ card->dai_link, card->num_links);
+ if (ret) {
+ of_node_put(adsp_node);
+ of_node_put(platform_node);
+ return dev_err_probe(&pdev->dev, ret,
+ "Cannot parse mediatek,dai-link\n");
+ }
+ }
+
+ soc_card_data->sof_priv = pdata->sof_priv;
+ card->probe = mtk_sof_card_probe;
+ card->late_probe = mtk_sof_card_late_probe;
+ if (!card->topology_shortname_created) {
+ snprintf(card->topology_shortname, 32, "sof-%s", card->name);
+ card->topology_shortname_created = true;
+ }
+ card->name = card->topology_shortname;
+ }
+
+ /*
+ * Regardless of whether the ADSP is wanted and/or present in a machine
+ * specific device tree or not and regardless of whether any AFE_SOF
+ * link is present, we have to make sure that the platforms->of_node
+ * is not NULL, and set to either ADSP (adsp_node) or AFE (platform_node).
+ */
+ for_each_card_prelinks(card, i, dai_link) {
+ if (adsp_node && !strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")))
+ dai_link->platforms->of_node = adsp_node;
+ else if (!dai_link->platforms->name && !dai_link->platforms->of_node)
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ if (!needs_legacy_probe) {
+ ret = parse_dai_link_info(card);
+ if (ret)
+ goto err_restore_dais;
+ } else {
+ if (adsp_node)
+ of_node_put(adsp_node);
+ of_node_put(platform_node);
+ }
+
+ if (pdata->soc_probe) {
+ ret = pdata->soc_probe(soc_card_data, needs_legacy_probe);
+ if (ret) {
+ if (!needs_legacy_probe)
+ clean_card_reference(card);
+ goto err_restore_dais;
+ }
+ }
+ snd_soc_card_set_drvdata(card, soc_card_data);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+ if (!needs_legacy_probe)
+ clean_card_reference(card);
+
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "Cannot register card\n");
+ goto err_restore_dais;
+ }
+
+ return 0;
+
+err_restore_dais:
+ card->dai_link = orig_dai_link;
+ card->num_links = orig_num_links;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_soundcard_common_probe);
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.h b/sound/soc/mediatek/common/mtk-soundcard-driver.h
new file mode 100644
index 000000000000..f6c275b54025
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-soundcard-driver.h -- MediaTek soundcard driver common definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MTK_SOUNDCARD_DRIVER_H_
+#define _MTK_SOUNDCARD_DRIVER_H_
+
+struct mtk_sof_priv;
+struct mtk_soc_card_data;
+struct snd_pcm_hw_constraint_list;
+
+enum mtk_pcm_constraint_type {
+ MTK_CONSTRAINT_PLAYBACK,
+ MTK_CONSTRAINT_CAPTURE,
+ MTK_CONSTRAINT_HDMIDP,
+ MTK_CONSTRAINT_MAX
+};
+
+struct mtk_pcm_constraints_data {
+ const struct snd_pcm_hw_constraint_list *channels;
+ const struct snd_pcm_hw_constraint_list *rates;
+};
+
+struct mtk_platform_card_data {
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jacks;
+ const struct mtk_pcm_constraints_data *pcm_constraints;
+ u8 num_jacks;
+ u8 num_pcm_constraints;
+ u8 flags;
+};
+
+struct mtk_soundcard_pdata {
+ const char *card_name;
+ struct mtk_platform_card_data *card_data;
+ const struct mtk_sof_priv *sof_priv;
+
+ int (*soc_probe)(struct mtk_soc_card_data *card_data, bool legacy);
+};
+
+/* Common playback/capture card startup ops */
+extern const struct snd_soc_ops mtk_soundcard_common_playback_ops;
+extern const struct snd_soc_ops mtk_soundcard_common_capture_ops;
+
+/* Exported for custom/extended soundcard startup ops */
+int mtk_soundcard_startup(struct snd_pcm_substream *substream,
+ enum mtk_pcm_constraint_type ctype);
+
+int parse_dai_link_info(struct snd_soc_card *card);
+void clean_card_reference(struct snd_soc_card *card);
+int mtk_soundcard_common_probe(struct platform_device *pdev);
+#endif
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644
index 000000000000..507fa26c3945
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# platform driver
+snd-soc-mt2701-afe-y := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
+obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
+obj-$(CONFIG_SND_SOC_MT2701_WM8960) += mt2701-wm8960.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644
index 000000000000..ae620890bb3a
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ * Ryder Lee <ryder.lee@mediatek.com>
+ */
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *const base_clks[] = {
+ [MT2701_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+ [MT2701_TOP_AUD_MCLK_SRC0] = "top_audio_mux1_sel",
+ [MT2701_TOP_AUD_MCLK_SRC1] = "top_audio_mux2_sel",
+ [MT2701_TOP_AUD_A1SYS] = "top_audio_a1sys_hp",
+ [MT2701_TOP_AUD_A2SYS] = "top_audio_a2sys_hp",
+ [MT2701_AUDSYS_AFE] = "audio_afe_pd",
+ [MT2701_AUDSYS_AFE_CONN] = "audio_afe_conn_pd",
+ [MT2701_AUDSYS_A1SYS] = "audio_a1sys_pd",
+ [MT2701_AUDSYS_A2SYS] = "audio_a2sys_pd",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i;
+
+ for (i = 0; i < MT2701_BASE_CLK_NUM; i++) {
+ afe_priv->base_ck[i] = devm_clk_get(afe->dev, base_clks[i]);
+ if (IS_ERR(afe_priv->base_ck[i])) {
+ dev_err(afe->dev, "failed to get %s\n", base_clks[i]);
+ return PTR_ERR(afe_priv->base_ck[i]);
+ }
+ }
+
+ /* Get I2S related clocks */
+ for (i = 0; i < afe_priv->soc->i2s_num; i++) {
+ struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i];
+ struct clk *i2s_ck;
+ char name[13];
+
+ snprintf(name, sizeof(name), "i2s%d_src_sel", i);
+ i2s_path->sel_ck = devm_clk_get(afe->dev, name);
+ if (IS_ERR(i2s_path->sel_ck)) {
+ dev_err(afe->dev, "failed to get %s\n", name);
+ return PTR_ERR(i2s_path->sel_ck);
+ }
+
+ snprintf(name, sizeof(name), "i2s%d_src_div", i);
+ i2s_path->div_ck = devm_clk_get(afe->dev, name);
+ if (IS_ERR(i2s_path->div_ck)) {
+ dev_err(afe->dev, "failed to get %s\n", name);
+ return PTR_ERR(i2s_path->div_ck);
+ }
+
+ snprintf(name, sizeof(name), "i2s%d_mclk_en", i);
+ i2s_path->mclk_ck = devm_clk_get(afe->dev, name);
+ if (IS_ERR(i2s_path->mclk_ck)) {
+ dev_err(afe->dev, "failed to get %s\n", name);
+ return PTR_ERR(i2s_path->mclk_ck);
+ }
+
+ snprintf(name, sizeof(name), "i2so%d_hop_ck", i);
+ i2s_ck = devm_clk_get(afe->dev, name);
+ if (IS_ERR(i2s_ck)) {
+ dev_err(afe->dev, "failed to get %s\n", name);
+ return PTR_ERR(i2s_ck);
+ }
+ i2s_path->hop_ck[SNDRV_PCM_STREAM_PLAYBACK] = i2s_ck;
+
+ snprintf(name, sizeof(name), "i2si%d_hop_ck", i);
+ i2s_ck = devm_clk_get(afe->dev, name);
+ if (IS_ERR(i2s_ck)) {
+ dev_err(afe->dev, "failed to get %s\n", name);
+ return PTR_ERR(i2s_ck);
+ }
+ i2s_path->hop_ck[SNDRV_PCM_STREAM_CAPTURE] = i2s_ck;
+
+ snprintf(name, sizeof(name), "asrc%d_out_ck", i);
+ i2s_path->asrco_ck = devm_clk_get(afe->dev, name);
+ if (IS_ERR(i2s_path->asrco_ck)) {
+ dev_err(afe->dev, "failed to get %s\n", name);
+ return PTR_ERR(i2s_path->asrco_ck);
+ }
+ }
+
+ /* Some platforms may support BT path */
+ afe_priv->mrgif_ck = devm_clk_get(afe->dev, "audio_mrgif_pd");
+ if (IS_ERR(afe_priv->mrgif_ck)) {
+ if (PTR_ERR(afe_priv->mrgif_ck) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ afe_priv->mrgif_ck = NULL;
+ }
+
+ return 0;
+}
+
+int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
+ struct mt2701_i2s_path *i2s_path,
+ int dir)
+{
+ int ret;
+
+ ret = clk_prepare_enable(i2s_path->asrco_ck);
+ if (ret) {
+ dev_err(afe->dev, "failed to enable ASRC clock %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(i2s_path->hop_ck[dir]);
+ if (ret) {
+ dev_err(afe->dev, "failed to enable I2S clock %d\n", ret);
+ goto err_hop_ck;
+ }
+
+ return 0;
+
+err_hop_ck:
+ clk_disable_unprepare(i2s_path->asrco_ck);
+
+ return ret;
+}
+
+void mt2701_afe_disable_i2s(struct mtk_base_afe *afe,
+ struct mt2701_i2s_path *i2s_path,
+ int dir)
+{
+ clk_disable_unprepare(i2s_path->hop_ck[dir]);
+ clk_disable_unprepare(i2s_path->asrco_ck);
+}
+
+int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id];
+
+ return clk_prepare_enable(i2s_path->mclk_ck);
+}
+
+void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id];
+
+ clk_disable_unprepare(i2s_path->mclk_ck);
+}
+
+int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ return clk_prepare_enable(afe_priv->mrgif_ck);
+}
+
+void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->mrgif_ck);
+}
+
+static int mt2701_afe_enable_audsys(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* Enable infra clock gate */
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]);
+ if (ret)
+ return ret;
+
+ /* Enable top a1sys clock gate */
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]);
+ if (ret)
+ goto err_a1sys;
+
+ /* Enable top a2sys clock gate */
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]);
+ if (ret)
+ goto err_a2sys;
+
+ /* Internal clock gates */
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_AFE]);
+ if (ret)
+ goto err_afe;
+
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]);
+ if (ret)
+ goto err_audio_a1sys;
+
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]);
+ if (ret)
+ goto err_audio_a2sys;
+
+ ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_AFE_CONN]);
+ if (ret)
+ goto err_afe_conn;
+
+ return 0;
+
+err_afe_conn:
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]);
+err_audio_a2sys:
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]);
+err_audio_a1sys:
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE]);
+err_afe:
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]);
+err_a2sys:
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]);
+err_a1sys:
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]);
+
+ return ret;
+}
+
+static void mt2701_afe_disable_audsys(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE_CONN]);
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]);
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]);
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE]);
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]);
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]);
+ clk_disable_unprepare(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]);
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+ int ret;
+
+ /* Enable audio system */
+ ret = mt2701_afe_enable_audsys(afe);
+ if (ret) {
+ dev_err(afe->dev, "failed to enable audio system %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+ ASYS_TOP_CON_ASYS_TIMING_ON,
+ ASYS_TOP_CON_ASYS_TIMING_ON);
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_DAC_CON0_AFE_ON,
+ AFE_DAC_CON0_AFE_ON);
+
+ /* Configure ASRC */
+ regmap_write(afe->regmap, PWR1_ASM_CON1, PWR1_ASM_CON1_INIT_VAL);
+ regmap_write(afe->regmap, PWR2_ASM_CON1, PWR2_ASM_CON1_INIT_VAL);
+
+ return 0;
+}
+
+int mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+ ASYS_TOP_CON_ASYS_TIMING_ON, 0);
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_DAC_CON0_AFE_ON, 0);
+
+ mt2701_afe_disable_audsys(afe);
+
+ return 0;
+}
+
+int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id)
+
+{
+ struct mt2701_afe_private *priv = afe->platform_priv;
+ struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id];
+ int ret = -EINVAL;
+
+ /* Set mclk source */
+ if (!(MT2701_PLL_DOMAIN_0_RATE % i2s_path->mclk_rate))
+ ret = clk_set_parent(i2s_path->sel_ck,
+ priv->base_ck[MT2701_TOP_AUD_MCLK_SRC0]);
+ else if (!(MT2701_PLL_DOMAIN_1_RATE % i2s_path->mclk_rate))
+ ret = clk_set_parent(i2s_path->sel_ck,
+ priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]);
+
+ if (ret) {
+ dev_err(afe->dev, "failed to set mclk source\n");
+ return ret;
+ }
+
+ /* Set mclk divider */
+ ret = clk_set_rate(i2s_path->div_ck, i2s_path->mclk_rate);
+ if (ret) {
+ dev_err(afe->dev, "failed to set mclk divider %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644
index 000000000000..0bd82fbda176
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ * Ryder Lee <ryder.lee@mediatek.com>
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+struct mt2701_i2s_path;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+int mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
+ struct mt2701_i2s_path *i2s_path,
+ int dir);
+void mt2701_afe_disable_i2s(struct mtk_base_afe *afe,
+ struct mt2701_i2s_path *i2s_path,
+ int dir);
+int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id);
+void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id);
+
+int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe);
+void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe);
+
+int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644
index 000000000000..32bef5e2a56d
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt2701-afe-common.h -- Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_PLL_DOMAIN_0_RATE 98304000
+#define MT2701_PLL_DOMAIN_1_RATE 90316800
+
+enum {
+ MT2701_MEMIF_DL1,
+ MT2701_MEMIF_DL2,
+ MT2701_MEMIF_DL3,
+ MT2701_MEMIF_DL4,
+ MT2701_MEMIF_DL5,
+ MT2701_MEMIF_DL_SINGLE_NUM,
+ MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+ MT2701_MEMIF_UL1,
+ MT2701_MEMIF_UL2,
+ MT2701_MEMIF_UL3,
+ MT2701_MEMIF_UL4,
+ MT2701_MEMIF_UL5,
+ MT2701_MEMIF_DLBT,
+ MT2701_MEMIF_ULBT,
+ MT2701_MEMIF_NUM,
+ MT2701_IO_I2S = MT2701_MEMIF_NUM,
+ MT2701_IO_2ND_I2S,
+ MT2701_IO_3RD_I2S,
+ MT2701_IO_4TH_I2S,
+ MT2701_IO_5TH_I2S,
+ MT2701_IO_6TH_I2S,
+ MT2701_IO_MRG,
+};
+
+enum {
+ MT2701_IRQ_ASYS_IRQ1,
+ MT2701_IRQ_ASYS_IRQ2,
+ MT2701_IRQ_ASYS_IRQ3,
+ MT2701_IRQ_ASYS_END,
+};
+
+enum audio_base_clock {
+ MT2701_INFRA_SYS_AUDIO,
+ MT2701_TOP_AUD_MCLK_SRC0,
+ MT2701_TOP_AUD_MCLK_SRC1,
+ MT2701_TOP_AUD_A1SYS,
+ MT2701_TOP_AUD_A2SYS,
+ MT2701_AUDSYS_AFE,
+ MT2701_AUDSYS_AFE_CONN,
+ MT2701_AUDSYS_A1SYS,
+ MT2701_AUDSYS_A2SYS,
+ MT2701_BASE_CLK_NUM,
+};
+
+struct mt2701_i2s_data {
+ int i2s_ctrl_reg;
+ int i2s_asrc_fs_shift;
+ int i2s_asrc_fs_mask;
+};
+
+struct mt2701_i2s_path {
+ int mclk_rate;
+ int on[MTK_STREAM_NUM];
+ int occupied[MTK_STREAM_NUM];
+ const struct mt2701_i2s_data *i2s_data[MTK_STREAM_NUM];
+ struct clk *hop_ck[MTK_STREAM_NUM];
+ struct clk *sel_ck;
+ struct clk *div_ck;
+ struct clk *mclk_ck;
+ struct clk *asrco_ck;
+};
+
+struct mt2701_soc_variants {
+ bool has_one_heart_mode;
+ int i2s_num;
+};
+
+struct mt2701_afe_private {
+ struct mt2701_i2s_path *i2s_path;
+ struct clk *base_ck[MT2701_BASE_CLK_NUM];
+ struct clk *mrgif_ck;
+ bool mrg_enable[MTK_STREAM_NUM];
+
+ const struct mt2701_soc_variants *soc;
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
new file mode 100644
index 000000000000..fcae38135d93
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -0,0 +1,1483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ * Ir Lian <ir.lian@mediatek.com>
+ * Ryder Lee <ryder.lee@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+static const struct snd_pcm_hardware mt2701_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 1024 * 256,
+ .periods_min = 4,
+ .periods_max = 1024,
+ .buffer_bytes_max = 1024 * 1024,
+ .fifo_size = 0,
+};
+
+struct mt2701_afe_rate {
+ unsigned int rate;
+ unsigned int regvalue;
+};
+
+static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
+ { .rate = 8000, .regvalue = 0 },
+ { .rate = 12000, .regvalue = 1 },
+ { .rate = 16000, .regvalue = 2 },
+ { .rate = 24000, .regvalue = 3 },
+ { .rate = 32000, .regvalue = 4 },
+ { .rate = 48000, .regvalue = 5 },
+ { .rate = 96000, .regvalue = 6 },
+ { .rate = 192000, .regvalue = 7 },
+ { .rate = 384000, .regvalue = 8 },
+ { .rate = 7350, .regvalue = 16 },
+ { .rate = 11025, .regvalue = 17 },
+ { .rate = 14700, .regvalue = 18 },
+ { .rate = 22050, .regvalue = 19 },
+ { .rate = 29400, .regvalue = 20 },
+ { .rate = 44100, .regvalue = 21 },
+ { .rate = 88200, .regvalue = 22 },
+ { .rate = 176400, .regvalue = 23 },
+ { .rate = 352800, .regvalue = 24 },
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+ AUDIO_TOP_CON0,
+ AUDIO_TOP_CON4,
+ AUDIO_TOP_CON5,
+ ASYS_TOP_CON,
+ AFE_CONN0,
+ AFE_CONN1,
+ AFE_CONN2,
+ AFE_CONN3,
+ AFE_CONN15,
+ AFE_CONN16,
+ AFE_CONN17,
+ AFE_CONN18,
+ AFE_CONN19,
+ AFE_CONN20,
+ AFE_CONN21,
+ AFE_CONN22,
+ AFE_DAC_CON0,
+ AFE_MEMIF_PBUF_SIZE,
+};
+
+static int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int val = num - MT2701_IO_I2S;
+
+ if (val < 0 || val >= afe_priv->soc->i2s_num) {
+ dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+ __func__, num, val);
+ return -EINVAL;
+ }
+ return val;
+}
+
+static int mt2701_afe_i2s_fs(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt2701_afe_i2s_rates); i++)
+ if (mt2701_afe_i2s_rates[i].rate == sample_rate)
+ return mt2701_afe_i2s_rates[i].regvalue;
+
+ return -EINVAL;
+}
+
+static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+ bool mode = afe_priv->soc->has_one_heart_mode;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ return mt2701_afe_enable_mclk(afe, mode ? 1 : i2s_num);
+}
+
+static int mt2701_afe_i2s_path_disable(struct mtk_base_afe *afe,
+ struct mt2701_i2s_path *i2s_path,
+ int stream_dir)
+{
+ const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir];
+
+ if (--i2s_path->on[stream_dir] < 0)
+ i2s_path->on[stream_dir] = 0;
+
+ if (i2s_path->on[stream_dir])
+ return 0;
+
+ /* disable i2s */
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_I2S_EN, 0);
+
+ mt2701_afe_disable_i2s(afe, i2s_path, stream_dir);
+
+ return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+ struct mt2701_i2s_path *i2s_path;
+ bool mode = afe_priv->soc->has_one_heart_mode;
+
+ if (i2s_num < 0)
+ return;
+
+ i2s_path = &afe_priv->i2s_path[i2s_num];
+
+ if (i2s_path->occupied[substream->stream])
+ i2s_path->occupied[substream->stream] = 0;
+ else
+ goto exit;
+
+ mt2701_afe_i2s_path_disable(afe, i2s_path, substream->stream);
+
+ /* need to disable i2s-out path when disable i2s-in */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt2701_afe_i2s_path_disable(afe, i2s_path, !substream->stream);
+
+exit:
+ /* disable mclk */
+ mt2701_afe_disable_mclk(afe, mode ? 1 : i2s_num);
+}
+
+static int mt2701_i2s_path_enable(struct mtk_base_afe *afe,
+ struct mt2701_i2s_path *i2s_path,
+ int stream_dir, int rate)
+{
+ const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir];
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int reg, fs, w_len = 1; /* now we support bck 64bits only */
+ unsigned int mask, val;
+
+ /* no need to enable if already done */
+ if (++i2s_path->on[stream_dir] != 1)
+ return 0;
+
+ fs = mt2701_afe_i2s_fs(rate);
+
+ mask = ASYS_I2S_CON_FS |
+ ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
+ ASYS_I2S_CON_I2S_MODE |
+ ASYS_I2S_CON_WIDE_MODE;
+
+ val = ASYS_I2S_CON_FS_SET(fs) |
+ ASYS_I2S_CON_I2S_MODE |
+ ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+ if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+ mask |= ASYS_I2S_IN_PHASE_FIX;
+ val |= ASYS_I2S_IN_PHASE_FIX;
+ reg = ASMI_TIMING_CON1;
+ } else {
+ if (afe_priv->soc->has_one_heart_mode) {
+ mask |= ASYS_I2S_CON_ONE_HEART_MODE;
+ val |= ASYS_I2S_CON_ONE_HEART_MODE;
+ }
+ reg = ASMO_TIMING_CON1;
+ }
+
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+ regmap_update_bits(afe->regmap, reg,
+ i2s_data->i2s_asrc_fs_mask
+ << i2s_data->i2s_asrc_fs_shift,
+ fs << i2s_data->i2s_asrc_fs_shift);
+
+ /* enable i2s */
+ mt2701_afe_enable_i2s(afe, i2s_path, stream_dir);
+
+ /* reset i2s hw status before enable */
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+ udelay(1);
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_RESET, 0);
+ udelay(1);
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+ return 0;
+}
+
+static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret, i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+ struct mt2701_i2s_path *i2s_path;
+ bool mode = afe_priv->soc->has_one_heart_mode;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ i2s_path = &afe_priv->i2s_path[i2s_num];
+
+ if (i2s_path->occupied[substream->stream])
+ return -EBUSY;
+
+ ret = mt2701_mclk_configuration(afe, mode ? 1 : i2s_num);
+ if (ret)
+ return ret;
+
+ i2s_path->occupied[substream->stream] = 1;
+
+ /* need to enable i2s-out path when enable i2s-in */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt2701_i2s_path_enable(afe, i2s_path, !substream->stream,
+ substream->runtime->rate);
+
+ mt2701_i2s_path_enable(afe, i2s_path, substream->stream,
+ substream->runtime->rate);
+
+ return 0;
+}
+
+static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+ bool mode = afe_priv->soc->has_one_heart_mode;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ /* mclk */
+ if (dir == SND_SOC_CLOCK_IN) {
+ dev_warn(dai->dev, "The SoCs doesn't support mclk input\n");
+ return -EINVAL;
+ }
+
+ afe_priv->i2s_path[mode ? 1 : i2s_num].mclk_rate = freq;
+
+ return 0;
+}
+
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt2701_enable_btmrg_clk(afe);
+ if (ret)
+ return ret;
+
+ afe_priv->mrg_enable[substream->stream] = 1;
+
+ return 0;
+}
+
+static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int stream_fs;
+ u32 val, msk;
+
+ stream_fs = params_rate(params);
+
+ if (stream_fs != 8000 && stream_fs != 16000) {
+ dev_err(afe->dev, "unsupported rate %d\n", stream_fs);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+ AFE_MRGIF_CON_I2S_MODE_MASK,
+ AFE_MRGIF_CON_I2S_MODE_32K);
+
+ val = AFE_DAIBT_CON0_BT_FUNC_EN | AFE_DAIBT_CON0_BT_FUNC_RDY
+ | AFE_DAIBT_CON0_MRG_USE;
+ msk = val;
+
+ if (stream_fs == 16000)
+ val |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+ msk |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+ regmap_update_bits(afe->regmap, AFE_DAIBT_CON0, msk, val);
+
+ regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+ AFE_DAIBT_CON0_DAIBT_EN,
+ AFE_DAIBT_CON0_DAIBT_EN);
+ regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+ AFE_MRGIF_CON_MRG_I2S_EN,
+ AFE_MRGIF_CON_MRG_I2S_EN);
+ regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+ AFE_MRGIF_CON_MRG_EN,
+ AFE_MRGIF_CON_MRG_EN);
+ return 0;
+}
+
+static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ /* if the other direction stream is not occupied */
+ if (!afe_priv->mrg_enable[!substream->stream]) {
+ regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+ AFE_DAIBT_CON0_DAIBT_EN, 0);
+ regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+ AFE_MRGIF_CON_MRG_EN, 0);
+ regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+ AFE_MRGIF_CON_MRG_I2S_EN, 0);
+ mt2701_disable_btmrg_clk(afe);
+ }
+
+ afe_priv->mrg_enable[substream->stream] = 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mtk_base_afe_memif *memif_tmp;
+ int stream_dir = substream->stream;
+
+ /* can't run single DL & DLM at the same time */
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ memif_tmp = &afe->memif[MT2701_MEMIF_DLM];
+ if (memif_tmp->substream) {
+ dev_warn(afe->dev, "memif is not available");
+ return -EBUSY;
+ }
+ }
+
+ return mtk_afe_fe_startup(substream, dai);
+}
+
+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int stream_dir = substream->stream;
+
+ /* single DL use PAIR_INTERLEAVE */
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+ AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mtk_base_afe_memif *memif_tmp;
+ const struct mtk_base_memif_data *memif_data;
+ int i;
+
+ for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+ memif_tmp = &afe->memif[i];
+ if (memif_tmp->substream)
+ return -EBUSY;
+ }
+
+ /* enable agent for all signal DL (due to hw design) */
+ for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+ memif_data = afe->memif[i].data;
+ regmap_update_bits(afe->regmap,
+ memif_data->agent_disable_reg,
+ 1 << memif_data->agent_disable_shift,
+ 0 << memif_data->agent_disable_shift);
+ }
+
+ return mtk_afe_fe_startup(substream, dai);
+}
+
+static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ const struct mtk_base_memif_data *memif_data;
+ int i;
+
+ for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+ memif_data = afe->memif[i].data;
+ regmap_update_bits(afe->regmap,
+ memif_data->agent_disable_reg,
+ 1 << memif_data->agent_disable_shift,
+ 1 << memif_data->agent_disable_shift);
+ }
+
+ return mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int channels = params_channels(params);
+
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+ AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+ AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+ AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+ 1 << memif_tmp->data->enable_shift,
+ 1 << memif_tmp->data->enable_shift);
+ mtk_afe_fe_trigger(substream, cmd, dai);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ mtk_afe_fe_trigger(substream, cmd, dai);
+ regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+ 1 << memif_tmp->data->enable_shift, 0);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt2701_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ int fs;
+
+ if (snd_soc_rtd_to_cpu(rtd, 0)->id != MT2701_MEMIF_ULBT)
+ fs = mt2701_afe_i2s_fs(rate);
+ else
+ fs = (rate == 16000 ? 1 : 0);
+
+ return fs;
+}
+
+static int mt2701_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ return mt2701_afe_i2s_fs(rate);
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+ .startup = mt2701_simple_fe_startup,
+ .shutdown = mtk_afe_fe_shutdown,
+ .hw_params = mt2701_simple_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mtk_afe_fe_trigger,
+};
+
+static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = {
+ .startup = mt2701_dlm_fe_startup,
+ .shutdown = mt2701_dlm_fe_shutdown,
+ .hw_params = mt2701_dlm_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mt2701_dlm_fe_trigger,
+};
+
+/* I2S BE DAIs */
+static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
+ .startup = mt2701_afe_i2s_startup,
+ .shutdown = mt2701_afe_i2s_shutdown,
+ .prepare = mt2701_afe_i2s_prepare,
+ .set_sysclk = mt2701_afe_i2s_set_sysclk,
+};
+
+/* MRG BE DAIs */
+static const struct snd_soc_dai_ops mt2701_btmrg_ops = {
+ .startup = mt2701_btmrg_startup,
+ .shutdown = mt2701_btmrg_shutdown,
+ .hw_params = mt2701_btmrg_hw_params,
+};
+
+static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "PCMO0",
+ .id = MT2701_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
+ {
+ .name = "PCM_multi",
+ .id = MT2701_MEMIF_DLM,
+ .playback = {
+ .stream_name = "DLM",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .ops = &mt2701_dlm_memif_dai_ops,
+ },
+ {
+ .name = "PCM0",
+ .id = MT2701_MEMIF_UL1,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
+ {
+ .name = "PCM1",
+ .id = MT2701_MEMIF_UL2,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
+ {
+ .name = "PCM_BT_DL",
+ .id = MT2701_MEMIF_DLBT,
+ .playback = {
+ .stream_name = "DLBT",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = (SNDRV_PCM_RATE_8000
+ | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
+ {
+ .name = "PCM_BT_UL",
+ .id = MT2701_MEMIF_ULBT,
+ .capture = {
+ .stream_name = "ULBT",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = (SNDRV_PCM_RATE_8000
+ | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
+ /* BE DAIs */
+ {
+ .name = "I2S0",
+ .id = MT2701_IO_I2S,
+ .playback = {
+ .stream_name = "I2S0 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .capture = {
+ .stream_name = "I2S0 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .ops = &mt2701_afe_i2s_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "I2S1",
+ .id = MT2701_IO_2ND_I2S,
+ .playback = {
+ .stream_name = "I2S1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "I2S1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mt2701_afe_i2s_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "I2S2",
+ .id = MT2701_IO_3RD_I2S,
+ .playback = {
+ .stream_name = "I2S2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "I2S2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mt2701_afe_i2s_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "I2S3",
+ .id = MT2701_IO_4TH_I2S,
+ .playback = {
+ .stream_name = "I2S3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "I2S3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mt2701_afe_i2s_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "MRG BT",
+ .id = MT2701_IO_MRG,
+ .playback = {
+ .stream_name = "BT 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 = "BT Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = (SNDRV_PCM_RATE_8000
+ | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt2701_btmrg_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o01_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o02_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o03_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o14_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN14, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o15_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o16_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o17_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o18_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o19_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o20_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o21_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o22_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o31_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I35 Switch", AFE_CONN41, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_i02_mix[] = {
+ SOC_DAPM_SINGLE("I2S0 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s0[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S0 Out Switch",
+ ASYS_I2SO1_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s1[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S1 Out Switch",
+ ASYS_I2SO2_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s2[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S2 Out Switch",
+ PWR2_TOP_CON, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s3[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S3 Out Switch",
+ PWR2_TOP_CON, 18, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, mt2701_afe_i02_mix,
+ ARRAY_SIZE(mt2701_afe_i02_mix)),
+ SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I35", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mt2701_afe_o00_mix,
+ ARRAY_SIZE(mt2701_afe_o00_mix)),
+ SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mt2701_afe_o01_mix,
+ ARRAY_SIZE(mt2701_afe_o01_mix)),
+ SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mt2701_afe_o02_mix,
+ ARRAY_SIZE(mt2701_afe_o02_mix)),
+ SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mt2701_afe_o03_mix,
+ ARRAY_SIZE(mt2701_afe_o03_mix)),
+ SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0, mt2701_afe_o14_mix,
+ ARRAY_SIZE(mt2701_afe_o14_mix)),
+ SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mt2701_afe_o15_mix,
+ ARRAY_SIZE(mt2701_afe_o15_mix)),
+ SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mt2701_afe_o16_mix,
+ ARRAY_SIZE(mt2701_afe_o16_mix)),
+ SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mt2701_afe_o17_mix,
+ ARRAY_SIZE(mt2701_afe_o17_mix)),
+ SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mt2701_afe_o18_mix,
+ ARRAY_SIZE(mt2701_afe_o18_mix)),
+ SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mt2701_afe_o19_mix,
+ ARRAY_SIZE(mt2701_afe_o19_mix)),
+ SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mt2701_afe_o20_mix,
+ ARRAY_SIZE(mt2701_afe_o20_mix)),
+ SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mt2701_afe_o21_mix,
+ ARRAY_SIZE(mt2701_afe_o21_mix)),
+ SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mt2701_afe_o22_mix,
+ ARRAY_SIZE(mt2701_afe_o22_mix)),
+ SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0, mt2701_afe_o31_mix,
+ ARRAY_SIZE(mt2701_afe_o31_mix)),
+
+ SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_i2s0,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s0)),
+ SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_i2s1,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s1)),
+ SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_i2s2,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s2)),
+ SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_i2s3,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)),
+};
+
+static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
+ {"I12", NULL, "DL1"},
+ {"I13", NULL, "DL1"},
+ {"I35", NULL, "DLBT"},
+
+ {"I2S0 Playback", NULL, "O15"},
+ {"I2S0 Playback", NULL, "O16"},
+ {"I2S1 Playback", NULL, "O17"},
+ {"I2S1 Playback", NULL, "O18"},
+ {"I2S2 Playback", NULL, "O19"},
+ {"I2S2 Playback", NULL, "O20"},
+ {"I2S3 Playback", NULL, "O21"},
+ {"I2S3 Playback", NULL, "O22"},
+ {"BT Playback", NULL, "O31"},
+
+ {"UL1", NULL, "O00"},
+ {"UL1", NULL, "O01"},
+ {"UL2", NULL, "O02"},
+ {"UL2", NULL, "O03"},
+ {"ULBT", NULL, "O14"},
+
+ {"I00", NULL, "I2S0 Capture"},
+ {"I01", NULL, "I2S0 Capture"},
+ {"I02", NULL, "I2S1 Capture"},
+ {"I03", NULL, "I2S1 Capture"},
+ /* I02,03 link to UL2, also need to open I2S0 */
+ {"I02", "I2S0 Switch", "I2S0 Capture"},
+
+ {"I26", NULL, "BT Capture"},
+
+ {"I12I13", "Multich I2S0 Out Switch", "DLM"},
+ {"I14I15", "Multich I2S1 Out Switch", "DLM"},
+ {"I16I17", "Multich I2S2 Out Switch", "DLM"},
+ {"I18I19", "Multich I2S3 Out Switch", "DLM"},
+
+ { "I12", NULL, "I12I13" },
+ { "I13", NULL, "I12I13" },
+ { "I14", NULL, "I14I15" },
+ { "I15", NULL, "I14I15" },
+ { "I16", NULL, "I16I17" },
+ { "I17", NULL, "I16I17" },
+ { "I18", NULL, "I18I19" },
+ { "I19", NULL, "I18I19" },
+
+ { "O00", "I00 Switch", "I00" },
+ { "O01", "I01 Switch", "I01" },
+ { "O02", "I02 Switch", "I02" },
+ { "O03", "I03 Switch", "I03" },
+ { "O14", "I26 Switch", "I26" },
+ { "O15", "I12 Switch", "I12" },
+ { "O16", "I13 Switch", "I13" },
+ { "O17", "I14 Switch", "I14" },
+ { "O18", "I15 Switch", "I15" },
+ { "O19", "I16 Switch", "I16" },
+ { "O20", "I17 Switch", "I17" },
+ { "O21", "I18 Switch", "I18" },
+ { "O22", "I19 Switch", "I19" },
+ { "O31", "I35 Switch", "I35" },
+};
+
+static int mt2701_afe_pcm_probe(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, afe->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+ .probe = mt2701_afe_pcm_probe,
+ .name = "mt2701-afe-pcm-dai",
+ .dapm_widgets = mt2701_afe_pcm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets),
+ .dapm_routes = mt2701_afe_pcm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes),
+ .suspend = mtk_afe_suspend,
+ .resume = mtk_afe_resume,
+};
+
+static const struct mtk_base_memif_data memif_data_array[MT2701_MEMIF_NUM] = {
+ {
+ .name = "DL1",
+ .id = MT2701_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 16,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 0,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 6,
+ .msb_reg = -1,
+ },
+ {
+ .name = "DL2",
+ .id = MT2701_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 17,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 2,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 2,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 7,
+ .msb_reg = -1,
+ },
+ {
+ .name = "DL3",
+ .id = MT2701_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 18,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 3,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 4,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 8,
+ .msb_reg = -1,
+ },
+ {
+ .name = "DL4",
+ .id = MT2701_MEMIF_DL4,
+ .reg_ofs_base = AFE_DL4_BASE,
+ .reg_ofs_cur = AFE_DL4_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 19,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 4,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 6,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 9,
+ .msb_reg = -1,
+ },
+ {
+ .name = "DL5",
+ .id = MT2701_MEMIF_DL5,
+ .reg_ofs_base = AFE_DL5_BASE,
+ .reg_ofs_cur = AFE_DL5_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 20,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 5,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 8,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 10,
+ .msb_reg = -1,
+ },
+ {
+ .name = "DLM",
+ .id = MT2701_MEMIF_DLM,
+ .reg_ofs_base = AFE_DLMCH_BASE,
+ .reg_ofs_cur = AFE_DLMCH_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 7,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 28,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 12,
+ .msb_reg = -1,
+ },
+ {
+ .name = "UL1",
+ .id = MT2701_MEMIF_UL1,
+ .reg_ofs_base = AFE_VUL_BASE,
+ .reg_ofs_cur = AFE_VUL_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 0,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 10,
+ .hd_reg = AFE_MEMIF_HD_CON1,
+ .hd_shift = 0,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 0,
+ .msb_reg = -1,
+ },
+ {
+ .name = "UL2",
+ .id = MT2701_MEMIF_UL2,
+ .reg_ofs_base = AFE_UL2_BASE,
+ .reg_ofs_cur = AFE_UL2_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 2,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 11,
+ .hd_reg = AFE_MEMIF_HD_CON1,
+ .hd_shift = 2,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 1,
+ .msb_reg = -1,
+ },
+ {
+ .name = "UL3",
+ .id = MT2701_MEMIF_UL3,
+ .reg_ofs_base = AFE_UL3_BASE,
+ .reg_ofs_cur = AFE_UL3_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 4,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 12,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 0,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 2,
+ .msb_reg = -1,
+ },
+ {
+ .name = "UL4",
+ .id = MT2701_MEMIF_UL4,
+ .reg_ofs_base = AFE_UL4_BASE,
+ .reg_ofs_cur = AFE_UL4_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 6,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 13,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 6,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 3,
+ .msb_reg = -1,
+ },
+ {
+ .name = "UL5",
+ .id = MT2701_MEMIF_UL5,
+ .reg_ofs_base = AFE_UL5_BASE,
+ .reg_ofs_cur = AFE_UL5_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 20,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 8,
+ .fs_maskbit = 0x1f,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 14,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 8,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 4,
+ .msb_reg = -1,
+ },
+ {
+ .name = "DLBT",
+ .id = MT2701_MEMIF_DLBT,
+ .reg_ofs_base = AFE_ARB1_BASE,
+ .reg_ofs_cur = AFE_ARB1_CUR,
+ .fs_reg = AFE_DAC_CON3,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 22,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 8,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 14,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 13,
+ .msb_reg = -1,
+ },
+ {
+ .name = "ULBT",
+ .id = MT2701_MEMIF_ULBT,
+ .reg_ofs_base = AFE_DAI_BASE,
+ .reg_ofs_cur = AFE_DAI_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 30,
+ .fs_maskbit = 0x1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 17,
+ .hd_reg = AFE_MEMIF_HD_CON1,
+ .hd_shift = 20,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 16,
+ .msb_reg = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
+ {
+ .id = MT2701_IRQ_ASYS_IRQ1,
+ .irq_cnt_reg = ASYS_IRQ1_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ1_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1f,
+ .irq_en_reg = ASYS_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 0,
+ },
+ {
+ .id = MT2701_IRQ_ASYS_IRQ2,
+ .irq_cnt_reg = ASYS_IRQ2_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ2_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1f,
+ .irq_en_reg = ASYS_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 1,
+ },
+ {
+ .id = MT2701_IRQ_ASYS_IRQ3,
+ .irq_cnt_reg = ASYS_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ3_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1f,
+ .irq_en_reg = ASYS_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 2,
+ }
+};
+
+static const struct mt2701_i2s_data mt2701_i2s_data[][2] = {
+ {
+ { ASYS_I2SO1_CON, 0, 0x1f },
+ { ASYS_I2SIN1_CON, 0, 0x1f },
+ },
+ {
+ { ASYS_I2SO2_CON, 5, 0x1f },
+ { ASYS_I2SIN2_CON, 5, 0x1f },
+ },
+ {
+ { ASYS_I2SO3_CON, 10, 0x1f },
+ { ASYS_I2SIN3_CON, 10, 0x1f },
+ },
+ {
+ { ASYS_I2SO4_CON, 15, 0x1f },
+ { ASYS_I2SIN4_CON, 15, 0x1f },
+ },
+ /* TODO - extend control registers supported by newer SoCs */
+};
+
+static irqreturn_t mt2701_asys_isr(int irq_id, void *dev)
+{
+ int id;
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_memif *memif;
+ struct mtk_base_afe_irq *irq;
+ u32 status;
+
+ regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+ regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+
+ for (id = 0; id < MT2701_MEMIF_NUM; ++id) {
+ memif = &afe->memif[id];
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+ if (status & 1 << irq->irq_data->irq_clr_shift)
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mt2701_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+ return mt2701_afe_disable_clock(afe);
+}
+
+static int mt2701_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+ return mt2701_afe_enable_clock(afe);
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt2701_afe_private *afe_priv;
+ struct device *dev;
+ int i, irq_id, ret;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe_priv->soc = of_device_get_match_data(&pdev->dev);
+ afe->dev = &pdev->dev;
+ dev = afe->dev;
+
+ afe_priv->i2s_path = devm_kcalloc(dev,
+ afe_priv->soc->i2s_num,
+ sizeof(struct mt2701_i2s_path),
+ GFP_KERNEL);
+ if (!afe_priv->i2s_path)
+ return -ENOMEM;
+
+ irq_id = platform_get_irq_byname(pdev, "asys");
+ if (irq_id < 0)
+ return irq_id;
+
+ ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret) {
+ dev_err(dev, "could not request_irq for asys-isr\n");
+ return ret;
+ }
+
+ afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(afe->regmap)) {
+ dev_err(dev, "could not get regmap from parent\n");
+ return PTR_ERR(afe->regmap);
+ }
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* memif initialize */
+ afe->memif_size = MT2701_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data_array[i];
+ afe->memif[i].irq_usage = -1;
+ }
+
+ /* irq initialize */
+ afe->irqs_size = MT2701_IRQ_ASYS_END;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* I2S initialize */
+ for (i = 0; i < afe_priv->soc->i2s_num; i++) {
+ afe_priv->i2s_path[i].i2s_data[SNDRV_PCM_STREAM_PLAYBACK] =
+ &mt2701_i2s_data[i][SNDRV_PCM_STREAM_PLAYBACK];
+ afe_priv->i2s_path[i].i2s_data[SNDRV_PCM_STREAM_CAPTURE] =
+ &mt2701_i2s_data[i][SNDRV_PCM_STREAM_CAPTURE];
+ }
+
+ afe->mtk_afe_hardware = &mt2701_afe_hardware;
+ afe->memif_fs = mt2701_memif_fs;
+ afe->irq_fs = mt2701_irq_fs;
+ afe->reg_back_up_list = mt2701_afe_backup_list;
+ afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list);
+ afe->runtime_resume = mt2701_afe_runtime_resume;
+ afe->runtime_suspend = mt2701_afe_runtime_suspend;
+
+ /* initial audio related clock */
+ ret = mt2701_init_clock(afe);
+ if (ret) {
+ dev_err(dev, "init clock error\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, afe);
+
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mt2701_afe_runtime_resume(dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+ pm_runtime_get_sync(dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_platform;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mt2701_afe_pcm_dai_component,
+ mt2701_afe_pcm_dais,
+ ARRAY_SIZE(mt2701_afe_pcm_dais));
+ if (ret) {
+ dev_warn(dev, "err_dai_component\n");
+ goto err_platform;
+ }
+
+ return 0;
+
+err_platform:
+ pm_runtime_put_sync(dev);
+err_pm_disable:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static void mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt2701_afe_runtime_suspend(&pdev->dev);
+}
+
+static const struct mt2701_soc_variants mt2701_soc_v1 = {
+ .i2s_num = 4,
+};
+
+static const struct mt2701_soc_variants mt2701_soc_v2 = {
+ .has_one_heart_mode = true,
+ .i2s_num = 4,
+};
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt2701-audio", .data = &mt2701_soc_v1 },
+ { .compatible = "mediatek,mt7622-audio", .data = &mt2701_soc_v2 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt2701_afe_runtime_suspend,
+ mt2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt2701_afe_pcm_driver = {
+ .driver = {
+ .name = "mt2701-audio",
+ .of_match_table = mt2701_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt2701_afe_pm_ops),
+ },
+ .probe = mt2701_afe_pcm_dev_probe,
+ .remove = mt2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
new file mode 100644
index 000000000000..778a9dccfcaa
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ir Lian <ir.lian@mediatek.com>
+ * Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+ int i2s1_in_mux;
+ struct gpio_desc *i2s1_in_mux_sel_1;
+ struct gpio_desc *i2s1_in_mux_sel_2;
+};
+
+static const char * const i2sin_mux_switch_text[] = {
+ "ADC_SDOUT2",
+ "ADC_SDOUT3",
+ "I2S_IN_1",
+ "I2S_IN_2",
+};
+
+static const struct soc_enum i2sin_mux_enum =
+ SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
+
+static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
+ return 0;
+}
+
+static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+ if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
+ return 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ gpiod_set_value(priv->i2s1_in_mux_sel_1, 0);
+ gpiod_set_value(priv->i2s1_in_mux_sel_2, 0);
+ break;
+ case 1:
+ gpiod_set_value(priv->i2s1_in_mux_sel_1, 1);
+ gpiod_set_value(priv->i2s1_in_mux_sel_2, 0);
+ break;
+ case 2:
+ gpiod_set_value(priv->i2s1_in_mux_sel_1, 0);
+ gpiod_set_value(priv->i2s1_in_mux_sel_2, 1);
+ break;
+ case 3:
+ gpiod_set_value(priv->i2s1_in_mux_sel_1, 1);
+ gpiod_set_value(priv->i2s1_in_mux_sel_2, 1);
+ break;
+ default:
+ dev_warn(card->dev, "%s invalid setting\n", __func__);
+ }
+
+ priv->i2s1_in_mux = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget
+ mt2701_cs42448_asoc_card_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+ SND_SOC_DAPM_LINE("Tuner In", NULL),
+ SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
+ SND_SOC_DAPM_LINE("AUX In", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Line Out Jack"),
+ SOC_DAPM_PIN_SWITCH("AMIC"),
+ SOC_DAPM_PIN_SWITCH("Tuner In"),
+ SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
+ SOC_DAPM_PIN_SWITCH("AUX In"),
+ SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
+ mt2701_cs42448_i2sin1_mux_get,
+ mt2701_cs42448_i2sin1_mux_set),
+};
+
+static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
+
+static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+ .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
+ .list = mt2701_cs42448_sampling_rates,
+ .mask = 0,
+};
+
+static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
+{
+ int err;
+
+ err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &mt2701_cs42448_constraints_rates);
+ if (err < 0) {
+ dev_err(substream->pcm->card->dev,
+ "%s snd_pcm_hw_constraint_list failed: 0x%x\n",
+ __func__, err);
+ return err;
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+ .startup = mt2701_cs42448_fe_ops_startup,
+};
+
+static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int mclk_rate;
+ unsigned int rate = params_rate(params);
+ unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
+ unsigned int div_bck_over_lrck = 64;
+
+ mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
+
+ /* mt2701 mclk */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+
+ /* codec mclk */
+ snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt2701_cs42448_be_ops = {
+ .hw_params = mt2701_cs42448_be_ops_hw_params
+};
+
+enum {
+ DAI_LINK_FE_MULTI_CH_OUT,
+ DAI_LINK_FE_PCM0_IN,
+ DAI_LINK_FE_PCM1_IN,
+ DAI_LINK_FE_BT_OUT,
+ DAI_LINK_FE_BT_IN,
+ DAI_LINK_BE_I2S0,
+ DAI_LINK_BE_I2S1,
+ DAI_LINK_BE_I2S2,
+ DAI_LINK_BE_I2S3,
+ DAI_LINK_BE_MRG_BT,
+};
+
+SND_SOC_DAILINK_DEFS(fe_multi_ch_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM_multi")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(fe_pcm0_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(fe_pcm1_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(fe_bt_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_DL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(fe_bt_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_UL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(be_i2s0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(be_i2s1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(be_i2s2,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(be_i2s3,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(be_mrg_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("MRG BT")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "bt-sco-pcm-wb")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
+ /* FE */
+ [DAI_LINK_FE_MULTI_CH_OUT] = {
+ .name = "mt2701-cs42448-multi-ch-out",
+ .stream_name = "mt2701-cs42448-multi-ch-out",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ops = &mt2701_cs42448_48k_fe_ops,
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(fe_multi_ch_out),
+ },
+ [DAI_LINK_FE_PCM0_IN] = {
+ .name = "mt2701-cs42448-pcm0",
+ .stream_name = "mt2701-cs42448-pcm0-data-UL",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ops = &mt2701_cs42448_48k_fe_ops,
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(fe_pcm0_in),
+ },
+ [DAI_LINK_FE_PCM1_IN] = {
+ .name = "mt2701-cs42448-pcm1-data-UL",
+ .stream_name = "mt2701-cs42448-pcm1-data-UL",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ops = &mt2701_cs42448_48k_fe_ops,
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(fe_pcm1_in),
+ },
+ [DAI_LINK_FE_BT_OUT] = {
+ .name = "mt2701-cs42448-pcm-BT-out",
+ .stream_name = "mt2701-cs42448-pcm-BT",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(fe_bt_out),
+ },
+ [DAI_LINK_FE_BT_IN] = {
+ .name = "mt2701-cs42448-pcm-BT-in",
+ .stream_name = "mt2701-cs42448-pcm-BT",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(fe_bt_in),
+ },
+ /* BE */
+ [DAI_LINK_BE_I2S0] = {
+ .name = "mt2701-cs42448-I2S0",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ SND_SOC_DAILINK_REG(be_i2s0),
+ },
+ [DAI_LINK_BE_I2S1] = {
+ .name = "mt2701-cs42448-I2S1",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ SND_SOC_DAILINK_REG(be_i2s1),
+ },
+ [DAI_LINK_BE_I2S2] = {
+ .name = "mt2701-cs42448-I2S2",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ SND_SOC_DAILINK_REG(be_i2s2),
+ },
+ [DAI_LINK_BE_I2S3] = {
+ .name = "mt2701-cs42448-I2S3",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ SND_SOC_DAILINK_REG(be_i2s3),
+ },
+ [DAI_LINK_BE_MRG_BT] = {
+ .name = "mt2701-cs42448-MRG-BT",
+ .no_pcm = 1,
+ SND_SOC_DAILINK_REG(be_mrg_bt),
+ },
+};
+
+static struct snd_soc_card mt2701_cs42448_soc_card = {
+ .name = "mt2701-cs42448",
+ .owner = THIS_MODULE,
+ .dai_link = mt2701_cs42448_dai_links,
+ .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
+ .controls = mt2701_cs42448_controls,
+ .num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
+ .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
+};
+
+static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt2701_cs42448_soc_card;
+ int ret;
+ int i;
+ struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
+ struct device *dev = &pdev->dev;
+ struct mt2701_cs42448_private *priv =
+ devm_kzalloc(dev, sizeof(struct mt2701_cs42448_private),
+ GFP_KERNEL);
+ struct snd_soc_dai_link *dai_link;
+
+ if (!priv)
+ return -ENOMEM;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ card->dev = dev;
+
+ codec_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,audio-codec", 0);
+ if (!codec_node) {
+ dev_err(dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->codecs->name)
+ continue;
+ dai_link->codecs->of_node = codec_node;
+ }
+
+ codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,audio-codec-bt-mrg", 0);
+ if (!codec_node_bt_mrg) {
+ dev_err(dev,
+ "Property 'audio-codec-bt-mrg' missing or invalid\n");
+ return -EINVAL;
+ }
+ mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codecs->of_node
+ = codec_node_bt_mrg;
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret) {
+ dev_err(dev, "failed to parse audio-routing: %d\n", ret);
+ return ret;
+ }
+
+ priv->i2s1_in_mux_sel_1 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio1",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->i2s1_in_mux_sel_1))
+ return dev_err_probe(dev, PTR_ERR(priv->i2s1_in_mux_sel_1),
+ "error getting mux 1 selector\n");
+
+ priv->i2s1_in_mux_sel_2 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio2",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->i2s1_in_mux_sel_2))
+ return dev_err_probe(dev, PTR_ERR(priv->i2s1_in_mux_sel_2),
+ "error getting mux 2 selector\n");
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = devm_snd_soc_register_card(dev, card);
+
+ if (ret)
+ dev_err(dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+ {.compatible = "mediatek,mt2701-cs42448-machine",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt2701_cs42448_machine_dt_match);
+#endif
+
+static struct platform_driver mt2701_cs42448_machine = {
+ .driver = {
+ .name = "mt2701-cs42448",
+ #ifdef CONFIG_OF
+ .of_match_table = mt2701_cs42448_machine_dt_match,
+ #endif
+ },
+ .probe = mt2701_cs42448_machine_probe,
+};
+
+module_platform_driver(mt2701_cs42448_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
+MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644
index 000000000000..c84d14cdd7ae
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt2701-reg.h -- Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/* AFE_DAIBT_CON0 (0x001c) */
+#define AFE_DAIBT_CON0_DAIBT_EN (0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN (0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY (0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN (0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE (0x1 << 12)
+
+/* PWR1_ASM_CON1 (0x0108) */
+#define PWR1_ASM_CON1_INIT_VAL (0x492)
+
+/* AFE_MRGIF_CON (0x003c) */
+#define AFE_MRGIF_CON_MRG_EN (0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN (0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK (0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K (0x4 << 20)
+
+/* ASYS_TOP_CON (0x0600) */
+#define ASYS_TOP_CON_ASYS_TIMING_ON (0x3 << 0)
+
+/* PWR2_ASM_CON1 (0x1070) */
+#define PWR2_ASM_CON1_INIT_VAL (0x492492)
+
+/* AFE_DAC_CON0 (0x1200) */
+#define AFE_DAC_CON0_AFE_ON (0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238) */
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK (0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE (0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE (0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK (0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK (0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x) ((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK (0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES (0x1 << 12)
+
+/* I2S in/out register bit control */
+#define ASYS_I2S_CON_FS (0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x) ((x) << 8)
+#define ASYS_I2S_CON_RESET (0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN (0x1 << 0)
+#define ASYS_I2S_CON_ONE_HEART_MODE (0x1 << 16)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE (0x1 << 17)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2S_CON_I2S_MODE (0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE (0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x) ((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX (0x1 << 31)
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
new file mode 100644
index 000000000000..84b3d6cd77a5
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt2701-wm8960.c -- MT2701 WM8960 ALSA SoC machine driver
+ *
+ * Copyright (c) 2017 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+static const struct snd_soc_dapm_widget mt2701_wm8960_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_wm8960_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("AMIC"),
+};
+
+static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ unsigned int mclk_rate;
+ unsigned int rate = params_rate(params);
+ unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
+ unsigned int div_bck_over_lrck = 64;
+
+ mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
+
+ snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt2701_wm8960_be_ops = {
+ .hw_params = mt2701_wm8960_be_ops_hw_params
+};
+
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCMO0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = {
+ /* FE */
+ {
+ .name = "wm8960-playback",
+ .stream_name = "wm8960-playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
+ },
+ {
+ .name = "wm8960-capture",
+ .stream_name = "wm8960-capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+ /* BE */
+ {
+ .name = "wm8960-codec",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_wm8960_be_ops,
+ SND_SOC_DAILINK_REG(codec),
+ },
+};
+
+static struct snd_soc_card mt2701_wm8960_card = {
+ .name = "mt2701-wm8960",
+ .owner = THIS_MODULE,
+ .dai_link = mt2701_wm8960_dai_links,
+ .num_links = ARRAY_SIZE(mt2701_wm8960_dai_links),
+ .controls = mt2701_wm8960_controls,
+ .num_controls = ARRAY_SIZE(mt2701_wm8960_controls),
+ .dapm_widgets = mt2701_wm8960_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets),
+};
+
+static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt2701_wm8960_card;
+ struct device_node *platform_node, *codec_node;
+ struct snd_soc_dai_link *dai_link;
+ int ret, i;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ card->dev = &pdev->dev;
+
+ codec_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,audio-codec", 0);
+ if (!codec_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto put_platform_node;
+ }
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->codecs->name)
+ continue;
+ dai_link->codecs->of_node = codec_node;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+ goto put_codec_node;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+
+put_codec_node:
+ of_node_put(codec_node);
+put_platform_node:
+ of_node_put(platform_node);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_wm8960_machine_dt_match[] = {
+ {.compatible = "mediatek,mt2701-wm8960-machine",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt2701_wm8960_machine_dt_match);
+#endif
+
+static struct platform_driver mt2701_wm8960_machine = {
+ .driver = {
+ .name = "mt2701-wm8960",
+#ifdef CONFIG_OF
+ .of_match_table = mt2701_wm8960_machine_dt_match,
+#endif
+ },
+ .probe = mt2701_wm8960_machine_probe,
+};
+
+module_platform_driver(mt2701_wm8960_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver");
+MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 wm8960 soc card");
+
diff --git a/sound/soc/mediatek/mt6797/Makefile b/sound/soc/mediatek/mt6797/Makefile
new file mode 100644
index 000000000000..150021495e94
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt6797-afe-y := \
+ mt6797-afe-pcm.o \
+ mt6797-afe-clk.o \
+ mt6797-dai-pcm.o \
+ mt6797-dai-hostless.o \
+ mt6797-dai-adda.o
+
+obj-$(CONFIG_SND_SOC_MT6797) += snd-soc-mt6797-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT6797_MT6351) += mt6797-mt6351.o
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-clk.c b/sound/soc/mediatek/mt6797/mt6797-afe-clk.c
new file mode 100644
index 000000000000..6f3e6acfcfab
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-clk.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6797-afe-clk.c -- Mediatek 6797 afe clock ctrl
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/clk.h>
+
+#include "mt6797-afe-common.h"
+#include "mt6797-afe-clk.h"
+
+enum {
+ CLK_INFRA_SYS_AUD,
+ CLK_INFRA_SYS_AUD_26M,
+ CLK_TOP_MUX_AUD,
+ CLK_TOP_MUX_AUD_BUS,
+ CLK_TOP_SYSPLL3_D4,
+ CLK_TOP_SYSPLL1_D4,
+ CLK_CLK26M,
+ CLK_NUM
+};
+
+static const char *aud_clks[CLK_NUM] = {
+ [CLK_INFRA_SYS_AUD] = "infra_sys_audio_clk",
+ [CLK_INFRA_SYS_AUD_26M] = "infra_sys_audio_26m",
+ [CLK_TOP_MUX_AUD] = "top_mux_audio",
+ [CLK_TOP_MUX_AUD_BUS] = "top_mux_aud_intbus",
+ [CLK_TOP_SYSPLL3_D4] = "top_sys_pll3_d4",
+ [CLK_TOP_SYSPLL1_D4] = "top_sys_pll1_d4",
+ [CLK_CLK26M] = "top_clk26m_clk",
+};
+
+int mt6797_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt6797_afe_private *afe_priv = afe->platform_priv;
+ int i;
+
+ afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
+ __func__, aud_clks[i],
+ PTR_ERR(afe_priv->clk[i]));
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ return 0;
+}
+
+int mt6797_afe_enable_clock(struct mtk_base_afe *afe)
+{
+ struct mt6797_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUD]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_SYS_AUD], ret);
+ goto CLK_INFRA_SYS_AUDIO_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_SYS_AUD_26M], ret);
+ goto CLK_INFRA_SYS_AUD_26M_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD], ret);
+ goto CLK_MUX_AUDIO_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD],
+ aud_clks[CLK_CLK26M], ret);
+ goto CLK_MUX_AUDIO_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_BUS], ret);
+ goto CLK_MUX_AUDIO_INTBUS_ERR;
+ }
+
+ return ret;
+
+CLK_MUX_AUDIO_INTBUS_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]);
+CLK_MUX_AUDIO_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD]);
+CLK_INFRA_SYS_AUD_26M_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]);
+CLK_INFRA_SYS_AUDIO_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD]);
+
+ return 0;
+}
+
+int mt6797_afe_disable_clock(struct mtk_base_afe *afe)
+{
+ struct mt6797_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD]);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-clk.h b/sound/soc/mediatek/mt6797/mt6797-afe-clk.h
new file mode 100644
index 000000000000..a6f0cb572711
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-clk.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6797-afe-clk.h -- Mediatek 6797 afe clock ctrl definition
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT6797_AFE_CLK_H_
+#define _MT6797_AFE_CLK_H_
+
+struct mtk_base_afe;
+
+int mt6797_init_clock(struct mtk_base_afe *afe);
+int mt6797_afe_enable_clock(struct mtk_base_afe *afe);
+int mt6797_afe_disable_clock(struct mtk_base_afe *afe);
+#endif
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-common.h b/sound/soc/mediatek/mt6797/mt6797-afe-common.h
new file mode 100644
index 000000000000..4eac9977b2b0
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-common.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6797-afe-common.h -- Mediatek 6797 audio driver definitions
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT_6797_AFE_COMMON_H_
+#define _MT_6797_AFE_COMMON_H_
+
+#include <sound/soc.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT6797_MEMIF_DL1,
+ MT6797_MEMIF_DL2,
+ MT6797_MEMIF_DL3,
+ MT6797_MEMIF_VUL,
+ MT6797_MEMIF_AWB,
+ MT6797_MEMIF_VUL12,
+ MT6797_MEMIF_DAI,
+ MT6797_MEMIF_MOD_DAI,
+ MT6797_MEMIF_NUM,
+ MT6797_DAI_ADDA = MT6797_MEMIF_NUM,
+ MT6797_DAI_PCM_1,
+ MT6797_DAI_PCM_2,
+ MT6797_DAI_HOSTLESS_LPBK,
+ MT6797_DAI_HOSTLESS_SPEECH,
+ MT6797_DAI_NUM,
+};
+
+enum {
+ MT6797_IRQ_1,
+ MT6797_IRQ_2,
+ MT6797_IRQ_3,
+ MT6797_IRQ_4,
+ MT6797_IRQ_7,
+ MT6797_IRQ_NUM,
+};
+
+struct clk;
+
+struct mt6797_afe_private {
+ struct clk **clk;
+};
+
+unsigned int mt6797_general_rate_transform(struct device *dev,
+ unsigned int rate);
+unsigned int mt6797_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk);
+
+/* dai register */
+int mt6797_dai_adda_register(struct mtk_base_afe *afe);
+int mt6797_dai_pcm_register(struct mtk_base_afe *afe);
+int mt6797_dai_hostless_register(struct mtk_base_afe *afe);
+#endif
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
new file mode 100644
index 000000000000..f62a32f2f2b6
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
@@ -0,0 +1,900 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Mediatek ALSA SoC AFE platform driver for 6797
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+
+#include "mt6797-afe-common.h"
+#include "mt6797-afe-clk.h"
+#include "mt6797-interconnection.h"
+#include "mt6797-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+enum {
+ MTK_AFE_RATE_8K = 0,
+ MTK_AFE_RATE_11K = 1,
+ MTK_AFE_RATE_12K = 2,
+ MTK_AFE_RATE_384K = 3,
+ MTK_AFE_RATE_16K = 4,
+ MTK_AFE_RATE_22K = 5,
+ MTK_AFE_RATE_24K = 6,
+ MTK_AFE_RATE_130K = 7,
+ MTK_AFE_RATE_32K = 8,
+ MTK_AFE_RATE_44K = 9,
+ MTK_AFE_RATE_48K = 10,
+ MTK_AFE_RATE_88K = 11,
+ MTK_AFE_RATE_96K = 12,
+ MTK_AFE_RATE_174K = 13,
+ MTK_AFE_RATE_192K = 14,
+ MTK_AFE_RATE_260K = 15,
+};
+
+enum {
+ MTK_AFE_DAI_MEMIF_RATE_8K = 0,
+ MTK_AFE_DAI_MEMIF_RATE_16K = 1,
+ MTK_AFE_DAI_MEMIF_RATE_32K = 2,
+};
+
+enum {
+ MTK_AFE_PCM_RATE_8K = 0,
+ MTK_AFE_PCM_RATE_16K = 1,
+ MTK_AFE_PCM_RATE_32K = 2,
+ MTK_AFE_PCM_RATE_48K = 3,
+};
+
+unsigned int mt6797_general_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_RATE_8K;
+ case 11025:
+ return MTK_AFE_RATE_11K;
+ case 12000:
+ return MTK_AFE_RATE_12K;
+ case 16000:
+ return MTK_AFE_RATE_16K;
+ case 22050:
+ return MTK_AFE_RATE_22K;
+ case 24000:
+ return MTK_AFE_RATE_24K;
+ case 32000:
+ return MTK_AFE_RATE_32K;
+ case 44100:
+ return MTK_AFE_RATE_44K;
+ case 48000:
+ return MTK_AFE_RATE_48K;
+ case 88200:
+ return MTK_AFE_RATE_88K;
+ case 96000:
+ return MTK_AFE_RATE_96K;
+ case 130000:
+ return MTK_AFE_RATE_130K;
+ case 176400:
+ return MTK_AFE_RATE_174K;
+ case 192000:
+ return MTK_AFE_RATE_192K;
+ case 260000:
+ return MTK_AFE_RATE_260K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_RATE_48K);
+ return MTK_AFE_RATE_48K;
+ }
+}
+
+static unsigned int dai_memif_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_DAI_MEMIF_RATE_8K;
+ case 16000:
+ return MTK_AFE_DAI_MEMIF_RATE_16K;
+ case 32000:
+ return MTK_AFE_DAI_MEMIF_RATE_32K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_DAI_MEMIF_RATE_16K);
+ return MTK_AFE_DAI_MEMIF_RATE_16K;
+ }
+}
+
+unsigned int mt6797_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk)
+{
+ switch (aud_blk) {
+ case MT6797_MEMIF_DAI:
+ case MT6797_MEMIF_MOD_DAI:
+ return dai_memif_rate_transform(dev, rate);
+ default:
+ return mt6797_general_rate_transform(dev, rate);
+ }
+}
+
+static const struct snd_pcm_hardware mt6797_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 256,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 8 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+static int mt6797_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+
+ return mt6797_rate_transform(afe->dev, rate, id);
+}
+
+static int mt6797_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return mt6797_general_rate_transform(afe->dev, rate);
+}
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt6797_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = MT6797_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL2",
+ .id = MT6797_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT6797_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT6797_MEMIF_VUL12,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT6797_MEMIF_AWB,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT6797_MEMIF_VUL,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL_MONO_1",
+ .id = MT6797_MEMIF_MOD_DAI,
+ .capture = {
+ .stream_name = "UL_MONO_1",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MTK_PCM_DAI_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL_MONO_2",
+ .id = MT6797_MEMIF_DAI,
+ .capture = {
+ .stream_name = "UL_MONO_2",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MTK_PCM_DAI_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+};
+
+/* dma widget & routes*/
+static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN21,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN22,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN5,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN5,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN5,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN5,
+ I_DL3_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN6,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN6,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN6,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN6,
+ I_DL3_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN9,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN10,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_mono_1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN12,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN12,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_mono_2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN11,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN11,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mt6797_memif_widgets[] = {
+ /* memif */
+ SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_MONO_1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_mono_1_mix,
+ ARRAY_SIZE(memif_ul_mono_1_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_MONO_2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_mono_2_mix,
+ ARRAY_SIZE(memif_ul_mono_2_mix)),
+};
+
+static const struct snd_soc_dapm_route mt6797_memif_routes[] = {
+ /* capture */
+ {"UL1", NULL, "UL1_CH1"},
+ {"UL1", NULL, "UL1_CH2"},
+ {"UL1_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL1_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL2", NULL, "UL2_CH1"},
+ {"UL2", NULL, "UL2_CH2"},
+ {"UL2_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL2_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL3", NULL, "UL3_CH1"},
+ {"UL3", NULL, "UL3_CH2"},
+ {"UL3_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL3_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL_MONO_1", NULL, "UL_MONO_1_CH1"},
+ {"UL_MONO_1_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_MONO_1_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL_MONO_2", NULL, "UL_MONO_2_CH1"},
+ {"UL_MONO_2_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_MONO_2_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+};
+
+static const struct snd_soc_component_driver mt6797_afe_pcm_dai_component = {
+ .name = "mt6797-afe-pcm-dai",
+};
+
+static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
+ [MT6797_MEMIF_DL1] = {
+ .name = "DL1",
+ .id = MT6797_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = DL1_MODE_SFT,
+ .fs_maskbit = DL1_MODE_MASK,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = DL1_DATA_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL1_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = DL1_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_DL2] = {
+ .name = "DL2",
+ .id = MT6797_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = DL2_MODE_SFT,
+ .fs_maskbit = DL2_MODE_MASK,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = DL2_DATA_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL2_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = DL2_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_DL3] = {
+ .name = "DL3",
+ .id = MT6797_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .fs_reg = AFE_DAC_CON0,
+ .fs_shift = DL3_MODE_SFT,
+ .fs_maskbit = DL3_MODE_MASK,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = DL3_DATA_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL3_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = DL3_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_VUL] = {
+ .name = "VUL",
+ .id = MT6797_MEMIF_VUL,
+ .reg_ofs_base = AFE_VUL_BASE,
+ .reg_ofs_cur = AFE_VUL_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = VUL_MODE_SFT,
+ .fs_maskbit = VUL_MODE_MASK,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = VUL_DATA_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = VUL_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_AWB] = {
+ .name = "AWB",
+ .id = MT6797_MEMIF_AWB,
+ .reg_ofs_base = AFE_AWB_BASE,
+ .reg_ofs_cur = AFE_AWB_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = AWB_MODE_SFT,
+ .fs_maskbit = AWB_MODE_MASK,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = AWB_DATA_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AWB_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = AWB_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_VUL12] = {
+ .name = "VUL12",
+ .id = MT6797_MEMIF_VUL12,
+ .reg_ofs_base = AFE_VUL_D2_BASE,
+ .reg_ofs_cur = AFE_VUL_D2_CUR,
+ .fs_reg = AFE_DAC_CON0,
+ .fs_shift = VUL_DATA2_MODE_SFT,
+ .fs_maskbit = VUL_DATA2_MODE_MASK,
+ .mono_reg = AFE_DAC_CON0,
+ .mono_shift = VUL_DATA2_DATA_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL_DATA2_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = VUL_DATA2_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_DAI] = {
+ .name = "DAI",
+ .id = MT6797_MEMIF_DAI,
+ .reg_ofs_base = AFE_DAI_BASE,
+ .reg_ofs_cur = AFE_DAI_CUR,
+ .fs_reg = AFE_DAC_CON0,
+ .fs_shift = DAI_MODE_SFT,
+ .fs_maskbit = DAI_MODE_MASK,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DAI_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = DAI_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+ [MT6797_MEMIF_MOD_DAI] = {
+ .name = "MOD_DAI",
+ .id = MT6797_MEMIF_MOD_DAI,
+ .reg_ofs_base = AFE_MOD_DAI_BASE,
+ .reg_ofs_cur = AFE_MOD_DAI_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = MOD_DAI_MODE_SFT,
+ .fs_maskbit = MOD_DAI_MODE_MASK,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = MOD_DAI_ON_SFT,
+ .hd_reg = AFE_MEMIF_HD_MODE,
+ .hd_shift = MOD_DAI_HD_SFT,
+ .agent_disable_reg = -1,
+ .msb_reg = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT6797_IRQ_NUM] = {
+ [MT6797_IRQ_1] = {
+ .id = MT6797_IRQ_1,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT1,
+ .irq_cnt_shift = AFE_IRQ_MCU_CNT1_SFT,
+ .irq_cnt_maskbit = AFE_IRQ_MCU_CNT1_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = IRQ1_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ1_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = IRQ1_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ1_MCU_CLR_SFT,
+ },
+ [MT6797_IRQ_2] = {
+ .id = MT6797_IRQ_2,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT2,
+ .irq_cnt_shift = AFE_IRQ_MCU_CNT2_SFT,
+ .irq_cnt_maskbit = AFE_IRQ_MCU_CNT2_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = IRQ2_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ2_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = IRQ2_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ2_MCU_CLR_SFT,
+ },
+ [MT6797_IRQ_3] = {
+ .id = MT6797_IRQ_3,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT3,
+ .irq_cnt_shift = AFE_IRQ_MCU_CNT3_SFT,
+ .irq_cnt_maskbit = AFE_IRQ_MCU_CNT3_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = IRQ3_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ3_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = IRQ3_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ3_MCU_CLR_SFT,
+ },
+ [MT6797_IRQ_4] = {
+ .id = MT6797_IRQ_4,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT4,
+ .irq_cnt_shift = AFE_IRQ_MCU_CNT4_SFT,
+ .irq_cnt_maskbit = AFE_IRQ_MCU_CNT4_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = IRQ4_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ4_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = IRQ4_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ4_MCU_CLR_SFT,
+ },
+ [MT6797_IRQ_7] = {
+ .id = MT6797_IRQ_7,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT7,
+ .irq_cnt_shift = AFE_IRQ_MCU_CNT7_SFT,
+ .irq_cnt_maskbit = AFE_IRQ_MCU_CNT7_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = IRQ7_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ7_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = IRQ7_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ7_MCU_CLR_SFT,
+ },
+};
+
+static const struct regmap_config mt6797_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_MAX_REGISTER,
+};
+
+static irqreturn_t mt6797_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ unsigned int status;
+ unsigned int mcu_en;
+ int ret;
+ int i;
+ irqreturn_t irq_ret = IRQ_HANDLED;
+
+ /* get irq that is sent to MCU */
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ if (ret || (status & mcu_en) == 0) {
+ dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n",
+ __func__, ret, status, mcu_en);
+
+ /* only clear IRQ which is sent to MCU */
+ status = mcu_en & AFE_IRQ_STATUS_BITS;
+
+ irq_ret = IRQ_NONE;
+ goto err_irq;
+ }
+
+ for (i = 0; i < MT6797_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (status & (1 << irq->irq_data->irq_en_shift))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap,
+ AFE_IRQ_MCU_CLR,
+ status & AFE_IRQ_STATUS_BITS);
+
+ return irq_ret;
+}
+
+static int mt6797_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ unsigned int afe_on_retm;
+ int retry = 0;
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, AFE_ON_MASK_SFT, 0x0);
+ do {
+ regmap_read(afe->regmap, AFE_DAC_CON0, &afe_on_retm);
+ if ((afe_on_retm & AFE_ON_RETM_MASK_SFT) == 0)
+ break;
+
+ udelay(10);
+ } while (++retry < 100000);
+
+ if (retry)
+ dev_warn(afe->dev, "%s(), retry %d\n", __func__, retry);
+
+ /* make sure all irq status are cleared */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff);
+
+ return mt6797_afe_disable_clock(afe);
+}
+
+static int mt6797_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ int ret;
+
+ ret = mt6797_afe_enable_clock(afe);
+ if (ret)
+ return ret;
+
+ /* irq signal to mcu only */
+ regmap_write(afe->regmap, AFE_IRQ_MCU_EN, AFE_IRQ_MCU_EN_MASK_SFT);
+
+ /* force all memif use normal mode */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_HDALIGN,
+ 0x7ff << 16, 0x7ff << 16);
+ /* force cpu use normal mode when access sram data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
+ CPU_COMPACT_MODE_MASK_SFT, 0);
+ /* force cpu use 8_24 format when writing 32bit data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
+ CPU_HD_ALIGN_MASK_SFT, 0);
+
+ /* set all output port to 24bit */
+ regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
+ 0x3fffffff, 0x3fffffff);
+
+ /* enable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_ON_MASK_SFT,
+ 0x1 << AFE_ON_SFT);
+
+ return 0;
+}
+
+static int mt6797_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt6797_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt6797_memif_dai_driver);
+
+ dai->dapm_widgets = mt6797_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt6797_memif_widgets);
+ dai->dapm_routes = mt6797_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt6797_memif_routes);
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt6797_dai_adda_register,
+ mt6797_dai_pcm_register,
+ mt6797_dai_hostless_register,
+ mt6797_dai_memif_register,
+};
+
+static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt6797_afe_private *afe_priv;
+ struct device *dev;
+ int i, irq_id, ret;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+ dev = afe->dev;
+
+ /* initial audio related clock */
+ ret = mt6797_init_clock(afe);
+ if (ret) {
+ dev_err(dev, "init clock error\n");
+ return ret;
+ }
+
+ /* regmap init */
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mt6797_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ /* init memif */
+ afe->memif_size = MT6797_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = -1;
+ }
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ afe->irqs_size = MT6797_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return irq_id;
+
+ ret = devm_request_irq(dev, irq_id, mt6797_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret) {
+ dev_err(dev, "could not request_irq for asys-isr\n");
+ return ret;
+ }
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret) {
+ dev_warn(afe->dev, "dai register i %d fail, ret %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret) {
+ dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ afe->mtk_afe_hardware = &mt6797_afe_hardware;
+ afe->memif_fs = mt6797_memif_fs;
+ afe->irq_fs = mt6797_irq_fs;
+
+ afe->runtime_resume = mt6797_afe_runtime_resume;
+ afe->runtime_suspend = mt6797_afe_runtime_suspend;
+
+ platform_set_drvdata(pdev, afe);
+
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev))
+ goto err_pm_disable;
+ pm_runtime_get_sync(&pdev->dev);
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_pm_disable;
+ }
+
+ ret = devm_snd_soc_register_component(afe->dev,
+ &mt6797_afe_pcm_dai_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_dai_component\n");
+ goto err_pm_disable;
+ }
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static void mt6797_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt6797_afe_runtime_suspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
+}
+
+static const struct of_device_id mt6797_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt6797-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6797_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt6797_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt6797_afe_runtime_suspend,
+ mt6797_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt6797_afe_pcm_driver = {
+ .driver = {
+ .name = "mt6797-audio",
+ .of_match_table = mt6797_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt6797_afe_pm_ops),
+ },
+ .probe = mt6797_afe_pcm_dev_probe,
+ .remove = mt6797_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt6797_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 6797");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-adda.c b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c
new file mode 100644
index 000000000000..78f3ad758c12
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI ADDA Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include "mt6797-afe-common.h"
+#include "mt6797-interconnection.h"
+#include "mt6797-reg.h"
+#include "../common/mtk-dai-adda-common.h"
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN3, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN3, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN3, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN3,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN3,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN3,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN3,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN4, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN4, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN4, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN4, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN4, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN4, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN4,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN4,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN4,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN4,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN4,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN4,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+enum {
+ SUPPLY_SEQ_AUD_TOP_PDN,
+ SUPPLY_SEQ_ADDA_AFE_ON,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_UL_ON,
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ /* adda */
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch1_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch2_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("aud_dac_clk", SUPPLY_SEQ_AUD_TOP_PDN,
+ AUDIO_TOP_CON0, PDN_DAC_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("aud_dac_predis_clk", SUPPLY_SEQ_AUD_TOP_PDN,
+ AUDIO_TOP_CON0, PDN_DAC_PREDIS_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("aud_adc_clk", SUPPLY_SEQ_AUD_TOP_PDN,
+ AUDIO_TOP_CON0, PDN_ADC_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("mtkaif_26m_clk"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* playback */
+ {"ADDA_DL_CH1", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH2", "DL1"},
+
+ {"ADDA_DL_CH1", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH2", "DL2"},
+
+ {"ADDA_DL_CH1", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH2", "DL3"},
+
+ {"ADDA Playback", NULL, "ADDA_DL_CH1"},
+ {"ADDA Playback", NULL, "ADDA_DL_CH2"},
+
+ /* adda enable */
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+
+ /* clk */
+ {"ADDA Playback", NULL, "mtkaif_26m_clk"},
+ {"ADDA Playback", NULL, "aud_dac_clk"},
+ {"ADDA Playback", NULL, "aud_dac_predis_clk"},
+
+ {"ADDA Capture", NULL, "mtkaif_26m_clk"},
+ {"ADDA Capture", NULL, "aud_adc_clk"},
+};
+
+/* dai ops */
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, dai->id, substream->stream, rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int dl_src2_con0 = 0;
+ unsigned int dl_src2_con1 = 0;
+
+ /* clean predistortion */
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
+
+ /* set input sampling rate */
+ dl_src2_con0 = mtk_adda_dl_rate_transform(afe, rate) << 28;
+
+ /* set output mode */
+ switch (rate) {
+ case 192000:
+ dl_src2_con0 |= (0x1 << 24); /* UP_SAMPLING_RATE_X2 */
+ dl_src2_con0 |= 1 << 14;
+ break;
+ case 96000:
+ dl_src2_con0 |= (0x2 << 24); /* UP_SAMPLING_RATE_X4 */
+ dl_src2_con0 |= 1 << 14;
+ break;
+ default:
+ dl_src2_con0 |= (0x3 << 24); /* UP_SAMPLING_RATE_X8 */
+ break;
+ }
+
+ /* turn off mute function */
+ dl_src2_con0 |= (0x03 << 11);
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ if (rate == 8000 || rate == 16000)
+ dl_src2_con0 |= 0x01 << 5;
+
+ if (rate < 96000) {
+ /* SA suggest apply -0.3db to audio/speech path */
+ dl_src2_con1 = 0xf74f0000;
+ } else {
+ /* SA suggest apply -0.3db to audio/speech path
+ * with DL gain set to half,
+ * 0xFFFF = 0dB -> 0x8000 = 0dB when 96k, 192k
+ */
+ dl_src2_con1 = 0x7ba70000;
+ }
+
+ /* turn on down-link gain */
+ dl_src2_con0 = dl_src2_con0 | (0x01 << 1);
+
+ regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, dl_src2_con0);
+ regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, dl_src2_con1);
+ } else {
+ unsigned int voice_mode = 0;
+ unsigned int ul_src_con0 = 0; /* default value */
+
+ /* Using Internal ADC */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_TOP_CON0,
+ 0x1 << 0,
+ 0x0 << 0);
+
+ voice_mode = mtk_adda_ul_rate_transform(afe, rate);
+
+ ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
+
+ /* up8x txif sat on */
+ regmap_write(afe->regmap, AFE_ADDA_NEWIF_CFG0, 0x03F87201);
+
+ if (rate >= 96000) { /* hires */
+ /* use hires format [1 0 23] */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_NEWIF_CFG0,
+ 0x1 << 5,
+ 0x1 << 5);
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_NEWIF_CFG2,
+ 0xf << 28,
+ voice_mode << 28);
+ } else { /* normal 8~48k */
+ /* use fixed 260k anc path */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_NEWIF_CFG2,
+ 0xf << 28,
+ 8 << 28);
+
+ /* ul_use_cic_out */
+ ul_src_con0 |= 0x1 << 20;
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_NEWIF_CFG2,
+ 0xf << 28,
+ 8 << 28);
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_UL_SRC_CON0,
+ 0xfffffffe,
+ ul_src_con0);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT6797_DAI_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+int mt6797_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c
new file mode 100644
index 000000000000..ed23e6a53b08
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI Hostless Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include "mt6797-afe-common.h"
+
+/* dai component */
+static const struct snd_soc_dapm_route mtk_dai_hostless_routes[] = {
+ /* Hostless ADDA Loopback */
+ {"ADDA_DL_CH1", "ADDA_UL_CH1", "Hostless LPBK DL"},
+ {"ADDA_DL_CH1", "ADDA_UL_CH2", "Hostless LPBK DL"},
+ {"ADDA_DL_CH2", "ADDA_UL_CH1", "Hostless LPBK DL"},
+ {"ADDA_DL_CH2", "ADDA_UL_CH2", "Hostless LPBK DL"},
+ {"Hostless LPBK UL", NULL, "ADDA Capture"},
+
+ /* Hostless Speech */
+ {"ADDA_DL_CH1", "PCM_1_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_1_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_1_CAP_CH2", "Hostless Speech DL"},
+ {"ADDA_DL_CH1", "PCM_2_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_2_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_2_CAP_CH2", "Hostless Speech DL"},
+ {"PCM_1_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"},
+ {"PCM_1_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"},
+ {"PCM_2_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"},
+ {"PCM_2_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"},
+
+ {"Hostless Speech UL", NULL, "PCM 1 Capture"},
+ {"Hostless Speech UL", NULL, "PCM 2 Capture"},
+ {"Hostless Speech UL", NULL, "ADDA Capture"},
+};
+
+/* dai ops */
+static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ return snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware);
+}
+
+static const struct snd_soc_dai_ops mtk_dai_hostless_ops = {
+ .startup = mtk_dai_hostless_startup,
+};
+
+/* dai driver */
+#define MTK_HOSTLESS_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_HOSTLESS_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = {
+ {
+ .name = "Hostless LPBK DAI",
+ .id = MT6797_DAI_HOSTLESS_LPBK,
+ .playback = {
+ .stream_name = "Hostless LPBK DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless LPBK UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless Speech DAI",
+ .id = MT6797_DAI_HOSTLESS_SPEECH,
+ .playback = {
+ .stream_name = "Hostless Speech DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless Speech UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+};
+
+int mt6797_dai_hostless_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_hostless_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver);
+
+ dai->dapm_routes = mtk_dai_hostless_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c
new file mode 100644
index 000000000000..8a309b0734f7
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt6797-afe-common.h"
+#include "mt6797-interconnection.h"
+#include "mt6797-reg.h"
+
+enum AUD_TX_LCH_RPT {
+ AUD_TX_LCH_RPT_NO_REPEAT = 0,
+ AUD_TX_LCH_RPT_REPEAT = 1
+};
+
+enum AUD_VBT_16K_MODE {
+ AUD_VBT_16K_MODE_DISABLE = 0,
+ AUD_VBT_16K_MODE_ENABLE = 1
+};
+
+enum AUD_EXT_MODEM {
+ AUD_EXT_MODEM_SELECT_INTERNAL = 0,
+ AUD_EXT_MODEM_SELECT_EXTERNAL = 1
+};
+
+enum AUD_PCM_SYNC_TYPE {
+ /* bck sync length = 1 */
+ AUD_PCM_ONE_BCK_CYCLE_SYNC = 0,
+ /* bck sync length = PCM_INTF_CON1[9:13] */
+ AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1
+};
+
+enum AUD_BT_MODE {
+ AUD_BT_MODE_DUAL_MIC_ON_TX = 0,
+ AUD_BT_MODE_SINGLE_MIC_ON_TX = 1
+};
+
+enum AUD_PCM_AFIFO_SRC {
+ /* slave mode & external modem uses different crystal */
+ AUD_PCM_AFIFO_ASRC = 0,
+ /* slave mode & external modem uses the same crystal */
+ AUD_PCM_AFIFO_AFIFO = 1
+};
+
+enum AUD_PCM_CLOCK_SOURCE {
+ AUD_PCM_CLOCK_MASTER_MODE = 0,
+ AUD_PCM_CLOCK_SLAVE_MODE = 1
+};
+
+enum AUD_PCM_WLEN {
+ AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0,
+ AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1
+};
+
+enum AUD_PCM_MODE {
+ AUD_PCM_MODE_PCM_MODE_8K = 0,
+ AUD_PCM_MODE_PCM_MODE_16K = 1,
+ AUD_PCM_MODE_PCM_MODE_32K = 2,
+ AUD_PCM_MODE_PCM_MODE_48K = 3,
+};
+
+enum AUD_PCM_FMT {
+ AUD_PCM_FMT_I2S = 0,
+ AUD_PCM_FMT_EIAJ = 1,
+ AUD_PCM_FMT_PCM_MODE_A = 2,
+ AUD_PCM_FMT_PCM_MODE_B = 3
+};
+
+enum AUD_BCLK_OUT_INV {
+ AUD_BCLK_OUT_INV_NO_INVERSE = 0,
+ AUD_BCLK_OUT_INV_INVERSE = 1
+};
+
+enum AUD_PCM_EN {
+ AUD_PCM_EN_DISABLE = 0,
+ AUD_PCM_EN_ENABLE = 1
+};
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN7,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN7,
+ I_DL2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN8,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN8,
+ I_DL2_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN27,
+ I_DL1_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN17,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN17,
+ I_DL2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN18,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN18,
+ I_DL2_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN24,
+ I_DL1_CH1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch4_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch4_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, PCM_EN_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PCM_2_EN", PCM2_INTF_CON, PCM2_EN_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MD1_TO_AFE"),
+ SND_SOC_DAPM_INPUT("MD2_TO_AFE"),
+ SND_SOC_DAPM_OUTPUT("AFE_TO_MD1"),
+ SND_SOC_DAPM_OUTPUT("AFE_TO_MD2"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH4"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH1"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH2"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH4"},
+
+ {"PCM 1 Playback", NULL, "PCM_1_EN"},
+ {"PCM 2 Playback", NULL, "PCM_2_EN"},
+ {"PCM 1 Capture", NULL, "PCM_1_EN"},
+ {"PCM 2 Capture", NULL, "PCM_2_EN"},
+
+ {"AFE_TO_MD1", NULL, "PCM 2 Playback"},
+ {"AFE_TO_MD2", NULL, "PCM 1 Playback"},
+ {"PCM 2 Capture", NULL, "MD1_TO_AFE"},
+ {"PCM 1 Capture", NULL, "MD2_TO_AFE"},
+
+ {"PCM_1_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_1_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_1_PB_CH4", "DL1_CH1", "DL1"},
+ {"PCM_2_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_2_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_2_PB_CH4", "DL1_CH1", "DL1"},
+};
+
+/* dai ops */
+static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt6797_rate_transform(afe->dev, rate, dai->id);
+ unsigned int pcm_con = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d, rate_reg %d, widget active p %d, c %d\n",
+ __func__,
+ dai->id,
+ substream->stream,
+ rate,
+ rate_reg,
+ p->active,
+ c->active);
+
+ if (p->active || c->active)
+ return 0;
+
+ switch (dai->id) {
+ case MT6797_DAI_PCM_1:
+ pcm_con |= AUD_BCLK_OUT_INV_NO_INVERSE << PCM_BCLK_OUT_INV_SFT;
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_EXT_MODEM_SELECT_INTERNAL << PCM_EXT_MODEM_SFT;
+ pcm_con |= 0 << PCM_SYNC_LENGTH_SFT;
+ pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT;
+ pcm_con |= AUD_PCM_CLOCK_SLAVE_MODE << PCM_SLAVE_SFT;
+ pcm_con |= rate_reg << PCM_MODE_SFT;
+ pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM_FMT_SFT;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1,
+ 0xfffffffe, pcm_con);
+ break;
+ case MT6797_DAI_PCM_2:
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM2_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM2_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM2_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM2_AFIFO_SFT;
+ pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM2_WLEN_SFT;
+ pcm_con |= rate_reg << PCM2_MODE_SFT;
+ pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM2_FMT_SFT;
+
+ regmap_update_bits(afe->regmap, PCM2_INTF_CON,
+ 0xfffffffe, pcm_con);
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .hw_params = mtk_dai_pcm_hw_params,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM 1",
+ .id = MT6797_DAI_PCM_1,
+ .playback = {
+ .stream_name = "PCM 1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "PCM 2",
+ .id = MT6797_DAI_PCM_2,
+ .playback = {
+ .stream_name = "PCM 2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int mt6797_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt6797/mt6797-interconnection.h b/sound/soc/mediatek/mt6797/mt6797-interconnection.h
new file mode 100644
index 000000000000..07b759b20079
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-interconnection.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Mediatek MT6797 audio driver interconnection definition
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT6797_INTERCONNECTION_H_
+#define _MT6797_INTERCONNECTION_H_
+
+#define I_I2S0_CH1 0
+#define I_I2S0_CH2 1
+#define I_ADDA_UL_CH1 3
+#define I_ADDA_UL_CH2 4
+#define I_DL1_CH1 5
+#define I_DL1_CH2 6
+#define I_DL2_CH1 7
+#define I_DL2_CH2 8
+#define I_PCM_1_CAP_CH1 9
+#define I_GAIN1_OUT_CH1 10
+#define I_GAIN1_OUT_CH2 11
+#define I_GAIN2_OUT_CH1 12
+#define I_GAIN2_OUT_CH2 13
+#define I_PCM_2_CAP_CH1 14
+#define I_PCM_2_CAP_CH2 21
+#define I_PCM_1_CAP_CH2 22
+#define I_DL3_CH1 23
+#define I_DL3_CH2 24
+#define I_I2S2_CH1 25
+#define I_I2S2_CH2 26
+
+#endif
diff --git a/sound/soc/mediatek/mt6797/mt6797-mt6351.c b/sound/soc/mediatek/mt6797/mt6797-mt6351.c
new file mode 100644
index 000000000000..daad9544a8d4
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-mt6351.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6797-mt6351.c -- MT6797 MT6351 ALSA SoC machine driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "mt6797-afe-common.h"
+
+SND_SOC_DAILINK_DEFS(playback_1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback_2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback_3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_mono_1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_MONO_1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hostless_lpbk,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless LPBK DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hostless_speech,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless Speech DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(primary_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "mt6351-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm2,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt6797_mt6351_dai_links[] = {
+ /* FE */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_1),
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_2),
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_3),
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_1),
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_2),
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_3),
+ },
+ {
+ .name = "Capture_Mono_1",
+ .stream_name = "Capture_Mono_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_mono_1),
+ },
+ {
+ .name = "Hostless_LPBK",
+ .stream_name = "Hostless_LPBK",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_lpbk),
+ },
+ {
+ .name = "Hostless_Speech",
+ .stream_name = "Hostless_Speech",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_speech),
+ },
+ /* BE */
+ {
+ .name = "Primary Codec",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(primary_codec),
+ },
+ {
+ .name = "PCM 1",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+ {
+ .name = "PCM 2",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm2),
+ },
+};
+
+static struct snd_soc_card mt6797_mt6351_card = {
+ .name = "mt6797-mt6351",
+ .owner = THIS_MODULE,
+ .dai_link = mt6797_mt6351_dai_links,
+ .num_links = ARRAY_SIZE(mt6797_mt6351_dai_links),
+};
+
+static int mt6797_mt6351_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt6797_mt6351_card;
+ struct device_node *platform_node, *codec_node;
+ struct snd_soc_dai_link *dai_link;
+ int ret, i;
+
+ card->dev = &pdev->dev;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ codec_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,audio-codec", 0);
+ if (!codec_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto put_platform_node;
+ }
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->codecs->name)
+ continue;
+ dai_link->codecs->of_node = codec_node;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+
+ of_node_put(codec_node);
+put_platform_node:
+ of_node_put(platform_node);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt6797_mt6351_dt_match[] = {
+ {.compatible = "mediatek,mt6797-mt6351-sound",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt6797_mt6351_dt_match);
+#endif
+
+static struct platform_driver mt6797_mt6351_driver = {
+ .driver = {
+ .name = "mt6797-mt6351",
+#ifdef CONFIG_OF
+ .of_match_table = mt6797_mt6351_dt_match,
+#endif
+ },
+ .probe = mt6797_mt6351_dev_probe,
+};
+
+module_platform_driver(mt6797_mt6351_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT6797 MT6351 ALSA SoC machine driver");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt6797 mt6351 soc card");
+
diff --git a/sound/soc/mediatek/mt6797/mt6797-reg.h b/sound/soc/mediatek/mt6797/mt6797-reg.h
new file mode 100644
index 000000000000..978f146c143c
--- /dev/null
+++ b/sound/soc/mediatek/mt6797/mt6797-reg.h
@@ -0,0 +1,1015 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6797-reg.h -- Mediatek 6797 audio driver reg definition
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT6797_REG_H_
+#define _MT6797_REG_H_
+
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON1 0x0004
+#define AUDIO_TOP_CON3 0x000c
+#define AFE_DAC_CON0 0x0010
+#define AFE_DAC_CON1 0x0014
+#define AFE_I2S_CON 0x0018
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_CONN0 0x0020
+#define AFE_CONN1 0x0024
+#define AFE_CONN2 0x0028
+#define AFE_CONN3 0x002c
+#define AFE_CONN4 0x0030
+#define AFE_I2S_CON1 0x0034
+#define AFE_I2S_CON2 0x0038
+#define AFE_MRGIF_CON 0x003c
+#define AFE_DL1_BASE 0x0040
+#define AFE_DL1_CUR 0x0044
+#define AFE_DL1_END 0x0048
+#define AFE_I2S_CON3 0x004c
+#define AFE_DL2_BASE 0x0050
+#define AFE_DL2_CUR 0x0054
+#define AFE_DL2_END 0x0058
+#define AFE_CONN5 0x005c
+#define AFE_CONN_24BIT 0x006c
+#define AFE_AWB_BASE 0x0070
+#define AFE_AWB_END 0x0078
+#define AFE_AWB_CUR 0x007c
+#define AFE_VUL_BASE 0x0080
+#define AFE_VUL_END 0x0088
+#define AFE_VUL_CUR 0x008c
+#define AFE_DAI_BASE 0x0090
+#define AFE_DAI_END 0x0098
+#define AFE_DAI_CUR 0x009c
+#define AFE_CONN6 0x00bc
+#define AFE_MEMIF_MSB 0x00cc
+#define AFE_MEMIF_MON0 0x00d0
+#define AFE_MEMIF_MON1 0x00d4
+#define AFE_MEMIF_MON2 0x00d8
+#define AFE_MEMIF_MON4 0x00e0
+#define AFE_ADDA_DL_SRC2_CON0 0x0108
+#define AFE_ADDA_DL_SRC2_CON1 0x010c
+#define AFE_ADDA_UL_SRC_CON0 0x0114
+#define AFE_ADDA_UL_SRC_CON1 0x0118
+#define AFE_ADDA_TOP_CON0 0x0120
+#define AFE_ADDA_UL_DL_CON0 0x0124
+#define AFE_ADDA_SRC_DEBUG 0x012c
+#define AFE_ADDA_SRC_DEBUG_MON0 0x0130
+#define AFE_ADDA_SRC_DEBUG_MON1 0x0134
+#define AFE_ADDA_NEWIF_CFG0 0x0138
+#define AFE_ADDA_NEWIF_CFG1 0x013c
+#define AFE_ADDA_NEWIF_CFG2 0x0140
+#define AFE_DMA_CTL 0x0150
+#define AFE_DMA_MON0 0x0154
+#define AFE_DMA_MON1 0x0158
+#define AFE_SIDETONE_DEBUG 0x01d0
+#define AFE_SIDETONE_MON 0x01d4
+#define AFE_SIDETONE_CON0 0x01e0
+#define AFE_SIDETONE_COEFF 0x01e4
+#define AFE_SIDETONE_CON1 0x01e8
+#define AFE_SIDETONE_GAIN 0x01ec
+#define AFE_SGEN_CON0 0x01f0
+#define AFE_SINEGEN_CON_TDM 0x01fc
+#define AFE_TOP_CON0 0x0200
+#define AFE_ADDA_PREDIS_CON0 0x0260
+#define AFE_ADDA_PREDIS_CON1 0x0264
+#define AFE_MRGIF_MON0 0x0270
+#define AFE_MRGIF_MON1 0x0274
+#define AFE_MRGIF_MON2 0x0278
+#define AFE_I2S_MON 0x027c
+#define AFE_MOD_DAI_BASE 0x0330
+#define AFE_MOD_DAI_END 0x0338
+#define AFE_MOD_DAI_CUR 0x033c
+#define AFE_VUL_D2_BASE 0x0350
+#define AFE_VUL_D2_END 0x0358
+#define AFE_VUL_D2_CUR 0x035c
+#define AFE_DL3_BASE 0x0360
+#define AFE_DL3_CUR 0x0364
+#define AFE_DL3_END 0x0368
+#define AFE_HDMI_OUT_CON0 0x0370
+#define AFE_HDMI_BASE 0x0374
+#define AFE_HDMI_CUR 0x0378
+#define AFE_HDMI_END 0x037c
+#define AFE_HDMI_CONN0 0x0390
+#define AFE_IRQ3_MCU_CNT_MON 0x0398
+#define AFE_IRQ4_MCU_CNT_MON 0x039c
+#define AFE_IRQ_MCU_CON 0x03a0
+#define AFE_IRQ_MCU_STATUS 0x03a4
+#define AFE_IRQ_MCU_CLR 0x03a8
+#define AFE_IRQ_MCU_CNT1 0x03ac
+#define AFE_IRQ_MCU_CNT2 0x03b0
+#define AFE_IRQ_MCU_EN 0x03b4
+#define AFE_IRQ_MCU_MON2 0x03b8
+#define AFE_IRQ_MCU_CNT5 0x03bc
+#define AFE_IRQ1_MCU_CNT_MON 0x03c0
+#define AFE_IRQ2_MCU_CNT_MON 0x03c4
+#define AFE_IRQ1_MCU_EN_CNT_MON 0x03c8
+#define AFE_IRQ5_MCU_CNT_MON 0x03cc
+#define AFE_MEMIF_MINLEN 0x03d0
+#define AFE_MEMIF_MAXLEN 0x03d4
+#define AFE_MEMIF_PBUF_SIZE 0x03d8
+#define AFE_IRQ_MCU_CNT7 0x03dc
+#define AFE_IRQ7_MCU_CNT_MON 0x03e0
+#define AFE_IRQ_MCU_CNT3 0x03e4
+#define AFE_IRQ_MCU_CNT4 0x03e8
+#define AFE_APLL1_TUNER_CFG 0x03f0
+#define AFE_APLL2_TUNER_CFG 0x03f4
+#define AFE_MEMIF_HD_MODE 0x03f8
+#define AFE_MEMIF_HDALIGN 0x03fc
+#define AFE_GAIN1_CON0 0x0410
+#define AFE_GAIN1_CON1 0x0414
+#define AFE_GAIN1_CON2 0x0418
+#define AFE_GAIN1_CON3 0x041c
+#define AFE_CONN7 0x0420
+#define AFE_GAIN1_CUR 0x0424
+#define AFE_GAIN2_CON0 0x0428
+#define AFE_GAIN2_CON1 0x042c
+#define AFE_GAIN2_CON2 0x0430
+#define AFE_GAIN2_CON3 0x0434
+#define AFE_CONN8 0x0438
+#define AFE_GAIN2_CUR 0x043c
+#define AFE_CONN9 0x0440
+#define AFE_CONN10 0x0444
+#define AFE_CONN11 0x0448
+#define AFE_CONN12 0x044c
+#define AFE_CONN13 0x0450
+#define AFE_CONN14 0x0454
+#define AFE_CONN15 0x0458
+#define AFE_CONN16 0x045c
+#define AFE_CONN17 0x0460
+#define AFE_CONN18 0x0464
+#define AFE_CONN19 0x0468
+#define AFE_CONN20 0x046c
+#define AFE_CONN21 0x0470
+#define AFE_CONN22 0x0474
+#define AFE_CONN23 0x0478
+#define AFE_CONN24 0x047c
+#define AFE_CONN_RS 0x0494
+#define AFE_CONN_DI 0x0498
+#define AFE_CONN25 0x04b0
+#define AFE_CONN26 0x04b4
+#define AFE_CONN27 0x04b8
+#define AFE_CONN28 0x04bc
+#define AFE_CONN29 0x04c0
+#define AFE_SRAM_DELSEL_CON0 0x04f0
+#define AFE_SRAM_DELSEL_CON1 0x04f4
+#define AFE_ASRC_CON0 0x0500
+#define AFE_ASRC_CON1 0x0504
+#define AFE_ASRC_CON2 0x0508
+#define AFE_ASRC_CON3 0x050c
+#define AFE_ASRC_CON4 0x0510
+#define AFE_ASRC_CON5 0x0514
+#define AFE_ASRC_CON6 0x0518
+#define AFE_ASRC_CON7 0x051c
+#define AFE_ASRC_CON8 0x0520
+#define AFE_ASRC_CON9 0x0524
+#define AFE_ASRC_CON10 0x0528
+#define AFE_ASRC_CON11 0x052c
+#define PCM_INTF_CON1 0x0530
+#define PCM_INTF_CON2 0x0538
+#define PCM2_INTF_CON 0x053c
+#define AFE_TDM_CON1 0x0548
+#define AFE_TDM_CON2 0x054c
+#define AFE_ASRC_CON13 0x0550
+#define AFE_ASRC_CON14 0x0554
+#define AFE_ASRC_CON15 0x0558
+#define AFE_ASRC_CON16 0x055c
+#define AFE_ASRC_CON17 0x0560
+#define AFE_ASRC_CON18 0x0564
+#define AFE_ASRC_CON19 0x0568
+#define AFE_ASRC_CON20 0x056c
+#define AFE_ASRC_CON21 0x0570
+#define CLK_AUDDIV_0 0x05a0
+#define CLK_AUDDIV_1 0x05a4
+#define CLK_AUDDIV_2 0x05a8
+#define CLK_AUDDIV_3 0x05ac
+#define AUDIO_TOP_DBG_CON 0x05c8
+#define AUDIO_TOP_DBG_MON0 0x05cc
+#define AUDIO_TOP_DBG_MON1 0x05d0
+#define AUDIO_TOP_DBG_MON2 0x05d4
+#define AFE_ADDA2_TOP_CON0 0x0600
+#define AFE_ASRC4_CON0 0x06c0
+#define AFE_ASRC4_CON1 0x06c4
+#define AFE_ASRC4_CON2 0x06c8
+#define AFE_ASRC4_CON3 0x06cc
+#define AFE_ASRC4_CON4 0x06d0
+#define AFE_ASRC4_CON5 0x06d4
+#define AFE_ASRC4_CON6 0x06d8
+#define AFE_ASRC4_CON7 0x06dc
+#define AFE_ASRC4_CON8 0x06e0
+#define AFE_ASRC4_CON9 0x06e4
+#define AFE_ASRC4_CON10 0x06e8
+#define AFE_ASRC4_CON11 0x06ec
+#define AFE_ASRC4_CON12 0x06f0
+#define AFE_ASRC4_CON13 0x06f4
+#define AFE_ASRC4_CON14 0x06f8
+#define AFE_ASRC2_CON0 0x0700
+#define AFE_ASRC2_CON1 0x0704
+#define AFE_ASRC2_CON2 0x0708
+#define AFE_ASRC2_CON3 0x070c
+#define AFE_ASRC2_CON4 0x0710
+#define AFE_ASRC2_CON5 0x0714
+#define AFE_ASRC2_CON6 0x0718
+#define AFE_ASRC2_CON7 0x071c
+#define AFE_ASRC2_CON8 0x0720
+#define AFE_ASRC2_CON9 0x0724
+#define AFE_ASRC2_CON10 0x0728
+#define AFE_ASRC2_CON11 0x072c
+#define AFE_ASRC2_CON12 0x0730
+#define AFE_ASRC2_CON13 0x0734
+#define AFE_ASRC2_CON14 0x0738
+#define AFE_ASRC3_CON0 0x0740
+#define AFE_ASRC3_CON1 0x0744
+#define AFE_ASRC3_CON2 0x0748
+#define AFE_ASRC3_CON3 0x074c
+#define AFE_ASRC3_CON4 0x0750
+#define AFE_ASRC3_CON5 0x0754
+#define AFE_ASRC3_CON6 0x0758
+#define AFE_ASRC3_CON7 0x075c
+#define AFE_ASRC3_CON8 0x0760
+#define AFE_ASRC3_CON9 0x0764
+#define AFE_ASRC3_CON10 0x0768
+#define AFE_ASRC3_CON11 0x076c
+#define AFE_ASRC3_CON12 0x0770
+#define AFE_ASRC3_CON13 0x0774
+#define AFE_ASRC3_CON14 0x0778
+#define AFE_GENERAL_REG0 0x0800
+#define AFE_GENERAL_REG1 0x0804
+#define AFE_GENERAL_REG2 0x0808
+#define AFE_GENERAL_REG3 0x080c
+#define AFE_GENERAL_REG4 0x0810
+#define AFE_GENERAL_REG5 0x0814
+#define AFE_GENERAL_REG6 0x0818
+#define AFE_GENERAL_REG7 0x081c
+#define AFE_GENERAL_REG8 0x0820
+#define AFE_GENERAL_REG9 0x0824
+#define AFE_GENERAL_REG10 0x0828
+#define AFE_GENERAL_REG11 0x082c
+#define AFE_GENERAL_REG12 0x0830
+#define AFE_GENERAL_REG13 0x0834
+#define AFE_GENERAL_REG14 0x0838
+#define AFE_GENERAL_REG15 0x083c
+#define AFE_CBIP_CFG0 0x0840
+#define AFE_CBIP_MON0 0x0844
+#define AFE_CBIP_SLV_MUX_MON0 0x0848
+#define AFE_CBIP_SLV_DECODER_MON0 0x084c
+
+#define AFE_MAX_REGISTER AFE_CBIP_SLV_DECODER_MON0
+#define AFE_IRQ_STATUS_BITS 0x5f
+
+/* AUDIO_TOP_CON0 */
+#define AHB_IDLE_EN_INT_SFT 30
+#define AHB_IDLE_EN_INT_MASK 0x1
+#define AHB_IDLE_EN_INT_MASK_SFT (0x1 << 30)
+#define AHB_IDLE_EN_EXT_SFT 29
+#define AHB_IDLE_EN_EXT_MASK 0x1
+#define AHB_IDLE_EN_EXT_MASK_SFT (0x1 << 29)
+#define PDN_TML_SFT 27
+#define PDN_TML_MASK 0x1
+#define PDN_TML_MASK_SFT (0x1 << 27)
+#define PDN_DAC_PREDIS_SFT 26
+#define PDN_DAC_PREDIS_MASK 0x1
+#define PDN_DAC_PREDIS_MASK_SFT (0x1 << 26)
+#define PDN_DAC_SFT 25
+#define PDN_DAC_MASK 0x1
+#define PDN_DAC_MASK_SFT (0x1 << 25)
+#define PDN_ADC_SFT 24
+#define PDN_ADC_MASK 0x1
+#define PDN_ADC_MASK_SFT (0x1 << 24)
+#define PDN_TDM_CK_SFT 20
+#define PDN_TDM_CK_MASK 0x1
+#define PDN_TDM_CK_MASK_SFT (0x1 << 20)
+#define PDN_APLL_TUNER_SFT 19
+#define PDN_APLL_TUNER_MASK 0x1
+#define PDN_APLL_TUNER_MASK_SFT (0x1 << 19)
+#define PDN_APLL2_TUNER_SFT 18
+#define PDN_APLL2_TUNER_MASK 0x1
+#define PDN_APLL2_TUNER_MASK_SFT (0x1 << 18)
+#define APB3_SEL_SFT 14
+#define APB3_SEL_MASK 0x1
+#define APB3_SEL_MASK_SFT (0x1 << 14)
+#define APB_R2T_SFT 13
+#define APB_R2T_MASK 0x1
+#define APB_R2T_MASK_SFT (0x1 << 13)
+#define APB_W2T_SFT 12
+#define APB_W2T_MASK 0x1
+#define APB_W2T_MASK_SFT (0x1 << 12)
+#define PDN_24M_SFT 9
+#define PDN_24M_MASK 0x1
+#define PDN_24M_MASK_SFT (0x1 << 9)
+#define PDN_22M_SFT 8
+#define PDN_22M_MASK 0x1
+#define PDN_22M_MASK_SFT (0x1 << 8)
+#define PDN_ADDA4_ADC_SFT 7
+#define PDN_ADDA4_ADC_MASK 0x1
+#define PDN_ADDA4_ADC_MASK_SFT (0x1 << 7)
+#define PDN_I2S_SFT 6
+#define PDN_I2S_MASK 0x1
+#define PDN_I2S_MASK_SFT (0x1 << 6)
+#define PDN_AFE_SFT 2
+#define PDN_AFE_MASK 0x1
+#define PDN_AFE_MASK_SFT (0x1 << 2)
+
+/* AUDIO_TOP_CON1 */
+#define PDN_ADC_HIRES_TML_SFT 17
+#define PDN_ADC_HIRES_TML_MASK 0x1
+#define PDN_ADC_HIRES_TML_MASK_SFT (0x1 << 17)
+#define PDN_ADC_HIRES_SFT 16
+#define PDN_ADC_HIRES_MASK 0x1
+#define PDN_ADC_HIRES_MASK_SFT (0x1 << 16)
+#define I2S4_BCLK_SW_CG_SFT 7
+#define I2S4_BCLK_SW_CG_MASK 0x1
+#define I2S4_BCLK_SW_CG_MASK_SFT (0x1 << 7)
+#define I2S3_BCLK_SW_CG_SFT 6
+#define I2S3_BCLK_SW_CG_MASK 0x1
+#define I2S3_BCLK_SW_CG_MASK_SFT (0x1 << 6)
+#define I2S2_BCLK_SW_CG_SFT 5
+#define I2S2_BCLK_SW_CG_MASK 0x1
+#define I2S2_BCLK_SW_CG_MASK_SFT (0x1 << 5)
+#define I2S1_BCLK_SW_CG_SFT 4
+#define I2S1_BCLK_SW_CG_MASK 0x1
+#define I2S1_BCLK_SW_CG_MASK_SFT (0x1 << 4)
+#define I2S_SOFT_RST2_SFT 2
+#define I2S_SOFT_RST2_MASK 0x1
+#define I2S_SOFT_RST2_MASK_SFT (0x1 << 2)
+#define I2S_SOFT_RST_SFT 1
+#define I2S_SOFT_RST_MASK 0x1
+#define I2S_SOFT_RST_MASK_SFT (0x1 << 1)
+
+/* AFE_DAC_CON0 */
+#define AFE_AWB_RETM_SFT 31
+#define AFE_AWB_RETM_MASK 0x1
+#define AFE_AWB_RETM_MASK_SFT (0x1 << 31)
+#define AFE_DL1_DATA2_RETM_SFT 30
+#define AFE_DL1_DATA2_RETM_MASK 0x1
+#define AFE_DL1_DATA2_RETM_MASK_SFT (0x1 << 30)
+#define AFE_DL2_RETM_SFT 29
+#define AFE_DL2_RETM_MASK 0x1
+#define AFE_DL2_RETM_MASK_SFT (0x1 << 29)
+#define AFE_DL1_RETM_SFT 28
+#define AFE_DL1_RETM_MASK 0x1
+#define AFE_DL1_RETM_MASK_SFT (0x1 << 28)
+#define AFE_ON_RETM_SFT 27
+#define AFE_ON_RETM_MASK 0x1
+#define AFE_ON_RETM_MASK_SFT (0x1 << 27)
+#define MOD_DAI_DUP_WR_SFT 26
+#define MOD_DAI_DUP_WR_MASK 0x1
+#define MOD_DAI_DUP_WR_MASK_SFT (0x1 << 26)
+#define DAI_MODE_SFT 24
+#define DAI_MODE_MASK 0x3
+#define DAI_MODE_MASK_SFT (0x3 << 24)
+#define VUL_DATA2_MODE_SFT 20
+#define VUL_DATA2_MODE_MASK 0xf
+#define VUL_DATA2_MODE_MASK_SFT (0xf << 20)
+#define DL1_DATA2_MODE_SFT 16
+#define DL1_DATA2_MODE_MASK 0xf
+#define DL1_DATA2_MODE_MASK_SFT (0xf << 16)
+#define DL3_MODE_SFT 12
+#define DL3_MODE_MASK 0xf
+#define DL3_MODE_MASK_SFT (0xf << 12)
+#define VUL_DATA2_R_MONO_SFT 11
+#define VUL_DATA2_R_MONO_MASK 0x1
+#define VUL_DATA2_R_MONO_MASK_SFT (0x1 << 11)
+#define VUL_DATA2_DATA_SFT 10
+#define VUL_DATA2_DATA_MASK 0x1
+#define VUL_DATA2_DATA_MASK_SFT (0x1 << 10)
+#define VUL_DATA2_ON_SFT 9
+#define VUL_DATA2_ON_MASK 0x1
+#define VUL_DATA2_ON_MASK_SFT (0x1 << 9)
+#define DL1_DATA2_ON_SFT 8
+#define DL1_DATA2_ON_MASK 0x1
+#define DL1_DATA2_ON_MASK_SFT (0x1 << 8)
+#define MOD_DAI_ON_SFT 7
+#define MOD_DAI_ON_MASK 0x1
+#define MOD_DAI_ON_MASK_SFT (0x1 << 7)
+#define AWB_ON_SFT 6
+#define AWB_ON_MASK 0x1
+#define AWB_ON_MASK_SFT (0x1 << 6)
+#define DL3_ON_SFT 5
+#define DL3_ON_MASK 0x1
+#define DL3_ON_MASK_SFT (0x1 << 5)
+#define DAI_ON_SFT 4
+#define DAI_ON_MASK 0x1
+#define DAI_ON_MASK_SFT (0x1 << 4)
+#define VUL_ON_SFT 3
+#define VUL_ON_MASK 0x1
+#define VUL_ON_MASK_SFT (0x1 << 3)
+#define DL2_ON_SFT 2
+#define DL2_ON_MASK 0x1
+#define DL2_ON_MASK_SFT (0x1 << 2)
+#define DL1_ON_SFT 1
+#define DL1_ON_MASK 0x1
+#define DL1_ON_MASK_SFT (0x1 << 1)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DAC_CON1 */
+#define MOD_DAI_MODE_SFT 30
+#define MOD_DAI_MODE_MASK 0x3
+#define MOD_DAI_MODE_MASK_SFT (0x3 << 30)
+#define DAI_DUP_WR_SFT 29
+#define DAI_DUP_WR_MASK 0x1
+#define DAI_DUP_WR_MASK_SFT (0x1 << 29)
+#define VUL_R_MONO_SFT 28
+#define VUL_R_MONO_MASK 0x1
+#define VUL_R_MONO_MASK_SFT (0x1 << 28)
+#define VUL_DATA_SFT 27
+#define VUL_DATA_MASK 0x1
+#define VUL_DATA_MASK_SFT (0x1 << 27)
+#define AXI_2X1_CG_DISABLE_SFT 26
+#define AXI_2X1_CG_DISABLE_MASK 0x1
+#define AXI_2X1_CG_DISABLE_MASK_SFT (0x1 << 26)
+#define AWB_R_MONO_SFT 25
+#define AWB_R_MONO_MASK 0x1
+#define AWB_R_MONO_MASK_SFT (0x1 << 25)
+#define AWB_DATA_SFT 24
+#define AWB_DATA_MASK 0x1
+#define AWB_DATA_MASK_SFT (0x1 << 24)
+#define DL3_DATA_SFT 23
+#define DL3_DATA_MASK 0x1
+#define DL3_DATA_MASK_SFT (0x1 << 23)
+#define DL2_DATA_SFT 22
+#define DL2_DATA_MASK 0x1
+#define DL2_DATA_MASK_SFT (0x1 << 22)
+#define DL1_DATA_SFT 21
+#define DL1_DATA_MASK 0x1
+#define DL1_DATA_MASK_SFT (0x1 << 21)
+#define DL1_DATA2_DATA_SFT 20
+#define DL1_DATA2_DATA_MASK 0x1
+#define DL1_DATA2_DATA_MASK_SFT (0x1 << 20)
+#define VUL_MODE_SFT 16
+#define VUL_MODE_MASK 0xf
+#define VUL_MODE_MASK_SFT (0xf << 16)
+#define AWB_MODE_SFT 12
+#define AWB_MODE_MASK 0xf
+#define AWB_MODE_MASK_SFT (0xf << 12)
+#define I2S_MODE_SFT 8
+#define I2S_MODE_MASK 0xf
+#define I2S_MODE_MASK_SFT (0xf << 8)
+#define DL2_MODE_SFT 4
+#define DL2_MODE_MASK 0xf
+#define DL2_MODE_MASK_SFT (0xf << 4)
+#define DL1_MODE_SFT 0
+#define DL1_MODE_MASK 0xf
+#define DL1_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL_SFT 28
+#define DL_2_INPUT_MODE_CTL_MASK 0xf
+#define DL_2_INPUT_MODE_CTL_MASK_SFT (0xf << 28)
+#define DL_2_CH1_SATURATION_EN_CTL_SFT 27
+#define DL_2_CH1_SATURATION_EN_CTL_MASK 0x1
+#define DL_2_CH1_SATURATION_EN_CTL_MASK_SFT (0x1 << 27)
+#define DL_2_CH2_SATURATION_EN_CTL_SFT 26
+#define DL_2_CH2_SATURATION_EN_CTL_MASK 0x1
+#define DL_2_CH2_SATURATION_EN_CTL_MASK_SFT (0x1 << 26)
+#define DL_2_OUTPUT_SEL_CTL_SFT 24
+#define DL_2_OUTPUT_SEL_CTL_MASK 0x3
+#define DL_2_OUTPUT_SEL_CTL_MASK_SFT (0x3 << 24)
+#define DL_2_FADEIN_0START_EN_SFT 16
+#define DL_2_FADEIN_0START_EN_MASK 0x3
+#define DL_2_FADEIN_0START_EN_MASK_SFT (0x3 << 16)
+#define DL_DISABLE_HW_CG_CTL_SFT 15
+#define DL_DISABLE_HW_CG_CTL_MASK 0x1
+#define DL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 15)
+#define C_DATA_EN_SEL_CTL_PRE_SFT 14
+#define C_DATA_EN_SEL_CTL_PRE_MASK 0x1
+#define C_DATA_EN_SEL_CTL_PRE_MASK_SFT (0x1 << 14)
+#define DL_2_SIDE_TONE_ON_CTL_PRE_SFT 13
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK 0x1
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK_SFT (0x1 << 13)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_SFT 12
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK 0x1
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK_SFT (0x1 << 12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_SFT 11
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK 0x1
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK_SFT (0x1 << 11)
+#define DL2_ARAMPSP_CTL_PRE_SFT 9
+#define DL2_ARAMPSP_CTL_PRE_MASK 0x3
+#define DL2_ARAMPSP_CTL_PRE_MASK_SFT (0x3 << 9)
+#define DL_2_IIRMODE_CTL_PRE_SFT 6
+#define DL_2_IIRMODE_CTL_PRE_MASK 0x7
+#define DL_2_IIRMODE_CTL_PRE_MASK_SFT (0x7 << 6)
+#define DL_2_VOICE_MODE_CTL_PRE_SFT 5
+#define DL_2_VOICE_MODE_CTL_PRE_MASK 0x1
+#define DL_2_VOICE_MODE_CTL_PRE_MASK_SFT (0x1 << 5)
+#define D2_2_MUTE_CH1_ON_CTL_PRE_SFT 4
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK 0x1
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK_SFT (0x1 << 4)
+#define D2_2_MUTE_CH2_ON_CTL_PRE_SFT 3
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK 0x1
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK_SFT (0x1 << 3)
+#define DL_2_IIR_ON_CTL_PRE_SFT 2
+#define DL_2_IIR_ON_CTL_PRE_MASK 0x1
+#define DL_2_IIR_ON_CTL_PRE_MASK_SFT (0x1 << 2)
+#define DL_2_GAIN_ON_CTL_PRE_SFT 1
+#define DL_2_GAIN_ON_CTL_PRE_MASK 0x1
+#define DL_2_GAIN_ON_CTL_PRE_MASK_SFT (0x1 << 1)
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE_SFT 16
+#define DL_2_GAIN_CTL_PRE_MASK 0xffff
+#define DL_2_GAIN_CTL_PRE_MASK_SFT (0xffff << 16)
+#define DL_2_GAIN_MODE_CTL_SFT 0
+#define DL_2_GAIN_MODE_CTL_MASK 0x1
+#define DL_2_GAIN_MODE_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define C_COMB_OUT_SIN_GEN_CTL_SFT 31
+#define C_COMB_OUT_SIN_GEN_CTL_MASK 0x1
+#define C_COMB_OUT_SIN_GEN_CTL_MASK_SFT (0x1 << 31)
+#define C_BASEBAND_SIN_GEN_CTL_SFT 30
+#define C_BASEBAND_SIN_GEN_CTL_MASK 0x1
+#define C_BASEBAND_SIN_GEN_CTL_MASK_SFT (0x1 << 30)
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 27
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 27)
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 24
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7
+#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 24)
+#define C_TWO_DIGITAL_MIC_CTL_SFT 23
+#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1
+#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 23)
+#define UL_MODE_3P25M_CH2_CTL_SFT 22
+#define UL_MODE_3P25M_CH2_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22)
+#define UL_MODE_3P25M_CH1_CTL_SFT 21
+#define UL_MODE_3P25M_CH1_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21)
+#define UL_SRC_USE_CIC_OUT_CTL_SFT 20
+#define UL_SRC_USE_CIC_OUT_CTL_MASK 0x1
+#define UL_SRC_USE_CIC_OUT_CTL_MASK_SFT (0x1 << 20)
+#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17)
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define DMIC_48K_SEL_CTL_SFT 13
+#define DMIC_48K_SEL_CTL_MASK 0x1
+#define DMIC_48K_SEL_CTL_MASK_SFT (0x1 << 13)
+#define UL_DISABLE_HW_CG_CTL_SFT 12
+#define UL_DISABLE_HW_CG_CTL_MASK 0x1
+#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12)
+#define UL_IIR_ON_TMP_CTL_SFT 10
+#define UL_IIR_ON_TMP_CTL_MASK 0x1
+#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10)
+#define UL_IIRMODE_CTL_SFT 7
+#define UL_IIRMODE_CTL_MASK 0x7
+#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define AGC_260K_SEL_CH2_CTL_SFT 4
+#define AGC_260K_SEL_CH2_CTL_MASK 0x1
+#define AGC_260K_SEL_CH2_CTL_MASK_SFT (0x1 << 4)
+#define AGC_260K_SEL_CH1_CTL_SFT 3
+#define AGC_260K_SEL_CH1_CTL_MASK 0x1
+#define AGC_260K_SEL_CH1_CTL_MASK_SFT (0x1 << 3)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_SRC_CON1 */
+#define C_SDM_RESET_CTL_SFT 31
+#define C_SDM_RESET_CTL_MASK 0x1
+#define C_SDM_RESET_CTL_MASK_SFT (0x1 << 31)
+#define ADITHON_CTL_SFT 30
+#define ADITHON_CTL_MASK 0x1
+#define ADITHON_CTL_MASK_SFT (0x1 << 30)
+#define ADITHVAL_CTL_SFT 28
+#define ADITHVAL_CTL_MASK 0x3
+#define ADITHVAL_CTL_MASK_SFT (0x3 << 28)
+#define C_DAC_EN_CTL_SFT 27
+#define C_DAC_EN_CTL_MASK 0x1
+#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27)
+#define C_MUTE_SW_CTL_SFT 26
+#define C_MUTE_SW_CTL_MASK 0x1
+#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26)
+#define ASDM_SRC_SEL_CTL_SFT 25
+#define ASDM_SRC_SEL_CTL_MASK 0x1
+#define ASDM_SRC_SEL_CTL_MASK_SFT (0x1 << 25)
+#define C_AMP_DIV_CH2_CTL_SFT 21
+#define C_AMP_DIV_CH2_CTL_MASK 0x7
+#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21)
+#define C_FREQ_DIV_CH2_CTL_SFT 16
+#define C_FREQ_DIV_CH2_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16)
+#define C_SINE_MODE_CH2_CTL_SFT 12
+#define C_SINE_MODE_CH2_CTL_MASK 0xf
+#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12)
+#define C_AMP_DIV_CH1_CTL_SFT 9
+#define C_AMP_DIV_CH1_CTL_MASK 0x7
+#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9)
+#define C_FREQ_DIV_CH1_CTL_SFT 4
+#define C_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4)
+#define C_SINE_MODE_CH1_CTL_SFT 0
+#define C_SINE_MODE_CH1_CTL_MASK 0xf
+#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOP_BACK_MODE_CTL_SFT 12
+#define C_LOOP_BACK_MODE_CTL_MASK 0xf
+#define C_LOOP_BACK_MODE_CTL_MASK_SFT (0xf << 12)
+#define C_EXT_ADC_CTL_SFT 0
+#define C_EXT_ADC_CTL_MASK 0x1
+#define C_EXT_ADC_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define AFE_UL_DL_CON0_RESERVED_SFT 1
+#define AFE_UL_DL_CON0_RESERVED_MASK 0x3fff
+#define AFE_UL_DL_CON0_RESERVED_MASK_SFT (0x3fff << 1)
+#define ADDA_AFE_ON_SFT 0
+#define ADDA_AFE_ON_MASK 0x1
+#define ADDA_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_CON */
+#define IRQ7_MCU_MODE_SFT 24
+#define IRQ7_MCU_MODE_MASK 0xf
+#define IRQ7_MCU_MODE_MASK_SFT (0xf << 24)
+#define IRQ4_MCU_MODE_SFT 20
+#define IRQ4_MCU_MODE_MASK 0xf
+#define IRQ4_MCU_MODE_MASK_SFT (0xf << 20)
+#define IRQ3_MCU_MODE_SFT 16
+#define IRQ3_MCU_MODE_MASK 0xf
+#define IRQ3_MCU_MODE_MASK_SFT (0xf << 16)
+#define IRQ7_MCU_ON_SFT 14
+#define IRQ7_MCU_ON_MASK 0x1
+#define IRQ7_MCU_ON_MASK_SFT (0x1 << 14)
+#define IRQ5_MCU_ON_SFT 12
+#define IRQ5_MCU_ON_MASK 0x1
+#define IRQ5_MCU_ON_MASK_SFT (0x1 << 12)
+#define IRQ2_MCU_MODE_SFT 8
+#define IRQ2_MCU_MODE_MASK 0xf
+#define IRQ2_MCU_MODE_MASK_SFT (0xf << 8)
+#define IRQ1_MCU_MODE_SFT 4
+#define IRQ1_MCU_MODE_MASK 0xf
+#define IRQ1_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ4_MCU_ON_SFT 3
+#define IRQ4_MCU_ON_MASK 0x1
+#define IRQ4_MCU_ON_MASK_SFT (0x1 << 3)
+#define IRQ3_MCU_ON_SFT 2
+#define IRQ3_MCU_ON_MASK 0x1
+#define IRQ3_MCU_ON_MASK_SFT (0x1 << 2)
+#define IRQ2_MCU_ON_SFT 1
+#define IRQ2_MCU_ON_MASK 0x1
+#define IRQ2_MCU_ON_MASK_SFT (0x1 << 1)
+#define IRQ1_MCU_ON_SFT 0
+#define IRQ1_MCU_ON_MASK 0x1
+#define IRQ1_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_EN */
+#define AFE_IRQ_CM4_EN_SFT 16
+#define AFE_IRQ_CM4_EN_MASK 0x7f
+#define AFE_IRQ_CM4_EN_MASK_SFT (0x7f << 16)
+#define AFE_IRQ_MD32_EN_SFT 8
+#define AFE_IRQ_MD32_EN_MASK 0x7f
+#define AFE_IRQ_MD32_EN_MASK_SFT (0x7f << 8)
+#define AFE_IRQ_MCU_EN_SFT 0
+#define AFE_IRQ_MCU_EN_MASK 0x7f
+#define AFE_IRQ_MCU_EN_MASK_SFT (0x7f << 0)
+
+/* AFE_IRQ_MCU_CLR */
+#define IRQ7_MCU_CLR_SFT 6
+#define IRQ7_MCU_CLR_MASK 0x1
+#define IRQ7_MCU_CLR_MASK_SFT (0x1 << 6)
+#define IRQ5_MCU_CLR_SFT 4
+#define IRQ5_MCU_CLR_MASK 0x1
+#define IRQ5_MCU_CLR_MASK_SFT (0x1 << 4)
+#define IRQ4_MCU_CLR_SFT 3
+#define IRQ4_MCU_CLR_MASK 0x1
+#define IRQ4_MCU_CLR_MASK_SFT (0x1 << 3)
+#define IRQ3_MCU_CLR_SFT 2
+#define IRQ3_MCU_CLR_MASK 0x1
+#define IRQ3_MCU_CLR_MASK_SFT (0x1 << 2)
+#define IRQ2_MCU_CLR_SFT 1
+#define IRQ2_MCU_CLR_MASK 0x1
+#define IRQ2_MCU_CLR_MASK_SFT (0x1 << 1)
+#define IRQ1_MCU_CLR_SFT 0
+#define IRQ1_MCU_CLR_MASK 0x1
+#define IRQ1_MCU_CLR_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_CNT1 */
+#define AFE_IRQ_MCU_CNT1_SFT 0
+#define AFE_IRQ_MCU_CNT1_MASK 0x3ffff
+#define AFE_IRQ_MCU_CNT1_MASK_SFT (0x3ffff << 0)
+
+/* AFE_IRQ_MCU_CNT2 */
+#define AFE_IRQ_MCU_CNT2_SFT 0
+#define AFE_IRQ_MCU_CNT2_MASK 0x3ffff
+#define AFE_IRQ_MCU_CNT2_MASK_SFT (0x3ffff << 0)
+
+/* AFE_IRQ_MCU_CNT3 */
+#define AFE_IRQ_MCU_CNT3_SFT 0
+#define AFE_IRQ_MCU_CNT3_MASK 0x3ffff
+#define AFE_IRQ_MCU_CNT3_MASK_SFT (0x3ffff << 0)
+
+/* AFE_IRQ_MCU_CNT4 */
+#define AFE_IRQ_MCU_CNT4_SFT 0
+#define AFE_IRQ_MCU_CNT4_MASK 0x3ffff
+#define AFE_IRQ_MCU_CNT4_MASK_SFT (0x3ffff << 0)
+
+/* AFE_IRQ_MCU_CNT5 */
+#define AFE_IRQ_MCU_CNT5_SFT 0
+#define AFE_IRQ_MCU_CNT5_MASK 0x3ffff
+#define AFE_IRQ_MCU_CNT5_MASK_SFT (0x3ffff << 0)
+
+/* AFE_IRQ_MCU_CNT7 */
+#define AFE_IRQ_MCU_CNT7_SFT 0
+#define AFE_IRQ_MCU_CNT7_MASK 0x3ffff
+#define AFE_IRQ_MCU_CNT7_MASK_SFT (0x3ffff << 0)
+
+/* AFE_MEMIF_MSB */
+#define CPU_COMPACT_MODE_SFT 23
+#define CPU_COMPACT_MODE_MASK 0x1
+#define CPU_COMPACT_MODE_MASK_SFT (0x1 << 23)
+#define CPU_HD_ALIGN_SFT 22
+#define CPU_HD_ALIGN_MASK 0x1
+#define CPU_HD_ALIGN_MASK_SFT (0x1 << 22)
+
+/* AFE_MEMIF_HD_MODE */
+#define HDMI_HD_SFT 20
+#define HDMI_HD_MASK 0x3
+#define HDMI_HD_MASK_SFT (0x3 << 20)
+#define MOD_DAI_HD_SFT 18
+#define MOD_DAI_HD_MASK 0x3
+#define MOD_DAI_HD_MASK_SFT (0x3 << 18)
+#define DAI_HD_SFT 16
+#define DAI_HD_MASK 0x3
+#define DAI_HD_MASK_SFT (0x3 << 16)
+#define VUL_DATA2_HD_SFT 12
+#define VUL_DATA2_HD_MASK 0x3
+#define VUL_DATA2_HD_MASK_SFT (0x3 << 12)
+#define VUL_HD_SFT 10
+#define VUL_HD_MASK 0x3
+#define VUL_HD_MASK_SFT (0x3 << 10)
+#define AWB_HD_SFT 8
+#define AWB_HD_MASK 0x3
+#define AWB_HD_MASK_SFT (0x3 << 8)
+#define DL3_HD_SFT 6
+#define DL3_HD_MASK 0x3
+#define DL3_HD_MASK_SFT (0x3 << 6)
+#define DL2_HD_SFT 4
+#define DL2_HD_MASK 0x3
+#define DL2_HD_MASK_SFT (0x3 << 4)
+#define DL1_DATA2_HD_SFT 2
+#define DL1_DATA2_HD_MASK 0x3
+#define DL1_DATA2_HD_MASK_SFT (0x3 << 2)
+#define DL1_HD_SFT 0
+#define DL1_HD_MASK 0x3
+#define DL1_HD_MASK_SFT (0x3 << 0)
+
+/* AFE_MEMIF_HDALIGN */
+#define HDMI_NORMAL_MODE_SFT 26
+#define HDMI_NORMAL_MODE_MASK 0x1
+#define HDMI_NORMAL_MODE_MASK_SFT (0x1 << 26)
+#define MOD_DAI_NORMAL_MODE_SFT 25
+#define MOD_DAI_NORMAL_MODE_MASK 0x1
+#define MOD_DAI_NORMAL_MODE_MASK_SFT (0x1 << 25)
+#define DAI_NORMAL_MODE_SFT 24
+#define DAI_NORMAL_MODE_MASK 0x1
+#define DAI_NORMAL_MODE_MASK_SFT (0x1 << 24)
+#define VUL_DATA2_NORMAL_MODE_SFT 22
+#define VUL_DATA2_NORMAL_MODE_MASK 0x1
+#define VUL_DATA2_NORMAL_MODE_MASK_SFT (0x1 << 22)
+#define VUL_NORMAL_MODE_SFT 21
+#define VUL_NORMAL_MODE_MASK 0x1
+#define VUL_NORMAL_MODE_MASK_SFT (0x1 << 21)
+#define AWB_NORMAL_MODE_SFT 20
+#define AWB_NORMAL_MODE_MASK 0x1
+#define AWB_NORMAL_MODE_MASK_SFT (0x1 << 20)
+#define DL3_NORMAL_MODE_SFT 19
+#define DL3_NORMAL_MODE_MASK 0x1
+#define DL3_NORMAL_MODE_MASK_SFT (0x1 << 19)
+#define DL2_NORMAL_MODE_SFT 18
+#define DL2_NORMAL_MODE_MASK 0x1
+#define DL2_NORMAL_MODE_MASK_SFT (0x1 << 18)
+#define DL1_DATA2_NORMAL_MODE_SFT 17
+#define DL1_DATA2_NORMAL_MODE_MASK 0x1
+#define DL1_DATA2_NORMAL_MODE_MASK_SFT (0x1 << 17)
+#define DL1_NORMAL_MODE_SFT 16
+#define DL1_NORMAL_MODE_MASK 0x1
+#define DL1_NORMAL_MODE_MASK_SFT (0x1 << 16)
+#define HDMI_HD_ALIGN_SFT 10
+#define HDMI_HD_ALIGN_MASK 0x1
+#define HDMI_HD_ALIGN_MASK_SFT (0x1 << 10)
+#define MOD_DAI_HD_ALIGN_SFT 9
+#define MOD_DAI_HD_ALIGN_MASK 0x1
+#define MOD_DAI_HD_ALIGN_MASK_SFT (0x1 << 9)
+#define DAI_ALIGN_SFT 8
+#define DAI_ALIGN_MASK 0x1
+#define DAI_ALIGN_MASK_SFT (0x1 << 8)
+#define VUL2_HD_ALIGN_SFT 7
+#define VUL2_HD_ALIGN_MASK 0x1
+#define VUL2_HD_ALIGN_MASK_SFT (0x1 << 7)
+#define VUL_DATA2_HD_ALIGN_SFT 6
+#define VUL_DATA2_HD_ALIGN_MASK 0x1
+#define VUL_DATA2_HD_ALIGN_MASK_SFT (0x1 << 6)
+#define VUL_HD_ALIGN_SFT 5
+#define VUL_HD_ALIGN_MASK 0x1
+#define VUL_HD_ALIGN_MASK_SFT (0x1 << 5)
+#define AWB_HD_ALIGN_SFT 4
+#define AWB_HD_ALIGN_MASK 0x1
+#define AWB_HD_ALIGN_MASK_SFT (0x1 << 4)
+#define DL3_HD_ALIGN_SFT 3
+#define DL3_HD_ALIGN_MASK 0x1
+#define DL3_HD_ALIGN_MASK_SFT (0x1 << 3)
+#define DL2_HD_ALIGN_SFT 2
+#define DL2_HD_ALIGN_MASK 0x1
+#define DL2_HD_ALIGN_MASK_SFT (0x1 << 2)
+#define DL1_DATA2_HD_ALIGN_SFT 1
+#define DL1_DATA2_HD_ALIGN_MASK 0x1
+#define DL1_DATA2_HD_ALIGN_MASK_SFT (0x1 << 1)
+#define DL1_HD_ALIGN_SFT 0
+#define DL1_HD_ALIGN_MASK 0x1
+#define DL1_HD_ALIGN_MASK_SFT (0x1 << 0)
+
+/* PCM_INTF_CON1 */
+#define PCM_FIX_VALUE_SEL_SFT 31
+#define PCM_FIX_VALUE_SEL_MASK 0x1
+#define PCM_FIX_VALUE_SEL_MASK_SFT (0x1 << 31)
+#define PCM_BUFFER_LOOPBACK_SFT 30
+#define PCM_BUFFER_LOOPBACK_MASK 0x1
+#define PCM_BUFFER_LOOPBACK_MASK_SFT (0x1 << 30)
+#define PCM_PARALLEL_LOOPBACK_SFT 29
+#define PCM_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 29)
+#define PCM_SERIAL_LOOPBACK_SFT 28
+#define PCM_SERIAL_LOOPBACK_MASK 0x1
+#define PCM_SERIAL_LOOPBACK_MASK_SFT (0x1 << 28)
+#define PCM_DAI_PCM_LOOPBACK_SFT 27
+#define PCM_DAI_PCM_LOOPBACK_MASK 0x1
+#define PCM_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 27)
+#define PCM_I2S_PCM_LOOPBACK_SFT 26
+#define PCM_I2S_PCM_LOOPBACK_MASK 0x1
+#define PCM_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 26)
+#define PCM_SYNC_DELSEL_SFT 25
+#define PCM_SYNC_DELSEL_MASK 0x1
+#define PCM_SYNC_DELSEL_MASK_SFT (0x1 << 25)
+#define PCM_TX_LR_SWAP_SFT 24
+#define PCM_TX_LR_SWAP_MASK 0x1
+#define PCM_TX_LR_SWAP_MASK_SFT (0x1 << 24)
+#define PCM_SYNC_OUT_INV_SFT 23
+#define PCM_SYNC_OUT_INV_MASK 0x1
+#define PCM_SYNC_OUT_INV_MASK_SFT (0x1 << 23)
+#define PCM_BCLK_OUT_INV_SFT 22
+#define PCM_BCLK_OUT_INV_MASK 0x1
+#define PCM_BCLK_OUT_INV_MASK_SFT (0x1 << 22)
+#define PCM_SYNC_IN_INV_SFT 21
+#define PCM_SYNC_IN_INV_MASK 0x1
+#define PCM_SYNC_IN_INV_MASK_SFT (0x1 << 21)
+#define PCM_BCLK_IN_INV_SFT 20
+#define PCM_BCLK_IN_INV_MASK 0x1
+#define PCM_BCLK_IN_INV_MASK_SFT (0x1 << 20)
+#define PCM_TX_LCH_RPT_SFT 19
+#define PCM_TX_LCH_RPT_MASK 0x1
+#define PCM_TX_LCH_RPT_MASK_SFT (0x1 << 19)
+#define PCM_VBT_16K_MODE_SFT 18
+#define PCM_VBT_16K_MODE_MASK 0x1
+#define PCM_VBT_16K_MODE_MASK_SFT (0x1 << 18)
+#define PCM_EXT_MODEM_SFT 17
+#define PCM_EXT_MODEM_MASK 0x1
+#define PCM_EXT_MODEM_MASK_SFT (0x1 << 17)
+#define PCM_24BIT_SFT 16
+#define PCM_24BIT_MASK 0x1
+#define PCM_24BIT_MASK_SFT (0x1 << 16)
+#define PCM_WLEN_SFT 14
+#define PCM_WLEN_MASK 0x3
+#define PCM_WLEN_MASK_SFT (0x3 << 14)
+#define PCM_SYNC_LENGTH_SFT 9
+#define PCM_SYNC_LENGTH_MASK 0x1f
+#define PCM_SYNC_LENGTH_MASK_SFT (0x1f << 9)
+#define PCM_SYNC_TYPE_SFT 8
+#define PCM_SYNC_TYPE_MASK 0x1
+#define PCM_SYNC_TYPE_MASK_SFT (0x1 << 8)
+#define PCM_BT_MODE_SFT 7
+#define PCM_BT_MODE_MASK 0x1
+#define PCM_BT_MODE_MASK_SFT (0x1 << 7)
+#define PCM_BYP_ASRC_SFT 6
+#define PCM_BYP_ASRC_MASK 0x1
+#define PCM_BYP_ASRC_MASK_SFT (0x1 << 6)
+#define PCM_SLAVE_SFT 5
+#define PCM_SLAVE_MASK 0x1
+#define PCM_SLAVE_MASK_SFT (0x1 << 5)
+#define PCM_MODE_SFT 3
+#define PCM_MODE_MASK 0x3
+#define PCM_MODE_MASK_SFT (0x3 << 3)
+#define PCM_FMT_SFT 1
+#define PCM_FMT_MASK 0x3
+#define PCM_FMT_MASK_SFT (0x3 << 1)
+#define PCM_EN_SFT 0
+#define PCM_EN_MASK 0x1
+#define PCM_EN_MASK_SFT (0x1 << 0)
+
+/* PCM_INTF_CON2 */
+#define PCM1_TX_FIFO_OV_SFT 31
+#define PCM1_TX_FIFO_OV_MASK 0x1
+#define PCM1_TX_FIFO_OV_MASK_SFT (0x1 << 31)
+#define PCM1_RX_FIFO_OV_SFT 30
+#define PCM1_RX_FIFO_OV_MASK 0x1
+#define PCM1_RX_FIFO_OV_MASK_SFT (0x1 << 30)
+#define PCM2_TX_FIFO_OV_SFT 29
+#define PCM2_TX_FIFO_OV_MASK 0x1
+#define PCM2_TX_FIFO_OV_MASK_SFT (0x1 << 29)
+#define PCM2_RX_FIFO_OV_SFT 28
+#define PCM2_RX_FIFO_OV_MASK 0x1
+#define PCM2_RX_FIFO_OV_MASK_SFT (0x1 << 28)
+#define PCM1_SYNC_GLITCH_SFT 27
+#define PCM1_SYNC_GLITCH_MASK 0x1
+#define PCM1_SYNC_GLITCH_MASK_SFT (0x1 << 27)
+#define PCM2_SYNC_GLITCH_SFT 26
+#define PCM2_SYNC_GLITCH_MASK 0x1
+#define PCM2_SYNC_GLITCH_MASK_SFT (0x1 << 26)
+#define PCM1_PCM2_LOOPBACK_SFT 15
+#define PCM1_PCM2_LOOPBACK_MASK 0x1
+#define PCM1_PCM2_LOOPBACK_MASK_SFT (0x1 << 15)
+#define DAI_PCM_LOOPBACK_CH_SFT 13
+#define DAI_PCM_LOOPBACK_CH_MASK 0x1
+#define DAI_PCM_LOOPBACK_CH_MASK_SFT (0x1 << 13)
+#define I2S_PCM_LOOPBACK_CH_SFT 12
+#define I2S_PCM_LOOPBACK_CH_MASK 0x1
+#define I2S_PCM_LOOPBACK_CH_MASK_SFT (0x1 << 12)
+#define PCM_USE_MD3_SFT 8
+#define PCM_USE_MD3_MASK 0x1
+#define PCM_USE_MD3_MASK_SFT (0x1 << 8)
+#define TX_FIX_VALUE_SFT 0
+#define TX_FIX_VALUE_MASK 0xff
+#define TX_FIX_VALUE_MASK_SFT (0xff << 0)
+
+/* PCM2_INTF_CON */
+#define PCM2_TX_FIX_VALUE_SFT 24
+#define PCM2_TX_FIX_VALUE_MASK 0xff
+#define PCM2_TX_FIX_VALUE_MASK_SFT (0xff << 24)
+#define PCM2_FIX_VALUE_SEL_SFT 23
+#define PCM2_FIX_VALUE_SEL_MASK 0x1
+#define PCM2_FIX_VALUE_SEL_MASK_SFT (0x1 << 23)
+#define PCM2_BUFFER_LOOPBACK_SFT 22
+#define PCM2_BUFFER_LOOPBACK_MASK 0x1
+#define PCM2_BUFFER_LOOPBACK_MASK_SFT (0x1 << 22)
+#define PCM2_PARALLEL_LOOPBACK_SFT 21
+#define PCM2_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM2_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 21)
+#define PCM2_SERIAL_LOOPBACK_SFT 20
+#define PCM2_SERIAL_LOOPBACK_MASK 0x1
+#define PCM2_SERIAL_LOOPBACK_MASK_SFT (0x1 << 20)
+#define PCM2_DAI_PCM_LOOPBACK_SFT 19
+#define PCM2_DAI_PCM_LOOPBACK_MASK 0x1
+#define PCM2_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 19)
+#define PCM2_I2S_PCM_LOOPBACK_SFT 18
+#define PCM2_I2S_PCM_LOOPBACK_MASK 0x1
+#define PCM2_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 18)
+#define PCM2_SYNC_DELSEL_SFT 17
+#define PCM2_SYNC_DELSEL_MASK 0x1
+#define PCM2_SYNC_DELSEL_MASK_SFT (0x1 << 17)
+#define PCM2_TX_LR_SWAP_SFT 16
+#define PCM2_TX_LR_SWAP_MASK 0x1
+#define PCM2_TX_LR_SWAP_MASK_SFT (0x1 << 16)
+#define PCM2_SYNC_IN_INV_SFT 15
+#define PCM2_SYNC_IN_INV_MASK 0x1
+#define PCM2_SYNC_IN_INV_MASK_SFT (0x1 << 15)
+#define PCM2_BCLK_IN_INV_SFT 14
+#define PCM2_BCLK_IN_INV_MASK 0x1
+#define PCM2_BCLK_IN_INV_MASK_SFT (0x1 << 14)
+#define PCM2_TX_LCH_RPT_SFT 13
+#define PCM2_TX_LCH_RPT_MASK 0x1
+#define PCM2_TX_LCH_RPT_MASK_SFT (0x1 << 13)
+#define PCM2_VBT_16K_MODE_SFT 12
+#define PCM2_VBT_16K_MODE_MASK 0x1
+#define PCM2_VBT_16K_MODE_MASK_SFT (0x1 << 12)
+#define PCM2_LOOPBACK_CH_SEL_SFT 10
+#define PCM2_LOOPBACK_CH_SEL_MASK 0x3
+#define PCM2_LOOPBACK_CH_SEL_MASK_SFT (0x3 << 10)
+#define PCM2_TX2_BT_MODE_SFT 8
+#define PCM2_TX2_BT_MODE_MASK 0x1
+#define PCM2_TX2_BT_MODE_MASK_SFT (0x1 << 8)
+#define PCM2_BT_MODE_SFT 7
+#define PCM2_BT_MODE_MASK 0x1
+#define PCM2_BT_MODE_MASK_SFT (0x1 << 7)
+#define PCM2_AFIFO_SFT 6
+#define PCM2_AFIFO_MASK 0x1
+#define PCM2_AFIFO_MASK_SFT (0x1 << 6)
+#define PCM2_WLEN_SFT 5
+#define PCM2_WLEN_MASK 0x1
+#define PCM2_WLEN_MASK_SFT (0x1 << 5)
+#define PCM2_MODE_SFT 3
+#define PCM2_MODE_MASK 0x3
+#define PCM2_MODE_MASK_SFT (0x3 << 3)
+#define PCM2_FMT_SFT 1
+#define PCM2_FMT_MASK 0x3
+#define PCM2_FMT_MASK_SFT (0x3 << 1)
+#define PCM2_EN_SFT 0
+#define PCM2_EN_MASK 0x1
+#define PCM2_EN_MASK_SFT (0x1 << 0)
+#endif
diff --git a/sound/soc/mediatek/mt7986/Makefile b/sound/soc/mediatek/mt7986/Makefile
new file mode 100644
index 000000000000..4b54bbe88683
--- /dev/null
+++ b/sound/soc/mediatek/mt7986/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt7986-afe-y := \
+ mt7986-afe-pcm.o \
+ mt7986-dai-etdm.o
+
+obj-$(CONFIG_SND_SOC_MT7986) += snd-soc-mt7986-afe.o
+obj-$(CONFIG_SND_SOC_MT7986_WM8960) += mt7986-wm8960.o
diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-common.h b/sound/soc/mediatek/mt7986/mt7986-afe-common.h
new file mode 100644
index 000000000000..fc3bb31e5167
--- /dev/null
+++ b/sound/soc/mediatek/mt7986/mt7986-afe-common.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt7986-afe-common.h -- MediaTek 7986 audio driver definitions
+ *
+ * Copyright (c) 2023 MediaTek Inc.
+ * Authors: Vic Wu <vic.wu@mediatek.com>
+ * Maso Huang <maso.huang@mediatek.com>
+ */
+
+#ifndef _MT_7986_AFE_COMMON_H_
+#define _MT_7986_AFE_COMMON_H_
+
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT7986_MEMIF_DL1,
+ MT7986_MEMIF_VUL12,
+ MT7986_MEMIF_NUM,
+ MT7986_DAI_ETDM = MT7986_MEMIF_NUM,
+ MT7986_DAI_NUM,
+};
+
+enum {
+ MT7986_IRQ_0,
+ MT7986_IRQ_1,
+ MT7986_IRQ_2,
+ MT7986_IRQ_NUM,
+};
+
+struct mt7986_afe_private {
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ int pm_runtime_bypass_reg_ctl;
+
+ /* dai */
+ void *dai_priv[MT7986_DAI_NUM];
+};
+
+unsigned int mt7986_afe_rate_transform(struct device *dev,
+ unsigned int rate);
+
+/* dai register */
+int mt7986_dai_etdm_register(struct mtk_base_afe *afe);
+#endif
diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
new file mode 100644
index 000000000000..7a6ad9116e55
--- /dev/null
+++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
@@ -0,0 +1,609 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC AFE platform driver for MT7986
+ *
+ * Copyright (c) 2023 MediaTek Inc.
+ * Authors: Vic Wu <vic.wu@mediatek.com>
+ * Maso Huang <maso.huang@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+
+#include "mt7986-afe-common.h"
+#include "mt7986-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+enum {
+ MTK_AFE_RATE_8K = 0,
+ MTK_AFE_RATE_11K = 1,
+ MTK_AFE_RATE_12K = 2,
+ MTK_AFE_RATE_16K = 4,
+ MTK_AFE_RATE_22K = 5,
+ MTK_AFE_RATE_24K = 6,
+ MTK_AFE_RATE_32K = 8,
+ MTK_AFE_RATE_44K = 9,
+ MTK_AFE_RATE_48K = 10,
+ MTK_AFE_RATE_88K = 13,
+ MTK_AFE_RATE_96K = 14,
+ MTK_AFE_RATE_176K = 17,
+ MTK_AFE_RATE_192K = 18,
+};
+
+enum {
+ CLK_INFRA_AUD_BUS_CK = 0,
+ CLK_INFRA_AUD_26M_CK,
+ CLK_INFRA_AUD_L_CK,
+ CLK_INFRA_AUD_AUD_CK,
+ CLK_INFRA_AUD_EG2_CK,
+ CLK_NUM
+};
+
+static const char *aud_clks[CLK_NUM] = {
+ [CLK_INFRA_AUD_BUS_CK] = "aud_bus_ck",
+ [CLK_INFRA_AUD_26M_CK] = "aud_26m_ck",
+ [CLK_INFRA_AUD_L_CK] = "aud_l_ck",
+ [CLK_INFRA_AUD_AUD_CK] = "aud_aud_ck",
+ [CLK_INFRA_AUD_EG2_CK] = "aud_eg2_ck",
+};
+
+unsigned int mt7986_afe_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_RATE_8K;
+ case 11025:
+ return MTK_AFE_RATE_11K;
+ case 12000:
+ return MTK_AFE_RATE_12K;
+ case 16000:
+ return MTK_AFE_RATE_16K;
+ case 22050:
+ return MTK_AFE_RATE_22K;
+ case 24000:
+ return MTK_AFE_RATE_24K;
+ case 32000:
+ return MTK_AFE_RATE_32K;
+ case 44100:
+ return MTK_AFE_RATE_44K;
+ case 48000:
+ return MTK_AFE_RATE_48K;
+ case 88200:
+ return MTK_AFE_RATE_88K;
+ case 96000:
+ return MTK_AFE_RATE_96K;
+ case 176400:
+ return MTK_AFE_RATE_176K;
+ case 192000:
+ return MTK_AFE_RATE_192K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n",
+ __func__, rate, MTK_AFE_RATE_48K);
+ return MTK_AFE_RATE_48K;
+ }
+}
+
+static const struct snd_pcm_hardware mt7986_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 256,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 8 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+static int mt7986_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return mt7986_afe_rate_transform(afe->dev, rate);
+}
+
+static int mt7986_irq_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return mt7986_afe_rate_transform(afe->dev, rate);
+}
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt7986_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = MT7986_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT7986_MEMIF_VUL12,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+};
+
+static const struct snd_kcontrol_new o018_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I150_Switch", AFE_CONN018_4, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new o019_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I151_Switch", AFE_CONN019_4, 23, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mt7986_memif_widgets[] = {
+ /* DL */
+ SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* UL */
+ SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0,
+ o018_mix, ARRAY_SIZE(o018_mix)),
+ SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0,
+ o019_mix, ARRAY_SIZE(o019_mix)),
+};
+
+static const struct snd_soc_dapm_route mt7986_memif_routes[] = {
+ {"I032", NULL, "DL1"},
+ {"I033", NULL, "DL1"},
+ {"UL1", NULL, "O018"},
+ {"UL1", NULL, "O019"},
+ {"O018", "I150_Switch", "I150"},
+ {"O019", "I151_Switch", "I151"},
+};
+
+static const struct snd_soc_component_driver mt7986_afe_pcm_dai_component = {
+ .name = "mt7986-afe-pcm-dai",
+};
+
+static const struct mtk_base_memif_data memif_data[MT7986_MEMIF_NUM] = {
+ [MT7986_MEMIF_DL1] = {
+ .name = "DL1",
+ .id = MT7986_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL0_BASE,
+ .reg_ofs_cur = AFE_DL0_CUR,
+ .reg_ofs_end = AFE_DL0_END,
+ .reg_ofs_base_msb = AFE_DL0_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL0_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL0_END_MSB,
+ .fs_reg = AFE_DL0_CON0,
+ .fs_shift = DL0_MODE_SFT,
+ .fs_maskbit = DL0_MODE_MASK,
+ .mono_reg = AFE_DL0_CON0,
+ .mono_shift = DL0_MONO_SFT,
+ .enable_reg = AFE_DL0_CON0,
+ .enable_shift = DL0_ON_SFT,
+ .hd_reg = AFE_DL0_CON0,
+ .hd_shift = DL0_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL0_CON0,
+ .hd_align_mshift = DL0_HALIGN_SFT,
+ .pbuf_reg = AFE_DL0_CON0,
+ .pbuf_shift = DL0_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL0_CON0,
+ .minlen_shift = DL0_MINLEN_SFT,
+ },
+ [MT7986_MEMIF_VUL12] = {
+ .name = "VUL12",
+ .id = MT7986_MEMIF_VUL12,
+ .reg_ofs_base = AFE_VUL0_BASE,
+ .reg_ofs_cur = AFE_VUL0_CUR,
+ .reg_ofs_end = AFE_VUL0_END,
+ .reg_ofs_base_msb = AFE_VUL0_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL0_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL0_END_MSB,
+ .fs_reg = AFE_VUL0_CON0,
+ .fs_shift = VUL0_MODE_SFT,
+ .fs_maskbit = VUL0_MODE_MASK,
+ .mono_reg = AFE_VUL0_CON0,
+ .mono_shift = VUL0_MONO_SFT,
+ .enable_reg = AFE_VUL0_CON0,
+ .enable_shift = VUL0_ON_SFT,
+ .hd_reg = AFE_VUL0_CON0,
+ .hd_shift = VUL0_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL0_CON0,
+ .hd_align_mshift = VUL0_HALIGN_SFT,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT7986_IRQ_NUM] = {
+ [MT7986_IRQ_0] = {
+ .id = MT7986_IRQ_0,
+ .irq_cnt_reg = AFE_IRQ0_MCU_CFG1,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ0_MCU_CFG0,
+ .irq_fs_shift = IRQ_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ0_MCU_CFG0,
+ .irq_en_shift = IRQ_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ0_MCU_CLR_SFT,
+ },
+ [MT7986_IRQ_1] = {
+ .id = MT7986_IRQ_1,
+ .irq_cnt_reg = AFE_IRQ1_MCU_CFG1,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ1_MCU_CFG0,
+ .irq_fs_shift = IRQ_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ1_MCU_CFG0,
+ .irq_en_shift = IRQ_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ1_MCU_CLR_SFT,
+ },
+ [MT7986_IRQ_2] = {
+ .id = MT7986_IRQ_2,
+ .irq_cnt_reg = AFE_IRQ2_MCU_CFG1,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ2_MCU_CFG0,
+ .irq_fs_shift = IRQ_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ2_MCU_CFG0,
+ .irq_en_shift = IRQ_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ2_MCU_CLR_SFT,
+ },
+};
+
+static bool mt7986_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /*
+ * Those auto-gen regs are read-only, so put it as volatile because
+ * volatile registers cannot be cached, which means that they cannot
+ * be set when power is off
+ */
+
+ switch (reg) {
+ case AFE_DL0_CUR_MSB:
+ case AFE_DL0_CUR:
+ case AFE_DL0_RCH_MON:
+ case AFE_DL0_LCH_MON:
+ case AFE_VUL0_CUR_MSB:
+ case AFE_VUL0_CUR:
+ case AFE_IRQ_MCU_STATUS:
+ case AFE_MEMIF_RD_MON:
+ case AFE_MEMIF_WR_MON:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt7986_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .volatile_reg = mt7986_is_volatile_reg,
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1),
+};
+
+static int mt7986_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+ int ret, i;
+
+ afe_priv->clks = devm_kcalloc(afe->dev, CLK_NUM,
+ sizeof(*afe_priv->clks), GFP_KERNEL);
+ if (!afe_priv->clks)
+ return -ENOMEM;
+ afe_priv->num_clks = CLK_NUM;
+
+ for (i = 0; i < afe_priv->num_clks; i++)
+ afe_priv->clks[i].id = aud_clks[i];
+
+ ret = devm_clk_bulk_get(afe->dev, afe_priv->num_clks, afe_priv->clks);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "Failed to get clocks\n");
+
+ return 0;
+}
+
+static irqreturn_t mt7986_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ u32 mcu_en, status, status_mcu;
+ int i, ret;
+ irqreturn_t irq_ret = IRQ_HANDLED;
+
+ /* get irq that is sent to MCU */
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ /* only care IRQ which is sent to MCU */
+ status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
+
+ if (ret || status_mcu == 0) {
+ dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n",
+ __func__, ret, status, mcu_en);
+
+ irq_ret = IRQ_NONE;
+ goto err_irq;
+ }
+
+ for (i = 0; i < MT7986_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (status_mcu & (1 << irq->irq_data->irq_en_shift))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, status_mcu);
+
+ return irq_ret;
+}
+
+static int mt7986_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ /* disable clk*/
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0x3fff);
+ regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, 0);
+ regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, 0);
+
+ /* make sure all irq status are cleared, twice intended */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff);
+
+skip_regmap:
+ clk_bulk_disable_unprepare(afe_priv->num_clks, afe_priv->clks);
+
+ return 0;
+}
+
+static int mt7986_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n");
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ return 0;
+
+ /* enable clk*/
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0);
+ regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK,
+ AUD_APLL2_EN);
+ regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK,
+ AUD_26M_EN);
+
+ return 0;
+}
+
+static int mt7986_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt7986_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt7986_memif_dai_driver);
+
+ dai->dapm_widgets = mt7986_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt7986_memif_widgets);
+ dai->dapm_routes = mt7986_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt7986_memif_routes);
+
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt7986_dai_etdm_register,
+ mt7986_dai_memif_register,
+};
+
+static int mt7986_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt7986_afe_private *afe_priv;
+ struct device *dev;
+ int i, irq_id, ret;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+ dev = afe->dev;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ /* initial audio related clock */
+ ret = mt7986_init_clock(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot initialize clocks\n");
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+ pm_runtime_get_sync(&pdev->dev);
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mt7986_afe_regmap_config);
+
+ pm_runtime_put_sync(&pdev->dev);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ /* init memif */
+ afe->memif_size = MT7986_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = -1;
+ }
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ afe->irqs_size = MT7986_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return irq_id;
+
+ ret = devm_request_irq(dev, irq_id, mt7986_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ afe->mtk_afe_hardware = &mt7986_afe_hardware;
+ afe->memif_fs = mt7986_memif_fs;
+ afe->irq_fs = mt7986_irq_fs;
+
+ afe->runtime_resume = mt7986_afe_runtime_resume;
+ afe->runtime_suspend = mt7986_afe_runtime_suspend;
+
+ /* register component */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register AFE component\n");
+
+ ret = devm_snd_soc_register_component(afe->dev,
+ &mt7986_afe_pcm_dai_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n");
+
+ return 0;
+}
+
+static void mt7986_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt7986_afe_runtime_suspend(&pdev->dev);
+}
+
+static const struct of_device_id mt7986_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt7986-afe" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mt7986_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt7986_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt7986_afe_runtime_suspend,
+ mt7986_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt7986_afe_pcm_driver = {
+ .driver = {
+ .name = "mt7986-audio",
+ .of_match_table = mt7986_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt7986_afe_pm_ops),
+ },
+ .probe = mt7986_afe_pcm_dev_probe,
+ .remove = mt7986_afe_pcm_dev_remove,
+};
+module_platform_driver(mt7986_afe_pcm_driver);
+
+MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA MT7986");
+MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c
new file mode 100644
index 000000000000..fc55ff47b7bc
--- /dev/null
+++ b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI eTDM Control
+ *
+ * Copyright (c) 2023 MediaTek Inc.
+ * Authors: Vic Wu <vic.wu@mediatek.com>
+ * Maso Huang <maso.huang@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt7986-afe-common.h"
+#include "mt7986-reg.h"
+
+#define HOPPING_CLK 0
+#define APLL_CLK 1
+#define MTK_DAI_ETDM_FORMAT_I2S 0
+#define MTK_DAI_ETDM_FORMAT_DSPA 4
+#define MTK_DAI_ETDM_FORMAT_DSPB 5
+
+enum {
+ MTK_ETDM_RATE_8K = 0,
+ MTK_ETDM_RATE_12K = 1,
+ MTK_ETDM_RATE_16K = 2,
+ MTK_ETDM_RATE_24K = 3,
+ MTK_ETDM_RATE_32K = 4,
+ MTK_ETDM_RATE_48K = 5,
+ MTK_ETDM_RATE_96K = 7,
+ MTK_ETDM_RATE_192K = 9,
+ MTK_ETDM_RATE_11K = 16,
+ MTK_ETDM_RATE_22K = 17,
+ MTK_ETDM_RATE_44K = 18,
+ MTK_ETDM_RATE_88K = 19,
+ MTK_ETDM_RATE_176K = 20,
+};
+
+struct mtk_dai_etdm_priv {
+ bool bck_inv;
+ bool lrck_inv;
+ bool slave_mode;
+ unsigned int format;
+};
+
+static unsigned int mt7986_etdm_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_ETDM_RATE_8K;
+ case 11025:
+ return MTK_ETDM_RATE_11K;
+ case 12000:
+ return MTK_ETDM_RATE_12K;
+ case 16000:
+ return MTK_ETDM_RATE_16K;
+ case 22050:
+ return MTK_ETDM_RATE_22K;
+ case 24000:
+ return MTK_ETDM_RATE_24K;
+ case 32000:
+ return MTK_ETDM_RATE_32K;
+ case 44100:
+ return MTK_ETDM_RATE_44K;
+ case 48000:
+ return MTK_ETDM_RATE_48K;
+ case 88200:
+ return MTK_ETDM_RATE_88K;
+ case 96000:
+ return MTK_ETDM_RATE_96K;
+ case 176400:
+ return MTK_ETDM_RATE_176K;
+ case 192000:
+ return MTK_ETDM_RATE_192K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n",
+ __func__, rate, MTK_ETDM_RATE_48K);
+ return MTK_ETDM_RATE_48K;
+ }
+}
+
+static int get_etdm_wlen(unsigned int bitwidth)
+{
+ return bitwidth <= 16 ? 16 : 32;
+}
+
+/* dai component */
+/* interconnection */
+
+static const struct snd_kcontrol_new o124_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032_Switch", AFE_CONN124_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new o125_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033_Switch", AFE_CONN125_1, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
+
+ /* DL */
+ SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* UL */
+ SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, o124_mix, ARRAY_SIZE(o124_mix)),
+ SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, o125_mix, ARRAY_SIZE(o125_mix)),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
+ {"I150", NULL, "ETDM Capture"},
+ {"I151", NULL, "ETDM Capture"},
+ {"ETDM Playback", NULL, "O124"},
+ {"ETDM Playback", NULL, "O125"},
+ {"O124", "I032_Switch", "I032"},
+ {"O125", "I033_Switch", "I033"},
+};
+
+/* dai ops */
+static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n");
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, 0);
+
+ return 0;
+}
+
+static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK,
+ CLK_OUT5_PDN);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK,
+ CLK_IN5_PDN);
+
+ clk_bulk_disable_unprepare(afe_priv->num_clks, afe_priv->clks);
+}
+
+static unsigned int get_etdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 16)
+ return 24;
+ else if (channels > 8)
+ return 16;
+ else if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+ else
+ return 2;
+}
+
+static int mtk_dai_etdm_config(struct mtk_base_afe *afe,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ int stream)
+{
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+ unsigned int rate = params_rate(params);
+ unsigned int etdm_rate = mt7986_etdm_rate_transform(afe->dev, rate);
+ unsigned int afe_rate = mt7986_afe_rate_transform(afe->dev, rate);
+ unsigned int channels = params_channels(params);
+ unsigned int bit_width = params_width(params);
+ unsigned int wlen = get_etdm_wlen(bit_width);
+ unsigned int val = 0;
+ unsigned int mask = 0;
+
+ dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n",
+ __func__, stream, rate, bit_width);
+
+ /* CON0 */
+ mask |= ETDM_BIT_LEN_MASK;
+ val |= FIELD_PREP(ETDM_BIT_LEN_MASK, bit_width - 1);
+ mask |= ETDM_WRD_LEN_MASK;
+ val |= FIELD_PREP(ETDM_WRD_LEN_MASK, wlen - 1);
+ mask |= ETDM_FMT_MASK;
+ val |= FIELD_PREP(ETDM_FMT_MASK, etdm_data->format);
+ mask |= ETDM_CH_NUM_MASK;
+ val |= FIELD_PREP(ETDM_CH_NUM_MASK, get_etdm_ch_fixup(channels) - 1);
+ mask |= RELATCH_SRC_MASK;
+ val |= FIELD_PREP(RELATCH_SRC_MASK, APLL_CLK);
+
+ switch (stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ /* set ETDM_OUT5_CON0 */
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, mask, val);
+
+ /* set ETDM_OUT5_CON4 */
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON4,
+ OUT_RELATCH_MASK, OUT_RELATCH(afe_rate));
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON4,
+ OUT_CLK_SRC_MASK, OUT_CLK_SRC(APLL_CLK));
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON4,
+ OUT_SEL_FS_MASK, OUT_SEL_FS(etdm_rate));
+
+ /* set ETDM_OUT5_CON5 */
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON5,
+ ETDM_CLK_DIV_MASK, ETDM_CLK_DIV);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ /* set ETDM_IN5_CON0 */
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON0, mask, val);
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON0,
+ ETDM_SYNC_MASK, ETDM_SYNC);
+
+ /* set ETDM_IN5_CON2 */
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON2,
+ IN_CLK_SRC_MASK, IN_CLK_SRC(APLL_CLK));
+
+ /* set ETDM_IN5_CON3 */
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON3,
+ IN_SEL_FS_MASK, IN_SEL_FS(etdm_rate));
+
+ /* set ETDM_IN5_CON4 */
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON4,
+ IN_RELATCH_MASK, IN_RELATCH(afe_rate));
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned int rate = params_rate(params);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ switch (rate) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_PLAYBACK);
+ mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_CAPTURE);
+ return 0;
+ default:
+ dev_err(afe->dev,
+ "Sample rate %d invalid. Supported rates: 8/12/16/24/32/48/96/192 kHz\n",
+ rate);
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK,
+ ETDM_EN);
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK,
+ ETDM_EN);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK,
+ 0);
+ regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK,
+ 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt7986_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ void *priv_data;
+
+ switch (dai->id) {
+ case MT7986_DAI_ETDM:
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv),
+ GFP_KERNEL);
+ if (!priv_data)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[dai->id] = priv_data;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ etdm_data->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ etdm_data->slave_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
+ .startup = mtk_dai_etdm_startup,
+ .shutdown = mtk_dai_etdm_shutdown,
+ .hw_params = mtk_dai_etdm_hw_params,
+ .trigger = mtk_dai_etdm_trigger,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
+ {
+ .name = "ETDM",
+ .id = MT7986_DAI_ETDM,
+ .capture = {
+ .stream_name = "ETDM Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .playback = {
+ .stream_name = "ETDM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int mt7986_dai_etdm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_etdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
+
+ dai->dapm_widgets = mtk_dai_etdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
+ dai->dapm_routes = mtk_dai_etdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt7986/mt7986-reg.h b/sound/soc/mediatek/mt7986/mt7986-reg.h
new file mode 100644
index 000000000000..c2b200743c3f
--- /dev/null
+++ b/sound/soc/mediatek/mt7986/mt7986-reg.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt7986-reg.h -- MediaTek 7986 audio driver reg definition
+ *
+ * Copyright (c) 2023 MediaTek Inc.
+ * Authors: Vic Wu <vic.wu@mediatek.com>
+ * Maso Huang <maso.huang@mediatek.com>
+ */
+
+#ifndef _MT7986_REG_H_
+#define _MT7986_REG_H_
+
+#define AUDIO_TOP_CON2 0x0008
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_ENGEN_CON0 0x0014
+#define AFE_IRQ_MCU_EN 0x0100
+#define AFE_IRQ_MCU_STATUS 0x0120
+#define AFE_IRQ_MCU_CLR 0x0128
+#define AFE_IRQ0_MCU_CFG0 0x0140
+#define AFE_IRQ0_MCU_CFG1 0x0144
+#define AFE_IRQ1_MCU_CFG0 0x0148
+#define AFE_IRQ1_MCU_CFG1 0x014c
+#define AFE_IRQ2_MCU_CFG0 0x0150
+#define AFE_IRQ2_MCU_CFG1 0x0154
+#define ETDM_IN5_CON0 0x13f0
+#define ETDM_IN5_CON1 0x13f4
+#define ETDM_IN5_CON2 0x13f8
+#define ETDM_IN5_CON3 0x13fc
+#define ETDM_IN5_CON4 0x1400
+#define ETDM_OUT5_CON0 0x1570
+#define ETDM_OUT5_CON4 0x1580
+#define ETDM_OUT5_CON5 0x1584
+#define ETDM_4_7_COWORK_CON0 0x15e0
+#define ETDM_4_7_COWORK_CON1 0x15e4
+#define AFE_CONN018_1 0x1b44
+#define AFE_CONN018_4 0x1b50
+#define AFE_CONN019_1 0x1b64
+#define AFE_CONN019_4 0x1b70
+#define AFE_CONN124_1 0x2884
+#define AFE_CONN124_4 0x2890
+#define AFE_CONN125_1 0x28a4
+#define AFE_CONN125_4 0x28b0
+#define AFE_CONN_RS_0 0x3920
+#define AFE_CONN_RS_3 0x392c
+#define AFE_CONN_16BIT_0 0x3960
+#define AFE_CONN_16BIT_3 0x396c
+#define AFE_CONN_24BIT_0 0x3980
+#define AFE_CONN_24BIT_3 0x398c
+#define AFE_MEMIF_CON0 0x3d98
+#define AFE_MEMIF_RD_MON 0x3da0
+#define AFE_MEMIF_WR_MON 0x3da4
+#define AFE_DL0_BASE_MSB 0x3e40
+#define AFE_DL0_BASE 0x3e44
+#define AFE_DL0_CUR_MSB 0x3e48
+#define AFE_DL0_CUR 0x3e4c
+#define AFE_DL0_END_MSB 0x3e50
+#define AFE_DL0_END 0x3e54
+#define AFE_DL0_RCH_MON 0x3e58
+#define AFE_DL0_LCH_MON 0x3e5c
+#define AFE_DL0_CON0 0x3e60
+#define AFE_VUL0_BASE_MSB 0x4220
+#define AFE_VUL0_BASE 0x4224
+#define AFE_VUL0_CUR_MSB 0x4228
+#define AFE_VUL0_CUR 0x422c
+#define AFE_VUL0_END_MSB 0x4230
+#define AFE_VUL0_END 0x4234
+#define AFE_VUL0_CON0 0x4238
+
+#define AFE_MAX_REGISTER AFE_VUL0_CON0
+#define AFE_IRQ_STATUS_BITS 0x7
+#define AFE_IRQ_CNT_SHIFT 0
+#define AFE_IRQ_CNT_MASK 0xffffff
+
+/* AUDIO_TOP_CON2 */
+#define CLK_OUT5_PDN BIT(14)
+#define CLK_OUT5_PDN_MASK BIT(14)
+#define CLK_IN5_PDN BIT(7)
+#define CLK_IN5_PDN_MASK BIT(7)
+
+/* AUDIO_TOP_CON4 */
+#define PDN_APLL_TUNER2 BIT(12)
+#define PDN_APLL_TUNER2_MASK BIT(12)
+
+/* AUDIO_ENGEN_CON0 */
+#define AUD_APLL2_EN BIT(3)
+#define AUD_APLL2_EN_MASK BIT(3)
+#define AUD_26M_EN BIT(0)
+#define AUD_26M_EN_MASK BIT(0)
+
+/* AFE_DL0_CON0 */
+#define DL0_ON_SFT 28
+#define DL0_ON_MASK 0x1
+#define DL0_ON_MASK_SFT BIT(28)
+#define DL0_MINLEN_SFT 20
+#define DL0_MINLEN_MASK 0xf
+#define DL0_MINLEN_MASK_SFT (0xf << 20)
+#define DL0_MODE_SFT 8
+#define DL0_MODE_MASK 0x1f
+#define DL0_MODE_MASK_SFT (0x1f << 8)
+#define DL0_PBUF_SIZE_SFT 5
+#define DL0_PBUF_SIZE_MASK 0x3
+#define DL0_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL0_MONO_SFT 4
+#define DL0_MONO_MASK 0x1
+#define DL0_MONO_MASK_SFT BIT(4)
+#define DL0_HALIGN_SFT 2
+#define DL0_HALIGN_MASK 0x1
+#define DL0_HALIGN_MASK_SFT BIT(2)
+#define DL0_HD_MODE_SFT 0
+#define DL0_HD_MODE_MASK 0x3
+#define DL0_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL0_CON0 */
+#define VUL0_ON_SFT 28
+#define VUL0_ON_MASK 0x1
+#define VUL0_ON_MASK_SFT BIT(28)
+#define VUL0_MODE_SFT 8
+#define VUL0_MODE_MASK 0x1f
+#define VUL0_MODE_MASK_SFT (0x1f << 8)
+#define VUL0_MONO_SFT 4
+#define VUL0_MONO_MASK 0x1
+#define VUL0_MONO_MASK_SFT BIT(4)
+#define VUL0_HALIGN_SFT 2
+#define VUL0_HALIGN_MASK 0x1
+#define VUL0_HALIGN_MASK_SFT BIT(2)
+#define VUL0_HD_MODE_SFT 0
+#define VUL0_HD_MODE_MASK 0x3
+#define VUL0_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_IRQ_MCU_CON */
+#define IRQ_MCU_MODE_SFT 4
+#define IRQ_MCU_MODE_MASK 0x1f
+#define IRQ_MCU_MODE_MASK_SFT (0x1f << 4)
+#define IRQ_MCU_ON_SFT 0
+#define IRQ_MCU_ON_MASK 0x1
+#define IRQ_MCU_ON_MASK_SFT BIT(0)
+#define IRQ0_MCU_CLR_SFT 0
+#define IRQ0_MCU_CLR_MASK 0x1
+#define IRQ0_MCU_CLR_MASK_SFT BIT(0)
+#define IRQ1_MCU_CLR_SFT 1
+#define IRQ1_MCU_CLR_MASK 0x1
+#define IRQ1_MCU_CLR_MASK_SFT BIT(1)
+#define IRQ2_MCU_CLR_SFT 2
+#define IRQ2_MCU_CLR_MASK 0x1
+#define IRQ2_MCU_CLR_MASK_SFT BIT(2)
+
+/* ETDM_IN5_CON2 */
+#define IN_CLK_SRC(x) ((x) << 10)
+#define IN_CLK_SRC_SFT 10
+#define IN_CLK_SRC_MASK GENMASK(12, 10)
+
+/* ETDM_IN5_CON3 */
+#define IN_SEL_FS(x) ((x) << 26)
+#define IN_SEL_FS_SFT 26
+#define IN_SEL_FS_MASK GENMASK(30, 26)
+
+/* ETDM_IN5_CON4 */
+#define IN_RELATCH(x) ((x) << 20)
+#define IN_RELATCH_SFT 20
+#define IN_RELATCH_MASK GENMASK(24, 20)
+#define IN_CLK_INV BIT(18)
+#define IN_CLK_INV_MASK BIT(18)
+
+/* ETDM_IN5_CON0 & ETDM_OUT5_CON0 */
+#define RELATCH_SRC_MASK GENMASK(30, 28)
+#define ETDM_CH_NUM_MASK GENMASK(27, 23)
+#define ETDM_WRD_LEN_MASK GENMASK(20, 16)
+#define ETDM_BIT_LEN_MASK GENMASK(15, 11)
+#define ETDM_FMT_MASK GENMASK(8, 6)
+#define ETDM_SYNC BIT(1)
+#define ETDM_SYNC_MASK BIT(1)
+#define ETDM_EN BIT(0)
+#define ETDM_EN_MASK BIT(0)
+
+/* ETDM_OUT5_CON4 */
+#define OUT_RELATCH(x) ((x) << 24)
+#define OUT_RELATCH_SFT 24
+#define OUT_RELATCH_MASK GENMASK(28, 24)
+#define OUT_CLK_SRC(x) ((x) << 6)
+#define OUT_CLK_SRC_SFT 6
+#define OUT_CLK_SRC_MASK GENMASK(8, 6)
+#define OUT_SEL_FS(x) (x)
+#define OUT_SEL_FS_SFT 0
+#define OUT_SEL_FS_MASK GENMASK(4, 0)
+
+/* ETDM_OUT5_CON5 */
+#define ETDM_CLK_DIV BIT(12)
+#define ETDM_CLK_DIV_MASK BIT(12)
+#define OUT_CLK_INV BIT(9)
+#define OUT_CLK_INV_MASK BIT(9)
+
+/* ETDM_4_7_COWORK_CON0 */
+#define OUT_SEL(x) ((x) << 12)
+#define OUT_SEL_SFT 12
+#define OUT_SEL_MASK GENMASK(15, 12)
+#endif
diff --git a/sound/soc/mediatek/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c
new file mode 100644
index 000000000000..f1dc18222be7
--- /dev/null
+++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt7986-wm8960.c -- MT7986-WM8960 ALSA SoC machine driver
+ *
+ * Copyright (c) 2023 MediaTek Inc.
+ * Authors: Vic Wu <vic.wu@mediatek.com>
+ * Maso Huang <maso.huang@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "mt7986-afe-common.h"
+
+static const struct snd_soc_dapm_widget mt7986_wm8960_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+};
+
+static const struct snd_kcontrol_new mt7986_wm8960_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("AMIC"),
+};
+
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt7986_wm8960_dai_links[] = {
+ /* FE */
+ {
+ .name = "wm8960-playback",
+ .stream_name = "wm8960-playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
+ },
+ {
+ .name = "wm8960-capture",
+ .stream_name = "wm8960-capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+ /* BE */
+ {
+ .name = "wm8960-codec",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC |
+ SND_SOC_DAIFMT_GATED,
+ SND_SOC_DAILINK_REG(codec),
+ },
+};
+
+static struct snd_soc_card mt7986_wm8960_card = {
+ .name = "mt7986-wm8960",
+ .owner = THIS_MODULE,
+ .dai_link = mt7986_wm8960_dai_links,
+ .num_links = ARRAY_SIZE(mt7986_wm8960_dai_links),
+ .controls = mt7986_wm8960_controls,
+ .num_controls = ARRAY_SIZE(mt7986_wm8960_controls),
+ .dapm_widgets = mt7986_wm8960_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt7986_wm8960_widgets),
+};
+
+static int mt7986_wm8960_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt7986_wm8960_card;
+ struct snd_soc_dai_link *dai_link;
+ struct device_node *platform, *codec;
+ struct device_node *platform_dai_node, *codec_dai_node;
+ int ret, i;
+
+ card->dev = &pdev->dev;
+
+ platform = of_get_child_by_name(pdev->dev.of_node, "platform");
+
+ if (platform) {
+ platform_dai_node = of_parse_phandle(platform, "sound-dai", 0);
+ of_node_put(platform);
+
+ if (!platform_dai_node) {
+ dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_dai_node;
+ }
+
+ codec = of_get_child_by_name(pdev->dev.of_node, "codec");
+
+ if (codec) {
+ codec_dai_node = of_parse_phandle(codec, "sound-dai", 0);
+ of_node_put(codec);
+
+ if (!codec_dai_node) {
+ of_node_put(platform_dai_node);
+ dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n");
+ return -EINVAL;
+ }
+ } else {
+ of_node_put(platform_dai_node);
+ dev_err(&pdev->dev, "Property 'codec' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->codecs->name)
+ continue;
+ dai_link->codecs->of_node = codec_dai_node;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret);
+ goto err_of_node_put;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
+ goto err_of_node_put;
+ }
+
+err_of_node_put:
+ of_node_put(platform_dai_node);
+ of_node_put(codec_dai_node);
+ return ret;
+}
+
+static const struct of_device_id mt7986_wm8960_machine_dt_match[] = {
+ {.compatible = "mediatek,mt7986-wm8960-sound"},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mt7986_wm8960_machine_dt_match);
+
+static struct platform_driver mt7986_wm8960_machine = {
+ .driver = {
+ .name = "mt7986-wm8960",
+ .of_match_table = mt7986_wm8960_machine_dt_match,
+ },
+ .probe = mt7986_wm8960_machine_probe,
+};
+
+module_platform_driver(mt7986_wm8960_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT7986 WM8960 ALSA SoC machine driver");
+MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mt7986 wm8960 soc card");
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644
index 000000000000..c1eed0d2653b
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
new file mode 100644
index 000000000000..396fe2355eea
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8173_afe_common.h -- Mediatek 8173 audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Hidalgo Huang <hidalgo.huang@mediatek.com>
+ * Ir Lian <ir.lian@mediatek.com>
+ */
+
+#ifndef _MT8173_AFE_COMMON_H_
+#define _MT8173_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+ MT8173_AFE_MEMIF_DL1,
+ MT8173_AFE_MEMIF_DL2,
+ MT8173_AFE_MEMIF_VUL,
+ MT8173_AFE_MEMIF_DAI,
+ MT8173_AFE_MEMIF_AWB,
+ MT8173_AFE_MEMIF_MOD_DAI,
+ MT8173_AFE_MEMIF_HDMI,
+ MT8173_AFE_MEMIF_NUM,
+ MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
+ MT8173_AFE_IO_MOD_PCM2,
+ MT8173_AFE_IO_PMIC,
+ MT8173_AFE_IO_I2S,
+ MT8173_AFE_IO_2ND_I2S,
+ MT8173_AFE_IO_HW_GAIN1,
+ MT8173_AFE_IO_HW_GAIN2,
+ MT8173_AFE_IO_MRG_O,
+ MT8173_AFE_IO_MRG_I,
+ MT8173_AFE_IO_DAIBT,
+ MT8173_AFE_IO_HDMI,
+};
+
+enum {
+ MT8173_AFE_IRQ_DL1,
+ MT8173_AFE_IRQ_DL2,
+ MT8173_AFE_IRQ_VUL,
+ MT8173_AFE_IRQ_DAI,
+ MT8173_AFE_IRQ_AWB,
+ MT8173_AFE_IRQ_MOD_DAI,
+ MT8173_AFE_IRQ_HDMI,
+ MT8173_AFE_IRQ_NUM,
+};
+
+enum {
+ MT8173_CLK_INFRASYS_AUD,
+ MT8173_CLK_TOP_PDN_AUD,
+ MT8173_CLK_TOP_PDN_AUD_BUS,
+ MT8173_CLK_I2S0_M,
+ MT8173_CLK_I2S1_M,
+ MT8173_CLK_I2S2_M,
+ MT8173_CLK_I2S3_M,
+ MT8173_CLK_I2S3_B,
+ MT8173_CLK_BCK0,
+ MT8173_CLK_BCK1,
+ MT8173_CLK_NUM
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
new file mode 100644
index 000000000000..c0fa623e0b17
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -0,0 +1,1241 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek 8173 ALSA SoC AFE platform driver
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Hidalgo Huang <hidalgo.huang@mediatek.com>
+ * Ir Lian <ir.lian@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+/*****************************************************************************
+ * R E G I S T E R D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON1 0x0004
+#define AFE_DAC_CON0 0x0010
+#define AFE_DAC_CON1 0x0014
+#define AFE_I2S_CON1 0x0034
+#define AFE_I2S_CON2 0x0038
+#define AFE_CONN_24BIT 0x006c
+#define AFE_MEMIF_MSB 0x00cc
+
+#define AFE_CONN1 0x0024
+#define AFE_CONN2 0x0028
+#define AFE_CONN3 0x002c
+#define AFE_CONN7 0x0460
+#define AFE_CONN8 0x0464
+#define AFE_HDMI_CONN0 0x0390
+
+/* Memory interface */
+#define AFE_DL1_BASE 0x0040
+#define AFE_DL1_CUR 0x0044
+#define AFE_DL1_END 0x0048
+#define AFE_DL2_BASE 0x0050
+#define AFE_DL2_CUR 0x0054
+#define AFE_AWB_BASE 0x0070
+#define AFE_AWB_CUR 0x007c
+#define AFE_VUL_BASE 0x0080
+#define AFE_VUL_CUR 0x008c
+#define AFE_VUL_END 0x0088
+#define AFE_DAI_BASE 0x0090
+#define AFE_DAI_CUR 0x009c
+#define AFE_MOD_PCM_BASE 0x0330
+#define AFE_MOD_PCM_CUR 0x033c
+#define AFE_HDMI_OUT_BASE 0x0374
+#define AFE_HDMI_OUT_CUR 0x0378
+#define AFE_HDMI_OUT_END 0x037c
+
+#define AFE_ADDA_TOP_CON0 0x0120
+#define AFE_ADDA2_TOP_CON0 0x0600
+
+#define AFE_HDMI_OUT_CON0 0x0370
+
+#define AFE_IRQ_MCU_CON 0x03a0
+#define AFE_IRQ_STATUS 0x03a4
+#define AFE_IRQ_CLR 0x03a8
+#define AFE_IRQ_CNT1 0x03ac
+#define AFE_IRQ_CNT2 0x03b0
+#define AFE_IRQ_MCU_EN 0x03b4
+#define AFE_IRQ_CNT5 0x03bc
+#define AFE_IRQ_CNT7 0x03dc
+
+#define AFE_TDM_CON1 0x0548
+#define AFE_TDM_CON2 0x054c
+
+#define AFE_IRQ_STATUS_BITS 0xff
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUD_TCON0_PDN_SPDF (0x1 << 21)
+#define AUD_TCON0_PDN_HDMI (0x1 << 20)
+#define AUD_TCON0_PDN_24M (0x1 << 9)
+#define AUD_TCON0_PDN_22M (0x1 << 8)
+#define AUD_TCON0_PDN_AFE (0x1 << 2)
+
+/* AFE_I2S_CON1 (0x0034) */
+#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12)
+#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8)
+#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3)
+#define AFE_I2S_CON1_EN (0x1 << 0)
+
+/* AFE_I2S_CON2 (0x0038) */
+#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12)
+#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8)
+#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3)
+#define AFE_I2S_CON2_EN (0x1 << 0)
+
+/* AFE_CONN_24BIT (0x006c) */
+#define AFE_CONN_24BIT_O04 (0x1 << 4)
+#define AFE_CONN_24BIT_O03 (0x1 << 3)
+
+/* AFE_HDMI_CONN0 (0x0390) */
+#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21)
+#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18)
+#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15)
+#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12)
+#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9)
+#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6)
+#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3)
+#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0)
+
+/* AFE_TDM_CON1 (0x0548) */
+#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24)
+#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12)
+#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8)
+#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4)
+#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3)
+#define AFE_TDM_CON1_LRCK_INV (0x1 << 2)
+#define AFE_TDM_CON1_BCK_INV (0x1 << 1)
+#define AFE_TDM_CON1_EN (0x1 << 0)
+
+enum afe_tdm_ch_start {
+ AFE_TDM_CH_START_O30_O31 = 0,
+ AFE_TDM_CH_START_O32_O33,
+ AFE_TDM_CH_START_O34_O35,
+ AFE_TDM_CH_START_O36_O37,
+ AFE_TDM_CH_ZERO,
+};
+
+static const unsigned int mt8173_afe_backup_list[] = {
+ AUDIO_TOP_CON0,
+ AFE_CONN1,
+ AFE_CONN2,
+ AFE_CONN7,
+ AFE_CONN8,
+ AFE_DAC_CON1,
+ AFE_DL1_BASE,
+ AFE_DL1_END,
+ AFE_VUL_BASE,
+ AFE_VUL_END,
+ AFE_HDMI_OUT_BASE,
+ AFE_HDMI_OUT_END,
+ AFE_HDMI_CONN0,
+ AFE_DAC_CON0,
+};
+
+struct mt8173_afe_private {
+ struct clk *clocks[MT8173_CLK_NUM];
+};
+
+static const struct snd_pcm_hardware mt8173_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 512,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .fifo_size = 0,
+};
+
+struct mt8173_afe_rate {
+ unsigned int rate;
+ unsigned int regvalue;
+};
+
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
+ { .rate = 8000, .regvalue = 0 },
+ { .rate = 11025, .regvalue = 1 },
+ { .rate = 12000, .regvalue = 2 },
+ { .rate = 16000, .regvalue = 4 },
+ { .rate = 22050, .regvalue = 5 },
+ { .rate = 24000, .regvalue = 6 },
+ { .rate = 32000, .regvalue = 8 },
+ { .rate = 44100, .regvalue = 9 },
+ { .rate = 48000, .regvalue = 10 },
+ { .rate = 88000, .regvalue = 11 },
+ { .rate = 96000, .regvalue = 12 },
+ { .rate = 174000, .regvalue = 13 },
+ { .rate = 192000, .regvalue = 14 },
+};
+
+static int mt8173_afe_i2s_fs(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8173_afe_i2s_rates); i++)
+ if (mt8173_afe_i2s_rates[i].rate == sample_rate)
+ return mt8173_afe_i2s_rates[i].regvalue;
+
+ return -EINVAL;
+}
+
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
+{
+ unsigned int val;
+ int fs = mt8173_afe_i2s_fs(rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ /* from external ADC */
+ regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1);
+ regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1);
+
+ /* set input */
+ val = AFE_I2S_CON2_LOW_JITTER_CLK |
+ AFE_I2S_CON2_RATE(fs) |
+ AFE_I2S_CON2_FORMAT_I2S;
+
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val);
+
+ /* set output */
+ val = AFE_I2S_CON1_LOW_JITTER_CLK |
+ AFE_I2S_CON1_RATE(fs) |
+ AFE_I2S_CON1_FORMAT_I2S;
+
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val);
+ return 0;
+}
+
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
+{
+ unsigned int val;
+
+ regmap_read(afe->regmap, AFE_I2S_CON2, &val);
+ if (!!(val & AFE_I2S_CON2_EN) == enable)
+ return;
+
+ /* input */
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable);
+
+ /* output */
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
+}
+
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
+ struct clk *m_ck, struct clk *b_ck)
+{
+ int ret;
+
+ if (m_ck) {
+ ret = clk_prepare_enable(m_ck);
+ if (ret) {
+ dev_err(afe->dev, "Failed to enable m_ck\n");
+ return ret;
+ }
+ }
+
+ if (b_ck) {
+ ret = clk_prepare_enable(b_ck);
+ if (ret) {
+ dev_err(afe->dev, "Failed to enable b_ck\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
+ struct clk *m_ck, unsigned int mck_rate,
+ struct clk *b_ck, unsigned int bck_rate)
+{
+ int ret;
+
+ if (m_ck) {
+ ret = clk_set_rate(m_ck, mck_rate);
+ if (ret) {
+ dev_err(afe->dev, "Failed to set m_ck rate\n");
+ return ret;
+ }
+ }
+
+ if (b_ck) {
+ ret = clk_set_rate(b_ck, bck_rate);
+ if (ret) {
+ dev_err(afe->dev, "Failed to set b_ck rate\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
+ struct clk *m_ck, struct clk *b_ck)
+{
+ clk_disable_unprepare(m_ck);
+ clk_disable_unprepare(b_ck);
+}
+
+static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ if (snd_soc_dai_active(dai))
+ return 0;
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
+ return 0;
+}
+
+static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ if (snd_soc_dai_active(dai))
+ return;
+
+ mt8173_afe_set_i2s_enable(afe, false);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
+ AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
+}
+
+static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
+ runtime->rate * 256, NULL, 0);
+ mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
+ runtime->rate * 256, NULL, 0);
+ /* config I2S */
+ ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
+ if (ret)
+ return ret;
+
+ mt8173_afe_set_i2s_enable(afe, true);
+
+ return 0;
+}
+
+static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
+ if (snd_soc_dai_active(dai))
+ return 0;
+
+ mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+ afe_priv->clocks[MT8173_CLK_I2S3_B]);
+ return 0;
+}
+
+static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
+ if (snd_soc_dai_active(dai))
+ return;
+
+ mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+ afe_priv->clocks[MT8173_CLK_I2S3_B]);
+}
+
+static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
+ unsigned int val;
+
+ mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+ runtime->rate * 128,
+ afe_priv->clocks[MT8173_CLK_I2S3_B],
+ runtime->rate * runtime->channels * 32);
+
+ val = AFE_TDM_CON1_BCK_INV |
+ AFE_TDM_CON1_LRCK_INV |
+ AFE_TDM_CON1_1_BCK_DELAY |
+ AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */
+ AFE_TDM_CON1_WLEN_32BIT |
+ AFE_TDM_CON1_32_BCK_CYCLES |
+ AFE_TDM_CON1_LRCK_WIDTH(32);
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val);
+
+ /* set tdm2 config */
+ switch (runtime->channels) {
+ case 1:
+ case 2:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_ZERO << 4);
+ val |= (AFE_TDM_CH_ZERO << 8);
+ val |= (AFE_TDM_CH_ZERO << 12);
+ break;
+ case 3:
+ case 4:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_START_O32_O33 << 4);
+ val |= (AFE_TDM_CH_ZERO << 8);
+ val |= (AFE_TDM_CH_ZERO << 12);
+ break;
+ case 5:
+ case 6:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_START_O32_O33 << 4);
+ val |= (AFE_TDM_CH_START_O34_O35 << 8);
+ val |= (AFE_TDM_CH_ZERO << 12);
+ break;
+ case 7:
+ case 8:
+ val = AFE_TDM_CH_START_O30_O31;
+ val |= (AFE_TDM_CH_START_O32_O33 << 4);
+ val |= (AFE_TDM_CH_START_O34_O35 << 8);
+ val |= (AFE_TDM_CH_START_O36_O37 << 12);
+ break;
+ default:
+ val = 0;
+ }
+ regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val);
+
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ 0x000000f0, runtime->channels << 4);
+ return 0;
+}
+
+static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0);
+
+ /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
+ regmap_write(afe->regmap, AFE_HDMI_CONN0,
+ AFE_HDMI_CONN0_O30_I30 |
+ AFE_HDMI_CONN0_O31_I31 |
+ AFE_HDMI_CONN0_O32_I34 |
+ AFE_HDMI_CONN0_O33_I35 |
+ AFE_HDMI_CONN0_O34_I32 |
+ AFE_HDMI_CONN0_O35_I33 |
+ AFE_HDMI_CONN0_O36_I36 |
+ AFE_HDMI_CONN0_O37_I37);
+
+ /* enable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
+
+ /* enable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1);
+
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0);
+
+ /* disable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0);
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
+ AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mtk_base_afe_memif *memif = &afe->memif[snd_soc_rtd_to_cpu(rtd, 0)->id];
+ int fs;
+
+ if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+ memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
+ switch (rate) {
+ case 8000:
+ fs = 0;
+ break;
+ case 16000:
+ fs = 1;
+ break;
+ case 32000:
+ fs = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ fs = mt8173_afe_i2s_fs(rate);
+ }
+ return fs;
+}
+
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ return mt8173_afe_i2s_fs(rate);
+}
+
+/* BE DAIs */
+static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
+ .startup = mt8173_afe_i2s_startup,
+ .shutdown = mt8173_afe_i2s_shutdown,
+ .prepare = mt8173_afe_i2s_prepare,
+};
+
+static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
+ .startup = mt8173_afe_hdmi_startup,
+ .shutdown = mt8173_afe_hdmi_shutdown,
+ .prepare = mt8173_afe_hdmi_prepare,
+ .trigger = mt8173_afe_hdmi_trigger,
+};
+
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1", /* downlink 1 */
+ .id = MT8173_AFE_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_fe_ops,
+ }, {
+ .name = "VUL", /* voice uplink */
+ .id = MT8173_AFE_MEMIF_VUL,
+ .capture = {
+ .stream_name = "VUL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk_afe_fe_ops,
+ }, {
+ /* BE DAIs */
+ .name = "I2S",
+ .id = MT8173_AFE_IO_I2S,
+ .playback = {
+ .stream_name = "I2S Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "I2S Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt8173_afe_i2s_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
+ /* FE DAIs */
+ {
+ .name = "HDMI",
+ .id = MT8173_AFE_MEMIF_HDMI,
+ .playback = {
+ .stream_name = "HDMI",
+ .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,
+ },
+ .ops = &mtk_afe_fe_ops,
+ }, {
+ /* BE DAIs */
+ .name = "HDMIO",
+ .id = MT8173_AFE_IO_HDMI,
+ .playback = {
+ .stream_name = "HDMIO 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,
+ },
+ .ops = &mt8173_afe_hdmi_ops,
+ },
+};
+
+static const struct snd_kcontrol_new mt8173_afe_o03_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8173_afe_o04_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8173_afe_o09_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8173_afe_o10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mt8173_afe_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
+ mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
+ SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
+ mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
+ SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
+ mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
+ SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
+ mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
+};
+
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
+ {"I05", NULL, "DL1"},
+ {"I06", NULL, "DL1"},
+ {"I2S Playback", NULL, "O03"},
+ {"I2S Playback", NULL, "O04"},
+ {"VUL", NULL, "O09"},
+ {"VUL", NULL, "O10"},
+ {"I03", NULL, "I2S Capture"},
+ {"I04", NULL, "I2S Capture"},
+ {"I17", NULL, "I2S Capture"},
+ {"I18", NULL, "I2S Capture"},
+ { "O03", "I05 Switch", "I05" },
+ { "O04", "I06 Switch", "I06" },
+ { "O09", "I17 Switch", "I17" },
+ { "O09", "I03 Switch", "I03" },
+ { "O10", "I18 Switch", "I18" },
+ { "O10", "I04 Switch", "I04" },
+};
+
+static const struct snd_soc_dapm_route mt8173_afe_hdmi_routes[] = {
+ {"HDMIO Playback", NULL, "HDMI"},
+};
+
+static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = {
+ .name = "mt8173-afe-pcm-dai",
+ .dapm_widgets = mt8173_afe_pcm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets),
+ .dapm_routes = mt8173_afe_pcm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes),
+ .suspend = mtk_afe_suspend,
+ .resume = mtk_afe_resume,
+};
+
+static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = {
+ .name = "mt8173-afe-hdmi-dai",
+ .dapm_routes = mt8173_afe_hdmi_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes),
+ .suspend = mtk_afe_suspend,
+ .resume = mtk_afe_resume,
+};
+
+static const char *aud_clks[MT8173_CLK_NUM] = {
+ [MT8173_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+ [MT8173_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+ [MT8173_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+ [MT8173_CLK_I2S0_M] = "i2s0_m",
+ [MT8173_CLK_I2S1_M] = "i2s1_m",
+ [MT8173_CLK_I2S2_M] = "i2s2_m",
+ [MT8173_CLK_I2S3_M] = "i2s3_m",
+ [MT8173_CLK_I2S3_B] = "i2s3_b",
+ [MT8173_CLK_BCK0] = "bck0",
+ [MT8173_CLK_BCK1] = "bck1",
+};
+
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
+ {
+ .name = "DL1",
+ .id = MT8173_AFE_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 21,
+ .hd_reg = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 0,
+ .agent_disable_reg = -1,
+ }, {
+ .name = "DL2",
+ .id = MT8173_AFE_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 4,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 22,
+ .hd_reg = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 2,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 1,
+ .agent_disable_reg = -1,
+ }, {
+ .name = "VUL",
+ .id = MT8173_AFE_MEMIF_VUL,
+ .reg_ofs_base = AFE_VUL_BASE,
+ .reg_ofs_cur = AFE_VUL_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 16,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 27,
+ .hd_reg = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 3,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 6,
+ .agent_disable_reg = -1,
+ }, {
+ .name = "DAI",
+ .id = MT8173_AFE_MEMIF_DAI,
+ .reg_ofs_base = AFE_DAI_BASE,
+ .reg_ofs_cur = AFE_DAI_CUR,
+ .fs_reg = AFE_DAC_CON0,
+ .fs_shift = 24,
+ .fs_maskbit = 0x3,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 4,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 5,
+ .agent_disable_reg = -1,
+ }, {
+ .name = "AWB",
+ .id = MT8173_AFE_MEMIF_AWB,
+ .reg_ofs_base = AFE_AWB_BASE,
+ .reg_ofs_cur = AFE_AWB_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 12,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 24,
+ .hd_reg = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 6,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 3,
+ .agent_disable_reg = -1,
+ }, {
+ .name = "MOD_DAI",
+ .id = MT8173_AFE_MEMIF_MOD_DAI,
+ .reg_ofs_base = AFE_MOD_PCM_BASE,
+ .reg_ofs_cur = AFE_MOD_PCM_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 30,
+ .fs_maskbit = 0x3,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 30,
+ .hd_reg = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 7,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 4,
+ .agent_disable_reg = -1,
+ }, {
+ .name = "HDMI",
+ .id = MT8173_AFE_MEMIF_HDMI,
+ .reg_ofs_base = AFE_HDMI_OUT_BASE,
+ .reg_ofs_cur = AFE_HDMI_OUT_CUR,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = -1,
+ .enable_reg = -1,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 8,
+ .agent_disable_reg = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+ {
+ .id = MT8173_AFE_IRQ_DL1,
+ .irq_cnt_reg = AFE_IRQ_CNT1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 0,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 4,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 0,
+ }, {
+ .id = MT8173_AFE_IRQ_DL2,
+ .irq_cnt_reg = AFE_IRQ_CNT1,
+ .irq_cnt_shift = 20,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 2,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 16,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 2,
+
+ }, {
+ .id = MT8173_AFE_IRQ_VUL,
+ .irq_cnt_reg = AFE_IRQ_CNT2,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 1,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 8,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 1,
+ }, {
+ .id = MT8173_AFE_IRQ_DAI,
+ .irq_cnt_reg = AFE_IRQ_CNT2,
+ .irq_cnt_shift = 20,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 3,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 20,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 3,
+ }, {
+ .id = MT8173_AFE_IRQ_AWB,
+ .irq_cnt_reg = AFE_IRQ_CNT7,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 14,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 6,
+ }, {
+ .id = MT8173_AFE_IRQ_DAI,
+ .irq_cnt_reg = AFE_IRQ_CNT2,
+ .irq_cnt_shift = 20,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 3,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 20,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 3,
+ }, {
+ .id = MT8173_AFE_IRQ_HDMI,
+ .irq_cnt_reg = AFE_IRQ_CNT5,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 12,
+ .irq_fs_reg = -1,
+ .irq_fs_maskbit = -1,
+ .irq_clr_reg = AFE_IRQ_CLR,
+ .irq_clr_shift = 4,
+ },
+};
+
+static const struct regmap_config mt8173_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_ADDA2_TOP_CON0,
+ .cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
+{
+ struct mtk_base_afe *afe = dev_id;
+ unsigned int reg_value;
+ int i, ret;
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &reg_value);
+ if (ret) {
+ dev_err(afe->dev, "%s irq status err\n", __func__);
+ reg_value = AFE_IRQ_STATUS_BITS;
+ goto err_irq;
+ }
+
+ for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+ struct mtk_base_afe_irq *irq_p;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq_p = &afe->irqs[memif->irq_usage];
+
+ if (!(reg_value & (1 << irq_p->irq_data->irq_clr_shift)))
+ continue;
+
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap, AFE_IRQ_CLR,
+ reg_value & AFE_IRQ_STATUS_BITS);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8173_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
+
+ /* disable AFE clk */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
+
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
+ return 0;
+}
+
+static int mt8173_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+ if (ret)
+ goto err_infra;
+
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+ if (ret)
+ goto err_top_aud_bus;
+
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
+ if (ret)
+ goto err_top_aud;
+
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
+ if (ret)
+ goto err_bck0;
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+ if (ret)
+ goto err_i2s1_m;
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+ if (ret)
+ goto err_i2s2_m;
+
+ /* enable AFE clk */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0);
+
+ /* set O3/O4 16bits */
+ regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
+ AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0);
+
+ /* unmask all IRQs */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff);
+
+ /* enable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+ return 0;
+
+err_i2s1_m:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+err_i2s2_m:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+err_bck0:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+err_top_aud:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+err_top_aud_bus:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+err_infra:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
+ return ret;
+}
+
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
+{
+ size_t i;
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clocks[i])) {
+ dev_err(afe->dev, "%s devm_clk_get %s fail\n",
+ __func__, aud_clks[i]);
+ return PTR_ERR(afe_priv->clocks[i]);
+ }
+ }
+ clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+ clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
+ return 0;
+}
+
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ int irq_id;
+ struct mtk_base_afe *afe;
+ struct mt8173_afe_private *afe_priv;
+ struct snd_soc_component *comp_pcm, *comp_hdmi;
+ struct device *dev = &pdev->dev;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL);
+ afe_priv = afe->platform_priv;
+ if (!afe_priv)
+ return -ENOMEM;
+
+ afe->dev = dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
+
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id <= 0)
+ return irq_id < 0 ? irq_id : -ENXIO;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr,
+ &mt8173_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ /* initial audio related clock */
+ ret = mt8173_afe_init_audio_clk(afe);
+ if (ret) {
+ dev_err(dev, "mt8173_afe_init_audio_clk fail\n");
+ return ret;
+ }
+
+ /* memif % irq initialize*/
+ afe->memif_size = MT8173_AFE_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size,
+ sizeof(*afe->memif), GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ afe->irqs_size = MT8173_AFE_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size,
+ sizeof(*afe->irqs), GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->irqs[i].irq_data = &irq_data[i];
+ afe->irqs[i].irq_occupyed = true;
+ afe->memif[i].irq_usage = i;
+ afe->memif[i].const_irq = 1;
+ }
+
+ afe->mtk_afe_hardware = &mt8173_afe_hardware;
+ afe->memif_fs = mt8173_memif_fs;
+ afe->irq_fs = mt8173_irq_fs;
+
+ platform_set_drvdata(pdev, afe);
+
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mt8173_afe_runtime_resume(dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ afe->reg_back_up_list = mt8173_afe_backup_list;
+ afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+ afe->runtime_resume = mt8173_afe_runtime_resume;
+ afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret)
+ goto err_pm_disable;
+
+ comp_pcm = devm_kzalloc(dev, sizeof(*comp_pcm), GFP_KERNEL);
+ if (!comp_pcm) {
+ ret = -ENOMEM;
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_component_initialize(comp_pcm,
+ &mt8173_afe_pcm_dai_component,
+ dev);
+ if (ret)
+ goto err_pm_disable;
+
+#ifdef CONFIG_DEBUG_FS
+ comp_pcm->debugfs_prefix = "pcm";
+#endif
+
+ ret = snd_soc_add_component(comp_pcm,
+ mt8173_afe_pcm_dais,
+ ARRAY_SIZE(mt8173_afe_pcm_dais));
+ if (ret)
+ goto err_pm_disable;
+
+ comp_hdmi = devm_kzalloc(dev, sizeof(*comp_hdmi), GFP_KERNEL);
+ if (!comp_hdmi) {
+ ret = -ENOMEM;
+ goto err_cleanup_components;
+ }
+
+ ret = snd_soc_component_initialize(comp_hdmi,
+ &mt8173_afe_hdmi_dai_component,
+ dev);
+ if (ret)
+ goto err_cleanup_components;
+
+#ifdef CONFIG_DEBUG_FS
+ comp_hdmi->debugfs_prefix = "hdmi";
+#endif
+
+ ret = snd_soc_add_component(comp_hdmi,
+ mt8173_afe_hdmi_dais,
+ ARRAY_SIZE(mt8173_afe_hdmi_dais));
+ if (ret)
+ goto err_cleanup_components;
+
+ ret = devm_request_irq(dev, irq_id, mt8173_afe_irq_handler,
+ 0, "Afe_ISR_Handle", (void *)afe);
+ if (ret) {
+ dev_err(dev, "could not request_irq\n");
+ goto err_cleanup_components;
+ }
+
+ dev_info(dev, "MT8173 AFE driver initialized.\n");
+ return 0;
+
+err_cleanup_components:
+ snd_soc_unregister_component(dev);
+err_pm_disable:
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ snd_soc_unregister_component(dev);
+
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt8173_afe_runtime_suspend(dev);
+}
+
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8173-afe-pcm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8173_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+ mt8173_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8173_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8173-afe-pcm",
+ .of_match_table = mt8173_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8173_afe_pm_ops),
+ },
+ .probe = mt8173_afe_pcm_dev_probe,
+ .remove = mt8173_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8173_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35047ba..49ebb67c818a 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -1,24 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.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 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.
*/
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/jack.h>
-#include <linux/gpio.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
static struct snd_soc_jack mt8173_max98090_jack;
@@ -60,14 +51,14 @@ static const struct snd_kcontrol_new mt8173_max98090_controls[] = {
static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256,
SND_SOC_CLOCK_IN);
}
-static struct snd_soc_ops mt8173_max98090_ops = {
+static const struct snd_soc_ops mt8173_max98090_ops = {
.hw_params = mt8173_max98090_hw_params,
};
@@ -75,62 +66,64 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
struct snd_soc_card *card = runtime->card;
- struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
/* enable jack detection */
- ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE,
- &mt8173_max98090_jack, NULL, 0);
- if (ret) {
- dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_jack_add_pins(&mt8173_max98090_jack,
- ARRAY_SIZE(mt8173_max98090_jack_pins),
- mt8173_max98090_jack_pins);
+ ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADSET,
+ &mt8173_max98090_jack,
+ mt8173_max98090_jack_pins,
+ ARRAY_SIZE(mt8173_max98090_jack_pins));
if (ret) {
- dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret);
+ dev_err(card->dev, "Can't create a new Jack %d\n", ret);
return ret;
}
- return max98090_mic_detect(codec, &mt8173_max98090_jack);
+ return max98090_mic_detect(component, &mt8173_max98090_jack);
}
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8173_max98090_dais[] = {
/* Front End DAI links */
{
.name = "MAX98090 Playback",
.stream_name = "MAX98090 Playback",
- .cpu_dai_name = "DL1",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
},
{
.name = "MAX98090 Capture",
.stream_name = "MAX98090 Capture",
- .cpu_dai_name = "VUL",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
- .dpcm_capture = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
},
/* Back End DAI links */
{
.name = "Codec",
- .cpu_dai_name = "I2S",
.no_pcm = 1,
- .codec_dai_name = "HiFi",
.init = mt8173_max98090_init,
.ops = &mt8173_max98090_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(hifi),
},
};
@@ -151,6 +144,7 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8173_max98090_card;
struct device_node *codec_node, *platform_node;
+ struct snd_soc_dai_link *dai_link;
int ret, i;
platform_node = of_parse_phandle(pdev->dev.of_node,
@@ -159,10 +153,10 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
- for (i = 0; i < card->num_links; i++) {
- if (mt8173_max98090_dais[i].platform_name)
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
continue;
- mt8173_max98090_dais[i].platform_of_node = platform_node;
+ dai_link->platforms->of_node = platform_node;
}
codec_node = of_parse_phandle(pdev->dev.of_node,
@@ -170,19 +164,22 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_platform_node;
}
- for (i = 0; i < card->num_links; i++) {
- if (mt8173_max98090_dais[i].codec_name)
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->codecs->name)
continue;
- mt8173_max98090_dais[i].codec_of_node = codec_node;
+ dai_link->codecs->of_node = codec_node;
}
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
- __func__, ret);
+
+ of_node_put(codec_node);
+
+put_platform_node:
+ of_node_put(platform_node);
return ret;
}
@@ -196,9 +193,7 @@ static struct platform_driver mt8173_max98090_driver = {
.driver = {
.name = "mt8173-max98090",
.of_match_table = mt8173_max98090_dt_match,
-#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
-#endif
},
.probe = mt8173_max98090_dev_probe,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
new file mode 100644
index 000000000000..dc063d85e62f
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8173-rt5650-rt5514.c -- MT8173 machine driver with RT5650/5514 codecs
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/rt5645.h"
+
+#define MCLK_FOR_CODECS 12288000
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = {
+ {"Speaker", NULL, "SPOL"},
+ {"Speaker", NULL, "SPOR"},
+ {"Sub DMIC1L", NULL, "Int Mic"},
+ {"Sub DMIC1R", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_jack_pin mt8173_rt5650_rt5514_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ /* pll from mclk 12.288M */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+ params_rate(params) * 512);
+ if (ret)
+ return ret;
+
+ /* sysclk from pll */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
+ .hw_params = mt8173_rt5650_rt5514_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_rt5514_jack;
+
+static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ int ret;
+
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &mt8173_rt5650_rt5514_jack,
+ mt8173_rt5650_rt5514_jack_pins,
+ ARRAY_SIZE(mt8173_rt5650_rt5514_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+ return ret;
+ }
+
+ return rt5645_set_jack_detect(component,
+ &mt8173_rt5650_rt5514_jack,
+ &mt8173_rt5650_rt5514_jack,
+ &mt8173_rt5650_rt5514_jack);
+}
+
+enum {
+ DAI_LINK_PLAYBACK,
+ DAI_LINK_CAPTURE,
+ DAI_LINK_CODEC_I2S,
+};
+
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"),
+ COMP_CODEC(NULL, "rt5514-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = {
+ /* Front End DAI links */
+ [DAI_LINK_PLAYBACK] = {
+ .name = "rt5650_rt5514 Playback",
+ .stream_name = "rt5650_rt5514 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
+ },
+ [DAI_LINK_CAPTURE] = {
+ .name = "rt5650_rt5514 Capture",
+ .stream_name = "rt5650_rt5514 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+ /* Back End DAI links */
+ [DAI_LINK_CODEC_I2S] = {
+ .name = "Codec",
+ .no_pcm = 1,
+ .init = mt8173_rt5650_rt5514_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &mt8173_rt5650_rt5514_ops,
+ .ignore_pmdown_time = 1,
+ SND_SOC_DAILINK_REG(codec),
+ },
+};
+
+static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = {
+ {
+ .name_prefix = "Sub",
+ },
+};
+
+static struct snd_soc_card mt8173_rt5650_rt5514_card = {
+ .name = "mtk-rt5650-rt5514",
+ .owner = THIS_MODULE,
+ .dai_link = mt8173_rt5650_rt5514_dais,
+ .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais),
+ .codec_conf = mt8173_rt5650_rt5514_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf),
+ .controls = mt8173_rt5650_rt5514_controls,
+ .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls),
+ .dapm_widgets = mt8173_rt5650_rt5514_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets),
+ .dapm_routes = mt8173_rt5650_rt5514_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes),
+};
+
+static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8173_rt5650_rt5514_card;
+ struct device_node *platform_node;
+ struct snd_soc_dai_link *dai_link;
+ int i, ret;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+ if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+ if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ mt8173_rt5650_rt5514_codec_conf[0].dlc.of_node =
+ mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+out:
+ of_node_put(platform_node);
+ return ret;
+}
+
+static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = {
+ { .compatible = "mediatek,mt8173-rt5650-rt5514", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match);
+
+static struct platform_driver mt8173_rt5650_rt5514_driver = {
+ .driver = {
+ .name = "mtk-rt5650-rt5514",
+ .of_match_table = mt8173_rt5650_rt5514_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mt8173_rt5650_rt5514_dev_probe,
+};
+
+module_platform_driver(mt8173_rt5650_rt5514_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650-rt5514");
+
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index 50ba538eccb3..a1ba5df87e1e 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -1,26 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.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 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.
*/
#include <linux/module.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "../codecs/rt5677.h"
+#include "../../codecs/rt5645.h"
+#include "../../codecs/rt5677.h"
#define MCLK_FOR_CODECS 12288000
@@ -40,8 +30,6 @@ static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = {
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
{"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */
- {"Headset Mic", NULL, "micbias1"},
- {"Headset Mic", NULL, "micbias2"},
{"IN1P", NULL, "Headset Mic"},
{"IN1N", NULL, "Headset Mic"},
{"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */
@@ -54,15 +42,25 @@ static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = {
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
+static struct snd_soc_jack_pin mt8173_rt5650_rt5676_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
int i, ret;
- for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
/* pll from mclk 12.288M */
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
params_rate(params) * 512);
@@ -79,7 +77,7 @@ static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
+static const struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
.hw_params = mt8173_rt5650_rt5676_hw_params,
};
@@ -88,99 +86,137 @@ static struct snd_soc_jack mt8173_rt5650_rt5676_jack;
static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
- struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
- struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_component *component_sub = snd_soc_rtd_to_codec(runtime, 1)->component;
int ret;
- rt5645_sel_asrc_clk_src(codec,
+ rt5645_sel_asrc_clk_src(component,
RT5645_DA_STEREO_FILTER |
RT5645_AD_STEREO_FILTER,
RT5645_CLK_SEL_I2S1_ASRC);
- rt5677_sel_asrc_clk_src(codec_sub,
+ rt5677_sel_asrc_clk_src(component_sub,
RT5677_DA_STEREO_FILTER |
RT5677_AD_STEREO1_FILTER,
RT5677_CLK_SEL_I2S1_ASRC);
- rt5677_sel_asrc_clk_src(codec_sub,
+ rt5677_sel_asrc_clk_src(component_sub,
RT5677_AD_STEREO2_FILTER |
RT5677_I2S2_SOURCE,
RT5677_CLK_SEL_I2S2_ASRC);
/* enable jack detection */
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &mt8173_rt5650_rt5676_jack, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &mt8173_rt5650_rt5676_jack,
+ mt8173_rt5650_rt5676_jack_pins,
+ ARRAY_SIZE(mt8173_rt5650_rt5676_jack_pins));
if (ret) {
dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
return ret;
}
- return rt5645_set_jack_detect(codec,
+ return rt5645_set_jack_detect(component,
&mt8173_rt5650_rt5676_jack,
&mt8173_rt5650_rt5676_jack,
&mt8173_rt5650_rt5676_jack);
}
-static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
- {
- .dai_name = "rt5645-aif1",
- },
- {
- .dai_name = "rt5677-aif1",
- },
+
+enum {
+ DAI_LINK_PLAYBACK,
+ DAI_LINK_CAPTURE,
+ DAI_LINK_HDMI,
+ DAI_LINK_CODEC_I2S,
+ DAI_LINK_HDMI_I2S,
+ DAI_LINK_INTERCODEC
};
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi_pcm,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"),
+ COMP_CODEC(NULL, "rt5677-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi_be,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMIO")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(intercodec,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5677-aif2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
/* Front End DAI links */
- {
+ [DAI_LINK_PLAYBACK] = {
.name = "rt5650_rt5676 Playback",
.stream_name = "rt5650_rt5676 Playback",
- .cpu_dai_name = "DL1",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
- .dpcm_playback = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
},
- {
+ [DAI_LINK_CAPTURE] = {
.name = "rt5650_rt5676 Capture",
.stream_name = "rt5650_rt5676 Capture",
- .cpu_dai_name = "VUL",
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
- .dpcm_capture = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+ [DAI_LINK_HDMI] = {
+ .name = "HDMI",
+ .stream_name = "HDMI PCM",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(hdmi_pcm),
},
/* Back End DAI links */
- {
+ [DAI_LINK_CODEC_I2S] = {
.name = "Codec",
- .cpu_dai_name = "I2S",
.no_pcm = 1,
- .codecs = mt8173_rt5650_rt5676_codecs,
- .num_codecs = 2,
.init = mt8173_rt5650_rt5676_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ops = &mt8173_rt5650_rt5676_ops,
.ignore_pmdown_time = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(codec),
+ },
+ [DAI_LINK_HDMI_I2S] = {
+ .name = "HDMI BE",
+ .no_pcm = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(hdmi_be),
},
- { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+ /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+ [DAI_LINK_INTERCODEC] = {
.name = "rt5650_rt5676 intercodec",
.stream_name = "rt5650_rt5676 intercodec",
- .cpu_dai_name = "snd-soc-dummy-dai",
- .platform_name = "snd-soc-dummy",
.no_pcm = 1,
- .codec_dai_name = "rt5677-aif2",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(intercodec),
},
-
};
static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = {
@@ -208,6 +244,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8173_rt5650_rt5676_card;
struct device_node *platform_node;
+ struct snd_soc_dai_link *dai_link;
int i, ret;
platform_node = of_parse_phandle(pdev->dev.of_node,
@@ -217,39 +254,49 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
- for (i = 0; i < card->num_links; i++) {
- if (mt8173_rt5650_rt5676_dais[i].platform_name)
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
continue;
- mt8173_rt5650_rt5676_dais[i].platform_of_node = platform_node;
+ dai_link->platforms->of_node = platform_node;
}
- mt8173_rt5650_rt5676_codecs[0].of_node =
+ mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
- if (!mt8173_rt5650_rt5676_codecs[0].of_node) {
+ if (!mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_node;
}
- mt8173_rt5650_rt5676_codecs[1].of_node =
+ mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
- if (!mt8173_rt5650_rt5676_codecs[1].of_node) {
+ if (!mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_node;
}
- mt8173_rt5650_rt5676_codec_conf[0].of_node =
- mt8173_rt5650_rt5676_codecs[1].of_node;
+ mt8173_rt5650_rt5676_codec_conf[0].dlc.of_node =
+ mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
- mt8173_rt5650_rt5676_dais[3].codec_of_node =
- mt8173_rt5650_rt5676_codecs[1].of_node;
+ mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codecs->of_node =
+ mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
+
+ mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codecs->of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
+ if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codecs->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto put_node;
+ }
card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
- __func__, ret);
+
+put_node:
+ of_node_put(platform_node);
return ret;
}
@@ -263,9 +310,7 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = {
.driver = {
.name = "mtk-rt5650-rt5676",
.of_match_table = mt8173_rt5650_rt5676_dt_match,
-#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
-#endif
},
.probe = mt8173_rt5650_rt5676_dev_probe,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
new file mode 100644
index 000000000000..3d6d7bc05b87
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/rt5645.h"
+
+#define MCLK_FOR_CODECS 12288000
+
+enum mt8173_rt5650_mclk {
+ MT8173_RT5650_MCLK_EXTERNAL = 0,
+ MT8173_RT5650_MCLK_INTERNAL,
+};
+
+struct mt8173_rt5650_platform_data {
+ enum mt8173_rt5650_mclk pll_from;
+ /* 0 = external oscillator; 1 = internal source from mt8173 */
+};
+
+static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
+ .pll_from = MT8173_RT5650_MCLK_EXTERNAL,
+};
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
+ {"Ext Spk", NULL, "SPOL"},
+ {"Ext Spk", NULL, "SPOR"},
+ {"DMIC L1", NULL, "Int Mic"},
+ {"DMIC R1", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_jack_pin mt8173_rt5650_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int mclk_clock;
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ switch (mt8173_rt5650_priv.pll_from) {
+ case MT8173_RT5650_MCLK_EXTERNAL:
+ /* mclk = 12.288M */
+ mclk_clock = MCLK_FOR_CODECS;
+ break;
+ case MT8173_RT5650_MCLK_INTERNAL:
+ /* mclk = sampling rate*256 */
+ mclk_clock = params_rate(params) * 256;
+ break;
+ default:
+ /* mclk = 12.288M */
+ mclk_clock = MCLK_FOR_CODECS;
+ break;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ /* pll from mclk */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
+ params_rate(params) * 512);
+ if (ret)
+ return ret;
+
+ /* sysclk from pll */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt8173_rt5650_ops = {
+ .hw_params = mt8173_rt5650_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_jack, mt8173_rt5650_hdmi_jack;
+
+static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ const char *codec_capture_dai = snd_soc_rtd_to_codec(runtime, 1)->name;
+ int ret;
+
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+
+ if (!strcmp(codec_capture_dai, "rt5645-aif1")) {
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ } else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S2_ASRC);
+ } else {
+ dev_warn(card->dev,
+ "Only one dai codec found in DTS, enabled rt5645 AD filter\n");
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ }
+
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &mt8173_rt5650_jack,
+ mt8173_rt5650_jack_pins,
+ ARRAY_SIZE(mt8173_rt5650_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+ return ret;
+ }
+
+ return rt5645_set_jack_detect(component,
+ &mt8173_rt5650_jack,
+ &mt8173_rt5650_jack,
+ &mt8173_rt5650_jack);
+}
+
+static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
+ &mt8173_rt5650_hdmi_jack);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component,
+ &mt8173_rt5650_hdmi_jack, NULL);
+}
+
+enum {
+ DAI_LINK_PLAYBACK,
+ DAI_LINK_CAPTURE,
+ DAI_LINK_HDMI,
+ DAI_LINK_CODEC_I2S,
+ DAI_LINK_HDMI_I2S,
+};
+
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi_pcm,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"), /* Playback */
+ COMP_CODEC(NULL, "rt5645-aif1")),/* Capture */
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi_be,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMIO")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
+ /* Front End DAI links */
+ [DAI_LINK_PLAYBACK] = {
+ .name = "rt5650 Playback",
+ .stream_name = "rt5650 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
+ },
+ [DAI_LINK_CAPTURE] = {
+ .name = "rt5650 Capture",
+ .stream_name = "rt5650 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+ [DAI_LINK_HDMI] = {
+ .name = "HDMI",
+ .stream_name = "HDMI PCM",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(hdmi_pcm),
+ },
+ /* Back End DAI links */
+ [DAI_LINK_CODEC_I2S] = {
+ .name = "Codec",
+ .no_pcm = 1,
+ .init = mt8173_rt5650_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &mt8173_rt5650_ops,
+ .ignore_pmdown_time = 1,
+ SND_SOC_DAILINK_REG(codec),
+ },
+ [DAI_LINK_HDMI_I2S] = {
+ .name = "HDMI BE",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .init = mt8173_rt5650_hdmi_init,
+ SND_SOC_DAILINK_REG(hdmi_be),
+ },
+};
+
+static struct snd_soc_card mt8173_rt5650_card = {
+ .name = "mtk-rt5650",
+ .owner = THIS_MODULE,
+ .dai_link = mt8173_rt5650_dais,
+ .num_links = ARRAY_SIZE(mt8173_rt5650_dais),
+ .controls = mt8173_rt5650_controls,
+ .num_controls = ARRAY_SIZE(mt8173_rt5650_controls),
+ .dapm_widgets = mt8173_rt5650_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),
+ .dapm_routes = mt8173_rt5650_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),
+};
+
+static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8173_rt5650_card;
+ struct device_node *platform_node;
+ struct device_node *np;
+ const char *codec_capture_dai;
+ struct snd_soc_dai_link *dai_link;
+ int i, ret;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+ if (!mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto put_platform_node;
+ }
+ mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
+ mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node;
+
+ np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
+ if (np) {
+ ret = snd_soc_of_get_dai_name(np, &codec_capture_dai, 0);
+ of_node_put(np);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s codec_capture_dai name fail %d\n",
+ __func__, ret);
+ goto put_platform_node;
+ }
+ mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].dai_name =
+ codec_capture_dai;
+ }
+
+ if (device_property_present(&pdev->dev, "mediatek,mclk")) {
+ ret = device_property_read_u32(&pdev->dev,
+ "mediatek,mclk",
+ &mt8173_rt5650_priv.pll_from);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ }
+ }
+
+ mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+ if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto put_platform_node;
+ }
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+put_platform_node:
+ of_node_put(platform_node);
+ return ret;
+}
+
+static const struct of_device_id mt8173_rt5650_dt_match[] = {
+ { .compatible = "mediatek,mt8173-rt5650", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);
+
+static struct platform_driver mt8173_rt5650_driver = {
+ .driver = {
+ .name = "mtk-rt5650",
+ .of_match_table = mt8173_rt5650_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mt8173_rt5650_dev_probe,
+};
+
+module_platform_driver(mt8173_rt5650_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650");
+
diff --git a/sound/soc/mediatek/mt8183/Makefile b/sound/soc/mediatek/mt8183/Makefile
new file mode 100644
index 000000000000..0d0dcdde00fc
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt8183-afe-y := \
+ mt8183-afe-pcm.o \
+ mt8183-afe-clk.o \
+ mt8183-dai-i2s.o \
+ mt8183-dai-tdm.o \
+ mt8183-dai-pcm.o \
+ mt8183-dai-hostless.o \
+ mt8183-dai-adda.o
+
+obj-$(CONFIG_SND_SOC_MT8183) += snd-soc-mt8183-afe.o
+obj-$(CONFIG_SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A) += mt8183-mt6358-ts3a227-max98357.o
+obj-$(CONFIG_SND_SOC_MT8183_DA7219_MAX98357A) += mt8183-da7219-max98357.o
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-clk.c b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
new file mode 100644
index 000000000000..cc4f8f4d3dab
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8183-afe-clk.c -- Mediatek 8183 afe clock ctrl
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/clk.h>
+
+#include "mt8183-afe-common.h"
+#include "mt8183-afe-clk.h"
+#include "mt8183-reg.h"
+
+enum {
+ CLK_AFE = 0,
+ CLK_TML,
+ CLK_APLL22M,
+ CLK_APLL24M,
+ CLK_APLL1_TUNER,
+ CLK_APLL2_TUNER,
+ CLK_I2S1_BCLK_SW,
+ CLK_I2S2_BCLK_SW,
+ CLK_I2S3_BCLK_SW,
+ CLK_I2S4_BCLK_SW,
+ CLK_INFRA_SYS_AUDIO,
+ CLK_MUX_AUDIO,
+ CLK_MUX_AUDIOINTBUS,
+ CLK_TOP_SYSPLL_D2_D4,
+ /* apll related mux */
+ CLK_TOP_MUX_AUD_1,
+ CLK_TOP_APLL1_CK,
+ CLK_TOP_MUX_AUD_2,
+ CLK_TOP_APLL2_CK,
+ CLK_TOP_MUX_AUD_ENG1,
+ CLK_TOP_APLL1_D8,
+ CLK_TOP_MUX_AUD_ENG2,
+ CLK_TOP_APLL2_D8,
+ CLK_TOP_I2S0_M_SEL,
+ CLK_TOP_I2S1_M_SEL,
+ CLK_TOP_I2S2_M_SEL,
+ CLK_TOP_I2S3_M_SEL,
+ CLK_TOP_I2S4_M_SEL,
+ CLK_TOP_I2S5_M_SEL,
+ CLK_TOP_APLL12_DIV0,
+ CLK_TOP_APLL12_DIV1,
+ CLK_TOP_APLL12_DIV2,
+ CLK_TOP_APLL12_DIV3,
+ CLK_TOP_APLL12_DIV4,
+ CLK_TOP_APLL12_DIVB,
+ CLK_CLK26M,
+ CLK_NUM
+};
+
+static const char *aud_clks[CLK_NUM] = {
+ [CLK_AFE] = "aud_afe_clk",
+ [CLK_TML] = "aud_tml_clk",
+ [CLK_APLL22M] = "aud_apll22m_clk",
+ [CLK_APLL24M] = "aud_apll24m_clk",
+ [CLK_APLL1_TUNER] = "aud_apll1_tuner_clk",
+ [CLK_APLL2_TUNER] = "aud_apll2_tuner_clk",
+ [CLK_I2S1_BCLK_SW] = "aud_i2s1_bclk_sw",
+ [CLK_I2S2_BCLK_SW] = "aud_i2s2_bclk_sw",
+ [CLK_I2S3_BCLK_SW] = "aud_i2s3_bclk_sw",
+ [CLK_I2S4_BCLK_SW] = "aud_i2s4_bclk_sw",
+ [CLK_INFRA_SYS_AUDIO] = "aud_infra_clk",
+ [CLK_MUX_AUDIO] = "top_mux_audio",
+ [CLK_MUX_AUDIOINTBUS] = "top_mux_aud_intbus",
+ [CLK_TOP_SYSPLL_D2_D4] = "top_syspll_d2_d4",
+ [CLK_TOP_MUX_AUD_1] = "top_mux_aud_1",
+ [CLK_TOP_APLL1_CK] = "top_apll1_ck",
+ [CLK_TOP_MUX_AUD_2] = "top_mux_aud_2",
+ [CLK_TOP_APLL2_CK] = "top_apll2_ck",
+ [CLK_TOP_MUX_AUD_ENG1] = "top_mux_aud_eng1",
+ [CLK_TOP_APLL1_D8] = "top_apll1_d8",
+ [CLK_TOP_MUX_AUD_ENG2] = "top_mux_aud_eng2",
+ [CLK_TOP_APLL2_D8] = "top_apll2_d8",
+ [CLK_TOP_I2S0_M_SEL] = "top_i2s0_m_sel",
+ [CLK_TOP_I2S1_M_SEL] = "top_i2s1_m_sel",
+ [CLK_TOP_I2S2_M_SEL] = "top_i2s2_m_sel",
+ [CLK_TOP_I2S3_M_SEL] = "top_i2s3_m_sel",
+ [CLK_TOP_I2S4_M_SEL] = "top_i2s4_m_sel",
+ [CLK_TOP_I2S5_M_SEL] = "top_i2s5_m_sel",
+ [CLK_TOP_APLL12_DIV0] = "top_apll12_div0",
+ [CLK_TOP_APLL12_DIV1] = "top_apll12_div1",
+ [CLK_TOP_APLL12_DIV2] = "top_apll12_div2",
+ [CLK_TOP_APLL12_DIV3] = "top_apll12_div3",
+ [CLK_TOP_APLL12_DIV4] = "top_apll12_div4",
+ [CLK_TOP_APLL12_DIVB] = "top_apll12_divb",
+ [CLK_CLK26M] = "top_clk26m_clk",
+};
+
+int mt8183_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int i;
+
+ afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
+ __func__, aud_clks[i],
+ PTR_ERR(afe_priv->clk[i]));
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ return 0;
+}
+
+int mt8183_afe_enable_clock(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_SYS_AUDIO], ret);
+ goto CLK_INFRA_SYS_AUDIO_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIO], ret);
+ goto CLK_MUX_AUDIO_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIO],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIO],
+ aud_clks[CLK_CLK26M], ret);
+ goto CLK_MUX_AUDIO_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
+ goto CLK_MUX_AUDIO_INTBUS_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIOINTBUS],
+ afe_priv->clk[CLK_TOP_SYSPLL_D2_D4]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS],
+ aud_clks[CLK_TOP_SYSPLL_D2_D4], ret);
+ goto CLK_MUX_AUDIO_INTBUS_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_AFE], ret);
+ goto CLK_AFE_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_I2S1_BCLK_SW]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_I2S1_BCLK_SW], ret);
+ goto CLK_I2S1_BCLK_SW_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_I2S2_BCLK_SW]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_I2S2_BCLK_SW], ret);
+ goto CLK_I2S2_BCLK_SW_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_I2S3_BCLK_SW]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_I2S3_BCLK_SW], ret);
+ goto CLK_I2S3_BCLK_SW_ERR;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_I2S4_BCLK_SW]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_I2S4_BCLK_SW], ret);
+ goto CLK_I2S4_BCLK_SW_ERR;
+ }
+
+ return 0;
+
+CLK_I2S4_BCLK_SW_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S3_BCLK_SW]);
+CLK_I2S3_BCLK_SW_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S2_BCLK_SW]);
+CLK_I2S2_BCLK_SW_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S1_BCLK_SW]);
+CLK_I2S1_BCLK_SW_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
+CLK_AFE_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+CLK_MUX_AUDIO_INTBUS_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
+CLK_MUX_AUDIO_ERR:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+CLK_INFRA_SYS_AUDIO_ERR:
+ return ret;
+}
+
+int mt8183_afe_disable_clock(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S4_BCLK_SW]);
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S3_BCLK_SW]);
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S2_BCLK_SW]);
+ clk_disable_unprepare(afe_priv->clk[CLK_I2S1_BCLK_SW]);
+ clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+
+ return 0;
+}
+
+/* apll */
+static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (enable) {
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1], ret);
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_1;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_TOP_APLL1_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1],
+ aud_clks[CLK_TOP_APLL1_CK], ret);
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_1;
+ }
+
+ /* 180.6336 / 8 = 22.5792MHz */
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret);
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_TOP_APLL1_D8]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
+ aud_clks[CLK_TOP_APLL1_D8], ret);
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG1;
+ }
+ } else {
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ }
+
+ return 0;
+
+ERR_SELECT_CLK_TOP_MUX_AUD_ENG1:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1:
+ERR_SELECT_CLK_TOP_MUX_AUD_1:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_1:
+EXIT:
+ return ret;
+}
+
+static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (enable) {
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2], ret);
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_2;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_TOP_APLL2_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2],
+ aud_clks[CLK_TOP_APLL2_CK], ret);
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_2;
+ }
+
+ /* 196.608 / 8 = 24.576MHz */
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret);
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_TOP_APLL2_D8]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
+ aud_clks[CLK_TOP_APLL2_D8], ret);
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG2;
+ }
+ } else {
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ }
+
+ return 0;
+
+ERR_SELECT_CLK_TOP_MUX_AUD_ENG2:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2:
+ERR_SELECT_CLK_TOP_MUX_AUD_2:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_2:
+EXIT:
+ return ret;
+}
+
+int mt8183_apll1_enable(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* setting for APLL */
+ apll1_mux_setting(afe, true);
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL22M], ret);
+ goto ERR_CLK_APLL22M;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL1_TUNER]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL1_TUNER], ret);
+ goto ERR_CLK_APLL1_TUNER;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ 0x0000FFF7, 0x00000832);
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x1);
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_ON_MASK_SFT,
+ 0x1 << AFE_22M_ON_SFT);
+
+ return 0;
+
+ERR_CLK_APLL1_TUNER:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+ERR_CLK_APLL22M:
+ return ret;
+}
+
+void mt8183_apll1_disable(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_ON_MASK_SFT,
+ 0x0 << AFE_22M_ON_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x0);
+
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]);
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+
+ apll1_mux_setting(afe, false);
+}
+
+int mt8183_apll2_enable(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* setting for APLL */
+ apll2_mux_setting(afe, true);
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL24M], ret);
+ goto ERR_CLK_APLL24M;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL2_TUNER]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL2_TUNER], ret);
+ goto ERR_CLK_APLL2_TUNER;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ 0x0000FFF7, 0x00000634);
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x1);
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_ON_MASK_SFT,
+ 0x1 << AFE_24M_ON_SFT);
+
+ return 0;
+
+ERR_CLK_APLL2_TUNER:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+ERR_CLK_APLL24M:
+ return ret;
+}
+
+void mt8183_apll2_disable(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_ON_MASK_SFT,
+ 0x0 << AFE_24M_ON_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x0);
+
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]);
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+
+ apll2_mux_setting(afe, false);
+}
+
+int mt8183_get_apll_rate(struct mtk_base_afe *afe, int apll)
+{
+ return (apll == MT8183_APLL1) ? 180633600 : 196608000;
+}
+
+int mt8183_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return ((rate % 8000) == 0) ? MT8183_APLL2 : MT8183_APLL1;
+}
+
+int mt8183_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8183_APLL1;
+ else
+ return MT8183_APLL2;
+}
+
+/* mck */
+struct mt8183_mck_div {
+ int m_sel_id;
+ int div_clk_id;
+};
+
+static const struct mt8183_mck_div mck_div[MT8183_MCK_NUM] = {
+ [MT8183_I2S0_MCK] = {
+ .m_sel_id = CLK_TOP_I2S0_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV0,
+ },
+ [MT8183_I2S1_MCK] = {
+ .m_sel_id = CLK_TOP_I2S1_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV1,
+ },
+ [MT8183_I2S2_MCK] = {
+ .m_sel_id = CLK_TOP_I2S2_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV2,
+ },
+ [MT8183_I2S3_MCK] = {
+ .m_sel_id = CLK_TOP_I2S3_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV3,
+ },
+ [MT8183_I2S4_MCK] = {
+ .m_sel_id = CLK_TOP_I2S4_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV4,
+ },
+ [MT8183_I2S4_BCK] = {
+ .m_sel_id = -1,
+ .div_clk_id = CLK_TOP_APLL12_DIVB,
+ },
+ [MT8183_I2S5_MCK] = {
+ .m_sel_id = -1,
+ .div_clk_id = -1,
+ },
+};
+
+int mt8183_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int apll = mt8183_get_apll_by_rate(afe, rate);
+ int apll_clk_id = apll == MT8183_APLL1 ?
+ CLK_TOP_MUX_AUD_1 : CLK_TOP_MUX_AUD_2;
+ int m_sel_id = mck_div[mck_id].m_sel_id;
+ int div_clk_id = mck_div[mck_id].div_clk_id;
+ int ret;
+
+ /* i2s5 mck not support */
+ if (mck_id == MT8183_I2S5_MCK)
+ return 0;
+
+ /* select apll */
+ if (m_sel_id >= 0) {
+ ret = clk_prepare_enable(afe_priv->clk[m_sel_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[m_sel_id], ret);
+ goto ERR_ENABLE_MCLK;
+ }
+ ret = clk_set_parent(afe_priv->clk[m_sel_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[m_sel_id],
+ aud_clks[apll_clk_id], ret);
+ goto ERR_SELECT_MCLK;
+ }
+ }
+
+ /* enable div, set rate */
+ ret = clk_prepare_enable(afe_priv->clk[div_clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[div_clk_id], ret);
+ goto ERR_ENABLE_MCLK_DIV;
+ }
+ ret = clk_set_rate(afe_priv->clk[div_clk_id], rate);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n",
+ __func__, aud_clks[div_clk_id],
+ rate, ret);
+ goto ERR_SET_MCLK_RATE;
+ }
+
+ return 0;
+
+ERR_SET_MCLK_RATE:
+ clk_disable_unprepare(afe_priv->clk[div_clk_id]);
+ERR_ENABLE_MCLK_DIV:
+ERR_SELECT_MCLK:
+ if (m_sel_id >= 0)
+ clk_disable_unprepare(afe_priv->clk[m_sel_id]);
+ERR_ENABLE_MCLK:
+ return ret;
+}
+
+void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int m_sel_id = mck_div[mck_id].m_sel_id;
+ int div_clk_id = mck_div[mck_id].div_clk_id;
+
+ /* i2s5 mck not support */
+ if (mck_id == MT8183_I2S5_MCK)
+ return;
+
+ clk_disable_unprepare(afe_priv->clk[div_clk_id]);
+ if (m_sel_id >= 0)
+ clk_disable_unprepare(afe_priv->clk[m_sel_id]);
+}
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-clk.h b/sound/soc/mediatek/mt8183/mt8183-afe-clk.h
new file mode 100644
index 000000000000..2c510aa80fc7
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-clk.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8183-afe-clk.h -- Mediatek 8183 afe clock ctrl definition
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT8183_AFE_CLK_H_
+#define _MT8183_AFE_CLK_H_
+
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+enum {
+ MT8183_APLL1 = 0,
+ MT8183_APLL2,
+};
+
+struct mtk_base_afe;
+
+int mt8183_init_clock(struct mtk_base_afe *afe);
+int mt8183_afe_enable_clock(struct mtk_base_afe *afe);
+int mt8183_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt8183_apll1_enable(struct mtk_base_afe *afe);
+void mt8183_apll1_disable(struct mtk_base_afe *afe);
+
+int mt8183_apll2_enable(struct mtk_base_afe *afe);
+void mt8183_apll2_disable(struct mtk_base_afe *afe);
+
+int mt8183_get_apll_rate(struct mtk_base_afe *afe, int apll);
+int mt8183_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8183_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
+
+int mt8183_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
+void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id);
+#endif
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-common.h b/sound/soc/mediatek/mt8183/mt8183-afe-common.h
new file mode 100644
index 000000000000..40ab48c1566c
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-common.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8183-afe-common.h -- Mediatek 8183 audio driver definitions
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT_8183_AFE_COMMON_H_
+#define _MT_8183_AFE_COMMON_H_
+
+#include <sound/soc.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT8183_MEMIF_DL1,
+ MT8183_MEMIF_DL2,
+ MT8183_MEMIF_DL3,
+ MT8183_MEMIF_VUL12,
+ MT8183_MEMIF_VUL2,
+ MT8183_MEMIF_AWB,
+ MT8183_MEMIF_AWB2,
+ MT8183_MEMIF_MOD_DAI,
+ MT8183_MEMIF_HDMI,
+ MT8183_MEMIF_NUM,
+ MT8183_DAI_ADDA = MT8183_MEMIF_NUM,
+ MT8183_DAI_PCM_1,
+ MT8183_DAI_PCM_2,
+ MT8183_DAI_I2S_0,
+ MT8183_DAI_I2S_1,
+ MT8183_DAI_I2S_2,
+ MT8183_DAI_I2S_3,
+ MT8183_DAI_I2S_5,
+ MT8183_DAI_TDM,
+ MT8183_DAI_HOSTLESS_LPBK,
+ MT8183_DAI_HOSTLESS_SPEECH,
+ MT8183_DAI_NUM,
+};
+
+enum {
+ MT8183_IRQ_0,
+ MT8183_IRQ_1,
+ MT8183_IRQ_2,
+ MT8183_IRQ_3,
+ MT8183_IRQ_4,
+ MT8183_IRQ_5,
+ MT8183_IRQ_6,
+ MT8183_IRQ_7,
+ MT8183_IRQ_8, /* hw bundle to TDM */
+ MT8183_IRQ_11,
+ MT8183_IRQ_12,
+ MT8183_IRQ_NUM,
+};
+
+enum {
+ MT8183_MTKAIF_PROTOCOL_1 = 0,
+ MT8183_MTKAIF_PROTOCOL_2,
+ MT8183_MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+/* MCLK */
+enum {
+ MT8183_I2S0_MCK = 0,
+ MT8183_I2S1_MCK,
+ MT8183_I2S2_MCK,
+ MT8183_I2S3_MCK,
+ MT8183_I2S4_MCK,
+ MT8183_I2S4_BCK,
+ MT8183_I2S5_MCK,
+ MT8183_MCK_NUM,
+};
+
+struct clk;
+
+struct mt8183_afe_private {
+ struct clk **clk;
+
+ int pm_runtime_bypass_reg_ctl;
+
+ /* dai */
+ void *dai_priv[MT8183_DAI_NUM];
+
+ /* adda */
+ int mtkaif_protocol;
+ int mtkaif_calibration_ok;
+ int mtkaif_chosen_phase[4];
+ int mtkaif_phase_cycle[4];
+ int mtkaif_calibration_num_phase;
+ int mtkaif_dmic;
+
+ /* mck */
+ int mck_rate[MT8183_MCK_NUM];
+};
+
+unsigned int mt8183_general_rate_transform(struct device *dev,
+ unsigned int rate);
+unsigned int mt8183_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk);
+
+int mt8183_dai_i2s_set_share(struct mtk_base_afe *afe, const char *main_i2s_name,
+ const char *secondary_i2s_name);
+
+/* dai register */
+int mt8183_dai_adda_register(struct mtk_base_afe *afe);
+int mt8183_dai_pcm_register(struct mtk_base_afe *afe);
+int mt8183_dai_i2s_register(struct mtk_base_afe *afe);
+int mt8183_dai_tdm_register(struct mtk_base_afe *afe);
+int mt8183_dai_hostless_register(struct mtk_base_afe *afe);
+#endif
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
new file mode 100644
index 000000000000..a7fef772760a
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -0,0 +1,979 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Mediatek ALSA SoC AFE platform driver for 8183
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "mt8183-afe-common.h"
+#include "mt8183-afe-clk.h"
+#include "mt8183-interconnection.h"
+#include "mt8183-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+enum {
+ MTK_AFE_RATE_8K = 0,
+ MTK_AFE_RATE_11K = 1,
+ MTK_AFE_RATE_12K = 2,
+ MTK_AFE_RATE_384K = 3,
+ MTK_AFE_RATE_16K = 4,
+ MTK_AFE_RATE_22K = 5,
+ MTK_AFE_RATE_24K = 6,
+ MTK_AFE_RATE_130K = 7,
+ MTK_AFE_RATE_32K = 8,
+ MTK_AFE_RATE_44K = 9,
+ MTK_AFE_RATE_48K = 10,
+ MTK_AFE_RATE_88K = 11,
+ MTK_AFE_RATE_96K = 12,
+ MTK_AFE_RATE_176K = 13,
+ MTK_AFE_RATE_192K = 14,
+ MTK_AFE_RATE_260K = 15,
+};
+
+enum {
+ MTK_AFE_DAI_MEMIF_RATE_8K = 0,
+ MTK_AFE_DAI_MEMIF_RATE_16K = 1,
+ MTK_AFE_DAI_MEMIF_RATE_32K = 2,
+ MTK_AFE_DAI_MEMIF_RATE_48K = 3,
+};
+
+enum {
+ MTK_AFE_PCM_RATE_8K = 0,
+ MTK_AFE_PCM_RATE_16K = 1,
+ MTK_AFE_PCM_RATE_32K = 2,
+ MTK_AFE_PCM_RATE_48K = 3,
+};
+
+unsigned int mt8183_general_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_RATE_8K;
+ case 11025:
+ return MTK_AFE_RATE_11K;
+ case 12000:
+ return MTK_AFE_RATE_12K;
+ case 16000:
+ return MTK_AFE_RATE_16K;
+ case 22050:
+ return MTK_AFE_RATE_22K;
+ case 24000:
+ return MTK_AFE_RATE_24K;
+ case 32000:
+ return MTK_AFE_RATE_32K;
+ case 44100:
+ return MTK_AFE_RATE_44K;
+ case 48000:
+ return MTK_AFE_RATE_48K;
+ case 88200:
+ return MTK_AFE_RATE_88K;
+ case 96000:
+ return MTK_AFE_RATE_96K;
+ case 130000:
+ return MTK_AFE_RATE_130K;
+ case 176400:
+ return MTK_AFE_RATE_176K;
+ case 192000:
+ return MTK_AFE_RATE_192K;
+ case 260000:
+ return MTK_AFE_RATE_260K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_RATE_48K);
+ return MTK_AFE_RATE_48K;
+ }
+}
+
+static unsigned int dai_memif_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_DAI_MEMIF_RATE_8K;
+ case 16000:
+ return MTK_AFE_DAI_MEMIF_RATE_16K;
+ case 32000:
+ return MTK_AFE_DAI_MEMIF_RATE_32K;
+ case 48000:
+ return MTK_AFE_DAI_MEMIF_RATE_48K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_DAI_MEMIF_RATE_16K);
+ return MTK_AFE_DAI_MEMIF_RATE_16K;
+ }
+}
+
+unsigned int mt8183_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk)
+{
+ switch (aud_blk) {
+ case MT8183_MEMIF_MOD_DAI:
+ return dai_memif_rate_transform(dev, rate);
+ default:
+ return mt8183_general_rate_transform(dev, rate);
+ }
+}
+
+static const struct snd_pcm_hardware mt8183_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 256,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 8 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+static int mt8183_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+
+ return mt8183_rate_transform(afe->dev, rate, id);
+}
+
+static int mt8183_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return mt8183_general_rate_transform(afe->dev, rate);
+}
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt8183_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = MT8183_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL2",
+ .id = MT8183_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT8183_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT8183_MEMIF_VUL12,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT8183_MEMIF_AWB,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT8183_MEMIF_VUL2,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL4",
+ .id = MT8183_MEMIF_AWB2,
+ .capture = {
+ .stream_name = "UL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL_MONO_1",
+ .id = MT8183_MEMIF_MOD_DAI,
+ .capture = {
+ .stream_name = "UL_MONO_1",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MTK_PCM_DAI_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "HDMI",
+ .id = MT8183_MEMIF_HDMI,
+ .playback = {
+ .stream_name = "HDMI",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+};
+
+/* dma widget & routes*/
+static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN21,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1", AFE_CONN21,
+ I_I2S0_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN22,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2", AFE_CONN21,
+ I_I2S0_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN5,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN5,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN5,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN5,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1", AFE_CONN5,
+ I_I2S2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN6,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN6,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN6,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN6,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2", AFE_CONN6,
+ I_I2S2_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN32,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1", AFE_CONN32,
+ I_I2S2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN33,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2", AFE_CONN33,
+ I_I2S2_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN38,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN39,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_mono_1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN12,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN12,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mt8183_memif_widgets[] = {
+ /* memif */
+ SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL4_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch1_mix, ARRAY_SIZE(memif_ul4_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL4_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch2_mix, ARRAY_SIZE(memif_ul4_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_MONO_1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_mono_1_mix,
+ ARRAY_SIZE(memif_ul_mono_1_mix)),
+};
+
+static const struct snd_soc_dapm_route mt8183_memif_routes[] = {
+ /* capture */
+ {"UL1", NULL, "UL1_CH1"},
+ {"UL1", NULL, "UL1_CH2"},
+ {"UL1_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL1_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL1_CH1", "I2S0_CH1", "I2S0"},
+ {"UL1_CH2", "I2S0_CH2", "I2S0"},
+
+ {"UL2", NULL, "UL2_CH1"},
+ {"UL2", NULL, "UL2_CH2"},
+ {"UL2_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL2_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL2_CH1", "I2S2_CH1", "I2S2"},
+ {"UL2_CH2", "I2S2_CH2", "I2S2"},
+
+ {"UL3", NULL, "UL3_CH1"},
+ {"UL3", NULL, "UL3_CH2"},
+ {"UL3_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL3_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL3_CH1", "I2S2_CH1", "I2S2"},
+ {"UL3_CH2", "I2S2_CH2", "I2S2"},
+
+ {"UL4", NULL, "UL4_CH1"},
+ {"UL4", NULL, "UL4_CH2"},
+ {"UL4_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL4_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL_MONO_1", NULL, "UL_MONO_1_CH1"},
+ {"UL_MONO_1_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_MONO_1_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+};
+
+static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = {
+ .name = "mt8183-afe-pcm-dai",
+};
+
+#define MT8183_MEMIF_BASE(_id, _en_reg, _fs_reg, _mono_reg) \
+ [MT8183_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8183_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = (_fs_reg), \
+ .fs_shift = _id##_MODE_SFT, \
+ .fs_maskbit = _id##_MODE_MASK, \
+ .mono_reg = (_mono_reg), \
+ .mono_shift = _id##_DATA_SFT, \
+ .enable_reg = (_en_reg), \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_MEMIF_HD_MODE, \
+ .hd_align_reg = AFE_MEMIF_HDALIGN, \
+ .hd_shift = _id##_HD_SFT, \
+ .hd_align_mshift = _id##_HD_ALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ }
+
+#define MT8183_MEMIF(_id, _fs_reg, _mono_reg) \
+ MT8183_MEMIF_BASE(_id, AFE_DAC_CON0, _fs_reg, _mono_reg)
+
+/* For convenience with macros: missing register fields */
+#define MOD_DAI_DATA_SFT -1
+#define HDMI_MODE_SFT -1
+#define HDMI_MODE_MASK -1
+#define HDMI_DATA_SFT -1
+#define HDMI_ON_SFT -1
+
+/* For convenience with macros: register name differences */
+#define AFE_VUL12_BASE AFE_VUL_D2_BASE
+#define AFE_VUL12_CUR AFE_VUL_D2_CUR
+#define AFE_VUL12_END AFE_VUL_D2_END
+#define AFE_VUL12_BASE_MSB AFE_VUL_D2_BASE_MSB
+#define AFE_VUL12_CUR_MSB AFE_VUL_D2_CUR_MSB
+#define AFE_VUL12_END_MSB AFE_VUL_D2_END_MSB
+#define AWB2_HD_ALIGN_SFT AWB2_ALIGN_SFT
+#define VUL12_DATA_SFT VUL12_MONO_SFT
+#define AFE_HDMI_BASE AFE_HDMI_OUT_BASE
+#define AFE_HDMI_CUR AFE_HDMI_OUT_CUR
+#define AFE_HDMI_END AFE_HDMI_OUT_END
+#define AFE_HDMI_BASE_MSB AFE_HDMI_OUT_BASE_MSB
+#define AFE_HDMI_CUR_MSB AFE_HDMI_OUT_CUR_MSB
+#define AFE_HDMI_END_MSB AFE_HDMI_OUT_END_MSB
+
+static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
+ MT8183_MEMIF(DL1, AFE_DAC_CON1, AFE_DAC_CON1),
+ MT8183_MEMIF(DL2, AFE_DAC_CON1, AFE_DAC_CON1),
+ MT8183_MEMIF(DL3, AFE_DAC_CON2, AFE_DAC_CON1),
+ MT8183_MEMIF(VUL2, AFE_DAC_CON2, AFE_DAC_CON2),
+ MT8183_MEMIF(AWB, AFE_DAC_CON1, AFE_DAC_CON1),
+ MT8183_MEMIF(AWB2, AFE_DAC_CON2, AFE_DAC_CON2),
+ MT8183_MEMIF(VUL12, AFE_DAC_CON0, AFE_DAC_CON0),
+ MT8183_MEMIF(MOD_DAI, AFE_DAC_CON1, -1),
+ /* enable control in tdm for sync start */
+ MT8183_MEMIF_BASE(HDMI, -1, -1, -1),
+};
+
+#define MT8183_AFE_IRQ_BASE(_id, _fs_reg, _fs_shift, _fs_maskbit) \
+ [MT8183_IRQ_##_id] = { \
+ .id = MT8183_IRQ_##_id, \
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT##_id, \
+ .irq_cnt_shift = 0, \
+ .irq_cnt_maskbit = 0x3ffff, \
+ .irq_fs_reg = _fs_reg, \
+ .irq_fs_shift = _fs_shift, \
+ .irq_fs_maskbit = _fs_maskbit, \
+ .irq_en_reg = AFE_IRQ_MCU_CON0, \
+ .irq_en_shift = IRQ##_id##_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_IRQ_MCU_CLR, \
+ .irq_clr_shift = IRQ##_id##_MCU_CLR_SFT, \
+ }
+
+#define MT8183_AFE_IRQ(_id) \
+ MT8183_AFE_IRQ_BASE(_id, AFE_IRQ_MCU_CON1 + _id / 8 * 4, \
+ IRQ##_id##_MCU_MODE_SFT, \
+ IRQ##_id##_MCU_MODE_MASK)
+
+#define MT8183_AFE_IRQ_NOFS(_id) MT8183_AFE_IRQ_BASE(_id, -1, -1, -1)
+
+static const struct mtk_base_irq_data irq_data[MT8183_IRQ_NUM] = {
+ MT8183_AFE_IRQ(0),
+ MT8183_AFE_IRQ(1),
+ MT8183_AFE_IRQ(2),
+ MT8183_AFE_IRQ(3),
+ MT8183_AFE_IRQ(4),
+ MT8183_AFE_IRQ(5),
+ MT8183_AFE_IRQ(6),
+ MT8183_AFE_IRQ(7),
+ MT8183_AFE_IRQ_NOFS(8),
+ MT8183_AFE_IRQ(11),
+ MT8183_AFE_IRQ(12),
+};
+
+static bool mt8183_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0 ... AUDIO_TOP_CON1: /* reg bit controlled by CCF */
+ case AUDIO_TOP_CON3:
+ case AFE_DL1_CUR ... AFE_DL1_END:
+ case AFE_DL2_CUR ... AFE_DL2_END:
+ case AFE_AWB_END ... AFE_AWB_CUR:
+ case AFE_VUL_END ... AFE_VUL_CUR:
+ case AFE_MEMIF_MON0 ... AFE_MEMIF_MON9:
+ case AFE_ADDA_SRC_DEBUG_MON0 ... AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_UL_SRC_MON0 ... AFE_ADDA_UL_SRC_MON1:
+ case AFE_SIDETONE_MON:
+ case AFE_SIDETONE_CON0 ... AFE_SIDETONE_COEFF:
+ case AFE_BUS_MON0:
+ case AFE_MRGIF_MON0 ... AFE_I2S_MON:
+ case AFE_DAC_MON:
+ case AFE_VUL2_END ... AFE_VUL2_CUR:
+ case AFE_IRQ0_MCU_CNT_MON ... AFE_IRQ6_MCU_CNT_MON:
+ case AFE_MOD_DAI_END ... AFE_MOD_DAI_CUR:
+ case AFE_VUL_D2_END ... AFE_VUL_D2_CUR:
+ case AFE_DL3_CUR ... AFE_DL3_END:
+ case AFE_HDMI_OUT_CON0:
+ case AFE_HDMI_OUT_CUR ... AFE_HDMI_OUT_END:
+ case AFE_IRQ3_MCU_CNT_MON... AFE_IRQ4_MCU_CNT_MON:
+ case AFE_IRQ_MCU_STATUS ... AFE_IRQ_MCU_CLR:
+ case AFE_IRQ_MCU_MON2:
+ case AFE_IRQ1_MCU_CNT_MON ... AFE_IRQ5_MCU_CNT_MON:
+ case AFE_IRQ7_MCU_CNT_MON:
+ case AFE_GAIN1_CUR:
+ case AFE_GAIN2_CUR:
+ case AFE_SRAM_DELSEL_CON0:
+ case AFE_SRAM_DELSEL_CON2 ... AFE_SRAM_DELSEL_CON3:
+ case AFE_ASRC_2CH_CON12 ... AFE_ASRC_2CH_CON13:
+ case PCM_INTF_CON2:
+ case FPGA_CFG0 ... FPGA_CFG1:
+ case FPGA_CFG2 ... FPGA_CFG3:
+ case AUDIO_TOP_DBG_MON0 ... AUDIO_TOP_DBG_MON1:
+ case AFE_IRQ8_MCU_CNT_MON ... AFE_IRQ12_MCU_CNT_MON:
+ case AFE_CBIP_MON0:
+ case AFE_CBIP_SLV_MUX_MON0 ... AFE_CBIP_SLV_DECODER_MON0:
+ case AFE_ADDA6_SRC_DEBUG_MON0:
+ case AFE_ADD6A_UL_SRC_MON0... AFE_ADDA6_UL_SRC_MON1:
+ case AFE_DL1_CUR_MSB:
+ case AFE_DL2_CUR_MSB:
+ case AFE_AWB_CUR_MSB:
+ case AFE_VUL_CUR_MSB:
+ case AFE_VUL2_CUR_MSB:
+ case AFE_MOD_DAI_CUR_MSB:
+ case AFE_VUL_D2_CUR_MSB:
+ case AFE_DL3_CUR_MSB:
+ case AFE_HDMI_OUT_CUR_MSB:
+ case AFE_AWB2_END ... AFE_AWB2_CUR:
+ case AFE_AWB2_CUR_MSB:
+ case AFE_ADDA_DL_SDM_FIFO_MON ... AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_CONNSYS_I2S_MON ... AFE_ASRC_2CH_CON0:
+ case AFE_ASRC_2CH_CON2 ... AFE_ASRC_2CH_CON5:
+ case AFE_ASRC_2CH_CON7 ... AFE_ASRC_2CH_CON8:
+ case AFE_MEMIF_MON12 ... AFE_MEMIF_MON24:
+ case AFE_ADDA_MTKAIF_MON0 ... AFE_ADDA_MTKAIF_MON1:
+ case AFE_AUD_PAD_TOP:
+ case AFE_GENERAL1_ASRC_2CH_CON0:
+ case AFE_GENERAL1_ASRC_2CH_CON2 ... AFE_GENERAL1_ASRC_2CH_CON5:
+ case AFE_GENERAL1_ASRC_2CH_CON7 ... AFE_GENERAL1_ASRC_2CH_CON8:
+ case AFE_GENERAL1_ASRC_2CH_CON12 ... AFE_GENERAL1_ASRC_2CH_CON13:
+ case AFE_GENERAL2_ASRC_2CH_CON0:
+ case AFE_GENERAL2_ASRC_2CH_CON2 ... AFE_GENERAL2_ASRC_2CH_CON5:
+ case AFE_GENERAL2_ASRC_2CH_CON7 ... AFE_GENERAL2_ASRC_2CH_CON8:
+ case AFE_GENERAL2_ASRC_2CH_CON12 ... AFE_GENERAL2_ASRC_2CH_CON13:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8183_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .volatile_reg = mt8183_is_volatile_reg,
+
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = AFE_MAX_REGISTER,
+
+ .cache_type = REGCACHE_FLAT,
+};
+
+static irqreturn_t mt8183_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ unsigned int status;
+ unsigned int status_mcu;
+ unsigned int mcu_en;
+ int ret;
+ int i;
+ irqreturn_t irq_ret = IRQ_HANDLED;
+
+ /* get irq that is sent to MCU */
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ /* only care IRQ which is sent to MCU */
+ status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
+
+ if (ret || status_mcu == 0) {
+ dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n",
+ __func__, ret, status, mcu_en);
+
+ irq_ret = IRQ_NONE;
+ goto err_irq;
+ }
+
+ for (i = 0; i < MT8183_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (status_mcu & (1 << irq->irq_data->irq_en_shift))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap,
+ AFE_IRQ_MCU_CLR,
+ status_mcu);
+
+ return irq_ret;
+}
+
+static int mt8183_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ unsigned int value;
+ int ret;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, AFE_ON_MASK_SFT, 0x0);
+
+ ret = regmap_read_poll_timeout(afe->regmap,
+ AFE_DAC_MON,
+ value,
+ (value & AFE_ON_RETM_MASK_SFT) == 0,
+ 20,
+ 1 * 1000 * 1000);
+ if (ret)
+ dev_warn(afe->dev, "%s(), ret %d\n", __func__, ret);
+
+ /* make sure all irq status are cleared, twice intended */
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff);
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff);
+
+ /* cache only */
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ return mt8183_afe_disable_clock(afe);
+}
+
+static int mt8183_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8183_afe_enable_clock(afe);
+ if (ret)
+ return ret;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ /* enable audio sys DCM for power saving */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, 0x1 << 29, 0x1 << 29);
+
+ /* force cpu use 8_24 format when writing 32bit data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
+ CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
+
+ /* set all output port to 24bit */
+ regmap_write(afe->regmap, AFE_CONN_24BIT, 0xffffffff);
+ regmap_write(afe->regmap, AFE_CONN_24BIT_1, 0xffffffff);
+
+ /* enable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+
+skip_regmap:
+ return 0;
+}
+
+static int mt8183_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8183_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8183_memif_dai_driver);
+
+ dai->dapm_widgets = mt8183_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8183_memif_widgets);
+ dai->dapm_routes = mt8183_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8183_memif_routes);
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8183_dai_adda_register,
+ mt8183_dai_i2s_register,
+ mt8183_dai_pcm_register,
+ mt8183_dai_tdm_register,
+ mt8183_dai_hostless_register,
+ mt8183_dai_memif_register,
+};
+
+static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8183_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ int i, irq_id, ret;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
+
+ /* initial audio related clock */
+ ret = mt8183_init_clock(afe);
+ if (ret) {
+ dev_err(dev, "init clock error\n");
+ return ret;
+ }
+
+ pm_runtime_enable(dev);
+
+ /* regmap init */
+ afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(afe->regmap)) {
+ dev_err(dev, "could not get regmap from parent\n");
+ ret = PTR_ERR(afe->regmap);
+ goto err_pm_disable;
+ }
+ ret = regmap_attach_dev(dev, afe->regmap, &mt8183_afe_regmap_config);
+ if (ret) {
+ dev_warn(dev, "regmap_attach_dev fail, ret %d\n", ret);
+ goto err_pm_disable;
+ }
+
+ rstc = devm_reset_control_get(dev, "audiosys");
+ if (IS_ERR(rstc)) {
+ ret = PTR_ERR(rstc);
+ dev_err(dev, "could not get audiosys reset:%d\n", ret);
+ goto err_pm_disable;
+ }
+
+ ret = reset_control_reset(rstc);
+ if (ret) {
+ dev_err(dev, "failed to trigger audio reset:%d\n", ret);
+ goto err_pm_disable;
+ }
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+ pm_runtime_get_sync(dev);
+
+ ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config);
+ if (ret) {
+ dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret);
+ goto err_pm_disable;
+ }
+
+ pm_runtime_put_sync(dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ /* init memif */
+ afe->memif_size = MT8183_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif) {
+ ret = -ENOMEM;
+ goto err_pm_disable;
+ }
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = -1;
+ }
+
+ afe->memif[MT8183_MEMIF_HDMI].irq_usage = MT8183_IRQ_8;
+ afe->memif[MT8183_MEMIF_HDMI].const_irq = 1;
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* init memif */
+ /* irq initialize */
+ afe->irqs_size = MT8183_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs) {
+ ret = -ENOMEM;
+ goto err_pm_disable;
+ }
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0) {
+ ret = irq_id;
+ goto err_pm_disable;
+ }
+
+ ret = devm_request_irq(dev, irq_id, mt8183_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret) {
+ dev_err(dev, "could not request_irq for asys-isr\n");
+ goto err_pm_disable;
+ }
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret) {
+ dev_warn(dev, "dai register i %d fail, ret %d\n",
+ i, ret);
+ goto err_pm_disable;
+ }
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret) {
+ dev_warn(dev, "mtk_afe_combine_sub_dai fail, ret %d\n", ret);
+ goto err_pm_disable;
+ }
+
+ afe->mtk_afe_hardware = &mt8183_afe_hardware;
+ afe->memif_fs = mt8183_memif_fs;
+ afe->irq_fs = mt8183_irq_fs;
+
+ afe->runtime_resume = mt8183_afe_runtime_resume;
+ afe->runtime_suspend = mt8183_afe_runtime_suspend;
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_pm_disable;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &mt8183_afe_pcm_dai_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_dai_component\n");
+ goto err_pm_disable;
+ }
+
+ return ret;
+
+err_pm_disable:
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void mt8183_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt8183_afe_runtime_suspend(dev);
+}
+
+static const struct of_device_id mt8183_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8183-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8183_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8183_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8183_afe_runtime_suspend,
+ mt8183_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8183_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8183-audio",
+ .of_match_table = mt8183_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8183_afe_pm_ops),
+ },
+ .probe = mt8183_afe_pcm_dev_probe,
+ .remove = mt8183_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8183_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8183");
+MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
new file mode 100644
index 000000000000..983f3b91119a
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -0,0 +1,887 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8183-da7219-max98357.c
+// -- MT8183-DA7219-MAX98357 ALSA SoC machine driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: Shunli Wang <shunli.wang@mediatek.com>
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../../codecs/da7219.h"
+#include "../../codecs/rt1015.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "mt8183-afe-common.h"
+
+#define DA7219_CODEC_DAI "da7219-hifi"
+#define DA7219_DEV_NAME "da7219.5-001a"
+#define RT1015_CODEC_DAI "rt1015-aif"
+#define RT1015_DEV0_NAME "rt1015.6-0028"
+#define RT1015_DEV1_NAME "rt1015.6-0029"
+
+struct mt8183_da7219_max98357_priv {
+ struct snd_soc_jack headset_jack, hdmi_jack;
+};
+
+static struct snd_soc_jack_pin mt8183_da7219_max98357_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+
+ return snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0),
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_i2s_ops = {
+ .hw_params = mt8183_mt6358_i2s_hw_params,
+};
+
+static int mt8183_da7219_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ unsigned int freq;
+ int ret = 0, j;
+
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0,
+ mclk_fs, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ dev_err(rtd->dev, "failed to set cpu dai sysclk\n");
+
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ if (!strcmp(codec_dai->component->name, DA7219_DEV_NAME)) {
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ DA7219_CLKSRC_MCLK,
+ mclk_fs,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "failed to set sysclk\n");
+
+ if ((rate % 8000) == 0)
+ freq = DA7219_PLL_FREQ_OUT_98304;
+ else
+ freq = DA7219_PLL_FREQ_OUT_90316;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7219_SYSCLK_PLL_SRM,
+ 0, freq);
+ if (ret)
+ dev_err(rtd->dev, "failed to start PLL: %d\n",
+ ret);
+ }
+ }
+
+ return ret;
+}
+
+static int mt8183_da7219_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0, j;
+
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ if (!strcmp(codec_dai->component->name, DA7219_DEV_NAME)) {
+ ret = snd_soc_dai_set_pll(codec_dai,
+ 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to stop PLL: %d\n",
+ ret);
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops mt8183_da7219_i2s_ops = {
+ .hw_params = mt8183_da7219_i2s_hw_params,
+ .hw_free = mt8183_da7219_hw_free,
+};
+
+static int
+mt8183_da7219_rt1015_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0, i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!strcmp(codec_dai->component->name, RT1015_DEV0_NAME) ||
+ !strcmp(codec_dai->component->name, RT1015_DEV1_NAME)) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT1015_PLL_S_BCLK,
+ rate * 64, rate * 256);
+ if (ret) {
+ dev_err(rtd->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT1015_SCLK_S_PLL,
+ rate * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(rtd->dev, "failed to set sysclk\n");
+ return ret;
+ }
+ }
+ }
+
+ return mt8183_da7219_i2s_hw_params(substream, params);
+}
+
+static const struct snd_soc_ops mt8183_da7219_rt1015_i2s_ops = {
+ .hw_params = mt8183_da7219_rt1015_i2s_hw_params,
+ .hw_free = mt8183_da7219_hw_free,
+};
+
+static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to S32_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+
+ return 0;
+}
+
+static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to S24_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int
+mt8183_da7219_max98357_startup(
+ struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 48000,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const unsigned int channels[] = {
+ 2,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8183_da7219_max98357_ops = {
+ .startup = mt8183_da7219_max98357_startup,
+};
+
+static int
+mt8183_da7219_max98357_bt_sco_startup(
+ struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 8000, 16000
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const unsigned int channels[] = {
+ 1,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ runtime->hw.channels_max = 1;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8183_da7219_max98357_bt_sco_ops = {
+ .startup = mt8183_da7219_max98357_bt_sco_startup,
+};
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_mono,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_MONO_1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback_hdmi,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(primary_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6358-sound", "mt6358-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm2,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s2,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(DA7219_DEV_NAME, DA7219_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3_max98357a,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("max98357a", "HiFi"),
+ COMP_CODEC(DA7219_DEV_NAME, DA7219_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3_rt1015,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1015_DEV0_NAME, RT1015_CODEC_DAI),
+ COMP_CODEC(RT1015_DEV1_NAME, RT1015_CODEC_DAI),
+ COMP_CODEC(DA7219_DEV_NAME, DA7219_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3_rt1015p,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("rt1015p", "HiFi"),
+ COMP_CODEC(DA7219_DEV_NAME, DA7219_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s5,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S5")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(tdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("TDM")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mt8183_da7219_max98357_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
+ &priv->hdmi_jack);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component,
+ &priv->hdmi_jack, NULL);
+}
+
+static int mt8183_bt_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ int ret;
+
+ ret = mt8183_dai_i2s_set_share(afe, "I2S5", "I2S0");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int mt8183_da7219_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ int ret;
+
+ ret = mt8183_dai_i2s_set_share(afe, "I2S2", "I2S3");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_dai_link mt8183_da7219_dai_links[] = {
+ /* FE */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mt8183_da7219_max98357_ops,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mt8183_da7219_max98357_bt_sco_ops,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mt8183_da7219_max98357_bt_sco_ops,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mt8183_da7219_max98357_ops,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ {
+ .name = "Capture_Mono_1",
+ .stream_name = "Capture_Mono_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_mono),
+ },
+ {
+ .name = "Playback_HDMI",
+ .stream_name = "Playback_HDMI",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_hdmi),
+ },
+ /* BE */
+ {
+ .name = "Primary Codec",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(primary_codec),
+ },
+ {
+ .name = "PCM 1",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+ {
+ .name = "PCM 2",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm2),
+ },
+ {
+ .name = "I2S0",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ SND_SOC_DAILINK_REG(i2s0),
+ },
+ {
+ .name = "I2S1",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ SND_SOC_DAILINK_REG(i2s1),
+ },
+ {
+ .name = "I2S2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_da7219_i2s_ops,
+ .init = &mt8183_da7219_init,
+ SND_SOC_DAILINK_REG(i2s2),
+ },
+ {
+ .name = "I2S3",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "I2S5",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ .init = &mt8183_bt_init,
+ SND_SOC_DAILINK_REG(i2s5),
+ },
+ {
+ .name = "TDM",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ignore = 1,
+ .init = mt8183_da7219_max98357_hdmi_init,
+ SND_SOC_DAILINK_REG(tdm),
+ },
+};
+
+static int
+mt8183_da7219_max98357_headset_init(struct snd_soc_component *component)
+{
+ int ret;
+ struct mt8183_da7219_max98357_priv *priv =
+ snd_soc_card_get_drvdata(component->card);
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new_pins(component->card,
+ "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_LINEOUT,
+ &priv->headset_jack,
+ mt8183_da7219_max98357_jack_pins,
+ ARRAY_SIZE(mt8183_da7219_max98357_jack_pins));
+ if (ret)
+ return ret;
+
+ snd_jack_set_key(
+ priv->headset_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(
+ priv->headset_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(
+ priv->headset_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(
+ priv->headset_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ snd_soc_component_set_jack(component, &priv->headset_jack, NULL);
+
+ return 0;
+}
+
+static struct snd_soc_aux_dev mt8183_da7219_max98357_headset_dev = {
+ .dlc = COMP_EMPTY(),
+ .init = mt8183_da7219_max98357_headset_init,
+};
+
+static struct snd_soc_codec_conf mt6358_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("mt6358-sound"),
+ .name_prefix = "Mt6358",
+ },
+};
+
+static const struct snd_kcontrol_new mt8183_da7219_max98357_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static const
+struct snd_soc_dapm_widget mt8183_da7219_max98357_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_SPK("Line Out", NULL),
+ SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL",
+ "aud_tdm_out_on", "aud_tdm_out_off"),
+};
+
+static const struct snd_soc_dapm_route mt8183_da7219_max98357_dapm_routes[] = {
+ {"Speakers", NULL, "Speaker"},
+ {"I2S Playback", NULL, "TDM_OUT_PINCTRL"},
+};
+
+static struct snd_soc_card mt8183_da7219_max98357_card = {
+ .name = "mt8183_da7219_max98357",
+ .owner = THIS_MODULE,
+ .controls = mt8183_da7219_max98357_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_da7219_max98357_snd_controls),
+ .dapm_widgets = mt8183_da7219_max98357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_max98357_dapm_widgets),
+ .dapm_routes = mt8183_da7219_max98357_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_max98357_dapm_routes),
+ .dai_link = mt8183_da7219_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_da7219_dai_links),
+ .aux_dev = &mt8183_da7219_max98357_headset_dev,
+ .num_aux_devs = 1,
+ .codec_conf = mt6358_codec_conf,
+ .num_configs = ARRAY_SIZE(mt6358_codec_conf),
+};
+
+static struct snd_soc_codec_conf mt8183_da7219_rt1015_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("mt6358-sound"),
+ .name_prefix = "Mt6358",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static const struct snd_kcontrol_new mt8183_da7219_rt1015_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static const
+struct snd_soc_dapm_widget mt8183_da7219_rt1015_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL",
+ "aud_tdm_out_on", "aud_tdm_out_off"),
+};
+
+static const struct snd_soc_dapm_route mt8183_da7219_rt1015_dapm_routes[] = {
+ {"Left Spk", NULL, "Left SPO"},
+ {"Right Spk", NULL, "Right SPO"},
+ {"I2S Playback", NULL, "TDM_OUT_PINCTRL"},
+};
+
+static struct snd_soc_card mt8183_da7219_rt1015_card = {
+ .name = "mt8183_da7219_rt1015",
+ .owner = THIS_MODULE,
+ .controls = mt8183_da7219_rt1015_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_da7219_rt1015_snd_controls),
+ .dapm_widgets = mt8183_da7219_rt1015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_rt1015_dapm_widgets),
+ .dapm_routes = mt8183_da7219_rt1015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_rt1015_dapm_routes),
+ .dai_link = mt8183_da7219_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_da7219_dai_links),
+ .aux_dev = &mt8183_da7219_max98357_headset_dev,
+ .num_aux_devs = 1,
+ .codec_conf = mt8183_da7219_rt1015_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8183_da7219_rt1015_codec_conf),
+};
+
+static struct snd_soc_card mt8183_da7219_rt1015p_card = {
+ .name = "mt8183_da7219_rt1015p",
+ .owner = THIS_MODULE,
+ .controls = mt8183_da7219_max98357_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_da7219_max98357_snd_controls),
+ .dapm_widgets = mt8183_da7219_max98357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_max98357_dapm_widgets),
+ .dapm_routes = mt8183_da7219_max98357_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_max98357_dapm_routes),
+ .dai_link = mt8183_da7219_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_da7219_dai_links),
+ .aux_dev = &mt8183_da7219_max98357_headset_dev,
+ .num_aux_devs = 1,
+ .codec_conf = mt6358_codec_conf,
+ .num_configs = ARRAY_SIZE(mt6358_codec_conf),
+};
+
+static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct device_node *platform_node, *hdmi_codec;
+ struct snd_soc_dai_link *dai_link;
+ struct mt8183_da7219_max98357_priv *priv;
+ struct pinctrl *pinctrl;
+ int ret, i;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ if (!card) {
+ ret = -EINVAL;
+ goto put_platform_node;
+ }
+
+ card->dev = &pdev->dev;
+
+ hdmi_codec = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,hdmi-codec", 0);
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "I2S3") == 0) {
+ if (card == &mt8183_da7219_max98357_card) {
+ dai_link->be_hw_params_fixup =
+ mt8183_i2s_hw_params_fixup;
+ dai_link->ops = &mt8183_da7219_i2s_ops;
+ dai_link->cpus = i2s3_max98357a_cpus;
+ dai_link->num_cpus =
+ ARRAY_SIZE(i2s3_max98357a_cpus);
+ dai_link->codecs = i2s3_max98357a_codecs;
+ dai_link->num_codecs =
+ ARRAY_SIZE(i2s3_max98357a_codecs);
+ dai_link->platforms = i2s3_max98357a_platforms;
+ dai_link->num_platforms =
+ ARRAY_SIZE(i2s3_max98357a_platforms);
+ } else if (card == &mt8183_da7219_rt1015_card) {
+ dai_link->be_hw_params_fixup =
+ mt8183_rt1015_i2s_hw_params_fixup;
+ dai_link->ops = &mt8183_da7219_rt1015_i2s_ops;
+ dai_link->cpus = i2s3_rt1015_cpus;
+ dai_link->num_cpus =
+ ARRAY_SIZE(i2s3_rt1015_cpus);
+ dai_link->codecs = i2s3_rt1015_codecs;
+ dai_link->num_codecs =
+ ARRAY_SIZE(i2s3_rt1015_codecs);
+ dai_link->platforms = i2s3_rt1015_platforms;
+ dai_link->num_platforms =
+ ARRAY_SIZE(i2s3_rt1015_platforms);
+ } else if (card == &mt8183_da7219_rt1015p_card) {
+ dai_link->be_hw_params_fixup =
+ mt8183_rt1015_i2s_hw_params_fixup;
+ dai_link->ops = &mt8183_da7219_i2s_ops;
+ dai_link->cpus = i2s3_rt1015p_cpus;
+ dai_link->num_cpus =
+ ARRAY_SIZE(i2s3_rt1015p_cpus);
+ dai_link->codecs = i2s3_rt1015p_codecs;
+ dai_link->num_codecs =
+ ARRAY_SIZE(i2s3_rt1015p_codecs);
+ dai_link->platforms = i2s3_rt1015p_platforms;
+ dai_link->num_platforms =
+ ARRAY_SIZE(i2s3_rt1015p_platforms);
+ }
+ }
+
+ if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
+ dai_link->codecs->of_node = hdmi_codec;
+ dai_link->ignore = 0;
+ }
+
+ if (!dai_link->platforms->name)
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ mt8183_da7219_max98357_headset_dev.dlc.of_node =
+ of_parse_phandle(pdev->dev.of_node,
+ "mediatek,headset-codec", 0);
+ if (!mt8183_da7219_max98357_headset_dev.dlc.of_node) {
+ dev_err(&pdev->dev,
+ "Property 'mediatek,headset-codec' missing/invalid\n");
+ ret = -EINVAL;
+ goto put_hdmi_codec;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto put_hdmi_codec;
+ }
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ pinctrl = devm_pinctrl_get_select(&pdev->dev, PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(pinctrl)) {
+ ret = PTR_ERR(pinctrl);
+ dev_err(&pdev->dev, "%s failed to select default state %d\n",
+ __func__, ret);
+ goto put_hdmi_codec;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+
+put_hdmi_codec:
+ of_node_put(hdmi_codec);
+put_platform_node:
+ of_node_put(platform_node);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt8183_da7219_max98357_dt_match[] = {
+ {
+ .compatible = "mediatek,mt8183_da7219_max98357",
+ .data = &mt8183_da7219_max98357_card,
+ },
+ {
+ .compatible = "mediatek,mt8183_da7219_rt1015",
+ .data = &mt8183_da7219_rt1015_card,
+ },
+ {
+ .compatible = "mediatek,mt8183_da7219_rt1015p",
+ .data = &mt8183_da7219_rt1015p_card,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt8183_da7219_max98357_dt_match);
+#endif
+
+static struct platform_driver mt8183_da7219_max98357_driver = {
+ .driver = {
+ .name = "mt8183_da7219",
+#ifdef CONFIG_OF
+ .of_match_table = mt8183_da7219_max98357_dt_match,
+#endif
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mt8183_da7219_max98357_dev_probe,
+};
+
+module_platform_driver(mt8183_da7219_max98357_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8183-DA7219-MAX98357 ALSA SoC machine driver");
+MODULE_AUTHOR("Shunli Wang <shunli.wang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt8183_da7219_max98357 soc card");
diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-adda.c b/sound/soc/mediatek/mt8183/mt8183-dai-adda.c
new file mode 100644
index 000000000000..1f55d9e342ba
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-dai-adda.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI ADDA Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include "mt8183-afe-common.h"
+#include "mt8183-interconnection.h"
+#include "mt8183-reg.h"
+#include "../common/mtk-dai-adda-common.h"
+
+enum {
+ AUDIO_SDM_LEVEL_MUTE = 0,
+ AUDIO_SDM_LEVEL_NORMAL = 0x1d,
+ /* if you change level normal */
+ /* you need to change formula of hp impedance and dc trim too */
+};
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN3, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN3, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN3, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN3,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN3,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN3,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN3,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN4, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN4, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN4, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN4, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN4, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN4, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN4,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN4,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN4,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN4,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN4,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN4,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* update setting to dmic */
+ if (afe_priv->mtkaif_dmic) {
+ /* mtkaif_rxif_data_mode = 1, dmic */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
+ 0x1, 0x1);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
+ 0x0, 0xf << 20);
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ 0x0, 0x1 << 5);
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ 0x0, 0x3 << 14);
+
+ /* turn on dmic, ch1, ch2 */
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ 0x1 << 1, 0x1 << 1);
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ 0x3 << 21, 0x3 << 21);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* mtkaif dmic */
+static const char * const mt8183_adda_off_on_str[] = {
+ "Off", "On"
+};
+
+static const struct soc_enum mt8183_adda_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8183_adda_off_on_str),
+ mt8183_adda_off_on_str),
+};
+
+static int mt8183_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->mtkaif_dmic;
+
+ return 0;
+}
+
+static int mt8183_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ afe_priv->mtkaif_dmic = ucontrol->value.integer.value[0];
+
+ dev_info(afe->dev, "%s(), kcontrol name %s, mtkaif_dmic %d\n",
+ __func__, kcontrol->id.name, afe_priv->mtkaif_dmic);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_adda_controls[] = {
+ SOC_ENUM_EXT("MTKAIF_DMIC", mt8183_adda_enum[0],
+ mt8183_adda_dmic_get, mt8183_adda_dmic_set),
+};
+
+enum {
+ SUPPLY_SEQ_ADDA_AFE_ON,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_UL_ON,
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ /* adda */
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch1_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch2_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("mtkaif_26m_clk"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* playback */
+ {"ADDA_DL_CH1", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH2", "DL1"},
+
+ {"ADDA_DL_CH1", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH2", "DL2"},
+
+ {"ADDA_DL_CH1", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH2", "DL3"},
+
+ {"ADDA Playback", NULL, "ADDA_DL_CH1"},
+ {"ADDA Playback", NULL, "ADDA_DL_CH2"},
+
+ /* adda enable */
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+
+ /* clk */
+ {"ADDA Playback", NULL, "mtkaif_26m_clk"},
+ {"ADDA Playback", NULL, "aud_dac_clk"},
+ {"ADDA Playback", NULL, "aud_dac_predis_clk"},
+
+ {"ADDA Capture", NULL, "mtkaif_26m_clk"},
+ {"ADDA Capture", NULL, "aud_adc_clk"},
+};
+
+static int set_mtkaif_rx(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int delay_data;
+ int delay_cycle;
+
+ switch (afe_priv->mtkaif_protocol) {
+ case MT8183_MTKAIF_PROTOCOL_2_CLK_P2:
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x38);
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x39);
+ /* mtkaif_rxif_clkinv_adc inverse for calibration */
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ 0x80010000);
+
+ if (afe_priv->mtkaif_phase_cycle[0] >=
+ afe_priv->mtkaif_phase_cycle[1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[0] -
+ afe_priv->mtkaif_phase_cycle[1];
+ } else {
+ delay_data = DELAY_DATA_MISO2;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
+ afe_priv->mtkaif_phase_cycle[0];
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_DATA_MASK_SFT,
+ delay_data << MTKAIF_RXIF_DELAY_DATA_SFT);
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT,
+ delay_cycle << MTKAIF_RXIF_DELAY_CYCLE_SFT);
+ break;
+ case MT8183_MTKAIF_PROTOCOL_2:
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ 0x00010000);
+ break;
+ case MT8183_MTKAIF_PROTOCOL_1:
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0x0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, dai->id, substream->stream, rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int dl_src2_con0 = 0;
+ unsigned int dl_src2_con1 = 0;
+
+ /* clean predistortion */
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
+
+ /* set sampling rate */
+ dl_src2_con0 = mtk_adda_dl_rate_transform(afe, rate) << 28;
+
+ /* set output mode */
+ switch (rate) {
+ case 192000:
+ dl_src2_con0 |= (0x1 << 24); /* UP_SAMPLING_RATE_X2 */
+ dl_src2_con0 |= 1 << 14;
+ break;
+ case 96000:
+ dl_src2_con0 |= (0x2 << 24); /* UP_SAMPLING_RATE_X4 */
+ dl_src2_con0 |= 1 << 14;
+ break;
+ default:
+ dl_src2_con0 |= (0x3 << 24); /* UP_SAMPLING_RATE_X8 */
+ break;
+ }
+
+ /* turn off mute function */
+ dl_src2_con0 |= (0x03 << 11);
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ if (rate == 8000 || rate == 16000)
+ dl_src2_con0 |= 0x01 << 5;
+
+ /* SA suggest apply -0.3db to audio/speech path */
+ dl_src2_con1 = 0xf74f0000;
+
+ /* turn on down-link gain */
+ dl_src2_con0 = dl_src2_con0 | (0x01 << 1);
+
+ regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, dl_src2_con0);
+ regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, dl_src2_con1);
+
+ /* set sdm gain */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ ATTGAIN_CTL_MASK_SFT,
+ AUDIO_SDM_LEVEL_NORMAL << ATTGAIN_CTL_SFT);
+ } else {
+ unsigned int voice_mode = 0;
+ unsigned int ul_src_con0 = 0; /* default value */
+
+ /* set mtkaif protocol */
+ set_mtkaif_rx(afe);
+
+ /* Using Internal ADC */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_TOP_CON0,
+ 0x1 << 0,
+ 0x0 << 0);
+
+ voice_mode = mtk_adda_ul_rate_transform(afe, rate);
+
+ ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
+
+ /* enable iir */
+ ul_src_con0 |= (1 << UL_IIR_ON_TMP_CTL_SFT) &
+ UL_IIR_ON_TMP_CTL_MASK_SFT;
+
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap, AFE_ADDA_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap, AFE_ADDA_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap, AFE_ADDA_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap, AFE_ADDA_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap, AFE_ADDA_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap, AFE_ADDA_UL_SRC_CON0, ul_src_con0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8183_DAI_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+int mt8183_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->controls = mtk_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-hostless.c b/sound/soc/mediatek/mt8183/mt8183-dai-hostless.c
new file mode 100644
index 000000000000..1667ad352d34
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-dai-hostless.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI Hostless Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include "mt8183-afe-common.h"
+
+/* dai component */
+static const struct snd_soc_dapm_route mtk_dai_hostless_routes[] = {
+ /* Hostless ADDA Loopback */
+ {"ADDA_DL_CH1", "ADDA_UL_CH1", "Hostless LPBK DL"},
+ {"ADDA_DL_CH1", "ADDA_UL_CH2", "Hostless LPBK DL"},
+ {"ADDA_DL_CH2", "ADDA_UL_CH1", "Hostless LPBK DL"},
+ {"ADDA_DL_CH2", "ADDA_UL_CH2", "Hostless LPBK DL"},
+ {"Hostless LPBK UL", NULL, "ADDA Capture"},
+
+ /* Hostless Speech */
+ {"ADDA_DL_CH1", "PCM_1_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_1_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_1_CAP_CH2", "Hostless Speech DL"},
+ {"ADDA_DL_CH1", "PCM_2_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_2_CAP_CH1", "Hostless Speech DL"},
+ {"ADDA_DL_CH2", "PCM_2_CAP_CH2", "Hostless Speech DL"},
+ {"PCM_1_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"},
+ {"PCM_1_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"},
+ {"PCM_2_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"},
+ {"PCM_2_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"},
+
+ {"Hostless Speech UL", NULL, "PCM 1 Capture"},
+ {"Hostless Speech UL", NULL, "PCM 2 Capture"},
+ {"Hostless Speech UL", NULL, "ADDA Capture"},
+};
+
+/* dai ops */
+static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ return snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware);
+}
+
+static const struct snd_soc_dai_ops mtk_dai_hostless_ops = {
+ .startup = mtk_dai_hostless_startup,
+};
+
+/* dai driver */
+#define MTK_HOSTLESS_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_HOSTLESS_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = {
+ {
+ .name = "Hostless LPBK DAI",
+ .id = MT8183_DAI_HOSTLESS_LPBK,
+ .playback = {
+ .stream_name = "Hostless LPBK DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless LPBK UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless Speech DAI",
+ .id = MT8183_DAI_HOSTLESS_SPEECH,
+ .playback = {
+ .stream_name = "Hostless Speech DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless Speech UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+};
+
+int mt8183_dai_hostless_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_hostless_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver);
+
+ dai->dapm_routes = mtk_dai_hostless_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-i2s.c b/sound/soc/mediatek/mt8183/mt8183-dai-i2s.c
new file mode 100644
index 000000000000..3709100a8865
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-dai-i2s.c
@@ -0,0 +1,1058 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8183-afe-clk.h"
+#include "mt8183-afe-common.h"
+#include "mt8183-interconnection.h"
+#include "mt8183-reg.h"
+
+enum {
+ I2S_FMT_EIAJ = 0,
+ I2S_FMT_I2S = 1,
+};
+
+enum {
+ I2S_WLEN_16_BIT = 0,
+ I2S_WLEN_32_BIT = 1,
+};
+
+enum {
+ I2S_HD_NORMAL = 0,
+ I2S_HD_LOW_JITTER = 1,
+};
+
+enum {
+ I2S1_SEL_O28_O29 = 0,
+ I2S1_SEL_O03_O04 = 1,
+};
+
+enum {
+ I2S_IN_PAD_CONNSYS = 0,
+ I2S_IN_PAD_IO_MUX = 1,
+};
+
+struct mtk_afe_i2s_priv {
+ int id;
+ int rate; /* for determine which apll to use */
+ int low_jitter_en;
+
+ int share_i2s_id;
+
+ int mclk_id;
+ int mclk_rate;
+ int mclk_apll;
+
+ int use_eiaj;
+};
+
+static unsigned int get_i2s_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ I2S_WLEN_16_BIT : I2S_WLEN_32_BIT;
+}
+
+#define MTK_AFE_I2S0_KCONTROL_NAME "I2S0_HD_Mux"
+#define MTK_AFE_I2S1_KCONTROL_NAME "I2S1_HD_Mux"
+#define MTK_AFE_I2S2_KCONTROL_NAME "I2S2_HD_Mux"
+#define MTK_AFE_I2S3_KCONTROL_NAME "I2S3_HD_Mux"
+#define MTK_AFE_I2S5_KCONTROL_NAME "I2S5_HD_Mux"
+
+#define I2S0_HD_EN_W_NAME "I2S0_HD_EN"
+#define I2S1_HD_EN_W_NAME "I2S1_HD_EN"
+#define I2S2_HD_EN_W_NAME "I2S2_HD_EN"
+#define I2S3_HD_EN_W_NAME "I2S3_HD_EN"
+#define I2S5_HD_EN_W_NAME "I2S5_HD_EN"
+
+#define I2S0_MCLK_EN_W_NAME "I2S0_MCLK_EN"
+#define I2S1_MCLK_EN_W_NAME "I2S1_MCLK_EN"
+#define I2S2_MCLK_EN_W_NAME "I2S2_MCLK_EN"
+#define I2S3_MCLK_EN_W_NAME "I2S3_MCLK_EN"
+#define I2S5_MCLK_EN_W_NAME "I2S5_MCLK_EN"
+
+static int get_i2s_id_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ if (strncmp(name, "I2S0", 4) == 0)
+ return MT8183_DAI_I2S_0;
+ else if (strncmp(name, "I2S1", 4) == 0)
+ return MT8183_DAI_I2S_1;
+ else if (strncmp(name, "I2S2", 4) == 0)
+ return MT8183_DAI_I2S_2;
+ else if (strncmp(name, "I2S3", 4) == 0)
+ return MT8183_DAI_I2S_3;
+ else if (strncmp(name, "I2S5", 4) == 0)
+ return MT8183_DAI_I2S_5;
+ else
+ return -EINVAL;
+}
+
+static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_i2s_id_by_name(afe, name);
+
+ if (dai_id < 0)
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+/* low jitter control */
+static const char * const mt8183_i2s_hd_str[] = {
+ "Normal", "Low_Jitter"
+};
+
+static const struct soc_enum mt8183_i2s_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8183_i2s_hd_str),
+ mt8183_i2s_hd_str),
+};
+
+static int mt8183_i2s_hd_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mt8183_i2s_hd_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int hd_en, change;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ hd_en = ucontrol->value.integer.value[0];
+
+ i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ change = i2s_priv->low_jitter_en != hd_en;
+ i2s_priv->low_jitter_en = hd_en;
+
+ return change;
+}
+
+static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
+ SOC_ENUM_EXT(MTK_AFE_I2S0_KCONTROL_NAME, mt8183_i2s_enum[0],
+ mt8183_i2s_hd_get, mt8183_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S1_KCONTROL_NAME, mt8183_i2s_enum[0],
+ mt8183_i2s_hd_get, mt8183_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S2_KCONTROL_NAME, mt8183_i2s_enum[0],
+ mt8183_i2s_hd_get, mt8183_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S3_KCONTROL_NAME, mt8183_i2s_enum[0],
+ mt8183_i2s_hd_get, mt8183_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S5_KCONTROL_NAME, mt8183_i2s_enum[0],
+ mt8183_i2s_hd_get, mt8183_i2s_hd_set),
+};
+
+/* dai component */
+/* interconnection */
+static const struct snd_kcontrol_new mtk_i2s3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN0, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN0, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN0, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN0,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN0,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN1,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN1,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN1,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN1,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN1,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN28, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN28, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN28, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN28,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN28,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN28,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN29, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN29, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN29, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN29,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN29,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN29,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN29,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN29,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s5_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN30, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN30, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN30, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN30,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN30,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN30,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s5_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN31, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN31, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN31, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN31,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN31,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN31,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN31,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN31,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_I2S_MCLK_EN,
+ SUPPLY_SEQ_I2S_HD_EN,
+ SUPPLY_SEQ_I2S_EN,
+};
+
+static int mtk_apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8183_apll1_enable(afe);
+ else
+ mt8183_apll2_enable(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8183_apll1_disable(afe);
+ else
+ mt8183_apll2_disable(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8183_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ i2s_priv->mclk_rate = 0;
+ mt8183_mck_disable(afe, i2s_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
+ SND_SOC_DAPM_MIXER("I2S1_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s1_ch1_mix,
+ ARRAY_SIZE(mtk_i2s1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S1_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s1_ch2_mix,
+ ARRAY_SIZE(mtk_i2s1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S3_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s3_ch1_mix,
+ ARRAY_SIZE(mtk_i2s3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S3_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s3_ch2_mix,
+ ARRAY_SIZE(mtk_i2s3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S5_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s5_ch1_mix,
+ ARRAY_SIZE(mtk_i2s5_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S5_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s5_ch2_mix,
+ ARRAY_SIZE(mtk_i2s5_ch2_mix)),
+
+ /* i2s en*/
+ SND_SOC_DAPM_SUPPLY_S("I2S0_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON, I2S_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S1_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON1, I2S_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON2, I2S_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S3_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON3, I2S_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S5_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON4, I2S5_EN_SFT, 0,
+ NULL, 0),
+ /* i2s hd en */
+ SND_SOC_DAPM_SUPPLY_S(I2S0_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON, I2S1_HD_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S1_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON1, I2S2_HD_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S2_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON2, I2S3_HD_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S3_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON3, I2S4_HD_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S5_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON4, I2S5_HD_EN_SFT, 0,
+ NULL, 0),
+
+ /* i2s mclk en */
+ SND_SOC_DAPM_SUPPLY_S(I2S0_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S1_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S2_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S3_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S5_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* apll */
+ SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static int mtk_afe_i2s_share_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ return i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name);
+}
+
+static int mtk_afe_i2s_hd_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ if (get_i2s_id_by_name(afe, sink->name) ==
+ get_i2s_id_by_name(afe, source->name))
+ return i2s_priv->low_jitter_en;
+
+ /* check if share i2s need hd en */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
+ return i2s_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mtk_afe_i2s_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+ int i2s_need_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ /* which apll */
+ cur_apll = mt8183_get_apll_by_name(afe, source->name);
+
+ /* choose APLL from i2s rate */
+ i2s_need_apll = mt8183_get_apll_by_rate(afe, i2s_priv->rate);
+
+ return (i2s_need_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_afe_i2s_mclk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ if (get_i2s_id_by_name(afe, sink->name) ==
+ get_i2s_id_by_name(afe, source->name))
+ return (i2s_priv->mclk_rate > 0) ? 1 : 0;
+
+ /* check if share i2s need mclk */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
+ return (i2s_priv->mclk_rate > 0) ? 1 : 0;
+
+ return 0;
+}
+
+static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ /* which apll */
+ cur_apll = mt8183_get_apll_by_name(afe, source->name);
+
+ return (i2s_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
+ /* i2s0 */
+ {"I2S0", NULL, "I2S0_EN"},
+ {"I2S0", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S0", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S0_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S0_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S0", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S0_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S0_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s1 */
+ {"I2S1_CH1", "DL1_CH1", "DL1"},
+ {"I2S1_CH2", "DL1_CH2", "DL1"},
+
+ {"I2S1_CH1", "DL2_CH1", "DL2"},
+ {"I2S1_CH2", "DL2_CH2", "DL2"},
+
+ {"I2S1_CH1", "DL3_CH1", "DL3"},
+ {"I2S1_CH2", "DL3_CH2", "DL3"},
+
+ {"I2S1", NULL, "I2S1_CH1"},
+ {"I2S1", NULL, "I2S1_CH2"},
+
+ {"I2S1", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S1_EN"},
+ {"I2S1", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S1", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S1_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S1_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S1", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S1_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S1_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s2 */
+ {"I2S2", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S2_EN"},
+ {"I2S2", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S2", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S2_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S2_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S2", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S2_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S2_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s3 */
+ {"I2S3_CH1", "DL1_CH1", "DL1"},
+ {"I2S3_CH2", "DL1_CH2", "DL1"},
+
+ {"I2S3_CH1", "DL2_CH1", "DL2"},
+ {"I2S3_CH2", "DL2_CH2", "DL2"},
+
+ {"I2S3_CH1", "DL3_CH1", "DL3"},
+ {"I2S3_CH2", "DL3_CH2", "DL3"},
+
+ {"I2S3", NULL, "I2S3_CH1"},
+ {"I2S3", NULL, "I2S3_CH2"},
+
+ {"I2S3", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S3_EN"},
+ {"I2S3", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S3", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S3_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S3_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S3", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S3_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S3_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s5 */
+ {"I2S5_CH1", "DL1_CH1", "DL1"},
+ {"I2S5_CH2", "DL1_CH2", "DL1"},
+
+ {"I2S5_CH1", "DL2_CH1", "DL2"},
+ {"I2S5_CH2", "DL2_CH2", "DL2"},
+
+ {"I2S5_CH1", "DL3_CH1", "DL3"},
+ {"I2S5_CH2", "DL3_CH2", "DL3"},
+
+ {"I2S5", NULL, "I2S5_CH1"},
+ {"I2S5", NULL, "I2S5_CH2"},
+
+ {"I2S5", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S5_EN"},
+
+ {"I2S5", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S5_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S5_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S5", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S5_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S5_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+};
+
+/* dai ops */
+static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
+ struct snd_pcm_hw_params *params,
+ int i2s_id)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[i2s_id];
+
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8183_rate_transform(afe->dev,
+ rate, i2s_id);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int i2s_con = 0, fmt_con = I2S_FMT_I2S << I2S_FMT_SFT;
+ int ret = 0;
+
+ if (i2s_priv) {
+ i2s_priv->rate = rate;
+
+ if (i2s_priv->use_eiaj)
+ fmt_con = I2S_FMT_EIAJ << I2S_FMT_SFT;
+ } else {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ }
+
+ switch (i2s_id) {
+ case MT8183_DAI_I2S_0:
+ regmap_update_bits(afe->regmap, AFE_DAC_CON1,
+ I2S_MODE_MASK_SFT, rate_reg << I2S_MODE_SFT);
+ i2s_con = I2S_IN_PAD_IO_MUX << I2SIN_PAD_SEL_SFT;
+ i2s_con |= fmt_con;
+ i2s_con |= get_i2s_wlen(format) << I2S_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8183_DAI_I2S_1:
+ i2s_con = I2S1_SEL_O28_O29 << I2S2_SEL_O03_O04_SFT;
+ i2s_con |= rate_reg << I2S2_OUT_MODE_SFT;
+ i2s_con |= fmt_con;
+ i2s_con |= get_i2s_wlen(format) << I2S2_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8183_DAI_I2S_2:
+ i2s_con = 8 << I2S3_UPDATE_WORD_SFT;
+ i2s_con |= rate_reg << I2S3_OUT_MODE_SFT;
+ i2s_con |= fmt_con;
+ i2s_con |= get_i2s_wlen(format) << I2S3_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8183_DAI_I2S_3:
+ i2s_con = rate_reg << I2S4_OUT_MODE_SFT;
+ i2s_con |= fmt_con;
+ i2s_con |= get_i2s_wlen(format) << I2S4_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON3,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8183_DAI_I2S_5:
+ i2s_con = rate_reg << I2S5_OUT_MODE_SFT;
+ i2s_con |= fmt_con;
+ i2s_con |= get_i2s_wlen(format) << I2S5_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON4,
+ 0xffffeffe, i2s_con);
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, i2s_id);
+ return -EINVAL;
+ }
+
+ /* set share i2s */
+ if (i2s_priv && i2s_priv->share_i2s_id >= 0)
+ ret = mtk_dai_i2s_config(afe, params, i2s_priv->share_i2s_id);
+
+ return ret;
+}
+
+static int mtk_dai_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ return mtk_dai_i2s_config(afe, params, dai->id);
+}
+
+static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[dai->id];
+ int apll;
+ int apll_rate;
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ if (dir != SND_SOC_CLOCK_OUT) {
+ dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
+ return -EINVAL;
+ }
+
+ apll = mt8183_get_apll_by_rate(afe, freq);
+ apll_rate = mt8183_get_apll_rate(afe, apll);
+
+ if (freq > apll_rate) {
+ dev_warn(afe->dev, "%s(), freq > apll rate", __func__);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_warn(afe->dev, "%s(), APLL cannot generate freq Hz",
+ __func__);
+ return -EINVAL;
+ }
+
+ i2s_priv->mclk_rate = freq;
+ i2s_priv->mclk_apll = apll;
+
+ if (i2s_priv->share_i2s_id > 0) {
+ struct mtk_afe_i2s_priv *share_i2s_priv;
+
+ share_i2s_priv = afe_priv->dai_priv[i2s_priv->share_i2s_id];
+ if (!share_i2s_priv) {
+ dev_warn(afe->dev, "%s(), share_i2s_priv == NULL",
+ __func__);
+ return -EINVAL;
+ }
+
+ share_i2s_priv->mclk_rate = i2s_priv->mclk_rate;
+ share_i2s_priv->mclk_apll = i2s_priv->mclk_apll;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ switch (dai->id) {
+ case MT8183_DAI_I2S_0:
+ case MT8183_DAI_I2S_1:
+ case MT8183_DAI_I2S_2:
+ case MT8183_DAI_I2S_3:
+ case MT8183_DAI_I2S_5:
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+ i2s_priv = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2s_priv->use_eiaj = 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2s_priv->use_eiaj = 0;
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), DAI format %d not support\n",
+ __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_i2s_ops = {
+ .hw_params = mtk_dai_i2s_hw_params,
+ .set_sysclk = mtk_dai_i2s_set_sysclk,
+ .set_fmt = mtk_dai_i2s_set_fmt,
+};
+
+/* dai driver */
+#define MTK_I2S_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
+ {
+ .name = "I2S0",
+ .id = MT8183_DAI_I2S_0,
+ .capture = {
+ .stream_name = "I2S0",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S1",
+ .id = MT8183_DAI_I2S_1,
+ .playback = {
+ .stream_name = "I2S1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S2",
+ .id = MT8183_DAI_I2S_2,
+ .capture = {
+ .stream_name = "I2S2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S3",
+ .id = MT8183_DAI_I2S_3,
+ .playback = {
+ .stream_name = "I2S3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S5",
+ .id = MT8183_DAI_I2S_5,
+ .playback = {
+ .stream_name = "I2S5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+};
+
+/* this enum is merely for mtk_afe_i2s_priv declare */
+enum {
+ DAI_I2S0 = 0,
+ DAI_I2S1,
+ DAI_I2S2,
+ DAI_I2S3,
+ DAI_I2S5,
+ DAI_I2S_NUM,
+};
+
+static const struct mtk_afe_i2s_priv mt8183_i2s_priv[DAI_I2S_NUM] = {
+ [DAI_I2S0] = {
+ .id = MT8183_DAI_I2S_0,
+ .mclk_id = MT8183_I2S0_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S1] = {
+ .id = MT8183_DAI_I2S_1,
+ .mclk_id = MT8183_I2S1_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S2] = {
+ .id = MT8183_DAI_I2S_2,
+ .mclk_id = MT8183_I2S2_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S3] = {
+ .id = MT8183_DAI_I2S_3,
+ .mclk_id = MT8183_I2S3_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S5] = {
+ .id = MT8183_DAI_I2S_5,
+ .mclk_id = MT8183_I2S5_MCK,
+ .share_i2s_id = -1,
+ },
+};
+
+/**
+ * mt8183_dai_i2s_set_share() - Set up I2S ports to share a single clock.
+ * @afe: Pointer to &struct mtk_base_afe
+ * @main_i2s_name: The name of the I2S port that will provide the clock
+ * @secondary_i2s_name: The name of the I2S port that will use this clock
+ */
+int mt8183_dai_i2s_set_share(struct mtk_base_afe *afe, const char *main_i2s_name,
+ const char *secondary_i2s_name)
+{
+ struct mtk_afe_i2s_priv *secondary_i2s_priv;
+ int main_i2s_id;
+
+ secondary_i2s_priv = get_i2s_priv_by_name(afe, secondary_i2s_name);
+ if (!secondary_i2s_priv)
+ return -EINVAL;
+
+ main_i2s_id = get_i2s_id_by_name(afe, main_i2s_name);
+ if (main_i2s_id < 0)
+ return main_i2s_id;
+
+ secondary_i2s_priv->share_i2s_id = main_i2s_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8183_dai_i2s_set_share);
+
+static int mt8183_dai_i2s_set_priv(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int i;
+
+ for (i = 0; i < DAI_I2S_NUM; i++) {
+ i2s_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_afe_i2s_priv),
+ GFP_KERNEL);
+ if (!i2s_priv)
+ return -ENOMEM;
+
+ memcpy(i2s_priv, &mt8183_i2s_priv[i],
+ sizeof(struct mtk_afe_i2s_priv));
+
+ afe_priv->dai_priv[mt8183_i2s_priv[i].id] = i2s_priv;
+ }
+
+ return 0;
+}
+
+int mt8183_dai_i2s_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_i2s_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
+
+ dai->controls = mtk_dai_i2s_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls);
+ dai->dapm_widgets = mtk_dai_i2s_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
+ dai->dapm_routes = mtk_dai_i2s_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
+
+ /* set all dai i2s private data */
+ return mt8183_dai_i2s_set_priv(afe);
+}
diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
new file mode 100644
index 000000000000..4e25287fc0e4
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8183-afe-common.h"
+#include "mt8183-interconnection.h"
+#include "mt8183-reg.h"
+
+enum AUD_TX_LCH_RPT {
+ AUD_TX_LCH_RPT_NO_REPEAT = 0,
+ AUD_TX_LCH_RPT_REPEAT = 1
+};
+
+enum AUD_VBT_16K_MODE {
+ AUD_VBT_16K_MODE_DISABLE = 0,
+ AUD_VBT_16K_MODE_ENABLE = 1
+};
+
+enum AUD_EXT_MODEM {
+ AUD_EXT_MODEM_SELECT_INTERNAL = 0,
+ AUD_EXT_MODEM_SELECT_EXTERNAL = 1
+};
+
+enum AUD_PCM_SYNC_TYPE {
+ /* bck sync length = 1 */
+ AUD_PCM_ONE_BCK_CYCLE_SYNC = 0,
+ /* bck sync length = PCM_INTF_CON1[9:13] */
+ AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1
+};
+
+enum AUD_BT_MODE {
+ AUD_BT_MODE_DUAL_MIC_ON_TX = 0,
+ AUD_BT_MODE_SINGLE_MIC_ON_TX = 1
+};
+
+enum AUD_PCM_AFIFO_SRC {
+ /* slave mode & external modem uses different crystal */
+ AUD_PCM_AFIFO_ASRC = 0,
+ /* slave mode & external modem uses the same crystal */
+ AUD_PCM_AFIFO_AFIFO = 1
+};
+
+enum AUD_PCM_CLOCK_SOURCE {
+ AUD_PCM_CLOCK_MASTER_MODE = 0,
+ AUD_PCM_CLOCK_SLAVE_MODE = 1
+};
+
+enum AUD_PCM_WLEN {
+ AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0,
+ AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1
+};
+
+enum AUD_PCM_MODE {
+ AUD_PCM_MODE_PCM_MODE_8K = 0,
+ AUD_PCM_MODE_PCM_MODE_16K = 1,
+ AUD_PCM_MODE_PCM_MODE_32K = 2,
+ AUD_PCM_MODE_PCM_MODE_48K = 3,
+};
+
+enum AUD_PCM_FMT {
+ AUD_PCM_FMT_I2S = 0,
+ AUD_PCM_FMT_EIAJ = 1,
+ AUD_PCM_FMT_PCM_MODE_A = 2,
+ AUD_PCM_FMT_PCM_MODE_B = 3
+};
+
+enum AUD_BCLK_OUT_INV {
+ AUD_BCLK_OUT_INV_NO_INVERSE = 0,
+ AUD_BCLK_OUT_INV_INVERSE = 1
+};
+
+enum AUD_PCM_EN {
+ AUD_PCM_EN_DISABLE = 0,
+ AUD_PCM_EN_ENABLE = 1
+};
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN7,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN7,
+ I_DL2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN8,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN8,
+ I_DL2_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN27,
+ I_DL1_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN17,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN17,
+ I_DL2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN18,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN18,
+ I_DL2_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN24,
+ I_DL1_CH1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch4_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch4_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, PCM_EN_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PCM_2_EN", PCM2_INTF_CON, PCM2_EN_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MD1_TO_AFE"),
+ SND_SOC_DAPM_INPUT("MD2_TO_AFE"),
+ SND_SOC_DAPM_OUTPUT("AFE_TO_MD1"),
+ SND_SOC_DAPM_OUTPUT("AFE_TO_MD2"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH4"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH1"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH2"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH4"},
+
+ {"PCM 1 Playback", NULL, "PCM_1_EN"},
+ {"PCM 2 Playback", NULL, "PCM_2_EN"},
+ {"PCM 1 Capture", NULL, "PCM_1_EN"},
+ {"PCM 2 Capture", NULL, "PCM_2_EN"},
+
+ {"AFE_TO_MD1", NULL, "PCM 2 Playback"},
+ {"AFE_TO_MD2", NULL, "PCM 1 Playback"},
+ {"PCM 2 Capture", NULL, "MD1_TO_AFE"},
+ {"PCM 1 Capture", NULL, "MD2_TO_AFE"},
+
+ {"PCM_1_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_1_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_1_PB_CH4", "DL1_CH1", "DL1"},
+ {"PCM_2_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_2_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_2_PB_CH4", "DL1_CH1", "DL1"},
+};
+
+/* dai ops */
+static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8183_rate_transform(afe->dev, rate, dai->id);
+ unsigned int pcm_con = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d, rate_reg %d, widget active p %d, c %d\n",
+ __func__,
+ dai->id,
+ substream->stream,
+ rate,
+ rate_reg,
+ p->active, c->active);
+
+ if (p->active || c->active)
+ return 0;
+
+ switch (dai->id) {
+ case MT8183_DAI_PCM_1:
+ pcm_con |= AUD_BCLK_OUT_INV_NO_INVERSE << PCM_BCLK_OUT_INV_SFT;
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_EXT_MODEM_SELECT_INTERNAL << PCM_EXT_MODEM_SFT;
+ pcm_con |= 0 << PCM_SYNC_LENGTH_SFT;
+ pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT;
+ pcm_con |= AUD_PCM_CLOCK_SLAVE_MODE << PCM_SLAVE_SFT;
+ pcm_con |= rate_reg << PCM_MODE_SFT;
+ pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM_FMT_SFT;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1,
+ 0xfffffffe, pcm_con);
+ break;
+ case MT8183_DAI_PCM_2:
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM2_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM2_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM2_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM2_AFIFO_SFT;
+ pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM2_WLEN_SFT;
+ pcm_con |= rate_reg << PCM2_MODE_SFT;
+ pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM2_FMT_SFT;
+
+ regmap_update_bits(afe->regmap, PCM2_INTF_CON,
+ 0xfffffffe, pcm_con);
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .hw_params = mtk_dai_pcm_hw_params,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM 1",
+ .id = MT8183_DAI_PCM_1,
+ .playback = {
+ .stream_name = "PCM 1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "PCM 2",
+ .id = MT8183_DAI_PCM_2,
+ .playback = {
+ .stream_name = "PCM 2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int mt8183_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-tdm.c b/sound/soc/mediatek/mt8183/mt8183-dai-tdm.c
new file mode 100644
index 000000000000..0d69cf440407
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-dai-tdm.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI TDM Control
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8183-afe-clk.h"
+#include "mt8183-afe-common.h"
+#include "mt8183-interconnection.h"
+#include "mt8183-reg.h"
+
+struct mtk_afe_tdm_priv {
+ int bck_id;
+ int bck_rate;
+ int tdm_out_mode;
+ int bck_invert;
+ int lck_invert;
+ int mclk_id;
+ int mclk_multiple; /* according to sample rate */
+ int mclk_rate;
+ int mclk_apll;
+};
+
+enum {
+ TDM_OUT_I2S = 0,
+ TDM_OUT_TDM = 1,
+};
+
+enum {
+ TDM_BCK_NON_INV = 0,
+ TDM_BCK_INV = 1,
+};
+
+enum {
+ TDM_LCK_NON_INV = 0,
+ TDM_LCK_INV = 1,
+};
+
+enum {
+ TDM_WLEN_16_BIT = 1,
+ TDM_WLEN_32_BIT = 2,
+};
+
+enum {
+ TDM_CHANNEL_BCK_16 = 0,
+ TDM_CHANNEL_BCK_24 = 1,
+ TDM_CHANNEL_BCK_32 = 2,
+};
+
+enum {
+ TDM_CHANNEL_NUM_2 = 0,
+ TDM_CHANNEL_NUM_4 = 1,
+ TDM_CHANNEL_NUM_8 = 2,
+};
+
+enum {
+ TDM_CH_START_O30_O31 = 0,
+ TDM_CH_START_O32_O33,
+ TDM_CH_START_O34_O35,
+ TDM_CH_START_O36_O37,
+ TDM_CH_ZERO,
+};
+
+enum {
+ HDMI_BIT_WIDTH_16_BIT = 0,
+ HDMI_BIT_WIDTH_32_BIT = 1,
+};
+
+static unsigned int get_hdmi_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ HDMI_BIT_WIDTH_16_BIT : HDMI_BIT_WIDTH_32_BIT;
+}
+
+static unsigned int get_tdm_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
+}
+
+static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
+}
+
+static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) - 1;
+}
+
+static unsigned int get_tdm_ch(unsigned int ch)
+{
+ switch (ch) {
+ case 1:
+ case 2:
+ return TDM_CHANNEL_NUM_2;
+ case 3:
+ case 4:
+ return TDM_CHANNEL_NUM_4;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ default:
+ return TDM_CHANNEL_NUM_8;
+ }
+}
+
+static unsigned int get_tdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+ else
+ return 2;
+}
+
+static unsigned int get_tdm_ch_per_sdata(unsigned int mode,
+ unsigned int channels)
+{
+ if (mode == TDM_OUT_TDM)
+ return get_tdm_ch_fixup(channels);
+ else
+ return 2;
+}
+
+/* interconnection */
+enum {
+ HDMI_CONN_CH0 = 0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static const char *const hdmi_conn_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3",
+ "CH4", "CH5", "CH6", "CH7",
+};
+
+static int hdmi_conn_mux_map_value[] = {
+ HDMI_CONN_CH0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_0_SFT,
+ HDMI_O_0_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch0_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_1_SFT,
+ HDMI_O_1_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch1_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_2_SFT,
+ HDMI_O_2_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch2_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_3_SFT,
+ HDMI_O_3_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch3_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_4_SFT,
+ HDMI_O_4_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch4_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_5_SFT,
+ HDMI_O_5_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch5_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_6_SFT,
+ HDMI_O_6_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch6_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_7_SFT,
+ HDMI_O_7_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch7_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_TDM_MCK_EN,
+ SUPPLY_SEQ_TDM_BCK_EN,
+};
+
+static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM];
+
+ dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8183_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8183_mck_disable(afe, tdm_priv->bck_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM];
+
+ dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8183_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tdm_priv->mclk_rate = 0;
+ mt8183_mck_disable(afe, tdm_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
+ SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch0_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch1_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch2_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch3_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch4_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch5_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch6_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch7_mux_control),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_tdm_clk"),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_bck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM];
+ int cur_apll;
+
+ /* which apll */
+ cur_apll = mt8183_get_apll_by_name(afe, source->name);
+
+ return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
+ {"HDMI_CH0_MUX", "CH0", "HDMI"},
+ {"HDMI_CH0_MUX", "CH1", "HDMI"},
+ {"HDMI_CH0_MUX", "CH2", "HDMI"},
+ {"HDMI_CH0_MUX", "CH3", "HDMI"},
+ {"HDMI_CH0_MUX", "CH4", "HDMI"},
+ {"HDMI_CH0_MUX", "CH5", "HDMI"},
+ {"HDMI_CH0_MUX", "CH6", "HDMI"},
+ {"HDMI_CH0_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH1_MUX", "CH0", "HDMI"},
+ {"HDMI_CH1_MUX", "CH1", "HDMI"},
+ {"HDMI_CH1_MUX", "CH2", "HDMI"},
+ {"HDMI_CH1_MUX", "CH3", "HDMI"},
+ {"HDMI_CH1_MUX", "CH4", "HDMI"},
+ {"HDMI_CH1_MUX", "CH5", "HDMI"},
+ {"HDMI_CH1_MUX", "CH6", "HDMI"},
+ {"HDMI_CH1_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH2_MUX", "CH0", "HDMI"},
+ {"HDMI_CH2_MUX", "CH1", "HDMI"},
+ {"HDMI_CH2_MUX", "CH2", "HDMI"},
+ {"HDMI_CH2_MUX", "CH3", "HDMI"},
+ {"HDMI_CH2_MUX", "CH4", "HDMI"},
+ {"HDMI_CH2_MUX", "CH5", "HDMI"},
+ {"HDMI_CH2_MUX", "CH6", "HDMI"},
+ {"HDMI_CH2_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH3_MUX", "CH0", "HDMI"},
+ {"HDMI_CH3_MUX", "CH1", "HDMI"},
+ {"HDMI_CH3_MUX", "CH2", "HDMI"},
+ {"HDMI_CH3_MUX", "CH3", "HDMI"},
+ {"HDMI_CH3_MUX", "CH4", "HDMI"},
+ {"HDMI_CH3_MUX", "CH5", "HDMI"},
+ {"HDMI_CH3_MUX", "CH6", "HDMI"},
+ {"HDMI_CH3_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH4_MUX", "CH0", "HDMI"},
+ {"HDMI_CH4_MUX", "CH1", "HDMI"},
+ {"HDMI_CH4_MUX", "CH2", "HDMI"},
+ {"HDMI_CH4_MUX", "CH3", "HDMI"},
+ {"HDMI_CH4_MUX", "CH4", "HDMI"},
+ {"HDMI_CH4_MUX", "CH5", "HDMI"},
+ {"HDMI_CH4_MUX", "CH6", "HDMI"},
+ {"HDMI_CH4_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH5_MUX", "CH0", "HDMI"},
+ {"HDMI_CH5_MUX", "CH1", "HDMI"},
+ {"HDMI_CH5_MUX", "CH2", "HDMI"},
+ {"HDMI_CH5_MUX", "CH3", "HDMI"},
+ {"HDMI_CH5_MUX", "CH4", "HDMI"},
+ {"HDMI_CH5_MUX", "CH5", "HDMI"},
+ {"HDMI_CH5_MUX", "CH6", "HDMI"},
+ {"HDMI_CH5_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH6_MUX", "CH0", "HDMI"},
+ {"HDMI_CH6_MUX", "CH1", "HDMI"},
+ {"HDMI_CH6_MUX", "CH2", "HDMI"},
+ {"HDMI_CH6_MUX", "CH3", "HDMI"},
+ {"HDMI_CH6_MUX", "CH4", "HDMI"},
+ {"HDMI_CH6_MUX", "CH5", "HDMI"},
+ {"HDMI_CH6_MUX", "CH6", "HDMI"},
+ {"HDMI_CH6_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH7_MUX", "CH0", "HDMI"},
+ {"HDMI_CH7_MUX", "CH1", "HDMI"},
+ {"HDMI_CH7_MUX", "CH2", "HDMI"},
+ {"HDMI_CH7_MUX", "CH3", "HDMI"},
+ {"HDMI_CH7_MUX", "CH4", "HDMI"},
+ {"HDMI_CH7_MUX", "CH5", "HDMI"},
+ {"HDMI_CH7_MUX", "CH6", "HDMI"},
+ {"HDMI_CH7_MUX", "CH7", "HDMI"},
+
+ {"TDM", NULL, "HDMI_CH0_MUX"},
+ {"TDM", NULL, "HDMI_CH1_MUX"},
+ {"TDM", NULL, "HDMI_CH2_MUX"},
+ {"TDM", NULL, "HDMI_CH3_MUX"},
+ {"TDM", NULL, "HDMI_CH4_MUX"},
+ {"TDM", NULL, "HDMI_CH5_MUX"},
+ {"TDM", NULL, "HDMI_CH6_MUX"},
+ {"TDM", NULL, "HDMI_CH7_MUX"},
+
+ {"TDM", NULL, "aud_tdm_clk"},
+ {"TDM", NULL, "TDM_BCK"},
+ {"TDM_BCK", NULL, "TDM_MCK"},
+ {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+};
+
+/* dai ops */
+static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
+ struct mtk_afe_tdm_priv *tdm_priv,
+ int freq)
+{
+ int apll;
+ int apll_rate;
+
+ apll = mt8183_get_apll_by_rate(afe, freq);
+ apll_rate = mt8183_get_apll_rate(afe, apll);
+
+ if (!freq || freq > apll_rate) {
+ dev_warn(afe->dev,
+ "%s(), freq(%d Hz) invalid\n", __func__, freq);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_warn(afe->dev,
+ "%s(), APLL cannot generate %d Hz", __func__, freq);
+ return -EINVAL;
+ }
+
+ tdm_priv->mclk_rate = freq;
+ tdm_priv->mclk_apll = apll;
+
+ return 0;
+}
+
+static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ int tdm_id = dai->id;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[tdm_id];
+ unsigned int tdm_out_mode = tdm_priv->tdm_out_mode;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ unsigned int out_channels_per_sdata =
+ get_tdm_ch_per_sdata(tdm_out_mode, channels);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int tdm_con = 0;
+
+ /* calculate mclk_rate, if not set explicitly */
+ if (!tdm_priv->mclk_rate) {
+ tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
+ mtk_dai_tdm_cal_mclk(afe,
+ tdm_priv,
+ tdm_priv->mclk_rate);
+ }
+
+ /* calculate bck */
+ tdm_priv->bck_rate = rate *
+ out_channels_per_sdata *
+ snd_pcm_format_physical_width(format);
+
+ if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
+ dev_warn(afe->dev, "%s(), bck_rate > mclk_rate rate", __func__);
+
+ if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
+ dev_warn(afe->dev, "%s(), bck cannot generate", __func__);
+
+ dev_info(afe->dev, "%s(), id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
+ __func__,
+ tdm_id, rate, channels, format,
+ tdm_priv->mclk_rate, tdm_priv->bck_rate);
+ dev_info(afe->dev, "%s(), out_channels_per_sdata = %d\n",
+ __func__, out_channels_per_sdata);
+
+ /* set tdm */
+ if (tdm_priv->bck_invert)
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON3,
+ BCK_INVERSE_MASK_SFT,
+ 0x1 << BCK_INVERSE_SFT);
+
+ if (tdm_priv->lck_invert)
+ tdm_con |= 1 << LRCK_INVERSE_SFT;
+
+ if (tdm_priv->tdm_out_mode == TDM_OUT_I2S) {
+ tdm_con |= 1 << DELAY_DATA_SFT;
+ tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
+ } else if (tdm_priv->tdm_out_mode == TDM_OUT_TDM) {
+ tdm_con |= 0 << DELAY_DATA_SFT;
+ tdm_con |= 0 << LRCK_TDM_WIDTH_SFT;
+ }
+
+ tdm_con |= 1 << LEFT_ALIGN_SFT;
+ tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
+ tdm_con |= get_tdm_ch(out_channels_per_sdata) << CHANNEL_NUM_SFT;
+ tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT;
+ regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
+
+ if (out_channels_per_sdata == 2) {
+ switch (channels) {
+ case 1:
+ case 2:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 3:
+ case 4:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 5:
+ case 6:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 7:
+ case 8:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ default:
+ tdm_con = 0;
+ }
+ } else {
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ }
+
+ regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
+
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ AFE_HDMI_OUT_CH_NUM_MASK_SFT,
+ channels << AFE_HDMI_OUT_CH_NUM_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ AFE_HDMI_OUT_BIT_WIDTH_MASK_SFT,
+ get_hdmi_wlen(format) << AFE_HDMI_OUT_BIT_WIDTH_SFT);
+ return 0;
+}
+
+static int mtk_dai_tdm_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* enable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ AFE_HDMI_OUT_ON_MASK_SFT,
+ 0x1 << AFE_HDMI_OUT_ON_SFT);
+ /* enable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1,
+ TDM_EN_MASK_SFT, 0x1 << TDM_EN_SFT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1,
+ TDM_EN_MASK_SFT, 0);
+ /* disable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ AFE_HDMI_OUT_ON_MASK_SFT,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ if (dir != SND_SOC_CLOCK_OUT) {
+ dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
+ return -EINVAL;
+ }
+
+ dev_info(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
+}
+
+static int mtk_dai_tdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ /* DAI mode*/
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ tdm_priv->tdm_out_mode = TDM_OUT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_priv->tdm_out_mode = TDM_OUT_TDM;
+ break;
+ default:
+ tdm_priv->tdm_out_mode = TDM_OUT_I2S;
+ }
+
+ /* DAI clock inversion*/
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ tdm_priv->bck_invert = TDM_BCK_NON_INV;
+ tdm_priv->lck_invert = TDM_LCK_NON_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ tdm_priv->bck_invert = TDM_BCK_NON_INV;
+ tdm_priv->lck_invert = TDM_LCK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ tdm_priv->bck_invert = TDM_BCK_INV;
+ tdm_priv->lck_invert = TDM_LCK_NON_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ default:
+ tdm_priv->bck_invert = TDM_BCK_INV;
+ tdm_priv->lck_invert = TDM_LCK_INV;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
+ .hw_params = mtk_dai_tdm_hw_params,
+ .trigger = mtk_dai_tdm_trigger,
+ .set_sysclk = mtk_dai_tdm_set_sysclk,
+ .set_fmt = mtk_dai_tdm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
+ {
+ .name = "TDM",
+ .id = MT8183_DAI_TDM,
+ .playback = {
+ .stream_name = "TDM",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+};
+
+int mt8183_dai_tdm_register(struct mtk_base_afe *afe)
+{
+ struct mt8183_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv;
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_tdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
+
+ dai->dapm_widgets = mtk_dai_tdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
+ dai->dapm_routes = mtk_dai_tdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
+
+ tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
+ GFP_KERNEL);
+ if (!tdm_priv)
+ return -ENOMEM;
+
+ tdm_priv->mclk_multiple = 128;
+ tdm_priv->bck_id = MT8183_I2S4_BCK;
+ tdm_priv->mclk_id = MT8183_I2S4_MCK;
+
+ afe_priv->dai_priv[MT8183_DAI_TDM] = tdm_priv;
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8183/mt8183-interconnection.h b/sound/soc/mediatek/mt8183/mt8183-interconnection.h
new file mode 100644
index 000000000000..6332f5f3e987
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-interconnection.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Mediatek MT8183 audio driver interconnection definition
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT8183_INTERCONNECTION_H_
+#define _MT8183_INTERCONNECTION_H_
+
+#define I_I2S0_CH1 0
+#define I_I2S0_CH2 1
+#define I_ADDA_UL_CH1 3
+#define I_ADDA_UL_CH2 4
+#define I_DL1_CH1 5
+#define I_DL1_CH2 6
+#define I_DL2_CH1 7
+#define I_DL2_CH2 8
+#define I_PCM_1_CAP_CH1 9
+#define I_GAIN1_OUT_CH1 10
+#define I_GAIN1_OUT_CH2 11
+#define I_GAIN2_OUT_CH1 12
+#define I_GAIN2_OUT_CH2 13
+#define I_PCM_2_CAP_CH1 14
+#define I_PCM_2_CAP_CH2 21
+#define I_PCM_1_CAP_CH2 22
+#define I_DL3_CH1 23
+#define I_DL3_CH2 24
+#define I_I2S2_CH1 25
+#define I_I2S2_CH2 26
+
+#endif
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
new file mode 100644
index 000000000000..0bc1f11e17aa
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -0,0 +1,899 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8183-mt6358.c --
+// MT8183-MT6358-TS3A227-MAX98357 ALSA SoC machine driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: Shunli Wang <shunli.wang@mediatek.com>
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../../codecs/rt1015.h"
+#include "../../codecs/ts3a227e.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "mt8183-afe-common.h"
+
+#define RT1015_CODEC_DAI "rt1015-aif"
+#define RT1015_DEV0_NAME "rt1015.6-0028"
+#define RT1015_DEV1_NAME "rt1015.6-0029"
+
+enum PINCTRL_PIN_STATE {
+ PIN_STATE_DEFAULT = 0,
+ PIN_TDM_OUT_ON,
+ PIN_TDM_OUT_OFF,
+ PIN_WOV,
+ PIN_STATE_MAX
+};
+
+static const char * const mt8183_pin_str[PIN_STATE_MAX] = {
+ "default", "aud_tdm_out_on", "aud_tdm_out_off", "wov",
+};
+
+struct mt8183_mt6358_ts3a227_max98357_priv {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pin_states[PIN_STATE_MAX];
+ struct snd_soc_jack headset_jack, hdmi_jack;
+};
+
+static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+
+ return snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0),
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_i2s_ops = {
+ .hw_params = mt8183_mt6358_i2s_hw_params,
+};
+
+static int
+mt8183_mt6358_rt1015_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai;
+ int ret, i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
+ rate * 64, rate * 256);
+ if (ret < 0) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL,
+ rate * 256, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+ }
+
+ return snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0),
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_rt1015_i2s_ops = {
+ .hw_params = mt8183_mt6358_rt1015_i2s_hw_params,
+};
+
+static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ dev_dbg(rtd->dev, "%s(), fix format to S32_LE\n", __func__);
+
+ /* fix BE i2s format to S32_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+ return 0;
+}
+
+static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ dev_dbg(rtd->dev, "%s(), fix format to S24_LE\n", __func__);
+
+ /* fix BE i2s format to S24_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static int
+mt8183_mt6358_startup(struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 48000,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const unsigned int channels[] = {
+ 2,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8183_mt6358_ops = {
+ .startup = mt8183_mt6358_startup,
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_bt_sco_startup(
+ struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 8000, 16000
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+ };
+ static const unsigned int channels[] = {
+ 1,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+ };
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ runtime->hw.channels_max = 1;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8183_mt6358_ts3a227_max98357_bt_sco_ops = {
+ .startup = mt8183_mt6358_ts3a227_max98357_bt_sco_startup,
+};
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_mono,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_MONO_1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback_hdmi,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(wake_on_voice,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(primary_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6358-sound", "mt6358-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm2,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm-wb")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s2,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3_max98357a,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("max98357a", "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3_rt1015,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1015_DEV0_NAME, RT1015_CODEC_DAI),
+ COMP_CODEC(RT1015_DEV1_NAME, RT1015_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3_rt1015p,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("rt1015p", "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s5,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S5")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm-wb")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(tdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("TDM")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static int mt8183_mt6358_tdm_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ if (IS_ERR(priv->pin_states[PIN_TDM_OUT_ON]))
+ return PTR_ERR(priv->pin_states[PIN_TDM_OUT_ON]);
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_TDM_OUT_ON]);
+ if (ret)
+ dev_err(rtd->card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static void mt8183_mt6358_tdm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ if (IS_ERR(priv->pin_states[PIN_TDM_OUT_OFF]))
+ return;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_TDM_OUT_OFF]);
+ if (ret)
+ dev_err(rtd->card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_tdm_ops = {
+ .startup = mt8183_mt6358_tdm_startup,
+ .shutdown = mt8183_mt6358_tdm_shutdown,
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_wov_startup(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(card);
+
+ return pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_WOV]);
+}
+
+static void
+mt8183_mt6358_ts3a227_max98357_wov_shutdown(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_DEFAULT]);
+ if (ret)
+ dev_err(card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_ts3a227_max98357_wov_ops = {
+ .startup = mt8183_mt6358_ts3a227_max98357_wov_startup,
+ .shutdown = mt8183_mt6358_ts3a227_max98357_wov_shutdown,
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
+ &priv->hdmi_jack);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component,
+ &priv->hdmi_jack, NULL);
+}
+
+static int mt8183_bt_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ int ret;
+
+ ret = mt8183_dai_i2s_set_share(afe, "I2S5", "I2S0");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int mt8183_i2s2_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ int ret;
+
+ ret = mt8183_dai_i2s_set_share(afe, "I2S2", "I2S3");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = {
+ /* FE */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mt8183_mt6358_ops,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mt8183_mt6358_ts3a227_max98357_bt_sco_ops,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mt8183_mt6358_ts3a227_max98357_bt_sco_ops,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mt8183_mt6358_ops,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ {
+ .name = "Capture_Mono_1",
+ .stream_name = "Capture_Mono_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_mono),
+ },
+ {
+ .name = "Playback_HDMI",
+ .stream_name = "Playback_HDMI",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_hdmi),
+ },
+ {
+ .name = "Wake on Voice",
+ .stream_name = "Wake on Voice",
+ .ignore_suspend = 1,
+ .ignore = 1,
+ SND_SOC_DAILINK_REG(wake_on_voice),
+ .ops = &mt8183_mt6358_ts3a227_max98357_wov_ops,
+ },
+
+ /* BE */
+ {
+ .name = "Primary Codec",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(primary_codec),
+ },
+ {
+ .name = "PCM 1",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+ {
+ .name = "PCM 2",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm2),
+ },
+ {
+ .name = "I2S0",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .ops = &mt8183_mt6358_i2s_ops,
+ SND_SOC_DAILINK_REG(i2s0),
+ },
+ {
+ .name = "I2S1",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ SND_SOC_DAILINK_REG(i2s1),
+ },
+ {
+ .name = "I2S2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ .init = &mt8183_i2s2_init,
+ SND_SOC_DAILINK_REG(i2s2),
+ },
+ {
+ .name = "I2S3",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "I2S5",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .ops = &mt8183_mt6358_i2s_ops,
+ .init = &mt8183_bt_init,
+ SND_SOC_DAILINK_REG(i2s5),
+ },
+ {
+ .name = "TDM",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_tdm_ops,
+ .ignore = 1,
+ .init = mt8183_mt6358_ts3a227_max98357_hdmi_init,
+ SND_SOC_DAILINK_REG(tdm),
+ },
+};
+
+static const
+struct snd_kcontrol_new mt8183_mt6358_ts3a227_max98357_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const
+struct snd_soc_dapm_widget mt8183_mt6358_ts3a227_max98357_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static struct snd_soc_jack_pin mt8183_mt6358_ts3a227_max98357_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_card mt8183_mt6358_ts3a227_max98357_card = {
+ .name = "mt8183_mt6358_ts3a227_max98357",
+ .owner = THIS_MODULE,
+ .dai_link = mt8183_mt6358_ts3a227_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links),
+ .controls = mt8183_mt6358_ts3a227_max98357_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls),
+ .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets),
+};
+
+static struct snd_soc_card mt8183_mt6358_ts3a227_max98357b_card = {
+ .name = "mt8183_mt6358_ts3a227_max98357b",
+ .owner = THIS_MODULE,
+ .dai_link = mt8183_mt6358_ts3a227_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links),
+ .controls = mt8183_mt6358_ts3a227_max98357_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls),
+ .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets),
+};
+
+static struct snd_soc_codec_conf mt8183_mt6358_ts3a227_rt1015_amp_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015_card = {
+ .name = "mt8183_mt6358_ts3a227_rt1015",
+ .owner = THIS_MODULE,
+ .dai_link = mt8183_mt6358_ts3a227_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links),
+ .codec_conf = mt8183_mt6358_ts3a227_rt1015_amp_conf,
+ .num_configs = ARRAY_SIZE(mt8183_mt6358_ts3a227_rt1015_amp_conf),
+ .controls = mt8183_mt6358_ts3a227_max98357_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls),
+ .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets),
+};
+
+static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015p_card = {
+ .name = "mt8183_mt6358_ts3a227_rt1015p",
+ .owner = THIS_MODULE,
+ .dai_link = mt8183_mt6358_ts3a227_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links),
+ .controls = mt8183_mt6358_ts3a227_max98357_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls),
+ .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets),
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component)
+{
+ int ret;
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv =
+ snd_soc_card_get_drvdata(component->card);
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new_pins(component->card,
+ "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &priv->headset_jack,
+ mt8183_mt6358_ts3a227_max98357_jack_pins,
+ ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_jack_pins));
+ if (ret)
+ return ret;
+
+ ret = ts3a227e_enable_jack_detect(component, &priv->headset_jack);
+
+ return ret;
+}
+
+static struct snd_soc_aux_dev mt8183_mt6358_ts3a227_max98357_headset_dev = {
+ .dlc = COMP_EMPTY(),
+ .init = mt8183_mt6358_ts3a227_max98357_headset_init,
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct device_node *platform_node, *ec_codec, *hdmi_codec;
+ struct snd_soc_dai_link *dai_link;
+ struct mt8183_mt6358_ts3a227_max98357_priv *priv;
+ int ret, i;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ if (!card) {
+ of_node_put(platform_node);
+ return -EINVAL;
+ }
+ card->dev = &pdev->dev;
+
+ ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0);
+ hdmi_codec = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,hdmi-codec", 0);
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (ec_codec && strcmp(dai_link->name, "Wake on Voice") == 0) {
+ dai_link->cpus[0].name = NULL;
+ dai_link->cpus[0].of_node = ec_codec;
+ dai_link->cpus[0].dai_name = NULL;
+ dai_link->codecs[0].name = NULL;
+ dai_link->codecs[0].of_node = ec_codec;
+ dai_link->codecs[0].dai_name = "Wake on Voice";
+ dai_link->platforms[0].of_node = ec_codec;
+ dai_link->ignore = 0;
+ }
+
+ if (strcmp(dai_link->name, "I2S3") == 0) {
+ if (card == &mt8183_mt6358_ts3a227_max98357_card ||
+ card == &mt8183_mt6358_ts3a227_max98357b_card) {
+ dai_link->be_hw_params_fixup =
+ mt8183_i2s_hw_params_fixup;
+ dai_link->ops = &mt8183_mt6358_i2s_ops;
+ dai_link->cpus = i2s3_max98357a_cpus;
+ dai_link->num_cpus =
+ ARRAY_SIZE(i2s3_max98357a_cpus);
+ dai_link->codecs = i2s3_max98357a_codecs;
+ dai_link->num_codecs =
+ ARRAY_SIZE(i2s3_max98357a_codecs);
+ dai_link->platforms = i2s3_max98357a_platforms;
+ dai_link->num_platforms =
+ ARRAY_SIZE(i2s3_max98357a_platforms);
+ } else if (card == &mt8183_mt6358_ts3a227_rt1015_card) {
+ dai_link->be_hw_params_fixup =
+ mt8183_rt1015_i2s_hw_params_fixup;
+ dai_link->ops = &mt8183_mt6358_rt1015_i2s_ops;
+ dai_link->cpus = i2s3_rt1015_cpus;
+ dai_link->num_cpus =
+ ARRAY_SIZE(i2s3_rt1015_cpus);
+ dai_link->codecs = i2s3_rt1015_codecs;
+ dai_link->num_codecs =
+ ARRAY_SIZE(i2s3_rt1015_codecs);
+ dai_link->platforms = i2s3_rt1015_platforms;
+ dai_link->num_platforms =
+ ARRAY_SIZE(i2s3_rt1015_platforms);
+ } else if (card == &mt8183_mt6358_ts3a227_rt1015p_card) {
+ dai_link->be_hw_params_fixup =
+ mt8183_rt1015_i2s_hw_params_fixup;
+ dai_link->ops = &mt8183_mt6358_i2s_ops;
+ dai_link->cpus = i2s3_rt1015p_cpus;
+ dai_link->num_cpus =
+ ARRAY_SIZE(i2s3_rt1015p_cpus);
+ dai_link->codecs = i2s3_rt1015p_codecs;
+ dai_link->num_codecs =
+ ARRAY_SIZE(i2s3_rt1015p_codecs);
+ dai_link->platforms = i2s3_rt1015p_platforms;
+ dai_link->num_platforms =
+ ARRAY_SIZE(i2s3_rt1015p_platforms);
+ }
+ }
+
+ if (card == &mt8183_mt6358_ts3a227_max98357b_card) {
+ if (strcmp(dai_link->name, "I2S2") == 0 ||
+ strcmp(dai_link->name, "I2S3") == 0)
+ dai_link->dai_fmt = SND_SOC_DAIFMT_LEFT_J |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP;
+ }
+
+ if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
+ dai_link->codecs->of_node = hdmi_codec;
+ dai_link->ignore = 0;
+ }
+
+ if (!dai_link->platforms->name)
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ mt8183_mt6358_ts3a227_max98357_headset_dev.dlc.of_node =
+ of_parse_phandle(pdev->dev.of_node,
+ "mediatek,headset-codec", 0);
+ if (mt8183_mt6358_ts3a227_max98357_headset_dev.dlc.of_node) {
+ card->aux_dev = &mt8183_mt6358_ts3a227_max98357_headset_dev;
+ card->num_aux_devs = 1;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ priv->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(priv->pinctrl)) {
+ dev_err(&pdev->dev, "%s devm_pinctrl_get failed\n",
+ __func__);
+ ret = PTR_ERR(priv->pinctrl);
+ goto out;
+ }
+
+ for (i = 0; i < PIN_STATE_MAX; i++) {
+ priv->pin_states[i] = pinctrl_lookup_state(priv->pinctrl,
+ mt8183_pin_str[i]);
+ if (IS_ERR(priv->pin_states[i])) {
+ ret = PTR_ERR(priv->pin_states[i]);
+ dev_info(&pdev->dev, "%s Can't find pin state %s %d\n",
+ __func__, mt8183_pin_str[i], ret);
+ }
+ }
+
+ if (!IS_ERR(priv->pin_states[PIN_TDM_OUT_OFF])) {
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_TDM_OUT_OFF]);
+ if (ret)
+ dev_info(&pdev->dev,
+ "%s failed to select state %d\n",
+ __func__, ret);
+ }
+
+ if (!IS_ERR(priv->pin_states[PIN_STATE_DEFAULT])) {
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_DEFAULT]);
+ if (ret)
+ dev_info(&pdev->dev,
+ "%s failed to select state %d\n",
+ __func__, ret);
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+out:
+ of_node_put(platform_node);
+ of_node_put(ec_codec);
+ of_node_put(hdmi_codec);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt8183_mt6358_ts3a227_max98357_dt_match[] = {
+ {
+ .compatible = "mediatek,mt8183_mt6358_ts3a227_max98357",
+ .data = &mt8183_mt6358_ts3a227_max98357_card,
+ },
+ {
+ .compatible = "mediatek,mt8183_mt6358_ts3a227_max98357b",
+ .data = &mt8183_mt6358_ts3a227_max98357b_card,
+ },
+ {
+ .compatible = "mediatek,mt8183_mt6358_ts3a227_rt1015",
+ .data = &mt8183_mt6358_ts3a227_rt1015_card,
+ },
+ {
+ .compatible = "mediatek,mt8183_mt6358_ts3a227_rt1015p",
+ .data = &mt8183_mt6358_ts3a227_rt1015p_card,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt8183_mt6358_ts3a227_max98357_dt_match);
+#endif
+
+static struct platform_driver mt8183_mt6358_ts3a227_max98357_driver = {
+ .driver = {
+ .name = "mt8183_mt6358_ts3a227",
+#ifdef CONFIG_OF
+ .of_match_table = mt8183_mt6358_ts3a227_max98357_dt_match,
+#endif
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mt8183_mt6358_ts3a227_max98357_dev_probe,
+};
+
+module_platform_driver(mt8183_mt6358_ts3a227_max98357_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8183-MT6358-TS3A227-MAX98357 ALSA SoC machine driver");
+MODULE_AUTHOR("Shunli Wang <shunli.wang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt8183_mt6358_ts3a227_max98357 soc card");
diff --git a/sound/soc/mediatek/mt8183/mt8183-reg.h b/sound/soc/mediatek/mt8183/mt8183-reg.h
new file mode 100644
index 000000000000..e544a09e1913
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-reg.h
@@ -0,0 +1,1668 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8183-reg.h -- Mediatek 8183 audio driver reg definition
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#ifndef _MT8183_REG_H_
+#define _MT8183_REG_H_
+
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON1 0x0004
+#define AUDIO_TOP_CON3 0x000c
+#define AFE_DAC_CON0 0x0010
+#define AFE_DAC_CON1 0x0014
+#define AFE_I2S_CON 0x0018
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_CONN0 0x0020
+#define AFE_CONN1 0x0024
+#define AFE_CONN2 0x0028
+#define AFE_CONN3 0x002c
+#define AFE_CONN4 0x0030
+#define AFE_I2S_CON1 0x0034
+#define AFE_I2S_CON2 0x0038
+#define AFE_MRGIF_CON 0x003c
+#define AFE_DL1_BASE 0x0040
+#define AFE_DL1_CUR 0x0044
+#define AFE_DL1_END 0x0048
+#define AFE_I2S_CON3 0x004c
+#define AFE_DL2_BASE 0x0050
+#define AFE_DL2_CUR 0x0054
+#define AFE_DL2_END 0x0058
+#define AFE_CONN5 0x005c
+#define AFE_CONN_24BIT 0x006c
+#define AFE_AWB_BASE 0x0070
+#define AFE_AWB_END 0x0078
+#define AFE_AWB_CUR 0x007c
+#define AFE_VUL_BASE 0x0080
+#define AFE_VUL_END 0x0088
+#define AFE_VUL_CUR 0x008c
+#define AFE_CONN6 0x00bc
+#define AFE_MEMIF_MSB 0x00cc
+#define AFE_MEMIF_MON0 0x00d0
+#define AFE_MEMIF_MON1 0x00d4
+#define AFE_MEMIF_MON2 0x00d8
+#define AFE_MEMIF_MON3 0x00dc
+#define AFE_MEMIF_MON4 0x00e0
+#define AFE_MEMIF_MON5 0x00e4
+#define AFE_MEMIF_MON6 0x00e8
+#define AFE_MEMIF_MON7 0x00ec
+#define AFE_MEMIF_MON8 0x00f0
+#define AFE_MEMIF_MON9 0x00f4
+#define AFE_ADDA_DL_SRC2_CON0 0x0108
+#define AFE_ADDA_DL_SRC2_CON1 0x010c
+#define AFE_ADDA_UL_SRC_CON0 0x0114
+#define AFE_ADDA_UL_SRC_CON1 0x0118
+#define AFE_ADDA_TOP_CON0 0x0120
+#define AFE_ADDA_UL_DL_CON0 0x0124
+#define AFE_ADDA_SRC_DEBUG 0x012c
+#define AFE_ADDA_SRC_DEBUG_MON0 0x0130
+#define AFE_ADDA_SRC_DEBUG_MON1 0x0134
+#define AFE_ADDA_UL_SRC_MON0 0x0148
+#define AFE_ADDA_UL_SRC_MON1 0x014c
+#define AFE_SIDETONE_DEBUG 0x01d0
+#define AFE_SIDETONE_MON 0x01d4
+#define AFE_SINEGEN_CON2 0x01dc
+#define AFE_SIDETONE_CON0 0x01e0
+#define AFE_SIDETONE_COEFF 0x01e4
+#define AFE_SIDETONE_CON1 0x01e8
+#define AFE_SIDETONE_GAIN 0x01ec
+#define AFE_SINEGEN_CON0 0x01f0
+#define AFE_TOP_CON0 0x0200
+#define AFE_BUS_CFG 0x0240
+#define AFE_BUS_MON0 0x0244
+#define AFE_ADDA_PREDIS_CON0 0x0260
+#define AFE_ADDA_PREDIS_CON1 0x0264
+#define AFE_MRGIF_MON0 0x0270
+#define AFE_MRGIF_MON1 0x0274
+#define AFE_MRGIF_MON2 0x0278
+#define AFE_I2S_MON 0x027c
+#define AFE_ADDA_IIR_COEF_02_01 0x0290
+#define AFE_ADDA_IIR_COEF_04_03 0x0294
+#define AFE_ADDA_IIR_COEF_06_05 0x0298
+#define AFE_ADDA_IIR_COEF_08_07 0x029c
+#define AFE_ADDA_IIR_COEF_10_09 0x02a0
+#define AFE_DAC_CON2 0x02e0
+#define AFE_IRQ_MCU_CON1 0x02e4
+#define AFE_IRQ_MCU_CON2 0x02e8
+#define AFE_DAC_MON 0x02ec
+#define AFE_VUL2_BASE 0x02f0
+#define AFE_VUL2_END 0x02f8
+#define AFE_VUL2_CUR 0x02fc
+#define AFE_IRQ_MCU_CNT0 0x0300
+#define AFE_IRQ_MCU_CNT6 0x0304
+#define AFE_IRQ_MCU_CNT8 0x0308
+#define AFE_IRQ_MCU_EN1 0x030c
+#define AFE_IRQ0_MCU_CNT_MON 0x0310
+#define AFE_IRQ6_MCU_CNT_MON 0x0314
+#define AFE_MOD_DAI_BASE 0x0330
+#define AFE_MOD_DAI_END 0x0338
+#define AFE_MOD_DAI_CUR 0x033c
+#define AFE_VUL_D2_BASE 0x0350
+#define AFE_VUL_D2_END 0x0358
+#define AFE_VUL_D2_CUR 0x035c
+#define AFE_DL3_BASE 0x0360
+#define AFE_DL3_CUR 0x0364
+#define AFE_DL3_END 0x0368
+#define AFE_HDMI_OUT_CON0 0x0370
+#define AFE_HDMI_OUT_BASE 0x0374
+#define AFE_HDMI_OUT_CUR 0x0378
+#define AFE_HDMI_OUT_END 0x037c
+#define AFE_HDMI_CONN0 0x0390
+#define AFE_IRQ3_MCU_CNT_MON 0x0398
+#define AFE_IRQ4_MCU_CNT_MON 0x039c
+#define AFE_IRQ_MCU_CON0 0x03a0
+#define AFE_IRQ_MCU_STATUS 0x03a4
+#define AFE_IRQ_MCU_CLR 0x03a8
+#define AFE_IRQ_MCU_CNT1 0x03ac
+#define AFE_IRQ_MCU_CNT2 0x03b0
+#define AFE_IRQ_MCU_EN 0x03b4
+#define AFE_IRQ_MCU_MON2 0x03b8
+#define AFE_IRQ_MCU_CNT5 0x03bc
+#define AFE_IRQ1_MCU_CNT_MON 0x03c0
+#define AFE_IRQ2_MCU_CNT_MON 0x03c4
+#define AFE_IRQ1_MCU_EN_CNT_MON 0x03c8
+#define AFE_IRQ5_MCU_CNT_MON 0x03cc
+#define AFE_MEMIF_MINLEN 0x03d0
+#define AFE_MEMIF_MAXLEN 0x03d4
+#define AFE_MEMIF_PBUF_SIZE 0x03d8
+#define AFE_IRQ_MCU_CNT7 0x03dc
+#define AFE_IRQ7_MCU_CNT_MON 0x03e0
+#define AFE_IRQ_MCU_CNT3 0x03e4
+#define AFE_IRQ_MCU_CNT4 0x03e8
+#define AFE_IRQ_MCU_CNT11 0x03ec
+#define AFE_APLL1_TUNER_CFG 0x03f0
+#define AFE_APLL2_TUNER_CFG 0x03f4
+#define AFE_MEMIF_HD_MODE 0x03f8
+#define AFE_MEMIF_HDALIGN 0x03fc
+#define AFE_CONN33 0x0408
+#define AFE_IRQ_MCU_CNT12 0x040c
+#define AFE_GAIN1_CON0 0x0410
+#define AFE_GAIN1_CON1 0x0414
+#define AFE_GAIN1_CON2 0x0418
+#define AFE_GAIN1_CON3 0x041c
+#define AFE_CONN7 0x0420
+#define AFE_GAIN1_CUR 0x0424
+#define AFE_GAIN2_CON0 0x0428
+#define AFE_GAIN2_CON1 0x042c
+#define AFE_GAIN2_CON2 0x0430
+#define AFE_GAIN2_CON3 0x0434
+#define AFE_CONN8 0x0438
+#define AFE_GAIN2_CUR 0x043c
+#define AFE_CONN9 0x0440
+#define AFE_CONN10 0x0444
+#define AFE_CONN11 0x0448
+#define AFE_CONN12 0x044c
+#define AFE_CONN13 0x0450
+#define AFE_CONN14 0x0454
+#define AFE_CONN15 0x0458
+#define AFE_CONN16 0x045c
+#define AFE_CONN17 0x0460
+#define AFE_CONN18 0x0464
+#define AFE_CONN19 0x0468
+#define AFE_CONN20 0x046c
+#define AFE_CONN21 0x0470
+#define AFE_CONN22 0x0474
+#define AFE_CONN23 0x0478
+#define AFE_CONN24 0x047c
+#define AFE_CONN_RS 0x0494
+#define AFE_CONN_DI 0x0498
+#define AFE_CONN25 0x04b0
+#define AFE_CONN26 0x04b4
+#define AFE_CONN27 0x04b8
+#define AFE_CONN28 0x04bc
+#define AFE_CONN29 0x04c0
+#define AFE_CONN30 0x04c4
+#define AFE_CONN31 0x04c8
+#define AFE_CONN32 0x04cc
+#define AFE_SRAM_DELSEL_CON0 0x04f0
+#define AFE_SRAM_DELSEL_CON2 0x04f8
+#define AFE_SRAM_DELSEL_CON3 0x04fc
+#define AFE_ASRC_2CH_CON12 0x0528
+#define AFE_ASRC_2CH_CON13 0x052c
+#define PCM_INTF_CON1 0x0530
+#define PCM_INTF_CON2 0x0538
+#define PCM2_INTF_CON 0x053c
+#define AFE_TDM_CON1 0x0548
+#define AFE_TDM_CON2 0x054c
+#define AFE_CONN34 0x0580
+#define FPGA_CFG0 0x05b0
+#define FPGA_CFG1 0x05b4
+#define FPGA_CFG2 0x05c0
+#define FPGA_CFG3 0x05c4
+#define AUDIO_TOP_DBG_CON 0x05c8
+#define AUDIO_TOP_DBG_MON0 0x05cc
+#define AUDIO_TOP_DBG_MON1 0x05d0
+#define AFE_IRQ8_MCU_CNT_MON 0x05e4
+#define AFE_IRQ11_MCU_CNT_MON 0x05e8
+#define AFE_IRQ12_MCU_CNT_MON 0x05ec
+#define AFE_GENERAL_REG0 0x0800
+#define AFE_GENERAL_REG1 0x0804
+#define AFE_GENERAL_REG2 0x0808
+#define AFE_GENERAL_REG3 0x080c
+#define AFE_GENERAL_REG4 0x0810
+#define AFE_GENERAL_REG5 0x0814
+#define AFE_GENERAL_REG6 0x0818
+#define AFE_GENERAL_REG7 0x081c
+#define AFE_GENERAL_REG8 0x0820
+#define AFE_GENERAL_REG9 0x0824
+#define AFE_GENERAL_REG10 0x0828
+#define AFE_GENERAL_REG11 0x082c
+#define AFE_GENERAL_REG12 0x0830
+#define AFE_GENERAL_REG13 0x0834
+#define AFE_GENERAL_REG14 0x0838
+#define AFE_GENERAL_REG15 0x083c
+#define AFE_CBIP_CFG0 0x0840
+#define AFE_CBIP_MON0 0x0844
+#define AFE_CBIP_SLV_MUX_MON0 0x0848
+#define AFE_CBIP_SLV_DECODER_MON0 0x084c
+#define AFE_CONN0_1 0x0900
+#define AFE_CONN1_1 0x0904
+#define AFE_CONN2_1 0x0908
+#define AFE_CONN3_1 0x090c
+#define AFE_CONN4_1 0x0910
+#define AFE_CONN5_1 0x0914
+#define AFE_CONN6_1 0x0918
+#define AFE_CONN7_1 0x091c
+#define AFE_CONN8_1 0x0920
+#define AFE_CONN9_1 0x0924
+#define AFE_CONN10_1 0x0928
+#define AFE_CONN11_1 0x092c
+#define AFE_CONN12_1 0x0930
+#define AFE_CONN13_1 0x0934
+#define AFE_CONN14_1 0x0938
+#define AFE_CONN15_1 0x093c
+#define AFE_CONN16_1 0x0940
+#define AFE_CONN17_1 0x0944
+#define AFE_CONN18_1 0x0948
+#define AFE_CONN19_1 0x094c
+#define AFE_CONN20_1 0x0950
+#define AFE_CONN21_1 0x0954
+#define AFE_CONN22_1 0x0958
+#define AFE_CONN23_1 0x095c
+#define AFE_CONN24_1 0x0960
+#define AFE_CONN25_1 0x0964
+#define AFE_CONN26_1 0x0968
+#define AFE_CONN27_1 0x096c
+#define AFE_CONN28_1 0x0970
+#define AFE_CONN29_1 0x0974
+#define AFE_CONN30_1 0x0978
+#define AFE_CONN31_1 0x097c
+#define AFE_CONN32_1 0x0980
+#define AFE_CONN33_1 0x0984
+#define AFE_CONN34_1 0x0988
+#define AFE_CONN_RS_1 0x098c
+#define AFE_CONN_DI_1 0x0990
+#define AFE_CONN_24BIT_1 0x0994
+#define AFE_CONN_REG 0x0998
+#define AFE_CONN35 0x09a0
+#define AFE_CONN36 0x09a4
+#define AFE_CONN37 0x09a8
+#define AFE_CONN38 0x09ac
+#define AFE_CONN35_1 0x09b0
+#define AFE_CONN36_1 0x09b4
+#define AFE_CONN37_1 0x09b8
+#define AFE_CONN38_1 0x09bc
+#define AFE_CONN39 0x09c0
+#define AFE_CONN40 0x09c4
+#define AFE_CONN41 0x09c8
+#define AFE_CONN42 0x09cc
+#define AFE_CONN39_1 0x09e0
+#define AFE_CONN40_1 0x09e4
+#define AFE_CONN41_1 0x09e8
+#define AFE_CONN42_1 0x09ec
+#define AFE_I2S_CON4 0x09f8
+#define AFE_ADDA6_TOP_CON0 0x0a80
+#define AFE_ADDA6_UL_SRC_CON0 0x0a84
+#define AFE_ADD6_UL_SRC_CON1 0x0a88
+#define AFE_ADDA6_SRC_DEBUG 0x0a8c
+#define AFE_ADDA6_SRC_DEBUG_MON0 0x0a90
+#define AFE_ADDA6_ULCF_CFG_02_01 0x0aa0
+#define AFE_ADDA6_ULCF_CFG_04_03 0x0aa4
+#define AFE_ADDA6_ULCF_CFG_06_05 0x0aa8
+#define AFE_ADDA6_ULCF_CFG_08_07 0x0aac
+#define AFE_ADDA6_ULCF_CFG_10_09 0x0ab0
+#define AFE_ADDA6_ULCF_CFG_12_11 0x0ab4
+#define AFE_ADDA6_ULCF_CFG_14_13 0x0ab8
+#define AFE_ADDA6_ULCF_CFG_16_15 0x0abc
+#define AFE_ADDA6_ULCF_CFG_18_17 0x0ac0
+#define AFE_ADDA6_ULCF_CFG_20_19 0x0ac4
+#define AFE_ADDA6_ULCF_CFG_22_21 0x0ac8
+#define AFE_ADDA6_ULCF_CFG_24_23 0x0acc
+#define AFE_ADDA6_ULCF_CFG_26_25 0x0ad0
+#define AFE_ADDA6_ULCF_CFG_28_27 0x0ad4
+#define AFE_ADDA6_ULCF_CFG_30_29 0x0ad8
+#define AFE_ADD6A_UL_SRC_MON0 0x0ae4
+#define AFE_ADDA6_UL_SRC_MON1 0x0ae8
+#define AFE_CONN43 0x0af8
+#define AFE_CONN43_1 0x0afc
+#define AFE_DL1_BASE_MSB 0x0b00
+#define AFE_DL1_CUR_MSB 0x0b04
+#define AFE_DL1_END_MSB 0x0b08
+#define AFE_DL2_BASE_MSB 0x0b10
+#define AFE_DL2_CUR_MSB 0x0b14
+#define AFE_DL2_END_MSB 0x0b18
+#define AFE_AWB_BASE_MSB 0x0b20
+#define AFE_AWB_END_MSB 0x0b28
+#define AFE_AWB_CUR_MSB 0x0b2c
+#define AFE_VUL_BASE_MSB 0x0b30
+#define AFE_VUL_END_MSB 0x0b38
+#define AFE_VUL_CUR_MSB 0x0b3c
+#define AFE_VUL2_BASE_MSB 0x0b50
+#define AFE_VUL2_END_MSB 0x0b58
+#define AFE_VUL2_CUR_MSB 0x0b5c
+#define AFE_MOD_DAI_BASE_MSB 0x0b60
+#define AFE_MOD_DAI_END_MSB 0x0b68
+#define AFE_MOD_DAI_CUR_MSB 0x0b6c
+#define AFE_VUL_D2_BASE_MSB 0x0b80
+#define AFE_VUL_D2_END_MSB 0x0b88
+#define AFE_VUL_D2_CUR_MSB 0x0b8c
+#define AFE_DL3_BASE_MSB 0x0b90
+#define AFE_DL3_CUR_MSB 0x0b94
+#define AFE_DL3_END_MSB 0x0b98
+#define AFE_HDMI_OUT_BASE_MSB 0x0ba4
+#define AFE_HDMI_OUT_CUR_MSB 0x0ba8
+#define AFE_HDMI_OUT_END_MSB 0x0bac
+#define AFE_AWB2_BASE 0x0bd0
+#define AFE_AWB2_END 0x0bd8
+#define AFE_AWB2_CUR 0x0bdc
+#define AFE_AWB2_BASE_MSB 0x0be0
+#define AFE_AWB2_END_MSB 0x0be8
+#define AFE_AWB2_CUR_MSB 0x0bec
+#define AFE_ADDA_DL_SDM_DCCOMP_CON 0x0c50
+#define AFE_ADDA_DL_SDM_TEST 0x0c54
+#define AFE_ADDA_DL_DC_COMP_CFG0 0x0c58
+#define AFE_ADDA_DL_DC_COMP_CFG1 0x0c5c
+#define AFE_ADDA_DL_SDM_FIFO_MON 0x0c60
+#define AFE_ADDA_DL_SRC_LCH_MON 0x0c64
+#define AFE_ADDA_DL_SRC_RCH_MON 0x0c68
+#define AFE_ADDA_DL_SDM_OUT_MON 0x0c6c
+#define AFE_CONNSYS_I2S_CON 0x0c78
+#define AFE_CONNSYS_I2S_MON 0x0c7c
+#define AFE_ASRC_2CH_CON0 0x0c80
+#define AFE_ASRC_2CH_CON1 0x0c84
+#define AFE_ASRC_2CH_CON2 0x0c88
+#define AFE_ASRC_2CH_CON3 0x0c8c
+#define AFE_ASRC_2CH_CON4 0x0c90
+#define AFE_ASRC_2CH_CON5 0x0c94
+#define AFE_ASRC_2CH_CON6 0x0c98
+#define AFE_ASRC_2CH_CON7 0x0c9c
+#define AFE_ASRC_2CH_CON8 0x0ca0
+#define AFE_ASRC_2CH_CON9 0x0ca4
+#define AFE_ASRC_2CH_CON10 0x0ca8
+#define AFE_ADDA6_IIR_COEF_02_01 0x0ce0
+#define AFE_ADDA6_IIR_COEF_04_03 0x0ce4
+#define AFE_ADDA6_IIR_COEF_06_05 0x0ce8
+#define AFE_ADDA6_IIR_COEF_08_07 0x0cec
+#define AFE_ADDA6_IIR_COEF_10_09 0x0cf0
+#define AFE_ADDA_PREDIS_CON2 0x0d40
+#define AFE_ADDA_PREDIS_CON3 0x0d44
+#define AFE_MEMIF_MON12 0x0d70
+#define AFE_MEMIF_MON13 0x0d74
+#define AFE_MEMIF_MON14 0x0d78
+#define AFE_MEMIF_MON15 0x0d7c
+#define AFE_MEMIF_MON16 0x0d80
+#define AFE_MEMIF_MON17 0x0d84
+#define AFE_MEMIF_MON18 0x0d88
+#define AFE_MEMIF_MON19 0x0d8c
+#define AFE_MEMIF_MON20 0x0d90
+#define AFE_MEMIF_MON21 0x0d94
+#define AFE_MEMIF_MON22 0x0d98
+#define AFE_MEMIF_MON23 0x0d9c
+#define AFE_MEMIF_MON24 0x0da0
+#define AFE_HD_ENGEN_ENABLE 0x0dd0
+#define AFE_ADDA_MTKAIF_CFG0 0x0e00
+#define AFE_ADDA_MTKAIF_TX_CFG1 0x0e14
+#define AFE_ADDA_MTKAIF_RX_CFG0 0x0e20
+#define AFE_ADDA_MTKAIF_RX_CFG1 0x0e24
+#define AFE_ADDA_MTKAIF_RX_CFG2 0x0e28
+#define AFE_ADDA_MTKAIF_MON0 0x0e34
+#define AFE_ADDA_MTKAIF_MON1 0x0e38
+#define AFE_AUD_PAD_TOP 0x0e40
+#define AFE_GENERAL1_ASRC_2CH_CON0 0x0e80
+#define AFE_GENERAL1_ASRC_2CH_CON1 0x0e84
+#define AFE_GENERAL1_ASRC_2CH_CON2 0x0e88
+#define AFE_GENERAL1_ASRC_2CH_CON3 0x0e8c
+#define AFE_GENERAL1_ASRC_2CH_CON4 0x0e90
+#define AFE_GENERAL1_ASRC_2CH_CON5 0x0e94
+#define AFE_GENERAL1_ASRC_2CH_CON6 0x0e98
+#define AFE_GENERAL1_ASRC_2CH_CON7 0x0e9c
+#define AFE_GENERAL1_ASRC_2CH_CON8 0x0ea0
+#define AFE_GENERAL1_ASRC_2CH_CON9 0x0ea4
+#define AFE_GENERAL1_ASRC_2CH_CON10 0x0ea8
+#define AFE_GENERAL1_ASRC_2CH_CON12 0x0eb0
+#define AFE_GENERAL1_ASRC_2CH_CON13 0x0eb4
+#define GENERAL_ASRC_MODE 0x0eb8
+#define GENERAL_ASRC_EN_ON 0x0ebc
+#define AFE_GENERAL2_ASRC_2CH_CON0 0x0f00
+#define AFE_GENERAL2_ASRC_2CH_CON1 0x0f04
+#define AFE_GENERAL2_ASRC_2CH_CON2 0x0f08
+#define AFE_GENERAL2_ASRC_2CH_CON3 0x0f0c
+#define AFE_GENERAL2_ASRC_2CH_CON4 0x0f10
+#define AFE_GENERAL2_ASRC_2CH_CON5 0x0f14
+#define AFE_GENERAL2_ASRC_2CH_CON6 0x0f18
+#define AFE_GENERAL2_ASRC_2CH_CON7 0x0f1c
+#define AFE_GENERAL2_ASRC_2CH_CON8 0x0f20
+#define AFE_GENERAL2_ASRC_2CH_CON9 0x0f24
+#define AFE_GENERAL2_ASRC_2CH_CON10 0x0f28
+#define AFE_GENERAL2_ASRC_2CH_CON12 0x0f30
+#define AFE_GENERAL2_ASRC_2CH_CON13 0x0f34
+
+#define AFE_MAX_REGISTER AFE_GENERAL2_ASRC_2CH_CON13
+#define AFE_IRQ_STATUS_BITS 0x1fff
+
+/* AUDIO_TOP_CON3 */
+#define BCK_INVERSE_SFT 3
+#define BCK_INVERSE_MASK 0x1
+#define BCK_INVERSE_MASK_SFT (0x1 << 3)
+
+/* AFE_DAC_CON0 */
+#define AWB2_ON_SFT 29
+#define AWB2_ON_MASK 0x1
+#define AWB2_ON_MASK_SFT (0x1 << 29)
+#define VUL2_ON_SFT 27
+#define VUL2_ON_MASK 0x1
+#define VUL2_ON_MASK_SFT (0x1 << 27)
+#define MOD_DAI_DUP_WR_SFT 26
+#define MOD_DAI_DUP_WR_MASK 0x1
+#define MOD_DAI_DUP_WR_MASK_SFT (0x1 << 26)
+#define VUL12_MODE_SFT 20
+#define VUL12_MODE_MASK 0xf
+#define VUL12_MODE_MASK_SFT (0xf << 20)
+#define VUL12_R_MONO_SFT 11
+#define VUL12_R_MONO_MASK 0x1
+#define VUL12_R_MONO_MASK_SFT (0x1 << 11)
+#define VUL12_MONO_SFT 10
+#define VUL12_MONO_MASK 0x1
+#define VUL12_MONO_MASK_SFT (0x1 << 10)
+#define VUL12_ON_SFT 9
+#define VUL12_ON_MASK 0x1
+#define VUL12_ON_MASK_SFT (0x1 << 9)
+#define MOD_DAI_ON_SFT 7
+#define MOD_DAI_ON_MASK 0x1
+#define MOD_DAI_ON_MASK_SFT (0x1 << 7)
+#define AWB_ON_SFT 6
+#define AWB_ON_MASK 0x1
+#define AWB_ON_MASK_SFT (0x1 << 6)
+#define DL3_ON_SFT 5
+#define DL3_ON_MASK 0x1
+#define DL3_ON_MASK_SFT (0x1 << 5)
+#define VUL_ON_SFT 3
+#define VUL_ON_MASK 0x1
+#define VUL_ON_MASK_SFT (0x1 << 3)
+#define DL2_ON_SFT 2
+#define DL2_ON_MASK 0x1
+#define DL2_ON_MASK_SFT (0x1 << 2)
+#define DL1_ON_SFT 1
+#define DL1_ON_MASK 0x1
+#define DL1_ON_MASK_SFT (0x1 << 1)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DAC_CON1 */
+#define MOD_DAI_MODE_SFT 30
+#define MOD_DAI_MODE_MASK 0x3
+#define MOD_DAI_MODE_MASK_SFT (0x3 << 30)
+#define VUL_R_MONO_SFT 28
+#define VUL_R_MONO_MASK 0x1
+#define VUL_R_MONO_MASK_SFT (0x1 << 28)
+#define VUL_DATA_SFT 27
+#define VUL_DATA_MASK 0x1
+#define VUL_DATA_MASK_SFT (0x1 << 27)
+#define AWB_R_MONO_SFT 25
+#define AWB_R_MONO_MASK 0x1
+#define AWB_R_MONO_MASK_SFT (0x1 << 25)
+#define AWB_DATA_SFT 24
+#define AWB_DATA_MASK 0x1
+#define AWB_DATA_MASK_SFT (0x1 << 24)
+#define DL3_DATA_SFT 23
+#define DL3_DATA_MASK 0x1
+#define DL3_DATA_MASK_SFT (0x1 << 23)
+#define DL2_DATA_SFT 22
+#define DL2_DATA_MASK 0x1
+#define DL2_DATA_MASK_SFT (0x1 << 22)
+#define DL1_DATA_SFT 21
+#define DL1_DATA_MASK 0x1
+#define DL1_DATA_MASK_SFT (0x1 << 21)
+#define VUL_MODE_SFT 16
+#define VUL_MODE_MASK 0xf
+#define VUL_MODE_MASK_SFT (0xf << 16)
+#define AWB_MODE_SFT 12
+#define AWB_MODE_MASK 0xf
+#define AWB_MODE_MASK_SFT (0xf << 12)
+#define I2S_MODE_SFT 8
+#define I2S_MODE_MASK 0xf
+#define I2S_MODE_MASK_SFT (0xf << 8)
+#define DL2_MODE_SFT 4
+#define DL2_MODE_MASK 0xf
+#define DL2_MODE_MASK_SFT (0xf << 4)
+#define DL1_MODE_SFT 0
+#define DL1_MODE_MASK 0xf
+#define DL1_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_DAC_CON2 */
+#define AWB2_R_MONO_SFT 21
+#define AWB2_R_MONO_MASK 0x1
+#define AWB2_R_MONO_MASK_SFT (0x1 << 21)
+#define AWB2_DATA_SFT 20
+#define AWB2_DATA_MASK 0x1
+#define AWB2_DATA_MASK_SFT (0x1 << 20)
+#define AWB2_MODE_SFT 16
+#define AWB2_MODE_MASK 0xf
+#define AWB2_MODE_MASK_SFT (0xf << 16)
+#define DL3_MODE_SFT 8
+#define DL3_MODE_MASK 0xf
+#define DL3_MODE_MASK_SFT (0xf << 8)
+#define VUL2_MODE_SFT 4
+#define VUL2_MODE_MASK 0xf
+#define VUL2_MODE_MASK_SFT (0xf << 4)
+#define VUL2_R_MONO_SFT 1
+#define VUL2_R_MONO_MASK 0x1
+#define VUL2_R_MONO_MASK_SFT (0x1 << 1)
+#define VUL2_DATA_SFT 0
+#define VUL2_DATA_MASK 0x1
+#define VUL2_DATA_MASK_SFT (0x1 << 0)
+
+/* AFE_DAC_MON */
+#define AFE_ON_RETM_SFT 0
+#define AFE_ON_RETM_MASK 0x1
+#define AFE_ON_RETM_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK 0x1
+#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK 0x1
+#define BCK_INV_MASK_SFT (0x1 << 29)
+#define I2SIN_PAD_SEL_SFT 28
+#define I2SIN_PAD_SEL_MASK 0x1
+#define I2SIN_PAD_SEL_MASK_SFT (0x1 << 28)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S1_HD_EN_SFT 12
+#define I2S1_HD_EN_MASK 0x1
+#define I2S1_HD_EN_MASK_SFT (0x1 << 12)
+#define INV_PAD_CTRL_SFT 7
+#define INV_PAD_CTRL_MASK 0x1
+#define INV_PAD_CTRL_MASK_SFT (0x1 << 7)
+#define I2S_BYPSRC_SFT 6
+#define I2S_BYPSRC_MASK 0x1
+#define I2S_BYPSRC_MASK_SFT (0x1 << 6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S_FMT_SFT 3
+#define I2S_FMT_MASK 0x1
+#define I2S_FMT_MASK_SFT (0x1 << 3)
+#define I2S_SRC_SFT 2
+#define I2S_SRC_MASK 0x1
+#define I2S_SRC_MASK_SFT (0x1 << 2)
+#define I2S_WLEN_SFT 1
+#define I2S_WLEN_MASK 0x1
+#define I2S_WLEN_MASK_SFT (0x1 << 1)
+#define I2S_EN_SFT 0
+#define I2S_EN_MASK 0x1
+#define I2S_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON1 */
+#define I2S2_LR_SWAP_SFT 31
+#define I2S2_LR_SWAP_MASK 0x1
+#define I2S2_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S2_SEL_O19_O20_SFT 18
+#define I2S2_SEL_O19_O20_MASK 0x1
+#define I2S2_SEL_O19_O20_MASK_SFT (0x1 << 18)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S2_SEL_O03_O04_SFT 16
+#define I2S2_SEL_O03_O04_MASK 0x1
+#define I2S2_SEL_O03_O04_MASK_SFT (0x1 << 16)
+#define I2S2_32BIT_EN_SFT 13
+#define I2S2_32BIT_EN_MASK 0x1
+#define I2S2_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S2_HD_EN_SFT 12
+#define I2S2_HD_EN_MASK 0x1
+#define I2S2_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S2_OUT_MODE_SFT 8
+#define I2S2_OUT_MODE_MASK 0xf
+#define I2S2_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S2_FMT_SFT 3
+#define I2S2_FMT_MASK 0x1
+#define I2S2_FMT_MASK_SFT (0x1 << 3)
+#define I2S2_WLEN_SFT 1
+#define I2S2_WLEN_MASK 0x1
+#define I2S2_WLEN_MASK_SFT (0x1 << 1)
+#define I2S2_EN_SFT 0
+#define I2S2_EN_MASK 0x1
+#define I2S2_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON2 */
+#define I2S3_LR_SWAP_SFT 31
+#define I2S3_LR_SWAP_MASK 0x1
+#define I2S3_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S3_UPDATE_WORD_SFT 24
+#define I2S3_UPDATE_WORD_MASK 0x1f
+#define I2S3_UPDATE_WORD_MASK_SFT (0x1f << 24)
+#define I2S3_BCK_INV_SFT 23
+#define I2S3_BCK_INV_MASK 0x1
+#define I2S3_BCK_INV_MASK_SFT (0x1 << 23)
+#define I2S3_FPGA_BIT_TEST_SFT 22
+#define I2S3_FPGA_BIT_TEST_MASK 0x1
+#define I2S3_FPGA_BIT_TEST_MASK_SFT (0x1 << 22)
+#define I2S3_FPGA_BIT_SFT 21
+#define I2S3_FPGA_BIT_MASK 0x1
+#define I2S3_FPGA_BIT_MASK_SFT (0x1 << 21)
+#define I2S3_LOOPBACK_SFT 20
+#define I2S3_LOOPBACK_MASK 0x1
+#define I2S3_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S3_HD_EN_SFT 12
+#define I2S3_HD_EN_MASK 0x1
+#define I2S3_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S3_OUT_MODE_SFT 8
+#define I2S3_OUT_MODE_MASK 0xf
+#define I2S3_OUT_MODE_MASK_SFT (0xf << 8)
+#define I2S3_FMT_SFT 3
+#define I2S3_FMT_MASK 0x1
+#define I2S3_FMT_MASK_SFT (0x1 << 3)
+#define I2S3_WLEN_SFT 1
+#define I2S3_WLEN_MASK 0x1
+#define I2S3_WLEN_MASK_SFT (0x1 << 1)
+#define I2S3_EN_SFT 0
+#define I2S3_EN_MASK 0x1
+#define I2S3_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON3 */
+#define I2S4_LR_SWAP_SFT 31
+#define I2S4_LR_SWAP_MASK 0x1
+#define I2S4_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S4_32BIT_EN_SFT 13
+#define I2S4_32BIT_EN_MASK 0x1
+#define I2S4_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S4_HD_EN_SFT 12
+#define I2S4_HD_EN_MASK 0x1
+#define I2S4_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S4_OUT_MODE_SFT 8
+#define I2S4_OUT_MODE_MASK 0xf
+#define I2S4_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S4_FMT_SFT 3
+#define I2S4_FMT_MASK 0x1
+#define I2S4_FMT_MASK_SFT (0x1 << 3)
+#define I2S4_WLEN_SFT 1
+#define I2S4_WLEN_MASK 0x1
+#define I2S4_WLEN_MASK_SFT (0x1 << 1)
+#define I2S4_EN_SFT 0
+#define I2S4_EN_MASK 0x1
+#define I2S4_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON4 */
+#define I2S5_LR_SWAP_SFT 31
+#define I2S5_LR_SWAP_MASK 0x1
+#define I2S5_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S5_32BIT_EN_SFT 13
+#define I2S5_32BIT_EN_MASK 0x1
+#define I2S5_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S5_HD_EN_SFT 12
+#define I2S5_HD_EN_MASK 0x1
+#define I2S5_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S5_OUT_MODE_SFT 8
+#define I2S5_OUT_MODE_MASK 0xf
+#define I2S5_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S5_FMT_SFT 3
+#define I2S5_FMT_MASK 0x1
+#define I2S5_FMT_MASK_SFT (0x1 << 3)
+#define I2S5_WLEN_SFT 1
+#define I2S5_WLEN_MASK 0x1
+#define I2S5_WLEN_MASK_SFT (0x1 << 1)
+#define I2S5_EN_SFT 0
+#define I2S5_EN_MASK 0x1
+#define I2S5_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_GAIN1_CON0 */
+#define GAIN1_SAMPLE_PER_STEP_SFT 8
+#define GAIN1_SAMPLE_PER_STEP_MASK 0xff
+#define GAIN1_SAMPLE_PER_STEP_MASK_SFT (0xff << 8)
+#define GAIN1_MODE_SFT 4
+#define GAIN1_MODE_MASK 0xf
+#define GAIN1_MODE_MASK_SFT (0xf << 4)
+#define GAIN1_ON_SFT 0
+#define GAIN1_ON_MASK 0x1
+#define GAIN1_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GAIN1_CON1 */
+#define GAIN1_TARGET_SFT 0
+#define GAIN1_TARGET_MASK 0xfffff
+#define GAIN1_TARGET_MASK_SFT (0xfffff << 0)
+
+/* AFE_GAIN2_CON0 */
+#define GAIN2_SAMPLE_PER_STEP_SFT 8
+#define GAIN2_SAMPLE_PER_STEP_MASK 0xff
+#define GAIN2_SAMPLE_PER_STEP_MASK_SFT (0xff << 8)
+#define GAIN2_MODE_SFT 4
+#define GAIN2_MODE_MASK 0xf
+#define GAIN2_MODE_MASK_SFT (0xf << 4)
+#define GAIN2_ON_SFT 0
+#define GAIN2_ON_MASK 0x1
+#define GAIN2_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GAIN2_CON1 */
+#define GAIN2_TARGET_SFT 0
+#define GAIN2_TARGET_MASK 0xfffff
+#define GAIN2_TARGET_MASK_SFT (0xfffff << 0)
+
+/* AFE_GAIN1_CUR */
+#define AFE_GAIN1_CUR_SFT 0
+#define AFE_GAIN1_CUR_MASK 0xfffff
+#define AFE_GAIN1_CUR_MASK_SFT (0xfffff << 0)
+
+/* AFE_GAIN2_CUR */
+#define AFE_GAIN2_CUR_SFT 0
+#define AFE_GAIN2_CUR_MASK 0xfffff
+#define AFE_GAIN2_CUR_MASK_SFT (0xfffff << 0)
+
+/* AFE_MEMIF_HD_MODE */
+#define AWB2_HD_SFT 28
+#define AWB2_HD_MASK 0x3
+#define AWB2_HD_MASK_SFT (0x3 << 28)
+#define HDMI_HD_SFT 20
+#define HDMI_HD_MASK 0x3
+#define HDMI_HD_MASK_SFT (0x3 << 20)
+#define MOD_DAI_HD_SFT 18
+#define MOD_DAI_HD_MASK 0x3
+#define MOD_DAI_HD_MASK_SFT (0x3 << 18)
+#define DAI_HD_SFT 16
+#define DAI_HD_MASK 0x3
+#define DAI_HD_MASK_SFT (0x3 << 16)
+#define VUL2_HD_SFT 14
+#define VUL2_HD_MASK 0x3
+#define VUL2_HD_MASK_SFT (0x3 << 14)
+#define VUL12_HD_SFT 12
+#define VUL12_HD_MASK 0x3
+#define VUL12_HD_MASK_SFT (0x3 << 12)
+#define VUL_HD_SFT 10
+#define VUL_HD_MASK 0x3
+#define VUL_HD_MASK_SFT (0x3 << 10)
+#define AWB_HD_SFT 8
+#define AWB_HD_MASK 0x3
+#define AWB_HD_MASK_SFT (0x3 << 8)
+#define DL3_HD_SFT 6
+#define DL3_HD_MASK 0x3
+#define DL3_HD_MASK_SFT (0x3 << 6)
+#define DL2_HD_SFT 4
+#define DL2_HD_MASK 0x3
+#define DL2_HD_MASK_SFT (0x3 << 4)
+#define DL1_HD_SFT 0
+#define DL1_HD_MASK 0x3
+#define DL1_HD_MASK_SFT (0x3 << 0)
+
+/* AFE_MEMIF_HDALIGN */
+#define AWB2_NORMAL_MODE_SFT 30
+#define AWB2_NORMAL_MODE_MASK 0x1
+#define AWB2_NORMAL_MODE_MASK_SFT (0x1 << 30)
+#define HDMI_NORMAL_MODE_SFT 26
+#define HDMI_NORMAL_MODE_MASK 0x1
+#define HDMI_NORMAL_MODE_MASK_SFT (0x1 << 26)
+#define MOD_DAI_NORMAL_MODE_SFT 25
+#define MOD_DAI_NORMAL_MODE_MASK 0x1
+#define MOD_DAI_NORMAL_MODE_MASK_SFT (0x1 << 25)
+#define DAI_NORMAL_MODE_SFT 24
+#define DAI_NORMAL_MODE_MASK 0x1
+#define DAI_NORMAL_MODE_MASK_SFT (0x1 << 24)
+#define VUL2_NORMAL_MODE_SFT 23
+#define VUL2_NORMAL_MODE_MASK 0x1
+#define VUL2_NORMAL_MODE_MASK_SFT (0x1 << 23)
+#define VUL12_NORMAL_MODE_SFT 22
+#define VUL12_NORMAL_MODE_MASK 0x1
+#define VUL12_NORMAL_MODE_MASK_SFT (0x1 << 22)
+#define VUL_NORMAL_MODE_SFT 21
+#define VUL_NORMAL_MODE_MASK 0x1
+#define VUL_NORMAL_MODE_MASK_SFT (0x1 << 21)
+#define AWB_NORMAL_MODE_SFT 20
+#define AWB_NORMAL_MODE_MASK 0x1
+#define AWB_NORMAL_MODE_MASK_SFT (0x1 << 20)
+#define DL3_NORMAL_MODE_SFT 19
+#define DL3_NORMAL_MODE_MASK 0x1
+#define DL3_NORMAL_MODE_MASK_SFT (0x1 << 19)
+#define DL2_NORMAL_MODE_SFT 18
+#define DL2_NORMAL_MODE_MASK 0x1
+#define DL2_NORMAL_MODE_MASK_SFT (0x1 << 18)
+#define DL1_NORMAL_MODE_SFT 16
+#define DL1_NORMAL_MODE_MASK 0x1
+#define DL1_NORMAL_MODE_MASK_SFT (0x1 << 16)
+#define RESERVED1_SFT 15
+#define RESERVED1_MASK 0x1
+#define RESERVED1_MASK_SFT (0x1 << 15)
+#define AWB2_ALIGN_SFT 14
+#define AWB2_ALIGN_MASK 0x1
+#define AWB2_ALIGN_MASK_SFT (0x1 << 14)
+#define HDMI_HD_ALIGN_SFT 10
+#define HDMI_HD_ALIGN_MASK 0x1
+#define HDMI_HD_ALIGN_MASK_SFT (0x1 << 10)
+#define MOD_DAI_HD_ALIGN_SFT 9
+#define MOD_DAI_HD_ALIGN_MASK 0x1
+#define MOD_DAI_HD_ALIGN_MASK_SFT (0x1 << 9)
+#define VUL2_HD_ALIGN_SFT 7
+#define VUL2_HD_ALIGN_MASK 0x1
+#define VUL2_HD_ALIGN_MASK_SFT (0x1 << 7)
+#define VUL12_HD_ALIGN_SFT 6
+#define VUL12_HD_ALIGN_MASK 0x1
+#define VUL12_HD_ALIGN_MASK_SFT (0x1 << 6)
+#define VUL_HD_ALIGN_SFT 5
+#define VUL_HD_ALIGN_MASK 0x1
+#define VUL_HD_ALIGN_MASK_SFT (0x1 << 5)
+#define AWB_HD_ALIGN_SFT 4
+#define AWB_HD_ALIGN_MASK 0x1
+#define AWB_HD_ALIGN_MASK_SFT (0x1 << 4)
+#define DL3_HD_ALIGN_SFT 3
+#define DL3_HD_ALIGN_MASK 0x1
+#define DL3_HD_ALIGN_MASK_SFT (0x1 << 3)
+#define DL2_HD_ALIGN_SFT 2
+#define DL2_HD_ALIGN_MASK 0x1
+#define DL2_HD_ALIGN_MASK_SFT (0x1 << 2)
+#define DL1_HD_ALIGN_SFT 0
+#define DL1_HD_ALIGN_MASK 0x1
+#define DL1_HD_ALIGN_MASK_SFT (0x1 << 0)
+
+/* PCM_INTF_CON1 */
+#define PCM_FIX_VALUE_SEL_SFT 31
+#define PCM_FIX_VALUE_SEL_MASK 0x1
+#define PCM_FIX_VALUE_SEL_MASK_SFT (0x1 << 31)
+#define PCM_BUFFER_LOOPBACK_SFT 30
+#define PCM_BUFFER_LOOPBACK_MASK 0x1
+#define PCM_BUFFER_LOOPBACK_MASK_SFT (0x1 << 30)
+#define PCM_PARALLEL_LOOPBACK_SFT 29
+#define PCM_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 29)
+#define PCM_SERIAL_LOOPBACK_SFT 28
+#define PCM_SERIAL_LOOPBACK_MASK 0x1
+#define PCM_SERIAL_LOOPBACK_MASK_SFT (0x1 << 28)
+#define PCM_DAI_PCM_LOOPBACK_SFT 27
+#define PCM_DAI_PCM_LOOPBACK_MASK 0x1
+#define PCM_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 27)
+#define PCM_I2S_PCM_LOOPBACK_SFT 26
+#define PCM_I2S_PCM_LOOPBACK_MASK 0x1
+#define PCM_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 26)
+#define PCM_SYNC_DELSEL_SFT 25
+#define PCM_SYNC_DELSEL_MASK 0x1
+#define PCM_SYNC_DELSEL_MASK_SFT (0x1 << 25)
+#define PCM_TX_LR_SWAP_SFT 24
+#define PCM_TX_LR_SWAP_MASK 0x1
+#define PCM_TX_LR_SWAP_MASK_SFT (0x1 << 24)
+#define PCM_SYNC_OUT_INV_SFT 23
+#define PCM_SYNC_OUT_INV_MASK 0x1
+#define PCM_SYNC_OUT_INV_MASK_SFT (0x1 << 23)
+#define PCM_BCLK_OUT_INV_SFT 22
+#define PCM_BCLK_OUT_INV_MASK 0x1
+#define PCM_BCLK_OUT_INV_MASK_SFT (0x1 << 22)
+#define PCM_SYNC_IN_INV_SFT 21
+#define PCM_SYNC_IN_INV_MASK 0x1
+#define PCM_SYNC_IN_INV_MASK_SFT (0x1 << 21)
+#define PCM_BCLK_IN_INV_SFT 20
+#define PCM_BCLK_IN_INV_MASK 0x1
+#define PCM_BCLK_IN_INV_MASK_SFT (0x1 << 20)
+#define PCM_TX_LCH_RPT_SFT 19
+#define PCM_TX_LCH_RPT_MASK 0x1
+#define PCM_TX_LCH_RPT_MASK_SFT (0x1 << 19)
+#define PCM_VBT_16K_MODE_SFT 18
+#define PCM_VBT_16K_MODE_MASK 0x1
+#define PCM_VBT_16K_MODE_MASK_SFT (0x1 << 18)
+#define PCM_EXT_MODEM_SFT 17
+#define PCM_EXT_MODEM_MASK 0x1
+#define PCM_EXT_MODEM_MASK_SFT (0x1 << 17)
+#define PCM_24BIT_SFT 16
+#define PCM_24BIT_MASK 0x1
+#define PCM_24BIT_MASK_SFT (0x1 << 16)
+#define PCM_WLEN_SFT 14
+#define PCM_WLEN_MASK 0x3
+#define PCM_WLEN_MASK_SFT (0x3 << 14)
+#define PCM_SYNC_LENGTH_SFT 9
+#define PCM_SYNC_LENGTH_MASK 0x1f
+#define PCM_SYNC_LENGTH_MASK_SFT (0x1f << 9)
+#define PCM_SYNC_TYPE_SFT 8
+#define PCM_SYNC_TYPE_MASK 0x1
+#define PCM_SYNC_TYPE_MASK_SFT (0x1 << 8)
+#define PCM_BT_MODE_SFT 7
+#define PCM_BT_MODE_MASK 0x1
+#define PCM_BT_MODE_MASK_SFT (0x1 << 7)
+#define PCM_BYP_ASRC_SFT 6
+#define PCM_BYP_ASRC_MASK 0x1
+#define PCM_BYP_ASRC_MASK_SFT (0x1 << 6)
+#define PCM_SLAVE_SFT 5
+#define PCM_SLAVE_MASK 0x1
+#define PCM_SLAVE_MASK_SFT (0x1 << 5)
+#define PCM_MODE_SFT 3
+#define PCM_MODE_MASK 0x3
+#define PCM_MODE_MASK_SFT (0x3 << 3)
+#define PCM_FMT_SFT 1
+#define PCM_FMT_MASK 0x3
+#define PCM_FMT_MASK_SFT (0x3 << 1)
+#define PCM_EN_SFT 0
+#define PCM_EN_MASK 0x1
+#define PCM_EN_MASK_SFT (0x1 << 0)
+
+/* PCM_INTF_CON2 */
+#define PCM1_TX_FIFO_OV_SFT 31
+#define PCM1_TX_FIFO_OV_MASK 0x1
+#define PCM1_TX_FIFO_OV_MASK_SFT (0x1 << 31)
+#define PCM1_RX_FIFO_OV_SFT 30
+#define PCM1_RX_FIFO_OV_MASK 0x1
+#define PCM1_RX_FIFO_OV_MASK_SFT (0x1 << 30)
+#define PCM2_TX_FIFO_OV_SFT 29
+#define PCM2_TX_FIFO_OV_MASK 0x1
+#define PCM2_TX_FIFO_OV_MASK_SFT (0x1 << 29)
+#define PCM2_RX_FIFO_OV_SFT 28
+#define PCM2_RX_FIFO_OV_MASK 0x1
+#define PCM2_RX_FIFO_OV_MASK_SFT (0x1 << 28)
+#define PCM1_SYNC_GLITCH_SFT 27
+#define PCM1_SYNC_GLITCH_MASK 0x1
+#define PCM1_SYNC_GLITCH_MASK_SFT (0x1 << 27)
+#define PCM2_SYNC_GLITCH_SFT 26
+#define PCM2_SYNC_GLITCH_MASK 0x1
+#define PCM2_SYNC_GLITCH_MASK_SFT (0x1 << 26)
+#define TX3_RCH_DBG_MODE_SFT 17
+#define TX3_RCH_DBG_MODE_MASK 0x1
+#define TX3_RCH_DBG_MODE_MASK_SFT (0x1 << 17)
+#define PCM1_PCM2_LOOPBACK_SFT 16
+#define PCM1_PCM2_LOOPBACK_MASK 0x1
+#define PCM1_PCM2_LOOPBACK_MASK_SFT (0x1 << 16)
+#define DAI_PCM_LOOPBACK_CH_SFT 14
+#define DAI_PCM_LOOPBACK_CH_MASK 0x3
+#define DAI_PCM_LOOPBACK_CH_MASK_SFT (0x3 << 14)
+#define I2S_PCM_LOOPBACK_CH_SFT 12
+#define I2S_PCM_LOOPBACK_CH_MASK 0x3
+#define I2S_PCM_LOOPBACK_CH_MASK_SFT (0x3 << 12)
+#define TX_FIX_VALUE_SFT 0
+#define TX_FIX_VALUE_MASK 0xff
+#define TX_FIX_VALUE_MASK_SFT (0xff << 0)
+
+/* PCM2_INTF_CON */
+#define PCM2_TX_FIX_VALUE_SFT 24
+#define PCM2_TX_FIX_VALUE_MASK 0xff
+#define PCM2_TX_FIX_VALUE_MASK_SFT (0xff << 24)
+#define PCM2_FIX_VALUE_SEL_SFT 23
+#define PCM2_FIX_VALUE_SEL_MASK 0x1
+#define PCM2_FIX_VALUE_SEL_MASK_SFT (0x1 << 23)
+#define PCM2_BUFFER_LOOPBACK_SFT 22
+#define PCM2_BUFFER_LOOPBACK_MASK 0x1
+#define PCM2_BUFFER_LOOPBACK_MASK_SFT (0x1 << 22)
+#define PCM2_PARALLEL_LOOPBACK_SFT 21
+#define PCM2_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM2_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 21)
+#define PCM2_SERIAL_LOOPBACK_SFT 20
+#define PCM2_SERIAL_LOOPBACK_MASK 0x1
+#define PCM2_SERIAL_LOOPBACK_MASK_SFT (0x1 << 20)
+#define PCM2_DAI_PCM_LOOPBACK_SFT 19
+#define PCM2_DAI_PCM_LOOPBACK_MASK 0x1
+#define PCM2_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 19)
+#define PCM2_I2S_PCM_LOOPBACK_SFT 18
+#define PCM2_I2S_PCM_LOOPBACK_MASK 0x1
+#define PCM2_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 18)
+#define PCM2_SYNC_DELSEL_SFT 17
+#define PCM2_SYNC_DELSEL_MASK 0x1
+#define PCM2_SYNC_DELSEL_MASK_SFT (0x1 << 17)
+#define PCM2_TX_LR_SWAP_SFT 16
+#define PCM2_TX_LR_SWAP_MASK 0x1
+#define PCM2_TX_LR_SWAP_MASK_SFT (0x1 << 16)
+#define PCM2_SYNC_IN_INV_SFT 15
+#define PCM2_SYNC_IN_INV_MASK 0x1
+#define PCM2_SYNC_IN_INV_MASK_SFT (0x1 << 15)
+#define PCM2_BCLK_IN_INV_SFT 14
+#define PCM2_BCLK_IN_INV_MASK 0x1
+#define PCM2_BCLK_IN_INV_MASK_SFT (0x1 << 14)
+#define PCM2_TX_LCH_RPT_SFT 13
+#define PCM2_TX_LCH_RPT_MASK 0x1
+#define PCM2_TX_LCH_RPT_MASK_SFT (0x1 << 13)
+#define PCM2_VBT_16K_MODE_SFT 12
+#define PCM2_VBT_16K_MODE_MASK 0x1
+#define PCM2_VBT_16K_MODE_MASK_SFT (0x1 << 12)
+#define PCM2_LOOPBACK_CH_SEL_SFT 10
+#define PCM2_LOOPBACK_CH_SEL_MASK 0x3
+#define PCM2_LOOPBACK_CH_SEL_MASK_SFT (0x3 << 10)
+#define PCM2_TX2_BT_MODE_SFT 8
+#define PCM2_TX2_BT_MODE_MASK 0x1
+#define PCM2_TX2_BT_MODE_MASK_SFT (0x1 << 8)
+#define PCM2_BT_MODE_SFT 7
+#define PCM2_BT_MODE_MASK 0x1
+#define PCM2_BT_MODE_MASK_SFT (0x1 << 7)
+#define PCM2_AFIFO_SFT 6
+#define PCM2_AFIFO_MASK 0x1
+#define PCM2_AFIFO_MASK_SFT (0x1 << 6)
+#define PCM2_WLEN_SFT 5
+#define PCM2_WLEN_MASK 0x1
+#define PCM2_WLEN_MASK_SFT (0x1 << 5)
+#define PCM2_MODE_SFT 3
+#define PCM2_MODE_MASK 0x3
+#define PCM2_MODE_MASK_SFT (0x3 << 3)
+#define PCM2_FMT_SFT 1
+#define PCM2_FMT_MASK 0x3
+#define PCM2_FMT_MASK_SFT (0x3 << 1)
+#define PCM2_EN_SFT 0
+#define PCM2_EN_MASK 0x1
+#define PCM2_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define MTKAIF_RXIF_CLKINV_ADC_SFT 31
+#define MTKAIF_RXIF_CLKINV_ADC_MASK 0x1
+#define MTKAIF_RXIF_CLKINV_ADC_MASK_SFT (0x1 << 31)
+#define MTKAIF_RXIF_BYPASS_SRC_SFT 17
+#define MTKAIF_RXIF_BYPASS_SRC_MASK 0x1
+#define MTKAIF_RXIF_BYPASS_SRC_MASK_SFT (0x1 << 17)
+#define MTKAIF_RXIF_PROTOCOL2_SFT 16
+#define MTKAIF_RXIF_PROTOCOL2_MASK 0x1
+#define MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 16)
+#define MTKAIF_TXIF_BYPASS_SRC_SFT 5
+#define MTKAIF_TXIF_BYPASS_SRC_MASK 0x1
+#define MTKAIF_TXIF_BYPASS_SRC_MASK_SFT (0x1 << 5)
+#define MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define MTKAIF_TXIF_8TO5_SFT 2
+#define MTKAIF_TXIF_8TO5_MASK 0x1
+#define MTKAIF_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define MTKAIF_RXIF_8TO5_SFT 1
+#define MTKAIF_RXIF_8TO5_MASK 0x1
+#define MTKAIF_RXIF_8TO5_MASK_SFT (0x1 << 1)
+#define MTKAIF_IF_LOOPBACK1_SFT 0
+#define MTKAIF_IF_LOOPBACK1_MASK 0x1
+#define MTKAIF_IF_LOOPBACK1_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 16
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 16)
+#define MTKAIF_RXIF_DELAY_CYCLE_SFT 12
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK 0xf
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT (0xf << 12)
+#define MTKAIF_RXIF_DELAY_DATA_SFT 8
+#define MTKAIF_RXIF_DELAY_DATA_MASK 0x1
+#define MTKAIF_RXIF_DELAY_DATA_MASK_SFT (0x1 << 8)
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4)
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL_SFT 28
+#define DL_2_INPUT_MODE_CTL_MASK 0xf
+#define DL_2_INPUT_MODE_CTL_MASK_SFT (0xf << 28)
+#define DL_2_CH1_SATURATION_EN_CTL_SFT 27
+#define DL_2_CH1_SATURATION_EN_CTL_MASK 0x1
+#define DL_2_CH1_SATURATION_EN_CTL_MASK_SFT (0x1 << 27)
+#define DL_2_CH2_SATURATION_EN_CTL_SFT 26
+#define DL_2_CH2_SATURATION_EN_CTL_MASK 0x1
+#define DL_2_CH2_SATURATION_EN_CTL_MASK_SFT (0x1 << 26)
+#define DL_2_OUTPUT_SEL_CTL_SFT 24
+#define DL_2_OUTPUT_SEL_CTL_MASK 0x3
+#define DL_2_OUTPUT_SEL_CTL_MASK_SFT (0x3 << 24)
+#define DL_2_FADEIN_0START_EN_SFT 16
+#define DL_2_FADEIN_0START_EN_MASK 0x3
+#define DL_2_FADEIN_0START_EN_MASK_SFT (0x3 << 16)
+#define DL_DISABLE_HW_CG_CTL_SFT 15
+#define DL_DISABLE_HW_CG_CTL_MASK 0x1
+#define DL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 15)
+#define C_DATA_EN_SEL_CTL_PRE_SFT 14
+#define C_DATA_EN_SEL_CTL_PRE_MASK 0x1
+#define C_DATA_EN_SEL_CTL_PRE_MASK_SFT (0x1 << 14)
+#define DL_2_SIDE_TONE_ON_CTL_PRE_SFT 13
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK 0x1
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK_SFT (0x1 << 13)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_SFT 12
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK 0x1
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK_SFT (0x1 << 12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_SFT 11
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK 0x1
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK_SFT (0x1 << 11)
+#define DL2_ARAMPSP_CTL_PRE_SFT 9
+#define DL2_ARAMPSP_CTL_PRE_MASK 0x3
+#define DL2_ARAMPSP_CTL_PRE_MASK_SFT (0x3 << 9)
+#define DL_2_IIRMODE_CTL_PRE_SFT 6
+#define DL_2_IIRMODE_CTL_PRE_MASK 0x7
+#define DL_2_IIRMODE_CTL_PRE_MASK_SFT (0x7 << 6)
+#define DL_2_VOICE_MODE_CTL_PRE_SFT 5
+#define DL_2_VOICE_MODE_CTL_PRE_MASK 0x1
+#define DL_2_VOICE_MODE_CTL_PRE_MASK_SFT (0x1 << 5)
+#define D2_2_MUTE_CH1_ON_CTL_PRE_SFT 4
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK 0x1
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK_SFT (0x1 << 4)
+#define D2_2_MUTE_CH2_ON_CTL_PRE_SFT 3
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK 0x1
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK_SFT (0x1 << 3)
+#define DL_2_IIR_ON_CTL_PRE_SFT 2
+#define DL_2_IIR_ON_CTL_PRE_MASK 0x1
+#define DL_2_IIR_ON_CTL_PRE_MASK_SFT (0x1 << 2)
+#define DL_2_GAIN_ON_CTL_PRE_SFT 1
+#define DL_2_GAIN_ON_CTL_PRE_MASK 0x1
+#define DL_2_GAIN_ON_CTL_PRE_MASK_SFT (0x1 << 1)
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE_SFT 16
+#define DL_2_GAIN_CTL_PRE_MASK 0xffff
+#define DL_2_GAIN_CTL_PRE_MASK_SFT (0xffff << 16)
+#define DL_2_GAIN_MODE_CTL_SFT 0
+#define DL_2_GAIN_MODE_CTL_MASK 0x1
+#define DL_2_GAIN_MODE_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define ULCF_CFG_EN_CTL_SFT 31
+#define ULCF_CFG_EN_CTL_MASK 0x1
+#define ULCF_CFG_EN_CTL_MASK_SFT (0x1 << 31)
+#define UL_MODE_3P25M_CH2_CTL_SFT 22
+#define UL_MODE_3P25M_CH2_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22)
+#define UL_MODE_3P25M_CH1_CTL_SFT 21
+#define UL_MODE_3P25M_CH1_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21)
+#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17)
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define UL_DISABLE_HW_CG_CTL_SFT 12
+#define UL_DISABLE_HW_CG_CTL_MASK 0x1
+#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12)
+#define UL_IIR_ON_TMP_CTL_SFT 10
+#define UL_IIR_ON_TMP_CTL_MASK 0x1
+#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10)
+#define UL_IIRMODE_CTL_SFT 7
+#define UL_IIRMODE_CTL_MASK 0x7
+#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_SRC_CON1 */
+#define C_DAC_EN_CTL_SFT 27
+#define C_DAC_EN_CTL_MASK 0x1
+#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27)
+#define C_MUTE_SW_CTL_SFT 26
+#define C_MUTE_SW_CTL_MASK 0x1
+#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26)
+#define ASDM_SRC_SEL_CTL_SFT 25
+#define ASDM_SRC_SEL_CTL_MASK 0x1
+#define ASDM_SRC_SEL_CTL_MASK_SFT (0x1 << 25)
+#define C_AMP_DIV_CH2_CTL_SFT 21
+#define C_AMP_DIV_CH2_CTL_MASK 0x7
+#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21)
+#define C_FREQ_DIV_CH2_CTL_SFT 16
+#define C_FREQ_DIV_CH2_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16)
+#define C_SINE_MODE_CH2_CTL_SFT 12
+#define C_SINE_MODE_CH2_CTL_MASK 0xf
+#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12)
+#define C_AMP_DIV_CH1_CTL_SFT 9
+#define C_AMP_DIV_CH1_CTL_MASK 0x7
+#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9)
+#define C_FREQ_DIV_CH1_CTL_SFT 4
+#define C_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4)
+#define C_SINE_MODE_CH1_CTL_SFT 0
+#define C_SINE_MODE_CH1_CTL_MASK 0xf
+#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOP_BACK_MODE_CTL_SFT 12
+#define C_LOOP_BACK_MODE_CTL_MASK 0xf
+#define C_LOOP_BACK_MODE_CTL_MASK_SFT (0xf << 12)
+#define C_EXT_ADC_CTL_SFT 0
+#define C_EXT_ADC_CTL_MASK 0x1
+#define C_EXT_ADC_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define AFE_ADDA6_UL_LR_SWAP_SFT 15
+#define AFE_ADDA6_UL_LR_SWAP_MASK 0x1
+#define AFE_ADDA6_UL_LR_SWAP_MASK_SFT (0x1 << 15)
+#define AFE_ADDA6_CKDIV_RST_SFT 14
+#define AFE_ADDA6_CKDIV_RST_MASK 0x1
+#define AFE_ADDA6_CKDIV_RST_MASK_SFT (0x1 << 14)
+#define AFE_ADDA6_FIFO_AUTO_RST_SFT 13
+#define AFE_ADDA6_FIFO_AUTO_RST_MASK 0x1
+#define AFE_ADDA6_FIFO_AUTO_RST_MASK_SFT (0x1 << 13)
+#define UL_FIFO_DIGMIC_TESTIN_SFT 5
+#define UL_FIFO_DIGMIC_TESTIN_MASK 0x3
+#define UL_FIFO_DIGMIC_TESTIN_MASK_SFT (0x3 << 5)
+#define UL_FIFO_DIGMIC_WDATA_TESTEN_SFT 4
+#define UL_FIFO_DIGMIC_WDATA_TESTEN_MASK 0x1
+#define UL_FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT (0x1 << 4)
+#define ADDA_AFE_ON_SFT 0
+#define ADDA_AFE_ON_MASK 0x1
+#define ADDA_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_SIDETONE_CON0 */
+#define R_RDY_SFT 30
+#define R_RDY_MASK 0x1
+#define R_RDY_MASK_SFT (0x1 << 30)
+#define W_RDY_SFT 29
+#define W_RDY_MASK 0x1
+#define W_RDY_MASK_SFT (0x1 << 29)
+#define R_W_EN_SFT 25
+#define R_W_EN_MASK 0x1
+#define R_W_EN_MASK_SFT (0x1 << 25)
+#define R_W_SEL_SFT 24
+#define R_W_SEL_MASK 0x1
+#define R_W_SEL_MASK_SFT (0x1 << 24)
+#define SEL_CH2_SFT 23
+#define SEL_CH2_MASK 0x1
+#define SEL_CH2_MASK_SFT (0x1 << 23)
+#define SIDE_TONE_COEFFICIENT_ADDR_SFT 16
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK 0x1f
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK_SFT (0x1f << 16)
+#define SIDE_TONE_COEFFICIENT_SFT 0
+#define SIDE_TONE_COEFFICIENT_MASK 0xffff
+#define SIDE_TONE_COEFFICIENT_MASK_SFT (0xffff << 0)
+
+/* AFE_SIDETONE_COEFF */
+#define SIDE_TONE_COEFF_SFT 0
+#define SIDE_TONE_COEFF_MASK 0xffff
+#define SIDE_TONE_COEFF_MASK_SFT (0xffff << 0)
+
+/* AFE_SIDETONE_CON1 */
+#define STF_BYPASS_MODE_SFT 31
+#define STF_BYPASS_MODE_MASK 0x1
+#define STF_BYPASS_MODE_MASK_SFT (0x1 << 31)
+#define STF_BYPASS_MODE_O28_O29_SFT 30
+#define STF_BYPASS_MODE_O28_O29_MASK 0x1
+#define STF_BYPASS_MODE_O28_O29_MASK_SFT (0x1 << 30)
+#define STF_BYPASS_MODE_I2S4_SFT 29
+#define STF_BYPASS_MODE_I2S4_MASK 0x1
+#define STF_BYPASS_MODE_I2S4_MASK_SFT (0x1 << 29)
+#define STF_BYPASS_MODE_I2S5_SFT 28
+#define STF_BYPASS_MODE_I2S5_MASK 0x1
+#define STF_BYPASS_MODE_I2S5_MASK_SFT (0x1 << 28)
+#define STF_INPUT_EN_SEL_SFT 13
+#define STF_INPUT_EN_SEL_MASK 0x1
+#define STF_INPUT_EN_SEL_MASK_SFT (0x1 << 13)
+#define STF_SOURCE_FROM_O19O20_SFT 12
+#define STF_SOURCE_FROM_O19O20_MASK 0x1
+#define STF_SOURCE_FROM_O19O20_MASK_SFT (0x1 << 12)
+#define SIDE_TONE_ON_SFT 8
+#define SIDE_TONE_ON_MASK 0x1
+#define SIDE_TONE_ON_MASK_SFT (0x1 << 8)
+#define SIDE_TONE_HALF_TAP_NUM_SFT 0
+#define SIDE_TONE_HALF_TAP_NUM_MASK 0x3f
+#define SIDE_TONE_HALF_TAP_NUM_MASK_SFT (0x3f << 0)
+
+/* AFE_SIDETONE_GAIN */
+#define POSITIVE_GAIN_SFT 16
+#define POSITIVE_GAIN_MASK 0x7
+#define POSITIVE_GAIN_MASK_SFT (0x7 << 16)
+#define SIDE_TONE_GAIN_SFT 0
+#define SIDE_TONE_GAIN_MASK 0xffff
+#define SIDE_TONE_GAIN_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define AUD_DC_COMP_EN_SFT 8
+#define AUD_DC_COMP_EN_MASK 0x1
+#define AUD_DC_COMP_EN_MASK_SFT (0x1 << 8)
+#define ATTGAIN_CTL_SFT 0
+#define ATTGAIN_CTL_MASK 0x3f
+#define ATTGAIN_CTL_MASK_SFT (0x3f << 0)
+
+/* AFE_SINEGEN_CON0 */
+#define DAC_EN_SFT 26
+#define DAC_EN_MASK 0x1
+#define DAC_EN_MASK_SFT (0x1 << 26)
+#define MUTE_SW_CH2_SFT 25
+#define MUTE_SW_CH2_MASK 0x1
+#define MUTE_SW_CH2_MASK_SFT (0x1 << 25)
+#define MUTE_SW_CH1_SFT 24
+#define MUTE_SW_CH1_MASK 0x1
+#define MUTE_SW_CH1_MASK_SFT (0x1 << 24)
+#define SINE_MODE_CH2_SFT 20
+#define SINE_MODE_CH2_MASK 0xf
+#define SINE_MODE_CH2_MASK_SFT (0xf << 20)
+#define AMP_DIV_CH2_SFT 17
+#define AMP_DIV_CH2_MASK 0x7
+#define AMP_DIV_CH2_MASK_SFT (0x7 << 17)
+#define FREQ_DIV_CH2_SFT 12
+#define FREQ_DIV_CH2_MASK 0x1f
+#define FREQ_DIV_CH2_MASK_SFT (0x1f << 12)
+#define SINE_MODE_CH1_SFT 8
+#define SINE_MODE_CH1_MASK 0xf
+#define SINE_MODE_CH1_MASK_SFT (0xf << 8)
+#define AMP_DIV_CH1_SFT 5
+#define AMP_DIV_CH1_MASK 0x7
+#define AMP_DIV_CH1_MASK_SFT (0x7 << 5)
+#define FREQ_DIV_CH1_SFT 0
+#define FREQ_DIV_CH1_MASK 0x1f
+#define FREQ_DIV_CH1_MASK_SFT (0x1f << 0)
+
+/* AFE_SINEGEN_CON2 */
+#define INNER_LOOP_BACK_MODE_SFT 0
+#define INNER_LOOP_BACK_MODE_MASK 0x3f
+#define INNER_LOOP_BACK_MODE_MASK_SFT (0x3f << 0)
+
+/* AFE_MEMIF_MINLEN */
+#define HDMI_MINLEN_SFT 24
+#define HDMI_MINLEN_MASK 0xf
+#define HDMI_MINLEN_MASK_SFT (0xf << 24)
+#define DL3_MINLEN_SFT 12
+#define DL3_MINLEN_MASK 0xf
+#define DL3_MINLEN_MASK_SFT (0xf << 12)
+#define DL2_MINLEN_SFT 8
+#define DL2_MINLEN_MASK 0xf
+#define DL2_MINLEN_MASK_SFT (0xf << 8)
+#define DL1_DATA2_MINLEN_SFT 4
+#define DL1_DATA2_MINLEN_MASK 0xf
+#define DL1_DATA2_MINLEN_MASK_SFT (0xf << 4)
+#define DL1_MINLEN_SFT 0
+#define DL1_MINLEN_MASK 0xf
+#define DL1_MINLEN_MASK_SFT (0xf << 0)
+
+/* AFE_MEMIF_MAXLEN */
+#define HDMI_MAXLEN_SFT 24
+#define HDMI_MAXLEN_MASK 0xf
+#define HDMI_MAXLEN_MASK_SFT (0xf << 24)
+#define DL3_MAXLEN_SFT 8
+#define DL3_MAXLEN_MASK 0xf
+#define DL3_MAXLEN_MASK_SFT (0xf << 8)
+#define DL2_MAXLEN_SFT 4
+#define DL2_MAXLEN_MASK 0xf
+#define DL2_MAXLEN_MASK_SFT (0xf << 4)
+#define DL1_MAXLEN_SFT 0
+#define DL1_MAXLEN_MASK 0x3
+#define DL1_MAXLEN_MASK_SFT (0x3 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE */
+#define VUL12_4CH_SFT 17
+#define VUL12_4CH_MASK 0x1
+#define VUL12_4CH_MASK_SFT (0x1 << 17)
+#define DL3_PBUF_SIZE_SFT 10
+#define DL3_PBUF_SIZE_MASK 0x3
+#define DL3_PBUF_SIZE_MASK_SFT (0x3 << 10)
+#define HDMI_PBUF_SIZE_SFT 4
+#define HDMI_PBUF_SIZE_MASK 0x3
+#define HDMI_PBUF_SIZE_MASK_SFT (0x3 << 4)
+#define DL2_PBUF_SIZE_SFT 2
+#define DL2_PBUF_SIZE_MASK 0x3
+#define DL2_PBUF_SIZE_MASK_SFT (0x3 << 2)
+#define DL1_PBUF_SIZE_SFT 0
+#define DL1_PBUF_SIZE_MASK 0x3
+#define DL1_PBUF_SIZE_MASK_SFT (0x3 << 0)
+
+/* AFE_HD_ENGEN_ENABLE */
+#define AFE_24M_ON_SFT 1
+#define AFE_24M_ON_MASK 0x1
+#define AFE_24M_ON_MASK_SFT (0x1 << 1)
+#define AFE_22M_ON_SFT 0
+#define AFE_22M_ON_MASK 0x1
+#define AFE_22M_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_CON0 */
+#define IRQ12_MCU_ON_SFT 12
+#define IRQ12_MCU_ON_MASK 0x1
+#define IRQ12_MCU_ON_MASK_SFT (0x1 << 12)
+#define IRQ11_MCU_ON_SFT 11
+#define IRQ11_MCU_ON_MASK 0x1
+#define IRQ11_MCU_ON_MASK_SFT (0x1 << 11)
+#define IRQ10_MCU_ON_SFT 10
+#define IRQ10_MCU_ON_MASK 0x1
+#define IRQ10_MCU_ON_MASK_SFT (0x1 << 10)
+#define IRQ9_MCU_ON_SFT 9
+#define IRQ9_MCU_ON_MASK 0x1
+#define IRQ9_MCU_ON_MASK_SFT (0x1 << 9)
+#define IRQ8_MCU_ON_SFT 8
+#define IRQ8_MCU_ON_MASK 0x1
+#define IRQ8_MCU_ON_MASK_SFT (0x1 << 8)
+#define IRQ7_MCU_ON_SFT 7
+#define IRQ7_MCU_ON_MASK 0x1
+#define IRQ7_MCU_ON_MASK_SFT (0x1 << 7)
+#define IRQ6_MCU_ON_SFT 6
+#define IRQ6_MCU_ON_MASK 0x1
+#define IRQ6_MCU_ON_MASK_SFT (0x1 << 6)
+#define IRQ5_MCU_ON_SFT 5
+#define IRQ5_MCU_ON_MASK 0x1
+#define IRQ5_MCU_ON_MASK_SFT (0x1 << 5)
+#define IRQ4_MCU_ON_SFT 4
+#define IRQ4_MCU_ON_MASK 0x1
+#define IRQ4_MCU_ON_MASK_SFT (0x1 << 4)
+#define IRQ3_MCU_ON_SFT 3
+#define IRQ3_MCU_ON_MASK 0x1
+#define IRQ3_MCU_ON_MASK_SFT (0x1 << 3)
+#define IRQ2_MCU_ON_SFT 2
+#define IRQ2_MCU_ON_MASK 0x1
+#define IRQ2_MCU_ON_MASK_SFT (0x1 << 2)
+#define IRQ1_MCU_ON_SFT 1
+#define IRQ1_MCU_ON_MASK 0x1
+#define IRQ1_MCU_ON_MASK_SFT (0x1 << 1)
+#define IRQ0_MCU_ON_SFT 0
+#define IRQ0_MCU_ON_MASK 0x1
+#define IRQ0_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_CON1 */
+#define IRQ7_MCU_MODE_SFT 28
+#define IRQ7_MCU_MODE_MASK 0xf
+#define IRQ7_MCU_MODE_MASK_SFT (0xf << 28)
+#define IRQ6_MCU_MODE_SFT 24
+#define IRQ6_MCU_MODE_MASK 0xf
+#define IRQ6_MCU_MODE_MASK_SFT (0xf << 24)
+#define IRQ5_MCU_MODE_SFT 20
+#define IRQ5_MCU_MODE_MASK 0xf
+#define IRQ5_MCU_MODE_MASK_SFT (0xf << 20)
+#define IRQ4_MCU_MODE_SFT 16
+#define IRQ4_MCU_MODE_MASK 0xf
+#define IRQ4_MCU_MODE_MASK_SFT (0xf << 16)
+#define IRQ3_MCU_MODE_SFT 12
+#define IRQ3_MCU_MODE_MASK 0xf
+#define IRQ3_MCU_MODE_MASK_SFT (0xf << 12)
+#define IRQ2_MCU_MODE_SFT 8
+#define IRQ2_MCU_MODE_MASK 0xf
+#define IRQ2_MCU_MODE_MASK_SFT (0xf << 8)
+#define IRQ1_MCU_MODE_SFT 4
+#define IRQ1_MCU_MODE_MASK 0xf
+#define IRQ1_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ0_MCU_MODE_SFT 0
+#define IRQ0_MCU_MODE_MASK 0xf
+#define IRQ0_MCU_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_IRQ_MCU_CON2 */
+#define IRQ12_MCU_MODE_SFT 4
+#define IRQ12_MCU_MODE_MASK 0xf
+#define IRQ12_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ11_MCU_MODE_SFT 0
+#define IRQ11_MCU_MODE_MASK 0xf
+#define IRQ11_MCU_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_IRQ_MCU_CLR */
+#define IRQ12_MCU_MISS_CNT_CLR_SFT 28
+#define IRQ12_MCU_MISS_CNT_CLR_MASK 0x1
+#define IRQ12_MCU_MISS_CNT_CLR_MASK_SFT (0x1 << 28)
+#define IRQ11_MCU_MISS_CNT_CLR_SFT 27
+#define IRQ11_MCU_MISS_CNT_CLR_MASK 0x1
+#define IRQ11_MCU_MISS_CNT_CLR_MASK_SFT (0x1 << 27)
+#define IRQ10_MCU_MISS_CLR_SFT 26
+#define IRQ10_MCU_MISS_CLR_MASK 0x1
+#define IRQ10_MCU_MISS_CLR_MASK_SFT (0x1 << 26)
+#define IRQ9_MCU_MISS_CLR_SFT 25
+#define IRQ9_MCU_MISS_CLR_MASK 0x1
+#define IRQ9_MCU_MISS_CLR_MASK_SFT (0x1 << 25)
+#define IRQ8_MCU_MISS_CLR_SFT 24
+#define IRQ8_MCU_MISS_CLR_MASK 0x1
+#define IRQ8_MCU_MISS_CLR_MASK_SFT (0x1 << 24)
+#define IRQ7_MCU_MISS_CLR_SFT 23
+#define IRQ7_MCU_MISS_CLR_MASK 0x1
+#define IRQ7_MCU_MISS_CLR_MASK_SFT (0x1 << 23)
+#define IRQ6_MCU_MISS_CLR_SFT 22
+#define IRQ6_MCU_MISS_CLR_MASK 0x1
+#define IRQ6_MCU_MISS_CLR_MASK_SFT (0x1 << 22)
+#define IRQ5_MCU_MISS_CLR_SFT 21
+#define IRQ5_MCU_MISS_CLR_MASK 0x1
+#define IRQ5_MCU_MISS_CLR_MASK_SFT (0x1 << 21)
+#define IRQ4_MCU_MISS_CLR_SFT 20
+#define IRQ4_MCU_MISS_CLR_MASK 0x1
+#define IRQ4_MCU_MISS_CLR_MASK_SFT (0x1 << 20)
+#define IRQ3_MCU_MISS_CLR_SFT 19
+#define IRQ3_MCU_MISS_CLR_MASK 0x1
+#define IRQ3_MCU_MISS_CLR_MASK_SFT (0x1 << 19)
+#define IRQ2_MCU_MISS_CLR_SFT 18
+#define IRQ2_MCU_MISS_CLR_MASK 0x1
+#define IRQ2_MCU_MISS_CLR_MASK_SFT (0x1 << 18)
+#define IRQ1_MCU_MISS_CLR_SFT 17
+#define IRQ1_MCU_MISS_CLR_MASK 0x1
+#define IRQ1_MCU_MISS_CLR_MASK_SFT (0x1 << 17)
+#define IRQ0_MCU_MISS_CLR_SFT 16
+#define IRQ0_MCU_MISS_CLR_MASK 0x1
+#define IRQ0_MCU_MISS_CLR_MASK_SFT (0x1 << 16)
+#define IRQ12_MCU_CLR_SFT 12
+#define IRQ12_MCU_CLR_MASK 0x1
+#define IRQ12_MCU_CLR_MASK_SFT (0x1 << 12)
+#define IRQ11_MCU_CLR_SFT 11
+#define IRQ11_MCU_CLR_MASK 0x1
+#define IRQ11_MCU_CLR_MASK_SFT (0x1 << 11)
+#define IRQ10_MCU_CLR_SFT 10
+#define IRQ10_MCU_CLR_MASK 0x1
+#define IRQ10_MCU_CLR_MASK_SFT (0x1 << 10)
+#define IRQ9_MCU_CLR_SFT 9
+#define IRQ9_MCU_CLR_MASK 0x1
+#define IRQ9_MCU_CLR_MASK_SFT (0x1 << 9)
+#define IRQ8_MCU_CLR_SFT 8
+#define IRQ8_MCU_CLR_MASK 0x1
+#define IRQ8_MCU_CLR_MASK_SFT (0x1 << 8)
+#define IRQ7_MCU_CLR_SFT 7
+#define IRQ7_MCU_CLR_MASK 0x1
+#define IRQ7_MCU_CLR_MASK_SFT (0x1 << 7)
+#define IRQ6_MCU_CLR_SFT 6
+#define IRQ6_MCU_CLR_MASK 0x1
+#define IRQ6_MCU_CLR_MASK_SFT (0x1 << 6)
+#define IRQ5_MCU_CLR_SFT 5
+#define IRQ5_MCU_CLR_MASK 0x1
+#define IRQ5_MCU_CLR_MASK_SFT (0x1 << 5)
+#define IRQ4_MCU_CLR_SFT 4
+#define IRQ4_MCU_CLR_MASK 0x1
+#define IRQ4_MCU_CLR_MASK_SFT (0x1 << 4)
+#define IRQ3_MCU_CLR_SFT 3
+#define IRQ3_MCU_CLR_MASK 0x1
+#define IRQ3_MCU_CLR_MASK_SFT (0x1 << 3)
+#define IRQ2_MCU_CLR_SFT 2
+#define IRQ2_MCU_CLR_MASK 0x1
+#define IRQ2_MCU_CLR_MASK_SFT (0x1 << 2)
+#define IRQ1_MCU_CLR_SFT 1
+#define IRQ1_MCU_CLR_MASK 0x1
+#define IRQ1_MCU_CLR_MASK_SFT (0x1 << 1)
+#define IRQ0_MCU_CLR_SFT 0
+#define IRQ0_MCU_CLR_MASK 0x1
+#define IRQ0_MCU_CLR_MASK_SFT (0x1 << 0)
+
+/* AFE_MEMIF_MSB */
+#define CPU_COMPACT_MODE_SFT 29
+#define CPU_COMPACT_MODE_MASK 0x1
+#define CPU_COMPACT_MODE_MASK_SFT (0x1 << 29)
+#define CPU_HD_ALIGN_SFT 28
+#define CPU_HD_ALIGN_MASK 0x1
+#define CPU_HD_ALIGN_MASK_SFT (0x1 << 28)
+#define AWB2_AXI_WR_SIGN_SFT 24
+#define AWB2_AXI_WR_SIGN_MASK 0x1
+#define AWB2_AXI_WR_SIGN_MASK_SFT (0x1 << 24)
+#define VUL2_AXI_WR_SIGN_SFT 22
+#define VUL2_AXI_WR_SIGN_MASK 0x1
+#define VUL2_AXI_WR_SIGN_MASK_SFT (0x1 << 22)
+#define VUL12_AXI_WR_SIGN_SFT 21
+#define VUL12_AXI_WR_SIGN_MASK 0x1
+#define VUL12_AXI_WR_SIGN_MASK_SFT (0x1 << 21)
+#define VUL_AXI_WR_SIGN_SFT 20
+#define VUL_AXI_WR_SIGN_MASK 0x1
+#define VUL_AXI_WR_SIGN_MASK_SFT (0x1 << 20)
+#define MOD_DAI_AXI_WR_SIGN_SFT 18
+#define MOD_DAI_AXI_WR_SIGN_MASK 0x1
+#define MOD_DAI_AXI_WR_SIGN_MASK_SFT (0x1 << 18)
+#define AWB_MSTR_SIGN_SFT 17
+#define AWB_MSTR_SIGN_MASK 0x1
+#define AWB_MSTR_SIGN_MASK_SFT (0x1 << 17)
+#define SYSRAM_SIGN_SFT 16
+#define SYSRAM_SIGN_MASK 0x1
+#define SYSRAM_SIGN_MASK_SFT (0x1 << 16)
+
+/* AFE_HDMI_CONN0 */
+#define HDMI_O_7_SFT 21
+#define HDMI_O_7_MASK 0x7
+#define HDMI_O_7_MASK_SFT (0x7 << 21)
+#define HDMI_O_6_SFT 18
+#define HDMI_O_6_MASK 0x7
+#define HDMI_O_6_MASK_SFT (0x7 << 18)
+#define HDMI_O_5_SFT 15
+#define HDMI_O_5_MASK 0x7
+#define HDMI_O_5_MASK_SFT (0x7 << 15)
+#define HDMI_O_4_SFT 12
+#define HDMI_O_4_MASK 0x7
+#define HDMI_O_4_MASK_SFT (0x7 << 12)
+#define HDMI_O_3_SFT 9
+#define HDMI_O_3_MASK 0x7
+#define HDMI_O_3_MASK_SFT (0x7 << 9)
+#define HDMI_O_2_SFT 6
+#define HDMI_O_2_MASK 0x7
+#define HDMI_O_2_MASK_SFT (0x7 << 6)
+#define HDMI_O_1_SFT 3
+#define HDMI_O_1_MASK 0x7
+#define HDMI_O_1_MASK_SFT (0x7 << 3)
+#define HDMI_O_0_SFT 0
+#define HDMI_O_0_MASK 0x7
+#define HDMI_O_0_MASK_SFT (0x7 << 0)
+
+/* AFE_TDM_CON1 */
+#define TDM_EN_SFT 0
+#define TDM_EN_MASK 0x1
+#define TDM_EN_MASK_SFT (0x1 << 0)
+#define LRCK_INVERSE_SFT 2
+#define LRCK_INVERSE_MASK 0x1
+#define LRCK_INVERSE_MASK_SFT (0x1 << 2)
+#define DELAY_DATA_SFT 3
+#define DELAY_DATA_MASK 0x1
+#define DELAY_DATA_MASK_SFT (0x1 << 3)
+#define LEFT_ALIGN_SFT 4
+#define LEFT_ALIGN_MASK 0x1
+#define LEFT_ALIGN_MASK_SFT (0x1 << 4)
+#define WLEN_SFT 8
+#define WLEN_MASK 0x3
+#define WLEN_MASK_SFT (0x3 << 8)
+#define CHANNEL_NUM_SFT 10
+#define CHANNEL_NUM_MASK 0x3
+#define CHANNEL_NUM_MASK_SFT (0x3 << 10)
+#define CHANNEL_BCK_CYCLES_SFT 12
+#define CHANNEL_BCK_CYCLES_MASK 0x3
+#define CHANNEL_BCK_CYCLES_MASK_SFT (0x3 << 12)
+#define DAC_BIT_NUM_SFT 16
+#define DAC_BIT_NUM_MASK 0x1f
+#define DAC_BIT_NUM_MASK_SFT (0x1f << 16)
+#define LRCK_TDM_WIDTH_SFT 24
+#define LRCK_TDM_WIDTH_MASK 0xff
+#define LRCK_TDM_WIDTH_MASK_SFT (0xff << 24)
+
+/* AFE_TDM_CON2 */
+#define ST_CH_PAIR_SOUT0_SFT 0
+#define ST_CH_PAIR_SOUT0_MASK 0x7
+#define ST_CH_PAIR_SOUT0_MASK_SFT (0x7 << 0)
+#define ST_CH_PAIR_SOUT1_SFT 4
+#define ST_CH_PAIR_SOUT1_MASK 0x7
+#define ST_CH_PAIR_SOUT1_MASK_SFT (0x7 << 4)
+#define ST_CH_PAIR_SOUT2_SFT 8
+#define ST_CH_PAIR_SOUT2_MASK 0x7
+#define ST_CH_PAIR_SOUT2_MASK_SFT (0x7 << 8)
+#define ST_CH_PAIR_SOUT3_SFT 12
+#define ST_CH_PAIR_SOUT3_MASK 0x7
+#define ST_CH_PAIR_SOUT3_MASK_SFT (0x7 << 12)
+#define TDM_FIX_VALUE_SEL_SFT 16
+#define TDM_FIX_VALUE_SEL_MASK 0x1
+#define TDM_FIX_VALUE_SEL_MASK_SFT (0x1 << 16)
+#define TDM_I2S_LOOPBACK_SFT 20
+#define TDM_I2S_LOOPBACK_MASK 0x1
+#define TDM_I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define TDM_I2S_LOOPBACK_CH_SFT 21
+#define TDM_I2S_LOOPBACK_CH_MASK 0x3
+#define TDM_I2S_LOOPBACK_CH_MASK_SFT (0x3 << 21)
+#define TDM_FIX_VALUE_SFT 24
+#define TDM_FIX_VALUE_MASK 0xff
+#define TDM_FIX_VALUE_MASK_SFT (0xff << 24)
+
+/* AFE_HDMI_OUT_CON0 */
+#define AFE_HDMI_OUT_ON_RETM_SFT 8
+#define AFE_HDMI_OUT_ON_RETM_MASK 0x1
+#define AFE_HDMI_OUT_ON_RETM_MASK_SFT (0x1 << 8)
+#define AFE_HDMI_OUT_CH_NUM_SFT 4
+#define AFE_HDMI_OUT_CH_NUM_MASK 0xf
+#define AFE_HDMI_OUT_CH_NUM_MASK_SFT (0xf << 4)
+#define AFE_HDMI_OUT_BIT_WIDTH_SFT 1
+#define AFE_HDMI_OUT_BIT_WIDTH_MASK 0x1
+#define AFE_HDMI_OUT_BIT_WIDTH_MASK_SFT (0x1 << 1)
+#define AFE_HDMI_OUT_ON_SFT 0
+#define AFE_HDMI_OUT_ON_MASK 0x1
+#define AFE_HDMI_OUT_ON_MASK_SFT (0x1 << 0)
+#endif
diff --git a/sound/soc/mediatek/mt8186/Makefile b/sound/soc/mediatek/mt8186/Makefile
new file mode 100644
index 000000000000..ab3f5b763df8
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/Makefile
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt8186-afe-y := \
+ mt8186-afe-pcm.o \
+ mt8186-audsys-clk.o \
+ mt8186-afe-clk.o \
+ mt8186-afe-gpio.o \
+ mt8186-dai-adda.o \
+ mt8186-afe-control.o \
+ mt8186-dai-i2s.o \
+ mt8186-dai-hw-gain.o \
+ mt8186-dai-pcm.o \
+ mt8186-dai-src.o \
+ mt8186-dai-hostless.o \
+ mt8186-dai-tdm.o \
+ mt8186-misc-control.o \
+ mt8186-mt6366-common.o
+
+obj-$(CONFIG_SND_SOC_MT8186) += snd-soc-mt8186-afe.o
+obj-$(CONFIG_SND_SOC_MT8186_MT6366) += mt8186-mt6366.o
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
new file mode 100644
index 000000000000..daaca36a2d08
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8186-afe-clk.c -- Mediatek 8186 afe clock ctrl
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-clk.h"
+#include "mt8186-audsys-clk.h"
+
+static const char *aud_clks[CLK_NUM] = {
+ [CLK_AFE] = "aud_afe_clk",
+ [CLK_DAC] = "aud_dac_clk",
+ [CLK_DAC_PREDIS] = "aud_dac_predis_clk",
+ [CLK_ADC] = "aud_adc_clk",
+ [CLK_TML] = "aud_tml_clk",
+ [CLK_APLL22M] = "aud_apll22m_clk",
+ [CLK_APLL24M] = "aud_apll24m_clk",
+ [CLK_APLL1_TUNER] = "aud_apll_tuner_clk",
+ [CLK_APLL2_TUNER] = "aud_apll2_tuner_clk",
+ [CLK_TDM] = "aud_tdm_clk",
+ [CLK_NLE] = "aud_nle_clk",
+ [CLK_DAC_HIRES] = "aud_dac_hires_clk",
+ [CLK_ADC_HIRES] = "aud_adc_hires_clk",
+ [CLK_I2S1_BCLK] = "aud_i2s1_bclk",
+ [CLK_I2S2_BCLK] = "aud_i2s2_bclk",
+ [CLK_I2S3_BCLK] = "aud_i2s3_bclk",
+ [CLK_I2S4_BCLK] = "aud_i2s4_bclk",
+ [CLK_CONNSYS_I2S_ASRC] = "aud_connsys_i2s_asrc",
+ [CLK_GENERAL1_ASRC] = "aud_general1_asrc",
+ [CLK_GENERAL2_ASRC] = "aud_general2_asrc",
+ [CLK_ADC_HIRES_TML] = "aud_adc_hires_tml",
+ [CLK_ADDA6_ADC] = "aud_adda6_adc",
+ [CLK_ADDA6_ADC_HIRES] = "aud_adda6_adc_hires",
+ [CLK_3RD_DAC] = "aud_3rd_dac",
+ [CLK_3RD_DAC_PREDIS] = "aud_3rd_dac_predis",
+ [CLK_3RD_DAC_TML] = "aud_3rd_dac_tml",
+ [CLK_3RD_DAC_HIRES] = "aud_3rd_dac_hires",
+ [CLK_ETDM_IN1_BCLK] = "aud_etdm_in1_bclk",
+ [CLK_ETDM_OUT1_BCLK] = "aud_etdm_out1_bclk",
+ [CLK_INFRA_SYS_AUDIO] = "aud_infra_clk",
+ [CLK_INFRA_AUDIO_26M] = "mtkaif_26m_clk",
+ [CLK_MUX_AUDIO] = "top_mux_audio",
+ [CLK_MUX_AUDIOINTBUS] = "top_mux_audio_int",
+ [CLK_TOP_MAINPLL_D2_D4] = "top_mainpll_d2_d4",
+ [CLK_TOP_MUX_AUD_1] = "top_mux_aud_1",
+ [CLK_TOP_APLL1_CK] = "top_apll1_ck",
+ [CLK_TOP_MUX_AUD_2] = "top_mux_aud_2",
+ [CLK_TOP_APLL2_CK] = "top_apll2_ck",
+ [CLK_TOP_MUX_AUD_ENG1] = "top_mux_aud_eng1",
+ [CLK_TOP_APLL1_D8] = "top_apll1_d8",
+ [CLK_TOP_MUX_AUD_ENG2] = "top_mux_aud_eng2",
+ [CLK_TOP_APLL2_D8] = "top_apll2_d8",
+ [CLK_TOP_MUX_AUDIO_H] = "top_mux_audio_h",
+ [CLK_TOP_I2S0_M_SEL] = "top_i2s0_m_sel",
+ [CLK_TOP_I2S1_M_SEL] = "top_i2s1_m_sel",
+ [CLK_TOP_I2S2_M_SEL] = "top_i2s2_m_sel",
+ [CLK_TOP_I2S4_M_SEL] = "top_i2s4_m_sel",
+ [CLK_TOP_TDM_M_SEL] = "top_tdm_m_sel",
+ [CLK_TOP_APLL12_DIV0] = "top_apll12_div0",
+ [CLK_TOP_APLL12_DIV1] = "top_apll12_div1",
+ [CLK_TOP_APLL12_DIV2] = "top_apll12_div2",
+ [CLK_TOP_APLL12_DIV4] = "top_apll12_div4",
+ [CLK_TOP_APLL12_DIV_TDM] = "top_apll12_div_tdm",
+ [CLK_CLK26M] = "top_clk26m_clk",
+};
+
+int mt8186_set_audio_int_bus_parent(struct mtk_base_afe *afe,
+ int clk_id)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIOINTBUS],
+ afe_priv->clk[clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS],
+ aud_clks[clk_id], ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (enable) {
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1], ret);
+ return ret;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_TOP_APLL1_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1],
+ aud_clks[CLK_TOP_APLL1_CK], ret);
+ return ret;
+ }
+
+ /* 180.6336 / 8 = 22.5792MHz */
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret);
+ return ret;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_TOP_APLL1_D8]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
+ aud_clks[CLK_TOP_APLL1_D8], ret);
+ return ret;
+ }
+ } else {
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
+ aud_clks[CLK_CLK26M], ret);
+ return ret;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1],
+ aud_clks[CLK_CLK26M], ret);
+ return ret;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ }
+
+ return 0;
+}
+
+static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (enable) {
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2], ret);
+ return ret;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_TOP_APLL2_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2],
+ aud_clks[CLK_TOP_APLL2_CK], ret);
+ return ret;
+ }
+
+ /* 196.608 / 8 = 24.576MHz */
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret);
+ return ret;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_TOP_APLL2_D8]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
+ aud_clks[CLK_TOP_APLL2_D8], ret);
+ return ret;
+ }
+ } else {
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
+ aud_clks[CLK_CLK26M], ret);
+ return ret;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2],
+ aud_clks[CLK_CLK26M], ret);
+ return ret;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ }
+
+ return 0;
+}
+
+int mt8186_afe_enable_cgs(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+ int i;
+
+ for (i = CLK_I2S1_BCLK; i <= CLK_ETDM_OUT1_BCLK; i++) {
+ ret = clk_prepare_enable(afe_priv->clk[i]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[i], ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void mt8186_afe_disable_cgs(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int i;
+
+ for (i = CLK_I2S1_BCLK; i <= CLK_ETDM_OUT1_BCLK; i++)
+ clk_disable_unprepare(afe_priv->clk[i]);
+}
+
+int mt8186_afe_enable_clock(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_SYS_AUDIO], ret);
+ goto clk_infra_sys_audio_err;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_AUDIO_26M], ret);
+ goto clk_infra_audio_26m_err;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIO], ret);
+ goto clk_mux_audio_err;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIO],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIO],
+ aud_clks[CLK_CLK26M], ret);
+ goto clk_mux_audio_err;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
+ goto clk_mux_audio_intbus_err;
+ }
+ ret = mt8186_set_audio_int_bus_parent(afe,
+ CLK_TOP_MAINPLL_D2_D4);
+ if (ret)
+ goto clk_mux_audio_intbus_parent_err;
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[CLK_TOP_APLL2_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUDIO_H],
+ aud_clks[CLK_TOP_APLL2_CK], ret);
+ goto clk_mux_audio_h_parent_err;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_AFE], ret);
+ goto clk_afe_err;
+ }
+
+ return 0;
+
+clk_afe_err:
+ clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
+clk_mux_audio_h_parent_err:
+clk_mux_audio_intbus_parent_err:
+ mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M);
+clk_mux_audio_intbus_err:
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+clk_mux_audio_err:
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
+clk_infra_sys_audio_err:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+clk_infra_audio_26m_err:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+
+ return ret;
+}
+
+void mt8186_afe_disable_clock(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
+ mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M);
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+}
+
+int mt8186_apll1_enable(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* setting for APLL */
+ apll1_mux_setting(afe, true);
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL22M], ret);
+ goto err_clk_apll22m;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL1_TUNER]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL1_TUNER], ret);
+ goto err_clk_apll1_tuner;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0xfff7, 0x832);
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x1);
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_ON_MASK_SFT, BIT(AFE_22M_ON_SFT));
+
+ return 0;
+
+err_clk_apll1_tuner:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]);
+err_clk_apll22m:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+
+ return ret;
+}
+
+void mt8186_apll1_disable(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_ON_MASK_SFT, 0);
+
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0);
+
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]);
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+
+ apll1_mux_setting(afe, false);
+}
+
+int mt8186_apll2_enable(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* setting for APLL */
+ apll2_mux_setting(afe, true);
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL24M], ret);
+ goto err_clk_apll24m;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL2_TUNER]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL2_TUNER], ret);
+ goto err_clk_apll2_tuner;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0xfff7, 0x634);
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x1);
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_ON_MASK_SFT, BIT(AFE_24M_ON_SFT));
+
+ return 0;
+
+err_clk_apll2_tuner:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]);
+err_clk_apll24m:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+
+ return ret;
+}
+
+void mt8186_apll2_disable(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_ON_MASK_SFT, 0);
+
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0);
+
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]);
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+
+ apll2_mux_setting(afe, false);
+}
+
+int mt8186_get_apll_rate(struct mtk_base_afe *afe, int apll)
+{
+ return (apll == MT8186_APLL1) ? 180633600 : 196608000;
+}
+
+int mt8186_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return ((rate % 8000) == 0) ? MT8186_APLL2 : MT8186_APLL1;
+}
+
+int mt8186_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8186_APLL1;
+
+ return MT8186_APLL2;
+}
+
+/* mck */
+struct mt8186_mck_div {
+ u32 m_sel_id;
+ u32 div_clk_id;
+};
+
+static const struct mt8186_mck_div mck_div[MT8186_MCK_NUM] = {
+ [MT8186_I2S0_MCK] = {
+ .m_sel_id = CLK_TOP_I2S0_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV0,
+ },
+ [MT8186_I2S1_MCK] = {
+ .m_sel_id = CLK_TOP_I2S1_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV1,
+ },
+ [MT8186_I2S2_MCK] = {
+ .m_sel_id = CLK_TOP_I2S2_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV2,
+ },
+ [MT8186_I2S4_MCK] = {
+ .m_sel_id = CLK_TOP_I2S4_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV4,
+ },
+ [MT8186_TDM_MCK] = {
+ .m_sel_id = CLK_TOP_TDM_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV_TDM,
+ },
+};
+
+int mt8186_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int apll = mt8186_get_apll_by_rate(afe, rate);
+ int apll_clk_id = apll == MT8186_APLL1 ?
+ CLK_TOP_MUX_AUD_1 : CLK_TOP_MUX_AUD_2;
+ int m_sel_id = mck_div[mck_id].m_sel_id;
+ int div_clk_id = mck_div[mck_id].div_clk_id;
+ int ret;
+
+ /* select apll */
+ if (m_sel_id >= 0) {
+ ret = clk_prepare_enable(afe_priv->clk[m_sel_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[m_sel_id], ret);
+ return ret;
+ }
+ ret = clk_set_parent(afe_priv->clk[m_sel_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[m_sel_id],
+ aud_clks[apll_clk_id], ret);
+ return ret;
+ }
+ }
+
+ /* enable div, set rate */
+ ret = clk_prepare_enable(afe_priv->clk[div_clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[div_clk_id], ret);
+ return ret;
+ }
+ ret = clk_set_rate(afe_priv->clk[div_clk_id], rate);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n",
+ __func__, aud_clks[div_clk_id], rate, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void mt8186_mck_disable(struct mtk_base_afe *afe, int mck_id)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int m_sel_id = mck_div[mck_id].m_sel_id;
+ int div_clk_id = mck_div[mck_id].div_clk_id;
+
+ clk_disable_unprepare(afe_priv->clk[div_clk_id]);
+ if (m_sel_id >= 0)
+ clk_disable_unprepare(afe_priv->clk[m_sel_id]);
+}
+
+int mt8186_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct device_node *of_node = afe->dev->of_node;
+ int i = 0;
+
+ mt8186_audsys_clk_register(afe);
+
+ afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "%s devm_clk_get %s fail, ret %ld\n",
+ __func__,
+ aud_clks[i], PTR_ERR(afe_priv->clk[i]));
+ afe_priv->clk[i] = NULL;
+ }
+ }
+
+ afe_priv->apmixedsys = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,apmixedsys");
+ if (IS_ERR(afe_priv->apmixedsys)) {
+ dev_err(afe->dev, "%s() Cannot find apmixedsys controller: %ld\n",
+ __func__, PTR_ERR(afe_priv->apmixedsys));
+ return PTR_ERR(afe_priv->apmixedsys);
+ }
+
+ afe_priv->topckgen = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,topckgen");
+ if (IS_ERR(afe_priv->topckgen)) {
+ dev_err(afe->dev, "%s() Cannot find topckgen controller: %ld\n",
+ __func__, PTR_ERR(afe_priv->topckgen));
+ return PTR_ERR(afe_priv->topckgen);
+ }
+
+ afe_priv->infracfg = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,infracfg");
+ if (IS_ERR(afe_priv->infracfg)) {
+ dev_err(afe->dev, "%s() Cannot find infracfg: %ld\n",
+ __func__, PTR_ERR(afe_priv->infracfg));
+ return PTR_ERR(afe_priv->infracfg);
+ }
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h
new file mode 100644
index 000000000000..e524833ce780
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * mt8186-afe-clk.h -- Mediatek 8186 afe clock ctrl definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT8186_AFE_CLOCK_CTRL_H_
+#define _MT8186_AFE_CLOCK_CTRL_H_
+
+#define PERI_BUS_DCM_CTRL 0x74
+
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+enum {
+ MT8186_APLL1 = 0,
+ MT8186_APLL2,
+};
+
+enum {
+ CLK_AFE = 0,
+ CLK_DAC,
+ CLK_DAC_PREDIS,
+ CLK_ADC,
+ CLK_TML,
+ CLK_APLL22M,
+ CLK_APLL24M,
+ CLK_APLL1_TUNER,
+ CLK_APLL2_TUNER,
+ CLK_TDM,
+ CLK_NLE,
+ CLK_DAC_HIRES,
+ CLK_ADC_HIRES,
+ CLK_I2S1_BCLK,
+ CLK_I2S2_BCLK,
+ CLK_I2S3_BCLK,
+ CLK_I2S4_BCLK,
+ CLK_CONNSYS_I2S_ASRC,
+ CLK_GENERAL1_ASRC,
+ CLK_GENERAL2_ASRC,
+ CLK_ADC_HIRES_TML,
+ CLK_ADDA6_ADC,
+ CLK_ADDA6_ADC_HIRES,
+ CLK_3RD_DAC,
+ CLK_3RD_DAC_PREDIS,
+ CLK_3RD_DAC_TML,
+ CLK_3RD_DAC_HIRES,
+ CLK_ETDM_IN1_BCLK,
+ CLK_ETDM_OUT1_BCLK,
+ CLK_INFRA_SYS_AUDIO,
+ CLK_INFRA_AUDIO_26M,
+ CLK_MUX_AUDIO,
+ CLK_MUX_AUDIOINTBUS,
+ CLK_TOP_MAINPLL_D2_D4,
+ /* apll related mux */
+ CLK_TOP_MUX_AUD_1,
+ CLK_TOP_APLL1_CK,
+ CLK_TOP_MUX_AUD_2,
+ CLK_TOP_APLL2_CK,
+ CLK_TOP_MUX_AUD_ENG1,
+ CLK_TOP_APLL1_D8,
+ CLK_TOP_MUX_AUD_ENG2,
+ CLK_TOP_APLL2_D8,
+ CLK_TOP_MUX_AUDIO_H,
+ CLK_TOP_I2S0_M_SEL,
+ CLK_TOP_I2S1_M_SEL,
+ CLK_TOP_I2S2_M_SEL,
+ CLK_TOP_I2S4_M_SEL,
+ CLK_TOP_TDM_M_SEL,
+ CLK_TOP_APLL12_DIV0,
+ CLK_TOP_APLL12_DIV1,
+ CLK_TOP_APLL12_DIV2,
+ CLK_TOP_APLL12_DIV4,
+ CLK_TOP_APLL12_DIV_TDM,
+ CLK_CLK26M,
+ CLK_NUM
+};
+
+struct mtk_base_afe;
+int mt8186_set_audio_int_bus_parent(struct mtk_base_afe *afe, int clk_id);
+int mt8186_init_clock(struct mtk_base_afe *afe);
+int mt8186_afe_enable_cgs(struct mtk_base_afe *afe);
+void mt8186_afe_disable_cgs(struct mtk_base_afe *afe);
+int mt8186_afe_enable_clock(struct mtk_base_afe *afe);
+void mt8186_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt8186_apll1_enable(struct mtk_base_afe *afe);
+void mt8186_apll1_disable(struct mtk_base_afe *afe);
+
+int mt8186_apll2_enable(struct mtk_base_afe *afe);
+void mt8186_apll2_disable(struct mtk_base_afe *afe);
+
+int mt8186_get_apll_rate(struct mtk_base_afe *afe, int apll);
+int mt8186_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8186_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
+
+/* these will be replaced by using CCF */
+int mt8186_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
+void mt8186_mck_disable(struct mtk_base_afe *afe, int mck_id);
+
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-common.h b/sound/soc/mediatek/mt8186/mt8186-afe-common.h
new file mode 100644
index 000000000000..d59258520995
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-common.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * mt8186-afe-common.h -- Mediatek 8186 audio driver definitions
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT_8186_AFE_COMMON_H_
+#define _MT_8186_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include "mt8186-reg.h"
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT8186_MEMIF_DL1,
+ MT8186_MEMIF_DL12,
+ MT8186_MEMIF_DL2,
+ MT8186_MEMIF_DL3,
+ MT8186_MEMIF_DL4,
+ MT8186_MEMIF_DL5,
+ MT8186_MEMIF_DL6,
+ MT8186_MEMIF_DL7,
+ MT8186_MEMIF_DL8,
+ MT8186_MEMIF_VUL12,
+ MT8186_MEMIF_VUL2,
+ MT8186_MEMIF_VUL3,
+ MT8186_MEMIF_VUL4,
+ MT8186_MEMIF_VUL5,
+ MT8186_MEMIF_VUL6,
+ MT8186_MEMIF_AWB,
+ MT8186_MEMIF_AWB2,
+ MT8186_MEMIF_NUM,
+ MT8186_DAI_ADDA = MT8186_MEMIF_NUM,
+ MT8186_DAI_AP_DMIC,
+ MT8186_DAI_CONNSYS_I2S,
+ MT8186_DAI_I2S_0,
+ MT8186_DAI_I2S_1,
+ MT8186_DAI_I2S_2,
+ MT8186_DAI_I2S_3,
+ MT8186_DAI_HW_GAIN_1,
+ MT8186_DAI_HW_GAIN_2,
+ MT8186_DAI_SRC_1,
+ MT8186_DAI_SRC_2,
+ MT8186_DAI_PCM,
+ MT8186_DAI_TDM_IN,
+ MT8186_DAI_HOSTLESS_LPBK,
+ MT8186_DAI_HOSTLESS_FM,
+ MT8186_DAI_HOSTLESS_HW_GAIN_AAUDIO,
+ MT8186_DAI_HOSTLESS_SRC_AAUDIO,
+ MT8186_DAI_HOSTLESS_SRC_1,
+ MT8186_DAI_HOSTLESS_SRC_BARGEIN,
+ MT8186_DAI_HOSTLESS_UL1,
+ MT8186_DAI_HOSTLESS_UL2,
+ MT8186_DAI_HOSTLESS_UL3,
+ MT8186_DAI_HOSTLESS_UL5,
+ MT8186_DAI_HOSTLESS_UL6,
+ MT8186_DAI_NUM,
+};
+
+#define MT8186_RECORD_MEMIF MT8186_MEMIF_VUL12
+#define MT8186_ECHO_REF_MEMIF MT8186_MEMIF_AWB
+#define MT8186_PRIMARY_MEMIF MT8186_MEMIF_DL1
+#define MT8186_FAST_MEMIF MT8186_MEMIF_DL2
+#define MT8186_DEEP_MEMIF MT8186_MEMIF_DL3
+#define MT8186_VOIP_MEMIF MT8186_MEMIF_DL12
+#define MT8186_MMAP_DL_MEMIF MT8186_MEMIF_DL5
+#define MT8186_MMAP_UL_MEMIF MT8186_MEMIF_VUL5
+#define MT8186_BARGEIN_MEMIF MT8186_MEMIF_AWB
+
+enum {
+ MT8186_IRQ_0,
+ MT8186_IRQ_1,
+ MT8186_IRQ_2,
+ MT8186_IRQ_3,
+ MT8186_IRQ_4,
+ MT8186_IRQ_5,
+ MT8186_IRQ_6,
+ MT8186_IRQ_7,
+ MT8186_IRQ_8,
+ MT8186_IRQ_9,
+ MT8186_IRQ_10,
+ MT8186_IRQ_11,
+ MT8186_IRQ_12,
+ MT8186_IRQ_13,
+ MT8186_IRQ_14,
+ MT8186_IRQ_15,
+ MT8186_IRQ_16,
+ MT8186_IRQ_17,
+ MT8186_IRQ_18,
+ MT8186_IRQ_19,
+ MT8186_IRQ_20,
+ MT8186_IRQ_21,
+ MT8186_IRQ_22,
+ MT8186_IRQ_23,
+ MT8186_IRQ_24,
+ MT8186_IRQ_25,
+ MT8186_IRQ_26,
+ MT8186_IRQ_NUM,
+};
+
+enum {
+ MT8186_AFE_IRQ_DIR_MCU = 0,
+ MT8186_AFE_IRQ_DIR_DSP,
+ MT8186_AFE_IRQ_DIR_BOTH,
+};
+
+enum {
+ MTKAIF_PROTOCOL_1 = 0,
+ MTKAIF_PROTOCOL_2,
+ MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+enum {
+ MTK_AFE_ADDA_DL_GAIN_MUTE = 0,
+ MTK_AFE_ADDA_DL_GAIN_NORMAL = 0xf74f,
+ /* SA suggest apply -0.3db to audio/speech path */
+};
+
+#define MTK_SPK_I2S_0_STR "MTK_SPK_I2S_0"
+#define MTK_SPK_I2S_1_STR "MTK_SPK_I2S_1"
+#define MTK_SPK_I2S_2_STR "MTK_SPK_I2S_2"
+#define MTK_SPK_I2S_3_STR "MTK_SPK_I2S_3"
+
+/* MCLK */
+enum {
+ MT8186_I2S0_MCK = 0,
+ MT8186_I2S1_MCK,
+ MT8186_I2S2_MCK,
+ MT8186_I2S4_MCK,
+ MT8186_TDM_MCK,
+ MT8186_MCK_NUM,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+struct clk;
+
+struct mt8186_afe_private {
+ struct clk **clk;
+ struct clk_lookup **lookup;
+ struct regmap *topckgen;
+ struct regmap *apmixedsys;
+ struct regmap *infracfg;
+ int irq_cnt[MT8186_MEMIF_NUM];
+ int stf_positive_gain_db;
+ int pm_runtime_bypass_reg_ctl;
+ int sgen_mode;
+ int sgen_rate;
+ int sgen_amplitude;
+
+ /* xrun assert */
+ int xrun_assert[MT8186_MEMIF_NUM];
+
+ /* dai */
+ bool dai_on[MT8186_DAI_NUM];
+ void *dai_priv[MT8186_DAI_NUM];
+
+ /* adda */
+ bool mtkaif_calibration_ok;
+ int mtkaif_protocol;
+ int mtkaif_chosen_phase[4];
+ int mtkaif_phase_cycle[4];
+ int mtkaif_calibration_num_phase;
+ int mtkaif_dmic;
+ int mtkaif_looback0;
+ int mtkaif_looback1;
+
+ /* mck */
+ int mck_rate[MT8186_MCK_NUM];
+};
+
+int mt8186_dai_adda_register(struct mtk_base_afe *afe);
+int mt8186_dai_i2s_register(struct mtk_base_afe *afe);
+int mt8186_dai_tdm_register(struct mtk_base_afe *afe);
+int mt8186_dai_hw_gain_register(struct mtk_base_afe *afe);
+int mt8186_dai_src_register(struct mtk_base_afe *afe);
+int mt8186_dai_pcm_register(struct mtk_base_afe *afe);
+int mt8186_dai_hostless_register(struct mtk_base_afe *afe);
+
+int mt8186_add_misc_control(struct snd_soc_component *component);
+
+unsigned int mt8186_general_rate_transform(struct device *dev,
+ unsigned int rate);
+unsigned int mt8186_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk);
+unsigned int mt8186_tdm_relatch_rate_transform(struct device *dev,
+ unsigned int rate);
+
+int mt8186_dai_i2s_set_share(struct mtk_base_afe *afe, const char *main_i2s_name,
+ const char *secondary_i2s_name);
+
+int mt8186_dai_set_priv(struct mtk_base_afe *afe, int id,
+ int priv_size, const void *priv_data);
+
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-control.c b/sound/soc/mediatek/mt8186/mt8186-afe-control.c
new file mode 100644
index 000000000000..55edf6374578
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-control.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include "mt8186-afe-common.h"
+
+enum {
+ MTK_AFE_RATE_8K = 0,
+ MTK_AFE_RATE_11K,
+ MTK_AFE_RATE_12K,
+ MTK_AFE_RATE_384K,
+ MTK_AFE_RATE_16K,
+ MTK_AFE_RATE_22K,
+ MTK_AFE_RATE_24K,
+ MTK_AFE_RATE_352K,
+ MTK_AFE_RATE_32K,
+ MTK_AFE_RATE_44K,
+ MTK_AFE_RATE_48K,
+ MTK_AFE_RATE_88K,
+ MTK_AFE_RATE_96K,
+ MTK_AFE_RATE_176K,
+ MTK_AFE_RATE_192K,
+ MTK_AFE_RATE_260K,
+};
+
+enum {
+ MTK_AFE_PCM_RATE_8K = 0,
+ MTK_AFE_PCM_RATE_16K,
+ MTK_AFE_PCM_RATE_32K,
+ MTK_AFE_PCM_RATE_48K,
+};
+
+enum {
+ MTK_AFE_TDM_RATE_8K = 0,
+ MTK_AFE_TDM_RATE_12K,
+ MTK_AFE_TDM_RATE_16K,
+ MTK_AFE_TDM_RATE_24K,
+ MTK_AFE_TDM_RATE_32K,
+ MTK_AFE_TDM_RATE_48K,
+ MTK_AFE_TDM_RATE_64K,
+ MTK_AFE_TDM_RATE_96K,
+ MTK_AFE_TDM_RATE_128K,
+ MTK_AFE_TDM_RATE_192K,
+ MTK_AFE_TDM_RATE_256K,
+ MTK_AFE_TDM_RATE_384K,
+ MTK_AFE_TDM_RATE_11K,
+ MTK_AFE_TDM_RATE_22K,
+ MTK_AFE_TDM_RATE_44K,
+ MTK_AFE_TDM_RATE_88K,
+ MTK_AFE_TDM_RATE_176K,
+ MTK_AFE_TDM_RATE_352K,
+};
+
+enum {
+ MTK_AFE_TDM_RELATCH_RATE_8K = 0,
+ MTK_AFE_TDM_RELATCH_RATE_11K,
+ MTK_AFE_TDM_RELATCH_RATE_12K,
+ MTK_AFE_TDM_RELATCH_RATE_16K,
+ MTK_AFE_TDM_RELATCH_RATE_22K,
+ MTK_AFE_TDM_RELATCH_RATE_24K,
+ MTK_AFE_TDM_RELATCH_RATE_32K,
+ MTK_AFE_TDM_RELATCH_RATE_44K,
+ MTK_AFE_TDM_RELATCH_RATE_48K,
+ MTK_AFE_TDM_RELATCH_RATE_88K,
+ MTK_AFE_TDM_RELATCH_RATE_96K,
+ MTK_AFE_TDM_RELATCH_RATE_176K,
+ MTK_AFE_TDM_RELATCH_RATE_192K,
+ MTK_AFE_TDM_RELATCH_RATE_352K,
+ MTK_AFE_TDM_RELATCH_RATE_384K,
+};
+
+unsigned int mt8186_general_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_RATE_8K;
+ case 11025:
+ return MTK_AFE_RATE_11K;
+ case 12000:
+ return MTK_AFE_RATE_12K;
+ case 16000:
+ return MTK_AFE_RATE_16K;
+ case 22050:
+ return MTK_AFE_RATE_22K;
+ case 24000:
+ return MTK_AFE_RATE_24K;
+ case 32000:
+ return MTK_AFE_RATE_32K;
+ case 44100:
+ return MTK_AFE_RATE_44K;
+ case 48000:
+ return MTK_AFE_RATE_48K;
+ case 88200:
+ return MTK_AFE_RATE_88K;
+ case 96000:
+ return MTK_AFE_RATE_96K;
+ case 176400:
+ return MTK_AFE_RATE_176K;
+ case 192000:
+ return MTK_AFE_RATE_192K;
+ case 260000:
+ return MTK_AFE_RATE_260K;
+ case 352800:
+ return MTK_AFE_RATE_352K;
+ case 384000:
+ return MTK_AFE_RATE_384K;
+ default:
+ dev_err(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_RATE_48K);
+ }
+
+ return MTK_AFE_RATE_48K;
+}
+
+static unsigned int tdm_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_TDM_RATE_8K;
+ case 11025:
+ return MTK_AFE_TDM_RATE_11K;
+ case 12000:
+ return MTK_AFE_TDM_RATE_12K;
+ case 16000:
+ return MTK_AFE_TDM_RATE_16K;
+ case 22050:
+ return MTK_AFE_TDM_RATE_22K;
+ case 24000:
+ return MTK_AFE_TDM_RATE_24K;
+ case 32000:
+ return MTK_AFE_TDM_RATE_32K;
+ case 44100:
+ return MTK_AFE_TDM_RATE_44K;
+ case 48000:
+ return MTK_AFE_TDM_RATE_48K;
+ case 64000:
+ return MTK_AFE_TDM_RATE_64K;
+ case 88200:
+ return MTK_AFE_TDM_RATE_88K;
+ case 96000:
+ return MTK_AFE_TDM_RATE_96K;
+ case 128000:
+ return MTK_AFE_TDM_RATE_128K;
+ case 176400:
+ return MTK_AFE_TDM_RATE_176K;
+ case 192000:
+ return MTK_AFE_TDM_RATE_192K;
+ case 256000:
+ return MTK_AFE_TDM_RATE_256K;
+ case 352800:
+ return MTK_AFE_TDM_RATE_352K;
+ case 384000:
+ return MTK_AFE_TDM_RATE_384K;
+ default:
+ dev_err(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_TDM_RATE_48K);
+ }
+
+ return MTK_AFE_TDM_RATE_48K;
+}
+
+static unsigned int pcm_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_PCM_RATE_8K;
+ case 16000:
+ return MTK_AFE_PCM_RATE_16K;
+ case 32000:
+ return MTK_AFE_PCM_RATE_32K;
+ case 48000:
+ return MTK_AFE_PCM_RATE_48K;
+ default:
+ dev_err(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_PCM_RATE_48K);
+ }
+
+ return MTK_AFE_PCM_RATE_48K;
+}
+
+unsigned int mt8186_tdm_relatch_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_TDM_RELATCH_RATE_8K;
+ case 11025:
+ return MTK_AFE_TDM_RELATCH_RATE_11K;
+ case 12000:
+ return MTK_AFE_TDM_RELATCH_RATE_12K;
+ case 16000:
+ return MTK_AFE_TDM_RELATCH_RATE_16K;
+ case 22050:
+ return MTK_AFE_TDM_RELATCH_RATE_22K;
+ case 24000:
+ return MTK_AFE_TDM_RELATCH_RATE_24K;
+ case 32000:
+ return MTK_AFE_TDM_RELATCH_RATE_32K;
+ case 44100:
+ return MTK_AFE_TDM_RELATCH_RATE_44K;
+ case 48000:
+ return MTK_AFE_TDM_RELATCH_RATE_48K;
+ case 88200:
+ return MTK_AFE_TDM_RELATCH_RATE_88K;
+ case 96000:
+ return MTK_AFE_TDM_RELATCH_RATE_96K;
+ case 176400:
+ return MTK_AFE_TDM_RELATCH_RATE_176K;
+ case 192000:
+ return MTK_AFE_TDM_RELATCH_RATE_192K;
+ case 352800:
+ return MTK_AFE_TDM_RELATCH_RATE_352K;
+ case 384000:
+ return MTK_AFE_TDM_RELATCH_RATE_384K;
+ default:
+ dev_err(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__, rate, MTK_AFE_TDM_RELATCH_RATE_48K);
+ }
+
+ return MTK_AFE_TDM_RELATCH_RATE_48K;
+}
+
+unsigned int mt8186_rate_transform(struct device *dev, unsigned int rate, int aud_blk)
+{
+ switch (aud_blk) {
+ case MT8186_DAI_PCM:
+ return pcm_rate_transform(dev, rate);
+ case MT8186_DAI_TDM_IN:
+ return tdm_rate_transform(dev, rate);
+ default:
+ return mt8186_general_rate_transform(dev, rate);
+ }
+}
+
+int mt8186_dai_set_priv(struct mtk_base_afe *afe, int id, int priv_size, const void *priv_data)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ void *temp_data;
+
+ temp_data = devm_kzalloc(afe->dev,
+ priv_size,
+ GFP_KERNEL);
+ if (!temp_data)
+ return -ENOMEM;
+
+ if (priv_data)
+ memcpy(temp_data, priv_data, priv_size);
+
+ afe_priv->dai_priv[id] = temp_data;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c b/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c
new file mode 100644
index 000000000000..9e86e7079718
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8186-afe-gpio.c -- Mediatek 8186 afe gpio ctrl
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/pinctrl/consumer.h>
+
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-gpio.h"
+
+static struct pinctrl *aud_pinctrl;
+
+enum mt8186_afe_gpio {
+ MT8186_AFE_GPIO_CLK_MOSI_OFF,
+ MT8186_AFE_GPIO_CLK_MOSI_ON,
+ MT8186_AFE_GPIO_CLK_MISO_OFF,
+ MT8186_AFE_GPIO_CLK_MISO_ON,
+ MT8186_AFE_GPIO_DAT_MISO_OFF,
+ MT8186_AFE_GPIO_DAT_MISO_ON,
+ MT8186_AFE_GPIO_DAT_MOSI_OFF,
+ MT8186_AFE_GPIO_DAT_MOSI_ON,
+ MT8186_AFE_GPIO_I2S0_OFF,
+ MT8186_AFE_GPIO_I2S0_ON,
+ MT8186_AFE_GPIO_I2S1_OFF,
+ MT8186_AFE_GPIO_I2S1_ON,
+ MT8186_AFE_GPIO_I2S2_OFF,
+ MT8186_AFE_GPIO_I2S2_ON,
+ MT8186_AFE_GPIO_I2S3_OFF,
+ MT8186_AFE_GPIO_I2S3_ON,
+ MT8186_AFE_GPIO_TDM_OFF,
+ MT8186_AFE_GPIO_TDM_ON,
+ MT8186_AFE_GPIO_PCM_OFF,
+ MT8186_AFE_GPIO_PCM_ON,
+ MT8186_AFE_GPIO_GPIO_NUM
+};
+
+struct audio_gpio_attr {
+ const char *name;
+ bool gpio_prepare;
+ struct pinctrl_state *gpioctrl;
+};
+
+static struct audio_gpio_attr aud_gpios[MT8186_AFE_GPIO_GPIO_NUM] = {
+ [MT8186_AFE_GPIO_CLK_MOSI_OFF] = {"aud_clk_mosi_off", false, NULL},
+ [MT8186_AFE_GPIO_CLK_MOSI_ON] = {"aud_clk_mosi_on", false, NULL},
+ [MT8186_AFE_GPIO_CLK_MISO_OFF] = {"aud_clk_miso_off", false, NULL},
+ [MT8186_AFE_GPIO_CLK_MISO_ON] = {"aud_clk_miso_on", false, NULL},
+ [MT8186_AFE_GPIO_DAT_MISO_OFF] = {"aud_dat_miso_off", false, NULL},
+ [MT8186_AFE_GPIO_DAT_MISO_ON] = {"aud_dat_miso_on", false, NULL},
+ [MT8186_AFE_GPIO_DAT_MOSI_OFF] = {"aud_dat_mosi_off", false, NULL},
+ [MT8186_AFE_GPIO_DAT_MOSI_ON] = {"aud_dat_mosi_on", false, NULL},
+ [MT8186_AFE_GPIO_I2S0_OFF] = {"aud_gpio_i2s0_off", false, NULL},
+ [MT8186_AFE_GPIO_I2S0_ON] = {"aud_gpio_i2s0_on", false, NULL},
+ [MT8186_AFE_GPIO_I2S1_OFF] = {"aud_gpio_i2s1_off", false, NULL},
+ [MT8186_AFE_GPIO_I2S1_ON] = {"aud_gpio_i2s1_on", false, NULL},
+ [MT8186_AFE_GPIO_I2S2_OFF] = {"aud_gpio_i2s2_off", false, NULL},
+ [MT8186_AFE_GPIO_I2S2_ON] = {"aud_gpio_i2s2_on", false, NULL},
+ [MT8186_AFE_GPIO_I2S3_OFF] = {"aud_gpio_i2s3_off", false, NULL},
+ [MT8186_AFE_GPIO_I2S3_ON] = {"aud_gpio_i2s3_on", false, NULL},
+ [MT8186_AFE_GPIO_TDM_OFF] = {"aud_gpio_tdm_off", false, NULL},
+ [MT8186_AFE_GPIO_TDM_ON] = {"aud_gpio_tdm_on", false, NULL},
+ [MT8186_AFE_GPIO_PCM_OFF] = {"aud_gpio_pcm_off", false, NULL},
+ [MT8186_AFE_GPIO_PCM_ON] = {"aud_gpio_pcm_on", false, NULL},
+};
+
+static DEFINE_MUTEX(gpio_request_mutex);
+
+int mt8186_afe_gpio_init(struct device *dev)
+{
+ int i, j, ret;
+
+ aud_pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(aud_pinctrl)) {
+ ret = PTR_ERR(aud_pinctrl);
+ dev_err(dev, "%s(), ret %d, cannot get aud_pinctrl!\n",
+ __func__, ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aud_gpios); i++) {
+ aud_gpios[i].gpioctrl = pinctrl_lookup_state(aud_pinctrl,
+ aud_gpios[i].name);
+ if (IS_ERR(aud_gpios[i].gpioctrl)) {
+ ret = PTR_ERR(aud_gpios[i].gpioctrl);
+ dev_dbg(dev, "%s(), pinctrl_lookup_state %s fail, ret %d\n",
+ __func__, aud_gpios[i].name, ret);
+ } else {
+ aud_gpios[i].gpio_prepare = true;
+ }
+ }
+
+ /* gpio status init */
+ for (i = MT8186_DAI_ADDA; i <= MT8186_DAI_TDM_IN; i++) {
+ for (j = 0; j <= 1; j++)
+ mt8186_afe_gpio_request(dev, false, i, j);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8186_afe_gpio_init);
+
+static int mt8186_afe_gpio_select(struct device *dev,
+ enum mt8186_afe_gpio type)
+{
+ int ret = 0;
+
+ if (type < 0 || type >= MT8186_AFE_GPIO_GPIO_NUM) {
+ dev_dbg(dev, "%s(), error, invalid gpio type %d\n",
+ __func__, type);
+ return -EINVAL;
+ }
+
+ if (!aud_gpios[type].gpio_prepare) {
+ dev_dbg(dev, "%s(), error, gpio type %d not prepared\n",
+ __func__, type);
+ return -EIO;
+ }
+
+ ret = pinctrl_select_state(aud_pinctrl,
+ aud_gpios[type].gpioctrl);
+ if (ret) {
+ dev_dbg(dev, "%s(), error, can not set gpio type %d\n",
+ __func__, type);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8186_afe_gpio_adda_dl(struct device *dev, bool enable)
+{
+ int ret;
+
+ if (enable) {
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MOSI_ON);
+ if (ret) {
+ dev_dbg(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
+ return ret;
+ }
+
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MOSI_ON);
+ if (ret) {
+ dev_dbg(dev, "%s(), MOSI DAT ON select fail!\n", __func__);
+ return ret;
+ }
+ } else {
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MOSI_OFF);
+ if (ret) {
+ dev_dbg(dev, "%s(), MOSI DAT OFF select fail!\n", __func__);
+ return ret;
+ }
+
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MOSI_OFF);
+ if (ret) {
+ dev_dbg(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt8186_afe_gpio_adda_ul(struct device *dev, bool enable)
+{
+ int ret;
+
+ if (enable) {
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MISO_ON);
+ if (ret) {
+ dev_dbg(dev, "%s(), MISO CLK ON select fail!\n", __func__);
+ return ret;
+ }
+
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MISO_ON);
+ if (ret) {
+ dev_dbg(dev, "%s(), MISO DAT ON select fail!\n", __func__);
+ return ret;
+ }
+ } else {
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MISO_OFF);
+ if (ret) {
+ dev_dbg(dev, "%s(), MISO DAT OFF select fail!\n", __func__);
+ return ret;
+ }
+
+ ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MISO_OFF);
+ if (ret) {
+ dev_dbg(dev, "%s(), MISO CLK OFF select fail!\n", __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mt8186_afe_gpio_request(struct device *dev, bool enable,
+ int dai, int uplink)
+{
+ enum mt8186_afe_gpio sel;
+ int ret = -EINVAL;
+
+ mutex_lock(&gpio_request_mutex);
+
+ switch (dai) {
+ case MT8186_DAI_ADDA:
+ if (uplink)
+ ret = mt8186_afe_gpio_adda_ul(dev, enable);
+ else
+ ret = mt8186_afe_gpio_adda_dl(dev, enable);
+ goto unlock;
+ case MT8186_DAI_I2S_0:
+ sel = enable ? MT8186_AFE_GPIO_I2S0_ON : MT8186_AFE_GPIO_I2S0_OFF;
+ break;
+ case MT8186_DAI_I2S_1:
+ sel = enable ? MT8186_AFE_GPIO_I2S1_ON : MT8186_AFE_GPIO_I2S1_OFF;
+ break;
+ case MT8186_DAI_I2S_2:
+ sel = enable ? MT8186_AFE_GPIO_I2S2_ON : MT8186_AFE_GPIO_I2S2_OFF;
+ break;
+ case MT8186_DAI_I2S_3:
+ sel = enable ? MT8186_AFE_GPIO_I2S3_ON : MT8186_AFE_GPIO_I2S3_OFF;
+ break;
+ case MT8186_DAI_TDM_IN:
+ sel = enable ? MT8186_AFE_GPIO_TDM_ON : MT8186_AFE_GPIO_TDM_OFF;
+ break;
+ case MT8186_DAI_PCM:
+ sel = enable ? MT8186_AFE_GPIO_PCM_ON : MT8186_AFE_GPIO_PCM_OFF;
+ break;
+ default:
+ dev_dbg(dev, "%s(), invalid dai %d\n", __func__, dai);
+ goto unlock;
+ }
+
+ ret = mt8186_afe_gpio_select(dev, sel);
+
+unlock:
+ mutex_unlock(&gpio_request_mutex);
+
+ return ret;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-gpio.h b/sound/soc/mediatek/mt8186/mt8186-afe-gpio.h
new file mode 100644
index 000000000000..1ddc27838eb1
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-gpio.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * mt6833-afe-gpio.h -- Mediatek 6833 afe gpio ctrl definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT8186_AFE_GPIO_H_
+#define _MT8186_AFE_GPIO_H_
+
+struct mtk_base_afe;
+
+int mt8186_afe_gpio_init(struct device *dev);
+
+int mt8186_afe_gpio_request(struct device *dev, bool enable,
+ int dai, int uplink);
+
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
new file mode 100644
index 000000000000..085e993c650d
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -0,0 +1,3005 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Mediatek ALSA SoC AFE platform driver for 8186
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-clk.h"
+#include "mt8186-afe-gpio.h"
+#include "mt8186-interconnection.h"
+
+static const struct snd_pcm_hardware mt8186_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .period_bytes_min = 96,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 4 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+static int mt8186_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+ int ret;
+
+ memif->substream = substream;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+
+ snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+ return ret;
+ }
+
+ /* dynamic allocate irq to memif */
+ if (memif->irq_usage < 0) {
+ int irq_id = mtk_dynamic_irq_acquire(afe);
+
+ if (irq_id != afe->irqs_size) {
+ /* link */
+ memif->irq_usage = irq_id;
+ } else {
+ dev_err(afe->dev, "%s() error: no more asys irq\n",
+ __func__);
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+static void mt8186_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int irq_id = memif->irq_usage;
+
+ memif->substream = NULL;
+ afe_priv->irq_cnt[id] = 0;
+ afe_priv->xrun_assert[id] = 0;
+
+ if (!memif->const_irq) {
+ mtk_dynamic_irq_release(afe, irq_id);
+ memif->irq_usage = -1;
+ memif->substream = NULL;
+ }
+}
+
+static int mt8186_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ ret = mtk_afe_fe_hw_params(substream, params, dai);
+ if (ret)
+ return ret;
+
+ /* channel merge configuration, enable control is in UL5_IN_MUX */
+ if (id == MT8186_MEMIF_VUL3) {
+ int update_cnt = 8;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int fs_mode = mt8186_rate_transform(afe->dev, rate, id);
+
+ /* set rate, channel, update cnt, disable sgen */
+ val = fs_mode << CM1_FS_SELECT_SFT |
+ (channels - 1) << CHANNEL_MERGE0_CHNUM_SFT |
+ update_cnt << CHANNEL_MERGE0_UPDATE_CNT_SFT;
+ mask = CM1_FS_SELECT_MASK_SFT |
+ CHANNEL_MERGE0_CHNUM_MASK_SFT |
+ CHANNEL_MERGE0_UPDATE_CNT_MASK_SFT;
+ regmap_update_bits(afe->regmap, AFE_CM1_CON, mask, val);
+ }
+
+ return 0;
+}
+
+static int mt8186_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = mtk_afe_fe_hw_free(substream, dai);
+ if (ret) {
+ dev_err(afe->dev, "%s failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8186_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int irq_id = memif->irq_usage;
+ struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int rate = runtime->rate;
+ unsigned int counter;
+ int fs;
+ int ret;
+
+ dev_dbg(afe->dev, "%s(), %s cmd %d, irq_id %d\n",
+ __func__, memif->data->name, cmd, irq_id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = mtk_memif_set_enable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+ return ret;
+ }
+
+ /*
+ * for small latency record
+ * ul memif need read some data before irq enable
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ ((runtime->period_size * 1000) / rate <= 10))
+ udelay(300);
+
+ /* set irq counter */
+ if (afe_priv->irq_cnt[id] > 0)
+ counter = afe_priv->irq_cnt[id];
+ else
+ counter = runtime->period_size;
+
+ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit
+ << irq_data->irq_cnt_shift,
+ counter << irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit
+ << irq_data->irq_fs_shift,
+ fs << irq_data->irq_fs_shift);
+
+ /* enable interrupt */
+ if (runtime->stop_threshold != ~(0U))
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 1 << irq_data->irq_en_shift);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (afe_priv->xrun_assert[id] > 0) {
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ int avail = snd_pcm_capture_avail(runtime);
+ /* alsa can trigger stop/start when occur xrun */
+ if (avail >= runtime->buffer_size)
+ dev_dbg(afe->dev, "%s(), id %d, xrun assert\n",
+ __func__, id);
+ }
+ }
+
+ ret = mtk_memif_set_disable(afe, id);
+ if (ret)
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+
+ /* disable interrupt */
+ if (runtime->stop_threshold != ~(0U))
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 0 << irq_data->irq_en_shift);
+
+ /* clear pending IRQ */
+ regmap_write(afe->regmap, irq_data->irq_clr_reg,
+ 1 << irq_data->irq_clr_shift);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt8186_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+
+ return mt8186_rate_transform(afe->dev, rate, id);
+}
+
+static int mt8186_get_dai_fs(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ return mt8186_rate_transform(afe->dev, rate, dai_id);
+}
+
+static int mt8186_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return mt8186_general_rate_transform(afe->dev, rate);
+}
+
+static int mt8186_get_memif_pbuf_size(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if ((runtime->period_size * 1000) / runtime->rate > 10)
+ return MT8186_MEMIF_PBUF_SIZE_256_BYTES;
+
+ return MT8186_MEMIF_PBUF_SIZE_32_BYTES;
+}
+
+static int mt8186_fe_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int irq_id = memif->irq_usage;
+ struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int counter = runtime->period_size;
+ int fs;
+ int ret;
+
+ ret = mtk_afe_fe_prepare(substream, dai);
+ if (ret)
+ return ret;
+
+ /* set irq counter */
+ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit
+ << irq_data->irq_cnt_shift,
+ counter << irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, runtime->rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit
+ << irq_data->irq_fs_shift,
+ fs << irq_data->irq_fs_shift);
+
+ return 0;
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt8186_memif_dai_ops = {
+ .startup = mt8186_fe_startup,
+ .shutdown = mt8186_fe_shutdown,
+ .hw_params = mt8186_fe_hw_params,
+ .hw_free = mt8186_fe_hw_free,
+ .prepare = mt8186_fe_prepare,
+ .trigger = mt8186_fe_trigger,
+};
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt8186_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = MT8186_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL12",
+ .id = MT8186_MEMIF_DL12,
+ .playback = {
+ .stream_name = "DL12",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL2",
+ .id = MT8186_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT8186_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL4",
+ .id = MT8186_MEMIF_DL4,
+ .playback = {
+ .stream_name = "DL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL5",
+ .id = MT8186_MEMIF_DL5,
+ .playback = {
+ .stream_name = "DL5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL6",
+ .id = MT8186_MEMIF_DL6,
+ .playback = {
+ .stream_name = "DL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL7",
+ .id = MT8186_MEMIF_DL7,
+ .playback = {
+ .stream_name = "DL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "DL8",
+ .id = MT8186_MEMIF_DL8,
+ .playback = {
+ .stream_name = "DL8",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT8186_MEMIF_VUL12,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT8186_MEMIF_AWB,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT8186_MEMIF_VUL2,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL4",
+ .id = MT8186_MEMIF_AWB2,
+ .capture = {
+ .stream_name = "UL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL5",
+ .id = MT8186_MEMIF_VUL3,
+ .capture = {
+ .stream_name = "UL5",
+ .channels_min = 1,
+ .channels_max = 12,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL6",
+ .id = MT8186_MEMIF_VUL4,
+ .capture = {
+ .stream_name = "UL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL7",
+ .id = MT8186_MEMIF_VUL5,
+ .capture = {
+ .stream_name = "UL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ },
+ {
+ .name = "UL8",
+ .id = MT8186_MEMIF_VUL6,
+ .capture = {
+ .stream_name = "UL8",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8186_memif_dai_ops,
+ }
+};
+
+/* kcontrol */
+static int mt8186_irq_cnt1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] =
+ afe_priv->irq_cnt[MT8186_PRIMARY_MEMIF];
+
+ return 0;
+}
+
+static int mt8186_irq_cnt1_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int memif_num = MT8186_PRIMARY_MEMIF;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ int irq_id = memif->irq_usage;
+ int irq_cnt = afe_priv->irq_cnt[memif_num];
+
+ dev_dbg(afe->dev, "%s(), irq_id %d, irq_cnt = %d, value = %ld\n",
+ __func__, irq_id, irq_cnt, ucontrol->value.integer.value[0]);
+
+ if (irq_cnt == ucontrol->value.integer.value[0])
+ return 0;
+
+ irq_cnt = ucontrol->value.integer.value[0];
+ afe_priv->irq_cnt[memif_num] = irq_cnt;
+
+ if (!pm_runtime_status_suspended(afe->dev) && irq_id >= 0) {
+ struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+
+ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit
+ << irq_data->irq_cnt_shift,
+ irq_cnt << irq_data->irq_cnt_shift);
+ } else {
+ dev_dbg(afe->dev, "%s(), suspended || irq_id %d, not set\n",
+ __func__, irq_id);
+ }
+
+ return 1;
+}
+
+static int mt8186_irq_cnt2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] =
+ afe_priv->irq_cnt[MT8186_RECORD_MEMIF];
+
+ return 0;
+}
+
+static int mt8186_irq_cnt2_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int memif_num = MT8186_RECORD_MEMIF;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ int irq_id = memif->irq_usage;
+ int irq_cnt = afe_priv->irq_cnt[memif_num];
+
+ dev_dbg(afe->dev, "%s(), irq_id %d, irq_cnt = %d, value = %ld\n",
+ __func__, irq_id, irq_cnt, ucontrol->value.integer.value[0]);
+
+ if (irq_cnt == ucontrol->value.integer.value[0])
+ return 0;
+
+ irq_cnt = ucontrol->value.integer.value[0];
+ afe_priv->irq_cnt[memif_num] = irq_cnt;
+
+ if (!pm_runtime_status_suspended(afe->dev) && irq_id >= 0) {
+ struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+
+ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit
+ << irq_data->irq_cnt_shift,
+ irq_cnt << irq_data->irq_cnt_shift);
+ } else {
+ dev_dbg(afe->dev, "%s(), suspended || irq_id %d, not set\n",
+ __func__, irq_id);
+ }
+
+ return 1;
+}
+
+static int mt8186_record_xrun_assert_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int xrun_assert = afe_priv->xrun_assert[MT8186_RECORD_MEMIF];
+
+ ucontrol->value.integer.value[0] = xrun_assert;
+
+ return 0;
+}
+
+static int mt8186_record_xrun_assert_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int xrun_assert = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), xrun_assert %d\n", __func__, xrun_assert);
+
+ if (xrun_assert == afe_priv->xrun_assert[MT8186_RECORD_MEMIF])
+ return 0;
+
+ afe_priv->xrun_assert[MT8186_RECORD_MEMIF] = xrun_assert;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new mt8186_pcm_kcontrols[] = {
+ SOC_SINGLE_EXT("Audio IRQ1 CNT", SND_SOC_NOPM, 0, 0x3ffff, 0,
+ mt8186_irq_cnt1_get, mt8186_irq_cnt1_set),
+ SOC_SINGLE_EXT("Audio IRQ2 CNT", SND_SOC_NOPM, 0, 0x3ffff, 0,
+ mt8186_irq_cnt2_get, mt8186_irq_cnt2_set),
+ SOC_SINGLE_EXT("record_xrun_assert", SND_SOC_NOPM, 0, 0x1, 0,
+ mt8186_record_xrun_assert_get,
+ mt8186_record_xrun_assert_set),
+};
+
+/* dma widget & routes*/
+static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN21,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN21,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3 Switch", AFE_CONN21,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH1 Switch", AFE_CONN21_1,
+ I_TDM_IN_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN22,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN22,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3 Switch", AFE_CONN22,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4 Switch", AFE_CONN22,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH2 Switch", AFE_CONN22_1,
+ I_TDM_IN_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN9,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN9,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3 Switch", AFE_CONN9,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH3 Switch", AFE_CONN9_1,
+ I_TDM_IN_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN10,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN10,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3 Switch", AFE_CONN10,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4 Switch", AFE_CONN10,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH4 Switch", AFE_CONN10_1,
+ I_TDM_IN_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN5,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN5,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN5,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN5,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN5,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN5_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN5_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN5_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1 Switch", AFE_CONN5,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN5,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN5_1,
+ I_CONNSYS_I2S_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH1 Switch", AFE_CONN5_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN6,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN6,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN6,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN6,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN6,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN6_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN6_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN6_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2 Switch", AFE_CONN6,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN6,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN6_1,
+ I_CONNSYS_I2S_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH2 Switch", AFE_CONN6_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN32_1,
+ I_CONNSYS_I2S_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN32,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN32,
+ I_DL2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN33_1,
+ I_CONNSYS_I2S_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN38,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN38,
+ I_I2S0_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN39,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN39,
+ I_I2S0_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN44,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN45,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN46,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN46,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN46,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN46_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN46,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN46,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN46_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1 Switch", AFE_CONN46,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN46,
+ I_GAIN1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN47,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN47,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN47,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN47_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN47,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN47,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN47_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2 Switch", AFE_CONN47,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN47,
+ I_GAIN1_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN48,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN2_OUT_CH1 Switch", AFE_CONN48,
+ I_GAIN2_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH1 Switch", AFE_CONN48_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN49,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN2_OUT_CH2 Switch", AFE_CONN49,
+ I_GAIN2_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH2 Switch", AFE_CONN49_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN50,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN51,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH1 Switch", AFE_CONN58_1,
+ I_TDM_IN_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN58,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN58,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN58,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN58,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN58,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN58,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN58,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN58,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN58_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN58_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH1 Switch", AFE_CONN58_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH1 Switch", AFE_CONN58_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH2 Switch", AFE_CONN59_1,
+ I_TDM_IN_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN59,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN59,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN59,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN59,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN59,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN59,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN59,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN59,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN59_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN59_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH2 Switch", AFE_CONN59_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH2 Switch", AFE_CONN59_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH3 Switch", AFE_CONN60_1,
+ I_TDM_IN_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN60,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN60,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN60,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN60,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN60,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN60,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN60,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN60,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN60_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN60_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH1 Switch", AFE_CONN60_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH1 Switch", AFE_CONN60_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH4 Switch", AFE_CONN61_1,
+ I_TDM_IN_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN61,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN61,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN61,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN61,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN61,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN61,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN61,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN61,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN61_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN61_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH2 Switch", AFE_CONN61_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH2 Switch", AFE_CONN61_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH5 Switch", AFE_CONN62_1,
+ I_TDM_IN_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN62,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN62,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN62,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN62,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN62,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN62,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN62,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN62,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN62_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN62_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH1 Switch", AFE_CONN62_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH1 Switch", AFE_CONN62_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH6 Switch", AFE_CONN63_1,
+ I_TDM_IN_CH6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN63,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN63,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN63,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN63,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN63,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN63,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN63,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN63,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN63_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN63_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH2 Switch", AFE_CONN63_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH2 Switch", AFE_CONN63_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH7 Switch", AFE_CONN64_1,
+ I_TDM_IN_CH7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN64,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN64,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN64,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN64,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1v", AFE_CONN64,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN64,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN64,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN64,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN64_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN64_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH1 Switch", AFE_CONN64_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH1 Switch", AFE_CONN64_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("TDM_IN_CH8 Switch", AFE_CONN65_1,
+ I_TDM_IN_CH8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN65,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN65,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN65,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN65,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN65,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN65,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN65,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN65,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN65_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN65_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH2 Switch", AFE_CONN65_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH2 Switch", AFE_CONN65_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch9_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN66,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN66,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN66,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN66,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN66,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN66,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN66,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN66,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN66_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN66_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH1 Switch", AFE_CONN66_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH1 Switch", AFE_CONN66_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN67,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN67,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN67,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN67,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN67,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN67,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN67,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN67,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN67_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN67_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH2 Switch", AFE_CONN67_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH2 Switch", AFE_CONN67_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch11_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN68,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1 Switch", AFE_CONN68,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN68,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN68,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN68,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN68,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN68,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN68,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN68_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN68_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH1 Switch", AFE_CONN68_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH1 Switch", AFE_CONN68_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new hw_cm1_ch12_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN69,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2 Switch", AFE_CONN69,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN69,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN69,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN69,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN69,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN69,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN69,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN69_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN69_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC1_OUT_CH2 Switch", AFE_CONN69_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC2_OUT_CH2 Switch", AFE_CONN69_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+/* ADDA UL MUX */
+enum {
+ UL5_IN_MUX_CM1 = 0,
+ UL5_IN_MUX_NORMAL,
+ UL5_IN_MUX_MASK = 0x1,
+};
+
+static const char * const ul5_in_mux_map[] = {
+ "UL5_IN_FROM_CM1", "UL5_IN_FROM_Normal"
+};
+
+static int ul5_in_map_value[] = {
+ UL5_IN_MUX_CM1,
+ UL5_IN_MUX_NORMAL,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul5_in_mux_map_enum,
+ AFE_CM1_CON,
+ VUL3_BYPASS_CM_SFT,
+ VUL3_BYPASS_CM_MASK,
+ ul5_in_mux_map,
+ ul5_in_map_value);
+
+static const struct snd_kcontrol_new ul5_in_mux_control =
+ SOC_DAPM_ENUM("UL5_IN_MUX Select", ul5_in_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mt8186_memif_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch3_mix, ARRAY_SIZE(memif_ul1_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch4_mix, ARRAY_SIZE(memif_ul1_ch4_mix)),
+
+ SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL4_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch1_mix, ARRAY_SIZE(memif_ul4_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL4_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch2_mix, ARRAY_SIZE(memif_ul4_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL5_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch1_mix, ARRAY_SIZE(memif_ul5_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL5_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch2_mix, ARRAY_SIZE(memif_ul5_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL6_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch1_mix, ARRAY_SIZE(memif_ul6_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL6_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch2_mix, ARRAY_SIZE(memif_ul6_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL7_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch1_mix, ARRAY_SIZE(memif_ul7_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL7_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch2_mix, ARRAY_SIZE(memif_ul7_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL8_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch1_mix, ARRAY_SIZE(memif_ul8_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL8_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch2_mix, ARRAY_SIZE(memif_ul8_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL5_2CH", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("HW_CM1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* CM1 en*/
+ SND_SOC_DAPM_SUPPLY_S("CM1_EN", 0, AFE_CM1_CON,
+ CHANNEL_MERGE0_EN_SFT, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("HW_CM1_CH1", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch1_mix, ARRAY_SIZE(hw_cm1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH2", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch2_mix, ARRAY_SIZE(hw_cm1_ch2_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH3", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch3_mix, ARRAY_SIZE(hw_cm1_ch3_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH4", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch4_mix, ARRAY_SIZE(hw_cm1_ch4_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH5", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch5_mix, ARRAY_SIZE(hw_cm1_ch5_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH6", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch6_mix, ARRAY_SIZE(hw_cm1_ch6_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH7", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch7_mix, ARRAY_SIZE(hw_cm1_ch7_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH8", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch8_mix, ARRAY_SIZE(hw_cm1_ch8_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH9", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch9_mix, ARRAY_SIZE(hw_cm1_ch9_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH10", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch10_mix, ARRAY_SIZE(hw_cm1_ch10_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH11", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch11_mix, ARRAY_SIZE(hw_cm1_ch11_mix)),
+ SND_SOC_DAPM_MIXER("HW_CM1_CH12", SND_SOC_NOPM, 0, 0,
+ hw_cm1_ch12_mix, ARRAY_SIZE(hw_cm1_ch12_mix)),
+
+ SND_SOC_DAPM_MUX("UL5_IN_MUX", SND_SOC_NOPM, 0, 0,
+ &ul5_in_mux_control),
+
+ SND_SOC_DAPM_MIXER("DSP_DL1_VIRT", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DSP_DL2_VIRT", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("UL1_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL2_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL3_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL4_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL5_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL6_VIRTUAL_INPUT"),
+};
+
+static const struct snd_soc_dapm_route mt8186_memif_routes[] = {
+ {"UL1", NULL, "UL1_CH1"},
+ {"UL1", NULL, "UL1_CH2"},
+ {"UL1", NULL, "UL1_CH3"},
+ {"UL1", NULL, "UL1_CH4"},
+ {"UL1_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH1", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH3", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH3", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH4", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH4", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL1_CH1", "TDM_IN_CH1 Switch", "TDM IN"},
+ {"UL1_CH2", "TDM_IN_CH2 Switch", "TDM IN"},
+ {"UL1_CH3", "TDM_IN_CH3 Switch", "TDM IN"},
+ {"UL1_CH4", "TDM_IN_CH4 Switch", "TDM IN"},
+
+ {"UL2", NULL, "UL2_CH1"},
+ {"UL2", NULL, "UL2_CH2"},
+
+ /* cannot connect FE to FE directly */
+ {"UL2_CH1", "DL1_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL1_CH2 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH1", "DL12_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL12_CH2 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH1", "DL6_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL6_CH2 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH1", "DL2_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL2_CH2 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH1", "DL3_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL3_CH2 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH1", "DL4_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL4_CH2 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH1", "DL5_CH1 Switch", "Hostless_UL2 UL"},
+ {"UL2_CH2", "DL5_CH2 Switch", "Hostless_UL2 UL"},
+
+ {"Hostless_UL2 UL", NULL, "UL2_VIRTUAL_INPUT"},
+
+ {"UL2_CH1", "I2S0_CH1 Switch", "I2S0"},
+ {"UL2_CH2", "I2S0_CH2 Switch", "I2S0"},
+ {"UL2_CH1", "I2S2_CH1 Switch", "I2S2"},
+ {"UL2_CH2", "I2S2_CH2 Switch", "I2S2"},
+
+ {"UL2_CH1", "PCM_1_CAP_CH1 Switch", "PCM 1 Capture"},
+ {"UL2_CH2", "PCM_1_CAP_CH2 Switch", "PCM 1 Capture"},
+
+ {"UL2_CH1", "CONNSYS_I2S_CH1 Switch", "Connsys I2S"},
+ {"UL2_CH2", "CONNSYS_I2S_CH2 Switch", "Connsys I2S"},
+
+ {"UL2_CH1", "SRC_1_OUT_CH1 Switch", "HW_SRC_1_Out"},
+ {"UL2_CH2", "SRC_1_OUT_CH2 Switch", "HW_SRC_1_Out"},
+
+ {"UL3", NULL, "UL3_CH1"},
+ {"UL3", NULL, "UL3_CH2"},
+ {"UL3_CH1", "CONNSYS_I2S_CH1 Switch", "Connsys I2S"},
+ {"UL3_CH2", "CONNSYS_I2S_CH2 Switch", "Connsys I2S"},
+
+ {"UL4", NULL, "UL4_CH1"},
+ {"UL4", NULL, "UL4_CH2"},
+ {"UL4_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL4_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL4_CH1", "I2S0_CH1 Switch", "I2S0"},
+ {"UL4_CH2", "I2S0_CH2 Switch", "I2S0"},
+
+ {"UL5", NULL, "UL5_IN_MUX"},
+ {"UL5_IN_MUX", "UL5_IN_FROM_Normal", "UL5_2CH"},
+ {"UL5_IN_MUX", "UL5_IN_FROM_CM1", "HW_CM1"},
+ {"UL5_2CH", NULL, "UL5_CH1"},
+ {"UL5_2CH", NULL, "UL5_CH2"},
+ {"UL5_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL5_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"HW_CM1", NULL, "CM1_EN"},
+ {"HW_CM1", NULL, "HW_CM1_CH1"},
+ {"HW_CM1", NULL, "HW_CM1_CH2"},
+ {"HW_CM1", NULL, "HW_CM1_CH3"},
+ {"HW_CM1", NULL, "HW_CM1_CH4"},
+ {"HW_CM1", NULL, "HW_CM1_CH5"},
+ {"HW_CM1", NULL, "HW_CM1_CH6"},
+ {"HW_CM1", NULL, "HW_CM1_CH7"},
+ {"HW_CM1", NULL, "HW_CM1_CH8"},
+ {"HW_CM1", NULL, "HW_CM1_CH9"},
+ {"HW_CM1", NULL, "HW_CM1_CH10"},
+ {"HW_CM1", NULL, "HW_CM1_CH11"},
+ {"HW_CM1", NULL, "HW_CM1_CH12"},
+ {"HW_CM1_CH1", "TDM_IN_CH1 Switch", "TDM IN"},
+ {"HW_CM1_CH2", "TDM_IN_CH2 Switch", "TDM IN"},
+ {"HW_CM1_CH3", "TDM_IN_CH3 Switch", "TDM IN"},
+ {"HW_CM1_CH4", "TDM_IN_CH4 Switch", "TDM IN"},
+ {"HW_CM1_CH5", "TDM_IN_CH5 Switch", "TDM IN"},
+ {"HW_CM1_CH6", "TDM_IN_CH6 Switch", "TDM IN"},
+ {"HW_CM1_CH7", "TDM_IN_CH7 Switch", "TDM IN"},
+ {"HW_CM1_CH8", "TDM_IN_CH8 Switch", "TDM IN"},
+ {"HW_CM1_CH9", "DL1_CH1 Switch", "Hostless_UL5 UL"},
+ {"HW_CM1_CH10", "DL1_CH2 Switch", "Hostless_UL5 UL"},
+
+ {"HW_CM1_CH3", "DL1_CH1 Switch", "Hostless_UL5 UL"},
+ {"HW_CM1_CH4", "DL1_CH2 Switch", "Hostless_UL5 UL"},
+
+ {"HW_CM1_CH3", "DL3_CH1 Switch", "Hostless_UL5 UL"},
+ {"HW_CM1_CH4", "DL3_CH2 Switch", "Hostless_UL5 UL"},
+
+ {"HW_CM1_CH5", "HW_SRC1_OUT_CH1 Switch", "HW_SRC_1_Out"},
+ {"HW_CM1_CH6", "HW_SRC1_OUT_CH2 Switch", "HW_SRC_1_Out"},
+
+ {"HW_CM1_CH9", "DL12_CH1 Switch", "Hostless_UL5 UL"},
+ {"HW_CM1_CH10", "DL12_CH2 Switch", "Hostless_UL5 UL"},
+ {"HW_CM1_CH11", "DL12_CH3 Switch", "Hostless_UL5 UL"},
+ {"HW_CM1_CH12", "DL12_CH4 Switch", "Hostless_UL5 UL"},
+
+ {"Hostless_UL5 UL", NULL, "UL5_VIRTUAL_INPUT"},
+
+ {"UL6", NULL, "UL6_CH1"},
+ {"UL6", NULL, "UL6_CH2"},
+
+ {"UL6_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL6_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL6_CH1", "DL1_CH1 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH2", "DL1_CH2 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH1", "DL2_CH1 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH2", "DL2_CH2 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH1", "DL12_CH1 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH2", "DL12_CH2 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH1", "DL6_CH1 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH2", "DL6_CH2 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH1", "DL3_CH1 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH2", "DL3_CH2 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH1", "DL4_CH1 Switch", "Hostless_UL6 UL"},
+ {"UL6_CH2", "DL4_CH2 Switch", "Hostless_UL6 UL"},
+ {"Hostless_UL6 UL", NULL, "UL6_VIRTUAL_INPUT"},
+ {"UL6_CH1", "PCM_1_CAP_CH1 Switch", "PCM 1 Capture"},
+ {"UL6_CH2", "PCM_1_CAP_CH2 Switch", "PCM 1 Capture"},
+ {"UL6_CH1", "GAIN1_OUT_CH1 Switch", "HW Gain 1 Out"},
+ {"UL6_CH2", "GAIN1_OUT_CH2 Switch", "HW Gain 1 Out"},
+
+ {"UL7", NULL, "UL7_CH1"},
+ {"UL7", NULL, "UL7_CH2"},
+ {"UL7_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL7_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+ {"UL7_CH1", "HW_GAIN2_OUT_CH1 Switch", "HW Gain 2 Out"},
+ {"UL7_CH2", "HW_GAIN2_OUT_CH2 Switch", "HW Gain 2 Out"},
+ {"UL7_CH1", "HW_SRC_2_OUT_CH1 Switch", "HW_SRC_2_Out"},
+ {"UL7_CH2", "HW_SRC_2_OUT_CH2 Switch", "HW_SRC_2_Out"},
+
+ {"UL8", NULL, "UL8_CH1"},
+ {"UL8", NULL, "UL8_CH2"},
+ {"UL8_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"UL8_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+
+ {"HW_GAIN2_IN_CH1", "ADDA_UL_CH1 Switch", "ADDA_UL_Mux"},
+ {"HW_GAIN2_IN_CH2", "ADDA_UL_CH2 Switch", "ADDA_UL_Mux"},
+};
+
+static const struct mtk_base_memif_data memif_data[MT8186_MEMIF_NUM] = {
+ [MT8186_MEMIF_DL1] = {
+ .name = "DL1",
+ .id = MT8186_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .reg_ofs_end = AFE_DL1_END,
+ .reg_ofs_base_msb = AFE_DL1_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL1_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL1_END_MSB,
+ .fs_reg = AFE_DL1_CON0,
+ .fs_shift = DL1_MODE_SFT,
+ .fs_maskbit = DL1_MODE_MASK,
+ .mono_reg = AFE_DL1_CON0,
+ .mono_shift = DL1_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL1_ON_SFT,
+ .hd_reg = AFE_DL1_CON0,
+ .hd_shift = DL1_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL1_CON0,
+ .hd_align_mshift = DL1_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL1_CON0,
+ .pbuf_mask = DL1_PBUF_SIZE_MASK,
+ .pbuf_shift = DL1_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL1_CON0,
+ .minlen_mask = DL1_MINLEN_MASK,
+ .minlen_shift = DL1_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL12] = {
+ .name = "DL12",
+ .id = MT8186_MEMIF_DL12,
+ .reg_ofs_base = AFE_DL12_BASE,
+ .reg_ofs_cur = AFE_DL12_CUR,
+ .reg_ofs_end = AFE_DL12_END,
+ .reg_ofs_base_msb = AFE_DL12_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL12_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL12_END_MSB,
+ .fs_reg = AFE_DL12_CON0,
+ .fs_shift = DL12_MODE_SFT,
+ .fs_maskbit = DL12_MODE_MASK,
+ .mono_reg = AFE_DL12_CON0,
+ .mono_shift = DL12_MONO_SFT,
+ .quad_ch_reg = AFE_DL12_CON0,
+ .quad_ch_mask = DL12_4CH_EN_MASK,
+ .quad_ch_shift = DL12_4CH_EN_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL12_ON_SFT,
+ .hd_reg = AFE_DL12_CON0,
+ .hd_shift = DL12_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL12_CON0,
+ .hd_align_mshift = DL12_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL12_CON0,
+ .pbuf_mask = DL12_PBUF_SIZE_MASK,
+ .pbuf_shift = DL12_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL12_CON0,
+ .minlen_mask = DL12_MINLEN_MASK,
+ .minlen_shift = DL12_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL2] = {
+ .name = "DL2",
+ .id = MT8186_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .reg_ofs_end = AFE_DL2_END,
+ .reg_ofs_base_msb = AFE_DL2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL2_END_MSB,
+ .fs_reg = AFE_DL2_CON0,
+ .fs_shift = DL2_MODE_SFT,
+ .fs_maskbit = DL2_MODE_MASK,
+ .mono_reg = AFE_DL2_CON0,
+ .mono_shift = DL2_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL2_ON_SFT,
+ .hd_reg = AFE_DL2_CON0,
+ .hd_shift = DL2_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL2_CON0,
+ .hd_align_mshift = DL2_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL2_CON0,
+ .pbuf_mask = DL2_PBUF_SIZE_MASK,
+ .pbuf_shift = DL2_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL2_CON0,
+ .minlen_mask = DL2_MINLEN_MASK,
+ .minlen_shift = DL2_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL3] = {
+ .name = "DL3",
+ .id = MT8186_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .reg_ofs_end = AFE_DL3_END,
+ .reg_ofs_base_msb = AFE_DL3_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL3_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL3_END_MSB,
+ .fs_reg = AFE_DL3_CON0,
+ .fs_shift = DL3_MODE_SFT,
+ .fs_maskbit = DL3_MODE_MASK,
+ .mono_reg = AFE_DL3_CON0,
+ .mono_shift = DL3_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL3_ON_SFT,
+ .hd_reg = AFE_DL3_CON0,
+ .hd_shift = DL3_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL3_CON0,
+ .hd_align_mshift = DL3_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL3_CON0,
+ .pbuf_mask = DL3_PBUF_SIZE_MASK,
+ .pbuf_shift = DL3_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL3_CON0,
+ .minlen_mask = DL3_MINLEN_MASK,
+ .minlen_shift = DL3_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL4] = {
+ .name = "DL4",
+ .id = MT8186_MEMIF_DL4,
+ .reg_ofs_base = AFE_DL4_BASE,
+ .reg_ofs_cur = AFE_DL4_CUR,
+ .reg_ofs_end = AFE_DL4_END,
+ .reg_ofs_base_msb = AFE_DL4_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL4_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL4_END_MSB,
+ .fs_reg = AFE_DL4_CON0,
+ .fs_shift = DL4_MODE_SFT,
+ .fs_maskbit = DL4_MODE_MASK,
+ .mono_reg = AFE_DL4_CON0,
+ .mono_shift = DL4_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL4_ON_SFT,
+ .hd_reg = AFE_DL4_CON0,
+ .hd_shift = DL4_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL4_CON0,
+ .hd_align_mshift = DL4_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL4_CON0,
+ .pbuf_mask = DL4_PBUF_SIZE_MASK,
+ .pbuf_shift = DL4_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL4_CON0,
+ .minlen_mask = DL4_MINLEN_MASK,
+ .minlen_shift = DL4_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL5] = {
+ .name = "DL5",
+ .id = MT8186_MEMIF_DL5,
+ .reg_ofs_base = AFE_DL5_BASE,
+ .reg_ofs_cur = AFE_DL5_CUR,
+ .reg_ofs_end = AFE_DL5_END,
+ .reg_ofs_base_msb = AFE_DL5_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL5_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL5_END_MSB,
+ .fs_reg = AFE_DL5_CON0,
+ .fs_shift = DL5_MODE_SFT,
+ .fs_maskbit = DL5_MODE_MASK,
+ .mono_reg = AFE_DL5_CON0,
+ .mono_shift = DL5_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL5_ON_SFT,
+ .hd_reg = AFE_DL5_CON0,
+ .hd_shift = DL5_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL5_CON0,
+ .hd_align_mshift = DL5_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL5_CON0,
+ .pbuf_mask = DL5_PBUF_SIZE_MASK,
+ .pbuf_shift = DL5_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL5_CON0,
+ .minlen_mask = DL5_MINLEN_MASK,
+ .minlen_shift = DL5_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL6] = {
+ .name = "DL6",
+ .id = MT8186_MEMIF_DL6,
+ .reg_ofs_base = AFE_DL6_BASE,
+ .reg_ofs_cur = AFE_DL6_CUR,
+ .reg_ofs_end = AFE_DL6_END,
+ .reg_ofs_base_msb = AFE_DL6_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL6_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL6_END_MSB,
+ .fs_reg = AFE_DL6_CON0,
+ .fs_shift = DL6_MODE_SFT,
+ .fs_maskbit = DL6_MODE_MASK,
+ .mono_reg = AFE_DL6_CON0,
+ .mono_shift = DL6_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL6_ON_SFT,
+ .hd_reg = AFE_DL6_CON0,
+ .hd_shift = DL6_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL6_CON0,
+ .hd_align_mshift = DL6_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL6_CON0,
+ .pbuf_mask = DL6_PBUF_SIZE_MASK,
+ .pbuf_shift = DL6_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL6_CON0,
+ .minlen_mask = DL6_MINLEN_MASK,
+ .minlen_shift = DL6_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL7] = {
+ .name = "DL7",
+ .id = MT8186_MEMIF_DL7,
+ .reg_ofs_base = AFE_DL7_BASE,
+ .reg_ofs_cur = AFE_DL7_CUR,
+ .reg_ofs_end = AFE_DL7_END,
+ .reg_ofs_base_msb = AFE_DL7_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL7_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL7_END_MSB,
+ .fs_reg = AFE_DL7_CON0,
+ .fs_shift = DL7_MODE_SFT,
+ .fs_maskbit = DL7_MODE_MASK,
+ .mono_reg = AFE_DL7_CON0,
+ .mono_shift = DL7_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL7_ON_SFT,
+ .hd_reg = AFE_DL7_CON0,
+ .hd_shift = DL7_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL7_CON0,
+ .hd_align_mshift = DL7_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL7_CON0,
+ .pbuf_mask = DL7_PBUF_SIZE_MASK,
+ .pbuf_shift = DL7_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL7_CON0,
+ .minlen_mask = DL7_MINLEN_MASK,
+ .minlen_shift = DL7_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_DL8] = {
+ .name = "DL8",
+ .id = MT8186_MEMIF_DL8,
+ .reg_ofs_base = AFE_DL8_BASE,
+ .reg_ofs_cur = AFE_DL8_CUR,
+ .reg_ofs_end = AFE_DL8_END,
+ .reg_ofs_base_msb = AFE_DL8_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL8_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL8_END_MSB,
+ .fs_reg = AFE_DL8_CON0,
+ .fs_shift = DL8_MODE_SFT,
+ .fs_maskbit = DL8_MODE_MASK,
+ .mono_reg = AFE_DL8_CON0,
+ .mono_shift = DL8_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL8_ON_SFT,
+ .hd_reg = AFE_DL8_CON0,
+ .hd_shift = DL8_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL8_CON0,
+ .hd_align_mshift = DL8_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .pbuf_reg = AFE_DL8_CON0,
+ .pbuf_mask = DL8_PBUF_SIZE_MASK,
+ .pbuf_shift = DL8_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL8_CON0,
+ .minlen_mask = DL8_MINLEN_MASK,
+ .minlen_shift = DL8_MINLEN_SFT,
+ },
+ [MT8186_MEMIF_VUL12] = {
+ .name = "VUL12",
+ .id = MT8186_MEMIF_VUL12,
+ .reg_ofs_base = AFE_VUL12_BASE,
+ .reg_ofs_cur = AFE_VUL12_CUR,
+ .reg_ofs_end = AFE_VUL12_END,
+ .reg_ofs_base_msb = AFE_VUL12_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL12_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL12_END_MSB,
+ .fs_reg = AFE_VUL12_CON0,
+ .fs_shift = VUL12_MODE_SFT,
+ .fs_maskbit = VUL12_MODE_MASK,
+ .mono_reg = AFE_VUL12_CON0,
+ .mono_shift = VUL12_MONO_SFT,
+ .quad_ch_reg = AFE_VUL12_CON0,
+ .quad_ch_mask = VUL12_4CH_EN_MASK,
+ .quad_ch_shift = VUL12_4CH_EN_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL12_ON_SFT,
+ .hd_reg = AFE_VUL12_CON0,
+ .hd_shift = VUL12_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL12_CON0,
+ .hd_align_mshift = VUL12_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_VUL2] = {
+ .name = "VUL2",
+ .id = MT8186_MEMIF_VUL2,
+ .reg_ofs_base = AFE_VUL2_BASE,
+ .reg_ofs_cur = AFE_VUL2_CUR,
+ .reg_ofs_end = AFE_VUL2_END,
+ .reg_ofs_base_msb = AFE_VUL2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL2_END_MSB,
+ .fs_reg = AFE_VUL2_CON0,
+ .fs_shift = VUL2_MODE_SFT,
+ .fs_maskbit = VUL2_MODE_MASK,
+ .mono_reg = AFE_VUL2_CON0,
+ .mono_shift = VUL2_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL2_ON_SFT,
+ .hd_reg = AFE_VUL2_CON0,
+ .hd_shift = VUL2_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL2_CON0,
+ .hd_align_mshift = VUL2_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_AWB] = {
+ .name = "AWB",
+ .id = MT8186_MEMIF_AWB,
+ .reg_ofs_base = AFE_AWB_BASE,
+ .reg_ofs_cur = AFE_AWB_CUR,
+ .reg_ofs_end = AFE_AWB_END,
+ .reg_ofs_base_msb = AFE_AWB_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_AWB_CUR_MSB,
+ .reg_ofs_end_msb = AFE_AWB_END_MSB,
+ .fs_reg = AFE_AWB_CON0,
+ .fs_shift = AWB_MODE_SFT,
+ .fs_maskbit = AWB_MODE_MASK,
+ .mono_reg = AFE_AWB_CON0,
+ .mono_shift = AWB_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AWB_ON_SFT,
+ .hd_reg = AFE_AWB_CON0,
+ .hd_shift = AWB_HD_MODE_SFT,
+ .hd_align_reg = AFE_AWB_CON0,
+ .hd_align_mshift = AWB_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_AWB2] = {
+ .name = "AWB2",
+ .id = MT8186_MEMIF_AWB2,
+ .reg_ofs_base = AFE_AWB2_BASE,
+ .reg_ofs_cur = AFE_AWB2_CUR,
+ .reg_ofs_end = AFE_AWB2_END,
+ .reg_ofs_base_msb = AFE_AWB2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_AWB2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_AWB2_END_MSB,
+ .fs_reg = AFE_AWB2_CON0,
+ .fs_shift = AWB2_MODE_SFT,
+ .fs_maskbit = AWB2_MODE_MASK,
+ .mono_reg = AFE_AWB2_CON0,
+ .mono_shift = AWB2_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AWB2_ON_SFT,
+ .hd_reg = AFE_AWB2_CON0,
+ .hd_shift = AWB2_HD_MODE_SFT,
+ .hd_align_reg = AFE_AWB2_CON0,
+ .hd_align_mshift = AWB2_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_VUL3] = {
+ .name = "VUL3",
+ .id = MT8186_MEMIF_VUL3,
+ .reg_ofs_base = AFE_VUL3_BASE,
+ .reg_ofs_cur = AFE_VUL3_CUR,
+ .reg_ofs_end = AFE_VUL3_END,
+ .reg_ofs_base_msb = AFE_VUL3_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL3_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL3_END_MSB,
+ .fs_reg = AFE_VUL3_CON0,
+ .fs_shift = VUL3_MODE_SFT,
+ .fs_maskbit = VUL3_MODE_MASK,
+ .mono_reg = AFE_VUL3_CON0,
+ .mono_shift = VUL3_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL3_ON_SFT,
+ .hd_reg = AFE_VUL3_CON0,
+ .hd_shift = VUL3_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL3_CON0,
+ .hd_align_mshift = VUL3_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_VUL4] = {
+ .name = "VUL4",
+ .id = MT8186_MEMIF_VUL4,
+ .reg_ofs_base = AFE_VUL4_BASE,
+ .reg_ofs_cur = AFE_VUL4_CUR,
+ .reg_ofs_end = AFE_VUL4_END,
+ .reg_ofs_base_msb = AFE_VUL4_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL4_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL4_END_MSB,
+ .fs_reg = AFE_VUL4_CON0,
+ .fs_shift = VUL4_MODE_SFT,
+ .fs_maskbit = VUL4_MODE_MASK,
+ .mono_reg = AFE_VUL4_CON0,
+ .mono_shift = VUL4_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL4_ON_SFT,
+ .hd_reg = AFE_VUL4_CON0,
+ .hd_shift = VUL4_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL4_CON0,
+ .hd_align_mshift = VUL4_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_VUL5] = {
+ .name = "VUL5",
+ .id = MT8186_MEMIF_VUL5,
+ .reg_ofs_base = AFE_VUL5_BASE,
+ .reg_ofs_cur = AFE_VUL5_CUR,
+ .reg_ofs_end = AFE_VUL5_END,
+ .reg_ofs_base_msb = AFE_VUL5_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL5_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL5_END_MSB,
+ .fs_reg = AFE_VUL5_CON0,
+ .fs_shift = VUL5_MODE_SFT,
+ .fs_maskbit = VUL5_MODE_MASK,
+ .mono_reg = AFE_VUL5_CON0,
+ .mono_shift = VUL5_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL5_ON_SFT,
+ .hd_reg = AFE_VUL5_CON0,
+ .hd_shift = VUL5_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL5_CON0,
+ .hd_align_mshift = VUL5_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+ [MT8186_MEMIF_VUL6] = {
+ .name = "VUL6",
+ .id = MT8186_MEMIF_VUL6,
+ .reg_ofs_base = AFE_VUL6_BASE,
+ .reg_ofs_cur = AFE_VUL6_CUR,
+ .reg_ofs_end = AFE_VUL6_END,
+ .reg_ofs_base_msb = AFE_VUL6_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL6_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL6_END_MSB,
+ .fs_reg = AFE_VUL6_CON0,
+ .fs_shift = VUL6_MODE_SFT,
+ .fs_maskbit = VUL6_MODE_MASK,
+ .mono_reg = AFE_VUL6_CON0,
+ .mono_shift = VUL6_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL6_ON_SFT,
+ .hd_reg = AFE_VUL6_CON0,
+ .hd_shift = VUL6_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL6_CON0,
+ .hd_align_mshift = VUL6_HALIGN_SFT,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8186_IRQ_NUM] = {
+ [MT8186_IRQ_0] = {
+ .id = MT8186_IRQ_0,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT0,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ0_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ0_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ0_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ0_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_1] = {
+ .id = MT8186_IRQ_1,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT1,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ1_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ1_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ1_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ1_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_2] = {
+ .id = MT8186_IRQ_2,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT2,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ2_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ2_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ2_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ2_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_3] = {
+ .id = MT8186_IRQ_3,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT3,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ3_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ3_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ3_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ3_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_4] = {
+ .id = MT8186_IRQ_4,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT4,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ4_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ4_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ4_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ4_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_5] = {
+ .id = MT8186_IRQ_5,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT5,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ5_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ5_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ5_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ5_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_6] = {
+ .id = MT8186_IRQ_6,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT6,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ6_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ6_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ6_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ6_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_7] = {
+ .id = MT8186_IRQ_7,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT7,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ7_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ7_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ7_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ7_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_8] = {
+ .id = MT8186_IRQ_8,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT8,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ8_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ8_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ8_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ8_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_9] = {
+ .id = MT8186_IRQ_9,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT9,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ9_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ9_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ9_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ9_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_10] = {
+ .id = MT8186_IRQ_10,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT10,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ10_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ10_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ10_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ10_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_11] = {
+ .id = MT8186_IRQ_11,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT11,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ11_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ11_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ11_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ11_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_12] = {
+ .id = MT8186_IRQ_12,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT12,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ12_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ12_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ12_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ12_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_13] = {
+ .id = MT8186_IRQ_13,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT13,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ13_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ13_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ13_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ13_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_14] = {
+ .id = MT8186_IRQ_14,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT14,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ14_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ14_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ14_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ14_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_15] = {
+ .id = MT8186_IRQ_15,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT15,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ15_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ15_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ15_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ15_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_16] = {
+ .id = MT8186_IRQ_16,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT16,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ16_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ16_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ16_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ16_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_17] = {
+ .id = MT8186_IRQ_17,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT17,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ17_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ17_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ17_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ17_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_18] = {
+ .id = MT8186_IRQ_18,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT18,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ18_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ18_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ18_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ18_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_19] = {
+ .id = MT8186_IRQ_19,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT19,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ19_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ19_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ19_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ19_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_20] = {
+ .id = MT8186_IRQ_20,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT20,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ20_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ20_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ20_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ20_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_21] = {
+ .id = MT8186_IRQ_21,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT21,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ21_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ21_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ21_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ21_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_22] = {
+ .id = MT8186_IRQ_22,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT22,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ22_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ22_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ22_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ22_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_23] = {
+ .id = MT8186_IRQ_23,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT23,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ23_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ23_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ23_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ23_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_24] = {
+ .id = MT8186_IRQ_24,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT24,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON4,
+ .irq_fs_shift = IRQ24_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ24_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ24_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ24_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_25] = {
+ .id = MT8186_IRQ_25,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT25,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON4,
+ .irq_fs_shift = IRQ25_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ25_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ25_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ25_MCU_CLR_SFT,
+ },
+ [MT8186_IRQ_26] = {
+ .id = MT8186_IRQ_26,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT26,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON4,
+ .irq_fs_shift = IRQ26_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ26_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ26_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ26_MCU_CLR_SFT,
+ },
+};
+
+static const int memif_irq_usage[MT8186_MEMIF_NUM] = {
+ /* TODO: verify each memif & irq */
+ [MT8186_MEMIF_DL1] = MT8186_IRQ_0,
+ [MT8186_MEMIF_DL2] = MT8186_IRQ_1,
+ [MT8186_MEMIF_DL3] = MT8186_IRQ_2,
+ [MT8186_MEMIF_DL4] = MT8186_IRQ_3,
+ [MT8186_MEMIF_DL5] = MT8186_IRQ_4,
+ [MT8186_MEMIF_DL6] = MT8186_IRQ_5,
+ [MT8186_MEMIF_DL7] = MT8186_IRQ_6,
+ [MT8186_MEMIF_DL8] = MT8186_IRQ_7,
+ [MT8186_MEMIF_DL12] = MT8186_IRQ_9,
+ [MT8186_MEMIF_VUL12] = MT8186_IRQ_10,
+ [MT8186_MEMIF_VUL2] = MT8186_IRQ_11,
+ [MT8186_MEMIF_AWB] = MT8186_IRQ_12,
+ [MT8186_MEMIF_AWB2] = MT8186_IRQ_13,
+ [MT8186_MEMIF_VUL3] = MT8186_IRQ_14,
+ [MT8186_MEMIF_VUL4] = MT8186_IRQ_15,
+ [MT8186_MEMIF_VUL5] = MT8186_IRQ_16,
+ [MT8186_MEMIF_VUL6] = MT8186_IRQ_17,
+};
+
+static bool mt8186_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0: /* reg bit controlled by CCF */
+ case AUDIO_TOP_CON1: /* reg bit controlled by CCF */
+ case AUDIO_TOP_CON2:
+ case AUDIO_TOP_CON3:
+ case AFE_DAC_CON0:
+ case AFE_DL1_CUR_MSB:
+ case AFE_DL1_CUR:
+ case AFE_DL1_END:
+ case AFE_DL2_CUR_MSB:
+ case AFE_DL2_CUR:
+ case AFE_DL2_END:
+ case AFE_DL3_CUR_MSB:
+ case AFE_DL3_CUR:
+ case AFE_DL3_END:
+ case AFE_DL4_CUR_MSB:
+ case AFE_DL4_CUR:
+ case AFE_DL4_END:
+ case AFE_DL12_CUR_MSB:
+ case AFE_DL12_CUR:
+ case AFE_DL12_END:
+ case AFE_ADDA_SRC_DEBUG_MON0:
+ case AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_UL_SRC_MON0:
+ case AFE_ADDA_UL_SRC_MON1:
+ case AFE_SECURE_CON0:
+ case AFE_SRAM_BOUND:
+ case AFE_SECURE_CON1:
+ case AFE_VUL_CUR_MSB:
+ case AFE_VUL_CUR:
+ case AFE_VUL_END:
+ case AFE_SIDETONE_MON:
+ case AFE_SIDETONE_CON0:
+ case AFE_SIDETONE_COEFF:
+ case AFE_VUL2_CUR_MSB:
+ case AFE_VUL2_CUR:
+ case AFE_VUL2_END:
+ case AFE_VUL3_CUR_MSB:
+ case AFE_VUL3_CUR:
+ case AFE_VUL3_END:
+ case AFE_I2S_MON:
+ case AFE_DAC_MON:
+ case AFE_IRQ0_MCU_CNT_MON:
+ case AFE_IRQ6_MCU_CNT_MON:
+ case AFE_VUL4_CUR_MSB:
+ case AFE_VUL4_CUR:
+ case AFE_VUL4_END:
+ case AFE_VUL12_CUR_MSB:
+ case AFE_VUL12_CUR:
+ case AFE_VUL12_END:
+ case AFE_IRQ3_MCU_CNT_MON:
+ case AFE_IRQ4_MCU_CNT_MON:
+ case AFE_IRQ_MCU_STATUS:
+ case AFE_IRQ_MCU_CLR:
+ case AFE_IRQ_MCU_MON2:
+ case AFE_IRQ1_MCU_CNT_MON:
+ case AFE_IRQ2_MCU_CNT_MON:
+ case AFE_IRQ5_MCU_CNT_MON:
+ case AFE_IRQ7_MCU_CNT_MON:
+ case AFE_IRQ_MCU_MISS_CLR:
+ case AFE_GAIN1_CUR:
+ case AFE_GAIN2_CUR:
+ case AFE_SRAM_DELSEL_CON1:
+ case PCM_INTF_CON2:
+ case FPGA_CFG0:
+ case FPGA_CFG1:
+ case FPGA_CFG2:
+ case FPGA_CFG3:
+ case AUDIO_TOP_DBG_MON0:
+ case AUDIO_TOP_DBG_MON1:
+ case AFE_IRQ8_MCU_CNT_MON:
+ case AFE_IRQ11_MCU_CNT_MON:
+ case AFE_IRQ12_MCU_CNT_MON:
+ case AFE_IRQ9_MCU_CNT_MON:
+ case AFE_IRQ10_MCU_CNT_MON:
+ case AFE_IRQ13_MCU_CNT_MON:
+ case AFE_IRQ14_MCU_CNT_MON:
+ case AFE_IRQ15_MCU_CNT_MON:
+ case AFE_IRQ16_MCU_CNT_MON:
+ case AFE_IRQ17_MCU_CNT_MON:
+ case AFE_IRQ18_MCU_CNT_MON:
+ case AFE_IRQ19_MCU_CNT_MON:
+ case AFE_IRQ20_MCU_CNT_MON:
+ case AFE_IRQ21_MCU_CNT_MON:
+ case AFE_IRQ22_MCU_CNT_MON:
+ case AFE_IRQ23_MCU_CNT_MON:
+ case AFE_IRQ24_MCU_CNT_MON:
+ case AFE_IRQ25_MCU_CNT_MON:
+ case AFE_IRQ26_MCU_CNT_MON:
+ case AFE_IRQ31_MCU_CNT_MON:
+ case AFE_CBIP_MON0:
+ case AFE_CBIP_SLV_MUX_MON0:
+ case AFE_CBIP_SLV_DECODER_MON0:
+ case AFE_ADDA6_MTKAIF_MON0:
+ case AFE_ADDA6_MTKAIF_MON1:
+ case AFE_AWB_CUR_MSB:
+ case AFE_AWB_CUR:
+ case AFE_AWB_END:
+ case AFE_AWB2_CUR_MSB:
+ case AFE_AWB2_CUR:
+ case AFE_AWB2_END:
+ case AFE_DAI_CUR_MSB:
+ case AFE_DAI_CUR:
+ case AFE_DAI_END:
+ case AFE_DAI2_CUR_MSB:
+ case AFE_DAI2_CUR:
+ case AFE_DAI2_END:
+ case AFE_ADDA6_SRC_DEBUG_MON0:
+ case AFE_ADD6A_UL_SRC_MON0:
+ case AFE_ADDA6_UL_SRC_MON1:
+ case AFE_MOD_DAI_CUR_MSB:
+ case AFE_MOD_DAI_CUR:
+ case AFE_MOD_DAI_END:
+ case AFE_AWB_RCH_MON:
+ case AFE_AWB_LCH_MON:
+ case AFE_VUL_RCH_MON:
+ case AFE_VUL_LCH_MON:
+ case AFE_VUL12_RCH_MON:
+ case AFE_VUL12_LCH_MON:
+ case AFE_VUL2_RCH_MON:
+ case AFE_VUL2_LCH_MON:
+ case AFE_DAI_DATA_MON:
+ case AFE_MOD_DAI_DATA_MON:
+ case AFE_DAI2_DATA_MON:
+ case AFE_AWB2_RCH_MON:
+ case AFE_AWB2_LCH_MON:
+ case AFE_VUL3_RCH_MON:
+ case AFE_VUL3_LCH_MON:
+ case AFE_VUL4_RCH_MON:
+ case AFE_VUL4_LCH_MON:
+ case AFE_VUL5_RCH_MON:
+ case AFE_VUL5_LCH_MON:
+ case AFE_VUL6_RCH_MON:
+ case AFE_VUL6_LCH_MON:
+ case AFE_DL1_RCH_MON:
+ case AFE_DL1_LCH_MON:
+ case AFE_DL2_RCH_MON:
+ case AFE_DL2_LCH_MON:
+ case AFE_DL12_RCH1_MON:
+ case AFE_DL12_LCH1_MON:
+ case AFE_DL12_RCH2_MON:
+ case AFE_DL12_LCH2_MON:
+ case AFE_DL3_RCH_MON:
+ case AFE_DL3_LCH_MON:
+ case AFE_DL4_RCH_MON:
+ case AFE_DL4_LCH_MON:
+ case AFE_DL5_RCH_MON:
+ case AFE_DL5_LCH_MON:
+ case AFE_DL6_RCH_MON:
+ case AFE_DL6_LCH_MON:
+ case AFE_DL7_RCH_MON:
+ case AFE_DL7_LCH_MON:
+ case AFE_DL8_RCH_MON:
+ case AFE_DL8_LCH_MON:
+ case AFE_VUL5_CUR_MSB:
+ case AFE_VUL5_CUR:
+ case AFE_VUL5_END:
+ case AFE_VUL6_CUR_MSB:
+ case AFE_VUL6_CUR:
+ case AFE_VUL6_END:
+ case AFE_ADDA_DL_SDM_FIFO_MON:
+ case AFE_ADDA_DL_SRC_LCH_MON:
+ case AFE_ADDA_DL_SRC_RCH_MON:
+ case AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_CONNSYS_I2S_MON:
+ case AFE_ASRC_2CH_CON0:
+ case AFE_ASRC_2CH_CON2:
+ case AFE_ASRC_2CH_CON3:
+ case AFE_ASRC_2CH_CON4:
+ case AFE_ASRC_2CH_CON5:
+ case AFE_ASRC_2CH_CON7:
+ case AFE_ASRC_2CH_CON8:
+ case AFE_ASRC_2CH_CON12:
+ case AFE_ASRC_2CH_CON13:
+ case AFE_ADDA_MTKAIF_MON0:
+ case AFE_ADDA_MTKAIF_MON1:
+ case AFE_AUD_PAD_TOP:
+ case AFE_DL_NLE_R_MON0:
+ case AFE_DL_NLE_R_MON1:
+ case AFE_DL_NLE_R_MON2:
+ case AFE_DL_NLE_L_MON0:
+ case AFE_DL_NLE_L_MON1:
+ case AFE_DL_NLE_L_MON2:
+ case AFE_GENERAL1_ASRC_2CH_CON0:
+ case AFE_GENERAL1_ASRC_2CH_CON2:
+ case AFE_GENERAL1_ASRC_2CH_CON3:
+ case AFE_GENERAL1_ASRC_2CH_CON4:
+ case AFE_GENERAL1_ASRC_2CH_CON5:
+ case AFE_GENERAL1_ASRC_2CH_CON7:
+ case AFE_GENERAL1_ASRC_2CH_CON8:
+ case AFE_GENERAL1_ASRC_2CH_CON12:
+ case AFE_GENERAL1_ASRC_2CH_CON13:
+ case AFE_GENERAL2_ASRC_2CH_CON0:
+ case AFE_GENERAL2_ASRC_2CH_CON2:
+ case AFE_GENERAL2_ASRC_2CH_CON3:
+ case AFE_GENERAL2_ASRC_2CH_CON4:
+ case AFE_GENERAL2_ASRC_2CH_CON5:
+ case AFE_GENERAL2_ASRC_2CH_CON7:
+ case AFE_GENERAL2_ASRC_2CH_CON8:
+ case AFE_GENERAL2_ASRC_2CH_CON12:
+ case AFE_GENERAL2_ASRC_2CH_CON13:
+ case AFE_DL5_CUR_MSB:
+ case AFE_DL5_CUR:
+ case AFE_DL5_END:
+ case AFE_DL6_CUR_MSB:
+ case AFE_DL6_CUR:
+ case AFE_DL6_END:
+ case AFE_DL7_CUR_MSB:
+ case AFE_DL7_CUR:
+ case AFE_DL7_END:
+ case AFE_DL8_CUR_MSB:
+ case AFE_DL8_CUR:
+ case AFE_DL8_END:
+ case AFE_PROT_SIDEBAND_MON:
+ case AFE_DOMAIN_SIDEBAND0_MON:
+ case AFE_DOMAIN_SIDEBAND1_MON:
+ case AFE_DOMAIN_SIDEBAND2_MON:
+ case AFE_DOMAIN_SIDEBAND3_MON:
+ case AFE_APLL1_TUNER_CFG: /* [20:31] is monitor */
+ case AFE_APLL2_TUNER_CFG: /* [20:31] is monitor */
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8186_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .volatile_reg = mt8186_is_volatile_reg,
+
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = AFE_MAX_REGISTER,
+
+ .cache_type = REGCACHE_FLAT,
+};
+
+static irqreturn_t mt8186_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ unsigned int status;
+ unsigned int status_mcu;
+ unsigned int mcu_en;
+ int ret;
+ int i;
+
+ /* get irq that is sent to MCU */
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+ if (ret) {
+ dev_err(afe->dev, "%s, get irq direction fail, ret %d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ /* only care IRQ which is sent to MCU */
+ status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
+
+ if (ret || status_mcu == 0) {
+ dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n",
+ __func__, ret, status, mcu_en);
+
+ goto err_irq;
+ }
+
+ for (i = 0; i < MT8186_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (status_mcu & (1 << irq->irq_data->irq_en_shift))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, status_mcu);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8186_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ unsigned int value = 0;
+ int ret;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
+
+ ret = regmap_read_poll_timeout(afe->regmap,
+ AFE_DAC_MON,
+ value,
+ (value & AFE_ON_RETM_MASK_SFT) == 0,
+ 20,
+ 1 * 1000 * 1000);
+ if (ret) {
+ dev_err(afe->dev, "%s(), ret %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* make sure all irq status are cleared */
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, 0xffffffff);
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, 0xffffffff);
+
+ /* reset sgen */
+ regmap_write(afe->regmap, AFE_SINEGEN_CON0, 0x0);
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON2,
+ INNER_LOOP_BACK_MODE_MASK_SFT,
+ 0x3f << INNER_LOOP_BACK_MODE_SFT);
+
+ /* cache only */
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8186_afe_disable_cgs(afe);
+ mt8186_afe_disable_clock(afe);
+
+ return 0;
+}
+
+static int mt8186_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8186_afe_enable_clock(afe);
+ if (ret)
+ return ret;
+
+ ret = mt8186_afe_enable_cgs(afe);
+ if (ret)
+ return ret;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ /* enable audio sys DCM for power saving */
+ regmap_update_bits(afe_priv->infracfg, PERI_BUS_DCM_CTRL, BIT(29), BIT(29));
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, BIT(29), BIT(29));
+
+ /* force cpu use 8_24 format when writing 32bit data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_CON0, CPU_HD_ALIGN_MASK_SFT, 0);
+
+ /* set all output port to 24bit */
+ regmap_write(afe->regmap, AFE_CONN_24BIT, 0xffffffff);
+ regmap_write(afe->regmap, AFE_CONN_24BIT_1, 0xffffffff);
+
+ /* enable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, AUDIO_AFE_ON_MASK_SFT, BIT(0));
+
+skip_regmap:
+ return 0;
+}
+
+static int mt8186_afe_component_probe(struct snd_soc_component *component)
+{
+ mtk_afe_add_sub_dai_control(component);
+ mt8186_add_misc_control(component);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt8186_afe_component = {
+ .name = AFE_PCM_NAME,
+ .pcm_construct = mtk_afe_pcm_new,
+ .pointer = mtk_afe_pcm_pointer,
+ .probe = mt8186_afe_component_probe,
+};
+
+static int mt8186_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8186_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8186_memif_dai_driver);
+
+ dai->controls = mt8186_pcm_kcontrols;
+ dai->num_controls = ARRAY_SIZE(mt8186_pcm_kcontrols);
+ dai->dapm_widgets = mt8186_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8186_memif_widgets);
+ dai->dapm_routes = mt8186_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8186_memif_routes);
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8186_dai_adda_register,
+ mt8186_dai_i2s_register,
+ mt8186_dai_tdm_register,
+ mt8186_dai_hw_gain_register,
+ mt8186_dai_src_register,
+ mt8186_dai_pcm_register,
+ mt8186_dai_hostless_register,
+ mt8186_dai_memif_register,
+};
+
+static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8186_afe_private *afe_priv;
+ struct reset_control *rstc;
+ struct device *dev = &pdev->dev;
+ int i, ret, irq_id;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ /* init audio related clock */
+ ret = mt8186_init_clock(afe);
+ if (ret) {
+ dev_err(dev, "init clock error, ret %d\n", ret);
+ return ret;
+ }
+
+ /* init memif */
+ afe->memif_32bit_supported = 0;
+ afe->memif_size = MT8186_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = memif_irq_usage[i];
+ afe->memif[i].const_irq = 1;
+ }
+
+ mutex_init(&afe->irq_alloc_lock); /* needed when dynamic irq */
+
+ /* init irq */
+ afe->irqs_size = MT8186_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id <= 0)
+ return dev_err_probe(dev, irq_id < 0 ? irq_id : -ENXIO,
+ "no irq found");
+
+ ret = devm_request_irq(dev, irq_id, mt8186_afe_irq_handler,
+ IRQF_TRIGGER_NONE,
+ "Afe_ISR_Handle", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+
+ ret = enable_irq_wake(irq_id);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "enable_irq_wake %d\n", irq_id);
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "dai register i %d fail\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ /* reset controller to reset audio regs before regmap cache */
+ rstc = devm_reset_control_get_exclusive(dev, "audiosys");
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n");
+
+ ret = reset_control_reset(rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to resume device\n");
+
+ afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr,
+ &mt8186_afe_regmap_config);
+ if (IS_ERR(afe->regmap)) {
+ ret = PTR_ERR(afe->regmap);
+ goto err_pm_disable;
+ }
+
+ /* others */
+ afe->mtk_afe_hardware = &mt8186_afe_hardware;
+ afe->memif_fs = mt8186_memif_fs;
+ afe->irq_fs = mt8186_irq_fs;
+ afe->get_dai_fs = mt8186_get_dai_fs;
+ afe->get_memif_pbuf_size = mt8186_get_memif_pbuf_size;
+
+ afe->runtime_resume = mt8186_afe_runtime_resume;
+ afe->runtime_suspend = mt8186_afe_runtime_suspend;
+
+ /* register platform */
+ dev_dbg(dev, "%s(), devm_snd_soc_register_component\n", __func__);
+
+ ret = devm_snd_soc_register_component(dev,
+ &mt8186_afe_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_err(dev, "err_dai_component\n");
+ goto err_pm_disable;
+ }
+
+ ret = pm_runtime_put_sync(dev);
+ if (ret) {
+ pm_runtime_get_noresume(dev);
+ dev_err(dev, "failed to suspend device: %d\n", ret);
+ goto err_pm_disable;
+ }
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_put_noidle(dev);
+ pm_runtime_set_suspended(dev);
+
+ return ret;
+}
+
+static const struct of_device_id mt8186_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8186-sound", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8186_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8186_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8186_afe_runtime_suspend,
+ mt8186_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8186_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8186-audio",
+ .of_match_table = mt8186_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8186_afe_pm_ops),
+ },
+ .probe = mt8186_afe_pcm_dev_probe,
+};
+
+module_platform_driver(mt8186_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8186");
+MODULE_AUTHOR("Jiaxin Yu <jiaxin.yu@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c
new file mode 100644
index 000000000000..5666be6b1bd2
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8186-audsys-clk.h -- Mediatek 8186 audsys clock control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "mt8186-afe-common.h"
+#include "mt8186-audsys-clk.h"
+#include "mt8186-audsys-clkid.h"
+#include "mt8186-reg.h"
+
+struct afe_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ int reg;
+ u8 bit;
+ const struct clk_ops *ops;
+ unsigned long flags;
+ u8 cg_flags;
+};
+
+#define GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, _flags, _cgflags) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .reg = _reg, \
+ .bit = _bit, \
+ .flags = _flags, \
+ .cg_flags = _cgflags, \
+ }
+
+#define GATE_AFE(_id, _name, _parent, _reg, _bit) \
+ GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, \
+ CLK_SET_RATE_PARENT, CLK_GATE_SET_TO_DISABLE)
+
+#define GATE_AUD0(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON0, _bit)
+
+#define GATE_AUD1(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON1, _bit)
+
+#define GATE_AUD2(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON2, _bit)
+
+static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = {
+ /* AUD0 */
+ GATE_AUD0(CLK_AUD_AFE, "aud_afe_clk", "top_audio", 2),
+ GATE_AUD0(CLK_AUD_22M, "aud_apll22m_clk", "top_aud_engen1", 8),
+ GATE_AUD0(CLK_AUD_24M, "aud_apll24m_clk", "top_aud_engen2", 9),
+ GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner_clk", "top_aud_engen2", 18),
+ GATE_AUD0(CLK_AUD_APLL_TUNER, "aud_apll_tuner_clk", "top_aud_engen1", 19),
+ GATE_AUD0(CLK_AUD_TDM, "aud_tdm_clk", "top_aud_1", 20),
+ GATE_AUD0(CLK_AUD_ADC, "aud_adc_clk", "top_audio", 24),
+ GATE_AUD0(CLK_AUD_DAC, "aud_dac_clk", "top_audio", 25),
+ GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis_clk", "top_audio", 26),
+ GATE_AUD0(CLK_AUD_TML, "aud_tml_clk", "top_audio", 27),
+ GATE_AUD0(CLK_AUD_NLE, "aud_nle_clk", "top_audio", 28),
+
+ /* AUD1 */
+ GATE_AUD1(CLK_AUD_I2S1_BCLK, "aud_i2s1_bclk", "top_audio", 4),
+ GATE_AUD1(CLK_AUD_I2S2_BCLK, "aud_i2s2_bclk", "top_audio", 5),
+ GATE_AUD1(CLK_AUD_I2S3_BCLK, "aud_i2s3_bclk", "top_audio", 6),
+ GATE_AUD1(CLK_AUD_I2S4_BCLK, "aud_i2s4_bclk", "top_audio", 7),
+ GATE_AUD1(CLK_AUD_CONNSYS_I2S_ASRC, "aud_connsys_i2s_asrc", "top_audio", 12),
+ GATE_AUD1(CLK_AUD_GENERAL1_ASRC, "aud_general1_asrc", "top_audio", 13),
+ GATE_AUD1(CLK_AUD_GENERAL2_ASRC, "aud_general2_asrc", "top_audio", 14),
+ GATE_AUD1(CLK_AUD_DAC_HIRES, "aud_dac_hires_clk", "top_audio_h", 15),
+ GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires_clk", "top_audio_h", 16),
+ GATE_AUD1(CLK_AUD_ADC_HIRES_TML, "aud_adc_hires_tml", "top_audio_h", 17),
+ GATE_AUD1(CLK_AUD_ADDA6_ADC, "aud_adda6_adc", "top_audio", 20),
+ GATE_AUD1(CLK_AUD_ADDA6_ADC_HIRES, "aud_adda6_adc_hires", "top_audio_h", 21),
+ GATE_AUD1(CLK_AUD_3RD_DAC, "aud_3rd_dac", "top_audio", 28),
+ GATE_AUD1(CLK_AUD_3RD_DAC_PREDIS, "aud_3rd_dac_predis", "top_audio", 29),
+ GATE_AUD1(CLK_AUD_3RD_DAC_TML, "aud_3rd_dac_tml", "top_audio", 30),
+ GATE_AUD1(CLK_AUD_3RD_DAC_HIRES, "aud_3rd_dac_hires", "top_audio_h", 31),
+
+ /* AUD2 */
+ GATE_AUD2(CLK_AUD_ETDM_IN1_BCLK, "aud_etdm_in1_bclk", "top_audio", 23),
+ GATE_AUD2(CLK_AUD_ETDM_OUT1_BCLK, "aud_etdm_out1_bclk", "top_audio", 24),
+};
+
+static void mt8186_audsys_clk_unregister(void *data)
+{
+ struct mtk_base_afe *afe = data;
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ if (!afe_priv)
+ return;
+
+ for (i = 0; i < CLK_AUD_NR_CLK; i++) {
+ cl = afe_priv->lookup[i];
+ if (!cl)
+ continue;
+
+ clk = cl->clk;
+ clk_unregister_gate(clk);
+
+ clkdev_drop(cl);
+ }
+}
+
+int mt8186_audsys_clk_register(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AUD_NR_CLK,
+ sizeof(*afe_priv->lookup),
+ GFP_KERNEL);
+
+ if (!afe_priv->lookup)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ const struct afe_gate *gate = &aud_clks[i];
+
+ clk = clk_register_gate(afe->dev, gate->name, gate->parent_name,
+ gate->flags, afe->base_addr + gate->reg,
+ gate->bit, gate->cg_flags, NULL);
+
+ if (IS_ERR(clk)) {
+ dev_err(afe->dev, "Failed to register clk %s: %ld\n",
+ gate->name, PTR_ERR(clk));
+ continue;
+ }
+
+ /* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return -ENOMEM;
+
+ cl->clk = clk;
+ cl->con_id = gate->name;
+ cl->dev_id = dev_name(afe->dev);
+ clkdev_add(cl);
+
+ afe_priv->lookup[i] = cl;
+ }
+
+ return devm_add_action_or_reset(afe->dev, mt8186_audsys_clk_unregister, afe);
+}
+
diff --git a/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h
new file mode 100644
index 000000000000..897a2914dc19
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * mt8186-audsys-clk.h -- Mediatek 8186 audsys clock definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MT8186_AUDSYS_CLK_H_
+#define _MT8186_AUDSYS_CLK_H_
+
+int mt8186_audsys_clk_register(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h b/sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h
new file mode 100644
index 000000000000..3ce5937c1823
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * mt8186-audsys-clkid.h -- Mediatek 8186 audsys clock id definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT8186_AUDSYS_CLKID_H_
+#define _MT8186_AUDSYS_CLKID_H_
+
+enum{
+ CLK_AUD_AFE,
+ CLK_AUD_22M,
+ CLK_AUD_24M,
+ CLK_AUD_APLL2_TUNER,
+ CLK_AUD_APLL_TUNER,
+ CLK_AUD_TDM,
+ CLK_AUD_ADC,
+ CLK_AUD_DAC,
+ CLK_AUD_DAC_PREDIS,
+ CLK_AUD_TML,
+ CLK_AUD_NLE,
+ CLK_AUD_I2S1_BCLK,
+ CLK_AUD_I2S2_BCLK,
+ CLK_AUD_I2S3_BCLK,
+ CLK_AUD_I2S4_BCLK,
+ CLK_AUD_CONNSYS_I2S_ASRC,
+ CLK_AUD_GENERAL1_ASRC,
+ CLK_AUD_GENERAL2_ASRC,
+ CLK_AUD_DAC_HIRES,
+ CLK_AUD_ADC_HIRES,
+ CLK_AUD_ADC_HIRES_TML,
+ CLK_AUD_ADDA6_ADC,
+ CLK_AUD_ADDA6_ADC_HIRES,
+ CLK_AUD_3RD_DAC,
+ CLK_AUD_3RD_DAC_PREDIS,
+ CLK_AUD_3RD_DAC_TML,
+ CLK_AUD_3RD_DAC_HIRES,
+ CLK_AUD_ETDM_IN1_BCLK,
+ CLK_AUD_ETDM_OUT1_BCLK,
+ CLK_AUD_NR_CLK,
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
new file mode 100644
index 000000000000..e74174ae8873
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
@@ -0,0 +1,776 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI ADDA Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include "mt8186-afe-clk.h"
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-gpio.h"
+#include "mt8186-interconnection.h"
+#include "../common/mtk-dai-adda-common.h"
+
+enum {
+ UL_IIR_SW = 0,
+ UL_IIR_5HZ,
+ UL_IIR_10HZ,
+ UL_IIR_25HZ,
+ UL_IIR_50HZ,
+ UL_IIR_75HZ,
+};
+
+enum {
+ AUDIO_SDM_LEVEL_MUTE = 0,
+ AUDIO_SDM_LEVEL_NORMAL = 0x1d,
+ /* if you change level normal */
+ /* you need to change formula of hp impedance and dc trim too */
+};
+
+enum {
+ AUDIO_SDM_2ND = 0,
+ AUDIO_SDM_3RD,
+};
+
+#define SDM_AUTO_RESET_THRESHOLD 0x190000
+
+struct mtk_afe_adda_priv {
+ int dl_rate;
+ int ul_rate;
+};
+
+static struct mtk_afe_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id;
+
+ if (strncmp(name, "aud_dac", 7) == 0 || strncmp(name, "aud_adc", 7) == 0)
+ dai_id = MT8186_DAI_ADDA;
+ else
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN3, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN3, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN3, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN3, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN3_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN3_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN3_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1 Switch", AFE_CONN3_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN3,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN3,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN3,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1 Switch", AFE_CONN3,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1 Switch", AFE_CONN3,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH1 Switch", AFE_CONN3_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_2_OUT_CH1 Switch", AFE_CONN3_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN4, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN4, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN4, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN4, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN4, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN4, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN4, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN4_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN4_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN4_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2 Switch", AFE_CONN4_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN4,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN4,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN4,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2 Switch", AFE_CONN4,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2 Switch", AFE_CONN4,
+ I_PCM_2_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH2 Switch", AFE_CONN4_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_2_OUT_CH2 Switch", AFE_CONN4_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+enum {
+ SUPPLY_SEQ_ADDA_AFE_ON,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
+ SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA_FIFO,
+ SUPPLY_SEQ_ADDA_AP_DMIC,
+ SUPPLY_SEQ_ADDA_UL_ON,
+};
+
+static int mtk_adda_ul_src_dmic(struct mtk_base_afe *afe, int id)
+{
+ unsigned int reg;
+
+ switch (id) {
+ case MT8186_DAI_ADDA:
+ case MT8186_DAI_AP_DMIC:
+ reg = AFE_ADDA_UL_SRC_CON0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, reg,
+ DIGMIC_3P25M_1P625M_SEL_MASK_SFT, 0);
+ regmap_update_bits(afe->regmap, reg,
+ DMIC_LOW_POWER_CTL_MASK_SFT, 0);
+
+ /* turn on dmic, ch1, ch2 */
+ regmap_update_bits(afe->regmap, reg,
+ UL_SDM_3_LEVEL_MASK_SFT,
+ BIT(UL_SDM_3_LEVEL_SFT));
+ regmap_update_bits(afe->regmap, reg,
+ UL_MODE_3P25M_CH1_CTL_MASK_SFT,
+ BIT(UL_MODE_3P25M_CH1_CTL_SFT));
+ regmap_update_bits(afe->regmap, reg,
+ UL_MODE_3P25M_CH2_CTL_MASK_SFT,
+ BIT(UL_MODE_3P25M_CH2_CTL_SFT));
+
+ return 0;
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int mtkaif_dmic = afe_priv->mtkaif_dmic;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x, mtkaif_dmic %d\n",
+ __func__, w->name, event, mtkaif_dmic);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_afe_gpio_request(afe->dev, true, MT8186_DAI_ADDA, 1);
+
+ /* update setting to dmic */
+ if (mtkaif_dmic) {
+ /* mtkaif_rxif_data_mode = 1, dmic */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
+ 0x1, 0x1);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
+ MTKAIF_RXIF_VOICE_MODE_MASK_SFT,
+ 0x0);
+ mtk_adda_ul_src_dmic(afe, MT8186_DAI_ADDA);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ mt8186_afe_gpio_request(afe->dev, false, MT8186_DAI_ADDA, 1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2)
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x39);
+ else
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int delay_data;
+ int delay_cycle;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) {
+ /* set protocol 2 */
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0x10000);
+ /* mtkaif_rxif_clkinv_adc inverse */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ MTKAIF_RXIF_CLKINV_ADC_MASK_SFT,
+ BIT(MTKAIF_RXIF_CLKINV_ADC_SFT));
+
+ if (snd_soc_dapm_widget_name_cmp(w, "ADDA_MTKAIF_CFG") == 0) {
+ if (afe_priv->mtkaif_chosen_phase[0] < 0 &&
+ afe_priv->mtkaif_chosen_phase[1] < 0) {
+ dev_err(afe->dev,
+ "%s(), calib fail mtkaif_chosen_phase[0/1]:%d/%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[0],
+ afe_priv->mtkaif_chosen_phase[1]);
+ break;
+ }
+
+ if (afe_priv->mtkaif_chosen_phase[0] < 0 ||
+ afe_priv->mtkaif_chosen_phase[1] < 0) {
+ dev_err(afe->dev,
+ "%s(), skip delay setting mtkaif_chosen_phase[0/1]:%d/%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[0],
+ afe_priv->mtkaif_chosen_phase[1]);
+ break;
+ }
+ }
+
+ /* set delay for ch12 */
+ if (afe_priv->mtkaif_phase_cycle[0] >=
+ afe_priv->mtkaif_phase_cycle[1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[0] -
+ afe_priv->mtkaif_phase_cycle[1];
+ } else {
+ delay_data = DELAY_DATA_MISO2;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
+ afe_priv->mtkaif_phase_cycle[0];
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_DATA_MASK_SFT,
+ delay_data <<
+ MTKAIF_RXIF_DELAY_DATA_SFT);
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT,
+ delay_cycle <<
+ MTKAIF_RXIF_DELAY_CYCLE_SFT);
+
+ } else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) {
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0x10000);
+ } else {
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_afe_gpio_request(afe->dev, true, MT8186_DAI_ADDA, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ mt8186_afe_gpio_request(afe->dev, false, MT8186_DAI_ADDA, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt8186_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->mtkaif_dmic;
+
+ return 0;
+}
+
+static int mt8186_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dmic_on;
+
+ dmic_on = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, dmic_on %d\n",
+ __func__, kcontrol->id.name, dmic_on);
+
+ if (afe_priv->mtkaif_dmic == dmic_on)
+ return 0;
+
+ afe_priv->mtkaif_dmic = dmic_on;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new mtk_adda_controls[] = {
+ SOC_SINGLE("ADDA_DL_GAIN", AFE_ADDA_DL_SRC2_CON1,
+ DL_2_GAIN_CTL_PRE_SFT, DL_2_GAIN_CTL_PRE_MASK, 0),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC Switch", 0,
+ mt8186_adda_dmic_get, mt8186_adda_dmic_set),
+};
+
+/* ADDA UL MUX */
+enum {
+ ADDA_UL_MUX_MTKAIF = 0,
+ ADDA_UL_MUX_AP_DMIC,
+ ADDA_UL_MUX_MASK = 0x1,
+};
+
+static const char * const adda_ul_mux_map[] = {
+ "MTKAIF", "AP_DMIC"
+};
+
+static int adda_ul_map_value[] = {
+ ADDA_UL_MUX_MTKAIF,
+ ADDA_UL_MUX_AP_DMIC,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adda_ul_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADDA_UL_MUX_MASK,
+ adda_ul_mux_map,
+ adda_ul_map_value);
+
+static const struct snd_kcontrol_new adda_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch1_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch2_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_CTL_PRE_SFT, 0,
+ mtk_adda_dl_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_CTL_SFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("AUD_PAD_TOP", SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
+ AFE_AUD_PAD_TOP, RG_RX_FIFO_ON_SFT, 0,
+ mtk_adda_pad_top_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_FIFO_AUTO_RST_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADDA_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ul_mux_control),
+
+ SND_SOC_DAPM_INPUT("AP_DMIC_INPUT"),
+
+ /* clock */
+ SND_SOC_DAPM_CLOCK_SUPPLY("top_mux_audio_h"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_hires_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_hires_clk"),
+};
+
+#define HIRES_THRESHOLD 48000
+static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_adda_priv *adda_priv;
+
+ adda_priv = get_adda_priv_by_name(afe, w->name);
+
+ if (!adda_priv) {
+ dev_err(afe->dev, "%s(), adda_priv == NULL", __func__);
+ return 0;
+ }
+
+ return (adda_priv->dl_rate > HIRES_THRESHOLD) ? 1 : 0;
+}
+
+static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_adda_priv *adda_priv;
+
+ adda_priv = get_adda_priv_by_name(afe, w->name);
+
+ if (!adda_priv) {
+ dev_err(afe->dev, "%s(), adda_priv == NULL", __func__);
+ return 0;
+ }
+
+ return (adda_priv->ul_rate > HIRES_THRESHOLD) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* playback */
+ {"ADDA_DL_CH1", "DL1_CH1 Switch", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH1 Switch", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH2 Switch", "DL1"},
+
+ {"ADDA_DL_CH1", "DL12_CH1 Switch", "DL12"},
+ {"ADDA_DL_CH2", "DL12_CH2 Switch", "DL12"},
+
+ {"ADDA_DL_CH1", "DL6_CH1 Switch", "DL6"},
+ {"ADDA_DL_CH2", "DL6_CH2 Switch", "DL6"},
+
+ {"ADDA_DL_CH1", "DL8_CH1 Switch", "DL8"},
+ {"ADDA_DL_CH2", "DL8_CH2 Switch", "DL8"},
+
+ {"ADDA_DL_CH1", "DL2_CH1 Switch", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH1 Switch", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH2 Switch", "DL2"},
+
+ {"ADDA_DL_CH1", "DL3_CH1 Switch", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH1 Switch", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH2 Switch", "DL3"},
+
+ {"ADDA_DL_CH1", "DL4_CH1 Switch", "DL4"},
+ {"ADDA_DL_CH2", "DL4_CH2 Switch", "DL4"},
+
+ {"ADDA_DL_CH1", "DL5_CH1 Switch", "DL5"},
+ {"ADDA_DL_CH2", "DL5_CH2 Switch", "DL5"},
+
+ {"ADDA Playback", NULL, "ADDA_DL_CH1"},
+ {"ADDA Playback", NULL, "ADDA_DL_CH2"},
+
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+
+ /* capture */
+ {"ADDA_UL_Mux", "MTKAIF", "ADDA Capture"},
+ {"ADDA_UL_Mux", "AP_DMIC", "AP DMIC Capture"},
+
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+ {"ADDA Capture", NULL, "AUD_PAD_TOP"},
+ {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
+
+ {"AP DMIC Capture", NULL, "ADDA Enable"},
+ {"AP DMIC Capture", NULL, "ADDA Capture Enable"},
+ {"AP DMIC Capture", NULL, "ADDA_FIFO"},
+ {"AP DMIC Capture", NULL, "AP_DMIC_EN"},
+
+ {"AP DMIC Capture", NULL, "AP_DMIC_INPUT"},
+
+ /* clk */
+ {"ADDA Playback", NULL, "aud_dac_clk"},
+ {"ADDA Playback", NULL, "aud_dac_predis_clk"},
+ {"ADDA Playback", NULL, "aud_dac_hires_clk", mtk_afe_dac_hires_connect},
+
+ {"ADDA Capture Enable", NULL, "aud_adc_clk"},
+ {"ADDA Capture Enable", NULL, "aud_adc_hires_clk",
+ mtk_afe_adc_hires_connect},
+
+ /* hires source from apll1 */
+ {"top_mux_audio_h", NULL, APLL2_W_NAME},
+
+ {"aud_dac_hires_clk", NULL, "top_mux_audio_h"},
+ {"aud_adc_hires_clk", NULL, "top_mux_audio_h"},
+};
+
+/* dai ops */
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ unsigned int rate = params_rate(params);
+ int id = dai->id;
+ struct mtk_afe_adda_priv *adda_priv = afe_priv->dai_priv[id];
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, id, substream->stream, rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int dl_src2_con0;
+ unsigned int dl_src2_con1;
+
+ adda_priv->dl_rate = rate;
+
+ /* set sampling rate */
+ dl_src2_con0 = mtk_adda_dl_rate_transform(afe, rate) <<
+ DL_2_INPUT_MODE_CTL_SFT;
+
+ /* set output mode, UP_SAMPLING_RATE_X8 */
+ dl_src2_con0 |= (0x3 << DL_2_OUTPUT_SEL_CTL_SFT);
+
+ /* turn off mute function */
+ dl_src2_con0 |= BIT(DL_2_MUTE_CH2_OFF_CTL_PRE_SFT);
+ dl_src2_con0 |= BIT(DL_2_MUTE_CH1_OFF_CTL_PRE_SFT);
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ if (rate == 8000 || rate == 16000)
+ dl_src2_con0 |= BIT(DL_2_VOICE_MODE_CTL_PRE_SFT);
+
+ /* SA suggest apply -0.3db to audio/speech path */
+ dl_src2_con1 = MTK_AFE_ADDA_DL_GAIN_NORMAL <<
+ DL_2_GAIN_CTL_PRE_SFT;
+
+ /* turn on down-link gain */
+ dl_src2_con0 |= BIT(DL_2_GAIN_ON_CTL_PRE_SFT);
+
+ if (id == MT8186_DAI_ADDA) {
+ /* clean predistortion */
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SRC2_CON0, dl_src2_con0);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SRC2_CON1, dl_src2_con1);
+
+ /* set sdm gain */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ ATTGAIN_CTL_MASK_SFT,
+ AUDIO_SDM_LEVEL_NORMAL <<
+ ATTGAIN_CTL_SFT);
+
+ /* Use new 2nd sdm */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DITHER_CON,
+ AFE_DL_SDM_DITHER_64TAP_EN_MASK_SFT,
+ BIT(AFE_DL_SDM_DITHER_64TAP_EN_SFT));
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ AFE_DL_USE_NEW_2ND_SDM_MASK_SFT,
+ BIT(AFE_DL_USE_NEW_2ND_SDM_SFT));
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ USE_3RD_SDM_MASK_SFT,
+ AUDIO_SDM_2ND << USE_3RD_SDM_SFT);
+
+ /* sdm auto reset */
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ SDM_AUTO_RESET_THRESHOLD);
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ SDM_AUTO_RESET_TEST_ON_MASK_SFT,
+ BIT(SDM_AUTO_RESET_TEST_ON_SFT));
+ }
+ } else {
+ unsigned int ul_src_con0 = 0;
+ unsigned int voice_mode = mtk_adda_ul_rate_transform(afe, rate);
+
+ adda_priv->ul_rate = rate;
+ ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
+
+ /* enable iir */
+ ul_src_con0 |= (1 << UL_IIR_ON_TMP_CTL_SFT) &
+ UL_IIR_ON_TMP_CTL_MASK_SFT;
+ ul_src_con0 |= (UL_IIR_SW << UL_IIRMODE_CTL_SFT) &
+ UL_IIRMODE_CTL_MASK_SFT;
+ switch (id) {
+ case MT8186_DAI_ADDA:
+ case MT8186_DAI_AP_DMIC:
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_02_01, 0);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_04_03, 0x3fb8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_06_05, 0x3fb80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_08_07, 0x3fb80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_10_09, 0xc048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL_SRC_CON0, ul_src_con0);
+
+ /* Using Internal ADC */
+ regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, BIT(0), 0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0, BIT(0), 0);
+ break;
+ default:
+ break;
+ }
+
+ /* ap dmic */
+ switch (id) {
+ case MT8186_DAI_AP_DMIC:
+ mtk_adda_ul_src_dmic(afe, id);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8186_DAI_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC",
+ .id = MT8186_DAI_AP_DMIC,
+ .capture = {
+ .stream_name = "AP DMIC Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+int mt8186_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->controls = mtk_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+
+ /* set dai priv */
+ ret = mt8186_dai_set_priv(afe, MT8186_DAI_ADDA,
+ sizeof(struct mtk_afe_adda_priv), NULL);
+ if (ret)
+ return ret;
+
+ /* ap dmic priv share with adda */
+ afe_priv->dai_priv[MT8186_DAI_AP_DMIC] =
+ afe_priv->dai_priv[MT8186_DAI_ADDA];
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-hostless.c b/sound/soc/mediatek/mt8186/mt8186-dai-hostless.c
new file mode 100644
index 000000000000..bf0d83840cf4
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-hostless.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI Hostless Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include "mt8186-afe-common.h"
+
+static const struct snd_pcm_hardware mt8186_hostless_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .period_bytes_min = 256,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 4 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+/* dai component */
+static const struct snd_soc_dapm_route mtk_dai_hostless_routes[] = {
+ /* Hostless ADDA Loopback */
+ {"ADDA_DL_CH1", "ADDA_UL_CH1 Switch", "Hostless LPBK DL"},
+ {"ADDA_DL_CH1", "ADDA_UL_CH2 Switch", "Hostless LPBK DL"},
+ {"ADDA_DL_CH2", "ADDA_UL_CH1 Switch", "Hostless LPBK DL"},
+ {"ADDA_DL_CH2", "ADDA_UL_CH2 Switch", "Hostless LPBK DL"},
+ {"I2S1_CH1", "ADDA_UL_CH1 Switch", "Hostless LPBK DL"},
+ {"I2S1_CH2", "ADDA_UL_CH2 Switch", "Hostless LPBK DL"},
+ {"I2S3_CH1", "ADDA_UL_CH1 Switch", "Hostless LPBK DL"},
+ {"I2S3_CH1", "ADDA_UL_CH2 Switch", "Hostless LPBK DL"},
+ {"I2S3_CH2", "ADDA_UL_CH1 Switch", "Hostless LPBK DL"},
+ {"I2S3_CH2", "ADDA_UL_CH2 Switch", "Hostless LPBK DL"},
+ {"Hostless LPBK UL", NULL, "ADDA_UL_Mux"},
+
+ /* Hostelss FM */
+ /* connsys_i2s to hw gain 1*/
+ {"Hostless FM UL", NULL, "Connsys I2S"},
+
+ {"HW_GAIN1_IN_CH1", "CONNSYS_I2S_CH1 Switch", "Hostless FM DL"},
+ {"HW_GAIN1_IN_CH2", "CONNSYS_I2S_CH2 Switch", "Hostless FM DL"},
+ /* hw gain to adda dl */
+ {"Hostless FM UL", NULL, "HW Gain 1 Out"},
+
+ {"ADDA_DL_CH1", "GAIN1_OUT_CH1 Switch", "Hostless FM DL"},
+ {"ADDA_DL_CH2", "GAIN1_OUT_CH2 Switch", "Hostless FM DL"},
+ /* hw gain to i2s3 */
+ {"I2S3_CH1", "GAIN1_OUT_CH1 Switch", "Hostless FM DL"},
+ {"I2S3_CH2", "GAIN1_OUT_CH2 Switch", "Hostless FM DL"},
+ /* hw gain to i2s1 */
+ {"I2S1_CH1", "GAIN1_OUT_CH1 Switch", "Hostless FM DL"},
+ {"I2S1_CH2", "GAIN1_OUT_CH2 Switch", "Hostless FM DL"},
+
+ /* Hostless_SRC */
+ {"ADDA_DL_CH1", "SRC_1_OUT_CH1 Switch", "Hostless_SRC_1_DL"},
+ {"ADDA_DL_CH2", "SRC_1_OUT_CH2 Switch", "Hostless_SRC_1_DL"},
+ {"I2S1_CH1", "SRC_1_OUT_CH1 Switch", "Hostless_SRC_1_DL"},
+ {"I2S1_CH2", "SRC_1_OUT_CH2 Switch", "Hostless_SRC_1_DL"},
+ {"I2S3_CH1", "SRC_1_OUT_CH1 Switch", "Hostless_SRC_1_DL"},
+ {"I2S3_CH2", "SRC_1_OUT_CH2 Switch", "Hostless_SRC_1_DL"},
+ {"Hostless_SRC_1_UL", NULL, "HW_SRC_1_Out"},
+
+ /* Hostless_SRC_bargein */
+ {"HW_SRC_1_IN_CH1", "I2S0_CH1 Switch", "Hostless_SRC_Bargein_DL"},
+ {"HW_SRC_1_IN_CH2", "I2S0_CH2 Switch", "Hostless_SRC_Bargein_DL"},
+ {"Hostless_SRC_Bargein_UL", NULL, "I2S0"},
+
+ /* Hostless AAudio */
+ {"Hostless HW Gain AAudio In", NULL, "HW Gain 2 In"},
+ {"Hostless SRC AAudio UL", NULL, "HW Gain 2 Out"},
+ {"HW_SRC_2_IN_CH1", "HW_GAIN2_OUT_CH1 Switch", "Hostless SRC AAudio DL"},
+ {"HW_SRC_2_IN_CH2", "HW_GAIN2_OUT_CH2 Switch", "Hostless SRC AAudio DL"},
+};
+
+/* dai ops */
+static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &mt8186_hostless_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_hostless_ops = {
+ .startup = mtk_dai_hostless_startup,
+};
+
+/* dai driver */
+#define MTK_HOSTLESS_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_HOSTLESS_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = {
+ {
+ .name = "Hostless LPBK DAI",
+ .id = MT8186_DAI_HOSTLESS_LPBK,
+ .playback = {
+ .stream_name = "Hostless LPBK DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless LPBK UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless FM DAI",
+ .id = MT8186_DAI_HOSTLESS_FM,
+ .playback = {
+ .stream_name = "Hostless FM DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless FM UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless_SRC_1_DAI",
+ .id = MT8186_DAI_HOSTLESS_SRC_1,
+ .playback = {
+ .stream_name = "Hostless_SRC_1_DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless_SRC_1_UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless_SRC_Bargein_DAI",
+ .id = MT8186_DAI_HOSTLESS_SRC_BARGEIN,
+ .playback = {
+ .stream_name = "Hostless_SRC_Bargein_DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless_SRC_Bargein_UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ /* BE dai */
+ {
+ .name = "Hostless_UL1 DAI",
+ .id = MT8186_DAI_HOSTLESS_UL1,
+ .capture = {
+ .stream_name = "Hostless_UL1 UL",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless_UL2 DAI",
+ .id = MT8186_DAI_HOSTLESS_UL2,
+ .capture = {
+ .stream_name = "Hostless_UL2 UL",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless_UL3 DAI",
+ .id = MT8186_DAI_HOSTLESS_UL3,
+ .capture = {
+ .stream_name = "Hostless_UL3 UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless_UL5 DAI",
+ .id = MT8186_DAI_HOSTLESS_UL5,
+ .capture = {
+ .stream_name = "Hostless_UL5 UL",
+ .channels_min = 1,
+ .channels_max = 12,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless_UL6 DAI",
+ .id = MT8186_DAI_HOSTLESS_UL6,
+ .capture = {
+ .stream_name = "Hostless_UL6 UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless HW Gain AAudio DAI",
+ .id = MT8186_DAI_HOSTLESS_HW_GAIN_AAUDIO,
+ .capture = {
+ .stream_name = "Hostless HW Gain AAudio In",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+ {
+ .name = "Hostless SRC AAudio DAI",
+ .id = MT8186_DAI_HOSTLESS_SRC_AAUDIO,
+ .playback = {
+ .stream_name = "Hostless SRC AAudio DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Hostless SRC AAudio UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HOSTLESS_RATES,
+ .formats = MTK_HOSTLESS_FORMATS,
+ },
+ .ops = &mtk_dai_hostless_ops,
+ },
+};
+
+int mt8186_dai_hostless_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_hostless_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver);
+
+ dai->dapm_routes = mtk_dai_hostless_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-hw-gain.c b/sound/soc/mediatek/mt8186/mt8186-dai-hw-gain.c
new file mode 100644
index 000000000000..75cb30790b1b
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-hw-gain.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI HW Gain Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/regmap.h>
+#include "mt8186-afe-common.h"
+#include "mt8186-interconnection.h"
+
+#define HW_GAIN_1_EN_W_NAME "HW GAIN 1 Enable"
+#define HW_GAIN_2_EN_W_NAME "HW GAIN 2 Enable"
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_hw_gain1_in_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN13_1,
+ I_CONNSYS_I2S_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain1_in_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN14_1,
+ I_CONNSYS_I2S_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain2_in_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN15,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain2_in_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN16,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static int mtk_hw_gain_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int gain_cur;
+ unsigned int gain_con1;
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (snd_soc_dapm_widget_name_cmp(w, HW_GAIN_1_EN_W_NAME) == 0) {
+ gain_cur = AFE_GAIN1_CUR;
+ gain_con1 = AFE_GAIN1_CON1;
+ } else {
+ gain_cur = AFE_GAIN2_CUR;
+ gain_con1 = AFE_GAIN2_CON1;
+ }
+
+ /* let hw gain ramp up, set cur gain to 0 */
+ regmap_update_bits(afe->regmap, gain_cur, AFE_GAIN1_CUR_MASK_SFT, 0);
+
+ /* set target gain to 0 */
+ regmap_update_bits(afe->regmap, gain_con1, GAIN1_TARGET_MASK_SFT, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_hw_gain_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_hw_gain1_in_ch1_mix,
+ ARRAY_SIZE(mtk_hw_gain1_in_ch1_mix)),
+ SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_hw_gain1_in_ch2_mix,
+ ARRAY_SIZE(mtk_hw_gain1_in_ch2_mix)),
+ SND_SOC_DAPM_MIXER("HW_GAIN2_IN_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_hw_gain2_in_ch1_mix,
+ ARRAY_SIZE(mtk_hw_gain2_in_ch1_mix)),
+ SND_SOC_DAPM_MIXER("HW_GAIN2_IN_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_hw_gain2_in_ch2_mix,
+ ARRAY_SIZE(mtk_hw_gain2_in_ch2_mix)),
+
+ SND_SOC_DAPM_SUPPLY(HW_GAIN_1_EN_W_NAME,
+ AFE_GAIN1_CON0, GAIN1_ON_SFT, 0,
+ mtk_hw_gain_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY(HW_GAIN_2_EN_W_NAME,
+ AFE_GAIN2_CON0, GAIN2_ON_SFT, 0,
+ mtk_hw_gain_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_INPUT("HW Gain 1 Out Endpoint"),
+ SND_SOC_DAPM_INPUT("HW Gain 2 Out Endpoint"),
+ SND_SOC_DAPM_OUTPUT("HW Gain 1 In Endpoint"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_hw_gain_routes[] = {
+ {"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH1"},
+ {"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH2"},
+ {"HW Gain 2 In", NULL, "HW_GAIN2_IN_CH1"},
+ {"HW Gain 2 In", NULL, "HW_GAIN2_IN_CH2"},
+
+ {"HW Gain 1 In", NULL, HW_GAIN_1_EN_W_NAME},
+ {"HW Gain 1 Out", NULL, HW_GAIN_1_EN_W_NAME},
+ {"HW Gain 2 In", NULL, HW_GAIN_2_EN_W_NAME},
+ {"HW Gain 2 Out", NULL, HW_GAIN_2_EN_W_NAME},
+
+ {"HW Gain 1 In Endpoint", NULL, "HW Gain 1 In"},
+ {"HW Gain 1 Out", NULL, "HW Gain 1 Out Endpoint"},
+ {"HW Gain 2 Out", NULL, "HW Gain 2 Out Endpoint"},
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain_controls[] = {
+ SOC_SINGLE("HW Gain 1 Volume", AFE_GAIN1_CON1,
+ GAIN1_TARGET_SFT, GAIN1_TARGET_MASK, 0),
+ SOC_SINGLE("HW Gain 2 Volume", AFE_GAIN2_CON1,
+ GAIN2_TARGET_SFT, GAIN2_TARGET_MASK, 0),
+};
+
+/* dai ops */
+static int mtk_dai_gain_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, dai->id);
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, dai->id, substream->stream, rate);
+
+ /* rate */
+ regmap_update_bits(afe->regmap,
+ dai->id == MT8186_DAI_HW_GAIN_1 ?
+ AFE_GAIN1_CON0 : AFE_GAIN2_CON0,
+ GAIN1_MODE_MASK_SFT,
+ rate_reg << GAIN1_MODE_SFT);
+
+ /* sample per step */
+ regmap_update_bits(afe->regmap,
+ dai->id == MT8186_DAI_HW_GAIN_1 ?
+ AFE_GAIN1_CON0 : AFE_GAIN2_CON0,
+ GAIN1_SAMPLE_PER_STEP_MASK_SFT,
+ (dai->id == MT8186_DAI_HW_GAIN_1 ? 0x40 : 0x0) <<
+ GAIN1_SAMPLE_PER_STEP_SFT);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_gain_ops = {
+ .hw_params = mtk_dai_gain_hw_params,
+};
+
+/* dai driver */
+#define MTK_HW_GAIN_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_HW_GAIN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_gain_driver[] = {
+ {
+ .name = "HW Gain 1",
+ .id = MT8186_DAI_HW_GAIN_1,
+ .playback = {
+ .stream_name = "HW Gain 1 In",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HW_GAIN_RATES,
+ .formats = MTK_HW_GAIN_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HW Gain 1 Out",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HW_GAIN_RATES,
+ .formats = MTK_HW_GAIN_FORMATS,
+ },
+ .ops = &mtk_dai_gain_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "HW Gain 2",
+ .id = MT8186_DAI_HW_GAIN_2,
+ .playback = {
+ .stream_name = "HW Gain 2 In",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HW_GAIN_RATES,
+ .formats = MTK_HW_GAIN_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HW Gain 2 Out",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_HW_GAIN_RATES,
+ .formats = MTK_HW_GAIN_FORMATS,
+ },
+ .ops = &mtk_dai_gain_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int mt8186_dai_hw_gain_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_gain_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_gain_driver);
+
+ dai->controls = mtk_hw_gain_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_hw_gain_controls);
+ dai->dapm_widgets = mtk_dai_hw_gain_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_hw_gain_widgets);
+ dai->dapm_routes = mtk_dai_hw_gain_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hw_gain_routes);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c b/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c
new file mode 100644
index 000000000000..f890e9173a07
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c
@@ -0,0 +1,1231 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8186-afe-clk.h"
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-gpio.h"
+#include "mt8186-interconnection.h"
+
+enum {
+ I2S_FMT_EIAJ = 0,
+ I2S_FMT_I2S = 1,
+};
+
+enum {
+ I2S_WLEN_16_BIT = 0,
+ I2S_WLEN_32_BIT = 1,
+};
+
+enum {
+ I2S_HD_NORMAL = 0,
+ I2S_HD_LOW_JITTER = 1,
+};
+
+enum {
+ I2S1_SEL_O28_O29 = 0,
+ I2S1_SEL_O03_O04 = 1,
+};
+
+enum {
+ I2S_IN_PAD_CONNSYS = 0,
+ I2S_IN_PAD_IO_MUX = 1,
+};
+
+struct mtk_afe_i2s_priv {
+ int id;
+ int rate; /* for determine which apll to use */
+ int low_jitter_en;
+ int master; /* only i2s0 has slave mode*/
+
+ int share_i2s_id;
+
+ int mclk_id;
+ int mclk_rate;
+ int mclk_apll;
+};
+
+static unsigned int get_i2s_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ I2S_WLEN_16_BIT : I2S_WLEN_32_BIT;
+}
+
+#define MTK_AFE_I2S0_KCONTROL_NAME "I2S0_HD_Mux"
+#define MTK_AFE_I2S1_KCONTROL_NAME "I2S1_HD_Mux"
+#define MTK_AFE_I2S2_KCONTROL_NAME "I2S2_HD_Mux"
+#define MTK_AFE_I2S3_KCONTROL_NAME "I2S3_HD_Mux"
+#define MTK_AFE_I2S0_SRC_KCONTROL_NAME "I2S0_SRC_Mux"
+
+#define I2S0_HD_EN_W_NAME "I2S0_HD_EN"
+#define I2S1_HD_EN_W_NAME "I2S1_HD_EN"
+#define I2S2_HD_EN_W_NAME "I2S2_HD_EN"
+#define I2S3_HD_EN_W_NAME "I2S3_HD_EN"
+
+#define I2S0_MCLK_EN_W_NAME "I2S0_MCLK_EN"
+#define I2S1_MCLK_EN_W_NAME "I2S1_MCLK_EN"
+#define I2S2_MCLK_EN_W_NAME "I2S2_MCLK_EN"
+#define I2S3_MCLK_EN_W_NAME "I2S3_MCLK_EN"
+
+static int get_i2s_id_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ if (strncmp(name, "I2S0", 4) == 0)
+ return MT8186_DAI_I2S_0;
+ else if (strncmp(name, "I2S1", 4) == 0)
+ return MT8186_DAI_I2S_1;
+ else if (strncmp(name, "I2S2", 4) == 0)
+ return MT8186_DAI_I2S_2;
+ else if (strncmp(name, "I2S3", 4) == 0)
+ return MT8186_DAI_I2S_3;
+
+ return -EINVAL;
+}
+
+static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_i2s_id_by_name(afe, name);
+
+ if (dai_id < 0)
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+/* low jitter control */
+static const char * const mt8186_i2s_hd_str[] = {
+ "Normal", "Low_Jitter"
+};
+
+static const struct soc_enum mt8186_i2s_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8186_i2s_hd_str),
+ mt8186_i2s_hd_str),
+};
+
+static int mt8186_i2s_hd_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
+ ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mt8186_i2s_hd_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int hd_en;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ hd_en = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, hd_en %d\n",
+ __func__, kcontrol->id.name, hd_en);
+
+ i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
+ if (i2s_priv->low_jitter_en == hd_en)
+ return 0;
+
+ i2s_priv->low_jitter_en = hd_en;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
+ SOC_ENUM_EXT(MTK_AFE_I2S0_KCONTROL_NAME, mt8186_i2s_enum[0],
+ mt8186_i2s_hd_get, mt8186_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S1_KCONTROL_NAME, mt8186_i2s_enum[0],
+ mt8186_i2s_hd_get, mt8186_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S2_KCONTROL_NAME, mt8186_i2s_enum[0],
+ mt8186_i2s_hd_get, mt8186_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S3_KCONTROL_NAME, mt8186_i2s_enum[0],
+ mt8186_i2s_hd_get, mt8186_i2s_hd_set),
+};
+
+/* dai component */
+/* i2s virtual mux to output widget */
+static const char * const i2s_mux_map[] = {
+ "Normal", "Dummy_Widget",
+};
+
+static int i2s_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ i2s_mux_map,
+ i2s_mux_map_value);
+
+static const struct snd_kcontrol_new i2s0_in_mux_control =
+ SOC_DAPM_ENUM("I2S0 In Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s1_out_mux_control =
+ SOC_DAPM_ENUM("I2S1 Out Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s2_in_mux_control =
+ SOC_DAPM_ENUM("I2S2 In Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s3_out_mux_control =
+ SOC_DAPM_ENUM("I2S3 Out Select", i2s_mux_map_enum);
+
+/* i2s in lpbk */
+static const char * const i2s_lpbk_mux_map[] = {
+ "Normal", "Lpbk",
+};
+
+static int i2s_lpbk_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s0_lpbk_mux_map_enum,
+ AFE_I2S_CON,
+ I2S_LOOPBACK_SFT,
+ 1,
+ i2s_lpbk_mux_map,
+ i2s_lpbk_mux_map_value);
+
+static const struct snd_kcontrol_new i2s0_lpbk_mux_control =
+ SOC_DAPM_ENUM("I2S Lpbk Select", i2s0_lpbk_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s2_lpbk_mux_map_enum,
+ AFE_I2S_CON2,
+ I2S3_LOOPBACK_SFT,
+ 1,
+ i2s_lpbk_mux_map,
+ i2s_lpbk_mux_map_value);
+
+static const struct snd_kcontrol_new i2s2_lpbk_mux_control =
+ SOC_DAPM_ENUM("I2S Lpbk Select", i2s2_lpbk_mux_map_enum);
+
+/* interconnection */
+static const struct snd_kcontrol_new mtk_i2s3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN0,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN0,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN0,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN0,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN0,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN0_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN0_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN0_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1 Switch", AFE_CONN0_1,
+ I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN0,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3 Switch", AFE_CONN0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1 Switch", AFE_CONN0,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH1 Switch", AFE_CONN0_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN1,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN1,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN1,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN1_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN1_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN1_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2 Switch", AFE_CONN1_1,
+ I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN1,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN1,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN1,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3 Switch", AFE_CONN1,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2 Switch", AFE_CONN1,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2 Switch", AFE_CONN1,
+ I_PCM_2_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH2 Switch", AFE_CONN1_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN28,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN28,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN28,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1 Switch", AFE_CONN28,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH3 Switch", AFE_CONN28,
+ I_DL12_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN28_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN28_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN28_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1 Switch", AFE_CONN28_1,
+ I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN28,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN28,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1 Switch", AFE_CONN28,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH1 Switch", AFE_CONN28_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN29,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN29,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN29,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2 Switch", AFE_CONN29,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH4 Switch", AFE_CONN29,
+ I_DL12_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN29_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN29_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN29_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2 Switch", AFE_CONN29_1,
+ I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN29,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN29,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2 Switch", AFE_CONN29,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2 Switch", AFE_CONN29,
+ I_PCM_2_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH2 Switch", AFE_CONN29_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+};
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_I2S_MCLK_EN,
+ SUPPLY_SEQ_I2S_HD_EN,
+ SUPPLY_SEQ_I2S_EN,
+};
+
+static int mtk_i2s_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_afe_gpio_request(afe->dev, true, i2s_priv->id, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8186_afe_gpio_request(afe->dev, false, i2s_priv->id, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8186_apll1_enable(afe);
+ else
+ mt8186_apll2_enable(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8186_apll1_disable(afe);
+ else
+ mt8186_apll2_disable(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ i2s_priv->mclk_rate = 0;
+ mt8186_mck_disable(afe, i2s_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
+ SND_SOC_DAPM_INPUT("CONNSYS"),
+
+ SND_SOC_DAPM_MIXER("I2S1_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s1_ch1_mix,
+ ARRAY_SIZE(mtk_i2s1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S1_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s1_ch2_mix,
+ ARRAY_SIZE(mtk_i2s1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S3_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s3_ch1_mix,
+ ARRAY_SIZE(mtk_i2s3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S3_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s3_ch2_mix,
+ ARRAY_SIZE(mtk_i2s3_ch2_mix)),
+
+ /* i2s en*/
+ SND_SOC_DAPM_SUPPLY_S("I2S0_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S1_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON1, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S2_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON2, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S3_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON3, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* i2s hd en */
+ SND_SOC_DAPM_SUPPLY_S(I2S0_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON, I2S1_HD_EN_SFT, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S1_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON1, I2S2_HD_EN_SFT, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S2_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON2, I2S3_HD_EN_SFT, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S3_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON3, I2S4_HD_EN_SFT, 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* i2s mclk en */
+ SND_SOC_DAPM_SUPPLY_S(I2S0_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S1_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S2_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S3_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* apll */
+ SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* allow i2s on without codec on */
+ SND_SOC_DAPM_OUTPUT("I2S_DUMMY_OUT"),
+ SND_SOC_DAPM_MUX("I2S1_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s1_out_mux_control),
+ SND_SOC_DAPM_MUX("I2S3_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s3_out_mux_control),
+ SND_SOC_DAPM_INPUT("I2S_DUMMY_IN"),
+ SND_SOC_DAPM_MUX("I2S0_In_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s0_in_mux_control),
+ SND_SOC_DAPM_MUX("I2S2_In_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s2_in_mux_control),
+
+ /* i2s in lpbk */
+ SND_SOC_DAPM_MUX("I2S0_Lpbk_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s0_lpbk_mux_control),
+ SND_SOC_DAPM_MUX("I2S2_Lpbk_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s2_lpbk_mux_control),
+};
+
+static int mtk_afe_i2s_share_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ return i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name);
+}
+
+static int mtk_afe_i2s_hd_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (get_i2s_id_by_name(afe, sink->name) ==
+ get_i2s_id_by_name(afe, source->name))
+ return i2s_priv->low_jitter_en;
+
+ /* check if share i2s need hd en */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
+ return i2s_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mtk_afe_i2s_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+ int i2s_need_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ /* which apll */
+ cur_apll = mt8186_get_apll_by_name(afe, source->name);
+ /* choose APLL from i2s rate */
+ i2s_need_apll = mt8186_get_apll_by_rate(afe, i2s_priv->rate);
+
+ return (i2s_need_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_afe_i2s_mclk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (get_i2s_id_by_name(afe, sink->name) ==
+ get_i2s_id_by_name(afe, source->name))
+ return (i2s_priv->mclk_rate > 0) ? 1 : 0;
+
+ /* check if share i2s need mclk */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
+ return (i2s_priv->mclk_rate > 0) ? 1 : 0;
+
+ return 0;
+}
+
+static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ /* which apll */
+ cur_apll = mt8186_get_apll_by_name(afe, source->name);
+
+ return (i2s_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
+ {"Connsys I2S", NULL, "CONNSYS"},
+
+ /* i2s0 */
+ {"I2S0", NULL, "I2S0_EN"},
+ {"I2S0", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S0", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S0_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S0_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S0", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S0_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S0_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s1 */
+ {"I2S1_CH1", "DL1_CH1 Switch", "DL1"},
+ {"I2S1_CH2", "DL1_CH2 Switch", "DL1"},
+
+ {"I2S1_CH1", "DL1_CH1 Switch", "DSP_DL1_VIRT"},
+ {"I2S1_CH2", "DL1_CH2 Switch", "DSP_DL1_VIRT"},
+
+ {"I2S1_CH1", "DL2_CH1 Switch", "DL2"},
+ {"I2S1_CH2", "DL2_CH2 Switch", "DL2"},
+
+ {"I2S1_CH1", "DL2_CH1 Switch", "DSP_DL2_VIRT"},
+ {"I2S1_CH2", "DL2_CH2 Switch", "DSP_DL2_VIRT"},
+
+ {"I2S1_CH1", "DL3_CH1 Switch", "DL3"},
+ {"I2S1_CH2", "DL3_CH2 Switch", "DL3"},
+
+ {"I2S1_CH1", "DL12_CH1 Switch", "DL12"},
+ {"I2S1_CH2", "DL12_CH2 Switch", "DL12"},
+
+ {"I2S1_CH1", "DL12_CH3 Switch", "DL12"},
+ {"I2S1_CH2", "DL12_CH4 Switch", "DL12"},
+
+ {"I2S1_CH1", "DL6_CH1 Switch", "DL6"},
+ {"I2S1_CH2", "DL6_CH2 Switch", "DL6"},
+
+ {"I2S1_CH1", "DL4_CH1 Switch", "DL4"},
+ {"I2S1_CH2", "DL4_CH2 Switch", "DL4"},
+
+ {"I2S1_CH1", "DL5_CH1 Switch", "DL5"},
+ {"I2S1_CH2", "DL5_CH2 Switch", "DL5"},
+
+ {"I2S1_CH1", "DL8_CH1 Switch", "DL8"},
+ {"I2S1_CH2", "DL8_CH2 Switch", "DL8"},
+
+ {"I2S1", NULL, "I2S1_CH1"},
+ {"I2S1", NULL, "I2S1_CH2"},
+
+ {"I2S1", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S1_EN"},
+ {"I2S1", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S1", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S1_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S1_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S1", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S1_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S1_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s2 */
+ {"I2S2", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S2_EN"},
+ {"I2S2", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S2", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S2_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S2_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S2", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S2_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S2_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s3 */
+ {"I2S3_CH1", "DL1_CH1 Switch", "DL1"},
+ {"I2S3_CH2", "DL1_CH2 Switch", "DL1"},
+
+ {"I2S3_CH1", "DL1_CH1 Switch", "DSP_DL1_VIRT"},
+ {"I2S3_CH2", "DL1_CH2 Switch", "DSP_DL1_VIRT"},
+
+ {"I2S3_CH1", "DL2_CH1 Switch", "DL2"},
+ {"I2S3_CH2", "DL2_CH2 Switch", "DL2"},
+
+ {"I2S3_CH1", "DL2_CH1 Switch", "DSP_DL2_VIRT"},
+ {"I2S3_CH2", "DL2_CH2 Switch", "DSP_DL2_VIRT"},
+
+ {"I2S3_CH1", "DL3_CH1 Switch", "DL3"},
+ {"I2S3_CH2", "DL3_CH2 Switch", "DL3"},
+
+ {"I2S3_CH1", "DL12_CH1 Switch", "DL12"},
+ {"I2S3_CH2", "DL12_CH2 Switch", "DL12"},
+
+ {"I2S3_CH1", "DL12_CH3 Switch", "DL12"},
+ {"I2S3_CH2", "DL12_CH4 Switch", "DL12"},
+
+ {"I2S3_CH1", "DL6_CH1 Switch", "DL6"},
+ {"I2S3_CH2", "DL6_CH2 Switch", "DL6"},
+
+ {"I2S3_CH1", "DL4_CH1 Switch", "DL4"},
+ {"I2S3_CH2", "DL4_CH2 Switch", "DL4"},
+
+ {"I2S3_CH1", "DL5_CH1 Switch", "DL5"},
+ {"I2S3_CH2", "DL5_CH2 Switch", "DL5"},
+
+ {"I2S3_CH1", "DL8_CH1 Switch", "DL8"},
+ {"I2S3_CH2", "DL8_CH2 Switch", "DL8"},
+
+ {"I2S3", NULL, "I2S3_CH1"},
+ {"I2S3", NULL, "I2S3_CH2"},
+
+ {"I2S3", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S3_EN"},
+
+ {"I2S3", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S3_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S3_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S3", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S3_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S3_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* allow i2s on without codec on */
+ {"I2S0", NULL, "I2S0_In_Mux"},
+ {"I2S0_In_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
+
+ {"I2S1_Out_Mux", "Dummy_Widget", "I2S1"},
+ {"I2S_DUMMY_OUT", NULL, "I2S1_Out_Mux"},
+
+ {"I2S2", NULL, "I2S2_In_Mux"},
+ {"I2S2_In_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
+
+ {"I2S3_Out_Mux", "Dummy_Widget", "I2S3"},
+ {"I2S_DUMMY_OUT", NULL, "I2S3_Out_Mux"},
+
+ /* i2s in lpbk */
+ {"I2S0_Lpbk_Mux", "Lpbk", "I2S3"},
+ {"I2S2_Lpbk_Mux", "Lpbk", "I2S1"},
+ {"I2S0", NULL, "I2S0_Lpbk_Mux"},
+ {"I2S2", NULL, "I2S2_Lpbk_Mux"},
+};
+
+/* dai ops */
+static int mtk_dai_connsys_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8186_rate_transform(afe->dev,
+ rate, dai->id);
+ unsigned int i2s_con = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, dai->id, substream->stream, rate);
+
+ /* non-inverse, i2s mode, slave, 16bits, from connsys */
+ i2s_con |= 0 << INV_PAD_CTRL_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT;
+ i2s_con |= 1 << I2S_SRC_SFT;
+ i2s_con |= get_i2s_wlen(SNDRV_PCM_FORMAT_S16_LE) << I2S_WLEN_SFT;
+ i2s_con |= 0 << I2SIN_PAD_SEL_SFT;
+ regmap_write(afe->regmap, AFE_CONNSYS_I2S_CON, i2s_con);
+
+ /* use asrc */
+ regmap_update_bits(afe->regmap, AFE_CONNSYS_I2S_CON,
+ I2S_BYPSRC_MASK_SFT, 0);
+
+ /* slave mode, set i2s for asrc */
+ regmap_update_bits(afe->regmap, AFE_CONNSYS_I2S_CON,
+ I2S_MODE_MASK_SFT, rate_reg << I2S_MODE_SFT);
+
+ if (rate == 44100)
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON3, 0x1b9000);
+ else if (rate == 32000)
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON3, 0x140000);
+ else
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON3, 0x1e0000);
+
+ /* Calibration setting */
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON4, 0x140000);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON9, 0x36000);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON10, 0x2fc00);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON6, 0x7ef4);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON5, 0xff5986);
+
+ /* 0:Stereo 1:Mono */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2,
+ CHSET_IS_MONO_MASK_SFT, 0);
+
+ return 0;
+}
+
+static int mtk_dai_connsys_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(afe->dev, "%s(), cmd %d, stream %d\n",
+ __func__, cmd, substream->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* i2s enable */
+ regmap_update_bits(afe->regmap,
+ AFE_CONNSYS_I2S_CON,
+ I2S_EN_MASK_SFT,
+ BIT(I2S_EN_SFT));
+
+ /* calibrator enable */
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON5,
+ CALI_EN_MASK_SFT,
+ BIT(CALI_EN_SFT));
+
+ /* asrc enable */
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON0,
+ CON0_CHSET_STR_CLR_MASK_SFT,
+ BIT(CON0_CHSET_STR_CLR_SFT));
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON0,
+ CON0_ASM_ON_MASK_SFT,
+ BIT(CON0_ASM_ON_SFT));
+
+ afe_priv->dai_on[dai->id] = true;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0,
+ CON0_ASM_ON_MASK_SFT, 0);
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5,
+ CALI_EN_MASK_SFT, 0);
+
+ /* i2s disable */
+ regmap_update_bits(afe->regmap, AFE_CONNSYS_I2S_CON,
+ I2S_EN_MASK_SFT, 0);
+
+ /* bypass asrc */
+ regmap_update_bits(afe->regmap, AFE_CONNSYS_I2S_CON,
+ I2S_BYPSRC_MASK_SFT, BIT(I2S_BYPSRC_SFT));
+
+ afe_priv->dai_on[dai->id] = false;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_connsys_i2s_ops = {
+ .hw_params = mtk_dai_connsys_i2s_hw_params,
+ .trigger = mtk_dai_connsys_i2s_trigger,
+};
+
+/* i2s */
+static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
+ struct snd_pcm_hw_params *params,
+ int i2s_id)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[i2s_id];
+
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8186_rate_transform(afe->dev,
+ rate, i2s_id);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int i2s_con = 0;
+ int ret;
+
+ dev_dbg(afe->dev, "%s(), id %d, rate %d, format %d\n",
+ __func__, i2s_id, rate, format);
+
+ i2s_priv->rate = rate;
+
+ switch (i2s_id) {
+ case MT8186_DAI_I2S_0:
+ i2s_con = I2S_IN_PAD_IO_MUX << I2SIN_PAD_SEL_SFT;
+ i2s_con |= rate_reg << I2S_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON,
+ 0xffffeffa, i2s_con);
+ break;
+ case MT8186_DAI_I2S_1:
+ i2s_con = I2S1_SEL_O28_O29 << I2S2_SEL_O03_O04_SFT;
+ i2s_con |= rate_reg << I2S2_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S2_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S2_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1,
+ 0xffffeffa, i2s_con);
+ break;
+ case MT8186_DAI_I2S_2:
+ i2s_con = 8 << I2S3_UPDATE_WORD_SFT;
+ i2s_con |= rate_reg << I2S3_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S3_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S3_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2,
+ 0xffffeffa, i2s_con);
+ break;
+ case MT8186_DAI_I2S_3:
+ i2s_con = rate_reg << I2S4_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S4_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S4_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON3,
+ 0xffffeffa, i2s_con);
+ break;
+ default:
+ dev_err(afe->dev, "%s(), id %d not support\n",
+ __func__, i2s_id);
+ return -EINVAL;
+ }
+
+ /* set share i2s */
+ if (i2s_priv->share_i2s_id >= 0) {
+ ret = mtk_dai_i2s_config(afe, params, i2s_priv->share_i2s_id);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ return mtk_dai_i2s_config(afe, params, dai->id);
+}
+
+static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[dai->id];
+ int apll;
+ int apll_rate;
+
+ if (dir != SND_SOC_CLOCK_OUT) {
+ dev_err(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ apll = mt8186_get_apll_by_rate(afe, freq);
+ apll_rate = mt8186_get_apll_rate(afe, apll);
+
+ if (freq > apll_rate) {
+ dev_err(afe->dev, "%s(), freq > apll rate", __func__);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_err(afe->dev, "%s(), APLL cannot generate freq Hz", __func__);
+ return -EINVAL;
+ }
+
+ i2s_priv->mclk_rate = freq;
+ i2s_priv->mclk_apll = apll;
+
+ if (i2s_priv->share_i2s_id > 0) {
+ struct mtk_afe_i2s_priv *share_i2s_priv;
+
+ share_i2s_priv = afe_priv->dai_priv[i2s_priv->share_i2s_id];
+ if (!share_i2s_priv) {
+ dev_err(afe->dev, "%s(), share_i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ share_i2s_priv->mclk_rate = i2s_priv->mclk_rate;
+ share_i2s_priv->mclk_apll = i2s_priv->mclk_apll;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_i2s_ops = {
+ .hw_params = mtk_dai_i2s_hw_params,
+ .set_sysclk = mtk_dai_i2s_set_sysclk,
+};
+
+/* dai driver */
+#define MTK_CONNSYS_I2S_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define MTK_I2S_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
+ {
+ .name = "CONNSYS_I2S",
+ .id = MT8186_DAI_CONNSYS_I2S,
+ .capture = {
+ .stream_name = "Connsys I2S",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_CONNSYS_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_connsys_i2s_ops,
+ },
+ {
+ .name = "I2S0",
+ .id = MT8186_DAI_I2S_0,
+ .capture = {
+ .stream_name = "I2S0",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S1",
+ .id = MT8186_DAI_I2S_1,
+ .playback = {
+ .stream_name = "I2S1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S2",
+ .id = MT8186_DAI_I2S_2,
+ .capture = {
+ .stream_name = "I2S2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S3",
+ .id = MT8186_DAI_I2S_3,
+ .playback = {
+ .stream_name = "I2S3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ }
+};
+
+/* this enum is merely for mtk_afe_i2s_priv declare */
+enum {
+ DAI_I2S0 = 0,
+ DAI_I2S1,
+ DAI_I2S2,
+ DAI_I2S3,
+ DAI_I2S_NUM,
+};
+
+static const struct mtk_afe_i2s_priv mt8186_i2s_priv[DAI_I2S_NUM] = {
+ [DAI_I2S0] = {
+ .id = MT8186_DAI_I2S_0,
+ .mclk_id = MT8186_I2S0_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S1] = {
+ .id = MT8186_DAI_I2S_1,
+ .mclk_id = MT8186_I2S1_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S2] = {
+ .id = MT8186_DAI_I2S_2,
+ .mclk_id = MT8186_I2S2_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S3] = {
+ .id = MT8186_DAI_I2S_3,
+ /* clock gate naming is hf_faud_i2s4_m_ck*/
+ .mclk_id = MT8186_I2S4_MCK,
+ .share_i2s_id = -1,
+ }
+};
+
+/**
+ * mt8186_dai_i2s_set_share() - Set up I2S ports to share a single clock.
+ * @afe: Pointer to &struct mtk_base_afe
+ * @main_i2s_name: The name of the I2S port that will provide the clock
+ * @secondary_i2s_name: The name of the I2S port that will use this clock
+ */
+int mt8186_dai_i2s_set_share(struct mtk_base_afe *afe, const char *main_i2s_name,
+ const char *secondary_i2s_name)
+{
+ struct mtk_afe_i2s_priv *secondary_i2s_priv;
+ int main_i2s_id;
+
+ secondary_i2s_priv = get_i2s_priv_by_name(afe, secondary_i2s_name);
+ if (!secondary_i2s_priv)
+ return -EINVAL;
+
+ main_i2s_id = get_i2s_id_by_name(afe, main_i2s_name);
+ if (main_i2s_id < 0)
+ return main_i2s_id;
+
+ secondary_i2s_priv->share_i2s_id = main_i2s_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8186_dai_i2s_set_share);
+
+static int mt8186_dai_i2s_set_priv(struct mtk_base_afe *afe)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < DAI_I2S_NUM; i++) {
+ ret = mt8186_dai_set_priv(afe, mt8186_i2s_priv[i].id,
+ sizeof(struct mtk_afe_i2s_priv),
+ &mt8186_i2s_priv[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int mt8186_dai_i2s_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ int ret;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_i2s_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
+
+ dai->controls = mtk_dai_i2s_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls);
+ dai->dapm_widgets = mtk_dai_i2s_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
+ dai->dapm_routes = mtk_dai_i2s_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
+
+ /* set all dai i2s private data */
+ ret = mt8186_dai_i2s_set_priv(afe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c
new file mode 100644
index 000000000000..a50aa294960b
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-gpio.h"
+#include "mt8186-interconnection.h"
+
+struct mtk_afe_pcm_priv {
+ unsigned int id;
+ unsigned int fmt;
+ unsigned int bck_invert;
+ unsigned int lck_invert;
+};
+
+enum aud_tx_lch_rpt {
+ AUD_TX_LCH_RPT_NO_REPEAT = 0,
+ AUD_TX_LCH_RPT_REPEAT = 1
+};
+
+enum aud_vbt_16k_mode {
+ AUD_VBT_16K_MODE_DISABLE = 0,
+ AUD_VBT_16K_MODE_ENABLE = 1
+};
+
+enum aud_ext_modem {
+ AUD_EXT_MODEM_SELECT_INTERNAL = 0,
+ AUD_EXT_MODEM_SELECT_EXTERNAL = 1
+};
+
+enum aud_pcm_sync_type {
+ /* bck sync length = 1 */
+ AUD_PCM_ONE_BCK_CYCLE_SYNC = 0,
+ /* bck sync length = PCM_INTF_CON1[9:13] */
+ AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1
+};
+
+enum aud_bt_mode {
+ AUD_BT_MODE_DUAL_MIC_ON_TX = 0,
+ AUD_BT_MODE_SINGLE_MIC_ON_TX = 1
+};
+
+enum aud_pcm_afifo_src {
+ /* slave mode & external modem uses different crystal */
+ AUD_PCM_AFIFO_ASRC = 0,
+ /* slave mode & external modem uses the same crystal */
+ AUD_PCM_AFIFO_AFIFO = 1
+};
+
+enum aud_pcm_clock_source {
+ AUD_PCM_CLOCK_MASTER_MODE = 0,
+ AUD_PCM_CLOCK_SLAVE_MODE = 1
+};
+
+enum aud_pcm_wlen {
+ AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0,
+ AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1
+};
+
+enum aud_pcm_24bit {
+ AUD_PCM_24BIT_PCM_16_BITS = 0,
+ AUD_PCM_24BIT_PCM_24_BITS = 1
+};
+
+enum aud_pcm_mode {
+ AUD_PCM_MODE_PCM_MODE_8K = 0,
+ AUD_PCM_MODE_PCM_MODE_16K = 1,
+ AUD_PCM_MODE_PCM_MODE_32K = 2,
+ AUD_PCM_MODE_PCM_MODE_48K = 3,
+};
+
+enum aud_pcm_fmt {
+ AUD_PCM_FMT_I2S = 0,
+ AUD_PCM_FMT_EIAJ = 1,
+ AUD_PCM_FMT_PCM_MODE_A = 2,
+ AUD_PCM_FMT_PCM_MODE_B = 3
+};
+
+enum aud_bclk_out_inv {
+ AUD_BCLK_OUT_INV_NO_INVERSE = 0,
+ AUD_BCLK_OUT_INV_INVERSE = 1
+};
+
+enum aud_lrclk_out_inv {
+ AUD_LRCLK_OUT_INV_NO_INVERSE = 0,
+ AUD_LRCLK_OUT_INV_INVERSE = 1
+};
+
+enum aud_pcm_en {
+ AUD_PCM_EN_DISABLE = 0,
+ AUD_PCM_EN_ENABLE = 1
+};
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN7,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN7,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN7_1,
+ I_DL4_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN8,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN8,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN8_1,
+ I_DL4_CH2, 1, 0),
+};
+
+static int mtk_pcm_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_afe_gpio_request(afe->dev, true, MT8186_DAI_PCM, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8186_afe_gpio_request(afe->dev, false, MT8186_DAI_PCM, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* pcm in/out lpbk */
+static const char * const pcm_lpbk_mux_map[] = {
+ "Normal", "Lpbk",
+};
+
+static int pcm_lpbk_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(pcm_in_lpbk_mux_map_enum,
+ PCM_INTF_CON1,
+ PCM_I2S_PCM_LOOPBACK_SFT,
+ 1,
+ pcm_lpbk_mux_map,
+ pcm_lpbk_mux_map_value);
+
+static const struct snd_kcontrol_new pcm_in_lpbk_mux_control =
+ SOC_DAPM_ENUM("PCM In Lpbk Select", pcm_in_lpbk_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(pcm_out_lpbk_mux_map_enum,
+ PCM_INTF_CON1,
+ PCM_I2S_PCM_LOOPBACK_SFT,
+ 1,
+ pcm_lpbk_mux_map,
+ pcm_lpbk_mux_map_value);
+
+static const struct snd_kcontrol_new pcm_out_lpbk_mux_control =
+ SOC_DAPM_ENUM("PCM Out Lpbk Select", pcm_out_lpbk_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_1_EN",
+ PCM_INTF_CON1, PCM_EN_SFT, 0,
+ mtk_pcm_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* pcm in lpbk */
+ SND_SOC_DAPM_MUX("PCM_In_Lpbk_Mux",
+ SND_SOC_NOPM, 0, 0, &pcm_in_lpbk_mux_control),
+
+ /* pcm out lpbk */
+ SND_SOC_DAPM_MUX("PCM_Out_Lpbk_Mux",
+ SND_SOC_NOPM, 0, 0, &pcm_out_lpbk_mux_control),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"},
+
+ {"PCM 1 Playback", NULL, "PCM_1_EN"},
+ {"PCM 1 Capture", NULL, "PCM_1_EN"},
+
+ {"PCM_1_PB_CH1", "DL2_CH1 Switch", "DL2"},
+ {"PCM_1_PB_CH2", "DL2_CH2 Switch", "DL2"},
+
+ {"PCM_1_PB_CH1", "DL4_CH1 Switch", "DL4"},
+ {"PCM_1_PB_CH2", "DL4_CH2 Switch", "DL4"},
+
+ /* pcm out lpbk */
+ {"PCM_Out_Lpbk_Mux", "Lpbk", "PCM 1 Playback"},
+ {"I2S0", NULL, "PCM_Out_Lpbk_Mux"},
+
+ /* pcm in lpbk */
+ {"PCM_In_Lpbk_Mux", "Lpbk", "PCM 1 Capture"},
+ {"I2S3", NULL, "PCM_In_Lpbk_Mux"},
+};
+
+/* dai ops */
+static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
+ int pcm_id = dai->id;
+ struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[pcm_id];
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, dai->id);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int data_width =
+ snd_pcm_format_width(format);
+ unsigned int wlen_width =
+ snd_pcm_format_physical_width(format);
+ unsigned int pcm_con = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
+ __func__, dai->id, substream->stream, p->active, c->active);
+ dev_dbg(afe->dev, "%s(), rate %d, rate_reg %d, data_width %d, wlen_width %d\n",
+ __func__, rate, rate_reg, data_width, wlen_width);
+
+ if (p->active || c->active)
+ return 0;
+
+ switch (dai->id) {
+ case MT8186_DAI_PCM:
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_EXT_MODEM_SELECT_EXTERNAL << PCM_EXT_MODEM_SFT;
+ pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT;
+ pcm_con |= AUD_PCM_CLOCK_MASTER_MODE << PCM_SLAVE_SFT;
+ pcm_con |= 0 << PCM_SYNC_LENGTH_SFT;
+
+ /* sampling rate */
+ pcm_con |= rate_reg << PCM_MODE_SFT;
+
+ /* format */
+ pcm_con |= pcm_priv->fmt << PCM_FMT_SFT;
+
+ /* 24bit data width */
+ if (data_width > 16)
+ pcm_con |= AUD_PCM_24BIT_PCM_24_BITS << PCM_24BIT_SFT;
+ else
+ pcm_con |= AUD_PCM_24BIT_PCM_16_BITS << PCM_24BIT_SFT;
+
+ /* wlen width*/
+ if (wlen_width > 16)
+ pcm_con |= AUD_PCM_WLEN_PCM_64_BCK_CYCLES << PCM_WLEN_SFT;
+ else
+ pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM_WLEN_SFT;
+
+ /* clock invert */
+ pcm_con |= pcm_priv->lck_invert << PCM_SYNC_OUT_INV_SFT;
+ pcm_con |= pcm_priv->bck_invert << PCM_BCLK_OUT_INV_SFT;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1, 0xfffffffe, pcm_con);
+ break;
+ default:
+ dev_err(afe->dev, "%s(), id %d not support\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[dai->id];
+
+ /* DAI mode*/
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcm_priv->fmt = AUD_PCM_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ pcm_priv->fmt = AUD_PCM_FMT_EIAJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ pcm_priv->fmt = AUD_PCM_FMT_PCM_MODE_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ pcm_priv->fmt = AUD_PCM_FMT_PCM_MODE_B;
+ break;
+ default:
+ pcm_priv->fmt = AUD_PCM_FMT_I2S;
+ }
+
+ /* DAI clock inversion*/
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
+ pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
+ pcm_priv->lck_invert = AUD_BCLK_OUT_INV_INVERSE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ pcm_priv->bck_invert = AUD_BCLK_OUT_INV_INVERSE;
+ pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ pcm_priv->bck_invert = AUD_BCLK_OUT_INV_INVERSE;
+ pcm_priv->lck_invert = AUD_BCLK_OUT_INV_INVERSE;
+ break;
+ default:
+ pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
+ pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .hw_params = mtk_dai_pcm_hw_params,
+ .set_fmt = mtk_dai_pcm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM 1",
+ .id = MT8186_DAI_PCM,
+ .playback = {
+ .stream_name = "PCM 1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+static struct mtk_afe_pcm_priv *init_pcm_priv_data(struct mtk_base_afe *afe)
+{
+ struct mtk_afe_pcm_priv *pcm_priv;
+
+ pcm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_pcm_priv),
+ GFP_KERNEL);
+ if (!pcm_priv)
+ return NULL;
+
+ pcm_priv->id = MT8186_DAI_PCM;
+ pcm_priv->fmt = AUD_PCM_FMT_I2S;
+ pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
+ pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
+
+ return pcm_priv;
+}
+
+int mt8186_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_pcm_priv *pcm_priv;
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+
+ pcm_priv = init_pcm_priv_data(afe);
+ if (!pcm_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8186_DAI_PCM] = pcm_priv;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-src.c b/sound/soc/mediatek/mt8186/mt8186-dai-src.c
new file mode 100644
index 000000000000..e475f4591aef
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-src.c
@@ -0,0 +1,695 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI SRC Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/regmap.h>
+#include "mt8186-afe-common.h"
+#include "mt8186-interconnection.h"
+
+struct mtk_afe_src_priv {
+ int dl_rate;
+ int ul_rate;
+};
+
+static const unsigned int src_iir_coeff_32_to_16[] = {
+ 0x0dbae6, 0xff9b0a, 0x0dbae6, 0x05e488, 0xe072b9, 0x000002,
+ 0x0dbae6, 0x000f3b, 0x0dbae6, 0x06a537, 0xe17d79, 0x000002,
+ 0x0dbae6, 0x01246a, 0x0dbae6, 0x087261, 0xe306be, 0x000002,
+ 0x0dbae6, 0x03437d, 0x0dbae6, 0x0bc16f, 0xe57c87, 0x000002,
+ 0x0dbae6, 0x072981, 0x0dbae6, 0x111dd3, 0xe94f2a, 0x000002,
+ 0x0dbae6, 0x0dc4a6, 0x0dbae6, 0x188611, 0xee85a0, 0x000002,
+ 0x0dbae6, 0x168b9a, 0x0dbae6, 0x200e8f, 0xf3ccf1, 0x000002,
+ 0x000000, 0x1b75cb, 0x1b75cb, 0x2374a2, 0x000000, 0x000001
+};
+
+static const unsigned int src_iir_coeff_44_to_16[] = {
+ 0x09ae28, 0xf7d97d, 0x09ae28, 0x212a3d, 0xe0ac3a, 0x000002,
+ 0x09ae28, 0xf8525a, 0x09ae28, 0x216d72, 0xe234be, 0x000002,
+ 0x09ae28, 0xf980f5, 0x09ae28, 0x22a057, 0xe45a81, 0x000002,
+ 0x09ae28, 0xfc0a08, 0x09ae28, 0x24d3bd, 0xe7752d, 0x000002,
+ 0x09ae28, 0x016162, 0x09ae28, 0x27da01, 0xeb6ea8, 0x000002,
+ 0x09ae28, 0x0b67df, 0x09ae28, 0x2aca4a, 0xef34c4, 0x000002,
+ 0x000000, 0x135c50, 0x135c50, 0x2c1079, 0x000000, 0x000001
+};
+
+static const unsigned int src_iir_coeff_44_to_32[] = {
+ 0x096966, 0x0c4d35, 0x096966, 0xedee81, 0xf05070, 0x000003,
+ 0x12d2cc, 0x193910, 0x12d2cc, 0xddbf4f, 0xe21e1d, 0x000002,
+ 0x12d2cc, 0x1a9e60, 0x12d2cc, 0xe18916, 0xe470fd, 0x000002,
+ 0x12d2cc, 0x1d06e0, 0x12d2cc, 0xe8a4a6, 0xe87b24, 0x000002,
+ 0x12d2cc, 0x207578, 0x12d2cc, 0xf4fe62, 0xef5917, 0x000002,
+ 0x12d2cc, 0x24055f, 0x12d2cc, 0x05ee2b, 0xf8b502, 0x000002,
+ 0x000000, 0x25a599, 0x25a599, 0x0fabe2, 0x000000, 0x000001
+};
+
+static const unsigned int src_iir_coeff_48_to_16[] = {
+ 0x0296a4, 0xfd69dd, 0x0296a4, 0x209439, 0xe01ff9, 0x000002,
+ 0x0f4ff3, 0xf0d6d4, 0x0f4ff3, 0x209bc9, 0xe076c3, 0x000002,
+ 0x0e8490, 0xf1fe63, 0x0e8490, 0x20cfd6, 0xe12124, 0x000002,
+ 0x14852f, 0xed794a, 0x14852f, 0x21503d, 0xe28b32, 0x000002,
+ 0x136222, 0xf17677, 0x136222, 0x225be1, 0xe56964, 0x000002,
+ 0x0a8d85, 0xfc4a97, 0x0a8d85, 0x24310c, 0xea6952, 0x000002,
+ 0x05eff5, 0x043455, 0x05eff5, 0x4ced8f, 0xe134d6, 0x000001,
+ 0x000000, 0x3aebe6, 0x3aebe6, 0x04f3b0, 0x000000, 0x000004
+};
+
+static const unsigned int src_iir_coeff_48_to_32[] = {
+ 0x10c1b8, 0x10a7df, 0x10c1b8, 0xe7514e, 0xe0b41f, 0x000002,
+ 0x10c1b8, 0x116257, 0x10c1b8, 0xe9402f, 0xe25aaa, 0x000002,
+ 0x10c1b8, 0x130c89, 0x10c1b8, 0xed3cc3, 0xe4dddb, 0x000002,
+ 0x10c1b8, 0x1600dd, 0x10c1b8, 0xf48000, 0xe90c55, 0x000002,
+ 0x10c1b8, 0x1a672e, 0x10c1b8, 0x00494c, 0xefa807, 0x000002,
+ 0x10c1b8, 0x1f38e6, 0x10c1b8, 0x0ee076, 0xf7c5f3, 0x000002,
+ 0x000000, 0x218370, 0x218370, 0x168b40, 0x000000, 0x000001
+};
+
+static const unsigned int src_iir_coeff_48_to_44[] = {
+ 0x0bf71c, 0x170f3f, 0x0bf71c, 0xe3a4c8, 0xf096cb, 0x000003,
+ 0x0bf71c, 0x17395e, 0x0bf71c, 0xe58085, 0xf210c8, 0x000003,
+ 0x0bf71c, 0x1782bd, 0x0bf71c, 0xe95ef6, 0xf4c899, 0x000003,
+ 0x0bf71c, 0x17cd97, 0x0bf71c, 0xf1608a, 0xfa3b18, 0x000003,
+ 0x000000, 0x2fdc6f, 0x2fdc6f, 0xf15663, 0x000000, 0x000001
+};
+
+static const unsigned int src_iir_coeff_96_to_16[] = {
+ 0x0805a1, 0xf21ae3, 0x0805a1, 0x3840bb, 0xe02a2e, 0x000002,
+ 0x0d5dd8, 0xe8f259, 0x0d5dd8, 0x1c0af6, 0xf04700, 0x000003,
+ 0x0bb422, 0xec08d9, 0x0bb422, 0x1bfccc, 0xf09216, 0x000003,
+ 0x08fde6, 0xf108be, 0x08fde6, 0x1bf096, 0xf10ae0, 0x000003,
+ 0x0ae311, 0xeeeda3, 0x0ae311, 0x37c646, 0xe385f5, 0x000002,
+ 0x044089, 0xfa7242, 0x044089, 0x37a785, 0xe56526, 0x000002,
+ 0x00c75c, 0xffb947, 0x00c75c, 0x378ba3, 0xe72c5f, 0x000002,
+ 0x000000, 0x0ef76e, 0x0ef76e, 0x377fda, 0x000000, 0x000001,
+};
+
+static const unsigned int src_iir_coeff_96_to_44[] = {
+ 0x08b543, 0xfd80f4, 0x08b543, 0x0e2332, 0xe06ed0, 0x000002,
+ 0x1b6038, 0xf90e7e, 0x1b6038, 0x0ec1ac, 0xe16f66, 0x000002,
+ 0x188478, 0xfbb921, 0x188478, 0x105859, 0xe2e596, 0x000002,
+ 0x13eff3, 0xffa707, 0x13eff3, 0x13455c, 0xe533b7, 0x000002,
+ 0x0dc239, 0x03d458, 0x0dc239, 0x17f120, 0xe8b617, 0x000002,
+ 0x0745f1, 0x05d790, 0x0745f1, 0x1e3d75, 0xed5f18, 0x000002,
+ 0x05641f, 0x085e2b, 0x05641f, 0x48efd0, 0xe3e9c8, 0x000001,
+ 0x000000, 0x28f632, 0x28f632, 0x273905, 0x000000, 0x000001,
+};
+
+static unsigned int mtk_get_src_freq_mode(struct mtk_base_afe *afe, int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0x50000;
+ case 11025:
+ return 0x6e400;
+ case 12000:
+ return 0x78000;
+ case 16000:
+ return 0xa0000;
+ case 22050:
+ return 0xdc800;
+ case 24000:
+ return 0xf0000;
+ case 32000:
+ return 0x140000;
+ case 44100:
+ return 0x1b9000;
+ case 48000:
+ return 0x1e0000;
+ case 88200:
+ return 0x372000;
+ case 96000:
+ return 0x3c0000;
+ case 176400:
+ return 0x6e4000;
+ case 192000:
+ return 0x780000;
+ default:
+ dev_err(afe->dev, "%s(), rate %d invalid!!!\n",
+ __func__, rate);
+ return 0;
+ }
+}
+
+static const unsigned int *get_iir_coeff(unsigned int rate_in,
+ unsigned int rate_out,
+ unsigned int *param_num)
+{
+ if (rate_in == 32000 && rate_out == 16000) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_32_to_16);
+ return src_iir_coeff_32_to_16;
+ } else if (rate_in == 44100 && rate_out == 16000) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_44_to_16);
+ return src_iir_coeff_44_to_16;
+ } else if (rate_in == 44100 && rate_out == 32000) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_44_to_32);
+ return src_iir_coeff_44_to_32;
+ } else if ((rate_in == 48000 && rate_out == 16000) ||
+ (rate_in == 96000 && rate_out == 32000)) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_48_to_16);
+ return src_iir_coeff_48_to_16;
+ } else if (rate_in == 48000 && rate_out == 32000) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_48_to_32);
+ return src_iir_coeff_48_to_32;
+ } else if (rate_in == 48000 && rate_out == 44100) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_48_to_44);
+ return src_iir_coeff_48_to_44;
+ } else if (rate_in == 96000 && rate_out == 16000) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_96_to_16);
+ return src_iir_coeff_96_to_16;
+ } else if ((rate_in == 96000 && rate_out == 44100) ||
+ (rate_in == 48000 && rate_out == 22050)) {
+ *param_num = ARRAY_SIZE(src_iir_coeff_96_to_44);
+ return src_iir_coeff_96_to_44;
+ }
+
+ *param_num = 0;
+ return NULL;
+}
+
+static int mtk_set_src_1_param(struct mtk_base_afe *afe, int id)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_src_priv *src_priv = afe_priv->dai_priv[id];
+ unsigned int iir_coeff_num;
+ unsigned int iir_stage;
+ int rate_in = src_priv->dl_rate;
+ int rate_out = src_priv->ul_rate;
+ unsigned int out_freq_mode = mtk_get_src_freq_mode(afe, rate_out);
+ unsigned int in_freq_mode = mtk_get_src_freq_mode(afe, rate_in);
+
+ /* set out freq mode */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON3,
+ G_SRC_ASM_FREQ_4_MASK_SFT,
+ out_freq_mode << G_SRC_ASM_FREQ_4_SFT);
+
+ /* set in freq mode */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON4,
+ G_SRC_ASM_FREQ_5_MASK_SFT,
+ in_freq_mode << G_SRC_ASM_FREQ_5_SFT);
+
+ regmap_write(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON5, 0x3f5986);
+ regmap_write(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON5, 0x3f5987);
+ regmap_write(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON6, 0x1fbd);
+ regmap_write(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON2, 0);
+
+ /* set iir if in_rate > out_rate */
+ if (rate_in > rate_out) {
+ int i;
+ const unsigned int *iir_coeff = get_iir_coeff(rate_in, rate_out,
+ &iir_coeff_num);
+
+ if (iir_coeff_num == 0 || !iir_coeff) {
+ dev_err(afe->dev, "%s(), iir coeff error, num %d, coeff %p\n",
+ __func__, iir_coeff_num, iir_coeff);
+ return -EINVAL;
+ }
+
+ /* COEFF_SRAM_CTRL */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON0,
+ G_SRC_COEFF_SRAM_CTRL_MASK_SFT,
+ BIT(G_SRC_COEFF_SRAM_CTRL_SFT));
+ /* Clear coeff history to r/w coeff from the first position */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON13,
+ G_SRC_COEFF_SRAM_ADR_MASK_SFT, 0);
+ /* Write SRC coeff, should not read the reg during write */
+ for (i = 0; i < iir_coeff_num; i++)
+ regmap_write(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON12,
+ iir_coeff[i]);
+ /* disable sram access */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON0,
+ G_SRC_COEFF_SRAM_CTRL_MASK_SFT, 0);
+ /* CHSET_IIR_STAGE */
+ iir_stage = (iir_coeff_num / 6) - 1;
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON2,
+ G_SRC_CHSET_IIR_STAGE_MASK_SFT,
+ iir_stage << G_SRC_CHSET_IIR_STAGE_SFT);
+ /* CHSET_IIR_EN */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON2,
+ G_SRC_CHSET_IIR_EN_MASK_SFT,
+ BIT(G_SRC_CHSET_IIR_EN_SFT));
+ } else {
+ /* CHSET_IIR_EN off */
+ regmap_update_bits(afe->regmap, AFE_GENERAL1_ASRC_2CH_CON2,
+ G_SRC_CHSET_IIR_EN_MASK_SFT, 0);
+ }
+
+ return 0;
+}
+
+static int mtk_set_src_2_param(struct mtk_base_afe *afe, int id)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_src_priv *src_priv = afe_priv->dai_priv[id];
+ unsigned int iir_coeff_num;
+ unsigned int iir_stage;
+ int rate_in = src_priv->dl_rate;
+ int rate_out = src_priv->ul_rate;
+ unsigned int out_freq_mode = mtk_get_src_freq_mode(afe, rate_out);
+ unsigned int in_freq_mode = mtk_get_src_freq_mode(afe, rate_in);
+
+ /* set out freq mode */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON3,
+ G_SRC_ASM_FREQ_4_MASK_SFT,
+ out_freq_mode << G_SRC_ASM_FREQ_4_SFT);
+
+ /* set in freq mode */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON4,
+ G_SRC_ASM_FREQ_5_MASK_SFT,
+ in_freq_mode << G_SRC_ASM_FREQ_5_SFT);
+
+ regmap_write(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON5, 0x3f5986);
+ regmap_write(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON5, 0x3f5987);
+ regmap_write(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON6, 0x1fbd);
+ regmap_write(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON2, 0);
+
+ /* set iir if in_rate > out_rate */
+ if (rate_in > rate_out) {
+ int i;
+ const unsigned int *iir_coeff = get_iir_coeff(rate_in, rate_out,
+ &iir_coeff_num);
+
+ if (iir_coeff_num == 0 || !iir_coeff) {
+ dev_err(afe->dev, "%s(), iir coeff error, num %d, coeff %p\n",
+ __func__, iir_coeff_num, iir_coeff);
+ return -EINVAL;
+ }
+
+ /* COEFF_SRAM_CTRL */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON0,
+ G_SRC_COEFF_SRAM_CTRL_MASK_SFT,
+ BIT(G_SRC_COEFF_SRAM_CTRL_SFT));
+ /* Clear coeff history to r/w coeff from the first position */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON13,
+ G_SRC_COEFF_SRAM_ADR_MASK_SFT, 0);
+ /* Write SRC coeff, should not read the reg during write */
+ for (i = 0; i < iir_coeff_num; i++)
+ regmap_write(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON12,
+ iir_coeff[i]);
+ /* disable sram access */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON0,
+ G_SRC_COEFF_SRAM_CTRL_MASK_SFT, 0);
+ /* CHSET_IIR_STAGE */
+ iir_stage = (iir_coeff_num / 6) - 1;
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON2,
+ G_SRC_CHSET_IIR_STAGE_MASK_SFT,
+ iir_stage << G_SRC_CHSET_IIR_STAGE_SFT);
+ /* CHSET_IIR_EN */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON2,
+ G_SRC_CHSET_IIR_EN_MASK_SFT,
+ BIT(G_SRC_CHSET_IIR_EN_SFT));
+ } else {
+ /* CHSET_IIR_EN off */
+ regmap_update_bits(afe->regmap, AFE_GENERAL2_ASRC_2CH_CON2,
+ G_SRC_CHSET_IIR_EN_MASK_SFT, 0);
+ }
+
+ return 0;
+}
+
+#define HW_SRC_1_EN_W_NAME "HW_SRC_1_Enable"
+#define HW_SRC_2_EN_W_NAME "HW_SRC_2_Enable"
+
+static int mtk_hw_src_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int id;
+ struct mtk_afe_src_priv *src_priv;
+ unsigned int reg;
+
+ if (snd_soc_dapm_widget_name_cmp(w, HW_SRC_1_EN_W_NAME) == 0)
+ id = MT8186_DAI_SRC_1;
+ else
+ id = MT8186_DAI_SRC_2;
+
+ src_priv = afe_priv->dai_priv[id];
+
+ dev_dbg(afe->dev,
+ "%s(), name %s, event 0x%x, id %d, src_priv %p, dl_rate %d, ul_rate %d\n",
+ __func__, w->name, event, id, src_priv,
+ src_priv->dl_rate, src_priv->ul_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (id == MT8186_DAI_SRC_1)
+ mtk_set_src_1_param(afe, id);
+ else
+ mtk_set_src_2_param(afe, id);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ reg = (id == MT8186_DAI_SRC_1) ?
+ AFE_GENERAL1_ASRC_2CH_CON0 : AFE_GENERAL2_ASRC_2CH_CON0;
+ /* ASM_ON */
+ regmap_update_bits(afe->regmap, reg,
+ G_SRC_ASM_ON_MASK_SFT,
+ BIT(G_SRC_ASM_ON_SFT));
+ /* CHSET_ON */
+ regmap_update_bits(afe->regmap, reg,
+ G_SRC_CHSET_ON_MASK_SFT,
+ BIT(G_SRC_CHSET_ON_SFT));
+ /* CHSET_STR_CLR */
+ regmap_update_bits(afe->regmap, reg,
+ G_SRC_CHSET_STR_CLR_MASK_SFT,
+ BIT(G_SRC_CHSET_STR_CLR_SFT));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ reg = (id == MT8186_DAI_SRC_1) ?
+ AFE_GENERAL1_ASRC_2CH_CON0 : AFE_GENERAL2_ASRC_2CH_CON0;
+ /* ASM_OFF */
+ regmap_update_bits(afe->regmap, reg, G_SRC_ASM_ON_MASK_SFT, 0);
+ /* CHSET_OFF */
+ regmap_update_bits(afe->regmap, reg, G_SRC_CHSET_ON_MASK_SFT, 0);
+ /* CHSET_STR_CLR */
+ regmap_update_bits(afe->regmap, reg, G_SRC_CHSET_STR_CLR_MASK_SFT, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_hw_src_1_in_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN40,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN40,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN40,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN40_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN40_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1 Switch", AFE_CONN40,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN40_1,
+ I_DL5_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_src_1_in_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN41,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN41,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN41,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN41_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN41_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2 Switch", AFE_CONN41,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN41_1,
+ I_DL5_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_src_2_in_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1 Switch", AFE_CONN42,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN42,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1 Switch", AFE_CONN42,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN42,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1 Switch", AFE_CONN42_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1 Switch", AFE_CONN42_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN2_OUT_CH1 Switch", AFE_CONN42,
+ I_GAIN2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_src_2_in_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2 Switch", AFE_CONN43,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN43,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2 Switch", AFE_CONN43,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN43,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2 Switch", AFE_CONN43_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2 Switch", AFE_CONN43_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN2_OUT_CH2 Switch", AFE_CONN43,
+ I_GAIN2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_src_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("HW_SRC_1_IN_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_hw_src_1_in_ch1_mix,
+ ARRAY_SIZE(mtk_hw_src_1_in_ch1_mix)),
+ SND_SOC_DAPM_MIXER("HW_SRC_1_IN_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_hw_src_1_in_ch2_mix,
+ ARRAY_SIZE(mtk_hw_src_1_in_ch2_mix)),
+ SND_SOC_DAPM_MIXER("HW_SRC_2_IN_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_hw_src_2_in_ch1_mix,
+ ARRAY_SIZE(mtk_hw_src_2_in_ch1_mix)),
+ SND_SOC_DAPM_MIXER("HW_SRC_2_IN_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_hw_src_2_in_ch2_mix,
+ ARRAY_SIZE(mtk_hw_src_2_in_ch2_mix)),
+
+ SND_SOC_DAPM_SUPPLY(HW_SRC_1_EN_W_NAME,
+ GENERAL_ASRC_EN_ON, GENERAL1_ASRC_EN_ON_SFT, 0,
+ mtk_hw_src_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY(HW_SRC_2_EN_W_NAME,
+ GENERAL_ASRC_EN_ON, GENERAL2_ASRC_EN_ON_SFT, 0,
+ mtk_hw_src_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_INPUT("HW SRC 1 Out Endpoint"),
+ SND_SOC_DAPM_INPUT("HW SRC 2 Out Endpoint"),
+ SND_SOC_DAPM_OUTPUT("HW SRC 1 In Endpoint"),
+ SND_SOC_DAPM_OUTPUT("HW SRC 2 In Endpoint"),
+};
+
+static int mtk_afe_src_en_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_src_priv *src_priv;
+
+ if (snd_soc_dapm_widget_name_cmp(w, HW_SRC_1_EN_W_NAME) == 0)
+ src_priv = afe_priv->dai_priv[MT8186_DAI_SRC_1];
+ else
+ src_priv = afe_priv->dai_priv[MT8186_DAI_SRC_2];
+
+ dev_dbg(afe->dev,
+ "%s(), source %s, sink %s, dl_rate %d, ul_rate %d\n",
+ __func__, source->name, sink->name,
+ src_priv->dl_rate, src_priv->ul_rate);
+
+ return (src_priv->dl_rate > 0 && src_priv->ul_rate > 0) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_src_routes[] = {
+ {"HW_SRC_1_IN_CH1", "DL1_CH1 Switch", "DL1"},
+ {"HW_SRC_1_IN_CH2", "DL1_CH2 Switch", "DL1"},
+ {"HW_SRC_2_IN_CH1", "DL1_CH1 Switch", "DL1"},
+ {"HW_SRC_2_IN_CH2", "DL1_CH2 Switch", "DL1"},
+ {"HW_SRC_1_IN_CH1", "DL2_CH1 Switch", "DL2"},
+ {"HW_SRC_1_IN_CH2", "DL2_CH2 Switch", "DL2"},
+ {"HW_SRC_2_IN_CH1", "DL2_CH1 Switch", "DL2"},
+ {"HW_SRC_2_IN_CH2", "DL2_CH2 Switch", "DL2"},
+ {"HW_SRC_1_IN_CH1", "DL3_CH1 Switch", "DL3"},
+ {"HW_SRC_1_IN_CH2", "DL3_CH2 Switch", "DL3"},
+ {"HW_SRC_2_IN_CH1", "DL3_CH1 Switch", "DL3"},
+ {"HW_SRC_2_IN_CH2", "DL3_CH2 Switch", "DL3"},
+ {"HW_SRC_1_IN_CH1", "DL6_CH1 Switch", "DL6"},
+ {"HW_SRC_1_IN_CH2", "DL6_CH2 Switch", "DL6"},
+ {"HW_SRC_2_IN_CH1", "DL6_CH1 Switch", "DL6"},
+ {"HW_SRC_2_IN_CH2", "DL6_CH2 Switch", "DL6"},
+ {"HW_SRC_1_IN_CH1", "DL5_CH1 Switch", "DL5"},
+ {"HW_SRC_1_IN_CH2", "DL5_CH2 Switch", "DL5"},
+ {"HW_SRC_2_IN_CH1", "DL5_CH1 Switch", "DL5"},
+ {"HW_SRC_2_IN_CH2", "DL5_CH2 Switch", "DL5"},
+ {"HW_SRC_1_IN_CH1", "DL4_CH1 Switch", "DL4"},
+ {"HW_SRC_1_IN_CH2", "DL4_CH2 Switch", "DL4"},
+ {"HW_SRC_2_IN_CH1", "DL4_CH1 Switch", "DL4"},
+ {"HW_SRC_2_IN_CH2", "DL4_CH2 Switch", "DL4"},
+
+ {"HW_SRC_1_In", NULL, "HW_SRC_1_IN_CH1"},
+ {"HW_SRC_1_In", NULL, "HW_SRC_1_IN_CH2"},
+
+ {"HW_SRC_2_In", NULL, "HW_SRC_2_IN_CH1"},
+ {"HW_SRC_2_In", NULL, "HW_SRC_2_IN_CH2"},
+
+ {"HW_SRC_1_In", NULL, HW_SRC_1_EN_W_NAME, mtk_afe_src_en_connect},
+ {"HW_SRC_1_Out", NULL, HW_SRC_1_EN_W_NAME, mtk_afe_src_en_connect},
+ {"HW_SRC_2_In", NULL, HW_SRC_2_EN_W_NAME, mtk_afe_src_en_connect},
+ {"HW_SRC_2_Out", NULL, HW_SRC_2_EN_W_NAME, mtk_afe_src_en_connect},
+
+ {"HW SRC 1 In Endpoint", NULL, "HW_SRC_1_In"},
+ {"HW SRC 2 In Endpoint", NULL, "HW_SRC_2_In"},
+ {"HW_SRC_1_Out", NULL, "HW SRC 1 Out Endpoint"},
+ {"HW_SRC_2_Out", NULL, "HW SRC 2 Out Endpoint"},
+};
+
+/* dai ops */
+static int mtk_dai_src_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int id = dai->id;
+ struct mtk_afe_src_priv *src_priv = afe_priv->dai_priv[id];
+ unsigned int sft, mask;
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, id);
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, id, substream->stream, rate);
+
+ /* rate */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ src_priv->dl_rate = rate;
+ if (id == MT8186_DAI_SRC_1) {
+ sft = GENERAL1_ASRCIN_MODE_SFT;
+ mask = GENERAL1_ASRCIN_MODE_MASK;
+ } else {
+ sft = GENERAL2_ASRCIN_MODE_SFT;
+ mask = GENERAL2_ASRCIN_MODE_MASK;
+ }
+ } else {
+ src_priv->ul_rate = rate;
+ if (id == MT8186_DAI_SRC_1) {
+ sft = GENERAL1_ASRCOUT_MODE_SFT;
+ mask = GENERAL1_ASRCOUT_MODE_MASK;
+ } else {
+ sft = GENERAL2_ASRCOUT_MODE_SFT;
+ mask = GENERAL2_ASRCOUT_MODE_MASK;
+ }
+ }
+
+ regmap_update_bits(afe->regmap, GENERAL_ASRC_MODE, mask << sft, rate_reg << sft);
+
+ return 0;
+}
+
+static int mtk_dai_src_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int id = dai->id;
+ struct mtk_afe_src_priv *src_priv = afe_priv->dai_priv[id];
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d\n",
+ __func__, id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ src_priv->dl_rate = 0;
+ else
+ src_priv->ul_rate = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_src_ops = {
+ .hw_params = mtk_dai_src_hw_params,
+ .hw_free = mtk_dai_src_hw_free,
+};
+
+/* dai driver */
+#define MTK_SRC_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_SRC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_src_driver[] = {
+ {
+ .name = "HW_SRC_1",
+ .id = MT8186_DAI_SRC_1,
+ .playback = {
+ .stream_name = "HW_SRC_1_In",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_SRC_RATES,
+ .formats = MTK_SRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HW_SRC_1_Out",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_SRC_RATES,
+ .formats = MTK_SRC_FORMATS,
+ },
+ .ops = &mtk_dai_src_ops,
+ },
+ {
+ .name = "HW_SRC_2",
+ .id = MT8186_DAI_SRC_2,
+ .playback = {
+ .stream_name = "HW_SRC_2_In",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_SRC_RATES,
+ .formats = MTK_SRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HW_SRC_2_Out",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_SRC_RATES,
+ .formats = MTK_SRC_FORMATS,
+ },
+ .ops = &mtk_dai_src_ops,
+ },
+};
+
+int mt8186_dai_src_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ int ret;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_src_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_src_driver);
+
+ dai->dapm_widgets = mtk_dai_src_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_src_widgets);
+ dai->dapm_routes = mtk_dai_src_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_src_routes);
+
+ /* set dai priv */
+ ret = mt8186_dai_set_priv(afe, MT8186_DAI_SRC_1,
+ sizeof(struct mtk_afe_src_priv), NULL);
+ if (ret)
+ return ret;
+
+ ret = mt8186_dai_set_priv(afe, MT8186_DAI_SRC_2,
+ sizeof(struct mtk_afe_src_priv), NULL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-tdm.c b/sound/soc/mediatek/mt8186/mt8186-dai-tdm.c
new file mode 100644
index 000000000000..7a8890d7122f
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-tdm.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI TDM Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+
+#include "mt8186-afe-clk.h"
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-gpio.h"
+#include "mt8186-interconnection.h"
+
+#define TDM_HD_EN_W_NAME "TDM_HD_EN"
+#define TDM_MCLK_EN_W_NAME "TDM_MCLK_EN"
+#define MTK_AFE_TDM_KCONTROL_NAME "TDM_HD_Mux"
+
+struct mtk_afe_tdm_priv {
+ unsigned int id;
+ unsigned int rate; /* for determine which apll to use */
+ unsigned int bck_invert;
+ unsigned int lck_invert;
+ unsigned int lrck_width;
+ unsigned int mclk_id;
+ unsigned int mclk_multiple; /* according to sample rate */
+ unsigned int mclk_rate;
+ unsigned int mclk_apll;
+ unsigned int tdm_mode;
+ unsigned int data_mode;
+ unsigned int slave_mode;
+ unsigned int low_jitter_en;
+};
+
+enum {
+ TDM_IN_I2S = 0,
+ TDM_IN_LJ = 1,
+ TDM_IN_RJ = 2,
+ TDM_IN_DSP_A = 4,
+ TDM_IN_DSP_B = 5,
+};
+
+enum {
+ TDM_DATA_ONE_PIN = 0,
+ TDM_DATA_MULTI_PIN,
+};
+
+enum {
+ TDM_BCK_NON_INV = 0,
+ TDM_BCK_INV = 1,
+};
+
+enum {
+ TDM_LCK_NON_INV = 0,
+ TDM_LCK_INV = 1,
+};
+
+static unsigned int get_tdm_lrck_width(snd_pcm_format_t format,
+ unsigned int mode)
+{
+ if (mode == TDM_IN_DSP_A || mode == TDM_IN_DSP_B)
+ return 0;
+
+ return snd_pcm_format_physical_width(format) - 1;
+}
+
+static unsigned int get_tdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+
+ return 2;
+}
+
+static unsigned int get_tdm_ch_per_sdata(unsigned int mode,
+ unsigned int channels)
+{
+ if (mode == TDM_IN_DSP_A || mode == TDM_IN_DSP_B)
+ return get_tdm_ch_fixup(channels);
+
+ return 2;
+}
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_TDM_MCK_EN,
+ SUPPLY_SEQ_TDM_HD_EN,
+ SUPPLY_SEQ_TDM_EN,
+};
+
+static int get_tdm_id_by_name(const char *name)
+{
+ return MT8186_DAI_TDM_IN;
+}
+
+static int mtk_tdm_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_afe_gpio_request(afe->dev, true, tdm_priv->id, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8186_afe_gpio_request(afe->dev, false, tdm_priv->id, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
+ __func__, w->name, event, dai_id);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8186_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tdm_priv->mclk_rate = 0;
+ mt8186_mck_disable(afe, tdm_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* dai component */
+/* tdm virtual mux to output widget */
+static const char * const tdm_mux_map[] = {
+ "Normal", "Dummy_Widget",
+};
+
+static int tdm_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(tdm_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ tdm_mux_map,
+ tdm_mux_map_value);
+
+static const struct snd_kcontrol_new tdm_in_mux_control =
+ SOC_DAPM_ENUM("TDM In Select", tdm_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_tdm_clk"),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_EN", SUPPLY_SEQ_TDM_EN,
+ ETDM_IN1_CON0, ETDM_IN1_CON0_REG_ETDM_IN_EN_SFT,
+ 0, mtk_tdm_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* tdm hd en */
+ SND_SOC_DAPM_SUPPLY_S(TDM_HD_EN_W_NAME, SUPPLY_SEQ_TDM_HD_EN,
+ ETDM_IN1_CON2, ETDM_IN1_CON2_REG_CLOCK_SOURCE_SEL_SFT,
+ 0, NULL,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S(TDM_MCLK_EN_W_NAME, SUPPLY_SEQ_TDM_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("TDM_DUMMY_IN"),
+
+ SND_SOC_DAPM_MUX("TDM_In_Mux",
+ SND_SOC_NOPM, 0, 0, &tdm_in_mux_control),
+};
+
+static int mtk_afe_tdm_mclk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ return (tdm_priv->mclk_rate > 0) ? 1 : 0;
+}
+
+static int mtk_afe_tdm_mclk_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int cur_apll;
+
+ /* which apll */
+ cur_apll = mt8186_get_apll_by_name(afe, source->name);
+
+ return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_afe_tdm_hd_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ return tdm_priv->low_jitter_en;
+}
+
+static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int cur_apll;
+ int tdm_need_apll;
+
+ /* which apll */
+ cur_apll = mt8186_get_apll_by_name(afe, source->name);
+
+ /* choose APLL from tdm rate */
+ tdm_need_apll = mt8186_get_apll_by_rate(afe, tdm_priv->rate);
+
+ return (tdm_need_apll == cur_apll) ? 1 : 0;
+}
+
+/* low jitter control */
+static const char * const mt8186_tdm_hd_str[] = {
+ "Normal", "Low_Jitter"
+};
+
+static const struct soc_enum mt8186_tdm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8186_tdm_hd_str),
+ mt8186_tdm_hd_str),
+};
+
+static int mt8186_tdm_hd_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(kcontrol->id.name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ ucontrol->value.integer.value[0] = tdm_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mt8186_tdm_hd_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(kcontrol->id.name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int hd_en;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ hd_en = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, hd_en %d\n",
+ __func__, kcontrol->id.name, hd_en);
+
+ if (tdm_priv->low_jitter_en == hd_en)
+ return 0;
+
+ tdm_priv->low_jitter_en = hd_en;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new mtk_dai_tdm_controls[] = {
+ SOC_ENUM_EXT(MTK_AFE_TDM_KCONTROL_NAME, mt8186_tdm_enum[0],
+ mt8186_tdm_hd_get, mt8186_tdm_hd_set),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
+ {"TDM IN", NULL, "aud_tdm_clk"},
+ {"TDM IN", NULL, "TDM_EN"},
+ {"TDM IN", NULL, TDM_HD_EN_W_NAME, mtk_afe_tdm_hd_connect},
+ {TDM_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {TDM_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+
+ {"TDM IN", NULL, TDM_MCLK_EN_W_NAME, mtk_afe_tdm_mclk_connect},
+ {TDM_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_tdm_mclk_apll_connect},
+ {TDM_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_tdm_mclk_apll_connect},
+
+ /* allow tdm on without codec on */
+ {"TDM IN", NULL, "TDM_In_Mux"},
+ {"TDM_In_Mux", "Dummy_Widget", "TDM_DUMMY_IN"},
+};
+
+/* dai ops */
+static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
+ struct mtk_afe_tdm_priv *tdm_priv,
+ int freq)
+{
+ int apll;
+ int apll_rate;
+
+ apll = mt8186_get_apll_by_rate(afe, freq);
+ apll_rate = mt8186_get_apll_rate(afe, apll);
+
+ if (!freq || freq > apll_rate) {
+ dev_err(afe->dev,
+ "%s(), freq(%d Hz) invalid\n", __func__, freq);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_err(afe->dev,
+ "%s(), APLL cannot generate %d Hz", __func__, freq);
+ return -EINVAL;
+ }
+
+ tdm_priv->mclk_rate = freq;
+ tdm_priv->mclk_apll = apll;
+
+ return 0;
+}
+
+static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ int tdm_id = dai->id;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[tdm_id];
+ unsigned int tdm_mode = tdm_priv->tdm_mode;
+ unsigned int data_mode = tdm_priv->data_mode;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int bit_width =
+ snd_pcm_format_physical_width(format);
+ unsigned int tdm_channels = (data_mode == TDM_DATA_ONE_PIN) ?
+ get_tdm_ch_per_sdata(tdm_mode, channels) : 2;
+ unsigned int lrck_width =
+ get_tdm_lrck_width(format, tdm_mode);
+ unsigned int tdm_con = 0;
+ bool slave_mode = tdm_priv->slave_mode;
+ bool lrck_inv = tdm_priv->lck_invert;
+ bool bck_inv = tdm_priv->bck_invert;
+ unsigned int tran_rate;
+ unsigned int tran_relatch_rate;
+
+ tdm_priv->rate = rate;
+ tran_rate = mt8186_rate_transform(afe->dev, rate, dai->id);
+ tran_relatch_rate = mt8186_tdm_relatch_rate_transform(afe->dev, rate);
+
+ /* calculate mclk_rate, if not set explicitly */
+ if (!tdm_priv->mclk_rate) {
+ tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
+ mtk_dai_tdm_cal_mclk(afe, tdm_priv, tdm_priv->mclk_rate);
+ }
+
+ /* ETDM_IN1_CON0 */
+ tdm_con |= slave_mode << ETDM_IN1_CON0_REG_SLAVE_MODE_SFT;
+ tdm_con |= tdm_mode << ETDM_IN1_CON0_REG_FMT_SFT;
+ tdm_con |= (bit_width - 1) << ETDM_IN1_CON0_REG_BIT_LENGTH_SFT;
+ tdm_con |= (bit_width - 1) << ETDM_IN1_CON0_REG_WORD_LENGTH_SFT;
+ tdm_con |= (tdm_channels - 1) << ETDM_IN1_CON0_REG_CH_NUM_SFT;
+ /* need to disable sync mode otherwise this may cause latch data error */
+ tdm_con |= 0 << ETDM_IN1_CON0_REG_SYNC_MODE_SFT;
+ /* relatch 1x en clock fix to h26m */
+ tdm_con |= 0 << ETDM_IN1_CON0_REG_RELATCH_1X_EN_SEL_DOMAIN_SFT;
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON0, ETDM_IN_CON0_CTRL_MASK, tdm_con);
+
+ /* ETDM_IN1_CON1 */
+ tdm_con = 0;
+ tdm_con |= 0 << ETDM_IN1_CON1_REG_LRCK_AUTO_MODE_SFT;
+ tdm_con |= 1 << ETDM_IN1_CON1_PINMUX_MCLK_CTRL_OE_SFT;
+ tdm_con |= (lrck_width - 1) << ETDM_IN1_CON1_REG_LRCK_WIDTH_SFT;
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON1, ETDM_IN_CON1_CTRL_MASK, tdm_con);
+
+ /* ETDM_IN1_CON3 */
+ tdm_con = ETDM_IN_CON3_FS(tran_rate);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON3, ETDM_IN_CON3_CTRL_MASK, tdm_con);
+
+ /* ETDM_IN1_CON4 */
+ tdm_con = ETDM_IN_CON4_FS(tran_relatch_rate);
+ if (slave_mode) {
+ if (lrck_inv)
+ tdm_con |= ETDM_IN_CON4_CON0_SLAVE_LRCK_INV;
+ if (bck_inv)
+ tdm_con |= ETDM_IN_CON4_CON0_SLAVE_BCK_INV;
+ } else {
+ if (lrck_inv)
+ tdm_con |= ETDM_IN_CON4_CON0_MASTER_LRCK_INV;
+ if (bck_inv)
+ tdm_con |= ETDM_IN_CON4_CON0_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON4, ETDM_IN_CON4_CTRL_MASK, tdm_con);
+
+ /* ETDM_IN1_CON2 */
+ tdm_con = 0;
+ if (data_mode == TDM_DATA_MULTI_PIN) {
+ tdm_con |= ETDM_IN_CON2_MULTI_IP_2CH_MODE;
+ tdm_con |= ETDM_IN_CON2_MULTI_IP_CH(channels);
+ }
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON2, ETDM_IN_CON2_CTRL_MASK, tdm_con);
+
+ /* ETDM_IN1_CON8 */
+ tdm_con = 0;
+ if (slave_mode) {
+ tdm_con |= 1 << ETDM_IN1_CON8_REG_ETDM_USE_AFIFO_SFT;
+ tdm_con |= 0 << ETDM_IN1_CON8_REG_AFIFO_CLOCK_DOMAIN_SEL_SFT;
+ tdm_con |= ETDM_IN_CON8_FS(tran_relatch_rate);
+ } else {
+ tdm_con |= 0 << ETDM_IN1_CON8_REG_ETDM_USE_AFIFO_SFT;
+ }
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON8, ETDM_IN_CON8_CTRL_MASK, tdm_con);
+
+ return 0;
+}
+
+static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (dir != SND_SOC_CLOCK_IN) {
+ dev_err(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
+}
+
+static int mtk_dai_tdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ /* DAI mode*/
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ tdm_priv->tdm_mode = TDM_IN_I2S;
+ tdm_priv->data_mode = TDM_DATA_MULTI_PIN;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ tdm_priv->tdm_mode = TDM_IN_LJ;
+ tdm_priv->data_mode = TDM_DATA_MULTI_PIN;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ tdm_priv->tdm_mode = TDM_IN_RJ;
+ tdm_priv->data_mode = TDM_DATA_MULTI_PIN;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_priv->tdm_mode = TDM_IN_DSP_A;
+ tdm_priv->data_mode = TDM_DATA_ONE_PIN;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_priv->tdm_mode = TDM_IN_DSP_B;
+ tdm_priv->data_mode = TDM_DATA_ONE_PIN;
+ break;
+ default:
+ dev_err(afe->dev, "%s(), invalid DAIFMT_FORMAT_MASK", __func__);
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion*/
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ tdm_priv->bck_invert = TDM_BCK_NON_INV;
+ tdm_priv->lck_invert = TDM_LCK_NON_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ tdm_priv->bck_invert = TDM_BCK_NON_INV;
+ tdm_priv->lck_invert = TDM_LCK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ tdm_priv->bck_invert = TDM_BCK_INV;
+ tdm_priv->lck_invert = TDM_LCK_NON_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ tdm_priv->bck_invert = TDM_BCK_INV;
+ tdm_priv->lck_invert = TDM_LCK_INV;
+ break;
+ default:
+ dev_err(afe->dev, "%s(), invalid DAIFMT_INV_MASK", __func__);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ tdm_priv->slave_mode = false;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ tdm_priv->slave_mode = true;
+ break;
+ default:
+ dev_err(afe->dev, "%s(), invalid DAIFMT_CLOCK_PROVIDER_MASK",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_tdm_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots,
+ int slot_width)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(dai->dev, "%s %d slot_width %d\n", __func__, dai->id, slot_width);
+
+ tdm_priv->lrck_width = slot_width;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
+ .hw_params = mtk_dai_tdm_hw_params,
+ .set_sysclk = mtk_dai_tdm_set_sysclk,
+ .set_fmt = mtk_dai_tdm_set_fmt,
+ .set_tdm_slot = mtk_dai_tdm_set_tdm_slot,
+};
+
+/* dai driver */
+#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
+ {
+ .name = "TDM IN",
+ .id = MT8186_DAI_TDM_IN,
+ .capture = {
+ .stream_name = "TDM IN",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+};
+
+static struct mtk_afe_tdm_priv *init_tdm_priv_data(struct mtk_base_afe *afe)
+{
+ struct mtk_afe_tdm_priv *tdm_priv;
+
+ tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
+ GFP_KERNEL);
+ if (!tdm_priv)
+ return NULL;
+
+ tdm_priv->mclk_multiple = 512;
+ tdm_priv->mclk_id = MT8186_TDM_MCK;
+ tdm_priv->id = MT8186_DAI_TDM_IN;
+
+ return tdm_priv;
+}
+
+int mt8186_dai_tdm_register(struct mtk_base_afe *afe)
+{
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv;
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_tdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
+
+ dai->controls = mtk_dai_tdm_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_tdm_controls);
+ dai->dapm_widgets = mtk_dai_tdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
+ dai->dapm_routes = mtk_dai_tdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
+
+ tdm_priv = init_tdm_priv_data(afe);
+ if (!tdm_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8186_DAI_TDM_IN] = tdm_priv;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-interconnection.h b/sound/soc/mediatek/mt8186/mt8186-interconnection.h
new file mode 100644
index 000000000000..5b188d93ebd3
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-interconnection.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Mediatek MT8186 audio driver interconnection definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT8186_INTERCONNECTION_H_
+#define _MT8186_INTERCONNECTION_H_
+
+/* in port define */
+#define I_I2S0_CH1 0
+#define I_I2S0_CH2 1
+#define I_ADDA_UL_CH1 3
+#define I_ADDA_UL_CH2 4
+#define I_DL1_CH1 5
+#define I_DL1_CH2 6
+#define I_DL2_CH1 7
+#define I_DL2_CH2 8
+#define I_PCM_1_CAP_CH1 9
+#define I_GAIN1_OUT_CH1 10
+#define I_GAIN1_OUT_CH2 11
+#define I_GAIN2_OUT_CH1 12
+#define I_GAIN2_OUT_CH2 13
+#define I_PCM_2_CAP_CH1 14
+#define I_ADDA_UL_CH3 17
+#define I_ADDA_UL_CH4 18
+#define I_DL12_CH1 19
+#define I_DL12_CH2 20
+#define I_DL12_CH3 5
+#define I_DL12_CH4 6
+#define I_PCM_2_CAP_CH2 21
+#define I_PCM_1_CAP_CH2 22
+#define I_DL3_CH1 23
+#define I_DL3_CH2 24
+#define I_I2S2_CH1 25
+#define I_I2S2_CH2 26
+#define I_I2S2_CH3 27
+#define I_I2S2_CH4 28
+
+/* in port define >= 32 */
+#define I_32_OFFSET 32
+#define I_CONNSYS_I2S_CH1 (34 - I_32_OFFSET)
+#define I_CONNSYS_I2S_CH2 (35 - I_32_OFFSET)
+#define I_SRC_1_OUT_CH1 (36 - I_32_OFFSET)
+#define I_SRC_1_OUT_CH2 (37 - I_32_OFFSET)
+#define I_SRC_2_OUT_CH1 (38 - I_32_OFFSET)
+#define I_SRC_2_OUT_CH2 (39 - I_32_OFFSET)
+#define I_DL4_CH1 (40 - I_32_OFFSET)
+#define I_DL4_CH2 (41 - I_32_OFFSET)
+#define I_DL5_CH1 (42 - I_32_OFFSET)
+#define I_DL5_CH2 (43 - I_32_OFFSET)
+#define I_DL6_CH1 (44 - I_32_OFFSET)
+#define I_DL6_CH2 (45 - I_32_OFFSET)
+#define I_DL7_CH1 (46 - I_32_OFFSET)
+#define I_DL7_CH2 (47 - I_32_OFFSET)
+#define I_DL8_CH1 (48 - I_32_OFFSET)
+#define I_DL8_CH2 (49 - I_32_OFFSET)
+#define I_TDM_IN_CH1 (56 - I_32_OFFSET)
+#define I_TDM_IN_CH2 (57 - I_32_OFFSET)
+#define I_TDM_IN_CH3 (58 - I_32_OFFSET)
+#define I_TDM_IN_CH4 (59 - I_32_OFFSET)
+#define I_TDM_IN_CH5 (60 - I_32_OFFSET)
+#define I_TDM_IN_CH6 (61 - I_32_OFFSET)
+#define I_TDM_IN_CH7 (62 - I_32_OFFSET)
+#define I_TDM_IN_CH8 (63 - I_32_OFFSET)
+
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-misc-control.c b/sound/soc/mediatek/mt8186/mt8186-misc-control.c
new file mode 100644
index 000000000000..3b569786306a
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-misc-control.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio Misc Control
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "../common/mtk-afe-fe-dai.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "mt8186-afe-common.h"
+
+static const char * const mt8186_sgen_mode_str[] = {
+ "I0I1", "I2", "I3I4", "I5I6",
+ "I7I8", "I9I22", "I10I11", "I12I13",
+ "I14I21", "I15I16", "I17I18", "I19I20",
+ "I23I24", "I25I26", "I27I28", "I33",
+ "I34I35", "I36I37", "I38I39", "I40I41",
+ "I42I43", "I44I45", "I46I47", "I48I49",
+ "I56I57", "I58I59", "I60I61", "I62I63",
+ "O0O1", "O2", "O3O4", "O5O6",
+ "O7O8", "O9O10", "O11", "O12",
+ "O13O14", "O15O16", "O17O18", "O19O20",
+ "O21O22", "O23O24", "O25", "O28O29",
+ "O34", "O35", "O32O33", "O36O37",
+ "O38O39", "O30O31", "O40O41", "O42O43",
+ "O44O45", "O46O47", "O48O49", "O50O51",
+ "O58O59", "O60O61", "O62O63", "O64O65",
+ "O66O67", "O68O69", "O26O27", "OFF",
+};
+
+static const int mt8186_sgen_mode_idx[] = {
+ 0, 2, 4, 6,
+ 8, 22, 10, 12,
+ 14, -1, 18, 20,
+ 24, 26, 28, 33,
+ 34, 36, 38, 40,
+ 42, 44, 46, 48,
+ 56, 58, 60, 62,
+ 128, 130, 132, 134,
+ 135, 138, 139, 140,
+ 142, 144, 166, 148,
+ 150, 152, 153, 156,
+ 162, 163, 160, 164,
+ 166, -1, 168, 170,
+ 172, 174, 176, 178,
+ 186, 188, 190, 192,
+ 194, 196, -1, -1,
+};
+
+static const char * const mt8186_sgen_rate_str[] = {
+ "8K", "11K", "12K", "16K",
+ "22K", "24K", "32K", "44K",
+ "48K", "88k", "96k", "176k",
+ "192k"
+};
+
+static const int mt8186_sgen_rate_idx[] = {
+ 0, 1, 2, 4,
+ 5, 6, 8, 9,
+ 10, 11, 12, 13,
+ 14
+};
+
+/* this order must match reg bit amp_div_ch1/2 */
+static const char * const mt8186_sgen_amp_str[] = {
+ "1/128", "1/64", "1/32", "1/16", "1/8", "1/4", "1/2", "1" };
+
+static int mt8186_sgen_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->sgen_mode;
+
+ return 0;
+}
+
+static int mt8186_sgen_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int mode;
+ int mode_idx;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ mode = ucontrol->value.integer.value[0];
+ mode_idx = mt8186_sgen_mode_idx[mode];
+
+ dev_dbg(afe->dev, "%s(), mode %d, mode_idx %d\n",
+ __func__, mode, mode_idx);
+
+ if (mode == afe_priv->sgen_mode)
+ return 0;
+
+ if (mode_idx >= 0) {
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON2,
+ INNER_LOOP_BACK_MODE_MASK_SFT,
+ mode_idx << INNER_LOOP_BACK_MODE_SFT);
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON0,
+ DAC_EN_MASK_SFT, BIT(DAC_EN_SFT));
+ } else {
+ /* disable sgen */
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON0,
+ DAC_EN_MASK_SFT, 0);
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON2,
+ INNER_LOOP_BACK_MODE_MASK_SFT,
+ 0x3f << INNER_LOOP_BACK_MODE_SFT);
+ }
+
+ afe_priv->sgen_mode = mode;
+
+ return 1;
+}
+
+static int mt8186_sgen_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->sgen_rate;
+
+ return 0;
+}
+
+static int mt8186_sgen_rate_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int rate;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ rate = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), rate %d\n", __func__, rate);
+
+ if (rate == afe_priv->sgen_rate)
+ return 0;
+
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON0,
+ SINE_MODE_CH1_MASK_SFT,
+ mt8186_sgen_rate_idx[rate] << SINE_MODE_CH1_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON0,
+ SINE_MODE_CH2_MASK_SFT,
+ mt8186_sgen_rate_idx[rate] << SINE_MODE_CH2_SFT);
+
+ afe_priv->sgen_rate = rate;
+
+ return 1;
+}
+
+static int mt8186_sgen_amplitude_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->sgen_amplitude;
+ return 0;
+}
+
+static int mt8186_sgen_amplitude_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int amplitude;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ amplitude = ucontrol->value.integer.value[0];
+ if (amplitude > AMP_DIV_CH1_MASK) {
+ dev_err(afe->dev, "%s(), amplitude %d invalid\n",
+ __func__, amplitude);
+ return -EINVAL;
+ }
+
+ dev_dbg(afe->dev, "%s(), amplitude %d\n", __func__, amplitude);
+
+ if (amplitude == afe_priv->sgen_amplitude)
+ return 0;
+
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON0,
+ AMP_DIV_CH1_MASK_SFT,
+ amplitude << AMP_DIV_CH1_SFT);
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON0,
+ AMP_DIV_CH2_MASK_SFT,
+ amplitude << AMP_DIV_CH2_SFT);
+
+ afe_priv->sgen_amplitude = amplitude;
+
+ return 1;
+}
+
+static const struct soc_enum mt8186_afe_sgen_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8186_sgen_mode_str),
+ mt8186_sgen_mode_str),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8186_sgen_rate_str),
+ mt8186_sgen_rate_str),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8186_sgen_amp_str),
+ mt8186_sgen_amp_str),
+};
+
+static const struct snd_kcontrol_new mt8186_afe_sgen_controls[] = {
+ SOC_ENUM_EXT("Audio_SineGen_Switch", mt8186_afe_sgen_enum[0],
+ mt8186_sgen_get, mt8186_sgen_set),
+ SOC_ENUM_EXT("Audio_SineGen_SampleRate", mt8186_afe_sgen_enum[1],
+ mt8186_sgen_rate_get, mt8186_sgen_rate_set),
+ SOC_ENUM_EXT("Audio_SineGen_Amplitude", mt8186_afe_sgen_enum[2],
+ mt8186_sgen_amplitude_get, mt8186_sgen_amplitude_set),
+ SOC_SINGLE("Audio_SineGen_Mute_Ch1", AFE_SINEGEN_CON0,
+ MUTE_SW_CH1_MASK_SFT, MUTE_SW_CH1_MASK, 0),
+ SOC_SINGLE("Audio_SineGen_Mute_Ch2", AFE_SINEGEN_CON0,
+ MUTE_SW_CH2_MASK_SFT, MUTE_SW_CH2_MASK, 0),
+ SOC_SINGLE("Audio_SineGen_Freq_Div_Ch1", AFE_SINEGEN_CON0,
+ FREQ_DIV_CH1_SFT, FREQ_DIV_CH1_MASK, 0),
+ SOC_SINGLE("Audio_SineGen_Freq_Div_Ch2", AFE_SINEGEN_CON0,
+ FREQ_DIV_CH2_SFT, FREQ_DIV_CH2_MASK, 0),
+};
+
+int mt8186_add_misc_control(struct snd_soc_component *component)
+{
+ snd_soc_add_component_controls(component,
+ mt8186_afe_sgen_controls,
+ ARRAY_SIZE(mt8186_afe_sgen_controls));
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-common.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-common.c
new file mode 100644
index 000000000000..e325d216c008
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-common.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8186-mt6366-common.c
+// -- MT8186 MT6366 ALSA common driver
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+//
+#include <sound/soc.h>
+
+#include "../../codecs/mt6358.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "mt8186-afe-common.h"
+#include "mt8186-mt6366-common.h"
+
+int mt8186_mt6366_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
+ int ret;
+
+ /* set mtkaif protocol */
+ mt6358_set_mtkaif_protocol(cmpnt_codec,
+ MT6358_MTKAIF_PROTOCOL_1);
+ afe_priv->mtkaif_protocol = MT6358_MTKAIF_PROTOCOL_1;
+
+ ret = snd_soc_dapm_sync(dapm);
+ if (ret) {
+ dev_err(rtd->dev, "failed to snd_soc_dapm_sync\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8186_mt6366_init);
+
+int mt8186_mt6366_card_set_be_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ char *link_name)
+{
+ int ret;
+
+ if (node && strcmp(link->name, link_name) == 0) {
+ ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link);
+ if (ret < 0)
+ return dev_err_probe(card->dev, ret, "get dai link codecs fail\n");
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8186_mt6366_card_set_be_link);
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-common.h b/sound/soc/mediatek/mt8186/mt8186-mt6366-common.h
new file mode 100644
index 000000000000..907d8f5e46b1
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-common.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8186-mt6366-common.h
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT8186_MT6366_COMMON_H_
+#define _MT8186_MT6366_COMMON_H_
+
+int mt8186_mt6366_init(struct snd_soc_pcm_runtime *rtd);
+int mt8186_mt6366_card_set_be_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ char *link_name);
+#endif
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c
new file mode 100644
index 000000000000..2c3033f305ea
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c
@@ -0,0 +1,1377 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8186-mt6366.c
+// -- MT8186-MT6366 ALSA SoC machine driver
+//
+// Copyright (c) 2022 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+//
+// Copyright (c) 2024 Collabora Ltd.
+// AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+//
+
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/rt5682.h>
+#include <sound/soc.h>
+
+#include "../../codecs/da7219.h"
+#include "../../codecs/mt6358.h"
+#include "../../codecs/rt5682.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-dsp-sof-common.h"
+#include "../common/mtk-soc-card.h"
+#include "../common/mtk-soundcard-driver.h"
+#include "mt8186-afe-common.h"
+#include "mt8186-afe-clk.h"
+#include "mt8186-afe-gpio.h"
+#include "mt8186-mt6366-common.h"
+
+#define RT1019_CODEC_DAI "HiFi"
+#define RT1019_DEV0_NAME "rt1019p"
+
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+#define RT5682S_DEV0_NAME "rt5682s.5-001a"
+
+#define DA7219_CODEC_DAI "da7219-hifi"
+#define DA7219_DEV_NAME "da7219.5-001a"
+
+#define SOF_DMA_DL1 "SOF_DMA_DL1"
+#define SOF_DMA_DL2 "SOF_DMA_DL2"
+#define SOF_DMA_UL1 "SOF_DMA_UL1"
+#define SOF_DMA_UL2 "SOF_DMA_UL2"
+
+#define DA7219_CODEC_PRESENT BIT(0)
+
+struct mt8186_mt6366_rt1019_rt5682s_priv {
+ struct gpio_desc *dmic_sel;
+ int dmic_switch;
+};
+
+enum mt8186_jacks {
+ MT8186_JACK_HEADSET,
+ MT8186_JACK_HDMI,
+ MT8186_JACK_MAX,
+};
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mt8186_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_codec_conf mt8186_mt6366_rt1019_rt5682s_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("mt6358-sound"),
+ .name_prefix = "Mt6366",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("bt-sco"),
+ .name_prefix = "Mt8186 bt",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("hdmi-audio-codec"),
+ .name_prefix = "Mt8186 hdmi",
+ },
+};
+
+static int dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct mt8186_mt6366_rt1019_rt5682s_priv *priv = soc_card_data->mach_priv;
+
+ ucontrol->value.integer.value[0] = priv->dmic_switch;
+ return 0;
+}
+
+static int dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct mt8186_mt6366_rt1019_rt5682s_priv *priv = soc_card_data->mach_priv;
+
+ priv->dmic_switch = ucontrol->value.integer.value[0];
+ if (priv->dmic_sel) {
+ gpiod_set_value(priv->dmic_sel, priv->dmic_switch);
+ dev_dbg(card->dev, "dmic_set_value %d\n",
+ priv->dmic_switch);
+ }
+ return 0;
+}
+
+static const char * const dmic_mux_text[] = {
+ "Front Mic",
+ "Rear Mic",
+};
+
+static SOC_ENUM_SINGLE_DECL(mt8186_dmic_enum,
+ SND_SOC_NOPM, 0, dmic_mux_text);
+
+static const struct snd_kcontrol_new mt8186_dmic_mux_control =
+ SOC_DAPM_ENUM_EXT("DMIC Select Mux", mt8186_dmic_enum,
+ dmic_get, dmic_set);
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &mt8186_dmic_mux_control),
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+};
+
+static int primary_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct mt8186_mt6366_rt1019_rt5682s_priv *priv = soc_card_data->mach_priv;
+ int ret;
+
+ ret = mt8186_mt6366_init(rtd);
+
+ if (ret) {
+ dev_err(card->dev, "mt8186_mt6366_init failed: %d\n", ret);
+ return ret;
+ }
+
+ if (!priv->dmic_sel) {
+ dev_dbg(card->dev, "dmic_sel is null\n");
+ return 0;
+ }
+
+ ret = snd_soc_dapm_new_controls(dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, dmic_map,
+ ARRAY_SIZE(dmic_map));
+
+ if (ret)
+ dev_err(card->dev, "DMic map addition failed: %d\n", ret);
+
+ return ret;
+}
+
+static int mt8186_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mtk_soc_card_data *soc_card_data =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8186_JACK_HEADSET];
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ const int hs_keys_rt5682[] = {
+ KEY_PLAYPAUSE, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_VOICECOMMAND
+ };
+ const int hs_keys_da7219[] = {
+ KEY_PLAYPAUSE, KEY_VOICECOMMAND, KEY_VOLUMEUP, KEY_VOLUMEDOWN
+ };
+ const int *hs_keys;
+ int ret;
+ int type;
+
+ ret = mt8186_dai_i2s_set_share(afe, "I2S1", "I2S0");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack, mt8186_jack_pins,
+ ARRAY_SIZE(mt8186_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ if (soc_card_data->card_data->flags & DA7219_CODEC_PRESENT)
+ hs_keys = hs_keys_da7219;
+ else
+ hs_keys = hs_keys_rt5682;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, hs_keys[0]);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, hs_keys[1]);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, hs_keys[2]);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, hs_keys[3]);
+
+ type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ return snd_soc_component_set_jack(cmpnt_codec, jack, (void *)&type);
+}
+
+static int mt8186_da7219_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ unsigned int freq;
+ int ret, j;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to set cpu dai sysclk: %d\n", ret);
+ return ret;
+ }
+
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ if (strcmp(codec_dai->component->name, DA7219_DEV_NAME))
+ continue;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK,
+ mclk_fs, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to set sysclk: %d\n", ret);
+ return ret;
+ }
+
+ if ((rate % 8000) == 0)
+ freq = DA7219_PLL_FREQ_OUT_98304;
+ else
+ freq = DA7219_PLL_FREQ_OUT_90316;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM,
+ 0, freq);
+ if (ret) {
+ dev_err(rtd->dev, "failed to start PLL: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt8186_da7219_i2s_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ int j, ret;
+
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ if (strcmp(codec_dai->component->name, DA7219_DEV_NAME))
+ continue;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to stop PLL: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8186_da7219_i2s_ops = {
+ .hw_params = mt8186_da7219_i2s_hw_params,
+ .hw_free = mt8186_da7219_i2s_hw_free,
+};
+
+static int mt8186_rt5682s_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1,
+ RT5682_PLL1_S_BCLK1,
+ params_rate(params) * 64,
+ params_rate(params) * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT5682_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8186_rt5682s_i2s_ops = {
+ .hw_params = mt8186_rt5682s_i2s_hw_params,
+};
+
+static int mt8186_mt6366_rt1019_rt5682s_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_soc_card_data *soc_card_data =
+ snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8186_JACK_HDMI];
+ int ret;
+
+ ret = mt8186_dai_i2s_set_share(afe, "I2S2", "I2S3");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
+ if (ret) {
+ dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+}
+
+static int mt8186_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params,
+ snd_pcm_format_t fmt)
+{
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ dev_dbg(rtd->dev, "%s(), fix format to %d\n", __func__, fmt);
+
+ /* fix BE i2s channel to 2 channel */
+ channels->min = 2;
+ channels->max = 2;
+
+ /* clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, fmt);
+
+ return 0;
+}
+
+static int mt8186_i2s_hw_params_24le_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ return mt8186_hw_params_fixup(rtd, params, SNDRV_PCM_FORMAT_S24_LE);
+}
+
+static int mt8186_i2s_hw_params_32le_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ return mt8186_hw_params_fixup(rtd, params, SNDRV_PCM_FORMAT_S32_LE);
+}
+
+/* fixup the BE DAI link to match any values from topology */
+static int mt8186_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = mtk_sof_dai_link_fixup(rtd, params);
+
+ if (!strcmp(rtd->dai_link->name, "I2S0") ||
+ !strcmp(rtd->dai_link->name, "I2S1") ||
+ !strcmp(rtd->dai_link->name, "I2S2")) {
+ if (soc_card_data->card_data->flags & DA7219_CODEC_PRESENT)
+ mt8186_i2s_hw_params_32le_fixup(rtd, params);
+ else
+ mt8186_i2s_hw_params_24le_fixup(rtd, params);
+ } else if (!strcmp(rtd->dai_link->name, "I2S3")) {
+ if (soc_card_data->card_data->flags & DA7219_CODEC_PRESENT)
+ mt8186_i2s_hw_params_24le_fixup(rtd, params);
+ else
+ mt8186_i2s_hw_params_32le_fixup(rtd, params);
+ }
+
+ return ret;
+}
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback12,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL12")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback4,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback5,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback6,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback7,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback8,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture4,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture5,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture6,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture7,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* hostless */
+SND_SOC_DAILINK_DEFS(hostless_lpbk,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless LPBK DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_fm,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless FM DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_src1,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_SRC_1_DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_src_bargein,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_SRC_Bargein_DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(adda,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6358-sound",
+ "mt6358-snd-codec-aif1"),
+ COMP_CODEC("dmic-codec",
+ "dmic-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2s0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2s1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2s2,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2s3,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hw_gain1,
+ DAILINK_COMP_ARRAY(COMP_CPU("HW Gain 1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hw_gain2,
+ DAILINK_COMP_ARRAY(COMP_CPU("HW Gain 2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hw_src1,
+ DAILINK_COMP_ARRAY(COMP_CPU("HW_SRC_1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hw_src2,
+ DAILINK_COMP_ARRAY(COMP_CPU("HW_SRC_2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(connsys_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("CONNSYS_I2S")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm-wb")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(tdm_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("TDM IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* hostless */
+SND_SOC_DAILINK_DEFS(hostless_ul1,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_UL1 DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_ul2,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_UL2 DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_ul3,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_UL3 DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_ul5,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_UL5 DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_ul6,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless_UL6 DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_hw_gain_aaudio,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless HW Gain AAudio DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(hostless_src_aaudio,
+ DAILINK_COMP_ARRAY(COMP_CPU("Hostless SRC AAudio DAI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ { "I2S1", "AFE_SOF_DL1", SOF_DMA_DL1, SNDRV_PCM_STREAM_PLAYBACK},
+ { "I2S3", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK},
+ { "Primary Codec", "AFE_SOF_UL1", SOF_DMA_UL1, SNDRV_PCM_STREAM_CAPTURE},
+ { "I2S0", "AFE_SOF_UL2", SOF_DMA_UL2, SNDRV_PCM_STREAM_CAPTURE},
+};
+
+static struct snd_soc_dai_link mt8186_mt6366_rt1019_rt5682s_dai_links[] = {
+ /* Front End DAI links */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_format = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ {
+ .name = "Playback_12",
+ .stream_name = "Playback_12",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback12),
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_format = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_format = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ {
+ .name = "Playback_4",
+ .stream_name = "Playback_4",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback4),
+ },
+ {
+ .name = "Playback_5",
+ .stream_name = "Playback_5",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback5),
+ },
+ {
+ .name = "Playback_6",
+ .stream_name = "Playback_6",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback6),
+ },
+ {
+ .name = "Playback_7",
+ .stream_name = "Playback_7",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback7),
+ },
+ {
+ .name = "Playback_8",
+ .stream_name = "Playback_8",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback8),
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_format = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ {
+ .name = "Capture_4",
+ .stream_name = "Capture_4",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_format = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(capture4),
+ },
+ {
+ .name = "Capture_5",
+ .stream_name = "Capture_5",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture5),
+ },
+ {
+ .name = "Capture_6",
+ .stream_name = "Capture_6",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_format = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ SND_SOC_DAILINK_REG(capture6),
+ },
+ {
+ .name = "Capture_7",
+ .stream_name = "Capture_7",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture7),
+ },
+ {
+ .name = "Hostless_LPBK",
+ .stream_name = "Hostless_LPBK",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_lpbk),
+ },
+ {
+ .name = "Hostless_FM",
+ .stream_name = "Hostless_FM",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_fm),
+ },
+ {
+ .name = "Hostless_SRC_1",
+ .stream_name = "Hostless_SRC_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_src1),
+ },
+ {
+ .name = "Hostless_SRC_Bargein",
+ .stream_name = "Hostless_SRC_Bargein",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_src_bargein),
+ },
+ {
+ .name = "Hostless_HW_Gain_AAudio",
+ .stream_name = "Hostless_HW_Gain_AAudio",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_hw_gain_aaudio),
+ },
+ {
+ .name = "Hostless_SRC_AAudio",
+ .stream_name = "Hostless_SRC_AAudio",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_src_aaudio),
+ },
+ /* Back End DAI links */
+ {
+ .name = "Primary Codec",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ .init = primary_codec_init,
+ SND_SOC_DAILINK_REG(adda),
+ },
+ {
+ .name = "I2S3",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .init = mt8186_mt6366_rt1019_rt5682s_hdmi_init,
+ SND_SOC_DAILINK_REG(i2s3),
+ },
+ {
+ .name = "I2S0",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .ops = &mt8186_rt5682s_i2s_ops,
+ SND_SOC_DAILINK_REG(i2s0),
+ },
+ {
+ .name = "I2S1",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .init = mt8186_headset_codec_init,
+ SND_SOC_DAILINK_REG(i2s1),
+ },
+ {
+ .name = "I2S2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(i2s2),
+ },
+ {
+ .name = "HW Gain 1",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hw_gain1),
+ },
+ {
+ .name = "HW Gain 2",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hw_gain2),
+ },
+ {
+ .name = "HW_SRC_1",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hw_src1),
+ },
+ {
+ .name = "HW_SRC_2",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hw_src2),
+ },
+ {
+ .name = "CONNSYS_I2S",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(connsys_i2s),
+ },
+ {
+ .name = "PCM 1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_IF,
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+ {
+ .name = "TDM IN",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(tdm_in),
+ },
+ /* dummy BE for ul memif to record from dl memif */
+ {
+ .name = "Hostless_UL1",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_ul1),
+ },
+ {
+ .name = "Hostless_UL2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_ul2),
+ },
+ {
+ .name = "Hostless_UL3",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_ul3),
+ },
+ {
+ .name = "Hostless_UL5",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_ul5),
+ },
+ {
+ .name = "Hostless_UL6",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(hostless_ul6),
+ },
+ /* SOF BE */
+ {
+ .name = "AFE_SOF_DL1",
+ .no_pcm = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL1),
+ },
+ {
+ .name = "AFE_SOF_DL2",
+ .no_pcm = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL2),
+ },
+ {
+ .name = "AFE_SOF_UL1",
+ .no_pcm = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL1),
+ },
+ {
+ .name = "AFE_SOF_UL2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL2),
+ },
+};
+
+static const struct snd_soc_dapm_widget
+mt8186_mt6366_da7219_max98357_widgets[] = {
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_LINE("HDMI1", NULL),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget
+mt8186_mt6366_rt1019_rt5682s_widgets[] = {
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_OUTPUT("HDMI1"),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route
+mt8186_mt6366_rt1019_rt5682s_routes[] = {
+ /* SPK */
+ { "Speakers", NULL, "Speaker" },
+ /* Headset */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+ /* HDMI */
+ { "HDMI1", NULL, "TX" },
+ /* SOF Uplink */
+ {SOF_DMA_UL1, NULL, "UL1_CH1"},
+ {SOF_DMA_UL1, NULL, "UL1_CH2"},
+ {SOF_DMA_UL2, NULL, "UL2_CH1"},
+ {SOF_DMA_UL2, NULL, "UL2_CH2"},
+ /* SOF Downlink */
+ {"DSP_DL1_VIRT", NULL, SOF_DMA_DL1},
+ {"DSP_DL2_VIRT", NULL, SOF_DMA_DL2},
+};
+
+static const struct snd_soc_dapm_route mt8186_mt6366_rt5650_routes[] = {
+ /* SPK */
+ {"Speakers", NULL, "SPOL"},
+ {"Speakers", NULL, "SPOR"},
+ /* Headset */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+ { "IN1N", NULL, "Headset Mic"},
+ /* HDMI */
+ { "HDMI1", NULL, "TX" },
+ /* SOF Uplink */
+ {SOF_DMA_UL1, NULL, "UL1_CH1"},
+ {SOF_DMA_UL1, NULL, "UL1_CH2"},
+ {SOF_DMA_UL2, NULL, "UL2_CH1"},
+ {SOF_DMA_UL2, NULL, "UL2_CH2"},
+ /* SOF Downlink */
+ {"DSP_DL1_VIRT", NULL, SOF_DMA_DL1},
+ {"DSP_DL2_VIRT", NULL, SOF_DMA_DL2},
+};
+
+static const struct snd_kcontrol_new mt8186_mt6366_da7219_max98357_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+ SOC_DAPM_PIN_SWITCH("HDMI1"),
+};
+
+static const struct snd_kcontrol_new
+mt8186_mt6366_rt1019_rt5682s_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("HDMI1"),
+};
+
+static struct snd_soc_card mt8186_mt6366_da7219_max98357_soc_card = {
+ .name = "mt8186_da7219_max98357",
+ .owner = THIS_MODULE,
+ .dai_link = mt8186_mt6366_rt1019_rt5682s_dai_links,
+ .num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links),
+ .controls = mt8186_mt6366_da7219_max98357_controls,
+ .num_controls = ARRAY_SIZE(mt8186_mt6366_da7219_max98357_controls),
+ .dapm_widgets = mt8186_mt6366_da7219_max98357_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8186_mt6366_da7219_max98357_widgets),
+ .dapm_routes = mt8186_mt6366_rt1019_rt5682s_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_routes),
+ .codec_conf = mt8186_mt6366_rt1019_rt5682s_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_codec_conf),
+};
+
+static struct snd_soc_card mt8186_mt6366_rt1019_rt5682s_soc_card = {
+ .name = "mt8186_rt1019_rt5682s",
+ .owner = THIS_MODULE,
+ .dai_link = mt8186_mt6366_rt1019_rt5682s_dai_links,
+ .num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links),
+ .controls = mt8186_mt6366_rt1019_rt5682s_controls,
+ .num_controls = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_controls),
+ .dapm_widgets = mt8186_mt6366_rt1019_rt5682s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_widgets),
+ .dapm_routes = mt8186_mt6366_rt1019_rt5682s_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_routes),
+ .codec_conf = mt8186_mt6366_rt1019_rt5682s_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_codec_conf),
+};
+
+static struct snd_soc_card mt8186_mt6366_rt5682s_max98360_soc_card = {
+ .name = "mt8186_rt5682s_max98360",
+ .owner = THIS_MODULE,
+ .dai_link = mt8186_mt6366_rt1019_rt5682s_dai_links,
+ .num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links),
+ .controls = mt8186_mt6366_rt1019_rt5682s_controls,
+ .num_controls = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_controls),
+ .dapm_widgets = mt8186_mt6366_rt1019_rt5682s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_widgets),
+ .dapm_routes = mt8186_mt6366_rt1019_rt5682s_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_routes),
+ .codec_conf = mt8186_mt6366_rt1019_rt5682s_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_codec_conf),
+};
+
+static struct snd_soc_card mt8186_mt6366_rt5650_soc_card = {
+ .name = "mt8186_rt5650",
+ .owner = THIS_MODULE,
+ .dai_link = mt8186_mt6366_rt1019_rt5682s_dai_links,
+ .num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links),
+ .controls = mt8186_mt6366_rt1019_rt5682s_controls,
+ .num_controls = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_controls),
+ .dapm_widgets = mt8186_mt6366_rt1019_rt5682s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_widgets),
+ .dapm_routes = mt8186_mt6366_rt5650_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8186_mt6366_rt5650_routes),
+ .codec_conf = mt8186_mt6366_rt1019_rt5682s_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_codec_conf),
+};
+
+static int mt8186_mt6366_legacy_probe(struct mtk_soc_card_data *soc_card_data)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *dai_link;
+ struct device_node *headset_codec, *playback_codec;
+ int ret, i;
+
+ playback_codec = of_get_child_by_name(dev->of_node, "playback-codecs");
+ if (!playback_codec)
+ return dev_err_probe(dev, -EINVAL,
+ "Property 'playback-codecs' missing or invalid\n");
+
+ headset_codec = of_get_child_by_name(dev->of_node, "headset-codec");
+ if (!headset_codec) {
+ of_node_put(playback_codec);
+ return dev_err_probe(dev, -EINVAL,
+ "Property 'headset-codec' missing or invalid\n");
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ ret = mt8186_mt6366_card_set_be_link(card, dai_link, playback_codec, "I2S3");
+ if (ret) {
+ dev_err_probe(dev, ret, "%s set playback_codec fail\n",
+ dai_link->name);
+ break;
+ }
+
+ ret = mt8186_mt6366_card_set_be_link(card, dai_link, headset_codec, "I2S0");
+ if (ret) {
+ dev_err_probe(dev, ret, "%s set headset_codec fail\n",
+ dai_link->name);
+ break;
+ }
+
+ ret = mt8186_mt6366_card_set_be_link(card, dai_link, headset_codec, "I2S1");
+ if (ret) {
+ dev_err_probe(dev, ret, "%s set headset_codec fail\n",
+ dai_link->name);
+ break;
+ }
+ }
+ of_node_put(headset_codec);
+ of_node_put(playback_codec);
+
+ return ret;
+}
+
+static int mt8186_mt6366_soc_card_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ struct snd_soc_dai_link *dai_link;
+ struct mt8186_mt6366_rt1019_rt5682s_priv *mach_priv;
+ int i, ret;
+
+ mach_priv = devm_kzalloc(card->dev, sizeof(*mach_priv), GFP_KERNEL);
+ if (!mach_priv)
+ return -ENOMEM;
+
+ soc_card_data->mach_priv = mach_priv;
+
+ mach_priv->dmic_sel = devm_gpiod_get_optional(card->dev,
+ "dmic", GPIOD_OUT_LOW);
+ if (IS_ERR(mach_priv->dmic_sel))
+ return dev_err_probe(card->dev, PTR_ERR(mach_priv->dmic_sel),
+ "DMIC gpio failed\n");
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "I2S0") == 0 ||
+ strcmp(dai_link->name, "I2S1") == 0 ||
+ strcmp(dai_link->name, "I2S2") == 0) {
+ if (card_data->flags & DA7219_CODEC_PRESENT) {
+ dai_link->be_hw_params_fixup = mt8186_i2s_hw_params_32le_fixup;
+ dai_link->ops = &mt8186_da7219_i2s_ops;
+ } else {
+ dai_link->be_hw_params_fixup = mt8186_i2s_hw_params_24le_fixup;
+ dai_link->ops = &mt8186_rt5682s_i2s_ops;
+ }
+ } else if (strcmp(dai_link->name, "I2S3") == 0) {
+ if (card_data->flags & DA7219_CODEC_PRESENT)
+ dai_link->be_hw_params_fixup = mt8186_i2s_hw_params_24le_fixup;
+ else
+ dai_link->be_hw_params_fixup = mt8186_i2s_hw_params_32le_fixup;
+ }
+ }
+
+ if (legacy) {
+ ret = mt8186_mt6366_legacy_probe(soc_card_data);
+ if (ret)
+ return ret;
+ }
+
+ ret = mt8186_afe_gpio_init(card->dev);
+ if (ret)
+ return dev_err_probe(card->dev, ret, "init AFE gpio error\n");
+
+ return 0;
+}
+
+static const unsigned int mt8186_pcm_playback_channels[] = { 2 };
+static const unsigned int mt8186_pcm_capture_channels[] = { 1, 2 };
+static const unsigned int mt8186_pcm_rates[] = { 48000 };
+
+static const struct snd_pcm_hw_constraint_list mt8186_rate_constraint = {
+ .list = mt8186_pcm_rates,
+ .count = ARRAY_SIZE(mt8186_pcm_rates)
+};
+
+static const struct mtk_pcm_constraints_data mt8186_pcm_constraints[MTK_CONSTRAINT_CAPTURE + 1] = {
+ [MTK_CONSTRAINT_PLAYBACK] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8186_pcm_playback_channels,
+ .count = ARRAY_SIZE(mt8186_pcm_playback_channels)
+ },
+ .rates = &mt8186_rate_constraint,
+ },
+ [MTK_CONSTRAINT_CAPTURE] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8186_pcm_capture_channels,
+ .count = ARRAY_SIZE(mt8186_pcm_capture_channels)
+ },
+ .rates = &mt8186_rate_constraint,
+ }
+};
+
+static const struct mtk_sof_priv mt8186_sof_priv = {
+ .conn_streams = g_sof_conn_streams,
+ .num_streams = ARRAY_SIZE(g_sof_conn_streams),
+ .sof_dai_link_fixup = mt8186_sof_dai_link_fixup
+};
+
+static const struct mtk_soundcard_pdata mt8186_mt6366_da7219_max98357_pdata = {
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8186_mt6366_da7219_max98357_soc_card,
+ .num_jacks = MT8186_JACK_MAX,
+ .pcm_constraints = mt8186_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8186_pcm_constraints),
+ .flags = DA7219_CODEC_PRESENT,
+ },
+ .sof_priv = &mt8186_sof_priv,
+ .soc_probe = mt8186_mt6366_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8186_mt6366_rt1019_rt5682s_pdata = {
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8186_mt6366_rt1019_rt5682s_soc_card,
+ .num_jacks = MT8186_JACK_MAX,
+ .pcm_constraints = mt8186_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8186_pcm_constraints),
+ },
+ .sof_priv = &mt8186_sof_priv,
+ .soc_probe = mt8186_mt6366_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8186_mt6366_rt5682s_max98360_pdata = {
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8186_mt6366_rt5682s_max98360_soc_card,
+ .num_jacks = MT8186_JACK_MAX,
+ .pcm_constraints = mt8186_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8186_pcm_constraints),
+ },
+ .sof_priv = &mt8186_sof_priv,
+ .soc_probe = mt8186_mt6366_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8186_mt6366_rt5650_pdata = {
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8186_mt6366_rt5650_soc_card,
+ .num_jacks = MT8186_JACK_MAX,
+ .pcm_constraints = mt8186_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8186_pcm_constraints),
+ },
+ .sof_priv = &mt8186_sof_priv,
+ .soc_probe = mt8186_mt6366_soc_card_probe
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id mt8186_mt6366_dt_match[] = {
+ {
+ .compatible = "mediatek,mt8186-mt6366-rt1019-rt5682s-sound",
+ .data = &mt8186_mt6366_rt1019_rt5682s_pdata,
+ },
+ {
+ .compatible = "mediatek,mt8186-mt6366-rt5682s-max98360-sound",
+ .data = &mt8186_mt6366_rt5682s_max98360_pdata,
+ },
+ {
+ .compatible = "mediatek,mt8186-mt6366-rt5650-sound",
+ .data = &mt8186_mt6366_rt5650_pdata,
+ },
+ {
+ .compatible = "mediatek,mt8186-mt6366-da7219-max98357-sound",
+ .data = &mt8186_mt6366_da7219_max98357_pdata,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mt8186_mt6366_dt_match);
+#endif
+
+static struct platform_driver mt8186_mt6366_driver = {
+ .driver = {
+ .name = "mt8186_mt6366",
+#if IS_ENABLED(CONFIG_OF)
+ .of_match_table = mt8186_mt6366_dt_match,
+#endif
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+
+module_platform_driver(mt8186_mt6366_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8186-MT6366 ALSA SoC machine driver");
+MODULE_AUTHOR("Jiaxin Yu <jiaxin.yu@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt8186_mt6366 soc card");
diff --git a/sound/soc/mediatek/mt8186/mt8186-reg.h b/sound/soc/mediatek/mt8186/mt8186-reg.h
new file mode 100644
index 000000000000..53c3eb7283d8
--- /dev/null
+++ b/sound/soc/mediatek/mt8186/mt8186-reg.h
@@ -0,0 +1,2913 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * mt8186-reg.h -- Mediatek 8186 audio driver reg definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+ */
+
+#ifndef _MT8186_REG_H_
+#define _MT8186_REG_H_
+
+/* reg bit enum */
+enum {
+ MT8186_MEMIF_PBUF_SIZE_32_BYTES,
+ MT8186_MEMIF_PBUF_SIZE_64_BYTES,
+ MT8186_MEMIF_PBUF_SIZE_128_BYTES,
+ MT8186_MEMIF_PBUF_SIZE_256_BYTES,
+ MT8186_MEMIF_PBUF_SIZE_NUM,
+};
+
+/*****************************************************************************
+ * R E G I S T E R D E F I N I T I O N
+ *****************************************************************************/
+/* AUDIO_TOP_CON0 */
+#define RESERVED_SFT 31
+#define RESERVED_MASK_SFT BIT(31)
+#define AHB_IDLE_EN_INT_SFT 30
+#define AHB_IDLE_EN_INT_MASK_SFT BIT(30)
+#define AHB_IDLE_EN_EXT_SFT 29
+#define AHB_IDLE_EN_EXT_MASK_SFT BIT(29)
+#define PDN_NLE_SFT 28
+#define PDN_NLE_MASK_SFT BIT(28)
+#define PDN_TML_SFT 27
+#define PDN_TML_MASK_SFT BIT(27)
+#define PDN_DAC_PREDIS_SFT 26
+#define PDN_DAC_PREDIS_MASK_SFT BIT(26)
+#define PDN_DAC_SFT 25
+#define PDN_DAC_MASK_SFT BIT(25)
+#define PDN_ADC_SFT 24
+#define PDN_ADC_MASK_SFT BIT(24)
+#define PDN_TDM_CK_SFT 20
+#define PDN_TDM_CK_MASK_SFT BIT(20)
+#define PDN_APLL_TUNER_SFT 19
+#define PDN_APLL_TUNER_MASK_SFT BIT(19)
+#define PDN_APLL2_TUNER_SFT 18
+#define PDN_APLL2_TUNER_MASK_SFT BIT(18)
+#define APB3_SEL_SFT 14
+#define APB3_SEL_MASK_SFT BIT(14)
+#define APB_R2T_SFT 13
+#define APB_R2T_MASK_SFT BIT(13)
+#define APB_W2T_SFT 12
+#define APB_W2T_MASK_SFT BIT(12)
+#define PDN_24M_SFT 9
+#define PDN_24M_MASK_SFT BIT(9)
+#define PDN_22M_SFT 8
+#define PDN_22M_MASK_SFT BIT(8)
+#define PDN_AFE_SFT 2
+#define PDN_AFE_MASK_SFT BIT(2)
+
+/* AUDIO_TOP_CON1 */
+#define PDN_3RD_DAC_HIRES_SFT 31
+#define PDN_3RD_DAC_HIRES_MASK_SFT BIT(31)
+#define PDN_3RD_DAC_TML_SFT 30
+#define PDN_3RD_DAC_TML_MASK_SFT BIT(30)
+#define PDN_3RD_DAC_PREDIS_SFT 29
+#define PDN_3RD_DAC_PREDIS_MASK_SFT BIT(29)
+#define PDN_3RD_DAC_SFT 28
+#define PDN_3RD_DAC_MASK_SFT BIT(28)
+#define I2S_SOFT_RST5_SFT 22
+#define I2S_SOFT_RST5_MASK_SFT BIT(22)
+#define PDN_ADDA6_ADC_HIRES_SFT 21
+#define PDN_ADDA6_ADC_HIRES_MASK_SFT BIT(21)
+#define PDN_ADDA6_ADC_SFT 20
+#define PDN_ADDA6_ADC_MASK_SFT BIT(20)
+#define PDN_ADC_HIRES_TML_SFT 17
+#define PDN_ADC_HIRES_TML_MASK_SFT BIT(17)
+#define PDN_ADC_HIRES_SFT 16
+#define PDN_ADC_HIRES_MASK_SFT BIT(16)
+#define PDN_DAC_HIRES_SFT 15
+#define PDN_DAC_HIRES_MASK_SFT BIT(15)
+#define PDN_GENERAL2_ASRC_SFT 14
+#define PDN_GENERAL2_ASRC_MASK_SFT BIT(14)
+#define PDN_GENERAL1_ASRC_SFT 13
+#define PDN_GENERAL1_ASRC_MASK_SFT BIT(13)
+#define PDN_CONNSYS_I2S_ASRC_SFT 12
+#define PDN_CONNSYS_I2S_ASRC_MASK_SFT BIT(12)
+#define I2S4_BCLK_SW_CG_SFT 7
+#define I2S4_BCLK_SW_CG_MASK_SFT BIT(7)
+#define I2S3_BCLK_SW_CG_SFT 6
+#define I2S3_BCLK_SW_CG_MASK_SFT BIT(6)
+#define I2S2_BCLK_SW_CG_SFT 5
+#define I2S2_BCLK_SW_CG_MASK_SFT BIT(5)
+#define I2S1_BCLK_SW_CG_SFT 4
+#define I2S1_BCLK_SW_CG_MASK_SFT BIT(4)
+#define I2S_SOFT_RST2_SFT 2
+#define I2S_SOFT_RST2_MASK_SFT BIT(2)
+#define I2S_SOFT_RST_SFT 1
+#define I2S_SOFT_RST_MASK_SFT BIT(1)
+
+/* AUDIO_TOP_CON3 */
+#define BUSY_SFT 31
+#define BUSY_MASK_SFT BIT(31)
+#define OS_DISABLE_SFT 30
+#define OS_DISABLE_MASK_SFT BIT(30)
+#define CG_DISABLE_SFT 29
+#define CG_DISABLE_MASK_SFT BIT(29)
+#define CLEAR_FLAG_SFT 0
+#define CLEAR_FLAG_MASK_SFT BIT(0)
+
+/* AFE_DAC_CON0 */
+#define VUL12_ON_SFT 31
+#define VUL12_ON_MASK_SFT BIT(31)
+#define MOD_DAI_ON_SFT 30
+#define MOD_DAI_ON_MASK_SFT BIT(30)
+#define DAI_ON_SFT 29
+#define DAI_ON_MASK_SFT BIT(29)
+#define DAI2_ON_SFT 28
+#define DAI2_ON_MASK_SFT BIT(28)
+#define VUL6_ON_SFT 23
+#define VUL6_ON_MASK_SFT BIT(23)
+#define VUL5_ON_SFT 22
+#define VUL5_ON_MASK_SFT BIT(22)
+#define VUL4_ON_SFT 21
+#define VUL4_ON_MASK_SFT BIT(21)
+#define VUL3_ON_SFT 20
+#define VUL3_ON_MASK_SFT BIT(20)
+#define VUL2_ON_SFT 19
+#define VUL2_ON_MASK_SFT BIT(19)
+#define VUL_ON_SFT 18
+#define VUL_ON_MASK_SFT BIT(18)
+#define AWB2_ON_SFT 17
+#define AWB2_ON_MASK_SFT BIT(17)
+#define AWB_ON_SFT 16
+#define AWB_ON_MASK_SFT BIT(16)
+#define DL12_ON_SFT 15
+#define DL12_ON_MASK_SFT BIT(15)
+#define DL8_ON_SFT 11
+#define DL8_ON_MASK_SFT BIT(11)
+#define DL7_ON_SFT 10
+#define DL7_ON_MASK_SFT BIT(10)
+#define DL6_ON_SFT 9
+#define DL6_ON_MASK_SFT BIT(9)
+#define DL5_ON_SFT 8
+#define DL5_ON_MASK_SFT BIT(8)
+#define DL4_ON_SFT 7
+#define DL4_ON_MASK_SFT BIT(7)
+#define DL3_ON_SFT 6
+#define DL3_ON_MASK_SFT BIT(6)
+#define DL2_ON_SFT 5
+#define DL2_ON_MASK_SFT BIT(5)
+#define DL1_ON_SFT 4
+#define DL1_ON_MASK_SFT BIT(4)
+#define AUDIO_AFE_ON_SFT 0
+#define AUDIO_AFE_ON_MASK_SFT BIT(0)
+
+/* AFE_DAC_MON */
+#define AFE_ON_RETM_SFT 0
+#define AFE_ON_RETM_MASK_SFT BIT(0)
+
+/* AFE_I2S_CON */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK_SFT BIT(30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK_SFT BIT(29)
+#define I2SIN_PAD_SEL_SFT 28
+#define I2SIN_PAD_SEL_MASK_SFT BIT(28)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK_SFT BIT(20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT BIT(17)
+#define I2S1_HD_EN_SFT 12
+#define I2S1_HD_EN_MASK_SFT BIT(12)
+#define I2S_OUT_MODE_SFT 8
+#define I2S_OUT_MODE_MASK_SFT GENMASK(11, 8)
+#define INV_PAD_CTRL_SFT 7
+#define INV_PAD_CTRL_MASK_SFT BIT(7)
+#define I2S_BYPSRC_SFT 6
+#define I2S_BYPSRC_MASK_SFT BIT(6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK_SFT BIT(5)
+#define I2S_FMT_SFT 3
+#define I2S_FMT_MASK_SFT BIT(3)
+#define I2S_SRC_SFT 2
+#define I2S_SRC_MASK_SFT BIT(2)
+#define I2S_WLEN_SFT 1
+#define I2S_WLEN_MASK_SFT BIT(1)
+#define I2S_EN_SFT 0
+#define I2S_EN_MASK_SFT BIT(0)
+
+/* AFE_I2S_CON1 */
+#define I2S2_LR_SWAP_SFT 31
+#define I2S2_LR_SWAP_MASK_SFT BIT(31)
+#define I2S2_SEL_O19_O20_SFT 18
+#define I2S2_SEL_O19_O20_MASK_SFT BIT(18)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT BIT(17)
+#define I2S2_SEL_O03_O04_SFT 16
+#define I2S2_SEL_O03_O04_MASK_SFT BIT(16)
+#define I2S2_HD_EN_SFT 12
+#define I2S2_HD_EN_MASK_SFT BIT(12)
+#define I2S2_OUT_MODE_SFT 8
+#define I2S2_OUT_MODE_MASK_SFT GENMASK(11, 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK_SFT BIT(5)
+#define I2S2_FMT_SFT 3
+#define I2S2_FMT_MASK_SFT BIT(3)
+#define I2S2_WLEN_SFT 1
+#define I2S2_WLEN_MASK_SFT BIT(1)
+#define I2S2_EN_SFT 0
+#define I2S2_EN_MASK_SFT BIT(0)
+
+/* AFE_I2S_CON2 */
+#define I2S3_LR_SWAP_SFT 31
+#define I2S3_LR_SWAP_MASK_SFT BIT(31)
+#define I2S3_UPDATE_WORD_SFT 24
+#define I2S3_UPDATE_WORD_MASK_SFT GENMASK(28, 24)
+#define I2S3_BCK_INV_SFT 23
+#define I2S3_BCK_INV_MASK_SFT BIT(23)
+#define I2S3_FPGA_BIT_TEST_SFT 22
+#define I2S3_FPGA_BIT_TEST_MASK_SFT BIT(22)
+#define I2S3_FPGA_BIT_SFT 21
+#define I2S3_FPGA_BIT_MASK_SFT BIT(21)
+#define I2S3_LOOPBACK_SFT 20
+#define I2S3_LOOPBACK_MASK_SFT BIT(20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT BIT(17)
+#define I2S3_HD_EN_SFT 12
+#define I2S3_HD_EN_MASK_SFT BIT(12)
+#define I2S3_OUT_MODE_SFT 8
+#define I2S3_OUT_MODE_MASK_SFT GENMASK(11, 8)
+#define I2S3_FMT_SFT 3
+#define I2S3_FMT_MASK_SFT BIT(3)
+#define I2S3_WLEN_SFT 1
+#define I2S3_WLEN_MASK_SFT BIT(1)
+#define I2S3_EN_SFT 0
+#define I2S3_EN_MASK_SFT BIT(0)
+
+/* AFE_I2S_CON3 */
+#define I2S4_LR_SWAP_SFT 31
+#define I2S4_LR_SWAP_MASK_SFT BIT(31)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT BIT(17)
+#define I2S4_HD_EN_SFT 12
+#define I2S4_HD_EN_MASK_SFT BIT(12)
+#define I2S4_OUT_MODE_SFT 8
+#define I2S4_OUT_MODE_MASK_SFT GENMASK(11, 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK_SFT BIT(5)
+#define I2S4_FMT_SFT 3
+#define I2S4_FMT_MASK_SFT BIT(3)
+#define I2S4_WLEN_SFT 1
+#define I2S4_WLEN_MASK_SFT BIT(1)
+#define I2S4_EN_SFT 0
+#define I2S4_EN_MASK_SFT BIT(0)
+
+/* AFE_I2S_CON4 */
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT BIT(20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT BIT(17)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT BIT(5)
+
+/* AFE_CONNSYS_I2S_CON */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK_SFT BIT(30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK_SFT BIT(29)
+#define I2SIN_PAD_SEL_SFT 28
+#define I2SIN_PAD_SEL_MASK_SFT BIT(28)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK_SFT BIT(20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT BIT(17)
+#define I2S_MODE_SFT 8
+#define I2S_MODE_MASK_SFT GENMASK(11, 8)
+#define INV_PAD_CTRL_SFT 7
+#define INV_PAD_CTRL_MASK_SFT BIT(7)
+#define I2S_BYPSRC_SFT 6
+#define I2S_BYPSRC_MASK_SFT BIT(6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK_SFT BIT(5)
+#define I2S_FMT_SFT 3
+#define I2S_FMT_MASK_SFT BIT(3)
+#define I2S_SRC_SFT 2
+#define I2S_SRC_MASK_SFT BIT(2)
+#define I2S_WLEN_SFT 1
+#define I2S_WLEN_MASK_SFT BIT(1)
+#define I2S_EN_SFT 0
+#define I2S_EN_MASK_SFT BIT(0)
+
+/* AFE_ASRC_2CH_CON2 */
+#define CHSET_O16BIT_SFT 19
+#define CHSET_O16BIT_MASK_SFT BIT(19)
+#define CHSET_CLR_IIR_HISTORY_SFT 17
+#define CHSET_CLR_IIR_HISTORY_MASK_SFT BIT(17)
+#define CHSET_IS_MONO_SFT 16
+#define CHSET_IS_MONO_MASK_SFT BIT(16)
+#define CHSET_IIR_EN_SFT 11
+#define CHSET_IIR_EN_MASK_SFT BIT(11)
+#define CHSET_IIR_STAGE_SFT 8
+#define CHSET_IIR_STAGE_MASK_SFT GENMASK(10, 8)
+#define CHSET_STR_CLR_SFT 5
+#define CHSET_STR_CLR_MASK_SFT BIT(5)
+#define CHSET_ON_SFT 2
+#define CHSET_ON_MASK_SFT BIT(2)
+#define COEFF_SRAM_CTRL_SFT 1
+#define COEFF_SRAM_CTRL_MASK_SFT BIT(1)
+#define ASM_ON_SFT 0
+#define ASM_ON_MASK_SFT BIT(0)
+
+/* AFE_GAIN1_CON0 */
+#define GAIN1_SAMPLE_PER_STEP_SFT 8
+#define GAIN1_SAMPLE_PER_STEP_MASK_SFT GENMASK(15, 8)
+#define GAIN1_MODE_SFT 4
+#define GAIN1_MODE_MASK_SFT GENMASK(7, 4)
+#define GAIN1_ON_SFT 0
+#define GAIN1_ON_MASK_SFT BIT(0)
+
+/* AFE_GAIN1_CON1 */
+#define GAIN1_TARGET_SFT 0
+#define GAIN1_TARGET_MASK 0xfffffff
+#define GAIN1_TARGET_MASK_SFT GENMASK(27, 0)
+
+/* AFE_GAIN2_CON0 */
+#define GAIN2_SAMPLE_PER_STEP_SFT 8
+#define GAIN2_SAMPLE_PER_STEP_MASK_SFT GENMASK(15, 8)
+#define GAIN2_MODE_SFT 4
+#define GAIN2_MODE_MASK_SFT GENMASK(7, 4)
+#define GAIN2_ON_SFT 0
+#define GAIN2_ON_MASK_SFT BIT(0)
+
+/* AFE_GAIN2_CON1 */
+#define GAIN2_TARGET_SFT 0
+#define GAIN2_TARGET_MASK 0xfffffff
+#define GAIN2_TARGET_MASK_SFT GENMASK(27, 0)
+
+/* AFE_GAIN1_CUR */
+#define AFE_GAIN1_CUR_SFT 0
+#define AFE_GAIN1_CUR_MASK_SFT GENMASK(27, 0)
+
+/* AFE_GAIN2_CUR */
+#define AFE_GAIN2_CUR_SFT 0
+#define AFE_GAIN2_CUR_MASK_SFT GENMASK(27, 0)
+
+/* PCM_INTF_CON1 */
+#define PCM_FIX_VALUE_SEL_SFT 31
+#define PCM_FIX_VALUE_SEL_MASK_SFT BIT(31)
+#define PCM_BUFFER_LOOPBACK_SFT 30
+#define PCM_BUFFER_LOOPBACK_MASK_SFT BIT(30)
+#define PCM_PARALLEL_LOOPBACK_SFT 29
+#define PCM_PARALLEL_LOOPBACK_MASK_SFT BIT(29)
+#define PCM_SERIAL_LOOPBACK_SFT 28
+#define PCM_SERIAL_LOOPBACK_MASK_SFT BIT(28)
+#define PCM_DAI_PCM_LOOPBACK_SFT 27
+#define PCM_DAI_PCM_LOOPBACK_MASK_SFT BIT(27)
+#define PCM_I2S_PCM_LOOPBACK_SFT 26
+#define PCM_I2S_PCM_LOOPBACK_MASK_SFT BIT(26)
+#define PCM_SYNC_DELSEL_SFT 25
+#define PCM_SYNC_DELSEL_MASK_SFT BIT(25)
+#define PCM_TX_LR_SWAP_SFT 24
+#define PCM_TX_LR_SWAP_MASK_SFT BIT(24)
+#define PCM_SYNC_OUT_INV_SFT 23
+#define PCM_SYNC_OUT_INV_MASK_SFT BIT(23)
+#define PCM_BCLK_OUT_INV_SFT 22
+#define PCM_BCLK_OUT_INV_MASK_SFT BIT(22)
+#define PCM_SYNC_IN_INV_SFT 21
+#define PCM_SYNC_IN_INV_MASK_SFT BIT(21)
+#define PCM_BCLK_IN_INV_SFT 20
+#define PCM_BCLK_IN_INV_MASK_SFT BIT(20)
+#define PCM_TX_LCH_RPT_SFT 19
+#define PCM_TX_LCH_RPT_MASK_SFT BIT(19)
+#define PCM_VBT_16K_MODE_SFT 18
+#define PCM_VBT_16K_MODE_MASK_SFT BIT(18)
+#define PCM_EXT_MODEM_SFT 17
+#define PCM_EXT_MODEM_MASK_SFT BIT(17)
+#define PCM_24BIT_SFT 16
+#define PCM_24BIT_MASK_SFT BIT(16)
+#define PCM_WLEN_SFT 14
+#define PCM_WLEN_MASK_SFT GENMASK(15, 14)
+#define PCM_SYNC_LENGTH_SFT 9
+#define PCM_SYNC_LENGTH_MASK_SFT GENMASK(13, 9)
+#define PCM_SYNC_TYPE_SFT 8
+#define PCM_SYNC_TYPE_MASK_SFT BIT(8)
+#define PCM_BT_MODE_SFT 7
+#define PCM_BT_MODE_MASK_SFT BIT(7)
+#define PCM_BYP_ASRC_SFT 6
+#define PCM_BYP_ASRC_MASK_SFT BIT(6)
+#define PCM_SLAVE_SFT 5
+#define PCM_SLAVE_MASK_SFT BIT(5)
+#define PCM_MODE_SFT 3
+#define PCM_MODE_MASK_SFT GENMASK(4, 3)
+#define PCM_FMT_SFT 1
+#define PCM_FMT_MASK_SFT GENMASK(2, 1)
+#define PCM_EN_SFT 0
+#define PCM_EN_MASK_SFT BIT(0)
+
+/* PCM_INTF_CON2 */
+#define PCM1_TX_FIFO_OV_SFT 31
+#define PCM1_TX_FIFO_OV_MASK_SFT BIT(31)
+#define PCM1_RX_FIFO_OV_SFT 30
+#define PCM1_RX_FIFO_OV_MASK_SFT BIT(30)
+#define PCM2_TX_FIFO_OV_SFT 29
+#define PCM2_TX_FIFO_OV_MASK_SFT BIT(29)
+#define PCM2_RX_FIFO_OV_SFT 28
+#define PCM2_RX_FIFO_OV_MASK_SFT BIT(28)
+#define PCM1_SYNC_GLITCH_SFT 27
+#define PCM1_SYNC_GLITCH_MASK_SFT BIT(27)
+#define PCM2_SYNC_GLITCH_SFT 26
+#define PCM2_SYNC_GLITCH_MASK_SFT BIT(26)
+#define TX3_RCH_DBG_MODE_SFT 17
+#define TX3_RCH_DBG_MODE_MASK_SFT BIT(17)
+#define PCM1_PCM2_LOOPBACK_SFT 16
+#define PCM1_PCM2_LOOPBACK_MASK_SFT BIT(16)
+#define DAI_PCM_LOOPBACK_CH_SFT 14
+#define DAI_PCM_LOOPBACK_CH_MASK_SFT GENMASK(15, 14)
+#define I2S_PCM_LOOPBACK_CH_SFT 12
+#define I2S_PCM_LOOPBACK_CH_MASK_SFT GENMASK(13, 12)
+#define TX_FIX_VALUE_SFT 0
+#define TX_FIX_VALUE_MASK_SFT GENMASK(7, 0)
+
+/* PCM2_INTF_CON */
+#define PCM2_TX_FIX_VALUE_SFT 24
+#define PCM2_TX_FIX_VALUE_MASK_SFT GENMASK(31, 24)
+#define PCM2_FIX_VALUE_SEL_SFT 23
+#define PCM2_FIX_VALUE_SEL_MASK_SFT BIT(23)
+#define PCM2_BUFFER_LOOPBACK_SFT 22
+#define PCM2_BUFFER_LOOPBACK_MASK_SFT BIT(22)
+#define PCM2_PARALLEL_LOOPBACK_SFT 21
+#define PCM2_PARALLEL_LOOPBACK_MASK_SFT BIT(21)
+#define PCM2_SERIAL_LOOPBACK_SFT 20
+#define PCM2_SERIAL_LOOPBACK_MASK_SFT BIT(20)
+#define PCM2_DAI_PCM_LOOPBACK_SFT 19
+#define PCM2_DAI_PCM_LOOPBACK_MASK_SFT BIT(19)
+#define PCM2_I2S_PCM_LOOPBACK_SFT 18
+#define PCM2_I2S_PCM_LOOPBACK_MASK_SFT BIT(18)
+#define PCM2_SYNC_DELSEL_SFT 17
+#define PCM2_SYNC_DELSEL_MASK_SFT BIT(17)
+#define PCM2_TX_LR_SWAP_SFT 16
+#define PCM2_TX_LR_SWAP_MASK_SFT BIT(16)
+#define PCM2_SYNC_IN_INV_SFT 15
+#define PCM2_SYNC_IN_INV_MASK_SFT BIT(15)
+#define PCM2_BCLK_IN_INV_SFT 14
+#define PCM2_BCLK_IN_INV_MASK_SFT BIT(14)
+#define PCM2_TX_LCH_RPT_SFT 13
+#define PCM2_TX_LCH_RPT_MASK_SFT BIT(13)
+#define PCM2_VBT_16K_MODE_SFT 12
+#define PCM2_VBT_16K_MODE_MASK_SFT BIT(12)
+#define PCM2_LOOPBACK_CH_SEL_SFT 10
+#define PCM2_LOOPBACK_CH_SEL_MASK_SFT GENMASK(11, 10)
+#define PCM2_TX2_BT_MODE_SFT 8
+#define PCM2_TX2_BT_MODE_MASK_SFT BIT(8)
+#define PCM2_BT_MODE_SFT 7
+#define PCM2_BT_MODE_MASK_SFT BIT(7)
+#define PCM2_AFIFO_SFT 6
+#define PCM2_AFIFO_MASK_SFT BIT(6)
+#define PCM2_WLEN_SFT 5
+#define PCM2_WLEN_MASK_SFT BIT(5)
+#define PCM2_MODE_SFT 3
+#define PCM2_MODE_MASK_SFT GENMASK(4, 3)
+#define PCM2_FMT_SFT 1
+#define PCM2_FMT_MASK_SFT GENMASK(2, 1)
+#define PCM2_EN_SFT 0
+#define PCM2_EN_MASK_SFT BIT(0)
+
+// AFE_CM1_CON
+#define CHANNEL_MERGE0_DEBUG_MODE_SFT (31)
+#define CHANNEL_MERGE0_DEBUG_MODE_MASK_SFT BIT(31)
+#define VUL3_BYPASS_CM_SFT (30)
+#define VUL3_BYPASS_CM_MASK (0x1)
+#define VUL3_BYPASS_CM_MASK_SFT BIT(30)
+#define CM1_DEBUG_MODE_SEL_SFT (29)
+#define CM1_DEBUG_MODE_SEL_MASK_SFT BIT(29)
+#define CHANNEL_MERGE0_UPDATE_CNT_SFT (16)
+#define CHANNEL_MERGE0_UPDATE_CNT_MASK_SFT GENMASK(28, 16)
+#define CM1_FS_SELECT_SFT (8)
+#define CM1_FS_SELECT_MASK_SFT GENMASK(12, 8)
+#define CHANNEL_MERGE0_CHNUM_SFT (3)
+#define CHANNEL_MERGE0_CHNUM_MASK_SFT GENMASK(7, 3)
+#define CHANNEL_MERGE0_BYTE_SWAP_SFT (1)
+#define CHANNEL_MERGE0_BYTE_SWAP_MASK_SFT BIT(1)
+#define CHANNEL_MERGE0_EN_SFT (0)
+#define CHANNEL_MERGE0_EN_MASK_SFT BIT(0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define MTKAIF_RXIF_CLKINV_ADC_SFT 31
+#define MTKAIF_RXIF_CLKINV_ADC_MASK_SFT BIT(31)
+#define MTKAIF_RXIF_BYPASS_SRC_SFT 17
+#define MTKAIF_RXIF_BYPASS_SRC_MASK_SFT BIT(17)
+#define MTKAIF_RXIF_PROTOCOL2_SFT 16
+#define MTKAIF_RXIF_PROTOCOL2_MASK_SFT BIT(16)
+#define MTKAIF_TXIF_BYPASS_SRC_SFT 5
+#define MTKAIF_TXIF_BYPASS_SRC_MASK_SFT BIT(5)
+#define MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define MTKAIF_TXIF_PROTOCOL2_MASK_SFT BIT(4)
+#define MTKAIF_TXIF_8TO5_SFT 2
+#define MTKAIF_TXIF_8TO5_MASK_SFT BIT(2)
+#define MTKAIF_RXIF_8TO5_SFT 1
+#define MTKAIF_RXIF_8TO5_MASK_SFT BIT(1)
+#define MTKAIF_IF_LOOPBACK1_SFT 0
+#define MTKAIF_IF_LOOPBACK1_MASK_SFT BIT(0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 16
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT BIT(16)
+#define MTKAIF_RXIF_DELAY_CYCLE_SFT 12
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT GENMASK(15, 12)
+#define MTKAIF_RXIF_DELAY_DATA_SFT 8
+#define MTKAIF_RXIF_DELAY_DATA_MASK 0x1
+#define MTKAIF_RXIF_DELAY_DATA_MASK_SFT BIT(8)
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT GENMASK(6, 4)
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL_SFT 28
+#define DL_2_INPUT_MODE_CTL_MASK_SFT GENMASK(31, 28)
+#define DL_2_CH1_SATURATION_EN_CTL_SFT 27
+#define DL_2_CH1_SATURATION_EN_CTL_MASK_SFT BIT(27)
+#define DL_2_CH2_SATURATION_EN_CTL_SFT 26
+#define DL_2_CH2_SATURATION_EN_CTL_MASK_SFT BIT(26)
+#define DL_2_OUTPUT_SEL_CTL_SFT 24
+#define DL_2_OUTPUT_SEL_CTL_MASK_SFT GENMASK(25, 24)
+#define DL_2_FADEIN_0START_EN_SFT 16
+#define DL_2_FADEIN_0START_EN_MASK_SFT GENMASK(17, 16)
+#define DL_DISABLE_HW_CG_CTL_SFT 15
+#define DL_DISABLE_HW_CG_CTL_MASK_SFT BIT(15)
+#define C_DATA_EN_SEL_CTL_PRE_SFT 14
+#define C_DATA_EN_SEL_CTL_PRE_MASK_SFT BIT(14)
+#define DL_2_SIDE_TONE_ON_CTL_PRE_SFT 13
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK_SFT BIT(13)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_SFT 12
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK_SFT BIT(12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_SFT 11
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK_SFT BIT(11)
+#define DL2_ARAMPSP_CTL_PRE_SFT 9
+#define DL2_ARAMPSP_CTL_PRE_MASK_SFT GENMASK(10, 9)
+#define DL_2_IIRMODE_CTL_PRE_SFT 6
+#define DL_2_IIRMODE_CTL_PRE_MASK_SFT GENMASK(8, 6)
+#define DL_2_VOICE_MODE_CTL_PRE_SFT 5
+#define DL_2_VOICE_MODE_CTL_PRE_MASK_SFT BIT(5)
+#define D2_2_MUTE_CH1_ON_CTL_PRE_SFT 4
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK_SFT BIT(4)
+#define D2_2_MUTE_CH2_ON_CTL_PRE_SFT 3
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK_SFT BIT(3)
+#define DL_2_IIR_ON_CTL_PRE_SFT 2
+#define DL_2_IIR_ON_CTL_PRE_MASK_SFT BIT(2)
+#define DL_2_GAIN_ON_CTL_PRE_SFT 1
+#define DL_2_GAIN_ON_CTL_PRE_MASK_SFT BIT(1)
+#define DL_2_SRC_ON_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_CTL_PRE_MASK_SFT BIT(0)
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE_SFT 16
+#define DL_2_GAIN_CTL_PRE_MASK 0xffff
+#define DL_2_GAIN_CTL_PRE_MASK_SFT GENMASK(31, 16)
+#define DL_2_GAIN_MODE_CTL_SFT 0
+#define DL_2_GAIN_MODE_CTL_MASK_SFT BIT(0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define ULCF_CFG_EN_CTL_SFT 31
+#define ULCF_CFG_EN_CTL_MASK_SFT BIT(31)
+#define UL_DMIC_PHASE_SEL_CH1_SFT 27
+#define UL_DMIC_PHASE_SEL_CH1_MASK_SFT GENMASK(29, 27)
+#define UL_DMIC_PHASE_SEL_CH2_SFT 24
+#define UL_DMIC_PHASE_SEL_CH2_MASK_SFT GENMASK(26, 24)
+#define UL_MODE_3P25M_CH2_CTL_SFT 22
+#define UL_MODE_3P25M_CH2_CTL_MASK_SFT BIT(22)
+#define UL_MODE_3P25M_CH1_CTL_SFT 21
+#define UL_MODE_3P25M_CH1_CTL_MASK_SFT BIT(21)
+#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT GENMASK(19, 17)
+#define UL_AP_DMIC_ON_SFT 16
+#define UL_AP_DMIC_ON_MASK_SFT BIT(16)
+#define DMIC_LOW_POWER_CTL_SFT 14
+#define DMIC_LOW_POWER_CTL_MASK_SFT GENMASK(15, 14)
+#define UL_DISABLE_HW_CG_CTL_SFT 12
+#define UL_DISABLE_HW_CG_CTL_MASK_SFT BIT(12)
+#define UL_IIR_ON_TMP_CTL_SFT 10
+#define UL_IIR_ON_TMP_CTL_MASK_SFT BIT(10)
+#define UL_IIRMODE_CTL_SFT 7
+#define UL_IIRMODE_CTL_MASK_SFT GENMASK(9, 7)
+#define DIGMIC_4P33M_SEL_SFT 6
+#define DIGMIC_4P33M_SEL_MASK_SFT BIT(6)
+#define DIGMIC_3P25M_1P625M_SEL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_MASK_SFT BIT(5)
+#define UL_LOOP_BACK_MODE_SFT 2
+#define UL_LOOP_BACK_MODE_MASK_SFT BIT(2)
+#define UL_SDM_3_LEVEL_SFT 1
+#define UL_SDM_3_LEVEL_MASK_SFT BIT(1)
+#define UL_SRC_ON_CTL_SFT 0
+#define UL_SRC_ON_CTL_MASK_SFT BIT(0)
+
+/* AFE_ADDA_UL_SRC_CON1 */
+#define C_DAC_EN_CTL_SFT 27
+#define C_DAC_EN_CTL_MASK_SFT BIT(27)
+#define C_MUTE_SW_CTL_SFT 26
+#define C_MUTE_SW_CTL_MASK_SFT BIT(26)
+#define ASDM_SRC_SEL_CTL_SFT 25
+#define ASDM_SRC_SEL_CTL_MASK_SFT BIT(25)
+#define C_AMP_DIV_CH2_CTL_SFT 21
+#define C_AMP_DIV_CH2_CTL_MASK_SFT GENMASK(23, 21)
+#define C_FREQ_DIV_CH2_CTL_SFT 16
+#define C_FREQ_DIV_CH2_CTL_MASK_SFT GENMASK(20, 16)
+#define C_SINE_MODE_CH2_CTL_SFT 12
+#define C_SINE_MODE_CH2_CTL_MASK_SFT GENMASK(15, 12)
+#define C_AMP_DIV_CH1_CTL_SFT 9
+#define C_AMP_DIV_CH1_CTL_MASK_SFT GENMASK(11, 9)
+#define C_FREQ_DIV_CH1_CTL_SFT 4
+#define C_FREQ_DIV_CH1_CTL_MASK_SFT GENMASK(8, 4)
+#define C_SINE_MODE_CH1_CTL_SFT 0
+#define C_SINE_MODE_CH1_CTL_MASK_SFT GENMASK(3, 0)
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOP_BACK_MODE_CTL_SFT 12
+#define C_LOOP_BACK_MODE_CTL_MASK_SFT GENMASK(15, 12)
+#define ADDA_UL_GAIN_MODE_SFT 8
+#define ADDA_UL_GAIN_MODE_MASK_SFT GENMASK(9, 8)
+#define C_EXT_ADC_CTL_SFT 0
+#define C_EXT_ADC_CTL_MASK_SFT BIT(0)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define AFE_ADDA_UL_LR_SWAP_SFT 31
+#define AFE_ADDA_UL_LR_SWAP_MASK_SFT BIT(31)
+#define AFE_ADDA_CKDIV_RST_SFT 30
+#define AFE_ADDA_CKDIV_RST_MASK_SFT BIT(30)
+#define AFE_ADDA_FIFO_AUTO_RST_SFT 29
+#define AFE_ADDA_FIFO_AUTO_RST_MASK_SFT BIT(29)
+#define AFE_ADDA_UL_FIFO_DIGMIC_TESTIN_SFT 21
+#define AFE_ADDA_UL_FIFO_DIGMIC_TESTIN_MASK_SFT GENMASK(22, 21)
+#define AFE_ADDA_UL_FIFO_DIGMIC_WDATA_TESTEN_SFT 20
+#define AFE_ADDA_UL_FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT BIT(20)
+#define AFE_ADDA6_UL_LR_SWAP_SFT 15
+#define AFE_ADDA6_UL_LR_SWAP_MASK_SFT BIT(15)
+#define AFE_ADDA6_CKDIV_RST_SFT 14
+#define AFE_ADDA6_CKDIV_RST_MASK_SFT BIT(14)
+#define AFE_ADDA6_FIFO_AUTO_RST_SFT 13
+#define AFE_ADDA6_FIFO_AUTO_RST_MASK_SFT BIT(13)
+#define AFE_ADDA6_UL_FIFO_DIGMIC_TESTIN_SFT 5
+#define AFE_ADDA6_UL_FIFO_DIGMIC_TESTIN_MASK_SFT GENMASK(6, 5)
+#define AFE_ADDA6_UL_FIFO_DIGMIC_WDATA_TESTEN_SFT 4
+#define AFE_ADDA6_UL_FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT BIT(4)
+#define ADDA_AFE_ON_SFT 0
+#define ADDA_AFE_ON_MASK_SFT BIT(0)
+
+/* AFE_SIDETONE_CON0 */
+#define R_RDY_SFT 30
+#define R_RDY_MASK_SFT BIT(30)
+#define W_RDY_SFT 29
+#define W_RDY_MASK_SFT BIT(29)
+#define R_W_EN_SFT 25
+#define R_W_EN_MASK_SFT BIT(25)
+#define R_W_SEL_SFT 24
+#define R_W_SEL_MASK_SFT BIT(24)
+#define SEL_CH2_SFT 23
+#define SEL_CH2_MASK_SFT BIT(23)
+#define SIDE_TONE_COEFFICIENT_ADDR_SFT 16
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK_SFT GENMASK(20, 16)
+#define SIDE_TONE_COEFFICIENT_SFT 0
+#define SIDE_TONE_COEFFICIENT_MASK_SFT GENMASK(15, 0)
+
+/* AFE_SIDETONE_COEFF */
+#define SIDE_TONE_COEFF_SFT 0
+#define SIDE_TONE_COEFF_MASK_SFT GENMASK(15, 0)
+
+/* AFE_SIDETONE_CON1 */
+#define STF_BYPASS_MODE_SFT 31
+#define STF_BYPASS_MODE_MASK_SFT BIT(31)
+#define STF_BYPASS_MODE_O28_O29_SFT 30
+#define STF_BYPASS_MODE_O28_O29_MASK_SFT BIT(30)
+#define STF_BYPASS_MODE_I2S4_SFT 29
+#define STF_BYPASS_MODE_I2S4_MASK_SFT BIT(29)
+#define STF_BYPASS_MODE_DL3_SFT 27
+#define STF_BYPASS_MODE_DL3_MASK_SFT BIT(27)
+#define STF_BYPASS_MODE_I2S7_SFT 26
+#define STF_BYPASS_MODE_I2S7_MASK_SFT BIT(26)
+#define STF_BYPASS_MODE_I2S9_SFT 25
+#define STF_BYPASS_MODE_I2S9_MASK_SFT BIT(25)
+#define STF_O19O20_OUT_EN_SEL_SFT 13
+#define STF_O19O20_OUT_EN_SEL_MASK_SFT BIT(13)
+#define STF_SOURCE_FROM_O19O20_SFT 12
+#define STF_SOURCE_FROM_O19O20_MASK_SFT BIT(12)
+#define SIDE_TONE_ON_SFT 8
+#define SIDE_TONE_ON_MASK_SFT BIT(8)
+#define SIDE_TONE_HALF_TAP_NUM_SFT 0
+#define SIDE_TONE_HALF_TAP_NUM_MASK_SFT GENMASK(5, 0)
+
+/* AFE_SIDETONE_GAIN */
+#define POSITIVE_GAIN_SFT 16
+#define POSITIVE_GAIN_MASK_SFT GENMASK(18, 16)
+#define SIDE_TONE_GAIN_SFT 0
+#define SIDE_TONE_GAIN_MASK_SFT GENMASK(15, 0)
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define USE_3RD_SDM_SFT 28
+#define USE_3RD_SDM_MASK_SFT BIT(28)
+#define DL_FIFO_START_POINT_SFT 24
+#define DL_FIFO_START_POINT_MASK_SFT GENMASK(26, 24)
+#define DL_FIFO_SWAP_SFT 20
+#define DL_FIFO_SWAP_MASK_SFT BIT(20)
+#define C_AUDSDM1ORDSELECT_CTL_SFT 19
+#define C_AUDSDM1ORDSELECT_CTL_MASK_SFT BIT(19)
+#define C_SDM7BITSEL_CTL_SFT 18
+#define C_SDM7BITSEL_CTL_MASK_SFT BIT(18)
+#define GAIN_AT_SDM_RST_PRE_CTL_SFT 15
+#define GAIN_AT_SDM_RST_PRE_CTL_MASK_SFT BIT(15)
+#define DL_DCM_AUTO_IDLE_EN_SFT 14
+#define DL_DCM_AUTO_IDLE_EN_MASK_SFT BIT(14)
+#define AFE_DL_SRC_DCM_EN_SFT 13
+#define AFE_DL_SRC_DCM_EN_MASK_SFT BIT(13)
+#define AFE_DL_POST_SRC_DCM_EN_SFT 12
+#define AFE_DL_POST_SRC_DCM_EN_MASK_SFT BIT(12)
+#define AUD_SDM_MONO_SFT 9
+#define AUD_SDM_MONO_MASK_SFT BIT(9)
+#define AUD_DC_COMP_EN_SFT 8
+#define AUD_DC_COMP_EN_MASK_SFT BIT(8)
+#define ATTGAIN_CTL_SFT 0
+#define ATTGAIN_CTL_MASK_SFT GENMASK(5, 0)
+
+/* AFE_SINEGEN_CON0 */
+#define DAC_EN_SFT 26
+#define DAC_EN_MASK 0x1
+#define DAC_EN_MASK_SFT BIT(26)
+#define MUTE_SW_CH2_SFT 25
+#define MUTE_SW_CH2_MASK 0x1
+#define MUTE_SW_CH2_MASK_SFT BIT(25)
+#define MUTE_SW_CH1_SFT 24
+#define MUTE_SW_CH1_MASK 0x1
+#define MUTE_SW_CH1_MASK_SFT BIT(24)
+#define SINE_MODE_CH2_SFT 20
+#define SINE_MODE_CH2_MASK 0xf
+#define SINE_MODE_CH2_MASK_SFT GENMASK(23, 20)
+#define AMP_DIV_CH2_SFT 17
+#define AMP_DIV_CH2_MASK 0x7
+#define AMP_DIV_CH2_MASK_SFT GENMASK(19, 17)
+#define FREQ_DIV_CH2_SFT 12
+#define FREQ_DIV_CH2_MASK 0x1f
+#define FREQ_DIV_CH2_MASK_SFT GENMASK(16, 12)
+#define SINE_MODE_CH1_SFT 8
+#define SINE_MODE_CH1_MASK 0xf
+#define SINE_MODE_CH1_MASK_SFT GENMASK(11, 8)
+#define AMP_DIV_CH1_SFT 5
+#define AMP_DIV_CH1_MASK 0x7
+#define AMP_DIV_CH1_MASK_SFT GENMASK(7, 5)
+#define FREQ_DIV_CH1_SFT 0
+#define FREQ_DIV_CH1_MASK 0x1f
+#define FREQ_DIV_CH1_MASK_SFT GENMASK(4, 0)
+
+/* AFE_SINEGEN_CON2 */
+#define INNER_LOOP_BACK_MODE_SFT 0
+#define INNER_LOOP_BACK_MODE_MASK_SFT GENMASK(7, 0)
+
+/* AFE_HD_ENGEN_ENABLE */
+#define AFE_24M_ON_SFT 1
+#define AFE_24M_ON_MASK_SFT BIT(1)
+#define AFE_22M_ON_SFT 0
+#define AFE_22M_ON_MASK_SFT BIT(0)
+
+/* AFE_ADDA_DL_NLE_FIFO_MON */
+#define DL_NLE_FIFO_WBIN_SFT 8
+#define DL_NLE_FIFO_WBIN_MASK_SFT GENMASK(11, 8)
+#define DL_NLE_FIFO_RBIN_SFT 4
+#define DL_NLE_FIFO_RBIN_MASK_SFT GENMASK(7, 4)
+#define DL_NLE_FIFO_RDACTIVE_SFT 3
+#define DL_NLE_FIFO_RDACTIVE_MASK_SFT BIT(3)
+#define DL_NLE_FIFO_STARTRD_SFT 2
+#define DL_NLE_FIFO_STARTRD_MASK_SFT BIT(2)
+#define DL_NLE_FIFO_RD_EMPTY_SFT 1
+#define DL_NLE_FIFO_RD_EMPTY_MASK_SFT BIT(1)
+#define DL_NLE_FIFO_WR_FULL_SFT 0
+#define DL_NLE_FIFO_WR_FULL_MASK_SFT BIT(0)
+
+/* AFE_DL1_CON0 */
+#define DL1_MODE_SFT 24
+#define DL1_MODE_MASK 0xf
+#define DL1_MODE_MASK_SFT GENMASK(27, 24)
+#define DL1_MINLEN_SFT 20
+#define DL1_MINLEN_MASK 0xf
+#define DL1_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL1_MAXLEN_SFT 16
+#define DL1_MAXLEN_MASK 0xf
+#define DL1_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL1_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL1_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL1_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL1_PBUF_SIZE_SFT 12
+#define DL1_PBUF_SIZE_MASK 0x3
+#define DL1_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL1_MONO_SFT 8
+#define DL1_MONO_MASK 0x1
+#define DL1_MONO_MASK_SFT BIT(8)
+#define DL1_NORMAL_MODE_SFT 5
+#define DL1_NORMAL_MODE_MASK 0x1
+#define DL1_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL1_HALIGN_SFT 4
+#define DL1_HALIGN_MASK 0x1
+#define DL1_HALIGN_MASK_SFT BIT(4)
+#define DL1_HD_MODE_SFT 0
+#define DL1_HD_MODE_MASK 0x3
+#define DL1_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL2_CON0 */
+#define DL2_MODE_SFT 24
+#define DL2_MODE_MASK 0xf
+#define DL2_MODE_MASK_SFT GENMASK(27, 24)
+#define DL2_MINLEN_SFT 20
+#define DL2_MINLEN_MASK 0xf
+#define DL2_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL2_MAXLEN_SFT 16
+#define DL2_MAXLEN_MASK 0xf
+#define DL2_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL2_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL2_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL2_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL2_PBUF_SIZE_SFT 12
+#define DL2_PBUF_SIZE_MASK 0x3
+#define DL2_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL2_MONO_SFT 8
+#define DL2_MONO_MASK 0x1
+#define DL2_MONO_MASK_SFT BIT(8)
+#define DL2_NORMAL_MODE_SFT 5
+#define DL2_NORMAL_MODE_MASK 0x1
+#define DL2_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL2_HALIGN_SFT 4
+#define DL2_HALIGN_MASK 0x1
+#define DL2_HALIGN_MASK_SFT BIT(4)
+#define DL2_HD_MODE_SFT 0
+#define DL2_HD_MODE_MASK 0x3
+#define DL2_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL3_CON0 */
+#define DL3_MODE_SFT 24
+#define DL3_MODE_MASK 0xf
+#define DL3_MODE_MASK_SFT GENMASK(27, 24)
+#define DL3_MINLEN_SFT 20
+#define DL3_MINLEN_MASK 0xf
+#define DL3_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL3_MAXLEN_SFT 16
+#define DL3_MAXLEN_MASK 0xf
+#define DL3_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL3_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL3_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL3_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL3_PBUF_SIZE_SFT 12
+#define DL3_PBUF_SIZE_MASK 0x3
+#define DL3_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL3_MONO_SFT 8
+#define DL3_MONO_MASK 0x1
+#define DL3_MONO_MASK_SFT BIT(8)
+#define DL3_NORMAL_MODE_SFT 5
+#define DL3_NORMAL_MODE_MASK 0x1
+#define DL3_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL3_HALIGN_SFT 4
+#define DL3_HALIGN_MASK 0x1
+#define DL3_HALIGN_MASK_SFT BIT(4)
+#define DL3_HD_MODE_SFT 0
+#define DL3_HD_MODE_MASK 0x3
+#define DL3_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL4_CON0 */
+#define DL4_MODE_SFT 24
+#define DL4_MODE_MASK 0xf
+#define DL4_MODE_MASK_SFT GENMASK(27, 24)
+#define DL4_MINLEN_SFT 20
+#define DL4_MINLEN_MASK 0xf
+#define DL4_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL4_MAXLEN_SFT 16
+#define DL4_MAXLEN_MASK 0xf
+#define DL4_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL4_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL4_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL4_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL4_PBUF_SIZE_SFT 12
+#define DL4_PBUF_SIZE_MASK 0x3
+#define DL4_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL4_MONO_SFT 8
+#define DL4_MONO_MASK 0x1
+#define DL4_MONO_MASK_SFT BIT(8)
+#define DL4_NORMAL_MODE_SFT 5
+#define DL4_NORMAL_MODE_MASK 0x1
+#define DL4_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL4_HALIGN_SFT 4
+#define DL4_HALIGN_MASK 0x1
+#define DL4_HALIGN_MASK_SFT BIT(4)
+#define DL4_HD_MODE_SFT 0
+#define DL4_HD_MODE_MASK 0x3
+#define DL4_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL5_CON0 */
+#define DL5_MODE_SFT 24
+#define DL5_MODE_MASK 0xf
+#define DL5_MODE_MASK_SFT GENMASK(27, 24)
+#define DL5_MINLEN_SFT 20
+#define DL5_MINLEN_MASK 0xf
+#define DL5_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL5_MAXLEN_SFT 16
+#define DL5_MAXLEN_MASK 0xf
+#define DL5_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL5_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL5_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL5_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL5_PBUF_SIZE_SFT 12
+#define DL5_PBUF_SIZE_MASK 0x3
+#define DL5_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL5_MONO_SFT 8
+#define DL5_MONO_MASK 0x1
+#define DL5_MONO_MASK_SFT BIT(8)
+#define DL5_NORMAL_MODE_SFT 5
+#define DL5_NORMAL_MODE_MASK 0x1
+#define DL5_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL5_HALIGN_SFT 4
+#define DL5_HALIGN_MASK 0x1
+#define DL5_HALIGN_MASK_SFT BIT(4)
+#define DL5_HD_MODE_SFT 0
+#define DL5_HD_MODE_MASK 0x3
+#define DL5_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL6_CON0 */
+#define DL6_MODE_SFT 24
+#define DL6_MODE_MASK 0xf
+#define DL6_MODE_MASK_SFT GENMASK(27, 24)
+#define DL6_MINLEN_SFT 20
+#define DL6_MINLEN_MASK 0xf
+#define DL6_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL6_MAXLEN_SFT 16
+#define DL6_MAXLEN_MASK 0xf
+#define DL6_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL6_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL6_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL6_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL6_PBUF_SIZE_SFT 12
+#define DL6_PBUF_SIZE_MASK 0x3
+#define DL6_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL6_MONO_SFT 8
+#define DL6_MONO_MASK 0x1
+#define DL6_MONO_MASK_SFT BIT(8)
+#define DL6_NORMAL_MODE_SFT 5
+#define DL6_NORMAL_MODE_MASK 0x1
+#define DL6_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL6_HALIGN_SFT 4
+#define DL6_HALIGN_MASK 0x1
+#define DL6_HALIGN_MASK_SFT BIT(4)
+#define DL6_HD_MODE_SFT 0
+#define DL6_HD_MODE_MASK 0x3
+#define DL6_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL7_CON0 */
+#define DL7_MODE_SFT 24
+#define DL7_MODE_MASK 0xf
+#define DL7_MODE_MASK_SFT GENMASK(27, 24)
+#define DL7_MINLEN_SFT 20
+#define DL7_MINLEN_MASK 0xf
+#define DL7_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL7_MAXLEN_SFT 16
+#define DL7_MAXLEN_MASK 0xf
+#define DL7_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL7_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL7_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL7_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL7_PBUF_SIZE_SFT 12
+#define DL7_PBUF_SIZE_MASK 0x3
+#define DL7_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL7_MONO_SFT 8
+#define DL7_MONO_MASK 0x1
+#define DL7_MONO_MASK_SFT BIT(8)
+#define DL7_NORMAL_MODE_SFT 5
+#define DL7_NORMAL_MODE_MASK 0x1
+#define DL7_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL7_HALIGN_SFT 4
+#define DL7_HALIGN_MASK 0x1
+#define DL7_HALIGN_MASK_SFT BIT(4)
+#define DL7_HD_MODE_SFT 0
+#define DL7_HD_MODE_MASK 0x3
+#define DL7_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL8_CON0 */
+#define DL8_MODE_SFT 24
+#define DL8_MODE_MASK 0xf
+#define DL8_MODE_MASK_SFT GENMASK(27, 24)
+#define DL8_MINLEN_SFT 20
+#define DL8_MINLEN_MASK 0xf
+#define DL8_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL8_MAXLEN_SFT 16
+#define DL8_MAXLEN_MASK 0xf
+#define DL8_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL8_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL8_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL8_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL8_PBUF_SIZE_SFT 12
+#define DL8_PBUF_SIZE_MASK 0x3
+#define DL8_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL8_MONO_SFT 8
+#define DL8_MONO_MASK 0x1
+#define DL8_MONO_MASK_SFT BIT(8)
+#define DL8_NORMAL_MODE_SFT 5
+#define DL8_NORMAL_MODE_MASK 0x1
+#define DL8_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL8_HALIGN_SFT 4
+#define DL8_HALIGN_MASK 0x1
+#define DL8_HALIGN_MASK_SFT BIT(4)
+#define DL8_HD_MODE_SFT 0
+#define DL8_HD_MODE_MASK 0x3
+#define DL8_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DL12_CON0 */
+#define DL12_MODE_SFT 24
+#define DL12_MODE_MASK 0xf
+#define DL12_MODE_MASK_SFT GENMASK(27, 24)
+#define DL12_MINLEN_SFT 20
+#define DL12_MINLEN_MASK 0xf
+#define DL12_MINLEN_MASK_SFT GENMASK(23, 20)
+#define DL12_MAXLEN_SFT 16
+#define DL12_MAXLEN_MASK 0xf
+#define DL12_MAXLEN_MASK_SFT GENMASK(19, 16)
+#define DL12_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL12_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL12_SW_CLEAR_BUF_EMPTY_MASK_SFT BIT(15)
+#define DL12_PBUF_SIZE_SFT 12
+#define DL12_PBUF_SIZE_MASK 0x3
+#define DL12_PBUF_SIZE_MASK_SFT GENMASK(13, 12)
+#define DL12_4CH_EN_SFT 11
+#define DL12_4CH_EN_MASK 0x1
+#define DL12_4CH_EN_MASK_SFT BIT(11)
+#define DL12_MONO_SFT 8
+#define DL12_MONO_MASK 0x1
+#define DL12_MONO_MASK_SFT BIT(8)
+#define DL12_NORMAL_MODE_SFT 5
+#define DL12_NORMAL_MODE_MASK 0x1
+#define DL12_NORMAL_MODE_MASK_SFT BIT(5)
+#define DL12_HALIGN_SFT 4
+#define DL12_HALIGN_MASK 0x1
+#define DL12_HALIGN_MASK_SFT BIT(4)
+#define DL12_HD_MODE_SFT 0
+#define DL12_HD_MODE_MASK 0x3
+#define DL12_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_AWB_CON0 */
+#define AWB_MODE_SFT 24
+#define AWB_MODE_MASK 0xf
+#define AWB_MODE_MASK_SFT GENMASK(27, 24)
+#define AWB_SW_CLEAR_BUF_FULL_SFT 15
+#define AWB_SW_CLEAR_BUF_FULL_MASK 0x1
+#define AWB_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define AWB_R_MONO_SFT 9
+#define AWB_R_MONO_MASK 0x1
+#define AWB_R_MONO_MASK_SFT BIT(9)
+#define AWB_MONO_SFT 8
+#define AWB_MONO_MASK 0x1
+#define AWB_MONO_MASK_SFT BIT(8)
+#define AWB_WR_SIGN_SFT 6
+#define AWB_WR_SIGN_MASK 0x1
+#define AWB_WR_SIGN_MASK_SFT BIT(6)
+#define AWB_NORMAL_MODE_SFT 5
+#define AWB_NORMAL_MODE_MASK 0x1
+#define AWB_NORMAL_MODE_MASK_SFT BIT(5)
+#define AWB_HALIGN_SFT 4
+#define AWB_HALIGN_MASK 0x1
+#define AWB_HALIGN_MASK_SFT BIT(4)
+#define AWB_HD_MODE_SFT 0
+#define AWB_HD_MODE_MASK 0x3
+#define AWB_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_AWB2_CON0 */
+#define AWB2_MODE_SFT 24
+#define AWB2_MODE_MASK 0xf
+#define AWB2_MODE_MASK_SFT GENMASK(27, 24)
+#define AWB2_SW_CLEAR_BUF_FULL_SFT 15
+#define AWB2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define AWB2_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define AWB2_R_MONO_SFT 9
+#define AWB2_R_MONO_MASK 0x1
+#define AWB2_R_MONO_MASK_SFT BIT(9)
+#define AWB2_MONO_SFT 8
+#define AWB2_MONO_MASK 0x1
+#define AWB2_MONO_MASK_SFT BIT(8)
+#define AWB2_WR_SIGN_SFT 6
+#define AWB2_WR_SIGN_MASK 0x1
+#define AWB2_WR_SIGN_MASK_SFT BIT(6)
+#define AWB2_NORMAL_MODE_SFT 5
+#define AWB2_NORMAL_MODE_MASK 0x1
+#define AWB2_NORMAL_MODE_MASK_SFT BIT(5)
+#define AWB2_HALIGN_SFT 4
+#define AWB2_HALIGN_MASK 0x1
+#define AWB2_HALIGN_MASK_SFT BIT(4)
+#define AWB2_HD_MODE_SFT 0
+#define AWB2_HD_MODE_MASK 0x3
+#define AWB2_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL_CON0 */
+#define VUL_MODE_SFT 24
+#define VUL_MODE_MASK 0xf
+#define VUL_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL_R_MONO_SFT 9
+#define VUL_R_MONO_MASK 0x1
+#define VUL_R_MONO_MASK_SFT BIT(9)
+#define VUL_MONO_SFT 8
+#define VUL_MONO_MASK 0x1
+#define VUL_MONO_MASK_SFT BIT(8)
+#define VUL_WR_SIGN_SFT 6
+#define VUL_WR_SIGN_MASK 0x1
+#define VUL_WR_SIGN_MASK_SFT BIT(6)
+#define VUL_NORMAL_MODE_SFT 5
+#define VUL_NORMAL_MODE_MASK 0x1
+#define VUL_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL_HALIGN_SFT 4
+#define VUL_HALIGN_MASK 0x1
+#define VUL_HALIGN_MASK_SFT BIT(4)
+#define VUL_HD_MODE_SFT 0
+#define VUL_HD_MODE_MASK 0x3
+#define VUL_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL12_CON0 */
+#define VUL12_MODE_SFT 24
+#define VUL12_MODE_MASK 0xf
+#define VUL12_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL12_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL12_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL12_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL12_4CH_EN_SFT 11
+#define VUL12_4CH_EN_MASK 0x1
+#define VUL12_4CH_EN_MASK_SFT BIT(11)
+#define VUL12_R_MONO_SFT 9
+#define VUL12_R_MONO_MASK 0x1
+#define VUL12_R_MONO_MASK_SFT BIT(9)
+#define VUL12_MONO_SFT 8
+#define VUL12_MONO_MASK 0x1
+#define VUL12_MONO_MASK_SFT BIT(8)
+#define VUL12_WR_SIGN_SFT 6
+#define VUL12_WR_SIGN_MASK 0x1
+#define VUL12_WR_SIGN_MASK_SFT BIT(6)
+#define VUL12_NORMAL_MODE_SFT 5
+#define VUL12_NORMAL_MODE_MASK 0x1
+#define VUL12_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL12_HALIGN_SFT 4
+#define VUL12_HALIGN_MASK 0x1
+#define VUL12_HALIGN_MASK_SFT BIT(4)
+#define VUL12_HD_MODE_SFT 0
+#define VUL12_HD_MODE_MASK 0x3
+#define VUL12_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL2_CON0 */
+#define VUL2_MODE_SFT 24
+#define VUL2_MODE_MASK 0xf
+#define VUL2_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL2_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL2_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL2_R_MONO_SFT 9
+#define VUL2_R_MONO_MASK 0x1
+#define VUL2_R_MONO_MASK_SFT BIT(9)
+#define VUL2_MONO_SFT 8
+#define VUL2_MONO_MASK 0x1
+#define VUL2_MONO_MASK_SFT BIT(8)
+#define VUL2_WR_SIGN_SFT 6
+#define VUL2_WR_SIGN_MASK 0x1
+#define VUL2_WR_SIGN_MASK_SFT BIT(6)
+#define VUL2_NORMAL_MODE_SFT 5
+#define VUL2_NORMAL_MODE_MASK 0x1
+#define VUL2_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL2_HALIGN_SFT 4
+#define VUL2_HALIGN_MASK 0x1
+#define VUL2_HALIGN_MASK_SFT BIT(4)
+#define VUL2_HD_MODE_SFT 0
+#define VUL2_HD_MODE_MASK 0x3
+#define VUL2_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL3_CON0 */
+#define VUL3_MODE_SFT 24
+#define VUL3_MODE_MASK 0xf
+#define VUL3_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL3_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL3_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL3_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL3_R_MONO_SFT 9
+#define VUL3_R_MONO_MASK 0x1
+#define VUL3_R_MONO_MASK_SFT BIT(9)
+#define VUL3_MONO_SFT 8
+#define VUL3_MONO_MASK 0x1
+#define VUL3_MONO_MASK_SFT BIT(8)
+#define VUL3_WR_SIGN_SFT 6
+#define VUL3_WR_SIGN_MASK 0x1
+#define VUL3_WR_SIGN_MASK_SFT BIT(6)
+#define VUL3_NORMAL_MODE_SFT 5
+#define VUL3_NORMAL_MODE_MASK 0x1
+#define VUL3_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL3_HALIGN_SFT 4
+#define VUL3_HALIGN_MASK 0x1
+#define VUL3_HALIGN_MASK_SFT BIT(4)
+#define VUL3_HD_MODE_SFT 0
+#define VUL3_HD_MODE_MASK 0x3
+#define VUL3_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL4_CON0 */
+#define VUL4_MODE_SFT 24
+#define VUL4_MODE_MASK 0xf
+#define VUL4_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL4_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL4_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL4_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL4_R_MONO_SFT 9
+#define VUL4_R_MONO_MASK 0x1
+#define VUL4_R_MONO_MASK_SFT BIT(9)
+#define VUL4_MONO_SFT 8
+#define VUL4_MONO_MASK 0x1
+#define VUL4_MONO_MASK_SFT BIT(8)
+#define VUL4_WR_SIGN_SFT 6
+#define VUL4_WR_SIGN_MASK 0x1
+#define VUL4_WR_SIGN_MASK_SFT BIT(6)
+#define VUL4_NORMAL_MODE_SFT 5
+#define VUL4_NORMAL_MODE_MASK 0x1
+#define VUL4_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL4_HALIGN_SFT 4
+#define VUL4_HALIGN_MASK 0x1
+#define VUL4_HALIGN_MASK_SFT BIT(4)
+#define VUL4_HD_MODE_SFT 0
+#define VUL4_HD_MODE_MASK 0x3
+#define VUL4_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL5_CON0 */
+#define VUL5_MODE_SFT 24
+#define VUL5_MODE_MASK 0xf
+#define VUL5_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL5_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL5_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL5_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL5_R_MONO_SFT 9
+#define VUL5_R_MONO_MASK 0x1
+#define VUL5_R_MONO_MASK_SFT BIT(9)
+#define VUL5_MONO_SFT 8
+#define VUL5_MONO_MASK 0x1
+#define VUL5_MONO_MASK_SFT BIT(8)
+#define VUL5_WR_SIGN_SFT 6
+#define VUL5_WR_SIGN_MASK 0x1
+#define VUL5_WR_SIGN_MASK_SFT BIT(6)
+#define VUL5_NORMAL_MODE_SFT 5
+#define VUL5_NORMAL_MODE_MASK 0x1
+#define VUL5_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL5_HALIGN_SFT 4
+#define VUL5_HALIGN_MASK 0x1
+#define VUL5_HALIGN_MASK_SFT BIT(4)
+#define VUL5_HD_MODE_SFT 0
+#define VUL5_HD_MODE_MASK 0x3
+#define VUL5_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_VUL6_CON0 */
+#define VUL6_MODE_SFT 24
+#define VUL6_MODE_MASK 0xf
+#define VUL6_MODE_MASK_SFT GENMASK(27, 24)
+#define VUL6_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL6_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL6_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define VUL6_R_MONO_SFT 9
+#define VUL6_R_MONO_MASK 0x1
+#define VUL6_R_MONO_MASK_SFT BIT(9)
+#define VUL6_MONO_SFT 8
+#define VUL6_MONO_MASK 0x1
+#define VUL6_MONO_MASK_SFT BIT(8)
+#define VUL6_WR_SIGN_SFT 6
+#define VUL6_WR_SIGN_MASK 0x1
+#define VUL6_WR_SIGN_MASK_SFT BIT(6)
+#define VUL6_NORMAL_MODE_SFT 5
+#define VUL6_NORMAL_MODE_MASK 0x1
+#define VUL6_NORMAL_MODE_MASK_SFT BIT(5)
+#define VUL6_HALIGN_SFT 4
+#define VUL6_HALIGN_MASK 0x1
+#define VUL6_HALIGN_MASK_SFT BIT(4)
+#define VUL6_HD_MODE_SFT 0
+#define VUL6_HD_MODE_MASK 0x3
+#define VUL6_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DAI_CON0 */
+#define DAI_MODE_SFT 24
+#define DAI_MODE_MASK 0x3
+#define DAI_MODE_MASK_SFT GENMASK(25, 24)
+#define DAI_SW_CLEAR_BUF_FULL_SFT 15
+#define DAI_SW_CLEAR_BUF_FULL_MASK 0x1
+#define DAI_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define DAI_DUPLICATE_WR_SFT 10
+#define DAI_DUPLICATE_WR_MASK 0x1
+#define DAI_DUPLICATE_WR_MASK_SFT BIT(10)
+#define DAI_MONO_SFT 8
+#define DAI_MONO_MASK 0x1
+#define DAI_MONO_MASK_SFT BIT(8)
+#define DAI_WR_SIGN_SFT 6
+#define DAI_WR_SIGN_MASK 0x1
+#define DAI_WR_SIGN_MASK_SFT BIT(6)
+#define DAI_NORMAL_MODE_SFT 5
+#define DAI_NORMAL_MODE_MASK 0x1
+#define DAI_NORMAL_MODE_MASK_SFT BIT(5)
+#define DAI_HALIGN_SFT 4
+#define DAI_HALIGN_MASK 0x1
+#define DAI_HALIGN_MASK_SFT BIT(4)
+#define DAI_HD_MODE_SFT 0
+#define DAI_HD_MODE_MASK 0x3
+#define DAI_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_MOD_DAI_CON0 */
+#define MOD_DAI_MODE_SFT 24
+#define MOD_DAI_MODE_MASK 0x3
+#define MOD_DAI_MODE_MASK_SFT GENMASK(25, 24)
+#define MOD_DAI_SW_CLEAR_BUF_FULL_SFT 15
+#define MOD_DAI_SW_CLEAR_BUF_FULL_MASK 0x1
+#define MOD_DAI_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define MOD_DAI_DUPLICATE_WR_SFT 10
+#define MOD_DAI_DUPLICATE_WR_MASK 0x1
+#define MOD_DAI_DUPLICATE_WR_MASK_SFT BIT(10)
+#define MOD_DAI_MONO_SFT 8
+#define MOD_DAI_MONO_MASK 0x1
+#define MOD_DAI_MONO_MASK_SFT BIT(8)
+#define MOD_DAI_WR_SIGN_SFT 6
+#define MOD_DAI_WR_SIGN_MASK 0x1
+#define MOD_DAI_WR_SIGN_MASK_SFT BIT(6)
+#define MOD_DAI_NORMAL_MODE_SFT 5
+#define MOD_DAI_NORMAL_MODE_MASK 0x1
+#define MOD_DAI_NORMAL_MODE_MASK_SFT BIT(5)
+#define MOD_DAI_HALIGN_SFT 4
+#define MOD_DAI_HALIGN_MASK 0x1
+#define MOD_DAI_HALIGN_MASK_SFT BIT(4)
+#define MOD_DAI_HD_MODE_SFT 0
+#define MOD_DAI_HD_MODE_MASK 0x3
+#define MOD_DAI_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_DAI2_CON0 */
+#define DAI2_MODE_SFT 24
+#define DAI2_MODE_MASK 0xf
+#define DAI2_MODE_MASK_SFT GENMASK(27, 24)
+#define DAI2_SW_CLEAR_BUF_FULL_SFT 15
+#define DAI2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define DAI2_SW_CLEAR_BUF_FULL_MASK_SFT BIT(15)
+#define DAI2_DUPLICATE_WR_SFT 10
+#define DAI2_DUPLICATE_WR_MASK 0x1
+#define DAI2_DUPLICATE_WR_MASK_SFT BIT(10)
+#define DAI2_MONO_SFT 8
+#define DAI2_MONO_MASK 0x1
+#define DAI2_MONO_MASK_SFT BIT(8)
+#define DAI2_WR_SIGN_SFT 6
+#define DAI2_WR_SIGN_MASK 0x1
+#define DAI2_WR_SIGN_MASK_SFT BIT(6)
+#define DAI2_NORMAL_MODE_SFT 5
+#define DAI2_NORMAL_MODE_MASK 0x1
+#define DAI2_NORMAL_MODE_MASK_SFT BIT(5)
+#define DAI2_HALIGN_SFT 4
+#define DAI2_HALIGN_MASK 0x1
+#define DAI2_HALIGN_MASK_SFT BIT(4)
+#define DAI2_HD_MODE_SFT 0
+#define DAI2_HD_MODE_MASK 0x3
+#define DAI2_HD_MODE_MASK_SFT GENMASK(1, 0)
+
+/* AFE_MEMIF_CON0 */
+#define CPU_COMPACT_MODE_SFT 2
+#define CPU_COMPACT_MODE_MASK_SFT BIT(2)
+#define CPU_HD_ALIGN_SFT 1
+#define CPU_HD_ALIGN_MASK_SFT BIT(1)
+#define SYSRAM_SIGN_SFT 0
+#define SYSRAM_SIGN_MASK_SFT BIT(0)
+
+/* AFE_IRQ_MCU_CON0 */
+#define IRQ31_MCU_ON_SFT 31
+#define IRQ31_MCU_ON_MASK 0x1
+#define IRQ31_MCU_ON_MASK_SFT BIT(31)
+#define IRQ26_MCU_ON_SFT 26
+#define IRQ26_MCU_ON_MASK 0x1
+#define IRQ26_MCU_ON_MASK_SFT BIT(26)
+#define IRQ25_MCU_ON_SFT 25
+#define IRQ25_MCU_ON_MASK 0x1
+#define IRQ25_MCU_ON_MASK_SFT BIT(25)
+#define IRQ24_MCU_ON_SFT 24
+#define IRQ24_MCU_ON_MASK 0x1
+#define IRQ24_MCU_ON_MASK_SFT BIT(24)
+#define IRQ23_MCU_ON_SFT 23
+#define IRQ23_MCU_ON_MASK 0x1
+#define IRQ23_MCU_ON_MASK_SFT BIT(23)
+#define IRQ22_MCU_ON_SFT 22
+#define IRQ22_MCU_ON_MASK 0x1
+#define IRQ22_MCU_ON_MASK_SFT BIT(22)
+#define IRQ21_MCU_ON_SFT 21
+#define IRQ21_MCU_ON_MASK 0x1
+#define IRQ21_MCU_ON_MASK_SFT BIT(21)
+#define IRQ20_MCU_ON_SFT 20
+#define IRQ20_MCU_ON_MASK 0x1
+#define IRQ20_MCU_ON_MASK_SFT BIT(20)
+#define IRQ19_MCU_ON_SFT 19
+#define IRQ19_MCU_ON_MASK 0x1
+#define IRQ19_MCU_ON_MASK_SFT BIT(19)
+#define IRQ18_MCU_ON_SFT 18
+#define IRQ18_MCU_ON_MASK 0x1
+#define IRQ18_MCU_ON_MASK_SFT BIT(18)
+#define IRQ17_MCU_ON_SFT 17
+#define IRQ17_MCU_ON_MASK 0x1
+#define IRQ17_MCU_ON_MASK_SFT BIT(17)
+#define IRQ16_MCU_ON_SFT 16
+#define IRQ16_MCU_ON_MASK 0x1
+#define IRQ16_MCU_ON_MASK_SFT BIT(16)
+#define IRQ15_MCU_ON_SFT 15
+#define IRQ15_MCU_ON_MASK 0x1
+#define IRQ15_MCU_ON_MASK_SFT BIT(15)
+#define IRQ14_MCU_ON_SFT 14
+#define IRQ14_MCU_ON_MASK 0x1
+#define IRQ14_MCU_ON_MASK_SFT BIT(14)
+#define IRQ13_MCU_ON_SFT 13
+#define IRQ13_MCU_ON_MASK 0x1
+#define IRQ13_MCU_ON_MASK_SFT BIT(13)
+#define IRQ12_MCU_ON_SFT 12
+#define IRQ12_MCU_ON_MASK 0x1
+#define IRQ12_MCU_ON_MASK_SFT BIT(12)
+#define IRQ11_MCU_ON_SFT 11
+#define IRQ11_MCU_ON_MASK 0x1
+#define IRQ11_MCU_ON_MASK_SFT BIT(11)
+#define IRQ10_MCU_ON_SFT 10
+#define IRQ10_MCU_ON_MASK 0x1
+#define IRQ10_MCU_ON_MASK_SFT BIT(10)
+#define IRQ9_MCU_ON_SFT 9
+#define IRQ9_MCU_ON_MASK 0x1
+#define IRQ9_MCU_ON_MASK_SFT BIT(9)
+#define IRQ8_MCU_ON_SFT 8
+#define IRQ8_MCU_ON_MASK 0x1
+#define IRQ8_MCU_ON_MASK_SFT BIT(8)
+#define IRQ7_MCU_ON_SFT 7
+#define IRQ7_MCU_ON_MASK 0x1
+#define IRQ7_MCU_ON_MASK_SFT BIT(7)
+#define IRQ6_MCU_ON_SFT 6
+#define IRQ6_MCU_ON_MASK 0x1
+#define IRQ6_MCU_ON_MASK_SFT BIT(6)
+#define IRQ5_MCU_ON_SFT 5
+#define IRQ5_MCU_ON_MASK 0x1
+#define IRQ5_MCU_ON_MASK_SFT BIT(5)
+#define IRQ4_MCU_ON_SFT 4
+#define IRQ4_MCU_ON_MASK 0x1
+#define IRQ4_MCU_ON_MASK_SFT BIT(4)
+#define IRQ3_MCU_ON_SFT 3
+#define IRQ3_MCU_ON_MASK 0x1
+#define IRQ3_MCU_ON_MASK_SFT BIT(3)
+#define IRQ2_MCU_ON_SFT 2
+#define IRQ2_MCU_ON_MASK 0x1
+#define IRQ2_MCU_ON_MASK_SFT BIT(2)
+#define IRQ1_MCU_ON_SFT 1
+#define IRQ1_MCU_ON_MASK 0x1
+#define IRQ1_MCU_ON_MASK_SFT BIT(1)
+#define IRQ0_MCU_ON_SFT 0
+#define IRQ0_MCU_ON_MASK 0x1
+#define IRQ0_MCU_ON_MASK_SFT BIT(0)
+
+/* AFE_IRQ_MCU_CON1 */
+#define IRQ7_MCU_MODE_SFT 28
+#define IRQ7_MCU_MODE_MASK 0xf
+#define IRQ7_MCU_MODE_MASK_SFT GENMASK(31, 28)
+#define IRQ6_MCU_MODE_SFT 24
+#define IRQ6_MCU_MODE_MASK 0xf
+#define IRQ6_MCU_MODE_MASK_SFT GENMASK(27, 24)
+#define IRQ5_MCU_MODE_SFT 20
+#define IRQ5_MCU_MODE_MASK 0xf
+#define IRQ5_MCU_MODE_MASK_SFT GENMASK(23, 20)
+#define IRQ4_MCU_MODE_SFT 16
+#define IRQ4_MCU_MODE_MASK 0xf
+#define IRQ4_MCU_MODE_MASK_SFT GENMASK(19, 16)
+#define IRQ3_MCU_MODE_SFT 12
+#define IRQ3_MCU_MODE_MASK 0xf
+#define IRQ3_MCU_MODE_MASK_SFT GENMASK(15, 12)
+#define IRQ2_MCU_MODE_SFT 8
+#define IRQ2_MCU_MODE_MASK 0xf
+#define IRQ2_MCU_MODE_MASK_SFT GENMASK(11, 8)
+#define IRQ1_MCU_MODE_SFT 4
+#define IRQ1_MCU_MODE_MASK 0xf
+#define IRQ1_MCU_MODE_MASK_SFT GENMASK(7, 4)
+#define IRQ0_MCU_MODE_SFT 0
+#define IRQ0_MCU_MODE_MASK 0xf
+#define IRQ0_MCU_MODE_MASK_SFT GENMASK(3, 0)
+
+/* AFE_IRQ_MCU_CON2 */
+#define IRQ15_MCU_MODE_SFT 28
+#define IRQ15_MCU_MODE_MASK 0xf
+#define IRQ15_MCU_MODE_MASK_SFT GENMASK(31, 28)
+#define IRQ14_MCU_MODE_SFT 24
+#define IRQ14_MCU_MODE_MASK 0xf
+#define IRQ14_MCU_MODE_MASK_SFT GENMASK(27, 24)
+#define IRQ13_MCU_MODE_SFT 20
+#define IRQ13_MCU_MODE_MASK 0xf
+#define IRQ13_MCU_MODE_MASK_SFT GENMASK(23, 20)
+#define IRQ12_MCU_MODE_SFT 16
+#define IRQ12_MCU_MODE_MASK 0xf
+#define IRQ12_MCU_MODE_MASK_SFT GENMASK(19, 16)
+#define IRQ11_MCU_MODE_SFT 12
+#define IRQ11_MCU_MODE_MASK 0xf
+#define IRQ11_MCU_MODE_MASK_SFT GENMASK(15, 12)
+#define IRQ10_MCU_MODE_SFT 8
+#define IRQ10_MCU_MODE_MASK 0xf
+#define IRQ10_MCU_MODE_MASK_SFT GENMASK(11, 8)
+#define IRQ9_MCU_MODE_SFT 4
+#define IRQ9_MCU_MODE_MASK 0xf
+#define IRQ9_MCU_MODE_MASK_SFT GENMASK(7, 4)
+#define IRQ8_MCU_MODE_SFT 0
+#define IRQ8_MCU_MODE_MASK 0xf
+#define IRQ8_MCU_MODE_MASK_SFT GENMASK(3, 0)
+
+/* AFE_IRQ_MCU_CON3 */
+#define IRQ23_MCU_MODE_SFT 28
+#define IRQ23_MCU_MODE_MASK 0xf
+#define IRQ23_MCU_MODE_MASK_SFT GENMASK(31, 28)
+#define IRQ22_MCU_MODE_SFT 24
+#define IRQ22_MCU_MODE_MASK 0xf
+#define IRQ22_MCU_MODE_MASK_SFT GENMASK(27, 24)
+#define IRQ21_MCU_MODE_SFT 20
+#define IRQ21_MCU_MODE_MASK 0xf
+#define IRQ21_MCU_MODE_MASK_SFT GENMASK(23, 20)
+#define IRQ20_MCU_MODE_SFT 16
+#define IRQ20_MCU_MODE_MASK 0xf
+#define IRQ20_MCU_MODE_MASK_SFT GENMASK(19, 16)
+#define IRQ19_MCU_MODE_SFT 12
+#define IRQ19_MCU_MODE_MASK 0xf
+#define IRQ19_MCU_MODE_MASK_SFT GENMASK(15, 12)
+#define IRQ18_MCU_MODE_SFT 8
+#define IRQ18_MCU_MODE_MASK 0xf
+#define IRQ18_MCU_MODE_MASK_SFT GENMASK(11, 8)
+#define IRQ17_MCU_MODE_SFT 4
+#define IRQ17_MCU_MODE_MASK 0xf
+#define IRQ17_MCU_MODE_MASK_SFT GENMASK(7, 4)
+#define IRQ16_MCU_MODE_SFT 0
+#define IRQ16_MCU_MODE_MASK 0xf
+#define IRQ16_MCU_MODE_MASK_SFT GENMASK(3, 0)
+
+/* AFE_IRQ_MCU_CON4 */
+#define IRQ26_MCU_MODE_SFT 8
+#define IRQ26_MCU_MODE_MASK 0xf
+#define IRQ26_MCU_MODE_MASK_SFT GENMASK(11, 8)
+#define IRQ25_MCU_MODE_SFT 4
+#define IRQ25_MCU_MODE_MASK 0xf
+#define IRQ25_MCU_MODE_MASK_SFT GENMASK(7, 4)
+#define IRQ24_MCU_MODE_SFT 0
+#define IRQ24_MCU_MODE_MASK 0xf
+#define IRQ24_MCU_MODE_MASK_SFT GENMASK(3, 0)
+
+/* AFE_IRQ_MCU_CLR */
+#define IRQ31_MCU_CLR_SFT 31
+#define IRQ31_MCU_CLR_MASK_SFT BIT(31)
+#define IRQ26_MCU_CLR_SFT 26
+#define IRQ26_MCU_CLR_MASK_SFT BIT(26)
+#define IRQ25_MCU_CLR_SFT 25
+#define IRQ25_MCU_CLR_MASK_SFT BIT(25)
+#define IRQ24_MCU_CLR_SFT 24
+#define IRQ24_MCU_CLR_MASK_SFT BIT(24)
+#define IRQ23_MCU_CLR_SFT 23
+#define IRQ23_MCU_CLR_MASK_SFT BIT(23)
+#define IRQ22_MCU_CLR_SFT 22
+#define IRQ22_MCU_CLR_MASK_SFT BIT(22)
+#define IRQ21_MCU_CLR_SFT 21
+#define IRQ21_MCU_CLR_MASK_SFT BIT(21)
+#define IRQ20_MCU_CLR_SFT 20
+#define IRQ20_MCU_CLR_MASK_SFT BIT(20)
+#define IRQ19_MCU_CLR_SFT 19
+#define IRQ19_MCU_CLR_MASK_SFT BIT(19)
+#define IRQ18_MCU_CLR_SFT 18
+#define IRQ18_MCU_CLR_MASK_SFT BIT(18)
+#define IRQ17_MCU_CLR_SFT 17
+#define IRQ17_MCU_CLR_MASK_SFT BIT(17)
+#define IRQ16_MCU_CLR_SFT 16
+#define IRQ16_MCU_CLR_MASK_SFT BIT(16)
+#define IRQ15_MCU_CLR_SFT 15
+#define IRQ15_MCU_CLR_MASK_SFT BIT(15)
+#define IRQ14_MCU_CLR_SFT 14
+#define IRQ14_MCU_CLR_MASK_SFT BIT(14)
+#define IRQ13_MCU_CLR_SFT 13
+#define IRQ13_MCU_CLR_MASK_SFT BIT(13)
+#define IRQ12_MCU_CLR_SFT 12
+#define IRQ12_MCU_CLR_MASK_SFT BIT(12)
+#define IRQ11_MCU_CLR_SFT 11
+#define IRQ11_MCU_CLR_MASK_SFT BIT(11)
+#define IRQ10_MCU_CLR_SFT 10
+#define IRQ10_MCU_CLR_MASK_SFT BIT(10)
+#define IRQ9_MCU_CLR_SFT 9
+#define IRQ9_MCU_CLR_MASK_SFT BIT(9)
+#define IRQ8_MCU_CLR_SFT 8
+#define IRQ8_MCU_CLR_MASK_SFT BIT(8)
+#define IRQ7_MCU_CLR_SFT 7
+#define IRQ7_MCU_CLR_MASK_SFT BIT(7)
+#define IRQ6_MCU_CLR_SFT 6
+#define IRQ6_MCU_CLR_MASK_SFT BIT(6)
+#define IRQ5_MCU_CLR_SFT 5
+#define IRQ5_MCU_CLR_MASK_SFT BIT(5)
+#define IRQ4_MCU_CLR_SFT 4
+#define IRQ4_MCU_CLR_MASK_SFT BIT(4)
+#define IRQ3_MCU_CLR_SFT 3
+#define IRQ3_MCU_CLR_MASK_SFT BIT(3)
+#define IRQ2_MCU_CLR_SFT 2
+#define IRQ2_MCU_CLR_MASK_SFT BIT(2)
+#define IRQ1_MCU_CLR_SFT 1
+#define IRQ1_MCU_CLR_MASK_SFT BIT(1)
+#define IRQ0_MCU_CLR_SFT 0
+#define IRQ0_MCU_CLR_MASK_SFT BIT(0)
+
+/* AFE_IRQ_MCU_EN */
+#define IRQ31_MCU_EN_SFT 31
+#define IRQ30_MCU_EN_SFT 30
+#define IRQ29_MCU_EN_SFT 29
+#define IRQ28_MCU_EN_SFT 28
+#define IRQ27_MCU_EN_SFT 27
+#define IRQ26_MCU_EN_SFT 26
+#define IRQ25_MCU_EN_SFT 25
+#define IRQ24_MCU_EN_SFT 24
+#define IRQ23_MCU_EN_SFT 23
+#define IRQ22_MCU_EN_SFT 22
+#define IRQ21_MCU_EN_SFT 21
+#define IRQ20_MCU_EN_SFT 20
+#define IRQ19_MCU_EN_SFT 19
+#define IRQ18_MCU_EN_SFT 18
+#define IRQ17_MCU_EN_SFT 17
+#define IRQ16_MCU_EN_SFT 16
+#define IRQ15_MCU_EN_SFT 15
+#define IRQ14_MCU_EN_SFT 14
+#define IRQ13_MCU_EN_SFT 13
+#define IRQ12_MCU_EN_SFT 12
+#define IRQ11_MCU_EN_SFT 11
+#define IRQ10_MCU_EN_SFT 10
+#define IRQ9_MCU_EN_SFT 9
+#define IRQ8_MCU_EN_SFT 8
+#define IRQ7_MCU_EN_SFT 7
+#define IRQ6_MCU_EN_SFT 6
+#define IRQ5_MCU_EN_SFT 5
+#define IRQ4_MCU_EN_SFT 4
+#define IRQ3_MCU_EN_SFT 3
+#define IRQ2_MCU_EN_SFT 2
+#define IRQ1_MCU_EN_SFT 1
+#define IRQ0_MCU_EN_SFT 0
+
+/* AFE_IRQ_MCU_SCP_EN */
+#define IRQ31_MCU_SCP_EN_SFT 31
+#define IRQ30_MCU_SCP_EN_SFT 30
+#define IRQ29_MCU_SCP_EN_SFT 29
+#define IRQ28_MCU_SCP_EN_SFT 28
+#define IRQ27_MCU_SCP_EN_SFT 27
+#define IRQ26_MCU_SCP_EN_SFT 26
+#define IRQ25_MCU_SCP_EN_SFT 25
+#define IRQ24_MCU_SCP_EN_SFT 24
+#define IRQ23_MCU_SCP_EN_SFT 23
+#define IRQ22_MCU_SCP_EN_SFT 22
+#define IRQ21_MCU_SCP_EN_SFT 21
+#define IRQ20_MCU_SCP_EN_SFT 20
+#define IRQ19_MCU_SCP_EN_SFT 19
+#define IRQ18_MCU_SCP_EN_SFT 18
+#define IRQ17_MCU_SCP_EN_SFT 17
+#define IRQ16_MCU_SCP_EN_SFT 16
+#define IRQ15_MCU_SCP_EN_SFT 15
+#define IRQ14_MCU_SCP_EN_SFT 14
+#define IRQ13_MCU_SCP_EN_SFT 13
+#define IRQ12_MCU_SCP_EN_SFT 12
+#define IRQ11_MCU_SCP_EN_SFT 11
+#define IRQ10_MCU_SCP_EN_SFT 10
+#define IRQ9_MCU_SCP_EN_SFT 9
+#define IRQ8_MCU_SCP_EN_SFT 8
+#define IRQ7_MCU_SCP_EN_SFT 7
+#define IRQ6_MCU_SCP_EN_SFT 6
+#define IRQ5_MCU_SCP_EN_SFT 5
+#define IRQ4_MCU_SCP_EN_SFT 4
+#define IRQ3_MCU_SCP_EN_SFT 3
+#define IRQ2_MCU_SCP_EN_SFT 2
+#define IRQ1_MCU_SCP_EN_SFT 1
+#define IRQ0_MCU_SCP_EN_SFT 0
+
+/* AFE_IRQ_MCU_DSP_EN */
+#define IRQ31_MCU_DSP_EN_SFT 31
+#define IRQ30_MCU_DSP_EN_SFT 30
+#define IRQ29_MCU_DSP_EN_SFT 29
+#define IRQ28_MCU_DSP_EN_SFT 28
+#define IRQ27_MCU_DSP_EN_SFT 27
+#define IRQ26_MCU_DSP_EN_SFT 26
+#define IRQ25_MCU_DSP_EN_SFT 25
+#define IRQ24_MCU_DSP_EN_SFT 24
+#define IRQ23_MCU_DSP_EN_SFT 23
+#define IRQ22_MCU_DSP_EN_SFT 22
+#define IRQ21_MCU_DSP_EN_SFT 21
+#define IRQ20_MCU_DSP_EN_SFT 20
+#define IRQ19_MCU_DSP_EN_SFT 19
+#define IRQ18_MCU_DSP_EN_SFT 18
+#define IRQ17_MCU_DSP_EN_SFT 17
+#define IRQ16_MCU_DSP_EN_SFT 16
+#define IRQ15_MCU_DSP_EN_SFT 15
+#define IRQ14_MCU_DSP_EN_SFT 14
+#define IRQ13_MCU_DSP_EN_SFT 13
+#define IRQ12_MCU_DSP_EN_SFT 12
+#define IRQ11_MCU_DSP_EN_SFT 11
+#define IRQ10_MCU_DSP_EN_SFT 10
+#define IRQ9_MCU_DSP_EN_SFT 9
+#define IRQ8_MCU_DSP_EN_SFT 8
+#define IRQ7_MCU_DSP_EN_SFT 7
+#define IRQ6_MCU_DSP_EN_SFT 6
+#define IRQ5_MCU_DSP_EN_SFT 5
+#define IRQ4_MCU_DSP_EN_SFT 4
+#define IRQ3_MCU_DSP_EN_SFT 3
+#define IRQ2_MCU_DSP_EN_SFT 2
+#define IRQ1_MCU_DSP_EN_SFT 1
+#define IRQ0_MCU_DSP_EN_SFT 0
+
+/* AFE_AUD_PAD_TOP */
+#define AUD_PAD_TOP_MON_SFT 15
+#define AUD_PAD_TOP_MON_MASK_SFT GENMASK(31, 15)
+#define AUD_PAD_TOP_FIFO_RSP_SFT 4
+#define AUD_PAD_TOP_FIFO_RSP_MASK_SFT GENMASK(7, 4)
+#define RG_RX_PROTOCOL2_SFT 3
+#define RG_RX_PROTOCOL2_MASK_SFT BIT(3)
+#define RESERVDED_01_SFT 1
+#define RESERVDED_01_MASK_SFT GENMASK(2, 1)
+#define RG_RX_FIFO_ON_SFT 0
+#define RG_RX_FIFO_ON_MASK_SFT BIT(0)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG */
+#define RG_ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE_SFT 23
+#define RG_ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE_MASK_SFT BIT(23)
+
+/* AFE_ADDA_MTKAIF_RX_CFG0 */
+#define MTKAIF_RXIF_VOICE_MODE_SFT 20
+#define MTKAIF_RXIF_VOICE_MODE_MASK_SFT GENMASK(23, 20)
+#define MTKAIF_RXIF_DETECT_ON_SFT 16
+#define MTKAIF_RXIF_DETECT_ON_MASK_SFT BIT(16)
+#define MTKAIF_RXIF_DATA_BIT_SFT 8
+#define MTKAIF_RXIF_DATA_BIT_MASK_SFT GENMASK(10, 8)
+#define MTKAIF_RXIF_FIFO_RSP_SFT 4
+#define MTKAIF_RXIF_FIFO_RSP_MASK_SFT GENMASK(6, 4)
+#define MTKAIF_RXIF_DATA_MODE_SFT 0
+#define MTKAIF_RXIF_DATA_MODE_MASK_SFT BIT(0)
+
+/* GENERAL_ASRC_MODE */
+#define GENERAL2_ASRCOUT_MODE_SFT 12
+#define GENERAL2_ASRCOUT_MODE_MASK 0xf
+#define GENERAL2_ASRCOUT_MODE_MASK_SFT GENMASK(15, 12)
+#define GENERAL2_ASRCIN_MODE_SFT 8
+#define GENERAL2_ASRCIN_MODE_MASK 0xf
+#define GENERAL2_ASRCIN_MODE_MASK_SFT GENMASK(11, 8)
+#define GENERAL1_ASRCOUT_MODE_SFT 4
+#define GENERAL1_ASRCOUT_MODE_MASK 0xf
+#define GENERAL1_ASRCOUT_MODE_MASK_SFT GENMASK(7, 4)
+#define GENERAL1_ASRCIN_MODE_SFT 0
+#define GENERAL1_ASRCIN_MODE_MASK 0xf
+#define GENERAL1_ASRCIN_MODE_MASK_SFT GENMASK(3, 0)
+
+/* GENERAL_ASRC_EN_ON */
+#define GENERAL2_ASRC_EN_ON_SFT 1
+#define GENERAL2_ASRC_EN_ON_MASK_SFT BIT(1)
+#define GENERAL1_ASRC_EN_ON_SFT 0
+#define GENERAL1_ASRC_EN_ON_MASK_SFT BIT(0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON0 */
+#define G_SRC_CHSET_STR_CLR_SFT 4
+#define G_SRC_CHSET_STR_CLR_MASK_SFT BIT(4)
+#define G_SRC_CHSET_ON_SFT 2
+#define G_SRC_CHSET_ON_MASK_SFT BIT(2)
+#define G_SRC_COEFF_SRAM_CTRL_SFT 1
+#define G_SRC_COEFF_SRAM_CTRL_MASK_SFT BIT(1)
+#define G_SRC_ASM_ON_SFT 0
+#define G_SRC_ASM_ON_MASK_SFT BIT(0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON3 */
+#define G_SRC_ASM_FREQ_4_SFT 0
+#define G_SRC_ASM_FREQ_4_MASK_SFT GENMASK(23, 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON4 */
+#define G_SRC_ASM_FREQ_5_SFT 0
+#define G_SRC_ASM_FREQ_5_MASK_SFT GENMASK(23, 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON13 */
+#define G_SRC_COEFF_SRAM_ADR_SFT 0
+#define G_SRC_COEFF_SRAM_ADR_MASK_SFT GENMASK(5, 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON2 */
+#define G_SRC_CHSET_O16BIT_SFT 19
+#define G_SRC_CHSET_O16BIT_MASK_SFT BIT(19)
+#define G_SRC_CHSET_CLR_IIR_HISTORY_SFT 17
+#define G_SRC_CHSET_CLR_IIR_HISTORY_MASK_SFT BIT(17)
+#define G_SRC_CHSET_IS_MONO_SFT 16
+#define G_SRC_CHSET_IS_MONO_MASK_SFT BIT(16)
+#define G_SRC_CHSET_IIR_EN_SFT 11
+#define G_SRC_CHSET_IIR_EN_MASK_SFT BIT(11)
+#define G_SRC_CHSET_IIR_STAGE_SFT 8
+#define G_SRC_CHSET_IIR_STAGE_MASK_SFT GENMASK(10, 8)
+#define G_SRC_CHSET_STR_CLR_RU_SFT 5
+#define G_SRC_CHSET_STR_CLR_RU_MASK_SFT BIT(5)
+#define G_SRC_CHSET_ON_SFT 2
+#define G_SRC_CHSET_ON_MASK_SFT BIT(2)
+#define G_SRC_COEFF_SRAM_CTRL_SFT 1
+#define G_SRC_COEFF_SRAM_CTRL_MASK_SFT BIT(1)
+#define G_SRC_ASM_ON_SFT 0
+#define G_SRC_ASM_ON_MASK_SFT BIT(0)
+
+/* AFE_ADDA_DL_SDM_DITHER_CON */
+#define AFE_DL_SDM_DITHER_64TAP_EN_SFT 20
+#define AFE_DL_SDM_DITHER_64TAP_EN_MASK_SFT BIT(20)
+#define AFE_DL_SDM_DITHER_EN_SFT 16
+#define AFE_DL_SDM_DITHER_EN_MASK_SFT BIT(16)
+#define AFE_DL_SDM_DITHER_GAIN_SFT 0
+#define AFE_DL_SDM_DITHER_GAIN_MASK_SFT GENMASK(7, 0)
+
+/* AFE_ADDA_DL_SDM_AUTO_RESET_CON */
+#define SDM_AUTO_RESET_TEST_ON_SFT 31
+#define SDM_AUTO_RESET_TEST_ON_MASK_SFT BIT(31)
+#define AFE_DL_USE_NEW_2ND_SDM_SFT 28
+#define AFE_DL_USE_NEW_2ND_SDM_MASK_SFT BIT(28)
+#define SDM_AUTO_RESET_COUNT_TH_SFT 0
+#define SDM_AUTO_RESET_COUNT_TH_MASK_SFT GENMASK(23, 0)
+
+/* AFE_ASRC_2CH_CON0 */
+#define CON0_CHSET_STR_CLR_SFT 4
+#define CON0_CHSET_STR_CLR_MASK_SFT BIT(4)
+#define CON0_ASM_ON_SFT 0
+#define CON0_ASM_ON_MASK_SFT BIT(0)
+
+/* AFE_ASRC_2CH_CON5 */
+#define CALI_EN_SFT 0
+#define CALI_EN_MASK_SFT BIT(0)
+
+/* FPGA_CFG4 */
+#define IRQ_COUNTER_SFT 3
+#define IRQ_COUNTER_MASK_SFT GENMASK(31, 3)
+#define IRQ_CLK_COUNTER_CLEAN_SFT 2
+#define IRQ_CLK_COUNTER_CLEAN_MASK_SFT BIT(2)
+#define IRQ_CLK_COUNTER_PAUSE_SFT 1
+#define IRQ_CLK_COUNTER_PAUSE_MASK_SFT BIT(1)
+#define IRQ_CLK_COUNTER_ON_SFT 0
+#define IRQ_CLK_COUNTER_ON_MASK_SFT BIT(0)
+
+/* FPGA_CFG5 */
+#define WR_MSTR_ON_SFT 16
+#define WR_MSTR_ON_MASK_SFT GENMASK(28, 16)
+#define WR_AG_SEL_SFT 0
+#define WR_AG_SEL_MASK_SFT GENMASK(12, 0)
+
+/* FPGA_CFG6 */
+#define WR_MSTR_REQ_REAL_SFT 16
+#define WR_MSTR_REQ_REAL_MASK_SFT GENMASK(28, 16)
+#define WR_MSTR_REQ_IN_SFT 0
+#define WR_MSTR_REQ_IN_MASK_SFT GENMASK(12, 0)
+
+/* FPGA_CFG7 */
+#define MEM1_WDATA_MON0_SFT 0
+#define MEM1_WDATA_MON0_MASK_SFT GENMASK(31, 0)
+
+/* FPGA_CFG8 */
+#define MEM1_WDATA_MON1_SFT 0
+#define MEM1_WDATA_MON1_MASK_SFT GENMASK(31, 0)
+
+/* FPGA_CFG9 */
+#define MEM_WE_SFT 31
+#define MEM_WE_MASK_SFT BIT(31)
+#define AFE_HREADY_SFT 30
+#define AFE_HREADY_MASK_SFT BIT(30)
+#define MEM_WR_REQ_SFT 29
+#define MEM_WR_REQ_MASK_SFT BIT(29)
+#define WR_AG_REG_MON_SFT 16
+#define WR_AG_REG_MON_MASK_SFT GENMASK(28, 16)
+#define HCLK_CK_SFT 15
+#define HCLK_CK_MASK_SFT BIT(15)
+#define MEM_RD_REQ_SFT 14
+#define MEM_RD_REQ_MASK_SFT BIT(14)
+#define RD_AG_REQ_MON_SFT 0
+#define RD_AG_REQ_MON_MASK_SFT GENMASK(13, 0)
+
+/* FPGA_CFG10 */
+#define MEM_BYTE_0_SFT 0
+#define MEM_BYTE_0_MASK_SFT GENMASK(31, 0)
+
+/* FPGA_CFG11 */
+#define MEM_BYTE_1_SFT 0
+#define MEM_BYTE_1_MASK_SFT GENMASK(31, 0)
+
+/* FPGA_CFG12 */
+#define RDATA_CNT_SFT 30
+#define RDATA_CNT_MASK_SFT GENMASK(31, 30)
+#define MS2_HREADY_SFT 29
+#define MS2_HREADY_MASK_SFT BIT(29)
+#define MS1_HREADY_SFT 28
+#define MS1_HREADY_MASK_SFT BIT(28)
+#define AG_SEL_SFT 0
+#define AG_SEL_MASK_SFT GENMASK(25, 0)
+
+/* FPGA_CFG13 */
+#define AFE_ST_SFT 27
+#define AFE_ST_MASK_SFT GENMASK(31, 27)
+#define AG_IN_SERVICE_SFT 0
+#define AG_IN_SERVICE_MASK_SFT GENMASK(25, 0)
+
+/* ETDM_IN1_CON0 */
+#define ETDM_IN1_CON0_REG_ETDM_IN_EN_SFT 0
+#define ETDM_IN1_CON0_REG_ETDM_IN_EN_MASK_SFT BIT(0)
+#define ETDM_IN1_CON0_REG_SYNC_MODE_SFT 1
+#define ETDM_IN1_CON0_REG_SYNC_MODE_MASK_SFT BIT(1)
+#define ETDM_IN1_CON0_REG_LSB_FIRST_SFT 3
+#define ETDM_IN1_CON0_REG_LSB_FIRST_MASK_SFT BIT(3)
+#define ETDM_IN1_CON0_REG_SOFT_RST_SFT 4
+#define ETDM_IN1_CON0_REG_SOFT_RST_MASK_SFT BIT(4)
+#define ETDM_IN1_CON0_REG_SLAVE_MODE_SFT 5
+#define ETDM_IN1_CON0_REG_SLAVE_MODE_MASK_SFT BIT(5)
+#define ETDM_IN1_CON0_REG_FMT_SFT 6
+#define ETDM_IN1_CON0_REG_FMT_MASK_SFT GENMASK(8, 6)
+#define ETDM_IN1_CON0_REG_LRCK_EDGE_SEL_SFT 10
+#define ETDM_IN1_CON0_REG_LRCK_EDGE_SEL_MASK_SFT BIT(10)
+#define ETDM_IN1_CON0_REG_BIT_LENGTH_SFT 11
+#define ETDM_IN1_CON0_REG_BIT_LENGTH_MASK_SFT GENMASK(15, 11)
+#define ETDM_IN1_CON0_REG_WORD_LENGTH_SFT 16
+#define ETDM_IN1_CON0_REG_WORD_LENGTH_MASK_SFT GENMASK(20, 16)
+#define ETDM_IN1_CON0_REG_CH_NUM_SFT 23
+#define ETDM_IN1_CON0_REG_CH_NUM_MASK_SFT GENMASK(27, 23)
+#define ETDM_IN1_CON0_REG_RELATCH_1X_EN_SEL_DOMAIN_SFT 28
+#define ETDM_IN1_CON0_REG_RELATCH_1X_EN_SEL_DOMAIN_MASK_SFT GENMASK(31, 28)
+#define ETDM_IN1_CON0_REG_VALID_TOGETHER_SFT 31
+#define ETDM_IN1_CON0_REG_VALID_TOGETHER_MASK_SFT BIT(31)
+#define ETDM_IN_CON0_CTRL_MASK 0x1f9ff9e2
+
+/* ETDM_IN1_CON1 */
+#define ETDM_IN1_CON1_REG_INITIAL_COUNT_SFT 0
+#define ETDM_IN1_CON1_REG_INITIAL_COUNT_MASK_SFT GENMASK(4, 0)
+#define ETDM_IN1_CON1_REG_INITIAL_POINT_SFT 5
+#define ETDM_IN1_CON1_REG_INITIAL_POINT_MASK_SFT GENMASK(9, 5)
+#define ETDM_IN1_CON1_REG_LRCK_AUTO_OFF_SFT 10
+#define ETDM_IN1_CON1_REG_LRCK_AUTO_OFF_MASK_SFT BIT(10)
+#define ETDM_IN1_CON1_REG_BCK_AUTO_OFF_SFT 11
+#define ETDM_IN1_CON1_REG_BCK_AUTO_OFF_MASK_SFT BIT(11)
+#define ETDM_IN1_CON1_REG_INITIAL_LRCK_SFT 13
+#define ETDM_IN1_CON1_REG_INITIAL_LRCK_MASK_SFT BIT(13)
+#define ETDM_IN1_CON1_REG_LRCK_RESET_SFT 15
+#define ETDM_IN1_CON1_REG_LRCK_RESET_MASK_SFT BIT(15)
+#define ETDM_IN1_CON1_PINMUX_MCLK_CTRL_OE_SFT 16
+#define ETDM_IN1_CON1_PINMUX_MCLK_CTRL_OE_MASK_SFT BIT(16)
+#define ETDM_IN1_CON1_REG_OUTPUT_CR_EN_SFT 18
+#define ETDM_IN1_CON1_REG_OUTPUT_CR_EN_MASK_SFT BIT(18)
+#define ETDM_IN1_CON1_REG_LR_ALIGN_SFT 19
+#define ETDM_IN1_CON1_REG_LR_ALIGN_MASK_SFT BIT(19)
+#define ETDM_IN1_CON1_REG_LRCK_WIDTH_SFT 20
+#define ETDM_IN1_CON1_REG_LRCK_WIDTH_MASK_SFT GENMASK(29, 20)
+#define ETDM_IN1_CON1_REG_DIRECT_INPUT_MASTER_BCK_SFT 30
+#define ETDM_IN1_CON1_REG_DIRECT_INPUT_MASTER_BCK_MASK_SFT BIT(30)
+#define ETDM_IN1_CON1_REG_LRCK_AUTO_MODE_SFT 31
+#define ETDM_IN1_CON1_REG_LRCK_AUTO_MODE_MASK_SFT BIT(31)
+#define ETDM_IN_CON1_CTRL_MASK 0xbff10000
+
+/* ETDM_IN1_CON2 */
+#define ETDM_IN1_CON2_REG_UPDATE_POINT_SFT 0
+#define ETDM_IN1_CON2_REG_UPDATE_POINT_MASK_SFT GENMASK(4, 0)
+#define ETDM_IN1_CON2_REG_UPDATE_GAP_SFT 5
+#define ETDM_IN1_CON2_REG_UPDATE_GAP_MASK_SFT GENMASK(9, 5)
+#define ETDM_IN1_CON2_REG_CLOCK_SOURCE_SEL_SFT 10
+#define ETDM_IN1_CON2_REG_CLOCK_SOURCE_SEL_MASK_SFT GENMASK(12, 10)
+#define ETDM_IN1_CON2_REG_AGENT_USE_ETDM_BCK_SFT 13
+#define ETDM_IN1_CON2_REG_AGENT_USE_ETDM_BCK_MASK_SFT BIT(13)
+#define ETDM_IN1_CON2_REG_CK_EN_SEL_AUTO_SFT 14
+#define ETDM_IN1_CON2_REG_CK_EN_SEL_AUTO_MASK_SFT BIT(14)
+#define ETDM_IN1_CON2_REG_MULTI_IP_ONE_DATA_CH_NUM_SFT 15
+#define ETDM_IN1_CON2_REG_MULTI_IP_ONE_DATA_CH_NUM_MASK_SFT GENMASK(19, 15)
+#define ETDM_IN1_CON2_REG_MASK_AUTO_SFT 20
+#define ETDM_IN1_CON2_REG_MASK_AUTO_MASK_SFT BIT(20)
+#define ETDM_IN1_CON2_REG_MASK_NUM_SFT 21
+#define ETDM_IN1_CON2_REG_MASK_NUM_MASK_SFT GENMASK(25, 21)
+#define ETDM_IN1_CON2_REG_UPDATE_POINT_AUTO_SFT 26
+#define ETDM_IN1_CON2_REG_UPDATE_POINT_AUTO_MASK_SFT BIT(26)
+#define ETDM_IN1_CON2_REG_SDATA_DELAY_0P5T_EN_SFT 27
+#define ETDM_IN1_CON2_REG_SDATA_DELAY_0P5T_EN_MASK_SFT BIT(27)
+#define ETDM_IN1_CON2_REG_SDATA_DELAY_BCK_INV_SFT 28
+#define ETDM_IN1_CON2_REG_SDATA_DELAY_BCK_INV_MASK_SFT BIT(28)
+#define ETDM_IN1_CON2_REG_LRCK_DELAY_0P5T_EN_SFT 29
+#define ETDM_IN1_CON2_REG_LRCK_DELAY_0P5T_EN_MASK_SFT BIT(29)
+#define ETDM_IN1_CON2_REG_LRCK_DELAY_BCK_INV_SFT 30
+#define ETDM_IN1_CON2_REG_LRCK_DELAY_BCK_INV_MASK_SFT BIT(30)
+#define ETDM_IN1_CON2_REG_MULTI_IP_MODE_SFT 31
+#define ETDM_IN1_CON2_REG_MULTI_IP_MODE_MASK_SFT BIT(31)
+#define ETDM_IN_CON2_CTRL_MASK 0x800f8000
+#define ETDM_IN_CON2_MULTI_IP_CH(x) (((x) - 1) << 15)
+#define ETDM_IN_CON2_MULTI_IP_2CH_MODE BIT(31)
+
+/* ETDM_IN1_CON3 */
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_0_SFT 0
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_0_MASK_SFT BIT(0)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_1_SFT 1
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_1_MASK_SFT BIT(1)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_2_SFT 2
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_2_MASK_SFT BIT(2)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_3_SFT 3
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_3_MASK_SFT BIT(3)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_4_SFT 4
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_4_MASK_SFT BIT(4)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_5_SFT 5
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_5_MASK_SFT BIT(5)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_6_SFT 6
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_6_MASK_SFT BIT(6)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_7_SFT 7
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_7_MASK_SFT BIT(7)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_8_SFT 8
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_8_MASK_SFT BIT(8)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_9_SFT 9
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_9_MASK_SFT BIT(9)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_10_SFT 10
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_10_MASK_SFT BIT(10)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_11_SFT 11
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_11_MASK_SFT BIT(11)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_12_SFT 12
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_12_MASK_SFT BIT(12)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_13_SFT 13
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_13_MASK_SFT BIT(13)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_14_SFT 14
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_14_MASK_SFT BIT(14)
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_15_SFT 15
+#define ETDM_IN1_CON3_REG_DISABLE_OUT_15_MASK_SFT BIT(15)
+#define ETDM_IN1_CON3_REG_RJ_DATA_RIGHT_ALIGN_SFT 16
+#define ETDM_IN1_CON3_REG_RJ_DATA_RIGHT_ALIGN_MASK_SFT BIT(16)
+#define ETDM_IN1_CON3_REG_MONITOR_SEL_SFT 17
+#define ETDM_IN1_CON3_REG_MONITOR_SEL_MASK_SFT GENMASK(18, 17)
+#define ETDM_IN1_CON3_REG_CNT_UPPER_LIMIT_SFT 19
+#define ETDM_IN1_CON3_REG_CNT_UPPER_LIMIT_MASK_SFT GENMASK(24, 19)
+#define ETDM_IN1_CON3_REG_COMPACT_SAMPLE_END_DIS_SFT 25
+#define ETDM_IN1_CON3_REG_COMPACT_SAMPLE_END_DIS_MASK_SFT BIT(25)
+#define ETDM_IN1_CON3_REG_FS_TIMING_SEL_SFT 26
+#define ETDM_IN1_CON3_REG_FS_TIMING_SEL_MASK_SFT GENMASK(30, 26)
+#define ETDM_IN1_CON3_REG_SAMPLE_END_MODE_SFT 31
+#define ETDM_IN1_CON3_REG_SAMPLE_END_MODE_MASK_SFT BIT(31)
+#define ETDM_IN_CON3_CTRL_MASK (0x7c000000)
+#define ETDM_IN_CON3_FS(x) (((x) & 0x1f) << 26)
+
+/* ETDM_IN1_CON4 */
+#define ETDM_IN1_CON4_REG_DSD_MODE_SFT 0
+#define ETDM_IN1_CON4_REG_DSD_MODE_MASK_SFT GENMASK(5, 0)
+#define ETDM_IN1_CON4_REG_DSD_REPACK_AUTO_MODE_SFT 8
+#define ETDM_IN1_CON4_REG_DSD_REPACK_AUTO_MODE_MASK_SFT BIT(8)
+#define ETDM_IN1_CON4_REG_REPACK_WORD_LENGTH_SFT 9
+#define ETDM_IN1_CON4_REG_REPACK_WORD_LENGTH_MASK_SFT GENMASK(10, 9)
+#define ETDM_IN1_CON4_REG_ASYNC_RESET_SFT 11
+#define ETDM_IN1_CON4_REG_ASYNC_RESET_MASK_SFT BIT(11)
+#define ETDM_IN1_CON4_REG_DSD_CHNUM_SFT 12
+#define ETDM_IN1_CON4_REG_DSD_CHNUM_MASK_SFT GENMASK(15, 12)
+#define ETDM_IN1_CON4_REG_SLAVE_BCK_INV_SFT 16
+#define ETDM_IN1_CON4_REG_SLAVE_BCK_INV_MASK_SFT BIT(16)
+#define ETDM_IN1_CON4_REG_SLAVE_LRCK_INV_SFT 17
+#define ETDM_IN1_CON4_REG_SLAVE_LRCK_INV_MASK_SFT BIT(17)
+#define ETDM_IN1_CON4_REG_MASTER_BCK_INV_SFT 18
+#define ETDM_IN1_CON4_REG_MASTER_BCK_INV_MASK_SFT BIT(18)
+#define ETDM_IN1_CON4_REG_MASTER_LRCK_INV_SFT 19
+#define ETDM_IN1_CON4_REG_MASTER_LRCK_INV_MASK_SFT BIT(19)
+#define ETDM_IN1_CON4_REG_RELATCH_1X_EN_SEL_SFT 20
+#define ETDM_IN1_CON4_REG_RELATCH_1X_EN_SEL_MASK_SFT GENMASK(24, 20)
+#define ETDM_IN1_CON4_REG_SAMPLE_END_POINT_SFT 25
+#define ETDM_IN1_CON4_REG_SAMPLE_END_POINT_MASK_SFT GENMASK(29, 25)
+#define ETDM_IN1_CON4_REG_WAIT_LAST_SAMPLE_SFT 30
+#define ETDM_IN1_CON4_REG_WAIT_LAST_SAMPLE_MASK_SFT BIT(30)
+#define ETDM_IN1_CON4_REG_MASTER_BCK_FORCE_ON_SFT 31
+#define ETDM_IN1_CON4_REG_MASTER_BCK_FORCE_ON_MASK_SFT BIT(31)
+#define ETDM_IN_CON4_CTRL_MASK 0x1ff0000
+#define ETDM_IN_CON4_FS(x) (((x) & 0x1f) << 20)
+#define ETDM_IN_CON4_CON0_MASTER_LRCK_INV BIT(19)
+#define ETDM_IN_CON4_CON0_MASTER_BCK_INV BIT(18)
+#define ETDM_IN_CON4_CON0_SLAVE_LRCK_INV BIT(17)
+#define ETDM_IN_CON4_CON0_SLAVE_BCK_INV BIT(16)
+
+/* ETDM_IN1_CON5 */
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_0_SFT 0
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_0_MASK_SFT BIT(0)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_1_SFT 1
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_1_MASK_SFT BIT(1)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_2_SFT 2
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_2_MASK_SFT BIT(2)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_3_SFT 3
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_3_MASK_SFT BIT(3)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_4_SFT 4
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_4_MASK_SFT BIT(4)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_5_SFT 5
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_5_MASK_SFT BIT(5)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_6_SFT 6
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_6_MASK_SFT BIT(6)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_7_SFT 7
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_7_MASK_SFT BIT(7)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_8_SFT 8
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_8_MASK_SFT BIT(8)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_9_SFT 9
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_9_MASK_SFT BIT(9)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_10_SFT 10
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_10_MASK_SFT BIT(10)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_11_SFT 11
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_11_MASK_SFT BIT(11)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_12_SFT 12
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_12_MASK_SFT BIT(12)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_13_SFT 13
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_13_MASK_SFT BIT(13)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_14_SFT 14
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_14_MASK_SFT BIT(14)
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_15_SFT 15
+#define ETDM_IN1_CON5_REG_ODD_FLAG_EN_15_MASK_SFT BIT(15)
+#define ETDM_IN1_CON5_REG_LR_SWAP_0_SFT 16
+#define ETDM_IN1_CON5_REG_LR_SWAP_0_MASK_SFT BIT(16)
+#define ETDM_IN1_CON5_REG_LR_SWAP_1_SFT 17
+#define ETDM_IN1_CON5_REG_LR_SWAP_1_MASK_SFT BIT(17)
+#define ETDM_IN1_CON5_REG_LR_SWAP_2_SFT 18
+#define ETDM_IN1_CON5_REG_LR_SWAP_2_MASK_SFT BIT(18)
+#define ETDM_IN1_CON5_REG_LR_SWAP_3_SFT 19
+#define ETDM_IN1_CON5_REG_LR_SWAP_3_MASK_SFT BIT(19)
+#define ETDM_IN1_CON5_REG_LR_SWAP_4_SFT 20
+#define ETDM_IN1_CON5_REG_LR_SWAP_4_MASK_SFT BIT(20)
+#define ETDM_IN1_CON5_REG_LR_SWAP_5_SFT 21
+#define ETDM_IN1_CON5_REG_LR_SWAP_5_MASK_SFT BIT(21)
+#define ETDM_IN1_CON5_REG_LR_SWAP_6_SFT 22
+#define ETDM_IN1_CON5_REG_LR_SWAP_6_MASK_SFT BIT(22)
+#define ETDM_IN1_CON5_REG_LR_SWAP_7_SFT 23
+#define ETDM_IN1_CON5_REG_LR_SWAP_7_MASK_SFT BIT(23)
+#define ETDM_IN1_CON5_REG_LR_SWAP_8_SFT 24
+#define ETDM_IN1_CON5_REG_LR_SWAP_8_MASK_SFT BIT(24)
+#define ETDM_IN1_CON5_REG_LR_SWAP_9_SFT 25
+#define ETDM_IN1_CON5_REG_LR_SWAP_9_MASK_SFT BIT(25)
+#define ETDM_IN1_CON5_REG_LR_SWAP_10_SFT 26
+#define ETDM_IN1_CON5_REG_LR_SWAP_10_MASK_SFT BIT(26)
+#define ETDM_IN1_CON5_REG_LR_SWAP_11_SFT 27
+#define ETDM_IN1_CON5_REG_LR_SWAP_11_MASK_SFT BIT(27)
+#define ETDM_IN1_CON5_REG_LR_SWAP_12_SFT 28
+#define ETDM_IN1_CON5_REG_LR_SWAP_12_MASK_SFT BIT(28)
+#define ETDM_IN1_CON5_REG_LR_SWAP_13_SFT 29
+#define ETDM_IN1_CON5_REG_LR_SWAP_13_MASK_SFT BIT(29)
+#define ETDM_IN1_CON5_REG_LR_SWAP_14_SFT 30
+#define ETDM_IN1_CON5_REG_LR_SWAP_14_MASK_SFT BIT(30)
+#define ETDM_IN1_CON5_REG_LR_SWAP_15_SFT 31
+#define ETDM_IN1_CON5_REG_LR_SWAP_15_MASK_SFT BIT(31)
+
+/* ETDM_IN1_CON6 */
+#define ETDM_IN1_CON6_LCH_DATA_REG_SFT 0
+#define ETDM_IN1_CON6_LCH_DATA_REG_MASK_SFT GENMASK(31, 0)
+
+/* ETDM_IN1_CON7 */
+#define ETDM_IN1_CON7_RCH_DATA_REG_SFT 0
+#define ETDM_IN1_CON7_RCH_DATA_REG_MASK_SFT GENMASK(31, 0)
+
+/* ETDM_IN1_CON8 */
+#define ETDM_IN1_CON8_REG_AFIFO_THRESHOLD_SFT 29
+#define ETDM_IN1_CON8_REG_AFIFO_THRESHOLD_MASK_SFT GENMASK(30, 29)
+#define ETDM_IN1_CON8_REG_CK_EN_SEL_MANUAL_SFT 16
+#define ETDM_IN1_CON8_REG_CK_EN_SEL_MANUAL_MASK_SFT GENMASK(25, 16)
+#define ETDM_IN1_CON8_REG_AFIFO_SW_RESET_SFT 15
+#define ETDM_IN1_CON8_REG_AFIFO_SW_RESET_MASK_SFT BIT(15)
+#define ETDM_IN1_CON8_REG_AFIFO_RESET_SEL_SFT 14
+#define ETDM_IN1_CON8_REG_AFIFO_RESET_SEL_MASK_SFT BIT(14)
+#define ETDM_IN1_CON8_REG_AFIFO_AUTO_RESET_DIS_SFT 9
+#define ETDM_IN1_CON8_REG_AFIFO_AUTO_RESET_DIS_MASK_SFT BIT(9)
+#define ETDM_IN1_CON8_REG_ETDM_USE_AFIFO_SFT 8
+#define ETDM_IN1_CON8_REG_ETDM_USE_AFIFO_MASK_SFT BIT(8)
+#define ETDM_IN1_CON8_REG_AFIFO_CLOCK_DOMAIN_SEL_SFT 5
+#define ETDM_IN1_CON8_REG_AFIFO_CLOCK_DOMAIN_SEL_MASK_SFT GENMASK(7, 5)
+#define ETDM_IN1_CON8_REG_AFIFO_MODE_SFT 0
+#define ETDM_IN1_CON8_REG_AFIFO_MODE_MASK_SFT GENMASK(4, 0)
+#define ETDM_IN_CON8_FS(x) (((x) & 0x1f) << 0)
+#define ETDM_IN_CON8_CTRL_MASK 0x13f
+
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON1 0x0004
+#define AUDIO_TOP_CON2 0x0008
+#define AUDIO_TOP_CON3 0x000c
+#define AFE_DAC_CON0 0x0010
+#define AFE_I2S_CON 0x0018
+#define AFE_CONN0 0x0020
+#define AFE_CONN1 0x0024
+#define AFE_CONN2 0x0028
+#define AFE_CONN3 0x002c
+#define AFE_CONN4 0x0030
+#define AFE_I2S_CON1 0x0034
+#define AFE_I2S_CON2 0x0038
+#define AFE_I2S_CON3 0x0040
+#define AFE_CONN5 0x0044
+#define AFE_CONN_24BIT 0x0048
+#define AFE_DL1_CON0 0x004c
+#define AFE_DL1_BASE_MSB 0x0050
+#define AFE_DL1_BASE 0x0054
+#define AFE_DL1_CUR_MSB 0x0058
+#define AFE_DL1_CUR 0x005c
+#define AFE_DL1_END_MSB 0x0060
+#define AFE_DL1_END 0x0064
+#define AFE_DL2_CON0 0x0068
+#define AFE_DL2_BASE_MSB 0x006c
+#define AFE_DL2_BASE 0x0070
+#define AFE_DL2_CUR_MSB 0x0074
+#define AFE_DL2_CUR 0x0078
+#define AFE_DL2_END_MSB 0x007c
+#define AFE_DL2_END 0x0080
+#define AFE_DL3_CON0 0x0084
+#define AFE_DL3_BASE_MSB 0x0088
+#define AFE_DL3_BASE 0x008c
+#define AFE_DL3_CUR_MSB 0x0090
+#define AFE_DL3_CUR 0x0094
+#define AFE_DL3_END_MSB 0x0098
+#define AFE_DL3_END 0x009c
+#define AFE_CONN6 0x00bc
+#define AFE_DL4_CON0 0x00cc
+#define AFE_DL4_BASE_MSB 0x00d0
+#define AFE_DL4_BASE 0x00d4
+#define AFE_DL4_CUR_MSB 0x00d8
+#define AFE_DL4_CUR 0x00dc
+#define AFE_DL4_END_MSB 0x00e0
+#define AFE_DL4_END 0x00e4
+#define AFE_DL12_CON0 0x00e8
+#define AFE_DL12_BASE_MSB 0x00ec
+#define AFE_DL12_BASE 0x00f0
+#define AFE_DL12_CUR_MSB 0x00f4
+#define AFE_DL12_CUR 0x00f8
+#define AFE_DL12_END_MSB 0x00fc
+#define AFE_DL12_END 0x0100
+#define AFE_ADDA_DL_SRC2_CON0 0x0108
+#define AFE_ADDA_DL_SRC2_CON1 0x010c
+#define AFE_ADDA_UL_SRC_CON0 0x0114
+#define AFE_ADDA_UL_SRC_CON1 0x0118
+#define AFE_ADDA_TOP_CON0 0x0120
+#define AFE_ADDA_UL_DL_CON0 0x0124
+#define AFE_ADDA_SRC_DEBUG 0x012c
+#define AFE_ADDA_SRC_DEBUG_MON0 0x0130
+#define AFE_ADDA_SRC_DEBUG_MON1 0x0134
+#define AFE_ADDA_UL_SRC_MON0 0x0148
+#define AFE_ADDA_UL_SRC_MON1 0x014c
+#define AFE_SECURE_CON0 0x0150
+#define AFE_SRAM_BOUND 0x0154
+#define AFE_SECURE_CON1 0x0158
+#define AFE_SECURE_CONN0 0x015c
+#define AFE_VUL_CON0 0x0170
+#define AFE_VUL_BASE_MSB 0x0174
+#define AFE_VUL_BASE 0x0178
+#define AFE_VUL_CUR_MSB 0x017c
+#define AFE_VUL_CUR 0x0180
+#define AFE_VUL_END_MSB 0x0184
+#define AFE_VUL_END 0x0188
+#define AFE_SIDETONE_DEBUG 0x01d0
+#define AFE_SIDETONE_MON 0x01d4
+#define AFE_SINEGEN_CON2 0x01dc
+#define AFE_SIDETONE_CON0 0x01e0
+#define AFE_SIDETONE_COEFF 0x01e4
+#define AFE_SIDETONE_CON1 0x01e8
+#define AFE_SIDETONE_GAIN 0x01ec
+#define AFE_SINEGEN_CON0 0x01f0
+#define AFE_TOP_CON0 0x0200
+#define AFE_VUL2_CON0 0x020c
+#define AFE_VUL2_BASE_MSB 0x0210
+#define AFE_VUL2_BASE 0x0214
+#define AFE_VUL2_CUR_MSB 0x0218
+#define AFE_VUL2_CUR 0x021c
+#define AFE_VUL2_END_MSB 0x0220
+#define AFE_VUL2_END 0x0224
+#define AFE_VUL3_CON0 0x0228
+#define AFE_VUL3_BASE_MSB 0x022c
+#define AFE_VUL3_BASE 0x0230
+#define AFE_VUL3_CUR_MSB 0x0234
+#define AFE_VUL3_CUR 0x0238
+#define AFE_VUL3_END_MSB 0x023c
+#define AFE_VUL3_END 0x0240
+#define AFE_BUSY 0x0244
+#define AFE_BUS_CFG 0x0250
+#define AFE_ADDA_PREDIS_CON0 0x0260
+#define AFE_ADDA_PREDIS_CON1 0x0264
+#define AFE_I2S_MON 0x027c
+#define AFE_ADDA_IIR_COEF_02_01 0x0290
+#define AFE_ADDA_IIR_COEF_04_03 0x0294
+#define AFE_ADDA_IIR_COEF_06_05 0x0298
+#define AFE_ADDA_IIR_COEF_08_07 0x029c
+#define AFE_ADDA_IIR_COEF_10_09 0x02a0
+#define AFE_IRQ_MCU_CON1 0x02e4
+#define AFE_IRQ_MCU_CON2 0x02e8
+#define AFE_DAC_MON 0x02ec
+#define AFE_IRQ_MCU_CON3 0x02f0
+#define AFE_IRQ_MCU_CON4 0x02f4
+#define AFE_IRQ_MCU_CNT0 0x0300
+#define AFE_IRQ_MCU_CNT6 0x0304
+#define AFE_IRQ_MCU_CNT8 0x0308
+#define AFE_IRQ_MCU_DSP2_EN 0x030c
+#define AFE_IRQ0_MCU_CNT_MON 0x0310
+#define AFE_IRQ6_MCU_CNT_MON 0x0314
+#define AFE_VUL4_CON0 0x0358
+#define AFE_VUL4_BASE_MSB 0x035c
+#define AFE_VUL4_BASE 0x0360
+#define AFE_VUL4_CUR_MSB 0x0364
+#define AFE_VUL4_CUR 0x0368
+#define AFE_VUL4_END_MSB 0x036c
+#define AFE_VUL4_END 0x0370
+#define AFE_VUL12_CON0 0x0374
+#define AFE_VUL12_BASE_MSB 0x0378
+#define AFE_VUL12_BASE 0x037c
+#define AFE_VUL12_CUR_MSB 0x0380
+#define AFE_VUL12_CUR 0x0384
+#define AFE_VUL12_END_MSB 0x0388
+#define AFE_VUL12_END 0x038c
+#define AFE_IRQ3_MCU_CNT_MON 0x0398
+#define AFE_IRQ4_MCU_CNT_MON 0x039c
+#define AFE_IRQ_MCU_CON0 0x03a0
+#define AFE_IRQ_MCU_STATUS 0x03a4
+#define AFE_IRQ_MCU_CLR 0x03a8
+#define AFE_IRQ_MCU_CNT1 0x03ac
+#define AFE_IRQ_MCU_CNT2 0x03b0
+#define AFE_IRQ_MCU_EN 0x03b4
+#define AFE_IRQ_MCU_MON2 0x03b8
+#define AFE_IRQ_MCU_CNT5 0x03bc
+#define AFE_IRQ1_MCU_CNT_MON 0x03c0
+#define AFE_IRQ2_MCU_CNT_MON 0x03c4
+#define AFE_IRQ5_MCU_CNT_MON 0x03cc
+#define AFE_IRQ_MCU_DSP_EN 0x03d0
+#define AFE_IRQ_MCU_SCP_EN 0x03d4
+#define AFE_IRQ_MCU_CNT7 0x03dc
+#define AFE_IRQ7_MCU_CNT_MON 0x03e0
+#define AFE_IRQ_MCU_CNT3 0x03e4
+#define AFE_IRQ_MCU_CNT4 0x03e8
+#define AFE_IRQ_MCU_CNT11 0x03ec
+#define AFE_APLL1_TUNER_CFG 0x03f0
+#define AFE_APLL2_TUNER_CFG 0x03f4
+#define AFE_IRQ_MCU_MISS_CLR 0x03f8
+#define AFE_CONN33 0x0408
+#define AFE_IRQ_MCU_CNT12 0x040c
+#define AFE_GAIN1_CON0 0x0410
+#define AFE_GAIN1_CON1 0x0414
+#define AFE_GAIN1_CON2 0x0418
+#define AFE_GAIN1_CON3 0x041c
+#define AFE_CONN7 0x0420
+#define AFE_GAIN1_CUR 0x0424
+#define AFE_GAIN2_CON0 0x0428
+#define AFE_GAIN2_CON1 0x042c
+#define AFE_GAIN2_CON2 0x0430
+#define AFE_GAIN2_CON3 0x0434
+#define AFE_CONN8 0x0438
+#define AFE_GAIN2_CUR 0x043c
+#define AFE_CONN9 0x0440
+#define AFE_CONN10 0x0444
+#define AFE_CONN11 0x0448
+#define AFE_CONN12 0x044c
+#define AFE_CONN13 0x0450
+#define AFE_CONN14 0x0454
+#define AFE_CONN15 0x0458
+#define AFE_CONN16 0x045c
+#define AFE_CONN17 0x0460
+#define AFE_CONN18 0x0464
+#define AFE_CONN19 0x0468
+#define AFE_CONN20 0x046c
+#define AFE_CONN21 0x0470
+#define AFE_CONN22 0x0474
+#define AFE_CONN23 0x0478
+#define AFE_CONN24 0x047c
+#define AFE_CONN_RS 0x0494
+#define AFE_CONN_DI 0x0498
+#define AFE_CONN25 0x04b0
+#define AFE_CONN26 0x04b4
+#define AFE_CONN27 0x04b8
+#define AFE_CONN28 0x04bc
+#define AFE_CONN29 0x04c0
+#define AFE_CONN30 0x04c4
+#define AFE_CONN31 0x04c8
+#define AFE_CONN32 0x04cc
+#define AFE_SRAM_DELSEL_CON1 0x04f4
+#define AFE_CONN56 0x0500
+#define AFE_CONN57 0x0504
+#define AFE_CONN58 0x0508
+#define AFE_CONN59 0x050c
+#define AFE_CONN56_1 0x0510
+#define AFE_CONN57_1 0x0514
+#define AFE_CONN58_1 0x0518
+#define AFE_CONN59_1 0x051c
+#define PCM_INTF_CON1 0x0530
+#define PCM_INTF_CON2 0x0538
+#define PCM2_INTF_CON 0x053c
+#define AFE_CM1_CON 0x0550
+#define AFE_CONN34 0x0580
+#define FPGA_CFG0 0x05b0
+#define FPGA_CFG1 0x05b4
+#define FPGA_CFG2 0x05c0
+#define FPGA_CFG3 0x05c4
+#define AUDIO_TOP_DBG_CON 0x05c8
+#define AUDIO_TOP_DBG_MON0 0x05cc
+#define AUDIO_TOP_DBG_MON1 0x05d0
+#define AFE_IRQ8_MCU_CNT_MON 0x05e4
+#define AFE_IRQ11_MCU_CNT_MON 0x05e8
+#define AFE_IRQ12_MCU_CNT_MON 0x05ec
+#define AFE_IRQ_MCU_CNT9 0x0600
+#define AFE_IRQ_MCU_CNT10 0x0604
+#define AFE_IRQ_MCU_CNT13 0x0608
+#define AFE_IRQ_MCU_CNT14 0x060c
+#define AFE_IRQ_MCU_CNT15 0x0610
+#define AFE_IRQ_MCU_CNT16 0x0614
+#define AFE_IRQ_MCU_CNT17 0x0618
+#define AFE_IRQ_MCU_CNT18 0x061c
+#define AFE_IRQ_MCU_CNT19 0x0620
+#define AFE_IRQ_MCU_CNT20 0x0624
+#define AFE_IRQ_MCU_CNT21 0x0628
+#define AFE_IRQ_MCU_CNT22 0x062c
+#define AFE_IRQ_MCU_CNT23 0x0630
+#define AFE_IRQ_MCU_CNT24 0x0634
+#define AFE_IRQ_MCU_CNT25 0x0638
+#define AFE_IRQ_MCU_CNT26 0x063c
+#define AFE_IRQ9_MCU_CNT_MON 0x0660
+#define AFE_IRQ10_MCU_CNT_MON 0x0664
+#define AFE_IRQ13_MCU_CNT_MON 0x0668
+#define AFE_IRQ14_MCU_CNT_MON 0x066c
+#define AFE_IRQ15_MCU_CNT_MON 0x0670
+#define AFE_IRQ16_MCU_CNT_MON 0x0674
+#define AFE_IRQ17_MCU_CNT_MON 0x0678
+#define AFE_IRQ18_MCU_CNT_MON 0x067c
+#define AFE_IRQ19_MCU_CNT_MON 0x0680
+#define AFE_IRQ20_MCU_CNT_MON 0x0684
+#define AFE_IRQ21_MCU_CNT_MON 0x0688
+#define AFE_IRQ22_MCU_CNT_MON 0x068c
+#define AFE_IRQ23_MCU_CNT_MON 0x0690
+#define AFE_IRQ24_MCU_CNT_MON 0x0694
+#define AFE_IRQ25_MCU_CNT_MON 0x0698
+#define AFE_IRQ26_MCU_CNT_MON 0x069c
+#define AFE_IRQ31_MCU_CNT_MON 0x06a0
+#define AFE_GENERAL_REG0 0x0800
+#define AFE_GENERAL_REG1 0x0804
+#define AFE_GENERAL_REG2 0x0808
+#define AFE_GENERAL_REG3 0x080c
+#define AFE_GENERAL_REG4 0x0810
+#define AFE_GENERAL_REG5 0x0814
+#define AFE_GENERAL_REG6 0x0818
+#define AFE_GENERAL_REG7 0x081c
+#define AFE_GENERAL_REG8 0x0820
+#define AFE_GENERAL_REG9 0x0824
+#define AFE_GENERAL_REG10 0x0828
+#define AFE_GENERAL_REG11 0x082c
+#define AFE_GENERAL_REG12 0x0830
+#define AFE_GENERAL_REG13 0x0834
+#define AFE_GENERAL_REG14 0x0838
+#define AFE_GENERAL_REG15 0x083c
+#define AFE_CBIP_CFG0 0x0840
+#define AFE_CBIP_MON0 0x0844
+#define AFE_CBIP_SLV_MUX_MON0 0x0848
+#define AFE_CBIP_SLV_DECODER_MON0 0x084c
+#define AFE_ADDA6_MTKAIF_MON0 0x0854
+#define AFE_ADDA6_MTKAIF_MON1 0x0858
+#define AFE_AWB_CON0 0x085c
+#define AFE_AWB_BASE_MSB 0x0860
+#define AFE_AWB_BASE 0x0864
+#define AFE_AWB_CUR_MSB 0x0868
+#define AFE_AWB_CUR 0x086c
+#define AFE_AWB_END_MSB 0x0870
+#define AFE_AWB_END 0x0874
+#define AFE_AWB2_CON0 0x0878
+#define AFE_AWB2_BASE_MSB 0x087c
+#define AFE_AWB2_BASE 0x0880
+#define AFE_AWB2_CUR_MSB 0x0884
+#define AFE_AWB2_CUR 0x0888
+#define AFE_AWB2_END_MSB 0x088c
+#define AFE_AWB2_END 0x0890
+#define AFE_DAI_CON0 0x0894
+#define AFE_DAI_BASE_MSB 0x0898
+#define AFE_DAI_BASE 0x089c
+#define AFE_DAI_CUR_MSB 0x08a0
+#define AFE_DAI_CUR 0x08a4
+#define AFE_DAI_END_MSB 0x08a8
+#define AFE_DAI_END 0x08ac
+#define AFE_DAI2_CON0 0x08b0
+#define AFE_DAI2_BASE_MSB 0x08b4
+#define AFE_DAI2_BASE 0x08b8
+#define AFE_DAI2_CUR_MSB 0x08bc
+#define AFE_DAI2_CUR 0x08c0
+#define AFE_DAI2_END_MSB 0x08c4
+#define AFE_DAI2_END 0x08c8
+#define AFE_MEMIF_CON0 0x08cc
+#define AFE_CONN0_1 0x0900
+#define AFE_CONN1_1 0x0904
+#define AFE_CONN2_1 0x0908
+#define AFE_CONN3_1 0x090c
+#define AFE_CONN4_1 0x0910
+#define AFE_CONN5_1 0x0914
+#define AFE_CONN6_1 0x0918
+#define AFE_CONN7_1 0x091c
+#define AFE_CONN8_1 0x0920
+#define AFE_CONN9_1 0x0924
+#define AFE_CONN10_1 0x0928
+#define AFE_CONN11_1 0x092c
+#define AFE_CONN12_1 0x0930
+#define AFE_CONN13_1 0x0934
+#define AFE_CONN14_1 0x0938
+#define AFE_CONN15_1 0x093c
+#define AFE_CONN16_1 0x0940
+#define AFE_CONN17_1 0x0944
+#define AFE_CONN18_1 0x0948
+#define AFE_CONN19_1 0x094c
+#define AFE_CONN20_1 0x0950
+#define AFE_CONN21_1 0x0954
+#define AFE_CONN22_1 0x0958
+#define AFE_CONN23_1 0x095c
+#define AFE_CONN24_1 0x0960
+#define AFE_CONN25_1 0x0964
+#define AFE_CONN26_1 0x0968
+#define AFE_CONN27_1 0x096c
+#define AFE_CONN28_1 0x0970
+#define AFE_CONN29_1 0x0974
+#define AFE_CONN30_1 0x0978
+#define AFE_CONN31_1 0x097c
+#define AFE_CONN32_1 0x0980
+#define AFE_CONN33_1 0x0984
+#define AFE_CONN34_1 0x0988
+#define AFE_CONN_RS_1 0x098c
+#define AFE_CONN_DI_1 0x0990
+#define AFE_CONN_24BIT_1 0x0994
+#define AFE_CONN_REG 0x0998
+#define AFE_CONN35 0x09a0
+#define AFE_CONN36 0x09a4
+#define AFE_CONN37 0x09a8
+#define AFE_CONN38 0x09ac
+#define AFE_CONN35_1 0x09b0
+#define AFE_CONN36_1 0x09b4
+#define AFE_CONN37_1 0x09b8
+#define AFE_CONN38_1 0x09bc
+#define AFE_CONN39 0x09c0
+#define AFE_CONN40 0x09c4
+#define AFE_CONN41 0x09c8
+#define AFE_CONN42 0x09cc
+#define AFE_CONN39_1 0x09e0
+#define AFE_CONN40_1 0x09e4
+#define AFE_CONN41_1 0x09e8
+#define AFE_CONN42_1 0x09ec
+#define AFE_I2S_CON4 0x09f8
+#define AFE_CONN60 0x0a64
+#define AFE_CONN61 0x0a68
+#define AFE_CONN62 0x0a6c
+#define AFE_CONN63 0x0a70
+#define AFE_CONN64 0x0a74
+#define AFE_CONN65 0x0a78
+#define AFE_CONN66 0x0a7c
+#define AFE_ADDA6_TOP_CON0 0x0a80
+#define AFE_ADDA6_UL_SRC_CON0 0x0a84
+#define AFE_ADDA6_UL_SRC_CON1 0x0a88
+#define AFE_ADDA6_SRC_DEBUG 0x0a8c
+#define AFE_ADDA6_SRC_DEBUG_MON0 0x0a90
+#define AFE_ADDA6_ULCF_CFG_02_01 0x0aa0
+#define AFE_ADDA6_ULCF_CFG_04_03 0x0aa4
+#define AFE_ADDA6_ULCF_CFG_06_05 0x0aa8
+#define AFE_ADDA6_ULCF_CFG_08_07 0x0aac
+#define AFE_ADDA6_ULCF_CFG_10_09 0x0ab0
+#define AFE_ADDA6_ULCF_CFG_12_11 0x0ab4
+#define AFE_ADDA6_ULCF_CFG_14_13 0x0ab8
+#define AFE_ADDA6_ULCF_CFG_16_15 0x0abc
+#define AFE_ADDA6_ULCF_CFG_18_17 0x0ac0
+#define AFE_ADDA6_ULCF_CFG_20_19 0x0ac4
+#define AFE_ADDA6_ULCF_CFG_22_21 0x0ac8
+#define AFE_ADDA6_ULCF_CFG_24_23 0x0acc
+#define AFE_ADDA6_ULCF_CFG_26_25 0x0ad0
+#define AFE_ADDA6_ULCF_CFG_28_27 0x0ad4
+#define AFE_ADDA6_ULCF_CFG_30_29 0x0ad8
+#define AFE_ADD6A_UL_SRC_MON0 0x0ae4
+#define AFE_ADDA6_UL_SRC_MON1 0x0ae8
+#define AFE_CONN43 0x0af8
+#define AFE_CONN43_1 0x0afc
+#define AFE_MOD_DAI_CON0 0x0b00
+#define AFE_MOD_DAI_BASE_MSB 0x0b04
+#define AFE_MOD_DAI_BASE 0x0b08
+#define AFE_MOD_DAI_CUR_MSB 0x0b0c
+#define AFE_MOD_DAI_CUR 0x0b10
+#define AFE_MOD_DAI_END_MSB 0x0b14
+#define AFE_MOD_DAI_END 0x0b18
+#define AFE_AWB_RCH_MON 0x0b70
+#define AFE_AWB_LCH_MON 0x0b74
+#define AFE_VUL_RCH_MON 0x0b78
+#define AFE_VUL_LCH_MON 0x0b7c
+#define AFE_VUL12_RCH_MON 0x0b80
+#define AFE_VUL12_LCH_MON 0x0b84
+#define AFE_VUL2_RCH_MON 0x0b88
+#define AFE_VUL2_LCH_MON 0x0b8c
+#define AFE_DAI_DATA_MON 0x0b90
+#define AFE_MOD_DAI_DATA_MON 0x0b94
+#define AFE_DAI2_DATA_MON 0x0b98
+#define AFE_AWB2_RCH_MON 0x0b9c
+#define AFE_AWB2_LCH_MON 0x0ba0
+#define AFE_VUL3_RCH_MON 0x0ba4
+#define AFE_VUL3_LCH_MON 0x0ba8
+#define AFE_VUL4_RCH_MON 0x0bac
+#define AFE_VUL4_LCH_MON 0x0bb0
+#define AFE_VUL5_RCH_MON 0x0bb4
+#define AFE_VUL5_LCH_MON 0x0bb8
+#define AFE_VUL6_RCH_MON 0x0bbc
+#define AFE_VUL6_LCH_MON 0x0bc0
+#define AFE_DL1_RCH_MON 0x0bc4
+#define AFE_DL1_LCH_MON 0x0bc8
+#define AFE_DL2_RCH_MON 0x0bcc
+#define AFE_DL2_LCH_MON 0x0bd0
+#define AFE_DL12_RCH1_MON 0x0bd4
+#define AFE_DL12_LCH1_MON 0x0bd8
+#define AFE_DL12_RCH2_MON 0x0bdc
+#define AFE_DL12_LCH2_MON 0x0be0
+#define AFE_DL3_RCH_MON 0x0be4
+#define AFE_DL3_LCH_MON 0x0be8
+#define AFE_DL4_RCH_MON 0x0bec
+#define AFE_DL4_LCH_MON 0x0bf0
+#define AFE_DL5_RCH_MON 0x0bf4
+#define AFE_DL5_LCH_MON 0x0bf8
+#define AFE_DL6_RCH_MON 0x0bfc
+#define AFE_DL6_LCH_MON 0x0c00
+#define AFE_DL7_RCH_MON 0x0c04
+#define AFE_DL7_LCH_MON 0x0c08
+#define AFE_DL8_RCH_MON 0x0c0c
+#define AFE_DL8_LCH_MON 0x0c10
+#define AFE_VUL5_CON0 0x0c14
+#define AFE_VUL5_BASE_MSB 0x0c18
+#define AFE_VUL5_BASE 0x0c1c
+#define AFE_VUL5_CUR_MSB 0x0c20
+#define AFE_VUL5_CUR 0x0c24
+#define AFE_VUL5_END_MSB 0x0c28
+#define AFE_VUL5_END 0x0c2c
+#define AFE_VUL6_CON0 0x0c30
+#define AFE_VUL6_BASE_MSB 0x0c34
+#define AFE_VUL6_BASE 0x0c38
+#define AFE_VUL6_CUR_MSB 0x0c3c
+#define AFE_VUL6_CUR 0x0c40
+#define AFE_VUL6_END_MSB 0x0c44
+#define AFE_VUL6_END 0x0c48
+#define AFE_ADDA_DL_SDM_DCCOMP_CON 0x0c50
+#define AFE_ADDA_DL_SDM_TEST 0x0c54
+#define AFE_ADDA_DL_DC_COMP_CFG0 0x0c58
+#define AFE_ADDA_DL_DC_COMP_CFG1 0x0c5c
+#define AFE_ADDA_DL_SDM_FIFO_MON 0x0c60
+#define AFE_ADDA_DL_SRC_LCH_MON 0x0c64
+#define AFE_ADDA_DL_SRC_RCH_MON 0x0c68
+#define AFE_ADDA_DL_SDM_OUT_MON 0x0c6c
+#define AFE_ADDA_DL_SDM_DITHER_CON 0x0c70
+#define AFE_ADDA_DL_SDM_AUTO_RESET_CON 0x0c74
+#define AFE_CONNSYS_I2S_CON 0x0c78
+#define AFE_CONNSYS_I2S_MON 0x0c7c
+#define AFE_ASRC_2CH_CON0 0x0c80
+#define AFE_ASRC_2CH_CON1 0x0c84
+#define AFE_ASRC_2CH_CON2 0x0c88
+#define AFE_ASRC_2CH_CON3 0x0c8c
+#define AFE_ASRC_2CH_CON4 0x0c90
+#define AFE_ASRC_2CH_CON5 0x0c94
+#define AFE_ASRC_2CH_CON6 0x0c98
+#define AFE_ASRC_2CH_CON7 0x0c9c
+#define AFE_ASRC_2CH_CON8 0x0ca0
+#define AFE_ASRC_2CH_CON9 0x0ca4
+#define AFE_ASRC_2CH_CON10 0x0ca8
+#define AFE_ASRC_2CH_CON12 0x0cb0
+#define AFE_ASRC_2CH_CON13 0x0cb4
+#define AFE_ADDA6_IIR_COEF_02_01 0x0ce0
+#define AFE_ADDA6_IIR_COEF_04_03 0x0ce4
+#define AFE_ADDA6_IIR_COEF_06_05 0x0ce8
+#define AFE_ADDA6_IIR_COEF_08_07 0x0cec
+#define AFE_ADDA6_IIR_COEF_10_09 0x0cf0
+#define AFE_CONN67 0x0cf4
+#define AFE_CONN68 0x0cf8
+#define AFE_CONN69 0x0cfc
+#define AFE_SE_PROT_SIDEBAND 0x0d38
+#define AFE_SE_DOMAIN_SIDEBAND0 0x0d3c
+#define AFE_ADDA_PREDIS_CON2 0x0d40
+#define AFE_ADDA_PREDIS_CON3 0x0d44
+#define AFE_SE_DOMAIN_SIDEBAND1 0x0d54
+#define AFE_SE_DOMAIN_SIDEBAND2 0x0d58
+#define AFE_SE_DOMAIN_SIDEBAND3 0x0d5c
+#define AFE_CONN44 0x0d70
+#define AFE_CONN45 0x0d74
+#define AFE_CONN46 0x0d78
+#define AFE_CONN47 0x0d7c
+#define AFE_CONN44_1 0x0d80
+#define AFE_CONN45_1 0x0d84
+#define AFE_CONN46_1 0x0d88
+#define AFE_CONN47_1 0x0d8c
+#define AFE_HD_ENGEN_ENABLE 0x0dd0
+#define AFE_ADDA_DL_NLE_FIFO_MON 0x0dfc
+#define AFE_ADDA_MTKAIF_CFG0 0x0e00
+#define AFE_CONN67_1 0x0e04
+#define AFE_CONN68_1 0x0e08
+#define AFE_CONN69_1 0x0e0c
+#define AFE_ADDA_MTKAIF_SYNCWORD_CFG 0x0e14
+#define AFE_ADDA_MTKAIF_RX_CFG0 0x0e20
+#define AFE_ADDA_MTKAIF_RX_CFG1 0x0e24
+#define AFE_ADDA_MTKAIF_RX_CFG2 0x0e28
+#define AFE_ADDA_MTKAIF_MON0 0x0e34
+#define AFE_ADDA_MTKAIF_MON1 0x0e38
+#define AFE_AUD_PAD_TOP 0x0e40
+#define AFE_DL_NLE_R_CFG0 0x0e44
+#define AFE_DL_NLE_R_CFG1 0x0e48
+#define AFE_DL_NLE_L_CFG0 0x0e4c
+#define AFE_DL_NLE_L_CFG1 0x0e50
+#define AFE_DL_NLE_R_MON0 0x0e54
+#define AFE_DL_NLE_R_MON1 0x0e58
+#define AFE_DL_NLE_R_MON2 0x0e5c
+#define AFE_DL_NLE_L_MON0 0x0e60
+#define AFE_DL_NLE_L_MON1 0x0e64
+#define AFE_DL_NLE_L_MON2 0x0e68
+#define AFE_DL_NLE_GAIN_CFG0 0x0e6c
+#define AFE_ADDA6_MTKAIF_CFG0 0x0e70
+#define AFE_ADDA6_MTKAIF_RX_CFG0 0x0e74
+#define AFE_ADDA6_MTKAIF_RX_CFG1 0x0e78
+#define AFE_ADDA6_MTKAIF_RX_CFG2 0x0e7c
+#define AFE_GENERAL1_ASRC_2CH_CON0 0x0e80
+#define AFE_GENERAL1_ASRC_2CH_CON1 0x0e84
+#define AFE_GENERAL1_ASRC_2CH_CON2 0x0e88
+#define AFE_GENERAL1_ASRC_2CH_CON3 0x0e8c
+#define AFE_GENERAL1_ASRC_2CH_CON4 0x0e90
+#define AFE_GENERAL1_ASRC_2CH_CON5 0x0e94
+#define AFE_GENERAL1_ASRC_2CH_CON6 0x0e98
+#define AFE_GENERAL1_ASRC_2CH_CON7 0x0e9c
+#define AFE_GENERAL1_ASRC_2CH_CON8 0x0ea0
+#define AFE_GENERAL1_ASRC_2CH_CON9 0x0ea4
+#define AFE_GENERAL1_ASRC_2CH_CON10 0x0ea8
+#define AFE_GENERAL1_ASRC_2CH_CON12 0x0eb0
+#define AFE_GENERAL1_ASRC_2CH_CON13 0x0eb4
+#define GENERAL_ASRC_MODE 0x0eb8
+#define GENERAL_ASRC_EN_ON 0x0ebc
+#define AFE_CONN48 0x0ec0
+#define AFE_CONN49 0x0ec4
+#define AFE_CONN50 0x0ec8
+#define AFE_CONN51 0x0ecc
+#define AFE_CONN52 0x0ed0
+#define AFE_CONN53 0x0ed4
+#define AFE_CONN54 0x0ed8
+#define AFE_CONN55 0x0edc
+#define AFE_CONN48_1 0x0ee0
+#define AFE_CONN49_1 0x0ee4
+#define AFE_CONN50_1 0x0ee8
+#define AFE_CONN51_1 0x0eec
+#define AFE_CONN52_1 0x0ef0
+#define AFE_CONN53_1 0x0ef4
+#define AFE_CONN54_1 0x0ef8
+#define AFE_CONN55_1 0x0efc
+#define AFE_GENERAL2_ASRC_2CH_CON0 0x0f00
+#define AFE_GENERAL2_ASRC_2CH_CON1 0x0f04
+#define AFE_GENERAL2_ASRC_2CH_CON2 0x0f08
+#define AFE_GENERAL2_ASRC_2CH_CON3 0x0f0c
+#define AFE_GENERAL2_ASRC_2CH_CON4 0x0f10
+#define AFE_GENERAL2_ASRC_2CH_CON5 0x0f14
+#define AFE_GENERAL2_ASRC_2CH_CON6 0x0f18
+#define AFE_GENERAL2_ASRC_2CH_CON7 0x0f1c
+#define AFE_GENERAL2_ASRC_2CH_CON8 0x0f20
+#define AFE_GENERAL2_ASRC_2CH_CON9 0x0f24
+#define AFE_GENERAL2_ASRC_2CH_CON10 0x0f28
+#define AFE_GENERAL2_ASRC_2CH_CON12 0x0f30
+#define AFE_GENERAL2_ASRC_2CH_CON13 0x0f34
+#define AFE_DL5_CON0 0x0f4c
+#define AFE_DL5_BASE_MSB 0x0f50
+#define AFE_DL5_BASE 0x0f54
+#define AFE_DL5_CUR_MSB 0x0f58
+#define AFE_DL5_CUR 0x0f5c
+#define AFE_DL5_END_MSB 0x0f60
+#define AFE_DL5_END 0x0f64
+#define AFE_DL6_CON0 0x0f68
+#define AFE_DL6_BASE_MSB 0x0f6c
+#define AFE_DL6_BASE 0x0f70
+#define AFE_DL6_CUR_MSB 0x0f74
+#define AFE_DL6_CUR 0x0f78
+#define AFE_DL6_END_MSB 0x0f7c
+#define AFE_DL6_END 0x0f80
+#define AFE_DL7_CON0 0x0f84
+#define AFE_DL7_BASE_MSB 0x0f88
+#define AFE_DL7_BASE 0x0f8c
+#define AFE_DL7_CUR_MSB 0x0f90
+#define AFE_DL7_CUR 0x0f94
+#define AFE_DL7_END_MSB 0x0f98
+#define AFE_DL7_END 0x0f9c
+#define AFE_DL8_CON0 0x0fa0
+#define AFE_DL8_BASE_MSB 0x0fa4
+#define AFE_DL8_BASE 0x0fa8
+#define AFE_DL8_CUR_MSB 0x0fac
+#define AFE_DL8_CUR 0x0fb0
+#define AFE_DL8_END_MSB 0x0fb4
+#define AFE_DL8_END 0x0fb8
+#define AFE_SE_SECURE_CON 0x1004
+#define AFE_PROT_SIDEBAND_MON 0x1008
+#define AFE_DOMAIN_SIDEBAND0_MON 0x100c
+#define AFE_DOMAIN_SIDEBAND1_MON 0x1010
+#define AFE_DOMAIN_SIDEBAND2_MON 0x1014
+#define AFE_DOMAIN_SIDEBAND3_MON 0x1018
+#define AFE_SECURE_MASK_CONN0 0x1020
+#define AFE_SECURE_MASK_CONN1 0x1024
+#define AFE_SECURE_MASK_CONN2 0x1028
+#define AFE_SECURE_MASK_CONN3 0x102c
+#define AFE_SECURE_MASK_CONN4 0x1030
+#define AFE_SECURE_MASK_CONN5 0x1034
+#define AFE_SECURE_MASK_CONN6 0x1038
+#define AFE_SECURE_MASK_CONN7 0x103c
+#define AFE_SECURE_MASK_CONN8 0x1040
+#define AFE_SECURE_MASK_CONN9 0x1044
+#define AFE_SECURE_MASK_CONN10 0x1048
+#define AFE_SECURE_MASK_CONN11 0x104c
+#define AFE_SECURE_MASK_CONN12 0x1050
+#define AFE_SECURE_MASK_CONN13 0x1054
+#define AFE_SECURE_MASK_CONN14 0x1058
+#define AFE_SECURE_MASK_CONN15 0x105c
+#define AFE_SECURE_MASK_CONN16 0x1060
+#define AFE_SECURE_MASK_CONN17 0x1064
+#define AFE_SECURE_MASK_CONN18 0x1068
+#define AFE_SECURE_MASK_CONN19 0x106c
+#define AFE_SECURE_MASK_CONN20 0x1070
+#define AFE_SECURE_MASK_CONN21 0x1074
+#define AFE_SECURE_MASK_CONN22 0x1078
+#define AFE_SECURE_MASK_CONN23 0x107c
+#define AFE_SECURE_MASK_CONN24 0x1080
+#define AFE_SECURE_MASK_CONN25 0x1084
+#define AFE_SECURE_MASK_CONN26 0x1088
+#define AFE_SECURE_MASK_CONN27 0x108c
+#define AFE_SECURE_MASK_CONN28 0x1090
+#define AFE_SECURE_MASK_CONN29 0x1094
+#define AFE_SECURE_MASK_CONN30 0x1098
+#define AFE_SECURE_MASK_CONN31 0x109c
+#define AFE_SECURE_MASK_CONN32 0x10a0
+#define AFE_SECURE_MASK_CONN33 0x10a4
+#define AFE_SECURE_MASK_CONN34 0x10a8
+#define AFE_SECURE_MASK_CONN35 0x10ac
+#define AFE_SECURE_MASK_CONN36 0x10b0
+#define AFE_SECURE_MASK_CONN37 0x10b4
+#define AFE_SECURE_MASK_CONN38 0x10b8
+#define AFE_SECURE_MASK_CONN39 0x10bc
+#define AFE_SECURE_MASK_CONN40 0x10c0
+#define AFE_SECURE_MASK_CONN41 0x10c4
+#define AFE_SECURE_MASK_CONN42 0x10c8
+#define AFE_SECURE_MASK_CONN43 0x10cc
+#define AFE_SECURE_MASK_CONN44 0x10d0
+#define AFE_SECURE_MASK_CONN45 0x10d4
+#define AFE_SECURE_MASK_CONN46 0x10d8
+#define AFE_SECURE_MASK_CONN47 0x10dc
+#define AFE_SECURE_MASK_CONN48 0x10e0
+#define AFE_SECURE_MASK_CONN49 0x10e4
+#define AFE_SECURE_MASK_CONN50 0x10e8
+#define AFE_SECURE_MASK_CONN51 0x10ec
+#define AFE_SECURE_MASK_CONN52 0x10f0
+#define AFE_SECURE_MASK_CONN53 0x10f4
+#define AFE_SECURE_MASK_CONN54 0x10f8
+#define AFE_SECURE_MASK_CONN55 0x10fc
+#define AFE_SECURE_MASK_CONN56 0x1100
+#define AFE_SECURE_MASK_CONN57 0x1104
+#define AFE_SECURE_MASK_CONN0_1 0x1108
+#define AFE_SECURE_MASK_CONN1_1 0x110c
+#define AFE_SECURE_MASK_CONN2_1 0x1110
+#define AFE_SECURE_MASK_CONN3_1 0x1114
+#define AFE_SECURE_MASK_CONN4_1 0x1118
+#define AFE_SECURE_MASK_CONN5_1 0x111c
+#define AFE_SECURE_MASK_CONN6_1 0x1120
+#define AFE_SECURE_MASK_CONN7_1 0x1124
+#define AFE_SECURE_MASK_CONN8_1 0x1128
+#define AFE_SECURE_MASK_CONN9_1 0x112c
+#define AFE_SECURE_MASK_CONN10_1 0x1130
+#define AFE_SECURE_MASK_CONN11_1 0x1134
+#define AFE_SECURE_MASK_CONN12_1 0x1138
+#define AFE_SECURE_MASK_CONN13_1 0x113c
+#define AFE_SECURE_MASK_CONN14_1 0x1140
+#define AFE_SECURE_MASK_CONN15_1 0x1144
+#define AFE_SECURE_MASK_CONN16_1 0x1148
+#define AFE_SECURE_MASK_CONN17_1 0x114c
+#define AFE_SECURE_MASK_CONN18_1 0x1150
+#define AFE_SECURE_MASK_CONN19_1 0x1154
+#define AFE_SECURE_MASK_CONN20_1 0x1158
+#define AFE_SECURE_MASK_CONN21_1 0x115c
+#define AFE_SECURE_MASK_CONN22_1 0x1160
+#define AFE_SECURE_MASK_CONN23_1 0x1164
+#define AFE_SECURE_MASK_CONN24_1 0x1168
+#define AFE_SECURE_MASK_CONN25_1 0x116c
+#define AFE_SECURE_MASK_CONN26_1 0x1170
+#define AFE_SECURE_MASK_CONN27_1 0x1174
+#define AFE_SECURE_MASK_CONN28_1 0x1178
+#define AFE_SECURE_MASK_CONN29_1 0x117c
+#define AFE_SECURE_MASK_CONN30_1 0x1180
+#define AFE_SECURE_MASK_CONN31_1 0x1184
+#define AFE_SECURE_MASK_CONN32_1 0x1188
+#define AFE_SECURE_MASK_CONN33_1 0x118c
+#define AFE_SECURE_MASK_CONN34_1 0x1190
+#define AFE_SECURE_MASK_CONN35_1 0x1194
+#define AFE_SECURE_MASK_CONN36_1 0x1198
+#define AFE_SECURE_MASK_CONN37_1 0x119c
+#define AFE_SECURE_MASK_CONN38_1 0x11a0
+#define AFE_SECURE_MASK_CONN39_1 0x11a4
+#define AFE_SECURE_MASK_CONN40_1 0x11a8
+#define AFE_SECURE_MASK_CONN41_1 0x11ac
+#define AFE_SECURE_MASK_CONN42_1 0x11b0
+#define AFE_SECURE_MASK_CONN43_1 0x11b4
+#define AFE_SECURE_MASK_CONN44_1 0x11b8
+#define AFE_SECURE_MASK_CONN45_1 0x11bc
+#define AFE_SECURE_MASK_CONN46_1 0x11c0
+#define AFE_SECURE_MASK_CONN47_1 0x11c4
+#define AFE_SECURE_MASK_CONN48_1 0x11c8
+#define AFE_SECURE_MASK_CONN49_1 0x11cc
+#define AFE_SECURE_MASK_CONN50_1 0x11d0
+#define AFE_SECURE_MASK_CONN51_1 0x11d4
+#define AFE_SECURE_MASK_CONN52_1 0x11d8
+#define AFE_SECURE_MASK_CONN53_1 0x11dc
+#define AFE_SECURE_MASK_CONN54_1 0x11e0
+#define AFE_SECURE_MASK_CONN55_1 0x11e4
+#define AFE_SECURE_MASK_CONN56_1 0x11e8
+#define AFE_CONN60_1 0x11f0
+#define AFE_CONN61_1 0x11f4
+#define AFE_CONN62_1 0x11f8
+#define AFE_CONN63_1 0x11fc
+#define AFE_CONN64_1 0x1220
+#define AFE_CONN65_1 0x1224
+#define AFE_CONN66_1 0x1228
+#define FPGA_CFG4 0x1230
+#define FPGA_CFG5 0x1234
+#define FPGA_CFG6 0x1238
+#define FPGA_CFG7 0x123c
+#define FPGA_CFG8 0x1240
+#define FPGA_CFG9 0x1244
+#define FPGA_CFG10 0x1248
+#define FPGA_CFG11 0x124c
+#define FPGA_CFG12 0x1250
+#define FPGA_CFG13 0x1254
+#define ETDM_IN1_CON0 0x1430
+#define ETDM_IN1_CON1 0x1434
+#define ETDM_IN1_CON2 0x1438
+#define ETDM_IN1_CON3 0x143c
+#define ETDM_IN1_CON4 0x1440
+#define ETDM_IN1_CON5 0x1444
+#define ETDM_IN1_CON6 0x1448
+#define ETDM_IN1_CON7 0x144c
+#define ETDM_IN1_CON8 0x1450
+#define ETDM_OUT1_CON0 0x1454
+#define ETDM_OUT1_CON1 0x1458
+#define ETDM_OUT1_CON2 0x145c
+#define ETDM_OUT1_CON3 0x1460
+#define ETDM_OUT1_CON4 0x1464
+#define ETDM_OUT1_CON5 0x1468
+#define ETDM_OUT1_CON6 0x146c
+#define ETDM_OUT1_CON7 0x1470
+#define ETDM_OUT1_CON8 0x1474
+#define ETDM_IN1_MON 0x1478
+#define ETDM_OUT1_MON 0x147c
+#define ETDM_0_3_COWORK_CON0 0x18b0
+#define ETDM_0_3_COWORK_CON1 0x18b4
+#define ETDM_0_3_COWORK_CON3 0x18bc
+
+#define AFE_MAX_REGISTER ETDM_0_3_COWORK_CON3
+
+#define AFE_IRQ_STATUS_BITS 0x87FFFFFF
+#define AFE_IRQ_CNT_SHIFT 0
+#define AFE_IRQ_CNT_MASK 0x3ffff
+#endif
diff --git a/sound/soc/mediatek/mt8188/Makefile b/sound/soc/mediatek/mt8188/Makefile
new file mode 100644
index 000000000000..b9f3e4ad7b07
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt8188-afe-y := \
+ mt8188-afe-clk.o \
+ mt8188-afe-pcm.o \
+ mt8188-audsys-clk.o \
+ mt8188-dai-adda.o \
+ mt8188-dai-dmic.o \
+ mt8188-dai-etdm.o \
+ mt8188-dai-pcm.o
+
+obj-$(CONFIG_SND_SOC_MT8188) += snd-soc-mt8188-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT8188_MT6359) += mt8188-mt6359.o
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
new file mode 100644
index 000000000000..7f411b857782
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8188-afe-clk.c -- MediaTek 8188 afe clock ctrl
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+
+#include "mt8188-afe-common.h"
+#include "mt8188-afe-clk.h"
+#include "mt8188-audsys-clk.h"
+#include "mt8188-reg.h"
+
+static const char *aud_clks[MT8188_CLK_NUM] = {
+ /* xtal */
+ [MT8188_CLK_XTAL_26M] = "clk26m",
+
+ /* pll */
+ [MT8188_CLK_APMIXED_APLL1] = "apll1",
+ [MT8188_CLK_APMIXED_APLL2] = "apll2",
+
+ /* divider */
+ [MT8188_CLK_TOP_APLL1_D4] = "apll1_d4",
+ [MT8188_CLK_TOP_APLL2_D4] = "apll2_d4",
+ [MT8188_CLK_TOP_APLL12_DIV0] = "apll12_div0",
+ [MT8188_CLK_TOP_APLL12_DIV1] = "apll12_div1",
+ [MT8188_CLK_TOP_APLL12_DIV2] = "apll12_div2",
+ [MT8188_CLK_TOP_APLL12_DIV3] = "apll12_div3",
+ [MT8188_CLK_TOP_APLL12_DIV4] = "apll12_div4",
+ [MT8188_CLK_TOP_APLL12_DIV9] = "apll12_div9",
+
+ /* mux */
+ [MT8188_CLK_TOP_A1SYS_HP_SEL] = "top_a1sys_hp",
+ [MT8188_CLK_TOP_A2SYS_SEL] = "top_a2sys",
+ [MT8188_CLK_TOP_AUD_IEC_SEL] = "top_aud_iec",
+ [MT8188_CLK_TOP_AUD_INTBUS_SEL] = "top_aud_intbus",
+ [MT8188_CLK_TOP_AUDIO_H_SEL] = "top_audio_h",
+ [MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL] = "top_audio_local_bus",
+ [MT8188_CLK_TOP_DPTX_M_SEL] = "top_dptx",
+ [MT8188_CLK_TOP_I2SO1_M_SEL] = "top_i2so1",
+ [MT8188_CLK_TOP_I2SO2_M_SEL] = "top_i2so2",
+ [MT8188_CLK_TOP_I2SI1_M_SEL] = "top_i2si1",
+ [MT8188_CLK_TOP_I2SI2_M_SEL] = "top_i2si2",
+
+ /* clock gate */
+ [MT8188_CLK_ADSP_AUDIO_26M] = "adsp_audio_26m",
+ /* afe clock gate */
+ [MT8188_CLK_AUD_AFE] = "aud_afe",
+ [MT8188_CLK_AUD_APLL1_TUNER] = "aud_apll1_tuner",
+ [MT8188_CLK_AUD_APLL2_TUNER] = "aud_apll2_tuner",
+ [MT8188_CLK_AUD_APLL] = "aud_apll",
+ [MT8188_CLK_AUD_APLL2] = "aud_apll2",
+ [MT8188_CLK_AUD_DAC] = "aud_dac",
+ [MT8188_CLK_AUD_ADC] = "aud_adc",
+ [MT8188_CLK_AUD_DAC_HIRES] = "aud_dac_hires",
+ [MT8188_CLK_AUD_A1SYS_HP] = "aud_a1sys_hp",
+ [MT8188_CLK_AUD_AFE_DMIC1] = "aud_afe_dmic1",
+ [MT8188_CLK_AUD_AFE_DMIC2] = "aud_afe_dmic2",
+ [MT8188_CLK_AUD_AFE_DMIC3] = "aud_afe_dmic3",
+ [MT8188_CLK_AUD_AFE_DMIC4] = "aud_afe_dmic4",
+ [MT8188_CLK_AUD_ADC_HIRES] = "aud_adc_hires",
+ [MT8188_CLK_AUD_DMIC_HIRES1] = "aud_dmic_hires1",
+ [MT8188_CLK_AUD_DMIC_HIRES2] = "aud_dmic_hires2",
+ [MT8188_CLK_AUD_DMIC_HIRES3] = "aud_dmic_hires3",
+ [MT8188_CLK_AUD_DMIC_HIRES4] = "aud_dmic_hires4",
+ [MT8188_CLK_AUD_I2SIN] = "aud_i2sin",
+ [MT8188_CLK_AUD_TDM_IN] = "aud_tdm_in",
+ [MT8188_CLK_AUD_I2S_OUT] = "aud_i2s_out",
+ [MT8188_CLK_AUD_TDM_OUT] = "aud_tdm_out",
+ [MT8188_CLK_AUD_HDMI_OUT] = "aud_hdmi_out",
+ [MT8188_CLK_AUD_ASRC11] = "aud_asrc11",
+ [MT8188_CLK_AUD_ASRC12] = "aud_asrc12",
+ [MT8188_CLK_AUD_A1SYS] = "aud_a1sys",
+ [MT8188_CLK_AUD_A2SYS] = "aud_a2sys",
+ [MT8188_CLK_AUD_PCMIF] = "aud_pcmif",
+ [MT8188_CLK_AUD_MEMIF_UL1] = "aud_memif_ul1",
+ [MT8188_CLK_AUD_MEMIF_UL2] = "aud_memif_ul2",
+ [MT8188_CLK_AUD_MEMIF_UL3] = "aud_memif_ul3",
+ [MT8188_CLK_AUD_MEMIF_UL4] = "aud_memif_ul4",
+ [MT8188_CLK_AUD_MEMIF_UL5] = "aud_memif_ul5",
+ [MT8188_CLK_AUD_MEMIF_UL6] = "aud_memif_ul6",
+ [MT8188_CLK_AUD_MEMIF_UL8] = "aud_memif_ul8",
+ [MT8188_CLK_AUD_MEMIF_UL9] = "aud_memif_ul9",
+ [MT8188_CLK_AUD_MEMIF_UL10] = "aud_memif_ul10",
+ [MT8188_CLK_AUD_MEMIF_DL2] = "aud_memif_dl2",
+ [MT8188_CLK_AUD_MEMIF_DL3] = "aud_memif_dl3",
+ [MT8188_CLK_AUD_MEMIF_DL6] = "aud_memif_dl6",
+ [MT8188_CLK_AUD_MEMIF_DL7] = "aud_memif_dl7",
+ [MT8188_CLK_AUD_MEMIF_DL8] = "aud_memif_dl8",
+ [MT8188_CLK_AUD_MEMIF_DL10] = "aud_memif_dl10",
+ [MT8188_CLK_AUD_MEMIF_DL11] = "aud_memif_dl11",
+};
+
+struct mt8188_afe_tuner_cfg {
+ unsigned int id;
+ int apll_div_reg;
+ unsigned int apll_div_shift;
+ unsigned int apll_div_maskbit;
+ unsigned int apll_div_default;
+ int ref_ck_sel_reg;
+ unsigned int ref_ck_sel_shift;
+ unsigned int ref_ck_sel_maskbit;
+ unsigned int ref_ck_sel_default;
+ int tuner_en_reg;
+ unsigned int tuner_en_shift;
+ unsigned int tuner_en_maskbit;
+ int upper_bound_reg;
+ unsigned int upper_bound_shift;
+ unsigned int upper_bound_maskbit;
+ unsigned int upper_bound_default;
+ spinlock_t ctrl_lock; /* lock for apll tuner ctrl*/
+ int ref_cnt;
+};
+
+static struct mt8188_afe_tuner_cfg
+ mt8188_afe_tuner_cfgs[MT8188_AUD_PLL_NUM] = {
+ [MT8188_AUD_PLL1] = {
+ .id = MT8188_AUD_PLL1,
+ .apll_div_reg = AFE_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0xf,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 1,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x2,
+ .tuner_en_reg = AFE_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_APLL_TUNER_CFG,
+ .upper_bound_shift = 8,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x3,
+ },
+ [MT8188_AUD_PLL2] = {
+ .id = MT8188_AUD_PLL2,
+ .apll_div_reg = AFE_APLL_TUNER_CFG1,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0xf,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_APLL_TUNER_CFG1,
+ .ref_ck_sel_shift = 1,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x1,
+ .tuner_en_reg = AFE_APLL_TUNER_CFG1,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_APLL_TUNER_CFG1,
+ .upper_bound_shift = 8,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x3,
+ },
+ [MT8188_AUD_PLL3] = {
+ .id = MT8188_AUD_PLL3,
+ .apll_div_reg = AFE_EARC_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x3,
+ .ref_ck_sel_reg = AFE_EARC_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 24,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x0,
+ .tuner_en_reg = AFE_EARC_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_EARC_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+ [MT8188_AUD_PLL4] = {
+ .id = MT8188_AUD_PLL4,
+ .apll_div_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_SPDIFIN_APLL_TUNER_CFG1,
+ .ref_ck_sel_shift = 8,
+ .ref_ck_sel_maskbit = 0x1,
+ .ref_ck_sel_default = 0,
+ .tuner_en_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+ [MT8188_AUD_PLL5] = {
+ .id = MT8188_AUD_PLL5,
+ .apll_div_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x3,
+ .ref_ck_sel_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 24,
+ .ref_ck_sel_maskbit = 0x1,
+ .ref_ck_sel_default = 0,
+ .tuner_en_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+};
+
+static struct mt8188_afe_tuner_cfg *mt8188_afe_found_apll_tuner(unsigned int id)
+{
+ if (id >= MT8188_AUD_PLL_NUM)
+ return NULL;
+
+ return &mt8188_afe_tuner_cfgs[id];
+}
+
+static int mt8188_afe_init_apll_tuner(unsigned int id)
+{
+ struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+
+ if (!cfg)
+ return -EINVAL;
+
+ cfg->ref_cnt = 0;
+ spin_lock_init(&cfg->ctrl_lock);
+
+ return 0;
+}
+
+static int mt8188_afe_setup_apll_tuner(struct mtk_base_afe *afe, unsigned int id)
+{
+ const struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+
+ if (!cfg)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cfg->apll_div_reg,
+ cfg->apll_div_maskbit << cfg->apll_div_shift,
+ cfg->apll_div_default << cfg->apll_div_shift);
+
+ regmap_update_bits(afe->regmap,
+ cfg->ref_ck_sel_reg,
+ cfg->ref_ck_sel_maskbit << cfg->ref_ck_sel_shift,
+ cfg->ref_ck_sel_default << cfg->ref_ck_sel_shift);
+
+ regmap_update_bits(afe->regmap,
+ cfg->upper_bound_reg,
+ cfg->upper_bound_maskbit << cfg->upper_bound_shift,
+ cfg->upper_bound_default << cfg->upper_bound_shift);
+
+ return 0;
+}
+
+static int mt8188_afe_enable_tuner_clk(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ switch (id) {
+ case MT8188_AUD_PLL1:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]);
+ break;
+ case MT8188_AUD_PLL2:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mt8188_afe_disable_tuner_clk(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ switch (id) {
+ case MT8188_AUD_PLL1:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
+ break;
+ case MT8188_AUD_PLL2:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mt8188_afe_enable_apll_tuner(struct mtk_base_afe *afe, unsigned int id)
+{
+ struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+ unsigned long flags;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ ret = mt8188_afe_setup_apll_tuner(afe, id);
+ if (ret)
+ return ret;
+
+ ret = mt8188_afe_enable_tuner_clk(afe, id);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&cfg->ctrl_lock, flags);
+
+ cfg->ref_cnt++;
+ if (cfg->ref_cnt == 1)
+ regmap_update_bits(afe->regmap,
+ cfg->tuner_en_reg,
+ cfg->tuner_en_maskbit << cfg->tuner_en_shift,
+ BIT(cfg->tuner_en_shift));
+
+ spin_unlock_irqrestore(&cfg->ctrl_lock, flags);
+
+ return 0;
+}
+
+static int mt8188_afe_disable_apll_tuner(struct mtk_base_afe *afe, unsigned int id)
+{
+ struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+ unsigned long flags;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&cfg->ctrl_lock, flags);
+
+ cfg->ref_cnt--;
+ if (cfg->ref_cnt == 0)
+ regmap_update_bits(afe->regmap,
+ cfg->tuner_en_reg,
+ cfg->tuner_en_maskbit << cfg->tuner_en_shift,
+ 0 << cfg->tuner_en_shift);
+ else if (cfg->ref_cnt < 0)
+ cfg->ref_cnt = 0;
+
+ spin_unlock_irqrestore(&cfg->ctrl_lock, flags);
+
+ ret = mt8188_afe_disable_tuner_clk(afe, id);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int mt8188_afe_get_mclk_source_clk_id(int sel)
+{
+ switch (sel) {
+ case MT8188_MCK_SEL_26M:
+ return MT8188_CLK_XTAL_26M;
+ case MT8188_MCK_SEL_APLL1:
+ return MT8188_CLK_APMIXED_APLL1;
+ case MT8188_MCK_SEL_APLL2:
+ return MT8188_CLK_APMIXED_APLL2;
+ default:
+ return -EINVAL;
+ }
+}
+
+int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int clk_id = mt8188_afe_get_mclk_source_clk_id(apll);
+
+ if (clk_id < 0) {
+ dev_dbg(afe->dev, "invalid clk id\n");
+ return 0;
+ }
+
+ return clk_get_rate(afe_priv->clk[clk_id]);
+}
+
+int mt8188_afe_get_default_mclk_source_by_rate(int rate)
+{
+ return ((rate % 8000) == 0) ?
+ MT8188_MCK_SEL_APLL1 : MT8188_MCK_SEL_APLL2;
+}
+
+int mt8188_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return ((rate % 8000) == 0) ? MT8188_AUD_PLL1 : MT8188_AUD_PLL2;
+}
+
+int mt8188_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8188_AUD_PLL1;
+
+ return MT8188_AUD_PLL2;
+}
+
+int mt8188_afe_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int i, ret;
+
+ ret = mt8188_audsys_clk_register(afe);
+ if (ret) {
+ dev_err(afe->dev, "register audsys clk fail %d\n", ret);
+ return ret;
+ }
+
+ afe_priv->clk =
+ devm_kcalloc(afe->dev, MT8188_CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < MT8188_CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
+ __func__, aud_clks[i],
+ PTR_ERR(afe_priv->clk[i]));
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ /* initial tuner */
+ for (i = 0; i < MT8188_AUD_PLL_NUM; i++) {
+ ret = mt8188_afe_init_apll_tuner(i);
+ if (ret) {
+ dev_info(afe->dev, "%s(), init apll_tuner%d failed",
+ __func__, (i + 1));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to enable clk\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ dev_dbg(afe->dev, "NULL clk\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8188_afe_enable_clk);
+
+void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_disable_unprepare(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+EXPORT_SYMBOL_GPL(mt8188_afe_disable_clk);
+
+int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to set clk rate\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent)
+{
+ int ret;
+
+ if (clk && parent) {
+ ret = clk_set_parent(clk, parent);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to set clk parent %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int get_top_cg_reg(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ case MT8188_TOP_CG_26M_TIMING:
+ return ASYS_TOP_CON;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_mask(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ return ASYS_TOP_CON_A1SYS_TIMING_ON;
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ return ASYS_TOP_CON_A2SYS_TIMING_ON;
+ case MT8188_TOP_CG_26M_TIMING:
+ return ASYS_TOP_CON_26M_TIMING_ON;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_on_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ case MT8188_TOP_CG_26M_TIMING:
+ return get_top_cg_mask(cg_type);
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_off_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ case MT8188_TOP_CG_26M_TIMING:
+ return 0;
+ default:
+ return get_top_cg_mask(cg_type);
+ }
+}
+
+static int mt8188_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_on_val(cg_type);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int mt8188_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_off_val(cg_type);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ /* bus clock for AFE external access, like DRAM */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+
+ /* bus clock for AFE internal access, like AFE SRAM */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+
+ /* audio 26m clock source */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+
+ /* AFE hw clock */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+
+ return 0;
+}
+
+int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+
+ return 0;
+}
+
+static int mt8188_afe_enable_afe_on(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+ return 0;
+}
+
+static int mt8188_afe_disable_afe_on(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
+ return 0;
+}
+
+static int mt8188_afe_enable_a1sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ if (ret)
+ return ret;
+
+ return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+}
+
+static int mt8188_afe_disable_a1sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ return 0;
+}
+
+static int mt8188_afe_enable_a2sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+ if (ret)
+ return ret;
+
+ return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+}
+
+static int mt8188_afe_disable_a2sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+ return 0;
+}
+
+int mt8188_apll1_enable(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+ if (ret)
+ return ret;
+
+ ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL],
+ afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+ if (ret)
+ goto err_clk_parent;
+
+ ret = mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1);
+ if (ret)
+ goto err_apll_tuner;
+
+ ret = mt8188_afe_enable_a1sys(afe);
+ if (ret)
+ goto err_a1sys;
+
+ return 0;
+
+err_a1sys:
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1);
+err_apll_tuner:
+ mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL],
+ afe_priv->clk[MT8188_CLK_XTAL_26M]);
+err_clk_parent:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+
+ return ret;
+}
+
+int mt8188_apll1_disable(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_a1sys(afe);
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1);
+ mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL],
+ afe_priv->clk[MT8188_CLK_XTAL_26M]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+
+ return 0;
+}
+
+int mt8188_apll2_enable(struct mtk_base_afe *afe)
+{
+ int ret;
+
+ ret = mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2);
+ if (ret)
+ return ret;
+
+ ret = mt8188_afe_enable_a2sys(afe);
+ if (ret)
+ goto err_a2sys;
+
+ return 0;
+err_a2sys:
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2);
+
+ return ret;
+}
+
+int mt8188_apll2_disable(struct mtk_base_afe *afe)
+{
+ mt8188_afe_disable_a2sys(afe);
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2);
+ return 0;
+}
+
+int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ mt8188_afe_enable_afe_on(afe);
+ return 0;
+}
+
+int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8188_afe_disable_afe_on(afe);
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
new file mode 100644
index 000000000000..c6c78d684f3e
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-afe-clk.h -- MediaTek 8188 afe clock ctrl definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_AFE_CLK_H_
+#define _MT8188_AFE_CLK_H_
+
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+
+enum {
+ /* xtal */
+ MT8188_CLK_XTAL_26M,
+ /* pll */
+ MT8188_CLK_APMIXED_APLL1,
+ MT8188_CLK_APMIXED_APLL2,
+ /* divider */
+ MT8188_CLK_TOP_APLL1_D4,
+ MT8188_CLK_TOP_APLL2_D4,
+ MT8188_CLK_TOP_APLL12_DIV0,
+ MT8188_CLK_TOP_APLL12_DIV1,
+ MT8188_CLK_TOP_APLL12_DIV2,
+ MT8188_CLK_TOP_APLL12_DIV3,
+ MT8188_CLK_TOP_APLL12_DIV4,
+ MT8188_CLK_TOP_APLL12_DIV9,
+ /* mux */
+ MT8188_CLK_TOP_A1SYS_HP_SEL,
+ MT8188_CLK_TOP_A2SYS_SEL,
+ MT8188_CLK_TOP_AUD_IEC_SEL,
+ MT8188_CLK_TOP_AUD_INTBUS_SEL,
+ MT8188_CLK_TOP_AUDIO_H_SEL,
+ MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL,
+ MT8188_CLK_TOP_DPTX_M_SEL,
+ MT8188_CLK_TOP_I2SO1_M_SEL,
+ MT8188_CLK_TOP_I2SO2_M_SEL,
+ MT8188_CLK_TOP_I2SI1_M_SEL,
+ MT8188_CLK_TOP_I2SI2_M_SEL,
+ /* clock gate */
+ MT8188_CLK_ADSP_AUDIO_26M,
+ MT8188_CLK_AUD_AFE,
+ MT8188_CLK_AUD_APLL1_TUNER,
+ MT8188_CLK_AUD_APLL2_TUNER,
+ MT8188_CLK_AUD_TOP0_SPDF,
+ MT8188_CLK_AUD_APLL,
+ MT8188_CLK_AUD_APLL2,
+ MT8188_CLK_AUD_DAC,
+ MT8188_CLK_AUD_ADC,
+ MT8188_CLK_AUD_DAC_HIRES,
+ MT8188_CLK_AUD_A1SYS_HP,
+ MT8188_CLK_AUD_AFE_DMIC1,
+ MT8188_CLK_AUD_AFE_DMIC2,
+ MT8188_CLK_AUD_AFE_DMIC3,
+ MT8188_CLK_AUD_AFE_DMIC4,
+ MT8188_CLK_AUD_ADC_HIRES,
+ MT8188_CLK_AUD_DMIC_HIRES1,
+ MT8188_CLK_AUD_DMIC_HIRES2,
+ MT8188_CLK_AUD_DMIC_HIRES3,
+ MT8188_CLK_AUD_DMIC_HIRES4,
+ MT8188_CLK_AUD_I2SIN,
+ MT8188_CLK_AUD_TDM_IN,
+ MT8188_CLK_AUD_I2S_OUT,
+ MT8188_CLK_AUD_TDM_OUT,
+ MT8188_CLK_AUD_HDMI_OUT,
+ MT8188_CLK_AUD_ASRC11,
+ MT8188_CLK_AUD_ASRC12,
+ MT8188_CLK_AUD_A1SYS,
+ MT8188_CLK_AUD_A2SYS,
+ MT8188_CLK_AUD_PCMIF,
+ MT8188_CLK_AUD_MEMIF_UL1,
+ MT8188_CLK_AUD_MEMIF_UL2,
+ MT8188_CLK_AUD_MEMIF_UL3,
+ MT8188_CLK_AUD_MEMIF_UL4,
+ MT8188_CLK_AUD_MEMIF_UL5,
+ MT8188_CLK_AUD_MEMIF_UL6,
+ MT8188_CLK_AUD_MEMIF_UL8,
+ MT8188_CLK_AUD_MEMIF_UL9,
+ MT8188_CLK_AUD_MEMIF_UL10,
+ MT8188_CLK_AUD_MEMIF_DL2,
+ MT8188_CLK_AUD_MEMIF_DL3,
+ MT8188_CLK_AUD_MEMIF_DL6,
+ MT8188_CLK_AUD_MEMIF_DL7,
+ MT8188_CLK_AUD_MEMIF_DL8,
+ MT8188_CLK_AUD_MEMIF_DL10,
+ MT8188_CLK_AUD_MEMIF_DL11,
+ MT8188_CLK_NUM,
+};
+
+enum {
+ MT8188_AUD_PLL1,
+ MT8188_AUD_PLL2,
+ MT8188_AUD_PLL3,
+ MT8188_AUD_PLL4,
+ MT8188_AUD_PLL5,
+ MT8188_AUD_PLL_NUM,
+};
+
+enum {
+ MT8188_MCK_SEL_26M,
+ MT8188_MCK_SEL_APLL1,
+ MT8188_MCK_SEL_APLL2,
+ MT8188_MCK_SEL_APLL3,
+ MT8188_MCK_SEL_APLL4,
+ MT8188_MCK_SEL_APLL5,
+ MT8188_MCK_SEL_NUM,
+};
+
+struct mtk_base_afe;
+
+int mt8188_afe_get_mclk_source_clk_id(int sel);
+int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll);
+int mt8188_afe_get_default_mclk_source_by_rate(int rate);
+int mt8188_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8188_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
+int mt8188_afe_init_clock(struct mtk_base_afe *afe);
+int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
+void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate);
+int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent);
+int mt8188_apll1_enable(struct mtk_base_afe *afe);
+int mt8188_apll1_disable(struct mtk_base_afe *afe);
+int mt8188_apll2_enable(struct mtk_base_afe *afe);
+int mt8188_apll2_disable(struct mtk_base_afe *afe);
+int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe);
+int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe);
+int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
+int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h
new file mode 100644
index 000000000000..01aa11242e29
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-afe-common.h -- MediaTek 8188 audio driver definitions
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT_8188_AFE_COMMON_H_
+#define _MT_8188_AFE_COMMON_H_
+
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT8188_DAI_START,
+ MT8188_AFE_MEMIF_START = MT8188_DAI_START,
+ MT8188_AFE_MEMIF_DL2 = MT8188_AFE_MEMIF_START,
+ MT8188_AFE_MEMIF_DL3,
+ MT8188_AFE_MEMIF_DL6,
+ MT8188_AFE_MEMIF_DL7,
+ MT8188_AFE_MEMIF_DL8,
+ MT8188_AFE_MEMIF_DL10,
+ MT8188_AFE_MEMIF_DL11,
+ MT8188_AFE_MEMIF_UL_START,
+ MT8188_AFE_MEMIF_UL1 = MT8188_AFE_MEMIF_UL_START,
+ MT8188_AFE_MEMIF_UL2,
+ MT8188_AFE_MEMIF_UL3,
+ MT8188_AFE_MEMIF_UL4,
+ MT8188_AFE_MEMIF_UL5,
+ MT8188_AFE_MEMIF_UL6,
+ MT8188_AFE_MEMIF_UL8,
+ MT8188_AFE_MEMIF_UL9,
+ MT8188_AFE_MEMIF_UL10,
+ MT8188_AFE_MEMIF_END,
+ MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START),
+ MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END,
+ MT8188_AFE_IO_DL_SRC = MT8188_AFE_IO_START,
+ MT8188_AFE_IO_DMIC_IN,
+ MT8188_AFE_IO_DPTX,
+ MT8188_AFE_IO_ETDM_START,
+ MT8188_AFE_IO_ETDM1_IN = MT8188_AFE_IO_ETDM_START,
+ MT8188_AFE_IO_ETDM2_IN,
+ MT8188_AFE_IO_ETDM1_OUT,
+ MT8188_AFE_IO_ETDM2_OUT,
+ MT8188_AFE_IO_ETDM3_OUT,
+ MT8188_AFE_IO_ETDM_END,
+ MT8188_AFE_IO_ETDM_NUM =
+ (MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START),
+ MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END,
+ MT8188_AFE_IO_UL_SRC,
+ MT8188_AFE_IO_END,
+ MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START),
+ MT8188_DAI_END = MT8188_AFE_IO_END,
+ MT8188_DAI_NUM = (MT8188_DAI_END - MT8188_DAI_START),
+};
+
+enum {
+ MT8188_TOP_CG_A1SYS_TIMING,
+ MT8188_TOP_CG_A2SYS_TIMING,
+ MT8188_TOP_CG_26M_TIMING,
+ MT8188_TOP_CG_NUM,
+};
+
+enum {
+ MT8188_AFE_IRQ_1,
+ MT8188_AFE_IRQ_2,
+ MT8188_AFE_IRQ_3,
+ MT8188_AFE_IRQ_8,
+ MT8188_AFE_IRQ_9,
+ MT8188_AFE_IRQ_10,
+ MT8188_AFE_IRQ_13,
+ MT8188_AFE_IRQ_14,
+ MT8188_AFE_IRQ_15,
+ MT8188_AFE_IRQ_16,
+ MT8188_AFE_IRQ_17,
+ MT8188_AFE_IRQ_18,
+ MT8188_AFE_IRQ_19,
+ MT8188_AFE_IRQ_20,
+ MT8188_AFE_IRQ_21,
+ MT8188_AFE_IRQ_22,
+ MT8188_AFE_IRQ_23,
+ MT8188_AFE_IRQ_24,
+ MT8188_AFE_IRQ_25,
+ MT8188_AFE_IRQ_26,
+ MT8188_AFE_IRQ_27,
+ MT8188_AFE_IRQ_28,
+ MT8188_AFE_IRQ_NUM,
+};
+
+enum {
+ MT8188_ETDM_OUT1_1X_EN = 9,
+ MT8188_ETDM_OUT2_1X_EN = 10,
+ MT8188_ETDM_OUT3_1X_EN = 11,
+ MT8188_ETDM_IN1_1X_EN = 12,
+ MT8188_ETDM_IN2_1X_EN = 13,
+ MT8188_ETDM_IN1_NX_EN = 25,
+ MT8188_ETDM_IN2_NX_EN = 26,
+};
+
+enum {
+ MT8188_MTKAIF_MISO_0,
+ MT8188_MTKAIF_MISO_1,
+ MT8188_MTKAIF_MISO_NUM,
+};
+
+struct mtk_dai_memif_irq_priv {
+ unsigned int asys_timing_sel;
+};
+
+struct mtkaif_param {
+ bool mtkaif_calibration_ok;
+ int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_dmic_on;
+};
+
+struct clk;
+
+struct mt8188_afe_private {
+ struct clk **clk;
+ struct clk_lookup **lookup;
+ struct regmap *topckgen;
+ int pm_runtime_bypass_reg_ctl;
+ spinlock_t afe_ctrl_lock; /* Lock for afe control */
+ struct mtk_dai_memif_irq_priv irq_priv[MT8188_AFE_IRQ_NUM];
+ struct mtkaif_param mtkaif_params;
+
+ /* dai */
+ void *dai_priv[MT8188_DAI_NUM];
+};
+
+int mt8188_afe_fs_timing(unsigned int rate);
+/* dai register */
+int mt8188_dai_adda_register(struct mtk_base_afe *afe);
+int mt8188_dai_dmic_register(struct mtk_base_afe *afe);
+int mt8188_dai_etdm_register(struct mtk_base_afe *afe);
+int mt8188_dai_pcm_register(struct mtk_base_afe *afe);
+
+#define MT8188_SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put, id) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .device = id, \
+ .private_value = (unsigned long)&(xenum), \
+}
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
new file mode 100644
index 000000000000..7b1f5d05f4d6
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
@@ -0,0 +1,3399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC AFE platform driver for 8188
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/soc/mediatek/infracfg.h>
+#include <linux/reset.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-common.h"
+#include "mt8188-afe-clk.h"
+#include "mt8188-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define MT8188_MEMIF_BUFFER_BYTES_ALIGN (0x40)
+#define MT8188_MEMIF_DL7_MAX_PERIOD_SIZE (0x3fff)
+
+#define MEMIF_AXI_MINLEN 9 /* register default value */
+
+struct mtk_dai_memif_priv {
+ unsigned int asys_timing_sel;
+ unsigned int fs_timing;
+};
+
+static const struct snd_pcm_hardware mt8188_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 64,
+ .period_bytes_max = 256 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 2 * 1024,
+};
+
+struct mt8188_afe_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+static const struct mt8188_afe_rate mt8188_afe_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 12000, .reg_value = 1, },
+ { .rate = 16000, .reg_value = 2, },
+ { .rate = 24000, .reg_value = 3, },
+ { .rate = 32000, .reg_value = 4, },
+ { .rate = 48000, .reg_value = 5, },
+ { .rate = 96000, .reg_value = 6, },
+ { .rate = 192000, .reg_value = 7, },
+ { .rate = 384000, .reg_value = 8, },
+ { .rate = 7350, .reg_value = 16, },
+ { .rate = 11025, .reg_value = 17, },
+ { .rate = 14700, .reg_value = 18, },
+ { .rate = 22050, .reg_value = 19, },
+ { .rate = 29400, .reg_value = 20, },
+ { .rate = 44100, .reg_value = 21, },
+ { .rate = 88200, .reg_value = 22, },
+ { .rate = 176400, .reg_value = 23, },
+ { .rate = 352800, .reg_value = 24, },
+};
+
+int mt8188_afe_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8188_afe_rates); i++)
+ if (mt8188_afe_rates[i].rate == rate)
+ return mt8188_afe_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static int mt8188_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = NULL;
+ struct mtk_base_afe *afe = NULL;
+ struct mt8188_afe_private *afe_priv = NULL;
+ struct mtk_base_afe_memif *memif = NULL;
+ struct mtk_dai_memif_priv *memif_priv = NULL;
+ int fs = mt8188_afe_fs_timing(rate);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+
+ if (id < 0)
+ return -EINVAL;
+
+ component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ if (!component)
+ return -EINVAL;
+
+ afe = snd_soc_component_get_drvdata(component);
+ memif = &afe->memif[id];
+
+ switch (memif->data->id) {
+ case MT8188_AFE_MEMIF_DL10:
+ fs = MT8188_ETDM_OUT3_1X_EN;
+ break;
+ case MT8188_AFE_MEMIF_UL8:
+ fs = MT8188_ETDM_IN1_NX_EN;
+ break;
+ case MT8188_AFE_MEMIF_UL3:
+ fs = MT8188_ETDM_IN2_NX_EN;
+ break;
+ default:
+ afe_priv = afe->platform_priv;
+ memif_priv = afe_priv->dai_priv[id];
+ if (memif_priv->fs_timing)
+ fs = memif_priv->fs_timing;
+ break;
+ }
+
+ return fs;
+}
+
+static int mt8188_irq_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ int fs = mt8188_memif_fs(substream, rate);
+
+ switch (fs) {
+ case MT8188_ETDM_IN1_NX_EN:
+ fs = MT8188_ETDM_IN1_1X_EN;
+ break;
+ case MT8188_ETDM_IN2_NX_EN:
+ fs = MT8188_ETDM_IN2_1X_EN;
+ break;
+ default:
+ break;
+ }
+
+ return fs;
+}
+
+enum {
+ MT8188_AFE_CM0,
+ MT8188_AFE_CM1,
+ MT8188_AFE_CM2,
+ MT8188_AFE_CM_NUM,
+};
+
+struct mt8188_afe_channel_merge {
+ int id;
+ int reg;
+ unsigned int sel_shift;
+ unsigned int sel_maskbit;
+ unsigned int sel_default;
+ unsigned int ch_num_shift;
+ unsigned int ch_num_maskbit;
+ unsigned int en_shift;
+ unsigned int en_maskbit;
+ unsigned int update_cnt_shift;
+ unsigned int update_cnt_maskbit;
+ unsigned int update_cnt_default;
+};
+
+static const struct mt8188_afe_channel_merge
+ mt8188_afe_cm[MT8188_AFE_CM_NUM] = {
+ [MT8188_AFE_CM0] = {
+ .id = MT8188_AFE_CM0,
+ .reg = AFE_CM0_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x3f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+ [MT8188_AFE_CM1] = {
+ .id = MT8188_AFE_CM1,
+ .reg = AFE_CM1_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x1f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+ [MT8188_AFE_CM2] = {
+ .id = MT8188_AFE_CM2,
+ .reg = AFE_CM2_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x1f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+};
+
+static int mt8188_afe_memif_is_ul(int id)
+{
+ if (id >= MT8188_AFE_MEMIF_UL_START && id < MT8188_AFE_MEMIF_END)
+ return 1;
+ else
+ return 0;
+}
+
+static const struct mt8188_afe_channel_merge *
+ mt8188_afe_found_cm(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = -EINVAL;
+
+ if (mt8188_afe_memif_is_ul(dai->id) == 0)
+ return NULL;
+
+ switch (dai->id) {
+ case MT8188_AFE_MEMIF_UL9:
+ id = MT8188_AFE_CM0;
+ break;
+ case MT8188_AFE_MEMIF_UL2:
+ id = MT8188_AFE_CM1;
+ break;
+ case MT8188_AFE_MEMIF_UL10:
+ id = MT8188_AFE_CM2;
+ break;
+ default:
+ break;
+ }
+
+ if (id < 0) {
+ dev_dbg(afe->dev, "%s, memif %d cannot find CM!\n", __func__, dai->id);
+ return NULL;
+ }
+
+ return &mt8188_afe_cm[id];
+}
+
+static int mt8188_afe_config_cm(struct mtk_base_afe *afe,
+ const struct mt8188_afe_channel_merge *cm,
+ unsigned int channels)
+{
+ if (!cm)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->sel_maskbit << cm->sel_shift,
+ cm->sel_default << cm->sel_shift);
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->ch_num_maskbit << cm->ch_num_shift,
+ (channels - 1) << cm->ch_num_shift);
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->update_cnt_maskbit << cm->update_cnt_shift,
+ cm->update_cnt_default << cm->update_cnt_shift);
+
+ return 0;
+}
+
+static int mt8188_afe_enable_cm(struct mtk_base_afe *afe,
+ const struct mt8188_afe_channel_merge *cm,
+ bool enable)
+{
+ if (!cm)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->en_maskbit << cm->en_shift,
+ enable << cm->en_shift);
+
+ return 0;
+}
+
+static int mt8188_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ int ret;
+
+ ret = mtk_afe_fe_startup(substream, dai);
+
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ MT8188_MEMIF_BUFFER_BYTES_ALIGN);
+
+ if (id != MT8188_AFE_MEMIF_DL7)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 1,
+ MT8188_MEMIF_DL7_MAX_PERIOD_SIZE);
+ if (ret < 0)
+ dev_dbg(afe->dev, "hw_constraint_minmax failed\n");
+out:
+ return ret;
+}
+
+static void mt8188_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt8188_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ const struct mtk_base_memif_data *data = memif->data;
+ const struct mt8188_afe_channel_merge *cm = mt8188_afe_found_cm(dai);
+ unsigned int channels = params_channels(params);
+
+ mt8188_afe_config_cm(afe, cm, channels);
+
+ if (data->ch_num_reg >= 0) {
+ regmap_update_bits(afe->regmap, data->ch_num_reg,
+ data->ch_num_maskbit << data->ch_num_shift,
+ channels << data->ch_num_shift);
+ }
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt8188_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ const struct mt8188_afe_channel_merge *cm = mt8188_afe_found_cm(dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int counter = runtime->period_size;
+ int fs;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ mt8188_afe_enable_cm(afe, cm, true);
+
+ ret = mtk_memif_set_enable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+ return ret;
+ }
+
+ /* set irq counter */
+ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit << irq_data->irq_cnt_shift,
+ counter << irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, runtime->rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ if (irq_data->irq_fs_reg >= 0)
+ regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit << irq_data->irq_fs_shift,
+ fs << irq_data->irq_fs_shift);
+
+ /* delay for uplink */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ u32 sample_delay;
+
+ sample_delay = ((MEMIF_AXI_MINLEN + 1) * 64 +
+ (runtime->channels * runtime->sample_bits - 1)) /
+ (runtime->channels * runtime->sample_bits) + 1;
+
+ udelay(sample_delay * 1000000 / runtime->rate);
+ }
+
+ /* enable interrupt */
+ regmap_set_bits(afe->regmap, irq_data->irq_en_reg,
+ BIT(irq_data->irq_en_shift));
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ mt8188_afe_enable_cm(afe, cm, false);
+
+ ret = mtk_memif_set_disable(afe, id);
+ if (ret)
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+
+ /* disable interrupt */
+
+ regmap_clear_bits(afe->regmap, irq_data->irq_en_reg,
+ BIT(irq_data->irq_en_shift));
+ /* and clear pending IRQ */
+ regmap_write(afe->regmap, irq_data->irq_clr_reg,
+ BIT(irq_data->irq_clr_shift));
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops mt8188_afe_fe_dai_ops = {
+ .startup = mt8188_afe_fe_startup,
+ .shutdown = mt8188_afe_fe_shutdown,
+ .hw_params = mt8188_afe_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mt8188_afe_fe_trigger,
+};
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_352800 |\
+ SNDRV_PCM_RATE_384000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt8188_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL2",
+ .id = MT8188_AFE_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT8188_AFE_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL6",
+ .id = MT8188_AFE_MEMIF_DL6,
+ .playback = {
+ .stream_name = "DL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL7",
+ .id = MT8188_AFE_MEMIF_DL7,
+ .playback = {
+ .stream_name = "DL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL8",
+ .id = MT8188_AFE_MEMIF_DL8,
+ .playback = {
+ .stream_name = "DL8",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL10",
+ .id = MT8188_AFE_MEMIF_DL10,
+ .playback = {
+ .stream_name = "DL10",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL11",
+ .id = MT8188_AFE_MEMIF_DL11,
+ .playback = {
+ .stream_name = "DL11",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT8188_AFE_MEMIF_UL1,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT8188_AFE_MEMIF_UL2,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT8188_AFE_MEMIF_UL3,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL4",
+ .id = MT8188_AFE_MEMIF_UL4,
+ .capture = {
+ .stream_name = "UL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL5",
+ .id = MT8188_AFE_MEMIF_UL5,
+ .capture = {
+ .stream_name = "UL5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL6",
+ .id = MT8188_AFE_MEMIF_UL6,
+ .capture = {
+ .stream_name = "UL6",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL8",
+ .id = MT8188_AFE_MEMIF_UL8,
+ .capture = {
+ .stream_name = "UL8",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL9",
+ .id = MT8188_AFE_MEMIF_UL9,
+ .capture = {
+ .stream_name = "UL9",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL10",
+ .id = MT8188_AFE_MEMIF_UL10,
+ .capture = {
+ .stream_name = "UL10",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+};
+
+static const struct snd_kcontrol_new o002_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN2, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I004 Switch", AFE_CONN2, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN2, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN2, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN2, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN2_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN2_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN2_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o003_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN3, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I005 Switch", AFE_CONN3, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I006 Switch", AFE_CONN3, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN3, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN3, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN3, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN3_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN3_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN3_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o004_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN4, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I006 Switch", AFE_CONN4, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I008 Switch", AFE_CONN4, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN4, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN4, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN4_2, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new o005_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN5, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I007 Switch", AFE_CONN5, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I010 Switch", AFE_CONN5, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN5, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN5, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN5_2, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new o006_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN6, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I008 Switch", AFE_CONN6, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN6, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN6, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN6_2, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new o007_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN7, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I009 Switch", AFE_CONN7, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN7, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN7, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN7_2, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new o008_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I010 Switch", AFE_CONN8, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN8, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN8, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN8_2, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new o009_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I011 Switch", AFE_CONN9, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN9, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN9, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN9_2, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new o010_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN10, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN10, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN10_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN10_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN10_2, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I188 Switch", AFE_CONN10_5, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new o011_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN11, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN11, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN11_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN11_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN11_2, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I189 Switch", AFE_CONN11_5, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new o012_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN12, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN12_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN12_1, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN12_2, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN12_2, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I190 Switch", AFE_CONN12_5, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new o013_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN13, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN13_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN13_1, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN13_2, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN13_2, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I191 Switch", AFE_CONN13_5, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new o014_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN14, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN14_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN14_1, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN14_2, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN14_2, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I192 Switch", AFE_CONN14_6, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new o015_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN15, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN15_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN15_1, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN15_2, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN15_2, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I193 Switch", AFE_CONN15_6, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new o016_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN16, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN16_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN16_1, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN16_2, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN16_2, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I194 Switch", AFE_CONN16_6, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new o017_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN17, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN17_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN17_1, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN17_2, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN17_2, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I195 Switch", AFE_CONN17_6, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new o018_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN18_2, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new o019_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN19_2, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new o020_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN20_2, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new o021_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN21_2, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new o022_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN22_2, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new o023_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN23_2, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new o024_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN24_2, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new o025_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN25_2, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new o026_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN26_1, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new o027_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN27_1, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new o028_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN28_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new o029_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN29_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new o030_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN30_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new o031_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN31_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new o032_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN32_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new o033_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN33_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new o034_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN34, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN34, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN34, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN34, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN34_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN34_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN34_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o035_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN35, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN35, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN35, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN35, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN35_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN35_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN35_5, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN35_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o036_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN36, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN36, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN36, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN36_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN36_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o037_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN37, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN37, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN37, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN37_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN37_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o038_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN38, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN38_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o039_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN39, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN39_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o040_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN40, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN40, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN40, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN40_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o041_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN41, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN41, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN41, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN41_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o042_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN42, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN42, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new o043_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN43, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN43, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new o044_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN44, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN44, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new o045_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN45, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN45, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new o046_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN46, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN46, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new o047_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN47, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN47, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new o182_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN182, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN182, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN182, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new o183_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN183, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN183, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN183, 25, 1, 0),
+};
+
+static const char * const dl8_dl11_data_sel_mux_text[] = {
+ "dl8", "dl11",
+};
+
+static SOC_ENUM_SINGLE_DECL(dl8_dl11_data_sel_mux_enum,
+ AFE_DAC_CON2, 0, dl8_dl11_data_sel_mux_text);
+
+static const struct snd_kcontrol_new dl8_dl11_data_sel_mux =
+ SOC_DAPM_ENUM("DL8_DL11 Sink",
+ dl8_dl11_data_sel_mux_enum);
+
+static const struct snd_soc_dapm_widget mt8188_memif_widgets[] = {
+ /* DL6 */
+ SND_SOC_DAPM_MIXER("I000", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I001", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL3 */
+ SND_SOC_DAPM_MIXER("I020", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I021", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL11 */
+ SND_SOC_DAPM_MIXER("I022", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I023", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I024", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I025", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I026", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I027", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I028", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I029", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I030", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I031", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I034", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I035", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I036", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I037", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL11/DL8 */
+ SND_SOC_DAPM_MIXER("I046", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I047", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I048", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I049", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I050", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I051", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I052", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I053", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I054", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I055", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I056", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I057", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I058", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I059", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I060", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I061", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL2 */
+ SND_SOC_DAPM_MIXER("I070", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I071", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DL8_DL11 Mux",
+ SND_SOC_NOPM, 0, 0, &dl8_dl11_data_sel_mux),
+
+ /* UL9 */
+ SND_SOC_DAPM_MIXER("O002", SND_SOC_NOPM, 0, 0,
+ o002_mix, ARRAY_SIZE(o002_mix)),
+ SND_SOC_DAPM_MIXER("O003", SND_SOC_NOPM, 0, 0,
+ o003_mix, ARRAY_SIZE(o003_mix)),
+ SND_SOC_DAPM_MIXER("O004", SND_SOC_NOPM, 0, 0,
+ o004_mix, ARRAY_SIZE(o004_mix)),
+ SND_SOC_DAPM_MIXER("O005", SND_SOC_NOPM, 0, 0,
+ o005_mix, ARRAY_SIZE(o005_mix)),
+ SND_SOC_DAPM_MIXER("O006", SND_SOC_NOPM, 0, 0,
+ o006_mix, ARRAY_SIZE(o006_mix)),
+ SND_SOC_DAPM_MIXER("O007", SND_SOC_NOPM, 0, 0,
+ o007_mix, ARRAY_SIZE(o007_mix)),
+ SND_SOC_DAPM_MIXER("O008", SND_SOC_NOPM, 0, 0,
+ o008_mix, ARRAY_SIZE(o008_mix)),
+ SND_SOC_DAPM_MIXER("O009", SND_SOC_NOPM, 0, 0,
+ o009_mix, ARRAY_SIZE(o009_mix)),
+ SND_SOC_DAPM_MIXER("O010", SND_SOC_NOPM, 0, 0,
+ o010_mix, ARRAY_SIZE(o010_mix)),
+ SND_SOC_DAPM_MIXER("O011", SND_SOC_NOPM, 0, 0,
+ o011_mix, ARRAY_SIZE(o011_mix)),
+ SND_SOC_DAPM_MIXER("O012", SND_SOC_NOPM, 0, 0,
+ o012_mix, ARRAY_SIZE(o012_mix)),
+ SND_SOC_DAPM_MIXER("O013", SND_SOC_NOPM, 0, 0,
+ o013_mix, ARRAY_SIZE(o013_mix)),
+ SND_SOC_DAPM_MIXER("O014", SND_SOC_NOPM, 0, 0,
+ o014_mix, ARRAY_SIZE(o014_mix)),
+ SND_SOC_DAPM_MIXER("O015", SND_SOC_NOPM, 0, 0,
+ o015_mix, ARRAY_SIZE(o015_mix)),
+ SND_SOC_DAPM_MIXER("O016", SND_SOC_NOPM, 0, 0,
+ o016_mix, ARRAY_SIZE(o016_mix)),
+ SND_SOC_DAPM_MIXER("O017", SND_SOC_NOPM, 0, 0,
+ o017_mix, ARRAY_SIZE(o017_mix)),
+ SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0,
+ o018_mix, ARRAY_SIZE(o018_mix)),
+ SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0,
+ o019_mix, ARRAY_SIZE(o019_mix)),
+ SND_SOC_DAPM_MIXER("O020", SND_SOC_NOPM, 0, 0,
+ o020_mix, ARRAY_SIZE(o020_mix)),
+ SND_SOC_DAPM_MIXER("O021", SND_SOC_NOPM, 0, 0,
+ o021_mix, ARRAY_SIZE(o021_mix)),
+ SND_SOC_DAPM_MIXER("O022", SND_SOC_NOPM, 0, 0,
+ o022_mix, ARRAY_SIZE(o022_mix)),
+ SND_SOC_DAPM_MIXER("O023", SND_SOC_NOPM, 0, 0,
+ o023_mix, ARRAY_SIZE(o023_mix)),
+ SND_SOC_DAPM_MIXER("O024", SND_SOC_NOPM, 0, 0,
+ o024_mix, ARRAY_SIZE(o024_mix)),
+ SND_SOC_DAPM_MIXER("O025", SND_SOC_NOPM, 0, 0,
+ o025_mix, ARRAY_SIZE(o025_mix)),
+ SND_SOC_DAPM_MIXER("O026", SND_SOC_NOPM, 0, 0,
+ o026_mix, ARRAY_SIZE(o026_mix)),
+ SND_SOC_DAPM_MIXER("O027", SND_SOC_NOPM, 0, 0,
+ o027_mix, ARRAY_SIZE(o027_mix)),
+ SND_SOC_DAPM_MIXER("O028", SND_SOC_NOPM, 0, 0,
+ o028_mix, ARRAY_SIZE(o028_mix)),
+ SND_SOC_DAPM_MIXER("O029", SND_SOC_NOPM, 0, 0,
+ o029_mix, ARRAY_SIZE(o029_mix)),
+ SND_SOC_DAPM_MIXER("O030", SND_SOC_NOPM, 0, 0,
+ o030_mix, ARRAY_SIZE(o030_mix)),
+ SND_SOC_DAPM_MIXER("O031", SND_SOC_NOPM, 0, 0,
+ o031_mix, ARRAY_SIZE(o031_mix)),
+ SND_SOC_DAPM_MIXER("O032", SND_SOC_NOPM, 0, 0,
+ o032_mix, ARRAY_SIZE(o032_mix)),
+ SND_SOC_DAPM_MIXER("O033", SND_SOC_NOPM, 0, 0,
+ o033_mix, ARRAY_SIZE(o033_mix)),
+
+ /* UL4 */
+ SND_SOC_DAPM_MIXER("O034", SND_SOC_NOPM, 0, 0,
+ o034_mix, ARRAY_SIZE(o034_mix)),
+ SND_SOC_DAPM_MIXER("O035", SND_SOC_NOPM, 0, 0,
+ o035_mix, ARRAY_SIZE(o035_mix)),
+
+ /* UL5 */
+ SND_SOC_DAPM_MIXER("O036", SND_SOC_NOPM, 0, 0,
+ o036_mix, ARRAY_SIZE(o036_mix)),
+ SND_SOC_DAPM_MIXER("O037", SND_SOC_NOPM, 0, 0,
+ o037_mix, ARRAY_SIZE(o037_mix)),
+
+ /* UL10 */
+ SND_SOC_DAPM_MIXER("O038", SND_SOC_NOPM, 0, 0,
+ o038_mix, ARRAY_SIZE(o038_mix)),
+ SND_SOC_DAPM_MIXER("O039", SND_SOC_NOPM, 0, 0,
+ o039_mix, ARRAY_SIZE(o039_mix)),
+ SND_SOC_DAPM_MIXER("O182", SND_SOC_NOPM, 0, 0,
+ o182_mix, ARRAY_SIZE(o182_mix)),
+ SND_SOC_DAPM_MIXER("O183", SND_SOC_NOPM, 0, 0,
+ o183_mix, ARRAY_SIZE(o183_mix)),
+
+ /* UL2 */
+ SND_SOC_DAPM_MIXER("O040", SND_SOC_NOPM, 0, 0,
+ o040_mix, ARRAY_SIZE(o040_mix)),
+ SND_SOC_DAPM_MIXER("O041", SND_SOC_NOPM, 0, 0,
+ o041_mix, ARRAY_SIZE(o041_mix)),
+ SND_SOC_DAPM_MIXER("O042", SND_SOC_NOPM, 0, 0,
+ o042_mix, ARRAY_SIZE(o042_mix)),
+ SND_SOC_DAPM_MIXER("O043", SND_SOC_NOPM, 0, 0,
+ o043_mix, ARRAY_SIZE(o043_mix)),
+ SND_SOC_DAPM_MIXER("O044", SND_SOC_NOPM, 0, 0,
+ o044_mix, ARRAY_SIZE(o044_mix)),
+ SND_SOC_DAPM_MIXER("O045", SND_SOC_NOPM, 0, 0,
+ o045_mix, ARRAY_SIZE(o045_mix)),
+ SND_SOC_DAPM_MIXER("O046", SND_SOC_NOPM, 0, 0,
+ o046_mix, ARRAY_SIZE(o046_mix)),
+ SND_SOC_DAPM_MIXER("O047", SND_SOC_NOPM, 0, 0,
+ o047_mix, ARRAY_SIZE(o047_mix)),
+};
+
+static const struct snd_soc_dapm_route mt8188_memif_routes[] = {
+ {"I000", NULL, "DL6"},
+ {"I001", NULL, "DL6"},
+
+ {"I020", NULL, "DL3"},
+ {"I021", NULL, "DL3"},
+
+ {"I022", NULL, "DL11"},
+ {"I023", NULL, "DL11"},
+ {"I024", NULL, "DL11"},
+ {"I025", NULL, "DL11"},
+ {"I026", NULL, "DL11"},
+ {"I027", NULL, "DL11"},
+ {"I028", NULL, "DL11"},
+ {"I029", NULL, "DL11"},
+ {"I030", NULL, "DL11"},
+ {"I031", NULL, "DL11"},
+ {"I032", NULL, "DL11"},
+ {"I033", NULL, "DL11"},
+ {"I034", NULL, "DL11"},
+ {"I035", NULL, "DL11"},
+ {"I036", NULL, "DL11"},
+ {"I037", NULL, "DL11"},
+
+ {"DL8_DL11 Mux", "dl8", "DL8"},
+ {"DL8_DL11 Mux", "dl11", "DL11"},
+
+ {"I046", NULL, "DL8_DL11 Mux"},
+ {"I047", NULL, "DL8_DL11 Mux"},
+ {"I048", NULL, "DL8_DL11 Mux"},
+ {"I049", NULL, "DL8_DL11 Mux"},
+ {"I050", NULL, "DL8_DL11 Mux"},
+ {"I051", NULL, "DL8_DL11 Mux"},
+ {"I052", NULL, "DL8_DL11 Mux"},
+ {"I053", NULL, "DL8_DL11 Mux"},
+ {"I054", NULL, "DL8_DL11 Mux"},
+ {"I055", NULL, "DL8_DL11 Mux"},
+ {"I056", NULL, "DL8_DL11 Mux"},
+ {"I057", NULL, "DL8_DL11 Mux"},
+ {"I058", NULL, "DL8_DL11 Mux"},
+ {"I059", NULL, "DL8_DL11 Mux"},
+ {"I060", NULL, "DL8_DL11 Mux"},
+ {"I061", NULL, "DL8_DL11 Mux"},
+
+ {"I070", NULL, "DL2"},
+ {"I071", NULL, "DL2"},
+
+ {"UL9", NULL, "O002"},
+ {"UL9", NULL, "O003"},
+ {"UL9", NULL, "O004"},
+ {"UL9", NULL, "O005"},
+ {"UL9", NULL, "O006"},
+ {"UL9", NULL, "O007"},
+ {"UL9", NULL, "O008"},
+ {"UL9", NULL, "O009"},
+ {"UL9", NULL, "O010"},
+ {"UL9", NULL, "O011"},
+ {"UL9", NULL, "O012"},
+ {"UL9", NULL, "O013"},
+ {"UL9", NULL, "O014"},
+ {"UL9", NULL, "O015"},
+ {"UL9", NULL, "O016"},
+ {"UL9", NULL, "O017"},
+ {"UL9", NULL, "O018"},
+ {"UL9", NULL, "O019"},
+ {"UL9", NULL, "O020"},
+ {"UL9", NULL, "O021"},
+ {"UL9", NULL, "O022"},
+ {"UL9", NULL, "O023"},
+ {"UL9", NULL, "O024"},
+ {"UL9", NULL, "O025"},
+ {"UL9", NULL, "O026"},
+ {"UL9", NULL, "O027"},
+ {"UL9", NULL, "O028"},
+ {"UL9", NULL, "O029"},
+ {"UL9", NULL, "O030"},
+ {"UL9", NULL, "O031"},
+ {"UL9", NULL, "O032"},
+ {"UL9", NULL, "O033"},
+
+ {"UL4", NULL, "O034"},
+ {"UL4", NULL, "O035"},
+
+ {"UL5", NULL, "O036"},
+ {"UL5", NULL, "O037"},
+
+ {"UL10", NULL, "O038"},
+ {"UL10", NULL, "O039"},
+ {"UL10", NULL, "O182"},
+ {"UL10", NULL, "O183"},
+
+ {"UL2", NULL, "O040"},
+ {"UL2", NULL, "O041"},
+ {"UL2", NULL, "O042"},
+ {"UL2", NULL, "O043"},
+ {"UL2", NULL, "O044"},
+ {"UL2", NULL, "O045"},
+ {"UL2", NULL, "O046"},
+ {"UL2", NULL, "O047"},
+
+ {"O004", "I000 Switch", "I000"},
+ {"O005", "I001 Switch", "I001"},
+
+ {"O006", "I000 Switch", "I000"},
+ {"O007", "I001 Switch", "I001"},
+
+ {"O010", "I022 Switch", "I022"},
+ {"O011", "I023 Switch", "I023"},
+ {"O012", "I024 Switch", "I024"},
+ {"O013", "I025 Switch", "I025"},
+ {"O014", "I026 Switch", "I026"},
+ {"O015", "I027 Switch", "I027"},
+ {"O016", "I028 Switch", "I028"},
+ {"O017", "I029 Switch", "I029"},
+
+ {"O010", "I046 Switch", "I046"},
+ {"O011", "I047 Switch", "I047"},
+ {"O012", "I048 Switch", "I048"},
+ {"O013", "I049 Switch", "I049"},
+ {"O014", "I050 Switch", "I050"},
+ {"O015", "I051 Switch", "I051"},
+ {"O016", "I052 Switch", "I052"},
+ {"O017", "I053 Switch", "I053"},
+
+ {"O002", "I022 Switch", "I022"},
+ {"O003", "I023 Switch", "I023"},
+ {"O004", "I024 Switch", "I024"},
+ {"O005", "I025 Switch", "I025"},
+ {"O006", "I026 Switch", "I026"},
+ {"O007", "I027 Switch", "I027"},
+ {"O008", "I028 Switch", "I028"},
+ {"O009", "I029 Switch", "I029"},
+ {"O010", "I030 Switch", "I030"},
+ {"O011", "I031 Switch", "I031"},
+ {"O012", "I032 Switch", "I032"},
+ {"O013", "I033 Switch", "I033"},
+ {"O014", "I034 Switch", "I034"},
+ {"O015", "I035 Switch", "I035"},
+ {"O016", "I036 Switch", "I036"},
+ {"O017", "I037 Switch", "I037"},
+ {"O026", "I046 Switch", "I046"},
+ {"O027", "I047 Switch", "I047"},
+ {"O028", "I048 Switch", "I048"},
+ {"O029", "I049 Switch", "I049"},
+ {"O030", "I050 Switch", "I050"},
+ {"O031", "I051 Switch", "I051"},
+ {"O032", "I052 Switch", "I052"},
+ {"O033", "I053 Switch", "I053"},
+
+ {"O002", "I000 Switch", "I000"},
+ {"O003", "I001 Switch", "I001"},
+ {"O002", "I020 Switch", "I020"},
+ {"O003", "I021 Switch", "I021"},
+ {"O002", "I070 Switch", "I070"},
+ {"O003", "I071 Switch", "I071"},
+
+ {"O002", "I004 Switch", "I004"},
+ {"O003", "I005 Switch", "I005"},
+ {"O003", "I006 Switch", "I006"},
+ {"O004", "I006 Switch", "I006"},
+ {"O004", "I008 Switch", "I008"},
+ {"O005", "I007 Switch", "I007"},
+ {"O005", "I010 Switch", "I010"},
+ {"O006", "I008 Switch", "I008"},
+ {"O007", "I009 Switch", "I009"},
+ {"O008", "I010 Switch", "I010"},
+ {"O009", "I011 Switch", "I011"},
+
+ {"O034", "I000 Switch", "I000"},
+ {"O035", "I001 Switch", "I001"},
+ {"O034", "I002 Switch", "I002"},
+ {"O035", "I003 Switch", "I003"},
+ {"O034", "I012 Switch", "I012"},
+ {"O035", "I013 Switch", "I013"},
+ {"O034", "I020 Switch", "I020"},
+ {"O035", "I021 Switch", "I021"},
+ {"O034", "I070 Switch", "I070"},
+ {"O035", "I071 Switch", "I071"},
+ {"O034", "I072 Switch", "I072"},
+ {"O035", "I073 Switch", "I073"},
+
+ {"O036", "I000 Switch", "I000"},
+ {"O037", "I001 Switch", "I001"},
+ {"O036", "I012 Switch", "I012"},
+ {"O037", "I013 Switch", "I013"},
+ {"O036", "I020 Switch", "I020"},
+ {"O037", "I021 Switch", "I021"},
+ {"O036", "I070 Switch", "I070"},
+ {"O037", "I071 Switch", "I071"},
+ {"O036", "I168 Switch", "I168"},
+ {"O037", "I169 Switch", "I169"},
+
+ {"O038", "I022 Switch", "I022"},
+ {"O039", "I023 Switch", "I023"},
+ {"O182", "I024 Switch", "I024"},
+ {"O183", "I025 Switch", "I025"},
+
+ {"O038", "I168 Switch", "I168"},
+ {"O039", "I169 Switch", "I169"},
+
+ {"O182", "I020 Switch", "I020"},
+ {"O183", "I021 Switch", "I021"},
+
+ {"O182", "I022 Switch", "I022"},
+ {"O183", "I023 Switch", "I023"},
+
+ {"O040", "I022 Switch", "I022"},
+ {"O041", "I023 Switch", "I023"},
+ {"O042", "I024 Switch", "I024"},
+ {"O043", "I025 Switch", "I025"},
+ {"O044", "I026 Switch", "I026"},
+ {"O045", "I027 Switch", "I027"},
+ {"O046", "I028 Switch", "I028"},
+ {"O047", "I029 Switch", "I029"},
+
+ {"O040", "I002 Switch", "I002"},
+ {"O041", "I003 Switch", "I003"},
+
+ {"O002", "I012 Switch", "I012"},
+ {"O003", "I013 Switch", "I013"},
+ {"O004", "I014 Switch", "I014"},
+ {"O005", "I015 Switch", "I015"},
+ {"O006", "I016 Switch", "I016"},
+ {"O007", "I017 Switch", "I017"},
+ {"O008", "I018 Switch", "I018"},
+ {"O009", "I019 Switch", "I019"},
+ {"O010", "I188 Switch", "I188"},
+ {"O011", "I189 Switch", "I189"},
+ {"O012", "I190 Switch", "I190"},
+ {"O013", "I191 Switch", "I191"},
+ {"O014", "I192 Switch", "I192"},
+ {"O015", "I193 Switch", "I193"},
+ {"O016", "I194 Switch", "I194"},
+ {"O017", "I195 Switch", "I195"},
+
+ {"O040", "I012 Switch", "I012"},
+ {"O041", "I013 Switch", "I013"},
+ {"O042", "I014 Switch", "I014"},
+ {"O043", "I015 Switch", "I015"},
+ {"O044", "I016 Switch", "I016"},
+ {"O045", "I017 Switch", "I017"},
+ {"O046", "I018 Switch", "I018"},
+ {"O047", "I019 Switch", "I019"},
+
+ {"O002", "I072 Switch", "I072"},
+ {"O003", "I073 Switch", "I073"},
+ {"O004", "I074 Switch", "I074"},
+ {"O005", "I075 Switch", "I075"},
+ {"O006", "I076 Switch", "I076"},
+ {"O007", "I077 Switch", "I077"},
+ {"O008", "I078 Switch", "I078"},
+ {"O009", "I079 Switch", "I079"},
+ {"O010", "I080 Switch", "I080"},
+ {"O011", "I081 Switch", "I081"},
+ {"O012", "I082 Switch", "I082"},
+ {"O013", "I083 Switch", "I083"},
+ {"O014", "I084 Switch", "I084"},
+ {"O015", "I085 Switch", "I085"},
+ {"O016", "I086 Switch", "I086"},
+ {"O017", "I087 Switch", "I087"},
+
+ {"O010", "I072 Switch", "I072"},
+ {"O011", "I073 Switch", "I073"},
+ {"O012", "I074 Switch", "I074"},
+ {"O013", "I075 Switch", "I075"},
+ {"O014", "I076 Switch", "I076"},
+ {"O015", "I077 Switch", "I077"},
+ {"O016", "I078 Switch", "I078"},
+ {"O017", "I079 Switch", "I079"},
+ {"O018", "I080 Switch", "I080"},
+ {"O019", "I081 Switch", "I081"},
+ {"O020", "I082 Switch", "I082"},
+ {"O021", "I083 Switch", "I083"},
+ {"O022", "I084 Switch", "I084"},
+ {"O023", "I085 Switch", "I085"},
+ {"O024", "I086 Switch", "I086"},
+ {"O025", "I087 Switch", "I087"},
+
+ {"O002", "I168 Switch", "I168"},
+ {"O003", "I169 Switch", "I169"},
+
+ {"O034", "I168 Switch", "I168"},
+ {"O035", "I168 Switch", "I168"},
+ {"O035", "I169 Switch", "I169"},
+
+ {"O040", "I168 Switch", "I168"},
+ {"O041", "I169 Switch", "I169"},
+};
+
+static const char * const mt8188_afe_1x_en_sel_text[] = {
+ "a1sys_a2sys", "a3sys", "a4sys",
+};
+
+static const unsigned int mt8188_afe_1x_en_sel_values[] = {
+ 0, 1, 2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dl2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 18, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 20, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 22, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl7_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 24, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 26, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 28, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl11_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 30, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul1_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 0, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 2, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 4, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 6, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul5_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 8, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 10, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 12, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul9_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 14, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 16, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq1_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 0, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 2, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 4, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq4_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 6, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq5_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 8, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 10, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq7_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 12, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 14, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq9_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 16, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 18, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq11_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 20, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq12_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 22, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq13_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 24, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq14_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 26, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq15_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 28, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq16_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 30, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+
+static const char * const mt8188_afe_fs_timing_sel_text[] = {
+ "asys",
+ "etdmout1_1x_en",
+ "etdmout2_1x_en",
+ "etdmout3_1x_en",
+ "etdmin1_1x_en",
+ "etdmin2_1x_en",
+ "etdmin1_nx_en",
+ "etdmin2_nx_en",
+};
+
+static const unsigned int mt8188_afe_fs_timing_sel_values[] = {
+ 0,
+ MT8188_ETDM_OUT1_1X_EN,
+ MT8188_ETDM_OUT2_1X_EN,
+ MT8188_ETDM_OUT3_1X_EN,
+ MT8188_ETDM_IN1_1X_EN,
+ MT8188_ETDM_IN2_1X_EN,
+ MT8188_ETDM_IN1_NX_EN,
+ MT8188_ETDM_IN2_NX_EN,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dl2_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl3_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl6_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl8_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl11_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul5_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul9_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul10_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+
+static int mt8188_memif_1x_en_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ long val = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ if (val == memif_priv->asys_timing_sel)
+ return 0;
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ memif_priv->asys_timing_sel = val;
+
+ return ret;
+}
+
+static int mt8188_asys_irq_1x_en_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ unsigned int id = kcontrol->id.device;
+ long val = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (val == afe_priv->irq_priv[id].asys_timing_sel)
+ return 0;
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ afe_priv->irq_priv[id].asys_timing_sel = val;
+
+ return ret;
+}
+
+static int mt8188_memif_fs_timing_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ ucontrol->value.enumerated.item[0] =
+ snd_soc_enum_val_to_item(e, memif_priv->fs_timing);
+
+ return 0;
+}
+
+static int mt8188_memif_fs_timing_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int prev_item = 0;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ prev_item = snd_soc_enum_val_to_item(e, memif_priv->fs_timing);
+
+ if (item[0] == prev_item)
+ return 0;
+
+ memif_priv->fs_timing = snd_soc_enum_item_to_val(e, item[0]);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new mt8188_memif_controls[] = {
+ MT8188_SOC_ENUM_EXT("dl2_1x_en_sel",
+ dl2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL2),
+ MT8188_SOC_ENUM_EXT("dl3_1x_en_sel",
+ dl3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL3),
+ MT8188_SOC_ENUM_EXT("dl6_1x_en_sel",
+ dl6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL6),
+ MT8188_SOC_ENUM_EXT("dl7_1x_en_sel",
+ dl7_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL7),
+ MT8188_SOC_ENUM_EXT("dl8_1x_en_sel",
+ dl8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL8),
+ MT8188_SOC_ENUM_EXT("dl10_1x_en_sel",
+ dl10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL10),
+ MT8188_SOC_ENUM_EXT("dl11_1x_en_sel",
+ dl11_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL11),
+ MT8188_SOC_ENUM_EXT("ul1_1x_en_sel",
+ ul1_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL1),
+ MT8188_SOC_ENUM_EXT("ul2_1x_en_sel",
+ ul2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL2),
+ MT8188_SOC_ENUM_EXT("ul3_1x_en_sel",
+ ul3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL3),
+ MT8188_SOC_ENUM_EXT("ul4_1x_en_sel",
+ ul4_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL4),
+ MT8188_SOC_ENUM_EXT("ul5_1x_en_sel",
+ ul5_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL5),
+ MT8188_SOC_ENUM_EXT("ul6_1x_en_sel",
+ ul6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL6),
+ MT8188_SOC_ENUM_EXT("ul8_1x_en_sel",
+ ul8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL8),
+ MT8188_SOC_ENUM_EXT("ul9_1x_en_sel",
+ ul9_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL9),
+ MT8188_SOC_ENUM_EXT("ul10_1x_en_sel",
+ ul10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL10),
+ MT8188_SOC_ENUM_EXT("asys_irq1_1x_en_sel",
+ asys_irq1_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_13),
+ MT8188_SOC_ENUM_EXT("asys_irq2_1x_en_sel",
+ asys_irq2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_14),
+ MT8188_SOC_ENUM_EXT("asys_irq3_1x_en_sel",
+ asys_irq3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_15),
+ MT8188_SOC_ENUM_EXT("asys_irq4_1x_en_sel",
+ asys_irq4_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_16),
+ MT8188_SOC_ENUM_EXT("asys_irq5_1x_en_sel",
+ asys_irq5_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_17),
+ MT8188_SOC_ENUM_EXT("asys_irq6_1x_en_sel",
+ asys_irq6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_18),
+ MT8188_SOC_ENUM_EXT("asys_irq7_1x_en_sel",
+ asys_irq7_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_19),
+ MT8188_SOC_ENUM_EXT("asys_irq8_1x_en_sel",
+ asys_irq8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_20),
+ MT8188_SOC_ENUM_EXT("asys_irq9_1x_en_sel",
+ asys_irq9_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_21),
+ MT8188_SOC_ENUM_EXT("asys_irq10_1x_en_sel",
+ asys_irq10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_22),
+ MT8188_SOC_ENUM_EXT("asys_irq11_1x_en_sel",
+ asys_irq11_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_23),
+ MT8188_SOC_ENUM_EXT("asys_irq12_1x_en_sel",
+ asys_irq12_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_24),
+ MT8188_SOC_ENUM_EXT("asys_irq13_1x_en_sel",
+ asys_irq13_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_25),
+ MT8188_SOC_ENUM_EXT("asys_irq14_1x_en_sel",
+ asys_irq14_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_26),
+ MT8188_SOC_ENUM_EXT("asys_irq15_1x_en_sel",
+ asys_irq15_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_27),
+ MT8188_SOC_ENUM_EXT("asys_irq16_1x_en_sel",
+ asys_irq16_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_28),
+ MT8188_SOC_ENUM_EXT("dl2_fs_timing_sel",
+ dl2_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL2),
+ MT8188_SOC_ENUM_EXT("dl3_fs_timing_sel",
+ dl3_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL3),
+ MT8188_SOC_ENUM_EXT("dl6_fs_timing_sel",
+ dl6_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL6),
+ MT8188_SOC_ENUM_EXT("dl8_fs_timing_sel",
+ dl8_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL8),
+ MT8188_SOC_ENUM_EXT("dl11_fs_timing_sel",
+ dl11_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL11),
+ MT8188_SOC_ENUM_EXT("ul2_fs_timing_sel",
+ ul2_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL2),
+ MT8188_SOC_ENUM_EXT("ul4_fs_timing_sel",
+ ul4_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL4),
+ MT8188_SOC_ENUM_EXT("ul5_fs_timing_sel",
+ ul5_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL5),
+ MT8188_SOC_ENUM_EXT("ul9_fs_timing_sel",
+ ul9_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL9),
+ MT8188_SOC_ENUM_EXT("ul10_fs_timing_sel",
+ ul10_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL10),
+};
+
+static const struct mtk_base_memif_data memif_data[MT8188_AFE_MEMIF_NUM] = {
+ [MT8188_AFE_MEMIF_DL2] = {
+ .name = "DL2",
+ .id = MT8188_AFE_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .reg_ofs_end = AFE_DL2_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON0,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 18,
+ .hd_reg = AFE_DL2_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 18,
+ .ch_num_reg = AFE_DL2_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 18,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 18,
+ },
+ [MT8188_AFE_MEMIF_DL3] = {
+ .name = "DL3",
+ .id = MT8188_AFE_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .reg_ofs_end = AFE_DL3_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON0,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 19,
+ .hd_reg = AFE_DL3_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 19,
+ .ch_num_reg = AFE_DL3_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 19,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 19,
+ },
+ [MT8188_AFE_MEMIF_DL6] = {
+ .name = "DL6",
+ .id = MT8188_AFE_MEMIF_DL6,
+ .reg_ofs_base = AFE_DL6_BASE,
+ .reg_ofs_cur = AFE_DL6_CUR,
+ .reg_ofs_end = AFE_DL6_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 22,
+ .hd_reg = AFE_DL6_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 22,
+ .ch_num_reg = AFE_DL6_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 22,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 22,
+ },
+ [MT8188_AFE_MEMIF_DL7] = {
+ .name = "DL7",
+ .id = MT8188_AFE_MEMIF_DL7,
+ .reg_ofs_base = AFE_DL7_BASE,
+ .reg_ofs_cur = AFE_DL7_CUR,
+ .reg_ofs_end = AFE_DL7_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 23,
+ .hd_reg = AFE_DL7_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 23,
+ .ch_num_reg = AFE_DL7_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 23,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 23,
+ },
+ [MT8188_AFE_MEMIF_DL8] = {
+ .name = "DL8",
+ .id = MT8188_AFE_MEMIF_DL8,
+ .reg_ofs_base = AFE_DL8_BASE,
+ .reg_ofs_cur = AFE_DL8_CUR,
+ .reg_ofs_end = AFE_DL8_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 24,
+ .hd_reg = AFE_DL8_CON0,
+ .hd_shift = 6,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 24,
+ .ch_num_reg = AFE_DL8_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x3f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 24,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 24,
+ },
+ [MT8188_AFE_MEMIF_DL10] = {
+ .name = "DL10",
+ .id = MT8188_AFE_MEMIF_DL10,
+ .reg_ofs_base = AFE_DL10_BASE,
+ .reg_ofs_cur = AFE_DL10_CUR,
+ .reg_ofs_end = AFE_DL10_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 26,
+ .hd_reg = AFE_DL10_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 26,
+ .ch_num_reg = AFE_DL10_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 26,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 26,
+ },
+ [MT8188_AFE_MEMIF_DL11] = {
+ .name = "DL11",
+ .id = MT8188_AFE_MEMIF_DL11,
+ .reg_ofs_base = AFE_DL11_BASE,
+ .reg_ofs_cur = AFE_DL11_CUR,
+ .reg_ofs_end = AFE_DL11_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 25,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 27,
+ .hd_reg = AFE_DL11_CON0,
+ .hd_shift = 7,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 27,
+ .ch_num_reg = AFE_DL11_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x7f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 27,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 27,
+ },
+ [MT8188_AFE_MEMIF_UL1] = {
+ .name = "UL1",
+ .id = MT8188_AFE_MEMIF_UL1,
+ .reg_ofs_base = AFE_UL1_BASE,
+ .reg_ofs_cur = AFE_UL1_CUR,
+ .reg_ofs_end = AFE_UL1_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = AFE_UL1_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL1_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .hd_reg = AFE_UL1_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 0,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 0,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 0,
+ },
+ [MT8188_AFE_MEMIF_UL2] = {
+ .name = "UL2",
+ .id = MT8188_AFE_MEMIF_UL2,
+ .reg_ofs_base = AFE_UL2_BASE,
+ .reg_ofs_cur = AFE_UL2_CUR,
+ .reg_ofs_end = AFE_UL2_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL2_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL2_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 2,
+ .hd_reg = AFE_UL2_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 1,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 1,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 1,
+ },
+ [MT8188_AFE_MEMIF_UL3] = {
+ .name = "UL3",
+ .id = MT8188_AFE_MEMIF_UL3,
+ .reg_ofs_base = AFE_UL3_BASE,
+ .reg_ofs_cur = AFE_UL3_CUR,
+ .reg_ofs_end = AFE_UL3_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL3_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL3_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 3,
+ .hd_reg = AFE_UL3_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 2,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 2,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 2,
+ },
+ [MT8188_AFE_MEMIF_UL4] = {
+ .name = "UL4",
+ .id = MT8188_AFE_MEMIF_UL4,
+ .reg_ofs_base = AFE_UL4_BASE,
+ .reg_ofs_cur = AFE_UL4_CUR,
+ .reg_ofs_end = AFE_UL4_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL4_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL4_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 4,
+ .hd_reg = AFE_UL4_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 3,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 3,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 3,
+ },
+ [MT8188_AFE_MEMIF_UL5] = {
+ .name = "UL5",
+ .id = MT8188_AFE_MEMIF_UL5,
+ .reg_ofs_base = AFE_UL5_BASE,
+ .reg_ofs_cur = AFE_UL5_CUR,
+ .reg_ofs_end = AFE_UL5_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL5_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL5_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 5,
+ .hd_reg = AFE_UL5_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 4,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 4,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 4,
+ },
+ [MT8188_AFE_MEMIF_UL6] = {
+ .name = "UL6",
+ .id = MT8188_AFE_MEMIF_UL6,
+ .reg_ofs_base = AFE_UL6_BASE,
+ .reg_ofs_cur = AFE_UL6_CUR,
+ .reg_ofs_end = AFE_UL6_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = AFE_UL6_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL6_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 6,
+ .hd_reg = AFE_UL6_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 5,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 5,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 5,
+ },
+ [MT8188_AFE_MEMIF_UL8] = {
+ .name = "UL8",
+ .id = MT8188_AFE_MEMIF_UL8,
+ .reg_ofs_base = AFE_UL8_BASE,
+ .reg_ofs_cur = AFE_UL8_CUR,
+ .reg_ofs_end = AFE_UL8_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL8_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL8_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 8,
+ .hd_reg = AFE_UL8_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 7,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 7,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 7,
+ },
+ [MT8188_AFE_MEMIF_UL9] = {
+ .name = "UL9",
+ .id = MT8188_AFE_MEMIF_UL9,
+ .reg_ofs_base = AFE_UL9_BASE,
+ .reg_ofs_cur = AFE_UL9_CUR,
+ .reg_ofs_end = AFE_UL9_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL9_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL9_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 9,
+ .hd_reg = AFE_UL9_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 8,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 8,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 8,
+ },
+ [MT8188_AFE_MEMIF_UL10] = {
+ .name = "UL10",
+ .id = MT8188_AFE_MEMIF_UL10,
+ .reg_ofs_base = AFE_UL10_BASE,
+ .reg_ofs_cur = AFE_UL10_CUR,
+ .reg_ofs_end = AFE_UL10_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL10_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL10_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 10,
+ .hd_reg = AFE_UL10_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 9,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 9,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 9,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8188_AFE_IRQ_NUM] = {
+ [MT8188_AFE_IRQ_1] = {
+ .id = MT8188_AFE_IRQ_1,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 0,
+ .irq_status_shift = 16,
+ },
+ [MT8188_AFE_IRQ_2] = {
+ .id = MT8188_AFE_IRQ_2,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 1,
+ .irq_status_shift = 17,
+ },
+ [MT8188_AFE_IRQ_3] = {
+ .id = MT8188_AFE_IRQ_3,
+ .irq_cnt_reg = AFE_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 2,
+ .irq_status_shift = 18,
+ },
+ [MT8188_AFE_IRQ_8] = {
+ .id = MT8188_AFE_IRQ_8,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ8_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 7,
+ .irq_status_shift = 23,
+ },
+ [MT8188_AFE_IRQ_9] = {
+ .id = MT8188_AFE_IRQ_9,
+ .irq_cnt_reg = AFE_IRQ9_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ9_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 8,
+ .irq_status_shift = 24,
+ },
+ [MT8188_AFE_IRQ_10] = {
+ .id = MT8188_AFE_IRQ_10,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ10_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 9,
+ .irq_status_shift = 25,
+ },
+ [MT8188_AFE_IRQ_13] = {
+ .id = MT8188_AFE_IRQ_13,
+ .irq_cnt_reg = ASYS_IRQ1_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ1_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 0,
+ .irq_status_shift = 0,
+ },
+ [MT8188_AFE_IRQ_14] = {
+ .id = MT8188_AFE_IRQ_14,
+ .irq_cnt_reg = ASYS_IRQ2_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ2_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 1,
+ .irq_status_shift = 1,
+ },
+ [MT8188_AFE_IRQ_15] = {
+ .id = MT8188_AFE_IRQ_15,
+ .irq_cnt_reg = ASYS_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ3_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 2,
+ .irq_status_shift = 2,
+ },
+ [MT8188_AFE_IRQ_16] = {
+ .id = MT8188_AFE_IRQ_16,
+ .irq_cnt_reg = ASYS_IRQ4_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ4_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ4_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 3,
+ .irq_status_shift = 3,
+ },
+ [MT8188_AFE_IRQ_17] = {
+ .id = MT8188_AFE_IRQ_17,
+ .irq_cnt_reg = ASYS_IRQ5_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ5_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ5_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 4,
+ .irq_status_shift = 4,
+ },
+ [MT8188_AFE_IRQ_18] = {
+ .id = MT8188_AFE_IRQ_18,
+ .irq_cnt_reg = ASYS_IRQ6_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ6_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ6_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 5,
+ .irq_status_shift = 5,
+ },
+ [MT8188_AFE_IRQ_19] = {
+ .id = MT8188_AFE_IRQ_19,
+ .irq_cnt_reg = ASYS_IRQ7_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ7_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ7_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 6,
+ .irq_status_shift = 6,
+ },
+ [MT8188_AFE_IRQ_20] = {
+ .id = MT8188_AFE_IRQ_20,
+ .irq_cnt_reg = ASYS_IRQ8_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ8_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ8_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 7,
+ .irq_status_shift = 7,
+ },
+ [MT8188_AFE_IRQ_21] = {
+ .id = MT8188_AFE_IRQ_21,
+ .irq_cnt_reg = ASYS_IRQ9_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ9_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ9_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 8,
+ .irq_status_shift = 8,
+ },
+ [MT8188_AFE_IRQ_22] = {
+ .id = MT8188_AFE_IRQ_22,
+ .irq_cnt_reg = ASYS_IRQ10_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ10_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ10_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 9,
+ .irq_status_shift = 9,
+ },
+ [MT8188_AFE_IRQ_23] = {
+ .id = MT8188_AFE_IRQ_23,
+ .irq_cnt_reg = ASYS_IRQ11_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ11_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ11_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 10,
+ .irq_status_shift = 10,
+ },
+ [MT8188_AFE_IRQ_24] = {
+ .id = MT8188_AFE_IRQ_24,
+ .irq_cnt_reg = ASYS_IRQ12_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ12_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ12_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 11,
+ .irq_status_shift = 11,
+ },
+ [MT8188_AFE_IRQ_25] = {
+ .id = MT8188_AFE_IRQ_25,
+ .irq_cnt_reg = ASYS_IRQ13_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ13_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ13_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 12,
+ .irq_status_shift = 12,
+ },
+ [MT8188_AFE_IRQ_26] = {
+ .id = MT8188_AFE_IRQ_26,
+ .irq_cnt_reg = ASYS_IRQ14_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ14_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ14_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 13,
+ .irq_status_shift = 13,
+ },
+ [MT8188_AFE_IRQ_27] = {
+ .id = MT8188_AFE_IRQ_27,
+ .irq_cnt_reg = ASYS_IRQ15_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ15_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ15_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 14,
+ .irq_status_shift = 14,
+ },
+ [MT8188_AFE_IRQ_28] = {
+ .id = MT8188_AFE_IRQ_28,
+ .irq_cnt_reg = ASYS_IRQ16_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ16_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ16_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 15,
+ .irq_status_shift = 15,
+ },
+};
+
+static const int mt8188_afe_memif_const_irqs[MT8188_AFE_MEMIF_NUM] = {
+ [MT8188_AFE_MEMIF_DL2] = MT8188_AFE_IRQ_13,
+ [MT8188_AFE_MEMIF_DL3] = MT8188_AFE_IRQ_14,
+ [MT8188_AFE_MEMIF_DL6] = MT8188_AFE_IRQ_15,
+ [MT8188_AFE_MEMIF_DL7] = MT8188_AFE_IRQ_1,
+ [MT8188_AFE_MEMIF_DL8] = MT8188_AFE_IRQ_16,
+ [MT8188_AFE_MEMIF_DL10] = MT8188_AFE_IRQ_17,
+ [MT8188_AFE_MEMIF_DL11] = MT8188_AFE_IRQ_18,
+ [MT8188_AFE_MEMIF_UL1] = MT8188_AFE_IRQ_3,
+ [MT8188_AFE_MEMIF_UL2] = MT8188_AFE_IRQ_19,
+ [MT8188_AFE_MEMIF_UL3] = MT8188_AFE_IRQ_20,
+ [MT8188_AFE_MEMIF_UL4] = MT8188_AFE_IRQ_21,
+ [MT8188_AFE_MEMIF_UL5] = MT8188_AFE_IRQ_22,
+ [MT8188_AFE_MEMIF_UL6] = MT8188_AFE_IRQ_9,
+ [MT8188_AFE_MEMIF_UL8] = MT8188_AFE_IRQ_23,
+ [MT8188_AFE_MEMIF_UL9] = MT8188_AFE_IRQ_24,
+ [MT8188_AFE_MEMIF_UL10] = MT8188_AFE_IRQ_25,
+};
+
+static bool mt8188_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0:
+ case AUDIO_TOP_CON1:
+ case AUDIO_TOP_CON3:
+ case AUDIO_TOP_CON4:
+ case AUDIO_TOP_CON5:
+ case AUDIO_TOP_CON6:
+ case ASYS_IRQ_CLR:
+ case ASYS_IRQ_STATUS:
+ case ASYS_IRQ_MON1:
+ case ASYS_IRQ_MON2:
+ case AFE_IRQ_MCU_CLR:
+ case AFE_IRQ_STATUS:
+ case AFE_IRQ3_CON_MON:
+ case AFE_IRQ_MCU_MON2:
+ case ADSP_IRQ_STATUS:
+ case AUDIO_TOP_STA0:
+ case AUDIO_TOP_STA1:
+ case AFE_GAIN1_CUR:
+ case AFE_GAIN2_CUR:
+ case AFE_IEC_BURST_INFO:
+ case AFE_IEC_CHL_STAT0:
+ case AFE_IEC_CHL_STAT1:
+ case AFE_IEC_CHR_STAT0:
+ case AFE_IEC_CHR_STAT1:
+ case AFE_SPDIFIN_CHSTS1:
+ case AFE_SPDIFIN_CHSTS2:
+ case AFE_SPDIFIN_CHSTS3:
+ case AFE_SPDIFIN_CHSTS4:
+ case AFE_SPDIFIN_CHSTS5:
+ case AFE_SPDIFIN_CHSTS6:
+ case AFE_SPDIFIN_DEBUG1:
+ case AFE_SPDIFIN_DEBUG2:
+ case AFE_SPDIFIN_DEBUG3:
+ case AFE_SPDIFIN_DEBUG4:
+ case AFE_SPDIFIN_EC:
+ case AFE_SPDIFIN_CKLOCK_CFG:
+ case AFE_SPDIFIN_BR_DBG1:
+ case AFE_SPDIFIN_CKFBDIV:
+ case AFE_SPDIFIN_INT_EXT:
+ case AFE_SPDIFIN_INT_EXT2:
+ case SPDIFIN_FREQ_STATUS:
+ case SPDIFIN_USERCODE1:
+ case SPDIFIN_USERCODE2:
+ case SPDIFIN_USERCODE3:
+ case SPDIFIN_USERCODE4:
+ case SPDIFIN_USERCODE5:
+ case SPDIFIN_USERCODE6:
+ case SPDIFIN_USERCODE7:
+ case SPDIFIN_USERCODE8:
+ case SPDIFIN_USERCODE9:
+ case SPDIFIN_USERCODE10:
+ case SPDIFIN_USERCODE11:
+ case SPDIFIN_USERCODE12:
+ case AFE_LINEIN_APLL_TUNER_MON:
+ case AFE_EARC_APLL_TUNER_MON:
+ case AFE_CM0_MON:
+ case AFE_CM1_MON:
+ case AFE_CM2_MON:
+ case AFE_MPHONE_MULTI_DET_MON0:
+ case AFE_MPHONE_MULTI_DET_MON1:
+ case AFE_MPHONE_MULTI_DET_MON2:
+ case AFE_MPHONE_MULTI2_DET_MON0:
+ case AFE_MPHONE_MULTI2_DET_MON1:
+ case AFE_MPHONE_MULTI2_DET_MON2:
+ case AFE_ADDA_MTKAIF_MON0:
+ case AFE_ADDA_MTKAIF_MON1:
+ case AFE_AUD_PAD_TOP:
+ case AFE_ADDA6_MTKAIF_MON0:
+ case AFE_ADDA6_MTKAIF_MON1:
+ case AFE_ADDA6_SRC_DEBUG_MON0:
+ case AFE_ADDA6_UL_SRC_MON0:
+ case AFE_ADDA6_UL_SRC_MON1:
+ case AFE_ASRC11_NEW_CON8:
+ case AFE_ASRC11_NEW_CON9:
+ case AFE_ASRC12_NEW_CON8:
+ case AFE_ASRC12_NEW_CON9:
+ case AFE_LRCK_CNT:
+ case AFE_DAC_MON0:
+ case AFE_DAC_CON0:
+ case AFE_DL2_CUR:
+ case AFE_DL3_CUR:
+ case AFE_DL6_CUR:
+ case AFE_DL7_CUR:
+ case AFE_DL8_CUR:
+ case AFE_DL10_CUR:
+ case AFE_DL11_CUR:
+ case AFE_UL1_CUR:
+ case AFE_UL2_CUR:
+ case AFE_UL3_CUR:
+ case AFE_UL4_CUR:
+ case AFE_UL5_CUR:
+ case AFE_UL6_CUR:
+ case AFE_UL8_CUR:
+ case AFE_UL9_CUR:
+ case AFE_UL10_CUR:
+ case AFE_DL8_CHK_SUM1:
+ case AFE_DL8_CHK_SUM2:
+ case AFE_DL8_CHK_SUM3:
+ case AFE_DL8_CHK_SUM4:
+ case AFE_DL8_CHK_SUM5:
+ case AFE_DL8_CHK_SUM6:
+ case AFE_DL10_CHK_SUM1:
+ case AFE_DL10_CHK_SUM2:
+ case AFE_DL10_CHK_SUM3:
+ case AFE_DL10_CHK_SUM4:
+ case AFE_DL10_CHK_SUM5:
+ case AFE_DL10_CHK_SUM6:
+ case AFE_DL11_CHK_SUM1:
+ case AFE_DL11_CHK_SUM2:
+ case AFE_DL11_CHK_SUM3:
+ case AFE_DL11_CHK_SUM4:
+ case AFE_DL11_CHK_SUM5:
+ case AFE_DL11_CHK_SUM6:
+ case AFE_UL1_CHK_SUM1:
+ case AFE_UL1_CHK_SUM2:
+ case AFE_UL2_CHK_SUM1:
+ case AFE_UL2_CHK_SUM2:
+ case AFE_UL3_CHK_SUM1:
+ case AFE_UL3_CHK_SUM2:
+ case AFE_UL4_CHK_SUM1:
+ case AFE_UL4_CHK_SUM2:
+ case AFE_UL5_CHK_SUM1:
+ case AFE_UL5_CHK_SUM2:
+ case AFE_UL6_CHK_SUM1:
+ case AFE_UL6_CHK_SUM2:
+ case AFE_UL8_CHK_SUM1:
+ case AFE_UL8_CHK_SUM2:
+ case AFE_DL2_CHK_SUM1:
+ case AFE_DL2_CHK_SUM2:
+ case AFE_DL3_CHK_SUM1:
+ case AFE_DL3_CHK_SUM2:
+ case AFE_DL6_CHK_SUM1:
+ case AFE_DL6_CHK_SUM2:
+ case AFE_DL7_CHK_SUM1:
+ case AFE_DL7_CHK_SUM2:
+ case AFE_UL9_CHK_SUM1:
+ case AFE_UL9_CHK_SUM2:
+ case AFE_BUS_MON1:
+ case UL1_MOD2AGT_CNT_LAT:
+ case UL2_MOD2AGT_CNT_LAT:
+ case UL3_MOD2AGT_CNT_LAT:
+ case UL4_MOD2AGT_CNT_LAT:
+ case UL5_MOD2AGT_CNT_LAT:
+ case UL6_MOD2AGT_CNT_LAT:
+ case UL8_MOD2AGT_CNT_LAT:
+ case UL9_MOD2AGT_CNT_LAT:
+ case UL10_MOD2AGT_CNT_LAT:
+ case AFE_MEMIF_BUF_FULL_MON:
+ case AFE_MEMIF_BUF_MON1:
+ case AFE_MEMIF_BUF_MON3:
+ case AFE_MEMIF_BUF_MON4:
+ case AFE_MEMIF_BUF_MON5:
+ case AFE_MEMIF_BUF_MON6:
+ case AFE_MEMIF_BUF_MON7:
+ case AFE_MEMIF_BUF_MON8:
+ case AFE_MEMIF_BUF_MON9:
+ case AFE_MEMIF_BUF_MON10:
+ case DL2_AGENT2MODULE_CNT:
+ case DL3_AGENT2MODULE_CNT:
+ case DL6_AGENT2MODULE_CNT:
+ case DL7_AGENT2MODULE_CNT:
+ case DL8_AGENT2MODULE_CNT:
+ case DL10_AGENT2MODULE_CNT:
+ case DL11_AGENT2MODULE_CNT:
+ case UL1_MODULE2AGENT_CNT:
+ case UL2_MODULE2AGENT_CNT:
+ case UL3_MODULE2AGENT_CNT:
+ case UL4_MODULE2AGENT_CNT:
+ case UL5_MODULE2AGENT_CNT:
+ case UL6_MODULE2AGENT_CNT:
+ case UL8_MODULE2AGENT_CNT:
+ case UL9_MODULE2AGENT_CNT:
+ case UL10_MODULE2AGENT_CNT:
+ case AFE_DMIC0_SRC_DEBUG_MON0:
+ case AFE_DMIC0_UL_SRC_MON0:
+ case AFE_DMIC0_UL_SRC_MON1:
+ case AFE_DMIC1_SRC_DEBUG_MON0:
+ case AFE_DMIC1_UL_SRC_MON0:
+ case AFE_DMIC1_UL_SRC_MON1:
+ case AFE_DMIC2_SRC_DEBUG_MON0:
+ case AFE_DMIC2_UL_SRC_MON0:
+ case AFE_DMIC2_UL_SRC_MON1:
+ case AFE_DMIC3_SRC_DEBUG_MON0:
+ case AFE_DMIC3_UL_SRC_MON0:
+ case AFE_DMIC3_UL_SRC_MON1:
+ case ETDM_IN1_MONITOR:
+ case ETDM_IN2_MONITOR:
+ case ETDM_OUT1_MONITOR:
+ case ETDM_OUT2_MONITOR:
+ case ETDM_OUT3_MONITOR:
+ case AFE_ADDA_SRC_DEBUG_MON0:
+ case AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_DL_SDM_FIFO_MON:
+ case AFE_ADDA_DL_SRC_LCH_MON:
+ case AFE_ADDA_DL_SRC_RCH_MON:
+ case AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_GASRC0_NEW_CON8:
+ case AFE_GASRC0_NEW_CON9:
+ case AFE_GASRC0_NEW_CON12:
+ case AFE_GASRC1_NEW_CON8:
+ case AFE_GASRC1_NEW_CON9:
+ case AFE_GASRC1_NEW_CON12:
+ case AFE_GASRC2_NEW_CON8:
+ case AFE_GASRC2_NEW_CON9:
+ case AFE_GASRC2_NEW_CON12:
+ case AFE_GASRC3_NEW_CON8:
+ case AFE_GASRC3_NEW_CON9:
+ case AFE_GASRC3_NEW_CON12:
+ case AFE_GASRC4_NEW_CON8:
+ case AFE_GASRC4_NEW_CON9:
+ case AFE_GASRC4_NEW_CON12:
+ case AFE_GASRC5_NEW_CON8:
+ case AFE_GASRC5_NEW_CON9:
+ case AFE_GASRC5_NEW_CON12:
+ case AFE_GASRC6_NEW_CON8:
+ case AFE_GASRC6_NEW_CON9:
+ case AFE_GASRC6_NEW_CON12:
+ case AFE_GASRC7_NEW_CON8:
+ case AFE_GASRC7_NEW_CON9:
+ case AFE_GASRC7_NEW_CON12:
+ case AFE_GASRC8_NEW_CON8:
+ case AFE_GASRC8_NEW_CON9:
+ case AFE_GASRC8_NEW_CON12:
+ case AFE_GASRC9_NEW_CON8:
+ case AFE_GASRC9_NEW_CON9:
+ case AFE_GASRC9_NEW_CON12:
+ case AFE_GASRC10_NEW_CON8:
+ case AFE_GASRC10_NEW_CON9:
+ case AFE_GASRC10_NEW_CON12:
+ case AFE_GASRC11_NEW_CON8:
+ case AFE_GASRC11_NEW_CON9:
+ case AFE_GASRC11_NEW_CON12:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8188_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .volatile_reg = mt8188_is_volatile_reg,
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1),
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define AFE_IRQ_CLR_BITS (0x387)
+#define ASYS_IRQ_CLR_BITS (0xffff)
+
+static irqreturn_t mt8188_afe_irq_handler(int irq_id, void *dev_id)
+{
+ struct mtk_base_afe *afe = dev_id;
+ unsigned int val = 0;
+ unsigned int asys_irq_clr_bits = 0;
+ unsigned int afe_irq_clr_bits = 0;
+ unsigned int irq_status_bits = 0;
+ unsigned int irq_clr_bits = 0;
+ unsigned int mcu_irq_mask = 0;
+ int i = 0;
+ int ret = 0;
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &val);
+ if (ret) {
+ dev_err(afe->dev, "%s irq status err\n", __func__);
+ afe_irq_clr_bits = AFE_IRQ_CLR_BITS;
+ asys_irq_clr_bits = ASYS_IRQ_CLR_BITS;
+ goto err_irq;
+ }
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MASK, &mcu_irq_mask);
+ if (ret) {
+ dev_err(afe->dev, "%s read irq mask err\n", __func__);
+ afe_irq_clr_bits = AFE_IRQ_CLR_BITS;
+ asys_irq_clr_bits = ASYS_IRQ_CLR_BITS;
+ goto err_irq;
+ }
+
+ /* only clr cpu irq */
+ val &= mcu_irq_mask;
+
+ for (i = 0; i < MT8188_AFE_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+ struct mtk_base_irq_data const *irq_data;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq_data = afe->irqs[memif->irq_usage].irq_data;
+
+ irq_status_bits = BIT(irq_data->irq_status_shift);
+ irq_clr_bits = BIT(irq_data->irq_clr_shift);
+
+ if (!(val & irq_status_bits))
+ continue;
+
+ if (irq_data->irq_clr_reg == ASYS_IRQ_CLR)
+ asys_irq_clr_bits |= irq_clr_bits;
+ else
+ afe_irq_clr_bits |= irq_clr_bits;
+
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ if (asys_irq_clr_bits)
+ regmap_write(afe->regmap, ASYS_IRQ_CLR, asys_irq_clr_bits);
+ if (afe_irq_clr_bits)
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, afe_irq_clr_bits);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8188_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ mt8188_afe_disable_main_clock(afe);
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8188_afe_disable_reg_rw_clk(afe);
+
+ return 0;
+}
+
+static int mt8188_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(MTK_SIP_AUDIO_CONTROL,
+ MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ mt8188_afe_enable_reg_rw_clk(afe);
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ mt8188_afe_enable_main_clock(afe);
+skip_regmap:
+ return 0;
+}
+
+static int init_memif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ int i;
+
+ for (i = MT8188_AFE_MEMIF_START; i < MT8188_AFE_MEMIF_END; i++) {
+ memif_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_memif_priv),
+ GFP_KERNEL);
+ if (!memif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[i] = memif_priv;
+ }
+
+ return 0;
+}
+
+static int mt8188_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8188_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8188_memif_dai_driver);
+
+ dai->dapm_widgets = mt8188_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8188_memif_widgets);
+ dai->dapm_routes = mt8188_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8188_memif_routes);
+ dai->controls = mt8188_memif_controls;
+ dai->num_controls = ARRAY_SIZE(mt8188_memif_controls);
+
+ return init_memif_priv_data(afe);
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8188_dai_adda_register,
+ mt8188_dai_dmic_register,
+ mt8188_dai_etdm_register,
+ mt8188_dai_pcm_register,
+ mt8188_dai_memif_register,
+};
+
+static const struct reg_sequence mt8188_afe_reg_defaults[] = {
+ { AFE_IRQ_MASK, 0x387ffff },
+ { AFE_IRQ3_CON, BIT(30) },
+ { AFE_IRQ9_CON, BIT(30) },
+ { ETDM_IN1_CON4, 0x12000100 },
+ { ETDM_IN2_CON4, 0x12000100 },
+};
+
+static const struct reg_sequence mt8188_cg_patch[] = {
+ { AUDIO_TOP_CON0, 0xfffffffb },
+ { AUDIO_TOP_CON1, 0xfffffff8 },
+};
+
+static int mt8188_afe_init_registers(struct mtk_base_afe *afe)
+{
+ return regmap_multi_reg_write(afe->regmap,
+ mt8188_afe_reg_defaults,
+ ARRAY_SIZE(mt8188_afe_reg_defaults));
+}
+
+static int mt8188_afe_parse_of(struct mtk_base_afe *afe,
+ struct device_node *np)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_MT6359)
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ afe_priv->topckgen = syscon_regmap_lookup_by_phandle(afe->dev->of_node,
+ "mediatek,topckgen");
+ if (IS_ERR(afe_priv->topckgen))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->topckgen),
+ "%s() Cannot find topckgen controller\n",
+ __func__);
+#endif
+ return 0;
+}
+
+#define MT8188_DELAY_US 10
+#define MT8188_TIMEOUT_US USEC_PER_SEC
+
+static int bus_protect_enable(struct regmap *regmap)
+{
+ int ret;
+ u32 val;
+ u32 mask;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_SET, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, (val & mask) == mask,
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_SET, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, (val & mask) == mask,
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ return ret;
+}
+
+static int bus_protect_disable(struct regmap *regmap)
+{
+ int ret;
+ u32 val;
+ u32 mask;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_CLR, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, !(val & mask),
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_CLR, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, !(val & mask),
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ return ret;
+}
+
+static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8188_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ struct regmap *infra_ao;
+ int i, irq_id, ret;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+ dev_dbg(dev, "failed to assign memory region: %d\n", ret);
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return dev_err_probe(dev, PTR_ERR(afe->base_addr),
+ "AFE base_addr not found\n");
+
+ infra_ao = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "mediatek,infracfg");
+ if (IS_ERR(infra_ao))
+ return dev_err_probe(dev, PTR_ERR(infra_ao),
+ "%s() Cannot find infra_ao controller\n",
+ __func__);
+
+ /* reset controller to reset audio regs before regmap cache */
+ rstc = devm_reset_control_get_exclusive(dev, "audiosys");
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "could not get audiosys reset\n");
+
+ ret = bus_protect_enable(infra_ao);
+ if (ret) {
+ dev_err(dev, "bus_protect_enable failed\n");
+ return ret;
+ }
+
+ ret = reset_control_reset(rstc);
+ if (ret) {
+ dev_err(dev, "failed to trigger audio reset:%d\n", ret);
+ return ret;
+ }
+
+ ret = bus_protect_disable(infra_ao);
+ if (ret) {
+ dev_err(dev, "bus_protect_disable failed\n");
+ return ret;
+ }
+
+ /* initial audio related clock */
+ ret = mt8188_afe_init_clock(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "init clock error");
+
+ spin_lock_init(&afe_priv->afe_ctrl_lock);
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ afe->irqs_size = MT8188_AFE_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* init memif */
+ afe->memif_size = MT8188_AFE_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = mt8188_afe_memif_const_irqs[i];
+ afe->memif[i].const_irq = 1;
+ afe->irqs[afe->memif[i].irq_usage].irq_occupyed = true;
+ }
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return dev_err_probe(dev, irq_id, "no irq found");
+
+ ret = devm_request_irq(dev, irq_id, mt8188_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "dai register i %d fail\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ afe->mtk_afe_hardware = &mt8188_afe_hardware;
+ afe->memif_fs = mt8188_memif_fs;
+ afe->irq_fs = mt8188_irq_fs;
+
+ afe->runtime_resume = mt8188_afe_runtime_resume;
+ afe->runtime_suspend = mt8188_afe_runtime_suspend;
+
+ platform_set_drvdata(pdev, afe);
+
+ ret = mt8188_afe_parse_of(afe, pdev->dev.of_node);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to resume device\n");
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mt8188_afe_regmap_config);
+ if (IS_ERR(afe->regmap)) {
+ ret = PTR_ERR(afe->regmap);
+ goto err_pm_put;
+ }
+
+ ret = regmap_register_patch(afe->regmap, mt8188_cg_patch,
+ ARRAY_SIZE(mt8188_cg_patch));
+ if (ret < 0) {
+ dev_info(dev, "Failed to apply cg patch\n");
+ goto err_pm_put;
+ }
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ afe->dai_drivers, afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_pm_put;
+ }
+
+ mt8188_afe_init_registers(afe);
+
+ pm_runtime_put_sync(&pdev->dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ return 0;
+err_pm_put:
+ pm_runtime_put_sync(dev);
+
+ return ret;
+}
+
+static const struct of_device_id mt8188_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8188-afe", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8188_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8188_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8188_afe_runtime_suspend,
+ mt8188_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8188_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8188-audio",
+ .of_match_table = mt8188_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8188_afe_pm_ops),
+ },
+ .probe = mt8188_afe_pcm_dev_probe,
+};
+
+module_platform_driver(mt8188_afe_pcm_driver);
+
+MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA 8188");
+MODULE_AUTHOR("Chun-Chia.Chiu <chun-chia.chiu@mediatek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
new file mode 100644
index 000000000000..40d2ab0a7677
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8188-audsys-clk.c -- MediaTek 8188 audsys clock control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "mt8188-afe-common.h"
+#include "mt8188-audsys-clk.h"
+#include "mt8188-audsys-clkid.h"
+#include "mt8188-reg.h"
+
+struct afe_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ int reg;
+ u8 bit;
+ const struct clk_ops *ops;
+ unsigned long flags;
+ u8 cg_flags;
+};
+
+#define GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, _flags, _cgflags) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .reg = _reg, \
+ .bit = _bit, \
+ .flags = _flags, \
+ .cg_flags = _cgflags, \
+ }
+
+#define GATE_AFE(_id, _name, _parent, _reg, _bit) \
+ GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, \
+ CLK_SET_RATE_PARENT, CLK_GATE_SET_TO_DISABLE)
+
+#define GATE_AUD0(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON0, _bit)
+
+#define GATE_AUD1(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON1, _bit)
+
+#define GATE_AUD3(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON3, _bit)
+
+#define GATE_AUD4(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON4, _bit)
+
+#define GATE_AUD5(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON5, _bit)
+
+#define GATE_AUD6(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON6, _bit)
+
+static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = {
+ /* AUD0 */
+ GATE_AUD0(CLK_AUD_AFE, "aud_afe", "top_a1sys_hp", 2),
+ GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "top_a1sys_hp", 4),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "top_apll4", 10),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "top_apll4", 11),
+ GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "top_a1sys_hp", 18),
+ GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "top_apll1", 19),
+ GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "top_apll2", 20),
+ GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "top_aud_iec_clk", 21),
+ GATE_AUD0(CLK_AUD_APLL, "aud_apll", "top_apll1", 23),
+ GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "top_apll2", 24),
+ GATE_AUD0(CLK_AUD_DAC, "aud_dac", "top_a1sys_hp", 25),
+ GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "top_a1sys_hp", 26),
+ GATE_AUD0(CLK_AUD_TML, "aud_tml", "top_a1sys_hp", 27),
+ GATE_AUD0(CLK_AUD_ADC, "aud_adc", "top_a1sys_hp", 28),
+ GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "top_audio_h", 31),
+
+ /* AUD1 */
+ GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "top_a1sys_hp", 2),
+ GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "top_a1sys_hp", 10),
+ GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "top_a1sys_hp", 11),
+ GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "top_a1sys_hp", 12),
+ GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "top_a1sys_hp", 13),
+ GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14),
+ GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16),
+ GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17),
+ GATE_AUD1(CLK_AUD_DMIC_HIRES1, "aud_dmic_hires1", "top_audio_h", 20),
+ GATE_AUD1(CLK_AUD_DMIC_HIRES2, "aud_dmic_hires2", "top_audio_h", 21),
+ GATE_AUD1(CLK_AUD_DMIC_HIRES3, "aud_dmic_hires3", "top_audio_h", 22),
+ GATE_AUD1(CLK_AUD_DMIC_HIRES4, "aud_dmic_hires4", "top_audio_h", 23),
+
+ /* AUD3 */
+ GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5),
+ GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "top_apll3", 7),
+
+ /* AUD4 */
+ GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "top_a1sys_hp", 0),
+ GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "top_a1sys_hp", 1),
+ GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "top_a1sys_hp", 6),
+ GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "top_a1sys_hp", 7),
+ GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "top_a1sys_hp", 8),
+ GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "top_a1sys_hp", 16),
+ GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "top_a1sys_hp", 17),
+ GATE_AUD4(CLK_AUD_MULTI_IN, "aud_multi_in", "mphone_slave_b", 19),
+ GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "top_intdir", 20),
+ GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "top_a1sys_hp", 21),
+ GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "top_a2sys", 22),
+ GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "top_a1sys_hp", 24),
+ GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "top_a3sys", 30),
+ GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "top_a4sys", 31),
+
+ /* AUD5 */
+ GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "top_a1sys_hp", 0),
+ GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "top_a1sys_hp", 1),
+ GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "top_a1sys_hp", 2),
+ GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "top_a1sys_hp", 3),
+ GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "top_a1sys_hp", 4),
+ GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "top_a1sys_hp", 5),
+ GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "top_a1sys_hp", 7),
+ GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "top_a1sys_hp", 8),
+ GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "top_a1sys_hp", 9),
+ GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "top_a1sys_hp", 18),
+ GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "top_a1sys_hp", 19),
+ GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "top_a1sys_hp", 22),
+ GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "top_a1sys_hp", 23),
+ GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "top_a1sys_hp", 24),
+ GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "top_a1sys_hp", 26),
+ GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "top_a1sys_hp", 27),
+
+ /* AUD6 */
+ GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "top_asm_h", 0),
+ GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "top_asm_h", 1),
+ GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "top_asm_h", 2),
+ GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "top_asm_h", 3),
+ GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "top_asm_h", 4),
+ GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "top_asm_h", 5),
+ GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "top_asm_h", 6),
+ GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "top_asm_h", 7),
+ GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "top_asm_h", 8),
+ GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "top_asm_h", 9),
+ GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "top_asm_h", 10),
+ GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "top_asm_h", 11),
+};
+
+static void mt8188_audsys_clk_unregister(void *data)
+{
+ struct mtk_base_afe *afe = data;
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ if (!afe_priv)
+ return;
+
+ for (i = 0; i < CLK_AUD_NR_CLK; i++) {
+ cl = afe_priv->lookup[i];
+ if (!cl)
+ continue;
+
+ clk = cl->clk;
+ clk_unregister_gate(clk);
+
+ clkdev_drop(cl);
+ }
+}
+
+int mt8188_audsys_clk_register(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AUD_NR_CLK,
+ sizeof(*afe_priv->lookup),
+ GFP_KERNEL);
+
+ if (!afe_priv->lookup)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ const struct afe_gate *gate = &aud_clks[i];
+
+ clk = clk_register_gate(afe->dev, gate->name, gate->parent_name,
+ gate->flags, afe->base_addr + gate->reg,
+ gate->bit, gate->cg_flags, NULL);
+
+ if (IS_ERR(clk)) {
+ dev_err(afe->dev, "Failed to register clk %s: %ld\n",
+ gate->name, PTR_ERR(clk));
+ continue;
+ }
+
+ /* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return -ENOMEM;
+
+ cl->clk = clk;
+ cl->con_id = gate->name;
+ cl->dev_id = dev_name(afe->dev);
+ cl->clk_hw = NULL;
+ clkdev_add(cl);
+
+ afe_priv->lookup[i] = cl;
+ }
+
+ return devm_add_action_or_reset(afe->dev, mt8188_audsys_clk_unregister, afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h
new file mode 100644
index 000000000000..45b0948c4a06
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-audsys-clk.h -- MediaTek 8188 audsys clock definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_AUDSYS_CLK_H_
+#define _MT8188_AUDSYS_CLK_H_
+
+int mt8188_audsys_clk_register(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h
new file mode 100644
index 000000000000..9cb732863c10
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-audsys-clkid.h -- MediaTek 8188 audsys clock id definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_AUDSYS_CLKID_H_
+#define _MT8188_AUDSYS_CLKID_H_
+
+enum{
+ CLK_AUD_AFE,
+ CLK_AUD_LRCK_CNT,
+ CLK_AUD_SPDIFIN_TUNER_APLL,
+ CLK_AUD_SPDIFIN_TUNER_DBG,
+ CLK_AUD_UL_TML,
+ CLK_AUD_APLL1_TUNER,
+ CLK_AUD_APLL2_TUNER,
+ CLK_AUD_TOP0_SPDF,
+ CLK_AUD_APLL,
+ CLK_AUD_APLL2,
+ CLK_AUD_DAC,
+ CLK_AUD_DAC_PREDIS,
+ CLK_AUD_TML,
+ CLK_AUD_ADC,
+ CLK_AUD_DAC_HIRES,
+ CLK_AUD_A1SYS_HP,
+ CLK_AUD_AFE_DMIC1,
+ CLK_AUD_AFE_DMIC2,
+ CLK_AUD_AFE_DMIC3,
+ CLK_AUD_AFE_DMIC4,
+ CLK_AUD_AFE_26M_DMIC_TM,
+ CLK_AUD_UL_TML_HIRES,
+ CLK_AUD_ADC_HIRES,
+ CLK_AUD_DMIC_HIRES1,
+ CLK_AUD_DMIC_HIRES2,
+ CLK_AUD_DMIC_HIRES3,
+ CLK_AUD_DMIC_HIRES4,
+ CLK_AUD_LINEIN_TUNER,
+ CLK_AUD_EARC_TUNER,
+ CLK_AUD_I2SIN,
+ CLK_AUD_TDM_IN,
+ CLK_AUD_I2S_OUT,
+ CLK_AUD_TDM_OUT,
+ CLK_AUD_HDMI_OUT,
+ CLK_AUD_ASRC11,
+ CLK_AUD_ASRC12,
+ CLK_AUD_MULTI_IN,
+ CLK_AUD_INTDIR,
+ CLK_AUD_A1SYS,
+ CLK_AUD_A2SYS,
+ CLK_AUD_PCMIF,
+ CLK_AUD_A3SYS,
+ CLK_AUD_A4SYS,
+ CLK_AUD_MEMIF_UL1,
+ CLK_AUD_MEMIF_UL2,
+ CLK_AUD_MEMIF_UL3,
+ CLK_AUD_MEMIF_UL4,
+ CLK_AUD_MEMIF_UL5,
+ CLK_AUD_MEMIF_UL6,
+ CLK_AUD_MEMIF_UL8,
+ CLK_AUD_MEMIF_UL9,
+ CLK_AUD_MEMIF_UL10,
+ CLK_AUD_MEMIF_DL2,
+ CLK_AUD_MEMIF_DL3,
+ CLK_AUD_MEMIF_DL6,
+ CLK_AUD_MEMIF_DL7,
+ CLK_AUD_MEMIF_DL8,
+ CLK_AUD_MEMIF_DL10,
+ CLK_AUD_MEMIF_DL11,
+ CLK_AUD_GASRC0,
+ CLK_AUD_GASRC1,
+ CLK_AUD_GASRC2,
+ CLK_AUD_GASRC3,
+ CLK_AUD_GASRC4,
+ CLK_AUD_GASRC5,
+ CLK_AUD_GASRC6,
+ CLK_AUD_GASRC7,
+ CLK_AUD_GASRC8,
+ CLK_AUD_GASRC9,
+ CLK_AUD_GASRC10,
+ CLK_AUD_GASRC11,
+ CLK_AUD_NR_CLK,
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
new file mode 100644
index 000000000000..ac547fc864a6
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI ADDA Control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+#include "../common/mtk-dai-adda-common.h"
+
+#define ADDA_HIRES_THRES 48000
+
+enum {
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA_UL_ON,
+ SUPPLY_SEQ_ADDA_AFE_ON,
+};
+
+struct mtk_dai_adda_priv {
+ bool hires_required;
+};
+
+static int mt8188_adda_mtkaif_init(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int delay_data;
+ int delay_cycle;
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ /* set rx protocol 2 & mtkaif_rxif_clkinv_adc inverse */
+ regmap_set_bits(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ MTKAIF_RXIF_CLKINV_ADC | MTKAIF_RXIF_PROTOCOL2);
+
+ regmap_set_bits(afe->regmap, AFE_AUD_PAD_TOP, RG_RX_PROTOCOL2);
+
+ if (!param->mtkaif_calibration_ok) {
+ dev_info(afe->dev, "%s(), calibration fail\n", __func__);
+ return 0;
+ }
+
+ /* set delay for ch1, ch2 */
+ if (param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] >=
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] -
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1];
+ } else {
+ delay_data = DELAY_DATA_MISO0;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] -
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0];
+ }
+
+ mask = (MTKAIF_RXIF_DELAY_DATA | MTKAIF_RXIF_DELAY_CYCLE_MASK);
+ val |= FIELD_PREP(MTKAIF_RXIF_DELAY_CYCLE_MASK, delay_cycle);
+ val |= FIELD_PREP(MTKAIF_RXIF_DELAY_DATA, delay_data);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG2, mask, val);
+
+ return 0;
+}
+
+static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8188_adda_mtkaif_init(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void mtk_adda_ul_mictype(struct mtk_base_afe *afe, bool dmic)
+{
+ unsigned int reg = AFE_ADDA_UL_SRC_CON0;
+ unsigned int val;
+
+ val = (UL_SDM3_LEVEL_CTL | UL_MODE_3P25M_CH1_CTL |
+ UL_MODE_3P25M_CH2_CTL);
+
+ /* turn on dmic, ch1, ch2 */
+ if (dmic)
+ regmap_set_bits(afe->regmap, reg, val);
+ else
+ regmap_clear_bits(afe->regmap, reg, val);
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_adda_ul_mictype(afe, param->mtkaif_dmic_on);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct mtk_dai_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ if (strstr(name, "aud_adc_hires"))
+ return afe_priv->dai_priv[MT8188_AFE_IO_UL_SRC];
+ else if (strstr(name, "aud_dac_hires"))
+ return afe_priv->dai_priv[MT8188_AFE_IO_DL_SRC];
+ else
+ return NULL;
+}
+
+static int mtk_afe_adda_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_dai_adda_priv *adda_priv;
+
+ adda_priv = get_adda_priv_by_name(afe, w->name);
+
+ if (!adda_priv) {
+ dev_dbg(afe->dev, "adda_priv == NULL");
+ return 0;
+ }
+
+ return (adda_priv->hires_required) ? 1 : 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN176, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN176, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN176, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN176, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN176_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_adda_o177_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN177, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN177, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN177, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN177, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN177_2, 7, 1, 0),
+};
+
+static const char * const adda_dlgain_mux_map[] = {
+ "Bypass", "Connect",
+};
+
+static SOC_ENUM_SINGLE_DECL(adda_dlgain_mux_map_enum,
+ SND_SOC_NOPM, 0,
+ adda_dlgain_mux_map);
+
+static const struct snd_kcontrol_new adda_dlgain_mux_control =
+ SOC_DAPM_ENUM("DL_GAIN_MUX", adda_dlgain_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ SND_SOC_DAPM_MIXER("I168", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I169", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O176", SND_SOC_NOPM, 0, 0,
+ mtk_dai_adda_o176_mix,
+ ARRAY_SIZE(mtk_dai_adda_o176_mix)),
+ SND_SOC_DAPM_MIXER("O177", SND_SOC_NOPM, 0, 0,
+ mtk_dai_adda_o177_mix,
+ ARRAY_SIZE(mtk_dai_adda_o177_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0,
+ ADDA_AFE_ON_SHIFT, 0,
+ NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT, 0,
+ mtk_adda_dl_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SHIFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SND_SOC_NOPM,
+ 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MUX("DL_GAIN_MUX", SND_SOC_NOPM, 0, 0,
+ &adda_dlgain_mux_control),
+
+ SND_SOC_DAPM_PGA("DL_GAIN", AFE_ADDA_DL_SRC2_CON0,
+ DL_2_GAIN_ON_CTL_PRE_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("ADDA_INPUT"),
+ SND_SOC_DAPM_OUTPUT("ADDA_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_hires"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_hires"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+ {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
+ {"ADDA Capture", NULL, "aud_adc"},
+ {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adda_hires_connect},
+
+ {"I168", NULL, "ADDA Capture"},
+ {"I169", NULL, "ADDA Capture"},
+
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+ {"ADDA Playback", NULL, "aud_dac"},
+ {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_adda_hires_connect},
+
+ {"DL_GAIN", NULL, "O176"},
+ {"DL_GAIN", NULL, "O177"},
+
+ {"DL_GAIN_MUX", "Bypass", "O176"},
+ {"DL_GAIN_MUX", "Bypass", "O177"},
+ {"DL_GAIN_MUX", "Connect", "DL_GAIN"},
+
+ {"ADDA Playback", NULL, "DL_GAIN_MUX"},
+
+ {"O176", "I000 Switch", "I000"},
+ {"O177", "I001 Switch", "I001"},
+
+ {"O176", "I002 Switch", "I002"},
+ {"O177", "I003 Switch", "I003"},
+
+ {"O176", "I020 Switch", "I020"},
+ {"O177", "I021 Switch", "I021"},
+
+ {"O176", "I022 Switch", "I022"},
+ {"O177", "I023 Switch", "I023"},
+
+ {"O176", "I070 Switch", "I070"},
+ {"O177", "I071 Switch", "I071"},
+
+ {"ADDA Capture", NULL, "ADDA_INPUT"},
+ {"ADDA_OUTPUT", NULL, "ADDA Playback"},
+};
+
+static int mt8188_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ ucontrol->value.integer.value[0] = param->mtkaif_dmic_on;
+ return 0;
+}
+
+static int mt8188_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int dmic_on;
+
+ dmic_on = !!ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, dmic_on %d\n",
+ __func__, kcontrol->id.name, dmic_on);
+
+ if (param->mtkaif_dmic_on == dmic_on)
+ return 0;
+
+ param->mtkaif_dmic_on = dmic_on;
+ return 1;
+}
+
+static const struct snd_kcontrol_new mtk_dai_adda_controls[] = {
+ SOC_SINGLE("ADDA_DL_GAIN", AFE_ADDA_DL_SRC2_CON1,
+ DL_2_GAIN_CTL_PRE_SHIFT, 65535, 0),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC Switch", 0,
+ mt8188_adda_dmic_get, mt8188_adda_dmic_set),
+};
+
+static int mtk_dai_da_configure(struct mtk_base_afe *afe,
+ unsigned int rate, int id)
+{
+ unsigned int val = 0;
+ unsigned int mask = 0;
+
+ /* set sampling rate */
+ mask |= DL_2_INPUT_MODE_CTL_MASK;
+ val |= FIELD_PREP(DL_2_INPUT_MODE_CTL_MASK,
+ mtk_adda_dl_rate_transform(afe, rate));
+
+ /* turn off saturation */
+ mask |= DL_2_CH1_SATURATION_EN_CTL;
+ mask |= DL_2_CH2_SATURATION_EN_CTL;
+
+ /* turn off mute function */
+ mask |= DL_2_MUTE_CH1_OFF_CTL_PRE;
+ mask |= DL_2_MUTE_CH2_OFF_CTL_PRE;
+ val |= DL_2_MUTE_CH1_OFF_CTL_PRE;
+ val |= DL_2_MUTE_CH2_OFF_CTL_PRE;
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ mask |= DL_2_VOICE_MODE_CTL_PRE;
+ if (rate == 8000 || rate == 16000)
+ val |= DL_2_VOICE_MODE_CTL_PRE;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, mask, val);
+
+ /* new 2nd sdm */
+ regmap_set_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON,
+ DL_USE_NEW_2ND_SDM);
+
+ return 0;
+}
+
+static int mtk_dai_ad_configure(struct mtk_base_afe *afe,
+ unsigned int rate, int id)
+{
+ unsigned int val;
+ unsigned int mask;
+
+ mask = UL_VOICE_MODE_CTL_MASK;
+ val = FIELD_PREP(UL_VOICE_MODE_CTL_MASK,
+ mtk_adda_ul_rate_transform(afe, rate));
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ mask, val);
+ return 0;
+}
+
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv = afe_priv->dai_priv[dai->id];
+ unsigned int rate = params_rate(params);
+ int id = dai->id;
+ int ret = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n",
+ __func__, id, substream->stream, rate);
+
+ adda_priv->hires_required = (rate > ADDA_HIRES_THRES);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = mtk_dai_da_configure(afe, rate, id);
+ else
+ ret = mtk_dai_ad_configure(afe, rate, id);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "DL_SRC",
+ .id = MT8188_AFE_IO_DL_SRC,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "UL_SRC",
+ .id = MT8188_AFE_IO_UL_SRC,
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+static int init_adda_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv;
+ int adda_dai_list[] = {MT8188_AFE_IO_DL_SRC, MT8188_AFE_IO_UL_SRC};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
+ adda_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_adda_priv),
+ GFP_KERNEL);
+ if (!adda_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
+ }
+
+ return 0;
+}
+
+int mt8188_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+ dai->controls = mtk_dai_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_adda_controls);
+
+ return init_adda_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c
new file mode 100644
index 000000000000..a9515d7fb70a
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI DMIC I/F Control
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Parker Yang <parker.yang@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+
+/* DMIC HW Gain configuration maximum value. */
+#define DMIC_GAIN_MAX_STEP GENMASK(19, 0)
+#define DMIC_GAIN_MAX_PER_STEP GENMASK(7, 0)
+#define DMIC_GAIN_MAX_TARGET GENMASK(27, 0)
+#define DMIC_GAIN_MAX_CURRENT GENMASK(27, 0)
+
+#define CLK_PHASE_SEL_CH1 0
+#define CLK_PHASE_SEL_CH2 ((CLK_PHASE_SEL_CH1) + 4)
+
+#define DMIC1_SRC_SEL 0
+#define DMIC2_SRC_SEL 0
+#define DMIC3_SRC_SEL 2
+#define DMIC4_SRC_SEL 0
+#define DMIC5_SRC_SEL 4
+#define DMIC6_SRC_SEL 0
+#define DMIC7_SRC_SEL 6
+#define DMIC8_SRC_SEL 0
+
+enum {
+ SUPPLY_SEQ_DMIC_GAIN,
+ SUPPLY_SEQ_DMIC_CK,
+};
+
+enum {
+ DMIC0,
+ DMIC1,
+ DMIC2,
+ DMIC3,
+ DMIC_NUM,
+};
+
+struct mtk_dai_dmic_ctrl_reg {
+ unsigned int con0;
+};
+
+struct mtk_dai_dmic_hw_gain_ctrl_reg {
+ unsigned int bypass;
+ unsigned int con0;
+};
+
+struct mtk_dai_dmic_priv {
+ unsigned int gain_on[DMIC_NUM];
+ unsigned int channels;
+ bool hires_required;
+};
+
+static const struct mtk_dai_dmic_ctrl_reg dmic_ctrl_regs[DMIC_NUM] = {
+ [DMIC0] = {
+ .con0 = AFE_DMIC0_UL_SRC_CON0,
+ },
+ [DMIC1] = {
+ .con0 = AFE_DMIC1_UL_SRC_CON0,
+ },
+ [DMIC2] = {
+ .con0 = AFE_DMIC2_UL_SRC_CON0,
+ },
+ [DMIC3] = {
+ .con0 = AFE_DMIC3_UL_SRC_CON0,
+ },
+};
+
+static const struct mtk_dai_dmic_ctrl_reg *get_dmic_ctrl_reg(int id)
+{
+ if (id < 0 || id >= DMIC_NUM)
+ return NULL;
+
+ return &dmic_ctrl_regs[id];
+}
+
+static const struct mtk_dai_dmic_hw_gain_ctrl_reg
+ dmic_hw_gain_ctrl_regs[DMIC_NUM] = {
+ [DMIC0] = {
+ .bypass = DMIC_BYPASS_HW_GAIN,
+ .con0 = DMIC_GAIN1_CON0,
+ },
+ [DMIC1] = {
+ .bypass = DMIC_BYPASS_HW_GAIN,
+ .con0 = DMIC_GAIN2_CON0,
+ },
+ [DMIC2] = {
+ .bypass = DMIC_BYPASS_HW_GAIN,
+ .con0 = DMIC_GAIN3_CON0,
+ },
+ [DMIC3] = {
+ .bypass = DMIC_BYPASS_HW_GAIN,
+ .con0 = DMIC_GAIN4_CON0,
+ },
+};
+
+static const struct mtk_dai_dmic_hw_gain_ctrl_reg
+ *get_dmic_hw_gain_ctrl_reg(struct mtk_base_afe *afe, int id)
+{
+ if ((id < 0) || (id >= DMIC_NUM)) {
+ dev_dbg(afe->dev, "%s invalid id\n", __func__);
+ return NULL;
+ }
+
+ return &dmic_hw_gain_ctrl_regs[id];
+}
+
+static void mtk_dai_dmic_hw_gain_bypass(struct mtk_base_afe *afe,
+ unsigned int id, bool bypass)
+{
+ const struct mtk_dai_dmic_hw_gain_ctrl_reg *reg;
+ unsigned int msk;
+
+ reg = get_dmic_hw_gain_ctrl_reg(afe, id);
+ if (!reg)
+ return;
+
+ switch (id) {
+ case DMIC0:
+ msk = DMIC_BYPASS_HW_GAIN_DMIC1_BYPASS;
+ break;
+ case DMIC1:
+ msk = DMIC_BYPASS_HW_GAIN_DMIC2_BYPASS;
+ break;
+ case DMIC2:
+ msk = DMIC_BYPASS_HW_GAIN_DMIC3_BYPASS;
+ break;
+ case DMIC3:
+ msk = DMIC_BYPASS_HW_GAIN_DMIC4_BYPASS;
+ break;
+ default:
+ return;
+ }
+
+ if (bypass)
+ regmap_set_bits(afe->regmap, reg->bypass, msk);
+ else
+ regmap_clear_bits(afe->regmap, reg->bypass, msk);
+}
+
+static void mtk_dai_dmic_hw_gain_on(struct mtk_base_afe *afe, unsigned int id,
+ bool on)
+{
+ const struct mtk_dai_dmic_hw_gain_ctrl_reg *reg = get_dmic_hw_gain_ctrl_reg(afe, id);
+
+ if (!reg)
+ return;
+
+ if (on)
+ regmap_set_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON);
+ else
+ regmap_clear_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON);
+}
+
+static const struct reg_sequence mtk_dai_dmic_iir_coeff_reg_defaults[] = {
+ { AFE_DMIC0_IIR_COEF_02_01, 0x00000000 },
+ { AFE_DMIC0_IIR_COEF_04_03, 0x00003FB8 },
+ { AFE_DMIC0_IIR_COEF_06_05, 0x3FB80000 },
+ { AFE_DMIC0_IIR_COEF_08_07, 0x3FB80000 },
+ { AFE_DMIC0_IIR_COEF_10_09, 0x0000C048 },
+ { AFE_DMIC1_IIR_COEF_02_01, 0x00000000 },
+ { AFE_DMIC1_IIR_COEF_04_03, 0x00003FB8 },
+ { AFE_DMIC1_IIR_COEF_06_05, 0x3FB80000 },
+ { AFE_DMIC1_IIR_COEF_08_07, 0x3FB80000 },
+ { AFE_DMIC1_IIR_COEF_10_09, 0x0000C048 },
+ { AFE_DMIC2_IIR_COEF_02_01, 0x00000000 },
+ { AFE_DMIC2_IIR_COEF_04_03, 0x00003FB8 },
+ { AFE_DMIC2_IIR_COEF_06_05, 0x3FB80000 },
+ { AFE_DMIC2_IIR_COEF_08_07, 0x3FB80000 },
+ { AFE_DMIC2_IIR_COEF_10_09, 0x0000C048 },
+ { AFE_DMIC3_IIR_COEF_02_01, 0x00000000 },
+ { AFE_DMIC3_IIR_COEF_04_03, 0x00003FB8 },
+ { AFE_DMIC3_IIR_COEF_06_05, 0x3FB80000 },
+ { AFE_DMIC3_IIR_COEF_08_07, 0x3FB80000 },
+ { AFE_DMIC3_IIR_COEF_10_09, 0x0000C048 },
+};
+
+static int mtk_dai_dmic_load_iir_coeff_table(struct mtk_base_afe *afe)
+{
+ return regmap_multi_reg_write(afe->regmap,
+ mtk_dai_dmic_iir_coeff_reg_defaults,
+ ARRAY_SIZE(mtk_dai_dmic_iir_coeff_reg_defaults));
+}
+
+static int mtk_dai_dmic_configure_array(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ const u32 mask = PWR2_TOP_CON_DMIC8_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC7_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC6_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC5_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC4_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC3_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC2_SRC_SEL_MASK |
+ PWR2_TOP_CON_DMIC1_SRC_SEL_MASK;
+ const u32 val = PWR2_TOP_CON_DMIC8_SRC_SEL_VAL(DMIC8_SRC_SEL) |
+ PWR2_TOP_CON_DMIC7_SRC_SEL_VAL(DMIC7_SRC_SEL) |
+ PWR2_TOP_CON_DMIC6_SRC_SEL_VAL(DMIC6_SRC_SEL) |
+ PWR2_TOP_CON_DMIC5_SRC_SEL_VAL(DMIC5_SRC_SEL) |
+ PWR2_TOP_CON_DMIC4_SRC_SEL_VAL(DMIC4_SRC_SEL) |
+ PWR2_TOP_CON_DMIC3_SRC_SEL_VAL(DMIC3_SRC_SEL) |
+ PWR2_TOP_CON_DMIC2_SRC_SEL_VAL(DMIC2_SRC_SEL) |
+ PWR2_TOP_CON_DMIC1_SRC_SEL_VAL(DMIC1_SRC_SEL);
+
+ return regmap_update_bits(afe->regmap, PWR2_TOP_CON0, mask, val);
+}
+
+/* This function assumes that the caller checked that channels is valid */
+static u8 mtk_dmic_channels_to_dmic_number(unsigned int channels)
+{
+ switch (channels) {
+ case 1:
+ return DMIC0;
+ case 2:
+ return DMIC1;
+ case 3:
+ return DMIC2;
+ case 4:
+ default:
+ return DMIC3;
+ }
+}
+
+static void mtk_dai_dmic_hw_gain_enable(struct mtk_base_afe *afe,
+ unsigned int channels, bool enable)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN];
+ u8 dmic_num;
+ int i;
+
+ dmic_num = mtk_dmic_channels_to_dmic_number(channels);
+ for (i = dmic_num; i >= DMIC0; i--) {
+ if (enable && dmic_priv->gain_on[i]) {
+ mtk_dai_dmic_hw_gain_bypass(afe, i, false);
+ mtk_dai_dmic_hw_gain_on(afe, i, true);
+ } else {
+ mtk_dai_dmic_hw_gain_on(afe, i, false);
+ mtk_dai_dmic_hw_gain_bypass(afe, i, true);
+ }
+ }
+}
+
+static int mtk_dmic_gain_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN];
+ unsigned int channels = dmic_priv->channels;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ if (!channels)
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_dai_dmic_hw_gain_enable(afe, channels, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mtk_dai_dmic_hw_gain_enable(afe, channels, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN];
+ const struct mtk_dai_dmic_ctrl_reg *reg = NULL;
+ unsigned int channels = dmic_priv->channels;
+ unsigned int msk;
+ u8 dmic_num;
+ int i;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ if (!channels)
+ return -EINVAL;
+
+ dmic_num = mtk_dmic_channels_to_dmic_number(channels);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* request fifo soft rst */
+ msk = 0;
+ for (i = dmic_num; i >= DMIC0; i--)
+ msk |= PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(i);
+
+ regmap_set_bits(afe->regmap, PWR2_TOP_CON1, msk);
+
+ msk = AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL;
+
+ for (i = dmic_num; i >= DMIC0; i--) {
+ reg = get_dmic_ctrl_reg(i);
+ if (reg)
+ regmap_set_bits(afe->regmap, reg->con0, msk);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msk = AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL;
+
+ for (i = dmic_num; i >= DMIC0; i--) {
+ reg = get_dmic_ctrl_reg(i);
+ if (reg)
+ regmap_set_bits(afe->regmap, reg->con0, msk);
+ }
+
+ if (dmic_priv->hires_required) {
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES1]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES2]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES3]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES4]);
+ }
+
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC1]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC2]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC3]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC4]);
+
+ /* release fifo soft rst */
+ msk = 0;
+ for (i = dmic_num; i >= DMIC0; i--)
+ msk |= PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(i);
+
+ regmap_clear_bits(afe->regmap, PWR2_TOP_CON1, msk);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ msk = AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL;
+
+ for (i = dmic_num; i >= DMIC0; i--) {
+ reg = get_dmic_ctrl_reg(i);
+ if (reg)
+ regmap_set_bits(afe->regmap, reg->con0, msk);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 126);
+
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC1]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC2]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC3]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC4]);
+
+ if (dmic_priv->hires_required) {
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES1]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES2]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES3]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES4]);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_dmic_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN];
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ const struct mtk_dai_dmic_ctrl_reg *reg = NULL;
+ u32 val = AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH1(CLK_PHASE_SEL_CH1) |
+ AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH2(CLK_PHASE_SEL_CH2) |
+ AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL(0);
+ const u32 msk = AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL |
+ AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_MASK |
+ AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK |
+ AFE_DMIC_UL_VOICE_MODE_MASK;
+ u8 dmic_num;
+ int ret;
+ int i;
+
+ if (!channels || channels > 8)
+ return -EINVAL;
+
+ ret = mtk_dai_dmic_configure_array(dai);
+ if (ret < 0)
+ return ret;
+
+ ret = mtk_dai_dmic_load_iir_coeff_table(afe);
+ if (ret < 0)
+ return ret;
+
+ switch (rate) {
+ case 96000:
+ val |= AFE_DMIC_UL_CON0_VOCIE_MODE_96K;
+ dmic_priv->hires_required = 1;
+ break;
+ case 48000:
+ val |= AFE_DMIC_UL_CON0_VOCIE_MODE_48K;
+ dmic_priv->hires_required = 0;
+ break;
+ case 32000:
+ val |= AFE_DMIC_UL_CON0_VOCIE_MODE_32K;
+ dmic_priv->hires_required = 0;
+ break;
+ case 16000:
+ val |= AFE_DMIC_UL_CON0_VOCIE_MODE_16K;
+ dmic_priv->hires_required = 0;
+ break;
+ case 8000:
+ val |= AFE_DMIC_UL_CON0_VOCIE_MODE_8K;
+ dmic_priv->hires_required = 0;
+ break;
+ default:
+ dev_dbg(afe->dev, "%s invalid rate %u, use 48000Hz\n", __func__, rate);
+ val |= AFE_DMIC_UL_CON0_VOCIE_MODE_48K;
+ dmic_priv->hires_required = 0;
+ break;
+ }
+
+ dmic_num = mtk_dmic_channels_to_dmic_number(channels);
+ for (i = dmic_num; i >= DMIC0; i--) {
+ reg = get_dmic_ctrl_reg(i);
+ if (reg) {
+ ret = regmap_update_bits(afe->regmap, reg->con0, msk, val);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ dmic_priv->channels = channels;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_dmic_ops = {
+ .hw_params = mtk_dai_dmic_hw_params,
+};
+
+#define MTK_DMIC_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000)
+
+#define MTK_DMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_dmic_driver[] = {
+ {
+ .name = "DMIC",
+ .id = MT8188_AFE_IO_DMIC_IN,
+ .capture = {
+ .stream_name = "DMIC Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_DMIC_RATES,
+ .formats = MTK_DMIC_FORMATS,
+ },
+ .ops = &mtk_dai_dmic_ops,
+ },
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_dmic_widgets[] = {
+ SND_SOC_DAPM_MIXER("I004", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I005", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I006", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I007", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I008", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I009", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I010", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I011", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("DMIC_GAIN_ON", SUPPLY_SEQ_DMIC_GAIN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_dmic_gain_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("DMIC_CK_ON", SUPPLY_SEQ_DMIC_CK,
+ PWR2_TOP_CON1,
+ PWR2_TOP_CON1_DMIC_CKDIV_ON_SHIFT, 0,
+ mtk_dmic_event,
+ SND_SOC_DAPM_PRE_POST_PMU |
+ SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_INPUT("DMIC_INPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_dmic_routes[] = {
+ {"I004", NULL, "DMIC Capture"},
+ {"I005", NULL, "DMIC Capture"},
+ {"I006", NULL, "DMIC Capture"},
+ {"I007", NULL, "DMIC Capture"},
+ {"I008", NULL, "DMIC Capture"},
+ {"I009", NULL, "DMIC Capture"},
+ {"I010", NULL, "DMIC Capture"},
+ {"I011", NULL, "DMIC Capture"},
+ {"DMIC Capture", NULL, "DMIC_CK_ON"},
+ {"DMIC Capture", NULL, "DMIC_GAIN_ON"},
+ {"DMIC Capture", NULL, "DMIC_INPUT"},
+};
+
+static const char * const mt8188_dmic_gain_enable_text[] = {
+ "Bypass", "Connect",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(dmic_gain_on_enum,
+ mt8188_dmic_gain_enable_text);
+
+static int mtk_dai_dmic_hw_gain_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN];
+ unsigned int source = ucontrol->value.enumerated.item[0];
+ unsigned int *cached;
+
+ if (source >= e->items)
+ return -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "DMIC1_HW_GAIN_EN"))
+ cached = &dmic_priv->gain_on[0];
+ else if (!strcmp(kcontrol->id.name, "DMIC2_HW_GAIN_EN"))
+ cached = &dmic_priv->gain_on[1];
+ else if (!strcmp(kcontrol->id.name, "DMIC3_HW_GAIN_EN"))
+ cached = &dmic_priv->gain_on[2];
+ else if (!strcmp(kcontrol->id.name, "DMIC4_HW_GAIN_EN"))
+ cached = &dmic_priv->gain_on[3];
+ else
+ return -EINVAL;
+
+ if (source == *cached)
+ return 0;
+
+ *cached = source;
+ return 1;
+}
+
+static int mtk_dai_dmic_hw_gain_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN];
+ unsigned int val;
+
+ if (!strcmp(kcontrol->id.name, "DMIC1_HW_GAIN_EN"))
+ val = dmic_priv->gain_on[0];
+ else if (!strcmp(kcontrol->id.name, "DMIC2_HW_GAIN_EN"))
+ val = dmic_priv->gain_on[1];
+ else if (!strcmp(kcontrol->id.name, "DMIC3_HW_GAIN_EN"))
+ val = dmic_priv->gain_on[2];
+ else if (!strcmp(kcontrol->id.name, "DMIC4_HW_GAIN_EN"))
+ val = dmic_priv->gain_on[3];
+ else
+ return -EINVAL;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_dmic_controls[] = {
+ SOC_ENUM_EXT("DMIC1_HW_GAIN_EN", dmic_gain_on_enum,
+ mtk_dai_dmic_hw_gain_ctrl_get,
+ mtk_dai_dmic_hw_gain_ctrl_put),
+ SOC_ENUM_EXT("DMIC2_HW_GAIN_EN", dmic_gain_on_enum,
+ mtk_dai_dmic_hw_gain_ctrl_get,
+ mtk_dai_dmic_hw_gain_ctrl_put),
+ SOC_ENUM_EXT("DMIC3_HW_GAIN_EN", dmic_gain_on_enum,
+ mtk_dai_dmic_hw_gain_ctrl_get,
+ mtk_dai_dmic_hw_gain_ctrl_put),
+ SOC_ENUM_EXT("DMIC4_HW_GAIN_EN", dmic_gain_on_enum,
+ mtk_dai_dmic_hw_gain_ctrl_get,
+ mtk_dai_dmic_hw_gain_ctrl_put),
+ SOC_SINGLE("DMIC1_HW_GAIN_TARGET", DMIC_GAIN1_CON1,
+ 0, DMIC_GAIN_MAX_TARGET, 0),
+ SOC_SINGLE("DMIC2_HW_GAIN_TARGET", DMIC_GAIN2_CON1,
+ 0, DMIC_GAIN_MAX_TARGET, 0),
+ SOC_SINGLE("DMIC3_HW_GAIN_TARGET", DMIC_GAIN3_CON1,
+ 0, DMIC_GAIN_MAX_TARGET, 0),
+ SOC_SINGLE("DMIC4_HW_GAIN_TARGET", DMIC_GAIN4_CON1,
+ 0, DMIC_GAIN_MAX_TARGET, 0),
+ SOC_SINGLE("DMIC1_HW_GAIN_CURRENT", DMIC_GAIN1_CUR,
+ 0, DMIC_GAIN_MAX_CURRENT, 0),
+ SOC_SINGLE("DMIC2_HW_GAIN_CURRENT", DMIC_GAIN2_CUR,
+ 0, DMIC_GAIN_MAX_CURRENT, 0),
+ SOC_SINGLE("DMIC3_HW_GAIN_CURRENT", DMIC_GAIN3_CUR,
+ 0, DMIC_GAIN_MAX_CURRENT, 0),
+ SOC_SINGLE("DMIC4_HW_GAIN_CURRENT", DMIC_GAIN4_CUR,
+ 0, DMIC_GAIN_MAX_CURRENT, 0),
+ SOC_SINGLE("DMIC1_HW_GAIN_UP_STEP", DMIC_GAIN1_CON3,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC2_HW_GAIN_UP_STEP", DMIC_GAIN2_CON3,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC3_HW_GAIN_UP_STEP", DMIC_GAIN3_CON3,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC4_HW_GAIN_UP_STEP", DMIC_GAIN4_CON3,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC1_HW_GAIN_DOWN_STEP", DMIC_GAIN1_CON2,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC2_HW_GAIN_DOWN_STEP", DMIC_GAIN2_CON2,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC3_HW_GAIN_DOWN_STEP", DMIC_GAIN3_CON2,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC4_HW_GAIN_DOWN_STEP", DMIC_GAIN4_CON2,
+ 0, DMIC_GAIN_MAX_STEP, 0),
+ SOC_SINGLE("DMIC1_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN1_CON0,
+ DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0),
+ SOC_SINGLE("DMIC2_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN2_CON0,
+ DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0),
+ SOC_SINGLE("DMIC3_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN3_CON0,
+ DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0),
+ SOC_SINGLE("DMIC4_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN4_CON0,
+ DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0),
+};
+
+static int init_dmic_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_dmic_priv *dmic_priv;
+
+ dmic_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_dmic_priv),
+ GFP_KERNEL);
+ if (!dmic_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN] = dmic_priv;
+ return 0;
+}
+
+int mt8188_dai_dmic_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_dmic_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_dmic_driver);
+ dai->dapm_widgets = mtk_dai_dmic_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_dmic_widgets);
+ dai->dapm_routes = mtk_dai_dmic_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_dmic_routes);
+ dai->controls = mtk_dai_dmic_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_dmic_controls);
+
+ return init_dmic_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
new file mode 100644
index 000000000000..4dfaa761f9f7
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
@@ -0,0 +1,2712 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI eTDM Control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+
+#define MT8188_ETDM_MAX_CHANNELS 16
+#define MT8188_ETDM_NORMAL_MAX_BCK_RATE 24576000
+#define ETDM_TO_DAI_ID(x) ((x) + MT8188_AFE_IO_ETDM_START)
+#define ENUM_TO_STR(x) #x
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_ETDM_MCLK,
+ SUPPLY_SEQ_ETDM_CG,
+ SUPPLY_SEQ_DPTX_EN,
+ SUPPLY_SEQ_ETDM_EN,
+};
+
+enum {
+ MTK_DAI_ETDM_FORMAT_I2S = 0,
+ MTK_DAI_ETDM_FORMAT_LJ,
+ MTK_DAI_ETDM_FORMAT_RJ,
+ MTK_DAI_ETDM_FORMAT_EIAJ,
+ MTK_DAI_ETDM_FORMAT_DSPA,
+ MTK_DAI_ETDM_FORMAT_DSPB,
+};
+
+enum {
+ MTK_DAI_ETDM_DATA_ONE_PIN = 0,
+ MTK_DAI_ETDM_DATA_MULTI_PIN,
+};
+
+enum {
+ ETDM_IN,
+ ETDM_OUT,
+};
+
+enum {
+ COWORK_ETDM_NONE = 0,
+ COWORK_ETDM_IN1_M = 2,
+ COWORK_ETDM_IN1_S = 3,
+ COWORK_ETDM_IN2_M = 4,
+ COWORK_ETDM_IN2_S = 5,
+ COWORK_ETDM_OUT1_M = 10,
+ COWORK_ETDM_OUT1_S = 11,
+ COWORK_ETDM_OUT2_M = 12,
+ COWORK_ETDM_OUT2_S = 13,
+ COWORK_ETDM_OUT3_M = 14,
+ COWORK_ETDM_OUT3_S = 15,
+};
+
+enum {
+ ETDM_RELATCH_TIMING_A1A2SYS,
+ ETDM_RELATCH_TIMING_A3SYS,
+ ETDM_RELATCH_TIMING_A4SYS,
+};
+
+enum {
+ ETDM_SYNC_NONE,
+ ETDM_SYNC_FROM_IN1 = 2,
+ ETDM_SYNC_FROM_IN2 = 4,
+ ETDM_SYNC_FROM_OUT1 = 10,
+ ETDM_SYNC_FROM_OUT2 = 12,
+ ETDM_SYNC_FROM_OUT3 = 14,
+};
+
+struct etdm_con_reg {
+ unsigned int con0;
+ unsigned int con1;
+ unsigned int con2;
+ unsigned int con3;
+ unsigned int con4;
+ unsigned int con5;
+};
+
+struct mtk_dai_etdm_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+struct mtk_dai_etdm_priv {
+ unsigned int data_mode;
+ bool slave_mode;
+ bool lrck_inv;
+ bool bck_inv;
+ unsigned int rate;
+ unsigned int format;
+ unsigned int slots;
+ unsigned int lrck_width;
+ unsigned int mclk_freq;
+ unsigned int mclk_fixed_apll;
+ unsigned int mclk_apll;
+ unsigned int mclk_dir;
+ int cowork_source_id; //dai id
+ unsigned int cowork_slv_count;
+ int cowork_slv_id[MT8188_AFE_IO_ETDM_NUM - 1]; //dai_id
+ bool in_disable_ch[MT8188_ETDM_MAX_CHANNELS];
+};
+
+static const struct mtk_dai_etdm_rate mt8188_etdm_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 12000, .reg_value = 1, },
+ { .rate = 16000, .reg_value = 2, },
+ { .rate = 24000, .reg_value = 3, },
+ { .rate = 32000, .reg_value = 4, },
+ { .rate = 48000, .reg_value = 5, },
+ { .rate = 96000, .reg_value = 7, },
+ { .rate = 192000, .reg_value = 9, },
+ { .rate = 384000, .reg_value = 11, },
+ { .rate = 11025, .reg_value = 16, },
+ { .rate = 22050, .reg_value = 17, },
+ { .rate = 44100, .reg_value = 18, },
+ { .rate = 88200, .reg_value = 19, },
+ { .rate = 176400, .reg_value = 20, },
+ { .rate = 352800, .reg_value = 21, },
+};
+
+static int get_etdm_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8188_etdm_rates); i++)
+ if (mt8188_etdm_rates[i].rate == rate)
+ return mt8188_etdm_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static unsigned int get_etdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 16)
+ return 24;
+ else if (channels > 8)
+ return 16;
+ else if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+ else
+ return 2;
+}
+
+static int get_etdm_reg(unsigned int dai_id, struct etdm_con_reg *etdm_reg)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ etdm_reg->con0 = ETDM_IN1_CON0;
+ etdm_reg->con1 = ETDM_IN1_CON1;
+ etdm_reg->con2 = ETDM_IN1_CON2;
+ etdm_reg->con3 = ETDM_IN1_CON3;
+ etdm_reg->con4 = ETDM_IN1_CON4;
+ etdm_reg->con5 = ETDM_IN1_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ etdm_reg->con0 = ETDM_IN2_CON0;
+ etdm_reg->con1 = ETDM_IN2_CON1;
+ etdm_reg->con2 = ETDM_IN2_CON2;
+ etdm_reg->con3 = ETDM_IN2_CON3;
+ etdm_reg->con4 = ETDM_IN2_CON4;
+ etdm_reg->con5 = ETDM_IN2_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ etdm_reg->con0 = ETDM_OUT1_CON0;
+ etdm_reg->con1 = ETDM_OUT1_CON1;
+ etdm_reg->con2 = ETDM_OUT1_CON2;
+ etdm_reg->con3 = ETDM_OUT1_CON3;
+ etdm_reg->con4 = ETDM_OUT1_CON4;
+ etdm_reg->con5 = ETDM_OUT1_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ etdm_reg->con0 = ETDM_OUT2_CON0;
+ etdm_reg->con1 = ETDM_OUT2_CON1;
+ etdm_reg->con2 = ETDM_OUT2_CON2;
+ etdm_reg->con3 = ETDM_OUT2_CON3;
+ etdm_reg->con4 = ETDM_OUT2_CON4;
+ etdm_reg->con5 = ETDM_OUT2_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ case MT8188_AFE_IO_DPTX:
+ etdm_reg->con0 = ETDM_OUT3_CON0;
+ etdm_reg->con1 = ETDM_OUT3_CON1;
+ etdm_reg->con2 = ETDM_OUT3_CON2;
+ etdm_reg->con3 = ETDM_OUT3_CON3;
+ etdm_reg->con4 = ETDM_OUT3_CON4;
+ etdm_reg->con5 = ETDM_OUT3_CON5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int get_etdm_dir(unsigned int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ case MT8188_AFE_IO_ETDM2_IN:
+ return ETDM_IN;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ case MT8188_AFE_IO_ETDM2_OUT:
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return ETDM_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int get_etdm_wlen(unsigned int bitwidth)
+{
+ return bitwidth <= 16 ? 16 : 32;
+}
+
+static bool is_valid_etdm_dai(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM2_IN:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ fallthrough;
+ case MT8188_AFE_IO_DPTX:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int is_cowork_mode(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ return (etdm_data->cowork_slv_count > 0 ||
+ etdm_data->cowork_source_id != COWORK_ETDM_NONE);
+}
+
+static int sync_to_dai_id(int source_sel)
+{
+ switch (source_sel) {
+ case ETDM_SYNC_FROM_IN1:
+ return MT8188_AFE_IO_ETDM1_IN;
+ case ETDM_SYNC_FROM_IN2:
+ return MT8188_AFE_IO_ETDM2_IN;
+ case ETDM_SYNC_FROM_OUT1:
+ return MT8188_AFE_IO_ETDM1_OUT;
+ case ETDM_SYNC_FROM_OUT2:
+ return MT8188_AFE_IO_ETDM2_OUT;
+ case ETDM_SYNC_FROM_OUT3:
+ return MT8188_AFE_IO_ETDM3_OUT;
+ default:
+ return 0;
+ }
+}
+
+static int get_etdm_cowork_master_id(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+ dai_id = etdm_data->cowork_source_id;
+
+ if (dai_id == COWORK_ETDM_NONE)
+ dai_id = dai->id;
+
+ return dai_id;
+}
+
+static int mtk_dai_etdm_get_cg_id_by_dai_id(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_DPTX:
+ return MT8188_CLK_AUD_HDMI_OUT;
+ case MT8188_AFE_IO_ETDM1_IN:
+ return MT8188_CLK_AUD_TDM_IN;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return MT8188_CLK_AUD_I2SIN;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return MT8188_CLK_AUD_TDM_OUT;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return MT8188_CLK_AUD_I2S_OUT;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return MT8188_CLK_AUD_HDMI_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_get_clk_id_by_dai_id(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_DPTX:
+ return MT8188_CLK_TOP_DPTX_M_SEL;
+ case MT8188_AFE_IO_ETDM1_IN:
+ return MT8188_CLK_TOP_I2SI1_M_SEL;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return MT8188_CLK_TOP_I2SI2_M_SEL;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return MT8188_CLK_TOP_I2SO1_M_SEL;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return MT8188_CLK_TOP_I2SO2_M_SEL;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_get_clkdiv_id_by_dai_id(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_DPTX:
+ return MT8188_CLK_TOP_APLL12_DIV9;
+ case MT8188_AFE_IO_ETDM1_IN:
+ return MT8188_CLK_TOP_APLL12_DIV0;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return MT8188_CLK_TOP_APLL12_DIV1;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return MT8188_CLK_TOP_APLL12_DIV2;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return MT8188_CLK_TOP_APLL12_DIV3;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ default:
+ return -EINVAL;
+ }
+}
+
+static int get_etdm_id_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ if (!strncmp(name, "ETDM1_IN", strlen("ETDM1_IN")))
+ return MT8188_AFE_IO_ETDM1_IN;
+ else if (!strncmp(name, "ETDM2_IN", strlen("ETDM2_IN")))
+ return MT8188_AFE_IO_ETDM2_IN;
+ else if (!strncmp(name, "ETDM1_OUT", strlen("ETDM1_OUT")))
+ return MT8188_AFE_IO_ETDM1_OUT;
+ else if (!strncmp(name, "ETDM2_OUT", strlen("ETDM2_OUT")))
+ return MT8188_AFE_IO_ETDM2_OUT;
+ else if (!strncmp(name, "ETDM3_OUT", strlen("ETDM3_OUT")))
+ return MT8188_AFE_IO_ETDM3_OUT;
+ else if (!strncmp(name, "DPTX", strlen("DPTX")))
+ return MT8188_AFE_IO_ETDM3_OUT;
+ else
+ return -EINVAL;
+}
+
+static struct mtk_dai_etdm_priv *get_etdm_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_etdm_id_by_name(afe, name);
+
+ if (dai_id < MT8188_AFE_IO_ETDM_START ||
+ dai_id >= MT8188_AFE_IO_ETDM_END)
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ unsigned int val = 0;
+ unsigned int mask;
+ int clkmux_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+ int apll_clk_id;
+ int apll;
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ apll = etdm_data->mclk_apll;
+ apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll);
+
+ if (clkmux_id < 0 || clkdiv_id < 0)
+ return -EINVAL;
+
+ if (apll_clk_id < 0)
+ return apll_clk_id;
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ mask = ETDM_CON1_MCLK_OUTPUT;
+ if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
+ val = ETDM_CON1_MCLK_OUTPUT;
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ /* enable parent clock before select apll*/
+ mt8188_afe_enable_clk(afe, afe_priv->clk[clkmux_id]);
+
+ /* select apll */
+ ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clkmux_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret)
+ return ret;
+
+ /* set rate */
+ ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id],
+ etdm_data->mclk_freq);
+
+ mt8188_afe_enable_clk(afe, afe_priv->clk[clkdiv_id]);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_disable_mclk(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int clkmux_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+
+ if (clkmux_id < 0 || clkdiv_id < 0)
+ return -EINVAL;
+
+ mt8188_afe_disable_clk(afe, afe_priv->clk[clkdiv_id]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[clkmux_id]);
+
+ return 0;
+}
+
+static int mtk_afe_etdm_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int cur_apll;
+ int need_apll;
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+ if (!etdm_priv) {
+ dev_dbg(afe->dev, "etdm_priv == NULL\n");
+ return 0;
+ }
+
+ cur_apll = mt8188_get_apll_by_name(afe, source->name);
+ need_apll = mt8188_get_apll_by_rate(afe, etdm_priv->rate);
+
+ return (need_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int cur_apll;
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+
+ cur_apll = mt8188_get_apll_by_name(afe, source->name);
+
+ return (etdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_etdm_mclk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int mclk_id;
+
+ mclk_id = get_etdm_id_by_name(afe, source->name);
+ if (mclk_id < 0) {
+ dev_dbg(afe->dev, "mclk_id < 0\n");
+ return 0;
+ }
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+ if (!etdm_priv) {
+ dev_dbg(afe->dev, "etdm_priv == NULL\n");
+ return 0;
+ }
+
+ if (get_etdm_id_by_name(afe, sink->name) == mclk_id)
+ return !!(etdm_priv->mclk_freq > 0);
+
+ if (etdm_priv->cowork_source_id == mclk_id) {
+ etdm_priv = afe_priv->dai_priv[mclk_id];
+ return !!(etdm_priv->mclk_freq > 0);
+ }
+
+ return 0;
+}
+
+static int mtk_etdm_cowork_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int source_id;
+ int i;
+
+ source_id = get_etdm_id_by_name(afe, source->name);
+ if (source_id < 0) {
+ dev_dbg(afe->dev, "%s() source_id < 0\n", __func__);
+ return 0;
+ }
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+ if (!etdm_priv) {
+ dev_dbg(afe->dev, "%s() etdm_priv == NULL\n", __func__);
+ return 0;
+ }
+
+ if (etdm_priv->cowork_source_id != COWORK_ETDM_NONE) {
+ if (etdm_priv->cowork_source_id == source_id)
+ return 1;
+
+ etdm_priv = afe_priv->dai_priv[etdm_priv->cowork_source_id];
+ for (i = 0; i < etdm_priv->cowork_slv_count; i++) {
+ if (etdm_priv->cowork_slv_id[i] == source_id)
+ return 1;
+ }
+ } else {
+ for (i = 0; i < etdm_priv->cowork_slv_count; i++) {
+ if (etdm_priv->cowork_slv_id[i] == source_id)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int mtk_apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8188_apll1_enable(afe);
+ else
+ mt8188_apll2_enable(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8188_apll1_disable(afe);
+ else
+ mt8188_apll2_disable(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_etdm_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ int mclk_id = get_etdm_id_by_name(afe, w->name);
+
+ if (mclk_id < 0) {
+ dev_dbg(afe->dev, "%s() mclk_id < 0\n", __func__);
+ return 0;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_dai_etdm_enable_mclk(afe, mclk_id);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mtk_dai_etdm_disable_mclk(afe, mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dptx_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_dai_etdm_enable_mclk(afe, MT8188_AFE_IO_DPTX);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mtk_dai_etdm_disable_mclk(afe, MT8188_AFE_IO_DPTX);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_etdm_cg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int etdm_id;
+ int cg_id;
+
+ etdm_id = get_etdm_id_by_name(afe, w->name);
+ if (etdm_id < 0) {
+ dev_dbg(afe->dev, "%s() etdm_id < 0\n", __func__);
+ return 0;
+ }
+
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(etdm_id);
+ if (cg_id < 0) {
+ dev_dbg(afe->dev, "%s() cg_id < 0\n", __func__);
+ return 0;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_etdm3_cg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_HDMI_OUT]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_HDMI_OUT]);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o048_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN48, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN48, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN48_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN48_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o049_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN49, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN49, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN49_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN49_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o050_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN50, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN50_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o051_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN51, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN51_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o052_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN52, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN52_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o053_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN53, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN53_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o054_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN54, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN54_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o055_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN55, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN55_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o056_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN56, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN56_1, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o057_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN57, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN57_1, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o058_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN58_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN58_1, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o059_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN59_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN59_1, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o060_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN60_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN60_1, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o061_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN61_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN61_1, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o062_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN62_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN62_1, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o063_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN63_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN63_1, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o072_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN72, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN72, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN72_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN72_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o073_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN73, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN73, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN73_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN73_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o074_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN74, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN74_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o075_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN75, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN75_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o076_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN76, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN76_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o077_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN77, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN77_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o078_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN78, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN78_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o079_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN79, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN79_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o080_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN80, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN80_1, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o081_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN81, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN81_1, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o082_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN82_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN82_1, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o083_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN83_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN83_1, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o084_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN84_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN84_1, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o085_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN85_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN85_1, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o086_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN86_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN86_1, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o087_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN87_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN87_1, 29, 1, 0),
+};
+
+static const char * const mt8188_etdm_clk_src_sel_text[] = {
+ "26m",
+ "a1sys_a2sys",
+ "a3sys",
+ "a4sys",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_text);
+
+static const char * const hdmitx_dptx_mux_map[] = {
+ "Disconnect", "Connect",
+};
+
+static int hdmitx_dptx_mux_map_value[] = {
+ 0, 1,
+};
+
+/* HDMI_OUT_MUX */
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ hdmitx_dptx_mux_map,
+ hdmitx_dptx_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_out_mux_control =
+ SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum);
+
+/* DPTX_OUT_MUX */
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ hdmitx_dptx_mux_map,
+ hdmitx_dptx_mux_map_value);
+
+static const struct snd_kcontrol_new dptx_out_mux_control =
+ SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum);
+
+/* HDMI_CH0_MUX ~ HDMI_CH7_MUX */
+static const char *const afe_conn_hdmi_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7",
+};
+
+static int afe_conn_hdmi_mux_map_value[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 0,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch0_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 4,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch1_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 8,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch2_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 12,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch3_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 16,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch4_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 20,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch5_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 24,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch6_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 28,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch7_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
+
+static int mt8188_etdm_clk_src_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int source = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ unsigned int old_val;
+ unsigned int mask;
+ unsigned int reg;
+
+ if (source >= e->items)
+ return -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) {
+ reg = ETDM_OUT1_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) {
+ reg = ETDM_OUT2_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) {
+ reg = ETDM_OUT3_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) {
+ reg = ETDM_IN1_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ val = FIELD_PREP(ETDM_IN_CON2_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) {
+ reg = ETDM_IN2_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ val = FIELD_PREP(ETDM_IN_CON2_CLOCK_MASK, source);
+ } else {
+ return -EINVAL;
+ }
+
+ regmap_read(afe->regmap, reg, &old_val);
+ old_val &= mask;
+ if (old_val == val)
+ return 0;
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 1;
+}
+
+static int mt8188_etdm_clk_src_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+ unsigned int reg;
+ unsigned int mask;
+ unsigned int shift;
+
+ if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) {
+ reg = ETDM_OUT1_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) {
+ reg = ETDM_OUT2_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) {
+ reg = ETDM_OUT3_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) {
+ reg = ETDM_IN1_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) {
+ reg = ETDM_IN2_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ } else {
+ return -EINVAL;
+ }
+
+ regmap_read(afe->regmap, reg, &value);
+
+ value &= mask;
+ value >>= shift;
+ ucontrol->value.enumerated.item[0] = value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_etdm_controls[] = {
+ SOC_ENUM_EXT("ETDM_OUT1_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_OUT2_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_OUT3_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_IN1_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_IN2_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
+ /* eTDM_IN2 */
+ SND_SOC_DAPM_MIXER("I012", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I013", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I014", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I015", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I016", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I017", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I018", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I019", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I188", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I189", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I190", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I191", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I192", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I193", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I194", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I195", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* eTDM_IN1 */
+ SND_SOC_DAPM_MIXER("I072", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I073", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I074", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I075", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I076", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I077", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I078", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I079", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I080", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I081", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I082", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I083", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I084", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I085", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I086", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I087", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* eTDM_OUT2 */
+ SND_SOC_DAPM_MIXER("O048", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o048_mix, ARRAY_SIZE(mtk_dai_etdm_o048_mix)),
+ SND_SOC_DAPM_MIXER("O049", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o049_mix, ARRAY_SIZE(mtk_dai_etdm_o049_mix)),
+ SND_SOC_DAPM_MIXER("O050", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o050_mix, ARRAY_SIZE(mtk_dai_etdm_o050_mix)),
+ SND_SOC_DAPM_MIXER("O051", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o051_mix, ARRAY_SIZE(mtk_dai_etdm_o051_mix)),
+ SND_SOC_DAPM_MIXER("O052", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o052_mix, ARRAY_SIZE(mtk_dai_etdm_o052_mix)),
+ SND_SOC_DAPM_MIXER("O053", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o053_mix, ARRAY_SIZE(mtk_dai_etdm_o053_mix)),
+ SND_SOC_DAPM_MIXER("O054", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o054_mix, ARRAY_SIZE(mtk_dai_etdm_o054_mix)),
+ SND_SOC_DAPM_MIXER("O055", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o055_mix, ARRAY_SIZE(mtk_dai_etdm_o055_mix)),
+ SND_SOC_DAPM_MIXER("O056", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o056_mix, ARRAY_SIZE(mtk_dai_etdm_o056_mix)),
+ SND_SOC_DAPM_MIXER("O057", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o057_mix, ARRAY_SIZE(mtk_dai_etdm_o057_mix)),
+ SND_SOC_DAPM_MIXER("O058", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o058_mix, ARRAY_SIZE(mtk_dai_etdm_o058_mix)),
+ SND_SOC_DAPM_MIXER("O059", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o059_mix, ARRAY_SIZE(mtk_dai_etdm_o059_mix)),
+ SND_SOC_DAPM_MIXER("O060", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o060_mix, ARRAY_SIZE(mtk_dai_etdm_o060_mix)),
+ SND_SOC_DAPM_MIXER("O061", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o061_mix, ARRAY_SIZE(mtk_dai_etdm_o061_mix)),
+ SND_SOC_DAPM_MIXER("O062", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o062_mix, ARRAY_SIZE(mtk_dai_etdm_o062_mix)),
+ SND_SOC_DAPM_MIXER("O063", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o063_mix, ARRAY_SIZE(mtk_dai_etdm_o063_mix)),
+
+ /* eTDM_OUT1 */
+ SND_SOC_DAPM_MIXER("O072", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o072_mix, ARRAY_SIZE(mtk_dai_etdm_o072_mix)),
+ SND_SOC_DAPM_MIXER("O073", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o073_mix, ARRAY_SIZE(mtk_dai_etdm_o073_mix)),
+ SND_SOC_DAPM_MIXER("O074", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o074_mix, ARRAY_SIZE(mtk_dai_etdm_o074_mix)),
+ SND_SOC_DAPM_MIXER("O075", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o075_mix, ARRAY_SIZE(mtk_dai_etdm_o075_mix)),
+ SND_SOC_DAPM_MIXER("O076", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o076_mix, ARRAY_SIZE(mtk_dai_etdm_o076_mix)),
+ SND_SOC_DAPM_MIXER("O077", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o077_mix, ARRAY_SIZE(mtk_dai_etdm_o077_mix)),
+ SND_SOC_DAPM_MIXER("O078", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o078_mix, ARRAY_SIZE(mtk_dai_etdm_o078_mix)),
+ SND_SOC_DAPM_MIXER("O079", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o079_mix, ARRAY_SIZE(mtk_dai_etdm_o079_mix)),
+ SND_SOC_DAPM_MIXER("O080", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o080_mix, ARRAY_SIZE(mtk_dai_etdm_o080_mix)),
+ SND_SOC_DAPM_MIXER("O081", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o081_mix, ARRAY_SIZE(mtk_dai_etdm_o081_mix)),
+ SND_SOC_DAPM_MIXER("O082", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o082_mix, ARRAY_SIZE(mtk_dai_etdm_o082_mix)),
+ SND_SOC_DAPM_MIXER("O083", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o083_mix, ARRAY_SIZE(mtk_dai_etdm_o083_mix)),
+ SND_SOC_DAPM_MIXER("O084", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o084_mix, ARRAY_SIZE(mtk_dai_etdm_o084_mix)),
+ SND_SOC_DAPM_MIXER("O085", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o085_mix, ARRAY_SIZE(mtk_dai_etdm_o085_mix)),
+ SND_SOC_DAPM_MIXER("O086", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o086_mix, ARRAY_SIZE(mtk_dai_etdm_o086_mix)),
+ SND_SOC_DAPM_MIXER("O087", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o087_mix, ARRAY_SIZE(mtk_dai_etdm_o087_mix)),
+
+ /* eTDM_OUT3 */
+ SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_out_mux_control),
+ SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &dptx_out_mux_control),
+
+ SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch0_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch1_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch2_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch3_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch4_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch5_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch6_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch7_mux_control),
+
+ /* mclk en */
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("DPTX_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_dptx_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* cg */
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM3_OUT_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm3_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* en */
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_IN1_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_IN2_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_OUT1_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_OUT2_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM3_OUT_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_OUT3_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DPTX_EN", SUPPLY_SEQ_DPTX_EN,
+ AFE_DPTX_CON, AFE_DPTX_CON_ON_SHIFT, 0, NULL, 0),
+
+ /* apll */
+ SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("ETDM_INPUT"),
+ SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
+ /* mclk */
+ {"ETDM1_IN", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_IN", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_IN", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_IN", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"ETDM2_IN", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_IN", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_IN", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_IN", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"ETDM1_OUT", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_OUT", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"ETDM2_OUT", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_OUT", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"DPTX", NULL, "DPTX_MCLK"},
+
+ {"ETDM1_IN_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM1_IN_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"ETDM2_IN_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM2_IN_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"ETDM1_OUT_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM1_OUT_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"ETDM2_OUT_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM2_OUT_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"DPTX_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"DPTX_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* cg */
+ {"ETDM1_IN", NULL, "ETDM1_IN_CG"},
+ {"ETDM1_IN", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect},
+
+ {"ETDM2_IN", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_IN_CG"},
+ {"ETDM2_IN", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect},
+
+ {"ETDM1_OUT", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM1_OUT_CG"},
+ {"ETDM1_OUT", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect},
+
+ {"ETDM2_OUT", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_OUT_CG"},
+
+ {"ETDM3_OUT", NULL, "ETDM3_OUT_CG"},
+ {"DPTX", NULL, "ETDM3_OUT_CG"},
+
+ /* en */
+ {"ETDM1_IN", NULL, "ETDM1_IN_EN"},
+ {"ETDM1_IN", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect},
+
+ {"ETDM2_IN", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_IN_EN"},
+ {"ETDM2_IN", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect},
+
+ {"ETDM1_OUT", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM1_OUT_EN"},
+ {"ETDM1_OUT", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect},
+
+ {"ETDM2_OUT", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_OUT_EN"},
+
+ {"ETDM3_OUT", NULL, "ETDM3_OUT_EN"},
+ {"DPTX", NULL, "ETDM3_OUT_EN"},
+ {"DPTX", NULL, "DPTX_EN"},
+
+ {"ETDM1_IN_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM1_IN_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM2_IN_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM2_IN_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM1_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM1_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM2_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM2_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM3_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM3_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"I012", NULL, "ETDM2_IN"},
+ {"I013", NULL, "ETDM2_IN"},
+ {"I014", NULL, "ETDM2_IN"},
+ {"I015", NULL, "ETDM2_IN"},
+ {"I016", NULL, "ETDM2_IN"},
+ {"I017", NULL, "ETDM2_IN"},
+ {"I018", NULL, "ETDM2_IN"},
+ {"I019", NULL, "ETDM2_IN"},
+ {"I188", NULL, "ETDM2_IN"},
+ {"I189", NULL, "ETDM2_IN"},
+ {"I190", NULL, "ETDM2_IN"},
+ {"I191", NULL, "ETDM2_IN"},
+ {"I192", NULL, "ETDM2_IN"},
+ {"I193", NULL, "ETDM2_IN"},
+ {"I194", NULL, "ETDM2_IN"},
+ {"I195", NULL, "ETDM2_IN"},
+
+ {"I072", NULL, "ETDM1_IN"},
+ {"I073", NULL, "ETDM1_IN"},
+ {"I074", NULL, "ETDM1_IN"},
+ {"I075", NULL, "ETDM1_IN"},
+ {"I076", NULL, "ETDM1_IN"},
+ {"I077", NULL, "ETDM1_IN"},
+ {"I078", NULL, "ETDM1_IN"},
+ {"I079", NULL, "ETDM1_IN"},
+ {"I080", NULL, "ETDM1_IN"},
+ {"I081", NULL, "ETDM1_IN"},
+ {"I082", NULL, "ETDM1_IN"},
+ {"I083", NULL, "ETDM1_IN"},
+ {"I084", NULL, "ETDM1_IN"},
+ {"I085", NULL, "ETDM1_IN"},
+ {"I086", NULL, "ETDM1_IN"},
+ {"I087", NULL, "ETDM1_IN"},
+
+ {"UL8", NULL, "ETDM1_IN"},
+ {"UL3", NULL, "ETDM2_IN"},
+
+ {"ETDM2_OUT", NULL, "O048"},
+ {"ETDM2_OUT", NULL, "O049"},
+ {"ETDM2_OUT", NULL, "O050"},
+ {"ETDM2_OUT", NULL, "O051"},
+ {"ETDM2_OUT", NULL, "O052"},
+ {"ETDM2_OUT", NULL, "O053"},
+ {"ETDM2_OUT", NULL, "O054"},
+ {"ETDM2_OUT", NULL, "O055"},
+ {"ETDM2_OUT", NULL, "O056"},
+ {"ETDM2_OUT", NULL, "O057"},
+ {"ETDM2_OUT", NULL, "O058"},
+ {"ETDM2_OUT", NULL, "O059"},
+ {"ETDM2_OUT", NULL, "O060"},
+ {"ETDM2_OUT", NULL, "O061"},
+ {"ETDM2_OUT", NULL, "O062"},
+ {"ETDM2_OUT", NULL, "O063"},
+
+ {"ETDM1_OUT", NULL, "O072"},
+ {"ETDM1_OUT", NULL, "O073"},
+ {"ETDM1_OUT", NULL, "O074"},
+ {"ETDM1_OUT", NULL, "O075"},
+ {"ETDM1_OUT", NULL, "O076"},
+ {"ETDM1_OUT", NULL, "O077"},
+ {"ETDM1_OUT", NULL, "O078"},
+ {"ETDM1_OUT", NULL, "O079"},
+ {"ETDM1_OUT", NULL, "O080"},
+ {"ETDM1_OUT", NULL, "O081"},
+ {"ETDM1_OUT", NULL, "O082"},
+ {"ETDM1_OUT", NULL, "O083"},
+ {"ETDM1_OUT", NULL, "O084"},
+ {"ETDM1_OUT", NULL, "O085"},
+ {"ETDM1_OUT", NULL, "O086"},
+ {"ETDM1_OUT", NULL, "O087"},
+
+ {"O048", "I020 Switch", "I020"},
+ {"O049", "I021 Switch", "I021"},
+
+ {"O048", "I022 Switch", "I022"},
+ {"O049", "I023 Switch", "I023"},
+ {"O050", "I024 Switch", "I024"},
+ {"O051", "I025 Switch", "I025"},
+ {"O052", "I026 Switch", "I026"},
+ {"O053", "I027 Switch", "I027"},
+ {"O054", "I028 Switch", "I028"},
+ {"O055", "I029 Switch", "I029"},
+ {"O056", "I030 Switch", "I030"},
+ {"O057", "I031 Switch", "I031"},
+ {"O058", "I032 Switch", "I032"},
+ {"O059", "I033 Switch", "I033"},
+ {"O060", "I034 Switch", "I034"},
+ {"O061", "I035 Switch", "I035"},
+ {"O062", "I036 Switch", "I036"},
+ {"O063", "I037 Switch", "I037"},
+
+ {"O048", "I046 Switch", "I046"},
+ {"O049", "I047 Switch", "I047"},
+ {"O050", "I048 Switch", "I048"},
+ {"O051", "I049 Switch", "I049"},
+ {"O052", "I050 Switch", "I050"},
+ {"O053", "I051 Switch", "I051"},
+ {"O054", "I052 Switch", "I052"},
+ {"O055", "I053 Switch", "I053"},
+ {"O056", "I054 Switch", "I054"},
+ {"O057", "I055 Switch", "I055"},
+ {"O058", "I056 Switch", "I056"},
+ {"O059", "I057 Switch", "I057"},
+ {"O060", "I058 Switch", "I058"},
+ {"O061", "I059 Switch", "I059"},
+ {"O062", "I060 Switch", "I060"},
+ {"O063", "I061 Switch", "I061"},
+
+ {"O048", "I070 Switch", "I070"},
+ {"O049", "I071 Switch", "I071"},
+
+ {"O072", "I020 Switch", "I020"},
+ {"O073", "I021 Switch", "I021"},
+
+ {"O072", "I022 Switch", "I022"},
+ {"O073", "I023 Switch", "I023"},
+ {"O074", "I024 Switch", "I024"},
+ {"O075", "I025 Switch", "I025"},
+ {"O076", "I026 Switch", "I026"},
+ {"O077", "I027 Switch", "I027"},
+ {"O078", "I028 Switch", "I028"},
+ {"O079", "I029 Switch", "I029"},
+ {"O080", "I030 Switch", "I030"},
+ {"O081", "I031 Switch", "I031"},
+ {"O082", "I032 Switch", "I032"},
+ {"O083", "I033 Switch", "I033"},
+ {"O084", "I034 Switch", "I034"},
+ {"O085", "I035 Switch", "I035"},
+ {"O086", "I036 Switch", "I036"},
+ {"O087", "I037 Switch", "I037"},
+
+ {"O072", "I046 Switch", "I046"},
+ {"O073", "I047 Switch", "I047"},
+ {"O074", "I048 Switch", "I048"},
+ {"O075", "I049 Switch", "I049"},
+ {"O076", "I050 Switch", "I050"},
+ {"O077", "I051 Switch", "I051"},
+ {"O078", "I052 Switch", "I052"},
+ {"O079", "I053 Switch", "I053"},
+ {"O080", "I054 Switch", "I054"},
+ {"O081", "I055 Switch", "I055"},
+ {"O082", "I056 Switch", "I056"},
+ {"O083", "I057 Switch", "I057"},
+ {"O084", "I058 Switch", "I058"},
+ {"O085", "I059 Switch", "I059"},
+ {"O086", "I060 Switch", "I060"},
+ {"O087", "I061 Switch", "I061"},
+
+ {"O072", "I070 Switch", "I070"},
+ {"O073", "I071 Switch", "I071"},
+
+ {"HDMI_CH0_MUX", "CH0", "DL10"},
+ {"HDMI_CH0_MUX", "CH1", "DL10"},
+ {"HDMI_CH0_MUX", "CH2", "DL10"},
+ {"HDMI_CH0_MUX", "CH3", "DL10"},
+ {"HDMI_CH0_MUX", "CH4", "DL10"},
+ {"HDMI_CH0_MUX", "CH5", "DL10"},
+ {"HDMI_CH0_MUX", "CH6", "DL10"},
+ {"HDMI_CH0_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH1_MUX", "CH0", "DL10"},
+ {"HDMI_CH1_MUX", "CH1", "DL10"},
+ {"HDMI_CH1_MUX", "CH2", "DL10"},
+ {"HDMI_CH1_MUX", "CH3", "DL10"},
+ {"HDMI_CH1_MUX", "CH4", "DL10"},
+ {"HDMI_CH1_MUX", "CH5", "DL10"},
+ {"HDMI_CH1_MUX", "CH6", "DL10"},
+ {"HDMI_CH1_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH2_MUX", "CH0", "DL10"},
+ {"HDMI_CH2_MUX", "CH1", "DL10"},
+ {"HDMI_CH2_MUX", "CH2", "DL10"},
+ {"HDMI_CH2_MUX", "CH3", "DL10"},
+ {"HDMI_CH2_MUX", "CH4", "DL10"},
+ {"HDMI_CH2_MUX", "CH5", "DL10"},
+ {"HDMI_CH2_MUX", "CH6", "DL10"},
+ {"HDMI_CH2_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH3_MUX", "CH0", "DL10"},
+ {"HDMI_CH3_MUX", "CH1", "DL10"},
+ {"HDMI_CH3_MUX", "CH2", "DL10"},
+ {"HDMI_CH3_MUX", "CH3", "DL10"},
+ {"HDMI_CH3_MUX", "CH4", "DL10"},
+ {"HDMI_CH3_MUX", "CH5", "DL10"},
+ {"HDMI_CH3_MUX", "CH6", "DL10"},
+ {"HDMI_CH3_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH4_MUX", "CH0", "DL10"},
+ {"HDMI_CH4_MUX", "CH1", "DL10"},
+ {"HDMI_CH4_MUX", "CH2", "DL10"},
+ {"HDMI_CH4_MUX", "CH3", "DL10"},
+ {"HDMI_CH4_MUX", "CH4", "DL10"},
+ {"HDMI_CH4_MUX", "CH5", "DL10"},
+ {"HDMI_CH4_MUX", "CH6", "DL10"},
+ {"HDMI_CH4_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH5_MUX", "CH0", "DL10"},
+ {"HDMI_CH5_MUX", "CH1", "DL10"},
+ {"HDMI_CH5_MUX", "CH2", "DL10"},
+ {"HDMI_CH5_MUX", "CH3", "DL10"},
+ {"HDMI_CH5_MUX", "CH4", "DL10"},
+ {"HDMI_CH5_MUX", "CH5", "DL10"},
+ {"HDMI_CH5_MUX", "CH6", "DL10"},
+ {"HDMI_CH5_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH6_MUX", "CH0", "DL10"},
+ {"HDMI_CH6_MUX", "CH1", "DL10"},
+ {"HDMI_CH6_MUX", "CH2", "DL10"},
+ {"HDMI_CH6_MUX", "CH3", "DL10"},
+ {"HDMI_CH6_MUX", "CH4", "DL10"},
+ {"HDMI_CH6_MUX", "CH5", "DL10"},
+ {"HDMI_CH6_MUX", "CH6", "DL10"},
+ {"HDMI_CH6_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH7_MUX", "CH0", "DL10"},
+ {"HDMI_CH7_MUX", "CH1", "DL10"},
+ {"HDMI_CH7_MUX", "CH2", "DL10"},
+ {"HDMI_CH7_MUX", "CH3", "DL10"},
+ {"HDMI_CH7_MUX", "CH4", "DL10"},
+ {"HDMI_CH7_MUX", "CH5", "DL10"},
+ {"HDMI_CH7_MUX", "CH6", "DL10"},
+ {"HDMI_CH7_MUX", "CH7", "DL10"},
+
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"ETDM3_OUT", NULL, "HDMI_OUT_MUX"},
+ {"DPTX", NULL, "DPTX_OUT_MUX"},
+
+ {"ETDM_OUTPUT", NULL, "DPTX"},
+ {"ETDM_OUTPUT", NULL, "ETDM1_OUT"},
+ {"ETDM_OUTPUT", NULL, "ETDM2_OUT"},
+ {"ETDM_OUTPUT", NULL, "ETDM3_OUT"},
+ {"ETDM1_IN", NULL, "ETDM_INPUT"},
+ {"ETDM2_IN", NULL, "ETDM_INPUT"},
+};
+
+static int etdm_cowork_slv_sel(int id, int slave_mode)
+{
+ if (slave_mode) {
+ switch (id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ return COWORK_ETDM_IN1_S;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return COWORK_ETDM_IN2_S;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return COWORK_ETDM_OUT1_S;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return COWORK_ETDM_OUT2_S;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return COWORK_ETDM_OUT3_S;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ return COWORK_ETDM_IN1_M;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return COWORK_ETDM_IN2_M;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return COWORK_ETDM_OUT1_M;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return COWORK_ETDM_OUT2_M;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return COWORK_ETDM_OUT3_M;
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+static int etdm_cowork_sync_sel(int id)
+{
+ switch (id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ return ETDM_SYNC_FROM_IN1;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return ETDM_SYNC_FROM_IN2;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return ETDM_SYNC_FROM_OUT1;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return ETDM_SYNC_FROM_OUT2;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return ETDM_SYNC_FROM_OUT3;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt8188_etdm_sync_mode_slv(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned int reg = 0;
+ unsigned int mask;
+ unsigned int val;
+ int cowork_source_sel;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ cowork_source_sel = etdm_cowork_slv_sel(etdm_data->cowork_source_id,
+ true);
+ if (cowork_source_sel < 0)
+ return cowork_source_sel;
+
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ reg = ETDM_COWORK_CON1;
+ mask = ETDM_IN1_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN1_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_IN2_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN2_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ reg = ETDM_COWORK_CON0;
+ mask = ETDM_OUT1_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT1_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT2_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT2_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT3_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT3_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int mt8188_etdm_sync_mode_mst(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ unsigned int reg = 0;
+ unsigned int mask;
+ unsigned int val;
+ int cowork_source_sel;
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ cowork_source_sel = etdm_cowork_sync_sel(etdm_data->cowork_source_id);
+ if (cowork_source_sel < 0)
+ return cowork_source_sel;
+
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ reg = ETDM_COWORK_CON1;
+ mask = ETDM_IN1_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN1_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_IN2_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN2_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ reg = ETDM_COWORK_CON0;
+ mask = ETDM_OUT1_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT1_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT2_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT2_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT3_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT3_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ default:
+ return 0;
+ }
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_SYNC_MODE);
+
+ return 0;
+}
+
+static int mt8188_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ if (etdm_data->cowork_source_id == COWORK_ETDM_NONE)
+ return 0;
+
+ if (etdm_data->slave_mode)
+ mt8188_etdm_sync_mode_slv(afe, dai_id);
+ else
+ mt8188_etdm_sync_mode_mst(afe, dai_id);
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_etdm_fifo_mode(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ unsigned int mode = 0;
+ unsigned int reg = 0;
+ unsigned int val = 0;
+ unsigned int mask = (ETDM_IN_AFIFO_MODE_MASK | ETDM_IN_USE_AFIFO);
+
+ if (rate != 0)
+ mode = mt8188_afe_fs_timing(rate);
+
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ reg = ETDM_IN1_AFIFO_CON;
+ if (rate == 0)
+ mode = MT8188_ETDM_IN1_1X_EN;
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ reg = ETDM_IN2_AFIFO_CON;
+ if (rate == 0)
+ mode = MT8188_ETDM_IN2_1X_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (mode | ETDM_IN_USE_AFIFO);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ return 0;
+}
+
+static int mtk_dai_etdm_in_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int data_mode;
+ unsigned int lrck_width;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int ret;
+ int i;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ data_mode = etdm_data->data_mode;
+ lrck_width = etdm_data->lrck_width;
+
+ dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
+ __func__, rate, channels, dai_id);
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ /* afifo */
+ if (slave_mode)
+ mtk_dai_etdm_fifo_mode(afe, dai_id, 0);
+ else
+ mtk_dai_etdm_fifo_mode(afe, dai_id, rate);
+
+ /* con1 */
+ if (lrck_width > 0) {
+ mask |= (ETDM_IN_CON1_LRCK_AUTO_MODE |
+ ETDM_IN_CON1_LRCK_WIDTH_MASK);
+ val |= FIELD_PREP(ETDM_IN_CON1_LRCK_WIDTH_MASK, lrck_width - 1);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con2 */
+ if (!slave_mode) {
+ mask |= ETDM_IN_CON2_UPDATE_GAP_MASK;
+ if (rate == 352800 || rate == 384000)
+ val |= FIELD_PREP(ETDM_IN_CON2_UPDATE_GAP_MASK, 4);
+ else
+ val |= FIELD_PREP(ETDM_IN_CON2_UPDATE_GAP_MASK, 3);
+ }
+ mask |= (ETDM_IN_CON2_MULTI_IP_2CH_MODE |
+ ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK);
+ if (data_mode == MTK_DAI_ETDM_DATA_MULTI_PIN) {
+ val |= ETDM_IN_CON2_MULTI_IP_2CH_MODE |
+ FIELD_PREP(ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK, channels - 1);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con2, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con3 */
+ mask |= ETDM_IN_CON3_DISABLE_OUT_MASK;
+ for (i = 0; i < channels; i += 2) {
+ if (etdm_data->in_disable_ch[i] &&
+ etdm_data->in_disable_ch[i + 1])
+ val |= ETDM_IN_CON3_DISABLE_OUT(i >> 1);
+ }
+ if (!slave_mode) {
+ mask |= ETDM_IN_CON3_FS_MASK;
+ val |= FIELD_PREP(ETDM_IN_CON3_FS_MASK, get_etdm_fs_timing(rate));
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con3, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con4 */
+ mask |= (ETDM_IN_CON4_MASTER_LRCK_INV | ETDM_IN_CON4_MASTER_BCK_INV |
+ ETDM_IN_CON4_SLAVE_LRCK_INV | ETDM_IN_CON4_SLAVE_BCK_INV);
+ if (slave_mode) {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_IN_CON4_SLAVE_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_IN_CON4_SLAVE_BCK_INV;
+ } else {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_IN_CON4_MASTER_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_IN_CON4_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con5 */
+ mask |= ETDM_IN_CON5_LR_SWAP_MASK;
+ mask |= ETDM_IN_CON5_ENABLE_ODD_MASK;
+ for (i = 0; i < channels; i += 2) {
+ if (etdm_data->in_disable_ch[i] &&
+ !etdm_data->in_disable_ch[i + 1]) {
+ val |= ETDM_IN_CON5_LR_SWAP(i >> 1);
+ val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1);
+ } else if (!etdm_data->in_disable_ch[i] &&
+ etdm_data->in_disable_ch[i + 1]) {
+ val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1);
+ }
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val);
+ return 0;
+}
+
+static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int lrck_width;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int fs = 0;
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ lrck_width = etdm_data->lrck_width;
+
+ dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
+ __func__, rate, channels, dai_id);
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ /* con0 */
+ mask = ETDM_OUT_CON0_RELATCH_DOMAIN_MASK;
+ val = FIELD_PREP(ETDM_OUT_CON0_RELATCH_DOMAIN_MASK,
+ ETDM_RELATCH_TIMING_A1A2SYS);
+ regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con1 */
+ if (lrck_width > 0) {
+ mask |= (ETDM_OUT_CON1_LRCK_AUTO_MODE |
+ ETDM_OUT_CON1_LRCK_WIDTH_MASK);
+ val |= FIELD_PREP(ETDM_OUT_CON1_LRCK_WIDTH_MASK, lrck_width - 1);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ if (!slave_mode) {
+ /* con4 */
+ mask |= ETDM_OUT_CON4_FS_MASK;
+ val |= FIELD_PREP(ETDM_OUT_CON4_FS_MASK, get_etdm_fs_timing(rate));
+ }
+
+ mask |= ETDM_OUT_CON4_RELATCH_EN_MASK;
+ if (dai_id == MT8188_AFE_IO_ETDM1_OUT)
+ fs = MT8188_ETDM_OUT1_1X_EN;
+ else if (dai_id == MT8188_AFE_IO_ETDM2_OUT)
+ fs = MT8188_ETDM_OUT2_1X_EN;
+
+ val |= FIELD_PREP(ETDM_OUT_CON4_RELATCH_EN_MASK, fs);
+
+ regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con5 */
+ mask |= (ETDM_OUT_CON5_MASTER_LRCK_INV | ETDM_OUT_CON5_MASTER_BCK_INV |
+ ETDM_OUT_CON5_SLAVE_LRCK_INV | ETDM_OUT_CON5_SLAVE_BCK_INV);
+ if (slave_mode) {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_OUT_CON5_SLAVE_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_OUT_CON5_SLAVE_BCK_INV;
+ } else {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_OUT_CON5_MASTER_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_OUT_CON5_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ unsigned int bit_width,
+ int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int etdm_channels;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ unsigned int bck;
+ unsigned int wlen = get_etdm_wlen(bit_width);
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ etdm_data->rate = rate;
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, slv %u\n",
+ __func__, etdm_data->format, etdm_data->data_mode,
+ etdm_data->lrck_inv, etdm_data->lrck_width, etdm_data->bck_inv,
+ etdm_data->slave_mode);
+ dev_dbg(afe->dev, "%s rate %u channels %u bitwidth %u, id %d\n",
+ __func__, rate, channels, bit_width, dai_id);
+
+ etdm_channels = (etdm_data->data_mode == MTK_DAI_ETDM_DATA_ONE_PIN) ?
+ get_etdm_ch_fixup(channels) : 2;
+
+ bck = rate * etdm_channels * wlen;
+ if (bck > MT8188_ETDM_NORMAL_MAX_BCK_RATE) {
+ dev_err(afe->dev, "%s bck rate %u not support\n",
+ __func__, bck);
+ return -EINVAL;
+ }
+
+ /* con0 */
+ mask |= ETDM_CON0_BIT_LEN_MASK;
+ val |= FIELD_PREP(ETDM_CON0_BIT_LEN_MASK, bit_width - 1);
+ mask |= ETDM_CON0_WORD_LEN_MASK;
+ val |= FIELD_PREP(ETDM_CON0_WORD_LEN_MASK, wlen - 1);
+ mask |= ETDM_CON0_FORMAT_MASK;
+ val |= FIELD_PREP(ETDM_CON0_FORMAT_MASK, etdm_data->format);
+ mask |= ETDM_CON0_CH_NUM_MASK;
+ val |= FIELD_PREP(ETDM_CON0_CH_NUM_MASK, etdm_channels - 1);
+
+ mask |= ETDM_CON0_SLAVE_MODE;
+ if (slave_mode) {
+ if (dai_id == MT8188_AFE_IO_ETDM1_OUT) {
+ dev_err(afe->dev, "%s id %d only support master mode\n",
+ __func__, dai_id);
+ return -EINVAL;
+ }
+ val |= ETDM_CON0_SLAVE_MODE;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val);
+
+ if (get_etdm_dir(dai_id) == ETDM_IN)
+ mtk_dai_etdm_in_configure(afe, rate, channels, dai_id);
+ else
+ mtk_dai_etdm_out_configure(afe, rate, channels, dai_id);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ unsigned int channels = params_channels(params);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int ret;
+ int i;
+
+ dev_dbg(afe->dev, "%s '%s' period %u-%u\n",
+ __func__, snd_pcm_stream_str(substream),
+ params_period_size(params), params_periods(params));
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!is_valid_etdm_dai(mst_dai_id))
+ return -EINVAL;
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+ if (mst_etdm_data->slots)
+ channels = mst_etdm_data->slots;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, mst_dai_id);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, slv_dai_id);
+ if (ret)
+ return ret;
+
+ ret = mt8188_etdm_sync_mode_configure(afe, slv_dai_id);
+ if (ret)
+ return ret;
+ }
+ } else {
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ mst_etdm_data = afe_priv->dai_priv[dai->id];
+ if (mst_etdm_data->slots)
+ channels = mst_etdm_data->slots;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, dai->id);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int apll_rate;
+ int apll;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ if (freq == 0) {
+ etdm_data->mclk_freq = freq;
+ return 0;
+ }
+
+ if (etdm_data->mclk_fixed_apll == 0)
+ apll = mt8188_afe_get_default_mclk_source_by_rate(freq);
+ else
+ apll = etdm_data->mclk_apll;
+
+ apll_rate = mt8188_afe_get_mclk_source_rate(afe, apll);
+
+ if (freq > apll_rate) {
+ dev_err(afe->dev, "freq %d > apll rate %d\n", freq, apll_rate);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_err(afe->dev, "APLL%d cannot generate freq Hz\n", apll);
+ return -EINVAL;
+ }
+
+ if (etdm_data->mclk_fixed_apll == 0)
+ etdm_data->mclk_apll = apll;
+ etdm_data->mclk_freq = freq;
+
+ return 0;
+}
+
+static int mtk_dai_etdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
+ __func__, dai->id, freq, dir);
+ if (is_cowork_mode(dai))
+ dai_id = get_etdm_cowork_master_id(dai);
+ else
+ dai_id = dai->id;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ etdm_data->mclk_dir = dir;
+ return mtk_dai_etdm_cal_mclk(afe, freq, dai_id);
+}
+
+static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ if (is_cowork_mode(dai))
+ dai_id = get_etdm_cowork_master_id(dai);
+ else
+ dai_id = dai->id;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(dai->dev, "%s id %d slot_width %d\n",
+ __func__, dai->id, slot_width);
+
+ etdm_data->slots = slots;
+ etdm_data->lrck_width = slot_width;
+ return 0;
+}
+
+static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_RJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ etdm_data->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ etdm_data->slave_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int mtk_dai_get_dptx_ch_en(unsigned int channel)
+{
+ switch (channel) {
+ case 1 ... 2:
+ return AFE_DPTX_CON_CH_EN_2CH;
+ case 3 ... 4:
+ return AFE_DPTX_CON_CH_EN_4CH;
+ case 5 ... 6:
+ return AFE_DPTX_CON_CH_EN_6CH;
+ case 7 ... 8:
+ return AFE_DPTX_CON_CH_EN_8CH;
+ default:
+ return AFE_DPTX_CON_CH_EN_2CH;
+ }
+}
+
+static unsigned int mtk_dai_get_dptx_ch(unsigned int ch)
+{
+ return (ch > 2) ?
+ AFE_DPTX_CON_CH_NUM_8CH : AFE_DPTX_CON_CH_NUM_2CH;
+}
+
+static unsigned int mtk_dai_get_dptx_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ AFE_DPTX_CON_16BIT : AFE_DPTX_CON_24BIT;
+}
+
+static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ int width = snd_pcm_format_physical_width(format);
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ /* dptx configure */
+ if (dai->id == MT8188_AFE_IO_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_CH_EN_MASK,
+ mtk_dai_get_dptx_ch_en(channels));
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_CH_NUM_MASK,
+ mtk_dai_get_dptx_ch(channels));
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_16BIT_MASK,
+ mtk_dai_get_dptx_wlen(format));
+
+ if (mtk_dai_get_dptx_ch(channels) == AFE_DPTX_CON_CH_NUM_8CH) {
+ etdm_data->data_mode = MTK_DAI_ETDM_DATA_ONE_PIN;
+ channels = 8;
+ } else {
+ channels = 2;
+ }
+ } else {
+ etdm_data->data_mode = MTK_DAI_ETDM_DATA_MULTI_PIN;
+ }
+
+ return mtk_dai_etdm_configure(afe, rate, channels, width, dai->id);
+}
+
+static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id,
+ unsigned int freq,
+ int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
+ __func__, dai->id, freq, dir);
+
+ etdm_data->mclk_dir = dir;
+ return mtk_dai_etdm_cal_mclk(afe, freq, dai->id);
+}
+
+static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
+ .hw_params = mtk_dai_etdm_hw_params,
+ .set_sysclk = mtk_dai_etdm_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+ .set_tdm_slot = mtk_dai_etdm_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = {
+ .hw_params = mtk_dai_hdmitx_dptx_hw_params,
+ .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
+ {
+ .name = "DPTX",
+ .id = MT8188_AFE_IO_DPTX,
+ .playback = {
+ .stream_name = "DPTX",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_hdmitx_dptx_ops,
+ },
+ {
+ .name = "ETDM1_IN",
+ .id = MT8188_AFE_IO_ETDM1_IN,
+ .capture = {
+ .stream_name = "ETDM1_IN",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM2_IN",
+ .id = MT8188_AFE_IO_ETDM2_IN,
+ .capture = {
+ .stream_name = "ETDM2_IN",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM1_OUT",
+ .id = MT8188_AFE_IO_ETDM1_OUT,
+ .playback = {
+ .stream_name = "ETDM1_OUT",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM2_OUT",
+ .id = MT8188_AFE_IO_ETDM2_OUT,
+ .playback = {
+ .stream_name = "ETDM2_OUT",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM3_OUT",
+ .id = MT8188_AFE_IO_ETDM3_OUT,
+ .playback = {
+ .stream_name = "ETDM3_OUT",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_hdmitx_dptx_ops,
+ },
+};
+
+static void mt8188_etdm_update_sync_info(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct mtk_dai_etdm_priv *mst_data;
+ int mst_dai_id;
+ int i;
+
+ for (i = MT8188_AFE_IO_ETDM_START; i < MT8188_AFE_IO_ETDM_END; i++) {
+ etdm_data = afe_priv->dai_priv[i];
+ if (etdm_data->cowork_source_id != COWORK_ETDM_NONE) {
+ mst_dai_id = etdm_data->cowork_source_id;
+ mst_data = afe_priv->dai_priv[mst_dai_id];
+ if (mst_data->cowork_source_id != COWORK_ETDM_NONE)
+ dev_err(afe->dev, "%s [%d] wrong sync source\n",
+ __func__, i);
+ mst_data->cowork_slv_id[mst_data->cowork_slv_count] = i;
+ mst_data->cowork_slv_count++;
+ }
+ }
+}
+
+static void mt8188_dai_etdm_parse_of(struct mtk_base_afe *afe)
+{
+ const struct device_node *of_node = afe->dev->of_node;
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ char prop[48];
+ u8 disable_chn[MT8188_ETDM_MAX_CHANNELS];
+ int max_chn = MT8188_ETDM_MAX_CHANNELS;
+ unsigned int sync_id;
+ u32 sel;
+ int ret;
+ int dai_id;
+ int i, j;
+ struct {
+ const char *name;
+ const unsigned int sync_id;
+ } of_afe_etdms[MT8188_AFE_IO_ETDM_NUM] = {
+ {"etdm-in1", ETDM_SYNC_FROM_IN1},
+ {"etdm-in2", ETDM_SYNC_FROM_IN2},
+ {"etdm-out1", ETDM_SYNC_FROM_OUT1},
+ {"etdm-out2", ETDM_SYNC_FROM_OUT2},
+ {"etdm-out3", ETDM_SYNC_FROM_OUT3},
+ };
+
+ for (i = 0; i < MT8188_AFE_IO_ETDM_NUM; i++) {
+ dai_id = ETDM_TO_DAI_ID(i);
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ snprintf(prop, sizeof(prop), "mediatek,%s-multi-pin-mode",
+ of_afe_etdms[i].name);
+
+ etdm_data->data_mode = of_property_read_bool(of_node, prop);
+
+ snprintf(prop, sizeof(prop), "mediatek,%s-cowork-source",
+ of_afe_etdms[i].name);
+
+ ret = of_property_read_u32(of_node, prop, &sel);
+ if (ret == 0) {
+ if (sel >= MT8188_AFE_IO_ETDM_NUM) {
+ dev_err(afe->dev, "%s invalid id=%d\n",
+ __func__, sel);
+ etdm_data->cowork_source_id = COWORK_ETDM_NONE;
+ } else {
+ sync_id = of_afe_etdms[sel].sync_id;
+ etdm_data->cowork_source_id =
+ sync_to_dai_id(sync_id);
+ }
+ } else {
+ etdm_data->cowork_source_id = COWORK_ETDM_NONE;
+ }
+ }
+
+ /* etdm in only */
+ for (i = 0; i < 2; i++) {
+ dai_id = ETDM_TO_DAI_ID(i);
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ snprintf(prop, sizeof(prop), "mediatek,%s-chn-disabled",
+ of_afe_etdms[i].name);
+
+ ret = of_property_read_variable_u8_array(of_node, prop,
+ disable_chn,
+ 1, max_chn);
+ if (ret < 0)
+ continue;
+
+ for (j = 0; j < ret; j++) {
+ if (disable_chn[j] >= MT8188_ETDM_MAX_CHANNELS)
+ dev_err(afe->dev, "%s [%d] invalid chn %u\n",
+ __func__, j, disable_chn[j]);
+ else
+ etdm_data->in_disable_ch[disable_chn[j]] = true;
+ }
+ }
+ mt8188_etdm_update_sync_info(afe);
+}
+
+static int init_etdm_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int i;
+
+ for (i = MT8188_AFE_IO_ETDM_START; i < MT8188_AFE_IO_ETDM_END; i++) {
+ etdm_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_etdm_priv),
+ GFP_KERNEL);
+ if (!etdm_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[i] = etdm_priv;
+ }
+
+ afe_priv->dai_priv[MT8188_AFE_IO_DPTX] =
+ afe_priv->dai_priv[MT8188_AFE_IO_ETDM3_OUT];
+
+ mt8188_dai_etdm_parse_of(afe);
+ return 0;
+}
+
+int mt8188_dai_etdm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_etdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
+
+ dai->dapm_widgets = mtk_dai_etdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
+ dai->dapm_routes = mtk_dai_etdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
+ dai->controls = mtk_dai_etdm_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_etdm_controls);
+
+ return init_etdm_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c
new file mode 100644
index 000000000000..8ca7cc75e21d
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI PCM I/F Control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+
+enum {
+ MTK_DAI_PCM_FMT_I2S,
+ MTK_DAI_PCM_FMT_EIAJ,
+ MTK_DAI_PCM_FMT_MODEA,
+ MTK_DAI_PCM_FMT_MODEB,
+};
+
+enum {
+ MTK_DAI_PCM_CLK_A1SYS,
+ MTK_DAI_PCM_CLK_A2SYS,
+ MTK_DAI_PCM_CLK_26M_48K,
+ MTK_DAI_PCM_CLK_26M_441K,
+};
+
+struct mtk_dai_pcm_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+struct mtk_dai_pcmif_priv {
+ unsigned int slave_mode;
+ unsigned int lrck_inv;
+ unsigned int bck_inv;
+ unsigned int format;
+};
+
+static const struct mtk_dai_pcm_rate mtk_dai_pcm_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 16000, .reg_value = 1, },
+ { .rate = 32000, .reg_value = 2, },
+ { .rate = 48000, .reg_value = 3, },
+ { .rate = 11025, .reg_value = 1, },
+ { .rate = 22050, .reg_value = 2, },
+ { .rate = 44100, .reg_value = 3, },
+};
+
+static int mtk_dai_pcm_mode(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_dai_pcm_rates); i++)
+ if (mtk_dai_pcm_rates[i].rate == rate)
+ return mtk_dai_pcm_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new mtk_dai_pcm_o000_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN0, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN0_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_pcm_o001_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN1_2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ SND_SOC_DAPM_MIXER("I002", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I003", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O000", SND_SOC_NOPM, 0, 0,
+ mtk_dai_pcm_o000_mix,
+ ARRAY_SIZE(mtk_dai_pcm_o000_mix)),
+ SND_SOC_DAPM_MIXER("O001", SND_SOC_NOPM, 0, 0,
+ mtk_dai_pcm_o001_mix,
+ ARRAY_SIZE(mtk_dai_pcm_o001_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("PCM1_INPUT"),
+ SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"I002", NULL, "PCM1 Capture"},
+ {"I003", NULL, "PCM1 Capture"},
+
+ {"O000", "I000 Switch", "I000"},
+ {"O001", "I001 Switch", "I001"},
+
+ {"O000", "I070 Switch", "I070"},
+ {"O001", "I071 Switch", "I071"},
+
+ {"PCM1 Playback", NULL, "O000"},
+ {"PCM1 Playback", NULL, "O001"},
+
+ {"PCM1 Playback", NULL, "PCM_1_EN"},
+ {"PCM1 Playback", NULL, "aud_asrc12"},
+ {"PCM1 Playback", NULL, "aud_pcmif"},
+
+ {"PCM1 Capture", NULL, "PCM_1_EN"},
+ {"PCM1 Capture", NULL, "aud_asrc11"},
+ {"PCM1 Capture", NULL, "aud_pcmif"},
+
+ {"PCM1_OUTPUT", NULL, "PCM1 Playback"},
+ {"PCM1 Capture", NULL, "PCM1_INPUT"},
+};
+
+static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv = NULL;
+ unsigned int slave_mode;
+ unsigned int lrck_inv;
+ unsigned int bck_inv;
+ unsigned int fmt;
+ unsigned int bit_width = dai->symmetric_sample_bits;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int fs = 0;
+ int mode = 0;
+
+ if (dai->id < 0)
+ return -EINVAL;
+
+ pcmif_priv = afe_priv->dai_priv[dai->id];
+ slave_mode = pcmif_priv->slave_mode;
+ lrck_inv = pcmif_priv->lrck_inv;
+ bck_inv = pcmif_priv->bck_inv;
+ fmt = pcmif_priv->format;
+
+ /* sync freq mode */
+ fs = mt8188_afe_fs_timing(runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ val |= FIELD_PREP(PCM_INTF_CON2_SYNC_FREQ_MODE_MASK, fs);
+ mask |= PCM_INTF_CON2_SYNC_FREQ_MODE_MASK;
+
+ /* clk domain sel */
+ if (runtime->rate % 8000)
+ val |= FIELD_PREP(PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK,
+ MTK_DAI_PCM_CLK_26M_441K);
+ else
+ val |= FIELD_PREP(PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK,
+ MTK_DAI_PCM_CLK_26M_48K);
+ mask |= PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON2, mask, val);
+
+ val = 0;
+ mask = 0;
+
+ /* pcm mode */
+ mode = mtk_dai_pcm_mode(runtime->rate);
+ if (mode < 0)
+ return -EINVAL;
+
+ val |= FIELD_PREP(PCM_INTF_CON1_PCM_MODE_MASK, mode);
+ mask |= PCM_INTF_CON1_PCM_MODE_MASK;
+
+ /* pcm format */
+ val |= FIELD_PREP(PCM_INTF_CON1_PCM_FMT_MASK, fmt);
+ mask |= PCM_INTF_CON1_PCM_FMT_MASK;
+
+ /* pcm sync length */
+ if (fmt == MTK_DAI_PCM_FMT_MODEA ||
+ fmt == MTK_DAI_PCM_FMT_MODEB)
+ val |= FIELD_PREP(PCM_INTF_CON1_SYNC_LENGTH_MASK, 1);
+ else
+ val |= FIELD_PREP(PCM_INTF_CON1_SYNC_LENGTH_MASK, bit_width);
+ mask |= PCM_INTF_CON1_SYNC_LENGTH_MASK;
+
+ /* pcm bits, word length */
+ if (bit_width > 16) {
+ val |= PCM_INTF_CON1_PCM_24BIT;
+ val |= PCM_INTF_CON1_PCM_WLEN_64BCK;
+ } else {
+ val |= PCM_INTF_CON1_PCM_16BIT;
+ val |= PCM_INTF_CON1_PCM_WLEN_32BCK;
+ }
+ mask |= PCM_INTF_CON1_PCM_BIT_MASK;
+ mask |= PCM_INTF_CON1_PCM_WLEN_MASK;
+
+ /* master/slave */
+ if (!slave_mode) {
+ val |= PCM_INTF_CON1_PCM_MASTER;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_OUT_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_OUT_INV;
+ mask |= PCM_INTF_CON1_CLK_OUT_INV_MASK;
+ } else {
+ val |= PCM_INTF_CON1_PCM_SLAVE;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_IN_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_IN_INV;
+ mask |= PCM_INTF_CON1_CLK_IN_INV_MASK;
+
+ // TODO: add asrc setting for slave mode
+ }
+ mask |= PCM_INTF_CON1_PCM_M_S_MASK;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1, mask, val);
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (snd_soc_dai_get_widget_playback(dai)->active ||
+ snd_soc_dai_get_widget_capture(dai)->active)
+ return 0;
+
+ return mtk_dai_pcm_configure(substream, dai);
+}
+
+static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv = NULL;
+
+ dev_dbg(dai->dev, "%s fmt 0x%x\n", __func__, fmt);
+
+ if (dai->id < 0)
+ return -EINVAL;
+
+ pcmif_priv = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_MODEA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_MODEB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ pcmif_priv->bck_inv = 0;
+ pcmif_priv->lrck_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ pcmif_priv->bck_inv = 0;
+ pcmif_priv->lrck_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ pcmif_priv->bck_inv = 1;
+ pcmif_priv->lrck_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ pcmif_priv->bck_inv = 1;
+ pcmif_priv->lrck_inv = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ pcmif_priv->slave_mode = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ pcmif_priv->slave_mode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .prepare = mtk_dai_pcm_prepare,
+ .set_fmt = mtk_dai_pcm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM1",
+ .id = MT8188_AFE_IO_PCM,
+ .playback = {
+ .stream_name = "PCM1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+static int init_pcmif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv;
+
+ pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_pcmif_priv),
+ GFP_KERNEL);
+ if (!pcmif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8188_AFE_IO_PCM] = pcmif_priv;
+ return 0;
+}
+
+int mt8188_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+
+ return init_pcmif_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
new file mode 100644
index 000000000000..55ebac0c3cef
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -0,0 +1,1478 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8188-mt6359.c -- MT8188-MT6359 ALSA SoC machine driver
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "mt8188-afe-common.h"
+#include "../../codecs/nau8825.h"
+#include "../../codecs/mt6359.h"
+#include "../../codecs/mt6359-accdet.h"
+#include "../../codecs/rt5682.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-soundcard-driver.h"
+#include "../common/mtk-dsp-sof-common.h"
+#include "../common/mtk-soc-card.h"
+
+#define CKSYS_AUD_TOP_CFG 0x032c
+ #define RG_TEST_ON BIT(0)
+ #define RG_TEST_TYPE BIT(2)
+#define CKSYS_AUD_TOP_MON 0x0330
+ #define TEST_MISO_COUNT_1 GENMASK(3, 0)
+ #define TEST_MISO_COUNT_2 GENMASK(7, 4)
+ #define TEST_MISO_DONE_1 BIT(28)
+ #define TEST_MISO_DONE_2 BIT(29)
+
+#define NAU8825_HS_PRESENT BIT(0)
+#define RT5682S_HS_PRESENT BIT(1)
+#define ES8326_HS_PRESENT BIT(2)
+#define MAX98390_TWO_AMP BIT(3)
+/*
+ * Maxim MAX98390
+ */
+#define MAX98390_CODEC_DAI "max98390-aif1"
+#define MAX98390_DEV0_NAME "max98390.0-0038" /* rear right */
+#define MAX98390_DEV1_NAME "max98390.0-0039" /* rear left */
+#define MAX98390_DEV2_NAME "max98390.0-003a" /* front right */
+#define MAX98390_DEV3_NAME "max98390.0-003b" /* front left */
+
+/*
+ * Nau88l25
+ */
+#define NAU8825_CODEC_DAI "nau8825-hifi"
+
+/*
+ * ES8326
+ */
+#define ES8326_CODEC_DAI "ES8326 HiFi"
+
+#define SOF_DMA_DL2 "SOF_DMA_DL2"
+#define SOF_DMA_DL3 "SOF_DMA_DL3"
+#define SOF_DMA_UL4 "SOF_DMA_UL4"
+#define SOF_DMA_UL5 "SOF_DMA_UL5"
+
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback6,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback7,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback8,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback10,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback11,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL11")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture4,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture5,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture6,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture8,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture9,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture10,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(dl_src,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DMIC_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DMIC")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(dptx,
+ DAILINK_COMP_ARRAY(COMP_CPU("DPTX")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm1_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm2_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm1_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm2_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm3_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ul_src,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL3,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL4,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL5,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ {
+ .sof_link = "AFE_SOF_DL2",
+ .sof_dma = SOF_DMA_DL2,
+ .stream_dir = SNDRV_PCM_STREAM_PLAYBACK
+ },
+ {
+ .sof_link = "AFE_SOF_DL3",
+ .sof_dma = SOF_DMA_DL3,
+ .stream_dir = SNDRV_PCM_STREAM_PLAYBACK
+ },
+ {
+ .sof_link = "AFE_SOF_UL4",
+ .sof_dma = SOF_DMA_UL4,
+ .stream_dir = SNDRV_PCM_STREAM_CAPTURE
+ },
+ {
+ .sof_link = "AFE_SOF_UL5",
+ .sof_dma = SOF_DMA_UL5,
+ .stream_dir = SNDRV_PCM_STREAM_CAPTURE
+ },
+};
+
+enum mt8188_jacks {
+ MT8188_JACK_HEADSET,
+ MT8188_JACK_DP,
+ MT8188_JACK_HDMI,
+ MT8188_JACK_MAX,
+};
+
+static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = {
+ {
+ .pin = "HDMI",
+ .mask = SND_JACK_AVOUT,
+ },
+};
+
+static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = {
+ {
+ .pin = "DP",
+ .mask = SND_JACK_AVOUT,
+ },
+};
+
+static struct snd_soc_jack_pin nau8825_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_pin mt8188_headset_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new mt8188_dumb_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_dumb_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_dual_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_dual_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_rear_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Rear Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Rear Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_rear_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Rear Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Rear Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("AP DMIC", NULL),
+ SND_SOC_DAPM_SINK("HDMI"),
+ SND_SOC_DAPM_SINK("DP"),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL4, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* dynamic pinctrl */
+ SND_SOC_DAPM_PINCTRL("ETDM_SPK_PIN", "aud_etdm_spk_on", "aud_etdm_spk_off"),
+ SND_SOC_DAPM_PINCTRL("ETDM_HP_PIN", "aud_etdm_hp_on", "aud_etdm_hp_off"),
+ SND_SOC_DAPM_PINCTRL("MTKAIF_PIN", "aud_mtkaif_on", "aud_mtkaif_off"),
+};
+
+static const struct snd_kcontrol_new mt8188_mt6359_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_nau8825_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_nau8825_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
+static const struct snd_soc_dapm_route mt8188_mt6359_routes[] = {
+ /* SOF Uplink */
+ {SOF_DMA_UL4, NULL, "O034"},
+ {SOF_DMA_UL4, NULL, "O035"},
+ {SOF_DMA_UL5, NULL, "O036"},
+ {SOF_DMA_UL5, NULL, "O037"},
+ /* SOF Downlink */
+ {"I070", NULL, SOF_DMA_DL2},
+ {"I071", NULL, SOF_DMA_DL2},
+ {"I020", NULL, SOF_DMA_DL3},
+ {"I021", NULL, SOF_DMA_DL3},
+};
+
+static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_dapm_widget *pin_w = NULL, *w;
+ struct mtk_base_afe *afe;
+ struct mt8188_afe_private *afe_priv;
+ struct mtkaif_param *param;
+ int chosen_phase_1, chosen_phase_2;
+ int prev_cycle_1, prev_cycle_2;
+ u8 test_done_1, test_done_2;
+ int cycle_1, cycle_2;
+ int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_calibration_num_phase;
+ bool mtkaif_calibration_ok;
+ u32 monitor = 0;
+ int counter;
+ int phase;
+ int i;
+
+ if (!cmpnt_afe)
+ return -EINVAL;
+
+ afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ afe_priv = afe->platform_priv;
+ param = &afe_priv->mtkaif_params;
+
+ dev_dbg(afe->dev, "%s(), start\n", __func__);
+
+ param->mtkaif_calibration_ok = false;
+ for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++) {
+ param->mtkaif_chosen_phase[i] = -1;
+ param->mtkaif_phase_cycle[i] = 0;
+ mtkaif_chosen_phase[i] = -1;
+ mtkaif_phase_cycle[i] = 0;
+ }
+
+ if (IS_ERR(afe_priv->topckgen)) {
+ dev_info(afe->dev, "%s() Cannot find topckgen controller\n",
+ __func__);
+ return 0;
+ }
+
+ for_each_card_widgets(rtd->card, w) {
+ if (!strcmp(w->name, "MTKAIF_PIN")) {
+ pin_w = w;
+ break;
+ }
+ }
+
+ if (pin_w)
+ snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU);
+ else
+ dev_dbg(afe->dev, "%s(), no pinmux widget, please check if default on\n", __func__);
+
+ pm_runtime_get_sync(afe->dev);
+ mt6359_mtkaif_calibration_enable(cmpnt_codec);
+
+ /* set test type to synchronizer pulse */
+ regmap_write(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_TYPE);
+ mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
+ mtkaif_calibration_ok = true;
+
+ for (phase = 0;
+ phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok;
+ phase++) {
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ phase, phase, phase);
+
+ regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_ON);
+
+ test_done_1 = 0;
+ test_done_2 = 0;
+
+ cycle_1 = -1;
+ cycle_2 = -1;
+
+ counter = 0;
+ while (!(test_done_1 & test_done_2)) {
+ regmap_read(afe_priv->topckgen,
+ CKSYS_AUD_TOP_MON, &monitor);
+ test_done_1 = FIELD_GET(TEST_MISO_DONE_1, monitor);
+ test_done_2 = FIELD_GET(TEST_MISO_DONE_2, monitor);
+
+ if (test_done_1 == 1)
+ cycle_1 = FIELD_GET(TEST_MISO_COUNT_1, monitor);
+
+ if (test_done_2 == 1)
+ cycle_2 = FIELD_GET(TEST_MISO_COUNT_2, monitor);
+
+ /* handle if never test done */
+ if (++counter > 10000) {
+ dev_err(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n",
+ __func__, cycle_1, cycle_2, monitor);
+ mtkaif_calibration_ok = false;
+ break;
+ }
+ }
+
+ if (phase == 0) {
+ prev_cycle_1 = cycle_1;
+ prev_cycle_2 = cycle_2;
+ }
+
+ if (cycle_1 != prev_cycle_1 &&
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] < 0) {
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] = phase - 1;
+ mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] = prev_cycle_1;
+ }
+
+ if (cycle_2 != prev_cycle_2 &&
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] < 0) {
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] = phase - 1;
+ mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] = prev_cycle_2;
+ }
+
+ regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_ON);
+
+ if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] >= 0 &&
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] >= 0)
+ break;
+ }
+
+ if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_1 = 0;
+ } else {
+ chosen_phase_1 = mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0];
+ }
+
+ if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_2 = 0;
+ } else {
+ chosen_phase_2 = mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1];
+ }
+
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ chosen_phase_1,
+ chosen_phase_2,
+ 0);
+
+ mt6359_mtkaif_calibration_disable(cmpnt_codec);
+ pm_runtime_put(afe->dev);
+
+ param->mtkaif_calibration_ok = mtkaif_calibration_ok;
+ param->mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] = chosen_phase_1;
+ param->mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] = chosen_phase_2;
+
+ for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++)
+ param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
+
+ if (pin_w)
+ snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD);
+
+ dev_dbg(afe->dev, "%s(), end, calibration ok %d\n",
+ __func__, param->mtkaif_calibration_ok);
+
+ return 0;
+}
+
+static int mt8188_mt6359_accdet_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8188_JACK_HEADSET];
+ int ret;
+
+ if (!soc_card_data->accdet)
+ return 0;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack, mt8188_headset_jack_pins,
+ ARRAY_SIZE(mt8188_headset_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack create failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = mt6359_accdet_enable_jack_detect(soc_card_data->accdet, jack);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack enable failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_mt6359_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ /* set mtkaif protocol */
+ mt6359_set_mtkaif_protocol(cmpnt_codec,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
+
+ /* mtkaif calibration */
+ mt8188_mt6359_mtkaif_calibration(rtd);
+
+ mt8188_mt6359_accdet_init(rtd);
+
+ return 0;
+}
+
+enum {
+ DAI_LINK_DL2_FE,
+ DAI_LINK_DL3_FE,
+ DAI_LINK_DL6_FE,
+ DAI_LINK_DL7_FE,
+ DAI_LINK_DL8_FE,
+ DAI_LINK_DL10_FE,
+ DAI_LINK_DL11_FE,
+ DAI_LINK_UL1_FE,
+ DAI_LINK_UL2_FE,
+ DAI_LINK_UL3_FE,
+ DAI_LINK_UL4_FE,
+ DAI_LINK_UL5_FE,
+ DAI_LINK_UL6_FE,
+ DAI_LINK_UL8_FE,
+ DAI_LINK_UL9_FE,
+ DAI_LINK_UL10_FE,
+ DAI_LINK_DL_SRC_BE,
+ DAI_LINK_DMIC_BE,
+ DAI_LINK_DPTX_BE,
+ DAI_LINK_ETDM1_IN_BE,
+ DAI_LINK_ETDM2_IN_BE,
+ DAI_LINK_ETDM1_OUT_BE,
+ DAI_LINK_ETDM2_OUT_BE,
+ DAI_LINK_ETDM3_OUT_BE,
+ DAI_LINK_PCM1_BE,
+ DAI_LINK_UL_SRC_BE,
+ DAI_LINK_REGULAR_LAST = DAI_LINK_UL_SRC_BE,
+ DAI_LINK_SOF_START,
+ DAI_LINK_SOF_DL2_BE = DAI_LINK_SOF_START,
+ DAI_LINK_SOF_DL3_BE,
+ DAI_LINK_SOF_UL4_BE,
+ DAI_LINK_SOF_UL5_BE,
+ DAI_LINK_SOF_END = DAI_LINK_SOF_UL5_BE,
+};
+
+#define DAI_LINK_REGULAR_NUM (DAI_LINK_REGULAR_LAST + 1)
+
+static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8188_dptx_ops = {
+ .hw_params = mt8188_dptx_hw_params,
+};
+
+static int mt8188_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+
+ return 0;
+}
+
+static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8188_JACK_HDMI];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret = 0;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack",
+ SND_JACK_AVOUT, jack,
+ mt8188_hdmi_jack_pins,
+ ARRAY_SIZE(mt8188_hdmi_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8188_JACK_DP];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret = 0;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_AVOUT,
+ jack, mt8188_dp_jack_pins,
+ ARRAY_SIZE(mt8188_dp_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret = 0;
+
+ ret = snd_soc_dapm_new_controls(dapm, mt8188_dumb_spk_widgets,
+ ARRAY_SIZE(mt8188_dumb_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_dumb_spk_controls,
+ ARRAY_SIZE(mt8188_dumb_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_max98390_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int bit_width = params_width(params);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ int i;
+
+ snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0xf, 4, bit_width);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV0_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x3, 4, bit_width);
+
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV1_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x3, 4, bit_width);
+
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV2_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x2, 0x3, 4, bit_width);
+
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV3_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x1, 0x3, 4, bit_width);
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt8188_max98390_ops = {
+ .hw_params = mt8188_max98390_hw_params,
+};
+
+static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ /* add regular speakers dapm route */
+ ret = snd_soc_dapm_new_controls(dapm, mt8188_dual_spk_widgets,
+ ARRAY_SIZE(mt8188_dual_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Left/Right Speaker widget, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_dual_spk_controls,
+ ARRAY_SIZE(mt8188_dual_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Left/Right card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ if (rtd->dai_link->num_codecs <= 2)
+ return 0;
+
+ /* add widgets/controls/dapm for rear speakers */
+ ret = snd_soc_dapm_new_controls(dapm, mt8188_rear_spk_widgets,
+ ARRAY_SIZE(mt8188_rear_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Rear Speaker widget, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_rear_spk_controls,
+ ARRAY_SIZE(mt8188_rear_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Rear card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8188_JACK_HEADSET];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, mt8188_nau8825_widgets,
+ ARRAY_SIZE(mt8188_nau8825_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_nau8825_controls,
+ ARRAY_SIZE(mt8188_nau8825_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ nau8825_jack_pins,
+ ARRAY_SIZE(nau8825_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ if (card_data->flags & ES8326_HS_PRESENT) {
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+ } else {
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static void mt8188_headset_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+
+static int mt8188_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ int clk_freq, ret;
+
+ clk_freq = rate * 2 * bit_width;
+
+ /* Configure clock for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
+ return ret;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8188_nau8825_ops = {
+ .hw_params = mt8188_nau8825_hw_params,
+};
+
+static int mt8188_rt5682s_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_BCLK1,
+ rate * 32, rate * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 128,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8188_rt5682s_i2s_ops = {
+ .hw_params = mt8188_rt5682s_i2s_hw_params,
+};
+
+static int mt8188_sof_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *cmpnt_afe = NULL;
+ struct snd_soc_pcm_runtime *runtime;
+
+ /* find afe component */
+ for_each_card_rtds(rtd->card, runtime) {
+ cmpnt_afe = snd_soc_rtdcom_lookup(runtime, AFE_PCM_NAME);
+ if (cmpnt_afe)
+ break;
+ }
+
+ if (cmpnt_afe && !pm_runtime_active(cmpnt_afe->dev)) {
+ dev_err(rtd->dev, "afe pm runtime is not active!!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8188_sof_be_ops = {
+ .hw_params = mt8188_sof_be_hw_params,
+};
+
+static int mt8188_es8326_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ /* Configure MCLK for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set MCLK %d\n", ret);
+ return ret;
+ }
+
+ /* Configure MCLK for cpu */
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8188_es8326_ops = {
+ .hw_params = mt8188_es8326_hw_params,
+};
+
+static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
+ /* FE */
+ [DAI_LINK_DL2_FE] = {
+ .name = "DL2_FE",
+ .stream_name = "DL2 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ [DAI_LINK_DL3_FE] = {
+ .name = "DL3_FE",
+ .stream_name = "DL3 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ [DAI_LINK_DL6_FE] = {
+ .name = "DL6_FE",
+ .stream_name = "DL6 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(playback6),
+ },
+ [DAI_LINK_DL7_FE] = {
+ .name = "DL7_FE",
+ .stream_name = "DL7 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback7),
+ },
+ [DAI_LINK_DL8_FE] = {
+ .name = "DL8_FE",
+ .stream_name = "DL8 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback8),
+ },
+ [DAI_LINK_DL10_FE] = {
+ .name = "DL10_FE",
+ .stream_name = "DL10 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback10),
+ },
+ [DAI_LINK_DL11_FE] = {
+ .name = "DL11_FE",
+ .stream_name = "DL11 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback11),
+ },
+ [DAI_LINK_UL1_FE] = {
+ .name = "UL1_FE",
+ .stream_name = "UL1 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ [DAI_LINK_UL2_FE] = {
+ .name = "UL2_FE",
+ .stream_name = "UL2 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ [DAI_LINK_UL3_FE] = {
+ .name = "UL3_FE",
+ .stream_name = "UL3 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ [DAI_LINK_UL4_FE] = {
+ .name = "UL4_FE",
+ .stream_name = "UL4 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(capture4),
+ },
+ [DAI_LINK_UL5_FE] = {
+ .name = "UL5_FE",
+ .stream_name = "UL5 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(capture5),
+ },
+ [DAI_LINK_UL6_FE] = {
+ .name = "UL6_FE",
+ .stream_name = "UL6 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture6),
+ },
+ [DAI_LINK_UL8_FE] = {
+ .name = "UL8_FE",
+ .stream_name = "UL8 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture8),
+ },
+ [DAI_LINK_UL9_FE] = {
+ .name = "UL9_FE",
+ .stream_name = "UL9 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture9),
+ },
+ [DAI_LINK_UL10_FE] = {
+ .name = "UL10_FE",
+ .stream_name = "UL10 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture10),
+ },
+ /* BE */
+ [DAI_LINK_DL_SRC_BE] = {
+ .name = "DL_SRC_BE",
+ .no_pcm = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(dl_src),
+ },
+ [DAI_LINK_DMIC_BE] = {
+ .name = "DMIC_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(DMIC_BE),
+ },
+ [DAI_LINK_DPTX_BE] = {
+ .name = "DPTX_BE",
+ .ops = &mt8188_dptx_ops,
+ .be_hw_params_fixup = mt8188_dptx_hw_params_fixup,
+ .no_pcm = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(dptx),
+ },
+ [DAI_LINK_ETDM1_IN_BE] = {
+ .name = "ETDM1_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(etdm1_in),
+ },
+ [DAI_LINK_ETDM2_IN_BE] = {
+ .name = "ETDM2_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(etdm2_in),
+ },
+ [DAI_LINK_ETDM1_OUT_BE] = {
+ .name = "ETDM1_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(etdm1_out),
+ },
+ [DAI_LINK_ETDM2_OUT_BE] = {
+ .name = "ETDM2_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(etdm2_out),
+ },
+ [DAI_LINK_ETDM3_OUT_BE] = {
+ .name = "ETDM3_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(etdm3_out),
+ },
+ [DAI_LINK_PCM1_BE] = {
+ .name = "PCM1_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+ [DAI_LINK_UL_SRC_BE] = {
+ .name = "UL_SRC_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(ul_src),
+ },
+
+ /* SOF BE */
+ [DAI_LINK_SOF_DL2_BE] = {
+ .name = "AFE_SOF_DL2",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8188_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL2),
+ },
+ [DAI_LINK_SOF_DL3_BE] = {
+ .name = "AFE_SOF_DL3",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8188_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL3),
+ },
+ [DAI_LINK_SOF_UL4_BE] = {
+ .name = "AFE_SOF_UL4",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8188_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL4),
+ },
+ [DAI_LINK_SOF_UL5_BE] = {
+ .name = "AFE_SOF_UL5",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8188_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL5),
+ },
+};
+
+static void mt8188_fixup_controls(struct snd_soc_card *card)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_kcontrol *kctl;
+
+ if (card_data->flags & (NAU8825_HS_PRESENT | RT5682S_HS_PRESENT | ES8326_HS_PRESENT)) {
+ struct snd_soc_dapm_widget *w, *next_w;
+
+ for_each_card_widgets_safe(card, w, next_w) {
+ if (strcmp(w->name, "Headphone"))
+ continue;
+
+ snd_soc_dapm_free_widget(w);
+ }
+
+ kctl = snd_ctl_find_id_mixer(card->snd_card, "Headphone Switch");
+ if (kctl)
+ snd_ctl_remove(card->snd_card, kctl);
+ else
+ dev_warn(card->dev, "Cannot find ctl : Headphone Switch\n");
+ }
+}
+
+static struct snd_soc_card mt8188_mt6359_soc_card = {
+ .owner = THIS_MODULE,
+ .dai_link = mt8188_mt6359_dai_links,
+ .num_links = ARRAY_SIZE(mt8188_mt6359_dai_links),
+ .dapm_widgets = mt8188_mt6359_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets),
+ .dapm_routes = mt8188_mt6359_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8188_mt6359_routes),
+ .controls = mt8188_mt6359_controls,
+ .num_controls = ARRAY_SIZE(mt8188_mt6359_controls),
+ .fixup_controls = mt8188_fixup_controls,
+};
+
+static int mt8188_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = soc_card_data->card_data->card;
+ struct snd_soc_dai_link *dai_link;
+ bool init_mt6359 = false;
+ bool init_es8326 = false;
+ bool init_nau8825 = false;
+ bool init_rt5682s = false;
+ bool init_max98390 = false;
+ bool init_dumb = false;
+ int i;
+
+ if (legacy)
+ return -EINVAL;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "DPTX_BE") == 0) {
+ if (dai_link->num_codecs &&
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
+ dai_link->init = mt8188_dptx_codec_init;
+ } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ if (dai_link->num_codecs &&
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
+ dai_link->init = mt8188_hdmi_codec_init;
+ } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC_BE") == 0) {
+ if (!init_mt6359) {
+ dai_link->init = mt8188_mt6359_init;
+ init_mt6359 = true;
+ }
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM1_IN_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ if (!dai_link->num_codecs)
+ continue;
+
+ if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) {
+ /*
+ * The TDM protocol settings with fixed 4 slots are defined in
+ * mt8188_max98390_ops. Two amps is I2S mode,
+ * SOC and codec don't require TDM settings.
+ */
+ if (!(card_data->flags & MAX98390_TWO_AMP)) {
+ dai_link->ops = &mt8188_max98390_ops;
+ }
+ if (!init_max98390) {
+ dai_link->init = mt8188_max98390_codec_init;
+ init_max98390 = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) {
+ dai_link->ops = &mt8188_nau8825_ops;
+ if (!init_nau8825) {
+ dai_link->init = mt8188_headset_codec_init;
+ dai_link->exit = mt8188_headset_codec_exit;
+ init_nau8825 = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5682S_CODEC_DAI)) {
+ dai_link->ops = &mt8188_rt5682s_i2s_ops;
+ if (!init_rt5682s) {
+ dai_link->init = mt8188_headset_codec_init;
+ dai_link->exit = mt8188_headset_codec_exit;
+ init_rt5682s = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, ES8326_CODEC_DAI)) {
+ dai_link->ops = &mt8188_es8326_ops;
+ if (!init_es8326) {
+ dai_link->init = mt8188_headset_codec_init;
+ dai_link->exit = mt8188_headset_codec_exit;
+ init_es8326 = true;
+ }
+ } else {
+ if (!snd_soc_dlc_is_dummy(dai_link->codecs)) {
+ if (!init_dumb) {
+ dai_link->init = mt8188_dumb_amp_init;
+ init_dumb = true;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct mtk_sof_priv mt8188_sof_priv = {
+ .conn_streams = g_sof_conn_streams,
+ .num_streams = ARRAY_SIZE(g_sof_conn_streams),
+};
+
+static const struct mtk_soundcard_pdata mt8188_evb_card = {
+ .card_name = "mt8188_mt6359",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8188_mt6359_soc_card,
+ .num_jacks = MT8188_JACK_MAX,
+ },
+ .sof_priv = &mt8188_sof_priv,
+ .soc_probe = mt8188_mt6359_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8188_nau8825_card = {
+ .card_name = "mt8188_nau8825",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8188_mt6359_soc_card,
+ .num_jacks = MT8188_JACK_MAX,
+ .flags = NAU8825_HS_PRESENT
+ },
+ .sof_priv = &mt8188_sof_priv,
+ .soc_probe = mt8188_mt6359_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8188_rt5682s_card = {
+ .card_name = "mt8188_rt5682s",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8188_mt6359_soc_card,
+ .num_jacks = MT8188_JACK_MAX,
+ .flags = RT5682S_HS_PRESENT | MAX98390_TWO_AMP
+ },
+ .sof_priv = &mt8188_sof_priv,
+ .soc_probe = mt8188_mt6359_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8188_es8326_card = {
+ .card_name = "mt8188_es8326",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8188_mt6359_soc_card,
+ .num_jacks = MT8188_JACK_MAX,
+ .flags = ES8326_HS_PRESENT | MAX98390_TWO_AMP
+ },
+ .sof_priv = &mt8188_sof_priv,
+ .soc_probe = mt8188_mt6359_soc_card_probe,
+};
+
+static const struct of_device_id mt8188_mt6359_dt_match[] = {
+ { .compatible = "mediatek,mt8188-mt6359-evb", .data = &mt8188_evb_card, },
+ { .compatible = "mediatek,mt8188-nau8825", .data = &mt8188_nau8825_card, },
+ { .compatible = "mediatek,mt8188-rt5682s", .data = &mt8188_rt5682s_card, },
+ { .compatible = "mediatek,mt8188-es8326", .data = &mt8188_es8326_card, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match);
+
+static struct platform_driver mt8188_mt6359_driver = {
+ .driver = {
+ .name = "mt8188_mt6359",
+ .of_match_table = mt8188_mt6359_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+
+module_platform_driver(mt8188_mt6359_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8188-MT6359 ALSA SoC machine driver");
+MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mt8188 mt6359 soc card");
diff --git a/sound/soc/mediatek/mt8188/mt8188-reg.h b/sound/soc/mediatek/mt8188/mt8188-reg.h
new file mode 100644
index 000000000000..2e9c65de249d
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-reg.h
@@ -0,0 +1,3195 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-reg.h -- MediaTek 8188 audio driver reg definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_REG_H_
+#define _MT8188_REG_H_
+
+#define AUDIO_TOP_CON0 (0x0000)
+#define AUDIO_TOP_CON1 (0x0004)
+#define AUDIO_TOP_CON2 (0x0008)
+#define AUDIO_TOP_CON3 (0x000c)
+#define AUDIO_TOP_CON4 (0x0010)
+#define AUDIO_TOP_CON5 (0x0014)
+#define AUDIO_TOP_CON6 (0x0018)
+#define AFE_MAS_HADDR_MSB (0x0020)
+#define AFE_MEMIF_ONE_HEART (0x0024)
+#define AFE_MUX_SEL_CFG (0x0044)
+#define PWR1_ASM_CON1 (0x0108)
+#define ASYS_IRQ_CONFIG (0x0110)
+#define ASYS_IRQ1_CON (0x0114)
+#define ASYS_IRQ2_CON (0x0118)
+#define ASYS_IRQ3_CON (0x011c)
+#define ASYS_IRQ4_CON (0x0120)
+#define ASYS_IRQ5_CON (0x0124)
+#define ASYS_IRQ6_CON (0x0128)
+#define ASYS_IRQ7_CON (0x012c)
+#define ASYS_IRQ8_CON (0x0130)
+#define ASYS_IRQ9_CON (0x0134)
+#define ASYS_IRQ10_CON (0x0138)
+#define ASYS_IRQ11_CON (0x013c)
+#define ASYS_IRQ12_CON (0x0140)
+#define ASYS_IRQ13_CON (0x0144)
+#define ASYS_IRQ14_CON (0x0148)
+#define ASYS_IRQ15_CON (0x014c)
+#define ASYS_IRQ16_CON (0x0150)
+#define ASYS_IRQ_CLR (0x0154)
+#define ASYS_IRQ_STATUS (0x0158)
+#define ASYS_IRQ_MON1 (0x015c)
+#define ASYS_IRQ_MON2 (0x0160)
+#define AFE_IRQ1_CON (0x0164)
+#define AFE_IRQ2_CON (0x0168)
+#define AFE_IRQ3_CON (0x016c)
+#define AFE_IRQ_MCU_CLR (0x0170)
+#define AFE_IRQ_STATUS (0x0174)
+#define AFE_IRQ_MASK (0x0178)
+#define ASYS_IRQ_MASK (0x017c)
+#define AFE_IRQ3_CON_MON (0x01b0)
+#define AFE_IRQ_MCU_MON2 (0x01b4)
+#define AFE_IRQ8_CON (0x01b8)
+#define AFE_IRQ9_CON (0x01bc)
+#define AFE_IRQ10_CON (0x01c0)
+#define AFE_IRQ9_CON_MON (0x01c4)
+#define ADSP_IRQ_MASK (0x01c8)
+#define ADSP_IRQ_STATUS (0x01cc)
+#define AFE_SINEGEN_CON0 (0x01f0)
+#define AFE_SINEGEN_CON1 (0x01f4)
+#define AFE_SINEGEN_CON2 (0x01f8)
+#define AFE_SINEGEN_CON3 (0x01fc)
+#define AFE_SPDIF_OUT_CON0 (0x0380)
+#define AFE_TDMOUT_CONN0 (0x0390)
+#define PWR1_ASM_CON2 (0x03b0)
+#define PWR1_ASM_CON3 (0x03b4)
+#define AFE_APLL_TUNER_CFG (0x03f8)
+#define AFE_APLL_TUNER_CFG1 (0x03fc)
+#define AUDIO_TOP_STA0 (0x0400)
+#define AUDIO_TOP_STA1 (0x0404)
+#define AFE_GAIN1_CON0 (0x0410)
+#define AFE_GAIN1_CON1 (0x0414)
+#define AFE_GAIN1_CON2 (0x0418)
+#define AFE_GAIN1_CON3 (0x041c)
+#define AFE_GAIN1_CUR (0x0424)
+#define AFE_GAIN2_CON0 (0x0428)
+#define AFE_GAIN2_CON1 (0x042c)
+#define AFE_GAIN2_CON2 (0x0430)
+#define AFE_GAIN2_CON3 (0x0434)
+#define AFE_GAIN2_CUR (0x043c)
+#define AFE_IEC_CFG (0x0480)
+#define AFE_IEC_NSNUM (0x0484)
+#define AFE_IEC_BURST_INFO (0x0488)
+#define AFE_IEC_BURST_LEN (0x048c)
+#define AFE_IEC_NSADR (0x0490)
+#define AFE_IEC_CHL_STAT0 (0x04a0)
+#define AFE_IEC_CHL_STAT1 (0x04a4)
+#define AFE_IEC_CHR_STAT0 (0x04a8)
+#define AFE_IEC_CHR_STAT1 (0x04ac)
+#define AFE_SPDIFIN_CFG0 (0x0500)
+#define AFE_SPDIFIN_CFG1 (0x0504)
+#define AFE_SPDIFIN_CHSTS1 (0x0508)
+#define AFE_SPDIFIN_CHSTS2 (0x050c)
+#define AFE_SPDIFIN_CHSTS3 (0x0510)
+#define AFE_SPDIFIN_CHSTS4 (0x0514)
+#define AFE_SPDIFIN_CHSTS5 (0x0518)
+#define AFE_SPDIFIN_CHSTS6 (0x051c)
+#define AFE_SPDIFIN_DEBUG1 (0x0520)
+#define AFE_SPDIFIN_DEBUG2 (0x0524)
+#define AFE_SPDIFIN_DEBUG3 (0x0528)
+#define AFE_SPDIFIN_DEBUG4 (0x052c)
+#define AFE_SPDIFIN_EC (0x0530)
+#define AFE_SPDIFIN_CKLOCK_CFG (0x0534)
+#define AFE_SPDIFIN_BR (0x053c)
+#define AFE_SPDIFIN_BR_DBG1 (0x0540)
+#define AFE_SPDIFIN_CKFBDIV (0x0544)
+#define AFE_SPDIFIN_INT_EXT (0x0548)
+#define AFE_SPDIFIN_INT_EXT2 (0x054c)
+#define SPDIFIN_FREQ_INFO (0x0550)
+#define SPDIFIN_FREQ_INFO_2 (0x0554)
+#define SPDIFIN_FREQ_INFO_3 (0x0558)
+#define SPDIFIN_FREQ_STATUS (0x055c)
+#define SPDIFIN_USERCODE1 (0x0560)
+#define SPDIFIN_USERCODE2 (0x0564)
+#define SPDIFIN_USERCODE3 (0x0568)
+#define SPDIFIN_USERCODE4 (0x056c)
+#define SPDIFIN_USERCODE5 (0x0570)
+#define SPDIFIN_USERCODE6 (0x0574)
+#define SPDIFIN_USERCODE7 (0x0578)
+#define SPDIFIN_USERCODE8 (0x057c)
+#define SPDIFIN_USERCODE9 (0x0580)
+#define SPDIFIN_USERCODE10 (0x0584)
+#define SPDIFIN_USERCODE11 (0x0588)
+#define SPDIFIN_USERCODE12 (0x058c)
+#define AFE_SPDIFIN_APLL_TUNER_CFG (0x0594)
+#define AFE_SPDIFIN_APLL_TUNER_CFG1 (0x0598)
+#define ASYS_TOP_CON (0x0600)
+#define AFE_LINEIN_APLL_TUNER_CFG (0x0610)
+#define AFE_LINEIN_APLL_TUNER_MON (0x0614)
+#define AFE_EARC_APLL_TUNER_CFG (0x0618)
+#define AFE_EARC_APLL_TUNER_MON (0x061c)
+#define PWR2_TOP_CON0 (0x0634)
+#define PWR2_TOP_CON1 (0x0638)
+#define PCM_INTF_CON1 (0x063c)
+#define PCM_INTF_CON2 (0x0640)
+#define AFE_CM0_CON (0x0660)
+#define AFE_CM1_CON (0x0664)
+#define AFE_CM2_CON (0x0668)
+#define AFE_CM0_MON (0x0670)
+#define AFE_CM1_MON (0x0674)
+#define AFE_CM2_MON (0x0678)
+#define AFE_MPHONE_MULTI_CON0 (0x06a4)
+#define AFE_MPHONE_MULTI_CON1 (0x06a8)
+#define AFE_MPHONE_MULTI_CON2 (0x06ac)
+#define AFE_MPHONE_MULTI_MON (0x06b0)
+#define AFE_MPHONE_MULTI_DET_REG_CON0 (0x06b4)
+#define AFE_MPHONE_MULTI_DET_REG_CON1 (0x06b8)
+#define AFE_MPHONE_MULTI_DET_REG_CON2 (0x06bc)
+#define AFE_MPHONE_MULTI_DET_REG_CON3 (0x06c0)
+#define AFE_MPHONE_MULTI_DET_MON0 (0x06c4)
+#define AFE_MPHONE_MULTI_DET_MON1 (0x06c8)
+#define AFE_MPHONE_MULTI_DET_MON2 (0x06d0)
+#define AFE_MPHONE_MULTI2_CON0 (0x06d4)
+#define AFE_MPHONE_MULTI2_CON1 (0x06d8)
+#define AFE_MPHONE_MULTI2_CON2 (0x06dc)
+#define AFE_MPHONE_MULTI2_MON (0x06e0)
+#define AFE_MPHONE_MULTI2_DET_REG_CON0 (0x06e4)
+#define AFE_MPHONE_MULTI2_DET_REG_CON1 (0x06e8)
+#define AFE_MPHONE_MULTI2_DET_REG_CON2 (0x06ec)
+#define AFE_MPHONE_MULTI2_DET_REG_CON3 (0x06f0)
+#define AFE_MPHONE_MULTI2_DET_MON0 (0x06f4)
+#define AFE_MPHONE_MULTI2_DET_MON1 (0x06f8)
+#define AFE_MPHONE_MULTI2_DET_MON2 (0x06fc)
+#define AFE_ADDA_IIR_COEF_02_01 (0x0700)
+#define AFE_ADDA_IIR_COEF_04_03 (0x0704)
+#define AFE_ADDA_IIR_COEF_06_05 (0x0708)
+#define AFE_ADDA_IIR_COEF_08_07 (0x070c)
+#define AFE_ADDA_IIR_COEF_10_09 (0x0710)
+#define AFE_ADDA_ULCF_CFG_02_01 (0x0714)
+#define AFE_ADDA_ULCF_CFG_04_03 (0x0718)
+#define AFE_ADDA_ULCF_CFG_06_05 (0x071c)
+#define AFE_ADDA_ULCF_CFG_08_07 (0x0720)
+#define AFE_ADDA_ULCF_CFG_10_09 (0x0724)
+#define AFE_ADDA_ULCF_CFG_12_11 (0x0728)
+#define AFE_ADDA_ULCF_CFG_14_13 (0x072c)
+#define AFE_ADDA_ULCF_CFG_16_15 (0x0730)
+#define AFE_ADDA_ULCF_CFG_18_17 (0x0734)
+#define AFE_ADDA_ULCF_CFG_20_19 (0x0738)
+#define AFE_ADDA_ULCF_CFG_22_21 (0x073c)
+#define AFE_ADDA_ULCF_CFG_24_23 (0x0740)
+#define AFE_ADDA_ULCF_CFG_26_25 (0x0744)
+#define AFE_ADDA_ULCF_CFG_28_27 (0x0748)
+#define AFE_ADDA_ULCF_CFG_30_29 (0x074c)
+#define AFE_ADDA6_IIR_COEF_02_01 (0x0750)
+#define AFE_ADDA6_IIR_COEF_04_03 (0x0754)
+#define AFE_ADDA6_IIR_COEF_06_05 (0x0758)
+#define AFE_ADDA6_IIR_COEF_08_07 (0x075c)
+#define AFE_ADDA6_IIR_COEF_10_09 (0x0760)
+#define AFE_ADDA6_ULCF_CFG_02_01 (0x0764)
+#define AFE_ADDA6_ULCF_CFG_04_03 (0x0768)
+#define AFE_ADDA6_ULCF_CFG_06_05 (0x076c)
+#define AFE_ADDA6_ULCF_CFG_08_07 (0x0770)
+#define AFE_ADDA6_ULCF_CFG_10_09 (0x0774)
+#define AFE_ADDA6_ULCF_CFG_12_11 (0x0778)
+#define AFE_ADDA6_ULCF_CFG_14_13 (0x077c)
+#define AFE_ADDA6_ULCF_CFG_16_15 (0x0780)
+#define AFE_ADDA6_ULCF_CFG_18_17 (0x0784)
+#define AFE_ADDA6_ULCF_CFG_20_19 (0x0788)
+#define AFE_ADDA6_ULCF_CFG_22_21 (0x078c)
+#define AFE_ADDA6_ULCF_CFG_24_23 (0x0790)
+#define AFE_ADDA6_ULCF_CFG_26_25 (0x0794)
+#define AFE_ADDA6_ULCF_CFG_28_27 (0x0798)
+#define AFE_ADDA6_ULCF_CFG_30_29 (0x079c)
+#define AFE_ADDA_MTKAIF_CFG0 (0x07a0)
+#define AFE_ADDA_MTKAIF_SYNCWORD_CFG (0x07a8)
+#define AFE_ADDA_MTKAIF_RX_CFG0 (0x07b4)
+#define AFE_ADDA_MTKAIF_RX_CFG1 (0x07b8)
+#define AFE_ADDA_MTKAIF_RX_CFG2 (0x07bc)
+#define AFE_ADDA_MTKAIF_MON0 (0x07c8)
+#define AFE_ADDA_MTKAIF_MON1 (0x07cc)
+#define AFE_AUD_PAD_TOP (0x07d4)
+#define AFE_ADDA6_MTKAIF_MON0 (0x07d8)
+#define AFE_ADDA6_MTKAIF_MON1 (0x07dc)
+#define AFE_ADDA6_MTKAIF_CFG0 (0x07e0)
+#define AFE_ADDA6_MTKAIF_RX_CFG0 (0x07e4)
+#define AFE_ADDA6_MTKAIF_RX_CFG1 (0x07e8)
+#define AFE_ADDA6_MTKAIF_RX_CFG2 (0x07ec)
+#define AFE_ADDA6_TOP_CON0 (0x07f0)
+#define AFE_ADDA6_UL_SRC_CON0 (0x07f4)
+#define AFE_ADDA6_UL_SRC_CON1 (0x07f8)
+#define AFE_ADDA6_SRC_DEBUG (0x0800)
+#define AFE_ADDA6_SRC_DEBUG_MON0 (0x0804)
+#define AFE_ADDA6_UL_SRC_MON0 (0x0818)
+#define AFE_ADDA6_UL_SRC_MON1 (0x081c)
+#define AFE_CONN0_5 (0x0830)
+#define AFE_CONN1_5 (0x0834)
+#define AFE_CONN2_5 (0x0838)
+#define AFE_CONN3_5 (0x083c)
+#define AFE_CONN4_5 (0x0840)
+#define AFE_CONN5_5 (0x0844)
+#define AFE_CONN6_5 (0x0848)
+#define AFE_CONN7_5 (0x084c)
+#define AFE_CONN8_5 (0x0850)
+#define AFE_CONN9_5 (0x0854)
+#define AFE_CONN10_5 (0x0858)
+#define AFE_CONN11_5 (0x085c)
+#define AFE_CONN12_5 (0x0860)
+#define AFE_CONN13_5 (0x0864)
+#define AFE_CONN14_5 (0x0868)
+#define AFE_CONN15_5 (0x086c)
+#define AFE_CONN16_5 (0x0870)
+#define AFE_CONN17_5 (0x0874)
+#define AFE_CONN18_5 (0x0878)
+#define AFE_CONN19_5 (0x087c)
+#define AFE_CONN20_5 (0x0880)
+#define AFE_CONN21_5 (0x0884)
+#define AFE_CONN22_5 (0x0888)
+#define AFE_CONN23_5 (0x088c)
+#define AFE_CONN24_5 (0x0890)
+#define AFE_CONN25_5 (0x0894)
+#define AFE_CONN26_5 (0x0898)
+#define AFE_CONN27_5 (0x089c)
+#define AFE_CONN28_5 (0x08a0)
+#define AFE_CONN29_5 (0x08a4)
+#define AFE_CONN30_5 (0x08a8)
+#define AFE_CONN31_5 (0x08ac)
+#define AFE_CONN32_5 (0x08b0)
+#define AFE_CONN33_5 (0x08b4)
+#define AFE_CONN34_5 (0x08b8)
+#define AFE_CONN35_5 (0x08bc)
+#define AFE_CONN36_5 (0x08c0)
+#define AFE_CONN37_5 (0x08c4)
+#define AFE_CONN38_5 (0x08c8)
+#define AFE_CONN39_5 (0x08cc)
+#define AFE_CONN40_5 (0x08d0)
+#define AFE_CONN41_5 (0x08d4)
+#define AFE_CONN42_5 (0x08d8)
+#define AFE_CONN43_5 (0x08dc)
+#define AFE_CONN44_5 (0x08e0)
+#define AFE_CONN45_5 (0x08e4)
+#define AFE_CONN46_5 (0x08e8)
+#define AFE_CONN47_5 (0x08ec)
+#define AFE_CONN48_5 (0x08f0)
+#define AFE_CONN49_5 (0x08f4)
+#define AFE_CONN50_5 (0x08f8)
+#define AFE_CONN51_5 (0x08fc)
+#define AFE_CONN52_5 (0x0900)
+#define AFE_CONN53_5 (0x0904)
+#define AFE_CONN54_5 (0x0908)
+#define AFE_CONN55_5 (0x090c)
+#define AFE_CONN56_5 (0x0910)
+#define AFE_CONN57_5 (0x0914)
+#define AFE_CONN58_5 (0x0918)
+#define AFE_CONN59_5 (0x091c)
+#define AFE_CONN60_5 (0x0920)
+#define AFE_CONN61_5 (0x0924)
+#define AFE_CONN62_5 (0x0928)
+#define AFE_CONN63_5 (0x092c)
+#define AFE_CONN64_5 (0x0930)
+#define AFE_CONN65_5 (0x0934)
+#define AFE_CONN66_5 (0x0938)
+#define AFE_CONN67_5 (0x093c)
+#define AFE_CONN68_5 (0x0940)
+#define AFE_CONN69_5 (0x0944)
+#define AFE_CONN70_5 (0x0948)
+#define AFE_CONN71_5 (0x094c)
+#define AFE_CONN72_5 (0x0950)
+#define AFE_CONN73_5 (0x0954)
+#define AFE_CONN74_5 (0x0958)
+#define AFE_CONN75_5 (0x095c)
+#define AFE_CONN76_5 (0x0960)
+#define AFE_CONN77_5 (0x0964)
+#define AFE_CONN78_5 (0x0968)
+#define AFE_CONN79_5 (0x096c)
+#define AFE_CONN80_5 (0x0970)
+#define AFE_CONN81_5 (0x0974)
+#define AFE_CONN82_5 (0x0978)
+#define AFE_CONN83_5 (0x097c)
+#define AFE_CONN84_5 (0x0980)
+#define AFE_CONN85_5 (0x0984)
+#define AFE_CONN86_5 (0x0988)
+#define AFE_CONN87_5 (0x098c)
+#define AFE_CONN88_5 (0x0990)
+#define AFE_CONN89_5 (0x0994)
+#define AFE_CONN90_5 (0x0998)
+#define AFE_CONN91_5 (0x099c)
+#define AFE_CONN92_5 (0x09a0)
+#define AFE_CONN93_5 (0x09a4)
+#define AFE_CONN94_5 (0x09a8)
+#define AFE_CONN95_5 (0x09ac)
+#define AFE_CONN96_5 (0x09b0)
+#define AFE_CONN97_5 (0x09b4)
+#define AFE_CONN98_5 (0x09b8)
+#define AFE_CONN99_5 (0x09bc)
+#define AFE_CONN100_5 (0x09c0)
+#define AFE_CONN101_5 (0x09c4)
+#define AFE_CONN102_5 (0x09c8)
+#define AFE_CONN103_5 (0x09cc)
+#define AFE_CONN104_5 (0x09d0)
+#define AFE_CONN105_5 (0x09d4)
+#define AFE_CONN106_5 (0x09d8)
+#define AFE_CONN107_5 (0x09dc)
+#define AFE_CONN108_5 (0x09e0)
+#define AFE_CONN109_5 (0x09e4)
+#define AFE_CONN110_5 (0x09e8)
+#define AFE_CONN111_5 (0x09ec)
+#define AFE_CONN112_5 (0x09f0)
+#define AFE_CONN113_5 (0x09f4)
+#define AFE_CONN114_5 (0x09f8)
+#define AFE_CONN115_5 (0x09fc)
+#define AFE_CONN116_5 (0x0a00)
+#define AFE_CONN117_5 (0x0a04)
+#define AFE_CONN118_5 (0x0a08)
+#define AFE_CONN119_5 (0x0a0c)
+#define AFE_CONN120_5 (0x0a10)
+#define AFE_CONN121_5 (0x0a14)
+#define AFE_CONN122_5 (0x0a18)
+#define AFE_CONN123_5 (0x0a1c)
+#define AFE_CONN124_5 (0x0a20)
+#define AFE_CONN125_5 (0x0a24)
+#define AFE_CONN126_5 (0x0a28)
+#define AFE_CONN127_5 (0x0a2c)
+#define AFE_CONN128_5 (0x0a30)
+#define AFE_CONN129_5 (0x0a34)
+#define AFE_CONN130_5 (0x0a38)
+#define AFE_CONN131_5 (0x0a3c)
+#define AFE_CONN132_5 (0x0a40)
+#define AFE_CONN133_5 (0x0a44)
+#define AFE_CONN134_5 (0x0a48)
+#define AFE_CONN135_5 (0x0a4c)
+#define AFE_CONN136_5 (0x0a50)
+#define AFE_CONN137_5 (0x0a54)
+#define AFE_CONN138_5 (0x0a58)
+#define AFE_CONN139_5 (0x0a5c)
+#define AFE_CONN_RS_5 (0x0a60)
+#define AFE_CONN_DI_5 (0x0a64)
+#define AFE_CONN_16BIT_5 (0x0a68)
+#define AFE_CONN_24BIT_5 (0x0a6c)
+#define AFE_SECURE_MASK_CONN53_5 (0x0a70)
+#define AFE_SECURE_MASK_CONN54_5 (0x0a74)
+#define AFE_SECURE_MASK_CONN55_5 (0x0a78)
+#define AFE_SECURE_MASK_CONN56_5 (0x0a7c)
+#define AFE_SECURE_MASK_CONN57_5 (0x0a80)
+#define AFE_SECURE_MASK_CONN58_5 (0x0a84)
+#define AFE_SECURE_MASK_CONN59_5 (0x0a88)
+#define AFE_SECURE_MASK_CONN60_5 (0x0a8c)
+#define AFE_SECURE_MASK_CONN61_5 (0x0a90)
+#define AFE_SECURE_MASK_CONN62_5 (0x0a94)
+#define AFE_SECURE_MASK_CONN63_5 (0x0a98)
+#define AFE_SECURE_MASK_CONN64_5 (0x0a9c)
+#define AFE_SECURE_MASK_CONN65_5 (0x0aa0)
+#define AFE_SECURE_MASK_CONN66_5 (0x0aa4)
+#define AFE_SECURE_MASK_CONN67_5 (0x0aa8)
+#define AFE_SECURE_MASK_CONN68_5 (0x0aac)
+#define AFE_SECURE_MASK_CONN69_5 (0x0ab0)
+#define AFE_SECURE_MASK_CONN70_5 (0x0ab4)
+#define AFE_SECURE_MASK_CONN71_5 (0x0ab8)
+#define AFE_SECURE_MASK_CONN72_5 (0x0abc)
+#define AFE_SECURE_MASK_CONN73_5 (0x0ac0)
+#define AFE_SECURE_MASK_CONN74_5 (0x0ac4)
+#define AFE_SECURE_MASK_CONN75_5 (0x0ac8)
+#define AFE_SECURE_MASK_CONN76_5 (0x0acc)
+#define AFE_SECURE_MASK_CONN77_5 (0x0ad0)
+#define AFE_SECURE_MASK_CONN78_5 (0x0ad4)
+#define AFE_SECURE_MASK_CONN79_5 (0x0ad8)
+#define AFE_SECURE_MASK_CONN80_5 (0x0adc)
+#define AFE_SECURE_MASK_CONN81_5 (0x0ae0)
+#define AFE_SECURE_MASK_CONN82_5 (0x0ae4)
+#define AFE_SECURE_MASK_CONN83_5 (0x0ae8)
+#define AFE_SECURE_MASK_CONN84_5 (0x0aec)
+#define AFE_SECURE_MASK_CONN85_5 (0x0af0)
+#define AFE_SECURE_MASK_CONN86_5 (0x0af4)
+#define AFE_SECURE_MASK_CONN87_5 (0x0af8)
+#define AFE_SECURE_MASK_CONN88_5 (0x0afc)
+#define AFE_SECURE_MASK_CONN89_5 (0x0b00)
+#define AFE_SECURE_MASK_CONN90_5 (0x0b04)
+#define AFE_SECURE_MASK_CONN91_5 (0x0b08)
+#define AFE_SECURE_MASK_CONN92_5 (0x0b0c)
+#define AFE_SECURE_MASK_CONN93_5 (0x0b10)
+#define AFE_SECURE_MASK_CONN94_5 (0x0b14)
+#define AFE_SECURE_MASK_CONN95_5 (0x0b18)
+#define AFE_SECURE_MASK_CONN96_5 (0x0b1c)
+#define AFE_SECURE_MASK_CONN97_5 (0x0b20)
+#define AFE_SECURE_MASK_CONN98_5 (0x0b24)
+#define AFE_SECURE_MASK_CONN99_5 (0x0b28)
+#define AFE_SECURE_MASK_CONN100_5 (0x0b2c)
+#define AFE_SECURE_MASK_CONN101_5 (0x0b30)
+#define AFE_SECURE_MASK_CONN102_5 (0x0b34)
+#define AFE_SECURE_MASK_CONN103_5 (0x0b38)
+#define AFE_SECURE_MASK_CONN104_5 (0x0b3c)
+#define AFE_SECURE_MASK_CONN105_5 (0x0b40)
+#define AFE_SECURE_MASK_CONN106_5 (0x0b44)
+#define AFE_SECURE_MASK_CONN107_5 (0x0b48)
+#define AFE_SECURE_MASK_CONN108_5 (0x0b4c)
+#define AFE_SECURE_MASK_CONN109_5 (0x0b50)
+#define AFE_SECURE_MASK_CONN110_5 (0x0b54)
+#define AFE_SECURE_MASK_CONN111_5 (0x0b58)
+#define AFE_SECURE_MASK_CONN112_5 (0x0b5c)
+#define AFE_SECURE_MASK_CONN113_5 (0x0b60)
+#define AFE_SECURE_MASK_CONN114_5 (0x0b64)
+#define AFE_SECURE_MASK_CONN115_5 (0x0b68)
+#define AFE_SECURE_MASK_CONN116_5 (0x0b6c)
+#define AFE_SECURE_MASK_CONN117_5 (0x0b70)
+#define AFE_SECURE_MASK_CONN118_5 (0x0b74)
+#define AFE_SECURE_MASK_CONN119_5 (0x0b78)
+#define AFE_SECURE_MASK_CONN120_5 (0x0b7c)
+#define AFE_SECURE_MASK_CONN121_5 (0x0b80)
+#define AFE_SECURE_MASK_CONN122_5 (0x0b84)
+#define AFE_SECURE_MASK_CONN123_5 (0x0b88)
+#define AFE_SECURE_MASK_CONN124_5 (0x0b8c)
+#define AFE_SECURE_MASK_CONN125_5 (0x0b90)
+#define AFE_SECURE_MASK_CONN126_5 (0x0b94)
+#define AFE_SECURE_MASK_CONN127_5 (0x0b98)
+#define AFE_SECURE_MASK_CONN128_5 (0x0b9c)
+#define AFE_SECURE_MASK_CONN129_5 (0x0ba0)
+#define AFE_SECURE_MASK_CONN130_5 (0x0ba4)
+#define AFE_SECURE_MASK_CONN131_5 (0x0ba8)
+#define AFE_SECURE_MASK_CONN132_5 (0x0bac)
+#define AFE_SECURE_MASK_CONN133_5 (0x0bb0)
+#define AFE_SECURE_MASK_CONN134_5 (0x0bb4)
+#define AFE_SECURE_MASK_CONN135_5 (0x0bb8)
+#define AFE_SECURE_MASK_CONN136_5 (0x0bbc)
+#define AFE_SECURE_MASK_CONN137_5 (0x0bc0)
+#define AFE_SECURE_MASK_CONN138_5 (0x0bc4)
+#define AFE_SECURE_MASK_CONN139_5 (0x0bc8)
+#define AFE_SECURE_MASK_CONN_RS_5 (0x0bcc)
+#define AFE_SECURE_MASK_CONN_16BIT_5 (0x0bd0)
+#define AFE_SECURE_MASK_CONN_24BIT_5 (0x0bd4)
+#define AFE_ASRC11_NEW_CON0 (0x0d80)
+#define AFE_ASRC11_NEW_CON1 (0x0d84)
+#define AFE_ASRC11_NEW_CON2 (0x0d88)
+#define AFE_ASRC11_NEW_CON3 (0x0d8c)
+#define AFE_ASRC11_NEW_CON4 (0x0d90)
+#define AFE_ASRC11_NEW_CON5 (0x0d94)
+#define AFE_ASRC11_NEW_CON6 (0x0d98)
+#define AFE_ASRC11_NEW_CON7 (0x0d9c)
+#define AFE_ASRC11_NEW_CON8 (0x0da0)
+#define AFE_ASRC11_NEW_CON9 (0x0da4)
+#define AFE_ASRC11_NEW_CON10 (0x0da8)
+#define AFE_ASRC11_NEW_CON11 (0x0dac)
+#define AFE_ASRC11_NEW_CON13 (0x0db4)
+#define AFE_ASRC11_NEW_CON14 (0x0db8)
+#define AFE_ASRC12_NEW_CON0 (0x0dc0)
+#define AFE_ASRC12_NEW_CON1 (0x0dc4)
+#define AFE_ASRC12_NEW_CON2 (0x0dc8)
+#define AFE_ASRC12_NEW_CON3 (0x0dcc)
+#define AFE_ASRC12_NEW_CON4 (0x0dd0)
+#define AFE_ASRC12_NEW_CON5 (0x0dd4)
+#define AFE_ASRC12_NEW_CON6 (0x0dd8)
+#define AFE_ASRC12_NEW_CON7 (0x0ddc)
+#define AFE_ASRC12_NEW_CON8 (0x0de0)
+#define AFE_ASRC12_NEW_CON9 (0x0de4)
+#define AFE_ASRC12_NEW_CON10 (0x0de8)
+#define AFE_ASRC12_NEW_CON11 (0x0dec)
+#define AFE_ASRC12_NEW_CON13 (0x0df4)
+#define AFE_ASRC12_NEW_CON14 (0x0df8)
+#define AFE_SECURE_MASK_CONN176 (0x0fe0)
+#define AFE_SECURE_MASK_CONN176_1 (0x0fe4)
+#define AFE_SECURE_MASK_CONN176_2 (0x0fe8)
+#define AFE_SECURE_MASK_CONN176_3 (0x0fec)
+#define AFE_SECURE_MASK_CONN176_4 (0x0ff0)
+#define AFE_SECURE_MASK_CONN176_5 (0x0ff4)
+#define AFE_SECURE_MASK_CONN177 (0x0ff8)
+#define AFE_SECURE_MASK_CONN177_1 (0x0ffc)
+#define AFE_LRCK_CNT (0x1018)
+#define AFE_SECURE_MASK_CONN177_2 (0x1020)
+#define AFE_SECURE_MASK_CONN177_3 (0x1024)
+#define AFE_SECURE_MASK_CONN177_4 (0x1028)
+#define AFE_SECURE_MASK_CONN177_5 (0x102c)
+#define AFE_SECURE_MASK_CONN182 (0x1090)
+#define AFE_SECURE_MASK_CONN182_1 (0x1094)
+#define AFE_SECURE_MASK_CONN182_2 (0x1098)
+#define AFE_SECURE_MASK_CONN182_3 (0x109c)
+#define AFE_SECURE_MASK_CONN182_4 (0x10a0)
+#define AFE_SECURE_MASK_CONN182_5 (0x10a4)
+#define AFE_SECURE_MASK_CONN183 (0x10a8)
+#define AFE_SECURE_MASK_CONN183_1 (0x10ac)
+#define AFE_SECURE_MASK_CONN183_2 (0x10b0)
+#define AFE_SECURE_MASK_CONN183_3 (0x10b4)
+#define AFE_SECURE_MASK_CONN183_4 (0x10b8)
+#define AFE_SECURE_MASK_CONN183_5 (0x10bc)
+#define AFE_DAC_CON0 (0x1200)
+#define AFE_DAC_CON1 (0x1204)
+#define AFE_DAC_CON2 (0x1208)
+#define AFE_DAC_MON0 (0x1218)
+#define AFE_DL1_BASE (0x1240)
+#define AFE_DL1_CUR (0x1244)
+#define AFE_DL1_END (0x1248)
+#define AFE_DL1_CON0 (0x124c)
+#define AFE_DL2_BASE (0x1250)
+#define AFE_DL2_CUR (0x1254)
+#define AFE_DL2_END (0x1258)
+#define AFE_DL2_CON0 (0x125c)
+#define AFE_DL3_BASE (0x1260)
+#define AFE_DL3_CUR (0x1264)
+#define AFE_DL3_END (0x1268)
+#define AFE_DL3_CON0 (0x126c)
+#define AFE_DL6_BASE (0x1290)
+#define AFE_DL6_CUR (0x1294)
+#define AFE_DL6_END (0x1298)
+#define AFE_DL6_CON0 (0x129c)
+#define AFE_DL7_BASE (0x12a0)
+#define AFE_DL7_CUR (0x12a4)
+#define AFE_DL7_END (0x12a8)
+#define AFE_DL7_CON0 (0x12ac)
+#define AFE_DL8_BASE (0x12b0)
+#define AFE_DL8_CUR (0x12b4)
+#define AFE_DL8_END (0x12b8)
+#define AFE_DL8_CON0 (0x12bc)
+#define AFE_DL10_BASE (0x12d0)
+#define AFE_DL10_CUR (0x12d4)
+#define AFE_DL10_END (0x12d8)
+#define AFE_DL10_CON0 (0x12dc)
+#define AFE_DL11_BASE (0x12e0)
+#define AFE_DL11_CUR (0x12e4)
+#define AFE_DL11_END (0x12e8)
+#define AFE_DL11_CON0 (0x12ec)
+#define AFE_UL1_BASE (0x1300)
+#define AFE_UL1_CUR (0x1304)
+#define AFE_UL1_END (0x1308)
+#define AFE_UL1_CON0 (0x130c)
+#define AFE_UL2_BASE (0x1310)
+#define AFE_UL2_CUR (0x1314)
+#define AFE_UL2_END (0x1318)
+#define AFE_UL2_CON0 (0x131c)
+#define AFE_UL3_BASE (0x1320)
+#define AFE_UL3_CUR (0x1324)
+#define AFE_UL3_END (0x1328)
+#define AFE_UL3_CON0 (0x132c)
+#define AFE_UL4_BASE (0x1330)
+#define AFE_UL4_CUR (0x1334)
+#define AFE_UL4_END (0x1338)
+#define AFE_UL4_CON0 (0x133c)
+#define AFE_UL5_BASE (0x1340)
+#define AFE_UL5_CUR (0x1344)
+#define AFE_UL5_END (0x1348)
+#define AFE_UL5_CON0 (0x134c)
+#define AFE_UL6_BASE (0x1350)
+#define AFE_UL6_CUR (0x1354)
+#define AFE_UL6_END (0x1358)
+#define AFE_UL6_CON0 (0x135c)
+#define AFE_UL8_BASE (0x1370)
+#define AFE_UL8_CUR (0x1374)
+#define AFE_UL8_END (0x1378)
+#define AFE_UL8_CON0 (0x137c)
+#define AFE_UL9_BASE (0x1380)
+#define AFE_UL9_CUR (0x1384)
+#define AFE_UL9_END (0x1388)
+#define AFE_UL9_CON0 (0x138c)
+#define AFE_UL10_BASE (0x13d0)
+#define AFE_UL10_CUR (0x13d4)
+#define AFE_UL10_END (0x13d8)
+#define AFE_UL10_CON0 (0x13dc)
+#define AFE_DL8_CHK_SUM1 (0x1400)
+#define AFE_DL8_CHK_SUM2 (0x1404)
+#define AFE_DL8_CHK_SUM3 (0x1408)
+#define AFE_DL8_CHK_SUM4 (0x140c)
+#define AFE_DL8_CHK_SUM5 (0x1410)
+#define AFE_DL8_CHK_SUM6 (0x1414)
+#define AFE_DL10_CHK_SUM1 (0x1418)
+#define AFE_DL10_CHK_SUM2 (0x141c)
+#define AFE_DL10_CHK_SUM3 (0x1420)
+#define AFE_DL10_CHK_SUM4 (0x1424)
+#define AFE_DL10_CHK_SUM5 (0x1428)
+#define AFE_DL10_CHK_SUM6 (0x142c)
+#define AFE_DL11_CHK_SUM1 (0x1430)
+#define AFE_DL11_CHK_SUM2 (0x1434)
+#define AFE_DL11_CHK_SUM3 (0x1438)
+#define AFE_DL11_CHK_SUM4 (0x143c)
+#define AFE_DL11_CHK_SUM5 (0x1440)
+#define AFE_DL11_CHK_SUM6 (0x1444)
+#define AFE_UL1_CHK_SUM1 (0x1450)
+#define AFE_UL1_CHK_SUM2 (0x1454)
+#define AFE_UL2_CHK_SUM1 (0x1458)
+#define AFE_UL2_CHK_SUM2 (0x145c)
+#define AFE_UL3_CHK_SUM1 (0x1460)
+#define AFE_UL3_CHK_SUM2 (0x1464)
+#define AFE_UL4_CHK_SUM1 (0x1468)
+#define AFE_UL4_CHK_SUM2 (0x146c)
+#define AFE_UL5_CHK_SUM1 (0x1470)
+#define AFE_UL5_CHK_SUM2 (0x1474)
+#define AFE_UL6_CHK_SUM1 (0x1478)
+#define AFE_UL6_CHK_SUM2 (0x147c)
+#define AFE_UL8_CHK_SUM1 (0x1488)
+#define AFE_UL8_CHK_SUM2 (0x148c)
+#define AFE_DL1_CHK_SUM1 (0x1490)
+#define AFE_DL1_CHK_SUM2 (0x1494)
+#define AFE_DL2_CHK_SUM1 (0x14a0)
+#define AFE_DL2_CHK_SUM2 (0x14a4)
+#define AFE_DL3_CHK_SUM1 (0x14b0)
+#define AFE_DL3_CHK_SUM2 (0x14b4)
+#define AFE_DL6_CHK_SUM1 (0x14e0)
+#define AFE_DL6_CHK_SUM2 (0x14e4)
+#define AFE_DL7_CHK_SUM1 (0x14f0)
+#define AFE_DL7_CHK_SUM2 (0x14f4)
+#define AFE_UL9_CHK_SUM1 (0x1528)
+#define AFE_UL9_CHK_SUM2 (0x152c)
+#define AFE_BUS_MON1 (0x1540)
+#define AFE_UL10_CHK_SUM1 (0x1550)
+#define AFE_UL10_CHK_SUM2 (0x1554)
+#define UL1_MOD2AGT_CNT_LAT (0x1568)
+#define UL2_MOD2AGT_CNT_LAT (0x156c)
+#define UL3_MOD2AGT_CNT_LAT (0x1570)
+#define UL4_MOD2AGT_CNT_LAT (0x1574)
+#define UL5_MOD2AGT_CNT_LAT (0x1578)
+#define UL6_MOD2AGT_CNT_LAT (0x157c)
+#define UL8_MOD2AGT_CNT_LAT (0x1588)
+#define UL9_MOD2AGT_CNT_LAT (0x158c)
+#define UL10_MOD2AGT_CNT_LAT (0x1590)
+#define AFE_MEMIF_AGENT_FS_CON0 (0x15a0)
+#define AFE_MEMIF_AGENT_FS_CON1 (0x15a4)
+#define AFE_MEMIF_AGENT_FS_CON2 (0x15a8)
+#define AFE_MEMIF_AGENT_FS_CON3 (0x15ac)
+#define AFE_MEMIF_BURST_CFG (0x1600)
+#define AFE_MEMIF_BUF_FULL_MON (0x1610)
+#define AFE_MEMIF_BUF_MON0 (0x1618)
+#define AFE_MEMIF_BUF_MON1 (0x161c)
+#define AFE_MEMIF_BUF_MON3 (0x1624)
+#define AFE_MEMIF_BUF_MON4 (0x1628)
+#define AFE_MEMIF_BUF_MON5 (0x162c)
+#define AFE_MEMIF_BUF_MON6 (0x1630)
+#define AFE_MEMIF_BUF_MON7 (0x1634)
+#define AFE_MEMIF_BUF_MON8 (0x1638)
+#define AFE_MEMIF_BUF_MON9 (0x163c)
+#define AFE_MEMIF_BUF_MON10 (0x1640)
+#define DL1_AGENT2MODULE_CNT (0x1674)
+#define DL2_AGENT2MODULE_CNT (0x1678)
+#define DL3_AGENT2MODULE_CNT (0x167c)
+#define DL6_AGENT2MODULE_CNT (0x1688)
+#define DL7_AGENT2MODULE_CNT (0x168c)
+#define DL8_AGENT2MODULE_CNT (0x1690)
+#define DL10_AGENT2MODULE_CNT (0x1698)
+#define DL11_AGENT2MODULE_CNT (0x169c)
+#define UL1_MODULE2AGENT_CNT (0x16a0)
+#define UL2_MODULE2AGENT_CNT (0x16a4)
+#define UL3_MODULE2AGENT_CNT (0x16a8)
+#define UL4_MODULE2AGENT_CNT (0x16ac)
+#define UL5_MODULE2AGENT_CNT (0x16b0)
+#define UL6_MODULE2AGENT_CNT (0x16b4)
+#define UL8_MODULE2AGENT_CNT (0x16bc)
+#define UL9_MODULE2AGENT_CNT (0x16c0)
+#define UL10_MODULE2AGENT_CNT (0x16c4)
+#define AFE_SECURE_CON2 (0x1798)
+#define AFE_SECURE_CON1 (0x179c)
+#define AFE_SECURE_CON (0x17a0)
+#define AFE_SRAM_BOUND (0x17a4)
+#define AFE_SE_SECURE_CON (0x17a8)
+#define AFE_SECURE_MASK_LOOPBACK (0x17bc)
+#define AFE_SRAM_SECURE_CON (0x1800)
+#define AFE_SRAM_SECURE_CON1 (0x1804)
+#define AFE_SRAM_SECURE_CON2 (0x1808)
+#define AFE_SECURE_SIDEBAND0 (0x1908)
+#define AFE_SECURE_SIDEBAND1 (0x190c)
+#define AFE_SECURE_SIDEBAND2 (0x1910)
+#define AFE_SECURE_SIDEBAND3 (0x1914)
+#define AFE_SECURE_MASK_BASE_ADR_MSB (0x1920)
+#define AFE_SECURE_MASK_END_ADR_MSB (0x1924)
+#define AFE_NORMAL_BASE_ADR_MSB (0x192c)
+#define AFE_NORMAL_END_ADR_MSB (0x1930)
+#define AFE_SECURE_MASK_LOOPBACK0 (0x1940)
+#define AFE_SECURE_MASK_LOOPBACK1 (0x1944)
+#define AFE_SECURE_MASK_LOOPBACK2 (0x1948)
+#define AFE_LOOPBACK_CFG0 (0x1950)
+#define AFE_LOOPBACK_CFG1 (0x1954)
+#define AFE_LOOPBACK_CFG2 (0x1958)
+#define AFE_DMIC0_UL_SRC_CON0 (0x1a00)
+#define AFE_DMIC0_UL_SRC_CON1 (0x1a04)
+#define AFE_DMIC0_SRC_DEBUG (0x1a08)
+#define AFE_DMIC0_SRC_DEBUG_MON0 (0x1a0c)
+#define AFE_DMIC0_UL_SRC_MON0 (0x1a10)
+#define AFE_DMIC0_UL_SRC_MON1 (0x1a14)
+#define AFE_DMIC0_IIR_COEF_02_01 (0x1a18)
+#define AFE_DMIC0_IIR_COEF_04_03 (0x1a1c)
+#define AFE_DMIC0_IIR_COEF_06_05 (0x1a20)
+#define AFE_DMIC0_IIR_COEF_08_07 (0x1a24)
+#define AFE_DMIC0_IIR_COEF_10_09 (0x1a28)
+#define AFE_DMIC1_UL_SRC_CON0 (0x1a68)
+#define AFE_DMIC1_UL_SRC_CON1 (0x1a6c)
+#define AFE_DMIC1_SRC_DEBUG (0x1a70)
+#define AFE_DMIC1_SRC_DEBUG_MON0 (0x1a74)
+#define AFE_DMIC1_UL_SRC_MON0 (0x1a78)
+#define AFE_DMIC1_UL_SRC_MON1 (0x1a7c)
+#define AFE_DMIC1_IIR_COEF_02_01 (0x1a80)
+#define AFE_DMIC1_IIR_COEF_04_03 (0x1a84)
+#define AFE_DMIC1_IIR_COEF_06_05 (0x1a88)
+#define AFE_DMIC1_IIR_COEF_08_07 (0x1a8c)
+#define AFE_DMIC1_IIR_COEF_10_09 (0x1a90)
+#define AFE_DMIC2_UL_SRC_CON0 (0x1ad0)
+#define AFE_DMIC2_UL_SRC_CON1 (0x1ad4)
+#define AFE_DMIC2_SRC_DEBUG (0x1ad8)
+#define AFE_DMIC2_SRC_DEBUG_MON0 (0x1adc)
+#define AFE_DMIC2_UL_SRC_MON0 (0x1ae0)
+#define AFE_DMIC2_UL_SRC_MON1 (0x1ae4)
+#define AFE_DMIC2_IIR_COEF_02_01 (0x1ae8)
+#define AFE_DMIC2_IIR_COEF_04_03 (0x1aec)
+#define AFE_DMIC2_IIR_COEF_06_05 (0x1af0)
+#define AFE_DMIC2_IIR_COEF_08_07 (0x1af4)
+#define AFE_DMIC2_IIR_COEF_10_09 (0x1af8)
+#define AFE_DMIC3_UL_SRC_CON0 (0x1b38)
+#define AFE_DMIC3_UL_SRC_CON1 (0x1b3c)
+#define AFE_DMIC3_SRC_DEBUG (0x1b40)
+#define AFE_DMIC3_SRC_DEBUG_MON0 (0x1b44)
+#define AFE_DMIC3_UL_SRC_MON0 (0x1b48)
+#define AFE_DMIC3_UL_SRC_MON1 (0x1b4c)
+#define AFE_DMIC3_IIR_COEF_02_01 (0x1b50)
+#define AFE_DMIC3_IIR_COEF_04_03 (0x1b54)
+#define AFE_DMIC3_IIR_COEF_06_05 (0x1b58)
+#define AFE_DMIC3_IIR_COEF_08_07 (0x1b5c)
+#define AFE_DMIC3_IIR_COEF_10_09 (0x1b60)
+#define DMIC_BYPASS_HW_GAIN (0x1bf0)
+#define DMIC_GAIN1_CON0 (0x1c00)
+#define DMIC_GAIN1_CON1 (0x1c04)
+#define DMIC_GAIN1_CON2 (0x1c08)
+#define DMIC_GAIN1_CON3 (0x1c0c)
+#define DMIC_GAIN1_CUR (0x1c10)
+#define DMIC_GAIN2_CON0 (0x1c20)
+#define DMIC_GAIN2_CON1 (0x1c24)
+#define DMIC_GAIN2_CON2 (0x1c28)
+#define DMIC_GAIN2_CON3 (0x1c2c)
+#define DMIC_GAIN2_CUR (0x1c30)
+#define DMIC_GAIN3_CON0 (0x1c40)
+#define DMIC_GAIN3_CON1 (0x1c44)
+#define DMIC_GAIN3_CON2 (0x1c48)
+#define DMIC_GAIN3_CON3 (0x1c4c)
+#define DMIC_GAIN3_CUR (0x1c50)
+#define DMIC_GAIN4_CON0 (0x1c60)
+#define DMIC_GAIN4_CON1 (0x1c64)
+#define DMIC_GAIN4_CON2 (0x1c68)
+#define DMIC_GAIN4_CON3 (0x1c6c)
+#define DMIC_GAIN4_CUR (0x1c70)
+#define ETDM_OUT1_DSD_FADE_CON (0x2260)
+#define ETDM_OUT1_DSD_FADE_CON1 (0x2264)
+#define ETDM_OUT3_DSD_FADE_CON (0x2280)
+#define ETDM_OUT3_DSD_FADE_CON1 (0x2284)
+#define ETDM_IN1_AFIFO_CON (0x2294)
+#define ETDM_IN2_AFIFO_CON (0x2298)
+#define ETDM_IN1_MONITOR (0x22c0)
+#define ETDM_IN2_MONITOR (0x22c4)
+#define ETDM_OUT1_MONITOR (0x22d0)
+#define ETDM_OUT2_MONITOR (0x22d4)
+#define ETDM_OUT3_MONITOR (0x22d8)
+#define ETDM_COWORK_SEC_CON0 (0x22e0)
+#define ETDM_COWORK_SEC_CON1 (0x22e4)
+#define ETDM_COWORK_SEC_CON2 (0x22e8)
+#define ETDM_COWORK_SEC_CON3 (0x22ec)
+#define ETDM_COWORK_CON0 (0x22f0)
+#define ETDM_COWORK_CON1 (0x22f4)
+#define ETDM_COWORK_CON2 (0x22f8)
+#define ETDM_COWORK_CON3 (0x22fc)
+#define ETDM_IN1_CON0 (0x2300)
+#define ETDM_IN1_CON1 (0x2304)
+#define ETDM_IN1_CON2 (0x2308)
+#define ETDM_IN1_CON3 (0x230c)
+#define ETDM_IN1_CON4 (0x2310)
+#define ETDM_IN1_CON5 (0x2314)
+#define ETDM_IN1_CON6 (0x2318)
+#define ETDM_IN1_CON7 (0x231c)
+#define ETDM_IN2_CON0 (0x2320)
+#define ETDM_IN2_CON1 (0x2324)
+#define ETDM_IN2_CON2 (0x2328)
+#define ETDM_IN2_CON3 (0x232c)
+#define ETDM_IN2_CON4 (0x2330)
+#define ETDM_IN2_CON5 (0x2334)
+#define ETDM_IN2_CON6 (0x2338)
+#define ETDM_IN2_CON7 (0x233c)
+#define ETDM_OUT1_CON0 (0x2380)
+#define ETDM_OUT1_CON1 (0x2384)
+#define ETDM_OUT1_CON2 (0x2388)
+#define ETDM_OUT1_CON3 (0x238c)
+#define ETDM_OUT1_CON4 (0x2390)
+#define ETDM_OUT1_CON5 (0x2394)
+#define ETDM_OUT1_CON6 (0x2398)
+#define ETDM_OUT1_CON7 (0x239c)
+#define ETDM_OUT2_CON0 (0x23a0)
+#define ETDM_OUT2_CON1 (0x23a4)
+#define ETDM_OUT2_CON2 (0x23a8)
+#define ETDM_OUT2_CON3 (0x23ac)
+#define ETDM_OUT2_CON4 (0x23b0)
+#define ETDM_OUT2_CON5 (0x23b4)
+#define ETDM_OUT2_CON6 (0x23b8)
+#define ETDM_OUT2_CON7 (0x23bc)
+#define ETDM_OUT3_CON0 (0x23c0)
+#define ETDM_OUT3_CON1 (0x23c4)
+#define ETDM_OUT3_CON2 (0x23c8)
+#define ETDM_OUT3_CON3 (0x23cc)
+#define ETDM_OUT3_CON4 (0x23d0)
+#define ETDM_OUT3_CON5 (0x23d4)
+#define ETDM_OUT3_CON6 (0x23d8)
+#define ETDM_OUT3_CON7 (0x23dc)
+#define ETDM_OUT3_CON8 (0x23e0)
+#define ETDM_OUT1_CON8 (0x23e4)
+#define ETDM_OUT2_CON8 (0x23e8)
+#define GASRC_TIMING_CON0 (0x2414)
+#define GASRC_TIMING_CON1 (0x2418)
+#define GASRC_TIMING_CON2 (0x241c)
+#define GASRC_TIMING_CON3 (0x2420)
+#define GASRC_TIMING_CON4 (0x2424)
+#define GASRC_TIMING_CON5 (0x2428)
+#define A3_A4_TIMING_SEL0 (0x2440)
+#define A3_A4_TIMING_SEL1 (0x2444)
+#define A3_A4_TIMING_SEL2 (0x2448)
+#define A3_A4_TIMING_SEL3 (0x244c)
+#define A3_A4_TIMING_SEL4 (0x2450)
+#define A3_A4_TIMING_SEL5 (0x2454)
+#define A3_A4_TIMING_SEL6 (0x2458)
+#define ASYS_TOP_DEBUG (0x2500)
+#define AFE_DPTX_CON (0x2558)
+#define AFE_DPTX_MON (0x255c)
+#define AFE_ADDA_DL_SRC2_CON0 (0x2d00)
+#define AFE_ADDA_DL_SRC2_CON1 (0x2d04)
+#define AFE_ADDA_TOP_CON0 (0x2d0c)
+#define AFE_ADDA_UL_DL_CON0 (0x2d10)
+#define AFE_ADDA_SRC_DEBUG (0x2d14)
+#define AFE_ADDA_SRC_DEBUG_MON0 (0x2d18)
+#define AFE_ADDA_SRC_DEBUG_MON1 (0x2d20)
+#define AFE_ADDA_PREDIS_CON0 (0x2d24)
+#define AFE_ADDA_PREDIS_CON1 (0x2d28)
+#define AFE_ADDA_PREDIS_CON2 (0x2d2c)
+#define AFE_ADDA_PREDIS_CON3 (0x2d30)
+#define AFE_ADDA_DL_SDM_DCCOMP_CON (0x2d34)
+#define AFE_ADDA_DL_SDM_TEST (0x2d38)
+#define AFE_ADDA_DL_DC_COMP_CFG0 (0x2d3c)
+#define AFE_ADDA_DL_DC_COMP_CFG1 (0x2d40)
+#define AFE_ADDA_DL_SDM_FIFO_MON (0x2d44)
+#define AFE_ADDA_DL_SRC_LCH_MON (0x2d50)
+#define AFE_ADDA_DL_SRC_RCH_MON (0x2d54)
+#define AFE_ADDA_DL_SDM_OUT_MON (0x2d58)
+#define AFE_ADDA_DL_SDM_DITHER_CON (0x2d5c)
+#define AFE_ADDA_DL_SDM_AUTO_RESET_CON (0x2d60)
+#define AFE_ADDA_UL_SRC_CON0 (0x2e3c)
+#define AFE_ADDA_UL_SRC_CON1 (0x2e40)
+#define AFE_CONN0 (0x3000)
+#define AFE_CONN0_1 (0x3004)
+#define AFE_CONN0_2 (0x3008)
+#define AFE_CONN0_3 (0x300c)
+#define AFE_CONN0_4 (0x3010)
+#define AFE_CONN1 (0x3014)
+#define AFE_CONN1_1 (0x3018)
+#define AFE_CONN1_2 (0x301c)
+#define AFE_CONN1_3 (0x3020)
+#define AFE_CONN1_4 (0x3024)
+#define AFE_CONN2 (0x3028)
+#define AFE_CONN2_1 (0x302c)
+#define AFE_CONN2_2 (0x3030)
+#define AFE_CONN2_3 (0x3034)
+#define AFE_CONN2_4 (0x3038)
+#define AFE_CONN3 (0x303c)
+#define AFE_CONN3_1 (0x3040)
+#define AFE_CONN3_2 (0x3044)
+#define AFE_CONN3_3 (0x3048)
+#define AFE_CONN3_4 (0x304c)
+#define AFE_CONN4 (0x3050)
+#define AFE_CONN4_1 (0x3054)
+#define AFE_CONN4_2 (0x3058)
+#define AFE_CONN4_3 (0x305c)
+#define AFE_CONN4_4 (0x3060)
+#define AFE_CONN5 (0x3064)
+#define AFE_CONN5_1 (0x3068)
+#define AFE_CONN5_2 (0x306c)
+#define AFE_CONN5_3 (0x3070)
+#define AFE_CONN5_4 (0x3074)
+#define AFE_CONN6 (0x3078)
+#define AFE_CONN6_1 (0x307c)
+#define AFE_CONN6_2 (0x3080)
+#define AFE_CONN6_3 (0x3084)
+#define AFE_CONN6_4 (0x3088)
+#define AFE_CONN7 (0x308c)
+#define AFE_CONN7_1 (0x3090)
+#define AFE_CONN7_2 (0x3094)
+#define AFE_CONN7_3 (0x3098)
+#define AFE_CONN7_4 (0x309c)
+#define AFE_CONN8 (0x30a0)
+#define AFE_CONN8_1 (0x30a4)
+#define AFE_CONN8_2 (0x30a8)
+#define AFE_CONN8_3 (0x30ac)
+#define AFE_CONN8_4 (0x30b0)
+#define AFE_CONN9 (0x30b4)
+#define AFE_CONN9_1 (0x30b8)
+#define AFE_CONN9_2 (0x30bc)
+#define AFE_CONN9_3 (0x30c0)
+#define AFE_CONN9_4 (0x30c4)
+#define AFE_CONN10 (0x30c8)
+#define AFE_CONN10_1 (0x30cc)
+#define AFE_CONN10_2 (0x30d0)
+#define AFE_CONN10_3 (0x30d4)
+#define AFE_CONN10_4 (0x30d8)
+#define AFE_CONN11 (0x30dc)
+#define AFE_CONN11_1 (0x30e0)
+#define AFE_CONN11_2 (0x30e4)
+#define AFE_CONN11_3 (0x30e8)
+#define AFE_CONN11_4 (0x30ec)
+#define AFE_CONN12 (0x30f0)
+#define AFE_CONN12_1 (0x30f4)
+#define AFE_CONN12_2 (0x30f8)
+#define AFE_CONN12_3 (0x30fc)
+#define AFE_CONN12_4 (0x3100)
+#define AFE_CONN13 (0x3104)
+#define AFE_CONN13_1 (0x3108)
+#define AFE_CONN13_2 (0x310c)
+#define AFE_CONN13_3 (0x3110)
+#define AFE_CONN13_4 (0x3114)
+#define AFE_CONN14 (0x3118)
+#define AFE_CONN14_1 (0x311c)
+#define AFE_CONN14_2 (0x3120)
+#define AFE_CONN14_3 (0x3124)
+#define AFE_CONN14_4 (0x3128)
+#define AFE_CONN15 (0x312c)
+#define AFE_CONN15_1 (0x3130)
+#define AFE_CONN15_2 (0x3134)
+#define AFE_CONN15_3 (0x3138)
+#define AFE_CONN15_4 (0x313c)
+#define AFE_CONN16 (0x3140)
+#define AFE_CONN16_1 (0x3144)
+#define AFE_CONN16_2 (0x3148)
+#define AFE_CONN16_3 (0x314c)
+#define AFE_CONN16_4 (0x3150)
+#define AFE_CONN17 (0x3154)
+#define AFE_CONN17_1 (0x3158)
+#define AFE_CONN17_2 (0x315c)
+#define AFE_CONN17_3 (0x3160)
+#define AFE_CONN17_4 (0x3164)
+#define AFE_CONN18 (0x3168)
+#define AFE_CONN18_1 (0x316c)
+#define AFE_CONN18_2 (0x3170)
+#define AFE_CONN18_3 (0x3174)
+#define AFE_CONN18_4 (0x3178)
+#define AFE_CONN19 (0x317c)
+#define AFE_CONN19_1 (0x3180)
+#define AFE_CONN19_2 (0x3184)
+#define AFE_CONN19_3 (0x3188)
+#define AFE_CONN19_4 (0x318c)
+#define AFE_CONN20 (0x3190)
+#define AFE_CONN20_1 (0x3194)
+#define AFE_CONN20_2 (0x3198)
+#define AFE_CONN20_3 (0x319c)
+#define AFE_CONN20_4 (0x31a0)
+#define AFE_CONN21 (0x31a4)
+#define AFE_CONN21_1 (0x31a8)
+#define AFE_CONN21_2 (0x31ac)
+#define AFE_CONN21_3 (0x31b0)
+#define AFE_CONN21_4 (0x31b4)
+#define AFE_CONN22 (0x31b8)
+#define AFE_CONN22_1 (0x31bc)
+#define AFE_CONN22_2 (0x31c0)
+#define AFE_CONN22_3 (0x31c4)
+#define AFE_CONN22_4 (0x31c8)
+#define AFE_CONN23 (0x31cc)
+#define AFE_CONN23_1 (0x31d0)
+#define AFE_CONN23_2 (0x31d4)
+#define AFE_CONN23_3 (0x31d8)
+#define AFE_CONN23_4 (0x31dc)
+#define AFE_CONN24 (0x31e0)
+#define AFE_CONN24_1 (0x31e4)
+#define AFE_CONN24_2 (0x31e8)
+#define AFE_CONN24_3 (0x31ec)
+#define AFE_CONN24_4 (0x31f0)
+#define AFE_CONN25 (0x31f4)
+#define AFE_CONN25_1 (0x31f8)
+#define AFE_CONN25_2 (0x31fc)
+#define AFE_CONN25_3 (0x3200)
+#define AFE_CONN25_4 (0x3204)
+#define AFE_CONN26 (0x3208)
+#define AFE_CONN26_1 (0x320c)
+#define AFE_CONN26_2 (0x3210)
+#define AFE_CONN26_3 (0x3214)
+#define AFE_CONN26_4 (0x3218)
+#define AFE_CONN27 (0x321c)
+#define AFE_CONN27_1 (0x3220)
+#define AFE_CONN27_2 (0x3224)
+#define AFE_CONN27_3 (0x3228)
+#define AFE_CONN27_4 (0x322c)
+#define AFE_CONN28 (0x3230)
+#define AFE_CONN28_1 (0x3234)
+#define AFE_CONN28_2 (0x3238)
+#define AFE_CONN28_3 (0x323c)
+#define AFE_CONN28_4 (0x3240)
+#define AFE_CONN29 (0x3244)
+#define AFE_CONN29_1 (0x3248)
+#define AFE_CONN29_2 (0x324c)
+#define AFE_CONN29_3 (0x3250)
+#define AFE_CONN29_4 (0x3254)
+#define AFE_CONN30 (0x3258)
+#define AFE_CONN30_1 (0x325c)
+#define AFE_CONN30_2 (0x3260)
+#define AFE_CONN30_3 (0x3264)
+#define AFE_CONN30_4 (0x3268)
+#define AFE_CONN31 (0x326c)
+#define AFE_CONN31_1 (0x3270)
+#define AFE_CONN31_2 (0x3274)
+#define AFE_CONN31_3 (0x3278)
+#define AFE_CONN31_4 (0x327c)
+#define AFE_CONN32 (0x3280)
+#define AFE_CONN32_1 (0x3284)
+#define AFE_CONN32_2 (0x3288)
+#define AFE_CONN32_3 (0x328c)
+#define AFE_CONN32_4 (0x3290)
+#define AFE_CONN33 (0x3294)
+#define AFE_CONN33_1 (0x3298)
+#define AFE_CONN33_2 (0x329c)
+#define AFE_CONN33_3 (0x32a0)
+#define AFE_CONN33_4 (0x32a4)
+#define AFE_CONN34 (0x32a8)
+#define AFE_CONN34_1 (0x32ac)
+#define AFE_CONN34_2 (0x32b0)
+#define AFE_CONN34_3 (0x32b4)
+#define AFE_CONN34_4 (0x32b8)
+#define AFE_CONN35 (0x32bc)
+#define AFE_CONN35_1 (0x32c0)
+#define AFE_CONN35_2 (0x32c4)
+#define AFE_CONN35_3 (0x32c8)
+#define AFE_CONN35_4 (0x32cc)
+#define AFE_CONN36 (0x32d0)
+#define AFE_CONN36_1 (0x32d4)
+#define AFE_CONN36_2 (0x32d8)
+#define AFE_CONN36_3 (0x32dc)
+#define AFE_CONN36_4 (0x32e0)
+#define AFE_CONN37 (0x32e4)
+#define AFE_CONN37_1 (0x32e8)
+#define AFE_CONN37_2 (0x32ec)
+#define AFE_CONN37_3 (0x32f0)
+#define AFE_CONN37_4 (0x32f4)
+#define AFE_CONN38 (0x32f8)
+#define AFE_CONN38_1 (0x32fc)
+#define AFE_CONN38_2 (0x3300)
+#define AFE_CONN38_3 (0x3304)
+#define AFE_CONN38_4 (0x3308)
+#define AFE_CONN39 (0x330c)
+#define AFE_CONN39_1 (0x3310)
+#define AFE_CONN39_2 (0x3314)
+#define AFE_CONN39_3 (0x3318)
+#define AFE_CONN39_4 (0x331c)
+#define AFE_CONN40 (0x3320)
+#define AFE_CONN40_1 (0x3324)
+#define AFE_CONN40_2 (0x3328)
+#define AFE_CONN40_3 (0x332c)
+#define AFE_CONN40_4 (0x3330)
+#define AFE_CONN41 (0x3334)
+#define AFE_CONN41_1 (0x3338)
+#define AFE_CONN41_2 (0x333c)
+#define AFE_CONN41_3 (0x3340)
+#define AFE_CONN41_4 (0x3344)
+#define AFE_CONN42 (0x3348)
+#define AFE_CONN42_1 (0x334c)
+#define AFE_CONN42_2 (0x3350)
+#define AFE_CONN42_3 (0x3354)
+#define AFE_CONN42_4 (0x3358)
+#define AFE_CONN43 (0x335c)
+#define AFE_CONN43_1 (0x3360)
+#define AFE_CONN43_2 (0x3364)
+#define AFE_CONN43_3 (0x3368)
+#define AFE_CONN43_4 (0x336c)
+#define AFE_CONN44 (0x3370)
+#define AFE_CONN44_1 (0x3374)
+#define AFE_CONN44_2 (0x3378)
+#define AFE_CONN44_3 (0x337c)
+#define AFE_CONN44_4 (0x3380)
+#define AFE_CONN45 (0x3384)
+#define AFE_CONN45_1 (0x3388)
+#define AFE_CONN45_2 (0x338c)
+#define AFE_CONN45_3 (0x3390)
+#define AFE_CONN45_4 (0x3394)
+#define AFE_CONN46 (0x3398)
+#define AFE_CONN46_1 (0x339c)
+#define AFE_CONN46_2 (0x33a0)
+#define AFE_CONN46_3 (0x33a4)
+#define AFE_CONN46_4 (0x33a8)
+#define AFE_CONN47 (0x33ac)
+#define AFE_CONN47_1 (0x33b0)
+#define AFE_CONN47_2 (0x33b4)
+#define AFE_CONN47_3 (0x33b8)
+#define AFE_CONN47_4 (0x33bc)
+#define AFE_CONN48 (0x33c0)
+#define AFE_CONN48_1 (0x33c4)
+#define AFE_CONN48_2 (0x33c8)
+#define AFE_CONN48_3 (0x33cc)
+#define AFE_CONN48_4 (0x33d0)
+#define AFE_CONN49 (0x33d4)
+#define AFE_CONN49_1 (0x33d8)
+#define AFE_CONN49_2 (0x33dc)
+#define AFE_CONN49_3 (0x33e0)
+#define AFE_CONN49_4 (0x33e4)
+#define AFE_CONN50 (0x33e8)
+#define AFE_CONN50_1 (0x33ec)
+#define AFE_CONN50_2 (0x33f0)
+#define AFE_CONN50_3 (0x33f4)
+#define AFE_CONN50_4 (0x33f8)
+#define AFE_CONN51 (0x33fc)
+#define AFE_CONN51_1 (0x3400)
+#define AFE_CONN51_2 (0x3404)
+#define AFE_CONN51_3 (0x3408)
+#define AFE_CONN51_4 (0x340c)
+#define AFE_CONN52 (0x3410)
+#define AFE_CONN52_1 (0x3414)
+#define AFE_CONN52_2 (0x3418)
+#define AFE_CONN52_3 (0x341c)
+#define AFE_CONN52_4 (0x3420)
+#define AFE_CONN53 (0x3424)
+#define AFE_CONN53_1 (0x3428)
+#define AFE_CONN53_2 (0x342c)
+#define AFE_CONN53_3 (0x3430)
+#define AFE_CONN53_4 (0x3434)
+#define AFE_CONN54 (0x3438)
+#define AFE_CONN54_1 (0x343c)
+#define AFE_CONN54_2 (0x3440)
+#define AFE_CONN54_3 (0x3444)
+#define AFE_CONN54_4 (0x3448)
+#define AFE_CONN55 (0x344c)
+#define AFE_CONN55_1 (0x3450)
+#define AFE_CONN55_2 (0x3454)
+#define AFE_CONN55_3 (0x3458)
+#define AFE_CONN55_4 (0x345c)
+#define AFE_CONN56 (0x3460)
+#define AFE_CONN56_1 (0x3464)
+#define AFE_CONN56_2 (0x3468)
+#define AFE_CONN56_3 (0x346c)
+#define AFE_CONN56_4 (0x3470)
+#define AFE_CONN57 (0x3474)
+#define AFE_CONN57_1 (0x3478)
+#define AFE_CONN57_2 (0x347c)
+#define AFE_CONN57_3 (0x3480)
+#define AFE_CONN57_4 (0x3484)
+#define AFE_CONN58 (0x3488)
+#define AFE_CONN58_1 (0x348c)
+#define AFE_CONN58_2 (0x3490)
+#define AFE_CONN58_3 (0x3494)
+#define AFE_CONN58_4 (0x3498)
+#define AFE_CONN59 (0x349c)
+#define AFE_CONN59_1 (0x34a0)
+#define AFE_CONN59_2 (0x34a4)
+#define AFE_CONN59_3 (0x34a8)
+#define AFE_CONN59_4 (0x34ac)
+#define AFE_CONN60 (0x34b0)
+#define AFE_CONN60_1 (0x34b4)
+#define AFE_CONN60_2 (0x34b8)
+#define AFE_CONN60_3 (0x34bc)
+#define AFE_CONN60_4 (0x34c0)
+#define AFE_CONN61 (0x34c4)
+#define AFE_CONN61_1 (0x34c8)
+#define AFE_CONN61_2 (0x34cc)
+#define AFE_CONN61_3 (0x34d0)
+#define AFE_CONN61_4 (0x34d4)
+#define AFE_CONN62 (0x34d8)
+#define AFE_CONN62_1 (0x34dc)
+#define AFE_CONN62_2 (0x34e0)
+#define AFE_CONN62_3 (0x34e4)
+#define AFE_CONN62_4 (0x34e8)
+#define AFE_CONN63 (0x34ec)
+#define AFE_CONN63_1 (0x34f0)
+#define AFE_CONN63_2 (0x34f4)
+#define AFE_CONN63_3 (0x34f8)
+#define AFE_CONN63_4 (0x34fc)
+#define AFE_CONN64 (0x3500)
+#define AFE_CONN64_1 (0x3504)
+#define AFE_CONN64_2 (0x3508)
+#define AFE_CONN64_3 (0x350c)
+#define AFE_CONN64_4 (0x3510)
+#define AFE_CONN65 (0x3514)
+#define AFE_CONN65_1 (0x3518)
+#define AFE_CONN65_2 (0x351c)
+#define AFE_CONN65_3 (0x3520)
+#define AFE_CONN65_4 (0x3524)
+#define AFE_CONN66 (0x3528)
+#define AFE_CONN66_1 (0x352c)
+#define AFE_CONN66_2 (0x3530)
+#define AFE_CONN66_3 (0x3534)
+#define AFE_CONN66_4 (0x3538)
+#define AFE_CONN67 (0x353c)
+#define AFE_CONN67_1 (0x3540)
+#define AFE_CONN67_2 (0x3544)
+#define AFE_CONN67_3 (0x3548)
+#define AFE_CONN67_4 (0x354c)
+#define AFE_CONN68 (0x3550)
+#define AFE_CONN68_1 (0x3554)
+#define AFE_CONN68_2 (0x3558)
+#define AFE_CONN68_3 (0x355c)
+#define AFE_CONN68_4 (0x3560)
+#define AFE_CONN69 (0x3564)
+#define AFE_CONN69_1 (0x3568)
+#define AFE_CONN69_2 (0x356c)
+#define AFE_CONN69_3 (0x3570)
+#define AFE_CONN69_4 (0x3574)
+#define AFE_CONN70 (0x3578)
+#define AFE_CONN70_1 (0x357c)
+#define AFE_CONN70_2 (0x3580)
+#define AFE_CONN70_3 (0x3584)
+#define AFE_CONN70_4 (0x3588)
+#define AFE_CONN71 (0x358c)
+#define AFE_CONN71_1 (0x3590)
+#define AFE_CONN71_2 (0x3594)
+#define AFE_CONN71_3 (0x3598)
+#define AFE_CONN71_4 (0x359c)
+#define AFE_CONN72 (0x35a0)
+#define AFE_CONN72_1 (0x35a4)
+#define AFE_CONN72_2 (0x35a8)
+#define AFE_CONN72_3 (0x35ac)
+#define AFE_CONN72_4 (0x35b0)
+#define AFE_CONN73 (0x35b4)
+#define AFE_CONN73_1 (0x35b8)
+#define AFE_CONN73_2 (0x35bc)
+#define AFE_CONN73_3 (0x35c0)
+#define AFE_CONN73_4 (0x35c4)
+#define AFE_CONN74 (0x35c8)
+#define AFE_CONN74_1 (0x35cc)
+#define AFE_CONN74_2 (0x35d0)
+#define AFE_CONN74_3 (0x35d4)
+#define AFE_CONN74_4 (0x35d8)
+#define AFE_CONN75 (0x35dc)
+#define AFE_CONN75_1 (0x35e0)
+#define AFE_CONN75_2 (0x35e4)
+#define AFE_CONN75_3 (0x35e8)
+#define AFE_CONN75_4 (0x35ec)
+#define AFE_CONN76 (0x35f0)
+#define AFE_CONN76_1 (0x35f4)
+#define AFE_CONN76_2 (0x35f8)
+#define AFE_CONN76_3 (0x35fc)
+#define AFE_CONN76_4 (0x3600)
+#define AFE_CONN77 (0x3604)
+#define AFE_CONN77_1 (0x3608)
+#define AFE_CONN77_2 (0x360c)
+#define AFE_CONN77_3 (0x3610)
+#define AFE_CONN77_4 (0x3614)
+#define AFE_CONN78 (0x3618)
+#define AFE_CONN78_1 (0x361c)
+#define AFE_CONN78_2 (0x3620)
+#define AFE_CONN78_3 (0x3624)
+#define AFE_CONN78_4 (0x3628)
+#define AFE_CONN79 (0x362c)
+#define AFE_CONN79_1 (0x3630)
+#define AFE_CONN79_2 (0x3634)
+#define AFE_CONN79_3 (0x3638)
+#define AFE_CONN79_4 (0x363c)
+#define AFE_CONN80 (0x3640)
+#define AFE_CONN80_1 (0x3644)
+#define AFE_CONN80_2 (0x3648)
+#define AFE_CONN80_3 (0x364c)
+#define AFE_CONN80_4 (0x3650)
+#define AFE_CONN81 (0x3654)
+#define AFE_CONN81_1 (0x3658)
+#define AFE_CONN81_2 (0x365c)
+#define AFE_CONN81_3 (0x3660)
+#define AFE_CONN81_4 (0x3664)
+#define AFE_CONN82 (0x3668)
+#define AFE_CONN82_1 (0x366c)
+#define AFE_CONN82_2 (0x3670)
+#define AFE_CONN82_3 (0x3674)
+#define AFE_CONN82_4 (0x3678)
+#define AFE_CONN83 (0x367c)
+#define AFE_CONN83_1 (0x3680)
+#define AFE_CONN83_2 (0x3684)
+#define AFE_CONN83_3 (0x3688)
+#define AFE_CONN83_4 (0x368c)
+#define AFE_CONN84 (0x3690)
+#define AFE_CONN84_1 (0x3694)
+#define AFE_CONN84_2 (0x3698)
+#define AFE_CONN84_3 (0x369c)
+#define AFE_CONN84_4 (0x36a0)
+#define AFE_CONN85 (0x36a4)
+#define AFE_CONN85_1 (0x36a8)
+#define AFE_CONN85_2 (0x36ac)
+#define AFE_CONN85_3 (0x36b0)
+#define AFE_CONN85_4 (0x36b4)
+#define AFE_CONN86 (0x36b8)
+#define AFE_CONN86_1 (0x36bc)
+#define AFE_CONN86_2 (0x36c0)
+#define AFE_CONN86_3 (0x36c4)
+#define AFE_CONN86_4 (0x36c8)
+#define AFE_CONN87 (0x36cc)
+#define AFE_CONN87_1 (0x36d0)
+#define AFE_CONN87_2 (0x36d4)
+#define AFE_CONN87_3 (0x36d8)
+#define AFE_CONN87_4 (0x36dc)
+#define AFE_CONN88 (0x36e0)
+#define AFE_CONN88_1 (0x36e4)
+#define AFE_CONN88_2 (0x36e8)
+#define AFE_CONN88_3 (0x36ec)
+#define AFE_CONN88_4 (0x36f0)
+#define AFE_CONN89 (0x36f4)
+#define AFE_CONN89_1 (0x36f8)
+#define AFE_CONN89_2 (0x36fc)
+#define AFE_CONN89_3 (0x3700)
+#define AFE_CONN89_4 (0x3704)
+#define AFE_CONN90 (0x3708)
+#define AFE_CONN90_1 (0x370c)
+#define AFE_CONN90_2 (0x3710)
+#define AFE_CONN90_3 (0x3714)
+#define AFE_CONN90_4 (0x3718)
+#define AFE_CONN91 (0x371c)
+#define AFE_CONN91_1 (0x3720)
+#define AFE_CONN91_2 (0x3724)
+#define AFE_CONN91_3 (0x3728)
+#define AFE_CONN91_4 (0x372c)
+#define AFE_CONN92 (0x3730)
+#define AFE_CONN92_1 (0x3734)
+#define AFE_CONN92_2 (0x3738)
+#define AFE_CONN92_3 (0x373c)
+#define AFE_CONN92_4 (0x3740)
+#define AFE_CONN93 (0x3744)
+#define AFE_CONN93_1 (0x3748)
+#define AFE_CONN93_2 (0x374c)
+#define AFE_CONN93_3 (0x3750)
+#define AFE_CONN93_4 (0x3754)
+#define AFE_CONN94 (0x3758)
+#define AFE_CONN94_1 (0x375c)
+#define AFE_CONN94_2 (0x3760)
+#define AFE_CONN94_3 (0x3764)
+#define AFE_CONN94_4 (0x3768)
+#define AFE_CONN95 (0x376c)
+#define AFE_CONN95_1 (0x3770)
+#define AFE_CONN95_2 (0x3774)
+#define AFE_CONN95_3 (0x3778)
+#define AFE_CONN95_4 (0x377c)
+#define AFE_CONN96 (0x3780)
+#define AFE_CONN96_1 (0x3784)
+#define AFE_CONN96_2 (0x3788)
+#define AFE_CONN96_3 (0x378c)
+#define AFE_CONN96_4 (0x3790)
+#define AFE_CONN97 (0x3794)
+#define AFE_CONN97_1 (0x3798)
+#define AFE_CONN97_2 (0x379c)
+#define AFE_CONN97_3 (0x37a0)
+#define AFE_CONN97_4 (0x37a4)
+#define AFE_CONN98 (0x37a8)
+#define AFE_CONN98_1 (0x37ac)
+#define AFE_CONN98_2 (0x37b0)
+#define AFE_CONN98_3 (0x37b4)
+#define AFE_CONN98_4 (0x37b8)
+#define AFE_CONN99 (0x37bc)
+#define AFE_CONN99_1 (0x37c0)
+#define AFE_CONN99_2 (0x37c4)
+#define AFE_CONN99_3 (0x37c8)
+#define AFE_CONN99_4 (0x37cc)
+#define AFE_CONN100 (0x37d0)
+#define AFE_CONN100_1 (0x37d4)
+#define AFE_CONN100_2 (0x37d8)
+#define AFE_CONN100_3 (0x37dc)
+#define AFE_CONN100_4 (0x37e0)
+#define AFE_CONN101 (0x37e4)
+#define AFE_CONN101_1 (0x37e8)
+#define AFE_CONN101_2 (0x37ec)
+#define AFE_CONN101_3 (0x37f0)
+#define AFE_CONN101_4 (0x37f4)
+#define AFE_CONN102 (0x37f8)
+#define AFE_CONN102_1 (0x37fc)
+#define AFE_CONN102_2 (0x3800)
+#define AFE_CONN102_3 (0x3804)
+#define AFE_CONN102_4 (0x3808)
+#define AFE_CONN103 (0x380c)
+#define AFE_CONN103_1 (0x3810)
+#define AFE_CONN103_2 (0x3814)
+#define AFE_CONN103_3 (0x3818)
+#define AFE_CONN103_4 (0x381c)
+#define AFE_CONN104 (0x3820)
+#define AFE_CONN104_1 (0x3824)
+#define AFE_CONN104_2 (0x3828)
+#define AFE_CONN104_3 (0x382c)
+#define AFE_CONN104_4 (0x3830)
+#define AFE_CONN105 (0x3834)
+#define AFE_CONN105_1 (0x3838)
+#define AFE_CONN105_2 (0x383c)
+#define AFE_CONN105_3 (0x3840)
+#define AFE_CONN105_4 (0x3844)
+#define AFE_CONN106 (0x3848)
+#define AFE_CONN106_1 (0x384c)
+#define AFE_CONN106_2 (0x3850)
+#define AFE_CONN106_3 (0x3854)
+#define AFE_CONN106_4 (0x3858)
+#define AFE_CONN107 (0x385c)
+#define AFE_CONN107_1 (0x3860)
+#define AFE_CONN107_2 (0x3864)
+#define AFE_CONN107_3 (0x3868)
+#define AFE_CONN107_4 (0x386c)
+#define AFE_CONN108 (0x3870)
+#define AFE_CONN108_1 (0x3874)
+#define AFE_CONN108_2 (0x3878)
+#define AFE_CONN108_3 (0x387c)
+#define AFE_CONN108_4 (0x3880)
+#define AFE_CONN109 (0x3884)
+#define AFE_CONN109_1 (0x3888)
+#define AFE_CONN109_2 (0x388c)
+#define AFE_CONN109_3 (0x3890)
+#define AFE_CONN109_4 (0x3894)
+#define AFE_CONN110 (0x3898)
+#define AFE_CONN110_1 (0x389c)
+#define AFE_CONN110_2 (0x38a0)
+#define AFE_CONN110_3 (0x38a4)
+#define AFE_CONN110_4 (0x38a8)
+#define AFE_CONN111 (0x38ac)
+#define AFE_CONN111_1 (0x38b0)
+#define AFE_CONN111_2 (0x38b4)
+#define AFE_CONN111_3 (0x38b8)
+#define AFE_CONN111_4 (0x38bc)
+#define AFE_CONN112 (0x38c0)
+#define AFE_CONN112_1 (0x38c4)
+#define AFE_CONN112_2 (0x38c8)
+#define AFE_CONN112_3 (0x38cc)
+#define AFE_CONN112_4 (0x38d0)
+#define AFE_CONN113 (0x38d4)
+#define AFE_CONN113_1 (0x38d8)
+#define AFE_CONN113_2 (0x38dc)
+#define AFE_CONN113_3 (0x38e0)
+#define AFE_CONN113_4 (0x38e4)
+#define AFE_CONN114 (0x38e8)
+#define AFE_CONN114_1 (0x38ec)
+#define AFE_CONN114_2 (0x38f0)
+#define AFE_CONN114_3 (0x38f4)
+#define AFE_CONN114_4 (0x38f8)
+#define AFE_CONN115 (0x38fc)
+#define AFE_CONN115_1 (0x3900)
+#define AFE_CONN115_2 (0x3904)
+#define AFE_CONN115_3 (0x3908)
+#define AFE_CONN115_4 (0x390c)
+#define AFE_CONN116 (0x3910)
+#define AFE_CONN116_1 (0x3914)
+#define AFE_CONN116_2 (0x3918)
+#define AFE_CONN116_3 (0x391c)
+#define AFE_CONN116_4 (0x3920)
+#define AFE_CONN117 (0x3924)
+#define AFE_CONN117_1 (0x3928)
+#define AFE_CONN117_2 (0x392c)
+#define AFE_CONN117_3 (0x3930)
+#define AFE_CONN117_4 (0x3934)
+#define AFE_CONN118 (0x3938)
+#define AFE_CONN118_1 (0x393c)
+#define AFE_CONN118_2 (0x3940)
+#define AFE_CONN118_3 (0x3944)
+#define AFE_CONN118_4 (0x3948)
+#define AFE_CONN119 (0x394c)
+#define AFE_CONN119_1 (0x3950)
+#define AFE_CONN119_2 (0x3954)
+#define AFE_CONN119_3 (0x3958)
+#define AFE_CONN119_4 (0x395c)
+#define AFE_CONN120 (0x3960)
+#define AFE_CONN120_1 (0x3964)
+#define AFE_CONN120_2 (0x3968)
+#define AFE_CONN120_3 (0x396c)
+#define AFE_CONN120_4 (0x3970)
+#define AFE_CONN121 (0x3974)
+#define AFE_CONN121_1 (0x3978)
+#define AFE_CONN121_2 (0x397c)
+#define AFE_CONN121_3 (0x3980)
+#define AFE_CONN121_4 (0x3984)
+#define AFE_CONN122 (0x3988)
+#define AFE_CONN122_1 (0x398c)
+#define AFE_CONN122_2 (0x3990)
+#define AFE_CONN122_3 (0x3994)
+#define AFE_CONN122_4 (0x3998)
+#define AFE_CONN123 (0x399c)
+#define AFE_CONN123_1 (0x39a0)
+#define AFE_CONN123_2 (0x39a4)
+#define AFE_CONN123_3 (0x39a8)
+#define AFE_CONN123_4 (0x39ac)
+#define AFE_CONN124 (0x39b0)
+#define AFE_CONN124_1 (0x39b4)
+#define AFE_CONN124_2 (0x39b8)
+#define AFE_CONN124_3 (0x39bc)
+#define AFE_CONN124_4 (0x39c0)
+#define AFE_CONN125 (0x39c4)
+#define AFE_CONN125_1 (0x39c8)
+#define AFE_CONN125_2 (0x39cc)
+#define AFE_CONN125_3 (0x39d0)
+#define AFE_CONN125_4 (0x39d4)
+#define AFE_CONN126 (0x39d8)
+#define AFE_CONN126_1 (0x39dc)
+#define AFE_CONN126_2 (0x39e0)
+#define AFE_CONN126_3 (0x39e4)
+#define AFE_CONN126_4 (0x39e8)
+#define AFE_CONN127 (0x39ec)
+#define AFE_CONN127_1 (0x39f0)
+#define AFE_CONN127_2 (0x39f4)
+#define AFE_CONN127_3 (0x39f8)
+#define AFE_CONN127_4 (0x39fc)
+#define AFE_CONN128 (0x3a00)
+#define AFE_CONN128_1 (0x3a04)
+#define AFE_CONN128_2 (0x3a08)
+#define AFE_CONN128_3 (0x3a0c)
+#define AFE_CONN128_4 (0x3a10)
+#define AFE_CONN129 (0x3a14)
+#define AFE_CONN129_1 (0x3a18)
+#define AFE_CONN129_2 (0x3a1c)
+#define AFE_CONN129_3 (0x3a20)
+#define AFE_CONN129_4 (0x3a24)
+#define AFE_CONN130 (0x3a28)
+#define AFE_CONN130_1 (0x3a2c)
+#define AFE_CONN130_2 (0x3a30)
+#define AFE_CONN130_3 (0x3a34)
+#define AFE_CONN130_4 (0x3a38)
+#define AFE_CONN131 (0x3a3c)
+#define AFE_CONN131_1 (0x3a40)
+#define AFE_CONN131_2 (0x3a44)
+#define AFE_CONN131_3 (0x3a48)
+#define AFE_CONN131_4 (0x3a4c)
+#define AFE_CONN132 (0x3a50)
+#define AFE_CONN132_1 (0x3a54)
+#define AFE_CONN132_2 (0x3a58)
+#define AFE_CONN132_3 (0x3a5c)
+#define AFE_CONN132_4 (0x3a60)
+#define AFE_CONN133 (0x3a64)
+#define AFE_CONN133_1 (0x3a68)
+#define AFE_CONN133_2 (0x3a6c)
+#define AFE_CONN133_3 (0x3a70)
+#define AFE_CONN133_4 (0x3a74)
+#define AFE_CONN134 (0x3a78)
+#define AFE_CONN134_1 (0x3a7c)
+#define AFE_CONN134_2 (0x3a80)
+#define AFE_CONN134_3 (0x3a84)
+#define AFE_CONN134_4 (0x3a88)
+#define AFE_CONN135 (0x3a8c)
+#define AFE_CONN135_1 (0x3a90)
+#define AFE_CONN135_2 (0x3a94)
+#define AFE_CONN135_3 (0x3a98)
+#define AFE_CONN135_4 (0x3a9c)
+#define AFE_CONN136 (0x3aa0)
+#define AFE_CONN136_1 (0x3aa4)
+#define AFE_CONN136_2 (0x3aa8)
+#define AFE_CONN136_3 (0x3aac)
+#define AFE_CONN136_4 (0x3ab0)
+#define AFE_CONN137 (0x3ab4)
+#define AFE_CONN137_1 (0x3ab8)
+#define AFE_CONN137_2 (0x3abc)
+#define AFE_CONN137_3 (0x3ac0)
+#define AFE_CONN137_4 (0x3ac4)
+#define AFE_CONN138 (0x3ac8)
+#define AFE_CONN138_1 (0x3acc)
+#define AFE_CONN138_2 (0x3ad0)
+#define AFE_CONN138_3 (0x3ad4)
+#define AFE_CONN138_4 (0x3ad8)
+#define AFE_CONN139 (0x3adc)
+#define AFE_CONN139_1 (0x3ae0)
+#define AFE_CONN139_2 (0x3ae4)
+#define AFE_CONN139_3 (0x3ae8)
+#define AFE_CONN139_4 (0x3aec)
+#define AFE_CONN_RS (0x3af0)
+#define AFE_CONN_RS_1 (0x3af4)
+#define AFE_CONN_RS_2 (0x3af8)
+#define AFE_CONN_RS_3 (0x3afc)
+#define AFE_CONN_RS_4 (0x3b00)
+#define AFE_CONN_16BIT (0x3b04)
+#define AFE_CONN_16BIT_1 (0x3b08)
+#define AFE_CONN_16BIT_2 (0x3b0c)
+#define AFE_CONN_16BIT_3 (0x3b10)
+#define AFE_CONN_16BIT_4 (0x3b14)
+#define AFE_CONN_24BIT (0x3b18)
+#define AFE_CONN_24BIT_1 (0x3b1c)
+#define AFE_CONN_24BIT_2 (0x3b20)
+#define AFE_CONN_24BIT_3 (0x3b24)
+#define AFE_CONN_24BIT_4 (0x3b28)
+#define AFE_CONN_DI (0x3b2c)
+#define AFE_CONN_DI_1 (0x3b30)
+#define AFE_CONN_DI_2 (0x3b34)
+#define AFE_CONN_DI_3 (0x3b38)
+#define AFE_CONN_DI_4 (0x3b3c)
+#define AFE_CONN176 (0x3ea0)
+#define AFE_CONN176_1 (0x3ea4)
+#define AFE_CONN176_2 (0x3ea8)
+#define AFE_CONN176_3 (0x3eac)
+#define AFE_CONN176_4 (0x3eb0)
+#define AFE_CONN176_5 (0x3eb4)
+#define AFE_CONN177 (0x3eb8)
+#define AFE_CONN177_1 (0x3ebc)
+#define AFE_CONN177_2 (0x3ec0)
+#define AFE_CONN177_3 (0x3ec4)
+#define AFE_CONN177_4 (0x3ec8)
+#define AFE_CONN177_5 (0x3ecc)
+#define AFE_CONN182 (0x3f30)
+#define AFE_CONN182_1 (0x3f34)
+#define AFE_CONN182_2 (0x3f38)
+#define AFE_CONN182_3 (0x3f3c)
+#define AFE_CONN182_4 (0x3f40)
+#define AFE_CONN182_5 (0x3f44)
+#define AFE_CONN183 (0x3f48)
+#define AFE_CONN183_1 (0x3f4c)
+#define AFE_CONN183_2 (0x3f50)
+#define AFE_CONN183_3 (0x3f54)
+#define AFE_CONN183_4 (0x3f58)
+#define AFE_CONN183_5 (0x3f5c)
+#define AFE_SECURE_MASK_CONN0 (0x4000)
+#define AFE_SECURE_MASK_CONN0_1 (0x4004)
+#define AFE_SECURE_MASK_CONN0_2 (0x4008)
+#define AFE_SECURE_MASK_CONN0_3 (0x400c)
+#define AFE_SECURE_MASK_CONN0_4 (0x4010)
+#define AFE_SECURE_MASK_CONN1 (0x4014)
+#define AFE_SECURE_MASK_CONN1_1 (0x4018)
+#define AFE_SECURE_MASK_CONN1_2 (0x401c)
+#define AFE_SECURE_MASK_CONN1_3 (0x4020)
+#define AFE_SECURE_MASK_CONN1_4 (0x4024)
+#define AFE_SECURE_MASK_CONN2 (0x4028)
+#define AFE_SECURE_MASK_CONN2_1 (0x402c)
+#define AFE_SECURE_MASK_CONN2_2 (0x4030)
+#define AFE_SECURE_MASK_CONN2_3 (0x4034)
+#define AFE_SECURE_MASK_CONN2_4 (0x4038)
+#define AFE_SECURE_MASK_CONN3 (0x403c)
+#define AFE_SECURE_MASK_CONN3_1 (0x4040)
+#define AFE_SECURE_MASK_CONN3_2 (0x4044)
+#define AFE_SECURE_MASK_CONN3_3 (0x4048)
+#define AFE_SECURE_MASK_CONN3_4 (0x404c)
+#define AFE_SECURE_MASK_CONN4 (0x4050)
+#define AFE_SECURE_MASK_CONN4_1 (0x4054)
+#define AFE_SECURE_MASK_CONN4_2 (0x4058)
+#define AFE_SECURE_MASK_CONN4_3 (0x405c)
+#define AFE_SECURE_MASK_CONN4_4 (0x4060)
+#define AFE_SECURE_MASK_CONN5 (0x4064)
+#define AFE_SECURE_MASK_CONN5_1 (0x4068)
+#define AFE_SECURE_MASK_CONN5_2 (0x406c)
+#define AFE_SECURE_MASK_CONN5_3 (0x4070)
+#define AFE_SECURE_MASK_CONN5_4 (0x4074)
+#define AFE_SECURE_MASK_CONN6 (0x4078)
+#define AFE_SECURE_MASK_CONN6_1 (0x407c)
+#define AFE_SECURE_MASK_CONN6_2 (0x4080)
+#define AFE_SECURE_MASK_CONN6_3 (0x4084)
+#define AFE_SECURE_MASK_CONN6_4 (0x4088)
+#define AFE_SECURE_MASK_CONN7 (0x408c)
+#define AFE_SECURE_MASK_CONN7_1 (0x4090)
+#define AFE_SECURE_MASK_CONN7_2 (0x4094)
+#define AFE_SECURE_MASK_CONN7_3 (0x4098)
+#define AFE_SECURE_MASK_CONN7_4 (0x409c)
+#define AFE_SECURE_MASK_CONN8 (0x40a0)
+#define AFE_SECURE_MASK_CONN8_1 (0x40a4)
+#define AFE_SECURE_MASK_CONN8_2 (0x40a8)
+#define AFE_SECURE_MASK_CONN8_3 (0x40ac)
+#define AFE_SECURE_MASK_CONN8_4 (0x40b0)
+#define AFE_SECURE_MASK_CONN9 (0x40b4)
+#define AFE_SECURE_MASK_CONN9_1 (0x40b8)
+#define AFE_SECURE_MASK_CONN9_2 (0x40bc)
+#define AFE_SECURE_MASK_CONN9_3 (0x40c0)
+#define AFE_SECURE_MASK_CONN9_4 (0x40c4)
+#define AFE_SECURE_MASK_CONN10 (0x40c8)
+#define AFE_SECURE_MASK_CONN10_1 (0x40cc)
+#define AFE_SECURE_MASK_CONN10_2 (0x40d0)
+#define AFE_SECURE_MASK_CONN10_3 (0x40d4)
+#define AFE_SECURE_MASK_CONN10_4 (0x40d8)
+#define AFE_SECURE_MASK_CONN11 (0x40dc)
+#define AFE_SECURE_MASK_CONN11_1 (0x40e0)
+#define AFE_SECURE_MASK_CONN11_2 (0x40e4)
+#define AFE_SECURE_MASK_CONN11_3 (0x40e8)
+#define AFE_SECURE_MASK_CONN11_4 (0x40ec)
+#define AFE_SECURE_MASK_CONN12 (0x40f0)
+#define AFE_SECURE_MASK_CONN12_1 (0x40f4)
+#define AFE_SECURE_MASK_CONN12_2 (0x40f8)
+#define AFE_SECURE_MASK_CONN12_3 (0x40fc)
+#define AFE_SECURE_MASK_CONN12_4 (0x4100)
+#define AFE_SECURE_MASK_CONN13 (0x4104)
+#define AFE_SECURE_MASK_CONN13_1 (0x4108)
+#define AFE_SECURE_MASK_CONN13_2 (0x410c)
+#define AFE_SECURE_MASK_CONN13_3 (0x4110)
+#define AFE_SECURE_MASK_CONN13_4 (0x4114)
+#define AFE_SECURE_MASK_CONN14 (0x4118)
+#define AFE_SECURE_MASK_CONN14_1 (0x411c)
+#define AFE_SECURE_MASK_CONN14_2 (0x4120)
+#define AFE_SECURE_MASK_CONN14_3 (0x4124)
+#define AFE_SECURE_MASK_CONN14_4 (0x4128)
+#define AFE_SECURE_MASK_CONN15 (0x412c)
+#define AFE_SECURE_MASK_CONN15_1 (0x4130)
+#define AFE_SECURE_MASK_CONN15_2 (0x4134)
+#define AFE_SECURE_MASK_CONN15_3 (0x4138)
+#define AFE_SECURE_MASK_CONN15_4 (0x413c)
+#define AFE_SECURE_MASK_CONN16 (0x4140)
+#define AFE_SECURE_MASK_CONN16_1 (0x4144)
+#define AFE_SECURE_MASK_CONN16_2 (0x4148)
+#define AFE_SECURE_MASK_CONN16_3 (0x414c)
+#define AFE_SECURE_MASK_CONN16_4 (0x4150)
+#define AFE_SECURE_MASK_CONN17 (0x4154)
+#define AFE_SECURE_MASK_CONN17_1 (0x4158)
+#define AFE_SECURE_MASK_CONN17_2 (0x415c)
+#define AFE_SECURE_MASK_CONN17_3 (0x4160)
+#define AFE_SECURE_MASK_CONN17_4 (0x4164)
+#define AFE_SECURE_MASK_CONN18 (0x4168)
+#define AFE_SECURE_MASK_CONN18_1 (0x416c)
+#define AFE_SECURE_MASK_CONN18_2 (0x4170)
+#define AFE_SECURE_MASK_CONN18_3 (0x4174)
+#define AFE_SECURE_MASK_CONN18_4 (0x4178)
+#define AFE_SECURE_MASK_CONN19 (0x417c)
+#define AFE_SECURE_MASK_CONN19_1 (0x4180)
+#define AFE_SECURE_MASK_CONN19_2 (0x4184)
+#define AFE_SECURE_MASK_CONN19_3 (0x4188)
+#define AFE_SECURE_MASK_CONN19_4 (0x418c)
+#define AFE_SECURE_MASK_CONN20 (0x4190)
+#define AFE_SECURE_MASK_CONN20_1 (0x4194)
+#define AFE_SECURE_MASK_CONN20_2 (0x4198)
+#define AFE_SECURE_MASK_CONN20_3 (0x419c)
+#define AFE_SECURE_MASK_CONN20_4 (0x41a0)
+#define AFE_SECURE_MASK_CONN21 (0x41a4)
+#define AFE_SECURE_MASK_CONN21_1 (0x41a8)
+#define AFE_SECURE_MASK_CONN21_2 (0x41ac)
+#define AFE_SECURE_MASK_CONN21_3 (0x41b0)
+#define AFE_SECURE_MASK_CONN21_4 (0x41b4)
+#define AFE_SECURE_MASK_CONN22 (0x41b8)
+#define AFE_SECURE_MASK_CONN22_1 (0x41bc)
+#define AFE_SECURE_MASK_CONN22_2 (0x41c0)
+#define AFE_SECURE_MASK_CONN22_3 (0x41c4)
+#define AFE_SECURE_MASK_CONN22_4 (0x41c8)
+#define AFE_SECURE_MASK_CONN23 (0x41cc)
+#define AFE_SECURE_MASK_CONN23_1 (0x41d0)
+#define AFE_SECURE_MASK_CONN23_2 (0x41d4)
+#define AFE_SECURE_MASK_CONN23_3 (0x41d8)
+#define AFE_SECURE_MASK_CONN23_4 (0x41dc)
+#define AFE_SECURE_MASK_CONN24 (0x41e0)
+#define AFE_SECURE_MASK_CONN24_1 (0x41e4)
+#define AFE_SECURE_MASK_CONN24_2 (0x41e8)
+#define AFE_SECURE_MASK_CONN24_3 (0x41ec)
+#define AFE_SECURE_MASK_CONN24_4 (0x41f0)
+#define AFE_SECURE_MASK_CONN25 (0x41f4)
+#define AFE_SECURE_MASK_CONN25_1 (0x41f8)
+#define AFE_SECURE_MASK_CONN25_2 (0x41fc)
+#define AFE_SECURE_MASK_CONN25_3 (0x4200)
+#define AFE_SECURE_MASK_CONN25_4 (0x4204)
+#define AFE_SECURE_MASK_CONN26 (0x4208)
+#define AFE_SECURE_MASK_CONN26_1 (0x420c)
+#define AFE_SECURE_MASK_CONN26_2 (0x4210)
+#define AFE_SECURE_MASK_CONN26_3 (0x4214)
+#define AFE_SECURE_MASK_CONN26_4 (0x4218)
+#define AFE_SECURE_MASK_CONN27 (0x421c)
+#define AFE_SECURE_MASK_CONN27_1 (0x4220)
+#define AFE_SECURE_MASK_CONN27_2 (0x4224)
+#define AFE_SECURE_MASK_CONN27_3 (0x4228)
+#define AFE_SECURE_MASK_CONN27_4 (0x422c)
+#define AFE_SECURE_MASK_CONN28 (0x4230)
+#define AFE_SECURE_MASK_CONN28_1 (0x4234)
+#define AFE_SECURE_MASK_CONN28_2 (0x4238)
+#define AFE_SECURE_MASK_CONN28_3 (0x423c)
+#define AFE_SECURE_MASK_CONN28_4 (0x4240)
+#define AFE_SECURE_MASK_CONN29 (0x4244)
+#define AFE_SECURE_MASK_CONN29_1 (0x4248)
+#define AFE_SECURE_MASK_CONN29_2 (0x424c)
+#define AFE_SECURE_MASK_CONN29_3 (0x4250)
+#define AFE_SECURE_MASK_CONN29_4 (0x4254)
+#define AFE_SECURE_MASK_CONN30 (0x4258)
+#define AFE_SECURE_MASK_CONN30_1 (0x425c)
+#define AFE_SECURE_MASK_CONN30_2 (0x4260)
+#define AFE_SECURE_MASK_CONN30_3 (0x4264)
+#define AFE_SECURE_MASK_CONN30_4 (0x4268)
+#define AFE_SECURE_MASK_CONN31 (0x426c)
+#define AFE_SECURE_MASK_CONN31_1 (0x4270)
+#define AFE_SECURE_MASK_CONN31_2 (0x4274)
+#define AFE_SECURE_MASK_CONN31_3 (0x4278)
+#define AFE_SECURE_MASK_CONN31_4 (0x427c)
+#define AFE_SECURE_MASK_CONN32 (0x4280)
+#define AFE_SECURE_MASK_CONN32_1 (0x4284)
+#define AFE_SECURE_MASK_CONN32_2 (0x4288)
+#define AFE_SECURE_MASK_CONN32_3 (0x428c)
+#define AFE_SECURE_MASK_CONN32_4 (0x4290)
+#define AFE_SECURE_MASK_CONN33 (0x4294)
+#define AFE_SECURE_MASK_CONN33_1 (0x4298)
+#define AFE_SECURE_MASK_CONN33_2 (0x429c)
+#define AFE_SECURE_MASK_CONN33_3 (0x42a0)
+#define AFE_SECURE_MASK_CONN33_4 (0x42a4)
+#define AFE_SECURE_MASK_CONN34 (0x42a8)
+#define AFE_SECURE_MASK_CONN34_1 (0x42ac)
+#define AFE_SECURE_MASK_CONN34_2 (0x42b0)
+#define AFE_SECURE_MASK_CONN34_3 (0x42b4)
+#define AFE_SECURE_MASK_CONN34_4 (0x42b8)
+#define AFE_SECURE_MASK_CONN35 (0x42bc)
+#define AFE_SECURE_MASK_CONN35_1 (0x42c0)
+#define AFE_SECURE_MASK_CONN35_2 (0x42c4)
+#define AFE_SECURE_MASK_CONN35_3 (0x42c8)
+#define AFE_SECURE_MASK_CONN35_4 (0x42cc)
+#define AFE_SECURE_MASK_CONN36 (0x42d0)
+#define AFE_SECURE_MASK_CONN36_1 (0x42d4)
+#define AFE_SECURE_MASK_CONN36_2 (0x42d8)
+#define AFE_SECURE_MASK_CONN36_3 (0x42dc)
+#define AFE_SECURE_MASK_CONN36_4 (0x42e0)
+#define AFE_SECURE_MASK_CONN37 (0x42e4)
+#define AFE_SECURE_MASK_CONN37_1 (0x42e8)
+#define AFE_SECURE_MASK_CONN37_2 (0x42ec)
+#define AFE_SECURE_MASK_CONN37_3 (0x42f0)
+#define AFE_SECURE_MASK_CONN37_4 (0x42f4)
+#define AFE_SECURE_MASK_CONN38 (0x42f8)
+#define AFE_SECURE_MASK_CONN38_1 (0x42fc)
+#define AFE_SECURE_MASK_CONN38_2 (0x4300)
+#define AFE_SECURE_MASK_CONN38_3 (0x4304)
+#define AFE_SECURE_MASK_CONN38_4 (0x4308)
+#define AFE_SECURE_MASK_CONN39 (0x430c)
+#define AFE_SECURE_MASK_CONN39_1 (0x4310)
+#define AFE_SECURE_MASK_CONN39_2 (0x4314)
+#define AFE_SECURE_MASK_CONN39_3 (0x4318)
+#define AFE_SECURE_MASK_CONN39_4 (0x431c)
+#define AFE_SECURE_MASK_CONN40 (0x4320)
+#define AFE_SECURE_MASK_CONN40_1 (0x4324)
+#define AFE_SECURE_MASK_CONN40_2 (0x4328)
+#define AFE_SECURE_MASK_CONN40_3 (0x432c)
+#define AFE_SECURE_MASK_CONN40_4 (0x4330)
+#define AFE_SECURE_MASK_CONN41 (0x4334)
+#define AFE_SECURE_MASK_CONN41_1 (0x4338)
+#define AFE_SECURE_MASK_CONN41_2 (0x433c)
+#define AFE_SECURE_MASK_CONN41_3 (0x4340)
+#define AFE_SECURE_MASK_CONN41_4 (0x4344)
+#define AFE_SECURE_MASK_CONN42 (0x4348)
+#define AFE_SECURE_MASK_CONN42_1 (0x434c)
+#define AFE_SECURE_MASK_CONN42_2 (0x4350)
+#define AFE_SECURE_MASK_CONN42_3 (0x4354)
+#define AFE_SECURE_MASK_CONN42_4 (0x4358)
+#define AFE_SECURE_MASK_CONN43 (0x435c)
+#define AFE_SECURE_MASK_CONN43_1 (0x4360)
+#define AFE_SECURE_MASK_CONN43_2 (0x4364)
+#define AFE_SECURE_MASK_CONN43_3 (0x4368)
+#define AFE_SECURE_MASK_CONN43_4 (0x436c)
+#define AFE_SECURE_MASK_CONN44 (0x4370)
+#define AFE_SECURE_MASK_CONN44_1 (0x4374)
+#define AFE_SECURE_MASK_CONN44_2 (0x4378)
+#define AFE_SECURE_MASK_CONN44_3 (0x437c)
+#define AFE_SECURE_MASK_CONN44_4 (0x4380)
+#define AFE_SECURE_MASK_CONN45 (0x4384)
+#define AFE_SECURE_MASK_CONN45_1 (0x4388)
+#define AFE_SECURE_MASK_CONN45_2 (0x438c)
+#define AFE_SECURE_MASK_CONN45_3 (0x4390)
+#define AFE_SECURE_MASK_CONN45_4 (0x4394)
+#define AFE_SECURE_MASK_CONN46 (0x4398)
+#define AFE_SECURE_MASK_CONN46_1 (0x439c)
+#define AFE_SECURE_MASK_CONN46_2 (0x43a0)
+#define AFE_SECURE_MASK_CONN46_3 (0x43a4)
+#define AFE_SECURE_MASK_CONN46_4 (0x43a8)
+#define AFE_SECURE_MASK_CONN47 (0x43ac)
+#define AFE_SECURE_MASK_CONN47_1 (0x43b0)
+#define AFE_SECURE_MASK_CONN47_2 (0x43b4)
+#define AFE_SECURE_MASK_CONN47_3 (0x43b8)
+#define AFE_SECURE_MASK_CONN47_4 (0x43bc)
+#define AFE_SECURE_MASK_CONN48 (0x43c0)
+#define AFE_SECURE_MASK_CONN48_1 (0x43c4)
+#define AFE_SECURE_MASK_CONN48_2 (0x43c8)
+#define AFE_SECURE_MASK_CONN48_3 (0x43cc)
+#define AFE_SECURE_MASK_CONN48_4 (0x43d0)
+#define AFE_SECURE_MASK_CONN49 (0x43d4)
+#define AFE_SECURE_MASK_CONN49_1 (0x43d8)
+#define AFE_SECURE_MASK_CONN49_2 (0x43dc)
+#define AFE_SECURE_MASK_CONN49_3 (0x43e0)
+#define AFE_SECURE_MASK_CONN49_4 (0x43e4)
+#define AFE_SECURE_MASK_CONN50 (0x43e8)
+#define AFE_SECURE_MASK_CONN50_1 (0x43ec)
+#define AFE_SECURE_MASK_CONN50_2 (0x43f0)
+#define AFE_SECURE_MASK_CONN50_3 (0x43f4)
+#define AFE_SECURE_MASK_CONN50_4 (0x43f8)
+#define AFE_SECURE_MASK_CONN51 (0x43fc)
+#define AFE_SECURE_MASK_CONN51_1 (0x4400)
+#define AFE_SECURE_MASK_CONN51_2 (0x4404)
+#define AFE_SECURE_MASK_CONN51_3 (0x4408)
+#define AFE_SECURE_MASK_CONN51_4 (0x440c)
+#define AFE_SECURE_MASK_CONN52 (0x4410)
+#define AFE_SECURE_MASK_CONN52_1 (0x4414)
+#define AFE_SECURE_MASK_CONN52_2 (0x4418)
+#define AFE_SECURE_MASK_CONN52_3 (0x441c)
+#define AFE_SECURE_MASK_CONN52_4 (0x4420)
+#define AFE_SECURE_MASK_CONN53 (0x4424)
+#define AFE_SECURE_MASK_CONN53_1 (0x4428)
+#define AFE_SECURE_MASK_CONN53_2 (0x442c)
+#define AFE_SECURE_MASK_CONN53_3 (0x4430)
+#define AFE_SECURE_MASK_CONN53_4 (0x4434)
+#define AFE_SECURE_MASK_CONN54 (0x4438)
+#define AFE_SECURE_MASK_CONN54_1 (0x443c)
+#define AFE_SECURE_MASK_CONN54_2 (0x4440)
+#define AFE_SECURE_MASK_CONN54_3 (0x4444)
+#define AFE_SECURE_MASK_CONN54_4 (0x4448)
+#define AFE_SECURE_MASK_CONN55 (0x444c)
+#define AFE_SECURE_MASK_CONN55_1 (0x4450)
+#define AFE_SECURE_MASK_CONN55_2 (0x4454)
+#define AFE_SECURE_MASK_CONN55_3 (0x4458)
+#define AFE_SECURE_MASK_CONN55_4 (0x445c)
+#define AFE_SECURE_MASK_CONN56 (0x4460)
+#define AFE_SECURE_MASK_CONN56_1 (0x4464)
+#define AFE_SECURE_MASK_CONN56_2 (0x4468)
+#define AFE_SECURE_MASK_CONN56_3 (0x446c)
+#define AFE_SECURE_MASK_CONN56_4 (0x4470)
+#define AFE_SECURE_MASK_CONN57 (0x4474)
+#define AFE_SECURE_MASK_CONN57_1 (0x4478)
+#define AFE_SECURE_MASK_CONN57_2 (0x447c)
+#define AFE_SECURE_MASK_CONN57_3 (0x4480)
+#define AFE_SECURE_MASK_CONN57_4 (0x4484)
+#define AFE_SECURE_MASK_CONN58 (0x4488)
+#define AFE_SECURE_MASK_CONN58_1 (0x448c)
+#define AFE_SECURE_MASK_CONN58_2 (0x4490)
+#define AFE_SECURE_MASK_CONN58_3 (0x4494)
+#define AFE_SECURE_MASK_CONN58_4 (0x4498)
+#define AFE_SECURE_MASK_CONN59 (0x449c)
+#define AFE_SECURE_MASK_CONN59_1 (0x44a0)
+#define AFE_SECURE_MASK_CONN59_2 (0x44a4)
+#define AFE_SECURE_MASK_CONN59_3 (0x44a8)
+#define AFE_SECURE_MASK_CONN59_4 (0x44ac)
+#define AFE_SECURE_MASK_CONN60 (0x44b0)
+#define AFE_SECURE_MASK_CONN60_1 (0x44b4)
+#define AFE_SECURE_MASK_CONN60_2 (0x44b8)
+#define AFE_SECURE_MASK_CONN60_3 (0x44bc)
+#define AFE_SECURE_MASK_CONN60_4 (0x44c0)
+#define AFE_SECURE_MASK_CONN61 (0x44c4)
+#define AFE_SECURE_MASK_CONN61_1 (0x44c8)
+#define AFE_SECURE_MASK_CONN61_2 (0x44cc)
+#define AFE_SECURE_MASK_CONN61_3 (0x44d0)
+#define AFE_SECURE_MASK_CONN61_4 (0x44d4)
+#define AFE_SECURE_MASK_CONN62 (0x44d8)
+#define AFE_SECURE_MASK_CONN62_1 (0x44dc)
+#define AFE_SECURE_MASK_CONN62_2 (0x44e0)
+#define AFE_SECURE_MASK_CONN62_3 (0x44e4)
+#define AFE_SECURE_MASK_CONN62_4 (0x44e8)
+#define AFE_SECURE_MASK_CONN63 (0x44ec)
+#define AFE_SECURE_MASK_CONN63_1 (0x44f0)
+#define AFE_SECURE_MASK_CONN63_2 (0x44f4)
+#define AFE_SECURE_MASK_CONN63_3 (0x44f8)
+#define AFE_SECURE_MASK_CONN63_4 (0x44fc)
+#define AFE_SECURE_MASK_CONN64 (0x4500)
+#define AFE_SECURE_MASK_CONN64_1 (0x4504)
+#define AFE_SECURE_MASK_CONN64_2 (0x4508)
+#define AFE_SECURE_MASK_CONN64_3 (0x450c)
+#define AFE_SECURE_MASK_CONN64_4 (0x4510)
+#define AFE_SECURE_MASK_CONN65 (0x4514)
+#define AFE_SECURE_MASK_CONN65_1 (0x4518)
+#define AFE_SECURE_MASK_CONN65_2 (0x451c)
+#define AFE_SECURE_MASK_CONN65_3 (0x4520)
+#define AFE_SECURE_MASK_CONN65_4 (0x4524)
+#define AFE_SECURE_MASK_CONN66 (0x4528)
+#define AFE_SECURE_MASK_CONN66_1 (0x452c)
+#define AFE_SECURE_MASK_CONN66_2 (0x4530)
+#define AFE_SECURE_MASK_CONN66_3 (0x4534)
+#define AFE_SECURE_MASK_CONN66_4 (0x4538)
+#define AFE_SECURE_MASK_CONN67 (0x453c)
+#define AFE_SECURE_MASK_CONN67_1 (0x4540)
+#define AFE_SECURE_MASK_CONN67_2 (0x4544)
+#define AFE_SECURE_MASK_CONN67_3 (0x4548)
+#define AFE_SECURE_MASK_CONN67_4 (0x454c)
+#define AFE_SECURE_MASK_CONN68 (0x4550)
+#define AFE_SECURE_MASK_CONN68_1 (0x4554)
+#define AFE_SECURE_MASK_CONN68_2 (0x4558)
+#define AFE_SECURE_MASK_CONN68_3 (0x455c)
+#define AFE_SECURE_MASK_CONN68_4 (0x4560)
+#define AFE_SECURE_MASK_CONN69 (0x4564)
+#define AFE_SECURE_MASK_CONN69_1 (0x4568)
+#define AFE_SECURE_MASK_CONN69_2 (0x456c)
+#define AFE_SECURE_MASK_CONN69_3 (0x4570)
+#define AFE_SECURE_MASK_CONN69_4 (0x4574)
+#define AFE_SECURE_MASK_CONN70 (0x4578)
+#define AFE_SECURE_MASK_CONN70_1 (0x457c)
+#define AFE_SECURE_MASK_CONN70_2 (0x4580)
+#define AFE_SECURE_MASK_CONN70_3 (0x4584)
+#define AFE_SECURE_MASK_CONN70_4 (0x4588)
+#define AFE_SECURE_MASK_CONN71 (0x458c)
+#define AFE_SECURE_MASK_CONN71_1 (0x4590)
+#define AFE_SECURE_MASK_CONN71_2 (0x4594)
+#define AFE_SECURE_MASK_CONN71_3 (0x4598)
+#define AFE_SECURE_MASK_CONN71_4 (0x459c)
+#define AFE_SECURE_MASK_CONN72 (0x45a0)
+#define AFE_SECURE_MASK_CONN72_1 (0x45a4)
+#define AFE_SECURE_MASK_CONN72_2 (0x45a8)
+#define AFE_SECURE_MASK_CONN72_3 (0x45ac)
+#define AFE_SECURE_MASK_CONN72_4 (0x45b0)
+#define AFE_SECURE_MASK_CONN73 (0x45b4)
+#define AFE_SECURE_MASK_CONN73_1 (0x45b8)
+#define AFE_SECURE_MASK_CONN73_2 (0x45bc)
+#define AFE_SECURE_MASK_CONN73_3 (0x45c0)
+#define AFE_SECURE_MASK_CONN73_4 (0x45c4)
+#define AFE_SECURE_MASK_CONN74 (0x45c8)
+#define AFE_SECURE_MASK_CONN74_1 (0x45cc)
+#define AFE_SECURE_MASK_CONN74_2 (0x45d0)
+#define AFE_SECURE_MASK_CONN74_3 (0x45d4)
+#define AFE_SECURE_MASK_CONN74_4 (0x45d8)
+#define AFE_SECURE_MASK_CONN75 (0x45dc)
+#define AFE_SECURE_MASK_CONN75_1 (0x45e0)
+#define AFE_SECURE_MASK_CONN75_2 (0x45e4)
+#define AFE_SECURE_MASK_CONN75_3 (0x45e8)
+#define AFE_SECURE_MASK_CONN75_4 (0x45ec)
+#define AFE_SECURE_MASK_CONN76 (0x45f0)
+#define AFE_SECURE_MASK_CONN76_1 (0x45f4)
+#define AFE_SECURE_MASK_CONN76_2 (0x45f8)
+#define AFE_SECURE_MASK_CONN76_3 (0x45fc)
+#define AFE_SECURE_MASK_CONN76_4 (0x4600)
+#define AFE_SECURE_MASK_CONN77 (0x4604)
+#define AFE_SECURE_MASK_CONN77_1 (0x4608)
+#define AFE_SECURE_MASK_CONN77_2 (0x460c)
+#define AFE_SECURE_MASK_CONN77_3 (0x4610)
+#define AFE_SECURE_MASK_CONN77_4 (0x4614)
+#define AFE_SECURE_MASK_CONN78 (0x4618)
+#define AFE_SECURE_MASK_CONN78_1 (0x461c)
+#define AFE_SECURE_MASK_CONN78_2 (0x4620)
+#define AFE_SECURE_MASK_CONN78_3 (0x4624)
+#define AFE_SECURE_MASK_CONN78_4 (0x4628)
+#define AFE_SECURE_MASK_CONN79 (0x462c)
+#define AFE_SECURE_MASK_CONN79_1 (0x4630)
+#define AFE_SECURE_MASK_CONN79_2 (0x4634)
+#define AFE_SECURE_MASK_CONN79_3 (0x4638)
+#define AFE_SECURE_MASK_CONN79_4 (0x463c)
+#define AFE_SECURE_MASK_CONN80 (0x4640)
+#define AFE_SECURE_MASK_CONN80_1 (0x4644)
+#define AFE_SECURE_MASK_CONN80_2 (0x4648)
+#define AFE_SECURE_MASK_CONN80_3 (0x464c)
+#define AFE_SECURE_MASK_CONN80_4 (0x4650)
+#define AFE_SECURE_MASK_CONN81 (0x4654)
+#define AFE_SECURE_MASK_CONN81_1 (0x4658)
+#define AFE_SECURE_MASK_CONN81_2 (0x465c)
+#define AFE_SECURE_MASK_CONN81_3 (0x4660)
+#define AFE_SECURE_MASK_CONN81_4 (0x4664)
+#define AFE_SECURE_MASK_CONN82 (0x4668)
+#define AFE_SECURE_MASK_CONN82_1 (0x466c)
+#define AFE_SECURE_MASK_CONN82_2 (0x4670)
+#define AFE_SECURE_MASK_CONN82_3 (0x4674)
+#define AFE_SECURE_MASK_CONN82_4 (0x4678)
+#define AFE_SECURE_MASK_CONN83 (0x467c)
+#define AFE_SECURE_MASK_CONN83_1 (0x4680)
+#define AFE_SECURE_MASK_CONN83_2 (0x4684)
+#define AFE_SECURE_MASK_CONN83_3 (0x4688)
+#define AFE_SECURE_MASK_CONN83_4 (0x468c)
+#define AFE_SECURE_MASK_CONN84 (0x4690)
+#define AFE_SECURE_MASK_CONN84_1 (0x4694)
+#define AFE_SECURE_MASK_CONN84_2 (0x4698)
+#define AFE_SECURE_MASK_CONN84_3 (0x469c)
+#define AFE_SECURE_MASK_CONN84_4 (0x46a0)
+#define AFE_SECURE_MASK_CONN85 (0x46a4)
+#define AFE_SECURE_MASK_CONN85_1 (0x46a8)
+#define AFE_SECURE_MASK_CONN85_2 (0x46ac)
+#define AFE_SECURE_MASK_CONN85_3 (0x46b0)
+#define AFE_SECURE_MASK_CONN85_4 (0x46b4)
+#define AFE_SECURE_MASK_CONN86 (0x46b8)
+#define AFE_SECURE_MASK_CONN86_1 (0x46bc)
+#define AFE_SECURE_MASK_CONN86_2 (0x46c0)
+#define AFE_SECURE_MASK_CONN86_3 (0x46c4)
+#define AFE_SECURE_MASK_CONN86_4 (0x46c8)
+#define AFE_SECURE_MASK_CONN87 (0x46cc)
+#define AFE_SECURE_MASK_CONN87_1 (0x46d0)
+#define AFE_SECURE_MASK_CONN87_2 (0x46d4)
+#define AFE_SECURE_MASK_CONN87_3 (0x46d8)
+#define AFE_SECURE_MASK_CONN87_4 (0x46dc)
+#define AFE_SECURE_MASK_CONN88 (0x46e0)
+#define AFE_SECURE_MASK_CONN88_1 (0x46e4)
+#define AFE_SECURE_MASK_CONN88_2 (0x46e8)
+#define AFE_SECURE_MASK_CONN88_3 (0x46ec)
+#define AFE_SECURE_MASK_CONN88_4 (0x46f0)
+#define AFE_SECURE_MASK_CONN89 (0x46f4)
+#define AFE_SECURE_MASK_CONN89_1 (0x46f8)
+#define AFE_SECURE_MASK_CONN89_2 (0x46fc)
+#define AFE_SECURE_MASK_CONN89_3 (0x4700)
+#define AFE_SECURE_MASK_CONN89_4 (0x4704)
+#define AFE_SECURE_MASK_CONN90 (0x4708)
+#define AFE_SECURE_MASK_CONN90_1 (0x470c)
+#define AFE_SECURE_MASK_CONN90_2 (0x4710)
+#define AFE_SECURE_MASK_CONN90_3 (0x4714)
+#define AFE_SECURE_MASK_CONN90_4 (0x4718)
+#define AFE_SECURE_MASK_CONN91 (0x471c)
+#define AFE_SECURE_MASK_CONN91_1 (0x4720)
+#define AFE_SECURE_MASK_CONN91_2 (0x4724)
+#define AFE_SECURE_MASK_CONN91_3 (0x4728)
+#define AFE_SECURE_MASK_CONN91_4 (0x472c)
+#define AFE_SECURE_MASK_CONN92 (0x4730)
+#define AFE_SECURE_MASK_CONN92_1 (0x4734)
+#define AFE_SECURE_MASK_CONN92_2 (0x4738)
+#define AFE_SECURE_MASK_CONN92_3 (0x473c)
+#define AFE_SECURE_MASK_CONN92_4 (0x4740)
+#define AFE_SECURE_MASK_CONN93 (0x4744)
+#define AFE_SECURE_MASK_CONN93_1 (0x4748)
+#define AFE_SECURE_MASK_CONN93_2 (0x474c)
+#define AFE_SECURE_MASK_CONN93_3 (0x4750)
+#define AFE_SECURE_MASK_CONN93_4 (0x4754)
+#define AFE_SECURE_MASK_CONN94 (0x4758)
+#define AFE_SECURE_MASK_CONN94_1 (0x475c)
+#define AFE_SECURE_MASK_CONN94_2 (0x4760)
+#define AFE_SECURE_MASK_CONN94_3 (0x4764)
+#define AFE_SECURE_MASK_CONN94_4 (0x4768)
+#define AFE_SECURE_MASK_CONN95 (0x476c)
+#define AFE_SECURE_MASK_CONN95_1 (0x4770)
+#define AFE_SECURE_MASK_CONN95_2 (0x4774)
+#define AFE_SECURE_MASK_CONN95_3 (0x4778)
+#define AFE_SECURE_MASK_CONN95_4 (0x477c)
+#define AFE_SECURE_MASK_CONN96 (0x4780)
+#define AFE_SECURE_MASK_CONN96_1 (0x4784)
+#define AFE_SECURE_MASK_CONN96_2 (0x4788)
+#define AFE_SECURE_MASK_CONN96_3 (0x478c)
+#define AFE_SECURE_MASK_CONN96_4 (0x4790)
+#define AFE_SECURE_MASK_CONN97 (0x4794)
+#define AFE_SECURE_MASK_CONN97_1 (0x4798)
+#define AFE_SECURE_MASK_CONN97_2 (0x479c)
+#define AFE_SECURE_MASK_CONN97_3 (0x47a0)
+#define AFE_SECURE_MASK_CONN97_4 (0x47a4)
+#define AFE_SECURE_MASK_CONN98 (0x47a8)
+#define AFE_SECURE_MASK_CONN98_1 (0x47ac)
+#define AFE_SECURE_MASK_CONN98_2 (0x47b0)
+#define AFE_SECURE_MASK_CONN98_3 (0x47b4)
+#define AFE_SECURE_MASK_CONN98_4 (0x47b8)
+#define AFE_SECURE_MASK_CONN99 (0x47bc)
+#define AFE_SECURE_MASK_CONN99_1 (0x47c0)
+#define AFE_SECURE_MASK_CONN99_2 (0x47c4)
+#define AFE_SECURE_MASK_CONN99_3 (0x47c8)
+#define AFE_SECURE_MASK_CONN99_4 (0x47cc)
+#define AFE_SECURE_MASK_CONN100 (0x47d0)
+#define AFE_SECURE_MASK_CONN100_1 (0x47d4)
+#define AFE_SECURE_MASK_CONN100_2 (0x47d8)
+#define AFE_SECURE_MASK_CONN100_3 (0x47dc)
+#define AFE_SECURE_MASK_CONN100_4 (0x47e0)
+#define AFE_SECURE_MASK_CONN101 (0x47e4)
+#define AFE_SECURE_MASK_CONN101_1 (0x47e8)
+#define AFE_SECURE_MASK_CONN101_2 (0x47ec)
+#define AFE_SECURE_MASK_CONN101_3 (0x47f0)
+#define AFE_SECURE_MASK_CONN101_4 (0x47f4)
+#define AFE_SECURE_MASK_CONN102 (0x47f8)
+#define AFE_SECURE_MASK_CONN102_1 (0x47fc)
+#define AFE_SECURE_MASK_CONN102_2 (0x4800)
+#define AFE_SECURE_MASK_CONN102_3 (0x4804)
+#define AFE_SECURE_MASK_CONN102_4 (0x4808)
+#define AFE_SECURE_MASK_CONN103 (0x480c)
+#define AFE_SECURE_MASK_CONN103_1 (0x4810)
+#define AFE_SECURE_MASK_CONN103_2 (0x4814)
+#define AFE_SECURE_MASK_CONN103_3 (0x4818)
+#define AFE_SECURE_MASK_CONN103_4 (0x481c)
+#define AFE_SECURE_MASK_CONN104 (0x4820)
+#define AFE_SECURE_MASK_CONN104_1 (0x4824)
+#define AFE_SECURE_MASK_CONN104_2 (0x4828)
+#define AFE_SECURE_MASK_CONN104_3 (0x482c)
+#define AFE_SECURE_MASK_CONN104_4 (0x4830)
+#define AFE_SECURE_MASK_CONN105 (0x4834)
+#define AFE_SECURE_MASK_CONN105_1 (0x4838)
+#define AFE_SECURE_MASK_CONN105_2 (0x483c)
+#define AFE_SECURE_MASK_CONN105_3 (0x4840)
+#define AFE_SECURE_MASK_CONN105_4 (0x4844)
+#define AFE_SECURE_MASK_CONN106 (0x4848)
+#define AFE_SECURE_MASK_CONN106_1 (0x484c)
+#define AFE_SECURE_MASK_CONN106_2 (0x4850)
+#define AFE_SECURE_MASK_CONN106_3 (0x4854)
+#define AFE_SECURE_MASK_CONN106_4 (0x4858)
+#define AFE_SECURE_MASK_CONN107 (0x485c)
+#define AFE_SECURE_MASK_CONN107_1 (0x4860)
+#define AFE_SECURE_MASK_CONN107_2 (0x4864)
+#define AFE_SECURE_MASK_CONN107_3 (0x4868)
+#define AFE_SECURE_MASK_CONN107_4 (0x486c)
+#define AFE_SECURE_MASK_CONN108 (0x4870)
+#define AFE_SECURE_MASK_CONN108_1 (0x4874)
+#define AFE_SECURE_MASK_CONN108_2 (0x4878)
+#define AFE_SECURE_MASK_CONN108_3 (0x487c)
+#define AFE_SECURE_MASK_CONN108_4 (0x4880)
+#define AFE_SECURE_MASK_CONN109 (0x4884)
+#define AFE_SECURE_MASK_CONN109_1 (0x4888)
+#define AFE_SECURE_MASK_CONN109_2 (0x488c)
+#define AFE_SECURE_MASK_CONN109_3 (0x4890)
+#define AFE_SECURE_MASK_CONN109_4 (0x4894)
+#define AFE_SECURE_MASK_CONN110 (0x4898)
+#define AFE_SECURE_MASK_CONN110_1 (0x489c)
+#define AFE_SECURE_MASK_CONN110_2 (0x48a0)
+#define AFE_SECURE_MASK_CONN110_3 (0x48a4)
+#define AFE_SECURE_MASK_CONN110_4 (0x48a8)
+#define AFE_SECURE_MASK_CONN111 (0x48ac)
+#define AFE_SECURE_MASK_CONN111_1 (0x48b0)
+#define AFE_SECURE_MASK_CONN111_2 (0x48b4)
+#define AFE_SECURE_MASK_CONN111_3 (0x48b8)
+#define AFE_SECURE_MASK_CONN111_4 (0x48bc)
+#define AFE_SECURE_MASK_CONN112 (0x48c0)
+#define AFE_SECURE_MASK_CONN112_1 (0x48c4)
+#define AFE_SECURE_MASK_CONN112_2 (0x48c8)
+#define AFE_SECURE_MASK_CONN112_3 (0x48cc)
+#define AFE_SECURE_MASK_CONN112_4 (0x48d0)
+#define AFE_SECURE_MASK_CONN113 (0x48d4)
+#define AFE_SECURE_MASK_CONN113_1 (0x48d8)
+#define AFE_SECURE_MASK_CONN113_2 (0x48dc)
+#define AFE_SECURE_MASK_CONN113_3 (0x48e0)
+#define AFE_SECURE_MASK_CONN113_4 (0x48e4)
+#define AFE_SECURE_MASK_CONN114 (0x48e8)
+#define AFE_SECURE_MASK_CONN114_1 (0x48ec)
+#define AFE_SECURE_MASK_CONN114_2 (0x48f0)
+#define AFE_SECURE_MASK_CONN114_3 (0x48f4)
+#define AFE_SECURE_MASK_CONN114_4 (0x48f8)
+#define AFE_SECURE_MASK_CONN115 (0x48fc)
+#define AFE_SECURE_MASK_CONN115_1 (0x4900)
+#define AFE_SECURE_MASK_CONN115_2 (0x4904)
+#define AFE_SECURE_MASK_CONN115_3 (0x4908)
+#define AFE_SECURE_MASK_CONN115_4 (0x490c)
+#define AFE_SECURE_MASK_CONN116 (0x4910)
+#define AFE_SECURE_MASK_CONN116_1 (0x4914)
+#define AFE_SECURE_MASK_CONN116_2 (0x4918)
+#define AFE_SECURE_MASK_CONN116_3 (0x491c)
+#define AFE_SECURE_MASK_CONN116_4 (0x4920)
+#define AFE_SECURE_MASK_CONN117 (0x4924)
+#define AFE_SECURE_MASK_CONN117_1 (0x4928)
+#define AFE_SECURE_MASK_CONN117_2 (0x492c)
+#define AFE_SECURE_MASK_CONN117_3 (0x4930)
+#define AFE_SECURE_MASK_CONN117_4 (0x4934)
+#define AFE_SECURE_MASK_CONN118 (0x4938)
+#define AFE_SECURE_MASK_CONN118_1 (0x493c)
+#define AFE_SECURE_MASK_CONN118_2 (0x4940)
+#define AFE_SECURE_MASK_CONN118_3 (0x4944)
+#define AFE_SECURE_MASK_CONN118_4 (0x4948)
+#define AFE_SECURE_MASK_CONN119 (0x494c)
+#define AFE_SECURE_MASK_CONN119_1 (0x4950)
+#define AFE_SECURE_MASK_CONN119_2 (0x4954)
+#define AFE_SECURE_MASK_CONN119_3 (0x4958)
+#define AFE_SECURE_MASK_CONN119_4 (0x495c)
+#define AFE_SECURE_MASK_CONN120 (0x4960)
+#define AFE_SECURE_MASK_CONN120_1 (0x4964)
+#define AFE_SECURE_MASK_CONN120_2 (0x4968)
+#define AFE_SECURE_MASK_CONN120_3 (0x496c)
+#define AFE_SECURE_MASK_CONN120_4 (0x4970)
+#define AFE_SECURE_MASK_CONN121 (0x4974)
+#define AFE_SECURE_MASK_CONN121_1 (0x4978)
+#define AFE_SECURE_MASK_CONN121_2 (0x497c)
+#define AFE_SECURE_MASK_CONN121_3 (0x4980)
+#define AFE_SECURE_MASK_CONN121_4 (0x4984)
+#define AFE_SECURE_MASK_CONN122 (0x4988)
+#define AFE_SECURE_MASK_CONN122_1 (0x498c)
+#define AFE_SECURE_MASK_CONN122_2 (0x4990)
+#define AFE_SECURE_MASK_CONN122_3 (0x4994)
+#define AFE_SECURE_MASK_CONN122_4 (0x4998)
+#define AFE_SECURE_MASK_CONN123 (0x499c)
+#define AFE_SECURE_MASK_CONN123_1 (0x49a0)
+#define AFE_SECURE_MASK_CONN123_2 (0x49a4)
+#define AFE_SECURE_MASK_CONN123_3 (0x49a8)
+#define AFE_SECURE_MASK_CONN123_4 (0x49ac)
+#define AFE_SECURE_MASK_CONN124 (0x49b0)
+#define AFE_SECURE_MASK_CONN124_1 (0x49b4)
+#define AFE_SECURE_MASK_CONN124_2 (0x49b8)
+#define AFE_SECURE_MASK_CONN124_3 (0x49bc)
+#define AFE_SECURE_MASK_CONN124_4 (0x49c0)
+#define AFE_SECURE_MASK_CONN125 (0x49c4)
+#define AFE_SECURE_MASK_CONN125_1 (0x49c8)
+#define AFE_SECURE_MASK_CONN125_2 (0x49cc)
+#define AFE_SECURE_MASK_CONN125_3 (0x49d0)
+#define AFE_SECURE_MASK_CONN125_4 (0x49d4)
+#define AFE_SECURE_MASK_CONN126 (0x49d8)
+#define AFE_SECURE_MASK_CONN126_1 (0x49dc)
+#define AFE_SECURE_MASK_CONN126_2 (0x49e0)
+#define AFE_SECURE_MASK_CONN126_3 (0x49e4)
+#define AFE_SECURE_MASK_CONN126_4 (0x49e8)
+#define AFE_SECURE_MASK_CONN127 (0x49ec)
+#define AFE_SECURE_MASK_CONN127_1 (0x49f0)
+#define AFE_SECURE_MASK_CONN127_2 (0x49f4)
+#define AFE_SECURE_MASK_CONN127_3 (0x49f8)
+#define AFE_SECURE_MASK_CONN127_4 (0x49fc)
+#define AFE_SECURE_MASK_CONN128 (0x4a00)
+#define AFE_SECURE_MASK_CONN128_1 (0x4a04)
+#define AFE_SECURE_MASK_CONN128_2 (0x4a08)
+#define AFE_SECURE_MASK_CONN128_3 (0x4a0c)
+#define AFE_SECURE_MASK_CONN128_4 (0x4a10)
+#define AFE_SECURE_MASK_CONN129 (0x4a14)
+#define AFE_SECURE_MASK_CONN129_1 (0x4a18)
+#define AFE_SECURE_MASK_CONN129_2 (0x4a1c)
+#define AFE_SECURE_MASK_CONN129_3 (0x4a20)
+#define AFE_SECURE_MASK_CONN129_4 (0x4a24)
+#define AFE_SECURE_MASK_CONN130 (0x4a28)
+#define AFE_SECURE_MASK_CONN130_1 (0x4a2c)
+#define AFE_SECURE_MASK_CONN130_2 (0x4a30)
+#define AFE_SECURE_MASK_CONN130_3 (0x4a34)
+#define AFE_SECURE_MASK_CONN130_4 (0x4a38)
+#define AFE_SECURE_MASK_CONN131 (0x4a3c)
+#define AFE_SECURE_MASK_CONN131_1 (0x4a40)
+#define AFE_SECURE_MASK_CONN131_2 (0x4a44)
+#define AFE_SECURE_MASK_CONN131_3 (0x4a48)
+#define AFE_SECURE_MASK_CONN131_4 (0x4a4c)
+#define AFE_SECURE_MASK_CONN132 (0x4a50)
+#define AFE_SECURE_MASK_CONN132_1 (0x4a54)
+#define AFE_SECURE_MASK_CONN132_2 (0x4a58)
+#define AFE_SECURE_MASK_CONN132_3 (0x4a5c)
+#define AFE_SECURE_MASK_CONN132_4 (0x4a60)
+#define AFE_SECURE_MASK_CONN133 (0x4a64)
+#define AFE_SECURE_MASK_CONN133_1 (0x4a68)
+#define AFE_SECURE_MASK_CONN133_2 (0x4a6c)
+#define AFE_SECURE_MASK_CONN133_3 (0x4a70)
+#define AFE_SECURE_MASK_CONN133_4 (0x4a74)
+#define AFE_SECURE_MASK_CONN134 (0x4a78)
+#define AFE_SECURE_MASK_CONN134_1 (0x4a7c)
+#define AFE_SECURE_MASK_CONN134_2 (0x4a80)
+#define AFE_SECURE_MASK_CONN134_3 (0x4a84)
+#define AFE_SECURE_MASK_CONN134_4 (0x4a88)
+#define AFE_SECURE_MASK_CONN135 (0x4a8c)
+#define AFE_SECURE_MASK_CONN135_1 (0x4a90)
+#define AFE_SECURE_MASK_CONN135_2 (0x4a94)
+#define AFE_SECURE_MASK_CONN135_3 (0x4a98)
+#define AFE_SECURE_MASK_CONN135_4 (0x4a9c)
+#define AFE_SECURE_MASK_CONN136 (0x4aa0)
+#define AFE_SECURE_MASK_CONN136_1 (0x4aa4)
+#define AFE_SECURE_MASK_CONN136_2 (0x4aa8)
+#define AFE_SECURE_MASK_CONN136_3 (0x4aac)
+#define AFE_SECURE_MASK_CONN136_4 (0x4ab0)
+#define AFE_SECURE_MASK_CONN137 (0x4ab4)
+#define AFE_SECURE_MASK_CONN137_1 (0x4ab8)
+#define AFE_SECURE_MASK_CONN137_2 (0x4abc)
+#define AFE_SECURE_MASK_CONN137_3 (0x4ac0)
+#define AFE_SECURE_MASK_CONN137_4 (0x4ac4)
+#define AFE_SECURE_MASK_CONN138 (0x4ac8)
+#define AFE_SECURE_MASK_CONN138_1 (0x4acc)
+#define AFE_SECURE_MASK_CONN138_2 (0x4ad0)
+#define AFE_SECURE_MASK_CONN138_3 (0x4ad4)
+#define AFE_SECURE_MASK_CONN138_4 (0x4ad8)
+#define AFE_SECURE_MASK_CONN139 (0x4adc)
+#define AFE_SECURE_MASK_CONN139_1 (0x4ae0)
+#define AFE_SECURE_MASK_CONN139_2 (0x4ae4)
+#define AFE_SECURE_MASK_CONN139_3 (0x4ae8)
+#define AFE_SECURE_MASK_CONN139_4 (0x4aec)
+#define AFE_SECURE_MASK_CONN_RS (0x4af0)
+#define AFE_SECURE_MASK_CONN_RS_1 (0x4af4)
+#define AFE_SECURE_MASK_CONN_RS_2 (0x4af8)
+#define AFE_SECURE_MASK_CONN_RS_3 (0x4afc)
+#define AFE_SECURE_MASK_CONN_RS_4 (0x4b00)
+#define AFE_SECURE_MASK_CONN_16BIT (0x4b04)
+#define AFE_SECURE_MASK_CONN_16BIT_1 (0x4b08)
+#define AFE_SECURE_MASK_CONN_16BIT_2 (0x4b0c)
+#define AFE_SECURE_MASK_CONN_16BIT_3 (0x4b10)
+#define AFE_SECURE_MASK_CONN_16BIT_4 (0x4b14)
+#define AFE_SECURE_MASK_CONN_24BIT (0x4b18)
+#define AFE_SECURE_MASK_CONN_24BIT_1 (0x4b1c)
+#define AFE_SECURE_MASK_CONN_24BIT_2 (0x4b20)
+#define AFE_SECURE_MASK_CONN_24BIT_3 (0x4b24)
+#define AFE_SECURE_MASK_CONN_24BIT_4 (0x4b28)
+#define AFE_SECURE_MASK_CONN0_5 (0x4b2c)
+#define AFE_SECURE_MASK_CONN1_5 (0x4b30)
+#define AFE_SECURE_MASK_CONN2_5 (0x4b34)
+#define AFE_SECURE_MASK_CONN3_5 (0x4b38)
+#define AFE_SECURE_MASK_CONN4_5 (0x4b3c)
+#define AFE_SECURE_MASK_CONN5_5 (0x4b40)
+#define AFE_SECURE_MASK_CONN6_5 (0x4b44)
+#define AFE_SECURE_MASK_CONN7_5 (0x4b48)
+#define AFE_SECURE_MASK_CONN8_5 (0x4b4c)
+#define AFE_SECURE_MASK_CONN9_5 (0x4b50)
+#define AFE_SECURE_MASK_CONN10_5 (0x4b54)
+#define AFE_SECURE_MASK_CONN11_5 (0x4b58)
+#define AFE_SECURE_MASK_CONN12_5 (0x4b5c)
+#define AFE_SECURE_MASK_CONN13_5 (0x4b60)
+#define AFE_SECURE_MASK_CONN14_5 (0x4b64)
+#define AFE_SECURE_MASK_CONN15_5 (0x4b68)
+#define AFE_SECURE_MASK_CONN16_5 (0x4b6c)
+#define AFE_SECURE_MASK_CONN17_5 (0x4b70)
+#define AFE_SECURE_MASK_CONN18_5 (0x4b74)
+#define AFE_SECURE_MASK_CONN19_5 (0x4b78)
+#define AFE_SECURE_MASK_CONN20_5 (0x4b7c)
+#define AFE_SECURE_MASK_CONN21_5 (0x4b80)
+#define AFE_SECURE_MASK_CONN22_5 (0x4b84)
+#define AFE_SECURE_MASK_CONN23_5 (0x4b88)
+#define AFE_SECURE_MASK_CONN24_5 (0x4b8c)
+#define AFE_SECURE_MASK_CONN25_5 (0x4b90)
+#define AFE_SECURE_MASK_CONN26_5 (0x4b94)
+#define AFE_SECURE_MASK_CONN27_5 (0x4b98)
+#define AFE_SECURE_MASK_CONN28_5 (0x4b9c)
+#define AFE_SECURE_MASK_CONN29_5 (0x4ba0)
+#define AFE_SECURE_MASK_CONN30_5 (0x4ba4)
+#define AFE_SECURE_MASK_CONN31_5 (0x4ba8)
+#define AFE_SECURE_MASK_CONN32_5 (0x4bac)
+#define AFE_SECURE_MASK_CONN33_5 (0x4bb0)
+#define AFE_SECURE_MASK_CONN34_5 (0x4bb4)
+#define AFE_SECURE_MASK_CONN35_5 (0x4bb8)
+#define AFE_SECURE_MASK_CONN36_5 (0x4bbc)
+#define AFE_SECURE_MASK_CONN37_5 (0x4bc0)
+#define AFE_SECURE_MASK_CONN38_5 (0x4bc4)
+#define AFE_SECURE_MASK_CONN39_5 (0x4bc8)
+#define AFE_SECURE_MASK_CONN40_5 (0x4bcc)
+#define AFE_SECURE_MASK_CONN41_5 (0x4bd0)
+#define AFE_SECURE_MASK_CONN42_5 (0x4bd4)
+#define AFE_SECURE_MASK_CONN43_5 (0x4bd8)
+#define AFE_SECURE_MASK_CONN44_5 (0x4bdc)
+#define AFE_SECURE_MASK_CONN45_5 (0x4be0)
+#define AFE_SECURE_MASK_CONN46_5 (0x4be4)
+#define AFE_SECURE_MASK_CONN47_5 (0x4be8)
+#define AFE_SECURE_MASK_CONN48_5 (0x4bec)
+#define AFE_SECURE_MASK_CONN49_5 (0x4bf0)
+#define AFE_SECURE_MASK_CONN50_5 (0x4bf4)
+#define AFE_SECURE_MASK_CONN51_5 (0x4bf8)
+#define AFE_SECURE_MASK_CONN52_5 (0x4bfc)
+#define AFE_GASRC0_NEW_CON0 (0x4c40)
+#define AFE_GASRC0_NEW_CON1 (0x4c44)
+#define AFE_GASRC0_NEW_CON2 (0x4c48)
+#define AFE_GASRC0_NEW_CON3 (0x4c4c)
+#define AFE_GASRC0_NEW_CON4 (0x4c50)
+#define AFE_GASRC0_NEW_CON5 (0x4c54)
+#define AFE_GASRC0_NEW_CON6 (0x4c58)
+#define AFE_GASRC0_NEW_CON7 (0x4c5c)
+#define AFE_GASRC0_NEW_CON8 (0x4c60)
+#define AFE_GASRC0_NEW_CON9 (0x4c64)
+#define AFE_GASRC0_NEW_CON10 (0x4c68)
+#define AFE_GASRC0_NEW_CON11 (0x4c6c)
+#define AFE_GASRC0_NEW_CON12 (0x4c70)
+#define AFE_GASRC0_NEW_CON13 (0x4c74)
+#define AFE_GASRC0_NEW_CON14 (0x4c78)
+#define AFE_GASRC1_NEW_CON0 (0x4c80)
+#define AFE_GASRC1_NEW_CON1 (0x4c84)
+#define AFE_GASRC1_NEW_CON2 (0x4c88)
+#define AFE_GASRC1_NEW_CON3 (0x4c8c)
+#define AFE_GASRC1_NEW_CON4 (0x4c90)
+#define AFE_GASRC1_NEW_CON5 (0x4c94)
+#define AFE_GASRC1_NEW_CON6 (0x4c98)
+#define AFE_GASRC1_NEW_CON7 (0x4c9c)
+#define AFE_GASRC1_NEW_CON8 (0x4ca0)
+#define AFE_GASRC1_NEW_CON9 (0x4ca4)
+#define AFE_GASRC1_NEW_CON10 (0x4ca8)
+#define AFE_GASRC1_NEW_CON11 (0x4cac)
+#define AFE_GASRC1_NEW_CON12 (0x4cb0)
+#define AFE_GASRC1_NEW_CON13 (0x4cb4)
+#define AFE_GASRC1_NEW_CON14 (0x4cb8)
+#define AFE_GASRC2_NEW_CON0 (0x4cc0)
+#define AFE_GASRC2_NEW_CON1 (0x4cc4)
+#define AFE_GASRC2_NEW_CON2 (0x4cc8)
+#define AFE_GASRC2_NEW_CON3 (0x4ccc)
+#define AFE_GASRC2_NEW_CON4 (0x4cd0)
+#define AFE_GASRC2_NEW_CON5 (0x4cd4)
+#define AFE_GASRC2_NEW_CON6 (0x4cd8)
+#define AFE_GASRC2_NEW_CON7 (0x4cdc)
+#define AFE_GASRC2_NEW_CON8 (0x4ce0)
+#define AFE_GASRC2_NEW_CON9 (0x4ce4)
+#define AFE_GASRC2_NEW_CON10 (0x4ce8)
+#define AFE_GASRC2_NEW_CON11 (0x4cec)
+#define AFE_GASRC2_NEW_CON12 (0x4cf0)
+#define AFE_GASRC2_NEW_CON13 (0x4cf4)
+#define AFE_GASRC2_NEW_CON14 (0x4cf8)
+#define AFE_GASRC3_NEW_CON0 (0x4d00)
+#define AFE_GASRC3_NEW_CON1 (0x4d04)
+#define AFE_GASRC3_NEW_CON2 (0x4d08)
+#define AFE_GASRC3_NEW_CON3 (0x4d0c)
+#define AFE_GASRC3_NEW_CON4 (0x4d10)
+#define AFE_GASRC3_NEW_CON5 (0x4d14)
+#define AFE_GASRC3_NEW_CON6 (0x4d18)
+#define AFE_GASRC3_NEW_CON7 (0x4d1c)
+#define AFE_GASRC3_NEW_CON8 (0x4d20)
+#define AFE_GASRC3_NEW_CON9 (0x4d24)
+#define AFE_GASRC3_NEW_CON10 (0x4d28)
+#define AFE_GASRC3_NEW_CON11 (0x4d2c)
+#define AFE_GASRC3_NEW_CON12 (0x4d30)
+#define AFE_GASRC3_NEW_CON13 (0x4d34)
+#define AFE_GASRC3_NEW_CON14 (0x4d38)
+#define AFE_GASRC4_NEW_CON0 (0x4d40)
+#define AFE_GASRC4_NEW_CON1 (0x4d44)
+#define AFE_GASRC4_NEW_CON2 (0x4d48)
+#define AFE_GASRC4_NEW_CON3 (0x4d4c)
+#define AFE_GASRC4_NEW_CON4 (0x4d50)
+#define AFE_GASRC4_NEW_CON5 (0x4d54)
+#define AFE_GASRC4_NEW_CON6 (0x4d58)
+#define AFE_GASRC4_NEW_CON7 (0x4d5c)
+#define AFE_GASRC4_NEW_CON8 (0x4d60)
+#define AFE_GASRC4_NEW_CON9 (0x4d64)
+#define AFE_GASRC4_NEW_CON10 (0x4d68)
+#define AFE_GASRC4_NEW_CON11 (0x4d6c)
+#define AFE_GASRC4_NEW_CON12 (0x4d70)
+#define AFE_GASRC4_NEW_CON13 (0x4d74)
+#define AFE_GASRC4_NEW_CON14 (0x4d78)
+#define AFE_GASRC5_NEW_CON0 (0x4d80)
+#define AFE_GASRC5_NEW_CON1 (0x4d84)
+#define AFE_GASRC5_NEW_CON2 (0x4d88)
+#define AFE_GASRC5_NEW_CON3 (0x4d8c)
+#define AFE_GASRC5_NEW_CON4 (0x4d90)
+#define AFE_GASRC5_NEW_CON5 (0x4d94)
+#define AFE_GASRC5_NEW_CON6 (0x4d98)
+#define AFE_GASRC5_NEW_CON7 (0x4d9c)
+#define AFE_GASRC5_NEW_CON8 (0x4da0)
+#define AFE_GASRC5_NEW_CON9 (0x4da4)
+#define AFE_GASRC5_NEW_CON10 (0x4da8)
+#define AFE_GASRC5_NEW_CON11 (0x4dac)
+#define AFE_GASRC5_NEW_CON12 (0x4db0)
+#define AFE_GASRC5_NEW_CON13 (0x4db4)
+#define AFE_GASRC5_NEW_CON14 (0x4db8)
+#define AFE_GASRC6_NEW_CON0 (0x4dc0)
+#define AFE_GASRC6_NEW_CON1 (0x4dc4)
+#define AFE_GASRC6_NEW_CON2 (0x4dc8)
+#define AFE_GASRC6_NEW_CON3 (0x4dcc)
+#define AFE_GASRC6_NEW_CON4 (0x4dd0)
+#define AFE_GASRC6_NEW_CON5 (0x4dd4)
+#define AFE_GASRC6_NEW_CON6 (0x4dd8)
+#define AFE_GASRC6_NEW_CON7 (0x4ddc)
+#define AFE_GASRC6_NEW_CON8 (0x4de0)
+#define AFE_GASRC6_NEW_CON9 (0x4de4)
+#define AFE_GASRC6_NEW_CON10 (0x4de8)
+#define AFE_GASRC6_NEW_CON11 (0x4dec)
+#define AFE_GASRC6_NEW_CON12 (0x4df0)
+#define AFE_GASRC6_NEW_CON13 (0x4df4)
+#define AFE_GASRC6_NEW_CON14 (0x4df8)
+#define AFE_GASRC7_NEW_CON0 (0x4e00)
+#define AFE_GASRC7_NEW_CON1 (0x4e04)
+#define AFE_GASRC7_NEW_CON2 (0x4e08)
+#define AFE_GASRC7_NEW_CON3 (0x4e0c)
+#define AFE_GASRC7_NEW_CON4 (0x4e10)
+#define AFE_GASRC7_NEW_CON5 (0x4e14)
+#define AFE_GASRC7_NEW_CON6 (0x4e18)
+#define AFE_GASRC7_NEW_CON7 (0x4e1c)
+#define AFE_GASRC7_NEW_CON8 (0x4e20)
+#define AFE_GASRC7_NEW_CON9 (0x4e24)
+#define AFE_GASRC7_NEW_CON10 (0x4e28)
+#define AFE_GASRC7_NEW_CON11 (0x4e2c)
+#define AFE_GASRC7_NEW_CON12 (0x4e30)
+#define AFE_GASRC7_NEW_CON13 (0x4e34)
+#define AFE_GASRC7_NEW_CON14 (0x4e38)
+#define AFE_GASRC8_NEW_CON0 (0x4e40)
+#define AFE_GASRC8_NEW_CON1 (0x4e44)
+#define AFE_GASRC8_NEW_CON2 (0x4e48)
+#define AFE_GASRC8_NEW_CON3 (0x4e4c)
+#define AFE_GASRC8_NEW_CON4 (0x4e50)
+#define AFE_GASRC8_NEW_CON5 (0x4e54)
+#define AFE_GASRC8_NEW_CON6 (0x4e58)
+#define AFE_GASRC8_NEW_CON7 (0x4e5c)
+#define AFE_GASRC8_NEW_CON8 (0x4e60)
+#define AFE_GASRC8_NEW_CON9 (0x4e64)
+#define AFE_GASRC8_NEW_CON10 (0x4e68)
+#define AFE_GASRC8_NEW_CON11 (0x4e6c)
+#define AFE_GASRC8_NEW_CON12 (0x4e70)
+#define AFE_GASRC8_NEW_CON13 (0x4e74)
+#define AFE_GASRC8_NEW_CON14 (0x4e78)
+#define AFE_GASRC9_NEW_CON0 (0x4e80)
+#define AFE_GASRC9_NEW_CON1 (0x4e84)
+#define AFE_GASRC9_NEW_CON2 (0x4e88)
+#define AFE_GASRC9_NEW_CON3 (0x4e8c)
+#define AFE_GASRC9_NEW_CON4 (0x4e90)
+#define AFE_GASRC9_NEW_CON5 (0x4e94)
+#define AFE_GASRC9_NEW_CON6 (0x4e98)
+#define AFE_GASRC9_NEW_CON7 (0x4e9c)
+#define AFE_GASRC9_NEW_CON8 (0x4ea0)
+#define AFE_GASRC9_NEW_CON9 (0x4ea4)
+#define AFE_GASRC9_NEW_CON10 (0x4ea8)
+#define AFE_GASRC9_NEW_CON11 (0x4eac)
+#define AFE_GASRC9_NEW_CON12 (0x4eb0)
+#define AFE_GASRC9_NEW_CON13 (0x4eb4)
+#define AFE_GASRC9_NEW_CON14 (0x4eb8)
+#define AFE_GASRC10_NEW_CON0 (0x4ec0)
+#define AFE_GASRC10_NEW_CON1 (0x4ec4)
+#define AFE_GASRC10_NEW_CON2 (0x4ec8)
+#define AFE_GASRC10_NEW_CON3 (0x4ecc)
+#define AFE_GASRC10_NEW_CON4 (0x4ed0)
+#define AFE_GASRC10_NEW_CON5 (0x4ed4)
+#define AFE_GASRC10_NEW_CON6 (0x4ed8)
+#define AFE_GASRC10_NEW_CON7 (0x4edc)
+#define AFE_GASRC10_NEW_CON8 (0x4ee0)
+#define AFE_GASRC10_NEW_CON9 (0x4ee4)
+#define AFE_GASRC10_NEW_CON10 (0x4ee8)
+#define AFE_GASRC10_NEW_CON11 (0x4eec)
+#define AFE_GASRC10_NEW_CON12 (0x4ef0)
+#define AFE_GASRC10_NEW_CON13 (0x4ef4)
+#define AFE_GASRC10_NEW_CON14 (0x4ef8)
+#define AFE_GASRC11_NEW_CON0 (0x4f00)
+#define AFE_GASRC11_NEW_CON1 (0x4f04)
+#define AFE_GASRC11_NEW_CON2 (0x4f08)
+#define AFE_GASRC11_NEW_CON3 (0x4f0c)
+#define AFE_GASRC11_NEW_CON4 (0x4f10)
+#define AFE_GASRC11_NEW_CON5 (0x4f14)
+#define AFE_GASRC11_NEW_CON6 (0x4f18)
+#define AFE_GASRC11_NEW_CON7 (0x4f1c)
+#define AFE_GASRC11_NEW_CON8 (0x4f20)
+#define AFE_GASRC11_NEW_CON9 (0x4f24)
+#define AFE_GASRC11_NEW_CON10 (0x4f28)
+#define AFE_GASRC11_NEW_CON11 (0x4f2c)
+#define AFE_GASRC11_NEW_CON12 (0x4f30)
+#define AFE_GASRC11_NEW_CON13 (0x4f34)
+#define AFE_GASRC11_NEW_CON14 (0x4f38)
+
+#define AFE_IEC_BURST_INFO_MON (0x64b0)
+#define AFE_SPDIFOUT_IP_VERSION (0x64b4)
+#define AFE_SPDIF_OUT_CFG0 (0x64b8)
+#define AFE_SPDIF_OUT_CFG1 (0x64bc)
+#define AFE_SPDIF_OUT_CHSTS1 (0x64c0)
+#define AFE_SPDIF_OUT_CHSTS2 (0x64c4)
+#define AFE_SPDIF_OUT_CHSTS3 (0x64c8)
+#define AFE_SPDIF_OUT_CHSTS4 (0x64cc)
+#define AFE_SPDIF_OUT_CHSTS5 (0x64d0)
+#define AFE_SPDIF_OUT_CHSTS6 (0x64d4)
+#define AFE_SPDIF_OUT_USERCODE1 (0x64d8)
+#define AFE_SPDIF_OUT_USERCODE2 (0x64dc)
+#define AFE_SPDIF_OUT_USERCODE3 (0x64e0)
+#define AFE_SPDIF_OUT_USERCODE4 (0x64e4)
+#define AFE_SPDIF_OUT_USERCODE5 (0x64e8)
+#define AFE_SPDIF_OUT_USERCODE6 (0x64ec)
+#define AFE_SPDIF_OUT_BURST_PRE0 (0x64f0)
+#define AFE_SPDIF_OUT_BURST_PRE1 (0x64f4)
+#define AFE_SPDIF_OUT_MON0 (0x64f8)
+#define AFE_SPDIF_OUT_MON1 (0x64fc)
+#define AFE_SPDIF_OUT_MON2 (0x6500)
+#define AFE_SPDIF_OUT_MON3 (0x6504)
+#define AFE_SPDIF_OUT_MON4 (0x6508)
+#define AFE_SPDIF_OUT_MON5 (0x650c)
+#define AFE_CONN0_6 (0x7000)
+#define AFE_CONN1_6 (0x7004)
+#define AFE_CONN2_6 (0x7008)
+#define AFE_CONN3_6 (0x700c)
+#define AFE_CONN4_6 (0x7010)
+#define AFE_CONN5_6 (0x7014)
+#define AFE_CONN6_6 (0x7018)
+#define AFE_CONN7_6 (0x701c)
+#define AFE_CONN8_6 (0x7020)
+#define AFE_CONN9_6 (0x7024)
+#define AFE_CONN10_6 (0x7028)
+#define AFE_CONN11_6 (0x702c)
+#define AFE_CONN12_6 (0x7030)
+#define AFE_CONN13_6 (0x7034)
+#define AFE_CONN14_6 (0x7038)
+#define AFE_CONN15_6 (0x703c)
+#define AFE_CONN16_6 (0x7040)
+#define AFE_CONN17_6 (0x7044)
+#define AFE_CONN18_6 (0x7048)
+#define AFE_CONN19_6 (0x704c)
+#define AFE_CONN20_6 (0x7050)
+#define AFE_CONN21_6 (0x7054)
+#define AFE_CONN22_6 (0x7058)
+#define AFE_CONN23_6 (0x705c)
+#define AFE_CONN24_6 (0x7060)
+#define AFE_CONN25_6 (0x7064)
+#define AFE_CONN26_6 (0x7068)
+#define AFE_CONN27_6 (0x706c)
+#define AFE_CONN28_6 (0x7070)
+#define AFE_CONN29_6 (0x7074)
+#define AFE_CONN30_6 (0x7078)
+#define AFE_CONN31_6 (0x707c)
+#define AFE_CONN32_6 (0x7080)
+#define AFE_CONN33_6 (0x7084)
+#define AFE_CONN34_6 (0x7088)
+#define AFE_CONN35_6 (0x708c)
+#define AFE_CONN36_6 (0x7090)
+#define AFE_CONN37_6 (0x7094)
+#define AFE_CONN38_6 (0x7098)
+#define AFE_CONN39_6 (0x709c)
+#define AFE_CONN40_6 (0x70a0)
+#define AFE_CONN41_6 (0x70a4)
+#define AFE_CONN42_6 (0x70a8)
+#define AFE_CONN43_6 (0x70ac)
+#define AFE_CONN44_6 (0x70b0)
+#define AFE_CONN45_6 (0x70b4)
+#define AFE_CONN46_6 (0x70b8)
+#define AFE_CONN47_6 (0x70bc)
+#define AFE_CONN48_6 (0x70c0)
+#define AFE_CONN49_6 (0x70c4)
+#define AFE_CONN50_6 (0x70c8)
+#define AFE_CONN51_6 (0x70cc)
+#define AFE_CONN52_6 (0x70d0)
+#define AFE_CONN53_6 (0x70d4)
+#define AFE_CONN54_6 (0x70d8)
+#define AFE_CONN55_6 (0x70dc)
+#define AFE_CONN56_6 (0x70e0)
+#define AFE_CONN57_6 (0x70e4)
+#define AFE_CONN58_6 (0x70e8)
+#define AFE_CONN59_6 (0x70ec)
+#define AFE_CONN60_6 (0x70f0)
+#define AFE_CONN61_6 (0x70f4)
+#define AFE_CONN62_6 (0x70f8)
+#define AFE_CONN63_6 (0x70fc)
+#define AFE_CONN64_6 (0x7100)
+#define AFE_CONN65_6 (0x7104)
+#define AFE_CONN66_6 (0x7108)
+#define AFE_CONN67_6 (0x710c)
+#define AFE_CONN68_6 (0x7110)
+#define AFE_CONN69_6 (0x7114)
+#define AFE_CONN70_6 (0x7118)
+#define AFE_CONN71_6 (0x711c)
+#define AFE_CONN72_6 (0x7120)
+#define AFE_CONN73_6 (0x7124)
+#define AFE_CONN74_6 (0x7128)
+#define AFE_CONN75_6 (0x712c)
+#define AFE_CONN76_6 (0x7130)
+#define AFE_CONN77_6 (0x7134)
+#define AFE_CONN78_6 (0x7138)
+#define AFE_CONN79_6 (0x713c)
+#define AFE_CONN80_6 (0x7140)
+#define AFE_CONN81_6 (0x7144)
+#define AFE_CONN82_6 (0x7148)
+#define AFE_CONN83_6 (0x714c)
+#define AFE_CONN84_6 (0x7150)
+#define AFE_CONN85_6 (0x7154)
+#define AFE_CONN86_6 (0x7158)
+#define AFE_CONN87_6 (0x715c)
+#define AFE_CONN88_6 (0x7160)
+#define AFE_CONN89_6 (0x7164)
+#define AFE_CONN90_6 (0x7168)
+#define AFE_CONN91_6 (0x716c)
+#define AFE_CONN92_6 (0x7170)
+#define AFE_CONN93_6 (0x7174)
+#define AFE_CONN94_6 (0x7178)
+#define AFE_CONN95_6 (0x717c)
+#define AFE_CONN96_6 (0x7180)
+#define AFE_CONN97_6 (0x7184)
+#define AFE_CONN98_6 (0x7188)
+#define AFE_CONN99_6 (0x718c)
+#define AFE_CONN100_6 (0x7190)
+#define AFE_CONN101_6 (0x7194)
+#define AFE_CONN102_6 (0x7198)
+#define AFE_CONN103_6 (0x719c)
+#define AFE_CONN104_6 (0x71a0)
+#define AFE_CONN105_6 (0x71a4)
+#define AFE_CONN106_6 (0x71a8)
+#define AFE_CONN107_6 (0x71ac)
+#define AFE_CONN108_6 (0x71b0)
+#define AFE_CONN109_6 (0x71b4)
+#define AFE_CONN110_6 (0x71b8)
+#define AFE_CONN111_6 (0x71bc)
+#define AFE_CONN112_6 (0x71c0)
+#define AFE_CONN113_6 (0x71c4)
+#define AFE_CONN114_6 (0x71c8)
+#define AFE_CONN115_6 (0x71cc)
+#define AFE_CONN116_6 (0x71d0)
+#define AFE_CONN117_6 (0x71d4)
+#define AFE_CONN118_6 (0x71d8)
+#define AFE_CONN119_6 (0x71dc)
+#define AFE_CONN120_6 (0x71e0)
+#define AFE_CONN121_6 (0x71e4)
+#define AFE_CONN122_6 (0x71e8)
+#define AFE_CONN123_6 (0x71ec)
+#define AFE_CONN124_6 (0x71f0)
+#define AFE_CONN125_6 (0x71f4)
+#define AFE_CONN126_6 (0x71f8)
+#define AFE_CONN127_6 (0x71fc)
+#define AFE_CONN128_6 (0x7200)
+#define AFE_CONN129_6 (0x7204)
+#define AFE_CONN130_6 (0x7208)
+#define AFE_CONN131_6 (0x720c)
+#define AFE_CONN132_6 (0x7210)
+#define AFE_CONN133_6 (0x7214)
+#define AFE_CONN134_6 (0x7218)
+#define AFE_CONN135_6 (0x721c)
+#define AFE_CONN136_6 (0x7220)
+#define AFE_CONN137_6 (0x7224)
+#define AFE_CONN138_6 (0x7228)
+#define AFE_CONN139_6 (0x722c)
+#define AFE_CONN176_6 (0x72c0)
+#define AFE_CONN177_6 (0x72c4)
+#define AFE_CONN182_6 (0x72d8)
+#define AFE_CONN183_6 (0x72dc)
+
+#define AFE_MAX_REGISTER (AFE_CONN183_6)
+
+/* PWR1_ASM_CON1 */
+#define PWR1_ASM_CON1_GASRC0_CALI_CK_SEL_MASK BIT(2)
+#define PWR1_ASM_CON1_GASRC1_CALI_CK_SEL_MASK BIT(5)
+#define PWR1_ASM_CON1_GASRC2_CALI_CK_SEL_MASK BIT(20)
+#define PWR1_ASM_CON1_GASRC3_CALI_CK_SEL_MASK BIT(23)
+
+/* PWR1_ASM_CON2 */
+#define PWR1_ASM_CON2_GASRC4_CALI_CK_SEL_MASK BIT(2)
+#define PWR1_ASM_CON2_GASRC5_CALI_CK_SEL_MASK BIT(7)
+#define PWR1_ASM_CON2_GASRC6_CALI_CK_SEL_MASK BIT(12)
+#define PWR1_ASM_CON2_GASRC7_CALI_CK_SEL_MASK BIT(17)
+#define PWR1_ASM_CON2_GASRC8_CALI_CK_SEL_MASK BIT(22)
+#define PWR1_ASM_CON2_GASRC9_CALI_CK_SEL_MASK BIT(27)
+
+/* PWR1_ASM_CON3 */
+#define PWR1_ASM_CON3_GASRC10_CALI_CK_SEL_MASK BIT(2)
+#define PWR1_ASM_CON3_GASRC11_CALI_CK_SEL_MASK BIT(7)
+
+/* AUDIO_TOP_CON0 */
+#define AUDIO_TOP_CON0_PDN_AFE BIT(2)
+#define AUDIO_TOP_CON0_PDN_APLL BIT(23)
+#define AUDIO_TOP_CON0_PDN_APLL_TUNER BIT(19)
+#define AUDIO_TOP_CON0_PDN_APLL2 BIT(24)
+#define AUDIO_TOP_CON0_PDN_APLL2_TUNER BIT(20)
+#define AUDIO_TOP_CON0_PDN_DAC BIT(25)
+#define AUDIO_TOP_CON0_PDN_DAC_HIRES BIT(31)
+#define AUDIO_TOP_CON0_PDN_DAC_PREDIS BIT(26)
+#define AUDIO_TOP_CON0_PDN_SPDIFIN_TUNER BIT(10)
+#define AUDIO_TOP_CON0_PDN_ADC BIT(28)
+#define AUDIO_TOP_CON0_PDN_SPDF BIT(21)
+#define AUDIO_TOP_CON0_PDN_TML BIT(27)
+#define AUDIO_TOP_CON0_PDN_UL_TML BIT(18)
+
+/* AUDIO_TOP_CON1 */
+#define AUDIO_TOP_CON1_PDN_ADC_HIRES BIT(17)
+#define AUDIO_TOP_CON1_PDN_ADDA6_ADC BIT(18)
+#define AUDIO_TOP_CON1_PDN_ADDA6_HIRES BIT(19)
+#define AUDIO_TOP_CON1_PDN_UL_TML_HIRES BIT(16)
+#define AUDIO_TOP_CON1_PDN_DMIC_TML BIT(14)
+#define AUDIO_TOP_CON1_PDN_A1SYS_HOPING BIT(2)
+#define AUDIO_TOP_CON1_PDN_DMIC0 BIT(10)
+#define AUDIO_TOP_CON1_PDN_DMIC1 BIT(11)
+#define AUDIO_TOP_CON1_PDN_DMIC2 BIT(12)
+#define AUDIO_TOP_CON1_PDN_DMIC3 BIT(13)
+
+/* AUDIO_TOP_CON3 */
+#define AUDIO_TOP_CON3_PDN_EARC_TUNER BIT(7)
+#define AUDIO_TOP_CON3_PDN_LINEIN_TUNER BIT(5)
+
+/* AUDIO_TOP_CON4 */
+#define AUDIO_TOP_CON4_PDN_I2S_IN BIT(0)
+#define AUDIO_TOP_CON4_PDN_TDM_IN BIT(1)
+#define AUDIO_TOP_CON4_PDN_I2S_OUT BIT(6)
+#define AUDIO_TOP_CON4_PDN_TDM_OUT BIT(7)
+#define AUDIO_TOP_CON4_PDN_HDMI_OUT BIT(8)
+#define AUDIO_TOP_CON4_PDN_ASRC11 BIT(16)
+#define AUDIO_TOP_CON4_PDN_ASRC12 BIT(17)
+#define AUDIO_TOP_CON4_PDN_A1SYS BIT(21)
+#define AUDIO_TOP_CON4_PDN_A2SYS BIT(22)
+#define AUDIO_TOP_CON4_PDN_A3SYS BIT(30)
+#define AUDIO_TOP_CON4_PDN_A4SYS BIT(31)
+#define AUDIO_TOP_CON4_PDN_PCMIF BIT(24)
+#define AUDIO_TOP_CON4_PDN_INTDIR BIT(20)
+#define AUDIO_TOP_CON4_PDN_MULTI_IN BIT(19)
+
+/* AUDIO_TOP_CON6 */
+#define AUDIO_TOP_CON6_PDN_GASRC11 BIT(11)
+#define AUDIO_TOP_CON6_PDN_GASRC10 BIT(10)
+#define AUDIO_TOP_CON6_PDN_GASRC9 BIT(9)
+#define AUDIO_TOP_CON6_PDN_GASRC8 BIT(8)
+#define AUDIO_TOP_CON6_PDN_GASRC7 BIT(7)
+#define AUDIO_TOP_CON6_PDN_GASRC6 BIT(6)
+#define AUDIO_TOP_CON6_PDN_GASRC5 BIT(5)
+#define AUDIO_TOP_CON6_PDN_GASRC4 BIT(4)
+#define AUDIO_TOP_CON6_PDN_GASRC3 BIT(3)
+#define AUDIO_TOP_CON6_PDN_GASRC2 BIT(2)
+#define AUDIO_TOP_CON6_PDN_GASRC1 BIT(1)
+#define AUDIO_TOP_CON6_PDN_GASRC0 BIT(0)
+
+/* AFE_GAINx_CON0 */
+#define AFE_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8)
+#define AFE_GAIN_CON0_GAIN_MODE_MASK GENMASK(7, 3)
+#define AFE_GAIN_CON0_GAIN_ON_MASK BIT(0)
+
+/* AFE_GAINx_CON1 */
+#define AFE_GAIN_CON1_TARGET_MASK GENMASK(19, 0)
+
+/* AFE_GAINx_CON2 */
+#define AFE_GAIN_CON2_DOWN_STEP_MASK GENMASK(19, 0)
+
+/* AFE_GAINx_CON3 */
+#define AFE_GAIN_CON3_UP_STEP_MASK GENMASK(19, 0)
+
+/* AFE_GAINx_CUR */
+#define AFE_GAIN_CUR_GAIN_MASK GENMASK(19, 0)
+
+/* ASYS_TOP_CON */
+#define ASYS_TOP_CON_A1SYS_TIMING_ON BIT(0)
+#define ASYS_TOP_CON_A2SYS_TIMING_ON BIT(1)
+#define ASYS_TOP_CON_A3SYS_TIMING_ON BIT(4)
+#define ASYS_TOP_CON_A4SYS_TIMING_ON BIT(5)
+#define ASYS_TOP_CON_26M_TIMING_ON BIT(2)
+
+/* PWR2_TOP_CON0 */
+#define PWR2_TOP_CON_DMIC8_SRC_SEL_MASK GENMASK(31, 29)
+#define PWR2_TOP_CON_DMIC7_SRC_SEL_MASK GENMASK(28, 26)
+#define PWR2_TOP_CON_DMIC6_SRC_SEL_MASK GENMASK(25, 23)
+#define PWR2_TOP_CON_DMIC5_SRC_SEL_MASK GENMASK(22, 20)
+#define PWR2_TOP_CON_DMIC4_SRC_SEL_MASK GENMASK(19, 17)
+#define PWR2_TOP_CON_DMIC3_SRC_SEL_MASK GENMASK(16, 14)
+#define PWR2_TOP_CON_DMIC2_SRC_SEL_MASK GENMASK(13, 11)
+#define PWR2_TOP_CON_DMIC1_SRC_SEL_MASK GENMASK(10, 8)
+#define PWR2_TOP_CON_DMIC8_SRC_SEL_VAL(x) ((x) << 29)
+#define PWR2_TOP_CON_DMIC7_SRC_SEL_VAL(x) ((x) << 26)
+#define PWR2_TOP_CON_DMIC6_SRC_SEL_VAL(x) ((x) << 23)
+#define PWR2_TOP_CON_DMIC5_SRC_SEL_VAL(x) ((x) << 20)
+#define PWR2_TOP_CON_DMIC4_SRC_SEL_VAL(x) ((x) << 17)
+#define PWR2_TOP_CON_DMIC3_SRC_SEL_VAL(x) ((x) << 14)
+#define PWR2_TOP_CON_DMIC2_SRC_SEL_VAL(x) ((x) << 11)
+#define PWR2_TOP_CON_DMIC1_SRC_SEL_VAL(x) ((x) << 8)
+
+/* PWR2_TOP_CON1 */
+#define PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(x) BIT(5 + 6 * (x))
+#define PWR2_TOP_CON1_DMIC_CKDIV_ON BIT(1)
+#define PWR2_TOP_CON1_DMIC_CKDIV_ON_SHIFT 1
+
+
+/* PCM_INTF_CON1 */
+#define PCM_INTF_CON1_SYNC_OUT_INV BIT(23)
+#define PCM_INTF_CON1_BCLK_OUT_INV BIT(22)
+#define PCM_INTF_CON1_CLK_OUT_INV_MASK GENMASK(23, 22)
+#define PCM_INTF_CON1_SYNC_IN_INV BIT(21)
+#define PCM_INTF_CON1_BCLK_IN_INV BIT(20)
+#define PCM_INTF_CON1_CLK_IN_INV_MASK GENMASK(21, 20)
+#define PCM_INTF_CON1_PCM_24BIT BIT(16)
+#define PCM_INTF_CON1_PCM_16BIT (0 << 16)
+#define PCM_INTF_CON1_PCM_BIT_MASK BIT(16)
+#define PCM_INTF_CON1_PCM_WLEN_32BCK (0 << 14)
+#define PCM_INTF_CON1_PCM_WLEN_64BCK BIT(14)
+#define PCM_INTF_CON1_PCM_WLEN_MASK BIT(14)
+#define PCM_INTF_CON1_SYNC_LENGTH_MASK GENMASK(13, 9)
+#define PCM_INTF_CON1_PCM_SLAVE BIT(5)
+#define PCM_INTF_CON1_PCM_MASTER (0 << 5)
+#define PCM_INTF_CON1_PCM_M_S_MASK BIT(5)
+#define PCM_INTF_CON1_PCM_MODE_MASK GENMASK(4, 3)
+#define PCM_INTF_CON1_PCM_FMT_MASK GENMASK(2, 1)
+#define PCM_INTF_CON1_PCM_EN BIT(0)
+
+/* PCM_INTF_CON2 */
+#define PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK GENMASK(24, 23)
+#define PCM_INTF_CON2_SYNC_FREQ_MODE_MASK GENMASK(16, 12)
+#define PCM_INTF_CON2_PCM_TX2RX_LPBK BIT(8)
+
+/* AFE_MPHONE_MULTIx_CON0 */
+#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP BIT(3)
+#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP_MASK BIT(3)
+#define AFE_MPHONE_MULTI_CON0_24BIT_DATA (0x1 << 1)
+#define AFE_MPHONE_MULTI_CON0_16BIT_DATA (0x0 << 1)
+#define AFE_MPHONE_MULIT_CON0_24BIT_DATA_MASK BIT(1)
+#define AFE_MPHONE_MULTI_CON0_EN BIT(0)
+
+/* AFE_MPHONE_MULTIx_CON1 */
+#define AFE_MPHONE_MULTI_CON1_SYNC_ON BIT(24)
+#define AFE_MPHONE_MULTI_CON1_24BIT_SWAP_BYPASS BIT(22)
+#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE (0x1 << 19)
+#define AFE_MPHONE_MULTI_CON1_COMPACT_MODE (0x0 << 19)
+#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE_MASK BIT(19)
+#define AFE_MPHONE_MULTI_CON1_HBR_MODE BIT(18)
+#define AFE_MPHONE_MULTI_CON1_LRCK_32_CYCLE (0x2 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_24_CYCLE (0x1 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_16_CYCLE (0x0 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_CYCLE_SEL_MASK GENMASK(17, 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_INV BIT(15)
+#define AFE_MPHONE_MULTI_CON1_DELAY_DATA BIT(14)
+#define AFE_MPHONE_MULTI_CON1_LEFT_ALIGN BIT(13)
+#define AFE_MPHONE_MULTI_CON1_BIT_NUM_MASK GENMASK(12, 8)
+#define AFE_MPHONE_MULTI_CON1_BCK_INV BIT(6)
+#define AFE_MPHONE_MULTI_CON1_CH_NUM_MASK GENMASK(1, 0)
+
+/* AFE_MPHONE_MULTIx_CON2 */
+#define AFE_MPHONE_MULTI_CON2_SEL_SPDIFIN BIT(19)
+/* AFE_AUD_PAD_TOP */
+#define RG_RX_PROTOCOL2 BIT(3)
+#define RG_RX_FIFO_ON BIT(0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define MTKAIF_RXIF_CLKINV_ADC BIT(31)
+#define MTKAIF_RXIF_PROTOCOL2 BIT(16)
+#define MTKAIF_TXIF_PROTOCOL2 BIT(4)
+#define MTKAIF_TXIF_8TO5 BIT(2)
+#define MTKAIF_RXIF_8TO5 BIT(1)
+#define MTKAIF_IF_LOOPBACK1 BIT(0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK GENMASK(15, 12)
+#define MTKAIF_RXIF_DELAY_DATA BIT(8)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG */
+#define ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE BIT(23)
+
+/* AFE_DMICx_UL_SRC_CON0 */
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH1(x) (((x) & 0x7) << 27)
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH2(x) (((x) & 0x7) << 24)
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_MASK GENMASK(29, 24)
+#define AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL BIT(23)
+#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL BIT(22)
+#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL BIT(21)
+#define AFE_DMIC_UL_VOICE_MODE(x) (((x) & GENMASK(2, 0)) << 17)
+#define AFE_DMIC_UL_VOICE_MODE_MASK GENMASK(19, 17)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_8K AFE_DMIC_UL_VOICE_MODE(0)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_16K AFE_DMIC_UL_VOICE_MODE(1)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_32K AFE_DMIC_UL_VOICE_MODE(2)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_48K AFE_DMIC_UL_VOICE_MODE(3)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_96K AFE_DMIC_UL_VOICE_MODE(4)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL(x) (((x) & GENMASK(2, 0)) << 7)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK GENMASK(9, 7)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL BIT(10)
+#define AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL BIT(1)
+#define AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL BIT(0)
+
+/* DMIC_BYPASS_HW_GAIN */
+#define DMIC_BYPASS_HW_GAIN4_ONE_HEART BIT(10)
+#define DMIC_BYPASS_HW_GAIN3_ONE_HEART BIT(9)
+#define DMIC_BYPASS_HW_GAIN2_ONE_HEART BIT(8)
+#define DMIC_BYPASS_HW_GAIN_DMIC4_BYPASS BIT(4)
+#define DMIC_BYPASS_HW_GAIN_DMIC3_BYPASS BIT(3)
+#define DMIC_BYPASS_HW_GAIN_DMIC2_BYPASS BIT(2)
+#define DMIC_BYPASS_HW_GAIN_DMIC1_BYPASS BIT(1)
+
+/* DMIC_GAINx_CON0 */
+#define DMIC_GAIN_CON0_GAIN_ON BIT(0)
+#define DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT 8
+#define DMIC_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8)
+
+/* DMIC_GAINx_CON1 */
+#define DMIC_GAIN_CON1_TARGET_MASK GENMASK(27, 0)
+
+/* DMIC_GAINx_CON2 */
+#define DMIC_GAIN_CON2_DOWN_STEP GENMASK(19, 0)
+
+/* DMIC_GAINx_CON3 */
+#define DMIC_GAIN_CON3_UP_STEP GENMASK(19, 0)
+
+/* DMIC_GAINx_CUR */
+#define DMIC_GAIN_CUR_GAIN_MASK GENMASK(27, 0)
+
+/* ETDM_INx_AFIFO_CON */
+#define ETDM_IN_USE_AFIFO BIT(8)
+#define ETDM_IN_AFIFO_CLOCK_MASK GENMASK(7, 5)
+#define ETDM_IN_AFIFO_MODE_MASK GENMASK(4, 0)
+
+/* ETDM_COWORK_CON0 */
+#define ETDM_OUT1_SLAVE_SEL_MASK GENMASK(23, 20)
+#define ETDM_OUT1_SLAVE_SEL_SHIFT 20
+#define ETDM_OUT1_SYNC_SEL_MASK GENMASK(19, 16)
+#define ETDM_OUT1_SYNC_SEL_SHIFT 16
+
+/* ETDM_COWORK_CON1 */
+#define ETDM_IN1_SDATA_SEL_MASK GENMASK(23, 20)
+#define ETDM_IN1_SDATA_SEL_SHIFT 20
+#define ETDM_IN1_SDATA0_SEL_MASK GENMASK(19, 16)
+#define ETDM_IN1_SDATA0_SEL_SHIFT 16
+#define ETDM_IN1_SYNC_SEL_MASK GENMASK(15, 12)
+#define ETDM_IN1_SYNC_SEL_SHIFT 12
+#define ETDM_IN1_SLAVE_SEL_MASK GENMASK(11, 8)
+#define ETDM_IN1_SLAVE_SEL_SHIFT 8
+
+/* ETDM_COWORK_CON2 */
+#define ETDM_IN2_SYNC_SEL_MASK GENMASK(31, 28)
+#define ETDM_IN2_SYNC_SEL_SHIFT 28
+#define ETDM_IN2_SLAVE_SEL_MASK GENMASK(27, 24)
+#define ETDM_IN2_SLAVE_SEL_SHIFT 24
+#define ETDM_OUT3_SLAVE_SEL_MASK GENMASK(23, 20)
+#define ETDM_OUT3_SLAVE_SEL_SHIFT 20
+#define ETDM_OUT3_SYNC_SEL_MASK GENMASK(19, 16)
+#define ETDM_OUT3_SYNC_SEL_SHIFT 16
+#define ETDM_OUT2_SLAVE_SEL_MASK GENMASK(11, 8)
+#define ETDM_OUT2_SLAVE_SEL_SHIFT 8
+#define ETDM_OUT2_SYNC_SEL_MASK GENMASK(7, 4)
+#define ETDM_OUT2_SYNC_SEL_SHIFT 4
+
+/* ETDM_COWORK_CON3 */
+#define ETDM_IN2_SDATA_SEL_MASK GENMASK(7, 4)
+#define ETDM_IN2_SDATA_SEL_SHIFT 4
+#define ETDM_IN2_SDATA0_SEL_MASK GENMASK(3, 0)
+#define ETDM_IN2_SDATA0_SEL_SHIFT 0
+
+/* ETDM_x_CONx */
+#define ETDM_CON0_CH_NUM_MASK GENMASK(27, 23)
+#define ETDM_CON0_WORD_LEN_MASK GENMASK(20, 16)
+#define ETDM_CON0_BIT_LEN_MASK GENMASK(15, 11)
+#define ETDM_CON0_FORMAT_MASK GENMASK(8, 6)
+#define ETDM_CON0_SLAVE_MODE BIT(5)
+#define ETDM_CON0_SYNC_MODE BIT(1)
+#define ETDM_CON0_EN BIT(0)
+#define ETDM_CON0_EN_SHIFT 0
+
+#define ETDM_OUT_CON0_RELATCH_DOMAIN_MASK GENMASK(29, 28)
+
+#define ETDM_CON1_MCLK_OUTPUT BIT(16)
+
+#define ETDM_IN_CON1_LRCK_AUTO_MODE BIT(31)
+#define ETDM_IN_CON1_LRCK_WIDTH_MASK GENMASK(29, 20)
+
+#define ETDM_OUT_CON1_LRCK_AUTO_MODE BIT(29)
+#define ETDM_OUT_CON1_LRCK_WIDTH_MASK GENMASK(28, 19)
+
+#define ETDM_IN_CON2_MULTI_IP_2CH_MODE BIT(31)
+#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK GENMASK(19, 15)
+#define ETDM_IN_CON2_CLOCK_MASK GENMASK(12, 10)
+#define ETDM_IN_CON2_CLOCK_SHIFT 10
+#define ETDM_IN_CON2_UPDATE_GAP_MASK GENMASK(9, 5)
+
+#define ETDM_OUT_CON2_LRCK_DELAY_BCK_INV BIT(30)
+#define ETDM_OUT_CON2_LRCK_DELAY_0P5T_EN BIT(29)
+
+#define ETDM_IN_CON3_FS_MASK GENMASK(30, 26)
+#define ETDM_IN_CON3_DISABLE_OUT(x) BIT(((x) & 0xffff))
+#define ETDM_IN_CON3_DISABLE_OUT_MASK GENMASK(15, 0)
+
+#define ETDM_IN_CON4_MASTER_LRCK_INV BIT(19)
+#define ETDM_IN_CON4_MASTER_BCK_INV BIT(18)
+#define ETDM_IN_CON4_SLAVE_LRCK_INV BIT(17)
+#define ETDM_IN_CON4_SLAVE_BCK_INV BIT(16)
+
+#define ETDM_OUT_CON4_RELATCH_EN_MASK GENMASK(28, 24)
+#define ETDM_OUT_CON4_CLOCK_MASK GENMASK(8, 6)
+#define ETDM_OUT_CON4_CLOCK_SHIFT 6
+#define ETDM_OUT_CON4_FS_MASK GENMASK(4, 0)
+
+#define ETDM_IN_CON5_LR_SWAP(x) BIT(((x) & 0xffff) + 16)
+#define ETDM_IN_CON5_LR_SWAP_MASK GENMASK(31, 16)
+#define ETDM_IN_CON5_ENABLE_ODD(x) BIT(((x) & 0xffff))
+#define ETDM_IN_CON5_ENABLE_ODD_MASK GENMASK(15, 0)
+
+#define ETDM_OUT_CON5_MASTER_LRCK_INV BIT(10)
+#define ETDM_OUT_CON5_MASTER_BCK_INV BIT(9)
+#define ETDM_OUT_CON5_SLAVE_LRCK_INV BIT(8)
+#define ETDM_OUT_CON5_SLAVE_BCK_INV BIT(7)
+
+/* GASRC_TIMING_CON0 */
+#define GASRC_TIMING_CON0_GASRC0_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON0_GASRC1_IN_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON0_GASRC2_IN_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON0_GASRC3_IN_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON0_GASRC4_IN_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON0_GASRC5_IN_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON1 */
+#define GASRC_TIMING_CON1_GASRC6_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON1_GASRC7_IN_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON1_GASRC8_IN_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON1_GASRC9_IN_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON1_GASRC10_IN_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON1_GASRC11_IN_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON2 */
+#define GASRC_TIMING_CON2_GASRC12_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON2_GASRC13_IN_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON2_GASRC14_IN_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON2_GASRC15_IN_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON2_GASRC16_IN_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON2_GASRC17_IN_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON3 */
+#define GASRC_TIMING_CON3_GASRC18_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON3_GASRC19_IN_MODE_MASK GENMASK(9, 5)
+
+/* GASRC_TIMING_CON4 */
+#define GASRC_TIMING_CON4_GASRC0_OUT_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON4_GASRC1_OUT_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON4_GASRC2_OUT_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON4_GASRC3_OUT_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON4_GASRC4_OUT_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON4_GASRC5_OUT_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON5 */
+#define GASRC_TIMING_CON5_GASRC6_OUT_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON5_GASRC7_OUT_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON5_GASRC8_OUT_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON5_GASRC9_OUT_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON5_GASRC10_OUT_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON5_GASRC11_OUT_MODE_MASK GENMASK(29, 25)
+
+/* AFE_DPTX_CON */
+#define AFE_DPTX_CON_CH_EN_2CH GENMASK(9, 8)
+#define AFE_DPTX_CON_CH_EN_4CH GENMASK(11, 8)
+#define AFE_DPTX_CON_CH_EN_6CH GENMASK(13, 8)
+#define AFE_DPTX_CON_CH_EN_8CH GENMASK(15, 8)
+#define AFE_DPTX_CON_CH_EN_MASK GENMASK(15, 8)
+#define AFE_DPTX_CON_16BIT (0x1 << 2)
+#define AFE_DPTX_CON_24BIT (0x0 << 2)
+#define AFE_DPTX_CON_16BIT_MASK BIT(2)
+#define AFE_DPTX_CON_CH_NUM_2CH (0x0 << 1)
+#define AFE_DPTX_CON_CH_NUM_8CH (0x1 << 1)
+#define AFE_DPTX_CON_CH_NUM_MASK BIT(1)
+#define AFE_DPTX_CON_ON BIT(0)
+#define AFE_DPTX_CON_ON_SHIFT 0
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL_MASK GENMASK(31, 28)
+#define DL_2_CH1_SATURATION_EN_CTL BIT(27)
+#define DL_2_CH2_SATURATION_EN_CTL BIT(26)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE BIT(12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE BIT(11)
+#define DL_2_VOICE_MODE_CTL_PRE BIT(5)
+#define DL_2_GAIN_ON_CTL_PRE_SHIFT 1
+#define DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT 0
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE_MASK GENMASK(31, 16)
+#define DL_2_GAIN_CTL_PRE_SHIFT 16
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOPBACK_MODE_CTL_MASK GENMASK(15, 12)
+#define DL_INPUT_FROM_SINEGEN (4 << 12)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define ADDA_AFE_ON_SHIFT 0
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define DL_USE_NEW_2ND_SDM BIT(30)
+#define ATTGAIN_CTL_MASK GENMASK(5, 0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define UL_MODE_3P25M_CH2_CTL BIT(22)
+#define UL_MODE_3P25M_CH1_CTL BIT(21)
+#define UL_VOICE_MODE_CTL_MASK GENMASK(19, 17)
+#define UL_LOOPBACK_MODE_CTL BIT(2)
+#define UL_SDM3_LEVEL_CTL BIT(1)
+#define UL_SRC_ON_TMP_CTL_SHIFT 0
+
+/* AFE_GASRCx_NEW_CON0 */
+#define AFE_GASRC_NEW_CON0_ONE_HEART BIT(31)
+#define AFE_GASRC_NEW_CON0_CHSET0_CLR_IIR_HISTORY BIT(17)
+#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_MASK GENMASK(15, 14)
+#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_TX (0 << 14)
+#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_RX BIT(14)
+#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_MASK GENMASK(13, 12)
+#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_TX (3 << 12)
+#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_RX (2 << 12)
+#define AFE_GASRC_NEW_CON0_CHSET0_IIR_EN BIT(11)
+#define AFE_GASRC_NEW_CON0_CHSET0_IIR_STAGE_MASK GENMASK(10, 8)
+#define AFE_GASRC_NEW_CON0_CHSET_STR_CLR BIT(4)
+#define AFE_GASRC_NEW_CON0_COEFF_SRAM_CTRL BIT(1)
+#define AFE_GASRC_NEW_CON0_ASM_ON BIT(0)
+
+/* AFE_GASRCx_NEW_CON5 */
+#define AFE_GASRC_NEW_CON5_CALI_LRCK_SEL_MASK GENMASK(3, 1)
+#define AFE_GASRC_NEW_CON5_SOFT_RESET BIT(0)
+
+/* AFE_GASRCx_NEW_CON6 */
+#define AFE_GASRC_NEW_CON6_FREQ_CALI_CYCLE_MASK GENMASK(31, 16)
+#define AFE_GASRC_NEW_CON6_AUTO_TUNE_FREQ3 BIT(12)
+#define AFE_GASRC_NEW_CON6_COMP_FREQ_RES_EN BIT(11)
+#define AFE_GASRC_NEW_CON6_CALI_SIG_MUX_SEL_MASK GENMASK(9, 8)
+#define AFE_GASRC_NEW_CON6_FREQ_CALI_BP_DGL BIT(7)
+#define AFE_GASRC_NEW_CON6_AUTO_TUNE_FREQ2 BIT(3)
+#define AFE_GASRC_NEW_CON6_FREQ_CALI_AUTO_RESTART BIT(2)
+#define AFE_GASRC_NEW_CON6_CALI_USE_FREQ_OUT BIT(1)
+#define AFE_GASRC_NEW_CON6_CALI_EN BIT(0)
+
+/* AFE_GASRCx_NEW_CON7 */
+#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_MASK GENMASK(23, 0)
+#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_49M (0x3c00)
+#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_45M (0x3720)
+
+#endif
diff --git a/sound/soc/mediatek/mt8189/Makefile b/sound/soc/mediatek/mt8189/Makefile
new file mode 100644
index 000000000000..83a033284182
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# common include path
+subdir-ccflags-y += -I$(srctree)/sound/soc/mediatek/common
+
+# platform driver
+snd-soc-mt8189-afe-objs += \
+ mt8189-afe-pcm.o \
+ mt8189-afe-clk.o \
+ mt8189-dai-adda.o \
+ mt8189-dai-i2s.o \
+ mt8189-dai-pcm.o \
+ mt8189-dai-tdm.o
+
+obj-$(CONFIG_SND_SOC_MT8189) += snd-soc-mt8189-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT8189_NAU8825) += mt8189-nau8825.o
diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-clk.c b/sound/soc/mediatek/mt8189/mt8189-afe-clk.c
new file mode 100644
index 000000000000..fc7a7a73b0cf
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-afe-clk.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8189-afe-clk.c -- Mediatek 8189 afe clock ctrl
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "mt8189-afe-common.h"
+#include "mt8189-afe-clk.h"
+
+/* mck */
+struct mt8189_mck_div {
+ int m_sel_id;
+ int div_clk_id;
+};
+
+static const struct mt8189_mck_div mck_div[MT8189_MCK_NUM] = {
+ [MT8189_I2SIN0_MCK] = {
+ .m_sel_id = MT8189_CLK_TOP_I2SIN0_M_SEL,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_I2SIN0,
+ },
+ [MT8189_I2SIN1_MCK] = {
+ .m_sel_id = MT8189_CLK_TOP_I2SIN1_M_SEL,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_I2SIN1,
+ },
+ [MT8189_I2SOUT0_MCK] = {
+ .m_sel_id = MT8189_CLK_TOP_I2SOUT0_M_SEL,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_I2SOUT0,
+ },
+ [MT8189_I2SOUT1_MCK] = {
+ .m_sel_id = MT8189_CLK_TOP_I2SOUT1_M_SEL,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_I2SOUT1,
+ },
+ [MT8189_FMI2S_MCK] = {
+ .m_sel_id = MT8189_CLK_TOP_FMI2S_M_SEL,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_FMI2S,
+ },
+ [MT8189_TDMOUT_MCK] = {
+ .m_sel_id = MT8189_CLK_TOP_TDMOUT_M_SEL,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_TDMOUT_M,
+ },
+ [MT8189_TDMOUT_BCK] = {
+ .m_sel_id = -1,
+ .div_clk_id = MT8189_CLK_TOP_APLL12_DIV_TDMOUT_B,
+ },
+};
+
+static const char *aud_clks[MT8189_CLK_NUM] = {
+ [MT8189_CLK_TOP_MUX_AUDIOINTBUS] = "top_aud_intbus",
+ [MT8189_CLK_TOP_MUX_AUD_ENG1] = "top_aud_eng1",
+ [MT8189_CLK_TOP_MUX_AUD_ENG2] = "top_aud_eng2",
+ [MT8189_CLK_TOP_MUX_AUDIO_H] = "top_aud_h",
+ /* pll */
+ [MT8189_CLK_TOP_APLL1_CK] = "apll1",
+ [MT8189_CLK_TOP_APLL2_CK] = "apll2",
+ /* divider */
+ [MT8189_CLK_TOP_APLL1_D4] = "apll1_d4",
+ [MT8189_CLK_TOP_APLL2_D4] = "apll2_d4",
+ [MT8189_CLK_TOP_APLL12_DIV_I2SIN0] = "apll12_div_i2sin0",
+ [MT8189_CLK_TOP_APLL12_DIV_I2SIN1] = "apll12_div_i2sin1",
+ [MT8189_CLK_TOP_APLL12_DIV_I2SOUT0] = "apll12_div_i2sout0",
+ [MT8189_CLK_TOP_APLL12_DIV_I2SOUT1] = "apll12_div_i2sout1",
+ [MT8189_CLK_TOP_APLL12_DIV_FMI2S] = "apll12_div_fmi2s",
+ [MT8189_CLK_TOP_APLL12_DIV_TDMOUT_M] = "apll12_div_tdmout_m",
+ [MT8189_CLK_TOP_APLL12_DIV_TDMOUT_B] = "apll12_div_tdmout_b",
+ /* mux */
+ [MT8189_CLK_TOP_MUX_AUD_1] = "top_apll1",
+ [MT8189_CLK_TOP_MUX_AUD_2] = "top_apll2",
+ [MT8189_CLK_TOP_I2SIN0_M_SEL] = "top_i2sin0",
+ [MT8189_CLK_TOP_I2SIN1_M_SEL] = "top_i2sin1",
+ [MT8189_CLK_TOP_I2SOUT0_M_SEL] = "top_i2sout0",
+ [MT8189_CLK_TOP_I2SOUT1_M_SEL] = "top_i2sout1",
+ [MT8189_CLK_TOP_FMI2S_M_SEL] = "top_fmi2s",
+ [MT8189_CLK_TOP_TDMOUT_M_SEL] = "top_dptx",
+ /* top 26m*/
+ [MT8189_CLK_TOP_CLK26M] = "clk26m",
+ /* peri */
+ [MT8189_CLK_PERAO_AUDIO_SLV_CK_PERI] = "aud_slv_ck_peri",
+ [MT8189_CLK_PERAO_AUDIO_MST_CK_PERI] = "aud_mst_ck_peri",
+ [MT8189_CLK_PERAO_INTBUS_CK_PERI] = "aud_intbus_ck_peri",
+};
+
+int mt8189_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(afe->dev, "failed to enable clk\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8189_afe_enable_clk);
+
+void mt8189_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_disable_unprepare(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+EXPORT_SYMBOL_GPL(mt8189_afe_disable_clk);
+
+static int mt8189_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_err(afe->dev, "failed to set clk rate\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt8189_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent)
+{
+ int ret;
+
+ if (clk && parent) {
+ ret = clk_set_parent(clk, parent);
+ if (ret) {
+ dev_dbg(afe->dev, "failed to set clk parent %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int get_top_cg_reg(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8189_AUDIO_26M_EN_ON:
+ case MT8189_AUDIO_F3P25M_EN_ON:
+ case MT8189_AUDIO_APLL1_EN_ON:
+ case MT8189_AUDIO_APLL2_EN_ON:
+ return AUDIO_ENGEN_CON0;
+ case MT8189_CG_AUDIO_HOPPING_CK:
+ case MT8189_CG_AUDIO_F26M_CK:
+ case MT8189_CG_APLL1_CK:
+ case MT8189_CG_APLL2_CK:
+ case MT8189_PDN_APLL_TUNER2:
+ case MT8189_PDN_APLL_TUNER1:
+ return AUDIO_TOP_CON4;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_mask(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8189_AUDIO_26M_EN_ON:
+ return AUDIO_26M_EN_ON_MASK_SFT;
+ case MT8189_AUDIO_F3P25M_EN_ON:
+ return AUDIO_F3P25M_EN_ON_MASK_SFT;
+ case MT8189_AUDIO_APLL1_EN_ON:
+ return AUDIO_APLL1_EN_ON_MASK_SFT;
+ case MT8189_AUDIO_APLL2_EN_ON:
+ return AUDIO_APLL2_EN_ON_MASK_SFT;
+ case MT8189_CG_AUDIO_HOPPING_CK:
+ return CG_AUDIO_HOPPING_CK_MASK_SFT;
+ case MT8189_CG_AUDIO_F26M_CK:
+ return CG_AUDIO_F26M_CK_MASK_SFT;
+ case MT8189_CG_APLL1_CK:
+ return CG_APLL1_CK_MASK_SFT;
+ case MT8189_CG_APLL2_CK:
+ return CG_APLL2_CK_MASK_SFT;
+ case MT8189_PDN_APLL_TUNER2:
+ return PDN_APLL_TUNER2_MASK_SFT;
+ case MT8189_PDN_APLL_TUNER1:
+ return PDN_APLL_TUNER1_MASK_SFT;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_on_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8189_AUDIO_26M_EN_ON:
+ case MT8189_AUDIO_F3P25M_EN_ON:
+ case MT8189_AUDIO_APLL1_EN_ON:
+ case MT8189_AUDIO_APLL2_EN_ON:
+ return get_top_cg_mask(cg_type);
+ case MT8189_CG_AUDIO_HOPPING_CK:
+ case MT8189_CG_AUDIO_F26M_CK:
+ case MT8189_CG_APLL1_CK:
+ case MT8189_CG_APLL2_CK:
+ case MT8189_PDN_APLL_TUNER2:
+ case MT8189_PDN_APLL_TUNER1:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_off_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8189_AUDIO_26M_EN_ON:
+ case MT8189_AUDIO_F3P25M_EN_ON:
+ case MT8189_AUDIO_APLL1_EN_ON:
+ case MT8189_AUDIO_APLL2_EN_ON:
+ return 0;
+ case MT8189_CG_AUDIO_HOPPING_CK:
+ case MT8189_CG_AUDIO_F26M_CK:
+ case MT8189_CG_APLL1_CK:
+ case MT8189_CG_APLL2_CK:
+ case MT8189_PDN_APLL_TUNER2:
+ case MT8189_PDN_APLL_TUNER1:
+ return get_top_cg_mask(cg_type);
+ default:
+ return get_top_cg_mask(cg_type);
+ }
+}
+
+static int mt8189_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_on_val(cg_type);
+
+ if (!afe->regmap) {
+ dev_err(afe->dev, "afe regmap is null !!!\n");
+ return 0;
+ }
+
+ dev_dbg(afe->dev, "reg: 0x%x, mask: 0x%x, val: 0x%x\n", reg, mask, val);
+
+ return regmap_update_bits(afe->regmap, reg, mask, val);
+}
+
+static void mt8189_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_off_val(cg_type);
+
+ if (!afe->regmap) {
+ dev_warn(afe->dev, "skip regmap\n");
+ return;
+ }
+
+ dev_dbg(afe->dev, "reg: 0x%x, mask: 0x%x, val: 0x%x\n", reg, mask, val);
+ regmap_update_bits(afe->regmap, reg, mask, val);
+}
+
+static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ dev_dbg(afe->dev, "enable: %d\n", enable);
+
+ if (enable) {
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8189_CLK_TOP_APLL1_CK]);
+ if (ret)
+ goto clk_ck_mux_aud1_parent_err;
+
+ /* 180.6336 / 4 = 45.1584MHz */
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG1]);
+ if (ret)
+ goto clk_ck_mux_eng1_err;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[MT8189_CLK_TOP_APLL1_D4]);
+ if (ret)
+ goto clk_ck_mux_eng1_parent_err;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ if (ret)
+ goto clk_ck_mux_audio_h_err;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_APLL1_CK]);
+ if (ret)
+ goto clk_ck_mux_audio_h_parent_err;
+ } else {
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG1]);
+
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1]);
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ }
+
+ return 0;
+
+clk_ck_mux_audio_h_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+clk_ck_mux_audio_h_err:
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+clk_ck_mux_eng1_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG1]);
+clk_ck_mux_eng1_err:
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+clk_ck_mux_aud1_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1]);
+
+ return ret;
+}
+
+static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ dev_dbg(afe->dev, "enable: %d\n", enable);
+
+ if (enable) {
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8189_CLK_TOP_APLL2_CK]);
+ if (ret)
+ goto clk_ck_mux_aud2_parent_err;
+
+ /* 196.608 / 4 = 49.152MHz */
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG2]);
+ if (ret)
+ goto clk_ck_mux_eng2_err;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[MT8189_CLK_TOP_APLL2_D4]);
+ if (ret)
+ goto clk_ck_mux_eng2_parent_err;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ if (ret)
+ goto clk_ck_mux_audio_h_err;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_APLL2_CK]);
+ if (ret)
+ goto clk_ck_mux_audio_h_parent_err;
+ } else {
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG2]);
+
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2]);
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ }
+
+ return 0;
+
+clk_ck_mux_audio_h_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+clk_ck_mux_audio_h_err:
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+clk_ck_mux_eng2_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_ENG2]);
+clk_ck_mux_eng2_err:
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+clk_ck_mux_aud2_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2]);
+
+ return ret;
+}
+
+static int mt8189_afe_disable_apll(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1]);
+ if (ret)
+ goto clk_ck_mux_aud1_err;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ if (ret)
+ goto clk_ck_mux_aud1_parent_err;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2]);
+ if (ret)
+ goto clk_ck_mux_aud2_err;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ if (ret)
+ goto clk_ck_mux_aud2_parent_err;
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1]);
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2]);
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+
+ return 0;
+
+clk_ck_mux_aud2_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_2]);
+clk_ck_mux_aud2_err:
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8189_CLK_TOP_APLL1_CK]);
+clk_ck_mux_aud1_parent_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUD_1]);
+clk_ck_mux_aud1_err:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+
+ return ret;
+}
+
+int mt8189_apll1_enable(struct mtk_base_afe *afe)
+{
+ int ret;
+
+ /* setting for APLL */
+ ret = apll1_mux_setting(afe, true);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_top_cg(afe, MT8189_CG_APLL1_CK);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_top_cg(afe, MT8189_PDN_APLL_TUNER1);
+ if (ret)
+ return ret;
+
+ /* sel 44.1kHz:1, apll_div:7, upper bound:3 */
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT |
+ UPPER_BOUND_MASK_SFT,
+ (0x1 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
+ (3 << UPPER_BOUND_SFT));
+
+ /* apll1 freq tuner enable */
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x1 << FREQ_TUNER_EN_SFT);
+
+ /* audio apll1 on */
+ ret = mt8189_afe_enable_top_cg(afe, MT8189_AUDIO_APLL1_EN_ON);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void mt8189_apll1_disable(struct mtk_base_afe *afe)
+{
+ /* audio apll1 off */
+ mt8189_afe_disable_top_cg(afe, MT8189_AUDIO_APLL1_EN_ON);
+
+ /* apll1 freq tuner disable */
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x0);
+
+ mt8189_afe_disable_top_cg(afe, MT8189_PDN_APLL_TUNER1);
+ mt8189_afe_disable_top_cg(afe, MT8189_CG_APLL1_CK);
+ apll1_mux_setting(afe, false);
+}
+
+int mt8189_apll2_enable(struct mtk_base_afe *afe)
+{
+ int ret;
+
+ /* setting for APLL */
+ ret = apll2_mux_setting(afe, true);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_top_cg(afe, MT8189_CG_APLL2_CK);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_top_cg(afe, MT8189_PDN_APLL_TUNER2);
+ if (ret)
+ return ret;
+
+ /* sel 48kHz: 2, apll_div: 7, upper bound: 3*/
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT |
+ UPPER_BOUND_MASK_SFT,
+ (0x2 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
+ (3 << UPPER_BOUND_SFT));
+
+ /* apll2 freq tuner enable */
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x1 << FREQ_TUNER_EN_SFT);
+
+ /* audio apll2 on */
+ ret = mt8189_afe_enable_top_cg(afe, MT8189_AUDIO_APLL2_EN_ON);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void mt8189_apll2_disable(struct mtk_base_afe *afe)
+{
+ /* audio apll2 off */
+ mt8189_afe_disable_top_cg(afe, MT8189_AUDIO_APLL2_EN_ON);
+
+ /* apll2 freq tuner disable */
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x0);
+
+ mt8189_afe_disable_top_cg(afe, MT8189_PDN_APLL_TUNER2);
+ mt8189_afe_disable_top_cg(afe, MT8189_CG_APLL2_CK);
+ apll2_mux_setting(afe, false);
+}
+
+int mt8189_get_apll_rate(struct mtk_base_afe *afe, int apll)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int clk_id;
+
+ if (apll < MT8189_APLL1 || apll > MT8189_APLL2) {
+ dev_warn(afe->dev, "invalid clk id %d\n", apll);
+ return 0;
+ }
+
+ if (apll == MT8189_APLL1)
+ clk_id = MT8189_CLK_TOP_APLL1_CK;
+ else
+ clk_id = MT8189_CLK_TOP_APLL2_CK;
+
+ return clk_get_rate(afe_priv->clk[clk_id]);
+}
+
+int mt8189_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return (rate % 8000) ? MT8189_APLL1 : MT8189_APLL2;
+}
+
+int mt8189_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8189_APLL1;
+
+ return MT8189_APLL2;
+}
+
+int mt8189_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int apll = mt8189_get_apll_by_rate(afe, rate);
+ int apll_clk_id = apll == MT8189_APLL1 ?
+ MT8189_CLK_TOP_MUX_AUD_1 : MT8189_CLK_TOP_MUX_AUD_2;
+ int m_sel_id;
+ int div_clk_id;
+ int ret;
+
+ dev_dbg(afe->dev, "mck_id: %d, rate: %d\n", mck_id, rate);
+
+ if (mck_id >= MT8189_MCK_NUM || mck_id < 0)
+ return -EINVAL;
+
+ m_sel_id = mck_div[mck_id].m_sel_id;
+ div_clk_id = mck_div[mck_id].div_clk_id;
+
+ /* select apll */
+ if (m_sel_id >= 0) {
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[m_sel_id]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[m_sel_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret)
+ return ret;
+ }
+
+ /* enable div, set rate */
+ if (div_clk_id < 0) {
+ dev_err(afe->dev, "invalid div_clk_id %d\n", div_clk_id);
+ return -EINVAL;
+ }
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[div_clk_id]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_set_clk_rate(afe, afe_priv->clk[div_clk_id], rate);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int mt8189_mck_disable(struct mtk_base_afe *afe, int mck_id)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int m_sel_id;
+ int div_clk_id;
+
+ dev_dbg(afe->dev, "mck_id: %d.\n", mck_id);
+
+ if (mck_id < 0) {
+ dev_err(afe->dev, "mck_id = %d < 0\n", mck_id);
+ return -EINVAL;
+ }
+
+ m_sel_id = mck_div[mck_id].m_sel_id;
+ div_clk_id = mck_div[mck_id].div_clk_id;
+
+ if (div_clk_id < 0) {
+ dev_err(afe->dev, "div_clk_id = %d < 0\n",
+ div_clk_id);
+ return -EINVAL;
+ }
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[div_clk_id]);
+
+ if (m_sel_id >= 0)
+ mt8189_afe_disable_clk(afe, afe_priv->clk[m_sel_id]);
+
+ return 0;
+}
+
+int mt8189_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ /* bus clock for AFE internal access, like AFE SRAM */
+ mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS]);
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ /* enable audio clock source */
+ mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+
+ return 0;
+}
+
+int mt8189_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS]);
+
+ return 0;
+}
+
+int mt8189_afe_enable_main_clock(struct mtk_base_afe *afe)
+{
+ return mt8189_afe_enable_top_cg(afe, MT8189_AUDIO_26M_EN_ON);
+}
+
+void mt8189_afe_disable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8189_afe_disable_top_cg(afe, MT8189_AUDIO_26M_EN_ON);
+}
+
+static int mt8189_afe_enable_ao_clock(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* Peri clock AO enable */
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_PERAO_INTBUS_CK_PERI]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_PERAO_AUDIO_SLV_CK_PERI]);
+ if (ret)
+ goto err_clk_perao_slv;
+
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_PERAO_AUDIO_MST_CK_PERI]);
+ if (ret)
+ goto err_clk_perao_mst;
+
+ return 0;
+
+err_clk_perao_mst:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_PERAO_AUDIO_SLV_CK_PERI]);
+err_clk_perao_slv:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_PERAO_INTBUS_CK_PERI]);
+
+ return ret;
+}
+
+int mt8189_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+ int i;
+
+ afe_priv->clk = devm_kcalloc(afe->dev, MT8189_CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < MT8189_CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "devm_clk_get %s fail\n", aud_clks[i]);
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ ret = mt8189_afe_disable_apll(afe);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_enable_ao_clock(afe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-clk.h b/sound/soc/mediatek/mt8189/mt8189-afe-clk.h
new file mode 100644
index 000000000000..9ce5d1043feb
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-afe-clk.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8189-afe-clk.h -- Mediatek 8189 afe clock ctrl definition
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#ifndef _MT8189_AFE_CLOCK_CTRL_H_
+#define _MT8189_AFE_CLOCK_CTRL_H_
+
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+
+enum {
+ MT8189_APLL1,
+ MT8189_APLL2,
+};
+
+enum {
+ MT8189_CLK_TOP_MUX_AUDIOINTBUS,
+ MT8189_CLK_TOP_MUX_AUD_ENG1,
+ MT8189_CLK_TOP_MUX_AUD_ENG2,
+ MT8189_CLK_TOP_MUX_AUDIO_H,
+ /* pll */
+ MT8189_CLK_TOP_APLL1_CK,
+ MT8189_CLK_TOP_APLL2_CK,
+ /* divider */
+ MT8189_CLK_TOP_APLL1_D4,
+ MT8189_CLK_TOP_APLL2_D4,
+ MT8189_CLK_TOP_APLL12_DIV_I2SIN0,
+ MT8189_CLK_TOP_APLL12_DIV_I2SIN1,
+ MT8189_CLK_TOP_APLL12_DIV_I2SOUT0,
+ MT8189_CLK_TOP_APLL12_DIV_I2SOUT1,
+ MT8189_CLK_TOP_APLL12_DIV_FMI2S,
+ MT8189_CLK_TOP_APLL12_DIV_TDMOUT_M,
+ MT8189_CLK_TOP_APLL12_DIV_TDMOUT_B,
+ /* mux */
+ MT8189_CLK_TOP_MUX_AUD_1,
+ MT8189_CLK_TOP_MUX_AUD_2,
+ MT8189_CLK_TOP_I2SIN0_M_SEL,
+ MT8189_CLK_TOP_I2SIN1_M_SEL,
+ MT8189_CLK_TOP_I2SOUT0_M_SEL,
+ MT8189_CLK_TOP_I2SOUT1_M_SEL,
+ MT8189_CLK_TOP_FMI2S_M_SEL,
+ MT8189_CLK_TOP_TDMOUT_M_SEL,
+ /* top 26m */
+ MT8189_CLK_TOP_CLK26M,
+ /* peri */
+ MT8189_CLK_PERAO_AUDIO_SLV_CK_PERI,
+ MT8189_CLK_PERAO_AUDIO_MST_CK_PERI,
+ MT8189_CLK_PERAO_INTBUS_CK_PERI,
+ MT8189_CLK_NUM,
+};
+
+struct mtk_base_afe;
+
+int mt8189_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
+int mt8189_mck_disable(struct mtk_base_afe *afe, int mck_id);
+int mt8189_get_apll_rate(struct mtk_base_afe *afe, int apll);
+int mt8189_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8189_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
+int mt8189_init_clock(struct mtk_base_afe *afe);
+int mt8189_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
+void mt8189_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8189_apll1_enable(struct mtk_base_afe *afe);
+void mt8189_apll1_disable(struct mtk_base_afe *afe);
+int mt8189_apll2_enable(struct mtk_base_afe *afe);
+void mt8189_apll2_disable(struct mtk_base_afe *afe);
+int mt8189_afe_enable_main_clock(struct mtk_base_afe *afe);
+void mt8189_afe_disable_main_clock(struct mtk_base_afe *afe);
+int mt8189_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
+int mt8189_afe_disable_reg_rw_clk(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-common.h b/sound/soc/mediatek/mt8189/mt8189-afe-common.h
new file mode 100644
index 000000000000..910d90d3d146
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-afe-common.h
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8189-afe-common.h -- Mediatek 8189 audio driver definitions
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#ifndef _MT_8189_AFE_COMMON_H_
+#define _MT_8189_AFE_COMMON_H_
+
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "mt8189-reg.h"
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MTK_AFE_RATE_8K,
+ MTK_AFE_RATE_11K,
+ MTK_AFE_RATE_12K,
+ MTK_AFE_RATE_384K,
+ MTK_AFE_RATE_16K,
+ MTK_AFE_RATE_22K,
+ MTK_AFE_RATE_24K,
+ MTK_AFE_RATE_352K,
+ MTK_AFE_RATE_32K,
+ MTK_AFE_RATE_44K,
+ MTK_AFE_RATE_48K,
+ MTK_AFE_RATE_88K,
+ MTK_AFE_RATE_96K,
+ MTK_AFE_RATE_176K,
+ MTK_AFE_RATE_192K,
+ MTK_AFE_RATE_260K,
+};
+
+/* HW IPM 2.0 */
+enum {
+ MTK_AFE_IPM2P0_RATE_8K = 0x0,
+ MTK_AFE_IPM2P0_RATE_11K = 0x1,
+ MTK_AFE_IPM2P0_RATE_12K = 0x2,
+ MTK_AFE_IPM2P0_RATE_16K = 0x4,
+ MTK_AFE_IPM2P0_RATE_22K = 0x5,
+ MTK_AFE_IPM2P0_RATE_24K = 0x6,
+ MTK_AFE_IPM2P0_RATE_32K = 0x8,
+ MTK_AFE_IPM2P0_RATE_44K = 0x9,
+ MTK_AFE_IPM2P0_RATE_48K = 0xa,
+ MTK_AFE_IPM2P0_RATE_88K = 0xd,
+ MTK_AFE_IPM2P0_RATE_96K = 0xe,
+ MTK_AFE_IPM2P0_RATE_176K = 0x11,
+ MTK_AFE_IPM2P0_RATE_192K = 0x12,
+ MTK_AFE_IPM2P0_RATE_352K = 0x15,
+ MTK_AFE_IPM2P0_RATE_384K = 0x16,
+};
+
+enum {
+ MTK_AFE_DAI_MEMIF_RATE_8K,
+ MTK_AFE_DAI_MEMIF_RATE_16K,
+ MTK_AFE_DAI_MEMIF_RATE_32K,
+ MTK_AFE_DAI_MEMIF_RATE_48K,
+};
+
+enum {
+ MTK_AFE_PCM_RATE_8K,
+ MTK_AFE_PCM_RATE_16K,
+ MTK_AFE_PCM_RATE_32K,
+ MTK_AFE_PCM_RATE_48K,
+};
+
+enum {
+ MTKAIF_PROTOCOL_1,
+ MTKAIF_PROTOCOL_2,
+ MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+enum {
+ MT8189_MEMIF_DL0,
+ MT8189_MEMIF_DL1,
+ MT8189_MEMIF_DL2,
+ MT8189_MEMIF_DL3,
+ MT8189_MEMIF_DL4,
+ MT8189_MEMIF_DL5,
+ MT8189_MEMIF_DL6,
+ MT8189_MEMIF_DL7,
+ MT8189_MEMIF_DL8,
+ MT8189_MEMIF_DL23,
+ MT8189_MEMIF_DL24,
+ MT8189_MEMIF_DL25,
+ MT8189_MEMIF_DL_24CH,
+ MT8189_MEMIF_VUL0,
+ MT8189_MEMIF_VUL1,
+ MT8189_MEMIF_VUL2,
+ MT8189_MEMIF_VUL3,
+ MT8189_MEMIF_VUL4,
+ MT8189_MEMIF_VUL5,
+ MT8189_MEMIF_VUL6,
+ MT8189_MEMIF_VUL7,
+ MT8189_MEMIF_VUL8,
+ MT8189_MEMIF_VUL9,
+ MT8189_MEMIF_VUL10,
+ MT8189_MEMIF_VUL24,
+ MT8189_MEMIF_VUL25,
+ MT8189_MEMIF_VUL_CM0,
+ MT8189_MEMIF_VUL_CM1,
+ MT8189_MEMIF_ETDM_IN0,
+ MT8189_MEMIF_ETDM_IN1,
+ MT8189_MEMIF_HDMI,
+ MT8189_MEMIF_NUM,
+ MT8189_DAI_ADDA = MT8189_MEMIF_NUM,
+ MT8189_DAI_ADDA_CH34,
+ MT8189_DAI_ADDA_CH56,
+ MT8189_DAI_AP_DMIC,
+ MT8189_DAI_AP_DMIC_CH34,
+ MT8189_DAI_I2S_IN0,
+ MT8189_DAI_I2S_IN1,
+ MT8189_DAI_I2S_OUT0,
+ MT8189_DAI_I2S_OUT1,
+ MT8189_DAI_I2S_OUT4,
+ MT8189_DAI_PCM_0,
+ MT8189_DAI_TDM,
+ MT8189_DAI_TDM_DPTX,
+ MT8189_DAI_NUM,
+};
+
+/* update irq ID (= enum) from AFE_IRQ_MCU_STATUS */
+enum {
+ MT8189_IRQ_0,
+ MT8189_IRQ_1,
+ MT8189_IRQ_2,
+ MT8189_IRQ_3,
+ MT8189_IRQ_4,
+ MT8189_IRQ_5,
+ MT8189_IRQ_6,
+ MT8189_IRQ_7,
+ MT8189_IRQ_8,
+ MT8189_IRQ_9,
+ MT8189_IRQ_10,
+ MT8189_IRQ_11,
+ MT8189_IRQ_12,
+ MT8189_IRQ_13,
+ MT8189_IRQ_14,
+ MT8189_IRQ_15,
+ MT8189_IRQ_16,
+ MT8189_IRQ_17,
+ MT8189_IRQ_18,
+ MT8189_IRQ_19,
+ MT8189_IRQ_20,
+ MT8189_IRQ_21,
+ MT8189_IRQ_22,
+ MT8189_IRQ_23,
+ MT8189_IRQ_24,
+ MT8189_IRQ_25,
+ MT8189_IRQ_26,
+ MT8189_IRQ_31,
+ MT8189_IRQ_NUM,
+};
+
+/* update irq ID (= enum) from AFE_IRQ_MCU_STATUS */
+enum {
+ MT8189_CUS_IRQ_TDM, /* used only for TDM */
+ MT8189_CUS_IRQ_NUM,
+};
+
+enum {
+ /* AUDIO_ENGEN_CON0 */
+ MT8189_AUDIO_26M_EN_ON,
+ MT8189_AUDIO_F3P25M_EN_ON,
+ MT8189_AUDIO_APLL1_EN_ON,
+ MT8189_AUDIO_APLL2_EN_ON,
+ MT8189_AUDIO_F26M_EN_RST,
+ MT8189_MULTI_USER_RST,
+ MT8189_MULTI_USER_BYPASS,
+ /* AUDIO_TOP_CON4 */
+ MT8189_CG_AUDIO_HOPPING_CK,
+ MT8189_CG_AUDIO_F26M_CK,
+ MT8189_CG_APLL1_CK,
+ MT8189_CG_APLL2_CK,
+ MT8189_PDN_APLL_TUNER2,
+ MT8189_PDN_APLL_TUNER1,
+ MT8189_AUDIO_CG_NUM,
+};
+
+/* MCLK */
+enum {
+ MT8189_I2SIN0_MCK,
+ MT8189_I2SIN1_MCK,
+ MT8189_I2SOUT0_MCK,
+ MT8189_I2SOUT1_MCK,
+ MT8189_FMI2S_MCK,
+ MT8189_TDMOUT_MCK,
+ MT8189_TDMOUT_BCK,
+ MT8189_MCK_NUM,
+};
+
+enum {
+ CM0,
+ CM1,
+ CM_NUM,
+};
+
+struct clk;
+
+struct mt8189_afe_private {
+ struct clk **clk;
+ struct regmap *pmic_regmap;
+
+ /* dai */
+ void *dai_priv[MT8189_DAI_NUM];
+
+ /* adda */
+ int mtkaif_protocol;
+ int mtkaif_chosen_phase[4];
+ int mtkaif_phase_cycle[4];
+ int mtkaif_calibration_num_phase;
+ int mtkaif_dmic;
+ int mtkaif_dmic_ch34;
+
+ /* add for vs1 voter */
+ bool is_adda_dl_on;
+ bool is_adda_ul_on;
+ /* adda dl vol idx is at maximum */
+ bool is_adda_dl_max_vol;
+ /* current vote status of vs1 */
+ bool is_mt6363_vote;
+
+ /* mck */
+ int mck_rate[MT8189_MCK_NUM];
+
+ /* channel merge */
+ unsigned int cm_rate[CM_NUM];
+ unsigned int cm_channels;
+};
+
+int mt8189_dai_adda_register(struct mtk_base_afe *afe);
+int mt8189_dai_i2s_register(struct mtk_base_afe *afe);
+int mt8189_dai_pcm_register(struct mtk_base_afe *afe);
+int mt8189_dai_tdm_register(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c
new file mode 100644
index 000000000000..166ece74270e
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c
@@ -0,0 +1,2615 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek ALSA SoC AFE platform driver for 8189
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "mt8189-afe-clk.h"
+#include "mt8189-afe-common.h"
+#include "mt8189-interconnection.h"
+
+#include "../common/mtk-afe-fe-dai.h"
+#include "../common/mtk-afe-platform-driver.h"
+
+static const struct snd_pcm_hardware mt8189_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .period_bytes_min = 96,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 1024,
+ .fifo_size = 0,
+};
+
+static unsigned int mt8189_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_IPM2P0_RATE_8K;
+ case 11025:
+ return MTK_AFE_IPM2P0_RATE_11K;
+ case 12000:
+ return MTK_AFE_IPM2P0_RATE_12K;
+ case 16000:
+ return MTK_AFE_IPM2P0_RATE_16K;
+ case 22050:
+ return MTK_AFE_IPM2P0_RATE_22K;
+ case 24000:
+ return MTK_AFE_IPM2P0_RATE_24K;
+ case 32000:
+ return MTK_AFE_IPM2P0_RATE_32K;
+ case 44100:
+ return MTK_AFE_IPM2P0_RATE_44K;
+ case 48000:
+ return MTK_AFE_IPM2P0_RATE_48K;
+ case 88200:
+ return MTK_AFE_IPM2P0_RATE_88K;
+ case 96000:
+ return MTK_AFE_IPM2P0_RATE_96K;
+ case 176400:
+ return MTK_AFE_IPM2P0_RATE_176K;
+ case 192000:
+ return MTK_AFE_IPM2P0_RATE_192K;
+ /* not support 260K */
+ case 352800:
+ return MTK_AFE_IPM2P0_RATE_352K;
+ case 384000:
+ return MTK_AFE_IPM2P0_RATE_384K;
+ default:
+ dev_warn(dev, "rate %u invalid, use %d!!!\n",
+ rate, MTK_AFE_IPM2P0_RATE_48K);
+ return MTK_AFE_IPM2P0_RATE_48K;
+ }
+}
+
+static inline unsigned int calculate_cm_update(unsigned int rate,
+ unsigned int ch)
+{
+ return (((26000000 / rate) - 10) / (ch / 2)) - 1;
+}
+
+static int mt8189_set_cm(struct mtk_base_afe *afe, int id,
+ bool update, bool swap, unsigned int ch)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ unsigned int rate = afe_priv->cm_rate[id];
+ unsigned int rate_val = mt8189_rate_transform(afe->dev, rate);
+ unsigned int update_val = update ? calculate_cm_update(rate, ch) : 0x64;
+ int reg = AFE_CM0_CON0 + 0x10 * id;
+
+ dev_dbg(afe->dev, "%s()-0, CM%d, rate %d, update %d, swap %d, ch %d\n",
+ __func__, id, rate, update, swap, ch);
+
+ /* update cnt */
+ regmap_update_bits(afe->regmap, reg,
+ AFE_CM_UPDATE_CNT_MASK << AFE_CM_UPDATE_CNT_SFT,
+ update_val << AFE_CM_UPDATE_CNT_SFT);
+
+ /* rate */
+ regmap_update_bits(afe->regmap, reg,
+ AFE_CM_1X_EN_SEL_FS_MASK << AFE_CM_1X_EN_SEL_FS_SFT,
+ rate_val << AFE_CM_1X_EN_SEL_FS_SFT);
+
+ /* ch num */
+ regmap_update_bits(afe->regmap, reg,
+ AFE_CM_CH_NUM_MASK << AFE_CM_CH_NUM_SFT,
+ (ch - 1) << AFE_CM_CH_NUM_SFT);
+
+ /* swap */
+ regmap_update_bits(afe->regmap, reg,
+ AFE_CM_BYTE_SWAP_MASK << AFE_CM_BYTE_SWAP_SFT,
+ swap << AFE_CM_BYTE_SWAP_SFT);
+
+ return 0;
+}
+
+static int mt8189_enable_cm_bypass(struct mtk_base_afe *afe, int id, bool en)
+{
+ return regmap_update_bits(afe->regmap, AFE_CM0_CON0 + 0x10 * id,
+ AFE_CM_BYPASS_MODE_MASK <<
+ AFE_CM_BYPASS_MODE_SFT,
+ en << AFE_CM_BYPASS_MODE_SFT);
+}
+
+static int mt8189_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int memif_num = cpu_dai->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+ int ret;
+
+ dev_dbg(afe->dev, "%s(), memif_num: %d.\n", __func__, memif_num);
+
+ memif->substream = substream;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+
+ snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_warn(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+ /* dynamic allocate irq to memif */
+ if (memif->irq_usage < 0) {
+ int irq_id = mtk_dynamic_irq_acquire(afe);
+
+ if (irq_id != afe->irqs_size) {
+ /* link */
+ memif->irq_usage = irq_id;
+ } else {
+ dev_err(afe->dev, "%s() error: no more asys irq\n",
+ __func__);
+ ret = -EBUSY;
+ }
+ }
+
+ return ret;
+}
+
+static void mt8189_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int memif_num = cpu_dai->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ int irq_id = memif->irq_usage;
+
+ dev_dbg(afe->dev, "%s(), memif_num: %d.\n", __func__, memif_num);
+
+ memif->substream = NULL;
+
+ if (!memif->const_irq) {
+ mtk_dynamic_irq_release(afe, irq_id);
+ memif->irq_usage = -1;
+ memif->substream = NULL;
+ }
+}
+
+static int mt8189_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int id = dai->id;
+ int cm;
+
+ switch (id) {
+ case MT8189_MEMIF_VUL8:
+ case MT8189_MEMIF_VUL_CM0:
+ cm = CM0;
+ break;
+ case MT8189_MEMIF_VUL9:
+ case MT8189_MEMIF_VUL_CM1:
+ cm = CM1;
+ break;
+ default:
+ cm = CM0;
+ break;
+ }
+
+ afe_priv->cm_rate[cm] = params_rate(params);
+ afe_priv->cm_channels = params_channels(params);
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt8189_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int id = cpu_dai->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int irq_id = memif->irq_usage;
+ struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int counter = runtime->period_size;
+ unsigned int rate = runtime->rate;
+ unsigned int tmp_reg;
+ int fs;
+ int ret;
+
+ dev_dbg(afe->dev, "%s(), %s cmd %d, irq_id %d, dai_id %d\n", __func__,
+ memif->data->name, cmd, irq_id, id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = mtk_memif_set_enable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "id %d, memif enable fail.\n", id);
+ return ret;
+ }
+
+ /*
+ * for small latency record
+ * ul memif need read some data before irq enable
+ * the context of this triger ops is atmoic, so it cannot sleep
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ if ((runtime->period_size * 1000) / rate <= 10)
+ udelay(300);
+
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit <<
+ irq_data->irq_cnt_shift,
+ counter << irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ if (irq_data->irq_fs_reg >= 0)
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit <<
+ irq_data->irq_fs_shift,
+ fs << irq_data->irq_fs_shift);
+
+ /* enable interrupt */
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 1 << irq_data->irq_en_shift);
+
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = mtk_memif_set_disable(afe, id);
+ if (ret)
+ dev_warn(afe->dev, "id %d, memif disable fail\n", id);
+
+ /* disable interrupt */
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 0 << irq_data->irq_en_shift);
+
+ /*
+ * clear pending IRQ, if the register read as one, there is no
+ * need to write one to clear operation.
+ */
+ regmap_read(afe->regmap, irq_data->irq_clr_reg, &tmp_reg);
+ regmap_update_bits(afe->regmap, irq_data->irq_clr_reg,
+ AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
+ tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt8189_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = NULL;
+
+ if (!component)
+ return -EINVAL;
+
+ afe = snd_soc_component_get_drvdata(component);
+ if (!afe)
+ return -EINVAL;
+
+ return mt8189_rate_transform(afe->dev, rate);
+}
+
+static int mt8189_get_dai_fs(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ return mt8189_rate_transform(afe->dev, rate);
+}
+
+static int mt8189_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = NULL;
+
+ if (!component)
+ return -EINVAL;
+ afe = snd_soc_component_get_drvdata(component);
+
+ return mt8189_rate_transform(afe->dev, rate);
+}
+
+static int mt8189_get_memif_pbuf_size(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if ((runtime->period_size * 1000) / runtime->rate > 10)
+ return MT8189_MEMIF_PBUF_SIZE_256_BYTES;
+
+ return MT8189_MEMIF_PBUF_SIZE_32_BYTES;
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt8189_memif_dai_ops = {
+ .startup = mt8189_fe_startup,
+ .shutdown = mt8189_fe_shutdown,
+ .hw_params = mt8189_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mt8189_fe_trigger,
+};
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define MT8189_FE_DAI_PLAYBACK(_name, _id, max_ch) \
+{ \
+ .name = #_name, \
+ .id = _id, \
+ .playback = { \
+ .stream_name = #_name, \
+ .channels_min = 1, \
+ .channels_max = max_ch, \
+ .rates = MTK_PCM_RATES, \
+ .formats = MTK_PCM_FORMATS, \
+ }, \
+ .ops = &mt8189_memif_dai_ops, \
+}
+
+#define MT8189_FE_DAI_CAPTURE(_name, _id, max_ch) \
+{ \
+ .name = #_name, \
+ .id = _id, \
+ .capture = { \
+ .stream_name = #_name, \
+ .channels_min = 1, \
+ .channels_max = max_ch, \
+ .rates = MTK_PCM_RATES, \
+ .formats = MTK_PCM_FORMATS, \
+ }, \
+ .ops = &mt8189_memif_dai_ops, \
+}
+
+static struct snd_soc_dai_driver mt8189_memif_dai_driver[] = {
+ /* FE DAIs: memory interfaces to CPU */
+ /* Playback */
+ MT8189_FE_DAI_PLAYBACK(DL0, MT8189_MEMIF_DL0, 2),
+ MT8189_FE_DAI_PLAYBACK(DL1, MT8189_MEMIF_DL1, 2),
+ MT8189_FE_DAI_PLAYBACK(DL2, MT8189_MEMIF_DL2, 2),
+ MT8189_FE_DAI_PLAYBACK(DL3, MT8189_MEMIF_DL3, 2),
+ MT8189_FE_DAI_PLAYBACK(DL4, MT8189_MEMIF_DL4, 2),
+ MT8189_FE_DAI_PLAYBACK(DL5, MT8189_MEMIF_DL5, 2),
+ MT8189_FE_DAI_PLAYBACK(DL6, MT8189_MEMIF_DL6, 2),
+ MT8189_FE_DAI_PLAYBACK(DL7, MT8189_MEMIF_DL7, 2),
+ MT8189_FE_DAI_PLAYBACK(DL8, MT8189_MEMIF_DL8, 2),
+ MT8189_FE_DAI_PLAYBACK(DL23, MT8189_MEMIF_DL23, 2),
+ MT8189_FE_DAI_PLAYBACK(DL24, MT8189_MEMIF_DL24, 2),
+ MT8189_FE_DAI_PLAYBACK(DL25, MT8189_MEMIF_DL25, 2),
+ MT8189_FE_DAI_PLAYBACK(DL_24CH, MT8189_MEMIF_DL_24CH, 8),
+ MT8189_FE_DAI_PLAYBACK(HDMI, MT8189_MEMIF_HDMI, 8),
+ /* Capture */
+ MT8189_FE_DAI_CAPTURE(UL0, MT8189_MEMIF_VUL0, 2),
+ MT8189_FE_DAI_CAPTURE(UL1, MT8189_MEMIF_VUL1, 2),
+ MT8189_FE_DAI_CAPTURE(UL2, MT8189_MEMIF_VUL2, 2),
+ MT8189_FE_DAI_CAPTURE(UL3, MT8189_MEMIF_VUL3, 2),
+ MT8189_FE_DAI_CAPTURE(UL4, MT8189_MEMIF_VUL4, 2),
+ MT8189_FE_DAI_CAPTURE(UL5, MT8189_MEMIF_VUL5, 2),
+ MT8189_FE_DAI_CAPTURE(UL6, MT8189_MEMIF_VUL6, 2),
+ MT8189_FE_DAI_CAPTURE(UL7, MT8189_MEMIF_VUL7, 2),
+ MT8189_FE_DAI_CAPTURE(UL8, MT8189_MEMIF_VUL8, 2),
+ MT8189_FE_DAI_CAPTURE(UL9, MT8189_MEMIF_VUL9, 16),
+ MT8189_FE_DAI_CAPTURE(UL10, MT8189_MEMIF_VUL10, 2),
+ MT8189_FE_DAI_CAPTURE(UL24, MT8189_MEMIF_VUL24, 2),
+ MT8189_FE_DAI_CAPTURE(UL25, MT8189_MEMIF_VUL25, 2),
+ MT8189_FE_DAI_CAPTURE(UL_CM0, MT8189_MEMIF_VUL_CM0, 8),
+ MT8189_FE_DAI_CAPTURE(UL_CM1, MT8189_MEMIF_VUL_CM1, 16),
+ MT8189_FE_DAI_CAPTURE(UL_ETDM_IN0, MT8189_MEMIF_ETDM_IN0, 2),
+ MT8189_FE_DAI_CAPTURE(UL_ETDM_IN1, MT8189_MEMIF_ETDM_IN1, 2),
+};
+
+static int ul_cm0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ unsigned int channels = afe_priv->cm_channels;
+
+ dev_dbg(afe->dev, "%s(), event 0x%x, name %s, channels %d\n",
+ __func__, event, w->name, channels);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8189_enable_cm_bypass(afe, CM0, false);
+ mt8189_set_cm(afe, CM0, true, false, channels);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM0_MASK_SFT, 0 << PDN_CM0_SFT);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mt8189_enable_cm_bypass(afe, CM0, true);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM0_MASK_SFT, 1 << PDN_CM0_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ul_cm1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ unsigned int channels = afe_priv->cm_channels;
+
+ dev_dbg(afe->dev, "%s(), event 0x%x, name %s, channels %d\n",
+ __func__, event, w->name, channels);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8189_enable_cm_bypass(afe, CM1, false);
+ mt8189_set_cm(afe, CM1, true, false, channels);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM1_MASK_SFT, 0 << PDN_CM1_SFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8189_enable_cm_bypass(afe, CM1, true);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM1_MASK_SFT, 1 << PDN_CM1_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * dma widget & routes
+ * The mixer controls and routes are by no means fully implemented,
+ * only the ones that are intended to be used are, as other wise a fully
+ * interconnected switch bar mixer would introduce way too many unused
+ * controls.
+ */
+static const struct snd_kcontrol_new memif_ul0_ch1_mix[] = {
+ /* Normal record */
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN018_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN018_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN018_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN018_0,
+ I_ADDA_UL_CH4, 1, 0),
+ /* AP DMIC */
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH1", AFE_CONN018_0,
+ I_DMIC0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH2", AFE_CONN018_0,
+ I_DMIC0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN018_1,
+ I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN018_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN018_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN018_1,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN018_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN018_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN018_1,
+ I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL23_CH1", AFE_CONN018_2,
+ I_DL23_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN018_1,
+ I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN018_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN018_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH1", AFE_CONN018_4,
+ I_I2SIN1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH1", AFE_CONN018_6,
+ I_SRC_0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH1", AFE_CONN018_6,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul0_ch2_mix[] = {
+ /* Normal record */
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN019_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN019_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN019_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN019_0,
+ I_ADDA_UL_CH4, 1, 0),
+ /* AP DMIC */
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH2", AFE_CONN019_0,
+ I_DMIC0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN019_1,
+ I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN019_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN019_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN019_1,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN019_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN019_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN019_1,
+ I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL23_CH2", AFE_CONN018_2,
+ I_DL23_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN019_1,
+ I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN019_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN019_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN019_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH2", AFE_CONN019_4,
+ I_I2SIN1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH2", AFE_CONN019_6,
+ I_SRC_0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH2", AFE_CONN019_6,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN020_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN020_1,
+ I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN020_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN020_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN020_1,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN020_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN020_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN020_1,
+ I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL23_CH1", AFE_CONN020_2,
+ I_DL23_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN020_1,
+ I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN020_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN020_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH1", AFE_CONN020_4,
+ I_I2SIN1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH1", AFE_CONN020_6,
+ I_SRC_0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH1", AFE_CONN020_6,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN021_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN021_1,
+ I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN021_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN021_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN021_1,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN021_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN021_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN021_1,
+ I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL23_CH2", AFE_CONN021_2,
+ I_DL23_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN021_1,
+ I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN021_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN021_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN021_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH2", AFE_CONN021_4,
+ I_I2SIN1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH2", AFE_CONN021_6,
+ I_SRC_0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH2", AFE_CONN021_6,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN022_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN022_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN022_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN022_0,
+ I_ADDA_UL_CH4, 1, 0),
+ /* AP DMIC */
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH3", AFE_CONN022_0,
+ I_DMIC1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN1_OUT_CH1", AFE_CONN022_0,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH1", AFE_CONN022_6,
+ I_SRC_1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN023_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN023_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN023_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN023_0,
+ I_ADDA_UL_CH4, 1, 0),
+ /* AP DMIC */
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH4", AFE_CONN023_0,
+ I_DMIC1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN1_OUT_CH2", AFE_CONN023_0,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH2", AFE_CONN023_6,
+ I_SRC_1_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN024_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH1", AFE_CONN024_4,
+ I_I2SIN1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH1", AFE_CONN024_6,
+ I_SRC_3_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN025_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH2", AFE_CONN025_4,
+ I_I2SIN1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH2", AFE_CONN025_6,
+ I_SRC_3_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN026_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN026_1,
+ I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN026_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN026_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN026_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN026_1,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN026_1,
+ I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN026_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH1", AFE_CONN026_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH1", AFE_CONN026_6,
+ I_SRC_3_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN027_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN027_1,
+ I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN027_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN027_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN027_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN027_1,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN027_1,
+ I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN027_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN027_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH2", AFE_CONN027_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH2", AFE_CONN027_6,
+ I_SRC_3_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN028_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN028_1,
+ I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN028_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN028_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN028_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN028_1,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN028_1,
+ I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN0_OUT_CH1", AFE_CONN028_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH1", AFE_CONN028_6,
+ I_SRC_3_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN029_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN029_1,
+ I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN029_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN029_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN029_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN029_1,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN029_1,
+ I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN029_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN029_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN0_OUT_CH2", AFE_CONN029_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH2", AFE_CONN029_6,
+ I_SRC_3_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN030_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH1", AFE_CONN030_0,
+ I_DMIC0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN030_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN030_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN030_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_4_OUT_CH1", AFE_CONN030_6,
+ I_SRC_4_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN031_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH2", AFE_CONN031_0,
+ I_DMIC0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN031_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN031_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN031_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_4_OUT_CH2", AFE_CONN031_6,
+ I_SRC_4_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN032_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN032_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH3", AFE_CONN032_0,
+ I_DMIC1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN032_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN032_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN032_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_4_OUT_CH1", AFE_CONN032_6,
+ I_SRC_4_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN033_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN033_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH4", AFE_CONN033_0,
+ I_DMIC1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN033_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN033_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN033_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_4_OUT_CH2", AFE_CONN033_6,
+ I_SRC_4_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN034_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN035_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN035_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN035_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul9_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN036_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN036_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN036_0,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul9_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN037_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN037_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN037_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN037_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul24_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN066_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul24_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN067_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN040_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN040_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN040_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN040_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH1", AFE_CONN040_0,
+ I_DMIC0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN1_OUT_CH1", AFE_CONN040_0,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH1", AFE_CONN040_6,
+ I_SRC_0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH1", AFE_CONN040_6,
+ I_SRC_1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN041_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN041_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN041_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN041_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH2", AFE_CONN041_0,
+ I_DMIC0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN1_OUT_CH2", AFE_CONN041_0,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH2", AFE_CONN041_6,
+ I_SRC_0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH2", AFE_CONN041_6,
+ I_SRC_1_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN042_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN042_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN042_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN042_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH3", AFE_CONN042_0,
+ I_DMIC1_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN043_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN043_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN043_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN043_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("AP_DMIC_UL_CH4", AFE_CONN043_0,
+ I_DMIC1_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN044_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN044_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN044_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN044_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN045_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN045_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN045_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN045_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN046_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN046_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN046_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN046_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN047_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN047_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN047_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN047_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN048_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN048_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN048_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN048_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN048_0,
+ I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN048_0,
+ I_ADDA_UL_CH6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH1", AFE_CONN048_6,
+ I_SRC_0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH1", AFE_CONN048_6,
+ I_SRC_3_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_4_OUT_CH1", AFE_CONN048_6,
+ I_SRC_4_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN049_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN049_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN049_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN049_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN049_0,
+ I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN049_0,
+ I_ADDA_UL_CH6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH2", AFE_CONN049_6,
+ I_SRC_0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_3_OUT_CH2", AFE_CONN049_6,
+ I_SRC_3_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_4_OUT_CH2", AFE_CONN049_6,
+ I_SRC_4_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN050_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN050_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN050_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN050_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN050_0,
+ I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN050_0,
+ I_ADDA_UL_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN051_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN051_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN051_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN051_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN051_0,
+ I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN051_0,
+ I_ADDA_UL_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN052_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN052_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN052_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN052_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN052_0,
+ I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN052_0,
+ I_ADDA_UL_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN053_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN053_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN053_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN053_0,
+ I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN053_0,
+ I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN053_0,
+ I_ADDA_UL_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN054_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN054_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN054_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN054_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN055_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN055_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN055_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN055_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch9_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN056_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN056_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN056_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN056_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN057_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN057_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN057_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN057_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch11_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN058_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN058_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN058_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN058_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch12_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN059_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN059_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN059_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN059_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch13_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN060_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN060_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN060_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN060_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch14_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN061_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN061_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN061_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN061_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch15_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN062_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN062_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN062_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN062_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch16_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN063_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN063_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN063_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN063_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const char * const cm0_mux_texts[] = {
+ "CM0_2CH_PATH",
+ "CM0_8CH_PATH",
+};
+
+static const char * const cm1_mux_map_texts[] = {
+ "CM1_2CH_PATH",
+ "CM1_16CH_PATH",
+};
+
+static SOC_ENUM_SINGLE_DECL(ul_cm0_mux_map_enum,
+ AFE_CM0_CON0,
+ AFE_CM0_OUTPUT_MUX_SFT,
+ cm0_mux_texts);
+static SOC_ENUM_SINGLE_DECL(ul_cm1_mux_map_enum,
+ AFE_CM1_CON0,
+ AFE_CM1_OUTPUT_MUX_SFT,
+ cm1_mux_map_texts);
+
+static const struct snd_kcontrol_new ul_cm0_mux_control =
+ SOC_DAPM_ENUM("CM0_UL_MUX Route", ul_cm0_mux_map_enum);
+static const struct snd_kcontrol_new ul_cm1_mux_control =
+ SOC_DAPM_ENUM("CM1_UL_MUX Route", ul_cm1_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mt8189_memif_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("UL0_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul0_ch1_mix, ARRAY_SIZE(memif_ul0_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL0_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul0_ch2_mix, ARRAY_SIZE(memif_ul0_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL4_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch1_mix, ARRAY_SIZE(memif_ul4_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL4_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch2_mix, ARRAY_SIZE(memif_ul4_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL5_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch1_mix, ARRAY_SIZE(memif_ul5_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL5_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch2_mix, ARRAY_SIZE(memif_ul5_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL6_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch1_mix, ARRAY_SIZE(memif_ul6_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL6_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch2_mix, ARRAY_SIZE(memif_ul6_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL7_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch1_mix, ARRAY_SIZE(memif_ul7_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL7_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch2_mix, ARRAY_SIZE(memif_ul7_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL8_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch1_mix, ARRAY_SIZE(memif_ul8_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL8_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch2_mix, ARRAY_SIZE(memif_ul8_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL9_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul9_ch1_mix, ARRAY_SIZE(memif_ul9_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL9_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul9_ch2_mix, ARRAY_SIZE(memif_ul9_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL24_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul24_ch1_mix, ARRAY_SIZE(memif_ul24_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL24_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul24_ch2_mix, ARRAY_SIZE(memif_ul24_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_CM0_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch1_mix, ARRAY_SIZE(memif_ul_cm0_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch2_mix, ARRAY_SIZE(memif_ul_cm0_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch3_mix, ARRAY_SIZE(memif_ul_cm0_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch4_mix, ARRAY_SIZE(memif_ul_cm0_ch4_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH5", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch5_mix, ARRAY_SIZE(memif_ul_cm0_ch5_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH6", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch6_mix, ARRAY_SIZE(memif_ul_cm0_ch6_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH7", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch7_mix, ARRAY_SIZE(memif_ul_cm0_ch7_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH8", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch8_mix, ARRAY_SIZE(memif_ul_cm0_ch8_mix)),
+ SND_SOC_DAPM_MUX_E("CM0_UL_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_cm0_mux_control,
+ ul_cm0_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("UL_CM1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch1_mix, ARRAY_SIZE(memif_ul_cm1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch2_mix, ARRAY_SIZE(memif_ul_cm1_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch3_mix, ARRAY_SIZE(memif_ul_cm1_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch4_mix, ARRAY_SIZE(memif_ul_cm1_ch4_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH5", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch5_mix, ARRAY_SIZE(memif_ul_cm1_ch5_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH6", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch6_mix, ARRAY_SIZE(memif_ul_cm1_ch6_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH7", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch7_mix, ARRAY_SIZE(memif_ul_cm1_ch7_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH8", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch8_mix, ARRAY_SIZE(memif_ul_cm1_ch8_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH9", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch9_mix, ARRAY_SIZE(memif_ul_cm1_ch9_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH10", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch10_mix, ARRAY_SIZE(memif_ul_cm1_ch10_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH11", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch11_mix, ARRAY_SIZE(memif_ul_cm1_ch11_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH12", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch12_mix, ARRAY_SIZE(memif_ul_cm1_ch12_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH13", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch13_mix, ARRAY_SIZE(memif_ul_cm1_ch13_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH14", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch14_mix, ARRAY_SIZE(memif_ul_cm1_ch14_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH15", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch15_mix, ARRAY_SIZE(memif_ul_cm1_ch15_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH16", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch16_mix, ARRAY_SIZE(memif_ul_cm1_ch16_mix)),
+ SND_SOC_DAPM_MUX("CM1_UL_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_cm1_mux_control),
+ SND_SOC_DAPM_SUPPLY("CM0_Enable",
+ AFE_CM0_CON0, AFE_CM0_ON_SFT, 0,
+ ul_cm0_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("CM1_Enable",
+ AFE_CM1_CON0, AFE_CM0_ON_SFT, 0,
+ ul_cm1_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* dynamic pinctrl */
+ SND_SOC_DAPM_PINCTRL("I2S0_PIN", "aud-gpio-i2s0-on", "aud-gpio-i2s0-off"),
+ SND_SOC_DAPM_PINCTRL("I2S1_PIN", "aud-gpio-i2s1-on", "aud-gpio-i2s1-off"),
+ SND_SOC_DAPM_PINCTRL("PCM0_PIN", "aud-gpio-pcm-on", "aud-gpio-pcm-off"),
+ SND_SOC_DAPM_PINCTRL("AP_DMIC0_PIN", "aud-gpio-ap-dmic-on", "aud-gpio-ap-dmic-off"),
+ SND_SOC_DAPM_PINCTRL("AP_DMIC1_PIN", "aud-gpio-ap-dmic1-on", "aud-gpio-ap-dmic1-off"),
+};
+
+static const struct snd_soc_dapm_route mt8189_memif_routes[] = {
+ {"UL0", NULL, "UL0_CH1"},
+ {"UL0", NULL, "UL0_CH2"},
+ /* Normal record */
+ {"UL0_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL0_CH1", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL0_CH1", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL0_CH1", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+ {"UL0_CH2", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL0_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL0_CH2", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL0_CH2", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+
+ {"UL0_CH1", "AP_DMIC_UL_CH1", "AP DMIC Capture"},
+ {"UL0_CH1", "AP_DMIC_UL_CH2", "AP DMIC Capture"},
+ {"UL0_CH2", "AP_DMIC_UL_CH2", "AP DMIC Capture"},
+
+ {"UL0_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL0_CH2", "I2SIN0_CH2", "I2SIN0"},
+ {"UL0_CH1", "I2SIN1_CH1", "I2SIN1"},
+ {"UL0_CH2", "I2SIN1_CH2", "I2SIN1"},
+
+ {"UL0_CH1", "PCM_0_CAP_CH1", "PCM 0 Capture"},
+ {"UL0_CH2", "PCM_0_CAP_CH1", "PCM 0 Capture"},
+
+ {"UL1", NULL, "UL1_CH1"},
+ {"UL1", NULL, "UL1_CH2"},
+
+ {"UL1_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL1_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL1_CH2", "I2SIN0_CH2", "I2SIN0"},
+ {"UL1_CH1", "I2SIN1_CH1", "I2SIN1"},
+ {"UL1_CH2", "I2SIN1_CH2", "I2SIN1"},
+
+ {"UL1_CH1", "PCM_0_CAP_CH1", "PCM 0 Capture"},
+ {"UL1_CH2", "PCM_0_CAP_CH1", "PCM 0 Capture"},
+
+ {"UL2", NULL, "UL2_CH1"},
+ {"UL2", NULL, "UL2_CH2"},
+
+ {"UL2_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL2_CH1", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL2_CH1", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL2_CH1", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+ {"UL2_CH2", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL2_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL2_CH2", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL2_CH2", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+
+ {"UL2_CH1", "AP_DMIC_UL_CH3", "AP DMIC CH34 Capture"},
+ {"UL2_CH2", "AP_DMIC_UL_CH4", "AP DMIC CH34 Capture"},
+
+ {"UL3", NULL, "UL3_CH1"},
+ {"UL3", NULL, "UL3_CH2"},
+
+ {"UL3_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL3_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL3_CH1", "I2SIN1_CH1", "I2SIN1"},
+ {"UL3_CH2", "I2SIN1_CH2", "I2SIN1"},
+
+ {"UL4", NULL, "UL4_CH1"},
+ {"UL4", NULL, "UL4_CH2"},
+ {"UL4_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL4_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL4_CH1", "PCM_0_CAP_CH1", "PCM 0 Capture"},
+ {"UL4_CH2", "PCM_0_CAP_CH1", "PCM 0 Capture"},
+
+ {"UL5", NULL, "UL5_CH1"},
+ {"UL5", NULL, "UL5_CH2"},
+
+ {"UL5_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL5_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL6", NULL, "UL6_CH1"},
+ {"UL6", NULL, "UL6_CH2"},
+ {"UL6_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL6_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL6_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL6_CH2", "I2SIN0_CH2", "I2SIN0"},
+ {"UL6_CH1", "AP_DMIC_UL_CH1", "AP DMIC Capture"},
+ {"UL6_CH2", "AP_DMIC_UL_CH2", "AP DMIC Capture"},
+
+ {"UL7", NULL, "UL7_CH1"},
+ {"UL7", NULL, "UL7_CH2"},
+ {"UL7_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL7_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL7_CH2", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL7_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL7_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL7_CH2", "I2SIN0_CH2", "I2SIN0"},
+ {"UL7_CH1", "AP_DMIC_UL_CH3", "AP DMIC CH34 Capture"},
+ {"UL7_CH2", "AP_DMIC_UL_CH4", "AP DMIC CH34 Capture"},
+
+ {"UL8", NULL, "CM0_UL_MUX"},
+ {"CM0_UL_MUX", "CM0_2CH_PATH", "UL8_CH1"},
+ {"CM0_UL_MUX", "CM0_2CH_PATH", "UL8_CH2"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH1"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH2"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH3"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH4"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH5"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH6"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH7"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH8"},
+
+ {"UL_CM0", NULL, "CM0_Enable"},
+
+ {"UL9", NULL, "CM1_UL_MUX"},
+ {"CM1_UL_MUX", "CM1_2CH_PATH", "UL9_CH1"},
+ {"CM1_UL_MUX", "CM1_2CH_PATH", "UL9_CH2"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH1"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH2"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH3"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH4"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH5"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH6"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH7"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH8"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH9"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH10"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH11"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH12"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH13"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH14"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH15"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH16"},
+
+ {"UL_CM1", NULL, "CM1_Enable"},
+
+ /* UL9 o36o37 <- ADDA */
+ {"UL9_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL9_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL9_CH2", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL9_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+
+ {"UL24", NULL, "UL24_CH1"},
+ {"UL24", NULL, "UL24_CH2"},
+ {"UL24_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+
+ {"UL_CM0", NULL, "UL_CM0_CH1"},
+ {"UL_CM0", NULL, "UL_CM0_CH2"},
+ {"UL_CM0", NULL, "UL_CM0_CH3"},
+ {"UL_CM0", NULL, "UL_CM0_CH4"},
+ {"UL_CM0", NULL, "UL_CM0_CH5"},
+ {"UL_CM0", NULL, "UL_CM0_CH6"},
+ {"UL_CM0", NULL, "UL_CM0_CH7"},
+ {"UL_CM0", NULL, "UL_CM0_CH8"},
+ {"UL_CM0_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM0_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM0_CH2", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM0_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM0_CH3", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM0_CH3", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM0_CH4", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM0_CH4", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM0_CH1", "AP_DMIC_UL_CH1", "AP DMIC Capture"},
+ {"UL_CM0_CH2", "AP_DMIC_UL_CH2", "AP DMIC Capture"},
+ {"UL_CM0_CH3", "AP_DMIC_UL_CH3", "AP DMIC CH34 Capture"},
+ {"UL_CM0_CH4", "AP_DMIC_UL_CH4", "AP DMIC CH34 Capture"},
+
+ {"UL_CM1", NULL, "UL_CM1_CH1"},
+ {"UL_CM1", NULL, "UL_CM1_CH2"},
+ {"UL_CM1", NULL, "UL_CM1_CH3"},
+ {"UL_CM1", NULL, "UL_CM1_CH4"},
+ {"UL_CM1", NULL, "UL_CM1_CH5"},
+ {"UL_CM1", NULL, "UL_CM1_CH6"},
+ {"UL_CM1", NULL, "UL_CM1_CH7"},
+ {"UL_CM1", NULL, "UL_CM1_CH8"},
+ {"UL_CM1", NULL, "UL_CM1_CH9"},
+ {"UL_CM1", NULL, "UL_CM1_CH10"},
+ {"UL_CM1", NULL, "UL_CM1_CH11"},
+ {"UL_CM1", NULL, "UL_CM1_CH12"},
+ {"UL_CM1", NULL, "UL_CM1_CH13"},
+ {"UL_CM1", NULL, "UL_CM1_CH14"},
+ {"UL_CM1", NULL, "UL_CM1_CH15"},
+ {"UL_CM1", NULL, "UL_CM1_CH16"},
+ {"UL_CM1_CH1", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM1_CH1", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM1_CH2", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM1_CH2", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM1_CH3", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM1_CH3", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM1_CH4", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM1_CH4", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM1_CH5", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM1_CH5", "ADDA_UL_CH2", "ADDA Capture"},
+ {"UL_CM1_CH6", "ADDA_UL_CH1", "ADDA Capture"},
+ {"UL_CM1_CH6", "ADDA_UL_CH2", "ADDA Capture"},
+
+ /* Audio Pin */
+ {"I2SOUT0", NULL, "I2S0_PIN"},
+ {"I2SIN0", NULL, "I2S0_PIN"},
+ {"I2SOUT1", NULL, "I2S1_PIN"},
+ {"I2SIN1", NULL, "I2S1_PIN"},
+ {"PCM 0 Playback", NULL, "PCM0_PIN"},
+ {"AP DMIC Capture", NULL, "AP_DMIC0_PIN"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC1_PIN"},
+};
+
+#define MT8189_DL_MEMIF(_id) \
+ [MT8189_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8189_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = AFE_##_id##_CON0, \
+ .fs_shift = _id##_SEL_FS_SFT, \
+ .fs_maskbit = _id##_SEL_FS_MASK, \
+ .mono_reg = AFE_##_id##_CON0, \
+ .mono_shift = _id##_MONO_SFT, \
+ .enable_reg = AFE_##_id##_CON0, \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_##_id##_CON0, \
+ .hd_shift = _id##_HD_MODE_SFT, \
+ .hd_align_reg = AFE_##_id##_CON0, \
+ .hd_align_mshift = _id##_HALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ .pbuf_reg = AFE_##_id##_CON0, \
+ .pbuf_mask = _id##_PBUF_SIZE_MASK, \
+ .pbuf_shift = _id##_PBUF_SIZE_SFT, \
+ .minlen_reg = AFE_##_id##_CON0, \
+ .minlen_mask = _id##_MINLEN_MASK, \
+ .minlen_shift = _id##_MINLEN_SFT, \
+}
+
+#define MT8189_MULTI_DL_MEMIF(_id) \
+ [MT8189_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8189_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = AFE_##_id##_CON0, \
+ .fs_shift = _id##_SEL_FS_SFT, \
+ .fs_maskbit = _id##_SEL_FS_MASK, \
+ .mono_reg = -1, \
+ .mono_shift = -1, \
+ .enable_reg = AFE_##_id##_CON0, \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_##_id##_CON0, \
+ .hd_shift = _id##_HD_MODE_SFT, \
+ .hd_align_reg = AFE_##_id##_CON0, \
+ .hd_align_mshift = _id##_HALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ .pbuf_reg = AFE_##_id##_CON0, \
+ .pbuf_mask = _id##_PBUF_SIZE_MASK, \
+ .pbuf_shift = _id##_PBUF_SIZE_SFT, \
+ .minlen_reg = AFE_##_id##_CON0, \
+ .minlen_mask = _id##_MINLEN_MASK, \
+ .minlen_shift = _id##_MINLEN_SFT, \
+ .ch_num_reg = AFE_##_id##_CON0, \
+ .ch_num_maskbit = _id##_NUM_MASK, \
+ .ch_num_shift = _id##_NUM_SFT, \
+}
+
+#define MT8189_UL_MEMIF(_id, _fs_shift, _fs_maskbit, _mono_shift) \
+ [MT8189_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8189_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = AFE_##_id##_CON0, \
+ .fs_shift = _fs_shift, \
+ .fs_maskbit = _fs_maskbit, \
+ .mono_reg = AFE_##_id##_CON0, \
+ .mono_shift = _mono_shift, \
+ .enable_reg = AFE_##_id##_CON0, \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_##_id##_CON0, \
+ .hd_shift = _id##_HD_MODE_SFT, \
+ .hd_align_reg = AFE_##_id##_CON0, \
+ .hd_align_mshift = _id##_HALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ }
+
+/* For convenience with macros: missing register fields */
+#define HDMI_SEL_FS_SFT -1
+#define HDMI_SEL_FS_MASK -1
+
+/* For convenience with macros: register name differences */
+#define AFE_HDMI_BASE AFE_HDMI_OUT_BASE
+#define AFE_HDMI_CUR AFE_HDMI_OUT_CUR
+#define AFE_HDMI_END AFE_HDMI_OUT_END
+#define AFE_HDMI_BASE_MSB AFE_HDMI_OUT_BASE_MSB
+#define AFE_HDMI_CUR_MSB AFE_HDMI_OUT_CUR_MSB
+#define AFE_HDMI_END_MSB AFE_HDMI_OUT_END_MSB
+#define AFE_HDMI_CON0 AFE_HDMI_OUT_CON0
+#define HDMI_ON_SFT HDMI_OUT_ON_SFT
+#define HDMI_HD_MODE_SFT HDMI_OUT_HD_MODE_SFT
+#define HDMI_HALIGN_SFT HDMI_OUT_HALIGN_SFT
+#define HDMI_PBUF_SIZE_MASK HDMI_OUT_PBUF_SIZE_MASK
+#define HDMI_PBUF_SIZE_SFT HDMI_OUT_PBUF_SIZE_SFT
+#define HDMI_MINLEN_MASK HDMI_OUT_MINLEN_MASK
+#define HDMI_MINLEN_SFT HDMI_OUT_MINLEN_SFT
+#define HDMI_NUM_MASK HDMI_CH_NUM_MASK
+#define HDMI_NUM_SFT HDMI_CH_NUM_SFT
+
+static const struct mtk_base_memif_data memif_data[MT8189_MEMIF_NUM] = {
+ MT8189_DL_MEMIF(DL0),
+ MT8189_DL_MEMIF(DL1),
+ MT8189_DL_MEMIF(DL2),
+ MT8189_DL_MEMIF(DL3),
+ MT8189_DL_MEMIF(DL4),
+ MT8189_DL_MEMIF(DL5),
+ MT8189_DL_MEMIF(DL6),
+ MT8189_DL_MEMIF(DL7),
+ MT8189_DL_MEMIF(DL8),
+ MT8189_DL_MEMIF(DL23),
+ MT8189_DL_MEMIF(DL24),
+ MT8189_DL_MEMIF(DL25),
+ MT8189_MULTI_DL_MEMIF(DL_24CH),
+ MT8189_MULTI_DL_MEMIF(HDMI),
+ MT8189_UL_MEMIF(VUL0, VUL0_SEL_FS_SFT, VUL0_SEL_FS_MASK, VUL0_MONO_SFT),
+ MT8189_UL_MEMIF(VUL1, VUL1_SEL_FS_SFT, VUL1_SEL_FS_MASK, VUL1_MONO_SFT),
+ MT8189_UL_MEMIF(VUL2, VUL2_SEL_FS_SFT, VUL2_SEL_FS_MASK, VUL2_MONO_SFT),
+ MT8189_UL_MEMIF(VUL3, VUL3_SEL_FS_SFT, VUL3_SEL_FS_MASK, VUL3_MONO_SFT),
+ MT8189_UL_MEMIF(VUL4, VUL4_SEL_FS_SFT, VUL4_SEL_FS_MASK, VUL4_MONO_SFT),
+ MT8189_UL_MEMIF(VUL5, VUL5_SEL_FS_SFT, VUL5_SEL_FS_MASK, VUL5_MONO_SFT),
+ MT8189_UL_MEMIF(VUL6, VUL6_SEL_FS_SFT, VUL6_SEL_FS_MASK, VUL6_MONO_SFT),
+ MT8189_UL_MEMIF(VUL7, VUL7_SEL_FS_SFT, VUL7_SEL_FS_MASK, VUL7_MONO_SFT),
+ MT8189_UL_MEMIF(VUL8, VUL8_SEL_FS_SFT, VUL8_SEL_FS_MASK, VUL8_MONO_SFT),
+ MT8189_UL_MEMIF(VUL9, VUL9_SEL_FS_SFT, VUL9_SEL_FS_MASK, VUL9_MONO_SFT),
+ MT8189_UL_MEMIF(VUL10, VUL10_SEL_FS_SFT, VUL10_SEL_FS_MASK, VUL10_MONO_SFT),
+ MT8189_UL_MEMIF(VUL24, VUL24_SEL_FS_SFT, VUL24_SEL_FS_MASK, VUL24_MONO_SFT),
+ MT8189_UL_MEMIF(VUL25, VUL25_SEL_FS_SFT, VUL25_SEL_FS_MASK, VUL25_MONO_SFT),
+ MT8189_UL_MEMIF(VUL_CM0, -1, -1, -1),
+ MT8189_UL_MEMIF(VUL_CM1, -1, -1, -1),
+ MT8189_UL_MEMIF(ETDM_IN0, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+ MT8189_UL_MEMIF(ETDM_IN1, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+};
+
+#define MT8189_AFE_IRQ(_id) \
+ [MT8189_IRQ_##_id] = { \
+ .id = MT8189_IRQ_##_id, \
+ .irq_cnt_reg = AFE_IRQ##_id##_MCU_CFG1, \
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, \
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, \
+ .irq_fs_reg = AFE_IRQ##_id##_MCU_CFG0, \
+ .irq_fs_shift = AFE_IRQ##_id##_MCU_FS_SFT, \
+ .irq_fs_maskbit = AFE_IRQ##_id##_MCU_FS_MASK, \
+ .irq_en_reg = AFE_IRQ##_id##_MCU_CFG0, \
+ .irq_en_shift = AFE_IRQ##_id##_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_IRQ##_id##_MCU_CFG1, \
+ .irq_clr_shift = AFE_IRQ##_id##_CLR_CFG_SFT, \
+ }
+
+#define MT8189_AFE_TDM_IRQ(_id) \
+ [MT8189_IRQ_##_id] = { \
+ .id = MT8189_CUS_IRQ_TDM, \
+ .irq_cnt_reg = AFE_CUSTOM_IRQ0_MCU_CFG1, \
+ .irq_cnt_shift = AFE_CUSTOM_IRQ0_MCU_CNT_SFT, \
+ .irq_cnt_maskbit = AFE_CUSTOM_IRQ0_MCU_CNT_MASK, \
+ .irq_fs_reg = -1, \
+ .irq_fs_shift = -1, \
+ .irq_fs_maskbit = -1, \
+ .irq_en_reg = AFE_CUSTOM_IRQ0_MCU_CFG0, \
+ .irq_en_shift = AFE_CUSTOM_IRQ0_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_CUSTOM_IRQ0_MCU_CFG1, \
+ .irq_clr_shift = AFE_CUSTOM_IRQ0_CLR_CFG_SFT, \
+ }
+
+static const struct mtk_base_irq_data irq_data[MT8189_IRQ_NUM] = {
+ MT8189_AFE_IRQ(0),
+ MT8189_AFE_IRQ(1),
+ MT8189_AFE_IRQ(2),
+ MT8189_AFE_IRQ(3),
+ MT8189_AFE_IRQ(4),
+ MT8189_AFE_IRQ(5),
+ MT8189_AFE_IRQ(6),
+ MT8189_AFE_IRQ(7),
+ MT8189_AFE_IRQ(8),
+ MT8189_AFE_IRQ(9),
+ MT8189_AFE_IRQ(10),
+ MT8189_AFE_IRQ(11),
+ MT8189_AFE_IRQ(12),
+ MT8189_AFE_IRQ(13),
+ MT8189_AFE_IRQ(14),
+ MT8189_AFE_IRQ(15),
+ MT8189_AFE_IRQ(16),
+ MT8189_AFE_IRQ(17),
+ MT8189_AFE_IRQ(18),
+ MT8189_AFE_IRQ(19),
+ MT8189_AFE_IRQ(20),
+ MT8189_AFE_IRQ(21),
+ MT8189_AFE_IRQ(22),
+ MT8189_AFE_IRQ(23),
+ MT8189_AFE_IRQ(24),
+ MT8189_AFE_IRQ(25),
+ MT8189_AFE_IRQ(26),
+ MT8189_AFE_TDM_IRQ(31),
+};
+
+static const int memif_irq_usage[MT8189_MEMIF_NUM] = {
+ /* TODO: verify each memif & irq */
+ [MT8189_MEMIF_DL0] = MT8189_IRQ_0,
+ [MT8189_MEMIF_DL1] = MT8189_IRQ_1,
+ [MT8189_MEMIF_DL2] = MT8189_IRQ_2,
+ [MT8189_MEMIF_DL3] = MT8189_IRQ_3,
+ [MT8189_MEMIF_DL4] = MT8189_IRQ_4,
+ [MT8189_MEMIF_DL5] = MT8189_IRQ_5,
+ [MT8189_MEMIF_DL6] = MT8189_IRQ_6,
+ [MT8189_MEMIF_DL7] = MT8189_IRQ_7,
+ [MT8189_MEMIF_DL8] = MT8189_IRQ_8,
+ [MT8189_MEMIF_DL23] = MT8189_IRQ_9,
+ [MT8189_MEMIF_DL24] = MT8189_IRQ_10,
+ [MT8189_MEMIF_DL25] = MT8189_IRQ_11,
+ [MT8189_MEMIF_DL_24CH] = MT8189_IRQ_12,
+ [MT8189_MEMIF_VUL0] = MT8189_IRQ_13,
+ [MT8189_MEMIF_VUL1] = MT8189_IRQ_14,
+ [MT8189_MEMIF_VUL2] = MT8189_IRQ_15,
+ [MT8189_MEMIF_VUL3] = MT8189_IRQ_16,
+ [MT8189_MEMIF_VUL4] = MT8189_IRQ_17,
+ [MT8189_MEMIF_VUL5] = MT8189_IRQ_18,
+ [MT8189_MEMIF_VUL6] = MT8189_IRQ_19,
+ [MT8189_MEMIF_VUL7] = MT8189_IRQ_20,
+ [MT8189_MEMIF_VUL8] = MT8189_IRQ_21,
+ [MT8189_MEMIF_VUL9] = MT8189_IRQ_22,
+ [MT8189_MEMIF_VUL10] = MT8189_IRQ_23,
+ [MT8189_MEMIF_VUL24] = MT8189_IRQ_24,
+ [MT8189_MEMIF_VUL25] = MT8189_IRQ_25,
+ [MT8189_MEMIF_VUL_CM0] = MT8189_IRQ_26,
+ [MT8189_MEMIF_VUL_CM1] = MT8189_IRQ_0,
+ [MT8189_MEMIF_ETDM_IN0] = MT8189_IRQ_0,
+ [MT8189_MEMIF_ETDM_IN1] = MT8189_IRQ_0,
+ [MT8189_MEMIF_HDMI] = MT8189_IRQ_31
+};
+
+static bool mt8189_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0:
+ case AUDIO_TOP_CON1:
+ case AUDIO_TOP_CON2:
+ case AUDIO_TOP_CON3:
+ case AUDIO_TOP_CON4:
+ case AFE_APLL1_TUNER_MON0:
+ case AFE_APLL2_TUNER_MON0:
+ case AFE_SPM_CONTROL_ACK:
+ case AUDIO_TOP_IP_VERSION:
+ case AUDIO_ENGEN_CON0_MON:
+ case AFE_CONNSYS_I2S_IPM_VER_MON:
+ case AFE_CONNSYS_I2S_MON:
+ case AFE_PCM_INTF_MON:
+ case AFE_PCM_TOP_IP_VERSION:
+ case AFE_IRQ_MCU_STATUS:
+ case AFE_CUSTOM_IRQ_MCU_STATUS:
+ case AFE_IRQ_MCU_MON0:
+ case AFE_IRQ_MCU_MON1:
+ case AFE_IRQ_MCU_MON2:
+ case AFE_IRQ0_CNT_MON:
+ case AFE_IRQ1_CNT_MON:
+ case AFE_IRQ2_CNT_MON:
+ case AFE_IRQ3_CNT_MON:
+ case AFE_IRQ4_CNT_MON:
+ case AFE_IRQ5_CNT_MON:
+ case AFE_IRQ6_CNT_MON:
+ case AFE_IRQ7_CNT_MON:
+ case AFE_IRQ8_CNT_MON:
+ case AFE_IRQ9_CNT_MON:
+ case AFE_IRQ10_CNT_MON:
+ case AFE_IRQ11_CNT_MON:
+ case AFE_IRQ12_CNT_MON:
+ case AFE_IRQ13_CNT_MON:
+ case AFE_IRQ14_CNT_MON:
+ case AFE_IRQ15_CNT_MON:
+ case AFE_IRQ16_CNT_MON:
+ case AFE_IRQ17_CNT_MON:
+ case AFE_IRQ18_CNT_MON:
+ case AFE_IRQ19_CNT_MON:
+ case AFE_IRQ20_CNT_MON:
+ case AFE_IRQ21_CNT_MON:
+ case AFE_IRQ22_CNT_MON:
+ case AFE_IRQ23_CNT_MON:
+ case AFE_IRQ24_CNT_MON:
+ case AFE_IRQ25_CNT_MON:
+ case AFE_IRQ26_CNT_MON:
+ case AFE_CM0_MON:
+ case AFE_CM0_IP_VERSION:
+ case AFE_CM1_MON:
+ case AFE_CM1_IP_VERSION:
+ case AFE_ADDA_UL0_SRC_DEBUG_MON0:
+ case AFE_ADDA_UL0_SRC_MON0:
+ case AFE_ADDA_UL0_SRC_MON1:
+ case AFE_ADDA_UL0_IP_VERSION:
+ case AFE_ADDA_DMIC0_SRC_DEBUG_MON0:
+ case AFE_ADDA_DMIC0_SRC_MON0:
+ case AFE_ADDA_DMIC0_SRC_MON1:
+ case AFE_ADDA_DMIC0_IP_VERSION:
+ case AFE_ADDA_DMIC1_SRC_DEBUG_MON0:
+ case AFE_ADDA_DMIC1_SRC_MON0:
+ case AFE_ADDA_DMIC1_SRC_MON1:
+ case AFE_ADDA_DMIC1_IP_VERSION:
+ case AFE_MTKAIF_IPM_VER_MON:
+ case AFE_MTKAIF_MON:
+ case AFE_AUD_PAD_TOP_MON:
+ case AFE_ADDA_MTKAIFV4_MON0:
+ case AFE_ADDA_MTKAIFV4_MON1:
+ case AFE_ADDA6_MTKAIFV4_MON0:
+ case ETDM_IN0_MON:
+ case ETDM_IN1_MON:
+ case ETDM_OUT0_MON:
+ case ETDM_OUT1_MON:
+ case ETDM_OUT4_MON:
+ case AFE_CONN_MON0:
+ case AFE_CONN_MON1:
+ case AFE_CONN_MON2:
+ case AFE_CONN_MON3:
+ case AFE_CONN_MON4:
+ case AFE_CONN_MON5:
+ case AFE_CBIP_SLV_DECODER_MON0:
+ case AFE_CBIP_SLV_DECODER_MON1:
+ case AFE_CBIP_SLV_MUX_MON0:
+ case AFE_CBIP_SLV_MUX_MON1:
+ case AFE_DL0_CUR_MSB:
+ case AFE_DL0_CUR:
+ case AFE_DL0_RCH_MON:
+ case AFE_DL0_LCH_MON:
+ case AFE_DL1_CUR_MSB:
+ case AFE_DL1_CUR:
+ case AFE_DL1_RCH_MON:
+ case AFE_DL1_LCH_MON:
+ case AFE_DL2_CUR_MSB:
+ case AFE_DL2_CUR:
+ case AFE_DL2_RCH_MON:
+ case AFE_DL2_LCH_MON:
+ case AFE_DL3_CUR_MSB:
+ case AFE_DL3_CUR:
+ case AFE_DL3_RCH_MON:
+ case AFE_DL3_LCH_MON:
+ case AFE_DL4_CUR_MSB:
+ case AFE_DL4_CUR:
+ case AFE_DL4_RCH_MON:
+ case AFE_DL4_LCH_MON:
+ case AFE_DL5_CUR_MSB:
+ case AFE_DL5_CUR:
+ case AFE_DL5_RCH_MON:
+ case AFE_DL5_LCH_MON:
+ case AFE_DL6_CUR_MSB:
+ case AFE_DL6_CUR:
+ case AFE_DL6_RCH_MON:
+ case AFE_DL6_LCH_MON:
+ case AFE_DL7_CUR_MSB:
+ case AFE_DL7_CUR:
+ case AFE_DL7_RCH_MON:
+ case AFE_DL7_LCH_MON:
+ case AFE_DL8_CUR_MSB:
+ case AFE_DL8_CUR:
+ case AFE_DL8_RCH_MON:
+ case AFE_DL8_LCH_MON:
+ case AFE_DL_24CH_CUR_MSB:
+ case AFE_DL_24CH_CUR:
+ case AFE_DL23_CUR_MSB:
+ case AFE_DL23_CUR:
+ case AFE_DL23_RCH_MON:
+ case AFE_DL23_LCH_MON:
+ case AFE_DL24_CUR_MSB:
+ case AFE_DL24_CUR:
+ case AFE_DL24_RCH_MON:
+ case AFE_DL24_LCH_MON:
+ case AFE_DL25_CUR_MSB:
+ case AFE_DL25_CUR:
+ case AFE_DL25_RCH_MON:
+ case AFE_DL25_LCH_MON:
+ case AFE_VUL0_CUR_MSB:
+ case AFE_VUL0_CUR:
+ case AFE_VUL1_CUR_MSB:
+ case AFE_VUL1_CUR:
+ case AFE_VUL2_CUR_MSB:
+ case AFE_VUL2_CUR:
+ case AFE_VUL3_CUR_MSB:
+ case AFE_VUL3_CUR:
+ case AFE_VUL4_CUR_MSB:
+ case AFE_VUL4_CUR:
+ case AFE_VUL5_CUR_MSB:
+ case AFE_VUL5_CUR:
+ case AFE_VUL6_CUR_MSB:
+ case AFE_VUL6_CUR:
+ case AFE_VUL7_CUR_MSB:
+ case AFE_VUL7_CUR:
+ case AFE_VUL8_CUR_MSB:
+ case AFE_VUL8_CUR:
+ case AFE_VUL9_CUR_MSB:
+ case AFE_VUL9_CUR:
+ case AFE_VUL10_CUR_MSB:
+ case AFE_VUL10_CUR:
+ case AFE_VUL24_CUR_MSB:
+ case AFE_VUL24_CUR:
+ case AFE_VUL25_CUR_MSB:
+ case AFE_VUL25_CUR:
+ case AFE_VUL_CM0_CUR_MSB:
+ case AFE_VUL_CM0_CUR:
+ case AFE_VUL_CM1_CUR_MSB:
+ case AFE_VUL_CM1_CUR:
+ case AFE_ETDM_IN0_CUR_MSB:
+ case AFE_ETDM_IN0_CUR:
+ case AFE_ETDM_IN1_CUR_MSB:
+ case AFE_ETDM_IN1_CUR:
+ case AFE_HDMI_OUT_CUR_MSB:
+ case AFE_HDMI_OUT_CUR:
+ case AFE_HDMI_OUT_END:
+ case AFE_HDMI_OUT_MON0:
+ case AFE_PROT_SIDEBAND0_MON:
+ case AFE_PROT_SIDEBAND1_MON:
+ case AFE_PROT_SIDEBAND2_MON:
+ case AFE_PROT_SIDEBAND3_MON:
+ case AFE_DOMAIN_SIDEBAND0_MON:
+ case AFE_DOMAIN_SIDEBAND1_MON:
+ case AFE_DOMAIN_SIDEBAND2_MON:
+ case AFE_DOMAIN_SIDEBAND3_MON:
+ case AFE_DOMAIN_SIDEBAND4_MON:
+ case AFE_DOMAIN_SIDEBAND5_MON:
+ case AFE_DOMAIN_SIDEBAND6_MON:
+ case AFE_DOMAIN_SIDEBAND7_MON:
+ case AFE_DOMAIN_SIDEBAND8_MON:
+ case AFE_DOMAIN_SIDEBAND9_MON:
+ case AFE_PCM0_INTF_CON1_MASK_MON:
+ case AFE_CONNSYS_I2S_CON_MASK_MON:
+ case AFE_MTKAIF0_CFG0_MASK_MON:
+ case AFE_MTKAIF1_CFG0_MASK_MON:
+ case AFE_ADDA_UL0_SRC_CON0_MASK_MON:
+ case AFE_ASRC_NEW_CON0:
+ case AFE_ASRC_NEW_CON6:
+ case AFE_ASRC_NEW_CON8:
+ case AFE_ASRC_NEW_CON9:
+ case AFE_ASRC_NEW_CON12:
+ case AFE_ASRC_NEW_IP_VERSION:
+ case AFE_GASRC0_NEW_CON0:
+ case AFE_GASRC0_NEW_CON6:
+ case AFE_GASRC0_NEW_CON8:
+ case AFE_GASRC0_NEW_CON9:
+ case AFE_GASRC0_NEW_CON10:
+ case AFE_GASRC0_NEW_CON11:
+ case AFE_GASRC0_NEW_CON12:
+ case AFE_GASRC0_NEW_IP_VERSION:
+ case AFE_GASRC1_NEW_CON0:
+ case AFE_GASRC1_NEW_CON6:
+ case AFE_GASRC1_NEW_CON8:
+ case AFE_GASRC1_NEW_CON9:
+ case AFE_GASRC1_NEW_CON12:
+ case AFE_GASRC1_NEW_IP_VERSION:
+ case AFE_GASRC2_NEW_CON0:
+ case AFE_GASRC2_NEW_CON6:
+ case AFE_GASRC2_NEW_CON8:
+ case AFE_GASRC2_NEW_CON9:
+ case AFE_GASRC2_NEW_CON12:
+ case AFE_GASRC2_NEW_IP_VERSION:
+ case AFE_GAIN0_CUR_L:
+ case AFE_GAIN0_CUR_R:
+ case AFE_GAIN1_CUR_L:
+ case AFE_GAIN1_CUR_R:
+ case AFE_GAIN2_CUR_L:
+ case AFE_GAIN2_CUR_R:
+ case AFE_GAIN3_CUR_L:
+ case AFE_GAIN3_CUR_R:
+ case AFE_IRQ_MCU_EN:
+ case AFE_CUSTOM_IRQ_MCU_EN:
+ case AFE_IRQ_MCU_DSP_EN:
+ case AFE_IRQ_MCU_DSP2_EN:
+ case AFE_DL5_CON0:
+ case AFE_DL6_CON0:
+ case AFE_DL23_CON0:
+ case AFE_DL_24CH_CON0:
+ case AFE_VUL1_CON0:
+ case AFE_VUL3_CON0:
+ case AFE_VUL4_CON0:
+ case AFE_VUL5_CON0:
+ case AFE_VUL9_CON0:
+ case AFE_VUL25_CON0:
+ case AFE_IRQ0_MCU_CFG0:
+ case AFE_IRQ1_MCU_CFG0:
+ case AFE_IRQ2_MCU_CFG0:
+ case AFE_IRQ3_MCU_CFG0:
+ case AFE_IRQ4_MCU_CFG0:
+ case AFE_IRQ5_MCU_CFG0:
+ case AFE_IRQ6_MCU_CFG0:
+ case AFE_IRQ7_MCU_CFG0:
+ case AFE_IRQ8_MCU_CFG0:
+ case AFE_IRQ9_MCU_CFG0:
+ case AFE_IRQ10_MCU_CFG0:
+ case AFE_IRQ11_MCU_CFG0:
+ case AFE_IRQ12_MCU_CFG0:
+ case AFE_IRQ13_MCU_CFG0:
+ case AFE_IRQ14_MCU_CFG0:
+ case AFE_IRQ15_MCU_CFG0:
+ case AFE_IRQ16_MCU_CFG0:
+ case AFE_IRQ17_MCU_CFG0:
+ case AFE_IRQ18_MCU_CFG0:
+ case AFE_IRQ19_MCU_CFG0:
+ case AFE_IRQ20_MCU_CFG0:
+ case AFE_IRQ21_MCU_CFG0:
+ case AFE_IRQ22_MCU_CFG0:
+ case AFE_IRQ23_MCU_CFG0:
+ case AFE_IRQ24_MCU_CFG0:
+ case AFE_IRQ25_MCU_CFG0:
+ case AFE_IRQ26_MCU_CFG0:
+ case AFE_CUSTOM_IRQ0_MCU_CFG0:
+ case AFE_IRQ0_MCU_CFG1:
+ case AFE_IRQ1_MCU_CFG1:
+ case AFE_IRQ2_MCU_CFG1:
+ case AFE_IRQ3_MCU_CFG1:
+ case AFE_IRQ4_MCU_CFG1:
+ case AFE_IRQ5_MCU_CFG1:
+ case AFE_IRQ6_MCU_CFG1:
+ case AFE_IRQ7_MCU_CFG1:
+ case AFE_IRQ8_MCU_CFG1:
+ case AFE_IRQ9_MCU_CFG1:
+ case AFE_IRQ10_MCU_CFG1:
+ case AFE_IRQ11_MCU_CFG1:
+ case AFE_IRQ12_MCU_CFG1:
+ case AFE_IRQ13_MCU_CFG1:
+ case AFE_IRQ14_MCU_CFG1:
+ case AFE_IRQ15_MCU_CFG1:
+ case AFE_IRQ16_MCU_CFG1:
+ case AFE_IRQ17_MCU_CFG1:
+ case AFE_IRQ18_MCU_CFG1:
+ case AFE_IRQ19_MCU_CFG1:
+ case AFE_IRQ20_MCU_CFG1:
+ case AFE_IRQ21_MCU_CFG1:
+ case AFE_IRQ22_MCU_CFG1:
+ case AFE_IRQ23_MCU_CFG1:
+ case AFE_IRQ24_MCU_CFG1:
+ case AFE_IRQ25_MCU_CFG1:
+ case AFE_IRQ26_MCU_CFG1:
+ case AFE_CUSTOM_IRQ0_MCU_CFG1:
+ /* for vow using */
+ case AFE_IRQ_MCU_SCP_EN:
+ case AFE_VUL_CM0_BASE_MSB:
+ case AFE_VUL_CM0_BASE:
+ case AFE_VUL_CM0_END_MSB:
+ case AFE_VUL_CM0_END:
+ case AFE_VUL_CM0_CON0:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8189_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .volatile_reg = mt8189_is_volatile_reg,
+
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = AFE_MAX_REGISTER,
+
+ .cache_type = REGCACHE_FLAT,
+};
+
+static irqreturn_t mt8189_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ u32 status;
+ u32 status_mcu;
+ u32 mcu_en;
+ u32 cus_status;
+ u32 cus_status_mcu;
+ u32 cus_mcu_en;
+ u32 tmp_reg;
+ int ret, cus_ret;
+ int i;
+ struct timespec64 ts64;
+ u64 t1, t2;
+ /* one interrupt period = 5ms */
+ const u64 timeout_limit = 5000000;
+
+ /* get irq that is sent to MCU */
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+ regmap_read(afe->regmap, AFE_CUSTOM_IRQ_MCU_EN, &cus_mcu_en);
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ cus_ret = regmap_read(afe->regmap, AFE_CUSTOM_IRQ_MCU_STATUS, &cus_status);
+ /* only care IRQ which is sent to MCU */
+ status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
+ cus_status_mcu = cus_status & cus_mcu_en & AFE_IRQ_STATUS_BITS;
+ if ((ret || status_mcu == 0) && (cus_ret || cus_status_mcu == 0)) {
+ dev_err(afe->dev, "%s(), irq status err, ret %d, 0x%x:0x%x:0x%x:0x%x\n",
+ __func__, ret, status, mcu_en, cus_status_mcu, cus_mcu_en);
+ return IRQ_NONE;
+ }
+
+ ktime_get_ts64(&ts64);
+ t1 = ktime_get_ns();
+
+ for (i = 0; i < MT8189_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (i == MT8189_MEMIF_HDMI) {
+ if (cus_status_mcu & BIT(irq->irq_data->id))
+ snd_pcm_period_elapsed(memif->substream);
+ } else if (status_mcu & BIT(irq->irq_data->id)) {
+ snd_pcm_period_elapsed(memif->substream);
+ }
+ }
+
+ ktime_get_ts64(&ts64);
+ t2 = ktime_get_ns();
+ t2 = t2 - t1; /* in ns (10^9) */
+
+ if (t2 > timeout_limit)
+ dev_warn(afe->dev, "IRQ handler exceeded time limit by %llu ns\n",
+ t2 - timeout_limit);
+
+ /* clear irq */
+ for (i = 0; i < MT8189_IRQ_NUM; ++i) {
+ if (((cus_status_mcu & BIT(irq_data[i].id)) && i == MT8189_IRQ_31) ||
+ ((status_mcu & BIT(irq_data[i].id)) && i != MT8189_IRQ_31)) {
+ regmap_read(afe->regmap, irq_data[i].irq_clr_reg, &tmp_reg);
+ regmap_update_bits(afe->regmap, irq_data[i].irq_clr_reg,
+ AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
+ tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mt8189_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ unsigned int value;
+ unsigned int tmp_reg;
+ int ret, i;
+
+ if (!afe->regmap) {
+ dev_warn(afe->dev, "%s() skip regmap\n", __func__);
+ goto skip_regmap;
+ }
+
+ /* disable AFE */
+ mt8189_afe_disable_main_clock(afe);
+
+ ret = regmap_read_poll_timeout(afe->regmap,
+ AUDIO_ENGEN_CON0_MON,
+ value,
+ (value & AUDIO_ENGEN_MON_SFT) == 0,
+ 20,
+ 1 * 1000 * 1000);
+ dev_dbg(afe->dev, "%s() read_poll ret %d\n", __func__, ret);
+ if (ret)
+ dev_warn(afe->dev, "%s(), ret %d\n", __func__, ret);
+
+ /* make sure all irq status are cleared */
+ for (i = 0; i < MT8189_IRQ_NUM; i++) {
+ regmap_read(afe->regmap, irq_data[i].irq_clr_reg, &tmp_reg);
+ regmap_update_bits(afe->regmap, irq_data[i].irq_clr_reg,
+ AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
+ tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
+ }
+
+ /* reset audio 26M request */
+ regmap_update_bits(afe->regmap,
+ AFE_SPM_CONTROL_REQ, 0x1, 0x0);
+
+ /* cache only */
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8189_afe_disable_reg_rw_clk(afe);
+ return 0;
+}
+
+static int mt8189_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ int ret;
+
+ ret = mt8189_afe_enable_reg_rw_clk(afe);
+ if (ret)
+ return ret;
+
+ if (!afe->regmap) {
+ dev_warn(afe->dev, "skip regmap\n");
+ return 0;
+ }
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ /* set audio 26M request */
+ regmap_update_bits(afe->regmap, AFE_SPM_CONTROL_REQ, 0x1, 0x1);
+ regmap_update_bits(afe->regmap, AFE_CBIP_CFG0, 0x1, 0x1);
+
+ /* force cpu use 8_24 format when writing 32bit data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_CON0,
+ CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
+
+ /* enable AFE */
+ mt8189_afe_enable_main_clock(afe);
+
+ return 0;
+}
+
+static int mt8189_afe_component_probe(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ /* enable clock for regcache get default value from hw */
+ pm_runtime_get_sync(afe->dev);
+ mtk_afe_add_sub_dai_control(component);
+ pm_runtime_put_sync(afe->dev);
+
+ return 0;
+}
+
+static int mt8189_afe_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ /* set the wait_for_avail to 2 sec*/
+ substream->wait_time = msecs_to_jiffies(2 * 1000);
+
+ return 0;
+}
+
+static void mt8189_afe_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_soc_component_driver mt8189_afe_component = {
+ .name = AFE_PCM_NAME,
+ .probe = mt8189_afe_component_probe,
+ .pcm_construct = mtk_afe_pcm_new,
+ .pcm_destruct = mt8189_afe_pcm_free,
+ .open = mt8189_afe_pcm_open,
+ .pointer = mtk_afe_pcm_pointer,
+};
+
+static int mt8189_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8189_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8189_memif_dai_driver);
+ dai->dapm_widgets = mt8189_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8189_memif_widgets);
+ dai->dapm_routes = mt8189_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8189_memif_routes);
+
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8189_dai_adda_register,
+ mt8189_dai_i2s_register,
+ mt8189_dai_pcm_register,
+ mt8189_dai_tdm_register,
+ mt8189_dai_memif_register,
+};
+
+static const struct reg_sequence mt8189_cg_patch[] = {
+ { AUDIO_TOP_CON4, 0x361c },
+};
+
+static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ unsigned int tmp_reg;
+ int irq_id;
+ struct mtk_base_afe *afe;
+ struct mt8189_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+ dev_warn(dev, "failed to assign memory region: %d\n", ret);
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = dev;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return dev_err_probe(dev, PTR_ERR(afe->base_addr),
+ "AFE base_addr not found\n");
+
+ /* init audio related clock */
+ ret = mt8189_init_clock(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "init clock error.\n");
+
+ /* init memif */
+ /* IPM2.0 no need banding */
+ afe->memif_32bit_supported = 1;
+ afe->memif_size = MT8189_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = memif_irq_usage[i];
+ afe->memif[i].const_irq = 1;
+ }
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* init irq */
+ afe->irqs_size = MT8189_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return dev_err_probe(dev, irq_id, "no irq found");
+
+ ret = devm_request_irq(dev, irq_id, mt8189_afe_irq_handler,
+ IRQF_TRIGGER_NONE,
+ "Afe_ISR_Handle", afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "dai register i %d fail\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ /* others */
+ afe->mtk_afe_hardware = &mt8189_afe_hardware;
+ afe->memif_fs = mt8189_memif_fs;
+ afe->irq_fs = mt8189_irq_fs;
+ afe->get_dai_fs = mt8189_get_dai_fs;
+ afe->get_memif_pbuf_size = mt8189_get_memif_pbuf_size;
+
+ afe->runtime_resume = mt8189_afe_runtime_resume;
+ afe->runtime_suspend = mt8189_afe_runtime_suspend;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Audio device is part of genpd. Registering it as a syscore device
+ * ensure the proper power-on sequence of the AFE device.
+ */
+ dev_pm_syscore_device(dev, true);
+
+ /* enable clock for regcache get default value from hw */
+ pm_runtime_get_sync(dev);
+
+ afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr,
+ &mt8189_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ ret = regmap_register_patch(afe->regmap, mt8189_cg_patch,
+ ARRAY_SIZE(mt8189_cg_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply cg patch\n");
+ goto err_pm_disable;
+ }
+
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg);
+ regmap_write(afe->regmap, AFE_IRQ_MCU_EN, 0xffffffff);
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg);
+
+ pm_runtime_put_sync(dev);
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev,
+ &mt8189_afe_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_err(dev, "afe component err: %d\n", ret);
+ goto err_pm_disable;
+ }
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_put_sync(dev);
+ return ret;
+}
+
+static void mt8189_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_put_sync(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt8189_afe_runtime_suspend(dev);
+
+ mt8189_afe_disable_main_clock(afe);
+ /* disable afe clock */
+ mt8189_afe_disable_reg_rw_clk(afe);
+ of_reserved_mem_device_release(dev);
+}
+
+static const struct of_device_id mt8189_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8189-afe-pcm", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8189_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8189_afe_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt8189_afe_runtime_suspend,
+ mt8189_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8189_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8189-afe-pcm",
+ .of_match_table = mt8189_afe_pcm_dt_match,
+ .pm = &mt8189_afe_pm_ops,
+ },
+ .probe = mt8189_afe_pcm_dev_probe,
+ .remove = mt8189_afe_pcm_dev_remove,
+};
+module_platform_driver(mt8189_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8189");
+MODULE_AUTHOR("Darren Ye <darren.ye@mediatek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c
new file mode 100644
index 000000000000..ad5b9546ff63
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c
@@ -0,0 +1,1228 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI ADDA Control
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/regmap.h>
+#include <linux/delay.h>
+
+#include "mt8189-afe-clk.h"
+#include "mt8189-afe-common.h"
+#include "mt8189-interconnection.h"
+
+/* mt6363 vs1 voter */
+#define VS1_MT6338_MASK_SFT 0x1
+#define RG_BUCK_VS1_VOTER_EN_LO 0x189a
+#define RG_BUCK_VS1_VOTER_EN_LO_SET 0x189b
+#define RG_BUCK_VS1_VOTER_EN_LO_CLR 0x189c
+
+#define AUDIO_SDM_LEVEL_NORMAL 0x1d
+#define MTK_AFE_ADDA_DL_GAIN_NORMAL 0xf74f
+#define SDM_AUTO_RESET_THRESHOLD 0x190000
+
+enum {
+ SUPPLY_SEQ_ADDA_AFE_ON,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
+ SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA6_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA_FIFO,
+ SUPPLY_SEQ_ADDA_AP_DMIC,
+ SUPPLY_SEQ_ADDA_UL_ON,
+};
+
+enum {
+ UL_IIR_SW,
+ UL_IIR_5HZ,
+ UL_IIR_10HZ,
+ UL_IIR_25HZ,
+ UL_IIR_50HZ,
+ UL_IIR_75HZ,
+};
+
+enum {
+ AUDIO_SDM_2ND,
+ AUDIO_SDM_3RD,
+};
+
+enum {
+ DELAY_DATA_MISO1,
+ DELAY_DATA_MISO2,
+};
+
+enum {
+ MTK_AFE_ADDA_DL_RATE_8K,
+ MTK_AFE_ADDA_DL_RATE_11K,
+ MTK_AFE_ADDA_DL_RATE_12K,
+ MTK_AFE_ADDA_DL_RATE_16K = 4,
+ MTK_AFE_ADDA_DL_RATE_22K,
+ MTK_AFE_ADDA_DL_RATE_24K,
+ MTK_AFE_ADDA_DL_RATE_32K = 8,
+ MTK_AFE_ADDA_DL_RATE_44K,
+ MTK_AFE_ADDA_DL_RATE_48K,
+ MTK_AFE_ADDA_DL_RATE_88K = 13,
+ MTK_AFE_ADDA_DL_RATE_96K,
+ MTK_AFE_ADDA_DL_RATE_176K = 17,
+ MTK_AFE_ADDA_DL_RATE_192K,
+ MTK_AFE_ADDA_DL_RATE_352K = 21,
+ MTK_AFE_ADDA_DL_RATE_384K,
+};
+
+enum {
+ MTK_AFE_ADDA_UL_RATE_8K,
+ MTK_AFE_ADDA_UL_RATE_16K,
+ MTK_AFE_ADDA_UL_RATE_32K,
+ MTK_AFE_ADDA_UL_RATE_48K,
+ MTK_AFE_ADDA_UL_RATE_96K,
+ MTK_AFE_ADDA_UL_RATE_192K,
+ MTK_AFE_ADDA_UL_RATE_48K_HD,
+};
+
+struct mtk_afe_adda_priv {
+ int dl_rate;
+ int ul_rate;
+};
+
+static unsigned int adda_dl_rate_transform(struct mtk_base_afe *afe,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_DL_RATE_8K;
+ case 11025:
+ return MTK_AFE_ADDA_DL_RATE_11K;
+ case 12000:
+ return MTK_AFE_ADDA_DL_RATE_12K;
+ case 16000:
+ return MTK_AFE_ADDA_DL_RATE_16K;
+ case 22050:
+ return MTK_AFE_ADDA_DL_RATE_22K;
+ case 24000:
+ return MTK_AFE_ADDA_DL_RATE_24K;
+ case 32000:
+ return MTK_AFE_ADDA_DL_RATE_32K;
+ case 44100:
+ return MTK_AFE_ADDA_DL_RATE_44K;
+ case 48000:
+ return MTK_AFE_ADDA_DL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_DL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_DL_RATE_192K;
+ default:
+ dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
+ __func__, rate);
+ return MTK_AFE_ADDA_DL_RATE_48K;
+ }
+}
+
+static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_UL_RATE_8K;
+ case 16000:
+ return MTK_AFE_ADDA_UL_RATE_16K;
+ case 32000:
+ return MTK_AFE_ADDA_UL_RATE_32K;
+ case 48000:
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_UL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_UL_RATE_192K;
+ default:
+ dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
+ __func__, rate);
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ }
+}
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN014_1, I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN014_1, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN014_1, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN014_1, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN014_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN014_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN014_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN014_1, I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN014_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN014_1, I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL24_CH1", AFE_CONN014_2, I_DL24_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN014_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN014_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN014_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH1", AFE_CONN014_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN014_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH1", AFE_CONN014_6,
+ I_SRC_0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH1", AFE_CONN014_6,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH1", AFE_CONN014_6,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN015_1, I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN015_1, I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN015_1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN015_1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN015_1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN015_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN015_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN015_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN015_1, I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN015_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN015_1, I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL24_CH2", AFE_CONN015_2, I_DL24_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN015_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN015_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN015_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH2", AFE_CONN015_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN015_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN015_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH2", AFE_CONN015_6,
+ I_SRC_0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH2", AFE_CONN015_6,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH2", AFE_CONN015_6,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN016_1, I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN016_1, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN016_1, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN016_1, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN016_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN016_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN016_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN016_1, I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN016_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN016_1, I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH3", AFE_CONN016_1, I_DL_24CH_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL24_CH1", AFE_CONN016_2, I_DL24_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN016_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN016_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN016_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH1", AFE_CONN016_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN016_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH1", AFE_CONN016_6,
+ I_SRC_0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH1", AFE_CONN016_6,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH1", AFE_CONN016_6,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN017_1, I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN017_1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN017_1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN017_1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN017_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN017_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN017_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN017_1, I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN017_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN017_1, I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH4", AFE_CONN017_1, I_DL_24CH_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL24_CH2", AFE_CONN017_2, I_DL24_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN017_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN017_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN017_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH2", AFE_CONN017_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN017_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN017_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_0_OUT_CH2", AFE_CONN017_6,
+ I_SRC_0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_1_OUT_CH2", AFE_CONN017_6,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH2", AFE_CONN017_6,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static int mtk_adda_ul_src_enable_dmic(struct mtk_base_afe *afe, int id)
+{
+ unsigned int reg, reg1;
+
+ switch (id) {
+ case MT8189_DAI_ADDA:
+ reg = AFE_ADDA_UL0_SRC_CON0;
+ reg1 = AFE_ADDA_UL0_SRC_CON1;
+ break;
+ case MT8189_DAI_AP_DMIC:
+ reg = AFE_ADDA_DMIC0_SRC_CON0;
+ reg1 = AFE_ADDA_DMIC0_SRC_CON1;
+ break;
+ case MT8189_DAI_AP_DMIC_CH34:
+ reg = AFE_ADDA_DMIC1_SRC_CON0;
+ reg1 = AFE_ADDA_DMIC1_SRC_CON1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* choose Phase */
+ regmap_update_bits(afe->regmap, reg,
+ UL_DMIC_PHASE_SEL_CH1_MASK_SFT,
+ 0x0 << UL_DMIC_PHASE_SEL_CH1_SFT);
+ regmap_update_bits(afe->regmap, reg,
+ UL_DMIC_PHASE_SEL_CH2_MASK_SFT,
+ 0x4 << UL_DMIC_PHASE_SEL_CH2_SFT);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, reg,
+ DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT,
+ 0x0);
+ regmap_update_bits(afe->regmap, reg,
+ DMIC_LOW_POWER_MODE_CTL_MASK_SFT,
+ 0x0);
+
+ /* turn on dmic, ch1, ch2 */
+ regmap_update_bits(afe->regmap, reg,
+ UL_SDM_3_LEVEL_CTL_MASK_SFT,
+ 0x1 << UL_SDM_3_LEVEL_CTL_SFT);
+ regmap_update_bits(afe->regmap, reg,
+ UL_MODE_3P25M_CH1_CTL_MASK_SFT,
+ 0x1 << UL_MODE_3P25M_CH1_CTL_SFT);
+ regmap_update_bits(afe->regmap, reg,
+ UL_MODE_3P25M_CH2_CTL_MASK_SFT,
+ 0x1 << UL_MODE_3P25M_CH2_CTL_SFT);
+
+ /* ul gain: gain = 0x7fff/positive_gain = 0x0/gain_mode = 0x10 */
+ regmap_update_bits(afe->regmap, reg1,
+ ADDA_UL_GAIN_VALUE_MASK_SFT,
+ 0x7fff << ADDA_UL_GAIN_VALUE_SFT);
+ regmap_update_bits(afe->regmap, reg1,
+ ADDA_UL_POSTIVEGAIN_MASK_SFT,
+ 0x0 << ADDA_UL_POSTIVEGAIN_SFT);
+ /* gain_mode = 0x02: Add 0.5 gain at CIC output */
+ regmap_update_bits(afe->regmap, reg1,
+ GAIN_MODE_MASK_SFT,
+ 0x02 << GAIN_MODE_SFT);
+
+ return 0;
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int mtkaif_dmic = afe_priv->mtkaif_dmic;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x, mtkaif_dmic %d\n",
+ __func__, w->name, event, mtkaif_dmic);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* update setting to dmic */
+ if (mtkaif_dmic) {
+ /* mtkaif_rxif_data_mode = 1, dmic */
+ regmap_update_bits(afe->regmap, AFE_MTKAIF0_RX_CFG0,
+ RG_MTKAIF0_RXIF_DATA_MODE_MASK_SFT,
+ 0x1);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, AFE_MTKAIF0_RX_CFG0,
+ RG_MTKAIF0_RXIF_VOICE_MODE_MASK_SFT,
+ 0x0);
+ mtk_adda_ul_src_enable_dmic(afe, MT8189_DAI_ADDA);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(120, 130);
+
+ /* reset dmic */
+ afe_priv->mtkaif_dmic = 0;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ if (event == SND_SOC_DAPM_PRE_PMU) {
+ if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2)
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8);
+ else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2)
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0);
+ else
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0);
+ }
+
+ return 0;
+}
+
+static bool is_adda_mtkaif_need_phase_delay(struct mt8189_afe_private *afe_priv)
+{
+ return afe_priv->mtkaif_chosen_phase[0] >= 0 &&
+ afe_priv->mtkaif_chosen_phase[1] >= 0;
+}
+
+static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int delay_data;
+ int delay_cycle;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) {
+ /* set protocol 2 */
+ regmap_write(afe->regmap, AFE_MTKAIF0_CFG0,
+ 0x00010000);
+ regmap_write(afe->regmap, AFE_MTKAIF1_CFG0,
+ 0x00010000);
+
+ /* mtkaif_rxif_clkinv_adc inverse for calibration */
+ regmap_update_bits(afe->regmap, AFE_MTKAIF0_CFG0,
+ RG_MTKAIF0_RXIF_CLKINV_MASK_SFT,
+ 0x1 << RG_MTKAIF0_RXIF_CLKINV_SFT);
+ regmap_update_bits(afe->regmap, AFE_MTKAIF1_CFG0,
+ RG_MTKAIF1_RXIF_CLKINV_ADC_MASK_SFT,
+ 0x1 << RG_MTKAIF1_RXIF_CLKINV_ADC_SFT);
+
+ /* This event align the phase of every miso pin */
+ /* If only 1 miso is used, there is no need to do phase delay. */
+ if (strcmp(w->name, "ADDA_MTKAIF_CFG") == 0 &&
+ !is_adda_mtkaif_need_phase_delay(afe_priv)) {
+ dev_dbg(afe->dev,
+ "%s(), check adda mtkaif_chosen_phase[0/1]:%d/%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[0],
+ afe_priv->mtkaif_chosen_phase[1]);
+ break;
+ } else if (strcmp(w->name, "ADDA6_MTKAIF_CFG") == 0 &&
+ afe_priv->mtkaif_chosen_phase[2] < 0) {
+ dev_dbg(afe->dev,
+ "%s(), check adda6 mtkaif_chosen_phase[2]:%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[2]);
+ break;
+ }
+
+ /* set delay for ch12 to align phase of miso0 and miso1 */
+ if (afe_priv->mtkaif_phase_cycle[0] >=
+ afe_priv->mtkaif_phase_cycle[1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[0] -
+ afe_priv->mtkaif_phase_cycle[1];
+ } else {
+ delay_data = DELAY_DATA_MISO2;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
+ afe_priv->mtkaif_phase_cycle[0];
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF0_RX_CFG2,
+ RG_MTKAIF0_RXIF_DELAY_DATA_MASK_SFT,
+ delay_data <<
+ RG_MTKAIF0_RXIF_DELAY_DATA_SFT);
+
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF0_RX_CFG2,
+ RG_MTKAIF0_RXIF_DELAY_CYCLE_MASK_SFT,
+ delay_cycle <<
+ RG_MTKAIF0_RXIF_DELAY_CYCLE_SFT);
+
+ /* set delay between ch3 and ch2 */
+ if (afe_priv->mtkaif_phase_cycle[2] >=
+ afe_priv->mtkaif_phase_cycle[1]) {
+ delay_data = DELAY_DATA_MISO1; /* ch3 */
+ delay_cycle = afe_priv->mtkaif_phase_cycle[2] -
+ afe_priv->mtkaif_phase_cycle[1];
+ } else {
+ delay_data = DELAY_DATA_MISO2; /* ch2 */
+ delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
+ afe_priv->mtkaif_phase_cycle[2];
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF1_RX_CFG2,
+ RG_MTKAIF1_RXIF_DELAY_DATA_MASK_SFT,
+ delay_data <<
+ RG_MTKAIF1_RXIF_DELAY_DATA_SFT);
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF1_RX_CFG2,
+ RG_MTKAIF1_RXIF_DELAY_CYCLE_MASK_SFT,
+ delay_cycle <<
+ RG_MTKAIF1_RXIF_DELAY_CYCLE_SFT);
+ } else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) {
+ regmap_write(afe->regmap, AFE_MTKAIF0_CFG0,
+ 0x00010000);
+ regmap_write(afe->regmap, AFE_MTKAIF1_CFG0,
+ 0x00010000);
+ } else {
+ regmap_write(afe->regmap, AFE_MTKAIF0_CFG0, 0x0);
+ regmap_write(afe->regmap, AFE_MTKAIF1_CFG0, 0x0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ if (event == SND_SOC_DAPM_POST_PMD)
+ usleep_range(120, 130);
+
+ return 0;
+}
+
+static void mt6363_vs1_vote(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ bool pre_enable = afe_priv->is_mt6363_vote;
+ bool enable;
+
+ if (!afe_priv->pmic_regmap)
+ return;
+
+ enable = (afe_priv->is_adda_dl_on && afe_priv->is_adda_dl_max_vol) ||
+ (afe_priv->is_adda_ul_on);
+ if (enable == pre_enable) {
+ dev_dbg(afe->dev, "%s() enable == pre_enable = %d\n",
+ __func__, enable);
+ return;
+ }
+
+ afe_priv->is_mt6363_vote = enable;
+ dev_dbg(afe->dev, "%s() enable = %d\n", __func__, enable);
+
+ if (enable)
+ regmap_update_bits(afe_priv->pmic_regmap, RG_BUCK_VS1_VOTER_EN_LO_SET,
+ VS1_MT6338_MASK_SFT, 0x1);
+ else
+ regmap_update_bits(afe_priv->pmic_regmap, RG_BUCK_VS1_VOTER_EN_LO_CLR,
+ VS1_MT6338_MASK_SFT, 0x1);
+}
+
+static int mt_vs1_voter_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(afe->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ afe_priv->is_adda_dl_on = true;
+ mt6363_vs1_vote(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ afe_priv->is_adda_dl_on = false;
+ mt6363_vs1_vote(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt_vs1_voter_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(afe->dev, "%s(), event = 0x%x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ afe_priv->is_adda_ul_on = true;
+ mt6363_vs1_vote(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ afe_priv->is_adda_ul_on = false;
+ mt6363_vs1_vote(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt8189_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->mtkaif_dmic;
+
+ return 0;
+}
+
+static int mt8189_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int dmic_on;
+
+ dmic_on = !!ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, dmic_on %d\n",
+ __func__, kcontrol->id.name, dmic_on);
+
+ afe_priv->mtkaif_dmic = dmic_on;
+ afe_priv->mtkaif_dmic_ch34 = dmic_on;
+
+ return 0;
+}
+
+static int mt8189_adda_dl_max_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->is_adda_dl_max_vol;
+
+ return 0;
+}
+
+static int mt8189_adda_dl_max_vol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ bool is_adda_dl_max_vol = ucontrol->value.integer.value[0];
+
+ afe_priv->is_adda_dl_max_vol = is_adda_dl_max_vol;
+ mt6363_vs1_vote(afe);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_adda_controls[] = {
+ SOC_SINGLE("ADDA_DL_GAIN", AFE_ADDA_DL_SRC_CON1,
+ AFE_DL_GAIN1_CTL_PRE_SFT, AFE_DL_GAIN1_CTL_PRE_MASK, 0),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC Switch", 0,
+ mt8189_adda_dmic_get, mt8189_adda_dmic_set),
+ SOC_SINGLE_BOOL_EXT("ADDA_DL_MAX_VOL Switch", 0,
+ mt8189_adda_dl_max_vol_get,
+ mt8189_adda_dl_max_vol_set),
+};
+
+static const char *const adda_ul_mux_texts[] = {
+ "MTKAIF", "AP_DMIC", "AP_DMIC_MULTI_CH",
+};
+
+static SOC_ENUM_SINGLE_DECL(adda_ul_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ adda_ul_mux_texts);
+
+static const struct snd_kcontrol_new adda_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_kcontrol_new adda_ch34_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_CH34_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch1_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch2_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH3", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch3_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch3_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch4_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch4_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AUDIO_ENGEN_CON0, AUDIO_F3P25M_EN_ON_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_DL0_CG", SUPPLY_SEQ_ADDA_DL_ON,
+ AUDIO_TOP_CON0,
+ PDN_DL0_DAC_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_UL0_CG", SUPPLY_SEQ_ADDA_UL_ON,
+ AUDIO_TOP_CON1,
+ PDN_UL0_ADC_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC_CON0,
+ AFE_DL_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ mtk_adda_dl_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL0_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("AP DMIC Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_DMIC0_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("AP DMIC CH34 Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_DMIC1_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("AUD_PAD_TOP", SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
+ AFE_AUD_PAD_TOP_CFG0,
+ RG_RX_FIFO_ON_SFT, 0,
+ mtk_adda_pad_top_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADDA6_MTKAIF_CFG", SUPPLY_SEQ_ADDA6_MTKAIF_CFG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA_DMIC0_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC0_CG", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AUDIO_TOP_CON1,
+ PDN_DMIC0_ADC_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_CH34_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA_DMIC1_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC1_CG", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AUDIO_TOP_CON1,
+ PDN_DMIC1_ADC_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_UL0_SRC_CON1,
+ FIFO_SOFT_RST_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_DMIC0_SRC_CON1,
+ FIFO_SOFT_RST_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_CH34_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_DMIC1_SRC_CON1,
+ FIFO_SOFT_RST_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("VS1_VOTER_DL", SUPPLY_SEQ_ADDA_AFE_ON,
+ SND_SOC_NOPM, 0, 0,
+ mt_vs1_voter_dl_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("VS1_VOTER_UL", SUPPLY_SEQ_ADDA_AFE_ON,
+ SND_SOC_NOPM, 0, 0,
+ mt_vs1_voter_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADDA_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ul_mux_control),
+ SND_SOC_DAPM_MUX("ADDA_CH34_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ch34_ul_mux_control),
+
+ SND_SOC_DAPM_INPUT("AP_DMIC_INPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* playback */
+ {"ADDA_DL_CH1", "DL0_CH1", "DL0"},
+ {"ADDA_DL_CH2", "DL0_CH1", "DL0"},
+ {"ADDA_DL_CH2", "DL0_CH2", "DL0"},
+
+ {"ADDA_DL_CH1", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH2", "DL1"},
+
+ {"ADDA_DL_CH1", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH2", "DL2"},
+
+ {"ADDA_DL_CH1", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH2", "DL3"},
+
+ {"ADDA_DL_CH1", "DL4_CH1", "DL4"},
+ {"ADDA_DL_CH2", "DL4_CH2", "DL4"},
+
+ {"ADDA_DL_CH1", "DL5_CH1", "DL5"},
+ {"ADDA_DL_CH2", "DL5_CH2", "DL5"},
+
+ {"ADDA_DL_CH1", "DL6_CH1", "DL6"},
+ {"ADDA_DL_CH2", "DL6_CH2", "DL6"},
+
+ {"ADDA_DL_CH1", "DL7_CH1", "DL7"},
+ {"ADDA_DL_CH2", "DL7_CH2", "DL7"},
+
+ {"ADDA_DL_CH1", "DL8_CH1", "DL8"},
+ {"ADDA_DL_CH2", "DL8_CH2", "DL8"},
+
+ {"ADDA_DL_CH1", "DL_24CH_CH1", "DL_24CH"},
+ {"ADDA_DL_CH2", "DL_24CH_CH2", "DL_24CH"},
+
+ {"ADDA_DL_CH1", "DL24_CH1", "DL24"},
+ {"ADDA_DL_CH2", "DL24_CH2", "DL24"},
+
+ {"ADDA Playback", NULL, "ADDA_DL_CH1"},
+ {"ADDA Playback", NULL, "ADDA_DL_CH2"},
+
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+ {"ADDA Playback", NULL, "AUD_PAD_TOP"},
+ {"ADDA Playback", NULL, "VS1_VOTER_DL"},
+ {"ADDA Playback", NULL, "ADDA_DL0_CG"},
+
+ {"ADDA_DL_CH3", "DL0_CH1", "DL0"},
+ {"ADDA_DL_CH4", "DL0_CH2", "DL0"},
+
+ {"ADDA_DL_CH3", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH4", "DL1_CH2", "DL1"},
+
+ {"ADDA_DL_CH3", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH4", "DL2_CH2", "DL2"},
+
+ {"ADDA_DL_CH3", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH4", "DL3_CH2", "DL3"},
+
+ {"ADDA_DL_CH3", "DL4_CH1", "DL4"},
+ {"ADDA_DL_CH4", "DL4_CH2", "DL4"},
+
+ {"ADDA_DL_CH3", "DL5_CH1", "DL5"},
+ {"ADDA_DL_CH4", "DL5_CH2", "DL5"},
+
+ {"ADDA_DL_CH3", "DL6_CH1", "DL6"},
+ {"ADDA_DL_CH4", "DL6_CH2", "DL6"},
+
+ {"ADDA_DL_CH3", "DL7_CH1", "DL7"},
+ {"ADDA_DL_CH4", "DL7_CH2", "DL7"},
+
+ {"ADDA_DL_CH3", "DL8_CH1", "DL8"},
+ {"ADDA_DL_CH4", "DL8_CH2", "DL8"},
+
+ {"ADDA_DL_CH3", "DL_24CH_CH1", "DL_24CH"},
+ {"ADDA_DL_CH4", "DL_24CH_CH2", "DL_24CH"},
+ {"ADDA_DL_CH3", "DL_24CH_CH3", "DL_24CH"},
+ {"ADDA_DL_CH4", "DL_24CH_CH4", "DL_24CH"},
+
+ {"ADDA_DL_CH3", "DL24_CH1", "DL24"},
+ {"ADDA_DL_CH4", "DL24_CH2", "DL24"},
+
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+ {"ADDA Capture", NULL, "AUD_PAD_TOP"},
+ {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
+ {"ADDA Capture", NULL, "VS1_VOTER_UL"},
+ {"ADDA Capture", NULL, "ADDA_UL0_CG"},
+
+ /* capture */
+ {"ADDA_UL_Mux", "MTKAIF", "ADDA Capture"},
+ {"ADDA_UL_Mux", "AP_DMIC", "AP DMIC Capture"},
+ {"ADDA_CH34_UL_Mux", "AP_DMIC", "AP DMIC CH34 Capture"},
+
+ {"AP DMIC Capture", NULL, "ADDA Enable"},
+ {"AP DMIC Capture", NULL, "AP DMIC Capture Enable"},
+ {"AP DMIC Capture", NULL, "AP_DMIC_FIFO"},
+ {"AP DMIC Capture", NULL, "AP_DMIC_EN"},
+ {"AP DMIC Capture", NULL, "AP_DMIC0_CG"},
+
+ {"AP DMIC CH34 Capture", NULL, "ADDA Enable"},
+ {"AP DMIC CH34 Capture", NULL, "AP DMIC CH34 Capture Enable"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_FIFO"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_EN"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC1_CG"},
+
+ {"AP DMIC Capture", NULL, "AP_DMIC_INPUT"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_INPUT"},
+};
+
+/* dai ops */
+static int set_playback_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ unsigned int rate = params_rate(params);
+ struct mtk_afe_adda_priv *adda_priv;
+ unsigned int dl_src_con0;
+ unsigned int dl_src_con1;
+ int id = dai->id;
+
+ adda_priv = afe_priv->dai_priv[id];
+ if (!adda_priv)
+ return -EINVAL;
+
+ adda_priv->dl_rate = rate;
+
+ /* set sampling rate */
+ dl_src_con0 = adda_dl_rate_transform(afe, rate) <<
+ AFE_DL_INPUT_MODE_CTL_SFT;
+
+ /* set output mode, UP_SAMPLING_RATE_X8 */
+ dl_src_con0 |= (0x3 << AFE_DL_OUTPUT_SEL_CTL_SFT);
+
+ /* turn off mute function */
+ dl_src_con0 |= (0x01 << AFE_DL_MUTE_CH2_OFF_CTL_PRE_SFT);
+ dl_src_con0 |= (0x01 << AFE_DL_MUTE_CH1_OFF_CTL_PRE_SFT);
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ if (rate == 8000 || rate == 16000)
+ dl_src_con0 |= 0x01 << AFE_DL_VOICE_MODE_CTL_PRE_SFT;
+
+ /* SA suggest apply -0.3db to audio/speech path */
+ dl_src_con1 = MTK_AFE_ADDA_DL_GAIN_NORMAL <<
+ AFE_DL_GAIN1_CTL_PRE_SFT;
+ dl_src_con1 |= MTK_AFE_ADDA_DL_GAIN_NORMAL <<
+ AFE_DL_GAIN2_CTL_PRE_SFT;
+
+ /* turn on down-link gain */
+ dl_src_con0 |= (0x01 << AFE_DL_GAIN_ON_CTL_PRE_SFT);
+
+ if (id == MT8189_DAI_ADDA) {
+ /* clean predistortion */
+ regmap_write(afe->regmap, AFE_ADDA_DL_PREDIS_CON0, 0);
+ regmap_write(afe->regmap, AFE_ADDA_DL_PREDIS_CON1, 0);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SRC_CON0, dl_src_con0);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SRC_CON1, dl_src_con1);
+
+ /* set sdm gain */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ AFE_DL_ATTGAIN_CTL_MASK_SFT,
+ AUDIO_SDM_LEVEL_NORMAL <<
+ AFE_DL_ATTGAIN_CTL_SFT);
+
+ /* 2nd sdm */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ AFE_DL_USE_3RD_SDM_MASK_SFT,
+ AUDIO_SDM_2ND << AFE_DL_USE_3RD_SDM_SFT);
+
+ /* sdm auto reset */
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ SDM_AUTO_RESET_THRESHOLD);
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ AFE_DL_SDM_AUTO_RESET_TEST_ON_SFT,
+ 0x1 << AFE_DL_SDM_AUTO_RESET_TEST_ON_SFT);
+ }
+
+ return 0;
+}
+
+static int set_capture_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ unsigned int rate = params_rate(params);
+ struct mtk_afe_adda_priv *adda_priv;
+ unsigned int voice_mode;
+ unsigned int ul_src_con0;
+ int id = dai->id;
+
+ adda_priv = afe_priv->dai_priv[id];
+ if (!adda_priv)
+ return -EINVAL;
+
+ adda_priv->ul_rate = rate;
+
+ voice_mode = adda_ul_rate_transform(afe, rate);
+
+ ul_src_con0 = (voice_mode << UL_VOICE_MODE_CH1_CH2_CTL_SFT) &
+ UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT;
+
+ /* enable iir */
+ ul_src_con0 |= (1 << UL_IIR_ON_TMP_CTL_SFT) &
+ UL_IIR_ON_TMP_CTL_MASK_SFT;
+ ul_src_con0 |= (UL_IIR_SW << UL_IIRMODE_CTL_SFT) &
+ UL_IIRMODE_CTL_MASK_SFT;
+
+ switch (id) {
+ case MT8189_DAI_ADDA:
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_SRC_CON0, ul_src_con0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF0_RX_CFG0,
+ RG_MTKAIF0_RXIF_DATA_MODE_MASK_SFT,
+ 0x0 << RG_MTKAIF0_RXIF_DATA_MODE_SFT);
+ break;
+ case MT8189_DAI_AP_DMIC:
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC0_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC0_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC0_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC0_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC0_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC0_SRC_CON0, ul_src_con0);
+ break;
+ case MT8189_DAI_AP_DMIC_CH34:
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC1_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC1_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC1_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC1_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC1_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_DMIC1_SRC_CON0, ul_src_con0);
+ break;
+ default:
+ break;
+ }
+
+ /* ap dmic */
+ if (id == MT8189_DAI_AP_DMIC || id == MT8189_DAI_AP_DMIC_CH34)
+ mtk_adda_ul_src_enable_dmic(afe, id);
+
+ return 0;
+}
+
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = dai->id;
+
+ if (id >= MT8189_DAI_NUM || id < 0)
+ return -EINVAL;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, id, substream->stream, params_rate(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return set_playback_hw_params(params, dai);
+ else
+ return set_capture_hw_params(params, dai);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8189_DAI_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "ADDA_CH34",
+ .id = MT8189_DAI_ADDA_CH34,
+ .playback = {
+ .stream_name = "ADDA CH34 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC",
+ .id = MT8189_DAI_AP_DMIC,
+ .capture = {
+ .stream_name = "AP DMIC Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC_CH34",
+ .id = MT8189_DAI_AP_DMIC_CH34,
+ .capture = {
+ .stream_name = "AP DMIC CH34 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+static int init_adda_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_adda_priv *adda_priv;
+ static const int adda_dai_list[] = {
+ MT8189_DAI_ADDA,
+ MT8189_DAI_ADDA_CH34,
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
+ adda_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_afe_adda_priv),
+ GFP_KERNEL);
+ if (!adda_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
+ }
+
+ /* ap dmic priv share with adda */
+ afe_priv->dai_priv[MT8189_DAI_AP_DMIC] =
+ afe_priv->dai_priv[MT8189_DAI_ADDA];
+ afe_priv->dai_priv[MT8189_DAI_AP_DMIC_CH34] =
+ afe_priv->dai_priv[MT8189_DAI_ADDA_CH34];
+
+ return 0;
+}
+
+int mt8189_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ int ret;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+ dai->controls = mtk_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+
+ ret = init_adda_priv_data(afe);
+ if (ret)
+ return ret;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c b/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c
new file mode 100644
index 000000000000..94c49a662e2d
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c
@@ -0,0 +1,1463 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI I2S Control
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+
+#include "mt8189-afe-clk.h"
+#include "mt8189-afe-common.h"
+#include "mt8189-interconnection.h"
+
+#include "../common/mtk-afe-fe-dai.h"
+
+#define I2SIN0_MCLK_EN_W_NAME "I2SIN0_MCLK_EN"
+#define I2SIN1_MCLK_EN_W_NAME "I2SIN1_MCLK_EN"
+#define I2SOUT0_MCLK_EN_W_NAME "I2SOUT0_MCLK_EN"
+#define I2SOUT1_MCLK_EN_W_NAME "I2SOUT1_MCLK_EN"
+#define I2SOUT4_MCLK_EN_W_NAME "I2SOUT4_MCLK_EN"
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_I2S_MCLK_EN,
+ SUPPLY_SEQ_I2S_CG_EN,
+ SUPPLY_SEQ_I2S_EN,
+};
+
+/* this enum is merely for mtk_afe_i2s_priv declare */
+enum {
+ DAI_I2SIN0,
+ DAI_I2SIN1,
+ DAI_I2SOUT0,
+ DAI_I2SOUT1,
+ DAI_I2SOUT4,
+ DAI_I2S_NUM,
+};
+
+enum {
+ ETDM_CLK_SOURCE_H26M,
+ ETDM_CLK_SOURCE_APLL,
+ ETDM_CLK_SOURCE_SPDIF,
+ ETDM_CLK_SOURCE_HDMI,
+ ETDM_CLK_SOURCE_EARC,
+ ETDM_CLK_SOURCE_LINEIN,
+};
+
+enum {
+ ETDM_RELATCH_SEL_H26M,
+ ETDM_RELATCH_SEL_APLL,
+};
+
+enum {
+ ETDM_RATE_8K,
+ ETDM_RATE_12K,
+ ETDM_RATE_16K,
+ ETDM_RATE_24K,
+ ETDM_RATE_32K,
+ ETDM_RATE_48K,
+ ETDM_RATE_64K,
+ ETDM_RATE_96K,
+ ETDM_RATE_128K,
+ ETDM_RATE_192K,
+ ETDM_RATE_256K,
+ ETDM_RATE_384K,
+ ETDM_RATE_11025 = 16,
+ ETDM_RATE_22050,
+ ETDM_RATE_44100,
+ ETDM_RATE_88200,
+ ETDM_RATE_176400,
+ ETDM_RATE_352800,
+};
+
+enum {
+ ETDM_CONN_8K,
+ ETDM_CONN_11K,
+ ETDM_CONN_12K,
+ ETDM_CONN_16K = 4,
+ ETDM_CONN_22K,
+ ETDM_CONN_24K,
+ ETDM_CONN_32K = 8,
+ ETDM_CONN_44K,
+ ETDM_CONN_48K,
+ ETDM_CONN_88K = 13,
+ ETDM_CONN_96K,
+ ETDM_CONN_176K = 17,
+ ETDM_CONN_192K,
+ ETDM_CONN_352K = 21,
+ ETDM_CONN_384K,
+};
+
+enum {
+ ETDM_WLEN_8_BIT = 0x7,
+ ETDM_WLEN_16_BIT = 0xf,
+ ETDM_WLEN_32_BIT = 0x1f,
+};
+
+enum {
+ ETDM_SLAVE_SEL_ETDMIN0_MASTER,
+ ETDM_SLAVE_SEL_ETDMIN0_SLAVE,
+ ETDM_SLAVE_SEL_ETDMIN1_MASTER,
+ ETDM_SLAVE_SEL_ETDMIN1_SLAVE,
+ ETDM_SLAVE_SEL_ETDMIN2_MASTER,
+ ETDM_SLAVE_SEL_ETDMIN2_SLAVE,
+ ETDM_SLAVE_SEL_ETDMIN3_MASTER,
+ ETDM_SLAVE_SEL_ETDMIN3_SLAVE,
+ ETDM_SLAVE_SEL_ETDMOUT0_MASTER,
+ ETDM_SLAVE_SEL_ETDMOUT0_SLAVE,
+ ETDM_SLAVE_SEL_ETDMOUT1_MASTER,
+ ETDM_SLAVE_SEL_ETDMOUT1_SLAVE,
+ ETDM_SLAVE_SEL_ETDMOUT2_MASTER,
+ ETDM_SLAVE_SEL_ETDMOUT2_SLAVE,
+ ETDM_SLAVE_SEL_ETDMOUT3_MASTER,
+ ETDM_SLAVE_SEL_ETDMOUT3_SLAVE,
+};
+
+struct mtk_afe_i2s_priv {
+ int id;
+ int rate; /* for determine which apll to use */
+ int low_jitter_en;
+ unsigned int i2s_low_power_mask;
+ const char *share_property_name;
+ int share_i2s_id;
+
+ int mclk_id;
+ int mclk_rate;
+ int mclk_apll;
+
+ int ch_num;
+ int sync;
+ int ip_mode;
+ int slave_mode;
+ int lpbk_mode;
+};
+
+static unsigned int get_etdm_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ ETDM_WLEN_16_BIT : ETDM_WLEN_32_BIT;
+}
+
+static unsigned int get_etdm_lrck_width(snd_pcm_format_t format)
+{
+ if (snd_pcm_format_physical_width(format) <= 1)
+ return 0;
+
+ /* The valid data bit number should be larger than 7 due to hardware limitation. */
+ return snd_pcm_format_physical_width(format) - 1;
+}
+
+static unsigned int get_etdm_rate(unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return ETDM_RATE_8K;
+ case 12000:
+ return ETDM_RATE_12K;
+ case 16000:
+ return ETDM_RATE_16K;
+ case 24000:
+ return ETDM_RATE_24K;
+ case 32000:
+ return ETDM_RATE_32K;
+ case 48000:
+ return ETDM_RATE_48K;
+ case 64000:
+ return ETDM_RATE_64K;
+ case 96000:
+ return ETDM_RATE_96K;
+ case 128000:
+ return ETDM_RATE_128K;
+ case 192000:
+ return ETDM_RATE_192K;
+ case 256000:
+ return ETDM_RATE_256K;
+ case 384000:
+ return ETDM_RATE_384K;
+ case 11025:
+ return ETDM_RATE_11025;
+ case 22050:
+ return ETDM_RATE_22050;
+ case 44100:
+ return ETDM_RATE_44100;
+ case 88200:
+ return ETDM_RATE_88200;
+ case 176400:
+ return ETDM_RATE_176400;
+ case 352800:
+ return ETDM_RATE_352800;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_etdm_inconn_rate(unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return ETDM_CONN_8K;
+ case 12000:
+ return ETDM_CONN_12K;
+ case 16000:
+ return ETDM_CONN_16K;
+ case 24000:
+ return ETDM_CONN_24K;
+ case 32000:
+ return ETDM_CONN_32K;
+ case 48000:
+ return ETDM_CONN_48K;
+ case 96000:
+ return ETDM_CONN_96K;
+ case 192000:
+ return ETDM_CONN_192K;
+ case 384000:
+ return ETDM_CONN_384K;
+ case 11025:
+ return ETDM_CONN_11K;
+ case 22050:
+ return ETDM_CONN_22K;
+ case 44100:
+ return ETDM_CONN_44K;
+ case 88200:
+ return ETDM_CONN_88K;
+ case 176400:
+ return ETDM_CONN_176K;
+ case 352800:
+ return ETDM_CONN_352K;
+ default:
+ return 0;
+ }
+}
+
+static int get_i2s_id_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ if (strncmp(name, "I2SIN0", 6) == 0)
+ return MT8189_DAI_I2S_IN0;
+ else if (strncmp(name, "I2SIN1", 6) == 0)
+ return MT8189_DAI_I2S_IN1;
+ else if (strncmp(name, "I2SOUT0", 7) == 0)
+ return MT8189_DAI_I2S_OUT0;
+ else if (strncmp(name, "I2SOUT1", 7) == 0)
+ return MT8189_DAI_I2S_OUT1;
+ else if (strncmp(name, "I2SOUT4", 7) == 0)
+ return MT8189_DAI_I2S_OUT4;
+ else
+ return -EINVAL;
+}
+
+static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_i2s_id_by_name(afe, name);
+
+ if (dai_id < 0)
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+static const char * const etdm_0_3_loopback_texts[] = {
+ "etdmin0", "etdmin1", "etdmout0", "etdmout1"
+};
+
+static const u32 etdm_loopback_values[] = {
+ 0, 2, 8, 10
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(i2sin0_loopback_enum,
+ ETDM_0_3_COWORK_CON1,
+ ETDM_IN0_SDATA0_SEL_SFT,
+ ETDM_IN0_SDATA0_SEL_MASK,
+ etdm_0_3_loopback_texts,
+ etdm_loopback_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(i2sin1_loopback_enum,
+ ETDM_0_3_COWORK_CON1,
+ ETDM_IN1_SDATA0_SEL_SFT,
+ ETDM_IN1_SDATA0_SEL_MASK,
+ etdm_0_3_loopback_texts,
+ etdm_loopback_values);
+
+static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
+ SOC_ENUM("I2SIN0 Loopback", i2sin0_loopback_enum),
+ SOC_ENUM("I2SIN1 Loopback", i2sin1_loopback_enum),
+};
+
+/*
+ * I2S virtual mux to output widget
+ * If the I2S interface is required but not connected to an actual codec dai,
+ * a Dummy_Widget must be used to establish the connection.
+ */
+static const char *const i2s_mux_map[] = {
+ "Normal", "Dummy_Widget",
+};
+
+static int i2s_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ i2s_mux_map,
+ i2s_mux_map_value);
+
+static const struct snd_kcontrol_new i2s_in0_mux_control =
+ SOC_DAPM_ENUM("I2S IN0 Select", i2s_mux_map_enum);
+static const struct snd_kcontrol_new i2s_in1_mux_control =
+ SOC_DAPM_ENUM("I2S IN1 Select", i2s_mux_map_enum);
+static const struct snd_kcontrol_new i2s_out0_mux_control =
+ SOC_DAPM_ENUM("I2S OUT0 Select", i2s_mux_map_enum);
+static const struct snd_kcontrol_new i2s_out1_mux_control =
+ SOC_DAPM_ENUM("I2S OUT1 Select", i2s_mux_map_enum);
+static const struct snd_kcontrol_new i2s_out4_mux_control =
+ SOC_DAPM_ENUM("I2S OUT4 Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new mtk_i2sout0_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN108_1, I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN108_1, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN108_1, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN108_1, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN108_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN108_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN108_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN108_1, I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN108_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN108_1, I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH1", AFE_CONN108_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN108_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN108_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout0_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN109_1, I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN109_1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN109_1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN109_1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN109_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN109_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN109_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN109_1, I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN109_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN109_1, I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH2", AFE_CONN109_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN109_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN109_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN109_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN110_1, I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN110_1, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN110_1, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN110_1, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN110_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN110_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN110_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN110_1, I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN110_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN110_1, I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH1", AFE_CONN110_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN110_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN110_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN111_1, I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN111_1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN111_1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN111_1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN111_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN111_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN111_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN111_1, I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN111_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN111_1, I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH2", AFE_CONN111_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN111_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN111_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN111_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN116_1, I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN116_1, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN116_1, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN116_1, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN116_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN116_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN116_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH1", AFE_CONN116_1, I_DL7_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN116_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN116_1, I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL24_CH1", AFE_CONN116_2, I_DL24_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH1", AFE_CONN116_0,
+ I_GAIN0_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN116_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN116_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN116_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH1", AFE_CONN116_6,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN117_1, I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN117_1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN117_1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN117_1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN117_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN117_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN117_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL7_CH2", AFE_CONN117_1, I_DL7_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN117_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN117_1, I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL24_CH2", AFE_CONN117_2, I_DL24_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_GAIN0_OUT_CH2", AFE_CONN117_0,
+ I_GAIN0_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN117_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN117_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN117_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH2", AFE_CONN117_4,
+ I_PCM_0_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("HW_SRC_2_OUT_CH2", AFE_CONN117_6,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH3", AFE_CONN118_1, I_DL_24CH_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN118_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH4", AFE_CONN119_1, I_DL_24CH_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_0_CAP_CH1", AFE_CONN118_4,
+ I_PCM_0_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH5", AFE_CONN120_1, I_DL_24CH_CH5, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH6", AFE_CONN121_1, I_DL_24CH_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH7", AFE_CONN122_1, I_DL_24CH_CH7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2sout4_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH8", AFE_CONN123_1, I_DL_24CH_CH8, 1, 0),
+};
+
+static int mtk_apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (strcmp(w->name, APLL1_W_NAME) == 0)
+ mt8189_apll1_enable(afe);
+ else
+ mt8189_apll2_enable(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (strcmp(w->name, APLL1_W_NAME) == 0)
+ mt8189_apll1_disable(afe);
+ else
+ mt8189_apll2_disable(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ if (!i2s_priv)
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8189_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ i2s_priv->mclk_rate = 0;
+ mt8189_mck_disable(afe, i2s_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
+ SND_SOC_DAPM_MIXER("I2SOUT0_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout0_ch1_mix,
+ ARRAY_SIZE(mtk_i2sout0_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT0_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout0_ch2_mix,
+ ARRAY_SIZE(mtk_i2sout0_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2SOUT1_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout1_ch1_mix,
+ ARRAY_SIZE(mtk_i2sout1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT1_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout1_ch2_mix,
+ ARRAY_SIZE(mtk_i2sout1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch1_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch2_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch2_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH3", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch3_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch3_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch4_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch4_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH5", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch5_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch5_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH6", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch6_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch6_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH7", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch7_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch7_mix)),
+ SND_SOC_DAPM_MIXER("I2SOUT4_CH8", SND_SOC_NOPM, 0, 0,
+ mtk_i2sout4_ch8_mix,
+ ARRAY_SIZE(mtk_i2sout4_ch8_mix)),
+
+ /* i2s en*/
+ SND_SOC_DAPM_SUPPLY_S("I2SIN0_EN", SUPPLY_SEQ_I2S_EN,
+ ETDM_IN0_CON0, REG_ETDM_IN_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SIN1_EN", SUPPLY_SEQ_I2S_EN,
+ ETDM_IN1_CON0, REG_ETDM_IN_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SOUT0_EN", SUPPLY_SEQ_I2S_EN,
+ ETDM_OUT0_CON0, OUT_REG_ETDM_OUT_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SOUT1_EN", SUPPLY_SEQ_I2S_EN,
+ ETDM_OUT1_CON0, OUT_REG_ETDM_OUT_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SOUT4_EN", SUPPLY_SEQ_I2S_EN,
+ ETDM_OUT4_CON0, OUT_REG_ETDM_OUT_EN_SFT, 0,
+ NULL, 0),
+
+ /* i2s mclk en */
+ SND_SOC_DAPM_SUPPLY_S(I2SIN0_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2SIN1_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2SOUT0_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2SOUT1_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2SOUT4_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* cg */
+ SND_SOC_DAPM_SUPPLY_S("I2SOUT0_CG", SUPPLY_SEQ_I2S_CG_EN,
+ AUDIO_TOP_CON2, PDN_ETDM_OUT0_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SOUT1_CG", SUPPLY_SEQ_I2S_CG_EN,
+ AUDIO_TOP_CON2, PDN_ETDM_OUT1_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SOUT4_CG", SUPPLY_SEQ_I2S_CG_EN,
+ AUDIO_TOP_CON2, PDN_ETDM_OUT4_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SIN0_CG", SUPPLY_SEQ_I2S_CG_EN,
+ AUDIO_TOP_CON2, PDN_ETDM_IN0_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2SIN1_CG", SUPPLY_SEQ_I2S_CG_EN,
+ AUDIO_TOP_CON2, PDN_ETDM_IN1_SFT, 1,
+ NULL, 0),
+
+ /* apll */
+ SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* allow i2s on without codec on */
+ SND_SOC_DAPM_OUTPUT("I2S_DUMMY_OUT"),
+ SND_SOC_DAPM_MUX("I2S_OUT0_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s_out0_mux_control),
+ SND_SOC_DAPM_MUX("I2S_OUT1_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s_out1_mux_control),
+ SND_SOC_DAPM_MUX("I2S_OUT4_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s_out4_mux_control),
+
+ SND_SOC_DAPM_INPUT("I2S_DUMMY_IN"),
+ SND_SOC_DAPM_MUX("I2S_IN0_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s_in0_mux_control),
+ SND_SOC_DAPM_MUX("I2S_IN1_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s_in1_mux_control),
+};
+
+static int mtk_afe_i2s_share_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(sink->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (!i2s_priv)
+ return 0;
+
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ return i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name);
+}
+
+static int mtk_afe_i2s_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(sink->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+ int needed_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (!i2s_priv)
+ return 0;
+
+ /* which apll */
+ cur_apll = mt8189_get_apll_by_name(afe, source->name);
+
+ /* choose APLL from i2s rate */
+ needed_apll = mt8189_get_apll_by_rate(afe, i2s_priv->rate);
+
+ return needed_apll == cur_apll;
+}
+
+static int mtk_afe_i2s_mclk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(sink->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int i2s_num;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (!i2s_priv)
+ return 0;
+
+ i2s_num = get_i2s_id_by_name(afe, source->name);
+ if (get_i2s_id_by_name(afe, sink->name) == i2s_num)
+ return i2s_priv->mclk_rate > 0;
+
+ /* check if share i2s need mclk */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == i2s_num)
+ return i2s_priv->mclk_rate > 0;
+
+ return 0;
+}
+
+static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ if (!i2s_priv)
+ return 0;
+
+ /* which apll */
+ cur_apll = mt8189_get_apll_by_name(afe, source->name);
+
+ return i2s_priv->mclk_apll == cur_apll;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
+ /* I2SIN0 */
+ {"I2SIN0", NULL, "I2SIN0_EN"},
+ {"I2SIN0", NULL, "I2SIN1_EN", mtk_afe_i2s_share_connect},
+ {"I2SIN0", NULL, "I2SOUT0_EN", mtk_afe_i2s_share_connect},
+ {"I2SIN0", NULL, "I2SOUT1_EN", mtk_afe_i2s_share_connect},
+ {"I2SIN0", NULL, "I2SOUT4_EN", mtk_afe_i2s_share_connect},
+
+ {"I2SIN0", NULL, I2SIN0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN0", NULL, I2SIN1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN0", NULL, I2SOUT0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN0", NULL, I2SOUT1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN0", NULL, I2SOUT4_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2SIN0_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2SIN0_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+ {"I2SIN0", NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SIN0", NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SIN0", NULL, "I2SOUT0_CG"},
+ {"I2SIN0", NULL, "I2SIN0_CG"},
+
+ /* i2sin1 */
+ {"I2SIN1", NULL, "I2SIN0_EN", mtk_afe_i2s_share_connect},
+ {"I2SIN1", NULL, "I2SIN1_EN"},
+ {"I2SIN1", NULL, "I2SOUT0_EN", mtk_afe_i2s_share_connect},
+ {"I2SIN1", NULL, "I2SOUT1_EN", mtk_afe_i2s_share_connect},
+ {"I2SIN1", NULL, "I2SOUT4_EN", mtk_afe_i2s_share_connect},
+
+ {"I2SIN1", NULL, I2SIN0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN1", NULL, I2SIN1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN1", NULL, I2SOUT0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN1", NULL, I2SOUT1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SIN1", NULL, I2SOUT4_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2SIN1_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2SIN1_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+ {"I2SIN1", NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SIN1", NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SIN1", NULL, "I2SIN1_CG"},
+ {"I2SIN1", NULL, "I2SOUT1_CG"},
+
+ /* i2sout0 */
+ {"I2SOUT0_CH1", "DL0_CH1", "DL0"},
+ {"I2SOUT0_CH2", "DL0_CH2", "DL0"},
+ {"I2SOUT0_CH1", "DL1_CH1", "DL1"},
+ {"I2SOUT0_CH2", "DL1_CH2", "DL1"},
+ {"I2SOUT0_CH1", "DL2_CH1", "DL2"},
+ {"I2SOUT0_CH2", "DL2_CH2", "DL2"},
+ {"I2SOUT0_CH1", "DL3_CH1", "DL3"},
+ {"I2SOUT0_CH2", "DL3_CH2", "DL3"},
+ {"I2SOUT0_CH1", "DL4_CH1", "DL4"},
+ {"I2SOUT0_CH2", "DL4_CH2", "DL4"},
+ {"I2SOUT0_CH1", "DL5_CH1", "DL5"},
+ {"I2SOUT0_CH2", "DL5_CH2", "DL5"},
+ {"I2SOUT0_CH1", "DL6_CH1", "DL6"},
+ {"I2SOUT0_CH2", "DL6_CH2", "DL6"},
+ {"I2SOUT0_CH1", "DL7_CH1", "DL7"},
+ {"I2SOUT0_CH2", "DL7_CH2", "DL7"},
+ {"I2SOUT0_CH1", "DL8_CH1", "DL8"},
+ {"I2SOUT0_CH2", "DL8_CH2", "DL8"},
+ {"I2SOUT0_CH1", "DL_24CH_CH1", "DL_24CH"},
+ {"I2SOUT0_CH2", "DL_24CH_CH2", "DL_24CH"},
+
+ {"I2SOUT0", NULL, "I2SOUT0_CH1"},
+ {"I2SOUT0", NULL, "I2SOUT0_CH2"},
+
+ {"I2SOUT0", NULL, "I2SIN0_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT0", NULL, "I2SIN1_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT0", NULL, "I2SOUT0_EN"},
+ {"I2SOUT0", NULL, "I2SOUT1_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT0", NULL, "I2SOUT4_EN", mtk_afe_i2s_share_connect},
+
+ {"I2SOUT0", NULL, I2SIN0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT0", NULL, I2SIN1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT0", NULL, I2SOUT0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT0", NULL, I2SOUT1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT0", NULL, I2SOUT4_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2SOUT0_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2SOUT0_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+ {"I2SOUT0", NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SOUT0", NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SOUT0", NULL, "I2SOUT0_CG"},
+ {"I2SOUT0", NULL, "I2SIN0_CG"},
+
+ /* i2sout1 */
+ {"I2SOUT1_CH1", "DL0_CH1", "DL0"},
+ {"I2SOUT1_CH2", "DL0_CH2", "DL0"},
+ {"I2SOUT1_CH1", "DL1_CH1", "DL1"},
+ {"I2SOUT1_CH2", "DL1_CH2", "DL1"},
+ {"I2SOUT1_CH1", "DL2_CH1", "DL2"},
+ {"I2SOUT1_CH2", "DL2_CH2", "DL2"},
+ {"I2SOUT1_CH1", "DL3_CH1", "DL3"},
+ {"I2SOUT1_CH2", "DL3_CH2", "DL3"},
+ {"I2SOUT1_CH1", "DL4_CH1", "DL4"},
+ {"I2SOUT1_CH2", "DL4_CH2", "DL4"},
+ {"I2SOUT1_CH1", "DL5_CH1", "DL5"},
+ {"I2SOUT1_CH2", "DL5_CH2", "DL5"},
+ {"I2SOUT1_CH1", "DL6_CH1", "DL6"},
+ {"I2SOUT1_CH2", "DL6_CH2", "DL6"},
+ {"I2SOUT1_CH1", "DL7_CH1", "DL7"},
+ {"I2SOUT1_CH2", "DL7_CH2", "DL7"},
+ {"I2SOUT1_CH1", "DL8_CH1", "DL8"},
+ {"I2SOUT1_CH2", "DL8_CH2", "DL8"},
+ {"I2SOUT1_CH1", "DL_24CH_CH1", "DL_24CH"},
+ {"I2SOUT1_CH2", "DL_24CH_CH2", "DL_24CH"},
+
+ {"I2SOUT1", NULL, "I2SOUT1_CH1"},
+ {"I2SOUT1", NULL, "I2SOUT1_CH2"},
+
+ {"I2SOUT1", NULL, "I2SIN0_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT1", NULL, "I2SIN1_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT1", NULL, "I2SOUT0_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT1", NULL, "I2SOUT1_EN"},
+ {"I2SOUT1", NULL, "I2SOUT4_EN", mtk_afe_i2s_share_connect},
+
+ {"I2SOUT1", NULL, I2SIN0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT1", NULL, I2SIN1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT1", NULL, I2SOUT0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT1", NULL, I2SOUT1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT1", NULL, I2SOUT4_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2SOUT1_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2SOUT1_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+ {"I2SOUT1", NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SOUT1", NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SOUT1", NULL, "I2SOUT1_CG"},
+ {"I2SOUT1", NULL, "I2SIN1_CG"},
+
+ /* i2sout4 */
+ {"I2SOUT4_CH1", "DL0_CH1", "DL0"},
+ {"I2SOUT4_CH2", "DL0_CH2", "DL0"},
+ {"I2SOUT4_CH1", "DL1_CH1", "DL1"},
+ {"I2SOUT4_CH2", "DL1_CH2", "DL1"},
+ {"I2SOUT4_CH1", "DL2_CH1", "DL2"},
+ {"I2SOUT4_CH2", "DL2_CH2", "DL2"},
+ {"I2SOUT4_CH1", "DL3_CH1", "DL3"},
+ {"I2SOUT4_CH2", "DL3_CH2", "DL3"},
+ {"I2SOUT4_CH1", "DL4_CH1", "DL4"},
+ {"I2SOUT4_CH2", "DL4_CH2", "DL4"},
+ {"I2SOUT4_CH1", "DL5_CH1", "DL5"},
+ {"I2SOUT4_CH2", "DL5_CH2", "DL5"},
+ {"I2SOUT4_CH1", "DL6_CH1", "DL6"},
+ {"I2SOUT4_CH2", "DL6_CH2", "DL6"},
+ {"I2SOUT4_CH1", "DL7_CH1", "DL7"},
+ {"I2SOUT4_CH2", "DL7_CH2", "DL7"},
+ {"I2SOUT4_CH1", "DL8_CH1", "DL8"},
+ {"I2SOUT4_CH2", "DL8_CH2", "DL8"},
+ {"I2SOUT4_CH1", "DL_24CH_CH1", "DL_24CH"},
+ {"I2SOUT4_CH2", "DL_24CH_CH2", "DL_24CH"},
+ {"I2SOUT4_CH3", "DL_24CH_CH3", "DL_24CH"},
+ {"I2SOUT4_CH4", "DL_24CH_CH4", "DL_24CH"},
+ {"I2SOUT4_CH5", "DL_24CH_CH5", "DL_24CH"},
+ {"I2SOUT4_CH6", "DL_24CH_CH6", "DL_24CH"},
+ {"I2SOUT4_CH7", "DL_24CH_CH7", "DL_24CH"},
+ {"I2SOUT4_CH8", "DL_24CH_CH8", "DL_24CH"},
+ {"I2SOUT4_CH1", "DL24_CH1", "DL24"},
+ {"I2SOUT4_CH2", "DL24_CH2", "DL24"},
+
+ {"I2SOUT4", NULL, "I2SOUT4_CH1"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH2"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH3"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH4"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH5"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH6"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH7"},
+ {"I2SOUT4", NULL, "I2SOUT4_CH8"},
+
+ {"I2SOUT4", NULL, "I2SIN0_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT4", NULL, "I2SIN1_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT4", NULL, "I2SOUT0_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT4", NULL, "I2SOUT1_EN", mtk_afe_i2s_share_connect},
+ {"I2SOUT4", NULL, "I2SOUT4_EN"},
+
+ {"I2SOUT4", NULL, I2SIN0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT4", NULL, I2SIN1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT4", NULL, I2SOUT0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT4", NULL, I2SOUT1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2SOUT4", NULL, I2SOUT4_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2SOUT4_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2SOUT4_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+ {"I2SOUT4", NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {"I2SOUT4", NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+ /* CG */
+ {"I2SOUT4", NULL, "I2SOUT4_CG"},
+
+ /* allow i2s on without codec on */
+ {"I2SIN0", NULL, "I2S_IN0_Mux"},
+ {"I2S_IN0_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
+
+ {"I2SIN1", NULL, "I2S_IN1_Mux"},
+ {"I2S_IN1_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
+
+ {"I2S_OUT0_Mux", "Dummy_Widget", "I2SOUT0"},
+ {"I2S_DUMMY_OUT", NULL, "I2S_OUT0_Mux"},
+
+ {"I2S_OUT1_Mux", "Dummy_Widget", "I2SOUT1"},
+ {"I2S_DUMMY_OUT", NULL, "I2S_OUT1_Mux"},
+
+ {"I2S_OUT4_Mux", "Dummy_Widget", "I2SOUT4"},
+ {"I2S_DUMMY_OUT", NULL, "I2S_OUT4_Mux"},
+};
+
+/* i2s dai ops*/
+static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
+ struct snd_pcm_hw_params *params,
+ int i2s_id)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv;
+ unsigned int rate = params_rate(params);
+ snd_pcm_format_t format = params_format(params);
+ int ret;
+
+ if (i2s_id >= MT8189_DAI_NUM || i2s_id < 0)
+ return -EINVAL;
+
+ i2s_priv = afe_priv->dai_priv[i2s_id];
+ if (!i2s_priv)
+ return -EINVAL;
+
+ i2s_priv->rate = rate;
+
+ dev_dbg(afe->dev, "%s(), id %d, rate %d, format %d\n",
+ __func__, i2s_id, rate, format);
+
+ switch (i2s_id) {
+ case MT8189_DAI_I2S_IN0:
+ /* ---etdm in --- */
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON1,
+ REG_INITIAL_COUNT_MASK_SFT,
+ 0x5 << REG_INITIAL_COUNT_SFT);
+ /* 3: pad top 5: no pad top */
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON1,
+ REG_INITIAL_POINT_MASK_SFT,
+ 0x5 << REG_INITIAL_POINT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON1,
+ REG_LRCK_RESET_MASK_SFT,
+ 0x1 << REG_LRCK_RESET_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON2,
+ REG_CLOCK_SOURCE_SEL_MASK_SFT,
+ ETDM_CLK_SOURCE_APLL <<
+ REG_CLOCK_SOURCE_SEL_SFT);
+ /* 0: manual 1: auto */
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON2,
+ REG_CK_EN_SEL_AUTO_MASK_SFT,
+ 0x1 << REG_CK_EN_SEL_AUTO_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON3,
+ REG_FS_TIMING_SEL_MASK_SFT,
+ get_etdm_rate(rate) <<
+ REG_FS_TIMING_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON4,
+ REG_RELATCH_1X_EN_SEL_MASK_SFT,
+ get_etdm_inconn_rate(rate) <<
+ REG_RELATCH_1X_EN_SEL_SFT);
+
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON8,
+ REG_ETDM_USE_AFIFO_MASK_SFT,
+ 0x0 << REG_ETDM_USE_AFIFO_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON8,
+ REG_AFIFO_MODE_MASK_SFT,
+ 0x0 << REG_AFIFO_MODE_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON9,
+ REG_ALMOST_END_CH_COUNT_MASK_SFT,
+ 0x0 << REG_ALMOST_END_CH_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON9,
+ REG_ALMOST_END_BIT_COUNT_MASK_SFT,
+ 0x0 << REG_ALMOST_END_BIT_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON9,
+ REG_OUT2LATCH_TIME_MASK_SFT,
+ 0x6 << REG_OUT2LATCH_TIME_SFT);
+
+ /* 5: TDM Mode */
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON0,
+ REG_FMT_MASK_SFT, 0x0 << REG_FMT_SFT);
+
+ /* APLL */
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON0,
+ REG_RELATCH_1X_EN_DOMAIN_SEL_MASK_SFT,
+ ETDM_RELATCH_SEL_APLL
+ << REG_RELATCH_1X_EN_DOMAIN_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON0,
+ REG_BIT_LENGTH_MASK_SFT,
+ get_etdm_lrck_width(format) <<
+ REG_BIT_LENGTH_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN0_CON0,
+ REG_WORD_LENGTH_MASK_SFT,
+ get_etdm_wlen(format) <<
+ REG_WORD_LENGTH_SFT);
+
+ /* ---etdm cowork --- */
+ regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON0,
+ ETDM_IN0_SLAVE_SEL_MASK_SFT,
+ ETDM_SLAVE_SEL_ETDMOUT0_MASTER
+ << ETDM_IN0_SLAVE_SEL_SFT);
+ break;
+ case MT8189_DAI_I2S_IN1:
+ /* ---etdm in --- */
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON1,
+ REG_INITIAL_COUNT_MASK_SFT,
+ 0x5 << REG_INITIAL_COUNT_SFT);
+ /* 3: pad top 5: no pad top */
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON1,
+ REG_INITIAL_POINT_MASK_SFT,
+ 0x5 << REG_INITIAL_POINT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON1,
+ REG_LRCK_RESET_MASK_SFT,
+ 0x1 << REG_LRCK_RESET_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON2,
+ REG_CLOCK_SOURCE_SEL_MASK_SFT,
+ ETDM_CLK_SOURCE_APLL <<
+ REG_CLOCK_SOURCE_SEL_SFT);
+ /* 0: manual 1: auto */
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON2,
+ REG_CK_EN_SEL_AUTO_MASK_SFT,
+ 0x1 << REG_CK_EN_SEL_AUTO_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON3,
+ REG_FS_TIMING_SEL_MASK_SFT,
+ get_etdm_rate(rate) <<
+ REG_FS_TIMING_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON4,
+ REG_RELATCH_1X_EN_SEL_MASK_SFT,
+ get_etdm_inconn_rate(rate) <<
+ REG_RELATCH_1X_EN_SEL_SFT);
+
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON8,
+ REG_ETDM_USE_AFIFO_MASK_SFT,
+ 0x0 << REG_ETDM_USE_AFIFO_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON8,
+ REG_AFIFO_MODE_MASK_SFT,
+ 0x0 << REG_AFIFO_MODE_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON9,
+ REG_ALMOST_END_CH_COUNT_MASK_SFT,
+ 0x0 << REG_ALMOST_END_CH_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON9,
+ REG_ALMOST_END_BIT_COUNT_MASK_SFT,
+ 0x0 << REG_ALMOST_END_BIT_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON9,
+ REG_OUT2LATCH_TIME_MASK_SFT,
+ 0x6 << REG_OUT2LATCH_TIME_SFT);
+
+ /* 5: TDM Mode */
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
+ REG_FMT_MASK_SFT, 0x0 << REG_FMT_SFT);
+
+ /* APLL */
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
+ REG_RELATCH_1X_EN_DOMAIN_SEL_MASK_SFT,
+ ETDM_RELATCH_SEL_APLL
+ << REG_RELATCH_1X_EN_DOMAIN_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
+ REG_BIT_LENGTH_MASK_SFT,
+ get_etdm_lrck_width(format) <<
+ REG_BIT_LENGTH_SFT);
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
+ REG_WORD_LENGTH_MASK_SFT,
+ get_etdm_wlen(format) <<
+ REG_WORD_LENGTH_SFT);
+
+ /* ---etdm cowork --- */
+ regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON1,
+ ETDM_IN1_SLAVE_SEL_MASK_SFT,
+ ETDM_SLAVE_SEL_ETDMOUT1_MASTER
+ << ETDM_IN1_SLAVE_SEL_SFT);
+ break;
+ case MT8189_DAI_I2S_OUT0:
+ /* ---etdm out --- */
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON1,
+ OUT_REG_INITIAL_COUNT_MASK_SFT,
+ 0x5 << OUT_REG_INITIAL_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON1,
+ OUT_REG_INITIAL_POINT_MASK_SFT,
+ 0x6 << OUT_REG_INITIAL_POINT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON1,
+ OUT_REG_LRCK_RESET_MASK_SFT,
+ 0x1 << OUT_REG_LRCK_RESET_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON4,
+ OUT_REG_FS_TIMING_SEL_MASK_SFT,
+ get_etdm_rate(rate) <<
+ OUT_REG_FS_TIMING_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON4,
+ OUT_REG_CLOCK_SOURCE_SEL_MASK_SFT,
+ ETDM_CLK_SOURCE_APLL <<
+ OUT_REG_CLOCK_SOURCE_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON4,
+ OUT_REG_RELATCH_EN_SEL_MASK_SFT,
+ get_etdm_inconn_rate(rate) <<
+ OUT_REG_RELATCH_EN_SEL_SFT);
+ /* 5: TDM Mode */
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON0,
+ OUT_REG_FMT_MASK_SFT,
+ 0x0 << OUT_REG_FMT_SFT);
+
+ /* APLL */
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON0,
+ OUT_REG_RELATCH_DOMAIN_SEL_MASK_SFT,
+ ETDM_RELATCH_SEL_APLL
+ << OUT_REG_RELATCH_DOMAIN_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON0,
+ OUT_REG_BIT_LENGTH_MASK_SFT,
+ get_etdm_lrck_width(format) <<
+ OUT_REG_BIT_LENGTH_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT0_CON0,
+ OUT_REG_WORD_LENGTH_MASK_SFT,
+ get_etdm_wlen(format) <<
+ OUT_REG_WORD_LENGTH_SFT);
+
+ /* ---etdm cowork --- */
+ regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON0,
+ ETDM_OUT0_SLAVE_SEL_MASK_SFT,
+ ETDM_SLAVE_SEL_ETDMIN0_MASTER
+ << ETDM_OUT0_SLAVE_SEL_SFT);
+ break;
+ case MT8189_DAI_I2S_OUT1:
+ /* ---etdm out --- */
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON1,
+ OUT_REG_INITIAL_COUNT_MASK_SFT,
+ 0x5 << OUT_REG_INITIAL_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON1,
+ OUT_REG_INITIAL_POINT_MASK_SFT,
+ 0x6 << OUT_REG_INITIAL_POINT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON1,
+ OUT_REG_LRCK_RESET_MASK_SFT,
+ 0x1 << OUT_REG_LRCK_RESET_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON4,
+ OUT_REG_FS_TIMING_SEL_MASK_SFT,
+ get_etdm_rate(rate) <<
+ OUT_REG_FS_TIMING_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON4,
+ OUT_REG_CLOCK_SOURCE_SEL_MASK_SFT,
+ ETDM_CLK_SOURCE_APLL <<
+ OUT_REG_CLOCK_SOURCE_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON4,
+ OUT_REG_RELATCH_EN_SEL_MASK_SFT,
+ get_etdm_inconn_rate(rate) <<
+ OUT_REG_RELATCH_EN_SEL_SFT);
+ /* 5: TDM Mode */
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
+ OUT_REG_FMT_MASK_SFT,
+ 0x0 << OUT_REG_FMT_SFT);
+
+ /* APLL */
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
+ OUT_REG_RELATCH_DOMAIN_SEL_MASK_SFT,
+ ETDM_RELATCH_SEL_APLL
+ << OUT_REG_RELATCH_DOMAIN_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
+ OUT_REG_BIT_LENGTH_MASK_SFT,
+ get_etdm_lrck_width(format) <<
+ OUT_REG_BIT_LENGTH_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
+ OUT_REG_WORD_LENGTH_MASK_SFT,
+ get_etdm_wlen(format) <<
+ OUT_REG_WORD_LENGTH_SFT);
+
+ /* ---etdm cowork --- */
+ regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON0,
+ ETDM_OUT1_SLAVE_SEL_MASK_SFT,
+ ETDM_SLAVE_SEL_ETDMIN1_MASTER
+ << ETDM_OUT1_SLAVE_SEL_SFT);
+ break;
+ case MT8189_DAI_I2S_OUT4:
+ /* ---etdm out --- */
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON1,
+ OUT_REG_INITIAL_COUNT_MASK_SFT,
+ 0x5 << OUT_REG_INITIAL_COUNT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON1,
+ OUT_REG_INITIAL_POINT_MASK_SFT,
+ 0x6 << OUT_REG_INITIAL_POINT_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON1,
+ OUT_REG_LRCK_RESET_MASK_SFT,
+ 0x1 << OUT_REG_LRCK_RESET_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON4,
+ OUT_REG_FS_TIMING_SEL_MASK_SFT,
+ get_etdm_rate(rate) <<
+ OUT_REG_FS_TIMING_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON4,
+ OUT_REG_CLOCK_SOURCE_SEL_MASK_SFT,
+ ETDM_CLK_SOURCE_APLL <<
+ OUT_REG_CLOCK_SOURCE_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON4,
+ OUT_REG_RELATCH_EN_SEL_MASK_SFT,
+ get_etdm_inconn_rate(rate) <<
+ OUT_REG_RELATCH_EN_SEL_SFT);
+ /* 5: TDM Mode */
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON0,
+ OUT_REG_FMT_MASK_SFT,
+ 0x0 << OUT_REG_FMT_SFT);
+
+ /* APLL */
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON0,
+ OUT_REG_RELATCH_DOMAIN_SEL_MASK_SFT,
+ ETDM_RELATCH_SEL_APLL
+ << OUT_REG_RELATCH_DOMAIN_SEL_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON0,
+ OUT_REG_BIT_LENGTH_MASK_SFT,
+ get_etdm_lrck_width(format) <<
+ OUT_REG_BIT_LENGTH_SFT);
+ regmap_update_bits(afe->regmap, ETDM_OUT4_CON0,
+ OUT_REG_WORD_LENGTH_MASK_SFT,
+ get_etdm_wlen(format) <<
+ OUT_REG_WORD_LENGTH_SFT);
+ break;
+ default:
+ dev_err(afe->dev, "%s(), id %d not support\n",
+ __func__, i2s_id);
+ return -EINVAL;
+ }
+
+ /* set share i2s */
+ if (i2s_priv->share_i2s_id >= 0) {
+ ret = mtk_dai_i2s_config(afe, params, i2s_priv->share_i2s_id);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ return mtk_dai_i2s_config(afe, params, dai->id);
+}
+
+static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int apll;
+ int apll_rate;
+
+ if (dai->id >= MT8189_DAI_NUM || dai->id < 0 ||
+ dir != SND_SOC_CLOCK_OUT)
+ return -EINVAL;
+
+ i2s_priv = afe_priv->dai_priv[dai->id];
+ if (!i2s_priv)
+ return -EINVAL;
+
+ dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ apll = mt8189_get_apll_by_rate(afe, freq);
+ apll_rate = mt8189_get_apll_rate(afe, apll);
+
+ if (freq > apll_rate || apll_rate % freq) {
+ dev_err(afe->dev, "%s(), freq %d, apll_rate %d\n",
+ __func__, freq, apll_rate);
+ return -EINVAL;
+ }
+
+ i2s_priv->mclk_rate = freq;
+ i2s_priv->mclk_apll = apll;
+
+ if (i2s_priv->share_i2s_id > 0) {
+ struct mtk_afe_i2s_priv *share_i2s_priv;
+
+ share_i2s_priv = afe_priv->dai_priv[i2s_priv->share_i2s_id];
+ if (!share_i2s_priv)
+ return -EINVAL;
+
+ share_i2s_priv->mclk_rate = i2s_priv->mclk_rate;
+ share_i2s_priv->mclk_apll = i2s_priv->mclk_apll;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_i2s_ops = {
+ .hw_params = mtk_dai_i2s_hw_params,
+ .set_sysclk = mtk_dai_i2s_set_sysclk,
+};
+
+/* dai driver */
+#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_192000)
+#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S8 |\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define MT8189_I2S_DAI(_name, _id, max_ch, dir) \
+{ \
+ .name = #_name, \
+ .id = _id, \
+ .dir = { \
+ .stream_name = #_name, \
+ .channels_min = 1, \
+ .channels_max = max_ch, \
+ .rates = MTK_ETDM_RATES, \
+ .formats = MTK_ETDM_FORMATS, \
+ }, \
+ .ops = &mtk_dai_i2s_ops, \
+}
+
+static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
+ /* capture */
+ MT8189_I2S_DAI(I2SIN0, MT8189_DAI_I2S_IN0, 2, capture),
+ MT8189_I2S_DAI(I2SIN1, MT8189_DAI_I2S_IN1, 2, capture),
+ /* playback */
+ MT8189_I2S_DAI(I2SOUT0, MT8189_DAI_I2S_OUT0, 2, playback),
+ MT8189_I2S_DAI(I2SOUT1, MT8189_DAI_I2S_OUT1, 2, playback),
+ MT8189_I2S_DAI(I2SOUT4, MT8189_DAI_I2S_OUT4, 8, playback),
+};
+
+static const struct mtk_afe_i2s_priv mt8189_i2s_priv[DAI_I2S_NUM] = {
+ [DAI_I2SIN0] = {
+ .id = MT8189_DAI_I2S_IN0,
+ .mclk_id = MT8189_I2SIN0_MCK,
+ .share_property_name = "i2sin0-share",
+ .share_i2s_id = MT8189_DAI_I2S_OUT0,
+ },
+ [DAI_I2SIN1] = {
+ .id = MT8189_DAI_I2S_IN1,
+ .mclk_id = MT8189_I2SIN1_MCK,
+ .share_property_name = "i2sin1-share",
+ .share_i2s_id = MT8189_DAI_I2S_OUT1,
+ },
+ [DAI_I2SOUT0] = {
+ .id = MT8189_DAI_I2S_OUT0,
+ .mclk_id = MT8189_I2SOUT0_MCK,
+ .share_property_name = "i2sout0-share",
+ .share_i2s_id = -1,
+ },
+ [DAI_I2SOUT1] = {
+ .id = MT8189_DAI_I2S_OUT1,
+ .mclk_id = MT8189_I2SOUT1_MCK,
+ .share_property_name = "i2sout1-share",
+ .share_i2s_id = -1,
+ },
+ [DAI_I2SOUT4] = {
+ .id = MT8189_DAI_I2S_OUT4,
+ .mclk_id = MT8189_I2SIN1_MCK,
+ .share_property_name = "i2sout4-share",
+ .share_i2s_id = -1,
+ },
+};
+
+static int mt8189_dai_i2s_get_share(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ const struct device_node *of_node = afe->dev->of_node;
+
+ for (int i = 0; i < DAI_I2S_NUM; i++) {
+ const char *of_str;
+ struct mtk_afe_i2s_priv *i2s_priv =
+ afe_priv->dai_priv[mt8189_i2s_priv[i].id];
+ const char *property_name =
+ mt8189_i2s_priv[i].share_property_name;
+
+ if (of_property_read_string(of_node, property_name, &of_str))
+ continue;
+
+ i2s_priv->share_i2s_id = get_i2s_id_by_name(afe, of_str);
+ }
+
+ return 0;
+}
+
+static int init_i2s_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ for (int i = 0; i < DAI_I2S_NUM; i++) {
+ int id = mt8189_i2s_priv[i].id;
+ size_t size = sizeof(struct mtk_afe_i2s_priv);
+
+ if (id >= MT8189_DAI_NUM || id < 0)
+ return -EINVAL;
+
+ i2s_priv = devm_kzalloc(afe->dev, size, GFP_KERNEL);
+ if (!i2s_priv)
+ return -ENOMEM;
+
+ memcpy(i2s_priv, &mt8189_i2s_priv[i], size);
+
+ afe_priv->dai_priv[id] = i2s_priv;
+ }
+
+ return 0;
+}
+
+int mt8189_dai_i2s_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ int ret;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ dai->dai_drivers = mtk_dai_i2s_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
+
+ dai->controls = mtk_dai_i2s_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls);
+ dai->dapm_widgets = mtk_dai_i2s_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
+ dai->dapm_routes = mtk_dai_i2s_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
+
+ /* set all dai i2s private data */
+ ret = init_i2s_priv_data(afe);
+ if (ret)
+ return ret;
+
+ /* parse share i2s */
+ ret = mt8189_dai_i2s_get_share(afe);
+ if (ret)
+ return ret;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-pcm.c b/sound/soc/mediatek/mt8189/mt8189-dai-pcm.c
new file mode 100644
index 000000000000..21317c0413e5
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-dai-pcm.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI I2S Control
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+
+#include "mt8189-afe-common.h"
+#include "mt8189-interconnection.h"
+#include "mt8189-afe-clk.h"
+
+enum AUD_TX_LCH_RPT {
+ AUD_TX_LCH_RPT_NO_REPEAT,
+ AUD_TX_LCH_RPT_REPEAT
+};
+
+enum AUD_VBT_16K_MODE {
+ AUD_VBT_16K_MODE_DISABLE,
+ AUD_VBT_16K_MODE_ENABLE
+};
+
+enum AUD_EXT_MODEM {
+ AUD_EXT_MODEM_SELECT_INTERNAL,
+ AUD_EXT_MODEM_SELECT_EXTERNAL
+};
+
+enum AUD_PCM_SYNC_TYPE {
+ /* bck sync length = 1 */
+ AUD_PCM_ONE_BCK_CYCLE_SYNC,
+ /* bck sync length = PCM_INTF_CON1[9:13] */
+ AUD_PCM_EXTENDED_BCK_CYCLE_SYNC
+};
+
+enum AUD_BT_MODE {
+ AUD_BT_MODE_DUAL_MIC_ON_TX,
+ AUD_BT_MODE_SINGLE_MIC_ON_TX
+};
+
+enum AUD_PCM_AFIFO_SRC {
+ /* slave mode & external modem uses different crystal */
+ AUD_PCM_AFIFO_ASRC,
+ /* slave mode & external modem uses the same crystal */
+ AUD_PCM_AFIFO_AFIFO
+};
+
+enum AUD_PCM_CLOCK_SOURCE {
+ AUD_PCM_CLOCK_MASTER_MODE,
+ AUD_PCM_CLOCK_SLAVE_MODE
+};
+
+enum AUD_PCM_WLEN {
+ AUD_PCM_WLEN_PCM_32_BCK_CYCLES,
+ AUD_PCM_WLEN_PCM_64_BCK_CYCLES
+};
+
+enum AUD_PCM_MODE {
+ AUD_PCM_MODE_PCM_MODE_8K,
+ AUD_PCM_MODE_PCM_MODE_16K,
+ AUD_PCM_MODE_PCM_MODE_32K,
+ AUD_PCM_MODE_PCM_MODE_48K
+};
+
+enum AUD_PCM_FMT {
+ AUD_PCM_FMT_I2S,
+ AUD_PCM_FMT_EIAJ,
+ AUD_PCM_FMT_PCM_MODE_A,
+ AUD_PCM_FMT_PCM_MODE_B
+};
+
+enum AUD_BCLK_OUT_INV {
+ AUD_BCLK_OUT_INV_NO_INVERSE,
+ AUD_BCLK_OUT_INV_INVERSE
+};
+
+enum AUD_PCM_EN {
+ AUD_PCM_EN_DISABLE,
+ AUD_PCM_EN_ENABLE
+};
+
+enum AUD_PCM1_1X_EN_DOMAIN {
+ HOPPING_26M,
+ APLL,
+ SLAVE = 6
+};
+
+enum AUD_PCM1_1X_EN_SLAVE_MODE {
+ PCM0_SLAVE_1X_EN,
+ PCM1_SLAVE_1X_EN
+};
+
+enum {
+ PCM_8K,
+ PCM_16K = 4,
+ PCM_32K = 8,
+ PCM_48K = 10
+};
+
+static unsigned int pcm_1x_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return PCM_8K;
+ case 16000:
+ return PCM_16K;
+ case 32000:
+ return PCM_32K;
+ case 48000:
+ return PCM_48K;
+ default:
+ dev_warn(dev, "rate %u invalid, use %d!!!\n",
+ rate, PCM_48K);
+ return PCM_48K;
+ }
+}
+
+static unsigned int pcm_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_PCM_RATE_8K;
+ case 16000:
+ return MTK_AFE_PCM_RATE_16K;
+ case 32000:
+ return MTK_AFE_PCM_RATE_32K;
+ case 48000:
+ return MTK_AFE_PCM_RATE_48K;
+ default:
+ dev_warn(dev, "rate %u invalid, use %d\n",
+ rate, MTK_AFE_PCM_RATE_48K);
+ return MTK_AFE_PCM_RATE_48K;
+ }
+}
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_pcm_0_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN096_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN096_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN096_1,
+ I_DL_24CH_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_0_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN097_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN097_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN097_1,
+ I_DL_24CH_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_0_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH1", AFE_CONN099_4,
+ I_I2SIN1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH2", AFE_CONN099_4,
+ I_I2SIN1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN099_1,
+ I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN099_1,
+ I_DL_24CH_CH1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("PCM_0_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_0_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_0_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_0_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_0_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_0_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_0_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_0_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_0_playback_ch4_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_0_EN",
+ AFE_PCM0_INTF_CON0, PCM0_EN_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PCM0_CG", AUDIO_TOP_CON0, PDN_PCM0_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_INPUT("AFE_PCM_INPUT"),
+ SND_SOC_DAPM_OUTPUT("AFE_PCM_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"PCM 0 Playback", NULL, "PCM_0_PB_CH1"},
+ {"PCM 0 Playback", NULL, "PCM_0_PB_CH2"},
+ {"PCM 0 Playback", NULL, "PCM_0_PB_CH4"},
+
+ {"PCM 0 Playback", NULL, "PCM_0_EN"},
+ {"PCM 0 Capture", NULL, "PCM_0_EN"},
+ {"PCM 0 Playback", NULL, "PCM0_CG"},
+ {"PCM 0 Capture", NULL, "PCM0_CG"},
+
+ {"AFE_PCM_OUTPUT", NULL, "PCM 0 Playback"},
+ {"PCM 0 Capture", NULL, "AFE_PCM_INPUT"},
+
+ {"PCM_0_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_0_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_0_PB_CH4", "DL0_CH1", "DL0"},
+
+ {"PCM_0_PB_CH1", "DL_24CH_CH1", "DL_24CH"},
+ {"PCM_0_PB_CH2", "DL_24CH_CH2", "DL_24CH"},
+ {"PCM_0_PB_CH4", "DL_24CH_CH1", "DL_24CH"},
+};
+
+/* dai ops */
+static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = pcm_rate_transform(afe->dev, rate);
+ unsigned int x_rate_reg = pcm_1x_rate_transform(afe->dev, rate);
+ unsigned int pcm_con0;
+ unsigned int pcm_con1;
+ unsigned int playback_active = 0;
+ unsigned int capture_active = 0;
+ struct snd_soc_dapm_widget *playback_widget =
+ snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_PLAYBACK);
+ struct snd_soc_dapm_widget *capture_widget =
+ snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_CAPTURE);
+
+ if (playback_widget)
+ playback_active = playback_widget->active;
+ if (capture_widget)
+ capture_active = capture_widget->active;
+ dev_dbg(afe->dev,
+ "id %d, stream %d, rate %d, rate_reg %d, active p %d, c %d\n",
+ dai->id, substream->stream, rate, rate_reg,
+ playback_active, capture_active);
+
+ if (playback_active || capture_active)
+ return 0;
+ switch (dai->id) {
+ case MT8189_DAI_PCM_0:
+ pcm_con0 = AUD_BCLK_OUT_INV_NO_INVERSE << PCM0_BCLK_OUT_INV_SFT;
+ pcm_con0 |= AUD_TX_LCH_RPT_NO_REPEAT << PCM0_TX_LCH_RPT_SFT;
+ pcm_con0 |= AUD_VBT_16K_MODE_DISABLE << PCM0_VBT_16K_MODE_SFT;
+ pcm_con0 |= 0 << PCM0_SYNC_LENGTH_SFT;
+ pcm_con0 |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM0_SYNC_TYPE_SFT;
+ pcm_con0 |= AUD_PCM_AFIFO_AFIFO << PCM0_BYP_ASRC_SFT;
+ pcm_con0 |= AUD_PCM_CLOCK_MASTER_MODE << PCM0_SLAVE_SFT;
+ pcm_con0 |= rate_reg << PCM0_MODE_SFT;
+ pcm_con0 |= AUD_PCM_FMT_I2S << PCM0_FMT_SFT;
+
+ pcm_con1 = AUD_EXT_MODEM_SELECT_INTERNAL << PCM0_EXT_MODEM_SFT;
+ pcm_con1 |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM0_BT_MODE_SFT;
+ pcm_con1 |= HOPPING_26M << PCM0_1X_EN_DOMAIN_SFT;
+ pcm_con1 |= x_rate_reg << PCM0_1X_EN_MODE_SFT;
+
+ regmap_update_bits(afe->regmap, AFE_PCM0_INTF_CON0,
+ ~(unsigned int)PCM0_EN_MASK_SFT, pcm_con0);
+ regmap_update_bits(afe->regmap, AFE_PCM0_INTF_CON1,
+ AFE_PCM0_INTF_CON1_MASK_MON_MASK_SFT,
+ pcm_con1);
+ break;
+ default:
+ dev_err(afe->dev, "%s(), id %d not support\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .hw_params = mtk_dai_pcm_hw_params,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM 0",
+ .id = MT8189_DAI_PCM_0,
+ .playback = {
+ .stream_name = "PCM 0 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 0 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int mt8189_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c b/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c
new file mode 100644
index 000000000000..5d68a55ccc45
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI TDM Control
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+
+#include "mt8189-afe-clk.h"
+#include "mt8189-afe-common.h"
+#include "mt8189-interconnection.h"
+
+#define DPTX_CH_EN_MASK_2CH (0x3)
+#define DPTX_CH_EN_MASK_4CH (0xf)
+#define DPTX_CH_EN_MASK_6CH (0x3f)
+#define DPTX_CH_EN_MASK_8CH (0xff)
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_TDM_MCK_EN,
+ SUPPLY_SEQ_TDM_BCK_EN,
+ SUPPLY_SEQ_TDM_DPTX_MCK_EN,
+ SUPPLY_SEQ_TDM_DPTX_BCK_EN,
+ SUPPLY_SEQ_TDM_CG_EN,
+};
+
+enum {
+ TDM_WLEN_8_BIT,
+ TDM_WLEN_16_BIT,
+ TDM_WLEN_24_BIT,
+ TDM_WLEN_32_BIT,
+};
+
+enum {
+ TDM_CHANNEL_BCK_16,
+ TDM_CHANNEL_BCK_24,
+ TDM_CHANNEL_BCK_32
+};
+
+enum {
+ TDM_CHANNEL_NUM_2,
+ TDM_CHANNEL_NUM_4,
+ TDM_CHANNEL_NUM_8
+};
+
+enum {
+ TDM_CH_START_O30_O31,
+ TDM_CH_START_O32_O33,
+ TDM_CH_START_O34_O35,
+ TDM_CH_START_O36_O37,
+ TDM_CH_ZERO,
+};
+
+enum {
+ DPTX_CHANNEL_2,
+ DPTX_CHANNEL_8,
+};
+
+enum {
+ DPTX_WLEN_24_BIT,
+ DPTX_WLEN_16_BIT,
+};
+
+struct mtk_afe_tdm_priv {
+ int bck_id;
+ int bck_rate;
+
+ int mclk_id;
+ int mclk_multiple; /* according to sample rate */
+ int mclk_rate;
+ int mclk_apll;
+};
+
+static unsigned int get_tdm_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
+}
+
+static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
+}
+
+static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) - 1;
+}
+
+static unsigned int get_tdm_ch(unsigned int ch)
+{
+ switch (ch) {
+ case 1:
+ case 2:
+ return TDM_CHANNEL_NUM_2;
+ case 3:
+ case 4:
+ return TDM_CHANNEL_NUM_4;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ default:
+ return TDM_CHANNEL_NUM_8;
+ }
+}
+
+static unsigned int get_dptx_ch_enable_mask(unsigned int ch)
+{
+ switch (ch) {
+ case 1:
+ case 2:
+ return DPTX_CH_EN_MASK_2CH;
+ case 3:
+ case 4:
+ return DPTX_CH_EN_MASK_4CH;
+ case 5:
+ case 6:
+ return DPTX_CH_EN_MASK_6CH;
+ case 7:
+ case 8:
+ return DPTX_CH_EN_MASK_8CH;
+ default:
+ return DPTX_CH_EN_MASK_2CH;
+ }
+}
+
+static unsigned int get_dptx_ch(unsigned int ch)
+{
+ if (ch == 2)
+ return DPTX_CHANNEL_2;
+
+ return DPTX_CHANNEL_8;
+}
+
+static unsigned int get_dptx_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ DPTX_WLEN_16_BIT : DPTX_WLEN_24_BIT;
+}
+
+/* interconnection */
+enum {
+ HDMI_CONN_CH0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static const char *const hdmi_conn_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7",
+};
+
+static int hdmi_conn_mux_map_value[] = {
+ HDMI_CONN_CH0, HDMI_CONN_CH1, HDMI_CONN_CH2, HDMI_CONN_CH3,
+ HDMI_CONN_CH4, HDMI_CONN_CH5, HDMI_CONN_CH6, HDMI_CONN_CH7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_0_SFT,
+ HDMI_O_0_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_1_SFT,
+ HDMI_O_1_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_2_SFT,
+ HDMI_O_2_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_3_SFT,
+ HDMI_O_3_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_4_SFT,
+ HDMI_O_4_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_5_SFT,
+ HDMI_O_5_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_6_SFT,
+ HDMI_O_6_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_7_SFT,
+ HDMI_O_7_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new mtk_dai_tdm_controls[] = {
+ SOC_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum),
+ SOC_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum),
+ SOC_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum),
+ SOC_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum),
+ SOC_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum),
+ SOC_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum),
+ SOC_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum),
+ SOC_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum),
+};
+
+static const char *const tdm_out_demux_texts[] = {
+ "NONE", "TDMOUT", "DPTXOUT",
+};
+
+static SOC_ENUM_SINGLE_DECL(tdm_out_demux_enum,
+ SND_SOC_NOPM,
+ 0,
+ tdm_out_demux_texts);
+
+static const struct snd_kcontrol_new tdm_out_demux_control =
+ SOC_DAPM_ENUM("TDM Playback Route", tdm_out_demux_enum);
+
+static int get_tdm_id_by_name(const char *name)
+{
+ if (strstr(name, "DPTX"))
+ return MT8189_DAI_TDM_DPTX;
+
+ return MT8189_DAI_TDM;
+}
+
+static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d, bck: %d\n",
+ w->name, event, dai_id, tdm_priv->bck_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8189_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8189_mck_disable(afe, tdm_priv->bck_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d, mclk %d\n",
+ w->name, event, dai_id, tdm_priv->mclk_rate);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8189_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tdm_priv->mclk_rate = 0;
+ mt8189_mck_disable(afe, tdm_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
+ SND_SOC_DAPM_DEMUX("TDM Playback Route", SND_SOC_NOPM, 0, 0,
+ &tdm_out_demux_control),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_bck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_BCK", SUPPLY_SEQ_TDM_DPTX_BCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_bck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_MCK", SUPPLY_SEQ_TDM_DPTX_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_CG", SUPPLY_SEQ_TDM_CG_EN,
+ AUDIO_TOP_CON2, PDN_TDM_OUT_SFT, 1,
+ NULL, 0),
+};
+
+static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(sink->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(sink->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int cur_apll;
+
+ /* which apll */
+ cur_apll = mt8189_get_apll_by_name(afe, source->name);
+
+ return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
+ {"TDM Playback Route", NULL, "HDMI"},
+
+ {"TDM", "TDMOUT", "TDM Playback Route"},
+ {"TDM", NULL, "TDM_BCK"},
+ {"TDM", NULL, "TDM_CG"},
+
+ {"TDM_DPTX", "DPTXOUT", "TDM Playback Route"},
+ {"TDM_DPTX", NULL, "TDM_DPTX_BCK"},
+ {"TDM_DPTX", NULL, "TDM_CG"},
+
+ {"TDM_BCK", NULL, "TDM_MCK"},
+ {"TDM_DPTX_BCK", NULL, "TDM_DPTX_MCK"},
+ {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_DPTX_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_DPTX_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+};
+
+/* dai ops */
+static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
+ struct mtk_afe_tdm_priv *tdm_priv,
+ int freq)
+{
+ int apll;
+ int apll_rate;
+
+ apll = mt8189_get_apll_by_rate(afe, freq);
+ apll_rate = mt8189_get_apll_rate(afe, apll);
+
+ if (freq > apll_rate)
+ return -EINVAL;
+
+ if (apll_rate % freq != 0)
+ return -EINVAL;
+
+ tdm_priv->mclk_rate = freq;
+ tdm_priv->mclk_apll = apll;
+
+ return 0;
+}
+
+static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int tdm_id = dai->id;
+ struct mtk_afe_tdm_priv *tdm_priv;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int tdm_con;
+
+ if (tdm_id >= MT8189_DAI_NUM || tdm_id < 0)
+ return -EINVAL;
+
+ tdm_priv = afe_priv->dai_priv[tdm_id];
+
+ /* calculate mclk_rate, if not set explicitly */
+ if (!tdm_priv->mclk_rate) {
+ tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
+ mtk_dai_tdm_cal_mclk(afe,
+ tdm_priv,
+ tdm_priv->mclk_rate);
+ }
+
+ /* calculate bck */
+ tdm_priv->bck_rate = rate *
+ channels *
+ snd_pcm_format_physical_width(format);
+
+ if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
+ return -EINVAL;
+
+ if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
+ return -EINVAL;
+
+ dev_dbg(afe->dev, "id %d, rate %d, ch %d, fmt %d, mclk %d, bck %d\n",
+ tdm_id, rate, channels, format,
+ tdm_priv->mclk_rate, tdm_priv->bck_rate);
+
+ /* set tdm */
+ tdm_con = 1 << LEFT_ALIGN_SFT;
+ tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
+ tdm_con |= get_tdm_ch(channels) << CHANNEL_NUM_SFT;
+ tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT;
+ tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
+ regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
+
+ /* set dptx */
+ if (tdm_id == MT8189_DAI_TDM_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_CHANNEL_ENABLE_MASK_SFT,
+ get_dptx_ch_enable_mask(channels) <<
+ DPTX_CHANNEL_ENABLE_SFT);
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_CHANNEL_NUMBER_MASK_SFT,
+ get_dptx_ch(channels) <<
+ DPTX_CHANNEL_NUMBER_SFT);
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_16BIT_MASK_SFT,
+ get_dptx_wlen(format) << DPTX_16BIT_SFT);
+ }
+ switch (channels) {
+ case 1:
+ case 2:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 3:
+ case 4:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 5:
+ case 6:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 7:
+ case 8:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ default:
+ tdm_con = 0;
+ }
+ regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_CH_NUM_MASK_SFT,
+ channels << HDMI_CH_NUM_SFT);
+
+ return 0;
+}
+
+static int mtk_dai_tdm_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int tdm_id = dai->id;
+
+ dev_dbg(afe->dev, "%s(), cmd %d, tdm_id %d\n", __func__, cmd, tdm_id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* enable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_OUT_ON_MASK_SFT,
+ 0x1 << HDMI_OUT_ON_SFT);
+
+ /* enable dptx */
+ if (tdm_id == MT8189_DAI_TDM_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_ON_MASK_SFT, 0x1 <<
+ DPTX_ON_SFT);
+ }
+
+ /* enable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1,
+ TDM_EN_MASK_SFT, 0x1 << TDM_EN_SFT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1,
+ TDM_EN_MASK_SFT, 0);
+
+ /* disable dptx */
+ if (tdm_id == MT8189_DAI_TDM_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_ON_MASK_SFT, 0);
+ }
+
+ /* disable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_OUT_ON_MASK_SFT, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv;
+
+ if (dai->id >= MT8189_DAI_NUM || dai->id < 0)
+ return -EINVAL;
+
+ tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (!tdm_priv)
+ return -EINVAL;
+
+ if (dir != SND_SOC_CLOCK_OUT)
+ return -EINVAL;
+
+ dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
+}
+
+static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
+ .hw_params = mtk_dai_tdm_hw_params,
+ .trigger = mtk_dai_tdm_trigger,
+ .set_sysclk = mtk_dai_tdm_set_sysclk,
+};
+
+/* dai driver */
+#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
+ {
+ .name = "TDM",
+ .id = MT8189_DAI_TDM,
+ .playback = {
+ .stream_name = "TDM",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+ {
+ .name = "TDM_DPTX",
+ .id = MT8189_DAI_TDM_DPTX,
+ .playback = {
+ .stream_name = "TDM_DPTX",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+};
+
+static struct mtk_afe_tdm_priv *init_tdm_priv_data(struct mtk_base_afe *afe,
+ int id)
+{
+ struct mtk_afe_tdm_priv *tdm_priv;
+
+ tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
+ GFP_KERNEL);
+ if (!tdm_priv)
+ return NULL;
+
+ if (id == MT8189_DAI_TDM_DPTX)
+ tdm_priv->mclk_multiple = 256;
+ else
+ tdm_priv->mclk_multiple = 128;
+
+ tdm_priv->bck_id = MT8189_TDMOUT_BCK;
+ tdm_priv->mclk_id = MT8189_TDMOUT_MCK;
+
+ return tdm_priv;
+}
+
+int mt8189_dai_tdm_register(struct mtk_base_afe *afe)
+{
+ struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv, *tdm_dptx_priv;
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ dai->dai_drivers = mtk_dai_tdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
+ dai->controls = mtk_dai_tdm_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_tdm_controls);
+ dai->dapm_widgets = mtk_dai_tdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
+ dai->dapm_routes = mtk_dai_tdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
+
+ tdm_priv = init_tdm_priv_data(afe, MT8189_DAI_TDM);
+ if (!tdm_priv)
+ return -ENOMEM;
+
+ tdm_dptx_priv = init_tdm_priv_data(afe, MT8189_DAI_TDM_DPTX);
+ if (!tdm_dptx_priv)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ afe_priv->dai_priv[MT8189_DAI_TDM] = tdm_priv;
+ afe_priv->dai_priv[MT8189_DAI_TDM_DPTX] = tdm_dptx_priv;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8189/mt8189-interconnection.h b/sound/soc/mediatek/mt8189/mt8189-interconnection.h
new file mode 100644
index 000000000000..a244a2599fa2
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-interconnection.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Mediatek MT8189 audio driver interconnection definition
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#ifndef _MT8189_INTERCONNECTION_H_
+#define _MT8189_INTERCONNECTION_H_
+
+/* in port define */
+#define I_CONNSYS_I2S_CH1 0
+#define I_CONNSYS_I2S_CH2 1
+#define I_GAIN0_OUT_CH1 6
+#define I_GAIN0_OUT_CH2 7
+#define I_GAIN1_OUT_CH1 8
+#define I_GAIN1_OUT_CH2 9
+#define I_GAIN2_OUT_CH1 10
+#define I_GAIN2_OUT_CH2 11
+#define I_GAIN3_OUT_CH1 12
+#define I_GAIN3_OUT_CH2 13
+#define I_STF_CH1 14
+#define I_ADDA_UL_CH1 16
+#define I_ADDA_UL_CH2 17
+#define I_ADDA_UL_CH3 18
+#define I_ADDA_UL_CH4 19
+#define I_UL_PROX_CH1 20
+#define I_UL_PROX_CH2 21
+#define I_ADDA_UL_CH5 24
+#define I_ADDA_UL_CH6 25
+#define I_DMIC0_CH1 28
+#define I_DMIC0_CH2 29
+#define I_DMIC1_CH1 30
+#define I_DMIC1_CH2 31
+
+/* in port define >= 32 */
+#define I_32_OFFSET 32
+#define I_DL0_CH1 (32 - I_32_OFFSET)
+#define I_DL0_CH2 (33 - I_32_OFFSET)
+#define I_DL1_CH1 (34 - I_32_OFFSET)
+#define I_DL1_CH2 (35 - I_32_OFFSET)
+#define I_DL2_CH1 (36 - I_32_OFFSET)
+#define I_DL2_CH2 (37 - I_32_OFFSET)
+#define I_DL3_CH1 (38 - I_32_OFFSET)
+#define I_DL3_CH2 (39 - I_32_OFFSET)
+#define I_DL4_CH1 (40 - I_32_OFFSET)
+#define I_DL4_CH2 (41 - I_32_OFFSET)
+#define I_DL5_CH1 (42 - I_32_OFFSET)
+#define I_DL5_CH2 (43 - I_32_OFFSET)
+#define I_DL6_CH1 (44 - I_32_OFFSET)
+#define I_DL6_CH2 (45 - I_32_OFFSET)
+#define I_DL7_CH1 (46 - I_32_OFFSET)
+#define I_DL7_CH2 (47 - I_32_OFFSET)
+#define I_DL8_CH1 (48 - I_32_OFFSET)
+#define I_DL8_CH2 (49 - I_32_OFFSET)
+#define I_DL_24CH_CH1 (54 - I_32_OFFSET)
+#define I_DL_24CH_CH2 (55 - I_32_OFFSET)
+#define I_DL_24CH_CH3 (56 - I_32_OFFSET)
+#define I_DL_24CH_CH4 (57 - I_32_OFFSET)
+#define I_DL_24CH_CH5 (58 - I_32_OFFSET)
+#define I_DL_24CH_CH6 (59 - I_32_OFFSET)
+#define I_DL_24CH_CH7 (60 - I_32_OFFSET)
+#define I_DL_24CH_CH8 (61 - I_32_OFFSET)
+
+/* in port define >= 64 */
+#define I_64_OFFSET 64
+#define I_DL23_CH1 (78 - I_64_OFFSET)
+#define I_DL23_CH2 (79 - I_64_OFFSET)
+#define I_DL24_CH1 (80 - I_64_OFFSET)
+#define I_DL24_CH2 (81 - I_64_OFFSET)
+#define I_DL25_CH1 (82 - I_64_OFFSET)
+#define I_DL25_CH2 (83 - I_64_OFFSET)
+
+/* in port define >= 128 */
+#define I_128_OFFSET 128
+#define I_PCM_0_CAP_CH1 (130 - I_128_OFFSET)
+#define I_PCM_0_CAP_CH2 (131 - I_128_OFFSET)
+#define I_I2SIN0_CH1 (134 - I_128_OFFSET)
+#define I_I2SIN0_CH2 (135 - I_128_OFFSET)
+#define I_I2SIN1_CH1 (136 - I_128_OFFSET)
+#define I_I2SIN1_CH2 (137 - I_128_OFFSET)
+
+/* in port define >= 192 */
+#define I_192_OFFSET 192
+#define I_SRC_0_OUT_CH1 (198 - I_192_OFFSET)
+#define I_SRC_0_OUT_CH2 (199 - I_192_OFFSET)
+#define I_SRC_1_OUT_CH1 (200 - I_192_OFFSET)
+#define I_SRC_1_OUT_CH2 (201 - I_192_OFFSET)
+#define I_SRC_2_OUT_CH1 (202 - I_192_OFFSET)
+#define I_SRC_2_OUT_CH2 (203 - I_192_OFFSET)
+#define I_SRC_3_OUT_CH1 (204 - I_192_OFFSET)
+#define I_SRC_3_OUT_CH2 (205 - I_192_OFFSET)
+#define I_SRC_4_OUT_CH1 (206 - I_192_OFFSET)
+#define I_SRC_4_OUT_CH2 (207 - I_192_OFFSET)
+
+#endif
diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c
new file mode 100644
index 000000000000..5ef15ec988be
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c
@@ -0,0 +1,1178 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8189-nau8825.c -- mt8189 nau8825 ALSA SoC machine driver
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+
+#include "mt8189-afe-common.h"
+
+#include "../common/mtk-soc-card.h"
+#include "../common/mtk-soundcard-driver.h"
+#include "../common/mtk-afe-platform-driver.h"
+
+#include "../../codecs/cs35l41.h"
+#include "../../codecs/nau8825.h"
+#include "../../codecs/rt5682s.h"
+#include "../../codecs/rt5682.h"
+
+#define NAU8825_HS_PRESENT BIT(0)
+#define RT5682S_HS_PRESENT BIT(1)
+#define RT5650_HS_PRESENT BIT(2)
+#define RT5682I_HS_PRESENT BIT(3)
+#define ES8326_HS_PRESENT BIT(4)
+
+/*
+ * Nau88l25
+ */
+#define NAU8825_CODEC_DAI "nau8825-hifi"
+
+/*
+ * Rt5682s
+ */
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+
+/*
+ * Rt5650
+ */
+#define RT5650_CODEC_DAI "rt5645-aif1"
+
+/*
+ * Rt5682i
+ */
+#define RT5682I_CODEC_DAI "rt5682-aif1"
+
+/*
+ * Cs35l41
+ */
+#define CS35L41_CODEC_DAI "cs35l41-pcm"
+#define CS35L41_DEV0_NAME "cs35l41.7-0040"
+#define CS35L41_DEV1_NAME "cs35l41.7-0042"
+
+/*
+ * ES8326
+ */
+#define ES8326_CODEC_DAI "ES8326 HiFi"
+
+enum mt8189_jacks {
+ MT8189_JACK_HEADSET,
+ MT8189_JACK_DP,
+ MT8189_JACK_HDMI,
+ MT8189_JACK_MAX,
+};
+
+static struct snd_soc_jack_pin mt8189_dp_jack_pins[] = {
+ {
+ .pin = "DP",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static struct snd_soc_jack_pin mt8189_hdmi_jack_pins[] = {
+ {
+ .pin = "HDMI",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static struct snd_soc_jack_pin mt8189_headset_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new mt8189_dumb_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8189_dumb_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_widget mt8189_headset_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_kcontrol_new mt8189_headset_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget mt8189_nau8825_card_widgets[] = {
+ SND_SOC_DAPM_SINK("DP"),
+};
+
+static int mt8189_common_i2s_startup(struct snd_pcm_substream *substream)
+{
+ static const unsigned int rates[] = {
+ 48000,
+ };
+ static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ };
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+}
+
+static int mt8189_common_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(cpu_dai,
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8189_common_i2s_ops = {
+ .startup = mt8189_common_i2s_startup,
+ .hw_params = mt8189_common_i2s_hw_params,
+};
+
+static int mt8189_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8189_dptx_ops = {
+ .hw_params = mt8189_dptx_hw_params,
+};
+
+static int mt8189_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__);
+
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8189_pcm_ops = {
+ .startup = mt8189_common_i2s_startup,
+};
+
+static int mt8189_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ int clk_freq, ret;
+
+ clk_freq = rate * 2 * bit_width;
+ dev_dbg(codec_dai->dev, "clk_freq %d, rate: %d, bit_width: %d\n",
+ clk_freq, rate, bit_width);
+
+ /* Configure clock for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
+ return ret;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
+ rate * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8189_nau8825_ops = {
+ .startup = mt8189_common_i2s_startup,
+ .hw_params = mt8189_nau8825_hw_params,
+};
+
+static int mt8189_rtxxxx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 1, rate * 32, rate * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1, rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 512,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8189_rtxxxx_i2s_ops = {
+ .startup = mt8189_common_i2s_startup,
+ .hw_params = mt8189_rtxxxx_i2s_hw_params,
+};
+
+static int mt8189_cs35l41_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs = rate * 128;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ int clk_freq = rate * 32;
+ int rx_slot[] = {0, 1};
+ int i, ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_component_set_sysclk(codec_dai->component,
+ CS35L41_CLKID_SCLK, 0,
+ clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "set component sysclk fail: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, CS35L41_CLKID_SCLK,
+ clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "set sysclk fail: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_channel_map(codec_dai, 0, NULL,
+ 1, &rx_slot[i]);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "set channel map fail: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai,
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8189_cs35l41_i2s_ops = {
+ .startup = mt8189_common_i2s_startup,
+ .hw_params = mt8189_cs35l41_i2s_hw_params,
+};
+
+static int mt8189_es8326_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ /* Configure MCLK for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set MCLK %d\n", ret);
+ return ret;
+ }
+
+ /* Configure MCLK for cpu */
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8189_es8326_ops = {
+ .startup = mt8189_common_i2s_startup,
+ .hw_params = mt8189_es8326_hw_params,
+};
+
+static int mt8189_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_dumb_spk_widgets,
+ ARRAY_SIZE(mt8189_dumb_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8189_dumb_spk_controls,
+ ARRAY_SIZE(mt8189_dumb_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8189_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8189_JACK_DP];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT,
+ jack, mt8189_dp_jack_pins,
+ ARRAY_SIZE(mt8189_dp_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8189_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8189_JACK_HDMI];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+ jack, mt8189_hdmi_jack_pins,
+ ARRAY_SIZE(mt8189_hdmi_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8189_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8189_JACK_HEADSET];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ int ret;
+ int type;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_headset_widgets,
+ ARRAY_SIZE(mt8189_headset_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8189_headset_controls,
+ ARRAY_SIZE(mt8189_headset_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ mt8189_headset_jack_pins,
+ ARRAY_SIZE(mt8189_headset_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ if (card_data->flags & ES8326_HS_PRESENT) {
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+ } else {
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ }
+
+ type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ ret = snd_soc_component_set_jack(component, jack, (void *)&type);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static void mt8189_headset_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback0,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback4,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback5,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback6,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback7,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback8,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback23,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL23")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback24,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL24")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback25,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL25")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback_24ch,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL_24CH")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture0,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture4,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture5,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture6,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture7,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture8,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture9,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture10,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture24,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL24")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture25,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL25")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture_cm0,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_CM0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture_cm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_CM1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture_etdm_in0,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_ETDM_IN0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture_etdm_in1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_ETDM_IN1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback_hdmi,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+/* BE */
+SND_SOC_DAILINK_DEFS(ap_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(ap_dmic_ch34,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC_CH34")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sin0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SIN0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sin1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SIN1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sout0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SOUT0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sout1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SOUT1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(pcm0,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(tdm_dptx,
+ DAILINK_COMP_ARRAY(COMP_CPU("TDM_DPTX")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt8189_nau8825_dai_links[] = {
+ /* Front End DAI links */
+ {
+ .name = "DL0_FE",
+ .stream_name = "DL0 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(playback0),
+ },
+ {
+ .name = "DL1_FE",
+ .stream_name = "DL1 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ {
+ .name = "UL0_FE",
+ .stream_name = "UL0 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(capture0),
+ },
+ {
+ .name = "UL1_FE",
+ .stream_name = "UL1 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ {
+ .name = "UL2_FE",
+ .stream_name = "UL2 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_format = 1,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ {
+ .name = "HDMI_FE",
+ .stream_name = "HDMI Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_hdmi),
+ },
+ {
+ .name = "DL2_FE",
+ .stream_name = "DL2 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ {
+ .name = "DL3_FE",
+ .stream_name = "DL3 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ {
+ .name = "DL4_FE",
+ .stream_name = "DL4 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback4),
+ },
+ {
+ .name = "DL5_FE",
+ .stream_name = "DL5 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback5),
+ },
+ {
+ .name = "DL6_FE",
+ .stream_name = "DL6 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback6),
+ },
+ {
+ .name = "DL7_FE",
+ .stream_name = "DL7 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback7),
+ },
+ {
+ .name = "DL8 FE",
+ .stream_name = "DL8 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback8),
+ },
+ {
+ .name = "DL23 FE",
+ .stream_name = "DL23 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback23),
+ },
+ {
+ .name = "DL24 FE",
+ .stream_name = "DL24 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback24),
+ },
+ {
+ .name = "DL25 FE",
+ .stream_name = "DL25 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback25),
+ },
+ {
+ .name = "DL_24CH_FE",
+ .stream_name = "DL_24CH Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_24ch),
+ },
+ {
+ .name = "UL9_FE",
+ .stream_name = "UL9 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture9),
+ },
+ {
+ .name = "UL3_FE",
+ .stream_name = "UL3 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ {
+ .name = "UL7_FE",
+ .stream_name = "UL7 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture7),
+ },
+ {
+ .name = "UL4_FE",
+ .stream_name = "UL4 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture4),
+ },
+ {
+ .name = "UL5_FE",
+ .stream_name = "UL5 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture5),
+ },
+ {
+ .name = "UL_CM0_FE",
+ .stream_name = "UL_CM0 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_cm0),
+ },
+ {
+ .name = "UL_CM1_FE",
+ .stream_name = "UL_CM1 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_cm1),
+ },
+ {
+ .name = "UL10_FE",
+ .stream_name = "UL10 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture10),
+ },
+ {
+ .name = "UL6_FE",
+ .stream_name = "UL6 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture6),
+ },
+ {
+ .name = "UL25_FE",
+ .stream_name = "UL25 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture25),
+ },
+ {
+ .name = "UL8_FE",
+ .stream_name = "UL8 Capture_Mono_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture8),
+ },
+ {
+ .name = "UL24_FE",
+ .stream_name = "UL24 Capture_Mono_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture24),
+ },
+ {
+ .name = "UL_ETDM_In0_FE",
+ .stream_name = "UL_ETDM_In0 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_etdm_in0),
+ },
+ {
+ .name = "UL_ETDM_In1_FE",
+ .stream_name = "UL_ETDM_In1 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_etdm_in1),
+ },
+ /* Back End DAI links */
+ {
+ .name = "I2SIN0_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8189_common_i2s_ops,
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(i2sin0),
+ },
+ {
+ .name = "I2SIN1_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8189_common_i2s_ops,
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(i2sin1),
+ },
+ {
+ .name = "I2SOUT0_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8189_common_i2s_ops,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(i2sout0),
+ },
+ {
+ .name = "I2SOUT1_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8189_common_i2s_ops,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(i2sout1),
+ },
+ {
+ .name = "AP_DMIC_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic),
+ },
+ {
+ .name = "AP_DMIC_CH34_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic_ch34),
+ },
+ {
+ .name = "TDM_DPTX_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8189_dptx_ops,
+ .be_hw_params_fixup = mt8189_dptx_hw_params_fixup,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(tdm_dptx),
+ },
+ {
+ .name = "PCM_0_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .no_pcm = 1,
+ .ops = &mt8189_pcm_ops,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm0),
+ },
+};
+
+static struct snd_soc_codec_conf mt8189_cs35l41_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(CS35L41_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(CS35L41_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static int mt8189_nau8825_soc_card_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct snd_soc_card *card = soc_card_data->card_data->card;
+ struct snd_soc_dai_link *dai_link;
+ bool init_nau8825 = false;
+ bool init_rt5682s = false;
+ bool init_rt5650 = false;
+ bool init_rt5682i = false;
+ bool init_es8326 = false;
+ bool init_dumb = false;
+ int i;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "TDM_DPTX_BE") == 0) {
+ if (dai_link->num_codecs &&
+ strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ dai_link->init = mt8189_dptx_codec_init;
+ } else if (strcmp(dai_link->name, "PCM_0_BE") == 0) {
+ if (dai_link->num_codecs &&
+ strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ dai_link->init = mt8189_hdmi_codec_init;
+ } else if (strcmp(dai_link->name, "I2SOUT0_BE") == 0 ||
+ strcmp(dai_link->name, "I2SIN0_BE") == 0) {
+ if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) {
+ dai_link->ops = &mt8189_nau8825_ops;
+ if (!init_nau8825) {
+ dai_link->init = mt8189_headset_codec_init;
+ dai_link->exit = mt8189_headset_codec_exit;
+ init_nau8825 = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5682S_CODEC_DAI)) {
+ dai_link->ops = &mt8189_rtxxxx_i2s_ops;
+ if (!init_rt5682s) {
+ dai_link->init = mt8189_headset_codec_init;
+ dai_link->exit = mt8189_headset_codec_exit;
+ init_rt5682s = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5650_CODEC_DAI)) {
+ dai_link->ops = &mt8189_rtxxxx_i2s_ops;
+ if (!init_rt5650) {
+ dai_link->init = mt8189_headset_codec_init;
+ dai_link->exit = mt8189_headset_codec_exit;
+ init_rt5650 = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5682I_CODEC_DAI)) {
+ dai_link->ops = &mt8189_rtxxxx_i2s_ops;
+ if (!init_rt5682i) {
+ dai_link->init = mt8189_headset_codec_init;
+ dai_link->exit = mt8189_headset_codec_exit;
+ init_rt5682i = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, ES8326_CODEC_DAI)) {
+ dai_link->ops = &mt8189_es8326_ops;
+ if (!init_es8326) {
+ dai_link->init = mt8189_headset_codec_init;
+ dai_link->exit = mt8189_headset_codec_exit;
+ init_es8326 = true;
+ }
+ } else {
+ if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
+ if (!init_dumb) {
+ dai_link->init = mt8189_dumb_amp_init;
+ init_dumb = true;
+ }
+ }
+ }
+ } else if (strcmp(dai_link->name, "I2SOUT1_BE") == 0) {
+ if (!strcmp(dai_link->codecs->dai_name, CS35L41_CODEC_DAI)) {
+ dai_link->ops = &mt8189_cs35l41_i2s_ops;
+ card->num_configs = ARRAY_SIZE(mt8189_cs35l41_codec_conf);
+ card->codec_conf = mt8189_cs35l41_codec_conf;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_card mt8189_nau8825_soc_card = {
+ .owner = THIS_MODULE,
+ .dai_link = mt8189_nau8825_dai_links,
+ .num_links = ARRAY_SIZE(mt8189_nau8825_dai_links),
+ .dapm_widgets = mt8189_nau8825_card_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8189_nau8825_card_widgets),
+};
+
+static const struct mtk_soundcard_pdata mt8189_nau8825_card = {
+ .card_name = "mt8189_nau8825",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8189_nau8825_soc_card,
+ .num_jacks = MT8189_JACK_MAX,
+ .flags = NAU8825_HS_PRESENT
+ },
+ .sof_priv = NULL,
+ .soc_probe = mt8189_nau8825_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8189_rt5650_card = {
+ .card_name = "mt8189_rt5650",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8189_nau8825_soc_card,
+ .num_jacks = MT8189_JACK_MAX,
+ .flags = RT5650_HS_PRESENT
+ },
+ .sof_priv = NULL,
+ .soc_probe = mt8189_nau8825_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8189_rt5682s_card = {
+ .card_name = "mt8189_rt5682s",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8189_nau8825_soc_card,
+ .num_jacks = MT8189_JACK_MAX,
+ .flags = RT5682S_HS_PRESENT
+ },
+ .sof_priv = NULL,
+ .soc_probe = mt8189_nau8825_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8189_rt5682i_card = {
+ .card_name = "mt8189_rt5682i",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8189_nau8825_soc_card,
+ .num_jacks = MT8189_JACK_MAX,
+ .flags = RT5682I_HS_PRESENT
+ },
+ .sof_priv = NULL,
+ .soc_probe = mt8189_nau8825_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8188_es8326_card = {
+ .card_name = "mt8188_es8326",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8189_nau8825_soc_card,
+ .num_jacks = MT8189_JACK_MAX,
+ .flags = ES8326_HS_PRESENT
+ },
+ .sof_priv = NULL,
+ .soc_probe = mt8189_nau8825_soc_card_probe,
+};
+
+static const struct of_device_id mt8189_nau8825_dt_match[] = {
+ {.compatible = "mediatek,mt8189-nau8825", .data = &mt8189_nau8825_card,},
+ {.compatible = "mediatek,mt8189-rt5650", .data = &mt8189_rt5650_card,},
+ {.compatible = "mediatek,mt8189-rt5682s", .data = &mt8189_rt5682s_card,},
+ {.compatible = "mediatek,mt8189-rt5682i", .data = &mt8189_rt5682i_card,},
+ {.compatible = "mediatek,mt8189-es8326", .data = &mt8188_es8326_card,},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt8189_nau8825_dt_match);
+
+static struct platform_driver mt8189_nau8825_driver = {
+ .driver = {
+ .name = "mt8189-nau8825",
+ .of_match_table = mt8189_nau8825_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+module_platform_driver(mt8189_nau8825_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8189 NAU8825 ALSA SoC machine driver");
+MODULE_AUTHOR("Darren Ye <darren.ye@mediatek.com>");
+MODULE_AUTHOR("Cyril Chao <cyril.chao@mediatek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/mt8189/mt8189-reg.h b/sound/soc/mediatek/mt8189/mt8189-reg.h
new file mode 100644
index 000000000000..25f9658b6eae
--- /dev/null
+++ b/sound/soc/mediatek/mt8189/mt8189-reg.h
@@ -0,0 +1,10773 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8189-reg.h -- Mediatek 8189 audio driver reg definition
+ *
+ * Copyright (c) 2025 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#ifndef _MT8189_REG_H_
+#define _MT8189_REG_H_
+
+ /* reg bit enum */
+enum {
+ MT8189_MEMIF_PBUF_SIZE_32_BYTES,
+ MT8189_MEMIF_PBUF_SIZE_64_BYTES,
+ MT8189_MEMIF_PBUF_SIZE_128_BYTES,
+ MT8189_MEMIF_PBUF_SIZE_256_BYTES,
+ MT8189_MEMIF_PBUF_SIZE_NUM,
+};
+
+/*****************************************************************************
+ * R E G I S T E R D E F I N I T I O N
+ *****************************************************************************/
+/* AUDIO_TOP_CON0 */
+#define PDN_MTKAIFV4_SFT 25
+#define PDN_MTKAIFV4_MASK 0x1
+#define PDN_MTKAIFV4_MASK_SFT (0x1 << 25)
+#define PDN_FM_I2S_SFT 24
+#define PDN_FM_I2S_MASK 0x1
+#define PDN_FM_I2S_MASK_SFT (0x1 << 24)
+#define PDN_HW_GAIN01_SFT 21
+#define PDN_HW_GAIN01_MASK 0x1
+#define PDN_HW_GAIN01_MASK_SFT (0x1 << 21)
+#define PDN_HW_GAIN23_SFT 20
+#define PDN_HW_GAIN23_MASK 0x1
+#define PDN_HW_GAIN23_MASK_SFT (0x1 << 20)
+#define PDN_STF_SFT 19
+#define PDN_STF_MASK 0x1
+#define PDN_STF_MASK_SFT (0x1 << 19)
+#define PDN_CM0_SFT 18
+#define PDN_CM0_MASK 0x1
+#define PDN_CM0_MASK_SFT (0x1 << 18)
+#define PDN_CM1_SFT 17
+#define PDN_CM1_MASK 0x1
+#define PDN_CM1_MASK_SFT (0x1 << 17)
+#define PDN_PCM0_SFT 14
+#define PDN_PCM0_MASK 0x1
+#define PDN_PCM0_MASK_SFT (0x1 << 14)
+#define PDN_DL0_NLE_SFT 11
+#define PDN_DL0_NLE_MASK 0x1
+#define PDN_DL0_NLE_MASK_SFT (0x1 << 11)
+#define PDN_DL0_PREDIS_SFT 10
+#define PDN_DL0_PREDIS_MASK 0x1
+#define PDN_DL0_PREDIS_MASK_SFT (0x1 << 10)
+#define PDN_DL0_DAC_SFT 9
+#define PDN_DL0_DAC_MASK 0x1
+#define PDN_DL0_DAC_MASK_SFT (0x1 << 9)
+#define PDN_DL0_DAC_HIRES_SFT 8
+#define PDN_DL0_DAC_HIRES_MASK 0x1
+#define PDN_DL0_DAC_HIRES_MASK_SFT (0x1 << 8)
+#define PDN_DL0_DAC_TML_SFT 7
+#define PDN_DL0_DAC_TML_MASK 0x1
+#define PDN_DL0_DAC_TML_MASK_SFT (0x1 << 7)
+
+/* AUDIO_TOP_CON1 */
+#define PDN_UL0_ADC_SFT 23
+#define PDN_UL0_ADC_MASK 0x1
+#define PDN_UL0_ADC_MASK_SFT (0x1 << 23)
+#define PDN_UL0_TML_SFT 22
+#define PDN_UL0_TML_MASK 0x1
+#define PDN_UL0_TML_MASK_SFT (0x1 << 22)
+#define PDN_UL0_ADC_HIRES_SFT 21
+#define PDN_UL0_ADC_HIRES_MASK 0x1
+#define PDN_UL0_ADC_HIRES_MASK_SFT (0x1 << 21)
+#define PDN_UL0_ADC_HIRES_TML_SFT 20
+#define PDN_UL0_ADC_HIRES_TML_MASK 0x1
+#define PDN_UL0_ADC_HIRES_TML_MASK_SFT (0x1 << 20)
+#define PDN_UL1_ADC_SFT 19
+#define PDN_UL1_ADC_MASK 0x1
+#define PDN_UL1_ADC_MASK_SFT (0x1 << 19)
+#define PDN_UL1_TML_SFT 18
+#define PDN_UL1_TML_MASK 0x1
+#define PDN_UL1_TML_MASK_SFT (0x1 << 18)
+#define PDN_UL1_ADC_HIRES_SFT 17
+#define PDN_UL1_ADC_HIRES_MASK 0x1
+#define PDN_UL1_ADC_HIRES_MASK_SFT (0x1 << 17)
+#define PDN_UL1_ADC_HIRES_TML_SFT 16
+#define PDN_UL1_ADC_HIRES_TML_MASK 0x1
+#define PDN_UL1_ADC_HIRES_TML_MASK_SFT (0x1 << 16)
+#define PDN_DMIC0_ADC_SFT 7
+#define PDN_DMIC0_ADC_MASK 0x1
+#define PDN_DMIC0_ADC_MASK_SFT (0x1 << 7)
+#define PDN_DMIC1_ADC_SFT 3
+#define PDN_DMIC1_ADC_MASK 0x1
+#define PDN_DMIC1_ADC_MASK_SFT (0x1 << 3)
+
+/* AUDIO_TOP_CON2 */
+#define PDN_TDM_OUT_SFT 24
+#define PDN_TDM_OUT_MASK 0x1
+#define PDN_TDM_OUT_MASK_SFT (0x1 << 24)
+#define PDN_ETDM_OUT0_SFT 21
+#define PDN_ETDM_OUT0_MASK 0x1
+#define PDN_ETDM_OUT0_MASK_SFT (0x1 << 21)
+#define PDN_ETDM_OUT1_SFT 20
+#define PDN_ETDM_OUT1_MASK 0x1
+#define PDN_ETDM_OUT1_MASK_SFT (0x1 << 20)
+#define PDN_ETDM_OUT4_SFT 17
+#define PDN_ETDM_OUT4_MASK 0x1
+#define PDN_ETDM_OUT4_MASK_SFT (0x1 << 17)
+#define PDN_ETDM_IN0_SFT 13
+#define PDN_ETDM_IN0_MASK 0x1
+#define PDN_ETDM_IN0_MASK_SFT (0x1 << 13)
+#define PDN_ETDM_IN1_SFT 12
+#define PDN_ETDM_IN1_MASK 0x1
+#define PDN_ETDM_IN1_MASK_SFT (0x1 << 12)
+
+/* AUDIO_TOP_CON3 */
+#define PDN_CONNSYS_I2S_ASRC_SFT 25
+#define PDN_CONNSYS_I2S_ASRC_MASK 0x1
+#define PDN_CONNSYS_I2S_ASRC_MASK_SFT (0x1 << 25)
+#define PDN_GENERAL0_ASRC_SFT 24
+#define PDN_GENERAL0_ASRC_MASK 0x1
+#define PDN_GENERAL0_ASRC_MASK_SFT (0x1 << 24)
+#define PDN_GENERAL1_ASRC_SFT 23
+#define PDN_GENERAL1_ASRC_MASK 0x1
+#define PDN_GENERAL1_ASRC_MASK_SFT (0x1 << 23)
+#define PDN_GENERAL2_ASRC_SFT 22
+#define PDN_GENERAL2_ASRC_MASK 0x1
+#define PDN_GENERAL2_ASRC_MASK_SFT (0x1 << 22)
+#define PDN_GENERAL3_ASRC_SFT 21
+#define PDN_GENERAL3_ASRC_MASK 0x1
+#define PDN_GENERAL3_ASRC_MASK_SFT (0x1 << 21)
+#define PDN_GENERAL4_ASRC_SFT 20
+#define PDN_GENERAL4_ASRC_MASK 0x1
+#define PDN_GENERAL4_ASRC_MASK_SFT (0x1 << 20)
+
+/* AUDIO_TOP_CON4 */
+#define PDN_APLL_TUNER1_SFT 13
+#define PDN_APLL_TUNER1_MASK 0x1
+#define PDN_APLL_TUNER1_MASK_SFT (0x1 << 13)
+#define PDN_APLL_TUNER2_SFT 12
+#define PDN_APLL_TUNER2_MASK 0x1
+#define PDN_APLL_TUNER2_MASK_SFT (0x1 << 12)
+#define CG_H208M_CK_SFT 4
+#define CG_H208M_CK_MASK 0x1
+#define CG_H208M_CK_MASK_SFT (0x1 << 4)
+#define CG_APLL2_CK_SFT 3
+#define CG_APLL2_CK_MASK 0x1
+#define CG_APLL2_CK_MASK_SFT (0x1 << 3)
+#define CG_APLL1_CK_SFT 2
+#define CG_APLL1_CK_MASK 0x1
+#define CG_APLL1_CK_MASK_SFT (0x1 << 2)
+#define CG_AUDIO_F26M_CK_SFT 1
+#define CG_AUDIO_F26M_CK_MASK 0x1
+#define CG_AUDIO_F26M_CK_MASK_SFT (0x1 << 1)
+#define CG_AUDIO_HOPPING_CK_SFT 0
+#define CG_AUDIO_HOPPING_CK_MASK 0x1
+#define CG_AUDIO_HOPPING_CK_MASK_SFT (0x1 << 0)
+
+/* AUDIO_ENGEN_CON0 */
+/* AUDIO_ENGEN_CON0_USER1 */
+/* AUDIO_ENGEN_CON0_USER2 */
+#define MULTI_USER_BYPASS_SFT 17
+#define MULTI_USER_BYPASS_MASK 0x1
+#define MULTI_USER_BYPASS_MASK_SFT (0x1 << 17)
+#define MULTI_USER_RST_SFT 16
+#define MULTI_USER_RST_MASK 0x1
+#define MULTI_USER_RST_MASK_SFT (0x1 << 16)
+#define AUDIO_F26M_EN_RST_SFT 8
+#define AUDIO_F26M_EN_RST_MASK 0x1
+#define AUDIO_F26M_EN_RST_MASK_SFT (0x1 << 8)
+#define AUDIO_APLL2_EN_ON_SFT 3
+#define AUDIO_APLL2_EN_ON_MASK 0x1
+#define AUDIO_APLL2_EN_ON_MASK_SFT (0x1 << 3)
+#define AUDIO_APLL1_EN_ON_SFT 2
+#define AUDIO_APLL1_EN_ON_MASK 0x1
+#define AUDIO_APLL1_EN_ON_MASK_SFT (0x1 << 2)
+#define AUDIO_F3P25M_EN_ON_SFT 1
+#define AUDIO_F3P25M_EN_ON_MASK 0x1
+#define AUDIO_F3P25M_EN_ON_MASK_SFT (0x1 << 1)
+#define AUDIO_26M_EN_ON_SFT 0
+#define AUDIO_26M_EN_ON_MASK 0x1
+#define AUDIO_26M_EN_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_SINEGEN_CON0 */
+#define DAC_EN_SFT 26
+#define DAC_EN_MASK 0x1
+#define DAC_EN_MASK_SFT (0x1 << 26)
+#define TIE_SW_CH2_SFT 25
+#define TIE_SW_CH2_MASK 0x1
+#define TIE_SW_CH2_MASK_SFT (0x1 << 25)
+#define TIE_SW_CH1_SFT 24
+#define TIE_SW_CH1_MASK 0x1
+#define TIE_SW_CH1_MASK_SFT (0x1 << 24)
+#define AMP_DIV_CH2_SFT 20
+#define AMP_DIV_CH2_MASK 0xf
+#define AMP_DIV_CH2_MASK_SFT (0xf << 20)
+#define FREQ_DIV_CH2_SFT 12
+#define FREQ_DIV_CH2_MASK 0x1f
+#define FREQ_DIV_CH2_MASK_SFT (0x1f << 12)
+#define AMP_DIV_CH1_SFT 8
+#define AMP_DIV_CH1_MASK 0xf
+#define AMP_DIV_CH1_MASK_SFT (0xf << 8)
+#define FREQ_DIV_CH1_SFT 0
+#define FREQ_DIV_CH1_MASK 0x1f
+#define FREQ_DIV_CH1_MASK_SFT (0x1f << 0)
+
+/* AFE_SINEGEN_CON1 */
+#define SINE_DOMAIN_SFT 20
+#define SINE_DOMAIN_MASK 0x7
+#define SINE_DOMAIN_MASK_SFT (0x7 << 20)
+#define SINE_MODE_SFT 12
+#define SINE_MODE_MASK 0x1f
+#define SINE_MODE_MASK_SFT (0x1f << 12)
+#define INNER_LOOP_BACKI_SEL_SFT 8
+#define INNER_LOOP_BACKI_SEL_MASK 0x1
+#define INNER_LOOP_BACKI_SEL_MASK_SFT (0x1 << 8)
+#define INNER_LOOP_BACK_MODE_SFT 0
+#define INNER_LOOP_BACK_MODE_MASK 0xff
+#define INNER_LOOP_BACK_MODE_MASK_SFT (0xff << 0)
+
+/* AFE_SINEGEN_CON2 */
+#define TIE_CH1_CONSTANT_SFT 0
+#define TIE_CH1_CONSTANT_MASK 0xffffffff
+#define TIE_CH1_CONSTANT_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SINEGEN_CON3 */
+#define TIE_CH2_CONSTANT_SFT 0
+#define TIE_CH2_CONSTANT_MASK 0xffffffff
+#define TIE_CH2_CONSTANT_MASK_SFT (0xffffffff << 0)
+
+/* AFE_APLL1_TUNER_CFG */
+#define UPPER_BOUND_SFT 8
+#define UPPER_BOUND_MASK 0xff
+#define UPPER_BOUND_MASK_SFT (0xff << 8)
+#define APLL_DIV_SFT 4
+#define APLL_DIV_MASK 0xf
+#define APLL_DIV_MASK_SFT (0xf << 4)
+#define XTAL_EN_128FS_SEL_SFT 1
+#define XTAL_EN_128FS_SEL_MASK 0x3
+#define XTAL_EN_128FS_SEL_MASK_SFT (0x3 << 1)
+#define FREQ_TUNER_EN_SFT 0
+#define FREQ_TUNER_EN_MASK 0x1
+#define FREQ_TUNER_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_APLL1_TUNER_MON0 */
+#define TUNER_MON_SFT 0
+#define TUNER_MON_MASK 0xffffffff
+#define TUNER_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_APLL2_TUNER_CFG */
+#define UPPER_BOUND_SFT 8
+#define UPPER_BOUND_MASK 0xff
+#define UPPER_BOUND_MASK_SFT (0xff << 8)
+#define APLL_DIV_SFT 4
+#define APLL_DIV_MASK 0xf
+#define APLL_DIV_MASK_SFT (0xf << 4)
+#define XTAL_EN_128FS_SEL_SFT 1
+#define XTAL_EN_128FS_SEL_MASK 0x3
+#define XTAL_EN_128FS_SEL_MASK_SFT (0x3 << 1)
+#define FREQ_TUNER_EN_SFT 0
+#define FREQ_TUNER_EN_MASK 0x1
+#define FREQ_TUNER_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_APLL2_TUNER_MON0 */
+#define TUNER_MON_SFT 0
+#define TUNER_MON_MASK 0xffffffff
+#define TUNER_MON_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_TOP_RG0 */
+#define RESERVE_RG_SFT 0
+#define RESERVE_RG_MASK 0xffffffff
+#define RESERVE_RG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_TOP_RG1 */
+#define RESERVE_RG_SFT 0
+#define RESERVE_RG_MASK 0xffffffff
+#define RESERVE_RG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_TOP_RG2 */
+#define RESERVE_RG_SFT 0
+#define RESERVE_RG_MASK 0xffffffff
+#define RESERVE_RG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_TOP_RG3 */
+#define RESERVE_RG_SFT 0
+#define RESERVE_RG_MASK 0xffffffff
+#define RESERVE_RG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_TOP_RG4 */
+#define RESERVE_RG_SFT 0
+#define RESERVE_RG_MASK 0xffffffff
+#define RESERVE_RG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SPM_CONTROL_REQ */
+#define AFE_DDREN_REQ_SFT 4
+#define AFE_DDREN_REQ_MASK 0x1
+#define AFE_DDREN_REQ_MASK_SFT (0x1 << 4)
+#define AFE_INFRA_REQ_SFT 3
+#define AFE_INFRA_REQ_MASK 0x1
+#define AFE_INFRA_REQ_MASK_SFT (0x1 << 3)
+#define AFE_VRF18_REQ_SFT 2
+#define AFE_VRF18_REQ_MASK 0x1
+#define AFE_VRF18_REQ_MASK_SFT (0x1 << 2)
+#define AFE_APSRC_REQ_SFT 1
+#define AFE_APSRC_REQ_MASK 0x1
+#define AFE_APSRC_REQ_MASK_SFT (0x1 << 1)
+#define AFE_SRCCLKENA_REQ_SFT 0
+#define AFE_SRCCLKENA_REQ_MASK 0x1
+#define AFE_SRCCLKENA_REQ_MASK_SFT (0x1 << 0)
+
+/* AFE_SPM_CONTROL_ACK */
+#define SPM_RESOURCE_CONTROL_ACK_SFT 0
+#define SPM_RESOURCE_CONTROL_ACK_MASK 0xffffffff
+#define SPM_RESOURCE_CONTROL_ACK_MASK_SFT (0xffffffff << 0)
+
+/* AUD_TOP_CFG_VCORE_RG */
+#define AUD_TOP_CFG_SFT 0
+#define AUD_TOP_CFG_MASK 0xffffffff
+#define AUD_TOP_CFG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_TOP_IP_VERSION */
+#define AUDIO_TOP_IP_VERSION_SFT 0
+#define AUDIO_TOP_IP_VERSION_MASK 0xffffffff
+#define AUDIO_TOP_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_ENGEN_CON0_MON */
+#define AUDIO_ENGEN_MON_SFT 0
+#define AUDIO_ENGEN_MON_MASK 0xffffffff
+#define AUDIO_ENGEN_MON_MASK_SFT (0xffffffff << 0)
+
+/* AUD_TOP_CFG_VLP_RG */
+#define AUD_TOP_CFG_SFT 0
+#define AUD_TOP_CFG_MASK 0xffffffff
+#define AUD_TOP_CFG_MASK_SFT (0xffffffff << 0)
+
+/* AUD_TOP_MON_RG */
+#define AUD_TOP_MON_SFT 0
+#define AUD_TOP_MON_MASK 0xffffffff
+#define AUD_TOP_MON_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_USE_DEFAULT_DELSEL0 */
+#define USE_DEFAULT_DELSEL_RG_SFT 0
+#define USE_DEFAULT_DELSEL_RG_MASK 0xffffffff
+#define USE_DEFAULT_DELSEL_RG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_USE_DEFAULT_DELSEL1 */
+#define USE_DEFAULT_DELSEL_RG_SFT 0
+#define USE_DEFAULT_DELSEL_RG_MASK 0xffffffff
+#define USE_DEFAULT_DELSEL_RG_MASK_SFT (0xffffffff << 0)
+
+/* AUDIO_USE_DEFAULT_DELSEL2 */
+#define USE_DEFAULT_DELSEL_RG_SFT 0
+#define USE_DEFAULT_DELSEL_RG_MASK 0xffffffff
+#define USE_DEFAULT_DELSEL_RG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CONNSYS_I2S_IPM_VER_MON */
+#define RG_CONNSYS_I2S_IPM_VER_MON_SFT 0
+#define RG_CONNSYS_I2S_IPM_VER_MON_MASK 0xffffffff
+#define RG_CONNSYS_I2S_IPM_VER_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CONNSYS_I2S_MON_SEL */
+#define RG_CONNSYS_I2S_MON_SEL_SFT 0
+#define RG_CONNSYS_I2S_MON_SEL_MASK 0xff
+#define RG_CONNSYS_I2S_MON_SEL_MASK_SFT (0xff << 0)
+
+/* AFE_CONNSYS_I2S_MON */
+#define RG_CONNSYS_I2S_MON_SFT 0
+#define RG_CONNSYS_I2S_MON_MASK 0xffffffff
+#define RG_CONNSYS_I2S_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CONNSYS_I2S_CON */
+#define I2S_SOFT_RST_SFT 31
+#define I2S_SOFT_RST_MASK 0x1
+#define I2S_SOFT_RST_MASK_SFT (0x1 << 31)
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK 0x1
+#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK 0x1
+#define BCK_INV_MASK_SFT (0x1 << 29)
+#define I2SIN_PAD_SEL_SFT 28
+#define I2SIN_PAD_SEL_MASK 0x1
+#define I2SIN_PAD_SEL_MASK_SFT (0x1 << 28)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_HDEN_SFT 12
+#define I2S_HDEN_MASK 0x1
+#define I2S_HDEN_MASK_SFT (0x1 << 12)
+#define I2S_MODE_SFT 8
+#define I2S_MODE_MASK 0xf
+#define I2S_MODE_MASK_SFT (0xf << 8)
+#define I2S_BYPSRC_SFT 6
+#define I2S_BYPSRC_MASK 0x1
+#define I2S_BYPSRC_MASK_SFT (0x1 << 6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S_FMT_SFT 3
+#define I2S_FMT_MASK 0x1
+#define I2S_FMT_MASK_SFT (0x1 << 3)
+#define I2S_SRC_SFT 2
+#define I2S_SRC_MASK 0x1
+#define I2S_SRC_MASK_SFT (0x1 << 2)
+#define I2S_WLEN_SFT 1
+#define I2S_WLEN_MASK 0x1
+#define I2S_WLEN_MASK_SFT (0x1 << 1)
+#define I2S_EN_SFT 0
+#define I2S_EN_MASK 0x1
+#define I2S_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_PCM0_INTF_CON0 */
+#define PCM0_HDEN_SFT 26
+#define PCM0_HDEN_MASK 0x1
+#define PCM0_HDEN_MASK_SFT (0x1 << 26)
+#define PCM0_SYNC_DELSEL_SFT 25
+#define PCM0_SYNC_DELSEL_MASK 0x1
+#define PCM0_SYNC_DELSEL_MASK_SFT (0x1 << 25)
+#define PCM0_TX_LR_SWAP_SFT 24
+#define PCM0_TX_LR_SWAP_MASK 0x1
+#define PCM0_TX_LR_SWAP_MASK_SFT (0x1 << 24)
+#define PCM0_SYNC_OUT_INV_SFT 23
+#define PCM0_SYNC_OUT_INV_MASK 0x1
+#define PCM0_SYNC_OUT_INV_MASK_SFT (0x1 << 23)
+#define PCM0_BCLK_OUT_INV_SFT 22
+#define PCM0_BCLK_OUT_INV_MASK 0x1
+#define PCM0_BCLK_OUT_INV_MASK_SFT (0x1 << 22)
+#define PCM0_SYNC_IN_INV_SFT 21
+#define PCM0_SYNC_IN_INV_MASK 0x1
+#define PCM0_SYNC_IN_INV_MASK_SFT (0x1 << 21)
+#define PCM0_BCLK_IN_INV_SFT 20
+#define PCM0_BCLK_IN_INV_MASK 0x1
+#define PCM0_BCLK_IN_INV_MASK_SFT (0x1 << 20)
+#define PCM0_TX_LCH_RPT_SFT 19
+#define PCM0_TX_LCH_RPT_MASK 0x1
+#define PCM0_TX_LCH_RPT_MASK_SFT (0x1 << 19)
+#define PCM0_VBT_16K_MODE_SFT 18
+#define PCM0_VBT_16K_MODE_MASK 0x1
+#define PCM0_VBT_16K_MODE_MASK_SFT (0x1 << 18)
+#define PCM0_BIT_LENGTH_SFT 16
+#define PCM0_BIT_LENGTH_MASK 0x3
+#define PCM0_BIT_LENGTH_MASK_SFT (0x3 << 16)
+#define PCM0_WLEN_SFT 14
+#define PCM0_WLEN_MASK 0x3
+#define PCM0_WLEN_MASK_SFT (0x3 << 14)
+#define PCM0_SYNC_LENGTH_SFT 9
+#define PCM0_SYNC_LENGTH_MASK 0x1f
+#define PCM0_SYNC_LENGTH_MASK_SFT (0x1f << 9)
+#define PCM0_SYNC_TYPE_SFT 8
+#define PCM0_SYNC_TYPE_MASK 0x1
+#define PCM0_SYNC_TYPE_MASK_SFT (0x1 << 8)
+#define PCM0_BYP_ASRC_SFT 7
+#define PCM0_BYP_ASRC_MASK 0x1
+#define PCM0_BYP_ASRC_MASK_SFT (0x1 << 7)
+#define PCM0_SLAVE_SFT 6
+#define PCM0_SLAVE_MASK 0x1
+#define PCM0_SLAVE_MASK_SFT (0x1 << 6)
+#define PCM0_MODE_SFT 3
+#define PCM0_MODE_MASK 0x7
+#define PCM0_MODE_MASK_SFT (0x7 << 3)
+#define PCM0_FMT_SFT 1
+#define PCM0_FMT_MASK 0x3
+#define PCM0_FMT_MASK_SFT (0x3 << 1)
+#define PCM0_EN_SFT 0
+#define PCM0_EN_MASK 0x1
+#define PCM0_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_PCM0_INTF_CON1 */
+#define PCM0_TX_RX_LOOPBACK_SFT 31
+#define PCM0_TX_RX_LOOPBACK_MASK 0x1
+#define PCM0_TX_RX_LOOPBACK_MASK_SFT (0x1 << 31)
+#define PCM0_BUFFER_LOOPBACK_SFT 30
+#define PCM0_BUFFER_LOOPBACK_MASK 0x1
+#define PCM0_BUFFER_LOOPBACK_MASK_SFT (0x1 << 30)
+#define PCM0_PARALLEL_LOOPBACK_SFT 29
+#define PCM0_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM0_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 29)
+#define PCM0_SERIAL_LOOPBACK_SFT 28
+#define PCM0_SERIAL_LOOPBACK_MASK 0x1
+#define PCM0_SERIAL_LOOPBACK_MASK_SFT (0x1 << 28)
+#define PCM0_DAI_LOOPBACK_SFT 27
+#define PCM0_DAI_LOOPBACK_MASK 0x1
+#define PCM0_DAI_LOOPBACK_MASK_SFT (0x1 << 27)
+#define PCM0_I2S_LOOPBACK_SFT 26
+#define PCM0_I2S_LOOPBACK_MASK 0x1
+#define PCM0_I2S_LOOPBACK_MASK_SFT (0x1 << 26)
+#define PCM0_1X_EN_DOMAIN_SFT 23
+#define PCM0_1X_EN_DOMAIN_MASK 0x7
+#define PCM0_1X_EN_DOMAIN_MASK_SFT (0x7 << 23)
+#define PCM0_1X_EN_MODE_SFT 18
+#define PCM0_1X_EN_MODE_MASK 0x1f
+#define PCM0_1X_EN_MODE_MASK_SFT (0x1f << 18)
+#define PCM0_TX3_RCH_DBG_MODE_SFT 17
+#define PCM0_TX3_RCH_DBG_MODE_MASK 0x1
+#define PCM0_TX3_RCH_DBG_MODE_MASK_SFT (0x1 << 17)
+#define PCM0_PCM1_LOOPBACK_SFT 16
+#define PCM0_PCM1_LOOPBACK_MASK 0x1
+#define PCM0_PCM1_LOOPBACK_MASK_SFT (0x1 << 16)
+#define PCM0_LOOPBACK_CH_SEL_SFT 12
+#define PCM0_LOOPBACK_CH_SEL_MASK 0x3
+#define PCM0_LOOPBACK_CH_SEL_MASK_SFT (0x3 << 12)
+#define PCM0_BT_MODE_SFT 11
+#define PCM0_BT_MODE_MASK 0x1
+#define PCM0_BT_MODE_MASK_SFT (0x1 << 11)
+#define PCM0_EXT_MODEM_SFT 10
+#define PCM0_EXT_MODEM_MASK 0x1
+#define PCM0_EXT_MODEM_MASK_SFT (0x1 << 10)
+#define PCM0_USE_MD3_SFT 9
+#define PCM0_USE_MD3_MASK 0x1
+#define PCM0_USE_MD3_MASK_SFT (0x1 << 9)
+#define PCM0_FIX_VALUE_SEL_SFT 8
+#define PCM0_FIX_VALUE_SEL_MASK 0x1
+#define PCM0_FIX_VALUE_SEL_MASK_SFT (0x1 << 8)
+#define PCM0_TX_FIX_VALUE_SFT 0
+#define PCM0_TX_FIX_VALUE_MASK 0xff
+#define PCM0_TX_FIX_VALUE_MASK_SFT (0xff << 0)
+
+/* AFE_PCM_INTF_MON */
+#define PCM0_TX_FIFO_OV_SFT 5
+#define PCM0_TX_FIFO_OV_MASK 0x1
+#define PCM0_TX_FIFO_OV_MASK_SFT (0x1 << 5)
+#define PCM0_RX_FIFO_OV_SFT 4
+#define PCM0_RX_FIFO_OV_MASK 0x1
+#define PCM0_RX_FIFO_OV_MASK_SFT (0x1 << 4)
+#define PCM1_TX_FIFO_OV_SFT 3
+#define PCM1_TX_FIFO_OV_MASK 0x1
+#define PCM1_TX_FIFO_OV_MASK_SFT (0x1 << 3)
+#define PCM1_RX_FIFO_OV_SFT 2
+#define PCM1_RX_FIFO_OV_MASK 0x1
+#define PCM1_RX_FIFO_OV_MASK_SFT (0x1 << 2)
+#define PCM0_SYNC_GLITCH_SFT 1
+#define PCM0_SYNC_GLITCH_MASK 0x1
+#define PCM0_SYNC_GLITCH_MASK_SFT (0x1 << 1)
+#define PCM1_SYNC_GLITCH_SFT 0
+#define PCM1_SYNC_GLITCH_MASK 0x1
+#define PCM1_SYNC_GLITCH_MASK_SFT (0x1 << 0)
+
+/* AFE_PCM_TOP_IP_VERSION */
+#define AFE_PCM_TOP_IP_VERSION_SFT 0
+#define AFE_PCM_TOP_IP_VERSION_MASK 0xffffffff
+#define AFE_PCM_TOP_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_IRQ_MCU_EN */
+#define AFE_IRQ_MCU_EN_SFT 0
+#define AFE_IRQ_MCU_EN_MASK 0xffffffff
+#define AFE_IRQ_MCU_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_IRQ_MCU_DSP_EN */
+#define AFE_IRQ_DSP_EN_SFT 0
+#define AFE_IRQ_DSP_EN_MASK 0xffffffff
+#define AFE_IRQ_DSP_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_IRQ_MCU_DSP2_EN */
+#define AFE_IRQ_DSP2_EN_SFT 0
+#define AFE_IRQ_DSP2_EN_MASK 0xffffffff
+#define AFE_IRQ_DSP2_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_IRQ_MCU_SCP_EN */
+#define IRQ31_MCU_SCP_EN_SFT 31
+#define IRQ30_MCU_SCP_EN_SFT 30
+#define IRQ29_MCU_SCP_EN_SFT 29
+#define IRQ28_MCU_SCP_EN_SFT 28
+#define IRQ27_MCU_SCP_EN_SFT 27
+#define IRQ26_MCU_SCP_EN_SFT 26
+#define IRQ25_MCU_SCP_EN_SFT 25
+#define IRQ24_MCU_SCP_EN_SFT 24
+#define IRQ23_MCU_SCP_EN_SFT 23
+#define IRQ22_MCU_SCP_EN_SFT 22
+#define IRQ21_MCU_SCP_EN_SFT 21
+#define IRQ20_MCU_SCP_EN_SFT 20
+#define IRQ19_MCU_SCP_EN_SFT 19
+#define IRQ18_MCU_SCP_EN_SFT 18
+#define IRQ17_MCU_SCP_EN_SFT 17
+#define IRQ16_MCU_SCP_EN_SFT 16
+#define IRQ15_MCU_SCP_EN_SFT 15
+#define IRQ14_MCU_SCP_EN_SFT 14
+#define IRQ13_MCU_SCP_EN_SFT 13
+#define IRQ12_MCU_SCP_EN_SFT 12
+#define IRQ11_MCU_SCP_EN_SFT 11
+#define IRQ10_MCU_SCP_EN_SFT 10
+#define IRQ9_MCU_SCP_EN_SFT 9
+#define IRQ8_MCU_SCP_EN_SFT 8
+#define IRQ7_MCU_SCP_EN_SFT 7
+#define IRQ6_MCU_SCP_EN_SFT 6
+#define IRQ5_MCU_SCP_EN_SFT 5
+#define IRQ4_MCU_SCP_EN_SFT 4
+#define IRQ3_MCU_SCP_EN_SFT 3
+#define IRQ2_MCU_SCP_EN_SFT 2
+#define IRQ1_MCU_SCP_EN_SFT 1
+#define IRQ0_MCU_SCP_EN_SFT 0
+
+/* AFE_CUSTOM_IRQ_MCU_EN */
+#define AFE_CUSTOM_IRQ_MCU_EN_SFT 0
+#define AFE_CUSTOM_IRQ_MCU_EN_MASK 0xffffffff
+#define AFE_CUSTOM_IRQ_MCU_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CUSTOM_IRQ_MCU_DSP_EN */
+#define AFE_CUSTOM_IRQ_DSP_EN_SFT 0
+#define AFE_CUSTOM_IRQ_DSP_EN_MASK 0xffffffff
+#define AFE_CUSTOM_IRQ_DSP_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CUSTOM_IRQ_MCU_DSP2_EN */
+#define AFE_CUSTOM_IRQ_DSP2_EN_SFT 0
+#define AFE_CUSTOM_IRQ_DSP2_EN_MASK 0xffffffff
+#define AFE_CUSTOM_IRQ_DSP2_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CUSTOM_IRQ_MCU_SCP_EN */
+#define AFE_CUSTOM_IRQ_SCP_EN_SFT 0
+#define AFE_CUSTOM_IRQ_SCP_EN_MASK 0xffffffff
+#define AFE_CUSTOM_IRQ_SCP_EN_MASK_SFT (0xffffffff << 0)
+
+/* AFE_IRQ_MCU_STATUS */
+#define IRQ26_MCU_SFT 26
+#define IRQ26_MCU_MASK 0x1
+#define IRQ26_MCU_MASK_SFT (0x1 << 26)
+#define IRQ25_MCU_SFT 25
+#define IRQ25_MCU_MASK 0x1
+#define IRQ25_MCU_MASK_SFT (0x1 << 25)
+#define IRQ24_MCU_SFT 24
+#define IRQ24_MCU_MASK 0x1
+#define IRQ24_MCU_MASK_SFT (0x1 << 24)
+#define IRQ23_MCU_SFT 23
+#define IRQ23_MCU_MASK 0x1
+#define IRQ23_MCU_MASK_SFT (0x1 << 23)
+#define IRQ22_MCU_SFT 22
+#define IRQ22_MCU_MASK 0x1
+#define IRQ22_MCU_MASK_SFT (0x1 << 22)
+#define IRQ21_MCU_SFT 21
+#define IRQ21_MCU_MASK 0x1
+#define IRQ21_MCU_MASK_SFT (0x1 << 21)
+#define IRQ20_MCU_SFT 20
+#define IRQ20_MCU_MASK 0x1
+#define IRQ20_MCU_MASK_SFT (0x1 << 20)
+#define IRQ19_MCU_SFT 19
+#define IRQ19_MCU_MASK 0x1
+#define IRQ19_MCU_MASK_SFT (0x1 << 19)
+#define IRQ18_MCU_SFT 18
+#define IRQ18_MCU_MASK 0x1
+#define IRQ18_MCU_MASK_SFT (0x1 << 18)
+#define IRQ17_MCU_SFT 17
+#define IRQ17_MCU_MASK 0x1
+#define IRQ17_MCU_MASK_SFT (0x1 << 17)
+#define IRQ16_MCU_SFT 16
+#define IRQ16_MCU_MASK 0x1
+#define IRQ16_MCU_MASK_SFT (0x1 << 16)
+#define IRQ15_MCU_SFT 15
+#define IRQ15_MCU_MASK 0x1
+#define IRQ15_MCU_MASK_SFT (0x1 << 15)
+#define IRQ14_MCU_SFT 14
+#define IRQ14_MCU_MASK 0x1
+#define IRQ14_MCU_MASK_SFT (0x1 << 14)
+#define IRQ13_MCU_SFT 13
+#define IRQ13_MCU_MASK 0x1
+#define IRQ13_MCU_MASK_SFT (0x1 << 13)
+#define IRQ12_MCU_SFT 12
+#define IRQ12_MCU_MASK 0x1
+#define IRQ12_MCU_MASK_SFT (0x1 << 12)
+#define IRQ11_MCU_SFT 11
+#define IRQ11_MCU_MASK 0x1
+#define IRQ11_MCU_MASK_SFT (0x1 << 11)
+#define IRQ10_MCU_SFT 10
+#define IRQ10_MCU_MASK 0x1
+#define IRQ10_MCU_MASK_SFT (0x1 << 10)
+#define IRQ9_MCU_SFT 9
+#define IRQ9_MCU_MASK 0x1
+#define IRQ9_MCU_MASK_SFT (0x1 << 9)
+#define IRQ8_MCU_SFT 8
+#define IRQ8_MCU_MASK 0x1
+#define IRQ8_MCU_MASK_SFT (0x1 << 8)
+#define IRQ7_MCU_SFT 7
+#define IRQ7_MCU_MASK 0x1
+#define IRQ7_MCU_MASK_SFT (0x1 << 7)
+#define IRQ6_MCU_SFT 6
+#define IRQ6_MCU_MASK 0x1
+#define IRQ6_MCU_MASK_SFT (0x1 << 6)
+#define IRQ5_MCU_SFT 5
+#define IRQ5_MCU_MASK 0x1
+#define IRQ5_MCU_MASK_SFT (0x1 << 5)
+#define IRQ4_MCU_SFT 4
+#define IRQ4_MCU_MASK 0x1
+#define IRQ4_MCU_MASK_SFT (0x1 << 4)
+#define IRQ3_MCU_SFT 3
+#define IRQ3_MCU_MASK 0x1
+#define IRQ3_MCU_MASK_SFT (0x1 << 3)
+#define IRQ2_MCU_SFT 2
+#define IRQ2_MCU_MASK 0x1
+#define IRQ2_MCU_MASK_SFT (0x1 << 2)
+#define IRQ1_MCU_SFT 1
+#define IRQ1_MCU_MASK 0x1
+#define IRQ1_MCU_MASK_SFT (0x1 << 1)
+#define IRQ0_MCU_SFT 0
+#define IRQ0_MCU_MASK 0x1
+#define IRQ0_MCU_MASK_SFT (0x1 << 0)
+
+/* AFE_CUSTOM_IRQ_MCU_STATUS */
+#define CUSTOM_IRQ21_MCU_SFT 21
+#define CUSTOM_IRQ21_MCU_MASK 0x1
+#define CUSTOM_IRQ21_MCU_MASK_SFT (0x1 << 21)
+#define CUSTOM_IRQ20_MCU_SFT 20
+#define CUSTOM_IRQ20_MCU_MASK 0x1
+#define CUSTOM_IRQ20_MCU_MASK_SFT (0x1 << 20)
+#define CUSTOM_IRQ19_MCU_SFT 19
+#define CUSTOM_IRQ19_MCU_MASK 0x1
+#define CUSTOM_IRQ19_MCU_MASK_SFT (0x1 << 19)
+#define CUSTOM_IRQ18_MCU_SFT 18
+#define CUSTOM_IRQ18_MCU_MASK 0x1
+#define CUSTOM_IRQ18_MCU_MASK_SFT (0x1 << 18)
+#define CUSTOM_IRQ17_MCU_SFT 17
+#define CUSTOM_IRQ17_MCU_MASK 0x1
+#define CUSTOM_IRQ17_MCU_MASK_SFT (0x1 << 17)
+#define CUSTOM_IRQ16_MCU_SFT 16
+#define CUSTOM_IRQ16_MCU_MASK 0x1
+#define CUSTOM_IRQ16_MCU_MASK_SFT (0x1 << 16)
+#define CUSTOM_IRQ9_MCU_SFT 9
+#define CUSTOM_IRQ9_MCU_MASK 0x1
+#define CUSTOM_IRQ9_MCU_MASK_SFT (0x1 << 9)
+#define CUSTOM_IRQ8_MCU_SFT 8
+#define CUSTOM_IRQ8_MCU_MASK 0x1
+#define CUSTOM_IRQ8_MCU_MASK_SFT (0x1 << 8)
+#define CUSTOM_IRQ7_MCU_SFT 7
+#define CUSTOM_IRQ7_MCU_MASK 0x1
+#define CUSTOM_IRQ7_MCU_MASK_SFT (0x1 << 7)
+#define CUSTOM_IRQ6_MCU_SFT 6
+#define CUSTOM_IRQ6_MCU_MASK 0x1
+#define CUSTOM_IRQ6_MCU_MASK_SFT (0x1 << 6)
+#define CUSTOM_IRQ5_MCU_SFT 5
+#define CUSTOM_IRQ5_MCU_MASK 0x1
+#define CUSTOM_IRQ5_MCU_MASK_SFT (0x1 << 5)
+#define CUSTOM_IRQ4_MCU_SFT 4
+#define CUSTOM_IRQ4_MCU_MASK 0x1
+#define CUSTOM_IRQ4_MCU_MASK_SFT (0x1 << 4)
+#define CUSTOM_IRQ3_MCU_SFT 3
+#define CUSTOM_IRQ3_MCU_MASK 0x1
+#define CUSTOM_IRQ3_MCU_MASK_SFT (0x1 << 3)
+#define CUSTOM_IRQ2_MCU_SFT 2
+#define CUSTOM_IRQ2_MCU_MASK 0x1
+#define CUSTOM_IRQ2_MCU_MASK_SFT (0x1 << 2)
+#define CUSTOM_IRQ1_MCU_SFT 1
+#define CUSTOM_IRQ1_MCU_MASK 0x1
+#define CUSTOM_IRQ1_MCU_MASK_SFT (0x1 << 1)
+#define CUSTOM_IRQ0_MCU_SFT 0
+#define CUSTOM_IRQ0_MCU_MASK 0x1
+#define CUSTOM_IRQ0_MCU_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_CFG */
+#define AFE_IRQ_CLR_CFG_SFT 31
+#define AFE_IRQ_CLR_CFG_MASK 0x1
+#define AFE_IRQ_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ_MCU_CNT_SFT 0
+#define AFE_IRQ_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ0_MCU_CFG0 */
+#define AFE_IRQ0_MCU_DOMAIN_SFT 9
+#define AFE_IRQ0_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ0_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ0_MCU_FS_SFT 4
+#define AFE_IRQ0_MCU_FS_MASK 0x1f
+#define AFE_IRQ0_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ0_MCU_ON_SFT 0
+#define AFE_IRQ0_MCU_ON_MASK 0x1
+#define AFE_IRQ0_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ0_MCU_CFG1 */
+#define AFE_IRQ0_CLR_CFG_SFT 31
+#define AFE_IRQ0_CLR_CFG_MASK 0x1
+#define AFE_IRQ0_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ0_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ0_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ0_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ0_MCU_CNT_SFT 0
+#define AFE_IRQ0_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ0_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ1_MCU_CFG0 */
+#define AFE_IRQ1_MCU_DOMAIN_SFT 9
+#define AFE_IRQ1_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ1_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ1_MCU_FS_SFT 4
+#define AFE_IRQ1_MCU_FS_MASK 0x1f
+#define AFE_IRQ1_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ1_MCU_ON_SFT 0
+#define AFE_IRQ1_MCU_ON_MASK 0x1
+#define AFE_IRQ1_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ1_MCU_CFG1 */
+#define AFE_IRQ1_CLR_CFG_SFT 31
+#define AFE_IRQ1_CLR_CFG_MASK 0x1
+#define AFE_IRQ1_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ1_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ1_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ1_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ1_MCU_CNT_SFT 0
+#define AFE_IRQ1_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ1_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ2_MCU_CFG0 */
+#define AFE_IRQ2_MCU_DOMAIN_SFT 9
+#define AFE_IRQ2_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ2_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ2_MCU_FS_SFT 4
+#define AFE_IRQ2_MCU_FS_MASK 0x1f
+#define AFE_IRQ2_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ2_MCU_ON_SFT 0
+#define AFE_IRQ2_MCU_ON_MASK 0x1
+#define AFE_IRQ2_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ2_MCU_CFG1 */
+#define AFE_IRQ2_CLR_CFG_SFT 31
+#define AFE_IRQ2_CLR_CFG_MASK 0x1
+#define AFE_IRQ2_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ2_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ2_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ2_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ2_MCU_CNT_SFT 0
+#define AFE_IRQ2_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ2_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ3_MCU_CFG0 */
+#define AFE_IRQ3_MCU_DOMAIN_SFT 9
+#define AFE_IRQ3_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ3_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ3_MCU_FS_SFT 4
+#define AFE_IRQ3_MCU_FS_MASK 0x1f
+#define AFE_IRQ3_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ3_MCU_ON_SFT 0
+#define AFE_IRQ3_MCU_ON_MASK 0x1
+#define AFE_IRQ3_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ3_MCU_CFG1 */
+#define AFE_IRQ3_CLR_CFG_SFT 31
+#define AFE_IRQ3_CLR_CFG_MASK 0x1
+#define AFE_IRQ3_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ3_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ3_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ3_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ3_MCU_CNT_SFT 0
+#define AFE_IRQ3_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ3_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ4_MCU_CFG0 */
+#define AFE_IRQ4_MCU_DOMAIN_SFT 9
+#define AFE_IRQ4_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ4_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ4_MCU_FS_SFT 4
+#define AFE_IRQ4_MCU_FS_MASK 0x1f
+#define AFE_IRQ4_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ4_MCU_ON_SFT 0
+#define AFE_IRQ4_MCU_ON_MASK 0x1
+#define AFE_IRQ4_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ4_MCU_CFG1 */
+#define AFE_IRQ4_CLR_CFG_SFT 31
+#define AFE_IRQ4_CLR_CFG_MASK 0x1
+#define AFE_IRQ4_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ4_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ4_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ4_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ4_MCU_CNT_SFT 0
+#define AFE_IRQ4_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ4_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ5_MCU_CFG0 */
+#define AFE_IRQ5_MCU_DOMAIN_SFT 9
+#define AFE_IRQ5_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ5_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ5_MCU_FS_SFT 4
+#define AFE_IRQ5_MCU_FS_MASK 0x1f
+#define AFE_IRQ5_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ5_MCU_ON_SFT 0
+#define AFE_IRQ5_MCU_ON_MASK 0x1
+#define AFE_IRQ5_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ5_MCU_CFG1 */
+#define AFE_IRQ5_CLR_CFG_SFT 31
+#define AFE_IRQ5_CLR_CFG_MASK 0x1
+#define AFE_IRQ5_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ5_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ5_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ5_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ5_MCU_CNT_SFT 0
+#define AFE_IRQ5_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ5_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ6_MCU_CFG0 */
+#define AFE_IRQ6_MCU_DOMAIN_SFT 9
+#define AFE_IRQ6_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ6_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ6_MCU_FS_SFT 4
+#define AFE_IRQ6_MCU_FS_MASK 0x1f
+#define AFE_IRQ6_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ6_MCU_ON_SFT 0
+#define AFE_IRQ6_MCU_ON_MASK 0x1
+#define AFE_IRQ6_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ6_MCU_CFG1 */
+#define AFE_IRQ6_CLR_CFG_SFT 31
+#define AFE_IRQ6_CLR_CFG_MASK 0x1
+#define AFE_IRQ6_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ6_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ6_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ6_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ6_MCU_CNT_SFT 0
+#define AFE_IRQ6_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ6_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ7_MCU_CFG0 */
+#define AFE_IRQ7_MCU_DOMAIN_SFT 9
+#define AFE_IRQ7_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ7_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ7_MCU_FS_SFT 4
+#define AFE_IRQ7_MCU_FS_MASK 0x1f
+#define AFE_IRQ7_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ7_MCU_ON_SFT 0
+#define AFE_IRQ7_MCU_ON_MASK 0x1
+#define AFE_IRQ7_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ7_MCU_CFG1 */
+#define AFE_IRQ7_CLR_CFG_SFT 31
+#define AFE_IRQ7_CLR_CFG_MASK 0x1
+#define AFE_IRQ7_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ7_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ7_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ7_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ7_MCU_CNT_SFT 0
+#define AFE_IRQ7_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ7_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ8_MCU_CFG0 */
+#define AFE_IRQ8_MCU_DOMAIN_SFT 9
+#define AFE_IRQ8_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ8_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ8_MCU_FS_SFT 4
+#define AFE_IRQ8_MCU_FS_MASK 0x1f
+#define AFE_IRQ8_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ8_MCU_ON_SFT 0
+#define AFE_IRQ8_MCU_ON_MASK 0x1
+#define AFE_IRQ8_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ8_MCU_CFG1 */
+#define AFE_IRQ8_CLR_CFG_SFT 31
+#define AFE_IRQ8_CLR_CFG_MASK 0x1
+#define AFE_IRQ8_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ8_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ8_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ8_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ8_MCU_CNT_SFT 0
+#define AFE_IRQ8_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ8_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ9_MCU_CFG0 */
+#define AFE_IRQ9_MCU_DOMAIN_SFT 9
+#define AFE_IRQ9_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ9_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ9_MCU_FS_SFT 4
+#define AFE_IRQ9_MCU_FS_MASK 0x1f
+#define AFE_IRQ9_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ9_MCU_ON_SFT 0
+#define AFE_IRQ9_MCU_ON_MASK 0x1
+#define AFE_IRQ9_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ9_MCU_CFG1 */
+#define AFE_IRQ9_CLR_CFG_SFT 31
+#define AFE_IRQ9_CLR_CFG_MASK 0x1
+#define AFE_IRQ9_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ9_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ9_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ9_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ9_MCU_CNT_SFT 0
+#define AFE_IRQ9_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ9_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ10_MCU_CFG0 */
+#define AFE_IRQ10_MCU_DOMAIN_SFT 9
+#define AFE_IRQ10_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ10_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ10_MCU_FS_SFT 4
+#define AFE_IRQ10_MCU_FS_MASK 0x1f
+#define AFE_IRQ10_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ10_MCU_ON_SFT 0
+#define AFE_IRQ10_MCU_ON_MASK 0x1
+#define AFE_IRQ10_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ10_MCU_CFG1 */
+#define AFE_IRQ10_CLR_CFG_SFT 31
+#define AFE_IRQ10_CLR_CFG_MASK 0x1
+#define AFE_IRQ10_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ10_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ10_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ10_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ10_MCU_CNT_SFT 0
+#define AFE_IRQ10_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ10_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ11_MCU_CFG0 */
+#define AFE_IRQ11_MCU_DOMAIN_SFT 9
+#define AFE_IRQ11_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ11_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ11_MCU_FS_SFT 4
+#define AFE_IRQ11_MCU_FS_MASK 0x1f
+#define AFE_IRQ11_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ11_MCU_ON_SFT 0
+#define AFE_IRQ11_MCU_ON_MASK 0x1
+#define AFE_IRQ11_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ11_MCU_CFG1 */
+#define AFE_IRQ11_CLR_CFG_SFT 31
+#define AFE_IRQ11_CLR_CFG_MASK 0x1
+#define AFE_IRQ11_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ11_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ11_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ11_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ11_MCU_CNT_SFT 0
+#define AFE_IRQ11_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ11_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ12_MCU_CFG0 */
+#define AFE_IRQ12_MCU_DOMAIN_SFT 9
+#define AFE_IRQ12_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ12_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ12_MCU_FS_SFT 4
+#define AFE_IRQ12_MCU_FS_MASK 0x1f
+#define AFE_IRQ12_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ12_MCU_ON_SFT 0
+#define AFE_IRQ12_MCU_ON_MASK 0x1
+#define AFE_IRQ12_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ12_MCU_CFG1 */
+#define AFE_IRQ12_CLR_CFG_SFT 31
+#define AFE_IRQ12_CLR_CFG_MASK 0x1
+#define AFE_IRQ12_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ12_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ12_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ12_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ12_MCU_CNT_SFT 0
+#define AFE_IRQ12_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ12_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ13_MCU_CFG0 */
+#define AFE_IRQ13_MCU_DOMAIN_SFT 9
+#define AFE_IRQ13_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ13_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ13_MCU_FS_SFT 4
+#define AFE_IRQ13_MCU_FS_MASK 0x1f
+#define AFE_IRQ13_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ13_MCU_ON_SFT 0
+#define AFE_IRQ13_MCU_ON_MASK 0x1
+#define AFE_IRQ13_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ13_MCU_CFG1 */
+#define AFE_IRQ13_CLR_CFG_SFT 31
+#define AFE_IRQ13_CLR_CFG_MASK 0x1
+#define AFE_IRQ13_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ13_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ13_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ13_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ13_MCU_CNT_SFT 0
+#define AFE_IRQ13_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ13_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ14_MCU_CFG0 */
+#define AFE_IRQ14_MCU_DOMAIN_SFT 9
+#define AFE_IRQ14_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ14_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ14_MCU_FS_SFT 4
+#define AFE_IRQ14_MCU_FS_MASK 0x1f
+#define AFE_IRQ14_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ14_MCU_ON_SFT 0
+#define AFE_IRQ14_MCU_ON_MASK 0x1
+#define AFE_IRQ14_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ14_MCU_CFG1 */
+#define AFE_IRQ14_CLR_CFG_SFT 31
+#define AFE_IRQ14_CLR_CFG_MASK 0x1
+#define AFE_IRQ14_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ14_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ14_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ14_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ14_MCU_CNT_SFT 0
+#define AFE_IRQ14_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ14_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ15_MCU_CFG0 */
+#define AFE_IRQ15_MCU_DOMAIN_SFT 9
+#define AFE_IRQ15_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ15_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ15_MCU_FS_SFT 4
+#define AFE_IRQ15_MCU_FS_MASK 0x1f
+#define AFE_IRQ15_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ15_MCU_ON_SFT 0
+#define AFE_IRQ15_MCU_ON_MASK 0x1
+#define AFE_IRQ15_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ15_MCU_CFG1 */
+#define AFE_IRQ15_CLR_CFG_SFT 31
+#define AFE_IRQ15_CLR_CFG_MASK 0x1
+#define AFE_IRQ15_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ15_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ15_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ15_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ15_MCU_CNT_SFT 0
+#define AFE_IRQ15_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ15_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ16_MCU_CFG0 */
+#define AFE_IRQ16_MCU_DOMAIN_SFT 9
+#define AFE_IRQ16_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ16_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ16_MCU_FS_SFT 4
+#define AFE_IRQ16_MCU_FS_MASK 0x1f
+#define AFE_IRQ16_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ16_MCU_ON_SFT 0
+#define AFE_IRQ16_MCU_ON_MASK 0x1
+#define AFE_IRQ16_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ16_MCU_CFG1 */
+#define AFE_IRQ16_CLR_CFG_SFT 31
+#define AFE_IRQ16_CLR_CFG_MASK 0x1
+#define AFE_IRQ16_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ16_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ16_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ16_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ16_MCU_CNT_SFT 0
+#define AFE_IRQ16_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ16_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ17_MCU_CFG0 */
+#define AFE_IRQ17_MCU_DOMAIN_SFT 9
+#define AFE_IRQ17_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ17_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ17_MCU_FS_SFT 4
+#define AFE_IRQ17_MCU_FS_MASK 0x1f
+#define AFE_IRQ17_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ17_MCU_ON_SFT 0
+#define AFE_IRQ17_MCU_ON_MASK 0x1
+#define AFE_IRQ17_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ17_MCU_CFG1 */
+#define AFE_IRQ17_CLR_CFG_SFT 31
+#define AFE_IRQ17_CLR_CFG_MASK 0x1
+#define AFE_IRQ17_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ17_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ17_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ17_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ17_MCU_CNT_SFT 0
+#define AFE_IRQ17_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ17_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ18_MCU_CFG0 */
+#define AFE_IRQ18_MCU_DOMAIN_SFT 9
+#define AFE_IRQ18_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ18_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ18_MCU_FS_SFT 4
+#define AFE_IRQ18_MCU_FS_MASK 0x1f
+#define AFE_IRQ18_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ18_MCU_ON_SFT 0
+#define AFE_IRQ18_MCU_ON_MASK 0x1
+#define AFE_IRQ18_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ18_MCU_CFG1 */
+#define AFE_IRQ18_CLR_CFG_SFT 31
+#define AFE_IRQ18_CLR_CFG_MASK 0x1
+#define AFE_IRQ18_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ18_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ18_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ18_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ18_MCU_CNT_SFT 0
+#define AFE_IRQ18_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ18_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ19_MCU_CFG0 */
+#define AFE_IRQ19_MCU_DOMAIN_SFT 9
+#define AFE_IRQ19_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ19_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ19_MCU_FS_SFT 4
+#define AFE_IRQ19_MCU_FS_MASK 0x1f
+#define AFE_IRQ19_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ19_MCU_ON_SFT 0
+#define AFE_IRQ19_MCU_ON_MASK 0x1
+#define AFE_IRQ19_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ19_MCU_CFG1 */
+#define AFE_IRQ19_CLR_CFG_SFT 31
+#define AFE_IRQ19_CLR_CFG_MASK 0x1
+#define AFE_IRQ19_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ19_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ19_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ19_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ19_MCU_CNT_SFT 0
+#define AFE_IRQ19_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ19_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ20_MCU_CFG0 */
+#define AFE_IRQ20_MCU_DOMAIN_SFT 9
+#define AFE_IRQ20_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ20_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ20_MCU_FS_SFT 4
+#define AFE_IRQ20_MCU_FS_MASK 0x1f
+#define AFE_IRQ20_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ20_MCU_ON_SFT 0
+#define AFE_IRQ20_MCU_ON_MASK 0x1
+#define AFE_IRQ20_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ20_MCU_CFG1 */
+#define AFE_IRQ20_CLR_CFG_SFT 31
+#define AFE_IRQ20_CLR_CFG_MASK 0x1
+#define AFE_IRQ20_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ20_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ20_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ20_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ20_MCU_CNT_SFT 0
+#define AFE_IRQ20_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ20_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ21_MCU_CFG0 */
+#define AFE_IRQ21_MCU_DOMAIN_SFT 9
+#define AFE_IRQ21_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ21_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ21_MCU_FS_SFT 4
+#define AFE_IRQ21_MCU_FS_MASK 0x1f
+#define AFE_IRQ21_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ21_MCU_ON_SFT 0
+#define AFE_IRQ21_MCU_ON_MASK 0x1
+#define AFE_IRQ21_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ21_MCU_CFG1 */
+#define AFE_IRQ21_CLR_CFG_SFT 31
+#define AFE_IRQ21_CLR_CFG_MASK 0x1
+#define AFE_IRQ21_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ21_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ21_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ21_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ21_MCU_CNT_SFT 0
+#define AFE_IRQ21_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ21_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ22_MCU_CFG0 */
+#define AFE_IRQ22_MCU_DOMAIN_SFT 9
+#define AFE_IRQ22_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ22_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ22_MCU_FS_SFT 4
+#define AFE_IRQ22_MCU_FS_MASK 0x1f
+#define AFE_IRQ22_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ22_MCU_ON_SFT 0
+#define AFE_IRQ22_MCU_ON_MASK 0x1
+#define AFE_IRQ22_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ22_MCU_CFG1 */
+#define AFE_IRQ22_CLR_CFG_SFT 31
+#define AFE_IRQ22_CLR_CFG_MASK 0x1
+#define AFE_IRQ22_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ22_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ22_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ22_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ22_MCU_CNT_SFT 0
+#define AFE_IRQ22_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ22_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ23_MCU_CFG0 */
+#define AFE_IRQ23_MCU_DOMAIN_SFT 9
+#define AFE_IRQ23_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ23_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ23_MCU_FS_SFT 4
+#define AFE_IRQ23_MCU_FS_MASK 0x1f
+#define AFE_IRQ23_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ23_MCU_ON_SFT 0
+#define AFE_IRQ23_MCU_ON_MASK 0x1
+#define AFE_IRQ23_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ23_MCU_CFG1 */
+#define AFE_IRQ23_CLR_CFG_SFT 31
+#define AFE_IRQ23_CLR_CFG_MASK 0x1
+#define AFE_IRQ23_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ23_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ23_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ23_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ23_MCU_CNT_SFT 0
+#define AFE_IRQ23_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ23_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ24_MCU_CFG0 */
+#define AFE_IRQ24_MCU_DOMAIN_SFT 9
+#define AFE_IRQ24_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ24_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ24_MCU_FS_SFT 4
+#define AFE_IRQ24_MCU_FS_MASK 0x1f
+#define AFE_IRQ24_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ24_MCU_ON_SFT 0
+#define AFE_IRQ24_MCU_ON_MASK 0x1
+#define AFE_IRQ24_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ24_MCU_CFG1 */
+#define AFE_IRQ24_CLR_CFG_SFT 31
+#define AFE_IRQ24_CLR_CFG_MASK 0x1
+#define AFE_IRQ24_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ24_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ24_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ24_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ24_MCU_CNT_SFT 0
+#define AFE_IRQ24_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ24_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ25_MCU_CFG0 */
+#define AFE_IRQ25_MCU_DOMAIN_SFT 9
+#define AFE_IRQ25_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ25_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ25_MCU_FS_SFT 4
+#define AFE_IRQ25_MCU_FS_MASK 0x1f
+#define AFE_IRQ25_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ25_MCU_ON_SFT 0
+#define AFE_IRQ25_MCU_ON_MASK 0x1
+#define AFE_IRQ25_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ25_MCU_CFG1 */
+#define AFE_IRQ25_CLR_CFG_SFT 31
+#define AFE_IRQ25_CLR_CFG_MASK 0x1
+#define AFE_IRQ25_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ25_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ25_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ25_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ25_MCU_CNT_SFT 0
+#define AFE_IRQ25_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ25_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ26_MCU_CFG0 */
+#define AFE_IRQ26_MCU_DOMAIN_SFT 9
+#define AFE_IRQ26_MCU_DOMAIN_MASK 0x7
+#define AFE_IRQ26_MCU_DOMAIN_MASK_SFT (0x7 << 9)
+#define AFE_IRQ26_MCU_FS_SFT 4
+#define AFE_IRQ26_MCU_FS_MASK 0x1f
+#define AFE_IRQ26_MCU_FS_MASK_SFT (0x1f << 4)
+#define AFE_IRQ26_MCU_ON_SFT 0
+#define AFE_IRQ26_MCU_ON_MASK 0x1
+#define AFE_IRQ26_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ26_MCU_CFG1 */
+#define AFE_IRQ26_CLR_CFG_SFT 31
+#define AFE_IRQ26_CLR_CFG_MASK 0x1
+#define AFE_IRQ26_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_IRQ26_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_IRQ26_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_IRQ26_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_IRQ26_MCU_CNT_SFT 0
+#define AFE_IRQ26_MCU_CNT_MASK 0xffffff
+#define AFE_IRQ26_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_CUSTOM_IRQ0_MCU_CFG0 */
+#define AFE_CUSTOM_IRQ0_MCU_ON_SFT 0
+#define AFE_CUSTOM_IRQ0_MCU_ON_MASK 0x1
+#define AFE_CUSTOM_IRQ0_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_CUSTOM_IRQ0_CNT_MON */
+#define AFE_CUSTOM_IRQ0_CNT_MON_SFT 0
+#define AFE_CUSTOM_IRQ0_CNT_MON_MASK 0xffffff
+#define AFE_CUSTOM_IRQ0_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_CUSTOM_IRQ0_MCU_CFG1 */
+#define AFE_CUSTOM_IRQ0_CLR_CFG_SFT 31
+#define AFE_CUSTOM_IRQ0_CLR_CFG_MASK 0x1
+#define AFE_CUSTOM_IRQ0_CLR_CFG_MASK_SFT (0x1 << 31)
+#define AFE_CUSTOM_IRQ0_MISS_FLAG_CLR_CFG_SFT 30
+#define AFE_CUSTOM_IRQ0_MISS_FLAG_CLR_CFG_MASK 0x1
+#define AFE_CUSTOM_IRQ0_MISS_FLAG_CLR_CFG_MASK_SFT (0x1 << 30)
+#define AFE_CUSTOM_IRQ0_MCU_CNT_SFT 0
+#define AFE_CUSTOM_IRQ0_MCU_CNT_MASK 0xffffff
+#define AFE_CUSTOM_IRQ0_MCU_CNT_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ_MCU_MON0 */
+#define AFE_IRQ26_MISS_FLAG_SFT 26
+#define AFE_IRQ26_MISS_FLAG_MASK 0x1
+#define AFE_IRQ26_MISS_FLAG_MASK_SFT (0x1 << 26)
+#define AFE_IRQ25_MISS_FLAG_SFT 25
+#define AFE_IRQ25_MISS_FLAG_MASK 0x1
+#define AFE_IRQ25_MISS_FLAG_MASK_SFT (0x1 << 25)
+#define AFE_IRQ24_MISS_FLAG_SFT 24
+#define AFE_IRQ24_MISS_FLAG_MASK 0x1
+#define AFE_IRQ24_MISS_FLAG_MASK_SFT (0x1 << 24)
+#define AFE_IRQ23_MISS_FLAG_SFT 23
+#define AFE_IRQ23_MISS_FLAG_MASK 0x1
+#define AFE_IRQ23_MISS_FLAG_MASK_SFT (0x1 << 23)
+#define AFE_IRQ22_MISS_FLAG_SFT 22
+#define AFE_IRQ22_MISS_FLAG_MASK 0x1
+#define AFE_IRQ22_MISS_FLAG_MASK_SFT (0x1 << 22)
+#define AFE_IRQ21_MISS_FLAG_SFT 21
+#define AFE_IRQ21_MISS_FLAG_MASK 0x1
+#define AFE_IRQ21_MISS_FLAG_MASK_SFT (0x1 << 21)
+#define AFE_IRQ20_MISS_FLAG_SFT 20
+#define AFE_IRQ20_MISS_FLAG_MASK 0x1
+#define AFE_IRQ20_MISS_FLAG_MASK_SFT (0x1 << 20)
+#define AFE_IRQ19_MISS_FLAG_SFT 19
+#define AFE_IRQ19_MISS_FLAG_MASK 0x1
+#define AFE_IRQ19_MISS_FLAG_MASK_SFT (0x1 << 19)
+#define AFE_IRQ18_MISS_FLAG_SFT 18
+#define AFE_IRQ18_MISS_FLAG_MASK 0x1
+#define AFE_IRQ18_MISS_FLAG_MASK_SFT (0x1 << 18)
+#define AFE_IRQ17_MISS_FLAG_SFT 17
+#define AFE_IRQ17_MISS_FLAG_MASK 0x1
+#define AFE_IRQ17_MISS_FLAG_MASK_SFT (0x1 << 17)
+#define AFE_IRQ16_MISS_FLAG_SFT 16
+#define AFE_IRQ16_MISS_FLAG_MASK 0x1
+#define AFE_IRQ16_MISS_FLAG_MASK_SFT (0x1 << 16)
+#define AFE_IRQ15_MISS_FLAG_SFT 15
+#define AFE_IRQ15_MISS_FLAG_MASK 0x1
+#define AFE_IRQ15_MISS_FLAG_MASK_SFT (0x1 << 15)
+#define AFE_IRQ14_MISS_FLAG_SFT 14
+#define AFE_IRQ14_MISS_FLAG_MASK 0x1
+#define AFE_IRQ14_MISS_FLAG_MASK_SFT (0x1 << 14)
+#define AFE_IRQ13_MISS_FLAG_SFT 13
+#define AFE_IRQ13_MISS_FLAG_MASK 0x1
+#define AFE_IRQ13_MISS_FLAG_MASK_SFT (0x1 << 13)
+#define AFE_IRQ12_MISS_FLAG_SFT 12
+#define AFE_IRQ12_MISS_FLAG_MASK 0x1
+#define AFE_IRQ12_MISS_FLAG_MASK_SFT (0x1 << 12)
+#define AFE_IRQ11_MISS_FLAG_SFT 11
+#define AFE_IRQ11_MISS_FLAG_MASK 0x1
+#define AFE_IRQ11_MISS_FLAG_MASK_SFT (0x1 << 11)
+#define AFE_IRQ10_MISS_FLAG_SFT 10
+#define AFE_IRQ10_MISS_FLAG_MASK 0x1
+#define AFE_IRQ10_MISS_FLAG_MASK_SFT (0x1 << 10)
+#define AFE_IRQ9_MISS_FLAG_SFT 9
+#define AFE_IRQ9_MISS_FLAG_MASK 0x1
+#define AFE_IRQ9_MISS_FLAG_MASK_SFT (0x1 << 9)
+#define AFE_IRQ8_MISS_FLAG_SFT 8
+#define AFE_IRQ8_MISS_FLAG_MASK 0x1
+#define AFE_IRQ8_MISS_FLAG_MASK_SFT (0x1 << 8)
+#define AFE_IRQ7_MISS_FLAG_SFT 7
+#define AFE_IRQ7_MISS_FLAG_MASK 0x1
+#define AFE_IRQ7_MISS_FLAG_MASK_SFT (0x1 << 7)
+#define AFE_IRQ6_MISS_FLAG_SFT 6
+#define AFE_IRQ6_MISS_FLAG_MASK 0x1
+#define AFE_IRQ6_MISS_FLAG_MASK_SFT (0x1 << 6)
+#define AFE_IRQ5_MISS_FLAG_SFT 5
+#define AFE_IRQ5_MISS_FLAG_MASK 0x1
+#define AFE_IRQ5_MISS_FLAG_MASK_SFT (0x1 << 5)
+#define AFE_IRQ4_MISS_FLAG_SFT 4
+#define AFE_IRQ4_MISS_FLAG_MASK 0x1
+#define AFE_IRQ4_MISS_FLAG_MASK_SFT (0x1 << 4)
+#define AFE_IRQ3_MISS_FLAG_SFT 3
+#define AFE_IRQ3_MISS_FLAG_MASK 0x1
+#define AFE_IRQ3_MISS_FLAG_MASK_SFT (0x1 << 3)
+#define AFE_IRQ2_MISS_FLAG_SFT 2
+#define AFE_IRQ2_MISS_FLAG_MASK 0x1
+#define AFE_IRQ2_MISS_FLAG_MASK_SFT (0x1 << 2)
+#define AFE_IRQ1_MISS_FLAG_SFT 1
+#define AFE_IRQ1_MISS_FLAG_MASK 0x1
+#define AFE_IRQ1_MISS_FLAG_MASK_SFT (0x1 << 1)
+#define AFE_IRQ0_MISS_FLAG_SFT 0
+#define AFE_IRQ0_MISS_FLAG_MASK 0x1
+#define AFE_IRQ0_MISS_FLAG_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_MON1 */
+#define AFE_CUSTOM_IRQ21_MISS_FLAG_SFT 21
+#define AFE_CUSTOM_IRQ21_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ21_MISS_FLAG_MASK_SFT (0x1 << 21)
+#define AFE_CUSTOM_IRQ20_MISS_FLAG_SFT 20
+#define AFE_CUSTOM_IRQ20_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ20_MISS_FLAG_MASK_SFT (0x1 << 20)
+#define AFE_CUSTOM_IRQ19_MISS_FLAG_SFT 19
+#define AFE_CUSTOM_IRQ19_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ19_MISS_FLAG_MASK_SFT (0x1 << 19)
+#define AFE_CUSTOM_IRQ18_MISS_FLAG_SFT 18
+#define AFE_CUSTOM_IRQ18_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ18_MISS_FLAG_MASK_SFT (0x1 << 18)
+#define AFE_CUSTOM_IRQ17_MISS_FLAG_SFT 17
+#define AFE_CUSTOM_IRQ17_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ17_MISS_FLAG_MASK_SFT (0x1 << 17)
+#define AFE_CUSTOM_IRQ16_MISS_FLAG_SFT 16
+#define AFE_CUSTOM_IRQ16_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ16_MISS_FLAG_MASK_SFT (0x1 << 16)
+#define AFE_CUSTOM_IRQ9_MISS_FLAG_SFT 9
+#define AFE_CUSTOM_IRQ9_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ9_MISS_FLAG_MASK_SFT (0x1 << 9)
+#define AFE_CUSTOM_IRQ8_MISS_FLAG_SFT 8
+#define AFE_CUSTOM_IRQ8_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ8_MISS_FLAG_MASK_SFT (0x1 << 8)
+#define AFE_CUSTOM_IRQ7_MISS_FLAG_SFT 7
+#define AFE_CUSTOM_IRQ7_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ7_MISS_FLAG_MASK_SFT (0x1 << 7)
+#define AFE_CUSTOM_IRQ6_MISS_FLAG_SFT 6
+#define AFE_CUSTOM_IRQ6_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ6_MISS_FLAG_MASK_SFT (0x1 << 6)
+#define AFE_CUSTOM_IRQ5_MISS_FLAG_SFT 5
+#define AFE_CUSTOM_IRQ5_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ5_MISS_FLAG_MASK_SFT (0x1 << 5)
+#define AFE_CUSTOM_IRQ4_MISS_FLAG_SFT 4
+#define AFE_CUSTOM_IRQ4_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ4_MISS_FLAG_MASK_SFT (0x1 << 4)
+#define AFE_CUSTOM_IRQ3_MISS_FLAG_SFT 3
+#define AFE_CUSTOM_IRQ3_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ3_MISS_FLAG_MASK_SFT (0x1 << 3)
+#define AFE_CUSTOM_IRQ2_MISS_FLAG_SFT 2
+#define AFE_CUSTOM_IRQ2_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ2_MISS_FLAG_MASK_SFT (0x1 << 2)
+#define AFE_CUSTOM_IRQ1_MISS_FLAG_SFT 1
+#define AFE_CUSTOM_IRQ1_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ1_MISS_FLAG_MASK_SFT (0x1 << 1)
+#define AFE_CUSTOM_IRQ0_MISS_FLAG_SFT 0
+#define AFE_CUSTOM_IRQ0_MISS_FLAG_MASK 0x1
+#define AFE_CUSTOM_IRQ0_MISS_FLAG_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_MON2 */
+#define AFE_IRQ_B_R_CNT_SFT 8
+#define AFE_IRQ_B_R_CNT_MASK 0xff
+#define AFE_IRQ_B_R_CNT_MASK_SFT (0xff << 8)
+#define AFE_IRQ_B_F_CNT_SFT 0
+#define AFE_IRQ_B_F_CNT_MASK 0xff
+#define AFE_IRQ_B_F_CNT_MASK_SFT (0xff << 0)
+
+/* AFE_IRQ0_CNT_MON */
+#define AFE_IRQ0_CNT_MON_SFT 0
+#define AFE_IRQ0_CNT_MON_MASK 0xffffff
+#define AFE_IRQ0_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ1_CNT_MON */
+#define AFE_IRQ1_CNT_MON_SFT 0
+#define AFE_IRQ1_CNT_MON_MASK 0xffffff
+#define AFE_IRQ1_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ2_CNT_MON */
+#define AFE_IRQ2_CNT_MON_SFT 0
+#define AFE_IRQ2_CNT_MON_MASK 0xffffff
+#define AFE_IRQ2_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ3_CNT_MON */
+#define AFE_IRQ3_CNT_MON_SFT 0
+#define AFE_IRQ3_CNT_MON_MASK 0xffffff
+#define AFE_IRQ3_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ4_CNT_MON */
+#define AFE_IRQ4_CNT_MON_SFT 0
+#define AFE_IRQ4_CNT_MON_MASK 0xffffff
+#define AFE_IRQ4_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ5_CNT_MON */
+#define AFE_IRQ5_CNT_MON_SFT 0
+#define AFE_IRQ5_CNT_MON_MASK 0xffffff
+#define AFE_IRQ5_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ6_CNT_MON */
+#define AFE_IRQ6_CNT_MON_SFT 0
+#define AFE_IRQ6_CNT_MON_MASK 0xffffff
+#define AFE_IRQ6_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ7_CNT_MON */
+#define AFE_IRQ7_CNT_MON_SFT 0
+#define AFE_IRQ7_CNT_MON_MASK 0xffffff
+#define AFE_IRQ7_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ8_CNT_MON */
+#define AFE_IRQ8_CNT_MON_SFT 0
+#define AFE_IRQ8_CNT_MON_MASK 0xffffff
+#define AFE_IRQ8_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ9_CNT_MON */
+#define AFE_IRQ9_CNT_MON_SFT 0
+#define AFE_IRQ9_CNT_MON_MASK 0xffffff
+#define AFE_IRQ9_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ10_CNT_MON */
+#define AFE_IRQ10_CNT_MON_SFT 0
+#define AFE_IRQ10_CNT_MON_MASK 0xffffff
+#define AFE_IRQ10_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ11_CNT_MON */
+#define AFE_IRQ11_CNT_MON_SFT 0
+#define AFE_IRQ11_CNT_MON_MASK 0xffffff
+#define AFE_IRQ11_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ12_CNT_MON */
+#define AFE_IRQ12_CNT_MON_SFT 0
+#define AFE_IRQ12_CNT_MON_MASK 0xffffff
+#define AFE_IRQ12_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ13_CNT_MON */
+#define AFE_IRQ13_CNT_MON_SFT 0
+#define AFE_IRQ13_CNT_MON_MASK 0xffffff
+#define AFE_IRQ13_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ14_CNT_MON */
+#define AFE_IRQ14_CNT_MON_SFT 0
+#define AFE_IRQ14_CNT_MON_MASK 0xffffff
+#define AFE_IRQ14_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ15_CNT_MON */
+#define AFE_IRQ15_CNT_MON_SFT 0
+#define AFE_IRQ15_CNT_MON_MASK 0xffffff
+#define AFE_IRQ15_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ16_CNT_MON */
+#define AFE_IRQ16_CNT_MON_SFT 0
+#define AFE_IRQ16_CNT_MON_MASK 0xffffff
+#define AFE_IRQ16_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ17_CNT_MON */
+#define AFE_IRQ17_CNT_MON_SFT 0
+#define AFE_IRQ17_CNT_MON_MASK 0xffffff
+#define AFE_IRQ17_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ18_CNT_MON */
+#define AFE_IRQ18_CNT_MON_SFT 0
+#define AFE_IRQ18_CNT_MON_MASK 0xffffff
+#define AFE_IRQ18_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ19_CNT_MON */
+#define AFE_IRQ19_CNT_MON_SFT 0
+#define AFE_IRQ19_CNT_MON_MASK 0xffffff
+#define AFE_IRQ19_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ20_CNT_MON */
+#define AFE_IRQ20_CNT_MON_SFT 0
+#define AFE_IRQ20_CNT_MON_MASK 0xffffff
+#define AFE_IRQ20_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ21_CNT_MON */
+#define AFE_IRQ21_CNT_MON_SFT 0
+#define AFE_IRQ21_CNT_MON_MASK 0xffffff
+#define AFE_IRQ21_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ22_CNT_MON */
+#define AFE_IRQ22_CNT_MON_SFT 0
+#define AFE_IRQ22_CNT_MON_MASK 0xffffff
+#define AFE_IRQ22_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ23_CNT_MON */
+#define AFE_IRQ23_CNT_MON_SFT 0
+#define AFE_IRQ23_CNT_MON_MASK 0xffffff
+#define AFE_IRQ23_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ24_CNT_MON */
+#define AFE_IRQ24_CNT_MON_SFT 0
+#define AFE_IRQ24_CNT_MON_MASK 0xffffff
+#define AFE_IRQ24_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ25_CNT_MON */
+#define AFE_IRQ25_CNT_MON_SFT 0
+#define AFE_IRQ25_CNT_MON_MASK 0xffffff
+#define AFE_IRQ25_CNT_MON_MASK_SFT (0xffffff << 0)
+
+/* AFE_IRQ26_CNT_MON */
+#define AFE_IRQ26_CNT_MON_SFT 0
+#define AFE_IRQ26_CNT_MON_MASK 0xffffff
+#define AFE_IRQ26_CNT_MON_MASK_SFT (0xffffff << 0)
+
+ /* AFE_GAIN0_CON0 */
+ /* AFE_GAIN1_CON0 */
+ /* AFE_GAIN2_CON0 */
+ /* AFE_GAIN3_CON0 */
+#define GAIN_TARGET_SYNC_ON_SFT 24
+#define GAIN_TARGET_SYNC_ON_MASK 0x1
+#define GAIN_TARGET_SYNC_ON_MASK_SFT (0x1 << 24)
+#define GAIN_TIMEOUT_SFT 18
+#define GAIN_TIMEOUT_MASK 0x3f
+#define GAIN_TIMEOUT_MASK_SFT (0x3f << 18)
+#define GAIN_TRIG_SFT 17
+#define GAIN_TRIG_MASK 0x1
+#define GAIN_TRIG_MASK_SFT (0x1 << 17)
+#define GAIN_ON_SFT 16
+#define GAIN_ON_MASK 0x1
+#define GAIN_ON_MASK_SFT (0x1 << 16)
+#define GAIN_SAMPLE_PER_STEP_SFT 8
+#define GAIN_SAMPLE_PER_STEP_MASK 0xff
+#define GAIN_SAMPLE_PER_STEP_MASK_SFT (0xff << 8)
+#define GAIN_SEL_DOMAIN_SFT 5
+#define GAIN_SEL_DOMAIN_MASK 0x7
+#define GAIN_SEL_DOMAIN_MASK_SFT (0x7 << 5)
+#define GAIN_SEL_FS_SFT 0
+#define GAIN_SEL_FS_MASK 0x1f
+#define GAIN_SEL_FS_MASK_SFT (0x1f << 0)
+
+ /* AFE_GAIN0_CON1_R */
+ /* AFE_GAIN1_CON1_R */
+ /* AFE_GAIN2_CON1_R */
+ /* AFE_GAIN3_CON1_R */
+#define GAIN_TARGET_R_SFT 0
+#define GAIN_TARGET_R_MASK 0xffffffff
+#define GAIN_TARGET_R_MASK_SFT (0xffffffff << 0)
+
+ /* AFE_GAIN0_CON1_L */
+ /* AFE_GAIN1_CON1_L */
+ /* AFE_GAIN2_CON1_L */
+ /* AFE_GAIN3_CON1_L */
+#define GAIN_TARGET_L_SFT 0
+#define GAIN_TARGET_L_MASK 0xffffffff
+#define GAIN_TARGET_L_MASK_SFT (0xffffffff << 0)
+
+ /* AFE_GAIN0_CON2 */
+ /* AFE_GAIN1_CON2 */
+ /* AFE_GAIN2_CON2 */
+ /* AFE_GAIN3_CON2 */
+#define GAIN_DOWN_STEP_SFT 0
+#define GAIN_DOWN_STEP_MASK 0x3fffff
+#define GAIN_DOWN_STEP_MASK_SFT (0x3fffff << 0)
+
+ /* AFE_GAIN0_CON3 */
+ /* AFE_GAIN1_CON3 */
+ /* AFE_GAIN2_CON3 */
+ /* AFE_GAIN3_CON3 */
+#define GAIN_UP_STEP_SFT 0
+#define GAIN_UP_STEP_MASK 0x3fffff
+#define GAIN_UP_STEP_MASK_SFT (0x3fffff << 0)
+
+ /* AFE_GAIN0_CUR_R */
+ /* AFE_GAIN1_CUR_R */
+ /* AFE_GAIN2_CUR_R */
+ /* AFE_GAIN3_CUR_R */
+#define AFE_GAIN_CUR_R_SFT 0
+#define AFE_GAIN_CUR_R_MASK 0xffffffff
+#define AFE_GAIN_CUR_R_MASK_SFT (0xffffffff << 0)
+
+ /* AFE_GAIN0_CUR_L */
+ /* AFE_GAIN1_CUR_L */
+ /* AFE_GAIN2_CUR_L */
+ /* AFE_GAIN3_CUR_L */
+#define AFE_GAIN_CUR_L_SFT 0
+#define AFE_GAIN_CUR_L_MASK 0xffffffff
+#define AFE_GAIN_CUR_L_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_IPM_VER_MON */
+#define RG_DL_IPM_VER_MON_SFT 0
+#define RG_DL_IPM_VER_MON_MASK 0xffffffff
+#define RG_DL_IPM_VER_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_SRC_CON0 */
+#define AFE_DL_INPUT_MODE_CTL_SFT 24
+#define AFE_DL_INPUT_MODE_CTL_MASK 0x1f
+#define AFE_DL_INPUT_MODE_CTL_MASK_SFT (0x1f << 24)
+#define AFE_DL_CH1_SATURATION_EN_CTL_SFT 21
+#define AFE_DL_CH1_SATURATION_EN_CTL_MASK 0x1
+#define AFE_DL_CH1_SATURATION_EN_CTL_MASK_SFT (0x1 << 21)
+#define AFE_DL_CH2_SATURATION_EN_CTL_SFT 20
+#define AFE_DL_CH2_SATURATION_EN_CTL_MASK 0x1
+#define AFE_DL_CH2_SATURATION_EN_CTL_MASK_SFT (0x1 << 20)
+#define AFE_DL_OUTPUT_SEL_CTL_SFT 18
+#define AFE_DL_OUTPUT_SEL_CTL_MASK 0x3
+#define AFE_DL_OUTPUT_SEL_CTL_MASK_SFT (0x3 << 18)
+#define AFE_DL_FADEIN_0START_EN_SFT 16
+#define AFE_DL_FADEIN_0START_EN_MASK 0x3
+#define AFE_DL_FADEIN_0START_EN_MASK_SFT (0x3 << 16)
+#define AFE_DL_DISABLE_HW_CG_CTL_SFT 15
+#define AFE_DL_DISABLE_HW_CG_CTL_MASK 0x1
+#define AFE_DL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 15)
+#define AFE_DL_MUTE_CH1_OFF_CTL_PRE_SFT 12
+#define AFE_DL_MUTE_CH1_OFF_CTL_PRE_MASK 0x1
+#define AFE_DL_MUTE_CH1_OFF_CTL_PRE_MASK_SFT (0x1 << 12)
+#define AFE_DL_MUTE_CH2_OFF_CTL_PRE_SFT 11
+#define AFE_DL_MUTE_CH2_OFF_CTL_PRE_MASK 0x1
+#define AFE_DL_MUTE_CH2_OFF_CTL_PRE_MASK_SFT (0x1 << 11)
+#define AFE_DL_ARAMPSP_CTL_PRE_SFT 9
+#define AFE_DL_ARAMPSP_CTL_PRE_MASK 0x3
+#define AFE_DL_ARAMPSP_CTL_PRE_MASK_SFT (0x3 << 9)
+#define AFE_DL_VOICE_MODE_CTL_PRE_SFT 5
+#define AFE_DL_VOICE_MODE_CTL_PRE_MASK 0x1
+#define AFE_DL_VOICE_MODE_CTL_PRE_MASK_SFT (0x1 << 5)
+#define AFE_DL_MUTE_CH1_ON_CTL_PRE_SFT 4
+#define AFE_DL_MUTE_CH1_ON_CTL_PRE_MASK 0x1
+#define AFE_DL_MUTE_CH1_ON_CTL_PRE_MASK_SFT (0x1 << 4)
+#define AFE_DL_MUTE_CH2_ON_CTL_PRE_SFT 3
+#define AFE_DL_MUTE_CH2_ON_CTL_PRE_MASK 0x1
+#define AFE_DL_MUTE_CH2_ON_CTL_PRE_MASK_SFT (0x1 << 3)
+#define AFE_DL_GAIN_ON_CTL_PRE_SFT 1
+#define AFE_DL_GAIN_ON_CTL_PRE_MASK 0x1
+#define AFE_DL_GAIN_ON_CTL_PRE_MASK_SFT (0x1 << 1)
+#define AFE_DL_SRC_ON_TMP_CTL_PRE_SFT 0
+#define AFE_DL_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define AFE_DL_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_SRC_CON1 */
+#define AFE_DL_GAIN1_CTL_PRE_SFT 16
+#define AFE_DL_GAIN1_CTL_PRE_MASK 0xffff
+#define AFE_DL_GAIN1_CTL_PRE_MASK_SFT (0xffff << 16)
+#define AFE_DL_GAIN2_CTL_PRE_SFT 0
+#define AFE_DL_GAIN2_CTL_PRE_MASK 0xffff
+#define AFE_DL_GAIN2_CTL_PRE_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_DL_SRC_DEBUG_MON0 */
+#define AFE_DL_SLT_CNT_FLAG_CTL_SFT 15
+#define AFE_DL_SLT_CNT_FLAG_CTL_MASK 0x1
+#define AFE_DL_SLT_CNT_FLAG_CTL_MASK_SFT (0x1 << 15)
+#define AFE_DL_INI_SRAM_FINISH_CTL_SFT 12
+#define AFE_DL_INI_SRAM_FINISH_CTL_MASK 0x1
+#define AFE_DL_INI_SRAM_FINISH_CTL_MASK_SFT (0x1 << 12)
+#define AFE_DL_SLT_COUNTER_CTL_SFT 0
+#define AFE_DL_SLT_COUNTER_CTL_MASK 0xfff
+#define AFE_DL_SLT_COUNTER_CTL_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_DL_PREDIS_CON0 */
+#define AFE_DL_PREDIS_ON_CH1_CTL_SFT 31
+#define AFE_DL_PREDIS_ON_CH1_CTL_MASK 0x1
+#define AFE_DL_PREDIS_ON_CH1_CTL_MASK_SFT (0x1 << 31)
+#define AFE_DL_PREDIS_A2_CH1_CTL_SFT 16
+#define AFE_DL_PREDIS_A2_CH1_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A2_CH1_CTL_MASK_SFT (0xfff << 16)
+#define AFE_DL_PREDIS_A3_CH1_CTL_SFT 0
+#define AFE_DL_PREDIS_A3_CH1_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A3_CH1_CTL_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_DL_PREDIS_CON1 */
+#define AFE_DL_PREDIS_ON_CH2_CTL_SFT 31
+#define AFE_DL_PREDIS_ON_CH2_CTL_MASK 0x1
+#define AFE_DL_PREDIS_ON_CH2_CTL_MASK_SFT (0x1 << 31)
+#define AFE_DL_PREDIS_A2_CH2_CTL_SFT 16
+#define AFE_DL_PREDIS_A2_CH2_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A2_CH2_CTL_MASK_SFT (0xfff << 16)
+#define AFE_DL_PREDIS_A3_CH2_CTL_SFT 0
+#define AFE_DL_PREDIS_A3_CH2_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A3_CH2_CTL_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_DL_PREDIS_CON2 */
+#define AFE_DL_PREDIS_A4_CH1_CTL_SFT 16
+#define AFE_DL_PREDIS_A4_CH1_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A4_CH1_CTL_MASK_SFT (0xfff << 16)
+#define AFE_DL_PREDIS_A5_CH1_CTL_SFT 0
+#define AFE_DL_PREDIS_A5_CH1_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A5_CH1_CTL_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_DL_PREDIS_CON3 */
+#define AFE_DL_PREDIS_A4_CH2_CTL_SFT 16
+#define AFE_DL_PREDIS_A4_CH2_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A4_CH2_CTL_MASK_SFT (0xfff << 16)
+#define AFE_DL_PREDIS_A5_CH2_CTL_SFT 0
+#define AFE_DL_PREDIS_A5_CH2_CTL_MASK 0xfff
+#define AFE_DL_PREDIS_A5_CH2_CTL_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define AFE_DL_USE_NEW_2ND_12BIT_SDM_SFT 31
+#define AFE_DL_USE_NEW_2ND_12BIT_SDM_MASK 0x1
+#define AFE_DL_USE_NEW_2ND_12BIT_SDM_MASK_SFT (0x1 << 31)
+#define AFE_DL_USE_NEW_2ND_SDM_SFT 30
+#define AFE_DL_USE_NEW_2ND_SDM_MASK 0x1
+#define AFE_DL_USE_NEW_2ND_SDM_MASK_SFT (0x1 << 30)
+#define AFE_DL_USE_3RD_SDM_SFT 28
+#define AFE_DL_USE_3RD_SDM_MASK 0x1
+#define AFE_DL_USE_3RD_SDM_MASK_SFT (0x1 << 28)
+#define AFE_DL_DCM_AUTO_IDLE_EN_SFT 14
+#define AFE_DL_DCM_AUTO_IDLE_EN_MASK 0x1
+#define AFE_DL_DCM_AUTO_IDLE_EN_MASK_SFT (0x1 << 14)
+#define AFE_DL_SRC_DCM_EN_SFT 13
+#define AFE_DL_SRC_DCM_EN_MASK 0x1
+#define AFE_DL_SRC_DCM_EN_MASK_SFT (0x1 << 13)
+#define AFE_DL_POST_SRC_DCM_EN_SFT 12
+#define AFE_DL_POST_SRC_DCM_EN_MASK 0x1
+#define AFE_DL_POST_SRC_DCM_EN_MASK_SFT (0x1 << 12)
+#define AFE_DL_DCCOMP_SYNC_TOGGLE_SFT 11
+#define AFE_DL_DCCOMP_SYNC_TOGGLE_MASK 0x1
+#define AFE_DL_DCCOMP_SYNC_TOGGLE_MASK_SFT (0x1 << 11)
+#define AFE_DL_AUD_SDM_MONO_SFT 9
+#define AFE_DL_AUD_SDM_MONO_MASK 0x1
+#define AFE_DL_AUD_SDM_MONO_MASK_SFT (0x1 << 9)
+#define AFE_DL_AUD_DC_COMP_EN_SFT 8
+#define AFE_DL_AUD_DC_COMP_EN_MASK 0x1
+#define AFE_DL_AUD_DC_COMP_EN_MASK_SFT (0x1 << 8)
+#define AFE_DL_ATTGAIN_CTL_SFT 0
+#define AFE_DL_ATTGAIN_CTL_MASK 0x3f
+#define AFE_DL_ATTGAIN_CTL_MASK_SFT (0x3f << 0)
+
+/* AFE_ADDA_DL_SDM_TEST */
+#define AFE_DL_TRI_AMP_DIV_SFT 12
+#define AFE_DL_TRI_AMP_DIV_MASK 0x7
+#define AFE_DL_TRI_AMP_DIV_MASK_SFT (0x7 << 12)
+#define AFE_DL_TRI_FREQ_DIV_SFT 4
+#define AFE_DL_TRI_FREQ_DIV_MASK 0x3f
+#define AFE_DL_TRI_FREQ_DIV_MASK_SFT (0x3f << 4)
+#define AFE_DL_RG_DL_LEFT_SAT_RSTN_SFT 3
+#define AFE_DL_RG_DL_LEFT_SAT_RSTN_MASK 0x1
+#define AFE_DL_RG_DL_LEFT_SAT_RSTN_MASK_SFT (0x1 << 3)
+#define AFE_DL_RG_DL_RIGHT_SAT_RSTN_SFT 2
+#define AFE_DL_RG_DL_RIGHT_SAT_RSTN_MASK 0x1
+#define AFE_DL_RG_DL_RIGHT_SAT_RSTN_MASK_SFT (0x1 << 2)
+#define AFE_DL_TRI_MUTE_SW_SFT 1
+#define AFE_DL_TRI_MUTE_SW_MASK 0x1
+#define AFE_DL_TRI_MUTE_SW_MASK_SFT (0x1 << 1)
+#define AFE_DL_TRI_DAC_EN_SFT 0
+#define AFE_DL_TRI_DAC_EN_MASK 0x1
+#define AFE_DL_TRI_DAC_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_DC_COMP_CFG0 */
+#define AFE_DL_AUD_DC_COMP_LCH_H_SFT 16
+#define AFE_DL_AUD_DC_COMP_LCH_H_MASK 0xffff
+#define AFE_DL_AUD_DC_COMP_LCH_H_MASK_SFT (0xffff << 16)
+#define AFE_DL_AUD_DC_COMP_LCH_L_SFT 0
+#define AFE_DL_AUD_DC_COMP_LCH_L_MASK 0xffff
+#define AFE_DL_AUD_DC_COMP_LCH_L_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_DL_DC_COMP_CFG1 */
+#define AFE_DL_AUD_DC_COMP_RCH_H_SFT 16
+#define AFE_DL_AUD_DC_COMP_RCH_H_MASK 0xffff
+#define AFE_DL_AUD_DC_COMP_RCH_H_MASK_SFT (0xffff << 16)
+#define AFE_DL_AUD_DC_COMP_RCH_L_SFT 0
+#define AFE_DL_AUD_DC_COMP_RCH_L_MASK 0xffff
+#define AFE_DL_AUD_DC_COMP_RCH_L_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_DL_SDM_OUT_MON */
+#define AFE_DL_SDM_DITHER_MON_SFT 28
+#define AFE_DL_SDM_DITHER_MON_MASK 0x3
+#define AFE_DL_SDM_DITHER_MON_MASK_SFT (0x3 << 28)
+#define AFE_DL_BF_SDM_LEFT_SAT_SFT 21
+#define AFE_DL_BF_SDM_LEFT_SAT_MASK 0x1
+#define AFE_DL_BF_SDM_LEFT_SAT_MASK_SFT (0x1 << 21)
+#define AFE_DL_BF_SDM_RIGHT_SAT_SFT 20
+#define AFE_DL_BF_SDM_RIGHT_SAT_MASK 0x1
+#define AFE_DL_BF_SDM_RIGHT_SAT_MASK_SFT (0x1 << 20)
+#define AFE_DL_3RD_SDM_AUTO_RESET_R_SFT 19
+#define AFE_DL_3RD_SDM_AUTO_RESET_R_MASK 0x1
+#define AFE_DL_3RD_SDM_AUTO_RESET_R_MASK_SFT (0x1 << 19)
+#define AFE_DL_3RD_SDM_AUTO_RESET_L_SFT 18
+#define AFE_DL_3RD_SDM_AUTO_RESET_L_MASK 0x1
+#define AFE_DL_3RD_SDM_AUTO_RESET_L_MASK_SFT (0x1 << 18)
+#define AFE_DL_2ND_SDM_AUTO_RESET_R_SFT 17
+#define AFE_DL_2ND_SDM_AUTO_RESET_R_MASK 0x1
+#define AFE_DL_2ND_SDM_AUTO_RESET_R_MASK_SFT (0x1 << 17)
+#define AFE_DL_2ND_SDM_AUTO_RESET_L_SFT 16
+#define AFE_DL_2ND_SDM_AUTO_RESET_L_MASK 0x1
+#define AFE_DL_2ND_SDM_AUTO_RESET_L_MASK_SFT (0x1 << 16)
+#define AFE_DL_AUD_SDM_OUT_L_SFT 8
+#define AFE_DL_AUD_SDM_OUT_L_MASK 0xff
+#define AFE_DL_AUD_SDM_OUT_L_MASK_SFT (0xff << 8)
+#define AFE_DL_AUD_SDM_OUT_R_SFT 0
+#define AFE_DL_AUD_SDM_OUT_R_MASK 0xff
+#define AFE_DL_AUD_SDM_OUT_R_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_DL_SRC_LCH_MON */
+#define AFE_DL_ASDM_LEFT_SFT 0
+#define AFE_DL_ASDM_LEFT_MASK 0xffffff
+#define AFE_DL_ASDM_LEFT_MASK_SFT (0xffffff << 0)
+
+/* AFE_ADDA_DL_SRC_RCH_MON */
+#define AFE_DL_ASDM_RIGHT_SFT 0
+#define AFE_DL_ASDM_RIGHT_MASK 0xffffff
+#define AFE_DL_ASDM_RIGHT_MASK_SFT (0xffffff << 0)
+
+/* AFE_ADDA_DL_SRC_DEBUG */
+#define AFE_DL_SLT_CNT_FLAG_RESET_CTL_SFT 12
+#define AFE_DL_SLT_CNT_FLAG_RESET_CTL_MASK 0x1
+#define AFE_DL_SLT_CNT_FLAG_RESET_CTL_MASK_SFT (0x1 << 12)
+#define AFE_DL_SLT_CNT_THD_CTL_SFT 0
+#define AFE_DL_SLT_CNT_THD_CTL_MASK 0xfff
+#define AFE_DL_SLT_CNT_THD_CTL_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_DL_SDM_DITHER_CON */
+#define AFE_DL_SDM_DITHER_64TAP_EN_SFT 20
+#define AFE_DL_SDM_DITHER_64TAP_EN_MASK 0x1
+#define AFE_DL_SDM_DITHER_64TAP_EN_MASK_SFT (0x1 << 20)
+#define AFE_DL_SDM_DITHER_EN_SFT 16
+#define AFE_DL_SDM_DITHER_EN_MASK 0x1
+#define AFE_DL_SDM_DITHER_EN_MASK_SFT (0x1 << 16)
+#define AFE_DL_SDM_DITHER_GAIN_SFT 0
+#define AFE_DL_SDM_DITHER_GAIN_MASK 0xff
+#define AFE_DL_SDM_DITHER_GAIN_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_DL_SDM_AUTO_RESET_CON */
+#define AFE_DL_SDM_AUTO_RESET_TEST_ON_SFT 31
+#define AFE_DL_SDM_AUTO_RESET_TEST_ON_MASK 0x1
+#define AFE_DL_SDM_AUTO_RESET_TEST_ON_MASK_SFT (0x1 << 31)
+#define AFE_DL_SDM_AUTO_RESET_SOURCE_SEL_SFT 24
+#define AFE_DL_SDM_AUTO_RESET_SOURCE_SEL_MASK 0x1
+#define AFE_DL_SDM_AUTO_RESET_SOURCE_SEL_MASK_SFT (0x1 << 24)
+#define AFE_DL_SDM_AUTO_RESET_COUNT_TH_SFT 0
+#define AFE_DL_SDM_AUTO_RESET_COUNT_TH_MASK 0xffffff
+#define AFE_DL_SDM_AUTO_RESET_COUNT_TH_MASK_SFT (0xffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_CONFIG */
+#define AFE_DL_HBF1_SW_CONFIG_SFT 31
+#define AFE_DL_HBF1_SW_CONFIG_MASK 0x1
+#define AFE_DL_HBF1_SW_CONFIG_MASK_SFT (0x1 << 31)
+#define AFE_DL_HBF1_TAPNUM_CONFIG_SFT 16
+#define AFE_DL_HBF1_TAPNUM_CONFIG_MASK 0x7f
+#define AFE_DL_HBF1_TAPNUM_CONFIG_MASK_SFT (0x7f << 16)
+#define AFE_DL_SCF1_SW_CONFIG_SFT 8
+#define AFE_DL_SCF1_SW_CONFIG_MASK 0x1
+#define AFE_DL_SCF1_SW_CONFIG_MASK_SFT (0x1 << 8)
+#define AFE_DL_SCF1_TAPNUM_CONFIG_SFT 0
+#define AFE_DL_SCF1_TAPNUM_CONFIG_MASK 0xff
+#define AFE_DL_SCF1_TAPNUM_CONFIG_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP1_TAP2_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP1_TAP2_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP1_TAP2_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP1_TAP2_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP3_TAP4_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP3_TAP4_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP3_TAP4_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP3_TAP4_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP5_TAP6_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP5_TAP6_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP5_TAP6_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP5_TAP6_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP7_TAP8_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP7_TAP8_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP7_TAP8_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP7_TAP8_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP9_TAP10_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP9_TAP10_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP9_TAP10_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP9_TAP10_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP11_TAP12_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP11_TAP12_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP11_TAP12_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP11_TAP12_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP13_TAP14_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP13_TAP14_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP13_TAP14_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP13_TAP14_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP15_TAP16_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP15_TAP16_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP15_TAP16_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP15_TAP16_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP17_TAP18_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP17_TAP18_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP17_TAP18_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP17_TAP18_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP19_TAP20_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP19_TAP20_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP19_TAP20_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP19_TAP20_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP21_TAP22_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP21_TAP22_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP21_TAP22_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP21_TAP22_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP23_TAP24_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP23_TAP24_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP23_TAP24_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP23_TAP24_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP25_TAP26_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP25_TAP26_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP25_TAP26_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP25_TAP26_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP27_TAP28_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP27_TAP28_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP27_TAP28_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP27_TAP28_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP29_TAP30_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP29_TAP30_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP29_TAP30_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP29_TAP30_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP31_TAP32_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP31_TAP32_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP31_TAP32_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP31_TAP32_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP33_TAP34_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP33_TAP34_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP33_TAP34_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP33_TAP34_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP35_TAP36_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP35_TAP36_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP35_TAP36_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP35_TAP36_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP37_TAP38_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP37_TAP38_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP37_TAP38_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP37_TAP38_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP39_TAP40_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP39_TAP40_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP39_TAP40_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP39_TAP40_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP41_TAP42_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP41_TAP42_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP41_TAP42_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP41_TAP42_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP43_TAP44_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP43_TAP44_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP43_TAP44_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP43_TAP44_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP45_TAP46_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP45_TAP46_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP45_TAP46_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP45_TAP46_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP47_TAP48_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP47_TAP48_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP47_TAP48_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP47_TAP48_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP49_TAP50_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP49_TAP50_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP49_TAP50_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP49_TAP50_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP51_TAP52_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP51_TAP52_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP51_TAP52_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP51_TAP52_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP53_TAP54_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP53_TAP54_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP53_TAP54_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP53_TAP54_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_DL_HBF1_SCF1_TAP55_TAP56_CONFIG */
+#define AFE_DL_HBF1_SCF1_TAP55_TAP56_CONFIG_SFT 0
+#define AFE_DL_HBF1_SCF1_TAP55_TAP56_CONFIG_MASK 0xffffffff
+#define AFE_DL_HBF1_SCF1_TAP55_TAP56_CONFIG_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_NLE_R_CFG0 */
+#define RG_NLE_R_GAIN_DIG_TAR_SFT 24
+#define RG_NLE_R_GAIN_DIG_TAR_MASK 0x3f
+#define RG_NLE_R_GAIN_DIG_TAR_MASK_SFT (0x3f << 24)
+#define RG_NLE_R_GAIN_ANA_TAR_SFT 16
+#define RG_NLE_R_GAIN_ANA_TAR_MASK 0x3f
+#define RG_NLE_R_GAIN_ANA_TAR_MASK_SFT (0x3f << 16)
+#define RG_NLE_R_NO_ZCE_SFT 15
+#define RG_NLE_R_NO_ZCE_MASK 0x1
+#define RG_NLE_R_NO_ZCE_MASK_SFT (0x1 << 15)
+#define RG_NLE_R_HP_MODE_SFT 14
+#define RG_NLE_R_HP_MODE_MASK 0x1
+#define RG_NLE_R_HP_MODE_MASK_SFT (0x1 << 14)
+#define RG_NLE_R_GAIN_STEP_SFT 8
+#define RG_NLE_R_GAIN_STEP_MASK 0x7
+#define RG_NLE_R_GAIN_STEP_MASK_SFT (0x7 << 8)
+#define RG_NLE_R_TOGGLE_NUM_SFT 0
+#define RG_NLE_R_TOGGLE_NUM_MASK 0x3f
+#define RG_NLE_R_TOGGLE_NUM_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_NLE_R_CFG1 */
+#define RG_NLE_R_INITIATE_SFT 24
+#define RG_NLE_R_INITIATE_MASK 0x1
+#define RG_NLE_R_INITIATE_MASK_SFT (0x1 << 24)
+#define RG_NLE_R_READY_SFT 16
+#define RG_NLE_R_READY_MASK 0x1
+#define RG_NLE_R_READY_MASK_SFT (0x1 << 16)
+#define RG_NLE_R_TIMEOUT_SCALE_SFT 12
+#define RG_NLE_R_TIMEOUT_SCALE_MASK 0x7
+#define RG_NLE_R_TIMEOUT_SCALE_MASK_SFT (0x7 << 12)
+#define RG_NLE_R_ANC_ON_SFT 11
+#define RG_NLE_R_ANC_ON_MASK 0x1
+#define RG_NLE_R_ANC_ON_MASK_SFT (0x1 << 11)
+#define RG_NLE_R_GTIME_SFT 8
+#define RG_NLE_R_GTIME_MASK 0x7
+#define RG_NLE_R_GTIME_MASK_SFT (0x7 << 8)
+#define RG_NLE_R_ON_SFT 7
+#define RG_NLE_R_ON_MASK 0x1
+#define RG_NLE_R_ON_MASK_SFT (0x1 << 7)
+#define RG_PDN_NLE_CTL_SFT 6
+#define RG_PDN_NLE_CTL_MASK 0x1
+#define RG_PDN_NLE_CTL_MASK_SFT (0x1 << 6)
+#define RG_NLE_R_DELAY_ANA_SFT 0
+#define RG_NLE_R_DELAY_ANA_MASK 0x3f
+#define RG_NLE_R_DELAY_ANA_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_NLE_L_CFG0 */
+#define RG_NLE_L_GAIN_DIG_TAR_SFT 24
+#define RG_NLE_L_GAIN_DIG_TAR_MASK 0x3f
+#define RG_NLE_L_GAIN_DIG_TAR_MASK_SFT (0x3f << 24)
+#define RG_NLE_L_GAIN_ANA_TAR_SFT 16
+#define RG_NLE_L_GAIN_ANA_TAR_MASK 0x3f
+#define RG_NLE_L_GAIN_ANA_TAR_MASK_SFT (0x3f << 16)
+#define RG_NLE_L_NO_ZCE_SFT 15
+#define RG_NLE_L_NO_ZCE_MASK 0x1
+#define RG_NLE_L_NO_ZCE_MASK_SFT (0x1 << 15)
+#define RG_NLE_L_HP_MODE_SFT 14
+#define RG_NLE_L_HP_MODE_MASK 0x1
+#define RG_NLE_L_HP_MODE_MASK_SFT (0x1 << 14)
+#define RG_NLE_L_GAIN_STEP_SFT 8
+#define RG_NLE_L_GAIN_STEP_MASK 0x7
+#define RG_NLE_L_GAIN_STEP_MASK_SFT (0x7 << 8)
+#define RG_NLE_L_TOGGLE_NUM_SFT 0
+#define RG_NLE_L_TOGGLE_NUM_MASK 0x3f
+#define RG_NLE_L_TOGGLE_NUM_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_NLE_L_CFG1 */
+#define RG_NLE_L_INITIATE_SFT 24
+#define RG_NLE_L_INITIATE_MASK 0x1
+#define RG_NLE_L_INITIATE_MASK_SFT (0x1 << 24)
+#define RG_NLE_L_READY_SFT 16
+#define RG_NLE_L_READY_MASK 0x1
+#define RG_NLE_L_READY_MASK_SFT (0x1 << 16)
+#define RG_NLE_L_TIMEOUT_SCALE_SFT 12
+#define RG_NLE_L_TIMEOUT_SCALE_MASK 0x7
+#define RG_NLE_L_TIMEOUT_SCALE_MASK_SFT (0x7 << 12)
+#define RG_NLE_L_ANC_ON_SFT 11
+#define RG_NLE_L_ANC_ON_MASK 0x1
+#define RG_NLE_L_ANC_ON_MASK_SFT (0x1 << 11)
+#define RG_NLE_L_GTIME_SFT 8
+#define RG_NLE_L_GTIME_MASK 0x7
+#define RG_NLE_L_GTIME_MASK_SFT (0x7 << 8)
+#define RG_NLE_L_ON_SFT 7
+#define RG_NLE_L_ON_MASK 0x1
+#define RG_NLE_L_ON_MASK_SFT (0x1 << 7)
+#define RG_PDN_NLE_CTL_SFT 6
+#define RG_PDN_NLE_CTL_MASK 0x1
+#define RG_PDN_NLE_CTL_MASK_SFT (0x1 << 6)
+#define RG_NLE_L_DELAY_ANA_SFT 0
+#define RG_NLE_L_DELAY_ANA_MASK 0x3f
+#define RG_NLE_L_DELAY_ANA_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_NLE_R_MON0 */
+#define NLE_R_GAIN_DIG_CUR_SFT 24
+#define NLE_R_GAIN_DIG_CUR_MASK 0x3f
+#define NLE_R_GAIN_DIG_CUR_MASK_SFT (0x3f << 24)
+#define NLE_R_ANC_MASK_SFT 23
+#define NLE_R_ANC_MASK_MASK 0x1
+#define NLE_R_ANC_MASK_MASK_SFT (0x1 << 23)
+#define NLE_R_GAIN_ANA_CUR_SFT 16
+#define NLE_R_GAIN_ANA_CUR_MASK 0x3f
+#define NLE_R_GAIN_ANA_CUR_MASK_SFT (0x3f << 16)
+#define NLE_R_GAIN_DIG_TAR_CUR_SFT 8
+#define NLE_R_GAIN_DIG_TAR_CUR_MASK 0x3f
+#define NLE_R_GAIN_DIG_TAR_CUR_MASK_SFT (0x3f << 8)
+#define NLE_R_GAIN_ANA_TAR_CUR_SFT 0
+#define NLE_R_GAIN_ANA_TAR_CUR_MASK 0x3f
+#define NLE_R_GAIN_ANA_TAR_CUR_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_NLE_R_MON1 */
+#define NLE_R_STATE_CUR_SFT 28
+#define NLE_R_STATE_CUR_MASK 0x7
+#define NLE_R_STATE_CUR_MASK_SFT (0x7 << 28)
+#define NLE_R_GAIN_STEP_CUR_SFT 24
+#define NLE_R_GAIN_STEP_CUR_MASK 0xf
+#define NLE_R_GAIN_STEP_CUR_MASK_SFT (0xf << 24)
+#define NLE_R_TOGGLE_NUM_CUR_SFT 16
+#define NLE_R_TOGGLE_NUM_CUR_MASK 0x3f
+#define NLE_R_TOGGLE_NUM_CUR_MASK_SFT (0x3f << 16)
+#define NLE_R_DIG_GAIN_TARGETED_SFT 15
+#define NLE_R_DIG_GAIN_TARGETED_MASK 0x1
+#define NLE_R_DIG_GAIN_TARGETED_MASK_SFT (0x1 << 15)
+#define NLE_R_DIG_GAIN_INCREASE_SFT 14
+#define NLE_R_DIG_GAIN_INCREASE_MASK 0x1
+#define NLE_R_DIG_GAIN_INCREASE_MASK_SFT (0x1 << 14)
+#define NLE_R_DIG_GAIN_DECREASE_SFT 13
+#define NLE_R_DIG_GAIN_DECREASE_MASK 0x1
+#define NLE_R_DIG_GAIN_DECREASE_MASK_SFT (0x1 << 13)
+#define NLE_R_ANA_GAIN_TARGETED_SFT 12
+#define NLE_R_ANA_GAIN_TARGETED_MASK 0x1
+#define NLE_R_ANA_GAIN_TARGETED_MASK_SFT (0x1 << 12)
+#define NLE_R_ANA_GAIN_INCREASE_SFT 11
+#define NLE_R_ANA_GAIN_INCREASE_MASK 0x1
+#define NLE_R_ANA_GAIN_INCREASE_MASK_SFT (0x1 << 11)
+#define NLE_R_ANA_GAIN_DECREASE_SFT 10
+#define NLE_R_ANA_GAIN_DECREASE_MASK 0x1
+#define NLE_R_ANA_GAIN_DECREASE_MASK_SFT (0x1 << 10)
+#define NLE_R_TIME_COUNTER_CUR_SFT 0
+#define NLE_R_TIME_COUNTER_CUR_MASK 0x1ff
+#define NLE_R_TIME_COUNTER_CUR_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL_NLE_R_MON2 */
+#define NLE_R_ANA_GAIN_SFT 8
+#define NLE_R_ANA_GAIN_MASK 0x1f
+#define NLE_R_ANA_GAIN_MASK_SFT (0x1f << 8)
+#define NLE_MOSI2_ANA_GAIN_SFT 0
+#define NLE_MOSI2_ANA_GAIN_MASK 0x7f
+#define NLE_MOSI2_ANA_GAIN_MASK_SFT (0x7f << 0)
+
+/* AFE_DL_NLE_L_MON0 */
+#define NLE_L_GAIN_DIG_CUR_SFT 24
+#define NLE_L_GAIN_DIG_CUR_MASK 0x3f
+#define NLE_L_GAIN_DIG_CUR_MASK_SFT (0x3f << 24)
+#define NLE_L_ANC_MASK_SFT 23
+#define NLE_L_ANC_MASK_MASK 0x1
+#define NLE_L_ANC_MASK_MASK_SFT (0x1 << 23)
+#define NLE_L_GAIN_ANA_CUR_SFT 16
+#define NLE_L_GAIN_ANA_CUR_MASK 0x3f
+#define NLE_L_GAIN_ANA_CUR_MASK_SFT (0x3f << 16)
+#define NLE_L_GAIN_DIG_TAR_CUR_SFT 8
+#define NLE_L_GAIN_DIG_TAR_CUR_MASK 0x3f
+#define NLE_L_GAIN_DIG_TAR_CUR_MASK_SFT (0x3f << 8)
+#define NLE_L_GAIN_ANA_TAR_CUR_SFT 0
+#define NLE_L_GAIN_ANA_TAR_CUR_MASK 0x3f
+#define NLE_L_GAIN_ANA_TAR_CUR_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_NLE_L_MON1 */
+#define NLE_L_STATE_CUR_SFT 28
+#define NLE_L_STATE_CUR_MASK 0x7
+#define NLE_L_STATE_CUR_MASK_SFT (0x7 << 28)
+#define NLE_L_GAIN_STEP_CUR_SFT 24
+#define NLE_L_GAIN_STEP_CUR_MASK 0xf
+#define NLE_L_GAIN_STEP_CUR_MASK_SFT (0xf << 24)
+#define NLE_L_TOGGLE_NUM_CUR_SFT 16
+#define NLE_L_TOGGLE_NUM_CUR_MASK 0x3f
+#define NLE_L_TOGGLE_NUM_CUR_MASK_SFT (0x3f << 16)
+#define NLE_L_DIG_GAIN_TARGETED_SFT 15
+#define NLE_L_DIG_GAIN_TARGETED_MASK 0x1
+#define NLE_L_DIG_GAIN_TARGETED_MASK_SFT (0x1 << 15)
+#define NLE_L_DIG_GAIN_INCREASE_SFT 14
+#define NLE_L_DIG_GAIN_INCREASE_MASK 0x1
+#define NLE_L_DIG_GAIN_INCREASE_MASK_SFT (0x1 << 14)
+#define NLE_L_DIG_GAIN_DECREASE_SFT 13
+#define NLE_L_DIG_GAIN_DECREASE_MASK 0x1
+#define NLE_L_DIG_GAIN_DECREASE_MASK_SFT (0x1 << 13)
+#define NLE_L_ANA_GAIN_TARGETED_SFT 12
+#define NLE_L_ANA_GAIN_TARGETED_MASK 0x1
+#define NLE_L_ANA_GAIN_TARGETED_MASK_SFT (0x1 << 12)
+#define NLE_L_ANA_GAIN_INCREASE_SFT 11
+#define NLE_L_ANA_GAIN_INCREASE_MASK 0x1
+#define NLE_L_ANA_GAIN_INCREASE_MASK_SFT (0x1 << 11)
+#define NLE_L_ANA_GAIN_DECREASE_SFT 10
+#define NLE_L_ANA_GAIN_DECREASE_MASK 0x1
+#define NLE_L_ANA_GAIN_DECREASE_MASK_SFT (0x1 << 10)
+#define NLE_L_TIME_COUNTER_CUR_SFT 0
+#define NLE_L_TIME_COUNTER_CUR_MASK 0x1ff
+#define NLE_L_TIME_COUNTER_CUR_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL_NLE_L_MON2 */
+#define NLE_L_ANA_GAIN_SFT 8
+#define NLE_L_ANA_GAIN_MASK 0x1f
+#define NLE_L_ANA_GAIN_MASK_SFT (0x1f << 8)
+#define NLE_MOSI1_ANA_GAIN_SFT 0
+#define NLE_MOSI1_ANA_GAIN_MASK 0x7f
+#define NLE_MOSI1_ANA_GAIN_MASK_SFT (0x7f << 0)
+
+/* AFE_DL_NLE_GAIN_CFG0 */
+#define MISO2_SEL_SFT 4
+#define MISO2_SEL_MASK 0x3
+#define MISO2_SEL_MASK_SFT (0x3 << 4)
+#define MISO1_SEL_SFT 0
+#define MISO1_SEL_MASK 0x3
+#define MISO1_SEL_MASK_SFT (0x3 << 0)
+
+/* AFE_DEM_IDWA_CON0 */
+#define RG_IDWA_SDM_MAV_EN_SFT 31
+#define RG_IDWA_SDM_MAV_EN_MASK 0x1
+#define RG_IDWA_SDM_MAV_EN_MASK_SFT (0x1 << 31)
+#define RG_IDWA_SDM_ADITHON_SFT 30
+#define RG_IDWA_SDM_ADITHON_MASK 0x1
+#define RG_IDWA_SDM_ADITHON_MASK_SFT (0x1 << 30)
+#define RG_IDWA_SDM_ADITHVAL_SFT 28
+#define RG_IDWA_SDM_ADITHVAL_MASK 0x3
+#define RG_IDWA_SDM_ADITHVAL_MASK_SFT (0x3 << 28)
+#define RG_IDWA_SDM_LOOPBACK_SFT 27
+#define RG_IDWA_SDM_LOOPBACK_MASK 0x1
+#define RG_IDWA_SDM_LOOPBACK_MASK_SFT (0x1 << 27)
+#define RG_IDWA_SEL_SFT 26
+#define RG_IDWA_SEL_MASK 0x1
+#define RG_IDWA_SEL_MASK_SFT (0x1 << 26)
+#define RG_IDWA_ON_SFT 25
+#define RG_IDWA_ON_MASK 0x1
+#define RG_IDWA_ON_MASK_SFT (0x1 << 25)
+#define RG_DEM_IN_LR_SWAP_SFT 24
+#define RG_DEM_IN_LR_SWAP_MASK 0x1
+#define RG_DEM_IN_LR_SWAP_MASK_SFT (0x1 << 24)
+#define RG_DEM_IN_L_INV_SFT 23
+#define RG_DEM_IN_L_INV_MASK 0x1
+#define RG_DEM_IN_L_INV_MASK_SFT (0x1 << 23)
+#define RG_DEM_IN_R_EQ_L_SFT 22
+#define RG_DEM_IN_R_EQ_L_MASK 0x1
+#define RG_DEM_IN_R_EQ_L_MASK_SFT (0x1 << 22)
+#define RG_DEM_IN_L_MUTE_SFT 21
+#define RG_DEM_IN_L_MUTE_MASK 0x1
+#define RG_DEM_IN_L_MUTE_MASK_SFT (0x1 << 21)
+#define RG_DEM_IN_R_MUTE_SFT 20
+#define RG_DEM_IN_R_MUTE_MASK 0x1
+#define RG_DEM_IN_R_MUTE_MASK_SFT (0x1 << 20)
+#define RG_DEM_IN_SOURCE_SFT 19
+#define RG_DEM_IN_SOURCE_MASK 0x1
+#define RG_DEM_IN_SOURCE_MASK_SFT (0x1 << 19)
+#define RG_DEM_SPLITTER_TRUNC_RND_SFT 18
+#define RG_DEM_SPLITTER_TRUNC_RND_MASK 0x1
+#define RG_DEM_SPLITTER_TRUNC_RND_MASK_SFT (0x1 << 18)
+#define RG_DEM_SCRAMBLER_CG_EN_SFT 17
+#define RG_DEM_SCRAMBLER_CG_EN_MASK 0x1
+#define RG_DEM_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 17)
+#define RG_DEM_SCRAMBLER_EN_SFT 16
+#define RG_DEM_SCRAMBLER_EN_MASK 0x1
+#define RG_DEM_SCRAMBLER_EN_MASK_SFT (0x1 << 16)
+#define RG_DEM_AUD_SDM_7BIT_SEL_SFT 15
+#define RG_DEM_AUD_SDM_7BIT_SEL_MASK 0x1
+#define RG_DEM_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 15)
+#define RG_DEM_ZERO_PAD_DISABLE_SFT 14
+#define RG_DEM_ZERO_PAD_DISABLE_MASK 0x1
+#define RG_DEM_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 14)
+#define RG_DEM_SPLITTER_TEST_EN_SFT 13
+#define RG_DEM_SPLITTER_TEST_EN_MASK 0x1
+#define RG_DEM_SPLITTER_TEST_EN_MASK_SFT (0x1 << 13)
+#define RG_DEM_IDAC_TEST_EN_SFT 12
+#define RG_DEM_IDAC_TEST_EN_MASK 0x1
+#define RG_DEM_IDAC_TEST_EN_MASK_SFT (0x1 << 12)
+#define RG_DEM_SPLIT_SCRAM_ON_SFT 11
+#define RG_DEM_SPLIT_SCRAM_ON_MASK 0x1
+#define RG_DEM_SPLIT_SCRAM_ON_MASK_SFT (0x1 << 11)
+#define RG_DEM_RAND_EN_SFT 10
+#define RG_DEM_RAND_EN_MASK 0x1
+#define RG_DEM_RAND_EN_MASK_SFT (0x1 << 10)
+#define RG_DEM_SPLITTER2_DITHER_EN_SFT 9
+#define RG_DEM_SPLITTER2_DITHER_EN_MASK 0x1
+#define RG_DEM_SPLITTER2_DITHER_EN_MASK_SFT (0x1 << 9)
+#define RG_DEM_SPLITTER1_DITHER_EN_SFT 8
+#define RG_DEM_SPLITTER1_DITHER_EN_MASK 0x1
+#define RG_DEM_SPLITTER1_DITHER_EN_MASK_SFT (0x1 << 8)
+#define RG_DEM_SPLITTER2_DITHER_GAIN_SFT 4
+#define RG_DEM_SPLITTER2_DITHER_GAIN_MASK 0xf
+#define RG_DEM_SPLITTER2_DITHER_GAIN_MASK_SFT (0xf << 4)
+#define RG_DEM_SPLITTER1_DITHER_GAIN_SFT 0
+#define RG_DEM_SPLITTER1_DITHER_GAIN_MASK 0xf
+#define RG_DEM_SPLITTER1_DITHER_GAIN_MASK_SFT (0xf << 0)
+
+/* DEM_RECONSTRUCT_MON */
+#define DEM_RECONSTRUCT_L_MON_SFT 8
+#define DEM_RECONSTRUCT_L_MON_MASK 0xff
+#define DEM_RECONSTRUCT_L_MON_MASK_SFT (0xff << 8)
+#define DEM_RECONSTRUCT_R_MON_SFT 0
+#define DEM_RECONSTRUCT_R_MON_MASK 0xff
+#define DEM_RECONSTRUCT_R_MON_MASK_SFT (0xff << 0)
+
+/* AFE_STF_CON0 */
+#define SLT_CNT_FLAG_RESET_SFT 28
+#define SLT_CNT_FLAG_RESET_MASK 0x1
+#define SLT_CNT_FLAG_RESET_MASK_SFT (0x1 << 28)
+#define SLT_CNT_THD_SFT 16
+#define SLT_CNT_THD_MASK 0xfff
+#define SLT_CNT_THD_MASK_SFT (0xfff << 16)
+#define SIDE_TONE_HALF_TAP_NUM_SFT 4
+#define SIDE_TONE_HALF_TAP_NUM_MASK 0x7f
+#define SIDE_TONE_HALF_TAP_NUM_MASK_SFT (0x7f << 4)
+#define SIDE_TONE_ODD_MODE_SFT 1
+#define SIDE_TONE_ODD_MODE_MASK 0x1
+#define SIDE_TONE_ODD_MODE_MASK_SFT (0x1 << 1)
+#define SIDE_TONE_ON_SFT 0
+#define SIDE_TONE_ON_MASK 0x1
+#define SIDE_TONE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_STF_CON1 */
+#define SIDE_TONE_IN_EN_SEL_DOMAIN_SFT 5
+#define SIDE_TONE_IN_EN_SEL_DOMAIN_MASK 0x7
+#define SIDE_TONE_IN_EN_SEL_DOMAIN_MASK_SFT (0x7 << 5)
+#define SIDE_TONE_IN_EN_SEL_FS_SFT 0
+#define SIDE_TONE_IN_EN_SEL_FS_MASK 0x1f
+#define SIDE_TONE_IN_EN_SEL_FS_MASK_SFT (0x1f << 0)
+
+/* AFE_STF_COEFF */
+#define SIDE_TONE_COEFFICIENT_R_W_SEL_SFT 24
+#define SIDE_TONE_COEFFICIENT_R_W_SEL_MASK 0x1
+#define SIDE_TONE_COEFFICIENT_R_W_SEL_MASK_SFT (0x1 << 24)
+#define SIDE_TONE_COEFFICIENT_ADDR_SFT 16
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK 0x1f
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK_SFT (0x1f << 16)
+#define SIDE_TONE_COEFFICIENT_SFT 0
+#define SIDE_TONE_COEFFICIENT_MASK 0xffff
+#define SIDE_TONE_COEFFICIENT_MASK_SFT (0xffff << 0)
+
+/* AFE_STF_GAIN */
+#define SIDE_TONE_POSITIVE_GAIN_SFT 16
+#define SIDE_TONE_POSITIVE_GAIN_MASK 0x7
+#define SIDE_TONE_POSITIVE_GAIN_MASK_SFT (0x7 << 16)
+#define SIDE_TONE_GAIN_SFT 0
+#define SIDE_TONE_GAIN_MASK 0xffff
+#define SIDE_TONE_GAIN_MASK_SFT (0xffff << 0)
+
+/* AFE_STF_MON */
+#define SIDE_TONE_R_RDY_SFT 30
+#define SIDE_TONE_R_RDY_MASK 0x1
+#define SIDE_TONE_R_RDY_MASK_SFT (0x1 << 30)
+#define SIDE_TONE_W_RDY_SFT 29
+#define SIDE_TONE_W_RDY_MASK 0x1
+#define SIDE_TONE_W_RDY_MASK_SFT (0x1 << 29)
+#define SLT_CNT_FLAG_SFT 28
+#define SLT_CNT_FLAG_MASK 0x1
+#define SLT_CNT_FLAG_MASK_SFT (0x1 << 28)
+#define SLT_CNT_SFT 16
+#define SLT_CNT_MASK 0xfff
+#define SLT_CNT_MASK_SFT (0xfff << 16)
+#define SIDE_TONE_COEFF_SFT 0
+#define SIDE_TONE_COEFF_MASK 0xffff
+#define SIDE_TONE_COEFF_MASK_SFT (0xffff << 0)
+
+/* AFE_STF_IP_VERSION */
+#define SIDE_TONE_IP_VERSION_SFT 0
+#define SIDE_TONE_IP_VERSION_MASK 0xffffffff
+#define SIDE_TONE_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CM_REG */
+#define AFE_CM_UPDATE_CNT_SFT 16
+#define AFE_CM_UPDATE_CNT_MASK 0x7fff
+#define AFE_CM_UPDATE_CNT_MASK_SFT (0x7fff << 16)
+#define AFE_CM_1X_EN_SEL_FS_SFT 8
+#define AFE_CM_1X_EN_SEL_FS_MASK 0x1f
+#define AFE_CM_1X_EN_SEL_FS_MASK_SFT (0x1f << 8)
+#define AFE_CM_CH_NUM_SFT 2
+#define AFE_CM_CH_NUM_MASK 0x1f
+#define AFE_CM_CH_NUM_MASK_SFT (0x1f << 2)
+#define AFE_CM_BYTE_SWAP_SFT 1
+#define AFE_CM_BYTE_SWAP_MASK 0x1
+#define AFE_CM_BYTE_SWAP_MASK_SFT (0x1 << 1)
+#define AFE_CM_BYPASS_MODE_SFT 31
+#define AFE_CM_BYPASS_MODE_MASK 0x1
+#define AFE_CM_BYPASS_MODE_MASK_SFT (0x1 << 31)
+
+/* AFE_CM0_CON0 */
+#define AFE_CM0_BYPASS_MODE_SFT 31
+#define AFE_CM0_BYPASS_MODE_MASK 0x1
+#define AFE_CM0_BYPASS_MODE_MASK_SFT (0x1 << 31)
+#define AFE_CM0_UPDATE_CNT_SFT 16
+#define AFE_CM0_UPDATE_CNT_MASK 0x7fff
+#define AFE_CM0_UPDATE_CNT_MASK_SFT (0x7fff << 16)
+#define AFE_CM0_1X_EN_SEL_DOMAIN_SFT 13
+#define AFE_CM0_1X_EN_SEL_DOMAIN_MASK 0x7
+#define AFE_CM0_1X_EN_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define AFE_CM0_1X_EN_SEL_FS_SFT 8
+#define AFE_CM0_1X_EN_SEL_FS_MASK 0x1f
+#define AFE_CM0_1X_EN_SEL_FS_MASK_SFT (0x1f << 8)
+#define AFE_CM0_OUTPUT_MUX_SFT 7
+#define AFE_CM0_OUTPUT_MUX_MASK 0x1
+#define AFE_CM0_OUTPUT_MUX_MASK_SFT (0x1 << 7)
+#define AFE_CM0_CH_NUM_SFT 2
+#define AFE_CM0_CH_NUM_MASK 0x1f
+#define AFE_CM0_CH_NUM_MASK_SFT (0x1f << 2)
+#define AFE_CM0_BYTE_SWAP_SFT 1
+#define AFE_CM0_BYTE_SWAP_MASK 0x1
+#define AFE_CM0_BYTE_SWAP_MASK_SFT (0x1 << 1)
+#define AFE_CM0_ON_SFT 0
+#define AFE_CM0_ON_MASK 0x1
+#define AFE_CM0_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_CM0_MON */
+#define AFE_CM0_BYPASS_MODE_MON_SFT 31
+#define AFE_CM0_BYPASS_MODE_MON_MASK 0x1
+#define AFE_CM0_BYPASS_MODE_MON_MASK_SFT (0x1 << 31)
+#define AFE_CM0_OUTPUT_CNT_MON_SFT 16
+#define AFE_CM0_OUTPUT_CNT_MON_MASK 0x7fff
+#define AFE_CM0_OUTPUT_CNT_MON_MASK_SFT (0x7fff << 16)
+#define AFE_CM0_CUR_CHSET_MON_SFT 5
+#define AFE_CM0_CUR_CHSET_MON_MASK 0xf
+#define AFE_CM0_CUR_CHSET_MON_MASK_SFT (0xf << 5)
+#define AFE_CM0_ODD_FLAG_MON_SFT 4
+#define AFE_CM0_ODD_FLAG_MON_MASK 0x1
+#define AFE_CM0_ODD_FLAG_MON_MASK_SFT (0x1 << 4)
+#define AFE_CM0_BYTE_SWAP_MON_SFT 1
+#define AFE_CM0_BYTE_SWAP_MON_MASK 0x1
+#define AFE_CM0_BYTE_SWAP_MON_MASK_SFT (0x1 << 1)
+#define AFE_CM0_ON_MON_SFT 0
+#define AFE_CM0_ON_MON_MASK 0x1
+#define AFE_CM0_ON_MON_MASK_SFT (0x1 << 0)
+
+/* AFE_CM0_IP_VERSION */
+#define AFE_CM0_IP_VERSION_SFT 0
+#define AFE_CM0_IP_VERSION_MASK 0xffffffff
+#define AFE_CM0_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CM1_CON0 */
+#define AFE_CM1_BYPASS_MODE_SFT 31
+#define AFE_CM1_BYPASS_MODE_MASK 0x1
+#define AFE_CM1_BYPASS_MODE_MASK_SFT (0x1 << 31)
+#define AFE_CM1_UPDATE_CNT_SFT 16
+#define AFE_CM1_UPDATE_CNT_MASK 0x7fff
+#define AFE_CM1_UPDATE_CNT_MASK_SFT (0x7fff << 16)
+#define AFE_CM1_1X_EN_SEL_DOMAIN_SFT 13
+#define AFE_CM1_1X_EN_SEL_DOMAIN_MASK 0x7
+#define AFE_CM1_1X_EN_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define AFE_CM1_1X_EN_SEL_FS_SFT 8
+#define AFE_CM1_1X_EN_SEL_FS_MASK 0x1f
+#define AFE_CM1_1X_EN_SEL_FS_MASK_SFT (0x1f << 8)
+#define AFE_CM1_OUTPUT_MUX_SFT 7
+#define AFE_CM1_OUTPUT_MUX_MASK 0x1
+#define AFE_CM1_OUTPUT_MUX_MASK_SFT (0x1 << 7)
+#define AFE_CM1_CH_NUM_SFT 2
+#define AFE_CM1_CH_NUM_MASK 0x1f
+#define AFE_CM1_CH_NUM_MASK_SFT (0x1f << 2)
+#define AFE_CM1_BYTE_SWAP_SFT 1
+#define AFE_CM1_BYTE_SWAP_MASK 0x1
+#define AFE_CM1_BYTE_SWAP_MASK_SFT (0x1 << 1)
+#define AFE_CM1_ON_SFT 0
+#define AFE_CM1_ON_MASK 0x1
+#define AFE_CM1_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_CM1_MON */
+#define AFE_CM1_BYPASS_MODE_MON_SFT 31
+#define AFE_CM1_BYPASS_MODE_MON_MASK 0x1
+#define AFE_CM1_BYPASS_MODE_MON_MASK_SFT (0x1 << 31)
+#define AFE_CM1_OUTPUT_CNT_MON_SFT 16
+#define AFE_CM1_OUTPUT_CNT_MON_MASK 0x7fff
+#define AFE_CM1_OUTPUT_CNT_MON_MASK_SFT (0x7fff << 16)
+#define AFE_CM1_CUR_CHSET_MON_SFT 5
+#define AFE_CM1_CUR_CHSET_MON_MASK 0xf
+#define AFE_CM1_CUR_CHSET_MON_MASK_SFT (0xf << 5)
+#define AFE_CM1_ODD_FLAG_MON_SFT 4
+#define AFE_CM1_ODD_FLAG_MON_MASK 0x1
+#define AFE_CM1_ODD_FLAG_MON_MASK_SFT (0x1 << 4)
+#define AFE_CM1_BYTE_SWAP_MON_SFT 1
+#define AFE_CM1_BYTE_SWAP_MON_MASK 0x1
+#define AFE_CM1_BYTE_SWAP_MON_MASK_SFT (0x1 << 1)
+#define AFE_CM1_ON_MON_SFT 0
+#define AFE_CM1_ON_MON_MASK 0x1
+#define AFE_CM1_ON_MON_MASK_SFT (0x1 << 0)
+
+/* AFE_CM1_IP_VERSION */
+#define AFE_CM1_IP_VERSION_SFT 0
+#define AFE_CM1_IP_VERSION_MASK 0xffffffff
+#define AFE_CM1_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_SRC_CON0 */
+#define ULCF_CFG_EN_CTL_SFT 31
+#define ULCF_CFG_EN_CTL_MASK 0x1
+#define ULCF_CFG_EN_CTL_MASK_SFT (0x1 << 31)
+#define UL_DMIC_PHASE_SEL_CH1_SFT 27
+#define UL_DMIC_PHASE_SEL_CH1_MASK 0x7
+#define UL_DMIC_PHASE_SEL_CH1_MASK_SFT (0x7 << 27)
+#define UL_DMIC_PHASE_SEL_CH2_SFT 24
+#define UL_DMIC_PHASE_SEL_CH2_MASK 0x7
+#define UL_DMIC_PHASE_SEL_CH2_MASK_SFT (0x7 << 24)
+#define UL_DMIC_TWO_WIRE_CTL_SFT 23
+#define UL_DMIC_TWO_WIRE_CTL_MASK 0x1
+#define UL_DMIC_TWO_WIRE_CTL_MASK_SFT (0x1 << 23)
+#define UL_MODE_3P25M_CH2_CTL_SFT 22
+#define UL_MODE_3P25M_CH2_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22)
+#define UL_MODE_3P25M_CH1_CTL_SFT 21
+#define UL_MODE_3P25M_CH1_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21)
+#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17)
+#define UL_AP_DMIC_ON_SFT 16
+#define UL_AP_DMIC_ON_MASK 0x1
+#define UL_AP_DMIC_ON_MASK_SFT (0x1 << 16)
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define UL_DISABLE_HW_CG_CTL_SFT 12
+#define UL_DISABLE_HW_CG_CTL_MASK 0x1
+#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12)
+#define AMIC_26M_SEL_CTL_SFT 11
+#define AMIC_26M_SEL_CTL_MASK 0x1
+#define AMIC_26M_SEL_CTL_MASK_SFT (0x1 << 11)
+#define UL_IIR_ON_TMP_CTL_SFT 10
+#define UL_IIR_ON_TMP_CTL_MASK 0x1
+#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10)
+#define UL_IIRMODE_CTL_SFT 7
+#define UL_IIRMODE_CTL_MASK 0x7
+#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7)
+#define DIGMIC_4P33M_SEL_SFT 6
+#define DIGMIC_4P33M_SEL_MASK 0x1
+#define DIGMIC_4P33M_SEL_MASK_SFT (0x1 << 6)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define AMIC_6P5M_SEL_CTL_SFT 4
+#define AMIC_6P5M_SEL_CTL_MASK 0x1
+#define AMIC_6P5M_SEL_CTL_MASK_SFT (0x1 << 4)
+#define AMIC_1P625M_SEL_CTL_SFT 3
+#define AMIC_1P625M_SEL_CTL_MASK 0x1
+#define AMIC_1P625M_SEL_CTL_MASK_SFT (0x1 << 3)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL0_SRC_CON1 */
+#define ADDA_UL_GAIN_VALUE_SFT 16
+#define ADDA_UL_GAIN_VALUE_MASK 0xffff
+#define ADDA_UL_GAIN_VALUE_MASK_SFT (0xffff << 16)
+#define ADDA_UL_POSTIVEGAIN_SFT 12
+#define ADDA_UL_POSTIVEGAIN_MASK 0x7
+#define ADDA_UL_POSTIVEGAIN_MASK_SFT (0x7 << 12)
+#define ADDA_UL_ODDTAP_MODE_SFT 11
+#define ADDA_UL_ODDTAP_MODE_MASK 0x1
+#define ADDA_UL_ODDTAP_MODE_MASK_SFT (0x1 << 11)
+#define ADDA_UL_HALF_TAP_NUM_SFT 5
+#define ADDA_UL_HALF_TAP_NUM_MASK 0x3f
+#define ADDA_UL_HALF_TAP_NUM_MASK_SFT (0x3f << 5)
+#define FIFO_SOFT_RST_SFT 4
+#define FIFO_SOFT_RST_MASK 0x1
+#define FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+#define FIFO_SOFT_RST_EN_SFT 3
+#define FIFO_SOFT_RST_EN_MASK 0x1
+#define FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 3)
+#define LR_SWAP_SFT 2
+#define LR_SWAP_MASK 0x1
+#define LR_SWAP_MASK_SFT (0x1 << 2)
+#define GAIN_MODE_SFT 0
+#define GAIN_MODE_MASK 0x3
+#define GAIN_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_ADDA_UL0_SRC_CON2 */
+#define C_DAC_EN_CTL_SFT 27
+#define C_DAC_EN_CTL_MASK 0x1
+#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27)
+#define C_MUTE_SW_CTL_SFT 26
+#define C_MUTE_SW_CTL_MASK 0x1
+#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26)
+#define C_AMP_DIV_CH2_CTL_SFT 21
+#define C_AMP_DIV_CH2_CTL_MASK 0x7
+#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21)
+#define C_FREQ_DIV_CH2_CTL_SFT 16
+#define C_FREQ_DIV_CH2_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16)
+#define C_SINE_MODE_CH2_CTL_SFT 12
+#define C_SINE_MODE_CH2_CTL_MASK 0xf
+#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12)
+#define C_AMP_DIV_CH1_CTL_SFT 9
+#define C_AMP_DIV_CH1_CTL_MASK 0x7
+#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9)
+#define C_FREQ_DIV_CH1_CTL_SFT 4
+#define C_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4)
+#define C_SINE_MODE_CH1_CTL_SFT 0
+#define C_SINE_MODE_CH1_CTL_MASK 0xf
+#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_UL0_SRC_DEBUG */
+#define UL_SLT_CNT_FLAG_RESET_CTL_SFT 16
+#define UL_SLT_CNT_FLAG_RESET_CTL_MASK 0x1
+#define UL_SLT_CNT_FLAG_RESET_CTL_MASK_SFT (0x1 << 16)
+#define FIFO_DIGMIC_TESTIN_SFT 12
+#define FIFO_DIGMIC_TESTIN_MASK 0x3
+#define FIFO_DIGMIC_TESTIN_MASK_SFT (0x3 << 12)
+#define FIFO_DIGMIC_WDATA_TESTEN_SFT 11
+#define FIFO_DIGMIC_WDATA_TESTEN_MASK 0x1
+#define FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT (0x1 << 11)
+#define SLT_CNT_THD_CTL_SFT 0
+#define SLT_CNT_THD_CTL_MASK 0x7ff
+#define SLT_CNT_THD_CTL_MASK_SFT (0x7ff << 0)
+
+/* AFE_ADDA_UL0_SRC_DEBUG_MON0 */
+#define SLT_CNT_FLAG_CTL_SFT 16
+#define SLT_CNT_FLAG_CTL_MASK 0x1
+#define SLT_CNT_FLAG_CTL_MASK_SFT (0x1 << 16)
+#define SLT_COUNTER_CTL_SFT 0
+#define SLT_COUNTER_CTL_MASK 0x7ff
+#define SLT_COUNTER_CTL_MASK_SFT (0x7ff << 0)
+
+/* AFE_ADDA_UL0_SRC_MON1 */
+#define UL_VOICE_MODE_CTL_SFT 29
+#define UL_VOICE_MODE_CTL_MASK 0x7
+#define UL_VOICE_MODE_CTL_MASK_SFT (0x7 << 29)
+#define DATA_COMB_IN_CH2_SFT 24
+#define DATA_COMB_IN_CH2_MASK 0x1f
+#define DATA_COMB_IN_CH2_MASK_SFT (0x1f << 24)
+#define DATA_COMB_OUT_CH2_SFT 0
+#define DATA_COMB_OUT_CH2_MASK 0xffffff
+#define DATA_COMB_OUT_CH2_MASK_SFT (0xffffff << 0)
+
+/* AFE_ADDA_UL0_IIR_COEF_02_01 */
+#define ADDA_IIR_COEF_02_01_SFT 0
+#define ADDA_IIR_COEF_02_01_MASK 0xffffffff
+#define ADDA_IIR_COEF_02_01_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_IIR_COEF_04_03 */
+#define ADDA_IIR_COEF_04_03_SFT 0
+#define ADDA_IIR_COEF_04_03_MASK 0xffffffff
+#define ADDA_IIR_COEF_04_03_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_IIR_COEF_06_05 */
+#define ADDA_IIR_COEF_06_05_SFT 0
+#define ADDA_IIR_COEF_06_05_MASK 0xffffffff
+#define ADDA_IIR_COEF_06_05_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_IIR_COEF_08_07 */
+#define ADDA_IIR_COEF_08_07_SFT 0
+#define ADDA_IIR_COEF_08_07_MASK 0xffffffff
+#define ADDA_IIR_COEF_08_07_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_IIR_COEF_10_09 */
+#define ADDA_IIR_COEF_10_09_SFT 0
+#define ADDA_IIR_COEF_10_09_MASK 0xffffffff
+#define ADDA_IIR_COEF_10_09_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_02_01 */
+#define ADDA_ULCF_CFG_02_01_SFT 0
+#define ADDA_ULCF_CFG_02_01_MASK 0xffffffff
+#define ADDA_ULCF_CFG_02_01_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_04_03 */
+#define ADDA_ULCF_CFG_04_03_SFT 0
+#define ADDA_ULCF_CFG_04_03_MASK 0xffffffff
+#define ADDA_ULCF_CFG_04_03_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_06_05 */
+#define ADDA_ULCF_CFG_06_05_SFT 0
+#define ADDA_ULCF_CFG_06_05_MASK 0xffffffff
+#define ADDA_ULCF_CFG_06_05_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_08_07 */
+#define ADDA_ULCF_CFG_08_07_SFT 0
+#define ADDA_ULCF_CFG_08_07_MASK 0xffffffff
+#define ADDA_ULCF_CFG_08_07_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_10_09 */
+#define ADDA_ULCF_CFG_10_09_SFT 0
+#define ADDA_ULCF_CFG_10_09_MASK 0xffffffff
+#define ADDA_ULCF_CFG_10_09_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_12_11 */
+#define ADDA_ULCF_CFG_12_11_SFT 0
+#define ADDA_ULCF_CFG_12_11_MASK 0xffffffff
+#define ADDA_ULCF_CFG_12_11_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_14_13 */
+#define ADDA_ULCF_CFG_14_13_SFT 0
+#define ADDA_ULCF_CFG_14_13_MASK 0xffffffff
+#define ADDA_ULCF_CFG_14_13_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_16_15 */
+#define ADDA_ULCF_CFG_16_15_SFT 0
+#define ADDA_ULCF_CFG_16_15_MASK 0xffffffff
+#define ADDA_ULCF_CFG_16_15_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_18_17 */
+#define ADDA_ULCF_CFG_18_17_SFT 0
+#define ADDA_ULCF_CFG_18_17_MASK 0xffffffff
+#define ADDA_ULCF_CFG_18_17_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_20_19 */
+#define ADDA_ULCF_CFG_20_19_SFT 0
+#define ADDA_ULCF_CFG_20_19_MASK 0xffffffff
+#define ADDA_ULCF_CFG_20_19_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_22_21 */
+#define ADDA_ULCF_CFG_22_21_SFT 0
+#define ADDA_ULCF_CFG_22_21_MASK 0xffffffff
+#define ADDA_ULCF_CFG_22_21_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_24_23 */
+#define ADDA_ULCF_CFG_24_23_SFT 0
+#define ADDA_ULCF_CFG_24_23_MASK 0xffffffff
+#define ADDA_ULCF_CFG_24_23_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_26_25 */
+#define ADDA_ULCF_CFG_26_25_SFT 0
+#define ADDA_ULCF_CFG_26_25_MASK 0xffffffff
+#define ADDA_ULCF_CFG_26_25_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_28_27 */
+#define ADDA_ULCF_CFG_28_27_SFT 0
+#define ADDA_ULCF_CFG_28_27_MASK 0xffffffff
+#define ADDA_ULCF_CFG_28_27_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_30_29 */
+#define ADDA_ULCF_CFG_30_29_SFT 0
+#define ADDA_ULCF_CFG_30_29_MASK 0xffffffff
+#define ADDA_ULCF_CFG_30_29_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_ULCF_CFG_32_31 */
+#define ADDA_ULCF_CFG_32_31_SFT 0
+#define ADDA_ULCF_CFG_32_31_MASK 0xffffffff
+#define ADDA_ULCF_CFG_32_31_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_IP_VERSION */
+#define ADDA_ULCF_IP_VERSION_SFT 0
+#define ADDA_ULCF_IP_VERSION_MASK 0xffffffff
+#define ADDA_ULCF_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_SRC_CON0 */
+#define ULCF_CFG_EN_CTL_SFT 31
+#define ULCF_CFG_EN_CTL_MASK 0x1
+#define ULCF_CFG_EN_CTL_MASK_SFT (0x1 << 31)
+#define UL_DMIC_PHASE_SEL_CH1_SFT 27
+#define UL_DMIC_PHASE_SEL_CH1_MASK 0x7
+#define UL_DMIC_PHASE_SEL_CH1_MASK_SFT (0x7 << 27)
+#define UL_DMIC_PHASE_SEL_CH2_SFT 24
+#define UL_DMIC_PHASE_SEL_CH2_MASK 0x7
+#define UL_DMIC_PHASE_SEL_CH2_MASK_SFT (0x7 << 24)
+#define UL_DMIC_TWO_WIRE_CTL_SFT 23
+#define UL_DMIC_TWO_WIRE_CTL_MASK 0x1
+#define UL_DMIC_TWO_WIRE_CTL_MASK_SFT (0x1 << 23)
+#define UL_MODE_3P25M_CH2_CTL_SFT 22
+#define UL_MODE_3P25M_CH2_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22)
+#define UL_MODE_3P25M_CH1_CTL_SFT 21
+#define UL_MODE_3P25M_CH1_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21)
+#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17)
+#define UL_AP_DMIC_ON_SFT 16
+#define UL_AP_DMIC_ON_MASK 0x1
+#define UL_AP_DMIC_ON_MASK_SFT (0x1 << 16)
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define UL_DISABLE_HW_CG_CTL_SFT 12
+#define UL_DISABLE_HW_CG_CTL_MASK 0x1
+#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12)
+#define AMIC_26M_SEL_CTL_SFT 11
+#define AMIC_26M_SEL_CTL_MASK 0x1
+#define AMIC_26M_SEL_CTL_MASK_SFT (0x1 << 11)
+#define UL_IIR_ON_TMP_CTL_SFT 10
+#define UL_IIR_ON_TMP_CTL_MASK 0x1
+#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10)
+#define UL_IIRMODE_CTL_SFT 7
+#define UL_IIRMODE_CTL_MASK 0x7
+#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7)
+#define DIGMIC_4P33M_SEL_SFT 6
+#define DIGMIC_4P33M_SEL_MASK 0x1
+#define DIGMIC_4P33M_SEL_MASK_SFT (0x1 << 6)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define AMIC_6P5M_SEL_CTL_SFT 4
+#define AMIC_6P5M_SEL_CTL_MASK 0x1
+#define AMIC_6P5M_SEL_CTL_MASK_SFT (0x1 << 4)
+#define AMIC_1P625M_SEL_CTL_SFT 3
+#define AMIC_1P625M_SEL_CTL_MASK 0x1
+#define AMIC_1P625M_SEL_CTL_MASK_SFT (0x1 << 3)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL1_SRC_CON1 */
+#define ADDA_UL_GAIN_VALUE_SFT 16
+#define ADDA_UL_GAIN_VALUE_MASK 0xffff
+#define ADDA_UL_GAIN_VALUE_MASK_SFT (0xffff << 16)
+#define ADDA_UL_POSTIVEGAIN_SFT 12
+#define ADDA_UL_POSTIVEGAIN_MASK 0x7
+#define ADDA_UL_POSTIVEGAIN_MASK_SFT (0x7 << 12)
+#define ADDA_UL_ODDTAP_MODE_SFT 11
+#define ADDA_UL_ODDTAP_MODE_MASK 0x1
+#define ADDA_UL_ODDTAP_MODE_MASK_SFT (0x1 << 11)
+#define ADDA_UL_HALF_TAP_NUM_SFT 5
+#define ADDA_UL_HALF_TAP_NUM_MASK 0x3f
+#define ADDA_UL_HALF_TAP_NUM_MASK_SFT (0x3f << 5)
+#define FIFO_SOFT_RST_SFT 4
+#define FIFO_SOFT_RST_MASK 0x1
+#define FIFO_SOFT_RST_MASK_SFT (0x1 << 4)
+#define FIFO_SOFT_RST_EN_SFT 3
+#define FIFO_SOFT_RST_EN_MASK 0x1
+#define FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 3)
+#define LR_SWAP_SFT 2
+#define LR_SWAP_MASK 0x1
+#define LR_SWAP_MASK_SFT (0x1 << 2)
+#define GAIN_MODE_SFT 0
+#define GAIN_MODE_MASK 0x3
+#define GAIN_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_ADDA_UL1_SRC_CON2 */
+#define C_DAC_EN_CTL_SFT 27
+#define C_DAC_EN_CTL_MASK 0x1
+#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27)
+#define C_MUTE_SW_CTL_SFT 26
+#define C_MUTE_SW_CTL_MASK 0x1
+#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26)
+#define C_AMP_DIV_CH2_CTL_SFT 21
+#define C_AMP_DIV_CH2_CTL_MASK 0x7
+#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21)
+#define C_FREQ_DIV_CH2_CTL_SFT 16
+#define C_FREQ_DIV_CH2_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16)
+#define C_SINE_MODE_CH2_CTL_SFT 12
+#define C_SINE_MODE_CH2_CTL_MASK 0xf
+#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12)
+#define C_AMP_DIV_CH1_CTL_SFT 9
+#define C_AMP_DIV_CH1_CTL_MASK 0x7
+#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9)
+#define C_FREQ_DIV_CH1_CTL_SFT 4
+#define C_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4)
+#define C_SINE_MODE_CH1_CTL_SFT 0
+#define C_SINE_MODE_CH1_CTL_MASK 0xf
+#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_UL1_SRC_DEBUG */
+#define UL_SLT_CNT_FLAG_RESET_CTL_SFT 16
+#define UL_SLT_CNT_FLAG_RESET_CTL_MASK 0x1
+#define UL_SLT_CNT_FLAG_RESET_CTL_MASK_SFT (0x1 << 16)
+#define FIFO_DIGMIC_TESTIN_SFT 12
+#define FIFO_DIGMIC_TESTIN_MASK 0x3
+#define FIFO_DIGMIC_TESTIN_MASK_SFT (0x3 << 12)
+#define FIFO_DIGMIC_WDATA_TESTEN_SFT 11
+#define FIFO_DIGMIC_WDATA_TESTEN_MASK 0x1
+#define FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT (0x1 << 11)
+#define SLT_CNT_THD_CTL_SFT 0
+#define SLT_CNT_THD_CTL_MASK 0x7ff
+#define SLT_CNT_THD_CTL_MASK_SFT (0x7ff << 0)
+
+/* AFE_ADDA_UL1_SRC_DEBUG_MON0 */
+#define SLT_CNT_FLAG_CTL_SFT 16
+#define SLT_CNT_FLAG_CTL_MASK 0x1
+#define SLT_CNT_FLAG_CTL_MASK_SFT (0x1 << 16)
+#define SLT_COUNTER_CTL_SFT 0
+#define SLT_COUNTER_CTL_MASK 0x7ff
+#define SLT_COUNTER_CTL_MASK_SFT (0x7ff << 0)
+
+/* AFE_ADDA_UL1_SRC_MON1 */
+#define UL_VOICE_MODE_CTL_SFT 29
+#define UL_VOICE_MODE_CTL_MASK 0x7
+#define UL_VOICE_MODE_CTL_MASK_SFT (0x7 << 29)
+#define DATA_COMB_IN_CH2_SFT 24
+#define DATA_COMB_IN_CH2_MASK 0x1f
+#define DATA_COMB_IN_CH2_MASK_SFT (0x1f << 24)
+#define DATA_COMB_OUT_CH2_SFT 0
+#define DATA_COMB_OUT_CH2_MASK 0xffffff
+#define DATA_COMB_OUT_CH2_MASK_SFT (0xffffff << 0)
+
+/* AFE_ADDA_UL1_IIR_COEF_02_01 */
+#define ADDA_IIR_COEF_02_01_SFT 0
+#define ADDA_IIR_COEF_02_01_MASK 0xffffffff
+#define ADDA_IIR_COEF_02_01_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_IIR_COEF_04_03 */
+#define ADDA_IIR_COEF_04_03_SFT 0
+#define ADDA_IIR_COEF_04_03_MASK 0xffffffff
+#define ADDA_IIR_COEF_04_03_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_IIR_COEF_06_05 */
+#define ADDA_IIR_COEF_06_05_SFT 0
+#define ADDA_IIR_COEF_06_05_MASK 0xffffffff
+#define ADDA_IIR_COEF_06_05_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_IIR_COEF_08_07 */
+#define ADDA_IIR_COEF_08_07_SFT 0
+#define ADDA_IIR_COEF_08_07_MASK 0xffffffff
+#define ADDA_IIR_COEF_08_07_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_IIR_COEF_10_09 */
+#define ADDA_IIR_COEF_10_09_SFT 0
+#define ADDA_IIR_COEF_10_09_MASK 0xffffffff
+#define ADDA_IIR_COEF_10_09_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_02_01 */
+#define ADDA_ULCF_CFG_02_01_SFT 0
+#define ADDA_ULCF_CFG_02_01_MASK 0xffffffff
+#define ADDA_ULCF_CFG_02_01_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_04_03 */
+#define ADDA_ULCF_CFG_04_03_SFT 0
+#define ADDA_ULCF_CFG_04_03_MASK 0xffffffff
+#define ADDA_ULCF_CFG_04_03_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_06_05 */
+#define ADDA_ULCF_CFG_06_05_SFT 0
+#define ADDA_ULCF_CFG_06_05_MASK 0xffffffff
+#define ADDA_ULCF_CFG_06_05_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_08_07 */
+#define ADDA_ULCF_CFG_08_07_SFT 0
+#define ADDA_ULCF_CFG_08_07_MASK 0xffffffff
+#define ADDA_ULCF_CFG_08_07_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_10_09 */
+#define ADDA_ULCF_CFG_10_09_SFT 0
+#define ADDA_ULCF_CFG_10_09_MASK 0xffffffff
+#define ADDA_ULCF_CFG_10_09_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_12_11 */
+#define ADDA_ULCF_CFG_12_11_SFT 0
+#define ADDA_ULCF_CFG_12_11_MASK 0xffffffff
+#define ADDA_ULCF_CFG_12_11_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_14_13 */
+#define ADDA_ULCF_CFG_14_13_SFT 0
+#define ADDA_ULCF_CFG_14_13_MASK 0xffffffff
+#define ADDA_ULCF_CFG_14_13_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_16_15 */
+#define ADDA_ULCF_CFG_16_15_SFT 0
+#define ADDA_ULCF_CFG_16_15_MASK 0xffffffff
+#define ADDA_ULCF_CFG_16_15_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_18_17 */
+#define ADDA_ULCF_CFG_18_17_SFT 0
+#define ADDA_ULCF_CFG_18_17_MASK 0xffffffff
+#define ADDA_ULCF_CFG_18_17_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_20_19 */
+#define ADDA_ULCF_CFG_20_19_SFT 0
+#define ADDA_ULCF_CFG_20_19_MASK 0xffffffff
+#define ADDA_ULCF_CFG_20_19_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_22_21 */
+#define ADDA_ULCF_CFG_22_21_SFT 0
+#define ADDA_ULCF_CFG_22_21_MASK 0xffffffff
+#define ADDA_ULCF_CFG_22_21_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_24_23 */
+#define ADDA_ULCF_CFG_24_23_SFT 0
+#define ADDA_ULCF_CFG_24_23_MASK 0xffffffff
+#define ADDA_ULCF_CFG_24_23_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_26_25 */
+#define ADDA_ULCF_CFG_26_25_SFT 0
+#define ADDA_ULCF_CFG_26_25_MASK 0xffffffff
+#define ADDA_ULCF_CFG_26_25_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_28_27 */
+#define ADDA_ULCF_CFG_28_27_SFT 0
+#define ADDA_ULCF_CFG_28_27_MASK 0xffffffff
+#define ADDA_ULCF_CFG_28_27_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_30_29 */
+#define ADDA_ULCF_CFG_30_29_SFT 0
+#define ADDA_ULCF_CFG_30_29_MASK 0xffffffff
+#define ADDA_ULCF_CFG_30_29_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_ULCF_CFG_32_31 */
+#define ADDA_ULCF_CFG_32_31_SFT 0
+#define ADDA_ULCF_CFG_32_31_MASK 0xffffffff
+#define ADDA_ULCF_CFG_32_31_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_IP_VERSION */
+#define ADDA_ULCF_IP_VERSION_SFT 0
+#define ADDA_ULCF_IP_VERSION_MASK 0xffffffff
+#define ADDA_ULCF_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_PROXIMITY_CON0 */
+#define PROXIMITY_CH1_ON_SFT 12
+#define PROXIMITY_CH1_ON_MASK 0x1
+#define PROXIMITY_CH1_ON_MASK_SFT (0x1 << 12)
+#define PROXIMITY_CH1_SEL_SFT 8
+#define PROXIMITY_CH1_SEL_MASK 0xf
+#define PROXIMITY_CH1_SEL_MASK_SFT (0xf << 8)
+#define PROXIMITY_CH2_ON_SFT 4
+#define PROXIMITY_CH2_ON_MASK 0x1
+#define PROXIMITY_CH2_ON_MASK_SFT (0x1 << 4)
+#define PROXIMITY_CH2_SEL_SFT 0
+#define PROXIMITY_CH2_SEL_MASK 0xf
+#define PROXIMITY_CH2_SEL_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_ULSRC_PHASE_CON0 */
+#define DMIC1_PHASE_FCLK_SEL_SFT 30
+#define DMIC1_PHASE_FCLK_SEL_MASK 0x3
+#define DMIC1_PHASE_FCLK_SEL_MASK_SFT (0x3 << 30)
+#define DMIC0_PHASE_FCLK_SEL_SFT 28
+#define DMIC0_PHASE_FCLK_SEL_MASK 0x3
+#define DMIC0_PHASE_FCLK_SEL_MASK_SFT (0x3 << 28)
+#define UL3_PHASE_FCLK_SEL_SFT 26
+#define UL3_PHASE_FCLK_SEL_MASK 0x3
+#define UL3_PHASE_FCLK_SEL_MASK_SFT (0x3 << 26)
+#define UL2_PHASE_FCLK_SEL_SFT 24
+#define UL2_PHASE_FCLK_SEL_MASK 0x3
+#define UL2_PHASE_FCLK_SEL_MASK_SFT (0x3 << 24)
+#define UL1_PHASE_FCLK_SEL_SFT 22
+#define UL1_PHASE_FCLK_SEL_MASK 0x3
+#define UL1_PHASE_FCLK_SEL_MASK_SFT (0x3 << 22)
+#define UL0_PHASE_FCLK_SEL_SFT 20
+#define UL0_PHASE_FCLK_SEL_MASK 0x3
+#define UL0_PHASE_FCLK_SEL_MASK_SFT (0x3 << 20)
+#define UL_PHASE_SYNC_FCLK_2_ON_SFT 18
+#define UL_PHASE_SYNC_FCLK_2_ON_MASK 0x1
+#define UL_PHASE_SYNC_FCLK_2_ON_MASK_SFT (0x1 << 18)
+#define UL_PHASE_SYNC_FCLK_1_ON_SFT 17
+#define UL_PHASE_SYNC_FCLK_1_ON_MASK 0x1
+#define UL_PHASE_SYNC_FCLK_1_ON_MASK_SFT (0x1 << 17)
+#define UL_PHASE_SYNC_FCLK_0_ON_SFT 16
+#define UL_PHASE_SYNC_FCLK_0_ON_MASK 0x1
+#define UL_PHASE_SYNC_FCLK_0_ON_MASK_SFT (0x1 << 16)
+#define DMIC1_PHASE_HCLK_SEL_SFT 14
+#define DMIC1_PHASE_HCLK_SEL_MASK 0x3
+#define DMIC1_PHASE_HCLK_SEL_MASK_SFT (0x3 << 14)
+#define DMIC0_PHASE_HCLK_SEL_SFT 12
+#define DMIC0_PHASE_HCLK_SEL_MASK 0x3
+#define DMIC0_PHASE_HCLK_SEL_MASK_SFT (0x3 << 12)
+#define UL3_PHASE_HCLK_SEL_SFT 10
+#define UL3_PHASE_HCLK_SEL_MASK 0x3
+#define UL3_PHASE_HCLK_SEL_MASK_SFT (0x3 << 10)
+#define UL2_PHASE_HCLK_SEL_SFT 8
+#define UL2_PHASE_HCLK_SEL_MASK 0x3
+#define UL2_PHASE_HCLK_SEL_MASK_SFT (0x3 << 8)
+#define UL1_PHASE_HCLK_SEL_SFT 6
+#define UL1_PHASE_HCLK_SEL_MASK 0x3
+#define UL1_PHASE_HCLK_SEL_MASK_SFT (0x3 << 6)
+#define UL0_PHASE_HCLK_SEL_SFT 4
+#define UL0_PHASE_HCLK_SEL_MASK 0x3
+#define UL0_PHASE_HCLK_SEL_MASK_SFT (0x3 << 4)
+#define UL_PHASE_SYNC_HCLK_2_ON_SFT 2
+#define UL_PHASE_SYNC_HCLK_2_ON_MASK 0x1
+#define UL_PHASE_SYNC_HCLK_2_ON_MASK_SFT (0x1 << 2)
+#define UL_PHASE_SYNC_HCLK_1_ON_SFT 1
+#define UL_PHASE_SYNC_HCLK_1_ON_MASK 0x1
+#define UL_PHASE_SYNC_HCLK_1_ON_MASK_SFT (0x1 << 1)
+#define UL_PHASE_SYNC_HCLK_0_ON_SFT 0
+#define UL_PHASE_SYNC_HCLK_0_ON_MASK 0x1
+#define UL_PHASE_SYNC_HCLK_0_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_ULSRC_PHASE_CON1 */
+#define DMIC_CLK_PHASE_SYNC_SET_SFT 31
+#define DMIC_CLK_PHASE_SYNC_SET_MASK 0x1
+#define DMIC_CLK_PHASE_SYNC_SET_MASK_SFT (0x1 << 31)
+#define DMIC1_PHASE_SYNC_FCLK_SET_SFT 11
+#define DMIC1_PHASE_SYNC_FCLK_SET_MASK 0x1
+#define DMIC1_PHASE_SYNC_FCLK_SET_MASK_SFT (0x1 << 11)
+#define DMIC1_PHASE_SYNC_HCLK_SET_SFT 10
+#define DMIC1_PHASE_SYNC_HCLK_SET_MASK 0x1
+#define DMIC1_PHASE_SYNC_HCLK_SET_MASK_SFT (0x1 << 10)
+#define DMIC0_PHASE_SYNC_FCLK_SET_SFT 9
+#define DMIC0_PHASE_SYNC_FCLK_SET_MASK 0x1
+#define DMIC0_PHASE_SYNC_FCLK_SET_MASK_SFT (0x1 << 9)
+#define DMIC0_PHASE_SYNC_HCLK_SET_SFT 8
+#define DMIC0_PHASE_SYNC_HCLK_SET_MASK 0x1
+#define DMIC0_PHASE_SYNC_HCLK_SET_MASK_SFT (0x1 << 8)
+#define UL3_PHASE_SYNC_FCLK_SET_SFT 7
+#define UL3_PHASE_SYNC_FCLK_SET_MASK 0x1
+#define UL3_PHASE_SYNC_FCLK_SET_MASK_SFT (0x1 << 7)
+#define UL3_PHASE_SYNC_HCLK_SET_SFT 6
+#define UL3_PHASE_SYNC_HCLK_SET_MASK 0x1
+#define UL3_PHASE_SYNC_HCLK_SET_MASK_SFT (0x1 << 6)
+#define UL2_PHASE_SYNC_FCLK_SET_SFT 5
+#define UL2_PHASE_SYNC_FCLK_SET_MASK 0x1
+#define UL2_PHASE_SYNC_FCLK_SET_MASK_SFT (0x1 << 5)
+#define UL2_PHASE_SYNC_HCLK_SET_SFT 4
+#define UL2_PHASE_SYNC_HCLK_SET_MASK 0x1
+#define UL2_PHASE_SYNC_HCLK_SET_MASK_SFT (0x1 << 4)
+#define UL1_PHASE_SYNC_FCLK_SET_SFT 3
+#define UL1_PHASE_SYNC_FCLK_SET_MASK 0x1
+#define UL1_PHASE_SYNC_FCLK_SET_MASK_SFT (0x1 << 3)
+#define UL1_PHASE_SYNC_HCLK_SET_SFT 2
+#define UL1_PHASE_SYNC_HCLK_SET_MASK 0x1
+#define UL1_PHASE_SYNC_HCLK_SET_MASK_SFT (0x1 << 2)
+#define UL0_PHASE_SYNC_FCLK_SET_SFT 1
+#define UL0_PHASE_SYNC_FCLK_SET_MASK 0x1
+#define UL0_PHASE_SYNC_FCLK_SET_MASK_SFT (0x1 << 1)
+#define UL0_PHASE_SYNC_HCLK_SET_SFT 0
+#define UL0_PHASE_SYNC_HCLK_SET_MASK 0x1
+#define UL0_PHASE_SYNC_HCLK_SET_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_ULSRC_PHASE_CON2 */
+#define DMIC1_PHASE_SYNC_1X_EN_SEL_SFT 26
+#define DMIC1_PHASE_SYNC_1X_EN_SEL_MASK 0x3
+#define DMIC1_PHASE_SYNC_1X_EN_SEL_MASK_SFT (0x3 << 26)
+#define DMIC0_PHASE_SYNC_1X_EN_SEL_SFT 24
+#define DMIC0_PHASE_SYNC_1X_EN_SEL_MASK 0x3
+#define DMIC0_PHASE_SYNC_1X_EN_SEL_MASK_SFT (0x3 << 24)
+#define UL3_PHASE_SYNC_1X_EN_SEL_SFT 22
+#define UL3_PHASE_SYNC_1X_EN_SEL_MASK 0x3
+#define UL3_PHASE_SYNC_1X_EN_SEL_MASK_SFT (0x3 << 22)
+#define UL2_PHASE_SYNC_1X_EN_SEL_SFT 20
+#define UL2_PHASE_SYNC_1X_EN_SEL_MASK 0x3
+#define UL2_PHASE_SYNC_1X_EN_SEL_MASK_SFT (0x3 << 20)
+#define UL1_PHASE_SYNC_1X_EN_SEL_SFT 18
+#define UL1_PHASE_SYNC_1X_EN_SEL_MASK 0x3
+#define UL1_PHASE_SYNC_1X_EN_SEL_MASK_SFT (0x3 << 18)
+#define UL0_PHASE_SYNC_1X_EN_SEL_SFT 16
+#define UL0_PHASE_SYNC_1X_EN_SEL_MASK 0x3
+#define UL0_PHASE_SYNC_1X_EN_SEL_MASK_SFT (0x3 << 16)
+#define UL_PHASE_SYNC_FCLK_1X_EN_2_ON_SFT 5
+#define UL_PHASE_SYNC_FCLK_1X_EN_2_ON_MASK 0x1
+#define UL_PHASE_SYNC_FCLK_1X_EN_2_ON_MASK_SFT (0x1 << 5)
+#define UL_PHASE_SYNC_FCLK_1X_EN_1_ON_SFT 4
+#define UL_PHASE_SYNC_FCLK_1X_EN_1_ON_MASK 0x1
+#define UL_PHASE_SYNC_FCLK_1X_EN_1_ON_MASK_SFT (0x1 << 4)
+#define UL_PHASE_SYNC_FCLK_1X_EN_0_ON_SFT 3
+#define UL_PHASE_SYNC_FCLK_1X_EN_0_ON_MASK 0x1
+#define UL_PHASE_SYNC_FCLK_1X_EN_0_ON_MASK_SFT (0x1 << 3)
+#define UL_PHASE_SYNC_HCLK_1X_EN_2_ON_SFT 2
+#define UL_PHASE_SYNC_HCLK_1X_EN_2_ON_MASK 0x1
+#define UL_PHASE_SYNC_HCLK_1X_EN_2_ON_MASK_SFT (0x1 << 2)
+#define UL_PHASE_SYNC_HCLK_1X_EN_1_ON_SFT 1
+#define UL_PHASE_SYNC_HCLK_1X_EN_1_ON_MASK 0x1
+#define UL_PHASE_SYNC_HCLK_1X_EN_1_ON_MASK_SFT (0x1 << 1)
+#define UL_PHASE_SYNC_HCLK_1X_EN_0_ON_SFT 0
+#define UL_PHASE_SYNC_HCLK_1X_EN_0_ON_MASK 0x1
+#define UL_PHASE_SYNC_HCLK_1X_EN_0_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_ULSRC_PHASE_CON3 */
+#define DMIC1_PHASE_SYNC_SOFT_RST_SEL_SFT 26
+#define DMIC1_PHASE_SYNC_SOFT_RST_SEL_MASK 0x3
+#define DMIC1_PHASE_SYNC_SOFT_RST_SEL_MASK_SFT (0x3 << 26)
+#define DMIC0_PHASE_SYNC_SOFT_RST_SEL_SFT 24
+#define DMIC0_PHASE_SYNC_SOFT_RST_SEL_MASK 0x3
+#define DMIC0_PHASE_SYNC_SOFT_RST_SEL_MASK_SFT (0x3 << 24)
+#define UL3_PHASE_SYNC_SOFT_RST_SEL_SFT 22
+#define UL3_PHASE_SYNC_SOFT_RST_SEL_MASK 0x3
+#define UL3_PHASE_SYNC_SOFT_RST_SEL_MASK_SFT (0x3 << 22)
+#define UL2_PHASE_SYNC_SOFT_RST_SEL_SFT 20
+#define UL2_PHASE_SYNC_SOFT_RST_SEL_MASK 0x3
+#define UL2_PHASE_SYNC_SOFT_RST_SEL_MASK_SFT (0x3 << 20)
+#define UL1_PHASE_SYNC_SOFT_RST_SEL_SFT 18
+#define UL1_PHASE_SYNC_SOFT_RST_SEL_MASK 0x3
+#define UL1_PHASE_SYNC_SOFT_RST_SEL_MASK_SFT (0x3 << 18)
+#define UL0_PHASE_SYNC_SOFT_RST_SEL_SFT 16
+#define UL0_PHASE_SYNC_SOFT_RST_SEL_MASK 0x3
+#define UL0_PHASE_SYNC_SOFT_RST_SEL_MASK_SFT (0x3 << 16)
+#define DMIC1_PHASE_SYNC_CH1_FIFO_SEL_SFT 13
+#define DMIC1_PHASE_SYNC_CH1_FIFO_SEL_MASK 0x1
+#define DMIC1_PHASE_SYNC_CH1_FIFO_SEL_MASK_SFT (0x1 << 13)
+#define DMIC0_PHASE_SYNC_CH1_FIFO_SEL_SFT 12
+#define DMIC0_PHASE_SYNC_CH1_FIFO_SEL_MASK 0x1
+#define DMIC0_PHASE_SYNC_CH1_FIFO_SEL_MASK_SFT (0x1 << 12)
+#define UL3_PHASE_SYNC_CH1_FIFO_SEL_SFT 11
+#define UL3_PHASE_SYNC_CH1_FIFO_SEL_MASK 0x1
+#define UL3_PHASE_SYNC_CH1_FIFO_SEL_MASK_SFT (0x1 << 11)
+#define UL2_PHASE_SYNC_CH1_FIFO_SEL_SFT 10
+#define UL2_PHASE_SYNC_CH1_FIFO_SEL_MASK 0x1
+#define UL2_PHASE_SYNC_CH1_FIFO_SEL_MASK_SFT (0x1 << 10)
+#define UL1_PHASE_SYNC_CH1_FIFO_SEL_SFT 9
+#define UL1_PHASE_SYNC_CH1_FIFO_SEL_MASK 0x1
+#define UL1_PHASE_SYNC_CH1_FIFO_SEL_MASK_SFT (0x1 << 9)
+#define UL0_PHASE_SYNC_CH1_FIFO_SEL_SFT 8
+#define UL0_PHASE_SYNC_CH1_FIFO_SEL_MASK 0x1
+#define UL0_PHASE_SYNC_CH1_FIFO_SEL_MASK_SFT (0x1 << 8)
+#define UL_PHASE_SYNC_SOFT_RST_EN_2_ON_SFT 5
+#define UL_PHASE_SYNC_SOFT_RST_EN_2_ON_MASK 0x1
+#define UL_PHASE_SYNC_SOFT_RST_EN_2_ON_MASK_SFT (0x1 << 5)
+#define UL_PHASE_SYNC_SOFT_RST_EN_1_ON_SFT 4
+#define UL_PHASE_SYNC_SOFT_RST_EN_1_ON_MASK 0x1
+#define UL_PHASE_SYNC_SOFT_RST_EN_1_ON_MASK_SFT (0x1 << 4)
+#define UL_PHASE_SYNC_SOFT_RST_EN_0_ON_SFT 3
+#define UL_PHASE_SYNC_SOFT_RST_EN_0_ON_MASK 0x1
+#define UL_PHASE_SYNC_SOFT_RST_EN_0_ON_MASK_SFT (0x1 << 3)
+#define UL_PHASE_SYNC_SOFT_RST_2_ON_SFT 2
+#define UL_PHASE_SYNC_SOFT_RST_2_ON_MASK 0x1
+#define UL_PHASE_SYNC_SOFT_RST_2_ON_MASK_SFT (0x1 << 2)
+#define UL_PHASE_SYNC_SOFT_RST_1_ON_SFT 1
+#define UL_PHASE_SYNC_SOFT_RST_1_ON_MASK 0x1
+#define UL_PHASE_SYNC_SOFT_RST_1_ON_MASK_SFT (0x1 << 1)
+#define UL_PHASE_SYNC_SOFT_RST_0_ON_SFT 0
+#define UL_PHASE_SYNC_SOFT_RST_0_ON_MASK 0x1
+#define UL_PHASE_SYNC_SOFT_RST_0_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF_IPM_VER_MON */
+#define RG_MTKAIF_IPM_VER_MON_SFT 0
+#define RG_MTKAIF_IPM_VER_MON_MASK 0xffffffff
+#define RG_MTKAIF_IPM_VER_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_MTKAIF_MON_SEL */
+#define RG_MTKAIF_MON_SEL_SFT 0
+#define RG_MTKAIF_MON_SEL_MASK 0xff
+#define RG_MTKAIF_MON_SEL_MASK_SFT (0xff << 0)
+
+/* AFE_MTKAIF_MON */
+#define RG_MTKAIF_MON_SFT 0
+#define RG_MTKAIF_MON_MASK 0xffffffff
+#define RG_MTKAIF_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_MTKAIF0_CFG0 */
+#define RG_MTKAIF0_RXIF_CLKINV_SFT 31
+#define RG_MTKAIF0_RXIF_CLKINV_MASK 0x1
+#define RG_MTKAIF0_RXIF_CLKINV_MASK_SFT (0x1 << 31)
+#define RG_MTKAIF0_RXIF_BYPASS_SRC_SFT 17
+#define RG_MTKAIF0_RXIF_BYPASS_SRC_MASK 0x1
+#define RG_MTKAIF0_RXIF_BYPASS_SRC_MASK_SFT (0x1 << 17)
+#define RG_MTKAIF0_RXIF_PROTOCOL2_SFT 16
+#define RG_MTKAIF0_RXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF0_RXIF_PROTOCOL2_MASK_SFT (0x1 << 16)
+#define RG_MTKAIF0_TXIF_NLE_DEBUG_SFT 8
+#define RG_MTKAIF0_TXIF_NLE_DEBUG_MASK 0x1
+#define RG_MTKAIF0_TXIF_NLE_DEBUG_MASK_SFT (0x1 << 8)
+#define RG_MTKAIF0_TXIF_BYPASS_SRC_SFT 5
+#define RG_MTKAIF0_TXIF_BYPASS_SRC_MASK 0x1
+#define RG_MTKAIF0_TXIF_BYPASS_SRC_MASK_SFT (0x1 << 5)
+#define RG_MTKAIF0_TXIF_PROTOCOL2_SFT 4
+#define RG_MTKAIF0_TXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF0_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define RG_MTKAIF0_TXIF_8TO5_SFT 2
+#define RG_MTKAIF0_TXIF_8TO5_MASK 0x1
+#define RG_MTKAIF0_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define RG_MTKAIF0_RXIF_8TO5_SFT 1
+#define RG_MTKAIF0_RXIF_8TO5_MASK 0x1
+#define RG_MTKAIF0_RXIF_8TO5_MASK_SFT (0x1 << 1)
+#define RG_MTKAIF0_TX2RX_LOOPBACK1_SFT 0
+#define RG_MTKAIF0_TX2RX_LOOPBACK1_MASK 0x1
+#define RG_MTKAIF0_TX2RX_LOOPBACK1_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF0_TX_CFG0 */
+#define RG_MTKAIF0_TXIF_NLE_FIFO_SWAP_SFT 23
+#define RG_MTKAIF0_TXIF_NLE_FIFO_SWAP_MASK 0x1
+#define RG_MTKAIF0_TXIF_NLE_FIFO_SWAP_MASK_SFT (0x1 << 23)
+#define RG_MTKAIF0_TXIF_NLE_FIFO_RSP_SFT 20
+#define RG_MTKAIF0_TXIF_NLE_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF0_TXIF_NLE_FIFO_RSP_MASK_SFT (0x7 << 20)
+#define RG_MTKAIF0_TXIF_FIFO_SWAP_SFT 15
+#define RG_MTKAIF0_TXIF_FIFO_SWAP_MASK 0x1
+#define RG_MTKAIF0_TXIF_FIFO_SWAP_MASK_SFT (0x1 << 15)
+#define RG_MTKAIF0_TXIF_FIFO_RSP_SFT 12
+#define RG_MTKAIF0_TXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF0_TXIF_FIFO_RSP_MASK_SFT (0x7 << 12)
+#define RG_MTKAIF0_TXIF_SYNC_WORD1_SFT 4
+#define RG_MTKAIF0_TXIF_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF0_TXIF_SYNC_WORD1_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF0_TXIF_SYNC_WORD0_SFT 0
+#define RG_MTKAIF0_TXIF_SYNC_WORD0_MASK 0x7
+#define RG_MTKAIF0_TXIF_SYNC_WORD0_MASK_SFT (0x7 << 0)
+
+/* AFE_MTKAIF0_RX_CFG0 */
+#define RG_MTKAIF0_RXIF_VOICE_MODE_SFT 20
+#define RG_MTKAIF0_RXIF_VOICE_MODE_MASK 0xf
+#define RG_MTKAIF0_RXIF_VOICE_MODE_MASK_SFT (0xf << 20)
+#define RG_MTKAIF0_RXIF_DETECT_ON_SFT 16
+#define RG_MTKAIF0_RXIF_DETECT_ON_MASK 0x1
+#define RG_MTKAIF0_RXIF_DETECT_ON_MASK_SFT (0x1 << 16)
+#define RG_MTKAIF0_RXIF_DATA_BIT_SFT 8
+#define RG_MTKAIF0_RXIF_DATA_BIT_MASK 0x7
+#define RG_MTKAIF0_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define RG_MTKAIF0_RXIF_FIFO_RSP_SFT 4
+#define RG_MTKAIF0_RXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF0_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF0_RXIF_DATA_MODE_SFT 0
+#define RG_MTKAIF0_RXIF_DATA_MODE_MASK 0x1
+#define RG_MTKAIF0_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF0_RX_CFG1 */
+#define RG_MTKAIF0_RXIF_CLEAR_SYNC_FAIL_SFT 28
+#define RG_MTKAIF0_RXIF_CLEAR_SYNC_FAIL_MASK 0x1
+#define RG_MTKAIF0_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 28)
+#define RG_MTKAIF0_RXIF_SYNC_CNT_TABLE_SFT 16
+#define RG_MTKAIF0_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define RG_MTKAIF0_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 16)
+#define RG_MTKAIF0_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define RG_MTKAIF0_RXIF_SYNC_SEARCH_TABLE_MASK 0xf
+#define RG_MTKAIF0_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF0_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8
+#define RG_MTKAIF0_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF0_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define RG_MTKAIF0_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define RG_MTKAIF0_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF0_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+
+/* AFE_MTKAIF0_RX_CFG2 */
+#define RG_MTKAIF0_RXIF_SYNC_WORD1_DISABLE_SFT 27
+#define RG_MTKAIF0_RXIF_SYNC_WORD1_DISABLE_MASK 0x1
+#define RG_MTKAIF0_RXIF_SYNC_WORD1_DISABLE_MASK_SFT (0x1 << 27)
+#define RG_MTKAIF0_RXIF_SYNC_WORD1_SFT 24
+#define RG_MTKAIF0_RXIF_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF0_RXIF_SYNC_WORD1_MASK_SFT (0x7 << 24)
+#define RG_MTKAIF0_RXIF_SYNC_WORD0_DISABLE_SFT 23
+#define RG_MTKAIF0_RXIF_SYNC_WORD0_DISABLE_MASK 0x1
+#define RG_MTKAIF0_RXIF_SYNC_WORD0_DISABLE_MASK_SFT (0x1 << 23)
+#define RG_MTKAIF0_RXIF_SYNC_WORD0_SFT 20
+#define RG_MTKAIF0_RXIF_SYNC_WORD0_MASK 0x7
+#define RG_MTKAIF0_RXIF_SYNC_WORD0_MASK_SFT (0x7 << 20)
+#define RG_MTKAIF0_RXIF_DELAY_CYCLE_SFT 12
+#define RG_MTKAIF0_RXIF_DELAY_CYCLE_MASK 0xf
+#define RG_MTKAIF0_RXIF_DELAY_CYCLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF0_RXIF_DELAY_DATA_SFT 8
+#define RG_MTKAIF0_RXIF_DELAY_DATA_MASK 0x1
+#define RG_MTKAIF0_RXIF_DELAY_DATA_MASK_SFT (0x1 << 8)
+
+/* AFE_MTKAIF1_CFG0 */
+#define RG_MTKAIF1_RXIF_CLKINV_ADC_SFT 31
+#define RG_MTKAIF1_RXIF_CLKINV_ADC_MASK 0x1
+#define RG_MTKAIF1_RXIF_CLKINV_ADC_MASK_SFT (0x1 << 31)
+#define RG_MTKAIF1_RXIF_BYPASS_SRC_SFT 17
+#define RG_MTKAIF1_RXIF_BYPASS_SRC_MASK 0x1
+#define RG_MTKAIF1_RXIF_BYPASS_SRC_MASK_SFT (0x1 << 17)
+#define RG_MTKAIF1_RXIF_PROTOCOL2_SFT 16
+#define RG_MTKAIF1_RXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF1_RXIF_PROTOCOL2_MASK_SFT (0x1 << 16)
+#define RG_MTKAIF1_TXIF_NLE_DEBUG_SFT 8
+#define RG_MTKAIF1_TXIF_NLE_DEBUG_MASK 0x1
+#define RG_MTKAIF1_TXIF_NLE_DEBUG_MASK_SFT (0x1 << 8)
+#define RG_MTKAIF1_TXIF_BYPASS_SRC_SFT 5
+#define RG_MTKAIF1_TXIF_BYPASS_SRC_MASK 0x1
+#define RG_MTKAIF1_TXIF_BYPASS_SRC_MASK_SFT (0x1 << 5)
+#define RG_MTKAIF1_TXIF_PROTOCOL2_SFT 4
+#define RG_MTKAIF1_TXIF_PROTOCOL2_MASK 0x1
+#define RG_MTKAIF1_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define RG_MTKAIF1_TXIF_8TO5_SFT 2
+#define RG_MTKAIF1_TXIF_8TO5_MASK 0x1
+#define RG_MTKAIF1_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define RG_MTKAIF1_RXIF_8TO5_SFT 1
+#define RG_MTKAIF1_RXIF_8TO5_MASK 0x1
+#define RG_MTKAIF1_RXIF_8TO5_MASK_SFT (0x1 << 1)
+#define RG_MTKAIF1_IF_LOOPBACK1_SFT 0
+#define RG_MTKAIF1_IF_LOOPBACK1_MASK 0x1
+#define RG_MTKAIF1_IF_LOOPBACK1_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF1_TX_CFG0 */
+#define RG_MTKAIF1_TXIF_NLE_FIFO_SWAP_SFT 23
+#define RG_MTKAIF1_TXIF_NLE_FIFO_SWAP_MASK 0x1
+#define RG_MTKAIF1_TXIF_NLE_FIFO_SWAP_MASK_SFT (0x1 << 23)
+#define RG_MTKAIF1_TXIF_NLE_FIFO_RSP_SFT 20
+#define RG_MTKAIF1_TXIF_NLE_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF1_TXIF_NLE_FIFO_RSP_MASK_SFT (0x7 << 20)
+#define RG_MTKAIF1_TXIF_FIFO_SWAP_SFT 15
+#define RG_MTKAIF1_TXIF_FIFO_SWAP_MASK 0x1
+#define RG_MTKAIF1_TXIF_FIFO_SWAP_MASK_SFT (0x1 << 15)
+#define RG_MTKAIF1_TXIF_FIFO_RSP_SFT 12
+#define RG_MTKAIF1_TXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF1_TXIF_FIFO_RSP_MASK_SFT (0x7 << 12)
+#define RG_MTKAIF1_TXIF_SYNC_WORD1_SFT 4
+#define RG_MTKAIF1_TXIF_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF1_TXIF_SYNC_WORD1_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF1_TXIF_SYNC_WORD0_SFT 0
+#define RG_MTKAIF1_TXIF_SYNC_WORD0_MASK 0x7
+#define RG_MTKAIF1_TXIF_SYNC_WORD0_MASK_SFT (0x7 << 0)
+
+/* AFE_MTKAIF1_RX_CFG0 */
+#define RG_MTKAIF1_RXIF_VOICE_MODE_SFT 20
+#define RG_MTKAIF1_RXIF_VOICE_MODE_MASK 0xf
+#define RG_MTKAIF1_RXIF_VOICE_MODE_MASK_SFT (0xf << 20)
+#define RG_MTKAIF1_RXIF_DETECT_ON_SFT 16
+#define RG_MTKAIF1_RXIF_DETECT_ON_MASK 0x1
+#define RG_MTKAIF1_RXIF_DETECT_ON_MASK_SFT (0x1 << 16)
+#define RG_MTKAIF1_RXIF_DATA_BIT_SFT 8
+#define RG_MTKAIF1_RXIF_DATA_BIT_MASK 0x7
+#define RG_MTKAIF1_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define RG_MTKAIF1_RXIF_FIFO_RSP_SFT 4
+#define RG_MTKAIF1_RXIF_FIFO_RSP_MASK 0x7
+#define RG_MTKAIF1_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define RG_MTKAIF1_RXIF_DATA_MODE_SFT 0
+#define RG_MTKAIF1_RXIF_DATA_MODE_MASK 0x1
+#define RG_MTKAIF1_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* AFE_MTKAIF1_RX_CFG1 */
+#define RG_MTKAIF1_RXIF_CLEAR_SYNC_FAIL_SFT 28
+#define RG_MTKAIF1_RXIF_CLEAR_SYNC_FAIL_MASK 0x1
+#define RG_MTKAIF1_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 28)
+#define RG_MTKAIF1_RXIF_SYNC_CNT_TABLE_SFT 16
+#define RG_MTKAIF1_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define RG_MTKAIF1_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 16)
+#define RG_MTKAIF1_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define RG_MTKAIF1_RXIF_SYNC_SEARCH_TABLE_MASK 0xf
+#define RG_MTKAIF1_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF1_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8
+#define RG_MTKAIF1_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF1_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define RG_MTKAIF1_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define RG_MTKAIF1_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define RG_MTKAIF1_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+
+/* AFE_MTKAIF1_RX_CFG2 */
+#define RG_MTKAIF1_RXIF_SYNC_WORD1_DISABLE_SFT 27
+#define RG_MTKAIF1_RXIF_SYNC_WORD1_DISABLE_MASK 0x1
+#define RG_MTKAIF1_RXIF_SYNC_WORD1_DISABLE_MASK_SFT (0x1 << 27)
+#define RG_MTKAIF1_RXIF_SYNC_WORD1_SFT 24
+#define RG_MTKAIF1_RXIF_SYNC_WORD1_MASK 0x7
+#define RG_MTKAIF1_RXIF_SYNC_WORD1_MASK_SFT (0x7 << 24)
+#define RG_MTKAIF1_RXIF_SYNC_WORD0_DISABLE_SFT 23
+#define RG_MTKAIF1_RXIF_SYNC_WORD0_DISABLE_MASK 0x1
+#define RG_MTKAIF1_RXIF_SYNC_WORD0_DISABLE_MASK_SFT (0x1 << 23)
+#define RG_MTKAIF1_RXIF_SYNC_WORD0_SFT 20
+#define RG_MTKAIF1_RXIF_SYNC_WORD0_MASK 0x7
+#define RG_MTKAIF1_RXIF_SYNC_WORD0_MASK_SFT (0x7 << 20)
+#define RG_MTKAIF1_RXIF_DELAY_CYCLE_SFT 12
+#define RG_MTKAIF1_RXIF_DELAY_CYCLE_MASK 0xf
+#define RG_MTKAIF1_RXIF_DELAY_CYCLE_MASK_SFT (0xf << 12)
+#define RG_MTKAIF1_RXIF_DELAY_DATA_SFT 8
+#define RG_MTKAIF1_RXIF_DELAY_DATA_MASK 0x1
+#define RG_MTKAIF1_RXIF_DELAY_DATA_MASK_SFT (0x1 << 8)
+
+/* AFE_AUD_PAD_TOP_CFG0 */
+#define AUD_PAD_TOP_FIFO_RSP_SFT 4
+#define AUD_PAD_TOP_FIFO_RSP_MASK 0xf
+#define AUD_PAD_TOP_FIFO_RSP_MASK_SFT (0xf << 4)
+#define RG_RX_PROTOCOL2_SFT 3
+#define RG_RX_PROTOCOL2_MASK 0x1
+#define RG_RX_PROTOCOL2_MASK_SFT (0x1 << 3)
+#define RG_RX_FIFO_ON_SFT 0
+#define RG_RX_FIFO_ON_MASK 0x1
+#define RG_RX_FIFO_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_AUD_PAD_TOP_MON */
+#define AUD_PAD_TOP_MON_SFT 0
+#define AUD_PAD_TOP_MON_MASK 0xffff
+#define AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_MTKAIFV4_TX_CFG0 */
+#define MTKAIFV4_TXIF_EN_SEL_SFT 12
+#define MTKAIFV4_TXIF_EN_SEL_MASK 0x1
+#define MTKAIFV4_TXIF_EN_SEL_MASK_SFT (0x1 << 12)
+#define MTKAIFV4_TXIF_V4_SFT 11
+#define MTKAIFV4_TXIF_V4_MASK 0x1
+#define MTKAIFV4_TXIF_V4_MASK_SFT (0x1 << 11)
+#define MTKAIFV4_ADDA6_OUT_EN_SEL_SFT 10
+#define MTKAIFV4_ADDA6_OUT_EN_SEL_MASK 0x1
+#define MTKAIFV4_ADDA6_OUT_EN_SEL_MASK_SFT (0x1 << 10)
+#define MTKAIFV4_ADDA_OUT_EN_SEL_SFT 9
+#define MTKAIFV4_ADDA_OUT_EN_SEL_MASK 0x1
+#define MTKAIFV4_ADDA_OUT_EN_SEL_MASK_SFT (0x1 << 9)
+#define MTKAIFV4_TXIF_INPUT_MODE_SFT 4
+#define MTKAIFV4_TXIF_INPUT_MODE_MASK 0x1f
+#define MTKAIFV4_TXIF_INPUT_MODE_MASK_SFT (0x1f << 4)
+#define MTKAIFV4_TXIF_FOUR_CHANNEL_SFT 1
+#define MTKAIFV4_TXIF_FOUR_CHANNEL_MASK 0x1
+#define MTKAIFV4_TXIF_FOUR_CHANNEL_MASK_SFT (0x1 << 1)
+#define MTKAIFV4_TXIF_AFE_ON_SFT 0
+#define MTKAIFV4_TXIF_AFE_ON_MASK 0x1
+#define MTKAIFV4_TXIF_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA6_MTKAIFV4_TX_CFG0 */
+#define ADDA6_MTKAIFV4_TXIF_EN_SEL_SFT 12
+#define ADDA6_MTKAIFV4_TXIF_EN_SEL_MASK 0x1
+#define ADDA6_MTKAIFV4_TXIF_EN_SEL_MASK_SFT (0x1 << 12)
+#define ADDA6_MTKAIFV4_TXIF_INPUT_MODE_SFT 4
+#define ADDA6_MTKAIFV4_TXIF_INPUT_MODE_MASK 0x1f
+#define ADDA6_MTKAIFV4_TXIF_INPUT_MODE_MASK_SFT (0x1f << 4)
+#define ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_SFT 1
+#define ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_MASK 0x1
+#define ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_MASK_SFT (0x1 << 1)
+#define ADDA6_MTKAIFV4_TXIF_AFE_ON_SFT 0
+#define ADDA6_MTKAIFV4_TXIF_AFE_ON_MASK 0x1
+#define ADDA6_MTKAIFV4_TXIF_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIFV4_RX_CFG0 */
+#define MTKAIFV4_RXIF_CLKINV_SFT 31
+#define MTKAIFV4_RXIF_CLKINV_MASK 0x1
+#define MTKAIFV4_RXIF_CLKINV_MASK_SFT (0x1 << 31)
+#define MTKAIFV4_RXIF_LOOPBACK_MODE_SFT 28
+#define MTKAIFV4_RXIF_LOOPBACK_MODE_MASK 0x1
+#define MTKAIFV4_RXIF_LOOPBACK_MODE_MASK_SFT (0x1 << 28)
+#define MTKAIFV4_UL_CH7CH8_IN_EN_SEL_SFT 19
+#define MTKAIFV4_UL_CH7CH8_IN_EN_SEL_MASK 0x1
+#define MTKAIFV4_UL_CH7CH8_IN_EN_SEL_MASK_SFT (0x1 << 19)
+#define MTKAIFV4_UL_CH5CH6_IN_EN_SEL_SFT 18
+#define MTKAIFV4_UL_CH5CH6_IN_EN_SEL_MASK 0x1
+#define MTKAIFV4_UL_CH5CH6_IN_EN_SEL_MASK_SFT (0x1 << 18)
+#define MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT 17
+#define MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK 0x1
+#define MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT (0x1 << 17)
+#define MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT 16
+#define MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK 0x1
+#define MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT (0x1 << 16)
+#define MTKAIFV4_RXIF_EN_SEL_SFT 12
+#define MTKAIFV4_RXIF_EN_SEL_MASK 0x1
+#define MTKAIFV4_RXIF_EN_SEL_MASK_SFT (0x1 << 12)
+#define MTKAIFV4_RXIF_INPUT_MODE_SFT 4
+#define MTKAIFV4_RXIF_INPUT_MODE_MASK 0x1f
+#define MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT (0x1f << 4)
+#define MTKAIFV4_RXIF_FOUR_CHANNEL_SFT 1
+#define MTKAIFV4_RXIF_FOUR_CHANNEL_MASK 0x1
+#define MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT (0x1 << 1)
+#define MTKAIFV4_RXIF_AFE_ON_SFT 0
+#define MTKAIFV4_RXIF_AFE_ON_MASK 0x1
+#define MTKAIFV4_RXIF_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIFV4_RX_CFG1 */
+#define MTKAIFV4_RXIF_SYNC_CNT_TABLE_SFT 17
+#define MTKAIFV4_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define MTKAIFV4_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 17)
+#define MTKAIFV4_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define MTKAIFV4_RXIF_SYNC_SEARCH_TABLE_MASK 0x1f
+#define MTKAIFV4_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0x1f << 12)
+#define MTKAIFV4_RXIF_INVAILD_SYNC_CHECK_ROUND_SFT 8
+#define MTKAIFV4_RXIF_INVAILD_SYNC_CHECK_ROUND_MASK 0xf
+#define MTKAIFV4_RXIF_INVAILD_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define MTKAIFV4_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define MTKAIFV4_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define MTKAIFV4_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+#define MTKAIFV4_RXIF_FIFO_RSP_SFT 1
+#define MTKAIFV4_RXIF_FIFO_RSP_MASK 0x7
+#define MTKAIFV4_RXIF_FIFO_RSP_MASK_SFT (0x7 << 1)
+#define MTKAIFV4_RXIF_SELF_DEFINE_TABLE_SFT 0
+#define MTKAIFV4_RXIF_SELF_DEFINE_TABLE_MASK 0x1
+#define MTKAIFV4_RXIF_SELF_DEFINE_TABLE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA6_MTKAIFV4_RX_CFG0 */
+#define ADDA6_MTKAIFV4_RXIF_CLKINV_SFT 31
+#define ADDA6_MTKAIFV4_RXIF_CLKINV_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_CLKINV_MASK_SFT (0x1 << 31)
+#define ADDA6_MTKAIFV4_RXIF_LOOPBACK_MODE_SFT 28
+#define ADDA6_MTKAIFV4_RXIF_LOOPBACK_MODE_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_LOOPBACK_MODE_MASK_SFT (0x1 << 28)
+#define ADDA6_MTKAIFV4_RXIF_EN_SEL_SFT 12
+#define ADDA6_MTKAIFV4_RXIF_EN_SEL_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_EN_SEL_MASK_SFT (0x1 << 12)
+#define ADDA6_MTKAIFV4_RXIF_INPUT_MODE_SFT 4
+#define ADDA6_MTKAIFV4_RXIF_INPUT_MODE_MASK 0x1f
+#define ADDA6_MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT (0x1f << 4)
+#define ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_SFT 1
+#define ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT (0x1 << 1)
+#define ADDA6_MTKAIFV4_RXIF_AFE_ON_SFT 0
+#define ADDA6_MTKAIFV4_RXIF_AFE_ON_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA6_MTKAIFV4_RX_CFG1 */
+#define ADDA6_MTKAIFV4_RXIF_SYNC_CNT_TABLE_SFT 17
+#define ADDA6_MTKAIFV4_RXIF_SYNC_CNT_TABLE_MASK 0xfff
+#define ADDA6_MTKAIFV4_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 17)
+#define ADDA6_MTKAIFV4_RXIF_SYNC_SEARCH_TABLE_SFT 12
+#define ADDA6_MTKAIFV4_RXIF_SYNC_SEARCH_TABLE_MASK 0x1f
+#define ADDA6_MTKAIFV4_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0x1f << 12)
+#define ADDA6_MTKAIFV4_RXIF_INVAILD_SYNC_CHECK_ROUND_SFT 8
+#define ADDA6_MTKAIFV4_RXIF_INVAILD_SYNC_CHECK_ROUND_MASK 0xf
+#define ADDA6_MTKAIFV4_RXIF_INVAILD_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8)
+#define ADDA6_MTKAIFV4_RXIF_SYNC_CHECK_ROUND_SFT 4
+#define ADDA6_MTKAIFV4_RXIF_SYNC_CHECK_ROUND_MASK 0xf
+#define ADDA6_MTKAIFV4_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4)
+#define ADDA6_MTKAIFV4_RXIF_FIFO_RSP_SFT 1
+#define ADDA6_MTKAIFV4_RXIF_FIFO_RSP_MASK 0x7
+#define ADDA6_MTKAIFV4_RXIF_FIFO_RSP_MASK_SFT (0x7 << 1)
+#define ADDA6_MTKAIFV4_RXIF_SELF_DEFINE_TABLE_SFT 0
+#define ADDA6_MTKAIFV4_RXIF_SELF_DEFINE_TABLE_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_SELF_DEFINE_TABLE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIFV4_TX_SYNCWORD_CFG */
+#define ADDA6_MTKAIFV4_TXIF_SYNCWORD_SFT 16
+#define ADDA6_MTKAIFV4_TXIF_SYNCWORD_MASK 0xffff
+#define ADDA6_MTKAIFV4_TXIF_SYNCWORD_MASK_SFT (0xffff << 16)
+#define ADDA_MTKAIFV4_TXIF_SYNCWORD_SFT 0
+#define ADDA_MTKAIFV4_TXIF_SYNCWORD_MASK 0xffff
+#define ADDA_MTKAIFV4_TXIF_SYNCWORD_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_MTKAIFV4_RX_SYNCWORD_CFG */
+#define ADDA6_MTKAIFV4_RXIF_SYNCWORD_SFT 16
+#define ADDA6_MTKAIFV4_RXIF_SYNCWORD_MASK 0xffff
+#define ADDA6_MTKAIFV4_RXIF_SYNCWORD_MASK_SFT (0xffff << 16)
+#define ADDA_MTKAIFV4_RXIF_SYNCWORD_SFT 0
+#define ADDA_MTKAIFV4_RXIF_SYNCWORD_MASK 0xffff
+#define ADDA_MTKAIFV4_RXIF_SYNCWORD_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_MTKAIFV4_MON0 */
+#define MTKAIFV4_TXIF_SDATA_OUT_SFT 23
+#define MTKAIFV4_TXIF_SDATA_OUT_MASK 0x1
+#define MTKAIFV4_TXIF_SDATA_OUT_MASK_SFT (0x1 << 23)
+#define MTKAIFV4_RXIF_SDATA_IN_SFT 22
+#define MTKAIFV4_RXIF_SDATA_IN_MASK 0x1
+#define MTKAIFV4_RXIF_SDATA_IN_MASK_SFT (0x1 << 22)
+#define MTKAIFV4_RXIF_SEARCH_FAIL_FLAG_SFT 21
+#define MTKAIFV4_RXIF_SEARCH_FAIL_FLAG_MASK 0x1
+#define MTKAIFV4_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 21)
+#define MTKAIFV4_RXIF_ADC_FIFO_STATUS_SFT 0
+#define MTKAIFV4_RXIF_ADC_FIFO_STATUS_MASK 0xfff
+#define MTKAIFV4_RXIF_ADC_FIFO_STATUS_MASK_SFT (0xfff << 0)
+
+/* AFE_ADDA_MTKAIFV4_MON1 */
+#define MTKAIFV4_RXIF_OUT_CH4_SFT 24
+#define MTKAIFV4_RXIF_OUT_CH4_MASK 0xff
+#define MTKAIFV4_RXIF_OUT_CH4_MASK_SFT (0xff << 24)
+#define MTKAIFV4_RXIF_OUT_CH3_SFT 16
+#define MTKAIFV4_RXIF_OUT_CH3_MASK 0xff
+#define MTKAIFV4_RXIF_OUT_CH3_MASK_SFT (0xff << 16)
+#define MTKAIFV4_RXIF_OUT_CH2_SFT 8
+#define MTKAIFV4_RXIF_OUT_CH2_MASK 0xff
+#define MTKAIFV4_RXIF_OUT_CH2_MASK_SFT (0xff << 8)
+#define MTKAIFV4_RXIF_OUT_CH1_SFT 0
+#define MTKAIFV4_RXIF_OUT_CH1_MASK 0xff
+#define MTKAIFV4_RXIF_OUT_CH1_MASK_SFT (0xff << 0)
+
+/* AFE_ADDA6_MTKAIFV4_MON0 */
+#define ADDA6_MTKAIFV4_TXIF_SDATA_OUT_SFT 23
+#define ADDA6_MTKAIFV4_TXIF_SDATA_OUT_MASK 0x1
+#define ADDA6_MTKAIFV4_TXIF_SDATA_OUT_MASK_SFT (0x1 << 23)
+#define ADDA6_MTKAIFV4_RXIF_SDATA_IN_SFT 22
+#define ADDA6_MTKAIFV4_RXIF_SDATA_IN_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_SDATA_IN_MASK_SFT (0x1 << 22)
+#define ADDA6_MTKAIFV4_RXIF_SEARCH_FAIL_FLAG_SFT 21
+#define ADDA6_MTKAIFV4_RXIF_SEARCH_FAIL_FLAG_MASK 0x1
+#define ADDA6_MTKAIFV4_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 21)
+#define ADDA6_MTKAIFV3P3_RXIF_ADC_FIFO_STATUS_SFT 0
+#define ADDA6_MTKAIFV3P3_RXIF_ADC_FIFO_STATUS_MASK 0xfff
+#define ADDA6_MTKAIFV3P3_RXIF_ADC_FIFO_STATUS_MASK_SFT (0xfff << 0)
+
+/* ETDM_IN0_CON0 */
+/* ETDM_IN1_CON0 */
+#define REG_ETDM_IN_EN_SFT 0
+#define REG_ETDM_IN_EN_MASK 0x1
+#define REG_ETDM_IN_EN_MASK_SFT (0x1 << 0)
+#define REG_SYNC_MODE_SFT 1
+#define REG_SYNC_MODE_MASK 0x1
+#define REG_SYNC_MODE_MASK_SFT (0x1 << 1)
+#define REG_LSB_FIRST_SFT 3
+#define REG_LSB_FIRST_MASK 0x1
+#define REG_LSB_FIRST_MASK_SFT (0x1 << 3)
+#define REG_SOFT_RST_SFT 4
+#define REG_SOFT_RST_MASK 0x1
+#define REG_SOFT_RST_MASK_SFT (0x1 << 4)
+#define REG_SLAVE_MODE_SFT 5
+#define REG_SLAVE_MODE_MASK 0x1
+#define REG_SLAVE_MODE_MASK_SFT (0x1 << 5)
+#define REG_FMT_SFT 6
+#define REG_FMT_MASK 0x7
+#define REG_FMT_MASK_SFT (0x7 << 6)
+#define REG_LRCK_EDGE_SEL_SFT 10
+#define REG_LRCK_EDGE_SEL_MASK 0x1
+#define REG_LRCK_EDGE_SEL_MASK_SFT (0x1 << 10)
+#define REG_BIT_LENGTH_SFT 11
+#define REG_BIT_LENGTH_MASK 0x1f
+#define REG_BIT_LENGTH_MASK_SFT (0x1f << 11)
+#define REG_WORD_LENGTH_SFT 16
+#define REG_WORD_LENGTH_MASK 0x1f
+#define REG_WORD_LENGTH_MASK_SFT (0x1f << 16)
+#define REG_CH_NUM_SFT 23
+#define REG_CH_NUM_MASK 0x1f
+#define REG_CH_NUM_MASK_SFT (0x1f << 23)
+#define REG_RELATCH_1X_EN_DOMAIN_SEL_SFT 28
+#define REG_RELATCH_1X_EN_DOMAIN_SEL_MASK 0x7
+#define REG_RELATCH_1X_EN_DOMAIN_SEL_MASK_SFT (0x7 << 28)
+#define REG_VALID_TOGETHER_SFT 31
+#define REG_VALID_TOGETHER_MASK 0x1
+#define REG_VALID_TOGETHER_MASK_SFT (0x1 << 31)
+
+/* ETDM_IN0_CON1 */
+/* ETDM_IN1_CON1 */
+#define REG_INITIAL_COUNT_SFT 0
+#define REG_INITIAL_COUNT_MASK 0x1f
+#define REG_INITIAL_COUNT_MASK_SFT (0x1f << 0)
+#define REG_INITIAL_POINT_SFT 5
+#define REG_INITIAL_POINT_MASK 0x1f
+#define REG_INITIAL_POINT_MASK_SFT (0x1f << 5)
+#define REG_LRCK_AUTO_OFF_SFT 10
+#define REG_LRCK_AUTO_OFF_MASK 0x1
+#define REG_LRCK_AUTO_OFF_MASK_SFT (0x1 << 10)
+#define REG_BCK_AUTO_OFF_SFT 11
+#define REG_BCK_AUTO_OFF_MASK 0x1
+#define REG_BCK_AUTO_OFF_MASK_SFT (0x1 << 11)
+#define REG_INITIAL_LRCK_SFT 13
+#define REG_INITIAL_LRCK_MASK 0x1
+#define REG_INITIAL_LRCK_MASK_SFT (0x1 << 13)
+#define REG_NO_ALIGN_1X_EN_SFT 14
+#define REG_NO_ALIGN_1X_EN_MASK 0x1
+#define REG_NO_ALIGN_1X_EN_MASK_SFT (0x1 << 14)
+#define REG_LRCK_RESET_SFT 15
+#define REG_LRCK_RESET_MASK 0x1
+#define REG_LRCK_RESET_MASK_SFT (0x1 << 15)
+#define PINMUX_MCLK_CTRL_OE_SFT 16
+#define PINMUX_MCLK_CTRL_OE_MASK 0x1
+#define PINMUX_MCLK_CTRL_OE_MASK_SFT (0x1 << 16)
+#define REG_OUTPUT_CR_EN_SFT 18
+#define REG_OUTPUT_CR_EN_MASK 0x1
+#define REG_OUTPUT_CR_EN_MASK_SFT (0x1 << 18)
+#define REG_LR_ALIGN_SFT 19
+#define REG_LR_ALIGN_MASK 0x1
+#define REG_LR_ALIGN_MASK_SFT (0x1 << 19)
+#define REG_LRCK_WIDTH_SFT 20
+#define REG_LRCK_WIDTH_MASK 0x3ff
+#define REG_LRCK_WIDTH_MASK_SFT (0x3ff << 20)
+#define REG_DIRECT_INPUT_MASTER_BCK_SFT 30
+#define REG_DIRECT_INPUT_MASTER_BCK_MASK 0x1
+#define REG_DIRECT_INPUT_MASTER_BCK_MASK_SFT (0x1 << 30)
+#define REG_LRCK_AUTO_MODE_SFT 31
+#define REG_LRCK_AUTO_MODE_MASK 0x1
+#define REG_LRCK_AUTO_MODE_MASK_SFT (0x1 << 31)
+
+/* ETDM_IN0_CON2 */
+/* ETDM_IN1_CON2 */
+#define REG_UPDATE_POINT_SFT 0
+#define REG_UPDATE_POINT_MASK 0x1f
+#define REG_UPDATE_POINT_MASK_SFT (0x1f << 0)
+#define REG_UPDATE_GAP_SFT 5
+#define REG_UPDATE_GAP_MASK 0x1f
+#define REG_UPDATE_GAP_MASK_SFT (0x1f << 5)
+#define REG_CLOCK_SOURCE_SEL_SFT 10
+#define REG_CLOCK_SOURCE_SEL_MASK 0x7
+#define REG_CLOCK_SOURCE_SEL_MASK_SFT (0x7 << 10)
+#define REG_CK_EN_SEL_AUTO_SFT 14
+#define REG_CK_EN_SEL_AUTO_MASK 0x1
+#define REG_CK_EN_SEL_AUTO_MASK_SFT (0x1 << 14)
+#define REG_MULTI_IP_TOTAL_CHNUM_SFT 15
+#define REG_MULTI_IP_TOTAL_CHNUM_MASK 0x1f
+#define REG_MULTI_IP_TOTAL_CHNUM_MASK_SFT (0x1f << 15)
+#define REG_MASK_AUTO_SFT 20
+#define REG_MASK_AUTO_MASK 0x1
+#define REG_MASK_AUTO_MASK_SFT (0x1 << 20)
+#define REG_MASK_NUM_SFT 21
+#define REG_MASK_NUM_MASK 0x1f
+#define REG_MASK_NUM_MASK_SFT (0x1f << 21)
+#define REG_UPDATE_POINT_AUTO_SFT 26
+#define REG_UPDATE_POINT_AUTO_MASK 0x1
+#define REG_UPDATE_POINT_AUTO_MASK_SFT (0x1 << 26)
+#define REG_SDATA_DELAY_0P5T_EN_SFT 27
+#define REG_SDATA_DELAY_0P5T_EN_MASK 0x1
+#define REG_SDATA_DELAY_0P5T_EN_MASK_SFT (0x1 << 27)
+#define REG_SDATA_DELAY_BCK_INV_SFT 28
+#define REG_SDATA_DELAY_BCK_INV_MASK 0x1
+#define REG_SDATA_DELAY_BCK_INV_MASK_SFT (0x1 << 28)
+#define REG_LRCK_DELAY_0P5T_EN_SFT 29
+#define REG_LRCK_DELAY_0P5T_EN_MASK 0x1
+#define REG_LRCK_DELAY_0P5T_EN_MASK_SFT (0x1 << 29)
+#define REG_LRCK_DELAY_BCK_INV_SFT 30
+#define REG_LRCK_DELAY_BCK_INV_MASK 0x1
+#define REG_LRCK_DELAY_BCK_INV_MASK_SFT (0x1 << 30)
+#define REG_MULTI_IP_MODE_SFT 31
+#define REG_MULTI_IP_MODE_MASK 0x1
+#define REG_MULTI_IP_MODE_MASK_SFT (0x1 << 31)
+
+/* ETDM_IN0_CON3 */
+/* ETDM_IN1_CON3 */
+#define REG_DISABLE_OUT_SFT 0
+#define REG_DISABLE_OUT_MASK 0xffff
+#define REG_DISABLE_OUT_MASK_SFT (0xffff << 0)
+#define REG_RJ_DATA_RIGHT_ALIGN_SFT 16
+#define REG_RJ_DATA_RIGHT_ALIGN_MASK 0x1
+#define REG_RJ_DATA_RIGHT_ALIGN_MASK_SFT (0x1 << 16)
+#define REG_MONITOR_SEL_SFT 17
+#define REG_MONITOR_SEL_MASK 0x3
+#define REG_MONITOR_SEL_MASK_SFT (0x3 << 17)
+#define REG_CNT_UPPER_LIMIT_SFT 19
+#define REG_CNT_UPPER_LIMIT_MASK 0x3f
+#define REG_CNT_UPPER_LIMIT_MASK_SFT (0x3f << 19)
+#define REG_COMPACT_SAMPLE_END_DIS_SFT 25
+#define REG_COMPACT_SAMPLE_END_DIS_MASK 0x1
+#define REG_COMPACT_SAMPLE_END_DIS_MASK_SFT (0x1 << 25)
+#define REG_FS_TIMING_SEL_SFT 26
+#define REG_FS_TIMING_SEL_MASK 0x1f
+#define REG_FS_TIMING_SEL_MASK_SFT (0x1f << 26)
+#define REG_SAMPLE_END_MODE_SFT 31
+#define REG_SAMPLE_END_MODE_MASK 0x1
+#define REG_SAMPLE_END_MODE_MASK_SFT (0x1 << 31)
+
+/* ETDM_IN0_CON4 */
+/* ETDM_IN1_CON4 */
+#define REG_ALWAYS_OPEN_1X_EN_SFT 31
+#define REG_ALWAYS_OPEN_1X_EN_MASK 0x1
+#define REG_ALWAYS_OPEN_1X_EN_MASK_SFT (0x1 << 31)
+#define REG_WAIT_LAST_SAMPLE_SFT 30
+#define REG_WAIT_LAST_SAMPLE_MASK 0x1
+#define REG_WAIT_LAST_SAMPLE_MASK_SFT (0x1 << 30)
+#define REG_SAMPLE_END_POINT_SFT 25
+#define REG_SAMPLE_END_POINT_MASK 0x1f
+#define REG_SAMPLE_END_POINT_MASK_SFT (0x1f << 25)
+#define REG_RELATCH_1X_EN_SEL_SFT 20
+#define REG_RELATCH_1X_EN_SEL_MASK 0x1f
+#define REG_RELATCH_1X_EN_SEL_MASK_SFT (0x1f << 20)
+#define REG_MASTER_WS_INV_SFT 19
+#define REG_MASTER_WS_INV_MASK 0x1
+#define REG_MASTER_WS_INV_MASK_SFT (0x1 << 19)
+#define REG_MASTER_BCK_INV_SFT 18
+#define REG_MASTER_BCK_INV_MASK 0x1
+#define REG_MASTER_BCK_INV_MASK_SFT (0x1 << 18)
+#define REG_SLAVE_LRCK_INV_SFT 17
+#define REG_SLAVE_LRCK_INV_MASK 0x1
+#define REG_SLAVE_LRCK_INV_MASK_SFT (0x1 << 17)
+#define REG_SLAVE_BCK_INV_SFT 16
+#define REG_SLAVE_BCK_INV_MASK 0x1
+#define REG_SLAVE_BCK_INV_MASK_SFT (0x1 << 16)
+#define REG_REPACK_CHNUM_SFT 12
+#define REG_REPACK_CHNUM_MASK 0xf
+#define REG_REPACK_CHNUM_MASK_SFT (0xf << 12)
+#define REG_ASYNC_RESET_SFT 11
+#define REG_ASYNC_RESET_MASK 0x1
+#define REG_ASYNC_RESET_MASK_SFT (0x1 << 11)
+#define REG_REPACK_WORD_LENGTH_SFT 9
+#define REG_REPACK_WORD_LENGTH_MASK 0x3
+#define REG_REPACK_WORD_LENGTH_MASK_SFT (0x3 << 9)
+#define REG_REPACK_AUTO_MODE_SFT 8
+#define REG_REPACK_AUTO_MODE_MASK 0x1
+#define REG_REPACK_AUTO_MODE_MASK_SFT (0x1 << 8)
+#define REG_REPACK_MODE_SFT 0
+#define REG_REPACK_MODE_MASK 0x3f
+#define REG_REPACK_MODE_MASK_SFT (0x3f << 0)
+
+/* ETDM_IN0_CON5 */
+/* ETDM_IN1_CON5 */
+#define REG_LR_SWAP_SFT 16
+#define REG_LR_SWAP_MASK 0xffff
+#define REG_LR_SWAP_MASK_SFT (0xffff << 16)
+#define REG_ODD_FLAG_EN_SFT 0
+#define REG_ODD_FLAG_EN_MASK 0xffff
+#define REG_ODD_FLAG_EN_MASK_SFT (0xffff << 0)
+
+/* ETDM_IN0_CON6 */
+/* ETDM_IN1_CON6 */
+#define LCH_DATA_REG_SFT 0
+#define LCH_DATA_REG_MASK 0xffffffff
+#define LCH_DATA_REG_MASK_SFT (0xffffffff << 0)
+
+/* ETDM_IN0_CON7 */
+/* ETDM_IN1_CON7 */
+#define RCH_DATA_REG_SFT 0
+#define RCH_DATA_REG_MASK 0xffffffff
+#define RCH_DATA_REG_MASK_SFT (0xffffffff << 0)
+
+/* ETDM_IN0_CON8 */
+/* ETDM_IN1_CON8 */
+#define REG_AFIFO_THRESHOLD_SFT 29
+#define REG_AFIFO_THRESHOLD_MASK 0x3
+#define REG_AFIFO_THRESHOLD_MASK_SFT (0x3 << 29)
+#define REG_CK_EN_SEL_MANUAL_SFT 16
+#define REG_CK_EN_SEL_MANUAL_MASK 0x3ff
+#define REG_CK_EN_SEL_MANUAL_MASK_SFT (0x3ff << 16)
+#define REG_AFIFO_SW_RESET_SFT 15
+#define REG_AFIFO_SW_RESET_MASK 0x1
+#define REG_AFIFO_SW_RESET_MASK_SFT (0x1 << 15)
+#define REG_AFIFO_RESET_SEL_SFT 14
+#define REG_AFIFO_RESET_SEL_MASK 0x1
+#define REG_AFIFO_RESET_SEL_MASK_SFT (0x1 << 14)
+#define REG_AFIFO_AUTO_RESET_DIS_SFT 9
+#define REG_AFIFO_AUTO_RESET_DIS_MASK 0x1
+#define REG_AFIFO_AUTO_RESET_DIS_MASK_SFT (0x1 << 9)
+#define REG_ETDM_USE_AFIFO_SFT 8
+#define REG_ETDM_USE_AFIFO_MASK 0x1
+#define REG_ETDM_USE_AFIFO_MASK_SFT (0x1 << 8)
+#define REG_AFIFO_CLOCK_DOMAIN_SEL_SFT 5
+#define REG_AFIFO_CLOCK_DOMAIN_SEL_MASK 0x7
+#define REG_AFIFO_CLOCK_DOMAIN_SEL_MASK_SFT (0x7 << 5)
+#define REG_AFIFO_MODE_SFT 0
+#define REG_AFIFO_MODE_MASK 0x1f
+#define REG_AFIFO_MODE_MASK_SFT (0x1f << 0)
+
+/* ETDM_IN0_CON9 */
+/* ETDM_IN1_CON9 */
+#define REG_OUT2LATCH_TIME_SFT 10
+#define REG_OUT2LATCH_TIME_MASK 0x1f
+#define REG_OUT2LATCH_TIME_MASK_SFT (0x1f << 10)
+#define REG_ALMOST_END_BIT_COUNT_SFT 5
+#define REG_ALMOST_END_BIT_COUNT_MASK 0x1f
+#define REG_ALMOST_END_BIT_COUNT_MASK_SFT (0x1f << 5)
+#define REG_ALMOST_END_CH_COUNT_SFT 0
+#define REG_ALMOST_END_CH_COUNT_MASK 0x1f
+#define REG_ALMOST_END_CH_COUNT_MASK_SFT (0x1f << 0)
+
+/* ETDM_IN0_MON */
+/* ETDM_IN1_MON */
+#define LRCK_INV_SFT 30
+#define LRCK_INV_MASK 0x1
+#define LRCK_INV_MASK_SFT (0x1 << 30)
+#define EN_SYNC_OUT_SFT 29
+#define EN_SYNC_OUT_MASK 0x1
+#define EN_SYNC_OUT_MASK_SFT (0x1 << 29)
+#define HOPPING_EN_SYNC_OUT_PRE_SFT 28
+#define HOPPING_EN_SYNC_OUT_PRE_MASK 0x1
+#define HOPPING_EN_SYNC_OUT_PRE_MASK_SFT (0x1 << 28)
+#define WFULL_SFT 27
+#define WFULL_MASK 0x1
+#define WFULL_MASK_SFT (0x1 << 27)
+#define REMPTY_SFT 26
+#define REMPTY_MASK 0x1
+#define REMPTY_MASK_SFT (0x1 << 26)
+#define ETDM_2X_CK_EN_SFT 25
+#define ETDM_2X_CK_EN_MASK 0x1
+#define ETDM_2X_CK_EN_MASK_SFT (0x1 << 25)
+#define ETDM_1X_CK_EN_SFT 24
+#define ETDM_1X_CK_EN_MASK 0x1
+#define ETDM_1X_CK_EN_MASK_SFT (0x1 << 24)
+#define SDATA0_SFT 23
+#define SDATA0_MASK 0x1
+#define SDATA0_MASK_SFT (0x1 << 23)
+#define CURRENT_STATUS_SFT 21
+#define CURRENT_STATUS_MASK 0x3
+#define CURRENT_STATUS_MASK_SFT (0x3 << 21)
+#define BIT_POINT_SFT 16
+#define BIT_POINT_MASK 0x1f
+#define BIT_POINT_MASK_SFT (0x1f << 16)
+#define BIT_CH_COUNT_SFT 10
+#define BIT_CH_COUNT_MASK 0x3f
+#define BIT_CH_COUNT_MASK_SFT (0x3f << 10)
+#define BIT_COUNT_SFT 5
+#define BIT_COUNT_MASK 0x1f
+#define BIT_COUNT_MASK_SFT (0x1f << 5)
+#define CH_COUNT_SFT 0
+#define CH_COUNT_MASK 0x1f
+#define CH_COUNT_MASK_SFT (0x1f << 0)
+
+/* ETDM_OUT0_CON0 */
+/* ETDM_OUT1_CON0 */
+/* ETDM_OUT4_CON0 */
+#define OUT_REG_ETDM_OUT_EN_SFT 0
+#define OUT_REG_ETDM_OUT_EN_MASK 0x1
+#define OUT_REG_ETDM_OUT_EN_MASK_SFT (0x1 << 0)
+#define OUT_REG_SYNC_MODE_SFT 1
+#define OUT_REG_SYNC_MODE_MASK 0x1
+#define OUT_REG_SYNC_MODE_MASK_SFT (0x1 << 1)
+#define OUT_REG_LSB_FIRST_SFT 3
+#define OUT_REG_LSB_FIRST_MASK 0x1
+#define OUT_REG_LSB_FIRST_MASK_SFT (0x1 << 3)
+#define OUT_REG_SOFT_RST_SFT 4
+#define OUT_REG_SOFT_RST_MASK 0x1
+#define OUT_REG_SOFT_RST_MASK_SFT (0x1 << 4)
+#define OUT_REG_SLAVE_MODE_SFT 5
+#define OUT_REG_SLAVE_MODE_MASK 0x1
+#define OUT_REG_SLAVE_MODE_MASK_SFT (0x1 << 5)
+#define OUT_REG_FMT_SFT 6
+#define OUT_REG_FMT_MASK 0x7
+#define OUT_REG_FMT_MASK_SFT (0x7 << 6)
+#define OUT_REG_LRCK_EDGE_SEL_SFT 10
+#define OUT_REG_LRCK_EDGE_SEL_MASK 0x1
+#define OUT_REG_LRCK_EDGE_SEL_MASK_SFT (0x1 << 10)
+#define OUT_REG_BIT_LENGTH_SFT 11
+#define OUT_REG_BIT_LENGTH_MASK 0x1f
+#define OUT_REG_BIT_LENGTH_MASK_SFT (0x1f << 11)
+#define OUT_REG_WORD_LENGTH_SFT 16
+#define OUT_REG_WORD_LENGTH_MASK 0x1f
+#define OUT_REG_WORD_LENGTH_MASK_SFT (0x1f << 16)
+#define OUT_REG_CH_NUM_SFT 23
+#define OUT_REG_CH_NUM_MASK 0x1f
+#define OUT_REG_CH_NUM_MASK_SFT (0x1f << 23)
+#define OUT_REG_RELATCH_DOMAIN_SEL_SFT 28
+#define OUT_REG_RELATCH_DOMAIN_SEL_MASK 0x7
+#define OUT_REG_RELATCH_DOMAIN_SEL_MASK_SFT (0x7 << 28)
+#define OUT_REG_VALID_TOGETHER_SFT 31
+#define OUT_REG_VALID_TOGETHER_MASK 0x1
+#define OUT_REG_VALID_TOGETHER_MASK_SFT (0x1 << 31)
+
+/* ETDM_OUT0_CON1 */
+/* ETDM_OUT1_CON1 */
+/* ETDM_OUT4_CON1 */
+#define OUT_REG_INITIAL_COUNT_SFT 0
+#define OUT_REG_INITIAL_COUNT_MASK 0x1f
+#define OUT_REG_INITIAL_COUNT_MASK_SFT (0x1f << 0)
+#define OUT_REG_INITIAL_POINT_SFT 5
+#define OUT_REG_INITIAL_POINT_MASK 0x1f
+#define OUT_REG_INITIAL_POINT_MASK_SFT (0x1f << 5)
+#define OUT_REG_LRCK_AUTO_OFF_SFT 10
+#define OUT_REG_LRCK_AUTO_OFF_MASK 0x1
+#define OUT_REG_LRCK_AUTO_OFF_MASK_SFT (0x1 << 10)
+#define OUT_REG_BCK_AUTO_OFF_SFT 11
+#define OUT_REG_BCK_AUTO_OFF_MASK 0x1
+#define OUT_REG_BCK_AUTO_OFF_MASK_SFT (0x1 << 11)
+#define OUT_REG_INITIAL_LRCK_SFT 13
+#define OUT_REG_INITIAL_LRCK_MASK 0x1
+#define OUT_REG_INITIAL_LRCK_MASK_SFT (0x1 << 13)
+#define OUT_REG_NO_ALIGN_1X_EN_SFT 14
+#define OUT_REG_NO_ALIGN_1X_EN_MASK 0x1
+#define OUT_REG_NO_ALIGN_1X_EN_MASK_SFT (0x1 << 14)
+#define OUT_REG_LRCK_RESET_SFT 15
+#define OUT_REG_LRCK_RESET_MASK 0x1
+#define OUT_REG_LRCK_RESET_MASK_SFT (0x1 << 15)
+#define OUT_PINMUX_MCLK_CTRL_OE_SFT 16
+#define OUT_PINMUX_MCLK_CTRL_OE_MASK 0x1
+#define OUT_PINMUX_MCLK_CTRL_OE_MASK_SFT (0x1 << 16)
+#define OUT_REG_OUTPUT_CR_EN_SFT 18
+#define OUT_REG_OUTPUT_CR_EN_MASK 0x1
+#define OUT_REG_OUTPUT_CR_EN_MASK_SFT (0x1 << 18)
+#define OUT_REG_LRCK_WIDTH_SFT 19
+#define OUT_REG_LRCK_WIDTH_MASK 0x3ff
+#define OUT_REG_LRCK_WIDTH_MASK_SFT (0x3ff << 19)
+#define OUT_REG_LRCK_AUTO_MODE_SFT 29
+#define OUT_REG_LRCK_AUTO_MODE_MASK 0x1
+#define OUT_REG_LRCK_AUTO_MODE_MASK_SFT (0x1 << 29)
+#define OUT_REG_DIRECT_INPUT_MASTER_BCK_SFT 30
+#define OUT_REG_DIRECT_INPUT_MASTER_BCK_MASK 0x1
+#define OUT_REG_DIRECT_INPUT_MASTER_BCK_MASK_SFT (0x1 << 30)
+#define OUT_REG_16B_COMPACT_MODE_SFT 31
+#define OUT_REG_16B_COMPACT_MODE_MASK 0x1
+#define OUT_REG_16B_COMPACT_MODE_MASK_SFT (0x1 << 31)
+
+/* ETDM_OUT0_CON2 */
+/* ETDM_OUT1_CON2 */
+/* ETDM_OUT4_CON2 */
+#define OUT_REG_IN2LATCH_TIME_SFT 0
+#define OUT_REG_IN2LATCH_TIME_MASK 0x1f
+#define OUT_REG_IN2LATCH_TIME_MASK_SFT (0x1f << 0)
+#define OUT_REG_MASK_NUM_SFT 5
+#define OUT_REG_MASK_NUM_MASK 0x1f
+#define OUT_REG_MASK_NUM_MASK_SFT (0x1f << 5)
+#define OUT_REG_MASK_AUTO_SFT 10
+#define OUT_REG_MASK_AUTO_MASK 0x1
+#define OUT_REG_MASK_AUTO_MASK_SFT (0x1 << 10)
+#define OUT_REG_SDATA_SHIFT_SFT 11
+#define OUT_REG_SDATA_SHIFT_MASK 0x3
+#define OUT_REG_SDATA_SHIFT_MASK_SFT (0x3 << 11)
+#define OUT_REG_ALMOST_END_BIT_COUNT_SFT 13
+#define OUT_REG_ALMOST_END_BIT_COUNT_MASK 0x1f
+#define OUT_REG_ALMOST_END_BIT_COUNT_MASK_SFT (0x1f << 13)
+#define OUT_REG_SDATA_CON_SFT 18
+#define OUT_REG_SDATA_CON_MASK 0x3
+#define OUT_REG_SDATA_CON_MASK_SFT (0x3 << 18)
+#define OUT_REG_REDUNDANT_0_SFT 20
+#define OUT_REG_REDUNDANT_0_MASK 0x1
+#define OUT_REG_REDUNDANT_0_MASK_SFT (0x1 << 20)
+#define OUT_REG_SDATA_AUTO_OFF_SFT 21
+#define OUT_REG_SDATA_AUTO_OFF_MASK 0x1
+#define OUT_REG_SDATA_AUTO_OFF_MASK_SFT (0x1 << 21)
+#define OUT_REG_BCK_OFF_TIME_SFT 22
+#define OUT_REG_BCK_OFF_TIME_MASK 0x3
+#define OUT_REG_BCK_OFF_TIME_MASK_SFT (0x3 << 22)
+#define OUT_REG_MONITOR_SEL_SFT 24
+#define OUT_REG_MONITOR_SEL_MASK 0x3
+#define OUT_REG_MONITOR_SEL_MASK_SFT (0x3 << 24)
+#define OUT_REG_SHIFT_AUTO_SFT 26
+#define OUT_REG_SHIFT_AUTO_MASK 0x1
+#define OUT_REG_SHIFT_AUTO_MASK_SFT (0x1 << 26)
+#define OUT_REG_SDATA_DELAY_0P5T_EN_SFT 27
+#define OUT_REG_SDATA_DELAY_0P5T_EN_MASK 0x1
+#define OUT_REG_SDATA_DELAY_0P5T_EN_MASK_SFT (0x1 << 27)
+#define OUT_REG_SDATA_DELAY_BCK_INV_SFT 28
+#define OUT_REG_SDATA_DELAY_BCK_INV_MASK 0x1
+#define OUT_REG_SDATA_DELAY_BCK_INV_MASK_SFT (0x1 << 28)
+#define OUT_REG_LRCK_DELAY_0P5T_EN_SFT 29
+#define OUT_REG_LRCK_DELAY_0P5T_EN_MASK 0x1
+#define OUT_REG_LRCK_DELAY_0P5T_EN_MASK_SFT (0x1 << 29)
+#define OUT_REG_LRCK_DELAY_BCK_INV_SFT 30
+#define OUT_REG_LRCK_DELAY_BCK_INV_MASK 0x1
+#define OUT_REG_LRCK_DELAY_BCK_INV_MASK_SFT (0x1 << 30)
+#define OUT_REG_OFF_CR_EN_SFT 31
+#define OUT_REG_OFF_CR_EN_MASK 0x1
+#define OUT_REG_OFF_CR_EN_MASK_SFT (0x1 << 31)
+
+/* ETDM_OUT0_CON3 */
+/* ETDM_OUT1_CON3 */
+/* ETDM_OUT4_CON3 */
+#define OUT_REG_START_CH_PAIR0_SFT 0
+#define OUT_REG_START_CH_PAIR0_MASK 0xf
+#define OUT_REG_START_CH_PAIR0_MASK_SFT (0xf << 0)
+#define OUT_REG_START_CH_PAIR1_SFT 4
+#define OUT_REG_START_CH_PAIR1_MASK 0xf
+#define OUT_REG_START_CH_PAIR1_MASK_SFT (0xf << 4)
+#define OUT_REG_START_CH_PAIR2_SFT 8
+#define OUT_REG_START_CH_PAIR2_MASK 0xf
+#define OUT_REG_START_CH_PAIR2_MASK_SFT (0xf << 8)
+#define OUT_REG_START_CH_PAIR3_SFT 12
+#define OUT_REG_START_CH_PAIR3_MASK 0xf
+#define OUT_REG_START_CH_PAIR3_MASK_SFT (0xf << 12)
+#define OUT_REG_START_CH_PAIR4_SFT 16
+#define OUT_REG_START_CH_PAIR4_MASK 0xf
+#define OUT_REG_START_CH_PAIR4_MASK_SFT (0xf << 16)
+#define OUT_REG_START_CH_PAIR5_SFT 20
+#define OUT_REG_START_CH_PAIR5_MASK 0xf
+#define OUT_REG_START_CH_PAIR5_MASK_SFT (0xf << 20)
+#define OUT_REG_START_CH_PAIR6_SFT 24
+#define OUT_REG_START_CH_PAIR6_MASK 0xf
+#define OUT_REG_START_CH_PAIR6_MASK_SFT (0xf << 24)
+#define OUT_REG_START_CH_PAIR7_SFT 28
+#define OUT_REG_START_CH_PAIR7_MASK 0xf
+#define OUT_REG_START_CH_PAIR7_MASK_SFT (0xf << 28)
+
+/* ETDM_OUT0_CON4 */
+/* ETDM_OUT1_CON4 */
+/* ETDM_OUT4_CON4 */
+#define OUT_REG_FS_TIMING_SEL_SFT 0
+#define OUT_REG_FS_TIMING_SEL_MASK 0x1f
+#define OUT_REG_FS_TIMING_SEL_MASK_SFT (0x1f << 0)
+#define OUT_REG_CLOCK_SOURCE_SEL_SFT 6
+#define OUT_REG_CLOCK_SOURCE_SEL_MASK 0x7
+#define OUT_REG_CLOCK_SOURCE_SEL_MASK_SFT (0x7 << 6)
+#define OUT_REG_CK_EN_SEL_AUTO_SFT 10
+#define OUT_REG_CK_EN_SEL_AUTO_MASK 0x1
+#define OUT_REG_CK_EN_SEL_AUTO_MASK_SFT (0x1 << 10)
+#define OUT_REG_ASYNC_RESET_SFT 11
+#define OUT_REG_ASYNC_RESET_MASK 0x1
+#define OUT_REG_ASYNC_RESET_MASK_SFT (0x1 << 11)
+#define OUT_REG_CK_EN_SEL_MANUAL_SFT 14
+#define OUT_REG_CK_EN_SEL_MANUAL_MASK 0x3ff
+#define OUT_REG_CK_EN_SEL_MANUAL_MASK_SFT (0x3ff << 14)
+#define OUT_REG_RELATCH_EN_SEL_SFT 24
+#define OUT_REG_RELATCH_EN_SEL_MASK 0x1f
+#define OUT_REG_RELATCH_EN_SEL_MASK_SFT (0x1f << 24)
+#define OUT_REG_WAIT_LAST_SAMPLE_SFT 30
+#define OUT_REG_WAIT_LAST_SAMPLE_MASK 0x1
+#define OUT_REG_WAIT_LAST_SAMPLE_MASK_SFT (0x1 << 30)
+#define OUT_REG_ALWAYS_OPEN_1X_EN_SFT 31
+#define OUT_REG_ALWAYS_OPEN_1X_EN_MASK 0x1
+#define OUT_REG_ALWAYS_OPEN_1X_EN_MASK_SFT (0x1 << 31)
+
+/* ETDM_OUT0_CON5 */
+/* ETDM_OUT1_CON5 */
+/* ETDM_OUT4_CON5 */
+#define OUT_REG_REPACK_BITNUM_SFT 0
+#define OUT_REG_REPACK_BITNUM_MASK 0x3
+#define OUT_REG_REPACK_BITNUM_MASK_SFT (0x3 << 0)
+#define OUT_REG_REPACK_CHNUM_SFT 2
+#define OUT_REG_REPACK_CHNUM_MASK 0xf
+#define OUT_REG_REPACK_CHNUM_MASK_SFT (0xf << 2)
+#define OUT_REG_SLAVE_BCK_INV_SFT 7
+#define OUT_REG_SLAVE_BCK_INV_MASK 0x1
+#define OUT_REG_SLAVE_BCK_INV_MASK_SFT (0x1 << 7)
+#define OUT_REG_SLAVE_LRCK_INV_SFT 8
+#define OUT_REG_SLAVE_LRCK_INV_MASK 0x1
+#define OUT_REG_SLAVE_LRCK_INV_MASK_SFT (0x1 << 8)
+#define OUT_REG_MASTER_BCK_INV_SFT 9
+#define OUT_REG_MASTER_BCK_INV_MASK 0x1
+#define OUT_REG_MASTER_BCK_INV_MASK_SFT (0x1 << 9)
+#define OUT_REG_MASTER_WS_INV_SFT 10
+#define OUT_REG_MASTER_WS_INV_MASK 0x1
+#define OUT_REG_MASTER_WS_INV_MASK_SFT (0x1 << 10)
+#define OUT_REG_REPACK_24B_MSB_ALIGN_SFT 11
+#define OUT_REG_REPACK_24B_MSB_ALIGN_MASK 0x1
+#define OUT_REG_REPACK_24B_MSB_ALIGN_MASK_SFT (0x1 << 11)
+#define OUT_REG_LR_SWAP_SFT 16
+#define OUT_REG_LR_SWAP_MASK 0xffff
+#define OUT_REG_LR_SWAP_MASK_SFT (0xffff << 16)
+
+/* ETDM_OUT0_CON6 */
+/* ETDM_OUT1_CON6 */
+/* ETDM_OUT4_CON6 */
+#define OUT_LCH_DATA_REG_SFT 0
+#define OUT_LCH_DATA_REG_MASK 0xffffffff
+#define OUT_LCH_DATA_REG_MASK_SFT (0xffffffff << 0)
+
+/* ETDM_OUT0_CON7 */
+/* ETDM_OUT1_CON7 */
+/* ETDM_OUT4_CON7 */
+#define OUT_RCH_DATA_REG_SFT 0
+#define OUT_RCH_DATA_REG_MASK 0xffffffff
+#define OUT_RCH_DATA_REG_MASK_SFT (0xffffffff << 0)
+
+/* ETDM_OUT0_CON8 */
+/* ETDM_OUT1_CON8 */
+/* ETDM_OUT4_CON8 */
+#define OUT_REG_START_CH_PAIR8_SFT 0
+#define OUT_REG_START_CH_PAIR8_MASK 0xf
+#define OUT_REG_START_CH_PAIR8_MASK_SFT (0xf << 0)
+#define OUT_REG_START_CH_PAIR9_SFT 4
+#define OUT_REG_START_CH_PAIR9_MASK 0xf
+#define OUT_REG_START_CH_PAIR9_MASK_SFT (0xf << 4)
+#define OUT_REG_START_CH_PAIR10_SFT 8
+#define OUT_REG_START_CH_PAIR10_MASK 0xf
+#define OUT_REG_START_CH_PAIR10_MASK_SFT (0xf << 8)
+#define OUT_REG_START_CH_PAIR11_SFT 12
+#define OUT_REG_START_CH_PAIR11_MASK 0xf
+#define OUT_REG_START_CH_PAIR11_MASK_SFT (0xf << 12)
+#define OUT_REG_START_CH_PAIR12_SFT 16
+#define OUT_REG_START_CH_PAIR12_MASK 0xf
+#define OUT_REG_START_CH_PAIR12_MASK_SFT (0xf << 16)
+#define OUT_REG_START_CH_PAIR13_SFT 20
+#define OUT_REG_START_CH_PAIR13_MASK 0xf
+#define OUT_REG_START_CH_PAIR13_MASK_SFT (0xf << 20)
+#define OUT_REG_START_CH_PAIR14_SFT 24
+#define OUT_REG_START_CH_PAIR14_MASK 0xf
+#define OUT_REG_START_CH_PAIR14_MASK_SFT (0xf << 24)
+#define OUT_REG_START_CH_PAIR15_SFT 28
+#define OUT_REG_START_CH_PAIR15_MASK 0xf
+#define OUT_REG_START_CH_PAIR15_MASK_SFT (0xf << 28)
+
+/* ETDM_OUT0_CON9 */
+/* ETDM_OUT1_CON9 */
+/* ETDM_OUT4_CON9 */
+#define OUT_REG_AFIFO_THRESHOLD_SFT 29
+#define OUT_REG_AFIFO_THRESHOLD_MASK 0x3
+#define OUT_REG_AFIFO_THRESHOLD_MASK_SFT (0x3 << 29)
+#define OUT_REG_AFIFO_SW_RESET_SFT 15
+#define OUT_REG_AFIFO_SW_RESET_MASK 0x1
+#define OUT_REG_AFIFO_SW_RESET_MASK_SFT (0x1 << 15)
+#define OUT_REG_AFIFO_RESET_SEL_SFT 14
+#define OUT_REG_AFIFO_RESET_SEL_MASK 0x1
+#define OUT_REG_AFIFO_RESET_SEL_MASK_SFT (0x1 << 14)
+#define OUT_REG_AFIFO_AUTO_RESET_DIS_SFT 9
+#define OUT_REG_AFIFO_AUTO_RESET_DIS_MASK 0x1
+#define OUT_REG_AFIFO_AUTO_RESET_DIS_MASK_SFT (0x1 << 9)
+#define OUT_REG_ETDM_USE_AFIFO_SFT 8
+#define OUT_REG_ETDM_USE_AFIFO_MASK 0x1
+#define OUT_REG_ETDM_USE_AFIFO_MASK_SFT (0x1 << 8)
+#define OUT_REG_AFIFO_CLOCK_DOMAIN_SEL_SFT 5
+#define OUT_REG_AFIFO_CLOCK_DOMAIN_SEL_MASK 0x7
+#define OUT_REG_AFIFO_CLOCK_DOMAIN_SEL_MASK_SFT (0x7 << 5)
+#define OUT_REG_AFIFO_MODE_SFT 0
+#define OUT_REG_AFIFO_MODE_MASK 0x1f
+#define OUT_REG_AFIFO_MODE_MASK_SFT (0x1f << 0)
+
+/* ETDM_OUT0_MON */
+/* ETDM_OUT1_MON */
+/* ETDM_OUT4_MON */
+#define LRCK_INV_SFT 30
+#define LRCK_INV_MASK 0x1
+#define LRCK_INV_MASK_SFT (0x1 << 30)
+#define EN_SYNC_OUT_SFT 29
+#define EN_SYNC_OUT_MASK 0x1
+#define EN_SYNC_OUT_MASK_SFT (0x1 << 29)
+#define HOPPING_EN_SYNC_OUT_PRE_SFT 28
+#define HOPPING_EN_SYNC_OUT_PRE_MASK 0x1
+#define HOPPING_EN_SYNC_OUT_PRE_MASK_SFT (0x1 << 28)
+#define ETDM_2X_CK_EN_SFT 25
+#define ETDM_2X_CK_EN_MASK 0x1
+#define ETDM_2X_CK_EN_MASK_SFT (0x1 << 25)
+#define ETDM_1X_CK_EN_SFT 24
+#define ETDM_1X_CK_EN_MASK 0x1
+#define ETDM_1X_CK_EN_MASK_SFT (0x1 << 24)
+#define SDATA0_SFT 23
+#define SDATA0_MASK 0x1
+#define SDATA0_MASK_SFT (0x1 << 23)
+#define CURRENT_STATUS_SFT 21
+#define CURRENT_STATUS_MASK 0x3
+#define CURRENT_STATUS_MASK_SFT (0x3 << 21)
+#define BIT_POINT_SFT 16
+#define BIT_POINT_MASK 0x1f
+#define BIT_POINT_MASK_SFT (0x1f << 16)
+#define BIT_CH_COUNT_SFT 10
+#define BIT_CH_COUNT_MASK 0x3f
+#define BIT_CH_COUNT_MASK_SFT (0x3f << 10)
+#define BIT_COUNT_SFT 5
+#define BIT_COUNT_MASK 0x1f
+#define BIT_COUNT_MASK_SFT (0x1f << 5)
+#define CH_COUNT_SFT 0
+#define CH_COUNT_MASK 0x1f
+#define CH_COUNT_MASK_SFT (0x1f << 0)
+
+/* ETDM_0_3_COWORK_CON0 */
+#define ETDM_OUT0_DATA_SEL_SFT 0
+#define ETDM_OUT0_DATA_SEL_MASK 0xf
+#define ETDM_OUT0_DATA_SEL_MASK_SFT (0xf << 0)
+#define ETDM_OUT0_SYNC_SEL_SFT 4
+#define ETDM_OUT0_SYNC_SEL_MASK 0xf
+#define ETDM_OUT0_SYNC_SEL_MASK_SFT (0xf << 4)
+#define ETDM_OUT0_SLAVE_SEL_SFT 8
+#define ETDM_OUT0_SLAVE_SEL_MASK 0xf
+#define ETDM_OUT0_SLAVE_SEL_MASK_SFT (0xf << 8)
+#define ETDM_OUT1_DATA_SEL_SFT 12
+#define ETDM_OUT1_DATA_SEL_MASK 0xf
+#define ETDM_OUT1_DATA_SEL_MASK_SFT (0xf << 12)
+#define ETDM_OUT1_SYNC_SEL_SFT 16
+#define ETDM_OUT1_SYNC_SEL_MASK 0xf
+#define ETDM_OUT1_SYNC_SEL_MASK_SFT (0xf << 16)
+#define ETDM_OUT1_SLAVE_SEL_SFT 20
+#define ETDM_OUT1_SLAVE_SEL_MASK 0xf
+#define ETDM_OUT1_SLAVE_SEL_MASK_SFT (0xf << 20)
+#define ETDM_IN0_SLAVE_SEL_SFT 24
+#define ETDM_IN0_SLAVE_SEL_MASK 0xf
+#define ETDM_IN0_SLAVE_SEL_MASK_SFT (0xf << 24)
+#define ETDM_IN0_SYNC_SEL_SFT 28
+#define ETDM_IN0_SYNC_SEL_MASK 0xf
+#define ETDM_IN0_SYNC_SEL_MASK_SFT (0xf << 28)
+
+/* ETDM_0_3_COWORK_CON1 */
+#define ETDM_IN0_SDATA0_SEL_SFT 0
+#define ETDM_IN0_SDATA0_SEL_MASK 0xf
+#define ETDM_IN0_SDATA0_SEL_MASK_SFT (0xf << 0)
+#define ETDM_IN0_SDATA1_15_SEL_SFT 4
+#define ETDM_IN0_SDATA1_15_SEL_MASK 0xf
+#define ETDM_IN0_SDATA1_15_SEL_MASK_SFT (0xf << 4)
+#define ETDM_IN1_SLAVE_SEL_SFT 8
+#define ETDM_IN1_SLAVE_SEL_MASK 0xf
+#define ETDM_IN1_SLAVE_SEL_MASK_SFT (0xf << 8)
+#define ETDM_IN1_SYNC_SEL_SFT 12
+#define ETDM_IN1_SYNC_SEL_MASK 0xf
+#define ETDM_IN1_SYNC_SEL_MASK_SFT (0xf << 12)
+#define ETDM_IN1_SDATA0_SEL_SFT 16
+#define ETDM_IN1_SDATA0_SEL_MASK 0xf
+#define ETDM_IN1_SDATA0_SEL_MASK_SFT (0xf << 16)
+#define ETDM_IN1_SDATA1_15_SEL_SFT 20
+#define ETDM_IN1_SDATA1_15_SEL_MASK 0xf
+#define ETDM_IN1_SDATA1_15_SEL_MASK_SFT (0xf << 20)
+
+/* ETDM_4_7_COWORK_CON0 */
+#define ETDM_OUT4_DATA_SEL_SFT 0
+#define ETDM_OUT4_DATA_SEL_MASK 0xf
+#define ETDM_OUT4_DATA_SEL_MASK_SFT (0xf << 0)
+#define ETDM_OUT4_SYNC_SEL_SFT 4
+#define ETDM_OUT4_SYNC_SEL_MASK 0xf
+#define ETDM_OUT4_SYNC_SEL_MASK_SFT (0xf << 4)
+#define ETDM_OUT4_SLAVE_SEL_SFT 8
+#define ETDM_OUT4_SLAVE_SEL_MASK 0xf
+#define ETDM_OUT4_SLAVE_SEL_MASK_SFT (0xf << 8)
+
+/* AFE_DPTX_CON */
+#define DPTX_CHANNEL_ENABLE_SFT 8
+#define DPTX_CHANNEL_ENABLE_MASK 0xff
+#define DPTX_CHANNEL_ENABLE_MASK_SFT (0xff << 8)
+#define DPTX_REGISTER_MONITOR_SELECT_SFT 3
+#define DPTX_REGISTER_MONITOR_SELECT_MASK 0xf
+#define DPTX_REGISTER_MONITOR_SELECT_MASK_SFT (0xf << 3)
+#define DPTX_16BIT_SFT 2
+#define DPTX_16BIT_MASK 0x1
+#define DPTX_16BIT_MASK_SFT (0x1 << 2)
+#define DPTX_CHANNEL_NUMBER_SFT 1
+#define DPTX_CHANNEL_NUMBER_MASK 0x1
+#define DPTX_CHANNEL_NUMBER_MASK_SFT (0x1 << 1)
+#define DPTX_ON_SFT 0
+#define DPTX_ON_MASK 0x1
+#define DPTX_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DPTX_MON */
+#define AFE_DPTX_MON0_SFT 0
+#define AFE_DPTX_MON0_MASK 0xffffffff
+#define AFE_DPTX_MON0_MASK_SFT (0xffffffff << 0)
+
+/* AFE_TDM_CON1 */
+#define TDM_EN_SFT 0
+#define TDM_EN_MASK 0x1
+#define TDM_EN_MASK_SFT (0x1 << 0)
+#define BCK_INVERSE_SFT 1
+#define BCK_INVERSE_MASK 0x1
+#define BCK_INVERSE_MASK_SFT (0x1 << 1)
+#define LRCK_INVERSE_SFT 2
+#define LRCK_INVERSE_MASK 0x1
+#define LRCK_INVERSE_MASK_SFT (0x1 << 2)
+#define DELAY_DATA_SFT 3
+#define DELAY_DATA_MASK 0x1
+#define DELAY_DATA_MASK_SFT (0x1 << 3)
+#define LEFT_ALIGN_SFT 4
+#define LEFT_ALIGN_MASK 0x1
+#define LEFT_ALIGN_MASK_SFT (0x1 << 4)
+#define TDM_LRCK_D0P5T_SFT 5
+#define TDM_LRCK_D0P5T_MASK 0x1
+#define TDM_LRCK_D0P5T_MASK_SFT (0x1 << 5)
+#define TDM_SDATA_D0P5T_SFT 6
+#define TDM_SDATA_D0P5T_MASK 0x1
+#define TDM_SDATA_D0P5T_MASK_SFT (0x1 << 6)
+#define WLEN_SFT 8
+#define WLEN_MASK 0x3
+#define WLEN_MASK_SFT (0x3 << 8)
+#define CHANNEL_NUM_SFT 10
+#define CHANNEL_NUM_MASK 0x3
+#define CHANNEL_NUM_MASK_SFT (0x3 << 10)
+#define CHANNEL_BCK_CYCLES_SFT 12
+#define CHANNEL_BCK_CYCLES_MASK 0x3
+#define CHANNEL_BCK_CYCLES_MASK_SFT (0x3 << 12)
+#define HDMI_CLK_INV_SEL_SFT 15
+#define HDMI_CLK_INV_SEL_MASK 0x1
+#define HDMI_CLK_INV_SEL_MASK_SFT (0x1 << 15)
+#define DAC_BIT_NUM_SFT 16
+#define DAC_BIT_NUM_MASK 0x1f
+#define DAC_BIT_NUM_MASK_SFT (0x1f << 16)
+#define LRCK_TDM_WIDTH_SFT 24
+#define LRCK_TDM_WIDTH_MASK 0xff
+#define LRCK_TDM_WIDTH_MASK_SFT (0xff << 24)
+
+/* AFE_TDM_CON2 */
+#define ST_CH_PAIR_SOUT0_SFT 0
+#define ST_CH_PAIR_SOUT0_MASK 0x7
+#define ST_CH_PAIR_SOUT0_MASK_SFT (0x7 << 0)
+#define ST_CH_PAIR_SOUT1_SFT 4
+#define ST_CH_PAIR_SOUT1_MASK 0x7
+#define ST_CH_PAIR_SOUT1_MASK_SFT (0x7 << 4)
+#define ST_CH_PAIR_SOUT2_SFT 8
+#define ST_CH_PAIR_SOUT2_MASK 0x7
+#define ST_CH_PAIR_SOUT2_MASK_SFT (0x7 << 8)
+#define ST_CH_PAIR_SOUT3_SFT 12
+#define ST_CH_PAIR_SOUT3_MASK 0x7
+#define ST_CH_PAIR_SOUT3_MASK_SFT (0x7 << 12)
+#define TDM_FIX_VALUE_SEL_SFT 16
+#define TDM_FIX_VALUE_SEL_MASK 0x1
+#define TDM_FIX_VALUE_SEL_MASK_SFT (0x1 << 16)
+#define TDM_I2S_LOOPBACK_SFT 20
+#define TDM_I2S_LOOPBACK_MASK 0x1
+#define TDM_I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define TDM_I2S_LOOPBACK_CH_SFT 21
+#define TDM_I2S_LOOPBACK_CH_MASK 0x3
+#define TDM_I2S_LOOPBACK_CH_MASK_SFT (0x3 << 21)
+#define TDM_USE_SINEGEN_INPUT_SFT 23
+#define TDM_USE_SINEGEN_INPUT_MASK 0x1
+#define TDM_USE_SINEGEN_INPUT_MASK_SFT (0x1 << 23)
+#define TDM_FIX_VALUE_SFT 24
+#define TDM_FIX_VALUE_MASK 0xff
+#define TDM_FIX_VALUE_MASK_SFT (0xff << 24)
+
+/* AFE_TDM_CON3 */
+#define TDM_OUT_SEL_DOMAIN_SFT 29
+#define TDM_OUT_SEL_DOMAIN_MASK 0x7
+#define TDM_OUT_SEL_DOMAIN_MASK_SFT (0x7 << 29)
+#define TDM_OUT_SEL_FS_SFT 24
+#define TDM_OUT_SEL_FS_MASK 0x1f
+#define TDM_OUT_SEL_FS_MASK_SFT (0x1f << 24)
+#define TDM_OUT_MON_SEL_SFT 3
+#define TDM_OUT_MON_SEL_MASK 0x1
+#define TDM_OUT_MON_SEL_MASK_SFT (0x1 << 3)
+#define RG_TDM_OUT_ASYNC_FIFO_SOFT_RST_EN_SFT 2
+#define RG_TDM_OUT_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1
+#define RG_TDM_OUT_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 2)
+#define RG_TDM_OUT_ASYNC_FIFO_SOFT_RST_SFT 1
+#define RG_TDM_OUT_ASYNC_FIFO_SOFT_RST_MASK 0x1
+#define RG_TDM_OUT_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 1)
+#define TDM_UPDATE_EN_SEL_SFT 0
+#define TDM_UPDATE_EN_SEL_MASK 0x1
+#define TDM_UPDATE_EN_SEL_MASK_SFT (0x1 << 0)
+
+/* AFE_TDM_OUT_MON */
+#define AFE_TDM_OUT_MON_SFT 0
+#define AFE_TDM_OUT_MON_MASK 0xffffffff
+#define AFE_TDM_OUT_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_HDMI_CONN0 */
+#define HDMI_O_7_SFT 21
+#define HDMI_O_7_MASK 0x7
+#define HDMI_O_7_MASK_SFT (0x7 << 21)
+#define HDMI_O_6_SFT 18
+#define HDMI_O_6_MASK 0x7
+#define HDMI_O_6_MASK_SFT (0x7 << 18)
+#define HDMI_O_5_SFT 15
+#define HDMI_O_5_MASK 0x7
+#define HDMI_O_5_MASK_SFT (0x7 << 15)
+#define HDMI_O_4_SFT 12
+#define HDMI_O_4_MASK 0x7
+#define HDMI_O_4_MASK_SFT (0x7 << 12)
+#define HDMI_O_3_SFT 9
+#define HDMI_O_3_MASK 0x7
+#define HDMI_O_3_MASK_SFT (0x7 << 9)
+#define HDMI_O_2_SFT 6
+#define HDMI_O_2_MASK 0x7
+#define HDMI_O_2_MASK_SFT (0x7 << 6)
+#define HDMI_O_1_SFT 3
+#define HDMI_O_1_MASK 0x7
+#define HDMI_O_1_MASK_SFT (0x7 << 3)
+#define HDMI_O_0_SFT 0
+#define HDMI_O_0_MASK 0x7
+#define HDMI_O_0_MASK_SFT (0x7 << 0)
+
+/* AFE_TDM_TOP_IP_VERSION */
+#define AFE_TDM_TOP_IP_VERSION_SFT 0
+#define AFE_TDM_TOP_IP_VERSION_MASK 0xffffffff
+#define AFE_TDM_TOP_IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+/* AFE_HDMI_OUT_BASE_MSB */
+#define AFE_HDMI_OUT_BASE_MSB_SFT 0
+#define AFE_HDMI_OUT_BASE_MSB_MASK 0x1ff
+#define AFE_HDMI_OUT_BASE_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_HDMI_OUT_BASE */
+#define AFE_HDMI_OUT_BASE_SFT 4
+#define AFE_HDMI_OUT_BASE_MASK 0xfffffff
+#define AFE_HDMI_OUT_BASE_MASK_SFT (0xfffffff << 4)
+
+/* AFE_HDMI_OUT_CUR_MSB */
+#define AFE_HDMI_OUT_CUR_MSB_SFT 0
+#define AFE_HDMI_OUT_CUR_MSB_MASK 0x1ff
+#define AFE_HDMI_OUT_CUR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_HDMI_OUT_CUR */
+#define AFE_HDMI_OUT_CUR_SFT 0
+#define AFE_HDMI_OUT_CUR_MASK 0xffffffff
+#define AFE_HDMI_OUT_CUR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_HDMI_OUT_END_MSB */
+#define AFE_HDMI_OUT_END_MSB_SFT 0
+#define AFE_HDMI_OUT_END_MSB_MASK 0x1ff
+#define AFE_HDMI_OUT_END_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_HDMI_OUT_END */
+#define AFE_HDMI_OUT_END_SFT 4
+#define AFE_HDMI_OUT_END_MASK 0xfffffff
+#define AFE_HDMI_OUT_END_MASK_SFT (0xfffffff << 4)
+#define AFE_HDMI_OUT_END_LSB_SFT 0
+#define AFE_HDMI_OUT_END_LSB_MASK 0xf
+#define AFE_HDMI_OUT_END_LSB_MASK_SFT (0xf << 0)
+
+/* AFE_HDMI_OUT_CON0 */
+#define HDMI_OUT_ON_SFT 28
+#define HDMI_OUT_ON_MASK 0x1
+#define HDMI_OUT_ON_MASK_SFT (0x1 << 28)
+#define HDMI_CH_NUM_SFT 24
+#define HDMI_CH_NUM_MASK 0xf
+#define HDMI_CH_NUM_MASK_SFT (0xf << 24)
+#define HDMI_OUT_ONE_HEART_SEL_SFT 22
+#define HDMI_OUT_ONE_HEART_SEL_MASK 0x3
+#define HDMI_OUT_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define HDMI_OUT_MINLEN_SFT 20
+#define HDMI_OUT_MINLEN_MASK 0x3
+#define HDMI_OUT_MINLEN_MASK_SFT (0x3 << 20)
+#define HDMI_OUT_MAXLEN_SFT 16
+#define HDMI_OUT_MAXLEN_MASK 0x3
+#define HDMI_OUT_MAXLEN_MASK_SFT (0x3 << 16)
+#define HDMI_OUT_SW_CLEAR_BUF_EMPTY_SFT 15
+#define HDMI_OUT_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define HDMI_OUT_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define HDMI_OUT_PBUF_SIZE_SFT 12
+#define HDMI_OUT_PBUF_SIZE_MASK 0x3
+#define HDMI_OUT_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define HDMI_OUT_SW_CLEAR_HDMI_BUF_EMPTY_SFT 7
+#define HDMI_OUT_SW_CLEAR_HDMI_BUF_EMPTY_MASK 0x1
+#define HDMI_OUT_SW_CLEAR_HDMI_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define HDMI_OUT_NORMAL_MODE_SFT 5
+#define HDMI_OUT_NORMAL_MODE_MASK 0x1
+#define HDMI_OUT_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define HDMI_OUT_HALIGN_SFT 4
+#define HDMI_OUT_HALIGN_MASK 0x1
+#define HDMI_OUT_HALIGN_MASK_SFT (0x1 << 4)
+#define HDMI_OUT_HD_MODE_SFT 0
+#define HDMI_OUT_HD_MODE_MASK 0x3
+#define HDMI_OUT_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_CBIP_CFG0 */
+#define CBIP_TOP_SLV_MUX_WAY_EN_SFT 16
+#define CBIP_TOP_SLV_MUX_WAY_EN_MASK 0xffff
+#define CBIP_TOP_SLV_MUX_WAY_EN_MASK_SFT (0xffff << 16)
+#define RESERVED_04_SFT 15
+#define RESERVED_04_MASK 0x1
+#define RESERVED_04_MASK_SFT (0x1 << 15)
+#define CBIP_ASYNC_MST_RG_FIFO_THRE_SFT 13
+#define CBIP_ASYNC_MST_RG_FIFO_THRE_MASK 0x3
+#define CBIP_ASYNC_MST_RG_FIFO_THRE_MASK_SFT (0x3 << 13)
+#define CBIP_ASYNC_MST_POSTWRITE_DIS_SFT 12
+#define CBIP_ASYNC_MST_POSTWRITE_DIS_MASK 0x1
+#define CBIP_ASYNC_MST_POSTWRITE_DIS_MASK_SFT (0x1 << 12)
+#define RESERVED_03_SFT 11
+#define RESERVED_03_MASK 0x1
+#define RESERVED_03_MASK_SFT (0x1 << 11)
+#define CBIP_ASYNC_SLV_RG_FIFO_THRE_SFT 9
+#define CBIP_ASYNC_SLV_RG_FIFO_THRE_MASK 0x3
+#define CBIP_ASYNC_SLV_RG_FIFO_THRE_MASK_SFT (0x3 << 9)
+#define CBIP_ASYNC_SLV_POSTWRITE_DIS_SFT 8
+#define CBIP_ASYNC_SLV_POSTWRITE_DIS_MASK 0x1
+#define CBIP_ASYNC_SLV_POSTWRITE_DIS_MASK_SFT (0x1 << 8)
+#define AUDIOSYS_BUSY_SFT 7
+#define AUDIOSYS_BUSY_MASK 0x1
+#define AUDIOSYS_BUSY_MASK_SFT (0x1 << 7)
+#define CBIP_SLV_DECODER_ERR_FLAG_EN_SFT 6
+#define CBIP_SLV_DECODER_ERR_FLAG_EN_MASK 0x1
+#define CBIP_SLV_DECODER_ERR_FLAG_EN_MASK_SFT (0x1 << 6)
+#define CBIP_SLV_DECODER_SLAVE_WAY_EN_SFT 5
+#define CBIP_SLV_DECODER_SLAVE_WAY_EN_MASK 0x1
+#define CBIP_SLV_DECODER_SLAVE_WAY_EN_MASK_SFT (0x1 << 5)
+#define APB_R2T_SFT 3
+#define APB_R2T_MASK 0x1
+#define APB_R2T_MASK_SFT (0x1 << 3)
+#define APB_W2T_SFT 2
+#define APB_W2T_MASK 0x1
+#define APB_W2T_MASK_SFT (0x1 << 2)
+#define AHB_IDLE_EN_INT_SFT 1
+#define AHB_IDLE_EN_INT_MASK 0x1
+#define AHB_IDLE_EN_INT_MASK_SFT (0x1 << 1)
+#define AHB_IDLE_EN_EXT_SFT 0
+#define AHB_IDLE_EN_EXT_MASK 0x1
+#define AHB_IDLE_EN_EXT_MASK_SFT (0x1 << 0)
+
+/* AFE_CBIP_SLV_DECODER_MON0 */
+#define CBIP_SLV_DECODER_ERR_DOMAIN_SFT 4
+#define CBIP_SLV_DECODER_ERR_DOMAIN_MASK 0x1
+#define CBIP_SLV_DECODER_ERR_DOMAIN_MASK_SFT (0x1 << 4)
+#define CBIP_SLV_DECODER_ERR_ID_SFT 3
+#define CBIP_SLV_DECODER_ERR_ID_MASK 0x1
+#define CBIP_SLV_DECODER_ERR_ID_MASK_SFT (0x1 << 3)
+#define CBIP_SLV_DECODER_ERR_RW_SFT 2
+#define CBIP_SLV_DECODER_ERR_RW_MASK 0x1
+#define CBIP_SLV_DECODER_ERR_RW_MASK_SFT (0x1 << 2)
+#define CBIP_SLV_DECODER_ERR_DECERR_SFT 1
+#define CBIP_SLV_DECODER_ERR_DECERR_MASK 0x1
+#define CBIP_SLV_DECODER_ERR_DECERR_MASK_SFT (0x1 << 1)
+#define CBIP_SLV_DECODER_CTRL_UPDATE_STATUS_SFT 0
+#define CBIP_SLV_DECODER_CTRL_UPDATE_STATUS_MASK 0x1
+#define CBIP_SLV_DECODER_CTRL_UPDATE_STATUS_MASK_SFT (0x1 << 0)
+
+/* AFE_CBIP_SLV_DECODER_MON1 */
+#define CBIP_SLV_DECODER_ERR_ADDR_SFT 0
+#define CBIP_SLV_DECODER_ERR_ADDR_MASK 0xffffffff
+#define CBIP_SLV_DECODER_ERR_ADDR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CBIP_SLV_MUX_MON_CFG */
+#define CBIP_SLV_MUX_ERR_FLAG_EN_SFT 3
+#define CBIP_SLV_MUX_ERR_FLAG_EN_MASK 0x1
+#define CBIP_SLV_MUX_ERR_FLAG_EN_MASK_SFT (0x1 << 3)
+#define CBIP_SLV_MUX_REG_SLAVE_WAY_EN_SFT 2
+#define CBIP_SLV_MUX_REG_SLAVE_WAY_EN_MASK 0x1
+#define CBIP_SLV_MUX_REG_SLAVE_WAY_EN_MASK_SFT (0x1 << 2)
+#define CBIP_SLV_MUX_REG_LAYER_WAY_EN_SFT 0
+#define CBIP_SLV_MUX_REG_LAYER_WAY_EN_MASK 0x3
+#define CBIP_SLV_MUX_REG_LAYER_WAY_EN_MASK_SFT (0x3 << 0)
+
+/* AFE_CBIP_SLV_MUX_MON0 */
+#define CBIP_SLV_MUX_ERR_DOMAIN_SFT 8
+#define CBIP_SLV_MUX_ERR_DOMAIN_MASK 0x1
+#define CBIP_SLV_MUX_ERR_DOMAIN_MASK_SFT (0x1 << 8)
+#define CBIP_SLV_MUX_ERR_ID_SFT 7
+#define CBIP_SLV_MUX_ERR_ID_MASK 0x1
+#define CBIP_SLV_MUX_ERR_ID_MASK_SFT (0x1 << 7)
+#define CBIP_SLV_MUX_ERR_RD_SFT 6
+#define CBIP_SLV_MUX_ERR_RD_MASK 0x1
+#define CBIP_SLV_MUX_ERR_RD_MASK_SFT (0x1 << 6)
+#define CBIP_SLV_MUX_ERR_WR_SFT 5
+#define CBIP_SLV_MUX_ERR_WR_MASK 0x1
+#define CBIP_SLV_MUX_ERR_WR_MASK_SFT (0x1 << 5)
+#define CBIP_SLV_MUX_ERR_EN_SLV_SFT 4
+#define CBIP_SLV_MUX_ERR_EN_SLV_MASK 0x1
+#define CBIP_SLV_MUX_ERR_EN_SLV_MASK_SFT (0x1 << 4)
+#define CBIP_SLV_MUX_ERR_EN_MST_SFT 2
+#define CBIP_SLV_MUX_ERR_EN_MST_MASK 0x3
+#define CBIP_SLV_MUX_ERR_EN_MST_MASK_SFT (0x3 << 2)
+#define CBIP_SLV_MUX_CTRL_UPDATE_STATUS_SFT 0
+#define CBIP_SLV_MUX_CTRL_UPDATE_STATUS_MASK 0x3
+#define CBIP_SLV_MUX_CTRL_UPDATE_STATUS_MASK_SFT (0x3 << 0)
+
+/* AFE_CBIP_SLV_MUX_MON1 */
+#define CBIP_SLV_MUX_ERR_ADDR_SFT 0
+#define CBIP_SLV_MUX_ERR_ADDR_MASK 0xffffffff
+#define CBIP_SLV_MUX_ERR_ADDR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_MEMIF_CON0 */
+#define CPU_COMPACT_MODE_SFT 2
+#define CPU_COMPACT_MODE_MASK 0x1
+#define CPU_COMPACT_MODE_MASK_SFT (0x1 << 2)
+#define CPU_HD_ALIGN_SFT 1
+#define CPU_HD_ALIGN_MASK 0x1
+#define CPU_HD_ALIGN_MASK_SFT (0x1 << 1)
+#define SYSRAM_SIGN_SFT 0
+#define SYSRAM_SIGN_MASK 0x1
+#define SYSRAM_SIGN_MASK_SFT (0x1 << 0)
+
+/* AFE_MEMIF_ONE_HEART */
+#define DL_ONE_HEART_ON_2_SFT 2
+#define DL_ONE_HEART_ON_2_MASK 0x1
+#define DL_ONE_HEART_ON_2_MASK_SFT (0x1 << 2)
+#define DL_ONE_HEART_ON_1_SFT 1
+#define DL_ONE_HEART_ON_1_MASK 0x1
+#define DL_ONE_HEART_ON_1_MASK_SFT (0x1 << 1)
+#define DL_ONE_HEART_ON_0_SFT 0
+#define DL_ONE_HEART_ON_0_MASK 0x1
+#define DL_ONE_HEART_ON_0_MASK_SFT (0x1 << 0)
+
+/* AFE_DL0_BASE_MSB */
+#define DL0_BASE_ADDR_MSB_SFT 0
+#define DL0_BASE_ADDR_MSB_MASK 0x1ff
+#define DL0_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL0_BASE */
+#define DL0_BASE_ADDR_SFT 4
+#define DL0_BASE_ADDR_MASK 0xfffffff
+#define DL0_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL0_CUR_MSB */
+#define DL0_CUR_PTR_MSB_SFT 0
+#define DL0_CUR_PTR_MSB_MASK 0x1ff
+#define DL0_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL0_CUR */
+#define DL0_CUR_PTR_SFT 0
+#define DL0_CUR_PTR_MASK 0xffffffff
+#define DL0_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL0_END_MSB */
+#define DL0_END_ADDR_MSB_SFT 0
+#define DL0_END_ADDR_MSB_MASK 0x1ff
+#define DL0_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL0_END */
+#define DL0_END_ADDR_SFT 4
+#define DL0_END_ADDR_MASK 0xfffffff
+#define DL0_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL0_RCH_MON */
+#define DL0_RCH_DATA_SFT 0
+#define DL0_RCH_DATA_MASK 0xffffffff
+#define DL0_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL0_LCH_MON */
+#define DL0_LCH_DATA_SFT 0
+#define DL0_LCH_DATA_MASK 0xffffffff
+#define DL0_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL0_CON0 */
+#define DL0_ON_SFT 28
+#define DL0_ON_MASK 0x1
+#define DL0_ON_MASK_SFT (0x1 << 28)
+#define DL0_ONE_HEART_SEL_SFT 22
+#define DL0_ONE_HEART_SEL_MASK 0x3
+#define DL0_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL0_MINLEN_SFT 20
+#define DL0_MINLEN_MASK 0x3
+#define DL0_MINLEN_MASK_SFT (0x3 << 20)
+#define DL0_MAXLEN_SFT 16
+#define DL0_MAXLEN_MASK 0x3
+#define DL0_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL0_SEL_DOMAIN_SFT 13
+#define DL0_SEL_DOMAIN_MASK 0x7
+#define DL0_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL0_SEL_FS_SFT 8
+#define DL0_SEL_FS_MASK 0x1f
+#define DL0_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL0_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL0_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL0_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL0_PBUF_SIZE_SFT 5
+#define DL0_PBUF_SIZE_MASK 0x3
+#define DL0_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL0_MONO_SFT 4
+#define DL0_MONO_MASK 0x1
+#define DL0_MONO_MASK_SFT (0x1 << 4)
+#define DL0_NORMAL_MODE_SFT 3
+#define DL0_NORMAL_MODE_MASK 0x1
+#define DL0_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL0_HALIGN_SFT 2
+#define DL0_HALIGN_MASK 0x1
+#define DL0_HALIGN_MASK_SFT (0x1 << 2)
+#define DL0_HD_MODE_SFT 0
+#define DL0_HD_MODE_MASK 0x3
+#define DL0_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL0_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL1_BASE_MSB */
+#define DL1_BASE_ADDR_MSB_SFT 0
+#define DL1_BASE_ADDR_MSB_MASK 0x1ff
+#define DL1_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL1_BASE */
+#define DL1_BASE_ADDR_SFT 4
+#define DL1_BASE_ADDR_MASK 0xfffffff
+#define DL1_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL1_CUR_MSB */
+#define DL1_CUR_PTR_MSB_SFT 0
+#define DL1_CUR_PTR_MSB_MASK 0x1ff
+#define DL1_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL1_CUR */
+#define DL1_CUR_PTR_SFT 0
+#define DL1_CUR_PTR_MASK 0xffffffff
+#define DL1_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL1_END_MSB */
+#define DL1_END_ADDR_MSB_SFT 0
+#define DL1_END_ADDR_MSB_MASK 0x1ff
+#define DL1_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL1_END */
+#define DL1_END_ADDR_SFT 4
+#define DL1_END_ADDR_MASK 0xfffffff
+#define DL1_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL1_RCH_MON */
+#define DL1_RCH_DATA_SFT 0
+#define DL1_RCH_DATA_MASK 0xffffffff
+#define DL1_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL1_LCH_MON */
+#define DL1_LCH_DATA_SFT 0
+#define DL1_LCH_DATA_MASK 0xffffffff
+#define DL1_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL1_CON0 */
+#define DL1_ON_SFT 28
+#define DL1_ON_MASK 0x1
+#define DL1_ON_MASK_SFT (0x1 << 28)
+#define DL1_ONE_HEART_SEL_SFT 22
+#define DL1_ONE_HEART_SEL_MASK 0x3
+#define DL1_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL1_MINLEN_SFT 20
+#define DL1_MINLEN_MASK 0x3
+#define DL1_MINLEN_MASK_SFT (0x3 << 20)
+#define DL1_MAXLEN_SFT 16
+#define DL1_MAXLEN_MASK 0x3
+#define DL1_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL1_SEL_DOMAIN_SFT 13
+#define DL1_SEL_DOMAIN_MASK 0x7
+#define DL1_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL1_SEL_FS_SFT 8
+#define DL1_SEL_FS_MASK 0x1f
+#define DL1_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL1_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL1_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL1_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL1_PBUF_SIZE_SFT 5
+#define DL1_PBUF_SIZE_MASK 0x3
+#define DL1_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL1_MONO_SFT 4
+#define DL1_MONO_MASK 0x1
+#define DL1_MONO_MASK_SFT (0x1 << 4)
+#define DL1_NORMAL_MODE_SFT 3
+#define DL1_NORMAL_MODE_MASK 0x1
+#define DL1_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL1_HALIGN_SFT 2
+#define DL1_HALIGN_MASK 0x1
+#define DL1_HALIGN_MASK_SFT (0x1 << 2)
+#define DL1_HD_MODE_SFT 0
+#define DL1_HD_MODE_MASK 0x3
+#define DL1_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL1_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL2_BASE_MSB */
+#define DL2_BASE__ADDR_MSB_SFT 0
+#define DL2_BASE__ADDR_MSB_MASK 0x1ff
+#define DL2_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL2_BASE */
+#define DL2_BASE_ADDR_SFT 4
+#define DL2_BASE_ADDR_MASK 0xfffffff
+#define DL2_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL2_CUR_MSB */
+#define DL2_CUR_PTR_MSB_SFT 0
+#define DL2_CUR_PTR_MSB_MASK 0x1ff
+#define DL2_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL2_CUR */
+#define DL2_CUR_PTR_SFT 0
+#define DL2_CUR_PTR_MASK 0xffffffff
+#define DL2_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL2_END_MSB */
+#define DL2_END_ADDR_MSB_SFT 0
+#define DL2_END_ADDR_MSB_MASK 0x1ff
+#define DL2_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL2_END */
+#define DL2_END_ADDR_SFT 4
+#define DL2_END_ADDR_MASK 0xfffffff
+#define DL2_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL2_RCH_MON */
+#define DL2_RCH_DATA_SFT 0
+#define DL2_RCH_DATA_MASK 0xffffffff
+#define DL2_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL2_LCH_MON */
+#define DL2_LCH_DATA_SFT 0
+#define DL2_LCH_DATA_MASK 0xffffffff
+#define DL2_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL2_CON0 */
+#define DL2_ON_SFT 28
+#define DL2_ON_MASK 0x1
+#define DL2_ON_MASK_SFT (0x1 << 28)
+#define DL2_ONE_HEART_SEL_SFT 22
+#define DL2_ONE_HEART_SEL_MASK 0x3
+#define DL2_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL2_MINLEN_SFT 20
+#define DL2_MINLEN_MASK 0x3
+#define DL2_MINLEN_MASK_SFT (0x3 << 20)
+#define DL2_MAXLEN_SFT 16
+#define DL2_MAXLEN_MASK 0x3
+#define DL2_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL2_SEL_DOMAIN_SFT 13
+#define DL2_SEL_DOMAIN_MASK 0x7
+#define DL2_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL2_SEL_FS_SFT 8
+#define DL2_SEL_FS_MASK 0x1f
+#define DL2_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL2_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL2_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL2_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL2_PBUF_SIZE_SFT 5
+#define DL2_PBUF_SIZE_MASK 0x3
+#define DL2_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL2_MONO_SFT 4
+#define DL2_MONO_MASK 0x1
+#define DL2_MONO_MASK_SFT (0x1 << 4)
+#define DL2_NORMAL_MODE_SFT 3
+#define DL2_NORMAL_MODE_MASK 0x1
+#define DL2_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL2_HALIGN_SFT 2
+#define DL2_HALIGN_MASK 0x1
+#define DL2_HALIGN_MASK_SFT (0x1 << 2)
+#define DL2_HD_MODE_SFT 0
+#define DL2_HD_MODE_MASK 0x3
+#define DL2_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL2_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL3_BASE_MSB */
+#define DL3_BASE__ADDR_MSB_SFT 0
+#define DL3_BASE__ADDR_MSB_MASK 0x1ff
+#define DL3_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL3_BASE */
+#define DL3_BASE_ADDR_SFT 4
+#define DL3_BASE_ADDR_MASK 0xfffffff
+#define DL3_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL3_CUR_MSB */
+#define DL3_CUR_PTR_MSB_SFT 0
+#define DL3_CUR_PTR_MSB_MASK 0x1ff
+#define DL3_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL3_CUR */
+#define DL3_CUR_PTR_SFT 0
+#define DL3_CUR_PTR_MASK 0xffffffff
+#define DL3_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL3_END_MSB */
+#define DL3_END_ADDR_MSB_SFT 0
+#define DL3_END_ADDR_MSB_MASK 0x1ff
+#define DL3_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL3_END */
+#define DL3_END_ADDR_SFT 4
+#define DL3_END_ADDR_MASK 0xfffffff
+#define DL3_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL3_RCH_MON */
+#define DL3_RCH_DATA_SFT 0
+#define DL3_RCH_DATA_MASK 0xffffffff
+#define DL3_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL3_LCH_MON */
+#define DL3_LCH_DATA_SFT 0
+#define DL3_LCH_DATA_MASK 0xffffffff
+#define DL3_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL3_CON0 */
+#define DL3_ON_SFT 28
+#define DL3_ON_MASK 0x1
+#define DL3_ON_MASK_SFT (0x1 << 28)
+#define DL3_ONE_HEART_SEL_SFT 22
+#define DL3_ONE_HEART_SEL_MASK 0x3
+#define DL3_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL3_MINLEN_SFT 20
+#define DL3_MINLEN_MASK 0x3
+#define DL3_MINLEN_MASK_SFT (0x3 << 20)
+#define DL3_MAXLEN_SFT 16
+#define DL3_MAXLEN_MASK 0x3
+#define DL3_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL3_SEL_DOMAIN_SFT 13
+#define DL3_SEL_DOMAIN_MASK 0x7
+#define DL3_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL3_SEL_FS_SFT 8
+#define DL3_SEL_FS_MASK 0x1f
+#define DL3_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL3_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL3_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL3_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL3_PBUF_SIZE_SFT 5
+#define DL3_PBUF_SIZE_MASK 0x3
+#define DL3_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL3_MONO_SFT 4
+#define DL3_MONO_MASK 0x1
+#define DL3_MONO_MASK_SFT (0x1 << 4)
+#define DL3_NORMAL_MODE_SFT 3
+#define DL3_NORMAL_MODE_MASK 0x1
+#define DL3_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL3_HALIGN_SFT 2
+#define DL3_HALIGN_MASK 0x1
+#define DL3_HALIGN_MASK_SFT (0x1 << 2)
+#define DL3_HD_MODE_SFT 0
+#define DL3_HD_MODE_MASK 0x3
+#define DL3_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL3_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL4_BASE_MSB */
+#define DL4_BASE__ADDR_MSB_SFT 0
+#define DL4_BASE__ADDR_MSB_MASK 0x1ff
+#define DL4_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL4_BASE */
+#define DL4_BASE_ADDR_SFT 4
+#define DL4_BASE_ADDR_MASK 0xfffffff
+#define DL4_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL4_CUR_MSB */
+#define DL4_CUR_PTR_MSB_SFT 0
+#define DL4_CUR_PTR_MSB_MASK 0x1ff
+#define DL4_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL4_CUR */
+#define DL4_CUR_PTR_SFT 0
+#define DL4_CUR_PTR_MASK 0xffffffff
+#define DL4_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL4_END_MSB */
+#define DL4_END_ADDR_MSB_SFT 0
+#define DL4_END_ADDR_MSB_MASK 0x1ff
+#define DL4_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL4_END */
+#define DL4_END_ADDR_SFT 4
+#define DL4_END_ADDR_MASK 0xfffffff
+#define DL4_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL4_RCH_MON */
+#define DL4_RCH_DATA_SFT 0
+#define DL4_RCH_DATA_MASK 0xffffffff
+#define DL4_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL4_LCH_MON */
+#define DL4_LCH_DATA_SFT 0
+#define DL4_LCH_DATA_MASK 0xffffffff
+#define DL4_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL4_CON0 */
+#define DL4_ON_SFT 28
+#define DL4_ON_MASK 0x1
+#define DL4_ON_MASK_SFT (0x1 << 28)
+#define DL4_ONE_HEART_SEL_SFT 22
+#define DL4_ONE_HEART_SEL_MASK 0x3
+#define DL4_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL4_MINLEN_SFT 20
+#define DL4_MINLEN_MASK 0x3
+#define DL4_MINLEN_MASK_SFT (0x3 << 20)
+#define DL4_MAXLEN_SFT 16
+#define DL4_MAXLEN_MASK 0x3
+#define DL4_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL4_SEL_DOMAIN_SFT 13
+#define DL4_SEL_DOMAIN_MASK 0x7
+#define DL4_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL4_SEL_FS_SFT 8
+#define DL4_SEL_FS_MASK 0x1f
+#define DL4_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL4_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL4_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL4_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL4_PBUF_SIZE_SFT 5
+#define DL4_PBUF_SIZE_MASK 0x3
+#define DL4_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL4_MONO_SFT 4
+#define DL4_MONO_MASK 0x1
+#define DL4_MONO_MASK_SFT (0x1 << 4)
+#define DL4_NORMAL_MODE_SFT 3
+#define DL4_NORMAL_MODE_MASK 0x1
+#define DL4_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL4_HALIGN_SFT 2
+#define DL4_HALIGN_MASK 0x1
+#define DL4_HALIGN_MASK_SFT (0x1 << 2)
+#define DL4_HD_MODE_SFT 0
+#define DL4_HD_MODE_MASK 0x3
+#define DL4_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL4_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL5_BASE_MSB */
+#define DL5_BASE__ADDR_MSB_SFT 0
+#define DL5_BASE__ADDR_MSB_MASK 0x1ff
+#define DL5_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL5_BASE */
+#define DL5_BASE_ADDR_SFT 4
+#define DL5_BASE_ADDR_MASK 0xfffffff
+#define DL5_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL5_CUR_MSB */
+#define DL5_CUR_PTR_MSB_SFT 0
+#define DL5_CUR_PTR_MSB_MASK 0x1ff
+#define DL5_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL5_CUR */
+#define DL5_CUR_PTR_SFT 0
+#define DL5_CUR_PTR_MASK 0xffffffff
+#define DL5_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL5_END_MSB */
+#define DL5_END_ADDR_MSB_SFT 0
+#define DL5_END_ADDR_MSB_MASK 0x1ff
+#define DL5_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL5_END */
+#define DL5_END_ADDR_SFT 4
+#define DL5_END_ADDR_MASK 0xfffffff
+#define DL5_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL5_RCH_MON */
+#define DL5_RCH_DATA_SFT 0
+#define DL5_RCH_DATA_MASK 0xffffffff
+#define DL5_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL5_LCH_MON */
+#define DL5_LCH_DATA_SFT 0
+#define DL5_LCH_DATA_MASK 0xffffffff
+#define DL5_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL5_CON0 */
+#define DL5_ON_SFT 28
+#define DL5_ON_MASK 0x1
+#define DL5_ON_MASK_SFT (0x1 << 28)
+#define DL5_ONE_HEART_SEL_SFT 22
+#define DL5_ONE_HEART_SEL_MASK 0x3
+#define DL5_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL5_MINLEN_SFT 20
+#define DL5_MINLEN_MASK 0x3
+#define DL5_MINLEN_MASK_SFT (0x3 << 20)
+#define DL5_MAXLEN_SFT 16
+#define DL5_MAXLEN_MASK 0x3
+#define DL5_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL5_SEL_DOMAIN_SFT 13
+#define DL5_SEL_DOMAIN_MASK 0x7
+#define DL5_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL5_SEL_FS_SFT 8
+#define DL5_SEL_FS_MASK 0x1f
+#define DL5_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL5_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL5_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL5_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL5_PBUF_SIZE_SFT 5
+#define DL5_PBUF_SIZE_MASK 0x3
+#define DL5_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL5_MONO_SFT 4
+#define DL5_MONO_MASK 0x1
+#define DL5_MONO_MASK_SFT (0x1 << 4)
+#define DL5_NORMAL_MODE_SFT 3
+#define DL5_NORMAL_MODE_MASK 0x1
+#define DL5_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL5_HALIGN_SFT 2
+#define DL5_HALIGN_MASK 0x1
+#define DL5_HALIGN_MASK_SFT (0x1 << 2)
+#define DL5_HD_MODE_SFT 0
+#define DL5_HD_MODE_MASK 0x3
+#define DL5_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL5_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL6_BASE_MSB */
+#define DL6_BASE__ADDR_MSB_SFT 0
+#define DL6_BASE__ADDR_MSB_MASK 0x1ff
+#define DL6_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL6_BASE */
+#define DL6_BASE_ADDR_SFT 4
+#define DL6_BASE_ADDR_MASK 0xfffffff
+#define DL6_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL6_CUR_MSB */
+#define DL6_CUR_PTR_MSB_SFT 0
+#define DL6_CUR_PTR_MSB_MASK 0x1ff
+#define DL6_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL6_CUR */
+#define DL6_CUR_PTR_SFT 0
+#define DL6_CUR_PTR_MASK 0xffffffff
+#define DL6_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL6_END_MSB */
+#define DL6_END_ADDR_MSB_SFT 0
+#define DL6_END_ADDR_MSB_MASK 0x1ff
+#define DL6_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL6_END */
+#define DL6_END_ADDR_SFT 4
+#define DL6_END_ADDR_MASK 0xfffffff
+#define DL6_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL6_RCH_MON */
+#define DL6_RCH_DATA_SFT 0
+#define DL6_RCH_DATA_MASK 0xffffffff
+#define DL6_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL6_LCH_MON */
+#define DL6_LCH_DATA_SFT 0
+#define DL6_LCH_DATA_MASK 0xffffffff
+#define DL6_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL6_CON0 */
+#define DL6_ON_SFT 28
+#define DL6_ON_MASK 0x1
+#define DL6_ON_MASK_SFT (0x1 << 28)
+#define DL6_ONE_HEART_SEL_SFT 22
+#define DL6_ONE_HEART_SEL_MASK 0x3
+#define DL6_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL6_MINLEN_SFT 20
+#define DL6_MINLEN_MASK 0x3
+#define DL6_MINLEN_MASK_SFT (0x3 << 20)
+#define DL6_MAXLEN_SFT 16
+#define DL6_MAXLEN_MASK 0x3
+#define DL6_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL6_SEL_DOMAIN_SFT 13
+#define DL6_SEL_DOMAIN_MASK 0x7
+#define DL6_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL6_SEL_FS_SFT 8
+#define DL6_SEL_FS_MASK 0x1f
+#define DL6_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL6_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL6_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL6_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL6_PBUF_SIZE_SFT 5
+#define DL6_PBUF_SIZE_MASK 0x3
+#define DL6_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL6_MONO_SFT 4
+#define DL6_MONO_MASK 0x1
+#define DL6_MONO_MASK_SFT (0x1 << 4)
+#define DL6_NORMAL_MODE_SFT 3
+#define DL6_NORMAL_MODE_MASK 0x1
+#define DL6_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL6_HALIGN_SFT 2
+#define DL6_HALIGN_MASK 0x1
+#define DL6_HALIGN_MASK_SFT (0x1 << 2)
+#define DL6_HD_MODE_SFT 0
+#define DL6_HD_MODE_MASK 0x3
+#define DL6_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL6_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL7_BASE_MSB */
+#define DL7_BASE__ADDR_MSB_SFT 0
+#define DL7_BASE__ADDR_MSB_MASK 0x1ff
+#define DL7_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL7_BASE */
+#define DL7_BASE_ADDR_SFT 4
+#define DL7_BASE_ADDR_MASK 0xfffffff
+#define DL7_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL7_CUR_MSB */
+#define DL7_CUR_PTR_MSB_SFT 0
+#define DL7_CUR_PTR_MSB_MASK 0x1ff
+#define DL7_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL7_CUR */
+#define DL7_CUR_PTR_SFT 0
+#define DL7_CUR_PTR_MASK 0xffffffff
+#define DL7_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL7_END_MSB */
+#define DL7_END_ADDR_MSB_SFT 0
+#define DL7_END_ADDR_MSB_MASK 0x1ff
+#define DL7_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL7_END */
+#define DL7_END_ADDR_SFT 4
+#define DL7_END_ADDR_MASK 0xfffffff
+#define DL7_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL7_RCH_MON */
+#define DL7_RCH_DATA_SFT 0
+#define DL7_RCH_DATA_MASK 0xffffffff
+#define DL7_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL7_LCH_MON */
+#define DL7_LCH_DATA_SFT 0
+#define DL7_LCH_DATA_MASK 0xffffffff
+#define DL7_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL7_CON0 */
+#define DL7_ON_SFT 28
+#define DL7_ON_MASK 0x1
+#define DL7_ON_MASK_SFT (0x1 << 28)
+#define DL7_ONE_HEART_SEL_SFT 22
+#define DL7_ONE_HEART_SEL_MASK 0x3
+#define DL7_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL7_MINLEN_SFT 20
+#define DL7_MINLEN_MASK 0x3
+#define DL7_MINLEN_MASK_SFT (0x3 << 20)
+#define DL7_MAXLEN_SFT 16
+#define DL7_MAXLEN_MASK 0x3
+#define DL7_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL7_SEL_DOMAIN_SFT 13
+#define DL7_SEL_DOMAIN_MASK 0x7
+#define DL7_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL7_SEL_FS_SFT 8
+#define DL7_SEL_FS_MASK 0x1f
+#define DL7_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL7_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL7_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL7_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL7_PBUF_SIZE_SFT 5
+#define DL7_PBUF_SIZE_MASK 0x3
+#define DL7_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL7_MONO_SFT 4
+#define DL7_MONO_MASK 0x1
+#define DL7_MONO_MASK_SFT (0x1 << 4)
+#define DL7_NORMAL_MODE_SFT 3
+#define DL7_NORMAL_MODE_MASK 0x1
+#define DL7_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL7_HALIGN_SFT 2
+#define DL7_HALIGN_MASK 0x1
+#define DL7_HALIGN_MASK_SFT (0x1 << 2)
+#define DL7_HD_MODE_SFT 0
+#define DL7_HD_MODE_MASK 0x3
+#define DL7_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL7_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL8_BASE_MSB */
+#define DL8_BASE__ADDR_MSB_SFT 0
+#define DL8_BASE__ADDR_MSB_MASK 0x1ff
+#define DL8_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL8_BASE */
+#define DL8_BASE_ADDR_SFT 4
+#define DL8_BASE_ADDR_MASK 0xfffffff
+#define DL8_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL8_CUR_MSB */
+#define DL8_CUR_PTR_MSB_SFT 0
+#define DL8_CUR_PTR_MSB_MASK 0x1ff
+#define DL8_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL8_CUR */
+#define DL8_CUR_PTR_SFT 0
+#define DL8_CUR_PTR_MASK 0xffffffff
+#define DL8_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL8_END_MSB */
+#define DL8_END_ADDR_MSB_SFT 0
+#define DL8_END_ADDR_MSB_MASK 0x1ff
+#define DL8_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL8_END */
+#define DL8_END_ADDR_SFT 4
+#define DL8_END_ADDR_MASK 0xfffffff
+#define DL8_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL8_RCH_MON */
+#define DL8_RCH_DATA_SFT 0
+#define DL8_RCH_DATA_MASK 0xffffffff
+#define DL8_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL8_LCH_MON */
+#define DL8_LCH_DATA_SFT 0
+#define DL8_LCH_DATA_MASK 0xffffffff
+#define DL8_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL8_CON0 */
+#define DL8_ON_SFT 28
+#define DL8_ON_MASK 0x1
+#define DL8_ON_MASK_SFT (0x1 << 28)
+#define DL8_ONE_HEART_SEL_SFT 22
+#define DL8_ONE_HEART_SEL_MASK 0x3
+#define DL8_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL8_MINLEN_SFT 20
+#define DL8_MINLEN_MASK 0x3
+#define DL8_MINLEN_MASK_SFT (0x3 << 20)
+#define DL8_MAXLEN_SFT 16
+#define DL8_MAXLEN_MASK 0x3
+#define DL8_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL8_SEL_DOMAIN_SFT 13
+#define DL8_SEL_DOMAIN_MASK 0x7
+#define DL8_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL8_SEL_FS_SFT 8
+#define DL8_SEL_FS_MASK 0x1f
+#define DL8_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL8_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL8_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL8_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL8_PBUF_SIZE_SFT 5
+#define DL8_PBUF_SIZE_MASK 0x3
+#define DL8_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL8_MONO_SFT 4
+#define DL8_MONO_MASK 0x1
+#define DL8_MONO_MASK_SFT (0x1 << 4)
+#define DL8_NORMAL_MODE_SFT 3
+#define DL8_NORMAL_MODE_MASK 0x1
+#define DL8_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL8_HALIGN_SFT 2
+#define DL8_HALIGN_MASK 0x1
+#define DL8_HALIGN_MASK_SFT (0x1 << 2)
+#define DL8_HD_MODE_SFT 0
+#define DL8_HD_MODE_MASK 0x3
+#define DL8_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL8_MON0 */
+#define RESERVED_01_SFT 20
+#define RESERVED_01_MASK 0xfff
+#define RESERVED_01_MASK_SFT (0xfff << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_EMPTY_SFT 18
+#define BUF_EMPTY_MASK 0x1
+#define BUF_EMPTY_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_DL_24CH_BASE_MSB */
+#define DL_24CH_BASE__ADDR_MSB_SFT 0
+#define DL_24CH_BASE__ADDR_MSB_MASK 0x1ff
+#define DL_24CH_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL_24CH_BASE */
+#define DL_24CH_BASE_ADDR_SFT 4
+#define DL_24CH_BASE_ADDR_MASK 0xfffffff
+#define DL_24CH_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL_24CH_CUR_MSB */
+#define DL_24CH_CUR_PTR_MSB_SFT 0
+#define DL_24CH_CUR_PTR_MSB_MASK 0x1ff
+#define DL_24CH_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL_24CH_CUR */
+#define DL_24CH_CUR_PTR_SFT 0
+#define DL_24CH_CUR_PTR_MASK 0xffffffff
+#define DL_24CH_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_END_MSB */
+#define DL_24CH_END_ADDR_MSB_SFT 0
+#define DL_24CH_END_ADDR_MSB_MASK 0x1ff
+#define DL_24CH_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL_24CH_END */
+#define DL_24CH_END_ADDR_SFT 4
+#define DL_24CH_END_ADDR_MASK 0xfffffff
+#define DL_24CH_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL_24CH_CON0 */
+#define DL_24CH_ON_SFT 31
+#define DL_24CH_ON_MASK 0x1
+#define DL_24CH_ON_MASK_SFT (0x1 << 31)
+#define DL_24CH_NUM_SFT 24
+#define DL_24CH_NUM_MASK 0x3f
+#define DL_24CH_NUM_MASK_SFT (0x3f << 24)
+#define DL_24CH_ONE_HEART_SEL_SFT 22
+#define DL_24CH_ONE_HEART_SEL_MASK 0x3
+#define DL_24CH_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL_24CH_MINLEN_SFT 20
+#define DL_24CH_MINLEN_MASK 0x3
+#define DL_24CH_MINLEN_MASK_SFT (0x3 << 20)
+#define DL_24CH_MAXLEN_SFT 16
+#define DL_24CH_MAXLEN_MASK 0x3
+#define DL_24CH_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL_24CH_SEL_DOMAIN_SFT 13
+#define DL_24CH_SEL_DOMAIN_MASK 0x7
+#define DL_24CH_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL_24CH_SEL_FS_SFT 8
+#define DL_24CH_SEL_FS_MASK 0x1f
+#define DL_24CH_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL_24CH_BUF_EMPTY_CLR_SFT 7
+#define DL_24CH_BUF_EMPTY_CLR_MASK 0x1
+#define DL_24CH_BUF_EMPTY_CLR_MASK_SFT (0x1 << 7)
+#define DL_24CH_PBUF_SIZE_SFT 5
+#define DL_24CH_PBUF_SIZE_MASK 0x3
+#define DL_24CH_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL_24CH_HANG_CLR_SFT 4
+#define DL_24CH_HANG_CLR_MASK 0x1
+#define DL_24CH_HANG_CLR_MASK_SFT (0x1 << 4)
+#define DL_24CH_NORMAL_MODE_SFT 3
+#define DL_24CH_NORMAL_MODE_MASK 0x1
+#define DL_24CH_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL_24CH_HALIGN_SFT 2
+#define DL_24CH_HALIGN_MASK 0x1
+#define DL_24CH_HALIGN_MASK_SFT (0x1 << 2)
+#define DL_24CH_HD_MODE_SFT 0
+#define DL_24CH_HD_MODE_MASK 0x3
+#define DL_24CH_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL23_BASE_MSB */
+#define DL23_BASE__ADDR_MSB_SFT 0
+#define DL23_BASE__ADDR_MSB_MASK 0x1ff
+#define DL23_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL23_BASE */
+#define DL23_BASE_ADDR_SFT 4
+#define DL23_BASE_ADDR_MASK 0xfffffff
+#define DL23_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL23_CUR_MSB */
+#define DL23_CUR_PTR_MSB_SFT 0
+#define DL23_CUR_PTR_MSB_MASK 0x1ff
+#define DL23_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL23_CUR */
+#define DL23_CUR_PTR_SFT 0
+#define DL23_CUR_PTR_MASK 0xffffffff
+#define DL23_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL23_END_MSB */
+#define DL23_END_ADDR_MSB_SFT 0
+#define DL23_END_ADDR_MSB_MASK 0x1ff
+#define DL23_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL23_END */
+#define DL23_END_ADDR_SFT 4
+#define DL23_END_ADDR_MASK 0xfffffff
+#define DL23_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL23_RCH_MON */
+#define DL23_RCH_DATA_SFT 0
+#define DL23_RCH_DATA_MASK 0xffffffff
+#define DL23_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL23_LCH_MON */
+#define DL23_LCH_DATA_SFT 0
+#define DL23_LCH_DATA_MASK 0xffffffff
+#define DL23_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL23_CON0 */
+#define DL23_ON_SFT 28
+#define DL23_ON_MASK 0x1
+#define DL23_ON_MASK_SFT (0x1 << 28)
+#define DL23_ONE_HEART_SEL_SFT 22
+#define DL23_ONE_HEART_SEL_MASK 0x3
+#define DL23_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL23_MINLEN_SFT 20
+#define DL23_MINLEN_MASK 0x3
+#define DL23_MINLEN_MASK_SFT (0x3 << 20)
+#define DL23_MAXLEN_SFT 16
+#define DL23_MAXLEN_MASK 0x3
+#define DL23_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL23_SEL_DOMAIN_SFT 13
+#define DL23_SEL_DOMAIN_MASK 0x7
+#define DL23_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL23_SEL_FS_SFT 8
+#define DL23_SEL_FS_MASK 0x1f
+#define DL23_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL23_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL23_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL23_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL23_PBUF_SIZE_SFT 5
+#define DL23_PBUF_SIZE_MASK 0x3
+#define DL23_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL23_MONO_SFT 4
+#define DL23_MONO_MASK 0x1
+#define DL23_MONO_MASK_SFT (0x1 << 4)
+#define DL23_NORMAL_MODE_SFT 3
+#define DL23_NORMAL_MODE_MASK 0x1
+#define DL23_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL23_HALIGN_SFT 2
+#define DL23_HALIGN_MASK 0x1
+#define DL23_HALIGN_MASK_SFT (0x1 << 2)
+#define DL23_HD_MODE_SFT 0
+#define DL23_HD_MODE_MASK 0x3
+#define DL23_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL24_BASE_MSB */
+#define DL24_BASE__ADDR_MSB_SFT 0
+#define DL24_BASE__ADDR_MSB_MASK 0x1ff
+#define DL24_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL24_BASE */
+#define DL24_BASE_ADDR_SFT 4
+#define DL24_BASE_ADDR_MASK 0xfffffff
+#define DL24_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL24_CUR_MSB */
+#define DL24_CUR_PTR_MSB_SFT 0
+#define DL24_CUR_PTR_MSB_MASK 0x1ff
+#define DL24_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL24_CUR */
+#define DL24_CUR_PTR_SFT 0
+#define DL24_CUR_PTR_MASK 0xffffffff
+#define DL24_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL24_END_MSB */
+#define DL24_END_ADDR_MSB_SFT 0
+#define DL24_END_ADDR_MSB_MASK 0x1ff
+#define DL24_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL24_END */
+#define DL24_END_ADDR_SFT 4
+#define DL24_END_ADDR_MASK 0xfffffff
+#define DL24_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL24_RCH_MON */
+#define DL24_RCH_DATA_SFT 0
+#define DL24_RCH_DATA_MASK 0xffffffff
+#define DL24_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL24_LCH_MON */
+#define DL24_LCH_DATA_SFT 0
+#define DL24_LCH_DATA_MASK 0xffffffff
+#define DL24_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL24_CON0 */
+#define DL24_ON_SFT 28
+#define DL24_ON_MASK 0x1
+#define DL24_ON_MASK_SFT (0x1 << 28)
+#define DL24_ONE_HEART_SEL_SFT 22
+#define DL24_ONE_HEART_SEL_MASK 0x3
+#define DL24_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL24_MINLEN_SFT 20
+#define DL24_MINLEN_MASK 0x3
+#define DL24_MINLEN_MASK_SFT (0x3 << 20)
+#define DL24_MAXLEN_SFT 16
+#define DL24_MAXLEN_MASK 0x3
+#define DL24_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL24_SEL_DOMAIN_SFT 13
+#define DL24_SEL_DOMAIN_MASK 0x7
+#define DL24_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL24_SEL_FS_SFT 8
+#define DL24_SEL_FS_MASK 0x1f
+#define DL24_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL24_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL24_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL24_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL24_PBUF_SIZE_SFT 5
+#define DL24_PBUF_SIZE_MASK 0x3
+#define DL24_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL24_MONO_SFT 4
+#define DL24_MONO_MASK 0x1
+#define DL24_MONO_MASK_SFT (0x1 << 4)
+#define DL24_NORMAL_MODE_SFT 3
+#define DL24_NORMAL_MODE_MASK 0x1
+#define DL24_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL24_HALIGN_SFT 2
+#define DL24_HALIGN_MASK 0x1
+#define DL24_HALIGN_MASK_SFT (0x1 << 2)
+#define DL24_HD_MODE_SFT 0
+#define DL24_HD_MODE_MASK 0x3
+#define DL24_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL25_BASE_MSB */
+#define DL25_BASE__ADDR_MSB_SFT 0
+#define DL25_BASE__ADDR_MSB_MASK 0x1ff
+#define DL25_BASE__ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL25_BASE */
+#define DL25_BASE_ADDR_SFT 4
+#define DL25_BASE_ADDR_MASK 0xfffffff
+#define DL25_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL25_CUR_MSB */
+#define DL25_CUR_PTR_MSB_SFT 0
+#define DL25_CUR_PTR_MSB_MASK 0x1ff
+#define DL25_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL25_CUR */
+#define DL25_CUR_PTR_SFT 0
+#define DL25_CUR_PTR_MASK 0xffffffff
+#define DL25_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL25_END_MSB */
+#define DL25_END_ADDR_MSB_SFT 0
+#define DL25_END_ADDR_MSB_MASK 0x1ff
+#define DL25_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_DL25_END */
+#define DL25_END_ADDR_SFT 4
+#define DL25_END_ADDR_MASK 0xfffffff
+#define DL25_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_DL25_RCH_MON */
+#define DL25_RCH_DATA_SFT 0
+#define DL25_RCH_DATA_MASK 0xffffffff
+#define DL25_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL25_LCH_MON */
+#define DL25_LCH_DATA_SFT 0
+#define DL25_LCH_DATA_MASK 0xffffffff
+#define DL25_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL25_CON0 */
+#define DL25_ON_SFT 28
+#define DL25_ON_MASK 0x1
+#define DL25_ON_MASK_SFT (0x1 << 28)
+#define DL25_ONE_HEART_SEL_SFT 22
+#define DL25_ONE_HEART_SEL_MASK 0x3
+#define DL25_ONE_HEART_SEL_MASK_SFT (0x3 << 22)
+#define DL25_MINLEN_SFT 20
+#define DL25_MINLEN_MASK 0x3
+#define DL25_MINLEN_MASK_SFT (0x3 << 20)
+#define DL25_MAXLEN_SFT 16
+#define DL25_MAXLEN_MASK 0x3
+#define DL25_MAXLEN_MASK_SFT (0x3 << 16)
+#define DL25_SEL_DOMAIN_SFT 13
+#define DL25_SEL_DOMAIN_MASK 0x7
+#define DL25_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define DL25_SEL_FS_SFT 8
+#define DL25_SEL_FS_MASK 0x1f
+#define DL25_SEL_FS_MASK_SFT (0x1f << 8)
+#define DL25_SW_CLEAR_BUF_EMPTY_SFT 7
+#define DL25_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL25_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 7)
+#define DL25_PBUF_SIZE_SFT 5
+#define DL25_PBUF_SIZE_MASK 0x3
+#define DL25_PBUF_SIZE_MASK_SFT (0x3 << 5)
+#define DL25_MONO_SFT 4
+#define DL25_MONO_MASK 0x1
+#define DL25_MONO_MASK_SFT (0x1 << 4)
+#define DL25_NORMAL_MODE_SFT 3
+#define DL25_NORMAL_MODE_MASK 0x1
+#define DL25_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define DL25_HALIGN_SFT 2
+#define DL25_HALIGN_MASK 0x1
+#define DL25_HALIGN_MASK_SFT (0x1 << 2)
+#define DL25_HD_MODE_SFT 0
+#define DL25_HD_MODE_MASK 0x3
+#define DL25_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL0_BASE_MSB */
+#define VUL0_BASE_ADDR_MSB_SFT 0
+#define VUL0_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL0_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL0_BASE */
+#define VUL0_BASE_ADDR_SFT 4
+#define VUL0_BASE_ADDR_MASK 0xfffffff
+#define VUL0_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL0_CUR_MSB */
+#define VUL0_CUR_PTR_MSB_SFT 0
+#define VUL0_CUR_PTR_MSB_MASK 0x1ff
+#define VUL0_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL0_CUR */
+#define VUL0_CUR_PTR_SFT 0
+#define VUL0_CUR_PTR_MASK 0xffffffff
+#define VUL0_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL0_END_MSB */
+#define VUL0_END_ADDR_MSB_SFT 0
+#define VUL0_END_ADDR_MSB_MASK 0x1ff
+#define VUL0_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL0_END */
+#define VUL0_END_ADDR_SFT 4
+#define VUL0_END_ADDR_MASK 0xfffffff
+#define VUL0_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL0_RCH_MON */
+#define VUL0_RCH_DATA_SFT 0
+#define VUL0_RCH_DATA_MASK 0xffffffff
+#define VUL0_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL0_LCH_MON */
+#define VUL0_LCH_DATA_SFT 0
+#define VUL0_LCH_DATA_MASK 0xffffffff
+#define VUL0_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL0_CON0 */
+#define VUL0_ON_SFT 28
+#define VUL0_ON_MASK 0x1
+#define VUL0_ON_MASK_SFT (0x1 << 28)
+#define VUL0_MINLEN_SFT 20
+#define VUL0_MINLEN_MASK 0x3
+#define VUL0_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL0_MAXLEN_SFT 16
+#define VUL0_MAXLEN_MASK 0x3
+#define VUL0_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL0_SEL_DOMAIN_SFT 13
+#define VUL0_SEL_DOMAIN_MASK 0x7
+#define VUL0_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL0_SEL_FS_SFT 8
+#define VUL0_SEL_FS_MASK 0x1f
+#define VUL0_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL0_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL0_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL0_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL0_WR_SIGN_SFT 6
+#define VUL0_WR_SIGN_MASK 0x1
+#define VUL0_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL0_R_MONO_SFT 5
+#define VUL0_R_MONO_MASK 0x1
+#define VUL0_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL0_MONO_SFT 4
+#define VUL0_MONO_MASK 0x1
+#define VUL0_MONO_MASK_SFT (0x1 << 4)
+#define VUL0_NORMAL_MODE_SFT 3
+#define VUL0_NORMAL_MODE_MASK 0x1
+#define VUL0_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL0_HALIGN_SFT 2
+#define VUL0_HALIGN_MASK 0x1
+#define VUL0_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL0_HD_MODE_SFT 0
+#define VUL0_HD_MODE_MASK 0x3
+#define VUL0_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL1_BASE_MSB */
+#define VUL1_BASE_ADDR_MSB_SFT 0
+#define VUL1_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL1_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL1_BASE */
+#define VUL1_BASE_ADDR_SFT 4
+#define VUL1_BASE_ADDR_MASK 0xfffffff
+#define VUL1_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL1_CUR_MSB */
+#define VUL1_CUR_PTR_MSB_SFT 0
+#define VUL1_CUR_PTR_MSB_MASK 0x1ff
+#define VUL1_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL1_CUR */
+#define VUL1_CUR_PTR_SFT 0
+#define VUL1_CUR_PTR_MASK 0xffffffff
+#define VUL1_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL1_END_MSB */
+#define VUL1_END_ADDR_MSB_SFT 0
+#define VUL1_END_ADDR_MSB_MASK 0x1ff
+#define VUL1_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL1_END */
+#define VUL1_END_ADDR_SFT 4
+#define VUL1_END_ADDR_MASK 0xfffffff
+#define VUL1_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL1_RCH_MON */
+#define VUL1_RCH_DATA_SFT 0
+#define VUL1_RCH_DATA_MASK 0xffffffff
+#define VUL1_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL1_LCH_MON */
+#define VUL1_LCH_DATA_SFT 0
+#define VUL1_LCH_DATA_MASK 0xffffffff
+#define VUL1_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL1_CON0 */
+#define VUL1_ON_SFT 28
+#define VUL1_ON_MASK 0x1
+#define VUL1_ON_MASK_SFT (0x1 << 28)
+#define VUL1_MINLEN_SFT 20
+#define VUL1_MINLEN_MASK 0x3
+#define VUL1_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL1_MAXLEN_SFT 16
+#define VUL1_MAXLEN_MASK 0x3
+#define VUL1_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL1_SEL_DOMAIN_SFT 13
+#define VUL1_SEL_DOMAIN_MASK 0x7
+#define VUL1_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL1_SEL_FS_SFT 8
+#define VUL1_SEL_FS_MASK 0x1f
+#define VUL1_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL1_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL1_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL1_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL1_WR_SIGN_SFT 6
+#define VUL1_WR_SIGN_MASK 0x1
+#define VUL1_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL1_R_MONO_SFT 5
+#define VUL1_R_MONO_MASK 0x1
+#define VUL1_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL1_MONO_SFT 4
+#define VUL1_MONO_MASK 0x1
+#define VUL1_MONO_MASK_SFT (0x1 << 4)
+#define VUL1_NORMAL_MODE_SFT 3
+#define VUL1_NORMAL_MODE_MASK 0x1
+#define VUL1_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL1_HALIGN_SFT 2
+#define VUL1_HALIGN_MASK 0x1
+#define VUL1_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL1_HD_MODE_SFT 0
+#define VUL1_HD_MODE_MASK 0x3
+#define VUL1_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL1_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL2_BASE_MSB */
+#define VUL2_BASE_ADDR_MSB_SFT 0
+#define VUL2_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL2_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL2_BASE */
+#define VUL2_BASE_ADDR_SFT 4
+#define VUL2_BASE_ADDR_MASK 0xfffffff
+#define VUL2_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL2_CUR_MSB */
+#define VUL2_CUR_PTR_MSB_SFT 0
+#define VUL2_CUR_PTR_MSB_MASK 0x1ff
+#define VUL2_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL2_CUR */
+#define VUL2_CUR_PTR_SFT 0
+#define VUL2_CUR_PTR_MASK 0xffffffff
+#define VUL2_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL2_END_MSB */
+#define VUL2_END_ADDR_MSB_SFT 0
+#define VUL2_END_ADDR_MSB_MASK 0x1ff
+#define VUL2_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL2_END */
+#define VUL2_END_ADDR_SFT 4
+#define VUL2_END_ADDR_MASK 0xfffffff
+#define VUL2_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL2_RCH_MON */
+#define VUL2_RCH_DATA_SFT 0
+#define VUL2_RCH_DATA_MASK 0xffffffff
+#define VUL2_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL2_LCH_MON */
+#define VUL2_LCH_DATA_SFT 0
+#define VUL2_LCH_DATA_MASK 0xffffffff
+#define VUL2_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL2_CON0 */
+#define VUL2_ON_SFT 28
+#define VUL2_ON_MASK 0x1
+#define VUL2_ON_MASK_SFT (0x1 << 28)
+#define VUL2_MINLEN_SFT 20
+#define VUL2_MINLEN_MASK 0x3
+#define VUL2_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL2_MAXLEN_SFT 16
+#define VUL2_MAXLEN_MASK 0x3
+#define VUL2_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL2_SEL_DOMAIN_SFT 13
+#define VUL2_SEL_DOMAIN_MASK 0x7
+#define VUL2_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL2_SEL_FS_SFT 8
+#define VUL2_SEL_FS_MASK 0x1f
+#define VUL2_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL2_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL2_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL2_WR_SIGN_SFT 6
+#define VUL2_WR_SIGN_MASK 0x1
+#define VUL2_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL2_R_MONO_SFT 5
+#define VUL2_R_MONO_MASK 0x1
+#define VUL2_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL2_MONO_SFT 4
+#define VUL2_MONO_MASK 0x1
+#define VUL2_MONO_MASK_SFT (0x1 << 4)
+#define VUL2_NORMAL_MODE_SFT 3
+#define VUL2_NORMAL_MODE_MASK 0x1
+#define VUL2_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL2_HALIGN_SFT 2
+#define VUL2_HALIGN_MASK 0x1
+#define VUL2_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL2_HD_MODE_SFT 0
+#define VUL2_HD_MODE_MASK 0x3
+#define VUL2_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL2_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL3_BASE_MSB */
+#define VUL3_BASE_ADDR_MSB_SFT 0
+#define VUL3_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL3_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL3_BASE */
+#define VUL3_BASE_ADDR_SFT 4
+#define VUL3_BASE_ADDR_MASK 0xfffffff
+#define VUL3_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL3_CUR_MSB */
+#define VUL3_CUR_PTR_MSB_SFT 0
+#define VUL3_CUR_PTR_MSB_MASK 0x1ff
+#define VUL3_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL3_CUR */
+#define VUL3_CUR_PTR_SFT 0
+#define VUL3_CUR_PTR_MASK 0xffffffff
+#define VUL3_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL3_END_MSB */
+#define VUL3_END_ADDR_MSB_SFT 0
+#define VUL3_END_ADDR_MSB_MASK 0x1ff
+#define VUL3_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL3_END */
+#define VUL3_END_ADDR_SFT 4
+#define VUL3_END_ADDR_MASK 0xfffffff
+#define VUL3_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL3_RCH_MON */
+#define VUL3_RCH_DATA_SFT 0
+#define VUL3_RCH_DATA_MASK 0xffffffff
+#define VUL3_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL3_LCH_MON */
+#define VUL3_LCH_DATA_SFT 0
+#define VUL3_LCH_DATA_MASK 0xffffffff
+#define VUL3_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL3_CON0 */
+#define VUL3_ON_SFT 28
+#define VUL3_ON_MASK 0x1
+#define VUL3_ON_MASK_SFT (0x1 << 28)
+#define VUL3_MINLEN_SFT 20
+#define VUL3_MINLEN_MASK 0x3
+#define VUL3_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL3_MAXLEN_SFT 16
+#define VUL3_MAXLEN_MASK 0x3
+#define VUL3_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL3_SEL_DOMAIN_SFT 13
+#define VUL3_SEL_DOMAIN_MASK 0x7
+#define VUL3_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL3_SEL_FS_SFT 8
+#define VUL3_SEL_FS_MASK 0x1f
+#define VUL3_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL3_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL3_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL3_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL3_WR_SIGN_SFT 6
+#define VUL3_WR_SIGN_MASK 0x1
+#define VUL3_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL3_R_MONO_SFT 5
+#define VUL3_R_MONO_MASK 0x1
+#define VUL3_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL3_MONO_SFT 4
+#define VUL3_MONO_MASK 0x1
+#define VUL3_MONO_MASK_SFT (0x1 << 4)
+#define VUL3_NORMAL_MODE_SFT 3
+#define VUL3_NORMAL_MODE_MASK 0x1
+#define VUL3_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL3_HALIGN_SFT 2
+#define VUL3_HALIGN_MASK 0x1
+#define VUL3_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL3_HD_MODE_SFT 0
+#define VUL3_HD_MODE_MASK 0x3
+#define VUL3_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL3_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL4_BASE_MSB */
+#define VUL4_BASE_ADDR_MSB_SFT 0
+#define VUL4_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL4_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL4_BASE */
+#define VUL4_BASE_ADDR_SFT 4
+#define VUL4_BASE_ADDR_MASK 0xfffffff
+#define VUL4_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL4_CUR_MSB */
+#define VUL4_CUR_PTR_MSB_SFT 0
+#define VUL4_CUR_PTR_MSB_MASK 0x1ff
+#define VUL4_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL4_CUR */
+#define VUL4_CUR_PTR_SFT 0
+#define VUL4_CUR_PTR_MASK 0xffffffff
+#define VUL4_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL4_END_MSB */
+#define VUL4_END_ADDR_MSB_SFT 0
+#define VUL4_END_ADDR_MSB_MASK 0x1ff
+#define VUL4_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL4_END */
+#define VUL4_END_ADDR_SFT 4
+#define VUL4_END_ADDR_MASK 0xfffffff
+#define VUL4_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL4_RCH_MON */
+#define VUL4_RCH_DATA_SFT 0
+#define VUL4_RCH_DATA_MASK 0xffffffff
+#define VUL4_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL4_LCH_MON */
+#define VUL4_LCH_DATA_SFT 0
+#define VUL4_LCH_DATA_MASK 0xffffffff
+#define VUL4_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL4_CON0 */
+#define VUL4_ON_SFT 28
+#define VUL4_ON_MASK 0x1
+#define VUL4_ON_MASK_SFT (0x1 << 28)
+#define VUL4_MINLEN_SFT 20
+#define VUL4_MINLEN_MASK 0x3
+#define VUL4_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL4_MAXLEN_SFT 16
+#define VUL4_MAXLEN_MASK 0x3
+#define VUL4_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL4_SEL_DOMAIN_SFT 13
+#define VUL4_SEL_DOMAIN_MASK 0x7
+#define VUL4_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL4_SEL_FS_SFT 8
+#define VUL4_SEL_FS_MASK 0x1f
+#define VUL4_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL4_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL4_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL4_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL4_WR_SIGN_SFT 6
+#define VUL4_WR_SIGN_MASK 0x1
+#define VUL4_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL4_R_MONO_SFT 5
+#define VUL4_R_MONO_MASK 0x1
+#define VUL4_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL4_MONO_SFT 4
+#define VUL4_MONO_MASK 0x1
+#define VUL4_MONO_MASK_SFT (0x1 << 4)
+#define VUL4_NORMAL_MODE_SFT 3
+#define VUL4_NORMAL_MODE_MASK 0x1
+#define VUL4_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL4_HALIGN_SFT 2
+#define VUL4_HALIGN_MASK 0x1
+#define VUL4_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL4_HD_MODE_SFT 0
+#define VUL4_HD_MODE_MASK 0x3
+#define VUL4_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL4_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL5_BASE_MSB */
+#define VUL5_BASE_ADDR_MSB_SFT 0
+#define VUL5_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL5_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL5_BASE */
+#define VUL5_BASE_ADDR_SFT 4
+#define VUL5_BASE_ADDR_MASK 0xfffffff
+#define VUL5_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL5_CUR_MSB */
+#define VUL5_CUR_PTR_MSB_SFT 0
+#define VUL5_CUR_PTR_MSB_MASK 0x1ff
+#define VUL5_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL5_CUR */
+#define VUL5_CUR_PTR_SFT 0
+#define VUL5_CUR_PTR_MASK 0xffffffff
+#define VUL5_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL5_END_MSB */
+#define VUL5_END_ADDR_MSB_SFT 0
+#define VUL5_END_ADDR_MSB_MASK 0x1ff
+#define VUL5_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL5_END */
+#define VUL5_END_ADDR_SFT 4
+#define VUL5_END_ADDR_MASK 0xfffffff
+#define VUL5_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL5_RCH_MON */
+#define VUL5_RCH_DATA_SFT 0
+#define VUL5_RCH_DATA_MASK 0xffffffff
+#define VUL5_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL5_LCH_MON */
+#define VUL5_LCH_DATA_SFT 0
+#define VUL5_LCH_DATA_MASK 0xffffffff
+#define VUL5_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL5_CON0 */
+#define VUL5_ON_SFT 28
+#define VUL5_ON_MASK 0x1
+#define VUL5_ON_MASK_SFT (0x1 << 28)
+#define VUL5_MINLEN_SFT 20
+#define VUL5_MINLEN_MASK 0x3
+#define VUL5_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL5_MAXLEN_SFT 16
+#define VUL5_MAXLEN_MASK 0x3
+#define VUL5_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL5_SEL_DOMAIN_SFT 13
+#define VUL5_SEL_DOMAIN_MASK 0x7
+#define VUL5_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL5_SEL_FS_SFT 8
+#define VUL5_SEL_FS_MASK 0x1f
+#define VUL5_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL5_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL5_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL5_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL5_WR_SIGN_SFT 6
+#define VUL5_WR_SIGN_MASK 0x1
+#define VUL5_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL5_R_MONO_SFT 5
+#define VUL5_R_MONO_MASK 0x1
+#define VUL5_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL5_MONO_SFT 4
+#define VUL5_MONO_MASK 0x1
+#define VUL5_MONO_MASK_SFT (0x1 << 4)
+#define VUL5_NORMAL_MODE_SFT 3
+#define VUL5_NORMAL_MODE_MASK 0x1
+#define VUL5_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL5_HALIGN_SFT 2
+#define VUL5_HALIGN_MASK 0x1
+#define VUL5_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL5_HD_MODE_SFT 0
+#define VUL5_HD_MODE_MASK 0x3
+#define VUL5_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL5_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL6_BASE_MSB */
+#define VUL6_BASE_ADDR_MSB_SFT 0
+#define VUL6_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL6_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL6_BASE */
+#define VUL6_BASE_ADDR_SFT 4
+#define VUL6_BASE_ADDR_MASK 0xfffffff
+#define VUL6_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL6_CUR_MSB */
+#define VUL6_CUR_PTR_MSB_SFT 0
+#define VUL6_CUR_PTR_MSB_MASK 0x1ff
+#define VUL6_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL6_CUR */
+#define VUL6_CUR_PTR_SFT 0
+#define VUL6_CUR_PTR_MASK 0xffffffff
+#define VUL6_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL6_END_MSB */
+#define VUL6_END_ADDR_MSB_SFT 0
+#define VUL6_END_ADDR_MSB_MASK 0x1ff
+#define VUL6_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL6_END */
+#define VUL6_END_ADDR_SFT 4
+#define VUL6_END_ADDR_MASK 0xfffffff
+#define VUL6_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL6_RCH_MON */
+#define VUL6_RCH_DATA_SFT 0
+#define VUL6_RCH_DATA_MASK 0xffffffff
+#define VUL6_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL6_LCH_MON */
+#define VUL6_LCH_DATA_SFT 0
+#define VUL6_LCH_DATA_MASK 0xffffffff
+#define VUL6_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL6_CON0 */
+#define VUL6_ON_SFT 28
+#define VUL6_ON_MASK 0x1
+#define VUL6_ON_MASK_SFT (0x1 << 28)
+#define VUL6_MINLEN_SFT 20
+#define VUL6_MINLEN_MASK 0x3
+#define VUL6_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL6_MAXLEN_SFT 16
+#define VUL6_MAXLEN_MASK 0x3
+#define VUL6_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL6_SEL_DOMAIN_SFT 13
+#define VUL6_SEL_DOMAIN_MASK 0x7
+#define VUL6_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL6_SEL_FS_SFT 8
+#define VUL6_SEL_FS_MASK 0x1f
+#define VUL6_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL6_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL6_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL6_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL6_WR_SIGN_SFT 6
+#define VUL6_WR_SIGN_MASK 0x1
+#define VUL6_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL6_R_MONO_SFT 5
+#define VUL6_R_MONO_MASK 0x1
+#define VUL6_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL6_MONO_SFT 4
+#define VUL6_MONO_MASK 0x1
+#define VUL6_MONO_MASK_SFT (0x1 << 4)
+#define VUL6_NORMAL_MODE_SFT 3
+#define VUL6_NORMAL_MODE_MASK 0x1
+#define VUL6_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL6_HALIGN_SFT 2
+#define VUL6_HALIGN_MASK 0x1
+#define VUL6_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL6_HD_MODE_SFT 0
+#define VUL6_HD_MODE_MASK 0x3
+#define VUL6_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL6_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL7_BASE_MSB */
+#define VUL7_BASE_ADDR_MSB_SFT 0
+#define VUL7_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL7_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL7_BASE */
+#define VUL7_BASE_ADDR_SFT 4
+#define VUL7_BASE_ADDR_MASK 0xfffffff
+#define VUL7_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL7_CUR_MSB */
+#define VUL7_CUR_PTR_MSB_SFT 0
+#define VUL7_CUR_PTR_MSB_MASK 0x1ff
+#define VUL7_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL7_CUR */
+#define VUL7_CUR_PTR_SFT 0
+#define VUL7_CUR_PTR_MASK 0xffffffff
+#define VUL7_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL7_END_MSB */
+#define VUL7_END_ADDR_MSB_SFT 0
+#define VUL7_END_ADDR_MSB_MASK 0x1ff
+#define VUL7_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL7_END */
+#define VUL7_END_ADDR_SFT 4
+#define VUL7_END_ADDR_MASK 0xfffffff
+#define VUL7_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL7_RCH_MON */
+#define VUL7_RCH_DATA_SFT 0
+#define VUL7_RCH_DATA_MASK 0xffffffff
+#define VUL7_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL7_LCH_MON */
+#define VUL7_LCH_DATA_SFT 0
+#define VUL7_LCH_DATA_MASK 0xffffffff
+#define VUL7_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL7_CON0 */
+#define VUL7_ON_SFT 28
+#define VUL7_ON_MASK 0x1
+#define VUL7_ON_MASK_SFT (0x1 << 28)
+#define VUL7_MINLEN_SFT 20
+#define VUL7_MINLEN_MASK 0x3
+#define VUL7_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL7_MAXLEN_SFT 16
+#define VUL7_MAXLEN_MASK 0x3
+#define VUL7_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL7_SEL_DOMAIN_SFT 13
+#define VUL7_SEL_DOMAIN_MASK 0x7
+#define VUL7_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL7_SEL_FS_SFT 8
+#define VUL7_SEL_FS_MASK 0x1f
+#define VUL7_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL7_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL7_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL7_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL7_WR_SIGN_SFT 6
+#define VUL7_WR_SIGN_MASK 0x1
+#define VUL7_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL7_R_MONO_SFT 5
+#define VUL7_R_MONO_MASK 0x1
+#define VUL7_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL7_MONO_SFT 4
+#define VUL7_MONO_MASK 0x1
+#define VUL7_MONO_MASK_SFT (0x1 << 4)
+#define VUL7_NORMAL_MODE_SFT 3
+#define VUL7_NORMAL_MODE_MASK 0x1
+#define VUL7_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL7_HALIGN_SFT 2
+#define VUL7_HALIGN_MASK 0x1
+#define VUL7_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL7_HD_MODE_SFT 0
+#define VUL7_HD_MODE_MASK 0x3
+#define VUL7_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL7_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL8_BASE_MSB */
+#define VUL8_BASE_ADDR_MSB_SFT 0
+#define VUL8_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL8_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL8_BASE */
+#define VUL8_BASE_ADDR_SFT 4
+#define VUL8_BASE_ADDR_MASK 0xfffffff
+#define VUL8_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL8_CUR_MSB */
+#define VUL8_CUR_PTR_MSB_SFT 0
+#define VUL8_CUR_PTR_MSB_MASK 0x1ff
+#define VUL8_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL8_CUR */
+#define VUL8_CUR_PTR_SFT 0
+#define VUL8_CUR_PTR_MASK 0xffffffff
+#define VUL8_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL8_END_MSB */
+#define VUL8_END_ADDR_MSB_SFT 0
+#define VUL8_END_ADDR_MSB_MASK 0x1ff
+#define VUL8_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL8_END */
+#define VUL8_END_ADDR_SFT 4
+#define VUL8_END_ADDR_MASK 0xfffffff
+#define VUL8_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL8_RCH_MON */
+#define VUL8_RCH_DATA_SFT 0
+#define VUL8_RCH_DATA_MASK 0xffffffff
+#define VUL8_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL8_LCH_MON */
+#define VUL8_LCH_DATA_SFT 0
+#define VUL8_LCH_DATA_MASK 0xffffffff
+#define VUL8_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL8_CON0 */
+#define VUL8_ON_SFT 28
+#define VUL8_ON_MASK 0x1
+#define VUL8_ON_MASK_SFT (0x1 << 28)
+#define VUL8_MINLEN_SFT 20
+#define VUL8_MINLEN_MASK 0x3
+#define VUL8_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL8_MAXLEN_SFT 16
+#define VUL8_MAXLEN_MASK 0x3
+#define VUL8_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL8_SEL_DOMAIN_SFT 13
+#define VUL8_SEL_DOMAIN_MASK 0x7
+#define VUL8_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL8_SEL_FS_SFT 8
+#define VUL8_SEL_FS_MASK 0x1f
+#define VUL8_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL8_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL8_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL8_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL8_WR_SIGN_SFT 6
+#define VUL8_WR_SIGN_MASK 0x1
+#define VUL8_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL8_R_MONO_SFT 5
+#define VUL8_R_MONO_MASK 0x1
+#define VUL8_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL8_MONO_SFT 4
+#define VUL8_MONO_MASK 0x1
+#define VUL8_MONO_MASK_SFT (0x1 << 4)
+#define VUL8_NORMAL_MODE_SFT 3
+#define VUL8_NORMAL_MODE_MASK 0x1
+#define VUL8_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL8_HALIGN_SFT 2
+#define VUL8_HALIGN_MASK 0x1
+#define VUL8_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL8_HD_MODE_SFT 0
+#define VUL8_HD_MODE_MASK 0x3
+#define VUL8_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL8_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL9_BASE_MSB */
+#define VUL9_BASE_ADDR_MSB_SFT 0
+#define VUL9_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL9_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL9_BASE */
+#define VUL9_BASE_ADDR_SFT 4
+#define VUL9_BASE_ADDR_MASK 0xfffffff
+#define VUL9_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL9_CUR_MSB */
+#define VUL9_CUR_PTR_MSB_SFT 0
+#define VUL9_CUR_PTR_MSB_MASK 0x1ff
+#define VUL9_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL9_CUR */
+#define VUL9_CUR_PTR_SFT 0
+#define VUL9_CUR_PTR_MASK 0xffffffff
+#define VUL9_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL9_END_MSB */
+#define VUL9_END_ADDR_MSB_SFT 0
+#define VUL9_END_ADDR_MSB_MASK 0x1ff
+#define VUL9_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL9_END */
+#define VUL9_END_ADDR_SFT 4
+#define VUL9_END_ADDR_MASK 0xfffffff
+#define VUL9_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL9_RCH_MON */
+#define VUL9_RCH_DATA_SFT 0
+#define VUL9_RCH_DATA_MASK 0xffffffff
+#define VUL9_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL9_LCH_MON */
+#define VUL9_LCH_DATA_SFT 0
+#define VUL9_LCH_DATA_MASK 0xffffffff
+#define VUL9_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL9_CON0 */
+#define VUL9_ON_SFT 28
+#define VUL9_ON_MASK 0x1
+#define VUL9_ON_MASK_SFT (0x1 << 28)
+#define VUL9_MINLEN_SFT 20
+#define VUL9_MINLEN_MASK 0x3
+#define VUL9_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL9_MAXLEN_SFT 16
+#define VUL9_MAXLEN_MASK 0x3
+#define VUL9_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL9_SEL_DOMAIN_SFT 13
+#define VUL9_SEL_DOMAIN_MASK 0x7
+#define VUL9_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL9_SEL_FS_SFT 8
+#define VUL9_SEL_FS_MASK 0x1f
+#define VUL9_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL9_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL9_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL9_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL9_WR_SIGN_SFT 6
+#define VUL9_WR_SIGN_MASK 0x1
+#define VUL9_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL9_R_MONO_SFT 5
+#define VUL9_R_MONO_MASK 0x1
+#define VUL9_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL9_MONO_SFT 4
+#define VUL9_MONO_MASK 0x1
+#define VUL9_MONO_MASK_SFT (0x1 << 4)
+#define VUL9_NORMAL_MODE_SFT 3
+#define VUL9_NORMAL_MODE_MASK 0x1
+#define VUL9_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL9_HALIGN_SFT 2
+#define VUL9_HALIGN_MASK 0x1
+#define VUL9_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL9_HD_MODE_SFT 0
+#define VUL9_HD_MODE_MASK 0x3
+#define VUL9_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL9_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL10_BASE_MSB */
+#define VUL10_BASE_ADDR_MSB_SFT 0
+#define VUL10_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL10_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL10_BASE */
+#define VUL10_BASE_ADDR_SFT 4
+#define VUL10_BASE_ADDR_MASK 0xfffffff
+#define VUL10_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL10_CUR_MSB */
+#define VUL10_CUR_PTR_MSB_SFT 0
+#define VUL10_CUR_PTR_MSB_MASK 0x1ff
+#define VUL10_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL10_CUR */
+#define VUL10_CUR_PTR_SFT 0
+#define VUL10_CUR_PTR_MASK 0xffffffff
+#define VUL10_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL10_END_MSB */
+#define VUL10_END_ADDR_MSB_SFT 0
+#define VUL10_END_ADDR_MSB_MASK 0x1ff
+#define VUL10_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL10_END */
+#define VUL10_END_ADDR_SFT 4
+#define VUL10_END_ADDR_MASK 0xfffffff
+#define VUL10_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL10_RCH_MON */
+#define VUL10_RCH_DATA_SFT 0
+#define VUL10_RCH_DATA_MASK 0xffffffff
+#define VUL10_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL10_LCH_MON */
+#define VUL10_LCH_DATA_SFT 0
+#define VUL10_LCH_DATA_MASK 0xffffffff
+#define VUL10_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL10_CON0 */
+#define VUL10_ON_SFT 28
+#define VUL10_ON_MASK 0x1
+#define VUL10_ON_MASK_SFT (0x1 << 28)
+#define VUL10_MINLEN_SFT 20
+#define VUL10_MINLEN_MASK 0x3
+#define VUL10_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL10_MAXLEN_SFT 16
+#define VUL10_MAXLEN_MASK 0x3
+#define VUL10_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL10_SEL_DOMAIN_SFT 13
+#define VUL10_SEL_DOMAIN_MASK 0x7
+#define VUL10_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL10_SEL_FS_SFT 8
+#define VUL10_SEL_FS_MASK 0x1f
+#define VUL10_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL10_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL10_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL10_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL10_WR_SIGN_SFT 6
+#define VUL10_WR_SIGN_MASK 0x1
+#define VUL10_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL10_R_MONO_SFT 5
+#define VUL10_R_MONO_MASK 0x1
+#define VUL10_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL10_MONO_SFT 4
+#define VUL10_MONO_MASK 0x1
+#define VUL10_MONO_MASK_SFT (0x1 << 4)
+#define VUL10_NORMAL_MODE_SFT 3
+#define VUL10_NORMAL_MODE_MASK 0x1
+#define VUL10_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL10_HALIGN_SFT 2
+#define VUL10_HALIGN_MASK 0x1
+#define VUL10_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL10_HD_MODE_SFT 0
+#define VUL10_HD_MODE_MASK 0x3
+#define VUL10_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL10_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL24_BASE_MSB */
+#define VUL24_BASE_ADDR_MSB_SFT 0
+#define VUL24_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL24_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL24_BASE */
+#define VUL24_BASE_ADDR_SFT 4
+#define VUL24_BASE_ADDR_MASK 0xfffffff
+#define VUL24_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL24_CUR_MSB */
+#define VUL24_CUR_PTR_MSB_SFT 0
+#define VUL24_CUR_PTR_MSB_MASK 0x1ff
+#define VUL24_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL24_CUR */
+#define VUL24_CUR_PTR_SFT 0
+#define VUL24_CUR_PTR_MASK 0xffffffff
+#define VUL24_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL24_END_MSB */
+#define VUL24_END_ADDR_MSB_SFT 0
+#define VUL24_END_ADDR_MSB_MASK 0x1ff
+#define VUL24_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL24_END */
+#define VUL24_END_ADDR_SFT 4
+#define VUL24_END_ADDR_MASK 0xfffffff
+#define VUL24_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL24_CON0 */
+#define OUT_ON_USE_VUL24_SFT 29
+#define OUT_ON_USE_VUL24_MASK 0x1
+#define OUT_ON_USE_VUL24_MASK_SFT (0x1 << 29)
+#define VUL24_ON_SFT 28
+#define VUL24_ON_MASK 0x1
+#define VUL24_ON_MASK_SFT (0x1 << 28)
+#define VUL24_MINLEN_SFT 20
+#define VUL24_MINLEN_MASK 0x3
+#define VUL24_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL24_MAXLEN_SFT 16
+#define VUL24_MAXLEN_MASK 0x3
+#define VUL24_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL24_SEL_DOMAIN_SFT 13
+#define VUL24_SEL_DOMAIN_MASK 0x7
+#define VUL24_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL24_SEL_FS_SFT 8
+#define VUL24_SEL_FS_MASK 0x1f
+#define VUL24_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL24_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL24_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL24_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL24_WR_SIGN_SFT 6
+#define VUL24_WR_SIGN_MASK 0x1
+#define VUL24_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL24_R_MONO_SFT 5
+#define VUL24_R_MONO_MASK 0x1
+#define VUL24_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL24_MONO_SFT 4
+#define VUL24_MONO_MASK 0x1
+#define VUL24_MONO_MASK_SFT (0x1 << 4)
+#define VUL24_NORMAL_MODE_SFT 3
+#define VUL24_NORMAL_MODE_MASK 0x1
+#define VUL24_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL24_HALIGN_SFT 2
+#define VUL24_HALIGN_MASK 0x1
+#define VUL24_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL24_HD_MODE_SFT 0
+#define VUL24_HD_MODE_MASK 0x3
+#define VUL24_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL24_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL25_BASE_MSB */
+#define VUL25_BASE_ADDR_MSB_SFT 0
+#define VUL25_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL25_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL25_BASE */
+#define VUL25_BASE_ADDR_SFT 4
+#define VUL25_BASE_ADDR_MASK 0xfffffff
+#define VUL25_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL25_CUR_MSB */
+#define VUL25_CUR_PTR_MSB_SFT 0
+#define VUL25_CUR_PTR_MSB_MASK 0x1ff
+#define VUL25_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL25_CUR */
+#define VUL25_CUR_PTR_SFT 0
+#define VUL25_CUR_PTR_MASK 0xffffffff
+#define VUL25_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL25_END_MSB */
+#define VUL25_END_ADDR_MSB_SFT 0
+#define VUL25_END_ADDR_MSB_MASK 0x1ff
+#define VUL25_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL25_END */
+#define VUL25_END_ADDR_SFT 4
+#define VUL25_END_ADDR_MASK 0xfffffff
+#define VUL25_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL25_CON0 */
+#define OUT_ON_USE_VUL25_SFT 29
+#define OUT_ON_USE_VUL25_MASK 0x1
+#define OUT_ON_USE_VUL25_MASK_SFT (0x1 << 29)
+#define VUL25_ON_SFT 28
+#define VUL25_ON_MASK 0x1
+#define VUL25_ON_MASK_SFT (0x1 << 28)
+#define VUL25_MINLEN_SFT 20
+#define VUL25_MINLEN_MASK 0x3
+#define VUL25_MINLEN_MASK_SFT (0x3 << 20)
+#define VUL25_MAXLEN_SFT 16
+#define VUL25_MAXLEN_MASK 0x3
+#define VUL25_MAXLEN_MASK_SFT (0x3 << 16)
+#define VUL25_SEL_DOMAIN_SFT 13
+#define VUL25_SEL_DOMAIN_MASK 0x7
+#define VUL25_SEL_DOMAIN_MASK_SFT (0x7 << 13)
+#define VUL25_SEL_FS_SFT 8
+#define VUL25_SEL_FS_MASK 0x1f
+#define VUL25_SEL_FS_MASK_SFT (0x1f << 8)
+#define VUL25_SW_CLEAR_BUF_FULL_SFT 7
+#define VUL25_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL25_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 7)
+#define VUL25_WR_SIGN_SFT 6
+#define VUL25_WR_SIGN_MASK 0x1
+#define VUL25_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL25_R_MONO_SFT 5
+#define VUL25_R_MONO_MASK 0x1
+#define VUL25_R_MONO_MASK_SFT (0x1 << 5)
+#define VUL25_MONO_SFT 4
+#define VUL25_MONO_MASK 0x1
+#define VUL25_MONO_MASK_SFT (0x1 << 4)
+#define VUL25_NORMAL_MODE_SFT 3
+#define VUL25_NORMAL_MODE_MASK 0x1
+#define VUL25_NORMAL_MODE_MASK_SFT (0x1 << 3)
+#define VUL25_HALIGN_SFT 2
+#define VUL25_HALIGN_MASK 0x1
+#define VUL25_HALIGN_MASK_SFT (0x1 << 2)
+#define VUL25_HD_MODE_SFT 0
+#define VUL25_HD_MODE_MASK 0x3
+#define VUL25_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL25_MON0 */
+#define MEM_HW_WEN_SFT 20
+#define MEM_HW_WEN_MASK 0xf
+#define MEM_HW_WEN_MASK_SFT (0xf << 20)
+#define MEM_REQ_PENDING_SFT 19
+#define MEM_REQ_PENDING_MASK 0x1
+#define MEM_REQ_PENDING_MASK_SFT (0x1 << 19)
+#define BUF_FULL_SFT 18
+#define BUF_FULL_MASK 0x1
+#define BUF_FULL_MASK_SFT (0x1 << 18)
+#define ENABLE_SYNC_MEM_SFT 17
+#define ENABLE_SYNC_MEM_MASK 0x1
+#define ENABLE_SYNC_MEM_MASK_SFT (0x1 << 17)
+#define ENABLE_SYNC_AGENT_SFT 16
+#define ENABLE_SYNC_AGENT_MASK 0x1
+#define ENABLE_SYNC_AGENT_MASK_SFT (0x1 << 16)
+#define RESERVED_02_SFT 6
+#define RESERVED_02_MASK 0x3ff
+#define RESERVED_02_MASK_SFT (0x3ff << 6)
+#define MEM_ADDR_DIFF_SFT 0
+#define MEM_ADDR_DIFF_MASK 0x3f
+#define MEM_ADDR_DIFF_MASK_SFT (0x3f << 0)
+
+/* AFE_VUL_CM0_BASE_MSB */
+#define VUL_CM0_BASE_ADDR_MSB_SFT 0
+#define VUL_CM0_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL_CM0_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL_CM0_BASE */
+#define VUL_CM0_BASE_ADDR_SFT 4
+#define VUL_CM0_BASE_ADDR_MASK 0xfffffff
+#define VUL_CM0_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL_CM0_CUR_MSB */
+#define VUL_CM0_CUR_PTR_MSB_SFT 0
+#define VUL_CM0_CUR_PTR_MSB_MASK 0x1ff
+#define VUL_CM0_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL_CM0_CUR */
+#define VUL_CM0_CUR_PTR_SFT 0
+#define VUL_CM0_CUR_PTR_MASK 0xffffffff
+#define VUL_CM0_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL_CM0_END_MSB */
+#define VUL_CM0_END_ADDR_MSB_SFT 0
+#define VUL_CM0_END_ADDR_MSB_MASK 0x1ff
+#define VUL_CM0_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL_CM0_END */
+#define VUL_CM0_END_ADDR_SFT 4
+#define VUL_CM0_END_ADDR_MASK 0xfffffff
+#define VUL_CM0_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL_CM0_CON0 */
+#define VUL_CM0_ON_SFT 28
+#define VUL_CM0_ON_MASK 0x1
+#define VUL_CM0_ON_MASK_SFT (0x1 << 28)
+#define VUL_CM0_REG_CH_SHIFT_MODE_SFT 26
+#define VUL_CM0_REG_CH_SHIFT_MODE_MASK 0x1
+#define VUL_CM0_REG_CH_SHIFT_MODE_MASK_SFT (0x1 << 26)
+#define VUL_CM0_RG_FORCE_NO_MASK_EXTRA_SFT 25
+#define VUL_CM0_RG_FORCE_NO_MASK_EXTRA_MASK 0x1
+#define VUL_CM0_RG_FORCE_NO_MASK_EXTRA_MASK_SFT (0x1 << 25)
+#define VUL_CM0_SW_CLEAR_BUF_FULL_SFT 24
+#define VUL_CM0_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL_CM0_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 24)
+#define VUL_CM0_ULTRA_TH_SFT 20
+#define VUL_CM0_ULTRA_TH_MASK 0xf
+#define VUL_CM0_ULTRA_TH_MASK_SFT (0xf << 20)
+#define VUL_CM0_NORMAL_MODE_SFT 17
+#define VUL_CM0_NORMAL_MODE_MASK 0x1
+#define VUL_CM0_NORMAL_MODE_MASK_SFT (0x1 << 17)
+#define VUL_CM0_ODD_USE_EVEN_SFT 16
+#define VUL_CM0_ODD_USE_EVEN_MASK 0x1
+#define VUL_CM0_ODD_USE_EVEN_MASK_SFT (0x1 << 16)
+#define VUL_CM0_AXI_REQ_MAXLEN_SFT 12
+#define VUL_CM0_AXI_REQ_MAXLEN_MASK 0x3
+#define VUL_CM0_AXI_REQ_MAXLEN_MASK_SFT (0x3 << 12)
+#define VUL_CM0_AXI_REQ_MINLEN_SFT 8
+#define VUL_CM0_AXI_REQ_MINLEN_MASK 0x3
+#define VUL_CM0_AXI_REQ_MINLEN_MASK_SFT (0x3 << 8)
+#define VUL_CM0_HALIGN_SFT 7
+#define VUL_CM0_HALIGN_MASK 0x1
+#define VUL_CM0_HALIGN_MASK_SFT (0x1 << 7)
+#define VUL_CM0_SIGN_EXT_SFT 6
+#define VUL_CM0_SIGN_EXT_MASK 0x1
+#define VUL_CM0_SIGN_EXT_MASK_SFT (0x1 << 6)
+#define VUL_CM0_HD_MODE_SFT 4
+#define VUL_CM0_HD_MODE_MASK 0x3
+#define VUL_CM0_HD_MODE_MASK_SFT (0x3 << 4)
+#define VUL_CM0_MAKE_EXTRA_UPDATE_SFT 3
+#define VUL_CM0_MAKE_EXTRA_UPDATE_MASK 0x1
+#define VUL_CM0_MAKE_EXTRA_UPDATE_MASK_SFT (0x1 << 3)
+#define VUL_CM0_AGENT_FREE_RUN_SFT 2
+#define VUL_CM0_AGENT_FREE_RUN_MASK 0x1
+#define VUL_CM0_AGENT_FREE_RUN_MASK_SFT (0x1 << 2)
+#define VUL_CM0_USE_INT_ODD_SFT 1
+#define VUL_CM0_USE_INT_ODD_MASK 0x1
+#define VUL_CM0_USE_INT_ODD_MASK_SFT (0x1 << 1)
+#define VUL_CM0_INT_ODD_FLAG_SFT 0
+#define VUL_CM0_INT_ODD_FLAG_MASK 0x1
+#define VUL_CM0_INT_ODD_FLAG_MASK_SFT (0x1 << 0)
+
+/* AFE_VUL_CM1_BASE_MSB */
+#define VUL_CM1_BASE_ADDR_MSB_SFT 0
+#define VUL_CM1_BASE_ADDR_MSB_MASK 0x1ff
+#define VUL_CM1_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL_CM1_BASE */
+#define VUL_CM1_BASE_ADDR_SFT 4
+#define VUL_CM1_BASE_ADDR_MASK 0xfffffff
+#define VUL_CM1_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL_CM1_CUR_MSB */
+#define VUL_CM1_CUR_PTR_MSB_SFT 0
+#define VUL_CM1_CUR_PTR_MSB_MASK 0x1ff
+#define VUL_CM1_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL_CM1_CUR */
+#define VUL_CM1_CUR_PTR_SFT 0
+#define VUL_CM1_CUR_PTR_MASK 0xffffffff
+#define VUL_CM1_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL_CM1_END_MSB */
+#define VUL_CM1_END_ADDR_MSB_SFT 0
+#define VUL_CM1_END_ADDR_MSB_MASK 0x1ff
+#define VUL_CM1_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_VUL_CM1_END */
+#define VUL_CM1_END_ADDR_SFT 4
+#define VUL_CM1_END_ADDR_MASK 0xfffffff
+#define VUL_CM1_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_VUL_CM1_CON0 */
+#define VUL_CM1_ON_SFT 28
+#define VUL_CM1_ON_MASK 0x1
+#define VUL_CM1_ON_MASK_SFT (0x1 << 28)
+#define VUL_CM1_REG_CH_SHIFT_MODE_SFT 26
+#define VUL_CM1_REG_CH_SHIFT_MODE_MASK 0x1
+#define VUL_CM1_REG_CH_SHIFT_MODE_MASK_SFT (0x1 << 26)
+#define VUL_CM1_RG_FORCE_NO_MASK_EXTRA_SFT 25
+#define VUL_CM1_RG_FORCE_NO_MASK_EXTRA_MASK 0x1
+#define VUL_CM1_RG_FORCE_NO_MASK_EXTRA_MASK_SFT (0x1 << 25)
+#define VUL_CM1_SW_CLEAR_BUF_FULL_SFT 24
+#define VUL_CM1_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL_CM1_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 24)
+#define VUL_CM1_ULTRA_TH_SFT 20
+#define VUL_CM1_ULTRA_TH_MASK 0xf
+#define VUL_CM1_ULTRA_TH_MASK_SFT (0xf << 20)
+#define VUL_CM1_NORMAL_MODE_SFT 17
+#define VUL_CM1_NORMAL_MODE_MASK 0x1
+#define VUL_CM1_NORMAL_MODE_MASK_SFT (0x1 << 17)
+#define VUL_CM1_ODD_USE_EVEN_SFT 16
+#define VUL_CM1_ODD_USE_EVEN_MASK 0x1
+#define VUL_CM1_ODD_USE_EVEN_MASK_SFT (0x1 << 16)
+#define VUL_CM1_AXI_REQ_MAXLEN_SFT 12
+#define VUL_CM1_AXI_REQ_MAXLEN_MASK 0x3
+#define VUL_CM1_AXI_REQ_MAXLEN_MASK_SFT (0x3 << 12)
+#define VUL_CM1_AXI_REQ_MINLEN_SFT 8
+#define VUL_CM1_AXI_REQ_MINLEN_MASK 0x3
+#define VUL_CM1_AXI_REQ_MINLEN_MASK_SFT (0x3 << 8)
+#define VUL_CM1_HALIGN_SFT 7
+#define VUL_CM1_HALIGN_MASK 0x1
+#define VUL_CM1_HALIGN_MASK_SFT (0x1 << 7)
+#define VUL_CM1_SIGN_EXT_SFT 6
+#define VUL_CM1_SIGN_EXT_MASK 0x1
+#define VUL_CM1_SIGN_EXT_MASK_SFT (0x1 << 6)
+#define VUL_CM1_HD_MODE_SFT 4
+#define VUL_CM1_HD_MODE_MASK 0x3
+#define VUL_CM1_HD_MODE_MASK_SFT (0x3 << 4)
+#define VUL_CM1_MAKE_EXTRA_UPDATE_SFT 3
+#define VUL_CM1_MAKE_EXTRA_UPDATE_MASK 0x1
+#define VUL_CM1_MAKE_EXTRA_UPDATE_MASK_SFT (0x1 << 3)
+#define VUL_CM1_AGENT_FREE_RUN_SFT 2
+#define VUL_CM1_AGENT_FREE_RUN_MASK 0x1
+#define VUL_CM1_AGENT_FREE_RUN_MASK_SFT (0x1 << 2)
+#define VUL_CM1_USE_INT_ODD_SFT 1
+#define VUL_CM1_USE_INT_ODD_MASK 0x1
+#define VUL_CM1_USE_INT_ODD_MASK_SFT (0x1 << 1)
+#define VUL_CM1_INT_ODD_FLAG_SFT 0
+#define VUL_CM1_INT_ODD_FLAG_MASK 0x1
+#define VUL_CM1_INT_ODD_FLAG_MASK_SFT (0x1 << 0)
+
+/* AFE_ETDM_IN0_BASE_MSB */
+#define ETDM_IN0_BASE_ADDR_MSB_SFT 0
+#define ETDM_IN0_BASE_ADDR_MSB_MASK 0x1ff
+#define ETDM_IN0_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_ETDM_IN0_BASE */
+#define ETDM_IN0_BASE_ADDR_SFT 4
+#define ETDM_IN0_BASE_ADDR_MASK 0xfffffff
+#define ETDM_IN0_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_ETDM_IN0_CUR_MSB */
+#define ETDM_IN0_CUR_PTR_MSB_SFT 0
+#define ETDM_IN0_CUR_PTR_MSB_MASK 0x1ff
+#define ETDM_IN0_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_ETDM_IN0_CUR */
+#define ETDM_IN0_CUR_PTR_SFT 0
+#define ETDM_IN0_CUR_PTR_MASK 0xffffffff
+#define ETDM_IN0_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ETDM_IN0_END_MSB */
+#define ETDM_IN0_END_ADDR_MSB_SFT 0
+#define ETDM_IN0_END_ADDR_MSB_MASK 0x1ff
+#define ETDM_IN0_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_ETDM_IN0_END */
+#define ETDM_IN0_END_ADDR_SFT 4
+#define ETDM_IN0_END_ADDR_MASK 0xfffffff
+#define ETDM_IN0_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_ETDM_IN0_CON0 */
+#define ETDM_IN0_CH_NUM_SFT 28
+#define ETDM_IN0_CH_NUM_MASK 0xf
+#define ETDM_IN0_CH_NUM_MASK_SFT (0xf << 28)
+#define ETDM_IN0_ON_SFT 27
+#define ETDM_IN0_ON_MASK 0x1
+#define ETDM_IN0_ON_MASK_SFT (0x1 << 27)
+#define ETDM_IN0_REG_CH_SHIFT_MODE_SFT 26
+#define ETDM_IN0_REG_CH_SHIFT_MODE_MASK 0x1
+#define ETDM_IN0_REG_CH_SHIFT_MODE_MASK_SFT (0x1 << 26)
+#define ETDM_IN0_RG_FORCE_NO_MASK_EXTRA_SFT 25
+#define ETDM_IN0_RG_FORCE_NO_MASK_EXTRA_MASK 0x1
+#define ETDM_IN0_RG_FORCE_NO_MASK_EXTRA_MASK_SFT (0x1 << 25)
+#define ETDM_IN0_SW_CLEAR_BUF_FULL_SFT 24
+#define ETDM_IN0_SW_CLEAR_BUF_FULL_MASK 0x1
+#define ETDM_IN0_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 24)
+#define ETDM_IN0_ULTRA_TH_SFT 20
+#define ETDM_IN0_ULTRA_TH_MASK 0xf
+#define ETDM_IN0_ULTRA_TH_MASK_SFT (0xf << 20)
+#define ETDM_IN0_NORMAL_MODE_SFT 17
+#define ETDM_IN0_NORMAL_MODE_MASK 0x1
+#define ETDM_IN0_NORMAL_MODE_MASK_SFT (0x1 << 17)
+#define ETDM_IN0_ODD_USE_EVEN_SFT 16
+#define ETDM_IN0_ODD_USE_EVEN_MASK 0x1
+#define ETDM_IN0_ODD_USE_EVEN_MASK_SFT (0x1 << 16)
+#define ETDM_IN0_AXI_REQ_MAXLEN_SFT 12
+#define ETDM_IN0_AXI_REQ_MAXLEN_MASK 0x3
+#define ETDM_IN0_AXI_REQ_MAXLEN_MASK_SFT (0x3 << 12)
+#define ETDM_IN0_AXI_REQ_MINLEN_SFT 8
+#define ETDM_IN0_AXI_REQ_MINLEN_MASK 0x3
+#define ETDM_IN0_AXI_REQ_MINLEN_MASK_SFT (0x3 << 8)
+#define ETDM_IN0_HALIGN_SFT 7
+#define ETDM_IN0_HALIGN_MASK 0x1
+#define ETDM_IN0_HALIGN_MASK_SFT (0x1 << 7)
+#define ETDM_IN0_SIGN_EXT_SFT 6
+#define ETDM_IN0_SIGN_EXT_MASK 0x1
+#define ETDM_IN0_SIGN_EXT_MASK_SFT (0x1 << 6)
+#define ETDM_IN0_HD_MODE_SFT 4
+#define ETDM_IN0_HD_MODE_MASK 0x3
+#define ETDM_IN0_HD_MODE_MASK_SFT (0x3 << 4)
+#define ETDM_IN0_MAKE_EXTRA_UPDATE_SFT 3
+#define ETDM_IN0_MAKE_EXTRA_UPDATE_MASK 0x1
+#define ETDM_IN0_MAKE_EXTRA_UPDATE_MASK_SFT (0x1 << 3)
+#define ETDM_IN0_AGENT_FREE_RUN_SFT 2
+#define ETDM_IN0_AGENT_FREE_RUN_MASK 0x1
+#define ETDM_IN0_AGENT_FREE_RUN_MASK_SFT (0x1 << 2)
+#define ETDM_IN0_USE_INT_ODD_SFT 1
+#define ETDM_IN0_USE_INT_ODD_MASK 0x1
+#define ETDM_IN0_USE_INT_ODD_MASK_SFT (0x1 << 1)
+#define ETDM_IN0_INT_ODD_FLAG_SFT 0
+#define ETDM_IN0_INT_ODD_FLAG_MASK 0x1
+#define ETDM_IN0_INT_ODD_FLAG_MASK_SFT (0x1 << 0)
+
+/* AFE_ETDM_IN1_BASE_MSB */
+#define ETDM_IN1_BASE_ADDR_MSB_SFT 0
+#define ETDM_IN1_BASE_ADDR_MSB_MASK 0x1ff
+#define ETDM_IN1_BASE_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_ETDM_IN1_BASE */
+#define ETDM_IN1_BASE_ADDR_SFT 4
+#define ETDM_IN1_BASE_ADDR_MASK 0xfffffff
+#define ETDM_IN1_BASE_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_ETDM_IN1_CUR_MSB */
+#define ETDM_IN1_CUR_PTR_MSB_SFT 0
+#define ETDM_IN1_CUR_PTR_MSB_MASK 0x1ff
+#define ETDM_IN1_CUR_PTR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_ETDM_IN1_CUR */
+#define ETDM_IN1_CUR_PTR_SFT 0
+#define ETDM_IN1_CUR_PTR_MASK 0xffffffff
+#define ETDM_IN1_CUR_PTR_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ETDM_IN1_END_MSB */
+#define ETDM_IN1_END_ADDR_MSB_SFT 0
+#define ETDM_IN1_END_ADDR_MSB_MASK 0x1ff
+#define ETDM_IN1_END_ADDR_MSB_MASK_SFT (0x1ff << 0)
+
+/* AFE_ETDM_IN1_END */
+#define ETDM_IN1_END_ADDR_SFT 4
+#define ETDM_IN1_END_ADDR_MASK 0xfffffff
+#define ETDM_IN1_END_ADDR_MASK_SFT (0xfffffff << 4)
+
+/* AFE_ETDM_IN1_CON0 */
+#define ETDM_IN1_CH_NUM_SFT 28
+#define ETDM_IN1_CH_NUM_MASK 0xf
+#define ETDM_IN1_CH_NUM_MASK_SFT (0xf << 28)
+#define ETDM_IN1_ON_SFT 27
+#define ETDM_IN1_ON_MASK 0x1
+#define ETDM_IN1_ON_MASK_SFT (0x1 << 27)
+#define ETDM_IN1_REG_CH_SHIFT_MODE_SFT 26
+#define ETDM_IN1_REG_CH_SHIFT_MODE_MASK 0x1
+#define ETDM_IN1_REG_CH_SHIFT_MODE_MASK_SFT (0x1 << 26)
+#define ETDM_IN1_RG_FORCE_NO_MASK_EXTRA_SFT 25
+#define ETDM_IN1_RG_FORCE_NO_MASK_EXTRA_MASK 0x1
+#define ETDM_IN1_RG_FORCE_NO_MASK_EXTRA_MASK_SFT (0x1 << 25)
+#define ETDM_IN1_SW_CLEAR_BUF_FULL_SFT 24
+#define ETDM_IN1_SW_CLEAR_BUF_FULL_MASK 0x1
+#define ETDM_IN1_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 24)
+#define ETDM_IN1_ULTRA_TH_SFT 20
+#define ETDM_IN1_ULTRA_TH_MASK 0xf
+#define ETDM_IN1_ULTRA_TH_MASK_SFT (0xf << 20)
+#define ETDM_IN1_NORMAL_MODE_SFT 17
+#define ETDM_IN1_NORMAL_MODE_MASK 0x1
+#define ETDM_IN1_NORMAL_MODE_MASK_SFT (0x1 << 17)
+#define ETDM_IN1_ODD_USE_EVEN_SFT 16
+#define ETDM_IN1_ODD_USE_EVEN_MASK 0x1
+#define ETDM_IN1_ODD_USE_EVEN_MASK_SFT (0x1 << 16)
+#define ETDM_IN1_AXI_REQ_MAXLEN_SFT 12
+#define ETDM_IN1_AXI_REQ_MAXLEN_MASK 0x3
+#define ETDM_IN1_AXI_REQ_MAXLEN_MASK_SFT (0x3 << 12)
+#define ETDM_IN1_AXI_REQ_MINLEN_SFT 8
+#define ETDM_IN1_AXI_REQ_MINLEN_MASK 0x3
+#define ETDM_IN1_AXI_REQ_MINLEN_MASK_SFT (0x3 << 8)
+#define ETDM_IN1_HALIGN_SFT 7
+#define ETDM_IN1_HALIGN_MASK 0x1
+#define ETDM_IN1_HALIGN_MASK_SFT (0x1 << 7)
+#define ETDM_IN1_SIGN_EXT_SFT 6
+#define ETDM_IN1_SIGN_EXT_MASK 0x1
+#define ETDM_IN1_SIGN_EXT_MASK_SFT (0x1 << 6)
+#define ETDM_IN1_HD_MODE_SFT 4
+#define ETDM_IN1_HD_MODE_MASK 0x3
+#define ETDM_IN1_HD_MODE_MASK_SFT (0x3 << 4)
+#define ETDM_IN1_MAKE_EXTRA_UPDATE_SFT 3
+#define ETDM_IN1_MAKE_EXTRA_UPDATE_MASK 0x1
+#define ETDM_IN1_MAKE_EXTRA_UPDATE_MASK_SFT (0x1 << 3)
+#define ETDM_IN1_AGENT_FREE_RUN_SFT 2
+#define ETDM_IN1_AGENT_FREE_RUN_MASK 0x1
+#define ETDM_IN1_AGENT_FREE_RUN_MASK_SFT (0x1 << 2)
+#define ETDM_IN1_USE_INT_ODD_SFT 1
+#define ETDM_IN1_USE_INT_ODD_MASK 0x1
+#define ETDM_IN1_USE_INT_ODD_MASK_SFT (0x1 << 1)
+#define ETDM_IN1_INT_ODD_FLAG_SFT 0
+#define ETDM_IN1_INT_ODD_FLAG_MASK 0x1
+#define ETDM_IN1_INT_ODD_FLAG_MASK_SFT (0x1 << 0)
+
+/* AFE_VUL24_RCH_MON */
+#define VUL24_RCH_DATA_SFT 0
+#define VUL24_RCH_DATA_MASK 0xffffffff
+#define VUL24_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL24_LCH_MON */
+#define VUL24_LCH_DATA_SFT 0
+#define VUL24_LCH_DATA_MASK 0xffffffff
+#define VUL24_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL25_RCH_MON */
+#define VUL25_RCH_DATA_SFT 0
+#define VUL25_RCH_DATA_MASK 0xffffffff
+#define VUL25_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL25_LCH_MON */
+#define VUL25_LCH_DATA_SFT 0
+#define VUL25_LCH_DATA_MASK 0xffffffff
+#define VUL25_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL_CM0_RCH_MON */
+#define VUL_CM0_RCH_DATA_SFT 0
+#define VUL_CM0_RCH_DATA_MASK 0xffffffff
+#define VUL_CM0_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL_CM0_LCH_MON */
+#define VUL_CM0_LCH_DATA_SFT 0
+#define VUL_CM0_LCH_DATA_MASK 0xffffffff
+#define VUL_CM0_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL_CM1_RCH_MON */
+#define VUL_CM1_RCH_DATA_SFT 0
+#define VUL_CM1_RCH_DATA_MASK 0xffffffff
+#define VUL_CM1_RCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_VUL_CM1_LCH_MON */
+#define VUL_CM1_LCH_DATA_SFT 0
+#define VUL_CM1_LCH_DATA_MASK 0xffffffff
+#define VUL_CM1_LCH_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH0_MON */
+#define DL_24CH_CH0_DATA_SFT 0
+#define DL_24CH_CH0_DATA_MASK 0xffffffff
+#define DL_24CH_CH0_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH1_MON */
+#define DL_24CH_CH1_DATA_SFT 0
+#define DL_24CH_CH1_DATA_MASK 0xffffffff
+#define DL_24CH_CH1_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH2_MON */
+#define DL_24CH_CH2_DATA_SFT 0
+#define DL_24CH_CH2_DATA_MASK 0xffffffff
+#define DL_24CH_CH2_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH3_MON */
+#define DL_24CH_CH3_DATA_SFT 0
+#define DL_24CH_CH3_DATA_MASK 0xffffffff
+#define DL_24CH_CH3_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH4_MON */
+#define DL_24CH_CH4_DATA_SFT 0
+#define DL_24CH_CH4_DATA_MASK 0xffffffff
+#define DL_24CH_CH4_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH5_MON */
+#define DL_24CH_CH5_DATA_SFT 0
+#define DL_24CH_CH5_DATA_MASK 0xffffffff
+#define DL_24CH_CH5_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH6_MON */
+#define DL_24CH_CH6_DATA_SFT 0
+#define DL_24CH_CH6_DATA_MASK 0xffffffff
+#define DL_24CH_CH6_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DL_24CH_CH7_MON */
+#define DL_24CH_CH7_DATA_SFT 0
+#define DL_24CH_CH7_DATA_MASK 0xffffffff
+#define DL_24CH_CH7_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SRAM_BOUND */
+#define SECURE_BIT_SFT 19
+#define SECURE_BIT_MASK 0x1
+#define SECURE_BIT_MASK_SFT (0x1 << 19)
+#define SECURE_SRAM_BOUND_SFT 0
+#define SECURE_SRAM_BOUND_MASK 0x7ffff
+#define SECURE_SRAM_BOUND_MASK_SFT (0x7ffff << 0)
+
+/* AFE_SECURE_CON0 */
+#define READ_EN15_NS_SFT 31
+#define READ_EN15_NS_MASK 0x1
+#define READ_EN15_NS_MASK_SFT (0x1 << 31)
+#define WRITE_EN15_NS_SFT 30
+#define WRITE_EN15_NS_MASK 0x1
+#define WRITE_EN15_NS_MASK_SFT (0x1 << 30)
+#define READ_EN14_NS_SFT 29
+#define READ_EN14_NS_MASK 0x1
+#define READ_EN14_NS_MASK_SFT (0x1 << 29)
+#define WRITE_EN14_NS_SFT 28
+#define WRITE_EN14_NS_MASK 0x1
+#define WRITE_EN14_NS_MASK_SFT (0x1 << 28)
+#define READ_EN13_NS_SFT 27
+#define READ_EN13_NS_MASK 0x1
+#define READ_EN13_NS_MASK_SFT (0x1 << 27)
+#define WRITE_EN13_NS_SFT 26
+#define WRITE_EN13_NS_MASK 0x1
+#define WRITE_EN13_NS_MASK_SFT (0x1 << 26)
+#define READ_EN12_NS_SFT 25
+#define READ_EN12_NS_MASK 0x1
+#define READ_EN12_NS_MASK_SFT (0x1 << 25)
+#define WRITE_EN12_NS_SFT 24
+#define WRITE_EN12_NS_MASK 0x1
+#define WRITE_EN12_NS_MASK_SFT (0x1 << 24)
+#define READ_EN11_NS_SFT 23
+#define READ_EN11_NS_MASK 0x1
+#define READ_EN11_NS_MASK_SFT (0x1 << 23)
+#define WRITE_EN11_NS_SFT 22
+#define WRITE_EN11_NS_MASK 0x1
+#define WRITE_EN11_NS_MASK_SFT (0x1 << 22)
+#define READ_EN10_NS_SFT 21
+#define READ_EN10_NS_MASK 0x1
+#define READ_EN10_NS_MASK_SFT (0x1 << 21)
+#define WRITE_EN10_NS_SFT 20
+#define WRITE_EN10_NS_MASK 0x1
+#define WRITE_EN10_NS_MASK_SFT (0x1 << 20)
+#define READ_EN9_NS_SFT 19
+#define READ_EN9_NS_MASK 0x1
+#define READ_EN9_NS_MASK_SFT (0x1 << 19)
+#define WRITE_EN9_NS_SFT 18
+#define WRITE_EN9_NS_MASK 0x1
+#define WRITE_EN9_NS_MASK_SFT (0x1 << 18)
+#define READ_EN8_NS_SFT 17
+#define READ_EN8_NS_MASK 0x1
+#define READ_EN8_NS_MASK_SFT (0x1 << 17)
+#define WRITE_EN8_NS_SFT 16
+#define WRITE_EN8_NS_MASK 0x1
+#define WRITE_EN8_NS_MASK_SFT (0x1 << 16)
+#define READ_EN7_NS_SFT 15
+#define READ_EN7_NS_MASK 0x1
+#define READ_EN7_NS_MASK_SFT (0x1 << 15)
+#define WRITE_EN7_NS_SFT 14
+#define WRITE_EN7_NS_MASK 0x1
+#define WRITE_EN7_NS_MASK_SFT (0x1 << 14)
+#define READ_EN6_NS_SFT 13
+#define READ_EN6_NS_MASK 0x1
+#define READ_EN6_NS_MASK_SFT (0x1 << 13)
+#define WRITE_EN6_NS_SFT 12
+#define WRITE_EN6_NS_MASK 0x1
+#define WRITE_EN6_NS_MASK_SFT (0x1 << 12)
+#define READ_EN5_NS_SFT 11
+#define READ_EN5_NS_MASK 0x1
+#define READ_EN5_NS_MASK_SFT (0x1 << 11)
+#define WRITE_EN5_NS_SFT 10
+#define WRITE_EN5_NS_MASK 0x1
+#define WRITE_EN5_NS_MASK_SFT (0x1 << 10)
+#define READ_EN4_NS_SFT 9
+#define READ_EN4_NS_MASK 0x1
+#define READ_EN4_NS_MASK_SFT (0x1 << 9)
+#define WRITE_EN4_NS_SFT 8
+#define WRITE_EN4_NS_MASK 0x1
+#define WRITE_EN4_NS_MASK_SFT (0x1 << 8)
+#define READ_EN3_NS_SFT 7
+#define READ_EN3_NS_MASK 0x1
+#define READ_EN3_NS_MASK_SFT (0x1 << 7)
+#define WRITE_EN3_NS_SFT 6
+#define WRITE_EN3_NS_MASK 0x1
+#define WRITE_EN3_NS_MASK_SFT (0x1 << 6)
+#define READ_EN2_NS_SFT 5
+#define READ_EN2_NS_MASK 0x1
+#define READ_EN2_NS_MASK_SFT (0x1 << 5)
+#define WRITE_EN2_NS_SFT 4
+#define WRITE_EN2_NS_MASK 0x1
+#define WRITE_EN2_NS_MASK_SFT (0x1 << 4)
+#define READ_EN1_NS_SFT 3
+#define READ_EN1_NS_MASK 0x1
+#define READ_EN1_NS_MASK_SFT (0x1 << 3)
+#define WRITE_EN1_NS_SFT 2
+#define WRITE_EN1_NS_MASK 0x1
+#define WRITE_EN1_NS_MASK_SFT (0x1 << 2)
+#define READ_EN0_NS_SFT 1
+#define READ_EN0_NS_MASK 0x1
+#define READ_EN0_NS_MASK_SFT (0x1 << 1)
+#define WRITE_EN0_NS_SFT 0
+#define WRITE_EN0_NS_MASK 0x1
+#define WRITE_EN0_NS_MASK_SFT (0x1 << 0)
+
+/* AFE_SECURE_CON1 */
+#define READ_EN15_S_SFT 31
+#define READ_EN15_S_MASK 0x1
+#define READ_EN15_S_MASK_SFT (0x1 << 31)
+#define WRITE_EN15_S_SFT 30
+#define WRITE_EN15_S_MASK 0x1
+#define WRITE_EN15_S_MASK_SFT (0x1 << 30)
+#define READ_EN14_S_SFT 29
+#define READ_EN14_S_MASK 0x1
+#define READ_EN14_S_MASK_SFT (0x1 << 29)
+#define WRITE_EN14_S_SFT 28
+#define WRITE_EN14_S_MASK 0x1
+#define WRITE_EN14_S_MASK_SFT (0x1 << 28)
+#define READ_EN13_S_SFT 27
+#define READ_EN13_S_MASK 0x1
+#define READ_EN13_S_MASK_SFT (0x1 << 27)
+#define WRITE_EN13_S_SFT 26
+#define WRITE_EN13_S_MASK 0x1
+#define WRITE_EN13_S_MASK_SFT (0x1 << 26)
+#define READ_EN12_S_SFT 25
+#define READ_EN12_S_MASK 0x1
+#define READ_EN12_S_MASK_SFT (0x1 << 25)
+#define WRITE_EN12_S_SFT 24
+#define WRITE_EN12_S_MASK 0x1
+#define WRITE_EN12_S_MASK_SFT (0x1 << 24)
+#define READ_EN11_S_SFT 23
+#define READ_EN11_S_MASK 0x1
+#define READ_EN11_S_MASK_SFT (0x1 << 23)
+#define WRITE_EN11_S_SFT 22
+#define WRITE_EN11_S_MASK 0x1
+#define WRITE_EN11_S_MASK_SFT (0x1 << 22)
+#define READ_EN10_S_SFT 21
+#define READ_EN10_S_MASK 0x1
+#define READ_EN10_S_MASK_SFT (0x1 << 21)
+#define WRITE_EN10_S_SFT 20
+#define WRITE_EN10_S_MASK 0x1
+#define WRITE_EN10_S_MASK_SFT (0x1 << 20)
+#define READ_EN9_S_SFT 19
+#define READ_EN9_S_MASK 0x1
+#define READ_EN9_S_MASK_SFT (0x1 << 19)
+#define WRITE_EN9_S_SFT 18
+#define WRITE_EN9_S_MASK 0x1
+#define WRITE_EN9_S_MASK_SFT (0x1 << 18)
+#define READ_EN8_S_SFT 17
+#define READ_EN8_S_MASK 0x1
+#define READ_EN8_S_MASK_SFT (0x1 << 17)
+#define WRITE_EN8_S_SFT 16
+#define WRITE_EN8_S_MASK 0x1
+#define WRITE_EN8_S_MASK_SFT (0x1 << 16)
+#define READ_EN7_S_SFT 15
+#define READ_EN7_S_MASK 0x1
+#define READ_EN7_S_MASK_SFT (0x1 << 15)
+#define WRITE_EN7_S_SFT 14
+#define WRITE_EN7_S_MASK 0x1
+#define WRITE_EN7_S_MASK_SFT (0x1 << 14)
+#define READ_EN6_S_SFT 13
+#define READ_EN6_S_MASK 0x1
+#define READ_EN6_S_MASK_SFT (0x1 << 13)
+#define WRITE_EN6_S_SFT 12
+#define WRITE_EN6_S_MASK 0x1
+#define WRITE_EN6_S_MASK_SFT (0x1 << 12)
+#define READ_EN5_S_SFT 11
+#define READ_EN5_S_MASK 0x1
+#define READ_EN5_S_MASK_SFT (0x1 << 11)
+#define WRITE_EN5_S_SFT 10
+#define WRITE_EN5_S_MASK 0x1
+#define WRITE_EN5_S_MASK_SFT (0x1 << 10)
+#define READ_EN4_S_SFT 9
+#define READ_EN4_S_MASK 0x1
+#define READ_EN4_S_MASK_SFT (0x1 << 9)
+#define WRITE_EN4_S_SFT 8
+#define WRITE_EN4_S_MASK 0x1
+#define WRITE_EN4_S_MASK_SFT (0x1 << 8)
+#define READ_EN3_S_SFT 7
+#define READ_EN3_S_MASK 0x1
+#define READ_EN3_S_MASK_SFT (0x1 << 7)
+#define WRITE_EN3_S_SFT 6
+#define WRITE_EN3_S_MASK 0x1
+#define WRITE_EN3_S_MASK_SFT (0x1 << 6)
+#define READ_EN2_S_SFT 5
+#define READ_EN2_S_MASK 0x1
+#define READ_EN2_S_MASK_SFT (0x1 << 5)
+#define WRITE_EN2_S_SFT 4
+#define WRITE_EN2_S_MASK 0x1
+#define WRITE_EN2_S_MASK_SFT (0x1 << 4)
+#define READ_EN1_S_SFT 3
+#define READ_EN1_S_MASK 0x1
+#define READ_EN1_S_MASK_SFT (0x1 << 3)
+#define WRITE_EN1_S_SFT 2
+#define WRITE_EN1_S_MASK 0x1
+#define WRITE_EN1_S_MASK_SFT (0x1 << 2)
+#define READ_EN0_S_SFT 1
+#define READ_EN0_S_MASK 0x1
+#define READ_EN0_S_MASK_SFT (0x1 << 1)
+#define WRITE_EN0_S_SFT 0
+#define WRITE_EN0_S_MASK 0x1
+#define WRITE_EN0_S_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_SECURE_CON0 */
+#define AFE_HDMI_SE_SECURE_BIT_SFT 11
+#define AFE_HDMI_SE_SECURE_BIT_MASK 0x1
+#define AFE_HDMI_SE_SECURE_BIT_MASK_SFT (0x1 << 11)
+#define AFE_SPDIF2_OUT_SE_SECURE_BIT_SFT 10
+#define AFE_SPDIF2_OUT_SE_SECURE_BIT_MASK 0x1
+#define AFE_SPDIF2_OUT_SE_SECURE_BIT_MASK_SFT (0x1 << 10)
+#define AFE_SPDIF_OUT_SE_SECURE_BIT_SFT 9
+#define AFE_SPDIF_OUT_SE_SECURE_BIT_MASK 0x1
+#define AFE_SPDIF_OUT_SE_SECURE_BIT_MASK_SFT (0x1 << 9)
+#define AFE_DL8_SE_SECURE_BIT_SFT 8
+#define AFE_DL8_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL8_SE_SECURE_BIT_MASK_SFT (0x1 << 8)
+#define AFE_DL7_SE_SECURE_BIT_SFT 7
+#define AFE_DL7_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL7_SE_SECURE_BIT_MASK_SFT (0x1 << 7)
+#define AFE_DL6_SE_SECURE_BIT_SFT 6
+#define AFE_DL6_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL6_SE_SECURE_BIT_MASK_SFT (0x1 << 6)
+#define AFE_DL5_SE_SECURE_BIT_SFT 5
+#define AFE_DL5_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL5_SE_SECURE_BIT_MASK_SFT (0x1 << 5)
+#define AFE_DL4_SE_SECURE_BIT_SFT 4
+#define AFE_DL4_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL4_SE_SECURE_BIT_MASK_SFT (0x1 << 4)
+#define AFE_DL3_SE_SECURE_BIT_SFT 3
+#define AFE_DL3_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL3_SE_SECURE_BIT_MASK_SFT (0x1 << 3)
+#define AFE_DL2_SE_SECURE_BIT_SFT 2
+#define AFE_DL2_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL2_SE_SECURE_BIT_MASK_SFT (0x1 << 2)
+#define AFE_DL1_SE_SECURE_BIT_SFT 1
+#define AFE_DL1_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL1_SE_SECURE_BIT_MASK_SFT (0x1 << 1)
+#define AFE_DL0_SE_SECURE_BIT_SFT 0
+#define AFE_DL0_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL0_SE_SECURE_BIT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_SECURE_CON1 */
+#define AFE_DL46_SE_SECURE_BIT_SFT 26
+#define AFE_DL46_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL46_SE_SECURE_BIT_MASK_SFT (0x1 << 26)
+#define AFE_DL45_SE_SECURE_BIT_SFT 25
+#define AFE_DL45_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL45_SE_SECURE_BIT_MASK_SFT (0x1 << 25)
+#define AFE_DL44_SE_SECURE_BIT_SFT 24
+#define AFE_DL44_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL44_SE_SECURE_BIT_MASK_SFT (0x1 << 24)
+#define AFE_DL43_SE_SECURE_BIT_SFT 23
+#define AFE_DL43_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL43_SE_SECURE_BIT_MASK_SFT (0x1 << 23)
+#define AFE_DL42_SE_SECURE_BIT_SFT 22
+#define AFE_DL42_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL42_SE_SECURE_BIT_MASK_SFT (0x1 << 22)
+#define AFE_DL41_SE_SECURE_BIT_SFT 21
+#define AFE_DL41_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL41_SE_SECURE_BIT_MASK_SFT (0x1 << 21)
+#define AFE_DL40_SE_SECURE_BIT_SFT 20
+#define AFE_DL40_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL40_SE_SECURE_BIT_MASK_SFT (0x1 << 20)
+#define AFE_DL39_SE_SECURE_BIT_SFT 19
+#define AFE_DL39_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL39_SE_SECURE_BIT_MASK_SFT (0x1 << 19)
+#define AFE_DL38_SE_SECURE_BIT_SFT 18
+#define AFE_DL38_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL38_SE_SECURE_BIT_MASK_SFT (0x1 << 18)
+#define AFE_DL37_SE_SECURE_BIT_SFT 17
+#define AFE_DL37_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL37_SE_SECURE_BIT_MASK_SFT (0x1 << 17)
+#define AFE_DL36_SE_SECURE_BIT_SFT 16
+#define AFE_DL36_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL36_SE_SECURE_BIT_MASK_SFT (0x1 << 16)
+#define AFE_DL35_SE_SECURE_BIT_SFT 15
+#define AFE_DL35_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL35_SE_SECURE_BIT_MASK_SFT (0x1 << 15)
+#define AFE_DL34_SE_SECURE_BIT_SFT 14
+#define AFE_DL34_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL34_SE_SECURE_BIT_MASK_SFT (0x1 << 14)
+#define AFE_DL33_SE_SECURE_BIT_SFT 13
+#define AFE_DL33_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL33_SE_SECURE_BIT_MASK_SFT (0x1 << 13)
+#define AFE_DL32_SE_SECURE_BIT_SFT 12
+#define AFE_DL32_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL32_SE_SECURE_BIT_MASK_SFT (0x1 << 12)
+#define AFE_DL31_SE_SECURE_BIT_SFT 11
+#define AFE_DL31_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL31_SE_SECURE_BIT_MASK_SFT (0x1 << 11)
+#define AFE_DL30_SE_SECURE_BIT_SFT 10
+#define AFE_DL30_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL30_SE_SECURE_BIT_MASK_SFT (0x1 << 10)
+#define AFE_DL29_SE_SECURE_BIT_SFT 9
+#define AFE_DL29_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL29_SE_SECURE_BIT_MASK_SFT (0x1 << 9)
+#define AFE_DL28_SE_SECURE_BIT_SFT 8
+#define AFE_DL28_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL28_SE_SECURE_BIT_MASK_SFT (0x1 << 8)
+#define AFE_DL27_SE_SECURE_BIT_SFT 7
+#define AFE_DL27_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL27_SE_SECURE_BIT_MASK_SFT (0x1 << 7)
+#define AFE_DL26_SE_SECURE_BIT_SFT 6
+#define AFE_DL26_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL26_SE_SECURE_BIT_MASK_SFT (0x1 << 6)
+#define AFE_DL25_SE_SECURE_BIT_SFT 5
+#define AFE_DL25_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL25_SE_SECURE_BIT_MASK_SFT (0x1 << 5)
+#define AFE_DL24_SE_SECURE_BIT_SFT 4
+#define AFE_DL24_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL24_SE_SECURE_BIT_MASK_SFT (0x1 << 4)
+#define AFE_DL23_SE_SECURE_BIT_SFT 3
+#define AFE_DL23_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL23_SE_SECURE_BIT_MASK_SFT (0x1 << 3)
+#define AFE_DL_48CH_SE_SECURE_BIT_SFT 2
+#define AFE_DL_48CH_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL_48CH_SE_SECURE_BIT_MASK_SFT (0x1 << 2)
+#define AFE_DL_24CH_SE_SECURE_BIT_SFT 1
+#define AFE_DL_24CH_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL_24CH_SE_SECURE_BIT_MASK_SFT (0x1 << 1)
+#define AFE_DL_4CH_SE_SECURE_BIT_SFT 0
+#define AFE_DL_4CH_SE_SECURE_BIT_MASK 0x1
+#define AFE_DL_4CH_SE_SECURE_BIT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_SECURE_CON2 */
+#define AFE_VUL38_SE_SECURE_BIT_SFT 28
+#define AFE_VUL38_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL38_SE_SECURE_BIT_MASK_SFT (0x1 << 28)
+#define AFE_VUL37_SE_SECURE_BIT_SFT 27
+#define AFE_VUL37_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL37_SE_SECURE_BIT_MASK_SFT (0x1 << 27)
+#define AFE_VUL36_SE_SECURE_BIT_SFT 26
+#define AFE_VUL36_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL36_SE_SECURE_BIT_MASK_SFT (0x1 << 26)
+#define AFE_VUL35_SE_SECURE_BIT_SFT 25
+#define AFE_VUL35_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL35_SE_SECURE_BIT_MASK_SFT (0x1 << 25)
+#define AFE_VUL34_SE_SECURE_BIT_SFT 24
+#define AFE_VUL34_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL34_SE_SECURE_BIT_MASK_SFT (0x1 << 24)
+#define AFE_VUL33_SE_SECURE_BIT_SFT 23
+#define AFE_VUL33_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL33_SE_SECURE_BIT_MASK_SFT (0x1 << 23)
+#define AFE_VUL32_SE_SECURE_BIT_SFT 22
+#define AFE_VUL32_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL32_SE_SECURE_BIT_MASK_SFT (0x1 << 22)
+#define AFE_VUL31_SE_SECURE_BIT_SFT 21
+#define AFE_VUL31_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL31_SE_SECURE_BIT_MASK_SFT (0x1 << 21)
+#define AFE_VUL30_SE_SECURE_BIT_SFT 20
+#define AFE_VUL30_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL30_SE_SECURE_BIT_MASK_SFT (0x1 << 20)
+#define AFE_VUL29_SE_SECURE_BIT_SFT 19
+#define AFE_VUL29_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL29_SE_SECURE_BIT_MASK_SFT (0x1 << 19)
+#define AFE_VUL28_SE_SECURE_BIT_SFT 18
+#define AFE_VUL28_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL28_SE_SECURE_BIT_MASK_SFT (0x1 << 18)
+#define AFE_VUL27_SE_SECURE_BIT_SFT 17
+#define AFE_VUL27_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL27_SE_SECURE_BIT_MASK_SFT (0x1 << 17)
+#define AFE_VUL26_SE_SECURE_BIT_SFT 16
+#define AFE_VUL26_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL26_SE_SECURE_BIT_MASK_SFT (0x1 << 16)
+#define AFE_VUL25_SE_SECURE_BIT_SFT 15
+#define AFE_VUL25_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL25_SE_SECURE_BIT_MASK_SFT (0x1 << 15)
+#define AFE_VUL24_SE_SECURE_BIT_SFT 14
+#define AFE_VUL24_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL24_SE_SECURE_BIT_MASK_SFT (0x1 << 14)
+#define AFE_VUL_CM2_SE_SECURE_BIT_SFT 13
+#define AFE_VUL_CM2_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL_CM2_SE_SECURE_BIT_MASK_SFT (0x1 << 13)
+#define AFE_VUL_CM1_SE_SECURE_BIT_SFT 12
+#define AFE_VUL_CM1_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL_CM1_SE_SECURE_BIT_MASK_SFT (0x1 << 12)
+#define AFE_VUL_CM0_SE_SECURE_BIT_SFT 11
+#define AFE_VUL_CM0_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL_CM0_SE_SECURE_BIT_MASK_SFT (0x1 << 11)
+#define AFE_VUL10_SE_SECURE_BIT_SFT 10
+#define AFE_VUL10_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL10_SE_SECURE_BIT_MASK_SFT (0x1 << 10)
+#define AFE_VUL9_SE_SECURE_BIT_SFT 9
+#define AFE_VUL9_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL9_SE_SECURE_BIT_MASK_SFT (0x1 << 9)
+#define AFE_VUL8_SE_SECURE_BIT_SFT 8
+#define AFE_VUL8_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL8_SE_SECURE_BIT_MASK_SFT (0x1 << 8)
+#define AFE_VUL7_SE_SECURE_BIT_SFT 7
+#define AFE_VUL7_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL7_SE_SECURE_BIT_MASK_SFT (0x1 << 7)
+#define AFE_VUL6_SE_SECURE_BIT_SFT 6
+#define AFE_VUL6_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL6_SE_SECURE_BIT_MASK_SFT (0x1 << 6)
+#define AFE_VUL5_SE_SECURE_BIT_SFT 5
+#define AFE_VUL5_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL5_SE_SECURE_BIT_MASK_SFT (0x1 << 5)
+#define AFE_VUL4_SE_SECURE_BIT_SFT 4
+#define AFE_VUL4_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL4_SE_SECURE_BIT_MASK_SFT (0x1 << 4)
+#define AFE_VUL3_SE_SECURE_BIT_SFT 3
+#define AFE_VUL3_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL3_SE_SECURE_BIT_MASK_SFT (0x1 << 3)
+#define AFE_VUL2_SE_SECURE_BIT_SFT 2
+#define AFE_VUL2_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL2_SE_SECURE_BIT_MASK_SFT (0x1 << 2)
+#define AFE_VUL1_SE_SECURE_BIT_SFT 1
+#define AFE_VUL1_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL1_SE_SECURE_BIT_MASK_SFT (0x1 << 1)
+#define AFE_VUL0_SE_SECURE_BIT_SFT 0
+#define AFE_VUL0_SE_SECURE_BIT_MASK 0x1
+#define AFE_VUL0_SE_SECURE_BIT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_SECURE_CON3 */
+#define AFE_SPDIFIN_SE_SECURE_BIT_SFT 10
+#define AFE_SPDIFIN_SE_SECURE_BIT_MASK 0x1
+#define AFE_SPDIFIN_SE_SECURE_BIT_MASK_SFT (0x1 << 10)
+#define AFE_TDM_IN_SE_SECURE_BIT_SFT 9
+#define AFE_TDM_IN_SE_SECURE_BIT_MASK 0x1
+#define AFE_TDM_IN_SE_SECURE_BIT_MASK_SFT (0x1 << 9)
+#define AFE_MPHONE_EARC_SE_SECURE_BIT_SFT 8
+#define AFE_MPHONE_EARC_SE_SECURE_BIT_MASK 0x1
+#define AFE_MPHONE_EARC_SE_SECURE_BIT_MASK_SFT (0x1 << 8)
+#define AFE_MPHONE_SPDIF_SE_SECURE_BIT_SFT 7
+#define AFE_MPHONE_SPDIF_SE_SECURE_BIT_MASK 0x1
+#define AFE_MPHONE_SPDIF_SE_SECURE_BIT_MASK_SFT (0x1 << 7)
+#define AFE_ETDM_IN1_SE_SECURE_BIT_SFT 1
+#define AFE_ETDM_IN1_SE_SECURE_BIT_MASK 0x1
+#define AFE_ETDM_IN1_SE_SECURE_BIT_MASK_SFT (0x1 << 1)
+#define AFE_ETDM_IN0_SE_SECURE_BIT_SFT 0
+#define AFE_ETDM_IN0_SE_SECURE_BIT_MASK 0x1
+#define AFE_ETDM_IN0_SE_SECURE_BIT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_PROT_SIDEBAND0 */
+#define HDMI_HPROT_SFT 11
+#define HDMI_HPROT_MASK 0x1
+#define HDMI_HPROT_MASK_SFT (0x1 << 11)
+#define SPDIF2_OUT_HPROT_SFT 10
+#define SPDIF2_OUT_HPROT_MASK 0x1
+#define SPDIF2_OUT_HPROT_MASK_SFT (0x1 << 10)
+#define SPDIF_OUT_HPROT_SFT 9
+#define SPDIF_OUT_HPROT_MASK 0x1
+#define SPDIF_OUT_HPROT_MASK_SFT (0x1 << 9)
+#define DL8_HPROT_SFT 8
+#define DL8_HPROT_MASK 0x1
+#define DL8_HPROT_MASK_SFT (0x1 << 8)
+#define DL7_HPROT_SFT 7
+#define DL7_HPROT_MASK 0x1
+#define DL7_HPROT_MASK_SFT (0x1 << 7)
+#define DL6_HPROT_SFT 6
+#define DL6_HPROT_MASK 0x1
+#define DL6_HPROT_MASK_SFT (0x1 << 6)
+#define DL5_HPROT_SFT 5
+#define DL5_HPROT_MASK 0x1
+#define DL5_HPROT_MASK_SFT (0x1 << 5)
+#define DL4_HPROT_SFT 4
+#define DL4_HPROT_MASK 0x1
+#define DL4_HPROT_MASK_SFT (0x1 << 4)
+#define DL3_HPROT_SFT 3
+#define DL3_HPROT_MASK 0x1
+#define DL3_HPROT_MASK_SFT (0x1 << 3)
+#define DL2_HPROT_SFT 2
+#define DL2_HPROT_MASK 0x1
+#define DL2_HPROT_MASK_SFT (0x1 << 2)
+#define DL1_HPROT_SFT 1
+#define DL1_HPROT_MASK 0x1
+#define DL1_HPROT_MASK_SFT (0x1 << 1)
+#define DL0_HPROT_SFT 0
+#define DL0_HPROT_MASK 0x1
+#define DL0_HPROT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_PROT_SIDEBAND1 */
+#define DL46_HPROT_SFT 26
+#define DL46_HPROT_MASK 0x1
+#define DL46_HPROT_MASK_SFT (0x1 << 26)
+#define DL45_HPROT_SFT 25
+#define DL45_HPROT_MASK 0x1
+#define DL45_HPROT_MASK_SFT (0x1 << 25)
+#define DL44_HPROT_SFT 24
+#define DL44_HPROT_MASK 0x1
+#define DL44_HPROT_MASK_SFT (0x1 << 24)
+#define DL43_HPROT_SFT 23
+#define DL43_HPROT_MASK 0x1
+#define DL43_HPROT_MASK_SFT (0x1 << 23)
+#define DL42_HPROT_SFT 22
+#define DL42_HPROT_MASK 0x1
+#define DL42_HPROT_MASK_SFT (0x1 << 22)
+#define DL41_HPROT_SFT 21
+#define DL41_HPROT_MASK 0x1
+#define DL41_HPROT_MASK_SFT (0x1 << 21)
+#define DL40_HPROT_SFT 20
+#define DL40_HPROT_MASK 0x1
+#define DL40_HPROT_MASK_SFT (0x1 << 20)
+#define DL39_HPROT_SFT 19
+#define DL39_HPROT_MASK 0x1
+#define DL39_HPROT_MASK_SFT (0x1 << 19)
+#define DL38_HPROT_SFT 18
+#define DL38_HPROT_MASK 0x1
+#define DL38_HPROT_MASK_SFT (0x1 << 18)
+#define DL37_HPROT_SFT 17
+#define DL37_HPROT_MASK 0x1
+#define DL37_HPROT_MASK_SFT (0x1 << 17)
+#define DL36_HPROT_SFT 16
+#define DL36_HPROT_MASK 0x1
+#define DL36_HPROT_MASK_SFT (0x1 << 16)
+#define DL35_HPROT_SFT 15
+#define DL35_HPROT_MASK 0x1
+#define DL35_HPROT_MASK_SFT (0x1 << 15)
+#define DL34_HPROT_SFT 14
+#define DL34_HPROT_MASK 0x1
+#define DL34_HPROT_MASK_SFT (0x1 << 14)
+#define DL33_HPROT_SFT 13
+#define DL33_HPROT_MASK 0x1
+#define DL33_HPROT_MASK_SFT (0x1 << 13)
+#define DL32_HPROT_SFT 12
+#define DL32_HPROT_MASK 0x1
+#define DL32_HPROT_MASK_SFT (0x1 << 12)
+#define DL31_HPROT_SFT 11
+#define DL31_HPROT_MASK 0x1
+#define DL31_HPROT_MASK_SFT (0x1 << 11)
+#define DL30_HPROT_SFT 10
+#define DL30_HPROT_MASK 0x1
+#define DL30_HPROT_MASK_SFT (0x1 << 10)
+#define DL29_HPROT_SFT 9
+#define DL29_HPROT_MASK 0x1
+#define DL29_HPROT_MASK_SFT (0x1 << 9)
+#define DL28_HPROT_SFT 8
+#define DL28_HPROT_MASK 0x1
+#define DL28_HPROT_MASK_SFT (0x1 << 8)
+#define DL27_HPROT_SFT 7
+#define DL27_HPROT_MASK 0x1
+#define DL27_HPROT_MASK_SFT (0x1 << 7)
+#define DL26_HPROT_SFT 6
+#define DL26_HPROT_MASK 0x1
+#define DL26_HPROT_MASK_SFT (0x1 << 6)
+#define DL25_HPROT_SFT 5
+#define DL25_HPROT_MASK 0x1
+#define DL25_HPROT_MASK_SFT (0x1 << 5)
+#define DL24_HPROT_SFT 4
+#define DL24_HPROT_MASK 0x1
+#define DL24_HPROT_MASK_SFT (0x1 << 4)
+#define DL23_HPROT_SFT 3
+#define DL23_HPROT_MASK 0x1
+#define DL23_HPROT_MASK_SFT (0x1 << 3)
+#define DL_48CH_PROT_SFT 2
+#define DL_48CH_PROT_MASK 0x1
+#define DL_48CH_PROT_MASK_SFT (0x1 << 2)
+#define DL_24CH_PROT_SFT 1
+#define DL_24CH_PROT_MASK 0x1
+#define DL_24CH_PROT_MASK_SFT (0x1 << 1)
+#define DL_4CH_PROT_SFT 0
+#define DL_4CH_PROT_MASK 0x1
+#define DL_4CH_PROT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_PROT_SIDEBAND2 */
+#define VUL38_HPROT_SFT 28
+#define VUL38_HPROT_MASK 0x1
+#define VUL38_HPROT_MASK_SFT (0x1 << 28)
+#define VUL37_HPROT_SFT 27
+#define VUL37_HPROT_MASK 0x1
+#define VUL37_HPROT_MASK_SFT (0x1 << 27)
+#define VUL36_HPROT_SFT 26
+#define VUL36_HPROT_MASK 0x1
+#define VUL36_HPROT_MASK_SFT (0x1 << 26)
+#define VUL35_HPROT_SFT 25
+#define VUL35_HPROT_MASK 0x1
+#define VUL35_HPROT_MASK_SFT (0x1 << 25)
+#define VUL34_HPROT_SFT 24
+#define VUL34_HPROT_MASK 0x1
+#define VUL34_HPROT_MASK_SFT (0x1 << 24)
+#define VUL33_HPROT_SFT 23
+#define VUL33_HPROT_MASK 0x1
+#define VUL33_HPROT_MASK_SFT (0x1 << 23)
+#define VUL32_HPROT_SFT 22
+#define VUL32_HPROT_MASK 0x1
+#define VUL32_HPROT_MASK_SFT (0x1 << 22)
+#define VUL31_HPROT_SFT 21
+#define VUL31_HPROT_MASK 0x1
+#define VUL31_HPROT_MASK_SFT (0x1 << 21)
+#define VUL30_HPROT_SFT 20
+#define VUL30_HPROT_MASK 0x1
+#define VUL30_HPROT_MASK_SFT (0x1 << 20)
+#define VUL29_HPROT_SFT 19
+#define VUL29_HPROT_MASK 0x1
+#define VUL29_HPROT_MASK_SFT (0x1 << 19)
+#define VUL28_HPROT_SFT 18
+#define VUL28_HPROT_MASK 0x1
+#define VUL28_HPROT_MASK_SFT (0x1 << 18)
+#define VUL27_HPROT_SFT 17
+#define VUL27_HPROT_MASK 0x1
+#define VUL27_HPROT_MASK_SFT (0x1 << 17)
+#define VUL26_HPROT_SFT 16
+#define VUL26_HPROT_MASK 0x1
+#define VUL26_HPROT_MASK_SFT (0x1 << 16)
+#define VUL25_HPROT_SFT 15
+#define VUL25_HPROT_MASK 0x1
+#define VUL25_HPROT_MASK_SFT (0x1 << 15)
+#define VUL24_HPROT_SFT 14
+#define VUL24_HPROT_MASK 0x1
+#define VUL24_HPROT_MASK_SFT (0x1 << 14)
+#define VUL_CM2_HPROT_SFT 13
+#define VUL_CM2_HPROT_MASK 0x1
+#define VUL_CM2_HPROT_MASK_SFT (0x1 << 13)
+#define VUL_CM1_HPROT_SFT 12
+#define VUL_CM1_HPROT_MASK 0x1
+#define VUL_CM1_HPROT_MASK_SFT (0x1 << 12)
+#define VUL_CM0_HPROT_SFT 11
+#define VUL_CM0_HPROT_MASK 0x1
+#define VUL_CM0_HPROT_MASK_SFT (0x1 << 11)
+#define VUL10_HPROT_SFT 10
+#define VUL10_HPROT_MASK 0x1
+#define VUL10_HPROT_MASK_SFT (0x1 << 10)
+#define VUL9_HPROT_SFT 9
+#define VUL9_HPROT_MASK 0x1
+#define VUL9_HPROT_MASK_SFT (0x1 << 9)
+#define VUL8_HPROT_SFT 8
+#define VUL8_HPROT_MASK 0x1
+#define VUL8_HPROT_MASK_SFT (0x1 << 8)
+#define VUL7_HPROT_SFT 7
+#define VUL7_HPROT_MASK 0x1
+#define VUL7_HPROT_MASK_SFT (0x1 << 7)
+#define VUL6_HPROT_SFT 6
+#define VUL6_HPROT_MASK 0x1
+#define VUL6_HPROT_MASK_SFT (0x1 << 6)
+#define VUL5_HPROT_SFT 5
+#define VUL5_HPROT_MASK 0x1
+#define VUL5_HPROT_MASK_SFT (0x1 << 5)
+#define VUL4_HPROT_SFT 4
+#define VUL4_HPROT_MASK 0x1
+#define VUL4_HPROT_MASK_SFT (0x1 << 4)
+#define VUL3_HPROT_SFT 3
+#define VUL3_HPROT_MASK 0x1
+#define VUL3_HPROT_MASK_SFT (0x1 << 3)
+#define VUL2_HPROT_SFT 2
+#define VUL2_HPROT_MASK 0x1
+#define VUL2_HPROT_MASK_SFT (0x1 << 2)
+#define VUL1_HPROT_SFT 1
+#define VUL1_HPROT_MASK 0x1
+#define VUL1_HPROT_MASK_SFT (0x1 << 1)
+#define VUL0_HPROT_SFT 0
+#define VUL0_HPROT_MASK 0x1
+#define VUL0_HPROT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_PROT_SIDEBAND3 */
+#define MPHONE_EARC_HPROT_SFT 10
+#define MPHONE_EARC_HPROT_MASK 0x1
+#define MPHONE_EARC_HPROT_MASK_SFT (0x1 << 10)
+#define MPHONE_SPDIF_HPROT_SFT 9
+#define MPHONE_SPDIF_HPROT_MASK 0x1
+#define MPHONE_SPDIF_HPROT_MASK_SFT (0x1 << 9)
+#define SPDIFIN_HPROT_SFT 8
+#define SPDIFIN_HPROT_MASK 0x1
+#define SPDIFIN_HPROT_MASK_SFT (0x1 << 8)
+#define TDMIN_HPROT_SFT 7
+#define TDMIN_HPROT_MASK 0x1
+#define TDMIN_HPROT_MASK_SFT (0x1 << 7)
+#define ETDM_IN1_HPROT_SFT 1
+#define ETDM_IN1_HPROT_MASK 0x1
+#define ETDM_IN1_HPROT_MASK_SFT (0x1 << 1)
+#define ETDM_IN0_HPROT_SFT 0
+#define ETDM_IN0_HPROT_MASK 0x1
+#define ETDM_IN0_HPROT_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND0 */
+#define DL7_HDOMAIN_SFT 28
+#define DL7_HDOMAIN_MASK 0xf
+#define DL7_HDOMAIN_MASK_SFT (0xf << 28)
+#define DL6_HDOMAIN_SFT 24
+#define DL6_HDOMAIN_MASK 0xf
+#define DL6_HDOMAIN_MASK_SFT (0xf << 24)
+#define DL5_HDOMAIN_SFT 20
+#define DL5_HDOMAIN_MASK 0xf
+#define DL5_HDOMAIN_MASK_SFT (0xf << 20)
+#define DL4_HDOMAIN_SFT 16
+#define DL4_HDOMAIN_MASK 0xf
+#define DL4_HDOMAIN_MASK_SFT (0xf << 16)
+#define DL3_HDOMAIN_SFT 12
+#define DL3_HDOMAIN_MASK 0xf
+#define DL3_HDOMAIN_MASK_SFT (0xf << 12)
+#define DL2_HDOMAIN_SFT 8
+#define DL2_HDOMAIN_MASK 0xf
+#define DL2_HDOMAIN_MASK_SFT (0xf << 8)
+#define DL1_HDOMAIN_SFT 4
+#define DL1_HDOMAIN_MASK 0xf
+#define DL1_HDOMAIN_MASK_SFT (0xf << 4)
+#define DL0_HDOMAIN_SFT 0
+#define DL0_HDOMAIN_MASK 0xf
+#define DL0_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND1 */
+#define DL_48CH_HDOMAIN_SFT 24
+#define DL_48CH_HDOMAIN_MASK 0xf
+#define DL_48CH_HDOMAIN_MASK_SFT (0xf << 24)
+#define DL_24CH_HDOMAIN_SFT 20
+#define DL_24CH_HDOMAIN_MASK 0xf
+#define DL_24CH_HDOMAIN_MASK_SFT (0xf << 20)
+#define DL_4CH_HDOMAIN_SFT 16
+#define DL_4CH_HDOMAIN_MASK 0xf
+#define DL_4CH_HDOMAIN_MASK_SFT (0xf << 16)
+#define HDMI_HDOMAIN_SFT 12
+#define HDMI_HDOMAIN_MASK 0xf
+#define HDMI_HDOMAIN_MASK_SFT (0xf << 12)
+#define SPDIF2_OUT_HDOMAIN_SFT 8
+#define SPDIF2_OUT_HDOMAIN_MASK 0xf
+#define SPDIF2_OUT_HDOMAIN_MASK_SFT (0xf << 8)
+#define SPDIF_OUT_HDOMAIN_SFT 4
+#define SPDIF_OUT_HDOMAIN_MASK 0xf
+#define SPDIF_OUT_HDOMAIN_MASK_SFT (0xf << 4)
+#define DL8_HDOMAIN_SFT 0
+#define DL8_HDOMAIN_MASK 0xf
+#define DL8_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND2 */
+#define DL30_HDOMAIN_SFT 28
+#define DL30_HDOMAIN_MASK 0xf
+#define DL30_HDOMAIN_MASK_SFT (0xf << 28)
+#define DL29_HDOMAIN_SFT 24
+#define DL29_HDOMAIN_MASK 0xf
+#define DL29_HDOMAIN_MASK_SFT (0xf << 24)
+#define DL28_HDOMAIN_SFT 20
+#define DL28_HDOMAIN_MASK 0xf
+#define DL28_HDOMAIN_MASK_SFT (0xf << 20)
+#define DL27_HDOMAIN_SFT 16
+#define DL27_HDOMAIN_MASK 0xf
+#define DL27_HDOMAIN_MASK_SFT (0xf << 16)
+#define DL26_HDOMAIN_SFT 12
+#define DL26_HDOMAIN_MASK 0xf
+#define DL26_HDOMAIN_MASK_SFT (0xf << 12)
+#define DL25_HDOMAIN_SFT 8
+#define DL25_HDOMAIN_MASK 0xf
+#define DL25_HDOMAIN_MASK_SFT (0xf << 8)
+#define DL24_HDOMAIN_SFT 4
+#define DL24_HDOMAIN_MASK 0xf
+#define DL24_HDOMAIN_MASK_SFT (0xf << 4)
+#define DL23_HDOMAIN_SFT 0
+#define DL23_HDOMAIN_MASK 0xf
+#define DL23_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND3 */
+#define DL38_HDOMAIN_SFT 28
+#define DL38_HDOMAIN_MASK 0xf
+#define DL38_HDOMAIN_MASK_SFT (0xf << 28)
+#define DL37_HDOMAIN_SFT 24
+#define DL37_HDOMAIN_MASK 0xf
+#define DL37_HDOMAIN_MASK_SFT (0xf << 24)
+#define DL36_HDOMAIN_SFT 20
+#define DL36_HDOMAIN_MASK 0xf
+#define DL36_HDOMAIN_MASK_SFT (0xf << 20)
+#define DL35_HDOMAIN_SFT 16
+#define DL35_HDOMAIN_MASK 0xf
+#define DL35_HDOMAIN_MASK_SFT (0xf << 16)
+#define DL34_HDOMAIN_SFT 12
+#define DL34_HDOMAIN_MASK 0xf
+#define DL34_HDOMAIN_MASK_SFT (0xf << 12)
+#define DL33_HDOMAIN_SFT 8
+#define DL33_HDOMAIN_MASK 0xf
+#define DL33_HDOMAIN_MASK_SFT (0xf << 8)
+#define DL32_HDOMAIN_SFT 4
+#define DL32_HDOMAIN_MASK 0xf
+#define DL32_HDOMAIN_MASK_SFT (0xf << 4)
+#define DL31_HDOMAIN_SFT 0
+#define DL31_HDOMAIN_MASK 0xf
+#define DL31_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND4 */
+#define DL46_HDOMAIN_SFT 28
+#define DL46_HDOMAIN_MASK 0xf
+#define DL46_HDOMAIN_MASK_SFT (0xf << 28)
+#define DL45_HDOMAIN_SFT 24
+#define DL45_HDOMAIN_MASK 0xf
+#define DL45_HDOMAIN_MASK_SFT (0xf << 24)
+#define DL44_HDOMAIN_SFT 20
+#define DL44_HDOMAIN_MASK 0xf
+#define DL44_HDOMAIN_MASK_SFT (0xf << 20)
+#define DL43_HDOMAIN_SFT 16
+#define DL43_HDOMAIN_MASK 0xf
+#define DL43_HDOMAIN_MASK_SFT (0xf << 16)
+#define DL42_HDOMAIN_SFT 12
+#define DL42_HDOMAIN_MASK 0xf
+#define DL42_HDOMAIN_MASK_SFT (0xf << 12)
+#define DL41_HDOMAIN_SFT 8
+#define DL41_HDOMAIN_MASK 0xf
+#define DL41_HDOMAIN_MASK_SFT (0xf << 8)
+#define DL40_HDOMAIN_SFT 4
+#define DL40_HDOMAIN_MASK 0xf
+#define DL40_HDOMAIN_MASK_SFT (0xf << 4)
+#define DL39_HDOMAIN_SFT 0
+#define DL39_HDOMAIN_MASK 0xf
+#define DL39_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND5 */
+#define VUL7_HDOMAIN_SFT 28
+#define VUL7_HDOMAIN_MASK 0xf
+#define VUL7_HDOMAIN_MASK_SFT (0xf << 28)
+#define VUL6_HDOMAIN_SFT 24
+#define VUL6_HDOMAIN_MASK 0xf
+#define VUL6_HDOMAIN_MASK_SFT (0xf << 24)
+#define VUL5_HDOMAIN_SFT 20
+#define VUL5_HDOMAIN_MASK 0xf
+#define VUL5_HDOMAIN_MASK_SFT (0xf << 20)
+#define VUL4_HDOMAIN_SFT 16
+#define VUL4_HDOMAIN_MASK 0xf
+#define VUL4_HDOMAIN_MASK_SFT (0xf << 16)
+#define VUL3_HDOMAIN_SFT 12
+#define VUL3_HDOMAIN_MASK 0xf
+#define VUL3_HDOMAIN_MASK_SFT (0xf << 12)
+#define VUL2_HDOMAIN_SFT 8
+#define VUL2_HDOMAIN_MASK 0xf
+#define VUL2_HDOMAIN_MASK_SFT (0xf << 8)
+#define VUL1_HDOMAIN_SFT 4
+#define VUL1_HDOMAIN_MASK 0xf
+#define VUL1_HDOMAIN_MASK_SFT (0xf << 4)
+#define VUL0_HDOMAIN_SFT 0
+#define VUL0_HDOMAIN_MASK 0xf
+#define VUL0_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND6 */
+#define VU25_HDOMAIN_SFT 28
+#define VU25_HDOMAIN_MASK 0xf
+#define VU25_HDOMAIN_MASK_SFT (0xf << 28)
+#define VUL24_HDOMAIN_SFT 24
+#define VUL24_HDOMAIN_MASK 0xf
+#define VUL24_HDOMAIN_MASK_SFT (0xf << 24)
+#define VUL_CM2_HDOMAIN_SFT 20
+#define VUL_CM2_HDOMAIN_MASK 0xf
+#define VUL_CM2_HDOMAIN_MASK_SFT (0xf << 20)
+#define VUL_CM1_HDOMAIN_SFT 16
+#define VUL_CM1_HDOMAIN_MASK 0xf
+#define VUL_CM1_HDOMAIN_MASK_SFT (0xf << 16)
+#define VUL_CM0_HDOMAIN_SFT 12
+#define VUL_CM0_HDOMAIN_MASK 0xf
+#define VUL_CM0_HDOMAIN_MASK_SFT (0xf << 12)
+#define VUL10_HDOMAIN_SFT 8
+#define VUL10_HDOMAIN_MASK 0xf
+#define VUL10_HDOMAIN_MASK_SFT (0xf << 8)
+#define VUL9_HDOMAIN_SFT 4
+#define VUL9_HDOMAIN_MASK 0xf
+#define VUL9_HDOMAIN_MASK_SFT (0xf << 4)
+#define VUL8_HDOMAIN_SFT 0
+#define VUL8_HDOMAIN_MASK 0xf
+#define VUL8_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND7 */
+#define VUL33_HDOMAIN_SFT 28
+#define VUL33_HDOMAIN_MASK 0xf
+#define VUL33_HDOMAIN_MASK_SFT (0xf << 28)
+#define VUL32_HDOMAIN_SFT 24
+#define VUL32_HDOMAIN_MASK 0xf
+#define VUL32_HDOMAIN_MASK_SFT (0xf << 24)
+#define VUL31_HDOMAIN_SFT 20
+#define VUL31_HDOMAIN_MASK 0xf
+#define VUL31_HDOMAIN_MASK_SFT (0xf << 20)
+#define VUL30_HDOMAIN_SFT 16
+#define VUL30_HDOMAIN_MASK 0xf
+#define VUL30_HDOMAIN_MASK_SFT (0xf << 16)
+#define VUL29_HDOMAIN_SFT 12
+#define VUL29_HDOMAIN_MASK 0xf
+#define VUL29_HDOMAIN_MASK_SFT (0xf << 12)
+#define VUL28_HDOMAIN_SFT 8
+#define VUL28_HDOMAIN_MASK 0xf
+#define VUL28_HDOMAIN_MASK_SFT (0xf << 8)
+#define VUL27_HDOMAIN_SFT 4
+#define VUL27_HDOMAIN_MASK 0xf
+#define VUL27_HDOMAIN_MASK_SFT (0xf << 4)
+#define VUL26_HDOMAIN_SFT 0
+#define VUL26_HDOMAIN_MASK 0xf
+#define VUL26_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND8 */
+#define ETDM_IN1_HDOMAIN_SFT 24
+#define ETDM_IN1_HDOMAIN_MASK 0xf
+#define ETDM_IN1_HDOMAIN_MASK_SFT (0xf << 24)
+#define ETDM_IN0_HDOMAIN_SFT 20
+#define ETDM_IN0_HDOMAIN_MASK 0xf
+#define ETDM_IN0_HDOMAIN_MASK_SFT (0xf << 20)
+#define VUL38_HDOMAIN_SFT 16
+#define VUL38_HDOMAIN_MASK 0xf
+#define VUL38_HDOMAIN_MASK_SFT (0xf << 16)
+#define VUL37_HDOMAIN_SFT 12
+#define VUL37_HDOMAIN_MASK 0xf
+#define VUL37_HDOMAIN_MASK_SFT (0xf << 12)
+#define VUL36_HDOMAIN_SFT 8
+#define VUL36_HDOMAIN_MASK 0xf
+#define VUL36_HDOMAIN_MASK_SFT (0xf << 8)
+#define VUL35_HDOMAIN_SFT 4
+#define VUL35_HDOMAIN_MASK 0xf
+#define VUL35_HDOMAIN_MASK_SFT (0xf << 4)
+#define VUL34_HDOMAIN_SFT 0
+#define VUL34_HDOMAIN_MASK 0xf
+#define VUL34_HDOMAIN_MASK_SFT (0xf << 0)
+
+/* AFE_SE_DOMAIN_SIDEBAND9 */
+#define MPHONE_EARC_HDOMAIN_SFT 28
+#define MPHONE_EARC_HDOMAIN_MASK 0xf
+#define MPHONE_EARC_HDOMAIN_MASK_SFT (0xf << 28)
+#define MPHONE_SPDIF_HDOMAIN_SFT 24
+#define MPHONE_SPDIF_HDOMAIN_MASK 0xf
+#define MPHONE_SPDIF_HDOMAIN_MASK_SFT (0xf << 24)
+#define SPDIFIN_HDOMAIN_SFT 20
+#define SPDIFIN_HDOMAIN_MASK 0xf
+#define SPDIFIN_HDOMAIN_MASK_SFT (0xf << 20)
+#define TDMIN_HDOMAIN_SFT 16
+#define TDMIN_HDOMAIN_MASK 0xf
+#define TDMIN_HDOMAIN_MASK_SFT (0xf << 16)
+
+/* AFE_PROT_SIDEBAND0_MON */
+#define AFE_DOMAIN_SIDEBAN0_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN0_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN0_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_PROT_SIDEBAND1_MON */
+#define AFE_DOMAIN_SIDEBAN1_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN1_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN1_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_PROT_SIDEBAND2_MON */
+#define AFE_DOMAIN_SIDEBAN2_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_PROT_SIDEBAND3_MON */
+#define AFE_DOMAIN_SIDEBAN3_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND0_MON */
+#define AFE_DOMAIN_SIDEBAN0_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN0_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN0_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND1_MON */
+#define AFE_DOMAIN_SIDEBAN1_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN1_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN1_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND2_MON */
+#define AFE_DOMAIN_SIDEBAN2_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND3_MON */
+#define AFE_DOMAIN_SIDEBAN3_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND4_MON */
+#define AFE_DOMAIN_SIDEBAN0_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN0_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN0_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND5_MON */
+#define AFE_DOMAIN_SIDEBAN1_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN1_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN1_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND6_MON */
+#define AFE_DOMAIN_SIDEBAN2_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND7_MON */
+#define AFE_DOMAIN_SIDEBAN3_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND8_MON */
+#define AFE_DOMAIN_SIDEBAN2_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN2_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_DOMAIN_SIDEBAND9_MON */
+#define AFE_DOMAIN_SIDEBAN3_MON_SFT 0
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK 0xffffffff
+#define AFE_DOMAIN_SIDEBAN3_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SECURE_CONN0 */
+#define AFE_SPDIFIN_LPBK_CON_MASK_S_SFT 26
+#define AFE_SPDIFIN_LPBK_CON_MASK_S_MASK 0x3
+#define AFE_SPDIFIN_LPBK_CON_MASK_S_MASK_SFT (0x3 << 26)
+#define AFE_ADDA_DMIC1_SRC_CON0_MASK_S_SFT 25
+#define AFE_ADDA_DMIC1_SRC_CON0_MASK_S_MASK 0x1
+#define AFE_ADDA_DMIC1_SRC_CON0_MASK_S_MASK_SFT (0x1 << 25)
+#define AFE_ADDA_DMIC0_SRC_CON0_MASK_S_SFT 24
+#define AFE_ADDA_DMIC0_SRC_CON0_MASK_S_MASK 0x1
+#define AFE_ADDA_DMIC0_SRC_CON0_MASK_S_MASK_SFT (0x1 << 24)
+#define AFE_ADDA_UL3_SRC_CON0_MASK_S_SFT 23
+#define AFE_ADDA_UL3_SRC_CON0_MASK_S_MASK 0x1
+#define AFE_ADDA_UL3_SRC_CON0_MASK_S_MASK_SFT (0x1 << 23)
+#define AFE_ADDA_UL2_SRC_CON0_MASK_S_SFT 22
+#define AFE_ADDA_UL2_SRC_CON0_MASK_S_MASK 0x1
+#define AFE_ADDA_UL2_SRC_CON0_MASK_S_MASK_SFT (0x1 << 22)
+#define AFE_ADDA_UL1_SRC_CON0_MASK_S_SFT 21
+#define AFE_ADDA_UL1_SRC_CON0_MASK_S_MASK 0x1
+#define AFE_ADDA_UL1_SRC_CON0_MASK_S_MASK_SFT (0x1 << 21)
+#define AFE_ADDA_UL0_SRC_CON0_MASK_S_SFT 20
+#define AFE_ADDA_UL0_SRC_CON0_MASK_S_MASK 0x1
+#define AFE_ADDA_UL0_SRC_CON0_MASK_S_MASK_SFT (0x1 << 20)
+#define AFE_MRKAIF1_CFG0_MASK_S_SFT 19
+#define AFE_MRKAIF1_CFG0_MASK_S_MASK 0x1
+#define AFE_MRKAIF1_CFG0_MASK_S_MASK_SFT (0x1 << 19)
+#define AFE_MRKAIF0_CFG0_MASK_S_SFT 18
+#define AFE_MRKAIF0_CFG0_MASK_S_MASK 0x1
+#define AFE_MRKAIF0_CFG0_MASK_S_MASK_SFT (0x1 << 18)
+#define AFE_TDMIN_CON1_MASK_S_SFT 17
+#define AFE_TDMIN_CON1_MASK_S_MASK 0x1
+#define AFE_TDMIN_CON1_MASK_S_MASK_SFT (0x1 << 17)
+#define AFE_TDM_CON2_MASK_S_SFT 16
+#define AFE_TDM_CON2_MASK_S_MASK 0x1
+#define AFE_TDM_CON2_MASK_S_MASK_SFT (0x1 << 16)
+#define AFE_DAIBT_CON_MASK_S_SFT 14
+#define AFE_DAIBT_CON_MASK_S_MASK 0x3
+#define AFE_DAIBT_CON_MASK_S_MASK_SFT (0x3 << 14)
+#define AFE_MRGIF_CON_MASK_S_SFT 12
+#define AFE_MRGIF_CON_MASK_S_MASK 0x3
+#define AFE_MRGIF_CON_MASK_S_MASK_SFT (0x3 << 12)
+#define AFE_CONNSYS_I2S_CON_MASK_S_SFT 11
+#define AFE_CONNSYS_I2S_CON_MASK_S_MASK 0x1
+#define AFE_CONNSYS_I2S_CON_MASK_S_MASK_SFT (0x1 << 11)
+#define AFE_PCM1_INFT_CON0_MASK_S_SFT 6
+#define AFE_PCM1_INFT_CON0_MASK_S_MASK 0x1f
+#define AFE_PCM1_INFT_CON0_MASK_S_MASK_SFT (0x1f << 6)
+#define AFE_PCM0_INTF_CON1_MASK_S_SFT 0
+#define AFE_PCM0_INTF_CON1_MASK_S_MASK 0x3f
+#define AFE_PCM0_INTF_CON1_MASK_S_MASK_SFT (0x3f << 0)
+
+/* AFE_SECURE_CONN_ETDM1 */
+#define ETDM1_4_7_COWORK_CON1_MASK_S_0_SFT 24
+#define ETDM1_4_7_COWORK_CON1_MASK_S_0_MASK 0xff
+#define ETDM1_4_7_COWORK_CON1_MASK_S_0_MASK_SFT (0xff << 24)
+#define ETDM1_4_7_COWORK_CON0_MASK_S_0_SFT 20
+#define ETDM1_4_7_COWORK_CON0_MASK_S_0_MASK 0xf
+#define ETDM1_4_7_COWORK_CON0_MASK_S_0_MASK_SFT (0xf << 20)
+#define ETDM1_4_7_COWORK_CON0_MASK_S_1_SFT 16
+#define ETDM1_4_7_COWORK_CON0_MASK_S_1_MASK 0xf
+#define ETDM1_4_7_COWORK_CON0_MASK_S_1_MASK_SFT (0xf << 16)
+#define ETDM1_0_3_COWORK_CON3_MASK_S_0_SFT 8
+#define ETDM1_0_3_COWORK_CON3_MASK_S_0_MASK 0xff
+#define ETDM1_0_3_COWORK_CON3_MASK_S_0_MASK_SFT (0xff << 8)
+#define ETDM1_0_3_COWORK_CON3_MASK_S_1_SFT 0
+#define ETDM1_0_3_COWORK_CON3_MASK_S_1_MASK 0xff
+#define ETDM1_0_3_COWORK_CON3_MASK_S_1_MASK_SFT (0xff << 0)
+
+/* AFE_SECURE_CONN_ETDM2 */
+#define ETDM2_4_7_COWORK_CON3_MASK_S_0_SFT 24
+#define ETDM2_4_7_COWORK_CON3_MASK_S_0_MASK 0xff
+#define ETDM2_4_7_COWORK_CON3_MASK_S_0_MASK_SFT (0xff << 24)
+#define ETDM2_4_7_COWORK_CON3_MASK_S_1_SFT 16
+#define ETDM2_4_7_COWORK_CON3_MASK_S_1_MASK 0xff
+#define ETDM2_4_7_COWORK_CON3_MASK_S_1_MASK_SFT (0xff << 16)
+#define ETDM2_4_7_COWORK_CON2_MASK_S_0_SFT 12
+#define ETDM2_4_7_COWORK_CON2_MASK_S_0_MASK 0xf
+#define ETDM2_4_7_COWORK_CON2_MASK_S_0_MASK_SFT (0xf << 12)
+#define ETDM2_4_7_COWORK_CON2_MASK_S_1_SFT 8
+#define ETDM2_4_7_COWORK_CON2_MASK_S_1_MASK 0xf
+#define ETDM2_4_7_COWORK_CON2_MASK_S_1_MASK_SFT (0xf << 8)
+#define ETDM2_4_7_COWORK_CON1_MASK_S_0_SFT 0
+#define ETDM2_4_7_COWORK_CON1_MASK_S_0_MASK 0xff
+#define ETDM2_4_7_COWORK_CON1_MASK_S_0_MASK_SFT (0xff << 0)
+
+/* AFE_SECURE_SRAM_CON0 */
+#define SRAM_READ_EN15_NS_SFT 31
+#define SRAM_READ_EN15_NS_MASK 0x1
+#define SRAM_READ_EN15_NS_MASK_SFT (0x1 << 31)
+#define SRAM_WRITE_EN15_NS_SFT 30
+#define SRAM_WRITE_EN15_NS_MASK 0x1
+#define SRAM_WRITE_EN15_NS_MASK_SFT (0x1 << 30)
+#define SRAM_READ_EN14_NS_SFT 29
+#define SRAM_READ_EN14_NS_MASK 0x1
+#define SRAM_READ_EN14_NS_MASK_SFT (0x1 << 29)
+#define SRAM_WRITE_EN14_NS_SFT 28
+#define SRAM_WRITE_EN14_NS_MASK 0x1
+#define SRAM_WRITE_EN14_NS_MASK_SFT (0x1 << 28)
+#define SRAM_READ_EN13_NS_SFT 27
+#define SRAM_READ_EN13_NS_MASK 0x1
+#define SRAM_READ_EN13_NS_MASK_SFT (0x1 << 27)
+#define SRAM_WRITE_EN13_NS_SFT 26
+#define SRAM_WRITE_EN13_NS_MASK 0x1
+#define SRAM_WRITE_EN13_NS_MASK_SFT (0x1 << 26)
+#define SRAM_READ_EN12_NS_SFT 25
+#define SRAM_READ_EN12_NS_MASK 0x1
+#define SRAM_READ_EN12_NS_MASK_SFT (0x1 << 25)
+#define SRAM_WRITE_EN12_NS_SFT 24
+#define SRAM_WRITE_EN12_NS_MASK 0x1
+#define SRAM_WRITE_EN12_NS_MASK_SFT (0x1 << 24)
+#define SRAM_READ_EN11_NS_SFT 23
+#define SRAM_READ_EN11_NS_MASK 0x1
+#define SRAM_READ_EN11_NS_MASK_SFT (0x1 << 23)
+#define SRAM_WRITE_EN11_NS_SFT 22
+#define SRAM_WRITE_EN11_NS_MASK 0x1
+#define SRAM_WRITE_EN11_NS_MASK_SFT (0x1 << 22)
+#define SRAM_READ_EN10_NS_SFT 21
+#define SRAM_READ_EN10_NS_MASK 0x1
+#define SRAM_READ_EN10_NS_MASK_SFT (0x1 << 21)
+#define SRAM_WRITE_EN10_NS_SFT 20
+#define SRAM_WRITE_EN10_NS_MASK 0x1
+#define SRAM_WRITE_EN10_NS_MASK_SFT (0x1 << 20)
+#define SRAM_READ_EN9_NS_SFT 19
+#define SRAM_READ_EN9_NS_MASK 0x1
+#define SRAM_READ_EN9_NS_MASK_SFT (0x1 << 19)
+#define SRAM_WRITE_EN9_NS_SFT 18
+#define SRAM_WRITE_EN9_NS_MASK 0x1
+#define SRAM_WRITE_EN9_NS_MASK_SFT (0x1 << 18)
+#define SRAM_READ_EN8_NS_SFT 17
+#define SRAM_READ_EN8_NS_MASK 0x1
+#define SRAM_READ_EN8_NS_MASK_SFT (0x1 << 17)
+#define SRAM_WRITE_EN8_NS_SFT 16
+#define SRAM_WRITE_EN8_NS_MASK 0x1
+#define SRAM_WRITE_EN8_NS_MASK_SFT (0x1 << 16)
+#define SRAM_READ_EN7_NS_SFT 15
+#define SRAM_READ_EN7_NS_MASK 0x1
+#define SRAM_READ_EN7_NS_MASK_SFT (0x1 << 15)
+#define SRAM_WRITE_EN7_NS_SFT 14
+#define SRAM_WRITE_EN7_NS_MASK 0x1
+#define SRAM_WRITE_EN7_NS_MASK_SFT (0x1 << 14)
+#define SRAM_READ_EN6_NS_SFT 13
+#define SRAM_READ_EN6_NS_MASK 0x1
+#define SRAM_READ_EN6_NS_MASK_SFT (0x1 << 13)
+#define SRAM_WRITE_EN6_NS_SFT 12
+#define SRAM_WRITE_EN6_NS_MASK 0x1
+#define SRAM_WRITE_EN6_NS_MASK_SFT (0x1 << 12)
+#define SRAM_READ_EN5_NS_SFT 11
+#define SRAM_READ_EN5_NS_MASK 0x1
+#define SRAM_READ_EN5_NS_MASK_SFT (0x1 << 11)
+#define SRAM_WRITE_EN5_NS_SFT 10
+#define SRAM_WRITE_EN5_NS_MASK 0x1
+#define SRAM_WRITE_EN5_NS_MASK_SFT (0x1 << 10)
+#define SRAM_READ_EN4_NS_SFT 9
+#define SRAM_READ_EN4_NS_MASK 0x1
+#define SRAM_READ_EN4_NS_MASK_SFT (0x1 << 9)
+#define SRAM_WRITE_EN4_NS_SFT 8
+#define SRAM_WRITE_EN4_NS_MASK 0x1
+#define SRAM_WRITE_EN4_NS_MASK_SFT (0x1 << 8)
+#define SRAM_READ_EN3_NS_SFT 7
+#define SRAM_READ_EN3_NS_MASK 0x1
+#define SRAM_READ_EN3_NS_MASK_SFT (0x1 << 7)
+#define SRAM_WRITE_EN3_NS_SFT 6
+#define SRAM_WRITE_EN3_NS_MASK 0x1
+#define SRAM_WRITE_EN3_NS_MASK_SFT (0x1 << 6)
+#define SRAM_READ_EN2_NS_SFT 5
+#define SRAM_READ_EN2_NS_MASK 0x1
+#define SRAM_READ_EN2_NS_MASK_SFT (0x1 << 5)
+#define SRAM_WRITE_EN2_NS_SFT 4
+#define SRAM_WRITE_EN2_NS_MASK 0x1
+#define SRAM_WRITE_EN2_NS_MASK_SFT (0x1 << 4)
+#define SRAM_READ_EN1_NS_SFT 3
+#define SRAM_READ_EN1_NS_MASK 0x1
+#define SRAM_READ_EN1_NS_MASK_SFT (0x1 << 3)
+#define SRAM_WRITE_EN1_NS_SFT 2
+#define SRAM_WRITE_EN1_NS_MASK 0x1
+#define SRAM_WRITE_EN1_NS_MASK_SFT (0x1 << 2)
+#define SRAM_READ_EN0_NS_SFT 1
+#define SRAM_READ_EN0_NS_MASK 0x1
+#define SRAM_READ_EN0_NS_MASK_SFT (0x1 << 1)
+#define SRAM_WRITE_EN0_NS_SFT 0
+#define SRAM_WRITE_EN0_NS_MASK 0x1
+#define SRAM_WRITE_EN0_NS_MASK_SFT (0x1 << 0)
+
+/* AFE_SECURE_SRAM_CON1 */
+#define SRAM_READ_EN15_S_SFT 31
+#define SRAM_READ_EN15_S_MASK 0x1
+#define SRAM_READ_EN15_S_MASK_SFT (0x1 << 31)
+#define SRAM_WRITE_EN15_S_SFT 30
+#define SRAM_WRITE_EN15_S_MASK 0x1
+#define SRAM_WRITE_EN15_S_MASK_SFT (0x1 << 30)
+#define SRAM_READ_EN14_S_SFT 29
+#define SRAM_READ_EN14_S_MASK 0x1
+#define SRAM_READ_EN14_S_MASK_SFT (0x1 << 29)
+#define SRAM_WRITE_EN14_S_SFT 28
+#define SRAM_WRITE_EN14_S_MASK 0x1
+#define SRAM_WRITE_EN14_S_MASK_SFT (0x1 << 28)
+#define SRAM_READ_EN13_S_SFT 27
+#define SRAM_READ_EN13_S_MASK 0x1
+#define SRAM_READ_EN13_S_MASK_SFT (0x1 << 27)
+#define SRAM_WRITE_EN13_S_SFT 26
+#define SRAM_WRITE_EN13_S_MASK 0x1
+#define SRAM_WRITE_EN13_S_MASK_SFT (0x1 << 26)
+#define SRAM_READ_EN12_S_SFT 25
+#define SRAM_READ_EN12_S_MASK 0x1
+#define SRAM_READ_EN12_S_MASK_SFT (0x1 << 25)
+#define SRAM_WRITE_EN12_S_SFT 24
+#define SRAM_WRITE_EN12_S_MASK 0x1
+#define SRAM_WRITE_EN12_S_MASK_SFT (0x1 << 24)
+#define SRAM_READ_EN11_S_SFT 23
+#define SRAM_READ_EN11_S_MASK 0x1
+#define SRAM_READ_EN11_S_MASK_SFT (0x1 << 23)
+#define SRAM_WRITE_EN11_S_SFT 22
+#define SRAM_WRITE_EN11_S_MASK 0x1
+#define SRAM_WRITE_EN11_S_MASK_SFT (0x1 << 22)
+#define SRAM_READ_EN10_S_SFT 21
+#define SRAM_READ_EN10_S_MASK 0x1
+#define SRAM_READ_EN10_S_MASK_SFT (0x1 << 21)
+#define SRAM_WRITE_EN10_S_SFT 20
+#define SRAM_WRITE_EN10_S_MASK 0x1
+#define SRAM_WRITE_EN10_S_MASK_SFT (0x1 << 20)
+#define SRAM_READ_EN9_S_SFT 19
+#define SRAM_READ_EN9_S_MASK 0x1
+#define SRAM_READ_EN9_S_MASK_SFT (0x1 << 19)
+#define SRAM_WRITE_EN9_S_SFT 18
+#define SRAM_WRITE_EN9_S_MASK 0x1
+#define SRAM_WRITE_EN9_S_MASK_SFT (0x1 << 18)
+#define SRAM_READ_EN8_S_SFT 17
+#define SRAM_READ_EN8_S_MASK 0x1
+#define SRAM_READ_EN8_S_MASK_SFT (0x1 << 17)
+#define SRAM_WRITE_EN8_S_SFT 16
+#define SRAM_WRITE_EN8_S_MASK 0x1
+#define SRAM_WRITE_EN8_S_MASK_SFT (0x1 << 16)
+#define SRAM_READ_EN7_S_SFT 15
+#define SRAM_READ_EN7_S_MASK 0x1
+#define SRAM_READ_EN7_S_MASK_SFT (0x1 << 15)
+#define SRAM_WRITE_EN7_S_SFT 14
+#define SRAM_WRITE_EN7_S_MASK 0x1
+#define SRAM_WRITE_EN7_S_MASK_SFT (0x1 << 14)
+#define SRAM_READ_EN6_S_SFT 13
+#define SRAM_READ_EN6_S_MASK 0x1
+#define SRAM_READ_EN6_S_MASK_SFT (0x1 << 13)
+#define SRAM_WRITE_EN6_S_SFT 12
+#define SRAM_WRITE_EN6_S_MASK 0x1
+#define SRAM_WRITE_EN6_S_MASK_SFT (0x1 << 12)
+#define SRAM_READ_EN5_S_SFT 11
+#define SRAM_READ_EN5_S_MASK 0x1
+#define SRAM_READ_EN5_S_MASK_SFT (0x1 << 11)
+#define SRAM_WRITE_EN5_S_SFT 10
+#define SRAM_WRITE_EN5_S_MASK 0x1
+#define SRAM_WRITE_EN5_S_MASK_SFT (0x1 << 10)
+#define SRAM_READ_EN4_S_SFT 9
+#define SRAM_READ_EN4_S_MASK 0x1
+#define SRAM_READ_EN4_S_MASK_SFT (0x1 << 9)
+#define SRAM_WRITE_EN4_S_SFT 8
+#define SRAM_WRITE_EN4_S_MASK 0x1
+#define SRAM_WRITE_EN4_S_MASK_SFT (0x1 << 8)
+#define SRAM_READ_EN3_S_SFT 7
+#define SRAM_READ_EN3_S_MASK 0x1
+#define SRAM_READ_EN3_S_MASK_SFT (0x1 << 7)
+#define SRAM_WRITE_EN3_S_SFT 6
+#define SRAM_WRITE_EN3_S_MASK 0x1
+#define SRAM_WRITE_EN3_S_MASK_SFT (0x1 << 6)
+#define SRAM_READ_EN2_S_SFT 5
+#define SRAM_READ_EN2_S_MASK 0x1
+#define SRAM_READ_EN2_S_MASK_SFT (0x1 << 5)
+#define SRAM_WRITE_EN2_S_SFT 4
+#define SRAM_WRITE_EN2_S_MASK 0x1
+#define SRAM_WRITE_EN2_S_MASK_SFT (0x1 << 4)
+#define SRAM_READ_EN1_S_SFT 3
+#define SRAM_READ_EN1_S_MASK 0x1
+#define SRAM_READ_EN1_S_MASK_SFT (0x1 << 3)
+#define SRAM_WRITE_EN1_S_SFT 2
+#define SRAM_WRITE_EN1_S_MASK 0x1
+#define SRAM_WRITE_EN1_S_MASK_SFT (0x1 << 2)
+#define SRAM_READ_EN0_S_SFT 1
+#define SRAM_READ_EN0_S_MASK 0x1
+#define SRAM_READ_EN0_S_MASK_SFT (0x1 << 1)
+#define SRAM_WRITE_EN0_S_SFT 0
+#define SRAM_WRITE_EN0_S_MASK 0x1
+#define SRAM_WRITE_EN0_S_MASK_SFT (0x1 << 0)
+
+/* AFE_SE_CONN_INPUT_MASK0 */
+#define SECURE_INTRCONN_I0_I31_S_SFT 0
+#define SECURE_INTRCONN_I0_I31_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I0_I31_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK1 */
+#define SECURE_INTRCONN_I32_I63_S_SFT 0
+#define SECURE_INTRCONN_I32_I63_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I32_I63_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK2 */
+#define SECURE_INTRCONN_I64_I95_S_SFT 0
+#define SECURE_INTRCONN_I64_I95_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I64_I95_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK3 */
+#define SECURE_INTRCONN_I96_I127_S_SFT 0
+#define SECURE_INTRCONN_I96_I127_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I96_I127_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK4 */
+#define SECURE_INTRCONN_I128_I159_S_SFT 0
+#define SECURE_INTRCONN_I128_I159_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I128_I159_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK5 */
+#define SECURE_INTRCONN_I160_I191_S_SFT 0
+#define SECURE_INTRCONN_I160_I191_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I160_I191_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK6 */
+#define SECURE_INTRCONN_I192_I223_S_SFT 0
+#define SECURE_INTRCONN_I192_I223_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I192_I223_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_INPUT_MASK7 */
+#define SECURE_INTRCONN_I224_I256_S_SFT 0
+#define SECURE_INTRCONN_I224_I256_S_MASK 0xffffffff
+#define SECURE_INTRCONN_I224_I256_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK0 */
+#define NORMAL_INTRCONN_I0_I31_S_SFT 0
+#define NORMAL_INTRCONN_I0_I31_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I0_I31_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK1 */
+#define NORMAL_INTRCONN_I32_I63_S_SFT 0
+#define NORMAL_INTRCONN_I32_I63_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I32_I63_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK2 */
+#define NORMAL_INTRCONN_I64_I95_S_SFT 0
+#define NORMAL_INTRCONN_I64_I95_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I64_I95_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK3 */
+#define NORMAL_INTRCONN_I96_I127_S_SFT 0
+#define NORMAL_INTRCONN_I96_I127_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I96_I127_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK4 */
+#define NORMAL_INTRCONN_I128_I159_S_SFT 0
+#define NORMAL_INTRCONN_I128_I159_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I128_I159_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK5 */
+#define NORMAL_INTRCONN_I160_I191_S_SFT 0
+#define NORMAL_INTRCONN_I160_I191_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I160_I191_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK6 */
+#define NORMAL_INTRCONN_I192_I223_S_SFT 0
+#define NORMAL_INTRCONN_I192_I223_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I192_I223_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_NON_SE_CONN_INPUT_MASK7 */
+#define NORMAL_INTRCONN_I224_I256_S_SFT 0
+#define NORMAL_INTRCONN_I224_I256_S_MASK 0xffffffff
+#define NORMAL_INTRCONN_I224_I256_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL0 */
+#define SECURE_INTRCONN_O0_O31_S_SFT 0
+#define SECURE_INTRCONN_O0_O31_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O0_O31_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL1 */
+#define SECURE_INTRCONN_O32_O63_S_SFT 0
+#define SECURE_INTRCONN_O32_O63_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O32_O63_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL2 */
+#define SECURE_INTRCONN_O64_O95_S_SFT 0
+#define SECURE_INTRCONN_O64_O95_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O64_O95_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL3 */
+#define SECURE_INTRCONN_O96_O127_S_SFT 0
+#define SECURE_INTRCONN_O96_O127_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O96_O127_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL4 */
+#define SECURE_INTRCONN_O128_O159_S_SFT 0
+#define SECURE_INTRCONN_O128_O159_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O128_O159_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL5 */
+#define SECURE_INTRCONN_O160_O191_S_SFT 0
+#define SECURE_INTRCONN_O160_O191_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O160_O191_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL6 */
+#define SECURE_INTRCONN_O192_O223_S_SFT 0
+#define SECURE_INTRCONN_O192_O223_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O192_O223_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_SE_CONN_OUTPUT_SEL7 */
+#define SECURE_INTRCONN_O224_O256_S_SFT 0
+#define SECURE_INTRCONN_O224_O256_S_MASK 0xffffffff
+#define SECURE_INTRCONN_O224_O256_S_MASK_SFT (0xffffffff << 0)
+
+/* AFE_PCM0_INTF_CON1_MASK_MON */
+#define AFE_PCM0_INTF_CON1_MASK_MON_SFT 0
+#define AFE_PCM0_INTF_CON1_MASK_MON_MASK 0xffffffff
+#define AFE_PCM0_INTF_CON1_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_PCM0_INTF_CON0_MASK_MON */
+#define AFE_PCM0_INTF_CON0_MASK_MON_SFT 0
+#define AFE_PCM0_INTF_CON0_MASK_MON_MASK 0xffffffff
+#define AFE_PCM0_INTF_CON0_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_CONNSYS_I2S_CON_MASK_MON */
+#define AFE_CONNSYS_I2S_CON_MASK_MON_SFT 0
+#define AFE_CONNSYS_I2S_CON_MASK_MON_MASK 0xffffffff
+#define AFE_CONNSYS_I2S_CON_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_MTKAIF0_CFG0_MASK_MON */
+#define AFE_MTKAIF0_CFG0_MASK_MON_SFT 0
+#define AFE_MTKAIF0_CFG0_MASK_MON_MASK 0xffffffff
+#define AFE_MTKAIF0_CFG0_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_MTKAIF1_CFG0_MASK_MON */
+#define AFE_MTKAIF1_CFG0_MASK_MON_SFT 0
+#define AFE_MTKAIF1_CFG0_MASK_MON_MASK 0xffffffff
+#define AFE_MTKAIF1_CFG0_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL0_SRC_CON0_MASK_MON */
+#define AFE_ADDA_UL0_SRC_CON0_MASK_MON_SFT 0
+#define AFE_ADDA_UL0_SRC_CON0_MASK_MON_MASK 0xffffffff
+#define AFE_ADDA_UL0_SRC_CON0_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_ADDA_UL1_SRC_CON0_MASK_MON */
+#define AFE_ADDA_UL1_SRC_CON0_MASK_MON_SFT 0
+#define AFE_ADDA_UL1_SRC_CON0_MASK_MON_MASK 0xffffffff
+#define AFE_ADDA_UL1_SRC_CON0_MASK_MON_MASK_SFT (0xffffffff << 0)
+
+/* AFE_GASRC0_NEW_CON0 */
+#define ONE_HEART_SFT 31
+#define ONE_HEART_MASK 0x1
+#define ONE_HEART_MASK_SFT (0x1 << 31)
+#define CHSET0_OFS_ONE_HEART_DISABLE_SFT 30
+#define CHSET0_OFS_ONE_HEART_DISABLE_MASK 0x1
+#define CHSET0_OFS_ONE_HEART_DISABLE_MASK_SFT (0x1 << 30)
+#define USE_SHORT_DELAY_COEFF_SFT 29
+#define USE_SHORT_DELAY_COEFF_MASK 0x1
+#define USE_SHORT_DELAY_COEFF_MASK_SFT (0x1 << 29)
+#define CHSET0_O16BIT_SFT 19
+#define CHSET0_O16BIT_MASK 0x1
+#define CHSET0_O16BIT_MASK_SFT (0x1 << 19)
+#define CHSET0_CLR_IIR_HISTORY_SFT 17
+#define CHSET0_CLR_IIR_HISTORY_MASK 0x1
+#define CHSET0_CLR_IIR_HISTORY_MASK_SFT (0x1 << 17)
+#define CHSET0_IS_MONO_SFT 16
+#define CHSET0_IS_MONO_MASK 0x1
+#define CHSET0_IS_MONO_MASK_SFT (0x1 << 16)
+#define CHSET0_OFS_SEL_SFT 14
+#define CHSET0_OFS_SEL_MASK 0x3
+#define CHSET0_OFS_SEL_MASK_SFT (0x3 << 14)
+#define CHSET0_IFS_SEL_SFT 12
+#define CHSET0_IFS_SEL_MASK 0x3
+#define CHSET0_IFS_SEL_MASK_SFT (0x3 << 12)
+#define CHSET0_IIR_EN_SFT 11
+#define CHSET0_IIR_EN_MASK 0x1
+#define CHSET0_IIR_EN_MASK_SFT (0x1 << 11)
+#define CHSET0_IIR_STAGE_SFT 8
+#define CHSET0_IIR_STAGE_MASK 0x7
+#define CHSET0_IIR_STAGE_MASK_SFT (0x7 << 8)
+#define ASM_ON_MOD_SFT 7
+#define ASM_ON_MOD_MASK 0x1
+#define ASM_ON_MOD_MASK_SFT (0x1 << 7)
+#define CHSET_STR_CLR_SFT 4
+#define CHSET_STR_CLR_MASK 0x1
+#define CHSET_STR_CLR_MASK_SFT (0x1 << 4)
+#define CHSET_ON_SFT 2
+#define CHSET_ON_MASK 0x1
+#define CHSET_ON_MASK_SFT (0x1 << 2)
+#define COEFF_SRAM_CTRL_SFT 1
+#define COEFF_SRAM_CTRL_MASK 0x1
+#define COEFF_SRAM_CTRL_MASK_SFT (0x1 << 1)
+#define ASM_ON_SFT 0
+#define ASM_ON_MASK 0x1
+#define ASM_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GASRC0_NEW_CON1 */
+#define ASM_FREQ_0_SFT 0
+#define ASM_FREQ_0_MASK 0xffffff
+#define ASM_FREQ_0_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON2 */
+#define ASM_FREQ_1_SFT 0
+#define ASM_FREQ_1_MASK 0xffffff
+#define ASM_FREQ_1_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON3 */
+#define ASM_FREQ_2_SFT 0
+#define ASM_FREQ_2_MASK 0xffffff
+#define ASM_FREQ_2_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON4 */
+#define ASM_FREQ_3_SFT 0
+#define ASM_FREQ_3_MASK 0xffffff
+#define ASM_FREQ_3_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON5 */
+#define OUT_EN_SEL_DOMAIN_SFT 29
+#define OUT_EN_SEL_DOMAIN_MASK 0x7
+#define OUT_EN_SEL_DOMAIN_MASK_SFT (0x7 << 29)
+#define OUT_EN_SEL_FS_SFT 24
+#define OUT_EN_SEL_FS_MASK 0x1f
+#define OUT_EN_SEL_FS_MASK_SFT (0x1f << 24)
+#define IN_EN_SEL_DOMAIN_SFT 21
+#define IN_EN_SEL_DOMAIN_MASK 0x7
+#define IN_EN_SEL_DOMAIN_MASK_SFT (0x7 << 21)
+#define IN_EN_SEL_FS_SFT 16
+#define IN_EN_SEL_FS_MASK 0x1f
+#define IN_EN_SEL_FS_MASK_SFT (0x1f << 16)
+#define RESULT_SEL_SFT 8
+#define RESULT_SEL_MASK 0x7
+#define RESULT_SEL_MASK_SFT (0x7 << 8)
+#define CALI_CK_SEL_SFT 4
+#define CALI_CK_SEL_MASK 0x7
+#define CALI_CK_SEL_MASK_SFT (0x7 << 4)
+#define CALI_LRCK_SEL_SFT 1
+#define CALI_LRCK_SEL_MASK 0x7
+#define CALI_LRCK_SEL_MASK_SFT (0x7 << 1)
+#define SOFT_RESET_SFT 0
+#define SOFT_RESET_MASK 0x1
+#define SOFT_RESET_MASK_SFT (0x1 << 0)
+
+/* AFE_GASRC0_NEW_CON6 */
+#define FREQ_CALI_CYCLE_SFT 16
+#define FREQ_CALI_CYCLE_MASK 0xffff
+#define FREQ_CALI_CYCLE_MASK_SFT (0xffff << 16)
+#define FREQ_CALI_AUTORST_EN_SFT 15
+#define FREQ_CALI_AUTORST_EN_MASK 0x1
+#define FREQ_CALI_AUTORST_EN_MASK_SFT (0x1 << 15)
+#define CALI_AUTORST_DETECT_SFT 14
+#define CALI_AUTORST_DETECT_MASK 0x1
+#define CALI_AUTORST_DETECT_MASK_SFT (0x1 << 14)
+#define FREQ_CALC_RUNNING_SFT 13
+#define FREQ_CALC_RUNNING_MASK 0x1
+#define FREQ_CALC_RUNNING_MASK_SFT (0x1 << 13)
+#define AUTO_TUNE_FREQ3_SFT 12
+#define AUTO_TUNE_FREQ3_MASK 0x1
+#define AUTO_TUNE_FREQ3_MASK_SFT (0x1 << 12)
+#define COMP_FREQ_RES_EN_SFT 11
+#define COMP_FREQ_RES_EN_MASK 0x1
+#define COMP_FREQ_RES_EN_MASK_SFT (0x1 << 11)
+#define FREQ_CALI_SEL_SFT 8
+#define FREQ_CALI_SEL_MASK 0x3
+#define FREQ_CALI_SEL_MASK_SFT (0x3 << 8)
+#define FREQ_CALI_BP_DGL_SFT 7
+#define FREQ_CALI_BP_DGL_MASK 0x1
+#define FREQ_CALI_BP_DGL_MASK_SFT (0x1 << 7)
+#define FREQ_CALI_MAX_GWIDTH_SFT 4
+#define FREQ_CALI_MAX_GWIDTH_MASK 0x7
+#define FREQ_CALI_MAX_GWIDTH_MASK_SFT (0x7 << 4)
+#define AUTO_TUNE_FREQ2_SFT 3
+#define AUTO_TUNE_FREQ2_MASK 0x1
+#define AUTO_TUNE_FREQ2_MASK_SFT (0x1 << 3)
+#define FREQ_CALI_AUTO_RESTART_SFT 2
+#define FREQ_CALI_AUTO_RESTART_MASK 0x1
+#define FREQ_CALI_AUTO_RESTART_MASK_SFT (0x1 << 2)
+#define CALI_USE_FREQ_OUT_SFT 1
+#define CALI_USE_FREQ_OUT_MASK 0x1
+#define CALI_USE_FREQ_OUT_MASK_SFT (0x1 << 1)
+#define CALI_EN_SFT 0
+#define CALI_EN_MASK 0x1
+#define CALI_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_GASRC0_NEW_CON7 */
+#define FREQ_CALC_DENOMINATOR_SFT 0
+#define FREQ_CALC_DENOMINATOR_MASK 0xffffff
+#define FREQ_CALC_DENOMINATOR_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON8 */
+#define PRD_CALI_RESULT_RECORD_SFT 0
+#define PRD_CALI_RESULT_RECORD_MASK 0xffffff
+#define PRD_CALI_RESULT_RECORD_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON9 */
+#define FREQ_CALI_RESULT_SFT 0
+#define FREQ_CALI_RESULT_MASK 0xffffff
+#define FREQ_CALI_RESULT_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON10 */
+#define COEFF_SRAM_DATA_SFT 0
+#define COEFF_SRAM_DATA_MASK 0xffffffff
+#define COEFF_SRAM_DATA_MASK_SFT (0xffffffff << 0)
+
+/* AFE_GASRC0_NEW_CON11 */
+#define COEFF_SRAM_ADR_SFT 0
+#define COEFF_SRAM_ADR_MASK 0x3f
+#define COEFF_SRAM_ADR_MASK_SFT (0x3f << 0)
+
+/* AFE_GASRC0_NEW_CON12 */
+#define RING_DBG_RD_SFT 0
+#define RING_DBG_RD_MASK 0x3ffffff
+#define RING_DBG_RD_MASK_SFT (0x3ffffff << 0)
+
+/* AFE_GASRC0_NEW_CON13 */
+#define FREQ_CALI_AUTORST_TH_HIGH_SFT 0
+#define FREQ_CALI_AUTORST_TH_HIGH_MASK 0xffffff
+#define FREQ_CALI_AUTORST_TH_HIGH_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_CON14 */
+#define FREQ_CALI_AUTORST_TH_LOW_SFT 0
+#define FREQ_CALI_AUTORST_TH_LOW_MASK 0xffffff
+#define FREQ_CALI_AUTORST_TH_LOW_MASK_SFT (0xffffff << 0)
+
+/* AFE_GASRC0_NEW_IP_VERSION */
+#define IP_VERSION_SFT 0
+#define IP_VERSION_MASK 0xffffffff
+#define IP_VERSION_MASK_SFT (0xffffffff << 0)
+
+#define AUDIO_TOP_CON0 0x0
+#define AUDIO_TOP_CON1 0x4
+#define AUDIO_TOP_CON2 0x8
+#define AUDIO_TOP_CON3 0xc
+#define AUDIO_TOP_CON4 0x10
+#define AUDIO_ENGEN_CON0 0x14
+#define AUDIO_ENGEN_CON0_USER1 0x18
+#define AUDIO_ENGEN_CON0_USER2 0x1c
+#define AFE_SINEGEN_CON0 0x20
+#define AFE_SINEGEN_CON1 0x24
+#define AFE_SINEGEN_CON2 0x28
+#define AFE_SINEGEN_CON3 0x2c
+#define AFE_APLL1_TUNER_CFG 0x30
+#define AFE_APLL1_TUNER_MON0 0x34
+#define AFE_APLL2_TUNER_CFG 0x38
+#define AFE_APLL2_TUNER_MON0 0x3c
+#define AUDIO_TOP_RG0 0x4c
+#define AUDIO_TOP_RG1 0x50
+#define AUDIO_TOP_RG2 0x54
+#define AUDIO_TOP_RG3 0x58
+#define AUDIO_TOP_RG4 0x5c
+#define AFE_SPM_CONTROL_REQ 0x60
+#define AFE_SPM_CONTROL_ACK 0x64
+#define AUD_TOP_CFG_VCORE_RG 0x68
+#define AUDIO_TOP_IP_VERSION 0x6c
+#define AUDIO_ENGEN_CON0_MON 0x7c
+#define AUDIO_PROJECT_MON 0x80
+#define AUD_TOP_CFG_VLP_RG 0x98
+#define AUD_TOP_MON_RG 0x9c
+#define AUDIO_USE_DEFAULT_DELSEL0 0xa0
+#define AUDIO_USE_DEFAULT_DELSEL1 0xa4
+#define AUDIO_USE_DEFAULT_DELSEL2 0xa8
+#define AFE_CONNSYS_I2S_IPM_VER_MON 0xb0
+#define AFE_CONNSYS_I2S_MON_SEL 0xb4
+#define AFE_CONNSYS_I2S_MON 0xb8
+#define AFE_CONNSYS_I2S_CON 0xbc
+#define AFE_PCM0_INTF_CON0 0xc0
+#define AFE_PCM0_INTF_CON1 0xc4
+#define AFE_PCM_INTF_MON 0xc8
+#define AFE_PCM_TOP_IP_VERSION 0xe8
+#define AFE_GAIN0_CON0 0x400
+#define AFE_GAIN0_CON1_R 0x404
+#define AFE_GAIN0_CON1_L 0x408
+#define AFE_GAIN0_CON2 0x40c
+#define AFE_GAIN0_CON3 0x410
+#define AFE_GAIN0_CUR_R 0x414
+#define AFE_GAIN0_CUR_L 0x418
+#define AFE_GAIN1_CON0 0x41c
+#define AFE_GAIN1_CON1_R 0x420
+#define AFE_GAIN1_CON1_L 0x424
+#define AFE_GAIN1_CON2 0x428
+#define AFE_GAIN1_CON3 0x42c
+#define AFE_GAIN1_CUR_R 0x430
+#define AFE_GAIN1_CUR_L 0x434
+#define AFE_GAIN2_CON0 0x438
+#define AFE_GAIN2_CON1_R 0x43c
+#define AFE_GAIN2_CON1_L 0x440
+#define AFE_GAIN2_CON2 0x444
+#define AFE_GAIN2_CON3 0x448
+#define AFE_GAIN2_CUR_R 0x44c
+#define AFE_GAIN2_CUR_L 0x450
+#define AFE_GAIN3_CON0 0x454
+#define AFE_GAIN3_CON1_R 0x458
+#define AFE_GAIN3_CON1_L 0x45c
+#define AFE_GAIN3_CON2 0x460
+#define AFE_GAIN3_CON3 0x464
+#define AFE_GAIN3_CUR_R 0x468
+#define AFE_GAIN3_CUR_L 0x46c
+#define AFE_GAIN_0_1_IP_VERSION 0x474
+#define AFE_GAIN_2_3_IP_VERSION 0x478
+#define AFE_ADDA_DL_IPM_VER_MON 0x4c0
+#define AFE_ADDA_DL_SRC_CON0 0x4d0
+#define AFE_ADDA_DL_SRC_CON1 0x4d4
+#define AFE_ADDA_DL_SRC_DEBUG_MON0 0x4d8
+#define AFE_ADDA_DL_PREDIS_CON0 0x4dc
+#define AFE_ADDA_DL_PREDIS_CON1 0x4e0
+#define AFE_ADDA_DL_PREDIS_CON2 0x4e4
+#define AFE_ADDA_DL_PREDIS_CON3 0x4e8
+#define AFE_ADDA_DL_SDM_DCCOMP_CON 0x4ec
+#define AFE_ADDA_DL_SDM_TEST 0x4f0
+#define AFE_ADDA_DL_DC_COMP_CFG0 0x4f4
+#define AFE_ADDA_DL_DC_COMP_CFG1 0x4f8
+#define AFE_ADDA_DL_SDM_OUT_MON 0x4fc
+#define AFE_ADDA_DL_SRC_LCH_MON 0x500
+#define AFE_ADDA_DL_SRC_RCH_MON 0x504
+#define AFE_ADDA_DL_SRC_DEBUG 0x508
+#define AFE_ADDA_DL_SDM_DITHER_CON 0x50c
+#define AFE_ADDA_DL_SDM_AUTO_RESET_CON 0x510
+#define AFE_ADDA_DL_HBF1_SCF1_CONFIG 0x514
+#define AFE_ADDA_DL_HBF1_SCF1_TAP1_TAP2_CONFIG 0x518
+#define AFE_ADDA_DL_HBF1_SCF1_TAP3_TAP4_CONFIG 0x51c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP5_TAP6_CONFIG 0x520
+#define AFE_ADDA_DL_HBF1_SCF1_TAP7_TAP8_CONFIG 0x524
+#define AFE_ADDA_DL_HBF1_SCF1_TAP9_TAP10_CONFIG 0x528
+#define AFE_ADDA_DL_HBF1_SCF1_TAP11_TAP12_CONFIG 0x52c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP13_TAP14_CONFIG 0x530
+#define AFE_ADDA_DL_HBF1_SCF1_TAP15_TAP16_CONFIG 0x534
+#define AFE_ADDA_DL_HBF1_SCF1_TAP17_TAP18_CONFIG 0x538
+#define AFE_ADDA_DL_HBF1_SCF1_TAP19_TAP20_CONFIG 0x53c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP21_TAP22_CONFIG 0x540
+#define AFE_ADDA_DL_HBF1_SCF1_TAP23_TAP24_CONFIG 0x544
+#define AFE_ADDA_DL_HBF1_SCF1_TAP25_TAP26_CONFIG 0x548
+#define AFE_ADDA_DL_HBF1_SCF1_TAP27_TAP28_CONFIG 0x54c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP29_TAP30_CONFIG 0x550
+#define AFE_ADDA_DL_HBF1_SCF1_TAP31_TAP32_CONFIG 0x554
+#define AFE_ADDA_DL_HBF1_SCF1_TAP33_TAP34_CONFIG 0x558
+#define AFE_ADDA_DL_HBF1_SCF1_TAP35_TAP36_CONFIG 0x55c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP37_TAP38_CONFIG 0x560
+#define AFE_ADDA_DL_HBF1_SCF1_TAP39_TAP40_CONFIG 0x564
+#define AFE_ADDA_DL_HBF1_SCF1_TAP41_TAP42_CONFIG 0x568
+#define AFE_ADDA_DL_HBF1_SCF1_TAP43_TAP44_CONFIG 0x56c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP45_TAP46_CONFIG 0x570
+#define AFE_ADDA_DL_HBF1_SCF1_TAP47_TAP48_CONFIG 0x574
+#define AFE_ADDA_DL_HBF1_SCF1_TAP49_TAP50_CONFIG 0x578
+#define AFE_ADDA_DL_HBF1_SCF1_TAP51_TAP52_CONFIG 0x57c
+#define AFE_ADDA_DL_HBF1_SCF1_TAP53_TAP54_CONFIG 0x580
+#define AFE_ADDA_DL_HBF1_SCF1_TAP55_TAP56_CONFIG 0x584
+#define AFE_DEM_IDWA_CON0 0xa1c
+#define DEM_RECONSTRUCT_MON 0xa20
+#define AFE_CM0_CON0 0xba0
+#define AFE_CM0_MON 0xba4
+#define AFE_CM0_IP_VERSION 0xba8
+#define AFE_CM1_CON0 0xbb0
+#define AFE_CM1_MON 0xbb4
+#define AFE_CM1_IP_VERSION 0xbb8
+#define AFE_ADDA_UL0_SRC_CON0 0xbd0
+#define AFE_ADDA_UL0_SRC_CON1 0xbd4
+#define AFE_ADDA_UL0_SRC_CON2 0xbd8
+#define AFE_ADDA_UL0_SRC_DEBUG 0xbdc
+#define AFE_ADDA_UL0_SRC_DEBUG_MON0 0xbe0
+#define AFE_ADDA_UL0_SRC_MON0 0xbe4
+#define AFE_ADDA_UL0_SRC_MON1 0xbe8
+#define AFE_ADDA_UL0_IIR_COEF_02_01 0xbec
+#define AFE_ADDA_UL0_IIR_COEF_04_03 0xbf0
+#define AFE_ADDA_UL0_IIR_COEF_06_05 0xbf4
+#define AFE_ADDA_UL0_IIR_COEF_08_07 0xbf8
+#define AFE_ADDA_UL0_IIR_COEF_10_09 0xbfc
+#define AFE_ADDA_UL0_ULCF_CFG_02_01 0xc00
+#define AFE_ADDA_UL0_ULCF_CFG_04_03 0xc04
+#define AFE_ADDA_UL0_ULCF_CFG_06_05 0xc08
+#define AFE_ADDA_UL0_ULCF_CFG_08_07 0xc0c
+#define AFE_ADDA_UL0_ULCF_CFG_10_09 0xc10
+#define AFE_ADDA_UL0_ULCF_CFG_12_11 0xc14
+#define AFE_ADDA_UL0_ULCF_CFG_14_13 0xc18
+#define AFE_ADDA_UL0_ULCF_CFG_16_15 0xc1c
+#define AFE_ADDA_UL0_ULCF_CFG_18_17 0xc20
+#define AFE_ADDA_UL0_ULCF_CFG_20_19 0xc24
+#define AFE_ADDA_UL0_ULCF_CFG_22_21 0xc28
+#define AFE_ADDA_UL0_ULCF_CFG_24_23 0xc2c
+#define AFE_ADDA_UL0_ULCF_CFG_26_25 0xc30
+#define AFE_ADDA_UL0_ULCF_CFG_28_27 0xc34
+#define AFE_ADDA_UL0_ULCF_CFG_30_29 0xc38
+#define AFE_ADDA_UL0_ULCF_CFG_32_31 0xc3c
+#define AFE_ADDA_UL0_IP_VERSION 0xc4c
+#define AFE_ADDA_DMIC0_SRC_CON0 0xdd0
+#define AFE_ADDA_DMIC0_SRC_CON1 0xdd4
+#define AFE_ADDA_DMIC0_SRC_CON2 0xdd8
+#define AFE_ADDA_DMIC0_SRC_DEBUG 0xddc
+#define AFE_ADDA_DMIC0_SRC_DEBUG_MON0 0xde0
+#define AFE_ADDA_DMIC0_SRC_MON0 0xde4
+#define AFE_ADDA_DMIC0_SRC_MON1 0xde8
+#define AFE_ADDA_DMIC0_IIR_COEF_02_01 0xdec
+#define AFE_ADDA_DMIC0_IIR_COEF_04_03 0xdf0
+#define AFE_ADDA_DMIC0_IIR_COEF_06_05 0xdf4
+#define AFE_ADDA_DMIC0_IIR_COEF_08_07 0xdf8
+#define AFE_ADDA_DMIC0_IIR_COEF_10_09 0xdfc
+#define AFE_ADDA_DMIC0_ULCF_CFG_02_01 0xe00
+#define AFE_ADDA_DMIC0_ULCF_CFG_04_03 0xe04
+#define AFE_ADDA_DMIC0_ULCF_CFG_06_05 0xe08
+#define AFE_ADDA_DMIC0_ULCF_CFG_08_07 0xe0c
+#define AFE_ADDA_DMIC0_ULCF_CFG_10_09 0xe10
+#define AFE_ADDA_DMIC0_ULCF_CFG_12_11 0xe14
+#define AFE_ADDA_DMIC0_ULCF_CFG_14_13 0xe18
+#define AFE_ADDA_DMIC0_ULCF_CFG_16_15 0xe1c
+#define AFE_ADDA_DMIC0_ULCF_CFG_18_17 0xe20
+#define AFE_ADDA_DMIC0_ULCF_CFG_20_19 0xe24
+#define AFE_ADDA_DMIC0_ULCF_CFG_22_21 0xe28
+#define AFE_ADDA_DMIC0_ULCF_CFG_24_23 0xe2c
+#define AFE_ADDA_DMIC0_ULCF_CFG_26_25 0xe30
+#define AFE_ADDA_DMIC0_ULCF_CFG_28_27 0xe34
+#define AFE_ADDA_DMIC0_ULCF_CFG_30_29 0xe38
+#define AFE_ADDA_DMIC0_ULCF_CFG_32_31 0xe3c
+#define AFE_ADDA_DMIC0_IP_VERSION 0xe4c
+#define AFE_ADDA_DMIC1_SRC_CON0 0xe50
+#define AFE_ADDA_DMIC1_SRC_CON1 0xe54
+#define AFE_ADDA_DMIC1_SRC_CON2 0xe58
+#define AFE_ADDA_DMIC1_SRC_DEBUG 0xe5c
+#define AFE_ADDA_DMIC1_SRC_DEBUG_MON0 0xe60
+#define AFE_ADDA_DMIC1_SRC_MON0 0xe64
+#define AFE_ADDA_DMIC1_SRC_MON1 0xe68
+#define AFE_ADDA_DMIC1_IIR_COEF_02_01 0xe6c
+#define AFE_ADDA_DMIC1_IIR_COEF_04_03 0xe70
+#define AFE_ADDA_DMIC1_IIR_COEF_06_05 0xe74
+#define AFE_ADDA_DMIC1_IIR_COEF_08_07 0xe78
+#define AFE_ADDA_DMIC1_IIR_COEF_10_09 0xe7c
+#define AFE_ADDA_DMIC1_ULCF_CFG_02_01 0xe80
+#define AFE_ADDA_DMIC1_ULCF_CFG_04_03 0xe84
+#define AFE_ADDA_DMIC1_ULCF_CFG_06_05 0xe88
+#define AFE_ADDA_DMIC1_ULCF_CFG_08_07 0xe8c
+#define AFE_ADDA_DMIC1_ULCF_CFG_10_09 0xe90
+#define AFE_ADDA_DMIC1_ULCF_CFG_12_11 0xe94
+#define AFE_ADDA_DMIC1_ULCF_CFG_14_13 0xe98
+#define AFE_ADDA_DMIC1_ULCF_CFG_16_15 0xe9c
+#define AFE_ADDA_DMIC1_ULCF_CFG_18_17 0xea0
+#define AFE_ADDA_DMIC1_ULCF_CFG_20_19 0xea4
+#define AFE_ADDA_DMIC1_ULCF_CFG_22_21 0xea8
+#define AFE_ADDA_DMIC1_ULCF_CFG_24_23 0xeac
+#define AFE_ADDA_DMIC1_ULCF_CFG_26_25 0xeb0
+#define AFE_ADDA_DMIC1_ULCF_CFG_28_27 0xeb4
+#define AFE_ADDA_DMIC1_ULCF_CFG_30_29 0xeb8
+#define AFE_ADDA_DMIC1_ULCF_CFG_32_31 0xebc
+#define AFE_ADDA_DMIC1_IP_VERSION 0xecc
+#define AFE_ADDA_ULSRC_PHASE_CLK_CON0 0xf00
+#define AFE_ADDA_ULSRC_PHASE_CLK_CON1 0xf04
+#define AFE_ADDA_ULSRC_PHASE_CLK_CON2 0xf08
+#define AFE_ADDA_ULSRC_PHASE_CLK_CON3 0xf0c
+#define AFE_ADDA_ULSRC_PHASE_CLK_CON4 0xf10
+#define AFE_ADDA_ULSRC_PHASE_ENGEN_CON0 0xf14
+#define AFE_ADDA_ULSRC_PHASE_ENGEN_CON1 0xf18
+#define AFE_ADDA_ULSRC_PHASE_RST_CON0 0xf1c
+#define AFE_MTKAIF_IPM_VER_MON 0x1180
+#define AFE_MTKAIF_MON_SEL 0x1184
+#define AFE_MTKAIF_MON 0x1188
+#define AFE_MTKAIF0_CFG0 0x1190
+#define AFE_MTKAIF0_TX_CFG0 0x1194
+#define AFE_MTKAIF0_RX_CFG0 0x1198
+#define AFE_MTKAIF0_RX_CFG1 0x119c
+#define AFE_MTKAIF0_RX_CFG2 0x11a0
+#define AFE_MTKAIF1_CFG0 0x11f0
+#define AFE_MTKAIF1_TX_CFG0 0x11f4
+#define AFE_MTKAIF1_RX_CFG0 0x11f8
+#define AFE_MTKAIF1_RX_CFG1 0x11fc
+#define AFE_MTKAIF1_RX_CFG2 0x1200
+#define AFE_AUD_PAD_TOP_CFG0 0x1204
+#define AFE_AUD_PAD_TOP_MON 0x1208
+#define AFE_ADDA_MTKAIFV4_TX_CFG0 0x1280
+#define AFE_ADDA6_MTKAIFV4_TX_CFG0 0x1284
+#define AFE_ADDA_MTKAIFV4_RX_CFG0 0x1288
+#define AFE_ADDA_MTKAIFV4_RX_CFG1 0x128c
+#define AFE_ADDA6_MTKAIFV4_RX_CFG0 0x1290
+#define AFE_ADDA6_MTKAIFV4_RX_CFG1 0x1294
+#define AFE_ADDA_MTKAIFV4_TX_SYNCWORD_CFG 0x1298
+#define AFE_ADDA_MTKAIFV4_RX_SYNCWORD_CFG 0x129c
+#define AFE_ADDA_MTKAIFV4_MON0 0x12a0
+#define AFE_ADDA_MTKAIFV4_MON1 0x12a4
+#define AFE_ADDA6_MTKAIFV4_MON0 0x12a8
+#define ETDM_IN0_CON0 0x1300
+#define ETDM_IN0_CON1 0x1304
+#define ETDM_IN0_CON2 0x1308
+#define ETDM_IN0_CON3 0x130c
+#define ETDM_IN0_CON4 0x1310
+#define ETDM_IN0_CON5 0x1314
+#define ETDM_IN0_CON6 0x1318
+#define ETDM_IN0_CON7 0x131c
+#define ETDM_IN0_CON8 0x1320
+#define ETDM_IN0_CON9 0x1324
+#define ETDM_IN0_MON 0x1328
+#define ETDM_IN1_CON0 0x1330
+#define ETDM_IN1_CON1 0x1334
+#define ETDM_IN1_CON2 0x1338
+#define ETDM_IN1_CON3 0x133c
+#define ETDM_IN1_CON4 0x1340
+#define ETDM_IN1_CON5 0x1344
+#define ETDM_IN1_CON6 0x1348
+#define ETDM_IN1_CON7 0x134c
+#define ETDM_IN1_CON8 0x1350
+#define ETDM_IN1_CON9 0x1354
+#define ETDM_IN1_MON 0x1358
+#define ETDM_OUT0_CON0 0x1480
+#define ETDM_OUT0_CON1 0x1484
+#define ETDM_OUT0_CON2 0x1488
+#define ETDM_OUT0_CON3 0x148c
+#define ETDM_OUT0_CON4 0x1490
+#define ETDM_OUT0_CON5 0x1494
+#define ETDM_OUT0_CON6 0x1498
+#define ETDM_OUT0_CON7 0x149c
+#define ETDM_OUT0_CON8 0x14a0
+#define ETDM_OUT0_CON9 0x14a4
+#define ETDM_OUT0_MON 0x14a8
+#define ETDM_OUT1_CON0 0x14c0
+#define ETDM_OUT1_CON1 0x14c4
+#define ETDM_OUT1_CON2 0x14c8
+#define ETDM_OUT1_CON3 0x14cc
+#define ETDM_OUT1_CON4 0x14d0
+#define ETDM_OUT1_CON5 0x14d4
+#define ETDM_OUT1_CON6 0x14d8
+#define ETDM_OUT1_CON7 0x14dc
+#define ETDM_OUT1_CON8 0x14e0
+#define ETDM_OUT1_CON9 0x14e4
+#define ETDM_OUT1_MON 0x14e8
+#define ETDM_OUT4_CON0 0x1580
+#define ETDM_OUT4_CON1 0x1584
+#define ETDM_OUT4_CON2 0x1588
+#define ETDM_OUT4_CON3 0x158c
+#define ETDM_OUT4_CON4 0x1590
+#define ETDM_OUT4_CON5 0x1594
+#define ETDM_OUT4_CON6 0x1598
+#define ETDM_OUT4_CON7 0x159c
+#define ETDM_OUT4_CON8 0x15a0
+#define ETDM_OUT4_CON9 0x15a4
+#define ETDM_OUT4_MON 0x15a8
+#define ETDM_0_3_COWORK_CON0 0x1680
+#define ETDM_0_3_COWORK_CON1 0x1684
+#define ETDM_0_3_COWORK_CON2 0x1688
+#define ETDM_0_3_COWORK_CON3 0x168c
+#define ETDM_4_7_COWORK_CON0 0x1690
+#define ETDM_4_7_COWORK_CON1 0x1694
+#define ETDM_4_7_COWORK_CON2 0x1698
+#define ETDM_4_7_COWORK_CON3 0x169c
+#define ETDM_IP_VERSION 0x1c4c
+#define AFE_DPTX_CON 0x2040
+#define AFE_DPTX_MON 0x2044
+#define AFE_TDM_CON1 0x2048
+#define AFE_TDM_CON2 0x204c
+#define AFE_TDM_CON3 0x2050
+#define AFE_TDM_OUT_MON 0x2054
+#define AFE_HDMI_CONN0 0x2078
+#define AFE_TDM_TOP_IP_VERSION 0x207c
+#define AFE_CONN004_0 0x2100
+#define AFE_CONN004_1 0x2104
+#define AFE_CONN004_2 0x2108
+#define AFE_CONN004_4 0x2110
+#define AFE_CONN004_6 0x2118
+#define AFE_CONN005_0 0x2120
+#define AFE_CONN005_1 0x2124
+#define AFE_CONN005_2 0x2128
+#define AFE_CONN005_4 0x2130
+#define AFE_CONN005_6 0x2138
+#define AFE_CONN006_0 0x2140
+#define AFE_CONN006_1 0x2144
+#define AFE_CONN006_2 0x2148
+#define AFE_CONN006_4 0x2150
+#define AFE_CONN006_6 0x2158
+#define AFE_CONN007_0 0x2160
+#define AFE_CONN007_1 0x2164
+#define AFE_CONN007_2 0x2168
+#define AFE_CONN007_4 0x2170
+#define AFE_CONN007_6 0x2178
+#define AFE_CONN008_0 0x2180
+#define AFE_CONN008_1 0x2184
+#define AFE_CONN008_2 0x2188
+#define AFE_CONN008_4 0x2190
+#define AFE_CONN008_6 0x2198
+#define AFE_CONN009_0 0x21a0
+#define AFE_CONN009_1 0x21a4
+#define AFE_CONN009_2 0x21a8
+#define AFE_CONN009_4 0x21b0
+#define AFE_CONN009_6 0x21b8
+#define AFE_CONN010_0 0x21c0
+#define AFE_CONN010_1 0x21c4
+#define AFE_CONN010_2 0x21c8
+#define AFE_CONN010_4 0x21d0
+#define AFE_CONN010_6 0x21d8
+#define AFE_CONN011_0 0x21e0
+#define AFE_CONN011_1 0x21e4
+#define AFE_CONN011_2 0x21e8
+#define AFE_CONN011_4 0x21f0
+#define AFE_CONN011_6 0x21f8
+#define AFE_CONN014_0 0x2240
+#define AFE_CONN014_1 0x2244
+#define AFE_CONN014_2 0x2248
+#define AFE_CONN014_4 0x2250
+#define AFE_CONN014_6 0x2258
+#define AFE_CONN015_0 0x2260
+#define AFE_CONN015_1 0x2264
+#define AFE_CONN015_2 0x2268
+#define AFE_CONN015_4 0x2270
+#define AFE_CONN015_6 0x2278
+#define AFE_CONN016_0 0x2280
+#define AFE_CONN016_1 0x2284
+#define AFE_CONN016_2 0x2288
+#define AFE_CONN016_4 0x2290
+#define AFE_CONN016_6 0x2298
+#define AFE_CONN017_0 0x22a0
+#define AFE_CONN017_1 0x22a4
+#define AFE_CONN017_2 0x22a8
+#define AFE_CONN017_4 0x22b0
+#define AFE_CONN017_6 0x22b8
+#define AFE_CONN018_0 0x22c0
+#define AFE_CONN018_1 0x22c4
+#define AFE_CONN018_2 0x22c8
+#define AFE_CONN018_4 0x22d0
+#define AFE_CONN018_6 0x22d8
+#define AFE_CONN019_0 0x22e0
+#define AFE_CONN019_1 0x22e4
+#define AFE_CONN019_2 0x22e8
+#define AFE_CONN019_4 0x22f0
+#define AFE_CONN019_6 0x22f8
+#define AFE_CONN020_0 0x2300
+#define AFE_CONN020_1 0x2304
+#define AFE_CONN020_2 0x2308
+#define AFE_CONN020_4 0x2310
+#define AFE_CONN020_6 0x2318
+#define AFE_CONN021_0 0x2320
+#define AFE_CONN021_1 0x2324
+#define AFE_CONN021_2 0x2328
+#define AFE_CONN021_4 0x2330
+#define AFE_CONN021_6 0x2338
+#define AFE_CONN022_0 0x2340
+#define AFE_CONN022_1 0x2344
+#define AFE_CONN022_2 0x2348
+#define AFE_CONN022_4 0x2350
+#define AFE_CONN022_6 0x2358
+#define AFE_CONN023_0 0x2360
+#define AFE_CONN023_1 0x2364
+#define AFE_CONN023_2 0x2368
+#define AFE_CONN023_4 0x2370
+#define AFE_CONN023_6 0x2378
+#define AFE_CONN024_0 0x2380
+#define AFE_CONN024_1 0x2384
+#define AFE_CONN024_2 0x2388
+#define AFE_CONN024_4 0x2390
+#define AFE_CONN024_6 0x2398
+#define AFE_CONN025_0 0x23a0
+#define AFE_CONN025_1 0x23a4
+#define AFE_CONN025_2 0x23a8
+#define AFE_CONN025_4 0x23b0
+#define AFE_CONN025_6 0x23b8
+#define AFE_CONN026_0 0x23c0
+#define AFE_CONN026_1 0x23c4
+#define AFE_CONN026_2 0x23c8
+#define AFE_CONN026_4 0x23d0
+#define AFE_CONN026_6 0x23d8
+#define AFE_CONN027_0 0x23e0
+#define AFE_CONN027_1 0x23e4
+#define AFE_CONN027_2 0x23e8
+#define AFE_CONN027_4 0x23f0
+#define AFE_CONN027_6 0x23f8
+#define AFE_CONN028_0 0x2400
+#define AFE_CONN028_1 0x2404
+#define AFE_CONN028_2 0x2408
+#define AFE_CONN028_4 0x2410
+#define AFE_CONN028_6 0x2418
+#define AFE_CONN029_0 0x2420
+#define AFE_CONN029_1 0x2424
+#define AFE_CONN029_2 0x2428
+#define AFE_CONN029_4 0x2430
+#define AFE_CONN029_6 0x2438
+#define AFE_CONN030_0 0x2440
+#define AFE_CONN030_1 0x2444
+#define AFE_CONN030_2 0x2448
+#define AFE_CONN030_4 0x2450
+#define AFE_CONN030_6 0x2458
+#define AFE_CONN031_0 0x2460
+#define AFE_CONN031_1 0x2464
+#define AFE_CONN031_2 0x2468
+#define AFE_CONN031_4 0x2470
+#define AFE_CONN031_6 0x2478
+#define AFE_CONN032_0 0x2480
+#define AFE_CONN032_1 0x2484
+#define AFE_CONN032_2 0x2488
+#define AFE_CONN032_4 0x2490
+#define AFE_CONN032_6 0x2498
+#define AFE_CONN033_0 0x24a0
+#define AFE_CONN033_1 0x24a4
+#define AFE_CONN033_2 0x24a8
+#define AFE_CONN033_4 0x24b0
+#define AFE_CONN033_6 0x24b8
+#define AFE_CONN034_0 0x24c0
+#define AFE_CONN034_1 0x24c4
+#define AFE_CONN034_2 0x24c8
+#define AFE_CONN034_4 0x24d0
+#define AFE_CONN034_6 0x24d8
+#define AFE_CONN035_0 0x24e0
+#define AFE_CONN035_1 0x24e4
+#define AFE_CONN035_2 0x24e8
+#define AFE_CONN035_4 0x24f0
+#define AFE_CONN035_6 0x24f8
+#define AFE_CONN036_0 0x2500
+#define AFE_CONN036_1 0x2504
+#define AFE_CONN036_2 0x2508
+#define AFE_CONN036_4 0x2510
+#define AFE_CONN036_6 0x2518
+#define AFE_CONN037_0 0x2520
+#define AFE_CONN037_1 0x2524
+#define AFE_CONN037_2 0x2528
+#define AFE_CONN037_4 0x2530
+#define AFE_CONN037_6 0x2538
+#define AFE_CONN038_0 0x2540
+#define AFE_CONN038_1 0x2544
+#define AFE_CONN038_2 0x2548
+#define AFE_CONN038_4 0x2550
+#define AFE_CONN038_6 0x2558
+#define AFE_CONN039_0 0x2560
+#define AFE_CONN039_1 0x2564
+#define AFE_CONN039_2 0x2568
+#define AFE_CONN039_4 0x2570
+#define AFE_CONN039_6 0x2578
+#define AFE_CONN040_0 0x2580
+#define AFE_CONN040_1 0x2584
+#define AFE_CONN040_2 0x2588
+#define AFE_CONN040_4 0x2590
+#define AFE_CONN040_6 0x2598
+#define AFE_CONN041_0 0x25a0
+#define AFE_CONN041_1 0x25a4
+#define AFE_CONN041_2 0x25a8
+#define AFE_CONN041_4 0x25b0
+#define AFE_CONN041_6 0x25b8
+#define AFE_CONN042_0 0x25c0
+#define AFE_CONN042_1 0x25c4
+#define AFE_CONN042_2 0x25c8
+#define AFE_CONN042_4 0x25d0
+#define AFE_CONN042_6 0x25d8
+#define AFE_CONN043_0 0x25e0
+#define AFE_CONN043_1 0x25e4
+#define AFE_CONN043_2 0x25e8
+#define AFE_CONN043_4 0x25f0
+#define AFE_CONN043_6 0x25f8
+#define AFE_CONN044_0 0x2600
+#define AFE_CONN044_1 0x2604
+#define AFE_CONN044_2 0x2608
+#define AFE_CONN044_4 0x2610
+#define AFE_CONN044_6 0x2618
+#define AFE_CONN045_0 0x2620
+#define AFE_CONN045_1 0x2624
+#define AFE_CONN045_2 0x2628
+#define AFE_CONN045_4 0x2630
+#define AFE_CONN045_6 0x2638
+#define AFE_CONN046_0 0x2640
+#define AFE_CONN046_1 0x2644
+#define AFE_CONN046_2 0x2648
+#define AFE_CONN046_4 0x2650
+#define AFE_CONN046_6 0x2658
+#define AFE_CONN047_0 0x2660
+#define AFE_CONN047_1 0x2664
+#define AFE_CONN047_2 0x2668
+#define AFE_CONN047_4 0x2670
+#define AFE_CONN047_6 0x2678
+#define AFE_CONN048_0 0x2680
+#define AFE_CONN048_1 0x2684
+#define AFE_CONN048_2 0x2688
+#define AFE_CONN048_4 0x2690
+#define AFE_CONN048_6 0x2698
+#define AFE_CONN049_0 0x26a0
+#define AFE_CONN049_1 0x26a4
+#define AFE_CONN049_2 0x26a8
+#define AFE_CONN049_4 0x26b0
+#define AFE_CONN049_6 0x26b8
+#define AFE_CONN050_0 0x26c0
+#define AFE_CONN050_1 0x26c4
+#define AFE_CONN050_2 0x26c8
+#define AFE_CONN050_4 0x26d0
+#define AFE_CONN050_6 0x26d8
+#define AFE_CONN051_0 0x26e0
+#define AFE_CONN051_1 0x26e4
+#define AFE_CONN051_2 0x26e8
+#define AFE_CONN051_4 0x26f0
+#define AFE_CONN051_6 0x26f8
+#define AFE_CONN052_0 0x2700
+#define AFE_CONN052_1 0x2704
+#define AFE_CONN052_2 0x2708
+#define AFE_CONN052_4 0x2710
+#define AFE_CONN052_6 0x2718
+#define AFE_CONN053_0 0x2720
+#define AFE_CONN053_1 0x2724
+#define AFE_CONN053_2 0x2728
+#define AFE_CONN053_4 0x2730
+#define AFE_CONN053_6 0x2738
+#define AFE_CONN054_0 0x2740
+#define AFE_CONN054_1 0x2744
+#define AFE_CONN054_2 0x2748
+#define AFE_CONN054_4 0x2750
+#define AFE_CONN054_6 0x2758
+#define AFE_CONN055_0 0x2760
+#define AFE_CONN055_1 0x2764
+#define AFE_CONN055_2 0x2768
+#define AFE_CONN055_4 0x2770
+#define AFE_CONN055_6 0x2778
+#define AFE_CONN056_0 0x2780
+#define AFE_CONN056_1 0x2784
+#define AFE_CONN056_2 0x2788
+#define AFE_CONN056_4 0x2790
+#define AFE_CONN056_6 0x2798
+#define AFE_CONN057_0 0x27a0
+#define AFE_CONN057_1 0x27a4
+#define AFE_CONN057_2 0x27a8
+#define AFE_CONN057_4 0x27b0
+#define AFE_CONN057_6 0x27b8
+#define AFE_CONN058_0 0x27c0
+#define AFE_CONN058_1 0x27c4
+#define AFE_CONN058_2 0x27c8
+#define AFE_CONN058_4 0x27d0
+#define AFE_CONN058_6 0x27d8
+#define AFE_CONN059_0 0x27e0
+#define AFE_CONN059_1 0x27e4
+#define AFE_CONN059_2 0x27e8
+#define AFE_CONN059_4 0x27f0
+#define AFE_CONN059_6 0x27f8
+#define AFE_CONN060_0 0x2800
+#define AFE_CONN060_1 0x2804
+#define AFE_CONN060_2 0x2808
+#define AFE_CONN060_4 0x2810
+#define AFE_CONN060_6 0x2818
+#define AFE_CONN061_0 0x2820
+#define AFE_CONN061_1 0x2824
+#define AFE_CONN061_2 0x2828
+#define AFE_CONN061_4 0x2830
+#define AFE_CONN061_6 0x2838
+#define AFE_CONN062_0 0x2840
+#define AFE_CONN062_1 0x2844
+#define AFE_CONN062_2 0x2848
+#define AFE_CONN062_4 0x2850
+#define AFE_CONN062_6 0x2858
+#define AFE_CONN063_0 0x2860
+#define AFE_CONN063_1 0x2864
+#define AFE_CONN063_2 0x2868
+#define AFE_CONN063_4 0x2870
+#define AFE_CONN063_6 0x2878
+#define AFE_CONN066_0 0x28c0
+#define AFE_CONN066_1 0x28c4
+#define AFE_CONN066_2 0x28c8
+#define AFE_CONN066_4 0x28d0
+#define AFE_CONN066_6 0x28d8
+#define AFE_CONN067_0 0x28e0
+#define AFE_CONN067_1 0x28e4
+#define AFE_CONN067_2 0x28e8
+#define AFE_CONN067_4 0x28f0
+#define AFE_CONN067_6 0x28f8
+#define AFE_CONN068_0 0x2900
+#define AFE_CONN068_1 0x2904
+#define AFE_CONN068_2 0x2908
+#define AFE_CONN068_4 0x2910
+#define AFE_CONN068_6 0x2918
+#define AFE_CONN069_0 0x2920
+#define AFE_CONN069_1 0x2924
+#define AFE_CONN069_2 0x2928
+#define AFE_CONN069_4 0x2930
+#define AFE_CONN069_6 0x2938
+#define AFE_CONN096_0 0x2c80
+#define AFE_CONN096_1 0x2c84
+#define AFE_CONN096_2 0x2c88
+#define AFE_CONN096_4 0x2c90
+#define AFE_CONN096_6 0x2c98
+#define AFE_CONN097_0 0x2ca0
+#define AFE_CONN097_1 0x2ca4
+#define AFE_CONN097_2 0x2ca8
+#define AFE_CONN097_4 0x2cb0
+#define AFE_CONN097_6 0x2cb8
+#define AFE_CONN098_0 0x2cc0
+#define AFE_CONN098_1 0x2cc4
+#define AFE_CONN098_2 0x2cc8
+#define AFE_CONN098_4 0x2cd0
+#define AFE_CONN098_6 0x2cd8
+#define AFE_CONN099_0 0x2ce0
+#define AFE_CONN099_1 0x2ce4
+#define AFE_CONN099_2 0x2ce8
+#define AFE_CONN099_4 0x2cf0
+#define AFE_CONN099_6 0x2cf8
+#define AFE_CONN100_0 0x2d00
+#define AFE_CONN100_1 0x2d04
+#define AFE_CONN100_2 0x2d08
+#define AFE_CONN100_4 0x2d10
+#define AFE_CONN100_6 0x2d18
+#define AFE_CONN108_0 0x2e00
+#define AFE_CONN108_1 0x2e04
+#define AFE_CONN108_2 0x2e08
+#define AFE_CONN108_4 0x2e10
+#define AFE_CONN108_6 0x2e18
+#define AFE_CONN109_0 0x2e20
+#define AFE_CONN109_1 0x2e24
+#define AFE_CONN109_2 0x2e28
+#define AFE_CONN109_4 0x2e30
+#define AFE_CONN109_6 0x2e38
+#define AFE_CONN110_0 0x2e40
+#define AFE_CONN110_1 0x2e44
+#define AFE_CONN110_2 0x2e48
+#define AFE_CONN110_4 0x2e50
+#define AFE_CONN110_6 0x2e58
+#define AFE_CONN111_0 0x2e60
+#define AFE_CONN111_1 0x2e64
+#define AFE_CONN111_2 0x2e68
+#define AFE_CONN111_4 0x2e70
+#define AFE_CONN111_6 0x2e78
+#define AFE_CONN116_0 0x2f00
+#define AFE_CONN116_1 0x2f04
+#define AFE_CONN116_2 0x2f08
+#define AFE_CONN116_4 0x2f10
+#define AFE_CONN116_6 0x2f18
+#define AFE_CONN117_0 0x2f20
+#define AFE_CONN117_1 0x2f24
+#define AFE_CONN117_2 0x2f28
+#define AFE_CONN117_4 0x2f30
+#define AFE_CONN117_6 0x2f38
+#define AFE_CONN118_0 0x2f40
+#define AFE_CONN118_1 0x2f44
+#define AFE_CONN118_2 0x2f48
+#define AFE_CONN118_4 0x2f50
+#define AFE_CONN118_6 0x2f58
+#define AFE_CONN119_0 0x2f60
+#define AFE_CONN119_1 0x2f64
+#define AFE_CONN119_2 0x2f68
+#define AFE_CONN119_4 0x2f70
+#define AFE_CONN119_6 0x2f78
+#define AFE_CONN120_0 0x2f80
+#define AFE_CONN120_1 0x2f84
+#define AFE_CONN120_2 0x2f88
+#define AFE_CONN120_4 0x2f90
+#define AFE_CONN120_6 0x2f98
+#define AFE_CONN121_0 0x2fa0
+#define AFE_CONN121_1 0x2fa4
+#define AFE_CONN121_2 0x2fa8
+#define AFE_CONN121_4 0x2fb0
+#define AFE_CONN121_6 0x2fb8
+#define AFE_CONN122_0 0x2fc0
+#define AFE_CONN122_1 0x2fc4
+#define AFE_CONN122_2 0x2fc8
+#define AFE_CONN122_4 0x2fd0
+#define AFE_CONN122_6 0x2fd8
+#define AFE_CONN123_0 0x2fe0
+#define AFE_CONN123_1 0x2fe4
+#define AFE_CONN123_2 0x2fe8
+#define AFE_CONN123_4 0x2ff0
+#define AFE_CONN123_6 0x2ff8
+#define AFE_CONN180_0 0x3700
+#define AFE_CONN180_1 0x3704
+#define AFE_CONN180_2 0x3708
+#define AFE_CONN180_4 0x3710
+#define AFE_CONN180_6 0x3718
+#define AFE_CONN181_0 0x3720
+#define AFE_CONN181_1 0x3724
+#define AFE_CONN181_2 0x3728
+#define AFE_CONN181_4 0x3730
+#define AFE_CONN181_6 0x3738
+#define AFE_CONN182_0 0x3740
+#define AFE_CONN182_1 0x3744
+#define AFE_CONN182_2 0x3748
+#define AFE_CONN182_4 0x3750
+#define AFE_CONN182_6 0x3758
+#define AFE_CONN183_0 0x3760
+#define AFE_CONN183_1 0x3764
+#define AFE_CONN183_2 0x3768
+#define AFE_CONN183_4 0x3770
+#define AFE_CONN183_6 0x3778
+#define AFE_CONN184_0 0x3780
+#define AFE_CONN184_1 0x3784
+#define AFE_CONN184_2 0x3788
+#define AFE_CONN184_4 0x3790
+#define AFE_CONN184_6 0x3798
+#define AFE_CONN185_0 0x37a0
+#define AFE_CONN185_1 0x37a4
+#define AFE_CONN185_2 0x37a8
+#define AFE_CONN185_4 0x37b0
+#define AFE_CONN185_6 0x37b8
+#define AFE_CONN186_0 0x37c0
+#define AFE_CONN186_1 0x37c4
+#define AFE_CONN186_2 0x37c8
+#define AFE_CONN186_4 0x37d0
+#define AFE_CONN186_6 0x37d8
+#define AFE_CONN187_0 0x37e0
+#define AFE_CONN187_1 0x37e4
+#define AFE_CONN187_2 0x37e8
+#define AFE_CONN187_4 0x37f0
+#define AFE_CONN187_6 0x37f8
+#define AFE_CONN188_0 0x3800
+#define AFE_CONN188_1 0x3804
+#define AFE_CONN188_2 0x3808
+#define AFE_CONN188_4 0x3810
+#define AFE_CONN188_6 0x3818
+#define AFE_CONN189_0 0x3820
+#define AFE_CONN189_1 0x3824
+#define AFE_CONN189_2 0x3828
+#define AFE_CONN189_4 0x3830
+#define AFE_CONN189_6 0x3838
+#define AFE_CONN_MON_CFG 0x4080
+#define AFE_CONN_MON0 0x4084
+#define AFE_CONN_MON1 0x4088
+#define AFE_CONN_MON2 0x408c
+#define AFE_CONN_MON3 0x4090
+#define AFE_CONN_MON4 0x4094
+#define AFE_CONN_MON5 0x4098
+#define AFE_CONN_RS_0 0x40a0
+#define AFE_CONN_RS_1 0x40a4
+#define AFE_CONN_RS_2 0x40a8
+#define AFE_CONN_RS_3 0x40ac
+#define AFE_CONN_RS_5 0x40b4
+#define AFE_CONN_DI_0 0x40c0
+#define AFE_CONN_DI_1 0x40c4
+#define AFE_CONN_DI_2 0x40c8
+#define AFE_CONN_DI_3 0x40cc
+#define AFE_CONN_DI_5 0x40d4
+#define AFE_CONN_16BIT_0 0x40e0
+#define AFE_CONN_16BIT_1 0x40e4
+#define AFE_CONN_16BIT_2 0x40e8
+#define AFE_CONN_16BIT_3 0x40ec
+#define AFE_CONN_16BIT_5 0x40f4
+#define AFE_CONN_24BIT_0 0x4100
+#define AFE_CONN_24BIT_1 0x4104
+#define AFE_CONN_24BIT_2 0x4108
+#define AFE_CONN_24BIT_3 0x410c
+#define AFE_CONN_24BIT_5 0x4114
+#define AFE_CONN_TOP_IP_VERSION 0x4120
+#define AFE_CBIP_CFG0 0x4380
+#define AFE_CBIP_SLV_DECODER_MON0 0x4384
+#define AFE_CBIP_SLV_DECODER_MON1 0x4388
+#define AFE_CBIP_SLV_MUX_MON_CFG 0x438c
+#define AFE_CBIP_SLV_MUX_MON0 0x4390
+#define AFE_CBIP_SLV_MUX_MON1 0x4394
+#define AFE_MEMIF_IP_VERSION 0x4398
+#define AFE_MEMIF_CON0 0x4400
+#define AFE_MEMIF_RD_MON 0x4408
+#define AFE_MEMIF_WR_MON 0x440c
+#define AFE_MEMIF_CFG_MON0 0x4410
+#define AFE_BUS_CFG0 0x4414
+#define AFE_BUS_MON1 0x4418
+#define AFE_BUS_MON2 0x441c
+#define AFE_MEMIF_ONE_HEART 0x4420
+#define AFE_DL0_BASE_MSB 0x4440
+#define AFE_DL0_BASE 0x4444
+#define AFE_DL0_CUR_MSB 0x4448
+#define AFE_DL0_CUR 0x444c
+#define AFE_DL0_END_MSB 0x4450
+#define AFE_DL0_END 0x4454
+#define AFE_DL0_RCH_MON 0x4458
+#define AFE_DL0_LCH_MON 0x445c
+#define AFE_DL0_CON0 0x4460
+#define AFE_DL0_MON0 0x4464
+#define AFE_DL0_MEM_UP_MSB 0x4468
+#define AFE_DL0_MEM_UP 0x446c
+#define AFE_DL1_BASE_MSB 0x4470
+#define AFE_DL1_BASE 0x4474
+#define AFE_DL1_CUR_MSB 0x4478
+#define AFE_DL1_CUR 0x447c
+#define AFE_DL1_END_MSB 0x4480
+#define AFE_DL1_END 0x4484
+#define AFE_DL1_RCH_MON 0x4488
+#define AFE_DL1_LCH_MON 0x448c
+#define AFE_DL1_CON0 0x4490
+#define AFE_DL1_MON0 0x4494
+#define AFE_DL1_MEM_UP_MSB 0x4498
+#define AFE_DL1_MEM_UP 0x449c
+#define AFE_DL2_BASE_MSB 0x44a0
+#define AFE_DL2_BASE 0x44a4
+#define AFE_DL2_CUR_MSB 0x44a8
+#define AFE_DL2_CUR 0x44ac
+#define AFE_DL2_END_MSB 0x44b0
+#define AFE_DL2_END 0x44b4
+#define AFE_DL2_RCH_MON 0x44b8
+#define AFE_DL2_LCH_MON 0x44bc
+#define AFE_DL2_CON0 0x44c0
+#define AFE_DL2_MON0 0x44c4
+#define AFE_DL2_MEM_UP_MSB 0x44c8
+#define AFE_DL2_MEM_UP 0x44cc
+#define AFE_DL3_BASE_MSB 0x44d0
+#define AFE_DL3_BASE 0x44d4
+#define AFE_DL3_CUR_MSB 0x44d8
+#define AFE_DL3_CUR 0x44dc
+#define AFE_DL3_END_MSB 0x44e0
+#define AFE_DL3_END 0x44e4
+#define AFE_DL3_RCH_MON 0x44e8
+#define AFE_DL3_LCH_MON 0x44ec
+#define AFE_DL3_CON0 0x44f0
+#define AFE_DL3_MON0 0x44f4
+#define AFE_DL3_MEM_UP_MSB 0x44f8
+#define AFE_DL3_MEM_UP 0x44fc
+#define AFE_DL4_BASE_MSB 0x4500
+#define AFE_DL4_BASE 0x4504
+#define AFE_DL4_CUR_MSB 0x4508
+#define AFE_DL4_CUR 0x450c
+#define AFE_DL4_END_MSB 0x4510
+#define AFE_DL4_END 0x4514
+#define AFE_DL4_RCH_MON 0x4518
+#define AFE_DL4_LCH_MON 0x451c
+#define AFE_DL4_CON0 0x4520
+#define AFE_DL4_MON0 0x4524
+#define AFE_DL4_MEM_UP_MSB 0x4528
+#define AFE_DL4_MEM_UP 0x452c
+#define AFE_DL5_BASE_MSB 0x4530
+#define AFE_DL5_BASE 0x4534
+#define AFE_DL5_CUR_MSB 0x4538
+#define AFE_DL5_CUR 0x453c
+#define AFE_DL5_END_MSB 0x4540
+#define AFE_DL5_END 0x4544
+#define AFE_DL5_RCH_MON 0x4548
+#define AFE_DL5_LCH_MON 0x454c
+#define AFE_DL5_CON0 0x4550
+#define AFE_DL5_MON0 0x4554
+#define AFE_DL5_MEM_UP_MSB 0x4558
+#define AFE_DL5_MEM_UP 0x455c
+#define AFE_DL6_BASE_MSB 0x4560
+#define AFE_DL6_BASE 0x4564
+#define AFE_DL6_CUR_MSB 0x4568
+#define AFE_DL6_CUR 0x456c
+#define AFE_DL6_END_MSB 0x4570
+#define AFE_DL6_END 0x4574
+#define AFE_DL6_RCH_MON 0x4578
+#define AFE_DL6_LCH_MON 0x457c
+#define AFE_DL6_CON0 0x4580
+#define AFE_DL6_MON0 0x4584
+#define AFE_DL6_MEM_UP_MSB 0x4588
+#define AFE_DL6_MEM_UP 0x458c
+#define AFE_DL7_BASE_MSB 0x4590
+#define AFE_DL7_BASE 0x4594
+#define AFE_DL7_CUR_MSB 0x4598
+#define AFE_DL7_CUR 0x459c
+#define AFE_DL7_END_MSB 0x45a0
+#define AFE_DL7_END 0x45a4
+#define AFE_DL7_RCH_MON 0x45a8
+#define AFE_DL7_LCH_MON 0x45ac
+#define AFE_DL7_CON0 0x45b0
+#define AFE_DL7_MON0 0x45b4
+#define AFE_DL7_MEM_UP_MSB 0x45b8
+#define AFE_DL7_MEM_UP 0x45bc
+#define AFE_DL8_BASE_MSB 0x45c0
+#define AFE_DL8_BASE 0x45c4
+#define AFE_DL8_CUR_MSB 0x45c8
+#define AFE_DL8_CUR 0x45cc
+#define AFE_DL8_END_MSB 0x45d0
+#define AFE_DL8_END 0x45d4
+#define AFE_DL8_RCH_MON 0x45d8
+#define AFE_DL8_LCH_MON 0x45dc
+#define AFE_DL8_CON0 0x45e0
+#define AFE_DL8_MON0 0x45e4
+#define AFE_DL8_MEM_UP_MSB 0x45e8
+#define AFE_DL8_MEM_UP 0x45ec
+#define AFE_DL_24CH_BASE_MSB 0x4620
+#define AFE_DL_24CH_BASE 0x4624
+#define AFE_DL_24CH_CUR_MSB 0x4628
+#define AFE_DL_24CH_CUR 0x462c
+#define AFE_DL_24CH_END_MSB 0x4630
+#define AFE_DL_24CH_END 0x4634
+#define AFE_DL_24CH_CON0 0x4640
+#define AFE_DL_24CH_MON0 0x4644
+#define AFE_DL_24CH_MEM_UP_MSB 0x4648
+#define AFE_DL_24CH_MEM_UP 0x464c
+#define AFE_DL23_BASE_MSB 0x4680
+#define AFE_DL23_BASE 0x4684
+#define AFE_DL23_CUR_MSB 0x4688
+#define AFE_DL23_CUR 0x468c
+#define AFE_DL23_END_MSB 0x4690
+#define AFE_DL23_END 0x4694
+#define AFE_DL23_RCH_MON 0x4698
+#define AFE_DL23_LCH_MON 0x469c
+#define AFE_DL23_CON0 0x46a0
+#define AFE_DL23_MON0 0x46a4
+#define AFE_DL23_MEM_UP_MSB 0x46a8
+#define AFE_DL23_MEM_UP 0x46ac
+#define AFE_DL24_BASE_MSB 0x46b0
+#define AFE_DL24_BASE 0x46b4
+#define AFE_DL24_CUR_MSB 0x46b8
+#define AFE_DL24_CUR 0x46bc
+#define AFE_DL24_END_MSB 0x46c0
+#define AFE_DL24_END 0x46c4
+#define AFE_DL24_RCH_MON 0x46c8
+#define AFE_DL24_LCH_MON 0x46cc
+#define AFE_DL24_CON0 0x46d0
+#define AFE_DL24_MON0 0x46d4
+#define AFE_DL24_MEM_UP_MSB 0x46d8
+#define AFE_DL24_MEM_UP 0x46dc
+#define AFE_DL25_BASE_MSB 0x46e0
+#define AFE_DL25_BASE 0x46e4
+#define AFE_DL25_CUR_MSB 0x46e8
+#define AFE_DL25_CUR 0x46ec
+#define AFE_DL25_END_MSB 0x46f0
+#define AFE_DL25_END 0x46f4
+#define AFE_DL25_RCH_MON 0x46f8
+#define AFE_DL25_LCH_MON 0x46fc
+#define AFE_DL25_CON0 0x4700
+#define AFE_DL25_MON0 0x4704
+#define AFE_DL25_MEM_UP_MSB 0x4708
+#define AFE_DL25_MEM_UP 0x470c
+#define AFE_VUL0_BASE_MSB 0x4d60
+#define AFE_VUL0_BASE 0x4d64
+#define AFE_VUL0_CUR_MSB 0x4d68
+#define AFE_VUL0_CUR 0x4d6c
+#define AFE_VUL0_END_MSB 0x4d70
+#define AFE_VUL0_END 0x4d74
+#define AFE_VUL0_RCH_MON 0x4d78
+#define AFE_VUL0_LCH_MON 0x4d7c
+#define AFE_VUL0_CON0 0x4d80
+#define AFE_VUL0_MON0 0x4d84
+#define AFE_VUL1_BASE_MSB 0x4d90
+#define AFE_VUL1_BASE 0x4d94
+#define AFE_VUL1_CUR_MSB 0x4d98
+#define AFE_VUL1_CUR 0x4d9c
+#define AFE_VUL1_END_MSB 0x4da0
+#define AFE_VUL1_END 0x4da4
+#define AFE_VUL1_RCH_MON 0x4da8
+#define AFE_VUL1_LCH_MON 0x4dac
+#define AFE_VUL1_CON0 0x4db0
+#define AFE_VUL1_MON0 0x4db4
+#define AFE_VUL2_BASE_MSB 0x4dc0
+#define AFE_VUL2_BASE 0x4dc4
+#define AFE_VUL2_CUR_MSB 0x4dc8
+#define AFE_VUL2_CUR 0x4dcc
+#define AFE_VUL2_END_MSB 0x4dd0
+#define AFE_VUL2_END 0x4dd4
+#define AFE_VUL2_RCH_MON 0x4dd8
+#define AFE_VUL2_LCH_MON 0x4ddc
+#define AFE_VUL2_CON0 0x4de0
+#define AFE_VUL2_MON0 0x4de4
+#define AFE_VUL3_BASE_MSB 0x4df0
+#define AFE_VUL3_BASE 0x4df4
+#define AFE_VUL3_CUR_MSB 0x4df8
+#define AFE_VUL3_CUR 0x4dfc
+#define AFE_VUL3_END_MSB 0x4e00
+#define AFE_VUL3_END 0x4e04
+#define AFE_VUL3_RCH_MON 0x4e08
+#define AFE_VUL3_LCH_MON 0x4e0c
+#define AFE_VUL3_CON0 0x4e10
+#define AFE_VUL3_MON0 0x4e14
+#define AFE_VUL4_BASE_MSB 0x4e20
+#define AFE_VUL4_BASE 0x4e24
+#define AFE_VUL4_CUR_MSB 0x4e28
+#define AFE_VUL4_CUR 0x4e2c
+#define AFE_VUL4_END_MSB 0x4e30
+#define AFE_VUL4_END 0x4e34
+#define AFE_VUL4_RCH_MON 0x4e38
+#define AFE_VUL4_LCH_MON 0x4e3c
+#define AFE_VUL4_CON0 0x4e40
+#define AFE_VUL4_MON0 0x4e44
+#define AFE_VUL5_BASE_MSB 0x4e50
+#define AFE_VUL5_BASE 0x4e54
+#define AFE_VUL5_CUR_MSB 0x4e58
+#define AFE_VUL5_CUR 0x4e5c
+#define AFE_VUL5_END_MSB 0x4e60
+#define AFE_VUL5_END 0x4e64
+#define AFE_VUL5_RCH_MON 0x4e68
+#define AFE_VUL5_LCH_MON 0x4e6c
+#define AFE_VUL5_CON0 0x4e70
+#define AFE_VUL5_MON0 0x4e74
+#define AFE_VUL6_BASE_MSB 0x4e80
+#define AFE_VUL6_BASE 0x4e84
+#define AFE_VUL6_CUR_MSB 0x4e88
+#define AFE_VUL6_CUR 0x4e8c
+#define AFE_VUL6_END_MSB 0x4e90
+#define AFE_VUL6_END 0x4e94
+#define AFE_VUL6_RCH_MON 0x4e98
+#define AFE_VUL6_LCH_MON 0x4e9c
+#define AFE_VUL6_CON0 0x4ea0
+#define AFE_VUL6_MON0 0x4ea4
+#define AFE_VUL7_BASE_MSB 0x4eb0
+#define AFE_VUL7_BASE 0x4eb4
+#define AFE_VUL7_CUR_MSB 0x4eb8
+#define AFE_VUL7_CUR 0x4ebc
+#define AFE_VUL7_END_MSB 0x4ec0
+#define AFE_VUL7_END 0x4ec4
+#define AFE_VUL7_RCH_MON 0x4ec8
+#define AFE_VUL7_LCH_MON 0x4ecc
+#define AFE_VUL7_CON0 0x4ed0
+#define AFE_VUL7_MON0 0x4ed4
+#define AFE_VUL8_BASE_MSB 0x4ee0
+#define AFE_VUL8_BASE 0x4ee4
+#define AFE_VUL8_CUR_MSB 0x4ee8
+#define AFE_VUL8_CUR 0x4eec
+#define AFE_VUL8_END_MSB 0x4ef0
+#define AFE_VUL8_END 0x4ef4
+#define AFE_VUL8_RCH_MON 0x4ef8
+#define AFE_VUL8_LCH_MON 0x4efc
+#define AFE_VUL8_CON0 0x4f00
+#define AFE_VUL8_MON0 0x4f04
+#define AFE_VUL9_BASE_MSB 0x4f10
+#define AFE_VUL9_BASE 0x4f14
+#define AFE_VUL9_CUR_MSB 0x4f18
+#define AFE_VUL9_CUR 0x4f1c
+#define AFE_VUL9_END_MSB 0x4f20
+#define AFE_VUL9_END 0x4f24
+#define AFE_VUL9_RCH_MON 0x4f28
+#define AFE_VUL9_LCH_MON 0x4f2c
+#define AFE_VUL9_CON0 0x4f30
+#define AFE_VUL9_MON0 0x4f34
+#define AFE_VUL10_BASE_MSB 0x4f40
+#define AFE_VUL10_BASE 0x4f44
+#define AFE_VUL10_CUR_MSB 0x4f48
+#define AFE_VUL10_CUR 0x4f4c
+#define AFE_VUL10_END_MSB 0x4f50
+#define AFE_VUL10_END 0x4f54
+#define AFE_VUL10_RCH_MON 0x4f58
+#define AFE_VUL10_LCH_MON 0x4f5c
+#define AFE_VUL10_CON0 0x4f60
+#define AFE_VUL10_MON0 0x4f64
+#define AFE_VUL24_BASE_MSB 0x4fa0
+#define AFE_VUL24_BASE 0x4fa4
+#define AFE_VUL24_CUR_MSB 0x4fa8
+#define AFE_VUL24_CUR 0x4fac
+#define AFE_VUL24_END_MSB 0x4fb0
+#define AFE_VUL24_END 0x4fb4
+#define AFE_VUL24_CON0 0x4fb8
+#define AFE_VUL24_MON0 0x4fbc
+#define AFE_VUL25_BASE_MSB 0x4fc0
+#define AFE_VUL25_BASE 0x4fc4
+#define AFE_VUL25_CUR_MSB 0x4fc8
+#define AFE_VUL25_CUR 0x4fcc
+#define AFE_VUL25_END_MSB 0x4fd0
+#define AFE_VUL25_END 0x4fd4
+#define AFE_VUL25_CON0 0x4fd8
+#define AFE_VUL25_MON0 0x4fdc
+#define AFE_VUL_CM0_BASE_MSB 0x51c0
+#define AFE_VUL_CM0_BASE 0x51c4
+#define AFE_VUL_CM0_CUR_MSB 0x51c8
+#define AFE_VUL_CM0_CUR 0x51cc
+#define AFE_VUL_CM0_END_MSB 0x51d0
+#define AFE_VUL_CM0_END 0x51d4
+#define AFE_VUL_CM0_CON0 0x51d8
+#define AFE_VUL_CM0_MON0 0x51dc
+#define AFE_VUL_CM1_BASE_MSB 0x51e0
+#define AFE_VUL_CM1_BASE 0x51e4
+#define AFE_VUL_CM1_CUR_MSB 0x51e8
+#define AFE_VUL_CM1_CUR 0x51ec
+#define AFE_VUL_CM1_END_MSB 0x51f0
+#define AFE_VUL_CM1_END 0x51f4
+#define AFE_VUL_CM1_CON0 0x51f8
+#define AFE_VUL_CM1_MON0 0x51fc
+#define AFE_ETDM_IN0_BASE_MSB 0x5220
+#define AFE_ETDM_IN0_BASE 0x5224
+#define AFE_ETDM_IN0_CUR_MSB 0x5228
+#define AFE_ETDM_IN0_CUR 0x522c
+#define AFE_ETDM_IN0_END_MSB 0x5230
+#define AFE_ETDM_IN0_END 0x5234
+#define AFE_ETDM_IN0_CON0 0x5238
+#define AFE_ETDM_IN1_BASE_MSB 0x5240
+#define AFE_ETDM_IN1_BASE 0x5244
+#define AFE_ETDM_IN1_CUR_MSB 0x5248
+#define AFE_ETDM_IN1_CUR 0x524c
+#define AFE_ETDM_IN1_END_MSB 0x5250
+#define AFE_ETDM_IN1_END 0x5254
+#define AFE_ETDM_IN1_CON0 0x5258
+#define AFE_HDMI_OUT_BASE_MSB 0x5360
+#define AFE_HDMI_OUT_BASE 0x5364
+#define AFE_HDMI_OUT_CUR_MSB 0x5368
+#define AFE_HDMI_OUT_CUR 0x536c
+#define AFE_HDMI_OUT_END_MSB 0x5370
+#define AFE_HDMI_OUT_END 0x5374
+#define AFE_HDMI_OUT_CON0 0x5378
+#define AFE_HDMI_OUT_MON0 0x537c
+#define AFE_VUL24_RCH_MON 0x53e0
+#define AFE_VUL24_LCH_MON 0x53e4
+#define AFE_VUL25_RCH_MON 0x53e8
+#define AFE_VUL25_LCH_MON 0x53ec
+#define AFE_VUL_CM0_RCH_MON 0x5458
+#define AFE_VUL_CM0_LCH_MON 0x545c
+#define AFE_VUL_CM1_RCH_MON 0x5460
+#define AFE_VUL_CM1_LCH_MON 0x5464
+#define AFE_DL_24CH_CH0_MON 0x5504
+#define AFE_DL_24CH_CH1_MON 0x5508
+#define AFE_DL_24CH_CH2_MON 0x550c
+#define AFE_DL_24CH_CH3_MON 0x5510
+#define AFE_DL_24CH_CH4_MON 0x5514
+#define AFE_DL_24CH_CH5_MON 0x5518
+#define AFE_DL_24CH_CH6_MON 0x551c
+#define AFE_DL_24CH_CH7_MON 0x5520
+#define AFE_HDMI_OUT_MEM_UP_MSB 0x55b0
+#define AFE_HDMI_OUT_MEM_UP 0x55b4
+#define AFE_SRAM_BOUND 0x5620
+#define AFE_SECURE_CON0 0x5624
+#define AFE_SECURE_CON1 0x5628
+#define AFE_SE_SECURE_CON0 0x5630
+#define AFE_SE_SECURE_CON1 0x5634
+#define AFE_SE_SECURE_CON2 0x5638
+#define AFE_SE_SECURE_CON3 0x563c
+#define AFE_SE_PROT_SIDEBAND0 0x5640
+#define AFE_SE_PROT_SIDEBAND1 0x5644
+#define AFE_SE_PROT_SIDEBAND2 0x5648
+#define AFE_SE_PROT_SIDEBAND3 0x564c
+#define AFE_SE_DOMAIN_SIDEBAND0 0x5650
+#define AFE_SE_DOMAIN_SIDEBAND1 0x5654
+#define AFE_SE_DOMAIN_SIDEBAND2 0x5658
+#define AFE_SE_DOMAIN_SIDEBAND3 0x565c
+#define AFE_SE_DOMAIN_SIDEBAND4 0x5660
+#define AFE_SE_DOMAIN_SIDEBAND5 0x5664
+#define AFE_SE_DOMAIN_SIDEBAND6 0x5668
+#define AFE_SE_DOMAIN_SIDEBAND7 0x566c
+#define AFE_SE_DOMAIN_SIDEBAND8 0x5670
+#define AFE_SE_DOMAIN_SIDEBAND9 0x5674
+#define AFE_PROT_SIDEBAND0_MON 0x5678
+#define AFE_PROT_SIDEBAND1_MON 0x567c
+#define AFE_PROT_SIDEBAND2_MON 0x5680
+#define AFE_PROT_SIDEBAND3_MON 0x5684
+#define AFE_DOMAIN_SIDEBAND0_MON 0x5688
+#define AFE_DOMAIN_SIDEBAND1_MON 0x568c
+#define AFE_DOMAIN_SIDEBAND2_MON 0x5690
+#define AFE_DOMAIN_SIDEBAND3_MON 0x5694
+#define AFE_DOMAIN_SIDEBAND4_MON 0x5698
+#define AFE_DOMAIN_SIDEBAND5_MON 0x569c
+#define AFE_DOMAIN_SIDEBAND6_MON 0x56a0
+#define AFE_DOMAIN_SIDEBAND7_MON 0x56a4
+#define AFE_DOMAIN_SIDEBAND8_MON 0x56a8
+#define AFE_DOMAIN_SIDEBAND9_MON 0x56ac
+#define AFE_SECURE_CONN0 0x56b0
+#define AFE_SECURE_CONN_ETDM0 0x56b4
+#define AFE_SECURE_CONN_ETDM1 0x56b8
+#define AFE_SECURE_CONN_ETDM2 0x56bc
+#define AFE_SECURE_SRAM_CON0 0x56c0
+#define AFE_SECURE_SRAM_CON1 0x56c4
+#define AFE_SE_CONN_INPUT_MASK0 0x56d0
+#define AFE_SE_CONN_INPUT_MASK1 0x56d4
+#define AFE_SE_CONN_INPUT_MASK2 0x56d8
+#define AFE_SE_CONN_INPUT_MASK3 0x56dc
+#define AFE_SE_CONN_INPUT_MASK4 0x56e0
+#define AFE_SE_CONN_INPUT_MASK5 0x56e4
+#define AFE_SE_CONN_INPUT_MASK6 0x56e8
+#define AFE_SE_CONN_INPUT_MASK7 0x56ec
+#define AFE_NON_SE_CONN_INPUT_MASK0 0x56f0
+#define AFE_NON_SE_CONN_INPUT_MASK1 0x56f4
+#define AFE_NON_SE_CONN_INPUT_MASK2 0x56f8
+#define AFE_NON_SE_CONN_INPUT_MASK3 0x56fc
+#define AFE_NON_SE_CONN_INPUT_MASK4 0x5700
+#define AFE_NON_SE_CONN_INPUT_MASK5 0x5704
+#define AFE_NON_SE_CONN_INPUT_MASK6 0x5708
+#define AFE_NON_SE_CONN_INPUT_MASK7 0x570c
+#define AFE_SE_CONN_OUTPUT_SEL0 0x5710
+#define AFE_SE_CONN_OUTPUT_SEL1 0x5714
+#define AFE_SE_CONN_OUTPUT_SEL2 0x5718
+#define AFE_SE_CONN_OUTPUT_SEL3 0x571c
+#define AFE_SE_CONN_OUTPUT_SEL4 0x5720
+#define AFE_SE_CONN_OUTPUT_SEL5 0x5724
+#define AFE_SE_CONN_OUTPUT_SEL6 0x5728
+#define AFE_SE_CONN_OUTPUT_SEL7 0x572c
+#define AFE_PCM0_INTF_CON1_MASK_MON 0x5730
+#define AFE_CONNSYS_I2S_CON_MASK_MON 0x5738
+#define AFE_TDM_CON2_MASK_MON 0x5744
+#define AFE_MTKAIF0_CFG0_MASK_MON 0x574c
+#define AFE_MTKAIF1_CFG0_MASK_MON 0x5750
+#define AFE_ADDA_UL0_SRC_CON0_MASK_MON 0x5754
+#define AFE_ADDA_DMIC0_SRC_CON0_MASK_MON 0x5764
+#define AFE_ADDA_DMIC1_SRC_CON0_MASK_MON 0x5768
+#define AFE_MON_SECURE_CON0 0x5840
+#define AFE_SECURE_CONN_ETDM3 0x5850
+#define AFE_ASRC_NEW_CON0 0x7800
+#define AFE_ASRC_NEW_CON1 0x7804
+#define AFE_ASRC_NEW_CON2 0x7808
+#define AFE_ASRC_NEW_CON3 0x780c
+#define AFE_ASRC_NEW_CON4 0x7810
+#define AFE_ASRC_NEW_CON5 0x7814
+#define AFE_ASRC_NEW_CON6 0x7818
+#define AFE_ASRC_NEW_CON7 0x781c
+#define AFE_ASRC_NEW_CON8 0x7820
+#define AFE_ASRC_NEW_CON9 0x7824
+#define AFE_ASRC_NEW_CON10 0x7828
+#define AFE_ASRC_NEW_CON11 0x782c
+#define AFE_ASRC_NEW_CON12 0x7830
+#define AFE_ASRC_NEW_CON13 0x7834
+#define AFE_ASRC_NEW_CON14 0x7838
+#define AFE_ASRC_NEW_IP_VERSION 0x783c
+#define AFE_GASRC0_NEW_CON0 0x7840
+#define AFE_GASRC0_NEW_CON1 0x7844
+#define AFE_GASRC0_NEW_CON2 0x7848
+#define AFE_GASRC0_NEW_CON3 0x784c
+#define AFE_GASRC0_NEW_CON4 0x7850
+#define AFE_GASRC0_NEW_CON5 0x7854
+#define AFE_GASRC0_NEW_CON6 0x7858
+#define AFE_GASRC0_NEW_CON7 0x785c
+#define AFE_GASRC0_NEW_CON8 0x7860
+#define AFE_GASRC0_NEW_CON9 0x7864
+#define AFE_GASRC0_NEW_CON10 0x7868
+#define AFE_GASRC0_NEW_CON11 0x786c
+#define AFE_GASRC0_NEW_CON12 0x7870
+#define AFE_GASRC0_NEW_CON13 0x7874
+#define AFE_GASRC0_NEW_CON14 0x7878
+#define AFE_GASRC0_NEW_IP_VERSION 0x787c
+#define AFE_GASRC1_NEW_CON0 0x7880
+#define AFE_GASRC1_NEW_CON1 0x7884
+#define AFE_GASRC1_NEW_CON2 0x7888
+#define AFE_GASRC1_NEW_CON3 0x788c
+#define AFE_GASRC1_NEW_CON4 0x7890
+#define AFE_GASRC1_NEW_CON5 0x7894
+#define AFE_GASRC1_NEW_CON6 0x7898
+#define AFE_GASRC1_NEW_CON7 0x789c
+#define AFE_GASRC1_NEW_CON8 0x78a0
+#define AFE_GASRC1_NEW_CON9 0x78a4
+#define AFE_GASRC1_NEW_CON10 0x78a8
+#define AFE_GASRC1_NEW_CON11 0x78ac
+#define AFE_GASRC1_NEW_CON12 0x78b0
+#define AFE_GASRC1_NEW_CON13 0x78b4
+#define AFE_GASRC1_NEW_CON14 0x78b8
+#define AFE_GASRC1_NEW_IP_VERSION 0x78bc
+#define AFE_GASRC2_NEW_CON0 0x78c0
+#define AFE_GASRC2_NEW_CON1 0x78c4
+#define AFE_GASRC2_NEW_CON2 0x78c8
+#define AFE_GASRC2_NEW_CON3 0x78cc
+#define AFE_GASRC2_NEW_CON4 0x78d0
+#define AFE_GASRC2_NEW_CON5 0x78d4
+#define AFE_GASRC2_NEW_CON6 0x78d8
+#define AFE_GASRC2_NEW_CON7 0x78dc
+#define AFE_GASRC2_NEW_CON8 0x78e0
+#define AFE_GASRC2_NEW_CON9 0x78e4
+#define AFE_GASRC2_NEW_CON10 0x78e8
+#define AFE_GASRC2_NEW_CON11 0x78ec
+#define AFE_GASRC2_NEW_CON12 0x78f0
+#define AFE_GASRC2_NEW_CON13 0x78f4
+#define AFE_GASRC2_NEW_CON14 0x78f8
+#define AFE_GASRC2_NEW_IP_VERSION 0x78fc
+#define AFE_GASRC3_NEW_CON0 0x7900
+#define AFE_GASRC3_NEW_CON1 0x7904
+#define AFE_GASRC3_NEW_CON2 0x7908
+#define AFE_GASRC3_NEW_CON3 0x790c
+#define AFE_GASRC3_NEW_CON4 0x7910
+#define AFE_GASRC3_NEW_CON5 0x7914
+#define AFE_GASRC3_NEW_CON6 0x7918
+#define AFE_GASRC3_NEW_CON7 0x791c
+#define AFE_GASRC3_NEW_CON8 0x7920
+#define AFE_GASRC3_NEW_CON9 0x7924
+#define AFE_GASRC3_NEW_CON10 0x7928
+#define AFE_GASRC3_NEW_CON11 0x792c
+#define AFE_GASRC3_NEW_CON12 0x7930
+#define AFE_GASRC3_NEW_CON13 0x7934
+#define AFE_GASRC3_NEW_CON14 0x7938
+#define AFE_GASRC3_NEW_IP_VERSION 0x793c
+#define AFE_GASRC4_NEW_CON0 0x7940
+#define AFE_GASRC4_NEW_CON1 0x7944
+#define AFE_GASRC4_NEW_CON2 0x7948
+#define AFE_GASRC4_NEW_CON3 0x794c
+#define AFE_GASRC4_NEW_CON4 0x7950
+#define AFE_GASRC4_NEW_CON5 0x7954
+#define AFE_GASRC4_NEW_CON6 0x7958
+#define AFE_GASRC4_NEW_CON7 0x795c
+#define AFE_GASRC4_NEW_CON8 0x7960
+#define AFE_GASRC4_NEW_CON9 0x7964
+#define AFE_GASRC4_NEW_CON10 0x7968
+#define AFE_GASRC4_NEW_CON11 0x796c
+#define AFE_GASRC4_NEW_CON12 0x7970
+#define AFE_GASRC4_NEW_CON13 0x7974
+#define AFE_GASRC4_NEW_CON14 0x7978
+#define AFE_GASRC4_NEW_IP_VERSION 0x797c
+#define AFE_SOUNDWIRE_ULSRC_PHASE_CLK_CON0 0x9400
+#define AFE_SOUNDWIRE_ULSRC_PHASE_CLK_CON1 0x9404
+#define AFE_SOUNDWIRE_ULSRC_PHASE_CLK_CON2 0x9408
+#define AFE_SOUNDWIRE_ULSRC_PHASE_CLK_CON3 0x940c
+#define AFE_SOUNDWIRE_ULSRC_PHASE_CLK_CON4 0x9410
+#define AFE_SOUNDWIRE_ULSRC_PHASE_ENGEN_CON0 0x9414
+#define AFE_SOUNDWIRE_ULSRC_PHASE_ENGEN_CON1 0x9418
+#define AFE_SOUNDWIRE_ULSRC_PHASE_RST_CON0 0x941c
+#define AFE_IRQ_MCU_EN 0x9d00
+#define AFE_IRQ_MCU_DSP_EN 0x9d04
+#define AFE_IRQ_MCU_DSP2_EN 0x9d08
+#define AFE_IRQ_MCU_SCP_EN 0x9d0c
+#define AFE_CUSTOM_IRQ_MCU_EN 0x9d10
+#define AFE_CUSTOM_IRQ_MCU_DSP_EN 0x9d14
+#define AFE_CUSTOM_IRQ_MCU_DSP2_EN 0x9d18
+#define AFE_CUSTOM_IRQ_MCU_SCP_EN 0x9d1c
+#define AFE_IRQ_MCU_STATUS 0x9d20
+#define AFE_CUSTOM_IRQ_MCU_STATUS 0x9d24
+#define AFE_IRQ0_MCU_CFG0 0x9d40
+#define AFE_IRQ0_MCU_CFG1 0x9d44
+#define AFE_IRQ1_MCU_CFG0 0x9d48
+#define AFE_IRQ1_MCU_CFG1 0x9d4c
+#define AFE_IRQ2_MCU_CFG0 0x9d50
+#define AFE_IRQ2_MCU_CFG1 0x9d54
+#define AFE_IRQ3_MCU_CFG0 0x9d58
+#define AFE_IRQ3_MCU_CFG1 0x9d5c
+#define AFE_IRQ4_MCU_CFG0 0x9d60
+#define AFE_IRQ4_MCU_CFG1 0x9d64
+#define AFE_IRQ5_MCU_CFG0 0x9d68
+#define AFE_IRQ5_MCU_CFG1 0x9d6c
+#define AFE_IRQ6_MCU_CFG0 0x9d70
+#define AFE_IRQ6_MCU_CFG1 0x9d74
+#define AFE_IRQ7_MCU_CFG0 0x9d78
+#define AFE_IRQ7_MCU_CFG1 0x9d7c
+#define AFE_IRQ8_MCU_CFG0 0x9d80
+#define AFE_IRQ8_MCU_CFG1 0x9d84
+#define AFE_IRQ9_MCU_CFG0 0x9d88
+#define AFE_IRQ9_MCU_CFG1 0x9d8c
+#define AFE_IRQ10_MCU_CFG0 0x9d90
+#define AFE_IRQ10_MCU_CFG1 0x9d94
+#define AFE_IRQ11_MCU_CFG0 0x9d98
+#define AFE_IRQ11_MCU_CFG1 0x9d9c
+#define AFE_IRQ12_MCU_CFG0 0x9da0
+#define AFE_IRQ12_MCU_CFG1 0x9da4
+#define AFE_IRQ13_MCU_CFG0 0x9da8
+#define AFE_IRQ13_MCU_CFG1 0x9dac
+#define AFE_IRQ14_MCU_CFG0 0x9db0
+#define AFE_IRQ14_MCU_CFG1 0x9db4
+#define AFE_IRQ15_MCU_CFG0 0x9db8
+#define AFE_IRQ15_MCU_CFG1 0x9dbc
+#define AFE_IRQ16_MCU_CFG0 0x9dc0
+#define AFE_IRQ16_MCU_CFG1 0x9dc4
+#define AFE_IRQ17_MCU_CFG0 0x9dc8
+#define AFE_IRQ17_MCU_CFG1 0x9dcc
+#define AFE_IRQ18_MCU_CFG0 0x9dd0
+#define AFE_IRQ18_MCU_CFG1 0x9dd4
+#define AFE_IRQ19_MCU_CFG0 0x9dd8
+#define AFE_IRQ19_MCU_CFG1 0x9ddc
+#define AFE_IRQ20_MCU_CFG0 0x9de0
+#define AFE_IRQ20_MCU_CFG1 0x9de4
+#define AFE_IRQ21_MCU_CFG0 0x9de8
+#define AFE_IRQ21_MCU_CFG1 0x9dec
+#define AFE_IRQ22_MCU_CFG0 0x9df0
+#define AFE_IRQ22_MCU_CFG1 0x9df4
+#define AFE_IRQ23_MCU_CFG0 0x9df8
+#define AFE_IRQ23_MCU_CFG1 0x9dfc
+#define AFE_IRQ24_MCU_CFG0 0x9e00
+#define AFE_IRQ24_MCU_CFG1 0x9e04
+#define AFE_IRQ25_MCU_CFG0 0x9e08
+#define AFE_IRQ25_MCU_CFG1 0x9e0c
+#define AFE_IRQ26_MCU_CFG0 0x9e10
+#define AFE_IRQ26_MCU_CFG1 0x9e14
+#define AFE_CUSTOM_IRQ0_MCU_CFG0 0x9e68
+#define AFE_CUSTOM_IRQ22_MCU_CFG0 0x9ec8
+#define AFE_CUSTOM_IRQ22_MCU_CFG1 0x9ecc
+#define AFE_CUSTOM_IRQ23_MCU_CFG0 0x9ed0
+#define AFE_CUSTOM_IRQ23_MCU_CFG1 0x9ed4
+#define AFE_IRQ0_CNT_MON 0x9f10
+#define AFE_IRQ1_CNT_MON 0x9f14
+#define AFE_IRQ2_CNT_MON 0x9f18
+#define AFE_IRQ3_CNT_MON 0x9f1c
+#define AFE_IRQ4_CNT_MON 0x9f20
+#define AFE_IRQ5_CNT_MON 0x9f24
+#define AFE_IRQ6_CNT_MON 0x9f28
+#define AFE_IRQ7_CNT_MON 0x9f2c
+#define AFE_IRQ8_CNT_MON 0x9f30
+#define AFE_IRQ9_CNT_MON 0x9f34
+#define AFE_IRQ10_CNT_MON 0x9f38
+#define AFE_IRQ11_CNT_MON 0x9f3c
+#define AFE_IRQ12_CNT_MON 0x9f40
+#define AFE_IRQ13_CNT_MON 0x9f44
+#define AFE_IRQ14_CNT_MON 0x9f48
+#define AFE_IRQ15_CNT_MON 0x9f4c
+#define AFE_IRQ16_CNT_MON 0x9f50
+#define AFE_IRQ17_CNT_MON 0x9f54
+#define AFE_IRQ18_CNT_MON 0x9f58
+#define AFE_IRQ19_CNT_MON 0x9f5c
+#define AFE_IRQ20_CNT_MON 0x9f60
+#define AFE_IRQ21_CNT_MON 0x9f64
+#define AFE_IRQ22_CNT_MON 0x9f68
+#define AFE_IRQ23_CNT_MON 0x9f6c
+#define AFE_IRQ24_CNT_MON 0x9f70
+#define AFE_IRQ25_CNT_MON 0x9f74
+#define AFE_IRQ26_CNT_MON 0x9f78
+#define AFE_CUSTOM_IRQ0_CNT_MON 0x9f90
+#define AFE_CUSTOM_IRQ0_MCU_CFG1 0x9fdc
+#define AFE_IRQ_MCU_DSP3_EN 0xa000
+#define AFE_CUSTOM_IRQ_MCU_DSP3_EN 0xa004
+#define AFE_CUSTOM2_IRQ_MCU_EN 0xa008
+#define AFE_CUSTOM2_IRQ_MCU_DSP_EN 0xa00c
+#define AFE_CUSTOM2_IRQ_MCU_DSP2_EN 0xa010
+#define AFE_CUSTOM2_IRQ_MCU_DSP3_EN 0xa014
+#define AFE_CUSTOM2_IRQ_MCU_SCP_EN 0xa018
+#define AFE_IRQ_MCU_MON3 0xa01c
+#define AFE_IRQ_MCU_MON0 0xa024
+#define AFE_IRQ_MCU_MON1 0xa028
+#define AFE_IRQ_MCU_MON2 0xa02c
+#define AFE_CUSTOM2_IRQ_MISS_FLAG_MCU_MON 0xa034
+#define AFE_CUSTOM2_IRQ_DELAY_EN 0xa038
+#define AFE_CUSTOM2_IRQ_MCU_STATUS 0xa03c
+#define AFE_CUSTOM2_IRQ0_MCU_CFG0 0xa040
+#define AFE_CUSTOM2_IRQ0_MCU_CFG1 0xa044
+#define AFE_CUSTOM2_IRQ0_CNT_MON 0xa048
+#define AFE_CUSTOM2_IRQ0_MCU_DELAY_CNT_CFG0 0xa04c
+#define AFE_CUSTOM2_IRQ1_MCU_CFG0 0xa050
+#define AFE_CUSTOM2_IRQ1_MCU_CFG1 0xa054
+#define AFE_CUSTOM2_IRQ1_CNT_MON 0xa058
+#define AFE_CUSTOM2_IRQ1_MCU_DELAY_CNT_CFG0 0xa05c
+#define AFE_CUSTOM2_IRQ2_MCU_CFG0 0xa060
+#define AFE_CUSTOM2_IRQ2_MCU_CFG1 0xa064
+#define AFE_CUSTOM2_IRQ2_CNT_MON 0xa068
+#define AFE_CUSTOM2_IRQ2_MCU_DELAY_CNT_CFG0 0xa06c
+#define AFE_CUSTOM2_IRQ3_MCU_CFG0 0xa070
+#define AFE_CUSTOM2_IRQ3_MCU_CFG1 0xa074
+#define AFE_CUSTOM2_IRQ3_CNT_MON 0xa078
+#define AFE_CUSTOM2_IRQ3_MCU_DELAY_CNT_CFG0 0xa07c
+#define AFE_CUSTOM2_IRQ4_MCU_CFG0 0xa080
+#define AFE_CUSTOM2_IRQ4_MCU_CFG1 0xa084
+#define AFE_CUSTOM2_IRQ4_CNT_MON 0xa088
+#define AFE_CUSTOM2_IRQ4_MCU_DELAY_CNT_CFG0 0xa08c
+#define AFE_CUSTOM2_IRQ5_MCU_CFG0 0xa090
+#define AFE_CUSTOM2_IRQ5_MCU_CFG1 0xa094
+#define AFE_CUSTOM2_IRQ5_CNT_MON 0xa098
+#define AFE_CUSTOM2_IRQ5_MCU_DELAY_CNT_CFG0 0xa09c
+#define AFE_CUSTOM2_IRQ6_MCU_CFG0 0xa0a0
+#define AFE_CUSTOM2_IRQ6_MCU_CFG1 0xa0a4
+#define AFE_CUSTOM2_IRQ6_CNT_MON 0xa0a8
+#define AFE_CUSTOM2_IRQ6_MCU_DELAY_CNT_CFG0 0xa0ac
+#define AFE_CUSTOM2_IRQ7_MCU_CFG0 0xa0b0
+#define AFE_CUSTOM2_IRQ7_MCU_CFG1 0xa0b4
+#define AFE_CUSTOM2_IRQ7_CNT_MON 0xa0b8
+#define AFE_CUSTOM2_IRQ7_MCU_DELAY_CNT_CFG0 0xa0bc
+#define AFE_CUSTOM2_IRQ8_MCU_CFG0 0xa0c0
+#define AFE_CUSTOM2_IRQ8_MCU_CFG1 0xa0c4
+#define AFE_CUSTOM2_IRQ8_CNT_MON 0xa0c8
+#define AFE_CUSTOM2_IRQ8_MCU_DELAY_CNT_CFG0 0xa0cc
+#define AFE_CUSTOM2_IRQ9_MCU_CFG0 0xa0d0
+#define AFE_CUSTOM2_IRQ9_MCU_CFG1 0xa0d4
+#define AFE_CUSTOM2_IRQ9_CNT_MON 0xa0d8
+#define AFE_CUSTOM2_IRQ9_MCU_DELAY_CNT_CFG0 0xa0dc
+#define AFE_CUSTOM2_IRQ10_MCU_CFG0 0xa0e0
+#define AFE_CUSTOM2_IRQ10_MCU_CFG1 0xa0e4
+#define AFE_CUSTOM2_IRQ10_CNT_MON 0xa0e8
+#define AFE_CUSTOM2_IRQ10_MCU_DELAY_CNT_CFG0 0xa0ec
+#define AFE_CUSTOM2_IRQ11_MCU_CFG0 0xa0f0
+#define AFE_CUSTOM2_IRQ11_MCU_CFG1 0xa0f4
+#define AFE_CUSTOM2_IRQ11_CNT_MON 0xa0f8
+#define AFE_CUSTOM2_IRQ11_MCU_DELAY_CNT_CFG0 0xa0fc
+#define AFE_CUSTOM2_IRQ12_MCU_CFG0 0xa100
+#define AFE_CUSTOM2_IRQ12_MCU_CFG1 0xa104
+#define AFE_CUSTOM2_IRQ12_CNT_MON 0xa108
+#define AFE_CUSTOM2_IRQ12_MCU_DELAY_CNT_CFG0 0xa10c
+#define AFE_CUSTOM2_IRQ30_MCU_CFG0 0xa220
+#define AFE_CUSTOM2_IRQ30_MCU_CFG1 0xa224
+#define AFE_CUSTOM2_IRQ30_CNT_MON 0xa228
+#define AFE_CUSTOM2_IRQ30_MCU_DELAY_CNT_CFG0 0xa22c
+#define AFE_CUSTOM2_IRQ31_MCU_CFG0 0xa230
+#define AFE_CUSTOM2_IRQ31_MCU_CFG1 0xa234
+#define AFE_CUSTOM2_IRQ31_CNT_MON 0xa238
+#define AFE_CUSTOM2_IRQ31_MCU_DELAY_CNT_CFG0 0xa23c
+#define AFE_CUSTOM3_IRQ8_MCU_CFG0 0xa2c0
+#define AFE_CUSTOM3_IRQ8_MCU_CFG1 0xa2c4
+#define AFE_CUSTOM3_IRQ8_CNT_MON 0xa2c8
+#define AFE_CUSTOM3_IRQ8_MCU_DELAY_CNT_CFG0 0xa2cc
+#define AFE_CUSTOM3_IRQ9_MCU_CFG0 0xa2d0
+#define AFE_CUSTOM3_IRQ9_MCU_CFG1 0xa2d4
+#define AFE_CUSTOM3_IRQ9_CNT_MON 0xa2d8
+#define AFE_CUSTOM3_IRQ9_MCU_DELAY_CNT_CFG0 0xa2dc
+#define AFE_CUSTOM3_IRQ_MISS_FLAG_MCU_MON 0xa440
+#define AFE_CUSTOM3_IRQ_DELAY_EN 0xa444
+#define AFE_CUSTOM3_IRQ_MCU_STATUS 0xa448
+#define AFE_CUSTOM3_IRQ_MCU_EN 0xa44c
+#define AFE_CUSTOM3_IRQ_MCU_DSP_EN 0xa450
+#define AFE_CUSTOM3_IRQ_MCU_DSP2_EN 0xa454
+#define AFE_CUSTOM3_IRQ_MCU_DSP3_EN 0xa458
+#define AFE_CUSTOM3_IRQ_MCU_DSP_WLA_EN 0xa45c
+#define AFE_CUSTOM3_IRQ_MCU_SCP_EN 0xa460
+#define AFE_CUSTOM2_IRQ_MCU_DSP_WLA_EN 0xa464
+#define AFE_IRQ_MCU_DSP_WLA_EN 0xa468
+#define AFE_COMMON2_IRQ_MCU_STATUS 0xa46c
+#define AFE_COMMON2_IRQ_MCU_EN 0xa470
+#define AFE_COMMON2_IRQ_MCU_DSP_EN 0xa474
+#define AFE_COMMON2_IRQ_MCU_DSP2_EN 0xa478
+#define AFE_COMMON2_IRQ_MCU_DSP3_EN 0xa47c
+#define AFE_COMMON2_IRQ_MCU_DSP_WLA_EN 0xa480
+#define AFE_COMMON2_IRQ_MCU_SCP_EN 0xa484
+#define AFE_CUSTOM_IRQ_MCU_DSP_WLA_EN 0xa508
+
+#define AFE_MAX_REGISTER AFE_CUSTOM_IRQ_MCU_DSP_WLA_EN
+
+#define AFE_IRQ_STATUS_BITS 0x7FFFFFF
+#define AFE_IRQ_CNT_SHIFT 0
+#define AFE_IRQ_CNT_MASK 0xffffff
+
+#endif
diff --git a/sound/soc/mediatek/mt8192/Makefile b/sound/soc/mediatek/mt8192/Makefile
new file mode 100644
index 000000000000..d60c36bcdcce
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt8192-afe-y := \
+ mt8192-afe-pcm.o \
+ mt8192-afe-clk.o \
+ mt8192-afe-gpio.o \
+ mt8192-dai-adda.o \
+ mt8192-afe-control.o \
+ mt8192-dai-i2s.o \
+ mt8192-dai-pcm.o \
+ mt8192-dai-tdm.o
+
+obj-$(CONFIG_SND_SOC_MT8192) += snd-soc-mt8192-afe.o
+obj-$(CONFIG_SND_SOC_MT8192_MT6359_RT1015_RT5682) += \
+ mt8192-mt6359-rt1015-rt5682.o
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-clk.c b/sound/soc/mediatek/mt8192/mt8192-afe-clk.c
new file mode 100644
index 000000000000..416aff726253
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-clk.c
@@ -0,0 +1,665 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8192-afe-clk.c -- Mediatek 8192 afe clock ctrl
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "mt8192-afe-clk.h"
+#include "mt8192-afe-common.h"
+
+static const char *aud_clks[CLK_NUM] = {
+ [CLK_AFE] = "aud_afe_clk",
+ [CLK_TML] = "aud_tml_clk",
+ [CLK_APLL22M] = "aud_apll22m_clk",
+ [CLK_APLL24M] = "aud_apll24m_clk",
+ [CLK_APLL1_TUNER] = "aud_apll1_tuner_clk",
+ [CLK_APLL2_TUNER] = "aud_apll2_tuner_clk",
+ [CLK_NLE] = "aud_nle",
+ [CLK_INFRA_SYS_AUDIO] = "aud_infra_clk",
+ [CLK_INFRA_AUDIO_26M] = "aud_infra_26m_clk",
+ [CLK_MUX_AUDIO] = "top_mux_audio",
+ [CLK_MUX_AUDIOINTBUS] = "top_mux_audio_int",
+ [CLK_TOP_MAINPLL_D4_D4] = "top_mainpll_d4_d4",
+ [CLK_TOP_MUX_AUD_1] = "top_mux_aud_1",
+ [CLK_TOP_APLL1_CK] = "top_apll1_ck",
+ [CLK_TOP_MUX_AUD_2] = "top_mux_aud_2",
+ [CLK_TOP_APLL2_CK] = "top_apll2_ck",
+ [CLK_TOP_MUX_AUD_ENG1] = "top_mux_aud_eng1",
+ [CLK_TOP_APLL1_D4] = "top_apll1_d4",
+ [CLK_TOP_MUX_AUD_ENG2] = "top_mux_aud_eng2",
+ [CLK_TOP_APLL2_D4] = "top_apll2_d4",
+ [CLK_TOP_MUX_AUDIO_H] = "top_mux_audio_h",
+ [CLK_TOP_I2S0_M_SEL] = "top_i2s0_m_sel",
+ [CLK_TOP_I2S1_M_SEL] = "top_i2s1_m_sel",
+ [CLK_TOP_I2S2_M_SEL] = "top_i2s2_m_sel",
+ [CLK_TOP_I2S3_M_SEL] = "top_i2s3_m_sel",
+ [CLK_TOP_I2S4_M_SEL] = "top_i2s4_m_sel",
+ [CLK_TOP_I2S5_M_SEL] = "top_i2s5_m_sel",
+ [CLK_TOP_I2S6_M_SEL] = "top_i2s6_m_sel",
+ [CLK_TOP_I2S7_M_SEL] = "top_i2s7_m_sel",
+ [CLK_TOP_I2S8_M_SEL] = "top_i2s8_m_sel",
+ [CLK_TOP_I2S9_M_SEL] = "top_i2s9_m_sel",
+ [CLK_TOP_APLL12_DIV0] = "top_apll12_div0",
+ [CLK_TOP_APLL12_DIV1] = "top_apll12_div1",
+ [CLK_TOP_APLL12_DIV2] = "top_apll12_div2",
+ [CLK_TOP_APLL12_DIV3] = "top_apll12_div3",
+ [CLK_TOP_APLL12_DIV4] = "top_apll12_div4",
+ [CLK_TOP_APLL12_DIVB] = "top_apll12_divb",
+ [CLK_TOP_APLL12_DIV5] = "top_apll12_div5",
+ [CLK_TOP_APLL12_DIV6] = "top_apll12_div6",
+ [CLK_TOP_APLL12_DIV7] = "top_apll12_div7",
+ [CLK_TOP_APLL12_DIV8] = "top_apll12_div8",
+ [CLK_TOP_APLL12_DIV9] = "top_apll12_div9",
+ [CLK_CLK26M] = "top_clk26m_clk",
+};
+
+int mt8192_set_audio_int_bus_parent(struct mtk_base_afe *afe,
+ int clk_id)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIOINTBUS],
+ afe_priv->clk[clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS],
+ aud_clks[clk_id], ret);
+ }
+
+ return ret;
+}
+
+static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (enable) {
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1], ret);
+ goto EXIT;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_TOP_APLL1_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1],
+ aud_clks[CLK_TOP_APLL1_CK], ret);
+ goto EXIT;
+ }
+
+ /* 180.6336 / 4 = 45.1584MHz */
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret);
+ goto EXIT;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_TOP_APLL1_D4]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
+ aud_clks[CLK_TOP_APLL1_D4], ret);
+ goto EXIT;
+ }
+ } else {
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_1],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ }
+
+EXIT:
+ return ret;
+}
+
+static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (enable) {
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2], ret);
+ goto EXIT;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_TOP_APLL2_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2],
+ aud_clks[CLK_TOP_APLL2_CK], ret);
+ goto EXIT;
+ }
+
+ /* 196.608 / 4 = 49.152MHz */
+ ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret);
+ goto EXIT;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_TOP_APLL2_D4]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
+ aud_clks[CLK_TOP_APLL2_D4], ret);
+ goto EXIT;
+ }
+ } else {
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUD_2],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ }
+
+EXIT:
+ return ret;
+}
+
+int mt8192_afe_enable_clock(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_SYS_AUDIO], ret);
+ goto EXIT;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_INFRA_AUDIO_26M], ret);
+ goto EXIT;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIO], ret);
+ goto EXIT;
+ }
+ ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIO],
+ afe_priv->clk[CLK_CLK26M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIO],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
+ goto EXIT;
+ }
+
+ ret = mt8192_set_audio_int_bus_parent(afe, CLK_CLK26M);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_MUX_AUDIOINTBUS],
+ aud_clks[CLK_CLK26M], ret);
+ goto EXIT;
+ }
+
+ ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[CLK_TOP_APLL2_CK]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[CLK_TOP_MUX_AUDIO_H],
+ aud_clks[CLK_TOP_APLL2_CK], ret);
+ goto EXIT;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_AFE], ret);
+ goto EXIT;
+ }
+
+EXIT:
+ return ret;
+}
+
+void mt8192_afe_disable_clock(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
+ mt8192_set_audio_int_bus_parent(afe, CLK_CLK26M);
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+}
+
+int mt8192_apll1_enable(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* setting for APLL */
+ apll1_mux_setting(afe, true);
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL22M], ret);
+ goto EXIT;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL1_TUNER]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL1_TUNER], ret);
+ goto EXIT;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ 0x0000FFF7, 0x00000832);
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x1);
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_ON_MASK_SFT,
+ 0x1 << AFE_22M_ON_SFT);
+
+EXIT:
+ return ret;
+}
+
+void mt8192_apll1_disable(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_ON_MASK_SFT,
+ 0x0 << AFE_22M_ON_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x0);
+
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]);
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+
+ apll1_mux_setting(afe, false);
+}
+
+int mt8192_apll2_enable(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* setting for APLL */
+ apll2_mux_setting(afe, true);
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL24M], ret);
+ goto EXIT;
+ }
+
+ ret = clk_prepare_enable(afe_priv->clk[CLK_APLL2_TUNER]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[CLK_APLL2_TUNER], ret);
+ goto EXIT;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ 0x0000FFF7, 0x00000634);
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x1);
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_ON_MASK_SFT,
+ 0x1 << AFE_24M_ON_SFT);
+
+EXIT:
+ return ret;
+}
+
+void mt8192_apll2_disable(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_ON_MASK_SFT,
+ 0x0 << AFE_24M_ON_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x0);
+
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]);
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+
+ apll2_mux_setting(afe, false);
+}
+
+int mt8192_get_apll_rate(struct mtk_base_afe *afe, int apll)
+{
+ return (apll == MT8192_APLL1) ? 180633600 : 196608000;
+}
+
+int mt8192_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return ((rate % 8000) == 0) ? MT8192_APLL2 : MT8192_APLL1;
+}
+
+int mt8192_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8192_APLL1;
+ else
+ return MT8192_APLL2;
+}
+
+/* mck */
+struct mt8192_mck_div {
+ int m_sel_id;
+ int div_clk_id;
+ /* below will be deprecated */
+ int div_pdn_reg;
+ int div_pdn_mask_sft;
+ int div_reg;
+ int div_mask_sft;
+ int div_mask;
+ int div_sft;
+ int div_apll_sel_reg;
+ int div_apll_sel_mask_sft;
+ int div_apll_sel_sft;
+};
+
+static const struct mt8192_mck_div mck_div[MT8192_MCK_NUM] = {
+ [MT8192_I2S0_MCK] = {
+ .m_sel_id = CLK_TOP_I2S0_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV0,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV0_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_2,
+ .div_mask_sft = APLL12_CK_DIV0_MASK_SFT,
+ .div_mask = APLL12_CK_DIV0_MASK,
+ .div_sft = APLL12_CK_DIV0_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S0_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S0_MCK_SEL_SFT,
+ },
+ [MT8192_I2S1_MCK] = {
+ .m_sel_id = CLK_TOP_I2S1_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV1,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV1_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_2,
+ .div_mask_sft = APLL12_CK_DIV1_MASK_SFT,
+ .div_mask = APLL12_CK_DIV1_MASK,
+ .div_sft = APLL12_CK_DIV1_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S1_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S1_MCK_SEL_SFT,
+ },
+ [MT8192_I2S2_MCK] = {
+ .m_sel_id = CLK_TOP_I2S2_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV2,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV2_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_2,
+ .div_mask_sft = APLL12_CK_DIV2_MASK_SFT,
+ .div_mask = APLL12_CK_DIV2_MASK,
+ .div_sft = APLL12_CK_DIV2_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S2_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S2_MCK_SEL_SFT,
+ },
+ [MT8192_I2S3_MCK] = {
+ .m_sel_id = CLK_TOP_I2S3_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV3,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV3_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_2,
+ .div_mask_sft = APLL12_CK_DIV3_MASK_SFT,
+ .div_mask = APLL12_CK_DIV3_MASK,
+ .div_sft = APLL12_CK_DIV3_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S3_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S3_MCK_SEL_SFT,
+ },
+ [MT8192_I2S4_MCK] = {
+ .m_sel_id = CLK_TOP_I2S4_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV4,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV4_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_3,
+ .div_mask_sft = APLL12_CK_DIV4_MASK_SFT,
+ .div_mask = APLL12_CK_DIV4_MASK,
+ .div_sft = APLL12_CK_DIV4_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S4_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S4_MCK_SEL_SFT,
+ },
+ [MT8192_I2S4_BCK] = {
+ .m_sel_id = -1,
+ .div_clk_id = CLK_TOP_APLL12_DIVB,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIVB_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_2,
+ .div_mask_sft = APLL12_CK_DIVB_MASK_SFT,
+ .div_mask = APLL12_CK_DIVB_MASK,
+ .div_sft = APLL12_CK_DIVB_SFT,
+ },
+ [MT8192_I2S5_MCK] = {
+ .m_sel_id = CLK_TOP_I2S5_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV5,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV5_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_3,
+ .div_mask_sft = APLL12_CK_DIV5_MASK_SFT,
+ .div_mask = APLL12_CK_DIV5_MASK,
+ .div_sft = APLL12_CK_DIV5_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S5_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S5_MCK_SEL_SFT,
+ },
+ [MT8192_I2S6_MCK] = {
+ .m_sel_id = CLK_TOP_I2S6_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV6,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV6_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_3,
+ .div_mask_sft = APLL12_CK_DIV6_MASK_SFT,
+ .div_mask = APLL12_CK_DIV6_MASK,
+ .div_sft = APLL12_CK_DIV6_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S6_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S6_MCK_SEL_SFT,
+ },
+ [MT8192_I2S7_MCK] = {
+ .m_sel_id = CLK_TOP_I2S7_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV7,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV7_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_4,
+ .div_mask_sft = APLL12_CK_DIV7_MASK_SFT,
+ .div_mask = APLL12_CK_DIV7_MASK,
+ .div_sft = APLL12_CK_DIV7_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S7_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S7_MCK_SEL_SFT,
+ },
+ [MT8192_I2S8_MCK] = {
+ .m_sel_id = CLK_TOP_I2S8_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV8,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV8_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_4,
+ .div_mask_sft = APLL12_CK_DIV8_MASK_SFT,
+ .div_mask = APLL12_CK_DIV8_MASK,
+ .div_sft = APLL12_CK_DIV8_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S8_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S8_MCK_SEL_SFT,
+ },
+ [MT8192_I2S9_MCK] = {
+ .m_sel_id = CLK_TOP_I2S9_M_SEL,
+ .div_clk_id = CLK_TOP_APLL12_DIV9,
+ .div_pdn_reg = CLK_AUDDIV_0,
+ .div_pdn_mask_sft = APLL12_DIV9_PDN_MASK_SFT,
+ .div_reg = CLK_AUDDIV_4,
+ .div_mask_sft = APLL12_CK_DIV9_MASK_SFT,
+ .div_mask = APLL12_CK_DIV9_MASK,
+ .div_sft = APLL12_CK_DIV9_SFT,
+ .div_apll_sel_reg = CLK_AUDDIV_0,
+ .div_apll_sel_mask_sft = APLL_I2S9_MCK_SEL_MASK_SFT,
+ .div_apll_sel_sft = APLL_I2S9_MCK_SEL_SFT,
+ },
+};
+
+int mt8192_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int apll = mt8192_get_apll_by_rate(afe, rate);
+ int apll_clk_id = apll == MT8192_APLL1 ?
+ CLK_TOP_MUX_AUD_1 : CLK_TOP_MUX_AUD_2;
+ int m_sel_id = mck_div[mck_id].m_sel_id;
+ int div_clk_id = mck_div[mck_id].div_clk_id;
+ int ret;
+
+ /* select apll */
+ if (m_sel_id >= 0) {
+ ret = clk_prepare_enable(afe_priv->clk[m_sel_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[m_sel_id], ret);
+ return ret;
+ }
+ ret = clk_set_parent(afe_priv->clk[m_sel_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[m_sel_id],
+ aud_clks[apll_clk_id], ret);
+ return ret;
+ }
+ }
+
+ /* enable div, set rate */
+ ret = clk_prepare_enable(afe_priv->clk[div_clk_id]);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[div_clk_id], ret);
+ return ret;
+ }
+ ret = clk_set_rate(afe_priv->clk[div_clk_id], rate);
+ if (ret) {
+ dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n",
+ __func__, aud_clks[div_clk_id],
+ rate, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void mt8192_mck_disable(struct mtk_base_afe *afe, int mck_id)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int m_sel_id = mck_div[mck_id].m_sel_id;
+ int div_clk_id = mck_div[mck_id].div_clk_id;
+
+ clk_disable_unprepare(afe_priv->clk[div_clk_id]);
+ if (m_sel_id >= 0)
+ clk_disable_unprepare(afe_priv->clk[m_sel_id]);
+}
+
+int mt8192_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ struct device_node *of_node = afe->dev->of_node;
+ int i = 0;
+
+ afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_warn(afe->dev, "%s devm_clk_get %s fail, ret %ld\n",
+ __func__,
+ aud_clks[i], PTR_ERR(afe_priv->clk[i]));
+ afe_priv->clk[i] = NULL;
+ }
+ }
+
+ afe_priv->apmixedsys = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,apmixedsys");
+ if (IS_ERR(afe_priv->apmixedsys)) {
+ dev_err(afe->dev, "%s() Cannot find apmixedsys controller: %ld\n",
+ __func__, PTR_ERR(afe_priv->apmixedsys));
+ return PTR_ERR(afe_priv->apmixedsys);
+ }
+
+ afe_priv->topckgen = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,topckgen");
+ if (IS_ERR(afe_priv->topckgen)) {
+ dev_err(afe->dev, "%s() Cannot find topckgen controller: %ld\n",
+ __func__, PTR_ERR(afe_priv->topckgen));
+ return PTR_ERR(afe_priv->topckgen);
+ }
+
+ afe_priv->infracfg = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,infracfg");
+ if (IS_ERR(afe_priv->infracfg)) {
+ dev_err(afe->dev, "%s() Cannot find infracfg: %ld\n",
+ __func__, PTR_ERR(afe_priv->infracfg));
+ return PTR_ERR(afe_priv->infracfg);
+ }
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-clk.h b/sound/soc/mediatek/mt8192/mt8192-afe-clk.h
new file mode 100644
index 000000000000..3adaf027af83
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-clk.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8192-afe-clk.h -- Mediatek 8192 afe clock ctrl definition
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Shane Chien <shane.chien@mediatek.com>
+ */
+
+#ifndef _MT8192_AFE_CLOCK_CTRL_H_
+#define _MT8192_AFE_CLOCK_CTRL_H_
+
+#define AP_PLL_CON3 0x0014
+#define APLL1_CON0 0x0318
+#define APLL1_CON1 0x031c
+#define APLL1_CON2 0x0320
+#define APLL1_CON4 0x0328
+#define APLL1_TUNER_CON0 0x0040
+
+#define APLL2_CON0 0x032c
+#define APLL2_CON1 0x0330
+#define APLL2_CON2 0x0334
+#define APLL2_CON4 0x033c
+#define APLL2_TUNER_CON0 0x0044
+
+#define CLK_CFG_7 0x0080
+#define CLK_CFG_8 0x0090
+#define CLK_CFG_11 0x00c0
+#define CLK_CFG_12 0x00d0
+#define CLK_CFG_13 0x00e0
+#define CLK_CFG_15 0x0100
+
+#define CLK_AUDDIV_0 0x0320
+#define CLK_AUDDIV_2 0x0328
+#define CLK_AUDDIV_3 0x0334
+#define CLK_AUDDIV_4 0x0338
+#define CKSYS_AUD_TOP_CFG 0x032c
+#define CKSYS_AUD_TOP_MON 0x0330
+
+#define PERI_BUS_DCM_CTRL 0x0074
+#define MODULE_SW_CG_1_STA 0x0094
+#define MODULE_SW_CG_2_STA 0x00ac
+
+/* CLK_AUDDIV_0 */
+#define APLL12_DIV0_PDN_SFT 0
+#define APLL12_DIV0_PDN_MASK 0x1
+#define APLL12_DIV0_PDN_MASK_SFT (0x1 << 0)
+#define APLL12_DIV1_PDN_SFT 1
+#define APLL12_DIV1_PDN_MASK 0x1
+#define APLL12_DIV1_PDN_MASK_SFT (0x1 << 1)
+#define APLL12_DIV2_PDN_SFT 2
+#define APLL12_DIV2_PDN_MASK 0x1
+#define APLL12_DIV2_PDN_MASK_SFT (0x1 << 2)
+#define APLL12_DIV3_PDN_SFT 3
+#define APLL12_DIV3_PDN_MASK 0x1
+#define APLL12_DIV3_PDN_MASK_SFT (0x1 << 3)
+#define APLL12_DIV4_PDN_SFT 4
+#define APLL12_DIV4_PDN_MASK 0x1
+#define APLL12_DIV4_PDN_MASK_SFT (0x1 << 4)
+#define APLL12_DIVB_PDN_SFT 5
+#define APLL12_DIVB_PDN_MASK 0x1
+#define APLL12_DIVB_PDN_MASK_SFT (0x1 << 5)
+#define APLL12_DIV5_PDN_SFT 6
+#define APLL12_DIV5_PDN_MASK 0x1
+#define APLL12_DIV5_PDN_MASK_SFT (0x1 << 6)
+#define APLL12_DIV6_PDN_SFT 7
+#define APLL12_DIV6_PDN_MASK 0x1
+#define APLL12_DIV6_PDN_MASK_SFT (0x1 << 7)
+#define APLL12_DIV7_PDN_SFT 8
+#define APLL12_DIV7_PDN_MASK 0x1
+#define APLL12_DIV7_PDN_MASK_SFT (0x1 << 8)
+#define APLL12_DIV8_PDN_SFT 9
+#define APLL12_DIV8_PDN_MASK 0x1
+#define APLL12_DIV8_PDN_MASK_SFT (0x1 << 9)
+#define APLL12_DIV9_PDN_SFT 10
+#define APLL12_DIV9_PDN_MASK 0x1
+#define APLL12_DIV9_PDN_MASK_SFT (0x1 << 10)
+#define APLL_I2S0_MCK_SEL_SFT 16
+#define APLL_I2S0_MCK_SEL_MASK 0x1
+#define APLL_I2S0_MCK_SEL_MASK_SFT (0x1 << 16)
+#define APLL_I2S1_MCK_SEL_SFT 17
+#define APLL_I2S1_MCK_SEL_MASK 0x1
+#define APLL_I2S1_MCK_SEL_MASK_SFT (0x1 << 17)
+#define APLL_I2S2_MCK_SEL_SFT 18
+#define APLL_I2S2_MCK_SEL_MASK 0x1
+#define APLL_I2S2_MCK_SEL_MASK_SFT (0x1 << 18)
+#define APLL_I2S3_MCK_SEL_SFT 19
+#define APLL_I2S3_MCK_SEL_MASK 0x1
+#define APLL_I2S3_MCK_SEL_MASK_SFT (0x1 << 19)
+#define APLL_I2S4_MCK_SEL_SFT 20
+#define APLL_I2S4_MCK_SEL_MASK 0x1
+#define APLL_I2S4_MCK_SEL_MASK_SFT (0x1 << 20)
+#define APLL_I2S5_MCK_SEL_SFT 21
+#define APLL_I2S5_MCK_SEL_MASK 0x1
+#define APLL_I2S5_MCK_SEL_MASK_SFT (0x1 << 21)
+#define APLL_I2S6_MCK_SEL_SFT 22
+#define APLL_I2S6_MCK_SEL_MASK 0x1
+#define APLL_I2S6_MCK_SEL_MASK_SFT (0x1 << 22)
+#define APLL_I2S7_MCK_SEL_SFT 23
+#define APLL_I2S7_MCK_SEL_MASK 0x1
+#define APLL_I2S7_MCK_SEL_MASK_SFT (0x1 << 23)
+#define APLL_I2S8_MCK_SEL_SFT 24
+#define APLL_I2S8_MCK_SEL_MASK 0x1
+#define APLL_I2S8_MCK_SEL_MASK_SFT (0x1 << 24)
+#define APLL_I2S9_MCK_SEL_SFT 25
+#define APLL_I2S9_MCK_SEL_MASK 0x1
+#define APLL_I2S9_MCK_SEL_MASK_SFT (0x1 << 25)
+
+/* CLK_AUDDIV_2 */
+#define APLL12_CK_DIV0_SFT 0
+#define APLL12_CK_DIV0_MASK 0xff
+#define APLL12_CK_DIV0_MASK_SFT (0xff << 0)
+#define APLL12_CK_DIV1_SFT 8
+#define APLL12_CK_DIV1_MASK 0xff
+#define APLL12_CK_DIV1_MASK_SFT (0xff << 8)
+#define APLL12_CK_DIV2_SFT 16
+#define APLL12_CK_DIV2_MASK 0xff
+#define APLL12_CK_DIV2_MASK_SFT (0xff << 16)
+#define APLL12_CK_DIV3_SFT 24
+#define APLL12_CK_DIV3_MASK 0xff
+#define APLL12_CK_DIV3_MASK_SFT (0xff << 24)
+
+/* CLK_AUDDIV_3 */
+#define APLL12_CK_DIV4_SFT 0
+#define APLL12_CK_DIV4_MASK 0xff
+#define APLL12_CK_DIV4_MASK_SFT (0xff << 0)
+#define APLL12_CK_DIVB_SFT 8
+#define APLL12_CK_DIVB_MASK 0xff
+#define APLL12_CK_DIVB_MASK_SFT (0xff << 8)
+#define APLL12_CK_DIV5_SFT 16
+#define APLL12_CK_DIV5_MASK 0xff
+#define APLL12_CK_DIV5_MASK_SFT (0xff << 16)
+#define APLL12_CK_DIV6_SFT 24
+#define APLL12_CK_DIV6_MASK 0xff
+#define APLL12_CK_DIV6_MASK_SFT (0xff << 24)
+
+/* CLK_AUDDIV_4 */
+#define APLL12_CK_DIV7_SFT 0
+#define APLL12_CK_DIV7_MASK 0xff
+#define APLL12_CK_DIV7_MASK_SFT (0xff << 0)
+#define APLL12_CK_DIV8_SFT 8
+#define APLL12_CK_DIV8_MASK 0xff
+#define APLL12_CK_DIV8_MASK_SFT (0xff << 0)
+#define APLL12_CK_DIV9_SFT 16
+#define APLL12_CK_DIV9_MASK 0xff
+#define APLL12_CK_DIV9_MASK_SFT (0xff << 0)
+
+/* AUD_TOP_CFG */
+#define AUD_TOP_CFG_SFT 0
+#define AUD_TOP_CFG_MASK 0xffffffff
+#define AUD_TOP_CFG_MASK_SFT (0xffffffff << 0)
+
+/* AUD_TOP_MON */
+#define AUD_TOP_MON_SFT 0
+#define AUD_TOP_MON_MASK 0xffffffff
+#define AUD_TOP_MON_MASK_SFT (0xffffffff << 0)
+
+/* CLK_AUDDIV_3 */
+#define APLL12_CK_DIV5_MSB_SFT 0
+#define APLL12_CK_DIV5_MSB_MASK 0xf
+#define APLL12_CK_DIV5_MSB_MASK_SFT (0xf << 0)
+#define RESERVED0_SFT 4
+#define RESERVED0_MASK 0xfffffff
+#define RESERVED0_MASK_SFT (0xfffffff << 4)
+
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+enum {
+ MT8192_APLL1 = 0,
+ MT8192_APLL2,
+};
+
+enum {
+ CLK_AFE = 0,
+ CLK_TML,
+ CLK_APLL22M,
+ CLK_APLL24M,
+ CLK_APLL1_TUNER,
+ CLK_APLL2_TUNER,
+ CLK_NLE,
+ CLK_INFRA_SYS_AUDIO,
+ CLK_INFRA_AUDIO_26M,
+ CLK_MUX_AUDIO,
+ CLK_MUX_AUDIOINTBUS,
+ CLK_TOP_MAINPLL_D4_D4,
+ /* apll related mux */
+ CLK_TOP_MUX_AUD_1,
+ CLK_TOP_APLL1_CK,
+ CLK_TOP_MUX_AUD_2,
+ CLK_TOP_APLL2_CK,
+ CLK_TOP_MUX_AUD_ENG1,
+ CLK_TOP_APLL1_D4,
+ CLK_TOP_MUX_AUD_ENG2,
+ CLK_TOP_APLL2_D4,
+ CLK_TOP_MUX_AUDIO_H,
+ CLK_TOP_I2S0_M_SEL,
+ CLK_TOP_I2S1_M_SEL,
+ CLK_TOP_I2S2_M_SEL,
+ CLK_TOP_I2S3_M_SEL,
+ CLK_TOP_I2S4_M_SEL,
+ CLK_TOP_I2S5_M_SEL,
+ CLK_TOP_I2S6_M_SEL,
+ CLK_TOP_I2S7_M_SEL,
+ CLK_TOP_I2S8_M_SEL,
+ CLK_TOP_I2S9_M_SEL,
+ CLK_TOP_APLL12_DIV0,
+ CLK_TOP_APLL12_DIV1,
+ CLK_TOP_APLL12_DIV2,
+ CLK_TOP_APLL12_DIV3,
+ CLK_TOP_APLL12_DIV4,
+ CLK_TOP_APLL12_DIVB,
+ CLK_TOP_APLL12_DIV5,
+ CLK_TOP_APLL12_DIV6,
+ CLK_TOP_APLL12_DIV7,
+ CLK_TOP_APLL12_DIV8,
+ CLK_TOP_APLL12_DIV9,
+ CLK_CLK26M,
+ CLK_NUM
+};
+
+struct mtk_base_afe;
+
+int mt8192_init_clock(struct mtk_base_afe *afe);
+int mt8192_afe_enable_clock(struct mtk_base_afe *afe);
+void mt8192_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt8192_apll1_enable(struct mtk_base_afe *afe);
+void mt8192_apll1_disable(struct mtk_base_afe *afe);
+
+int mt8192_apll2_enable(struct mtk_base_afe *afe);
+void mt8192_apll2_disable(struct mtk_base_afe *afe);
+
+int mt8192_get_apll_rate(struct mtk_base_afe *afe, int apll);
+int mt8192_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8192_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
+
+/* these will be replaced by using CCF */
+int mt8192_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
+void mt8192_mck_disable(struct mtk_base_afe *afe, int mck_id);
+
+int mt8192_set_audio_int_bus_parent(struct mtk_base_afe *afe,
+ int clk_id);
+
+#endif
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-common.h b/sound/soc/mediatek/mt8192/mt8192-afe-common.h
new file mode 100644
index 000000000000..ad461dcb6ee1
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-common.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8192-afe-common.h -- Mediatek 8192 audio driver definitions
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Shane Chien <shane.chien@mediatek.com>
+ */
+
+#ifndef _MT_8192_AFE_COMMON_H_
+#define _MT_8192_AFE_COMMON_H_
+
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "../common/mtk-base-afe.h"
+#include "mt8192-reg.h"
+
+enum {
+ MT8192_MEMIF_DL1,
+ MT8192_MEMIF_DL12,
+ MT8192_MEMIF_DL2,
+ MT8192_MEMIF_DL3,
+ MT8192_MEMIF_DL4,
+ MT8192_MEMIF_DL5,
+ MT8192_MEMIF_DL6,
+ MT8192_MEMIF_DL7,
+ MT8192_MEMIF_DL8,
+ MT8192_MEMIF_DL9,
+ MT8192_MEMIF_DAI,
+ MT8192_MEMIF_DAI2,
+ MT8192_MEMIF_MOD_DAI,
+ MT8192_MEMIF_VUL12,
+ MT8192_MEMIF_VUL2,
+ MT8192_MEMIF_VUL3,
+ MT8192_MEMIF_VUL4,
+ MT8192_MEMIF_VUL5,
+ MT8192_MEMIF_VUL6,
+ MT8192_MEMIF_AWB,
+ MT8192_MEMIF_AWB2,
+ MT8192_MEMIF_HDMI,
+ MT8192_MEMIF_NUM,
+ MT8192_DAI_ADDA = MT8192_MEMIF_NUM,
+ MT8192_DAI_ADDA_CH34,
+ MT8192_DAI_AP_DMIC,
+ MT8192_DAI_AP_DMIC_CH34,
+ MT8192_DAI_VOW,
+ MT8192_DAI_CONNSYS_I2S,
+ MT8192_DAI_I2S_0,
+ MT8192_DAI_I2S_1,
+ MT8192_DAI_I2S_2,
+ MT8192_DAI_I2S_3,
+ MT8192_DAI_I2S_5,
+ MT8192_DAI_I2S_6,
+ MT8192_DAI_I2S_7,
+ MT8192_DAI_I2S_8,
+ MT8192_DAI_I2S_9,
+ MT8192_DAI_HW_GAIN_1,
+ MT8192_DAI_HW_GAIN_2,
+ MT8192_DAI_SRC_1,
+ MT8192_DAI_SRC_2,
+ MT8192_DAI_PCM_1,
+ MT8192_DAI_PCM_2,
+ MT8192_DAI_TDM,
+ MT8192_DAI_NUM,
+};
+
+enum {
+ MT8192_IRQ_0,
+ MT8192_IRQ_1,
+ MT8192_IRQ_2,
+ MT8192_IRQ_3,
+ MT8192_IRQ_4,
+ MT8192_IRQ_5,
+ MT8192_IRQ_6,
+ MT8192_IRQ_7,
+ MT8192_IRQ_8,
+ MT8192_IRQ_9,
+ MT8192_IRQ_10,
+ MT8192_IRQ_11,
+ MT8192_IRQ_12,
+ MT8192_IRQ_13,
+ MT8192_IRQ_14,
+ MT8192_IRQ_15,
+ MT8192_IRQ_16,
+ MT8192_IRQ_17,
+ MT8192_IRQ_18,
+ MT8192_IRQ_19,
+ MT8192_IRQ_20,
+ MT8192_IRQ_21,
+ MT8192_IRQ_22,
+ MT8192_IRQ_23,
+ MT8192_IRQ_24,
+ MT8192_IRQ_25,
+ MT8192_IRQ_26,
+ MT8192_IRQ_31, /* used only for TDM */
+ MT8192_IRQ_NUM,
+};
+
+enum {
+ MTKAIF_PROTOCOL_1 = 0,
+ MTKAIF_PROTOCOL_2,
+ MTKAIF_PROTOCOL_2_CLK_P2,
+};
+
+enum {
+ MTK_AFE_ADDA_DL_GAIN_MUTE = 0,
+ MTK_AFE_ADDA_DL_GAIN_NORMAL = 0xf74f,
+ /* SA suggest apply -0.3db to audio/speech path */
+};
+
+/* MCLK */
+enum {
+ MT8192_I2S0_MCK = 0,
+ MT8192_I2S1_MCK,
+ MT8192_I2S2_MCK,
+ MT8192_I2S3_MCK,
+ MT8192_I2S4_MCK,
+ MT8192_I2S4_BCK,
+ MT8192_I2S5_MCK,
+ MT8192_I2S6_MCK,
+ MT8192_I2S7_MCK,
+ MT8192_I2S8_MCK,
+ MT8192_I2S9_MCK,
+ MT8192_MCK_NUM,
+};
+
+struct clk;
+
+struct mt8192_afe_private {
+ struct clk **clk;
+ struct regmap *topckgen;
+ struct regmap *apmixedsys;
+ struct regmap *infracfg;
+ int stf_positive_gain_db;
+ int pm_runtime_bypass_reg_ctl;
+
+ /* dai */
+ bool dai_on[MT8192_DAI_NUM];
+ void *dai_priv[MT8192_DAI_NUM];
+
+ /* adda */
+ int mtkaif_protocol;
+ int mtkaif_chosen_phase[4];
+ int mtkaif_phase_cycle[4];
+ int mtkaif_calibration_num_phase;
+ int mtkaif_dmic;
+ int mtkaif_dmic_ch34;
+ int mtkaif_adda6_only;
+
+ /* mck */
+ int mck_rate[MT8192_MCK_NUM];
+};
+
+int mt8192_dai_adda_register(struct mtk_base_afe *afe);
+int mt8192_dai_i2s_register(struct mtk_base_afe *afe);
+int mt8192_dai_hw_gain_register(struct mtk_base_afe *afe);
+int mt8192_dai_src_register(struct mtk_base_afe *afe);
+int mt8192_dai_pcm_register(struct mtk_base_afe *afe);
+int mt8192_dai_tdm_register(struct mtk_base_afe *afe);
+
+int mt8192_dai_i2s_set_share(struct mtk_base_afe *afe, const char *main_i2s_name,
+ const char *secondary_i2s_name);
+
+unsigned int mt8192_general_rate_transform(struct device *dev,
+ unsigned int rate);
+unsigned int mt8192_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk);
+
+int mt8192_dai_set_priv(struct mtk_base_afe *afe, int id,
+ int priv_size, const void *priv_data);
+
+#endif
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-control.c b/sound/soc/mediatek/mt8192/mt8192-afe-control.c
new file mode 100644
index 000000000000..d01b62e10088
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-control.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio Control
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include "mt8192-afe-common.h"
+
+enum {
+ MTK_AFE_RATE_8K = 0,
+ MTK_AFE_RATE_11K = 1,
+ MTK_AFE_RATE_12K = 2,
+ MTK_AFE_RATE_384K = 3,
+ MTK_AFE_RATE_16K = 4,
+ MTK_AFE_RATE_22K = 5,
+ MTK_AFE_RATE_24K = 6,
+ MTK_AFE_RATE_352K = 7,
+ MTK_AFE_RATE_32K = 8,
+ MTK_AFE_RATE_44K = 9,
+ MTK_AFE_RATE_48K = 10,
+ MTK_AFE_RATE_88K = 11,
+ MTK_AFE_RATE_96K = 12,
+ MTK_AFE_RATE_176K = 13,
+ MTK_AFE_RATE_192K = 14,
+ MTK_AFE_RATE_260K = 15,
+};
+
+enum {
+ MTK_AFE_DAI_MEMIF_RATE_8K = 0,
+ MTK_AFE_DAI_MEMIF_RATE_16K = 1,
+ MTK_AFE_DAI_MEMIF_RATE_32K = 2,
+ MTK_AFE_DAI_MEMIF_RATE_48K = 3,
+};
+
+enum {
+ MTK_AFE_PCM_RATE_8K = 0,
+ MTK_AFE_PCM_RATE_16K = 1,
+ MTK_AFE_PCM_RATE_32K = 2,
+ MTK_AFE_PCM_RATE_48K = 3,
+};
+
+unsigned int mt8192_general_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_RATE_8K;
+ case 11025:
+ return MTK_AFE_RATE_11K;
+ case 12000:
+ return MTK_AFE_RATE_12K;
+ case 16000:
+ return MTK_AFE_RATE_16K;
+ case 22050:
+ return MTK_AFE_RATE_22K;
+ case 24000:
+ return MTK_AFE_RATE_24K;
+ case 32000:
+ return MTK_AFE_RATE_32K;
+ case 44100:
+ return MTK_AFE_RATE_44K;
+ case 48000:
+ return MTK_AFE_RATE_48K;
+ case 88200:
+ return MTK_AFE_RATE_88K;
+ case 96000:
+ return MTK_AFE_RATE_96K;
+ case 176400:
+ return MTK_AFE_RATE_176K;
+ case 192000:
+ return MTK_AFE_RATE_192K;
+ case 260000:
+ return MTK_AFE_RATE_260K;
+ case 352800:
+ return MTK_AFE_RATE_352K;
+ case 384000:
+ return MTK_AFE_RATE_384K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__,
+ rate, MTK_AFE_RATE_48K);
+ return MTK_AFE_RATE_48K;
+ }
+}
+
+static unsigned int dai_memif_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_DAI_MEMIF_RATE_8K;
+ case 16000:
+ return MTK_AFE_DAI_MEMIF_RATE_16K;
+ case 32000:
+ return MTK_AFE_DAI_MEMIF_RATE_32K;
+ case 48000:
+ return MTK_AFE_DAI_MEMIF_RATE_48K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__,
+ rate, MTK_AFE_DAI_MEMIF_RATE_16K);
+ return MTK_AFE_DAI_MEMIF_RATE_16K;
+ }
+}
+
+static unsigned int pcm_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_PCM_RATE_8K;
+ case 16000:
+ return MTK_AFE_PCM_RATE_16K;
+ case 32000:
+ return MTK_AFE_PCM_RATE_32K;
+ case 48000:
+ return MTK_AFE_PCM_RATE_48K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n",
+ __func__,
+ rate, MTK_AFE_PCM_RATE_32K);
+ return MTK_AFE_PCM_RATE_32K;
+ }
+}
+
+unsigned int mt8192_rate_transform(struct device *dev,
+ unsigned int rate, int aud_blk)
+{
+ switch (aud_blk) {
+ case MT8192_MEMIF_DAI:
+ case MT8192_MEMIF_MOD_DAI:
+ return dai_memif_rate_transform(dev, rate);
+ case MT8192_DAI_PCM_1:
+ case MT8192_DAI_PCM_2:
+ return pcm_rate_transform(dev, rate);
+ default:
+ return mt8192_general_rate_transform(dev, rate);
+ }
+}
+
+int mt8192_dai_set_priv(struct mtk_base_afe *afe, int id,
+ int priv_size, const void *priv_data)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ void *temp_data;
+
+ temp_data = devm_kzalloc(afe->dev,
+ priv_size,
+ GFP_KERNEL);
+ if (!temp_data)
+ return -ENOMEM;
+
+ if (priv_data)
+ memcpy(temp_data, priv_data, priv_size);
+
+ afe_priv->dai_priv[id] = temp_data;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c b/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c
new file mode 100644
index 000000000000..de5e1deaa167
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8192-afe-gpio.c -- Mediatek 8192 afe gpio ctrl
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include <linux/pinctrl/consumer.h>
+
+#include "mt8192-afe-common.h"
+#include "mt8192-afe-gpio.h"
+
+static struct pinctrl *aud_pinctrl;
+
+enum mt8192_afe_gpio {
+ MT8192_AFE_GPIO_DAT_MISO_OFF,
+ MT8192_AFE_GPIO_DAT_MISO_ON,
+ MT8192_AFE_GPIO_DAT_MOSI_OFF,
+ MT8192_AFE_GPIO_DAT_MOSI_ON,
+ MT8192_AFE_GPIO_DAT_MISO_CH34_OFF,
+ MT8192_AFE_GPIO_DAT_MISO_CH34_ON,
+ MT8192_AFE_GPIO_DAT_MOSI_CH34_OFF,
+ MT8192_AFE_GPIO_DAT_MOSI_CH34_ON,
+ MT8192_AFE_GPIO_I2S0_OFF,
+ MT8192_AFE_GPIO_I2S0_ON,
+ MT8192_AFE_GPIO_I2S1_OFF,
+ MT8192_AFE_GPIO_I2S1_ON,
+ MT8192_AFE_GPIO_I2S2_OFF,
+ MT8192_AFE_GPIO_I2S2_ON,
+ MT8192_AFE_GPIO_I2S3_OFF,
+ MT8192_AFE_GPIO_I2S3_ON,
+ MT8192_AFE_GPIO_I2S5_OFF,
+ MT8192_AFE_GPIO_I2S5_ON,
+ MT8192_AFE_GPIO_I2S6_OFF,
+ MT8192_AFE_GPIO_I2S6_ON,
+ MT8192_AFE_GPIO_I2S7_OFF,
+ MT8192_AFE_GPIO_I2S7_ON,
+ MT8192_AFE_GPIO_I2S8_OFF,
+ MT8192_AFE_GPIO_I2S8_ON,
+ MT8192_AFE_GPIO_I2S9_OFF,
+ MT8192_AFE_GPIO_I2S9_ON,
+ MT8192_AFE_GPIO_VOW_DAT_OFF,
+ MT8192_AFE_GPIO_VOW_DAT_ON,
+ MT8192_AFE_GPIO_VOW_CLK_OFF,
+ MT8192_AFE_GPIO_VOW_CLK_ON,
+ MT8192_AFE_GPIO_CLK_MOSI_OFF,
+ MT8192_AFE_GPIO_CLK_MOSI_ON,
+ MT8192_AFE_GPIO_TDM_OFF,
+ MT8192_AFE_GPIO_TDM_ON,
+ MT8192_AFE_GPIO_GPIO_NUM
+};
+
+struct audio_gpio_attr {
+ const char *name;
+ bool gpio_prepare;
+ struct pinctrl_state *gpioctrl;
+};
+
+static struct audio_gpio_attr aud_gpios[MT8192_AFE_GPIO_GPIO_NUM] = {
+ [MT8192_AFE_GPIO_DAT_MISO_OFF] = {"aud_dat_miso_off", false, NULL},
+ [MT8192_AFE_GPIO_DAT_MISO_ON] = {"aud_dat_miso_on", false, NULL},
+ [MT8192_AFE_GPIO_DAT_MOSI_OFF] = {"aud_dat_mosi_off", false, NULL},
+ [MT8192_AFE_GPIO_DAT_MOSI_ON] = {"aud_dat_mosi_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S0_OFF] = {"aud_gpio_i2s0_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S0_ON] = {"aud_gpio_i2s0_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S1_OFF] = {"aud_gpio_i2s1_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S1_ON] = {"aud_gpio_i2s1_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S2_OFF] = {"aud_gpio_i2s2_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S2_ON] = {"aud_gpio_i2s2_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S3_OFF] = {"aud_gpio_i2s3_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S3_ON] = {"aud_gpio_i2s3_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S5_OFF] = {"aud_gpio_i2s5_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S5_ON] = {"aud_gpio_i2s5_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S6_OFF] = {"aud_gpio_i2s6_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S6_ON] = {"aud_gpio_i2s6_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S7_OFF] = {"aud_gpio_i2s7_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S7_ON] = {"aud_gpio_i2s7_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S8_OFF] = {"aud_gpio_i2s8_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S8_ON] = {"aud_gpio_i2s8_on", false, NULL},
+ [MT8192_AFE_GPIO_I2S9_OFF] = {"aud_gpio_i2s9_off", false, NULL},
+ [MT8192_AFE_GPIO_I2S9_ON] = {"aud_gpio_i2s9_on", false, NULL},
+ [MT8192_AFE_GPIO_TDM_OFF] = {"aud_gpio_tdm_off", false, NULL},
+ [MT8192_AFE_GPIO_TDM_ON] = {"aud_gpio_tdm_on", false, NULL},
+ [MT8192_AFE_GPIO_VOW_DAT_OFF] = {"vow_dat_miso_off", false, NULL},
+ [MT8192_AFE_GPIO_VOW_DAT_ON] = {"vow_dat_miso_on", false, NULL},
+ [MT8192_AFE_GPIO_VOW_CLK_OFF] = {"vow_clk_miso_off", false, NULL},
+ [MT8192_AFE_GPIO_VOW_CLK_ON] = {"vow_clk_miso_on", false, NULL},
+ [MT8192_AFE_GPIO_DAT_MISO_CH34_OFF] = {"aud_dat_miso_ch34_off",
+ false, NULL},
+ [MT8192_AFE_GPIO_DAT_MISO_CH34_ON] = {"aud_dat_miso_ch34_on",
+ false, NULL},
+ [MT8192_AFE_GPIO_DAT_MOSI_CH34_OFF] = {"aud_dat_mosi_ch34_off",
+ false, NULL},
+ [MT8192_AFE_GPIO_DAT_MOSI_CH34_ON] = {"aud_dat_mosi_ch34_on",
+ false, NULL},
+ [MT8192_AFE_GPIO_CLK_MOSI_OFF] = {"aud_clk_mosi_off", false, NULL},
+ [MT8192_AFE_GPIO_CLK_MOSI_ON] = {"aud_clk_mosi_on", false, NULL},
+};
+
+static DEFINE_MUTEX(gpio_request_mutex);
+
+static int mt8192_afe_gpio_select(struct device *dev,
+ enum mt8192_afe_gpio type)
+{
+ int ret;
+
+ if (type < 0 || type >= MT8192_AFE_GPIO_GPIO_NUM) {
+ dev_err(dev, "%s(), error, invalid gpio type %d\n",
+ __func__, type);
+ return -EINVAL;
+ }
+
+ if (!aud_gpios[type].gpio_prepare) {
+ dev_warn(dev, "%s(), error, gpio type %d not prepared\n",
+ __func__, type);
+ return -EIO;
+ }
+
+ ret = pinctrl_select_state(aud_pinctrl,
+ aud_gpios[type].gpioctrl);
+ if (ret) {
+ dev_dbg(dev, "%s(), error, can not set gpio type %d\n",
+ __func__, type);
+ }
+
+ return ret;
+}
+
+int mt8192_afe_gpio_init(struct device *dev)
+{
+ int i, ret;
+
+ aud_pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(aud_pinctrl)) {
+ ret = PTR_ERR(aud_pinctrl);
+ dev_err(dev, "%s(), ret %d, cannot get aud_pinctrl!\n",
+ __func__, ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aud_gpios); i++) {
+ aud_gpios[i].gpioctrl = pinctrl_lookup_state(aud_pinctrl,
+ aud_gpios[i].name);
+ if (IS_ERR(aud_gpios[i].gpioctrl)) {
+ ret = PTR_ERR(aud_gpios[i].gpioctrl);
+ dev_dbg(dev, "%s(), pinctrl_lookup_state %s fail, ret %d\n",
+ __func__, aud_gpios[i].name, ret);
+ } else {
+ aud_gpios[i].gpio_prepare = true;
+ }
+ }
+
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_CLK_MOSI_ON);
+
+ /* gpio status init */
+ mt8192_afe_gpio_request(dev, false, MT8192_DAI_ADDA, 0);
+ mt8192_afe_gpio_request(dev, false, MT8192_DAI_ADDA, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(mt8192_afe_gpio_init);
+
+static int mt8192_afe_gpio_adda_dl(struct device *dev, bool enable)
+{
+ if (enable) {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MOSI_ON);
+ } else {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MOSI_OFF);
+ }
+}
+
+static int mt8192_afe_gpio_adda_ul(struct device *dev, bool enable)
+{
+ if (enable) {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MISO_ON);
+ } else {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MISO_OFF);
+ }
+}
+
+static int mt8192_afe_gpio_adda_ch34_dl(struct device *dev, bool enable)
+{
+ if (enable) {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MOSI_CH34_ON);
+ } else {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MOSI_CH34_OFF);
+ }
+}
+
+static int mt8192_afe_gpio_adda_ch34_ul(struct device *dev, bool enable)
+{
+ if (enable) {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MISO_CH34_ON);
+ } else {
+ return mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_DAT_MISO_CH34_OFF);
+ }
+}
+
+int mt8192_afe_gpio_request(struct device *dev, bool enable,
+ int dai, int uplink)
+{
+ mutex_lock(&gpio_request_mutex);
+ switch (dai) {
+ case MT8192_DAI_ADDA:
+ if (uplink)
+ mt8192_afe_gpio_adda_ul(dev, enable);
+ else
+ mt8192_afe_gpio_adda_dl(dev, enable);
+ break;
+ case MT8192_DAI_ADDA_CH34:
+ if (uplink)
+ mt8192_afe_gpio_adda_ch34_ul(dev, enable);
+ else
+ mt8192_afe_gpio_adda_ch34_dl(dev, enable);
+ break;
+ case MT8192_DAI_I2S_0:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S0_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S0_OFF);
+ break;
+ case MT8192_DAI_I2S_1:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S1_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S1_OFF);
+ break;
+ case MT8192_DAI_I2S_2:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S2_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S2_OFF);
+ break;
+ case MT8192_DAI_I2S_3:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S3_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S3_OFF);
+ break;
+ case MT8192_DAI_I2S_5:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S5_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S5_OFF);
+ break;
+ case MT8192_DAI_I2S_6:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S6_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S6_OFF);
+ break;
+ case MT8192_DAI_I2S_7:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S7_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S7_OFF);
+ break;
+ case MT8192_DAI_I2S_8:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S8_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S8_OFF);
+ break;
+ case MT8192_DAI_I2S_9:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S9_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S9_OFF);
+ break;
+ case MT8192_DAI_TDM:
+ if (enable)
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_TDM_ON);
+ else
+ mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_TDM_OFF);
+ break;
+ case MT8192_DAI_VOW:
+ if (enable) {
+ mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_CLK_ON);
+ mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_DAT_ON);
+ } else {
+ mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_CLK_OFF);
+ mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_DAT_OFF);
+ }
+ break;
+ default:
+ mutex_unlock(&gpio_request_mutex);
+ dev_warn(dev, "%s(), invalid dai %d\n", __func__, dai);
+ return -EINVAL;
+ }
+ mutex_unlock(&gpio_request_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mt8192_afe_gpio_request);
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-gpio.h b/sound/soc/mediatek/mt8192/mt8192-afe-gpio.h
new file mode 100644
index 000000000000..5d29469da1c1
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-gpio.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8192-afe-gpio.h -- Mediatek 8192 afe gpio ctrl definition
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Shane Chien <shane.chien@mediatek.com>
+ */
+
+#ifndef _MT8192_AFE_GPIO_H_
+#define _MT8192_AFE_GPIO_H_
+
+struct device;
+
+int mt8192_afe_gpio_init(struct device *dev);
+
+int mt8192_afe_gpio_request(struct device *dev, bool enable,
+ int dai, int uplink);
+
+#endif
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
new file mode 100644
index 000000000000..3d32fe46118e
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
@@ -0,0 +1,2341 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Mediatek ALSA SoC AFE platform driver for 8192
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+
+#include "../common/mtk-afe-fe-dai.h"
+#include "../common/mtk-afe-platform-driver.h"
+
+#include "mt8192-afe-common.h"
+#include "mt8192-afe-clk.h"
+#include "mt8192-afe-gpio.h"
+#include "mt8192-interconnection.h"
+
+static const struct snd_pcm_hardware mt8192_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .period_bytes_min = 96,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 4 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+static int mt8192_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+
+ return mt8192_rate_transform(afe->dev, rate, id);
+}
+
+static int mt8192_get_dai_fs(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ return mt8192_rate_transform(afe->dev, rate, dai_id);
+}
+
+static int mt8192_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return mt8192_general_rate_transform(afe->dev, rate);
+}
+
+static int mt8192_get_memif_pbuf_size(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if ((runtime->period_size * 1000) / runtime->rate > 10)
+ return MT8192_MEMIF_PBUF_SIZE_256_BYTES;
+ else
+ return MT8192_MEMIF_PBUF_SIZE_32_BYTES;
+}
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt8192_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = MT8192_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL12",
+ .id = MT8192_MEMIF_DL12,
+ .playback = {
+ .stream_name = "DL12",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL2",
+ .id = MT8192_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT8192_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL4",
+ .id = MT8192_MEMIF_DL4,
+ .playback = {
+ .stream_name = "DL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL5",
+ .id = MT8192_MEMIF_DL5,
+ .playback = {
+ .stream_name = "DL5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL6",
+ .id = MT8192_MEMIF_DL6,
+ .playback = {
+ .stream_name = "DL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL7",
+ .id = MT8192_MEMIF_DL7,
+ .playback = {
+ .stream_name = "DL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL8",
+ .id = MT8192_MEMIF_DL8,
+ .playback = {
+ .stream_name = "DL8",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "DL9",
+ .id = MT8192_MEMIF_DL9,
+ .playback = {
+ .stream_name = "DL9",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT8192_MEMIF_VUL12,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT8192_MEMIF_AWB,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT8192_MEMIF_VUL2,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL4",
+ .id = MT8192_MEMIF_AWB2,
+ .capture = {
+ .stream_name = "UL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL5",
+ .id = MT8192_MEMIF_VUL3,
+ .capture = {
+ .stream_name = "UL5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL6",
+ .id = MT8192_MEMIF_VUL4,
+ .capture = {
+ .stream_name = "UL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL7",
+ .id = MT8192_MEMIF_VUL5,
+ .capture = {
+ .stream_name = "UL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL8",
+ .id = MT8192_MEMIF_VUL6,
+ .capture = {
+ .stream_name = "UL8",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL_MONO_1",
+ .id = MT8192_MEMIF_MOD_DAI,
+ .capture = {
+ .stream_name = "UL_MONO_1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_DAI_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL_MONO_2",
+ .id = MT8192_MEMIF_DAI,
+ .capture = {
+ .stream_name = "UL_MONO_2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_DAI_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL_MONO_3",
+ .id = MT8192_MEMIF_DAI2,
+ .capture = {
+ .stream_name = "UL_MONO_3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_DAI_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "HDMI",
+ .id = MT8192_MEMIF_HDMI,
+ .playback = {
+ .stream_name = "HDMI",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+};
+
+static int ul_tinyconn_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int reg_shift;
+ unsigned int reg_mask_shift;
+
+ dev_dbg(afe->dev, "%s(), event 0x%x\n", __func__, event);
+
+ if (strstr(w->name, "UL1")) {
+ reg_shift = VUL1_USE_TINY_SFT;
+ reg_mask_shift = VUL1_USE_TINY_MASK_SFT;
+ } else if (strstr(w->name, "UL2")) {
+ reg_shift = VUL2_USE_TINY_SFT;
+ reg_mask_shift = VUL2_USE_TINY_MASK_SFT;
+ } else if (strstr(w->name, "UL3")) {
+ reg_shift = VUL12_USE_TINY_SFT;
+ reg_mask_shift = VUL12_USE_TINY_MASK_SFT;
+ } else if (strstr(w->name, "UL4")) {
+ reg_shift = AWB2_USE_TINY_SFT;
+ reg_mask_shift = AWB2_USE_TINY_MASK_SFT;
+ } else {
+ reg_shift = AWB2_USE_TINY_SFT;
+ reg_mask_shift = AWB2_USE_TINY_MASK_SFT;
+ dev_warn(afe->dev, "%s(), err widget name %s, default use UL4",
+ __func__, w->name);
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(afe->regmap, AFE_MEMIF_CONN, reg_mask_shift,
+ 0x1 << reg_shift);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(afe->regmap, AFE_MEMIF_CONN, reg_mask_shift,
+ 0x0 << reg_shift);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* dma widget & routes*/
+static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN21,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN21,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN21,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN22,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN22,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN22,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN22,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN9,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN9,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN9,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN10,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN10,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN10,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN10,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1", AFE_CONN5,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN5,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN5,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN5,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN5,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN5_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN5_1,
+ I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN5_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN5,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN5,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1", AFE_CONN5,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S6_CH1", AFE_CONN5_1,
+ I_I2S6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S8_CH1", AFE_CONN5_1,
+ I_I2S8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1", AFE_CONN5_1,
+ I_CONNSYS_I2S_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH1", AFE_CONN5_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2", AFE_CONN6,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN6,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN6,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN6,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN6,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN6_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN6_1,
+ I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN6_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN6,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN6,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2", AFE_CONN6,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S6_CH2", AFE_CONN6_1,
+ I_I2S6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S8_CH2", AFE_CONN6_1,
+ I_I2S8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2", AFE_CONN6_1,
+ I_CONNSYS_I2S_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH2", AFE_CONN6_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1", AFE_CONN32_1,
+ I_CONNSYS_I2S_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN32,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN32,
+ I_DL2_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2", AFE_CONN33_1,
+ I_CONNSYS_I2S_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN38,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1", AFE_CONN38,
+ I_I2S0_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN39,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2", AFE_CONN39,
+ I_I2S0_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN44,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN45,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN46,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN46,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN46,
+ I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN46_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN46,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN46,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN46_1,
+ I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN46,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN46,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN47,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN47,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN47,
+ I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN47_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN47,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN47,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN47_1,
+ I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN47,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN47,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN48,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN49,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN50,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN51,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_mono_1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN12,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN12,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_mono_2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN11,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_mono_3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN35,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+/* TINYCONN MUX */
+enum {
+ TINYCONN_CH1_MUX_I2S0 = 0x14,
+ TINYCONN_CH2_MUX_I2S0 = 0x15,
+ TINYCONN_CH1_MUX_I2S6 = 0x1a,
+ TINYCONN_CH2_MUX_I2S6 = 0x1b,
+ TINYCONN_CH1_MUX_I2S8 = 0x1c,
+ TINYCONN_CH2_MUX_I2S8 = 0x1d,
+ TINYCONN_MUX_NONE = 0x1f,
+};
+
+static const char * const tinyconn_mux_map[] = {
+ "NONE",
+ "I2S0_CH1",
+ "I2S0_CH2",
+ "I2S6_CH1",
+ "I2S6_CH2",
+ "I2S8_CH1",
+ "I2S8_CH2",
+};
+
+static int tinyconn_mux_map_value[] = {
+ TINYCONN_MUX_NONE,
+ TINYCONN_CH1_MUX_I2S0,
+ TINYCONN_CH2_MUX_I2S0,
+ TINYCONN_CH1_MUX_I2S6,
+ TINYCONN_CH2_MUX_I2S6,
+ TINYCONN_CH1_MUX_I2S8,
+ TINYCONN_CH2_MUX_I2S8,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_tinyconn_ch1_mux_map_enum,
+ AFE_TINY_CONN0,
+ O_2_CFG_SFT,
+ O_2_CFG_MASK,
+ tinyconn_mux_map,
+ tinyconn_mux_map_value);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_tinyconn_ch2_mux_map_enum,
+ AFE_TINY_CONN0,
+ O_3_CFG_SFT,
+ O_3_CFG_MASK,
+ tinyconn_mux_map,
+ tinyconn_mux_map_value);
+
+static const struct snd_kcontrol_new ul4_tinyconn_ch1_mux_control =
+ SOC_DAPM_ENUM("UL4_TINYCONN_CH1_MUX", ul4_tinyconn_ch1_mux_map_enum);
+static const struct snd_kcontrol_new ul4_tinyconn_ch2_mux_control =
+ SOC_DAPM_ENUM("UL4_TINYCONN_CH2_MUX", ul4_tinyconn_ch2_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mt8192_memif_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch3_mix, ARRAY_SIZE(memif_ul1_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch4_mix, ARRAY_SIZE(memif_ul1_ch4_mix)),
+
+ SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL4_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch1_mix, ARRAY_SIZE(memif_ul4_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL4_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch2_mix, ARRAY_SIZE(memif_ul4_ch2_mix)),
+ SND_SOC_DAPM_MUX_E("UL4_TINYCONN_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &ul4_tinyconn_ch1_mux_control,
+ ul_tinyconn_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("UL4_TINYCONN_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &ul4_tinyconn_ch2_mux_control,
+ ul_tinyconn_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("UL5_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch1_mix, ARRAY_SIZE(memif_ul5_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL5_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch2_mix, ARRAY_SIZE(memif_ul5_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL6_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch1_mix, ARRAY_SIZE(memif_ul6_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL6_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch2_mix, ARRAY_SIZE(memif_ul6_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL7_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch1_mix, ARRAY_SIZE(memif_ul7_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL7_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch2_mix, ARRAY_SIZE(memif_ul7_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL8_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch1_mix, ARRAY_SIZE(memif_ul8_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL8_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch2_mix, ARRAY_SIZE(memif_ul8_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_MONO_1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_mono_1_mix,
+ ARRAY_SIZE(memif_ul_mono_1_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_MONO_2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_mono_2_mix,
+ ARRAY_SIZE(memif_ul_mono_2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_MONO_3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_mono_3_mix,
+ ARRAY_SIZE(memif_ul_mono_3_mix)),
+
+ SND_SOC_DAPM_INPUT("UL1_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL2_VIRTUAL_INPUT"),
+ SND_SOC_DAPM_INPUT("UL6_VIRTUAL_INPUT"),
+};
+
+static const struct snd_soc_dapm_route mt8192_memif_routes[] = {
+ {"UL1", NULL, "UL1_CH1"},
+ {"UL1", NULL, "UL1_CH2"},
+ {"UL1", NULL, "UL1_CH3"},
+ {"UL1", NULL, "UL1_CH4"},
+ {"UL1_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL1_CH1", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL1_CH1", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL1_CH2", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+ {"UL1_CH3", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL1_CH3", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL1_CH3", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL1_CH4", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL1_CH4", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL1_CH4", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL1_CH4", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+
+ {"UL2", NULL, "UL2_CH1"},
+ {"UL2", NULL, "UL2_CH2"},
+ {"UL2_CH1", "I2S0_CH1", "I2S0"},
+ {"UL2_CH2", "I2S0_CH2", "I2S0"},
+ {"UL2_CH1", "I2S2_CH1", "I2S2"},
+ {"UL2_CH2", "I2S2_CH2", "I2S2"},
+ {"UL2_CH1", "I2S6_CH1", "I2S6"},
+ {"UL2_CH2", "I2S6_CH2", "I2S6"},
+ {"UL2_CH1", "I2S8_CH1", "I2S8"},
+ {"UL2_CH2", "I2S8_CH2", "I2S8"},
+
+ {"UL2_CH1", "PCM_1_CAP_CH1", "PCM 1 Capture"},
+ {"UL2_CH2", "PCM_1_CAP_CH1", "PCM 1 Capture"},
+ {"UL2_CH1", "PCM_2_CAP_CH1", "PCM 2 Capture"},
+ {"UL2_CH2", "PCM_2_CAP_CH1", "PCM 2 Capture"},
+
+ {"UL_MONO_1", NULL, "UL_MONO_1_CH1"},
+ {"UL_MONO_1_CH1", "PCM_1_CAP_CH1", "PCM 1 Capture"},
+ {"UL_MONO_1_CH1", "PCM_2_CAP_CH1", "PCM 2 Capture"},
+
+ {"UL_MONO_2", NULL, "UL_MONO_2_CH1"},
+ {"UL_MONO_2_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+
+ {"UL_MONO_3", NULL, "UL_MONO_3_CH1"},
+ {"UL_MONO_3_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+
+ {"UL2_CH1", "CONNSYS_I2S_CH1", "Connsys I2S"},
+ {"UL2_CH2", "CONNSYS_I2S_CH2", "Connsys I2S"},
+
+ {"UL3", NULL, "UL3_CH1"},
+ {"UL3", NULL, "UL3_CH2"},
+ {"UL3_CH1", "CONNSYS_I2S_CH1", "Connsys I2S"},
+ {"UL3_CH2", "CONNSYS_I2S_CH2", "Connsys I2S"},
+
+ {"UL4", NULL, "UL4_CH1"},
+ {"UL4", NULL, "UL4_CH2"},
+ {"UL4", NULL, "UL4_TINYCONN_CH1_MUX"},
+ {"UL4", NULL, "UL4_TINYCONN_CH2_MUX"},
+ {"UL4_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL4_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL4_CH1", "I2S0_CH1", "I2S0"},
+ {"UL4_CH2", "I2S0_CH2", "I2S0"},
+ {"UL4_TINYCONN_CH1_MUX", "I2S0_CH1", "I2S0"},
+ {"UL4_TINYCONN_CH2_MUX", "I2S0_CH2", "I2S0"},
+
+ {"UL5", NULL, "UL5_CH1"},
+ {"UL5", NULL, "UL5_CH2"},
+ {"UL5_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL5_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL6", NULL, "UL6_CH1"},
+ {"UL6", NULL, "UL6_CH2"},
+
+ {"UL6_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL6_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL6_CH1", "PCM_1_CAP_CH1", "PCM 1 Capture"},
+ {"UL6_CH2", "PCM_1_CAP_CH1", "PCM 1 Capture"},
+ {"UL6_CH1", "PCM_2_CAP_CH1", "PCM 2 Capture"},
+ {"UL6_CH2", "PCM_2_CAP_CH1", "PCM 2 Capture"},
+
+ {"UL7", NULL, "UL7_CH1"},
+ {"UL7", NULL, "UL7_CH2"},
+ {"UL7_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL7_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL8", NULL, "UL8_CH1"},
+ {"UL8", NULL, "UL8_CH2"},
+ {"UL8_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL8_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+};
+
+static const struct mtk_base_memif_data memif_data[MT8192_MEMIF_NUM] = {
+ [MT8192_MEMIF_DL1] = {
+ .name = "DL1",
+ .id = MT8192_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .reg_ofs_end = AFE_DL1_END,
+ .reg_ofs_base_msb = AFE_DL1_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL1_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL1_END_MSB,
+ .fs_reg = AFE_DL1_CON0,
+ .fs_shift = DL1_MODE_SFT,
+ .fs_maskbit = DL1_MODE_MASK,
+ .mono_reg = AFE_DL1_CON0,
+ .mono_shift = DL1_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL1_ON_SFT,
+ .hd_reg = AFE_DL1_CON0,
+ .hd_shift = DL1_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL1_CON0,
+ .hd_align_mshift = DL1_HALIGN_SFT,
+ .pbuf_reg = AFE_DL1_CON0,
+ .pbuf_shift = DL1_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL1_CON0,
+ .minlen_shift = DL1_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL12] = {
+ .name = "DL12",
+ .id = MT8192_MEMIF_DL12,
+ .reg_ofs_base = AFE_DL12_BASE,
+ .reg_ofs_cur = AFE_DL12_CUR,
+ .reg_ofs_end = AFE_DL12_END,
+ .reg_ofs_base_msb = AFE_DL12_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL12_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL12_END_MSB,
+ .fs_reg = AFE_DL12_CON0,
+ .fs_shift = DL12_MODE_SFT,
+ .fs_maskbit = DL12_MODE_MASK,
+ .mono_reg = AFE_DL12_CON0,
+ .mono_shift = DL12_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL12_ON_SFT,
+ .hd_reg = AFE_DL12_CON0,
+ .hd_shift = DL12_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL12_CON0,
+ .hd_align_mshift = DL12_HALIGN_SFT,
+ .pbuf_reg = AFE_DL12_CON0,
+ .pbuf_shift = DL12_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL12_CON0,
+ .minlen_shift = DL12_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL2] = {
+ .name = "DL2",
+ .id = MT8192_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .reg_ofs_end = AFE_DL2_END,
+ .reg_ofs_base_msb = AFE_DL2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL2_END_MSB,
+ .fs_reg = AFE_DL2_CON0,
+ .fs_shift = DL2_MODE_SFT,
+ .fs_maskbit = DL2_MODE_MASK,
+ .mono_reg = AFE_DL2_CON0,
+ .mono_shift = DL2_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL2_ON_SFT,
+ .hd_reg = AFE_DL2_CON0,
+ .hd_shift = DL2_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL2_CON0,
+ .hd_align_mshift = DL2_HALIGN_SFT,
+ .pbuf_reg = AFE_DL2_CON0,
+ .pbuf_shift = DL2_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL2_CON0,
+ .minlen_shift = DL2_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL3] = {
+ .name = "DL3",
+ .id = MT8192_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .reg_ofs_end = AFE_DL3_END,
+ .reg_ofs_base_msb = AFE_DL3_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL3_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL3_END_MSB,
+ .fs_reg = AFE_DL3_CON0,
+ .fs_shift = DL3_MODE_SFT,
+ .fs_maskbit = DL3_MODE_MASK,
+ .mono_reg = AFE_DL3_CON0,
+ .mono_shift = DL3_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL3_ON_SFT,
+ .hd_reg = AFE_DL3_CON0,
+ .hd_shift = DL3_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL3_CON0,
+ .hd_align_mshift = DL3_HALIGN_SFT,
+ .pbuf_reg = AFE_DL3_CON0,
+ .pbuf_shift = DL3_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL3_CON0,
+ .minlen_shift = DL3_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL4] = {
+ .name = "DL4",
+ .id = MT8192_MEMIF_DL4,
+ .reg_ofs_base = AFE_DL4_BASE,
+ .reg_ofs_cur = AFE_DL4_CUR,
+ .reg_ofs_end = AFE_DL4_END,
+ .reg_ofs_base_msb = AFE_DL4_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL4_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL4_END_MSB,
+ .fs_reg = AFE_DL4_CON0,
+ .fs_shift = DL4_MODE_SFT,
+ .fs_maskbit = DL4_MODE_MASK,
+ .mono_reg = AFE_DL4_CON0,
+ .mono_shift = DL4_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL4_ON_SFT,
+ .hd_reg = AFE_DL4_CON0,
+ .hd_shift = DL4_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL4_CON0,
+ .hd_align_mshift = DL4_HALIGN_SFT,
+ .pbuf_reg = AFE_DL4_CON0,
+ .pbuf_shift = DL4_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL4_CON0,
+ .minlen_shift = DL4_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL5] = {
+ .name = "DL5",
+ .id = MT8192_MEMIF_DL5,
+ .reg_ofs_base = AFE_DL5_BASE,
+ .reg_ofs_cur = AFE_DL5_CUR,
+ .reg_ofs_end = AFE_DL5_END,
+ .reg_ofs_base_msb = AFE_DL5_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL5_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL5_END_MSB,
+ .fs_reg = AFE_DL5_CON0,
+ .fs_shift = DL5_MODE_SFT,
+ .fs_maskbit = DL5_MODE_MASK,
+ .mono_reg = AFE_DL5_CON0,
+ .mono_shift = DL5_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL5_ON_SFT,
+ .hd_reg = AFE_DL5_CON0,
+ .hd_shift = DL5_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL5_CON0,
+ .hd_align_mshift = DL5_HALIGN_SFT,
+ .pbuf_reg = AFE_DL5_CON0,
+ .pbuf_shift = DL5_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL5_CON0,
+ .minlen_shift = DL5_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL6] = {
+ .name = "DL6",
+ .id = MT8192_MEMIF_DL6,
+ .reg_ofs_base = AFE_DL6_BASE,
+ .reg_ofs_cur = AFE_DL6_CUR,
+ .reg_ofs_end = AFE_DL6_END,
+ .reg_ofs_base_msb = AFE_DL6_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL6_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL6_END_MSB,
+ .fs_reg = AFE_DL6_CON0,
+ .fs_shift = DL6_MODE_SFT,
+ .fs_maskbit = DL6_MODE_MASK,
+ .mono_reg = AFE_DL6_CON0,
+ .mono_shift = DL6_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL6_ON_SFT,
+ .hd_reg = AFE_DL6_CON0,
+ .hd_shift = DL6_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL6_CON0,
+ .hd_align_mshift = DL6_HALIGN_SFT,
+ .pbuf_reg = AFE_DL6_CON0,
+ .pbuf_shift = DL6_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL6_CON0,
+ .minlen_shift = DL6_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL7] = {
+ .name = "DL7",
+ .id = MT8192_MEMIF_DL7,
+ .reg_ofs_base = AFE_DL7_BASE,
+ .reg_ofs_cur = AFE_DL7_CUR,
+ .reg_ofs_end = AFE_DL7_END,
+ .reg_ofs_base_msb = AFE_DL7_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL7_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL7_END_MSB,
+ .fs_reg = AFE_DL7_CON0,
+ .fs_shift = DL7_MODE_SFT,
+ .fs_maskbit = DL7_MODE_MASK,
+ .mono_reg = AFE_DL7_CON0,
+ .mono_shift = DL7_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL7_ON_SFT,
+ .hd_reg = AFE_DL7_CON0,
+ .hd_shift = DL7_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL7_CON0,
+ .hd_align_mshift = DL7_HALIGN_SFT,
+ .pbuf_reg = AFE_DL7_CON0,
+ .pbuf_shift = DL7_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL7_CON0,
+ .minlen_shift = DL7_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL8] = {
+ .name = "DL8",
+ .id = MT8192_MEMIF_DL8,
+ .reg_ofs_base = AFE_DL8_BASE,
+ .reg_ofs_cur = AFE_DL8_CUR,
+ .reg_ofs_end = AFE_DL8_END,
+ .reg_ofs_base_msb = AFE_DL8_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL8_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL8_END_MSB,
+ .fs_reg = AFE_DL8_CON0,
+ .fs_shift = DL8_MODE_SFT,
+ .fs_maskbit = DL8_MODE_MASK,
+ .mono_reg = AFE_DL8_CON0,
+ .mono_shift = DL8_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL8_ON_SFT,
+ .hd_reg = AFE_DL8_CON0,
+ .hd_shift = DL8_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL8_CON0,
+ .hd_align_mshift = DL8_HALIGN_SFT,
+ .pbuf_reg = AFE_DL8_CON0,
+ .pbuf_shift = DL8_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL8_CON0,
+ .minlen_shift = DL8_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DL9] = {
+ .name = "DL9",
+ .id = MT8192_MEMIF_DL9,
+ .reg_ofs_base = AFE_DL9_BASE,
+ .reg_ofs_cur = AFE_DL9_CUR,
+ .reg_ofs_end = AFE_DL9_END,
+ .reg_ofs_base_msb = AFE_DL9_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DL9_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DL9_END_MSB,
+ .fs_reg = AFE_DL9_CON0,
+ .fs_shift = DL9_MODE_SFT,
+ .fs_maskbit = DL9_MODE_MASK,
+ .mono_reg = AFE_DL9_CON0,
+ .mono_shift = DL9_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DL9_ON_SFT,
+ .hd_reg = AFE_DL9_CON0,
+ .hd_shift = DL9_HD_MODE_SFT,
+ .hd_align_reg = AFE_DL9_CON0,
+ .hd_align_mshift = DL9_HALIGN_SFT,
+ .pbuf_reg = AFE_DL9_CON0,
+ .pbuf_shift = DL9_PBUF_SIZE_SFT,
+ .minlen_reg = AFE_DL9_CON0,
+ .minlen_shift = DL9_MINLEN_SFT,
+ },
+ [MT8192_MEMIF_DAI] = {
+ .name = "DAI",
+ .id = MT8192_MEMIF_DAI,
+ .reg_ofs_base = AFE_DAI_BASE,
+ .reg_ofs_cur = AFE_DAI_CUR,
+ .reg_ofs_end = AFE_DAI_END,
+ .reg_ofs_base_msb = AFE_DAI_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DAI_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DAI_END_MSB,
+ .fs_reg = AFE_DAI_CON0,
+ .fs_shift = DAI_MODE_SFT,
+ .fs_maskbit = DAI_MODE_MASK,
+ .mono_reg = AFE_DAI_CON0,
+ .mono_shift = DAI_DUPLICATE_WR_SFT,
+ .mono_invert = 1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DAI_ON_SFT,
+ .hd_reg = AFE_DAI_CON0,
+ .hd_shift = DAI_HD_MODE_SFT,
+ .hd_align_reg = AFE_DAI_CON0,
+ .hd_align_mshift = DAI_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_MOD_DAI] = {
+ .name = "MOD_DAI",
+ .id = MT8192_MEMIF_MOD_DAI,
+ .reg_ofs_base = AFE_MOD_DAI_BASE,
+ .reg_ofs_cur = AFE_MOD_DAI_CUR,
+ .reg_ofs_end = AFE_MOD_DAI_END,
+ .reg_ofs_base_msb = AFE_MOD_DAI_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_MOD_DAI_CUR_MSB,
+ .reg_ofs_end_msb = AFE_MOD_DAI_END_MSB,
+ .fs_reg = AFE_MOD_DAI_CON0,
+ .fs_shift = MOD_DAI_MODE_SFT,
+ .fs_maskbit = MOD_DAI_MODE_MASK,
+ .mono_reg = AFE_MOD_DAI_CON0,
+ .mono_shift = MOD_DAI_DUPLICATE_WR_SFT,
+ .mono_invert = 1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = MOD_DAI_ON_SFT,
+ .hd_reg = AFE_MOD_DAI_CON0,
+ .hd_shift = MOD_DAI_HD_MODE_SFT,
+ .hd_align_reg = AFE_MOD_DAI_CON0,
+ .hd_align_mshift = MOD_DAI_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_DAI2] = {
+ .name = "DAI2",
+ .id = MT8192_MEMIF_DAI2,
+ .reg_ofs_base = AFE_DAI2_BASE,
+ .reg_ofs_cur = AFE_DAI2_CUR,
+ .reg_ofs_end = AFE_DAI2_END,
+ .reg_ofs_base_msb = AFE_DAI2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_DAI2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_DAI2_END_MSB,
+ .fs_reg = AFE_DAI2_CON0,
+ .fs_shift = DAI2_MODE_SFT,
+ .fs_maskbit = DAI2_MODE_MASK,
+ .mono_reg = AFE_DAI2_CON0,
+ .mono_shift = DAI2_DUPLICATE_WR_SFT,
+ .mono_invert = 1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = DAI2_ON_SFT,
+ .hd_reg = AFE_DAI2_CON0,
+ .hd_shift = DAI2_HD_MODE_SFT,
+ .hd_align_reg = AFE_DAI2_CON0,
+ .hd_align_mshift = DAI2_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_VUL12] = {
+ .name = "VUL12",
+ .id = MT8192_MEMIF_VUL12,
+ .reg_ofs_base = AFE_VUL12_BASE,
+ .reg_ofs_cur = AFE_VUL12_CUR,
+ .reg_ofs_end = AFE_VUL12_END,
+ .reg_ofs_base_msb = AFE_VUL12_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL12_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL12_END_MSB,
+ .fs_reg = AFE_VUL12_CON0,
+ .fs_shift = VUL12_MODE_SFT,
+ .fs_maskbit = VUL12_MODE_MASK,
+ .mono_reg = AFE_VUL12_CON0,
+ .mono_shift = VUL12_MONO_SFT,
+ .quad_ch_reg = AFE_VUL12_CON0,
+ .quad_ch_shift = VUL12_4CH_EN_SFT,
+ .quad_ch_mask = VUL12_4CH_EN_MASK,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL12_ON_SFT,
+ .hd_reg = AFE_VUL12_CON0,
+ .hd_shift = VUL12_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL12_CON0,
+ .hd_align_mshift = VUL12_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_VUL2] = {
+ .name = "VUL2",
+ .id = MT8192_MEMIF_VUL2,
+ .reg_ofs_base = AFE_VUL2_BASE,
+ .reg_ofs_cur = AFE_VUL2_CUR,
+ .reg_ofs_end = AFE_VUL2_END,
+ .reg_ofs_base_msb = AFE_VUL2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL2_END_MSB,
+ .fs_reg = AFE_VUL2_CON0,
+ .fs_shift = VUL2_MODE_SFT,
+ .fs_maskbit = VUL2_MODE_MASK,
+ .mono_reg = AFE_VUL2_CON0,
+ .mono_shift = VUL2_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL2_ON_SFT,
+ .hd_reg = AFE_VUL2_CON0,
+ .hd_shift = VUL2_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL2_CON0,
+ .hd_align_mshift = VUL2_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_AWB] = {
+ .name = "AWB",
+ .id = MT8192_MEMIF_AWB,
+ .reg_ofs_base = AFE_AWB_BASE,
+ .reg_ofs_cur = AFE_AWB_CUR,
+ .reg_ofs_end = AFE_AWB_END,
+ .reg_ofs_base_msb = AFE_AWB_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_AWB_CUR_MSB,
+ .reg_ofs_end_msb = AFE_AWB_END_MSB,
+ .fs_reg = AFE_AWB_CON0,
+ .fs_shift = AWB_MODE_SFT,
+ .fs_maskbit = AWB_MODE_MASK,
+ .mono_reg = AFE_AWB_CON0,
+ .mono_shift = AWB_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AWB_ON_SFT,
+ .hd_reg = AFE_AWB_CON0,
+ .hd_shift = AWB_HD_MODE_SFT,
+ .hd_align_reg = AFE_AWB_CON0,
+ .hd_align_mshift = AWB_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_AWB2] = {
+ .name = "AWB2",
+ .id = MT8192_MEMIF_AWB2,
+ .reg_ofs_base = AFE_AWB2_BASE,
+ .reg_ofs_cur = AFE_AWB2_CUR,
+ .reg_ofs_end = AFE_AWB2_END,
+ .reg_ofs_base_msb = AFE_AWB2_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_AWB2_CUR_MSB,
+ .reg_ofs_end_msb = AFE_AWB2_END_MSB,
+ .fs_reg = AFE_AWB2_CON0,
+ .fs_shift = AWB2_MODE_SFT,
+ .fs_maskbit = AWB2_MODE_MASK,
+ .mono_reg = AFE_AWB2_CON0,
+ .mono_shift = AWB2_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AWB2_ON_SFT,
+ .hd_reg = AFE_AWB2_CON0,
+ .hd_shift = AWB2_HD_MODE_SFT,
+ .hd_align_reg = AFE_AWB2_CON0,
+ .hd_align_mshift = AWB2_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_VUL3] = {
+ .name = "VUL3",
+ .id = MT8192_MEMIF_VUL3,
+ .reg_ofs_base = AFE_VUL3_BASE,
+ .reg_ofs_cur = AFE_VUL3_CUR,
+ .reg_ofs_end = AFE_VUL3_END,
+ .reg_ofs_base_msb = AFE_VUL3_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL3_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL3_END_MSB,
+ .fs_reg = AFE_VUL3_CON0,
+ .fs_shift = VUL3_MODE_SFT,
+ .fs_maskbit = VUL3_MODE_MASK,
+ .mono_reg = AFE_VUL3_CON0,
+ .mono_shift = VUL3_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL3_ON_SFT,
+ .hd_reg = AFE_VUL3_CON0,
+ .hd_shift = VUL3_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL3_CON0,
+ .hd_align_mshift = VUL3_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_VUL4] = {
+ .name = "VUL4",
+ .id = MT8192_MEMIF_VUL4,
+ .reg_ofs_base = AFE_VUL4_BASE,
+ .reg_ofs_cur = AFE_VUL4_CUR,
+ .reg_ofs_end = AFE_VUL4_END,
+ .reg_ofs_base_msb = AFE_VUL4_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL4_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL4_END_MSB,
+ .fs_reg = AFE_VUL4_CON0,
+ .fs_shift = VUL4_MODE_SFT,
+ .fs_maskbit = VUL4_MODE_MASK,
+ .mono_reg = AFE_VUL4_CON0,
+ .mono_shift = VUL4_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL4_ON_SFT,
+ .hd_reg = AFE_VUL4_CON0,
+ .hd_shift = VUL4_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL4_CON0,
+ .hd_align_mshift = VUL4_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_VUL5] = {
+ .name = "VUL5",
+ .id = MT8192_MEMIF_VUL5,
+ .reg_ofs_base = AFE_VUL5_BASE,
+ .reg_ofs_cur = AFE_VUL5_CUR,
+ .reg_ofs_end = AFE_VUL5_END,
+ .reg_ofs_base_msb = AFE_VUL5_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL5_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL5_END_MSB,
+ .fs_reg = AFE_VUL5_CON0,
+ .fs_shift = VUL5_MODE_SFT,
+ .fs_maskbit = VUL5_MODE_MASK,
+ .mono_reg = AFE_VUL5_CON0,
+ .mono_shift = VUL5_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL5_ON_SFT,
+ .hd_reg = AFE_VUL5_CON0,
+ .hd_shift = VUL5_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL5_CON0,
+ .hd_align_mshift = VUL5_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_VUL6] = {
+ .name = "VUL6",
+ .id = MT8192_MEMIF_VUL6,
+ .reg_ofs_base = AFE_VUL6_BASE,
+ .reg_ofs_cur = AFE_VUL6_CUR,
+ .reg_ofs_end = AFE_VUL6_END,
+ .reg_ofs_base_msb = AFE_VUL6_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_VUL6_CUR_MSB,
+ .reg_ofs_end_msb = AFE_VUL6_END_MSB,
+ .fs_reg = AFE_VUL6_CON0,
+ .fs_shift = VUL6_MODE_SFT,
+ .fs_maskbit = VUL6_MODE_MASK,
+ .mono_reg = AFE_VUL6_CON0,
+ .mono_shift = VUL6_MONO_SFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = VUL6_ON_SFT,
+ .hd_reg = AFE_VUL6_CON0,
+ .hd_shift = VUL6_HD_MODE_SFT,
+ .hd_align_reg = AFE_VUL6_CON0,
+ .hd_align_mshift = VUL6_HALIGN_SFT,
+ },
+ [MT8192_MEMIF_HDMI] = {
+ .name = "HDMI",
+ .id = MT8192_MEMIF_HDMI,
+ .reg_ofs_base = AFE_HDMI_OUT_BASE,
+ .reg_ofs_cur = AFE_HDMI_OUT_CUR,
+ .reg_ofs_end = AFE_HDMI_OUT_END,
+ .reg_ofs_base_msb = AFE_HDMI_OUT_BASE_MSB,
+ .reg_ofs_cur_msb = AFE_HDMI_OUT_CUR_MSB,
+ .reg_ofs_end_msb = AFE_HDMI_OUT_END_MSB,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = HDMI_OUT_ON_SFT,
+ .hd_reg = AFE_HDMI_OUT_CON0,
+ .hd_shift = HDMI_OUT_HD_MODE_SFT,
+ .hd_align_reg = AFE_HDMI_OUT_CON0,
+ .hd_align_mshift = HDMI_OUT_HALIGN_SFT,
+ .pbuf_reg = AFE_HDMI_OUT_CON0,
+ .minlen_reg = AFE_HDMI_OUT_CON0,
+ .minlen_shift = HDMI_OUT_MINLEN_SFT,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8192_IRQ_NUM] = {
+ [MT8192_IRQ_0] = {
+ .id = MT8192_IRQ_0,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT0,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ0_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ0_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ0_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ0_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_1] = {
+ .id = MT8192_IRQ_1,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT1,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ1_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ1_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ1_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ1_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_2] = {
+ .id = MT8192_IRQ_2,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT2,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ2_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ2_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ2_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ2_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_3] = {
+ .id = MT8192_IRQ_3,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT3,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ3_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ3_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ3_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ3_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_4] = {
+ .id = MT8192_IRQ_4,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT4,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ4_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ4_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ4_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ4_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_5] = {
+ .id = MT8192_IRQ_5,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT5,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ5_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ5_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ5_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ5_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_6] = {
+ .id = MT8192_IRQ_6,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT6,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ6_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ6_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ6_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ6_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_7] = {
+ .id = MT8192_IRQ_7,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT7,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON1,
+ .irq_fs_shift = IRQ7_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ7_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ7_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ7_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_8] = {
+ .id = MT8192_IRQ_8,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT8,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ8_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ8_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ8_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ8_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_9] = {
+ .id = MT8192_IRQ_9,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT9,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ9_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ9_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ9_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ9_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_10] = {
+ .id = MT8192_IRQ_10,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT10,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ10_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ10_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ10_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ10_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_11] = {
+ .id = MT8192_IRQ_11,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT11,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ11_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ11_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ11_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ11_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_12] = {
+ .id = MT8192_IRQ_12,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT12,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ12_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ12_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ12_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ12_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_13] = {
+ .id = MT8192_IRQ_13,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT13,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ13_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ13_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ13_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ13_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_14] = {
+ .id = MT8192_IRQ_14,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT14,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ14_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ14_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ14_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ14_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_15] = {
+ .id = MT8192_IRQ_15,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT15,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON2,
+ .irq_fs_shift = IRQ15_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ15_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ15_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ15_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_16] = {
+ .id = MT8192_IRQ_16,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT16,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ16_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ16_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ16_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ16_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_17] = {
+ .id = MT8192_IRQ_17,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT17,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ17_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ17_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ17_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ17_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_18] = {
+ .id = MT8192_IRQ_18,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT18,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ18_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ18_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ18_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ18_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_19] = {
+ .id = MT8192_IRQ_19,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT19,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ19_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ19_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ19_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ19_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_20] = {
+ .id = MT8192_IRQ_20,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT20,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ20_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ20_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ20_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ20_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_21] = {
+ .id = MT8192_IRQ_21,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT21,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ21_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ21_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ21_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ21_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_22] = {
+ .id = MT8192_IRQ_22,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT22,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ22_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ22_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ22_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ22_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_23] = {
+ .id = MT8192_IRQ_23,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT23,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON3,
+ .irq_fs_shift = IRQ23_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ23_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ23_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ23_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_24] = {
+ .id = MT8192_IRQ_24,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT24,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON4,
+ .irq_fs_shift = IRQ24_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ24_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ24_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ24_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_25] = {
+ .id = MT8192_IRQ_25,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT25,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON4,
+ .irq_fs_shift = IRQ25_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ25_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ25_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ25_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_26] = {
+ .id = MT8192_IRQ_26,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT26,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = AFE_IRQ_MCU_CON4,
+ .irq_fs_shift = IRQ26_MCU_MODE_SFT,
+ .irq_fs_maskbit = IRQ26_MCU_MODE_MASK,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ26_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ26_MCU_CLR_SFT,
+ },
+ [MT8192_IRQ_31] = {
+ .id = MT8192_IRQ_31,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT31,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = -1,
+ .irq_fs_maskbit = -1,
+ .irq_en_reg = AFE_IRQ_MCU_CON0,
+ .irq_en_shift = IRQ31_MCU_ON_SFT,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = IRQ31_MCU_CLR_SFT,
+ },
+};
+
+static const int memif_irq_usage[MT8192_MEMIF_NUM] = {
+ [MT8192_MEMIF_DL1] = MT8192_IRQ_0,
+ [MT8192_MEMIF_DL2] = MT8192_IRQ_1,
+ [MT8192_MEMIF_DL3] = MT8192_IRQ_2,
+ [MT8192_MEMIF_DL4] = MT8192_IRQ_3,
+ [MT8192_MEMIF_DL5] = MT8192_IRQ_4,
+ [MT8192_MEMIF_DL6] = MT8192_IRQ_5,
+ [MT8192_MEMIF_DL7] = MT8192_IRQ_6,
+ [MT8192_MEMIF_DL8] = MT8192_IRQ_7,
+ [MT8192_MEMIF_DL9] = MT8192_IRQ_8,
+ [MT8192_MEMIF_DL12] = MT8192_IRQ_9,
+ [MT8192_MEMIF_DAI] = MT8192_IRQ_10,
+ [MT8192_MEMIF_MOD_DAI] = MT8192_IRQ_11,
+ [MT8192_MEMIF_DAI2] = MT8192_IRQ_12,
+ [MT8192_MEMIF_VUL12] = MT8192_IRQ_13,
+ [MT8192_MEMIF_VUL2] = MT8192_IRQ_14,
+ [MT8192_MEMIF_AWB] = MT8192_IRQ_15,
+ [MT8192_MEMIF_AWB2] = MT8192_IRQ_16,
+ [MT8192_MEMIF_VUL3] = MT8192_IRQ_17,
+ [MT8192_MEMIF_VUL4] = MT8192_IRQ_18,
+ [MT8192_MEMIF_VUL5] = MT8192_IRQ_19,
+ [MT8192_MEMIF_VUL6] = MT8192_IRQ_20,
+ [MT8192_MEMIF_HDMI] = MT8192_IRQ_31,
+};
+
+static bool mt8192_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0: /* reg bit controlled by CCF */
+ case AUDIO_TOP_CON1: /* reg bit controlled by CCF */
+ case AUDIO_TOP_CON2:
+ case AUDIO_TOP_CON3:
+ case AFE_DL1_CUR_MSB:
+ case AFE_DL1_CUR:
+ case AFE_DL1_END:
+ case AFE_DL2_CUR_MSB:
+ case AFE_DL2_CUR:
+ case AFE_DL2_END:
+ case AFE_DL3_CUR_MSB:
+ case AFE_DL3_CUR:
+ case AFE_DL3_END:
+ case AFE_DL4_CUR_MSB:
+ case AFE_DL4_CUR:
+ case AFE_DL4_END:
+ case AFE_DL12_CUR_MSB:
+ case AFE_DL12_CUR:
+ case AFE_DL12_END:
+ case AFE_ADDA_SRC_DEBUG_MON0:
+ case AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_UL_SRC_MON0:
+ case AFE_ADDA_UL_SRC_MON1:
+ case AFE_SECURE_CON0:
+ case AFE_SRAM_BOUND:
+ case AFE_SECURE_CON1:
+ case AFE_VUL_CUR_MSB:
+ case AFE_VUL_CUR:
+ case AFE_VUL_END:
+ case AFE_ADDA_3RD_DAC_DL_SDM_FIFO_MON:
+ case AFE_ADDA_3RD_DAC_DL_SRC_LCH_MON:
+ case AFE_ADDA_3RD_DAC_DL_SRC_RCH_MON:
+ case AFE_ADDA_3RD_DAC_DL_SDM_OUT_MON:
+ case AFE_SIDETONE_MON:
+ case AFE_SIDETONE_CON0:
+ case AFE_SIDETONE_COEFF:
+ case AFE_VUL2_CUR_MSB:
+ case AFE_VUL2_CUR:
+ case AFE_VUL2_END:
+ case AFE_VUL3_CUR_MSB:
+ case AFE_VUL3_CUR:
+ case AFE_VUL3_END:
+ case AFE_I2S_MON:
+ case AFE_DAC_MON:
+ case AFE_IRQ0_MCU_CNT_MON:
+ case AFE_IRQ6_MCU_CNT_MON:
+ case AFE_VUL4_CUR_MSB:
+ case AFE_VUL4_CUR:
+ case AFE_VUL4_END:
+ case AFE_VUL12_CUR_MSB:
+ case AFE_VUL12_CUR:
+ case AFE_VUL12_END:
+ case AFE_IRQ3_MCU_CNT_MON:
+ case AFE_IRQ4_MCU_CNT_MON:
+ case AFE_IRQ_MCU_STATUS:
+ case AFE_IRQ_MCU_CLR:
+ case AFE_IRQ_MCU_MON2:
+ case AFE_IRQ1_MCU_CNT_MON:
+ case AFE_IRQ2_MCU_CNT_MON:
+ case AFE_IRQ5_MCU_CNT_MON:
+ case AFE_IRQ7_MCU_CNT_MON:
+ case AFE_IRQ_MCU_MISS_CLR:
+ case AFE_GAIN1_CUR:
+ case AFE_GAIN2_CUR:
+ case AFE_SRAM_DELSEL_CON1:
+ case PCM_INTF_CON2:
+ case FPGA_CFG0:
+ case FPGA_CFG1:
+ case FPGA_CFG2:
+ case FPGA_CFG3:
+ case AUDIO_TOP_DBG_MON0:
+ case AUDIO_TOP_DBG_MON1:
+ case AFE_IRQ8_MCU_CNT_MON:
+ case AFE_IRQ11_MCU_CNT_MON:
+ case AFE_IRQ12_MCU_CNT_MON:
+ case AFE_IRQ9_MCU_CNT_MON:
+ case AFE_IRQ10_MCU_CNT_MON:
+ case AFE_IRQ13_MCU_CNT_MON:
+ case AFE_IRQ14_MCU_CNT_MON:
+ case AFE_IRQ15_MCU_CNT_MON:
+ case AFE_IRQ16_MCU_CNT_MON:
+ case AFE_IRQ17_MCU_CNT_MON:
+ case AFE_IRQ18_MCU_CNT_MON:
+ case AFE_IRQ19_MCU_CNT_MON:
+ case AFE_IRQ20_MCU_CNT_MON:
+ case AFE_IRQ21_MCU_CNT_MON:
+ case AFE_IRQ22_MCU_CNT_MON:
+ case AFE_IRQ23_MCU_CNT_MON:
+ case AFE_IRQ24_MCU_CNT_MON:
+ case AFE_IRQ25_MCU_CNT_MON:
+ case AFE_IRQ26_MCU_CNT_MON:
+ case AFE_IRQ31_MCU_CNT_MON:
+ case AFE_CBIP_MON0:
+ case AFE_CBIP_SLV_MUX_MON0:
+ case AFE_CBIP_SLV_DECODER_MON0:
+ case AFE_ADDA6_MTKAIF_MON0:
+ case AFE_ADDA6_MTKAIF_MON1:
+ case AFE_AWB_CUR_MSB:
+ case AFE_AWB_CUR:
+ case AFE_AWB_END:
+ case AFE_AWB2_CUR_MSB:
+ case AFE_AWB2_CUR:
+ case AFE_AWB2_END:
+ case AFE_DAI_CUR_MSB:
+ case AFE_DAI_CUR:
+ case AFE_DAI_END:
+ case AFE_DAI2_CUR_MSB:
+ case AFE_DAI2_CUR:
+ case AFE_DAI2_END:
+ case AFE_ADDA6_SRC_DEBUG_MON0:
+ case AFE_ADD6A_UL_SRC_MON0:
+ case AFE_ADDA6_UL_SRC_MON1:
+ case AFE_MOD_DAI_CUR_MSB:
+ case AFE_MOD_DAI_CUR:
+ case AFE_MOD_DAI_END:
+ case AFE_HDMI_OUT_CUR_MSB:
+ case AFE_HDMI_OUT_CUR:
+ case AFE_HDMI_OUT_END:
+ case AFE_AWB_RCH_MON:
+ case AFE_AWB_LCH_MON:
+ case AFE_VUL_RCH_MON:
+ case AFE_VUL_LCH_MON:
+ case AFE_VUL12_RCH_MON:
+ case AFE_VUL12_LCH_MON:
+ case AFE_VUL2_RCH_MON:
+ case AFE_VUL2_LCH_MON:
+ case AFE_DAI_DATA_MON:
+ case AFE_MOD_DAI_DATA_MON:
+ case AFE_DAI2_DATA_MON:
+ case AFE_AWB2_RCH_MON:
+ case AFE_AWB2_LCH_MON:
+ case AFE_VUL3_RCH_MON:
+ case AFE_VUL3_LCH_MON:
+ case AFE_VUL4_RCH_MON:
+ case AFE_VUL4_LCH_MON:
+ case AFE_VUL5_RCH_MON:
+ case AFE_VUL5_LCH_MON:
+ case AFE_VUL6_RCH_MON:
+ case AFE_VUL6_LCH_MON:
+ case AFE_DL1_RCH_MON:
+ case AFE_DL1_LCH_MON:
+ case AFE_DL2_RCH_MON:
+ case AFE_DL2_LCH_MON:
+ case AFE_DL12_RCH1_MON:
+ case AFE_DL12_LCH1_MON:
+ case AFE_DL12_RCH2_MON:
+ case AFE_DL12_LCH2_MON:
+ case AFE_DL3_RCH_MON:
+ case AFE_DL3_LCH_MON:
+ case AFE_DL4_RCH_MON:
+ case AFE_DL4_LCH_MON:
+ case AFE_DL5_RCH_MON:
+ case AFE_DL5_LCH_MON:
+ case AFE_DL6_RCH_MON:
+ case AFE_DL6_LCH_MON:
+ case AFE_DL7_RCH_MON:
+ case AFE_DL7_LCH_MON:
+ case AFE_DL8_RCH_MON:
+ case AFE_DL8_LCH_MON:
+ case AFE_VUL5_CUR_MSB:
+ case AFE_VUL5_CUR:
+ case AFE_VUL5_END:
+ case AFE_VUL6_CUR_MSB:
+ case AFE_VUL6_CUR:
+ case AFE_VUL6_END:
+ case AFE_ADDA_DL_SDM_FIFO_MON:
+ case AFE_ADDA_DL_SRC_LCH_MON:
+ case AFE_ADDA_DL_SRC_RCH_MON:
+ case AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_CONNSYS_I2S_MON:
+ case AFE_ASRC_2CH_CON0:
+ case AFE_ASRC_2CH_CON2:
+ case AFE_ASRC_2CH_CON3:
+ case AFE_ASRC_2CH_CON4:
+ case AFE_ASRC_2CH_CON5:
+ case AFE_ASRC_2CH_CON7:
+ case AFE_ASRC_2CH_CON8:
+ case AFE_ASRC_2CH_CON12:
+ case AFE_ASRC_2CH_CON13:
+ case AFE_DL9_CUR_MSB:
+ case AFE_DL9_CUR:
+ case AFE_DL9_END:
+ case AFE_ADDA_MTKAIF_MON0:
+ case AFE_ADDA_MTKAIF_MON1:
+ case AFE_DL_NLE_R_MON0:
+ case AFE_DL_NLE_R_MON1:
+ case AFE_DL_NLE_R_MON2:
+ case AFE_DL_NLE_L_MON0:
+ case AFE_DL_NLE_L_MON1:
+ case AFE_DL_NLE_L_MON2:
+ case AFE_GENERAL1_ASRC_2CH_CON0:
+ case AFE_GENERAL1_ASRC_2CH_CON2:
+ case AFE_GENERAL1_ASRC_2CH_CON3:
+ case AFE_GENERAL1_ASRC_2CH_CON4:
+ case AFE_GENERAL1_ASRC_2CH_CON5:
+ case AFE_GENERAL1_ASRC_2CH_CON7:
+ case AFE_GENERAL1_ASRC_2CH_CON8:
+ case AFE_GENERAL1_ASRC_2CH_CON12:
+ case AFE_GENERAL1_ASRC_2CH_CON13:
+ case AFE_GENERAL2_ASRC_2CH_CON0:
+ case AFE_GENERAL2_ASRC_2CH_CON2:
+ case AFE_GENERAL2_ASRC_2CH_CON3:
+ case AFE_GENERAL2_ASRC_2CH_CON4:
+ case AFE_GENERAL2_ASRC_2CH_CON5:
+ case AFE_GENERAL2_ASRC_2CH_CON7:
+ case AFE_GENERAL2_ASRC_2CH_CON8:
+ case AFE_GENERAL2_ASRC_2CH_CON12:
+ case AFE_GENERAL2_ASRC_2CH_CON13:
+ case AFE_DL9_RCH_MON:
+ case AFE_DL9_LCH_MON:
+ case AFE_DL5_CUR_MSB:
+ case AFE_DL5_CUR:
+ case AFE_DL5_END:
+ case AFE_DL6_CUR_MSB:
+ case AFE_DL6_CUR:
+ case AFE_DL6_END:
+ case AFE_DL7_CUR_MSB:
+ case AFE_DL7_CUR:
+ case AFE_DL7_END:
+ case AFE_DL8_CUR_MSB:
+ case AFE_DL8_CUR:
+ case AFE_DL8_END:
+ case AFE_PROT_SIDEBAND_MON:
+ case AFE_DOMAIN_SIDEBAND0_MON:
+ case AFE_DOMAIN_SIDEBAND1_MON:
+ case AFE_DOMAIN_SIDEBAND2_MON:
+ case AFE_DOMAIN_SIDEBAND3_MON:
+ case AFE_APLL1_TUNER_CFG: /* [20:31] is monitor */
+ case AFE_APLL2_TUNER_CFG: /* [20:31] is monitor */
+ case AFE_DAC_CON0:
+ case AFE_IRQ_MCU_CON0:
+ case AFE_IRQ_MCU_EN:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8192_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .volatile_reg = mt8192_is_volatile_reg,
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = AFE_MAX_REGISTER,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static irqreturn_t mt8192_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ unsigned int status;
+ unsigned int status_mcu;
+ unsigned int mcu_en;
+ int ret;
+ int i;
+
+ /* get irq that is sent to MCU */
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ /* only care IRQ which is sent to MCU */
+ status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
+
+ if (ret || status_mcu == 0) {
+ dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n",
+ __func__, ret, status, mcu_en);
+
+ goto err_irq;
+ }
+
+ for (i = 0; i < MT8192_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (status_mcu & (1 << irq->irq_data->irq_en_shift))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap,
+ AFE_IRQ_MCU_CLR,
+ status_mcu);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8192_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ unsigned int value;
+ int ret;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ /* disable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, AFE_ON_MASK_SFT, 0x0);
+
+ ret = regmap_read_poll_timeout(afe->regmap,
+ AFE_DAC_MON,
+ value,
+ (value & AFE_ON_RETM_MASK_SFT) == 0,
+ 20,
+ 1 * 1000 * 1000);
+ if (ret)
+ dev_warn(afe->dev, "%s(), ret %d\n", __func__, ret);
+
+ /* make sure all irq status are cleared */
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, 0xffffffff);
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, 0xffffffff);
+
+ /* reset sgen */
+ regmap_write(afe->regmap, AFE_SINEGEN_CON0, 0x0);
+ regmap_update_bits(afe->regmap, AFE_SINEGEN_CON2,
+ INNER_LOOP_BACK_MODE_MASK_SFT,
+ 0x3f << INNER_LOOP_BACK_MODE_SFT);
+
+ /* cache only */
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8192_afe_disable_clock(afe);
+ return 0;
+}
+
+static int mt8192_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ ret = mt8192_afe_enable_clock(afe);
+ if (ret)
+ return ret;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ /* enable audio sys DCM for power saving */
+ regmap_update_bits(afe_priv->infracfg,
+ PERI_BUS_DCM_CTRL, 0x1 << 29, 0x1 << 29);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, 0x1 << 29, 0x1 << 29);
+
+ /* force cpu use 8_24 format when writing 32bit data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_CON0,
+ CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
+
+ /* set all output port to 24bit */
+ regmap_write(afe->regmap, AFE_CONN_24BIT, 0xffffffff);
+ regmap_write(afe->regmap, AFE_CONN_24BIT_1, 0xffffffff);
+
+ /* enable AFE */
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, AFE_ON_MASK_SFT, 0x1);
+
+skip_regmap:
+ return 0;
+}
+
+static int mt8192_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8192_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8192_memif_dai_driver);
+
+ dai->dapm_widgets = mt8192_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8192_memif_widgets);
+ dai->dapm_routes = mt8192_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8192_memif_routes);
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8192_dai_adda_register,
+ mt8192_dai_i2s_register,
+ mt8192_dai_pcm_register,
+ mt8192_dai_tdm_register,
+ mt8192_dai_memif_register,
+};
+
+static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8192_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ int i, ret, irq_id;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+ afe_priv = afe->platform_priv;
+
+ afe->dev = dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
+
+ /* init audio related clock */
+ ret = mt8192_init_clock(afe);
+ if (ret) {
+ dev_err(dev, "init clock error\n");
+ return ret;
+ }
+
+ /* reset controller to reset audio regs before regmap cache */
+ rstc = devm_reset_control_get_exclusive(dev, "audiosys");
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n");
+
+ ret = reset_control_reset(rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /* regmap init */
+ afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(afe->regmap))
+ return dev_err_probe(dev, PTR_ERR(afe->regmap),
+ "could not get regmap from parent");
+
+ ret = regmap_attach_dev(dev, afe->regmap, &mt8192_afe_regmap_config);
+ if (ret)
+ return dev_err_probe(dev, ret, "regmap_attach_dev fail\n");
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+ pm_runtime_get_sync(dev);
+
+ ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config);
+ if (ret)
+ return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n");
+
+ pm_runtime_put_sync(dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ /* init memif */
+ afe->memif_size = MT8192_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = memif_irq_usage[i];
+ afe->memif[i].const_irq = 1;
+ }
+
+ mutex_init(&afe->irq_alloc_lock); /* needed when dynamic irq */
+
+ /* init irq */
+ afe->irqs_size = MT8192_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return irq_id;
+
+ ret = devm_request_irq(dev, irq_id, mt8192_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "dai %d register fail", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ /* others */
+ afe->mtk_afe_hardware = &mt8192_afe_hardware;
+ afe->memif_fs = mt8192_memif_fs;
+ afe->irq_fs = mt8192_irq_fs;
+ afe->get_dai_fs = mt8192_get_dai_fs;
+ afe->get_memif_pbuf_size = mt8192_get_memif_pbuf_size;
+ afe->memif_32bit_supported = 1;
+
+ afe->runtime_resume = mt8192_afe_runtime_resume;
+ afe->runtime_suspend = mt8192_afe_runtime_suspend;
+
+ /* register platform */
+ ret = devm_snd_soc_register_component(dev,
+ &mtk_afe_pcm_platform,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret)
+ return dev_err_probe(dev, ret, "Couldn't register AFE component\n");
+
+ return 0;
+}
+
+static void mt8192_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt8192_afe_runtime_suspend(&pdev->dev);
+
+ /* disable afe clock */
+ mt8192_afe_disable_clock(afe);
+}
+
+static const struct of_device_id mt8192_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8192-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8192_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8192_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8192_afe_runtime_suspend,
+ mt8192_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8192_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8192-audio",
+ .of_match_table = mt8192_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8192_afe_pm_ops),
+ },
+ .probe = mt8192_afe_pcm_dev_probe,
+ .remove = mt8192_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8192_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8192");
+MODULE_AUTHOR("Shane Chien <shane.chien@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-adda.c b/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
new file mode 100644
index 000000000000..f8cb84621d38
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
@@ -0,0 +1,1365 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI ADDA Control
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include <linux/delay.h>
+#include <linux/regmap.h>
+
+#include "mt8192-afe-clk.h"
+#include "mt8192-afe-common.h"
+#include "mt8192-afe-gpio.h"
+#include "mt8192-interconnection.h"
+#include "../common/mtk-dai-adda-common.h"
+
+enum {
+ UL_IIR_SW = 0,
+ UL_IIR_5HZ,
+ UL_IIR_10HZ,
+ UL_IIR_25HZ,
+ UL_IIR_50HZ,
+ UL_IIR_75HZ,
+};
+
+enum {
+ AUDIO_SDM_LEVEL_MUTE = 0,
+ AUDIO_SDM_LEVEL_NORMAL = 0x1d,
+ /* if you change level normal */
+ /* you need to change formula of hp impedance and dc trim too */
+};
+
+enum {
+ AUDIO_SDM_2ND = 0,
+ AUDIO_SDM_3RD,
+};
+
+#define SDM_AUTO_RESET_THRESHOLD 0x190000
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN3, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN3, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN3, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN3, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN3_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN3_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN3_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN3_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN3,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN3,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN3,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN3,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN3,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN3,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH1", AFE_CONN3_1,
+ I_SRC_1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_2_OUT_CH1", AFE_CONN3_1,
+ I_SRC_2_OUT_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN4, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN4, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN4, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN4, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN4, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN4, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN4, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN4_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN4_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN4_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN4_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN4,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN4,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN4,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN4,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN4,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN4,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN4,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN4,
+ I_PCM_2_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_1_OUT_CH2", AFE_CONN4_1,
+ I_SRC_1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("SRC_2_OUT_CH2", AFE_CONN4_1,
+ I_SRC_2_OUT_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN52, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN52, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN52, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN52, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN52_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN52_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN52_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN52,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN52,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN52,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN52,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN52,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN52,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN53, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN53, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN53, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN53, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN53, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN53, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN53, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN53_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN53_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN53_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN53,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN53,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN53,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN53,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN53,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN53,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN53,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN53,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_stf_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN19,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_stf_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN20,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+enum {
+ SUPPLY_SEQ_ADDA_AFE_ON,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
+ SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA6_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA_FIFO,
+ SUPPLY_SEQ_ADDA_AP_DMIC,
+ SUPPLY_SEQ_ADDA_UL_ON,
+};
+
+static int mtk_adda_ul_src_dmic(struct mtk_base_afe *afe, int id)
+{
+ unsigned int reg;
+
+ switch (id) {
+ case MT8192_DAI_ADDA:
+ case MT8192_DAI_AP_DMIC:
+ reg = AFE_ADDA_UL_SRC_CON0;
+ break;
+ case MT8192_DAI_ADDA_CH34:
+ case MT8192_DAI_AP_DMIC_CH34:
+ reg = AFE_ADDA6_UL_SRC_CON0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, reg,
+ DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT,
+ 0x0);
+ regmap_update_bits(afe->regmap, reg,
+ DMIC_LOW_POWER_MODE_CTL_MASK_SFT,
+ 0x0);
+
+ /* turn on dmic, ch1, ch2 */
+ regmap_update_bits(afe->regmap, reg,
+ UL_SDM_3_LEVEL_CTL_MASK_SFT,
+ 0x1 << UL_SDM_3_LEVEL_CTL_SFT);
+ regmap_update_bits(afe->regmap, reg,
+ UL_MODE_3P25M_CH1_CTL_MASK_SFT,
+ 0x1 << UL_MODE_3P25M_CH1_CTL_SFT);
+ regmap_update_bits(afe->regmap, reg,
+ UL_MODE_3P25M_CH2_CTL_MASK_SFT,
+ 0x1 << UL_MODE_3P25M_CH2_CTL_SFT);
+ return 0;
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int mtkaif_dmic = afe_priv->mtkaif_dmic;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
+
+ /* update setting to dmic */
+ if (mtkaif_dmic) {
+ /* mtkaif_rxif_data_mode = 1, dmic */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
+ 0x1, 0x1);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
+ MTKAIF_RXIF_VOICE_MODE_MASK_SFT,
+ 0x0);
+ mtk_adda_ul_src_dmic(afe, MT8192_DAI_ADDA);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_ch34_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int mtkaif_dmic = afe_priv->mtkaif_dmic_ch34;
+ int mtkaif_adda6_only = afe_priv->mtkaif_adda6_only;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34,
+ 1);
+
+ /* update setting to dmic */
+ if (mtkaif_dmic) {
+ /* mtkaif_rxif_data_mode = 1, dmic */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA6_MTKAIF_RX_CFG0,
+ 0x1, 0x1);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA6_MTKAIF_RX_CFG0,
+ MTKAIF_RXIF_VOICE_MODE_MASK_SFT,
+ 0x0);
+ mtk_adda_ul_src_dmic(afe, MT8192_DAI_ADDA_CH34);
+ }
+
+ /* when using adda6 without adda enabled,
+ * RG_ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE_SFT need to be set or
+ * data cannot be received.
+ */
+ if (mtkaif_adda6_only) {
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_SYNCWORD_CFG,
+ 0x1 << 23, 0x1 << 23);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34,
+ 1);
+
+ /* reset dmic */
+ afe_priv->mtkaif_dmic_ch34 = 0;
+
+ if (mtkaif_adda6_only) {
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_SYNCWORD_CFG,
+ 0x1 << 23, 0x0 << 23);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2)
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x38);
+ else
+ regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x30);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int delay_data;
+ int delay_cycle;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) {
+ /* set protocol 2 */
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ 0x00010000);
+ regmap_write(afe->regmap, AFE_ADDA6_MTKAIF_CFG0,
+ 0x00010000);
+
+ if (snd_soc_dapm_widget_name_cmp(w, "ADDA_MTKAIF_CFG") == 0 &&
+ (afe_priv->mtkaif_chosen_phase[0] < 0 ||
+ afe_priv->mtkaif_chosen_phase[1] < 0)) {
+ dev_warn(afe->dev,
+ "%s(), mtkaif_chosen_phase[0/1]:%d/%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[0],
+ afe_priv->mtkaif_chosen_phase[1]);
+ break;
+ } else if (snd_soc_dapm_widget_name_cmp(w, "ADDA6_MTKAIF_CFG") == 0 &&
+ afe_priv->mtkaif_chosen_phase[2] < 0) {
+ dev_warn(afe->dev,
+ "%s(), mtkaif_chosen_phase[2]:%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[2]);
+ break;
+ }
+
+ /* mtkaif_rxif_clkinv_adc inverse for calibration */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ MTKAIF_RXIF_CLKINV_ADC_MASK_SFT,
+ 0x1 << MTKAIF_RXIF_CLKINV_ADC_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIF_CFG0,
+ MTKAIF_RXIF_CLKINV_ADC_MASK_SFT,
+ 0x1 << MTKAIF_RXIF_CLKINV_ADC_SFT);
+
+ /* set delay for ch12 */
+ if (afe_priv->mtkaif_phase_cycle[0] >=
+ afe_priv->mtkaif_phase_cycle[1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[0] -
+ afe_priv->mtkaif_phase_cycle[1];
+ } else {
+ delay_data = DELAY_DATA_MISO2;
+ delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
+ afe_priv->mtkaif_phase_cycle[0];
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_DATA_MASK_SFT,
+ delay_data <<
+ MTKAIF_RXIF_DELAY_DATA_SFT);
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT,
+ delay_cycle <<
+ MTKAIF_RXIF_DELAY_CYCLE_SFT);
+
+ /* set delay between ch3 and ch2 */
+ if (afe_priv->mtkaif_phase_cycle[2] >=
+ afe_priv->mtkaif_phase_cycle[1]) {
+ delay_data = DELAY_DATA_MISO1; /* ch3 */
+ delay_cycle = afe_priv->mtkaif_phase_cycle[2] -
+ afe_priv->mtkaif_phase_cycle[1];
+ } else {
+ delay_data = DELAY_DATA_MISO2; /* ch2 */
+ delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
+ afe_priv->mtkaif_phase_cycle[2];
+ }
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA6_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_DATA_MASK_SFT,
+ delay_data <<
+ MTKAIF_RXIF_DELAY_DATA_SFT);
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA6_MTKAIF_RX_CFG2,
+ MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT,
+ delay_cycle <<
+ MTKAIF_RXIF_DELAY_CYCLE_SFT);
+ } else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) {
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ 0x00010000);
+ regmap_write(afe->regmap, AFE_ADDA6_MTKAIF_CFG0,
+ 0x00010000);
+ } else {
+ regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0x0);
+ regmap_write(afe->regmap, AFE_ADDA6_MTKAIF_CFG0, 0x0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_ch34_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34,
+ 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34,
+ 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* stf */
+static int stf_positive_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->stf_positive_gain_db;
+ return 0;
+}
+
+static int stf_positive_gain_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int gain_db = ucontrol->value.integer.value[0];
+ bool change = false;
+
+ afe_priv->stf_positive_gain_db = gain_db;
+
+ if (gain_db >= 0 && gain_db <= 24) {
+ regmap_update_bits_check(afe->regmap,
+ AFE_SIDETONE_GAIN,
+ POSITIVE_GAIN_MASK_SFT,
+ (gain_db / 6) << POSITIVE_GAIN_SFT,
+ &change);
+ } else {
+ return -EINVAL;
+ }
+
+ return change;
+}
+
+static int mt8192_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->mtkaif_dmic;
+ return 0;
+}
+
+static int mt8192_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int dmic_on;
+ bool change;
+
+ dmic_on = ucontrol->value.integer.value[0];
+
+ change = (afe_priv->mtkaif_dmic != dmic_on) ||
+ (afe_priv->mtkaif_dmic_ch34 != dmic_on);
+
+ afe_priv->mtkaif_dmic = dmic_on;
+ afe_priv->mtkaif_dmic_ch34 = dmic_on;
+
+ return change;
+}
+
+static int mt8192_adda6_only_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ ucontrol->value.integer.value[0] = afe_priv->mtkaif_adda6_only;
+ return 0;
+}
+
+static int mt8192_adda6_only_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int mtkaif_adda6_only;
+ bool change;
+
+ mtkaif_adda6_only = ucontrol->value.integer.value[0];
+
+ change = afe_priv->mtkaif_adda6_only != mtkaif_adda6_only;
+ afe_priv->mtkaif_adda6_only = mtkaif_adda6_only;
+
+ return change;
+}
+
+static const struct snd_kcontrol_new mtk_adda_controls[] = {
+ SOC_SINGLE("Sidetone_Gain", AFE_SIDETONE_GAIN,
+ SIDE_TONE_GAIN_SFT, SIDE_TONE_GAIN_MASK, 0),
+ SOC_SINGLE_EXT("Sidetone_Positive_Gain_dB", SND_SOC_NOPM, 0, 24, 0,
+ stf_positive_gain_get, stf_positive_gain_set),
+ SOC_SINGLE("ADDA_DL_GAIN", AFE_ADDA_DL_SRC2_CON1,
+ DL_2_GAIN_CTL_PRE_SFT, DL_2_GAIN_CTL_PRE_MASK, 0),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC Switch", 0,
+ mt8192_adda_dmic_get, mt8192_adda_dmic_set),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_ADDA6_ONLY Switch", 0,
+ mt8192_adda6_only_get, mt8192_adda6_only_set),
+};
+
+static const struct snd_kcontrol_new stf_ctl =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const u16 stf_coeff_table_16k[] = {
+ 0x049C, 0x09E8, 0x09E0, 0x089C,
+ 0xFF54, 0xF488, 0xEAFC, 0xEBAC,
+ 0xfA40, 0x17AC, 0x3D1C, 0x6028,
+ 0x7538
+};
+
+static const u16 stf_coeff_table_32k[] = {
+ 0xFE52, 0x0042, 0x00C5, 0x0194,
+ 0x029A, 0x03B7, 0x04BF, 0x057D,
+ 0x05BE, 0x0555, 0x0426, 0x0230,
+ 0xFF92, 0xFC89, 0xF973, 0xF6C6,
+ 0xF500, 0xF49D, 0xF603, 0xF970,
+ 0xFEF3, 0x065F, 0x0F4F, 0x1928,
+ 0x2329, 0x2C80, 0x345E, 0x3A0D,
+ 0x3D08
+};
+
+static const u16 stf_coeff_table_48k[] = {
+ 0x0401, 0xFFB0, 0xFF5A, 0xFECE,
+ 0xFE10, 0xFD28, 0xFC21, 0xFB08,
+ 0xF9EF, 0xF8E8, 0xF80A, 0xF76C,
+ 0xF724, 0xF746, 0xF7E6, 0xF90F,
+ 0xFACC, 0xFD1E, 0xFFFF, 0x0364,
+ 0x0737, 0x0B62, 0x0FC1, 0x1431,
+ 0x188A, 0x1CA4, 0x2056, 0x237D,
+ 0x25F9, 0x27B0, 0x2890
+};
+
+static int mtk_stf_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ size_t half_tap_num;
+ const u16 *stf_coeff_table;
+ unsigned int ul_rate, reg_value;
+ size_t coef_addr;
+
+ regmap_read(afe->regmap, AFE_ADDA_UL_SRC_CON0, &ul_rate);
+ ul_rate = ul_rate >> UL_VOICE_MODE_CH1_CH2_CTL_SFT;
+ ul_rate = ul_rate & UL_VOICE_MODE_CH1_CH2_CTL_MASK;
+
+ if (ul_rate == MTK_AFE_ADDA_UL_RATE_48K) {
+ half_tap_num = ARRAY_SIZE(stf_coeff_table_48k);
+ stf_coeff_table = stf_coeff_table_48k;
+ } else if (ul_rate == MTK_AFE_ADDA_UL_RATE_32K) {
+ half_tap_num = ARRAY_SIZE(stf_coeff_table_32k);
+ stf_coeff_table = stf_coeff_table_32k;
+ } else {
+ half_tap_num = ARRAY_SIZE(stf_coeff_table_16k);
+ stf_coeff_table = stf_coeff_table_16k;
+ }
+
+ regmap_read(afe->regmap, AFE_SIDETONE_CON1, &reg_value);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* set side tone gain = 0 */
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_GAIN,
+ SIDE_TONE_GAIN_MASK_SFT,
+ 0);
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_GAIN,
+ POSITIVE_GAIN_MASK_SFT,
+ 0);
+ /* don't bypass stf */
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_CON1,
+ 0x1f << 27,
+ 0x0);
+ /* set stf half tap num */
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_CON1,
+ SIDE_TONE_HALF_TAP_NUM_MASK_SFT,
+ half_tap_num << SIDE_TONE_HALF_TAP_NUM_SFT);
+
+ /* set side tone coefficient */
+ regmap_read(afe->regmap, AFE_SIDETONE_CON0, &reg_value);
+ for (coef_addr = 0; coef_addr < half_tap_num; coef_addr++) {
+ bool old_w_ready = (reg_value >> W_RDY_SFT) & 0x1;
+ bool new_w_ready = 0;
+ int try_cnt = 0;
+
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_CON0,
+ 0x39FFFFF,
+ (1 << R_W_EN_SFT) |
+ (1 << R_W_SEL_SFT) |
+ (0 << SEL_CH2_SFT) |
+ (coef_addr <<
+ SIDE_TONE_COEFFICIENT_ADDR_SFT) |
+ stf_coeff_table[coef_addr]);
+
+ /* wait until flag write_ready changed */
+ for (try_cnt = 0; try_cnt < 10; try_cnt++) {
+ regmap_read(afe->regmap,
+ AFE_SIDETONE_CON0, &reg_value);
+ new_w_ready = (reg_value >> W_RDY_SFT) & 0x1;
+
+ /* flip => ok */
+ if (new_w_ready == old_w_ready) {
+ udelay(3);
+ if (try_cnt == 9) {
+ dev_warn(afe->dev,
+ "%s(), write coeff not ready",
+ __func__);
+ }
+ } else {
+ break;
+ }
+ }
+ /* need write -> read -> write to write next coeff */
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_CON0,
+ R_W_SEL_MASK_SFT,
+ 0x0);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* bypass stf */
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_CON1,
+ 0x1f << 27,
+ 0x1f << 27);
+
+ /* set side tone gain = 0 */
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_GAIN,
+ SIDE_TONE_GAIN_MASK_SFT,
+ 0);
+ regmap_update_bits(afe->regmap,
+ AFE_SIDETONE_GAIN,
+ POSITIVE_GAIN_MASK_SFT,
+ 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* stf mux */
+enum {
+ STF_SRC_ADDA_ADDA6 = 0,
+ STF_SRC_O19O20,
+};
+
+static const char *const stf_o19o20_mux_map[] = {
+ "ADDA_ADDA6",
+ "O19O20",
+};
+
+static int stf_o19o20_mux_map_value[] = {
+ STF_SRC_ADDA_ADDA6,
+ STF_SRC_O19O20,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(stf_o19o20_mux_map_enum,
+ AFE_SIDETONE_CON1,
+ STF_SOURCE_FROM_O19O20_SFT,
+ STF_SOURCE_FROM_O19O20_MASK,
+ stf_o19o20_mux_map,
+ stf_o19o20_mux_map_value);
+
+static const struct snd_kcontrol_new stf_o19O20_mux_control =
+ SOC_DAPM_ENUM("STF_O19O20_MUX", stf_o19o20_mux_map_enum);
+
+enum {
+ STF_SRC_ADDA = 0,
+ STF_SRC_ADDA6,
+};
+
+static const char *const stf_adda_mux_map[] = {
+ "ADDA",
+ "ADDA6",
+};
+
+static int stf_adda_mux_map_value[] = {
+ STF_SRC_ADDA,
+ STF_SRC_ADDA6,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(stf_adda_mux_map_enum,
+ AFE_SIDETONE_CON1,
+ STF_O19O20_OUT_EN_SEL_SFT,
+ STF_O19O20_OUT_EN_SEL_MASK,
+ stf_adda_mux_map,
+ stf_adda_mux_map_value);
+
+static const struct snd_kcontrol_new stf_adda_mux_control =
+ SOC_DAPM_ENUM("STF_ADDA_MUX", stf_adda_mux_map_enum);
+
+/* ADDA UL MUX */
+enum {
+ ADDA_UL_MUX_MTKAIF = 0,
+ ADDA_UL_MUX_AP_DMIC,
+ ADDA_UL_MUX_MASK = 0x1,
+};
+
+static const char * const adda_ul_mux_map[] = {
+ "MTKAIF", "AP_DMIC"
+};
+
+static int adda_ul_map_value[] = {
+ ADDA_UL_MUX_MTKAIF,
+ ADDA_UL_MUX_AP_DMIC,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adda_ul_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADDA_UL_MUX_MASK,
+ adda_ul_mux_map,
+ adda_ul_map_value);
+
+static const struct snd_kcontrol_new adda_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_kcontrol_new adda_ch34_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_CH34_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch1_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch2_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH3", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch3_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch3_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch4_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch4_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ mtk_adda_dl_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ADDA CH34 Playback Enable",
+ SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_3RD_DAC_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
+ mtk_adda_ch34_dl_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ADDA CH34 Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA6_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ch34_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("AUD_PAD_TOP", SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
+ AFE_AUD_PAD_TOP,
+ RG_RX_FIFO_ON_SFT, 0,
+ mtk_adda_pad_top_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("ADDA6_MTKAIF_CFG", SUPPLY_SEQ_ADDA6_MTKAIF_CFG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_CH34_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA6_UL_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_FIFO_AUTO_RST_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_CH34_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA6_FIFO_AUTO_RST_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADDA_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ul_mux_control),
+ SND_SOC_DAPM_MUX("ADDA_CH34_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ch34_ul_mux_control),
+
+ SND_SOC_DAPM_INPUT("AP_DMIC_INPUT"),
+ SND_SOC_DAPM_INPUT("AP_DMIC_CH34_INPUT"),
+
+ /* stf */
+ SND_SOC_DAPM_SWITCH_E("Sidetone Filter",
+ AFE_SIDETONE_CON1, SIDE_TONE_ON_SFT, 0,
+ &stf_ctl,
+ mtk_stf_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("STF_O19O20_MUX", SND_SOC_NOPM, 0, 0,
+ &stf_o19O20_mux_control),
+ SND_SOC_DAPM_MUX("STF_ADDA_MUX", SND_SOC_NOPM, 0, 0,
+ &stf_adda_mux_control),
+ SND_SOC_DAPM_MIXER("STF_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_stf_ch1_mix,
+ ARRAY_SIZE(mtk_stf_ch1_mix)),
+ SND_SOC_DAPM_MIXER("STF_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_stf_ch2_mix,
+ ARRAY_SIZE(mtk_stf_ch2_mix)),
+ SND_SOC_DAPM_OUTPUT("STF_OUTPUT"),
+
+ /* clock */
+ SND_SOC_DAPM_CLOCK_SUPPLY("top_mux_audio_h"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_3rd_dac_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_3rd_dac_predis_clk"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adda6_adc_clk"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* playback */
+ {"ADDA_DL_CH1", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH2", "DL1_CH2", "DL1"},
+
+ {"ADDA_DL_CH1", "DL12_CH1", "DL12"},
+ {"ADDA_DL_CH2", "DL12_CH2", "DL12"},
+
+ {"ADDA_DL_CH1", "DL6_CH1", "DL6"},
+ {"ADDA_DL_CH2", "DL6_CH2", "DL6"},
+
+ {"ADDA_DL_CH1", "DL8_CH1", "DL8"},
+ {"ADDA_DL_CH2", "DL8_CH2", "DL8"},
+
+ {"ADDA_DL_CH1", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH2", "DL2_CH2", "DL2"},
+
+ {"ADDA_DL_CH1", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH2", "DL3_CH2", "DL3"},
+
+ {"ADDA_DL_CH1", "DL4_CH1", "DL4"},
+ {"ADDA_DL_CH2", "DL4_CH2", "DL4"},
+
+ {"ADDA_DL_CH1", "DL5_CH1", "DL5"},
+ {"ADDA_DL_CH2", "DL5_CH2", "DL5"},
+
+ {"ADDA Playback", NULL, "ADDA_DL_CH1"},
+ {"ADDA Playback", NULL, "ADDA_DL_CH2"},
+
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+
+ {"ADDA_DL_CH3", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH4", "DL1_CH1", "DL1"},
+ {"ADDA_DL_CH4", "DL1_CH2", "DL1"},
+
+ {"ADDA_DL_CH3", "DL12_CH1", "DL12"},
+ {"ADDA_DL_CH4", "DL12_CH2", "DL12"},
+
+ {"ADDA_DL_CH3", "DL6_CH1", "DL6"},
+ {"ADDA_DL_CH4", "DL6_CH2", "DL6"},
+
+ {"ADDA_DL_CH3", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH4", "DL2_CH1", "DL2"},
+ {"ADDA_DL_CH4", "DL2_CH2", "DL2"},
+
+ {"ADDA_DL_CH3", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH4", "DL3_CH1", "DL3"},
+ {"ADDA_DL_CH4", "DL3_CH2", "DL3"},
+
+ {"ADDA_DL_CH3", "DL4_CH1", "DL4"},
+ {"ADDA_DL_CH4", "DL4_CH2", "DL4"},
+
+ {"ADDA_DL_CH3", "DL5_CH1", "DL5"},
+ {"ADDA_DL_CH4", "DL5_CH2", "DL5"},
+
+ {"ADDA CH34 Playback", NULL, "ADDA_DL_CH3"},
+ {"ADDA CH34 Playback", NULL, "ADDA_DL_CH4"},
+
+ {"ADDA CH34 Playback", NULL, "ADDA Enable"},
+ {"ADDA CH34 Playback", NULL, "ADDA CH34 Playback Enable"},
+
+ /* capture */
+ {"ADDA_UL_Mux", "MTKAIF", "ADDA Capture"},
+ {"ADDA_UL_Mux", "AP_DMIC", "AP DMIC Capture"},
+
+ {"ADDA_CH34_UL_Mux", "MTKAIF", "ADDA CH34 Capture"},
+ {"ADDA_CH34_UL_Mux", "AP_DMIC", "AP DMIC CH34 Capture"},
+
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+ {"ADDA Capture", NULL, "AUD_PAD_TOP"},
+ {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
+
+ {"AP DMIC Capture", NULL, "ADDA Enable"},
+ {"AP DMIC Capture", NULL, "ADDA Capture Enable"},
+ {"AP DMIC Capture", NULL, "ADDA_FIFO"},
+ {"AP DMIC Capture", NULL, "AP_DMIC_EN"},
+
+ {"ADDA CH34 Capture", NULL, "ADDA Enable"},
+ {"ADDA CH34 Capture", NULL, "ADDA CH34 Capture Enable"},
+ {"ADDA CH34 Capture", NULL, "AUD_PAD_TOP"},
+ {"ADDA CH34 Capture", NULL, "ADDA6_MTKAIF_CFG"},
+
+ {"AP DMIC CH34 Capture", NULL, "ADDA Enable"},
+ {"AP DMIC CH34 Capture", NULL, "ADDA CH34 Capture Enable"},
+ {"AP DMIC CH34 Capture", NULL, "ADDA_CH34_FIFO"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_EN"},
+
+ {"AP DMIC Capture", NULL, "AP_DMIC_INPUT"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_INPUT"},
+
+ /* sidetone filter */
+ {"STF_ADDA_MUX", "ADDA", "ADDA_UL_Mux"},
+ {"STF_ADDA_MUX", "ADDA6", "ADDA_CH34_UL_Mux"},
+
+ {"STF_O19O20_MUX", "ADDA_ADDA6", "STF_ADDA_MUX"},
+ {"STF_O19O20_MUX", "O19O20", "STF_CH1"},
+ {"STF_O19O20_MUX", "O19O20", "STF_CH2"},
+
+ {"Sidetone Filter", "Switch", "STF_O19O20_MUX"},
+ {"STF_OUTPUT", NULL, "Sidetone Filter"},
+ {"ADDA Playback", NULL, "Sidetone Filter"},
+ {"ADDA CH34 Playback", NULL, "Sidetone Filter"},
+
+ /* clk */
+ {"ADDA Playback", NULL, "aud_dac_clk"},
+ {"ADDA Playback", NULL, "aud_dac_predis_clk"},
+
+ {"ADDA CH34 Playback", NULL, "aud_3rd_dac_clk"},
+ {"ADDA CH34 Playback", NULL, "aud_3rd_dac_predis_clk"},
+
+ {"ADDA Capture Enable", NULL, "aud_adc_clk"},
+ {"ADDA CH34 Capture Enable", NULL, "aud_adda6_adc_clk"},
+};
+
+/* dai ops */
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ int id = dai->id;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int dl_src2_con0 = 0;
+ unsigned int dl_src2_con1 = 0;
+
+ /* set sampling rate */
+ dl_src2_con0 = mtk_adda_dl_rate_transform(afe, rate) <<
+ DL_2_INPUT_MODE_CTL_SFT;
+
+ /* set output mode, UP_SAMPLING_RATE_X8 */
+ dl_src2_con0 |= (0x3 << DL_2_OUTPUT_SEL_CTL_SFT);
+
+ /* turn off mute function */
+ dl_src2_con0 |= (0x01 << DL_2_MUTE_CH2_OFF_CTL_PRE_SFT);
+ dl_src2_con0 |= (0x01 << DL_2_MUTE_CH1_OFF_CTL_PRE_SFT);
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ if (rate == 8000 || rate == 16000)
+ dl_src2_con0 |= 0x01 << DL_2_VOICE_MODE_CTL_PRE_SFT;
+
+ /* SA suggest apply -0.3db to audio/speech path */
+ dl_src2_con1 = MTK_AFE_ADDA_DL_GAIN_NORMAL <<
+ DL_2_GAIN_CTL_PRE_SFT;
+
+ /* turn on down-link gain */
+ dl_src2_con0 |= (0x01 << DL_2_GAIN_ON_CTL_PRE_SFT);
+
+ if (id == MT8192_DAI_ADDA) {
+ /* clean predistortion */
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SRC2_CON0, dl_src2_con0);
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SRC2_CON1, dl_src2_con1);
+
+ /* set sdm gain */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ ATTGAIN_CTL_MASK_SFT,
+ AUDIO_SDM_LEVEL_NORMAL <<
+ ATTGAIN_CTL_SFT);
+
+ /* 2nd sdm */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_DCCOMP_CON,
+ USE_3RD_SDM_MASK_SFT,
+ AUDIO_SDM_2ND << USE_3RD_SDM_SFT);
+
+ /* sdm auto reset */
+ regmap_write(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ SDM_AUTO_RESET_THRESHOLD);
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_DL_SDM_AUTO_RESET_CON,
+ ADDA_SDM_AUTO_RESET_ONOFF_MASK_SFT,
+ 0x1 << ADDA_SDM_AUTO_RESET_ONOFF_SFT);
+ } else {
+ /* clean predistortion */
+ regmap_write(afe->regmap,
+ AFE_ADDA_3RD_DAC_PREDIS_CON0, 0);
+ regmap_write(afe->regmap,
+ AFE_ADDA_3RD_DAC_PREDIS_CON1, 0);
+
+ regmap_write(afe->regmap, AFE_ADDA_3RD_DAC_DL_SRC2_CON0,
+ dl_src2_con0);
+ regmap_write(afe->regmap, AFE_ADDA_3RD_DAC_DL_SRC2_CON1,
+ dl_src2_con1);
+
+ /* set sdm gain */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_3RD_DAC_DL_SDM_DCCOMP_CON,
+ ATTGAIN_CTL_MASK_SFT,
+ AUDIO_SDM_LEVEL_NORMAL <<
+ ATTGAIN_CTL_SFT);
+
+ /* 2nd sdm */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_3RD_DAC_DL_SDM_DCCOMP_CON,
+ USE_3RD_SDM_MASK_SFT,
+ AUDIO_SDM_2ND << USE_3RD_SDM_SFT);
+
+ /* sdm auto reset */
+ regmap_write(afe->regmap,
+ AFE_ADDA_3RD_DAC_DL_SDM_AUTO_RESET_CON,
+ SDM_AUTO_RESET_THRESHOLD);
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_3RD_DAC_DL_SDM_AUTO_RESET_CON,
+ ADDA_3RD_DAC_SDM_AUTO_RESET_ONOFF_MASK_SFT,
+ 0x1 << ADDA_3RD_DAC_SDM_AUTO_RESET_ONOFF_SFT);
+ }
+ } else {
+ unsigned int voice_mode = 0;
+ unsigned int ul_src_con0 = 0; /* default value */
+
+ voice_mode = mtk_adda_ul_rate_transform(afe, rate);
+
+ ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
+
+ /* enable iir */
+ ul_src_con0 |= (1 << UL_IIR_ON_TMP_CTL_SFT) &
+ UL_IIR_ON_TMP_CTL_MASK_SFT;
+ ul_src_con0 |= (UL_IIR_SW << UL_IIRMODE_CTL_SFT) &
+ UL_IIRMODE_CTL_MASK_SFT;
+
+ switch (id) {
+ case MT8192_DAI_ADDA:
+ case MT8192_DAI_AP_DMIC:
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL_SRC_CON0, ul_src_con0);
+
+ /* Using Internal ADC */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_TOP_CON0,
+ 0x1 << 0,
+ 0x0 << 0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_RX_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+ break;
+ case MT8192_DAI_ADDA_CH34:
+ case MT8192_DAI_AP_DMIC_CH34:
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA6_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA6_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA6_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA6_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA6_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA6_UL_SRC_CON0, ul_src_con0);
+
+ /* Using Internal ADC */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA6_TOP_CON0,
+ 0x1 << 0,
+ 0x0 << 0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA6_MTKAIF_RX_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+ break;
+ default:
+ break;
+ }
+
+ /* ap dmic */
+ switch (id) {
+ case MT8192_DAI_AP_DMIC:
+ case MT8192_DAI_AP_DMIC_CH34:
+ mtk_adda_ul_src_dmic(afe, id);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8192_DAI_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "ADDA_CH34",
+ .id = MT8192_DAI_ADDA_CH34,
+ .playback = {
+ .stream_name = "ADDA CH34 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA CH34 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC",
+ .id = MT8192_DAI_AP_DMIC,
+ .capture = {
+ .stream_name = "AP DMIC Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC_CH34",
+ .id = MT8192_DAI_AP_DMIC_CH34,
+ .capture = {
+ .stream_name = "AP DMIC CH34 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+int mt8192_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->controls = mtk_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+
+ /* ap dmic priv share with adda */
+ afe_priv->dai_priv[MT8192_DAI_AP_DMIC] =
+ afe_priv->dai_priv[MT8192_DAI_ADDA];
+ afe_priv->dai_priv[MT8192_DAI_AP_DMIC_CH34] =
+ afe_priv->dai_priv[MT8192_DAI_ADDA_CH34];
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c b/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c
new file mode 100644
index 000000000000..1632fc94776d
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c
@@ -0,0 +1,2101 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+
+#include "mt8192-afe-clk.h"
+#include "mt8192-afe-common.h"
+#include "mt8192-afe-gpio.h"
+#include "mt8192-interconnection.h"
+
+enum {
+ I2S_FMT_EIAJ = 0,
+ I2S_FMT_I2S = 1,
+};
+
+enum {
+ I2S_WLEN_16_BIT = 0,
+ I2S_WLEN_32_BIT = 1,
+};
+
+enum {
+ I2S_HD_NORMAL = 0,
+ I2S_HD_LOW_JITTER = 1,
+};
+
+enum {
+ I2S1_SEL_O28_O29 = 0,
+ I2S1_SEL_O03_O04 = 1,
+};
+
+enum {
+ I2S_IN_PAD_CONNSYS = 0,
+ I2S_IN_PAD_IO_MUX = 1,
+};
+
+struct mtk_afe_i2s_priv {
+ int id;
+ int rate; /* for determine which apll to use */
+ int low_jitter_en;
+
+ int share_i2s_id;
+
+ int mclk_id;
+ int mclk_rate;
+ int mclk_apll;
+};
+
+static unsigned int get_i2s_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ I2S_WLEN_16_BIT : I2S_WLEN_32_BIT;
+}
+
+#define MTK_AFE_I2S0_KCONTROL_NAME "I2S0_HD_Mux"
+#define MTK_AFE_I2S1_KCONTROL_NAME "I2S1_HD_Mux"
+#define MTK_AFE_I2S2_KCONTROL_NAME "I2S2_HD_Mux"
+#define MTK_AFE_I2S3_KCONTROL_NAME "I2S3_HD_Mux"
+#define MTK_AFE_I2S5_KCONTROL_NAME "I2S5_HD_Mux"
+#define MTK_AFE_I2S6_KCONTROL_NAME "I2S6_HD_Mux"
+#define MTK_AFE_I2S7_KCONTROL_NAME "I2S7_HD_Mux"
+#define MTK_AFE_I2S8_KCONTROL_NAME "I2S8_HD_Mux"
+#define MTK_AFE_I2S9_KCONTROL_NAME "I2S9_HD_Mux"
+
+#define I2S0_HD_EN_W_NAME "I2S0_HD_EN"
+#define I2S1_HD_EN_W_NAME "I2S1_HD_EN"
+#define I2S2_HD_EN_W_NAME "I2S2_HD_EN"
+#define I2S3_HD_EN_W_NAME "I2S3_HD_EN"
+#define I2S5_HD_EN_W_NAME "I2S5_HD_EN"
+#define I2S6_HD_EN_W_NAME "I2S6_HD_EN"
+#define I2S7_HD_EN_W_NAME "I2S7_HD_EN"
+#define I2S8_HD_EN_W_NAME "I2S8_HD_EN"
+#define I2S9_HD_EN_W_NAME "I2S9_HD_EN"
+
+#define I2S0_MCLK_EN_W_NAME "I2S0_MCLK_EN"
+#define I2S1_MCLK_EN_W_NAME "I2S1_MCLK_EN"
+#define I2S2_MCLK_EN_W_NAME "I2S2_MCLK_EN"
+#define I2S3_MCLK_EN_W_NAME "I2S3_MCLK_EN"
+#define I2S5_MCLK_EN_W_NAME "I2S5_MCLK_EN"
+#define I2S6_MCLK_EN_W_NAME "I2S6_MCLK_EN"
+#define I2S7_MCLK_EN_W_NAME "I2S7_MCLK_EN"
+#define I2S8_MCLK_EN_W_NAME "I2S8_MCLK_EN"
+#define I2S9_MCLK_EN_W_NAME "I2S9_MCLK_EN"
+
+static int get_i2s_id_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ if (strncmp(name, "I2S0", 4) == 0)
+ return MT8192_DAI_I2S_0;
+ else if (strncmp(name, "I2S1", 4) == 0)
+ return MT8192_DAI_I2S_1;
+ else if (strncmp(name, "I2S2", 4) == 0)
+ return MT8192_DAI_I2S_2;
+ else if (strncmp(name, "I2S3", 4) == 0)
+ return MT8192_DAI_I2S_3;
+ else if (strncmp(name, "I2S5", 4) == 0)
+ return MT8192_DAI_I2S_5;
+ else if (strncmp(name, "I2S6", 4) == 0)
+ return MT8192_DAI_I2S_6;
+ else if (strncmp(name, "I2S7", 4) == 0)
+ return MT8192_DAI_I2S_7;
+ else if (strncmp(name, "I2S8", 4) == 0)
+ return MT8192_DAI_I2S_8;
+ else if (strncmp(name, "I2S9", 4) == 0)
+ return MT8192_DAI_I2S_9;
+ else
+ return -EINVAL;
+}
+
+static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_i2s_id_by_name(afe, name);
+
+ if (dai_id < 0)
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+/* low jitter control */
+static const char * const mt8192_i2s_hd_str[] = {
+ "Normal", "Low_Jitter"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(mt8192_i2s_enum, mt8192_i2s_hd_str);
+
+static int mt8192_i2s_hd_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mt8192_i2s_hd_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int hd_en;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ hd_en = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, hd_en %d\n",
+ __func__, kcontrol->id.name, hd_en);
+
+ i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ i2s_priv->low_jitter_en = hd_en;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
+ SOC_ENUM_EXT(MTK_AFE_I2S0_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S1_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S2_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S3_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S5_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S6_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S7_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S8_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+ SOC_ENUM_EXT(MTK_AFE_I2S9_KCONTROL_NAME, mt8192_i2s_enum,
+ mt8192_i2s_hd_get, mt8192_i2s_hd_set),
+};
+
+/* dai component */
+/* i2s virtual mux to output widget */
+static const char * const i2s_mux_map[] = {
+ "Normal", "Dummy_Widget",
+};
+
+static int i2s_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ i2s_mux_map,
+ i2s_mux_map_value);
+
+static const struct snd_kcontrol_new i2s0_in_mux_control =
+ SOC_DAPM_ENUM("I2S0 In Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s8_in_mux_control =
+ SOC_DAPM_ENUM("I2S8 In Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s1_out_mux_control =
+ SOC_DAPM_ENUM("I2S1 Out Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s3_out_mux_control =
+ SOC_DAPM_ENUM("I2S3 Out Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s5_out_mux_control =
+ SOC_DAPM_ENUM("I2S5 Out Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s7_out_mux_control =
+ SOC_DAPM_ENUM("I2S7 Out Select", i2s_mux_map_enum);
+
+static const struct snd_kcontrol_new i2s9_out_mux_control =
+ SOC_DAPM_ENUM("I2S9 Out Select", i2s_mux_map_enum);
+
+/* Tinyconn Mux */
+enum {
+ TINYCONN_CH1_MUX_DL1 = 0x0,
+ TINYCONN_CH2_MUX_DL1 = 0x1,
+ TINYCONN_CH1_MUX_DL12 = 0x2,
+ TINYCONN_CH2_MUX_DL12 = 0x3,
+ TINYCONN_CH1_MUX_DL2 = 0x4,
+ TINYCONN_CH2_MUX_DL2 = 0x5,
+ TINYCONN_CH1_MUX_DL3 = 0x6,
+ TINYCONN_CH2_MUX_DL3 = 0x7,
+ TINYCONN_MUX_NONE = 0x1f,
+};
+
+static const char * const tinyconn_mux_map[] = {
+ "NONE",
+ "DL1_CH1",
+ "DL1_CH2",
+ "DL12_CH1",
+ "DL12_CH2",
+ "DL2_CH1",
+ "DL2_CH2",
+ "DL3_CH1",
+ "DL3_CH2",
+};
+
+static int tinyconn_mux_map_value[] = {
+ TINYCONN_MUX_NONE,
+ TINYCONN_CH1_MUX_DL1,
+ TINYCONN_CH2_MUX_DL1,
+ TINYCONN_CH1_MUX_DL12,
+ TINYCONN_CH2_MUX_DL12,
+ TINYCONN_CH1_MUX_DL2,
+ TINYCONN_CH2_MUX_DL2,
+ TINYCONN_CH1_MUX_DL3,
+ TINYCONN_CH2_MUX_DL3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(i2s1_tinyconn_ch1_mux_map_enum,
+ AFE_TINY_CONN5,
+ O_20_CFG_SFT,
+ O_20_CFG_MASK,
+ tinyconn_mux_map,
+ tinyconn_mux_map_value);
+static const struct snd_kcontrol_new i2s1_tinyconn_ch1_mux_control =
+ SOC_DAPM_ENUM("i2s1 ch1 tinyconn Select",
+ i2s1_tinyconn_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(i2s1_tinyconn_ch2_mux_map_enum,
+ AFE_TINY_CONN5,
+ O_21_CFG_SFT,
+ O_21_CFG_MASK,
+ tinyconn_mux_map,
+ tinyconn_mux_map_value);
+static const struct snd_kcontrol_new i2s1_tinyconn_ch2_mux_control =
+ SOC_DAPM_ENUM("i2s1 ch2 tinyconn Select",
+ i2s1_tinyconn_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(i2s3_tinyconn_ch1_mux_map_enum,
+ AFE_TINY_CONN5,
+ O_22_CFG_SFT,
+ O_22_CFG_MASK,
+ tinyconn_mux_map,
+ tinyconn_mux_map_value);
+static const struct snd_kcontrol_new i2s3_tinyconn_ch1_mux_control =
+ SOC_DAPM_ENUM("i2s3 ch1 tinyconn Select",
+ i2s3_tinyconn_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(i2s3_tinyconn_ch2_mux_map_enum,
+ AFE_TINY_CONN5,
+ O_23_CFG_SFT,
+ O_23_CFG_MASK,
+ tinyconn_mux_map,
+ tinyconn_mux_map_value);
+static const struct snd_kcontrol_new i2s3_tinyconn_ch2_mux_control =
+ SOC_DAPM_ENUM("i2s3 ch2 tinyconn Select",
+ i2s3_tinyconn_ch2_mux_map_enum);
+
+/* i2s in lpbk */
+static const char * const i2s_lpbk_mux_map[] = {
+ "Normal", "Lpbk",
+};
+
+static int i2s_lpbk_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s0_lpbk_mux_map_enum,
+ AFE_I2S_CON,
+ I2S_LOOPBACK_SFT,
+ 1,
+ i2s_lpbk_mux_map,
+ i2s_lpbk_mux_map_value);
+
+static const struct snd_kcontrol_new i2s0_lpbk_mux_control =
+ SOC_DAPM_ENUM("I2S Lpbk Select", i2s0_lpbk_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s2_lpbk_mux_map_enum,
+ AFE_I2S_CON2,
+ I2S3_LOOPBACK_SFT,
+ 1,
+ i2s_lpbk_mux_map,
+ i2s_lpbk_mux_map_value);
+
+static const struct snd_kcontrol_new i2s2_lpbk_mux_control =
+ SOC_DAPM_ENUM("I2S Lpbk Select", i2s2_lpbk_mux_map_enum);
+
+/* interconnection */
+static const struct snd_kcontrol_new mtk_i2s3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN0, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN0, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN0, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN0, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN0_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN0_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN0_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN0_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH1", AFE_CONN0_1, I_DL9_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN0,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN0,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN0,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN1, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN1, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN1, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN1, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN1_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN1_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN1_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN1_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH2", AFE_CONN1_1, I_DL9_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN1,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN1,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN1,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN1,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN1,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN1,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN1,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN1,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN28, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN28, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN28, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN28, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN28_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN28_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN28_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN28_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH1", AFE_CONN28_1, I_DL9_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN28,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN28,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN28,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN28,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN29, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN29, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN29, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN29, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN29_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN29_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN29_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN29_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH2", AFE_CONN29_1, I_DL9_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN29,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN29,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN29,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN29,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN29,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN29,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s5_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN30, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN30, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN30, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN30, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN30_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN30_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN30_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN30_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH1", AFE_CONN30_1, I_DL9_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN30,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN30,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN30,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN30,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s5_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN31, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN31, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN31, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN31, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN31_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN31_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN31_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN31_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH2", AFE_CONN31_1, I_DL9_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN31,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN31,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN31,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN31,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN31,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN31,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s7_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN54, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN54, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN54, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN54, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN54_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN54_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN54_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH1", AFE_CONN54_1, I_DL9_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN54,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN54,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN54,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN54,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s7_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN55, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN55, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN55, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN55, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN55_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN55_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN55_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH2", AFE_CONN55_1, I_DL9_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN55,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN55,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN55,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN55,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN55,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN55,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s9_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN56, I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN56, I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN56, I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH1", AFE_CONN56, I_DL12_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN56_1, I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN56_1, I_DL4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH1", AFE_CONN56_1, I_DL5_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH1", AFE_CONN56_1, I_DL8_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH1", AFE_CONN56_1, I_DL9_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1", AFE_CONN56,
+ I_GAIN1_OUT_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN56,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN56,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN56,
+ I_PCM_2_CAP_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_i2s9_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN57, I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN57, I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN57, I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL12_CH2", AFE_CONN57, I_DL12_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN57_1, I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN57_1, I_DL4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL5_CH2", AFE_CONN57_1, I_DL5_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL8_CH2", AFE_CONN57_1, I_DL8_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL9_CH2", AFE_CONN57_1, I_DL9_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2", AFE_CONN57,
+ I_GAIN1_OUT_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN57,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN57,
+ I_PCM_1_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN57,
+ I_PCM_2_CAP_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN57,
+ I_PCM_1_CAP_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN57,
+ I_PCM_2_CAP_CH2, 1, 0),
+};
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_I2S_MCLK_EN,
+ SUPPLY_SEQ_I2S_HD_EN,
+ SUPPLY_SEQ_I2S_EN,
+};
+
+static int mtk_i2s_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_afe_gpio_request(afe->dev, true, i2s_priv->id, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8192_afe_gpio_request(afe->dev, false, i2s_priv->id, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8192_apll1_enable(afe);
+ else
+ mt8192_apll2_enable(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
+ mt8192_apll1_disable(afe);
+ else
+ mt8192_apll2_disable(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int i2s_out_tinyconn_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int reg;
+ unsigned int reg_shift;
+ unsigned int reg_mask_shift;
+
+ dev_dbg(afe->dev, "%s(), event 0x%x\n", __func__, event);
+
+ if (strstr(w->name, "I2S1")) {
+ reg = AFE_I2S_CON1;
+ reg_shift = I2S2_32BIT_EN_SFT;
+ reg_mask_shift = I2S2_32BIT_EN_MASK_SFT;
+ } else if (strstr(w->name, "I2S3")) {
+ reg = AFE_I2S_CON3;
+ reg_shift = I2S4_32BIT_EN_SFT;
+ reg_mask_shift = I2S4_32BIT_EN_MASK_SFT;
+ } else if (strstr(w->name, "I2S5")) {
+ reg = AFE_I2S_CON4;
+ reg_shift = I2S5_32BIT_EN_SFT;
+ reg_mask_shift = I2S5_32BIT_EN_MASK_SFT;
+ } else if (strstr(w->name, "I2S7")) {
+ reg = AFE_I2S_CON7;
+ reg_shift = I2S7_32BIT_EN_SFT;
+ reg_mask_shift = I2S7_32BIT_EN_MASK_SFT;
+ } else if (strstr(w->name, "I2S9")) {
+ reg = AFE_I2S_CON9;
+ reg_shift = I2S9_32BIT_EN_SFT;
+ reg_mask_shift = I2S9_32BIT_EN_MASK_SFT;
+ } else {
+ reg = AFE_I2S_CON1;
+ reg_shift = I2S2_32BIT_EN_SFT;
+ reg_mask_shift = I2S2_32BIT_EN_MASK_SFT;
+ dev_warn(afe->dev, "%s(), error widget name %s, use i2s1\n",
+ __func__, w->name);
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(afe->regmap, reg, reg_mask_shift,
+ 0x1 << reg_shift);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(afe->regmap, reg, reg_mask_shift,
+ 0x0 << reg_shift);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ i2s_priv->mclk_rate = 0;
+ mt8192_mck_disable(afe, i2s_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
+ SND_SOC_DAPM_INPUT("CONNSYS"),
+
+ SND_SOC_DAPM_MIXER("I2S1_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s1_ch1_mix,
+ ARRAY_SIZE(mtk_i2s1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S1_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s1_ch2_mix,
+ ARRAY_SIZE(mtk_i2s1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S3_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s3_ch1_mix,
+ ARRAY_SIZE(mtk_i2s3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S3_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s3_ch2_mix,
+ ARRAY_SIZE(mtk_i2s3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S5_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s5_ch1_mix,
+ ARRAY_SIZE(mtk_i2s5_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S5_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s5_ch2_mix,
+ ARRAY_SIZE(mtk_i2s5_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S7_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s7_ch1_mix,
+ ARRAY_SIZE(mtk_i2s7_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S7_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s7_ch2_mix,
+ ARRAY_SIZE(mtk_i2s7_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("I2S9_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_i2s9_ch1_mix,
+ ARRAY_SIZE(mtk_i2s9_ch1_mix)),
+ SND_SOC_DAPM_MIXER("I2S9_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_i2s9_ch2_mix,
+ ARRAY_SIZE(mtk_i2s9_ch2_mix)),
+
+ SND_SOC_DAPM_MUX_E("I2S1_TINYCONN_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &i2s1_tinyconn_ch1_mux_control,
+ i2s_out_tinyconn_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("I2S1_TINYCONN_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &i2s1_tinyconn_ch2_mux_control,
+ i2s_out_tinyconn_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("I2S3_TINYCONN_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &i2s3_tinyconn_ch1_mux_control,
+ i2s_out_tinyconn_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("I2S3_TINYCONN_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &i2s3_tinyconn_ch2_mux_control,
+ i2s_out_tinyconn_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* i2s en*/
+ SND_SOC_DAPM_SUPPLY_S("I2S0_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S1_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON1, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S2_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON2, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S3_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON3, I2S_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S5_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON4, I2S5_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S6_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON6, I2S6_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S7_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON7, I2S7_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S8_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON8, I2S8_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("I2S9_EN", SUPPLY_SEQ_I2S_EN,
+ AFE_I2S_CON9, I2S9_EN_SFT, 0,
+ mtk_i2s_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /* i2s hd en */
+ SND_SOC_DAPM_SUPPLY_S(I2S0_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON, I2S1_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S1_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON1, I2S2_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S2_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON2, I2S3_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S3_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON3, I2S4_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S5_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON4, I2S5_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S6_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON6, I2S6_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S7_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON7, I2S7_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S8_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON8, I2S8_HD_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S(I2S9_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
+ AFE_I2S_CON9, I2S9_HD_EN_SFT, 0, NULL, 0),
+
+ /* i2s mclk en */
+ SND_SOC_DAPM_SUPPLY_S(I2S0_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S1_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S2_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S3_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S5_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S6_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S7_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S8_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(I2S9_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_mclk_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* apll */
+ SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* allow i2s on without codec on */
+ SND_SOC_DAPM_OUTPUT("I2S_DUMMY_OUT"),
+ SND_SOC_DAPM_MUX("I2S1_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s1_out_mux_control),
+ SND_SOC_DAPM_MUX("I2S3_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s3_out_mux_control),
+ SND_SOC_DAPM_MUX("I2S5_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s5_out_mux_control),
+ SND_SOC_DAPM_MUX("I2S7_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s7_out_mux_control),
+ SND_SOC_DAPM_MUX("I2S9_Out_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s9_out_mux_control),
+
+ SND_SOC_DAPM_INPUT("I2S_DUMMY_IN"),
+ SND_SOC_DAPM_MUX("I2S0_In_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s0_in_mux_control),
+ SND_SOC_DAPM_MUX("I2S8_In_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s8_in_mux_control),
+
+ /* i2s in lpbk */
+ SND_SOC_DAPM_MUX("I2S0_Lpbk_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s0_lpbk_mux_control),
+ SND_SOC_DAPM_MUX("I2S2_Lpbk_Mux",
+ SND_SOC_NOPM, 0, 0, &i2s2_lpbk_mux_control),
+};
+
+static int mtk_afe_i2s_share_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ return i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name);
+}
+
+static int mtk_afe_i2s_hd_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ if (get_i2s_id_by_name(afe, sink->name) ==
+ get_i2s_id_by_name(afe, source->name))
+ return i2s_priv->low_jitter_en;
+
+ /* check if share i2s need hd en */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
+ return i2s_priv->low_jitter_en;
+
+ return 0;
+}
+
+static int mtk_afe_i2s_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+ int i2s_need_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ /* which apll */
+ cur_apll = mt8192_get_apll_by_name(afe, source->name);
+
+ /* choose APLL from i2s rate */
+ i2s_need_apll = mt8192_get_apll_by_rate(afe, i2s_priv->rate);
+
+ if (i2s_need_apll == cur_apll)
+ return 1;
+
+ return 0;
+}
+
+static int mtk_afe_i2s_mclk_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+
+ i2s_priv = get_i2s_priv_by_name(afe, sink->name);
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ if (get_i2s_id_by_name(afe, sink->name) ==
+ get_i2s_id_by_name(afe, source->name))
+ return (i2s_priv->mclk_rate > 0) ? 1 : 0;
+
+ /* check if share i2s need mclk */
+ if (i2s_priv->share_i2s_id < 0)
+ return 0;
+
+ if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
+ return (i2s_priv->mclk_rate > 0) ? 1 : 0;
+
+ return 0;
+}
+
+static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_afe_i2s_priv *i2s_priv;
+ int cur_apll;
+
+ i2s_priv = get_i2s_priv_by_name(afe, w->name);
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return 0;
+ }
+
+ /* which apll */
+ cur_apll = mt8192_get_apll_by_name(afe, source->name);
+
+ if (i2s_priv->mclk_apll == cur_apll)
+ return 1;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
+ {"Connsys I2S", NULL, "CONNSYS"},
+
+ /* i2s0 */
+ {"I2S0", NULL, "I2S0_EN"},
+ {"I2S0", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S0", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S0", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S0", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S0_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S0_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S0", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S0", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S0_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S0_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s1 */
+ {"I2S1_CH1", "DL1_CH1", "DL1"},
+ {"I2S1_CH2", "DL1_CH2", "DL1"},
+ {"I2S1_TINYCONN_CH1_MUX", "DL1_CH1", "DL1"},
+ {"I2S1_TINYCONN_CH2_MUX", "DL1_CH2", "DL1"},
+
+ {"I2S1_CH1", "DL2_CH1", "DL2"},
+ {"I2S1_CH2", "DL2_CH2", "DL2"},
+ {"I2S1_TINYCONN_CH1_MUX", "DL2_CH1", "DL2"},
+ {"I2S1_TINYCONN_CH2_MUX", "DL2_CH2", "DL2"},
+
+ {"I2S1_CH1", "DL3_CH1", "DL3"},
+ {"I2S1_CH2", "DL3_CH2", "DL3"},
+ {"I2S1_TINYCONN_CH1_MUX", "DL3_CH1", "DL3"},
+ {"I2S1_TINYCONN_CH2_MUX", "DL3_CH2", "DL3"},
+
+ {"I2S1_CH1", "DL12_CH1", "DL12"},
+ {"I2S1_CH2", "DL12_CH2", "DL12"},
+ {"I2S1_TINYCONN_CH1_MUX", "DL12_CH1", "DL12"},
+ {"I2S1_TINYCONN_CH2_MUX", "DL12_CH2", "DL12"},
+
+ {"I2S1_CH1", "DL4_CH1", "DL4"},
+ {"I2S1_CH2", "DL4_CH2", "DL4"},
+
+ {"I2S1_CH1", "DL5_CH1", "DL5"},
+ {"I2S1_CH2", "DL5_CH2", "DL5"},
+
+ {"I2S1_CH1", "DL6_CH1", "DL6"},
+ {"I2S1_CH2", "DL6_CH2", "DL6"},
+
+ {"I2S1_CH1", "DL8_CH1", "DL8"},
+ {"I2S1_CH2", "DL8_CH2", "DL8"},
+
+ {"I2S1", NULL, "I2S1_CH1"},
+ {"I2S1", NULL, "I2S1_CH2"},
+ {"I2S1", NULL, "I2S3_TINYCONN_CH1_MUX"},
+ {"I2S1", NULL, "I2S3_TINYCONN_CH2_MUX"},
+
+ {"I2S1", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S1_EN"},
+ {"I2S1", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S1", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S1", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S1", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S1_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S1_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S1", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S1", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S1_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S1_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s2 */
+ {"I2S2", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S2_EN"},
+ {"I2S2", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S2", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S2", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S2", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S2_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S2_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S2", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S2", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S2_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S2_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s3 */
+ {"I2S3_CH1", "DL1_CH1", "DL1"},
+ {"I2S3_CH2", "DL1_CH2", "DL1"},
+ {"I2S3_TINYCONN_CH1_MUX", "DL1_CH1", "DL1"},
+ {"I2S3_TINYCONN_CH2_MUX", "DL1_CH2", "DL1"},
+
+ {"I2S3_CH1", "DL2_CH1", "DL2"},
+ {"I2S3_CH2", "DL2_CH2", "DL2"},
+ {"I2S3_TINYCONN_CH1_MUX", "DL2_CH1", "DL2"},
+ {"I2S3_TINYCONN_CH2_MUX", "DL2_CH2", "DL2"},
+
+ {"I2S3_CH1", "DL3_CH1", "DL3"},
+ {"I2S3_CH2", "DL3_CH2", "DL3"},
+ {"I2S3_TINYCONN_CH1_MUX", "DL3_CH1", "DL3"},
+ {"I2S3_TINYCONN_CH2_MUX", "DL3_CH2", "DL3"},
+
+ {"I2S3_CH1", "DL12_CH1", "DL12"},
+ {"I2S3_CH2", "DL12_CH2", "DL12"},
+ {"I2S3_TINYCONN_CH1_MUX", "DL12_CH1", "DL12"},
+ {"I2S3_TINYCONN_CH2_MUX", "DL12_CH2", "DL12"},
+
+ {"I2S3_CH1", "DL4_CH1", "DL4"},
+ {"I2S3_CH2", "DL4_CH2", "DL4"},
+
+ {"I2S3_CH1", "DL5_CH1", "DL5"},
+ {"I2S3_CH2", "DL5_CH2", "DL5"},
+
+ {"I2S3_CH1", "DL6_CH1", "DL6"},
+ {"I2S3_CH2", "DL6_CH2", "DL6"},
+
+ {"I2S3_CH1", "DL8_CH1", "DL8"},
+ {"I2S3_CH2", "DL8_CH2", "DL8"},
+
+ {"I2S3", NULL, "I2S3_CH1"},
+ {"I2S3", NULL, "I2S3_CH2"},
+ {"I2S3", NULL, "I2S3_TINYCONN_CH1_MUX"},
+ {"I2S3", NULL, "I2S3_TINYCONN_CH2_MUX"},
+
+ {"I2S3", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S3_EN"},
+ {"I2S3", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S3", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S3", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S3", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S3_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S3_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S3", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S3", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S3_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S3_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s5 */
+ {"I2S5_CH1", "DL1_CH1", "DL1"},
+ {"I2S5_CH2", "DL1_CH2", "DL1"},
+
+ {"I2S5_CH1", "DL2_CH1", "DL2"},
+ {"I2S5_CH2", "DL2_CH2", "DL2"},
+
+ {"I2S5_CH1", "DL3_CH1", "DL3"},
+ {"I2S5_CH2", "DL3_CH2", "DL3"},
+
+ {"I2S5_CH1", "DL12_CH1", "DL12"},
+ {"I2S5_CH2", "DL12_CH2", "DL12"},
+
+ {"I2S5_CH1", "DL4_CH1", "DL4"},
+ {"I2S5_CH2", "DL4_CH2", "DL4"},
+
+ {"I2S5_CH1", "DL5_CH1", "DL5"},
+ {"I2S5_CH2", "DL5_CH2", "DL5"},
+
+ {"I2S5_CH1", "DL6_CH1", "DL6"},
+ {"I2S5_CH2", "DL6_CH2", "DL6"},
+
+ {"I2S5_CH1", "DL8_CH1", "DL8"},
+ {"I2S5_CH2", "DL8_CH2", "DL8"},
+
+ {"I2S5", NULL, "I2S5_CH1"},
+ {"I2S5", NULL, "I2S5_CH2"},
+
+ {"I2S5", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S5_EN"},
+ {"I2S5", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S5", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S5", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S5", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S5_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S5_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S5", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S5", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S5_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S5_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s6 */
+ {"I2S6", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S6_EN"},
+ {"I2S6", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S6", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S6", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S6", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S6_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S6_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S6", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S6", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S6_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S6_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s7 */
+ {"I2S7", NULL, "I2S7_CH1"},
+ {"I2S7", NULL, "I2S7_CH2"},
+
+ {"I2S7", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S7_EN"},
+ {"I2S7", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S7", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S7", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S7", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S7_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S7_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S7", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S7", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S7_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S7_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s8 */
+ {"I2S8", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S8", NULL, "I2S8_EN"},
+ {"I2S8", NULL, "I2S9_EN", mtk_afe_i2s_share_connect},
+
+ {"I2S8", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S8", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S8_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S8_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S8", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S8", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S8_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S8_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* i2s9 */
+ {"I2S9_CH1", "DL1_CH1", "DL1"},
+ {"I2S9_CH2", "DL1_CH2", "DL1"},
+
+ {"I2S9_CH1", "DL2_CH1", "DL2"},
+ {"I2S9_CH2", "DL2_CH2", "DL2"},
+
+ {"I2S9_CH1", "DL3_CH1", "DL3"},
+ {"I2S9_CH2", "DL3_CH2", "DL3"},
+
+ {"I2S9_CH1", "DL12_CH1", "DL12"},
+ {"I2S9_CH2", "DL12_CH2", "DL12"},
+
+ {"I2S9_CH1", "DL4_CH1", "DL4"},
+ {"I2S9_CH2", "DL4_CH2", "DL4"},
+
+ {"I2S9_CH1", "DL5_CH1", "DL5"},
+ {"I2S9_CH2", "DL5_CH2", "DL5"},
+
+ {"I2S9_CH1", "DL6_CH1", "DL6"},
+ {"I2S9_CH2", "DL6_CH2", "DL6"},
+
+ {"I2S9_CH1", "DL8_CH1", "DL8"},
+ {"I2S9_CH2", "DL8_CH2", "DL8"},
+
+ {"I2S9_CH1", "DL9_CH1", "DL9"},
+ {"I2S9_CH2", "DL9_CH2", "DL9"},
+
+ {"I2S9", NULL, "I2S9_CH1"},
+ {"I2S9", NULL, "I2S9_CH2"},
+
+ {"I2S9", NULL, "I2S0_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S1_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S2_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S3_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S5_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S6_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S7_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S8_EN", mtk_afe_i2s_share_connect},
+ {"I2S9", NULL, "I2S9_EN"},
+
+ {"I2S9", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S6_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S7_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S8_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {"I2S9", NULL, I2S9_HD_EN_W_NAME, mtk_afe_i2s_hd_connect},
+ {I2S9_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect},
+ {I2S9_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect},
+
+ {"I2S9", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S6_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S7_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S8_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {"I2S9", NULL, I2S9_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect},
+ {I2S9_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {I2S9_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* allow i2s on without codec on */
+ {"I2S0", NULL, "I2S0_In_Mux"},
+ {"I2S0_In_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
+
+ {"I2S8", NULL, "I2S8_In_Mux"},
+ {"I2S8_In_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
+
+ {"I2S1_Out_Mux", "Dummy_Widget", "I2S1"},
+ {"I2S_DUMMY_OUT", NULL, "I2S1_Out_Mux"},
+
+ {"I2S3_Out_Mux", "Dummy_Widget", "I2S3"},
+ {"I2S_DUMMY_OUT", NULL, "I2S3_Out_Mux"},
+
+ {"I2S5_Out_Mux", "Dummy_Widget", "I2S5"},
+ {"I2S_DUMMY_OUT", NULL, "I2S5_Out_Mux"},
+
+ {"I2S7_Out_Mux", "Dummy_Widget", "I2S7"},
+ {"I2S_DUMMY_OUT", NULL, "I2S7_Out_Mux"},
+
+ {"I2S9_Out_Mux", "Dummy_Widget", "I2S9"},
+ {"I2S_DUMMY_OUT", NULL, "I2S9_Out_Mux"},
+
+ /* i2s in lpbk */
+ {"I2S0_Lpbk_Mux", "Lpbk", "I2S3"},
+ {"I2S2_Lpbk_Mux", "Lpbk", "I2S1"},
+ {"I2S0", NULL, "I2S0_Lpbk_Mux"},
+ {"I2S2", NULL, "I2S2_Lpbk_Mux"},
+};
+
+/* dai ops */
+static int mtk_dai_connsys_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8192_rate_transform(afe->dev,
+ rate, dai->id);
+ unsigned int i2s_con = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, dai->id, substream->stream, rate);
+
+ /* non-inverse, i2s mode, proxy mode, 16bits, from connsys */
+ i2s_con |= 0 << INV_PAD_CTRL_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT;
+ i2s_con |= 1 << I2S_SRC_SFT;
+ i2s_con |= get_i2s_wlen(SNDRV_PCM_FORMAT_S16_LE) << I2S_WLEN_SFT;
+ i2s_con |= 0 << I2SIN_PAD_SEL_SFT;
+ regmap_write(afe->regmap, AFE_CONNSYS_I2S_CON, i2s_con);
+
+ /* use asrc */
+ regmap_update_bits(afe->regmap,
+ AFE_CONNSYS_I2S_CON,
+ I2S_BYPSRC_MASK_SFT,
+ 0x0 << I2S_BYPSRC_SFT);
+
+ /* proxy mode, set i2s for asrc */
+ regmap_update_bits(afe->regmap,
+ AFE_CONNSYS_I2S_CON,
+ I2S_MODE_MASK_SFT,
+ rate_reg << I2S_MODE_SFT);
+
+ switch (rate) {
+ case 32000:
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON3, 0x140000);
+ break;
+ case 44100:
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON3, 0x001B9000);
+ break;
+ default:
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON3, 0x001E0000);
+ break;
+ }
+
+ /* Calibration setting */
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON4, 0x00140000);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON9, 0x00036000);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON10, 0x0002FC00);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON6, 0x00007EF4);
+ regmap_write(afe->regmap, AFE_ASRC_2CH_CON5, 0x00FF5986);
+
+ /* 0:Stereo 1:Mono */
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON2,
+ CHSET_IS_MONO_MASK_SFT,
+ 0x0 << CHSET_IS_MONO_SFT);
+
+ return 0;
+}
+
+static int mtk_dai_connsys_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(afe->dev, "%s(), cmd %d, stream %d\n",
+ __func__, cmd, substream->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* i2s enable */
+ regmap_update_bits(afe->regmap,
+ AFE_CONNSYS_I2S_CON,
+ I2S_EN_MASK_SFT,
+ 0x1 << I2S_EN_SFT);
+
+ /* calibrator enable */
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON5,
+ CALI_EN_MASK_SFT,
+ 0x1 << CALI_EN_SFT);
+
+ /* asrc enable */
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON0,
+ CON0_CHSET_STR_CLR_MASK_SFT,
+ 0x1 << CON0_CHSET_STR_CLR_SFT);
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON0,
+ CON0_ASM_ON_MASK_SFT,
+ 0x1 << CON0_ASM_ON_SFT);
+
+ afe_priv->dai_on[dai->id] = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON0,
+ CON0_ASM_ON_MASK_SFT,
+ 0 << CON0_ASM_ON_SFT);
+ regmap_update_bits(afe->regmap,
+ AFE_ASRC_2CH_CON5,
+ CALI_EN_MASK_SFT,
+ 0 << CALI_EN_SFT);
+
+ /* i2s disable */
+ regmap_update_bits(afe->regmap,
+ AFE_CONNSYS_I2S_CON,
+ I2S_EN_MASK_SFT,
+ 0x0 << I2S_EN_SFT);
+
+ /* bypass asrc */
+ regmap_update_bits(afe->regmap,
+ AFE_CONNSYS_I2S_CON,
+ I2S_BYPSRC_MASK_SFT,
+ 0x1 << I2S_BYPSRC_SFT);
+
+ afe_priv->dai_on[dai->id] = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_connsys_i2s_ops = {
+ .hw_params = mtk_dai_connsys_i2s_hw_params,
+ .trigger = mtk_dai_connsys_i2s_trigger,
+};
+
+/* i2s */
+static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
+ struct snd_pcm_hw_params *params,
+ int i2s_id)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[i2s_id];
+
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8192_rate_transform(afe->dev,
+ rate, i2s_id);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int i2s_con = 0;
+ int ret = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, rate %d, format %d\n",
+ __func__, i2s_id, rate, format);
+
+ if (i2s_priv)
+ i2s_priv->rate = rate;
+ else
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+
+ switch (i2s_id) {
+ case MT8192_DAI_I2S_0:
+ i2s_con = I2S_IN_PAD_IO_MUX << I2SIN_PAD_SEL_SFT;
+ i2s_con |= rate_reg << I2S_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_1:
+ i2s_con = I2S1_SEL_O28_O29 << I2S2_SEL_O03_O04_SFT;
+ i2s_con |= rate_reg << I2S2_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S2_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S2_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_2:
+ i2s_con = 8 << I2S3_UPDATE_WORD_SFT;
+ i2s_con |= rate_reg << I2S3_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S3_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S3_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON2,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_3:
+ i2s_con = rate_reg << I2S4_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S4_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S4_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON3,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_5:
+ i2s_con = rate_reg << I2S5_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S5_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S5_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON4,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_6:
+ i2s_con = rate_reg << I2S6_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S6_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S6_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON6,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_7:
+ i2s_con = rate_reg << I2S7_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S7_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S7_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON7,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_8:
+ i2s_con = rate_reg << I2S8_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S8_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S8_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON8,
+ 0xffffeffe, i2s_con);
+ break;
+ case MT8192_DAI_I2S_9:
+ i2s_con = rate_reg << I2S9_OUT_MODE_SFT;
+ i2s_con |= I2S_FMT_I2S << I2S9_FMT_SFT;
+ i2s_con |= get_i2s_wlen(format) << I2S9_WLEN_SFT;
+ regmap_update_bits(afe->regmap, AFE_I2S_CON9,
+ 0xffffeffe, i2s_con);
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, i2s_id);
+ return -EINVAL;
+ }
+
+ /* set share i2s */
+ if (i2s_priv && i2s_priv->share_i2s_id >= 0)
+ ret = mtk_dai_i2s_config(afe, params, i2s_priv->share_i2s_id);
+
+ return ret;
+}
+
+static int mtk_dai_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ return mtk_dai_i2s_config(afe, params, dai->id);
+}
+
+static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[dai->id];
+ int apll;
+ int apll_rate;
+
+ if (!i2s_priv) {
+ dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ if (dir != SND_SOC_CLOCK_OUT) {
+ dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ apll = mt8192_get_apll_by_rate(afe, freq);
+ apll_rate = mt8192_get_apll_rate(afe, apll);
+
+ if (freq > apll_rate) {
+ dev_warn(afe->dev, "%s(), freq > apll rate", __func__);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_warn(afe->dev, "%s(), APLL can't gen freq Hz", __func__);
+ return -EINVAL;
+ }
+
+ i2s_priv->mclk_rate = freq;
+ i2s_priv->mclk_apll = apll;
+
+ if (i2s_priv->share_i2s_id > 0) {
+ struct mtk_afe_i2s_priv *share_i2s_priv;
+
+ share_i2s_priv = afe_priv->dai_priv[i2s_priv->share_i2s_id];
+ if (!share_i2s_priv) {
+ dev_warn(afe->dev, "%s(), share_i2s_priv = NULL",
+ __func__);
+ return -EINVAL;
+ }
+
+ share_i2s_priv->mclk_rate = i2s_priv->mclk_rate;
+ share_i2s_priv->mclk_apll = i2s_priv->mclk_apll;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_i2s_ops = {
+ .hw_params = mtk_dai_i2s_hw_params,
+ .set_sysclk = mtk_dai_i2s_set_sysclk,
+};
+
+/* dai driver */
+#define MTK_CONNSYS_I2S_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define MTK_I2S_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
+ {
+ .name = "CONNSYS_I2S",
+ .id = MT8192_DAI_CONNSYS_I2S,
+ .capture = {
+ .stream_name = "Connsys I2S",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_CONNSYS_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_connsys_i2s_ops,
+ },
+ {
+ .name = "I2S0",
+ .id = MT8192_DAI_I2S_0,
+ .capture = {
+ .stream_name = "I2S0",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S1",
+ .id = MT8192_DAI_I2S_1,
+ .playback = {
+ .stream_name = "I2S1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S2",
+ .id = MT8192_DAI_I2S_2,
+ .capture = {
+ .stream_name = "I2S2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S3",
+ .id = MT8192_DAI_I2S_3,
+ .playback = {
+ .stream_name = "I2S3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S5",
+ .id = MT8192_DAI_I2S_5,
+ .playback = {
+ .stream_name = "I2S5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S6",
+ .id = MT8192_DAI_I2S_6,
+ .capture = {
+ .stream_name = "I2S6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S7",
+ .id = MT8192_DAI_I2S_7,
+ .playback = {
+ .stream_name = "I2S7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S8",
+ .id = MT8192_DAI_I2S_8,
+ .capture = {
+ .stream_name = "I2S8",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ },
+ {
+ .name = "I2S9",
+ .id = MT8192_DAI_I2S_9,
+ .playback = {
+ .stream_name = "I2S9",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_I2S_RATES,
+ .formats = MTK_I2S_FORMATS,
+ },
+ .ops = &mtk_dai_i2s_ops,
+ }
+};
+
+/* this enum is merely for mtk_afe_i2s_priv declare */
+enum {
+ DAI_I2S0 = 0,
+ DAI_I2S1,
+ DAI_I2S2,
+ DAI_I2S3,
+ DAI_I2S5,
+ DAI_I2S6,
+ DAI_I2S7,
+ DAI_I2S8,
+ DAI_I2S9,
+ DAI_I2S_NUM,
+};
+
+static const struct mtk_afe_i2s_priv mt8192_i2s_priv[DAI_I2S_NUM] = {
+ [DAI_I2S0] = {
+ .id = MT8192_DAI_I2S_0,
+ .mclk_id = MT8192_I2S0_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S1] = {
+ .id = MT8192_DAI_I2S_1,
+ .mclk_id = MT8192_I2S1_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S2] = {
+ .id = MT8192_DAI_I2S_2,
+ .mclk_id = MT8192_I2S2_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S3] = {
+ .id = MT8192_DAI_I2S_3,
+ .mclk_id = MT8192_I2S3_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S5] = {
+ .id = MT8192_DAI_I2S_5,
+ .mclk_id = MT8192_I2S5_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S6] = {
+ .id = MT8192_DAI_I2S_6,
+ .mclk_id = MT8192_I2S6_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S7] = {
+ .id = MT8192_DAI_I2S_7,
+ .mclk_id = MT8192_I2S7_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S8] = {
+ .id = MT8192_DAI_I2S_8,
+ .mclk_id = MT8192_I2S8_MCK,
+ .share_i2s_id = -1,
+ },
+ [DAI_I2S9] = {
+ .id = MT8192_DAI_I2S_9,
+ .mclk_id = MT8192_I2S9_MCK,
+ .share_i2s_id = -1,
+ },
+};
+
+/**
+ * mt8192_dai_i2s_set_share() - Set up I2S ports to share a single clock.
+ * @afe: Pointer to &struct mtk_base_afe
+ * @main_i2s_name: The name of the I2S port that will provide the clock
+ * @secondary_i2s_name: The name of the I2S port that will use this clock
+ */
+int mt8192_dai_i2s_set_share(struct mtk_base_afe *afe, const char *main_i2s_name,
+ const char *secondary_i2s_name)
+{
+ struct mtk_afe_i2s_priv *secondary_i2s_priv;
+ int main_i2s_id;
+
+ secondary_i2s_priv = get_i2s_priv_by_name(afe, secondary_i2s_name);
+ if (!secondary_i2s_priv)
+ return -EINVAL;
+
+ main_i2s_id = get_i2s_id_by_name(afe, main_i2s_name);
+ if (main_i2s_id < 0)
+ return main_i2s_id;
+
+ secondary_i2s_priv->share_i2s_id = main_i2s_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8192_dai_i2s_set_share);
+
+static int mt8192_dai_i2s_set_priv(struct mtk_base_afe *afe)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < DAI_I2S_NUM; i++) {
+ ret = mt8192_dai_set_priv(afe, mt8192_i2s_priv[i].id,
+ sizeof(struct mtk_afe_i2s_priv),
+ &mt8192_i2s_priv[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int mt8192_dai_i2s_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+ int ret;
+
+ dev_dbg(afe->dev, "%s()\n", __func__);
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_i2s_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
+
+ dai->controls = mtk_dai_i2s_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls);
+ dai->dapm_widgets = mtk_dai_i2s_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
+ dai->dapm_routes = mtk_dai_i2s_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
+
+ /* set all dai i2s private data */
+ ret = mt8192_dai_i2s_set_priv(afe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c
new file mode 100644
index 000000000000..2847a2e747be
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI I2S Control
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+//
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+
+#include "mt8192-afe-common.h"
+#include "mt8192-interconnection.h"
+
+enum AUD_TX_LCH_RPT {
+ AUD_TX_LCH_RPT_NO_REPEAT = 0,
+ AUD_TX_LCH_RPT_REPEAT = 1
+};
+
+enum AUD_VBT_16K_MODE {
+ AUD_VBT_16K_MODE_DISABLE = 0,
+ AUD_VBT_16K_MODE_ENABLE = 1
+};
+
+enum AUD_EXT_MODEM {
+ AUD_EXT_MODEM_SELECT_INTERNAL = 0,
+ AUD_EXT_MODEM_SELECT_EXTERNAL = 1
+};
+
+enum AUD_PCM_SYNC_TYPE {
+ /* bck sync length = 1 */
+ AUD_PCM_ONE_BCK_CYCLE_SYNC = 0,
+ /* bck sync length = PCM_INTF_CON1[9:13] */
+ AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1
+};
+
+enum AUD_BT_MODE {
+ AUD_BT_MODE_DUAL_MIC_ON_TX = 0,
+ AUD_BT_MODE_SINGLE_MIC_ON_TX = 1
+};
+
+enum AUD_PCM_AFIFO_SRC {
+ /* slave mode & external modem uses different crystal */
+ AUD_PCM_AFIFO_ASRC = 0,
+ /* slave mode & external modem uses the same crystal */
+ AUD_PCM_AFIFO_AFIFO = 1
+};
+
+enum AUD_PCM_CLOCK_SOURCE {
+ AUD_PCM_CLOCK_MASTER_MODE = 0,
+ AUD_PCM_CLOCK_SLAVE_MODE = 1
+};
+
+enum AUD_PCM_WLEN {
+ AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0,
+ AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1
+};
+
+enum AUD_PCM_MODE {
+ AUD_PCM_MODE_PCM_MODE_8K = 0,
+ AUD_PCM_MODE_PCM_MODE_16K = 1,
+ AUD_PCM_MODE_PCM_MODE_32K = 2,
+ AUD_PCM_MODE_PCM_MODE_48K = 3,
+};
+
+enum AUD_PCM_FMT {
+ AUD_PCM_FMT_I2S = 0,
+ AUD_PCM_FMT_EIAJ = 1,
+ AUD_PCM_FMT_PCM_MODE_A = 2,
+ AUD_PCM_FMT_PCM_MODE_B = 3
+};
+
+enum AUD_BCLK_OUT_INV {
+ AUD_BCLK_OUT_INV_NO_INVERSE = 0,
+ AUD_BCLK_OUT_INV_INVERSE = 1
+};
+
+enum AUD_PCM_EN {
+ AUD_PCM_EN_DISABLE = 0,
+ AUD_PCM_EN_ENABLE = 1
+};
+
+/* dai component */
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN7,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN7,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN7_1,
+ I_DL4_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN8,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN8,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN8_1,
+ I_DL4_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_1_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1", AFE_CONN27,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2", AFE_CONN27,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN27,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1", AFE_CONN27,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2", AFE_CONN27,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN27_1,
+ I_DL4_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN17,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN17,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN17,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN17,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN17_1,
+ I_DL4_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN18,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN18,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN18,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN18,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN18_1,
+ I_DL4_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN23,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH1", AFE_CONN24,
+ I_I2S0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2", AFE_CONN24,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN24,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH1", AFE_CONN24,
+ I_I2S2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2", AFE_CONN24,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1", AFE_CONN24_1,
+ I_DL4_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_pcm_2_playback_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S0_CH2", AFE_CONN25,
+ I_I2S0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN25,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2S2_CH2", AFE_CONN25,
+ I_I2S2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2", AFE_CONN25_1,
+ I_DL4_CH2, 1, 0),
+};
+
+static int mtk_pcm_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_info(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_1_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_1_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_1_playback_ch4_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch1_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch1_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch2_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch2_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH3", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch3_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch3_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH4", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch4_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch4_mix)),
+ SND_SOC_DAPM_MIXER("PCM_2_PB_CH5", SND_SOC_NOPM, 0, 0,
+ mtk_pcm_2_playback_ch5_mix,
+ ARRAY_SIZE(mtk_pcm_2_playback_ch5_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_1_EN",
+ PCM_INTF_CON1, PCM_EN_SFT, 0,
+ mtk_pcm_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PCM_2_EN",
+ PCM2_INTF_CON, PCM2_EN_SFT, 0,
+ mtk_pcm_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("MD1_TO_AFE"),
+ SND_SOC_DAPM_INPUT("MD2_TO_AFE"),
+ SND_SOC_DAPM_OUTPUT("AFE_TO_MD1"),
+ SND_SOC_DAPM_OUTPUT("AFE_TO_MD2"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"},
+ {"PCM 1 Playback", NULL, "PCM_1_PB_CH4"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH1"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH2"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH3"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH4"},
+ {"PCM 2 Playback", NULL, "PCM_2_PB_CH5"},
+
+ {"PCM 1 Playback", NULL, "PCM_1_EN"},
+ {"PCM 2 Playback", NULL, "PCM_2_EN"},
+ {"PCM 1 Capture", NULL, "PCM_1_EN"},
+ {"PCM 2 Capture", NULL, "PCM_2_EN"},
+
+ {"AFE_TO_MD1", NULL, "PCM 2 Playback"},
+ {"AFE_TO_MD2", NULL, "PCM 1 Playback"},
+ {"PCM 2 Capture", NULL, "MD1_TO_AFE"},
+ {"PCM 1 Capture", NULL, "MD2_TO_AFE"},
+
+ {"PCM_1_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_1_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_1_PB_CH4", "DL1_CH1", "DL1"},
+ {"PCM_2_PB_CH1", "DL2_CH1", "DL2"},
+ {"PCM_2_PB_CH2", "DL2_CH2", "DL2"},
+ {"PCM_2_PB_CH4", "DL1_CH1", "DL1"},
+
+ {"PCM_1_PB_CH1", "DL4_CH1", "DL4"},
+ {"PCM_1_PB_CH2", "DL4_CH2", "DL4"},
+ {"PCM_1_PB_CH4", "DL4_CH1", "DL4"},
+ {"PCM_2_PB_CH1", "DL4_CH1", "DL4"},
+ {"PCM_2_PB_CH2", "DL4_CH2", "DL4"},
+ {"PCM_2_PB_CH4", "DL4_CH1", "DL4"},
+ {"PCM_1_PB_CH4", "I2S0_CH1", "I2S0"},
+ {"PCM_2_PB_CH4", "I2S2_CH1", "I2S2"},
+ {"PCM_2_PB_CH5", "DL1_CH2", "DL1"},
+ {"PCM_2_PB_CH5", "DL4_CH2", "DL4"},
+ {"PCM_2_PB_CH5", "I2S0_CH2", "I2S0"},
+ {"PCM_2_PB_CH5", "I2S2_CH2", "I2S2"},
+};
+
+/* dai ops */
+static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int rate_reg = mt8192_rate_transform(afe->dev, rate, dai->id);
+ unsigned int pcm_con = 0;
+
+ dev_info(afe->dev, "%s(), id %d, stream %d, rate %d, rate_reg %d, widget active p %d, c %d\n",
+ __func__,
+ dai->id,
+ substream->stream,
+ rate,
+ rate_reg,
+ p->active,
+ c->active);
+
+ if (p->active || c->active)
+ return 0;
+
+ switch (dai->id) {
+ case MT8192_DAI_PCM_1:
+ pcm_con |= AUD_BCLK_OUT_INV_NO_INVERSE << PCM_BCLK_OUT_INV_SFT;
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_EXT_MODEM_SELECT_INTERNAL << PCM_EXT_MODEM_SFT;
+ pcm_con |= 0 << PCM_SYNC_LENGTH_SFT;
+ pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT;
+ pcm_con |= AUD_PCM_CLOCK_SLAVE_MODE << PCM_SLAVE_SFT;
+ pcm_con |= rate_reg << PCM_MODE_SFT;
+ pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM_FMT_SFT;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1,
+ 0xfffffffe, pcm_con);
+ break;
+ case MT8192_DAI_PCM_2:
+ pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM2_TX_LCH_RPT_SFT;
+ pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM2_VBT_16K_MODE_SFT;
+ pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM2_BT_MODE_SFT;
+ pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM2_AFIFO_SFT;
+ pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM2_WLEN_SFT;
+ pcm_con |= rate_reg << PCM2_MODE_SFT;
+ pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM2_FMT_SFT;
+
+ regmap_update_bits(afe->regmap, PCM2_INTF_CON,
+ 0xfffffffe, pcm_con);
+ break;
+ default:
+ dev_warn(afe->dev, "%s(), id %d not support\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .hw_params = mtk_dai_pcm_hw_params,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM 1",
+ .id = MT8192_DAI_PCM_1,
+ .playback = {
+ .stream_name = "PCM 1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "PCM 2",
+ .id = MT8192_DAI_PCM_2,
+ .playback = {
+ .stream_name = "PCM 2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM 2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int mt8192_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dev_info(afe->dev, "%s()\n", __func__);
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c b/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
new file mode 100644
index 000000000000..49440db370af
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
@@ -0,0 +1,778 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MediaTek ALSA SoC Audio DAI TDM Control
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Shane Chien <shane.chien@mediatek.com>
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+
+#include "mt8192-afe-clk.h"
+#include "mt8192-afe-common.h"
+#include "mt8192-afe-gpio.h"
+#include "mt8192-interconnection.h"
+
+struct mtk_afe_tdm_priv {
+ int id;
+ int bck_id;
+ int bck_rate;
+ int tdm_out_mode;
+ int bck_invert;
+ int lck_invert;
+ int mclk_id;
+ int mclk_multiple; /* according to sample rate */
+ int mclk_rate;
+ int mclk_apll;
+};
+
+enum {
+ TDM_OUT_I2S = 0,
+ TDM_OUT_DSP_A = 1,
+ TDM_OUT_DSP_B = 2,
+};
+
+enum {
+ TDM_BCK_NON_INV = 0,
+ TDM_BCK_INV = 1,
+};
+
+enum {
+ TDM_LCK_NON_INV = 0,
+ TDM_LCK_INV = 1,
+};
+
+enum {
+ TDM_WLEN_16_BIT = 1,
+ TDM_WLEN_32_BIT = 2,
+};
+
+enum {
+ TDM_CHANNEL_BCK_16 = 0,
+ TDM_CHANNEL_BCK_24 = 1,
+ TDM_CHANNEL_BCK_32 = 2,
+};
+
+enum {
+ TDM_CHANNEL_NUM_2 = 0,
+ TDM_CHANNEL_NUM_4 = 1,
+ TDM_CHANNEL_NUM_8 = 2,
+};
+
+enum {
+ TDM_CH_START_O30_O31 = 0,
+ TDM_CH_START_O32_O33,
+ TDM_CH_START_O34_O35,
+ TDM_CH_START_O36_O37,
+ TDM_CH_ZERO,
+};
+
+static unsigned int get_tdm_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
+}
+
+static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
+}
+
+static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) - 1;
+}
+
+static unsigned int get_tdm_ch(unsigned int ch)
+{
+ switch (ch) {
+ case 1:
+ case 2:
+ return TDM_CHANNEL_NUM_2;
+ case 3:
+ case 4:
+ return TDM_CHANNEL_NUM_4;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ default:
+ return TDM_CHANNEL_NUM_8;
+ }
+}
+
+static unsigned int get_tdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+ else
+ return 2;
+}
+
+static unsigned int get_tdm_ch_per_sdata(unsigned int mode,
+ unsigned int channels)
+{
+ if (mode == TDM_OUT_DSP_A || mode == TDM_OUT_DSP_B)
+ return get_tdm_ch_fixup(channels);
+ else
+ return 2;
+}
+
+/* interconnection */
+enum {
+ HDMI_CONN_CH0 = 0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static const char *const hdmi_conn_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3",
+ "CH4", "CH5", "CH6", "CH7",
+};
+
+static int hdmi_conn_mux_map_value[] = {
+ HDMI_CONN_CH0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_0_SFT,
+ HDMI_O_0_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch0_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_1_SFT,
+ HDMI_O_1_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch1_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_2_SFT,
+ HDMI_O_2_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch2_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_3_SFT,
+ HDMI_O_3_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch3_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_4_SFT,
+ HDMI_O_4_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch4_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_5_SFT,
+ HDMI_O_5_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch5_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_6_SFT,
+ HDMI_O_6_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch6_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_7_SFT,
+ HDMI_O_7_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch7_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_TDM_MCK_EN,
+ SUPPLY_SEQ_TDM_BCK_EN,
+ SUPPLY_SEQ_TDM_EN,
+};
+
+static int get_tdm_id_by_name(const char *name)
+{
+ return MT8192_DAI_TDM;
+}
+
+static int mtk_tdm_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_afe_gpio_request(afe->dev, true, tdm_priv->id, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8192_afe_gpio_request(afe->dev, false, tdm_priv->id, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
+ __func__, w->name, event, dai_id);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8192_mck_disable(afe, tdm_priv->bck_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x, dai_id %d\n",
+ __func__, w->name, event, dai_id);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8192_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tdm_priv->mclk_rate = 0;
+ mt8192_mck_disable(afe, tdm_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
+ SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch0_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch1_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch2_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch3_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch4_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch5_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch6_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch7_mux_control),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_tdm_clk"),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_EN", SUPPLY_SEQ_TDM_EN,
+ AFE_TDM_CON1, TDM_EN_SFT, 0,
+ mtk_tdm_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_bck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int cur_apll;
+
+ /* which apll */
+ cur_apll = mt8192_get_apll_by_name(afe, source->name);
+
+ return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
+ {"HDMI_CH0_MUX", "CH0", "HDMI"},
+ {"HDMI_CH0_MUX", "CH1", "HDMI"},
+ {"HDMI_CH0_MUX", "CH2", "HDMI"},
+ {"HDMI_CH0_MUX", "CH3", "HDMI"},
+ {"HDMI_CH0_MUX", "CH4", "HDMI"},
+ {"HDMI_CH0_MUX", "CH5", "HDMI"},
+ {"HDMI_CH0_MUX", "CH6", "HDMI"},
+ {"HDMI_CH0_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH1_MUX", "CH0", "HDMI"},
+ {"HDMI_CH1_MUX", "CH1", "HDMI"},
+ {"HDMI_CH1_MUX", "CH2", "HDMI"},
+ {"HDMI_CH1_MUX", "CH3", "HDMI"},
+ {"HDMI_CH1_MUX", "CH4", "HDMI"},
+ {"HDMI_CH1_MUX", "CH5", "HDMI"},
+ {"HDMI_CH1_MUX", "CH6", "HDMI"},
+ {"HDMI_CH1_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH2_MUX", "CH0", "HDMI"},
+ {"HDMI_CH2_MUX", "CH1", "HDMI"},
+ {"HDMI_CH2_MUX", "CH2", "HDMI"},
+ {"HDMI_CH2_MUX", "CH3", "HDMI"},
+ {"HDMI_CH2_MUX", "CH4", "HDMI"},
+ {"HDMI_CH2_MUX", "CH5", "HDMI"},
+ {"HDMI_CH2_MUX", "CH6", "HDMI"},
+ {"HDMI_CH2_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH3_MUX", "CH0", "HDMI"},
+ {"HDMI_CH3_MUX", "CH1", "HDMI"},
+ {"HDMI_CH3_MUX", "CH2", "HDMI"},
+ {"HDMI_CH3_MUX", "CH3", "HDMI"},
+ {"HDMI_CH3_MUX", "CH4", "HDMI"},
+ {"HDMI_CH3_MUX", "CH5", "HDMI"},
+ {"HDMI_CH3_MUX", "CH6", "HDMI"},
+ {"HDMI_CH3_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH4_MUX", "CH0", "HDMI"},
+ {"HDMI_CH4_MUX", "CH1", "HDMI"},
+ {"HDMI_CH4_MUX", "CH2", "HDMI"},
+ {"HDMI_CH4_MUX", "CH3", "HDMI"},
+ {"HDMI_CH4_MUX", "CH4", "HDMI"},
+ {"HDMI_CH4_MUX", "CH5", "HDMI"},
+ {"HDMI_CH4_MUX", "CH6", "HDMI"},
+ {"HDMI_CH4_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH5_MUX", "CH0", "HDMI"},
+ {"HDMI_CH5_MUX", "CH1", "HDMI"},
+ {"HDMI_CH5_MUX", "CH2", "HDMI"},
+ {"HDMI_CH5_MUX", "CH3", "HDMI"},
+ {"HDMI_CH5_MUX", "CH4", "HDMI"},
+ {"HDMI_CH5_MUX", "CH5", "HDMI"},
+ {"HDMI_CH5_MUX", "CH6", "HDMI"},
+ {"HDMI_CH5_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH6_MUX", "CH0", "HDMI"},
+ {"HDMI_CH6_MUX", "CH1", "HDMI"},
+ {"HDMI_CH6_MUX", "CH2", "HDMI"},
+ {"HDMI_CH6_MUX", "CH3", "HDMI"},
+ {"HDMI_CH6_MUX", "CH4", "HDMI"},
+ {"HDMI_CH6_MUX", "CH5", "HDMI"},
+ {"HDMI_CH6_MUX", "CH6", "HDMI"},
+ {"HDMI_CH6_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH7_MUX", "CH0", "HDMI"},
+ {"HDMI_CH7_MUX", "CH1", "HDMI"},
+ {"HDMI_CH7_MUX", "CH2", "HDMI"},
+ {"HDMI_CH7_MUX", "CH3", "HDMI"},
+ {"HDMI_CH7_MUX", "CH4", "HDMI"},
+ {"HDMI_CH7_MUX", "CH5", "HDMI"},
+ {"HDMI_CH7_MUX", "CH6", "HDMI"},
+ {"HDMI_CH7_MUX", "CH7", "HDMI"},
+
+ {"TDM", NULL, "HDMI_CH0_MUX"},
+ {"TDM", NULL, "HDMI_CH1_MUX"},
+ {"TDM", NULL, "HDMI_CH2_MUX"},
+ {"TDM", NULL, "HDMI_CH3_MUX"},
+ {"TDM", NULL, "HDMI_CH4_MUX"},
+ {"TDM", NULL, "HDMI_CH5_MUX"},
+ {"TDM", NULL, "HDMI_CH6_MUX"},
+ {"TDM", NULL, "HDMI_CH7_MUX"},
+
+ {"TDM", NULL, "aud_tdm_clk"},
+ {"TDM", NULL, "TDM_BCK"},
+ {"TDM", NULL, "TDM_EN"},
+ {"TDM_BCK", NULL, "TDM_MCK"},
+ {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+};
+
+/* dai ops */
+static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
+ struct mtk_afe_tdm_priv *tdm_priv,
+ int freq)
+{
+ int apll;
+ int apll_rate;
+
+ apll = mt8192_get_apll_by_rate(afe, freq);
+ apll_rate = mt8192_get_apll_rate(afe, apll);
+
+ if (!freq || freq > apll_rate) {
+ dev_warn(afe->dev,
+ "%s(), freq(%d Hz) invalid\n", __func__, freq);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_warn(afe->dev,
+ "%s(), APLL cannot generate %d Hz", __func__, freq);
+ return -EINVAL;
+ }
+
+ tdm_priv->mclk_rate = freq;
+ tdm_priv->mclk_apll = apll;
+
+ return 0;
+}
+
+static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int tdm_id = dai->id;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[tdm_id];
+ unsigned int tdm_out_mode = tdm_priv->tdm_out_mode;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ unsigned int out_channels_per_sdata =
+ get_tdm_ch_per_sdata(tdm_out_mode, channels);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int tdm_con = 0;
+
+ /* calculate mclk_rate, if not set explicitly */
+ if (!tdm_priv->mclk_rate) {
+ tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
+ mtk_dai_tdm_cal_mclk(afe,
+ tdm_priv,
+ tdm_priv->mclk_rate);
+ }
+
+ /* calculate bck */
+ tdm_priv->bck_rate = rate *
+ out_channels_per_sdata *
+ snd_pcm_format_physical_width(format);
+
+ if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
+ dev_warn(afe->dev, "%s(), bck_rate > mclk_rate rate", __func__);
+
+ if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
+ dev_warn(afe->dev, "%s(), bck cannot generate", __func__);
+
+ dev_dbg(afe->dev, "%s(), id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
+ __func__,
+ tdm_id, rate, channels, format,
+ tdm_priv->mclk_rate, tdm_priv->bck_rate);
+
+ dev_dbg(afe->dev, "%s(), out_channels_per_sdata = %d\n",
+ __func__, out_channels_per_sdata);
+
+ /* set tdm */
+ if (tdm_priv->bck_invert)
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON3,
+ BCK_INVERSE_MASK_SFT,
+ 0x1 << BCK_INVERSE_SFT);
+
+ if (tdm_priv->lck_invert)
+ tdm_con |= 1 << LRCK_INVERSE_SFT;
+
+ if (tdm_priv->tdm_out_mode == TDM_OUT_I2S) {
+ tdm_con |= 1 << DELAY_DATA_SFT;
+ tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
+ } else if (tdm_priv->tdm_out_mode == TDM_OUT_DSP_A) {
+ tdm_con |= 1 << DELAY_DATA_SFT;
+ tdm_con |= 0 << LRCK_TDM_WIDTH_SFT;
+ } else if (tdm_priv->tdm_out_mode == TDM_OUT_DSP_B) {
+ tdm_con |= 0 << DELAY_DATA_SFT;
+ tdm_con |= 0 << LRCK_TDM_WIDTH_SFT;
+ }
+
+ tdm_con |= 1 << LEFT_ALIGN_SFT;
+ tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
+ tdm_con |= get_tdm_ch(out_channels_per_sdata) << CHANNEL_NUM_SFT;
+ tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT;
+ regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
+
+ if (out_channels_per_sdata == 2) {
+ switch (channels) {
+ case 1:
+ case 2:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 3:
+ case 4:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 5:
+ case 6:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 7:
+ case 8:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ default:
+ tdm_con = 0;
+ }
+ } else {
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ }
+
+ regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
+
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_CH_NUM_MASK_SFT,
+ channels << HDMI_CH_NUM_SFT);
+ return 0;
+}
+
+static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ if (dir != SND_SOC_CLOCK_OUT) {
+ dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(afe->dev, "%s(), freq %d\n", __func__, freq);
+
+ return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
+}
+
+static int mtk_dai_tdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (!tdm_priv) {
+ dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
+ return -EINVAL;
+ }
+
+ /* DAI mode*/
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ tdm_priv->tdm_out_mode = TDM_OUT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_priv->tdm_out_mode = TDM_OUT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_priv->tdm_out_mode = TDM_OUT_DSP_B;
+ break;
+ default:
+ tdm_priv->tdm_out_mode = TDM_OUT_I2S;
+ }
+
+ /* DAI clock inversion*/
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ tdm_priv->bck_invert = TDM_BCK_NON_INV;
+ tdm_priv->lck_invert = TDM_LCK_NON_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ tdm_priv->bck_invert = TDM_BCK_NON_INV;
+ tdm_priv->lck_invert = TDM_LCK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ tdm_priv->bck_invert = TDM_BCK_INV;
+ tdm_priv->lck_invert = TDM_LCK_NON_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ default:
+ tdm_priv->bck_invert = TDM_BCK_INV;
+ tdm_priv->lck_invert = TDM_LCK_INV;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
+ .hw_params = mtk_dai_tdm_hw_params,
+ .set_sysclk = mtk_dai_tdm_set_sysclk,
+ .set_fmt = mtk_dai_tdm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
+ {
+ .name = "TDM",
+ .id = MT8192_DAI_TDM,
+ .playback = {
+ .stream_name = "TDM",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+};
+
+static struct mtk_afe_tdm_priv *init_tdm_priv_data(struct mtk_base_afe *afe)
+{
+ struct mtk_afe_tdm_priv *tdm_priv;
+
+ tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
+ GFP_KERNEL);
+ if (!tdm_priv)
+ return NULL;
+
+ tdm_priv->mclk_multiple = 512;
+ tdm_priv->bck_id = MT8192_I2S4_BCK;
+ tdm_priv->mclk_id = MT8192_I2S4_MCK;
+ tdm_priv->id = MT8192_DAI_TDM;
+
+ return tdm_priv;
+}
+
+int mt8192_dai_tdm_register(struct mtk_base_afe *afe)
+{
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv;
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_tdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
+
+ dai->dapm_widgets = mtk_dai_tdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
+ dai->dapm_routes = mtk_dai_tdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
+
+ tdm_priv = init_tdm_priv_data(afe);
+ if (!tdm_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8192_DAI_TDM] = tdm_priv;
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8192/mt8192-interconnection.h b/sound/soc/mediatek/mt8192/mt8192-interconnection.h
new file mode 100644
index 000000000000..6a1bc7c1a862
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-interconnection.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Mediatek MT8192 audio driver interconnection definition
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Shane Chien <shane.chien@mediatek.com>
+ */
+
+#ifndef _MT8192_INTERCONNECTION_H_
+#define _MT8192_INTERCONNECTION_H_
+
+/* in port define */
+#define I_I2S0_CH1 0
+#define I_I2S0_CH2 1
+#define I_ADDA_UL_CH1 3
+#define I_ADDA_UL_CH2 4
+#define I_DL1_CH1 5
+#define I_DL1_CH2 6
+#define I_DL2_CH1 7
+#define I_DL2_CH2 8
+#define I_PCM_1_CAP_CH1 9
+#define I_GAIN1_OUT_CH1 10
+#define I_GAIN1_OUT_CH2 11
+#define I_GAIN2_OUT_CH1 12
+#define I_GAIN2_OUT_CH2 13
+#define I_PCM_2_CAP_CH1 14
+#define I_ADDA_UL_CH3 17
+#define I_ADDA_UL_CH4 18
+#define I_DL12_CH1 19
+#define I_DL12_CH2 20
+#define I_PCM_2_CAP_CH2 21
+#define I_PCM_1_CAP_CH2 22
+#define I_DL3_CH1 23
+#define I_DL3_CH2 24
+#define I_I2S2_CH1 25
+#define I_I2S2_CH2 26
+#define I_I2S2_CH3 27
+#define I_I2S2_CH4 28
+
+/* in port define >= 32 */
+#define I_32_OFFSET 32
+#define I_CONNSYS_I2S_CH1 (34 - I_32_OFFSET)
+#define I_CONNSYS_I2S_CH2 (35 - I_32_OFFSET)
+#define I_SRC_1_OUT_CH1 (36 - I_32_OFFSET)
+#define I_SRC_1_OUT_CH2 (37 - I_32_OFFSET)
+#define I_SRC_2_OUT_CH1 (38 - I_32_OFFSET)
+#define I_SRC_2_OUT_CH2 (39 - I_32_OFFSET)
+#define I_DL4_CH1 (40 - I_32_OFFSET)
+#define I_DL4_CH2 (41 - I_32_OFFSET)
+#define I_DL5_CH1 (42 - I_32_OFFSET)
+#define I_DL5_CH2 (43 - I_32_OFFSET)
+#define I_DL6_CH1 (44 - I_32_OFFSET)
+#define I_DL6_CH2 (45 - I_32_OFFSET)
+#define I_DL7_CH1 (46 - I_32_OFFSET)
+#define I_DL7_CH2 (47 - I_32_OFFSET)
+#define I_DL8_CH1 (48 - I_32_OFFSET)
+#define I_DL8_CH2 (49 - I_32_OFFSET)
+#define I_DL9_CH1 (50 - I_32_OFFSET)
+#define I_DL9_CH2 (51 - I_32_OFFSET)
+#define I_I2S6_CH1 (52 - I_32_OFFSET)
+#define I_I2S6_CH2 (53 - I_32_OFFSET)
+#define I_I2S8_CH1 (54 - I_32_OFFSET)
+#define I_I2S8_CH2 (55 - I_32_OFFSET)
+
+#endif
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
new file mode 100644
index 000000000000..91c57765ab57
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -0,0 +1,1239 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8192-mt6359-rt1015-rt5682.c --
+// MT8192-MT6359-RT1015-RT6358 ALSA SoC machine driver
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
+//
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/rt5682.h>
+#include <sound/soc.h>
+
+#include "../../codecs/mt6359.h"
+#include "../../codecs/rt1015.h"
+#include "../../codecs/rt5682.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-soc-card.h"
+#include "../common/mtk-soundcard-driver.h"
+#include "mt8192-afe-common.h"
+#include "mt8192-afe-clk.h"
+#include "mt8192-afe-gpio.h"
+
+#define DRIVER_NAME "mt8192_mt6359"
+
+#define RT1015_CODEC_DAI "rt1015-aif"
+#define RT1015_DEV0_NAME "rt1015.1-0028"
+#define RT1015_DEV1_NAME "rt1015.1-0029"
+
+#define RT1015_RT5682_CARD_NAME "mt8192_mt6359_rt1015_rt5682"
+#define RT1015P_RT5682_CARD_NAME "mt8192_mt6359_rt1015p_rt5682"
+#define RT1015P_RT5682S_CARD_NAME "mt8192_mt6359_rt1015p_rt5682s"
+
+#define RT1015_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015_rt5682"
+#define RT1015P_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682"
+#define RT1015P_RT5682S_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682s"
+
+enum mt8192_jacks {
+ MT8192_JACK_HEADSET,
+ MT8192_JACK_HDMI,
+ MT8192_JACK_MAX,
+};
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mt8192_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int mt8192_rt1015_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ int ret, i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT1015_PLL_S_BCLK,
+ params_rate(params) * 64,
+ params_rate(params) * 256);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT1015_SCLK_S_PLL,
+ params_rate(params) * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static int mt8192_rt5682x_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1,
+ RT5682_PLL1_S_BCLK1,
+ params_rate(params) * 64,
+ params_rate(params) * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT5682_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8192_rt1015_i2s_ops = {
+ .hw_params = mt8192_rt1015_i2s_hw_params,
+};
+
+static const struct snd_soc_ops mt8192_rt5682x_i2s_ops = {
+ .hw_params = mt8192_rt5682x_i2s_hw_params,
+};
+
+static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+ int phase;
+ unsigned int monitor;
+ int test_done_1, test_done_2, test_done_3;
+ int cycle_1, cycle_2, cycle_3;
+ int prev_cycle_1, prev_cycle_2, prev_cycle_3;
+ int chosen_phase_1, chosen_phase_2, chosen_phase_3;
+ int counter;
+ int mtkaif_calib_ok;
+
+ pm_runtime_get_sync(afe->dev);
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34, 1);
+ mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34, 0);
+
+ mt6359_mtkaif_calibration_enable(cmpnt_codec);
+
+ /* set clock protocol 2 */
+ regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP, 0xff, 0x38);
+ regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP, 0xff, 0x39);
+
+ /* set test type to synchronizer pulse */
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
+
+ mtkaif_calib_ok = true;
+ afe_priv->mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
+ afe_priv->mtkaif_chosen_phase[0] = -1;
+ afe_priv->mtkaif_chosen_phase[1] = -1;
+ afe_priv->mtkaif_chosen_phase[2] = -1;
+
+ for (phase = 0;
+ phase <= afe_priv->mtkaif_calibration_num_phase &&
+ mtkaif_calib_ok;
+ phase++) {
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ phase, phase, phase);
+
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0x1, 0x1);
+
+ test_done_1 = 0;
+ test_done_2 = 0;
+ test_done_3 = 0;
+ cycle_1 = -1;
+ cycle_2 = -1;
+ cycle_3 = -1;
+ counter = 0;
+ while (test_done_1 == 0 ||
+ test_done_2 == 0 ||
+ test_done_3 == 0) {
+ regmap_read(afe_priv->topckgen,
+ CKSYS_AUD_TOP_MON, &monitor);
+
+ test_done_1 = (monitor >> 28) & 0x1;
+ test_done_2 = (monitor >> 29) & 0x1;
+ test_done_3 = (monitor >> 30) & 0x1;
+ if (test_done_1 == 1)
+ cycle_1 = monitor & 0xf;
+
+ if (test_done_2 == 1)
+ cycle_2 = (monitor >> 4) & 0xf;
+
+ if (test_done_3 == 1)
+ cycle_3 = (monitor >> 8) & 0xf;
+
+ /* handle if never test done */
+ if (++counter > 10000) {
+ dev_err(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, cycle_3 %d, monitor 0x%x\n",
+ __func__,
+ cycle_1, cycle_2, cycle_3, monitor);
+ mtkaif_calib_ok = false;
+ break;
+ }
+ }
+
+ if (phase == 0) {
+ prev_cycle_1 = cycle_1;
+ prev_cycle_2 = cycle_2;
+ prev_cycle_3 = cycle_3;
+ }
+
+ if (cycle_1 != prev_cycle_1 &&
+ afe_priv->mtkaif_chosen_phase[0] < 0) {
+ afe_priv->mtkaif_chosen_phase[0] = phase - 1;
+ afe_priv->mtkaif_phase_cycle[0] = prev_cycle_1;
+ }
+
+ if (cycle_2 != prev_cycle_2 &&
+ afe_priv->mtkaif_chosen_phase[1] < 0) {
+ afe_priv->mtkaif_chosen_phase[1] = phase - 1;
+ afe_priv->mtkaif_phase_cycle[1] = prev_cycle_2;
+ }
+
+ if (cycle_3 != prev_cycle_3 &&
+ afe_priv->mtkaif_chosen_phase[2] < 0) {
+ afe_priv->mtkaif_chosen_phase[2] = phase - 1;
+ afe_priv->mtkaif_phase_cycle[2] = prev_cycle_3;
+ }
+
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0x1, 0x0);
+
+ if (afe_priv->mtkaif_chosen_phase[0] >= 0 &&
+ afe_priv->mtkaif_chosen_phase[1] >= 0 &&
+ afe_priv->mtkaif_chosen_phase[2] >= 0)
+ break;
+ }
+
+ if (afe_priv->mtkaif_chosen_phase[0] < 0)
+ chosen_phase_1 = 0;
+ else
+ chosen_phase_1 = afe_priv->mtkaif_chosen_phase[0];
+
+ if (afe_priv->mtkaif_chosen_phase[1] < 0)
+ chosen_phase_2 = 0;
+ else
+ chosen_phase_2 = afe_priv->mtkaif_chosen_phase[1];
+
+ if (afe_priv->mtkaif_chosen_phase[2] < 0)
+ chosen_phase_3 = 0;
+ else
+ chosen_phase_3 = afe_priv->mtkaif_chosen_phase[2];
+
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ chosen_phase_1,
+ chosen_phase_2,
+ chosen_phase_3);
+
+ /* disable rx fifo */
+ regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP, 0xff, 0x38);
+
+ mt6359_mtkaif_calibration_disable(cmpnt_codec);
+
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 1);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 0);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34, 1);
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34, 0);
+ pm_runtime_put(afe->dev);
+
+ dev_dbg(afe->dev, "%s(), mtkaif_chosen_phase[0/1/2]:%d/%d/%d\n",
+ __func__,
+ afe_priv->mtkaif_chosen_phase[0],
+ afe_priv->mtkaif_chosen_phase[1],
+ afe_priv->mtkaif_chosen_phase[2]);
+
+ return 0;
+}
+
+static int mt8192_mt6359_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8192_afe_private *afe_priv = afe->platform_priv;
+
+ /* set mtkaif protocol */
+ mt6359_set_mtkaif_protocol(cmpnt_codec,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
+ afe_priv->mtkaif_protocol = MTKAIF_PROTOCOL_2_CLK_P2;
+
+ /* mtkaif calibration */
+ mt8192_mt6359_mtkaif_calibration(rtd);
+
+ return 0;
+}
+
+static int mt8192_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8192_JACK_HEADSET];
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = mt8192_dai_i2s_set_share(afe, "I2S8", "I2S9");
+ if (ret) {
+ dev_err(rtd->dev, "Failed to set up shared clocks\n");
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack, mt8192_jack_pins,
+ ARRAY_SIZE(mt8192_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ return snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+};
+
+static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8192_JACK_HDMI];
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
+ if (ret) {
+ dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+}
+
+static int mt8192_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to S24_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback12,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL12")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback4,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback5,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback6,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback7,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback8,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback9,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL9")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture4,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture5,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture6,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture7,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture8,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_mono1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_MONO_1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_mono2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_MONO_2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture_mono3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_MONO_3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback_hdmi,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(primary_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1"),
+ COMP_CODEC("dmic-codec",
+ "dmic-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(primary_codec_ch34,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA_CH34")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif2")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ap_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ap_dmic_ch34,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC_CH34")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s0,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s1,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s2,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s5,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s6,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s7,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s8,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S8")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s9,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2S9")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(connsys_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("CONNSYS_I2S")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm2,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM 2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(tdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("TDM")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
+ /* Front End DAI links */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ {
+ .name = "Playback_12",
+ .stream_name = "Playback_12",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback12),
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ {
+ .name = "Playback_4",
+ .stream_name = "Playback_4",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback4),
+ },
+ {
+ .name = "Playback_5",
+ .stream_name = "Playback_5",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback5),
+ },
+ {
+ .name = "Playback_6",
+ .stream_name = "Playback_6",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback6),
+ },
+ {
+ .name = "Playback_7",
+ .stream_name = "Playback_7",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback7),
+ },
+ {
+ .name = "Playback_8",
+ .stream_name = "Playback_8",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback8),
+ },
+ {
+ .name = "Playback_9",
+ .stream_name = "Playback_9",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback9),
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ {
+ .name = "Capture_4",
+ .stream_name = "Capture_4",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture4),
+ },
+ {
+ .name = "Capture_5",
+ .stream_name = "Capture_5",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture5),
+ },
+ {
+ .name = "Capture_6",
+ .stream_name = "Capture_6",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture6),
+ },
+ {
+ .name = "Capture_7",
+ .stream_name = "Capture_7",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture7),
+ },
+ {
+ .name = "Capture_8",
+ .stream_name = "Capture_8",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture8),
+ },
+ {
+ .name = "Capture_Mono_1",
+ .stream_name = "Capture_Mono_1",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_mono1),
+ },
+ {
+ .name = "Capture_Mono_2",
+ .stream_name = "Capture_Mono_2",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_mono2),
+ },
+ {
+ .name = "Capture_Mono_3",
+ .stream_name = "Capture_Mono_3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_mono3),
+ },
+ {
+ .name = "playback_hdmi",
+ .stream_name = "Playback_HDMI",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_hdmi),
+ },
+ /* Back End DAI links */
+ {
+ .name = "Primary Codec",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ .init = mt8192_mt6359_init,
+ SND_SOC_DAILINK_REG(primary_codec),
+ },
+ {
+ .name = "Primary Codec CH34",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(primary_codec_ch34),
+ },
+ {
+ .name = "AP_DMIC",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic),
+ },
+ {
+ .name = "AP_DMIC_CH34",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic_ch34),
+ },
+ {
+ .name = "I2S0",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s0),
+ },
+ {
+ .name = "I2S1",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s1),
+ },
+ {
+ .name = "I2S2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s2),
+ },
+ {
+ .name = "I2S3",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s3),
+ },
+ {
+ .name = "I2S5",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s5),
+ },
+ {
+ .name = "I2S6",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s6),
+ },
+ {
+ .name = "I2S7",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s7),
+ },
+ {
+ .name = "I2S8",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .init = mt8192_rt5682_init,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s8),
+ .ops = &mt8192_rt5682x_i2s_ops,
+ },
+ {
+ .name = "I2S9",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s9),
+ .ops = &mt8192_rt5682x_i2s_ops,
+ },
+ {
+ .name = "CONNSYS_I2S",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(connsys_i2s),
+ },
+ {
+ .name = "PCM 1",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+ {
+ .name = "PCM 2",
+ .no_pcm = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(pcm2),
+ },
+ {
+ .name = "TDM",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ .ignore = 1,
+ .init = mt8192_mt6359_hdmi_init,
+ SND_SOC_DAILINK_REG(tdm),
+ },
+};
+
+static const struct snd_soc_dapm_widget
+mt8192_mt6359_rt1015_rt5682_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_OUTPUT("TDM Out"),
+};
+
+static const struct snd_soc_dapm_route mt8192_mt6359_rt1015_rt5682_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+ /* headset */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+ /* TDM */
+ { "TDM Out", NULL, "TDM" },
+};
+
+static const struct snd_kcontrol_new mt8192_mt6359_rt1015_rt5682_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_codec_conf rt1015_amp_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = {
+ .name = RT1015_RT5682_CARD_NAME,
+ .driver_name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .dai_link = mt8192_mt6359_dai_links,
+ .num_links = ARRAY_SIZE(mt8192_mt6359_dai_links),
+ .controls = mt8192_mt6359_rt1015_rt5682_controls,
+ .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015_rt5682_controls),
+ .dapm_widgets = mt8192_mt6359_rt1015_rt5682_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015_rt5682_widgets),
+ .dapm_routes = mt8192_mt6359_rt1015_rt5682_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015_rt5682_routes),
+ .codec_conf = rt1015_amp_conf,
+ .num_configs = ARRAY_SIZE(rt1015_amp_conf),
+};
+
+static const struct snd_soc_dapm_widget mt8192_mt6359_rt1015p_rt5682x_widgets[] = {
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682x_routes[] = {
+ /* speaker */
+ { "Speakers", NULL, "Speaker" },
+ /* headset */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682x_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = {
+ .driver_name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .dai_link = mt8192_mt6359_dai_links,
+ .num_links = ARRAY_SIZE(mt8192_mt6359_dai_links),
+ .controls = mt8192_mt6359_rt1015p_rt5682x_controls,
+ .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_controls),
+ .dapm_widgets = mt8192_mt6359_rt1015p_rt5682x_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_widgets),
+ .dapm_routes = mt8192_mt6359_rt1015p_rt5682x_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes),
+};
+
+static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ char *link_name)
+{
+ int ret;
+
+ if (node && strcmp(link->name, link_name) == 0) {
+ ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link);
+ if (ret < 0) {
+ dev_err_probe(card->dev, ret, "get dai link codecs fail\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ struct device *dev = card->dev;
+ struct device_node *hdmi_codec, *headset_codec, *speaker_codec;
+ struct snd_soc_dai_link *dai_link;
+ int i, ret = 0;
+
+ hdmi_codec = of_parse_phandle(dev->of_node, "mediatek,hdmi-codec", 0);
+ if (!hdmi_codec)
+ dev_dbg(dev, "The machine has no hdmi-codec\n");
+
+ speaker_codec = of_get_child_by_name(dev->of_node, "speaker-codecs");
+ if (!speaker_codec) {
+ ret = -EINVAL;
+ dev_err_probe(dev, ret, "Property 'speaker-codecs' missing or invalid\n");
+ goto err_speaker_codec;
+ }
+
+ headset_codec = of_get_child_by_name(dev->of_node, "headset-codec");
+ if (!headset_codec) {
+ ret = -EINVAL;
+ dev_err_probe(dev, ret, "Property 'headset-codec' missing or invalid\n");
+ goto err_headset_codec;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3");
+ if (ret) {
+ dev_err_probe(dev, ret, "%s set speaker_codec fail\n",
+ dai_link->name);
+ break;
+ }
+
+ ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8");
+ if (ret) {
+ dev_err_probe(dev, ret, "%s set headset_codec fail\n",
+ dai_link->name);
+ break;
+ }
+
+ ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9");
+ if (ret) {
+ dev_err_probe(dev, ret, "%s set headset_codec fail\n",
+ dai_link->name);
+ break;
+ }
+
+ if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
+ dai_link->codecs->of_node = hdmi_codec;
+ dai_link->ignore = 0;
+ }
+
+ if (dai_link->num_codecs &&
+ strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0)
+ dai_link->ops = &mt8192_rt1015_i2s_ops;
+ }
+
+ of_node_put(headset_codec);
+err_headset_codec:
+ of_node_put(speaker_codec);
+err_speaker_codec:
+ of_node_put(hdmi_codec);
+ return ret;
+}
+
+static int mt8192_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ int ret;
+
+ if (legacy) {
+ ret = mt8192_mt6359_legacy_probe(soc_card_data);
+ if (ret)
+ return ret;
+ } else {
+ struct snd_soc_dai_link *dai_link;
+ int i;
+
+ for_each_card_prelinks(card, i, dai_link)
+ if (dai_link->num_codecs &&
+ strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0)
+ dai_link->ops = &mt8192_rt1015_i2s_ops;
+ }
+
+ ret = mt8192_afe_gpio_init(card->dev);
+ if (ret)
+ return dev_err_probe(card->dev, ret, "%s init gpio error\n", __func__);
+
+ return 0;
+}
+
+static const unsigned int mt8192_pcm_playback_channels[] = { 1, 2 };
+static const unsigned int mt8192_pcm_playback_rates[] = { 48000 };
+
+static const unsigned int mt8192_pcm_capture_channels[] = { 1, 2, 4 };
+static const unsigned int mt8192_pcm_capture_rates[] = {
+ 8000, 16000, 32000, 48000, 96000, 192000
+};
+
+static const struct mtk_pcm_constraints_data mt8192_pcm_constraints[MTK_CONSTRAINT_CAPTURE + 1] = {
+ [MTK_CONSTRAINT_PLAYBACK] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8192_pcm_playback_channels,
+ .count = ARRAY_SIZE(mt8192_pcm_playback_channels)
+ },
+ .rates = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8192_pcm_playback_rates,
+ .count = ARRAY_SIZE(mt8192_pcm_playback_rates)
+ }
+ },
+ [MTK_CONSTRAINT_CAPTURE] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8192_pcm_capture_channels,
+ .count = ARRAY_SIZE(mt8192_pcm_capture_channels)
+ },
+ .rates = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8192_pcm_capture_rates,
+ .count = ARRAY_SIZE(mt8192_pcm_capture_rates)
+ }
+ }
+};
+
+static const struct mtk_soundcard_pdata mt8192_mt6359_rt1015_rt5682_pdata = {
+ .card_name = RT1015_RT5682_CARD_NAME,
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8192_mt6359_rt1015_rt5682_card,
+ .num_jacks = MT8192_JACK_MAX,
+ .pcm_constraints = mt8192_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8192_pcm_constraints),
+ },
+ .soc_probe = mt8192_mt6359_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8192_mt6359_rt1015p_rt5682_pdata = {
+ .card_name = RT1015P_RT5682_CARD_NAME,
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8192_mt6359_rt1015p_rt5682x_card,
+ .num_jacks = MT8192_JACK_MAX,
+ .pcm_constraints = mt8192_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8192_pcm_constraints),
+ },
+ .soc_probe = mt8192_mt6359_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8192_mt6359_rt1015p_rt5682s_pdata = {
+ .card_name = RT1015P_RT5682S_CARD_NAME,
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8192_mt6359_rt1015p_rt5682x_card,
+ .num_jacks = MT8192_JACK_MAX,
+ .pcm_constraints = mt8192_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8192_pcm_constraints),
+ },
+ .soc_probe = mt8192_mt6359_soc_card_probe
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt8192_mt6359_dt_match[] = {
+ {
+ .compatible = RT1015_RT5682_OF_NAME,
+ .data = &mt8192_mt6359_rt1015_rt5682_pdata,
+ },
+ {
+ .compatible = RT1015P_RT5682_OF_NAME,
+ .data = &mt8192_mt6359_rt1015p_rt5682_pdata,
+ },
+ {
+ .compatible = RT1015P_RT5682S_OF_NAME,
+ .data = &mt8192_mt6359_rt1015p_rt5682s_pdata,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt8192_mt6359_dt_match);
+#endif
+
+static const struct dev_pm_ops mt8192_mt6359_pm_ops = {
+ .poweroff = snd_soc_poweroff,
+ .restore = snd_soc_resume,
+};
+
+static struct platform_driver mt8192_mt6359_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+#ifdef CONFIG_OF
+ .of_match_table = mt8192_mt6359_dt_match,
+#endif
+ .pm = &mt8192_mt6359_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+
+module_platform_driver(mt8192_mt6359_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8192-MT6359 ALSA SoC machine driver");
+MODULE_AUTHOR("Jiaxin Yu <jiaxin.yu@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt8192_mt6359 soc card");
diff --git a/sound/soc/mediatek/mt8192/mt8192-reg.h b/sound/soc/mediatek/mt8192/mt8192-reg.h
new file mode 100644
index 000000000000..b9fb80d4afec
--- /dev/null
+++ b/sound/soc/mediatek/mt8192/mt8192-reg.h
@@ -0,0 +1,3133 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8192-reg.h -- Mediatek 8192 audio driver reg definition
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Shane Chien <shane.chien@mediatek.com>
+ */
+
+#ifndef _MT8192_REG_H_
+#define _MT8192_REG_H_
+
+/* reg bit enum */
+enum {
+ MT8192_MEMIF_PBUF_SIZE_32_BYTES,
+ MT8192_MEMIF_PBUF_SIZE_64_BYTES,
+ MT8192_MEMIF_PBUF_SIZE_128_BYTES,
+ MT8192_MEMIF_PBUF_SIZE_256_BYTES,
+ MT8192_MEMIF_PBUF_SIZE_NUM,
+};
+
+/*****************************************************************************
+ * R E G I S T E R D E F I N I T I O N
+ *****************************************************************************/
+/* AUDIO_TOP_CON3 */
+#define BCK_INVERSE_SFT 3
+#define BCK_INVERSE_MASK 0x1
+#define BCK_INVERSE_MASK_SFT (0x1 << 3)
+
+/* AFE_DAC_CON0 */
+#define VUL12_ON_SFT 31
+#define VUL12_ON_MASK 0x1
+#define VUL12_ON_MASK_SFT (0x1 << 31)
+#define MOD_DAI_ON_SFT 30
+#define MOD_DAI_ON_MASK 0x1
+#define MOD_DAI_ON_MASK_SFT (0x1 << 30)
+#define DAI_ON_SFT 29
+#define DAI_ON_MASK 0x1
+#define DAI_ON_MASK_SFT (0x1 << 29)
+#define DAI2_ON_SFT 28
+#define DAI2_ON_MASK 0x1
+#define DAI2_ON_MASK_SFT (0x1 << 28)
+#define VUL6_ON_SFT 23
+#define VUL6_ON_MASK 0x1
+#define VUL6_ON_MASK_SFT (0x1 << 23)
+#define VUL5_ON_SFT 22
+#define VUL5_ON_MASK 0x1
+#define VUL5_ON_MASK_SFT (0x1 << 22)
+#define VUL4_ON_SFT 21
+#define VUL4_ON_MASK 0x1
+#define VUL4_ON_MASK_SFT (0x1 << 21)
+#define VUL3_ON_SFT 20
+#define VUL3_ON_MASK 0x1
+#define VUL3_ON_MASK_SFT (0x1 << 20)
+#define VUL2_ON_SFT 19
+#define VUL2_ON_MASK 0x1
+#define VUL2_ON_MASK_SFT (0x1 << 19)
+#define VUL_ON_SFT 18
+#define VUL_ON_MASK 0x1
+#define VUL_ON_MASK_SFT (0x1 << 18)
+#define AWB2_ON_SFT 17
+#define AWB2_ON_MASK 0x1
+#define AWB2_ON_MASK_SFT (0x1 << 17)
+#define AWB_ON_SFT 16
+#define AWB_ON_MASK 0x1
+#define AWB_ON_MASK_SFT (0x1 << 16)
+#define DL12_ON_SFT 15
+#define DL12_ON_MASK 0x1
+#define DL12_ON_MASK_SFT (0x1 << 15)
+#define DL9_ON_SFT 12
+#define DL9_ON_MASK 0x1
+#define DL9_ON_MASK_SFT (0x1 << 12)
+#define DL8_ON_SFT 11
+#define DL8_ON_MASK 0x1
+#define DL8_ON_MASK_SFT (0x1 << 11)
+#define DL7_ON_SFT 10
+#define DL7_ON_MASK 0x1
+#define DL7_ON_MASK_SFT (0x1 << 10)
+#define DL6_ON_SFT 9
+#define DL6_ON_MASK 0x1
+#define DL6_ON_MASK_SFT (0x1 << 9)
+#define DL5_ON_SFT 8
+#define DL5_ON_MASK 0x1
+#define DL5_ON_MASK_SFT (0x1 << 8)
+#define DL4_ON_SFT 7
+#define DL4_ON_MASK 0x1
+#define DL4_ON_MASK_SFT (0x1 << 7)
+#define DL3_ON_SFT 6
+#define DL3_ON_MASK 0x1
+#define DL3_ON_MASK_SFT (0x1 << 6)
+#define DL2_ON_SFT 5
+#define DL2_ON_MASK 0x1
+#define DL2_ON_MASK_SFT (0x1 << 5)
+#define DL1_ON_SFT 4
+#define DL1_ON_MASK 0x1
+#define DL1_ON_MASK_SFT (0x1 << 4)
+#define HDMI_OUT_ON_SFT 1
+#define HDMI_OUT_ON_MASK 0x1
+#define HDMI_OUT_ON_MASK_SFT (0x1 << 1)
+#define AFE_ON_SFT 0
+#define AFE_ON_MASK 0x1
+#define AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_DAC_MON */
+#define AFE_ON_RETM_SFT 0
+#define AFE_ON_RETM_MASK 0x1
+#define AFE_ON_RETM_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK 0x1
+#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK 0x1
+#define BCK_INV_MASK_SFT (0x1 << 29)
+#define I2SIN_PAD_SEL_SFT 28
+#define I2SIN_PAD_SEL_MASK 0x1
+#define I2SIN_PAD_SEL_MASK_SFT (0x1 << 28)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S1_HD_EN_SFT 12
+#define I2S1_HD_EN_MASK 0x1
+#define I2S1_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S_OUT_MODE_SFT 8
+#define I2S_OUT_MODE_MASK 0xf
+#define I2S_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_PAD_CTRL_SFT 7
+#define INV_PAD_CTRL_MASK 0x1
+#define INV_PAD_CTRL_MASK_SFT (0x1 << 7)
+#define I2S_BYPSRC_SFT 6
+#define I2S_BYPSRC_MASK 0x1
+#define I2S_BYPSRC_MASK_SFT (0x1 << 6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S_FMT_SFT 3
+#define I2S_FMT_MASK 0x1
+#define I2S_FMT_MASK_SFT (0x1 << 3)
+#define I2S_SRC_SFT 2
+#define I2S_SRC_MASK 0x1
+#define I2S_SRC_MASK_SFT (0x1 << 2)
+#define I2S_WLEN_SFT 1
+#define I2S_WLEN_MASK 0x1
+#define I2S_WLEN_MASK_SFT (0x1 << 1)
+#define I2S_EN_SFT 0
+#define I2S_EN_MASK 0x1
+#define I2S_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON1 */
+#define I2S2_LR_SWAP_SFT 31
+#define I2S2_LR_SWAP_MASK 0x1
+#define I2S2_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S2_SEL_O19_O20_SFT 18
+#define I2S2_SEL_O19_O20_MASK 0x1
+#define I2S2_SEL_O19_O20_MASK_SFT (0x1 << 18)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S2_SEL_O03_O04_SFT 16
+#define I2S2_SEL_O03_O04_MASK 0x1
+#define I2S2_SEL_O03_O04_MASK_SFT (0x1 << 16)
+#define I2S2_32BIT_EN_SFT 13
+#define I2S2_32BIT_EN_MASK 0x1
+#define I2S2_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S2_HD_EN_SFT 12
+#define I2S2_HD_EN_MASK 0x1
+#define I2S2_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S2_OUT_MODE_SFT 8
+#define I2S2_OUT_MODE_MASK 0xf
+#define I2S2_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S2_FMT_SFT 3
+#define I2S2_FMT_MASK 0x1
+#define I2S2_FMT_MASK_SFT (0x1 << 3)
+#define I2S2_WLEN_SFT 1
+#define I2S2_WLEN_MASK 0x1
+#define I2S2_WLEN_MASK_SFT (0x1 << 1)
+#define I2S2_EN_SFT 0
+#define I2S2_EN_MASK 0x1
+#define I2S2_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON2 */
+#define I2S3_LR_SWAP_SFT 31
+#define I2S3_LR_SWAP_MASK 0x1
+#define I2S3_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S3_UPDATE_WORD_SFT 24
+#define I2S3_UPDATE_WORD_MASK 0x1f
+#define I2S3_UPDATE_WORD_MASK_SFT (0x1f << 24)
+#define I2S3_BCK_INV_SFT 23
+#define I2S3_BCK_INV_MASK 0x1
+#define I2S3_BCK_INV_MASK_SFT (0x1 << 23)
+#define I2S3_FPGA_BIT_TEST_SFT 22
+#define I2S3_FPGA_BIT_TEST_MASK 0x1
+#define I2S3_FPGA_BIT_TEST_MASK_SFT (0x1 << 22)
+#define I2S3_FPGA_BIT_SFT 21
+#define I2S3_FPGA_BIT_MASK 0x1
+#define I2S3_FPGA_BIT_MASK_SFT (0x1 << 21)
+#define I2S3_LOOPBACK_SFT 20
+#define I2S3_LOOPBACK_MASK 0x1
+#define I2S3_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S3_HD_EN_SFT 12
+#define I2S3_HD_EN_MASK 0x1
+#define I2S3_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S3_OUT_MODE_SFT 8
+#define I2S3_OUT_MODE_MASK 0xf
+#define I2S3_OUT_MODE_MASK_SFT (0xf << 8)
+#define I2S3_FMT_SFT 3
+#define I2S3_FMT_MASK 0x1
+#define I2S3_FMT_MASK_SFT (0x1 << 3)
+#define I2S3_WLEN_SFT 1
+#define I2S3_WLEN_MASK 0x1
+#define I2S3_WLEN_MASK_SFT (0x1 << 1)
+#define I2S3_EN_SFT 0
+#define I2S3_EN_MASK 0x1
+#define I2S3_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON3 */
+#define I2S4_LR_SWAP_SFT 31
+#define I2S4_LR_SWAP_MASK 0x1
+#define I2S4_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S4_32BIT_EN_SFT 13
+#define I2S4_32BIT_EN_MASK 0x1
+#define I2S4_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S4_HD_EN_SFT 12
+#define I2S4_HD_EN_MASK 0x1
+#define I2S4_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S4_OUT_MODE_SFT 8
+#define I2S4_OUT_MODE_MASK 0xf
+#define I2S4_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S4_FMT_SFT 3
+#define I2S4_FMT_MASK 0x1
+#define I2S4_FMT_MASK_SFT (0x1 << 3)
+#define I2S4_WLEN_SFT 1
+#define I2S4_WLEN_MASK 0x1
+#define I2S4_WLEN_MASK_SFT (0x1 << 1)
+#define I2S4_EN_SFT 0
+#define I2S4_EN_MASK 0x1
+#define I2S4_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON4 */
+#define I2S5_LR_SWAP_SFT 31
+#define I2S5_LR_SWAP_MASK 0x1
+#define I2S5_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S5_32BIT_EN_SFT 13
+#define I2S5_32BIT_EN_MASK 0x1
+#define I2S5_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S5_HD_EN_SFT 12
+#define I2S5_HD_EN_MASK 0x1
+#define I2S5_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S5_OUT_MODE_SFT 8
+#define I2S5_OUT_MODE_MASK 0xf
+#define I2S5_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S5_FMT_SFT 3
+#define I2S5_FMT_MASK 0x1
+#define I2S5_FMT_MASK_SFT (0x1 << 3)
+#define I2S5_WLEN_SFT 1
+#define I2S5_WLEN_MASK 0x1
+#define I2S5_WLEN_MASK_SFT (0x1 << 1)
+#define I2S5_EN_SFT 0
+#define I2S5_EN_MASK 0x1
+#define I2S5_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_CONNSYS_I2S_CON */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK 0x1
+#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK 0x1
+#define BCK_INV_MASK_SFT (0x1 << 29)
+#define I2SIN_PAD_SEL_SFT 28
+#define I2SIN_PAD_SEL_MASK 0x1
+#define I2SIN_PAD_SEL_MASK_SFT (0x1 << 28)
+#define I2S_LOOPBACK_SFT 20
+#define I2S_LOOPBACK_MASK 0x1
+#define I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S_MODE_SFT 8
+#define I2S_MODE_MASK 0xf
+#define I2S_MODE_MASK_SFT (0xf << 8)
+#define INV_PAD_CTRL_SFT 7
+#define INV_PAD_CTRL_MASK 0x1
+#define INV_PAD_CTRL_MASK_SFT (0x1 << 7)
+#define I2S_BYPSRC_SFT 6
+#define I2S_BYPSRC_MASK 0x1
+#define I2S_BYPSRC_MASK_SFT (0x1 << 6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S_FMT_SFT 3
+#define I2S_FMT_MASK 0x1
+#define I2S_FMT_MASK_SFT (0x1 << 3)
+#define I2S_SRC_SFT 2
+#define I2S_SRC_MASK 0x1
+#define I2S_SRC_MASK_SFT (0x1 << 2)
+#define I2S_WLEN_SFT 1
+#define I2S_WLEN_MASK 0x1
+#define I2S_WLEN_MASK_SFT (0x1 << 1)
+#define I2S_EN_SFT 0
+#define I2S_EN_MASK 0x1
+#define I2S_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON6 */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK 0x1
+#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK 0x1
+#define BCK_INV_MASK_SFT (0x1 << 29)
+#define I2S6_LOOPBACK_SFT 20
+#define I2S6_LOOPBACK_MASK 0x1
+#define I2S6_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S6_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S6_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S6_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S6_HD_EN_SFT 12
+#define I2S6_HD_EN_MASK 0x1
+#define I2S6_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S6_OUT_MODE_SFT 8
+#define I2S6_OUT_MODE_MASK 0xf
+#define I2S6_OUT_MODE_MASK_SFT (0xf << 8)
+#define I2S6_BYPSRC_SFT 6
+#define I2S6_BYPSRC_MASK 0x1
+#define I2S6_BYPSRC_MASK_SFT (0x1 << 6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S6_FMT_SFT 3
+#define I2S6_FMT_MASK 0x1
+#define I2S6_FMT_MASK_SFT (0x1 << 3)
+#define I2S6_SRC_SFT 2
+#define I2S6_SRC_MASK 0x1
+#define I2S6_SRC_MASK_SFT (0x1 << 2)
+#define I2S6_WLEN_SFT 1
+#define I2S6_WLEN_MASK 0x1
+#define I2S6_WLEN_MASK_SFT (0x1 << 1)
+#define I2S6_EN_SFT 0
+#define I2S6_EN_MASK 0x1
+#define I2S6_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON7 */
+#define I2S7_LR_SWAP_SFT 31
+#define I2S7_LR_SWAP_MASK 0x1
+#define I2S7_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S7_32BIT_EN_SFT 13
+#define I2S7_32BIT_EN_MASK 0x1
+#define I2S7_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S7_HD_EN_SFT 12
+#define I2S7_HD_EN_MASK 0x1
+#define I2S7_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S7_OUT_MODE_SFT 8
+#define I2S7_OUT_MODE_MASK 0xf
+#define I2S7_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S7_FMT_SFT 3
+#define I2S7_FMT_MASK 0x1
+#define I2S7_FMT_MASK_SFT (0x1 << 3)
+#define I2S7_WLEN_SFT 1
+#define I2S7_WLEN_MASK 0x1
+#define I2S7_WLEN_MASK_SFT (0x1 << 1)
+#define I2S7_EN_SFT 0
+#define I2S7_EN_MASK 0x1
+#define I2S7_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON8 */
+#define BCK_NEG_EG_LATCH_SFT 30
+#define BCK_NEG_EG_LATCH_MASK 0x1
+#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30)
+#define BCK_INV_SFT 29
+#define BCK_INV_MASK 0x1
+#define BCK_INV_MASK_SFT (0x1 << 29)
+#define I2S8_LOOPBACK_SFT 20
+#define I2S8_LOOPBACK_MASK 0x1
+#define I2S8_LOOPBACK_MASK_SFT (0x1 << 20)
+#define I2S8_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S8_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S8_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S8_HD_EN_SFT 12
+#define I2S8_HD_EN_MASK 0x1
+#define I2S8_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S8_OUT_MODE_SFT 8
+#define I2S8_OUT_MODE_MASK 0xf
+#define I2S8_OUT_MODE_MASK_SFT (0xf << 8)
+#define I2S8_BYPSRC_SFT 6
+#define I2S8_BYPSRC_MASK 0x1
+#define I2S8_BYPSRC_MASK_SFT (0x1 << 6)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S8_FMT_SFT 3
+#define I2S8_FMT_MASK 0x1
+#define I2S8_FMT_MASK_SFT (0x1 << 3)
+#define I2S8_SRC_SFT 2
+#define I2S8_SRC_MASK 0x1
+#define I2S8_SRC_MASK_SFT (0x1 << 2)
+#define I2S8_WLEN_SFT 1
+#define I2S8_WLEN_MASK 0x1
+#define I2S8_WLEN_MASK_SFT (0x1 << 1)
+#define I2S8_EN_SFT 0
+#define I2S8_EN_MASK 0x1
+#define I2S8_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_I2S_CON9 */
+#define I2S9_LR_SWAP_SFT 31
+#define I2S9_LR_SWAP_MASK 0x1
+#define I2S9_LR_SWAP_MASK_SFT (0x1 << 31)
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1
+#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17)
+#define I2S9_32BIT_EN_SFT 13
+#define I2S9_32BIT_EN_MASK 0x1
+#define I2S9_32BIT_EN_MASK_SFT (0x1 << 13)
+#define I2S9_HD_EN_SFT 12
+#define I2S9_HD_EN_MASK 0x1
+#define I2S9_HD_EN_MASK_SFT (0x1 << 12)
+#define I2S9_OUT_MODE_SFT 8
+#define I2S9_OUT_MODE_MASK 0xf
+#define I2S9_OUT_MODE_MASK_SFT (0xf << 8)
+#define INV_LRCK_SFT 5
+#define INV_LRCK_MASK 0x1
+#define INV_LRCK_MASK_SFT (0x1 << 5)
+#define I2S9_FMT_SFT 3
+#define I2S9_FMT_MASK 0x1
+#define I2S9_FMT_MASK_SFT (0x1 << 3)
+#define I2S9_WLEN_SFT 1
+#define I2S9_WLEN_MASK 0x1
+#define I2S9_WLEN_MASK_SFT (0x1 << 1)
+#define I2S9_EN_SFT 0
+#define I2S9_EN_MASK 0x1
+#define I2S9_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_ASRC_2CH_CON2 */
+#define CHSET_O16BIT_SFT 19
+#define CHSET_O16BIT_MASK 0x1
+#define CHSET_O16BIT_MASK_SFT (0x1 << 19)
+#define CHSET_CLR_IIR_HISTORY_SFT 17
+#define CHSET_CLR_IIR_HISTORY_MASK 0x1
+#define CHSET_CLR_IIR_HISTORY_MASK_SFT (0x1 << 17)
+#define CHSET_IS_MONO_SFT 16
+#define CHSET_IS_MONO_MASK 0x1
+#define CHSET_IS_MONO_MASK_SFT (0x1 << 16)
+#define CHSET_IIR_EN_SFT 11
+#define CHSET_IIR_EN_MASK 0x1
+#define CHSET_IIR_EN_MASK_SFT (0x1 << 11)
+#define CHSET_IIR_STAGE_SFT 8
+#define CHSET_IIR_STAGE_MASK 0x7
+#define CHSET_IIR_STAGE_MASK_SFT (0x7 << 8)
+#define CHSET_STR_CLR_SFT 5
+#define CHSET_STR_CLR_MASK 0x1
+#define CHSET_STR_CLR_MASK_SFT (0x1 << 5)
+#define CHSET_ON_SFT 2
+#define CHSET_ON_MASK 0x1
+#define CHSET_ON_MASK_SFT (0x1 << 2)
+#define COEFF_SRAM_CTRL_SFT 1
+#define COEFF_SRAM_CTRL_MASK 0x1
+#define COEFF_SRAM_CTRL_MASK_SFT (0x1 << 1)
+#define ASM_ON_SFT 0
+#define ASM_ON_MASK 0x1
+#define ASM_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GAIN1_CON0 */
+#define GAIN1_SAMPLE_PER_STEP_SFT 8
+#define GAIN1_SAMPLE_PER_STEP_MASK 0xff
+#define GAIN1_SAMPLE_PER_STEP_MASK_SFT (0xff << 8)
+#define GAIN1_MODE_SFT 4
+#define GAIN1_MODE_MASK 0xf
+#define GAIN1_MODE_MASK_SFT (0xf << 4)
+#define GAIN1_ON_SFT 0
+#define GAIN1_ON_MASK 0x1
+#define GAIN1_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GAIN1_CON1 */
+#define GAIN1_TARGET_SFT 0
+#define GAIN1_TARGET_MASK 0xfffffff
+#define GAIN1_TARGET_MASK_SFT (0xfffffff << 0)
+
+/* AFE_GAIN2_CON0 */
+#define GAIN2_SAMPLE_PER_STEP_SFT 8
+#define GAIN2_SAMPLE_PER_STEP_MASK 0xff
+#define GAIN2_SAMPLE_PER_STEP_MASK_SFT (0xff << 8)
+#define GAIN2_MODE_SFT 4
+#define GAIN2_MODE_MASK 0xf
+#define GAIN2_MODE_MASK_SFT (0xf << 4)
+#define GAIN2_ON_SFT 0
+#define GAIN2_ON_MASK 0x1
+#define GAIN2_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GAIN2_CON1 */
+#define GAIN2_TARGET_SFT 0
+#define GAIN2_TARGET_MASK 0xfffffff
+#define GAIN2_TARGET_MASK_SFT (0xfffffff << 0)
+
+/* AFE_GAIN1_CUR */
+#define AFE_GAIN1_CUR_SFT 0
+#define AFE_GAIN1_CUR_MASK 0xfffffff
+#define AFE_GAIN1_CUR_MASK_SFT (0xfffffff << 0)
+
+/* AFE_GAIN2_CUR */
+#define AFE_GAIN2_CUR_SFT 0
+#define AFE_GAIN2_CUR_MASK 0xfffffff
+#define AFE_GAIN2_CUR_MASK_SFT (0xfffffff << 0)
+
+/* PCM_INTF_CON1 */
+#define PCM_FIX_VALUE_SEL_SFT 31
+#define PCM_FIX_VALUE_SEL_MASK 0x1
+#define PCM_FIX_VALUE_SEL_MASK_SFT (0x1 << 31)
+#define PCM_BUFFER_LOOPBACK_SFT 30
+#define PCM_BUFFER_LOOPBACK_MASK 0x1
+#define PCM_BUFFER_LOOPBACK_MASK_SFT (0x1 << 30)
+#define PCM_PARALLEL_LOOPBACK_SFT 29
+#define PCM_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 29)
+#define PCM_SERIAL_LOOPBACK_SFT 28
+#define PCM_SERIAL_LOOPBACK_MASK 0x1
+#define PCM_SERIAL_LOOPBACK_MASK_SFT (0x1 << 28)
+#define PCM_DAI_PCM_LOOPBACK_SFT 27
+#define PCM_DAI_PCM_LOOPBACK_MASK 0x1
+#define PCM_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 27)
+#define PCM_I2S_PCM_LOOPBACK_SFT 26
+#define PCM_I2S_PCM_LOOPBACK_MASK 0x1
+#define PCM_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 26)
+#define PCM_SYNC_DELSEL_SFT 25
+#define PCM_SYNC_DELSEL_MASK 0x1
+#define PCM_SYNC_DELSEL_MASK_SFT (0x1 << 25)
+#define PCM_TX_LR_SWAP_SFT 24
+#define PCM_TX_LR_SWAP_MASK 0x1
+#define PCM_TX_LR_SWAP_MASK_SFT (0x1 << 24)
+#define PCM_SYNC_OUT_INV_SFT 23
+#define PCM_SYNC_OUT_INV_MASK 0x1
+#define PCM_SYNC_OUT_INV_MASK_SFT (0x1 << 23)
+#define PCM_BCLK_OUT_INV_SFT 22
+#define PCM_BCLK_OUT_INV_MASK 0x1
+#define PCM_BCLK_OUT_INV_MASK_SFT (0x1 << 22)
+#define PCM_SYNC_IN_INV_SFT 21
+#define PCM_SYNC_IN_INV_MASK 0x1
+#define PCM_SYNC_IN_INV_MASK_SFT (0x1 << 21)
+#define PCM_BCLK_IN_INV_SFT 20
+#define PCM_BCLK_IN_INV_MASK 0x1
+#define PCM_BCLK_IN_INV_MASK_SFT (0x1 << 20)
+#define PCM_TX_LCH_RPT_SFT 19
+#define PCM_TX_LCH_RPT_MASK 0x1
+#define PCM_TX_LCH_RPT_MASK_SFT (0x1 << 19)
+#define PCM_VBT_16K_MODE_SFT 18
+#define PCM_VBT_16K_MODE_MASK 0x1
+#define PCM_VBT_16K_MODE_MASK_SFT (0x1 << 18)
+#define PCM_EXT_MODEM_SFT 17
+#define PCM_EXT_MODEM_MASK 0x1
+#define PCM_EXT_MODEM_MASK_SFT (0x1 << 17)
+#define PCM_24BIT_SFT 16
+#define PCM_24BIT_MASK 0x1
+#define PCM_24BIT_MASK_SFT (0x1 << 16)
+#define PCM_WLEN_SFT 14
+#define PCM_WLEN_MASK 0x3
+#define PCM_WLEN_MASK_SFT (0x3 << 14)
+#define PCM_SYNC_LENGTH_SFT 9
+#define PCM_SYNC_LENGTH_MASK 0x1f
+#define PCM_SYNC_LENGTH_MASK_SFT (0x1f << 9)
+#define PCM_SYNC_TYPE_SFT 8
+#define PCM_SYNC_TYPE_MASK 0x1
+#define PCM_SYNC_TYPE_MASK_SFT (0x1 << 8)
+#define PCM_BT_MODE_SFT 7
+#define PCM_BT_MODE_MASK 0x1
+#define PCM_BT_MODE_MASK_SFT (0x1 << 7)
+#define PCM_BYP_ASRC_SFT 6
+#define PCM_BYP_ASRC_MASK 0x1
+#define PCM_BYP_ASRC_MASK_SFT (0x1 << 6)
+#define PCM_SLAVE_SFT 5
+#define PCM_SLAVE_MASK 0x1
+#define PCM_SLAVE_MASK_SFT (0x1 << 5)
+#define PCM_MODE_SFT 3
+#define PCM_MODE_MASK 0x3
+#define PCM_MODE_MASK_SFT (0x3 << 3)
+#define PCM_FMT_SFT 1
+#define PCM_FMT_MASK 0x3
+#define PCM_FMT_MASK_SFT (0x3 << 1)
+#define PCM_EN_SFT 0
+#define PCM_EN_MASK 0x1
+#define PCM_EN_MASK_SFT (0x1 << 0)
+
+/* PCM_INTF_CON2 */
+#define PCM1_TX_FIFO_OV_SFT 31
+#define PCM1_TX_FIFO_OV_MASK 0x1
+#define PCM1_TX_FIFO_OV_MASK_SFT (0x1 << 31)
+#define PCM1_RX_FIFO_OV_SFT 30
+#define PCM1_RX_FIFO_OV_MASK 0x1
+#define PCM1_RX_FIFO_OV_MASK_SFT (0x1 << 30)
+#define PCM2_TX_FIFO_OV_SFT 29
+#define PCM2_TX_FIFO_OV_MASK 0x1
+#define PCM2_TX_FIFO_OV_MASK_SFT (0x1 << 29)
+#define PCM2_RX_FIFO_OV_SFT 28
+#define PCM2_RX_FIFO_OV_MASK 0x1
+#define PCM2_RX_FIFO_OV_MASK_SFT (0x1 << 28)
+#define PCM1_SYNC_GLITCH_SFT 27
+#define PCM1_SYNC_GLITCH_MASK 0x1
+#define PCM1_SYNC_GLITCH_MASK_SFT (0x1 << 27)
+#define PCM2_SYNC_GLITCH_SFT 26
+#define PCM2_SYNC_GLITCH_MASK 0x1
+#define PCM2_SYNC_GLITCH_MASK_SFT (0x1 << 26)
+#define TX3_RCH_DBG_MODE_SFT 17
+#define TX3_RCH_DBG_MODE_MASK 0x1
+#define TX3_RCH_DBG_MODE_MASK_SFT (0x1 << 17)
+#define PCM1_PCM2_LOOPBACK_SFT 16
+#define PCM1_PCM2_LOOPBACK_MASK 0x1
+#define PCM1_PCM2_LOOPBACK_MASK_SFT (0x1 << 16)
+#define DAI_PCM_LOOPBACK_CH_SFT 14
+#define DAI_PCM_LOOPBACK_CH_MASK 0x3
+#define DAI_PCM_LOOPBACK_CH_MASK_SFT (0x3 << 14)
+#define I2S_PCM_LOOPBACK_CH_SFT 12
+#define I2S_PCM_LOOPBACK_CH_MASK 0x3
+#define I2S_PCM_LOOPBACK_CH_MASK_SFT (0x3 << 12)
+#define TX_FIX_VALUE_SFT 0
+#define TX_FIX_VALUE_MASK 0xff
+#define TX_FIX_VALUE_MASK_SFT (0xff << 0)
+
+/* PCM2_INTF_CON */
+#define PCM2_TX_FIX_VALUE_SFT 24
+#define PCM2_TX_FIX_VALUE_MASK 0xff
+#define PCM2_TX_FIX_VALUE_MASK_SFT (0xff << 24)
+#define PCM2_FIX_VALUE_SEL_SFT 23
+#define PCM2_FIX_VALUE_SEL_MASK 0x1
+#define PCM2_FIX_VALUE_SEL_MASK_SFT (0x1 << 23)
+#define PCM2_BUFFER_LOOPBACK_SFT 22
+#define PCM2_BUFFER_LOOPBACK_MASK 0x1
+#define PCM2_BUFFER_LOOPBACK_MASK_SFT (0x1 << 22)
+#define PCM2_PARALLEL_LOOPBACK_SFT 21
+#define PCM2_PARALLEL_LOOPBACK_MASK 0x1
+#define PCM2_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 21)
+#define PCM2_SERIAL_LOOPBACK_SFT 20
+#define PCM2_SERIAL_LOOPBACK_MASK 0x1
+#define PCM2_SERIAL_LOOPBACK_MASK_SFT (0x1 << 20)
+#define PCM2_DAI_PCM_LOOPBACK_SFT 19
+#define PCM2_DAI_PCM_LOOPBACK_MASK 0x1
+#define PCM2_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 19)
+#define PCM2_I2S_PCM_LOOPBACK_SFT 18
+#define PCM2_I2S_PCM_LOOPBACK_MASK 0x1
+#define PCM2_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 18)
+#define PCM2_SYNC_DELSEL_SFT 17
+#define PCM2_SYNC_DELSEL_MASK 0x1
+#define PCM2_SYNC_DELSEL_MASK_SFT (0x1 << 17)
+#define PCM2_TX_LR_SWAP_SFT 16
+#define PCM2_TX_LR_SWAP_MASK 0x1
+#define PCM2_TX_LR_SWAP_MASK_SFT (0x1 << 16)
+#define PCM2_SYNC_IN_INV_SFT 15
+#define PCM2_SYNC_IN_INV_MASK 0x1
+#define PCM2_SYNC_IN_INV_MASK_SFT (0x1 << 15)
+#define PCM2_BCLK_IN_INV_SFT 14
+#define PCM2_BCLK_IN_INV_MASK 0x1
+#define PCM2_BCLK_IN_INV_MASK_SFT (0x1 << 14)
+#define PCM2_TX_LCH_RPT_SFT 13
+#define PCM2_TX_LCH_RPT_MASK 0x1
+#define PCM2_TX_LCH_RPT_MASK_SFT (0x1 << 13)
+#define PCM2_VBT_16K_MODE_SFT 12
+#define PCM2_VBT_16K_MODE_MASK 0x1
+#define PCM2_VBT_16K_MODE_MASK_SFT (0x1 << 12)
+#define PCM2_LOOPBACK_CH_SEL_SFT 10
+#define PCM2_LOOPBACK_CH_SEL_MASK 0x3
+#define PCM2_LOOPBACK_CH_SEL_MASK_SFT (0x3 << 10)
+#define PCM2_TX2_BT_MODE_SFT 8
+#define PCM2_TX2_BT_MODE_MASK 0x1
+#define PCM2_TX2_BT_MODE_MASK_SFT (0x1 << 8)
+#define PCM2_BT_MODE_SFT 7
+#define PCM2_BT_MODE_MASK 0x1
+#define PCM2_BT_MODE_MASK_SFT (0x1 << 7)
+#define PCM2_AFIFO_SFT 6
+#define PCM2_AFIFO_MASK 0x1
+#define PCM2_AFIFO_MASK_SFT (0x1 << 6)
+#define PCM2_WLEN_SFT 5
+#define PCM2_WLEN_MASK 0x1
+#define PCM2_WLEN_MASK_SFT (0x1 << 5)
+#define PCM2_MODE_SFT 3
+#define PCM2_MODE_MASK 0x3
+#define PCM2_MODE_MASK_SFT (0x3 << 3)
+#define PCM2_FMT_SFT 1
+#define PCM2_FMT_MASK 0x3
+#define PCM2_FMT_MASK_SFT (0x3 << 1)
+#define PCM2_EN_SFT 0
+#define PCM2_EN_MASK 0x1
+#define PCM2_EN_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define MTKAIF_RXIF_CLKINV_ADC_SFT 31
+#define MTKAIF_RXIF_CLKINV_ADC_MASK 0x1
+#define MTKAIF_RXIF_CLKINV_ADC_MASK_SFT (0x1 << 31)
+#define MTKAIF_RXIF_BYPASS_SRC_SFT 17
+#define MTKAIF_RXIF_BYPASS_SRC_MASK 0x1
+#define MTKAIF_RXIF_BYPASS_SRC_MASK_SFT (0x1 << 17)
+#define MTKAIF_RXIF_PROTOCOL2_SFT 16
+#define MTKAIF_RXIF_PROTOCOL2_MASK 0x1
+#define MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 16)
+#define MTKAIF_TXIF_BYPASS_SRC_SFT 5
+#define MTKAIF_TXIF_BYPASS_SRC_MASK 0x1
+#define MTKAIF_TXIF_BYPASS_SRC_MASK_SFT (0x1 << 5)
+#define MTKAIF_TXIF_PROTOCOL2_SFT 4
+#define MTKAIF_TXIF_PROTOCOL2_MASK 0x1
+#define MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4)
+#define MTKAIF_TXIF_8TO5_SFT 2
+#define MTKAIF_TXIF_8TO5_MASK 0x1
+#define MTKAIF_TXIF_8TO5_MASK_SFT (0x1 << 2)
+#define MTKAIF_RXIF_8TO5_SFT 1
+#define MTKAIF_RXIF_8TO5_MASK 0x1
+#define MTKAIF_RXIF_8TO5_MASK_SFT (0x1 << 1)
+#define MTKAIF_IF_LOOPBACK1_SFT 0
+#define MTKAIF_IF_LOOPBACK1_MASK 0x1
+#define MTKAIF_IF_LOOPBACK1_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 16
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1
+#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 16)
+#define MTKAIF_RXIF_DELAY_CYCLE_SFT 12
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK 0xf
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT (0xf << 12)
+#define MTKAIF_RXIF_DELAY_DATA_SFT 8
+#define MTKAIF_RXIF_DELAY_DATA_MASK 0x1
+#define MTKAIF_RXIF_DELAY_DATA_MASK_SFT (0x1 << 8)
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7
+#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4)
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL_SFT 28
+#define DL_2_INPUT_MODE_CTL_MASK 0xf
+#define DL_2_INPUT_MODE_CTL_MASK_SFT (0xf << 28)
+#define DL_2_CH1_SATURATION_EN_CTL_SFT 27
+#define DL_2_CH1_SATURATION_EN_CTL_MASK 0x1
+#define DL_2_CH1_SATURATION_EN_CTL_MASK_SFT (0x1 << 27)
+#define DL_2_CH2_SATURATION_EN_CTL_SFT 26
+#define DL_2_CH2_SATURATION_EN_CTL_MASK 0x1
+#define DL_2_CH2_SATURATION_EN_CTL_MASK_SFT (0x1 << 26)
+#define DL_2_OUTPUT_SEL_CTL_SFT 24
+#define DL_2_OUTPUT_SEL_CTL_MASK 0x3
+#define DL_2_OUTPUT_SEL_CTL_MASK_SFT (0x3 << 24)
+#define DL_2_FADEIN_0START_EN_SFT 16
+#define DL_2_FADEIN_0START_EN_MASK 0x3
+#define DL_2_FADEIN_0START_EN_MASK_SFT (0x3 << 16)
+#define DL_DISABLE_HW_CG_CTL_SFT 15
+#define DL_DISABLE_HW_CG_CTL_MASK 0x1
+#define DL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 15)
+#define C_DATA_EN_SEL_CTL_PRE_SFT 14
+#define C_DATA_EN_SEL_CTL_PRE_MASK 0x1
+#define C_DATA_EN_SEL_CTL_PRE_MASK_SFT (0x1 << 14)
+#define DL_2_SIDE_TONE_ON_CTL_PRE_SFT 13
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK 0x1
+#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK_SFT (0x1 << 13)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_SFT 12
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK 0x1
+#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK_SFT (0x1 << 12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_SFT 11
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK 0x1
+#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK_SFT (0x1 << 11)
+#define DL2_ARAMPSP_CTL_PRE_SFT 9
+#define DL2_ARAMPSP_CTL_PRE_MASK 0x3
+#define DL2_ARAMPSP_CTL_PRE_MASK_SFT (0x3 << 9)
+#define DL_2_IIRMODE_CTL_PRE_SFT 6
+#define DL_2_IIRMODE_CTL_PRE_MASK 0x7
+#define DL_2_IIRMODE_CTL_PRE_MASK_SFT (0x7 << 6)
+#define DL_2_VOICE_MODE_CTL_PRE_SFT 5
+#define DL_2_VOICE_MODE_CTL_PRE_MASK 0x1
+#define DL_2_VOICE_MODE_CTL_PRE_MASK_SFT (0x1 << 5)
+#define D2_2_MUTE_CH1_ON_CTL_PRE_SFT 4
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK 0x1
+#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK_SFT (0x1 << 4)
+#define D2_2_MUTE_CH2_ON_CTL_PRE_SFT 3
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK 0x1
+#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK_SFT (0x1 << 3)
+#define DL_2_IIR_ON_CTL_PRE_SFT 2
+#define DL_2_IIR_ON_CTL_PRE_MASK 0x1
+#define DL_2_IIR_ON_CTL_PRE_MASK_SFT (0x1 << 2)
+#define DL_2_GAIN_ON_CTL_PRE_SFT 1
+#define DL_2_GAIN_ON_CTL_PRE_MASK 0x1
+#define DL_2_GAIN_ON_CTL_PRE_MASK_SFT (0x1 << 1)
+#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1
+#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE_SFT 16
+#define DL_2_GAIN_CTL_PRE_MASK 0xffff
+#define DL_2_GAIN_CTL_PRE_MASK_SFT (0xffff << 16)
+#define DL_2_GAIN_MODE_CTL_SFT 0
+#define DL_2_GAIN_MODE_CTL_MASK 0x1
+#define DL_2_GAIN_MODE_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define ULCF_CFG_EN_CTL_SFT 31
+#define ULCF_CFG_EN_CTL_MASK 0x1
+#define ULCF_CFG_EN_CTL_MASK_SFT (0x1 << 31)
+#define UL_DMIC_PHASE_SEL_CH1_SFT 27
+#define UL_DMIC_PHASE_SEL_CH1_MASK 0x7
+#define UL_DMIC_PHASE_SEL_CH1_MASK_SFT (0x7 << 27)
+#define UL_DMIC_PHASE_SEL_CH2_SFT 24
+#define UL_DMIC_PHASE_SEL_CH2_MASK 0x7
+#define UL_DMIC_PHASE_SEL_CH2_MASK_SFT (0x7 << 24)
+#define UL_MODE_3P25M_CH2_CTL_SFT 22
+#define UL_MODE_3P25M_CH2_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22)
+#define UL_MODE_3P25M_CH1_CTL_SFT 21
+#define UL_MODE_3P25M_CH1_CTL_MASK 0x1
+#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21)
+#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7
+#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17)
+#define UL_AP_DMIC_ON_SFT 16
+#define UL_AP_DMIC_ON_MASK 0x1
+#define UL_AP_DMIC_ON_MASK_SFT (0x1 << 16)
+#define DMIC_LOW_POWER_MODE_CTL_SFT 14
+#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3
+#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14)
+#define UL_DISABLE_HW_CG_CTL_SFT 12
+#define UL_DISABLE_HW_CG_CTL_MASK 0x1
+#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12)
+#define UL_IIR_ON_TMP_CTL_SFT 10
+#define UL_IIR_ON_TMP_CTL_MASK 0x1
+#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10)
+#define UL_IIRMODE_CTL_SFT 7
+#define UL_IIRMODE_CTL_MASK 0x7
+#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7)
+#define DIGMIC_4P33M_SEL_SFT 6
+#define DIGMIC_4P33M_SEL_MASK 0x1
+#define DIGMIC_4P33M_SEL_MASK_SFT (0x1 << 6)
+#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1
+#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5)
+#define UL_LOOP_BACK_MODE_CTL_SFT 2
+#define UL_LOOP_BACK_MODE_CTL_MASK 0x1
+#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2)
+#define UL_SDM_3_LEVEL_CTL_SFT 1
+#define UL_SDM_3_LEVEL_CTL_MASK 0x1
+#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1)
+#define UL_SRC_ON_TMP_CTL_SFT 0
+#define UL_SRC_ON_TMP_CTL_MASK 0x1
+#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_SRC_CON1 */
+#define C_DAC_EN_CTL_SFT 27
+#define C_DAC_EN_CTL_MASK 0x1
+#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27)
+#define C_MUTE_SW_CTL_SFT 26
+#define C_MUTE_SW_CTL_MASK 0x1
+#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26)
+#define ASDM_SRC_SEL_CTL_SFT 25
+#define ASDM_SRC_SEL_CTL_MASK 0x1
+#define ASDM_SRC_SEL_CTL_MASK_SFT (0x1 << 25)
+#define C_AMP_DIV_CH2_CTL_SFT 21
+#define C_AMP_DIV_CH2_CTL_MASK 0x7
+#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21)
+#define C_FREQ_DIV_CH2_CTL_SFT 16
+#define C_FREQ_DIV_CH2_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16)
+#define C_SINE_MODE_CH2_CTL_SFT 12
+#define C_SINE_MODE_CH2_CTL_MASK 0xf
+#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12)
+#define C_AMP_DIV_CH1_CTL_SFT 9
+#define C_AMP_DIV_CH1_CTL_MASK 0x7
+#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9)
+#define C_FREQ_DIV_CH1_CTL_SFT 4
+#define C_FREQ_DIV_CH1_CTL_MASK 0x1f
+#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4)
+#define C_SINE_MODE_CH1_CTL_SFT 0
+#define C_SINE_MODE_CH1_CTL_MASK 0xf
+#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0)
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOP_BACK_MODE_CTL_SFT 12
+#define C_LOOP_BACK_MODE_CTL_MASK 0xf
+#define C_LOOP_BACK_MODE_CTL_MASK_SFT (0xf << 12)
+#define ADDA_UL_GAIN_MODE_SFT 8
+#define ADDA_UL_GAIN_MODE_MASK 0x3
+#define ADDA_UL_GAIN_MODE_MASK_SFT (0x3 << 8)
+#define C_EXT_ADC_CTL_SFT 0
+#define C_EXT_ADC_CTL_MASK 0x1
+#define C_EXT_ADC_CTL_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define AFE_ADDA_UL_LR_SWAP_SFT 31
+#define AFE_ADDA_UL_LR_SWAP_MASK 0x1
+#define AFE_ADDA_UL_LR_SWAP_MASK_SFT (0x1 << 31)
+#define AFE_ADDA_CKDIV_RST_SFT 30
+#define AFE_ADDA_CKDIV_RST_MASK 0x1
+#define AFE_ADDA_CKDIV_RST_MASK_SFT (0x1 << 30)
+#define AFE_ADDA_FIFO_AUTO_RST_SFT 29
+#define AFE_ADDA_FIFO_AUTO_RST_MASK 0x1
+#define AFE_ADDA_FIFO_AUTO_RST_MASK_SFT (0x1 << 29)
+#define AFE_ADDA_UL_FIFO_DIGMIC_TESTIN_SFT 21
+#define AFE_ADDA_UL_FIFO_DIGMIC_TESTIN_MASK 0x3
+#define AFE_ADDA_UL_FIFO_DIGMIC_TESTIN_MASK_SFT (0x3 << 21)
+#define AFE_ADDA_UL_FIFO_DIGMIC_WDATA_TESTEN_SFT 20
+#define AFE_ADDA_UL_FIFO_DIGMIC_WDATA_TESTEN_MASK 0x1
+#define AFE_ADDA_UL_FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT (0x1 << 20)
+#define AFE_ADDA6_UL_LR_SWAP_SFT 15
+#define AFE_ADDA6_UL_LR_SWAP_MASK 0x1
+#define AFE_ADDA6_UL_LR_SWAP_MASK_SFT (0x1 << 15)
+#define AFE_ADDA6_CKDIV_RST_SFT 14
+#define AFE_ADDA6_CKDIV_RST_MASK 0x1
+#define AFE_ADDA6_CKDIV_RST_MASK_SFT (0x1 << 14)
+#define AFE_ADDA6_FIFO_AUTO_RST_SFT 13
+#define AFE_ADDA6_FIFO_AUTO_RST_MASK 0x1
+#define AFE_ADDA6_FIFO_AUTO_RST_MASK_SFT (0x1 << 13)
+#define AFE_ADDA6_UL_FIFO_DIGMIC_TESTIN_SFT 5
+#define AFE_ADDA6_UL_FIFO_DIGMIC_TESTIN_MASK 0x3
+#define AFE_ADDA6_UL_FIFO_DIGMIC_TESTIN_MASK_SFT (0x3 << 5)
+#define AFE_ADDA6_UL_FIFO_DIGMIC_WDATA_TESTEN_SFT 4
+#define AFE_ADDA6_UL_FIFO_DIGMIC_WDATA_TESTEN_MASK 0x1
+#define AFE_ADDA6_UL_FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT (0x1 << 4)
+#define ADDA_AFE_ON_SFT 0
+#define ADDA_AFE_ON_MASK 0x1
+#define ADDA_AFE_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_SIDETONE_CON0 */
+#define R_RDY_SFT 30
+#define R_RDY_MASK 0x1
+#define R_RDY_MASK_SFT (0x1 << 30)
+#define W_RDY_SFT 29
+#define W_RDY_MASK 0x1
+#define W_RDY_MASK_SFT (0x1 << 29)
+#define R_W_EN_SFT 25
+#define R_W_EN_MASK 0x1
+#define R_W_EN_MASK_SFT (0x1 << 25)
+#define R_W_SEL_SFT 24
+#define R_W_SEL_MASK 0x1
+#define R_W_SEL_MASK_SFT (0x1 << 24)
+#define SEL_CH2_SFT 23
+#define SEL_CH2_MASK 0x1
+#define SEL_CH2_MASK_SFT (0x1 << 23)
+#define SIDE_TONE_COEFFICIENT_ADDR_SFT 16
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK 0x1f
+#define SIDE_TONE_COEFFICIENT_ADDR_MASK_SFT (0x1f << 16)
+#define SIDE_TONE_COEFFICIENT_SFT 0
+#define SIDE_TONE_COEFFICIENT_MASK 0xffff
+#define SIDE_TONE_COEFFICIENT_MASK_SFT (0xffff << 0)
+
+/* AFE_SIDETONE_COEFF */
+#define SIDE_TONE_COEFF_SFT 0
+#define SIDE_TONE_COEFF_MASK 0xffff
+#define SIDE_TONE_COEFF_MASK_SFT (0xffff << 0)
+
+/* AFE_SIDETONE_CON1 */
+#define STF_BYPASS_MODE_SFT 31
+#define STF_BYPASS_MODE_MASK 0x1
+#define STF_BYPASS_MODE_MASK_SFT (0x1 << 31)
+#define STF_BYPASS_MODE_O28_O29_SFT 30
+#define STF_BYPASS_MODE_O28_O29_MASK 0x1
+#define STF_BYPASS_MODE_O28_O29_MASK_SFT (0x1 << 30)
+#define STF_BYPASS_MODE_I2S4_SFT 29
+#define STF_BYPASS_MODE_I2S4_MASK 0x1
+#define STF_BYPASS_MODE_I2S4_MASK_SFT (0x1 << 29)
+#define STF_BYPASS_MODE_I2S5_SFT 28
+#define STF_BYPASS_MODE_I2S5_MASK 0x1
+#define STF_BYPASS_MODE_I2S5_MASK_SFT (0x1 << 28)
+#define STF_BYPASS_MODE_DL3_SFT 27
+#define STF_BYPASS_MODE_DL3_MASK 0x1
+#define STF_BYPASS_MODE_DL3_MASK_SFT (0x1 << 27)
+#define STF_BYPASS_MODE_I2S7_SFT 26
+#define STF_BYPASS_MODE_I2S7_MASK 0x1
+#define STF_BYPASS_MODE_I2S7_MASK_SFT (0x1 << 26)
+#define STF_BYPASS_MODE_I2S9_SFT 25
+#define STF_BYPASS_MODE_I2S9_MASK 0x1
+#define STF_BYPASS_MODE_I2S9_MASK_SFT (0x1 << 25)
+#define STF_O19O20_OUT_EN_SEL_SFT 13
+#define STF_O19O20_OUT_EN_SEL_MASK 0x1
+#define STF_O19O20_OUT_EN_SEL_MASK_SFT (0x1 << 13)
+#define STF_SOURCE_FROM_O19O20_SFT 12
+#define STF_SOURCE_FROM_O19O20_MASK 0x1
+#define STF_SOURCE_FROM_O19O20_MASK_SFT (0x1 << 12)
+#define SIDE_TONE_ON_SFT 8
+#define SIDE_TONE_ON_MASK 0x1
+#define SIDE_TONE_ON_MASK_SFT (0x1 << 8)
+#define SIDE_TONE_HALF_TAP_NUM_SFT 0
+#define SIDE_TONE_HALF_TAP_NUM_MASK 0x3f
+#define SIDE_TONE_HALF_TAP_NUM_MASK_SFT (0x3f << 0)
+
+/* AFE_SIDETONE_GAIN */
+#define POSITIVE_GAIN_SFT 16
+#define POSITIVE_GAIN_MASK 0x7
+#define POSITIVE_GAIN_MASK_SFT (0x7 << 16)
+#define SIDE_TONE_GAIN_SFT 0
+#define SIDE_TONE_GAIN_MASK 0xffff
+#define SIDE_TONE_GAIN_MASK_SFT (0xffff << 0)
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define USE_3RD_SDM_SFT 28
+#define USE_3RD_SDM_MASK 0x1
+#define USE_3RD_SDM_MASK_SFT (0x1 << 28)
+#define DL_FIFO_START_POINT_SFT 24
+#define DL_FIFO_START_POINT_MASK 0x7
+#define DL_FIFO_START_POINT_MASK_SFT (0x7 << 24)
+#define DL_FIFO_SWAP_SFT 20
+#define DL_FIFO_SWAP_MASK 0x1
+#define DL_FIFO_SWAP_MASK_SFT (0x1 << 20)
+#define C_AUDSDM1ORDSELECT_CTL_SFT 19
+#define C_AUDSDM1ORDSELECT_CTL_MASK 0x1
+#define C_AUDSDM1ORDSELECT_CTL_MASK_SFT (0x1 << 19)
+#define C_SDM7BITSEL_CTL_SFT 18
+#define C_SDM7BITSEL_CTL_MASK 0x1
+#define C_SDM7BITSEL_CTL_MASK_SFT (0x1 << 18)
+#define GAIN_AT_SDM_RST_PRE_CTL_SFT 15
+#define GAIN_AT_SDM_RST_PRE_CTL_MASK 0x1
+#define GAIN_AT_SDM_RST_PRE_CTL_MASK_SFT (0x1 << 15)
+#define DL_DCM_AUTO_IDLE_EN_SFT 14
+#define DL_DCM_AUTO_IDLE_EN_MASK 0x1
+#define DL_DCM_AUTO_IDLE_EN_MASK_SFT (0x1 << 14)
+#define AFE_DL_SRC_DCM_EN_SFT 13
+#define AFE_DL_SRC_DCM_EN_MASK 0x1
+#define AFE_DL_SRC_DCM_EN_MASK_SFT (0x1 << 13)
+#define AFE_DL_POST_SRC_DCM_EN_SFT 12
+#define AFE_DL_POST_SRC_DCM_EN_MASK 0x1
+#define AFE_DL_POST_SRC_DCM_EN_MASK_SFT (0x1 << 12)
+#define AUD_SDM_MONO_SFT 9
+#define AUD_SDM_MONO_MASK 0x1
+#define AUD_SDM_MONO_MASK_SFT (0x1 << 9)
+#define AUD_DC_COMP_EN_SFT 8
+#define AUD_DC_COMP_EN_MASK 0x1
+#define AUD_DC_COMP_EN_MASK_SFT (0x1 << 8)
+#define ATTGAIN_CTL_SFT 0
+#define ATTGAIN_CTL_MASK 0x3f
+#define ATTGAIN_CTL_MASK_SFT (0x3f << 0)
+
+/* AFE_SINEGEN_CON0 */
+#define DAC_EN_SFT 26
+#define DAC_EN_MASK 0x1
+#define DAC_EN_MASK_SFT (0x1 << 26)
+#define MUTE_SW_CH2_SFT 25
+#define MUTE_SW_CH2_MASK 0x1
+#define MUTE_SW_CH2_MASK_SFT (0x1 << 25)
+#define MUTE_SW_CH1_SFT 24
+#define MUTE_SW_CH1_MASK 0x1
+#define MUTE_SW_CH1_MASK_SFT (0x1 << 24)
+#define SINE_MODE_CH2_SFT 20
+#define SINE_MODE_CH2_MASK 0xf
+#define SINE_MODE_CH2_MASK_SFT (0xf << 20)
+#define AMP_DIV_CH2_SFT 17
+#define AMP_DIV_CH2_MASK 0x7
+#define AMP_DIV_CH2_MASK_SFT (0x7 << 17)
+#define FREQ_DIV_CH2_SFT 12
+#define FREQ_DIV_CH2_MASK 0x1f
+#define FREQ_DIV_CH2_MASK_SFT (0x1f << 12)
+#define SINE_MODE_CH1_SFT 8
+#define SINE_MODE_CH1_MASK 0xf
+#define SINE_MODE_CH1_MASK_SFT (0xf << 8)
+#define AMP_DIV_CH1_SFT 5
+#define AMP_DIV_CH1_MASK 0x7
+#define AMP_DIV_CH1_MASK_SFT (0x7 << 5)
+#define FREQ_DIV_CH1_SFT 0
+#define FREQ_DIV_CH1_MASK 0x1f
+#define FREQ_DIV_CH1_MASK_SFT (0x1f << 0)
+
+/* AFE_SINEGEN_CON2 */
+#define INNER_LOOP_BACK_MODE_SFT 0
+#define INNER_LOOP_BACK_MODE_MASK 0x3f
+#define INNER_LOOP_BACK_MODE_MASK_SFT (0x3f << 0)
+
+/* AFE_HD_ENGEN_ENABLE */
+#define AFE_24M_ON_SFT 1
+#define AFE_24M_ON_MASK 0x1
+#define AFE_24M_ON_MASK_SFT (0x1 << 1)
+#define AFE_22M_ON_SFT 0
+#define AFE_22M_ON_MASK 0x1
+#define AFE_22M_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_NLE_FIFO_MON */
+#define DL_NLE_FIFO_WBIN_SFT 8
+#define DL_NLE_FIFO_WBIN_MASK 0xf
+#define DL_NLE_FIFO_WBIN_MASK_SFT (0xf << 8)
+#define DL_NLE_FIFO_RBIN_SFT 4
+#define DL_NLE_FIFO_RBIN_MASK 0xf
+#define DL_NLE_FIFO_RBIN_MASK_SFT (0xf << 4)
+#define DL_NLE_FIFO_RDACTIVE_SFT 3
+#define DL_NLE_FIFO_RDACTIVE_MASK 0x1
+#define DL_NLE_FIFO_RDACTIVE_MASK_SFT (0x1 << 3)
+#define DL_NLE_FIFO_STARTRD_SFT 2
+#define DL_NLE_FIFO_STARTRD_MASK 0x1
+#define DL_NLE_FIFO_STARTRD_MASK_SFT (0x1 << 2)
+#define DL_NLE_FIFO_RD_EMPTY_SFT 1
+#define DL_NLE_FIFO_RD_EMPTY_MASK 0x1
+#define DL_NLE_FIFO_RD_EMPTY_MASK_SFT (0x1 << 1)
+#define DL_NLE_FIFO_WR_FULL_SFT 0
+#define DL_NLE_FIFO_WR_FULL_MASK 0x1
+#define DL_NLE_FIFO_WR_FULL_MASK_SFT (0x1 << 0)
+
+/* AFE_DL1_CON0 */
+#define DL1_MODE_SFT 24
+#define DL1_MODE_MASK 0xf
+#define DL1_MODE_MASK_SFT (0xf << 24)
+#define DL1_MINLEN_SFT 20
+#define DL1_MINLEN_MASK 0xf
+#define DL1_MINLEN_MASK_SFT (0xf << 20)
+#define DL1_MAXLEN_SFT 16
+#define DL1_MAXLEN_MASK 0xf
+#define DL1_MAXLEN_MASK_SFT (0xf << 16)
+#define DL1_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL1_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL1_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL1_PBUF_SIZE_SFT 12
+#define DL1_PBUF_SIZE_MASK 0x3
+#define DL1_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL1_MONO_SFT 8
+#define DL1_MONO_MASK 0x1
+#define DL1_MONO_MASK_SFT (0x1 << 8)
+#define DL1_NORMAL_MODE_SFT 5
+#define DL1_NORMAL_MODE_MASK 0x1
+#define DL1_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL1_HALIGN_SFT 4
+#define DL1_HALIGN_MASK 0x1
+#define DL1_HALIGN_MASK_SFT (0x1 << 4)
+#define DL1_HD_MODE_SFT 0
+#define DL1_HD_MODE_MASK 0x3
+#define DL1_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL2_CON0 */
+#define DL2_MODE_SFT 24
+#define DL2_MODE_MASK 0xf
+#define DL2_MODE_MASK_SFT (0xf << 24)
+#define DL2_MINLEN_SFT 20
+#define DL2_MINLEN_MASK 0xf
+#define DL2_MINLEN_MASK_SFT (0xf << 20)
+#define DL2_MAXLEN_SFT 16
+#define DL2_MAXLEN_MASK 0xf
+#define DL2_MAXLEN_MASK_SFT (0xf << 16)
+#define DL2_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL2_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL2_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL2_PBUF_SIZE_SFT 12
+#define DL2_PBUF_SIZE_MASK 0x3
+#define DL2_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL2_MONO_SFT 8
+#define DL2_MONO_MASK 0x1
+#define DL2_MONO_MASK_SFT (0x1 << 8)
+#define DL2_NORMAL_MODE_SFT 5
+#define DL2_NORMAL_MODE_MASK 0x1
+#define DL2_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL2_HALIGN_SFT 4
+#define DL2_HALIGN_MASK 0x1
+#define DL2_HALIGN_MASK_SFT (0x1 << 4)
+#define DL2_HD_MODE_SFT 0
+#define DL2_HD_MODE_MASK 0x3
+#define DL2_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL3_CON0 */
+#define DL3_MODE_SFT 24
+#define DL3_MODE_MASK 0xf
+#define DL3_MODE_MASK_SFT (0xf << 24)
+#define DL3_MINLEN_SFT 20
+#define DL3_MINLEN_MASK 0xf
+#define DL3_MINLEN_MASK_SFT (0xf << 20)
+#define DL3_MAXLEN_SFT 16
+#define DL3_MAXLEN_MASK 0xf
+#define DL3_MAXLEN_MASK_SFT (0xf << 16)
+#define DL3_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL3_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL3_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL3_PBUF_SIZE_SFT 12
+#define DL3_PBUF_SIZE_MASK 0x3
+#define DL3_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL3_MONO_SFT 8
+#define DL3_MONO_MASK 0x1
+#define DL3_MONO_MASK_SFT (0x1 << 8)
+#define DL3_NORMAL_MODE_SFT 5
+#define DL3_NORMAL_MODE_MASK 0x1
+#define DL3_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL3_HALIGN_SFT 4
+#define DL3_HALIGN_MASK 0x1
+#define DL3_HALIGN_MASK_SFT (0x1 << 4)
+#define DL3_HD_MODE_SFT 0
+#define DL3_HD_MODE_MASK 0x3
+#define DL3_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL4_CON0 */
+#define DL4_MODE_SFT 24
+#define DL4_MODE_MASK 0xf
+#define DL4_MODE_MASK_SFT (0xf << 24)
+#define DL4_MINLEN_SFT 20
+#define DL4_MINLEN_MASK 0xf
+#define DL4_MINLEN_MASK_SFT (0xf << 20)
+#define DL4_MAXLEN_SFT 16
+#define DL4_MAXLEN_MASK 0xf
+#define DL4_MAXLEN_MASK_SFT (0xf << 16)
+#define DL4_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL4_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL4_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL4_PBUF_SIZE_SFT 12
+#define DL4_PBUF_SIZE_MASK 0x3
+#define DL4_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL4_MONO_SFT 8
+#define DL4_MONO_MASK 0x1
+#define DL4_MONO_MASK_SFT (0x1 << 8)
+#define DL4_NORMAL_MODE_SFT 5
+#define DL4_NORMAL_MODE_MASK 0x1
+#define DL4_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL4_HALIGN_SFT 4
+#define DL4_HALIGN_MASK 0x1
+#define DL4_HALIGN_MASK_SFT (0x1 << 4)
+#define DL4_HD_MODE_SFT 0
+#define DL4_HD_MODE_MASK 0x3
+#define DL4_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL5_CON0 */
+#define DL5_MODE_SFT 24
+#define DL5_MODE_MASK 0xf
+#define DL5_MODE_MASK_SFT (0xf << 24)
+#define DL5_MINLEN_SFT 20
+#define DL5_MINLEN_MASK 0xf
+#define DL5_MINLEN_MASK_SFT (0xf << 20)
+#define DL5_MAXLEN_SFT 16
+#define DL5_MAXLEN_MASK 0xf
+#define DL5_MAXLEN_MASK_SFT (0xf << 16)
+#define DL5_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL5_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL5_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL5_PBUF_SIZE_SFT 12
+#define DL5_PBUF_SIZE_MASK 0x3
+#define DL5_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL5_MONO_SFT 8
+#define DL5_MONO_MASK 0x1
+#define DL5_MONO_MASK_SFT (0x1 << 8)
+#define DL5_NORMAL_MODE_SFT 5
+#define DL5_NORMAL_MODE_MASK 0x1
+#define DL5_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL5_HALIGN_SFT 4
+#define DL5_HALIGN_MASK 0x1
+#define DL5_HALIGN_MASK_SFT (0x1 << 4)
+#define DL5_HD_MODE_SFT 0
+#define DL5_HD_MODE_MASK 0x3
+#define DL5_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL6_CON0 */
+#define DL6_MODE_SFT 24
+#define DL6_MODE_MASK 0xf
+#define DL6_MODE_MASK_SFT (0xf << 24)
+#define DL6_MINLEN_SFT 20
+#define DL6_MINLEN_MASK 0xf
+#define DL6_MINLEN_MASK_SFT (0xf << 20)
+#define DL6_MAXLEN_SFT 16
+#define DL6_MAXLEN_MASK 0xf
+#define DL6_MAXLEN_MASK_SFT (0xf << 16)
+#define DL6_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL6_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL6_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL6_PBUF_SIZE_SFT 12
+#define DL6_PBUF_SIZE_MASK 0x3
+#define DL6_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL6_MONO_SFT 8
+#define DL6_MONO_MASK 0x1
+#define DL6_MONO_MASK_SFT (0x1 << 8)
+#define DL6_NORMAL_MODE_SFT 5
+#define DL6_NORMAL_MODE_MASK 0x1
+#define DL6_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL6_HALIGN_SFT 4
+#define DL6_HALIGN_MASK 0x1
+#define DL6_HALIGN_MASK_SFT (0x1 << 4)
+#define DL6_HD_MODE_SFT 0
+#define DL6_HD_MODE_MASK 0x3
+#define DL6_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL7_CON0 */
+#define DL7_MODE_SFT 24
+#define DL7_MODE_MASK 0xf
+#define DL7_MODE_MASK_SFT (0xf << 24)
+#define DL7_MINLEN_SFT 20
+#define DL7_MINLEN_MASK 0xf
+#define DL7_MINLEN_MASK_SFT (0xf << 20)
+#define DL7_MAXLEN_SFT 16
+#define DL7_MAXLEN_MASK 0xf
+#define DL7_MAXLEN_MASK_SFT (0xf << 16)
+#define DL7_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL7_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL7_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL7_PBUF_SIZE_SFT 12
+#define DL7_PBUF_SIZE_MASK 0x3
+#define DL7_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL7_MONO_SFT 8
+#define DL7_MONO_MASK 0x1
+#define DL7_MONO_MASK_SFT (0x1 << 8)
+#define DL7_NORMAL_MODE_SFT 5
+#define DL7_NORMAL_MODE_MASK 0x1
+#define DL7_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL7_HALIGN_SFT 4
+#define DL7_HALIGN_MASK 0x1
+#define DL7_HALIGN_MASK_SFT (0x1 << 4)
+#define DL7_HD_MODE_SFT 0
+#define DL7_HD_MODE_MASK 0x3
+#define DL7_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL8_CON0 */
+#define DL8_MODE_SFT 24
+#define DL8_MODE_MASK 0xf
+#define DL8_MODE_MASK_SFT (0xf << 24)
+#define DL8_MINLEN_SFT 20
+#define DL8_MINLEN_MASK 0xf
+#define DL8_MINLEN_MASK_SFT (0xf << 20)
+#define DL8_MAXLEN_SFT 16
+#define DL8_MAXLEN_MASK 0xf
+#define DL8_MAXLEN_MASK_SFT (0xf << 16)
+#define DL8_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL8_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL8_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL8_PBUF_SIZE_SFT 12
+#define DL8_PBUF_SIZE_MASK 0x3
+#define DL8_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL8_MONO_SFT 8
+#define DL8_MONO_MASK 0x1
+#define DL8_MONO_MASK_SFT (0x1 << 8)
+#define DL8_NORMAL_MODE_SFT 5
+#define DL8_NORMAL_MODE_MASK 0x1
+#define DL8_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL8_HALIGN_SFT 4
+#define DL8_HALIGN_MASK 0x1
+#define DL8_HALIGN_MASK_SFT (0x1 << 4)
+#define DL8_HD_MODE_SFT 0
+#define DL8_HD_MODE_MASK 0x3
+#define DL8_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL9_CON0 */
+#define DL9_MODE_SFT 24
+#define DL9_MODE_MASK 0xf
+#define DL9_MODE_MASK_SFT (0xf << 24)
+#define DL9_MINLEN_SFT 20
+#define DL9_MINLEN_MASK 0xf
+#define DL9_MINLEN_MASK_SFT (0xf << 20)
+#define DL9_MAXLEN_SFT 16
+#define DL9_MAXLEN_MASK 0xf
+#define DL9_MAXLEN_MASK_SFT (0xf << 16)
+#define DL9_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL9_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL9_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL9_PBUF_SIZE_SFT 12
+#define DL9_PBUF_SIZE_MASK 0x3
+#define DL9_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL9_MONO_SFT 8
+#define DL9_MONO_MASK 0x1
+#define DL9_MONO_MASK_SFT (0x1 << 8)
+#define DL9_NORMAL_MODE_SFT 5
+#define DL9_NORMAL_MODE_MASK 0x1
+#define DL9_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL9_HALIGN_SFT 4
+#define DL9_HALIGN_MASK 0x1
+#define DL9_HALIGN_MASK_SFT (0x1 << 4)
+#define DL9_HD_MODE_SFT 0
+#define DL9_HD_MODE_MASK 0x3
+#define DL9_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DL12_CON0 */
+#define DL12_MODE_SFT 24
+#define DL12_MODE_MASK 0xf
+#define DL12_MODE_MASK_SFT (0xf << 24)
+#define DL12_MINLEN_SFT 20
+#define DL12_MINLEN_MASK 0xf
+#define DL12_MINLEN_MASK_SFT (0xf << 20)
+#define DL12_MAXLEN_SFT 16
+#define DL12_MAXLEN_MASK 0xf
+#define DL12_MAXLEN_MASK_SFT (0xf << 16)
+#define DL12_SW_CLEAR_BUF_EMPTY_SFT 15
+#define DL12_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define DL12_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define DL12_PBUF_SIZE_SFT 12
+#define DL12_PBUF_SIZE_MASK 0x3
+#define DL12_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define DL12_4CH_EN_SFT 11
+#define DL12_4CH_EN_MASK 0x1
+#define DL12_4CH_EN_MASK_SFT (0x1 << 11)
+#define DL12_MONO_SFT 8
+#define DL12_MONO_MASK 0x1
+#define DL12_MONO_MASK_SFT (0x1 << 8)
+#define DL12_NORMAL_MODE_SFT 5
+#define DL12_NORMAL_MODE_MASK 0x1
+#define DL12_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DL12_HALIGN_SFT 4
+#define DL12_HALIGN_MASK 0x1
+#define DL12_HALIGN_MASK_SFT (0x1 << 4)
+#define DL12_HD_MODE_SFT 0
+#define DL12_HD_MODE_MASK 0x3
+#define DL12_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_AWB_CON0 */
+#define AWB_MODE_SFT 24
+#define AWB_MODE_MASK 0xf
+#define AWB_MODE_MASK_SFT (0xf << 24)
+#define AWB_SW_CLEAR_BUF_FULL_SFT 15
+#define AWB_SW_CLEAR_BUF_FULL_MASK 0x1
+#define AWB_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define AWB_R_MONO_SFT 9
+#define AWB_R_MONO_MASK 0x1
+#define AWB_R_MONO_MASK_SFT (0x1 << 9)
+#define AWB_MONO_SFT 8
+#define AWB_MONO_MASK 0x1
+#define AWB_MONO_MASK_SFT (0x1 << 8)
+#define AWB_WR_SIGN_SFT 6
+#define AWB_WR_SIGN_MASK 0x1
+#define AWB_WR_SIGN_MASK_SFT (0x1 << 6)
+#define AWB_NORMAL_MODE_SFT 5
+#define AWB_NORMAL_MODE_MASK 0x1
+#define AWB_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define AWB_HALIGN_SFT 4
+#define AWB_HALIGN_MASK 0x1
+#define AWB_HALIGN_MASK_SFT (0x1 << 4)
+#define AWB_HD_MODE_SFT 0
+#define AWB_HD_MODE_MASK 0x3
+#define AWB_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_AWB2_CON0 */
+#define AWB2_MODE_SFT 24
+#define AWB2_MODE_MASK 0xf
+#define AWB2_MODE_MASK_SFT (0xf << 24)
+#define AWB2_SW_CLEAR_BUF_FULL_SFT 15
+#define AWB2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define AWB2_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define AWB2_R_MONO_SFT 9
+#define AWB2_R_MONO_MASK 0x1
+#define AWB2_R_MONO_MASK_SFT (0x1 << 9)
+#define AWB2_MONO_SFT 8
+#define AWB2_MONO_MASK 0x1
+#define AWB2_MONO_MASK_SFT (0x1 << 8)
+#define AWB2_WR_SIGN_SFT 6
+#define AWB2_WR_SIGN_MASK 0x1
+#define AWB2_WR_SIGN_MASK_SFT (0x1 << 6)
+#define AWB2_NORMAL_MODE_SFT 5
+#define AWB2_NORMAL_MODE_MASK 0x1
+#define AWB2_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define AWB2_HALIGN_SFT 4
+#define AWB2_HALIGN_MASK 0x1
+#define AWB2_HALIGN_MASK_SFT (0x1 << 4)
+#define AWB2_HD_MODE_SFT 0
+#define AWB2_HD_MODE_MASK 0x3
+#define AWB2_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL_CON0 */
+#define VUL_MODE_SFT 24
+#define VUL_MODE_MASK 0xf
+#define VUL_MODE_MASK_SFT (0xf << 24)
+#define VUL_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL_R_MONO_SFT 9
+#define VUL_R_MONO_MASK 0x1
+#define VUL_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL_MONO_SFT 8
+#define VUL_MONO_MASK 0x1
+#define VUL_MONO_MASK_SFT (0x1 << 8)
+#define VUL_WR_SIGN_SFT 6
+#define VUL_WR_SIGN_MASK 0x1
+#define VUL_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL_NORMAL_MODE_SFT 5
+#define VUL_NORMAL_MODE_MASK 0x1
+#define VUL_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL_HALIGN_SFT 4
+#define VUL_HALIGN_MASK 0x1
+#define VUL_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL_HD_MODE_SFT 0
+#define VUL_HD_MODE_MASK 0x3
+#define VUL_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL12_CON0 */
+#define VUL12_MODE_SFT 24
+#define VUL12_MODE_MASK 0xf
+#define VUL12_MODE_MASK_SFT (0xf << 24)
+#define VUL12_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL12_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL12_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL12_4CH_EN_SFT 11
+#define VUL12_4CH_EN_MASK 0x1
+#define VUL12_4CH_EN_MASK_SFT (0x1 << 11)
+#define VUL12_R_MONO_SFT 9
+#define VUL12_R_MONO_MASK 0x1
+#define VUL12_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL12_MONO_SFT 8
+#define VUL12_MONO_MASK 0x1
+#define VUL12_MONO_MASK_SFT (0x1 << 8)
+#define VUL12_WR_SIGN_SFT 6
+#define VUL12_WR_SIGN_MASK 0x1
+#define VUL12_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL12_NORMAL_MODE_SFT 5
+#define VUL12_NORMAL_MODE_MASK 0x1
+#define VUL12_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL12_HALIGN_SFT 4
+#define VUL12_HALIGN_MASK 0x1
+#define VUL12_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL12_HD_MODE_SFT 0
+#define VUL12_HD_MODE_MASK 0x3
+#define VUL12_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL2_CON0 */
+#define VUL2_MODE_SFT 24
+#define VUL2_MODE_MASK 0xf
+#define VUL2_MODE_MASK_SFT (0xf << 24)
+#define VUL2_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL2_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL2_R_MONO_SFT 9
+#define VUL2_R_MONO_MASK 0x1
+#define VUL2_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL2_MONO_SFT 8
+#define VUL2_MONO_MASK 0x1
+#define VUL2_MONO_MASK_SFT (0x1 << 8)
+#define VUL2_WR_SIGN_SFT 6
+#define VUL2_WR_SIGN_MASK 0x1
+#define VUL2_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL2_NORMAL_MODE_SFT 5
+#define VUL2_NORMAL_MODE_MASK 0x1
+#define VUL2_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL2_HALIGN_SFT 4
+#define VUL2_HALIGN_MASK 0x1
+#define VUL2_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL2_HD_MODE_SFT 0
+#define VUL2_HD_MODE_MASK 0x3
+#define VUL2_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL3_CON0 */
+#define VUL3_MODE_SFT 24
+#define VUL3_MODE_MASK 0xf
+#define VUL3_MODE_MASK_SFT (0xf << 24)
+#define VUL3_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL3_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL3_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL3_R_MONO_SFT 9
+#define VUL3_R_MONO_MASK 0x1
+#define VUL3_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL3_MONO_SFT 8
+#define VUL3_MONO_MASK 0x1
+#define VUL3_MONO_MASK_SFT (0x1 << 8)
+#define VUL3_WR_SIGN_SFT 6
+#define VUL3_WR_SIGN_MASK 0x1
+#define VUL3_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL3_NORMAL_MODE_SFT 5
+#define VUL3_NORMAL_MODE_MASK 0x1
+#define VUL3_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL3_HALIGN_SFT 4
+#define VUL3_HALIGN_MASK 0x1
+#define VUL3_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL3_HD_MODE_SFT 0
+#define VUL3_HD_MODE_MASK 0x3
+#define VUL3_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL4_CON0 */
+#define VUL4_MODE_SFT 24
+#define VUL4_MODE_MASK 0xf
+#define VUL4_MODE_MASK_SFT (0xf << 24)
+#define VUL4_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL4_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL4_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL4_R_MONO_SFT 9
+#define VUL4_R_MONO_MASK 0x1
+#define VUL4_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL4_MONO_SFT 8
+#define VUL4_MONO_MASK 0x1
+#define VUL4_MONO_MASK_SFT (0x1 << 8)
+#define VUL4_WR_SIGN_SFT 6
+#define VUL4_WR_SIGN_MASK 0x1
+#define VUL4_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL4_NORMAL_MODE_SFT 5
+#define VUL4_NORMAL_MODE_MASK 0x1
+#define VUL4_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL4_HALIGN_SFT 4
+#define VUL4_HALIGN_MASK 0x1
+#define VUL4_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL4_HD_MODE_SFT 0
+#define VUL4_HD_MODE_MASK 0x3
+#define VUL4_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL5_CON0 */
+#define VUL5_MODE_SFT 24
+#define VUL5_MODE_MASK 0xf
+#define VUL5_MODE_MASK_SFT (0xf << 24)
+#define VUL5_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL5_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL5_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL5_R_MONO_SFT 9
+#define VUL5_R_MONO_MASK 0x1
+#define VUL5_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL5_MONO_SFT 8
+#define VUL5_MONO_MASK 0x1
+#define VUL5_MONO_MASK_SFT (0x1 << 8)
+#define VUL5_WR_SIGN_SFT 6
+#define VUL5_WR_SIGN_MASK 0x1
+#define VUL5_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL5_NORMAL_MODE_SFT 5
+#define VUL5_NORMAL_MODE_MASK 0x1
+#define VUL5_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL5_HALIGN_SFT 4
+#define VUL5_HALIGN_MASK 0x1
+#define VUL5_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL5_HD_MODE_SFT 0
+#define VUL5_HD_MODE_MASK 0x3
+#define VUL5_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_VUL6_CON0 */
+#define VUL6_MODE_SFT 24
+#define VUL6_MODE_MASK 0xf
+#define VUL6_MODE_MASK_SFT (0xf << 24)
+#define VUL6_SW_CLEAR_BUF_FULL_SFT 15
+#define VUL6_SW_CLEAR_BUF_FULL_MASK 0x1
+#define VUL6_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define VUL6_R_MONO_SFT 9
+#define VUL6_R_MONO_MASK 0x1
+#define VUL6_R_MONO_MASK_SFT (0x1 << 9)
+#define VUL6_MONO_SFT 8
+#define VUL6_MONO_MASK 0x1
+#define VUL6_MONO_MASK_SFT (0x1 << 8)
+#define VUL6_WR_SIGN_SFT 6
+#define VUL6_WR_SIGN_MASK 0x1
+#define VUL6_WR_SIGN_MASK_SFT (0x1 << 6)
+#define VUL6_NORMAL_MODE_SFT 5
+#define VUL6_NORMAL_MODE_MASK 0x1
+#define VUL6_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define VUL6_HALIGN_SFT 4
+#define VUL6_HALIGN_MASK 0x1
+#define VUL6_HALIGN_MASK_SFT (0x1 << 4)
+#define VUL6_HD_MODE_SFT 0
+#define VUL6_HD_MODE_MASK 0x3
+#define VUL6_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DAI_CON0 */
+#define DAI_MODE_SFT 24
+#define DAI_MODE_MASK 0x3
+#define DAI_MODE_MASK_SFT (0x3 << 24)
+#define DAI_SW_CLEAR_BUF_FULL_SFT 15
+#define DAI_SW_CLEAR_BUF_FULL_MASK 0x1
+#define DAI_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define DAI_DUPLICATE_WR_SFT 10
+#define DAI_DUPLICATE_WR_MASK 0x1
+#define DAI_DUPLICATE_WR_MASK_SFT (0x1 << 10)
+#define DAI_MONO_SFT 8
+#define DAI_MONO_MASK 0x1
+#define DAI_MONO_MASK_SFT (0x1 << 8)
+#define DAI_WR_SIGN_SFT 6
+#define DAI_WR_SIGN_MASK 0x1
+#define DAI_WR_SIGN_MASK_SFT (0x1 << 6)
+#define DAI_NORMAL_MODE_SFT 5
+#define DAI_NORMAL_MODE_MASK 0x1
+#define DAI_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DAI_HALIGN_SFT 4
+#define DAI_HALIGN_MASK 0x1
+#define DAI_HALIGN_MASK_SFT (0x1 << 4)
+#define DAI_HD_MODE_SFT 0
+#define DAI_HD_MODE_MASK 0x3
+#define DAI_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_MOD_DAI_CON0 */
+#define MOD_DAI_MODE_SFT 24
+#define MOD_DAI_MODE_MASK 0x3
+#define MOD_DAI_MODE_MASK_SFT (0x3 << 24)
+#define MOD_DAI_SW_CLEAR_BUF_FULL_SFT 15
+#define MOD_DAI_SW_CLEAR_BUF_FULL_MASK 0x1
+#define MOD_DAI_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define MOD_DAI_DUPLICATE_WR_SFT 10
+#define MOD_DAI_DUPLICATE_WR_MASK 0x1
+#define MOD_DAI_DUPLICATE_WR_MASK_SFT (0x1 << 10)
+#define MOD_DAI_MONO_SFT 8
+#define MOD_DAI_MONO_MASK 0x1
+#define MOD_DAI_MONO_MASK_SFT (0x1 << 8)
+#define MOD_DAI_WR_SIGN_SFT 6
+#define MOD_DAI_WR_SIGN_MASK 0x1
+#define MOD_DAI_WR_SIGN_MASK_SFT (0x1 << 6)
+#define MOD_DAI_NORMAL_MODE_SFT 5
+#define MOD_DAI_NORMAL_MODE_MASK 0x1
+#define MOD_DAI_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define MOD_DAI_HALIGN_SFT 4
+#define MOD_DAI_HALIGN_MASK 0x1
+#define MOD_DAI_HALIGN_MASK_SFT (0x1 << 4)
+#define MOD_DAI_HD_MODE_SFT 0
+#define MOD_DAI_HD_MODE_MASK 0x3
+#define MOD_DAI_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_DAI2_CON0 */
+#define DAI2_MODE_SFT 24
+#define DAI2_MODE_MASK 0xf
+#define DAI2_MODE_MASK_SFT (0xf << 24)
+#define DAI2_SW_CLEAR_BUF_FULL_SFT 15
+#define DAI2_SW_CLEAR_BUF_FULL_MASK 0x1
+#define DAI2_SW_CLEAR_BUF_FULL_MASK_SFT (0x1 << 15)
+#define DAI2_DUPLICATE_WR_SFT 10
+#define DAI2_DUPLICATE_WR_MASK 0x1
+#define DAI2_DUPLICATE_WR_MASK_SFT (0x1 << 10)
+#define DAI2_MONO_SFT 8
+#define DAI2_MONO_MASK 0x1
+#define DAI2_MONO_MASK_SFT (0x1 << 8)
+#define DAI2_WR_SIGN_SFT 6
+#define DAI2_WR_SIGN_MASK 0x1
+#define DAI2_WR_SIGN_MASK_SFT (0x1 << 6)
+#define DAI2_NORMAL_MODE_SFT 5
+#define DAI2_NORMAL_MODE_MASK 0x1
+#define DAI2_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define DAI2_HALIGN_SFT 4
+#define DAI2_HALIGN_MASK 0x1
+#define DAI2_HALIGN_MASK_SFT (0x1 << 4)
+#define DAI2_HD_MODE_SFT 0
+#define DAI2_HD_MODE_MASK 0x3
+#define DAI2_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_MEMIF_CON0 */
+#define CPU_COMPACT_MODE_SFT 2
+#define CPU_COMPACT_MODE_MASK 0x1
+#define CPU_COMPACT_MODE_MASK_SFT (0x1 << 2)
+#define CPU_HD_ALIGN_SFT 1
+#define CPU_HD_ALIGN_MASK 0x1
+#define CPU_HD_ALIGN_MASK_SFT (0x1 << 1)
+#define SYSRAM_SIGN_SFT 0
+#define SYSRAM_SIGN_MASK 0x1
+#define SYSRAM_SIGN_MASK_SFT (0x1 << 0)
+
+/* AFE_HDMI_OUT_CON0 */
+#define HDMI_CH_NUM_SFT 24
+#define HDMI_CH_NUM_MASK 0xf
+#define HDMI_CH_NUM_MASK_SFT (0xf << 24)
+#define HDMI_OUT_MINLEN_SFT 20
+#define HDMI_OUT_MINLEN_MASK 0xf
+#define HDMI_OUT_MINLEN_MASK_SFT (0xf << 20)
+#define HDMI_OUT_MAXLEN_SFT 16
+#define HDMI_OUT_MAXLEN_MASK 0xf
+#define HDMI_OUT_MAXLEN_MASK_SFT (0xf << 16)
+#define HDMI_OUT_SW_CLEAR_BUF_EMPTY_SFT 15
+#define HDMI_OUT_SW_CLEAR_BUF_EMPTY_MASK 0x1
+#define HDMI_OUT_SW_CLEAR_BUF_EMPTY_MASK_SFT (0x1 << 15)
+#define HDMI_OUT_PBUF_SIZE_SFT 12
+#define HDMI_OUT_PBUF_SIZE_MASK 0x3
+#define HDMI_OUT_PBUF_SIZE_MASK_SFT (0x3 << 12)
+#define HDMI_OUT_NORMAL_MODE_SFT 5
+#define HDMI_OUT_NORMAL_MODE_MASK 0x1
+#define HDMI_OUT_NORMAL_MODE_MASK_SFT (0x1 << 5)
+#define HDMI_OUT_HALIGN_SFT 4
+#define HDMI_OUT_HALIGN_MASK 0x1
+#define HDMI_OUT_HALIGN_MASK_SFT (0x1 << 4)
+#define HDMI_OUT_HD_MODE_SFT 0
+#define HDMI_OUT_HD_MODE_MASK 0x3
+#define HDMI_OUT_HD_MODE_MASK_SFT (0x3 << 0)
+
+/* AFE_IRQ_MCU_CON0 */
+#define IRQ31_MCU_ON_SFT 31
+#define IRQ31_MCU_ON_MASK 0x1
+#define IRQ31_MCU_ON_MASK_SFT (0x1 << 31)
+#define IRQ26_MCU_ON_SFT 26
+#define IRQ26_MCU_ON_MASK 0x1
+#define IRQ26_MCU_ON_MASK_SFT (0x1 << 26)
+#define IRQ25_MCU_ON_SFT 25
+#define IRQ25_MCU_ON_MASK 0x1
+#define IRQ25_MCU_ON_MASK_SFT (0x1 << 25)
+#define IRQ24_MCU_ON_SFT 24
+#define IRQ24_MCU_ON_MASK 0x1
+#define IRQ24_MCU_ON_MASK_SFT (0x1 << 24)
+#define IRQ23_MCU_ON_SFT 23
+#define IRQ23_MCU_ON_MASK 0x1
+#define IRQ23_MCU_ON_MASK_SFT (0x1 << 23)
+#define IRQ22_MCU_ON_SFT 22
+#define IRQ22_MCU_ON_MASK 0x1
+#define IRQ22_MCU_ON_MASK_SFT (0x1 << 22)
+#define IRQ21_MCU_ON_SFT 21
+#define IRQ21_MCU_ON_MASK 0x1
+#define IRQ21_MCU_ON_MASK_SFT (0x1 << 21)
+#define IRQ20_MCU_ON_SFT 20
+#define IRQ20_MCU_ON_MASK 0x1
+#define IRQ20_MCU_ON_MASK_SFT (0x1 << 20)
+#define IRQ19_MCU_ON_SFT 19
+#define IRQ19_MCU_ON_MASK 0x1
+#define IRQ19_MCU_ON_MASK_SFT (0x1 << 19)
+#define IRQ18_MCU_ON_SFT 18
+#define IRQ18_MCU_ON_MASK 0x1
+#define IRQ18_MCU_ON_MASK_SFT (0x1 << 18)
+#define IRQ17_MCU_ON_SFT 17
+#define IRQ17_MCU_ON_MASK 0x1
+#define IRQ17_MCU_ON_MASK_SFT (0x1 << 17)
+#define IRQ16_MCU_ON_SFT 16
+#define IRQ16_MCU_ON_MASK 0x1
+#define IRQ16_MCU_ON_MASK_SFT (0x1 << 16)
+#define IRQ15_MCU_ON_SFT 15
+#define IRQ15_MCU_ON_MASK 0x1
+#define IRQ15_MCU_ON_MASK_SFT (0x1 << 15)
+#define IRQ14_MCU_ON_SFT 14
+#define IRQ14_MCU_ON_MASK 0x1
+#define IRQ14_MCU_ON_MASK_SFT (0x1 << 14)
+#define IRQ13_MCU_ON_SFT 13
+#define IRQ13_MCU_ON_MASK 0x1
+#define IRQ13_MCU_ON_MASK_SFT (0x1 << 13)
+#define IRQ12_MCU_ON_SFT 12
+#define IRQ12_MCU_ON_MASK 0x1
+#define IRQ12_MCU_ON_MASK_SFT (0x1 << 12)
+#define IRQ11_MCU_ON_SFT 11
+#define IRQ11_MCU_ON_MASK 0x1
+#define IRQ11_MCU_ON_MASK_SFT (0x1 << 11)
+#define IRQ10_MCU_ON_SFT 10
+#define IRQ10_MCU_ON_MASK 0x1
+#define IRQ10_MCU_ON_MASK_SFT (0x1 << 10)
+#define IRQ9_MCU_ON_SFT 9
+#define IRQ9_MCU_ON_MASK 0x1
+#define IRQ9_MCU_ON_MASK_SFT (0x1 << 9)
+#define IRQ8_MCU_ON_SFT 8
+#define IRQ8_MCU_ON_MASK 0x1
+#define IRQ8_MCU_ON_MASK_SFT (0x1 << 8)
+#define IRQ7_MCU_ON_SFT 7
+#define IRQ7_MCU_ON_MASK 0x1
+#define IRQ7_MCU_ON_MASK_SFT (0x1 << 7)
+#define IRQ6_MCU_ON_SFT 6
+#define IRQ6_MCU_ON_MASK 0x1
+#define IRQ6_MCU_ON_MASK_SFT (0x1 << 6)
+#define IRQ5_MCU_ON_SFT 5
+#define IRQ5_MCU_ON_MASK 0x1
+#define IRQ5_MCU_ON_MASK_SFT (0x1 << 5)
+#define IRQ4_MCU_ON_SFT 4
+#define IRQ4_MCU_ON_MASK 0x1
+#define IRQ4_MCU_ON_MASK_SFT (0x1 << 4)
+#define IRQ3_MCU_ON_SFT 3
+#define IRQ3_MCU_ON_MASK 0x1
+#define IRQ3_MCU_ON_MASK_SFT (0x1 << 3)
+#define IRQ2_MCU_ON_SFT 2
+#define IRQ2_MCU_ON_MASK 0x1
+#define IRQ2_MCU_ON_MASK_SFT (0x1 << 2)
+#define IRQ1_MCU_ON_SFT 1
+#define IRQ1_MCU_ON_MASK 0x1
+#define IRQ1_MCU_ON_MASK_SFT (0x1 << 1)
+#define IRQ0_MCU_ON_SFT 0
+#define IRQ0_MCU_ON_MASK 0x1
+#define IRQ0_MCU_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_CON1 */
+#define IRQ7_MCU_MODE_SFT 28
+#define IRQ7_MCU_MODE_MASK 0xf
+#define IRQ7_MCU_MODE_MASK_SFT (0xf << 28)
+#define IRQ6_MCU_MODE_SFT 24
+#define IRQ6_MCU_MODE_MASK 0xf
+#define IRQ6_MCU_MODE_MASK_SFT (0xf << 24)
+#define IRQ5_MCU_MODE_SFT 20
+#define IRQ5_MCU_MODE_MASK 0xf
+#define IRQ5_MCU_MODE_MASK_SFT (0xf << 20)
+#define IRQ4_MCU_MODE_SFT 16
+#define IRQ4_MCU_MODE_MASK 0xf
+#define IRQ4_MCU_MODE_MASK_SFT (0xf << 16)
+#define IRQ3_MCU_MODE_SFT 12
+#define IRQ3_MCU_MODE_MASK 0xf
+#define IRQ3_MCU_MODE_MASK_SFT (0xf << 12)
+#define IRQ2_MCU_MODE_SFT 8
+#define IRQ2_MCU_MODE_MASK 0xf
+#define IRQ2_MCU_MODE_MASK_SFT (0xf << 8)
+#define IRQ1_MCU_MODE_SFT 4
+#define IRQ1_MCU_MODE_MASK 0xf
+#define IRQ1_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ0_MCU_MODE_SFT 0
+#define IRQ0_MCU_MODE_MASK 0xf
+#define IRQ0_MCU_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_IRQ_MCU_CON2 */
+#define IRQ15_MCU_MODE_SFT 28
+#define IRQ15_MCU_MODE_MASK 0xf
+#define IRQ15_MCU_MODE_MASK_SFT (0xf << 28)
+#define IRQ14_MCU_MODE_SFT 24
+#define IRQ14_MCU_MODE_MASK 0xf
+#define IRQ14_MCU_MODE_MASK_SFT (0xf << 24)
+#define IRQ13_MCU_MODE_SFT 20
+#define IRQ13_MCU_MODE_MASK 0xf
+#define IRQ13_MCU_MODE_MASK_SFT (0xf << 20)
+#define IRQ12_MCU_MODE_SFT 16
+#define IRQ12_MCU_MODE_MASK 0xf
+#define IRQ12_MCU_MODE_MASK_SFT (0xf << 16)
+#define IRQ11_MCU_MODE_SFT 12
+#define IRQ11_MCU_MODE_MASK 0xf
+#define IRQ11_MCU_MODE_MASK_SFT (0xf << 12)
+#define IRQ10_MCU_MODE_SFT 8
+#define IRQ10_MCU_MODE_MASK 0xf
+#define IRQ10_MCU_MODE_MASK_SFT (0xf << 8)
+#define IRQ9_MCU_MODE_SFT 4
+#define IRQ9_MCU_MODE_MASK 0xf
+#define IRQ9_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ8_MCU_MODE_SFT 0
+#define IRQ8_MCU_MODE_MASK 0xf
+#define IRQ8_MCU_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_IRQ_MCU_CON3 */
+#define IRQ23_MCU_MODE_SFT 28
+#define IRQ23_MCU_MODE_MASK 0xf
+#define IRQ23_MCU_MODE_MASK_SFT (0xf << 28)
+#define IRQ22_MCU_MODE_SFT 24
+#define IRQ22_MCU_MODE_MASK 0xf
+#define IRQ22_MCU_MODE_MASK_SFT (0xf << 24)
+#define IRQ21_MCU_MODE_SFT 20
+#define IRQ21_MCU_MODE_MASK 0xf
+#define IRQ21_MCU_MODE_MASK_SFT (0xf << 20)
+#define IRQ20_MCU_MODE_SFT 16
+#define IRQ20_MCU_MODE_MASK 0xf
+#define IRQ20_MCU_MODE_MASK_SFT (0xf << 16)
+#define IRQ19_MCU_MODE_SFT 12
+#define IRQ19_MCU_MODE_MASK 0xf
+#define IRQ19_MCU_MODE_MASK_SFT (0xf << 12)
+#define IRQ18_MCU_MODE_SFT 8
+#define IRQ18_MCU_MODE_MASK 0xf
+#define IRQ18_MCU_MODE_MASK_SFT (0xf << 8)
+#define IRQ17_MCU_MODE_SFT 4
+#define IRQ17_MCU_MODE_MASK 0xf
+#define IRQ17_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ16_MCU_MODE_SFT 0
+#define IRQ16_MCU_MODE_MASK 0xf
+#define IRQ16_MCU_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_IRQ_MCU_CON4 */
+#define IRQ26_MCU_MODE_SFT 8
+#define IRQ26_MCU_MODE_MASK 0xf
+#define IRQ26_MCU_MODE_MASK_SFT (0xf << 8)
+#define IRQ25_MCU_MODE_SFT 4
+#define IRQ25_MCU_MODE_MASK 0xf
+#define IRQ25_MCU_MODE_MASK_SFT (0xf << 4)
+#define IRQ24_MCU_MODE_SFT 0
+#define IRQ24_MCU_MODE_MASK 0xf
+#define IRQ24_MCU_MODE_MASK_SFT (0xf << 0)
+
+/* AFE_IRQ_MCU_CLR */
+#define IRQ31_MCU_CLR_SFT 31
+#define IRQ31_MCU_CLR_MASK 0x1
+#define IRQ31_MCU_CLR_MASK_SFT (0x1 << 31)
+#define IRQ26_MCU_CLR_SFT 26
+#define IRQ26_MCU_CLR_MASK 0x1
+#define IRQ26_MCU_CLR_MASK_SFT (0x1 << 26)
+#define IRQ25_MCU_CLR_SFT 25
+#define IRQ25_MCU_CLR_MASK 0x1
+#define IRQ25_MCU_CLR_MASK_SFT (0x1 << 25)
+#define IRQ24_MCU_CLR_SFT 24
+#define IRQ24_MCU_CLR_MASK 0x1
+#define IRQ24_MCU_CLR_MASK_SFT (0x1 << 24)
+#define IRQ23_MCU_CLR_SFT 23
+#define IRQ23_MCU_CLR_MASK 0x1
+#define IRQ23_MCU_CLR_MASK_SFT (0x1 << 23)
+#define IRQ22_MCU_CLR_SFT 22
+#define IRQ22_MCU_CLR_MASK 0x1
+#define IRQ22_MCU_CLR_MASK_SFT (0x1 << 22)
+#define IRQ21_MCU_CLR_SFT 21
+#define IRQ21_MCU_CLR_MASK 0x1
+#define IRQ21_MCU_CLR_MASK_SFT (0x1 << 21)
+#define IRQ20_MCU_CLR_SFT 20
+#define IRQ20_MCU_CLR_MASK 0x1
+#define IRQ20_MCU_CLR_MASK_SFT (0x1 << 20)
+#define IRQ19_MCU_CLR_SFT 19
+#define IRQ19_MCU_CLR_MASK 0x1
+#define IRQ19_MCU_CLR_MASK_SFT (0x1 << 19)
+#define IRQ18_MCU_CLR_SFT 18
+#define IRQ18_MCU_CLR_MASK 0x1
+#define IRQ18_MCU_CLR_MASK_SFT (0x1 << 18)
+#define IRQ17_MCU_CLR_SFT 17
+#define IRQ17_MCU_CLR_MASK 0x1
+#define IRQ17_MCU_CLR_MASK_SFT (0x1 << 17)
+#define IRQ16_MCU_CLR_SFT 16
+#define IRQ16_MCU_CLR_MASK 0x1
+#define IRQ16_MCU_CLR_MASK_SFT (0x1 << 16)
+#define IRQ15_MCU_CLR_SFT 15
+#define IRQ15_MCU_CLR_MASK 0x1
+#define IRQ15_MCU_CLR_MASK_SFT (0x1 << 15)
+#define IRQ14_MCU_CLR_SFT 14
+#define IRQ14_MCU_CLR_MASK 0x1
+#define IRQ14_MCU_CLR_MASK_SFT (0x1 << 14)
+#define IRQ13_MCU_CLR_SFT 13
+#define IRQ13_MCU_CLR_MASK 0x1
+#define IRQ13_MCU_CLR_MASK_SFT (0x1 << 13)
+#define IRQ12_MCU_CLR_SFT 12
+#define IRQ12_MCU_CLR_MASK 0x1
+#define IRQ12_MCU_CLR_MASK_SFT (0x1 << 12)
+#define IRQ11_MCU_CLR_SFT 11
+#define IRQ11_MCU_CLR_MASK 0x1
+#define IRQ11_MCU_CLR_MASK_SFT (0x1 << 11)
+#define IRQ10_MCU_CLR_SFT 10
+#define IRQ10_MCU_CLR_MASK 0x1
+#define IRQ10_MCU_CLR_MASK_SFT (0x1 << 10)
+#define IRQ9_MCU_CLR_SFT 9
+#define IRQ9_MCU_CLR_MASK 0x1
+#define IRQ9_MCU_CLR_MASK_SFT (0x1 << 9)
+#define IRQ8_MCU_CLR_SFT 8
+#define IRQ8_MCU_CLR_MASK 0x1
+#define IRQ8_MCU_CLR_MASK_SFT (0x1 << 8)
+#define IRQ7_MCU_CLR_SFT 7
+#define IRQ7_MCU_CLR_MASK 0x1
+#define IRQ7_MCU_CLR_MASK_SFT (0x1 << 7)
+#define IRQ6_MCU_CLR_SFT 6
+#define IRQ6_MCU_CLR_MASK 0x1
+#define IRQ6_MCU_CLR_MASK_SFT (0x1 << 6)
+#define IRQ5_MCU_CLR_SFT 5
+#define IRQ5_MCU_CLR_MASK 0x1
+#define IRQ5_MCU_CLR_MASK_SFT (0x1 << 5)
+#define IRQ4_MCU_CLR_SFT 4
+#define IRQ4_MCU_CLR_MASK 0x1
+#define IRQ4_MCU_CLR_MASK_SFT (0x1 << 4)
+#define IRQ3_MCU_CLR_SFT 3
+#define IRQ3_MCU_CLR_MASK 0x1
+#define IRQ3_MCU_CLR_MASK_SFT (0x1 << 3)
+#define IRQ2_MCU_CLR_SFT 2
+#define IRQ2_MCU_CLR_MASK 0x1
+#define IRQ2_MCU_CLR_MASK_SFT (0x1 << 2)
+#define IRQ1_MCU_CLR_SFT 1
+#define IRQ1_MCU_CLR_MASK 0x1
+#define IRQ1_MCU_CLR_MASK_SFT (0x1 << 1)
+#define IRQ0_MCU_CLR_SFT 0
+#define IRQ0_MCU_CLR_MASK 0x1
+#define IRQ0_MCU_CLR_MASK_SFT (0x1 << 0)
+
+/* AFE_IRQ_MCU_EN */
+#define IRQ31_MCU_EN_SFT 31
+#define IRQ30_MCU_EN_SFT 30
+#define IRQ29_MCU_EN_SFT 29
+#define IRQ28_MCU_EN_SFT 28
+#define IRQ27_MCU_EN_SFT 27
+#define IRQ26_MCU_EN_SFT 26
+#define IRQ25_MCU_EN_SFT 25
+#define IRQ24_MCU_EN_SFT 24
+#define IRQ23_MCU_EN_SFT 23
+#define IRQ22_MCU_EN_SFT 22
+#define IRQ21_MCU_EN_SFT 21
+#define IRQ20_MCU_EN_SFT 20
+#define IRQ19_MCU_EN_SFT 19
+#define IRQ18_MCU_EN_SFT 18
+#define IRQ17_MCU_EN_SFT 17
+#define IRQ16_MCU_EN_SFT 16
+#define IRQ15_MCU_EN_SFT 15
+#define IRQ14_MCU_EN_SFT 14
+#define IRQ13_MCU_EN_SFT 13
+#define IRQ12_MCU_EN_SFT 12
+#define IRQ11_MCU_EN_SFT 11
+#define IRQ10_MCU_EN_SFT 10
+#define IRQ9_MCU_EN_SFT 9
+#define IRQ8_MCU_EN_SFT 8
+#define IRQ7_MCU_EN_SFT 7
+#define IRQ6_MCU_EN_SFT 6
+#define IRQ5_MCU_EN_SFT 5
+#define IRQ4_MCU_EN_SFT 4
+#define IRQ3_MCU_EN_SFT 3
+#define IRQ2_MCU_EN_SFT 2
+#define IRQ1_MCU_EN_SFT 1
+#define IRQ0_MCU_EN_SFT 0
+
+/* AFE_IRQ_MCU_SCP_EN */
+#define IRQ31_MCU_SCP_EN_SFT 31
+#define IRQ30_MCU_SCP_EN_SFT 30
+#define IRQ29_MCU_SCP_EN_SFT 29
+#define IRQ28_MCU_SCP_EN_SFT 28
+#define IRQ27_MCU_SCP_EN_SFT 27
+#define IRQ26_MCU_SCP_EN_SFT 26
+#define IRQ25_MCU_SCP_EN_SFT 25
+#define IRQ24_MCU_SCP_EN_SFT 24
+#define IRQ23_MCU_SCP_EN_SFT 23
+#define IRQ22_MCU_SCP_EN_SFT 22
+#define IRQ21_MCU_SCP_EN_SFT 21
+#define IRQ20_MCU_SCP_EN_SFT 20
+#define IRQ19_MCU_SCP_EN_SFT 19
+#define IRQ18_MCU_SCP_EN_SFT 18
+#define IRQ17_MCU_SCP_EN_SFT 17
+#define IRQ16_MCU_SCP_EN_SFT 16
+#define IRQ15_MCU_SCP_EN_SFT 15
+#define IRQ14_MCU_SCP_EN_SFT 14
+#define IRQ13_MCU_SCP_EN_SFT 13
+#define IRQ12_MCU_SCP_EN_SFT 12
+#define IRQ11_MCU_SCP_EN_SFT 11
+#define IRQ10_MCU_SCP_EN_SFT 10
+#define IRQ9_MCU_SCP_EN_SFT 9
+#define IRQ8_MCU_SCP_EN_SFT 8
+#define IRQ7_MCU_SCP_EN_SFT 7
+#define IRQ6_MCU_SCP_EN_SFT 6
+#define IRQ5_MCU_SCP_EN_SFT 5
+#define IRQ4_MCU_SCP_EN_SFT 4
+#define IRQ3_MCU_SCP_EN_SFT 3
+#define IRQ2_MCU_SCP_EN_SFT 2
+#define IRQ1_MCU_SCP_EN_SFT 1
+#define IRQ0_MCU_SCP_EN_SFT 0
+
+/* AFE_TDM_CON1 */
+#define TDM_EN_SFT 0
+#define TDM_EN_MASK 0x1
+#define TDM_EN_MASK_SFT (0x1 << 0)
+#define LRCK_INVERSE_SFT 2
+#define LRCK_INVERSE_MASK 0x1
+#define LRCK_INVERSE_MASK_SFT (0x1 << 2)
+#define DELAY_DATA_SFT 3
+#define DELAY_DATA_MASK 0x1
+#define DELAY_DATA_MASK_SFT (0x1 << 3)
+#define LEFT_ALIGN_SFT 4
+#define LEFT_ALIGN_MASK 0x1
+#define LEFT_ALIGN_MASK_SFT (0x1 << 4)
+#define WLEN_SFT 8
+#define WLEN_MASK 0x3
+#define WLEN_MASK_SFT (0x3 << 8)
+#define CHANNEL_NUM_SFT 10
+#define CHANNEL_NUM_MASK 0x3
+#define CHANNEL_NUM_MASK_SFT (0x3 << 10)
+#define CHANNEL_BCK_CYCLES_SFT 12
+#define CHANNEL_BCK_CYCLES_MASK 0x3
+#define CHANNEL_BCK_CYCLES_MASK_SFT (0x3 << 12)
+#define DAC_BIT_NUM_SFT 16
+#define DAC_BIT_NUM_MASK 0x1f
+#define DAC_BIT_NUM_MASK_SFT (0x1f << 16)
+#define LRCK_TDM_WIDTH_SFT 24
+#define LRCK_TDM_WIDTH_MASK 0xff
+#define LRCK_TDM_WIDTH_MASK_SFT (0xff << 24)
+
+/* AFE_TDM_CON2 */
+#define ST_CH_PAIR_SOUT0_SFT 0
+#define ST_CH_PAIR_SOUT0_MASK 0x7
+#define ST_CH_PAIR_SOUT0_MASK_SFT (0x7 << 0)
+#define ST_CH_PAIR_SOUT1_SFT 4
+#define ST_CH_PAIR_SOUT1_MASK 0x7
+#define ST_CH_PAIR_SOUT1_MASK_SFT (0x7 << 4)
+#define ST_CH_PAIR_SOUT2_SFT 8
+#define ST_CH_PAIR_SOUT2_MASK 0x7
+#define ST_CH_PAIR_SOUT2_MASK_SFT (0x7 << 8)
+#define ST_CH_PAIR_SOUT3_SFT 12
+#define ST_CH_PAIR_SOUT3_MASK 0x7
+#define ST_CH_PAIR_SOUT3_MASK_SFT (0x7 << 12)
+#define TDM_FIX_VALUE_SEL_SFT 16
+#define TDM_FIX_VALUE_SEL_MASK 0x1
+#define TDM_FIX_VALUE_SEL_MASK_SFT (0x1 << 16)
+#define TDM_I2S_LOOPBACK_SFT 20
+#define TDM_I2S_LOOPBACK_MASK 0x1
+#define TDM_I2S_LOOPBACK_MASK_SFT (0x1 << 20)
+#define TDM_I2S_LOOPBACK_CH_SFT 21
+#define TDM_I2S_LOOPBACK_CH_MASK 0x3
+#define TDM_I2S_LOOPBACK_CH_MASK_SFT (0x3 << 21)
+#define TDM_FIX_VALUE_SFT 24
+#define TDM_FIX_VALUE_MASK 0xff
+#define TDM_FIX_VALUE_MASK_SFT (0xff << 24)
+
+/* AFE_HDMI_CONN0 */
+#define HDMI_O_7_SFT 21
+#define HDMI_O_7_MASK 0x7
+#define HDMI_O_7_MASK_SFT (0x7 << 21)
+#define HDMI_O_6_SFT 18
+#define HDMI_O_6_MASK 0x7
+#define HDMI_O_6_MASK_SFT (0x7 << 18)
+#define HDMI_O_5_SFT 15
+#define HDMI_O_5_MASK 0x7
+#define HDMI_O_5_MASK_SFT (0x7 << 15)
+#define HDMI_O_4_SFT 12
+#define HDMI_O_4_MASK 0x7
+#define HDMI_O_4_MASK_SFT (0x7 << 12)
+#define HDMI_O_3_SFT 9
+#define HDMI_O_3_MASK 0x7
+#define HDMI_O_3_MASK_SFT (0x7 << 9)
+#define HDMI_O_2_SFT 6
+#define HDMI_O_2_MASK 0x7
+#define HDMI_O_2_MASK_SFT (0x7 << 6)
+#define HDMI_O_1_SFT 3
+#define HDMI_O_1_MASK 0x7
+#define HDMI_O_1_MASK_SFT (0x7 << 3)
+#define HDMI_O_0_SFT 0
+#define HDMI_O_0_MASK 0x7
+#define HDMI_O_0_MASK_SFT (0x7 << 0)
+
+/* AFE_AUD_PAD_TOP */
+#define AUD_PAD_TOP_MON_SFT 15
+#define AUD_PAD_TOP_MON_MASK 0x1ffff
+#define AUD_PAD_TOP_MON_MASK_SFT (0x1ffff << 15)
+#define AUD_PAD_TOP_FIFO_RSP_SFT 4
+#define AUD_PAD_TOP_FIFO_RSP_MASK 0xf
+#define AUD_PAD_TOP_FIFO_RSP_MASK_SFT (0xf << 4)
+#define RG_RX_PROTOCOL2_SFT 3
+#define RG_RX_PROTOCOL2_MASK 0x1
+#define RG_RX_PROTOCOL2_MASK_SFT (0x1 << 3)
+#define RESERVDED_01_SFT 1
+#define RESERVDED_01_MASK 0x3
+#define RESERVDED_01_MASK_SFT (0x3 << 1)
+#define RG_RX_FIFO_ON_SFT 0
+#define RG_RX_FIFO_ON_MASK 0x1
+#define RG_RX_FIFO_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG */
+#define RG_ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE_SFT 23
+#define RG_ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE_MASK 0x1
+#define RG_ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE_MASK_SFT (0x1 << 23)
+
+/* AFE_ADDA_MTKAIF_RX_CFG0 */
+#define MTKAIF_RXIF_VOICE_MODE_SFT 20
+#define MTKAIF_RXIF_VOICE_MODE_MASK 0xf
+#define MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 20)
+#define MTKAIF_RXIF_DETECT_ON_SFT 16
+#define MTKAIF_RXIF_DETECT_ON_MASK 0x1
+#define MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 16)
+#define MTKAIF_RXIF_DATA_BIT_SFT 8
+#define MTKAIF_RXIF_DATA_BIT_MASK 0x7
+#define MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8)
+#define MTKAIF_RXIF_FIFO_RSP_SFT 4
+#define MTKAIF_RXIF_FIFO_RSP_MASK 0x7
+#define MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4)
+#define MTKAIF_RXIF_DATA_MODE_SFT 0
+#define MTKAIF_RXIF_DATA_MODE_MASK 0x1
+#define MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0)
+
+/* GENERAL_ASRC_MODE */
+#define GENERAL2_ASRCOUT_MODE_SFT 12
+#define GENERAL2_ASRCOUT_MODE_MASK 0xf
+#define GENERAL2_ASRCOUT_MODE_MASK_SFT (0xf << 12)
+#define GENERAL2_ASRCIN_MODE_SFT 8
+#define GENERAL2_ASRCIN_MODE_MASK 0xf
+#define GENERAL2_ASRCIN_MODE_MASK_SFT (0xf << 8)
+#define GENERAL1_ASRCOUT_MODE_SFT 4
+#define GENERAL1_ASRCOUT_MODE_MASK 0xf
+#define GENERAL1_ASRCOUT_MODE_MASK_SFT (0xf << 4)
+#define GENERAL1_ASRCIN_MODE_SFT 0
+#define GENERAL1_ASRCIN_MODE_MASK 0xf
+#define GENERAL1_ASRCIN_MODE_MASK_SFT (0xf << 0)
+
+/* GENERAL_ASRC_EN_ON */
+#define GENERAL2_ASRC_EN_ON_SFT 1
+#define GENERAL2_ASRC_EN_ON_MASK 0x1
+#define GENERAL2_ASRC_EN_ON_MASK_SFT (0x1 << 1)
+#define GENERAL1_ASRC_EN_ON_SFT 0
+#define GENERAL1_ASRC_EN_ON_MASK 0x1
+#define GENERAL1_ASRC_EN_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON0 */
+#define G_SRC_CHSET_STR_CLR_SFT 4
+#define G_SRC_CHSET_STR_CLR_MASK 0x1
+#define G_SRC_CHSET_STR_CLR_MASK_SFT (0x1 << 4)
+#define G_SRC_CHSET_ON_SFT 2
+#define G_SRC_CHSET_ON_MASK 0x1
+#define G_SRC_CHSET_ON_MASK_SFT (0x1 << 2)
+#define G_SRC_COEFF_SRAM_CTRL_SFT 1
+#define G_SRC_COEFF_SRAM_CTRL_MASK 0x1
+#define G_SRC_COEFF_SRAM_CTRL_MASK_SFT (0x1 << 1)
+#define G_SRC_ASM_ON_SFT 0
+#define G_SRC_ASM_ON_MASK 0x1
+#define G_SRC_ASM_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON3 */
+#define G_SRC_ASM_FREQ_4_SFT 0
+#define G_SRC_ASM_FREQ_4_MASK 0xffffff
+#define G_SRC_ASM_FREQ_4_MASK_SFT (0xffffff << 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON4 */
+#define G_SRC_ASM_FREQ_5_SFT 0
+#define G_SRC_ASM_FREQ_5_MASK 0xffffff
+#define G_SRC_ASM_FREQ_5_MASK_SFT (0xffffff << 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON13 */
+#define G_SRC_COEFF_SRAM_ADR_SFT 0
+#define G_SRC_COEFF_SRAM_ADR_MASK 0x3f
+#define G_SRC_COEFF_SRAM_ADR_MASK_SFT (0x3f << 0)
+
+/* AFE_GENERAL1_ASRC_2CH_CON2 */
+#define G_SRC_CHSET_O16BIT_SFT 19
+#define G_SRC_CHSET_O16BIT_MASK 0x1
+#define G_SRC_CHSET_O16BIT_MASK_SFT (0x1 << 19)
+#define G_SRC_CHSET_CLR_IIR_HISTORY_SFT 17
+#define G_SRC_CHSET_CLR_IIR_HISTORY_MASK 0x1
+#define G_SRC_CHSET_CLR_IIR_HISTORY_MASK_SFT (0x1 << 17)
+#define G_SRC_CHSET_IS_MONO_SFT 16
+#define G_SRC_CHSET_IS_MONO_MASK 0x1
+#define G_SRC_CHSET_IS_MONO_MASK_SFT (0x1 << 16)
+#define G_SRC_CHSET_IIR_EN_SFT 11
+#define G_SRC_CHSET_IIR_EN_MASK 0x1
+#define G_SRC_CHSET_IIR_EN_MASK_SFT (0x1 << 11)
+#define G_SRC_CHSET_IIR_STAGE_SFT 8
+#define G_SRC_CHSET_IIR_STAGE_MASK 0x7
+#define G_SRC_CHSET_IIR_STAGE_MASK_SFT (0x7 << 8)
+#define G_SRC_CHSET_STR_CLR_RU_SFT 5
+#define G_SRC_CHSET_STR_CLR_RU_MASK 0x1
+#define G_SRC_CHSET_STR_CLR_RU_MASK_SFT (0x1 << 5)
+#define G_SRC_CHSET_ON_SFT 2
+#define G_SRC_CHSET_ON_MASK 0x1
+#define G_SRC_CHSET_ON_MASK_SFT (0x1 << 2)
+#define G_SRC_COEFF_SRAM_CTRL_SFT 1
+#define G_SRC_COEFF_SRAM_CTRL_MASK 0x1
+#define G_SRC_COEFF_SRAM_CTRL_MASK_SFT (0x1 << 1)
+#define G_SRC_ASM_ON_SFT 0
+#define G_SRC_ASM_ON_MASK 0x1
+#define G_SRC_ASM_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ADDA_DL_SDM_AUTO_RESET_CON */
+#define ADDA_SDM_AUTO_RESET_ONOFF_SFT 31
+#define ADDA_SDM_AUTO_RESET_ONOFF_MASK 0x1
+#define ADDA_SDM_AUTO_RESET_ONOFF_MASK_SFT (0x1 << 31)
+
+/* AFE_ADDA_3RD_DAC_DL_SDM_AUTO_RESET_CON */
+#define ADDA_3RD_DAC_SDM_AUTO_RESET_ONOFF_SFT 31
+#define ADDA_3RD_DAC_SDM_AUTO_RESET_ONOFF_MASK 0x1
+#define ADDA_3RD_DAC_SDM_AUTO_RESET_ONOFF_MASK_SFT (0x1 << 31)
+
+/* AFE_TINY_CONN0 */
+#define O_3_CFG_SFT 24
+#define O_3_CFG_MASK 0x1f
+#define O_3_CFG_MASK_SFT (0x1f << 24)
+#define O_2_CFG_SFT 16
+#define O_2_CFG_MASK 0x1f
+#define O_2_CFG_MASK_SFT (0x1f << 16)
+#define O_1_CFG_SFT 8
+#define O_1_CFG_MASK 0x1f
+#define O_1_CFG_MASK_SFT (0x1f << 8)
+#define O_0_CFG_SFT 0
+#define O_0_CFG_MASK 0x1f
+#define O_0_CFG_MASK_SFT (0x1f << 0)
+
+/* AFE_TINY_CONN5 */
+#define O_23_CFG_SFT 24
+#define O_23_CFG_MASK 0x1f
+#define O_23_CFG_MASK_SFT (0x1f << 24)
+#define O_22_CFG_SFT 16
+#define O_22_CFG_MASK 0x1f
+#define O_22_CFG_MASK_SFT (0x1f << 16)
+#define O_21_CFG_SFT 8
+#define O_21_CFG_MASK 0x1f
+#define O_21_CFG_MASK_SFT (0x1f << 8)
+#define O_20_CFG_SFT 0
+#define O_20_CFG_MASK 0x1f
+#define O_20_CFG_MASK_SFT (0x1f << 0)
+
+/* AFE_MEMIF_CONN */
+#define VUL6_USE_TINY_SFT 8
+#define VUL6_USE_TINY_MASK 1
+#define VUL6_USE_TINY_MASK_SFT (0x1 << 8)
+#define VUL5_USE_TINY_SFT 7
+#define VUL5_USE_TINY_MASK 1
+#define VUL5_USE_TINY_MASK_SFT (0x1 << 7)
+#define VUL4_USE_TINY_SFT 6
+#define VUL4_USE_TINY_MASK 1
+#define VUL4_USE_TINY_MASK_SFT (0x1 << 6)
+#define VUL3_USE_TINY_SFT 5
+#define VUL3_USE_TINY_MASK 1
+#define VUL3_USE_TINY_MASK_SFT (0x1 << 5)
+#define AWB2_USE_TINY_SFT 4
+#define AWB2_USE_TINY_MASK 1
+#define AWB2_USE_TINY_MASK_SFT (0x1 << 4)
+#define AWB_USE_TINY_SFT 3
+#define AWB_USE_TINY_MASK 1
+#define AWB_USE_TINY_MASK_SFT (0x1 << 3)
+#define VUL12_USE_TINY_SFT 2
+#define VUL12_USE_TINY_MASK 1
+#define VUL12_USE_TINY_MASK_SFT (0x1 << 2)
+#define VUL2_USE_TINY_SFT 1
+#define VUL2_USE_TINY_MASK 1
+#define VUL2_USE_TINY_MASK_SFT (0x1 << 1)
+#define VUL1_USE_TINY_SFT 0
+#define VUL1_USE_TINY_MASK 1
+#define VUL1_USE_TINY_MASK_SFT (0x1 << 0)
+
+/* AFE_ASRC_2CH_CON0 */
+#define CON0_CHSET_STR_CLR_SFT 4
+#define CON0_CHSET_STR_CLR_MASK 1
+#define CON0_CHSET_STR_CLR_MASK_SFT (0x1 << 4)
+#define CON0_ASM_ON_SFT 0
+#define CON0_ASM_ON_MASK 1
+#define CON0_ASM_ON_MASK_SFT (0x1 << 0)
+
+/* AFE_ASRC_2CH_CON5 */
+#define CALI_EN_SFT 0
+#define CALI_EN_MASK 1
+#define CALI_EN_MASK_SFT (0x1 << 0)
+
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON1 0x0004
+#define AUDIO_TOP_CON2 0x0008
+#define AUDIO_TOP_CON3 0x000c
+#define AFE_DAC_CON0 0x0010
+#define AFE_I2S_CON 0x0018
+#define AFE_CONN0 0x0020
+#define AFE_CONN1 0x0024
+#define AFE_CONN2 0x0028
+#define AFE_CONN3 0x002c
+#define AFE_CONN4 0x0030
+#define AFE_I2S_CON1 0x0034
+#define AFE_I2S_CON2 0x0038
+#define AFE_I2S_CON3 0x0040
+#define AFE_CONN5 0x0044
+#define AFE_CONN_24BIT 0x0048
+#define AFE_DL1_CON0 0x004c
+#define AFE_DL1_BASE_MSB 0x0050
+#define AFE_DL1_BASE 0x0054
+#define AFE_DL1_CUR_MSB 0x0058
+#define AFE_DL1_CUR 0x005c
+#define AFE_DL1_END_MSB 0x0060
+#define AFE_DL1_END 0x0064
+#define AFE_DL2_CON0 0x0068
+#define AFE_DL2_BASE_MSB 0x006c
+#define AFE_DL2_BASE 0x0070
+#define AFE_DL2_CUR_MSB 0x0074
+#define AFE_DL2_CUR 0x0078
+#define AFE_DL2_END_MSB 0x007c
+#define AFE_DL2_END 0x0080
+#define AFE_DL3_CON0 0x0084
+#define AFE_DL3_BASE_MSB 0x0088
+#define AFE_DL3_BASE 0x008c
+#define AFE_DL3_CUR_MSB 0x0090
+#define AFE_DL3_CUR 0x0094
+#define AFE_DL3_END_MSB 0x0098
+#define AFE_DL3_END 0x009c
+#define AFE_CONN6 0x00bc
+#define AFE_DL4_CON0 0x00cc
+#define AFE_DL4_BASE_MSB 0x00d0
+#define AFE_DL4_BASE 0x00d4
+#define AFE_DL4_CUR_MSB 0x00d8
+#define AFE_DL4_CUR 0x00dc
+#define AFE_DL4_END_MSB 0x00e0
+#define AFE_DL4_END 0x00e4
+#define AFE_DL12_CON0 0x00e8
+#define AFE_DL12_BASE_MSB 0x00ec
+#define AFE_DL12_BASE 0x00f0
+#define AFE_DL12_CUR_MSB 0x00f4
+#define AFE_DL12_CUR 0x00f8
+#define AFE_DL12_END_MSB 0x00fc
+#define AFE_DL12_END 0x0100
+#define AFE_ADDA_DL_SRC2_CON0 0x0108
+#define AFE_ADDA_DL_SRC2_CON1 0x010c
+#define AFE_ADDA_UL_SRC_CON0 0x0114
+#define AFE_ADDA_UL_SRC_CON1 0x0118
+#define AFE_ADDA_TOP_CON0 0x0120
+#define AFE_ADDA_UL_DL_CON0 0x0124
+#define AFE_ADDA_SRC_DEBUG 0x012c
+#define AFE_ADDA_SRC_DEBUG_MON0 0x0130
+#define AFE_ADDA_SRC_DEBUG_MON1 0x0134
+#define AFE_ADDA_UL_SRC_MON0 0x0148
+#define AFE_ADDA_UL_SRC_MON1 0x014c
+#define AFE_SECURE_CON0 0x0150
+#define AFE_SRAM_BOUND 0x0154
+#define AFE_SECURE_CON1 0x0158
+#define AFE_SECURE_CONN0 0x015c
+#define AFE_VUL_CON0 0x0170
+#define AFE_VUL_BASE_MSB 0x0174
+#define AFE_VUL_BASE 0x0178
+#define AFE_VUL_CUR_MSB 0x017c
+#define AFE_VUL_CUR 0x0180
+#define AFE_VUL_END_MSB 0x0184
+#define AFE_VUL_END 0x0188
+#define AFE_ADDA_3RD_DAC_DL_SDM_AUTO_RESET_CON 0x018c
+#define AFE_ADDA_3RD_DAC_DL_SRC2_CON0 0x0190
+#define AFE_ADDA_3RD_DAC_DL_SRC2_CON1 0x0194
+#define AFE_ADDA_3RD_DAC_PREDIS_CON0 0x01a0
+#define AFE_ADDA_3RD_DAC_PREDIS_CON1 0x01a4
+#define AFE_ADDA_3RD_DAC_PREDIS_CON2 0x01a8
+#define AFE_ADDA_3RD_DAC_PREDIS_CON3 0x01ac
+#define AFE_ADDA_3RD_DAC_DL_SDM_DCCOMP_CON 0x01b0
+#define AFE_ADDA_3RD_DAC_DL_SDM_TEST 0x01b4
+#define AFE_ADDA_3RD_DAC_DL_DC_COMP_CFG0 0x01b8
+#define AFE_ADDA_3RD_DAC_DL_DC_COMP_CFG1 0x01bc
+#define AFE_ADDA_3RD_DAC_DL_SDM_FIFO_MON 0x01c0
+#define AFE_ADDA_3RD_DAC_DL_SRC_LCH_MON 0x01c4
+#define AFE_ADDA_3RD_DAC_DL_SRC_RCH_MON 0x01c8
+#define AFE_ADDA_3RD_DAC_DL_SDM_OUT_MON 0x01cc
+#define AFE_SIDETONE_DEBUG 0x01d0
+#define AFE_SIDETONE_MON 0x01d4
+#define AFE_ADDA_3RD_DAC_DL_SDM_DITHER_CON 0x01d8
+#define AFE_SINEGEN_CON2 0x01dc
+#define AFE_SIDETONE_CON0 0x01e0
+#define AFE_SIDETONE_COEFF 0x01e4
+#define AFE_SIDETONE_CON1 0x01e8
+#define AFE_SIDETONE_GAIN 0x01ec
+#define AFE_SINEGEN_CON0 0x01f0
+#define AFE_I2S_MON2 0x01f8
+#define AFE_SINEGEN_CON_TDM 0x01fc
+#define AFE_TOP_CON0 0x0200
+#define AFE_VUL2_CON0 0x020c
+#define AFE_VUL2_BASE_MSB 0x0210
+#define AFE_VUL2_BASE 0x0214
+#define AFE_VUL2_CUR_MSB 0x0218
+#define AFE_VUL2_CUR 0x021c
+#define AFE_VUL2_END_MSB 0x0220
+#define AFE_VUL2_END 0x0224
+#define AFE_VUL3_CON0 0x0228
+#define AFE_VUL3_BASE_MSB 0x022c
+#define AFE_VUL3_BASE 0x0230
+#define AFE_VUL3_CUR_MSB 0x0234
+#define AFE_VUL3_CUR 0x0238
+#define AFE_VUL3_END_MSB 0x023c
+#define AFE_VUL3_END 0x0240
+#define AFE_BUSY 0x0244
+#define AFE_BUS_CFG 0x0250
+#define AFE_ADDA_PREDIS_CON0 0x0260
+#define AFE_ADDA_PREDIS_CON1 0x0264
+#define AFE_I2S_MON 0x027c
+#define AFE_ADDA_IIR_COEF_02_01 0x0290
+#define AFE_ADDA_IIR_COEF_04_03 0x0294
+#define AFE_ADDA_IIR_COEF_06_05 0x0298
+#define AFE_ADDA_IIR_COEF_08_07 0x029c
+#define AFE_ADDA_IIR_COEF_10_09 0x02a0
+#define AFE_IRQ_MCU_CON1 0x02e4
+#define AFE_IRQ_MCU_CON2 0x02e8
+#define AFE_DAC_MON 0x02ec
+#define AFE_IRQ_MCU_CON3 0x02f0
+#define AFE_IRQ_MCU_CON4 0x02f4
+#define AFE_IRQ_MCU_CNT0 0x0300
+#define AFE_IRQ_MCU_CNT6 0x0304
+#define AFE_IRQ_MCU_CNT8 0x0308
+#define AFE_IRQ_MCU_DSP2_EN 0x030c
+#define AFE_IRQ0_MCU_CNT_MON 0x0310
+#define AFE_IRQ6_MCU_CNT_MON 0x0314
+#define AFE_VUL4_CON0 0x0358
+#define AFE_VUL4_BASE_MSB 0x035c
+#define AFE_VUL4_BASE 0x0360
+#define AFE_VUL4_CUR_MSB 0x0364
+#define AFE_VUL4_CUR 0x0368
+#define AFE_VUL4_END_MSB 0x036c
+#define AFE_VUL4_END 0x0370
+#define AFE_VUL12_CON0 0x0374
+#define AFE_VUL12_BASE_MSB 0x0378
+#define AFE_VUL12_BASE 0x037c
+#define AFE_VUL12_CUR_MSB 0x0380
+#define AFE_VUL12_CUR 0x0384
+#define AFE_VUL12_END_MSB 0x0388
+#define AFE_VUL12_END 0x038c
+#define AFE_HDMI_CONN0 0x0390
+#define AFE_IRQ3_MCU_CNT_MON 0x0398
+#define AFE_IRQ4_MCU_CNT_MON 0x039c
+#define AFE_IRQ_MCU_CON0 0x03a0
+#define AFE_IRQ_MCU_STATUS 0x03a4
+#define AFE_IRQ_MCU_CLR 0x03a8
+#define AFE_IRQ_MCU_CNT1 0x03ac
+#define AFE_IRQ_MCU_CNT2 0x03b0
+#define AFE_IRQ_MCU_EN 0x03b4
+#define AFE_IRQ_MCU_MON2 0x03b8
+#define AFE_IRQ_MCU_CNT5 0x03bc
+#define AFE_IRQ1_MCU_CNT_MON 0x03c0
+#define AFE_IRQ2_MCU_CNT_MON 0x03c4
+#define AFE_IRQ5_MCU_CNT_MON 0x03cc
+#define AFE_IRQ_MCU_DSP_EN 0x03d0
+#define AFE_IRQ_MCU_SCP_EN 0x03d4
+#define AFE_IRQ_MCU_CNT7 0x03dc
+#define AFE_IRQ7_MCU_CNT_MON 0x03e0
+#define AFE_IRQ_MCU_CNT3 0x03e4
+#define AFE_IRQ_MCU_CNT4 0x03e8
+#define AFE_IRQ_MCU_CNT11 0x03ec
+#define AFE_APLL1_TUNER_CFG 0x03f0
+#define AFE_APLL2_TUNER_CFG 0x03f4
+#define AFE_IRQ_MCU_MISS_CLR 0x03f8
+#define AFE_CONN33 0x0408
+#define AFE_IRQ_MCU_CNT12 0x040c
+#define AFE_GAIN1_CON0 0x0410
+#define AFE_GAIN1_CON1 0x0414
+#define AFE_GAIN1_CON2 0x0418
+#define AFE_GAIN1_CON3 0x041c
+#define AFE_CONN7 0x0420
+#define AFE_GAIN1_CUR 0x0424
+#define AFE_GAIN2_CON0 0x0428
+#define AFE_GAIN2_CON1 0x042c
+#define AFE_GAIN2_CON2 0x0430
+#define AFE_GAIN2_CON3 0x0434
+#define AFE_CONN8 0x0438
+#define AFE_GAIN2_CUR 0x043c
+#define AFE_CONN9 0x0440
+#define AFE_CONN10 0x0444
+#define AFE_CONN11 0x0448
+#define AFE_CONN12 0x044c
+#define AFE_CONN13 0x0450
+#define AFE_CONN14 0x0454
+#define AFE_CONN15 0x0458
+#define AFE_CONN16 0x045c
+#define AFE_CONN17 0x0460
+#define AFE_CONN18 0x0464
+#define AFE_CONN19 0x0468
+#define AFE_CONN20 0x046c
+#define AFE_CONN21 0x0470
+#define AFE_CONN22 0x0474
+#define AFE_CONN23 0x0478
+#define AFE_CONN24 0x047c
+#define AFE_CONN_RS 0x0494
+#define AFE_CONN_DI 0x0498
+#define AFE_CONN25 0x04b0
+#define AFE_CONN26 0x04b4
+#define AFE_CONN27 0x04b8
+#define AFE_CONN28 0x04bc
+#define AFE_CONN29 0x04c0
+#define AFE_CONN30 0x04c4
+#define AFE_CONN31 0x04c8
+#define AFE_CONN32 0x04cc
+#define AFE_SRAM_DELSEL_CON1 0x04f4
+#define AFE_CONN56 0x0500
+#define AFE_CONN57 0x0504
+#define AFE_CONN56_1 0x0510
+#define AFE_CONN57_1 0x0514
+#define AFE_TINY_CONN2 0x0520
+#define AFE_TINY_CONN3 0x0524
+#define AFE_TINY_CONN4 0x0528
+#define AFE_TINY_CONN5 0x052c
+#define PCM_INTF_CON1 0x0530
+#define PCM_INTF_CON2 0x0538
+#define PCM2_INTF_CON 0x053c
+#define AFE_TDM_CON1 0x0548
+#define AFE_TDM_CON2 0x054c
+#define AFE_I2S_CON6 0x0564
+#define AFE_I2S_CON7 0x0568
+#define AFE_I2S_CON8 0x056c
+#define AFE_I2S_CON9 0x0570
+#define AFE_CONN34 0x0580
+#define FPGA_CFG0 0x05b0
+#define FPGA_CFG1 0x05b4
+#define FPGA_CFG2 0x05c0
+#define FPGA_CFG3 0x05c4
+#define AUDIO_TOP_DBG_CON 0x05c8
+#define AUDIO_TOP_DBG_MON0 0x05cc
+#define AUDIO_TOP_DBG_MON1 0x05d0
+#define AFE_IRQ8_MCU_CNT_MON 0x05e4
+#define AFE_IRQ11_MCU_CNT_MON 0x05e8
+#define AFE_IRQ12_MCU_CNT_MON 0x05ec
+#define AFE_IRQ_MCU_CNT9 0x0600
+#define AFE_IRQ_MCU_CNT10 0x0604
+#define AFE_IRQ_MCU_CNT13 0x0608
+#define AFE_IRQ_MCU_CNT14 0x060c
+#define AFE_IRQ_MCU_CNT15 0x0610
+#define AFE_IRQ_MCU_CNT16 0x0614
+#define AFE_IRQ_MCU_CNT17 0x0618
+#define AFE_IRQ_MCU_CNT18 0x061c
+#define AFE_IRQ_MCU_CNT19 0x0620
+#define AFE_IRQ_MCU_CNT20 0x0624
+#define AFE_IRQ_MCU_CNT21 0x0628
+#define AFE_IRQ_MCU_CNT22 0x062c
+#define AFE_IRQ_MCU_CNT23 0x0630
+#define AFE_IRQ_MCU_CNT24 0x0634
+#define AFE_IRQ_MCU_CNT25 0x0638
+#define AFE_IRQ_MCU_CNT26 0x063c
+#define AFE_IRQ_MCU_CNT31 0x0640
+#define AFE_TINY_CONN6 0x0650
+#define AFE_TINY_CONN7 0x0654
+#define AFE_IRQ9_MCU_CNT_MON 0x0660
+#define AFE_IRQ10_MCU_CNT_MON 0x0664
+#define AFE_IRQ13_MCU_CNT_MON 0x0668
+#define AFE_IRQ14_MCU_CNT_MON 0x066c
+#define AFE_IRQ15_MCU_CNT_MON 0x0670
+#define AFE_IRQ16_MCU_CNT_MON 0x0674
+#define AFE_IRQ17_MCU_CNT_MON 0x0678
+#define AFE_IRQ18_MCU_CNT_MON 0x067c
+#define AFE_IRQ19_MCU_CNT_MON 0x0680
+#define AFE_IRQ20_MCU_CNT_MON 0x0684
+#define AFE_IRQ21_MCU_CNT_MON 0x0688
+#define AFE_IRQ22_MCU_CNT_MON 0x068c
+#define AFE_IRQ23_MCU_CNT_MON 0x0690
+#define AFE_IRQ24_MCU_CNT_MON 0x0694
+#define AFE_IRQ25_MCU_CNT_MON 0x0698
+#define AFE_IRQ26_MCU_CNT_MON 0x069c
+#define AFE_IRQ31_MCU_CNT_MON 0x06a0
+#define AFE_GENERAL_REG0 0x0800
+#define AFE_GENERAL_REG1 0x0804
+#define AFE_GENERAL_REG2 0x0808
+#define AFE_GENERAL_REG3 0x080c
+#define AFE_GENERAL_REG4 0x0810
+#define AFE_GENERAL_REG5 0x0814
+#define AFE_GENERAL_REG6 0x0818
+#define AFE_GENERAL_REG7 0x081c
+#define AFE_GENERAL_REG8 0x0820
+#define AFE_GENERAL_REG9 0x0824
+#define AFE_GENERAL_REG10 0x0828
+#define AFE_GENERAL_REG11 0x082c
+#define AFE_GENERAL_REG12 0x0830
+#define AFE_GENERAL_REG13 0x0834
+#define AFE_GENERAL_REG14 0x0838
+#define AFE_GENERAL_REG15 0x083c
+#define AFE_CBIP_CFG0 0x0840
+#define AFE_CBIP_MON0 0x0844
+#define AFE_CBIP_SLV_MUX_MON0 0x0848
+#define AFE_CBIP_SLV_DECODER_MON0 0x084c
+#define AFE_ADDA6_MTKAIF_MON0 0x0854
+#define AFE_ADDA6_MTKAIF_MON1 0x0858
+#define AFE_AWB_CON0 0x085c
+#define AFE_AWB_BASE_MSB 0x0860
+#define AFE_AWB_BASE 0x0864
+#define AFE_AWB_CUR_MSB 0x0868
+#define AFE_AWB_CUR 0x086c
+#define AFE_AWB_END_MSB 0x0870
+#define AFE_AWB_END 0x0874
+#define AFE_AWB2_CON0 0x0878
+#define AFE_AWB2_BASE_MSB 0x087c
+#define AFE_AWB2_BASE 0x0880
+#define AFE_AWB2_CUR_MSB 0x0884
+#define AFE_AWB2_CUR 0x0888
+#define AFE_AWB2_END_MSB 0x088c
+#define AFE_AWB2_END 0x0890
+#define AFE_DAI_CON0 0x0894
+#define AFE_DAI_BASE_MSB 0x0898
+#define AFE_DAI_BASE 0x089c
+#define AFE_DAI_CUR_MSB 0x08a0
+#define AFE_DAI_CUR 0x08a4
+#define AFE_DAI_END_MSB 0x08a8
+#define AFE_DAI_END 0x08ac
+#define AFE_DAI2_CON0 0x08b0
+#define AFE_DAI2_BASE_MSB 0x08b4
+#define AFE_DAI2_BASE 0x08b8
+#define AFE_DAI2_CUR_MSB 0x08bc
+#define AFE_DAI2_CUR 0x08c0
+#define AFE_DAI2_END_MSB 0x08c4
+#define AFE_DAI2_END 0x08c8
+#define AFE_MEMIF_CON0 0x08cc
+#define AFE_CONN0_1 0x0900
+#define AFE_CONN1_1 0x0904
+#define AFE_CONN2_1 0x0908
+#define AFE_CONN3_1 0x090c
+#define AFE_CONN4_1 0x0910
+#define AFE_CONN5_1 0x0914
+#define AFE_CONN6_1 0x0918
+#define AFE_CONN7_1 0x091c
+#define AFE_CONN8_1 0x0920
+#define AFE_CONN9_1 0x0924
+#define AFE_CONN10_1 0x0928
+#define AFE_CONN11_1 0x092c
+#define AFE_CONN12_1 0x0930
+#define AFE_CONN13_1 0x0934
+#define AFE_CONN14_1 0x0938
+#define AFE_CONN15_1 0x093c
+#define AFE_CONN16_1 0x0940
+#define AFE_CONN17_1 0x0944
+#define AFE_CONN18_1 0x0948
+#define AFE_CONN19_1 0x094c
+#define AFE_CONN20_1 0x0950
+#define AFE_CONN21_1 0x0954
+#define AFE_CONN22_1 0x0958
+#define AFE_CONN23_1 0x095c
+#define AFE_CONN24_1 0x0960
+#define AFE_CONN25_1 0x0964
+#define AFE_CONN26_1 0x0968
+#define AFE_CONN27_1 0x096c
+#define AFE_CONN28_1 0x0970
+#define AFE_CONN29_1 0x0974
+#define AFE_CONN30_1 0x0978
+#define AFE_CONN31_1 0x097c
+#define AFE_CONN32_1 0x0980
+#define AFE_CONN33_1 0x0984
+#define AFE_CONN34_1 0x0988
+#define AFE_CONN_RS_1 0x098c
+#define AFE_CONN_DI_1 0x0990
+#define AFE_CONN_24BIT_1 0x0994
+#define AFE_CONN_REG 0x0998
+#define AFE_CONN35 0x09a0
+#define AFE_CONN36 0x09a4
+#define AFE_CONN37 0x09a8
+#define AFE_CONN38 0x09ac
+#define AFE_CONN35_1 0x09b0
+#define AFE_CONN36_1 0x09b4
+#define AFE_CONN37_1 0x09b8
+#define AFE_CONN38_1 0x09bc
+#define AFE_CONN39 0x09c0
+#define AFE_CONN40 0x09c4
+#define AFE_CONN41 0x09c8
+#define AFE_CONN42 0x09cc
+#define AFE_SGEN_CON_SGEN32 0x09d0
+#define AFE_CONN39_1 0x09e0
+#define AFE_CONN40_1 0x09e4
+#define AFE_CONN41_1 0x09e8
+#define AFE_CONN42_1 0x09ec
+#define AFE_I2S_CON4 0x09f8
+#define AFE_ADDA6_TOP_CON0 0x0a80
+#define AFE_ADDA6_UL_SRC_CON0 0x0a84
+#define AFE_ADDA6_UL_SRC_CON1 0x0a88
+#define AFE_ADDA6_SRC_DEBUG 0x0a8c
+#define AFE_ADDA6_SRC_DEBUG_MON0 0x0a90
+#define AFE_ADDA6_ULCF_CFG_02_01 0x0aa0
+#define AFE_ADDA6_ULCF_CFG_04_03 0x0aa4
+#define AFE_ADDA6_ULCF_CFG_06_05 0x0aa8
+#define AFE_ADDA6_ULCF_CFG_08_07 0x0aac
+#define AFE_ADDA6_ULCF_CFG_10_09 0x0ab0
+#define AFE_ADDA6_ULCF_CFG_12_11 0x0ab4
+#define AFE_ADDA6_ULCF_CFG_14_13 0x0ab8
+#define AFE_ADDA6_ULCF_CFG_16_15 0x0abc
+#define AFE_ADDA6_ULCF_CFG_18_17 0x0ac0
+#define AFE_ADDA6_ULCF_CFG_20_19 0x0ac4
+#define AFE_ADDA6_ULCF_CFG_22_21 0x0ac8
+#define AFE_ADDA6_ULCF_CFG_24_23 0x0acc
+#define AFE_ADDA6_ULCF_CFG_26_25 0x0ad0
+#define AFE_ADDA6_ULCF_CFG_28_27 0x0ad4
+#define AFE_ADDA6_ULCF_CFG_30_29 0x0ad8
+#define AFE_ADD6A_UL_SRC_MON0 0x0ae4
+#define AFE_ADDA6_UL_SRC_MON1 0x0ae8
+#define AFE_TINY_CONN0 0x0af0
+#define AFE_TINY_CONN1 0x0af4
+#define AFE_CONN43 0x0af8
+#define AFE_CONN43_1 0x0afc
+#define AFE_MOD_DAI_CON0 0x0b00
+#define AFE_MOD_DAI_BASE_MSB 0x0b04
+#define AFE_MOD_DAI_BASE 0x0b08
+#define AFE_MOD_DAI_CUR_MSB 0x0b0c
+#define AFE_MOD_DAI_CUR 0x0b10
+#define AFE_MOD_DAI_END_MSB 0x0b14
+#define AFE_MOD_DAI_END 0x0b18
+#define AFE_HDMI_OUT_CON0 0x0b1c
+#define AFE_HDMI_OUT_BASE_MSB 0x0b20
+#define AFE_HDMI_OUT_BASE 0x0b24
+#define AFE_HDMI_OUT_CUR_MSB 0x0b28
+#define AFE_HDMI_OUT_CUR 0x0b2c
+#define AFE_HDMI_OUT_END_MSB 0x0b30
+#define AFE_HDMI_OUT_END 0x0b34
+#define AFE_AWB_RCH_MON 0x0b70
+#define AFE_AWB_LCH_MON 0x0b74
+#define AFE_VUL_RCH_MON 0x0b78
+#define AFE_VUL_LCH_MON 0x0b7c
+#define AFE_VUL12_RCH_MON 0x0b80
+#define AFE_VUL12_LCH_MON 0x0b84
+#define AFE_VUL2_RCH_MON 0x0b88
+#define AFE_VUL2_LCH_MON 0x0b8c
+#define AFE_DAI_DATA_MON 0x0b90
+#define AFE_MOD_DAI_DATA_MON 0x0b94
+#define AFE_DAI2_DATA_MON 0x0b98
+#define AFE_AWB2_RCH_MON 0x0b9c
+#define AFE_AWB2_LCH_MON 0x0ba0
+#define AFE_VUL3_RCH_MON 0x0ba4
+#define AFE_VUL3_LCH_MON 0x0ba8
+#define AFE_VUL4_RCH_MON 0x0bac
+#define AFE_VUL4_LCH_MON 0x0bb0
+#define AFE_VUL5_RCH_MON 0x0bb4
+#define AFE_VUL5_LCH_MON 0x0bb8
+#define AFE_VUL6_RCH_MON 0x0bbc
+#define AFE_VUL6_LCH_MON 0x0bc0
+#define AFE_DL1_RCH_MON 0x0bc4
+#define AFE_DL1_LCH_MON 0x0bc8
+#define AFE_DL2_RCH_MON 0x0bcc
+#define AFE_DL2_LCH_MON 0x0bd0
+#define AFE_DL12_RCH1_MON 0x0bd4
+#define AFE_DL12_LCH1_MON 0x0bd8
+#define AFE_DL12_RCH2_MON 0x0bdc
+#define AFE_DL12_LCH2_MON 0x0be0
+#define AFE_DL3_RCH_MON 0x0be4
+#define AFE_DL3_LCH_MON 0x0be8
+#define AFE_DL4_RCH_MON 0x0bec
+#define AFE_DL4_LCH_MON 0x0bf0
+#define AFE_DL5_RCH_MON 0x0bf4
+#define AFE_DL5_LCH_MON 0x0bf8
+#define AFE_DL6_RCH_MON 0x0bfc
+#define AFE_DL6_LCH_MON 0x0c00
+#define AFE_DL7_RCH_MON 0x0c04
+#define AFE_DL7_LCH_MON 0x0c08
+#define AFE_DL8_RCH_MON 0x0c0c
+#define AFE_DL8_LCH_MON 0x0c10
+#define AFE_VUL5_CON0 0x0c14
+#define AFE_VUL5_BASE_MSB 0x0c18
+#define AFE_VUL5_BASE 0x0c1c
+#define AFE_VUL5_CUR_MSB 0x0c20
+#define AFE_VUL5_CUR 0x0c24
+#define AFE_VUL5_END_MSB 0x0c28
+#define AFE_VUL5_END 0x0c2c
+#define AFE_VUL6_CON0 0x0c30
+#define AFE_VUL6_BASE_MSB 0x0c34
+#define AFE_VUL6_BASE 0x0c38
+#define AFE_VUL6_CUR_MSB 0x0c3c
+#define AFE_VUL6_CUR 0x0c40
+#define AFE_VUL6_END_MSB 0x0c44
+#define AFE_VUL6_END 0x0c48
+#define AFE_ADDA_DL_SDM_DCCOMP_CON 0x0c50
+#define AFE_ADDA_DL_SDM_TEST 0x0c54
+#define AFE_ADDA_DL_DC_COMP_CFG0 0x0c58
+#define AFE_ADDA_DL_DC_COMP_CFG1 0x0c5c
+#define AFE_ADDA_DL_SDM_FIFO_MON 0x0c60
+#define AFE_ADDA_DL_SRC_LCH_MON 0x0c64
+#define AFE_ADDA_DL_SRC_RCH_MON 0x0c68
+#define AFE_ADDA_DL_SDM_OUT_MON 0x0c6c
+#define AFE_ADDA_DL_SDM_DITHER_CON 0x0c70
+#define AFE_ADDA_DL_SDM_AUTO_RESET_CON 0x0c74
+#define AFE_CONNSYS_I2S_CON 0x0c78
+#define AFE_CONNSYS_I2S_MON 0x0c7c
+#define AFE_ASRC_2CH_CON0 0x0c80
+#define AFE_ASRC_2CH_CON1 0x0c84
+#define AFE_ASRC_2CH_CON2 0x0c88
+#define AFE_ASRC_2CH_CON3 0x0c8c
+#define AFE_ASRC_2CH_CON4 0x0c90
+#define AFE_ASRC_2CH_CON5 0x0c94
+#define AFE_ASRC_2CH_CON6 0x0c98
+#define AFE_ASRC_2CH_CON7 0x0c9c
+#define AFE_ASRC_2CH_CON8 0x0ca0
+#define AFE_ASRC_2CH_CON9 0x0ca4
+#define AFE_ASRC_2CH_CON10 0x0ca8
+#define AFE_ASRC_2CH_CON12 0x0cb0
+#define AFE_ASRC_2CH_CON13 0x0cb4
+#define AFE_ADDA6_IIR_COEF_02_01 0x0ce0
+#define AFE_ADDA6_IIR_COEF_04_03 0x0ce4
+#define AFE_ADDA6_IIR_COEF_06_05 0x0ce8
+#define AFE_ADDA6_IIR_COEF_08_07 0x0cec
+#define AFE_ADDA6_IIR_COEF_10_09 0x0cf0
+#define AFE_SE_PROT_SIDEBAND 0x0d38
+#define AFE_SE_DOMAIN_SIDEBAND0 0x0d3c
+#define AFE_ADDA_PREDIS_CON2 0x0d40
+#define AFE_ADDA_PREDIS_CON3 0x0d44
+#define AFE_MEMIF_CONN 0x0d50
+#define AFE_SE_DOMAIN_SIDEBAND1 0x0d54
+#define AFE_SE_DOMAIN_SIDEBAND2 0x0d58
+#define AFE_SE_DOMAIN_SIDEBAND3 0x0d5c
+#define AFE_CONN44 0x0d70
+#define AFE_CONN45 0x0d74
+#define AFE_CONN46 0x0d78
+#define AFE_CONN47 0x0d7c
+#define AFE_CONN44_1 0x0d80
+#define AFE_CONN45_1 0x0d84
+#define AFE_CONN46_1 0x0d88
+#define AFE_CONN47_1 0x0d8c
+#define AFE_DL9_CUR_MSB 0x0dc0
+#define AFE_DL9_CUR 0x0dc4
+#define AFE_DL9_END_MSB 0x0dc8
+#define AFE_DL9_END 0x0dcc
+#define AFE_HD_ENGEN_ENABLE 0x0dd0
+#define AFE_ADDA_DL_NLE_FIFO_MON 0x0dfc
+#define AFE_ADDA_MTKAIF_CFG0 0x0e00
+#define AFE_ADDA_MTKAIF_SYNCWORD_CFG 0x0e14
+#define AFE_ADDA_MTKAIF_RX_CFG0 0x0e20
+#define AFE_ADDA_MTKAIF_RX_CFG1 0x0e24
+#define AFE_ADDA_MTKAIF_RX_CFG2 0x0e28
+#define AFE_ADDA_MTKAIF_MON0 0x0e34
+#define AFE_ADDA_MTKAIF_MON1 0x0e38
+#define AFE_AUD_PAD_TOP 0x0e40
+#define AFE_DL_NLE_R_CFG0 0x0e44
+#define AFE_DL_NLE_R_CFG1 0x0e48
+#define AFE_DL_NLE_L_CFG0 0x0e4c
+#define AFE_DL_NLE_L_CFG1 0x0e50
+#define AFE_DL_NLE_R_MON0 0x0e54
+#define AFE_DL_NLE_R_MON1 0x0e58
+#define AFE_DL_NLE_R_MON2 0x0e5c
+#define AFE_DL_NLE_L_MON0 0x0e60
+#define AFE_DL_NLE_L_MON1 0x0e64
+#define AFE_DL_NLE_L_MON2 0x0e68
+#define AFE_DL_NLE_GAIN_CFG0 0x0e6c
+#define AFE_ADDA6_MTKAIF_CFG0 0x0e70
+#define AFE_ADDA6_MTKAIF_RX_CFG0 0x0e74
+#define AFE_ADDA6_MTKAIF_RX_CFG1 0x0e78
+#define AFE_ADDA6_MTKAIF_RX_CFG2 0x0e7c
+#define AFE_GENERAL1_ASRC_2CH_CON0 0x0e80
+#define AFE_GENERAL1_ASRC_2CH_CON1 0x0e84
+#define AFE_GENERAL1_ASRC_2CH_CON2 0x0e88
+#define AFE_GENERAL1_ASRC_2CH_CON3 0x0e8c
+#define AFE_GENERAL1_ASRC_2CH_CON4 0x0e90
+#define AFE_GENERAL1_ASRC_2CH_CON5 0x0e94
+#define AFE_GENERAL1_ASRC_2CH_CON6 0x0e98
+#define AFE_GENERAL1_ASRC_2CH_CON7 0x0e9c
+#define AFE_GENERAL1_ASRC_2CH_CON8 0x0ea0
+#define AFE_GENERAL1_ASRC_2CH_CON9 0x0ea4
+#define AFE_GENERAL1_ASRC_2CH_CON10 0x0ea8
+#define AFE_GENERAL1_ASRC_2CH_CON12 0x0eb0
+#define AFE_GENERAL1_ASRC_2CH_CON13 0x0eb4
+#define GENERAL_ASRC_MODE 0x0eb8
+#define GENERAL_ASRC_EN_ON 0x0ebc
+#define AFE_CONN48 0x0ec0
+#define AFE_CONN49 0x0ec4
+#define AFE_CONN50 0x0ec8
+#define AFE_CONN51 0x0ecc
+#define AFE_CONN52 0x0ed0
+#define AFE_CONN53 0x0ed4
+#define AFE_CONN54 0x0ed8
+#define AFE_CONN55 0x0edc
+#define AFE_CONN48_1 0x0ee0
+#define AFE_CONN49_1 0x0ee4
+#define AFE_CONN50_1 0x0ee8
+#define AFE_CONN51_1 0x0eec
+#define AFE_CONN52_1 0x0ef0
+#define AFE_CONN53_1 0x0ef4
+#define AFE_CONN54_1 0x0ef8
+#define AFE_CONN55_1 0x0efc
+#define AFE_GENERAL2_ASRC_2CH_CON0 0x0f00
+#define AFE_GENERAL2_ASRC_2CH_CON1 0x0f04
+#define AFE_GENERAL2_ASRC_2CH_CON2 0x0f08
+#define AFE_GENERAL2_ASRC_2CH_CON3 0x0f0c
+#define AFE_GENERAL2_ASRC_2CH_CON4 0x0f10
+#define AFE_GENERAL2_ASRC_2CH_CON5 0x0f14
+#define AFE_GENERAL2_ASRC_2CH_CON6 0x0f18
+#define AFE_GENERAL2_ASRC_2CH_CON7 0x0f1c
+#define AFE_GENERAL2_ASRC_2CH_CON8 0x0f20
+#define AFE_GENERAL2_ASRC_2CH_CON9 0x0f24
+#define AFE_GENERAL2_ASRC_2CH_CON10 0x0f28
+#define AFE_GENERAL2_ASRC_2CH_CON12 0x0f30
+#define AFE_GENERAL2_ASRC_2CH_CON13 0x0f34
+#define AFE_DL9_RCH_MON 0x0f38
+#define AFE_DL9_LCH_MON 0x0f3c
+#define AFE_DL5_CON0 0x0f4c
+#define AFE_DL5_BASE_MSB 0x0f50
+#define AFE_DL5_BASE 0x0f54
+#define AFE_DL5_CUR_MSB 0x0f58
+#define AFE_DL5_CUR 0x0f5c
+#define AFE_DL5_END_MSB 0x0f60
+#define AFE_DL5_END 0x0f64
+#define AFE_DL6_CON0 0x0f68
+#define AFE_DL6_BASE_MSB 0x0f6c
+#define AFE_DL6_BASE 0x0f70
+#define AFE_DL6_CUR_MSB 0x0f74
+#define AFE_DL6_CUR 0x0f78
+#define AFE_DL6_END_MSB 0x0f7c
+#define AFE_DL6_END 0x0f80
+#define AFE_DL7_CON0 0x0f84
+#define AFE_DL7_BASE_MSB 0x0f88
+#define AFE_DL7_BASE 0x0f8c
+#define AFE_DL7_CUR_MSB 0x0f90
+#define AFE_DL7_CUR 0x0f94
+#define AFE_DL7_END_MSB 0x0f98
+#define AFE_DL7_END 0x0f9c
+#define AFE_DL8_CON0 0x0fa0
+#define AFE_DL8_BASE_MSB 0x0fa4
+#define AFE_DL8_BASE 0x0fa8
+#define AFE_DL8_CUR_MSB 0x0fac
+#define AFE_DL8_CUR 0x0fb0
+#define AFE_DL8_END_MSB 0x0fb4
+#define AFE_DL8_END 0x0fb8
+#define AFE_DL9_CON0 0x0fbc
+#define AFE_DL9_BASE_MSB 0x0fc0
+#define AFE_DL9_BASE 0x0fc4
+#define AFE_SE_SECURE_CON 0x1004
+#define AFE_PROT_SIDEBAND_MON 0x1008
+#define AFE_DOMAIN_SIDEBAND0_MON 0x100c
+#define AFE_DOMAIN_SIDEBAND1_MON 0x1010
+#define AFE_DOMAIN_SIDEBAND2_MON 0x1014
+#define AFE_DOMAIN_SIDEBAND3_MON 0x1018
+#define AFE_SECURE_MASK_CONN0 0x1020
+#define AFE_SECURE_MASK_CONN1 0x1024
+#define AFE_SECURE_MASK_CONN2 0x1028
+#define AFE_SECURE_MASK_CONN3 0x102c
+#define AFE_SECURE_MASK_CONN4 0x1030
+#define AFE_SECURE_MASK_CONN5 0x1034
+#define AFE_SECURE_MASK_CONN6 0x1038
+#define AFE_SECURE_MASK_CONN7 0x103c
+#define AFE_SECURE_MASK_CONN8 0x1040
+#define AFE_SECURE_MASK_CONN9 0x1044
+#define AFE_SECURE_MASK_CONN10 0x1048
+#define AFE_SECURE_MASK_CONN11 0x104c
+#define AFE_SECURE_MASK_CONN12 0x1050
+#define AFE_SECURE_MASK_CONN13 0x1054
+#define AFE_SECURE_MASK_CONN14 0x1058
+#define AFE_SECURE_MASK_CONN15 0x105c
+#define AFE_SECURE_MASK_CONN16 0x1060
+#define AFE_SECURE_MASK_CONN17 0x1064
+#define AFE_SECURE_MASK_CONN18 0x1068
+#define AFE_SECURE_MASK_CONN19 0x106c
+#define AFE_SECURE_MASK_CONN20 0x1070
+#define AFE_SECURE_MASK_CONN21 0x1074
+#define AFE_SECURE_MASK_CONN22 0x1078
+#define AFE_SECURE_MASK_CONN23 0x107c
+#define AFE_SECURE_MASK_CONN24 0x1080
+#define AFE_SECURE_MASK_CONN25 0x1084
+#define AFE_SECURE_MASK_CONN26 0x1088
+#define AFE_SECURE_MASK_CONN27 0x108c
+#define AFE_SECURE_MASK_CONN28 0x1090
+#define AFE_SECURE_MASK_CONN29 0x1094
+#define AFE_SECURE_MASK_CONN30 0x1098
+#define AFE_SECURE_MASK_CONN31 0x109c
+#define AFE_SECURE_MASK_CONN32 0x10a0
+#define AFE_SECURE_MASK_CONN33 0x10a4
+#define AFE_SECURE_MASK_CONN34 0x10a8
+#define AFE_SECURE_MASK_CONN35 0x10ac
+#define AFE_SECURE_MASK_CONN36 0x10b0
+#define AFE_SECURE_MASK_CONN37 0x10b4
+#define AFE_SECURE_MASK_CONN38 0x10b8
+#define AFE_SECURE_MASK_CONN39 0x10bc
+#define AFE_SECURE_MASK_CONN40 0x10c0
+#define AFE_SECURE_MASK_CONN41 0x10c4
+#define AFE_SECURE_MASK_CONN42 0x10c8
+#define AFE_SECURE_MASK_CONN43 0x10cc
+#define AFE_SECURE_MASK_CONN44 0x10d0
+#define AFE_SECURE_MASK_CONN45 0x10d4
+#define AFE_SECURE_MASK_CONN46 0x10d8
+#define AFE_SECURE_MASK_CONN47 0x10dc
+#define AFE_SECURE_MASK_CONN48 0x10e0
+#define AFE_SECURE_MASK_CONN49 0x10e4
+#define AFE_SECURE_MASK_CONN50 0x10e8
+#define AFE_SECURE_MASK_CONN51 0x10ec
+#define AFE_SECURE_MASK_CONN52 0x10f0
+#define AFE_SECURE_MASK_CONN53 0x10f4
+#define AFE_SECURE_MASK_CONN54 0x10f8
+#define AFE_SECURE_MASK_CONN55 0x10fc
+#define AFE_SECURE_MASK_CONN56 0x1100
+#define AFE_SECURE_MASK_CONN57 0x1104
+#define AFE_SECURE_MASK_CONN0_1 0x1108
+#define AFE_SECURE_MASK_CONN1_1 0x110c
+#define AFE_SECURE_MASK_CONN2_1 0x1110
+#define AFE_SECURE_MASK_CONN3_1 0x1114
+#define AFE_SECURE_MASK_CONN4_1 0x1118
+#define AFE_SECURE_MASK_CONN5_1 0x111c
+#define AFE_SECURE_MASK_CONN6_1 0x1120
+#define AFE_SECURE_MASK_CONN7_1 0x1124
+#define AFE_SECURE_MASK_CONN8_1 0x1128
+#define AFE_SECURE_MASK_CONN9_1 0x112c
+#define AFE_SECURE_MASK_CONN10_1 0x1130
+#define AFE_SECURE_MASK_CONN11_1 0x1134
+#define AFE_SECURE_MASK_CONN12_1 0x1138
+#define AFE_SECURE_MASK_CONN13_1 0x113c
+#define AFE_SECURE_MASK_CONN14_1 0x1140
+#define AFE_SECURE_MASK_CONN15_1 0x1144
+#define AFE_SECURE_MASK_CONN16_1 0x1148
+#define AFE_SECURE_MASK_CONN17_1 0x114c
+#define AFE_SECURE_MASK_CONN18_1 0x1150
+#define AFE_SECURE_MASK_CONN19_1 0x1154
+#define AFE_SECURE_MASK_CONN20_1 0x1158
+#define AFE_SECURE_MASK_CONN21_1 0x115c
+#define AFE_SECURE_MASK_CONN22_1 0x1160
+#define AFE_SECURE_MASK_CONN23_1 0x1164
+#define AFE_SECURE_MASK_CONN24_1 0x1168
+#define AFE_SECURE_MASK_CONN25_1 0x116c
+#define AFE_SECURE_MASK_CONN26_1 0x1170
+#define AFE_SECURE_MASK_CONN27_1 0x1174
+#define AFE_SECURE_MASK_CONN28_1 0x1178
+#define AFE_SECURE_MASK_CONN29_1 0x117c
+#define AFE_SECURE_MASK_CONN30_1 0x1180
+#define AFE_SECURE_MASK_CONN31_1 0x1184
+#define AFE_SECURE_MASK_CONN32_1 0x1188
+#define AFE_SECURE_MASK_CONN33_1 0x118c
+#define AFE_SECURE_MASK_CONN34_1 0x1190
+#define AFE_SECURE_MASK_CONN35_1 0x1194
+#define AFE_SECURE_MASK_CONN36_1 0x1198
+#define AFE_SECURE_MASK_CONN37_1 0x119c
+#define AFE_SECURE_MASK_CONN38_1 0x11a0
+#define AFE_SECURE_MASK_CONN39_1 0x11a4
+#define AFE_SECURE_MASK_CONN40_1 0x11a8
+#define AFE_SECURE_MASK_CONN41_1 0x11ac
+#define AFE_SECURE_MASK_CONN42_1 0x11b0
+#define AFE_SECURE_MASK_CONN43_1 0x11b4
+#define AFE_SECURE_MASK_CONN44_1 0x11b8
+#define AFE_SECURE_MASK_CONN45_1 0x11bc
+#define AFE_SECURE_MASK_CONN46_1 0x11c0
+#define AFE_SECURE_MASK_CONN47_1 0x11c4
+#define AFE_SECURE_MASK_CONN48_1 0x11c8
+#define AFE_SECURE_MASK_CONN49_1 0x11cc
+#define AFE_SECURE_MASK_CONN50_1 0x11d0
+#define AFE_SECURE_MASK_CONN51_1 0x11d4
+#define AFE_SECURE_MASK_CONN52_1 0x11d8
+#define AFE_SECURE_MASK_CONN53_1 0x11dc
+#define AFE_SECURE_MASK_CONN54_1 0x11e0
+#define AFE_SECURE_MASK_CONN55_1 0x11e4
+#define AFE_SECURE_MASK_CONN56_1 0x11e8
+#define AFE_SECURE_MASK_TINY_CONN0 0x1200
+#define AFE_SECURE_MASK_TINY_CONN1 0x1204
+#define AFE_SECURE_MASK_TINY_CONN2 0x1208
+#define AFE_SECURE_MASK_TINY_CONN3 0x120c
+#define AFE_SECURE_MASK_TINY_CONN4 0x1210
+#define AFE_SECURE_MASK_TINY_CONN5 0x1214
+#define AFE_SECURE_MASK_TINY_CONN6 0x1218
+#define AFE_SECURE_MASK_TINY_CONN7 0x121c
+
+#define AFE_MAX_REGISTER AFE_SECURE_MASK_TINY_CONN7
+
+#define AFE_IRQ_STATUS_BITS 0x87FFFFFF
+#define AFE_IRQ_CNT_SHIFT 0
+#define AFE_IRQ_CNT_MASK 0x3ffff
+
+#endif
diff --git a/sound/soc/mediatek/mt8195/Makefile b/sound/soc/mediatek/mt8195/Makefile
new file mode 100644
index 000000000000..014e93dace26
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt8195-afe-y := \
+ mt8195-audsys-clk.o \
+ mt8195-afe-clk.o \
+ mt8195-afe-pcm.o \
+ mt8195-dai-adda.o \
+ mt8195-dai-etdm.o \
+ mt8195-dai-pcm.o
+
+obj-$(CONFIG_SND_SOC_MT8195) += snd-soc-mt8195-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT8195_MT6359) += mt8195-mt6359.o
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c
new file mode 100644
index 000000000000..f35318ae0739
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8195-afe-clk.c -- Mediatek 8195 afe clock ctrl
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+
+#include "mt8195-afe-common.h"
+#include "mt8195-afe-clk.h"
+#include "mt8195-reg.h"
+#include "mt8195-audsys-clk.h"
+
+static const char *aud_clks[MT8195_CLK_NUM] = {
+ /* xtal */
+ [MT8195_CLK_XTAL_26M] = "clk26m",
+ /* divider */
+ [MT8195_CLK_TOP_APLL1] = "apll1_ck",
+ [MT8195_CLK_TOP_APLL2] = "apll2_ck",
+ [MT8195_CLK_TOP_APLL12_DIV0] = "apll12_div0",
+ [MT8195_CLK_TOP_APLL12_DIV1] = "apll12_div1",
+ [MT8195_CLK_TOP_APLL12_DIV2] = "apll12_div2",
+ [MT8195_CLK_TOP_APLL12_DIV3] = "apll12_div3",
+ [MT8195_CLK_TOP_APLL12_DIV9] = "apll12_div9",
+ /* mux */
+ [MT8195_CLK_TOP_A1SYS_HP_SEL] = "a1sys_hp_sel",
+ [MT8195_CLK_TOP_AUD_INTBUS_SEL] = "aud_intbus_sel",
+ [MT8195_CLK_TOP_AUDIO_H_SEL] = "audio_h_sel",
+ [MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL] = "audio_local_bus_sel",
+ [MT8195_CLK_TOP_DPTX_M_SEL] = "dptx_m_sel",
+ [MT8195_CLK_TOP_I2SO1_M_SEL] = "i2so1_m_sel",
+ [MT8195_CLK_TOP_I2SO2_M_SEL] = "i2so2_m_sel",
+ [MT8195_CLK_TOP_I2SI1_M_SEL] = "i2si1_m_sel",
+ [MT8195_CLK_TOP_I2SI2_M_SEL] = "i2si2_m_sel",
+ /* clock gate */
+ [MT8195_CLK_INFRA_AO_AUDIO_26M_B] = "infra_ao_audio_26m_b",
+ [MT8195_CLK_SCP_ADSP_AUDIODSP] = "scp_adsp_audiodsp",
+ /* afe clock gate */
+ [MT8195_CLK_AUD_AFE] = "aud_afe",
+ [MT8195_CLK_AUD_APLL1_TUNER] = "aud_apll1_tuner",
+ [MT8195_CLK_AUD_APLL2_TUNER] = "aud_apll2_tuner",
+ [MT8195_CLK_AUD_APLL] = "aud_apll",
+ [MT8195_CLK_AUD_APLL2] = "aud_apll2",
+ [MT8195_CLK_AUD_DAC] = "aud_dac",
+ [MT8195_CLK_AUD_ADC] = "aud_adc",
+ [MT8195_CLK_AUD_DAC_HIRES] = "aud_dac_hires",
+ [MT8195_CLK_AUD_A1SYS_HP] = "aud_a1sys_hp",
+ [MT8195_CLK_AUD_ADC_HIRES] = "aud_adc_hires",
+ [MT8195_CLK_AUD_ADDA6_ADC] = "aud_adda6_adc",
+ [MT8195_CLK_AUD_ADDA6_ADC_HIRES] = "aud_adda6_adc_hires",
+ [MT8195_CLK_AUD_I2SIN] = "aud_i2sin",
+ [MT8195_CLK_AUD_TDM_IN] = "aud_tdm_in",
+ [MT8195_CLK_AUD_I2S_OUT] = "aud_i2s_out",
+ [MT8195_CLK_AUD_TDM_OUT] = "aud_tdm_out",
+ [MT8195_CLK_AUD_HDMI_OUT] = "aud_hdmi_out",
+ [MT8195_CLK_AUD_ASRC11] = "aud_asrc11",
+ [MT8195_CLK_AUD_ASRC12] = "aud_asrc12",
+ [MT8195_CLK_AUD_A1SYS] = "aud_a1sys",
+ [MT8195_CLK_AUD_A2SYS] = "aud_a2sys",
+ [MT8195_CLK_AUD_PCMIF] = "aud_pcmif",
+ [MT8195_CLK_AUD_MEMIF_UL1] = "aud_memif_ul1",
+ [MT8195_CLK_AUD_MEMIF_UL2] = "aud_memif_ul2",
+ [MT8195_CLK_AUD_MEMIF_UL3] = "aud_memif_ul3",
+ [MT8195_CLK_AUD_MEMIF_UL4] = "aud_memif_ul4",
+ [MT8195_CLK_AUD_MEMIF_UL5] = "aud_memif_ul5",
+ [MT8195_CLK_AUD_MEMIF_UL6] = "aud_memif_ul6",
+ [MT8195_CLK_AUD_MEMIF_UL8] = "aud_memif_ul8",
+ [MT8195_CLK_AUD_MEMIF_UL9] = "aud_memif_ul9",
+ [MT8195_CLK_AUD_MEMIF_UL10] = "aud_memif_ul10",
+ [MT8195_CLK_AUD_MEMIF_DL2] = "aud_memif_dl2",
+ [MT8195_CLK_AUD_MEMIF_DL3] = "aud_memif_dl3",
+ [MT8195_CLK_AUD_MEMIF_DL6] = "aud_memif_dl6",
+ [MT8195_CLK_AUD_MEMIF_DL7] = "aud_memif_dl7",
+ [MT8195_CLK_AUD_MEMIF_DL8] = "aud_memif_dl8",
+ [MT8195_CLK_AUD_MEMIF_DL10] = "aud_memif_dl10",
+ [MT8195_CLK_AUD_MEMIF_DL11] = "aud_memif_dl11",
+};
+
+struct mt8195_afe_tuner_cfg {
+ unsigned int id;
+ int apll_div_reg;
+ unsigned int apll_div_shift;
+ unsigned int apll_div_maskbit;
+ unsigned int apll_div_default;
+ int ref_ck_sel_reg;
+ unsigned int ref_ck_sel_shift;
+ unsigned int ref_ck_sel_maskbit;
+ unsigned int ref_ck_sel_default;
+ int tuner_en_reg;
+ unsigned int tuner_en_shift;
+ unsigned int tuner_en_maskbit;
+ int upper_bound_reg;
+ unsigned int upper_bound_shift;
+ unsigned int upper_bound_maskbit;
+ unsigned int upper_bound_default;
+ spinlock_t ctrl_lock; /* lock for apll tuner ctrl*/
+ int ref_cnt;
+};
+
+static struct mt8195_afe_tuner_cfg mt8195_afe_tuner_cfgs[MT8195_AUD_PLL_NUM] = {
+ [MT8195_AUD_PLL1] = {
+ .id = MT8195_AUD_PLL1,
+ .apll_div_reg = AFE_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0xf,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 1,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x2,
+ .tuner_en_reg = AFE_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_APLL_TUNER_CFG,
+ .upper_bound_shift = 8,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x3,
+ },
+ [MT8195_AUD_PLL2] = {
+ .id = MT8195_AUD_PLL2,
+ .apll_div_reg = AFE_APLL_TUNER_CFG1,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0xf,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_APLL_TUNER_CFG1,
+ .ref_ck_sel_shift = 1,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x1,
+ .tuner_en_reg = AFE_APLL_TUNER_CFG1,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_APLL_TUNER_CFG1,
+ .upper_bound_shift = 8,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x3,
+ },
+ [MT8195_AUD_PLL3] = {
+ .id = MT8195_AUD_PLL3,
+ .apll_div_reg = AFE_EARC_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x3,
+ .ref_ck_sel_reg = AFE_EARC_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 24,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x0,
+ .tuner_en_reg = AFE_EARC_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_EARC_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+ [MT8195_AUD_PLL4] = {
+ .id = MT8195_AUD_PLL4,
+ .apll_div_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_SPDIFIN_APLL_TUNER_CFG1,
+ .ref_ck_sel_shift = 8,
+ .ref_ck_sel_maskbit = 0x1,
+ .ref_ck_sel_default = 0,
+ .tuner_en_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+ [MT8195_AUD_PLL5] = {
+ .id = MT8195_AUD_PLL5,
+ .apll_div_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x3,
+ .ref_ck_sel_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 24,
+ .ref_ck_sel_maskbit = 0x1,
+ .ref_ck_sel_default = 0,
+ .tuner_en_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+};
+
+static struct mt8195_afe_tuner_cfg *mt8195_afe_found_apll_tuner(unsigned int id)
+{
+ if (id >= MT8195_AUD_PLL_NUM)
+ return NULL;
+
+ return &mt8195_afe_tuner_cfgs[id];
+}
+
+static int mt8195_afe_init_apll_tuner(unsigned int id)
+{
+ struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id);
+
+ if (!cfg)
+ return -EINVAL;
+
+ cfg->ref_cnt = 0;
+ spin_lock_init(&cfg->ctrl_lock);
+
+ return 0;
+}
+
+static int mt8195_afe_setup_apll_tuner(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ const struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id);
+
+ if (!cfg)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap, cfg->apll_div_reg,
+ cfg->apll_div_maskbit << cfg->apll_div_shift,
+ cfg->apll_div_default << cfg->apll_div_shift);
+
+ regmap_update_bits(afe->regmap, cfg->ref_ck_sel_reg,
+ cfg->ref_ck_sel_maskbit << cfg->ref_ck_sel_shift,
+ cfg->ref_ck_sel_default << cfg->ref_ck_sel_shift);
+
+ regmap_update_bits(afe->regmap, cfg->upper_bound_reg,
+ cfg->upper_bound_maskbit << cfg->upper_bound_shift,
+ cfg->upper_bound_default << cfg->upper_bound_shift);
+
+ return 0;
+}
+
+static int mt8195_afe_enable_tuner_clk(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+
+ switch (id) {
+ case MT8195_AUD_PLL1:
+ mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL]);
+ mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL1_TUNER]);
+ break;
+ case MT8195_AUD_PLL2:
+ mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2]);
+ mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2_TUNER]);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt8195_afe_disable_tuner_clk(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+
+ switch (id) {
+ case MT8195_AUD_PLL1:
+ mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL1_TUNER]);
+ mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL]);
+ break;
+ case MT8195_AUD_PLL2:
+ mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2_TUNER]);
+ mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2]);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mt8195_afe_enable_apll_tuner(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id);
+ unsigned long flags;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ ret = mt8195_afe_setup_apll_tuner(afe, id);
+ if (ret)
+ return ret;
+
+ ret = mt8195_afe_enable_tuner_clk(afe, id);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&cfg->ctrl_lock, flags);
+
+ cfg->ref_cnt++;
+ if (cfg->ref_cnt == 1)
+ regmap_update_bits(afe->regmap,
+ cfg->tuner_en_reg,
+ cfg->tuner_en_maskbit << cfg->tuner_en_shift,
+ 1 << cfg->tuner_en_shift);
+
+ spin_unlock_irqrestore(&cfg->ctrl_lock, flags);
+
+ return 0;
+}
+
+static int mt8195_afe_disable_apll_tuner(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id);
+ unsigned long flags;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&cfg->ctrl_lock, flags);
+
+ cfg->ref_cnt--;
+ if (cfg->ref_cnt == 0)
+ regmap_update_bits(afe->regmap,
+ cfg->tuner_en_reg,
+ cfg->tuner_en_maskbit << cfg->tuner_en_shift,
+ 0 << cfg->tuner_en_shift);
+ else if (cfg->ref_cnt < 0)
+ cfg->ref_cnt = 0;
+
+ spin_unlock_irqrestore(&cfg->ctrl_lock, flags);
+
+ ret = mt8195_afe_disable_tuner_clk(afe, id);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int mt8195_afe_get_mclk_source_clk_id(int sel)
+{
+ switch (sel) {
+ case MT8195_MCK_SEL_26M:
+ return MT8195_CLK_XTAL_26M;
+ case MT8195_MCK_SEL_APLL1:
+ return MT8195_CLK_TOP_APLL1;
+ case MT8195_MCK_SEL_APLL2:
+ return MT8195_CLK_TOP_APLL2;
+ default:
+ return -EINVAL;
+ }
+}
+
+int mt8195_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int clk_id = mt8195_afe_get_mclk_source_clk_id(apll);
+
+ if (clk_id < 0) {
+ dev_dbg(afe->dev, "invalid clk id\n");
+ return 0;
+ }
+
+ return clk_get_rate(afe_priv->clk[clk_id]);
+}
+
+int mt8195_afe_get_default_mclk_source_by_rate(int rate)
+{
+ return ((rate % 8000) == 0) ?
+ MT8195_MCK_SEL_APLL1 : MT8195_MCK_SEL_APLL2;
+}
+
+int mt8195_afe_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int i, ret;
+
+ mt8195_audsys_clk_register(afe);
+
+ afe_priv->clk =
+ devm_kcalloc(afe->dev, MT8195_CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < MT8195_CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_dbg(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
+ __func__, aud_clks[i],
+ PTR_ERR(afe_priv->clk[i]));
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ /* initial tuner */
+ for (i = 0; i < MT8195_AUD_PLL_NUM; i++) {
+ ret = mt8195_afe_init_apll_tuner(i);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), init apll_tuner%d failed",
+ __func__, (i + 1));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int mt8195_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to enable clk\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ dev_dbg(afe->dev, "NULL clk\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8195_afe_enable_clk);
+
+void mt8195_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_disable_unprepare(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+EXPORT_SYMBOL_GPL(mt8195_afe_disable_clk);
+
+int mt8195_afe_prepare_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_prepare(clk);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to prepare clk\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ dev_dbg(afe->dev, "NULL clk\n");
+ }
+ return 0;
+}
+
+void mt8195_afe_unprepare_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_unprepare(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+
+int mt8195_afe_enable_clk_atomic(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_enable(clk);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to clk enable\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ dev_dbg(afe->dev, "NULL clk\n");
+ }
+ return 0;
+}
+
+void mt8195_afe_disable_clk_atomic(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_disable(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+
+int mt8195_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to set clk rate\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mt8195_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent)
+{
+ int ret;
+
+ if (clk && parent) {
+ ret = clk_set_parent(clk, parent);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to set clk parent\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int get_top_cg_reg(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8195_TOP_CG_A1SYS_TIMING:
+ case MT8195_TOP_CG_A2SYS_TIMING:
+ case MT8195_TOP_CG_26M_TIMING:
+ return ASYS_TOP_CON;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_mask(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8195_TOP_CG_A1SYS_TIMING:
+ return ASYS_TOP_CON_A1SYS_TIMING_ON;
+ case MT8195_TOP_CG_A2SYS_TIMING:
+ return ASYS_TOP_CON_A2SYS_TIMING_ON;
+ case MT8195_TOP_CG_26M_TIMING:
+ return ASYS_TOP_CON_26M_TIMING_ON;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_on_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8195_TOP_CG_A1SYS_TIMING:
+ case MT8195_TOP_CG_A2SYS_TIMING:
+ case MT8195_TOP_CG_26M_TIMING:
+ return get_top_cg_mask(cg_type);
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_off_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8195_TOP_CG_A1SYS_TIMING:
+ case MT8195_TOP_CG_A2SYS_TIMING:
+ case MT8195_TOP_CG_26M_TIMING:
+ return 0;
+ default:
+ return get_top_cg_mask(cg_type);
+ }
+}
+
+static int mt8195_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_on_val(cg_type);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ return 0;
+}
+
+static int mt8195_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_off_val(cg_type);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ return 0;
+}
+
+int mt8195_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int i;
+ static const unsigned int clk_array[] = {
+ MT8195_CLK_SCP_ADSP_AUDIODSP, /* bus clock for infra */
+ MT8195_CLK_TOP_AUDIO_H_SEL, /* clock for ADSP bus */
+ MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL, /* bus clock for DRAM access */
+ MT8195_CLK_TOP_AUD_INTBUS_SEL, /* bus clock for AFE SRAM access */
+ MT8195_CLK_INFRA_AO_AUDIO_26M_B, /* audio 26M clock */
+ MT8195_CLK_AUD_AFE, /* AFE HW master switch */
+ MT8195_CLK_AUD_A1SYS_HP, /* AFE HW clock*/
+ MT8195_CLK_AUD_A1SYS, /* AFE HW clock */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(clk_array); i++)
+ mt8195_afe_enable_clk(afe, afe_priv->clk[clk_array[i]]);
+
+ return 0;
+}
+
+int mt8195_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int i;
+ static const unsigned int clk_array[] = {
+ MT8195_CLK_AUD_A1SYS,
+ MT8195_CLK_AUD_A1SYS_HP,
+ MT8195_CLK_AUD_AFE,
+ MT8195_CLK_INFRA_AO_AUDIO_26M_B,
+ MT8195_CLK_TOP_AUD_INTBUS_SEL,
+ MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL,
+ MT8195_CLK_TOP_AUDIO_H_SEL,
+ MT8195_CLK_SCP_ADSP_AUDIODSP,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(clk_array); i++)
+ mt8195_afe_disable_clk(afe, afe_priv->clk[clk_array[i]]);
+
+ return 0;
+}
+
+static int mt8195_afe_enable_afe_on(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+ return 0;
+}
+
+static int mt8195_afe_disable_afe_on(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
+ return 0;
+}
+
+static int mt8195_afe_enable_timing_sys(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int i;
+ static const unsigned int clk_array[] = {
+ MT8195_CLK_AUD_A1SYS,
+ MT8195_CLK_AUD_A2SYS,
+ };
+ static const unsigned int cg_array[] = {
+ MT8195_TOP_CG_A1SYS_TIMING,
+ MT8195_TOP_CG_A2SYS_TIMING,
+ MT8195_TOP_CG_26M_TIMING,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(clk_array); i++)
+ mt8195_afe_enable_clk(afe, afe_priv->clk[clk_array[i]]);
+
+ for (i = 0; i < ARRAY_SIZE(cg_array); i++)
+ mt8195_afe_enable_top_cg(afe, cg_array[i]);
+
+ return 0;
+}
+
+static int mt8195_afe_disable_timing_sys(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int i;
+ static const unsigned int clk_array[] = {
+ MT8195_CLK_AUD_A2SYS,
+ MT8195_CLK_AUD_A1SYS,
+ };
+ static const unsigned int cg_array[] = {
+ MT8195_TOP_CG_26M_TIMING,
+ MT8195_TOP_CG_A2SYS_TIMING,
+ MT8195_TOP_CG_A1SYS_TIMING,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(cg_array); i++)
+ mt8195_afe_disable_top_cg(afe, cg_array[i]);
+
+ for (i = 0; i < ARRAY_SIZE(clk_array); i++)
+ mt8195_afe_disable_clk(afe, afe_priv->clk[clk_array[i]]);
+
+ return 0;
+}
+
+int mt8195_afe_enable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8195_afe_enable_timing_sys(afe);
+
+ mt8195_afe_enable_afe_on(afe);
+
+ mt8195_afe_enable_apll_tuner(afe, MT8195_AUD_PLL1);
+ mt8195_afe_enable_apll_tuner(afe, MT8195_AUD_PLL2);
+
+ return 0;
+}
+
+int mt8195_afe_disable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8195_afe_disable_apll_tuner(afe, MT8195_AUD_PLL2);
+ mt8195_afe_disable_apll_tuner(afe, MT8195_AUD_PLL1);
+
+ mt8195_afe_disable_afe_on(afe);
+
+ mt8195_afe_disable_timing_sys(afe);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.h b/sound/soc/mediatek/mt8195/mt8195-afe-clk.h
new file mode 100644
index 000000000000..a08c0ee6c860
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8195-afe-clk.h -- Mediatek 8195 afe clock ctrl definition
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MT8195_AFE_CLK_H_
+#define _MT8195_AFE_CLK_H_
+
+enum {
+ /* xtal */
+ MT8195_CLK_XTAL_26M,
+ /* divider */
+ MT8195_CLK_TOP_APLL1,
+ MT8195_CLK_TOP_APLL2,
+ MT8195_CLK_TOP_APLL12_DIV0,
+ MT8195_CLK_TOP_APLL12_DIV1,
+ MT8195_CLK_TOP_APLL12_DIV2,
+ MT8195_CLK_TOP_APLL12_DIV3,
+ MT8195_CLK_TOP_APLL12_DIV9,
+ /* mux */
+ MT8195_CLK_TOP_A1SYS_HP_SEL,
+ MT8195_CLK_TOP_AUD_INTBUS_SEL,
+ MT8195_CLK_TOP_AUDIO_H_SEL,
+ MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL,
+ MT8195_CLK_TOP_DPTX_M_SEL,
+ MT8195_CLK_TOP_I2SO1_M_SEL,
+ MT8195_CLK_TOP_I2SO2_M_SEL,
+ MT8195_CLK_TOP_I2SI1_M_SEL,
+ MT8195_CLK_TOP_I2SI2_M_SEL,
+ /* clock gate */
+ MT8195_CLK_INFRA_AO_AUDIO_26M_B,
+ MT8195_CLK_SCP_ADSP_AUDIODSP,
+ MT8195_CLK_AUD_AFE,
+ MT8195_CLK_AUD_APLL1_TUNER,
+ MT8195_CLK_AUD_APLL2_TUNER,
+ MT8195_CLK_AUD_APLL,
+ MT8195_CLK_AUD_APLL2,
+ MT8195_CLK_AUD_DAC,
+ MT8195_CLK_AUD_ADC,
+ MT8195_CLK_AUD_DAC_HIRES,
+ MT8195_CLK_AUD_A1SYS_HP,
+ MT8195_CLK_AUD_ADC_HIRES,
+ MT8195_CLK_AUD_ADDA6_ADC,
+ MT8195_CLK_AUD_ADDA6_ADC_HIRES,
+ MT8195_CLK_AUD_I2SIN,
+ MT8195_CLK_AUD_TDM_IN,
+ MT8195_CLK_AUD_I2S_OUT,
+ MT8195_CLK_AUD_TDM_OUT,
+ MT8195_CLK_AUD_HDMI_OUT,
+ MT8195_CLK_AUD_ASRC11,
+ MT8195_CLK_AUD_ASRC12,
+ MT8195_CLK_AUD_A1SYS,
+ MT8195_CLK_AUD_A2SYS,
+ MT8195_CLK_AUD_PCMIF,
+ MT8195_CLK_AUD_MEMIF_UL1,
+ MT8195_CLK_AUD_MEMIF_UL2,
+ MT8195_CLK_AUD_MEMIF_UL3,
+ MT8195_CLK_AUD_MEMIF_UL4,
+ MT8195_CLK_AUD_MEMIF_UL5,
+ MT8195_CLK_AUD_MEMIF_UL6,
+ MT8195_CLK_AUD_MEMIF_UL8,
+ MT8195_CLK_AUD_MEMIF_UL9,
+ MT8195_CLK_AUD_MEMIF_UL10,
+ MT8195_CLK_AUD_MEMIF_DL2,
+ MT8195_CLK_AUD_MEMIF_DL3,
+ MT8195_CLK_AUD_MEMIF_DL6,
+ MT8195_CLK_AUD_MEMIF_DL7,
+ MT8195_CLK_AUD_MEMIF_DL8,
+ MT8195_CLK_AUD_MEMIF_DL10,
+ MT8195_CLK_AUD_MEMIF_DL11,
+ MT8195_CLK_NUM,
+};
+
+enum {
+ MT8195_MCK_SEL_26M,
+ MT8195_MCK_SEL_APLL1,
+ MT8195_MCK_SEL_APLL2,
+ MT8195_MCK_SEL_APLL3,
+ MT8195_MCK_SEL_APLL4,
+ MT8195_MCK_SEL_APLL5,
+ MT8195_MCK_SEL_HDMIRX_APLL,
+ MT8195_MCK_SEL_NUM,
+};
+
+enum {
+ MT8195_AUD_PLL1,
+ MT8195_AUD_PLL2,
+ MT8195_AUD_PLL3,
+ MT8195_AUD_PLL4,
+ MT8195_AUD_PLL5,
+ MT8195_AUD_PLL_NUM,
+};
+
+struct mtk_base_afe;
+
+int mt8195_afe_get_mclk_source_clk_id(int sel);
+int mt8195_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll);
+int mt8195_afe_get_default_mclk_source_by_rate(int rate);
+int mt8195_afe_init_clock(struct mtk_base_afe *afe);
+int mt8195_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
+void mt8195_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8195_afe_prepare_clk(struct mtk_base_afe *afe, struct clk *clk);
+void mt8195_afe_unprepare_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8195_afe_enable_clk_atomic(struct mtk_base_afe *afe, struct clk *clk);
+void mt8195_afe_disable_clk_atomic(struct mtk_base_afe *afe, struct clk *clk);
+int mt8195_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate);
+int mt8195_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent);
+int mt8195_afe_enable_main_clock(struct mtk_base_afe *afe);
+int mt8195_afe_disable_main_clock(struct mtk_base_afe *afe);
+int mt8195_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
+int mt8195_afe_disable_reg_rw_clk(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-common.h b/sound/soc/mediatek/mt8195/mt8195-afe-common.h
new file mode 100644
index 000000000000..f93f439e2bd9
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-common.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8195-afe-common.h -- Mediatek 8195 audio driver definitions
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MT_8195_AFE_COMMON_H_
+#define _MT_8195_AFE_COMMON_H_
+
+#include <sound/soc.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT8195_DAI_START,
+ MT8195_AFE_MEMIF_START = MT8195_DAI_START,
+ MT8195_AFE_MEMIF_DL2 = MT8195_AFE_MEMIF_START,
+ MT8195_AFE_MEMIF_DL3,
+ MT8195_AFE_MEMIF_DL6,
+ MT8195_AFE_MEMIF_DL7,
+ MT8195_AFE_MEMIF_DL8,
+ MT8195_AFE_MEMIF_DL10,
+ MT8195_AFE_MEMIF_DL11,
+ MT8195_AFE_MEMIF_UL_START,
+ MT8195_AFE_MEMIF_UL1 = MT8195_AFE_MEMIF_UL_START,
+ MT8195_AFE_MEMIF_UL2,
+ MT8195_AFE_MEMIF_UL3,
+ MT8195_AFE_MEMIF_UL4,
+ MT8195_AFE_MEMIF_UL5,
+ MT8195_AFE_MEMIF_UL6,
+ MT8195_AFE_MEMIF_UL8,
+ MT8195_AFE_MEMIF_UL9,
+ MT8195_AFE_MEMIF_UL10,
+ MT8195_AFE_MEMIF_END,
+ MT8195_AFE_MEMIF_NUM = (MT8195_AFE_MEMIF_END - MT8195_AFE_MEMIF_START),
+ MT8195_AFE_IO_START = MT8195_AFE_MEMIF_END,
+ MT8195_AFE_IO_DL_SRC = MT8195_AFE_IO_START,
+ MT8195_AFE_IO_DPTX,
+ MT8195_AFE_IO_ETDM_START,
+ MT8195_AFE_IO_ETDM1_IN = MT8195_AFE_IO_ETDM_START,
+ MT8195_AFE_IO_ETDM2_IN,
+ MT8195_AFE_IO_ETDM1_OUT,
+ MT8195_AFE_IO_ETDM2_OUT,
+ MT8195_AFE_IO_ETDM3_OUT,
+ MT8195_AFE_IO_ETDM_END,
+ MT8195_AFE_IO_ETDM_NUM =
+ (MT8195_AFE_IO_ETDM_END - MT8195_AFE_IO_ETDM_START),
+ MT8195_AFE_IO_PCM = MT8195_AFE_IO_ETDM_END,
+ MT8195_AFE_IO_UL_SRC1,
+ MT8195_AFE_IO_UL_SRC2,
+ MT8195_AFE_IO_END,
+ MT8195_AFE_IO_NUM = (MT8195_AFE_IO_END - MT8195_AFE_IO_START),
+ MT8195_DAI_END = MT8195_AFE_IO_END,
+ MT8195_DAI_NUM = (MT8195_DAI_END - MT8195_DAI_START),
+};
+
+enum {
+ MT8195_TOP_CG_A1SYS_TIMING,
+ MT8195_TOP_CG_A2SYS_TIMING,
+ MT8195_TOP_CG_26M_TIMING,
+ MT8195_TOP_CG_NUM,
+};
+
+enum {
+ MT8195_AFE_IRQ_1,
+ MT8195_AFE_IRQ_2,
+ MT8195_AFE_IRQ_3,
+ MT8195_AFE_IRQ_8,
+ MT8195_AFE_IRQ_9,
+ MT8195_AFE_IRQ_10,
+ MT8195_AFE_IRQ_13,
+ MT8195_AFE_IRQ_14,
+ MT8195_AFE_IRQ_15,
+ MT8195_AFE_IRQ_16,
+ MT8195_AFE_IRQ_17,
+ MT8195_AFE_IRQ_18,
+ MT8195_AFE_IRQ_19,
+ MT8195_AFE_IRQ_20,
+ MT8195_AFE_IRQ_21,
+ MT8195_AFE_IRQ_22,
+ MT8195_AFE_IRQ_23,
+ MT8195_AFE_IRQ_24,
+ MT8195_AFE_IRQ_25,
+ MT8195_AFE_IRQ_26,
+ MT8195_AFE_IRQ_27,
+ MT8195_AFE_IRQ_28,
+ MT8195_AFE_IRQ_NUM,
+};
+
+enum {
+ MT8195_ETDM_OUT1_1X_EN = 9,
+ MT8195_ETDM_OUT2_1X_EN = 10,
+ MT8195_ETDM_OUT3_1X_EN = 11,
+ MT8195_ETDM_IN1_1X_EN = 12,
+ MT8195_ETDM_IN2_1X_EN = 13,
+ MT8195_ETDM_IN1_NX_EN = 25,
+ MT8195_ETDM_IN2_NX_EN = 26,
+};
+
+enum {
+ MT8195_MTKAIF_MISO_0,
+ MT8195_MTKAIF_MISO_1,
+ MT8195_MTKAIF_MISO_2,
+ MT8195_MTKAIF_MISO_NUM,
+};
+
+struct mtk_dai_memif_irq_priv {
+ unsigned int asys_timing_sel;
+};
+
+struct mtkaif_param {
+ bool mtkaif_calibration_ok;
+ int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_dmic_on;
+ int mtkaif_adda6_only;
+};
+
+struct clk;
+
+struct mt8195_afe_private {
+ struct clk **clk;
+ struct clk_lookup **lookup;
+ struct regmap *topckgen;
+ int pm_runtime_bypass_reg_ctl;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry **debugfs_dentry;
+#endif
+ int afe_on_ref_cnt;
+ int top_cg_ref_cnt[MT8195_TOP_CG_NUM];
+ spinlock_t afe_ctrl_lock; /* Lock for afe control */
+ struct mtk_dai_memif_irq_priv irq_priv[MT8195_AFE_IRQ_NUM];
+ struct mtkaif_param mtkaif_params;
+
+ /* dai */
+ void *dai_priv[MT8195_DAI_NUM];
+};
+
+int mt8195_afe_fs_timing(unsigned int rate);
+/* dai register */
+int mt8195_dai_adda_register(struct mtk_base_afe *afe);
+int mt8195_dai_etdm_register(struct mtk_base_afe *afe);
+int mt8195_dai_pcm_register(struct mtk_base_afe *afe);
+
+#define MT8195_SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put, id) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .device = id, \
+ .private_value = (unsigned long)&xenum, \
+}
+
+#endif
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
new file mode 100644
index 000000000000..bc0a63457cd7
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -0,0 +1,3206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek ALSA SoC AFE platform driver for 8195
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include "mt8195-afe-common.h"
+#include "mt8195-afe-clk.h"
+#include "mt8195-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define MT8195_MEMIF_BUFFER_BYTES_ALIGN (0x40)
+#define MT8195_MEMIF_DL7_MAX_PERIOD_SIZE (0x3fff)
+
+struct mtk_dai_memif_priv {
+ unsigned int asys_timing_sel;
+};
+
+static const struct snd_pcm_hardware mt8195_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 64,
+ .period_bytes_max = 256 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 2 * 1024,
+};
+
+struct mt8195_afe_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+static const struct mt8195_afe_rate mt8195_afe_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 12000, .reg_value = 1, },
+ { .rate = 16000, .reg_value = 2, },
+ { .rate = 24000, .reg_value = 3, },
+ { .rate = 32000, .reg_value = 4, },
+ { .rate = 48000, .reg_value = 5, },
+ { .rate = 96000, .reg_value = 6, },
+ { .rate = 192000, .reg_value = 7, },
+ { .rate = 384000, .reg_value = 8, },
+ { .rate = 7350, .reg_value = 16, },
+ { .rate = 11025, .reg_value = 17, },
+ { .rate = 14700, .reg_value = 18, },
+ { .rate = 22050, .reg_value = 19, },
+ { .rate = 29400, .reg_value = 20, },
+ { .rate = 44100, .reg_value = 21, },
+ { .rate = 88200, .reg_value = 22, },
+ { .rate = 176400, .reg_value = 23, },
+ { .rate = 352800, .reg_value = 24, },
+};
+
+int mt8195_afe_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8195_afe_rates); i++)
+ if (mt8195_afe_rates[i].rate == rate)
+ return mt8195_afe_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static int mt8195_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int fs = mt8195_afe_fs_timing(rate);
+
+ switch (memif->data->id) {
+ case MT8195_AFE_MEMIF_DL10:
+ fs = MT8195_ETDM_OUT3_1X_EN;
+ break;
+ case MT8195_AFE_MEMIF_UL8:
+ fs = MT8195_ETDM_IN1_NX_EN;
+ break;
+ case MT8195_AFE_MEMIF_UL3:
+ fs = MT8195_ETDM_IN2_NX_EN;
+ break;
+ default:
+ break;
+ }
+
+ return fs;
+}
+
+static int mt8195_irq_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ int fs = mt8195_memif_fs(substream, rate);
+
+ switch (fs) {
+ case MT8195_ETDM_IN1_NX_EN:
+ fs = MT8195_ETDM_IN1_1X_EN;
+ break;
+ case MT8195_ETDM_IN2_NX_EN:
+ fs = MT8195_ETDM_IN2_1X_EN;
+ break;
+ default:
+ break;
+ }
+
+ return fs;
+}
+
+enum {
+ MT8195_AFE_CM0,
+ MT8195_AFE_CM1,
+ MT8195_AFE_CM2,
+ MT8195_AFE_CM_NUM,
+};
+
+struct mt8195_afe_channel_merge {
+ int id;
+ int reg;
+ unsigned int sel_shift;
+ unsigned int sel_maskbit;
+ unsigned int sel_default;
+ unsigned int ch_num_shift;
+ unsigned int ch_num_maskbit;
+ unsigned int en_shift;
+ unsigned int en_maskbit;
+ unsigned int update_cnt_shift;
+ unsigned int update_cnt_maskbit;
+ unsigned int update_cnt_default;
+};
+
+static const struct mt8195_afe_channel_merge
+ mt8195_afe_cm[MT8195_AFE_CM_NUM] = {
+ [MT8195_AFE_CM0] = {
+ .id = MT8195_AFE_CM0,
+ .reg = AFE_CM0_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x3f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+ [MT8195_AFE_CM1] = {
+ .id = MT8195_AFE_CM1,
+ .reg = AFE_CM1_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x1f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+ [MT8195_AFE_CM2] = {
+ .id = MT8195_AFE_CM2,
+ .reg = AFE_CM2_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x1f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+};
+
+static int mt8195_afe_memif_is_ul(int id)
+{
+ if (id >= MT8195_AFE_MEMIF_UL_START && id < MT8195_AFE_MEMIF_END)
+ return 1;
+ else
+ return 0;
+}
+
+static const struct mt8195_afe_channel_merge*
+mt8195_afe_found_cm(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = -EINVAL;
+
+ if (mt8195_afe_memif_is_ul(dai->id) == 0)
+ return NULL;
+
+ switch (dai->id) {
+ case MT8195_AFE_MEMIF_UL9:
+ id = MT8195_AFE_CM0;
+ break;
+ case MT8195_AFE_MEMIF_UL2:
+ id = MT8195_AFE_CM1;
+ break;
+ case MT8195_AFE_MEMIF_UL10:
+ id = MT8195_AFE_CM2;
+ break;
+ default:
+ break;
+ }
+
+ if (id < 0) {
+ dev_dbg(afe->dev, "%s, memif %d cannot find CM!\n",
+ __func__, dai->id);
+ return NULL;
+ }
+
+ return &mt8195_afe_cm[id];
+}
+
+static int mt8195_afe_config_cm(struct mtk_base_afe *afe,
+ const struct mt8195_afe_channel_merge *cm,
+ unsigned int channels)
+{
+ if (!cm)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->sel_maskbit << cm->sel_shift,
+ cm->sel_default << cm->sel_shift);
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->ch_num_maskbit << cm->ch_num_shift,
+ (channels - 1) << cm->ch_num_shift);
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->update_cnt_maskbit << cm->update_cnt_shift,
+ cm->update_cnt_default << cm->update_cnt_shift);
+
+ return 0;
+}
+
+static int mt8195_afe_enable_cm(struct mtk_base_afe *afe,
+ const struct mt8195_afe_channel_merge *cm,
+ bool enable)
+{
+ if (!cm)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->en_maskbit << cm->en_shift,
+ enable << cm->en_shift);
+
+ return 0;
+}
+
+static int
+mt8195_afe_paired_memif_clk_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ int enable)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ int clk_id;
+
+ if (id != MT8195_AFE_MEMIF_DL8 && id != MT8195_AFE_MEMIF_DL10)
+ return 0;
+
+ if (enable) {
+ clk_id = MT8195_CLK_AUD_MEMIF_DL10;
+ mt8195_afe_prepare_clk(afe, afe_priv->clk[clk_id]);
+ clk_id = MT8195_CLK_AUD_MEMIF_DL8;
+ mt8195_afe_prepare_clk(afe, afe_priv->clk[clk_id]);
+ } else {
+ clk_id = MT8195_CLK_AUD_MEMIF_DL8;
+ mt8195_afe_unprepare_clk(afe, afe_priv->clk[clk_id]);
+ clk_id = MT8195_CLK_AUD_MEMIF_DL10;
+ mt8195_afe_unprepare_clk(afe, afe_priv->clk[clk_id]);
+ }
+
+ return 0;
+}
+
+static int
+mt8195_afe_paired_memif_clk_enable(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ int enable)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ int clk_id;
+
+ if (id != MT8195_AFE_MEMIF_DL8 && id != MT8195_AFE_MEMIF_DL10)
+ return 0;
+
+ if (enable) {
+ /* DL8_DL10_MEM */
+ clk_id = MT8195_CLK_AUD_MEMIF_DL10;
+ mt8195_afe_enable_clk_atomic(afe, afe_priv->clk[clk_id]);
+ udelay(1);
+ /* DL8_DL10_AGENT */
+ clk_id = MT8195_CLK_AUD_MEMIF_DL8;
+ mt8195_afe_enable_clk_atomic(afe, afe_priv->clk[clk_id]);
+ } else {
+ /* DL8_DL10_AGENT */
+ clk_id = MT8195_CLK_AUD_MEMIF_DL8;
+ mt8195_afe_disable_clk_atomic(afe, afe_priv->clk[clk_id]);
+ /* DL8_DL10_MEM */
+ clk_id = MT8195_CLK_AUD_MEMIF_DL10;
+ mt8195_afe_disable_clk_atomic(afe, afe_priv->clk[clk_id]);
+ }
+
+ return 0;
+}
+
+static int mt8195_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ int ret = 0;
+
+ mt8195_afe_paired_memif_clk_prepare(substream, dai, 1);
+
+ ret = mtk_afe_fe_startup(substream, dai);
+
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ MT8195_MEMIF_BUFFER_BYTES_ALIGN);
+
+ if (id != MT8195_AFE_MEMIF_DL7)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 1,
+ MT8195_MEMIF_DL7_MAX_PERIOD_SIZE);
+ if (ret < 0)
+ dev_dbg(afe->dev, "hw_constraint_minmax failed\n");
+out:
+ return ret;
+}
+
+static void mt8195_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ mtk_afe_fe_shutdown(substream, dai);
+ mt8195_afe_paired_memif_clk_prepare(substream, dai, 0);
+}
+
+static int mt8195_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ const struct mtk_base_memif_data *data = memif->data;
+ const struct mt8195_afe_channel_merge *cm = mt8195_afe_found_cm(dai);
+ unsigned int ch_num = params_channels(params);
+
+ mt8195_afe_config_cm(afe, cm, params_channels(params));
+
+ if (data->ch_num_reg >= 0) {
+ regmap_update_bits(afe->regmap, data->ch_num_reg,
+ data->ch_num_maskbit << data->ch_num_shift,
+ ch_num << data->ch_num_shift);
+ }
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt8195_afe_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return mtk_afe_fe_hw_free(substream, dai);
+}
+
+static int mt8195_afe_fe_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return mtk_afe_fe_prepare(substream, dai);
+}
+
+static int mt8195_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ const struct mt8195_afe_channel_merge *cm = mt8195_afe_found_cm(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ mt8195_afe_enable_cm(afe, cm, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ mt8195_afe_enable_cm(afe, cm, false);
+ break;
+ default:
+ break;
+ }
+
+ ret = mtk_afe_fe_trigger(substream, cmd, dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ mt8195_afe_paired_memif_clk_enable(substream, dai, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ mt8195_afe_paired_memif_clk_enable(substream, dai, 0);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int mt8195_afe_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt8195_afe_fe_dai_ops = {
+ .startup = mt8195_afe_fe_startup,
+ .shutdown = mt8195_afe_fe_shutdown,
+ .hw_params = mt8195_afe_fe_hw_params,
+ .hw_free = mt8195_afe_fe_hw_free,
+ .prepare = mt8195_afe_fe_prepare,
+ .trigger = mt8195_afe_fe_trigger,
+ .set_fmt = mt8195_afe_fe_set_fmt,
+};
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_352800 |\
+ SNDRV_PCM_RATE_384000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt8195_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL2",
+ .id = MT8195_AFE_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT8195_AFE_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL6",
+ .id = MT8195_AFE_MEMIF_DL6,
+ .playback = {
+ .stream_name = "DL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL7",
+ .id = MT8195_AFE_MEMIF_DL7,
+ .playback = {
+ .stream_name = "DL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL8",
+ .id = MT8195_AFE_MEMIF_DL8,
+ .playback = {
+ .stream_name = "DL8",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL10",
+ .id = MT8195_AFE_MEMIF_DL10,
+ .playback = {
+ .stream_name = "DL10",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL11",
+ .id = MT8195_AFE_MEMIF_DL11,
+ .playback = {
+ .stream_name = "DL11",
+ .channels_min = 1,
+ .channels_max = 48,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT8195_AFE_MEMIF_UL1,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT8195_AFE_MEMIF_UL2,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT8195_AFE_MEMIF_UL3,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL4",
+ .id = MT8195_AFE_MEMIF_UL4,
+ .capture = {
+ .stream_name = "UL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL5",
+ .id = MT8195_AFE_MEMIF_UL5,
+ .capture = {
+ .stream_name = "UL5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL6",
+ .id = MT8195_AFE_MEMIF_UL6,
+ .capture = {
+ .stream_name = "UL6",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL8",
+ .id = MT8195_AFE_MEMIF_UL8,
+ .capture = {
+ .stream_name = "UL8",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL9",
+ .id = MT8195_AFE_MEMIF_UL9,
+ .capture = {
+ .stream_name = "UL9",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL10",
+ .id = MT8195_AFE_MEMIF_UL10,
+ .capture = {
+ .stream_name = "UL10",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8195_afe_fe_dai_ops,
+ },
+};
+
+static const struct snd_kcontrol_new o002_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN2, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN2, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN2, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN2, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN2_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN2_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN2_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o003_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN3, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN3, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN3, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN3, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN3_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN3_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN3_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o004_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN4, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN4, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN4, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN4_2, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I170 Switch", AFE_CONN4_5, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new o005_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN5, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN5, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN5, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN5_2, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I171 Switch", AFE_CONN5_5, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new o006_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN6, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN6, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN6, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN6_2, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new o007_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN7, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN7, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN7, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN7_2, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new o008_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN8, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN8, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN8_2, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new o009_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN9, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN9, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN9_2, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new o010_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN10, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN10, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN10_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN10_2, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o011_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN11, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN11, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN11_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN11_2, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o012_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN12, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN12_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN12_1, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN12_2, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new o013_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN13, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN13_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN13_1, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN13_2, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new o014_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN14, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN14_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN14_1, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN14_2, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new o015_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN15, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN15_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN15_1, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN15_2, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new o016_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN16, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN16_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN16_1, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN16_2, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new o017_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN17, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN17_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN17_1, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN17_2, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new o018_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I038 Switch", AFE_CONN18_1, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN18_2, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new o019_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I039 Switch", AFE_CONN19_1, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN19_2, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new o020_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I040 Switch", AFE_CONN20_1, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN20_2, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new o021_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I041 Switch", AFE_CONN21_1, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN21_2, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new o022_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I042 Switch", AFE_CONN22_1, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN22_2, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new o023_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I043 Switch", AFE_CONN23_1, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN23_2, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new o024_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I044 Switch", AFE_CONN24_1, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN24_2, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new o025_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I045 Switch", AFE_CONN25_1, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN25_2, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new o026_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN26_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I088 Switch", AFE_CONN26_2, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new o027_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN27_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I089 Switch", AFE_CONN27_2, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new o028_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN28_1, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I090 Switch", AFE_CONN28_2, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new o029_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN29_1, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I091 Switch", AFE_CONN29_2, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new o030_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN30_1, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I092 Switch", AFE_CONN30_2, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new o031_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN31_1, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I093 Switch", AFE_CONN31_2, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new o032_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN32_1, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I094 Switch", AFE_CONN32_2, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new o033_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN33_1, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I095 Switch", AFE_CONN33_2, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new o034_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN34, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN34, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN34, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN34, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN34_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN34_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN34_5, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I170 Switch", AFE_CONN34_5, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new o035_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN35, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN35, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN35, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN35, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN35_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN35_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I137 Switch", AFE_CONN35_4, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I139 Switch", AFE_CONN35_4, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN35_5, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN35_5, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I170 Switch", AFE_CONN35_5, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I171 Switch", AFE_CONN35_5, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new o036_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN36, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN36, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN36, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN36_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN36_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o037_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN37, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN37, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN37, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN37_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN37_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o038_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN38, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new o039_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN39, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new o040_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN40, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN40, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN40, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN40_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o041_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN41, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN41, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN41, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN41_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o042_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN42, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN42, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I170 Switch", AFE_CONN42_5, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new o043_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN43, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN43, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I171 Switch", AFE_CONN43_5, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new o044_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN44, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN44, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new o045_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN45, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN45, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new o046_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN46, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN46, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new o047_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN47, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN47, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new o182_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN182, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new o183_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN183, 25, 1, 0),
+};
+
+static const char * const dl8_dl11_data_sel_mux_text[] = {
+ "dl8", "dl11",
+};
+
+static SOC_ENUM_SINGLE_DECL(dl8_dl11_data_sel_mux_enum,
+ AFE_DAC_CON2, 0, dl8_dl11_data_sel_mux_text);
+
+static const struct snd_kcontrol_new dl8_dl11_data_sel_mux =
+ SOC_DAPM_ENUM("DL8_DL11 Sink", dl8_dl11_data_sel_mux_enum);
+
+static const struct snd_soc_dapm_widget mt8195_memif_widgets[] = {
+ /* DL6 */
+ SND_SOC_DAPM_MIXER("I000", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I001", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL3 */
+ SND_SOC_DAPM_MIXER("I020", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I021", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL11 */
+ SND_SOC_DAPM_MIXER("I022", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I023", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I024", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I025", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I026", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I027", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I028", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I029", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I030", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I031", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I034", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I035", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I036", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I037", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I038", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I039", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I040", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I041", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I042", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I043", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I044", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I045", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL11/DL8 */
+ SND_SOC_DAPM_MIXER("I046", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I047", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I048", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I049", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I050", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I051", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I052", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I053", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I054", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I055", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I056", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I057", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I058", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I059", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I060", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I061", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I062", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I063", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I064", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I065", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I066", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I067", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I068", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I069", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL2 */
+ SND_SOC_DAPM_MIXER("I070", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I071", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DL8_DL11 Mux",
+ SND_SOC_NOPM, 0, 0, &dl8_dl11_data_sel_mux),
+
+ /* UL9 */
+ SND_SOC_DAPM_MIXER("O002", SND_SOC_NOPM, 0, 0,
+ o002_mix, ARRAY_SIZE(o002_mix)),
+ SND_SOC_DAPM_MIXER("O003", SND_SOC_NOPM, 0, 0,
+ o003_mix, ARRAY_SIZE(o003_mix)),
+ SND_SOC_DAPM_MIXER("O004", SND_SOC_NOPM, 0, 0,
+ o004_mix, ARRAY_SIZE(o004_mix)),
+ SND_SOC_DAPM_MIXER("O005", SND_SOC_NOPM, 0, 0,
+ o005_mix, ARRAY_SIZE(o005_mix)),
+ SND_SOC_DAPM_MIXER("O006", SND_SOC_NOPM, 0, 0,
+ o006_mix, ARRAY_SIZE(o006_mix)),
+ SND_SOC_DAPM_MIXER("O007", SND_SOC_NOPM, 0, 0,
+ o007_mix, ARRAY_SIZE(o007_mix)),
+ SND_SOC_DAPM_MIXER("O008", SND_SOC_NOPM, 0, 0,
+ o008_mix, ARRAY_SIZE(o008_mix)),
+ SND_SOC_DAPM_MIXER("O009", SND_SOC_NOPM, 0, 0,
+ o009_mix, ARRAY_SIZE(o009_mix)),
+ SND_SOC_DAPM_MIXER("O010", SND_SOC_NOPM, 0, 0,
+ o010_mix, ARRAY_SIZE(o010_mix)),
+ SND_SOC_DAPM_MIXER("O011", SND_SOC_NOPM, 0, 0,
+ o011_mix, ARRAY_SIZE(o011_mix)),
+ SND_SOC_DAPM_MIXER("O012", SND_SOC_NOPM, 0, 0,
+ o012_mix, ARRAY_SIZE(o012_mix)),
+ SND_SOC_DAPM_MIXER("O013", SND_SOC_NOPM, 0, 0,
+ o013_mix, ARRAY_SIZE(o013_mix)),
+ SND_SOC_DAPM_MIXER("O014", SND_SOC_NOPM, 0, 0,
+ o014_mix, ARRAY_SIZE(o014_mix)),
+ SND_SOC_DAPM_MIXER("O015", SND_SOC_NOPM, 0, 0,
+ o015_mix, ARRAY_SIZE(o015_mix)),
+ SND_SOC_DAPM_MIXER("O016", SND_SOC_NOPM, 0, 0,
+ o016_mix, ARRAY_SIZE(o016_mix)),
+ SND_SOC_DAPM_MIXER("O017", SND_SOC_NOPM, 0, 0,
+ o017_mix, ARRAY_SIZE(o017_mix)),
+ SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0,
+ o018_mix, ARRAY_SIZE(o018_mix)),
+ SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0,
+ o019_mix, ARRAY_SIZE(o019_mix)),
+ SND_SOC_DAPM_MIXER("O020", SND_SOC_NOPM, 0, 0,
+ o020_mix, ARRAY_SIZE(o020_mix)),
+ SND_SOC_DAPM_MIXER("O021", SND_SOC_NOPM, 0, 0,
+ o021_mix, ARRAY_SIZE(o021_mix)),
+ SND_SOC_DAPM_MIXER("O022", SND_SOC_NOPM, 0, 0,
+ o022_mix, ARRAY_SIZE(o022_mix)),
+ SND_SOC_DAPM_MIXER("O023", SND_SOC_NOPM, 0, 0,
+ o023_mix, ARRAY_SIZE(o023_mix)),
+ SND_SOC_DAPM_MIXER("O024", SND_SOC_NOPM, 0, 0,
+ o024_mix, ARRAY_SIZE(o024_mix)),
+ SND_SOC_DAPM_MIXER("O025", SND_SOC_NOPM, 0, 0,
+ o025_mix, ARRAY_SIZE(o025_mix)),
+ SND_SOC_DAPM_MIXER("O026", SND_SOC_NOPM, 0, 0,
+ o026_mix, ARRAY_SIZE(o026_mix)),
+ SND_SOC_DAPM_MIXER("O027", SND_SOC_NOPM, 0, 0,
+ o027_mix, ARRAY_SIZE(o027_mix)),
+ SND_SOC_DAPM_MIXER("O028", SND_SOC_NOPM, 0, 0,
+ o028_mix, ARRAY_SIZE(o028_mix)),
+ SND_SOC_DAPM_MIXER("O029", SND_SOC_NOPM, 0, 0,
+ o029_mix, ARRAY_SIZE(o029_mix)),
+ SND_SOC_DAPM_MIXER("O030", SND_SOC_NOPM, 0, 0,
+ o030_mix, ARRAY_SIZE(o030_mix)),
+ SND_SOC_DAPM_MIXER("O031", SND_SOC_NOPM, 0, 0,
+ o031_mix, ARRAY_SIZE(o031_mix)),
+ SND_SOC_DAPM_MIXER("O032", SND_SOC_NOPM, 0, 0,
+ o032_mix, ARRAY_SIZE(o032_mix)),
+ SND_SOC_DAPM_MIXER("O033", SND_SOC_NOPM, 0, 0,
+ o033_mix, ARRAY_SIZE(o033_mix)),
+
+ /* UL4 */
+ SND_SOC_DAPM_MIXER("O034", SND_SOC_NOPM, 0, 0,
+ o034_mix, ARRAY_SIZE(o034_mix)),
+ SND_SOC_DAPM_MIXER("O035", SND_SOC_NOPM, 0, 0,
+ o035_mix, ARRAY_SIZE(o035_mix)),
+
+ /* UL5 */
+ SND_SOC_DAPM_MIXER("O036", SND_SOC_NOPM, 0, 0,
+ o036_mix, ARRAY_SIZE(o036_mix)),
+ SND_SOC_DAPM_MIXER("O037", SND_SOC_NOPM, 0, 0,
+ o037_mix, ARRAY_SIZE(o037_mix)),
+
+ /* UL10 */
+ SND_SOC_DAPM_MIXER("O038", SND_SOC_NOPM, 0, 0,
+ o038_mix, ARRAY_SIZE(o038_mix)),
+ SND_SOC_DAPM_MIXER("O039", SND_SOC_NOPM, 0, 0,
+ o039_mix, ARRAY_SIZE(o039_mix)),
+ SND_SOC_DAPM_MIXER("O182", SND_SOC_NOPM, 0, 0,
+ o182_mix, ARRAY_SIZE(o182_mix)),
+ SND_SOC_DAPM_MIXER("O183", SND_SOC_NOPM, 0, 0,
+ o183_mix, ARRAY_SIZE(o183_mix)),
+
+ /* UL2 */
+ SND_SOC_DAPM_MIXER("O040", SND_SOC_NOPM, 0, 0,
+ o040_mix, ARRAY_SIZE(o040_mix)),
+ SND_SOC_DAPM_MIXER("O041", SND_SOC_NOPM, 0, 0,
+ o041_mix, ARRAY_SIZE(o041_mix)),
+ SND_SOC_DAPM_MIXER("O042", SND_SOC_NOPM, 0, 0,
+ o042_mix, ARRAY_SIZE(o042_mix)),
+ SND_SOC_DAPM_MIXER("O043", SND_SOC_NOPM, 0, 0,
+ o043_mix, ARRAY_SIZE(o043_mix)),
+ SND_SOC_DAPM_MIXER("O044", SND_SOC_NOPM, 0, 0,
+ o044_mix, ARRAY_SIZE(o044_mix)),
+ SND_SOC_DAPM_MIXER("O045", SND_SOC_NOPM, 0, 0,
+ o045_mix, ARRAY_SIZE(o045_mix)),
+ SND_SOC_DAPM_MIXER("O046", SND_SOC_NOPM, 0, 0,
+ o046_mix, ARRAY_SIZE(o046_mix)),
+ SND_SOC_DAPM_MIXER("O047", SND_SOC_NOPM, 0, 0,
+ o047_mix, ARRAY_SIZE(o047_mix)),
+};
+
+static const struct snd_soc_dapm_route mt8195_memif_routes[] = {
+ {"I000", NULL, "DL6"},
+ {"I001", NULL, "DL6"},
+
+ {"I020", NULL, "DL3"},
+ {"I021", NULL, "DL3"},
+
+ {"I022", NULL, "DL11"},
+ {"I023", NULL, "DL11"},
+ {"I024", NULL, "DL11"},
+ {"I025", NULL, "DL11"},
+ {"I026", NULL, "DL11"},
+ {"I027", NULL, "DL11"},
+ {"I028", NULL, "DL11"},
+ {"I029", NULL, "DL11"},
+ {"I030", NULL, "DL11"},
+ {"I031", NULL, "DL11"},
+ {"I032", NULL, "DL11"},
+ {"I033", NULL, "DL11"},
+ {"I034", NULL, "DL11"},
+ {"I035", NULL, "DL11"},
+ {"I036", NULL, "DL11"},
+ {"I037", NULL, "DL11"},
+ {"I038", NULL, "DL11"},
+ {"I039", NULL, "DL11"},
+ {"I040", NULL, "DL11"},
+ {"I041", NULL, "DL11"},
+ {"I042", NULL, "DL11"},
+ {"I043", NULL, "DL11"},
+ {"I044", NULL, "DL11"},
+ {"I045", NULL, "DL11"},
+
+ {"DL8_DL11 Mux", "dl8", "DL8"},
+ {"DL8_DL11 Mux", "dl11", "DL11"},
+
+ {"I046", NULL, "DL8_DL11 Mux"},
+ {"I047", NULL, "DL8_DL11 Mux"},
+ {"I048", NULL, "DL8_DL11 Mux"},
+ {"I049", NULL, "DL8_DL11 Mux"},
+ {"I050", NULL, "DL8_DL11 Mux"},
+ {"I051", NULL, "DL8_DL11 Mux"},
+ {"I052", NULL, "DL8_DL11 Mux"},
+ {"I053", NULL, "DL8_DL11 Mux"},
+ {"I054", NULL, "DL8_DL11 Mux"},
+ {"I055", NULL, "DL8_DL11 Mux"},
+ {"I056", NULL, "DL8_DL11 Mux"},
+ {"I057", NULL, "DL8_DL11 Mux"},
+ {"I058", NULL, "DL8_DL11 Mux"},
+ {"I059", NULL, "DL8_DL11 Mux"},
+ {"I060", NULL, "DL8_DL11 Mux"},
+ {"I061", NULL, "DL8_DL11 Mux"},
+ {"I062", NULL, "DL8_DL11 Mux"},
+ {"I063", NULL, "DL8_DL11 Mux"},
+ {"I064", NULL, "DL8_DL11 Mux"},
+ {"I065", NULL, "DL8_DL11 Mux"},
+ {"I066", NULL, "DL8_DL11 Mux"},
+ {"I067", NULL, "DL8_DL11 Mux"},
+ {"I068", NULL, "DL8_DL11 Mux"},
+ {"I069", NULL, "DL8_DL11 Mux"},
+
+ {"I070", NULL, "DL2"},
+ {"I071", NULL, "DL2"},
+
+ {"UL9", NULL, "O002"},
+ {"UL9", NULL, "O003"},
+ {"UL9", NULL, "O004"},
+ {"UL9", NULL, "O005"},
+ {"UL9", NULL, "O006"},
+ {"UL9", NULL, "O007"},
+ {"UL9", NULL, "O008"},
+ {"UL9", NULL, "O009"},
+ {"UL9", NULL, "O010"},
+ {"UL9", NULL, "O011"},
+ {"UL9", NULL, "O012"},
+ {"UL9", NULL, "O013"},
+ {"UL9", NULL, "O014"},
+ {"UL9", NULL, "O015"},
+ {"UL9", NULL, "O016"},
+ {"UL9", NULL, "O017"},
+ {"UL9", NULL, "O018"},
+ {"UL9", NULL, "O019"},
+ {"UL9", NULL, "O020"},
+ {"UL9", NULL, "O021"},
+ {"UL9", NULL, "O022"},
+ {"UL9", NULL, "O023"},
+ {"UL9", NULL, "O024"},
+ {"UL9", NULL, "O025"},
+ {"UL9", NULL, "O026"},
+ {"UL9", NULL, "O027"},
+ {"UL9", NULL, "O028"},
+ {"UL9", NULL, "O029"},
+ {"UL9", NULL, "O030"},
+ {"UL9", NULL, "O031"},
+ {"UL9", NULL, "O032"},
+ {"UL9", NULL, "O033"},
+
+ {"UL4", NULL, "O034"},
+ {"UL4", NULL, "O035"},
+
+ {"UL5", NULL, "O036"},
+ {"UL5", NULL, "O037"},
+
+ {"UL10", NULL, "O038"},
+ {"UL10", NULL, "O039"},
+ {"UL10", NULL, "O182"},
+ {"UL10", NULL, "O183"},
+
+ {"UL2", NULL, "O040"},
+ {"UL2", NULL, "O041"},
+ {"UL2", NULL, "O042"},
+ {"UL2", NULL, "O043"},
+ {"UL2", NULL, "O044"},
+ {"UL2", NULL, "O045"},
+ {"UL2", NULL, "O046"},
+ {"UL2", NULL, "O047"},
+
+ {"O004", "I000 Switch", "I000"},
+ {"O005", "I001 Switch", "I001"},
+
+ {"O006", "I000 Switch", "I000"},
+ {"O007", "I001 Switch", "I001"},
+
+ {"O010", "I022 Switch", "I022"},
+ {"O011", "I023 Switch", "I023"},
+ {"O012", "I024 Switch", "I024"},
+ {"O013", "I025 Switch", "I025"},
+ {"O014", "I026 Switch", "I026"},
+ {"O015", "I027 Switch", "I027"},
+ {"O016", "I028 Switch", "I028"},
+ {"O017", "I029 Switch", "I029"},
+
+ {"O010", "I046 Switch", "I046"},
+ {"O011", "I047 Switch", "I047"},
+ {"O012", "I048 Switch", "I048"},
+ {"O013", "I049 Switch", "I049"},
+ {"O014", "I050 Switch", "I050"},
+ {"O015", "I051 Switch", "I051"},
+ {"O016", "I052 Switch", "I052"},
+ {"O017", "I053 Switch", "I053"},
+ {"O002", "I022 Switch", "I022"},
+ {"O003", "I023 Switch", "I023"},
+ {"O004", "I024 Switch", "I024"},
+ {"O005", "I025 Switch", "I025"},
+ {"O006", "I026 Switch", "I026"},
+ {"O007", "I027 Switch", "I027"},
+ {"O008", "I028 Switch", "I028"},
+ {"O009", "I029 Switch", "I029"},
+ {"O010", "I030 Switch", "I030"},
+ {"O011", "I031 Switch", "I031"},
+ {"O012", "I032 Switch", "I032"},
+ {"O013", "I033 Switch", "I033"},
+ {"O014", "I034 Switch", "I034"},
+ {"O015", "I035 Switch", "I035"},
+ {"O016", "I036 Switch", "I036"},
+ {"O017", "I037 Switch", "I037"},
+ {"O018", "I038 Switch", "I038"},
+ {"O019", "I039 Switch", "I039"},
+ {"O020", "I040 Switch", "I040"},
+ {"O021", "I041 Switch", "I041"},
+ {"O022", "I042 Switch", "I042"},
+ {"O023", "I043 Switch", "I043"},
+ {"O024", "I044 Switch", "I044"},
+ {"O025", "I045 Switch", "I045"},
+ {"O026", "I046 Switch", "I046"},
+ {"O027", "I047 Switch", "I047"},
+ {"O028", "I048 Switch", "I048"},
+ {"O029", "I049 Switch", "I049"},
+ {"O030", "I050 Switch", "I050"},
+ {"O031", "I051 Switch", "I051"},
+ {"O032", "I052 Switch", "I052"},
+ {"O033", "I053 Switch", "I053"},
+
+ {"O002", "I000 Switch", "I000"},
+ {"O003", "I001 Switch", "I001"},
+ {"O002", "I020 Switch", "I020"},
+ {"O003", "I021 Switch", "I021"},
+ {"O002", "I070 Switch", "I070"},
+ {"O003", "I071 Switch", "I071"},
+
+ {"O034", "I000 Switch", "I000"},
+ {"O035", "I001 Switch", "I001"},
+ {"O034", "I002 Switch", "I002"},
+ {"O035", "I003 Switch", "I003"},
+ {"O034", "I012 Switch", "I012"},
+ {"O035", "I013 Switch", "I013"},
+ {"O034", "I020 Switch", "I020"},
+ {"O035", "I021 Switch", "I021"},
+ {"O034", "I070 Switch", "I070"},
+ {"O035", "I071 Switch", "I071"},
+ {"O034", "I072 Switch", "I072"},
+ {"O035", "I073 Switch", "I073"},
+
+ {"O036", "I000 Switch", "I000"},
+ {"O037", "I001 Switch", "I001"},
+ {"O036", "I012 Switch", "I012"},
+ {"O037", "I013 Switch", "I013"},
+ {"O036", "I020 Switch", "I020"},
+ {"O037", "I021 Switch", "I021"},
+ {"O036", "I070 Switch", "I070"},
+ {"O037", "I071 Switch", "I071"},
+ {"O036", "I168 Switch", "I168"},
+ {"O037", "I169 Switch", "I169"},
+
+ {"O038", "I022 Switch", "I022"},
+ {"O039", "I023 Switch", "I023"},
+ {"O182", "I024 Switch", "I024"},
+ {"O183", "I025 Switch", "I025"},
+
+ {"O040", "I022 Switch", "I022"},
+ {"O041", "I023 Switch", "I023"},
+ {"O042", "I024 Switch", "I024"},
+ {"O043", "I025 Switch", "I025"},
+ {"O044", "I026 Switch", "I026"},
+ {"O045", "I027 Switch", "I027"},
+ {"O046", "I028 Switch", "I028"},
+ {"O047", "I029 Switch", "I029"},
+
+ {"O040", "I002 Switch", "I002"},
+ {"O041", "I003 Switch", "I003"},
+ {"O002", "I012 Switch", "I012"},
+ {"O003", "I013 Switch", "I013"},
+ {"O004", "I014 Switch", "I014"},
+ {"O005", "I015 Switch", "I015"},
+ {"O006", "I016 Switch", "I016"},
+ {"O007", "I017 Switch", "I017"},
+ {"O008", "I018 Switch", "I018"},
+ {"O009", "I019 Switch", "I019"},
+
+ {"O040", "I012 Switch", "I012"},
+ {"O041", "I013 Switch", "I013"},
+ {"O042", "I014 Switch", "I014"},
+ {"O043", "I015 Switch", "I015"},
+ {"O044", "I016 Switch", "I016"},
+ {"O045", "I017 Switch", "I017"},
+ {"O046", "I018 Switch", "I018"},
+ {"O047", "I019 Switch", "I019"},
+
+ {"O002", "I072 Switch", "I072"},
+ {"O003", "I073 Switch", "I073"},
+ {"O004", "I074 Switch", "I074"},
+ {"O005", "I075 Switch", "I075"},
+ {"O006", "I076 Switch", "I076"},
+ {"O007", "I077 Switch", "I077"},
+ {"O008", "I078 Switch", "I078"},
+ {"O009", "I079 Switch", "I079"},
+
+ {"O010", "I072 Switch", "I072"},
+ {"O011", "I073 Switch", "I073"},
+ {"O012", "I074 Switch", "I074"},
+ {"O013", "I075 Switch", "I075"},
+ {"O014", "I076 Switch", "I076"},
+ {"O015", "I077 Switch", "I077"},
+ {"O016", "I078 Switch", "I078"},
+ {"O017", "I079 Switch", "I079"},
+ {"O018", "I080 Switch", "I080"},
+ {"O019", "I081 Switch", "I081"},
+ {"O020", "I082 Switch", "I082"},
+ {"O021", "I083 Switch", "I083"},
+ {"O022", "I084 Switch", "I084"},
+ {"O023", "I085 Switch", "I085"},
+ {"O024", "I086 Switch", "I086"},
+ {"O025", "I087 Switch", "I087"},
+ {"O026", "I088 Switch", "I088"},
+ {"O027", "I089 Switch", "I089"},
+ {"O028", "I090 Switch", "I090"},
+ {"O029", "I091 Switch", "I091"},
+ {"O030", "I092 Switch", "I092"},
+ {"O031", "I093 Switch", "I093"},
+ {"O032", "I094 Switch", "I094"},
+ {"O033", "I095 Switch", "I095"},
+
+ {"O002", "I168 Switch", "I168"},
+ {"O003", "I169 Switch", "I169"},
+ {"O004", "I170 Switch", "I170"},
+ {"O005", "I171 Switch", "I171"},
+
+ {"O034", "I168 Switch", "I168"},
+ {"O035", "I168 Switch", "I168"},
+ {"O035", "I169 Switch", "I169"},
+
+ {"O034", "I170 Switch", "I170"},
+ {"O035", "I170 Switch", "I170"},
+ {"O035", "I171 Switch", "I171"},
+
+ {"O040", "I168 Switch", "I168"},
+ {"O041", "I169 Switch", "I169"},
+ {"O042", "I170 Switch", "I170"},
+ {"O043", "I171 Switch", "I171"},
+};
+
+static const char * const mt8195_afe_1x_en_sel_text[] = {
+ "a1sys_a2sys", "a3sys", "a4sys",
+};
+
+static const unsigned int mt8195_afe_1x_en_sel_values[] = {
+ 0, 1, 2,
+};
+
+static int mt8195_memif_1x_en_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ long val = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ if (val == memif_priv->asys_timing_sel)
+ return 0;
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ memif_priv->asys_timing_sel = val;
+
+ return ret;
+}
+
+static int mt8195_asys_irq_1x_en_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ unsigned int id = kcontrol->id.device;
+ long val = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (val == afe_priv->irq_priv[id].asys_timing_sel)
+ return 0;
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ afe_priv->irq_priv[id].asys_timing_sel = val;
+
+ return ret;
+}
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dl2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 18, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 20, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 22, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl7_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 24, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 26, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 28, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl11_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 30, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul1_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 0, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 2, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 4, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 6, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul5_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 8, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 10, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 12, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul9_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 14, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 16, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq1_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 0, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 2, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 4, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq4_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 6, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq5_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 8, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 10, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq7_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 12, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 14, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq9_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 16, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 18, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq11_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 20, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq12_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 22, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq13_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 24, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq14_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 26, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq15_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 28, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq16_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 30, 0x3,
+ mt8195_afe_1x_en_sel_text,
+ mt8195_afe_1x_en_sel_values);
+
+static const struct snd_kcontrol_new mt8195_memif_controls[] = {
+ MT8195_SOC_ENUM_EXT("dl2_1x_en_sel",
+ dl2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL2),
+ MT8195_SOC_ENUM_EXT("dl3_1x_en_sel",
+ dl3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL3),
+ MT8195_SOC_ENUM_EXT("dl6_1x_en_sel",
+ dl6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL6),
+ MT8195_SOC_ENUM_EXT("dl7_1x_en_sel",
+ dl7_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL7),
+ MT8195_SOC_ENUM_EXT("dl8_1x_en_sel",
+ dl8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL8),
+ MT8195_SOC_ENUM_EXT("dl10_1x_en_sel",
+ dl10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL10),
+ MT8195_SOC_ENUM_EXT("dl11_1x_en_sel",
+ dl11_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_DL11),
+ MT8195_SOC_ENUM_EXT("ul1_1x_en_sel",
+ ul1_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL1),
+ MT8195_SOC_ENUM_EXT("ul2_1x_en_sel",
+ ul2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL2),
+ MT8195_SOC_ENUM_EXT("ul3_1x_en_sel",
+ ul3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL3),
+ MT8195_SOC_ENUM_EXT("ul4_1x_en_sel",
+ ul4_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL4),
+ MT8195_SOC_ENUM_EXT("ul5_1x_en_sel",
+ ul5_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL5),
+ MT8195_SOC_ENUM_EXT("ul6_1x_en_sel",
+ ul6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL6),
+ MT8195_SOC_ENUM_EXT("ul8_1x_en_sel",
+ ul8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL8),
+ MT8195_SOC_ENUM_EXT("ul9_1x_en_sel",
+ ul9_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL9),
+ MT8195_SOC_ENUM_EXT("ul10_1x_en_sel",
+ ul10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_memif_1x_en_sel_put,
+ MT8195_AFE_MEMIF_UL10),
+ MT8195_SOC_ENUM_EXT("asys_irq1_1x_en_sel",
+ asys_irq1_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_13),
+ MT8195_SOC_ENUM_EXT("asys_irq2_1x_en_sel",
+ asys_irq2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_14),
+ MT8195_SOC_ENUM_EXT("asys_irq3_1x_en_sel",
+ asys_irq3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_15),
+ MT8195_SOC_ENUM_EXT("asys_irq4_1x_en_sel",
+ asys_irq4_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_16),
+ MT8195_SOC_ENUM_EXT("asys_irq5_1x_en_sel",
+ asys_irq5_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_17),
+ MT8195_SOC_ENUM_EXT("asys_irq6_1x_en_sel",
+ asys_irq6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_18),
+ MT8195_SOC_ENUM_EXT("asys_irq7_1x_en_sel",
+ asys_irq7_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_19),
+ MT8195_SOC_ENUM_EXT("asys_irq8_1x_en_sel",
+ asys_irq8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_20),
+ MT8195_SOC_ENUM_EXT("asys_irq9_1x_en_sel",
+ asys_irq9_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_21),
+ MT8195_SOC_ENUM_EXT("asys_irq10_1x_en_sel",
+ asys_irq10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_22),
+ MT8195_SOC_ENUM_EXT("asys_irq11_1x_en_sel",
+ asys_irq11_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_23),
+ MT8195_SOC_ENUM_EXT("asys_irq12_1x_en_sel",
+ asys_irq12_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_24),
+ MT8195_SOC_ENUM_EXT("asys_irq13_1x_en_sel",
+ asys_irq13_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_25),
+ MT8195_SOC_ENUM_EXT("asys_irq14_1x_en_sel",
+ asys_irq14_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_26),
+ MT8195_SOC_ENUM_EXT("asys_irq15_1x_en_sel",
+ asys_irq15_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_27),
+ MT8195_SOC_ENUM_EXT("asys_irq16_1x_en_sel",
+ asys_irq16_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8195_asys_irq_1x_en_sel_put,
+ MT8195_AFE_IRQ_28),
+};
+
+static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM] = {
+ [MT8195_AFE_MEMIF_DL2] = {
+ .name = "DL2",
+ .id = MT8195_AFE_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .reg_ofs_end = AFE_DL2_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON0,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 18,
+ .hd_reg = AFE_DL2_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 18,
+ .ch_num_reg = AFE_DL2_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 18,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 18,
+ },
+ [MT8195_AFE_MEMIF_DL3] = {
+ .name = "DL3",
+ .id = MT8195_AFE_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .reg_ofs_end = AFE_DL3_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON0,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 19,
+ .hd_reg = AFE_DL3_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 19,
+ .ch_num_reg = AFE_DL3_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 19,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 19,
+ },
+ [MT8195_AFE_MEMIF_DL6] = {
+ .name = "DL6",
+ .id = MT8195_AFE_MEMIF_DL6,
+ .reg_ofs_base = AFE_DL6_BASE,
+ .reg_ofs_cur = AFE_DL6_CUR,
+ .reg_ofs_end = AFE_DL6_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 22,
+ .hd_reg = AFE_DL6_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 22,
+ .ch_num_reg = AFE_DL6_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 22,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 22,
+ },
+ [MT8195_AFE_MEMIF_DL7] = {
+ .name = "DL7",
+ .id = MT8195_AFE_MEMIF_DL7,
+ .reg_ofs_base = AFE_DL7_BASE,
+ .reg_ofs_cur = AFE_DL7_CUR,
+ .reg_ofs_end = AFE_DL7_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 23,
+ .hd_reg = AFE_DL7_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 23,
+ .ch_num_reg = AFE_DL7_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 23,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 23,
+ },
+ [MT8195_AFE_MEMIF_DL8] = {
+ .name = "DL8",
+ .id = MT8195_AFE_MEMIF_DL8,
+ .reg_ofs_base = AFE_DL8_BASE,
+ .reg_ofs_cur = AFE_DL8_CUR,
+ .reg_ofs_end = AFE_DL8_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 24,
+ .hd_reg = AFE_DL8_CON0,
+ .hd_shift = 6,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = 0,
+ .ch_num_reg = AFE_DL8_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x3f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 24,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 24,
+ },
+ [MT8195_AFE_MEMIF_DL10] = {
+ .name = "DL10",
+ .id = MT8195_AFE_MEMIF_DL10,
+ .reg_ofs_base = AFE_DL10_BASE,
+ .reg_ofs_cur = AFE_DL10_CUR,
+ .reg_ofs_end = AFE_DL10_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 26,
+ .hd_reg = AFE_DL10_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = 0,
+ .ch_num_reg = AFE_DL10_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 26,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 26,
+ },
+ [MT8195_AFE_MEMIF_DL11] = {
+ .name = "DL11",
+ .id = MT8195_AFE_MEMIF_DL11,
+ .reg_ofs_base = AFE_DL11_BASE,
+ .reg_ofs_cur = AFE_DL11_CUR,
+ .reg_ofs_end = AFE_DL11_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 25,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 27,
+ .hd_reg = AFE_DL11_CON0,
+ .hd_shift = 7,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 27,
+ .ch_num_reg = AFE_DL11_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x7f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 27,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 27,
+ },
+ [MT8195_AFE_MEMIF_UL1] = {
+ .name = "UL1",
+ .id = MT8195_AFE_MEMIF_UL1,
+ .reg_ofs_base = AFE_UL1_BASE,
+ .reg_ofs_cur = AFE_UL1_CUR,
+ .reg_ofs_end = AFE_UL1_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = AFE_UL1_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL1_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .hd_reg = AFE_UL1_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 0,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 0,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 0,
+ },
+ [MT8195_AFE_MEMIF_UL2] = {
+ .name = "UL2",
+ .id = MT8195_AFE_MEMIF_UL2,
+ .reg_ofs_base = AFE_UL2_BASE,
+ .reg_ofs_cur = AFE_UL2_CUR,
+ .reg_ofs_end = AFE_UL2_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL2_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL2_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 2,
+ .hd_reg = AFE_UL2_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 1,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 1,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 1,
+ },
+ [MT8195_AFE_MEMIF_UL3] = {
+ .name = "UL3",
+ .id = MT8195_AFE_MEMIF_UL3,
+ .reg_ofs_base = AFE_UL3_BASE,
+ .reg_ofs_cur = AFE_UL3_CUR,
+ .reg_ofs_end = AFE_UL3_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL3_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL3_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 3,
+ .hd_reg = AFE_UL3_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 2,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 2,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 2,
+ },
+ [MT8195_AFE_MEMIF_UL4] = {
+ .name = "UL4",
+ .id = MT8195_AFE_MEMIF_UL4,
+ .reg_ofs_base = AFE_UL4_BASE,
+ .reg_ofs_cur = AFE_UL4_CUR,
+ .reg_ofs_end = AFE_UL4_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL4_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL4_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 4,
+ .hd_reg = AFE_UL4_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 3,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 3,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 3,
+ },
+ [MT8195_AFE_MEMIF_UL5] = {
+ .name = "UL5",
+ .id = MT8195_AFE_MEMIF_UL5,
+ .reg_ofs_base = AFE_UL5_BASE,
+ .reg_ofs_cur = AFE_UL5_CUR,
+ .reg_ofs_end = AFE_UL5_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL5_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL5_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 5,
+ .hd_reg = AFE_UL5_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 4,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 4,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 4,
+ },
+ [MT8195_AFE_MEMIF_UL6] = {
+ .name = "UL6",
+ .id = MT8195_AFE_MEMIF_UL6,
+ .reg_ofs_base = AFE_UL6_BASE,
+ .reg_ofs_cur = AFE_UL6_CUR,
+ .reg_ofs_end = AFE_UL6_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = AFE_UL6_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL6_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 6,
+ .hd_reg = AFE_UL6_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 5,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 5,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 5,
+ },
+ [MT8195_AFE_MEMIF_UL8] = {
+ .name = "UL8",
+ .id = MT8195_AFE_MEMIF_UL8,
+ .reg_ofs_base = AFE_UL8_BASE,
+ .reg_ofs_cur = AFE_UL8_CUR,
+ .reg_ofs_end = AFE_UL8_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL8_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL8_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 8,
+ .hd_reg = AFE_UL8_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 7,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 7,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 7,
+ },
+ [MT8195_AFE_MEMIF_UL9] = {
+ .name = "UL9",
+ .id = MT8195_AFE_MEMIF_UL9,
+ .reg_ofs_base = AFE_UL9_BASE,
+ .reg_ofs_cur = AFE_UL9_CUR,
+ .reg_ofs_end = AFE_UL9_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL9_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL9_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 9,
+ .hd_reg = AFE_UL9_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 8,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 8,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 8,
+ },
+ [MT8195_AFE_MEMIF_UL10] = {
+ .name = "UL10",
+ .id = MT8195_AFE_MEMIF_UL10,
+ .reg_ofs_base = AFE_UL10_BASE,
+ .reg_ofs_cur = AFE_UL10_CUR,
+ .reg_ofs_end = AFE_UL10_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL10_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL10_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 10,
+ .hd_reg = AFE_UL10_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 9,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 9,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 9,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data_array[MT8195_AFE_IRQ_NUM] = {
+ [MT8195_AFE_IRQ_1] = {
+ .id = MT8195_AFE_IRQ_1,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 0,
+ .irq_status_shift = 16,
+ },
+ [MT8195_AFE_IRQ_2] = {
+ .id = MT8195_AFE_IRQ_2,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 1,
+ .irq_status_shift = 17,
+ },
+ [MT8195_AFE_IRQ_3] = {
+ .id = MT8195_AFE_IRQ_3,
+ .irq_cnt_reg = AFE_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 2,
+ .irq_status_shift = 18,
+ },
+ [MT8195_AFE_IRQ_8] = {
+ .id = MT8195_AFE_IRQ_8,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ8_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 7,
+ .irq_status_shift = 23,
+ },
+ [MT8195_AFE_IRQ_9] = {
+ .id = MT8195_AFE_IRQ_9,
+ .irq_cnt_reg = AFE_IRQ9_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ9_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 8,
+ .irq_status_shift = 24,
+ },
+ [MT8195_AFE_IRQ_10] = {
+ .id = MT8195_AFE_IRQ_10,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ10_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 9,
+ .irq_status_shift = 25,
+ },
+ [MT8195_AFE_IRQ_13] = {
+ .id = MT8195_AFE_IRQ_13,
+ .irq_cnt_reg = ASYS_IRQ1_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ1_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 0,
+ .irq_status_shift = 0,
+ },
+ [MT8195_AFE_IRQ_14] = {
+ .id = MT8195_AFE_IRQ_14,
+ .irq_cnt_reg = ASYS_IRQ2_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ2_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 1,
+ .irq_status_shift = 1,
+ },
+ [MT8195_AFE_IRQ_15] = {
+ .id = MT8195_AFE_IRQ_15,
+ .irq_cnt_reg = ASYS_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ3_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 2,
+ .irq_status_shift = 2,
+ },
+ [MT8195_AFE_IRQ_16] = {
+ .id = MT8195_AFE_IRQ_16,
+ .irq_cnt_reg = ASYS_IRQ4_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ4_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ4_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 3,
+ .irq_status_shift = 3,
+ },
+ [MT8195_AFE_IRQ_17] = {
+ .id = MT8195_AFE_IRQ_17,
+ .irq_cnt_reg = ASYS_IRQ5_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ5_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ5_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 4,
+ .irq_status_shift = 4,
+ },
+ [MT8195_AFE_IRQ_18] = {
+ .id = MT8195_AFE_IRQ_18,
+ .irq_cnt_reg = ASYS_IRQ6_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ6_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ6_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 5,
+ .irq_status_shift = 5,
+ },
+ [MT8195_AFE_IRQ_19] = {
+ .id = MT8195_AFE_IRQ_19,
+ .irq_cnt_reg = ASYS_IRQ7_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ7_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ7_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 6,
+ .irq_status_shift = 6,
+ },
+ [MT8195_AFE_IRQ_20] = {
+ .id = MT8195_AFE_IRQ_20,
+ .irq_cnt_reg = ASYS_IRQ8_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ8_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ8_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 7,
+ .irq_status_shift = 7,
+ },
+ [MT8195_AFE_IRQ_21] = {
+ .id = MT8195_AFE_IRQ_21,
+ .irq_cnt_reg = ASYS_IRQ9_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ9_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ9_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 8,
+ .irq_status_shift = 8,
+ },
+ [MT8195_AFE_IRQ_22] = {
+ .id = MT8195_AFE_IRQ_22,
+ .irq_cnt_reg = ASYS_IRQ10_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ10_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ10_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 9,
+ .irq_status_shift = 9,
+ },
+ [MT8195_AFE_IRQ_23] = {
+ .id = MT8195_AFE_IRQ_23,
+ .irq_cnt_reg = ASYS_IRQ11_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ11_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ11_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 10,
+ .irq_status_shift = 10,
+ },
+ [MT8195_AFE_IRQ_24] = {
+ .id = MT8195_AFE_IRQ_24,
+ .irq_cnt_reg = ASYS_IRQ12_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ12_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ12_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 11,
+ .irq_status_shift = 11,
+ },
+ [MT8195_AFE_IRQ_25] = {
+ .id = MT8195_AFE_IRQ_25,
+ .irq_cnt_reg = ASYS_IRQ13_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ13_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ13_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 12,
+ .irq_status_shift = 12,
+ },
+ [MT8195_AFE_IRQ_26] = {
+ .id = MT8195_AFE_IRQ_26,
+ .irq_cnt_reg = ASYS_IRQ14_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ14_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ14_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 13,
+ .irq_status_shift = 13,
+ },
+ [MT8195_AFE_IRQ_27] = {
+ .id = MT8195_AFE_IRQ_27,
+ .irq_cnt_reg = ASYS_IRQ15_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ15_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ15_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 14,
+ .irq_status_shift = 14,
+ },
+ [MT8195_AFE_IRQ_28] = {
+ .id = MT8195_AFE_IRQ_28,
+ .irq_cnt_reg = ASYS_IRQ16_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ16_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ16_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 15,
+ .irq_status_shift = 15,
+ },
+};
+
+static const int mt8195_afe_memif_const_irqs[MT8195_AFE_MEMIF_NUM] = {
+ [MT8195_AFE_MEMIF_DL2] = MT8195_AFE_IRQ_13,
+ [MT8195_AFE_MEMIF_DL3] = MT8195_AFE_IRQ_14,
+ [MT8195_AFE_MEMIF_DL6] = MT8195_AFE_IRQ_15,
+ [MT8195_AFE_MEMIF_DL7] = MT8195_AFE_IRQ_1,
+ [MT8195_AFE_MEMIF_DL8] = MT8195_AFE_IRQ_16,
+ [MT8195_AFE_MEMIF_DL10] = MT8195_AFE_IRQ_17,
+ [MT8195_AFE_MEMIF_DL11] = MT8195_AFE_IRQ_18,
+ [MT8195_AFE_MEMIF_UL1] = MT8195_AFE_IRQ_3,
+ [MT8195_AFE_MEMIF_UL2] = MT8195_AFE_IRQ_19,
+ [MT8195_AFE_MEMIF_UL3] = MT8195_AFE_IRQ_20,
+ [MT8195_AFE_MEMIF_UL4] = MT8195_AFE_IRQ_21,
+ [MT8195_AFE_MEMIF_UL5] = MT8195_AFE_IRQ_22,
+ [MT8195_AFE_MEMIF_UL6] = MT8195_AFE_IRQ_9,
+ [MT8195_AFE_MEMIF_UL8] = MT8195_AFE_IRQ_23,
+ [MT8195_AFE_MEMIF_UL9] = MT8195_AFE_IRQ_24,
+ [MT8195_AFE_MEMIF_UL10] = MT8195_AFE_IRQ_25,
+};
+
+static bool mt8195_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0:
+ case AUDIO_TOP_CON1:
+ case AUDIO_TOP_CON3:
+ case AUDIO_TOP_CON4:
+ case AUDIO_TOP_CON5:
+ case AUDIO_TOP_CON6:
+ case ASYS_IRQ_CLR:
+ case ASYS_IRQ_STATUS:
+ case ASYS_IRQ_MON1:
+ case ASYS_IRQ_MON2:
+ case AFE_IRQ_MCU_CLR:
+ case AFE_IRQ_STATUS:
+ case AFE_IRQ3_CON_MON:
+ case AFE_IRQ_MCU_MON2:
+ case ADSP_IRQ_STATUS:
+ case AUDIO_TOP_STA0:
+ case AUDIO_TOP_STA1:
+ case AFE_GAIN1_CUR:
+ case AFE_GAIN2_CUR:
+ case AFE_IEC_BURST_INFO:
+ case AFE_IEC_CHL_STAT0:
+ case AFE_IEC_CHL_STAT1:
+ case AFE_IEC_CHR_STAT0:
+ case AFE_IEC_CHR_STAT1:
+ case AFE_SPDIFIN_CHSTS1:
+ case AFE_SPDIFIN_CHSTS2:
+ case AFE_SPDIFIN_CHSTS3:
+ case AFE_SPDIFIN_CHSTS4:
+ case AFE_SPDIFIN_CHSTS5:
+ case AFE_SPDIFIN_CHSTS6:
+ case AFE_SPDIFIN_DEBUG1:
+ case AFE_SPDIFIN_DEBUG2:
+ case AFE_SPDIFIN_DEBUG3:
+ case AFE_SPDIFIN_DEBUG4:
+ case AFE_SPDIFIN_EC:
+ case AFE_SPDIFIN_CKLOCK_CFG:
+ case AFE_SPDIFIN_BR_DBG1:
+ case AFE_SPDIFIN_CKFBDIV:
+ case AFE_SPDIFIN_INT_EXT:
+ case AFE_SPDIFIN_INT_EXT2:
+ case SPDIFIN_FREQ_STATUS:
+ case SPDIFIN_USERCODE1:
+ case SPDIFIN_USERCODE2:
+ case SPDIFIN_USERCODE3:
+ case SPDIFIN_USERCODE4:
+ case SPDIFIN_USERCODE5:
+ case SPDIFIN_USERCODE6:
+ case SPDIFIN_USERCODE7:
+ case SPDIFIN_USERCODE8:
+ case SPDIFIN_USERCODE9:
+ case SPDIFIN_USERCODE10:
+ case SPDIFIN_USERCODE11:
+ case SPDIFIN_USERCODE12:
+ case AFE_LINEIN_APLL_TUNER_MON:
+ case AFE_EARC_APLL_TUNER_MON:
+ case AFE_CM0_MON:
+ case AFE_CM1_MON:
+ case AFE_CM2_MON:
+ case AFE_MPHONE_MULTI_DET_MON0:
+ case AFE_MPHONE_MULTI_DET_MON1:
+ case AFE_MPHONE_MULTI_DET_MON2:
+ case AFE_MPHONE_MULTI2_DET_MON0:
+ case AFE_MPHONE_MULTI2_DET_MON1:
+ case AFE_MPHONE_MULTI2_DET_MON2:
+ case AFE_ADDA_MTKAIF_MON0:
+ case AFE_ADDA_MTKAIF_MON1:
+ case AFE_AUD_PAD_TOP:
+ case AFE_ADDA6_MTKAIF_MON0:
+ case AFE_ADDA6_MTKAIF_MON1:
+ case AFE_ADDA6_SRC_DEBUG_MON0:
+ case AFE_ADDA6_UL_SRC_MON0:
+ case AFE_ADDA6_UL_SRC_MON1:
+ case AFE_ASRC11_NEW_CON8:
+ case AFE_ASRC11_NEW_CON9:
+ case AFE_ASRC12_NEW_CON8:
+ case AFE_ASRC12_NEW_CON9:
+ case AFE_LRCK_CNT:
+ case AFE_DAC_MON0:
+ case AFE_DL2_CUR:
+ case AFE_DL3_CUR:
+ case AFE_DL6_CUR:
+ case AFE_DL7_CUR:
+ case AFE_DL8_CUR:
+ case AFE_DL10_CUR:
+ case AFE_DL11_CUR:
+ case AFE_UL1_CUR:
+ case AFE_UL2_CUR:
+ case AFE_UL3_CUR:
+ case AFE_UL4_CUR:
+ case AFE_UL5_CUR:
+ case AFE_UL6_CUR:
+ case AFE_UL8_CUR:
+ case AFE_UL9_CUR:
+ case AFE_UL10_CUR:
+ case AFE_DL8_CHK_SUM1:
+ case AFE_DL8_CHK_SUM2:
+ case AFE_DL8_CHK_SUM3:
+ case AFE_DL8_CHK_SUM4:
+ case AFE_DL8_CHK_SUM5:
+ case AFE_DL8_CHK_SUM6:
+ case AFE_DL10_CHK_SUM1:
+ case AFE_DL10_CHK_SUM2:
+ case AFE_DL10_CHK_SUM3:
+ case AFE_DL10_CHK_SUM4:
+ case AFE_DL10_CHK_SUM5:
+ case AFE_DL10_CHK_SUM6:
+ case AFE_DL11_CHK_SUM1:
+ case AFE_DL11_CHK_SUM2:
+ case AFE_DL11_CHK_SUM3:
+ case AFE_DL11_CHK_SUM4:
+ case AFE_DL11_CHK_SUM5:
+ case AFE_DL11_CHK_SUM6:
+ case AFE_UL1_CHK_SUM1:
+ case AFE_UL1_CHK_SUM2:
+ case AFE_UL2_CHK_SUM1:
+ case AFE_UL2_CHK_SUM2:
+ case AFE_UL3_CHK_SUM1:
+ case AFE_UL3_CHK_SUM2:
+ case AFE_UL4_CHK_SUM1:
+ case AFE_UL4_CHK_SUM2:
+ case AFE_UL5_CHK_SUM1:
+ case AFE_UL5_CHK_SUM2:
+ case AFE_UL6_CHK_SUM1:
+ case AFE_UL6_CHK_SUM2:
+ case AFE_UL8_CHK_SUM1:
+ case AFE_UL8_CHK_SUM2:
+ case AFE_DL2_CHK_SUM1:
+ case AFE_DL2_CHK_SUM2:
+ case AFE_DL3_CHK_SUM1:
+ case AFE_DL3_CHK_SUM2:
+ case AFE_DL6_CHK_SUM1:
+ case AFE_DL6_CHK_SUM2:
+ case AFE_DL7_CHK_SUM1:
+ case AFE_DL7_CHK_SUM2:
+ case AFE_UL9_CHK_SUM1:
+ case AFE_UL9_CHK_SUM2:
+ case AFE_BUS_MON1:
+ case UL1_MOD2AGT_CNT_LAT:
+ case UL2_MOD2AGT_CNT_LAT:
+ case UL3_MOD2AGT_CNT_LAT:
+ case UL4_MOD2AGT_CNT_LAT:
+ case UL5_MOD2AGT_CNT_LAT:
+ case UL6_MOD2AGT_CNT_LAT:
+ case UL8_MOD2AGT_CNT_LAT:
+ case UL9_MOD2AGT_CNT_LAT:
+ case UL10_MOD2AGT_CNT_LAT:
+ case AFE_MEMIF_BUF_FULL_MON:
+ case AFE_MEMIF_BUF_MON1:
+ case AFE_MEMIF_BUF_MON3:
+ case AFE_MEMIF_BUF_MON4:
+ case AFE_MEMIF_BUF_MON5:
+ case AFE_MEMIF_BUF_MON6:
+ case AFE_MEMIF_BUF_MON7:
+ case AFE_MEMIF_BUF_MON8:
+ case AFE_MEMIF_BUF_MON9:
+ case AFE_MEMIF_BUF_MON10:
+ case DL2_AGENT2MODULE_CNT:
+ case DL3_AGENT2MODULE_CNT:
+ case DL6_AGENT2MODULE_CNT:
+ case DL7_AGENT2MODULE_CNT:
+ case DL8_AGENT2MODULE_CNT:
+ case DL10_AGENT2MODULE_CNT:
+ case DL11_AGENT2MODULE_CNT:
+ case UL1_MODULE2AGENT_CNT:
+ case UL2_MODULE2AGENT_CNT:
+ case UL3_MODULE2AGENT_CNT:
+ case UL4_MODULE2AGENT_CNT:
+ case UL5_MODULE2AGENT_CNT:
+ case UL6_MODULE2AGENT_CNT:
+ case UL8_MODULE2AGENT_CNT:
+ case UL9_MODULE2AGENT_CNT:
+ case UL10_MODULE2AGENT_CNT:
+ case AFE_DMIC0_SRC_DEBUG_MON0:
+ case AFE_DMIC0_UL_SRC_MON0:
+ case AFE_DMIC0_UL_SRC_MON1:
+ case AFE_DMIC1_SRC_DEBUG_MON0:
+ case AFE_DMIC1_UL_SRC_MON0:
+ case AFE_DMIC1_UL_SRC_MON1:
+ case AFE_DMIC2_SRC_DEBUG_MON0:
+ case AFE_DMIC2_UL_SRC_MON0:
+ case AFE_DMIC2_UL_SRC_MON1:
+ case AFE_DMIC3_SRC_DEBUG_MON0:
+ case AFE_DMIC3_UL_SRC_MON0:
+ case AFE_DMIC3_UL_SRC_MON1:
+ case DMIC_GAIN1_CUR:
+ case DMIC_GAIN2_CUR:
+ case DMIC_GAIN3_CUR:
+ case DMIC_GAIN4_CUR:
+ case ETDM_IN1_MONITOR:
+ case ETDM_IN2_MONITOR:
+ case ETDM_OUT1_MONITOR:
+ case ETDM_OUT2_MONITOR:
+ case ETDM_OUT3_MONITOR:
+ case AFE_ADDA_SRC_DEBUG_MON0:
+ case AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_DL_SDM_FIFO_MON:
+ case AFE_ADDA_DL_SRC_LCH_MON:
+ case AFE_ADDA_DL_SRC_RCH_MON:
+ case AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_GASRC0_NEW_CON8:
+ case AFE_GASRC0_NEW_CON9:
+ case AFE_GASRC0_NEW_CON12:
+ case AFE_GASRC1_NEW_CON8:
+ case AFE_GASRC1_NEW_CON9:
+ case AFE_GASRC1_NEW_CON12:
+ case AFE_GASRC2_NEW_CON8:
+ case AFE_GASRC2_NEW_CON9:
+ case AFE_GASRC2_NEW_CON12:
+ case AFE_GASRC3_NEW_CON8:
+ case AFE_GASRC3_NEW_CON9:
+ case AFE_GASRC3_NEW_CON12:
+ case AFE_GASRC4_NEW_CON8:
+ case AFE_GASRC4_NEW_CON9:
+ case AFE_GASRC4_NEW_CON12:
+ case AFE_GASRC5_NEW_CON8:
+ case AFE_GASRC5_NEW_CON9:
+ case AFE_GASRC5_NEW_CON12:
+ case AFE_GASRC6_NEW_CON8:
+ case AFE_GASRC6_NEW_CON9:
+ case AFE_GASRC6_NEW_CON12:
+ case AFE_GASRC7_NEW_CON8:
+ case AFE_GASRC7_NEW_CON9:
+ case AFE_GASRC7_NEW_CON12:
+ case AFE_GASRC8_NEW_CON8:
+ case AFE_GASRC8_NEW_CON9:
+ case AFE_GASRC8_NEW_CON12:
+ case AFE_GASRC9_NEW_CON8:
+ case AFE_GASRC9_NEW_CON9:
+ case AFE_GASRC9_NEW_CON12:
+ case AFE_GASRC10_NEW_CON8:
+ case AFE_GASRC10_NEW_CON9:
+ case AFE_GASRC10_NEW_CON12:
+ case AFE_GASRC11_NEW_CON8:
+ case AFE_GASRC11_NEW_CON9:
+ case AFE_GASRC11_NEW_CON12:
+ case AFE_GASRC12_NEW_CON8:
+ case AFE_GASRC12_NEW_CON9:
+ case AFE_GASRC12_NEW_CON12:
+ case AFE_GASRC13_NEW_CON8:
+ case AFE_GASRC13_NEW_CON9:
+ case AFE_GASRC13_NEW_CON12:
+ case AFE_GASRC14_NEW_CON8:
+ case AFE_GASRC14_NEW_CON9:
+ case AFE_GASRC14_NEW_CON12:
+ case AFE_GASRC15_NEW_CON8:
+ case AFE_GASRC15_NEW_CON9:
+ case AFE_GASRC15_NEW_CON12:
+ case AFE_GASRC16_NEW_CON8:
+ case AFE_GASRC16_NEW_CON9:
+ case AFE_GASRC16_NEW_CON12:
+ case AFE_GASRC17_NEW_CON8:
+ case AFE_GASRC17_NEW_CON9:
+ case AFE_GASRC17_NEW_CON12:
+ case AFE_GASRC18_NEW_CON8:
+ case AFE_GASRC18_NEW_CON9:
+ case AFE_GASRC18_NEW_CON12:
+ case AFE_GASRC19_NEW_CON8:
+ case AFE_GASRC19_NEW_CON9:
+ case AFE_GASRC19_NEW_CON12:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8195_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .volatile_reg = mt8195_is_volatile_reg,
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1),
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define AFE_IRQ_CLR_BITS (0x387)
+#define ASYS_IRQ_CLR_BITS (0xffff)
+
+static irqreturn_t mt8195_afe_irq_handler(int irq_id, void *dev_id)
+{
+ struct mtk_base_afe *afe = dev_id;
+ unsigned int val = 0;
+ unsigned int asys_irq_clr_bits = 0;
+ unsigned int afe_irq_clr_bits = 0;
+ unsigned int irq_status_bits = 0;
+ unsigned int irq_clr_bits = 0;
+ unsigned int mcu_irq_mask = 0;
+ int i = 0;
+ int ret = 0;
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &val);
+ if (ret) {
+ dev_info(afe->dev, "%s irq status err\n", __func__);
+ afe_irq_clr_bits = AFE_IRQ_CLR_BITS;
+ asys_irq_clr_bits = ASYS_IRQ_CLR_BITS;
+ goto err_irq;
+ }
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MASK, &mcu_irq_mask);
+ if (ret) {
+ dev_info(afe->dev, "%s read irq mask err\n", __func__);
+ afe_irq_clr_bits = AFE_IRQ_CLR_BITS;
+ asys_irq_clr_bits = ASYS_IRQ_CLR_BITS;
+ goto err_irq;
+ }
+
+ /* only clr cpu irq */
+ val &= mcu_irq_mask;
+
+ for (i = 0; i < MT8195_AFE_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+ struct mtk_base_irq_data const *irq_data;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq_data = afe->irqs[memif->irq_usage].irq_data;
+
+ irq_status_bits = BIT(irq_data->irq_status_shift);
+ irq_clr_bits = BIT(irq_data->irq_clr_shift);
+
+ if (!(val & irq_status_bits))
+ continue;
+
+ if (irq_data->irq_clr_reg == ASYS_IRQ_CLR)
+ asys_irq_clr_bits |= irq_clr_bits;
+ else
+ afe_irq_clr_bits |= irq_clr_bits;
+
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ if (asys_irq_clr_bits)
+ regmap_write(afe->regmap, ASYS_IRQ_CLR, asys_irq_clr_bits);
+ if (afe_irq_clr_bits)
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, afe_irq_clr_bits);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8195_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ mt8195_afe_disable_main_clock(afe);
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8195_afe_disable_reg_rw_clk(afe);
+
+ return 0;
+}
+
+static int mt8195_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+
+ mt8195_afe_enable_reg_rw_clk(afe);
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ mt8195_afe_enable_main_clock(afe);
+skip_regmap:
+ return 0;
+}
+
+static int init_memif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ int i;
+
+ for (i = MT8195_AFE_MEMIF_START; i < MT8195_AFE_MEMIF_END; i++) {
+ memif_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_memif_priv),
+ GFP_KERNEL);
+ if (!memif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[i] = memif_priv;
+ }
+
+ return 0;
+}
+
+static int mt8195_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8195_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8195_memif_dai_driver);
+
+ dai->dapm_widgets = mt8195_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8195_memif_widgets);
+ dai->dapm_routes = mt8195_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8195_memif_routes);
+ dai->controls = mt8195_memif_controls;
+ dai->num_controls = ARRAY_SIZE(mt8195_memif_controls);
+
+ return init_memif_priv_data(afe);
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8195_dai_adda_register,
+ mt8195_dai_etdm_register,
+ mt8195_dai_pcm_register,
+ mt8195_dai_memif_register,
+};
+
+static const struct reg_sequence mt8195_afe_reg_defaults[] = {
+ { AFE_IRQ_MASK, 0x387ffff },
+ { AFE_IRQ3_CON, BIT(30) },
+ { AFE_IRQ9_CON, BIT(30) },
+ { ETDM_IN1_CON4, 0x12000100 },
+ { ETDM_IN2_CON4, 0x12000100 },
+};
+
+static const struct reg_sequence mt8195_cg_patch[] = {
+ { AUDIO_TOP_CON0, 0xfffffffb },
+ { AUDIO_TOP_CON1, 0xfffffff8 },
+};
+
+static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8195_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ int i, irq_id, ret;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to assign memory region\n");
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ /* initial audio related clock */
+ ret = mt8195_afe_init_clock(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "init clock error\n");
+
+ /* reset controller to reset audio regs before regmap cache */
+ rstc = devm_reset_control_get_exclusive(dev, "audiosys");
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n");
+
+ ret = reset_control_reset(rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
+
+ spin_lock_init(&afe_priv->afe_ctrl_lock);
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ afe->irqs_size = MT8195_AFE_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data_array[i];
+
+ /* init memif */
+ afe->memif_size = MT8195_AFE_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = mt8195_afe_memif_const_irqs[i];
+ afe->memif[i].const_irq = 1;
+ afe->irqs[afe->memif[i].irq_usage].irq_occupyed = true;
+ }
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return -ENXIO;
+
+ ret = devm_request_irq(dev, irq_id, mt8195_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "dai cb%i register fail\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ afe->mtk_afe_hardware = &mt8195_afe_hardware;
+ afe->memif_fs = mt8195_memif_fs;
+ afe->irq_fs = mt8195_irq_fs;
+
+ afe->runtime_resume = mt8195_afe_runtime_resume;
+ afe->runtime_suspend = mt8195_afe_runtime_suspend;
+
+ platform_set_drvdata(pdev, afe);
+
+ afe_priv->topckgen = syscon_regmap_lookup_by_phandle(dev->of_node, "mediatek,topckgen");
+ if (IS_ERR(afe_priv->topckgen))
+ dev_dbg(afe->dev, "Cannot find topckgen controller: %ld\n",
+ PTR_ERR(afe_priv->topckgen));
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to resume device\n");
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mt8195_afe_regmap_config);
+ if (IS_ERR(afe->regmap)) {
+ ret = PTR_ERR(afe->regmap);
+ goto err_pm_put;
+ }
+
+ ret = regmap_register_patch(afe->regmap, mt8195_cg_patch,
+ ARRAY_SIZE(mt8195_cg_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply cg patch\n");
+ goto err_pm_put;
+ }
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ afe->dai_drivers, afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_pm_put;
+ }
+
+ ret = regmap_multi_reg_write(afe->regmap, mt8195_afe_reg_defaults,
+ ARRAY_SIZE(mt8195_afe_reg_defaults));
+ if (ret)
+ goto err_pm_put;
+
+ ret = pm_runtime_put_sync(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to suspend device\n");
+
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_put_sync(dev);
+
+ return ret;
+}
+
+static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt8195_afe_runtime_suspend(&pdev->dev);
+}
+
+static const struct of_device_id mt8195_afe_pcm_dt_match[] = {
+ {.compatible = "mediatek,mt8195-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8195_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8195_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8195_afe_runtime_suspend,
+ mt8195_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8195_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8195-audio",
+ .of_match_table = mt8195_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8195_afe_pm_ops),
+ },
+ .probe = mt8195_afe_pcm_dev_probe,
+ .remove = mt8195_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8195_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8195");
+MODULE_AUTHOR("Bicycle Tsai <bicycle.tsai@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c
new file mode 100644
index 000000000000..38594bc3f2f7
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8195-audsys-clk.h -- Mediatek 8195 audsys clock control
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "mt8195-afe-common.h"
+#include "mt8195-audsys-clk.h"
+#include "mt8195-audsys-clkid.h"
+#include "mt8195-reg.h"
+
+struct afe_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ int reg;
+ u8 bit;
+ const struct clk_ops *ops;
+ unsigned long flags;
+ u8 cg_flags;
+};
+
+#define GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, _flags, _cgflags) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .reg = _reg, \
+ .bit = _bit, \
+ .flags = _flags, \
+ .cg_flags = _cgflags, \
+ }
+
+#define GATE_AFE(_id, _name, _parent, _reg, _bit) \
+ GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, \
+ CLK_SET_RATE_PARENT, CLK_GATE_SET_TO_DISABLE)
+
+#define GATE_AUD0(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON0, _bit)
+
+#define GATE_AUD1(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON1, _bit)
+
+#define GATE_AUD3(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON3, _bit)
+
+#define GATE_AUD4(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON4, _bit)
+
+#define GATE_AUD5(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON5, _bit)
+
+#define GATE_AUD6(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON6, _bit)
+
+static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = {
+ /* AUD0 */
+ GATE_AUD0(CLK_AUD_AFE, "aud_afe", "top_a1sys_hp", 2),
+ GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "top_a1sys_hp", 4),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "top_apll4", 10),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "top_apll4", 11),
+ GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "top_a1sys_hp", 18),
+ GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "top_apll1", 19),
+ GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "top_apll2", 20),
+ GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "top_aud_iec_clk", 21),
+ GATE_AUD0(CLK_AUD_APLL, "aud_apll", "top_apll1", 23),
+ GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "top_apll2", 24),
+ GATE_AUD0(CLK_AUD_DAC, "aud_dac", "top_a1sys_hp", 25),
+ GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "top_a1sys_hp", 26),
+ GATE_AUD0(CLK_AUD_TML, "aud_tml", "top_a1sys_hp", 27),
+ GATE_AUD0(CLK_AUD_ADC, "aud_adc", "top_a1sys_hp", 28),
+ GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "top_audio_h", 31),
+
+ /* AUD1 */
+ GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "top_a1sys_hp", 2),
+ GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "top_a1sys_hp", 10),
+ GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "top_a1sys_hp", 11),
+ GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "top_a1sys_hp", 12),
+ GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "top_a1sys_hp", 13),
+ GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14),
+ GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16),
+ GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17),
+ GATE_AUD1(CLK_AUD_ADDA6_ADC, "aud_adda6_adc", "top_a1sys_hp", 18),
+ GATE_AUD1(CLK_AUD_ADDA6_ADC_HIRES, "aud_adda6_adc_hires", "top_audio_h", 19),
+
+ /* AUD3 */
+ GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5),
+ GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "top_apll3", 7),
+
+ /* AUD4 */
+ GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "top_a1sys_hp", 0),
+ GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "top_a1sys_hp", 1),
+ GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "top_a1sys_hp", 6),
+ GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "top_a1sys_hp", 7),
+ GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "top_a1sys_hp", 8),
+ GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "top_a1sys_hp", 16),
+ GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "top_a1sys_hp", 17),
+ GATE_AUD4(CLK_AUD_MULTI_IN, "aud_multi_in", "mphone_slave_b", 19),
+ GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "top_intdir", 20),
+ GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "top_a1sys_hp", 21),
+ GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "top_a2sys_hf", 22),
+ GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "top_a1sys_hp", 24),
+ GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "top_a3sys_hf", 30),
+ GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "top_a4sys_hf", 31),
+
+ /* AUD5 */
+ GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "top_a1sys_hp", 0),
+ GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "top_a1sys_hp", 1),
+ GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "top_a1sys_hp", 2),
+ GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "top_a1sys_hp", 3),
+ GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "top_a1sys_hp", 4),
+ GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "top_a1sys_hp", 5),
+ GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "top_a1sys_hp", 7),
+ GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "top_a1sys_hp", 8),
+ GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "top_a1sys_hp", 9),
+ GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "top_a1sys_hp", 18),
+ GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "top_a1sys_hp", 19),
+ GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "top_a1sys_hp", 22),
+ GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "top_a1sys_hp", 23),
+ GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "top_a1sys_hp", 24),
+ GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "top_a1sys_hp", 26),
+ GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "top_a1sys_hp", 27),
+
+ /* AUD6 */
+ GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "top_asm_h", 0),
+ GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "top_asm_h", 1),
+ GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "top_asm_h", 2),
+ GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "top_asm_h", 3),
+ GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "top_asm_h", 4),
+ GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "top_asm_h", 5),
+ GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "top_asm_h", 6),
+ GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "top_asm_h", 7),
+ GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "top_asm_h", 8),
+ GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "top_asm_h", 9),
+ GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "top_asm_h", 10),
+ GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "top_asm_h", 11),
+ GATE_AUD6(CLK_AUD_GASRC12, "aud_gasrc12", "top_asm_h", 12),
+ GATE_AUD6(CLK_AUD_GASRC13, "aud_gasrc13", "top_asm_h", 13),
+ GATE_AUD6(CLK_AUD_GASRC14, "aud_gasrc14", "top_asm_h", 14),
+ GATE_AUD6(CLK_AUD_GASRC15, "aud_gasrc15", "top_asm_h", 15),
+ GATE_AUD6(CLK_AUD_GASRC16, "aud_gasrc16", "top_asm_h", 16),
+ GATE_AUD6(CLK_AUD_GASRC17, "aud_gasrc17", "top_asm_h", 17),
+ GATE_AUD6(CLK_AUD_GASRC18, "aud_gasrc18", "top_asm_h", 18),
+ GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "top_asm_h", 19),
+};
+
+static void mt8195_audsys_clk_unregister(void *data)
+{
+ struct mtk_base_afe *afe = data;
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ if (!afe_priv)
+ return;
+
+ for (i = 0; i < CLK_AUD_NR_CLK; i++) {
+ cl = afe_priv->lookup[i];
+ if (!cl)
+ continue;
+
+ clk = cl->clk;
+ clk_unregister_gate(clk);
+
+ clkdev_drop(cl);
+ }
+}
+
+int mt8195_audsys_clk_register(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AUD_NR_CLK,
+ sizeof(*afe_priv->lookup),
+ GFP_KERNEL);
+
+ if (!afe_priv->lookup)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ const struct afe_gate *gate = &aud_clks[i];
+
+ clk = clk_register_gate(afe->dev, gate->name, gate->parent_name,
+ gate->flags, afe->base_addr + gate->reg,
+ gate->bit, gate->cg_flags, NULL);
+
+ if (IS_ERR(clk)) {
+ dev_err(afe->dev, "Failed to register clk %s: %ld\n",
+ gate->name, PTR_ERR(clk));
+ continue;
+ }
+
+ /* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return -ENOMEM;
+
+ cl->clk = clk;
+ cl->con_id = gate->name;
+ cl->dev_id = dev_name(afe->dev);
+ clkdev_add(cl);
+
+ afe_priv->lookup[i] = cl;
+ }
+
+ return devm_add_action_or_reset(afe->dev, mt8195_audsys_clk_unregister, afe);
+}
diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h
new file mode 100644
index 000000000000..69db2dd1c9e0
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8195-audsys-clk.h -- Mediatek 8195 audsys clock definition
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MT8195_AUDSYS_CLK_H_
+#define _MT8195_AUDSYS_CLK_H_
+
+int mt8195_audsys_clk_register(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h b/sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h
new file mode 100644
index 000000000000..4dd0a5c8b8fa
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8195-audsys-clkid.h -- Mediatek 8195 audsys clock id definition
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MT8195_AUDSYS_CLKID_H_
+#define _MT8195_AUDSYS_CLKID_H_
+
+enum{
+ CLK_AUD_AFE,
+ CLK_AUD_LRCK_CNT,
+ CLK_AUD_SPDIFIN_TUNER_APLL,
+ CLK_AUD_SPDIFIN_TUNER_DBG,
+ CLK_AUD_UL_TML,
+ CLK_AUD_APLL1_TUNER,
+ CLK_AUD_APLL2_TUNER,
+ CLK_AUD_TOP0_SPDF,
+ CLK_AUD_APLL,
+ CLK_AUD_APLL2,
+ CLK_AUD_DAC,
+ CLK_AUD_DAC_PREDIS,
+ CLK_AUD_TML,
+ CLK_AUD_ADC,
+ CLK_AUD_DAC_HIRES,
+ CLK_AUD_A1SYS_HP,
+ CLK_AUD_AFE_DMIC1,
+ CLK_AUD_AFE_DMIC2,
+ CLK_AUD_AFE_DMIC3,
+ CLK_AUD_AFE_DMIC4,
+ CLK_AUD_AFE_26M_DMIC_TM,
+ CLK_AUD_UL_TML_HIRES,
+ CLK_AUD_ADC_HIRES,
+ CLK_AUD_ADDA6_ADC,
+ CLK_AUD_ADDA6_ADC_HIRES,
+ CLK_AUD_LINEIN_TUNER,
+ CLK_AUD_EARC_TUNER,
+ CLK_AUD_I2SIN,
+ CLK_AUD_TDM_IN,
+ CLK_AUD_I2S_OUT,
+ CLK_AUD_TDM_OUT,
+ CLK_AUD_HDMI_OUT,
+ CLK_AUD_ASRC11,
+ CLK_AUD_ASRC12,
+ CLK_AUD_MULTI_IN,
+ CLK_AUD_INTDIR,
+ CLK_AUD_A1SYS,
+ CLK_AUD_A2SYS,
+ CLK_AUD_PCMIF,
+ CLK_AUD_A3SYS,
+ CLK_AUD_A4SYS,
+ CLK_AUD_MEMIF_UL1,
+ CLK_AUD_MEMIF_UL2,
+ CLK_AUD_MEMIF_UL3,
+ CLK_AUD_MEMIF_UL4,
+ CLK_AUD_MEMIF_UL5,
+ CLK_AUD_MEMIF_UL6,
+ CLK_AUD_MEMIF_UL8,
+ CLK_AUD_MEMIF_UL9,
+ CLK_AUD_MEMIF_UL10,
+ CLK_AUD_MEMIF_DL2,
+ CLK_AUD_MEMIF_DL3,
+ CLK_AUD_MEMIF_DL6,
+ CLK_AUD_MEMIF_DL7,
+ CLK_AUD_MEMIF_DL8,
+ CLK_AUD_MEMIF_DL10,
+ CLK_AUD_MEMIF_DL11,
+ CLK_AUD_GASRC0,
+ CLK_AUD_GASRC1,
+ CLK_AUD_GASRC2,
+ CLK_AUD_GASRC3,
+ CLK_AUD_GASRC4,
+ CLK_AUD_GASRC5,
+ CLK_AUD_GASRC6,
+ CLK_AUD_GASRC7,
+ CLK_AUD_GASRC8,
+ CLK_AUD_GASRC9,
+ CLK_AUD_GASRC10,
+ CLK_AUD_GASRC11,
+ CLK_AUD_GASRC12,
+ CLK_AUD_GASRC13,
+ CLK_AUD_GASRC14,
+ CLK_AUD_GASRC15,
+ CLK_AUD_GASRC16,
+ CLK_AUD_GASRC17,
+ CLK_AUD_GASRC18,
+ CLK_AUD_GASRC19,
+ CLK_AUD_NR_CLK,
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-adda.c b/sound/soc/mediatek/mt8195/mt8195-dai-adda.c
new file mode 100644
index 000000000000..94abde15ea09
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-adda.c
@@ -0,0 +1,753 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI ADDA Control
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include "mt8195-afe-clk.h"
+#include "mt8195-afe-common.h"
+#include "mt8195-reg.h"
+#include "../common/mtk-dai-adda-common.h"
+
+#define ADDA_DL_GAIN_LOOPBACK 0x1800
+#define ADDA_HIRES_THRES 48000
+
+enum {
+ SUPPLY_SEQ_CLOCK_SEL,
+ SUPPLY_SEQ_CLOCK_ON,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA_UL_ON,
+ SUPPLY_SEQ_ADDA_AFE_ON,
+};
+
+enum {
+ MTK_AFE_ADDA,
+ MTK_AFE_ADDA6,
+};
+
+struct mtk_dai_adda_priv {
+ bool hires_required;
+};
+
+static int mt8195_adda_mtkaif_init(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int delay_data;
+ int delay_cycle;
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ /* set rx protocol 2 & mtkaif_rxif_clkinv_adc inverse */
+ mask = (MTKAIF_RXIF_CLKINV_ADC | MTKAIF_RXIF_PROTOCOL2);
+ val = (MTKAIF_RXIF_CLKINV_ADC | MTKAIF_RXIF_PROTOCOL2);
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_CFG0, mask, val);
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIF_CFG0, mask, val);
+
+ mask = RG_RX_PROTOCOL2;
+ val = RG_RX_PROTOCOL2;
+ regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP, mask, val);
+
+ if (!param->mtkaif_calibration_ok) {
+ dev_info(afe->dev, "%s(), calibration fail\n", __func__);
+ return 0;
+ }
+
+ /* set delay for ch1, ch2 */
+ if (param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] >=
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] -
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1];
+ } else {
+ delay_data = DELAY_DATA_MISO0;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] -
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0];
+ }
+
+ val = 0;
+ mask = (MTKAIF_RXIF_DELAY_DATA | MTKAIF_RXIF_DELAY_CYCLE_MASK);
+ val |= MTKAIF_RXIF_DELAY_CYCLE(delay_cycle) &
+ MTKAIF_RXIF_DELAY_CYCLE_MASK;
+ val |= delay_data << MTKAIF_RXIF_DELAY_DATA_SHIFT;
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG2, mask, val);
+
+ /* set delay between ch3 and ch2 */
+ if (param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] >=
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] -
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1];
+ } else {
+ delay_data = DELAY_DATA_MISO2;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] -
+ param->mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2];
+ }
+
+ val = 0;
+ mask = (MTKAIF_RXIF_DELAY_DATA | MTKAIF_RXIF_DELAY_CYCLE_MASK);
+ val |= MTKAIF_RXIF_DELAY_CYCLE(delay_cycle) &
+ MTKAIF_RXIF_DELAY_CYCLE_MASK;
+ val |= delay_data << MTKAIF_RXIF_DELAY_DATA_SHIFT;
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIF_RX_CFG2, mask, val);
+
+ return 0;
+}
+
+static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8195_adda_mtkaif_init(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void mtk_adda_ul_mictype(struct mtk_base_afe *afe, int adda, bool dmic)
+{
+ unsigned int reg = 0;
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ switch (adda) {
+ case MTK_AFE_ADDA:
+ reg = AFE_ADDA_UL_SRC_CON0;
+ break;
+ case MTK_AFE_ADDA6:
+ reg = AFE_ADDA6_UL_SRC_CON0;
+ break;
+ default:
+ dev_info(afe->dev, "%s(), wrong parameter\n", __func__);
+ return;
+ }
+
+ mask = (UL_SDM3_LEVEL_CTL | UL_MODE_3P25M_CH1_CTL |
+ UL_MODE_3P25M_CH2_CTL);
+
+ /* turn on dmic, ch1, ch2 */
+ if (dmic)
+ val = mask;
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_adda_ul_mictype(afe, MTK_AFE_ADDA, param->mtkaif_dmic_on);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda6_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ unsigned int val;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_adda_ul_mictype(afe, MTK_AFE_ADDA6, param->mtkaif_dmic_on);
+
+ val = (param->mtkaif_adda6_only ?
+ ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE : 0);
+
+ regmap_update_bits(afe->regmap,
+ AFE_ADDA_MTKAIF_SYNCWORD_CFG,
+ ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE,
+ val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_audio_hires_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk = afe_priv->clk[MT8195_CLK_TOP_AUDIO_H_SEL];
+ struct clk *clk_parent;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ clk_parent = afe_priv->clk[MT8195_CLK_TOP_APLL1];
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ clk_parent = afe_priv->clk[MT8195_CLK_XTAL_26M];
+ break;
+ default:
+ return 0;
+ }
+ mt8195_afe_set_clk_parent(afe, clk, clk_parent);
+
+ return 0;
+}
+
+static struct mtk_dai_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int dai_id;
+
+ if (strstr(name, "aud_adc_hires"))
+ dai_id = MT8195_AFE_IO_UL_SRC1;
+ else if (strstr(name, "aud_adda6_adc_hires"))
+ dai_id = MT8195_AFE_IO_UL_SRC2;
+ else if (strstr(name, "aud_dac_hires"))
+ dai_id = MT8195_AFE_IO_DL_SRC;
+ else
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
+static int mtk_afe_adda_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_dai_adda_priv *adda_priv;
+
+ adda_priv = get_adda_priv_by_name(afe, w->name);
+
+ if (!adda_priv) {
+ dev_info(afe->dev, "adda_priv == NULL");
+ return 0;
+ }
+
+ return (adda_priv->hires_required) ? 1 : 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN176, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN176, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN176, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN176, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN176_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_adda_o177_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN177, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN177, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN177, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN177, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN177_2, 7, 1, 0),
+};
+
+static const char * const adda_dlgain_mux_map[] = {
+ "Bypass", "Connect",
+};
+
+static SOC_ENUM_SINGLE_DECL(adda_dlgain_mux_map_enum,
+ SND_SOC_NOPM, 0,
+ adda_dlgain_mux_map);
+
+static const struct snd_kcontrol_new adda_dlgain_mux_control =
+ SOC_DAPM_ENUM("DL_GAIN_MUX", adda_dlgain_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ SND_SOC_DAPM_MIXER("I168", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I169", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I170", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I171", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O176", SND_SOC_NOPM, 0, 0,
+ mtk_dai_adda_o176_mix,
+ ARRAY_SIZE(mtk_dai_adda_o176_mix)),
+ SND_SOC_DAPM_MIXER("O177", SND_SOC_NOPM, 0, 0,
+ mtk_dai_adda_o177_mix,
+ ARRAY_SIZE(mtk_dai_adda_o177_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0,
+ ADDA_AFE_ON_SHIFT, 0,
+ NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT, 0,
+ mtk_adda_dl_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SHIFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA6 Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA6_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SHIFT, 0,
+ mtk_adda6_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_HIRES", SUPPLY_SEQ_CLOCK_SEL,
+ SND_SOC_NOPM,
+ 0, 0,
+ mtk_audio_hires_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SND_SOC_NOPM,
+ 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MUX("DL_GAIN_MUX", SND_SOC_NOPM, 0, 0,
+ &adda_dlgain_mux_control),
+
+ SND_SOC_DAPM_PGA("DL_GAIN", AFE_ADDA_DL_SRC2_CON0,
+ DL_2_GAIN_ON_CTL_PRE_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("ADDA_INPUT"),
+ SND_SOC_DAPM_OUTPUT("ADDA_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adda6_adc"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_hires"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_hires"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adda6_adc_hires"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+ {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
+ {"ADDA Capture", NULL, "aud_adc"},
+ {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adda_hires_connect},
+ {"aud_adc_hires", NULL, "AUDIO_HIRES"},
+
+ {"ADDA6 Capture", NULL, "ADDA Enable"},
+ {"ADDA6 Capture", NULL, "ADDA6 Capture Enable"},
+ {"ADDA6 Capture", NULL, "ADDA_MTKAIF_CFG"},
+ {"ADDA6 Capture", NULL, "aud_adda6_adc"},
+ {"ADDA6 Capture", NULL, "aud_adda6_adc_hires",
+ mtk_afe_adda_hires_connect},
+ {"aud_adda6_adc_hires", NULL, "AUDIO_HIRES"},
+
+ {"I168", NULL, "ADDA Capture"},
+ {"I169", NULL, "ADDA Capture"},
+ {"I170", NULL, "ADDA6 Capture"},
+ {"I171", NULL, "ADDA6 Capture"},
+
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+ {"ADDA Playback", NULL, "aud_dac"},
+ {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_adda_hires_connect},
+ {"aud_dac_hires", NULL, "AUDIO_HIRES"},
+
+ {"DL_GAIN", NULL, "O176"},
+ {"DL_GAIN", NULL, "O177"},
+
+ {"DL_GAIN_MUX", "Bypass", "O176"},
+ {"DL_GAIN_MUX", "Bypass", "O177"},
+ {"DL_GAIN_MUX", "Connect", "DL_GAIN"},
+
+ {"ADDA Playback", NULL, "DL_GAIN_MUX"},
+
+ {"O176", "I000 Switch", "I000"},
+ {"O177", "I001 Switch", "I001"},
+
+ {"O176", "I002 Switch", "I002"},
+ {"O177", "I003 Switch", "I003"},
+
+ {"O176", "I020 Switch", "I020"},
+ {"O177", "I021 Switch", "I021"},
+
+ {"O176", "I022 Switch", "I022"},
+ {"O177", "I023 Switch", "I023"},
+
+ {"O176", "I070 Switch", "I070"},
+ {"O177", "I071 Switch", "I071"},
+
+ {"ADDA Capture", NULL, "ADDA_INPUT"},
+ {"ADDA6 Capture", NULL, "ADDA_INPUT"},
+ {"ADDA_OUTPUT", NULL, "ADDA Playback"},
+};
+
+static int mt8195_adda_dl_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int reg = AFE_ADDA_DL_SRC2_CON1;
+ unsigned int mask = DL_2_GAIN_CTL_PRE_MASK;
+ unsigned int value = (unsigned int)(ucontrol->value.integer.value[0]);
+
+ regmap_update_bits(afe->regmap, reg, mask, DL_2_GAIN_CTL_PRE(value));
+ return 0;
+}
+
+static int mt8195_adda_dl_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int reg = AFE_ADDA_DL_SRC2_CON1;
+ unsigned int mask = DL_2_GAIN_CTL_PRE_MASK;
+ unsigned int value = 0;
+
+ regmap_read(afe->regmap, reg, &value);
+
+ ucontrol->value.integer.value[0] = ((value & mask) >>
+ DL_2_GAIN_CTL_PRE_SHIFT);
+ return 0;
+}
+
+static int mt8195_adda6_only_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ ucontrol->value.integer.value[0] = param->mtkaif_adda6_only;
+ return 0;
+}
+
+static int mt8195_adda6_only_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int mtkaif_adda6_only;
+
+ mtkaif_adda6_only = ucontrol->value.integer.value[0];
+
+ dev_info(afe->dev, "%s(), kcontrol name %s, mtkaif_adda6_only %d\n",
+ __func__, kcontrol->id.name, mtkaif_adda6_only);
+
+ param->mtkaif_adda6_only = mtkaif_adda6_only;
+
+ return 0;
+}
+
+static int mt8195_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ ucontrol->value.integer.value[0] = param->mtkaif_dmic_on;
+ return 0;
+}
+
+static int mt8195_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int dmic_on;
+
+ dmic_on = ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, dmic_on %d\n",
+ __func__, kcontrol->id.name, dmic_on);
+
+ param->mtkaif_dmic_on = dmic_on;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_adda_controls[] = {
+ SOC_SINGLE_EXT("ADDA_DL_Gain", SND_SOC_NOPM, 0, 65535, 0,
+ mt8195_adda_dl_gain_get, mt8195_adda_dl_gain_put),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC", 0,
+ mt8195_adda_dmic_get, mt8195_adda_dmic_set),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_ADDA6_ONLY", 0,
+ mt8195_adda6_only_get,
+ mt8195_adda6_only_set),
+};
+
+static int mtk_dai_da_configure(struct mtk_base_afe *afe,
+ unsigned int rate, int id)
+{
+ unsigned int val = 0;
+ unsigned int mask = 0;
+
+ /* set sampling rate */
+ mask |= DL_2_INPUT_MODE_CTL_MASK;
+ val |= DL_2_INPUT_MODE_CTL(mtk_adda_dl_rate_transform(afe, rate));
+
+ /* turn off saturation */
+ mask |= DL_2_CH1_SATURATION_EN_CTL;
+ mask |= DL_2_CH2_SATURATION_EN_CTL;
+
+ /* turn off mute function */
+ mask |= DL_2_MUTE_CH1_OFF_CTL_PRE;
+ mask |= DL_2_MUTE_CH2_OFF_CTL_PRE;
+ val |= DL_2_MUTE_CH1_OFF_CTL_PRE;
+ val |= DL_2_MUTE_CH2_OFF_CTL_PRE;
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ mask |= DL_2_VOICE_MODE_CTL_PRE;
+ if (rate == 8000 || rate == 16000)
+ val |= DL_2_VOICE_MODE_CTL_PRE;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* new 2nd sdm */
+ mask |= DL_USE_NEW_2ND_SDM;
+ val |= DL_USE_NEW_2ND_SDM;
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON, mask, val);
+
+ return 0;
+}
+
+static int mtk_dai_ad_configure(struct mtk_base_afe *afe,
+ unsigned int rate, int id)
+{
+ unsigned int val = 0;
+ unsigned int mask = 0;
+
+ mask |= UL_VOICE_MODE_CTL_MASK;
+ val |= UL_VOICE_MODE_CTL(mtk_adda_ul_rate_transform(afe, rate));
+
+ switch (id) {
+ case MT8195_AFE_IO_UL_SRC1:
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ mask, val);
+ break;
+ case MT8195_AFE_IO_UL_SRC2:
+ regmap_update_bits(afe->regmap, AFE_ADDA6_UL_SRC_CON0,
+ mask, val);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv;
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ if (dai->id != MT8195_AFE_IO_DL_SRC &&
+ dai->id != MT8195_AFE_IO_UL_SRC1 &&
+ dai->id != MT8195_AFE_IO_UL_SRC2)
+ return -EINVAL;
+ adda_priv = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
+ __func__, dai->id, substream->stream, rate);
+
+ if (rate > ADDA_HIRES_THRES)
+ adda_priv->hires_required = 1;
+ else
+ adda_priv->hires_required = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = mtk_dai_da_configure(afe, rate, dai->id);
+ else
+ ret = mtk_dai_ad_configure(afe, rate, dai->id);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "DL_SRC",
+ .id = MT8195_AFE_IO_DL_SRC,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "UL_SRC1",
+ .id = MT8195_AFE_IO_UL_SRC1,
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "UL_SRC2",
+ .id = MT8195_AFE_IO_UL_SRC2,
+ .capture = {
+ .stream_name = "ADDA6 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+static int init_adda_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv;
+ static const int adda_dai_list[] = {
+ MT8195_AFE_IO_DL_SRC,
+ MT8195_AFE_IO_UL_SRC1,
+ MT8195_AFE_IO_UL_SRC2
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
+ adda_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_adda_priv),
+ GFP_KERNEL);
+ if (!adda_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
+ }
+
+ return 0;
+}
+
+int mt8195_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+ dai->controls = mtk_dai_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_adda_controls);
+
+ return init_adda_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
new file mode 100644
index 000000000000..723cab01e72e
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
@@ -0,0 +1,2778 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI eTDM Control
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8195-afe-clk.h"
+#include "mt8195-afe-common.h"
+#include "mt8195-reg.h"
+
+#define MT8195_ETDM_MAX_CHANNELS 24
+#define MT8195_ETDM_NORMAL_MAX_BCK_RATE 24576000
+#define ETDM_TO_DAI_ID(x) ((x) + MT8195_AFE_IO_ETDM_START)
+#define ENUM_TO_STR(x) #x
+
+enum {
+ MTK_DAI_ETDM_FORMAT_I2S = 0,
+ MTK_DAI_ETDM_FORMAT_LJ,
+ MTK_DAI_ETDM_FORMAT_RJ,
+ MTK_DAI_ETDM_FORMAT_EIAJ,
+ MTK_DAI_ETDM_FORMAT_DSPA,
+ MTK_DAI_ETDM_FORMAT_DSPB,
+};
+
+enum {
+ MTK_DAI_ETDM_DATA_ONE_PIN = 0,
+ MTK_DAI_ETDM_DATA_MULTI_PIN,
+};
+
+enum {
+ ETDM_IN,
+ ETDM_OUT,
+};
+
+enum {
+ ETDM_IN_FROM_PAD,
+ ETDM_IN_FROM_ETDM_OUT1,
+ ETDM_IN_FROM_ETDM_OUT2,
+};
+
+enum {
+ ETDM_IN_SLAVE_FROM_PAD,
+ ETDM_IN_SLAVE_FROM_ETDM_OUT1,
+ ETDM_IN_SLAVE_FROM_ETDM_OUT2,
+};
+
+enum {
+ ETDM_OUT_SLAVE_FROM_PAD,
+ ETDM_OUT_SLAVE_FROM_ETDM_IN1,
+ ETDM_OUT_SLAVE_FROM_ETDM_IN2,
+};
+
+enum {
+ COWORK_ETDM_NONE = 0,
+ COWORK_ETDM_IN1_M = 2,
+ COWORK_ETDM_IN1_S = 3,
+ COWORK_ETDM_IN2_M = 4,
+ COWORK_ETDM_IN2_S = 5,
+ COWORK_ETDM_OUT1_M = 10,
+ COWORK_ETDM_OUT1_S = 11,
+ COWORK_ETDM_OUT2_M = 12,
+ COWORK_ETDM_OUT2_S = 13,
+ COWORK_ETDM_OUT3_M = 14,
+ COWORK_ETDM_OUT3_S = 15,
+};
+
+enum {
+ ETDM_RELATCH_TIMING_A1A2SYS,
+ ETDM_RELATCH_TIMING_A3SYS,
+ ETDM_RELATCH_TIMING_A4SYS,
+};
+
+enum {
+ ETDM_SYNC_NONE,
+ ETDM_SYNC_FROM_IN1,
+ ETDM_SYNC_FROM_IN2,
+ ETDM_SYNC_FROM_OUT1,
+ ETDM_SYNC_FROM_OUT2,
+ ETDM_SYNC_FROM_OUT3,
+};
+
+struct etdm_con_reg {
+ unsigned int con0;
+ unsigned int con1;
+ unsigned int con2;
+ unsigned int con3;
+ unsigned int con4;
+ unsigned int con5;
+};
+
+struct mtk_dai_etdm_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+struct mtk_dai_etdm_priv {
+ unsigned int clock_mode;
+ unsigned int data_mode;
+ bool slave_mode;
+ bool lrck_inv;
+ bool bck_inv;
+ unsigned int format;
+ unsigned int slots;
+ unsigned int lrck_width;
+ unsigned int mclk_freq;
+ unsigned int mclk_apll;
+ unsigned int mclk_dir;
+ int cowork_source_id; //dai id
+ unsigned int cowork_slv_count;
+ int cowork_slv_id[MT8195_AFE_IO_ETDM_NUM - 1]; //dai_id
+ bool in_disable_ch[MT8195_ETDM_MAX_CHANNELS];
+ unsigned int en_ref_cnt;
+};
+
+static const struct mtk_dai_etdm_rate mt8195_etdm_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 12000, .reg_value = 1, },
+ { .rate = 16000, .reg_value = 2, },
+ { .rate = 24000, .reg_value = 3, },
+ { .rate = 32000, .reg_value = 4, },
+ { .rate = 48000, .reg_value = 5, },
+ { .rate = 96000, .reg_value = 7, },
+ { .rate = 192000, .reg_value = 9, },
+ { .rate = 384000, .reg_value = 11, },
+ { .rate = 11025, .reg_value = 16, },
+ { .rate = 22050, .reg_value = 17, },
+ { .rate = 44100, .reg_value = 18, },
+ { .rate = 88200, .reg_value = 19, },
+ { .rate = 176400, .reg_value = 20, },
+ { .rate = 352800, .reg_value = 21, },
+};
+
+static bool mt8195_afe_etdm_is_valid(int id)
+{
+ switch (id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ fallthrough;
+ case MT8195_AFE_IO_ETDM2_IN:
+ fallthrough;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ fallthrough;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ fallthrough;
+ case MT8195_AFE_IO_DPTX:
+ fallthrough;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mt8195_afe_hdmitx_dptx_is_valid(int id)
+{
+ switch (id) {
+ case MT8195_AFE_IO_DPTX:
+ fallthrough;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int get_etdm_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8195_etdm_rates); i++)
+ if (mt8195_etdm_rates[i].rate == rate)
+ return mt8195_etdm_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static unsigned int get_etdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 16)
+ return 24;
+ else if (channels > 8)
+ return 16;
+ else if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+ else
+ return 2;
+}
+
+static int get_etdm_reg(unsigned int dai_id, struct etdm_con_reg *etdm_reg)
+{
+ switch (dai_id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ etdm_reg->con0 = ETDM_IN1_CON0;
+ etdm_reg->con1 = ETDM_IN1_CON1;
+ etdm_reg->con2 = ETDM_IN1_CON2;
+ etdm_reg->con3 = ETDM_IN1_CON3;
+ etdm_reg->con4 = ETDM_IN1_CON4;
+ etdm_reg->con5 = ETDM_IN1_CON5;
+ break;
+ case MT8195_AFE_IO_ETDM2_IN:
+ etdm_reg->con0 = ETDM_IN2_CON0;
+ etdm_reg->con1 = ETDM_IN2_CON1;
+ etdm_reg->con2 = ETDM_IN2_CON2;
+ etdm_reg->con3 = ETDM_IN2_CON3;
+ etdm_reg->con4 = ETDM_IN2_CON4;
+ etdm_reg->con5 = ETDM_IN2_CON5;
+ break;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ etdm_reg->con0 = ETDM_OUT1_CON0;
+ etdm_reg->con1 = ETDM_OUT1_CON1;
+ etdm_reg->con2 = ETDM_OUT1_CON2;
+ etdm_reg->con3 = ETDM_OUT1_CON3;
+ etdm_reg->con4 = ETDM_OUT1_CON4;
+ etdm_reg->con5 = ETDM_OUT1_CON5;
+ break;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ etdm_reg->con0 = ETDM_OUT2_CON0;
+ etdm_reg->con1 = ETDM_OUT2_CON1;
+ etdm_reg->con2 = ETDM_OUT2_CON2;
+ etdm_reg->con3 = ETDM_OUT2_CON3;
+ etdm_reg->con4 = ETDM_OUT2_CON4;
+ etdm_reg->con5 = ETDM_OUT2_CON5;
+ break;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ case MT8195_AFE_IO_DPTX:
+ etdm_reg->con0 = ETDM_OUT3_CON0;
+ etdm_reg->con1 = ETDM_OUT3_CON1;
+ etdm_reg->con2 = ETDM_OUT3_CON2;
+ etdm_reg->con3 = ETDM_OUT3_CON3;
+ etdm_reg->con4 = ETDM_OUT3_CON4;
+ etdm_reg->con5 = ETDM_OUT3_CON5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int get_etdm_dir(unsigned int dai_id)
+{
+ switch (dai_id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ case MT8195_AFE_IO_ETDM2_IN:
+ return ETDM_IN;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ case MT8195_AFE_IO_ETDM2_OUT:
+ case MT8195_AFE_IO_ETDM3_OUT:
+ return ETDM_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int get_etdm_wlen(unsigned int bitwidth)
+{
+ return bitwidth <= 16 ? 16 : 32;
+}
+
+static int is_cowork_mode(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!mt8195_afe_etdm_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+ return (etdm_data->cowork_slv_count > 0 ||
+ etdm_data->cowork_source_id != COWORK_ETDM_NONE);
+}
+
+static int sync_to_dai_id(int source_sel)
+{
+ switch (source_sel) {
+ case ETDM_SYNC_FROM_IN1:
+ return MT8195_AFE_IO_ETDM1_IN;
+ case ETDM_SYNC_FROM_IN2:
+ return MT8195_AFE_IO_ETDM2_IN;
+ case ETDM_SYNC_FROM_OUT1:
+ return MT8195_AFE_IO_ETDM1_OUT;
+ case ETDM_SYNC_FROM_OUT2:
+ return MT8195_AFE_IO_ETDM2_OUT;
+ case ETDM_SYNC_FROM_OUT3:
+ return MT8195_AFE_IO_ETDM3_OUT;
+ default:
+ return 0;
+ }
+}
+
+static int get_etdm_cowork_master_id(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ if (!mt8195_afe_etdm_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+ dai_id = etdm_data->cowork_source_id;
+
+ if (dai_id == COWORK_ETDM_NONE)
+ dai_id = dai->id;
+
+ return dai_id;
+}
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o048_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN48, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN48, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN48_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN48_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o049_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN49, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN49, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN49_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN49_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o050_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN50, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN50_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o051_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN51, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN51_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o052_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN52, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN52_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o053_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN53, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN53_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o054_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN54, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN54_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o055_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN55, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN55_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o056_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN56, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN56_1, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o057_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN57, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN57_1, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o058_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN58_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN58_1, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o059_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN59_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN59_1, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o060_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN60_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN60_1, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o061_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN61_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN61_1, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o062_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN62_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN62_1, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o063_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN63_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN63_1, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o064_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I038 Switch", AFE_CONN64_1, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I062 Switch", AFE_CONN64_1, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o065_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I039 Switch", AFE_CONN65_1, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I063 Switch", AFE_CONN65_1, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o066_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I040 Switch", AFE_CONN66_1, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I064 Switch", AFE_CONN66_2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o067_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I041 Switch", AFE_CONN67_1, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I065 Switch", AFE_CONN67_2, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o068_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I042 Switch", AFE_CONN68_1, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I066 Switch", AFE_CONN68_2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o069_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I043 Switch", AFE_CONN69_1, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I067 Switch", AFE_CONN69_2, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o070_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I044 Switch", AFE_CONN70_1, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I068 Switch", AFE_CONN70_2, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o071_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I045 Switch", AFE_CONN71_1, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I069 Switch", AFE_CONN71_2, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o072_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN72, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN72, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN72_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN72_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o073_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN73, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN73, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN73_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN73_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o074_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN74, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN74_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o075_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN75, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN75_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o076_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN76, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN76_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o077_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN77, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN77_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o078_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN78, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN78_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o079_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN79, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN79_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o080_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN80, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN80_1, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o081_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN81, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN81_1, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o082_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN82_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN82_1, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o083_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN83_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN83_1, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o084_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN84_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN84_1, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o085_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN85_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN85_1, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o086_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN86_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN86_1, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o087_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN87_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN87_1, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o088_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I038 Switch", AFE_CONN88_1, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I062 Switch", AFE_CONN88_1, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o089_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I039 Switch", AFE_CONN89_1, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I063 Switch", AFE_CONN89_1, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o090_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I040 Switch", AFE_CONN90_1, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I064 Switch", AFE_CONN90_2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o091_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I041 Switch", AFE_CONN91_1, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I065 Switch", AFE_CONN91_2, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o092_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I042 Switch", AFE_CONN92_1, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I066 Switch", AFE_CONN92_2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o093_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I043 Switch", AFE_CONN93_1, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I067 Switch", AFE_CONN93_2, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o094_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I044 Switch", AFE_CONN94_1, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I068 Switch", AFE_CONN94_2, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o095_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I045 Switch", AFE_CONN95_1, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I069 Switch", AFE_CONN95_2, 5, 1, 0),
+};
+
+static const char * const mt8195_etdm_clk_src_sel_text[] = {
+ "26m",
+ "a1sys_a2sys",
+ "a3sys",
+ "a4sys",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(etdmout_clk_src_enum,
+ mt8195_etdm_clk_src_sel_text);
+
+static const char * const hdmitx_dptx_mux_map[] = {
+ "Disconnect", "Connect",
+};
+
+static int hdmitx_dptx_mux_map_value[] = {
+ 0, 1,
+};
+
+/* HDMI_OUT_MUX */
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ hdmitx_dptx_mux_map,
+ hdmitx_dptx_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_out_mux_control =
+ SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum);
+
+/* DPTX_OUT_MUX */
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ hdmitx_dptx_mux_map,
+ hdmitx_dptx_mux_map_value);
+
+static const struct snd_kcontrol_new dptx_out_mux_control =
+ SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum);
+
+/* HDMI_CH0_MUX ~ HDMI_CH7_MUX */
+static const char *const afe_conn_hdmi_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7",
+};
+
+static int afe_conn_hdmi_mux_map_value[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 0,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch0_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 4,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch1_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 8,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch2_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 12,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch3_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 16,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch4_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 20,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch5_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 24,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch6_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 28,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch7_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
+
+static int mt8195_etdm_clk_src_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int source = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ unsigned int mask;
+ unsigned int reg;
+
+ if (source >= e->items)
+ return -EINVAL;
+
+ reg = 0;
+ if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) {
+ reg = ETDM_OUT1_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ val = ETDM_OUT_CON4_CLOCK(source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) {
+ reg = ETDM_OUT2_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ val = ETDM_OUT_CON4_CLOCK(source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) {
+ reg = ETDM_OUT3_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ val = ETDM_OUT_CON4_CLOCK(source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) {
+ reg = ETDM_IN1_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ val = ETDM_IN_CON2_CLOCK(source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) {
+ reg = ETDM_IN2_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ val = ETDM_IN_CON2_CLOCK(source);
+ }
+
+ if (reg)
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int mt8195_etdm_clk_src_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int value = 0;
+ unsigned int reg = 0;
+ unsigned int mask = 0;
+ unsigned int shift = 0;
+
+ if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) {
+ reg = ETDM_OUT1_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) {
+ reg = ETDM_OUT2_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) {
+ reg = ETDM_OUT3_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) {
+ reg = ETDM_IN1_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) {
+ reg = ETDM_IN2_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ }
+
+ if (reg)
+ regmap_read(afe->regmap, reg, &value);
+
+ value &= mask;
+ value >>= shift;
+ ucontrol->value.enumerated.item[0] = value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_etdm_controls[] = {
+ SOC_ENUM_EXT("ETDM_OUT1_Clock_Source",
+ etdmout_clk_src_enum,
+ mt8195_etdm_clk_src_sel_get,
+ mt8195_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_OUT2_Clock_Source",
+ etdmout_clk_src_enum,
+ mt8195_etdm_clk_src_sel_get,
+ mt8195_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_OUT3_Clock_Source",
+ etdmout_clk_src_enum,
+ mt8195_etdm_clk_src_sel_get,
+ mt8195_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_IN1_Clock_Source",
+ etdmout_clk_src_enum,
+ mt8195_etdm_clk_src_sel_get,
+ mt8195_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_IN2_Clock_Source",
+ etdmout_clk_src_enum,
+ mt8195_etdm_clk_src_sel_get,
+ mt8195_etdm_clk_src_sel_put),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
+ /* eTDM_IN2 */
+ SND_SOC_DAPM_MIXER("I012", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I013", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I014", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I015", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I016", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I017", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I018", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I019", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* eTDM_IN1 */
+ SND_SOC_DAPM_MIXER("I072", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I073", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I074", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I075", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I076", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I077", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I078", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I079", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I080", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I081", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I082", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I083", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I084", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I085", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I086", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I087", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I088", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I089", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I090", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I091", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I092", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I093", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I094", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I095", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* eTDM_OUT2 */
+ SND_SOC_DAPM_MIXER("O048", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o048_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o048_mix)),
+ SND_SOC_DAPM_MIXER("O049", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o049_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o049_mix)),
+ SND_SOC_DAPM_MIXER("O050", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o050_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o050_mix)),
+ SND_SOC_DAPM_MIXER("O051", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o051_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o051_mix)),
+ SND_SOC_DAPM_MIXER("O052", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o052_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o052_mix)),
+ SND_SOC_DAPM_MIXER("O053", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o053_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o053_mix)),
+ SND_SOC_DAPM_MIXER("O054", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o054_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o054_mix)),
+ SND_SOC_DAPM_MIXER("O055", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o055_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o055_mix)),
+ SND_SOC_DAPM_MIXER("O056", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o056_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o056_mix)),
+ SND_SOC_DAPM_MIXER("O057", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o057_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o057_mix)),
+ SND_SOC_DAPM_MIXER("O058", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o058_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o058_mix)),
+ SND_SOC_DAPM_MIXER("O059", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o059_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o059_mix)),
+ SND_SOC_DAPM_MIXER("O060", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o060_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o060_mix)),
+ SND_SOC_DAPM_MIXER("O061", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o061_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o061_mix)),
+ SND_SOC_DAPM_MIXER("O062", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o062_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o062_mix)),
+ SND_SOC_DAPM_MIXER("O063", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o063_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o063_mix)),
+ SND_SOC_DAPM_MIXER("O064", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o064_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o064_mix)),
+ SND_SOC_DAPM_MIXER("O065", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o065_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o065_mix)),
+ SND_SOC_DAPM_MIXER("O066", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o066_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o066_mix)),
+ SND_SOC_DAPM_MIXER("O067", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o067_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o067_mix)),
+ SND_SOC_DAPM_MIXER("O068", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o068_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o068_mix)),
+ SND_SOC_DAPM_MIXER("O069", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o069_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o069_mix)),
+ SND_SOC_DAPM_MIXER("O070", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o070_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o070_mix)),
+ SND_SOC_DAPM_MIXER("O071", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o071_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o071_mix)),
+
+ /* eTDM_OUT1 */
+ SND_SOC_DAPM_MIXER("O072", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o072_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o072_mix)),
+ SND_SOC_DAPM_MIXER("O073", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o073_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o073_mix)),
+ SND_SOC_DAPM_MIXER("O074", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o074_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o074_mix)),
+ SND_SOC_DAPM_MIXER("O075", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o075_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o075_mix)),
+ SND_SOC_DAPM_MIXER("O076", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o076_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o076_mix)),
+ SND_SOC_DAPM_MIXER("O077", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o077_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o077_mix)),
+ SND_SOC_DAPM_MIXER("O078", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o078_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o078_mix)),
+ SND_SOC_DAPM_MIXER("O079", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o079_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o079_mix)),
+ SND_SOC_DAPM_MIXER("O080", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o080_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o080_mix)),
+ SND_SOC_DAPM_MIXER("O081", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o081_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o081_mix)),
+ SND_SOC_DAPM_MIXER("O082", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o082_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o082_mix)),
+ SND_SOC_DAPM_MIXER("O083", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o083_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o083_mix)),
+ SND_SOC_DAPM_MIXER("O084", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o084_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o084_mix)),
+ SND_SOC_DAPM_MIXER("O085", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o085_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o085_mix)),
+ SND_SOC_DAPM_MIXER("O086", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o086_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o086_mix)),
+ SND_SOC_DAPM_MIXER("O087", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o087_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o087_mix)),
+ SND_SOC_DAPM_MIXER("O088", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o088_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o088_mix)),
+ SND_SOC_DAPM_MIXER("O089", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o089_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o089_mix)),
+ SND_SOC_DAPM_MIXER("O090", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o090_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o090_mix)),
+ SND_SOC_DAPM_MIXER("O091", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o091_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o091_mix)),
+ SND_SOC_DAPM_MIXER("O092", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o092_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o092_mix)),
+ SND_SOC_DAPM_MIXER("O093", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o093_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o093_mix)),
+ SND_SOC_DAPM_MIXER("O094", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o094_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o094_mix)),
+ SND_SOC_DAPM_MIXER("O095", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o095_mix,
+ ARRAY_SIZE(mtk_dai_etdm_o095_mix)),
+
+ /* eTDM_OUT3 */
+ SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_out_mux_control),
+ SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &dptx_out_mux_control),
+
+ SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch0_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch1_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch2_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch3_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch4_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch5_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch6_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch7_mux_control),
+
+ SND_SOC_DAPM_INPUT("ETDM_INPUT"),
+ SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
+ {"I012", NULL, "ETDM2 Capture"},
+ {"I013", NULL, "ETDM2 Capture"},
+ {"I014", NULL, "ETDM2 Capture"},
+ {"I015", NULL, "ETDM2 Capture"},
+ {"I016", NULL, "ETDM2 Capture"},
+ {"I017", NULL, "ETDM2 Capture"},
+ {"I018", NULL, "ETDM2 Capture"},
+ {"I019", NULL, "ETDM2 Capture"},
+
+ {"I072", NULL, "ETDM1 Capture"},
+ {"I073", NULL, "ETDM1 Capture"},
+ {"I074", NULL, "ETDM1 Capture"},
+ {"I075", NULL, "ETDM1 Capture"},
+ {"I076", NULL, "ETDM1 Capture"},
+ {"I077", NULL, "ETDM1 Capture"},
+ {"I078", NULL, "ETDM1 Capture"},
+ {"I079", NULL, "ETDM1 Capture"},
+ {"I080", NULL, "ETDM1 Capture"},
+ {"I081", NULL, "ETDM1 Capture"},
+ {"I082", NULL, "ETDM1 Capture"},
+ {"I083", NULL, "ETDM1 Capture"},
+ {"I084", NULL, "ETDM1 Capture"},
+ {"I085", NULL, "ETDM1 Capture"},
+ {"I086", NULL, "ETDM1 Capture"},
+ {"I087", NULL, "ETDM1 Capture"},
+ {"I088", NULL, "ETDM1 Capture"},
+ {"I089", NULL, "ETDM1 Capture"},
+ {"I090", NULL, "ETDM1 Capture"},
+ {"I091", NULL, "ETDM1 Capture"},
+ {"I092", NULL, "ETDM1 Capture"},
+ {"I093", NULL, "ETDM1 Capture"},
+ {"I094", NULL, "ETDM1 Capture"},
+ {"I095", NULL, "ETDM1 Capture"},
+
+ {"UL8", NULL, "ETDM1 Capture"},
+ {"UL3", NULL, "ETDM2 Capture"},
+
+ {"ETDM2 Playback", NULL, "O048"},
+ {"ETDM2 Playback", NULL, "O049"},
+ {"ETDM2 Playback", NULL, "O050"},
+ {"ETDM2 Playback", NULL, "O051"},
+ {"ETDM2 Playback", NULL, "O052"},
+ {"ETDM2 Playback", NULL, "O053"},
+ {"ETDM2 Playback", NULL, "O054"},
+ {"ETDM2 Playback", NULL, "O055"},
+ {"ETDM2 Playback", NULL, "O056"},
+ {"ETDM2 Playback", NULL, "O057"},
+ {"ETDM2 Playback", NULL, "O058"},
+ {"ETDM2 Playback", NULL, "O059"},
+ {"ETDM2 Playback", NULL, "O060"},
+ {"ETDM2 Playback", NULL, "O061"},
+ {"ETDM2 Playback", NULL, "O062"},
+ {"ETDM2 Playback", NULL, "O063"},
+ {"ETDM2 Playback", NULL, "O064"},
+ {"ETDM2 Playback", NULL, "O065"},
+ {"ETDM2 Playback", NULL, "O066"},
+ {"ETDM2 Playback", NULL, "O067"},
+ {"ETDM2 Playback", NULL, "O068"},
+ {"ETDM2 Playback", NULL, "O069"},
+ {"ETDM2 Playback", NULL, "O070"},
+ {"ETDM2 Playback", NULL, "O071"},
+
+ {"ETDM1 Playback", NULL, "O072"},
+ {"ETDM1 Playback", NULL, "O073"},
+ {"ETDM1 Playback", NULL, "O074"},
+ {"ETDM1 Playback", NULL, "O075"},
+ {"ETDM1 Playback", NULL, "O076"},
+ {"ETDM1 Playback", NULL, "O077"},
+ {"ETDM1 Playback", NULL, "O078"},
+ {"ETDM1 Playback", NULL, "O079"},
+ {"ETDM1 Playback", NULL, "O080"},
+ {"ETDM1 Playback", NULL, "O081"},
+ {"ETDM1 Playback", NULL, "O082"},
+ {"ETDM1 Playback", NULL, "O083"},
+ {"ETDM1 Playback", NULL, "O084"},
+ {"ETDM1 Playback", NULL, "O085"},
+ {"ETDM1 Playback", NULL, "O086"},
+ {"ETDM1 Playback", NULL, "O087"},
+ {"ETDM1 Playback", NULL, "O088"},
+ {"ETDM1 Playback", NULL, "O089"},
+ {"ETDM1 Playback", NULL, "O090"},
+ {"ETDM1 Playback", NULL, "O091"},
+ {"ETDM1 Playback", NULL, "O092"},
+ {"ETDM1 Playback", NULL, "O093"},
+ {"ETDM1 Playback", NULL, "O094"},
+ {"ETDM1 Playback", NULL, "O095"},
+
+ {"O048", "I020 Switch", "I020"},
+ {"O049", "I021 Switch", "I021"},
+
+ {"O048", "I022 Switch", "I022"},
+ {"O049", "I023 Switch", "I023"},
+ {"O050", "I024 Switch", "I024"},
+ {"O051", "I025 Switch", "I025"},
+ {"O052", "I026 Switch", "I026"},
+ {"O053", "I027 Switch", "I027"},
+ {"O054", "I028 Switch", "I028"},
+ {"O055", "I029 Switch", "I029"},
+ {"O056", "I030 Switch", "I030"},
+ {"O057", "I031 Switch", "I031"},
+ {"O058", "I032 Switch", "I032"},
+ {"O059", "I033 Switch", "I033"},
+ {"O060", "I034 Switch", "I034"},
+ {"O061", "I035 Switch", "I035"},
+ {"O062", "I036 Switch", "I036"},
+ {"O063", "I037 Switch", "I037"},
+ {"O064", "I038 Switch", "I038"},
+ {"O065", "I039 Switch", "I039"},
+ {"O066", "I040 Switch", "I040"},
+ {"O067", "I041 Switch", "I041"},
+ {"O068", "I042 Switch", "I042"},
+ {"O069", "I043 Switch", "I043"},
+ {"O070", "I044 Switch", "I044"},
+ {"O071", "I045 Switch", "I045"},
+
+ {"O048", "I046 Switch", "I046"},
+ {"O049", "I047 Switch", "I047"},
+ {"O050", "I048 Switch", "I048"},
+ {"O051", "I049 Switch", "I049"},
+ {"O052", "I050 Switch", "I050"},
+ {"O053", "I051 Switch", "I051"},
+ {"O054", "I052 Switch", "I052"},
+ {"O055", "I053 Switch", "I053"},
+ {"O056", "I054 Switch", "I054"},
+ {"O057", "I055 Switch", "I055"},
+ {"O058", "I056 Switch", "I056"},
+ {"O059", "I057 Switch", "I057"},
+ {"O060", "I058 Switch", "I058"},
+ {"O061", "I059 Switch", "I059"},
+ {"O062", "I060 Switch", "I060"},
+ {"O063", "I061 Switch", "I061"},
+ {"O064", "I062 Switch", "I062"},
+ {"O065", "I063 Switch", "I063"},
+ {"O066", "I064 Switch", "I064"},
+ {"O067", "I065 Switch", "I065"},
+ {"O068", "I066 Switch", "I066"},
+ {"O069", "I067 Switch", "I067"},
+ {"O070", "I068 Switch", "I068"},
+ {"O071", "I069 Switch", "I069"},
+
+ {"O048", "I070 Switch", "I070"},
+ {"O049", "I071 Switch", "I071"},
+
+ {"O072", "I020 Switch", "I020"},
+ {"O073", "I021 Switch", "I021"},
+
+ {"O072", "I022 Switch", "I022"},
+ {"O073", "I023 Switch", "I023"},
+ {"O074", "I024 Switch", "I024"},
+ {"O075", "I025 Switch", "I025"},
+ {"O076", "I026 Switch", "I026"},
+ {"O077", "I027 Switch", "I027"},
+ {"O078", "I028 Switch", "I028"},
+ {"O079", "I029 Switch", "I029"},
+ {"O080", "I030 Switch", "I030"},
+ {"O081", "I031 Switch", "I031"},
+ {"O082", "I032 Switch", "I032"},
+ {"O083", "I033 Switch", "I033"},
+ {"O084", "I034 Switch", "I034"},
+ {"O085", "I035 Switch", "I035"},
+ {"O086", "I036 Switch", "I036"},
+ {"O087", "I037 Switch", "I037"},
+ {"O088", "I038 Switch", "I038"},
+ {"O089", "I039 Switch", "I039"},
+ {"O090", "I040 Switch", "I040"},
+ {"O091", "I041 Switch", "I041"},
+ {"O092", "I042 Switch", "I042"},
+ {"O093", "I043 Switch", "I043"},
+ {"O094", "I044 Switch", "I044"},
+ {"O095", "I045 Switch", "I045"},
+
+ {"O072", "I046 Switch", "I046"},
+ {"O073", "I047 Switch", "I047"},
+ {"O074", "I048 Switch", "I048"},
+ {"O075", "I049 Switch", "I049"},
+ {"O076", "I050 Switch", "I050"},
+ {"O077", "I051 Switch", "I051"},
+ {"O078", "I052 Switch", "I052"},
+ {"O079", "I053 Switch", "I053"},
+ {"O080", "I054 Switch", "I054"},
+ {"O081", "I055 Switch", "I055"},
+ {"O082", "I056 Switch", "I056"},
+ {"O083", "I057 Switch", "I057"},
+ {"O084", "I058 Switch", "I058"},
+ {"O085", "I059 Switch", "I059"},
+ {"O086", "I060 Switch", "I060"},
+ {"O087", "I061 Switch", "I061"},
+ {"O088", "I062 Switch", "I062"},
+ {"O089", "I063 Switch", "I063"},
+ {"O090", "I064 Switch", "I064"},
+ {"O091", "I065 Switch", "I065"},
+ {"O092", "I066 Switch", "I066"},
+ {"O093", "I067 Switch", "I067"},
+ {"O094", "I068 Switch", "I068"},
+ {"O095", "I069 Switch", "I069"},
+
+ {"O072", "I070 Switch", "I070"},
+ {"O073", "I071 Switch", "I071"},
+
+ {"HDMI_CH0_MUX", "CH0", "DL10"},
+ {"HDMI_CH0_MUX", "CH1", "DL10"},
+ {"HDMI_CH0_MUX", "CH2", "DL10"},
+ {"HDMI_CH0_MUX", "CH3", "DL10"},
+ {"HDMI_CH0_MUX", "CH4", "DL10"},
+ {"HDMI_CH0_MUX", "CH5", "DL10"},
+ {"HDMI_CH0_MUX", "CH6", "DL10"},
+ {"HDMI_CH0_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH1_MUX", "CH0", "DL10"},
+ {"HDMI_CH1_MUX", "CH1", "DL10"},
+ {"HDMI_CH1_MUX", "CH2", "DL10"},
+ {"HDMI_CH1_MUX", "CH3", "DL10"},
+ {"HDMI_CH1_MUX", "CH4", "DL10"},
+ {"HDMI_CH1_MUX", "CH5", "DL10"},
+ {"HDMI_CH1_MUX", "CH6", "DL10"},
+ {"HDMI_CH1_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH2_MUX", "CH0", "DL10"},
+ {"HDMI_CH2_MUX", "CH1", "DL10"},
+ {"HDMI_CH2_MUX", "CH2", "DL10"},
+ {"HDMI_CH2_MUX", "CH3", "DL10"},
+ {"HDMI_CH2_MUX", "CH4", "DL10"},
+ {"HDMI_CH2_MUX", "CH5", "DL10"},
+ {"HDMI_CH2_MUX", "CH6", "DL10"},
+ {"HDMI_CH2_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH3_MUX", "CH0", "DL10"},
+ {"HDMI_CH3_MUX", "CH1", "DL10"},
+ {"HDMI_CH3_MUX", "CH2", "DL10"},
+ {"HDMI_CH3_MUX", "CH3", "DL10"},
+ {"HDMI_CH3_MUX", "CH4", "DL10"},
+ {"HDMI_CH3_MUX", "CH5", "DL10"},
+ {"HDMI_CH3_MUX", "CH6", "DL10"},
+ {"HDMI_CH3_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH4_MUX", "CH0", "DL10"},
+ {"HDMI_CH4_MUX", "CH1", "DL10"},
+ {"HDMI_CH4_MUX", "CH2", "DL10"},
+ {"HDMI_CH4_MUX", "CH3", "DL10"},
+ {"HDMI_CH4_MUX", "CH4", "DL10"},
+ {"HDMI_CH4_MUX", "CH5", "DL10"},
+ {"HDMI_CH4_MUX", "CH6", "DL10"},
+ {"HDMI_CH4_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH5_MUX", "CH0", "DL10"},
+ {"HDMI_CH5_MUX", "CH1", "DL10"},
+ {"HDMI_CH5_MUX", "CH2", "DL10"},
+ {"HDMI_CH5_MUX", "CH3", "DL10"},
+ {"HDMI_CH5_MUX", "CH4", "DL10"},
+ {"HDMI_CH5_MUX", "CH5", "DL10"},
+ {"HDMI_CH5_MUX", "CH6", "DL10"},
+ {"HDMI_CH5_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH6_MUX", "CH0", "DL10"},
+ {"HDMI_CH6_MUX", "CH1", "DL10"},
+ {"HDMI_CH6_MUX", "CH2", "DL10"},
+ {"HDMI_CH6_MUX", "CH3", "DL10"},
+ {"HDMI_CH6_MUX", "CH4", "DL10"},
+ {"HDMI_CH6_MUX", "CH5", "DL10"},
+ {"HDMI_CH6_MUX", "CH6", "DL10"},
+ {"HDMI_CH6_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH7_MUX", "CH0", "DL10"},
+ {"HDMI_CH7_MUX", "CH1", "DL10"},
+ {"HDMI_CH7_MUX", "CH2", "DL10"},
+ {"HDMI_CH7_MUX", "CH3", "DL10"},
+ {"HDMI_CH7_MUX", "CH4", "DL10"},
+ {"HDMI_CH7_MUX", "CH5", "DL10"},
+ {"HDMI_CH7_MUX", "CH6", "DL10"},
+ {"HDMI_CH7_MUX", "CH7", "DL10"},
+
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"ETDM3 Playback", NULL, "HDMI_OUT_MUX"},
+ {"DPTX Playback", NULL, "DPTX_OUT_MUX"},
+
+ {"ETDM_OUTPUT", NULL, "DPTX Playback"},
+ {"ETDM_OUTPUT", NULL, "ETDM1 Playback"},
+ {"ETDM_OUTPUT", NULL, "ETDM2 Playback"},
+ {"ETDM_OUTPUT", NULL, "ETDM3 Playback"},
+ {"ETDM1 Capture", NULL, "ETDM_INPUT"},
+ {"ETDM2 Capture", NULL, "ETDM_INPUT"},
+};
+
+static int mt8195_afe_enable_etdm(struct mtk_base_afe *afe, int dai_id)
+{
+ int ret = 0;
+ struct etdm_con_reg etdm_reg;
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned long flags;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+ etdm_data->en_ref_cnt++;
+ if (etdm_data->en_ref_cnt == 1) {
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ goto out;
+
+ regmap_update_bits(afe->regmap, etdm_reg.con0,
+ ETDM_CON0_EN, ETDM_CON0_EN);
+ }
+out:
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+ return ret;
+}
+
+static int mt8195_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id)
+{
+ int ret = 0;
+ struct etdm_con_reg etdm_reg;
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned long flags;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+ if (etdm_data->en_ref_cnt > 0) {
+ etdm_data->en_ref_cnt--;
+ if (etdm_data->en_ref_cnt == 0) {
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ goto out;
+
+ regmap_update_bits(afe->regmap, etdm_reg.con0,
+ ETDM_CON0_EN, 0);
+ }
+ }
+out:
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+ return ret;
+}
+
+static int etdm_cowork_slv_sel(int id, int slave_mode)
+{
+ if (slave_mode) {
+ switch (id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ return COWORK_ETDM_IN1_S;
+ case MT8195_AFE_IO_ETDM2_IN:
+ return COWORK_ETDM_IN2_S;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ return COWORK_ETDM_OUT1_S;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ return COWORK_ETDM_OUT2_S;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ return COWORK_ETDM_OUT3_S;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ return COWORK_ETDM_IN1_M;
+ case MT8195_AFE_IO_ETDM2_IN:
+ return COWORK_ETDM_IN2_M;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ return COWORK_ETDM_OUT1_M;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ return COWORK_ETDM_OUT2_M;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ return COWORK_ETDM_OUT3_M;
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+static int mt8195_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned int reg = 0;
+ unsigned int mask;
+ unsigned int val;
+ int cowork_source_sel;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ if (etdm_data->cowork_source_id == COWORK_ETDM_NONE)
+ return 0;
+
+ cowork_source_sel = etdm_cowork_slv_sel(etdm_data->cowork_source_id,
+ etdm_data->slave_mode);
+ if (cowork_source_sel < 0)
+ return cowork_source_sel;
+
+ switch (dai_id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ reg = ETDM_COWORK_CON1;
+ mask = ETDM_IN1_SLAVE_SEL_MASK;
+ val = ETDM_IN1_SLAVE_SEL(cowork_source_sel);
+ break;
+ case MT8195_AFE_IO_ETDM2_IN:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_IN2_SLAVE_SEL_MASK;
+ val = ETDM_IN2_SLAVE_SEL(cowork_source_sel);
+ break;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ reg = ETDM_COWORK_CON0;
+ mask = ETDM_OUT1_SLAVE_SEL_MASK;
+ val = ETDM_OUT1_SLAVE_SEL(cowork_source_sel);
+ break;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT2_SLAVE_SEL_MASK;
+ val = ETDM_OUT2_SLAVE_SEL(cowork_source_sel);
+ break;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT3_SLAVE_SEL_MASK;
+ val = ETDM_OUT3_SLAVE_SEL(cowork_source_sel);
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_get_cg_id_by_dai_id(int dai_id)
+{
+ int cg_id = -1;
+
+ switch (dai_id) {
+ case MT8195_AFE_IO_DPTX:
+ cg_id = MT8195_CLK_AUD_HDMI_OUT;
+ break;
+ case MT8195_AFE_IO_ETDM1_IN:
+ cg_id = MT8195_CLK_AUD_TDM_IN;
+ break;
+ case MT8195_AFE_IO_ETDM2_IN:
+ cg_id = MT8195_CLK_AUD_I2SIN;
+ break;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ cg_id = MT8195_CLK_AUD_TDM_OUT;
+ break;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ cg_id = MT8195_CLK_AUD_I2S_OUT;
+ break;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ cg_id = MT8195_CLK_AUD_HDMI_OUT;
+ break;
+ default:
+ break;
+ }
+
+ return cg_id;
+}
+
+static int mtk_dai_etdm_get_clk_id_by_dai_id(int dai_id)
+{
+ int clk_id = -1;
+
+ switch (dai_id) {
+ case MT8195_AFE_IO_DPTX:
+ clk_id = MT8195_CLK_TOP_DPTX_M_SEL;
+ break;
+ case MT8195_AFE_IO_ETDM1_IN:
+ clk_id = MT8195_CLK_TOP_I2SI1_M_SEL;
+ break;
+ case MT8195_AFE_IO_ETDM2_IN:
+ clk_id = MT8195_CLK_TOP_I2SI2_M_SEL;
+ break;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ clk_id = MT8195_CLK_TOP_I2SO1_M_SEL;
+ break;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ clk_id = MT8195_CLK_TOP_I2SO2_M_SEL;
+ break;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ default:
+ break;
+ }
+
+ return clk_id;
+}
+
+static int mtk_dai_etdm_get_clkdiv_id_by_dai_id(int dai_id)
+{
+ int clk_id = -1;
+
+ switch (dai_id) {
+ case MT8195_AFE_IO_DPTX:
+ clk_id = MT8195_CLK_TOP_APLL12_DIV9;
+ break;
+ case MT8195_AFE_IO_ETDM1_IN:
+ clk_id = MT8195_CLK_TOP_APLL12_DIV0;
+ break;
+ case MT8195_AFE_IO_ETDM2_IN:
+ clk_id = MT8195_CLK_TOP_APLL12_DIV1;
+ break;
+ case MT8195_AFE_IO_ETDM1_OUT:
+ clk_id = MT8195_CLK_TOP_APLL12_DIV2;
+ break;
+ case MT8195_AFE_IO_ETDM2_OUT:
+ clk_id = MT8195_CLK_TOP_APLL12_DIV3;
+ break;
+ case MT8195_AFE_IO_ETDM3_OUT:
+ default:
+ break;
+ }
+
+ return clk_id;
+}
+
+static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+
+ if (clkdiv_id < 0)
+ return -EINVAL;
+
+ mt8195_afe_enable_clk(afe, afe_priv->clk[clkdiv_id]);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_disable_mclk(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+
+ if (clkdiv_id < 0)
+ return -EINVAL;
+
+ mt8195_afe_disable_clk(afe, afe_priv->clk[clkdiv_id]);
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int cg_id;
+ int mst_dai_id;
+ int slv_dai_id;
+ int i;
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+ return -EINVAL;
+
+ mtk_dai_etdm_enable_mclk(afe, mst_dai_id);
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
+ if (cg_id >= 0)
+ mt8195_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id);
+ if (cg_id >= 0)
+ mt8195_afe_enable_clk(afe,
+ afe_priv->clk[cg_id]);
+ }
+ } else {
+ mtk_dai_etdm_enable_mclk(afe, dai->id);
+
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+ if (cg_id >= 0)
+ mt8195_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+ }
+
+ return 0;
+}
+
+static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int cg_id;
+ int mst_dai_id;
+ int slv_dai_id;
+ int i;
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+ return;
+
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
+ if (cg_id >= 0)
+ mt8195_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id);
+ if (cg_id >= 0)
+ mt8195_afe_disable_clk(afe,
+ afe_priv->clk[cg_id]);
+ }
+ mtk_dai_etdm_disable_mclk(afe, mst_dai_id);
+ } else {
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+ if (cg_id >= 0)
+ mt8195_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+
+ mtk_dai_etdm_disable_mclk(afe, dai->id);
+ }
+}
+
+static int mtk_dai_etdm_fifo_mode(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ unsigned int mode = 0;
+ unsigned int reg = 0;
+ unsigned int val = 0;
+ unsigned int mask = (ETDM_IN_AFIFO_MODE_MASK | ETDM_IN_USE_AFIFO);
+
+ if (rate != 0)
+ mode = mt8195_afe_fs_timing(rate);
+
+ switch (dai_id) {
+ case MT8195_AFE_IO_ETDM1_IN:
+ reg = ETDM_IN1_AFIFO_CON;
+ if (rate == 0)
+ mode = MT8195_ETDM_IN1_1X_EN;
+ break;
+ case MT8195_AFE_IO_ETDM2_IN:
+ reg = ETDM_IN2_AFIFO_CON;
+ if (rate == 0)
+ mode = MT8195_ETDM_IN2_1X_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (mode | ETDM_IN_USE_AFIFO);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ return 0;
+}
+
+static int mtk_dai_etdm_in_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int data_mode;
+ unsigned int lrck_width;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int i;
+ int ret;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ data_mode = etdm_data->data_mode;
+ lrck_width = etdm_data->lrck_width;
+
+ dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
+ __func__, rate, channels, dai_id);
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ if (etdm_data->cowork_source_id != COWORK_ETDM_NONE)
+ slave_mode = true;
+
+ /* afifo */
+ if (slave_mode)
+ mtk_dai_etdm_fifo_mode(afe, dai_id, 0);
+ else
+ mtk_dai_etdm_fifo_mode(afe, dai_id, rate);
+
+ /* con1 */
+ if (lrck_width > 0) {
+ mask |= (ETDM_IN_CON1_LRCK_AUTO_MODE |
+ ETDM_IN_CON1_LRCK_WIDTH_MASK);
+ val |= ETDM_IN_CON1_LRCK_WIDTH(lrck_width);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con2 */
+ if (!slave_mode) {
+ mask |= ETDM_IN_CON2_UPDATE_GAP_MASK;
+ if (rate == 352800 || rate == 384000)
+ val |= ETDM_IN_CON2_UPDATE_GAP(4);
+ else
+ val |= ETDM_IN_CON2_UPDATE_GAP(3);
+ }
+ mask |= (ETDM_IN_CON2_MULTI_IP_2CH_MODE |
+ ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK);
+ if (data_mode == MTK_DAI_ETDM_DATA_MULTI_PIN) {
+ val |= ETDM_IN_CON2_MULTI_IP_2CH_MODE |
+ ETDM_IN_CON2_MULTI_IP_TOTAL_CH(channels);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con2, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con3 */
+ mask |= ETDM_IN_CON3_DISABLE_OUT_MASK;
+ for (i = 0; i < channels; i += 2) {
+ if (etdm_data->in_disable_ch[i] &&
+ etdm_data->in_disable_ch[i + 1])
+ val |= ETDM_IN_CON3_DISABLE_OUT(i >> 1);
+ }
+ if (!slave_mode) {
+ mask |= ETDM_IN_CON3_FS_MASK;
+ val |= ETDM_IN_CON3_FS(get_etdm_fs_timing(rate));
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con3, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con4 */
+ mask |= (ETDM_IN_CON4_MASTER_LRCK_INV | ETDM_IN_CON4_MASTER_BCK_INV |
+ ETDM_IN_CON4_SLAVE_LRCK_INV | ETDM_IN_CON4_SLAVE_BCK_INV);
+ if (slave_mode) {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_IN_CON4_SLAVE_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_IN_CON4_SLAVE_BCK_INV;
+ } else {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_IN_CON4_MASTER_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_IN_CON4_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con5 */
+ mask |= ETDM_IN_CON5_LR_SWAP_MASK;
+ mask |= ETDM_IN_CON5_ENABLE_ODD_MASK;
+ for (i = 0; i < channels; i += 2) {
+ if (etdm_data->in_disable_ch[i] &&
+ !etdm_data->in_disable_ch[i + 1]) {
+ if (i == (channels - 2))
+ val |= ETDM_IN_CON5_LR_SWAP(15);
+ else
+ val |= ETDM_IN_CON5_LR_SWAP(i >> 1);
+ val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1);
+ } else if (!etdm_data->in_disable_ch[i] &&
+ etdm_data->in_disable_ch[i + 1]) {
+ val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1);
+ }
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val);
+ return 0;
+}
+
+static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int lrck_width;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int ret;
+ int fs = 0;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ lrck_width = etdm_data->lrck_width;
+
+ dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
+ __func__, rate, channels, dai_id);
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ if (etdm_data->cowork_source_id != COWORK_ETDM_NONE)
+ slave_mode = true;
+
+ /* con0 */
+ mask = ETDM_OUT_CON0_RELATCH_DOMAIN_MASK;
+ val = ETDM_OUT_CON0_RELATCH_DOMAIN(ETDM_RELATCH_TIMING_A1A2SYS);
+ regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con1 */
+ if (lrck_width > 0) {
+ mask |= (ETDM_OUT_CON1_LRCK_AUTO_MODE |
+ ETDM_OUT_CON1_LRCK_WIDTH_MASK);
+ val |= ETDM_OUT_CON1_LRCK_WIDTH(lrck_width);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ if (slave_mode) {
+ /* con2 */
+ mask = (ETDM_OUT_CON2_LRCK_DELAY_BCK_INV |
+ ETDM_OUT_CON2_LRCK_DELAY_0P5T_EN);
+ val = (ETDM_OUT_CON2_LRCK_DELAY_BCK_INV |
+ ETDM_OUT_CON2_LRCK_DELAY_0P5T_EN);
+ regmap_update_bits(afe->regmap, etdm_reg.con2,
+ mask, val);
+ mask = 0;
+ val = 0;
+ } else {
+ /* con4 */
+ mask |= ETDM_OUT_CON4_FS_MASK;
+ val |= ETDM_OUT_CON4_FS(get_etdm_fs_timing(rate));
+ }
+
+ mask |= ETDM_OUT_CON4_RELATCH_EN_MASK;
+ if (dai_id == MT8195_AFE_IO_ETDM1_OUT)
+ fs = MT8195_ETDM_OUT1_1X_EN;
+ else if (dai_id == MT8195_AFE_IO_ETDM2_OUT)
+ fs = MT8195_ETDM_OUT2_1X_EN;
+
+ val |= ETDM_OUT_CON4_RELATCH_EN(fs);
+
+ regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con5 */
+ mask |= (ETDM_OUT_CON5_MASTER_LRCK_INV | ETDM_OUT_CON5_MASTER_BCK_INV |
+ ETDM_OUT_CON5_SLAVE_LRCK_INV | ETDM_OUT_CON5_SLAVE_BCK_INV);
+ if (slave_mode) {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_OUT_CON5_SLAVE_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_OUT_CON5_SLAVE_BCK_INV;
+ } else {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_OUT_CON5_MASTER_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_OUT_CON5_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int clk_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+ int apll;
+ int apll_clk_id;
+ struct etdm_con_reg etdm_reg;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int ret = 0;
+
+ if (clk_id < 0 || clkdiv_id < 0)
+ return 0;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ mask |= ETDM_CON1_MCLK_OUTPUT;
+ if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
+ val |= ETDM_CON1_MCLK_OUTPUT;
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ if (etdm_data->mclk_freq) {
+ apll = etdm_data->mclk_apll;
+ apll_clk_id = mt8195_afe_get_mclk_source_clk_id(apll);
+ if (apll_clk_id < 0)
+ return apll_clk_id;
+
+ /* select apll */
+ ret = mt8195_afe_set_clk_parent(afe, afe_priv->clk[clk_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret)
+ return ret;
+
+ /* set rate */
+ ret = mt8195_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id],
+ etdm_data->mclk_freq);
+ } else {
+ if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
+ dev_dbg(afe->dev, "%s mclk freq = 0\n", __func__);
+ }
+ return ret;
+}
+
+static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ unsigned int bit_width,
+ int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int etdm_channels;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ unsigned int bck;
+ unsigned int wlen = get_etdm_wlen(bit_width);
+ int ret;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ if (etdm_data->cowork_source_id != COWORK_ETDM_NONE)
+ slave_mode = true;
+
+ dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, clock %u slv %u\n",
+ __func__, etdm_data->format, etdm_data->data_mode,
+ etdm_data->lrck_inv, etdm_data->lrck_width, etdm_data->bck_inv,
+ etdm_data->clock_mode, etdm_data->slave_mode);
+ dev_dbg(afe->dev, "%s rate %u channels %u bitwidth %u, id %d\n",
+ __func__, rate, channels, bit_width, dai_id);
+
+ etdm_channels = (etdm_data->data_mode == MTK_DAI_ETDM_DATA_ONE_PIN) ?
+ get_etdm_ch_fixup(channels) : 2;
+
+ bck = rate * etdm_channels * wlen;
+ if (bck > MT8195_ETDM_NORMAL_MAX_BCK_RATE) {
+ dev_info(afe->dev, "%s bck rate %u not support\n",
+ __func__, bck);
+ return -EINVAL;
+ }
+
+ /* con0 */
+ mask |= ETDM_CON0_BIT_LEN_MASK;
+ val |= ETDM_CON0_BIT_LEN(bit_width);
+ mask |= ETDM_CON0_WORD_LEN_MASK;
+ val |= ETDM_CON0_WORD_LEN(wlen);
+ mask |= ETDM_CON0_FORMAT_MASK;
+ val |= ETDM_CON0_FORMAT(etdm_data->format);
+ mask |= ETDM_CON0_CH_NUM_MASK;
+ val |= ETDM_CON0_CH_NUM(etdm_channels);
+
+ mask |= ETDM_CON0_SLAVE_MODE;
+ if (slave_mode) {
+ if (dai_id == MT8195_AFE_IO_ETDM1_OUT &&
+ etdm_data->cowork_source_id == COWORK_ETDM_NONE) {
+ dev_info(afe->dev, "%s id %d only support master mode\n",
+ __func__, dai_id);
+ return -EINVAL;
+ }
+ val |= ETDM_CON0_SLAVE_MODE;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val);
+
+ if (get_etdm_dir(dai_id) == ETDM_IN)
+ mtk_dai_etdm_in_configure(afe, rate, channels, dai_id);
+ else
+ mtk_dai_etdm_out_configure(afe, rate, channels, dai_id);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ unsigned int channels = params_channels(params);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int i;
+
+ dev_dbg(afe->dev, "%s '%s' period %u-%u\n",
+ __func__, snd_pcm_stream_str(substream),
+ params_period_size(params), params_periods(params));
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+ return -EINVAL;
+
+ ret = mtk_dai_etdm_mclk_configure(afe, mst_dai_id);
+ if (ret)
+ return ret;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, mst_dai_id);
+ if (ret)
+ return ret;
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, slv_dai_id);
+ if (ret)
+ return ret;
+
+ ret = mt8195_etdm_sync_mode_configure(afe, slv_dai_id);
+ if (ret)
+ return ret;
+ }
+ } else {
+ ret = mtk_dai_etdm_mclk_configure(afe, dai->id);
+ if (ret)
+ return ret;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, dai->id);
+ }
+
+ return ret;
+}
+
+static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int i;
+
+ dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+ return -EINVAL;
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+
+ //open master first
+ ret |= mt8195_afe_enable_etdm(afe, mst_dai_id);
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret |= mt8195_afe_enable_etdm(afe, slv_dai_id);
+ }
+ } else {
+ ret = mt8195_afe_enable_etdm(afe, dai->id);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!mt8195_afe_etdm_is_valid(mst_dai_id))
+ return -EINVAL;
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret |= mt8195_afe_disable_etdm(afe, slv_dai_id);
+ }
+ // close master at last
+ ret |= mt8195_afe_disable_etdm(afe, mst_dai_id);
+ } else {
+ ret = mt8195_afe_disable_etdm(afe, dai->id);
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int apll;
+ int apll_rate;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ if (freq == 0) {
+ etdm_data->mclk_freq = freq;
+ return 0;
+ }
+
+ apll = mt8195_afe_get_default_mclk_source_by_rate(freq);
+ apll_rate = mt8195_afe_get_mclk_source_rate(afe, apll);
+
+ if (freq > apll_rate) {
+ dev_info(afe->dev, "freq %d > apll rate %d\n", freq, apll_rate);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_info(afe->dev, "APLL%d cannot generate freq Hz\n", apll);
+ return -EINVAL;
+ }
+
+ etdm_data->mclk_apll = apll;
+ etdm_data->mclk_freq = freq;
+
+ return 0;
+}
+
+static int mtk_dai_etdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
+ __func__, dai->id, freq, dir);
+ if (is_cowork_mode(dai))
+ dai_id = get_etdm_cowork_master_id(dai);
+ else
+ dai_id = dai->id;
+
+ if (!mt8195_afe_etdm_is_valid(dai_id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+ etdm_data->mclk_dir = dir;
+ return mtk_dai_etdm_cal_mclk(afe, freq, dai_id);
+}
+
+static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!mt8195_afe_etdm_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+ dev_dbg(dai->dev, "%s id %d slot_width %d\n",
+ __func__, dai->id, slot_width);
+
+ etdm_data->slots = slots;
+ etdm_data->lrck_width = slot_width;
+ return 0;
+}
+
+static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!mt8195_afe_etdm_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_RJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ etdm_data->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ etdm_data->slave_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_hdmitx_dptx_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+
+ if (cg_id >= 0)
+ mt8195_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+
+ mtk_dai_etdm_enable_mclk(afe, dai->id);
+
+ return 0;
+}
+
+static void mtk_dai_hdmitx_dptx_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+
+ mtk_dai_etdm_disable_mclk(afe, dai->id);
+
+ if (cg_id >= 0)
+ mt8195_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+}
+
+static unsigned int mtk_dai_get_dptx_ch_en(unsigned int channel)
+{
+ switch (channel) {
+ case 1 ... 2:
+ return AFE_DPTX_CON_CH_EN_2CH;
+ case 3 ... 4:
+ return AFE_DPTX_CON_CH_EN_4CH;
+ case 5 ... 6:
+ return AFE_DPTX_CON_CH_EN_6CH;
+ case 7 ... 8:
+ return AFE_DPTX_CON_CH_EN_8CH;
+ default:
+ return AFE_DPTX_CON_CH_EN_2CH;
+ }
+}
+
+static unsigned int mtk_dai_get_dptx_ch(unsigned int ch)
+{
+ return (ch > 2) ?
+ AFE_DPTX_CON_CH_NUM_8CH : AFE_DPTX_CON_CH_NUM_2CH;
+}
+
+static unsigned int mtk_dai_get_dptx_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ AFE_DPTX_CON_16BIT : AFE_DPTX_CON_24BIT;
+}
+
+static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ int width = snd_pcm_format_physical_width(format);
+ int ret = 0;
+
+ if (!mt8195_afe_hdmitx_dptx_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ /* dptx configure */
+ if (dai->id == MT8195_AFE_IO_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_CH_EN_MASK,
+ mtk_dai_get_dptx_ch_en(channels));
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_CH_NUM_MASK,
+ mtk_dai_get_dptx_ch(channels));
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_16BIT_MASK,
+ mtk_dai_get_dptx_wlen(format));
+
+ if (mtk_dai_get_dptx_ch(channels) == AFE_DPTX_CON_CH_NUM_8CH) {
+ etdm_data->data_mode = MTK_DAI_ETDM_DATA_ONE_PIN;
+ channels = 8;
+ } else {
+ channels = 2;
+ }
+ } else {
+ etdm_data->data_mode = MTK_DAI_ETDM_DATA_MULTI_PIN;
+ }
+
+ ret = mtk_dai_etdm_mclk_configure(afe, dai->id);
+ if (ret)
+ return ret;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels, width, dai->id);
+
+ return ret;
+}
+
+static int mtk_dai_hdmitx_dptx_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* enable dptx interface */
+ if (dai->id == MT8195_AFE_IO_DPTX)
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_ON_MASK,
+ AFE_DPTX_CON_ON);
+
+ /* enable etdm_out3 */
+ ret = mt8195_afe_enable_etdm(afe, dai->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable etdm_out3 */
+ ret = mt8195_afe_disable_etdm(afe, dai->id);
+
+ /* disable dptx interface */
+ if (dai->id == MT8195_AFE_IO_DPTX)
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_ON_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id,
+ unsigned int freq,
+ int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!mt8195_afe_hdmitx_dptx_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
+ __func__, dai->id, freq, dir);
+
+ etdm_data->mclk_dir = dir;
+ return mtk_dai_etdm_cal_mclk(afe, freq, dai->id);
+}
+
+/* dai driver */
+#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_384000)
+
+#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int mtk_dai_etdm_probe(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ dev_dbg(dai->dev, "%s id %d\n", __func__, dai->id);
+
+ if (!mt8195_afe_etdm_is_valid(dai->id))
+ return -EINVAL;
+
+ etdm_data = afe_priv->dai_priv[dai->id];
+ if (etdm_data->mclk_freq) {
+ dev_dbg(afe->dev, "MCLK always on, rate %d\n",
+ etdm_data->mclk_freq);
+ pm_runtime_get_sync(afe->dev);
+ mtk_dai_etdm_mclk_configure(afe, dai->id);
+ mtk_dai_etdm_enable_mclk(afe, dai->id);
+ pm_runtime_put_sync(afe->dev);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = {
+ .startup = mtk_dai_hdmitx_dptx_startup,
+ .shutdown = mtk_dai_hdmitx_dptx_shutdown,
+ .hw_params = mtk_dai_hdmitx_dptx_hw_params,
+ .trigger = mtk_dai_hdmitx_dptx_trigger,
+ .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+};
+
+static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops2 = {
+ .probe = mtk_dai_etdm_probe,
+ .startup = mtk_dai_hdmitx_dptx_startup,
+ .shutdown = mtk_dai_hdmitx_dptx_shutdown,
+ .hw_params = mtk_dai_hdmitx_dptx_hw_params,
+ .trigger = mtk_dai_hdmitx_dptx_trigger,
+ .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+};
+
+static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
+ .probe = mtk_dai_etdm_probe,
+ .startup = mtk_dai_etdm_startup,
+ .shutdown = mtk_dai_etdm_shutdown,
+ .hw_params = mtk_dai_etdm_hw_params,
+ .trigger = mtk_dai_etdm_trigger,
+ .set_sysclk = mtk_dai_etdm_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+ .set_tdm_slot = mtk_dai_etdm_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
+ {
+ .name = "DPTX",
+ .id = MT8195_AFE_IO_DPTX,
+ .playback = {
+ .stream_name = "DPTX Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_hdmitx_dptx_ops,
+ },
+ {
+ .name = "ETDM1_IN",
+ .id = MT8195_AFE_IO_ETDM1_IN,
+ .capture = {
+ .stream_name = "ETDM1 Capture",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM2_IN",
+ .id = MT8195_AFE_IO_ETDM2_IN,
+ .capture = {
+ .stream_name = "ETDM2 Capture",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM1_OUT",
+ .id = MT8195_AFE_IO_ETDM1_OUT,
+ .playback = {
+ .stream_name = "ETDM1 Playback",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM2_OUT",
+ .id = MT8195_AFE_IO_ETDM2_OUT,
+ .playback = {
+ .stream_name = "ETDM2 Playback",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM3_OUT",
+ .id = MT8195_AFE_IO_ETDM3_OUT,
+ .playback = {
+ .stream_name = "ETDM3 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_hdmitx_dptx_ops2,
+ },
+};
+
+static void mt8195_etdm_update_sync_info(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct mtk_dai_etdm_priv *mst_data;
+ int i;
+ int mst_dai_id;
+
+ for (i = MT8195_AFE_IO_ETDM_START; i < MT8195_AFE_IO_ETDM_END; i++) {
+ etdm_data = afe_priv->dai_priv[i];
+ if (etdm_data->cowork_source_id != COWORK_ETDM_NONE) {
+ mst_dai_id = etdm_data->cowork_source_id;
+ if (!mt8195_afe_etdm_is_valid(mst_dai_id)) {
+ dev_err(afe->dev, "%s invalid dai id %d\n",
+ __func__, mst_dai_id);
+ return;
+ }
+ mst_data = afe_priv->dai_priv[mst_dai_id];
+ if (mst_data->cowork_source_id != COWORK_ETDM_NONE)
+ dev_info(afe->dev, "%s [%d] wrong sync source\n"
+ , __func__, i);
+ mst_data->cowork_slv_id[mst_data->cowork_slv_count] = i;
+ mst_data->cowork_slv_count++;
+ }
+ }
+}
+
+static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
+{
+ const struct device_node *of_node = afe->dev->of_node;
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int i, j;
+ char prop[48];
+ u8 disable_chn[MT8195_ETDM_MAX_CHANNELS];
+ int max_chn = MT8195_ETDM_MAX_CHANNELS;
+ u32 sel;
+ int ret;
+ int dai_id;
+ unsigned int sync_id;
+ struct {
+ const char *name;
+ const unsigned int sync_id;
+ } of_afe_etdms[MT8195_AFE_IO_ETDM_NUM] = {
+ {"etdm-in1", ETDM_SYNC_FROM_IN1},
+ {"etdm-in2", ETDM_SYNC_FROM_IN2},
+ {"etdm-out1", ETDM_SYNC_FROM_OUT1},
+ {"etdm-out2", ETDM_SYNC_FROM_OUT2},
+ {"etdm-out3", ETDM_SYNC_FROM_OUT3},
+ };
+
+ for (i = 0; i < MT8195_AFE_IO_ETDM_NUM; i++) {
+ dai_id = ETDM_TO_DAI_ID(i);
+ if (!mt8195_afe_etdm_is_valid(dai_id)) {
+ dev_err(afe->dev, "%s invalid dai id %d\n",
+ __func__, dai_id);
+ return;
+ }
+
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ ret = snprintf(prop, sizeof(prop),
+ "mediatek,%s-mclk-always-on-rate",
+ of_afe_etdms[i].name);
+ if (ret < 0) {
+ dev_info(afe->dev, "%s snprintf err=%d\n",
+ __func__, ret);
+ return;
+ }
+ ret = of_property_read_u32(of_node, prop, &sel);
+ if (ret == 0) {
+ etdm_data->mclk_dir = SND_SOC_CLOCK_OUT;
+ if (mtk_dai_etdm_cal_mclk(afe, sel, dai_id))
+ dev_info(afe->dev, "%s unsupported mclk %uHz\n",
+ __func__, sel);
+ }
+
+ ret = snprintf(prop, sizeof(prop),
+ "mediatek,%s-multi-pin-mode",
+ of_afe_etdms[i].name);
+ if (ret < 0) {
+ dev_info(afe->dev, "%s snprintf err=%d\n",
+ __func__, ret);
+ return;
+ }
+ etdm_data->data_mode = of_property_read_bool(of_node, prop);
+
+ ret = snprintf(prop, sizeof(prop),
+ "mediatek,%s-cowork-source",
+ of_afe_etdms[i].name);
+ if (ret < 0) {
+ dev_info(afe->dev, "%s snprintf err=%d\n",
+ __func__, ret);
+ return;
+ }
+ ret = of_property_read_u32(of_node, prop, &sel);
+ if (ret == 0) {
+ if (sel >= MT8195_AFE_IO_ETDM_NUM) {
+ dev_info(afe->dev, "%s invalid id=%d\n",
+ __func__, sel);
+ etdm_data->cowork_source_id = COWORK_ETDM_NONE;
+ } else {
+ sync_id = of_afe_etdms[sel].sync_id;
+ etdm_data->cowork_source_id =
+ sync_to_dai_id(sync_id);
+ }
+ } else {
+ etdm_data->cowork_source_id = COWORK_ETDM_NONE;
+ }
+ }
+
+ /* etdm in only */
+ for (i = 0; i < 2; i++) {
+ dai_id = ETDM_TO_DAI_ID(i);
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ ret = snprintf(prop, sizeof(prop),
+ "mediatek,%s-chn-disabled",
+ of_afe_etdms[i].name);
+ if (ret < 0) {
+ dev_info(afe->dev, "%s snprintf err=%d\n",
+ __func__, ret);
+ return;
+ }
+ ret = of_property_read_variable_u8_array(of_node, prop,
+ disable_chn,
+ 1, max_chn);
+ if (ret < 0)
+ continue;
+
+ for (j = 0; j < ret; j++) {
+ if (disable_chn[j] >= MT8195_ETDM_MAX_CHANNELS)
+ dev_info(afe->dev, "%s [%d] invalid chn %u\n",
+ __func__, j, disable_chn[j]);
+ else
+ etdm_data->in_disable_ch[disable_chn[j]] = true;
+ }
+ }
+ mt8195_etdm_update_sync_info(afe);
+}
+
+static int init_etdm_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int i;
+
+ for (i = MT8195_AFE_IO_ETDM_START; i < MT8195_AFE_IO_ETDM_END; i++) {
+ etdm_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_etdm_priv),
+ GFP_KERNEL);
+ if (!etdm_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[i] = etdm_priv;
+ }
+
+ afe_priv->dai_priv[MT8195_AFE_IO_DPTX] =
+ afe_priv->dai_priv[MT8195_AFE_IO_ETDM3_OUT];
+
+ mt8195_dai_etdm_parse_of(afe);
+ return 0;
+}
+
+int mt8195_dai_etdm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_etdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
+
+ dai->dapm_widgets = mtk_dai_etdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
+ dai->dapm_routes = mtk_dai_etdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
+ dai->controls = mtk_dai_etdm_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_etdm_controls);
+
+ return init_etdm_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
new file mode 100644
index 000000000000..cdc16057d50e
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI PCM I/F Control
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8195-afe-clk.h"
+#include "mt8195-afe-common.h"
+#include "mt8195-reg.h"
+
+enum {
+ MTK_DAI_PCM_FMT_I2S,
+ MTK_DAI_PCM_FMT_EIAJ,
+ MTK_DAI_PCM_FMT_MODEA,
+ MTK_DAI_PCM_FMT_MODEB,
+};
+
+enum {
+ MTK_DAI_PCM_CLK_A1SYS,
+ MTK_DAI_PCM_CLK_A2SYS,
+ MTK_DAI_PCM_CLK_26M_48K,
+ MTK_DAI_PCM_CLK_26M_441K,
+};
+
+struct mtk_dai_pcm_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+struct mtk_dai_pcmif_priv {
+ unsigned int slave_mode;
+ unsigned int lrck_inv;
+ unsigned int bck_inv;
+ unsigned int format;
+};
+
+static const struct mtk_dai_pcm_rate mtk_dai_pcm_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 16000, .reg_value = 1, },
+ { .rate = 32000, .reg_value = 2, },
+ { .rate = 48000, .reg_value = 3, },
+ { .rate = 11025, .reg_value = 1, },
+ { .rate = 22050, .reg_value = 2, },
+ { .rate = 44100, .reg_value = 3, },
+};
+
+static int mtk_dai_pcm_mode(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_dai_pcm_rates); i++)
+ if (mtk_dai_pcm_rates[i].rate == rate)
+ return mtk_dai_pcm_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new mtk_dai_pcm_o000_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN0, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN0_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_pcm_o001_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN1_2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ SND_SOC_DAPM_MIXER("I002", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I003", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O000", SND_SOC_NOPM, 0, 0,
+ mtk_dai_pcm_o000_mix,
+ ARRAY_SIZE(mtk_dai_pcm_o000_mix)),
+ SND_SOC_DAPM_MIXER("O001", SND_SOC_NOPM, 0, 0,
+ mtk_dai_pcm_o001_mix,
+ ARRAY_SIZE(mtk_dai_pcm_o001_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_EN", PCM_INTF_CON1,
+ PCM_INTF_CON1_PCM_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("PCM1_INPUT"),
+ SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"I002", NULL, "PCM1 Capture"},
+ {"I003", NULL, "PCM1 Capture"},
+
+ {"O000", "I000 Switch", "I000"},
+ {"O001", "I001 Switch", "I001"},
+
+ {"O000", "I070 Switch", "I070"},
+ {"O001", "I071 Switch", "I071"},
+
+ {"PCM1 Playback", NULL, "O000"},
+ {"PCM1 Playback", NULL, "O001"},
+
+ {"PCM1 Playback", NULL, "PCM_EN"},
+ {"PCM1 Playback", NULL, "aud_asrc12"},
+ {"PCM1 Playback", NULL, "aud_pcmif"},
+
+ {"PCM1 Capture", NULL, "PCM_EN"},
+ {"PCM1 Capture", NULL, "aud_asrc11"},
+ {"PCM1 Capture", NULL, "aud_pcmif"},
+
+ {"PCM1_OUTPUT", NULL, "PCM1 Playback"},
+ {"PCM1 Capture", NULL, "PCM1_INPUT"},
+};
+
+static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv;
+ unsigned int slave_mode;
+ unsigned int lrck_inv;
+ unsigned int bck_inv;
+ unsigned int fmt;
+ unsigned int bit_width = dai->symmetric_sample_bits;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int fs = 0;
+ int mode = 0;
+
+ if (dai->id != MT8195_AFE_IO_PCM)
+ return -EINVAL;
+
+ pcmif_priv = afe_priv->dai_priv[dai->id];
+ slave_mode = pcmif_priv->slave_mode;
+ lrck_inv = pcmif_priv->lrck_inv;
+ bck_inv = pcmif_priv->bck_inv;
+ fmt = pcmif_priv->format;
+
+ /* sync freq mode */
+ fs = mt8195_afe_fs_timing(runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+ val |= PCM_INTF_CON2_SYNC_FREQ_MODE(fs);
+ mask |= PCM_INTF_CON2_SYNC_FREQ_MODE_MASK;
+
+ /* clk domain sel */
+ if (runtime->rate % 8000)
+ val |= PCM_INTF_CON2_CLK_DOMAIN_SEL(MTK_DAI_PCM_CLK_26M_441K);
+ else
+ val |= PCM_INTF_CON2_CLK_DOMAIN_SEL(MTK_DAI_PCM_CLK_26M_48K);
+ mask |= PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON2, mask, val);
+
+ val = 0;
+ mask = 0;
+
+ /* pcm mode */
+ mode = mtk_dai_pcm_mode(runtime->rate);
+ if (mode < 0)
+ return -EINVAL;
+ val |= PCM_INTF_CON1_PCM_MODE(mode);
+ mask |= PCM_INTF_CON1_PCM_MODE_MASK;
+
+ /* pcm format */
+ val |= PCM_INTF_CON1_PCM_FMT(fmt);
+ mask |= PCM_INTF_CON1_PCM_FMT_MASK;
+
+ /* pcm sync length */
+ if (fmt == MTK_DAI_PCM_FMT_MODEA ||
+ fmt == MTK_DAI_PCM_FMT_MODEB)
+ val |= PCM_INTF_CON1_SYNC_LENGTH(1);
+ else
+ val |= PCM_INTF_CON1_SYNC_LENGTH(bit_width);
+ mask |= PCM_INTF_CON1_SYNC_LENGTH_MASK;
+
+ /* pcm bits, word length */
+ if (bit_width > 16) {
+ val |= PCM_INTF_CON1_PCM_24BIT;
+ val |= PCM_INTF_CON1_PCM_WLEN_64BCK;
+ } else {
+ val |= PCM_INTF_CON1_PCM_16BIT;
+ val |= PCM_INTF_CON1_PCM_WLEN_32BCK;
+ }
+ mask |= PCM_INTF_CON1_PCM_BIT_MASK;
+ mask |= PCM_INTF_CON1_PCM_WLEN_MASK;
+
+ /* master/slave */
+ if (!slave_mode) {
+ val |= PCM_INTF_CON1_PCM_MASTER;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_OUT_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_OUT_INV;
+ mask |= PCM_INTF_CON1_CLK_OUT_INV_MASK;
+ } else {
+ val |= PCM_INTF_CON1_PCM_SLAVE;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_IN_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_IN_INV;
+ mask |= PCM_INTF_CON1_CLK_IN_INV_MASK;
+
+ /* TODO: add asrc setting for slave mode */
+ }
+ mask |= PCM_INTF_CON1_PCM_M_S_MASK;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1, mask, val);
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
+
+ dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
+ __func__, dai->id, substream->stream,
+ p->active, c->active);
+
+ if (p->active || c->active)
+ return 0;
+
+ return mtk_dai_pcm_configure(substream, dai);
+}
+
+static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv;
+
+ dev_dbg(dai->dev, "%s fmt 0x%x\n", __func__, fmt);
+
+ if (dai->id != MT8195_AFE_IO_PCM)
+ return -EINVAL;
+
+ pcmif_priv = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_MODEA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_MODEB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ pcmif_priv->bck_inv = 0;
+ pcmif_priv->lrck_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ pcmif_priv->bck_inv = 0;
+ pcmif_priv->lrck_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ pcmif_priv->bck_inv = 1;
+ pcmif_priv->lrck_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ pcmif_priv->bck_inv = 1;
+ pcmif_priv->lrck_inv = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ pcmif_priv->slave_mode = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ pcmif_priv->slave_mode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .prepare = mtk_dai_pcm_prepare,
+ .set_fmt = mtk_dai_pcm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM1",
+ .id = MT8195_AFE_IO_PCM,
+ .playback = {
+ .stream_name = "PCM1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+static int init_pcmif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv;
+
+ pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_pcmif_priv),
+ GFP_KERNEL);
+ if (!pcmif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8195_AFE_IO_PCM] = pcmif_priv;
+ return 0;
+}
+
+int mt8195_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+
+ return init_pcmif_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
new file mode 100644
index 000000000000..4d62bc654a58
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -0,0 +1,1581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8195-mt6359.c --
+ * MT8195-MT6359 ALSA SoC machine driver code
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ * YC Hung <yc.hung@mediatek.com>
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/rt5682.h>
+#include <sound/soc.h>
+#include "../../codecs/mt6359.h"
+#include "../../codecs/rt1011.h"
+#include "../../codecs/rt5682.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-dsp-sof-common.h"
+#include "../common/mtk-soc-card.h"
+#include "../common/mtk-soundcard-driver.h"
+#include "mt8195-afe-clk.h"
+#include "mt8195-afe-common.h"
+
+#define RT1011_SPEAKER_AMP_PRESENT BIT(0)
+#define RT1019_SPEAKER_AMP_PRESENT BIT(1)
+#define MAX98390_SPEAKER_AMP_PRESENT BIT(2)
+
+#define DUMB_CODEC_INIT BIT(0)
+#define MT6359_CODEC_INIT BIT(1)
+#define RT1011_CODEC_INIT BIT(2)
+#define RT1019_CODEC_INIT BIT(3)
+#define MAX98390_CODEC_INIT BIT(4)
+#define RT5682_CODEC_INIT BIT(5)
+
+#define RT1011_CODEC_DAI "rt1011-aif"
+#define RT1011_DEV0_NAME "rt1011.2-0038"
+#define RT1011_DEV1_NAME "rt1011.2-0039"
+
+#define RT1019_CODEC_DAI "HiFi"
+#define RT1019_DEV0_NAME "rt1019p"
+
+#define MAX98390_CODEC_DAI "max98390-aif1"
+#define MAX98390_DEV0_NAME "max98390.2-0038" /* right */
+#define MAX98390_DEV1_NAME "max98390.2-0039" /* left */
+
+#define RT5682_CODEC_DAI "rt5682-aif1"
+#define RT5682_DEV0_NAME "rt5682.2-001a"
+
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+#define RT5682S_DEV0_NAME "rt5682s.2-001a"
+
+#define SOF_DMA_DL2 "SOF_DMA_DL2"
+#define SOF_DMA_DL3 "SOF_DMA_DL3"
+#define SOF_DMA_UL4 "SOF_DMA_UL4"
+#define SOF_DMA_UL5 "SOF_DMA_UL5"
+
+struct mt8195_mt6359_priv {
+ struct clk *i2so1_mclk;
+};
+
+enum mt8195_jacks {
+ MT8195_JACK_HEADSET,
+ MT8195_JACK_DP,
+ MT8195_JACK_HDMI,
+ MT8195_JACK_MAX,
+};
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mt8195_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget mt8195_mt6359_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL4, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route mt8195_mt6359_routes[] = {
+ /* SOF Uplink */
+ {SOF_DMA_UL4, NULL, "O034"},
+ {SOF_DMA_UL4, NULL, "O035"},
+ {SOF_DMA_UL5, NULL, "O036"},
+ {SOF_DMA_UL5, NULL, "O037"},
+ /* SOF Downlink */
+ {"I070", NULL, SOF_DMA_DL2},
+ {"I071", NULL, SOF_DMA_DL2},
+ {"I020", NULL, SOF_DMA_DL3},
+ {"I021", NULL, SOF_DMA_DL3},
+};
+
+static const struct snd_kcontrol_new mt8195_mt6359_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget mt8195_dual_speaker_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_kcontrol_new mt8195_dual_speaker_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8195_speaker_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_kcontrol_new mt8195_speaker_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_route mt8195_rt5682_routes[] = {
+ /* headset */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route mt8195_rt1011_routes[] = {
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
+
+static const struct snd_soc_dapm_route mt8195_rt1019_routes[] = {
+ { "Ext Spk", NULL, "Speaker" },
+};
+
+static const struct snd_soc_dapm_route mt8195_max98390_routes[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+#define CKSYS_AUD_TOP_CFG 0x032c
+#define CKSYS_AUD_TOP_MON 0x0330
+
+static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int chosen_phase_1, chosen_phase_2, chosen_phase_3;
+ int prev_cycle_1, prev_cycle_2, prev_cycle_3;
+ int test_done_1, test_done_2, test_done_3;
+ int cycle_1, cycle_2, cycle_3;
+ int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_calibration_num_phase;
+ bool mtkaif_calibration_ok;
+ unsigned int monitor = 0;
+ int counter;
+ int phase;
+ int i;
+
+ dev_dbg(afe->dev, "%s(), start\n", __func__);
+
+ param->mtkaif_calibration_ok = false;
+ for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) {
+ param->mtkaif_chosen_phase[i] = -1;
+ param->mtkaif_phase_cycle[i] = 0;
+ mtkaif_chosen_phase[i] = -1;
+ mtkaif_phase_cycle[i] = 0;
+ }
+
+ if (IS_ERR(afe_priv->topckgen)) {
+ dev_info(afe->dev, "%s() Cannot find topckgen controller\n",
+ __func__);
+ return 0;
+ }
+
+ pm_runtime_get_sync(afe->dev);
+ mt6359_mtkaif_calibration_enable(cmpnt_codec);
+
+ /* set test type to synchronizer pulse */
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
+ mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
+ mtkaif_calibration_ok = true;
+
+ for (phase = 0;
+ phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok;
+ phase++) {
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ phase, phase, phase);
+
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0x1, 0x1);
+
+ test_done_1 = 0;
+ test_done_2 = 0;
+ test_done_3 = 0;
+ cycle_1 = -1;
+ cycle_2 = -1;
+ cycle_3 = -1;
+ counter = 0;
+ while (!(test_done_1 & test_done_2 & test_done_3)) {
+ regmap_read(afe_priv->topckgen,
+ CKSYS_AUD_TOP_MON, &monitor);
+ test_done_1 = (monitor >> 28) & 0x1;
+ test_done_2 = (monitor >> 29) & 0x1;
+ test_done_3 = (monitor >> 30) & 0x1;
+ if (test_done_1 == 1)
+ cycle_1 = monitor & 0xf;
+
+ if (test_done_2 == 1)
+ cycle_2 = (monitor >> 4) & 0xf;
+
+ if (test_done_3 == 1)
+ cycle_3 = (monitor >> 8) & 0xf;
+
+ /* handle if never test done */
+ if (++counter > 10000) {
+ dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, cycle_3 %d, monitor 0x%x\n",
+ __func__,
+ cycle_1, cycle_2, cycle_3, monitor);
+ mtkaif_calibration_ok = false;
+ break;
+ }
+ }
+
+ if (phase == 0) {
+ prev_cycle_1 = cycle_1;
+ prev_cycle_2 = cycle_2;
+ prev_cycle_3 = cycle_3;
+ }
+
+ if (cycle_1 != prev_cycle_1 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = phase - 1;
+ mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] = prev_cycle_1;
+ }
+
+ if (cycle_2 != prev_cycle_2 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = phase - 1;
+ mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] = prev_cycle_2;
+ }
+
+ if (cycle_3 != prev_cycle_3 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = phase - 1;
+ mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] = prev_cycle_3;
+ }
+
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0x1, 0x0);
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] >= 0 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] >= 0 &&
+ mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] >= 0)
+ break;
+ }
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_1 = 0;
+ } else {
+ chosen_phase_1 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0];
+ }
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_2 = 0;
+ } else {
+ chosen_phase_2 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1];
+ }
+
+ if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_3 = 0;
+ } else {
+ chosen_phase_3 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2];
+ }
+
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ chosen_phase_1,
+ chosen_phase_2,
+ chosen_phase_3);
+
+ mt6359_mtkaif_calibration_disable(cmpnt_codec);
+ pm_runtime_put(afe->dev);
+
+ param->mtkaif_calibration_ok = mtkaif_calibration_ok;
+ param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = chosen_phase_1;
+ param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = chosen_phase_2;
+ param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = chosen_phase_3;
+ for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++)
+ param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
+
+ dev_info(afe->dev, "%s(), end, calibration ok %d\n",
+ __func__, param->mtkaif_calibration_ok);
+
+ return 0;
+}
+
+static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ /* set mtkaif protocol */
+ mt6359_set_mtkaif_protocol(cmpnt_codec,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
+
+ /* mtkaif calibration */
+ mt8195_mt6359_mtkaif_calibration(rtd);
+
+ return 0;
+}
+
+static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream)
+{
+ return mtk_soundcard_startup(substream, MTK_CONSTRAINT_HDMIDP);
+}
+
+static const struct snd_soc_ops mt8195_hdmitx_dptx_playback_ops = {
+ .startup = mt8195_hdmitx_dptx_startup,
+};
+
+static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8195_dptx_ops = {
+ .hw_params = mt8195_dptx_hw_params,
+};
+
+static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8195_JACK_DP];
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_AVOUT, jack);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+}
+
+static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8195_JACK_HDMI];
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+}
+
+static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to S24_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK,
+ rate * 256, rate * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
+ .hw_params = mt8195_rt5682_etdm_hw_params,
+};
+
+static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct mt8195_mt6359_priv *priv = soc_card_data->mach_priv;
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8195_JACK_HEADSET];
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack, mt8195_jack_pins,
+ ARRAY_SIZE(mt8195_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, mt8195_rt5682_routes,
+ ARRAY_SIZE(mt8195_rt5682_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+};
+
+static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ int srate, i, ret;
+
+ srate = params_rate(params);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
+ 64 * srate, 256 * srate);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT1011_FS_SYS_PRE_S_PLL1,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
+ .hw_params = mt8195_rt1011_etdm_hw_params,
+};
+
+static int mt8195_sof_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *cmpnt_afe = NULL;
+ struct snd_soc_pcm_runtime *runtime;
+
+ /* find afe component */
+ for_each_card_rtds(rtd->card, runtime) {
+ cmpnt_afe = snd_soc_rtdcom_lookup(runtime, AFE_PCM_NAME);
+ if (cmpnt_afe)
+ break;
+ }
+
+ if (cmpnt_afe && !pm_runtime_active(cmpnt_afe->dev)) {
+ dev_err(rtd->dev, "afe pm runtime is not active!!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_sof_be_ops = {
+ .hw_params = mt8195_sof_be_hw_params,
+};
+
+static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, mt8195_dual_speaker_widgets,
+ ARRAY_SIZE(mt8195_dual_speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8195_dual_speaker_controls,
+ ARRAY_SIZE(mt8195_dual_speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, mt8195_rt1011_routes,
+ ARRAY_SIZE(mt8195_rt1011_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int mt8195_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, mt8195_speaker_widgets,
+ ARRAY_SIZE(mt8195_speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8195_speaker_controls,
+ ARRAY_SIZE(mt8195_speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8195_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = mt8195_dumb_amp_init(rtd);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, mt8195_rt1019_routes,
+ ARRAY_SIZE(mt8195_rt1019_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int mt8195_max98390_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, mt8195_dual_speaker_widgets,
+ ARRAY_SIZE(mt8195_dual_speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8195_dual_speaker_controls,
+ ARRAY_SIZE(mt8195_dual_speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, mt8195_max98390_routes,
+ ARRAY_SIZE(mt8195_max98390_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to S24_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int mt8195_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct mt8195_mt6359_priv *priv = soc_card_data->mach_priv;
+ int ret;
+
+ /*
+ * It's required to control mclk directly in the set_bias_level_post
+ * function for rt5682 and rt5682s codec, or the unexpected pop happens
+ * at the end of playback.
+ */
+ if (!component ||
+ (strcmp(component->name, RT5682_DEV0_NAME) &&
+ strcmp(component->name, RT5682S_DEV0_NAME)))
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ if (!__clk_is_enabled(priv->i2so1_mclk))
+ return 0;
+
+ clk_disable_unprepare(priv->i2so1_mclk);
+ dev_dbg(card->dev, "Disable i2so1 mclk\n");
+ break;
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(priv->i2so1_mclk);
+ if (ret) {
+ dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(card->dev, "Enable i2so1 mclk\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+enum {
+ DAI_LINK_DL2_FE,
+ DAI_LINK_DL3_FE,
+ DAI_LINK_DL6_FE,
+ DAI_LINK_DL7_FE,
+ DAI_LINK_DL8_FE,
+ DAI_LINK_DL10_FE,
+ DAI_LINK_DL11_FE,
+ DAI_LINK_UL1_FE,
+ DAI_LINK_UL2_FE,
+ DAI_LINK_UL3_FE,
+ DAI_LINK_UL4_FE,
+ DAI_LINK_UL5_FE,
+ DAI_LINK_UL6_FE,
+ DAI_LINK_UL8_FE,
+ DAI_LINK_UL9_FE,
+ DAI_LINK_UL10_FE,
+ DAI_LINK_DL_SRC_BE,
+ DAI_LINK_DPTX_BE,
+ DAI_LINK_ETDM1_IN_BE,
+ DAI_LINK_ETDM2_IN_BE,
+ DAI_LINK_ETDM1_OUT_BE,
+ DAI_LINK_ETDM2_OUT_BE,
+ DAI_LINK_ETDM3_OUT_BE,
+ DAI_LINK_PCM1_BE,
+ DAI_LINK_UL_SRC1_BE,
+ DAI_LINK_UL_SRC2_BE,
+ DAI_LINK_REGULAR_LAST = DAI_LINK_UL_SRC2_BE,
+ DAI_LINK_SOF_START,
+ DAI_LINK_SOF_DL2_BE = DAI_LINK_SOF_START,
+ DAI_LINK_SOF_DL3_BE,
+ DAI_LINK_SOF_UL4_BE,
+ DAI_LINK_SOF_UL5_BE,
+ DAI_LINK_SOF_END = DAI_LINK_SOF_UL5_BE,
+};
+
+#define DAI_LINK_REGULAR_NUM (DAI_LINK_REGULAR_LAST + 1)
+
+/* FE */
+SND_SOC_DAILINK_DEFS(DL2_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL3_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL6_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL7_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL8_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL10_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DL11_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL11")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL1_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL2_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL3_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL4_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL5_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL6_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL8_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL9_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL10_FE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(DL_SRC_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(DPTX_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("DPTX")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(PCM1_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL_SRC1_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1"),
+ COMP_CODEC("dmic-codec",
+ "dmic-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(UL_SRC2_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif2")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL3,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL4,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL5,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* codec */
+SND_SOC_DAILINK_DEF(rt1019_comps,
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME,
+ RT1019_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(rt1011_comps,
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME,
+ RT1011_CODEC_DAI),
+ COMP_CODEC(RT1011_DEV1_NAME,
+ RT1011_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(max98390_comps,
+ DAILINK_COMP_ARRAY(COMP_CODEC(MAX98390_DEV0_NAME,
+ MAX98390_CODEC_DAI),
+ COMP_CODEC(MAX98390_DEV1_NAME,
+ MAX98390_CODEC_DAI)));
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ { "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK},
+ { "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK},
+ { "UL_SRC1_BE", "AFE_SOF_UL4", SOF_DMA_UL4, SNDRV_PCM_STREAM_CAPTURE},
+ { "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE},
+};
+
+static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
+ /* FE */
+ [DAI_LINK_DL2_FE] = {
+ .name = "DL2_FE",
+ .stream_name = "DL2 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(DL2_FE),
+ },
+ [DAI_LINK_DL3_FE] = {
+ .name = "DL3_FE",
+ .stream_name = "DL3 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(DL3_FE),
+ },
+ [DAI_LINK_DL6_FE] = {
+ .name = "DL6_FE",
+ .stream_name = "DL6 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(DL6_FE),
+ },
+ [DAI_LINK_DL7_FE] = {
+ .name = "DL7_FE",
+ .stream_name = "DL7 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(DL7_FE),
+ },
+ [DAI_LINK_DL8_FE] = {
+ .name = "DL8_FE",
+ .stream_name = "DL8 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(DL8_FE),
+ },
+ [DAI_LINK_DL10_FE] = {
+ .name = "DL10_FE",
+ .stream_name = "DL10 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mt8195_hdmitx_dptx_playback_ops,
+ SND_SOC_DAILINK_REG(DL10_FE),
+ },
+ [DAI_LINK_DL11_FE] = {
+ .name = "DL11_FE",
+ .stream_name = "DL11 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .ops = &mtk_soundcard_common_playback_ops,
+ SND_SOC_DAILINK_REG(DL11_FE),
+ },
+ [DAI_LINK_UL1_FE] = {
+ .name = "UL1_FE",
+ .stream_name = "UL1 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(UL1_FE),
+ },
+ [DAI_LINK_UL2_FE] = {
+ .name = "UL2_FE",
+ .stream_name = "UL2 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL2_FE),
+ },
+ [DAI_LINK_UL3_FE] = {
+ .name = "UL3_FE",
+ .stream_name = "UL3 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL3_FE),
+ },
+ [DAI_LINK_UL4_FE] = {
+ .name = "UL4_FE",
+ .stream_name = "UL4 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL4_FE),
+ },
+ [DAI_LINK_UL5_FE] = {
+ .name = "UL5_FE",
+ .stream_name = "UL5 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL5_FE),
+ },
+ [DAI_LINK_UL6_FE] = {
+ .name = "UL6_FE",
+ .stream_name = "UL6 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(UL6_FE),
+ },
+ [DAI_LINK_UL8_FE] = {
+ .name = "UL8_FE",
+ .stream_name = "UL8 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL8_FE),
+ },
+ [DAI_LINK_UL9_FE] = {
+ .name = "UL9_FE",
+ .stream_name = "UL9 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL9_FE),
+ },
+ [DAI_LINK_UL10_FE] = {
+ .name = "UL10_FE",
+ .stream_name = "UL10 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .ops = &mtk_soundcard_common_capture_ops,
+ SND_SOC_DAILINK_REG(UL10_FE),
+ },
+ /* BE */
+ [DAI_LINK_DL_SRC_BE] = {
+ .name = "DL_SRC_BE",
+ .no_pcm = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(DL_SRC_BE),
+ },
+ [DAI_LINK_DPTX_BE] = {
+ .name = "DPTX_BE",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8195_dptx_ops,
+ .be_hw_params_fixup = mt8195_dptx_hw_params_fixup,
+ SND_SOC_DAILINK_REG(DPTX_BE),
+ },
+ [DAI_LINK_ETDM1_IN_BE] = {
+ .name = "ETDM1_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(ETDM1_IN_BE),
+ },
+ [DAI_LINK_ETDM2_IN_BE] = {
+ .name = "ETDM2_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .capture_only = 1,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM2_IN_BE),
+ },
+ [DAI_LINK_ETDM1_OUT_BE] = {
+ .name = "ETDM1_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
+ },
+ [DAI_LINK_ETDM2_OUT_BE] = {
+ .name = "ETDM2_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(ETDM2_OUT_BE),
+ },
+ [DAI_LINK_ETDM3_OUT_BE] = {
+ .name = "ETDM3_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(ETDM3_OUT_BE),
+ },
+ [DAI_LINK_PCM1_BE] = {
+ .name = "PCM1_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(PCM1_BE),
+ },
+ [DAI_LINK_UL_SRC1_BE] = {
+ .name = "UL_SRC1_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(UL_SRC1_BE),
+ },
+ [DAI_LINK_UL_SRC2_BE] = {
+ .name = "UL_SRC2_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(UL_SRC2_BE),
+ },
+ /* SOF BE */
+ [DAI_LINK_SOF_DL2_BE] = {
+ .name = "AFE_SOF_DL2",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8195_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL2),
+ },
+ [DAI_LINK_SOF_DL3_BE] = {
+ .name = "AFE_SOF_DL3",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8195_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL3),
+ },
+ [DAI_LINK_SOF_UL4_BE] = {
+ .name = "AFE_SOF_UL4",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8195_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL4),
+ },
+ [DAI_LINK_SOF_UL5_BE] = {
+ .name = "AFE_SOF_UL5",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8195_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL5),
+ },
+};
+
+static struct snd_soc_codec_conf rt1011_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_codec_conf max98390_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX98390_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX98390_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static struct snd_soc_card mt8195_mt6359_soc_card = {
+ .owner = THIS_MODULE,
+ .dai_link = mt8195_mt6359_dai_links,
+ .num_links = ARRAY_SIZE(mt8195_mt6359_dai_links),
+ .controls = mt8195_mt6359_controls,
+ .num_controls = ARRAY_SIZE(mt8195_mt6359_controls),
+ .dapm_widgets = mt8195_mt6359_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_widgets),
+ .dapm_routes = mt8195_mt6359_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_routes),
+ .set_bias_level_post = mt8195_set_bias_level_post,
+};
+
+/* fixup the BE DAI link to match any values from topology */
+static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+
+ ret = mtk_sof_dai_link_fixup(rtd, params);
+
+ if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") ||
+ !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) {
+ mt8195_etdm_hw_params_fixup(rtd, params);
+ }
+
+ return ret;
+}
+
+static int mt8195_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ struct device_node *codec_node, *dp_node, *hdmi_node;
+ struct snd_soc_dai_link *dai_link;
+ struct device *dev = card->dev;
+ bool is5682s, init6359 = false;
+ int i;
+
+ if (strstr(card->name, "_5682s")) {
+ codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682s");
+ is5682s = true;
+ } else {
+ codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682i");
+ is5682s = false;
+ }
+
+ dp_node = of_parse_phandle(dev->of_node, "mediatek,dptx-codec", 0);
+ hdmi_node = of_parse_phandle(dev->of_node, "mediatek,hdmi-codec", 0);
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "DPTX_BE") == 0) {
+ if (!dp_node) {
+ dev_dbg(dev, "No property 'dptx-codec'\n");
+ } else {
+ dai_link->codecs->of_node = dp_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name = "i2s-hifi";
+ dai_link->init = mt8195_dptx_codec_init;
+ }
+ } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ if (!hdmi_node) {
+ dev_dbg(dev, "No property 'hdmi-codec'\n");
+ } else {
+ dai_link->codecs->of_node = hdmi_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name = "i2s-hifi";
+ dai_link->init = mt8195_hdmi_codec_init;
+ }
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0) {
+ if (!codec_node) {
+ dev_err(dev, "Codec not found!\n");
+ } else {
+ dai_link->codecs->of_node = codec_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name =
+ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
+ dai_link->init = mt8195_rt5682_init;
+ dai_link->ops = &mt8195_rt5682_etdm_ops;
+ }
+ } else if (strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ if (!codec_node) {
+ dev_err(dev, "Codec not found!\n");
+ } else {
+ dai_link->codecs->of_node = codec_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name =
+ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
+ dai_link->ops = &mt8195_rt5682_etdm_ops;
+ }
+ } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC1_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC2_BE") == 0) {
+ if (!init6359) {
+ dai_link->init = mt8195_mt6359_init;
+ init6359 = true;
+ }
+ } else if (strcmp(dai_link->name, "ETDM2_OUT_BE") == 0) {
+ switch (card_data->flags) {
+ case RT1011_SPEAKER_AMP_PRESENT:
+ dai_link->codecs = rt1011_comps;
+ dai_link->num_codecs = ARRAY_SIZE(rt1011_comps);
+ dai_link->init = mt8195_rt1011_init;
+ dai_link->ops = &mt8195_rt1011_etdm_ops;
+ dai_link->be_hw_params_fixup = mt8195_etdm_hw_params_fixup;
+ card->codec_conf = rt1011_codec_conf;
+ card->num_configs = ARRAY_SIZE(rt1011_codec_conf);
+ break;
+ case RT1019_SPEAKER_AMP_PRESENT:
+ dai_link->codecs = rt1019_comps;
+ dai_link->num_codecs = ARRAY_SIZE(rt1019_comps);
+ dai_link->init = mt8195_rt1019_init;
+ break;
+ case MAX98390_SPEAKER_AMP_PRESENT:
+ dai_link->codecs = max98390_comps;
+ dai_link->num_codecs = ARRAY_SIZE(max98390_comps);
+ dai_link->init = mt8195_max98390_init;
+ card->codec_conf = max98390_codec_conf;
+ card->num_configs = ARRAY_SIZE(max98390_codec_conf);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mt8195_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ struct mt8195_mt6359_priv *mach_priv;
+ struct snd_soc_dai_link *dai_link;
+ u8 codec_init = 0;
+ int i;
+
+ mach_priv = devm_kzalloc(card->dev, sizeof(*mach_priv), GFP_KERNEL);
+ if (!mach_priv)
+ return -ENOMEM;
+
+ soc_card_data->mach_priv = mach_priv;
+
+ if (legacy)
+ return mt8195_mt6359_legacy_probe(soc_card_data);
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "DPTX_BE") == 0) {
+ if (dai_link->num_codecs &&
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
+ dai_link->init = mt8195_dptx_codec_init;
+ } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ if (dai_link->num_codecs &&
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
+ dai_link->init = mt8195_hdmi_codec_init;
+ } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC1_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC2_BE") == 0) {
+ if (!(codec_init & MT6359_CODEC_INIT)) {
+ dai_link->init = mt8195_mt6359_init;
+ codec_init |= MT6359_CODEC_INIT;
+ }
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM1_IN_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ if (!dai_link->num_codecs)
+ continue;
+
+ if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) {
+ if (!(codec_init & MAX98390_CODEC_INIT)) {
+ dai_link->init = mt8195_max98390_init;
+ codec_init |= MAX98390_CODEC_INIT;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT1011_CODEC_DAI)) {
+ dai_link->ops = &mt8195_rt1011_etdm_ops;
+ if (!(codec_init & RT1011_CODEC_INIT)) {
+ dai_link->init = mt8195_rt1011_init;
+ codec_init |= RT1011_CODEC_INIT;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT1019_CODEC_DAI)) {
+ if (!(codec_init & RT1019_CODEC_INIT)) {
+ dai_link->init = mt8195_rt1019_init;
+ codec_init |= RT1019_CODEC_INIT;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5682_CODEC_DAI) ||
+ !strcmp(dai_link->codecs->dai_name, RT5682S_CODEC_DAI)) {
+ dai_link->ops = &mt8195_rt5682_etdm_ops;
+ if (!(codec_init & RT5682_CODEC_INIT)) {
+ dai_link->init = mt8195_rt5682_init;
+ codec_init |= RT5682_CODEC_INIT;
+ }
+ } else {
+ if (!snd_soc_dlc_is_dummy(dai_link->codecs)) {
+ if (!(codec_init & DUMB_CODEC_INIT)) {
+ dai_link->init = mt8195_dumb_amp_init;
+ codec_init |= DUMB_CODEC_INIT;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const unsigned int mt8195_pcm_playback_channels[] = { 2 };
+static const unsigned int mt8195_pcm_capture_channels[] = { 1, 2 };
+static const unsigned int mt8195_pcm_hdmidp_channels[] = { 2, 4, 6, 8 };
+static const unsigned int mt8195_pcm_rates[] = { 48000 };
+
+static const struct snd_pcm_hw_constraint_list mt8195_rate_constraint = {
+ .list = mt8195_pcm_rates,
+ .count = ARRAY_SIZE(mt8195_pcm_rates)
+};
+
+static const struct mtk_pcm_constraints_data mt8195_pcm_constraints[MTK_CONSTRAINT_HDMIDP + 1] = {
+ [MTK_CONSTRAINT_PLAYBACK] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8195_pcm_playback_channels,
+ .count = ARRAY_SIZE(mt8195_pcm_playback_channels)
+ },
+ .rates = &mt8195_rate_constraint,
+ },
+ [MTK_CONSTRAINT_CAPTURE] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8195_pcm_capture_channels,
+ .count = ARRAY_SIZE(mt8195_pcm_capture_channels)
+ },
+ .rates = &mt8195_rate_constraint,
+ },
+ [MTK_CONSTRAINT_HDMIDP] = {
+ .channels = &(const struct snd_pcm_hw_constraint_list) {
+ .list = mt8195_pcm_hdmidp_channels,
+ .count = ARRAY_SIZE(mt8195_pcm_hdmidp_channels)
+ },
+ .rates = &mt8195_rate_constraint,
+ },
+};
+
+static const struct mtk_sof_priv mt8195_sof_priv = {
+ .conn_streams = g_sof_conn_streams,
+ .num_streams = ARRAY_SIZE(g_sof_conn_streams),
+ .sof_dai_link_fixup = mt8195_dai_link_fixup
+};
+
+static const struct mtk_soundcard_pdata mt8195_mt6359_rt1019_rt5682_card = {
+ .card_name = "mt8195_r1019_5682",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8195_mt6359_soc_card,
+ .num_jacks = MT8195_JACK_MAX,
+ .pcm_constraints = mt8195_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8195_pcm_constraints),
+ .flags = RT1019_SPEAKER_AMP_PRESENT
+ },
+ .sof_priv = &mt8195_sof_priv,
+ .soc_probe = mt8195_mt6359_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8195_mt6359_rt1011_rt5682_card = {
+ .card_name = "mt8195_r1011_5682",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8195_mt6359_soc_card,
+ .num_jacks = MT8195_JACK_MAX,
+ .pcm_constraints = mt8195_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8195_pcm_constraints),
+ .flags = RT1011_SPEAKER_AMP_PRESENT
+ },
+ .sof_priv = &mt8195_sof_priv,
+ .soc_probe = mt8195_mt6359_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8195_mt6359_max98390_rt5682_card = {
+ .card_name = "mt8195_m98390_r5682",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8195_mt6359_soc_card,
+ .num_jacks = MT8195_JACK_MAX,
+ .pcm_constraints = mt8195_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8195_pcm_constraints),
+ .flags = MAX98390_SPEAKER_AMP_PRESENT
+ },
+ .sof_priv = &mt8195_sof_priv,
+ .soc_probe = mt8195_mt6359_soc_card_probe
+};
+
+static const struct mtk_soundcard_pdata mt8195_mt6359_card = {
+ .card_name = "mt8195_mt6359",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8195_mt6359_soc_card,
+ .num_jacks = MT8195_JACK_MAX,
+ .pcm_constraints = mt8195_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8195_pcm_constraints),
+ },
+ .sof_priv = &mt8195_sof_priv,
+ .soc_probe = mt8195_mt6359_soc_card_probe
+};
+
+static const struct of_device_id mt8195_mt6359_dt_match[] = {
+ {
+ .compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",
+ .data = &mt8195_mt6359_rt1019_rt5682_card,
+ },
+ {
+ .compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",
+ .data = &mt8195_mt6359_rt1011_rt5682_card,
+ },
+ {
+ .compatible = "mediatek,mt8195_mt6359_max98390_rt5682",
+ .data = &mt8195_mt6359_max98390_rt5682_card,
+ },
+ {
+ .compatible = "mediatek,mt8195_mt6359",
+ .data = &mt8195_mt6359_card,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8195_mt6359_dt_match);
+
+static struct platform_driver mt8195_mt6359_driver = {
+ .driver = {
+ .name = "mt8195_mt6359",
+ .of_match_table = mt8195_mt6359_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+
+module_platform_driver(mt8195_mt6359_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8195-MT6359 ALSA SoC machine driver");
+MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
+MODULE_AUTHOR("YC Hung <yc.hung@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mt8195_mt6359 soc card");
diff --git a/sound/soc/mediatek/mt8195/mt8195-reg.h b/sound/soc/mediatek/mt8195/mt8195-reg.h
new file mode 100644
index 000000000000..d3871353db41
--- /dev/null
+++ b/sound/soc/mediatek/mt8195/mt8195-reg.h
@@ -0,0 +1,2797 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8195-reg.h -- Mediatek 8195 audio driver reg definition
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MT8195_REG_H_
+#define _MT8195_REG_H_
+
+#define AFE_SRAM_BASE (0x10880000)
+#define AFE_SRAM_SIZE (0x10000)
+
+#define AUDIO_TOP_CON0 (0x0000)
+#define AUDIO_TOP_CON1 (0x0004)
+#define AUDIO_TOP_CON2 (0x0008)
+#define AUDIO_TOP_CON3 (0x000c)
+#define AUDIO_TOP_CON4 (0x0010)
+#define AUDIO_TOP_CON5 (0x0014)
+#define AUDIO_TOP_CON6 (0x0018)
+#define AFE_MAS_HADDR_MSB (0x0020)
+#define PWR1_ASM_CON1 (0x0108)
+#define ASYS_IRQ_CONFIG (0x0110)
+#define ASYS_IRQ1_CON (0x0114)
+#define ASYS_IRQ2_CON (0x0118)
+#define ASYS_IRQ3_CON (0x011c)
+#define ASYS_IRQ4_CON (0x0120)
+#define ASYS_IRQ5_CON (0x0124)
+#define ASYS_IRQ6_CON (0x0128)
+#define ASYS_IRQ7_CON (0x012c)
+#define ASYS_IRQ8_CON (0x0130)
+#define ASYS_IRQ9_CON (0x0134)
+#define ASYS_IRQ10_CON (0x0138)
+#define ASYS_IRQ11_CON (0x013c)
+#define ASYS_IRQ12_CON (0x0140)
+#define ASYS_IRQ13_CON (0x0144)
+#define ASYS_IRQ14_CON (0x0148)
+#define ASYS_IRQ15_CON (0x014c)
+#define ASYS_IRQ16_CON (0x0150)
+#define ASYS_IRQ_CLR (0x0154)
+#define ASYS_IRQ_STATUS (0x0158)
+#define ASYS_IRQ_MON1 (0x015c)
+#define ASYS_IRQ_MON2 (0x0160)
+#define AFE_IRQ1_CON (0x0164)
+#define AFE_IRQ2_CON (0x0168)
+#define AFE_IRQ3_CON (0x016c)
+#define AFE_IRQ_MCU_CLR (0x0170)
+#define AFE_IRQ_STATUS (0x0174)
+#define AFE_IRQ_MASK (0x0178)
+#define ASYS_IRQ_MASK (0x017c)
+#define AFE_IRQ3_CON_MON (0x01b0)
+#define AFE_IRQ_MCU_MON2 (0x01b4)
+#define AFE_IRQ8_CON (0x01b8)
+#define AFE_IRQ9_CON (0x01bc)
+#define AFE_IRQ10_CON (0x01c0)
+#define AFE_IRQ9_CON_MON (0x01c4)
+#define ADSP_IRQ_MASK (0x01c8)
+#define ADSP_IRQ_STATUS (0x01cc)
+#define AFE_SINEGEN_CON0 (0x01f0)
+#define AFE_SINEGEN_CON1 (0x01f4)
+#define AFE_SINEGEN_CON2 (0x01f8)
+#define AFE_SINEGEN_CON3 (0x01fc)
+#define AFE_SPDIF_OUT_CON0 (0x0380)
+#define AFE_TDMOUT_CONN0 (0x0390)
+#define PWR1_ASM_CON2 (0x03b0)
+#define PWR1_ASM_CON3 (0x03b4)
+#define PWR1_ASM_CON4 (0x03b8)
+#define AFE_APLL_TUNER_CFG (0x03f8)
+#define AFE_APLL_TUNER_CFG1 (0x03fc)
+#define AUDIO_TOP_STA0 (0x0400)
+#define AUDIO_TOP_STA1 (0x0404)
+#define AFE_GAIN1_CON0 (0x0410)
+#define AFE_GAIN1_CON1 (0x0414)
+#define AFE_GAIN1_CON2 (0x0418)
+#define AFE_GAIN1_CON3 (0x041c)
+#define AFE_GAIN1_CUR (0x0424)
+#define AFE_GAIN2_CON0 (0x0428)
+#define AFE_GAIN2_CON1 (0x042c)
+#define AFE_GAIN2_CON2 (0x0430)
+#define AFE_GAIN2_CON3 (0x0434)
+#define AFE_GAIN2_CUR (0x043c)
+#define AFE_IEC_CFG (0x0480)
+#define AFE_IEC_NSNUM (0x0484)
+#define AFE_IEC_BURST_INFO (0x0488)
+#define AFE_IEC_BURST_LEN (0x048c)
+#define AFE_IEC_NSADR (0x0490)
+#define AFE_IEC_CHL_STAT0 (0x04a0)
+#define AFE_IEC_CHL_STAT1 (0x04a4)
+#define AFE_IEC_CHR_STAT0 (0x04a8)
+#define AFE_IEC_CHR_STAT1 (0x04ac)
+#define AFE_SPDIFIN_CFG0 (0x0500)
+#define AFE_SPDIFIN_CFG1 (0x0504)
+#define AFE_SPDIFIN_CHSTS1 (0x0508)
+#define AFE_SPDIFIN_CHSTS2 (0x050c)
+#define AFE_SPDIFIN_CHSTS3 (0x0510)
+#define AFE_SPDIFIN_CHSTS4 (0x0514)
+#define AFE_SPDIFIN_CHSTS5 (0x0518)
+#define AFE_SPDIFIN_CHSTS6 (0x051c)
+#define AFE_SPDIFIN_DEBUG1 (0x0520)
+#define AFE_SPDIFIN_DEBUG2 (0x0524)
+#define AFE_SPDIFIN_DEBUG3 (0x0528)
+#define AFE_SPDIFIN_DEBUG4 (0x052c)
+#define AFE_SPDIFIN_EC (0x0530)
+#define AFE_SPDIFIN_CKLOCK_CFG (0x0534)
+#define AFE_SPDIFIN_BR (0x053c)
+#define AFE_SPDIFIN_BR_DBG1 (0x0540)
+#define AFE_SPDIFIN_CKFBDIV (0x0544)
+#define AFE_SPDIFIN_INT_EXT (0x0548)
+#define AFE_SPDIFIN_INT_EXT2 (0x054c)
+#define SPDIFIN_FREQ_INFO (0x0550)
+#define SPDIFIN_FREQ_INFO_2 (0x0554)
+#define SPDIFIN_FREQ_INFO_3 (0x0558)
+#define SPDIFIN_FREQ_STATUS (0x055c)
+#define SPDIFIN_USERCODE1 (0x0560)
+#define SPDIFIN_USERCODE2 (0x0564)
+#define SPDIFIN_USERCODE3 (0x0568)
+#define SPDIFIN_USERCODE4 (0x056c)
+#define SPDIFIN_USERCODE5 (0x0570)
+#define SPDIFIN_USERCODE6 (0x0574)
+#define SPDIFIN_USERCODE7 (0x0578)
+#define SPDIFIN_USERCODE8 (0x057c)
+#define SPDIFIN_USERCODE9 (0x0580)
+#define SPDIFIN_USERCODE10 (0x0584)
+#define SPDIFIN_USERCODE11 (0x0588)
+#define SPDIFIN_USERCODE12 (0x058c)
+#define AFE_SPDIFIN_APLL_TUNER_CFG (0x0594)
+#define AFE_SPDIFIN_APLL_TUNER_CFG1 (0x0598)
+#define ASYS_TOP_CON (0x0600)
+#define AFE_LINEIN_APLL_TUNER_CFG (0x0610)
+#define AFE_LINEIN_APLL_TUNER_MON (0x0614)
+#define AFE_EARC_APLL_TUNER_CFG (0x0618)
+#define AFE_EARC_APLL_TUNER_MON (0x061c)
+#define PWR2_TOP_CON0 (0x0634)
+#define PWR2_TOP_CON1 (0x0638)
+#define PCM_INTF_CON1 (0x063c)
+#define PCM_INTF_CON2 (0x0640)
+#define AFE_CM0_CON (0x0660)
+#define AFE_CM1_CON (0x0664)
+#define AFE_CM2_CON (0x0668)
+#define AFE_CM0_MON (0x0670)
+#define AFE_CM1_MON (0x0674)
+#define AFE_CM2_MON (0x0678)
+#define AFE_MPHONE_MULTI_CON0 (0x06a4)
+#define AFE_MPHONE_MULTI_CON1 (0x06a8)
+#define AFE_MPHONE_MULTI_CON2 (0x06ac)
+#define AFE_MPHONE_MULTI_MON (0x06b0)
+#define AFE_MPHONE_MULTI_DET_REG_CON0 (0x06b4)
+#define AFE_MPHONE_MULTI_DET_REG_CON1 (0x06b8)
+#define AFE_MPHONE_MULTI_DET_REG_CON2 (0x06bc)
+#define AFE_MPHONE_MULTI_DET_REG_CON3 (0x06c0)
+#define AFE_MPHONE_MULTI_DET_MON0 (0x06c4)
+#define AFE_MPHONE_MULTI_DET_MON1 (0x06c8)
+#define AFE_MPHONE_MULTI_DET_MON2 (0x06d0)
+#define AFE_MPHONE_MULTI2_CON0 (0x06d4)
+#define AFE_MPHONE_MULTI2_CON1 (0x06d8)
+#define AFE_MPHONE_MULTI2_CON2 (0x06dc)
+#define AFE_MPHONE_MULTI2_MON (0x06e0)
+#define AFE_MPHONE_MULTI2_DET_REG_CON0 (0x06e4)
+#define AFE_MPHONE_MULTI2_DET_REG_CON1 (0x06e8)
+#define AFE_MPHONE_MULTI2_DET_REG_CON2 (0x06ec)
+#define AFE_MPHONE_MULTI2_DET_REG_CON3 (0x06f0)
+#define AFE_MPHONE_MULTI2_DET_MON0 (0x06f4)
+#define AFE_MPHONE_MULTI2_DET_MON1 (0x06f8)
+#define AFE_MPHONE_MULTI2_DET_MON2 (0x06fc)
+#define AFE_ADDA_IIR_COEF_02_01 (0x0700)
+#define AFE_ADDA_IIR_COEF_04_03 (0x0704)
+#define AFE_ADDA_IIR_COEF_06_05 (0x0708)
+#define AFE_ADDA_IIR_COEF_08_07 (0x070c)
+#define AFE_ADDA_IIR_COEF_10_09 (0x0710)
+#define AFE_ADDA_ULCF_CFG_02_01 (0x0714)
+#define AFE_ADDA_ULCF_CFG_04_03 (0x0718)
+#define AFE_ADDA_ULCF_CFG_06_05 (0x071c)
+#define AFE_ADDA_ULCF_CFG_08_07 (0x0720)
+#define AFE_ADDA_ULCF_CFG_10_09 (0x0724)
+#define AFE_ADDA_ULCF_CFG_12_11 (0x0728)
+#define AFE_ADDA_ULCF_CFG_14_13 (0x072c)
+#define AFE_ADDA_ULCF_CFG_16_15 (0x0730)
+#define AFE_ADDA_ULCF_CFG_18_17 (0x0734)
+#define AFE_ADDA_ULCF_CFG_20_19 (0x0738)
+#define AFE_ADDA_ULCF_CFG_22_21 (0x073c)
+#define AFE_ADDA_ULCF_CFG_24_23 (0x0740)
+#define AFE_ADDA_ULCF_CFG_26_25 (0x0744)
+#define AFE_ADDA_ULCF_CFG_28_27 (0x0748)
+#define AFE_ADDA_ULCF_CFG_30_29 (0x074c)
+#define AFE_ADDA6_IIR_COEF_02_01 (0x0750)
+#define AFE_ADDA6_IIR_COEF_04_03 (0x0754)
+#define AFE_ADDA6_IIR_COEF_06_05 (0x0758)
+#define AFE_ADDA6_IIR_COEF_08_07 (0x075c)
+#define AFE_ADDA6_IIR_COEF_10_09 (0x0760)
+#define AFE_ADDA6_ULCF_CFG_02_01 (0x0764)
+#define AFE_ADDA6_ULCF_CFG_04_03 (0x0768)
+#define AFE_ADDA6_ULCF_CFG_06_05 (0x076c)
+#define AFE_ADDA6_ULCF_CFG_08_07 (0x0770)
+#define AFE_ADDA6_ULCF_CFG_10_09 (0x0774)
+#define AFE_ADDA6_ULCF_CFG_12_11 (0x0778)
+#define AFE_ADDA6_ULCF_CFG_14_13 (0x077c)
+#define AFE_ADDA6_ULCF_CFG_16_15 (0x0780)
+#define AFE_ADDA6_ULCF_CFG_18_17 (0x0784)
+#define AFE_ADDA6_ULCF_CFG_20_19 (0x0788)
+#define AFE_ADDA6_ULCF_CFG_22_21 (0x078c)
+#define AFE_ADDA6_ULCF_CFG_24_23 (0x0790)
+#define AFE_ADDA6_ULCF_CFG_26_25 (0x0794)
+#define AFE_ADDA6_ULCF_CFG_28_27 (0x0798)
+#define AFE_ADDA6_ULCF_CFG_30_29 (0x079c)
+#define AFE_ADDA_MTKAIF_CFG0 (0x07a0)
+#define AFE_ADDA_MTKAIF_SYNCWORD_CFG (0x07a8)
+#define AFE_ADDA_MTKAIF_RX_CFG0 (0x07b4)
+#define AFE_ADDA_MTKAIF_RX_CFG1 (0x07b8)
+#define AFE_ADDA_MTKAIF_RX_CFG2 (0x07bc)
+#define AFE_ADDA_MTKAIF_MON0 (0x07c8)
+#define AFE_ADDA_MTKAIF_MON1 (0x07cc)
+#define AFE_AUD_PAD_TOP (0x07d4)
+#define AFE_ADDA6_MTKAIF_MON0 (0x07d8)
+#define AFE_ADDA6_MTKAIF_MON1 (0x07dc)
+#define AFE_ADDA6_MTKAIF_CFG0 (0x07e0)
+#define AFE_ADDA6_MTKAIF_RX_CFG0 (0x07e4)
+#define AFE_ADDA6_MTKAIF_RX_CFG1 (0x07e8)
+#define AFE_ADDA6_MTKAIF_RX_CFG2 (0x07ec)
+#define AFE_ADDA6_TOP_CON0 (0x07f0)
+#define AFE_ADDA6_UL_SRC_CON0 (0x07f4)
+#define AFE_ADDA6_UL_SRC_CON1 (0x07f8)
+#define AFE_ADDA6_SRC_DEBUG (0x0800)
+#define AFE_ADDA6_SRC_DEBUG_MON0 (0x0804)
+#define AFE_ADDA6_UL_SRC_MON0 (0x0818)
+#define AFE_ADDA6_UL_SRC_MON1 (0x081c)
+#define AFE_CONN0_5 (0x0830)
+#define AFE_CONN1_5 (0x0834)
+#define AFE_CONN2_5 (0x0838)
+#define AFE_CONN3_5 (0x083c)
+#define AFE_CONN4_5 (0x0840)
+#define AFE_CONN5_5 (0x0844)
+#define AFE_CONN6_5 (0x0848)
+#define AFE_CONN7_5 (0x084c)
+#define AFE_CONN8_5 (0x0850)
+#define AFE_CONN9_5 (0x0854)
+#define AFE_CONN10_5 (0x0858)
+#define AFE_CONN11_5 (0x085c)
+#define AFE_CONN12_5 (0x0860)
+#define AFE_CONN13_5 (0x0864)
+#define AFE_CONN14_5 (0x0868)
+#define AFE_CONN15_5 (0x086c)
+#define AFE_CONN16_5 (0x0870)
+#define AFE_CONN17_5 (0x0874)
+#define AFE_CONN18_5 (0x0878)
+#define AFE_CONN19_5 (0x087c)
+#define AFE_CONN20_5 (0x0880)
+#define AFE_CONN21_5 (0x0884)
+#define AFE_CONN22_5 (0x0888)
+#define AFE_CONN23_5 (0x088c)
+#define AFE_CONN24_5 (0x0890)
+#define AFE_CONN25_5 (0x0894)
+#define AFE_CONN26_5 (0x0898)
+#define AFE_CONN27_5 (0x089c)
+#define AFE_CONN28_5 (0x08a0)
+#define AFE_CONN29_5 (0x08a4)
+#define AFE_CONN30_5 (0x08a8)
+#define AFE_CONN31_5 (0x08ac)
+#define AFE_CONN32_5 (0x08b0)
+#define AFE_CONN33_5 (0x08b4)
+#define AFE_CONN34_5 (0x08b8)
+#define AFE_CONN35_5 (0x08bc)
+#define AFE_CONN36_5 (0x08c0)
+#define AFE_CONN37_5 (0x08c4)
+#define AFE_CONN38_5 (0x08c8)
+#define AFE_CONN39_5 (0x08cc)
+#define AFE_CONN40_5 (0x08d0)
+#define AFE_CONN41_5 (0x08d4)
+#define AFE_CONN42_5 (0x08d8)
+#define AFE_CONN43_5 (0x08dc)
+#define AFE_CONN44_5 (0x08e0)
+#define AFE_CONN45_5 (0x08e4)
+#define AFE_CONN46_5 (0x08e8)
+#define AFE_CONN47_5 (0x08ec)
+#define AFE_CONN48_5 (0x08f0)
+#define AFE_CONN49_5 (0x08f4)
+#define AFE_CONN50_5 (0x08f8)
+#define AFE_CONN51_5 (0x08fc)
+#define AFE_CONN52_5 (0x0900)
+#define AFE_CONN53_5 (0x0904)
+#define AFE_CONN54_5 (0x0908)
+#define AFE_CONN55_5 (0x090c)
+#define AFE_CONN56_5 (0x0910)
+#define AFE_CONN57_5 (0x0914)
+#define AFE_CONN58_5 (0x0918)
+#define AFE_CONN59_5 (0x091c)
+#define AFE_CONN60_5 (0x0920)
+#define AFE_CONN61_5 (0x0924)
+#define AFE_CONN62_5 (0x0928)
+#define AFE_CONN63_5 (0x092c)
+#define AFE_CONN64_5 (0x0930)
+#define AFE_CONN65_5 (0x0934)
+#define AFE_CONN66_5 (0x0938)
+#define AFE_CONN67_5 (0x093c)
+#define AFE_CONN68_5 (0x0940)
+#define AFE_CONN69_5 (0x0944)
+#define AFE_CONN70_5 (0x0948)
+#define AFE_CONN71_5 (0x094c)
+#define AFE_CONN72_5 (0x0950)
+#define AFE_CONN73_5 (0x0954)
+#define AFE_CONN74_5 (0x0958)
+#define AFE_CONN75_5 (0x095c)
+#define AFE_CONN76_5 (0x0960)
+#define AFE_CONN77_5 (0x0964)
+#define AFE_CONN78_5 (0x0968)
+#define AFE_CONN79_5 (0x096c)
+#define AFE_CONN80_5 (0x0970)
+#define AFE_CONN81_5 (0x0974)
+#define AFE_CONN82_5 (0x0978)
+#define AFE_CONN83_5 (0x097c)
+#define AFE_CONN84_5 (0x0980)
+#define AFE_CONN85_5 (0x0984)
+#define AFE_CONN86_5 (0x0988)
+#define AFE_CONN87_5 (0x098c)
+#define AFE_CONN88_5 (0x0990)
+#define AFE_CONN89_5 (0x0994)
+#define AFE_CONN90_5 (0x0998)
+#define AFE_CONN91_5 (0x099c)
+#define AFE_CONN92_5 (0x09a0)
+#define AFE_CONN93_5 (0x09a4)
+#define AFE_CONN94_5 (0x09a8)
+#define AFE_CONN95_5 (0x09ac)
+#define AFE_CONN96_5 (0x09b0)
+#define AFE_CONN97_5 (0x09b4)
+#define AFE_CONN98_5 (0x09b8)
+#define AFE_CONN99_5 (0x09bc)
+#define AFE_CONN100_5 (0x09c0)
+#define AFE_CONN101_5 (0x09c4)
+#define AFE_CONN102_5 (0x09c8)
+#define AFE_CONN103_5 (0x09cc)
+#define AFE_CONN104_5 (0x09d0)
+#define AFE_CONN105_5 (0x09d4)
+#define AFE_CONN106_5 (0x09d8)
+#define AFE_CONN107_5 (0x09dc)
+#define AFE_CONN108_5 (0x09e0)
+#define AFE_CONN109_5 (0x09e4)
+#define AFE_CONN110_5 (0x09e8)
+#define AFE_CONN111_5 (0x09ec)
+#define AFE_CONN112_5 (0x09f0)
+#define AFE_CONN113_5 (0x09f4)
+#define AFE_CONN114_5 (0x09f8)
+#define AFE_CONN115_5 (0x09fc)
+#define AFE_CONN116_5 (0x0a00)
+#define AFE_CONN117_5 (0x0a04)
+#define AFE_CONN118_5 (0x0a08)
+#define AFE_CONN119_5 (0x0a0c)
+#define AFE_CONN120_5 (0x0a10)
+#define AFE_CONN121_5 (0x0a14)
+#define AFE_CONN122_5 (0x0a18)
+#define AFE_CONN123_5 (0x0a1c)
+#define AFE_CONN124_5 (0x0a20)
+#define AFE_CONN125_5 (0x0a24)
+#define AFE_CONN126_5 (0x0a28)
+#define AFE_CONN127_5 (0x0a2c)
+#define AFE_CONN128_5 (0x0a30)
+#define AFE_CONN129_5 (0x0a34)
+#define AFE_CONN130_5 (0x0a38)
+#define AFE_CONN131_5 (0x0a3c)
+#define AFE_CONN132_5 (0x0a40)
+#define AFE_CONN133_5 (0x0a44)
+#define AFE_CONN134_5 (0x0a48)
+#define AFE_CONN135_5 (0x0a4c)
+#define AFE_CONN136_5 (0x0a50)
+#define AFE_CONN137_5 (0x0a54)
+#define AFE_CONN138_5 (0x0a58)
+#define AFE_CONN139_5 (0x0a5c)
+#define AFE_CONN_RS_5 (0x0a60)
+#define AFE_CONN_DI_5 (0x0a64)
+#define AFE_CONN_16BIT_5 (0x0a68)
+#define AFE_CONN_24BIT_5 (0x0a6c)
+#define AFE_ASRC11_NEW_CON0 (0x0d80)
+#define AFE_ASRC11_NEW_CON1 (0x0d84)
+#define AFE_ASRC11_NEW_CON2 (0x0d88)
+#define AFE_ASRC11_NEW_CON3 (0x0d8c)
+#define AFE_ASRC11_NEW_CON4 (0x0d90)
+#define AFE_ASRC11_NEW_CON5 (0x0d94)
+#define AFE_ASRC11_NEW_CON6 (0x0d98)
+#define AFE_ASRC11_NEW_CON7 (0x0d9c)
+#define AFE_ASRC11_NEW_CON8 (0x0da0)
+#define AFE_ASRC11_NEW_CON9 (0x0da4)
+#define AFE_ASRC11_NEW_CON10 (0x0da8)
+#define AFE_ASRC11_NEW_CON11 (0x0dac)
+#define AFE_ASRC11_NEW_CON13 (0x0db4)
+#define AFE_ASRC11_NEW_CON14 (0x0db8)
+#define AFE_ASRC12_NEW_CON0 (0x0dc0)
+#define AFE_ASRC12_NEW_CON1 (0x0dc4)
+#define AFE_ASRC12_NEW_CON2 (0x0dc8)
+#define AFE_ASRC12_NEW_CON3 (0x0dcc)
+#define AFE_ASRC12_NEW_CON4 (0x0dd0)
+#define AFE_ASRC12_NEW_CON5 (0x0dd4)
+#define AFE_ASRC12_NEW_CON6 (0x0dd8)
+#define AFE_ASRC12_NEW_CON7 (0x0ddc)
+#define AFE_ASRC12_NEW_CON8 (0x0de0)
+#define AFE_ASRC12_NEW_CON9 (0x0de4)
+#define AFE_ASRC12_NEW_CON10 (0x0de8)
+#define AFE_ASRC12_NEW_CON11 (0x0dec)
+#define AFE_ASRC12_NEW_CON13 (0x0df4)
+#define AFE_ASRC12_NEW_CON14 (0x0df8)
+#define AFE_LRCK_CNT (0x1018)
+#define AFE_DAC_CON0 (0x1200)
+#define AFE_DAC_CON1 (0x1204)
+#define AFE_DAC_CON2 (0x1208)
+#define AFE_DAC_MON0 (0x1218)
+#define AFE_DL2_BASE (0x1250)
+#define AFE_DL2_CUR (0x1254)
+#define AFE_DL2_END (0x1258)
+#define AFE_DL2_CON0 (0x125c)
+#define AFE_DL3_BASE (0x1260)
+#define AFE_DL3_CUR (0x1264)
+#define AFE_DL3_END (0x1268)
+#define AFE_DL3_CON0 (0x126c)
+#define AFE_DL6_BASE (0x1290)
+#define AFE_DL6_CUR (0x1294)
+#define AFE_DL6_END (0x1298)
+#define AFE_DL6_CON0 (0x129c)
+#define AFE_DL7_BASE (0x12a0)
+#define AFE_DL7_CUR (0x12a4)
+#define AFE_DL7_END (0x12a8)
+#define AFE_DL7_CON0 (0x12ac)
+#define AFE_DL8_BASE (0x12b0)
+#define AFE_DL8_CUR (0x12b4)
+#define AFE_DL8_END (0x12b8)
+#define AFE_DL8_CON0 (0x12bc)
+#define AFE_DL10_BASE (0x12d0)
+#define AFE_DL10_CUR (0x12d4)
+#define AFE_DL10_END (0x12d8)
+#define AFE_DL10_CON0 (0x12dc)
+#define AFE_DL11_BASE (0x12e0)
+#define AFE_DL11_CUR (0x12e4)
+#define AFE_DL11_END (0x12e8)
+#define AFE_DL11_CON0 (0x12ec)
+#define AFE_UL1_BASE (0x1300)
+#define AFE_UL1_CUR (0x1304)
+#define AFE_UL1_END (0x1308)
+#define AFE_UL1_CON0 (0x130c)
+#define AFE_UL2_BASE (0x1310)
+#define AFE_UL2_CUR (0x1314)
+#define AFE_UL2_END (0x1318)
+#define AFE_UL2_CON0 (0x131c)
+#define AFE_UL3_BASE (0x1320)
+#define AFE_UL3_CUR (0x1324)
+#define AFE_UL3_END (0x1328)
+#define AFE_UL3_CON0 (0x132c)
+#define AFE_UL4_BASE (0x1330)
+#define AFE_UL4_CUR (0x1334)
+#define AFE_UL4_END (0x1338)
+#define AFE_UL4_CON0 (0x133c)
+#define AFE_UL5_BASE (0x1340)
+#define AFE_UL5_CUR (0x1344)
+#define AFE_UL5_END (0x1348)
+#define AFE_UL5_CON0 (0x134c)
+#define AFE_UL6_BASE (0x1350)
+#define AFE_UL6_CUR (0x1354)
+#define AFE_UL6_END (0x1358)
+#define AFE_UL6_CON0 (0x135c)
+#define AFE_UL8_BASE (0x1370)
+#define AFE_UL8_CUR (0x1374)
+#define AFE_UL8_END (0x1378)
+#define AFE_UL8_CON0 (0x137c)
+#define AFE_UL9_BASE (0x1380)
+#define AFE_UL9_CUR (0x1384)
+#define AFE_UL9_END (0x1388)
+#define AFE_UL9_CON0 (0x138c)
+#define AFE_UL10_BASE (0x13d0)
+#define AFE_UL10_CUR (0x13d4)
+#define AFE_UL10_END (0x13d8)
+#define AFE_UL10_CON0 (0x13dc)
+#define AFE_DL8_CHK_SUM1 (0x1400)
+#define AFE_DL8_CHK_SUM2 (0x1404)
+#define AFE_DL8_CHK_SUM3 (0x1408)
+#define AFE_DL8_CHK_SUM4 (0x140c)
+#define AFE_DL8_CHK_SUM5 (0x1410)
+#define AFE_DL8_CHK_SUM6 (0x1414)
+#define AFE_DL10_CHK_SUM1 (0x1418)
+#define AFE_DL10_CHK_SUM2 (0x141c)
+#define AFE_DL10_CHK_SUM3 (0x1420)
+#define AFE_DL10_CHK_SUM4 (0x1424)
+#define AFE_DL10_CHK_SUM5 (0x1428)
+#define AFE_DL10_CHK_SUM6 (0x142c)
+#define AFE_DL11_CHK_SUM1 (0x1430)
+#define AFE_DL11_CHK_SUM2 (0x1434)
+#define AFE_DL11_CHK_SUM3 (0x1438)
+#define AFE_DL11_CHK_SUM4 (0x143c)
+#define AFE_DL11_CHK_SUM5 (0x1440)
+#define AFE_DL11_CHK_SUM6 (0x1444)
+#define AFE_UL1_CHK_SUM1 (0x1450)
+#define AFE_UL1_CHK_SUM2 (0x1454)
+#define AFE_UL2_CHK_SUM1 (0x1458)
+#define AFE_UL2_CHK_SUM2 (0x145c)
+#define AFE_UL3_CHK_SUM1 (0x1460)
+#define AFE_UL3_CHK_SUM2 (0x1464)
+#define AFE_UL4_CHK_SUM1 (0x1468)
+#define AFE_UL4_CHK_SUM2 (0x146c)
+#define AFE_UL5_CHK_SUM1 (0x1470)
+#define AFE_UL5_CHK_SUM2 (0x1474)
+#define AFE_UL6_CHK_SUM1 (0x1478)
+#define AFE_UL6_CHK_SUM2 (0x147c)
+#define AFE_UL8_CHK_SUM1 (0x1488)
+#define AFE_UL8_CHK_SUM2 (0x148c)
+#define AFE_DL2_CHK_SUM1 (0x14a0)
+#define AFE_DL2_CHK_SUM2 (0x14a4)
+#define AFE_DL3_CHK_SUM1 (0x14b0)
+#define AFE_DL3_CHK_SUM2 (0x14b4)
+#define AFE_DL6_CHK_SUM1 (0x14e0)
+#define AFE_DL6_CHK_SUM2 (0x14e4)
+#define AFE_DL7_CHK_SUM1 (0x14f0)
+#define AFE_DL7_CHK_SUM2 (0x14f4)
+#define AFE_UL9_CHK_SUM1 (0x1528)
+#define AFE_UL9_CHK_SUM2 (0x152c)
+#define AFE_BUS_MON1 (0x1540)
+#define UL1_MOD2AGT_CNT_LAT (0x1568)
+#define UL2_MOD2AGT_CNT_LAT (0x156c)
+#define UL3_MOD2AGT_CNT_LAT (0x1570)
+#define UL4_MOD2AGT_CNT_LAT (0x1574)
+#define UL5_MOD2AGT_CNT_LAT (0x1578)
+#define UL6_MOD2AGT_CNT_LAT (0x157c)
+#define UL8_MOD2AGT_CNT_LAT (0x1588)
+#define UL9_MOD2AGT_CNT_LAT (0x158c)
+#define UL10_MOD2AGT_CNT_LAT (0x1590)
+#define AFE_MEMIF_AGENT_FS_CON0 (0x15a0)
+#define AFE_MEMIF_AGENT_FS_CON1 (0x15a4)
+#define AFE_MEMIF_AGENT_FS_CON2 (0x15a8)
+#define AFE_MEMIF_AGENT_FS_CON3 (0x15ac)
+#define AFE_MEMIF_BURST_CFG (0x1600)
+#define AFE_MEMIF_BUF_FULL_MON (0x1610)
+#define AFE_MEMIF_BUF_MON1 (0x161c)
+#define AFE_MEMIF_BUF_MON3 (0x1624)
+#define AFE_MEMIF_BUF_MON4 (0x1628)
+#define AFE_MEMIF_BUF_MON5 (0x162c)
+#define AFE_MEMIF_BUF_MON6 (0x1630)
+#define AFE_MEMIF_BUF_MON7 (0x1634)
+#define AFE_MEMIF_BUF_MON8 (0x1638)
+#define AFE_MEMIF_BUF_MON9 (0x163c)
+#define AFE_MEMIF_BUF_MON10 (0x1640)
+#define DL2_AGENT2MODULE_CNT (0x1678)
+#define DL3_AGENT2MODULE_CNT (0x167c)
+#define DL6_AGENT2MODULE_CNT (0x1688)
+#define DL7_AGENT2MODULE_CNT (0x168c)
+#define DL8_AGENT2MODULE_CNT (0x1690)
+#define DL10_AGENT2MODULE_CNT (0x1698)
+#define DL11_AGENT2MODULE_CNT (0x169c)
+#define UL1_MODULE2AGENT_CNT (0x16a0)
+#define UL2_MODULE2AGENT_CNT (0x16a4)
+#define UL3_MODULE2AGENT_CNT (0x16a8)
+#define UL4_MODULE2AGENT_CNT (0x16ac)
+#define UL5_MODULE2AGENT_CNT (0x16b0)
+#define UL6_MODULE2AGENT_CNT (0x16b4)
+#define UL8_MODULE2AGENT_CNT (0x16bc)
+#define UL9_MODULE2AGENT_CNT (0x16c0)
+#define UL10_MODULE2AGENT_CNT (0x16c4)
+#define AFE_SECURE_CON2 (0x1798)
+#define AFE_SECURE_CON1 (0x179c)
+#define AFE_SECURE_CON (0x17a0)
+#define AFE_SRAM_BOUND (0x17a4)
+#define AFE_SE_SECURE_CON (0x17a8)
+#define AFE_SECURE_MASK_LOOPBACK (0x17bc)
+#define AFE_SECURE_SIDEBAND0 (0x1908)
+#define AFE_SECURE_SIDEBAND1 (0x190c)
+#define AFE_SECURE_SIDEBAND2 (0x1910)
+#define AFE_SECURE_SIDEBAND3 (0x1914)
+#define AFE_SECURE_MASK_BASE_ADR_MSB (0x1920)
+#define AFE_SECURE_MASK_END_ADR_MSB (0x1924)
+#define AFE_NORMAL_BASE_ADR_MSB (0x192c)
+#define AFE_NORMAL_END_ADR_MSB (0x1930)
+#define AFE_SECURE_MASK_LOOPBACK0 (0x1940)
+#define AFE_SECURE_MASK_LOOPBACK1 (0x1944)
+#define AFE_SECURE_MASK_LOOPBACK2 (0x1948)
+#define AFE_LOOPBACK_CFG0 (0x1950)
+#define AFE_LOOPBACK_CFG1 (0x1954)
+#define AFE_LOOPBACK_CFG2 (0x1958)
+#define AFE_DMIC0_UL_SRC_CON0 (0x1a00)
+#define AFE_DMIC0_UL_SRC_CON1 (0x1a04)
+#define AFE_DMIC0_SRC_DEBUG (0x1a08)
+#define AFE_DMIC0_SRC_DEBUG_MON0 (0x1a0c)
+#define AFE_DMIC0_UL_SRC_MON0 (0x1a10)
+#define AFE_DMIC0_UL_SRC_MON1 (0x1a14)
+#define AFE_DMIC0_IIR_COEF_02_01 (0x1a18)
+#define AFE_DMIC0_IIR_COEF_04_03 (0x1a1c)
+#define AFE_DMIC0_IIR_COEF_06_05 (0x1a20)
+#define AFE_DMIC0_IIR_COEF_08_07 (0x1a24)
+#define AFE_DMIC0_IIR_COEF_10_09 (0x1a28)
+#define AFE_DMIC1_UL_SRC_CON0 (0x1a68)
+#define AFE_DMIC1_UL_SRC_CON1 (0x1a6c)
+#define AFE_DMIC1_SRC_DEBUG (0x1a70)
+#define AFE_DMIC1_SRC_DEBUG_MON0 (0x1a74)
+#define AFE_DMIC1_UL_SRC_MON0 (0x1a78)
+#define AFE_DMIC1_UL_SRC_MON1 (0x1a7c)
+#define AFE_DMIC1_IIR_COEF_02_01 (0x1a80)
+#define AFE_DMIC1_IIR_COEF_04_03 (0x1a84)
+#define AFE_DMIC1_IIR_COEF_06_05 (0x1a88)
+#define AFE_DMIC1_IIR_COEF_08_07 (0x1a8c)
+#define AFE_DMIC1_IIR_COEF_10_09 (0x1a90)
+#define AFE_DMIC2_UL_SRC_CON0 (0x1ad0)
+#define AFE_DMIC2_UL_SRC_CON1 (0x1ad4)
+#define AFE_DMIC2_SRC_DEBUG (0x1ad8)
+#define AFE_DMIC2_SRC_DEBUG_MON0 (0x1adc)
+#define AFE_DMIC2_UL_SRC_MON0 (0x1ae0)
+#define AFE_DMIC2_UL_SRC_MON1 (0x1ae4)
+#define AFE_DMIC2_IIR_COEF_02_01 (0x1ae8)
+#define AFE_DMIC2_IIR_COEF_04_03 (0x1aec)
+#define AFE_DMIC2_IIR_COEF_06_05 (0x1af0)
+#define AFE_DMIC2_IIR_COEF_08_07 (0x1af4)
+#define AFE_DMIC2_IIR_COEF_10_09 (0x1af8)
+#define AFE_DMIC3_UL_SRC_CON0 (0x1b38)
+#define AFE_DMIC3_UL_SRC_CON1 (0x1b3c)
+#define AFE_DMIC3_SRC_DEBUG (0x1b40)
+#define AFE_DMIC3_SRC_DEBUG_MON0 (0x1b44)
+#define AFE_DMIC3_UL_SRC_MON0 (0x1b48)
+#define AFE_DMIC3_UL_SRC_MON1 (0x1b4c)
+#define AFE_DMIC3_IIR_COEF_02_01 (0x1b50)
+#define AFE_DMIC3_IIR_COEF_04_03 (0x1b54)
+#define AFE_DMIC3_IIR_COEF_06_05 (0x1b58)
+#define AFE_DMIC3_IIR_COEF_08_07 (0x1b5c)
+#define AFE_DMIC3_IIR_COEF_10_09 (0x1b60)
+#define DMIC_BYPASS_HW_GAIN (0x1bf0)
+#define DMIC_GAIN1_CON0 (0x1c00)
+#define DMIC_GAIN1_CON1 (0x1c04)
+#define DMIC_GAIN1_CON2 (0x1c08)
+#define DMIC_GAIN1_CON3 (0x1c0c)
+#define DMIC_GAIN1_CUR (0x1c10)
+#define DMIC_GAIN2_CON0 (0x1c20)
+#define DMIC_GAIN2_CON1 (0x1c24)
+#define DMIC_GAIN2_CON2 (0x1c28)
+#define DMIC_GAIN2_CON3 (0x1c2c)
+#define DMIC_GAIN2_CUR (0x1c30)
+#define DMIC_GAIN3_CON0 (0x1c40)
+#define DMIC_GAIN3_CON1 (0x1c44)
+#define DMIC_GAIN3_CON2 (0x1c48)
+#define DMIC_GAIN3_CON3 (0x1c4c)
+#define DMIC_GAIN3_CUR (0x1c50)
+#define DMIC_GAIN4_CON0 (0x1c60)
+#define DMIC_GAIN4_CON1 (0x1c64)
+#define DMIC_GAIN4_CON2 (0x1c68)
+#define DMIC_GAIN4_CON3 (0x1c6c)
+#define DMIC_GAIN4_CUR (0x1c70)
+#define ETDM_OUT1_DSD_FADE_CON (0x2260)
+#define ETDM_OUT1_DSD_FADE_CON1 (0x2264)
+#define ETDM_OUT3_DSD_FADE_CON (0x2280)
+#define ETDM_OUT3_DSD_FADE_CON1 (0x2284)
+#define ETDM_IN1_AFIFO_CON (0x2294)
+#define ETDM_IN2_AFIFO_CON (0x2298)
+#define ETDM_IN1_MONITOR (0x22c0)
+#define ETDM_IN2_MONITOR (0x22c4)
+#define ETDM_OUT1_MONITOR (0x22d0)
+#define ETDM_OUT2_MONITOR (0x22d4)
+#define ETDM_OUT3_MONITOR (0x22d8)
+#define ETDM_COWORK_SEC_CON0 (0x22e0)
+#define ETDM_COWORK_SEC_CON1 (0x22e4)
+#define ETDM_COWORK_SEC_CON2 (0x22e8)
+#define ETDM_COWORK_SEC_CON3 (0x22ec)
+#define ETDM_COWORK_CON0 (0x22f0)
+#define ETDM_COWORK_CON1 (0x22f4)
+#define ETDM_COWORK_CON2 (0x22f8)
+#define ETDM_COWORK_CON3 (0x22fc)
+#define ETDM_IN1_CON0 (0x2300)
+#define ETDM_IN1_CON1 (0x2304)
+#define ETDM_IN1_CON2 (0x2308)
+#define ETDM_IN1_CON3 (0x230c)
+#define ETDM_IN1_CON4 (0x2310)
+#define ETDM_IN1_CON5 (0x2314)
+#define ETDM_IN1_CON6 (0x2318)
+#define ETDM_IN1_CON7 (0x231c)
+#define ETDM_IN2_CON0 (0x2320)
+#define ETDM_IN2_CON1 (0x2324)
+#define ETDM_IN2_CON2 (0x2328)
+#define ETDM_IN2_CON3 (0x232c)
+#define ETDM_IN2_CON4 (0x2330)
+#define ETDM_IN2_CON5 (0x2334)
+#define ETDM_IN2_CON6 (0x2338)
+#define ETDM_IN2_CON7 (0x233c)
+#define ETDM_OUT1_CON0 (0x2380)
+#define ETDM_OUT1_CON1 (0x2384)
+#define ETDM_OUT1_CON2 (0x2388)
+#define ETDM_OUT1_CON3 (0x238c)
+#define ETDM_OUT1_CON4 (0x2390)
+#define ETDM_OUT1_CON5 (0x2394)
+#define ETDM_OUT1_CON6 (0x2398)
+#define ETDM_OUT1_CON7 (0x239c)
+#define ETDM_OUT2_CON0 (0x23a0)
+#define ETDM_OUT2_CON1 (0x23a4)
+#define ETDM_OUT2_CON2 (0x23a8)
+#define ETDM_OUT2_CON3 (0x23ac)
+#define ETDM_OUT2_CON4 (0x23b0)
+#define ETDM_OUT2_CON5 (0x23b4)
+#define ETDM_OUT2_CON6 (0x23b8)
+#define ETDM_OUT2_CON7 (0x23bc)
+#define ETDM_OUT3_CON0 (0x23c0)
+#define ETDM_OUT3_CON1 (0x23c4)
+#define ETDM_OUT3_CON2 (0x23c8)
+#define ETDM_OUT3_CON3 (0x23cc)
+#define ETDM_OUT3_CON4 (0x23d0)
+#define ETDM_OUT3_CON5 (0x23d4)
+#define ETDM_OUT3_CON6 (0x23d8)
+#define ETDM_OUT3_CON7 (0x23dc)
+#define ETDM_OUT3_CON8 (0x23e0)
+#define ETDM_OUT1_CON8 (0x23e4)
+#define ETDM_OUT2_CON8 (0x23e8)
+#define GASRC_TIMING_CON0 (0x2414)
+#define GASRC_TIMING_CON1 (0x2418)
+#define GASRC_TIMING_CON2 (0x241c)
+#define GASRC_TIMING_CON3 (0x2420)
+#define GASRC_TIMING_CON4 (0x2424)
+#define GASRC_TIMING_CON5 (0x2428)
+#define GASRC_TIMING_CON6 (0x242c)
+#define GASRC_TIMING_CON7 (0x2430)
+#define A3_A4_TIMING_SEL0 (0x2440)
+#define A3_A4_TIMING_SEL1 (0x2444)
+#define A3_A4_TIMING_SEL2 (0x2448)
+#define A3_A4_TIMING_SEL3 (0x244c)
+#define A3_A4_TIMING_SEL4 (0x2450)
+#define A3_A4_TIMING_SEL5 (0x2454)
+#define A3_A4_TIMING_SEL6 (0x2458)
+#define ASYS_TOP_DEBUG (0x2500)
+#define AFE_DPTX_CON (0x2558)
+#define AFE_DPTX_MON (0x255c)
+#define AFE_ADDA_DL_SRC2_CON0 (0x2d00)
+#define AFE_ADDA_DL_SRC2_CON1 (0x2d04)
+#define AFE_ADDA_TOP_CON0 (0x2d0c)
+#define AFE_ADDA_UL_DL_CON0 (0x2d10)
+#define AFE_ADDA_SRC_DEBUG (0x2d14)
+#define AFE_ADDA_SRC_DEBUG_MON0 (0x2d18)
+#define AFE_ADDA_SRC_DEBUG_MON1 (0x2d20)
+#define AFE_ADDA_PREDIS_CON0 (0x2d24)
+#define AFE_ADDA_PREDIS_CON1 (0x2d28)
+#define AFE_ADDA_PREDIS_CON2 (0x2d2c)
+#define AFE_ADDA_PREDIS_CON3 (0x2d30)
+#define AFE_ADDA_DL_SDM_DCCOMP_CON (0x2d34)
+#define AFE_ADDA_DL_SDM_TEST (0x2d38)
+#define AFE_ADDA_DL_DC_COMP_CFG0 (0x2d3c)
+#define AFE_ADDA_DL_DC_COMP_CFG1 (0x2d40)
+#define AFE_ADDA_DL_SDM_FIFO_MON (0x2d44)
+#define AFE_ADDA_DL_SRC_LCH_MON (0x2d50)
+#define AFE_ADDA_DL_SRC_RCH_MON (0x2d54)
+#define AFE_ADDA_DL_SDM_OUT_MON (0x2d58)
+#define AFE_ADDA_DL_SDM_DITHER_CON (0x2d5c)
+#define AFE_ADDA_DL_SDM_AUTO_RESET_CON (0x2d60)
+#define AFE_ADDA_UL_SRC_CON0 (0x2e3c)
+#define AFE_ADDA_UL_SRC_CON1 (0x2e40)
+#define AFE_CONN0 (0x3000)
+#define AFE_CONN0_1 (0x3004)
+#define AFE_CONN0_2 (0x3008)
+#define AFE_CONN0_3 (0x300c)
+#define AFE_CONN0_4 (0x3010)
+#define AFE_CONN1 (0x3014)
+#define AFE_CONN1_1 (0x3018)
+#define AFE_CONN1_2 (0x301c)
+#define AFE_CONN1_3 (0x3020)
+#define AFE_CONN1_4 (0x3024)
+#define AFE_CONN2 (0x3028)
+#define AFE_CONN2_1 (0x302c)
+#define AFE_CONN2_2 (0x3030)
+#define AFE_CONN2_3 (0x3034)
+#define AFE_CONN2_4 (0x3038)
+#define AFE_CONN3 (0x303c)
+#define AFE_CONN3_1 (0x3040)
+#define AFE_CONN3_2 (0x3044)
+#define AFE_CONN3_3 (0x3048)
+#define AFE_CONN3_4 (0x304c)
+#define AFE_CONN4 (0x3050)
+#define AFE_CONN4_1 (0x3054)
+#define AFE_CONN4_2 (0x3058)
+#define AFE_CONN4_3 (0x305c)
+#define AFE_CONN4_4 (0x3060)
+#define AFE_CONN5 (0x3064)
+#define AFE_CONN5_1 (0x3068)
+#define AFE_CONN5_2 (0x306c)
+#define AFE_CONN5_3 (0x3070)
+#define AFE_CONN5_4 (0x3074)
+#define AFE_CONN6 (0x3078)
+#define AFE_CONN6_1 (0x307c)
+#define AFE_CONN6_2 (0x3080)
+#define AFE_CONN6_3 (0x3084)
+#define AFE_CONN6_4 (0x3088)
+#define AFE_CONN7 (0x308c)
+#define AFE_CONN7_1 (0x3090)
+#define AFE_CONN7_2 (0x3094)
+#define AFE_CONN7_3 (0x3098)
+#define AFE_CONN7_4 (0x309c)
+#define AFE_CONN8 (0x30a0)
+#define AFE_CONN8_1 (0x30a4)
+#define AFE_CONN8_2 (0x30a8)
+#define AFE_CONN8_3 (0x30ac)
+#define AFE_CONN8_4 (0x30b0)
+#define AFE_CONN9 (0x30b4)
+#define AFE_CONN9_1 (0x30b8)
+#define AFE_CONN9_2 (0x30bc)
+#define AFE_CONN9_3 (0x30c0)
+#define AFE_CONN9_4 (0x30c4)
+#define AFE_CONN10 (0x30c8)
+#define AFE_CONN10_1 (0x30cc)
+#define AFE_CONN10_2 (0x30d0)
+#define AFE_CONN10_3 (0x30d4)
+#define AFE_CONN10_4 (0x30d8)
+#define AFE_CONN11 (0x30dc)
+#define AFE_CONN11_1 (0x30e0)
+#define AFE_CONN11_2 (0x30e4)
+#define AFE_CONN11_3 (0x30e8)
+#define AFE_CONN11_4 (0x30ec)
+#define AFE_CONN12 (0x30f0)
+#define AFE_CONN12_1 (0x30f4)
+#define AFE_CONN12_2 (0x30f8)
+#define AFE_CONN12_3 (0x30fc)
+#define AFE_CONN12_4 (0x3100)
+#define AFE_CONN13 (0x3104)
+#define AFE_CONN13_1 (0x3108)
+#define AFE_CONN13_2 (0x310c)
+#define AFE_CONN13_3 (0x3110)
+#define AFE_CONN13_4 (0x3114)
+#define AFE_CONN14 (0x3118)
+#define AFE_CONN14_1 (0x311c)
+#define AFE_CONN14_2 (0x3120)
+#define AFE_CONN14_3 (0x3124)
+#define AFE_CONN14_4 (0x3128)
+#define AFE_CONN15 (0x312c)
+#define AFE_CONN15_1 (0x3130)
+#define AFE_CONN15_2 (0x3134)
+#define AFE_CONN15_3 (0x3138)
+#define AFE_CONN15_4 (0x313c)
+#define AFE_CONN16 (0x3140)
+#define AFE_CONN16_1 (0x3144)
+#define AFE_CONN16_2 (0x3148)
+#define AFE_CONN16_3 (0x314c)
+#define AFE_CONN16_4 (0x3150)
+#define AFE_CONN17 (0x3154)
+#define AFE_CONN17_1 (0x3158)
+#define AFE_CONN17_2 (0x315c)
+#define AFE_CONN17_3 (0x3160)
+#define AFE_CONN17_4 (0x3164)
+#define AFE_CONN18 (0x3168)
+#define AFE_CONN18_1 (0x316c)
+#define AFE_CONN18_2 (0x3170)
+#define AFE_CONN18_3 (0x3174)
+#define AFE_CONN18_4 (0x3178)
+#define AFE_CONN19 (0x317c)
+#define AFE_CONN19_1 (0x3180)
+#define AFE_CONN19_2 (0x3184)
+#define AFE_CONN19_3 (0x3188)
+#define AFE_CONN19_4 (0x318c)
+#define AFE_CONN20 (0x3190)
+#define AFE_CONN20_1 (0x3194)
+#define AFE_CONN20_2 (0x3198)
+#define AFE_CONN20_3 (0x319c)
+#define AFE_CONN20_4 (0x31a0)
+#define AFE_CONN21 (0x31a4)
+#define AFE_CONN21_1 (0x31a8)
+#define AFE_CONN21_2 (0x31ac)
+#define AFE_CONN21_3 (0x31b0)
+#define AFE_CONN21_4 (0x31b4)
+#define AFE_CONN22 (0x31b8)
+#define AFE_CONN22_1 (0x31bc)
+#define AFE_CONN22_2 (0x31c0)
+#define AFE_CONN22_3 (0x31c4)
+#define AFE_CONN22_4 (0x31c8)
+#define AFE_CONN23 (0x31cc)
+#define AFE_CONN23_1 (0x31d0)
+#define AFE_CONN23_2 (0x31d4)
+#define AFE_CONN23_3 (0x31d8)
+#define AFE_CONN23_4 (0x31dc)
+#define AFE_CONN24 (0x31e0)
+#define AFE_CONN24_1 (0x31e4)
+#define AFE_CONN24_2 (0x31e8)
+#define AFE_CONN24_3 (0x31ec)
+#define AFE_CONN24_4 (0x31f0)
+#define AFE_CONN25 (0x31f4)
+#define AFE_CONN25_1 (0x31f8)
+#define AFE_CONN25_2 (0x31fc)
+#define AFE_CONN25_3 (0x3200)
+#define AFE_CONN25_4 (0x3204)
+#define AFE_CONN26 (0x3208)
+#define AFE_CONN26_1 (0x320c)
+#define AFE_CONN26_2 (0x3210)
+#define AFE_CONN26_3 (0x3214)
+#define AFE_CONN26_4 (0x3218)
+#define AFE_CONN27 (0x321c)
+#define AFE_CONN27_1 (0x3220)
+#define AFE_CONN27_2 (0x3224)
+#define AFE_CONN27_3 (0x3228)
+#define AFE_CONN27_4 (0x322c)
+#define AFE_CONN28 (0x3230)
+#define AFE_CONN28_1 (0x3234)
+#define AFE_CONN28_2 (0x3238)
+#define AFE_CONN28_3 (0x323c)
+#define AFE_CONN28_4 (0x3240)
+#define AFE_CONN29 (0x3244)
+#define AFE_CONN29_1 (0x3248)
+#define AFE_CONN29_2 (0x324c)
+#define AFE_CONN29_3 (0x3250)
+#define AFE_CONN29_4 (0x3254)
+#define AFE_CONN30 (0x3258)
+#define AFE_CONN30_1 (0x325c)
+#define AFE_CONN30_2 (0x3260)
+#define AFE_CONN30_3 (0x3264)
+#define AFE_CONN30_4 (0x3268)
+#define AFE_CONN31 (0x326c)
+#define AFE_CONN31_1 (0x3270)
+#define AFE_CONN31_2 (0x3274)
+#define AFE_CONN31_3 (0x3278)
+#define AFE_CONN31_4 (0x327c)
+#define AFE_CONN32 (0x3280)
+#define AFE_CONN32_1 (0x3284)
+#define AFE_CONN32_2 (0x3288)
+#define AFE_CONN32_3 (0x328c)
+#define AFE_CONN32_4 (0x3290)
+#define AFE_CONN33 (0x3294)
+#define AFE_CONN33_1 (0x3298)
+#define AFE_CONN33_2 (0x329c)
+#define AFE_CONN33_3 (0x32a0)
+#define AFE_CONN33_4 (0x32a4)
+#define AFE_CONN34 (0x32a8)
+#define AFE_CONN34_1 (0x32ac)
+#define AFE_CONN34_2 (0x32b0)
+#define AFE_CONN34_3 (0x32b4)
+#define AFE_CONN34_4 (0x32b8)
+#define AFE_CONN35 (0x32bc)
+#define AFE_CONN35_1 (0x32c0)
+#define AFE_CONN35_2 (0x32c4)
+#define AFE_CONN35_3 (0x32c8)
+#define AFE_CONN35_4 (0x32cc)
+#define AFE_CONN36 (0x32d0)
+#define AFE_CONN36_1 (0x32d4)
+#define AFE_CONN36_2 (0x32d8)
+#define AFE_CONN36_3 (0x32dc)
+#define AFE_CONN36_4 (0x32e0)
+#define AFE_CONN37 (0x32e4)
+#define AFE_CONN37_1 (0x32e8)
+#define AFE_CONN37_2 (0x32ec)
+#define AFE_CONN37_3 (0x32f0)
+#define AFE_CONN37_4 (0x32f4)
+#define AFE_CONN38 (0x32f8)
+#define AFE_CONN38_1 (0x32fc)
+#define AFE_CONN38_2 (0x3300)
+#define AFE_CONN38_3 (0x3304)
+#define AFE_CONN38_4 (0x3308)
+#define AFE_CONN39 (0x330c)
+#define AFE_CONN39_1 (0x3310)
+#define AFE_CONN39_2 (0x3314)
+#define AFE_CONN39_3 (0x3318)
+#define AFE_CONN39_4 (0x331c)
+#define AFE_CONN40 (0x3320)
+#define AFE_CONN40_1 (0x3324)
+#define AFE_CONN40_2 (0x3328)
+#define AFE_CONN40_3 (0x332c)
+#define AFE_CONN40_4 (0x3330)
+#define AFE_CONN41 (0x3334)
+#define AFE_CONN41_1 (0x3338)
+#define AFE_CONN41_2 (0x333c)
+#define AFE_CONN41_3 (0x3340)
+#define AFE_CONN41_4 (0x3344)
+#define AFE_CONN42 (0x3348)
+#define AFE_CONN42_1 (0x334c)
+#define AFE_CONN42_2 (0x3350)
+#define AFE_CONN42_3 (0x3354)
+#define AFE_CONN42_4 (0x3358)
+#define AFE_CONN43 (0x335c)
+#define AFE_CONN43_1 (0x3360)
+#define AFE_CONN43_2 (0x3364)
+#define AFE_CONN43_3 (0x3368)
+#define AFE_CONN43_4 (0x336c)
+#define AFE_CONN44 (0x3370)
+#define AFE_CONN44_1 (0x3374)
+#define AFE_CONN44_2 (0x3378)
+#define AFE_CONN44_3 (0x337c)
+#define AFE_CONN44_4 (0x3380)
+#define AFE_CONN45 (0x3384)
+#define AFE_CONN45_1 (0x3388)
+#define AFE_CONN45_2 (0x338c)
+#define AFE_CONN45_3 (0x3390)
+#define AFE_CONN45_4 (0x3394)
+#define AFE_CONN46 (0x3398)
+#define AFE_CONN46_1 (0x339c)
+#define AFE_CONN46_2 (0x33a0)
+#define AFE_CONN46_3 (0x33a4)
+#define AFE_CONN46_4 (0x33a8)
+#define AFE_CONN47 (0x33ac)
+#define AFE_CONN47_1 (0x33b0)
+#define AFE_CONN47_2 (0x33b4)
+#define AFE_CONN47_3 (0x33b8)
+#define AFE_CONN47_4 (0x33bc)
+#define AFE_CONN48 (0x33c0)
+#define AFE_CONN48_1 (0x33c4)
+#define AFE_CONN48_2 (0x33c8)
+#define AFE_CONN48_3 (0x33cc)
+#define AFE_CONN48_4 (0x33d0)
+#define AFE_CONN49 (0x33d4)
+#define AFE_CONN49_1 (0x33d8)
+#define AFE_CONN49_2 (0x33dc)
+#define AFE_CONN49_3 (0x33e0)
+#define AFE_CONN49_4 (0x33e4)
+#define AFE_CONN50 (0x33e8)
+#define AFE_CONN50_1 (0x33ec)
+#define AFE_CONN50_2 (0x33f0)
+#define AFE_CONN50_3 (0x33f4)
+#define AFE_CONN50_4 (0x33f8)
+#define AFE_CONN51 (0x33fc)
+#define AFE_CONN51_1 (0x3400)
+#define AFE_CONN51_2 (0x3404)
+#define AFE_CONN51_3 (0x3408)
+#define AFE_CONN51_4 (0x340c)
+#define AFE_CONN52 (0x3410)
+#define AFE_CONN52_1 (0x3414)
+#define AFE_CONN52_2 (0x3418)
+#define AFE_CONN52_3 (0x341c)
+#define AFE_CONN52_4 (0x3420)
+#define AFE_CONN53 (0x3424)
+#define AFE_CONN53_1 (0x3428)
+#define AFE_CONN53_2 (0x342c)
+#define AFE_CONN53_3 (0x3430)
+#define AFE_CONN53_4 (0x3434)
+#define AFE_CONN54 (0x3438)
+#define AFE_CONN54_1 (0x343c)
+#define AFE_CONN54_2 (0x3440)
+#define AFE_CONN54_3 (0x3444)
+#define AFE_CONN54_4 (0x3448)
+#define AFE_CONN55 (0x344c)
+#define AFE_CONN55_1 (0x3450)
+#define AFE_CONN55_2 (0x3454)
+#define AFE_CONN55_3 (0x3458)
+#define AFE_CONN55_4 (0x345c)
+#define AFE_CONN56 (0x3460)
+#define AFE_CONN56_1 (0x3464)
+#define AFE_CONN56_2 (0x3468)
+#define AFE_CONN56_3 (0x346c)
+#define AFE_CONN56_4 (0x3470)
+#define AFE_CONN57 (0x3474)
+#define AFE_CONN57_1 (0x3478)
+#define AFE_CONN57_2 (0x347c)
+#define AFE_CONN57_3 (0x3480)
+#define AFE_CONN57_4 (0x3484)
+#define AFE_CONN58 (0x3488)
+#define AFE_CONN58_1 (0x348c)
+#define AFE_CONN58_2 (0x3490)
+#define AFE_CONN58_3 (0x3494)
+#define AFE_CONN58_4 (0x3498)
+#define AFE_CONN59 (0x349c)
+#define AFE_CONN59_1 (0x34a0)
+#define AFE_CONN59_2 (0x34a4)
+#define AFE_CONN59_3 (0x34a8)
+#define AFE_CONN59_4 (0x34ac)
+#define AFE_CONN60 (0x34b0)
+#define AFE_CONN60_1 (0x34b4)
+#define AFE_CONN60_2 (0x34b8)
+#define AFE_CONN60_3 (0x34bc)
+#define AFE_CONN60_4 (0x34c0)
+#define AFE_CONN61 (0x34c4)
+#define AFE_CONN61_1 (0x34c8)
+#define AFE_CONN61_2 (0x34cc)
+#define AFE_CONN61_3 (0x34d0)
+#define AFE_CONN61_4 (0x34d4)
+#define AFE_CONN62 (0x34d8)
+#define AFE_CONN62_1 (0x34dc)
+#define AFE_CONN62_2 (0x34e0)
+#define AFE_CONN62_3 (0x34e4)
+#define AFE_CONN62_4 (0x34e8)
+#define AFE_CONN63 (0x34ec)
+#define AFE_CONN63_1 (0x34f0)
+#define AFE_CONN63_2 (0x34f4)
+#define AFE_CONN63_3 (0x34f8)
+#define AFE_CONN63_4 (0x34fc)
+#define AFE_CONN64 (0x3500)
+#define AFE_CONN64_1 (0x3504)
+#define AFE_CONN64_2 (0x3508)
+#define AFE_CONN64_3 (0x350c)
+#define AFE_CONN64_4 (0x3510)
+#define AFE_CONN65 (0x3514)
+#define AFE_CONN65_1 (0x3518)
+#define AFE_CONN65_2 (0x351c)
+#define AFE_CONN65_3 (0x3520)
+#define AFE_CONN65_4 (0x3524)
+#define AFE_CONN66 (0x3528)
+#define AFE_CONN66_1 (0x352c)
+#define AFE_CONN66_2 (0x3530)
+#define AFE_CONN66_3 (0x3534)
+#define AFE_CONN66_4 (0x3538)
+#define AFE_CONN67 (0x353c)
+#define AFE_CONN67_1 (0x3540)
+#define AFE_CONN67_2 (0x3544)
+#define AFE_CONN67_3 (0x3548)
+#define AFE_CONN67_4 (0x354c)
+#define AFE_CONN68 (0x3550)
+#define AFE_CONN68_1 (0x3554)
+#define AFE_CONN68_2 (0x3558)
+#define AFE_CONN68_3 (0x355c)
+#define AFE_CONN68_4 (0x3560)
+#define AFE_CONN69 (0x3564)
+#define AFE_CONN69_1 (0x3568)
+#define AFE_CONN69_2 (0x356c)
+#define AFE_CONN69_3 (0x3570)
+#define AFE_CONN69_4 (0x3574)
+#define AFE_CONN70 (0x3578)
+#define AFE_CONN70_1 (0x357c)
+#define AFE_CONN70_2 (0x3580)
+#define AFE_CONN70_3 (0x3584)
+#define AFE_CONN70_4 (0x3588)
+#define AFE_CONN71 (0x358c)
+#define AFE_CONN71_1 (0x3590)
+#define AFE_CONN71_2 (0x3594)
+#define AFE_CONN71_3 (0x3598)
+#define AFE_CONN71_4 (0x359c)
+#define AFE_CONN72 (0x35a0)
+#define AFE_CONN72_1 (0x35a4)
+#define AFE_CONN72_2 (0x35a8)
+#define AFE_CONN72_3 (0x35ac)
+#define AFE_CONN72_4 (0x35b0)
+#define AFE_CONN73 (0x35b4)
+#define AFE_CONN73_1 (0x35b8)
+#define AFE_CONN73_2 (0x35bc)
+#define AFE_CONN73_3 (0x35c0)
+#define AFE_CONN73_4 (0x35c4)
+#define AFE_CONN74 (0x35c8)
+#define AFE_CONN74_1 (0x35cc)
+#define AFE_CONN74_2 (0x35d0)
+#define AFE_CONN74_3 (0x35d4)
+#define AFE_CONN74_4 (0x35d8)
+#define AFE_CONN75 (0x35dc)
+#define AFE_CONN75_1 (0x35e0)
+#define AFE_CONN75_2 (0x35e4)
+#define AFE_CONN75_3 (0x35e8)
+#define AFE_CONN75_4 (0x35ec)
+#define AFE_CONN76 (0x35f0)
+#define AFE_CONN76_1 (0x35f4)
+#define AFE_CONN76_2 (0x35f8)
+#define AFE_CONN76_3 (0x35fc)
+#define AFE_CONN76_4 (0x3600)
+#define AFE_CONN77 (0x3604)
+#define AFE_CONN77_1 (0x3608)
+#define AFE_CONN77_2 (0x360c)
+#define AFE_CONN77_3 (0x3610)
+#define AFE_CONN77_4 (0x3614)
+#define AFE_CONN78 (0x3618)
+#define AFE_CONN78_1 (0x361c)
+#define AFE_CONN78_2 (0x3620)
+#define AFE_CONN78_3 (0x3624)
+#define AFE_CONN78_4 (0x3628)
+#define AFE_CONN79 (0x362c)
+#define AFE_CONN79_1 (0x3630)
+#define AFE_CONN79_2 (0x3634)
+#define AFE_CONN79_3 (0x3638)
+#define AFE_CONN79_4 (0x363c)
+#define AFE_CONN80 (0x3640)
+#define AFE_CONN80_1 (0x3644)
+#define AFE_CONN80_2 (0x3648)
+#define AFE_CONN80_3 (0x364c)
+#define AFE_CONN80_4 (0x3650)
+#define AFE_CONN81 (0x3654)
+#define AFE_CONN81_1 (0x3658)
+#define AFE_CONN81_2 (0x365c)
+#define AFE_CONN81_3 (0x3660)
+#define AFE_CONN81_4 (0x3664)
+#define AFE_CONN82 (0x3668)
+#define AFE_CONN82_1 (0x366c)
+#define AFE_CONN82_2 (0x3670)
+#define AFE_CONN82_3 (0x3674)
+#define AFE_CONN82_4 (0x3678)
+#define AFE_CONN83 (0x367c)
+#define AFE_CONN83_1 (0x3680)
+#define AFE_CONN83_2 (0x3684)
+#define AFE_CONN83_3 (0x3688)
+#define AFE_CONN83_4 (0x368c)
+#define AFE_CONN84 (0x3690)
+#define AFE_CONN84_1 (0x3694)
+#define AFE_CONN84_2 (0x3698)
+#define AFE_CONN84_3 (0x369c)
+#define AFE_CONN84_4 (0x36a0)
+#define AFE_CONN85 (0x36a4)
+#define AFE_CONN85_1 (0x36a8)
+#define AFE_CONN85_2 (0x36ac)
+#define AFE_CONN85_3 (0x36b0)
+#define AFE_CONN85_4 (0x36b4)
+#define AFE_CONN86 (0x36b8)
+#define AFE_CONN86_1 (0x36bc)
+#define AFE_CONN86_2 (0x36c0)
+#define AFE_CONN86_3 (0x36c4)
+#define AFE_CONN86_4 (0x36c8)
+#define AFE_CONN87 (0x36cc)
+#define AFE_CONN87_1 (0x36d0)
+#define AFE_CONN87_2 (0x36d4)
+#define AFE_CONN87_3 (0x36d8)
+#define AFE_CONN87_4 (0x36dc)
+#define AFE_CONN88 (0x36e0)
+#define AFE_CONN88_1 (0x36e4)
+#define AFE_CONN88_2 (0x36e8)
+#define AFE_CONN88_3 (0x36ec)
+#define AFE_CONN88_4 (0x36f0)
+#define AFE_CONN89 (0x36f4)
+#define AFE_CONN89_1 (0x36f8)
+#define AFE_CONN89_2 (0x36fc)
+#define AFE_CONN89_3 (0x3700)
+#define AFE_CONN89_4 (0x3704)
+#define AFE_CONN90 (0x3708)
+#define AFE_CONN90_1 (0x370c)
+#define AFE_CONN90_2 (0x3710)
+#define AFE_CONN90_3 (0x3714)
+#define AFE_CONN90_4 (0x3718)
+#define AFE_CONN91 (0x371c)
+#define AFE_CONN91_1 (0x3720)
+#define AFE_CONN91_2 (0x3724)
+#define AFE_CONN91_3 (0x3728)
+#define AFE_CONN91_4 (0x372c)
+#define AFE_CONN92 (0x3730)
+#define AFE_CONN92_1 (0x3734)
+#define AFE_CONN92_2 (0x3738)
+#define AFE_CONN92_3 (0x373c)
+#define AFE_CONN92_4 (0x3740)
+#define AFE_CONN93 (0x3744)
+#define AFE_CONN93_1 (0x3748)
+#define AFE_CONN93_2 (0x374c)
+#define AFE_CONN93_3 (0x3750)
+#define AFE_CONN93_4 (0x3754)
+#define AFE_CONN94 (0x3758)
+#define AFE_CONN94_1 (0x375c)
+#define AFE_CONN94_2 (0x3760)
+#define AFE_CONN94_3 (0x3764)
+#define AFE_CONN94_4 (0x3768)
+#define AFE_CONN95 (0x376c)
+#define AFE_CONN95_1 (0x3770)
+#define AFE_CONN95_2 (0x3774)
+#define AFE_CONN95_3 (0x3778)
+#define AFE_CONN95_4 (0x377c)
+#define AFE_CONN96 (0x3780)
+#define AFE_CONN96_1 (0x3784)
+#define AFE_CONN96_2 (0x3788)
+#define AFE_CONN96_3 (0x378c)
+#define AFE_CONN96_4 (0x3790)
+#define AFE_CONN97 (0x3794)
+#define AFE_CONN97_1 (0x3798)
+#define AFE_CONN97_2 (0x379c)
+#define AFE_CONN97_3 (0x37a0)
+#define AFE_CONN97_4 (0x37a4)
+#define AFE_CONN98 (0x37a8)
+#define AFE_CONN98_1 (0x37ac)
+#define AFE_CONN98_2 (0x37b0)
+#define AFE_CONN98_3 (0x37b4)
+#define AFE_CONN98_4 (0x37b8)
+#define AFE_CONN99 (0x37bc)
+#define AFE_CONN99_1 (0x37c0)
+#define AFE_CONN99_2 (0x37c4)
+#define AFE_CONN99_3 (0x37c8)
+#define AFE_CONN99_4 (0x37cc)
+#define AFE_CONN100 (0x37d0)
+#define AFE_CONN100_1 (0x37d4)
+#define AFE_CONN100_2 (0x37d8)
+#define AFE_CONN100_3 (0x37dc)
+#define AFE_CONN100_4 (0x37e0)
+#define AFE_CONN101 (0x37e4)
+#define AFE_CONN101_1 (0x37e8)
+#define AFE_CONN101_2 (0x37ec)
+#define AFE_CONN101_3 (0x37f0)
+#define AFE_CONN101_4 (0x37f4)
+#define AFE_CONN102 (0x37f8)
+#define AFE_CONN102_1 (0x37fc)
+#define AFE_CONN102_2 (0x3800)
+#define AFE_CONN102_3 (0x3804)
+#define AFE_CONN102_4 (0x3808)
+#define AFE_CONN103 (0x380c)
+#define AFE_CONN103_1 (0x3810)
+#define AFE_CONN103_2 (0x3814)
+#define AFE_CONN103_3 (0x3818)
+#define AFE_CONN103_4 (0x381c)
+#define AFE_CONN104 (0x3820)
+#define AFE_CONN104_1 (0x3824)
+#define AFE_CONN104_2 (0x3828)
+#define AFE_CONN104_3 (0x382c)
+#define AFE_CONN104_4 (0x3830)
+#define AFE_CONN105 (0x3834)
+#define AFE_CONN105_1 (0x3838)
+#define AFE_CONN105_2 (0x383c)
+#define AFE_CONN105_3 (0x3840)
+#define AFE_CONN105_4 (0x3844)
+#define AFE_CONN106 (0x3848)
+#define AFE_CONN106_1 (0x384c)
+#define AFE_CONN106_2 (0x3850)
+#define AFE_CONN106_3 (0x3854)
+#define AFE_CONN106_4 (0x3858)
+#define AFE_CONN107 (0x385c)
+#define AFE_CONN107_1 (0x3860)
+#define AFE_CONN107_2 (0x3864)
+#define AFE_CONN107_3 (0x3868)
+#define AFE_CONN107_4 (0x386c)
+#define AFE_CONN108 (0x3870)
+#define AFE_CONN108_1 (0x3874)
+#define AFE_CONN108_2 (0x3878)
+#define AFE_CONN108_3 (0x387c)
+#define AFE_CONN108_4 (0x3880)
+#define AFE_CONN109 (0x3884)
+#define AFE_CONN109_1 (0x3888)
+#define AFE_CONN109_2 (0x388c)
+#define AFE_CONN109_3 (0x3890)
+#define AFE_CONN109_4 (0x3894)
+#define AFE_CONN110 (0x3898)
+#define AFE_CONN110_1 (0x389c)
+#define AFE_CONN110_2 (0x38a0)
+#define AFE_CONN110_3 (0x38a4)
+#define AFE_CONN110_4 (0x38a8)
+#define AFE_CONN111 (0x38ac)
+#define AFE_CONN111_1 (0x38b0)
+#define AFE_CONN111_2 (0x38b4)
+#define AFE_CONN111_3 (0x38b8)
+#define AFE_CONN111_4 (0x38bc)
+#define AFE_CONN112 (0x38c0)
+#define AFE_CONN112_1 (0x38c4)
+#define AFE_CONN112_2 (0x38c8)
+#define AFE_CONN112_3 (0x38cc)
+#define AFE_CONN112_4 (0x38d0)
+#define AFE_CONN113 (0x38d4)
+#define AFE_CONN113_1 (0x38d8)
+#define AFE_CONN113_2 (0x38dc)
+#define AFE_CONN113_3 (0x38e0)
+#define AFE_CONN113_4 (0x38e4)
+#define AFE_CONN114 (0x38e8)
+#define AFE_CONN114_1 (0x38ec)
+#define AFE_CONN114_2 (0x38f0)
+#define AFE_CONN114_3 (0x38f4)
+#define AFE_CONN114_4 (0x38f8)
+#define AFE_CONN115 (0x38fc)
+#define AFE_CONN115_1 (0x3900)
+#define AFE_CONN115_2 (0x3904)
+#define AFE_CONN115_3 (0x3908)
+#define AFE_CONN115_4 (0x390c)
+#define AFE_CONN116 (0x3910)
+#define AFE_CONN116_1 (0x3914)
+#define AFE_CONN116_2 (0x3918)
+#define AFE_CONN116_3 (0x391c)
+#define AFE_CONN116_4 (0x3920)
+#define AFE_CONN117 (0x3924)
+#define AFE_CONN117_1 (0x3928)
+#define AFE_CONN117_2 (0x392c)
+#define AFE_CONN117_3 (0x3930)
+#define AFE_CONN117_4 (0x3934)
+#define AFE_CONN118 (0x3938)
+#define AFE_CONN118_1 (0x393c)
+#define AFE_CONN118_2 (0x3940)
+#define AFE_CONN118_3 (0x3944)
+#define AFE_CONN118_4 (0x3948)
+#define AFE_CONN119 (0x394c)
+#define AFE_CONN119_1 (0x3950)
+#define AFE_CONN119_2 (0x3954)
+#define AFE_CONN119_3 (0x3958)
+#define AFE_CONN119_4 (0x395c)
+#define AFE_CONN120 (0x3960)
+#define AFE_CONN120_1 (0x3964)
+#define AFE_CONN120_2 (0x3968)
+#define AFE_CONN120_3 (0x396c)
+#define AFE_CONN120_4 (0x3970)
+#define AFE_CONN121 (0x3974)
+#define AFE_CONN121_1 (0x3978)
+#define AFE_CONN121_2 (0x397c)
+#define AFE_CONN121_3 (0x3980)
+#define AFE_CONN121_4 (0x3984)
+#define AFE_CONN122 (0x3988)
+#define AFE_CONN122_1 (0x398c)
+#define AFE_CONN122_2 (0x3990)
+#define AFE_CONN122_3 (0x3994)
+#define AFE_CONN122_4 (0x3998)
+#define AFE_CONN123 (0x399c)
+#define AFE_CONN123_1 (0x39a0)
+#define AFE_CONN123_2 (0x39a4)
+#define AFE_CONN123_3 (0x39a8)
+#define AFE_CONN123_4 (0x39ac)
+#define AFE_CONN124 (0x39b0)
+#define AFE_CONN124_1 (0x39b4)
+#define AFE_CONN124_2 (0x39b8)
+#define AFE_CONN124_3 (0x39bc)
+#define AFE_CONN124_4 (0x39c0)
+#define AFE_CONN125 (0x39c4)
+#define AFE_CONN125_1 (0x39c8)
+#define AFE_CONN125_2 (0x39cc)
+#define AFE_CONN125_3 (0x39d0)
+#define AFE_CONN125_4 (0x39d4)
+#define AFE_CONN126 (0x39d8)
+#define AFE_CONN126_1 (0x39dc)
+#define AFE_CONN126_2 (0x39e0)
+#define AFE_CONN126_3 (0x39e4)
+#define AFE_CONN126_4 (0x39e8)
+#define AFE_CONN127 (0x39ec)
+#define AFE_CONN127_1 (0x39f0)
+#define AFE_CONN127_2 (0x39f4)
+#define AFE_CONN127_3 (0x39f8)
+#define AFE_CONN127_4 (0x39fc)
+#define AFE_CONN128 (0x3a00)
+#define AFE_CONN128_1 (0x3a04)
+#define AFE_CONN128_2 (0x3a08)
+#define AFE_CONN128_3 (0x3a0c)
+#define AFE_CONN128_4 (0x3a10)
+#define AFE_CONN129 (0x3a14)
+#define AFE_CONN129_1 (0x3a18)
+#define AFE_CONN129_2 (0x3a1c)
+#define AFE_CONN129_3 (0x3a20)
+#define AFE_CONN129_4 (0x3a24)
+#define AFE_CONN130 (0x3a28)
+#define AFE_CONN130_1 (0x3a2c)
+#define AFE_CONN130_2 (0x3a30)
+#define AFE_CONN130_3 (0x3a34)
+#define AFE_CONN130_4 (0x3a38)
+#define AFE_CONN131 (0x3a3c)
+#define AFE_CONN131_1 (0x3a40)
+#define AFE_CONN131_2 (0x3a44)
+#define AFE_CONN131_3 (0x3a48)
+#define AFE_CONN131_4 (0x3a4c)
+#define AFE_CONN132 (0x3a50)
+#define AFE_CONN132_1 (0x3a54)
+#define AFE_CONN132_2 (0x3a58)
+#define AFE_CONN132_3 (0x3a5c)
+#define AFE_CONN132_4 (0x3a60)
+#define AFE_CONN133 (0x3a64)
+#define AFE_CONN133_1 (0x3a68)
+#define AFE_CONN133_2 (0x3a6c)
+#define AFE_CONN133_3 (0x3a70)
+#define AFE_CONN133_4 (0x3a74)
+#define AFE_CONN134 (0x3a78)
+#define AFE_CONN134_1 (0x3a7c)
+#define AFE_CONN134_2 (0x3a80)
+#define AFE_CONN134_3 (0x3a84)
+#define AFE_CONN134_4 (0x3a88)
+#define AFE_CONN135 (0x3a8c)
+#define AFE_CONN135_1 (0x3a90)
+#define AFE_CONN135_2 (0x3a94)
+#define AFE_CONN135_3 (0x3a98)
+#define AFE_CONN135_4 (0x3a9c)
+#define AFE_CONN136 (0x3aa0)
+#define AFE_CONN136_1 (0x3aa4)
+#define AFE_CONN136_2 (0x3aa8)
+#define AFE_CONN136_3 (0x3aac)
+#define AFE_CONN136_4 (0x3ab0)
+#define AFE_CONN137 (0x3ab4)
+#define AFE_CONN137_1 (0x3ab8)
+#define AFE_CONN137_2 (0x3abc)
+#define AFE_CONN137_3 (0x3ac0)
+#define AFE_CONN137_4 (0x3ac4)
+#define AFE_CONN138 (0x3ac8)
+#define AFE_CONN138_1 (0x3acc)
+#define AFE_CONN138_2 (0x3ad0)
+#define AFE_CONN138_3 (0x3ad4)
+#define AFE_CONN138_4 (0x3ad8)
+#define AFE_CONN139 (0x3adc)
+#define AFE_CONN139_1 (0x3ae0)
+#define AFE_CONN139_2 (0x3ae4)
+#define AFE_CONN139_3 (0x3ae8)
+#define AFE_CONN139_4 (0x3aec)
+#define AFE_CONN_RS (0x3af0)
+#define AFE_CONN_RS_1 (0x3af4)
+#define AFE_CONN_RS_2 (0x3af8)
+#define AFE_CONN_RS_3 (0x3afc)
+#define AFE_CONN_RS_4 (0x3b00)
+#define AFE_CONN_16BIT (0x3b04)
+#define AFE_CONN_16BIT_1 (0x3b08)
+#define AFE_CONN_16BIT_2 (0x3b0c)
+#define AFE_CONN_16BIT_3 (0x3b10)
+#define AFE_CONN_16BIT_4 (0x3b14)
+#define AFE_CONN_24BIT (0x3b18)
+#define AFE_CONN_24BIT_1 (0x3b1c)
+#define AFE_CONN_24BIT_2 (0x3b20)
+#define AFE_CONN_24BIT_3 (0x3b24)
+#define AFE_CONN_24BIT_4 (0x3b28)
+#define AFE_CONN_DI (0x3b2c)
+#define AFE_CONN_DI_1 (0x3b30)
+#define AFE_CONN_DI_2 (0x3b34)
+#define AFE_CONN_DI_3 (0x3b38)
+#define AFE_CONN_DI_4 (0x3b3c)
+#define AFE_CONN176 (0x3ea0)
+#define AFE_CONN176_1 (0x3ea4)
+#define AFE_CONN176_2 (0x3ea8)
+#define AFE_CONN176_3 (0x3eac)
+#define AFE_CONN176_4 (0x3eb0)
+#define AFE_CONN176_5 (0x3eb4)
+#define AFE_CONN177 (0x3eb8)
+#define AFE_CONN177_1 (0x3ebc)
+#define AFE_CONN177_2 (0x3ec0)
+#define AFE_CONN177_3 (0x3ec4)
+#define AFE_CONN177_4 (0x3ec8)
+#define AFE_CONN177_5 (0x3ecc)
+#define AFE_CONN182 (0x3f30)
+#define AFE_CONN182_1 (0x3f34)
+#define AFE_CONN182_2 (0x3f38)
+#define AFE_CONN182_3 (0x3f3c)
+#define AFE_CONN182_4 (0x3f40)
+#define AFE_CONN182_5 (0x3f44)
+#define AFE_CONN183 (0x3f48)
+#define AFE_CONN183_1 (0x3f4c)
+#define AFE_CONN183_2 (0x3f50)
+#define AFE_CONN183_3 (0x3f54)
+#define AFE_CONN183_4 (0x3f58)
+#define AFE_CONN183_5 (0x3f5c)
+#define AFE_SECURE_MASK_CONN0 (0x4000)
+#define AFE_SECURE_MASK_CONN0_1 (0x4004)
+#define AFE_SECURE_MASK_CONN0_2 (0x4008)
+#define AFE_SECURE_MASK_CONN0_3 (0x400c)
+#define AFE_SECURE_MASK_CONN0_4 (0x4010)
+#define AFE_SECURE_MASK_CONN1 (0x4014)
+#define AFE_SECURE_MASK_CONN1_1 (0x4018)
+#define AFE_SECURE_MASK_CONN1_2 (0x401c)
+#define AFE_SECURE_MASK_CONN1_3 (0x4020)
+#define AFE_SECURE_MASK_CONN1_4 (0x4024)
+#define AFE_SECURE_MASK_CONN2 (0x4028)
+#define AFE_SECURE_MASK_CONN2_1 (0x402c)
+#define AFE_SECURE_MASK_CONN2_2 (0x4030)
+#define AFE_SECURE_MASK_CONN2_3 (0x4034)
+#define AFE_SECURE_MASK_CONN2_4 (0x4038)
+#define AFE_SECURE_MASK_CONN3 (0x403c)
+#define AFE_SECURE_MASK_CONN3_1 (0x4040)
+#define AFE_SECURE_MASK_CONN3_2 (0x4044)
+#define AFE_SECURE_MASK_CONN3_3 (0x4048)
+#define AFE_SECURE_MASK_CONN3_4 (0x404c)
+#define AFE_SECURE_MASK_CONN4 (0x4050)
+#define AFE_SECURE_MASK_CONN4_1 (0x4054)
+#define AFE_SECURE_MASK_CONN4_2 (0x4058)
+#define AFE_SECURE_MASK_CONN4_3 (0x405c)
+#define AFE_SECURE_MASK_CONN4_4 (0x4060)
+#define AFE_SECURE_MASK_CONN5 (0x4064)
+#define AFE_SECURE_MASK_CONN5_1 (0x4068)
+#define AFE_SECURE_MASK_CONN5_2 (0x406c)
+#define AFE_SECURE_MASK_CONN5_3 (0x4070)
+#define AFE_SECURE_MASK_CONN5_4 (0x4074)
+#define AFE_SECURE_MASK_CONN6 (0x4078)
+#define AFE_SECURE_MASK_CONN6_1 (0x407c)
+#define AFE_SECURE_MASK_CONN6_2 (0x4080)
+#define AFE_SECURE_MASK_CONN6_3 (0x4084)
+#define AFE_SECURE_MASK_CONN6_4 (0x4088)
+#define AFE_SECURE_MASK_CONN7 (0x408c)
+#define AFE_SECURE_MASK_CONN7_1 (0x4090)
+#define AFE_SECURE_MASK_CONN7_2 (0x4094)
+#define AFE_SECURE_MASK_CONN7_3 (0x4098)
+#define AFE_SECURE_MASK_CONN7_4 (0x409c)
+#define AFE_SECURE_MASK_CONN8 (0x40a0)
+#define AFE_SECURE_MASK_CONN8_1 (0x40a4)
+#define AFE_SECURE_MASK_CONN8_2 (0x40a8)
+#define AFE_SECURE_MASK_CONN8_3 (0x40ac)
+#define AFE_SECURE_MASK_CONN8_4 (0x40b0)
+#define AFE_SECURE_MASK_CONN9 (0x40b4)
+#define AFE_SECURE_MASK_CONN9_1 (0x40b8)
+#define AFE_SECURE_MASK_CONN9_2 (0x40bc)
+#define AFE_SECURE_MASK_CONN9_3 (0x40c0)
+#define AFE_SECURE_MASK_CONN9_4 (0x40c4)
+#define AFE_SECURE_MASK_CONN10 (0x40c8)
+#define AFE_SECURE_MASK_CONN10_1 (0x40cc)
+#define AFE_SECURE_MASK_CONN10_2 (0x40d0)
+#define AFE_SECURE_MASK_CONN10_3 (0x40d4)
+#define AFE_SECURE_MASK_CONN10_4 (0x40d8)
+#define AFE_SECURE_MASK_CONN11 (0x40dc)
+#define AFE_SECURE_MASK_CONN11_1 (0x40e0)
+#define AFE_SECURE_MASK_CONN11_2 (0x40e4)
+#define AFE_SECURE_MASK_CONN11_3 (0x40e8)
+#define AFE_SECURE_MASK_CONN11_4 (0x40ec)
+#define AFE_SECURE_MASK_CONN12 (0x40f0)
+#define AFE_SECURE_MASK_CONN12_1 (0x40f4)
+#define AFE_SECURE_MASK_CONN12_2 (0x40f8)
+#define AFE_SECURE_MASK_CONN12_3 (0x40fc)
+#define AFE_SECURE_MASK_CONN12_4 (0x4100)
+#define AFE_SECURE_MASK_CONN13 (0x4104)
+#define AFE_SECURE_MASK_CONN13_1 (0x4108)
+#define AFE_SECURE_MASK_CONN13_2 (0x410c)
+#define AFE_SECURE_MASK_CONN13_3 (0x4110)
+#define AFE_SECURE_MASK_CONN13_4 (0x4114)
+#define AFE_SECURE_MASK_CONN14 (0x4118)
+#define AFE_SECURE_MASK_CONN14_1 (0x411c)
+#define AFE_SECURE_MASK_CONN14_2 (0x4120)
+#define AFE_SECURE_MASK_CONN14_3 (0x4124)
+#define AFE_SECURE_MASK_CONN14_4 (0x4128)
+#define AFE_SECURE_MASK_CONN15 (0x412c)
+#define AFE_SECURE_MASK_CONN15_1 (0x4130)
+#define AFE_SECURE_MASK_CONN15_2 (0x4134)
+#define AFE_SECURE_MASK_CONN15_3 (0x4138)
+#define AFE_SECURE_MASK_CONN15_4 (0x413c)
+#define AFE_SECURE_MASK_CONN16 (0x4140)
+#define AFE_SECURE_MASK_CONN16_1 (0x4144)
+#define AFE_SECURE_MASK_CONN16_2 (0x4148)
+#define AFE_SECURE_MASK_CONN16_3 (0x414c)
+#define AFE_SECURE_MASK_CONN16_4 (0x4150)
+#define AFE_SECURE_MASK_CONN17 (0x4154)
+#define AFE_SECURE_MASK_CONN17_1 (0x4158)
+#define AFE_SECURE_MASK_CONN17_2 (0x415c)
+#define AFE_SECURE_MASK_CONN17_3 (0x4160)
+#define AFE_SECURE_MASK_CONN17_4 (0x4164)
+#define AFE_SECURE_MASK_CONN18 (0x4168)
+#define AFE_SECURE_MASK_CONN18_1 (0x416c)
+#define AFE_SECURE_MASK_CONN18_2 (0x4170)
+#define AFE_SECURE_MASK_CONN18_3 (0x4174)
+#define AFE_SECURE_MASK_CONN18_4 (0x4178)
+#define AFE_SECURE_MASK_CONN19 (0x417c)
+#define AFE_SECURE_MASK_CONN19_1 (0x4180)
+#define AFE_SECURE_MASK_CONN19_2 (0x4184)
+#define AFE_SECURE_MASK_CONN19_3 (0x4188)
+#define AFE_SECURE_MASK_CONN19_4 (0x418c)
+#define AFE_SECURE_MASK_CONN20 (0x4190)
+#define AFE_SECURE_MASK_CONN20_1 (0x4194)
+#define AFE_SECURE_MASK_CONN20_2 (0x4198)
+#define AFE_SECURE_MASK_CONN20_3 (0x419c)
+#define AFE_SECURE_MASK_CONN20_4 (0x41a0)
+#define AFE_SECURE_MASK_CONN21 (0x41a4)
+#define AFE_SECURE_MASK_CONN21_1 (0x41a8)
+#define AFE_SECURE_MASK_CONN21_2 (0x41ac)
+#define AFE_SECURE_MASK_CONN21_3 (0x41b0)
+#define AFE_SECURE_MASK_CONN21_4 (0x41b4)
+#define AFE_SECURE_MASK_CONN22 (0x41b8)
+#define AFE_SECURE_MASK_CONN22_1 (0x41bc)
+#define AFE_SECURE_MASK_CONN22_2 (0x41c0)
+#define AFE_SECURE_MASK_CONN22_3 (0x41c4)
+#define AFE_SECURE_MASK_CONN22_4 (0x41c8)
+#define AFE_SECURE_MASK_CONN23 (0x41cc)
+#define AFE_SECURE_MASK_CONN23_1 (0x41d0)
+#define AFE_SECURE_MASK_CONN23_2 (0x41d4)
+#define AFE_SECURE_MASK_CONN23_3 (0x41d8)
+#define AFE_SECURE_MASK_CONN23_4 (0x41dc)
+#define AFE_SECURE_MASK_CONN24 (0x41e0)
+#define AFE_SECURE_MASK_CONN24_1 (0x41e4)
+#define AFE_SECURE_MASK_CONN24_2 (0x41e8)
+#define AFE_SECURE_MASK_CONN24_3 (0x41ec)
+#define AFE_SECURE_MASK_CONN24_4 (0x41f0)
+#define AFE_SECURE_MASK_CONN25 (0x41f4)
+#define AFE_SECURE_MASK_CONN25_1 (0x41f8)
+#define AFE_SECURE_MASK_CONN25_2 (0x41fc)
+#define AFE_SECURE_MASK_CONN25_3 (0x4200)
+#define AFE_SECURE_MASK_CONN25_4 (0x4204)
+#define AFE_SECURE_MASK_CONN26 (0x4208)
+#define AFE_SECURE_MASK_CONN26_1 (0x420c)
+#define AFE_SECURE_MASK_CONN26_2 (0x4210)
+#define AFE_SECURE_MASK_CONN26_3 (0x4214)
+#define AFE_SECURE_MASK_CONN26_4 (0x4218)
+#define AFE_SECURE_MASK_CONN27 (0x421c)
+#define AFE_SECURE_MASK_CONN27_1 (0x4220)
+#define AFE_SECURE_MASK_CONN27_2 (0x4224)
+#define AFE_SECURE_MASK_CONN27_3 (0x4228)
+#define AFE_SECURE_MASK_CONN27_4 (0x422c)
+#define AFE_SECURE_MASK_CONN28 (0x4230)
+#define AFE_SECURE_MASK_CONN28_1 (0x4234)
+#define AFE_SECURE_MASK_CONN28_2 (0x4238)
+#define AFE_SECURE_MASK_CONN28_3 (0x423c)
+#define AFE_SECURE_MASK_CONN28_4 (0x4240)
+#define AFE_SECURE_MASK_CONN29 (0x4244)
+#define AFE_SECURE_MASK_CONN29_1 (0x4248)
+#define AFE_SECURE_MASK_CONN29_2 (0x424c)
+#define AFE_SECURE_MASK_CONN29_3 (0x4250)
+#define AFE_SECURE_MASK_CONN29_4 (0x4254)
+#define AFE_SECURE_MASK_CONN30 (0x4258)
+#define AFE_SECURE_MASK_CONN30_1 (0x425c)
+#define AFE_SECURE_MASK_CONN30_2 (0x4260)
+#define AFE_SECURE_MASK_CONN30_3 (0x4264)
+#define AFE_SECURE_MASK_CONN30_4 (0x4268)
+#define AFE_SECURE_MASK_CONN31 (0x426c)
+#define AFE_SECURE_MASK_CONN31_1 (0x4270)
+#define AFE_SECURE_MASK_CONN31_2 (0x4274)
+#define AFE_SECURE_MASK_CONN31_3 (0x4278)
+#define AFE_SECURE_MASK_CONN31_4 (0x427c)
+#define AFE_SECURE_MASK_CONN32 (0x4280)
+#define AFE_SECURE_MASK_CONN32_1 (0x4284)
+#define AFE_SECURE_MASK_CONN32_2 (0x4288)
+#define AFE_SECURE_MASK_CONN32_3 (0x428c)
+#define AFE_SECURE_MASK_CONN32_4 (0x4290)
+#define AFE_SECURE_MASK_CONN33 (0x4294)
+#define AFE_SECURE_MASK_CONN33_1 (0x4298)
+#define AFE_SECURE_MASK_CONN33_2 (0x429c)
+#define AFE_SECURE_MASK_CONN33_3 (0x42a0)
+#define AFE_SECURE_MASK_CONN33_4 (0x42a4)
+#define AFE_SECURE_MASK_CONN34 (0x42a8)
+#define AFE_SECURE_MASK_CONN34_1 (0x42ac)
+#define AFE_SECURE_MASK_CONN34_2 (0x42b0)
+#define AFE_SECURE_MASK_CONN34_3 (0x42b4)
+#define AFE_SECURE_MASK_CONN34_4 (0x42b8)
+#define AFE_SECURE_MASK_CONN35 (0x42bc)
+#define AFE_SECURE_MASK_CONN35_1 (0x42c0)
+#define AFE_SECURE_MASK_CONN35_2 (0x42c4)
+#define AFE_SECURE_MASK_CONN35_3 (0x42c8)
+#define AFE_SECURE_MASK_CONN35_4 (0x42cc)
+#define AFE_SECURE_MASK_CONN36 (0x42d0)
+#define AFE_SECURE_MASK_CONN36_1 (0x42d4)
+#define AFE_SECURE_MASK_CONN36_2 (0x42d8)
+#define AFE_SECURE_MASK_CONN36_3 (0x42dc)
+#define AFE_SECURE_MASK_CONN36_4 (0x42e0)
+#define AFE_SECURE_MASK_CONN37 (0x42e4)
+#define AFE_SECURE_MASK_CONN37_1 (0x42e8)
+#define AFE_SECURE_MASK_CONN37_2 (0x42ec)
+#define AFE_SECURE_MASK_CONN37_3 (0x42f0)
+#define AFE_SECURE_MASK_CONN37_4 (0x42f4)
+#define AFE_SECURE_MASK_CONN38 (0x42f8)
+#define AFE_SECURE_MASK_CONN38_1 (0x42fc)
+#define AFE_SECURE_MASK_CONN38_2 (0x4300)
+#define AFE_SECURE_MASK_CONN38_3 (0x4304)
+#define AFE_SECURE_MASK_CONN38_4 (0x4308)
+#define AFE_SECURE_MASK_CONN39 (0x430c)
+#define AFE_SECURE_MASK_CONN39_1 (0x4310)
+#define AFE_SECURE_MASK_CONN39_2 (0x4314)
+#define AFE_SECURE_MASK_CONN39_3 (0x4318)
+#define AFE_SECURE_MASK_CONN39_4 (0x431c)
+#define AFE_SECURE_MASK_CONN40 (0x4320)
+#define AFE_SECURE_MASK_CONN40_1 (0x4324)
+#define AFE_SECURE_MASK_CONN40_2 (0x4328)
+#define AFE_SECURE_MASK_CONN40_3 (0x432c)
+#define AFE_SECURE_MASK_CONN40_4 (0x4330)
+#define AFE_SECURE_MASK_CONN41 (0x4334)
+#define AFE_SECURE_MASK_CONN41_1 (0x4338)
+#define AFE_SECURE_MASK_CONN41_2 (0x433c)
+#define AFE_SECURE_MASK_CONN41_3 (0x4340)
+#define AFE_SECURE_MASK_CONN41_4 (0x4344)
+#define AFE_SECURE_MASK_CONN42 (0x4348)
+#define AFE_SECURE_MASK_CONN42_1 (0x434c)
+#define AFE_SECURE_MASK_CONN42_2 (0x4350)
+#define AFE_SECURE_MASK_CONN42_3 (0x4354)
+#define AFE_SECURE_MASK_CONN42_4 (0x4358)
+#define AFE_SECURE_MASK_CONN43 (0x435c)
+#define AFE_SECURE_MASK_CONN43_1 (0x4360)
+#define AFE_SECURE_MASK_CONN43_2 (0x4364)
+#define AFE_SECURE_MASK_CONN43_3 (0x4368)
+#define AFE_SECURE_MASK_CONN43_4 (0x436c)
+#define AFE_SECURE_MASK_CONN44 (0x4370)
+#define AFE_SECURE_MASK_CONN44_1 (0x4374)
+#define AFE_SECURE_MASK_CONN44_2 (0x4378)
+#define AFE_SECURE_MASK_CONN44_3 (0x437c)
+#define AFE_SECURE_MASK_CONN44_4 (0x4380)
+#define AFE_SECURE_MASK_CONN45 (0x4384)
+#define AFE_SECURE_MASK_CONN45_1 (0x4388)
+#define AFE_SECURE_MASK_CONN45_2 (0x438c)
+#define AFE_SECURE_MASK_CONN45_3 (0x4390)
+#define AFE_SECURE_MASK_CONN45_4 (0x4394)
+#define AFE_SECURE_MASK_CONN46 (0x4398)
+#define AFE_SECURE_MASK_CONN46_1 (0x439c)
+#define AFE_SECURE_MASK_CONN46_2 (0x43a0)
+#define AFE_SECURE_MASK_CONN46_3 (0x43a4)
+#define AFE_SECURE_MASK_CONN46_4 (0x43a8)
+#define AFE_SECURE_MASK_CONN47 (0x43ac)
+#define AFE_SECURE_MASK_CONN47_1 (0x43b0)
+#define AFE_SECURE_MASK_CONN47_2 (0x43b4)
+#define AFE_SECURE_MASK_CONN47_3 (0x43b8)
+#define AFE_SECURE_MASK_CONN47_4 (0x43bc)
+#define AFE_SECURE_MASK_CONN48 (0x43c0)
+#define AFE_SECURE_MASK_CONN48_1 (0x43c4)
+#define AFE_SECURE_MASK_CONN48_2 (0x43c8)
+#define AFE_SECURE_MASK_CONN48_3 (0x43cc)
+#define AFE_SECURE_MASK_CONN48_4 (0x43d0)
+#define AFE_SECURE_MASK_CONN49 (0x43d4)
+#define AFE_SECURE_MASK_CONN49_1 (0x43d8)
+#define AFE_SECURE_MASK_CONN49_2 (0x43dc)
+#define AFE_SECURE_MASK_CONN49_3 (0x43e0)
+#define AFE_SECURE_MASK_CONN49_4 (0x43e4)
+#define AFE_SECURE_MASK_CONN50 (0x43e8)
+#define AFE_SECURE_MASK_CONN50_1 (0x43ec)
+#define AFE_SECURE_MASK_CONN50_2 (0x43f0)
+#define AFE_SECURE_MASK_CONN50_3 (0x43f4)
+#define AFE_SECURE_MASK_CONN50_4 (0x43f8)
+#define AFE_SECURE_MASK_CONN51 (0x43fc)
+#define AFE_SECURE_MASK_CONN51_1 (0x4400)
+#define AFE_SECURE_MASK_CONN51_2 (0x4404)
+#define AFE_SECURE_MASK_CONN51_3 (0x4408)
+#define AFE_SECURE_MASK_CONN51_4 (0x440c)
+#define AFE_SECURE_MASK_CONN52 (0x4410)
+#define AFE_SECURE_MASK_CONN52_1 (0x4414)
+#define AFE_SECURE_MASK_CONN52_2 (0x4418)
+#define AFE_SECURE_MASK_CONN52_3 (0x441c)
+#define AFE_SECURE_MASK_CONN52_4 (0x4420)
+#define AFE_SECURE_MASK_CONN53 (0x4424)
+#define AFE_SECURE_MASK_CONN53_1 (0x4428)
+#define AFE_SECURE_MASK_CONN53_2 (0x442c)
+#define AFE_SECURE_MASK_CONN53_3 (0x4430)
+#define AFE_SECURE_MASK_CONN53_4 (0x4434)
+#define AFE_SECURE_MASK_CONN54 (0x4438)
+#define AFE_SECURE_MASK_CONN54_1 (0x443c)
+#define AFE_SECURE_MASK_CONN54_2 (0x4440)
+#define AFE_SECURE_MASK_CONN54_3 (0x4444)
+#define AFE_SECURE_MASK_CONN54_4 (0x4448)
+#define AFE_SECURE_MASK_CONN55 (0x444c)
+#define AFE_SECURE_MASK_CONN55_1 (0x4450)
+#define AFE_SECURE_MASK_CONN55_2 (0x4454)
+#define AFE_SECURE_MASK_CONN55_3 (0x4458)
+#define AFE_SECURE_MASK_CONN55_4 (0x445c)
+#define AFE_SECURE_MASK_CONN56 (0x4460)
+#define AFE_SECURE_MASK_CONN56_1 (0x4464)
+#define AFE_SECURE_MASK_CONN56_2 (0x4468)
+#define AFE_SECURE_MASK_CONN56_3 (0x446c)
+#define AFE_SECURE_MASK_CONN56_4 (0x4470)
+#define AFE_SECURE_MASK_CONN57 (0x4474)
+#define AFE_SECURE_MASK_CONN57_1 (0x4478)
+#define AFE_SECURE_MASK_CONN57_2 (0x447c)
+#define AFE_SECURE_MASK_CONN57_3 (0x4480)
+#define AFE_SECURE_MASK_CONN57_4 (0x4484)
+#define AFE_SECURE_MASK_CONN58 (0x4488)
+#define AFE_SECURE_MASK_CONN58_1 (0x448c)
+#define AFE_SECURE_MASK_CONN58_2 (0x4490)
+#define AFE_SECURE_MASK_CONN58_3 (0x4494)
+#define AFE_SECURE_MASK_CONN58_4 (0x4498)
+#define AFE_SECURE_MASK_CONN59 (0x449c)
+#define AFE_SECURE_MASK_CONN59_1 (0x44a0)
+#define AFE_SECURE_MASK_CONN59_2 (0x44a4)
+#define AFE_SECURE_MASK_CONN59_3 (0x44a8)
+#define AFE_SECURE_MASK_CONN59_4 (0x44ac)
+#define AFE_SECURE_MASK_CONN60 (0x44b0)
+#define AFE_SECURE_MASK_CONN60_1 (0x44b4)
+#define AFE_SECURE_MASK_CONN60_2 (0x44b8)
+#define AFE_SECURE_MASK_CONN60_3 (0x44bc)
+#define AFE_SECURE_MASK_CONN60_4 (0x44c0)
+#define AFE_SECURE_MASK_CONN61 (0x44c4)
+#define AFE_SECURE_MASK_CONN61_1 (0x44c8)
+#define AFE_SECURE_MASK_CONN61_2 (0x44cc)
+#define AFE_SECURE_MASK_CONN61_3 (0x44d0)
+#define AFE_SECURE_MASK_CONN61_4 (0x44d4)
+#define AFE_SECURE_MASK_CONN62 (0x44d8)
+#define AFE_SECURE_MASK_CONN62_1 (0x44dc)
+#define AFE_SECURE_MASK_CONN62_2 (0x44e0)
+#define AFE_SECURE_MASK_CONN62_3 (0x44e4)
+#define AFE_SECURE_MASK_CONN62_4 (0x44e8)
+#define AFE_SECURE_MASK_CONN63 (0x44ec)
+#define AFE_SECURE_MASK_CONN63_1 (0x44f0)
+#define AFE_SECURE_MASK_CONN63_2 (0x44f4)
+#define AFE_SECURE_MASK_CONN63_3 (0x44f8)
+#define AFE_SECURE_MASK_CONN63_4 (0x44fc)
+#define AFE_SECURE_MASK_CONN64 (0x4500)
+#define AFE_SECURE_MASK_CONN64_1 (0x4504)
+#define AFE_SECURE_MASK_CONN64_2 (0x4508)
+#define AFE_SECURE_MASK_CONN64_3 (0x450c)
+#define AFE_SECURE_MASK_CONN64_4 (0x4510)
+#define AFE_SECURE_MASK_CONN65 (0x4514)
+#define AFE_SECURE_MASK_CONN65_1 (0x4518)
+#define AFE_SECURE_MASK_CONN65_2 (0x451c)
+#define AFE_SECURE_MASK_CONN65_3 (0x4520)
+#define AFE_SECURE_MASK_CONN65_4 (0x4524)
+#define AFE_SECURE_MASK_CONN66 (0x4528)
+#define AFE_SECURE_MASK_CONN66_1 (0x452c)
+#define AFE_SECURE_MASK_CONN66_2 (0x4530)
+#define AFE_SECURE_MASK_CONN66_3 (0x4534)
+#define AFE_SECURE_MASK_CONN66_4 (0x4538)
+#define AFE_SECURE_MASK_CONN67 (0x453c)
+#define AFE_SECURE_MASK_CONN67_1 (0x4540)
+#define AFE_SECURE_MASK_CONN67_2 (0x4544)
+#define AFE_SECURE_MASK_CONN67_3 (0x4548)
+#define AFE_SECURE_MASK_CONN67_4 (0x454c)
+#define AFE_SECURE_MASK_CONN68 (0x4550)
+#define AFE_SECURE_MASK_CONN68_1 (0x4554)
+#define AFE_SECURE_MASK_CONN68_2 (0x4558)
+#define AFE_SECURE_MASK_CONN68_3 (0x455c)
+#define AFE_SECURE_MASK_CONN68_4 (0x4560)
+#define AFE_SECURE_MASK_CONN69 (0x4564)
+#define AFE_SECURE_MASK_CONN69_1 (0x4568)
+#define AFE_SECURE_MASK_CONN69_2 (0x456c)
+#define AFE_SECURE_MASK_CONN69_3 (0x4570)
+#define AFE_SECURE_MASK_CONN69_4 (0x4574)
+#define AFE_SECURE_MASK_CONN70 (0x4578)
+#define AFE_SECURE_MASK_CONN70_1 (0x457c)
+#define AFE_SECURE_MASK_CONN70_2 (0x4580)
+#define AFE_SECURE_MASK_CONN70_3 (0x4584)
+#define AFE_SECURE_MASK_CONN70_4 (0x4588)
+#define AFE_SECURE_MASK_CONN71 (0x458c)
+#define AFE_SECURE_MASK_CONN71_1 (0x4590)
+#define AFE_SECURE_MASK_CONN71_2 (0x4594)
+#define AFE_SECURE_MASK_CONN71_3 (0x4598)
+#define AFE_SECURE_MASK_CONN71_4 (0x459c)
+#define AFE_SECURE_MASK_CONN72 (0x45a0)
+#define AFE_SECURE_MASK_CONN72_1 (0x45a4)
+#define AFE_SECURE_MASK_CONN72_2 (0x45a8)
+#define AFE_SECURE_MASK_CONN72_3 (0x45ac)
+#define AFE_SECURE_MASK_CONN72_4 (0x45b0)
+#define AFE_SECURE_MASK_CONN73 (0x45b4)
+#define AFE_SECURE_MASK_CONN73_1 (0x45b8)
+#define AFE_SECURE_MASK_CONN73_2 (0x45bc)
+#define AFE_SECURE_MASK_CONN73_3 (0x45c0)
+#define AFE_SECURE_MASK_CONN73_4 (0x45c4)
+#define AFE_SECURE_MASK_CONN74 (0x45c8)
+#define AFE_SECURE_MASK_CONN74_1 (0x45cc)
+#define AFE_SECURE_MASK_CONN74_2 (0x45d0)
+#define AFE_SECURE_MASK_CONN74_3 (0x45d4)
+#define AFE_SECURE_MASK_CONN74_4 (0x45d8)
+#define AFE_SECURE_MASK_CONN75 (0x45dc)
+#define AFE_SECURE_MASK_CONN75_1 (0x45e0)
+#define AFE_SECURE_MASK_CONN75_2 (0x45e4)
+#define AFE_SECURE_MASK_CONN75_3 (0x45e8)
+#define AFE_SECURE_MASK_CONN75_4 (0x45ec)
+#define AFE_SECURE_MASK_CONN76 (0x45f0)
+#define AFE_SECURE_MASK_CONN76_1 (0x45f4)
+#define AFE_SECURE_MASK_CONN76_2 (0x45f8)
+#define AFE_SECURE_MASK_CONN76_3 (0x45fc)
+#define AFE_SECURE_MASK_CONN76_4 (0x4600)
+#define AFE_SECURE_MASK_CONN77 (0x4604)
+#define AFE_SECURE_MASK_CONN77_1 (0x4608)
+#define AFE_SECURE_MASK_CONN77_2 (0x460c)
+#define AFE_SECURE_MASK_CONN77_3 (0x4610)
+#define AFE_SECURE_MASK_CONN77_4 (0x4614)
+#define AFE_SECURE_MASK_CONN78 (0x4618)
+#define AFE_SECURE_MASK_CONN78_1 (0x461c)
+#define AFE_SECURE_MASK_CONN78_2 (0x4620)
+#define AFE_SECURE_MASK_CONN78_3 (0x4624)
+#define AFE_SECURE_MASK_CONN78_4 (0x4628)
+#define AFE_SECURE_MASK_CONN79 (0x462c)
+#define AFE_SECURE_MASK_CONN79_1 (0x4630)
+#define AFE_SECURE_MASK_CONN79_2 (0x4634)
+#define AFE_SECURE_MASK_CONN79_3 (0x4638)
+#define AFE_SECURE_MASK_CONN79_4 (0x463c)
+#define AFE_SECURE_MASK_CONN80 (0x4640)
+#define AFE_SECURE_MASK_CONN80_1 (0x4644)
+#define AFE_SECURE_MASK_CONN80_2 (0x4648)
+#define AFE_SECURE_MASK_CONN80_3 (0x464c)
+#define AFE_SECURE_MASK_CONN80_4 (0x4650)
+#define AFE_SECURE_MASK_CONN81 (0x4654)
+#define AFE_SECURE_MASK_CONN81_1 (0x4658)
+#define AFE_SECURE_MASK_CONN81_2 (0x465c)
+#define AFE_SECURE_MASK_CONN81_3 (0x4660)
+#define AFE_SECURE_MASK_CONN81_4 (0x4664)
+#define AFE_SECURE_MASK_CONN82 (0x4668)
+#define AFE_SECURE_MASK_CONN82_1 (0x466c)
+#define AFE_SECURE_MASK_CONN82_2 (0x4670)
+#define AFE_SECURE_MASK_CONN82_3 (0x4674)
+#define AFE_SECURE_MASK_CONN82_4 (0x4678)
+#define AFE_SECURE_MASK_CONN83 (0x467c)
+#define AFE_SECURE_MASK_CONN83_1 (0x4680)
+#define AFE_SECURE_MASK_CONN83_2 (0x4684)
+#define AFE_SECURE_MASK_CONN83_3 (0x4688)
+#define AFE_SECURE_MASK_CONN83_4 (0x468c)
+#define AFE_SECURE_MASK_CONN84 (0x4690)
+#define AFE_SECURE_MASK_CONN84_1 (0x4694)
+#define AFE_SECURE_MASK_CONN84_2 (0x4698)
+#define AFE_SECURE_MASK_CONN84_3 (0x469c)
+#define AFE_SECURE_MASK_CONN84_4 (0x46a0)
+#define AFE_SECURE_MASK_CONN85 (0x46a4)
+#define AFE_SECURE_MASK_CONN85_1 (0x46a8)
+#define AFE_SECURE_MASK_CONN85_2 (0x46ac)
+#define AFE_SECURE_MASK_CONN85_3 (0x46b0)
+#define AFE_SECURE_MASK_CONN85_4 (0x46b4)
+#define AFE_SECURE_MASK_CONN86 (0x46b8)
+#define AFE_SECURE_MASK_CONN86_1 (0x46bc)
+#define AFE_SECURE_MASK_CONN86_2 (0x46c0)
+#define AFE_SECURE_MASK_CONN86_3 (0x46c4)
+#define AFE_SECURE_MASK_CONN86_4 (0x46c8)
+#define AFE_SECURE_MASK_CONN87 (0x46cc)
+#define AFE_SECURE_MASK_CONN87_1 (0x46d0)
+#define AFE_SECURE_MASK_CONN87_2 (0x46d4)
+#define AFE_SECURE_MASK_CONN87_3 (0x46d8)
+#define AFE_SECURE_MASK_CONN87_4 (0x46dc)
+#define AFE_SECURE_MASK_CONN88 (0x46e0)
+#define AFE_SECURE_MASK_CONN88_1 (0x46e4)
+#define AFE_SECURE_MASK_CONN88_2 (0x46e8)
+#define AFE_SECURE_MASK_CONN88_3 (0x46ec)
+#define AFE_SECURE_MASK_CONN88_4 (0x46f0)
+#define AFE_SECURE_MASK_CONN89 (0x46f4)
+#define AFE_SECURE_MASK_CONN89_1 (0x46f8)
+#define AFE_SECURE_MASK_CONN89_2 (0x46fc)
+#define AFE_SECURE_MASK_CONN89_3 (0x4700)
+#define AFE_SECURE_MASK_CONN89_4 (0x4704)
+#define AFE_SECURE_MASK_CONN90 (0x4708)
+#define AFE_SECURE_MASK_CONN90_1 (0x470c)
+#define AFE_SECURE_MASK_CONN90_2 (0x4710)
+#define AFE_SECURE_MASK_CONN90_3 (0x4714)
+#define AFE_SECURE_MASK_CONN90_4 (0x4718)
+#define AFE_SECURE_MASK_CONN91 (0x471c)
+#define AFE_SECURE_MASK_CONN91_1 (0x4720)
+#define AFE_SECURE_MASK_CONN91_2 (0x4724)
+#define AFE_SECURE_MASK_CONN91_3 (0x4728)
+#define AFE_SECURE_MASK_CONN91_4 (0x472c)
+#define AFE_SECURE_MASK_CONN92 (0x4730)
+#define AFE_SECURE_MASK_CONN92_1 (0x4734)
+#define AFE_SECURE_MASK_CONN92_2 (0x4738)
+#define AFE_SECURE_MASK_CONN92_3 (0x473c)
+#define AFE_SECURE_MASK_CONN92_4 (0x4740)
+#define AFE_SECURE_MASK_CONN93 (0x4744)
+#define AFE_SECURE_MASK_CONN93_1 (0x4748)
+#define AFE_SECURE_MASK_CONN93_2 (0x474c)
+#define AFE_SECURE_MASK_CONN93_3 (0x4750)
+#define AFE_SECURE_MASK_CONN93_4 (0x4754)
+#define AFE_SECURE_MASK_CONN94 (0x4758)
+#define AFE_SECURE_MASK_CONN94_1 (0x475c)
+#define AFE_SECURE_MASK_CONN94_2 (0x4760)
+#define AFE_SECURE_MASK_CONN94_3 (0x4764)
+#define AFE_SECURE_MASK_CONN94_4 (0x4768)
+#define AFE_SECURE_MASK_CONN95 (0x476c)
+#define AFE_SECURE_MASK_CONN95_1 (0x4770)
+#define AFE_SECURE_MASK_CONN95_2 (0x4774)
+#define AFE_SECURE_MASK_CONN95_3 (0x4778)
+#define AFE_SECURE_MASK_CONN95_4 (0x477c)
+#define AFE_SECURE_MASK_CONN96 (0x4780)
+#define AFE_SECURE_MASK_CONN96_1 (0x4784)
+#define AFE_SECURE_MASK_CONN96_2 (0x4788)
+#define AFE_SECURE_MASK_CONN96_3 (0x478c)
+#define AFE_SECURE_MASK_CONN96_4 (0x4790)
+#define AFE_SECURE_MASK_CONN97 (0x4794)
+#define AFE_SECURE_MASK_CONN97_1 (0x4798)
+#define AFE_SECURE_MASK_CONN97_2 (0x479c)
+#define AFE_SECURE_MASK_CONN97_3 (0x47a0)
+#define AFE_SECURE_MASK_CONN97_4 (0x47a4)
+#define AFE_SECURE_MASK_CONN98 (0x47a8)
+#define AFE_SECURE_MASK_CONN98_1 (0x47ac)
+#define AFE_SECURE_MASK_CONN98_2 (0x47b0)
+#define AFE_SECURE_MASK_CONN98_3 (0x47b4)
+#define AFE_SECURE_MASK_CONN98_4 (0x47b8)
+#define AFE_SECURE_MASK_CONN99 (0x47bc)
+#define AFE_SECURE_MASK_CONN99_1 (0x47c0)
+#define AFE_SECURE_MASK_CONN99_2 (0x47c4)
+#define AFE_SECURE_MASK_CONN99_3 (0x47c8)
+#define AFE_SECURE_MASK_CONN99_4 (0x47cc)
+#define AFE_SECURE_MASK_CONN100 (0x47d0)
+#define AFE_SECURE_MASK_CONN100_1 (0x47d4)
+#define AFE_SECURE_MASK_CONN100_2 (0x47d8)
+#define AFE_SECURE_MASK_CONN100_3 (0x47dc)
+#define AFE_SECURE_MASK_CONN100_4 (0x47e0)
+#define AFE_SECURE_MASK_CONN101 (0x47e4)
+#define AFE_SECURE_MASK_CONN101_1 (0x47e8)
+#define AFE_SECURE_MASK_CONN101_2 (0x47ec)
+#define AFE_SECURE_MASK_CONN101_3 (0x47f0)
+#define AFE_SECURE_MASK_CONN101_4 (0x47f4)
+#define AFE_SECURE_MASK_CONN102 (0x47f8)
+#define AFE_SECURE_MASK_CONN102_1 (0x47fc)
+#define AFE_SECURE_MASK_CONN102_2 (0x4800)
+#define AFE_SECURE_MASK_CONN102_3 (0x4804)
+#define AFE_SECURE_MASK_CONN102_4 (0x4808)
+#define AFE_SECURE_MASK_CONN103 (0x480c)
+#define AFE_SECURE_MASK_CONN103_1 (0x4810)
+#define AFE_SECURE_MASK_CONN103_2 (0x4814)
+#define AFE_SECURE_MASK_CONN103_3 (0x4818)
+#define AFE_SECURE_MASK_CONN103_4 (0x481c)
+#define AFE_SECURE_MASK_CONN104 (0x4820)
+#define AFE_SECURE_MASK_CONN104_1 (0x4824)
+#define AFE_SECURE_MASK_CONN104_2 (0x4828)
+#define AFE_SECURE_MASK_CONN104_3 (0x482c)
+#define AFE_SECURE_MASK_CONN104_4 (0x4830)
+#define AFE_SECURE_MASK_CONN105 (0x4834)
+#define AFE_SECURE_MASK_CONN105_1 (0x4838)
+#define AFE_SECURE_MASK_CONN105_2 (0x483c)
+#define AFE_SECURE_MASK_CONN105_3 (0x4840)
+#define AFE_SECURE_MASK_CONN105_4 (0x4844)
+#define AFE_SECURE_MASK_CONN106 (0x4848)
+#define AFE_SECURE_MASK_CONN106_1 (0x484c)
+#define AFE_SECURE_MASK_CONN106_2 (0x4850)
+#define AFE_SECURE_MASK_CONN106_3 (0x4854)
+#define AFE_SECURE_MASK_CONN106_4 (0x4858)
+#define AFE_SECURE_MASK_CONN107 (0x485c)
+#define AFE_SECURE_MASK_CONN107_1 (0x4860)
+#define AFE_SECURE_MASK_CONN107_2 (0x4864)
+#define AFE_SECURE_MASK_CONN107_3 (0x4868)
+#define AFE_SECURE_MASK_CONN107_4 (0x486c)
+#define AFE_SECURE_MASK_CONN108 (0x4870)
+#define AFE_SECURE_MASK_CONN108_1 (0x4874)
+#define AFE_SECURE_MASK_CONN108_2 (0x4878)
+#define AFE_SECURE_MASK_CONN108_3 (0x487c)
+#define AFE_SECURE_MASK_CONN108_4 (0x4880)
+#define AFE_SECURE_MASK_CONN109 (0x4884)
+#define AFE_SECURE_MASK_CONN109_1 (0x4888)
+#define AFE_SECURE_MASK_CONN109_2 (0x488c)
+#define AFE_SECURE_MASK_CONN109_3 (0x4890)
+#define AFE_SECURE_MASK_CONN109_4 (0x4894)
+#define AFE_SECURE_MASK_CONN110 (0x4898)
+#define AFE_SECURE_MASK_CONN110_1 (0x489c)
+#define AFE_SECURE_MASK_CONN110_2 (0x48a0)
+#define AFE_SECURE_MASK_CONN110_3 (0x48a4)
+#define AFE_SECURE_MASK_CONN110_4 (0x48a8)
+#define AFE_SECURE_MASK_CONN111 (0x48ac)
+#define AFE_SECURE_MASK_CONN111_1 (0x48b0)
+#define AFE_SECURE_MASK_CONN111_2 (0x48b4)
+#define AFE_SECURE_MASK_CONN111_3 (0x48b8)
+#define AFE_SECURE_MASK_CONN111_4 (0x48bc)
+#define AFE_SECURE_MASK_CONN112 (0x48c0)
+#define AFE_SECURE_MASK_CONN112_1 (0x48c4)
+#define AFE_SECURE_MASK_CONN112_2 (0x48c8)
+#define AFE_SECURE_MASK_CONN112_3 (0x48cc)
+#define AFE_SECURE_MASK_CONN112_4 (0x48d0)
+#define AFE_SECURE_MASK_CONN113 (0x48d4)
+#define AFE_SECURE_MASK_CONN113_1 (0x48d8)
+#define AFE_SECURE_MASK_CONN113_2 (0x48dc)
+#define AFE_SECURE_MASK_CONN113_3 (0x48e0)
+#define AFE_SECURE_MASK_CONN113_4 (0x48e4)
+#define AFE_SECURE_MASK_CONN114 (0x48e8)
+#define AFE_SECURE_MASK_CONN114_1 (0x48ec)
+#define AFE_SECURE_MASK_CONN114_2 (0x48f0)
+#define AFE_SECURE_MASK_CONN114_3 (0x48f4)
+#define AFE_SECURE_MASK_CONN114_4 (0x48f8)
+#define AFE_SECURE_MASK_CONN115 (0x48fc)
+#define AFE_SECURE_MASK_CONN115_1 (0x4900)
+#define AFE_SECURE_MASK_CONN115_2 (0x4904)
+#define AFE_SECURE_MASK_CONN115_3 (0x4908)
+#define AFE_SECURE_MASK_CONN115_4 (0x490c)
+#define AFE_SECURE_MASK_CONN116 (0x4910)
+#define AFE_SECURE_MASK_CONN116_1 (0x4914)
+#define AFE_SECURE_MASK_CONN116_2 (0x4918)
+#define AFE_SECURE_MASK_CONN116_3 (0x491c)
+#define AFE_SECURE_MASK_CONN116_4 (0x4920)
+#define AFE_SECURE_MASK_CONN117 (0x4924)
+#define AFE_SECURE_MASK_CONN117_1 (0x4928)
+#define AFE_SECURE_MASK_CONN117_2 (0x492c)
+#define AFE_SECURE_MASK_CONN117_3 (0x4930)
+#define AFE_SECURE_MASK_CONN117_4 (0x4934)
+#define AFE_SECURE_MASK_CONN118 (0x4938)
+#define AFE_SECURE_MASK_CONN118_1 (0x493c)
+#define AFE_SECURE_MASK_CONN118_2 (0x4940)
+#define AFE_SECURE_MASK_CONN118_3 (0x4944)
+#define AFE_SECURE_MASK_CONN118_4 (0x4948)
+#define AFE_SECURE_MASK_CONN119 (0x494c)
+#define AFE_SECURE_MASK_CONN119_1 (0x4950)
+#define AFE_SECURE_MASK_CONN119_2 (0x4954)
+#define AFE_SECURE_MASK_CONN119_3 (0x4958)
+#define AFE_SECURE_MASK_CONN119_4 (0x495c)
+#define AFE_SECURE_MASK_CONN120 (0x4960)
+#define AFE_SECURE_MASK_CONN120_1 (0x4964)
+#define AFE_SECURE_MASK_CONN120_2 (0x4968)
+#define AFE_SECURE_MASK_CONN120_3 (0x496c)
+#define AFE_SECURE_MASK_CONN120_4 (0x4970)
+#define AFE_SECURE_MASK_CONN121 (0x4974)
+#define AFE_SECURE_MASK_CONN121_1 (0x4978)
+#define AFE_SECURE_MASK_CONN121_2 (0x497c)
+#define AFE_SECURE_MASK_CONN121_3 (0x4980)
+#define AFE_SECURE_MASK_CONN121_4 (0x4984)
+#define AFE_SECURE_MASK_CONN122 (0x4988)
+#define AFE_SECURE_MASK_CONN122_1 (0x498c)
+#define AFE_SECURE_MASK_CONN122_2 (0x4990)
+#define AFE_SECURE_MASK_CONN122_3 (0x4994)
+#define AFE_SECURE_MASK_CONN122_4 (0x4998)
+#define AFE_SECURE_MASK_CONN123 (0x499c)
+#define AFE_SECURE_MASK_CONN123_1 (0x49a0)
+#define AFE_SECURE_MASK_CONN123_2 (0x49a4)
+#define AFE_SECURE_MASK_CONN123_3 (0x49a8)
+#define AFE_SECURE_MASK_CONN123_4 (0x49ac)
+#define AFE_SECURE_MASK_CONN124 (0x49b0)
+#define AFE_SECURE_MASK_CONN124_1 (0x49b4)
+#define AFE_SECURE_MASK_CONN124_2 (0x49b8)
+#define AFE_SECURE_MASK_CONN124_3 (0x49bc)
+#define AFE_SECURE_MASK_CONN124_4 (0x49c0)
+#define AFE_SECURE_MASK_CONN125 (0x49c4)
+#define AFE_SECURE_MASK_CONN125_1 (0x49c8)
+#define AFE_SECURE_MASK_CONN125_2 (0x49cc)
+#define AFE_SECURE_MASK_CONN125_3 (0x49d0)
+#define AFE_SECURE_MASK_CONN125_4 (0x49d4)
+#define AFE_SECURE_MASK_CONN126 (0x49d8)
+#define AFE_SECURE_MASK_CONN126_1 (0x49dc)
+#define AFE_SECURE_MASK_CONN126_2 (0x49e0)
+#define AFE_SECURE_MASK_CONN126_3 (0x49e4)
+#define AFE_SECURE_MASK_CONN126_4 (0x49e8)
+#define AFE_SECURE_MASK_CONN127 (0x49ec)
+#define AFE_SECURE_MASK_CONN127_1 (0x49f0)
+#define AFE_SECURE_MASK_CONN127_2 (0x49f4)
+#define AFE_SECURE_MASK_CONN127_3 (0x49f8)
+#define AFE_SECURE_MASK_CONN127_4 (0x49fc)
+#define AFE_SECURE_MASK_CONN128 (0x4a00)
+#define AFE_SECURE_MASK_CONN128_1 (0x4a04)
+#define AFE_SECURE_MASK_CONN128_2 (0x4a08)
+#define AFE_SECURE_MASK_CONN128_3 (0x4a0c)
+#define AFE_SECURE_MASK_CONN128_4 (0x4a10)
+#define AFE_SECURE_MASK_CONN129 (0x4a14)
+#define AFE_SECURE_MASK_CONN129_1 (0x4a18)
+#define AFE_SECURE_MASK_CONN129_2 (0x4a1c)
+#define AFE_SECURE_MASK_CONN129_3 (0x4a20)
+#define AFE_SECURE_MASK_CONN129_4 (0x4a24)
+#define AFE_SECURE_MASK_CONN130 (0x4a28)
+#define AFE_SECURE_MASK_CONN130_1 (0x4a2c)
+#define AFE_SECURE_MASK_CONN130_2 (0x4a30)
+#define AFE_SECURE_MASK_CONN130_3 (0x4a34)
+#define AFE_SECURE_MASK_CONN130_4 (0x4a38)
+#define AFE_SECURE_MASK_CONN131 (0x4a3c)
+#define AFE_SECURE_MASK_CONN131_1 (0x4a40)
+#define AFE_SECURE_MASK_CONN131_2 (0x4a44)
+#define AFE_SECURE_MASK_CONN131_3 (0x4a48)
+#define AFE_SECURE_MASK_CONN131_4 (0x4a4c)
+#define AFE_SECURE_MASK_CONN132 (0x4a50)
+#define AFE_SECURE_MASK_CONN132_1 (0x4a54)
+#define AFE_SECURE_MASK_CONN132_2 (0x4a58)
+#define AFE_SECURE_MASK_CONN132_3 (0x4a5c)
+#define AFE_SECURE_MASK_CONN132_4 (0x4a60)
+#define AFE_SECURE_MASK_CONN133 (0x4a64)
+#define AFE_SECURE_MASK_CONN133_1 (0x4a68)
+#define AFE_SECURE_MASK_CONN133_2 (0x4a6c)
+#define AFE_SECURE_MASK_CONN133_3 (0x4a70)
+#define AFE_SECURE_MASK_CONN133_4 (0x4a74)
+#define AFE_SECURE_MASK_CONN134 (0x4a78)
+#define AFE_SECURE_MASK_CONN134_1 (0x4a7c)
+#define AFE_SECURE_MASK_CONN134_2 (0x4a80)
+#define AFE_SECURE_MASK_CONN134_3 (0x4a84)
+#define AFE_SECURE_MASK_CONN134_4 (0x4a88)
+#define AFE_SECURE_MASK_CONN135 (0x4a8c)
+#define AFE_SECURE_MASK_CONN135_1 (0x4a90)
+#define AFE_SECURE_MASK_CONN135_2 (0x4a94)
+#define AFE_SECURE_MASK_CONN135_3 (0x4a98)
+#define AFE_SECURE_MASK_CONN135_4 (0x4a9c)
+#define AFE_SECURE_MASK_CONN136 (0x4aa0)
+#define AFE_SECURE_MASK_CONN136_1 (0x4aa4)
+#define AFE_SECURE_MASK_CONN136_2 (0x4aa8)
+#define AFE_SECURE_MASK_CONN136_3 (0x4aac)
+#define AFE_SECURE_MASK_CONN136_4 (0x4ab0)
+#define AFE_SECURE_MASK_CONN137 (0x4ab4)
+#define AFE_SECURE_MASK_CONN137_1 (0x4ab8)
+#define AFE_SECURE_MASK_CONN137_2 (0x4abc)
+#define AFE_SECURE_MASK_CONN137_3 (0x4ac0)
+#define AFE_SECURE_MASK_CONN137_4 (0x4ac4)
+#define AFE_SECURE_MASK_CONN138 (0x4ac8)
+#define AFE_SECURE_MASK_CONN138_1 (0x4acc)
+#define AFE_SECURE_MASK_CONN138_2 (0x4ad0)
+#define AFE_SECURE_MASK_CONN138_3 (0x4ad4)
+#define AFE_SECURE_MASK_CONN138_4 (0x4ad8)
+#define AFE_SECURE_MASK_CONN139 (0x4adc)
+#define AFE_SECURE_MASK_CONN139_1 (0x4ae0)
+#define AFE_SECURE_MASK_CONN139_2 (0x4ae4)
+#define AFE_SECURE_MASK_CONN139_3 (0x4ae8)
+#define AFE_SECURE_MASK_CONN139_4 (0x4aec)
+#define AFE_SECURE_MASK_CONN_RS (0x4af0)
+#define AFE_SECURE_MASK_CONN_RS_1 (0x4af4)
+#define AFE_SECURE_MASK_CONN_RS_2 (0x4af8)
+#define AFE_SECURE_MASK_CONN_RS_3 (0x4afc)
+#define AFE_SECURE_MASK_CONN_RS_4 (0x4b00)
+#define AFE_SECURE_MASK_CONN_16BIT (0x4b04)
+#define AFE_SECURE_MASK_CONN_16BIT_1 (0x4b08)
+#define AFE_SECURE_MASK_CONN_16BIT_2 (0x4b0c)
+#define AFE_SECURE_MASK_CONN_16BIT_3 (0x4b10)
+#define AFE_SECURE_MASK_CONN_16BIT_4 (0x4b14)
+#define AFE_SECURE_MASK_CONN_24BIT (0x4b18)
+#define AFE_SECURE_MASK_CONN_24BIT_1 (0x4b1c)
+#define AFE_SECURE_MASK_CONN_24BIT_2 (0x4b20)
+#define AFE_SECURE_MASK_CONN_24BIT_3 (0x4b24)
+#define AFE_SECURE_MASK_CONN_24BIT_4 (0x4b28)
+#define AFE_GASRC0_NEW_CON0 (0x4c40)
+#define AFE_GASRC0_NEW_CON1 (0x4c44)
+#define AFE_GASRC0_NEW_CON2 (0x4c48)
+#define AFE_GASRC0_NEW_CON3 (0x4c4c)
+#define AFE_GASRC0_NEW_CON4 (0x4c50)
+#define AFE_GASRC0_NEW_CON5 (0x4c54)
+#define AFE_GASRC0_NEW_CON6 (0x4c58)
+#define AFE_GASRC0_NEW_CON7 (0x4c5c)
+#define AFE_GASRC0_NEW_CON8 (0x4c60)
+#define AFE_GASRC0_NEW_CON9 (0x4c64)
+#define AFE_GASRC0_NEW_CON10 (0x4c68)
+#define AFE_GASRC0_NEW_CON11 (0x4c6c)
+#define AFE_GASRC0_NEW_CON12 (0x4c70)
+#define AFE_GASRC0_NEW_CON13 (0x4c74)
+#define AFE_GASRC0_NEW_CON14 (0x4c78)
+#define AFE_GASRC1_NEW_CON0 (0x4c80)
+#define AFE_GASRC1_NEW_CON1 (0x4c84)
+#define AFE_GASRC1_NEW_CON2 (0x4c88)
+#define AFE_GASRC1_NEW_CON3 (0x4c8c)
+#define AFE_GASRC1_NEW_CON4 (0x4c90)
+#define AFE_GASRC1_NEW_CON5 (0x4c94)
+#define AFE_GASRC1_NEW_CON6 (0x4c98)
+#define AFE_GASRC1_NEW_CON7 (0x4c9c)
+#define AFE_GASRC1_NEW_CON8 (0x4ca0)
+#define AFE_GASRC1_NEW_CON9 (0x4ca4)
+#define AFE_GASRC1_NEW_CON10 (0x4ca8)
+#define AFE_GASRC1_NEW_CON11 (0x4cac)
+#define AFE_GASRC1_NEW_CON12 (0x4cb0)
+#define AFE_GASRC1_NEW_CON13 (0x4cb4)
+#define AFE_GASRC1_NEW_CON14 (0x4cb8)
+#define AFE_GASRC2_NEW_CON0 (0x4cc0)
+#define AFE_GASRC2_NEW_CON1 (0x4cc4)
+#define AFE_GASRC2_NEW_CON2 (0x4cc8)
+#define AFE_GASRC2_NEW_CON3 (0x4ccc)
+#define AFE_GASRC2_NEW_CON4 (0x4cd0)
+#define AFE_GASRC2_NEW_CON5 (0x4cd4)
+#define AFE_GASRC2_NEW_CON6 (0x4cd8)
+#define AFE_GASRC2_NEW_CON7 (0x4cdc)
+#define AFE_GASRC2_NEW_CON8 (0x4ce0)
+#define AFE_GASRC2_NEW_CON9 (0x4ce4)
+#define AFE_GASRC2_NEW_CON10 (0x4ce8)
+#define AFE_GASRC2_NEW_CON11 (0x4cec)
+#define AFE_GASRC2_NEW_CON12 (0x4cf0)
+#define AFE_GASRC2_NEW_CON13 (0x4cf4)
+#define AFE_GASRC2_NEW_CON14 (0x4cf8)
+#define AFE_GASRC3_NEW_CON0 (0x4d00)
+#define AFE_GASRC3_NEW_CON1 (0x4d04)
+#define AFE_GASRC3_NEW_CON2 (0x4d08)
+#define AFE_GASRC3_NEW_CON3 (0x4d0c)
+#define AFE_GASRC3_NEW_CON4 (0x4d10)
+#define AFE_GASRC3_NEW_CON5 (0x4d14)
+#define AFE_GASRC3_NEW_CON6 (0x4d18)
+#define AFE_GASRC3_NEW_CON7 (0x4d1c)
+#define AFE_GASRC3_NEW_CON8 (0x4d20)
+#define AFE_GASRC3_NEW_CON9 (0x4d24)
+#define AFE_GASRC3_NEW_CON10 (0x4d28)
+#define AFE_GASRC3_NEW_CON11 (0x4d2c)
+#define AFE_GASRC3_NEW_CON12 (0x4d30)
+#define AFE_GASRC3_NEW_CON13 (0x4d34)
+#define AFE_GASRC3_NEW_CON14 (0x4d38)
+#define AFE_GASRC4_NEW_CON0 (0x4d40)
+#define AFE_GASRC4_NEW_CON1 (0x4d44)
+#define AFE_GASRC4_NEW_CON2 (0x4d48)
+#define AFE_GASRC4_NEW_CON3 (0x4d4c)
+#define AFE_GASRC4_NEW_CON4 (0x4d50)
+#define AFE_GASRC4_NEW_CON5 (0x4d54)
+#define AFE_GASRC4_NEW_CON6 (0x4d58)
+#define AFE_GASRC4_NEW_CON7 (0x4d5c)
+#define AFE_GASRC4_NEW_CON8 (0x4d60)
+#define AFE_GASRC4_NEW_CON9 (0x4d64)
+#define AFE_GASRC4_NEW_CON10 (0x4d68)
+#define AFE_GASRC4_NEW_CON11 (0x4d6c)
+#define AFE_GASRC4_NEW_CON12 (0x4d70)
+#define AFE_GASRC4_NEW_CON13 (0x4d74)
+#define AFE_GASRC4_NEW_CON14 (0x4d78)
+#define AFE_GASRC5_NEW_CON0 (0x4d80)
+#define AFE_GASRC5_NEW_CON1 (0x4d84)
+#define AFE_GASRC5_NEW_CON2 (0x4d88)
+#define AFE_GASRC5_NEW_CON3 (0x4d8c)
+#define AFE_GASRC5_NEW_CON4 (0x4d90)
+#define AFE_GASRC5_NEW_CON5 (0x4d94)
+#define AFE_GASRC5_NEW_CON6 (0x4d98)
+#define AFE_GASRC5_NEW_CON7 (0x4d9c)
+#define AFE_GASRC5_NEW_CON8 (0x4da0)
+#define AFE_GASRC5_NEW_CON9 (0x4da4)
+#define AFE_GASRC5_NEW_CON10 (0x4da8)
+#define AFE_GASRC5_NEW_CON11 (0x4dac)
+#define AFE_GASRC5_NEW_CON12 (0x4db0)
+#define AFE_GASRC5_NEW_CON13 (0x4db4)
+#define AFE_GASRC5_NEW_CON14 (0x4db8)
+#define AFE_GASRC6_NEW_CON0 (0x4dc0)
+#define AFE_GASRC6_NEW_CON1 (0x4dc4)
+#define AFE_GASRC6_NEW_CON2 (0x4dc8)
+#define AFE_GASRC6_NEW_CON3 (0x4dcc)
+#define AFE_GASRC6_NEW_CON4 (0x4dd0)
+#define AFE_GASRC6_NEW_CON5 (0x4dd4)
+#define AFE_GASRC6_NEW_CON6 (0x4dd8)
+#define AFE_GASRC6_NEW_CON7 (0x4ddc)
+#define AFE_GASRC6_NEW_CON8 (0x4de0)
+#define AFE_GASRC6_NEW_CON9 (0x4de4)
+#define AFE_GASRC6_NEW_CON10 (0x4de8)
+#define AFE_GASRC6_NEW_CON11 (0x4dec)
+#define AFE_GASRC6_NEW_CON12 (0x4df0)
+#define AFE_GASRC6_NEW_CON13 (0x4df4)
+#define AFE_GASRC6_NEW_CON14 (0x4df8)
+#define AFE_GASRC7_NEW_CON0 (0x4e00)
+#define AFE_GASRC7_NEW_CON1 (0x4e04)
+#define AFE_GASRC7_NEW_CON2 (0x4e08)
+#define AFE_GASRC7_NEW_CON3 (0x4e0c)
+#define AFE_GASRC7_NEW_CON4 (0x4e10)
+#define AFE_GASRC7_NEW_CON5 (0x4e14)
+#define AFE_GASRC7_NEW_CON6 (0x4e18)
+#define AFE_GASRC7_NEW_CON7 (0x4e1c)
+#define AFE_GASRC7_NEW_CON8 (0x4e20)
+#define AFE_GASRC7_NEW_CON9 (0x4e24)
+#define AFE_GASRC7_NEW_CON10 (0x4e28)
+#define AFE_GASRC7_NEW_CON11 (0x4e2c)
+#define AFE_GASRC7_NEW_CON12 (0x4e30)
+#define AFE_GASRC7_NEW_CON13 (0x4e34)
+#define AFE_GASRC7_NEW_CON14 (0x4e38)
+#define AFE_GASRC8_NEW_CON0 (0x4e40)
+#define AFE_GASRC8_NEW_CON1 (0x4e44)
+#define AFE_GASRC8_NEW_CON2 (0x4e48)
+#define AFE_GASRC8_NEW_CON3 (0x4e4c)
+#define AFE_GASRC8_NEW_CON4 (0x4e50)
+#define AFE_GASRC8_NEW_CON5 (0x4e54)
+#define AFE_GASRC8_NEW_CON6 (0x4e58)
+#define AFE_GASRC8_NEW_CON7 (0x4e5c)
+#define AFE_GASRC8_NEW_CON8 (0x4e60)
+#define AFE_GASRC8_NEW_CON9 (0x4e64)
+#define AFE_GASRC8_NEW_CON10 (0x4e68)
+#define AFE_GASRC8_NEW_CON11 (0x4e6c)
+#define AFE_GASRC8_NEW_CON12 (0x4e70)
+#define AFE_GASRC8_NEW_CON13 (0x4e74)
+#define AFE_GASRC8_NEW_CON14 (0x4e78)
+#define AFE_GASRC9_NEW_CON0 (0x4e80)
+#define AFE_GASRC9_NEW_CON1 (0x4e84)
+#define AFE_GASRC9_NEW_CON2 (0x4e88)
+#define AFE_GASRC9_NEW_CON3 (0x4e8c)
+#define AFE_GASRC9_NEW_CON4 (0x4e90)
+#define AFE_GASRC9_NEW_CON5 (0x4e94)
+#define AFE_GASRC9_NEW_CON6 (0x4e98)
+#define AFE_GASRC9_NEW_CON7 (0x4e9c)
+#define AFE_GASRC9_NEW_CON8 (0x4ea0)
+#define AFE_GASRC9_NEW_CON9 (0x4ea4)
+#define AFE_GASRC9_NEW_CON10 (0x4ea8)
+#define AFE_GASRC9_NEW_CON11 (0x4eac)
+#define AFE_GASRC9_NEW_CON12 (0x4eb0)
+#define AFE_GASRC9_NEW_CON13 (0x4eb4)
+#define AFE_GASRC9_NEW_CON14 (0x4eb8)
+#define AFE_GASRC10_NEW_CON0 (0x4ec0)
+#define AFE_GASRC10_NEW_CON1 (0x4ec4)
+#define AFE_GASRC10_NEW_CON2 (0x4ec8)
+#define AFE_GASRC10_NEW_CON3 (0x4ecc)
+#define AFE_GASRC10_NEW_CON4 (0x4ed0)
+#define AFE_GASRC10_NEW_CON5 (0x4ed4)
+#define AFE_GASRC10_NEW_CON6 (0x4ed8)
+#define AFE_GASRC10_NEW_CON7 (0x4edc)
+#define AFE_GASRC10_NEW_CON8 (0x4ee0)
+#define AFE_GASRC10_NEW_CON9 (0x4ee4)
+#define AFE_GASRC10_NEW_CON10 (0x4ee8)
+#define AFE_GASRC10_NEW_CON11 (0x4eec)
+#define AFE_GASRC10_NEW_CON12 (0x4ef0)
+#define AFE_GASRC10_NEW_CON13 (0x4ef4)
+#define AFE_GASRC10_NEW_CON14 (0x4ef8)
+#define AFE_GASRC11_NEW_CON0 (0x4f00)
+#define AFE_GASRC11_NEW_CON1 (0x4f04)
+#define AFE_GASRC11_NEW_CON2 (0x4f08)
+#define AFE_GASRC11_NEW_CON3 (0x4f0c)
+#define AFE_GASRC11_NEW_CON4 (0x4f10)
+#define AFE_GASRC11_NEW_CON5 (0x4f14)
+#define AFE_GASRC11_NEW_CON6 (0x4f18)
+#define AFE_GASRC11_NEW_CON7 (0x4f1c)
+#define AFE_GASRC11_NEW_CON8 (0x4f20)
+#define AFE_GASRC11_NEW_CON9 (0x4f24)
+#define AFE_GASRC11_NEW_CON10 (0x4f28)
+#define AFE_GASRC11_NEW_CON11 (0x4f2c)
+#define AFE_GASRC11_NEW_CON12 (0x4f30)
+#define AFE_GASRC11_NEW_CON13 (0x4f34)
+#define AFE_GASRC11_NEW_CON14 (0x4f38)
+#define AFE_GASRC12_NEW_CON0 (0x4f40)
+#define AFE_GASRC12_NEW_CON1 (0x4f44)
+#define AFE_GASRC12_NEW_CON2 (0x4f48)
+#define AFE_GASRC12_NEW_CON3 (0x4f4c)
+#define AFE_GASRC12_NEW_CON4 (0x4f50)
+#define AFE_GASRC12_NEW_CON5 (0x4f54)
+#define AFE_GASRC12_NEW_CON6 (0x4f58)
+#define AFE_GASRC12_NEW_CON7 (0x4f5c)
+#define AFE_GASRC12_NEW_CON8 (0x4f60)
+#define AFE_GASRC12_NEW_CON9 (0x4f64)
+#define AFE_GASRC12_NEW_CON10 (0x4f68)
+#define AFE_GASRC12_NEW_CON11 (0x4f6c)
+#define AFE_GASRC12_NEW_CON12 (0x4f70)
+#define AFE_GASRC12_NEW_CON13 (0x4f74)
+#define AFE_GASRC12_NEW_CON14 (0x4f78)
+#define AFE_GASRC13_NEW_CON0 (0x4f80)
+#define AFE_GASRC13_NEW_CON1 (0x4f84)
+#define AFE_GASRC13_NEW_CON2 (0x4f88)
+#define AFE_GASRC13_NEW_CON3 (0x4f8c)
+#define AFE_GASRC13_NEW_CON4 (0x4f90)
+#define AFE_GASRC13_NEW_CON5 (0x4f94)
+#define AFE_GASRC13_NEW_CON6 (0x4f98)
+#define AFE_GASRC13_NEW_CON7 (0x4f9c)
+#define AFE_GASRC13_NEW_CON8 (0x4fa0)
+#define AFE_GASRC13_NEW_CON9 (0x4fa4)
+#define AFE_GASRC13_NEW_CON10 (0x4fa8)
+#define AFE_GASRC13_NEW_CON11 (0x4fac)
+#define AFE_GASRC13_NEW_CON12 (0x4fb0)
+#define AFE_GASRC13_NEW_CON13 (0x4fb4)
+#define AFE_GASRC13_NEW_CON14 (0x4fb8)
+#define AFE_GASRC14_NEW_CON0 (0x4fc0)
+#define AFE_GASRC14_NEW_CON1 (0x4fc4)
+#define AFE_GASRC14_NEW_CON2 (0x4fc8)
+#define AFE_GASRC14_NEW_CON3 (0x4fcc)
+#define AFE_GASRC14_NEW_CON4 (0x4fd0)
+#define AFE_GASRC14_NEW_CON5 (0x4fd4)
+#define AFE_GASRC14_NEW_CON6 (0x4fd8)
+#define AFE_GASRC14_NEW_CON7 (0x4fdc)
+#define AFE_GASRC14_NEW_CON8 (0x4fe0)
+#define AFE_GASRC14_NEW_CON9 (0x4fe4)
+#define AFE_GASRC14_NEW_CON10 (0x4fe8)
+#define AFE_GASRC14_NEW_CON11 (0x4fec)
+#define AFE_GASRC14_NEW_CON12 (0x4ff0)
+#define AFE_GASRC14_NEW_CON13 (0x4ff4)
+#define AFE_GASRC14_NEW_CON14 (0x4ff8)
+#define AFE_GASRC15_NEW_CON0 (0x5000)
+#define AFE_GASRC15_NEW_CON1 (0x5004)
+#define AFE_GASRC15_NEW_CON2 (0x5008)
+#define AFE_GASRC15_NEW_CON3 (0x500c)
+#define AFE_GASRC15_NEW_CON4 (0x5010)
+#define AFE_GASRC15_NEW_CON5 (0x5014)
+#define AFE_GASRC15_NEW_CON6 (0x5018)
+#define AFE_GASRC15_NEW_CON7 (0x501c)
+#define AFE_GASRC15_NEW_CON8 (0x5020)
+#define AFE_GASRC15_NEW_CON9 (0x5024)
+#define AFE_GASRC15_NEW_CON10 (0x5028)
+#define AFE_GASRC15_NEW_CON11 (0x502c)
+#define AFE_GASRC15_NEW_CON12 (0x5030)
+#define AFE_GASRC15_NEW_CON13 (0x5034)
+#define AFE_GASRC15_NEW_CON14 (0x5038)
+#define AFE_GASRC16_NEW_CON0 (0x5040)
+#define AFE_GASRC16_NEW_CON1 (0x5044)
+#define AFE_GASRC16_NEW_CON2 (0x5048)
+#define AFE_GASRC16_NEW_CON3 (0x504c)
+#define AFE_GASRC16_NEW_CON4 (0x5050)
+#define AFE_GASRC16_NEW_CON5 (0x5054)
+#define AFE_GASRC16_NEW_CON6 (0x5058)
+#define AFE_GASRC16_NEW_CON7 (0x505c)
+#define AFE_GASRC16_NEW_CON8 (0x5060)
+#define AFE_GASRC16_NEW_CON9 (0x5064)
+#define AFE_GASRC16_NEW_CON10 (0x5068)
+#define AFE_GASRC16_NEW_CON11 (0x506c)
+#define AFE_GASRC16_NEW_CON12 (0x5070)
+#define AFE_GASRC16_NEW_CON13 (0x5074)
+#define AFE_GASRC16_NEW_CON14 (0x5078)
+#define AFE_GASRC17_NEW_CON0 (0x5080)
+#define AFE_GASRC17_NEW_CON1 (0x5084)
+#define AFE_GASRC17_NEW_CON2 (0x5088)
+#define AFE_GASRC17_NEW_CON3 (0x508c)
+#define AFE_GASRC17_NEW_CON4 (0x5090)
+#define AFE_GASRC17_NEW_CON5 (0x5094)
+#define AFE_GASRC17_NEW_CON6 (0x5098)
+#define AFE_GASRC17_NEW_CON7 (0x509c)
+#define AFE_GASRC17_NEW_CON8 (0x50a0)
+#define AFE_GASRC17_NEW_CON9 (0x50a4)
+#define AFE_GASRC17_NEW_CON10 (0x50a8)
+#define AFE_GASRC17_NEW_CON11 (0x50ac)
+#define AFE_GASRC17_NEW_CON12 (0x50b0)
+#define AFE_GASRC17_NEW_CON13 (0x50b4)
+#define AFE_GASRC17_NEW_CON14 (0x50b8)
+#define AFE_GASRC18_NEW_CON0 (0x50c0)
+#define AFE_GASRC18_NEW_CON1 (0x50c4)
+#define AFE_GASRC18_NEW_CON2 (0x50c8)
+#define AFE_GASRC18_NEW_CON3 (0x50cc)
+#define AFE_GASRC18_NEW_CON4 (0x50d0)
+#define AFE_GASRC18_NEW_CON5 (0x50d4)
+#define AFE_GASRC18_NEW_CON6 (0x50d8)
+#define AFE_GASRC18_NEW_CON7 (0x50dc)
+#define AFE_GASRC18_NEW_CON8 (0x50e0)
+#define AFE_GASRC18_NEW_CON9 (0x50e4)
+#define AFE_GASRC18_NEW_CON10 (0x50e8)
+#define AFE_GASRC18_NEW_CON11 (0x50ec)
+#define AFE_GASRC18_NEW_CON12 (0x50f0)
+#define AFE_GASRC18_NEW_CON13 (0x50f4)
+#define AFE_GASRC18_NEW_CON14 (0x50f8)
+#define AFE_GASRC19_NEW_CON0 (0x5100)
+#define AFE_GASRC19_NEW_CON1 (0x5104)
+#define AFE_GASRC19_NEW_CON2 (0x5108)
+#define AFE_GASRC19_NEW_CON3 (0x510c)
+#define AFE_GASRC19_NEW_CON4 (0x5110)
+#define AFE_GASRC19_NEW_CON5 (0x5114)
+#define AFE_GASRC19_NEW_CON6 (0x5118)
+#define AFE_GASRC19_NEW_CON7 (0x511c)
+#define AFE_GASRC19_NEW_CON8 (0x5120)
+#define AFE_GASRC19_NEW_CON9 (0x5124)
+#define AFE_GASRC19_NEW_CON10 (0x5128)
+#define AFE_GASRC19_NEW_CON11 (0x512c)
+#define AFE_GASRC19_NEW_CON12 (0x5130)
+#define AFE_GASRC19_NEW_CON13 (0x5134)
+#define AFE_GASRC19_NEW_CON14 (0x5138)
+
+#define AFE_MAX_REGISTER (AFE_GASRC19_NEW_CON14)
+
+/* ASYS_TOP_CON */
+#define ASYS_TOP_CON_A1SYS_TIMING_ON BIT(0)
+#define ASYS_TOP_CON_A2SYS_TIMING_ON BIT(1)
+#define ASYS_TOP_CON_A3SYS_TIMING_ON BIT(4)
+#define ASYS_TOP_CON_A4SYS_TIMING_ON BIT(5)
+#define ASYS_TOP_CON_26M_TIMING_ON BIT(2)
+
+/* PWR2_TOP_CON0 */
+#define PWR2_TOP_CON_DMIC8_SRC_SEL_MASK GENMASK(31, 29)
+#define PWR2_TOP_CON_DMIC7_SRC_SEL_MASK GENMASK(28, 26)
+#define PWR2_TOP_CON_DMIC6_SRC_SEL_MASK GENMASK(25, 23)
+#define PWR2_TOP_CON_DMIC5_SRC_SEL_MASK GENMASK(22, 20)
+#define PWR2_TOP_CON_DMIC4_SRC_SEL_MASK GENMASK(19, 17)
+#define PWR2_TOP_CON_DMIC3_SRC_SEL_MASK GENMASK(16, 14)
+#define PWR2_TOP_CON_DMIC2_SRC_SEL_MASK GENMASK(13, 11)
+#define PWR2_TOP_CON_DMIC1_SRC_SEL_MASK GENMASK(10, 8)
+#define PWR2_TOP_CON_DMIC8_SRC_SEL_VAL(x) ((x) << 29)
+#define PWR2_TOP_CON_DMIC7_SRC_SEL_VAL(x) ((x) << 26)
+#define PWR2_TOP_CON_DMIC6_SRC_SEL_VAL(x) ((x) << 23)
+#define PWR2_TOP_CON_DMIC5_SRC_SEL_VAL(x) ((x) << 20)
+#define PWR2_TOP_CON_DMIC4_SRC_SEL_VAL(x) ((x) << 17)
+#define PWR2_TOP_CON_DMIC3_SRC_SEL_VAL(x) ((x) << 14)
+#define PWR2_TOP_CON_DMIC2_SRC_SEL_VAL(x) ((x) << 11)
+#define PWR2_TOP_CON_DMIC1_SRC_SEL_VAL(x) ((x) << 8)
+
+/* PWR2_TOP_CON1 */
+#define PWR2_TOP_CON1_DMIC_CKDIV_ON BIT(1)
+
+/* PCM_INTF_CON1 */
+#define PCM_INTF_CON1_SYNC_OUT_INV BIT(23)
+#define PCM_INTF_CON1_BCLK_OUT_INV BIT(22)
+#define PCM_INTF_CON1_CLK_OUT_INV_MASK GENMASK(23, 22)
+#define PCM_INTF_CON1_SYNC_IN_INV BIT(21)
+#define PCM_INTF_CON1_BCLK_IN_INV BIT(20)
+#define PCM_INTF_CON1_CLK_IN_INV_MASK GENMASK(21, 20)
+#define PCM_INTF_CON1_PCM_24BIT (0x1 << 16)
+#define PCM_INTF_CON1_PCM_16BIT (0x0 << 16)
+#define PCM_INTF_CON1_PCM_BIT_MASK BIT(16)
+#define PCM_INTF_CON1_PCM_WLEN_32BCK (0x0 << 14)
+#define PCM_INTF_CON1_PCM_WLEN_64BCK (0x1 << 14)
+#define PCM_INTF_CON1_PCM_WLEN_MASK BIT(14)
+#define PCM_INTF_CON1_SYNC_LENGTH(x) (((x) & 0x1f) << 9)
+#define PCM_INTF_CON1_SYNC_LENGTH_MASK (0x1f << 9)
+#define PCM_INTF_CON1_PCM_SLAVE (0x1 << 5)
+#define PCM_INTF_CON1_PCM_MASTER (0x0 << 5)
+#define PCM_INTF_CON1_PCM_M_S_MASK BIT(5)
+#define PCM_INTF_CON1_PCM_MODE(x) (((x) & 0x3) << 3)
+#define PCM_INTF_CON1_PCM_MODE_MASK (0x3 << 3)
+#define PCM_INTF_CON1_PCM_FMT(x) (((x) & 0x3) << 1)
+#define PCM_INTF_CON1_PCM_FMT_MASK (0x3 << 1)
+#define PCM_INTF_CON1_PCM_EN BIT(0)
+#define PCM_INTF_CON1_PCM_EN_SHIFT 0
+
+/* PCM_INTF_CON2 */
+#define PCM_INTF_CON2_CLK_DOMAIN_SEL(x) (((x) & 0x3) << 23)
+#define PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK (0x3 << 23)
+#define PCM_INTF_CON2_SYNC_FREQ_MODE(x) (((x) & 0x1f) << 12)
+#define PCM_INTF_CON2_SYNC_FREQ_MODE_MASK (0x1f << 12)
+#define PCM_INTF_CON2_PCM_TX2RX_LPBK BIT(8)
+
+/* AFE_MPHONE_MULTIx_CON0 */
+#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP BIT(3)
+#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP_MASK BIT(3)
+#define AFE_MPHONE_MULTI_CON0_24BIT_DATA (0x1 << 1)
+#define AFE_MPHONE_MULTI_CON0_16BIT_DATA (0x0 << 1)
+#define AFE_MPHONE_MULIT_CON0_24BIT_DATA_MASK BIT(1)
+#define AFE_MPHONE_MULTI_CON0_EN BIT(0)
+#define AFE_MPHONE_MULTI_CON0_EN_MASK BIT(0)
+
+/* AFE_MPHONE_MULTIx_CON1 */
+#define AFE_MPHONE_MULTI_CON1_SYNC_ON BIT(24)
+#define AFE_MPHONE_MULTI_CON1_SYNC_ON_MASK BIT(24)
+#define AFE_MPHONE_MULTI_CON1_24BIT_SWAP_BYPASS BIT(22)
+#define AFE_MPHONE_MULTI_CON1_24BIT_SWAP_BYPASS_MASK BIT(22)
+#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE (0x1 << 19)
+#define AFE_MPHONE_MULTI_CON1_COMPACT_MODE (0x0 << 19)
+#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE_MASK BIT(19)
+#define AFE_MPHONE_MULTI_CON1_HBR_MODE BIT(18)
+#define AFE_MPHONE_MULTI_CON1_HBR_MODE_MASK BIT(18)
+#define AFE_MPHONE_MULTI_CON1_LRCK_32_CYCLE (0x2 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_24_CYCLE (0x1 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_16_CYCLE (0x0 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_CYCLE_SEL_MASK GENMASK(17, 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_INV BIT(15)
+#define AFE_MPHONE_MULTI_CON1_LRCK_INV_MASK BIT(15)
+#define AFE_MPHONE_MULTI_CON1_DELAY_DATA BIT(14)
+#define AFE_MPHONE_MULTI_CON1_DELAY_DATA_MASK BIT(14)
+#define AFE_MPHONE_MULTI_CON1_LEFT_ALIGN BIT(13)
+#define AFE_MPHONE_MULTI_CON1_LEFT_ALIGN_MASK BIT(13)
+#define AFE_MPHONE_MULTI_CON1_BIT_NUM(x) ((((x) - 1) & 0x1f) << 8)
+#define AFE_MPHONE_MULTI_CON1_BIT_NUM_MASK GENMASK(12, 8)
+#define AFE_MPHONE_MULTI_CON1_BCK_INV BIT(6)
+#define AFE_MPHONE_MULTI_CON1_BCK_INV_MASK BIT(6)
+#define AFE_MPHONE_MULTI_CON1_CH_NUM(x) ((((x) >> 1) - 1) & 0x3)
+#define AFE_MPHONE_MULTI_CON1_CH_NUM_MASK GENMASK(1, 0)
+
+/* AFE_MPHONE_MULTIx_CON2 */
+#define AFE_MPHONE_MULTI_CON2_SEL_SPDIFIN BIT(19)
+#define AFE_MPHONE_MULTI_CON2_SEL_SPDIFIN_MASK BIT(19)
+
+/* AFE_AUD_PAD_TOP */
+#define RG_RX_PROTOCOL2 BIT(3)
+#define RG_RX_FIFO_ON BIT(0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define MTKAIF_RXIF_CLKINV_ADC BIT(31)
+#define MTKAIF_RXIF_PROTOCOL2 BIT(16)
+#define MTKAIF_TXIF_PROTOCOL2 BIT(4)
+#define MTKAIF_TXIF_8TO5 BIT(2)
+#define MTKAIF_RXIF_8TO5 BIT(1)
+#define MTKAIF_IF_LOOPBACK1 BIT(0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define MTKAIF_RXIF_DELAY_CYCLE(x) ((x) << 12)
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK GENMASK(15, 12)
+#define MTKAIF_RXIF_DELAY_DATA BIT(8)
+#define MTKAIF_RXIF_DELAY_DATA_SHIFT 8
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG */
+#define ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE BIT(23)
+
+/* AFE_DMICx_UL_SRC_CON0 */
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH1(x) (((x) & 0x7) << 27)
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH2(x) (((x) & 0x7) << 24)
+#define AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL BIT(23)
+#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL BIT(22)
+#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL BIT(21)
+#define AFE_DMIC_UL_VOICE_MODE(x) (((x) & 0x7) << 17)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_8K AFE_DMIC_UL_VOICE_MODE(0)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_16K AFE_DMIC_UL_VOICE_MODE(1)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_32K AFE_DMIC_UL_VOICE_MODE(2)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_48K AFE_DMIC_UL_VOICE_MODE(3)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL(x) (((x) & 0x7) << 7)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL BIT(10)
+#define AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL BIT(1)
+#define AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL BIT(0)
+
+/* ETDM_INx_AFIFO_CON */
+#define ETDM_IN_USE_AFIFO BIT(8)
+#define ETDM_IN_AFIFO_CLOCK(x) ((x) << 5)
+#define ETDM_IN_AFIFO_CLOCK_MASK GENMASK(7, 5)
+#define ETDM_IN_AFIFO_MODE(x) ((x) << 0)
+#define ETDM_IN_AFIFO_MODE_MASK GENMASK(4, 0)
+
+/* ETDM_COWORK_CON0 */
+#define ETDM_OUT1_SLAVE_SEL(x) ((x) << 20)
+#define ETDM_OUT1_SLAVE_SEL_MASK GENMASK(23, 20)
+#define ETDM_OUT1_SLAVE_SEL_SHIFT 20
+
+/* ETDM_COWORK_CON1 */
+#define ETDM_IN1_SDATA_SEL(x) ((x) << 20)
+#define ETDM_IN1_SDATA_SEL_MASK GENMASK(23, 20)
+#define ETDM_IN1_SDATA_SEL_SHIFT 20
+#define ETDM_IN1_SDATA0_SEL(x) ((x) << 16)
+#define ETDM_IN1_SDATA0_SEL_MASK GENMASK(19, 16)
+#define ETDM_IN1_SDATA0_SEL_SHIFT 16
+#define ETDM_IN1_SLAVE_SEL(x) ((x) << 8)
+#define ETDM_IN1_SLAVE_SEL_MASK GENMASK(11, 8)
+#define ETDM_IN1_SLAVE_SEL_SHIFT 8
+
+/* ETDM_COWORK_CON2 */
+#define ETDM_IN2_SLAVE_SEL(x) ((x) << 24)
+#define ETDM_IN2_SLAVE_SEL_MASK GENMASK(27, 24)
+#define ETDM_IN2_SLAVE_SEL_SHIFT 24
+#define ETDM_OUT3_SLAVE_SEL(x) ((x) << 20)
+#define ETDM_OUT3_SLAVE_SEL_MASK GENMASK(23, 20)
+#define ETDM_OUT3_SLAVE_SEL_SHIFT 20
+#define ETDM_OUT2_SLAVE_SEL(x) ((x) << 8)
+#define ETDM_OUT2_SLAVE_SEL_MASK GENMASK(11, 8)
+#define ETDM_OUT2_SLAVE_SEL_SHIFT 8
+
+/* ETDM_COWORK_CON3 */
+#define ETDM_IN2_SDATA_SEL(x) ((x) << 4)
+#define ETDM_IN2_SDATA_SEL_MASK GENMASK(7, 4)
+#define ETDM_IN2_SDATA_SEL_SHIFT 4
+#define ETDM_IN2_SDATA0_SEL(x) ((x) << 0)
+#define ETDM_IN2_SDATA0_SEL_MASK GENMASK(3, 0)
+#define ETDM_IN2_SDATA0_SEL_SHIFT 0
+
+/* ETDM_x_CONx */
+#define ETDM_CON0_CH_NUM(x) (((x) - 1) << 23)
+#define ETDM_CON0_CH_NUM_MASK GENMASK(27, 23)
+#define ETDM_CON0_WORD_LEN(x) (((x) - 1) << 16)
+#define ETDM_CON0_WORD_LEN_MASK GENMASK(20, 16)
+#define ETDM_CON0_BIT_LEN(x) (((x) - 1) << 11)
+#define ETDM_CON0_BIT_LEN_MASK GENMASK(15, 11)
+#define ETDM_CON0_FORMAT(x) ((x) << 6)
+#define ETDM_CON0_FORMAT_MASK GENMASK(8, 6)
+#define ETDM_CON0_SLAVE_MODE BIT(5)
+#define ETDM_CON0_EN BIT(0)
+
+#define ETDM_OUT_CON0_RELATCH_DOMAIN(x) ((x) << 28)
+#define ETDM_OUT_CON0_RELATCH_DOMAIN_MASK GENMASK(29, 28)
+
+#define ETDM_CON1_MCLK_OUTPUT BIT(16)
+#define ETDM_IN_CON1_LRCK_AUTO_MODE BIT(31)
+#define ETDM_IN_CON1_LRCK_WIDTH(x) (((x) - 1) << 20)
+#define ETDM_IN_CON1_LRCK_WIDTH_MASK GENMASK(29, 20)
+#define ETDM_OUT_CON1_LRCK_AUTO_MODE BIT(29)
+#define ETDM_OUT_CON1_LRCK_WIDTH(x) (((x) - 1) << 19)
+#define ETDM_OUT_CON1_LRCK_WIDTH_MASK GENMASK(28, 19)
+
+#define ETDM_IN_CON2_MULTI_IP_2CH_MODE BIT(31)
+#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH(x) (((x) - 1) << 15)
+#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK GENMASK(19, 15)
+#define ETDM_IN_CON2_CLOCK(x) ((x) << 10)
+#define ETDM_IN_CON2_CLOCK_MASK GENMASK(12, 10)
+#define ETDM_IN_CON2_CLOCK_SHIFT 10
+#define ETDM_IN_CON2_UPDATE_GAP(x) ((x) << 5)
+#define ETDM_IN_CON2_UPDATE_GAP_MASK GENMASK(9, 5)
+
+#define ETDM_OUT_CON2_LRCK_DELAY_BCK_INV BIT(30)
+#define ETDM_OUT_CON2_LRCK_DELAY_0P5T_EN BIT(29)
+
+#define ETDM_IN_CON3_FS(x) ((x) << 26)
+#define ETDM_IN_CON3_FS_MASK GENMASK(30, 26)
+#define ETDM_IN_CON3_DISABLE_OUT(x) BIT(((x) & 0xf))
+#define ETDM_IN_CON3_DISABLE_OUT_MASK GENMASK(15, 0)
+
+#define ETDM_IN_CON4_MASTER_LRCK_INV BIT(19)
+#define ETDM_IN_CON4_MASTER_BCK_INV BIT(18)
+#define ETDM_IN_CON4_SLAVE_LRCK_INV BIT(17)
+#define ETDM_IN_CON4_SLAVE_BCK_INV BIT(16)
+
+#define ETDM_OUT_CON4_RELATCH_EN(x) ((x) << 24)
+#define ETDM_OUT_CON4_RELATCH_EN_MASK GENMASK(28, 24)
+#define ETDM_OUT_CON4_CLOCK(x) ((x) << 6)
+#define ETDM_OUT_CON4_CLOCK_MASK GENMASK(8, 6)
+#define ETDM_OUT_CON4_CLOCK_SHIFT 6
+#define ETDM_OUT_CON4_FS(x) ((x) << 0)
+#define ETDM_OUT_CON4_FS_MASK GENMASK(4, 0)
+
+#define ETDM_IN_CON5_LR_SWAP(x) BIT(((x) & 0xf) + 16)
+#define ETDM_IN_CON5_LR_SWAP_MASK GENMASK(31, 16)
+#define ETDM_IN_CON5_ENABLE_ODD(x) BIT(((x) & 0xf))
+#define ETDM_IN_CON5_ENABLE_ODD_MASK GENMASK(15, 0)
+
+#define ETDM_OUT_CON5_MASTER_LRCK_INV BIT(10)
+#define ETDM_OUT_CON5_MASTER_BCK_INV BIT(9)
+#define ETDM_OUT_CON5_SLAVE_LRCK_INV BIT(8)
+#define ETDM_OUT_CON5_SLAVE_BCK_INV BIT(7)
+
+/* AFE_DPTX_CON */
+#define AFE_DPTX_CON_CH_EN(x) (((x) & 0xff) << 8)
+#define AFE_DPTX_CON_CH_EN_2CH (AFE_DPTX_CON_CH_EN(GENMASK(1, 0)))
+#define AFE_DPTX_CON_CH_EN_4CH (AFE_DPTX_CON_CH_EN(GENMASK(3, 0)))
+#define AFE_DPTX_CON_CH_EN_6CH (AFE_DPTX_CON_CH_EN(GENMASK(5, 0)))
+#define AFE_DPTX_CON_CH_EN_8CH (AFE_DPTX_CON_CH_EN(GENMASK(7, 0)))
+#define AFE_DPTX_CON_CH_EN_MASK GENMASK(15, 8)
+#define AFE_DPTX_CON_16BIT (0x1 << 2)
+#define AFE_DPTX_CON_24BIT (0x0 << 2)
+#define AFE_DPTX_CON_16BIT_MASK BIT(2)
+#define AFE_DPTX_CON_CH_NUM(x) (((x) & 0x1) << 1)
+#define AFE_DPTX_CON_CH_NUM_2CH (AFE_DPTX_CON_CH_NUM(0))
+#define AFE_DPTX_CON_CH_NUM_8CH (AFE_DPTX_CON_CH_NUM(1))
+#define AFE_DPTX_CON_CH_NUM_MASK (0x1 << 1)
+#define AFE_DPTX_CON_ON BIT(0)
+#define AFE_DPTX_CON_ON_MASK BIT(0)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define ADDA_AFE_ON_SHIFT 0
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL(x) ((x) << 28)
+#define DL_2_INPUT_MODE_CTL_MASK GENMASK(31, 28)
+#define DL_2_CH1_SATURATION_EN_CTL BIT(27)
+#define DL_2_CH2_SATURATION_EN_CTL BIT(26)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE BIT(12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE BIT(11)
+#define DL_2_VOICE_MODE_CTL_PRE BIT(5)
+#define DL_2_GAIN_ON_CTL_PRE_SHIFT 1
+#define DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT 0
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE(x) ((x) << 16)
+#define DL_2_GAIN_CTL_PRE_MASK GENMASK(31, 16)
+#define DL_2_GAIN_CTL_PRE_SHIFT 16
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOPBACK_MODE_CTL_MASK GENMASK(15, 12)
+#define DL_INPUT_FROM_SINEGEN (4 << 12)
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define DL_USE_NEW_2ND_SDM BIT(30)
+#define ATTGAIN_CTL_MASK GENMASK(5, 0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define UL_MODE_3P25M_CH2_CTL BIT(22)
+#define UL_MODE_3P25M_CH1_CTL BIT(21)
+#define UL_VOICE_MODE_CTL(x) ((x) << 17)
+#define UL_VOICE_MODE_CTL_MASK GENMASK(19, 17)
+#define UL_LOOPBACK_MODE_CTL BIT(2)
+#define UL_SDM3_LEVEL_CTL BIT(1)
+#define UL_SRC_ON_TMP_CTL_SHIFT 0
+
+#endif
diff --git a/sound/soc/mediatek/mt8365/Makefile b/sound/soc/mediatek/mt8365/Makefile
new file mode 100644
index 000000000000..b197025e34bb
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# MTK Platform driver
+snd-soc-mt8365-pcm-y := \
+ mt8365-afe-clk.o \
+ mt8365-afe-pcm.o \
+ mt8365-dai-adda.o \
+ mt8365-dai-dmic.o \
+ mt8365-dai-i2s.o \
+ mt8365-dai-pcm.o
+
+obj-$(CONFIG_SND_SOC_MT8365) += snd-soc-mt8365-pcm.o
+
+# Machine driver
+obj-$(CONFIG_SND_SOC_MT8365_MT6357) += mt8365-mt6357.o
diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-clk.c b/sound/soc/mediatek/mt8365/mt8365-afe-clk.c
new file mode 100644
index 000000000000..7078c01ba19b
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-afe-clk.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 AFE clock control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#include "mt8365-afe-clk.h"
+#include "mt8365-afe-common.h"
+#include "mt8365-reg.h"
+#include "../common/mtk-base-afe.h"
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+
+static const char *aud_clks[MT8365_CLK_NUM] = {
+ [MT8365_CLK_TOP_AUD_SEL] = "top_audio_sel",
+ [MT8365_CLK_AUD_I2S0_M] = "audio_i2s0_m",
+ [MT8365_CLK_AUD_I2S1_M] = "audio_i2s1_m",
+ [MT8365_CLK_AUD_I2S2_M] = "audio_i2s2_m",
+ [MT8365_CLK_AUD_I2S3_M] = "audio_i2s3_m",
+ [MT8365_CLK_ENGEN1] = "engen1",
+ [MT8365_CLK_ENGEN2] = "engen2",
+ [MT8365_CLK_AUD1] = "aud1",
+ [MT8365_CLK_AUD2] = "aud2",
+ [MT8365_CLK_I2S0_M_SEL] = "i2s0_m_sel",
+ [MT8365_CLK_I2S1_M_SEL] = "i2s1_m_sel",
+ [MT8365_CLK_I2S2_M_SEL] = "i2s2_m_sel",
+ [MT8365_CLK_I2S3_M_SEL] = "i2s3_m_sel",
+ [MT8365_CLK_CLK26M] = "top_clk26m_clk",
+};
+
+int mt8365_afe_init_audio_clk(struct mtk_base_afe *afe)
+{
+ size_t i;
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clocks[i])) {
+ dev_err(afe->dev, "%s devm_clk_get %s fail\n",
+ __func__, aud_clks[i]);
+ return PTR_ERR(afe_priv->clocks[i]);
+ }
+ }
+ return 0;
+}
+
+void mt8365_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+int mt8365_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_err(afe->dev, "Failed to set rate\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int mt8365_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent)
+{
+ int ret;
+
+ if (clk && parent) {
+ ret = clk_set_parent(clk, parent);
+ if (ret) {
+ dev_err(afe->dev, "Failed to set parent\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static unsigned int get_top_cg_reg(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8365_TOP_CG_AFE:
+ case MT8365_TOP_CG_I2S_IN:
+ case MT8365_TOP_CG_22M:
+ case MT8365_TOP_CG_24M:
+ case MT8365_TOP_CG_INTDIR_CK:
+ case MT8365_TOP_CG_APLL2_TUNER:
+ case MT8365_TOP_CG_APLL_TUNER:
+ case MT8365_TOP_CG_SPDIF:
+ case MT8365_TOP_CG_TDM_OUT:
+ case MT8365_TOP_CG_TDM_IN:
+ case MT8365_TOP_CG_ADC:
+ case MT8365_TOP_CG_DAC:
+ case MT8365_TOP_CG_DAC_PREDIS:
+ case MT8365_TOP_CG_TML:
+ return AUDIO_TOP_CON0;
+ case MT8365_TOP_CG_I2S1_BCLK:
+ case MT8365_TOP_CG_I2S2_BCLK:
+ case MT8365_TOP_CG_I2S3_BCLK:
+ case MT8365_TOP_CG_I2S4_BCLK:
+ case MT8365_TOP_CG_DMIC0_ADC:
+ case MT8365_TOP_CG_DMIC1_ADC:
+ case MT8365_TOP_CG_DMIC2_ADC:
+ case MT8365_TOP_CG_DMIC3_ADC:
+ case MT8365_TOP_CG_CONNSYS_I2S_ASRC:
+ case MT8365_TOP_CG_GENERAL1_ASRC:
+ case MT8365_TOP_CG_GENERAL2_ASRC:
+ case MT8365_TOP_CG_TDM_ASRC:
+ return AUDIO_TOP_CON1;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_mask(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8365_TOP_CG_AFE:
+ return AUD_TCON0_PDN_AFE;
+ case MT8365_TOP_CG_I2S_IN:
+ return AUD_TCON0_PDN_I2S_IN;
+ case MT8365_TOP_CG_22M:
+ return AUD_TCON0_PDN_22M;
+ case MT8365_TOP_CG_24M:
+ return AUD_TCON0_PDN_24M;
+ case MT8365_TOP_CG_INTDIR_CK:
+ return AUD_TCON0_PDN_INTDIR;
+ case MT8365_TOP_CG_APLL2_TUNER:
+ return AUD_TCON0_PDN_APLL2_TUNER;
+ case MT8365_TOP_CG_APLL_TUNER:
+ return AUD_TCON0_PDN_APLL_TUNER;
+ case MT8365_TOP_CG_SPDIF:
+ return AUD_TCON0_PDN_SPDIF;
+ case MT8365_TOP_CG_TDM_OUT:
+ return AUD_TCON0_PDN_TDM_OUT;
+ case MT8365_TOP_CG_TDM_IN:
+ return AUD_TCON0_PDN_TDM_IN;
+ case MT8365_TOP_CG_ADC:
+ return AUD_TCON0_PDN_ADC;
+ case MT8365_TOP_CG_DAC:
+ return AUD_TCON0_PDN_DAC;
+ case MT8365_TOP_CG_DAC_PREDIS:
+ return AUD_TCON0_PDN_DAC_PREDIS;
+ case MT8365_TOP_CG_TML:
+ return AUD_TCON0_PDN_TML;
+ case MT8365_TOP_CG_I2S1_BCLK:
+ return AUD_TCON1_PDN_I2S1_BCLK;
+ case MT8365_TOP_CG_I2S2_BCLK:
+ return AUD_TCON1_PDN_I2S2_BCLK;
+ case MT8365_TOP_CG_I2S3_BCLK:
+ return AUD_TCON1_PDN_I2S3_BCLK;
+ case MT8365_TOP_CG_I2S4_BCLK:
+ return AUD_TCON1_PDN_I2S4_BCLK;
+ case MT8365_TOP_CG_DMIC0_ADC:
+ return AUD_TCON1_PDN_DMIC0_ADC;
+ case MT8365_TOP_CG_DMIC1_ADC:
+ return AUD_TCON1_PDN_DMIC1_ADC;
+ case MT8365_TOP_CG_DMIC2_ADC:
+ return AUD_TCON1_PDN_DMIC2_ADC;
+ case MT8365_TOP_CG_DMIC3_ADC:
+ return AUD_TCON1_PDN_DMIC3_ADC;
+ case MT8365_TOP_CG_CONNSYS_I2S_ASRC:
+ return AUD_TCON1_PDN_CONNSYS_I2S_ASRC;
+ case MT8365_TOP_CG_GENERAL1_ASRC:
+ return AUD_TCON1_PDN_GENERAL1_ASRC;
+ case MT8365_TOP_CG_GENERAL2_ASRC:
+ return AUD_TCON1_PDN_GENERAL2_ASRC;
+ case MT8365_TOP_CG_TDM_ASRC:
+ return AUD_TCON1_PDN_TDM_ASRC;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_on_val(unsigned int cg_type)
+{
+ return 0;
+}
+
+static unsigned int get_top_cg_off_val(unsigned int cg_type)
+{
+ return get_top_cg_mask(cg_type);
+}
+
+int mt8365_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_on_val(cg_type);
+ unsigned long flags;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ afe_priv->top_cg_ref_cnt[cg_type]++;
+ if (afe_priv->top_cg_ref_cnt[cg_type] == 1)
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+
+ return 0;
+}
+
+int mt8365_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_off_val(cg_type);
+ unsigned long flags;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ afe_priv->top_cg_ref_cnt[cg_type]--;
+ if (afe_priv->top_cg_ref_cnt[cg_type] == 0)
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ else if (afe_priv->top_cg_ref_cnt[cg_type] < 0)
+ afe_priv->top_cg_ref_cnt[cg_type] = 0;
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+
+ return 0;
+}
+
+int mt8365_afe_enable_main_clk(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ clk_prepare_enable(afe_priv->clocks[MT8365_CLK_TOP_AUD_SEL]);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_AFE);
+ mt8365_afe_enable_afe_on(afe);
+
+ return 0;
+}
+
+int mt8365_afe_disable_main_clk(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ mt8365_afe_disable_afe_on(afe);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_AFE);
+ mt8365_afe_disable_clk(afe, afe_priv->clocks[MT8365_CLK_TOP_AUD_SEL]);
+
+ return 0;
+}
+
+int mt8365_afe_emi_clk_on(struct mtk_base_afe *afe)
+{
+ return 0;
+}
+
+int mt8365_afe_emi_clk_off(struct mtk_base_afe *afe)
+{
+ return 0;
+}
+
+int mt8365_afe_enable_afe_on(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ afe_priv->afe_on_ref_cnt++;
+ if (afe_priv->afe_on_ref_cnt == 1)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+
+ return 0;
+}
+
+int mt8365_afe_disable_afe_on(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ afe_priv->afe_on_ref_cnt--;
+ if (afe_priv->afe_on_ref_cnt == 0)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
+ else if (afe_priv->afe_on_ref_cnt < 0)
+ afe_priv->afe_on_ref_cnt = 0;
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+
+ return 0;
+}
+
+static int mt8365_afe_hd_engen_enable(struct mtk_base_afe *afe, bool apll1)
+{
+ if (apll1)
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_PLL_EN, AFE_22M_PLL_EN);
+ else
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_PLL_EN, AFE_24M_PLL_EN);
+
+ return 0;
+}
+
+static int mt8365_afe_hd_engen_disable(struct mtk_base_afe *afe, bool apll1)
+{
+ if (apll1)
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_22M_PLL_EN, ~AFE_22M_PLL_EN);
+ else
+ regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
+ AFE_24M_PLL_EN, ~AFE_24M_PLL_EN);
+
+ return 0;
+}
+
+int mt8365_afe_enable_apll_tuner_cfg(struct mtk_base_afe *afe, unsigned int apll)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ mutex_lock(&afe_priv->afe_clk_mutex);
+
+ afe_priv->apll_tuner_ref_cnt[apll]++;
+ if (afe_priv->apll_tuner_ref_cnt[apll] != 1) {
+ mutex_unlock(&afe_priv->afe_clk_mutex);
+ return 0;
+ }
+
+ if (apll == MT8365_AFE_APLL1) {
+ regmap_update_bits(afe->regmap, AFE_APLL_TUNER_CFG,
+ AFE_APLL_TUNER_CFG_MASK, 0x432);
+ regmap_update_bits(afe->regmap, AFE_APLL_TUNER_CFG,
+ AFE_APLL_TUNER_CFG_EN_MASK, 0x1);
+ } else {
+ regmap_update_bits(afe->regmap, AFE_APLL_TUNER_CFG1,
+ AFE_APLL_TUNER_CFG1_MASK, 0x434);
+ regmap_update_bits(afe->regmap, AFE_APLL_TUNER_CFG1,
+ AFE_APLL_TUNER_CFG1_EN_MASK, 0x1);
+ }
+
+ mutex_unlock(&afe_priv->afe_clk_mutex);
+ return 0;
+}
+
+int mt8365_afe_disable_apll_tuner_cfg(struct mtk_base_afe *afe, unsigned int apll)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ mutex_lock(&afe_priv->afe_clk_mutex);
+
+ afe_priv->apll_tuner_ref_cnt[apll]--;
+ if (afe_priv->apll_tuner_ref_cnt[apll] == 0) {
+ if (apll == MT8365_AFE_APLL1)
+ regmap_update_bits(afe->regmap, AFE_APLL_TUNER_CFG,
+ AFE_APLL_TUNER_CFG_EN_MASK, 0x0);
+ else
+ regmap_update_bits(afe->regmap, AFE_APLL_TUNER_CFG1,
+ AFE_APLL_TUNER_CFG1_EN_MASK, 0x0);
+
+ } else if (afe_priv->apll_tuner_ref_cnt[apll] < 0) {
+ afe_priv->apll_tuner_ref_cnt[apll] = 0;
+ }
+
+ mutex_unlock(&afe_priv->afe_clk_mutex);
+ return 0;
+}
+
+int mt8365_afe_enable_apll_associated_cfg(struct mtk_base_afe *afe, unsigned int apll)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ if (apll == MT8365_AFE_APLL1) {
+ if (clk_prepare_enable(afe_priv->clocks[MT8365_CLK_ENGEN1])) {
+ dev_info(afe->dev, "%s Failed to enable ENGEN1 clk\n",
+ __func__);
+ return 0;
+ }
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_22M);
+ mt8365_afe_hd_engen_enable(afe, true);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_APLL_TUNER);
+ mt8365_afe_enable_apll_tuner_cfg(afe, MT8365_AFE_APLL1);
+ } else {
+ if (clk_prepare_enable(afe_priv->clocks[MT8365_CLK_ENGEN2])) {
+ dev_info(afe->dev, "%s Failed to enable ENGEN2 clk\n",
+ __func__);
+ return 0;
+ }
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_24M);
+ mt8365_afe_hd_engen_enable(afe, false);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_APLL2_TUNER);
+ mt8365_afe_enable_apll_tuner_cfg(afe, MT8365_AFE_APLL2);
+ }
+
+ return 0;
+}
+
+int mt8365_afe_disable_apll_associated_cfg(struct mtk_base_afe *afe, unsigned int apll)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ if (apll == MT8365_AFE_APLL1) {
+ mt8365_afe_disable_apll_tuner_cfg(afe, MT8365_AFE_APLL1);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_APLL_TUNER);
+ mt8365_afe_hd_engen_disable(afe, true);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_22M);
+ clk_disable_unprepare(afe_priv->clocks[MT8365_CLK_ENGEN1]);
+ } else {
+ mt8365_afe_disable_apll_tuner_cfg(afe, MT8365_AFE_APLL2);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_APLL2_TUNER);
+ mt8365_afe_hd_engen_disable(afe, false);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_24M);
+ clk_disable_unprepare(afe_priv->clocks[MT8365_CLK_ENGEN2]);
+ }
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-clk.h b/sound/soc/mediatek/mt8365/mt8365-afe-clk.h
new file mode 100644
index 000000000000..a6fa653f2183
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-afe-clk.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * MediaTek 8365 AFE clock control definitions
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#ifndef _MT8365_AFE_UTILS_H_
+#define _MT8365_AFE_UTILS_H_
+
+struct mtk_base_afe;
+struct clk;
+
+int mt8365_afe_init_audio_clk(struct mtk_base_afe *afe);
+void mt8365_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8365_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk, unsigned int rate);
+int mt8365_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk, struct clk *parent);
+int mt8365_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type);
+int mt8365_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type);
+int mt8365_afe_enable_main_clk(struct mtk_base_afe *afe);
+int mt8365_afe_disable_main_clk(struct mtk_base_afe *afe);
+int mt8365_afe_emi_clk_on(struct mtk_base_afe *afe);
+int mt8365_afe_emi_clk_off(struct mtk_base_afe *afe);
+int mt8365_afe_enable_afe_on(struct mtk_base_afe *afe);
+int mt8365_afe_disable_afe_on(struct mtk_base_afe *afe);
+int mt8365_afe_enable_apll_tuner_cfg(struct mtk_base_afe *afe, unsigned int apll);
+int mt8365_afe_disable_apll_tuner_cfg(struct mtk_base_afe *afe, unsigned int apll);
+int mt8365_afe_enable_apll_associated_cfg(struct mtk_base_afe *afe, unsigned int apll);
+int mt8365_afe_disable_apll_associated_cfg(struct mtk_base_afe *afe, unsigned int apll);
+#endif
diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-common.h b/sound/soc/mediatek/mt8365/mt8365-afe-common.h
new file mode 100644
index 000000000000..731406e15ac7
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-afe-common.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * MediaTek 8365 audio driver common definitions
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#ifndef _MT8365_AFE_COMMON_H_
+#define _MT8365_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/asound.h>
+#include "../common/mtk-base-afe.h"
+#include "mt8365-reg.h"
+
+enum {
+ MT8365_AFE_MEMIF_DL1,
+ MT8365_AFE_MEMIF_DL2,
+ MT8365_AFE_MEMIF_TDM_OUT,
+ /*
+ * MT8365_AFE_MEMIF_SPDIF_OUT,
+ */
+ MT8365_AFE_MEMIF_AWB,
+ MT8365_AFE_MEMIF_VUL,
+ MT8365_AFE_MEMIF_VUL2,
+ MT8365_AFE_MEMIF_VUL3,
+ MT8365_AFE_MEMIF_TDM_IN,
+ /*
+ * MT8365_AFE_MEMIF_SPDIF_IN,
+ */
+ MT8365_AFE_MEMIF_NUM,
+ MT8365_AFE_BACKEND_BASE = MT8365_AFE_MEMIF_NUM,
+ MT8365_AFE_IO_TDM_OUT = MT8365_AFE_BACKEND_BASE,
+ MT8365_AFE_IO_TDM_IN,
+ MT8365_AFE_IO_I2S,
+ MT8365_AFE_IO_2ND_I2S,
+ MT8365_AFE_IO_PCM1,
+ MT8365_AFE_IO_VIRTUAL_DL_SRC,
+ MT8365_AFE_IO_VIRTUAL_TDM_OUT_SRC,
+ MT8365_AFE_IO_VIRTUAL_FM,
+ MT8365_AFE_IO_DMIC,
+ MT8365_AFE_IO_INT_ADDA,
+ MT8365_AFE_IO_GASRC1,
+ MT8365_AFE_IO_GASRC2,
+ MT8365_AFE_IO_TDM_ASRC,
+ MT8365_AFE_IO_HW_GAIN1,
+ MT8365_AFE_IO_HW_GAIN2,
+ MT8365_AFE_BACKEND_END,
+ MT8365_AFE_BACKEND_NUM = (MT8365_AFE_BACKEND_END -
+ MT8365_AFE_BACKEND_BASE),
+};
+
+enum {
+ MT8365_AFE_IRQ1,
+ MT8365_AFE_IRQ2,
+ MT8365_AFE_IRQ3,
+ MT8365_AFE_IRQ4,
+ MT8365_AFE_IRQ5,
+ MT8365_AFE_IRQ6,
+ MT8365_AFE_IRQ7,
+ MT8365_AFE_IRQ8,
+ MT8365_AFE_IRQ9,
+ MT8365_AFE_IRQ10,
+ MT8365_AFE_IRQ_NUM,
+};
+
+enum {
+ MT8365_TOP_CG_AFE,
+ MT8365_TOP_CG_I2S_IN,
+ MT8365_TOP_CG_22M,
+ MT8365_TOP_CG_24M,
+ MT8365_TOP_CG_INTDIR_CK,
+ MT8365_TOP_CG_APLL2_TUNER,
+ MT8365_TOP_CG_APLL_TUNER,
+ MT8365_TOP_CG_SPDIF,
+ MT8365_TOP_CG_TDM_OUT,
+ MT8365_TOP_CG_TDM_IN,
+ MT8365_TOP_CG_ADC,
+ MT8365_TOP_CG_DAC,
+ MT8365_TOP_CG_DAC_PREDIS,
+ MT8365_TOP_CG_TML,
+ MT8365_TOP_CG_I2S1_BCLK,
+ MT8365_TOP_CG_I2S2_BCLK,
+ MT8365_TOP_CG_I2S3_BCLK,
+ MT8365_TOP_CG_I2S4_BCLK,
+ MT8365_TOP_CG_DMIC0_ADC,
+ MT8365_TOP_CG_DMIC1_ADC,
+ MT8365_TOP_CG_DMIC2_ADC,
+ MT8365_TOP_CG_DMIC3_ADC,
+ MT8365_TOP_CG_CONNSYS_I2S_ASRC,
+ MT8365_TOP_CG_GENERAL1_ASRC,
+ MT8365_TOP_CG_GENERAL2_ASRC,
+ MT8365_TOP_CG_TDM_ASRC,
+ MT8365_TOP_CG_NUM
+};
+
+enum {
+ MT8365_CLK_TOP_AUD_SEL,
+ MT8365_CLK_AUD_I2S0_M,
+ MT8365_CLK_AUD_I2S1_M,
+ MT8365_CLK_AUD_I2S2_M,
+ MT8365_CLK_AUD_I2S3_M,
+ MT8365_CLK_ENGEN1,
+ MT8365_CLK_ENGEN2,
+ MT8365_CLK_AUD1,
+ MT8365_CLK_AUD2,
+ MT8365_CLK_I2S0_M_SEL,
+ MT8365_CLK_I2S1_M_SEL,
+ MT8365_CLK_I2S2_M_SEL,
+ MT8365_CLK_I2S3_M_SEL,
+ MT8365_CLK_CLK26M,
+ MT8365_CLK_NUM
+};
+
+enum {
+ MT8365_AFE_APLL1 = 0,
+ MT8365_AFE_APLL2,
+ MT8365_AFE_APLL_NUM,
+};
+
+enum {
+ MT8365_AFE_1ST_I2S = 0,
+ MT8365_AFE_2ND_I2S,
+ MT8365_AFE_I2S_SETS,
+};
+
+enum {
+ MT8365_AFE_I2S_SEPARATE_CLOCK = 0,
+ MT8365_AFE_I2S_SHARED_CLOCK,
+};
+
+enum {
+ MT8365_AFE_TDM_OUT_I2S = 0,
+ MT8365_AFE_TDM_OUT_TDM,
+ MT8365_AFE_TDM_OUT_I2S_32BITS,
+};
+
+enum mt8365_afe_tdm_ch_start {
+ AFE_TDM_CH_START_O28_O29 = 0,
+ AFE_TDM_CH_START_O30_O31,
+ AFE_TDM_CH_START_O32_O33,
+ AFE_TDM_CH_START_O34_O35,
+ AFE_TDM_CH_ZERO,
+};
+
+enum {
+ MT8365_PCM_FORMAT_I2S = 0,
+ MT8365_PCM_FORMAT_EIAJ,
+ MT8365_PCM_FORMAT_PCMA,
+ MT8365_PCM_FORMAT_PCMB,
+};
+
+enum {
+ MT8365_FS_8K = 0,
+ MT8365_FS_11D025K,
+ MT8365_FS_12K,
+ MT8365_FS_384K,
+ MT8365_FS_16K,
+ MT8365_FS_22D05K,
+ MT8365_FS_24K,
+ MT8365_FS_130K,
+ MT8365_FS_32K,
+ MT8365_FS_44D1K,
+ MT8365_FS_48K,
+ MT8365_FS_88D2K,
+ MT8365_FS_96K,
+ MT8365_FS_176D4K,
+ MT8365_FS_192K,
+};
+
+enum {
+ FS_8000HZ = 0, /* 0000b */
+ FS_11025HZ = 1, /* 0001b */
+ FS_12000HZ = 2, /* 0010b */
+ FS_384000HZ = 3, /* 0011b */
+ FS_16000HZ = 4, /* 0100b */
+ FS_22050HZ = 5, /* 0101b */
+ FS_24000HZ = 6, /* 0110b */
+ FS_130000HZ = 7, /* 0111b */
+ FS_32000HZ = 8, /* 1000b */
+ FS_44100HZ = 9, /* 1001b */
+ FS_48000HZ = 10, /* 1010b */
+ FS_88200HZ = 11, /* 1011b */
+ FS_96000HZ = 12, /* 1100b */
+ FS_176400HZ = 13, /* 1101b */
+ FS_192000HZ = 14, /* 1110b */
+ FS_260000HZ = 15, /* 1111b */
+};
+
+enum {
+ MT8365_AFE_DEBUGFS_AFE,
+ MT8365_AFE_DEBUGFS_MEMIF,
+ MT8365_AFE_DEBUGFS_IRQ,
+ MT8365_AFE_DEBUGFS_CONN,
+ MT8365_AFE_DEBUGFS_DBG,
+ MT8365_AFE_DEBUGFS_NUM,
+};
+
+enum {
+ MT8365_AFE_IRQ_DIR_MCU = 0,
+ MT8365_AFE_IRQ_DIR_DSP,
+ MT8365_AFE_IRQ_DIR_BOTH,
+};
+
+/* MCLK */
+enum {
+ MT8365_I2S0_MCK = 0,
+ MT8365_I2S3_MCK,
+ MT8365_MCK_NUM,
+};
+
+struct mt8365_fe_dai_data {
+ bool use_sram;
+ unsigned int sram_phy_addr;
+ void __iomem *sram_vir_addr;
+ unsigned int sram_size;
+};
+
+struct mt8365_be_dai_data {
+ bool prepared[SNDRV_PCM_STREAM_LAST + 1];
+ unsigned int fmt_mode;
+};
+
+#define MT8365_CLK_26M 26000000
+#define MT8365_CLK_24M 24000000
+#define MT8365_CLK_22M 22000000
+#define MT8365_CM_UPDATA_CNT_SET 8
+
+enum mt8365_cm_num {
+ MT8365_CM1 = 0,
+ MT8365_CM2,
+ MT8365_CM_NUM,
+};
+
+enum mt8365_cm2_mux_in {
+ MT8365_FROM_GASRC1 = 1,
+ MT8365_FROM_GASRC2,
+ MT8365_FROM_TDM_ASRC,
+ MT8365_CM_MUX_NUM,
+};
+
+enum cm2_mux_conn_in {
+ GENERAL2_ASRC_OUT_LCH = 0,
+ GENERAL2_ASRC_OUT_RCH = 1,
+ TDM_IN_CH0 = 2,
+ TDM_IN_CH1 = 3,
+ TDM_IN_CH2 = 4,
+ TDM_IN_CH3 = 5,
+ TDM_IN_CH4 = 6,
+ TDM_IN_CH5 = 7,
+ TDM_IN_CH6 = 8,
+ TDM_IN_CH7 = 9,
+ GENERAL1_ASRC_OUT_LCH = 10,
+ GENERAL1_ASRC_OUT_RCH = 11,
+ TDM_OUT_ASRC_CH0 = 12,
+ TDM_OUT_ASRC_CH1 = 13,
+ TDM_OUT_ASRC_CH2 = 14,
+ TDM_OUT_ASRC_CH3 = 15,
+ TDM_OUT_ASRC_CH4 = 16,
+ TDM_OUT_ASRC_CH5 = 17,
+ TDM_OUT_ASRC_CH6 = 18,
+ TDM_OUT_ASRC_CH7 = 19
+};
+
+struct mt8365_cm_ctrl_reg {
+ unsigned int con0;
+ unsigned int con1;
+ unsigned int con2;
+ unsigned int con3;
+ unsigned int con4;
+};
+
+struct mt8365_control_data {
+ bool bypass_cm1;
+ bool bypass_cm2;
+ unsigned int loopback_type;
+};
+
+enum dmic_input_mode {
+ DMIC_MODE_3P25M = 0,
+ DMIC_MODE_1P625M,
+ DMIC_MODE_812P5K,
+ DMIC_MODE_406P25K,
+};
+
+enum iir_mode {
+ IIR_MODE0 = 0,
+ IIR_MODE1,
+ IIR_MODE2,
+ IIR_MODE3,
+ IIR_MODE4,
+ IIR_MODE5,
+};
+
+enum {
+ MT8365_GASRC1 = 0,
+ MT8365_GASRC2,
+ MT8365_GASRC_NUM,
+ MT8365_TDM_ASRC1 = MT8365_GASRC_NUM,
+ MT8365_TDM_ASRC2,
+ MT8365_TDM_ASRC3,
+ MT8365_TDM_ASRC4,
+ MT8365_TDM_ASRC_NUM,
+};
+
+struct mt8365_gasrc_ctrl_reg {
+ unsigned int con0;
+ unsigned int con2;
+ unsigned int con3;
+ unsigned int con4;
+ unsigned int con5;
+ unsigned int con6;
+ unsigned int con9;
+ unsigned int con10;
+ unsigned int con12;
+ unsigned int con13;
+};
+
+struct mt8365_gasrc_data {
+ bool duplex;
+ bool tx_mode;
+ bool cali_on;
+ bool tdm_asrc_out_cm2;
+ bool iir_on;
+};
+
+struct mt8365_afe_private {
+ struct clk *clocks[MT8365_CLK_NUM];
+ struct regmap *topckgen;
+ struct mt8365_fe_dai_data fe_data[MT8365_AFE_MEMIF_NUM];
+ struct mt8365_be_dai_data be_data[MT8365_AFE_BACKEND_NUM];
+ struct mt8365_control_data ctrl_data;
+ struct mt8365_gasrc_data gasrc_data[MT8365_TDM_ASRC_NUM];
+ int afe_on_ref_cnt;
+ int top_cg_ref_cnt[MT8365_TOP_CG_NUM];
+ void __iomem *afe_sram_vir_addr;
+ unsigned int afe_sram_phy_addr;
+ unsigned int afe_sram_size;
+ /* locks */
+ spinlock_t afe_ctrl_lock;
+ struct mutex afe_clk_mutex; /* Protect & sync APLL TUNER registers access*/
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_dentry[MT8365_AFE_DEBUGFS_NUM];
+#endif
+ int apll_tuner_ref_cnt[MT8365_AFE_APLL_NUM];
+ unsigned int tdm_out_mode;
+ unsigned int cm2_mux_input;
+
+ /* dai */
+ bool dai_on[MT8365_AFE_BACKEND_END];
+ void *dai_priv[MT8365_AFE_BACKEND_END];
+};
+
+static inline u32 rx_frequency_palette(unsigned int fs)
+{
+ /* *
+ * A = (26M / fs) * 64
+ * B = 8125 / A
+ * return = DEC2HEX(B * 2^23)
+ */
+ switch (fs) {
+ case FS_8000HZ: return 0x050000;
+ case FS_11025HZ: return 0x06E400;
+ case FS_12000HZ: return 0x078000;
+ case FS_16000HZ: return 0x0A0000;
+ case FS_22050HZ: return 0x0DC800;
+ case FS_24000HZ: return 0x0F0000;
+ case FS_32000HZ: return 0x140000;
+ case FS_44100HZ: return 0x1B9000;
+ case FS_48000HZ: return 0x1E0000;
+ case FS_88200HZ: return 0x372000;
+ case FS_96000HZ: return 0x3C0000;
+ case FS_176400HZ: return 0x6E4000;
+ case FS_192000HZ: return 0x780000;
+ default: return 0x0;
+ }
+}
+
+static inline u32 AutoRstThHi(unsigned int fs)
+{
+ switch (fs) {
+ case FS_8000HZ: return 0x36000;
+ case FS_11025HZ: return 0x27000;
+ case FS_12000HZ: return 0x24000;
+ case FS_16000HZ: return 0x1B000;
+ case FS_22050HZ: return 0x14000;
+ case FS_24000HZ: return 0x12000;
+ case FS_32000HZ: return 0x0D800;
+ case FS_44100HZ: return 0x09D00;
+ case FS_48000HZ: return 0x08E00;
+ case FS_88200HZ: return 0x04E00;
+ case FS_96000HZ: return 0x04800;
+ case FS_176400HZ: return 0x02700;
+ case FS_192000HZ: return 0x02400;
+ default: return 0x0;
+ }
+}
+
+static inline u32 AutoRstThLo(unsigned int fs)
+{
+ switch (fs) {
+ case FS_8000HZ: return 0x30000;
+ case FS_11025HZ: return 0x23000;
+ case FS_12000HZ: return 0x20000;
+ case FS_16000HZ: return 0x18000;
+ case FS_22050HZ: return 0x11000;
+ case FS_24000HZ: return 0x0FE00;
+ case FS_32000HZ: return 0x0BE00;
+ case FS_44100HZ: return 0x08A00;
+ case FS_48000HZ: return 0x07F00;
+ case FS_88200HZ: return 0x04500;
+ case FS_96000HZ: return 0x04000;
+ case FS_176400HZ: return 0x02300;
+ case FS_192000HZ: return 0x02000;
+ default: return 0x0;
+ }
+}
+
+bool mt8365_afe_rate_supported(unsigned int rate, unsigned int id);
+bool mt8365_afe_channel_supported(unsigned int channel, unsigned int id);
+
+int mt8365_dai_i2s_register(struct mtk_base_afe *afe);
+int mt8365_dai_set_priv(struct mtk_base_afe *afe,
+ int id,
+ int priv_size,
+ const void *priv_data);
+
+int mt8365_afe_fs_timing(unsigned int rate);
+
+void mt8365_afe_set_i2s_out_enable(struct mtk_base_afe *afe, bool enable);
+int mt8365_afe_set_i2s_out(struct mtk_base_afe *afe, unsigned int rate, int bit_width);
+
+int mt8365_dai_adda_register(struct mtk_base_afe *afe);
+int mt8365_dai_enable_adda_on(struct mtk_base_afe *afe);
+int mt8365_dai_disable_adda_on(struct mtk_base_afe *afe);
+
+int mt8365_dai_dmic_register(struct mtk_base_afe *afe);
+
+int mt8365_dai_pcm_register(struct mtk_base_afe *afe);
+
+int mt8365_dai_tdm_register(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c
new file mode 100644
index 000000000000..d01793394f22
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c
@@ -0,0 +1,2271 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 ALSA SoC AFE platform driver
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "mt8365-afe-common.h"
+#include "mt8365-afe-clk.h"
+#include "mt8365-reg.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+static unsigned int mCM2Input;
+
+static const unsigned int mt8365_afe_backup_list[] = {
+ AUDIO_TOP_CON0,
+ AFE_CONN0,
+ AFE_CONN1,
+ AFE_CONN3,
+ AFE_CONN4,
+ AFE_CONN5,
+ AFE_CONN6,
+ AFE_CONN7,
+ AFE_CONN8,
+ AFE_CONN9,
+ AFE_CONN10,
+ AFE_CONN11,
+ AFE_CONN12,
+ AFE_CONN13,
+ AFE_CONN14,
+ AFE_CONN15,
+ AFE_CONN16,
+ AFE_CONN17,
+ AFE_CONN18,
+ AFE_CONN19,
+ AFE_CONN20,
+ AFE_CONN21,
+ AFE_CONN26,
+ AFE_CONN27,
+ AFE_CONN28,
+ AFE_CONN29,
+ AFE_CONN30,
+ AFE_CONN31,
+ AFE_CONN32,
+ AFE_CONN33,
+ AFE_CONN34,
+ AFE_CONN35,
+ AFE_CONN36,
+ AFE_CONN_24BIT,
+ AFE_CONN_24BIT_1,
+ AFE_DAC_CON0,
+ AFE_DAC_CON1,
+ AFE_DL1_BASE,
+ AFE_DL1_END,
+ AFE_DL2_BASE,
+ AFE_DL2_END,
+ AFE_VUL_BASE,
+ AFE_VUL_END,
+ AFE_AWB_BASE,
+ AFE_AWB_END,
+ AFE_VUL3_BASE,
+ AFE_VUL3_END,
+ AFE_HDMI_OUT_BASE,
+ AFE_HDMI_OUT_END,
+ AFE_HDMI_IN_2CH_BASE,
+ AFE_HDMI_IN_2CH_END,
+ AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_DL_SRC2_CON0,
+ AFE_ADDA_DL_SRC2_CON1,
+ AFE_I2S_CON,
+ AFE_I2S_CON1,
+ AFE_I2S_CON2,
+ AFE_I2S_CON3,
+ AFE_ADDA_UL_SRC_CON0,
+ AFE_AUD_PAD_TOP,
+ AFE_HD_ENGEN_ENABLE,
+};
+
+static const struct snd_pcm_hardware mt8365_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 512,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .fifo_size = 0,
+};
+
+struct mt8365_afe_rate {
+ unsigned int rate;
+ unsigned int reg_val;
+};
+
+static const struct mt8365_afe_rate mt8365_afe_fs_rates[] = {
+ { .rate = 8000, .reg_val = MT8365_FS_8K },
+ { .rate = 11025, .reg_val = MT8365_FS_11D025K },
+ { .rate = 12000, .reg_val = MT8365_FS_12K },
+ { .rate = 16000, .reg_val = MT8365_FS_16K },
+ { .rate = 22050, .reg_val = MT8365_FS_22D05K },
+ { .rate = 24000, .reg_val = MT8365_FS_24K },
+ { .rate = 32000, .reg_val = MT8365_FS_32K },
+ { .rate = 44100, .reg_val = MT8365_FS_44D1K },
+ { .rate = 48000, .reg_val = MT8365_FS_48K },
+ { .rate = 88200, .reg_val = MT8365_FS_88D2K },
+ { .rate = 96000, .reg_val = MT8365_FS_96K },
+ { .rate = 176400, .reg_val = MT8365_FS_176D4K },
+ { .rate = 192000, .reg_val = MT8365_FS_192K },
+};
+
+int mt8365_afe_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8365_afe_fs_rates); i++)
+ if (mt8365_afe_fs_rates[i].rate == rate)
+ return mt8365_afe_fs_rates[i].reg_val;
+
+ return -EINVAL;
+}
+
+bool mt8365_afe_rate_supported(unsigned int rate, unsigned int id)
+{
+ switch (id) {
+ case MT8365_AFE_IO_TDM_IN:
+ if (rate >= 8000 && rate <= 192000)
+ return true;
+ break;
+ case MT8365_AFE_IO_DMIC:
+ if (rate >= 8000 && rate <= 48000)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool mt8365_afe_channel_supported(unsigned int channel, unsigned int id)
+{
+ switch (id) {
+ case MT8365_AFE_IO_TDM_IN:
+ if (channel >= 1 && channel <= 8)
+ return true;
+ break;
+ case MT8365_AFE_IO_DMIC:
+ if (channel >= 1 && channel <= 8)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool mt8365_afe_clk_group_44k(int sample_rate)
+{
+ if (sample_rate == 11025 ||
+ sample_rate == 22050 ||
+ sample_rate == 44100 ||
+ sample_rate == 88200 ||
+ sample_rate == 176400)
+ return true;
+ else
+ return false;
+}
+
+static bool mt8365_afe_clk_group_48k(int sample_rate)
+{
+ return (!mt8365_afe_clk_group_44k(sample_rate));
+}
+
+int mt8365_dai_set_priv(struct mtk_base_afe *afe, int id,
+ int priv_size, const void *priv_data)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ void *temp_data;
+
+ temp_data = devm_kzalloc(afe->dev, priv_size, GFP_KERNEL);
+ if (!temp_data)
+ return -ENOMEM;
+
+ if (priv_data)
+ memcpy(temp_data, priv_data, priv_size);
+
+ afe_priv->dai_priv[id] = temp_data;
+
+ return 0;
+}
+
+static int mt8365_afe_irq_direction_enable(struct mtk_base_afe *afe,
+ int irq_id, int direction)
+{
+ struct mtk_base_afe_irq *irq;
+
+ if (irq_id >= MT8365_AFE_IRQ_NUM)
+ return -1;
+
+ irq = &afe->irqs[irq_id];
+
+ if (direction == MT8365_AFE_IRQ_DIR_MCU) {
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_DSP_EN,
+ (1 << irq->irq_data->irq_clr_shift),
+ 0);
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN,
+ (1 << irq->irq_data->irq_clr_shift),
+ (1 << irq->irq_data->irq_clr_shift));
+ } else if (direction == MT8365_AFE_IRQ_DIR_DSP) {
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_DSP_EN,
+ (1 << irq->irq_data->irq_clr_shift),
+ (1 << irq->irq_data->irq_clr_shift));
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN,
+ (1 << irq->irq_data->irq_clr_shift),
+ 0);
+ } else {
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_DSP_EN,
+ (1 << irq->irq_data->irq_clr_shift),
+ (1 << irq->irq_data->irq_clr_shift));
+ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN,
+ (1 << irq->irq_data->irq_clr_shift),
+ (1 << irq->irq_data->irq_clr_shift));
+ }
+ return 0;
+}
+
+static int mt8365_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ return mt8365_afe_fs_timing(rate);
+}
+
+static int mt8365_irq_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ return mt8365_memif_fs(substream, rate);
+}
+
+static const struct mt8365_cm_ctrl_reg cm_ctrl_reg[MT8365_CM_NUM] = {
+ [MT8365_CM1] = {
+ .con0 = AFE_CM1_CON0,
+ .con1 = AFE_CM1_CON1,
+ .con2 = AFE_CM1_CON2,
+ .con3 = AFE_CM1_CON3,
+ .con4 = AFE_CM1_CON4,
+ },
+ [MT8365_CM2] = {
+ .con0 = AFE_CM2_CON0,
+ .con1 = AFE_CM2_CON1,
+ .con2 = AFE_CM2_CON2,
+ .con3 = AFE_CM2_CON3,
+ .con4 = AFE_CM2_CON4,
+ }
+};
+
+static int mt8365_afe_cm2_mux_conn(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ unsigned int input = afe_priv->cm2_mux_input;
+
+ /* TDM_IN interconnect to CM2 */
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+ CM2_AFE_CM2_CONN_CFG1_MASK,
+ CM2_AFE_CM2_CONN_CFG1(TDM_IN_CH0));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+ CM2_AFE_CM2_CONN_CFG2_MASK,
+ CM2_AFE_CM2_CONN_CFG2(TDM_IN_CH1));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+ CM2_AFE_CM2_CONN_CFG3_MASK,
+ CM2_AFE_CM2_CONN_CFG3(TDM_IN_CH2));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+ CM2_AFE_CM2_CONN_CFG4_MASK,
+ CM2_AFE_CM2_CONN_CFG4(TDM_IN_CH3));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+ CM2_AFE_CM2_CONN_CFG5_MASK,
+ CM2_AFE_CM2_CONN_CFG5(TDM_IN_CH4));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+ CM2_AFE_CM2_CONN_CFG6_MASK,
+ CM2_AFE_CM2_CONN_CFG6(TDM_IN_CH5));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG7_MASK,
+ CM2_AFE_CM2_CONN_CFG7(TDM_IN_CH6));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG8_MASK,
+ CM2_AFE_CM2_CONN_CFG8(TDM_IN_CH7));
+
+ /* ref data interconnect to CM2 */
+ if (input == MT8365_FROM_GASRC1) {
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG9_MASK,
+ CM2_AFE_CM2_CONN_CFG9(GENERAL1_ASRC_OUT_LCH));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG10_MASK,
+ CM2_AFE_CM2_CONN_CFG10(GENERAL1_ASRC_OUT_RCH));
+ } else if (input == MT8365_FROM_GASRC2) {
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG9_MASK,
+ CM2_AFE_CM2_CONN_CFG9(GENERAL2_ASRC_OUT_LCH));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG10_MASK,
+ CM2_AFE_CM2_CONN_CFG10(GENERAL2_ASRC_OUT_RCH));
+ } else if (input == MT8365_FROM_TDM_ASRC) {
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG9_MASK,
+ CM2_AFE_CM2_CONN_CFG9(TDM_OUT_ASRC_CH0));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG10_MASK,
+ CM2_AFE_CM2_CONN_CFG10(TDM_OUT_ASRC_CH1));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG11_MASK,
+ CM2_AFE_CM2_CONN_CFG11(TDM_OUT_ASRC_CH2));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+ CM2_AFE_CM2_CONN_CFG12_MASK,
+ CM2_AFE_CM2_CONN_CFG12(TDM_OUT_ASRC_CH3));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+ CM2_AFE_CM2_CONN_CFG13_MASK,
+ CM2_AFE_CM2_CONN_CFG13(TDM_OUT_ASRC_CH4));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+ CM2_AFE_CM2_CONN_CFG14_MASK,
+ CM2_AFE_CM2_CONN_CFG14(TDM_OUT_ASRC_CH5));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+ CM2_AFE_CM2_CONN_CFG15_MASK,
+ CM2_AFE_CM2_CONN_CFG15(TDM_OUT_ASRC_CH6));
+ regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+ CM2_AFE_CM2_CONN_CFG16_MASK,
+ CM2_AFE_CM2_CONN_CFG16(TDM_OUT_ASRC_CH7));
+ } else {
+ dev_err(afe->dev, "%s wrong CM2 input %d\n", __func__, input);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mt8365_afe_get_cm_update_cnt(struct mtk_base_afe *afe,
+ enum mt8365_cm_num cmNum,
+ unsigned int rate, unsigned int channel)
+{
+ unsigned int total_cnt, div_cnt, ch_pair, best_cnt;
+ unsigned int ch_update_cnt[MT8365_CM_UPDATA_CNT_SET];
+ int i;
+
+ /* calculate cm update cnt
+ * total_cnt = clk / fs, clk is 26m or 24m or 22m
+ * div_cnt = total_cnt / ch_pair, max ch 16ch ,2ch is a set
+ * best_cnt < div_cnt ,we set best_cnt = div_cnt -10
+ * ch01 = best_cnt, ch23 = 2* ch01_up_cnt
+ * ch45 = 3* ch01_up_cnt ...ch1415 = 8* ch01_up_cnt
+ */
+
+ if (cmNum == MT8365_CM1) {
+ total_cnt = MT8365_CLK_26M / rate;
+ } else if (cmNum == MT8365_CM2) {
+ if (mt8365_afe_clk_group_48k(rate))
+ total_cnt = MT8365_CLK_24M / rate;
+ else
+ total_cnt = MT8365_CLK_22M / rate;
+ } else {
+ return -1;
+ }
+
+ if (channel % 2)
+ ch_pair = (channel / 2) + 1;
+ else
+ ch_pair = channel / 2;
+
+ div_cnt = total_cnt / ch_pair;
+ best_cnt = div_cnt - 10;
+
+ if (best_cnt <= 0)
+ return -1;
+
+ for (i = 0; i < ch_pair; i++)
+ ch_update_cnt[i] = (i + 1) * best_cnt;
+
+ switch (channel) {
+ case 16:
+ fallthrough;
+ case 15:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con4,
+ CM_AFE_CM_UPDATE_CNT2_MASK,
+ CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[7]));
+ fallthrough;
+ case 14:
+ fallthrough;
+ case 13:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con4,
+ CM_AFE_CM_UPDATE_CNT1_MASK,
+ CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[6]));
+ fallthrough;
+ case 12:
+ fallthrough;
+ case 11:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con3,
+ CM_AFE_CM_UPDATE_CNT2_MASK,
+ CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[5]));
+ fallthrough;
+ case 10:
+ fallthrough;
+ case 9:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con3,
+ CM_AFE_CM_UPDATE_CNT1_MASK,
+ CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[4]));
+ fallthrough;
+ case 8:
+ fallthrough;
+ case 7:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con2,
+ CM_AFE_CM_UPDATE_CNT2_MASK,
+ CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[3]));
+ fallthrough;
+ case 6:
+ fallthrough;
+ case 5:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con2,
+ CM_AFE_CM_UPDATE_CNT1_MASK,
+ CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[2]));
+ fallthrough;
+ case 4:
+ fallthrough;
+ case 3:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con1,
+ CM_AFE_CM_UPDATE_CNT2_MASK,
+ CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[1]));
+ fallthrough;
+ case 2:
+ fallthrough;
+ case 1:
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con1,
+ CM_AFE_CM_UPDATE_CNT1_MASK,
+ CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[0]));
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mt8365_afe_configure_cm(struct mtk_base_afe *afe,
+ enum mt8365_cm_num cmNum,
+ unsigned int channels,
+ unsigned int rate)
+{
+ unsigned int val, mask;
+ unsigned int fs = mt8365_afe_fs_timing(rate);
+
+ val = FIELD_PREP(CM_AFE_CM_CH_NUM_MASK, (channels - 1)) |
+ FIELD_PREP(CM_AFE_CM_START_DATA_MASK, 0);
+
+ mask = CM_AFE_CM_CH_NUM_MASK |
+ CM_AFE_CM_START_DATA_MASK;
+
+ if (cmNum == MT8365_CM1) {
+ val |= FIELD_PREP(CM_AFE_CM1_IN_MODE_MASK, fs);
+
+ mask |= CM_AFE_CM1_VUL_SEL |
+ CM_AFE_CM1_IN_MODE_MASK;
+ } else if (cmNum == MT8365_CM2) {
+ if (mt8365_afe_clk_group_48k(rate))
+ val |= FIELD_PREP(CM_AFE_CM2_CLK_SEL, 0);
+ else
+ val |= FIELD_PREP(CM_AFE_CM2_CLK_SEL, 1);
+
+ val |= FIELD_PREP(CM_AFE_CM2_TDM_SEL, 1);
+
+ mask |= CM_AFE_CM2_TDM_SEL |
+ CM_AFE_CM1_IN_MODE_MASK |
+ CM_AFE_CM2_CLK_SEL;
+
+ mt8365_afe_cm2_mux_conn(afe);
+ } else {
+ return -1;
+ }
+
+ regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con0, mask, val);
+
+ mt8365_afe_get_cm_update_cnt(afe, cmNum, rate, channels);
+
+ return 0;
+}
+
+static int mt8365_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int memif_num = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ int ret;
+
+ memif->substream = substream;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+
+ snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+ mt8365_afe_enable_main_clk(afe);
+ return ret;
+}
+
+static void mt8365_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int memif_num = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+
+ memif->substream = NULL;
+
+ mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_control_data *ctrl_data = &afe_priv->ctrl_data;
+ int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[dai_id];
+ struct mt8365_fe_dai_data *fe_data = &afe_priv->fe_data[dai_id];
+ size_t request_size = params_buffer_bytes(params);
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ unsigned int base_end_offset = 8;
+ int ret, fs;
+
+ dev_info(afe->dev, "%s %s period = %d rate = %d channels = %d\n",
+ __func__, memif->data->name, params_period_size(params),
+ rate, channels);
+
+ if (dai_id == MT8365_AFE_MEMIF_VUL2) {
+ if (!ctrl_data->bypass_cm1)
+ /* configure cm1 */
+ mt8365_afe_configure_cm(afe, MT8365_CM1,
+ channels, rate);
+ else
+ regmap_update_bits(afe->regmap, AFE_CM1_CON0,
+ CM_AFE_CM1_VUL_SEL,
+ CM_AFE_CM1_VUL_SEL);
+ } else if (dai_id == MT8365_AFE_MEMIF_TDM_IN) {
+ if (!ctrl_data->bypass_cm2)
+ /* configure cm2 */
+ mt8365_afe_configure_cm(afe, MT8365_CM2,
+ channels, rate);
+ else
+ regmap_update_bits(afe->regmap, AFE_CM2_CON0,
+ CM_AFE_CM2_TDM_SEL,
+ ~CM_AFE_CM2_TDM_SEL);
+
+ base_end_offset = 4;
+ }
+
+ if (request_size > fe_data->sram_size) {
+ ret = snd_pcm_lib_malloc_pages(substream, request_size);
+ if (ret < 0) {
+ dev_err(afe->dev,
+ "%s %s malloc pages %zu bytes failed %d\n",
+ __func__, memif->data->name, request_size, ret);
+ return ret;
+ }
+
+ fe_data->use_sram = false;
+
+ mt8365_afe_emi_clk_on(afe);
+ } else {
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->area = (unsigned char *)fe_data->sram_vir_addr;
+ dma_buf->addr = fe_data->sram_phy_addr;
+ dma_buf->bytes = request_size;
+ snd_pcm_set_runtime_buffer(substream, dma_buf);
+
+ fe_data->use_sram = true;
+ }
+
+ memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+ memif->buffer_size = substream->runtime->dma_bytes;
+
+ /* start */
+ regmap_write(afe->regmap, memif->data->reg_ofs_base,
+ memif->phys_buf_addr);
+ /* end */
+ regmap_write(afe->regmap,
+ memif->data->reg_ofs_base + base_end_offset,
+ memif->phys_buf_addr + memif->buffer_size - 1);
+
+ /* set channel */
+ if (memif->data->mono_shift >= 0) {
+ unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+ if (memif->data->mono_reg < 0)
+ dev_info(afe->dev, "%s mono_reg is NULL\n", __func__);
+ else
+ regmap_update_bits(afe->regmap, memif->data->mono_reg,
+ 1 << memif->data->mono_shift,
+ mono << memif->data->mono_shift);
+ }
+
+ /* set rate */
+ if (memif->data->fs_shift < 0)
+ return 0;
+
+ fs = afe->memif_fs(substream, params_rate(params));
+
+ if (fs < 0)
+ return -EINVAL;
+
+ if (memif->data->fs_reg < 0)
+ dev_info(afe->dev, "%s fs_reg is NULL\n", __func__);
+ else
+ regmap_update_bits(afe->regmap, memif->data->fs_reg,
+ memif->data->fs_maskbit << memif->data->fs_shift,
+ fs << memif->data->fs_shift);
+
+ return 0;
+}
+
+static int mt8365_afe_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mt8365_fe_dai_data *fe_data = &afe_priv->fe_data[dai_id];
+ int ret = 0;
+
+ if (fe_data->use_sram) {
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ } else {
+ ret = snd_pcm_lib_free_pages(substream);
+
+ mt8365_afe_emi_clk_off(afe);
+ }
+
+ return ret;
+}
+
+static int mt8365_afe_fe_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[dai_id];
+
+ /* set format */
+ if (memif->data->hd_reg >= 0) {
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(afe->regmap, memif->data->hd_reg,
+ 3 << memif->data->hd_shift,
+ 0 << memif->data->hd_shift);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(afe->regmap, memif->data->hd_reg,
+ 3 << memif->data->hd_shift,
+ 3 << memif->data->hd_shift);
+
+ if (dai_id == MT8365_AFE_MEMIF_TDM_IN) {
+ regmap_update_bits(afe->regmap,
+ memif->data->hd_reg,
+ 3 << memif->data->hd_shift,
+ 1 << memif->data->hd_shift);
+ regmap_update_bits(afe->regmap,
+ memif->data->hd_reg,
+ 1 << memif->data->hd_align_mshift,
+ 1 << memif->data->hd_align_mshift);
+ }
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(afe->regmap, memif->data->hd_reg,
+ 3 << memif->data->hd_shift,
+ 1 << memif->data->hd_shift);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ mt8365_afe_irq_direction_enable(afe, memif->irq_usage,
+ MT8365_AFE_IRQ_DIR_MCU);
+
+ return 0;
+}
+
+static int mt8365_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mt8365_control_data *ctrl_data = &afe_priv->ctrl_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* enable channel merge */
+ if (dai_id == MT8365_AFE_MEMIF_VUL2 &&
+ !ctrl_data->bypass_cm1) {
+ regmap_update_bits(afe->regmap, AFE_CM1_CON0,
+ CM_AFE_CM_ON, CM_AFE_CM_ON);
+ } else if (dai_id == MT8365_AFE_MEMIF_TDM_IN &&
+ !ctrl_data->bypass_cm2) {
+ regmap_update_bits(afe->regmap, AFE_CM2_CON0,
+ CM_AFE_CM_ON, CM_AFE_CM_ON);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable channel merge */
+ if (dai_id == MT8365_AFE_MEMIF_VUL2 &&
+ !ctrl_data->bypass_cm1) {
+ regmap_update_bits(afe->regmap, AFE_CM1_CON0,
+ CM_AFE_CM_ON, ~CM_AFE_CM_ON);
+ } else if (dai_id == MT8365_AFE_MEMIF_TDM_IN &&
+ !ctrl_data->bypass_cm2) {
+ regmap_update_bits(afe->regmap, AFE_CM2_CON0,
+ CM_AFE_CM_ON, ~CM_AFE_CM_ON);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return mtk_afe_fe_trigger(substream, cmd, dai);
+}
+
+static int mt8365_afe_hw_gain1_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ mt8365_afe_enable_main_clk(afe);
+ return 0;
+}
+
+static void mt8365_afe_hw_gain1_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_be_dai_data *be =
+ &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+
+ if (be->prepared[substream->stream]) {
+ regmap_update_bits(afe->regmap, AFE_GAIN1_CON0,
+ AFE_GAIN1_CON0_EN_MASK, 0);
+ be->prepared[substream->stream] = false;
+ }
+ mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_afe_hw_gain1_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_be_dai_data *be =
+ &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+
+ int fs;
+ unsigned int val1 = 0, val2 = 0;
+
+ if (be->prepared[substream->stream]) {
+ dev_info(afe->dev, "%s prepared already\n", __func__);
+ return 0;
+ }
+
+ fs = mt8365_afe_fs_timing(substream->runtime->rate);
+ regmap_update_bits(afe->regmap, AFE_GAIN1_CON0,
+ AFE_GAIN1_CON0_MODE_MASK, (unsigned int)fs << 4);
+
+ regmap_read(afe->regmap, AFE_GAIN1_CON1, &val1);
+ regmap_read(afe->regmap, AFE_GAIN1_CUR, &val2);
+ if ((val1 & AFE_GAIN1_CON1_MASK) != (val2 & AFE_GAIN1_CUR_MASK))
+ regmap_update_bits(afe->regmap, AFE_GAIN1_CUR,
+ AFE_GAIN1_CUR_MASK, val1);
+
+ regmap_update_bits(afe->regmap, AFE_GAIN1_CON0,
+ AFE_GAIN1_CON0_EN_MASK, 1);
+ be->prepared[substream->stream] = true;
+
+ return 0;
+}
+
+static const struct snd_pcm_hardware mt8365_hostless_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .period_bytes_min = 256,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 8 * 48 * 1024,
+ .fifo_size = 0,
+};
+
+/* dai ops */
+static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &mt8365_hostless_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+ return ret;
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt8365_afe_fe_dai_ops = {
+ .startup = mt8365_afe_fe_startup,
+ .shutdown = mt8365_afe_fe_shutdown,
+ .hw_params = mt8365_afe_fe_hw_params,
+ .hw_free = mt8365_afe_fe_hw_free,
+ .prepare = mt8365_afe_fe_prepare,
+ .trigger = mt8365_afe_fe_trigger,
+};
+
+static const struct snd_soc_dai_ops mt8365_dai_hostless_ops = {
+ .startup = mtk_dai_hostless_startup,
+};
+
+static const struct snd_soc_dai_ops mt8365_afe_hw_gain1_ops = {
+ .startup = mt8365_afe_hw_gain1_startup,
+ .shutdown = mt8365_afe_hw_gain1_shutdown,
+ .prepare = mt8365_afe_hw_gain1_prepare,
+};
+
+static struct snd_soc_dai_driver mt8365_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = MT8365_AFE_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "DL2",
+ .id = MT8365_AFE_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "TDM_OUT",
+ .id = MT8365_AFE_MEMIF_TDM_OUT,
+ .playback = {
+ .stream_name = "TDM_OUT",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "AWB",
+ .id = MT8365_AFE_MEMIF_AWB,
+ .capture = {
+ .stream_name = "AWB",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "VUL",
+ .id = MT8365_AFE_MEMIF_VUL,
+ .capture = {
+ .stream_name = "VUL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "VUL2",
+ .id = MT8365_AFE_MEMIF_VUL2,
+ .capture = {
+ .stream_name = "VUL2",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "VUL3",
+ .id = MT8365_AFE_MEMIF_VUL3,
+ .capture = {
+ .stream_name = "VUL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "TDM_IN",
+ .id = MT8365_AFE_MEMIF_TDM_IN,
+ .capture = {
+ .stream_name = "TDM_IN",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_fe_dai_ops,
+ }, {
+ .name = "Hostless FM DAI",
+ .id = MT8365_AFE_IO_VIRTUAL_FM,
+ .playback = {
+ .stream_name = "Hostless FM DL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Hostless FM UL",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_dai_hostless_ops,
+ }, {
+ .name = "HW_GAIN1",
+ .id = MT8365_AFE_IO_HW_GAIN1,
+ .playback = {
+ .stream_name = "HW Gain 1 In",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "HW Gain 1 Out",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_hw_gain1_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o00_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN0, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN0, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o01_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN1, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o03_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN3, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN3, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN3, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I10 Switch", AFE_CONN3, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o04_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN4, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN4, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN4, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I11 Switch", AFE_CONN4, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o05_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN5, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN5, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN5, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN5, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I09 Switch", AFE_CONN5, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN5, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN5, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN5, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN5, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN5, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I10L Switch", AFE_CONN5, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o06_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN6, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN6, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN6, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN6, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I22 Switch", AFE_CONN6, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN6, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN6, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN6, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN6, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN6, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I11L Switch", AFE_CONN6, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o07_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN7, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN7, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o08_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN8, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN8, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o09_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN9, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN9, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I09 Switch", AFE_CONN9, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN9, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN9, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN9, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN9, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN10, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN10, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I22 Switch", AFE_CONN10, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN10, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN10, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN10, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN10, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o11_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN11, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN11, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I09 Switch", AFE_CONN11, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN11, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN11, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN11, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN11, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o12_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN12, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN12, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I22 Switch", AFE_CONN12, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN12, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN12, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN12, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN12, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o13_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN13, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o14_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN14, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o15_mix[] = {
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o16_mix[] = {
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o17_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN17, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o18_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN18, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN18, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN18, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o19_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN19, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN19, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN19, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN19, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN19, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o20_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN20, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN20, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o21_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN21, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN21, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o22_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN22, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN22, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o23_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN23, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN23, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o24_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN24, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN24, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN24, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN24, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o25_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I27 Switch", AFE_CONN25, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN25, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN25, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o26_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I28 Switch", AFE_CONN26, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN26, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN26, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o27_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN27, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN27, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o28_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN28, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN28, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o29_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN29, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN29, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o30_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN30, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN30, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o31_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I29 Switch", AFE_CONN31, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o32_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I30 Switch", AFE_CONN32, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o33_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I31 Switch", AFE_CONN33, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o34_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I32 Switch", AFE_CONN34_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o35_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I33 Switch", AFE_CONN35_1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o36_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I34 Switch", AFE_CONN36_1, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain1_in_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN13,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain1_in_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN14,
+ 1, 1, 0),
+};
+
+static int mt8365_afe_cm2_io_input_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = mCM2Input;
+
+ return 0;
+}
+
+static int mt8365_afe_cm2_io_input_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(comp);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ mCM2Input = ucontrol->value.enumerated.item[0];
+
+ afe_priv->cm2_mux_input = mCM2Input;
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return ret;
+}
+
+static const char * const fmhwgain_text[] = {
+ "OPEN", "FM_HW_GAIN_IO"
+};
+
+static const char * const ain_text[] = {
+ "INT ADC", "EXT ADC",
+};
+
+static const char * const vul2_in_input_text[] = {
+ "VUL2_IN_FROM_O17O18", "VUL2_IN_FROM_CM1",
+};
+
+static const char * const mt8365_afe_cm2_mux_text[] = {
+ "OPEN", "FROM_GASRC1_OUT", "FROM_GASRC2_OUT", "FROM_TDM_ASRC_OUT",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(fmhwgain_enum, fmhwgain_text);
+static SOC_ENUM_SINGLE_DECL(ain_enum, AFE_ADDA_TOP_CON0, 0, ain_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(vul2_in_input_enum, vul2_in_input_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(mt8365_afe_cm2_mux_input_enum,
+ mt8365_afe_cm2_mux_text);
+
+static const struct snd_kcontrol_new fmhwgain_mux =
+ SOC_DAPM_ENUM("FM HW Gain Source", fmhwgain_enum);
+
+static const struct snd_kcontrol_new ain_mux =
+ SOC_DAPM_ENUM("AIN Source", ain_enum);
+
+static const struct snd_kcontrol_new vul2_in_input_mux =
+ SOC_DAPM_ENUM("VUL2 Input", vul2_in_input_enum);
+
+static const struct snd_kcontrol_new mt8365_afe_cm2_mux_input_mux =
+ SOC_DAPM_ENUM_EXT("CM2_MUX Source", mt8365_afe_cm2_mux_input_enum,
+ mt8365_afe_cm2_io_input_mux_get,
+ mt8365_afe_cm2_io_input_mux_put);
+
+static const struct snd_soc_dapm_widget mt8365_memif_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I07", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I08", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I05L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I06L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I07L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I08L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I09", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I10", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I11", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I10L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I11L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I20", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I21", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I22", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I23", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I24", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I25", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I27", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I28", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I29", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I30", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I31", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I32", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I33", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I34", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o00_mix, ARRAY_SIZE(mt8365_afe_o00_mix)),
+ SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o01_mix, ARRAY_SIZE(mt8365_afe_o01_mix)),
+ SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o03_mix, ARRAY_SIZE(mt8365_afe_o03_mix)),
+ SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o04_mix, ARRAY_SIZE(mt8365_afe_o04_mix)),
+ SND_SOC_DAPM_MIXER("O05", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o05_mix, ARRAY_SIZE(mt8365_afe_o05_mix)),
+ SND_SOC_DAPM_MIXER("O06", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o06_mix, ARRAY_SIZE(mt8365_afe_o06_mix)),
+ SND_SOC_DAPM_MIXER("O07", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o07_mix, ARRAY_SIZE(mt8365_afe_o07_mix)),
+ SND_SOC_DAPM_MIXER("O08", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o08_mix, ARRAY_SIZE(mt8365_afe_o08_mix)),
+ SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o09_mix, ARRAY_SIZE(mt8365_afe_o09_mix)),
+ SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o10_mix, ARRAY_SIZE(mt8365_afe_o10_mix)),
+ SND_SOC_DAPM_MIXER("O11", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o11_mix, ARRAY_SIZE(mt8365_afe_o11_mix)),
+ SND_SOC_DAPM_MIXER("O12", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o12_mix, ARRAY_SIZE(mt8365_afe_o12_mix)),
+ SND_SOC_DAPM_MIXER("O13", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o13_mix, ARRAY_SIZE(mt8365_afe_o13_mix)),
+ SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o14_mix, ARRAY_SIZE(mt8365_afe_o14_mix)),
+ SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o15_mix, ARRAY_SIZE(mt8365_afe_o15_mix)),
+ SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o16_mix, ARRAY_SIZE(mt8365_afe_o16_mix)),
+ SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o17_mix, ARRAY_SIZE(mt8365_afe_o17_mix)),
+ SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o18_mix, ARRAY_SIZE(mt8365_afe_o18_mix)),
+ SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o19_mix, ARRAY_SIZE(mt8365_afe_o19_mix)),
+ SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o20_mix, ARRAY_SIZE(mt8365_afe_o20_mix)),
+ SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o21_mix, ARRAY_SIZE(mt8365_afe_o21_mix)),
+ SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o22_mix, ARRAY_SIZE(mt8365_afe_o22_mix)),
+ SND_SOC_DAPM_MIXER("O23", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o23_mix, ARRAY_SIZE(mt8365_afe_o23_mix)),
+ SND_SOC_DAPM_MIXER("O24", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o24_mix, ARRAY_SIZE(mt8365_afe_o24_mix)),
+ SND_SOC_DAPM_MIXER("O25", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o25_mix, ARRAY_SIZE(mt8365_afe_o25_mix)),
+ SND_SOC_DAPM_MIXER("O26", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o26_mix, ARRAY_SIZE(mt8365_afe_o26_mix)),
+ SND_SOC_DAPM_MIXER("O27", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o27_mix, ARRAY_SIZE(mt8365_afe_o27_mix)),
+ SND_SOC_DAPM_MIXER("O28", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o28_mix, ARRAY_SIZE(mt8365_afe_o28_mix)),
+ SND_SOC_DAPM_MIXER("O29", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o29_mix, ARRAY_SIZE(mt8365_afe_o29_mix)),
+ SND_SOC_DAPM_MIXER("O30", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o30_mix, ARRAY_SIZE(mt8365_afe_o30_mix)),
+ SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o31_mix, ARRAY_SIZE(mt8365_afe_o31_mix)),
+ SND_SOC_DAPM_MIXER("O32", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o32_mix, ARRAY_SIZE(mt8365_afe_o32_mix)),
+ SND_SOC_DAPM_MIXER("O33", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o33_mix, ARRAY_SIZE(mt8365_afe_o33_mix)),
+ SND_SOC_DAPM_MIXER("O34", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o34_mix, ARRAY_SIZE(mt8365_afe_o34_mix)),
+ SND_SOC_DAPM_MIXER("O35", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o35_mix, ARRAY_SIZE(mt8365_afe_o35_mix)),
+ SND_SOC_DAPM_MIXER("O36", SND_SOC_NOPM, 0, 0,
+ mt8365_afe_o36_mix, ARRAY_SIZE(mt8365_afe_o36_mix)),
+ SND_SOC_DAPM_MIXER("CM2_Mux IO", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("CM1_IO", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O17O18", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_hw_gain1_in_ch1_mix,
+ ARRAY_SIZE(mtk_hw_gain1_in_ch1_mix)),
+ SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_hw_gain1_in_ch2_mix,
+ ARRAY_SIZE(mtk_hw_gain1_in_ch2_mix)),
+
+ SND_SOC_DAPM_INPUT("DL Source"),
+
+ SND_SOC_DAPM_MUX("CM2_Mux_IO Input Mux", SND_SOC_NOPM, 0, 0,
+ &mt8365_afe_cm2_mux_input_mux),
+
+ SND_SOC_DAPM_MUX("AIN Mux", SND_SOC_NOPM, 0, 0, &ain_mux),
+ SND_SOC_DAPM_MUX("VUL2 Input Mux", SND_SOC_NOPM, 0, 0,
+ &vul2_in_input_mux),
+
+ SND_SOC_DAPM_MUX("FM HW Gain Mux", SND_SOC_NOPM, 0, 0, &fmhwgain_mux),
+
+ SND_SOC_DAPM_INPUT("HW Gain 1 Out Endpoint"),
+ SND_SOC_DAPM_OUTPUT("HW Gain 1 In Endpoint"),
+};
+
+static const struct snd_soc_dapm_route mt8365_memif_routes[] = {
+ /* downlink */
+ {"I00", NULL, "2ND I2S Capture"},
+ {"I01", NULL, "2ND I2S Capture"},
+ {"I05", NULL, "DL1"},
+ {"I06", NULL, "DL1"},
+ {"I07", NULL, "DL2"},
+ {"I08", NULL, "DL2"},
+
+ {"O03", "I05 Switch", "I05"},
+ {"O04", "I06 Switch", "I06"},
+ {"O00", "I05 Switch", "I05"},
+ {"O01", "I06 Switch", "I06"},
+ {"O07", "I05 Switch", "I05"},
+ {"O08", "I06 Switch", "I06"},
+ {"O27", "I05 Switch", "I05"},
+ {"O28", "I06 Switch", "I06"},
+ {"O29", "I05 Switch", "I05"},
+ {"O30", "I06 Switch", "I06"},
+
+ {"O03", "I07 Switch", "I07"},
+ {"O04", "I08 Switch", "I08"},
+ {"O00", "I07 Switch", "I07"},
+ {"O01", "I08 Switch", "I08"},
+ {"O07", "I07 Switch", "I07"},
+ {"O08", "I08 Switch", "I08"},
+
+ /* uplink */
+ {"AWB", NULL, "O05"},
+ {"AWB", NULL, "O06"},
+ {"VUL", NULL, "O09"},
+ {"VUL", NULL, "O10"},
+ {"VUL3", NULL, "O11"},
+ {"VUL3", NULL, "O12"},
+
+ {"AIN Mux", "EXT ADC", "I2S Capture"},
+ {"I03", NULL, "AIN Mux"},
+ {"I04", NULL, "AIN Mux"},
+
+ {"HW_GAIN1_IN_CH1", "CONNSYS_I2S_CH1", "Hostless FM DL"},
+ {"HW_GAIN1_IN_CH2", "CONNSYS_I2S_CH2", "Hostless FM DL"},
+
+ {"HW Gain 1 In Endpoint", NULL, "HW Gain 1 In"},
+ {"HW Gain 1 Out", NULL, "HW Gain 1 Out Endpoint"},
+ {"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH1"},
+ {"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH2"},
+
+ {"FM HW Gain Mux", "FM_HW_GAIN_IO", "HW Gain 1 Out"},
+ {"Hostless FM UL", NULL, "FM HW Gain Mux"},
+ {"Hostless FM UL", NULL, "FM 2ND I2S Mux"},
+
+ {"O05", "I05 Switch", "I05L"},
+ {"O06", "I06 Switch", "I06L"},
+ {"O05", "I07 Switch", "I07L"},
+ {"O06", "I08 Switch", "I08L"},
+
+ {"O05", "I03 Switch", "I03"},
+ {"O06", "I04 Switch", "I04"},
+ {"O05", "I00 Switch", "I00"},
+ {"O06", "I01 Switch", "I01"},
+ {"O05", "I09 Switch", "I09"},
+ {"O06", "I22 Switch", "I22"},
+ {"O05", "I14 Switch", "I14"},
+ {"O06", "I15 Switch", "I15"},
+ {"O05", "I16 Switch", "I16"},
+ {"O06", "I17 Switch", "I17"},
+ {"O05", "I18 Switch", "I18"},
+ {"O06", "I19 Switch", "I19"},
+ {"O05", "I20 Switch", "I20"},
+ {"O06", "I21 Switch", "I21"},
+ {"O05", "I23 Switch", "I23"},
+ {"O06", "I24 Switch", "I24"},
+
+ {"O09", "I03 Switch", "I03"},
+ {"O10", "I04 Switch", "I04"},
+ {"O09", "I00 Switch", "I00"},
+ {"O10", "I01 Switch", "I01"},
+ {"O09", "I09 Switch", "I09"},
+ {"O10", "I22 Switch", "I22"},
+ {"O09", "I14 Switch", "I14"},
+ {"O10", "I15 Switch", "I15"},
+ {"O09", "I16 Switch", "I16"},
+ {"O10", "I17 Switch", "I17"},
+ {"O09", "I18 Switch", "I18"},
+ {"O10", "I19 Switch", "I19"},
+ {"O09", "I20 Switch", "I20"},
+ {"O10", "I21 Switch", "I21"},
+
+ {"O11", "I03 Switch", "I03"},
+ {"O12", "I04 Switch", "I04"},
+ {"O11", "I00 Switch", "I00"},
+ {"O12", "I01 Switch", "I01"},
+ {"O11", "I09 Switch", "I09"},
+ {"O12", "I22 Switch", "I22"},
+ {"O11", "I14 Switch", "I14"},
+ {"O12", "I15 Switch", "I15"},
+ {"O11", "I16 Switch", "I16"},
+ {"O12", "I17 Switch", "I17"},
+ {"O11", "I18 Switch", "I18"},
+ {"O12", "I19 Switch", "I19"},
+ {"O11", "I20 Switch", "I20"},
+ {"O12", "I21 Switch", "I21"},
+
+ /* CM2_Mux*/
+ {"CM2_Mux IO", NULL, "CM2_Mux_IO Input Mux"},
+
+ /* VUL2 */
+ {"VUL2", NULL, "VUL2 Input Mux"},
+ {"VUL2 Input Mux", "VUL2_IN_FROM_O17O18", "O17O18"},
+ {"VUL2 Input Mux", "VUL2_IN_FROM_CM1", "CM1_IO"},
+
+ {"O17O18", NULL, "O17"},
+ {"O17O18", NULL, "O18"},
+ {"CM1_IO", NULL, "O17"},
+ {"CM1_IO", NULL, "O18"},
+ {"CM1_IO", NULL, "O19"},
+ {"CM1_IO", NULL, "O20"},
+ {"CM1_IO", NULL, "O21"},
+ {"CM1_IO", NULL, "O22"},
+ {"CM1_IO", NULL, "O23"},
+ {"CM1_IO", NULL, "O24"},
+ {"CM1_IO", NULL, "O25"},
+ {"CM1_IO", NULL, "O26"},
+ {"CM1_IO", NULL, "O31"},
+ {"CM1_IO", NULL, "O32"},
+ {"CM1_IO", NULL, "O33"},
+ {"CM1_IO", NULL, "O34"},
+ {"CM1_IO", NULL, "O35"},
+ {"CM1_IO", NULL, "O36"},
+
+ {"O17", "I14 Switch", "I14"},
+ {"O18", "I15 Switch", "I15"},
+ {"O19", "I16 Switch", "I16"},
+ {"O20", "I17 Switch", "I17"},
+ {"O21", "I18 Switch", "I18"},
+ {"O22", "I19 Switch", "I19"},
+ {"O23", "I20 Switch", "I20"},
+ {"O24", "I21 Switch", "I21"},
+ {"O25", "I23 Switch", "I23"},
+ {"O26", "I24 Switch", "I24"},
+ {"O25", "I25 Switch", "I25"},
+ {"O26", "I26 Switch", "I26"},
+
+ {"O17", "I03 Switch", "I03"},
+ {"O18", "I04 Switch", "I04"},
+ {"O18", "I23 Switch", "I23"},
+ {"O18", "I25 Switch", "I25"},
+ {"O19", "I04 Switch", "I04"},
+ {"O19", "I23 Switch", "I23"},
+ {"O19", "I24 Switch", "I24"},
+ {"O19", "I25 Switch", "I25"},
+ {"O19", "I26 Switch", "I26"},
+ {"O20", "I24 Switch", "I24"},
+ {"O20", "I26 Switch", "I26"},
+ {"O21", "I23 Switch", "I23"},
+ {"O21", "I25 Switch", "I25"},
+ {"O22", "I24 Switch", "I24"},
+ {"O22", "I26 Switch", "I26"},
+
+ {"O23", "I23 Switch", "I23"},
+ {"O23", "I25 Switch", "I25"},
+ {"O24", "I24 Switch", "I24"},
+ {"O24", "I26 Switch", "I26"},
+ {"O24", "I23 Switch", "I23"},
+ {"O24", "I25 Switch", "I25"},
+ {"O13", "I00 Switch", "I00"},
+ {"O14", "I01 Switch", "I01"},
+ {"O03", "I10 Switch", "I10"},
+ {"O04", "I11 Switch", "I11"},
+};
+
+static const struct mtk_base_memif_data memif_data[MT8365_AFE_MEMIF_NUM] = {
+ {
+ .name = "DL1",
+ .id = MT8365_AFE_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 21,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 16,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "DL2",
+ .id = MT8365_AFE_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 4,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 22,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 18,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 2,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "TDM OUT",
+ .id = MT8365_AFE_MEMIF_TDM_OUT,
+ .reg_ofs_base = AFE_HDMI_OUT_BASE,
+ .reg_ofs_cur = AFE_HDMI_OUT_CUR,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 28,
+ .enable_reg = AFE_HDMI_OUT_CON0,
+ .enable_shift = 0,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "AWB",
+ .id = MT8365_AFE_MEMIF_AWB,
+ .reg_ofs_base = AFE_AWB_BASE,
+ .reg_ofs_cur = AFE_AWB_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 12,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 24,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 20,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 6,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 17,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "VUL",
+ .id = MT8365_AFE_MEMIF_VUL,
+ .reg_ofs_base = AFE_VUL_BASE,
+ .reg_ofs_cur = AFE_VUL_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 16,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON1,
+ .mono_shift = 27,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 22,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 3,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 20,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "VUL2",
+ .id = MT8365_AFE_MEMIF_VUL2,
+ .reg_ofs_base = AFE_VUL_D2_BASE,
+ .reg_ofs_cur = AFE_VUL_D2_CUR,
+ .fs_reg = AFE_DAC_CON0,
+ .fs_shift = 20,
+ .fs_maskbit = 0xf,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 14,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 9,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 21,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "VUL3",
+ .id = MT8365_AFE_MEMIF_VUL3,
+ .reg_ofs_base = AFE_VUL3_BASE,
+ .reg_ofs_cur = AFE_VUL3_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 8,
+ .fs_maskbit = 0xf,
+ .mono_reg = AFE_DAC_CON0,
+ .mono_shift = 13,
+ .hd_reg = AFE_MEMIF_PBUF2_SIZE,
+ .hd_shift = 10,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 12,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 27,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ }, {
+ .name = "TDM IN",
+ .id = MT8365_AFE_MEMIF_TDM_IN,
+ .reg_ofs_base = AFE_HDMI_IN_2CH_BASE,
+ .reg_ofs_cur = AFE_HDMI_IN_2CH_CUR,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = AFE_HDMI_IN_2CH_CON0,
+ .mono_shift = 1,
+ .hd_reg = AFE_MEMIF_PBUF2_SIZE,
+ .hd_shift = 8,
+ .hd_align_mshift = 5,
+ .enable_reg = AFE_HDMI_IN_2CH_CON0,
+ .enable_shift = 0,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 28,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8365_AFE_IRQ_NUM] = {
+ {
+ .id = MT8365_AFE_IRQ1,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 0,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 4,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 0,
+ }, {
+ .id = MT8365_AFE_IRQ2,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT2,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 1,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 8,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 1,
+ }, {
+ .id = MT8365_AFE_IRQ3,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT3,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 2,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 16,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 2,
+ }, {
+ .id = MT8365_AFE_IRQ4,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT4,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 3,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 20,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 3,
+ }, {
+ .id = MT8365_AFE_IRQ5,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT5,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON2,
+ .irq_en_shift = 3,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0x0,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 4,
+ }, {
+ .id = MT8365_AFE_IRQ6,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x0,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 13,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0x0,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 5,
+ }, {
+ .id = MT8365_AFE_IRQ7,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT7,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 14,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 6,
+ }, {
+ .id = MT8365_AFE_IRQ8,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT8,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON,
+ .irq_en_shift = 15,
+ .irq_fs_reg = AFE_IRQ_MCU_CON,
+ .irq_fs_shift = 28,
+ .irq_fs_maskbit = 0xf,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 7,
+ }, {
+ .id = MT8365_AFE_IRQ9,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x0,
+ .irq_en_reg = AFE_IRQ_MCU_CON2,
+ .irq_en_shift = 2,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0x0,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 8,
+ }, {
+ .id = MT8365_AFE_IRQ10,
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT10,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0x3ffff,
+ .irq_en_reg = AFE_IRQ_MCU_CON2,
+ .irq_en_shift = 4,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0x0,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 9,
+ },
+};
+
+static int memif_specified_irqs[MT8365_AFE_MEMIF_NUM] = {
+ [MT8365_AFE_MEMIF_DL1] = MT8365_AFE_IRQ1,
+ [MT8365_AFE_MEMIF_DL2] = MT8365_AFE_IRQ2,
+ [MT8365_AFE_MEMIF_TDM_OUT] = MT8365_AFE_IRQ5,
+ [MT8365_AFE_MEMIF_AWB] = MT8365_AFE_IRQ3,
+ [MT8365_AFE_MEMIF_VUL] = MT8365_AFE_IRQ4,
+ [MT8365_AFE_MEMIF_VUL2] = MT8365_AFE_IRQ7,
+ [MT8365_AFE_MEMIF_VUL3] = MT8365_AFE_IRQ8,
+ [MT8365_AFE_MEMIF_TDM_IN] = MT8365_AFE_IRQ10,
+};
+
+static const struct regmap_config mt8365_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = MAX_REGISTER,
+ .cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt8365_afe_irq_handler(int irq, void *dev_id)
+{
+ struct mtk_base_afe *afe = dev_id;
+ unsigned int reg_value;
+ unsigned int mcu_irq_mask;
+ int i, ret;
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &reg_value);
+ if (ret) {
+ dev_err_ratelimited(afe->dev, "%s irq status err\n", __func__);
+ reg_value = AFE_IRQ_STATUS_BITS;
+ goto err_irq;
+ }
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_irq_mask);
+ if (ret) {
+ dev_err_ratelimited(afe->dev, "%s irq mcu_en err\n", __func__);
+ reg_value = AFE_IRQ_STATUS_BITS;
+ goto err_irq;
+ }
+
+ /* only clr cpu irq */
+ reg_value &= mcu_irq_mask;
+
+ for (i = 0; i < MT8365_AFE_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+ struct mtk_base_afe_irq *mcu_irq;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ mcu_irq = &afe->irqs[memif->irq_usage];
+
+ if (!(reg_value & (1 << mcu_irq->irq_data->irq_clr_shift)))
+ continue;
+
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR,
+ reg_value & AFE_IRQ_STATUS_BITS);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8365_afe_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int mt8365_afe_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int mt8365_afe_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct regmap *regmap = afe->regmap;
+ int i;
+
+ mt8365_afe_enable_main_clk(afe);
+
+ if (!afe->reg_back_up)
+ afe->reg_back_up =
+ devm_kcalloc(dev, afe->reg_back_up_list_num,
+ sizeof(unsigned int), GFP_KERNEL);
+
+ for (i = 0; i < afe->reg_back_up_list_num; i++)
+ regmap_read(regmap, afe->reg_back_up_list[i],
+ &afe->reg_back_up[i]);
+
+ mt8365_afe_disable_main_clk(afe);
+
+ return 0;
+}
+
+static int mt8365_afe_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct regmap *regmap = afe->regmap;
+ int i = 0;
+
+ if (!afe->reg_back_up)
+ return 0;
+
+ mt8365_afe_enable_main_clk(afe);
+
+ for (i = 0; i < afe->reg_back_up_list_num; i++)
+ regmap_write(regmap, afe->reg_back_up_list[i],
+ afe->reg_back_up[i]);
+
+ mt8365_afe_disable_main_clk(afe);
+
+ return 0;
+}
+
+static int mt8365_afe_dev_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+ if (pm_runtime_status_suspended(dev) || afe->suspended)
+ return 0;
+
+ mt8365_afe_suspend(dev);
+ afe->suspended = true;
+ return 0;
+}
+
+static int mt8365_afe_dev_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+ if (pm_runtime_status_suspended(dev) || !afe->suspended)
+ return 0;
+
+ mt8365_afe_resume(dev);
+ afe->suspended = false;
+ return 0;
+}
+
+static int mt8365_afe_init_registers(struct mtk_base_afe *afe)
+{
+ size_t i;
+
+ static struct {
+ unsigned int reg;
+ unsigned int mask;
+ unsigned int val;
+ } init_regs[] = {
+ { AFE_CONN_24BIT, GENMASK(31, 0), GENMASK(31, 0) },
+ { AFE_CONN_24BIT_1, GENMASK(21, 0), GENMASK(21, 0) },
+ };
+
+ mt8365_afe_enable_main_clk(afe);
+
+ for (i = 0; i < ARRAY_SIZE(init_regs); i++)
+ regmap_update_bits(afe->regmap, init_regs[i].reg,
+ init_regs[i].mask, init_regs[i].val);
+
+ mt8365_afe_disable_main_clk(afe);
+
+ return 0;
+}
+
+static int mt8365_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8365_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8365_memif_dai_driver);
+
+ dai->dapm_widgets = mt8365_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8365_memif_widgets);
+ dai->dapm_routes = mt8365_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8365_memif_routes);
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8365_dai_pcm_register,
+ mt8365_dai_i2s_register,
+ mt8365_dai_adda_register,
+ mt8365_dai_dmic_register,
+ mt8365_dai_memif_register,
+};
+
+static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8365_afe_private *afe_priv;
+ struct device *dev;
+ int ret, i, sel_irq;
+ unsigned int irq_id;
+ struct resource *res;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+ dev = afe->dev;
+
+ spin_lock_init(&afe_priv->afe_ctrl_lock);
+ mutex_init(&afe_priv->afe_clk_mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ afe_priv->afe_sram_vir_addr =
+ devm_ioremap_resource(&pdev->dev, res);
+ if (!IS_ERR(afe_priv->afe_sram_vir_addr)) {
+ afe_priv->afe_sram_phy_addr = res->start;
+ afe_priv->afe_sram_size = resource_size(res);
+ }
+ }
+
+ /* initial audio related clock */
+ ret = mt8365_afe_init_audio_clk(afe);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "mt8365_afe_init_audio_clk fail\n");
+
+ afe->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "top_audio_sel",
+ afe->base_addr,
+ &mt8365_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ /* memif % irq initialize*/
+ afe->memif_size = MT8365_AFE_MEMIF_NUM;
+ afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+ sizeof(*afe->memif), GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ afe->irqs_size = MT8365_AFE_IRQ_NUM;
+ afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+ sizeof(*afe->irqs), GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ irq_id = ret;
+ ret = devm_request_irq(afe->dev, irq_id, mt8365_afe_irq_handler,
+ 0, "Afe_ISR_Handle", (void *)afe);
+ if (ret)
+ return dev_err_probe(afe->dev, ret, "could not request_irq\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret) {
+ dev_warn(afe->dev, "dai register i %d fail, ret %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret) {
+ dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ sel_irq = memif_specified_irqs[i];
+ if (sel_irq >= 0) {
+ afe->memif[i].irq_usage = sel_irq;
+ afe->memif[i].const_irq = 1;
+ afe->irqs[sel_irq].irq_occupyed = true;
+ } else {
+ afe->memif[i].irq_usage = -1;
+ }
+ }
+
+ afe->mtk_afe_hardware = &mt8365_afe_hardware;
+ afe->memif_fs = mt8365_memif_fs;
+ afe->irq_fs = mt8365_irq_fs;
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_sync(&pdev->dev);
+ afe->reg_back_up_list = mt8365_afe_backup_list;
+ afe->reg_back_up_list_num = ARRAY_SIZE(mt8365_afe_backup_list);
+ afe->runtime_resume = mt8365_afe_runtime_resume;
+ afe->runtime_suspend = mt8365_afe_runtime_suspend;
+
+ /* open afe pdn for dapm read/write audio register */
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_AFE);
+
+ /* Set 26m parent clk */
+ mt8365_afe_set_clk_parent(afe,
+ afe_priv->clocks[MT8365_CLK_TOP_AUD_SEL],
+ afe_priv->clocks[MT8365_CLK_CLK26M]);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mtk_afe_pcm_platform,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ return ret;
+ }
+
+ mt8365_afe_init_registers(afe);
+
+ return 0;
+}
+
+static void mt8365_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_AFE);
+
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt8365_afe_runtime_suspend(&pdev->dev);
+}
+
+static const struct of_device_id mt8365_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8365-afe-pcm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt8365_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8365_afe_pm_ops = {
+ RUNTIME_PM_OPS(mt8365_afe_dev_runtime_suspend,
+ mt8365_afe_dev_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mt8365_afe_suspend, mt8365_afe_resume)
+};
+
+static struct platform_driver mt8365_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8365-afe-pcm",
+ .of_match_table = mt8365_afe_pcm_dt_match,
+ .pm = pm_ptr(&mt8365_afe_pm_ops),
+ },
+ .probe = mt8365_afe_pcm_dev_probe,
+ .remove = mt8365_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8365_afe_pcm_driver);
+
+MODULE_DESCRIPTION("MediaTek ALSA SoC AFE platform driver");
+MODULE_AUTHOR("Jia Zeng <jia.zeng@mediatek.com>");
+MODULE_AUTHOR("Alexandre Mergnat <amergnat@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-adda.c b/sound/soc/mediatek/mt8365/mt8365-dai-adda.c
new file mode 100644
index 000000000000..a04c24bbfcff
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-dai-adda.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 ALSA SoC Audio DAI ADDA Control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8365-afe-clk.h"
+#include "mt8365-afe-common.h"
+#include "../common/mtk-dai-adda-common.h"
+
+static int adda_afe_on_ref_cnt;
+
+/* DAI Drivers */
+
+static int mt8365_dai_set_adda_out(struct mtk_base_afe *afe, unsigned int rate)
+{
+ unsigned int val;
+
+ if (rate == 8000 || rate == 16000)
+ val = AFE_ADDA_DL_VOICE_DATA;
+ else
+ val = 0;
+
+ val |= FIELD_PREP(AFE_ADDA_DL_SAMPLING_RATE,
+ mtk_adda_dl_rate_transform(afe, rate));
+ val |= AFE_ADDA_DL_8X_UPSAMPLE |
+ AFE_ADDA_DL_MUTE_OFF_CH1 |
+ AFE_ADDA_DL_MUTE_OFF_CH2 |
+ AFE_ADDA_DL_DEGRADE_GAIN;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON0, 0xffffffff, 0);
+ regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON1, 0xffffffff, 0);
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0xffffffff, val);
+ /* SA suggest apply -0.3db to audio/speech path */
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON1,
+ 0xffffffff, 0xf74f0000);
+ /* SA suggest use default value for sdm */
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON,
+ 0xffffffff, 0x0700701e);
+
+ return 0;
+}
+
+static int mt8365_dai_set_adda_in(struct mtk_base_afe *afe, unsigned int rate)
+{
+ unsigned int val;
+
+ val = FIELD_PREP(AFE_ADDA_UL_SAMPLING_RATE,
+ mtk_adda_ul_rate_transform(afe, rate));
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ AFE_ADDA_UL_SAMPLING_RATE, val);
+ /* Using Internal ADC */
+ regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x0);
+
+ return 0;
+}
+
+int mt8365_dai_enable_adda_on(struct mtk_base_afe *afe)
+{
+ unsigned long flags;
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ adda_afe_on_ref_cnt++;
+ if (adda_afe_on_ref_cnt == 1)
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_UL_DL_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_ADDA_AFE_ON);
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+
+ return 0;
+}
+
+int mt8365_dai_disable_adda_on(struct mtk_base_afe *afe)
+{
+ unsigned long flags;
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ adda_afe_on_ref_cnt--;
+ if (adda_afe_on_ref_cnt == 0)
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_UL_DL_ADDA_AFE_ON,
+ ~AFE_ADDA_UL_DL_ADDA_AFE_ON);
+ else if (adda_afe_on_ref_cnt < 0) {
+ adda_afe_on_ref_cnt = 0;
+ dev_warn(afe->dev, "Abnormal adda_on ref count. Force it to 0\n");
+ }
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+
+ return 0;
+}
+
+static void mt8365_dai_set_adda_out_enable(struct mtk_base_afe *afe,
+ bool enable)
+{
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0x1, enable);
+
+ if (enable)
+ mt8365_dai_enable_adda_on(afe);
+ else
+ mt8365_dai_disable_adda_on(afe);
+}
+
+static void mt8365_dai_set_adda_in_enable(struct mtk_base_afe *afe, bool enable)
+{
+ if (enable) {
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x1);
+ mt8365_dai_enable_adda_on(afe);
+ /* enable aud_pad_top fifo */
+ regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP,
+ 0xffffffff, 0x31);
+ } else {
+ /* disable aud_pad_top fifo */
+ regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP,
+ 0xffffffff, 0x30);
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x0);
+ /* de suggest disable ADDA_UL_SRC at least wait 125us */
+ usleep_range(150, 300);
+ mt8365_dai_disable_adda_on(afe);
+ }
+}
+
+static int mt8365_dai_int_adda_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int stream = substream->stream;
+
+ mt8365_afe_enable_main_clk(afe);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS);
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_ADC);
+ }
+
+ return 0;
+}
+
+static void mt8365_dai_int_adda_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_be_dai_data *be =
+ &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+ unsigned int stream = substream->stream;
+
+ if (be->prepared[stream]) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mt8365_dai_set_adda_out_enable(afe, false);
+ mt8365_afe_set_i2s_out_enable(afe, false);
+ } else {
+ mt8365_dai_set_adda_in_enable(afe, false);
+ }
+ be->prepared[stream] = false;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC);
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_ADC);
+ }
+
+ mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_dai_int_adda_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_be_dai_data *be =
+ &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+ unsigned int rate = substream->runtime->rate;
+ int bit_width = snd_pcm_format_width(substream->runtime->format);
+ int ret;
+
+ dev_info(afe->dev, "%s '%s' rate = %u\n", __func__,
+ snd_pcm_stream_str(substream), rate);
+
+ if (be->prepared[substream->stream]) {
+ dev_info(afe->dev, "%s '%s' prepared already\n",
+ __func__, snd_pcm_stream_str(substream));
+ return 0;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = mt8365_dai_set_adda_out(afe, rate);
+ if (ret)
+ return ret;
+
+ ret = mt8365_afe_set_i2s_out(afe, rate, bit_width);
+ if (ret)
+ return ret;
+
+ mt8365_dai_set_adda_out_enable(afe, true);
+ mt8365_afe_set_i2s_out_enable(afe, true);
+ } else {
+ ret = mt8365_dai_set_adda_in(afe, rate);
+ if (ret)
+ return ret;
+
+ mt8365_dai_set_adda_in_enable(afe, true);
+ }
+ be->prepared[substream->stream] = true;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt8365_afe_int_adda_ops = {
+ .startup = mt8365_dai_int_adda_startup,
+ .shutdown = mt8365_dai_int_adda_shutdown,
+ .prepare = mt8365_dai_int_adda_prepare,
+};
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "INT ADDA",
+ .id = MT8365_AFE_IO_INT_ADDA,
+ .playback = {
+ .stream_name = "INT ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "INT ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_int_adda_ops,
+ }
+};
+
+/* DAI Controls */
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN3,
+ 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN4,
+ 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new int_adda_o03_o04_enable_ctl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+/* DAI widget */
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ SND_SOC_DAPM_SWITCH("INT ADDA O03_O04", SND_SOC_NOPM, 0, 0,
+ &int_adda_o03_o04_enable_ctl),
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch1_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
+ SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
+ mtk_adda_dl_ch2_mix,
+ ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
+};
+
+/* DAI route */
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ {"INT ADDA O03_O04", "Switch", "O03"},
+ {"INT ADDA O03_O04", "Switch", "O04"},
+ {"INT ADDA Playback", NULL, "INT ADDA O03_O04"},
+ {"INT ADDA Playback", NULL, "ADDA_DL_CH1"},
+ {"INT ADDA Playback", NULL, "ADDA_DL_CH2"},
+ {"AIN Mux", "INT ADC", "INT ADDA Capture"},
+ {"ADDA_DL_CH1", "GAIN1_OUT_CH1", "Hostless FM DL"},
+ {"ADDA_DL_CH2", "GAIN1_OUT_CH2", "Hostless FM DL"},
+};
+
+int mt8365_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+ list_add(&dai->list, &afe->sub_dais);
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-dmic.c b/sound/soc/mediatek/mt8365/mt8365-dai-dmic.c
new file mode 100644
index 000000000000..0bac143b48bf
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-dai-dmic.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 ALSA SoC Audio DAI DMIC Control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8365-afe-clk.h"
+#include "mt8365-afe-common.h"
+
+struct mt8365_dmic_data {
+ bool two_wire_mode;
+ unsigned int clk_phase_sel_ch1;
+ unsigned int clk_phase_sel_ch2;
+ bool iir_on;
+ unsigned int irr_mode;
+ unsigned int dmic_mode;
+ unsigned int dmic_channel;
+};
+
+static int get_chan_reg(unsigned int channel)
+{
+ switch (channel) {
+ case 8:
+ fallthrough;
+ case 7:
+ return AFE_DMIC3_UL_SRC_CON0;
+ case 6:
+ fallthrough;
+ case 5:
+ return AFE_DMIC2_UL_SRC_CON0;
+ case 4:
+ fallthrough;
+ case 3:
+ return AFE_DMIC1_UL_SRC_CON0;
+ case 2:
+ fallthrough;
+ case 1:
+ return AFE_DMIC0_UL_SRC_CON0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* DAI Drivers */
+
+static void audio_dmic_adda_enable(struct mtk_base_afe *afe)
+{
+ mt8365_dai_enable_adda_on(afe);
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_UL_DL_DMIC_CLKDIV_ON,
+ AFE_ADDA_UL_DL_DMIC_CLKDIV_ON);
+}
+
+static void audio_dmic_adda_disable(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
+ AFE_ADDA_UL_DL_DMIC_CLKDIV_ON,
+ ~AFE_ADDA_UL_DL_DMIC_CLKDIV_ON);
+ mt8365_dai_disable_adda_on(afe);
+}
+
+static void mt8365_dai_enable_dmic(struct mtk_base_afe *afe,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_dmic_data *dmic_data = afe_priv->dai_priv[MT8365_AFE_IO_DMIC];
+ unsigned int val_mask;
+ int reg = get_chan_reg(dmic_data->dmic_channel);
+
+ if (reg < 0)
+ return;
+
+ /* val and mask will be always same to enable */
+ val_mask = DMIC_TOP_CON_CH1_ON |
+ DMIC_TOP_CON_CH2_ON |
+ DMIC_TOP_CON_SRC_ON;
+
+ regmap_update_bits(afe->regmap, reg, val_mask, val_mask);
+}
+
+static void mt8365_dai_disable_dmic(struct mtk_base_afe *afe,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_dmic_data *dmic_data = afe_priv->dai_priv[MT8365_AFE_IO_DMIC];
+ unsigned int mask;
+ int reg = get_chan_reg(dmic_data->dmic_channel);
+
+ if (reg < 0)
+ return;
+
+ dev_dbg(afe->dev, "%s dmic_channel %d\n", __func__, dmic_data->dmic_channel);
+
+ mask = DMIC_TOP_CON_CH1_ON |
+ DMIC_TOP_CON_CH2_ON |
+ DMIC_TOP_CON_SRC_ON |
+ DMIC_TOP_CON_SDM3_LEVEL_MODE;
+
+ /* Set all masked values to 0 */
+ regmap_update_bits(afe->regmap, reg, mask, 0);
+}
+
+static int mt8365_dai_configure_dmic(struct mtk_base_afe *afe,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_dmic_data *dmic_data = afe_priv->dai_priv[MT8365_AFE_IO_DMIC];
+ bool two_wire_mode = dmic_data->two_wire_mode;
+ unsigned int clk_phase_sel_ch1 = dmic_data->clk_phase_sel_ch1;
+ unsigned int clk_phase_sel_ch2 = dmic_data->clk_phase_sel_ch2;
+ unsigned int val = 0;
+ unsigned int rate = dai->symmetric_rate;
+ int reg = get_chan_reg(dai->symmetric_channels);
+
+ if (reg < 0)
+ return -EINVAL;
+
+ dmic_data->dmic_channel = dai->symmetric_channels;
+
+ val |= DMIC_TOP_CON_SDM3_LEVEL_MODE;
+
+ if (two_wire_mode) {
+ val |= DMIC_TOP_CON_TWO_WIRE_MODE;
+ } else {
+ val |= FIELD_PREP(DMIC_TOP_CON_CK_PHASE_SEL_CH1,
+ clk_phase_sel_ch1);
+ val |= FIELD_PREP(DMIC_TOP_CON_CK_PHASE_SEL_CH2,
+ clk_phase_sel_ch2);
+ }
+
+ switch (rate) {
+ case 48000:
+ val |= DMIC_TOP_CON_VOICE_MODE_48K;
+ break;
+ case 32000:
+ val |= DMIC_TOP_CON_VOICE_MODE_32K;
+ break;
+ case 16000:
+ val |= DMIC_TOP_CON_VOICE_MODE_16K;
+ break;
+ case 8000:
+ val |= DMIC_TOP_CON_VOICE_MODE_8K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(afe->regmap, reg, DMIC_TOP_CON_CONFIG_MASK, val);
+
+ return 0;
+}
+
+static int mt8365_dai_dmic_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ mt8365_afe_enable_main_clk(afe);
+
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC0_ADC);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC1_ADC);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC2_ADC);
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC3_ADC);
+
+ audio_dmic_adda_enable(afe);
+
+ return 0;
+}
+
+static void mt8365_dai_dmic_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ mt8365_dai_disable_dmic(afe, substream, dai);
+ audio_dmic_adda_disable(afe);
+ /* HW Request delay 125us before CG off */
+ usleep_range(125, 300);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC3_ADC);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC2_ADC);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC1_ADC);
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC0_ADC);
+
+ mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_dai_dmic_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ mt8365_dai_configure_dmic(afe, substream, dai);
+ mt8365_dai_enable_dmic(afe, substream, dai);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt8365_afe_dmic_ops = {
+ .startup = mt8365_dai_dmic_startup,
+ .shutdown = mt8365_dai_dmic_shutdown,
+ .prepare = mt8365_dai_dmic_prepare,
+};
+
+static struct snd_soc_dai_driver mtk_dai_dmic_driver[] = {
+ {
+ .name = "DMIC",
+ .id = MT8365_AFE_IO_DMIC,
+ .capture = {
+ .stream_name = "DMIC Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_dmic_ops,
+ }
+};
+
+/* DAI Controls */
+
+/* Values for 48kHz mode */
+static const char * const iir_mode_src[] = {
+ "SW custom", "5Hz", "10Hz", "25Hz", "50Hz", "65Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(iir_mode, AFE_DMIC0_UL_SRC_CON0, 7, iir_mode_src);
+
+static const struct snd_kcontrol_new mtk_dai_dmic_controls[] = {
+ SOC_SINGLE("DMIC IIR Switch", AFE_DMIC0_UL_SRC_CON0, DMIC_TOP_CON_IIR_ON, 1, 0),
+ SOC_ENUM("DMIC IIR Mode", iir_mode),
+};
+
+/* DAI widget */
+
+static const struct snd_soc_dapm_widget mtk_dai_dmic_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC In"),
+};
+
+/* DAI route */
+
+static const struct snd_soc_dapm_route mtk_dai_dmic_routes[] = {
+ {"I14", NULL, "DMIC Capture"},
+ {"I15", NULL, "DMIC Capture"},
+ {"I16", NULL, "DMIC Capture"},
+ {"I17", NULL, "DMIC Capture"},
+ {"I18", NULL, "DMIC Capture"},
+ {"I19", NULL, "DMIC Capture"},
+ {"I20", NULL, "DMIC Capture"},
+ {"I21", NULL, "DMIC Capture"},
+ {"DMIC Capture", NULL, "DMIC In"},
+};
+
+static int init_dmic_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_dmic_data *dmic_priv;
+ struct device_node *np = afe->dev->of_node;
+ unsigned int temps[4];
+ int ret;
+
+ dmic_priv = devm_kzalloc(afe->dev, sizeof(*dmic_priv), GFP_KERNEL);
+ if (!dmic_priv)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "mediatek,dmic-mode",
+ &temps[0],
+ 1);
+ if (ret == 0)
+ dmic_priv->two_wire_mode = !!temps[0];
+
+ if (!dmic_priv->two_wire_mode) {
+ dmic_priv->clk_phase_sel_ch1 = 0;
+ dmic_priv->clk_phase_sel_ch2 = 4;
+ }
+
+ afe_priv->dai_priv[MT8365_AFE_IO_DMIC] = dmic_priv;
+ return 0;
+}
+
+int mt8365_dai_dmic_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+ dai->dai_drivers = mtk_dai_dmic_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_dmic_driver);
+ dai->controls = mtk_dai_dmic_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_dmic_controls);
+ dai->dapm_widgets = mtk_dai_dmic_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_dmic_widgets);
+ dai->dapm_routes = mtk_dai_dmic_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_dmic_routes);
+ return init_dmic_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c
new file mode 100644
index 000000000000..cb9beb172ed5
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c
@@ -0,0 +1,845 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 ALSA SoC Audio DAI I2S Control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8365-afe-clk.h"
+#include "mt8365-afe-common.h"
+
+#define IIR_RATIOVER 9
+#define IIR_INV_COEF 10
+#define IIR_NO_NEED 11
+
+struct mtk_afe_i2s_priv {
+ bool adda_link;
+ int i2s_out_on_ref_cnt;
+ int id;
+ int low_jitter_en;
+ int mclk_id;
+ int share_i2s_id;
+ unsigned int clk_id_in;
+ unsigned int clk_id_in_m_sel;
+ unsigned int clk_id_out;
+ unsigned int clk_id_out_m_sel;
+ unsigned int clk_in_mult;
+ unsigned int clk_out_mult;
+ unsigned int config_val_in;
+ unsigned int config_val_out;
+ unsigned int dynamic_bck;
+ unsigned int reg_off_in;
+ unsigned int reg_off_out;
+};
+
+/* This enum is merely for mtk_afe_i2s_priv declare */
+enum {
+ DAI_I2S0 = 0,
+ DAI_I2S3,
+ DAI_I2S_NUM,
+};
+
+static const struct mtk_afe_i2s_priv mt8365_i2s_priv[DAI_I2S_NUM] = {
+ [DAI_I2S0] = {
+ .id = MT8365_AFE_IO_I2S,
+ .mclk_id = MT8365_I2S0_MCK,
+ .share_i2s_id = -1,
+ .clk_id_in = MT8365_CLK_AUD_I2S2_M,
+ .clk_id_out = MT8365_CLK_AUD_I2S1_M,
+ .clk_id_in_m_sel = MT8365_CLK_I2S2_M_SEL,
+ .clk_id_out_m_sel = MT8365_CLK_I2S1_M_SEL,
+ .clk_in_mult = 256,
+ .clk_out_mult = 256,
+ .adda_link = true,
+ .config_val_out = AFE_I2S_CON1_I2S2_TO_PAD,
+ .reg_off_in = AFE_I2S_CON2,
+ .reg_off_out = AFE_I2S_CON1,
+ },
+ [DAI_I2S3] = {
+ .id = MT8365_AFE_IO_2ND_I2S,
+ .mclk_id = MT8365_I2S3_MCK,
+ .share_i2s_id = -1,
+ .clk_id_in = MT8365_CLK_AUD_I2S0_M,
+ .clk_id_out = MT8365_CLK_AUD_I2S3_M,
+ .clk_id_in_m_sel = MT8365_CLK_I2S0_M_SEL,
+ .clk_id_out_m_sel = MT8365_CLK_I2S3_M_SEL,
+ .clk_in_mult = 256,
+ .clk_out_mult = 256,
+ .adda_link = false,
+ .config_val_in = AFE_I2S_CON_FROM_IO_MUX,
+ .reg_off_in = AFE_I2S_CON,
+ .reg_off_out = AFE_I2S_CON3,
+ },
+};
+
+static const u32 *get_iir_coef(unsigned int input_fs,
+ unsigned int output_fs, unsigned int *count)
+{
+ static const u32 IIR_COEF_48_TO_44p1[30] = {
+ 0x061fb0, 0x0bd256, 0x061fb0, 0xe3a3e6, 0xf0a300, 0x000003,
+ 0x0e416d, 0x1bb577, 0x0e416d, 0xe59178, 0xf23637, 0x000003,
+ 0x0c7d72, 0x189060, 0x0c7d72, 0xe96f09, 0xf505b2, 0x000003,
+ 0x126054, 0x249143, 0x126054, 0xe1fc0c, 0xf4b20a, 0x000002,
+ 0x000000, 0x323c85, 0x323c85, 0xf76d4e, 0x000000, 0x000002,
+ };
+
+ static const u32 IIR_COEF_44p1_TO_32[42] = {
+ 0x0a6074, 0x0d237a, 0x0a6074, 0xdd8d6c, 0xe0b3f6, 0x000002,
+ 0x0e41f8, 0x128d48, 0x0e41f8, 0xefc14e, 0xf12d7a, 0x000003,
+ 0x0cfa60, 0x11e89c, 0x0cfa60, 0xf1b09e, 0xf27205, 0x000003,
+ 0x15b69c, 0x20e7e4, 0x15b69c, 0xea799a, 0xe9314a, 0x000002,
+ 0x0f79e2, 0x1a7064, 0x0f79e2, 0xf65e4a, 0xf03d8e, 0x000002,
+ 0x10c34f, 0x1ffe4b, 0x10c34f, 0x0bbecb, 0xf2bc4b, 0x000001,
+ 0x000000, 0x23b063, 0x23b063, 0x07335f, 0x000000, 0x000002,
+ };
+
+ static const u32 IIR_COEF_48_TO_32[42] = {
+ 0x0a2a9b, 0x0a2f05, 0x0a2a9b, 0xe73873, 0xe0c525, 0x000002,
+ 0x0dd4ad, 0x0e765a, 0x0dd4ad, 0xf49808, 0xf14844, 0x000003,
+ 0x18a8cd, 0x1c40d0, 0x18a8cd, 0xed2aab, 0xe542ec, 0x000002,
+ 0x13e044, 0x1a47c4, 0x13e044, 0xf44aed, 0xe9acc7, 0x000002,
+ 0x1abd9c, 0x2a5429, 0x1abd9c, 0xff3441, 0xe0fc5f, 0x000001,
+ 0x0d86db, 0x193e2e, 0x0d86db, 0x1a6f15, 0xf14507, 0x000001,
+ 0x000000, 0x1f820c, 0x1f820c, 0x0a1b1f, 0x000000, 0x000002,
+ };
+
+ static const u32 IIR_COEF_32_TO_16[48] = {
+ 0x122893, 0xffadd4, 0x122893, 0x0bc205, 0xc0ee1c, 0x000001,
+ 0x1bab8a, 0x00750d, 0x1bab8a, 0x06a983, 0xe18a5c, 0x000002,
+ 0x18f68e, 0x02706f, 0x18f68e, 0x0886a9, 0xe31bcb, 0x000002,
+ 0x149c05, 0x054487, 0x149c05, 0x0bec31, 0xe5973e, 0x000002,
+ 0x0ea303, 0x07f24a, 0x0ea303, 0x115ff9, 0xe967b6, 0x000002,
+ 0x0823fd, 0x085531, 0x0823fd, 0x18d5b4, 0xee8d21, 0x000002,
+ 0x06888e, 0x0acbbb, 0x06888e, 0x40b55c, 0xe76dce, 0x000001,
+ 0x000000, 0x2d31a9, 0x2d31a9, 0x23ba4f, 0x000000, 0x000001,
+ };
+
+ static const u32 IIR_COEF_96_TO_44p1[48] = {
+ 0x08b543, 0xfd80f4, 0x08b543, 0x0e2332, 0xe06ed0, 0x000002,
+ 0x1b6038, 0xf90e7e, 0x1b6038, 0x0ec1ac, 0xe16f66, 0x000002,
+ 0x188478, 0xfbb921, 0x188478, 0x105859, 0xe2e596, 0x000002,
+ 0x13eff3, 0xffa707, 0x13eff3, 0x13455c, 0xe533b7, 0x000002,
+ 0x0dc239, 0x03d458, 0x0dc239, 0x17f120, 0xe8b617, 0x000002,
+ 0x0745f1, 0x05d790, 0x0745f1, 0x1e3d75, 0xed5f18, 0x000002,
+ 0x05641f, 0x085e2b, 0x05641f, 0x48efd0, 0xe3e9c8, 0x000001,
+ 0x000000, 0x28f632, 0x28f632, 0x273905, 0x000000, 0x000001,
+ };
+
+ static const u32 IIR_COEF_44p1_TO_16[48] = {
+ 0x0998fb, 0xf7f925, 0x0998fb, 0x1e54a0, 0xe06605, 0x000002,
+ 0x0d828e, 0xf50f97, 0x0d828e, 0x0f41b5, 0xf0a999, 0x000003,
+ 0x17ebeb, 0xee30d8, 0x17ebeb, 0x1f48ca, 0xe2ae88, 0x000002,
+ 0x12fab5, 0xf46ddc, 0x12fab5, 0x20cc51, 0xe4d068, 0x000002,
+ 0x0c7ac6, 0xfbd00e, 0x0c7ac6, 0x2337da, 0xe8028c, 0x000002,
+ 0x060ddc, 0x015b3e, 0x060ddc, 0x266754, 0xec21b6, 0x000002,
+ 0x0407b5, 0x04f827, 0x0407b5, 0x52e3d0, 0xe0149f, 0x000001,
+ 0x000000, 0x1f9521, 0x1f9521, 0x2ac116, 0x000000, 0x000001,
+ };
+
+ static const u32 IIR_COEF_48_TO_16[48] = {
+ 0x0955ff, 0xf6544a, 0x0955ff, 0x2474e5, 0xe062e6, 0x000002,
+ 0x0d4180, 0xf297f4, 0x0d4180, 0x12415b, 0xf0a3b0, 0x000003,
+ 0x0ba079, 0xf4f0b0, 0x0ba079, 0x1285d3, 0xf1488b, 0x000003,
+ 0x12247c, 0xf1033c, 0x12247c, 0x2625be, 0xe48e0d, 0x000002,
+ 0x0b98e0, 0xf96d1a, 0x0b98e0, 0x27e79c, 0xe7798a, 0x000002,
+ 0x055e3b, 0xffed09, 0x055e3b, 0x2a2e2d, 0xeb2854, 0x000002,
+ 0x01a934, 0x01ca03, 0x01a934, 0x2c4fea, 0xee93ab, 0x000002,
+ 0x000000, 0x1c46c5, 0x1c46c5, 0x2d37dc, 0x000000, 0x000001,
+ };
+
+ static const u32 IIR_COEF_96_TO_16[48] = {
+ 0x0805a1, 0xf21ae3, 0x0805a1, 0x3840bb, 0xe02a2e, 0x000002,
+ 0x0d5dd8, 0xe8f259, 0x0d5dd8, 0x1c0af6, 0xf04700, 0x000003,
+ 0x0bb422, 0xec08d9, 0x0bb422, 0x1bfccc, 0xf09216, 0x000003,
+ 0x08fde6, 0xf108be, 0x08fde6, 0x1bf096, 0xf10ae0, 0x000003,
+ 0x0ae311, 0xeeeda3, 0x0ae311, 0x37c646, 0xe385f5, 0x000002,
+ 0x044089, 0xfa7242, 0x044089, 0x37a785, 0xe56526, 0x000002,
+ 0x00c75c, 0xffb947, 0x00c75c, 0x378ba3, 0xe72c5f, 0x000002,
+ 0x000000, 0x0ef76e, 0x0ef76e, 0x377fda, 0x000000, 0x000001,
+ };
+
+ static const struct {
+ const u32 *coef;
+ unsigned int cnt;
+ } iir_coef_tbl_list[8] = {
+ /* 0: 0.9188 */
+ { IIR_COEF_48_TO_44p1, ARRAY_SIZE(IIR_COEF_48_TO_44p1) },
+ /* 1: 0.7256 */
+ { IIR_COEF_44p1_TO_32, ARRAY_SIZE(IIR_COEF_44p1_TO_32) },
+ /* 2: 0.6667 */
+ { IIR_COEF_48_TO_32, ARRAY_SIZE(IIR_COEF_48_TO_32) },
+ /* 3: 0.5 */
+ { IIR_COEF_32_TO_16, ARRAY_SIZE(IIR_COEF_32_TO_16) },
+ /* 4: 0.4594 */
+ { IIR_COEF_96_TO_44p1, ARRAY_SIZE(IIR_COEF_96_TO_44p1) },
+ /* 5: 0.3628 */
+ { IIR_COEF_44p1_TO_16, ARRAY_SIZE(IIR_COEF_44p1_TO_16) },
+ /* 6: 0.3333 */
+ { IIR_COEF_48_TO_16, ARRAY_SIZE(IIR_COEF_48_TO_16) },
+ /* 7: 0.1667 */
+ { IIR_COEF_96_TO_16, ARRAY_SIZE(IIR_COEF_96_TO_16) },
+ };
+
+ static const u32 freq_new_index[16] = {
+ 0, 1, 2, 99, 3, 4, 5, 99, 6, 7, 8, 9, 10, 11, 12, 99
+ };
+
+ static const u32 iir_coef_tbl_matrix[13][13] = {
+ {/*0*/
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*1*/
+ 1, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*2*/
+ 2, 0, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*3*/
+ 3, IIR_INV_COEF, IIR_INV_COEF, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*4*/
+ 5, 3, IIR_INV_COEF, 2, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*5*/
+ 6, 4, 3, 2, 0, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*6*/
+ IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, 3, IIR_INV_COEF,
+ IIR_INV_COEF, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*7*/
+ IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, 5, 3,
+ IIR_INV_COEF, 1, IIR_NO_NEED, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*8*/
+ 7, IIR_INV_COEF, IIR_INV_COEF, 6, 4, 3, 2, 0, IIR_NO_NEED,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*9*/
+ IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF,
+ IIR_INV_COEF, IIR_INV_COEF, 5, 3, IIR_INV_COEF,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*10*/
+ IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, 7, IIR_INV_COEF,
+ IIR_INV_COEF, 6, 4, 3, 0,
+ IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED
+ },
+ { /*11*/
+ IIR_RATIOVER, IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF,
+ IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF,
+ IIR_INV_COEF, 3, IIR_INV_COEF, IIR_NO_NEED, IIR_NO_NEED
+ },
+ {/*12*/
+ IIR_RATIOVER, IIR_RATIOVER, IIR_INV_COEF, IIR_INV_COEF,
+ IIR_INV_COEF, IIR_INV_COEF, 7, IIR_INV_COEF,
+ IIR_INV_COEF, 4, 3, 0, IIR_NO_NEED
+ },
+ };
+
+ const u32 *coef = NULL;
+ unsigned int cnt = 0;
+ u32 i = freq_new_index[input_fs];
+ u32 j = freq_new_index[output_fs];
+
+ if (i < 13 && j < 13) {
+ u32 k = iir_coef_tbl_matrix[i][j];
+
+ if (k >= IIR_NO_NEED) {
+ } else if (k == IIR_RATIOVER) {
+ } else if (k == IIR_INV_COEF) {
+ } else {
+ coef = iir_coef_tbl_list[k].coef;
+ cnt = iir_coef_tbl_list[k].cnt;
+ }
+ }
+ *count = cnt;
+ return coef;
+}
+
+static int mt8365_dai_set_config(struct mtk_base_afe *afe,
+ struct mtk_afe_i2s_priv *i2s_data,
+ bool is_input, unsigned int rate,
+ int bit_width)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_be_dai_data *be =
+ &afe_priv->be_data[i2s_data->id - MT8365_AFE_BACKEND_BASE];
+ unsigned int val, reg_off;
+ int fs = mt8365_afe_fs_timing(rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ val = AFE_I2S_CON_LOW_JITTER_CLK | AFE_I2S_CON_FORMAT_I2S;
+ val |= FIELD_PREP(AFE_I2S_CON_RATE_MASK, fs);
+
+ if (is_input) {
+ reg_off = i2s_data->reg_off_in;
+ if (i2s_data->adda_link)
+ val |= i2s_data->config_val_in;
+ } else {
+ reg_off = i2s_data->reg_off_out;
+ val |= i2s_data->config_val_in;
+ }
+
+ /* 1:bck=32lrck(16bit) or bck=64lrck(32bit) 0:fix bck=64lrck */
+ if (i2s_data->dynamic_bck) {
+ if (bit_width > 16)
+ val |= AFE_I2S_CON_WLEN_32BIT;
+ else
+ val &= ~(u32)AFE_I2S_CON_WLEN_32BIT;
+ } else {
+ val |= AFE_I2S_CON_WLEN_32BIT;
+ }
+
+ if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) ==
+ SND_SOC_DAIFMT_CBP_CFP) {
+ val |= AFE_I2S_CON_SRC_SLAVE;
+ val &= ~(u32)AFE_I2S_CON_FROM_IO_MUX;//from consys
+ }
+
+ regmap_update_bits(afe->regmap, reg_off, ~(u32)AFE_I2S_CON_EN, val);
+
+ if (i2s_data->adda_link && is_input)
+ regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1);
+
+ return 0;
+}
+
+int mt8365_afe_set_i2s_out(struct mtk_base_afe *afe,
+ unsigned int rate, int bit_width)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_data =
+ afe_priv->dai_priv[MT8365_AFE_IO_I2S];
+
+ return mt8365_dai_set_config(afe, i2s_data, false, rate, bit_width);
+}
+
+static int mt8365_afe_set_2nd_i2s_asrc(struct mtk_base_afe *afe,
+ unsigned int rate_in,
+ unsigned int rate_out,
+ unsigned int width,
+ unsigned int mono,
+ int o16bit, int tracking)
+{
+ int ifs, ofs = 0;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ const u32 *coef;
+ u32 iir_stage;
+ unsigned int coef_count = 0;
+
+ ifs = mt8365_afe_fs_timing(rate_in);
+
+ if (ifs < 0)
+ return -EINVAL;
+
+ ofs = mt8365_afe_fs_timing(rate_out);
+
+ if (ofs < 0)
+ return -EINVAL;
+
+ val = FIELD_PREP(O16BIT, o16bit) | FIELD_PREP(IS_MONO, mono);
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2,
+ O16BIT | IS_MONO, val);
+
+ coef = get_iir_coef(ifs, ofs, &coef_count);
+ iir_stage = ((u32)coef_count / 6) - 1;
+
+ if (coef) {
+ unsigned int i;
+
+ /* CPU control IIR coeff SRAM */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0,
+ COEFF_SRAM_CTRL, COEFF_SRAM_CTRL);
+
+ /* set to 0, IIR coeff SRAM addr */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON13,
+ 0xffffffff, 0x0);
+
+ for (i = 0; i < coef_count; ++i)
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON12,
+ 0xffffffff, coef[i]);
+
+ /* disable IIR coeff SRAM access */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0,
+ COEFF_SRAM_CTRL,
+ ~COEFF_SRAM_CTRL);
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2,
+ CLR_IIR_HISTORY | IIR_EN | IIR_STAGE_MASK,
+ CLR_IIR_HISTORY | IIR_EN |
+ FIELD_PREP(IIR_STAGE_MASK, iir_stage));
+ } else {
+ /* disable IIR */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2,
+ IIR_EN, ~IIR_EN);
+ }
+
+ /* CON3 setting (RX OFS) */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON3,
+ 0x00FFFFFF, rx_frequency_palette(ofs));
+ /* CON4 setting (RX IFS) */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON4,
+ 0x00FFFFFF, rx_frequency_palette(ifs));
+
+ /* CON5 setting */
+ if (tracking) {
+ val = CALI_64_CYCLE |
+ CALI_AUTORST |
+ AUTO_TUNE_FREQ5 |
+ COMP_FREQ_RES |
+ CALI_BP_DGL |
+ CALI_AUTO_RESTART |
+ CALI_USE_FREQ_OUT |
+ CALI_SEL_01;
+
+ mask = CALI_CYCLE_MASK |
+ CALI_AUTORST |
+ AUTO_TUNE_FREQ5 |
+ COMP_FREQ_RES |
+ CALI_SEL_MASK |
+ CALI_BP_DGL |
+ AUTO_TUNE_FREQ4 |
+ CALI_AUTO_RESTART |
+ CALI_USE_FREQ_OUT |
+ CALI_ON;
+
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5,
+ mask, val);
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5,
+ CALI_ON, CALI_ON);
+ } else {
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5,
+ 0xffffffff, 0x0);
+ }
+ /* CON6 setting fix 8125 */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON6,
+ 0x0000ffff, 0x1FBD);
+ /* CON9 setting (RX IFS) */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON9,
+ 0x000fffff, AutoRstThHi(ifs));
+ /* CON10 setting (RX IFS) */
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON10,
+ 0x000fffff, AutoRstThLo(ifs));
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0,
+ CHSET_STR_CLR, CHSET_STR_CLR);
+
+ return 0;
+}
+
+static int mt8365_afe_set_2nd_i2s_asrc_enable(struct mtk_base_afe *afe,
+ bool enable)
+{
+ if (enable)
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0,
+ ASM_ON, ASM_ON);
+ else
+ regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0,
+ ASM_ON, ~ASM_ON);
+ return 0;
+}
+
+void mt8365_afe_set_i2s_out_enable(struct mtk_base_afe *afe, bool enable)
+{
+ int i;
+ unsigned long flags;
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_data = NULL;
+
+ for (i = 0; i < DAI_I2S_NUM; i++) {
+ if (mt8365_i2s_priv[i].adda_link)
+ i2s_data = afe_priv->dai_priv[mt8365_i2s_priv[i].id];
+ }
+
+ if (!i2s_data)
+ return;
+
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+
+ if (enable) {
+ i2s_data->i2s_out_on_ref_cnt++;
+ if (i2s_data->i2s_out_on_ref_cnt == 1)
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1,
+ 0x1, enable);
+ } else {
+ i2s_data->i2s_out_on_ref_cnt--;
+ if (i2s_data->i2s_out_on_ref_cnt == 0)
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1,
+ 0x1, enable);
+ else if (i2s_data->i2s_out_on_ref_cnt < 0)
+ i2s_data->i2s_out_on_ref_cnt = 0;
+ }
+
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+}
+
+static void mt8365_dai_set_enable(struct mtk_base_afe *afe,
+ struct mtk_afe_i2s_priv *i2s_data,
+ bool is_input, bool enable)
+{
+ unsigned int reg_off;
+
+ if (is_input) {
+ reg_off = i2s_data->reg_off_in;
+ } else {
+ if (i2s_data->adda_link) {
+ mt8365_afe_set_i2s_out_enable(afe, enable);
+ return;
+ }
+ reg_off = i2s_data->reg_off_out;
+ }
+ regmap_update_bits(afe->regmap, reg_off,
+ 0x1, enable);
+}
+
+static int mt8365_dai_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_data = afe_priv->dai_priv[dai->id];
+ struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+ bool i2s_in_slave =
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE) &&
+ ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) ==
+ SND_SOC_DAIFMT_CBP_CFP);
+
+ mt8365_afe_enable_main_clk(afe);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk_prepare_enable(afe_priv->clocks[i2s_data->clk_id_out]);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && !i2s_in_slave)
+ clk_prepare_enable(afe_priv->clocks[i2s_data->clk_id_in]);
+
+ if (i2s_in_slave)
+ mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_I2S_IN);
+
+ return 0;
+}
+
+static void mt8365_dai_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_data = afe_priv->dai_priv[dai->id];
+ struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+ bool reset_i2s_out_change = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ bool reset_i2s_in_change = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+ bool i2s_in_slave =
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE) &&
+ ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) ==
+ SND_SOC_DAIFMT_CBP_CFP);
+
+ if (be->prepared[substream->stream]) {
+ if (reset_i2s_out_change)
+ mt8365_dai_set_enable(afe, i2s_data, false, false);
+
+ if (reset_i2s_in_change)
+ mt8365_dai_set_enable(afe, i2s_data, true, false);
+
+ if (substream->runtime->rate % 8000)
+ mt8365_afe_disable_apll_associated_cfg(afe, MT8365_AFE_APLL1);
+ else
+ mt8365_afe_disable_apll_associated_cfg(afe, MT8365_AFE_APLL2);
+
+ if (reset_i2s_out_change)
+ be->prepared[SNDRV_PCM_STREAM_PLAYBACK] = false;
+
+ if (reset_i2s_in_change)
+ be->prepared[SNDRV_PCM_STREAM_CAPTURE] = false;
+ }
+
+ if (reset_i2s_out_change)
+ mt8365_afe_disable_clk(afe,
+ afe_priv->clocks[i2s_data->clk_id_out]);
+
+ if (reset_i2s_in_change && !i2s_in_slave)
+ mt8365_afe_disable_clk(afe,
+ afe_priv->clocks[i2s_data->clk_id_in]);
+
+ if (i2s_in_slave)
+ mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_I2S_IN);
+
+ mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_dai_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_i2s_priv *i2s_data = afe_priv->dai_priv[dai->id];
+ struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+ bool apply_i2s_out_change = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ bool apply_i2s_in_change = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+ unsigned int rate = substream->runtime->rate;
+ int bit_width = snd_pcm_format_width(substream->runtime->format);
+ int ret;
+
+ if (be->prepared[substream->stream]) {
+ dev_info(afe->dev, "%s '%s' prepared already\n",
+ __func__, snd_pcm_stream_str(substream));
+ return 0;
+ }
+
+ if (apply_i2s_out_change) {
+ ret = mt8365_dai_set_config(afe, i2s_data, false, rate, bit_width);
+ if (ret)
+ return ret;
+ }
+
+ if (apply_i2s_in_change) {
+ if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK)
+ == SND_SOC_DAIFMT_CBP_CFP) {
+ ret = mt8365_afe_set_2nd_i2s_asrc(afe, 32000, rate,
+ (unsigned int)bit_width,
+ 0, 0, 1);
+ if (ret < 0)
+ return ret;
+ }
+ ret = mt8365_dai_set_config(afe, i2s_data, true, rate, bit_width);
+ if (ret)
+ return ret;
+ }
+
+ if (rate % 8000)
+ mt8365_afe_enable_apll_associated_cfg(afe, MT8365_AFE_APLL1);
+ else
+ mt8365_afe_enable_apll_associated_cfg(afe, MT8365_AFE_APLL2);
+
+ if (apply_i2s_out_change) {
+ mt8365_afe_set_clk_parent(afe,
+ afe_priv->clocks[i2s_data->clk_id_out_m_sel],
+ ((rate % 8000) ?
+ afe_priv->clocks[MT8365_CLK_AUD1] :
+ afe_priv->clocks[MT8365_CLK_AUD2]));
+
+ mt8365_afe_set_clk_rate(afe,
+ afe_priv->clocks[i2s_data->clk_id_out],
+ rate * i2s_data->clk_out_mult);
+
+ mt8365_dai_set_enable(afe, i2s_data, false, true);
+ be->prepared[SNDRV_PCM_STREAM_PLAYBACK] = true;
+ }
+
+ if (apply_i2s_in_change) {
+ mt8365_afe_set_clk_parent(afe,
+ afe_priv->clocks[i2s_data->clk_id_in_m_sel],
+ ((rate % 8000) ?
+ afe_priv->clocks[MT8365_CLK_AUD1] :
+ afe_priv->clocks[MT8365_CLK_AUD2]));
+
+ mt8365_afe_set_clk_rate(afe,
+ afe_priv->clocks[i2s_data->clk_id_in],
+ rate * i2s_data->clk_in_mult);
+
+ mt8365_dai_set_enable(afe, i2s_data, true, true);
+
+ if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK)
+ == SND_SOC_DAIFMT_CBP_CFP)
+ mt8365_afe_set_2nd_i2s_asrc_enable(afe, true);
+
+ be->prepared[SNDRV_PCM_STREAM_CAPTURE] = true;
+ }
+ return 0;
+}
+
+static int mt8365_afe_2nd_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int width_val = params_width(params) > 16 ?
+ (AFE_CONN_24BIT_O00 | AFE_CONN_24BIT_O01) : 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
+ AFE_CONN_24BIT_O00 | AFE_CONN_24BIT_O01, width_val);
+
+ return 0;
+}
+
+static int mt8365_afe_2nd_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+
+ be->fmt_mode = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ be->fmt_mode |= SND_SOC_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ be->fmt_mode |= SND_SOC_DAIFMT_LEFT_J;
+ break;
+ default:
+ dev_err(afe->dev, "invalid audio format for 2nd i2s!\n");
+ return -EINVAL;
+ }
+
+ if (((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) &&
+ ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_IF) &&
+ ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_IB_NF) &&
+ ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_IB_IF)) {
+ dev_err(afe->dev, "invalid audio format for 2nd i2s!\n");
+ return -EINVAL;
+ }
+
+ be->fmt_mode |= (fmt & SND_SOC_DAIFMT_INV_MASK);
+
+ if (((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP))
+ be->fmt_mode |= (fmt & SND_SOC_DAIFMT_MASTER_MASK);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt8365_afe_i2s_ops = {
+ .startup = mt8365_dai_i2s_startup,
+ .shutdown = mt8365_dai_i2s_shutdown,
+ .prepare = mt8365_dai_i2s_prepare,
+};
+
+static const struct snd_soc_dai_ops mt8365_afe_2nd_i2s_ops = {
+ .startup = mt8365_dai_i2s_startup,
+ .shutdown = mt8365_dai_i2s_shutdown,
+ .hw_params = mt8365_afe_2nd_i2s_hw_params,
+ .prepare = mt8365_dai_i2s_prepare,
+ .set_fmt = mt8365_afe_2nd_i2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
+ {
+ .name = "I2S",
+ .id = MT8365_AFE_IO_I2S,
+ .playback = {
+ .stream_name = "I2S Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "I2S Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_i2s_ops,
+ }, {
+ .name = "2ND I2S",
+ .id = MT8365_AFE_IO_2ND_I2S,
+ .playback = {
+ .stream_name = "2ND I2S Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "2ND I2S Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_afe_2nd_i2s_ops,
+ }
+};
+
+static const char * const fmi2sin_text[] = {
+ "OPEN", "FM_2ND_I2S_IN"
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(fmi2sin_enum, fmi2sin_text);
+
+static const struct snd_kcontrol_new fmi2sin_mux =
+ SOC_DAPM_ENUM("FM 2ND I2S Source", fmi2sin_enum);
+
+static const struct snd_kcontrol_new i2s_o03_o04_enable_ctl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
+ SND_SOC_DAPM_SWITCH("I2S O03_O04", SND_SOC_NOPM, 0, 0,
+ &i2s_o03_o04_enable_ctl),
+ SND_SOC_DAPM_MUX("FM 2ND I2S Mux", SND_SOC_NOPM, 0, 0, &fmi2sin_mux),
+ SND_SOC_DAPM_INPUT("2ND I2S In"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
+ {"I2S O03_O04", "Switch", "O03"},
+ {"I2S O03_O04", "Switch", "O04"},
+ {"I2S Playback", NULL, "I2S O03_O04"},
+ {"2ND I2S Playback", NULL, "O00"},
+ {"2ND I2S Playback", NULL, "O01"},
+ {"2ND I2S Capture", NULL, "2ND I2S In"},
+ {"FM 2ND I2S Mux", "FM_2ND_I2S_IN", "2ND I2S Capture"},
+};
+
+static int mt8365_dai_i2s_set_priv(struct mtk_base_afe *afe)
+{
+ int i, ret;
+
+ for (i = 0; i < DAI_I2S_NUM; i++) {
+ ret = mt8365_dai_set_priv(afe, mt8365_i2s_priv[i].id,
+ sizeof(mt8365_i2s_priv[i]),
+ &mt8365_i2s_priv[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int mt8365_dai_i2s_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_i2s_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
+ dai->dapm_widgets = mtk_dai_i2s_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
+ dai->dapm_routes = mtk_dai_i2s_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
+
+ /* set all dai i2s private data */
+ return mt8365_dai_i2s_set_priv(afe);
+}
diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c b/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c
new file mode 100644
index 000000000000..0ec114a566ad
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 ALSA SoC Audio DAI PCM Control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8365-afe-clk.h"
+#include "mt8365-afe-common.h"
+
+struct mt8365_pcm_intf_data {
+ bool slave_mode;
+ bool lrck_inv;
+ bool bck_inv;
+ unsigned int format;
+};
+
+/* DAI Drivers */
+
+static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1,
+ PCM_INTF_CON1_EN, PCM_INTF_CON1_EN);
+}
+
+static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1,
+ PCM_INTF_CON1_EN, 0x0);
+}
+
+static int mt8365_dai_configure_pcm1(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
+ bool slave_mode = pcm_priv->slave_mode;
+ bool lrck_inv = pcm_priv->lrck_inv;
+ bool bck_inv = pcm_priv->bck_inv;
+ unsigned int fmt = pcm_priv->format;
+ unsigned int bit_width = dai->symmetric_sample_bits;
+ unsigned int val = 0;
+
+ if (!slave_mode) {
+ val |= PCM_INTF_CON1_MASTER_MODE |
+ PCM_INTF_CON1_BYPASS_ASRC;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_OUT_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_OUT_INV;
+ } else {
+ val |= PCM_INTF_CON1_SLAVE_MODE;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_IN_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_IN_INV;
+
+ /* TODO: add asrc setting */
+ }
+
+ val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt);
+
+ if (fmt == MT8365_PCM_FORMAT_PCMA ||
+ fmt == MT8365_PCM_FORMAT_PCMB)
+ val |= PCM_INTF_CON1_SYNC_LEN(1);
+ else
+ val |= PCM_INTF_CON1_SYNC_LEN(bit_width);
+
+ switch (substream->runtime->rate) {
+ case 48000:
+ val |= PCM_INTF_CON1_FS_48K;
+ break;
+ case 32000:
+ val |= PCM_INTF_CON1_FS_32K;
+ break;
+ case 16000:
+ val |= PCM_INTF_CON1_FS_16K;
+ break;
+ case 8000:
+ val |= PCM_INTF_CON1_FS_8K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (bit_width > 16)
+ val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK;
+ else
+ val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK;
+
+ val |= PCM_INTF_CON1_EXT_MODEM;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1,
+ PCM_INTF_CON1_CONFIG_MASK, val);
+
+ return 0;
+}
+
+static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ if (snd_soc_dai_active(dai))
+ return 0;
+
+ mt8365_afe_enable_main_clk(afe);
+
+ return 0;
+}
+
+static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ if (snd_soc_dai_active(dai))
+ return;
+
+ mt8365_dai_disable_pcm1(afe);
+ mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ if ((snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) +
+ snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) > 1) {
+ dev_info(afe->dev, "%s '%s' active(%u-%u) already\n",
+ __func__, snd_pcm_stream_str(substream),
+ snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK),
+ snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE));
+ return 0;
+ }
+
+ ret = mt8365_dai_configure_pcm1(substream, dai);
+ if (ret)
+ return ret;
+
+ mt8365_dai_enable_pcm1(afe);
+
+ return 0;
+}
+
+static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcm_priv->format = MT8365_PCM_FORMAT_I2S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ pcm_priv->bck_inv = false;
+ pcm_priv->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ pcm_priv->bck_inv = false;
+ pcm_priv->lrck_inv = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ pcm_priv->bck_inv = true;
+ pcm_priv->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ pcm_priv->bck_inv = true;
+ pcm_priv->lrck_inv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ pcm_priv->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ pcm_priv->slave_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = {
+ .startup = mt8365_dai_pcm1_startup,
+ .shutdown = mt8365_dai_pcm1_shutdown,
+ .prepare = mt8365_dai_pcm1_prepare,
+ .set_fmt = mt8365_dai_pcm1_set_fmt,
+};
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM1",
+ .id = MT8365_AFE_IO_PCM1,
+ .playback = {
+ .stream_name = "PCM1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "PCM1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mt8365_dai_pcm1_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ }
+};
+
+/* DAI widget */
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("PCM1 Out"),
+ SND_SOC_DAPM_INPUT("PCM1 In"),
+};
+
+/* DAI route */
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"PCM1 Playback", NULL, "O07"},
+ {"PCM1 Playback", NULL, "O08"},
+ {"PCM1 Out", NULL, "PCM1 Playback"},
+
+ {"I09", NULL, "PCM1 Capture"},
+ {"I22", NULL, "PCM1 Capture"},
+ {"PCM1 Capture", NULL, "PCM1 In"},
+};
+
+static int init_pcmif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8365_afe_private *afe_priv = afe->platform_priv;
+ struct mt8365_pcm_intf_data *pcmif_priv;
+
+ pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data),
+ GFP_KERNEL);
+ if (!pcmif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv;
+ return 0;
+}
+
+int mt8365_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+ return init_pcmif_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c
new file mode 100644
index 000000000000..a998fba82bfe
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek MT8365 Sound Card driver
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "mt8365-afe-common.h"
+#include "../common/mtk-soc-card.h"
+#include "../common/mtk-soundcard-driver.h"
+
+enum pinctrl_pin_state {
+ PIN_STATE_DEFAULT,
+ PIN_STATE_DMIC,
+ PIN_STATE_MISO_OFF,
+ PIN_STATE_MISO_ON,
+ PIN_STATE_MOSI_OFF,
+ PIN_STATE_MOSI_ON,
+ PIN_STATE_MAX
+};
+
+static const char * const mt8365_mt6357_pin_str[PIN_STATE_MAX] = {
+ "default",
+ "dmic",
+ "miso_off",
+ "miso_on",
+ "mosi_off",
+ "mosi_on",
+};
+
+struct mt8365_mt6357_priv {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pin_states[PIN_STATE_MAX];
+};
+
+enum {
+ /* FE */
+ DAI_LINK_DL1_PLAYBACK = 0,
+ DAI_LINK_DL2_PLAYBACK,
+ DAI_LINK_AWB_CAPTURE,
+ DAI_LINK_VUL_CAPTURE,
+ /* BE */
+ DAI_LINK_2ND_I2S_INTF,
+ DAI_LINK_DMIC,
+ DAI_LINK_INT_ADDA,
+ DAI_LINK_NUM
+};
+
+static const struct snd_soc_dapm_widget mt8365_mt6357_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HDMI Out"),
+};
+
+static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = {
+ {"HDMI Out", NULL, "2ND I2S Playback"},
+ {"DMIC In", NULL, "MICBIAS0"},
+};
+
+static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (IS_ERR(priv->pin_states[PIN_STATE_MOSI_ON]))
+ return ret;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_MOSI_ON]);
+ if (ret)
+ dev_err(rtd->card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (IS_ERR(priv->pin_states[PIN_STATE_MISO_ON]))
+ return ret;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_MISO_ON]);
+ if (ret)
+ dev_err(rtd->card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+ }
+
+ return 0;
+}
+
+static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (IS_ERR(priv->pin_states[PIN_STATE_MOSI_OFF]))
+ return;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_MOSI_OFF]);
+ if (ret)
+ dev_err(rtd->card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (IS_ERR(priv->pin_states[PIN_STATE_MISO_OFF]))
+ return;
+
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[PIN_STATE_MISO_OFF]);
+ if (ret)
+ dev_err(rtd->card->dev, "%s failed to select state %d\n",
+ __func__, ret);
+ }
+}
+
+static const struct snd_soc_ops mt8365_mt6357_int_adda_ops = {
+ .startup = mt8365_mt6357_int_adda_startup,
+ .shutdown = mt8365_mt6357_int_adda_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(awb_capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("AWB")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(vul,
+ DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(i2s3,
+ DAILINK_COMP_ARRAY(COMP_CPU("2ND I2S")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("DMIC")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(primary_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("INT ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6357-sound", "mt6357-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8365_mt6357_dais[] = {
+ /* Front End DAI links */
+ [DAI_LINK_DL1_PLAYBACK] = {
+ .name = "DL1_FE",
+ .stream_name = "MultiMedia1_PLayback",
+ .id = DAI_LINK_DL1_PLAYBACK,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_rate = 1,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ [DAI_LINK_DL2_PLAYBACK] = {
+ .name = "DL2_FE",
+ .stream_name = "MultiMedia2_PLayback",
+ .id = DAI_LINK_DL2_PLAYBACK,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dynamic = 1,
+ .playback_only = 1,
+ .dpcm_merged_rate = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ [DAI_LINK_AWB_CAPTURE] = {
+ .name = "AWB_FE",
+ .stream_name = "DL1_AWB_Record",
+ .id = DAI_LINK_AWB_CAPTURE,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_rate = 1,
+ SND_SOC_DAILINK_REG(awb_capture),
+ },
+ [DAI_LINK_VUL_CAPTURE] = {
+ .name = "VUL_FE",
+ .stream_name = "MultiMedia1_Capture",
+ .id = DAI_LINK_VUL_CAPTURE,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dynamic = 1,
+ .capture_only = 1,
+ .dpcm_merged_rate = 1,
+ SND_SOC_DAILINK_REG(vul),
+ },
+ /* Back End DAI links */
+ [DAI_LINK_2ND_I2S_INTF] = {
+ .name = "I2S_OUT_BE",
+ .no_pcm = 1,
+ .id = DAI_LINK_2ND_I2S_INTF,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(i2s3),
+ },
+ [DAI_LINK_DMIC] = {
+ .name = "DMIC_BE",
+ .no_pcm = 1,
+ .id = DAI_LINK_DMIC,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(dmic),
+ },
+ [DAI_LINK_INT_ADDA] = {
+ .name = "MTK_Codec",
+ .no_pcm = 1,
+ .id = DAI_LINK_INT_ADDA,
+ .ops = &mt8365_mt6357_int_adda_ops,
+ SND_SOC_DAILINK_REG(primary_codec),
+ },
+};
+
+static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card)
+{
+ struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card);
+ int ret, i;
+
+ priv->pinctrl = devm_pinctrl_get(card->dev);
+ if (IS_ERR(priv->pinctrl)) {
+ ret = PTR_ERR(priv->pinctrl);
+ return dev_err_probe(card->dev, ret,
+ "Failed to get pinctrl\n");
+ }
+
+ for (i = PIN_STATE_DEFAULT ; i < PIN_STATE_MAX ; i++) {
+ priv->pin_states[i] = pinctrl_lookup_state(priv->pinctrl,
+ mt8365_mt6357_pin_str[i]);
+ if (IS_ERR(priv->pin_states[i])) {
+ dev_info(card->dev, "No pin state for %s\n",
+ mt8365_mt6357_pin_str[i]);
+ } else {
+ ret = pinctrl_select_state(priv->pinctrl,
+ priv->pin_states[i]);
+ if (ret) {
+ dev_err_probe(card->dev, ret,
+ "Failed to select pin state %s\n",
+ mt8365_mt6357_pin_str[i]);
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static struct snd_soc_card mt8365_mt6357_soc_card = {
+ .name = "mt8365-evk",
+ .owner = THIS_MODULE,
+ .dai_link = mt8365_mt6357_dais,
+ .num_links = ARRAY_SIZE(mt8365_mt6357_dais),
+ .dapm_widgets = mt8365_mt6357_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8365_mt6357_widgets),
+ .dapm_routes = mt8365_mt6357_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8365_mt6357_routes),
+};
+
+static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_card *card = card_data->card;
+ struct device *dev = card->dev;
+ struct mt8365_mt6357_priv *mach_priv;
+ int ret;
+
+ card->dev = dev;
+ ret = parse_dai_link_info(card);
+ if (ret)
+ goto err;
+
+ mach_priv = devm_kzalloc(dev, sizeof(*mach_priv),
+ GFP_KERNEL);
+ if (!mach_priv)
+ return -ENOMEM;
+ soc_card_data->mach_priv = mach_priv;
+ snd_soc_card_set_drvdata(card, soc_card_data);
+ mt8365_mt6357_gpio_probe(card);
+ return 0;
+
+err:
+ clean_card_reference(card);
+ return ret;
+}
+
+static const struct mtk_soundcard_pdata mt8365_mt6357_card = {
+ .card_name = "mt8365-mt6357",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8365_mt6357_soc_card,
+ },
+ .soc_probe = mt8365_mt6357_dev_probe
+};
+
+static const struct of_device_id mt8365_mt6357_dt_match[] = {
+ { .compatible = "mediatek,mt8365-mt6357", .data = &mt8365_mt6357_card },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mt8365_mt6357_dt_match);
+
+static struct platform_driver mt8365_mt6357_driver = {
+ .driver = {
+ .name = "mt8365_mt6357",
+ .of_match_table = mt8365_mt6357_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+
+module_platform_driver(mt8365_mt6357_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8365 EVK SoC machine driver");
+MODULE_AUTHOR("Nicolas Belin <nbelin@baylibre.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: mt8365_mt6357");
diff --git a/sound/soc/mediatek/mt8365/mt8365-reg.h b/sound/soc/mediatek/mt8365/mt8365-reg.h
new file mode 100644
index 000000000000..4ebbb94ff02e
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-reg.h
@@ -0,0 +1,993 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * MediaTek 8365 audio driver reg definition
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng@mediatek.com>
+ * Alexandre Mergnat <amergnat@baylibre.com>
+ */
+
+#ifndef _MT8365_REG_H_
+#define _MT8365_REG_H_
+
+#include <linux/bitfield.h>
+
+#define AUDIO_TOP_CON0 (0x0000)
+#define AUDIO_TOP_CON1 (0x0004)
+#define AUDIO_TOP_CON2 (0x0008)
+#define AUDIO_TOP_CON3 (0x000c)
+
+#define AFE_DAC_CON0 (0x0010)
+#define AFE_DAC_CON1 (0x0014)
+#define AFE_I2S_CON (0x0018)
+#define AFE_CONN0 (0x0020)
+#define AFE_CONN1 (0x0024)
+#define AFE_CONN2 (0x0028)
+#define AFE_CONN3 (0x002c)
+#define AFE_CONN4 (0x0030)
+#define AFE_I2S_CON1 (0x0034)
+#define AFE_I2S_CON2 (0x0038)
+#define AFE_MRGIF_CON (0x003c)
+#define AFE_DL1_BASE (0x0040)
+#define AFE_DL1_CUR (0x0044)
+#define AFE_DL1_END (0x0048)
+#define AFE_I2S_CON3 (0x004c)
+#define AFE_DL2_BASE (0x0050)
+#define AFE_DL2_CUR (0x0054)
+#define AFE_DL2_END (0x0058)
+#define AFE_CONN5 (0x005c)
+#define AFE_AWB_BASE (0x0070)
+#define AFE_AWB_END (0x0078)
+#define AFE_AWB_CUR (0x007c)
+#define AFE_VUL_BASE (0x0080)
+#define AFE_VUL_END (0x0088)
+#define AFE_VUL_CUR (0x008c)
+#define AFE_CONN6 (0x00bc)
+#define AFE_MEMIF_MSB (0x00cc)
+#define AFE_MEMIF_MON0 (0x00d0)
+#define AFE_MEMIF_MON1 (0x00d4)
+#define AFE_MEMIF_MON2 (0x00d8)
+#define AFE_MEMIF_MON3 (0x00dc)
+#define AFE_MEMIF_MON4 (0x00e0)
+#define AFE_MEMIF_MON5 (0x00e4)
+#define AFE_MEMIF_MON6 (0x00e8)
+#define AFE_MEMIF_MON7 (0x00ec)
+#define AFE_MEMIF_MON8 (0x00f0)
+#define AFE_MEMIF_MON9 (0x00f4)
+#define AFE_MEMIF_MON10 (0x00f8)
+#define AFE_MEMIF_MON11 (0x00fc)
+#define AFE_ADDA_DL_SRC2_CON0 (0x0108)
+#define AFE_ADDA_DL_SRC2_CON1 (0x010c)
+#define AFE_ADDA_UL_SRC_CON0 (0x0114)
+#define AFE_ADDA_UL_SRC_CON1 (0x0118)
+#define AFE_ADDA_TOP_CON0 (0x0120)
+#define AFE_ADDA_UL_DL_CON0 (0x0124)
+#define AFE_ADDA_SRC_DEBUG (0x012c)
+#define AFE_ADDA_SRC_DEBUG_MON0 (0x0130)
+#define AFE_ADDA_SRC_DEBUG_MON1 (0x0134)
+#define AFE_ADDA_UL_SRC_MON0 (0x0148)
+#define AFE_ADDA_UL_SRC_MON1 (0x014c)
+#define AFE_SRAM_BOUND (0x0170)
+#define AFE_SECURE_CON (0x0174)
+#define AFE_SECURE_CONN0 (0x0178)
+#define AFE_SIDETONE_DEBUG (0x01d0)
+#define AFE_SIDETONE_MON (0x01d4)
+#define AFE_SIDETONE_CON0 (0x01e0)
+#define AFE_SIDETONE_COEFF (0x01e4)
+#define AFE_SIDETONE_CON1 (0x01e8)
+#define AFE_SIDETONE_GAIN (0x01ec)
+#define AFE_SGEN_CON0 (0x01f0)
+#define AFE_SINEGEN_CON_TDM (0x01f8)
+#define AFE_SINEGEN_CON_TDM_IN (0x01fc)
+#define AFE_TOP_CON0 (0x0200)
+#define AFE_BUS_CFG (0x0240)
+#define AFE_BUS_MON0 (0x0244)
+#define AFE_ADDA_PREDIS_CON0 (0x0260)
+#define AFE_ADDA_PREDIS_CON1 (0x0264)
+#define AFE_CONN_MON0 (0x0280)
+#define AFE_CONN_MON1 (0x0284)
+#define AFE_CONN_MON2 (0x0288)
+#define AFE_CONN_MON3 (0x028c)
+#define AFE_ADDA_IIR_COEF_02_01 (0x0290)
+#define AFE_ADDA_IIR_COEF_04_03 (0x0294)
+#define AFE_ADDA_IIR_COEF_06_05 (0x0298)
+#define AFE_ADDA_IIR_COEF_08_07 (0x029c)
+#define AFE_ADDA_IIR_COEF_10_09 (0x02a0)
+#define AFE_VUL_D2_BASE (0x0350)
+#define AFE_VUL_D2_END (0x0358)
+#define AFE_VUL_D2_CUR (0x035c)
+#define AFE_HDMI_OUT_CON0 (0x0370)
+#define AFE_HDMI_OUT_BASE (0x0374)
+#define AFE_HDMI_OUT_CUR (0x0378)
+#define AFE_HDMI_OUT_END (0x037c)
+#define AFE_SPDIF_OUT_CON0 (0x0380)
+#define AFE_SPDIF_OUT_BASE (0x0384)
+#define AFE_SPDIF_OUT_CUR (0x0388)
+#define AFE_SPDIF_OUT_END (0x038c)
+#define AFE_HDMI_CONN0 (0x0390)
+#define AFE_HDMI_CONN1 (0x0398)
+#define AFE_CONN_TDMIN_CON (0x039c)
+#define AFE_IRQ_MCU_CON (0x03a0)
+#define AFE_IRQ_MCU_STATUS (0x03a4)
+#define AFE_IRQ_MCU_CLR (0x03a8)
+#define AFE_IRQ_MCU_CNT1 (0x03ac)
+#define AFE_IRQ_MCU_CNT2 (0x03b0)
+#define AFE_IRQ_MCU_EN (0x03b4)
+#define AFE_IRQ_MCU_MON2 (0x03b8)
+#define AFE_IRQ_MCU_CNT5 (0x03bc)
+#define AFE_IRQ1_MCU_CNT_MON (0x03c0)
+#define AFE_IRQ2_MCU_CNT_MON (0x03c4)
+#define AFE_IRQ1_MCU_EN_CNT_MON (0x03c8)
+#define AFE_IRQ5_MCU_CNT_MON (0x03cc)
+#define AFE_MEMIF_MINLEN (0x03d0)
+#define AFE_MEMIF_MAXLEN (0x03d4)
+#define AFE_MEMIF_PBUF_SIZE (0x03d8)
+#define AFE_IRQ_MCU_CNT7 (0x03dc)
+#define AFE_IRQ7_MCU_CNT_MON (0x03e0)
+#define AFE_MEMIF_PBUF2_SIZE (0x03ec)
+#define AFE_APLL_TUNER_CFG (0x03f0)
+#define AFE_APLL_TUNER_CFG1 (0x03f4)
+#define AFE_IRQ_MCU_CON2 (0x03f8)
+#define IRQ13_MCU_CNT (0x0408)
+#define IRQ13_MCU_CNT_MON (0x040c)
+#define AFE_GAIN1_CON0 (0x0410)
+#define AFE_GAIN1_CON1 (0x0414)
+#define AFE_GAIN1_CON2 (0x0418)
+#define AFE_GAIN1_CON3 (0x041c)
+#define AFE_GAIN2_CON0 (0x0428)
+#define AFE_GAIN2_CON1 (0x042c)
+#define AFE_GAIN2_CON2 (0x0430)
+#define AFE_GAIN2_CON3 (0x0434)
+#define AFE_GAIN2_CUR (0x043c)
+#define AFE_CONN11 (0x0448)
+#define AFE_CONN12 (0x044c)
+#define AFE_CONN13 (0x0450)
+#define AFE_CONN14 (0x0454)
+#define AFE_CONN15 (0x0458)
+#define AFE_CONN16 (0x045c)
+#define AFE_CONN7 (0x0460)
+#define AFE_CONN8 (0x0464)
+#define AFE_CONN9 (0x0468)
+#define AFE_CONN10 (0x046c)
+#define AFE_CONN21 (0x0470)
+#define AFE_CONN22 (0x0474)
+#define AFE_CONN23 (0x0478)
+#define AFE_CONN24 (0x047c)
+#define AFE_IEC_CFG (0x0480)
+#define AFE_IEC_NSNUM (0x0484)
+#define AFE_IEC_BURST_INFO (0x0488)
+#define AFE_IEC_BURST_LEN (0x048c)
+#define AFE_IEC_NSADR (0x0490)
+#define AFE_CONN_RS (0x0494)
+#define AFE_CONN_DI (0x0498)
+#define AFE_IEC_CHL_STAT0 (0x04a0)
+#define AFE_IEC_CHL_STAT1 (0x04a4)
+#define AFE_IEC_CHR_STAT0 (0x04a8)
+#define AFE_IEC_CHR_STAT1 (0x04ac)
+#define AFE_CONN25 (0x04b0)
+#define AFE_CONN26 (0x04b4)
+#define FPGA_CFG2 (0x04b8)
+#define FPGA_CFG3 (0x04bc)
+#define FPGA_CFG0 (0x04c0)
+#define FPGA_CFG1 (0x04c4)
+#define AFE_SRAM_DELSEL_CON0 (0x04f0)
+#define AFE_SRAM_DELSEL_CON1 (0x04f4)
+#define AFE_SRAM_DELSEL_CON2 (0x04f8)
+#define FPGA_CFG4 (0x04fc)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON0 (0x0500)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON1 (0x0504)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON2 (0x0508)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON3 (0x050c)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON4 (0x0510)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON5 (0x0514)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON6 (0x0518)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON7 (0x051c)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON8 (0x0520)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON9 (0x0524)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON10 (0x0528)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON12 (0x0530)
+#define AFE_TDM_GASRC4_ASRC_2CH_CON13 (0x0534)
+#define PCM_INTF_CON2 (0x0538)
+#define PCM2_INTF_CON (0x053c)
+#define AFE_APB_MON (0x0540)
+#define AFE_CONN34 (0x0544)
+#define AFE_TDM_CON1 (0x0548)
+#define AFE_TDM_CON2 (0x054c)
+#define PCM_INTF_CON1 (0x0550)
+#define AFE_SECURE_MASK_CONN47_1 (0x0554)
+#define AFE_SECURE_MASK_CONN48_1 (0x0558)
+#define AFE_SECURE_MASK_CONN49_1 (0x055c)
+#define AFE_SECURE_MASK_CONN50_1 (0x0560)
+#define AFE_SECURE_MASK_CONN51_1 (0x0564)
+#define AFE_SECURE_MASK_CONN52_1 (0x0568)
+#define AFE_SECURE_MASK_CONN53_1 (0x056c)
+#define AFE_SE_SECURE_CON (0x0570)
+#define AFE_TDM_IN_CON1 (0x0588)
+#define AFE_TDM_IN_CON2 (0x058c)
+#define AFE_TDM_IN_MON1 (0x0590)
+#define AFE_TDM_IN_MON2 (0x0594)
+#define AFE_TDM_IN_MON3 (0x0598)
+#define AFE_DMIC0_UL_SRC_CON0 (0x05b4)
+#define AFE_DMIC0_UL_SRC_CON1 (0x05b8)
+#define AFE_DMIC0_SRC_DEBUG (0x05bc)
+#define AFE_DMIC0_SRC_DEBUG_MON0 (0x05c0)
+#define AFE_DMIC0_UL_SRC_MON0 (0x05c8)
+#define AFE_DMIC0_UL_SRC_MON1 (0x05cc)
+#define AFE_DMIC0_IIR_COEF_02_01 (0x05d0)
+#define AFE_DMIC0_IIR_COEF_04_03 (0x05d4)
+#define AFE_DMIC0_IIR_COEF_06_05 (0x05d8)
+#define AFE_DMIC0_IIR_COEF_08_07 (0x05dc)
+#define AFE_DMIC0_IIR_COEF_10_09 (0x05e0)
+#define AFE_DMIC1_UL_SRC_CON0 (0x0620)
+#define AFE_DMIC1_UL_SRC_CON1 (0x0624)
+#define AFE_DMIC1_SRC_DEBUG (0x0628)
+#define AFE_DMIC1_SRC_DEBUG_MON0 (0x062c)
+#define AFE_DMIC1_UL_SRC_MON0 (0x0634)
+#define AFE_DMIC1_UL_SRC_MON1 (0x0638)
+#define AFE_DMIC1_IIR_COEF_02_01 (0x063c)
+#define AFE_DMIC1_IIR_COEF_04_03 (0x0640)
+#define AFE_DMIC1_IIR_COEF_06_05 (0x0644)
+#define AFE_DMIC1_IIR_COEF_08_07 (0x0648)
+#define AFE_DMIC1_IIR_COEF_10_09 (0x064c)
+#define AFE_SECURE_MASK_CONN39_1 (0x068c)
+#define AFE_SECURE_MASK_CONN40_1 (0x0690)
+#define AFE_SECURE_MASK_CONN41_1 (0x0694)
+#define AFE_SECURE_MASK_CONN42_1 (0x0698)
+#define AFE_SECURE_MASK_CONN43_1 (0x069c)
+#define AFE_SECURE_MASK_CONN44_1 (0x06a0)
+#define AFE_SECURE_MASK_CONN45_1 (0x06a4)
+#define AFE_SECURE_MASK_CONN46_1 (0x06a8)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON0 (0x06c0)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON1 (0x06c4)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON2 (0x06c8)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON3 (0x06cc)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON4 (0x06d0)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON5 (0x06d4)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON6 (0x06d8)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON7 (0x06dc)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON8 (0x06e0)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON9 (0x06e4)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON10 (0x06e8)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON12 (0x06f0)
+#define AFE_TDM_GASRC1_ASRC_2CH_CON13 (0x06f4)
+#define AFE_TDM_ASRC_CON0 (0x06f8)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON0 (0x0700)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON1 (0x0704)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON2 (0x0708)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON3 (0x070c)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON4 (0x0710)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON5 (0x0714)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON6 (0x0718)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON7 (0x071c)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON8 (0x0720)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON9 (0x0724)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON10 (0x0728)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON12 (0x0730)
+#define AFE_TDM_GASRC2_ASRC_2CH_CON13 (0x0734)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON0 (0x0740)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON1 (0x0744)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON2 (0x0748)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON3 (0x074c)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON4 (0x0750)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON5 (0x0754)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON6 (0x0758)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON7 (0x075c)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON8 (0x0760)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON9 (0x0764)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON10 (0x0768)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON12 (0x0770)
+#define AFE_TDM_GASRC3_ASRC_2CH_CON13 (0x0774)
+#define AFE_DMIC2_UL_SRC_CON0 (0x0780)
+#define AFE_DMIC2_UL_SRC_CON1 (0x0784)
+#define AFE_DMIC2_SRC_DEBUG (0x0788)
+#define AFE_DMIC2_SRC_DEBUG_MON0 (0x078c)
+#define AFE_DMIC2_UL_SRC_MON0 (0x0794)
+#define AFE_DMIC2_UL_SRC_MON1 (0x0798)
+#define AFE_DMIC2_IIR_COEF_02_01 (0x079c)
+#define AFE_DMIC2_IIR_COEF_04_03 (0x07a0)
+#define AFE_DMIC2_IIR_COEF_06_05 (0x07a4)
+#define AFE_DMIC2_IIR_COEF_08_07 (0x07a8)
+#define AFE_DMIC2_IIR_COEF_10_09 (0x07ac)
+#define AFE_DMIC3_UL_SRC_CON0 (0x07ec)
+#define AFE_DMIC3_UL_SRC_CON1 (0x07f0)
+#define AFE_DMIC3_SRC_DEBUG (0x07f4)
+#define AFE_DMIC3_SRC_DEBUG_MON0 (0x07f8)
+#define AFE_DMIC3_UL_SRC_MON0 (0x0800)
+#define AFE_DMIC3_UL_SRC_MON1 (0x0804)
+#define AFE_DMIC3_IIR_COEF_02_01 (0x0808)
+#define AFE_DMIC3_IIR_COEF_04_03 (0x080c)
+#define AFE_DMIC3_IIR_COEF_06_05 (0x0810)
+#define AFE_DMIC3_IIR_COEF_08_07 (0x0814)
+#define AFE_DMIC3_IIR_COEF_10_09 (0x0818)
+#define AFE_SECURE_MASK_CONN25_1 (0x0858)
+#define AFE_SECURE_MASK_CONN26_1 (0x085c)
+#define AFE_SECURE_MASK_CONN27_1 (0x0860)
+#define AFE_SECURE_MASK_CONN28_1 (0x0864)
+#define AFE_SECURE_MASK_CONN29_1 (0x0868)
+#define AFE_SECURE_MASK_CONN30_1 (0x086c)
+#define AFE_SECURE_MASK_CONN31_1 (0x0870)
+#define AFE_SECURE_MASK_CONN32_1 (0x0874)
+#define AFE_SECURE_MASK_CONN33_1 (0x0878)
+#define AFE_SECURE_MASK_CONN34_1 (0x087c)
+#define AFE_SECURE_MASK_CONN35_1 (0x0880)
+#define AFE_SECURE_MASK_CONN36_1 (0x0884)
+#define AFE_SECURE_MASK_CONN37_1 (0x0888)
+#define AFE_SECURE_MASK_CONN38_1 (0x088c)
+#define AFE_IRQ_MCU_SCP_EN (0x0890)
+#define AFE_IRQ_MCU_DSP_EN (0x0894)
+#define AFE_IRQ3_MCU_CNT_MON (0x0898)
+#define AFE_IRQ4_MCU_CNT_MON (0x089c)
+#define AFE_IRQ8_MCU_CNT_MON (0x08a0)
+#define AFE_IRQ_MCU_CNT3 (0x08a4)
+#define AFE_IRQ_MCU_CNT4 (0x08a8)
+#define AFE_IRQ_MCU_CNT8 (0x08ac)
+#define AFE_IRQ_MCU_CNT11 (0x08b0)
+#define AFE_IRQ_MCU_CNT12 (0x08b4)
+#define AFE_IRQ11_MCU_CNT_MON (0x08b8)
+#define AFE_IRQ12_MCU_CNT_MON (0x08bc)
+#define AFE_VUL3_BASE (0x08c0)
+#define AFE_VUL3_CUR (0x08c4)
+#define AFE_VUL3_END (0x08c8)
+#define AFE_VUL3_BASE_MSB (0x08d0)
+#define AFE_VUL3_END_MSB (0x08d4)
+#define AFE_IRQ10_MCU_CNT_MON (0x08d8)
+#define AFE_IRQ_MCU_CNT10 (0x08dc)
+#define AFE_IRQ_ACC1_CNT (0x08e0)
+#define AFE_IRQ_ACC2_CNT (0x08e4)
+#define AFE_IRQ_ACC1_CNT_MON1 (0x08e8)
+#define AFE_IRQ_ACC2_CNT_MON (0x08ec)
+#define AFE_TSF_CON (0x08f0)
+#define AFE_TSF_MON (0x08f4)
+#define AFE_IRQ_ACC1_CNT_MON2 (0x08f8)
+#define AFE_SPDIFIN_CFG0 (0x0900)
+#define AFE_SPDIFIN_CFG1 (0x0904)
+#define AFE_SPDIFIN_CHSTS1 (0x0908)
+#define AFE_SPDIFIN_CHSTS2 (0x090c)
+#define AFE_SPDIFIN_CHSTS3 (0x0910)
+#define AFE_SPDIFIN_CHSTS4 (0x0914)
+#define AFE_SPDIFIN_CHSTS5 (0x0918)
+#define AFE_SPDIFIN_CHSTS6 (0x091c)
+#define AFE_SPDIFIN_DEBUG1 (0x0920)
+#define AFE_SPDIFIN_DEBUG2 (0x0924)
+#define AFE_SPDIFIN_DEBUG3 (0x0928)
+#define AFE_SPDIFIN_DEBUG4 (0x092c)
+#define AFE_SPDIFIN_EC (0x0930)
+#define AFE_SPDIFIN_CKLOCK_CFG (0x0934)
+#define AFE_SPDIFIN_BR (0x093c)
+#define AFE_SPDIFIN_BR_DBG1 (0x0940)
+#define AFE_SPDIFIN_INT_EXT (0x0948)
+#define AFE_SPDIFIN_INT_EXT2 (0x094c)
+#define SPDIFIN_FREQ_INFO (0x0950)
+#define SPDIFIN_FREQ_INFO_2 (0x0954)
+#define SPDIFIN_FREQ_INFO_3 (0x0958)
+#define SPDIFIN_FREQ_STATUS (0x095c)
+#define SPDIFIN_USERCODE1 (0x0960)
+#define SPDIFIN_USERCODE2 (0x0964)
+#define SPDIFIN_USERCODE3 (0x0968)
+#define SPDIFIN_USERCODE4 (0x096c)
+#define SPDIFIN_USERCODE5 (0x0970)
+#define SPDIFIN_USERCODE6 (0x0974)
+#define SPDIFIN_USERCODE7 (0x0978)
+#define SPDIFIN_USERCODE8 (0x097c)
+#define SPDIFIN_USERCODE9 (0x0980)
+#define SPDIFIN_USERCODE10 (0x0984)
+#define SPDIFIN_USERCODE11 (0x0988)
+#define SPDIFIN_USERCODE12 (0x098c)
+#define SPDIFIN_MEMIF_CON0 (0x0990)
+#define SPDIFIN_BASE_ADR (0x0994)
+#define SPDIFIN_END_ADR (0x0998)
+#define SPDIFIN_APLL_TUNER_CFG (0x09a0)
+#define SPDIFIN_APLL_TUNER_CFG1 (0x09a4)
+#define SPDIFIN_APLL2_TUNER_CFG (0x09a8)
+#define SPDIFIN_APLL2_TUNER_CFG1 (0x09ac)
+#define SPDIFIN_TYPE_DET (0x09b0)
+#define MPHONE_MULTI_CON0 (0x09b4)
+#define SPDIFIN_CUR_ADR (0x09b8)
+#define AFE_SINEGEN_CON_SPDIFIN (0x09bc)
+#define AFE_HDMI_IN_2CH_CON0 (0x09c0)
+#define AFE_HDMI_IN_2CH_BASE (0x09c4)
+#define AFE_HDMI_IN_2CH_END (0x09c8)
+#define AFE_HDMI_IN_2CH_CUR (0x09cc)
+#define AFE_MEMIF_BUF_MON0 (0x09d0)
+#define AFE_MEMIF_BUF_MON1 (0x09d4)
+#define AFE_MEMIF_BUF_MON2 (0x09d8)
+#define AFE_MEMIF_BUF_MON3 (0x09dc)
+#define AFE_MEMIF_BUF_MON6 (0x09e8)
+#define AFE_MEMIF_BUF_MON7 (0x09ec)
+#define AFE_MEMIF_BUF_MON8 (0x09f0)
+#define AFE_MEMIF_BUF_MON10 (0x09f8)
+#define AFE_MEMIF_BUF_MON11 (0x09fc)
+#define SYSTOP_STC_CONFIG (0x0a00)
+#define AUDIO_STC_STATUS (0x0a04)
+#define SYSTOP_W_STC_H (0x0a08)
+#define SYSTOP_W_STC_L (0x0a0c)
+#define SYSTOP_R_STC_H (0x0a10)
+#define SYSTOP_R_STC_L (0x0a14)
+#define AUDIO_W_STC_H (0x0a18)
+#define AUDIO_W_STC_L (0x0a1c)
+#define AUDIO_R_STC_H (0x0a20)
+#define AUDIO_R_STC_L (0x0a24)
+#define SYSTOP_W_STC2_H (0x0a28)
+#define SYSTOP_W_STC2_L (0x0a2c)
+#define SYSTOP_R_STC2_H (0x0a30)
+#define SYSTOP_R_STC2_L (0x0a34)
+#define AUDIO_W_STC2_H (0x0a38)
+#define AUDIO_W_STC2_L (0x0a3c)
+#define AUDIO_R_STC2_H (0x0a40)
+#define AUDIO_R_STC2_L (0x0a44)
+
+#define AFE_CONN17 (0x0a48)
+#define AFE_CONN18 (0x0a4c)
+#define AFE_CONN19 (0x0a50)
+#define AFE_CONN20 (0x0a54)
+#define AFE_CONN27 (0x0a58)
+#define AFE_CONN28 (0x0a5c)
+#define AFE_CONN29 (0x0a60)
+#define AFE_CONN30 (0x0a64)
+#define AFE_CONN31 (0x0a68)
+#define AFE_CONN32 (0x0a6c)
+#define AFE_CONN33 (0x0a70)
+#define AFE_CONN35 (0x0a74)
+#define AFE_CONN36 (0x0a78)
+#define AFE_CONN37 (0x0a7c)
+#define AFE_CONN38 (0x0a80)
+#define AFE_CONN39 (0x0a84)
+#define AFE_CONN40 (0x0a88)
+#define AFE_CONN41 (0x0a8c)
+#define AFE_CONN42 (0x0a90)
+#define AFE_CONN44 (0x0a94)
+#define AFE_CONN45 (0x0a98)
+#define AFE_CONN46 (0x0a9c)
+#define AFE_CONN47 (0x0aa0)
+#define AFE_CONN_24BIT (0x0aa4)
+#define AFE_CONN0_1 (0x0aa8)
+#define AFE_CONN1_1 (0x0aac)
+#define AFE_CONN2_1 (0x0ab0)
+#define AFE_CONN3_1 (0x0ab4)
+#define AFE_CONN4_1 (0x0ab8)
+#define AFE_CONN5_1 (0x0abc)
+#define AFE_CONN6_1 (0x0ac0)
+#define AFE_CONN7_1 (0x0ac4)
+#define AFE_CONN8_1 (0x0ac8)
+#define AFE_CONN9_1 (0x0acc)
+#define AFE_CONN10_1 (0x0ad0)
+#define AFE_CONN11_1 (0x0ad4)
+#define AFE_CONN12_1 (0x0ad8)
+#define AFE_CONN13_1 (0x0adc)
+#define AFE_CONN14_1 (0x0ae0)
+#define AFE_CONN15_1 (0x0ae4)
+#define AFE_CONN16_1 (0x0ae8)
+#define AFE_CONN17_1 (0x0aec)
+#define AFE_CONN18_1 (0x0af0)
+#define AFE_CONN19_1 (0x0af4)
+#define AFE_CONN43 (0x0af8)
+#define AFE_CONN43_1 (0x0afc)
+#define AFE_CONN21_1 (0x0b00)
+#define AFE_CONN22_1 (0x0b04)
+#define AFE_CONN23_1 (0x0b08)
+#define AFE_CONN24_1 (0x0b0c)
+#define AFE_CONN25_1 (0x0b10)
+#define AFE_CONN26_1 (0x0b14)
+#define AFE_CONN27_1 (0x0b18)
+#define AFE_CONN28_1 (0x0b1c)
+#define AFE_CONN29_1 (0x0b20)
+#define AFE_CONN30_1 (0x0b24)
+#define AFE_CONN31_1 (0x0b28)
+#define AFE_CONN32_1 (0x0b2c)
+#define AFE_CONN33_1 (0x0b30)
+#define AFE_CONN34_1 (0x0b34)
+#define AFE_CONN35_1 (0x0b38)
+#define AFE_CONN36_1 (0x0b3c)
+#define AFE_CONN37_1 (0x0b40)
+#define AFE_CONN38_1 (0x0b44)
+#define AFE_CONN39_1 (0x0b48)
+#define AFE_CONN40_1 (0x0b4c)
+#define AFE_CONN41_1 (0x0b50)
+#define AFE_CONN42_1 (0x0b54)
+#define AFE_CONN44_1 (0x0b58)
+#define AFE_CONN45_1 (0x0b5c)
+#define AFE_CONN46_1 (0x0b60)
+#define AFE_CONN47_1 (0x0b64)
+#define AFE_CONN_RS_1 (0x0b68)
+#define AFE_CONN_DI_1 (0x0b6c)
+#define AFE_CONN_24BIT_1 (0x0b70)
+#define AFE_GAIN1_CUR (0x0b78)
+#define AFE_CONN20_1 (0x0b7c)
+#define AFE_DL1_BASE_MSB (0x0b80)
+#define AFE_DL1_END_MSB (0x0b84)
+#define AFE_DL2_BASE_MSB (0x0b88)
+#define AFE_DL2_END_MSB (0x0b8c)
+#define AFE_AWB_BASE_MSB (0x0b90)
+#define AFE_AWB_END_MSB (0x0b94)
+#define AFE_VUL_BASE_MSB (0x0ba0)
+#define AFE_VUL_END_MSB (0x0ba4)
+#define AFE_VUL_D2_BASE_MSB (0x0ba8)
+#define AFE_VUL_D2_END_MSB (0x0bac)
+#define AFE_HDMI_OUT_BASE_MSB (0x0bb8)
+#define AFE_HDMI_OUT_END_MSB (0x0bbc)
+#define AFE_HDMI_IN_2CH_BASE_MSB (0x0bc0)
+#define AFE_HDMI_IN_2CH_END_MSB (0x0bc4)
+#define AFE_SPDIF_OUT_BASE_MSB (0x0bc8)
+#define AFE_SPDIF_OUT_END_MSB (0x0bcc)
+#define SPDIFIN_BASE_MSB (0x0bd0)
+#define SPDIFIN_END_MSB (0x0bd4)
+#define AFE_DL1_CUR_MSB (0x0bd8)
+#define AFE_DL2_CUR_MSB (0x0bdc)
+#define AFE_AWB_CUR_MSB (0x0be8)
+#define AFE_VUL_CUR_MSB (0x0bf8)
+#define AFE_VUL_D2_CUR_MSB (0x0c04)
+#define AFE_HDMI_OUT_CUR_MSB (0x0c0c)
+#define AFE_HDMI_IN_2CH_CUR_MSB (0x0c10)
+#define AFE_SPDIF_OUT_CUR_MSB (0x0c14)
+#define SPDIFIN_CUR_MSB (0x0c18)
+#define AFE_CONN_REG (0x0c20)
+#define AFE_SECURE_MASK_CONN14_1 (0x0c24)
+#define AFE_SECURE_MASK_CONN15_1 (0x0c28)
+#define AFE_SECURE_MASK_CONN16_1 (0x0c2c)
+#define AFE_SECURE_MASK_CONN17_1 (0x0c30)
+#define AFE_SECURE_MASK_CONN18_1 (0x0c34)
+#define AFE_SECURE_MASK_CONN19_1 (0x0c38)
+#define AFE_SECURE_MASK_CONN20_1 (0x0c3c)
+#define AFE_SECURE_MASK_CONN21_1 (0x0c40)
+#define AFE_SECURE_MASK_CONN22_1 (0x0c44)
+#define AFE_SECURE_MASK_CONN23_1 (0x0c48)
+#define AFE_SECURE_MASK_CONN24_1 (0x0c4c)
+#define AFE_ADDA_DL_SDM_DCCOMP_CON (0x0c50)
+#define AFE_ADDA_DL_SDM_TEST (0x0c54)
+#define AFE_ADDA_DL_DC_COMP_CFG0 (0x0c58)
+#define AFE_ADDA_DL_DC_COMP_CFG1 (0x0c5c)
+#define AFE_ADDA_DL_SDM_FIFO_MON (0x0c60)
+#define AFE_ADDA_DL_SRC_LCH_MON (0x0c64)
+#define AFE_ADDA_DL_SRC_RCH_MON (0x0c68)
+#define AFE_ADDA_DL_SDM_OUT_MON (0x0c6c)
+#define AFE_ADDA_DL_SDM_DITHER_CON (0x0c70)
+
+#define AFE_VUL3_CUR_MSB (0x0c78)
+#define AFE_ASRC_2CH_CON0 (0x0c80)
+#define AFE_ASRC_2CH_CON1 (0x0c84)
+#define AFE_ASRC_2CH_CON2 (0x0c88)
+#define AFE_ASRC_2CH_CON3 (0x0c8c)
+#define AFE_ASRC_2CH_CON4 (0x0c90)
+#define AFE_ASRC_2CH_CON5 (0x0c94)
+#define AFE_ASRC_2CH_CON6 (0x0c98)
+#define AFE_ASRC_2CH_CON7 (0x0c9c)
+#define AFE_ASRC_2CH_CON8 (0x0ca0)
+#define AFE_ASRC_2CH_CON9 (0x0ca4)
+#define AFE_ASRC_2CH_CON10 (0x0ca8)
+#define AFE_ASRC_2CH_CON12 (0x0cb0)
+#define AFE_ASRC_2CH_CON13 (0x0cb4)
+
+#define AFE_PCM_TX_ASRC_2CH_CON0 (0x0cc0)
+#define AFE_PCM_TX_ASRC_2CH_CON1 (0x0cc4)
+#define AFE_PCM_TX_ASRC_2CH_CON2 (0x0cc8)
+#define AFE_PCM_TX_ASRC_2CH_CON3 (0x0ccc)
+#define AFE_PCM_TX_ASRC_2CH_CON4 (0x0cd0)
+#define AFE_PCM_TX_ASRC_2CH_CON5 (0x0cd4)
+#define AFE_PCM_TX_ASRC_2CH_CON6 (0x0cd8)
+#define AFE_PCM_TX_ASRC_2CH_CON7 (0x0cdc)
+#define AFE_PCM_TX_ASRC_2CH_CON8 (0x0ce0)
+#define AFE_PCM_TX_ASRC_2CH_CON9 (0x0ce4)
+#define AFE_PCM_TX_ASRC_2CH_CON10 (0x0ce8)
+#define AFE_PCM_TX_ASRC_2CH_CON12 (0x0cf0)
+#define AFE_PCM_TX_ASRC_2CH_CON13 (0x0cf4)
+#define AFE_PCM_RX_ASRC_2CH_CON0 (0x0d00)
+#define AFE_PCM_RX_ASRC_2CH_CON1 (0x0d04)
+#define AFE_PCM_RX_ASRC_2CH_CON2 (0x0d08)
+#define AFE_PCM_RX_ASRC_2CH_CON3 (0x0d0c)
+#define AFE_PCM_RX_ASRC_2CH_CON4 (0x0d10)
+#define AFE_PCM_RX_ASRC_2CH_CON5 (0x0d14)
+#define AFE_PCM_RX_ASRC_2CH_CON6 (0x0d18)
+#define AFE_PCM_RX_ASRC_2CH_CON7 (0x0d1c)
+#define AFE_PCM_RX_ASRC_2CH_CON8 (0x0d20)
+#define AFE_PCM_RX_ASRC_2CH_CON9 (0x0d24)
+#define AFE_PCM_RX_ASRC_2CH_CON10 (0x0d28)
+#define AFE_PCM_RX_ASRC_2CH_CON12 (0x0d30)
+#define AFE_PCM_RX_ASRC_2CH_CON13 (0x0d34)
+
+#define AFE_ADDA_PREDIS_CON2 (0x0d40)
+#define AFE_ADDA_PREDIS_CON3 (0x0d44)
+#define AFE_SECURE_MASK_CONN4_1 (0x0d48)
+#define AFE_SECURE_MASK_CONN5_1 (0x0d4c)
+#define AFE_SECURE_MASK_CONN6_1 (0x0d50)
+#define AFE_SECURE_MASK_CONN7_1 (0x0d54)
+#define AFE_SECURE_MASK_CONN8_1 (0x0d58)
+#define AFE_SECURE_MASK_CONN9_1 (0x0d5c)
+#define AFE_SECURE_MASK_CONN10_1 (0x0d60)
+#define AFE_SECURE_MASK_CONN11_1 (0x0d64)
+#define AFE_SECURE_MASK_CONN12_1 (0x0d68)
+#define AFE_SECURE_MASK_CONN13_1 (0x0d6c)
+#define AFE_MEMIF_MON12 (0x0d70)
+#define AFE_MEMIF_MON13 (0x0d74)
+#define AFE_MEMIF_MON14 (0x0d78)
+#define AFE_MEMIF_MON15 (0x0d7c)
+#define AFE_SECURE_MASK_CONN42 (0x0dbc)
+#define AFE_SECURE_MASK_CONN43 (0x0dc0)
+#define AFE_SECURE_MASK_CONN44 (0x0dc4)
+#define AFE_SECURE_MASK_CONN45 (0x0dc8)
+#define AFE_SECURE_MASK_CONN46 (0x0dcc)
+#define AFE_HD_ENGEN_ENABLE (0x0dd0)
+#define AFE_SECURE_MASK_CONN47 (0x0dd4)
+#define AFE_SECURE_MASK_CONN48 (0x0dd8)
+#define AFE_SECURE_MASK_CONN49 (0x0ddc)
+#define AFE_SECURE_MASK_CONN50 (0x0de0)
+#define AFE_SECURE_MASK_CONN51 (0x0de4)
+#define AFE_SECURE_MASK_CONN52 (0x0de8)
+#define AFE_SECURE_MASK_CONN53 (0x0dec)
+#define AFE_SECURE_MASK_CONN0_1 (0x0df0)
+#define AFE_SECURE_MASK_CONN1_1 (0x0df4)
+#define AFE_SECURE_MASK_CONN2_1 (0x0df8)
+#define AFE_SECURE_MASK_CONN3_1 (0x0dfc)
+
+#define AFE_ADDA_MTKAIF_CFG0 (0x0e00)
+#define AFE_ADDA_MTKAIF_SYNCWORD_CFG (0x0e14)
+#define AFE_ADDA_MTKAIF_RX_CFG0 (0x0e20)
+#define AFE_ADDA_MTKAIF_RX_CFG1 (0x0e24)
+#define AFE_ADDA_MTKAIF_RX_CFG2 (0x0e28)
+#define AFE_ADDA_MTKAIF_MON0 (0x0e34)
+#define AFE_ADDA_MTKAIF_MON1 (0x0e38)
+#define AFE_AUD_PAD_TOP (0x0e40)
+
+#define AFE_CM1_CON4 (0x0e48)
+#define AFE_CM2_CON4 (0x0e4c)
+#define AFE_CM1_CON0 (0x0e50)
+#define AFE_CM1_CON1 (0x0e54)
+#define AFE_CM1_CON2 (0x0e58)
+#define AFE_CM1_CON3 (0x0e5c)
+#define AFE_CM2_CON0 (0x0e60)
+#define AFE_CM2_CON1 (0x0e64)
+#define AFE_CM2_CON2 (0x0e68)
+#define AFE_CM2_CON3 (0x0e6c)
+#define AFE_CM2_CONN0 (0x0e70)
+#define AFE_CM2_CONN1 (0x0e74)
+#define AFE_CM2_CONN2 (0x0e78)
+
+#define AFE_GENERAL1_ASRC_2CH_CON0 (0x0e80)
+#define AFE_GENERAL1_ASRC_2CH_CON1 (0x0e84)
+#define AFE_GENERAL1_ASRC_2CH_CON2 (0x0e88)
+#define AFE_GENERAL1_ASRC_2CH_CON3 (0x0e8c)
+#define AFE_GENERAL1_ASRC_2CH_CON4 (0x0e90)
+#define AFE_GENERAL1_ASRC_2CH_CON5 (0x0e94)
+#define AFE_GENERAL1_ASRC_2CH_CON6 (0x0e98)
+#define AFE_GENERAL1_ASRC_2CH_CON7 (0x0e9c)
+#define AFE_GENERAL1_ASRC_2CH_CON8 (0x0ea0)
+#define AFE_GENERAL1_ASRC_2CH_CON9 (0x0ea4)
+#define AFE_GENERAL1_ASRC_2CH_CON10 (0x0ea8)
+#define AFE_GENERAL1_ASRC_2CH_CON12 (0x0eb0)
+#define AFE_GENERAL1_ASRC_2CH_CON13 (0x0eb4)
+#define GENERAL_ASRC_MODE (0x0eb8)
+#define GENERAL_ASRC_EN_ON (0x0ebc)
+
+#define AFE_CONN48 (0x0ec0)
+#define AFE_CONN49 (0x0ec4)
+#define AFE_CONN50 (0x0ec8)
+#define AFE_CONN51 (0x0ecc)
+#define AFE_CONN52 (0x0ed0)
+#define AFE_CONN53 (0x0ed4)
+#define AFE_CONN48_1 (0x0ee0)
+#define AFE_CONN49_1 (0x0ee4)
+#define AFE_CONN50_1 (0x0ee8)
+#define AFE_CONN51_1 (0x0eec)
+#define AFE_CONN52_1 (0x0ef0)
+#define AFE_CONN53_1 (0x0ef4)
+
+#define AFE_GENERAL2_ASRC_2CH_CON0 (0x0f00)
+#define AFE_GENERAL2_ASRC_2CH_CON1 (0x0f04)
+#define AFE_GENERAL2_ASRC_2CH_CON2 (0x0f08)
+#define AFE_GENERAL2_ASRC_2CH_CON3 (0x0f0c)
+#define AFE_GENERAL2_ASRC_2CH_CON4 (0x0f10)
+#define AFE_GENERAL2_ASRC_2CH_CON5 (0x0f14)
+#define AFE_GENERAL2_ASRC_2CH_CON6 (0x0f18)
+#define AFE_GENERAL2_ASRC_2CH_CON7 (0x0f1c)
+#define AFE_GENERAL2_ASRC_2CH_CON8 (0x0f20)
+#define AFE_GENERAL2_ASRC_2CH_CON9 (0x0f24)
+#define AFE_GENERAL2_ASRC_2CH_CON10 (0x0f28)
+#define AFE_GENERAL2_ASRC_2CH_CON12 (0x0f30)
+#define AFE_GENERAL2_ASRC_2CH_CON13 (0x0f34)
+
+#define AFE_SECURE_MASK_CONN28 (0x0f48)
+#define AFE_SECURE_MASK_CONN29 (0x0f4c)
+#define AFE_SECURE_MASK_CONN30 (0x0f50)
+#define AFE_SECURE_MASK_CONN31 (0x0f54)
+#define AFE_SECURE_MASK_CONN32 (0x0f58)
+#define AFE_SECURE_MASK_CONN33 (0x0f5c)
+#define AFE_SECURE_MASK_CONN34 (0x0f60)
+#define AFE_SECURE_MASK_CONN35 (0x0f64)
+#define AFE_SECURE_MASK_CONN36 (0x0f68)
+#define AFE_SECURE_MASK_CONN37 (0x0f6c)
+#define AFE_SECURE_MASK_CONN38 (0x0f70)
+#define AFE_SECURE_MASK_CONN39 (0x0f74)
+#define AFE_SECURE_MASK_CONN40 (0x0f78)
+#define AFE_SECURE_MASK_CONN41 (0x0f7c)
+#define AFE_SIDEBAND0 (0x0f80)
+#define AFE_SIDEBAND1 (0x0f84)
+#define AFE_SECURE_SIDEBAND0 (0x0f88)
+#define AFE_SECURE_SIDEBAND1 (0x0f8c)
+#define AFE_SECURE_MASK_CONN0 (0x0f90)
+#define AFE_SECURE_MASK_CONN1 (0x0f94)
+#define AFE_SECURE_MASK_CONN2 (0x0f98)
+#define AFE_SECURE_MASK_CONN3 (0x0f9c)
+#define AFE_SECURE_MASK_CONN4 (0x0fa0)
+#define AFE_SECURE_MASK_CONN5 (0x0fa4)
+#define AFE_SECURE_MASK_CONN6 (0x0fa8)
+#define AFE_SECURE_MASK_CONN7 (0x0fac)
+#define AFE_SECURE_MASK_CONN8 (0x0fb0)
+#define AFE_SECURE_MASK_CONN9 (0x0fb4)
+#define AFE_SECURE_MASK_CONN10 (0x0fb8)
+#define AFE_SECURE_MASK_CONN11 (0x0fbc)
+#define AFE_SECURE_MASK_CONN12 (0x0fc0)
+#define AFE_SECURE_MASK_CONN13 (0x0fc4)
+#define AFE_SECURE_MASK_CONN14 (0x0fc8)
+#define AFE_SECURE_MASK_CONN15 (0x0fcc)
+#define AFE_SECURE_MASK_CONN16 (0x0fd0)
+#define AFE_SECURE_MASK_CONN17 (0x0fd4)
+#define AFE_SECURE_MASK_CONN18 (0x0fd8)
+#define AFE_SECURE_MASK_CONN19 (0x0fdc)
+#define AFE_SECURE_MASK_CONN20 (0x0fe0)
+#define AFE_SECURE_MASK_CONN21 (0x0fe4)
+#define AFE_SECURE_MASK_CONN22 (0x0fe8)
+#define AFE_SECURE_MASK_CONN23 (0x0fec)
+#define AFE_SECURE_MASK_CONN24 (0x0ff0)
+#define AFE_SECURE_MASK_CONN25 (0x0ff4)
+#define AFE_SECURE_MASK_CONN26 (0x0ff8)
+#define AFE_SECURE_MASK_CONN27 (0x0ffc)
+
+#define MAX_REGISTER AFE_SECURE_MASK_CONN27
+
+#define AFE_IRQ_STATUS_BITS 0x3ff
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUD_TCON0_PDN_TML (1U << 27)
+#define AUD_TCON0_PDN_DAC_PREDIS (1U << 26)
+#define AUD_TCON0_PDN_DAC (1U << 25)
+#define AUD_TCON0_PDN_ADC (1U << 24)
+#define AUD_TCON0_PDN_TDM_IN (1U << 23)
+#define AUD_TCON0_PDN_TDM_OUT (1U << 22)
+#define AUD_TCON0_PDN_SPDIF (1U << 21)
+#define AUD_TCON0_PDN_APLL_TUNER (1U << 19)
+#define AUD_TCON0_PDN_APLL2_TUNER (1U << 18)
+#define AUD_TCON0_PDN_INTDIR (1U << 15)
+#define AUD_TCON0_PDN_24M (1U << 9)
+#define AUD_TCON0_PDN_22M (1U << 8)
+#define AUD_TCON0_PDN_I2S_IN (1U << 6)
+#define AUD_TCON0_PDN_AFE (1U << 2)
+
+/* AUDIO_TOP_CON1 (0x0004) */
+#define AUD_TCON1_PDN_TDM_ASRC (1U << 15)
+#define AUD_TCON1_PDN_GENERAL2_ASRC (1U << 14)
+#define AUD_TCON1_PDN_GENERAL1_ASRC (1U << 13)
+#define AUD_TCON1_PDN_CONNSYS_I2S_ASRC (1U << 12)
+#define AUD_TCON1_PDN_DMIC3_ADC (1U << 11)
+#define AUD_TCON1_PDN_DMIC2_ADC (1U << 10)
+#define AUD_TCON1_PDN_DMIC1_ADC (1U << 9)
+#define AUD_TCON1_PDN_DMIC0_ADC (1U << 8)
+#define AUD_TCON1_PDN_I2S4_BCLK (1U << 7)
+#define AUD_TCON1_PDN_I2S3_BCLK (1U << 6)
+#define AUD_TCON1_PDN_I2S2_BCLK (1U << 5)
+#define AUD_TCON1_PDN_I2S1_BCLK (1U << 4)
+
+/* AUDIO_TOP_CON3 (0x000C) */
+#define AUD_TCON3_HDMI_BCK_INV (1U << 3)
+
+/* AFE_I2S_CON (0x0018) */
+#define AFE_I2S_CON_PHASE_SHIFT_FIX (1U << 31)
+#define AFE_I2S_CON_FROM_IO_MUX (1U << 28)
+#define AFE_I2S_CON_LOW_JITTER_CLK (1U << 12)
+#define AFE_I2S_CON_RATE_MASK GENMASK(11, 8)
+#define AFE_I2S_CON_FORMAT_I2S (1U << 3)
+#define AFE_I2S_CON_SRC_SLAVE (1U << 2)
+
+/* AFE_ASRC_2CH_CON0 */
+#define ONE_HEART (1U << 31)
+#define CHSET_STR_CLR (1U << 4)
+#define COEFF_SRAM_CTRL (1U << 1)
+#define ASM_ON (1U << 0)
+
+/* CON2 */
+#define O16BIT (1U << 19)
+#define CLR_IIR_HISTORY (1U << 17)
+#define IS_MONO (1U << 16)
+#define IIR_EN (1U << 11)
+#define IIR_STAGE_MASK GENMASK(10, 8)
+
+/* CON5 */
+#define CALI_CYCLE_MASK GENMASK(31, 16)
+#define CALI_64_CYCLE FIELD_PREP(CALI_CYCLE_MASK, 0x3F)
+#define CALI_96_CYCLE FIELD_PREP(CALI_CYCLE_MASK, 0x5F)
+#define CALI_441_CYCLE FIELD_PREP(CALI_CYCLE_MASK, 0x1B8)
+
+#define CALI_AUTORST (1U << 15)
+#define AUTO_TUNE_FREQ5 (1U << 12)
+#define COMP_FREQ_RES (1U << 11)
+
+#define CALI_SEL_MASK GENMASK(9, 8)
+#define CALI_SEL_00 FIELD_PREP(CALI_SEL_MASK, 0)
+#define CALI_SEL_01 FIELD_PREP(CALI_SEL_MASK, 1)
+
+#define CALI_BP_DGL (1U << 7) /* Bypass the deglitch circuit */
+#define AUTO_TUNE_FREQ4 (1U << 3)
+#define CALI_AUTO_RESTART (1U << 2)
+#define CALI_USE_FREQ_OUT (1U << 1)
+#define CALI_ON (1U << 0)
+
+#define AFE_I2S_CON_WLEN_32BIT (1U << 1)
+#define AFE_I2S_CON_EN (1U << 0)
+
+#define AFE_CONN3_I03_O03_S (1U << 3)
+#define AFE_CONN4_I04_O04_S (1U << 4)
+#define AFE_CONN4_I03_O04_S (1U << 3)
+
+/* AFE_I2S_CON1 (0x0034) */
+#define AFE_I2S_CON1_I2S2_TO_PAD (1U << 18)
+#define AFE_I2S_CON1_TDMOUT_TO_PAD (0 << 18)
+#define AFE_I2S_CON1_RATE GENMASK(11, 8)
+#define AFE_I2S_CON1_FORMAT_I2S (1U << 3)
+#define AFE_I2S_CON1_WLEN_32BIT (1U << 1)
+#define AFE_I2S_CON1_EN (1U << 0)
+
+/* AFE_I2S_CON2 (0x0038) */
+#define AFE_I2S_CON2_LOW_JITTER_CLK (1U << 12)
+#define AFE_I2S_CON2_RATE GENMASK(11, 8)
+#define AFE_I2S_CON2_FORMAT_I2S (1U << 3)
+#define AFE_I2S_CON2_WLEN_32BIT (1U << 1)
+#define AFE_I2S_CON2_EN (1U << 0)
+
+/* AFE_I2S_CON3 (0x004C) */
+#define AFE_I2S_CON3_LOW_JITTER_CLK (1U << 12)
+#define AFE_I2S_CON3_RATE GENMASK(11, 8)
+#define AFE_I2S_CON3_FORMAT_I2S (1U << 3)
+#define AFE_I2S_CON3_WLEN_32BIT (1U << 1)
+#define AFE_I2S_CON3_EN (1U << 0)
+
+/* AFE_ADDA_DL_SRC2_CON0 (0x0108) */
+#define AFE_ADDA_DL_SAMPLING_RATE GENMASK(31, 28)
+#define AFE_ADDA_DL_8X_UPSAMPLE GENMASK(25, 24)
+#define AFE_ADDA_DL_MUTE_OFF_CH1 (1U << 12)
+#define AFE_ADDA_DL_MUTE_OFF_CH2 (1U << 11)
+#define AFE_ADDA_DL_VOICE_DATA (1U << 5)
+#define AFE_ADDA_DL_DEGRADE_GAIN (1U << 1)
+
+/* AFE_ADDA_UL_SRC_CON0 (0x0114) */
+#define AFE_ADDA_UL_SAMPLING_RATE GENMASK(19, 17)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define AFE_ADDA_UL_DL_ADDA_AFE_ON (1U << 0)
+#define AFE_ADDA_UL_DL_DMIC_CLKDIV_ON (1U << 1)
+
+/* AFE_APLL_TUNER_CFG (0x03f0) */
+#define AFE_APLL_TUNER_CFG_MASK GENMASK(15, 1)
+#define AFE_APLL_TUNER_CFG_EN_MASK (1U << 0)
+
+/* AFE_APLL_TUNER_CFG1 (0x03f4) */
+#define AFE_APLL_TUNER_CFG1_MASK GENMASK(15, 1)
+#define AFE_APLL_TUNER_CFG1_EN_MASK (1U << 0)
+
+/* PCM_INTF_CON1 (0x0550) */
+#define PCM_INTF_CON1_EXT_MODEM (1U << 17)
+#define PCM_INTF_CON1_16BIT (0 << 16)
+#define PCM_INTF_CON1_24BIT (1U << 16)
+#define PCM_INTF_CON1_32BCK (0 << 14)
+#define PCM_INTF_CON1_64BCK (1U << 14)
+#define PCM_INTF_CON1_MASTER_MODE (0 << 5)
+#define PCM_INTF_CON1_SLAVE_MODE (1U << 5)
+#define PCM_INTF_CON1_FS_MASK GENMASK(4, 3)
+#define PCM_INTF_CON1_FS_8K FIELD_PREP(PCM_INTF_CON1_FS_MASK, 0)
+#define PCM_INTF_CON1_FS_16K FIELD_PREP(PCM_INTF_CON1_FS_MASK, 1)
+#define PCM_INTF_CON1_FS_32K FIELD_PREP(PCM_INTF_CON1_FS_MASK, 2)
+#define PCM_INTF_CON1_FS_48K FIELD_PREP(PCM_INTF_CON1_FS_MASK, 3)
+#define PCM_INTF_CON1_SYNC_LEN_MASK GENMASK(13, 9)
+#define PCM_INTF_CON1_SYNC_LEN(x) FIELD_PREP(PCM_INTF_CON1_SYNC_LEN_MASK, ((x) - 1))
+#define PCM_INTF_CON1_FORMAT_MASK GENMASK(2, 1)
+#define PCM_INTF_CON1_SYNC_OUT_INV (1U << 23)
+#define PCM_INTF_CON1_BCLK_OUT_INV (1U << 22)
+#define PCM_INTF_CON1_SYNC_IN_INV (1U << 21)
+#define PCM_INTF_CON1_BCLK_IN_INV (1U << 20)
+#define PCM_INTF_CON1_BYPASS_ASRC (1U << 6)
+#define PCM_INTF_CON1_EN (1U << 0)
+#define PCM_INTF_CON1_CONFIG_MASK (0xf3fffe)
+
+/* AFE_DMIC0_UL_SRC_CON0 (0x05b4)
+ * AFE_DMIC1_UL_SRC_CON0 (0x0620)
+ * AFE_DMIC2_UL_SRC_CON0 (0x0780)
+ * AFE_DMIC3_UL_SRC_CON0 (0x07ec)
+ */
+#define DMIC_TOP_CON_CK_PHASE_SEL_CH1 GENMASK(29, 27)
+#define DMIC_TOP_CON_CK_PHASE_SEL_CH2 GENMASK(26, 24)
+#define DMIC_TOP_CON_TWO_WIRE_MODE (1U << 23)
+#define DMIC_TOP_CON_CH2_ON (1U << 22)
+#define DMIC_TOP_CON_CH1_ON (1U << 21)
+#define DMIC_TOP_CON_VOICE_MODE_MASK GENMASK(19, 17)
+#define DMIC_TOP_CON_VOICE_MODE_8K FIELD_PREP(DMIC_TOP_CON_VOICE_MODE_MASK, 0)
+#define DMIC_TOP_CON_VOICE_MODE_16K FIELD_PREP(DMIC_TOP_CON_VOICE_MODE_MASK, 1)
+#define DMIC_TOP_CON_VOICE_MODE_32K FIELD_PREP(DMIC_TOP_CON_VOICE_MODE_MASK, 2)
+#define DMIC_TOP_CON_VOICE_MODE_48K FIELD_PREP(DMIC_TOP_CON_VOICE_MODE_MASK, 3)
+#define DMIC_TOP_CON_LOW_POWER_MODE_MASK GENMASK(15, 14)
+#define DMIC_TOP_CON_LOW_POWER_MODE(x) FIELD_PREP(DMIC_TOP_CON_LOW_POWER_MODE_MASK, (x))
+#define DMIC_TOP_CON_IIR_ON (1U << 10)
+#define DMIC_TOP_CON_IIR_MODE GENMASK(9, 7)
+#define DMIC_TOP_CON_INPUT_MODE (1U << 5)
+#define DMIC_TOP_CON_SDM3_LEVEL_MODE (1U << 1)
+#define DMIC_TOP_CON_SRC_ON (1U << 0)
+#define DMIC_TOP_CON_SDM3_DE_SELECT (0 << 1)
+#define DMIC_TOP_CON_CONFIG_MASK (0x3f8ed7a6)
+
+/* AFE_CONN_24BIT (0x0AA4) */
+#define AFE_CONN_24BIT_O10 (1U << 10)
+#define AFE_CONN_24BIT_O09 (1U << 9)
+#define AFE_CONN_24BIT_O06 (1U << 6)
+#define AFE_CONN_24BIT_O05 (1U << 5)
+#define AFE_CONN_24BIT_O04 (1U << 4)
+#define AFE_CONN_24BIT_O03 (1U << 3)
+#define AFE_CONN_24BIT_O02 (1U << 2)
+#define AFE_CONN_24BIT_O01 (1U << 1)
+#define AFE_CONN_24BIT_O00 (1U << 0)
+
+/* AFE_HD_ENGEN_ENABLE */
+#define AFE_22M_PLL_EN (1U << 0)
+#define AFE_24M_PLL_EN (1U << 1)
+
+/* AFE_GAIN1_CON0 (0x0410) */
+#define AFE_GAIN1_CON0_EN_MASK GENMASK(0, 0)
+#define AFE_GAIN1_CON0_MODE_MASK GENMASK(7, 4)
+#define AFE_GAIN1_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8)
+
+/* AFE_GAIN1_CON1 (0x0414) */
+#define AFE_GAIN1_CON1_MASK GENMASK(19, 0)
+
+/* AFE_GAIN1_CUR (0x0B78) */
+#define AFE_GAIN1_CUR_MASK GENMASK(19, 0)
+
+/* AFE_CM1_CON0 (0x0e50) */
+/* AFE_CM2_CON0 (0x0e60) */
+#define CM_AFE_CM_CH_NUM_MASK GENMASK(3, 0)
+#define CM_AFE_CM_CH_NUM(x) FIELD_PREP(CM_AFE_CM_CH_NUM_MASK, ((x) - 1))
+#define CM_AFE_CM_ON (1U << 4)
+#define CM_AFE_CM_START_DATA_MASK GENMASK(11, 8)
+
+#define CM_AFE_CM1_VUL_SEL (1U << 12)
+#define CM_AFE_CM1_IN_MODE_MASK GENMASK(19, 16)
+#define CM_AFE_CM2_TDM_SEL (1U << 12)
+#define CM_AFE_CM2_CLK_SEL (1U << 13)
+#define CM_AFE_CM2_GASRC1_OUT_SEL (1U << 17)
+#define CM_AFE_CM2_GASRC2_OUT_SEL (1U << 16)
+
+/* AFE_CM2_CONN* */
+#define CM2_AFE_CM2_CONN_CFG1(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG1_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG1_MASK GENMASK(4, 0)
+#define CM2_AFE_CM2_CONN_CFG2(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG2_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG2_MASK GENMASK(9, 5)
+#define CM2_AFE_CM2_CONN_CFG3(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG3_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG3_MASK GENMASK(14, 10)
+#define CM2_AFE_CM2_CONN_CFG4(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG4_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG4_MASK GENMASK(19, 15)
+#define CM2_AFE_CM2_CONN_CFG5(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG5_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG5_MASK GENMASK(24, 20)
+#define CM2_AFE_CM2_CONN_CFG6(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG6_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG6_MASK GENMASK(29, 25)
+#define CM2_AFE_CM2_CONN_CFG7(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG7_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG7_MASK GENMASK(4, 0)
+#define CM2_AFE_CM2_CONN_CFG8(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG8_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG8_MASK GENMASK(9, 5)
+#define CM2_AFE_CM2_CONN_CFG9(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG9_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG9_MASK GENMASK(14, 10)
+#define CM2_AFE_CM2_CONN_CFG10(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG10_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG10_MASK GENMASK(19, 15)
+#define CM2_AFE_CM2_CONN_CFG11(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG11_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG11_MASK GENMASK(24, 20)
+#define CM2_AFE_CM2_CONN_CFG12(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG12_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG12_MASK GENMASK(29, 25)
+#define CM2_AFE_CM2_CONN_CFG13(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG13_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG13_MASK GENMASK(4, 0)
+#define CM2_AFE_CM2_CONN_CFG14(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG14_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG14_MASK GENMASK(9, 5)
+#define CM2_AFE_CM2_CONN_CFG15(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG15_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG15_MASK GENMASK(14, 10)
+#define CM2_AFE_CM2_CONN_CFG16(x) FIELD_PREP(CM2_AFE_CM2_CONN_CFG16_MASK, (x))
+#define CM2_AFE_CM2_CONN_CFG16_MASK GENMASK(19, 15)
+
+/* AFE_CM1_CON* */
+#define CM_AFE_CM_UPDATE_CNT1_MASK GENMASK(15, 0)
+#define CM_AFE_CM_UPDATE_CNT1(x) FIELD_PREP(CM_AFE_CM_UPDATE_CNT1_MASK, (x))
+#define CM_AFE_CM_UPDATE_CNT2_MASK GENMASK(31, 16)
+#define CM_AFE_CM_UPDATE_CNT2(x) FIELD_PREP(CM_AFE_CM_UPDATE_CNT2_MASK, (x))
+
+#endif
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644
index 9b1af1a70874..000000000000
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * mtk_afe_common.h -- Mediatek audio driver common definitions
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen@mediatek.com>
- * Sascha Hauer <s.hauer@pengutronix.de>
- * Hidalgo Huang <hidalgo.huang@mediatek.com>
- * Ir Lian <ir.lian@mediatek.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 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.
- */
-
-#ifndef _MTK_AFE_COMMON_H_
-#define _MTK_AFE_COMMON_H_
-
-#include <linux/clk.h>
-#include <linux/regmap.h>
-
-enum {
- MTK_AFE_MEMIF_DL1,
- MTK_AFE_MEMIF_DL2,
- MTK_AFE_MEMIF_VUL,
- MTK_AFE_MEMIF_DAI,
- MTK_AFE_MEMIF_AWB,
- MTK_AFE_MEMIF_MOD_DAI,
- MTK_AFE_MEMIF_HDMI,
- MTK_AFE_MEMIF_NUM,
- MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
- MTK_AFE_IO_MOD_PCM2,
- MTK_AFE_IO_PMIC,
- MTK_AFE_IO_I2S,
- MTK_AFE_IO_2ND_I2S,
- MTK_AFE_IO_HW_GAIN1,
- MTK_AFE_IO_HW_GAIN2,
- MTK_AFE_IO_MRG_O,
- MTK_AFE_IO_MRG_I,
- MTK_AFE_IO_DAIBT,
- MTK_AFE_IO_HDMI,
-};
-
-enum {
- MTK_AFE_IRQ_1,
- MTK_AFE_IRQ_2,
- MTK_AFE_IRQ_3,
- MTK_AFE_IRQ_4,
- MTK_AFE_IRQ_5,
- MTK_AFE_IRQ_6,
- MTK_AFE_IRQ_7,
- MTK_AFE_IRQ_8,
- MTK_AFE_IRQ_NUM,
-};
-
-enum {
- MTK_CLK_INFRASYS_AUD,
- MTK_CLK_TOP_PDN_AUD,
- MTK_CLK_TOP_PDN_AUD_BUS,
- MTK_CLK_I2S0_M,
- MTK_CLK_I2S1_M,
- MTK_CLK_I2S2_M,
- MTK_CLK_I2S3_M,
- MTK_CLK_I2S3_B,
- MTK_CLK_BCK0,
- MTK_CLK_BCK1,
- MTK_CLK_NUM
-};
-
-struct mtk_afe;
-struct snd_pcm_substream;
-
-struct mtk_afe_memif_data {
- int id;
- const char *name;
- int reg_ofs_base;
- int reg_ofs_cur;
- int fs_shift;
- int mono_shift;
- int enable_shift;
- int irq_reg_cnt;
- int irq_cnt_shift;
- int irq_en_shift;
- int irq_fs_shift;
- int irq_clr_shift;
-};
-
-struct mtk_afe_memif {
- unsigned int phys_buf_addr;
- int buffer_size;
- struct snd_pcm_substream *substream;
- const struct mtk_afe_memif_data *data;
- const struct mtk_afe_irq_data *irqdata;
-};
-
-#endif
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c
deleted file mode 100644
index 08af9f5dc4ab..000000000000
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ /dev/null
@@ -1,1304 +0,0 @@
-/*
- * Mediatek ALSA SoC AFE platform driver
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen@mediatek.com>
- * Sascha Hauer <s.hauer@pengutronix.de>
- * Hidalgo Huang <hidalgo.huang@mediatek.com>
- * Ir Lian <ir.lian@mediatek.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 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.
- */
-
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/pm_runtime.h>
-#include <sound/soc.h>
-#include "mtk-afe-common.h"
-
-/*****************************************************************************
- * R E G I S T E R D E F I N I T I O N
- *****************************************************************************/
-#define AUDIO_TOP_CON0 0x0000
-#define AUDIO_TOP_CON1 0x0004
-#define AFE_DAC_CON0 0x0010
-#define AFE_DAC_CON1 0x0014
-#define AFE_I2S_CON1 0x0034
-#define AFE_I2S_CON2 0x0038
-#define AFE_CONN_24BIT 0x006c
-
-#define AFE_CONN1 0x0024
-#define AFE_CONN2 0x0028
-#define AFE_CONN7 0x0460
-#define AFE_CONN8 0x0464
-#define AFE_HDMI_CONN0 0x0390
-
-/* Memory interface */
-#define AFE_DL1_BASE 0x0040
-#define AFE_DL1_CUR 0x0044
-#define AFE_DL1_END 0x0048
-#define AFE_DL2_BASE 0x0050
-#define AFE_DL2_CUR 0x0054
-#define AFE_AWB_BASE 0x0070
-#define AFE_AWB_CUR 0x007c
-#define AFE_VUL_BASE 0x0080
-#define AFE_VUL_CUR 0x008c
-#define AFE_VUL_END 0x0088
-#define AFE_DAI_BASE 0x0090
-#define AFE_DAI_CUR 0x009c
-#define AFE_MOD_PCM_BASE 0x0330
-#define AFE_MOD_PCM_CUR 0x033c
-#define AFE_HDMI_OUT_BASE 0x0374
-#define AFE_HDMI_OUT_CUR 0x0378
-#define AFE_HDMI_OUT_END 0x037c
-
-#define AFE_ADDA2_TOP_CON0 0x0600
-
-#define AFE_HDMI_OUT_CON0 0x0370
-
-#define AFE_IRQ_MCU_CON 0x03a0
-#define AFE_IRQ_STATUS 0x03a4
-#define AFE_IRQ_CLR 0x03a8
-#define AFE_IRQ_CNT1 0x03ac
-#define AFE_IRQ_CNT2 0x03b0
-#define AFE_IRQ_MCU_EN 0x03b4
-#define AFE_IRQ_CNT5 0x03bc
-#define AFE_IRQ_CNT7 0x03dc
-
-#define AFE_TDM_CON1 0x0548
-#define AFE_TDM_CON2 0x054c
-
-#define AFE_BASE_END_OFFSET 8
-#define AFE_IRQ_STATUS_BITS 0xff
-
-/* AUDIO_TOP_CON0 (0x0000) */
-#define AUD_TCON0_PDN_SPDF (0x1 << 21)
-#define AUD_TCON0_PDN_HDMI (0x1 << 20)
-#define AUD_TCON0_PDN_24M (0x1 << 9)
-#define AUD_TCON0_PDN_22M (0x1 << 8)
-#define AUD_TCON0_PDN_AFE (0x1 << 2)
-
-/* AFE_I2S_CON1 (0x0034) */
-#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12)
-#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8)
-#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3)
-#define AFE_I2S_CON1_EN (0x1 << 0)
-
-/* AFE_I2S_CON2 (0x0038) */
-#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12)
-#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8)
-#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3)
-#define AFE_I2S_CON2_EN (0x1 << 0)
-
-/* AFE_CONN_24BIT (0x006c) */
-#define AFE_CONN_24BIT_O04 (0x1 << 4)
-#define AFE_CONN_24BIT_O03 (0x1 << 3)
-
-/* AFE_HDMI_CONN0 (0x0390) */
-#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21)
-#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18)
-#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15)
-#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12)
-#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9)
-#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6)
-#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3)
-#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0)
-
-/* AFE_TDM_CON1 (0x0548) */
-#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24)
-#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12)
-#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8)
-#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4)
-#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3)
-#define AFE_TDM_CON1_BCK_INV (0x1 << 1)
-#define AFE_TDM_CON1_EN (0x1 << 0)
-
-enum afe_tdm_ch_start {
- AFE_TDM_CH_START_O30_O31 = 0,
- AFE_TDM_CH_START_O32_O33,
- AFE_TDM_CH_START_O34_O35,
- AFE_TDM_CH_START_O36_O37,
- AFE_TDM_CH_ZERO,
-};
-
-static const unsigned int mtk_afe_backup_list[] = {
- AUDIO_TOP_CON0,
- AFE_CONN1,
- AFE_CONN2,
- AFE_CONN7,
- AFE_CONN8,
- AFE_DAC_CON1,
- AFE_DL1_BASE,
- AFE_DL1_END,
- AFE_VUL_BASE,
- AFE_VUL_END,
- AFE_HDMI_OUT_BASE,
- AFE_HDMI_OUT_END,
- AFE_HDMI_CONN0,
- AFE_DAC_CON0,
-};
-
-struct mtk_afe {
- /* address for ioremap audio hardware register */
- void __iomem *base_addr;
- struct device *dev;
- struct regmap *regmap;
- struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
- struct clk *clocks[MTK_CLK_NUM];
- unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
- bool suspended;
-};
-
-static const struct snd_pcm_hardware mtk_afe_hardware = {
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .buffer_bytes_max = 256 * 1024,
- .period_bytes_min = 512,
- .period_bytes_max = 128 * 1024,
- .periods_min = 2,
- .periods_max = 256,
- .fifo_size = 0,
-};
-
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
- (struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
- unsigned int hw_ptr;
- int ret;
-
- ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
- if (ret || hw_ptr == 0) {
- dev_err(afe->dev, "%s hw_ptr err\n", __func__);
- hw_ptr = memif->phys_buf_addr;
- }
-
- return bytes_to_frames(substream->runtime,
- hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mtk_afe_pcm_ops = {
- .ioctl = snd_pcm_lib_ioctl,
- .pointer = mtk_afe_pcm_pointer,
-};
-
-static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- size_t size;
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
-
- size = mtk_afe_hardware.buffer_bytes_max;
-
- return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- card->dev, size, size);
-}
-
-static void mtk_afe_pcm_free(struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
- .ops = &mtk_afe_pcm_ops,
- .pcm_new = mtk_afe_pcm_new,
- .pcm_free = mtk_afe_pcm_free,
-};
-
-struct mtk_afe_rate {
- unsigned int rate;
- unsigned int regvalue;
-};
-
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
- { .rate = 8000, .regvalue = 0 },
- { .rate = 11025, .regvalue = 1 },
- { .rate = 12000, .regvalue = 2 },
- { .rate = 16000, .regvalue = 4 },
- { .rate = 22050, .regvalue = 5 },
- { .rate = 24000, .regvalue = 6 },
- { .rate = 32000, .regvalue = 8 },
- { .rate = 44100, .regvalue = 9 },
- { .rate = 48000, .regvalue = 10 },
- { .rate = 88000, .regvalue = 11 },
- { .rate = 96000, .regvalue = 12 },
- { .rate = 174000, .regvalue = 13 },
- { .rate = 192000, .regvalue = 14 },
-};
-
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
- if (mtk_afe_i2s_rates[i].rate == sample_rate)
- return mtk_afe_i2s_rates[i].regvalue;
-
- return -EINVAL;
-}
-
-static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
-{
- unsigned int val;
- int fs = mtk_afe_i2s_fs(rate);
-
- if (fs < 0)
- return -EINVAL;
-
- /* from external ADC */
- regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1);
-
- /* set input */
- val = AFE_I2S_CON2_LOW_JITTER_CLK |
- AFE_I2S_CON2_RATE(fs) |
- AFE_I2S_CON2_FORMAT_I2S;
-
- regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val);
-
- /* set output */
- val = AFE_I2S_CON1_LOW_JITTER_CLK |
- AFE_I2S_CON1_RATE(fs) |
- AFE_I2S_CON1_FORMAT_I2S;
-
- regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val);
- return 0;
-}
-
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
-{
- unsigned int val;
-
- regmap_read(afe->regmap, AFE_I2S_CON2, &val);
- if (!!(val & AFE_I2S_CON2_EN) == enable)
- return; /* must skip soft reset */
-
- /* I2S soft reset begin */
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4);
-
- /* input */
- regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable);
-
- /* output */
- regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
-
- /* I2S soft reset end */
- udelay(1);
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0);
-}
-
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
- struct clk *m_ck, struct clk *b_ck)
-{
- int ret;
-
- if (m_ck) {
- ret = clk_prepare_enable(m_ck);
- if (ret) {
- dev_err(afe->dev, "Failed to enable m_ck\n");
- return ret;
- }
- }
-
- if (b_ck) {
- ret = clk_prepare_enable(b_ck);
- if (ret) {
- dev_err(afe->dev, "Failed to enable b_ck\n");
- return ret;
- }
- }
- return 0;
-}
-
-static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
- struct clk *m_ck, unsigned int mck_rate,
- struct clk *b_ck, unsigned int bck_rate)
-{
- int ret;
-
- if (m_ck) {
- ret = clk_set_rate(m_ck, mck_rate);
- if (ret) {
- dev_err(afe->dev, "Failed to set m_ck rate\n");
- return ret;
- }
- }
-
- if (b_ck) {
- ret = clk_set_rate(b_ck, bck_rate);
- if (ret) {
- dev_err(afe->dev, "Failed to set b_ck rate\n");
- return ret;
- }
- }
- return 0;
-}
-
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
- struct clk *m_ck, struct clk *b_ck)
-{
- if (m_ck)
- clk_disable_unprepare(m_ck);
- if (b_ck)
- clk_disable_unprepare(b_ck);
-}
-
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-
- if (dai->active)
- return 0;
-
- mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
- AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
- return 0;
-}
-
-static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-
- if (dai->active)
- return;
-
- mtk_afe_set_i2s_enable(afe, false);
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
- AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
- AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
- mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
-}
-
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime * const runtime = substream->runtime;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- int ret;
-
- mtk_afe_dais_set_clks(afe,
- afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
- NULL, 0);
- /* config I2S */
- ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
- if (ret)
- return ret;
-
- mtk_afe_set_i2s_enable(afe, true);
-
- return 0;
-}
-
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-
- if (dai->active)
- return 0;
-
- mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
- afe->clocks[MTK_CLK_I2S3_B]);
- return 0;
-}
-
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-
- if (dai->active)
- return;
-
- mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
- afe->clocks[MTK_CLK_I2S3_B]);
-}
-
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime * const runtime = substream->runtime;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- unsigned int val;
-
- mtk_afe_dais_set_clks(afe,
- afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
- afe->clocks[MTK_CLK_I2S3_B],
- runtime->rate * runtime->channels * 32);
-
- val = AFE_TDM_CON1_BCK_INV |
- AFE_TDM_CON1_1_BCK_DELAY |
- AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */
- AFE_TDM_CON1_WLEN_32BIT |
- AFE_TDM_CON1_32_BCK_CYCLES |
- AFE_TDM_CON1_LRCK_WIDTH(32);
- regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val);
-
- /* set tdm2 config */
- switch (runtime->channels) {
- case 1:
- case 2:
- val = AFE_TDM_CH_START_O30_O31;
- val |= (AFE_TDM_CH_ZERO << 4);
- val |= (AFE_TDM_CH_ZERO << 8);
- val |= (AFE_TDM_CH_ZERO << 12);
- break;
- case 3:
- case 4:
- val = AFE_TDM_CH_START_O30_O31;
- val |= (AFE_TDM_CH_START_O32_O33 << 4);
- val |= (AFE_TDM_CH_ZERO << 8);
- val |= (AFE_TDM_CH_ZERO << 12);
- break;
- case 5:
- case 6:
- val = AFE_TDM_CH_START_O30_O31;
- val |= (AFE_TDM_CH_START_O32_O33 << 4);
- val |= (AFE_TDM_CH_START_O34_O35 << 8);
- val |= (AFE_TDM_CH_ZERO << 12);
- break;
- case 7:
- case 8:
- val = AFE_TDM_CH_START_O30_O31;
- val |= (AFE_TDM_CH_START_O32_O33 << 4);
- val |= (AFE_TDM_CH_START_O34_O35 << 8);
- val |= (AFE_TDM_CH_START_O36_O37 << 12);
- break;
- default:
- val = 0;
- }
- regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val);
-
- regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
- 0x000000f0, runtime->channels << 4);
- return 0;
-}
-
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-
- dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
- AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0);
-
- /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
- regmap_write(afe->regmap, AFE_HDMI_CONN0,
- AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
- AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
- AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
- AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
-
- /* enable Out control */
- regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
-
- /* enable tdm */
- regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1);
-
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- /* disable tdm */
- regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0);
-
- /* disable Out control */
- regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0);
-
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
- AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
- AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
- int ret;
-
- memif->substream = substream;
-
- snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
-
- /*
- * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
- * smaller than period_size due to AFE's internal buffer.
- * This easily leads to overrun when avail_min is period_size.
- * One more period can hold the possible unread buffer.
- */
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- ret = snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS,
- 3,
- mtk_afe_hardware.periods_max);
- if (ret < 0) {
- dev_err(afe->dev, "hw_constraint_minmax failed\n");
- return ret;
- }
- }
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
- return ret;
-}
-
-static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
- memif->substream = NULL;
-}
-
-static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
- int ret;
-
- dev_dbg(afe->dev,
- "%s period = %u, rate= %u, channels=%u\n",
- __func__, params_period_size(params), params_rate(params),
- params_channels(params));
-
- ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (ret < 0)
- return ret;
-
- memif->phys_buf_addr = substream->runtime->dma_addr;
- memif->buffer_size = substream->runtime->dma_bytes;
-
- /* start */
- regmap_write(afe->regmap,
- memif->data->reg_ofs_base, memif->phys_buf_addr);
- /* end */
- regmap_write(afe->regmap,
- memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
- memif->phys_buf_addr + memif->buffer_size - 1);
-
- /* set channel */
- if (memif->data->mono_shift >= 0) {
- unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
- regmap_update_bits(afe->regmap, AFE_DAC_CON1,
- 1 << memif->data->mono_shift,
- mono << memif->data->mono_shift);
- }
-
- /* set rate */
- if (memif->data->fs_shift < 0)
- return 0;
- if (memif->data->id == MTK_AFE_MEMIF_DAI ||
- memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
- unsigned int val;
-
- switch (params_rate(params)) {
- case 8000:
- val = 0;
- break;
- case 16000:
- val = 1;
- break;
- case 32000:
- val = 2;
- break;
- default:
- return -EINVAL;
- }
-
- if (memif->data->id == MTK_AFE_MEMIF_DAI)
- regmap_update_bits(afe->regmap, AFE_DAC_CON0,
- 0x3 << memif->data->fs_shift,
- val << memif->data->fs_shift);
- else
- regmap_update_bits(afe->regmap, AFE_DAC_CON1,
- 0x3 << memif->data->fs_shift,
- val << memif->data->fs_shift);
-
- } else {
- int fs = mtk_afe_i2s_fs(params_rate(params));
-
- if (fs < 0)
- return -EINVAL;
-
- regmap_update_bits(afe->regmap, AFE_DAC_CON1,
- 0xf << memif->data->fs_shift,
- fs << memif->data->fs_shift);
- }
-
- return 0;
-}
-
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime * const runtime = substream->runtime;
- struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
- struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
- unsigned int counter = runtime->period_size;
-
- dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- if (memif->data->enable_shift >= 0)
- regmap_update_bits(afe->regmap, AFE_DAC_CON0,
- 1 << memif->data->enable_shift,
- 1 << memif->data->enable_shift);
-
- /* set irq counter */
- regmap_update_bits(afe->regmap,
- memif->data->irq_reg_cnt,
- 0x3ffff << memif->data->irq_cnt_shift,
- counter << memif->data->irq_cnt_shift);
-
- /* set irq fs */
- if (memif->data->irq_fs_shift >= 0) {
- int fs = mtk_afe_i2s_fs(runtime->rate);
-
- if (fs < 0)
- return -EINVAL;
-
- regmap_update_bits(afe->regmap,
- AFE_IRQ_MCU_CON,
- 0xf << memif->data->irq_fs_shift,
- fs << memif->data->irq_fs_shift);
- }
- /* enable interrupt */
- regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
- 1 << memif->data->irq_en_shift,
- 1 << memif->data->irq_en_shift);
-
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- if (memif->data->enable_shift >= 0)
- regmap_update_bits(afe->regmap, AFE_DAC_CON0,
- 1 << memif->data->enable_shift, 0);
- /* disable interrupt */
- regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
- 1 << memif->data->irq_en_shift,
- 0 << memif->data->irq_en_shift);
- /* and clear pending IRQ */
- regmap_write(afe->regmap, AFE_IRQ_CLR,
- 1 << memif->data->irq_clr_shift);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
- .startup = mtk_afe_dais_startup,
- .shutdown = mtk_afe_dais_shutdown,
- .hw_params = mtk_afe_dais_hw_params,
- .hw_free = mtk_afe_dais_hw_free,
- .trigger = mtk_afe_dais_trigger,
-};
-
-/* BE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
- .startup = mtk_afe_i2s_startup,
- .shutdown = mtk_afe_i2s_shutdown,
- .prepare = mtk_afe_i2s_prepare,
-};
-
-static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
- .startup = mtk_afe_hdmi_startup,
- .shutdown = mtk_afe_hdmi_shutdown,
- .prepare = mtk_afe_hdmi_prepare,
- .trigger = mtk_afe_hdmi_trigger,
-
-};
-
-static int mtk_afe_runtime_suspend(struct device *dev);
-static int mtk_afe_runtime_resume(struct device *dev);
-
-static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
-{
- struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
- int i;
-
- dev_dbg(afe->dev, "%s\n", __func__);
- if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
- regmap_read(afe->regmap, mtk_afe_backup_list[i],
- &afe->backup_regs[i]);
-
- afe->suspended = true;
- mtk_afe_runtime_suspend(afe->dev);
- return 0;
-}
-
-static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
-{
- struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
- int i = 0;
-
- dev_dbg(afe->dev, "%s\n", __func__);
- if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
- return 0;
-
- mtk_afe_runtime_resume(afe->dev);
-
- for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
- regmap_write(afe->regmap, mtk_afe_backup_list[i],
- afe->backup_regs[i]);
-
- afe->suspended = false;
- return 0;
-}
-
-static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
- /* FE DAIs: memory intefaces to CPU */
- {
- .name = "DL1", /* downlink 1 */
- .id = MTK_AFE_MEMIF_DL1,
- .suspend = mtk_afe_dai_suspend,
- .resume = mtk_afe_dai_resume,
- .playback = {
- .stream_name = "DL1",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &mtk_afe_dai_ops,
- }, {
- .name = "VUL", /* voice uplink */
- .id = MTK_AFE_MEMIF_VUL,
- .suspend = mtk_afe_dai_suspend,
- .resume = mtk_afe_dai_resume,
- .capture = {
- .stream_name = "VUL",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &mtk_afe_dai_ops,
- }, {
- /* BE DAIs */
- .name = "I2S",
- .id = MTK_AFE_IO_I2S,
- .playback = {
- .stream_name = "I2S Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "I2S Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &mtk_afe_i2s_ops,
- .symmetric_rates = 1,
- },
-};
-
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
- /* FE DAIs */
- {
- .name = "HDMI",
- .id = MTK_AFE_MEMIF_HDMI,
- .suspend = mtk_afe_dai_suspend,
- .resume = mtk_afe_dai_resume,
- .playback = {
- .stream_name = "HDMI",
- .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,
- },
- .ops = &mtk_afe_dai_ops,
- }, {
- /* BE DAIs */
- .name = "HDMIO",
- .id = MTK_AFE_IO_HDMI,
- .playback = {
- .stream_name = "HDMIO 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,
- },
- .ops = &mtk_afe_hdmi_ops,
- },
-};
-
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
- SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
-};
-
-static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
- SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
-};
-
-static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
- SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
-};
-
-static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
- SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
-};
-
-static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
- /* inter-connections */
- SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
-
- SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
- mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
- SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
- mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
- SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
- mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
- SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
- mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
-};
-
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
- {"I05", NULL, "DL1"},
- {"I06", NULL, "DL1"},
- {"I2S Playback", NULL, "O03"},
- {"I2S Playback", NULL, "O04"},
- {"VUL", NULL, "O09"},
- {"VUL", NULL, "O10"},
- {"I17", NULL, "I2S Capture"},
- {"I18", NULL, "I2S Capture"},
- { "O03", "I05 Switch", "I05" },
- { "O04", "I06 Switch", "I06" },
- { "O09", "I17 Switch", "I17" },
- { "O10", "I18 Switch", "I18" },
-};
-
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
- {"HDMIO Playback", NULL, "HDMI"},
-};
-
-static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
- .name = "mtk-afe-pcm-dai",
- .dapm_widgets = mtk_afe_pcm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
- .dapm_routes = mtk_afe_pcm_routes,
- .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
-};
-
-static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = {
- .name = "mtk-afe-hdmi-dai",
- .dapm_routes = mtk_afe_hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes),
-};
-
-static const char *aud_clks[MTK_CLK_NUM] = {
- [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
- [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
- [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
- [MTK_CLK_I2S0_M] = "i2s0_m",
- [MTK_CLK_I2S1_M] = "i2s1_m",
- [MTK_CLK_I2S2_M] = "i2s2_m",
- [MTK_CLK_I2S3_M] = "i2s3_m",
- [MTK_CLK_I2S3_B] = "i2s3_b",
- [MTK_CLK_BCK0] = "bck0",
- [MTK_CLK_BCK1] = "bck1",
-};
-
-static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
- {
- .name = "DL1",
- .id = MTK_AFE_MEMIF_DL1,
- .reg_ofs_base = AFE_DL1_BASE,
- .reg_ofs_cur = AFE_DL1_CUR,
- .fs_shift = 0,
- .mono_shift = 21,
- .enable_shift = 1,
- .irq_reg_cnt = AFE_IRQ_CNT1,
- .irq_cnt_shift = 0,
- .irq_en_shift = 0,
- .irq_fs_shift = 4,
- .irq_clr_shift = 0,
- }, {
- .name = "DL2",
- .id = MTK_AFE_MEMIF_DL2,
- .reg_ofs_base = AFE_DL2_BASE,
- .reg_ofs_cur = AFE_DL2_CUR,
- .fs_shift = 4,
- .mono_shift = 22,
- .enable_shift = 2,
- .irq_reg_cnt = AFE_IRQ_CNT1,
- .irq_cnt_shift = 20,
- .irq_en_shift = 2,
- .irq_fs_shift = 16,
- .irq_clr_shift = 2,
- }, {
- .name = "VUL",
- .id = MTK_AFE_MEMIF_VUL,
- .reg_ofs_base = AFE_VUL_BASE,
- .reg_ofs_cur = AFE_VUL_CUR,
- .fs_shift = 16,
- .mono_shift = 27,
- .enable_shift = 3,
- .irq_reg_cnt = AFE_IRQ_CNT2,
- .irq_cnt_shift = 0,
- .irq_en_shift = 1,
- .irq_fs_shift = 8,
- .irq_clr_shift = 1,
- }, {
- .name = "DAI",
- .id = MTK_AFE_MEMIF_DAI,
- .reg_ofs_base = AFE_DAI_BASE,
- .reg_ofs_cur = AFE_DAI_CUR,
- .fs_shift = 24,
- .mono_shift = -1,
- .enable_shift = 4,
- .irq_reg_cnt = AFE_IRQ_CNT2,
- .irq_cnt_shift = 20,
- .irq_en_shift = 3,
- .irq_fs_shift = 20,
- .irq_clr_shift = 3,
- }, {
- .name = "AWB",
- .id = MTK_AFE_MEMIF_AWB,
- .reg_ofs_base = AFE_AWB_BASE,
- .reg_ofs_cur = AFE_AWB_CUR,
- .fs_shift = 12,
- .mono_shift = 24,
- .enable_shift = 6,
- .irq_reg_cnt = AFE_IRQ_CNT7,
- .irq_cnt_shift = 0,
- .irq_en_shift = 14,
- .irq_fs_shift = 24,
- .irq_clr_shift = 6,
- }, {
- .name = "MOD_DAI",
- .id = MTK_AFE_MEMIF_MOD_DAI,
- .reg_ofs_base = AFE_MOD_PCM_BASE,
- .reg_ofs_cur = AFE_MOD_PCM_CUR,
- .fs_shift = 30,
- .mono_shift = 30,
- .enable_shift = 7,
- .irq_reg_cnt = AFE_IRQ_CNT2,
- .irq_cnt_shift = 20,
- .irq_en_shift = 3,
- .irq_fs_shift = 20,
- .irq_clr_shift = 3,
- }, {
- .name = "HDMI",
- .id = MTK_AFE_MEMIF_HDMI,
- .reg_ofs_base = AFE_HDMI_OUT_BASE,
- .reg_ofs_cur = AFE_HDMI_OUT_CUR,
- .fs_shift = -1,
- .mono_shift = -1,
- .enable_shift = -1,
- .irq_reg_cnt = AFE_IRQ_CNT5,
- .irq_cnt_shift = 0,
- .irq_en_shift = 12,
- .irq_fs_shift = -1,
- .irq_clr_shift = 4,
- },
-};
-
-static const struct regmap_config mtk_afe_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = AFE_ADDA2_TOP_CON0,
- .cache_type = REGCACHE_NONE,
-};
-
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
-{
- struct mtk_afe *afe = dev_id;
- unsigned int reg_value;
- int i, ret;
-
- ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &reg_value);
- if (ret) {
- dev_err(afe->dev, "%s irq status err\n", __func__);
- reg_value = AFE_IRQ_STATUS_BITS;
- goto err_irq;
- }
-
- for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
- struct mtk_afe_memif *memif = &afe->memif[i];
-
- if (!(reg_value & (1 << memif->data->irq_clr_shift)))
- continue;
-
- snd_pcm_period_elapsed(memif->substream);
- }
-
-err_irq:
- /* clear irq */
- regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
-
- return IRQ_HANDLED;
-}
-
-static int mtk_afe_runtime_suspend(struct device *dev)
-{
- struct mtk_afe *afe = dev_get_drvdata(dev);
-
- /* disable AFE */
- regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
-
- /* disable AFE clk */
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
- AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-
- clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
- clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
- clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
- clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
- clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
- return 0;
-}
-
-static int mtk_afe_runtime_resume(struct device *dev)
-{
- struct mtk_afe *afe = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
- if (ret)
- goto err_infra;
-
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
- if (ret)
- goto err_top_aud_bus;
-
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
- if (ret)
- goto err_top_aud;
-
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
- if (ret)
- goto err_bck0;
-
- /* enable AFE clk */
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0);
-
- /* set O3/O4 16bits */
- regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
- AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0);
-
- /* unmask all IRQs */
- regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff);
-
- /* enable AFE */
- regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
- return 0;
-
-err_bck0:
- clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
-err_top_aud:
- clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
-err_top_aud_bus:
- clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
-err_infra:
- clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
- return ret;
-}
-
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
- afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe->clocks[i])) {
- dev_err(afe->dev, "%s devm_clk_get %s fail\n",
- __func__, aud_clks[i]);
- return PTR_ERR(afe->clocks[i]);
- }
- }
- clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
- clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
- return 0;
-}
-
-static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
-{
- int ret, i;
- unsigned int irq_id;
- struct mtk_afe *afe;
- struct resource *res;
-
- afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
- if (!afe)
- return -ENOMEM;
-
- afe->dev = &pdev->dev;
-
- irq_id = platform_get_irq(pdev, 0);
- if (!irq_id) {
- dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
- return -ENXIO;
- }
- ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
- 0, "Afe_ISR_Handle", (void *)afe);
- if (ret) {
- dev_err(afe->dev, "could not request_irq\n");
- return ret;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(afe->base_addr))
- return PTR_ERR(afe->base_addr);
-
- afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
- &mtk_afe_regmap_config);
- if (IS_ERR(afe->regmap))
- return PTR_ERR(afe->regmap);
-
- /* initial audio related clock */
- ret = mtk_afe_init_audio_clk(afe);
- if (ret) {
- dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
- return ret;
- }
-
- for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
- afe->memif[i].data = &memif_data[i];
-
- platform_set_drvdata(pdev, afe);
-
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- ret = mtk_afe_runtime_resume(&pdev->dev);
- if (ret)
- goto err_pm_disable;
- }
-
- ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
- if (ret)
- goto err_pm_disable;
-
- ret = snd_soc_register_component(&pdev->dev,
- &mtk_afe_pcm_dai_component,
- mtk_afe_pcm_dais,
- ARRAY_SIZE(mtk_afe_pcm_dais));
- if (ret)
- goto err_platform;
-
- ret = snd_soc_register_component(&pdev->dev,
- &mtk_afe_hdmi_dai_component,
- mtk_afe_hdmi_dais,
- ARRAY_SIZE(mtk_afe_hdmi_dais));
- if (ret)
- goto err_comp;
-
- dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
- return 0;
-
-err_comp:
- snd_soc_unregister_component(&pdev->dev);
-err_platform:
- snd_soc_unregister_platform(&pdev->dev);
-err_pm_disable:
- pm_runtime_disable(&pdev->dev);
- return ret;
-}
-
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- mtk_afe_runtime_suspend(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static const struct of_device_id mtk_afe_pcm_dt_match[] = {
- { .compatible = "mediatek,mt8173-afe-pcm", },
- { }
-};
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
-
-static const struct dev_pm_ops mtk_afe_pm_ops = {
- SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
- NULL)
-};
-
-static struct platform_driver mtk_afe_pcm_driver = {
- .driver = {
- .name = "mtk-afe-pcm",
- .of_match_table = mtk_afe_pcm_dt_match,
- .pm = &mtk_afe_pm_ops,
- },
- .probe = mtk_afe_pcm_dev_probe,
- .remove = mtk_afe_pcm_dev_remove,
-};
-
-module_platform_driver(mtk_afe_pcm_driver);
-
-MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
-MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
new file mode 100644
index 000000000000..d9a730994a2a
--- /dev/null
+++ b/sound/soc/meson/Kconfig
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Amlogic"
+ depends on ARCH_MESON || (COMPILE_TEST && COMMON_CLK)
+
+config SND_MESON_AIU
+ tristate "Amlogic AIU"
+ select SND_MESON_CODEC_GLUE
+ select SND_PCM_IEC958
+ imply SND_SOC_MESON_T9015
+ imply SND_SOC_HDMI_CODEC if DRM_MESON_DW_HDMI
+ help
+ Select Y or M to add support for the Audio output subsystem found
+ in the Amlogic Meson8, Meson8b and GX SoC families
+
+config SND_MESON_AXG_FIFO
+ tristate
+ select REGMAP_MMIO
+ imply COMMON_CLK_AXG_AUDIO
+ imply RESET_MESON_AUDIO_ARB
+
+config SND_MESON_AXG_FRDDR
+ tristate "Amlogic AXG Playback FIFO support"
+ select SND_MESON_AXG_FIFO
+ help
+ Select Y or M to add support for the frontend playback interfaces
+ embedded in the Amlogic AXG SoC family
+
+config SND_MESON_AXG_TODDR
+ tristate "Amlogic AXG Capture FIFO support"
+ select SND_MESON_AXG_FIFO
+ help
+ Select Y or M to add support for the frontend capture interfaces
+ embedded in the Amlogic AXG SoC family
+
+config SND_MESON_AXG_TDM_FORMATTER
+ tristate
+ select REGMAP_MMIO
+ imply COMMON_CLK_AXG_AUDIO
+
+config SND_MESON_AXG_TDM_INTERFACE
+ tristate
+ select SND_MESON_AXG_TDM_FORMATTER
+
+config SND_MESON_AXG_TDMIN
+ tristate "Amlogic AXG TDM Input Support"
+ select SND_MESON_AXG_TDM_FORMATTER
+ select SND_MESON_AXG_TDM_INTERFACE
+ help
+ Select Y or M to add support for TDM input formatter embedded
+ in the Amlogic AXG SoC family
+
+config SND_MESON_AXG_TDMOUT
+ tristate "Amlogic AXG TDM Output Support"
+ select SND_MESON_AXG_TDM_FORMATTER
+ select SND_MESON_AXG_TDM_INTERFACE
+ help
+ Select Y or M to add support for TDM output formatter embedded
+ in the Amlogic AXG SoC family
+
+config SND_MESON_AXG_SOUND_CARD
+ tristate "Amlogic AXG Sound Card Support"
+ select SND_MESON_AXG_TDM_INTERFACE
+ select SND_MESON_CARD_UTILS
+ imply SND_MESON_AXG_FRDDR
+ imply SND_MESON_AXG_TODDR
+ imply SND_MESON_AXG_TDMIN
+ imply SND_MESON_AXG_TDMOUT
+ imply SND_MESON_AXG_SPDIFOUT
+ imply SND_MESON_AXG_SPDIFIN
+ imply SND_MESON_AXG_PDM
+ imply SND_MESON_G12A_TOACODEC
+ imply SND_MESON_G12A_TOHDMITX if DRM_MESON_DW_HDMI
+ help
+ Select Y or M to add support for the AXG SoC sound card
+
+config SND_MESON_AXG_SPDIFOUT
+ tristate "Amlogic AXG SPDIF Output Support"
+ select SND_PCM_IEC958
+ imply SND_SOC_SPDIF
+ imply COMMON_CLK_AXG_AUDIO
+ help
+ Select Y or M to add support for SPDIF output serializer embedded
+ in the Amlogic AXG SoC family
+
+config SND_MESON_AXG_SPDIFIN
+ tristate "Amlogic AXG SPDIF Input Support"
+ imply SND_SOC_SPDIF
+ help
+ Select Y or M to add support for SPDIF input embedded
+ in the Amlogic AXG SoC family
+
+config SND_MESON_AXG_PDM
+ tristate "Amlogic AXG PDM Input Support"
+ imply SND_SOC_DMIC
+ imply COMMON_CLK_AXG_AUDIO
+ help
+ Select Y or M to add support for PDM input embedded
+ in the Amlogic AXG SoC family
+
+config SND_MESON_CARD_UTILS
+ tristate
+ select SND_DYNAMIC_MINORS
+
+config SND_MESON_CODEC_GLUE
+ tristate
+
+config SND_MESON_GX_SOUND_CARD
+ tristate "Amlogic GX Sound Card Support"
+ select SND_MESON_CARD_UTILS
+ imply SND_MESON_AIU
+ help
+ Select Y or M to add support for the GXBB/GXL SoC sound card
+
+config SND_MESON_G12A_TOACODEC
+ tristate "Amlogic G12A To Internal DAC Control Support"
+ select SND_MESON_CODEC_GLUE
+ select REGMAP_MMIO
+ imply SND_SOC_MESON_T9015
+ help
+ Select Y or M to add support for the internal audio DAC on the
+ g12a SoC family
+
+config SND_MESON_G12A_TOHDMITX
+ tristate "Amlogic G12A To HDMI TX Control Support"
+ select REGMAP_MMIO
+ select SND_MESON_CODEC_GLUE
+ imply SND_SOC_HDMI_CODEC
+ help
+ Select Y or M to add support for HDMI audio on the g12a SoC
+ family
+
+config SND_SOC_MESON_T9015
+ tristate "Amlogic T9015 DAC"
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for the internal DAC found
+ on GXL, G12 and SM1 SoC family.
+endmenu
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
new file mode 100644
index 000000000000..24078e4396b0
--- /dev/null
+++ b/sound/soc/meson/Makefile
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0 OR MIT)
+
+snd-soc-meson-aiu-y := aiu.o
+snd-soc-meson-aiu-y += aiu-acodec-ctrl.o
+snd-soc-meson-aiu-y += aiu-codec-ctrl.o
+snd-soc-meson-aiu-y += aiu-encoder-i2s.o
+snd-soc-meson-aiu-y += aiu-encoder-spdif.o
+snd-soc-meson-aiu-y += aiu-fifo.o
+snd-soc-meson-aiu-y += aiu-fifo-i2s.o
+snd-soc-meson-aiu-y += aiu-fifo-spdif.o
+snd-soc-meson-axg-fifo-y := axg-fifo.o
+snd-soc-meson-axg-frddr-y := axg-frddr.o
+snd-soc-meson-axg-toddr-y := axg-toddr.o
+snd-soc-meson-axg-tdm-formatter-y := axg-tdm-formatter.o
+snd-soc-meson-axg-tdm-interface-y := axg-tdm-interface.o
+snd-soc-meson-axg-tdmin-y := axg-tdmin.o
+snd-soc-meson-axg-tdmout-y := axg-tdmout.o
+snd-soc-meson-axg-sound-card-y := axg-card.o
+snd-soc-meson-axg-spdifin-y := axg-spdifin.o
+snd-soc-meson-axg-spdifout-y := axg-spdifout.o
+snd-soc-meson-axg-pdm-y := axg-pdm.o
+snd-soc-meson-card-utils-y := meson-card-utils.o
+snd-soc-meson-codec-glue-y := meson-codec-glue.o
+snd-soc-meson-gx-sound-card-y := gx-card.o
+snd-soc-meson-g12a-toacodec-y := g12a-toacodec.o
+snd-soc-meson-g12a-tohdmitx-y := g12a-tohdmitx.o
+snd-soc-meson-t9015-y := t9015.o
+
+obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o
+obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
+obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
+obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
+obj-$(CONFIG_SND_MESON_AXG_TDM_FORMATTER) += snd-soc-meson-axg-tdm-formatter.o
+obj-$(CONFIG_SND_MESON_AXG_TDM_INTERFACE) += snd-soc-meson-axg-tdm-interface.o
+obj-$(CONFIG_SND_MESON_AXG_TDMIN) += snd-soc-meson-axg-tdmin.o
+obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o
+obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o
+obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o
+obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o
+obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o
+obj-$(CONFIG_SND_MESON_CARD_UTILS) += snd-soc-meson-card-utils.o
+obj-$(CONFIG_SND_MESON_CODEC_GLUE) += snd-soc-meson-codec-glue.o
+obj-$(CONFIG_SND_MESON_GX_SOUND_CARD) += snd-soc-meson-gx-sound-card.o
+obj-$(CONFIG_SND_MESON_G12A_TOACODEC) += snd-soc-meson-g12a-toacodec.o
+obj-$(CONFIG_SND_MESON_G12A_TOHDMITX) += snd-soc-meson-g12a-tohdmitx.o
+obj-$(CONFIG_SND_SOC_MESON_T9015) += snd-soc-meson-t9015.o
diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c
new file mode 100644
index 000000000000..483772ba69cd
--- /dev/null
+++ b/sound/soc/meson/aiu-acodec-ctrl.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/meson-aiu.h>
+#include "aiu.h"
+#include "meson-codec-glue.h"
+
+#define CTRL_DIN_EN 15
+#define CTRL_CLK_INV BIT(14)
+#define CTRL_LRCLK_INV BIT(13)
+#define CTRL_I2S_IN_BCLK_SRC BIT(11)
+#define CTRL_DIN_LRCLK_SRC_SHIFT 6
+#define CTRL_DIN_LRCLK_SRC (0x3 << CTRL_DIN_LRCLK_SRC_SHIFT)
+#define CTRL_BCLK_MCLK_SRC GENMASK(5, 4)
+#define CTRL_DIN_SKEW GENMASK(3, 2)
+#define CTRL_I2S_OUT_LANE_SRC 0
+
+#define AIU_ACODEC_OUT_CHMAX 2
+
+static const char * const aiu_acodec_ctrl_mux_texts[] = {
+ "DISABLED", "I2S", "PCM",
+};
+
+static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, changed;
+
+ mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
+ changed = snd_soc_component_test_bits(component, e->reg,
+ CTRL_DIN_LRCLK_SRC,
+ FIELD_PREP(CTRL_DIN_LRCLK_SRC,
+ mux));
+
+ if (!changed)
+ return 0;
+
+ /* Force disconnect of the mux while updating */
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
+
+ snd_soc_component_update_bits(component, e->reg,
+ CTRL_DIN_LRCLK_SRC |
+ CTRL_BCLK_MCLK_SRC,
+ FIELD_PREP(CTRL_DIN_LRCLK_SRC, mux) |
+ FIELD_PREP(CTRL_BCLK_MCLK_SRC, mux));
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return 1;
+}
+
+static SOC_ENUM_SINGLE_DECL(aiu_acodec_ctrl_mux_enum, AIU_ACODEC_CTRL,
+ CTRL_DIN_LRCLK_SRC_SHIFT,
+ aiu_acodec_ctrl_mux_texts);
+
+static const struct snd_kcontrol_new aiu_acodec_ctrl_mux =
+ SOC_DAPM_ENUM_EXT("ACodec Source", aiu_acodec_ctrl_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ aiu_acodec_ctrl_mux_put_enum);
+
+static const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL,
+ CTRL_DIN_EN, 1, 0);
+
+static const struct snd_soc_dapm_widget aiu_acodec_ctrl_widgets[] = {
+ SND_SOC_DAPM_MUX("ACODEC SRC", SND_SOC_NOPM, 0, 0,
+ &aiu_acodec_ctrl_mux),
+ SND_SOC_DAPM_SWITCH("ACODEC OUT EN", SND_SOC_NOPM, 0, 0,
+ &aiu_acodec_ctrl_out_enable),
+};
+
+static int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct meson_codec_glue_input *data;
+ int ret;
+
+ ret = meson_codec_glue_input_hw_params(substream, params, dai);
+ if (ret)
+ return ret;
+
+ /* The glue will provide 1 lane out of the 4 to the output */
+ data = meson_codec_glue_input_get_data(dai);
+ data->params.channels_min = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX,
+ data->params.channels_min);
+ data->params.channels_max = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX,
+ data->params.channels_max);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = {
+ .probe = meson_codec_glue_input_dai_probe,
+ .remove = meson_codec_glue_input_dai_remove,
+ .hw_params = aiu_acodec_ctrl_input_hw_params,
+ .set_fmt = meson_codec_glue_input_set_fmt,
+};
+
+static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = {
+ .startup = meson_codec_glue_output_startup,
+};
+
+#define AIU_ACODEC_CTRL_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 AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \
+{ \
+ .stream_name = xname " " xsuffix, \
+ .channels_min = 1, \
+ .channels_max = (xchmax), \
+ .rate_min = 5512, \
+ .rate_max = 192000, \
+ .formats = AIU_ACODEC_CTRL_FORMATS, \
+}
+
+#define AIU_ACODEC_INPUT(xname) { \
+ .name = "ACODEC CTRL " xname, \
+ .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \
+ .ops = &aiu_acodec_ctrl_input_ops, \
+}
+
+#define AIU_ACODEC_OUTPUT(xname) { \
+ .name = "ACODEC CTRL " xname, \
+ .capture = AIU_ACODEC_STREAM(xname, "Capture", AIU_ACODEC_OUT_CHMAX), \
+ .ops = &aiu_acodec_ctrl_output_ops, \
+}
+
+static struct snd_soc_dai_driver aiu_acodec_ctrl_dai_drv[] = {
+ [CTRL_I2S] = AIU_ACODEC_INPUT("ACODEC I2S IN"),
+ [CTRL_PCM] = AIU_ACODEC_INPUT("ACODEC PCM IN"),
+ [CTRL_OUT] = AIU_ACODEC_OUTPUT("ACODEC OUT"),
+};
+
+static const struct snd_soc_dapm_route aiu_acodec_ctrl_routes[] = {
+ { "ACODEC SRC", "I2S", "ACODEC I2S IN Playback" },
+ { "ACODEC SRC", "PCM", "ACODEC PCM IN Playback" },
+ { "ACODEC OUT EN", "Switch", "ACODEC SRC" },
+ { "ACODEC OUT Capture", NULL, "ACODEC OUT EN" },
+};
+
+static const struct snd_kcontrol_new aiu_acodec_ctrl_controls[] = {
+ SOC_SINGLE("ACODEC I2S Lane Select", AIU_ACODEC_CTRL,
+ CTRL_I2S_OUT_LANE_SRC, 3, 0),
+};
+
+static int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC);
+}
+
+static int aiu_acodec_ctrl_component_probe(struct snd_soc_component *component)
+{
+ /*
+ * NOTE: Din Skew setting
+ * According to the documentation, the following update adds one delay
+ * to the din line. Without this, the output saturates. This happens
+ * regardless of the link format (i2s or left_j) so it is not clear what
+ * it actually does but it seems to be required
+ */
+ snd_soc_component_update_bits(component, AIU_ACODEC_CTRL,
+ CTRL_DIN_SKEW,
+ FIELD_PREP(CTRL_DIN_SKEW, 2));
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver aiu_acodec_ctrl_component = {
+ .name = "AIU Internal DAC Codec Control",
+ .probe = aiu_acodec_ctrl_component_probe,
+ .controls = aiu_acodec_ctrl_controls,
+ .num_controls = ARRAY_SIZE(aiu_acodec_ctrl_controls),
+ .dapm_widgets = aiu_acodec_ctrl_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aiu_acodec_ctrl_widgets),
+ .dapm_routes = aiu_acodec_ctrl_routes,
+ .num_dapm_routes = ARRAY_SIZE(aiu_acodec_ctrl_routes),
+ .of_xlate_dai_name = aiu_acodec_of_xlate_dai_name,
+ .endianness = 1,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "acodec",
+#endif
+};
+
+int aiu_acodec_ctrl_register_component(struct device *dev)
+{
+ return snd_soc_register_component(dev, &aiu_acodec_ctrl_component,
+ aiu_acodec_ctrl_dai_drv,
+ ARRAY_SIZE(aiu_acodec_ctrl_dai_drv));
+}
diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c
new file mode 100644
index 000000000000..396f815077e2
--- /dev/null
+++ b/sound/soc/meson/aiu-codec-ctrl.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/meson-aiu.h>
+#include "aiu.h"
+#include "meson-codec-glue.h"
+
+#define CTRL_CLK_SEL GENMASK(1, 0)
+#define CTRL_DATA_SEL_SHIFT 4
+#define CTRL_DATA_SEL (0x3 << CTRL_DATA_SEL_SHIFT)
+
+static const char * const aiu_codec_ctrl_mux_texts[] = {
+ "DISABLED", "PCM", "I2S",
+};
+
+static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, changed;
+
+ mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
+ changed = snd_soc_component_test_bits(component, e->reg,
+ CTRL_DATA_SEL,
+ FIELD_PREP(CTRL_DATA_SEL, mux));
+
+ if (!changed)
+ return 0;
+
+ /* Force disconnect of the mux while updating */
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
+
+ /* Reset the source first */
+ snd_soc_component_update_bits(component, e->reg,
+ CTRL_CLK_SEL |
+ CTRL_DATA_SEL,
+ FIELD_PREP(CTRL_CLK_SEL, 0) |
+ FIELD_PREP(CTRL_DATA_SEL, 0));
+
+ /* Set the appropriate source */
+ snd_soc_component_update_bits(component, e->reg,
+ CTRL_CLK_SEL |
+ CTRL_DATA_SEL,
+ FIELD_PREP(CTRL_CLK_SEL, mux) |
+ FIELD_PREP(CTRL_DATA_SEL, mux));
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return 1;
+}
+
+static SOC_ENUM_SINGLE_DECL(aiu_hdmi_ctrl_mux_enum, AIU_HDMI_CLK_DATA_CTRL,
+ CTRL_DATA_SEL_SHIFT,
+ aiu_codec_ctrl_mux_texts);
+
+static const struct snd_kcontrol_new aiu_hdmi_ctrl_mux =
+ SOC_DAPM_ENUM_EXT("HDMI Source", aiu_hdmi_ctrl_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ aiu_codec_ctrl_mux_put_enum);
+
+static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = {
+ SND_SOC_DAPM_MUX("HDMI CTRL SRC", SND_SOC_NOPM, 0, 0,
+ &aiu_hdmi_ctrl_mux),
+};
+
+static const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = {
+ .probe = meson_codec_glue_input_dai_probe,
+ .remove = meson_codec_glue_input_dai_remove,
+ .hw_params = meson_codec_glue_input_hw_params,
+ .set_fmt = meson_codec_glue_input_set_fmt,
+};
+
+static const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = {
+ .startup = meson_codec_glue_output_startup,
+};
+
+#define AIU_CODEC_CTRL_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 AIU_CODEC_CTRL_STREAM(xname, xsuffix) \
+{ \
+ .stream_name = xname " " xsuffix, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 5512, \
+ .rate_max = 192000, \
+ .formats = AIU_CODEC_CTRL_FORMATS, \
+}
+
+#define AIU_CODEC_CTRL_INPUT(xname) { \
+ .name = "CODEC CTRL " xname, \
+ .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"), \
+ .ops = &aiu_codec_ctrl_input_ops, \
+}
+
+#define AIU_CODEC_CTRL_OUTPUT(xname) { \
+ .name = "CODEC CTRL " xname, \
+ .capture = AIU_CODEC_CTRL_STREAM(xname, "Capture"), \
+ .ops = &aiu_codec_ctrl_output_ops, \
+}
+
+static struct snd_soc_dai_driver aiu_hdmi_ctrl_dai_drv[] = {
+ [CTRL_I2S] = AIU_CODEC_CTRL_INPUT("HDMI I2S IN"),
+ [CTRL_PCM] = AIU_CODEC_CTRL_INPUT("HDMI PCM IN"),
+ [CTRL_OUT] = AIU_CODEC_CTRL_OUTPUT("HDMI OUT"),
+};
+
+static const struct snd_soc_dapm_route aiu_hdmi_ctrl_routes[] = {
+ { "HDMI CTRL SRC", "I2S", "HDMI I2S IN Playback" },
+ { "HDMI CTRL SRC", "PCM", "HDMI PCM IN Playback" },
+ { "HDMI OUT Capture", NULL, "HDMI CTRL SRC" },
+};
+
+static int aiu_hdmi_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ return aiu_of_xlate_dai_name(component, args, dai_name, AIU_HDMI);
+}
+
+static const struct snd_soc_component_driver aiu_hdmi_ctrl_component = {
+ .name = "AIU HDMI Codec Control",
+ .dapm_widgets = aiu_hdmi_ctrl_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aiu_hdmi_ctrl_widgets),
+ .dapm_routes = aiu_hdmi_ctrl_routes,
+ .num_dapm_routes = ARRAY_SIZE(aiu_hdmi_ctrl_routes),
+ .of_xlate_dai_name = aiu_hdmi_of_xlate_dai_name,
+ .endianness = 1,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "hdmi",
+#endif
+};
+
+int aiu_hdmi_ctrl_register_component(struct device *dev)
+{
+ return snd_soc_register_component(dev, &aiu_hdmi_ctrl_component,
+ aiu_hdmi_ctrl_dai_drv,
+ ARRAY_SIZE(aiu_hdmi_ctrl_dai_drv));
+}
+
diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c
new file mode 100644
index 000000000000..3b4061508c18
--- /dev/null
+++ b/sound/soc/meson/aiu-encoder-i2s.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu.h"
+
+#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)
+#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)
+#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)
+#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11)
+#define AIU_RST_SOFT_I2S_FAST BIT(0)
+
+#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2)
+#define AIU_CLK_CTRL_I2S_DIV_EN BIT(0)
+#define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2)
+#define AIU_CLK_CTRL_AOCLK_INVERT BIT(6)
+#define AIU_CLK_CTRL_LRCLK_INVERT BIT(7)
+#define AIU_CLK_CTRL_LRCLK_SKEW GENMASK(9, 8)
+#define AIU_CLK_CTRL_MORE_HDMI_AMCLK BIT(6)
+#define AIU_CLK_CTRL_MORE_I2S_DIV GENMASK(5, 0)
+#define AIU_CODEC_DAC_LRCLK_CTRL_DIV GENMASK(11, 0)
+
+static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV_EN,
+ enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0);
+}
+
+static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ /* Always operate in split (classic interleaved) mode */
+ unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT;
+
+ /* Reset required to update the pipeline */
+ snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST);
+ snd_soc_component_read(component, AIU_I2S_SYNC);
+
+ switch (params_physical_width(params)) {
+ case 16: /* Nothing to do */
+ break;
+
+ case 32:
+ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT |
+ AIU_I2S_SOURCE_DESC_MODE_32BIT);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_channels(params)) {
+ case 2: /* Nothing to do */
+ break;
+ case 8:
+ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC,
+ AIU_I2S_SOURCE_DESC_MODE_8CH |
+ AIU_I2S_SOURCE_DESC_MODE_24BIT |
+ AIU_I2S_SOURCE_DESC_MODE_32BIT |
+ AIU_I2S_SOURCE_DESC_MODE_SPLIT,
+ desc);
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params,
+ unsigned int bs)
+{
+ switch (bs) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ /* These are the only valid legacy dividers */
+ break;
+
+ default:
+ dev_err(component->dev, "Unsupported i2s divider: %u\n", bs);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_I2S_DIV,
+ __ffs(bs)));
+
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
+ AIU_CLK_CTRL_MORE_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
+ 0));
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params,
+ unsigned int bs)
+{
+ /*
+ * NOTE: this HW is odd.
+ * In most configuration, the i2s divider is 'mclk / blck'.
+ * However, in 16 bits - 8ch mode, this factor needs to be
+ * increased by 50% to get the correct output rate.
+ * No idea why !
+ */
+ if (params_width(params) == 16 && params_channels(params) == 8) {
+ if (bs % 2) {
+ dev_err(component->dev,
+ "Cannot increase i2s divider by 50%%\n");
+ return -EINVAL;
+ }
+ bs += bs / 2;
+ }
+
+ /* Use CLK_MORE for mclk to bclk divider */
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 0));
+
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
+ AIU_CLK_CTRL_MORE_I2S_DIV,
+ FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
+ bs - 1));
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ unsigned int srate = params_rate(params);
+ unsigned int fs, bs;
+ int ret;
+
+ /* Get the oversampling factor */
+ fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate);
+
+ if (fs % 64)
+ return -EINVAL;
+
+ /* Send data MSB first */
+ snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG,
+ AIU_I2S_DAC_CFG_MSB_FIRST,
+ AIU_I2S_DAC_CFG_MSB_FIRST);
+
+ /* Set bclk to lrlck ratio */
+ snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL,
+ AIU_CODEC_DAC_LRCLK_CTRL_DIV,
+ FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
+ 64 - 1));
+
+ bs = fs / 64;
+
+ if (aiu->platform->has_clk_ctrl_more_i2s_div)
+ ret = aiu_encoder_i2s_set_more_div(component, params, bs);
+ else
+ ret = aiu_encoder_i2s_set_legacy_div(component, params, bs);
+
+ if (ret)
+ return ret;
+
+ /* Make sure amclk is used for HDMI i2s as well */
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
+ AIU_CLK_CTRL_MORE_HDMI_AMCLK,
+ AIU_CLK_CTRL_MORE_HDMI_AMCLK);
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ /* Disable the clock while changing the settings */
+ aiu_encoder_i2s_divider_enable(component, false);
+
+ ret = aiu_encoder_i2s_setup_desc(component, params);
+ if (ret) {
+ dev_err(dai->dev, "setting i2s desc failed\n");
+ return ret;
+ }
+
+ ret = aiu_encoder_i2s_set_clocks(component, params);
+ if (ret) {
+ dev_err(dai->dev, "setting i2s clocks failed\n");
+ return ret;
+ }
+
+ aiu_encoder_i2s_divider_enable(component, true);
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ aiu_encoder_i2s_divider_enable(component, false);
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+ unsigned int val = 0;
+ unsigned int skew;
+
+ /* Only CPU Master / Codec Slave supported ATM */
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP)
+ return -EINVAL;
+
+ if (inv == SND_SOC_DAIFMT_NB_IF ||
+ inv == SND_SOC_DAIFMT_IB_IF)
+ val |= AIU_CLK_CTRL_LRCLK_INVERT;
+
+ /*
+ * The SoC changes data on the rising edge of the bitclock
+ * so an inversion of the bitclock is required in normal mode
+ */
+ if (inv == SND_SOC_DAIFMT_NB_NF ||
+ inv == SND_SOC_DAIFMT_NB_IF)
+ val |= AIU_CLK_CTRL_AOCLK_INVERT;
+
+ /* Signal skew */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Invert sample clock for i2s */
+ val ^= AIU_CLK_CTRL_LRCLK_INVERT;
+ skew = 1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ skew = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew);
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_LRCLK_INVERT |
+ AIU_CLK_CTRL_AOCLK_INVERT |
+ AIU_CLK_CTRL_LRCLK_SKEW,
+ val);
+
+ return 0;
+}
+
+static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ if (WARN_ON(clk_id != 0))
+ return -EINVAL;
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq);
+ if (ret)
+ dev_err(dai->dev, "Failed to set sysclk to %uHz", freq);
+
+ return ret;
+}
+
+static const unsigned int hw_channels[] = {2, 8};
+static const struct snd_pcm_hw_constraint_list hw_channel_constraints = {
+ .list = hw_channels,
+ .count = ARRAY_SIZE(hw_channels),
+ .mask = 0,
+};
+
+static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ /* Make sure the encoder gets either 2 or 8 channels */
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_channel_constraints);
+ if (ret) {
+ dev_err(dai->dev, "adding channels constraints failed\n");
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks);
+ if (ret)
+ dev_err(dai->dev, "failed to enable i2s clocks\n");
+
+ return ret;
+}
+
+static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+
+ clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks);
+}
+
+const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = {
+ .hw_params = aiu_encoder_i2s_hw_params,
+ .hw_free = aiu_encoder_i2s_hw_free,
+ .set_fmt = aiu_encoder_i2s_set_fmt,
+ .set_sysclk = aiu_encoder_i2s_set_sysclk,
+ .startup = aiu_encoder_i2s_startup,
+ .shutdown = aiu_encoder_i2s_shutdown,
+};
diff --git a/sound/soc/meson/aiu-encoder-spdif.c b/sound/soc/meson/aiu-encoder-spdif.c
new file mode 100644
index 000000000000..97da60db2c4d
--- /dev/null
+++ b/sound/soc/meson/aiu-encoder-spdif.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu.h"
+
+#define AIU_958_MISC_NON_PCM BIT(0)
+#define AIU_958_MISC_MODE_16BITS BIT(1)
+#define AIU_958_MISC_16BITS_ALIGN GENMASK(6, 5)
+#define AIU_958_MISC_MODE_32BITS BIT(7)
+#define AIU_958_MISC_U_FROM_STREAM BIT(12)
+#define AIU_958_MISC_FORCE_LR BIT(13)
+#define AIU_958_CTRL_HOLD_EN BIT(0)
+#define AIU_CLK_CTRL_958_DIV_EN BIT(1)
+#define AIU_CLK_CTRL_958_DIV GENMASK(5, 4)
+#define AIU_CLK_CTRL_958_DIV_MORE BIT(12)
+
+#define AIU_CS_WORD_LEN 4
+#define AIU_958_INTERNAL_DIV 2
+
+static void
+aiu_encoder_spdif_divider_enable(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_958_DIV_EN,
+ enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
+}
+
+static void aiu_encoder_spdif_hold(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_update_bits(component, AIU_958_CTRL,
+ AIU_958_CTRL_HOLD_EN,
+ enable ? AIU_958_CTRL_HOLD_EN : 0);
+}
+
+static int
+aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ aiu_encoder_spdif_hold(component, false);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ aiu_encoder_spdif_hold(component, true);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ u8 cs[AIU_CS_WORD_LEN];
+ unsigned int val;
+ int ret;
+
+ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
+ AIU_CS_WORD_LEN);
+ if (ret < 0)
+ return ret;
+
+ /* Write the 1st half word */
+ val = cs[1] | cs[0] << 8;
+ snd_soc_component_write(component, AIU_958_CHSTAT_L0, val);
+ snd_soc_component_write(component, AIU_958_CHSTAT_R0, val);
+
+ /* Write the 2nd half word */
+ val = cs[3] | cs[2] << 8;
+ snd_soc_component_write(component, AIU_958_CHSTAT_L1, val);
+ snd_soc_component_write(component, AIU_958_CHSTAT_R1, val);
+
+ return 0;
+}
+
+static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mrate;
+ int ret;
+
+ /* Disable the clock while changing the settings */
+ aiu_encoder_spdif_divider_enable(component, false);
+
+ switch (params_physical_width(params)) {
+ case 16:
+ val |= AIU_958_MISC_MODE_16BITS;
+ val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2);
+ break;
+ case 32:
+ val |= AIU_958_MISC_MODE_32BITS;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported physical width\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AIU_958_MISC,
+ AIU_958_MISC_NON_PCM |
+ AIU_958_MISC_MODE_16BITS |
+ AIU_958_MISC_16BITS_ALIGN |
+ AIU_958_MISC_MODE_32BITS |
+ AIU_958_MISC_FORCE_LR |
+ AIU_958_MISC_U_FROM_STREAM,
+ val);
+
+ /* Set the stream channel status word */
+ ret = aiu_encoder_spdif_setup_cs_word(component, params);
+ if (ret) {
+ dev_err(dai->dev, "failed to set channel status word\n");
+ return ret;
+ }
+
+ snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_958_DIV |
+ AIU_CLK_CTRL_958_DIV_MORE,
+ FIELD_PREP(AIU_CLK_CTRL_958_DIV,
+ __ffs(AIU_958_INTERNAL_DIV)));
+
+ /* 2 * 32bits per subframe * 2 channels = 128 */
+ mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV;
+ ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate);
+ if (ret) {
+ dev_err(dai->dev, "failed to set mclk rate\n");
+ return ret;
+ }
+
+ aiu_encoder_spdif_divider_enable(component, true);
+
+ return 0;
+}
+
+static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ aiu_encoder_spdif_divider_enable(component, false);
+
+ return 0;
+}
+
+static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ /*
+ * NOTE: Make sure the spdif block is on its own divider.
+ *
+ * The spdif can be clocked by the i2s master clock or its own
+ * clock. We should (in theory) change the source depending on the
+ * origin of the data.
+ *
+ * However, considering the clocking scheme used on these platforms,
+ * the master clocks will pick the same PLL source when they are
+ * playing from the same FIFO. The clock should be in sync so, it
+ * should not be necessary to reparent the spdif master clock.
+ */
+ ret = clk_set_parent(aiu->spdif.clks[MCLK].clk,
+ aiu->spdif_mclk);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks);
+ if (ret)
+ dev_err(dai->dev, "failed to enable spdif clocks\n");
+
+ return ret;
+}
+
+static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+
+ clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks);
+}
+
+const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = {
+ .trigger = aiu_encoder_spdif_trigger,
+ .hw_params = aiu_encoder_spdif_hw_params,
+ .hw_free = aiu_encoder_spdif_hw_free,
+ .startup = aiu_encoder_spdif_startup,
+ .shutdown = aiu_encoder_spdif_shutdown,
+};
diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c
new file mode 100644
index 000000000000..eccbc16b293a
--- /dev/null
+++ b/sound/soc/meson/aiu-fifo-i2s.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu.h"
+#include "aiu-fifo.h"
+
+#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)
+#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)
+#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)
+#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11)
+#define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16)
+#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6)
+#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0)
+#define AIU_RST_SOFT_I2S_FAST BIT(0)
+#define AIU_I2S_MISC_HOLD_EN BIT(2)
+#define AIU_I2S_MISC_FORCE_LEFT_RIGHT BIT(4)
+
+#define AIU_FIFO_I2S_BLOCK 256
+
+static const struct snd_pcm_hardware fifo_i2s_pcm = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = AIU_FORMATS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .period_bytes_min = AIU_FIFO_I2S_BLOCK,
+ .period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+
+ /* No real justification for this */
+ .buffer_bytes_max = 1 * 1024 * 1024,
+};
+
+static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_write(component, AIU_RST_SOFT,
+ AIU_RST_SOFT_I2S_FAST);
+ snd_soc_component_read(component, AIU_I2S_SYNC);
+ break;
+ }
+
+ return aiu_fifo_trigger(substream, cmd, dai);
+}
+
+static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ ret = aiu_fifo_prepare(substream, dai);
+ if (ret)
+ return ret;
+
+ snd_soc_component_update_bits(component,
+ AIU_MEM_I2S_BUF_CNTL,
+ AIU_MEM_I2S_BUF_CNTL_INIT,
+ AIU_MEM_I2S_BUF_CNTL_INIT);
+ snd_soc_component_update_bits(component,
+ AIU_MEM_I2S_BUF_CNTL,
+ AIU_MEM_I2S_BUF_CNTL_INIT, 0);
+
+ return 0;
+}
+
+static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+ unsigned int val;
+ int ret;
+
+ snd_soc_component_update_bits(component, AIU_I2S_MISC,
+ AIU_I2S_MISC_HOLD_EN,
+ AIU_I2S_MISC_HOLD_EN);
+
+ ret = aiu_fifo_hw_params(substream, params, dai);
+ if (ret)
+ return ret;
+
+ switch (params_physical_width(params)) {
+ case 16:
+ val = AIU_MEM_I2S_CONTROL_MODE_16BIT;
+ break;
+ case 32:
+ val = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported physical width %u\n",
+ params_physical_width(params));
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,
+ AIU_MEM_I2S_CONTROL_MODE_16BIT,
+ val);
+
+ /* Setup the irq periodicity */
+ val = params_period_bytes(params) / fifo->fifo_block;
+ val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
+ snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,
+ AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
+
+ /*
+ * Most (all?) supported SoCs have this bit set by default. The vendor
+ * driver however sets it manually (depending on the version either
+ * while un-setting AIU_I2S_MISC_HOLD_EN or right before that). Follow
+ * the same approach for consistency with the vendor driver.
+ */
+ snd_soc_component_update_bits(component, AIU_I2S_MISC,
+ AIU_I2S_MISC_FORCE_LEFT_RIGHT,
+ AIU_I2S_MISC_FORCE_LEFT_RIGHT);
+
+ snd_soc_component_update_bits(component, AIU_I2S_MISC,
+ AIU_I2S_MISC_HOLD_EN, 0);
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = {
+ .pcm_new = aiu_fifo_pcm_new,
+ .probe = aiu_fifo_i2s_dai_probe,
+ .remove = aiu_fifo_dai_remove,
+ .trigger = aiu_fifo_i2s_trigger,
+ .prepare = aiu_fifo_i2s_prepare,
+ .hw_params = aiu_fifo_i2s_hw_params,
+ .startup = aiu_fifo_startup,
+ .shutdown = aiu_fifo_shutdown,
+};
+
+int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ struct aiu_fifo *fifo;
+ int ret;
+
+ ret = aiu_fifo_dai_probe(dai);
+ if (ret)
+ return ret;
+
+ fifo = snd_soc_dai_dma_data_get_playback(dai);
+
+ fifo->pcm = &fifo_i2s_pcm;
+ fifo->mem_offset = AIU_MEM_I2S_START;
+ fifo->fifo_block = AIU_FIFO_I2S_BLOCK;
+ fifo->pclk = aiu->i2s.clks[PCLK].clk;
+ fifo->irq = aiu->i2s.irq;
+
+ return 0;
+}
diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c
new file mode 100644
index 000000000000..e0e00ec026dc
--- /dev/null
+++ b/sound/soc/meson/aiu-fifo-spdif.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu.h"
+#include "aiu-fifo.h"
+
+#define AIU_IEC958_DCU_FF_CTRL_EN BIT(0)
+#define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1)
+#define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2)
+#define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2)
+#define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3)
+#define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4)
+#define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5)
+#define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6)
+#define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3)
+#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6)
+#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7)
+#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8)
+#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0)
+
+#define AIU_FIFO_SPDIF_BLOCK 8
+
+static const struct snd_pcm_hardware fifo_spdif_pcm = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = AIU_FORMATS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = AIU_FIFO_SPDIF_BLOCK,
+ .period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+
+ /* No real justification for this */
+ .buffer_bytes_max = 1 * 1024 * 1024,
+};
+
+static void fifo_spdif_dcu_enable(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
+ AIU_IEC958_DCU_FF_CTRL_EN,
+ enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0);
+}
+
+static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ ret = aiu_fifo_trigger(substream, cmd, dai);
+ if (ret)
+ return ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ fifo_spdif_dcu_enable(component, true);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ fifo_spdif_dcu_enable(component, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fifo_spdif_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ ret = aiu_fifo_prepare(substream, dai);
+ if (ret)
+ return ret;
+
+ snd_soc_component_update_bits(component,
+ AIU_MEM_IEC958_BUF_CNTL,
+ AIU_MEM_IEC958_BUF_CNTL_INIT,
+ AIU_MEM_IEC958_BUF_CNTL_INIT);
+ snd_soc_component_update_bits(component,
+ AIU_MEM_IEC958_BUF_CNTL,
+ AIU_MEM_IEC958_BUF_CNTL_INIT, 0);
+
+ return 0;
+}
+
+static int fifo_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val;
+ int ret;
+
+ ret = aiu_fifo_hw_params(substream, params, dai);
+ if (ret)
+ return ret;
+
+ val = AIU_MEM_IEC958_CONTROL_RD_DDR |
+ AIU_MEM_IEC958_CONTROL_MODE_LINEAR;
+
+ switch (params_physical_width(params)) {
+ case 16:
+ val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported physical width %u\n",
+ params_physical_width(params));
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL,
+ AIU_MEM_IEC958_CONTROL_ENDIAN |
+ AIU_MEM_IEC958_CONTROL_RD_DDR |
+ AIU_MEM_IEC958_CONTROL_MODE_LINEAR |
+ AIU_MEM_IEC958_CONTROL_MODE_16BIT,
+ val);
+
+ /* Number bytes read by the FIFO between each IRQ */
+ snd_soc_component_write(component, AIU_IEC958_BPF,
+ params_period_bytes(params));
+
+ /*
+ * AUTO_DISABLE and SYNC_HEAD are enabled by default but
+ * this should be disabled in PCM (uncompressed) mode
+ */
+ snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
+ AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE |
+ AIU_IEC958_DCU_FF_CTRL_IRQ_MODE |
+ AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN,
+ AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ);
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = {
+ .pcm_new = aiu_fifo_pcm_new,
+ .probe = aiu_fifo_spdif_dai_probe,
+ .remove = aiu_fifo_dai_remove,
+ .trigger = fifo_spdif_trigger,
+ .prepare = fifo_spdif_prepare,
+ .hw_params = fifo_spdif_hw_params,
+ .startup = aiu_fifo_startup,
+ .shutdown = aiu_fifo_shutdown,
+};
+
+int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ struct aiu_fifo *fifo;
+ int ret;
+
+ ret = aiu_fifo_dai_probe(dai);
+ if (ret)
+ return ret;
+
+ fifo = snd_soc_dai_dma_data_get_playback(dai);
+
+ fifo->pcm = &fifo_spdif_pcm;
+ fifo->mem_offset = AIU_MEM_IEC958_START;
+ fifo->fifo_block = 1;
+ fifo->pclk = aiu->spdif.clks[PCLK].clk;
+ fifo->irq = aiu->spdif.irq;
+
+ return 0;
+}
diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c
new file mode 100644
index 000000000000..b222bde1f61b
--- /dev/null
+++ b/sound/soc/meson/aiu-fifo.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu-fifo.h"
+
+#define AIU_MEM_START 0x00
+#define AIU_MEM_RD 0x04
+#define AIU_MEM_END 0x08
+#define AIU_MEM_MASKS 0x0c
+#define AIU_MEM_MASK_CH_RD GENMASK(7, 0)
+#define AIU_MEM_MASK_CH_MEM GENMASK(15, 8)
+#define AIU_MEM_CONTROL 0x10
+#define AIU_MEM_CONTROL_INIT BIT(0)
+#define AIU_MEM_CONTROL_FILL_EN BIT(1)
+#define AIU_MEM_CONTROL_EMPTY_EN BIT(2)
+
+static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(ss);
+
+ return snd_soc_rtd_to_cpu(rtd, 0);
+}
+
+snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_dai *dai = aiu_fifo_dai(substream);
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int addr;
+
+ addr = snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD);
+
+ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
+}
+
+static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+ unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
+ AIU_MEM_CONTROL_EMPTY_EN);
+
+ snd_soc_component_update_bits(component,
+ fifo->mem_offset + AIU_MEM_CONTROL,
+ en_mask, enable ? en_mask : 0);
+}
+
+int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ aiu_fifo_enable(dai, true);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ aiu_fifo_enable(dai, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int aiu_fifo_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+
+ snd_soc_component_update_bits(component,
+ fifo->mem_offset + AIU_MEM_CONTROL,
+ AIU_MEM_CONTROL_INIT,
+ AIU_MEM_CONTROL_INIT);
+ snd_soc_component_update_bits(component,
+ fifo->mem_offset + AIU_MEM_CONTROL,
+ AIU_MEM_CONTROL_INIT, 0);
+ return 0;
+}
+
+int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+ dma_addr_t end;
+
+ /* Setup the fifo boundaries */
+ end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block;
+ snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START,
+ runtime->dma_addr);
+ snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD,
+ runtime->dma_addr);
+ snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END,
+ end);
+
+ /* Setup the fifo to read all the memory - no skip */
+ snd_soc_component_update_bits(component,
+ fifo->mem_offset + AIU_MEM_MASKS,
+ AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM,
+ FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) |
+ FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff));
+
+ return 0;
+}
+
+static irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *playback = dev_id;
+
+ snd_pcm_period_elapsed(playback);
+
+ return IRQ_HANDLED;
+}
+
+int aiu_fifo_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, fifo->pcm);
+
+ /*
+ * Make sure the buffer and period size are multiple of the fifo burst
+ * size
+ */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ fifo->fifo_block);
+ if (ret)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ fifo->fifo_block);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fifo->pclk);
+ if (ret)
+ return ret;
+
+ ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev),
+ substream);
+ if (ret)
+ clk_disable_unprepare(fifo->pclk);
+
+ return ret;
+}
+
+void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+
+ free_irq(fifo->irq, substream);
+ clk_disable_unprepare(fifo->pclk);
+}
+
+int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+ size_t size = fifo->pcm->buffer_bytes_max;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+
+ return 0;
+}
+
+int aiu_fifo_dai_probe(struct snd_soc_dai *dai)
+{
+ struct aiu_fifo *fifo;
+
+ fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
+ if (!fifo)
+ return -ENOMEM;
+
+ snd_soc_dai_dma_data_set_playback(dai, fifo);
+
+ return 0;
+}
+
+int aiu_fifo_dai_remove(struct snd_soc_dai *dai)
+{
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+
+ kfree(fifo);
+
+ return 0;
+}
diff --git a/sound/soc/meson/aiu-fifo.h b/sound/soc/meson/aiu-fifo.h
new file mode 100644
index 000000000000..b02cfcc4de7f
--- /dev/null
+++ b/sound/soc/meson/aiu-fifo.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2020 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_AIU_FIFO_H
+#define _MESON_AIU_FIFO_H
+
+struct snd_pcm_hardware;
+struct snd_soc_component_driver;
+struct snd_soc_dai_driver;
+struct clk;
+struct snd_pcm_ops;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+struct snd_pcm_hw_params;
+struct platform_device;
+
+struct aiu_fifo {
+ const struct snd_pcm_hardware *pcm;
+ unsigned int mem_offset;
+ unsigned int fifo_block;
+ struct clk *pclk;
+ int irq;
+};
+
+int aiu_fifo_dai_probe(struct snd_soc_dai *dai);
+int aiu_fifo_dai_remove(struct snd_soc_dai *dai);
+
+snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+
+int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai);
+int aiu_fifo_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int aiu_fifo_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai);
+
+#endif /* _MESON_AIU_FIFO_H */
diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c
new file mode 100644
index 000000000000..f2890111c1d2
--- /dev/null
+++ b/sound/soc/meson/aiu.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/meson-aiu.h>
+#include "aiu.h"
+#include "aiu-fifo.h"
+
+#define AIU_I2S_MISC_958_SRC_SHIFT 3
+
+static const char * const aiu_spdif_encode_sel_texts[] = {
+ "SPDIF", "I2S",
+};
+
+static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC,
+ AIU_I2S_MISC_958_SRC_SHIFT,
+ aiu_spdif_encode_sel_texts);
+
+static const struct snd_kcontrol_new aiu_spdif_encode_mux =
+ SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum);
+
+static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0,
+ &aiu_spdif_encode_mux),
+};
+
+static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = {
+ { "I2S Encoder Playback", NULL, "I2S FIFO Playback" },
+ { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" },
+ { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" },
+ { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" },
+};
+
+int aiu_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name,
+ unsigned int component_id)
+{
+ struct snd_soc_dai *dai;
+ int id;
+
+ if (args->args_count != 2)
+ return -EINVAL;
+
+ if (args->args[0] != component_id)
+ return -EINVAL;
+
+ id = args->args[1];
+
+ if (id < 0 || id >= component->num_dai)
+ return -EINVAL;
+
+ for_each_component_dais(component, dai) {
+ if (id == 0)
+ break;
+ id--;
+ }
+
+ *dai_name = dai->driver->name;
+
+ return 0;
+}
+
+static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU);
+}
+
+static int aiu_cpu_component_probe(struct snd_soc_component *component)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+
+ /* Required for the SPDIF Source control operation */
+ return clk_prepare_enable(aiu->i2s.clks[PCLK].clk);
+}
+
+static void aiu_cpu_component_remove(struct snd_soc_component *component)
+{
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk);
+}
+
+static const struct snd_soc_component_driver aiu_cpu_component = {
+ .name = "AIU CPU",
+ .dapm_widgets = aiu_cpu_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aiu_cpu_dapm_widgets),
+ .dapm_routes = aiu_cpu_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aiu_cpu_dapm_routes),
+ .of_xlate_dai_name = aiu_cpu_of_xlate_dai_name,
+ .pointer = aiu_fifo_pointer,
+ .probe = aiu_cpu_component_probe,
+ .remove = aiu_cpu_component_remove,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "cpu",
+#endif
+};
+
+static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
+ [CPU_I2S_FIFO] = {
+ .name = "I2S FIFO",
+ .playback = {
+ .stream_name = "I2S FIFO Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = AIU_FORMATS,
+ },
+ .ops = &aiu_fifo_i2s_dai_ops,
+ },
+ [CPU_SPDIF_FIFO] = {
+ .name = "SPDIF FIFO",
+ .playback = {
+ .stream_name = "SPDIF FIFO Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = AIU_FORMATS,
+ },
+ .ops = &aiu_fifo_spdif_dai_ops,
+ },
+ [CPU_I2S_ENCODER] = {
+ .name = "I2S Encoder",
+ .playback = {
+ .stream_name = "I2S Encoder Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = AIU_FORMATS,
+ },
+ .ops = &aiu_encoder_i2s_dai_ops,
+ },
+ [CPU_SPDIF_ENCODER] = {
+ .name = "SPDIF Encoder",
+ .playback = {
+ .stream_name = "SPDIF Encoder Playback",
+ .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 = AIU_FORMATS,
+ },
+ .ops = &aiu_encoder_spdif_dai_ops,
+ }
+};
+
+static const struct regmap_config aiu_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x2ac,
+};
+
+static int aiu_clk_bulk_get(struct device *dev,
+ const char * const *ids,
+ unsigned int num,
+ struct aiu_interface *interface)
+{
+ struct clk_bulk_data *clks;
+ int i, ret;
+
+ clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL);
+ if (!clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ clks[i].id = ids[i];
+
+ ret = devm_clk_bulk_get(dev, num, clks);
+ if (ret < 0)
+ return ret;
+
+ interface->clks = clks;
+ interface->clk_num = num;
+ return 0;
+}
+
+static const char * const aiu_i2s_ids[] = {
+ [PCLK] = "i2s_pclk",
+ [AOCLK] = "i2s_aoclk",
+ [MCLK] = "i2s_mclk",
+ [MIXER] = "i2s_mixer",
+};
+
+static const char * const aiu_spdif_ids[] = {
+ [PCLK] = "spdif_pclk",
+ [AOCLK] = "spdif_aoclk",
+ [MCLK] = "spdif_mclk_sel"
+};
+
+static int aiu_clk_get(struct device *dev)
+{
+ struct aiu *aiu = dev_get_drvdata(dev);
+ struct clk *pclk;
+ int ret;
+
+ pclk = devm_clk_get_enabled(dev, "pclk");
+ if (IS_ERR(pclk))
+ return dev_err_probe(dev, PTR_ERR(pclk), "Can't get the aiu pclk\n");
+
+ aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
+ if (IS_ERR(aiu->spdif_mclk))
+ return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk),
+ "Can't get the aiu spdif master clock\n");
+
+ ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
+ &aiu->i2s);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't get the i2s clocks\n");
+
+ ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
+ &aiu->spdif);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't get the spdif clocks\n");
+
+ return ret;
+}
+
+static int aiu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *regs;
+ struct regmap *map;
+ struct aiu *aiu;
+ int ret;
+
+ aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL);
+ if (!aiu)
+ return -ENOMEM;
+
+ aiu->platform = device_get_match_data(dev);
+ if (!aiu->platform)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, aiu);
+
+ ret = device_reset(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s");
+ if (aiu->i2s.irq < 0)
+ return aiu->i2s.irq;
+
+ aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif");
+ if (aiu->spdif.irq < 0)
+ return aiu->spdif.irq;
+
+ ret = aiu_clk_get(dev);
+ if (ret)
+ return ret;
+
+ /* Register the cpu component of the aiu */
+ ret = snd_soc_register_component(dev, &aiu_cpu_component,
+ aiu_cpu_dai_drv,
+ ARRAY_SIZE(aiu_cpu_dai_drv));
+ if (ret) {
+ dev_err(dev, "Failed to register cpu component\n");
+ return ret;
+ }
+
+ /* Register the hdmi codec control component */
+ ret = aiu_hdmi_ctrl_register_component(dev);
+ if (ret) {
+ dev_err(dev, "Failed to register hdmi control component\n");
+ goto err;
+ }
+
+ /* Register the internal dac control component on gxl */
+ if (aiu->platform->has_acodec) {
+ ret = aiu_acodec_ctrl_register_component(dev);
+ if (ret) {
+ dev_err(dev,
+ "Failed to register acodec control component\n");
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ snd_soc_unregister_component(dev);
+ return ret;
+}
+
+static void aiu_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+}
+
+static const struct aiu_platform_data aiu_gxbb_pdata = {
+ .has_acodec = false,
+ .has_clk_ctrl_more_i2s_div = true,
+};
+
+static const struct aiu_platform_data aiu_gxl_pdata = {
+ .has_acodec = true,
+ .has_clk_ctrl_more_i2s_div = true,
+};
+
+static const struct aiu_platform_data aiu_meson8_pdata = {
+ .has_acodec = false,
+ .has_clk_ctrl_more_i2s_div = false,
+};
+
+static const struct of_device_id aiu_of_match[] = {
+ { .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata },
+ { .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata },
+ { .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata },
+ { .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aiu_of_match);
+
+static struct platform_driver aiu_pdrv = {
+ .probe = aiu_probe,
+ .remove = aiu_remove,
+ .driver = {
+ .name = "meson-aiu",
+ .of_match_table = aiu_of_match,
+ },
+};
+module_platform_driver(aiu_pdrv);
+
+MODULE_DESCRIPTION("Meson AIU Driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h
new file mode 100644
index 000000000000..0f94c8bf6081
--- /dev/null
+++ b/sound/soc/meson/aiu.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_AIU_H
+#define _MESON_AIU_H
+
+struct clk;
+struct clk_bulk_data;
+struct device;
+struct of_phandle_args;
+struct snd_soc_dai;
+struct snd_soc_dai_ops;
+
+enum aiu_clk_ids {
+ PCLK = 0,
+ AOCLK,
+ MCLK,
+ MIXER
+};
+
+struct aiu_interface {
+ struct clk_bulk_data *clks;
+ unsigned int clk_num;
+ int irq;
+};
+
+struct aiu_platform_data {
+ bool has_acodec;
+ bool has_clk_ctrl_more_i2s_div;
+};
+
+struct aiu {
+ struct clk *spdif_mclk;
+ struct aiu_interface i2s;
+ struct aiu_interface spdif;
+ const struct aiu_platform_data *platform;
+};
+
+#define AIU_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+int aiu_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name,
+ unsigned int component_id);
+
+int aiu_hdmi_ctrl_register_component(struct device *dev);
+int aiu_acodec_ctrl_register_component(struct device *dev);
+
+int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai);
+int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops;
+extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops;
+extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops;
+extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops;
+
+#define AIU_IEC958_BPF 0x000
+#define AIU_958_MISC 0x010
+#define AIU_IEC958_DCU_FF_CTRL 0x01c
+#define AIU_958_CHSTAT_L0 0x020
+#define AIU_958_CHSTAT_L1 0x024
+#define AIU_958_CTRL 0x028
+#define AIU_I2S_SOURCE_DESC 0x034
+#define AIU_I2S_DAC_CFG 0x040
+#define AIU_I2S_SYNC 0x044
+#define AIU_I2S_MISC 0x048
+#define AIU_RST_SOFT 0x054
+#define AIU_CLK_CTRL 0x058
+#define AIU_CLK_CTRL_MORE 0x064
+#define AIU_CODEC_DAC_LRCLK_CTRL 0x0a0
+#define AIU_HDMI_CLK_DATA_CTRL 0x0a8
+#define AIU_ACODEC_CTRL 0x0b0
+#define AIU_958_CHSTAT_R0 0x0c0
+#define AIU_958_CHSTAT_R1 0x0c4
+#define AIU_MEM_I2S_START 0x180
+#define AIU_MEM_I2S_MASKS 0x18c
+#define AIU_MEM_I2S_CONTROL 0x190
+#define AIU_MEM_IEC958_START 0x194
+#define AIU_MEM_IEC958_CONTROL 0x1a4
+#define AIU_MEM_I2S_BUF_CNTL 0x1d8
+#define AIU_MEM_IEC958_BUF_CNTL 0x1fc
+
+#endif /* _MESON_AIU_H */
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
new file mode 100644
index 000000000000..b4dca80e15e4
--- /dev/null
+++ b/sound/soc/meson/axg-card.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-tdm.h"
+#include "meson-card.h"
+
+struct axg_dai_link_tdm_mask {
+ u32 tx;
+ u32 rx;
+};
+
+struct axg_dai_link_tdm_data {
+ unsigned int mclk_fs;
+ unsigned int slots;
+ unsigned int slot_width;
+ u32 *tx_mask;
+ u32 *rx_mask;
+ struct axg_dai_link_tdm_mask *codec_masks;
+};
+
+/*
+ * Base params for the codec to codec links
+ * Those will be over-written by the CPU side of the link
+ */
+static const struct snd_soc_pcm_stream codec_params = {
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .rate_min = 5525,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+};
+
+static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct axg_dai_link_tdm_data *be =
+ (struct axg_dai_link_tdm_data *)priv->link_data[rtd->id];
+
+ return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs);
+}
+
+static const struct snd_soc_ops axg_card_tdm_be_ops = {
+ .hw_params = axg_card_tdm_be_hw_params,
+};
+
+static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct axg_dai_link_tdm_data *be =
+ (struct axg_dai_link_tdm_data *)priv->link_data[rtd->id];
+ struct snd_soc_dai *codec_dai;
+ int ret, i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ be->codec_masks[i].tx,
+ be->codec_masks[i].rx,
+ be->slots, be->slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev,
+ "setting tdm link slots failed\n");
+ return ret;
+ }
+ }
+
+ ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask,
+ be->slots, be->slot_width);
+ if (ret) {
+ dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct axg_dai_link_tdm_data *be =
+ (struct axg_dai_link_tdm_data *)priv->link_data[rtd->id];
+ int ret;
+
+ /* The loopback rx_mask is the pad tx_mask */
+ ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask,
+ be->slots, be->slot_width);
+ if (ret) {
+ dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
+ int *index)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *pad;
+ struct snd_soc_dai_link *lb;
+ struct snd_soc_dai_link_component *dlc;
+ int ret;
+
+ /* extend links */
+ ret = meson_card_reallocate_links(card, card->num_links + 1);
+ if (ret)
+ return ret;
+
+ pad = &card->dai_link[*index];
+ lb = &card->dai_link[*index + 1];
+
+ lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name);
+ if (!lb->name)
+ return -ENOMEM;
+
+ dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ lb->cpus = dlc;
+ lb->codecs = &snd_soc_dummy_dlc;
+ lb->num_cpus = 1;
+ lb->num_codecs = 1;
+
+ lb->stream_name = lb->name;
+ lb->cpus->of_node = pad->cpus->of_node;
+ lb->cpus->dai_name = "TDM Loopback";
+ lb->capture_only = 1;
+ lb->no_pcm = 1;
+ lb->ops = &axg_card_tdm_be_ops;
+ lb->init = axg_card_tdm_dai_lb_init;
+
+ /* Provide the same link data to the loopback */
+ priv->link_data[*index + 1] = priv->link_data[*index];
+
+ /*
+ * axg_card_clean_references() will iterate over this link,
+ * make sure the node count is balanced
+ */
+ of_node_get(lb->cpus->of_node);
+
+ /* Let add_links continue where it should */
+ *index += 1;
+
+ return 0;
+}
+
+static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ struct axg_dai_link_tdm_data *be)
+{
+ char propname[32];
+ u32 tx, rx;
+ int i;
+
+ be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
+ sizeof(*be->tx_mask), GFP_KERNEL);
+ be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
+ sizeof(*be->rx_mask), GFP_KERNEL);
+ if (!be->tx_mask || !be->rx_mask)
+ return -ENOMEM;
+
+ for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) {
+ snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i);
+ snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]);
+ tx = max(tx, be->tx_mask[i]);
+ }
+
+ /* Disable playback is the interface has no tx slots */
+ if (!tx)
+ link->capture_only = 1;
+
+ for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) {
+ snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i);
+ snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]);
+ rx = max(rx, be->rx_mask[i]);
+ }
+
+ /* Disable capture is the interface has no rx slots */
+ if (!rx)
+ link->playback_only = 1;
+
+ /* ... but the interface should at least have one direction */
+ if (!tx && !rx) {
+ dev_err(card->dev, "tdm link has no cpu slots\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32(node, "dai-tdm-slot-num", &be->slots);
+ if (!be->slots) {
+ /*
+ * If the slot number is not provided, set it such as it
+ * accommodates the largest mask
+ */
+ be->slots = fls(max(tx, rx));
+ } else if (be->slots < fls(max(tx, rx)) || be->slots > 32) {
+ /*
+ * Error if the slots can't accommodate the largest mask or
+ * if it is just too big
+ */
+ dev_err(card->dev, "bad slot number\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width);
+
+ return 0;
+}
+
+static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ struct axg_dai_link_tdm_data *be)
+{
+ struct axg_dai_link_tdm_mask *codec_mask;
+
+ codec_mask = devm_kcalloc(card->dev, link->num_codecs,
+ sizeof(*codec_mask), GFP_KERNEL);
+ if (!codec_mask)
+ return -ENOMEM;
+
+ be->codec_masks = codec_mask;
+
+ for_each_child_of_node_scoped(node, np) {
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask",
+ &codec_mask->rx);
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask",
+ &codec_mask->tx);
+
+ codec_mask++;
+ }
+
+ return 0;
+}
+
+static int axg_card_parse_tdm(struct snd_soc_card *card,
+ struct device_node *node,
+ int *index)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *link = &card->dai_link[*index];
+ struct axg_dai_link_tdm_data *be;
+ int ret;
+
+ /* Allocate tdm link parameters */
+ be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
+ if (!be)
+ return -ENOMEM;
+ priv->link_data[*index] = be;
+
+ /* Setup tdm link */
+ link->ops = &axg_card_tdm_be_ops;
+ link->init = axg_card_tdm_dai_init;
+ link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node);
+
+ of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
+
+ ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
+ if (ret) {
+ dev_err(card->dev, "error parsing tdm link slots\n");
+ return ret;
+ }
+
+ ret = axg_card_parse_codecs_masks(card, link, node, be);
+ if (ret)
+ return ret;
+
+ /* Add loopback if the pad dai has playback */
+ if (!link->capture_only) {
+ ret = axg_card_add_tdm_loopback(card, index);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int axg_card_cpu_is_capture_fe(struct device_node *np)
+{
+ return of_device_is_compatible(np, DT_PREFIX "axg-toddr");
+}
+
+static int axg_card_cpu_is_playback_fe(struct device_node *np)
+{
+ return of_device_is_compatible(np, DT_PREFIX "axg-frddr");
+}
+
+static int axg_card_cpu_is_tdm_iface(struct device_node *np)
+{
+ return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface");
+}
+
+static int axg_card_cpu_is_codec(struct device_node *np)
+{
+ return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") ||
+ of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
+}
+
+static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
+ int *index)
+{
+ struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
+ struct snd_soc_dai_link_component *cpu;
+ int ret;
+
+ cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
+ if (!cpu)
+ return -ENOMEM;
+
+ dai_link->cpus = cpu;
+ dai_link->num_cpus = 1;
+ dai_link->nonatomic = true;
+
+ ret = meson_card_parse_dai(card, np, dai_link->cpus);
+ if (ret)
+ return ret;
+
+ if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
+ return meson_card_set_fe_link(card, dai_link, np, true);
+ else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
+ return meson_card_set_fe_link(card, dai_link, np, false);
+
+
+ ret = meson_card_set_be_link(card, dai_link, np);
+ if (ret)
+ return ret;
+
+ if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
+ dai_link->c2c_params = &codec_params;
+ dai_link->num_c2c_params = 1;
+ } else {
+ dai_link->no_pcm = 1;
+ if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
+ ret = axg_card_parse_tdm(card, np, index);
+ }
+
+ return ret;
+}
+
+static const struct meson_card_match_data axg_card_match_data = {
+ .add_link = axg_card_add_link,
+};
+
+static const struct of_device_id axg_card_of_match[] = {
+ {
+ .compatible = "amlogic,axg-sound-card",
+ .data = &axg_card_match_data,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_card_of_match);
+
+static struct platform_driver axg_card_pdrv = {
+ .probe = meson_card_probe,
+ .remove = meson_card_remove,
+ .driver = {
+ .name = "axg-sound-card",
+ .of_match_table = axg_card_of_match,
+ },
+};
+module_platform_driver(axg_card_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
new file mode 100644
index 000000000000..75909196b769
--- /dev/null
+++ b/sound/soc/meson/axg-fifo.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-fifo.h"
+
+/*
+ * This file implements the platform operations common to the playback and
+ * capture frontend DAI. The logic behind this two types of fifo is very
+ * similar but some difference exist.
+ * These differences are handled in the respective DAI drivers
+ */
+
+static const struct snd_pcm_hardware axg_fifo_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+ .formats = AXG_FIFO_FORMATS,
+ .rate_min = 5512,
+ .rate_max = 768000,
+ .channels_min = 1,
+ .channels_max = AXG_FIFO_CH_MAX,
+ .period_bytes_min = AXG_FIFO_BURST,
+ .period_bytes_max = UINT_MAX,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+
+ /* No real justification for this */
+ .buffer_bytes_max = 1 * 1024 * 1024,
+};
+
+static struct snd_soc_dai *axg_fifo_dai(struct snd_pcm_substream *ss)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(ss);
+
+ return snd_soc_rtd_to_cpu(rtd, 0);
+}
+
+static struct axg_fifo *axg_fifo_data(struct snd_pcm_substream *ss)
+{
+ struct snd_soc_dai *dai = axg_fifo_dai(ss);
+
+ return snd_soc_dai_get_drvdata(dai);
+}
+
+static struct device *axg_fifo_dev(struct snd_pcm_substream *ss)
+{
+ struct snd_soc_dai *dai = axg_fifo_dai(ss);
+
+ return dai->dev;
+}
+
+static void __dma_enable(struct axg_fifo *fifo, bool enable)
+{
+ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_DMA_EN,
+ enable ? CTRL0_DMA_EN : 0);
+}
+
+int axg_fifo_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss, int cmd)
+{
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ __dma_enable(fifo, true);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ __dma_enable(fifo, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_trigger);
+
+snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
+{
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+ struct snd_pcm_runtime *runtime = ss->runtime;
+ unsigned int addr;
+
+ regmap_read(fifo->map, FIFO_STATUS2, &addr);
+
+ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_pointer);
+
+int axg_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = ss->runtime;
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+ unsigned int burst_num, period, threshold, irq_en;
+ dma_addr_t end_ptr;
+
+ period = params_period_bytes(params);
+
+ /* Setup dma memory pointers */
+ end_ptr = runtime->dma_addr + runtime->dma_bytes - AXG_FIFO_BURST;
+ regmap_write(fifo->map, FIFO_START_ADDR, runtime->dma_addr);
+ regmap_write(fifo->map, FIFO_FINISH_ADDR, end_ptr);
+
+ /* Setup interrupt periodicity */
+ burst_num = period / AXG_FIFO_BURST;
+ regmap_write(fifo->map, FIFO_INT_ADDR, burst_num);
+
+ /*
+ * Start the fifo request on the smallest of the following:
+ * - Half the fifo size
+ * - Half the period size
+ */
+ threshold = min(period / 2, fifo->depth / 2);
+
+ /*
+ * With the threshold in bytes, register value is:
+ * V = (threshold / burst) - 1
+ */
+ threshold /= AXG_FIFO_BURST;
+ regmap_field_write(fifo->field_threshold,
+ threshold ? threshold - 1 : 0);
+
+ /* Enable irq if necessary */
+ irq_en = runtime->no_period_wakeup ? 0 : FIFO_INT_COUNT_REPEAT;
+ regmap_update_bits(fifo->map, FIFO_CTRL0,
+ CTRL0_INT_EN,
+ FIELD_PREP(CTRL0_INT_EN, irq_en));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_params);
+
+int g12a_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params)
+{
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+ struct snd_pcm_runtime *runtime = ss->runtime;
+ int ret;
+
+ ret = axg_fifo_pcm_hw_params(component, ss, params);
+ if (ret)
+ return ret;
+
+ /* Set the initial memory address of the DMA */
+ regmap_write(fifo->map, FIFO_INIT_ADDR, runtime->dma_addr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(g12a_fifo_pcm_hw_params);
+
+int axg_fifo_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
+{
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+
+ /* Disable irqs */
+ regmap_update_bits(fifo->map, FIFO_CTRL0,
+ CTRL0_INT_EN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free);
+
+static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask)
+{
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_INT_CLR,
+ FIELD_PREP(CTRL1_INT_CLR, mask));
+
+ /* Clear must also be cleared */
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_INT_CLR,
+ FIELD_PREP(CTRL1_INT_CLR, 0));
+}
+
+static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *ss = dev_id;
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+ unsigned int status;
+
+ regmap_read(fifo->map, FIFO_STATUS1, &status);
+ status = FIELD_GET(STATUS1_INT_STS, status);
+ axg_fifo_ack_irq(fifo, status);
+
+ if (status & ~FIFO_INT_COUNT_REPEAT)
+ dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n",
+ status);
+
+ if (status & FIFO_INT_COUNT_REPEAT) {
+ snd_pcm_period_elapsed(ss);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+int axg_fifo_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
+{
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+ struct device *dev = axg_fifo_dev(ss);
+ int ret;
+
+ snd_soc_set_runtime_hwparams(ss, &axg_fifo_hw);
+
+ /*
+ * Make sure the buffer and period size are multiple of the FIFO
+ * burst
+ */
+ ret = snd_pcm_hw_constraint_step(ss->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ AXG_FIFO_BURST);
+ if (ret)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(ss->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ AXG_FIFO_BURST);
+ if (ret)
+ return ret;
+
+ /* Use the threaded irq handler only with non-atomic links */
+ ret = request_threaded_irq(fifo->irq, NULL,
+ axg_fifo_pcm_irq_block,
+ IRQF_ONESHOT, dev_name(dev), ss);
+ if (ret)
+ return ret;
+
+ /* Enable pclk to access registers and clock the fifo ip */
+ ret = clk_prepare_enable(fifo->pclk);
+ if (ret)
+ goto free_irq;
+
+ /* Setup status2 so it reports the memory pointer */
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_STATUS2_SEL,
+ FIELD_PREP(CTRL1_STATUS2_SEL, STATUS2_SEL_DDR_READ));
+
+ /* Make sure the dma is initially disabled */
+ __dma_enable(fifo, false);
+
+ /* Disable irqs until params are ready */
+ regmap_update_bits(fifo->map, FIFO_CTRL0,
+ CTRL0_INT_EN, 0);
+
+ /* Clear any pending interrupt */
+ axg_fifo_ack_irq(fifo, FIFO_INT_MASK);
+
+ /* Take memory arbitror out of reset */
+ ret = reset_control_deassert(fifo->arb);
+ if (ret)
+ goto free_clk;
+
+ return 0;
+
+free_clk:
+ clk_disable_unprepare(fifo->pclk);
+free_irq:
+ free_irq(fifo->irq, ss);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_open);
+
+int axg_fifo_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
+{
+ struct axg_fifo *fifo = axg_fifo_data(ss);
+ int ret;
+
+ /* Put the memory arbitror back in reset */
+ ret = reset_control_assert(fifo->arb);
+
+ /* Disable fifo ip and register access */
+ clk_disable_unprepare(fifo->pclk);
+
+ /* remove IRQ */
+ free_irq(fifo->irq, ss);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_close);
+
+int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ size_t size = axg_fifo_hw.buffer_bytes_max;
+
+ snd_pcm_set_managed_buffer(rtd->pcm->streams[type].substream,
+ SNDRV_DMA_TYPE_DEV, card->dev,
+ size, size);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(axg_fifo_pcm_new);
+
+static const struct regmap_config axg_fifo_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = FIFO_CTRL2,
+};
+
+int axg_fifo_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct axg_fifo_match_data *data;
+ struct axg_fifo *fifo;
+ void __iomem *regs;
+ int ret;
+
+ data = of_device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL);
+ if (!fifo)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, fifo);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ fifo->map = devm_regmap_init_mmio(dev, regs, &axg_fifo_regmap_cfg);
+ if (IS_ERR(fifo->map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(fifo->map));
+ return PTR_ERR(fifo->map);
+ }
+
+ fifo->pclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(fifo->pclk))
+ return dev_err_probe(dev, PTR_ERR(fifo->pclk), "failed to get pclk\n");
+
+ fifo->arb = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(fifo->arb))
+ return dev_err_probe(dev, PTR_ERR(fifo->arb), "failed to get arb reset\n");
+
+ fifo->irq = of_irq_get(dev->of_node, 0);
+ if (fifo->irq <= 0) {
+ dev_err(dev, "failed to get irq: %d\n", fifo->irq);
+ return fifo->irq;
+ }
+
+ fifo->field_threshold =
+ devm_regmap_field_alloc(dev, fifo->map, data->field_threshold);
+ if (IS_ERR(fifo->field_threshold))
+ return PTR_ERR(fifo->field_threshold);
+
+ ret = of_property_read_u32(dev->of_node, "amlogic,fifo-depth",
+ &fifo->depth);
+ if (ret) {
+ /* Error out for anything but a missing property */
+ if (ret != -EINVAL)
+ return ret;
+ /*
+ * If the property is missing, it might be because of an old
+ * DT. In such case, assume the smallest known fifo depth
+ */
+ fifo->depth = 256;
+ dev_warn(dev, "fifo depth not found, assume %u bytes\n",
+ fifo->depth);
+ }
+
+ return devm_snd_soc_register_component(dev, data->component_drv,
+ data->dai_drv, 1);
+}
+EXPORT_SYMBOL_GPL(axg_fifo_probe);
+
+MODULE_DESCRIPTION("Amlogic AXG/G12A fifo driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h
new file mode 100644
index 000000000000..4c48c0a08481
--- /dev/null
+++ b/sound/soc/meson/axg-fifo.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_AXG_FIFO_H
+#define _MESON_AXG_FIFO_H
+
+struct clk;
+struct platform_device;
+struct reg_field;
+struct regmap;
+struct regmap_field;
+struct reset_control;
+
+struct snd_soc_component_driver;
+struct snd_soc_dai;
+struct snd_soc_dai_driver;
+
+struct snd_soc_pcm_runtime;
+
+#define AXG_FIFO_CH_MAX 128
+#define AXG_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+#define AXG_FIFO_BURST 8
+
+#define FIFO_INT_ADDR_FINISH BIT(0)
+#define FIFO_INT_ADDR_INT BIT(1)
+#define FIFO_INT_COUNT_REPEAT BIT(2)
+#define FIFO_INT_COUNT_ONCE BIT(3)
+#define FIFO_INT_FIFO_ZERO BIT(4)
+#define FIFO_INT_FIFO_DEPTH BIT(5)
+#define FIFO_INT_MASK GENMASK(7, 0)
+
+#define FIFO_CTRL0 0x00
+#define CTRL0_DMA_EN BIT(31)
+#define CTRL0_INT_EN GENMASK(23, 16)
+#define CTRL0_SEL_MASK GENMASK(2, 0)
+#define CTRL0_SEL_SHIFT 0
+#define FIFO_CTRL1 0x04
+#define CTRL1_INT_CLR GENMASK(7, 0)
+#define CTRL1_STATUS2_SEL GENMASK(11, 8)
+#define STATUS2_SEL_DDR_READ 0
+#define CTRL1_FRDDR_DEPTH GENMASK(31, 24)
+#define FIFO_START_ADDR 0x08
+#define FIFO_FINISH_ADDR 0x0c
+#define FIFO_INT_ADDR 0x10
+#define FIFO_STATUS1 0x14
+#define STATUS1_INT_STS GENMASK(7, 0)
+#define FIFO_STATUS2 0x18
+#define FIFO_INIT_ADDR 0x24
+#define FIFO_CTRL2 0x28
+
+struct axg_fifo {
+ struct regmap *map;
+ struct clk *pclk;
+ struct reset_control *arb;
+ struct regmap_field *field_threshold;
+ unsigned int depth;
+ int irq;
+};
+
+struct axg_fifo_match_data {
+ const struct snd_soc_component_driver *component_drv;
+ struct snd_soc_dai_driver *dai_drv;
+ struct reg_field field_threshold;
+};
+
+int axg_fifo_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+int axg_fifo_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+int axg_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params);
+int g12a_fifo_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss,
+ struct snd_pcm_hw_params *params);
+int axg_fifo_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss);
+int axg_fifo_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss, int cmd);
+
+int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type);
+int axg_fifo_probe(struct platform_device *pdev);
+
+#endif /* _MESON_AXG_FIFO_H */
diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c
new file mode 100644
index 000000000000..e70c8c34c7db
--- /dev/null
+++ b/sound/soc/meson/axg-frddr.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+/*
+ * This driver implements the frontend playback DAI of AXG and G12A based SoCs
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-fifo.h"
+
+#define CTRL0_FRDDR_PP_MODE BIT(30)
+#define CTRL0_SEL1_EN_SHIFT 3
+#define CTRL0_SEL2_SHIFT 4
+#define CTRL0_SEL2_EN_SHIFT 7
+#define CTRL0_SEL3_SHIFT 8
+#define CTRL0_SEL3_EN_SHIFT 11
+#define CTRL1_FRDDR_FORCE_FINISH BIT(12)
+#define CTRL2_SEL1_SHIFT 0
+#define CTRL2_SEL1_EN_SHIFT 4
+#define CTRL2_SEL2_SHIFT 8
+#define CTRL2_SEL2_EN_SHIFT 12
+#define CTRL2_SEL3_SHIFT 16
+#define CTRL2_SEL3_EN_SHIFT 20
+
+static int g12a_frddr_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+
+ /* Reset the read pointer to the FIFO_INIT_ADDR */
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_FRDDR_FORCE_FINISH, 0);
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_FRDDR_FORCE_FINISH, CTRL1_FRDDR_FORCE_FINISH);
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_FRDDR_FORCE_FINISH, 0);
+
+ return 0;
+}
+
+static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+ unsigned int period, depth, val;
+
+ period = params_period_bytes(params);
+
+ /* Trim the FIFO depth if the period is small to improve latency */
+ depth = min(period, fifo->depth);
+ val = (depth / AXG_FIFO_BURST) - 1;
+ regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH,
+ FIELD_PREP(CTRL1_FRDDR_DEPTH, val));
+
+ return 0;
+}
+
+static int axg_frddr_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* Enable pclk to access registers and clock the fifo ip */
+ ret = clk_prepare_enable(fifo->pclk);
+ if (ret)
+ return ret;
+
+ /* Apply single buffer mode to the interface */
+ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_FRDDR_PP_MODE, 0);
+
+ return 0;
+}
+
+static void axg_frddr_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(fifo->pclk);
+}
+
+static int axg_frddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static const struct snd_soc_dai_ops axg_frddr_ops = {
+ .hw_params = axg_frddr_dai_hw_params,
+ .startup = axg_frddr_dai_startup,
+ .shutdown = axg_frddr_dai_shutdown,
+ .pcm_new = axg_frddr_pcm_new,
+};
+
+static struct snd_soc_dai_driver axg_frddr_dai_drv = {
+ .name = "FRDDR",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = AXG_FIFO_CH_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5515,
+ .rate_max = 768000,
+ .formats = AXG_FIFO_FORMATS,
+ },
+ .ops = &axg_frddr_ops,
+};
+
+static const char * const axg_frddr_sel_texts[] = {
+ "OUT 0", "OUT 1", "OUT 2", "OUT 3", "OUT 4", "OUT 5", "OUT 6", "OUT 7",
+};
+
+static SOC_ENUM_SINGLE_DECL(axg_frddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
+ axg_frddr_sel_texts);
+
+static const struct snd_kcontrol_new axg_frddr_out_demux =
+ SOC_DAPM_ENUM("Output Sink", axg_frddr_sel_enum);
+
+static const struct snd_soc_dapm_widget axg_frddr_dapm_widgets[] = {
+ SND_SOC_DAPM_DEMUX("SINK SEL", SND_SOC_NOPM, 0, 0,
+ &axg_frddr_out_demux),
+ SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route axg_frddr_dapm_routes[] = {
+ { "SINK SEL", NULL, "Playback" },
+ { "OUT 0", "OUT 0", "SINK SEL" },
+ { "OUT 1", "OUT 1", "SINK SEL" },
+ { "OUT 2", "OUT 2", "SINK SEL" },
+ { "OUT 3", "OUT 3", "SINK SEL" },
+ { "OUT 4", "OUT 4", "SINK SEL" },
+ { "OUT 5", "OUT 5", "SINK SEL" },
+ { "OUT 6", "OUT 6", "SINK SEL" },
+ { "OUT 7", "OUT 7", "SINK SEL" },
+};
+
+static const struct snd_soc_component_driver axg_frddr_component_drv = {
+ .dapm_widgets = axg_frddr_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets),
+ .dapm_routes = axg_frddr_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes),
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .hw_params = axg_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
+ .legacy_dai_naming = 1,
+};
+
+static const struct axg_fifo_match_data axg_frddr_match_data = {
+ .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
+ .component_drv = &axg_frddr_component_drv,
+ .dai_drv = &axg_frddr_dai_drv
+};
+
+static const struct snd_soc_dai_ops g12a_frddr_ops = {
+ .prepare = g12a_frddr_dai_prepare,
+ .hw_params = axg_frddr_dai_hw_params,
+ .startup = axg_frddr_dai_startup,
+ .shutdown = axg_frddr_dai_shutdown,
+ .pcm_new = axg_frddr_pcm_new,
+};
+
+static struct snd_soc_dai_driver g12a_frddr_dai_drv = {
+ .name = "FRDDR",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = AXG_FIFO_CH_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5515,
+ .rate_max = 768000,
+ .formats = AXG_FIFO_FORMATS,
+ },
+ .ops = &g12a_frddr_ops,
+};
+
+static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel1_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
+ axg_frddr_sel_texts);
+static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel2_enum, FIFO_CTRL0, CTRL0_SEL2_SHIFT,
+ axg_frddr_sel_texts);
+static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel3_enum, FIFO_CTRL0, CTRL0_SEL3_SHIFT,
+ axg_frddr_sel_texts);
+
+static const struct snd_kcontrol_new g12a_frddr_out1_demux =
+ SOC_DAPM_ENUM("Output Src 1", g12a_frddr_sel1_enum);
+static const struct snd_kcontrol_new g12a_frddr_out2_demux =
+ SOC_DAPM_ENUM("Output Src 2", g12a_frddr_sel2_enum);
+static const struct snd_kcontrol_new g12a_frddr_out3_demux =
+ SOC_DAPM_ENUM("Output Src 3", g12a_frddr_sel3_enum);
+
+static const struct snd_kcontrol_new g12a_frddr_out1_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
+ CTRL0_SEL1_EN_SHIFT, 1, 0);
+static const struct snd_kcontrol_new g12a_frddr_out2_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
+ CTRL0_SEL2_EN_SHIFT, 1, 0);
+static const struct snd_kcontrol_new g12a_frddr_out3_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
+ CTRL0_SEL3_EN_SHIFT, 1, 0);
+
+static const struct snd_soc_dapm_widget g12a_frddr_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0,
+ &g12a_frddr_out1_enable),
+ SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0,
+ &g12a_frddr_out2_enable),
+ SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0,
+ &g12a_frddr_out3_enable),
+ SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0,
+ &g12a_frddr_out1_demux),
+ SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0,
+ &g12a_frddr_out2_demux),
+ SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0,
+ &g12a_frddr_out3_demux),
+ SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route g12a_frddr_dapm_routes[] = {
+ { "SRC 1", NULL, "Playback" },
+ { "SRC 2", NULL, "Playback" },
+ { "SRC 3", NULL, "Playback" },
+ { "SRC 1 EN", "Switch", "SRC 1" },
+ { "SRC 2 EN", "Switch", "SRC 2" },
+ { "SRC 3 EN", "Switch", "SRC 3" },
+ { "SINK 1 SEL", NULL, "SRC 1 EN" },
+ { "SINK 2 SEL", NULL, "SRC 2 EN" },
+ { "SINK 3 SEL", NULL, "SRC 3 EN" },
+ { "OUT 0", "OUT 0", "SINK 1 SEL" },
+ { "OUT 1", "OUT 1", "SINK 1 SEL" },
+ { "OUT 2", "OUT 2", "SINK 1 SEL" },
+ { "OUT 3", "OUT 3", "SINK 1 SEL" },
+ { "OUT 4", "OUT 4", "SINK 1 SEL" },
+ { "OUT 5", "OUT 5", "SINK 1 SEL" },
+ { "OUT 6", "OUT 6", "SINK 1 SEL" },
+ { "OUT 7", "OUT 7", "SINK 1 SEL" },
+ { "OUT 0", "OUT 0", "SINK 2 SEL" },
+ { "OUT 1", "OUT 1", "SINK 2 SEL" },
+ { "OUT 2", "OUT 2", "SINK 2 SEL" },
+ { "OUT 3", "OUT 3", "SINK 2 SEL" },
+ { "OUT 4", "OUT 4", "SINK 2 SEL" },
+ { "OUT 5", "OUT 5", "SINK 2 SEL" },
+ { "OUT 6", "OUT 6", "SINK 2 SEL" },
+ { "OUT 7", "OUT 7", "SINK 2 SEL" },
+ { "OUT 0", "OUT 0", "SINK 3 SEL" },
+ { "OUT 1", "OUT 1", "SINK 3 SEL" },
+ { "OUT 2", "OUT 2", "SINK 3 SEL" },
+ { "OUT 3", "OUT 3", "SINK 3 SEL" },
+ { "OUT 4", "OUT 4", "SINK 3 SEL" },
+ { "OUT 5", "OUT 5", "SINK 3 SEL" },
+ { "OUT 6", "OUT 6", "SINK 3 SEL" },
+ { "OUT 7", "OUT 7", "SINK 3 SEL" },
+};
+
+static const struct snd_soc_component_driver g12a_frddr_component_drv = {
+ .dapm_widgets = g12a_frddr_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(g12a_frddr_dapm_widgets),
+ .dapm_routes = g12a_frddr_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes),
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
+ .legacy_dai_naming = 1,
+};
+
+static const struct axg_fifo_match_data g12a_frddr_match_data = {
+ .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
+ .component_drv = &g12a_frddr_component_drv,
+ .dai_drv = &g12a_frddr_dai_drv
+};
+
+/* On SM1, the output selection in on CTRL2 */
+static const struct snd_kcontrol_new sm1_frddr_out1_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
+ CTRL2_SEL1_EN_SHIFT, 1, 0);
+static const struct snd_kcontrol_new sm1_frddr_out2_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
+ CTRL2_SEL2_EN_SHIFT, 1, 0);
+static const struct snd_kcontrol_new sm1_frddr_out3_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
+ CTRL2_SEL3_EN_SHIFT, 1, 0);
+
+static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel1_enum, FIFO_CTRL2, CTRL2_SEL1_SHIFT,
+ axg_frddr_sel_texts);
+static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel2_enum, FIFO_CTRL2, CTRL2_SEL2_SHIFT,
+ axg_frddr_sel_texts);
+static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel3_enum, FIFO_CTRL2, CTRL2_SEL3_SHIFT,
+ axg_frddr_sel_texts);
+
+static const struct snd_kcontrol_new sm1_frddr_out1_demux =
+ SOC_DAPM_ENUM("Output Src 1", sm1_frddr_sel1_enum);
+static const struct snd_kcontrol_new sm1_frddr_out2_demux =
+ SOC_DAPM_ENUM("Output Src 2", sm1_frddr_sel2_enum);
+static const struct snd_kcontrol_new sm1_frddr_out3_demux =
+ SOC_DAPM_ENUM("Output Src 3", sm1_frddr_sel3_enum);
+
+static const struct snd_soc_dapm_widget sm1_frddr_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0,
+ &sm1_frddr_out1_enable),
+ SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0,
+ &sm1_frddr_out2_enable),
+ SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0,
+ &sm1_frddr_out3_enable),
+ SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0,
+ &sm1_frddr_out1_demux),
+ SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0,
+ &sm1_frddr_out2_demux),
+ SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0,
+ &sm1_frddr_out3_demux),
+ SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_component_driver sm1_frddr_component_drv = {
+ .dapm_widgets = sm1_frddr_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sm1_frddr_dapm_widgets),
+ .dapm_routes = g12a_frddr_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes),
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
+ .legacy_dai_naming = 1,
+};
+
+static const struct axg_fifo_match_data sm1_frddr_match_data = {
+ .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
+ .component_drv = &sm1_frddr_component_drv,
+ .dai_drv = &g12a_frddr_dai_drv
+};
+
+static const struct of_device_id axg_frddr_of_match[] = {
+ {
+ .compatible = "amlogic,axg-frddr",
+ .data = &axg_frddr_match_data,
+ }, {
+ .compatible = "amlogic,g12a-frddr",
+ .data = &g12a_frddr_match_data,
+ }, {
+ .compatible = "amlogic,sm1-frddr",
+ .data = &sm1_frddr_match_data,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_frddr_of_match);
+
+static struct platform_driver axg_frddr_pdrv = {
+ .probe = axg_fifo_probe,
+ .driver = {
+ .name = "axg-frddr",
+ .of_match_table = axg_frddr_of_match,
+ },
+};
+module_platform_driver(axg_frddr_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG/G12A playback fifo driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c
new file mode 100644
index 000000000000..d59050914d3c
--- /dev/null
+++ b/sound/soc/meson/axg-pdm.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm_params.h>
+
+#define PDM_CTRL 0x00
+#define PDM_CTRL_EN BIT(31)
+#define PDM_CTRL_OUT_MODE BIT(29)
+#define PDM_CTRL_BYPASS_MODE BIT(28)
+#define PDM_CTRL_RST_FIFO BIT(16)
+#define PDM_CTRL_CHAN_RSTN_MASK GENMASK(15, 8)
+#define PDM_CTRL_CHAN_RSTN(x) ((x) << 8)
+#define PDM_CTRL_CHAN_EN_MASK GENMASK(7, 0)
+#define PDM_CTRL_CHAN_EN(x) ((x) << 0)
+#define PDM_HCIC_CTRL1 0x04
+#define PDM_FILTER_EN BIT(31)
+#define PDM_HCIC_CTRL1_GAIN_SFT_MASK GENMASK(29, 24)
+#define PDM_HCIC_CTRL1_GAIN_SFT(x) ((x) << 24)
+#define PDM_HCIC_CTRL1_GAIN_MULT_MASK GENMASK(23, 16)
+#define PDM_HCIC_CTRL1_GAIN_MULT(x) ((x) << 16)
+#define PDM_HCIC_CTRL1_DSR_MASK GENMASK(8, 4)
+#define PDM_HCIC_CTRL1_DSR(x) ((x) << 4)
+#define PDM_HCIC_CTRL1_STAGE_NUM_MASK GENMASK(3, 0)
+#define PDM_HCIC_CTRL1_STAGE_NUM(x) ((x) << 0)
+#define PDM_HCIC_CTRL2 0x08
+#define PDM_F1_CTRL 0x0c
+#define PDM_LPF_ROUND_MODE_MASK GENMASK(17, 16)
+#define PDM_LPF_ROUND_MODE(x) ((x) << 16)
+#define PDM_LPF_DSR_MASK GENMASK(15, 12)
+#define PDM_LPF_DSR(x) ((x) << 12)
+#define PDM_LPF_STAGE_NUM_MASK GENMASK(8, 0)
+#define PDM_LPF_STAGE_NUM(x) ((x) << 0)
+#define PDM_LPF_MAX_STAGE 336
+#define PDM_LPF_NUM 3
+#define PDM_F2_CTRL 0x10
+#define PDM_F3_CTRL 0x14
+#define PDM_HPF_CTRL 0x18
+#define PDM_HPF_SFT_STEPS_MASK GENMASK(20, 16)
+#define PDM_HPF_SFT_STEPS(x) ((x) << 16)
+#define PDM_HPF_OUT_FACTOR_MASK GENMASK(15, 0)
+#define PDM_HPF_OUT_FACTOR(x) ((x) << 0)
+#define PDM_CHAN_CTRL 0x1c
+#define PDM_CHAN_CTRL_POINTER_WIDTH 8
+#define PDM_CHAN_CTRL_POINTER_MAX ((1 << PDM_CHAN_CTRL_POINTER_WIDTH) - 1)
+#define PDM_CHAN_CTRL_NUM 4
+#define PDM_CHAN_CTRL1 0x20
+#define PDM_COEFF_ADDR 0x24
+#define PDM_COEFF_DATA 0x28
+#define PDM_CLKG_CTRL 0x2c
+#define PDM_STS 0x30
+
+struct axg_pdm_lpf {
+ unsigned int ds;
+ unsigned int round_mode;
+ const unsigned int *tap;
+ unsigned int tap_num;
+};
+
+struct axg_pdm_hcic {
+ unsigned int shift;
+ unsigned int mult;
+ unsigned int steps;
+ unsigned int ds;
+};
+
+struct axg_pdm_hpf {
+ unsigned int out_factor;
+ unsigned int steps;
+};
+
+struct axg_pdm_filters {
+ struct axg_pdm_hcic hcic;
+ struct axg_pdm_hpf hpf;
+ struct axg_pdm_lpf lpf[PDM_LPF_NUM];
+};
+
+struct axg_pdm_cfg {
+ const struct axg_pdm_filters *filters;
+ unsigned int sys_rate;
+};
+
+struct axg_pdm {
+ const struct axg_pdm_cfg *cfg;
+ struct regmap *map;
+ struct clk *dclk;
+ struct clk *sysclk;
+ struct clk *pclk;
+};
+
+static void axg_pdm_enable(struct regmap *map)
+{
+ /* Reset AFIFO */
+ regmap_update_bits(map, PDM_CTRL, PDM_CTRL_RST_FIFO, PDM_CTRL_RST_FIFO);
+ regmap_update_bits(map, PDM_CTRL, PDM_CTRL_RST_FIFO, 0);
+
+ /* Enable PDM */
+ regmap_update_bits(map, PDM_CTRL, PDM_CTRL_EN, PDM_CTRL_EN);
+}
+
+static void axg_pdm_disable(struct regmap *map)
+{
+ regmap_update_bits(map, PDM_CTRL, PDM_CTRL_EN, 0);
+}
+
+static void axg_pdm_filters_enable(struct regmap *map, bool enable)
+{
+ unsigned int val = enable ? PDM_FILTER_EN : 0;
+
+ regmap_update_bits(map, PDM_HCIC_CTRL1, PDM_FILTER_EN, val);
+ regmap_update_bits(map, PDM_F1_CTRL, PDM_FILTER_EN, val);
+ regmap_update_bits(map, PDM_F2_CTRL, PDM_FILTER_EN, val);
+ regmap_update_bits(map, PDM_F3_CTRL, PDM_FILTER_EN, val);
+ regmap_update_bits(map, PDM_HPF_CTRL, PDM_FILTER_EN, val);
+}
+
+static int axg_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axg_pdm *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ axg_pdm_enable(priv->map);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ axg_pdm_disable(priv->map);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned int axg_pdm_get_os(struct axg_pdm *priv)
+{
+ const struct axg_pdm_filters *filters = priv->cfg->filters;
+ unsigned int os = filters->hcic.ds;
+ int i;
+
+ /*
+ * The global oversampling factor is defined by the down sampling
+ * factor applied by each filter (HCIC and LPFs)
+ */
+
+ for (i = 0; i < PDM_LPF_NUM; i++)
+ os *= filters->lpf[i].ds;
+
+ return os;
+}
+
+static int axg_pdm_set_sysclk(struct axg_pdm *priv, unsigned int os,
+ unsigned int rate)
+{
+ unsigned int sys_rate = os * 2 * rate * PDM_CHAN_CTRL_POINTER_MAX;
+
+ /*
+ * Set the default system clock rate unless it is too fast for
+ * the requested sample rate. In this case, the sample pointer
+ * counter could overflow so set a lower system clock rate
+ */
+ if (sys_rate < priv->cfg->sys_rate)
+ return clk_set_rate(priv->sysclk, sys_rate);
+
+ return clk_set_rate(priv->sysclk, priv->cfg->sys_rate);
+}
+
+static int axg_pdm_set_sample_pointer(struct axg_pdm *priv)
+{
+ unsigned int spmax, sp, val;
+ int i;
+
+ /* Max sample counter value per half period of dclk */
+ spmax = DIV_ROUND_UP_ULL((u64)clk_get_rate(priv->sysclk),
+ clk_get_rate(priv->dclk) * 2);
+
+ /* Check if sysclk is not too fast - should not happen */
+ if (WARN_ON(spmax > PDM_CHAN_CTRL_POINTER_MAX))
+ return -EINVAL;
+
+ /* Capture the data when we are at 75% of the half period */
+ sp = spmax * 3 / 4;
+
+ for (i = 0, val = 0; i < PDM_CHAN_CTRL_NUM; i++)
+ val |= sp << (PDM_CHAN_CTRL_POINTER_WIDTH * i);
+
+ regmap_write(priv->map, PDM_CHAN_CTRL, val);
+ regmap_write(priv->map, PDM_CHAN_CTRL1, val);
+
+ return 0;
+}
+
+static void axg_pdm_set_channel_mask(struct axg_pdm *priv,
+ unsigned int channels)
+{
+ unsigned int mask = GENMASK(channels - 1, 0);
+
+ /* Put all channel in reset */
+ regmap_update_bits(priv->map, PDM_CTRL,
+ PDM_CTRL_CHAN_RSTN_MASK, 0);
+
+ /* Take the necessary channels out of reset and enable them */
+ regmap_update_bits(priv->map, PDM_CTRL,
+ PDM_CTRL_CHAN_RSTN_MASK |
+ PDM_CTRL_CHAN_EN_MASK,
+ PDM_CTRL_CHAN_RSTN(mask) |
+ PDM_CTRL_CHAN_EN(mask));
+}
+
+static int axg_pdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_pdm *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int os = axg_pdm_get_os(priv);
+ unsigned int rate = params_rate(params);
+ unsigned int val;
+ int ret;
+
+ switch (params_width(params)) {
+ case 24:
+ val = PDM_CTRL_OUT_MODE;
+ break;
+ case 32:
+ val = 0;
+ break;
+ default:
+ dev_err(dai->dev, "unsupported sample width\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->map, PDM_CTRL, PDM_CTRL_OUT_MODE, val);
+
+ ret = axg_pdm_set_sysclk(priv, os, rate);
+ if (ret) {
+ dev_err(dai->dev, "failed to set system clock\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(priv->dclk, rate * os);
+ if (ret) {
+ dev_err(dai->dev, "failed to set dclk\n");
+ return ret;
+ }
+
+ ret = axg_pdm_set_sample_pointer(priv);
+ if (ret) {
+ dev_err(dai->dev, "invalid clock setting\n");
+ return ret;
+ }
+
+ axg_pdm_set_channel_mask(priv, params_channels(params));
+
+ return 0;
+}
+
+static int axg_pdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_pdm *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = clk_prepare_enable(priv->dclk);
+ if (ret) {
+ dev_err(dai->dev, "enabling dclk failed\n");
+ return ret;
+ }
+
+ /* Enable the filters */
+ axg_pdm_filters_enable(priv->map, true);
+
+ return ret;
+}
+
+static void axg_pdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_pdm *priv = snd_soc_dai_get_drvdata(dai);
+
+ axg_pdm_filters_enable(priv->map, false);
+ clk_disable_unprepare(priv->dclk);
+}
+
+static void axg_pdm_set_hcic_ctrl(struct axg_pdm *priv)
+{
+ const struct axg_pdm_hcic *hcic = &priv->cfg->filters->hcic;
+ unsigned int val;
+
+ val = PDM_HCIC_CTRL1_STAGE_NUM(hcic->steps);
+ val |= PDM_HCIC_CTRL1_DSR(hcic->ds);
+ val |= PDM_HCIC_CTRL1_GAIN_MULT(hcic->mult);
+ val |= PDM_HCIC_CTRL1_GAIN_SFT(hcic->shift);
+
+ regmap_update_bits(priv->map, PDM_HCIC_CTRL1,
+ PDM_HCIC_CTRL1_STAGE_NUM_MASK |
+ PDM_HCIC_CTRL1_DSR_MASK |
+ PDM_HCIC_CTRL1_GAIN_MULT_MASK |
+ PDM_HCIC_CTRL1_GAIN_SFT_MASK,
+ val);
+}
+
+static void axg_pdm_set_lpf_ctrl(struct axg_pdm *priv, unsigned int index)
+{
+ const struct axg_pdm_lpf *lpf = &priv->cfg->filters->lpf[index];
+ unsigned int offset = index * regmap_get_reg_stride(priv->map)
+ + PDM_F1_CTRL;
+ unsigned int val;
+
+ val = PDM_LPF_STAGE_NUM(lpf->tap_num);
+ val |= PDM_LPF_DSR(lpf->ds);
+ val |= PDM_LPF_ROUND_MODE(lpf->round_mode);
+
+ regmap_update_bits(priv->map, offset,
+ PDM_LPF_STAGE_NUM_MASK |
+ PDM_LPF_DSR_MASK |
+ PDM_LPF_ROUND_MODE_MASK,
+ val);
+}
+
+static void axg_pdm_set_hpf_ctrl(struct axg_pdm *priv)
+{
+ const struct axg_pdm_hpf *hpf = &priv->cfg->filters->hpf;
+ unsigned int val;
+
+ val = PDM_HPF_OUT_FACTOR(hpf->out_factor);
+ val |= PDM_HPF_SFT_STEPS(hpf->steps);
+
+ regmap_update_bits(priv->map, PDM_HPF_CTRL,
+ PDM_HPF_OUT_FACTOR_MASK |
+ PDM_HPF_SFT_STEPS_MASK,
+ val);
+}
+
+static int axg_pdm_set_lpf_filters(struct axg_pdm *priv)
+{
+ const struct axg_pdm_lpf *lpf = priv->cfg->filters->lpf;
+ unsigned int count = 0;
+ int i, j;
+
+ for (i = 0; i < PDM_LPF_NUM; i++)
+ count += lpf[i].tap_num;
+
+ /* Make sure the coeffs fit in the memory */
+ if (count >= PDM_LPF_MAX_STAGE)
+ return -EINVAL;
+
+ /* Set the initial APB bus register address */
+ regmap_write(priv->map, PDM_COEFF_ADDR, 0);
+
+ /* Set the tap filter values of all 3 filters */
+ for (i = 0; i < PDM_LPF_NUM; i++) {
+ axg_pdm_set_lpf_ctrl(priv, i);
+
+ for (j = 0; j < lpf[i].tap_num; j++)
+ regmap_write(priv->map, PDM_COEFF_DATA, lpf[i].tap[j]);
+ }
+
+ return 0;
+}
+
+static int axg_pdm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct axg_pdm *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret) {
+ dev_err(dai->dev, "enabling pclk failed\n");
+ return ret;
+ }
+
+ /*
+ * sysclk must be set and enabled as well to access the pdm registers
+ * Accessing the register w/o it will give a bus error.
+ */
+ ret = clk_set_rate(priv->sysclk, priv->cfg->sys_rate);
+ if (ret) {
+ dev_err(dai->dev, "setting sysclk failed\n");
+ goto err_pclk;
+ }
+
+ ret = clk_prepare_enable(priv->sysclk);
+ if (ret) {
+ dev_err(dai->dev, "enabling sysclk failed\n");
+ goto err_pclk;
+ }
+
+ /* Make sure the device is initially disabled */
+ axg_pdm_disable(priv->map);
+
+ /* Make sure filter bypass is disabled */
+ regmap_update_bits(priv->map, PDM_CTRL, PDM_CTRL_BYPASS_MODE, 0);
+
+ /* Load filter settings */
+ axg_pdm_set_hcic_ctrl(priv);
+ axg_pdm_set_hpf_ctrl(priv);
+
+ ret = axg_pdm_set_lpf_filters(priv);
+ if (ret) {
+ dev_err(dai->dev, "invalid filter configuration\n");
+ goto err_sysclk;
+ }
+
+ return 0;
+
+err_sysclk:
+ clk_disable_unprepare(priv->sysclk);
+err_pclk:
+ clk_disable_unprepare(priv->pclk);
+ return ret;
+}
+
+static int axg_pdm_dai_remove(struct snd_soc_dai *dai)
+{
+ struct axg_pdm *priv = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(priv->sysclk);
+ clk_disable_unprepare(priv->pclk);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops axg_pdm_dai_ops = {
+ .probe = axg_pdm_dai_probe,
+ .remove = axg_pdm_dai_remove,
+ .trigger = axg_pdm_trigger,
+ .hw_params = axg_pdm_hw_params,
+ .startup = axg_pdm_startup,
+ .shutdown = axg_pdm_shutdown,
+};
+
+static struct snd_soc_dai_driver axg_pdm_dai_drv = {
+ .name = "PDM",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 48000,
+ .formats = (SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .ops = &axg_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver axg_pdm_component_drv = {
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config axg_pdm_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = PDM_STS,
+};
+
+static const unsigned int lpf1_default_tap[] = {
+ 0x000014, 0xffffb2, 0xfffed9, 0xfffdce, 0xfffd45,
+ 0xfffe32, 0x000147, 0x000645, 0x000b86, 0x000e21,
+ 0x000ae3, 0x000000, 0xffeece, 0xffdca8, 0xffd212,
+ 0xffd7d1, 0xfff2a7, 0x001f4c, 0x0050c2, 0x0072aa,
+ 0x006ff1, 0x003c32, 0xffdc4e, 0xff6a18, 0xff0fef,
+ 0xfefbaf, 0xff4c40, 0x000000, 0x00ebc8, 0x01c077,
+ 0x02209e, 0x01c1a4, 0x008e60, 0xfebe52, 0xfcd690,
+ 0xfb8fa5, 0xfba498, 0xfd9812, 0x0181ce, 0x06f5f3,
+ 0x0d112f, 0x12a958, 0x169686, 0x18000e, 0x169686,
+ 0x12a958, 0x0d112f, 0x06f5f3, 0x0181ce, 0xfd9812,
+ 0xfba498, 0xfb8fa5, 0xfcd690, 0xfebe52, 0x008e60,
+ 0x01c1a4, 0x02209e, 0x01c077, 0x00ebc8, 0x000000,
+ 0xff4c40, 0xfefbaf, 0xff0fef, 0xff6a18, 0xffdc4e,
+ 0x003c32, 0x006ff1, 0x0072aa, 0x0050c2, 0x001f4c,
+ 0xfff2a7, 0xffd7d1, 0xffd212, 0xffdca8, 0xffeece,
+ 0x000000, 0x000ae3, 0x000e21, 0x000b86, 0x000645,
+ 0x000147, 0xfffe32, 0xfffd45, 0xfffdce, 0xfffed9,
+ 0xffffb2, 0x000014,
+};
+
+static const unsigned int lpf2_default_tap[] = {
+ 0x00050a, 0xfff004, 0x0002c1, 0x003c12, 0xffa818,
+ 0xffc87d, 0x010aef, 0xff5223, 0xfebd93, 0x028f41,
+ 0xff5c0e, 0xfc63f8, 0x055f81, 0x000000, 0xf478a0,
+ 0x11c5e3, 0x2ea74d, 0x11c5e3, 0xf478a0, 0x000000,
+ 0x055f81, 0xfc63f8, 0xff5c0e, 0x028f41, 0xfebd93,
+ 0xff5223, 0x010aef, 0xffc87d, 0xffa818, 0x003c12,
+ 0x0002c1, 0xfff004, 0x00050a,
+};
+
+static const unsigned int lpf3_default_tap[] = {
+ 0x000000, 0x000081, 0x000000, 0xfffedb, 0x000000,
+ 0x00022d, 0x000000, 0xfffc46, 0x000000, 0x0005f7,
+ 0x000000, 0xfff6eb, 0x000000, 0x000d4e, 0x000000,
+ 0xffed1e, 0x000000, 0x001a1c, 0x000000, 0xffdcb0,
+ 0x000000, 0x002ede, 0x000000, 0xffc2d1, 0x000000,
+ 0x004ebe, 0x000000, 0xff9beb, 0x000000, 0x007dd7,
+ 0x000000, 0xff633a, 0x000000, 0x00c1d2, 0x000000,
+ 0xff11d5, 0x000000, 0x012368, 0x000000, 0xfe9c45,
+ 0x000000, 0x01b252, 0x000000, 0xfdebf6, 0x000000,
+ 0x0290b8, 0x000000, 0xfcca0d, 0x000000, 0x041d7c,
+ 0x000000, 0xfa8152, 0x000000, 0x07e9c6, 0x000000,
+ 0xf28fb5, 0x000000, 0x28b216, 0x3fffde, 0x28b216,
+ 0x000000, 0xf28fb5, 0x000000, 0x07e9c6, 0x000000,
+ 0xfa8152, 0x000000, 0x041d7c, 0x000000, 0xfcca0d,
+ 0x000000, 0x0290b8, 0x000000, 0xfdebf6, 0x000000,
+ 0x01b252, 0x000000, 0xfe9c45, 0x000000, 0x012368,
+ 0x000000, 0xff11d5, 0x000000, 0x00c1d2, 0x000000,
+ 0xff633a, 0x000000, 0x007dd7, 0x000000, 0xff9beb,
+ 0x000000, 0x004ebe, 0x000000, 0xffc2d1, 0x000000,
+ 0x002ede, 0x000000, 0xffdcb0, 0x000000, 0x001a1c,
+ 0x000000, 0xffed1e, 0x000000, 0x000d4e, 0x000000,
+ 0xfff6eb, 0x000000, 0x0005f7, 0x000000, 0xfffc46,
+ 0x000000, 0x00022d, 0x000000, 0xfffedb, 0x000000,
+ 0x000081, 0x000000,
+};
+
+/*
+ * These values are sane defaults for the axg platform:
+ * - OS = 64
+ * - Latency = 38700 (?)
+ *
+ * TODO: There is a lot of different HCIC, LPFs and HPF configurations possible.
+ * the configuration may depend on the dmic used by the platform, the
+ * expected tradeoff between latency and quality, etc ... If/When other
+ * settings are required, we should add a fw interface to this driver to
+ * load new filter settings.
+ */
+static const struct axg_pdm_filters axg_default_filters = {
+ .hcic = {
+ .shift = 0x15,
+ .mult = 0x80,
+ .steps = 7,
+ .ds = 8,
+ },
+ .hpf = {
+ .out_factor = 0x8000,
+ .steps = 13,
+ },
+ .lpf = {
+ [0] = {
+ .ds = 2,
+ .round_mode = 1,
+ .tap = lpf1_default_tap,
+ .tap_num = ARRAY_SIZE(lpf1_default_tap),
+ },
+ [1] = {
+ .ds = 2,
+ .round_mode = 0,
+ .tap = lpf2_default_tap,
+ .tap_num = ARRAY_SIZE(lpf2_default_tap),
+ },
+ [2] = {
+ .ds = 2,
+ .round_mode = 1,
+ .tap = lpf3_default_tap,
+ .tap_num = ARRAY_SIZE(lpf3_default_tap)
+ },
+ },
+};
+
+static const struct axg_pdm_cfg axg_pdm_config = {
+ .filters = &axg_default_filters,
+ .sys_rate = 250000000,
+};
+
+static const struct of_device_id axg_pdm_of_match[] = {
+ {
+ .compatible = "amlogic,axg-pdm",
+ .data = &axg_pdm_config,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_pdm_of_match);
+
+static int axg_pdm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct axg_pdm *priv;
+ void __iomem *regs;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->cfg = of_device_get_match_data(dev);
+ if (!priv->cfg) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->map = devm_regmap_init_mmio(dev, regs, &axg_pdm_regmap_cfg);
+ if (IS_ERR(priv->map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(priv->map));
+ return PTR_ERR(priv->map);
+ }
+
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");
+
+ priv->dclk = devm_clk_get(dev, "dclk");
+ if (IS_ERR(priv->dclk))
+ return dev_err_probe(dev, PTR_ERR(priv->dclk), "failed to get dclk\n");
+
+ priv->sysclk = devm_clk_get(dev, "sysclk");
+ if (IS_ERR(priv->sysclk))
+ return dev_err_probe(dev, PTR_ERR(priv->sysclk), "failed to get dclk\n");
+
+ return devm_snd_soc_register_component(dev, &axg_pdm_component_drv,
+ &axg_pdm_dai_drv, 1);
+}
+
+static struct platform_driver axg_pdm_pdrv = {
+ .probe = axg_pdm_probe,
+ .driver = {
+ .name = "axg-pdm",
+ .of_match_table = axg_pdm_of_match,
+ },
+};
+module_platform_driver(axg_pdm_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG PDM Input driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c
new file mode 100644
index 000000000000..e721f579321e
--- /dev/null
+++ b/sound/soc/meson/axg-spdifin.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm_params.h>
+
+#define SPDIFIN_CTRL0 0x00
+#define SPDIFIN_CTRL0_EN BIT(31)
+#define SPDIFIN_CTRL0_RST_OUT BIT(29)
+#define SPDIFIN_CTRL0_RST_IN BIT(28)
+#define SPDIFIN_CTRL0_WIDTH_SEL BIT(24)
+#define SPDIFIN_CTRL0_STATUS_CH_SHIFT 11
+#define SPDIFIN_CTRL0_STATUS_SEL GENMASK(10, 8)
+#define SPDIFIN_CTRL0_SRC_SEL GENMASK(5, 4)
+#define SPDIFIN_CTRL0_CHK_VALID BIT(3)
+#define SPDIFIN_CTRL1 0x04
+#define SPDIFIN_CTRL1_BASE_TIMER GENMASK(19, 0)
+#define SPDIFIN_CTRL1_IRQ_MASK GENMASK(27, 20)
+#define SPDIFIN_CTRL2 0x08
+#define SPDIFIN_THRES_PER_REG 3
+#define SPDIFIN_THRES_WIDTH 10
+#define SPDIFIN_CTRL3 0x0c
+#define SPDIFIN_CTRL4 0x10
+#define SPDIFIN_TIMER_PER_REG 4
+#define SPDIFIN_TIMER_WIDTH 8
+#define SPDIFIN_CTRL5 0x14
+#define SPDIFIN_CTRL6 0x18
+#define SPDIFIN_STAT0 0x1c
+#define SPDIFIN_STAT0_MODE GENMASK(30, 28)
+#define SPDIFIN_STAT0_MAXW GENMASK(17, 8)
+#define SPDIFIN_STAT0_IRQ GENMASK(7, 0)
+#define SPDIFIN_IRQ_MODE_CHANGED BIT(2)
+#define SPDIFIN_STAT1 0x20
+#define SPDIFIN_STAT2 0x24
+#define SPDIFIN_MUTE_VAL 0x28
+
+#define SPDIFIN_MODE_NUM 7
+
+struct axg_spdifin_cfg {
+ const unsigned int *mode_rates;
+ unsigned int ref_rate;
+};
+
+struct axg_spdifin {
+ const struct axg_spdifin_cfg *conf;
+ struct regmap *map;
+ struct clk *refclk;
+ struct clk *pclk;
+};
+
+/*
+ * TODO:
+ * It would have been nice to check the actual rate against the sample rate
+ * requested in hw_params(). Unfortunately, I was not able to make the mode
+ * detection and IRQ work reliably:
+ *
+ * 1. IRQs are generated on mode change only, so there is no notification
+ * on transition between no signal and mode 0 (32kHz).
+ * 2. Mode detection very often has glitches, and may detects the
+ * lowest or the highest mode before zeroing in on the actual mode.
+ *
+ * This makes calling snd_pcm_stop() difficult to get right. Even notifying
+ * the kcontrol would be very unreliable at this point.
+ * Let's keep things simple until the magic spell that makes this work is
+ * found.
+ */
+
+static unsigned int axg_spdifin_get_rate(struct axg_spdifin *priv)
+{
+ unsigned int stat, mode, rate = 0;
+
+ regmap_read(priv->map, SPDIFIN_STAT0, &stat);
+ mode = FIELD_GET(SPDIFIN_STAT0_MODE, stat);
+
+ /*
+ * If max width is zero, we are not capturing anything.
+ * Also Sometimes, when the capture is on but there is no data,
+ * mode is SPDIFIN_MODE_NUM, but not always ...
+ */
+ if (FIELD_GET(SPDIFIN_STAT0_MAXW, stat) &&
+ mode < SPDIFIN_MODE_NUM)
+ rate = priv->conf->mode_rates[mode];
+
+ return rate;
+}
+
+static int axg_spdifin_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai);
+
+ /* Apply both reset */
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0,
+ SPDIFIN_CTRL0_RST_OUT |
+ SPDIFIN_CTRL0_RST_IN,
+ 0);
+
+ /* Clear out reset before in reset */
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0,
+ SPDIFIN_CTRL0_RST_OUT, SPDIFIN_CTRL0_RST_OUT);
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0,
+ SPDIFIN_CTRL0_RST_IN, SPDIFIN_CTRL0_RST_IN);
+
+ return 0;
+}
+
+static void axg_spdifin_write_mode_param(struct regmap *map, int mode,
+ unsigned int val,
+ unsigned int num_per_reg,
+ unsigned int base_reg,
+ unsigned int width)
+{
+ uint64_t offset = mode;
+ unsigned int reg, shift, rem;
+
+ rem = do_div(offset, num_per_reg);
+
+ reg = offset * regmap_get_reg_stride(map) + base_reg;
+ shift = width * (num_per_reg - 1 - rem);
+
+ regmap_update_bits(map, reg, GENMASK(width - 1, 0) << shift,
+ val << shift);
+}
+
+static void axg_spdifin_write_timer(struct regmap *map, int mode,
+ unsigned int val)
+{
+ axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_TIMER_PER_REG,
+ SPDIFIN_CTRL4, SPDIFIN_TIMER_WIDTH);
+}
+
+static void axg_spdifin_write_threshold(struct regmap *map, int mode,
+ unsigned int val)
+{
+ axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_THRES_PER_REG,
+ SPDIFIN_CTRL2, SPDIFIN_THRES_WIDTH);
+}
+
+static unsigned int axg_spdifin_mode_timer(struct axg_spdifin *priv,
+ int mode,
+ unsigned int rate)
+{
+ /*
+ * Number of period of the reference clock during a period of the
+ * input signal reference clock
+ */
+ return rate / (128 * priv->conf->mode_rates[mode]);
+}
+
+static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai,
+ struct axg_spdifin *priv)
+{
+ unsigned int rate, t_next;
+ int ret, i = SPDIFIN_MODE_NUM - 1;
+
+ /* Set spdif input reference clock */
+ ret = clk_set_rate(priv->refclk, priv->conf->ref_rate);
+ if (ret) {
+ dev_err(dai->dev, "reference clock rate set failed\n");
+ return ret;
+ }
+
+ /*
+ * The rate actually set might be slightly different, get
+ * the actual rate for the following mode calculation
+ */
+ rate = clk_get_rate(priv->refclk);
+
+ /* HW will update mode every 1ms */
+ regmap_update_bits(priv->map, SPDIFIN_CTRL1,
+ SPDIFIN_CTRL1_BASE_TIMER,
+ FIELD_PREP(SPDIFIN_CTRL1_BASE_TIMER, rate / 1000));
+
+ /* Threshold based on the maximum width between two edges */
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0,
+ SPDIFIN_CTRL0_WIDTH_SEL, 0);
+
+ /* Calculate the last timer which has no threshold */
+ t_next = axg_spdifin_mode_timer(priv, i, rate);
+ axg_spdifin_write_timer(priv->map, i, t_next);
+
+ do {
+ unsigned int t;
+
+ i -= 1;
+
+ /* Calculate the timer */
+ t = axg_spdifin_mode_timer(priv, i, rate);
+
+ /* Set the timer value */
+ axg_spdifin_write_timer(priv->map, i, t);
+
+ /* Set the threshold value */
+ axg_spdifin_write_threshold(priv->map, i, 3 * (t + t_next));
+
+ /* Save the current timer for the next threshold calculation */
+ t_next = t;
+
+ } while (i > 0);
+
+ return 0;
+}
+
+static int axg_spdifin_dai_probe(struct snd_soc_dai *dai)
+{
+ struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret) {
+ dev_err(dai->dev, "failed to enable pclk\n");
+ return ret;
+ }
+
+ ret = axg_spdifin_sample_mode_config(dai, priv);
+ if (ret) {
+ dev_err(dai->dev, "mode configuration failed\n");
+ goto pclk_err;
+ }
+
+ ret = clk_prepare_enable(priv->refclk);
+ if (ret) {
+ dev_err(dai->dev,
+ "failed to enable spdifin reference clock\n");
+ goto pclk_err;
+ }
+
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN,
+ SPDIFIN_CTRL0_EN);
+
+ return 0;
+
+pclk_err:
+ clk_disable_unprepare(priv->pclk);
+ return ret;
+}
+
+static int axg_spdifin_dai_remove(struct snd_soc_dai *dai)
+{
+ struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai);
+
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, 0);
+ clk_disable_unprepare(priv->refclk);
+ clk_disable_unprepare(priv->pclk);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops axg_spdifin_ops = {
+ .probe = axg_spdifin_dai_probe,
+ .remove = axg_spdifin_dai_remove,
+ .prepare = axg_spdifin_prepare,
+};
+
+static int axg_spdifin_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int axg_spdifin_get_status_mask(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+
+ for (i = 0; i < 24; i++)
+ ucontrol->value.iec958.status[i] = 0xff;
+
+ return 0;
+}
+
+static int axg_spdifin_get_status(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct axg_spdifin *priv = snd_soc_component_get_drvdata(c);
+ int i, j;
+
+ for (i = 0; i < 6; i++) {
+ unsigned int val;
+
+ regmap_update_bits(priv->map, SPDIFIN_CTRL0,
+ SPDIFIN_CTRL0_STATUS_SEL,
+ FIELD_PREP(SPDIFIN_CTRL0_STATUS_SEL, i));
+
+ regmap_read(priv->map, SPDIFIN_STAT1, &val);
+
+ for (j = 0; j < 4; j++) {
+ unsigned int offset = i * 4 + j;
+
+ ucontrol->value.iec958.status[offset] =
+ (val >> (j * 8)) & 0xff;
+ }
+ }
+
+ return 0;
+}
+
+#define AXG_SPDIFIN_IEC958_MASK \
+ { \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), \
+ .info = axg_spdifin_iec958_info, \
+ .get = axg_spdifin_get_status_mask, \
+ }
+
+#define AXG_SPDIFIN_IEC958_STATUS \
+ { \
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE), \
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE), \
+ .info = axg_spdifin_iec958_info, \
+ .get = axg_spdifin_get_status, \
+ }
+
+static const char * const spdifin_chsts_src_texts[] = {
+ "A", "B",
+};
+
+static SOC_ENUM_SINGLE_DECL(axg_spdifin_chsts_src_enum, SPDIFIN_CTRL0,
+ SPDIFIN_CTRL0_STATUS_CH_SHIFT,
+ spdifin_chsts_src_texts);
+
+static int axg_spdifin_rate_lock_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+
+ return 0;
+}
+
+static int axg_spdifin_rate_lock_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct axg_spdifin *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = axg_spdifin_get_rate(priv);
+
+ return 0;
+}
+
+#define AXG_SPDIFIN_LOCK_RATE(xname) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE), \
+ .get = axg_spdifin_rate_lock_get, \
+ .info = axg_spdifin_rate_lock_info, \
+ .name = xname, \
+ }
+
+static const struct snd_kcontrol_new axg_spdifin_controls[] = {
+ AXG_SPDIFIN_LOCK_RATE("Capture Rate Lock"),
+ SOC_DOUBLE("Capture Switch", SPDIFIN_CTRL0, 7, 6, 1, 1),
+ SOC_ENUM(SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Src",
+ axg_spdifin_chsts_src_enum),
+ AXG_SPDIFIN_IEC958_MASK,
+ AXG_SPDIFIN_IEC958_STATUS,
+};
+
+static const struct snd_soc_component_driver axg_spdifin_component_drv = {
+ .controls = axg_spdifin_controls,
+ .num_controls = ARRAY_SIZE(axg_spdifin_controls),
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config axg_spdifin_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPDIFIN_MUTE_VAL,
+};
+
+static const unsigned int axg_spdifin_mode_rates[SPDIFIN_MODE_NUM] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000,
+};
+
+static const struct axg_spdifin_cfg axg_cfg = {
+ .mode_rates = axg_spdifin_mode_rates,
+ .ref_rate = 333333333,
+};
+
+static const struct of_device_id axg_spdifin_of_match[] = {
+ {
+ .compatible = "amlogic,axg-spdifin",
+ .data = &axg_cfg,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_spdifin_of_match);
+
+static struct snd_soc_dai_driver *
+axg_spdifin_get_dai_drv(struct device *dev, struct axg_spdifin *priv)
+{
+ struct snd_soc_dai_driver *drv;
+ int i;
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return ERR_PTR(-ENOMEM);
+
+ drv->name = "SPDIF Input";
+ drv->ops = &axg_spdifin_ops;
+ drv->capture.stream_name = "Capture";
+ drv->capture.channels_min = 1;
+ drv->capture.channels_max = 2;
+ drv->capture.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+
+ for (i = 0; i < SPDIFIN_MODE_NUM; i++) {
+ unsigned int rb =
+ snd_pcm_rate_to_rate_bit(priv->conf->mode_rates[i]);
+
+ if (rb == SNDRV_PCM_RATE_KNOT)
+ return ERR_PTR(-EINVAL);
+
+ drv->capture.rates |= rb;
+ }
+
+ return drv;
+}
+
+static int axg_spdifin_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct axg_spdifin *priv;
+ struct snd_soc_dai_driver *dai_drv;
+ void __iomem *regs;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->conf = of_device_get_match_data(dev);
+ if (!priv->conf) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifin_regmap_cfg);
+ if (IS_ERR(priv->map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(priv->map));
+ return PTR_ERR(priv->map);
+ }
+
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");
+
+ priv->refclk = devm_clk_get(dev, "refclk");
+ if (IS_ERR(priv->refclk))
+ return dev_err_probe(dev, PTR_ERR(priv->refclk), "failed to get mclk\n");
+
+ dai_drv = axg_spdifin_get_dai_drv(dev, priv);
+ if (IS_ERR(dai_drv)) {
+ dev_err(dev, "failed to get dai driver: %ld\n",
+ PTR_ERR(dai_drv));
+ return PTR_ERR(dai_drv);
+ }
+
+ return devm_snd_soc_register_component(dev, &axg_spdifin_component_drv,
+ dai_drv, 1);
+}
+
+static struct platform_driver axg_spdifin_pdrv = {
+ .probe = axg_spdifin_probe,
+ .driver = {
+ .name = "axg-spdifin",
+ .of_match_table = axg_spdifin_of_match,
+ },
+};
+module_platform_driver(axg_spdifin_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG SPDIF Input driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c
new file mode 100644
index 000000000000..84868fe574e0
--- /dev/null
+++ b/sound/soc/meson/axg-spdifout.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
+
+/*
+ * NOTE:
+ * The meaning of bits SPDIFOUT_CTRL0_XXX_SEL is actually the opposite
+ * of what the documentation says. Manual control on V, U and C bits is
+ * applied when the related sel bits are cleared
+ */
+
+#define SPDIFOUT_STAT 0x00
+#define SPDIFOUT_GAIN0 0x04
+#define SPDIFOUT_GAIN1 0x08
+#define SPDIFOUT_CTRL0 0x0c
+#define SPDIFOUT_CTRL0_EN BIT(31)
+#define SPDIFOUT_CTRL0_RST_OUT BIT(29)
+#define SPDIFOUT_CTRL0_RST_IN BIT(28)
+#define SPDIFOUT_CTRL0_USEL BIT(26)
+#define SPDIFOUT_CTRL0_USET BIT(25)
+#define SPDIFOUT_CTRL0_CHSTS_SEL BIT(24)
+#define SPDIFOUT_CTRL0_DATA_SEL BIT(20)
+#define SPDIFOUT_CTRL0_MSB_FIRST BIT(19)
+#define SPDIFOUT_CTRL0_VSEL BIT(18)
+#define SPDIFOUT_CTRL0_VSET BIT(17)
+#define SPDIFOUT_CTRL0_MASK_MASK GENMASK(11, 4)
+#define SPDIFOUT_CTRL0_MASK(x) ((x) << 4)
+#define SPDIFOUT_CTRL1 0x10
+#define SPDIFOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8)
+#define SPDIFOUT_CTRL1_MSB_POS(x) ((x) << 8)
+#define SPDIFOUT_CTRL1_TYPE_MASK GENMASK(6, 4)
+#define SPDIFOUT_CTRL1_TYPE(x) ((x) << 4)
+#define SPDIFOUT_PREAMB 0x14
+#define SPDIFOUT_SWAP 0x18
+#define SPDIFOUT_CHSTS0 0x1c
+#define SPDIFOUT_CHSTS1 0x20
+#define SPDIFOUT_CHSTS2 0x24
+#define SPDIFOUT_CHSTS3 0x28
+#define SPDIFOUT_CHSTS4 0x2c
+#define SPDIFOUT_CHSTS5 0x30
+#define SPDIFOUT_CHSTS6 0x34
+#define SPDIFOUT_CHSTS7 0x38
+#define SPDIFOUT_CHSTS8 0x3c
+#define SPDIFOUT_CHSTS9 0x40
+#define SPDIFOUT_CHSTSA 0x44
+#define SPDIFOUT_CHSTSB 0x48
+#define SPDIFOUT_MUTE_VAL 0x4c
+
+struct axg_spdifout {
+ struct regmap *map;
+ struct clk *mclk;
+ struct clk *pclk;
+};
+
+static void axg_spdifout_enable(struct regmap *map)
+{
+ /* Apply both reset */
+ regmap_update_bits(map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_RST_OUT | SPDIFOUT_CTRL0_RST_IN,
+ 0);
+
+ /* Clear out reset before in reset */
+ regmap_update_bits(map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_RST_OUT, SPDIFOUT_CTRL0_RST_OUT);
+ regmap_update_bits(map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_RST_IN, SPDIFOUT_CTRL0_RST_IN);
+
+ /* Enable spdifout */
+ regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN,
+ SPDIFOUT_CTRL0_EN);
+}
+
+static void axg_spdifout_disable(struct regmap *map)
+{
+ regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN, 0);
+}
+
+static int axg_spdifout_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ axg_spdifout_enable(priv->map);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ axg_spdifout_disable(priv->map);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axg_spdifout_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+
+ /* Use spdif valid bit to perform digital mute */
+ regmap_update_bits(priv->map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_VSET,
+ mute ? SPDIFOUT_CTRL0_VSET : 0);
+
+ return 0;
+}
+
+static int axg_spdifout_sample_fmt(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int val;
+
+ /* Set the samples spdifout will pull from the FIFO */
+ switch (params_channels(params)) {
+ case 1:
+ val = SPDIFOUT_CTRL0_MASK(0x1);
+ break;
+ case 2:
+ val = SPDIFOUT_CTRL0_MASK(0x3);
+ break;
+ default:
+ dev_err(dai->dev, "too many channels for spdif dai: %u\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_MASK_MASK, val);
+
+ /* FIFO data are arranged in chunks of 64bits */
+ switch (params_physical_width(params)) {
+ case 8:
+ /* 8 samples of 8 bits */
+ val = SPDIFOUT_CTRL1_TYPE(0);
+ break;
+ case 16:
+ /* 4 samples of 16 bits - right justified */
+ val = SPDIFOUT_CTRL1_TYPE(2);
+ break;
+ case 32:
+ /* 2 samples of 32 bits - right justified */
+ val = SPDIFOUT_CTRL1_TYPE(4);
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported physical width: %u\n",
+ params_physical_width(params));
+ return -EINVAL;
+ }
+
+ /* Position of the MSB in FIFO samples */
+ val |= SPDIFOUT_CTRL1_MSB_POS(params_width(params) - 1);
+
+ regmap_update_bits(priv->map, SPDIFOUT_CTRL1,
+ SPDIFOUT_CTRL1_MSB_POS_MASK |
+ SPDIFOUT_CTRL1_TYPE_MASK, val);
+
+ regmap_update_bits(priv->map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL,
+ 0);
+
+ return 0;
+}
+
+static int axg_spdifout_set_chsts(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int offset;
+ int ret;
+ u8 cs[4];
+ u32 val;
+
+ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, 4);
+ if (ret < 0) {
+ dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+ ret);
+ return ret;
+ }
+ val = cs[0] | cs[1] << 8 | cs[2] << 16 | cs[3] << 24;
+
+ /* Setup channel status A bits [31 - 0]*/
+ regmap_write(priv->map, SPDIFOUT_CHSTS0, val);
+
+ /* Clear channel status A bits [191 - 32] */
+ for (offset = SPDIFOUT_CHSTS1; offset <= SPDIFOUT_CHSTS5;
+ offset += regmap_get_reg_stride(priv->map))
+ regmap_write(priv->map, offset, 0);
+
+ /* Setup channel status B bits [31 - 0]*/
+ regmap_write(priv->map, SPDIFOUT_CHSTS6, val);
+
+ /* Clear channel status B bits [191 - 32] */
+ for (offset = SPDIFOUT_CHSTS7; offset <= SPDIFOUT_CHSTSB;
+ offset += regmap_get_reg_stride(priv->map))
+ regmap_write(priv->map, offset, 0);
+
+ return 0;
+}
+
+static int axg_spdifout_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ /* 2 * 32bits per subframe * 2 channels = 128 */
+ ret = clk_set_rate(priv->mclk, rate * 128);
+ if (ret) {
+ dev_err(dai->dev, "failed to set spdif clock\n");
+ return ret;
+ }
+
+ ret = axg_spdifout_sample_fmt(params, dai);
+ if (ret) {
+ dev_err(dai->dev, "failed to setup sample format\n");
+ return ret;
+ }
+
+ ret = axg_spdifout_set_chsts(params, dai);
+ if (ret) {
+ dev_err(dai->dev, "failed to setup channel status words\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int axg_spdifout_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* Clock the spdif output block */
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret) {
+ dev_err(dai->dev, "failed to enable pclk\n");
+ return ret;
+ }
+
+ /* Make sure the block is initially stopped */
+ axg_spdifout_disable(priv->map);
+
+ /* Insert data from bit 27 lsb first */
+ regmap_update_bits(priv->map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL,
+ 0);
+
+ /* Manual control of V, C and U, U = 0 */
+ regmap_update_bits(priv->map, SPDIFOUT_CTRL0,
+ SPDIFOUT_CTRL0_CHSTS_SEL | SPDIFOUT_CTRL0_VSEL |
+ SPDIFOUT_CTRL0_USEL | SPDIFOUT_CTRL0_USET,
+ 0);
+
+ /* Static SWAP configuration ATM */
+ regmap_write(priv->map, SPDIFOUT_SWAP, 0x10);
+
+ return 0;
+}
+
+static void axg_spdifout_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(priv->pclk);
+}
+
+static const struct snd_soc_dai_ops axg_spdifout_ops = {
+ .trigger = axg_spdifout_trigger,
+ .mute_stream = axg_spdifout_mute,
+ .hw_params = axg_spdifout_hw_params,
+ .startup = axg_spdifout_startup,
+ .shutdown = axg_spdifout_shutdown,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver axg_spdifout_dai_drv[] = {
+ {
+ .name = "SPDIF Output",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .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_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &axg_spdifout_ops,
+ },
+};
+
+static const char * const spdifout_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(axg_spdifout_sel_enum, SPDIFOUT_CTRL1, 24,
+ spdifout_sel_texts);
+
+static const struct snd_kcontrol_new axg_spdifout_in_mux =
+ SOC_DAPM_ENUM("Input Source", axg_spdifout_sel_enum);
+
+static const struct snd_soc_dapm_widget axg_spdifout_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_spdifout_in_mux),
+};
+
+static const struct snd_soc_dapm_route axg_spdifout_dapm_routes[] = {
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "Playback", NULL, "SRC SEL" },
+};
+
+static const struct snd_kcontrol_new axg_spdifout_controls[] = {
+ SOC_DOUBLE("Playback Volume", SPDIFOUT_GAIN0, 0, 8, 255, 0),
+ SOC_DOUBLE("Playback Switch", SPDIFOUT_CTRL0, 22, 21, 1, 1),
+ SOC_SINGLE("Playback Gain Enable Switch",
+ SPDIFOUT_CTRL1, 26, 1, 0),
+ SOC_SINGLE("Playback Channels Mix Switch",
+ SPDIFOUT_CTRL0, 23, 1, 0),
+};
+
+static int axg_spdifout_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct axg_spdifout *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ enum snd_soc_bias_level now = snd_soc_dapm_get_bias_level(dapm);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (now == SND_SOC_BIAS_STANDBY)
+ ret = clk_prepare_enable(priv->mclk);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (now == SND_SOC_BIAS_PREPARE)
+ clk_disable_unprepare(priv->mclk);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver axg_spdifout_component_drv = {
+ .controls = axg_spdifout_controls,
+ .num_controls = ARRAY_SIZE(axg_spdifout_controls),
+ .dapm_widgets = axg_spdifout_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_spdifout_dapm_widgets),
+ .dapm_routes = axg_spdifout_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_spdifout_dapm_routes),
+ .set_bias_level = axg_spdifout_set_bias_level,
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config axg_spdifout_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPDIFOUT_MUTE_VAL,
+};
+
+static const struct of_device_id axg_spdifout_of_match[] = {
+ { .compatible = "amlogic,axg-spdifout", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axg_spdifout_of_match);
+
+static int axg_spdifout_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct axg_spdifout *priv;
+ void __iomem *regs;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifout_regmap_cfg);
+ if (IS_ERR(priv->map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(priv->map));
+ return PTR_ERR(priv->map);
+ }
+
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk))
+ return dev_err_probe(dev, PTR_ERR(priv->mclk), "failed to get mclk\n");
+
+ return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv,
+ axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv));
+}
+
+static struct platform_driver axg_spdifout_pdrv = {
+ .probe = axg_spdifout_probe,
+ .driver = {
+ .name = "axg-spdifout",
+ .of_match_table = axg_spdifout_of_match,
+ },
+};
+module_platform_driver(axg_spdifout_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG SPDIF Output driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
new file mode 100644
index 000000000000..a6579efd3775
--- /dev/null
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+
+#include "axg-tdm-formatter.h"
+
+struct axg_tdm_formatter {
+ struct list_head list;
+ struct axg_tdm_stream *stream;
+ const struct axg_tdm_formatter_driver *drv;
+ struct clk *pclk;
+ struct clk *sclk;
+ struct clk *lrclk;
+ struct clk *sclk_sel;
+ struct clk *lrclk_sel;
+ struct reset_control *reset;
+ bool enabled;
+ struct regmap *map;
+};
+
+int axg_tdm_formatter_set_channel_masks(struct regmap *map,
+ struct axg_tdm_stream *ts,
+ unsigned int offset)
+{
+ unsigned int ch = ts->channels;
+ u32 val[AXG_TDM_NUM_LANES];
+ int i, j, k;
+
+ /*
+ * We need to mimick the slot distribution used by the HW to keep the
+ * channel placement consistent regardless of the number of channel
+ * in the stream. This is why the odd algorithm below is used.
+ */
+ memset(val, 0, sizeof(*val) * AXG_TDM_NUM_LANES);
+
+ /*
+ * Distribute the channels of the stream over the available slots
+ * of each TDM lane. We need to go over the 32 slots ...
+ */
+ for (i = 0; (i < 32) && ch; i += 2) {
+ /* ... of all the lanes ... */
+ for (j = 0; j < AXG_TDM_NUM_LANES; j++) {
+ /* ... then distribute the channels in pairs */
+ for (k = 0; k < 2; k++) {
+ if ((BIT(i + k) & ts->mask[j]) && ch) {
+ val[j] |= BIT(i + k);
+ ch -= 1;
+ }
+ }
+ }
+ }
+
+ /*
+ * If we still have channel left at the end of the process, it means
+ * the stream has more channels than we can accommodate and we should
+ * have caught this earlier.
+ */
+ if (WARN_ON(ch != 0)) {
+ pr_err("channel mask error\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
+ regmap_write(map, offset, val[i]);
+ offset += regmap_get_reg_stride(map);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
+
+static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
+{
+ struct axg_tdm_stream *ts = formatter->stream;
+ bool invert;
+ int ret;
+
+ /* Do nothing if the formatter is already enabled */
+ if (formatter->enabled)
+ return 0;
+
+ /*
+ * On the g12a (and possibly other SoCs), when a stream using
+ * multiple lanes is restarted, it will sometimes not start
+ * from the first lane, but randomly from another used one.
+ * The result is an unexpected and random channel shift.
+ *
+ * The hypothesis is that an HW counter is not properly reset
+ * and the formatter simply starts on the lane it stopped
+ * before. Unfortunately, there does not seems to be a way to
+ * reset this through the registers of the block.
+ *
+ * However, the g12a has indenpendent reset lines for each audio
+ * devices. Using this reset before each start solves the issue.
+ */
+ ret = reset_control_reset(formatter->reset);
+ if (ret)
+ return ret;
+
+ /*
+ * If sclk is inverted, it means the bit should latched on the
+ * rising edge which is what our HW expects. If not, we need to
+ * invert it before the formatter.
+ */
+ invert = axg_tdm_sclk_invert(ts->iface->fmt);
+ ret = clk_set_phase(formatter->sclk, invert ? 0 : 180);
+ if (ret)
+ return ret;
+
+ /* Setup the stream parameter in the formatter */
+ ret = formatter->drv->ops->prepare(formatter->map,
+ formatter->drv->quirks,
+ formatter->stream);
+ if (ret)
+ return ret;
+
+ /* Enable the signal clocks feeding the formatter */
+ ret = clk_prepare_enable(formatter->sclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(formatter->lrclk);
+ if (ret) {
+ clk_disable_unprepare(formatter->sclk);
+ return ret;
+ }
+
+ /* Finally, actually enable the formatter */
+ formatter->drv->ops->enable(formatter->map);
+ formatter->enabled = true;
+
+ return 0;
+}
+
+static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter)
+{
+ /* Do nothing if the formatter is already disabled */
+ if (!formatter->enabled)
+ return;
+
+ formatter->drv->ops->disable(formatter->map);
+ clk_disable_unprepare(formatter->lrclk);
+ clk_disable_unprepare(formatter->sclk);
+ formatter->enabled = false;
+}
+
+static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter)
+{
+ struct axg_tdm_stream *ts = formatter->stream;
+ int ret = 0;
+
+ mutex_lock(&ts->lock);
+
+ /* Catch up if the stream is already running when we attach */
+ if (ts->ready) {
+ ret = axg_tdm_formatter_enable(formatter);
+ if (ret) {
+ pr_err("failed to enable formatter\n");
+ goto out;
+ }
+ }
+
+ list_add_tail(&formatter->list, &ts->formatter_list);
+out:
+ mutex_unlock(&ts->lock);
+ return ret;
+}
+
+static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter)
+{
+ struct axg_tdm_stream *ts = formatter->stream;
+
+ mutex_lock(&ts->lock);
+ list_del(&formatter->list);
+ mutex_unlock(&ts->lock);
+
+ axg_tdm_formatter_disable(formatter);
+}
+
+static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter,
+ struct snd_soc_dapm_widget *w)
+{
+ struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w);
+ int ret;
+
+ /*
+ * If we don't get a stream at this stage, it would mean that the
+ * widget is powering up but is not attached to any backend DAI.
+ * It should not happen, ever !
+ */
+ if (WARN_ON(!ts))
+ return -ENODEV;
+
+ /* Clock our device */
+ ret = clk_prepare_enable(formatter->pclk);
+ if (ret)
+ return ret;
+
+ /* Reparent the bit clock to the TDM interface */
+ ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk);
+ if (ret)
+ goto disable_pclk;
+
+ /* Reparent the sample clock to the TDM interface */
+ ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk);
+ if (ret)
+ goto disable_pclk;
+
+ formatter->stream = ts;
+ ret = axg_tdm_formatter_attach(formatter);
+ if (ret)
+ goto disable_pclk;
+
+ return 0;
+
+disable_pclk:
+ clk_disable_unprepare(formatter->pclk);
+ return ret;
+}
+
+static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter)
+{
+ axg_tdm_formatter_dettach(formatter);
+ clk_disable_unprepare(formatter->pclk);
+ formatter->stream = NULL;
+}
+
+int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control,
+ int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = axg_tdm_formatter_power_up(formatter, w);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ axg_tdm_formatter_power_down(formatter);
+ break;
+
+ default:
+ dev_err(c->dev, "Unexpected event %d\n", event);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_formatter_event);
+
+int axg_tdm_formatter_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct axg_tdm_formatter_driver *drv;
+ struct axg_tdm_formatter *formatter;
+ void __iomem *regs;
+
+ drv = of_device_get_match_data(dev);
+ if (!drv) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
+ if (!formatter)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, formatter);
+ formatter->drv = drv;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg);
+ if (IS_ERR(formatter->map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(formatter->map));
+ return PTR_ERR(formatter->map);
+ }
+
+ /* Peripharal clock */
+ formatter->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(formatter->pclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n");
+
+ /* Formatter bit clock */
+ formatter->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(formatter->sclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n");
+
+ /* Formatter sample clock */
+ formatter->lrclk = devm_clk_get(dev, "lrclk");
+ if (IS_ERR(formatter->lrclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n");
+
+ /* Formatter bit clock input multiplexer */
+ formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
+ if (IS_ERR(formatter->sclk_sel))
+ return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n");
+
+ /* Formatter sample clock input multiplexer */
+ formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
+ if (IS_ERR(formatter->lrclk_sel))
+ return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel),
+ "failed to get lrclk_sel\n");
+
+ /* Formatter dedicated reset line */
+ formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(formatter->reset))
+ return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n");
+
+ return devm_snd_soc_register_component(dev, drv->component_drv,
+ NULL, 0);
+}
+EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe);
+
+int axg_tdm_stream_start(struct axg_tdm_stream *ts)
+{
+ struct axg_tdm_formatter *formatter;
+ int ret = 0;
+
+ mutex_lock(&ts->lock);
+ ts->ready = true;
+
+ /* Start all the formatters attached to the stream */
+ list_for_each_entry(formatter, &ts->formatter_list, list) {
+ ret = axg_tdm_formatter_enable(formatter);
+ if (ret) {
+ pr_err("failed to start tdm stream\n");
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&ts->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_start);
+
+void axg_tdm_stream_stop(struct axg_tdm_stream *ts)
+{
+ struct axg_tdm_formatter *formatter;
+
+ mutex_lock(&ts->lock);
+ ts->ready = false;
+
+ /* Stop all the formatters attached to the stream */
+ list_for_each_entry(formatter, &ts->formatter_list, list) {
+ axg_tdm_formatter_disable(formatter);
+ }
+
+ mutex_unlock(&ts->lock);
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_stop);
+
+struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface)
+{
+ struct axg_tdm_stream *ts;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts) {
+ INIT_LIST_HEAD(&ts->formatter_list);
+ mutex_init(&ts->lock);
+ ts->iface = iface;
+ }
+
+ return ts;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc);
+
+void axg_tdm_stream_free(struct axg_tdm_stream *ts)
+{
+ /*
+ * If the list is not empty, it would mean that one of the formatter
+ * widget is still powered and attached to the interface while we
+ * are removing the TDM DAI. It should not be possible
+ */
+ WARN_ON(!list_empty(&ts->formatter_list));
+ mutex_destroy(&ts->lock);
+ kfree(ts);
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
+
+int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
+ unsigned int fmt)
+{
+ int ret = 0;
+
+ if (fmt & SND_SOC_DAIFMT_CONT) {
+ /* Clock are already enabled - skipping */
+ if (ts->clk_enabled)
+ return 0;
+
+ ret = clk_prepare_enable(ts->iface->mclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(ts->iface->sclk);
+ if (ret)
+ goto err_sclk;
+
+ ret = clk_prepare_enable(ts->iface->lrclk);
+ if (ret)
+ goto err_lrclk;
+
+ ts->clk_enabled = true;
+ return 0;
+ }
+
+ /* Clocks are already disabled - skipping */
+ if (!ts->clk_enabled)
+ return 0;
+
+ clk_disable_unprepare(ts->iface->lrclk);
+err_lrclk:
+ clk_disable_unprepare(ts->iface->sclk);
+err_sclk:
+ clk_disable_unprepare(ts->iface->mclk);
+ ts->clk_enabled = false;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_set_cont_clocks);
+
+MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-tdm-formatter.h b/sound/soc/meson/axg-tdm-formatter.h
new file mode 100644
index 000000000000..a1f0dcc0ff13
--- /dev/null
+++ b/sound/soc/meson/axg-tdm-formatter.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT)
+ *
+ * Copyright (c) 2018 Baylibre SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_AXG_TDM_FORMATTER_H
+#define _MESON_AXG_TDM_FORMATTER_H
+
+#include "axg-tdm.h"
+
+struct platform_device;
+struct regmap;
+struct snd_soc_dapm_widget;
+struct snd_kcontrol;
+
+struct axg_tdm_formatter_hw {
+ unsigned int skew_offset;
+};
+
+struct axg_tdm_formatter_ops {
+ struct axg_tdm_stream *(*get_stream)(struct snd_soc_dapm_widget *w);
+ void (*enable)(struct regmap *map);
+ void (*disable)(struct regmap *map);
+ int (*prepare)(struct regmap *map,
+ const struct axg_tdm_formatter_hw *quirks,
+ struct axg_tdm_stream *ts);
+};
+
+struct axg_tdm_formatter_driver {
+ const struct snd_soc_component_driver *component_drv;
+ const struct regmap_config *regmap_cfg;
+ const struct axg_tdm_formatter_ops *ops;
+ const struct axg_tdm_formatter_hw *quirks;
+};
+
+int axg_tdm_formatter_set_channel_masks(struct regmap *map,
+ struct axg_tdm_stream *ts,
+ unsigned int offset);
+int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control,
+ int event);
+int axg_tdm_formatter_probe(struct platform_device *pdev);
+
+#endif /* _MESON_AXG_TDM_FORMATTER_H */
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
new file mode 100644
index 000000000000..d5287d78f53b
--- /dev/null
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-tdm.h"
+
+/* Maximum bit clock frequency according the datasheets */
+#define MAX_SCLK 100000000 /* Hz */
+
+enum {
+ TDM_IFACE_PAD,
+ TDM_IFACE_LOOPBACK,
+};
+
+static unsigned int axg_tdm_slots_total(u32 *mask)
+{
+ unsigned int slots = 0;
+ int i;
+
+ if (!mask)
+ return 0;
+
+ /* Count the total number of slots provided by all 4 lanes */
+ for (i = 0; i < AXG_TDM_NUM_LANES; i++)
+ slots += hweight32(mask[i]);
+
+ return slots;
+}
+
+int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask,
+ u32 *rx_mask, unsigned int slots,
+ unsigned int slot_width)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ struct axg_tdm_stream *tx = snd_soc_dai_dma_data_get_playback(dai);
+ struct axg_tdm_stream *rx = snd_soc_dai_dma_data_get_capture(dai);
+ unsigned int tx_slots, rx_slots;
+ unsigned int fmt = 0;
+
+ tx_slots = axg_tdm_slots_total(tx_mask);
+ rx_slots = axg_tdm_slots_total(rx_mask);
+
+ /* We should at least have a slot for a valid interface */
+ if (!tx_slots && !rx_slots) {
+ dev_err(dai->dev, "interface has no slot\n");
+ return -EINVAL;
+ }
+
+ iface->slots = slots;
+
+ switch (slot_width) {
+ case 0:
+ slot_width = 32;
+ fallthrough;
+ case 32:
+ fmt |= SNDRV_PCM_FMTBIT_S32_LE;
+ fallthrough;
+ case 24:
+ fmt |= SNDRV_PCM_FMTBIT_S24_LE;
+ fmt |= SNDRV_PCM_FMTBIT_S20_LE;
+ fallthrough;
+ case 16:
+ fmt |= SNDRV_PCM_FMTBIT_S16_LE;
+ fallthrough;
+ case 8:
+ fmt |= SNDRV_PCM_FMTBIT_S8;
+ break;
+ default:
+ dev_err(dai->dev, "unsupported slot width: %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ iface->slot_width = slot_width;
+
+ /* Amend the dai driver and let dpcm merge do its job */
+ if (tx) {
+ tx->mask = tx_mask;
+ dai->driver->playback.channels_max = tx_slots;
+ dai->driver->playback.formats = fmt;
+ }
+
+ if (rx) {
+ rx->mask = rx_mask;
+ dai->driver->capture.channels_max = rx_slots;
+ dai->driver->capture.formats = fmt;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots);
+
+static int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ int ret = -ENOTSUPP;
+
+ if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) {
+ if (!iface->mclk) {
+ dev_warn(dai->dev, "master clock not provided\n");
+ } else {
+ ret = clk_set_rate(iface->mclk, freq);
+ if (!ret)
+ iface->mclk_rate = freq;
+ }
+ }
+
+ return ret;
+}
+
+static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ if (!iface->mclk) {
+ dev_err(dai->dev, "cpu clock master: mclk missing\n");
+ return -ENODEV;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+
+ case SND_SOC_DAIFMT_BP_FC:
+ case SND_SOC_DAIFMT_BC_FP:
+ dev_err(dai->dev, "only BP_FP and BC_FC are supported\n");
+ fallthrough;
+ default:
+ return -EINVAL;
+ }
+
+ iface->fmt = fmt;
+ return 0;
+}
+
+static int axg_tdm_iface_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ struct axg_tdm_stream *ts =
+ snd_soc_dai_get_dma_data(dai, substream);
+ int ret;
+
+ if (!axg_tdm_slots_total(ts->mask)) {
+ dev_err(dai->dev, "interface has not slots\n");
+ return -EINVAL;
+ }
+
+ if (snd_soc_component_active(dai->component)) {
+ /* Apply component wide rate symmetry */
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ iface->rate);
+
+ } else {
+ /* Limit rate according to the slot number and width */
+ unsigned int max_rate =
+ MAX_SCLK / (iface->slots * iface->slot_width);
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, max_rate);
+ }
+
+ if (ret < 0)
+ dev_err(dai->dev, "can't set iface rate constraint\n");
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
+ unsigned int channels = params_channels(params);
+ unsigned int width = params_width(params);
+
+ /* Save rate and sample_bits for component symmetry */
+ iface->rate = params_rate(params);
+
+ /* Make sure this interface can cope with the stream */
+ if (axg_tdm_slots_total(ts->mask) < channels) {
+ dev_err(dai->dev, "not enough slots for channels\n");
+ return -EINVAL;
+ }
+
+ if (iface->slot_width < width) {
+ dev_err(dai->dev, "incompatible slots width for stream\n");
+ return -EINVAL;
+ }
+
+ /* Save the parameter for tdmout/tdmin widgets */
+ ts->physical_width = params_physical_width(params);
+ ts->width = params_width(params);
+ ts->channels = params_channels(params);
+
+ return 0;
+}
+
+static int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ unsigned int ratio_num;
+ int ret;
+
+ ret = clk_set_rate(iface->lrclk, params_rate(params));
+ if (ret) {
+ dev_err(dai->dev, "setting sample clock failed: %d\n", ret);
+ return ret;
+ }
+
+ switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ /* 50% duty cycle ratio */
+ ratio_num = 1;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /*
+ * A zero duty cycle ratio will result in setting the mininum
+ * ratio possible which, for this clock, is 1 cycle of the
+ * parent bclk clock high and the rest low, This is exactly
+ * what we want here.
+ */
+ ratio_num = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2);
+ if (ret) {
+ dev_err(dai->dev,
+ "setting sample clock duty cycle failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Set sample clock inversion */
+ ret = clk_set_phase(iface->lrclk,
+ axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0);
+ if (ret) {
+ dev_err(dai->dev,
+ "setting sample clock phase failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ unsigned long srate;
+ int ret;
+
+ srate = iface->slots * iface->slot_width * params_rate(params);
+
+ if (!iface->mclk_rate) {
+ /* If no specific mclk is requested, default to bit clock * 2 */
+ clk_set_rate(iface->mclk, 2 * srate);
+ } else {
+ /* Check if we can actually get the bit clock from mclk */
+ if (iface->mclk_rate % srate) {
+ dev_err(dai->dev,
+ "can't derive sclk %lu from mclk %lu\n",
+ srate, iface->mclk_rate);
+ return -EINVAL;
+ }
+ }
+
+ ret = clk_set_rate(iface->sclk, srate);
+ if (ret) {
+ dev_err(dai->dev, "setting bit clock failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Set the bit clock inversion */
+ ret = clk_set_phase(iface->sclk,
+ axg_tdm_sclk_invert(iface->fmt) ? 0 : 180);
+ if (ret) {
+ dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
+ int ret;
+
+ switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (iface->slots > 2) {
+ dev_err(dai->dev, "bad slot number for format: %d\n",
+ iface->slots);
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+
+ default:
+ dev_err(dai->dev, "unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ ret = axg_tdm_iface_set_stream(substream, params, dai);
+ if (ret)
+ return ret;
+
+ if ((iface->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+ SND_SOC_DAIFMT_BP_FP) {
+ ret = axg_tdm_iface_set_sclk(dai, params);
+ if (ret)
+ return ret;
+
+ ret = axg_tdm_iface_set_lrclk(dai, params);
+ if (ret)
+ return ret;
+ }
+
+ ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt);
+ if (ret)
+ dev_err(dai->dev, "failed to apply continuous clock setting\n");
+
+ return ret;
+}
+
+static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
+
+ return axg_tdm_stream_set_cont_clocks(ts, 0);
+}
+
+static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axg_tdm_stream *ts =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ axg_tdm_stream_start(ts);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ axg_tdm_stream_stop(ts);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
+{
+ int stream;
+
+ for_each_pcm_streams(stream) {
+ struct axg_tdm_stream *ts = snd_soc_dai_dma_data_get(dai, stream);
+
+ if (ts)
+ axg_tdm_stream_free(ts);
+ }
+
+ return 0;
+}
+
+static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai)
+{
+ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ int stream;
+
+ for_each_pcm_streams(stream) {
+ struct axg_tdm_stream *ts;
+
+ if (!snd_soc_dai_get_widget(dai, stream))
+ continue;
+
+ ts = axg_tdm_stream_alloc(iface);
+ if (!ts) {
+ axg_tdm_iface_remove_dai(dai);
+ return -ENOMEM;
+ }
+ snd_soc_dai_dma_data_set(dai, stream, ts);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops axg_tdm_iface_ops = {
+ .probe = axg_tdm_iface_probe_dai,
+ .remove = axg_tdm_iface_remove_dai,
+ .set_sysclk = axg_tdm_iface_set_sysclk,
+ .set_fmt = axg_tdm_iface_set_fmt,
+ .startup = axg_tdm_iface_startup,
+ .hw_params = axg_tdm_iface_hw_params,
+ .hw_free = axg_tdm_iface_hw_free,
+ .trigger = axg_tdm_iface_trigger,
+};
+
+/* TDM Backend DAIs */
+static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = {
+ [TDM_IFACE_PAD] = {
+ .name = "TDM Pad",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = AXG_TDM_CHANNEL_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 768000,
+ .formats = AXG_TDM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = AXG_TDM_CHANNEL_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 768000,
+ .formats = AXG_TDM_FORMATS,
+ },
+ .id = TDM_IFACE_PAD,
+ .ops = &axg_tdm_iface_ops,
+ },
+ [TDM_IFACE_LOOPBACK] = {
+ .name = "TDM Loopback",
+ .capture = {
+ .stream_name = "Loopback",
+ .channels_min = 1,
+ .channels_max = AXG_TDM_CHANNEL_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 768000,
+ .formats = AXG_TDM_FORMATS,
+ },
+ .id = TDM_IFACE_LOOPBACK,
+ .ops = &axg_tdm_iface_ops,
+ },
+};
+
+static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ enum snd_soc_bias_level now = snd_soc_dapm_get_bias_level(dapm);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (now == SND_SOC_BIAS_STANDBY)
+ ret = clk_prepare_enable(iface->mclk);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (now == SND_SOC_BIAS_PREPARE)
+ clk_disable_unprepare(iface->mclk);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = {
+ SND_SOC_DAPM_SIGGEN("Playback Signal"),
+};
+
+static const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = {
+ { "Loopback", NULL, "Playback Signal" },
+};
+
+static const struct snd_soc_component_driver axg_tdm_iface_component_drv = {
+ .dapm_widgets = axg_tdm_iface_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_tdm_iface_dapm_widgets),
+ .dapm_routes = axg_tdm_iface_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_tdm_iface_dapm_routes),
+ .set_bias_level = axg_tdm_iface_set_bias_level,
+};
+
+static const struct of_device_id axg_tdm_iface_of_match[] = {
+ { .compatible = "amlogic,axg-tdm-iface", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match);
+
+static int axg_tdm_iface_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_driver *dai_drv;
+ struct axg_tdm_iface *iface;
+
+ iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL);
+ if (!iface)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, iface);
+
+ /*
+ * Duplicate dai driver: depending on the slot masks configuration
+ * We'll change the number of channel provided by DAI stream, so dpcm
+ * channel merge can be done properly
+ */
+ dai_drv = devm_kmemdup_array(dev, axg_tdm_iface_dai_drv, ARRAY_SIZE(axg_tdm_iface_dai_drv),
+ sizeof(axg_tdm_iface_dai_drv[0]), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ /* Bit clock provided on the pad */
+ iface->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(iface->sclk))
+ return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n");
+
+ /* Sample clock provided on the pad */
+ iface->lrclk = devm_clk_get(dev, "lrclk");
+ if (IS_ERR(iface->lrclk))
+ return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n");
+
+ /*
+ * mclk maybe be missing when the cpu dai is in slave mode and
+ * the codec does not require it to provide a master clock.
+ * At this point, ignore the error if mclk is missing. We'll
+ * throw an error if the cpu dai is master and mclk is missing
+ */
+ iface->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(iface->mclk))
+ return dev_err_probe(dev, PTR_ERR(iface->mclk), "failed to get mclk\n");
+
+ return devm_snd_soc_register_component(dev,
+ &axg_tdm_iface_component_drv, dai_drv,
+ ARRAY_SIZE(axg_tdm_iface_dai_drv));
+}
+
+static struct platform_driver axg_tdm_iface_pdrv = {
+ .probe = axg_tdm_iface_probe,
+ .driver = {
+ .name = "axg-tdm-iface",
+ .of_match_table = axg_tdm_iface_of_match,
+ },
+};
+module_platform_driver(axg_tdm_iface_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG TDM interface driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h
new file mode 100644
index 000000000000..acfcd48f8a00
--- /dev/null
+++ b/sound/soc/meson/axg-tdm.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT)
+ *
+ * Copyright (c) 2018 Baylibre SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_AXG_TDM_H
+#define _MESON_AXG_TDM_H
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define AXG_TDM_NUM_LANES 4
+#define AXG_TDM_CHANNEL_MAX 128
+#define AXG_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct axg_tdm_iface {
+ struct clk *sclk;
+ struct clk *lrclk;
+ struct clk *mclk;
+ unsigned long mclk_rate;
+
+ /* format is common to all the DAIs of the iface */
+ unsigned int fmt;
+ unsigned int slots;
+ unsigned int slot_width;
+
+ /* For component wide symmetry */
+ int rate;
+};
+
+static inline bool axg_tdm_lrclk_invert(unsigned int fmt)
+{
+ return ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) ^
+ !!(fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_NB_IF));
+}
+
+static inline bool axg_tdm_sclk_invert(unsigned int fmt)
+{
+ return fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_IB_NF);
+}
+
+struct axg_tdm_stream {
+ struct axg_tdm_iface *iface;
+ struct list_head formatter_list;
+ struct mutex lock;
+ unsigned int channels;
+ unsigned int width;
+ unsigned int physical_width;
+ u32 *mask;
+ bool ready;
+
+ /* For continuous clock tracking */
+ bool clk_enabled;
+};
+
+struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
+void axg_tdm_stream_free(struct axg_tdm_stream *ts);
+int axg_tdm_stream_start(struct axg_tdm_stream *ts);
+void axg_tdm_stream_stop(struct axg_tdm_stream *ts);
+int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
+ unsigned int fmt);
+
+static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts)
+{
+ axg_tdm_stream_stop(ts);
+ return axg_tdm_stream_start(ts);
+}
+
+int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask,
+ u32 *rx_mask, unsigned int slots,
+ unsigned int slot_width);
+
+#endif /* _MESON_AXG_TDM_H */
diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c
new file mode 100644
index 000000000000..c8f6ea24ae78
--- /dev/null
+++ b/sound/soc/meson/axg-tdmin.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-tdm-formatter.h"
+
+#define TDMIN_CTRL 0x00
+#define TDMIN_CTRL_ENABLE BIT(31)
+#define TDMIN_CTRL_I2S_MODE BIT(30)
+#define TDMIN_CTRL_RST_OUT BIT(29)
+#define TDMIN_CTRL_RST_IN BIT(28)
+#define TDMIN_CTRL_WS_INV BIT(25)
+#define TDMIN_CTRL_SEL_SHIFT 20
+#define TDMIN_CTRL_IN_BIT_SKEW_MASK GENMASK(18, 16)
+#define TDMIN_CTRL_IN_BIT_SKEW(x) ((x) << 16)
+#define TDMIN_CTRL_LSB_FIRST BIT(5)
+#define TDMIN_CTRL_BITNUM_MASK GENMASK(4, 0)
+#define TDMIN_CTRL_BITNUM(x) ((x) << 0)
+#define TDMIN_SWAP 0x04
+#define TDMIN_MASK0 0x08
+#define TDMIN_MASK1 0x0c
+#define TDMIN_MASK2 0x10
+#define TDMIN_MASK3 0x14
+#define TDMIN_STAT 0x18
+#define TDMIN_MUTE_VAL 0x1c
+#define TDMIN_MUTE0 0x20
+#define TDMIN_MUTE1 0x24
+#define TDMIN_MUTE2 0x28
+#define TDMIN_MUTE3 0x2c
+
+static const struct regmap_config axg_tdmin_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TDMIN_MUTE3,
+};
+
+static const char * const axg_tdmin_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 5", "IN 6", "IN 7",
+ "IN 8", "IN 9", "IN 10", "IN 11", "IN 12", "IN 13", "IN 14", "IN 15",
+};
+
+/* Change to special mux control to reset dapm */
+static SOC_ENUM_SINGLE_DECL(axg_tdmin_sel_enum, TDMIN_CTRL,
+ TDMIN_CTRL_SEL_SHIFT, axg_tdmin_sel_texts);
+
+static const struct snd_kcontrol_new axg_tdmin_in_mux =
+ SOC_DAPM_ENUM("Input Source", axg_tdmin_sel_enum);
+
+static struct snd_soc_dai *
+axg_tdmin_get_be(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dai *be;
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ if (p->source->id == snd_soc_dapm_dai_out)
+ return (struct snd_soc_dai *)p->source->priv;
+
+ be = axg_tdmin_get_be(p->source);
+ if (be)
+ return be;
+ }
+
+ return NULL;
+}
+
+static struct axg_tdm_stream *
+axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dai *be = axg_tdmin_get_be(w);
+
+ if (!be)
+ return NULL;
+
+ return snd_soc_dai_dma_data_get_capture(be);
+}
+
+static void axg_tdmin_enable(struct regmap *map)
+{
+ /* Apply both reset */
+ regmap_update_bits(map, TDMIN_CTRL,
+ TDMIN_CTRL_RST_OUT | TDMIN_CTRL_RST_IN, 0);
+
+ /* Clear out reset before in reset */
+ regmap_update_bits(map, TDMIN_CTRL,
+ TDMIN_CTRL_RST_OUT, TDMIN_CTRL_RST_OUT);
+ regmap_update_bits(map, TDMIN_CTRL,
+ TDMIN_CTRL_RST_IN, TDMIN_CTRL_RST_IN);
+
+ /* Actually enable tdmin */
+ regmap_update_bits(map, TDMIN_CTRL,
+ TDMIN_CTRL_ENABLE, TDMIN_CTRL_ENABLE);
+}
+
+static void axg_tdmin_disable(struct regmap *map)
+{
+ regmap_update_bits(map, TDMIN_CTRL, TDMIN_CTRL_ENABLE, 0);
+}
+
+static int axg_tdmin_prepare(struct regmap *map,
+ const struct axg_tdm_formatter_hw *quirks,
+ struct axg_tdm_stream *ts)
+{
+ unsigned int val, skew = quirks->skew_offset;
+
+ /* Set stream skew */
+ switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ skew += 1;
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+
+ default:
+ pr_err("Unsupported format: %u\n",
+ ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ val = TDMIN_CTRL_IN_BIT_SKEW(skew);
+
+ /* Set stream format mode */
+ switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= TDMIN_CTRL_I2S_MODE;
+ break;
+ }
+
+ /* If the sample clock is inverted, invert it back for the formatter */
+ if (axg_tdm_lrclk_invert(ts->iface->fmt))
+ val |= TDMIN_CTRL_WS_INV;
+
+ /* Set the slot width */
+ val |= TDMIN_CTRL_BITNUM(ts->iface->slot_width - 1);
+
+ /*
+ * The following also reset LSB_FIRST which result in the formatter
+ * placing the first bit received at bit 31
+ */
+ regmap_update_bits(map, TDMIN_CTRL,
+ (TDMIN_CTRL_IN_BIT_SKEW_MASK | TDMIN_CTRL_WS_INV |
+ TDMIN_CTRL_I2S_MODE | TDMIN_CTRL_LSB_FIRST |
+ TDMIN_CTRL_BITNUM_MASK), val);
+
+ /* Set static swap mask configuration */
+ regmap_write(map, TDMIN_SWAP, 0x76543210);
+
+ return axg_tdm_formatter_set_channel_masks(map, ts, TDMIN_MASK0);
+}
+
+static const struct snd_soc_dapm_widget axg_tdmin_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 7", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 8", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 9", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 10", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 11", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 12", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 13", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 14", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 15", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmin_in_mux),
+ SND_SOC_DAPM_PGA_E("DEC", SND_SOC_NOPM, 0, 0, NULL, 0,
+ axg_tdm_formatter_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route axg_tdmin_dapm_routes[] = {
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "SRC SEL", "IN 3", "IN 3" },
+ { "SRC SEL", "IN 4", "IN 4" },
+ { "SRC SEL", "IN 5", "IN 5" },
+ { "SRC SEL", "IN 6", "IN 6" },
+ { "SRC SEL", "IN 7", "IN 7" },
+ { "SRC SEL", "IN 8", "IN 8" },
+ { "SRC SEL", "IN 9", "IN 9" },
+ { "SRC SEL", "IN 10", "IN 10" },
+ { "SRC SEL", "IN 11", "IN 11" },
+ { "SRC SEL", "IN 12", "IN 12" },
+ { "SRC SEL", "IN 13", "IN 13" },
+ { "SRC SEL", "IN 14", "IN 14" },
+ { "SRC SEL", "IN 15", "IN 15" },
+ { "DEC", NULL, "SRC SEL" },
+ { "OUT", NULL, "DEC" },
+};
+
+static const struct snd_soc_component_driver axg_tdmin_component_drv = {
+ .dapm_widgets = axg_tdmin_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_tdmin_dapm_widgets),
+ .dapm_routes = axg_tdmin_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_tdmin_dapm_routes),
+};
+
+static const struct axg_tdm_formatter_ops axg_tdmin_ops = {
+ .get_stream = axg_tdmin_get_tdm_stream,
+ .prepare = axg_tdmin_prepare,
+ .enable = axg_tdmin_enable,
+ .disable = axg_tdmin_disable,
+};
+
+static const struct axg_tdm_formatter_driver axg_tdmin_drv = {
+ .component_drv = &axg_tdmin_component_drv,
+ .regmap_cfg = &axg_tdmin_regmap_cfg,
+ .ops = &axg_tdmin_ops,
+ .quirks = &(const struct axg_tdm_formatter_hw) {
+ .skew_offset = 3,
+ },
+};
+
+static const struct of_device_id axg_tdmin_of_match[] = {
+ {
+ .compatible = "amlogic,axg-tdmin",
+ .data = &axg_tdmin_drv,
+ }, {
+ .compatible = "amlogic,g12a-tdmin",
+ .data = &axg_tdmin_drv,
+ }, {
+ .compatible = "amlogic,sm1-tdmin",
+ .data = &axg_tdmin_drv,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
+
+static struct platform_driver axg_tdmin_pdrv = {
+ .probe = axg_tdm_formatter_probe,
+ .driver = {
+ .name = "axg-tdmin",
+ .of_match_table = axg_tdmin_of_match,
+ },
+};
+module_platform_driver(axg_tdmin_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG TDM input formatter driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c
new file mode 100644
index 000000000000..c4039e4f0847
--- /dev/null
+++ b/sound/soc/meson/axg-tdmout.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-tdm-formatter.h"
+
+#define TDMOUT_CTRL0 0x00
+#define TDMOUT_CTRL0_BITNUM_MASK GENMASK(4, 0)
+#define TDMOUT_CTRL0_BITNUM(x) ((x) << 0)
+#define TDMOUT_CTRL0_SLOTNUM_MASK GENMASK(9, 5)
+#define TDMOUT_CTRL0_SLOTNUM(x) ((x) << 5)
+#define TDMOUT_CTRL0_INIT_BITNUM_MASK GENMASK(19, 15)
+#define TDMOUT_CTRL0_INIT_BITNUM(x) ((x) << 15)
+#define TDMOUT_CTRL0_ENABLE BIT(31)
+#define TDMOUT_CTRL0_RST_OUT BIT(29)
+#define TDMOUT_CTRL0_RST_IN BIT(28)
+#define TDMOUT_CTRL1 0x04
+#define TDMOUT_CTRL1_TYPE_MASK GENMASK(6, 4)
+#define TDMOUT_CTRL1_TYPE(x) ((x) << 4)
+#define SM1_TDMOUT_CTRL1_GAIN_EN 7
+#define TDMOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8)
+#define TDMOUT_CTRL1_MSB_POS(x) ((x) << 8)
+#define TDMOUT_CTRL1_SEL_SHIFT 24
+#define TDMOUT_CTRL1_GAIN_EN 26
+#define TDMOUT_CTRL1_WS_INV BIT(28)
+#define TDMOUT_SWAP 0x08
+#define TDMOUT_MASK0 0x0c
+#define TDMOUT_MASK1 0x10
+#define TDMOUT_MASK2 0x14
+#define TDMOUT_MASK3 0x18
+#define TDMOUT_STAT 0x1c
+#define TDMOUT_GAIN0 0x20
+#define TDMOUT_GAIN1 0x24
+#define TDMOUT_MUTE_VAL 0x28
+#define TDMOUT_MUTE0 0x2c
+#define TDMOUT_MUTE1 0x30
+#define TDMOUT_MUTE2 0x34
+#define TDMOUT_MUTE3 0x38
+#define TDMOUT_MASK_VAL 0x3c
+
+static const struct regmap_config axg_tdmout_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TDMOUT_MASK_VAL,
+};
+
+static struct snd_soc_dai *
+axg_tdmout_get_be(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dai *be;
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ if (p->sink->id == snd_soc_dapm_dai_in)
+ return (struct snd_soc_dai *)p->sink->priv;
+
+ be = axg_tdmout_get_be(p->sink);
+ if (be)
+ return be;
+ }
+
+ return NULL;
+}
+
+static struct axg_tdm_stream *
+axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dai *be = axg_tdmout_get_be(w);
+
+ if (!be)
+ return NULL;
+
+ return snd_soc_dai_dma_data_get_playback(be);
+}
+
+static void axg_tdmout_enable(struct regmap *map)
+{
+ /* Apply both reset */
+ regmap_update_bits(map, TDMOUT_CTRL0,
+ TDMOUT_CTRL0_RST_OUT | TDMOUT_CTRL0_RST_IN, 0);
+
+ /* Clear out reset before in reset */
+ regmap_update_bits(map, TDMOUT_CTRL0,
+ TDMOUT_CTRL0_RST_OUT, TDMOUT_CTRL0_RST_OUT);
+ regmap_update_bits(map, TDMOUT_CTRL0,
+ TDMOUT_CTRL0_RST_IN, TDMOUT_CTRL0_RST_IN);
+
+ /* Actually enable tdmout */
+ regmap_update_bits(map, TDMOUT_CTRL0,
+ TDMOUT_CTRL0_ENABLE, TDMOUT_CTRL0_ENABLE);
+}
+
+static void axg_tdmout_disable(struct regmap *map)
+{
+ regmap_update_bits(map, TDMOUT_CTRL0, TDMOUT_CTRL0_ENABLE, 0);
+}
+
+static int axg_tdmout_prepare(struct regmap *map,
+ const struct axg_tdm_formatter_hw *quirks,
+ struct axg_tdm_stream *ts)
+{
+ unsigned int val, skew = quirks->skew_offset;
+
+ /* Set the stream skew */
+ switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ skew += 1;
+ break;
+
+ default:
+ pr_err("Unsupported format: %u\n",
+ ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ val = TDMOUT_CTRL0_INIT_BITNUM(skew);
+
+ /* Set the slot width */
+ val |= TDMOUT_CTRL0_BITNUM(ts->iface->slot_width - 1);
+
+ /* Set the slot number */
+ val |= TDMOUT_CTRL0_SLOTNUM(ts->iface->slots - 1);
+
+ regmap_update_bits(map, TDMOUT_CTRL0,
+ TDMOUT_CTRL0_INIT_BITNUM_MASK |
+ TDMOUT_CTRL0_BITNUM_MASK |
+ TDMOUT_CTRL0_SLOTNUM_MASK, val);
+
+ /* Set the sample width */
+ val = TDMOUT_CTRL1_MSB_POS(ts->width - 1);
+
+ /* FIFO data are arranged in chunks of 64bits */
+ switch (ts->physical_width) {
+ case 8:
+ /* 8 samples of 8 bits */
+ val |= TDMOUT_CTRL1_TYPE(0);
+ break;
+ case 16:
+ /* 4 samples of 16 bits - right justified */
+ val |= TDMOUT_CTRL1_TYPE(2);
+ break;
+ case 32:
+ /* 2 samples of 32 bits - right justified */
+ val |= TDMOUT_CTRL1_TYPE(4);
+ break;
+ default:
+ pr_err("Unsupported physical width: %u\n",
+ ts->physical_width);
+ return -EINVAL;
+ }
+
+ /* If the sample clock is inverted, invert it back for the formatter */
+ if (axg_tdm_lrclk_invert(ts->iface->fmt))
+ val |= TDMOUT_CTRL1_WS_INV;
+
+ regmap_update_bits(map, TDMOUT_CTRL1,
+ (TDMOUT_CTRL1_TYPE_MASK | TDMOUT_CTRL1_MSB_POS_MASK |
+ TDMOUT_CTRL1_WS_INV), val);
+
+ /* Set static swap mask configuration */
+ regmap_write(map, TDMOUT_SWAP, 0x76543210);
+
+ return axg_tdm_formatter_set_channel_masks(map, ts, TDMOUT_MASK0);
+}
+
+static const struct snd_kcontrol_new axg_tdmout_controls[] = {
+ SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
+ SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
+ SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
+ TDMOUT_CTRL1_GAIN_EN, 1, 0),
+};
+
+static const char * const axg_tdmout_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1,
+ TDMOUT_CTRL1_SEL_SHIFT, axg_tdmout_sel_texts);
+
+static const struct snd_kcontrol_new axg_tdmout_in_mux =
+ SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum);
+
+static const struct snd_soc_dapm_widget axg_tdmout_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmout_in_mux),
+ SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0,
+ axg_tdm_formatter_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route axg_tdmout_dapm_routes[] = {
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "ENC", NULL, "SRC SEL" },
+ { "OUT", NULL, "ENC" },
+};
+
+static const struct snd_soc_component_driver axg_tdmout_component_drv = {
+ .controls = axg_tdmout_controls,
+ .num_controls = ARRAY_SIZE(axg_tdmout_controls),
+ .dapm_widgets = axg_tdmout_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_tdmout_dapm_widgets),
+ .dapm_routes = axg_tdmout_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_tdmout_dapm_routes),
+};
+
+static const struct axg_tdm_formatter_ops axg_tdmout_ops = {
+ .get_stream = axg_tdmout_get_tdm_stream,
+ .prepare = axg_tdmout_prepare,
+ .enable = axg_tdmout_enable,
+ .disable = axg_tdmout_disable,
+};
+
+static const struct axg_tdm_formatter_driver axg_tdmout_drv = {
+ .component_drv = &axg_tdmout_component_drv,
+ .regmap_cfg = &axg_tdmout_regmap_cfg,
+ .ops = &axg_tdmout_ops,
+ .quirks = &(const struct axg_tdm_formatter_hw) {
+ .skew_offset = 1,
+ },
+};
+
+static const struct axg_tdm_formatter_driver g12a_tdmout_drv = {
+ .component_drv = &axg_tdmout_component_drv,
+ .regmap_cfg = &axg_tdmout_regmap_cfg,
+ .ops = &axg_tdmout_ops,
+ .quirks = &(const struct axg_tdm_formatter_hw) {
+ .skew_offset = 2,
+ },
+};
+
+static const struct snd_kcontrol_new sm1_tdmout_controls[] = {
+ SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
+ SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
+ SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
+ SM1_TDMOUT_CTRL1_GAIN_EN, 1, 0),
+};
+
+static const char * const sm1_tdmout_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2", "IN 3", "IN 4",
+};
+
+static SOC_ENUM_SINGLE_DECL(sm1_tdmout_sel_enum, TDMOUT_CTRL1,
+ TDMOUT_CTRL1_SEL_SHIFT, sm1_tdmout_sel_texts);
+
+static const struct snd_kcontrol_new sm1_tdmout_in_mux =
+ SOC_DAPM_ENUM("Input Source", sm1_tdmout_sel_enum);
+
+static const struct snd_soc_dapm_widget sm1_tdmout_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &sm1_tdmout_in_mux),
+ SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0,
+ axg_tdm_formatter_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sm1_tdmout_dapm_routes[] = {
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "SRC SEL", "IN 3", "IN 3" },
+ { "SRC SEL", "IN 4", "IN 4" },
+ { "ENC", NULL, "SRC SEL" },
+ { "OUT", NULL, "ENC" },
+};
+
+static const struct snd_soc_component_driver sm1_tdmout_component_drv = {
+ .controls = sm1_tdmout_controls,
+ .num_controls = ARRAY_SIZE(sm1_tdmout_controls),
+ .dapm_widgets = sm1_tdmout_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sm1_tdmout_dapm_widgets),
+ .dapm_routes = sm1_tdmout_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sm1_tdmout_dapm_routes),
+};
+
+static const struct axg_tdm_formatter_driver sm1_tdmout_drv = {
+ .component_drv = &sm1_tdmout_component_drv,
+ .regmap_cfg = &axg_tdmout_regmap_cfg,
+ .ops = &axg_tdmout_ops,
+ .quirks = &(const struct axg_tdm_formatter_hw) {
+ .skew_offset = 2,
+ },
+};
+
+static const struct of_device_id axg_tdmout_of_match[] = {
+ {
+ .compatible = "amlogic,axg-tdmout",
+ .data = &axg_tdmout_drv,
+ }, {
+ .compatible = "amlogic,g12a-tdmout",
+ .data = &g12a_tdmout_drv,
+ }, {
+ .compatible = "amlogic,sm1-tdmout",
+ .data = &sm1_tdmout_drv,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_tdmout_of_match);
+
+static struct platform_driver axg_tdmout_pdrv = {
+ .probe = axg_tdm_formatter_probe,
+ .driver = {
+ .name = "axg-tdmout",
+ .of_match_table = axg_tdmout_of_match,
+ },
+};
+module_platform_driver(axg_tdmout_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG TDM output formatter driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c
new file mode 100644
index 000000000000..03512da4092b
--- /dev/null
+++ b/sound/soc/meson/axg-toddr.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+/* This driver implements the frontend capture DAI of AXG based SoCs */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "axg-fifo.h"
+
+#define CTRL0_TODDR_SEL_RESAMPLE BIT(30)
+#define CTRL0_TODDR_EXT_SIGNED BIT(29)
+#define CTRL0_TODDR_PP_MODE BIT(28)
+#define CTRL0_TODDR_SYNC_CH BIT(27)
+#define CTRL0_TODDR_TYPE GENMASK(15, 13)
+#define CTRL0_TODDR_MSB_POS GENMASK(12, 8)
+#define CTRL0_TODDR_LSB_POS GENMASK(7, 3)
+#define CTRL1_TODDR_FORCE_FINISH BIT(25)
+#define CTRL1_SEL_SHIFT 28
+
+#define TODDR_MSB_POS 31
+
+static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static int g12a_toddr_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+
+ /* Reset the write pointer to the FIFO_INIT_ADDR */
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_TODDR_FORCE_FINISH, 0);
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_TODDR_FORCE_FINISH, CTRL1_TODDR_FORCE_FINISH);
+ regmap_update_bits(fifo->map, FIFO_CTRL1,
+ CTRL1_TODDR_FORCE_FINISH, 0);
+
+ return 0;
+}
+
+static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+ unsigned int type, width;
+
+ switch (params_physical_width(params)) {
+ case 8:
+ type = 0; /* 8 samples of 8 bits */
+ break;
+ case 16:
+ type = 2; /* 4 samples of 16 bits - right justified */
+ break;
+ case 32:
+ type = 4; /* 2 samples of 32 bits - right justified */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ width = params_width(params);
+
+ regmap_update_bits(fifo->map, FIFO_CTRL0,
+ CTRL0_TODDR_TYPE |
+ CTRL0_TODDR_MSB_POS |
+ CTRL0_TODDR_LSB_POS,
+ FIELD_PREP(CTRL0_TODDR_TYPE, type) |
+ FIELD_PREP(CTRL0_TODDR_MSB_POS, TODDR_MSB_POS) |
+ FIELD_PREP(CTRL0_TODDR_LSB_POS, TODDR_MSB_POS - (width - 1)));
+
+ return 0;
+}
+
+static int axg_toddr_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* Enable pclk to access registers and clock the fifo ip */
+ ret = clk_prepare_enable(fifo->pclk);
+ if (ret)
+ return ret;
+
+ /* Select orginal data - resampling not supported ATM */
+ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0);
+
+ /* Only signed format are supported ATM */
+ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED,
+ CTRL0_TODDR_EXT_SIGNED);
+
+ /* Apply single buffer mode to the interface */
+ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0);
+
+ return 0;
+}
+
+static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(fifo->pclk);
+}
+
+static const struct snd_soc_dai_ops axg_toddr_ops = {
+ .hw_params = axg_toddr_dai_hw_params,
+ .startup = axg_toddr_dai_startup,
+ .shutdown = axg_toddr_dai_shutdown,
+ .pcm_new = axg_toddr_pcm_new,
+};
+
+static struct snd_soc_dai_driver axg_toddr_dai_drv = {
+ .name = "TODDR",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = AXG_FIFO_CH_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5515,
+ .rate_max = 768000,
+ .formats = AXG_FIFO_FORMATS,
+ },
+ .ops = &axg_toddr_ops,
+};
+
+static const char * const axg_toddr_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 5", "IN 6", "IN 7"
+};
+
+static SOC_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
+ axg_toddr_sel_texts);
+
+static const struct snd_kcontrol_new axg_toddr_in_mux =
+ SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum);
+
+static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux),
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 7", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = {
+ { "Capture", NULL, "SRC SEL" },
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "SRC SEL", "IN 3", "IN 3" },
+ { "SRC SEL", "IN 4", "IN 4" },
+ { "SRC SEL", "IN 5", "IN 5" },
+ { "SRC SEL", "IN 6", "IN 6" },
+ { "SRC SEL", "IN 7", "IN 7" },
+};
+
+static const struct snd_soc_component_driver axg_toddr_component_drv = {
+ .dapm_widgets = axg_toddr_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets),
+ .dapm_routes = axg_toddr_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes),
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .hw_params = axg_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
+ .legacy_dai_naming = 1,
+};
+
+static const struct axg_fifo_match_data axg_toddr_match_data = {
+ .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
+ .component_drv = &axg_toddr_component_drv,
+ .dai_drv = &axg_toddr_dai_drv
+};
+
+static int g12a_toddr_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = axg_toddr_dai_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure the first channel ends up in the at beginning of the output
+ * As weird as it looks, without this the first channel may be misplaced
+ * in memory, with a random shift of 2 channels.
+ */
+ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SYNC_CH,
+ CTRL0_TODDR_SYNC_CH);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops g12a_toddr_ops = {
+ .prepare = g12a_toddr_dai_prepare,
+ .hw_params = axg_toddr_dai_hw_params,
+ .startup = g12a_toddr_dai_startup,
+ .shutdown = axg_toddr_dai_shutdown,
+ .pcm_new = axg_toddr_pcm_new,
+};
+
+static struct snd_soc_dai_driver g12a_toddr_dai_drv = {
+ .name = "TODDR",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = AXG_FIFO_CH_MAX,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5515,
+ .rate_max = 768000,
+ .formats = AXG_FIFO_FORMATS,
+ },
+ .ops = &g12a_toddr_ops,
+};
+
+static const struct snd_soc_component_driver g12a_toddr_component_drv = {
+ .dapm_widgets = axg_toddr_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets),
+ .dapm_routes = axg_toddr_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes),
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
+ .legacy_dai_naming = 1,
+};
+
+static const struct axg_fifo_match_data g12a_toddr_match_data = {
+ .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
+ .component_drv = &g12a_toddr_component_drv,
+ .dai_drv = &g12a_toddr_dai_drv
+};
+
+static const char * const sm1_toddr_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 5", "IN 6", "IN 7",
+ "IN 8", "IN 9", "IN 10", "IN 11", "IN 12", "IN 13", "IN 14", "IN 15"
+};
+
+static SOC_ENUM_SINGLE_DECL(sm1_toddr_sel_enum, FIFO_CTRL1, CTRL1_SEL_SHIFT,
+ sm1_toddr_sel_texts);
+
+static const struct snd_kcontrol_new sm1_toddr_in_mux =
+ SOC_DAPM_ENUM("Input Source", sm1_toddr_sel_enum);
+
+static const struct snd_soc_dapm_widget sm1_toddr_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &sm1_toddr_in_mux),
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 7", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 8", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 9", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 10", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 11", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 12", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 13", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 14", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 15", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sm1_toddr_dapm_routes[] = {
+ { "Capture", NULL, "SRC SEL" },
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "SRC SEL", "IN 3", "IN 3" },
+ { "SRC SEL", "IN 4", "IN 4" },
+ { "SRC SEL", "IN 5", "IN 5" },
+ { "SRC SEL", "IN 6", "IN 6" },
+ { "SRC SEL", "IN 7", "IN 7" },
+ { "SRC SEL", "IN 8", "IN 8" },
+ { "SRC SEL", "IN 9", "IN 9" },
+ { "SRC SEL", "IN 10", "IN 10" },
+ { "SRC SEL", "IN 11", "IN 11" },
+ { "SRC SEL", "IN 12", "IN 12" },
+ { "SRC SEL", "IN 13", "IN 13" },
+ { "SRC SEL", "IN 14", "IN 14" },
+ { "SRC SEL", "IN 15", "IN 15" },
+};
+
+static const struct snd_soc_component_driver sm1_toddr_component_drv = {
+ .dapm_widgets = sm1_toddr_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sm1_toddr_dapm_widgets),
+ .dapm_routes = sm1_toddr_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sm1_toddr_dapm_routes),
+ .open = axg_fifo_pcm_open,
+ .close = axg_fifo_pcm_close,
+ .hw_params = g12a_fifo_pcm_hw_params,
+ .hw_free = axg_fifo_pcm_hw_free,
+ .pointer = axg_fifo_pcm_pointer,
+ .trigger = axg_fifo_pcm_trigger,
+ .legacy_dai_naming = 1,
+};
+
+static const struct axg_fifo_match_data sm1_toddr_match_data = {
+ .field_threshold = REG_FIELD(FIFO_CTRL1, 12, 23),
+ .component_drv = &sm1_toddr_component_drv,
+ .dai_drv = &g12a_toddr_dai_drv
+};
+
+static const struct of_device_id axg_toddr_of_match[] = {
+ {
+ .compatible = "amlogic,axg-toddr",
+ .data = &axg_toddr_match_data,
+ }, {
+ .compatible = "amlogic,g12a-toddr",
+ .data = &g12a_toddr_match_data,
+ }, {
+ .compatible = "amlogic,sm1-toddr",
+ .data = &sm1_toddr_match_data,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, axg_toddr_of_match);
+
+static struct platform_driver axg_toddr_pdrv = {
+ .probe = axg_fifo_probe,
+ .driver = {
+ .name = "axg-toddr",
+ .of_match_table = axg_toddr_of_match,
+ },
+};
+module_platform_driver(axg_toddr_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AXG capture fifo driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
new file mode 100644
index 000000000000..a95375b53f0a
--- /dev/null
+++ b/sound/soc/meson/g12a-toacodec.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/meson-g12a-toacodec.h>
+#include "axg-tdm.h"
+#include "meson-codec-glue.h"
+
+#define G12A_TOACODEC_DRV_NAME "g12a-toacodec"
+
+#define TOACODEC_CTRL0 0x0
+#define CTRL0_ENABLE_SHIFT 31
+#define CTRL0_DAT_SEL_SM1_MSB 19
+#define CTRL0_DAT_SEL_SM1_LSB 18
+#define CTRL0_DAT_SEL_MSB 15
+#define CTRL0_DAT_SEL_LSB 14
+#define CTRL0_LANE_SEL_SM1 16
+#define CTRL0_LANE_SEL 12
+#define CTRL0_LRCLK_SEL_SM1_MSB 14
+#define CTRL0_LRCLK_SEL_SM1_LSB 12
+#define CTRL0_LRCLK_SEL_MSB 9
+#define CTRL0_LRCLK_SEL_LSB 8
+#define CTRL0_LRCLK_INV_SM1 BIT(10)
+#define CTRL0_BLK_CAP_INV_SM1 BIT(9)
+#define CTRL0_BLK_CAP_INV BIT(7)
+#define CTRL0_BCLK_O_INV_SM1 BIT(8)
+#define CTRL0_BCLK_O_INV BIT(6)
+#define CTRL0_BCLK_SEL_SM1_MSB 6
+#define CTRL0_BCLK_SEL_MSB 5
+#define CTRL0_BCLK_SEL_LSB 4
+#define CTRL0_MCLK_SEL GENMASK(2, 0)
+
+#define TOACODEC_OUT_CHMAX 2
+
+struct g12a_toacodec {
+ struct regmap_field *field_dat_sel;
+ struct regmap_field *field_lrclk_sel;
+ struct regmap_field *field_bclk_sel;
+};
+
+struct g12a_toacodec_match_data {
+ const struct snd_soc_component_driver *component_drv;
+ struct reg_field field_dat_sel;
+ struct reg_field field_lrclk_sel;
+ struct reg_field field_bclk_sel;
+};
+
+static const char * const g12a_toacodec_mux_texts[] = {
+ "I2S A", "I2S B", "I2S C",
+};
+
+static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct g12a_toacodec *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, reg;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
+ regmap_field_read(priv->field_dat_sel, &reg);
+
+ if (mux == reg)
+ return 0;
+
+ /* Force disconnect of the mux while updating */
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
+
+ regmap_field_write(priv->field_dat_sel, mux);
+ regmap_field_write(priv->field_lrclk_sel, mux);
+ regmap_field_write(priv->field_bclk_sel, mux);
+
+ /*
+ * FIXME:
+ * On this soc, the glue gets the MCLK directly from the clock
+ * controller instead of going the through the TDM interface.
+ *
+ * Here we assume interface A uses clock A, etc ... While it is
+ * true for now, it could be different. Instead the glue should
+ * find out the clock used by the interface and select the same
+ * source. For that, we will need regmap backed clock mux which
+ * is a work in progress
+ */
+ snd_soc_component_update_bits(component, e->reg,
+ CTRL0_MCLK_SEL,
+ FIELD_PREP(CTRL0_MCLK_SEL, mux));
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return 1;
+}
+
+static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0,
+ CTRL0_DAT_SEL_LSB,
+ g12a_toacodec_mux_texts);
+
+static SOC_ENUM_SINGLE_DECL(sm1_toacodec_mux_enum, TOACODEC_CTRL0,
+ CTRL0_DAT_SEL_SM1_LSB,
+ g12a_toacodec_mux_texts);
+
+static const struct snd_kcontrol_new g12a_toacodec_mux =
+ SOC_DAPM_ENUM_EXT("Source", g12a_toacodec_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ g12a_toacodec_mux_put_enum);
+
+static const struct snd_kcontrol_new sm1_toacodec_mux =
+ SOC_DAPM_ENUM_EXT("Source", sm1_toacodec_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ g12a_toacodec_mux_put_enum);
+
+static const struct snd_kcontrol_new g12a_toacodec_out_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOACODEC_CTRL0,
+ CTRL0_ENABLE_SHIFT, 1, 0);
+
+static const struct snd_soc_dapm_widget g12a_toacodec_widgets[] = {
+ SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
+ &g12a_toacodec_mux),
+ SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
+ &g12a_toacodec_out_enable),
+};
+
+static const struct snd_soc_dapm_widget sm1_toacodec_widgets[] = {
+ SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
+ &sm1_toacodec_mux),
+ SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
+ &g12a_toacodec_out_enable),
+};
+
+static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct meson_codec_glue_input *data;
+ int ret;
+
+ ret = meson_codec_glue_input_hw_params(substream, params, dai);
+ if (ret)
+ return ret;
+
+ /* The glue will provide 1 lane out of the 4 to the output */
+ data = meson_codec_glue_input_get_data(dai);
+ data->params.channels_min = min_t(unsigned int, TOACODEC_OUT_CHMAX,
+ data->params.channels_min);
+ data->params.channels_max = min_t(unsigned int, TOACODEC_OUT_CHMAX,
+ data->params.channels_max);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops g12a_toacodec_input_ops = {
+ .probe = meson_codec_glue_input_dai_probe,
+ .remove = meson_codec_glue_input_dai_remove,
+ .hw_params = g12a_toacodec_input_hw_params,
+ .set_fmt = meson_codec_glue_input_set_fmt,
+};
+
+static const struct snd_soc_dai_ops g12a_toacodec_output_ops = {
+ .startup = meson_codec_glue_output_startup,
+};
+
+#define TOACODEC_STREAM(xname, xsuffix, xchmax) \
+{ \
+ .stream_name = xname " " xsuffix, \
+ .channels_min = 1, \
+ .channels_max = (xchmax), \
+ .rate_min = 5512, \
+ .rate_max = 192000, \
+ .formats = AXG_TDM_FORMATS, \
+}
+
+#define TOACODEC_INPUT(xname, xid) { \
+ .name = xname, \
+ .id = (xid), \
+ .playback = TOACODEC_STREAM(xname, "Playback", 8), \
+ .ops = &g12a_toacodec_input_ops, \
+}
+
+#define TOACODEC_OUTPUT(xname, xid) { \
+ .name = xname, \
+ .id = (xid), \
+ .capture = TOACODEC_STREAM(xname, "Capture", TOACODEC_OUT_CHMAX), \
+ .ops = &g12a_toacodec_output_ops, \
+}
+
+static struct snd_soc_dai_driver g12a_toacodec_dai_drv[] = {
+ TOACODEC_INPUT("IN A", TOACODEC_IN_A),
+ TOACODEC_INPUT("IN B", TOACODEC_IN_B),
+ TOACODEC_INPUT("IN C", TOACODEC_IN_C),
+ TOACODEC_OUTPUT("OUT", TOACODEC_OUT),
+};
+
+static int g12a_toacodec_component_probe(struct snd_soc_component *c)
+{
+ /* Initialize the static clock parameters */
+ return snd_soc_component_write(c, TOACODEC_CTRL0,
+ CTRL0_BLK_CAP_INV);
+}
+
+static int sm1_toacodec_component_probe(struct snd_soc_component *c)
+{
+ /* Initialize the static clock parameters */
+ return snd_soc_component_write(c, TOACODEC_CTRL0,
+ CTRL0_BLK_CAP_INV_SM1);
+}
+
+static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
+ { "SRC", "I2S A", "IN A Playback" },
+ { "SRC", "I2S B", "IN B Playback" },
+ { "SRC", "I2S C", "IN C Playback" },
+ { "OUT EN", "Switch", "SRC" },
+ { "OUT Capture", NULL, "OUT EN" },
+};
+
+static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
+ SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
+};
+
+static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
+ SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
+};
+
+static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
+ .probe = g12a_toacodec_component_probe,
+ .controls = g12a_toacodec_controls,
+ .num_controls = ARRAY_SIZE(g12a_toacodec_controls),
+ .dapm_widgets = g12a_toacodec_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(g12a_toacodec_widgets),
+ .dapm_routes = g12a_toacodec_routes,
+ .num_dapm_routes = ARRAY_SIZE(g12a_toacodec_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver sm1_toacodec_component_drv = {
+ .probe = sm1_toacodec_component_probe,
+ .controls = sm1_toacodec_controls,
+ .num_controls = ARRAY_SIZE(sm1_toacodec_controls),
+ .dapm_widgets = sm1_toacodec_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sm1_toacodec_widgets),
+ .dapm_routes = g12a_toacodec_routes,
+ .num_dapm_routes = ARRAY_SIZE(g12a_toacodec_routes),
+ .endianness = 1,
+};
+
+static const struct regmap_config g12a_toacodec_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static const struct g12a_toacodec_match_data g12a_toacodec_match_data = {
+ .component_drv = &g12a_toacodec_component_drv,
+ .field_dat_sel = REG_FIELD(TOACODEC_CTRL0, 14, 15),
+ .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 8, 9),
+ .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 5),
+};
+
+static const struct g12a_toacodec_match_data sm1_toacodec_match_data = {
+ .component_drv = &sm1_toacodec_component_drv,
+ .field_dat_sel = REG_FIELD(TOACODEC_CTRL0, 18, 19),
+ .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 12, 14),
+ .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 6),
+};
+
+static const struct of_device_id g12a_toacodec_of_match[] = {
+ {
+ .compatible = "amlogic,g12a-toacodec",
+ .data = &g12a_toacodec_match_data,
+ },
+ {
+ .compatible = "amlogic,sm1-toacodec",
+ .data = &sm1_toacodec_match_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, g12a_toacodec_of_match);
+
+static int g12a_toacodec_probe(struct platform_device *pdev)
+{
+ const struct g12a_toacodec_match_data *data;
+ struct device *dev = &pdev->dev;
+ struct g12a_toacodec *priv;
+ void __iomem *regs;
+ struct regmap *map;
+ int ret;
+
+ data = device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = device_reset(dev);
+ if (ret)
+ return ret;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &g12a_toacodec_regmap_cfg);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ priv->field_dat_sel = devm_regmap_field_alloc(dev, map, data->field_dat_sel);
+ if (IS_ERR(priv->field_dat_sel))
+ return PTR_ERR(priv->field_dat_sel);
+
+ priv->field_lrclk_sel = devm_regmap_field_alloc(dev, map, data->field_lrclk_sel);
+ if (IS_ERR(priv->field_lrclk_sel))
+ return PTR_ERR(priv->field_lrclk_sel);
+
+ priv->field_bclk_sel = devm_regmap_field_alloc(dev, map, data->field_bclk_sel);
+ if (IS_ERR(priv->field_bclk_sel))
+ return PTR_ERR(priv->field_bclk_sel);
+
+ return devm_snd_soc_register_component(dev,
+ data->component_drv, g12a_toacodec_dai_drv,
+ ARRAY_SIZE(g12a_toacodec_dai_drv));
+}
+
+static struct platform_driver g12a_toacodec_pdrv = {
+ .driver = {
+ .name = G12A_TOACODEC_DRV_NAME,
+ .of_match_table = g12a_toacodec_of_match,
+ },
+ .probe = g12a_toacodec_probe,
+};
+module_platform_driver(g12a_toacodec_pdrv);
+
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_DESCRIPTION("Amlogic G12a To Internal DAC Codec Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c
new file mode 100644
index 000000000000..d541ca4acfaf
--- /dev/null
+++ b/sound/soc/meson/g12a-tohdmitx.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2019 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/meson-g12a-tohdmitx.h>
+#include "meson-codec-glue.h"
+
+#define G12A_TOHDMITX_DRV_NAME "g12a-tohdmitx"
+
+#define TOHDMITX_CTRL0 0x0
+#define CTRL0_ENABLE_SHIFT 31
+#define CTRL0_I2S_DAT_SEL_SHIFT 12
+#define CTRL0_I2S_DAT_SEL (0x3 << CTRL0_I2S_DAT_SEL_SHIFT)
+#define CTRL0_I2S_LRCLK_SEL GENMASK(9, 8)
+#define CTRL0_I2S_BLK_CAP_INV BIT(7)
+#define CTRL0_I2S_BCLK_O_INV BIT(6)
+#define CTRL0_I2S_BCLK_SEL GENMASK(5, 4)
+#define CTRL0_SPDIF_CLK_CAP_INV BIT(3)
+#define CTRL0_SPDIF_CLK_O_INV BIT(2)
+#define CTRL0_SPDIF_SEL_SHIFT 1
+#define CTRL0_SPDIF_SEL (0x1 << CTRL0_SPDIF_SEL_SHIFT)
+#define CTRL0_SPDIF_CLK_SEL BIT(0)
+
+static const char * const g12a_tohdmitx_i2s_mux_texts[] = {
+ "I2S A", "I2S B", "I2S C",
+};
+
+static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, changed;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
+ changed = snd_soc_component_test_bits(component, e->reg,
+ CTRL0_I2S_DAT_SEL,
+ FIELD_PREP(CTRL0_I2S_DAT_SEL,
+ mux));
+
+ if (!changed)
+ return 0;
+
+ /* Force disconnect of the mux while updating */
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
+
+ snd_soc_component_update_bits(component, e->reg,
+ CTRL0_I2S_DAT_SEL |
+ CTRL0_I2S_LRCLK_SEL |
+ CTRL0_I2S_BCLK_SEL,
+ FIELD_PREP(CTRL0_I2S_DAT_SEL, mux) |
+ FIELD_PREP(CTRL0_I2S_LRCLK_SEL, mux) |
+ FIELD_PREP(CTRL0_I2S_BCLK_SEL, mux));
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return 1;
+}
+
+static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_i2s_mux_enum, TOHDMITX_CTRL0,
+ CTRL0_I2S_DAT_SEL_SHIFT,
+ g12a_tohdmitx_i2s_mux_texts);
+
+static const struct snd_kcontrol_new g12a_tohdmitx_i2s_mux =
+ SOC_DAPM_ENUM_EXT("I2S Source", g12a_tohdmitx_i2s_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ g12a_tohdmitx_i2s_mux_put_enum);
+
+static const char * const g12a_tohdmitx_spdif_mux_texts[] = {
+ "SPDIF A", "SPDIF B",
+};
+
+static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_to_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, changed;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
+ changed = snd_soc_component_test_bits(component, TOHDMITX_CTRL0,
+ CTRL0_SPDIF_SEL,
+ FIELD_PREP(CTRL0_SPDIF_SEL, mux));
+
+ if (!changed)
+ return 0;
+
+ /* Force disconnect of the mux while updating */
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
+
+ snd_soc_component_update_bits(component, TOHDMITX_CTRL0,
+ CTRL0_SPDIF_SEL |
+ CTRL0_SPDIF_CLK_SEL,
+ FIELD_PREP(CTRL0_SPDIF_SEL, mux) |
+ FIELD_PREP(CTRL0_SPDIF_CLK_SEL, mux));
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return 1;
+}
+
+static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_spdif_mux_enum, TOHDMITX_CTRL0,
+ CTRL0_SPDIF_SEL_SHIFT,
+ g12a_tohdmitx_spdif_mux_texts);
+
+static const struct snd_kcontrol_new g12a_tohdmitx_spdif_mux =
+ SOC_DAPM_ENUM_EXT("SPDIF Source", g12a_tohdmitx_spdif_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ g12a_tohdmitx_spdif_mux_put_enum);
+
+static const struct snd_kcontrol_new g12a_tohdmitx_out_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOHDMITX_CTRL0,
+ CTRL0_ENABLE_SHIFT, 1, 0);
+
+static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = {
+ SND_SOC_DAPM_MUX("I2S SRC", SND_SOC_NOPM, 0, 0,
+ &g12a_tohdmitx_i2s_mux),
+ SND_SOC_DAPM_SWITCH("I2S OUT EN", SND_SOC_NOPM, 0, 0,
+ &g12a_tohdmitx_out_enable),
+ SND_SOC_DAPM_MUX("SPDIF SRC", SND_SOC_NOPM, 0, 0,
+ &g12a_tohdmitx_spdif_mux),
+ SND_SOC_DAPM_SWITCH("SPDIF OUT EN", SND_SOC_NOPM, 0, 0,
+ &g12a_tohdmitx_out_enable),
+};
+
+static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = {
+ .probe = meson_codec_glue_input_dai_probe,
+ .remove = meson_codec_glue_input_dai_remove,
+ .hw_params = meson_codec_glue_input_hw_params,
+ .set_fmt = meson_codec_glue_input_set_fmt,
+};
+
+static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = {
+ .startup = meson_codec_glue_output_startup,
+};
+
+#define TOHDMITX_SPDIF_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+#define TOHDMITX_I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TOHDMITX_STREAM(xname, xsuffix, xfmt, xchmax) \
+{ \
+ .stream_name = xname " " xsuffix, \
+ .channels_min = 1, \
+ .channels_max = (xchmax), \
+ .rate_min = 8000, \
+ .rate_max = 192000, \
+ .formats = (xfmt), \
+}
+
+#define TOHDMITX_IN(xname, xid, xfmt, xchmax) { \
+ .name = xname, \
+ .id = (xid), \
+ .playback = TOHDMITX_STREAM(xname, "Playback", xfmt, xchmax), \
+ .ops = &g12a_tohdmitx_input_ops, \
+}
+
+#define TOHDMITX_OUT(xname, xid, xfmt, xchmax) { \
+ .name = xname, \
+ .id = (xid), \
+ .capture = TOHDMITX_STREAM(xname, "Capture", xfmt, xchmax), \
+ .ops = &g12a_tohdmitx_output_ops, \
+}
+
+static struct snd_soc_dai_driver g12a_tohdmitx_dai_drv[] = {
+ TOHDMITX_IN("I2S IN A", TOHDMITX_I2S_IN_A,
+ TOHDMITX_I2S_FORMATS, 8),
+ TOHDMITX_IN("I2S IN B", TOHDMITX_I2S_IN_B,
+ TOHDMITX_I2S_FORMATS, 8),
+ TOHDMITX_IN("I2S IN C", TOHDMITX_I2S_IN_C,
+ TOHDMITX_I2S_FORMATS, 8),
+ TOHDMITX_OUT("I2S OUT", TOHDMITX_I2S_OUT,
+ TOHDMITX_I2S_FORMATS, 8),
+ TOHDMITX_IN("SPDIF IN A", TOHDMITX_SPDIF_IN_A,
+ TOHDMITX_SPDIF_FORMATS, 2),
+ TOHDMITX_IN("SPDIF IN B", TOHDMITX_SPDIF_IN_B,
+ TOHDMITX_SPDIF_FORMATS, 2),
+ TOHDMITX_OUT("SPDIF OUT", TOHDMITX_SPDIF_OUT,
+ TOHDMITX_SPDIF_FORMATS, 2),
+};
+
+static int g12a_tohdmi_component_probe(struct snd_soc_component *c)
+{
+ /* Initialize the static clock parameters */
+ return snd_soc_component_write(c, TOHDMITX_CTRL0,
+ CTRL0_I2S_BLK_CAP_INV | CTRL0_SPDIF_CLK_CAP_INV);
+}
+
+static const struct snd_soc_dapm_route g12a_tohdmitx_routes[] = {
+ { "I2S SRC", "I2S A", "I2S IN A Playback" },
+ { "I2S SRC", "I2S B", "I2S IN B Playback" },
+ { "I2S SRC", "I2S C", "I2S IN C Playback" },
+ { "I2S OUT EN", "Switch", "I2S SRC" },
+ { "I2S OUT Capture", NULL, "I2S OUT EN" },
+ { "SPDIF SRC", "SPDIF A", "SPDIF IN A Playback" },
+ { "SPDIF SRC", "SPDIF B", "SPDIF IN B Playback" },
+ { "SPDIF OUT EN", "Switch", "SPDIF SRC" },
+ { "SPDIF OUT Capture", NULL, "SPDIF OUT EN" },
+};
+
+static const struct snd_soc_component_driver g12a_tohdmitx_component_drv = {
+ .probe = g12a_tohdmi_component_probe,
+ .dapm_widgets = g12a_tohdmitx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(g12a_tohdmitx_widgets),
+ .dapm_routes = g12a_tohdmitx_routes,
+ .num_dapm_routes = ARRAY_SIZE(g12a_tohdmitx_routes),
+ .endianness = 1,
+};
+
+static const struct regmap_config g12a_tohdmitx_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static const struct of_device_id g12a_tohdmitx_of_match[] = {
+ { .compatible = "amlogic,g12a-tohdmitx", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, g12a_tohdmitx_of_match);
+
+static int g12a_tohdmitx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *regs;
+ struct regmap *map;
+ int ret;
+
+ ret = device_reset(dev);
+ if (ret)
+ return ret;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &g12a_tohdmitx_regmap_cfg);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ return devm_snd_soc_register_component(dev,
+ &g12a_tohdmitx_component_drv, g12a_tohdmitx_dai_drv,
+ ARRAY_SIZE(g12a_tohdmitx_dai_drv));
+}
+
+static struct platform_driver g12a_tohdmitx_pdrv = {
+ .driver = {
+ .name = G12A_TOHDMITX_DRV_NAME,
+ .of_match_table = g12a_tohdmitx_of_match,
+ },
+ .probe = g12a_tohdmitx_probe,
+};
+module_platform_driver(g12a_tohdmitx_pdrv);
+
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_DESCRIPTION("Amlogic G12a To HDMI Tx Control Codec Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c
new file mode 100644
index 000000000000..b408cc2bbc91
--- /dev/null
+++ b/sound/soc/meson/gx-card.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "meson-card.h"
+
+struct gx_dai_link_i2s_data {
+ unsigned int mclk_fs;
+};
+
+/*
+ * Base params for the codec to codec links
+ * Those will be over-written by the CPU side of the link
+ */
+static const struct snd_soc_pcm_stream codec_params = {
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .rate_min = 5525,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+};
+
+static int gx_card_i2s_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct gx_dai_link_i2s_data *be =
+ (struct gx_dai_link_i2s_data *)priv->link_data[rtd->id];
+
+ return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs);
+}
+
+static const struct snd_soc_ops gx_card_i2s_be_ops = {
+ .hw_params = gx_card_i2s_be_hw_params,
+};
+
+static int gx_card_parse_i2s(struct snd_soc_card *card,
+ struct device_node *node,
+ int *index)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *link = &card->dai_link[*index];
+ struct gx_dai_link_i2s_data *be;
+
+ /* Allocate i2s link parameters */
+ be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
+ if (!be)
+ return -ENOMEM;
+ priv->link_data[*index] = be;
+
+ /* Setup i2s link */
+ link->ops = &gx_card_i2s_be_ops;
+ link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node);
+
+ of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
+
+ return 0;
+}
+
+static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c,
+ char *match)
+{
+ if (of_device_is_compatible(c->of_node, DT_PREFIX "aiu")) {
+ if (strstr(c->dai_name, match))
+ return 1;
+ }
+
+ /* dai not matched */
+ return 0;
+}
+
+static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
+ int *index)
+{
+ struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
+ struct snd_soc_dai_link_component *cpu;
+ int ret;
+
+ cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
+ if (!cpu)
+ return -ENOMEM;
+
+ dai_link->cpus = cpu;
+ dai_link->num_cpus = 1;
+
+ ret = meson_card_parse_dai(card, np, dai_link->cpus);
+ if (ret)
+ return ret;
+
+ if (gx_card_cpu_identify(dai_link->cpus, "FIFO"))
+ return meson_card_set_fe_link(card, dai_link, np, true);
+
+ ret = meson_card_set_be_link(card, dai_link, np);
+ if (ret)
+ return ret;
+
+ /* Or apply codec to codec params if necessary */
+ if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) {
+ dai_link->c2c_params = &codec_params;
+ dai_link->num_c2c_params = 1;
+ } else {
+ dai_link->no_pcm = 1;
+ /* Check if the cpu is the i2s encoder and parse i2s data */
+ if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder"))
+ ret = gx_card_parse_i2s(card, np, index);
+ }
+
+ return ret;
+}
+
+static const struct meson_card_match_data gx_card_match_data = {
+ .add_link = gx_card_add_link,
+};
+
+static const struct of_device_id gx_card_of_match[] = {
+ {
+ .compatible = "amlogic,gx-sound-card",
+ .data = &gx_card_match_data,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, gx_card_of_match);
+
+static struct platform_driver gx_card_pdrv = {
+ .probe = meson_card_probe,
+ .remove = meson_card_remove,
+ .driver = {
+ .name = "gx-sound-card",
+ .of_match_table = gx_card_of_match,
+ },
+};
+module_platform_driver(gx_card_pdrv);
+
+MODULE_DESCRIPTION("Amlogic GX ALSA machine driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
new file mode 100644
index 000000000000..cdb759b466ad
--- /dev/null
+++ b/sound/soc/meson/meson-card-utils.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+#include "meson-card.h"
+
+int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ unsigned int mclk_fs)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ unsigned int mclk;
+ int ret, i;
+
+ if (!mclk_fs)
+ return 0;
+
+ mclk = params_rate(params) * mclk_fs;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk);
+
+int meson_card_reallocate_links(struct snd_soc_card *card,
+ unsigned int num_links)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *links;
+ void **ldata;
+
+ links = krealloc(priv->card.dai_link,
+ num_links * sizeof(*priv->card.dai_link),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!links)
+ goto err_links;
+
+ ldata = krealloc(priv->link_data,
+ num_links * sizeof(*priv->link_data),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!ldata)
+ goto err_ldata;
+
+ priv->card.dai_link = links;
+ priv->link_data = ldata;
+ priv->card.num_links = num_links;
+ return 0;
+
+err_ldata:
+ kfree(links);
+err_links:
+ dev_err(priv->card.dev, "failed to allocate links\n");
+ return -ENOMEM;
+
+}
+EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
+
+int meson_card_parse_dai(struct snd_soc_card *card,
+ struct device_node *node,
+ struct snd_soc_dai_link_component *dlc)
+{
+ int ret;
+
+ if (!dlc || !node)
+ return -EINVAL;
+
+ ret = snd_soc_of_get_dlc(node, NULL, dlc, 0);
+ if (ret)
+ return dev_err_probe(card->dev, ret, "can't parse dai\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(meson_card_parse_dai);
+
+static int meson_card_set_link_name(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ const char *prefix)
+{
+ char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
+ prefix, node->full_name);
+ if (!name)
+ return -ENOMEM;
+
+ link->name = name;
+ link->stream_name = name;
+
+ return 0;
+}
+
+unsigned int meson_card_parse_daifmt(struct device_node *node,
+ struct device_node *cpu_node)
+{
+ struct device_node *bitclkmaster = NULL;
+ struct device_node *framemaster = NULL;
+ unsigned int daifmt;
+
+ daifmt = snd_soc_daifmt_parse_format(node, NULL);
+
+ snd_soc_daifmt_parse_clock_provider_as_phandle(node, NULL, &bitclkmaster, &framemaster);
+
+ /* If no master is provided, default to cpu master */
+ if (!bitclkmaster || bitclkmaster == cpu_node) {
+ daifmt |= (!framemaster || framemaster == cpu_node) ?
+ SND_SOC_DAIFMT_CBC_CFC : SND_SOC_DAIFMT_CBC_CFP;
+ } else {
+ daifmt |= (!framemaster || framemaster == cpu_node) ?
+ SND_SOC_DAIFMT_CBP_CFC : SND_SOC_DAIFMT_CBP_CFP;
+ }
+
+ of_node_put(bitclkmaster);
+ of_node_put(framemaster);
+
+ return daifmt;
+}
+EXPORT_SYMBOL_GPL(meson_card_parse_daifmt);
+
+int meson_card_set_be_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node)
+{
+ struct snd_soc_dai_link_component *codec;
+ int ret, num_codecs;
+
+ num_codecs = of_get_child_count(node);
+ if (!num_codecs) {
+ dev_err(card->dev, "be link %s has no codec\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ link->codecs = codec;
+ link->num_codecs = num_codecs;
+
+ for_each_child_of_node_scoped(node, np) {
+ ret = meson_card_parse_dai(card, np, codec);
+ if (ret)
+ return ret;
+
+ codec++;
+ }
+
+ ret = meson_card_set_link_name(card, link, node, "be");
+ if (ret)
+ dev_err(card->dev, "error setting %pOFn link name\n", node);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(meson_card_set_be_link);
+
+int meson_card_set_fe_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ bool is_playback)
+{
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+
+ link->dynamic = 1;
+ link->dpcm_merged_format = 1;
+ link->dpcm_merged_chan = 1;
+ link->dpcm_merged_rate = 1;
+
+ if (is_playback)
+ link->playback_only = 1;
+ else
+ link->capture_only = 1;
+
+ return meson_card_set_link_name(card, link, node, "fe");
+}
+EXPORT_SYMBOL_GPL(meson_card_set_fe_link);
+
+static int meson_card_add_links(struct snd_soc_card *card)
+{
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
+ struct device_node *node = card->dev->of_node;
+ int num, i, ret;
+
+ num = of_get_child_count(node);
+ if (!num) {
+ dev_err(card->dev, "card has no links\n");
+ return -EINVAL;
+ }
+
+ ret = meson_card_reallocate_links(card, num);
+ if (ret)
+ return ret;
+
+ i = 0;
+ for_each_child_of_node_scoped(node, np) {
+ ret = priv->match_data->add_link(card, np, &i);
+ if (ret)
+ return ret;
+
+ i++;
+ }
+
+ return 0;
+}
+
+static int meson_card_parse_of_optional(struct snd_soc_card *card,
+ const char *propname,
+ int (*func)(struct snd_soc_card *c,
+ const char *p))
+{
+ /* If property is not provided, don't fail ... */
+ if (!of_property_present(card->dev->of_node, propname))
+ return 0;
+
+ /* ... but do fail if it is provided and the parsing fails */
+ return func(card, propname);
+}
+
+static void meson_card_clean_references(struct meson_card *priv)
+{
+ struct snd_soc_card *card = &priv->card;
+ struct snd_soc_dai_link *link;
+ struct snd_soc_dai_link_component *codec;
+ struct snd_soc_aux_dev *aux;
+ int i, j;
+
+ if (card->dai_link) {
+ for_each_card_prelinks(card, i, link) {
+ if (link->cpus)
+ of_node_put(link->cpus->of_node);
+ for_each_link_codecs(link, j, codec)
+ of_node_put(codec->of_node);
+ }
+ }
+
+ if (card->aux_dev) {
+ for_each_card_pre_auxs(card, i, aux)
+ of_node_put(aux->dlc.of_node);
+ }
+
+ kfree(card->dai_link);
+ kfree(priv->link_data);
+}
+
+int meson_card_probe(struct platform_device *pdev)
+{
+ const struct meson_card_match_data *data;
+ struct device *dev = &pdev->dev;
+ struct meson_card *priv;
+ int ret;
+
+ data = of_device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ snd_soc_card_set_drvdata(&priv->card, priv);
+
+ priv->card.owner = THIS_MODULE;
+ priv->card.dev = dev;
+ priv->card.driver_name = dev->driver->name;
+ priv->match_data = data;
+
+ ret = snd_soc_of_parse_card_name(&priv->card, "model");
+ if (ret < 0)
+ return ret;
+
+ ret = meson_card_parse_of_optional(&priv->card, "audio-routing",
+ snd_soc_of_parse_audio_routing);
+ if (ret) {
+ dev_err(dev, "error while parsing routing\n");
+ return ret;
+ }
+
+ ret = meson_card_parse_of_optional(&priv->card, "audio-widgets",
+ snd_soc_of_parse_audio_simple_widgets);
+ if (ret) {
+ dev_err(dev, "error while parsing widgets\n");
+ return ret;
+ }
+
+ ret = meson_card_add_links(&priv->card);
+ if (ret)
+ goto out_err;
+
+ ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs");
+ if (ret)
+ goto out_err;
+
+ ret = devm_snd_soc_register_card(dev, &priv->card);
+ if (ret)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ meson_card_clean_references(priv);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(meson_card_probe);
+
+void meson_card_remove(struct platform_device *pdev)
+{
+ struct meson_card *priv = platform_get_drvdata(pdev);
+
+ meson_card_clean_references(priv);
+}
+EXPORT_SYMBOL_GPL(meson_card_remove);
+
+MODULE_DESCRIPTION("Amlogic Sound Card Utils");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h
new file mode 100644
index 000000000000..a0d693e4f460
--- /dev/null
+++ b/sound/soc/meson/meson-card.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_SND_CARD_H
+#define _MESON_SND_CARD_H
+
+struct device_node;
+struct platform_device;
+
+struct snd_soc_card;
+struct snd_pcm_substream;
+struct snd_pcm_hw_params;
+
+#define DT_PREFIX "amlogic,"
+
+struct meson_card_match_data {
+ int (*add_link)(struct snd_soc_card *card,
+ struct device_node *node,
+ int *index);
+};
+
+struct meson_card {
+ const struct meson_card_match_data *match_data;
+ struct snd_soc_card card;
+ void **link_data;
+};
+
+unsigned int meson_card_parse_daifmt(struct device_node *node,
+ struct device_node *cpu_node);
+
+int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ unsigned int mclk_fs);
+
+int meson_card_reallocate_links(struct snd_soc_card *card,
+ unsigned int num_links);
+int meson_card_parse_dai(struct snd_soc_card *card,
+ struct device_node *node,
+ struct snd_soc_dai_link_component *dlc);
+int meson_card_set_be_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node);
+int meson_card_set_fe_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ bool is_playback);
+
+int meson_card_probe(struct platform_device *pdev);
+void meson_card_remove(struct platform_device *pdev);
+
+#endif /* _MESON_SND_CARD_H */
diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c
new file mode 100644
index 000000000000..f8c5643f3cfe
--- /dev/null
+++ b/sound/soc/meson/meson-codec-glue.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2019 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "meson-codec-glue.h"
+
+static struct snd_soc_dapm_widget *
+meson_codec_glue_get_input(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *in;
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ /* Check that we still are in the same component */
+ if (snd_soc_dapm_to_component(w->dapm) !=
+ snd_soc_dapm_to_component(p->source->dapm))
+ continue;
+
+ if (p->source->id == snd_soc_dapm_dai_in)
+ return p->source;
+
+ in = meson_codec_glue_get_input(p->source);
+ if (in)
+ return in;
+ }
+
+ return NULL;
+}
+
+static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai,
+ struct meson_codec_glue_input *data)
+{
+ snd_soc_dai_dma_data_set_playback(dai, data);
+}
+
+struct meson_codec_glue_input *
+meson_codec_glue_input_get_data(struct snd_soc_dai *dai)
+{
+ return snd_soc_dai_dma_data_get_playback(dai);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data);
+
+static struct meson_codec_glue_input *
+meson_codec_glue_output_get_input_data(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget *in =
+ meson_codec_glue_get_input(w);
+ struct snd_soc_dai *dai;
+
+ if (WARN_ON(!in))
+ return NULL;
+
+ dai = in->priv;
+
+ return meson_codec_glue_input_get_data(dai);
+}
+
+int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct meson_codec_glue_input *data =
+ meson_codec_glue_input_get_data(dai);
+
+ data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params));
+ data->params.rate_min = params_rate(params);
+ data->params.rate_max = params_rate(params);
+ data->params.formats = 1ULL << (__force int) params_format(params);
+ data->params.channels_min = params_channels(params);
+ data->params.channels_max = params_channels(params);
+ data->params.sig_bits = dai->driver->playback.sig_bits;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_input_hw_params);
+
+int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct meson_codec_glue_input *data =
+ meson_codec_glue_input_get_data(dai);
+
+ /* Save the source stream format for the downstream link */
+ data->fmt = fmt;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_input_set_fmt);
+
+int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget_capture(dai);
+ struct meson_codec_glue_input *in_data = meson_codec_glue_output_get_input_data(w);
+
+ if (!in_data)
+ return -ENODEV;
+
+ if (WARN_ON(!rtd->dai_link->c2c_params)) {
+ dev_warn(dai->dev, "codec2codec link expected\n");
+ return -EINVAL;
+ }
+
+ /* Replace link params with the input params */
+ rtd->dai_link->c2c_params = &in_data->params;
+ rtd->dai_link->num_c2c_params = 1;
+
+ return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup);
+
+int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai)
+{
+ struct meson_codec_glue_input *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ meson_codec_glue_input_set_data(dai, data);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_probe);
+
+int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai)
+{
+ struct meson_codec_glue_input *data =
+ meson_codec_glue_input_get_data(dai);
+
+ kfree(data);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_remove);
+
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_DESCRIPTION("Amlogic Codec Glue Helpers");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/meson/meson-codec-glue.h b/sound/soc/meson/meson-codec-glue.h
new file mode 100644
index 000000000000..07f99446c0c6
--- /dev/null
+++ b/sound/soc/meson/meson-codec-glue.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2018 Baylibre SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_CODEC_GLUE_H
+#define _MESON_CODEC_GLUE_H
+
+#include <sound/soc.h>
+
+struct meson_codec_glue_input {
+ struct snd_soc_pcm_stream params;
+ unsigned int fmt;
+};
+
+/* Input helpers */
+struct meson_codec_glue_input *
+meson_codec_glue_input_get_data(struct snd_soc_dai *dai);
+int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt);
+int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai);
+int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai);
+
+/* Output helpers */
+int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+
+#endif /* _MESON_CODEC_GLUE_H */
diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
new file mode 100644
index 000000000000..da1a93946d67
--- /dev/null
+++ b/sound/soc/meson/t9015.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define BLOCK_EN 0x00
+#define LORN_EN 0
+#define LORP_EN 1
+#define LOLN_EN 2
+#define LOLP_EN 3
+#define DACR_EN 4
+#define DACL_EN 5
+#define DACR_INV 20
+#define DACL_INV 21
+#define DACR_SRC 22
+#define DACL_SRC 23
+#define REFP_BUF_EN BIT(12)
+#define BIAS_CURRENT_EN BIT(13)
+#define VMID_GEN_FAST BIT(14)
+#define VMID_GEN_EN BIT(15)
+#define I2S_MODE BIT(30)
+#define VOL_CTRL0 0x04
+#define GAIN_H 31
+#define GAIN_L 23
+#define VOL_CTRL1 0x08
+#define DAC_MONO 8
+#define RAMP_RATE 10
+#define VC_RAMP_MODE 12
+#define MUTE_MODE 13
+#define UNMUTE_MODE 14
+#define DAC_SOFT_MUTE 15
+#define DACR_VC 16
+#define DACL_VC 24
+#define LINEOUT_CFG 0x0c
+#define LORN_POL 0
+#define LORP_POL 4
+#define LOLN_POL 8
+#define LOLP_POL 12
+#define POWER_CFG 0x10
+
+struct t9015 {
+ struct regulator *avdd;
+};
+
+static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ val = I2S_MODE;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ val = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, BLOCK_EN, I2S_MODE, val);
+
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) &&
+ ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_LEFT_J))
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops t9015_dai_ops = {
+ .set_fmt = t9015_dai_set_fmt,
+};
+
+static struct snd_soc_dai_driver t9015_dai = {
+ .name = "t9015-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &t9015_dai_ops,
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
+
+static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
+static SOC_ENUM_SINGLE_DECL(ramp_rate_enum, VOL_CTRL1, RAMP_RATE,
+ ramp_rate_txt);
+
+static const char * const dacr_in_txt[] = { "Right", "Left" };
+static SOC_ENUM_SINGLE_DECL(dacr_in_enum, BLOCK_EN, DACR_SRC, dacr_in_txt);
+
+static const char * const dacl_in_txt[] = { "Left", "Right" };
+static SOC_ENUM_SINGLE_DECL(dacl_in_enum, BLOCK_EN, DACL_SRC, dacl_in_txt);
+
+static const char * const mono_txt[] = { "Stereo", "Mono"};
+static SOC_ENUM_SINGLE_DECL(mono_enum, VOL_CTRL1, DAC_MONO, mono_txt);
+
+static const struct snd_kcontrol_new t9015_snd_controls[] = {
+ /* Volume Controls */
+ SOC_ENUM("Playback Channel Mode", mono_enum),
+ SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
+ SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
+ 0xff, 0, dac_vol_tlv),
+
+ /* Ramp Controls */
+ SOC_ENUM("Ramp Rate", ramp_rate_enum),
+ SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
+ SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
+ SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
+};
+
+static const struct snd_kcontrol_new t9015_right_dac_mux =
+ SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum);
+static const struct snd_kcontrol_new t9015_left_dac_mux =
+ SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum);
+
+static const struct snd_soc_dapm_widget t9015_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
+ &t9015_right_dac_mux),
+ SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
+ &t9015_left_dac_mux),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
+ SND_SOC_DAPM_OUT_DRV("Right- Driver", BLOCK_EN, LORN_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Left- Driver", BLOCK_EN, LOLN_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LORN"),
+ SND_SOC_DAPM_OUTPUT("LORP"),
+ SND_SOC_DAPM_OUTPUT("LOLN"),
+ SND_SOC_DAPM_OUTPUT("LOLP"),
+};
+
+static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
+ { "Right IN", NULL, "Playback" },
+ { "Left IN", NULL, "Playback" },
+ { "Right DAC Sel", "Right", "Right IN" },
+ { "Right DAC Sel", "Left", "Left IN" },
+ { "Left DAC Sel", "Right", "Right IN" },
+ { "Left DAC Sel", "Left", "Left IN" },
+ { "Right DAC", NULL, "Right DAC Sel" },
+ { "Left DAC", NULL, "Left DAC Sel" },
+ { "Right- Driver", NULL, "Right DAC" },
+ { "Right+ Driver", NULL, "Right DAC" },
+ { "Left- Driver", NULL, "Left DAC" },
+ { "Left+ Driver", NULL, "Left DAC" },
+ { "LORN", NULL, "Right- Driver", },
+ { "LORP", NULL, "Right+ Driver", },
+ { "LOLN", NULL, "Left- Driver", },
+ { "LOLP", NULL, "Left+ Driver", },
+};
+
+static int t9015_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct t9015 *priv = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ enum snd_soc_bias_level now = snd_soc_dapm_get_bias_level(dapm);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_component_update_bits(component, BLOCK_EN,
+ BIAS_CURRENT_EN,
+ BIAS_CURRENT_EN);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, BLOCK_EN,
+ BIAS_CURRENT_EN,
+ 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ ret = regulator_enable(priv->avdd);
+ if (ret) {
+ dev_err(component->dev, "AVDD enable failed\n");
+ return ret;
+ }
+
+ if (now == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, BLOCK_EN,
+ VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN,
+ VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN);
+
+ mdelay(200);
+ snd_soc_component_update_bits(component, BLOCK_EN,
+ VMID_GEN_FAST,
+ 0);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, BLOCK_EN,
+ VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN,
+ 0);
+
+ regulator_disable(priv->avdd);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver t9015_codec_driver = {
+ .set_bias_level = t9015_set_bias_level,
+ .controls = t9015_snd_controls,
+ .num_controls = ARRAY_SIZE(t9015_snd_controls),
+ .dapm_widgets = t9015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(t9015_dapm_widgets),
+ .dapm_routes = t9015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(t9015_dapm_routes),
+ .suspend_bias_off = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config t9015_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = POWER_CFG,
+};
+
+static int t9015_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct t9015 *priv;
+ void __iomem *regs;
+ struct regmap *regmap;
+ struct clk *pclk;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ pclk = devm_clk_get_enabled(dev, "pclk");
+ if (IS_ERR(pclk))
+ return dev_err_probe(dev, PTR_ERR(pclk), "failed to get core clock\n");
+
+ priv->avdd = devm_regulator_get(dev, "AVDD");
+ if (IS_ERR(priv->avdd))
+ return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n");
+
+ ret = device_reset(dev);
+ if (ret) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs)) {
+ dev_err(dev, "register map failed\n");
+ return PTR_ERR(regs);
+ }
+
+ regmap = devm_regmap_init_mmio(dev, regs, &t9015_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(regmap);
+ }
+
+ /*
+ * Initialize output polarity:
+ * ATM the output polarity is fixed but in the future it might useful
+ * to add DT property to set this depending on the platform needs
+ */
+ regmap_write(regmap, LINEOUT_CFG, 0x1111);
+
+ return devm_snd_soc_register_component(dev, &t9015_codec_driver,
+ &t9015_dai, 1);
+}
+
+static const struct of_device_id t9015_ids[] __maybe_unused = {
+ { .compatible = "amlogic,t9015", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, t9015_ids);
+
+static struct platform_driver t9015_driver = {
+ .driver = {
+ .name = "t9015-codec",
+ .of_match_table = of_match_ptr(t9015_ids),
+ },
+ .probe = t9015_probe,
+};
+
+module_platform_driver(t9015_driver);
+
+MODULE_DESCRIPTION("ASoC Amlogic T9015 codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
index 219235c02212..2fb1ca711f71 100644
--- a/sound/soc/mxs/Kconfig
+++ b/sound/soc/mxs/Kconfig
@@ -1,5 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_MXS_SOC
- tristate "SoC Audio for Freescale MXS CPUs"
+ tristate "Freescale MXS"
depends on ARCH_MXS || COMPILE_TEST
depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/mxs/Makefile b/sound/soc/mxs/Makefile
index 565b5b51e8b7..474bdd75513f 100644
--- a/sound/soc/mxs/Makefile
+++ b/sound/soc/mxs/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
# MXS Platform Support
-snd-soc-mxs-objs := mxs-saif.o
-snd-soc-mxs-pcm-objs := mxs-pcm.o
+snd-soc-mxs-y := mxs-saif.o
+snd-soc-mxs-pcm-y := mxs-pcm.o
obj-$(CONFIG_SND_MXS_SOC) += snd-soc-mxs.o snd-soc-mxs-pcm.o
# i.MX Machine Support
-snd-soc-mxs-sgtl5000-objs := mxs-sgtl5000.o
+snd-soc-mxs-sgtl5000-y := mxs-sgtl5000.o
obj-$(CONFIG_SND_SOC_MXS_SGTL5000) += snd-soc-mxs-sgtl5000.o
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index a371b4f91c53..9bb08cadeb18 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
*
* Based on sound/soc/imx/imx-pcm-dma-mx2.c
- *
- * 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/device.h>
@@ -56,4 +43,5 @@ int mxs_pcm_platform_register(struct device *dev)
}
EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
+MODULE_DESCRIPTION("MXS ASoC PCM driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h
index 035ea0436ca5..4f9c419f3520 100644
--- a/sound/soc/mxs/mxs-pcm.h
+++ b/sound/soc/mxs/mxs-pcm.h
@@ -1,19 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 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 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 _MXS_PCM_H
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index c866ade28ad0..a01a680ad4d7 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -1,31 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 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 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/init.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
+#include <linux/io.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -37,9 +24,80 @@
#define MXS_SET_ADDR 0x4
#define MXS_CLR_ADDR 0x8
+#define MXS_SAIF_BUSY_TIMEOUT_US 10000
+
static struct mxs_saif *mxs_saif[2];
/*
+ * Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK
+ * is provided by other SAIF, we provide a interface here to get its master
+ * from its master_id.
+ * Note that the master could be itself.
+ */
+static inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif *saif)
+{
+ return mxs_saif[saif->master_id];
+}
+
+static int __mxs_saif_put_mclk(struct mxs_saif *saif)
+{
+ u32 stat;
+ int ret;
+
+ ret = readx_poll_timeout(__raw_readl, saif->base + SAIF_STAT, stat,
+ (stat & BM_SAIF_STAT_BUSY) == 0,
+ MXS_SAIF_BUSY_TIMEOUT_US,
+ USEC_PER_SEC);
+ if (ret) {
+ dev_err(saif->dev, "error: busy\n");
+ return -EBUSY;
+ }
+
+ /* disable MCLK output */
+ __raw_writel(BM_SAIF_CTRL_CLKGATE,
+ saif->base + SAIF_CTRL + MXS_SET_ADDR);
+ __raw_writel(BM_SAIF_CTRL_RUN,
+ saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+ saif->mclk_in_use = 0;
+
+ return 0;
+}
+
+static int __mxs_saif_get_mclk(struct mxs_saif *saif)
+{
+ u32 stat;
+ struct mxs_saif *master_saif;
+
+ if (!saif)
+ return -EINVAL;
+
+ /* Clear Reset */
+ __raw_writel(BM_SAIF_CTRL_SFTRST,
+ saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+ /* FIXME: need clear clk gate for register r/w */
+ __raw_writel(BM_SAIF_CTRL_CLKGATE,
+ saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+ master_saif = mxs_saif_get_master(saif);
+ if (saif != master_saif) {
+ dev_err(saif->dev, "can not get mclk from a non-master saif\n");
+ return -EINVAL;
+ }
+
+ stat = __raw_readl(saif->base + SAIF_STAT);
+ if (stat & BM_SAIF_STAT_BUSY) {
+ dev_err(saif->dev, "error: busy\n");
+ return -EBUSY;
+ }
+
+ saif->mclk_in_use = 1;
+
+ return 0;
+}
+
+/*
* SAIF is a little different with other normal SOC DAIs on clock using.
*
* For MXS, two SAIF modules are instantiated on-chip.
@@ -61,6 +119,7 @@ static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
switch (clk_id) {
case MXS_SAIF_MCLK:
@@ -69,18 +128,22 @@ static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
default:
return -EINVAL;
}
- return 0;
-}
-/*
- * Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK
- * is provided by other SAIF, we provide a interface here to get its master
- * from its master_id.
- * Note that the master could be itself.
- */
-static inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif * saif)
-{
- return mxs_saif[saif->master_id];
+ if (!saif->mclk_in_use && freq) {
+ ret = __mxs_saif_get_mclk(saif);
+ if (ret)
+ return ret;
+
+ /* enable MCLK output */
+ __raw_writel(BM_SAIF_CTRL_RUN,
+ saif->base + SAIF_CTRL + MXS_SET_ADDR);
+ } else if (saif->mclk_in_use && freq == 0) {
+ ret = __mxs_saif_put_mclk(saif);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
/*
@@ -119,23 +182,35 @@ static int mxs_saif_set_clk(struct mxs_saif *saif,
* Set SAIF clock
*
* The SAIF clock should be either 384*fs or 512*fs.
- * If MCLK is used, the SAIF clk ratio need to match mclk ratio.
- * For 32x mclk, set saif clk as 512*fs.
- * For 48x mclk, set saif clk as 384*fs.
+ * If MCLK is used, the SAIF clk ratio needs to match mclk ratio.
+ * For 256x, 128x, 64x, and 32x sub-rates, set saif clk as 512*fs.
+ * For 192x, 96x, and 48x sub-rates, set saif clk as 384*fs.
*
* If MCLK is not used, we just set saif clk to 512*fs.
*/
- clk_prepare_enable(master_saif->clk);
+ ret = clk_prepare_enable(master_saif->clk);
+ if (ret)
+ return ret;
if (master_saif->mclk_in_use) {
- if (mclk % 32 == 0) {
+ switch (mclk / rate) {
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
ret = clk_set_rate(master_saif->clk, 512 * rate);
- } else if (mclk % 48 == 0) {
+ break;
+ case 48:
+ case 96:
+ case 192:
+ case 384:
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
ret = clk_set_rate(master_saif->clk, 384 * rate);
- } else {
- /* SAIF MCLK should be either 32x or 48x */
+ break;
+ default:
+ /* SAIF MCLK should be a sub-rate of 512x or 384x */
clk_disable_unprepare(master_saif->clk);
return -EINVAL;
}
@@ -239,34 +314,15 @@ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
unsigned int rate)
{
struct mxs_saif *saif = mxs_saif[saif_id];
- u32 stat;
int ret;
- struct mxs_saif *master_saif;
if (!saif)
return -EINVAL;
- /* Clear Reset */
- __raw_writel(BM_SAIF_CTRL_SFTRST,
- saif->base + SAIF_CTRL + MXS_CLR_ADDR);
-
- /* FIXME: need clear clk gate for register r/w */
- __raw_writel(BM_SAIF_CTRL_CLKGATE,
- saif->base + SAIF_CTRL + MXS_CLR_ADDR);
-
- master_saif = mxs_saif_get_master(saif);
- if (saif != master_saif) {
- dev_err(saif->dev, "can not get mclk from a non-master saif\n");
- return -EINVAL;
- }
-
- stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_BUSY) {
- dev_err(saif->dev, "error: busy\n");
- return -EBUSY;
- }
+ ret = __mxs_saif_get_mclk(saif);
+ if (ret)
+ return ret;
- saif->mclk_in_use = 1;
ret = mxs_saif_set_clk(saif, mclk, rate);
if (ret)
return ret;
@@ -299,6 +355,16 @@ static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
return -EBUSY;
}
+ /* If SAIF1 is configured as slave, the clk gate needs to be cleared
+ * before the register can be written.
+ */
+ if (saif->id != saif->master_id) {
+ __raw_writel(BM_SAIF_CTRL_SFTRST,
+ saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+ __raw_writel(BM_SAIF_CTRL_CLKGATE,
+ saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+ }
+
scr0 = __raw_readl(saif->base + SAIF_CTRL);
scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \
& ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY;
@@ -348,8 +414,8 @@ static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
* Saif internally could be slave when working on EXTMASTER mode.
* We just hide this to machine driver.
*/
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
if (saif->id == saif->master_id)
scr &= ~BM_SAIF_CTRL_SLAVE_MODE;
else
@@ -368,6 +434,7 @@ static int mxs_saif_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
/* clear error status to 0 for each re-open */
saif->fifo_underrun = 0;
@@ -381,9 +448,21 @@ static int mxs_saif_startup(struct snd_pcm_substream *substream,
__raw_writel(BM_SAIF_CTRL_CLKGATE,
saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+ ret = clk_prepare(saif->clk);
+ if (ret)
+ return ret;
+
return 0;
}
+static void mxs_saif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+
+ clk_unprepare(saif->clk);
+}
+
/*
* Should only be called when port is inactive.
* although can be called multiple times by upper layers.
@@ -408,7 +487,7 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
}
stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_BUSY) {
+ if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) {
dev_err(cpu_dai->dev, "error: busy\n");
return -EBUSY;
}
@@ -424,8 +503,6 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- /* prepare clk in hw_param, enable in trigger */
- clk_prepare(saif->clk);
if (saif != master_saif) {
/*
* Set an initial clock rate for the saif internal logic to work
@@ -434,13 +511,18 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
* basic clock which should be fast enough for the internal
* logic.
*/
- clk_enable(saif->clk);
+ ret = clk_enable(saif->clk);
+ if (ret)
+ return ret;
+
ret = clk_set_rate(saif->clk, 24000000);
clk_disable(saif->clk);
if (ret)
return ret;
- clk_prepare(master_saif->clk);
+ ret = clk_prepare(master_saif->clk);
+ if (ret)
+ return ret;
}
scr = __raw_readl(saif->base + SAIF_CTRL);
@@ -611,6 +693,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
static const struct snd_soc_dai_ops mxs_saif_dai_ops = {
.startup = mxs_saif_startup,
+ .shutdown = mxs_saif_shutdown,
.trigger = mxs_saif_trigger,
.prepare = mxs_saif_prepare,
.hw_params = mxs_saif_hw_params,
@@ -618,18 +701,8 @@ static const struct snd_soc_dai_ops mxs_saif_dai_ops = {
.set_fmt = mxs_saif_set_dai_fmt,
};
-static int mxs_saif_dai_probe(struct snd_soc_dai *dai)
-{
- struct mxs_saif *saif = dev_get_drvdata(dai->dev);
-
- snd_soc_dai_set_drvdata(dai, saif);
-
- return 0;
-}
-
static struct snd_soc_dai_driver mxs_saif_dai = {
.name = "mxs-saif",
- .probe = mxs_saif_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -646,7 +719,8 @@ static struct snd_soc_dai_driver mxs_saif_dai = {
};
static const struct snd_soc_component_driver mxs_saif_component = {
- .name = "mxs-saif",
+ .name = "mxs-saif",
+ .legacy_dai_naming = 1,
};
static irqreturn_t mxs_saif_irq(int irq, void *dev_id)
@@ -708,14 +782,10 @@ static int mxs_saif_mclk_init(struct platform_device *pdev)
static int mxs_saif_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct resource *iores;
struct mxs_saif *saif;
- int irq, ret = 0;
+ int irq, ret;
struct device_node *master;
- if (!np)
- return -EINVAL;
-
saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL);
if (!saif)
return -ENOMEM;
@@ -726,6 +796,11 @@ static int mxs_saif_probe(struct platform_device *pdev)
else
saif->id = ret;
+ if (saif->id >= ARRAY_SIZE(mxs_saif)) {
+ dev_err(&pdev->dev, "get wrong saif id\n");
+ return -EINVAL;
+ }
+
/*
* If there is no "fsl,saif-master" phandle, it's a saif
* master. Otherwise, it's a slave and its phandle points
@@ -736,15 +811,16 @@ static int mxs_saif_probe(struct platform_device *pdev)
saif->master_id = saif->id;
} else {
ret = of_alias_get_id(master, "saif");
+ of_node_put(master);
if (ret < 0)
return ret;
else
saif->master_id = ret;
- }
- if (saif->master_id >= ARRAY_SIZE(mxs_saif)) {
- dev_err(&pdev->dev, "get wrong master id\n");
- return -EINVAL;
+ if (saif->master_id >= ARRAY_SIZE(mxs_saif)) {
+ dev_err(&pdev->dev, "get wrong master id\n");
+ return -EINVAL;
+ }
}
mxs_saif[saif->id] = saif;
@@ -757,19 +833,13 @@ static int mxs_saif_probe(struct platform_device *pdev)
return ret;
}
- iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- saif->base = devm_ioremap_resource(&pdev->dev, iores);
+ saif->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(saif->base))
return PTR_ERR(saif->base);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = irq;
- dev_err(&pdev->dev, "failed to get irq resource: %d\n",
- ret);
- return ret;
- }
+ if (irq < 0)
+ return irq;
saif->dev = &pdev->dev;
ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0,
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h
index 9a4c0b291b9e..8f369283df43 100644
--- a/sound/soc/mxs/mxs-saif.h
+++ b/sound/soc/mxs/mxs-saif.h
@@ -1,19 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 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 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.
*/
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 2b23ffbac6b1..245f17411638 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 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 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/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -32,9 +18,9 @@
static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
unsigned int rate = params_rate(params);
u32 mclk;
int ret;
@@ -68,31 +54,50 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
+static const struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
.hw_params = mxs_sgtl5000_hw_params,
};
#define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS)
+ SND_SOC_DAIFMT_CBC_CFC)
+
+
+SND_SOC_DAILINK_DEFS(hifi_tx,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hifi_rx,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
{
.name = "HiFi Tx",
.stream_name = "HiFi Playback",
- .codec_dai_name = "sgtl5000",
.dai_fmt = MXS_SGTL5000_DAI_FMT,
.ops = &mxs_sgtl5000_hifi_ops,
.playback_only = true,
+ SND_SOC_DAILINK_REG(hifi_tx),
}, {
.name = "HiFi Rx",
.stream_name = "HiFi Capture",
- .codec_dai_name = "sgtl5000",
.dai_fmt = MXS_SGTL5000_DAI_FMT,
.ops = &mxs_sgtl5000_hifi_ops,
.capture_only = true,
+ SND_SOC_DAILINK_REG(hifi_rx),
},
};
+static const struct snd_soc_dapm_widget mxs_sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Line Out Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
static struct snd_soc_card mxs_sgtl5000 = {
.name = "mxs_sgtl5000",
.owner = THIS_MODULE,
@@ -112,16 +117,19 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
codec_np = of_parse_phandle(np, "audio-codec", 0);
if (!saif_np[0] || !saif_np[1] || !codec_np) {
dev_err(&pdev->dev, "phandle missing or invalid\n");
+ of_node_put(codec_np);
+ of_node_put(saif_np[0]);
+ of_node_put(saif_np[1]);
return -EINVAL;
}
for (i = 0; i < 2; i++) {
- mxs_sgtl5000_dai[i].codec_name = NULL;
- mxs_sgtl5000_dai[i].codec_of_node = codec_np;
- mxs_sgtl5000_dai[i].cpu_dai_name = NULL;
- mxs_sgtl5000_dai[i].cpu_of_node = saif_np[i];
- mxs_sgtl5000_dai[i].platform_name = NULL;
- mxs_sgtl5000_dai[i].platform_of_node = saif_np[i];
+ mxs_sgtl5000_dai[i].codecs->name = NULL;
+ mxs_sgtl5000_dai[i].codecs->of_node = codec_np;
+ mxs_sgtl5000_dai[i].cpus->dai_name = NULL;
+ mxs_sgtl5000_dai[i].cpus->of_node = saif_np[i];
+ mxs_sgtl5000_dai[i].platforms->name = NULL;
+ mxs_sgtl5000_dai[i].platforms->of_node = saif_np[i];
}
of_node_put(codec_np);
@@ -140,23 +148,29 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
}
card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- return ret;
+ if (of_property_present(np, "audio-routing")) {
+ card->dapm_widgets = mxs_sgtl5000_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets);
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n",
+ ret);
+ return ret;
+ }
}
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+
return 0;
}
-static int mxs_sgtl5000_remove(struct platform_device *pdev)
+static void mxs_sgtl5000_remove(struct platform_device *pdev)
{
mxs_saif_put_mclk(0);
-
- return 0;
}
static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
diff --git a/sound/soc/nuc900/Kconfig b/sound/soc/nuc900/Kconfig
deleted file mode 100644
index 7f0c954dff6f..000000000000
--- a/sound/soc/nuc900/Kconfig
+++ /dev/null
@@ -1,28 +0,0 @@
-##
-## NUC900 series AC97 API
-##
-config SND_SOC_NUC900
- tristate "SoC Audio for NUC900 series"
- depends on ARCH_W90X900
- select SND_SOC_NUC900_AC97
- help
- This option enables support for AC97 mode on the NUC900 SoC.
-
-config SND_SOC_NUC900_AC97
- tristate
- select AC97_BUS
- select SND_AC97_CODEC
- select SND_SOC_AC97_BUS
-
-
-##
-## Boards
-##
-config SND_SOC_NUC900EVB
- tristate "NUC900 AC97 support for demo board"
- depends on SND_SOC_NUC900
- select SND_SOC_NUC900_AC97
- select SND_SOC_AC97_CODEC
- help
- Select this option to enable audio (AC97) on the
- NUC900 demoboard.
diff --git a/sound/soc/nuc900/Makefile b/sound/soc/nuc900/Makefile
deleted file mode 100644
index 7e46c7150316..000000000000
--- a/sound/soc/nuc900/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# NUC900 series audio
-snd-soc-nuc900-pcm-objs := nuc900-pcm.o
-snd-soc-nuc900-ac97-objs := nuc900-ac97.o
-
-obj-$(CONFIG_SND_SOC_NUC900) += snd-soc-nuc900-pcm.o
-obj-$(CONFIG_SND_SOC_NUC900_AC97) += snd-soc-nuc900-ac97.o
-
-# Boards
-snd-soc-nuc900-audio-objs := nuc900-audio.o
-
-obj-$(CONFIG_SND_SOC_NUC900EVB) += snd-soc-nuc900-audio.o
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
deleted file mode 100644
index b6615affe571..000000000000
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (c) 2009-2010 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@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.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/suspend.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <linux/clk.h>
-
-#include <mach/mfp.h>
-
-#include "nuc900-audio.h"
-
-static DEFINE_MUTEX(ac97_mutex);
-struct nuc900_audio *nuc900_ac97_data;
-EXPORT_SYMBOL_GPL(nuc900_ac97_data);
-
-static int nuc900_checkready(void)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
-
- if (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS0) & CODEC_READY))
- return -EPERM;
-
- return 0;
-}
-
-/* AC97 controller reads codec register */
-static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
- unsigned long timeout = 0x10000, val;
-
- mutex_lock(&ac97_mutex);
-
- val = nuc900_checkready();
- if (val) {
- dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
- goto out;
- }
-
- /* set the R_WB bit and write register index */
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, R_WB | reg);
-
- /* set the valid frame bit and valid slots */
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
- val |= (VALID_FRAME | SLOT1_VALID);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
-
- udelay(100);
-
- /* polling the AC_R_FINISH */
- while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH)
- && timeout--)
- mdelay(1);
-
- if (!timeout) {
- dev_err(nuc900_audio->dev, "AC97 read register time out !\n");
- val = -EPERM;
- goto out;
- }
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0) ;
- val &= ~SLOT1_VALID;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
-
- if (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS1) >> 2 != reg) {
- dev_err(nuc900_audio->dev,
- "R_INDEX of REG_ACTL_ACIS1 not match!\n");
- }
-
- udelay(100);
- val = (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS2) & 0xFFFF);
-
-out:
- mutex_unlock(&ac97_mutex);
- return val;
-}
-
-/* AC97 controller writes to codec register */
-static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
- unsigned long tmp, timeout = 0x10000;
-
- mutex_lock(&ac97_mutex);
-
- tmp = nuc900_checkready();
- if (tmp)
- dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
-
- /* clear the R_WB bit and write register index */
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, reg);
-
- /* write register value */
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS2, val);
-
- /* set the valid frame bit and valid slots */
- tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
- tmp |= SLOT1_VALID | SLOT2_VALID | VALID_FRAME;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
-
- udelay(100);
-
- /* polling the AC_W_FINISH */
- while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH)
- && timeout--)
- mdelay(1);
-
- if (!timeout)
- dev_err(nuc900_audio->dev, "AC97 write register time out !\n");
-
- tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
- tmp &= ~(SLOT1_VALID | SLOT2_VALID);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
-
- mutex_unlock(&ac97_mutex);
-
-}
-
-static void nuc900_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
- unsigned long val;
-
- mutex_lock(&ac97_mutex);
-
- /* warm reset AC 97 */
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
- val |= AC_W_RES;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
-
- udelay(100);
-
- val = nuc900_checkready();
- if (val)
- dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
-
- mutex_unlock(&ac97_mutex);
-}
-
-static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
- unsigned long val;
-
- mutex_lock(&ac97_mutex);
-
- /* reset Audio Controller */
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
- val |= ACTL_RESET_BIT;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
- val &= (~ACTL_RESET_BIT);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
-
- /* reset AC-link interface */
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
- val |= AC_RESET;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
- val &= ~AC_RESET;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
-
- /* cold reset AC 97 */
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
- val |= AC_C_RES;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
- val &= (~AC_C_RES);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
-
- udelay(100);
-
- mutex_unlock(&ac97_mutex);
-
-}
-
-/* AC97 controller operations */
-static struct snd_ac97_bus_ops nuc900_ac97_ops = {
- .read = nuc900_ac97_read,
- .write = nuc900_ac97_write,
- .reset = nuc900_ac97_cold_reset,
- .warm_reset = nuc900_ac97_warm_reset,
-};
-
-static int nuc900_ac97_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
- int ret;
- unsigned long val, tmp;
-
- ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
- tmp |= (SLOT3_VALID | SLOT4_VALID | VALID_FRAME);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
-
- tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
- tmp |= (P_DMA_END_IRQ | P_DMA_MIDDLE_IRQ);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, tmp);
- val |= AC_PLAY;
- } else {
- tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
- tmp |= (R_DMA_END_IRQ | R_DMA_MIDDLE_IRQ);
-
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, tmp);
- val |= AC_RECORD;
- }
-
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
-
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
- tmp &= ~(SLOT3_VALID | SLOT4_VALID);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
-
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, RESET_PRSR);
- val &= ~AC_PLAY;
- } else {
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, RESET_PRSR);
- val &= ~AC_RECORD;
- }
-
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
-
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static int nuc900_ac97_probe(struct snd_soc_dai *dai)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
- unsigned long val;
-
- mutex_lock(&ac97_mutex);
-
- /* enable unit clock */
- clk_enable(nuc900_audio->clk);
-
- /* enable audio controller and AC-link interface */
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
- val |= (IIS_AC_PIN_SEL | ACLINK_EN);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
-
- mutex_unlock(&ac97_mutex);
-
- return 0;
-}
-
-static int nuc900_ac97_remove(struct snd_soc_dai *dai)
-{
- struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
-
- clk_disable(nuc900_audio->clk);
- return 0;
-}
-
-static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
- .trigger = nuc900_ac97_trigger,
-};
-
-static struct snd_soc_dai_driver nuc900_ac97_dai = {
- .probe = nuc900_ac97_probe,
- .remove = nuc900_ac97_remove,
- .bus_control = true,
- .playback = {
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 2,
- },
- .capture = {
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 1,
- .channels_max = 2,
- },
- .ops = &nuc900_ac97_dai_ops,
-};
-
-static const struct snd_soc_component_driver nuc900_ac97_component = {
- .name = "nuc900-ac97",
-};
-
-static int nuc900_ac97_drvprobe(struct platform_device *pdev)
-{
- struct nuc900_audio *nuc900_audio;
- int ret;
-
- if (nuc900_ac97_data)
- return -EBUSY;
-
- nuc900_audio = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_audio),
- GFP_KERNEL);
- if (!nuc900_audio)
- return -ENOMEM;
-
- spin_lock_init(&nuc900_audio->lock);
-
- nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev,
- nuc900_audio->res);
- if (IS_ERR(nuc900_audio->mmio))
- return PTR_ERR(nuc900_audio->mmio);
-
- nuc900_audio->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(nuc900_audio->clk)) {
- ret = PTR_ERR(nuc900_audio->clk);
- goto out;
- }
-
- nuc900_audio->irq_num = platform_get_irq(pdev, 0);
- if (!nuc900_audio->irq_num) {
- ret = -EBUSY;
- goto out;
- }
-
- nuc900_ac97_data = nuc900_audio;
-
- ret = snd_soc_set_ac97_ops(&nuc900_ac97_ops);
- if (ret)
- goto out;
-
- ret = snd_soc_register_component(&pdev->dev, &nuc900_ac97_component,
- &nuc900_ac97_dai, 1);
- if (ret)
- goto out;
-
- /* enbale ac97 multifunction pin */
- mfp_set_groupg(nuc900_audio->dev, NULL);
-
- return 0;
-
-out:
- snd_soc_set_ac97_ops(NULL);
- return ret;
-}
-
-static int nuc900_ac97_drvremove(struct platform_device *pdev)
-{
- snd_soc_unregister_component(&pdev->dev);
-
- nuc900_ac97_data = NULL;
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver nuc900_ac97_driver = {
- .driver = {
- .name = "nuc900-ac97",
- },
- .probe = nuc900_ac97_drvprobe,
- .remove = nuc900_ac97_drvremove,
-};
-
-module_platform_driver(nuc900_ac97_driver);
-
-MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("NUC900 AC97 SoC driver!");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:nuc900-ac97");
diff --git a/sound/soc/nuc900/nuc900-audio.c b/sound/soc/nuc900/nuc900-audio.c
deleted file mode 100644
index 2f6e6fd6e05c..000000000000
--- a/sound/soc/nuc900/nuc900-audio.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2010 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include "nuc900-audio.h"
-
-static struct snd_soc_dai_link nuc900evb_ac97_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "nuc900-ac97",
- .codec_dai_name = "ac97-hifi",
- .codec_name = "ac97-codec",
- .platform_name = "nuc900-pcm-audio",
-};
-
-static struct snd_soc_card nuc900evb_audio_machine = {
- .name = "NUC900EVB_AC97",
- .owner = THIS_MODULE,
- .dai_link = &nuc900evb_ac97_dai,
- .num_links = 1,
-};
-
-static struct platform_device *nuc900evb_asoc_dev;
-
-static int __init nuc900evb_audio_init(void)
-{
- int ret;
-
- ret = -ENOMEM;
- nuc900evb_asoc_dev = platform_device_alloc("soc-audio", -1);
- if (!nuc900evb_asoc_dev)
- goto out;
-
- /* nuc900 board audio device */
- platform_set_drvdata(nuc900evb_asoc_dev, &nuc900evb_audio_machine);
-
- ret = platform_device_add(nuc900evb_asoc_dev);
-
- if (ret) {
- platform_device_put(nuc900evb_asoc_dev);
- nuc900evb_asoc_dev = NULL;
- }
-
-out:
- return ret;
-}
-
-static void __exit nuc900evb_audio_exit(void)
-{
- platform_device_unregister(nuc900evb_asoc_dev);
-}
-
-module_init(nuc900evb_audio_init);
-module_exit(nuc900evb_audio_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("NUC900 Series ASoC audio support");
-MODULE_AUTHOR("Wan ZongShun");
diff --git a/sound/soc/nuc900/nuc900-audio.h b/sound/soc/nuc900/nuc900-audio.h
deleted file mode 100644
index d0b725705914..000000000000
--- a/sound/soc/nuc900/nuc900-audio.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2010 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@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.
- *
- */
-
-#ifndef _NUC900_AUDIO_H
-#define _NUC900_AUDIO_H
-
-#include <linux/io.h>
-
-/* Audio Control Registers */
-#define ACTL_CON 0x00
-#define ACTL_RESET 0x04
-#define ACTL_RDSTB 0x08
-#define ACTL_RDST_LENGTH 0x0C
-#define ACTL_RDSTC 0x10
-#define ACTL_RSR 0x14
-#define ACTL_PDSTB 0x18
-#define ACTL_PDST_LENGTH 0x1C
-#define ACTL_PDSTC 0x20
-#define ACTL_PSR 0x24
-#define ACTL_IISCON 0x28
-#define ACTL_ACCON 0x2C
-#define ACTL_ACOS0 0x30
-#define ACTL_ACOS1 0x34
-#define ACTL_ACOS2 0x38
-#define ACTL_ACIS0 0x3C
-#define ACTL_ACIS1 0x40
-#define ACTL_ACIS2 0x44
-#define ACTL_COUNTER 0x48
-
-/* bit definition of REG_ACTL_CON register */
-#define R_DMA_IRQ 0x1000
-#define T_DMA_IRQ 0x0800
-#define IIS_AC_PIN_SEL 0x0100
-#define FIFO_TH 0x0080
-#define ADC_EN 0x0010
-#define M80_EN 0x0008
-#define ACLINK_EN 0x0004
-#define IIS_EN 0x0002
-
-/* bit definition of REG_ACTL_RESET register */
-#define W5691_PLAY 0x20000
-#define ACTL_RESET_BIT 0x10000
-#define RECORD_RIGHT_CHNNEL 0x08000
-#define RECORD_LEFT_CHNNEL 0x04000
-#define PLAY_RIGHT_CHNNEL 0x02000
-#define PLAY_LEFT_CHNNEL 0x01000
-#define DAC_PLAY 0x00800
-#define ADC_RECORD 0x00400
-#define M80_PLAY 0x00200
-#define AC_RECORD 0x00100
-#define AC_PLAY 0x00080
-#define IIS_RECORD 0x00040
-#define IIS_PLAY 0x00020
-#define DAC_RESET 0x00010
-#define ADC_RESET 0x00008
-#define M80_RESET 0x00004
-#define AC_RESET 0x00002
-#define IIS_RESET 0x00001
-
-/* bit definition of REG_ACTL_ACCON register */
-#define AC_BCLK_PU_EN 0x20
-#define AC_R_FINISH 0x10
-#define AC_W_FINISH 0x08
-#define AC_W_RES 0x04
-#define AC_C_RES 0x02
-
-/* bit definition of ACTL_RSR register */
-#define R_FIFO_EMPTY 0x04
-#define R_DMA_END_IRQ 0x02
-#define R_DMA_MIDDLE_IRQ 0x01
-
-/* bit definition of ACTL_PSR register */
-#define P_FIFO_EMPTY 0x04
-#define P_DMA_END_IRQ 0x02
-#define P_DMA_MIDDLE_IRQ 0x01
-
-/* bit definition of ACTL_ACOS0 register */
-#define SLOT1_VALID 0x01
-#define SLOT2_VALID 0x02
-#define SLOT3_VALID 0x04
-#define SLOT4_VALID 0x08
-#define VALID_FRAME 0x10
-
-/* bit definition of ACTL_ACOS1 register */
-#define R_WB 0x80
-
-#define CODEC_READY 0x10
-#define RESET_PRSR 0x00
-#define AUDIO_WRITE(addr, val) __raw_writel(val, addr)
-#define AUDIO_READ(addr) __raw_readl(addr)
-
-struct nuc900_audio {
- void __iomem *mmio;
- spinlock_t lock;
- unsigned long irq_num;
- struct resource *res;
- struct clk *clk;
- struct device *dev;
-
-};
-
-extern struct nuc900_audio *nuc900_ac97_data;
-
-#endif /*end _NUC900_AUDIO_H */
diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c
deleted file mode 100644
index e09326158bc2..000000000000
--- a/sound/soc/nuc900/nuc900-pcm.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (c) 2010 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-
-#include "nuc900-audio.h"
-
-static const struct snd_pcm_hardware nuc900_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .buffer_bytes_max = 4*1024,
- .period_bytes_min = 1*1024,
- .period_bytes_max = 4*1024,
- .periods_min = 1,
- .periods_max = 1024,
-};
-
-static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-}
-
-static void nuc900_update_dma_register(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio = runtime->private_data;
- void __iomem *mmio_addr, *mmio_len;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
- mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
- } else {
- mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
- mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
- }
-
- AUDIO_WRITE(mmio_addr, runtime->dma_addr);
- AUDIO_WRITE(mmio_len, runtime->dma_bytes);
-}
-
-static void nuc900_dma_start(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio = runtime->private_data;
- unsigned long val;
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
- val |= (T_DMA_IRQ | R_DMA_IRQ);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
-}
-
-static void nuc900_dma_stop(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio = runtime->private_data;
- unsigned long val;
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
- val &= ~(T_DMA_IRQ | R_DMA_IRQ);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
-}
-
-static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
- unsigned long val;
-
- spin_lock(&nuc900_audio->lock);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
-
- if (val & R_DMA_IRQ) {
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
-
- if (val & R_DMA_MIDDLE_IRQ) {
- val |= R_DMA_MIDDLE_IRQ;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
- }
-
- if (val & R_DMA_END_IRQ) {
- val |= R_DMA_END_IRQ;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
- }
- } else if (val & T_DMA_IRQ) {
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
-
- if (val & P_DMA_MIDDLE_IRQ) {
- val |= P_DMA_MIDDLE_IRQ;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
- }
-
- if (val & P_DMA_END_IRQ) {
- val |= P_DMA_END_IRQ;
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
- }
- } else {
- dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
- spin_unlock(&nuc900_audio->lock);
- return IRQ_HANDLED;
- }
-
- spin_unlock(&nuc900_audio->lock);
-
- snd_pcm_period_elapsed(substream);
-
- return IRQ_HANDLED;
-}
-
-static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio = runtime->private_data;
- unsigned long flags, val;
- int ret = 0;
-
- spin_lock_irqsave(&nuc900_audio->lock, flags);
-
- nuc900_update_dma_register(substream);
-
- val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
-
- switch (runtime->channels) {
- case 1:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
- val |= PLAY_RIGHT_CHNNEL;
- } else {
- val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
- val |= RECORD_RIGHT_CHNNEL;
- }
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
- break;
- case 2:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
- else
- val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
- AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
- break;
- default:
- ret = -EINVAL;
- }
- spin_unlock_irqrestore(&nuc900_audio->lock, flags);
- return ret;
-}
-
-static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- nuc900_dma_start(substream);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- nuc900_dma_stop(substream);
- break;
-
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
- dma_addr_t *src, dma_addr_t *dst)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio = runtime->private_data;
-
- if (src != NULL)
- *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
-
- if (dst != NULL)
- *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
-
- return 0;
-}
-
-static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- dma_addr_t src, dst;
- unsigned long res;
-
- nuc900_dma_getposition(substream, &src, &dst);
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- res = dst - runtime->dma_addr;
- else
- res = src - runtime->dma_addr;
-
- return bytes_to_frames(substream->runtime, res);
-}
-
-static int nuc900_dma_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio;
-
- snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
-
- nuc900_audio = nuc900_ac97_data;
-
- if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
- 0, "nuc900-dma", substream))
- return -EBUSY;
-
- runtime->private_data = nuc900_audio;
-
- return 0;
-}
-
-static int nuc900_dma_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nuc900_audio *nuc900_audio = runtime->private_data;
-
- free_irq(nuc900_audio->irq_num, substream);
-
- return 0;
-}
-
-static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops nuc900_dma_ops = {
- .open = nuc900_dma_open,
- .close = nuc900_dma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = nuc900_dma_hw_params,
- .hw_free = nuc900_dma_hw_free,
- .prepare = nuc900_dma_prepare,
- .trigger = nuc900_dma_trigger,
- .pointer = nuc900_dma_pointer,
- .mmap = nuc900_dma_mmap,
-};
-
-static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- card->dev, 4 * 1024, (4 * 1024) - 1);
-
- return 0;
-}
-
-static struct snd_soc_platform_driver nuc900_soc_platform = {
- .ops = &nuc900_dma_ops,
- .pcm_new = nuc900_dma_new,
-};
-
-static int nuc900_soc_platform_probe(struct platform_device *pdev)
-{
- return devm_snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
-}
-
-static struct platform_driver nuc900_pcm_driver = {
- .driver = {
- .name = "nuc900-pcm-audio",
- },
-
- .probe = nuc900_soc_platform_probe,
-};
-
-module_platform_driver(nuc900_pcm_driver);
-
-MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("nuc900 Audio DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
deleted file mode 100644
index 5185a3844da9..000000000000
--- a/sound/soc/omap/Kconfig
+++ /dev/null
@@ -1,124 +0,0 @@
-config SND_OMAP_SOC
- tristate "SoC Audio for the Texas Instruments OMAP chips"
- depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST)
- select SND_DMAENGINE_PCM
-
-config SND_OMAP_SOC_DMIC
- tristate
-
-config SND_OMAP_SOC_MCBSP
- tristate
-
-config SND_OMAP_SOC_MCPDM
- tristate
-
-config SND_OMAP_SOC_HDMI_AUDIO
- tristate "HDMI audio support for OMAP4+ based SoCs"
- depends on SND_OMAP_SOC
- help
- For HDMI audio to work OMAPDSS HDMI support should be
- enabled.
- The hdmi audio driver implements cpu-dai component using the
- callbacks provided by OMAPDSS and registers the component
- under DSS HDMI device. Omap-pcm is registered for platform
- component also under DSS HDMI device. Dummy codec is used as
- as codec component. The hdmi audio driver implements also
- the card and registers it under its own platform device.
- The device for the driver is registered by OMAPDSS hdmi
- driver.
-
-config SND_OMAP_SOC_N810
- tristate "SoC Audio support for Nokia N810"
- depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
- depends on OMAP_MUX
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC3X
- help
- Say Y if you want to add support for SoC audio on Nokia N810.
-
-config SND_OMAP_SOC_RX51
- tristate "SoC Audio support for Nokia N900 (RX-51)"
- depends on SND_OMAP_SOC && ARM && I2C
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC3X
- select SND_SOC_TPA6130A2
- depends on GPIOLIB
- help
- Say Y if you want to add support for SoC audio on Nokia N900
- cellphone.
-
-config SND_OMAP_SOC_AMS_DELTA
- tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
- depends on SND_OMAP_SOC && MACH_AMS_DELTA && TTY
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_CX20442
- help
- Say Y if you want to add support for SoC audio device connected to
- a handset and a speakerphone found on Amstrad E3 (Delta) videophone.
-
- Note that in order to get those devices fully supported, you have to
- build the kernel with standard serial port driver included and
- configured for at least 4 ports. Then, from userspace, you must load
- a line discipline #19 on the modem (ttyS3) serial line. The simplest
- way to achieve this is to install util-linux-ng and use the included
- ldattach utility. This can be started automatically from udev,
- a simple rule like this one should do the trick (it does for me):
- ACTION=="add", KERNEL=="controlC0", \
- RUN+="/usr/sbin/ldattach 19 /dev/ttyS3"
-
-config SND_OMAP_SOC_OSK5912
- tristate "SoC Audio support for omap osk5912"
- depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC23_I2C
- help
- Say Y if you want to add support for SoC audio on osk5912.
-
-config SND_OMAP_SOC_AM3517EVM
- tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
- depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC23_I2C
- help
- Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
- EVM.
-
-config SND_OMAP_SOC_OMAP_TWL4030
- tristate "SoC Audio support for TI SoC based boards with twl4030 codec"
- depends on TWL4030_CORE && SND_OMAP_SOC
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on TI SoC based boards
- using twl4030 as c codec. This driver currently supports:
- - Beagleboard or Devkit8000
- - Gumstix Overo or CompuLab CM-T35/CM-T3730
- - IGEP v2
- - OMAP3EVM
- - SDP3430
- - Zoom2
-
-config SND_OMAP_SOC_OMAP_ABE_TWL6040
- tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
- depends on TWL6040_CORE && SND_OMAP_SOC
- depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
- select SND_OMAP_SOC_DMIC
- select SND_OMAP_SOC_MCPDM
- select SND_SOC_TWL6040
- select SND_SOC_DMIC
- select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
- help
- Say Y if you want to add support for SoC audio on OMAP boards using
- ABE and twl6040 codec. This driver currently supports:
- - SDP4430/Blaze boards
- - PandaBoard (4430)
- - PandaBoardES (4460)
- - omap5-uevm (5432)
-
-config SND_OMAP_SOC_OMAP3_PANDORA
- tristate "SoC Audio support for OMAP3 Pandora"
- depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
deleted file mode 100644
index db36fbd5d1a0..000000000000
--- a/sound/soc/omap/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-# OMAP Platform Support
-snd-soc-omap-objs := omap-pcm.o
-snd-soc-omap-dmic-objs := omap-dmic.o
-snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o
-snd-soc-omap-mcpdm-objs := omap-mcpdm.o
-snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o
-
-obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
-obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o
-obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
-obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
-obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o
-
-# OMAP Machine Support
-snd-soc-n810-objs := n810.o
-snd-soc-rx51-objs := rx51.o
-snd-soc-ams-delta-objs := ams-delta.o
-snd-soc-osk5912-objs := osk5912.o
-snd-soc-am3517evm-objs := am3517evm.o
-snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
-snd-soc-omap-twl4030-objs := omap-twl4030.o
-snd-soc-omap3pandora-objs := omap3pandora.o
-
-obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
-obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
-obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
-obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
-obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
deleted file mode 100644
index 25a33e9d417a..000000000000
--- a/sound/soc/omap/am3517evm.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM
- *
- * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
- *
- * Based on sound/soc/omap/beagle.c by Steve Sakoman
- *
- * Copyright (C) 2009 Texas Instruments Incorporated
- *
- * 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
- * whether express or implied; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-
-#include "../codecs/tlv320aic23.h"
-
-#define CODEC_CLOCK 12000000
-
-static int am3517evm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0,
- CODEC_CLOCK, SND_SOC_CLOCK_IN);
- if (ret < 0)
- printk(KERN_ERR "can't set codec system clock\n");
-
- return ret;
-}
-
-static struct snd_soc_ops am3517evm_ops = {
- .hw_params = am3517evm_hw_params,
-};
-
-/* am3517evm machine dapm widgets */
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Line Out", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_MIC("Mic In", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Line Out connected to LLOUT, RLOUT */
- {"Line Out", NULL, "LOUT"},
- {"Line Out", NULL, "ROUT"},
-
- {"LLINEIN", NULL, "Line In"},
- {"RLINEIN", NULL, "Line In"},
-
- {"MICIN", NULL, "Mic In"},
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link am3517evm_dai = {
- .name = "TLV320AIC23",
- .stream_name = "AIC23",
- .cpu_dai_name = "omap-mcbsp.1",
- .codec_dai_name = "tlv320aic23-hifi",
- .platform_name = "omap-mcbsp.1",
- .codec_name = "tlv320aic23-codec.2-001a",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &am3517evm_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_am3517evm = {
- .name = "am3517evm",
- .owner = THIS_MODULE,
- .dai_link = &am3517evm_dai,
- .num_links = 1,
-
- .dapm_widgets = tlv320aic23_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *am3517evm_snd_device;
-
-static int __init am3517evm_soc_init(void)
-{
- int ret;
-
- if (!machine_is_omap3517evm())
- return -ENODEV;
- pr_info("OMAP3517 / AM3517 EVM SoC init\n");
-
- am3517evm_snd_device = platform_device_alloc("soc-audio", -1);
- if (!am3517evm_snd_device) {
- printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(am3517evm_snd_device, &snd_soc_am3517evm);
-
- ret = platform_device_add(am3517evm_snd_device);
- if (ret)
- goto err1;
-
- return 0;
-
-err1:
- printk(KERN_ERR "Unable to add platform device\n");
- platform_device_put(am3517evm_snd_device);
-
- return ret;
-}
-
-static void __exit am3517evm_soc_exit(void)
-{
- platform_device_unregister(am3517evm_snd_device);
-}
-
-module_init(am3517evm_soc_init);
-module_exit(am3517evm_soc_exit);
-
-MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
deleted file mode 100644
index c7563e230c7d..000000000000
--- a/sound/soc/omap/mcbsp.c
+++ /dev/null
@@ -1,1090 +0,0 @@
-/*
- * sound/soc/omap/mcbsp.c
- *
- * Copyright (C) 2004 Nokia Corporation
- * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * 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.
- *
- * Multichannel mode not supported.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "mcbsp.h"
-
-static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
-{
- void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
-
- if (mcbsp->pdata->reg_size == 2) {
- ((u16 *)mcbsp->reg_cache)[reg] = (u16)val;
- writew_relaxed((u16)val, addr);
- } else {
- ((u32 *)mcbsp->reg_cache)[reg] = val;
- writel_relaxed(val, addr);
- }
-}
-
-static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
-{
- void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
-
- if (mcbsp->pdata->reg_size == 2) {
- return !from_cache ? readw_relaxed(addr) :
- ((u16 *)mcbsp->reg_cache)[reg];
- } else {
- return !from_cache ? readl_relaxed(addr) :
- ((u32 *)mcbsp->reg_cache)[reg];
- }
-}
-
-static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
-{
- writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
-}
-
-static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
-{
- return readl_relaxed(mcbsp->st_data->io_base_st + reg);
-}
-
-#define MCBSP_READ(mcbsp, reg) \
- omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
-#define MCBSP_WRITE(mcbsp, reg, val) \
- omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
-#define MCBSP_READ_CACHE(mcbsp, reg) \
- omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
-
-#define MCBSP_ST_READ(mcbsp, reg) \
- omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
-#define MCBSP_ST_WRITE(mcbsp, reg, val) \
- omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
-
-static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
-{
- dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
- dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
- MCBSP_READ(mcbsp, DRR2));
- dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
- MCBSP_READ(mcbsp, DRR1));
- dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
- MCBSP_READ(mcbsp, DXR2));
- dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
- MCBSP_READ(mcbsp, DXR1));
- dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
- MCBSP_READ(mcbsp, SPCR2));
- dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
- MCBSP_READ(mcbsp, SPCR1));
- dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
- MCBSP_READ(mcbsp, RCR2));
- dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
- MCBSP_READ(mcbsp, RCR1));
- dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
- MCBSP_READ(mcbsp, XCR2));
- dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
- MCBSP_READ(mcbsp, XCR1));
- dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
- MCBSP_READ(mcbsp, SRGR2));
- dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
- MCBSP_READ(mcbsp, SRGR1));
- dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
- MCBSP_READ(mcbsp, PCR0));
- dev_dbg(mcbsp->dev, "***********************\n");
-}
-
-static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id)
-{
- struct omap_mcbsp *mcbsp = dev_id;
- u16 irqst;
-
- irqst = MCBSP_READ(mcbsp, IRQST);
- dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
-
- if (irqst & RSYNCERREN)
- dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
- if (irqst & RFSREN)
- dev_dbg(mcbsp->dev, "RX Frame Sync\n");
- if (irqst & REOFEN)
- dev_dbg(mcbsp->dev, "RX End Of Frame\n");
- if (irqst & RRDYEN)
- dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
- if (irqst & RUNDFLEN)
- dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
- if (irqst & ROVFLEN)
- dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
-
- if (irqst & XSYNCERREN)
- dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
- if (irqst & XFSXEN)
- dev_dbg(mcbsp->dev, "TX Frame Sync\n");
- if (irqst & XEOFEN)
- dev_dbg(mcbsp->dev, "TX End Of Frame\n");
- if (irqst & XRDYEN)
- dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
- if (irqst & XUNDFLEN)
- dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
- if (irqst & XOVFLEN)
- dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
- if (irqst & XEMPTYEOFEN)
- dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
-
- MCBSP_WRITE(mcbsp, IRQST, irqst);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
-{
- struct omap_mcbsp *mcbsp_tx = dev_id;
- u16 irqst_spcr2;
-
- irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
- dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
-
- if (irqst_spcr2 & XSYNC_ERR) {
- dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
- irqst_spcr2);
- /* Writing zero to XSYNC_ERR clears the IRQ */
- MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2));
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
-{
- struct omap_mcbsp *mcbsp_rx = dev_id;
- u16 irqst_spcr1;
-
- irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
- dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
-
- if (irqst_spcr1 & RSYNC_ERR) {
- dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
- irqst_spcr1);
- /* Writing zero to RSYNC_ERR clears the IRQ */
- MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1));
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * omap_mcbsp_config simply write a config to the
- * appropriate McBSP.
- * You either call this function or set the McBSP registers
- * by yourself before calling omap_mcbsp_start().
- */
-void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
- const struct omap_mcbsp_reg_cfg *config)
-{
- dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
- mcbsp->id, mcbsp->phys_base);
-
- /* We write the given config */
- MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
- MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
- MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
- MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
- MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
- MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
- MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
- MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
- MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
- MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
- MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
- if (mcbsp->pdata->has_ccr) {
- MCBSP_WRITE(mcbsp, XCCR, config->xccr);
- MCBSP_WRITE(mcbsp, RCCR, config->rccr);
- }
- /* Enable wakeup behavior */
- if (mcbsp->pdata->has_wakeup)
- MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
-
- /* Enable TX/RX sync error interrupts by default */
- if (mcbsp->irq)
- MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN);
-}
-
-/**
- * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register
- * @id - mcbsp id
- * @stream - indicates the direction of data flow (rx or tx)
- *
- * Returns the address of mcbsp data transmit register or data receive register
- * to be used by DMA for transferring/receiving data based on the value of
- * @stream for the requested mcbsp given by @id
- */
-static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp,
- unsigned int stream)
-{
- int data_reg;
-
- if (mcbsp->pdata->reg_size == 2) {
- if (stream)
- data_reg = OMAP_MCBSP_REG_DRR1;
- else
- data_reg = OMAP_MCBSP_REG_DXR1;
- } else {
- if (stream)
- data_reg = OMAP_MCBSP_REG_DRR;
- else
- data_reg = OMAP_MCBSP_REG_DXR;
- }
-
- return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step;
-}
-
-static void omap_st_on(struct omap_mcbsp *mcbsp)
-{
- unsigned int w;
-
- if (mcbsp->pdata->enable_st_clock)
- mcbsp->pdata->enable_st_clock(mcbsp->id, 1);
-
- /* Enable McBSP Sidetone */
- w = MCBSP_READ(mcbsp, SSELCR);
- MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
-
- /* Enable Sidetone from Sidetone Core */
- w = MCBSP_ST_READ(mcbsp, SSELCR);
- MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
-}
-
-static void omap_st_off(struct omap_mcbsp *mcbsp)
-{
- unsigned int w;
-
- w = MCBSP_ST_READ(mcbsp, SSELCR);
- MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
-
- w = MCBSP_READ(mcbsp, SSELCR);
- MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
-
- if (mcbsp->pdata->enable_st_clock)
- mcbsp->pdata->enable_st_clock(mcbsp->id, 0);
-}
-
-static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
-{
- u16 val, i;
-
- val = MCBSP_ST_READ(mcbsp, SSELCR);
-
- if (val & ST_COEFFWREN)
- MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
-
- MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
-
- for (i = 0; i < 128; i++)
- MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
-
- i = 0;
-
- val = MCBSP_ST_READ(mcbsp, SSELCR);
- while (!(val & ST_COEFFWRDONE) && (++i < 1000))
- val = MCBSP_ST_READ(mcbsp, SSELCR);
-
- MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
-
- if (i == 1000)
- dev_err(mcbsp->dev, "McBSP FIR load error!\n");
-}
-
-static void omap_st_chgain(struct omap_mcbsp *mcbsp)
-{
- u16 w;
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- w = MCBSP_ST_READ(mcbsp, SSELCR);
-
- MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
- ST_CH1GAIN(st_data->ch1gain));
-}
-
-int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int ret = 0;
-
- if (!st_data)
- return -ENOENT;
-
- spin_lock_irq(&mcbsp->lock);
- if (channel == 0)
- st_data->ch0gain = chgain;
- else if (channel == 1)
- st_data->ch1gain = chgain;
- else
- ret = -EINVAL;
-
- if (st_data->enabled)
- omap_st_chgain(mcbsp);
- spin_unlock_irq(&mcbsp->lock);
-
- return ret;
-}
-
-int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int ret = 0;
-
- if (!st_data)
- return -ENOENT;
-
- spin_lock_irq(&mcbsp->lock);
- if (channel == 0)
- *chgain = st_data->ch0gain;
- else if (channel == 1)
- *chgain = st_data->ch1gain;
- else
- ret = -EINVAL;
- spin_unlock_irq(&mcbsp->lock);
-
- return ret;
-}
-
-static int omap_st_start(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (st_data->enabled && !st_data->running) {
- omap_st_fir_write(mcbsp, st_data->taps);
- omap_st_chgain(mcbsp);
-
- if (!mcbsp->free) {
- omap_st_on(mcbsp);
- st_data->running = 1;
- }
- }
-
- return 0;
-}
-
-int omap_st_enable(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (!st_data)
- return -ENODEV;
-
- spin_lock_irq(&mcbsp->lock);
- st_data->enabled = 1;
- omap_st_start(mcbsp);
- spin_unlock_irq(&mcbsp->lock);
-
- return 0;
-}
-
-static int omap_st_stop(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (st_data->running) {
- if (!mcbsp->free) {
- omap_st_off(mcbsp);
- st_data->running = 0;
- }
- }
-
- return 0;
-}
-
-int omap_st_disable(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int ret = 0;
-
- if (!st_data)
- return -ENODEV;
-
- spin_lock_irq(&mcbsp->lock);
- omap_st_stop(mcbsp);
- st_data->enabled = 0;
- spin_unlock_irq(&mcbsp->lock);
-
- return ret;
-}
-
-int omap_st_is_enabled(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (!st_data)
- return -ENODEV;
-
- return st_data->enabled;
-}
-
-/*
- * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
- * The threshold parameter is 1 based, and it is converted (threshold - 1)
- * for the THRSH2 register.
- */
-void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
-{
- if (mcbsp->pdata->buffer_size == 0)
- return;
-
- if (threshold && threshold <= mcbsp->max_tx_thres)
- MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
-}
-
-/*
- * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
- * The threshold parameter is 1 based, and it is converted (threshold - 1)
- * for the THRSH1 register.
- */
-void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
-{
- if (mcbsp->pdata->buffer_size == 0)
- return;
-
- if (threshold && threshold <= mcbsp->max_rx_thres)
- MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
-}
-
-/*
- * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
- */
-u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp)
-{
- u16 buffstat;
-
- if (mcbsp->pdata->buffer_size == 0)
- return 0;
-
- /* Returns the number of free locations in the buffer */
- buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
-
- /* Number of slots are different in McBSP ports */
- return mcbsp->pdata->buffer_size - buffstat;
-}
-
-/*
- * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
- * to reach the threshold value (when the DMA will be triggered to read it)
- */
-u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp)
-{
- u16 buffstat, threshold;
-
- if (mcbsp->pdata->buffer_size == 0)
- return 0;
-
- /* Returns the number of used locations in the buffer */
- buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
- /* RX threshold */
- threshold = MCBSP_READ(mcbsp, THRSH1);
-
- /* Return the number of location till we reach the threshold limit */
- if (threshold <= buffstat)
- return 0;
- else
- return threshold - buffstat;
-}
-
-int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
-{
- void *reg_cache;
- int err;
-
- reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL);
- if (!reg_cache) {
- return -ENOMEM;
- }
-
- spin_lock(&mcbsp->lock);
- if (!mcbsp->free) {
- dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
- mcbsp->id);
- err = -EBUSY;
- goto err_kfree;
- }
-
- mcbsp->free = false;
- mcbsp->reg_cache = reg_cache;
- spin_unlock(&mcbsp->lock);
-
- if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
- mcbsp->pdata->ops->request(mcbsp->id - 1);
-
- /*
- * Make sure that transmitter, receiver and sample-rate generator are
- * not running before activating IRQs.
- */
- MCBSP_WRITE(mcbsp, SPCR1, 0);
- MCBSP_WRITE(mcbsp, SPCR2, 0);
-
- if (mcbsp->irq) {
- err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
- "McBSP", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request IRQ\n");
- goto err_clk_disable;
- }
- } else {
- err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
- "McBSP TX", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
- goto err_clk_disable;
- }
-
- err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
- "McBSP RX", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
- goto err_free_irq;
- }
- }
-
- return 0;
-err_free_irq:
- free_irq(mcbsp->tx_irq, (void *)mcbsp);
-err_clk_disable:
- if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
- mcbsp->pdata->ops->free(mcbsp->id - 1);
-
- /* Disable wakeup behavior */
- if (mcbsp->pdata->has_wakeup)
- MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
-
- spin_lock(&mcbsp->lock);
- mcbsp->free = true;
- mcbsp->reg_cache = NULL;
-err_kfree:
- spin_unlock(&mcbsp->lock);
- kfree(reg_cache);
-
- return err;
-}
-
-void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
-{
- void *reg_cache;
-
- if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
- mcbsp->pdata->ops->free(mcbsp->id - 1);
-
- /* Disable wakeup behavior */
- if (mcbsp->pdata->has_wakeup)
- MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
-
- /* Disable interrupt requests */
- if (mcbsp->irq)
- MCBSP_WRITE(mcbsp, IRQEN, 0);
-
- if (mcbsp->irq) {
- free_irq(mcbsp->irq, (void *)mcbsp);
- } else {
- free_irq(mcbsp->rx_irq, (void *)mcbsp);
- free_irq(mcbsp->tx_irq, (void *)mcbsp);
- }
-
- reg_cache = mcbsp->reg_cache;
-
- /*
- * Select CLKS source from internal source unconditionally before
- * marking the McBSP port as free.
- * If the external clock source via MCBSP_CLKS pin has been selected the
- * system will refuse to enter idle if the CLKS pin source is not reset
- * back to internal source.
- */
- if (!mcbsp_omap1())
- omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC);
-
- spin_lock(&mcbsp->lock);
- if (mcbsp->free)
- dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
- else
- mcbsp->free = true;
- mcbsp->reg_cache = NULL;
- spin_unlock(&mcbsp->lock);
-
- kfree(reg_cache);
-}
-
-/*
- * Here we start the McBSP, by enabling transmitter, receiver or both.
- * If no transmitter or receiver is active prior calling, then sample-rate
- * generator and frame sync are started.
- */
-void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx)
-{
- int enable_srg = 0;
- u16 w;
-
- if (mcbsp->st_data)
- omap_st_start(mcbsp);
-
- /* Only enable SRG, if McBSP is master */
- w = MCBSP_READ_CACHE(mcbsp, PCR0);
- if (w & (FSXM | FSRM | CLKXM | CLKRM))
- enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
- MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
-
- if (enable_srg) {
- /* Start the sample generator */
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
- }
-
- /* Enable transmitter and receiver */
- tx &= 1;
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w | tx);
-
- rx &= 1;
- w = MCBSP_READ_CACHE(mcbsp, SPCR1);
- MCBSP_WRITE(mcbsp, SPCR1, w | rx);
-
- /*
- * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
- * REVISIT: 100us may give enough time for two CLKSRG, however
- * due to some unknown PM related, clock gating etc. reason it
- * is now at 500us.
- */
- udelay(500);
-
- if (enable_srg) {
- /* Start frame sync */
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
- }
-
- if (mcbsp->pdata->has_ccr) {
- /* Release the transmitter and receiver */
- w = MCBSP_READ_CACHE(mcbsp, XCCR);
- w &= ~(tx ? XDISABLE : 0);
- MCBSP_WRITE(mcbsp, XCCR, w);
- w = MCBSP_READ_CACHE(mcbsp, RCCR);
- w &= ~(rx ? RDISABLE : 0);
- MCBSP_WRITE(mcbsp, RCCR, w);
- }
-
- /* Dump McBSP Regs */
- omap_mcbsp_dump_reg(mcbsp);
-}
-
-void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx)
-{
- int idle;
- u16 w;
-
- /* Reset transmitter */
- tx &= 1;
- if (mcbsp->pdata->has_ccr) {
- w = MCBSP_READ_CACHE(mcbsp, XCCR);
- w |= (tx ? XDISABLE : 0);
- MCBSP_WRITE(mcbsp, XCCR, w);
- }
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
-
- /* Reset receiver */
- rx &= 1;
- if (mcbsp->pdata->has_ccr) {
- w = MCBSP_READ_CACHE(mcbsp, RCCR);
- w |= (rx ? RDISABLE : 0);
- MCBSP_WRITE(mcbsp, RCCR, w);
- }
- w = MCBSP_READ_CACHE(mcbsp, SPCR1);
- MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
-
- idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
- MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
-
- if (idle) {
- /* Reset the sample rate generator */
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
- }
-
- if (mcbsp->st_data)
- omap_st_stop(mcbsp);
-}
-
-int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
-{
- struct clk *fck_src;
- const char *src;
- int r;
-
- if (fck_src_id == MCBSP_CLKS_PAD_SRC)
- src = "pad_fck";
- else if (fck_src_id == MCBSP_CLKS_PRCM_SRC)
- src = "prcm_fck";
- else
- return -EINVAL;
-
- fck_src = clk_get(mcbsp->dev, src);
- if (IS_ERR(fck_src)) {
- dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
- return -EINVAL;
- }
-
- pm_runtime_put_sync(mcbsp->dev);
-
- r = clk_set_parent(mcbsp->fclk, fck_src);
- if (r) {
- dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n",
- src);
- clk_put(fck_src);
- return r;
- }
-
- pm_runtime_get_sync(mcbsp->dev);
-
- clk_put(fck_src);
-
- return 0;
-
-}
-
-#define max_thres(m) (mcbsp->pdata->buffer_size)
-#define valid_threshold(m, val) ((val) <= max_thres(m))
-#define THRESHOLD_PROP_BUILDER(prop) \
-static ssize_t prop##_show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
- \
- return sprintf(buf, "%u\n", mcbsp->prop); \
-} \
- \
-static ssize_t prop##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t size) \
-{ \
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
- unsigned long val; \
- int status; \
- \
- status = kstrtoul(buf, 0, &val); \
- if (status) \
- return status; \
- \
- if (!valid_threshold(mcbsp, val)) \
- return -EDOM; \
- \
- mcbsp->prop = val; \
- return size; \
-} \
- \
-static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
-
-THRESHOLD_PROP_BUILDER(max_tx_thres);
-THRESHOLD_PROP_BUILDER(max_rx_thres);
-
-static const char *dma_op_modes[] = {
- "element", "threshold",
-};
-
-static ssize_t dma_op_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- int dma_op_mode, i = 0;
- ssize_t len = 0;
- const char * const *s;
-
- dma_op_mode = mcbsp->dma_op_mode;
-
- for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
- if (dma_op_mode == i)
- len += sprintf(buf + len, "[%s] ", *s);
- else
- len += sprintf(buf + len, "%s ", *s);
- }
- len += sprintf(buf + len, "\n");
-
- return len;
-}
-
-static ssize_t dma_op_mode_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- const char * const *s;
- int i = 0;
-
- for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
- if (sysfs_streq(buf, *s))
- break;
-
- if (i == ARRAY_SIZE(dma_op_modes))
- return -EINVAL;
-
- spin_lock_irq(&mcbsp->lock);
- if (!mcbsp->free) {
- size = -EBUSY;
- goto unlock;
- }
- mcbsp->dma_op_mode = i;
-
-unlock:
- spin_unlock_irq(&mcbsp->lock);
-
- return size;
-}
-
-static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
-
-static const struct attribute *additional_attrs[] = {
- &dev_attr_max_tx_thres.attr,
- &dev_attr_max_rx_thres.attr,
- &dev_attr_dma_op_mode.attr,
- NULL,
-};
-
-static const struct attribute_group additional_attr_group = {
- .attrs = (struct attribute **)additional_attrs,
-};
-
-static ssize_t st_taps_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- ssize_t status = 0;
- int i;
-
- spin_lock_irq(&mcbsp->lock);
- for (i = 0; i < st_data->nr_taps; i++)
- status += sprintf(&buf[status], (i ? ", %d" : "%d"),
- st_data->taps[i]);
- if (i)
- status += sprintf(&buf[status], "\n");
- spin_unlock_irq(&mcbsp->lock);
-
- return status;
-}
-
-static ssize_t st_taps_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int val, tmp, status, i = 0;
-
- spin_lock_irq(&mcbsp->lock);
- memset(st_data->taps, 0, sizeof(st_data->taps));
- st_data->nr_taps = 0;
-
- do {
- status = sscanf(buf, "%d%n", &val, &tmp);
- if (status < 0 || status == 0) {
- size = -EINVAL;
- goto out;
- }
- if (val < -32768 || val > 32767) {
- size = -EINVAL;
- goto out;
- }
- st_data->taps[i++] = val;
- buf += tmp;
- if (*buf != ',')
- break;
- buf++;
- } while (1);
-
- st_data->nr_taps = i;
-
-out:
- spin_unlock_irq(&mcbsp->lock);
-
- return size;
-}
-
-static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
-
-static const struct attribute *sidetone_attrs[] = {
- &dev_attr_st_taps.attr,
- NULL,
-};
-
-static const struct attribute_group sidetone_attr_group = {
- .attrs = (struct attribute **)sidetone_attrs,
-};
-
-static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res)
-{
- struct omap_mcbsp_st_data *st_data;
- int err;
-
- st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
- if (!st_data)
- return -ENOMEM;
-
- st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
- resource_size(res));
- if (!st_data->io_base_st)
- return -ENOMEM;
-
- err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
- if (err)
- return err;
-
- mcbsp->st_data = st_data;
- return 0;
-}
-
-/*
- * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
- * 730 has only 2 McBSP, and both of them are MPU peripherals.
- */
-int omap_mcbsp_init(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
- struct resource *res;
- int ret = 0;
-
- spin_lock_init(&mcbsp->lock);
- mcbsp->free = true;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (!res)
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(mcbsp->io_base))
- return PTR_ERR(mcbsp->io_base);
-
- mcbsp->phys_base = res->start;
- mcbsp->reg_cache_size = resource_size(res);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
- if (!res)
- mcbsp->phys_dma_base = mcbsp->phys_base;
- else
- mcbsp->phys_dma_base = res->start;
-
- /*
- * OMAP1, 2 uses two interrupt lines: TX, RX
- * OMAP2430, OMAP3 SoC have combined IRQ line as well.
- * OMAP4 and newer SoC only have the combined IRQ line.
- * Use the combined IRQ if available since it gives better debugging
- * possibilities.
- */
- mcbsp->irq = platform_get_irq_byname(pdev, "common");
- if (mcbsp->irq == -ENXIO) {
- mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
-
- if (mcbsp->tx_irq == -ENXIO) {
- mcbsp->irq = platform_get_irq(pdev, 0);
- mcbsp->tx_irq = 0;
- } else {
- mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
- mcbsp->irq = 0;
- }
- }
-
- if (!pdev->dev.of_node) {
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
- if (!res) {
- dev_err(&pdev->dev, "invalid tx DMA channel\n");
- return -ENODEV;
- }
- mcbsp->dma_req[0] = res->start;
- mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0];
-
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
- if (!res) {
- dev_err(&pdev->dev, "invalid rx DMA channel\n");
- return -ENODEV;
- }
- mcbsp->dma_req[1] = res->start;
- mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1];
- } else {
- mcbsp->dma_data[0].filter_data = "tx";
- mcbsp->dma_data[1].filter_data = "rx";
- }
-
- mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 0);
- mcbsp->dma_data[0].maxburst = 4;
-
- mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 1);
- mcbsp->dma_data[1].maxburst = 4;
-
- mcbsp->fclk = clk_get(&pdev->dev, "fck");
- if (IS_ERR(mcbsp->fclk)) {
- ret = PTR_ERR(mcbsp->fclk);
- dev_err(mcbsp->dev, "unable to get fck: %d\n", ret);
- return ret;
- }
-
- mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
- if (mcbsp->pdata->buffer_size) {
- /*
- * Initially configure the maximum thresholds to a safe value.
- * The McBSP FIFO usage with these values should not go under
- * 16 locations.
- * If the whole FIFO without safety buffer is used, than there
- * is a possibility that the DMA will be not able to push the
- * new data on time, causing channel shifts in runtime.
- */
- mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
- mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
-
- ret = sysfs_create_group(&mcbsp->dev->kobj,
- &additional_attr_group);
- if (ret) {
- dev_err(mcbsp->dev,
- "Unable to create additional controls\n");
- goto err_thres;
- }
- } else {
- mcbsp->max_tx_thres = -EINVAL;
- mcbsp->max_rx_thres = -EINVAL;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
- if (res) {
- ret = omap_st_add(mcbsp, res);
- if (ret) {
- dev_err(mcbsp->dev,
- "Unable to create sidetone controls\n");
- goto err_st;
- }
- }
-
- return 0;
-
-err_st:
- if (mcbsp->pdata->buffer_size)
- sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
-err_thres:
- clk_put(mcbsp->fclk);
- return ret;
-}
-
-void omap_mcbsp_sysfs_remove(struct omap_mcbsp *mcbsp)
-{
- if (mcbsp->pdata->buffer_size)
- sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
-
- if (mcbsp->st_data)
- sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
-}
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
deleted file mode 100644
index fd99d89de6a8..000000000000
--- a/sound/soc/omap/omap-mcbsp.c
+++ /dev/null
@@ -1,857 +0,0 @@
-/*
- * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
- *
- * Copyright (C) 2008 Nokia Corporation
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * 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/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_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/dmaengine_pcm.h>
-#include <sound/omap-pcm.h>
-
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-#include "mcbsp.h"
-#include "omap-mcbsp.h"
-
-#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
-
-#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
- xhandler_get, xhandler_put) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = omap_mcbsp_st_info_volsw, \
- .get = xhandler_get, .put = xhandler_put, \
- .private_value = (unsigned long) &(struct soc_mixer_control) \
- {.min = xmin, .max = xmax} }
-
-enum {
- OMAP_MCBSP_WORD_8 = 0,
- OMAP_MCBSP_WORD_12,
- OMAP_MCBSP_WORD_16,
- OMAP_MCBSP_WORD_20,
- OMAP_MCBSP_WORD_24,
- OMAP_MCBSP_WORD_32,
-};
-
-/*
- * Stream DMA parameters. DMA request line and port address are set runtime
- * since they are different between OMAP1 and later OMAPs
- */
-static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream,
- unsigned int packet_size)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- int words;
-
- /*
- * Configure McBSP threshold based on either:
- * packet_size, when the sDMA is in packet mode, or based on the
- * period size in THRESHOLD mode, otherwise use McBSP threshold = 1
- * for mono streams.
- */
- if (packet_size)
- words = packet_size;
- else
- words = 1;
-
- /* Configure McBSP internal buffer usage */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- omap_mcbsp_set_tx_threshold(mcbsp, words);
- else
- omap_mcbsp_set_rx_threshold(mcbsp, words);
-}
-
-static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_interval *buffer_size = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct omap_mcbsp *mcbsp = rule->private;
- struct snd_interval frames;
- int size;
-
- snd_interval_any(&frames);
- size = mcbsp->pdata->buffer_size;
-
- frames.min = size / channels->min;
- frames.integer = 1;
- return snd_interval_refine(buffer_size, &frames);
-}
-
-static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- int err = 0;
-
- if (!cpu_dai->active)
- err = omap_mcbsp_request(mcbsp);
-
- /*
- * OMAP3 McBSP FIFO is word structured.
- * McBSP2 has 1024 + 256 = 1280 word long buffer,
- * McBSP1,3,4,5 has 128 word long buffer
- * This means that the size of the FIFO depends on the sample format.
- * For example on McBSP3:
- * 16bit samples: size is 128 * 2 = 256 bytes
- * 32bit samples: size is 128 * 4 = 512 bytes
- * It is simpler to place constraint for buffer and period based on
- * channels.
- * McBSP3 as example again (16 or 32 bit samples):
- * 1 channel (mono): size is 128 frames (128 words)
- * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
- * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
- */
- if (mcbsp->pdata->buffer_size) {
- /*
- * Rule for the buffer size. We should not allow
- * smaller buffer than the FIFO size to avoid underruns.
- * This applies only for the playback stream.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- omap_mcbsp_hwrule_min_buffersize,
- mcbsp,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-
- /* Make sure, that the period size is always even */
- snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
- }
-
- return err;
-}
-
-static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
-
- if (!cpu_dai->active) {
- omap_mcbsp_free(mcbsp);
- mcbsp->configured = 0;
- }
-}
-
-static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- mcbsp->active++;
- omap_mcbsp_start(mcbsp, play, !play);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- omap_mcbsp_stop(mcbsp, play, !play);
- mcbsp->active--;
- break;
- default:
- err = -EINVAL;
- }
-
- return err;
-}
-
-static snd_pcm_sframes_t omap_mcbsp_dai_delay(
- struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- u16 fifo_use;
- snd_pcm_sframes_t delay;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- fifo_use = omap_mcbsp_get_tx_delay(mcbsp);
- else
- fifo_use = omap_mcbsp_get_rx_delay(mcbsp);
-
- /*
- * Divide the used locations with the channel count to get the
- * FIFO usage in samples (don't care about partial samples in the
- * buffer).
- */
- delay = fifo_use / substream->runtime->channels;
-
- return delay;
-}
-
-static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
- struct snd_dmaengine_dai_dma_data *dma_data;
- int wlen, channels, wpf;
- int pkt_size = 0;
- unsigned int format, div, framesize, master;
-
- dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
- channels = params_channels(params);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- wlen = 16;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- wlen = 32;
- break;
- default:
- return -EINVAL;
- }
- if (mcbsp->pdata->buffer_size) {
- if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
- int period_words, max_thrsh;
- int divider = 0;
-
- period_words = params_period_bytes(params) / (wlen / 8);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- max_thrsh = mcbsp->max_tx_thres;
- else
- max_thrsh = mcbsp->max_rx_thres;
- /*
- * Use sDMA packet mode if McBSP is in threshold mode:
- * If period words less than the FIFO size the packet
- * size is set to the number of period words, otherwise
- * Look for the biggest threshold value which divides
- * the period size evenly.
- */
- divider = period_words / max_thrsh;
- if (period_words % max_thrsh)
- divider++;
- while (period_words % divider &&
- divider < period_words)
- divider++;
- if (divider == period_words)
- return -EINVAL;
-
- pkt_size = period_words / divider;
- } else if (channels > 1) {
- /* Use packet mode for non mono streams */
- pkt_size = channels;
- }
- omap_mcbsp_set_threshold(substream, pkt_size);
- }
-
- dma_data->maxburst = pkt_size;
-
- if (mcbsp->configured) {
- /* McBSP already configured by another stream */
- return 0;
- }
-
- regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7));
- regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7));
- regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7));
- regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7));
- format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- wpf = channels;
- if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
- format == SND_SOC_DAIFMT_LEFT_J)) {
- /* Use dual-phase frames */
- regs->rcr2 |= RPHASE;
- regs->xcr2 |= XPHASE;
- /* Set 1 word per (McBSP) frame for phase1 and phase2 */
- wpf--;
- regs->rcr2 |= RFRLEN2(wpf - 1);
- regs->xcr2 |= XFRLEN2(wpf - 1);
- }
-
- regs->rcr1 |= RFRLEN1(wpf - 1);
- regs->xcr1 |= XFRLEN1(wpf - 1);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- /* Set word lengths */
- regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
- regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
- regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
- regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- /* Set word lengths */
- regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
- regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
- regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
- regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32);
- break;
- default:
- /* Unsupported PCM format */
- return -EINVAL;
- }
-
- /* In McBSP master modes, FRAME (i.e. sample rate) is generated
- * by _counting_ BCLKs. Calculate frame size in BCLKs */
- master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK;
- if (master == SND_SOC_DAIFMT_CBS_CFS) {
- div = mcbsp->clk_div ? mcbsp->clk_div : 1;
- framesize = (mcbsp->in_freq / div) / params_rate(params);
-
- if (framesize < wlen * channels) {
- printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
- "channels\n", __func__);
- return -EINVAL;
- }
- } else
- framesize = wlen * channels;
-
- /* Set FS period and length in terms of bit clock periods */
- regs->srgr2 &= ~FPER(0xfff);
- regs->srgr1 &= ~FWID(0xff);
- switch (format) {
- case SND_SOC_DAIFMT_I2S:
- case SND_SOC_DAIFMT_LEFT_J:
- regs->srgr2 |= FPER(framesize - 1);
- regs->srgr1 |= FWID((framesize >> 1) - 1);
- break;
- case SND_SOC_DAIFMT_DSP_A:
- case SND_SOC_DAIFMT_DSP_B:
- regs->srgr2 |= FPER(framesize - 1);
- regs->srgr1 |= FWID(0);
- break;
- }
-
- omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs);
- mcbsp->wlen = wlen;
- mcbsp->configured = 1;
-
- return 0;
-}
-
-/*
- * This must be called before _set_clkdiv and _set_sysclk since McBSP register
- * cache is initialized here
- */
-static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
- bool inv_fs = false;
-
- if (mcbsp->configured)
- return 0;
-
- mcbsp->fmt = fmt;
- memset(regs, 0, sizeof(*regs));
- /* Generic McBSP register settings */
- regs->spcr2 |= XINTM(3) | FREE;
- regs->spcr1 |= RINTM(3);
- /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */
- if (!mcbsp->pdata->has_ccr) {
- regs->rcr2 |= RFIG;
- regs->xcr2 |= XFIG;
- }
-
- /* Configure XCCR/RCCR only for revisions which have ccr registers */
- if (mcbsp->pdata->has_ccr) {
- regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
- regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- /* 1-bit data delay */
- regs->rcr2 |= RDATDLY(1);
- regs->xcr2 |= XDATDLY(1);
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- /* 0-bit data delay */
- regs->rcr2 |= RDATDLY(0);
- regs->xcr2 |= XDATDLY(0);
- regs->spcr1 |= RJUST(2);
- /* Invert FS polarity configuration */
- inv_fs = true;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- /* 1-bit data delay */
- regs->rcr2 |= RDATDLY(1);
- regs->xcr2 |= XDATDLY(1);
- /* Invert FS polarity configuration */
- inv_fs = true;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- /* 0-bit data delay */
- regs->rcr2 |= RDATDLY(0);
- regs->xcr2 |= XDATDLY(0);
- /* Invert FS polarity configuration */
- inv_fs = true;
- break;
- default:
- /* Unsupported data format */
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* McBSP master. Set FS and bit clocks as outputs */
- regs->pcr0 |= FSXM | FSRM |
- CLKXM | CLKRM;
- /* Sample rate generator drives the FS */
- regs->srgr2 |= FSGM;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- /* McBSP slave. FS clock as output */
- regs->srgr2 |= FSGM;
- regs->pcr0 |= FSXM | FSRM;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* McBSP slave */
- break;
- default:
- /* Unsupported master/slave configuration */
- return -EINVAL;
- }
-
- /* Set bit clock (CLKX/CLKR) and FS polarities */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- /*
- * Normal BCLK + FS.
- * FS active low. TX data driven on falling edge of bit clock
- * and RX data sampled on rising edge of bit clock.
- */
- regs->pcr0 |= FSXP | FSRP |
- CLKXP | CLKRP;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- regs->pcr0 |= CLKXP | CLKRP;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- regs->pcr0 |= FSXP | FSRP;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- break;
- default:
- return -EINVAL;
- }
- if (inv_fs == true)
- regs->pcr0 ^= FSXP | FSRP;
-
- return 0;
-}
-
-static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
-
- if (div_id != OMAP_MCBSP_CLKGDV)
- return -ENODEV;
-
- mcbsp->clk_div = div;
- regs->srgr1 &= ~CLKGDV(0xff);
- regs->srgr1 |= CLKGDV(div - 1);
-
- return 0;
-}
-
-static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq,
- int dir)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
- int err = 0;
-
- if (mcbsp->active) {
- if (freq == mcbsp->in_freq)
- return 0;
- else
- return -EBUSY;
- }
-
- mcbsp->in_freq = freq;
- regs->srgr2 &= ~CLKSM;
- regs->pcr0 &= ~SCLKME;
-
- switch (clk_id) {
- case OMAP_MCBSP_SYSCLK_CLK:
- regs->srgr2 |= CLKSM;
- break;
- case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
- if (mcbsp_omap1()) {
- err = -EINVAL;
- break;
- }
- err = omap2_mcbsp_set_clks_src(mcbsp,
- MCBSP_CLKS_PRCM_SRC);
- break;
- case OMAP_MCBSP_SYSCLK_CLKS_EXT:
- if (mcbsp_omap1()) {
- err = 0;
- break;
- }
- err = omap2_mcbsp_set_clks_src(mcbsp,
- MCBSP_CLKS_PAD_SRC);
- break;
-
- case OMAP_MCBSP_SYSCLK_CLKX_EXT:
- regs->srgr2 |= CLKSM;
- regs->pcr0 |= SCLKME;
- /*
- * If McBSP is master but yet the CLKX/CLKR pin drives the SRG,
- * disable output on those pins. This enables to inject the
- * reference clock through CLKX/CLKR. For this to work
- * set_dai_sysclk() _needs_ to be called after set_dai_fmt().
- */
- regs->pcr0 &= ~CLKXM;
- break;
- case OMAP_MCBSP_SYSCLK_CLKR_EXT:
- regs->pcr0 |= SCLKME;
- /* Disable ouput on CLKR pin in master mode */
- regs->pcr0 &= ~CLKRM;
- break;
- default:
- err = -ENODEV;
- }
-
- return err;
-}
-
-static const struct snd_soc_dai_ops mcbsp_dai_ops = {
- .startup = omap_mcbsp_dai_startup,
- .shutdown = omap_mcbsp_dai_shutdown,
- .trigger = omap_mcbsp_dai_trigger,
- .delay = omap_mcbsp_dai_delay,
- .hw_params = omap_mcbsp_dai_hw_params,
- .set_fmt = omap_mcbsp_dai_set_dai_fmt,
- .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
- .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
-};
-
-static int omap_mcbsp_probe(struct snd_soc_dai *dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_enable(mcbsp->dev);
-
- snd_soc_dai_init_dma_data(dai,
- &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
- &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
-
- return 0;
-}
-
-static int omap_mcbsp_remove(struct snd_soc_dai *dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_disable(mcbsp->dev);
-
- return 0;
-}
-
-static struct snd_soc_dai_driver omap_mcbsp_dai = {
- .probe = omap_mcbsp_probe,
- .remove = omap_mcbsp_remove,
- .playback = {
- .channels_min = 1,
- .channels_max = 16,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 16,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
- },
- .ops = &mcbsp_dai_ops,
-};
-
-static const struct snd_soc_component_driver omap_mcbsp_component = {
- .name = "omap-mcbsp",
-};
-
-static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int max = mc->max;
- int min = mc->min;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = min;
- uinfo->value.integer.max = max;
- return 0;
-}
-
-#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \
-static int \
-omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
- struct snd_ctl_elem_value *uc) \
-{ \
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
- struct soc_mixer_control *mc = \
- (struct soc_mixer_control *)kc->private_value; \
- int max = mc->max; \
- int min = mc->min; \
- int val = uc->value.integer.value[0]; \
- \
- if (val < min || val > max) \
- return -EINVAL; \
- \
- /* OMAP McBSP implementation uses index values 0..4 */ \
- return omap_st_set_chgain(mcbsp, channel, val); \
-} \
- \
-static int \
-omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
- struct snd_ctl_elem_value *uc) \
-{ \
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
- s16 chgain; \
- \
- if (omap_st_get_chgain(mcbsp, channel, &chgain)) \
- return -EAGAIN; \
- \
- uc->value.integer.value[0] = chgain; \
- return 0; \
-}
-
-OMAP_MCBSP_ST_CHANNEL_VOLUME(0)
-OMAP_MCBSP_ST_CHANNEL_VOLUME(1)
-
-static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- u8 value = ucontrol->value.integer.value[0];
-
- if (value == omap_st_is_enabled(mcbsp))
- return 0;
-
- if (value)
- omap_st_enable(mcbsp);
- else
- omap_st_disable(mcbsp);
-
- return 1;
-}
-
-static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
-
- ucontrol->value.integer.value[0] = omap_st_is_enabled(mcbsp);
- return 0;
-}
-
-#define OMAP_MCBSP_ST_CONTROLS(port) \
-static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
-SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \
- omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \
-OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
- -32768, 32767, \
- omap_mcbsp_get_st_ch0_volume, \
- omap_mcbsp_set_st_ch0_volume), \
-OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
- -32768, 32767, \
- omap_mcbsp_get_st_ch1_volume, \
- omap_mcbsp_set_st_ch1_volume), \
-}
-
-OMAP_MCBSP_ST_CONTROLS(2);
-OMAP_MCBSP_ST_CONTROLS(3);
-
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
-{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
-
- if (!mcbsp->st_data) {
- dev_warn(mcbsp->dev, "No sidetone data for port\n");
- return 0;
- }
-
- switch (port_id) {
- case 2: /* McBSP 2 */
- return snd_soc_add_dai_controls(cpu_dai,
- omap_mcbsp2_st_controls,
- ARRAY_SIZE(omap_mcbsp2_st_controls));
- case 3: /* McBSP 3 */
- return snd_soc_add_dai_controls(cpu_dai,
- omap_mcbsp3_st_controls,
- ARRAY_SIZE(omap_mcbsp3_st_controls));
- default:
- dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
- break;
- }
-
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
-
-static struct omap_mcbsp_platform_data omap2420_pdata = {
- .reg_step = 4,
- .reg_size = 2,
-};
-
-static struct omap_mcbsp_platform_data omap2430_pdata = {
- .reg_step = 4,
- .reg_size = 4,
- .has_ccr = true,
-};
-
-static struct omap_mcbsp_platform_data omap3_pdata = {
- .reg_step = 4,
- .reg_size = 4,
- .has_ccr = true,
- .has_wakeup = true,
-};
-
-static struct omap_mcbsp_platform_data omap4_pdata = {
- .reg_step = 4,
- .reg_size = 4,
- .has_ccr = true,
- .has_wakeup = true,
-};
-
-static const struct of_device_id omap_mcbsp_of_match[] = {
- {
- .compatible = "ti,omap2420-mcbsp",
- .data = &omap2420_pdata,
- },
- {
- .compatible = "ti,omap2430-mcbsp",
- .data = &omap2430_pdata,
- },
- {
- .compatible = "ti,omap3-mcbsp",
- .data = &omap3_pdata,
- },
- {
- .compatible = "ti,omap4-mcbsp",
- .data = &omap4_pdata,
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match);
-
-static int asoc_mcbsp_probe(struct platform_device *pdev)
-{
- struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct omap_mcbsp *mcbsp;
- const struct of_device_id *match;
- int ret;
-
- match = of_match_device(omap_mcbsp_of_match, &pdev->dev);
- if (match) {
- struct device_node *node = pdev->dev.of_node;
- int buffer_size;
-
- pdata = devm_kzalloc(&pdev->dev,
- sizeof(struct omap_mcbsp_platform_data),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- memcpy(pdata, match->data, sizeof(*pdata));
- if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size))
- pdata->buffer_size = buffer_size;
- } else if (!pdata) {
- dev_err(&pdev->dev, "missing platform data.\n");
- return -EINVAL;
- }
- mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL);
- if (!mcbsp)
- return -ENOMEM;
-
- mcbsp->id = pdev->id;
- mcbsp->pdata = pdata;
- mcbsp->dev = &pdev->dev;
- platform_set_drvdata(pdev, mcbsp);
-
- ret = omap_mcbsp_init(pdev);
- if (ret)
- return ret;
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &omap_mcbsp_component,
- &omap_mcbsp_dai, 1);
- if (ret)
- return ret;
-
- return omap_pcm_platform_register(&pdev->dev);
-}
-
-static int asoc_mcbsp_remove(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
-
- if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
- mcbsp->pdata->ops->free(mcbsp->id);
-
- omap_mcbsp_sysfs_remove(mcbsp);
-
- clk_put(mcbsp->fclk);
-
- return 0;
-}
-
-static struct platform_driver asoc_mcbsp_driver = {
- .driver = {
- .name = "omap-mcbsp",
- .of_match_table = omap_mcbsp_of_match,
- },
-
- .probe = asoc_mcbsp_probe,
- .remove = asoc_mcbsp_remove,
-};
-
-module_platform_driver(asoc_mcbsp_driver);
-
-MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
-MODULE_DESCRIPTION("OMAP I2S SoC Interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:omap-mcbsp");
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
deleted file mode 100644
index 2e3369c27be3..000000000000
--- a/sound/soc/omap/omap-mcbsp.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * omap-mcbsp.h
- *
- * Copyright (C) 2008 Nokia Corporation
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * 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 __OMAP_I2S_H__
-#define __OMAP_I2S_H__
-
-/* Source clocks for McBSP sample rate generator */
-enum omap_mcbsp_clksrg_clk {
- OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */
- OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */
- OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
- OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
- OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
-};
-
-/* McBSP dividers */
-enum omap_mcbsp_div {
- OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
-};
-
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id);
-
-#endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
deleted file mode 100644
index 6bb623a2a4df..000000000000
--- a/sound/soc/omap/omap-pcm.c
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * omap-pcm.c -- ALSA PCM interface for the OMAP SoC
- *
- * Copyright (C) 2008 Nokia Corporation
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * 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/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/omap-dma.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/dmaengine_pcm.h>
-#include <sound/soc.h>
-#include <sound/omap-pcm.h>
-
-#ifdef CONFIG_ARCH_OMAP1
-#define pcm_omap1510() cpu_is_omap1510()
-#else
-#define pcm_omap1510() 0
-#endif
-
-static struct snd_pcm_hardware omap_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .period_bytes_min = 32,
- .period_bytes_max = 64 * 1024,
- .periods_min = 2,
- .periods_max = 255,
- .buffer_bytes_max = 128 * 1024,
-};
-
-/* sDMA supports only 1, 2, and 4 byte transfer elements. */
-static void omap_pcm_limit_supported_formats(void)
-{
- int i;
-
- for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
- switch (snd_pcm_format_physical_width(i)) {
- case 8:
- case 16:
- case 32:
- omap_pcm_hardware.formats |= (1LL << i);
- break;
- default:
- break;
- }
- }
-}
-
-/* this may get called several times by oss emulation */
-static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct omap_pcm_dma_data *dma_data;
- struct dma_slave_config config;
- struct dma_chan *chan;
- int err = 0;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- /* return if this is a bufferless transfer e.g.
- * codec <--> BT codec or GSM modem -- lg FIXME */
- if (!dma_data)
- return 0;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- chan = snd_dmaengine_pcm_get_chan(substream);
- if (!chan)
- return -EINVAL;
-
- /* fills in addr_width and direction */
- err = snd_hwparams_to_dma_slave_config(substream, params, &config);
- if (err)
- return err;
-
- snd_dmaengine_pcm_set_config_from_dai_data(substream,
- snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
- &config);
-
- return dmaengine_slave_config(chan, &config);
-}
-
-static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
-static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
-{
- snd_pcm_uframes_t offset;
-
- if (pcm_omap1510())
- offset = snd_dmaengine_pcm_pointer_no_residue(substream);
- else
- offset = snd_dmaengine_pcm_pointer(substream);
-
- return offset;
-}
-
-static int omap_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_dmaengine_dai_dma_data *dma_data;
- int ret;
-
- snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- /* DT boot: filter_data is the DMA name */
- if (rtd->cpu_dai->dev->of_node) {
- struct dma_chan *chan;
-
- chan = dma_request_slave_channel(rtd->cpu_dai->dev,
- dma_data->filter_data);
- ret = snd_dmaengine_pcm_open(substream, chan);
- } else {
- ret = snd_dmaengine_pcm_open_request_chan(substream,
- omap_dma_filter_fn,
- dma_data->filter_data);
- }
- return ret;
-}
-
-static int omap_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops omap_pcm_ops = {
- .open = omap_pcm_open,
- .close = snd_dmaengine_pcm_close_release_chan,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = omap_pcm_hw_params,
- .hw_free = omap_pcm_hw_free,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = omap_pcm_pointer,
- .mmap = omap_pcm_mmap,
-};
-
-static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
- int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = omap_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- return 0;
-}
-
-static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = omap_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = omap_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
-
-out:
- /* free preallocated buffers in case of error */
- if (ret)
- omap_pcm_free_dma_buffers(pcm);
-
- return ret;
-}
-
-static struct snd_soc_platform_driver omap_soc_platform = {
- .ops = &omap_pcm_ops,
- .pcm_new = omap_pcm_new,
- .pcm_free = omap_pcm_free_dma_buffers,
-};
-
-int omap_pcm_platform_register(struct device *dev)
-{
- omap_pcm_limit_supported_formats();
- return devm_snd_soc_register_platform(dev, &omap_soc_platform);
-}
-EXPORT_SYMBOL_GPL(omap_pcm_platform_register);
-
-MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
-MODULE_DESCRIPTION("OMAP PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index f2bf8661dd21..e54abcd39f79 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,50 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "PXA"
+
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
- depends on ARCH_PXA
+ depends on ARCH_PXA || (COMPILE_TEST && GPIOLIB_LEGACY)
select SND_PXA2XX_LIB
help
Say Y or M if you want to add support for codecs attached to
the PXA2xx AC97, I2S or SSP interface. You will also need
to select the audio interfaces to support below.
-config SND_MMP_SOC
- bool "Soc Audio for Marvell MMP chips"
- depends on ARCH_MMP
- select MMP_SRAM
- select SND_SOC_GENERIC_DMAENGINE_PCM
- select SND_ARM
- help
- Say Y if you want to add support for codecs attached to
- the MMP SSPA interface.
-
-config SND_PXA2XX_AC97
- tristate
- select SND_AC97_CODEC
-
config SND_PXA2XX_SOC_AC97
- tristate
- select AC97_BUS
+ tristate "SoC AC97 support for PXA2xx"
+ depends on SND_PXA2XX_SOC
+ depends on AC97_BUS=n
+ default y
+ select AC97_BUS_NEW
+ select SND_PXA2XX_LIB
select SND_PXA2XX_LIB_AC97
- select SND_SOC_AC97_BUS
+ select SND_SOC_AC97_BUS_NEW
config SND_PXA2XX_SOC_I2S
+ select SND_PXA2XX_LIB
tristate
config SND_PXA_SOC_SSP
- tristate
+ tristate "Soc Audio via PXA2xx/PXA3xx SSP ports"
+ depends on ARCH_PXA
select PXA_SSP
+ select SND_PXA2XX_LIB
config SND_MMP_SOC_SSPA
- tristate
-
-config SND_PXA2XX_SOC_CORGI
- tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
- depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C
- select SND_PXA2XX_SOC_I2S
- select SND_SOC_WM8731
+ tristate "SoC Audio via MMP SSPA ports"
+ depends on ARCH_MMP
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
- Say Y if you want to add support for SoC audio on Sharp
- Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
+ Say Y if you want to add support for codecs attached to
+ the MMP SSPA interface.
config SND_PXA2XX_SOC_SPITZ
tristate "SoC Audio support for Sharp Zaurus SL-Cxx00"
@@ -55,80 +47,6 @@ config SND_PXA2XX_SOC_SPITZ
Say Y if you want to add support for SoC audio on Sharp
Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita).
-config SND_PXA2XX_SOC_Z2
- tristate "SoC Audio support for Zipit Z2"
- depends on SND_PXA2XX_SOC && MACH_ZIPIT2 && I2C
- select SND_PXA2XX_SOC_I2S
- select SND_SOC_WM8750
- help
- Say Y if you want to add support for SoC audio on Zipit Z2.
-
-config SND_PXA2XX_SOC_POODLE
- tristate "SoC Audio support for Poodle"
- depends on SND_PXA2XX_SOC && MACH_POODLE && I2C
- select SND_PXA2XX_SOC_I2S
- select SND_SOC_WM8731
- help
- Say Y if you want to add support for SoC audio on Sharp
- Zaurus SL-5600 model (Poodle).
-
-config SND_PXA2XX_SOC_TOSA
- tristate "SoC AC97 Audio support for Tosa"
- depends on SND_PXA2XX_SOC && MACH_TOSA
- depends on MFD_TC6393XB
- select SND_PXA2XX_SOC_AC97
- select SND_SOC_WM9712
- help
- Say Y if you want to add support for SoC audio on Sharp
- Zaurus SL-C6000x models (Tosa).
-
-config SND_PXA2XX_SOC_E740
- tristate "SoC AC97 Audio support for e740"
- depends on SND_PXA2XX_SOC && MACH_E740
- select SND_SOC_WM9705
- select SND_PXA2XX_SOC_AC97
- help
- Say Y if you want to add support for SoC audio on the
- toshiba e740 PDA
-
-config SND_PXA2XX_SOC_E750
- tristate "SoC AC97 Audio support for e750"
- depends on SND_PXA2XX_SOC && MACH_E750
- select SND_SOC_WM9705
- select SND_PXA2XX_SOC_AC97
- help
- Say Y if you want to add support for SoC audio on the
- toshiba e750 PDA
-
-config SND_PXA2XX_SOC_E800
- tristate "SoC AC97 Audio support for e800"
- depends on SND_PXA2XX_SOC && MACH_E800
- select SND_SOC_WM9712
- select SND_PXA2XX_SOC_AC97
- help
- Say Y if you want to add support for SoC audio on the
- Toshiba e800 PDA
-
-config SND_PXA2XX_SOC_EM_X270
- tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
- depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
- MACH_CM_X300)
- select SND_PXA2XX_SOC_AC97
- select SND_SOC_WM9712
- help
- Say Y if you want to add support for SoC audio on
- CompuLab EM-x270, eXeda and CM-X300 machines.
-
-config SND_PXA2XX_SOC_PALM27X
- bool "SoC Audio support for Palm T|X, T5, E2 and LifeDrive"
- depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \
- MACH_PALMT5 || MACH_PALMTE2)
- select SND_PXA2XX_SOC_AC97
- select SND_SOC_WM9712
- help
- Say Y if you want to add support for SoC audio on
- Palm T|X, T5, E2 or LifeDrive handheld computer.
-
config SND_PXA910_SOC
tristate "SoC Audio for Marvell PXA910 chip"
depends on ARCH_MMP && SND
@@ -137,81 +55,4 @@ config SND_PXA910_SOC
Say Y if you want to add support for SoC audio on the
Marvell PXA910 reference platform.
-config SND_SOC_TTC_DKB
- tristate "SoC Audio support for TTC DKB"
- depends on SND_PXA910_SOC && MACH_TTC_DKB && I2C=y
- select PXA_SSP
- select SND_PXA_SOC_SSP
- select SND_MMP_SOC
- select MFD_88PM860X
- select SND_SOC_88PM860X
- help
- Say Y if you want to add support for SoC audio on TTC DKB
-
-
-config SND_SOC_ZYLONITE
- tristate "SoC Audio support for Marvell Zylonite"
- depends on SND_PXA2XX_SOC && MACH_ZYLONITE
- select SND_PXA2XX_SOC_AC97
- select SND_PXA_SOC_SSP
- select SND_SOC_WM9713
- help
- Say Y if you want to add support for SoC audio on the
- Marvell Zylonite reference platform.
-
-config SND_SOC_RAUMFELD
- tristate "SoC Audio support Raumfeld audio adapter"
- depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR)
- depends on I2C && SPI_MASTER
- select SND_PXA_SOC_SSP
- select SND_SOC_CS4270
- select SND_SOC_AK4104
- help
- Say Y if you want to add support for SoC audio on Raumfeld devices
-
-config SND_PXA2XX_SOC_HX4700
- tristate "SoC Audio support for HP iPAQ hx4700"
- depends on SND_PXA2XX_SOC && MACH_H4700 && I2C
- select SND_PXA2XX_SOC_I2S
- select SND_SOC_AK4641
- help
- Say Y if you want to add support for SoC audio on the
- HP iPAQ hx4700.
-
-config SND_PXA2XX_SOC_MAGICIAN
- tristate "SoC Audio support for HTC Magician"
- depends on SND_PXA2XX_SOC && MACH_MAGICIAN && I2C
- select SND_PXA2XX_SOC_I2S
- select SND_PXA_SOC_SSP
- select SND_SOC_UDA1380
- help
- Say Y if you want to add support for SoC audio on the
- HTC Magician.
-
-config SND_PXA2XX_SOC_MIOA701
- tristate "SoC Audio support for MIO A701"
- depends on SND_PXA2XX_SOC && MACH_MIOA701
- select SND_PXA2XX_SOC_AC97
- select SND_SOC_WM9713
- help
- Say Y if you want to add support for SoC audio on the
- MIO A701.
-
-config SND_PXA2XX_SOC_IMOTE2
- tristate "SoC Audio support for IMote 2"
- depends on SND_PXA2XX_SOC && MACH_INTELMOTE2 && I2C
- select SND_PXA2XX_SOC_I2S
- select SND_SOC_WM8940
- help
- Say Y if you want to add support for SoC audio on the
- IMote 2.
-
-config SND_MMP_SOC_BROWNSTONE
- tristate "SoC Audio support for Marvell Brownstone"
- depends on SND_MMP_SOC && MACH_BROWNSTONE
- select SND_MMP_SOC_SSPA
- select MFD_WM8994
- select SND_SOC_WM8994
- help
- Say Y if you want to add support for SoC audio on the
- Marvell Brownstone reference platform.
+endmenu
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 2cff67b61dc3..93b4e57eaa5c 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -1,53 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
# PXA Platform Support
-snd-soc-pxa2xx-objs := pxa2xx-pcm.o
-snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
-snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
-snd-soc-pxa-ssp-objs := pxa-ssp.o
-snd-soc-mmp-objs := mmp-pcm.o
-snd-soc-mmp-sspa-objs := mmp-sspa.o
+snd-soc-pxa2xx-y := pxa2xx-pcm.o
+snd-soc-pxa2xx-ac97-y := pxa2xx-ac97.o
+snd-soc-pxa2xx-i2s-y := pxa2xx-i2s.o
+snd-soc-pxa-ssp-y := pxa-ssp.o
+snd-soc-mmp-sspa-y := mmp-sspa.o
obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
-obj-$(CONFIG_SND_MMP_SOC) += snd-soc-mmp.o
obj-$(CONFIG_SND_MMP_SOC_SSPA) += snd-soc-mmp-sspa.o
# PXA Machine Support
-snd-soc-corgi-objs := corgi.o
-snd-soc-poodle-objs := poodle.o
-snd-soc-tosa-objs := tosa.o
-snd-soc-e740-objs := e740_wm9705.o
-snd-soc-e750-objs := e750_wm9705.o
-snd-soc-e800-objs := e800_wm9712.o
-snd-soc-spitz-objs := spitz.o
-snd-soc-em-x270-objs := em-x270.o
-snd-soc-palm27x-objs := palm27x.o
-snd-soc-zylonite-objs := zylonite.o
-snd-soc-hx4700-objs := hx4700.o
-snd-soc-magician-objs := magician.o
-snd-soc-mioa701-objs := mioa701_wm9713.o
-snd-soc-z2-objs := z2.o
-snd-soc-imote2-objs := imote2.o
-snd-soc-raumfeld-objs := raumfeld.o
-snd-soc-brownstone-objs := brownstone.o
-snd-soc-ttc-dkb-objs := ttc-dkb.o
-
-obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
-obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
-obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
-obj-$(CONFIG_SND_PXA2XX_SOC_E740) += snd-soc-e740.o
-obj-$(CONFIG_SND_PXA2XX_SOC_E750) += snd-soc-e750.o
-obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
+snd-soc-spitz-y := spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
-obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
-obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
-obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
-obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
-obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
-obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
-obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
-obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
-obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o
-obj-$(CONFIG_SND_MMP_SOC_BROWNSTONE) += snd-soc-brownstone.o
-obj-$(CONFIG_SND_SOC_TTC_DKB) += snd-soc-ttc-dkb.o
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
deleted file mode 100644
index 416ea646c3b1..000000000000
--- a/sound/soc/pxa/brownstone.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * linux/sound/soc/pxa/brownstone.c
- *
- * Copyright (C) 2011 Marvell International 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/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include "../codecs/wm8994.h"
-#include "mmp-sspa.h"
-
-static const struct snd_kcontrol_new brownstone_dapm_control[] = {
- SOC_DAPM_PIN_SWITCH("Ext Spk"),
-};
-
-static const struct snd_soc_dapm_widget brownstone_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_HP("Headset Stereophone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Main Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route brownstone_audio_map[] = {
- {"Ext Spk", NULL, "SPKOUTLP"},
- {"Ext Spk", NULL, "SPKOUTLN"},
- {"Ext Spk", NULL, "SPKOUTRP"},
- {"Ext Spk", NULL, "SPKOUTRN"},
-
- {"Headset Stereophone", NULL, "HPOUT1L"},
- {"Headset Stereophone", NULL, "HPOUT1R"},
-
- {"IN1RN", NULL, "Headset Mic"},
-
- {"DMIC1DAT", NULL, "MICBIAS1"},
- {"MICBIAS1", NULL, "Main Mic"},
-};
-
-static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int freq_out, sspa_mclk, sysclk;
- int sspa_div;
-
- if (params_rate(params) > 11025) {
- freq_out = params_rate(params) * 512;
- sysclk = params_rate(params) * 256;
- sspa_mclk = params_rate(params) * 64;
- } else {
- freq_out = params_rate(params) * 1024;
- sysclk = params_rate(params) * 512;
- sspa_mclk = params_rate(params) * 64;
- }
- sspa_div = freq_out / sspa_mclk;
-
- snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
- snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
- snd_soc_dai_set_pll(cpu_dai, MMP_SSPA_CLK, 0, freq_out, sspa_mclk);
-
- /* set wm8994 sysclk */
- snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, sysclk, 0);
-
- return 0;
-}
-
-/* machine stream operations */
-static struct snd_soc_ops brownstone_ops = {
- .hw_params = brownstone_wm8994_hw_params,
-};
-
-static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
-{
- .name = "WM8994",
- .stream_name = "WM8994 HiFi",
- .cpu_dai_name = "mmp-sspa-dai.0",
- .codec_dai_name = "wm8994-aif1",
- .platform_name = "mmp-pcm-audio",
- .codec_name = "wm8994-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &brownstone_ops,
-},
-};
-
-/* audio machine driver */
-static struct snd_soc_card brownstone = {
- .name = "brownstone",
- .owner = THIS_MODULE,
- .dai_link = brownstone_wm8994_dai,
- .num_links = ARRAY_SIZE(brownstone_wm8994_dai),
-
- .controls = brownstone_dapm_control,
- .num_controls = ARRAY_SIZE(brownstone_dapm_control),
- .dapm_widgets = brownstone_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
- .dapm_routes = brownstone_audio_map,
- .num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
- .fully_routed = true,
-};
-
-static int brownstone_probe(struct platform_device *pdev)
-{
- int ret;
-
- brownstone.dev = &pdev->dev;
- ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- return ret;
-}
-
-static struct platform_driver mmp_driver = {
- .driver = {
- .name = "brownstone-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = brownstone_probe,
-};
-
-module_platform_driver(mmp_driver);
-
-MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
-MODULE_DESCRIPTION("ALSA SoC Brownstone");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
deleted file mode 100644
index c97dc13d3608..000000000000
--- a/sound/soc/pxa/corgi.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * corgi.c -- SoC audio for Corgi
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- *
- * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
- * Richard Purdie <richard@openedhand.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/timer.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/corgi.h>
-#include <mach/audio.h>
-
-#include "../codecs/wm8731.h"
-#include "pxa2xx-i2s.h"
-
-#define CORGI_HP 0
-#define CORGI_MIC 1
-#define CORGI_LINE 2
-#define CORGI_HEADSET 3
-#define CORGI_HP_OFF 4
-#define CORGI_SPK_ON 0
-#define CORGI_SPK_OFF 1
-
- /* audio clock in Hz - rounded from 12.235MHz */
-#define CORGI_AUDIO_CLOCK 12288000
-
-static int corgi_jack_func;
-static int corgi_spk_func;
-
-static void corgi_ext_control(struct snd_soc_dapm_context *dapm)
-{
- snd_soc_dapm_mutex_lock(dapm);
-
- /* set up jack connection */
- switch (corgi_jack_func) {
- case CORGI_HP:
- /* set = unmute headphone */
- gpio_set_value(CORGI_GPIO_MUTE_L, 1);
- gpio_set_value(CORGI_GPIO_MUTE_R, 1);
- snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
- break;
- case CORGI_MIC:
- /* reset = mute headphone */
- gpio_set_value(CORGI_GPIO_MUTE_L, 0);
- gpio_set_value(CORGI_GPIO_MUTE_R, 0);
- snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
- break;
- case CORGI_LINE:
- gpio_set_value(CORGI_GPIO_MUTE_L, 0);
- gpio_set_value(CORGI_GPIO_MUTE_R, 0);
- snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
- break;
- case CORGI_HEADSET:
- gpio_set_value(CORGI_GPIO_MUTE_L, 0);
- gpio_set_value(CORGI_GPIO_MUTE_R, 1);
- snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
- break;
- }
-
- if (corgi_spk_func == CORGI_SPK_ON)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
-
- /* signal a DAPM event */
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-}
-
-static int corgi_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* check the jack status at stream startup */
- corgi_ext_control(&rtd->card->dapm);
-
- return 0;
-}
-
-/* we need to unmute the HP at shutdown as the mute burns power on corgi */
-static void corgi_shutdown(struct snd_pcm_substream *substream)
-{
- /* set = unmute headphone */
- gpio_set_value(CORGI_GPIO_MUTE_L, 1);
- gpio_set_value(CORGI_GPIO_MUTE_R, 1);
-}
-
-static int corgi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret = 0;
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 48000:
- case 96000:
- clk = 12288000;
- break;
- case 11025:
- case 22050:
- case 44100:
- clk = 11289600;
- break;
- }
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set the I2S system clock as input (unused) */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops corgi_ops = {
- .startup = corgi_startup,
- .hw_params = corgi_hw_params,
- .shutdown = corgi_shutdown,
-};
-
-static int corgi_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = corgi_jack_func;
- return 0;
-}
-
-static int corgi_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (corgi_jack_func == ucontrol->value.integer.value[0])
- return 0;
-
- corgi_jack_func = ucontrol->value.integer.value[0];
- corgi_ext_control(&card->dapm);
- return 1;
-}
-
-static int corgi_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = corgi_spk_func;
- return 0;
-}
-
-static int corgi_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (corgi_spk_func == ucontrol->value.integer.value[0])
- return 0;
-
- corgi_spk_func = ucontrol->value.integer.value[0];
- corgi_ext_control(&card->dapm);
- return 1;
-}
-
-static int corgi_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-static int corgi_mic_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-/* corgi machine dapm widgets */
-static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
-SND_SOC_DAPM_HP("Headphone Jack", NULL),
-SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
-SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
-SND_SOC_DAPM_LINE("Line Jack", NULL),
-SND_SOC_DAPM_HP("Headset Jack", NULL),
-};
-
-/* Corgi machine audio map (connections to the codec pins) */
-static const struct snd_soc_dapm_route corgi_audio_map[] = {
-
- /* headset Jack - in = micin, out = LHPOUT*/
- {"Headset Jack", NULL, "LHPOUT"},
-
- /* headphone connected to LHPOUT1, RHPOUT1 */
- {"Headphone Jack", NULL, "LHPOUT"},
- {"Headphone Jack", NULL, "RHPOUT"},
-
- /* speaker connected to LOUT, ROUT */
- {"Ext Spk", NULL, "ROUT"},
- {"Ext Spk", NULL, "LOUT"},
-
- /* mic is connected to MICIN (via right channel of headphone jack) */
- {"MICIN", NULL, "Mic Jack"},
-
- /* Same as the above but no mic bias for line signals */
- {"MICIN", NULL, "Line Jack"},
-};
-
-static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
- "Off"};
-static const char *spk_function[] = {"On", "Off"};
-static const struct soc_enum corgi_enum[] = {
- SOC_ENUM_SINGLE_EXT(5, jack_function),
- SOC_ENUM_SINGLE_EXT(2, spk_function),
-};
-
-static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
- SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
- corgi_set_jack),
- SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
- corgi_set_spk),
-};
-
-/* corgi digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link corgi_dai = {
- .name = "WM8731",
- .stream_name = "WM8731",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "wm8731-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731.0-001b",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &corgi_ops,
-};
-
-/* corgi audio machine driver */
-static struct snd_soc_card corgi = {
- .name = "Corgi",
- .owner = THIS_MODULE,
- .dai_link = &corgi_dai,
- .num_links = 1,
-
- .controls = wm8731_corgi_controls,
- .num_controls = ARRAY_SIZE(wm8731_corgi_controls),
- .dapm_widgets = wm8731_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
- .dapm_routes = corgi_audio_map,
- .num_dapm_routes = ARRAY_SIZE(corgi_audio_map),
- .fully_routed = true,
-};
-
-static int corgi_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &corgi;
- int ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- return ret;
-}
-
-static struct platform_driver corgi_driver = {
- .driver = {
- .name = "corgi-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = corgi_probe,
-};
-
-module_platform_driver(corgi_driver);
-
-/* Module information */
-MODULE_AUTHOR("Richard Purdie");
-MODULE_DESCRIPTION("ALSA SoC Corgi");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:corgi-audio");
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c
deleted file mode 100644
index 1de876529aa1..000000000000
--- a/sound/soc/pxa/e740_wm9705.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * e740-wm9705.c -- SoC audio for e740
- *
- * Copyright 2007 (c) 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 ONLY.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <mach/audio.h>
-#include <mach/eseries-gpio.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/wm9705.h"
-#include "pxa2xx-ac97.h"
-
-
-#define E740_AUDIO_OUT 1
-#define E740_AUDIO_IN 2
-
-static int e740_audio_power;
-
-static void e740_sync_audio_power(int status)
-{
- gpio_set_value(GPIO_E740_WM9705_nAVDD2, !status);
- gpio_set_value(GPIO_E740_AMP_ON, (status & E740_AUDIO_OUT) ? 1 : 0);
- gpio_set_value(GPIO_E740_MIC_ON, (status & E740_AUDIO_IN) ? 1 : 0);
-}
-
-static int e740_mic_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (event & SND_SOC_DAPM_PRE_PMU)
- e740_audio_power |= E740_AUDIO_IN;
- else if (event & SND_SOC_DAPM_POST_PMD)
- e740_audio_power &= ~E740_AUDIO_IN;
-
- e740_sync_audio_power(e740_audio_power);
-
- return 0;
-}
-
-static int e740_output_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (event & SND_SOC_DAPM_PRE_PMU)
- e740_audio_power |= E740_AUDIO_OUT;
- else if (event & SND_SOC_DAPM_POST_PMD)
- e740_audio_power &= ~E740_AUDIO_OUT;
-
- e740_sync_audio_power(e740_audio_power);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget e740_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
- SND_SOC_DAPM_PGA_E("Output Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
- e740_output_amp_event, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_PGA_E("Mic Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
- e740_mic_amp_event, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Output Amp", NULL, "LOUT"},
- {"Output Amp", NULL, "ROUT"},
- {"Output Amp", NULL, "MONOOUT"},
-
- {"Speaker", NULL, "Output Amp"},
- {"Headphone Jack", NULL, "Output Amp"},
-
- {"MIC1", NULL, "Mic Amp"},
- {"Mic Amp", NULL, "Mic (Internal)"},
-};
-
-static struct snd_soc_dai_link e740_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9705-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9705-codec",
- },
- {
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name = "wm9705-aux",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9705-codec",
- },
-};
-
-static struct snd_soc_card e740 = {
- .name = "Toshiba e740",
- .owner = THIS_MODULE,
- .dai_link = e740_dai,
- .num_links = ARRAY_SIZE(e740_dai),
-
- .dapm_widgets = e740_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(e740_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static struct gpio e740_audio_gpios[] = {
- { GPIO_E740_MIC_ON, GPIOF_OUT_INIT_LOW, "Mic amp" },
- { GPIO_E740_AMP_ON, GPIOF_OUT_INIT_LOW, "Output amp" },
- { GPIO_E740_WM9705_nAVDD2, GPIOF_OUT_INIT_HIGH, "Audio power" },
-};
-
-static int e740_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &e740;
- int ret;
-
- ret = gpio_request_array(e740_audio_gpios,
- ARRAY_SIZE(e740_audio_gpios));
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
- }
- return ret;
-}
-
-static int e740_remove(struct platform_device *pdev)
-{
- gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
- return 0;
-}
-
-static struct platform_driver e740_driver = {
- .driver = {
- .name = "e740-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = e740_probe,
- .remove = e740_remove,
-};
-
-module_platform_driver(e740_driver);
-
-/* Module information */
-MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
-MODULE_DESCRIPTION("ALSA SoC driver for e740");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:e740-audio");
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
deleted file mode 100644
index b7eb7cd5df7d..000000000000
--- a/sound/soc/pxa/e750_wm9705.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * e750-wm9705.c -- SoC audio for e750
- *
- * Copyright 2007 (c) 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 ONLY.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <mach/audio.h>
-#include <mach/eseries-gpio.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/wm9705.h"
-#include "pxa2xx-ac97.h"
-
-static int e750_spk_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (event & SND_SOC_DAPM_PRE_PMU)
- gpio_set_value(GPIO_E750_SPK_AMP_OFF, 0);
- else if (event & SND_SOC_DAPM_POST_PMD)
- gpio_set_value(GPIO_E750_SPK_AMP_OFF, 1);
-
- return 0;
-}
-
-static int e750_hp_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (event & SND_SOC_DAPM_PRE_PMU)
- gpio_set_value(GPIO_E750_HP_AMP_OFF, 0);
- else if (event & SND_SOC_DAPM_POST_PMD)
- gpio_set_value(GPIO_E750_HP_AMP_OFF, 1);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget e750_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
- SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
- e750_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
- e750_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Amp", NULL, "HPOUTL"},
- {"Headphone Amp", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "Headphone Amp"},
-
- {"Speaker Amp", NULL, "MONOOUT"},
- {"Speaker", NULL, "Speaker Amp"},
-
- {"MIC1", NULL, "Mic (Internal)"},
-};
-
-static struct snd_soc_dai_link e750_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9705-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9705-codec",
- /* use ops to check startup state */
- },
- {
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9705-aux",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9705-codec",
- },
-};
-
-static struct snd_soc_card e750 = {
- .name = "Toshiba e750",
- .owner = THIS_MODULE,
- .dai_link = e750_dai,
- .num_links = ARRAY_SIZE(e750_dai),
-
- .dapm_widgets = e750_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(e750_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static struct gpio e750_audio_gpios[] = {
- { GPIO_E750_HP_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Headphone amp" },
- { GPIO_E750_SPK_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Speaker amp" },
-};
-
-static int e750_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &e750;
- int ret;
-
- ret = gpio_request_array(e750_audio_gpios,
- ARRAY_SIZE(e750_audio_gpios));
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
- }
- return ret;
-}
-
-static int e750_remove(struct platform_device *pdev)
-{
- gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
- return 0;
-}
-
-static struct platform_driver e750_driver = {
- .driver = {
- .name = "e750-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = e750_probe,
- .remove = e750_remove,
-};
-
-module_platform_driver(e750_driver);
-
-/* Module information */
-MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
-MODULE_DESCRIPTION("ALSA SoC driver for e750");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:e750-audio");
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
deleted file mode 100644
index 41bf71466a7b..000000000000
--- a/sound/soc/pxa/e800_wm9712.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * e800-wm9712.c -- SoC audio for e800
- *
- * Copyright 2007 (c) 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 ONLY.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/audio.h>
-#include <mach/eseries-gpio.h>
-
-#include "../codecs/wm9712.h"
-#include "pxa2xx-ac97.h"
-
-static int e800_spk_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (event & SND_SOC_DAPM_PRE_PMU)
- gpio_set_value(GPIO_E800_SPK_AMP_ON, 1);
- else if (event & SND_SOC_DAPM_POST_PMD)
- gpio_set_value(GPIO_E800_SPK_AMP_ON, 0);
-
- return 0;
-}
-
-static int e800_hp_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (event & SND_SOC_DAPM_PRE_PMU)
- gpio_set_value(GPIO_E800_HP_AMP_OFF, 0);
- else if (event & SND_SOC_DAPM_POST_PMD)
- gpio_set_value(GPIO_E800_HP_AMP_OFF, 1);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget e800_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic (Internal1)", NULL),
- SND_SOC_DAPM_MIC("Mic (Internal2)", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
- e800_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
- e800_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Headphone Jack", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "Headphone Amp"},
-
- {"Speaker Amp", NULL, "MONOOUT"},
- {"Speaker", NULL, "Speaker Amp"},
-
- {"MIC1", NULL, "Mic (Internal1)"},
- {"MIC2", NULL, "Mic (Internal2)"},
-};
-
-static struct snd_soc_dai_link e800_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9712-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9712-codec",
- },
- {
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9712-aux",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9712-codec",
- },
-};
-
-static struct snd_soc_card e800 = {
- .name = "Toshiba e800",
- .owner = THIS_MODULE,
- .dai_link = e800_dai,
- .num_links = ARRAY_SIZE(e800_dai),
-
- .dapm_widgets = e800_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(e800_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct gpio e800_audio_gpios[] = {
- { GPIO_E800_SPK_AMP_ON, GPIOF_OUT_INIT_HIGH, "Headphone amp" },
- { GPIO_E800_HP_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Speaker amp" },
-};
-
-static int e800_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &e800;
- int ret;
-
- ret = gpio_request_array(e800_audio_gpios,
- ARRAY_SIZE(e800_audio_gpios));
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
- }
- return ret;
-}
-
-static int e800_remove(struct platform_device *pdev)
-{
- gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
- return 0;
-}
-
-static struct platform_driver e800_driver = {
- .driver = {
- .name = "e800-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = e800_probe,
- .remove = e800_remove,
-};
-
-module_platform_driver(e800_driver);
-
-/* Module information */
-MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
-MODULE_DESCRIPTION("ALSA SoC driver for e800");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:e800-audio");
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
deleted file mode 100644
index 64743a05aeae..000000000000
--- a/sound/soc/pxa/em-x270.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * SoC audio driver for EM-X270, eXeda and CM-X300
- *
- * Copyright 2007, 2009 CompuLab, Ltd.
- *
- * Author: Mike Rapoport <mike@compulab.co.il>
- *
- * Copied from tosa.c:
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- *
- * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
- * Richard Purdie <richard@openedhand.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/device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/audio.h>
-
-#include "../codecs/wm9712.h"
-#include "pxa2xx-ac97.h"
-
-static struct snd_soc_dai_link em_x270_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9712-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9712-codec",
- },
- {
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9712-aux",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9712-codec",
- },
-};
-
-static struct snd_soc_card em_x270 = {
- .name = "EM-X270",
- .owner = THIS_MODULE,
- .dai_link = em_x270_dai,
- .num_links = ARRAY_SIZE(em_x270_dai),
-};
-
-static struct platform_device *em_x270_snd_device;
-
-static int __init em_x270_init(void)
-{
- int ret;
-
- if (!(machine_is_em_x270() || machine_is_exeda()
- || machine_is_cm_x300()))
- return -ENODEV;
-
- em_x270_snd_device = platform_device_alloc("soc-audio", -1);
- if (!em_x270_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(em_x270_snd_device, &em_x270);
- ret = platform_device_add(em_x270_snd_device);
-
- if (ret)
- platform_device_put(em_x270_snd_device);
-
- return ret;
-}
-
-static void __exit em_x270_exit(void)
-{
- platform_device_unregister(em_x270_snd_device);
-}
-
-module_init(em_x270_init);
-module_exit(em_x270_exit);
-
-/* Module information */
-MODULE_AUTHOR("Mike Rapoport");
-MODULE_DESCRIPTION("ALSA SoC EM-X270, eXeda and CM-X300");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
deleted file mode 100644
index ecbf2873b7ff..000000000000
--- a/sound/soc/pxa/hx4700.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * SoC audio for HP iPAQ hx4700
- *
- * Copyright (c) 2009 Philipp Zabel
- *
- * 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/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <mach/hx4700.h>
-#include <asm/mach-types.h>
-#include "pxa2xx-i2s.h"
-
-#include "../codecs/ak4641.h"
-
-static struct snd_soc_jack hs_jack;
-
-/* Headphones jack detection DAPM pin */
-static struct snd_soc_jack_pin hs_jack_pin[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "Speaker",
- /* disable speaker when hp jack is inserted */
- .mask = SND_JACK_HEADPHONE,
- .invert = 1,
- },
-};
-
-/* Headphones jack detection GPIO */
-static struct snd_soc_jack_gpio hs_jack_gpio = {
- .gpio = GPIO75_HX4700_EARPHONE_nDET,
- .invert = true,
- .name = "hp-gpio",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 200,
-};
-
-/*
- * iPAQ hx4700 uses I2S for capture and playback.
- */
-static int hx4700_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret = 0;
-
- /* set the I2S system clock as output */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- /* inform codec driver about clock freq *
- * (PXA I2S always uses divider 256) */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops hx4700_ops = {
- .hw_params = hx4700_hw_params,
-};
-
-static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-/* hx4700 machine dapm widgets */
-static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
- SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
- SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
-};
-
-/* hx4700 machine audio_map */
-static const struct snd_soc_dapm_route hx4700_audio_map[] = {
-
- /* Headphone connected to LOUT, ROUT */
- {"Headphone Jack", NULL, "LOUT"},
- {"Headphone Jack", NULL, "ROUT"},
-
- /* Speaker connected to MOUT2 */
- {"Speaker", NULL, "MOUT2"},
-
- /* Microphone connected to MICIN */
- {"MICIN", NULL, "Built-in Microphone"},
- {"AIN", NULL, "MICOUT"},
-};
-
-/*
- * Logic for a ak4641 as connected on a HP iPAQ hx4700
- */
-static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
-{
- int err;
-
- /* Jack detection API stuff */
- err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin,
- ARRAY_SIZE(hs_jack_pin));
- if (err)
- return err;
-
- err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
-
- return err;
-}
-
-static int hx4700_card_remove(struct snd_soc_card *card)
-{
- snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
-
- return 0;
-}
-
-/* hx4700 digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link hx4700_dai = {
- .name = "ak4641",
- .stream_name = "AK4641",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "ak4641-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "ak4641.0-0012",
- .init = hx4700_ak4641_init,
- .dai_fmt = SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &hx4700_ops,
-};
-
-/* hx4700 audio machine driver */
-static struct snd_soc_card snd_soc_card_hx4700 = {
- .name = "iPAQ hx4700",
- .owner = THIS_MODULE,
- .remove = hx4700_card_remove,
- .dai_link = &hx4700_dai,
- .num_links = 1,
- .dapm_widgets = hx4700_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
- .dapm_routes = hx4700_audio_map,
- .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
- .fully_routed = true,
-};
-
-static struct gpio hx4700_audio_gpios[] = {
- { GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
- { GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
-};
-
-static int hx4700_audio_probe(struct platform_device *pdev)
-{
- int ret;
-
- if (!machine_is_h4700())
- return -ENODEV;
-
- ret = gpio_request_array(hx4700_audio_gpios,
- ARRAY_SIZE(hx4700_audio_gpios));
- if (ret)
- return ret;
-
- snd_soc_card_hx4700.dev = &pdev->dev;
- ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
- if (ret)
- gpio_free_array(hx4700_audio_gpios,
- ARRAY_SIZE(hx4700_audio_gpios));
-
- return ret;
-}
-
-static int hx4700_audio_remove(struct platform_device *pdev)
-{
- gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
- gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
-
- gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
- return 0;
-}
-
-static struct platform_driver hx4700_audio_driver = {
- .driver = {
- .name = "hx4700-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = hx4700_audio_probe,
- .remove = hx4700_audio_remove,
-};
-
-module_platform_driver(hx4700_audio_driver);
-
-MODULE_AUTHOR("Philipp Zabel");
-MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:hx4700-audio");
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
deleted file mode 100644
index 9d0e40771ef5..000000000000
--- a/sound/soc/pxa/imote2.c
+++ /dev/null
@@ -1,95 +0,0 @@
-
-#include <linux/module.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/wm8940.h"
-#include "pxa2xx-i2s.h"
-
-static int imote2_asoc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret;
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 48000:
- case 96000:
- clk = 12288000;
- break;
- case 11025:
- case 22050:
- case 44100:
- clk = 11289600;
- break;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set the I2S system clock as input (unused) */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, clk,
- SND_SOC_CLOCK_OUT);
-
- return ret;
-}
-
-static struct snd_soc_ops imote2_asoc_ops = {
- .hw_params = imote2_asoc_hw_params,
-};
-
-static struct snd_soc_dai_link imote2_dai = {
- .name = "WM8940",
- .stream_name = "WM8940",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "wm8940-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm8940-codec.0-0034",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &imote2_asoc_ops,
-};
-
-static struct snd_soc_card imote2 = {
- .name = "Imote2",
- .owner = THIS_MODULE,
- .dai_link = &imote2_dai,
- .num_links = 1,
-};
-
-static int imote2_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &imote2;
- int ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- return ret;
-}
-
-static struct platform_driver imote2_driver = {
- .driver = {
- .name = "imote2-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = imote2_probe,
-};
-
-module_platform_driver(imote2_driver);
-
-MODULE_AUTHOR("Jonathan Cameron");
-MODULE_DESCRIPTION("ALSA SoC Imote 2");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imote2-audio");
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
deleted file mode 100644
index 241d0be42d7a..000000000000
--- a/sound/soc/pxa/magician.c
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * SoC audio for HTC Magician
- *
- * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
- *
- * based on spitz.c,
- * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
- * Richard Purdie <richard@openedhand.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/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/uda1380.h>
-
-#include <mach/magician.h>
-#include <asm/mach-types.h>
-#include "../codecs/uda1380.h"
-#include "pxa2xx-i2s.h"
-#include "pxa-ssp.h"
-
-#define MAGICIAN_MIC 0
-#define MAGICIAN_MIC_EXT 1
-
-static int magician_hp_switch;
-static int magician_spk_switch = 1;
-static int magician_in_sel = MAGICIAN_MIC;
-
-static void magician_ext_control(struct snd_soc_dapm_context *dapm)
-{
-
- snd_soc_dapm_mutex_lock(dapm);
-
- if (magician_spk_switch)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
- if (magician_hp_switch)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
-
- switch (magician_in_sel) {
- case MAGICIAN_MIC:
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic");
- break;
- case MAGICIAN_MIC_EXT:
- snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic");
- break;
- }
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-}
-
-static int magician_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* check the jack status at stream startup */
- magician_ext_control(&rtd->card->dapm);
-
- return 0;
-}
-
-/*
- * Magician uses SSP port for playback.
- */
-static int magician_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int acps, acds, width;
- unsigned int div4 = PXA_SSP_CLK_SCDB_4;
- int ret = 0;
-
- width = snd_pcm_format_physical_width(params_format(params));
-
- /*
- * rate = SSPSCLK / (2 * width(16 or 32))
- * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1)
- */
- switch (params_rate(params)) {
- case 8000:
- /* off by a factor of 2: bug in the PXA27x audio clock? */
- acps = 32842000;
- switch (width) {
- case 16:
- /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_16;
- break;
- default: /* 32 */
- /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_8;
- }
- break;
- case 11025:
- acps = 5622000;
- switch (width) {
- case 16:
- /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_4;
- break;
- default: /* 32 */
- /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_2;
- }
- break;
- case 22050:
- acps = 5622000;
- switch (width) {
- case 16:
- /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_2;
- break;
- default: /* 32 */
- /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_1;
- }
- break;
- case 44100:
- acps = 5622000;
- switch (width) {
- case 16:
- /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_2;
- break;
- default: /* 32 */
- /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_1;
- }
- break;
- case 48000:
- acps = 12235000;
- switch (width) {
- case 16:
- /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_2;
- break;
- default: /* 32 */
- /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_1;
- }
- break;
- case 96000:
- default:
- acps = 12235000;
- switch (width) {
- case 16:
- /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_1;
- break;
- default: /* 32 */
- /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */
- acds = PXA_SSP_CLK_AUDIO_DIV_2;
- div4 = PXA_SSP_CLK_SCDB_1;
- break;
- }
- break;
- }
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
- if (ret < 0)
- return ret;
-
- /* set audio clock as clock source */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- /* set the SSP audio system clock ACDS divider */
- ret = snd_soc_dai_set_clkdiv(cpu_dai,
- PXA_SSP_AUDIO_DIV_ACDS, acds);
- if (ret < 0)
- return ret;
-
- /* set the SSP audio system clock SCDB divider4 */
- ret = snd_soc_dai_set_clkdiv(cpu_dai,
- PXA_SSP_AUDIO_DIV_SCDB, div4);
- if (ret < 0)
- return ret;
-
- /* set SSP audio pll clock */
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-/*
- * Magician uses I2S for capture.
- */
-static int magician_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret = 0;
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /* set the I2S system clock as output */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops magician_capture_ops = {
- .startup = magician_startup,
- .hw_params = magician_capture_hw_params,
-};
-
-static struct snd_soc_ops magician_playback_ops = {
- .startup = magician_startup,
- .hw_params = magician_playback_hw_params,
-};
-
-static int magician_get_hp(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = magician_hp_switch;
- return 0;
-}
-
-static int magician_set_hp(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (magician_hp_switch == ucontrol->value.integer.value[0])
- return 0;
-
- magician_hp_switch = ucontrol->value.integer.value[0];
- magician_ext_control(&card->dapm);
- return 1;
-}
-
-static int magician_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = magician_spk_switch;
- return 0;
-}
-
-static int magician_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (magician_spk_switch == ucontrol->value.integer.value[0])
- return 0;
-
- magician_spk_switch = ucontrol->value.integer.value[0];
- magician_ext_control(&card->dapm);
- return 1;
-}
-
-static int magician_get_input(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = magician_in_sel;
- return 0;
-}
-
-static int magician_set_input(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- if (magician_in_sel == ucontrol->value.integer.value[0])
- return 0;
-
- magician_in_sel = ucontrol->value.integer.value[0];
-
- switch (magician_in_sel) {
- case MAGICIAN_MIC:
- gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1);
- break;
- case MAGICIAN_MIC_EXT:
- gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0);
- }
-
- return 1;
-}
-
-static int magician_spk_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-static int magician_hp_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-static int magician_mic_bias(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event));
- return 0;
-}
-
-/* magician machine dapm widgets */
-static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
- SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
- SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
- SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
-};
-
-/* magician machine audio_map */
-static const struct snd_soc_dapm_route audio_map[] = {
-
- /* Headphone connected to VOUTL, VOUTR */
- {"Headphone Jack", NULL, "VOUTL"},
- {"Headphone Jack", NULL, "VOUTR"},
-
- /* Speaker connected to VOUTL, VOUTR */
- {"Speaker", NULL, "VOUTL"},
- {"Speaker", NULL, "VOUTR"},
-
- /* Mics are connected to VINM */
- {"VINM", NULL, "Headset Mic"},
- {"VINM", NULL, "Call Mic"},
-};
-
-static const char *input_select[] = {"Call Mic", "Headset Mic"};
-static const struct soc_enum magician_in_sel_enum =
- SOC_ENUM_SINGLE_EXT(2, input_select);
-
-static const struct snd_kcontrol_new uda1380_magician_controls[] = {
- SOC_SINGLE_BOOL_EXT("Headphone Switch",
- (unsigned long)&magician_hp_switch,
- magician_get_hp, magician_set_hp),
- SOC_SINGLE_BOOL_EXT("Speaker Switch",
- (unsigned long)&magician_spk_switch,
- magician_get_spk, magician_set_spk),
- SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
- magician_get_input, magician_set_input),
-};
-
-/* magician digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link magician_dai[] = {
-{
- .name = "uda1380",
- .stream_name = "UDA1380 Playback",
- .cpu_dai_name = "pxa-ssp-dai.0",
- .codec_dai_name = "uda1380-hifi-playback",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "uda1380-codec.0-0018",
- .ops = &magician_playback_ops,
-},
-{
- .name = "uda1380",
- .stream_name = "UDA1380 Capture",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "uda1380-hifi-capture",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "uda1380-codec.0-0018",
- .ops = &magician_capture_ops,
-}
-};
-
-/* magician audio machine driver */
-static struct snd_soc_card snd_soc_card_magician = {
- .name = "Magician",
- .owner = THIS_MODULE,
- .dai_link = magician_dai,
- .num_links = ARRAY_SIZE(magician_dai),
-
- .controls = uda1380_magician_controls,
- .num_controls = ARRAY_SIZE(uda1380_magician_controls),
- .dapm_widgets = uda1380_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static struct platform_device *magician_snd_device;
-
-/*
- * FIXME: move into magician board file once merged into the pxa tree
- */
-static struct uda1380_platform_data uda1380_info = {
- .gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
- .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
- .dac_clk = UDA1380_DAC_CLK_WSPLL,
-};
-
-static struct i2c_board_info i2c_board_info[] = {
- {
- I2C_BOARD_INFO("uda1380", 0x18),
- .platform_data = &uda1380_info,
- },
-};
-
-static int __init magician_init(void)
-{
- int ret;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
-
- if (!machine_is_magician())
- return -ENODEV;
-
- adapter = i2c_get_adapter(0);
- if (!adapter)
- return -ENODEV;
- client = i2c_new_device(adapter, i2c_board_info);
- i2c_put_adapter(adapter);
- if (!client)
- return -ENODEV;
-
- ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
- if (ret)
- goto err_request_spk;
- ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER");
- if (ret)
- goto err_request_ep;
- ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER");
- if (ret)
- goto err_request_mic;
- ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0");
- if (ret)
- goto err_request_in_sel0;
- ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1");
- if (ret)
- goto err_request_in_sel1;
-
- gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
-
- magician_snd_device = platform_device_alloc("soc-audio", -1);
- if (!magician_snd_device) {
- ret = -ENOMEM;
- goto err_pdev;
- }
-
- platform_set_drvdata(magician_snd_device, &snd_soc_card_magician);
- ret = platform_device_add(magician_snd_device);
- if (ret) {
- platform_device_put(magician_snd_device);
- goto err_pdev;
- }
-
- return 0;
-
-err_pdev:
- gpio_free(EGPIO_MAGICIAN_IN_SEL1);
-err_request_in_sel1:
- gpio_free(EGPIO_MAGICIAN_IN_SEL0);
-err_request_in_sel0:
- gpio_free(EGPIO_MAGICIAN_MIC_POWER);
-err_request_mic:
- gpio_free(EGPIO_MAGICIAN_EP_POWER);
-err_request_ep:
- gpio_free(EGPIO_MAGICIAN_SPK_POWER);
-err_request_spk:
- return ret;
-}
-
-static void __exit magician_exit(void)
-{
- platform_device_unregister(magician_snd_device);
-
- gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
- gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
- gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
-
- gpio_free(EGPIO_MAGICIAN_IN_SEL1);
- gpio_free(EGPIO_MAGICIAN_IN_SEL0);
- gpio_free(EGPIO_MAGICIAN_MIC_POWER);
- gpio_free(EGPIO_MAGICIAN_EP_POWER);
- gpio_free(EGPIO_MAGICIAN_SPK_POWER);
-}
-
-module_init(magician_init);
-module_exit(magician_exit);
-
-MODULE_AUTHOR("Philipp Zabel");
-MODULE_DESCRIPTION("ALSA SoC Magician");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
deleted file mode 100644
index 5c8f9db50a47..000000000000
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Handles the Mitac mioa701 SoC system
- *
- * Copyright (C) 2008 Robert Jarzmik
- *
- * 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 in 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
- *
- * This is a little schema of the sound interconnections :
- *
- * Sagem X200 Wolfson WM9713
- * +--------+ +-------------------+ Rear Speaker
- * | | | | /-+
- * | +--->----->---+MONOIN SPKL+--->----+-+ |
- * | GSM | | | | | |
- * | +--->----->---+PCBEEP SPKR+--->----+-+ |
- * | CHIP | | | \-+
- * | +---<-----<---+MONO |
- * | | | | Front Speaker
- * +--------+ | | /-+
- * | HPL+--->----+-+ |
- * | | | | |
- * | OUT3+--->----+-+ |
- * | | \-+
- * | |
- * | | Front Micro
- * | | +
- * | MIC1+-----<--+o+
- * | | +
- * +-------------------+ ---
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-
-#include <asm/mach-types.h>
-#include <mach/audio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-#include <sound/ac97_codec.h>
-
-#include "pxa2xx-ac97.h"
-#include "../codecs/wm9713.h"
-
-#define AC97_GPIO_PULL 0x58
-
-/* Use GPIO8 for rear speaker amplifier */
-static int rear_amp_power(struct snd_soc_codec *codec, int power)
-{
- unsigned short reg;
-
- if (power) {
- reg = snd_soc_read(codec, AC97_GPIO_CFG);
- snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100);
- reg = snd_soc_read(codec, AC97_GPIO_PULL);
- snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15));
- } else {
- reg = snd_soc_read(codec, AC97_GPIO_CFG);
- snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100);
- reg = snd_soc_read(codec, AC97_GPIO_PULL);
- snd_soc_write(codec, AC97_GPIO_PULL, reg & ~(1<<15));
- }
-
- return 0;
-}
-
-static int rear_amp_event(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kctl, int event)
-{
- struct snd_soc_card *card = widget->dapm->card;
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_codec *codec;
-
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec = rtd->codec;
- return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event));
-}
-
-/* mioa701 machine dapm widgets */
-static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Front Speaker", NULL),
- SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event),
- SND_SOC_DAPM_MIC("Headset", NULL),
- SND_SOC_DAPM_LINE("GSM Line Out", NULL),
- SND_SOC_DAPM_LINE("GSM Line In", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Front Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Call Mic */
- {"Mic Bias", NULL, "Front Mic"},
- {"MIC1", NULL, "Mic Bias"},
-
- /* Headset Mic */
- {"LINEL", NULL, "Headset Mic"},
- {"LINER", NULL, "Headset Mic"},
-
- /* GSM Module */
- {"MONOIN", NULL, "GSM Line Out"},
- {"PCBEEP", NULL, "GSM Line Out"},
- {"GSM Line In", NULL, "MONO"},
-
- /* headphone connected to HPL, HPR */
- {"Headset", NULL, "HPL"},
- {"Headset", NULL, "HPR"},
-
- /* front speaker connected to HPL, OUT3 */
- {"Front Speaker", NULL, "HPL"},
- {"Front Speaker", NULL, "OUT3"},
-
- /* rear speaker connected to SPKL, SPKR */
- {"Rear Speaker", NULL, "SPKL"},
- {"Rear Speaker", NULL, "SPKR"},
-};
-
-static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
-
- /* Prepare GPIO8 for rear speaker amplifier */
- snd_soc_update_bits(codec, AC97_GPIO_CFG, 0x100, 0x100);
-
- /* Prepare MIC input */
- snd_soc_update_bits(codec, AC97_3D_CONTROL, 0xc000, 0xc000);
-
- return 0;
-}
-
-static struct snd_soc_ops mioa701_ops;
-
-static struct snd_soc_dai_link mioa701_dai[] = {
- {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9713-hifi",
- .codec_name = "wm9713-codec",
- .init = mioa701_wm9713_init,
- .platform_name = "pxa-pcm-audio",
- .ops = &mioa701_ops,
- },
- {
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9713-aux",
- .codec_name = "wm9713-codec",
- .platform_name = "pxa-pcm-audio",
- .ops = &mioa701_ops,
- },
-};
-
-static struct snd_soc_card mioa701 = {
- .name = "MioA701",
- .owner = THIS_MODULE,
- .dai_link = mioa701_dai,
- .num_links = ARRAY_SIZE(mioa701_dai),
-
- .dapm_widgets = mioa701_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mioa701_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static int mioa701_wm9713_probe(struct platform_device *pdev)
-{
- int rc;
-
- if (!machine_is_mioa701())
- return -ENODEV;
-
- mioa701.dev = &pdev->dev;
- rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
- if (!rc)
- dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
- "lead to overheating and possible destruction of your device."
- " Do not use without a good knowledge of mio's board design!\n");
- return rc;
-}
-
-static struct platform_driver mioa701_wm9713_driver = {
- .probe = mioa701_wm9713_probe,
- .driver = {
- .name = "mioa701-wm9713",
- .pm = &snd_soc_pm_ops,
- },
-};
-
-module_platform_driver(mioa701_wm9713_driver);
-
-/* Module information */
-MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)");
-MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
deleted file mode 100644
index 51e790d006f5..000000000000
--- a/sound/soc/pxa/mmp-pcm.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * linux/sound/soc/pxa/mmp-pcm.c
- *
- * Copyright (C) 2011 Marvell International 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/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/platform_data/dma-mmp_tdma.h>
-#include <linux/platform_data/mmp_audio.h>
-
-#include <sound/pxa2xx-lib.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-struct mmp_dma_data {
- int ssp_id;
- struct resource *dma_res;
-};
-
-#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
- SNDRV_PCM_INFO_MMAP_VALID | \
- SNDRV_PCM_INFO_INTERLEAVED | \
- SNDRV_PCM_INFO_PAUSE | \
- SNDRV_PCM_INFO_RESUME | \
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
-
-static struct snd_pcm_hardware mmp_pcm_hardware[] = {
- {
- .info = MMP_PCM_INFO,
- .period_bytes_min = 1024,
- .period_bytes_max = 2048,
- .periods_min = 2,
- .periods_max = 32,
- .buffer_bytes_max = 4096,
- .fifo_size = 32,
- },
- {
- .info = MMP_PCM_INFO,
- .period_bytes_min = 1024,
- .period_bytes_max = 2048,
- .periods_min = 2,
- .periods_max = 32,
- .buffer_bytes_max = 4096,
- .fifo_size = 32,
- },
-};
-
-static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
- struct dma_slave_config slave_config;
- int ret;
-
- ret =
- snd_dmaengine_pcm_prepare_slave_config(substream, params,
- &slave_config);
- if (ret)
- return ret;
-
- ret = dmaengine_slave_config(chan, &slave_config);
- if (ret)
- return ret;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
- return 0;
-}
-
-static bool filter(struct dma_chan *chan, void *param)
-{
- struct mmp_dma_data *dma_data = param;
- bool found = false;
- char *devname;
-
- devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
- dma_data->ssp_id);
- if ((strcmp(dev_name(chan->device->dev), devname) == 0) &&
- (chan->chan_id == dma_data->dma_res->start)) {
- found = true;
- }
-
- kfree(devname);
- return found;
-}
-
-static int mmp_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct platform_device *pdev = to_platform_device(rtd->platform->dev);
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct mmp_dma_data dma_data;
- struct resource *r;
-
- r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
- if (!r)
- return -EBUSY;
-
- snd_soc_set_runtime_hwparams(substream,
- &mmp_pcm_hardware[substream->stream]);
-
- dma_data.dma_res = r;
- dma_data.ssp_id = cpu_dai->id;
-
- return snd_dmaengine_pcm_open_request_chan(substream, filter,
- &dma_data);
-}
-
-static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long off = vma->vm_pgoff;
-
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- return remap_pfn_range(vma, vma->vm_start,
- __phys_to_pfn(runtime->dma_addr) + off,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-static struct snd_pcm_ops mmp_pcm_ops = {
- .open = mmp_pcm_open,
- .close = snd_dmaengine_pcm_close_release_chan,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = mmp_pcm_hw_params,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer,
- .mmap = mmp_pcm_mmap,
-};
-
-static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
- struct gen_pool *gpool;
-
- gpool = sram_get_gpool("asram");
- if (!gpool)
- return;
-
- for (stream = 0; stream < 2; stream++) {
- size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
-
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- gen_pool_free(gpool, (unsigned long)buf->area, size);
- buf->area = NULL;
- }
-
- return;
-}
-
-static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
- int stream)
-{
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
- struct gen_pool *gpool;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = substream->pcm->card->dev;
- buf->private_data = NULL;
-
- gpool = sram_get_gpool("asram");
- if (!gpool)
- return -ENOMEM;
-
- buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
-}
-
-static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_pcm_substream *substream;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0, stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
-
- ret = mmp_pcm_preallocate_dma_buffer(substream, stream);
- if (ret)
- goto err;
- }
-
- return 0;
-
-err:
- mmp_pcm_free_dma_buffers(pcm);
- return ret;
-}
-
-static struct snd_soc_platform_driver mmp_soc_platform = {
- .ops = &mmp_pcm_ops,
- .pcm_new = mmp_pcm_new,
- .pcm_free = mmp_pcm_free_dma_buffers,
-};
-
-static int mmp_pcm_probe(struct platform_device *pdev)
-{
- struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
-
- if (pdata) {
- mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
- pdata->buffer_max_playback;
- mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
- pdata->period_max_playback;
- mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
- pdata->buffer_max_capture;
- mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
- pdata->period_max_capture;
- }
- return devm_snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
-}
-
-static struct platform_driver mmp_pcm_driver = {
- .driver = {
- .name = "mmp-pcm-audio",
- },
-
- .probe = mmp_pcm_probe,
-};
-
-module_platform_driver(mmp_pcm_driver);
-
-MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
-MODULE_DESCRIPTION("MMP Soc Audio DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index eca60c29791a..73f36c9dd35c 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* linux/sound/soc/pxa/mmp-sspa.c
* Base on pxa2xx-ssp.c
*
* Copyright (C) 2011 Marvell International 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.
- *
- * 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/init.h>
#include <linux/module.h>
@@ -25,9 +11,9 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/pxa2xx_ssp.h>
#include <linux/io.h>
#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -42,71 +28,65 @@
* SSPA audio private data
*/
struct sspa_priv {
- struct ssp_device *sspa;
- struct snd_dmaengine_dai_dma_data *dma_params;
+ void __iomem *tx_base;
+ void __iomem *rx_base;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct clk *clk;
struct clk *audio_clk;
struct clk *sysclk;
- int dai_fmt;
+
int running_cnt;
+ u32 sp;
+ u32 ctrl;
};
-static void mmp_sspa_write_reg(struct ssp_device *sspa, u32 reg, u32 val)
+static void mmp_sspa_tx_enable(struct sspa_priv *sspa)
{
- __raw_writel(val, sspa->mmio_base + reg);
-}
+ unsigned int sspa_sp = sspa->sp;
-static u32 mmp_sspa_read_reg(struct ssp_device *sspa, u32 reg)
-{
- return __raw_readl(sspa->mmio_base + reg);
-}
-
-static void mmp_sspa_tx_enable(struct ssp_device *sspa)
-{
- unsigned int sspa_sp;
-
- sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP);
+ sspa_sp &= ~SSPA_SP_MSL;
sspa_sp |= SSPA_SP_S_EN;
sspa_sp |= SSPA_SP_WEN;
- mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+ __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP);
}
-static void mmp_sspa_tx_disable(struct ssp_device *sspa)
+static void mmp_sspa_tx_disable(struct sspa_priv *sspa)
{
- unsigned int sspa_sp;
+ unsigned int sspa_sp = sspa->sp;
- sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP);
+ sspa_sp &= ~SSPA_SP_MSL;
sspa_sp &= ~SSPA_SP_S_EN;
sspa_sp |= SSPA_SP_WEN;
- mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+ __raw_writel(sspa_sp, sspa->tx_base + SSPA_SP);
}
-static void mmp_sspa_rx_enable(struct ssp_device *sspa)
+static void mmp_sspa_rx_enable(struct sspa_priv *sspa)
{
- unsigned int sspa_sp;
+ unsigned int sspa_sp = sspa->sp;
- sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP);
sspa_sp |= SSPA_SP_S_EN;
sspa_sp |= SSPA_SP_WEN;
- mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+ __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP);
}
-static void mmp_sspa_rx_disable(struct ssp_device *sspa)
+static void mmp_sspa_rx_disable(struct sspa_priv *sspa)
{
- unsigned int sspa_sp;
+ unsigned int sspa_sp = sspa->sp;
- sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP);
sspa_sp &= ~SSPA_SP_S_EN;
sspa_sp |= SSPA_SP_WEN;
- mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+ __raw_writel(sspa_sp, sspa->rx_base + SSPA_SP);
}
static int mmp_sspa_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
- clk_enable(priv->sysclk);
- clk_enable(priv->sspa->clk);
+ clk_prepare_enable(sspa->sysclk);
+ clk_prepare_enable(sspa->clk);
return 0;
}
@@ -114,12 +94,10 @@ static int mmp_sspa_startup(struct snd_pcm_substream *substream,
static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai);
-
- clk_disable(priv->sspa->clk);
- clk_disable(priv->sysclk);
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
- return;
+ clk_disable_unprepare(sspa->clk);
+ clk_disable_unprepare(sspa->sysclk);
}
/*
@@ -128,12 +106,16 @@ static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
- struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = cpu_dai->component->dev;
int ret = 0;
+ if (dev->of_node)
+ return -ENOTSUPP;
+
switch (clk_id) {
case MMP_SSPA_CLK_AUDIO:
- ret = clk_set_rate(priv->audio_clk, freq);
+ ret = clk_set_rate(sspa->audio_clk, freq);
if (ret)
return ret;
break;
@@ -152,17 +134,21 @@ static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
- struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = cpu_dai->component->dev;
int ret = 0;
+ if (dev->of_node)
+ return -ENOTSUPP;
+
switch (pll_id) {
case MMP_SYSCLK:
- ret = clk_set_rate(priv->sysclk, freq_out);
+ ret = clk_set_rate(sspa->sysclk, freq_out);
if (ret)
return ret;
break;
case MMP_SSPA_CLK:
- ret = clk_set_rate(priv->sspa->clk, freq_out);
+ ret = clk_set_rate(sspa->clk, freq_out);
if (ret)
return ret;
break;
@@ -174,38 +160,22 @@ static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
}
/*
- * Set up the sspa dai format. The sspa port must be inactive
- * before calling this function as the physical
- * interface format is changed.
+ * Set up the sspa dai format.
*/
static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai);
- struct ssp_device *sspa = sspa_priv->sspa;
- u32 sspa_sp, sspa_ctrl;
-
- /* check if we need to change anything at all */
- if (sspa_priv->dai_fmt == fmt)
- return 0;
-
- /* we can only change the settings if the port is not in use */
- if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) ||
- (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) {
- dev_err(&sspa->pdev->dev,
- "can't change hardware dai format: stream is in use\n");
- return -EINVAL;
- }
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);
/* reset port settings */
- sspa_sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;
- sspa_ctrl = 0;
+ sspa->sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;
+ sspa->ctrl = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- sspa_sp |= SSPA_SP_MSL;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ sspa->sp |= SSPA_SP_MSL;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_BC_FC:
break;
default:
return -EINVAL;
@@ -213,7 +183,7 @@ static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
- sspa_sp |= SSPA_SP_FSP;
+ sspa->sp |= SSPA_SP_FSP;
break;
default:
return -EINVAL;
@@ -221,39 +191,16 @@ static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- sspa_sp |= SSPA_TXSP_FPER(63);
- sspa_sp |= SSPA_SP_FWID(31);
- sspa_ctrl |= SSPA_CTL_XDATDLY(1);
+ sspa->ctrl |= SSPA_CTL_XDATDLY(1);
break;
default:
return -EINVAL;
}
- mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
- mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
-
- sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);
- mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
- mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
-
- /*
- * FIXME: hw issue, for the tx serial port,
- * can not config the master/slave mode;
- * so must clean this bit.
- * The master/slave mode has been set in the
- * rx port.
- */
- sspa_sp &= ~SSPA_SP_MSL;
- mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
-
- mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
- mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
-
/* Since we are configuring the timings for the format by hand
* we have to defer some things until hw_params() where we
* know parameters like the sample size.
*/
- sspa_priv->dai_fmt = fmt;
return 0;
}
@@ -265,65 +212,75 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
- struct ssp_device *sspa = sspa_priv->sspa;
- struct snd_dmaengine_dai_dma_data *dma_params;
- u32 sspa_ctrl;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL);
- else
- sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL);
-
- sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK;
- sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1);
- sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
- sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS);
- sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = dai->component->dev;
+ u32 sspa_ctrl = sspa->ctrl;
+ int bits;
+ int bitval;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
- sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS);
+ bits = 8;
+ bitval = SSPA_CTL_8_BITS;
break;
case SNDRV_PCM_FORMAT_S16_LE:
- sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS);
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_20_BITS);
+ bits = 16;
+ bitval = SSPA_CTL_16_BITS;
break;
case SNDRV_PCM_FORMAT_S24_3LE:
- sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS);
+ bits = 24;
+ bitval = SSPA_CTL_24_BITS;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS);
+ bits = 32;
+ bitval = SSPA_CTL_32_BITS;
break;
default:
return -EINVAL;
}
+ sspa_ctrl &= ~SSPA_CTL_XPH;
+ if (dev->of_node || params_channels(params) == 2)
+ sspa_ctrl |= SSPA_CTL_XPH;
+
+ sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
+ sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval);
+
+ sspa_ctrl &= ~SSPA_CTL_XWDLEN2_MASK;
+ sspa_ctrl |= SSPA_CTL_XWDLEN2(bitval);
+
+ sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;
+ sspa_ctrl |= SSPA_CTL_XSSZ1(bitval);
+
+ sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK;
+ sspa_ctrl |= SSPA_CTL_XSSZ2(bitval);
+
+ sspa->sp &= ~SSPA_SP_FWID_MASK;
+ sspa->sp |= SSPA_SP_FWID(bits - 1);
+
+ sspa->sp &= ~SSPA_TXSP_FPER_MASK;
+ sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1);
+
+ if (dev->of_node) {
+ clk_set_rate(sspa->clk, params_rate(params) *
+ params_channels(params) * bits);
+ }
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
- mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1);
+ __raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL);
+ __raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL);
} else {
- mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
- mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0);
+ __raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL);
+ __raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL);
}
- dma_params = &sspa_priv->dma_params[substream->stream];
- dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- (sspa->phys_base + SSPA_TXD) :
- (sspa->phys_base + SSPA_RXD);
- snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params);
return 0;
}
static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
- struct ssp_device *sspa = sspa_priv->sspa;
+ struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
int ret = 0;
switch (cmd) {
@@ -336,25 +293,25 @@ static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,
* enabled or not; if has been enabled by another
* stream, do not enable again.
*/
- if (!sspa_priv->running_cnt)
+ if (!sspa->running_cnt)
mmp_sspa_rx_enable(sspa);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
mmp_sspa_tx_enable(sspa);
- sspa_priv->running_cnt++;
+ sspa->running_cnt++;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- sspa_priv->running_cnt--;
+ sspa->running_cnt--;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
mmp_sspa_tx_disable(sspa);
/* have no capture stream, disable rx port */
- if (!sspa_priv->running_cnt)
+ if (!sspa->running_cnt)
mmp_sspa_rx_disable(sspa);
break;
@@ -367,21 +324,23 @@ static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,
static int mmp_sspa_probe(struct snd_soc_dai *dai)
{
- struct sspa_priv *priv = dev_get_drvdata(dai->dev);
+ struct sspa_priv *sspa = dev_get_drvdata(dai->dev);
- snd_soc_dai_set_drvdata(dai, priv);
- return 0;
+ snd_soc_dai_init_dma_data(dai,
+ &sspa->playback_dma_data,
+ &sspa->capture_dma_data);
+ return 0;
}
#define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000
#define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops mmp_sspa_dai_ops = {
+static const struct snd_soc_dai_ops mmp_sspa_dai_ops = {
+ .probe = mmp_sspa_probe,
.startup = mmp_sspa_startup,
.shutdown = mmp_sspa_shutdown,
.trigger = mmp_sspa_trigger,
@@ -392,7 +351,6 @@ static struct snd_soc_dai_ops mmp_sspa_dai_ops = {
};
static struct snd_soc_dai_driver mmp_sspa_dai = {
- .probe = mmp_sspa_probe,
.playback = {
.channels_min = 1,
.channels_max = 128,
@@ -408,70 +366,212 @@ static struct snd_soc_dai_driver mmp_sspa_dai = {
.ops = &mmp_sspa_dai_ops,
};
+#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
+ SNDRV_PCM_INFO_MMAP_VALID | \
+ SNDRV_PCM_INFO_INTERLEAVED | \
+ SNDRV_PCM_INFO_PAUSE | \
+ SNDRV_PCM_INFO_RESUME | \
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
+
+static const struct snd_pcm_hardware mmp_pcm_hardware[] = {
+ {
+ .info = MMP_PCM_INFO,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 2048,
+ .periods_min = 2,
+ .periods_max = 32,
+ .buffer_bytes_max = 4096,
+ .fifo_size = 32,
+ },
+ {
+ .info = MMP_PCM_INFO,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 2048,
+ .periods_min = 2,
+ .periods_max = 32,
+ .buffer_bytes_max = 4096,
+ .fifo_size = 32,
+ },
+};
+
+static const struct snd_dmaengine_pcm_config mmp_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .pcm_hardware = mmp_pcm_hardware,
+ .prealloc_buffer_size = 4096,
+};
+
+static int mmp_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static int mmp_sspa_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sspa_priv *sspa = snd_soc_component_get_drvdata(component);
+
+ pm_runtime_get_sync(component->dev);
+
+ /* we can only change the settings if the port is not in use */
+ if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) ||
+ (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) {
+ dev_err(component->dev,
+ "can't change hardware dai format: stream is in use\n");
+ return -EBUSY;
+ }
+
+ __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP);
+ __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP);
+
+ sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);
+ __raw_writel(sspa->sp, sspa->tx_base + SSPA_SP);
+ __raw_writel(sspa->sp, sspa->rx_base + SSPA_SP);
+
+ /*
+ * FIXME: hw issue, for the tx serial port,
+ * can not config the master/slave mode;
+ * so must clean this bit.
+ * The master/slave mode has been set in the
+ * rx port.
+ */
+ __raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP);
+
+ __raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL);
+ __raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL);
+
+ return 0;
+}
+
+static int mmp_sspa_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ pm_runtime_put_sync(component->dev);
+ return 0;
+}
+
static const struct snd_soc_component_driver mmp_sspa_component = {
- .name = "mmp-sspa",
+ .name = "mmp-sspa",
+ .mmap = mmp_pcm_mmap,
+ .open = mmp_sspa_open,
+ .close = mmp_sspa_close,
+ .legacy_dai_naming = 1,
};
static int asoc_mmp_sspa_probe(struct platform_device *pdev)
{
- struct sspa_priv *priv;
- struct resource *res;
+ struct sspa_priv *sspa;
+ int ret;
- priv = devm_kzalloc(&pdev->dev,
+ sspa = devm_kzalloc(&pdev->dev,
sizeof(struct sspa_priv), GFP_KERNEL);
- if (!priv)
+ if (!sspa)
return -ENOMEM;
- priv->sspa = devm_kzalloc(&pdev->dev,
- sizeof(struct ssp_device), GFP_KERNEL);
- if (priv->sspa == NULL)
- return -ENOMEM;
+ if (pdev->dev.of_node) {
+ sspa->rx_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(sspa->rx_base))
+ return PTR_ERR(sspa->rx_base);
- priv->dma_params = devm_kzalloc(&pdev->dev,
- 2 * sizeof(struct snd_dmaengine_dai_dma_data),
- GFP_KERNEL);
- if (priv->dma_params == NULL)
- return -ENOMEM;
+ sspa->tx_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(sspa->tx_base))
+ return PTR_ERR(sspa->tx_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(priv->sspa->mmio_base))
- return PTR_ERR(priv->sspa->mmio_base);
+ sspa->clk = devm_clk_get(&pdev->dev, "bitclk");
+ if (IS_ERR(sspa->clk))
+ return PTR_ERR(sspa->clk);
- priv->sspa->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(priv->sspa->clk))
- return PTR_ERR(priv->sspa->clk);
+ sspa->audio_clk = devm_clk_get(&pdev->dev, "audio");
+ if (IS_ERR(sspa->audio_clk))
+ return PTR_ERR(sspa->audio_clk);
+ } else {
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL)
+ return -ENODEV;
+
+ sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30);
+ if (!sspa->rx_base)
+ return -ENOMEM;
+
+ sspa->tx_base = devm_ioremap(&pdev->dev,
+ res->start + 0x80, 0x30);
+ if (!sspa->tx_base)
+ return -ENOMEM;
+
+ sspa->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sspa->clk))
+ return PTR_ERR(sspa->clk);
+
+ sspa->audio_clk = clk_get(NULL, "mmp-audio");
+ if (IS_ERR(sspa->audio_clk))
+ return PTR_ERR(sspa->audio_clk);
+
+ sspa->sysclk = clk_get(NULL, "mmp-sysclk");
+ if (IS_ERR(sspa->sysclk)) {
+ clk_put(sspa->audio_clk);
+ return PTR_ERR(sspa->sysclk);
+ }
+ }
+ platform_set_drvdata(pdev, sspa);
- priv->audio_clk = clk_get(NULL, "mmp-audio");
- if (IS_ERR(priv->audio_clk))
- return PTR_ERR(priv->audio_clk);
+ sspa->playback_dma_data.maxburst = 4;
+ sspa->capture_dma_data.maxburst = 4;
+ /* You know, these addresses are actually ignored. */
+ sspa->capture_dma_data.addr = SSPA_D;
+ sspa->playback_dma_data.addr = 0x80 + SSPA_D;
- priv->sysclk = clk_get(NULL, "mmp-sysclk");
- if (IS_ERR(priv->sysclk)) {
- clk_put(priv->audio_clk);
- return PTR_ERR(priv->sysclk);
+ if (pdev->dev.of_node) {
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+ &mmp_pcm_config, 0);
+ if (ret)
+ return ret;
}
- clk_enable(priv->audio_clk);
- priv->dai_fmt = (unsigned int) -1;
- platform_set_drvdata(pdev, priv);
- return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component,
- &mmp_sspa_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component,
+ &mmp_sspa_dai, 1);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+ clk_prepare_enable(sspa->audio_clk);
+
+ return 0;
}
-static int asoc_mmp_sspa_remove(struct platform_device *pdev)
+static void asoc_mmp_sspa_remove(struct platform_device *pdev)
{
- struct sspa_priv *priv = platform_get_drvdata(pdev);
+ struct sspa_priv *sspa = platform_get_drvdata(pdev);
- clk_disable(priv->audio_clk);
- clk_put(priv->audio_clk);
- clk_put(priv->sysclk);
- return 0;
+ clk_disable_unprepare(sspa->audio_clk);
+ pm_runtime_disable(&pdev->dev);
+
+ if (pdev->dev.of_node)
+ return;
+
+ clk_put(sspa->audio_clk);
+ clk_put(sspa->sysclk);
}
+#ifdef CONFIG_OF
+static const struct of_device_id mmp_sspa_of_match[] = {
+ { .compatible = "marvell,mmp-sspa" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, mmp_sspa_of_match);
+#endif
+
static struct platform_driver asoc_mmp_sspa_driver = {
.driver = {
.name = "mmp-sspa-dai",
+ .of_match_table = of_match_ptr(mmp_sspa_of_match),
},
.probe = asoc_mmp_sspa_probe,
.remove = asoc_mmp_sspa_remove,
@@ -482,3 +582,4 @@ module_platform_driver(asoc_mmp_sspa_driver);
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
MODULE_DESCRIPTION("MMP SSPA SoC Interface");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mmp-sspa-dai");
diff --git a/sound/soc/pxa/mmp-sspa.h b/sound/soc/pxa/mmp-sspa.h
index ea365cb9e784..938ef2f667e3 100644
--- a/sound/soc/pxa/mmp-sspa.h
+++ b/sound/soc/pxa/mmp-sspa.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* linux/sound/soc/pxa/mmp-sspa.h
*
* Copyright (C) 2011 Marvell International 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.
- *
- * 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 _MMP_SSPA_H
#define _MMP_SSPA_H
@@ -24,25 +10,15 @@
/*
* SSPA Registers
*/
-#define SSPA_RXD (0x00)
-#define SSPA_RXID (0x04)
-#define SSPA_RXCTL (0x08)
-#define SSPA_RXSP (0x0c)
-#define SSPA_RXFIFO_UL (0x10)
-#define SSPA_RXINT_MASK (0x14)
-#define SSPA_RXC (0x18)
-#define SSPA_RXFIFO_NOFS (0x1c)
-#define SSPA_RXFIFO_SIZE (0x20)
-
-#define SSPA_TXD (0x80)
-#define SSPA_TXID (0x84)
-#define SSPA_TXCTL (0x88)
-#define SSPA_TXSP (0x8c)
-#define SSPA_TXFIFO_LL (0x90)
-#define SSPA_TXINT_MASK (0x94)
-#define SSPA_TXC (0x98)
-#define SSPA_TXFIFO_NOFS (0x9c)
-#define SSPA_TXFIFO_SIZE (0xa0)
+#define SSPA_D (0x00)
+#define SSPA_ID (0x04)
+#define SSPA_CTL (0x08)
+#define SSPA_SP (0x0c)
+#define SSPA_FIFO_UL (0x10)
+#define SSPA_INT_MASK (0x14)
+#define SSPA_C (0x18)
+#define SSPA_FIFO_NOFS (0x1c)
+#define SSPA_FIFO_SIZE (0x20)
/* SSPA Control Register */
#define SSPA_CTL_XPH (1 << 31) /* Read Phase */
@@ -52,7 +28,7 @@
#define SSPA_CTL_XFRLEN2(x) ((x) << 24) /* Transmit Frame Length in Phase 2 */
#define SSPA_CTL_XWDLEN2_MASK (7 << 21)
#define SSPA_CTL_XWDLEN2(x) ((x) << 21) /* Transmit Word Length in Phase 2 */
-#define SSPA_CTL_XDATDLY(x) ((x) << 19) /* Tansmit Data Delay */
+#define SSPA_CTL_XDATDLY(x) ((x) << 19) /* Transmit Data Delay */
#define SSPA_CTL_XSSZ2_MASK (7 << 16)
#define SSPA_CTL_XSSZ2(x) ((x) << 16) /* Transmit Sample Audio Size */
#define SSPA_CTL_XFRLEN1_MASK (7 << 8)
@@ -77,7 +53,9 @@
#define SSPA_SP_FFLUSH (1 << 2) /* FIFO Flush */
#define SSPA_SP_S_RST (1 << 1) /* Active High Reset Signal */
#define SSPA_SP_S_EN (1 << 0) /* Serial Clock Domain Enable */
+#define SSPA_SP_FWID_MASK (0x3f << 20)
#define SSPA_SP_FWID(x) ((x) << 20) /* Frame-Sync Width */
+#define SSPA_TXSP_FPER_MASK (0x3f << 4)
#define SSPA_TXSP_FPER(x) ((x) << 4) /* Frame-Sync Active */
/* sspa clock sources */
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
deleted file mode 100644
index 4e74d9573f03..000000000000
--- a/sound/soc/pxa/palm27x.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * linux/sound/soc/pxa/palm27x.c
- *
- * SoC Audio driver for Palm T|X, T5 and LifeDrive
- *
- * based on tosa.c
- *
- * Copyright (C) 2008 Marek Vasut <marek.vasut@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/moduleparam.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-#include <mach/audio.h>
-#include <linux/platform_data/asoc-palm27x.h>
-
-#include "../codecs/wm9712.h"
-#include "pxa2xx-ac97.h"
-
-static struct snd_soc_jack hs_jack;
-
-/* Headphones jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-/* Headphones jack detection gpios */
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
- [0] = {
- /* gpio is set on per-platform basis */
- .name = "hp-gpio",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 200,
- },
-};
-
-/* Palm27x machine dapm widgets */
-static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Ext. Speaker", NULL),
- SND_SOC_DAPM_MIC("Ext. Microphone", NULL),
-};
-
-/* PalmTX audio map */
-static const struct snd_soc_dapm_route audio_map[] = {
- /* headphone connected to HPOUTL, HPOUTR */
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Headphone Jack", NULL, "HPOUTR"},
-
- /* ext speaker connected to ROUT2, LOUT2 */
- {"Ext. Speaker", NULL, "LOUT2"},
- {"Ext. Speaker", NULL, "ROUT2"},
-
- /* mic connected to MIC1 */
- {"MIC1", NULL, "Ext. Microphone"},
-};
-
-static struct snd_soc_card palm27x_asoc;
-
-static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
-{
- int err;
-
- /* Jack detection API stuff */
- err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
- if (err)
- return err;
-
- err = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
-
- return err;
-}
-
-static struct snd_soc_dai_link palm27x_dai[] = {
-{
- .name = "AC97 HiFi",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9712-hifi",
- .codec_name = "wm9712-codec",
- .platform_name = "pxa-pcm-audio",
- .init = palm27x_ac97_init,
-},
-{
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name = "wm9712-aux",
- .codec_name = "wm9712-codec",
- .platform_name = "pxa-pcm-audio",
-},
-};
-
-static struct snd_soc_card palm27x_asoc = {
- .name = "Palm/PXA27x",
- .owner = THIS_MODULE,
- .dai_link = palm27x_dai,
- .num_links = ARRAY_SIZE(palm27x_dai),
- .dapm_widgets = palm27x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static int palm27x_asoc_probe(struct platform_device *pdev)
-{
- int ret;
-
- if (!(machine_is_palmtx() || machine_is_palmt5() ||
- machine_is_palmld() || machine_is_palmte2()))
- return -ENODEV;
-
- if (!pdev->dev.platform_data) {
- dev_err(&pdev->dev, "please supply platform_data\n");
- return -ENODEV;
- }
-
- hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *)
- (pdev->dev.platform_data))->jack_gpio;
-
- palm27x_asoc.dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- return ret;
-}
-
-static struct platform_driver palm27x_wm9712_driver = {
- .probe = palm27x_asoc_probe,
- .driver = {
- .name = "palm27x-asoc",
- .pm = &snd_soc_pm_ops,
- },
-};
-
-module_platform_driver(palm27x_wm9712_driver);
-
-/* Module information */
-MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
-MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
deleted file mode 100644
index 84d0e2e50808..000000000000
--- a/sound/soc/pxa/poodle.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * poodle.c -- SoC audio for Poodle
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- *
- * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
- * Richard Purdie <richard@openedhand.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/timer.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <asm/hardware/locomo.h>
-#include <mach/poodle.h>
-#include <mach/audio.h>
-
-#include "../codecs/wm8731.h"
-#include "pxa2xx-i2s.h"
-
-#define POODLE_HP 1
-#define POODLE_HP_OFF 0
-#define POODLE_SPK_ON 1
-#define POODLE_SPK_OFF 0
-
- /* audio clock in Hz - rounded from 12.235MHz */
-#define POODLE_AUDIO_CLOCK 12288000
-
-static int poodle_jack_func;
-static int poodle_spk_func;
-
-static void poodle_ext_control(struct snd_soc_dapm_context *dapm)
-{
- /* set up jack connection */
- if (poodle_jack_func == POODLE_HP) {
- /* set = unmute headphone */
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_L, 1);
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_R, 1);
- snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
- } else {
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_L, 0);
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_R, 0);
- snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
- }
-
- /* set the enpoints to their new connetion states */
- if (poodle_spk_func == POODLE_SPK_ON)
- snd_soc_dapm_enable_pin(dapm, "Ext Spk");
- else
- snd_soc_dapm_disable_pin(dapm, "Ext Spk");
-
- /* signal a DAPM event */
- snd_soc_dapm_sync(dapm);
-}
-
-static int poodle_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* check the jack status at stream startup */
- poodle_ext_control(&rtd->card->dapm);
-
- return 0;
-}
-
-/* we need to unmute the HP at shutdown as the mute burns power on poodle */
-static void poodle_shutdown(struct snd_pcm_substream *substream)
-{
- /* set = unmute headphone */
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_L, 1);
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_R, 1);
-}
-
-static int poodle_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret = 0;
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 48000:
- case 96000:
- clk = 12288000;
- break;
- case 11025:
- case 22050:
- case 44100:
- clk = 11289600;
- break;
- }
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set the I2S system clock as input (unused) */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops poodle_ops = {
- .startup = poodle_startup,
- .hw_params = poodle_hw_params,
- .shutdown = poodle_shutdown,
-};
-
-static int poodle_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = poodle_jack_func;
- return 0;
-}
-
-static int poodle_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (poodle_jack_func == ucontrol->value.integer.value[0])
- return 0;
-
- poodle_jack_func = ucontrol->value.integer.value[0];
- poodle_ext_control(&card->dapm);
- return 1;
-}
-
-static int poodle_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = poodle_spk_func;
- return 0;
-}
-
-static int poodle_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (poodle_spk_func == ucontrol->value.integer.value[0])
- return 0;
-
- poodle_spk_func = ucontrol->value.integer.value[0];
- poodle_ext_control(&card->dapm);
- return 1;
-}
-
-static int poodle_amp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_AMP_ON, 0);
- else
- locomo_gpio_write(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_AMP_ON, 1);
-
- return 0;
-}
-
-/* poodle machine dapm widgets */
-static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
-SND_SOC_DAPM_HP("Headphone Jack", NULL),
-SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
-SND_SOC_DAPM_MIC("Microphone", NULL),
-};
-
-/* Corgi machine connections to the codec pins */
-static const struct snd_soc_dapm_route poodle_audio_map[] = {
-
- /* headphone connected to LHPOUT1, RHPOUT1 */
- {"Headphone Jack", NULL, "LHPOUT"},
- {"Headphone Jack", NULL, "RHPOUT"},
-
- /* speaker connected to LOUT, ROUT */
- {"Ext Spk", NULL, "ROUT"},
- {"Ext Spk", NULL, "LOUT"},
-
- {"MICIN", NULL, "Microphone"},
-};
-
-static const char *jack_function[] = {"Off", "Headphone"};
-static const char *spk_function[] = {"Off", "On"};
-static const struct soc_enum poodle_enum[] = {
- SOC_ENUM_SINGLE_EXT(2, jack_function),
- SOC_ENUM_SINGLE_EXT(2, spk_function),
-};
-
-static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
- SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack,
- poodle_set_jack),
- SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk,
- poodle_set_spk),
-};
-
-/* poodle digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link poodle_dai = {
- .name = "WM8731",
- .stream_name = "WM8731",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "wm8731-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731.0-001b",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &poodle_ops,
-};
-
-/* poodle audio machine driver */
-static struct snd_soc_card poodle = {
- .name = "Poodle",
- .dai_link = &poodle_dai,
- .num_links = 1,
- .owner = THIS_MODULE,
-
- .controls = wm8731_poodle_controls,
- .num_controls = ARRAY_SIZE(wm8731_poodle_controls),
- .dapm_widgets = wm8731_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
- .dapm_routes = poodle_audio_map,
- .num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
- .fully_routed = true,
-};
-
-static int poodle_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &poodle;
- int ret;
-
- locomo_gpio_set_dir(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_AMP_ON, 0);
- /* should we mute HP at startup - burning power ?*/
- locomo_gpio_set_dir(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_L, 0);
- locomo_gpio_set_dir(&poodle_locomo_device.dev,
- POODLE_LOCOMO_GPIO_MUTE_R, 0);
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- return ret;
-}
-
-static struct platform_driver poodle_driver = {
- .driver = {
- .name = "poodle-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = poodle_probe,
-};
-
-module_platform_driver(poodle_driver);
-
-/* Module information */
-MODULE_AUTHOR("Richard Purdie");
-MODULE_DESCRIPTION("ALSA SoC Poodle");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:poodle-audio");
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index da03fad1b9cd..b8a3cb8b7597 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pxa-ssp.c -- ALSA Soc Audio Layer
*
@@ -5,11 +6,6 @@
* Author: Liam Girdwood
* 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.
- *
* TODO:
* o Test network mode for > 16bit sample size
*/
@@ -34,7 +30,6 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include "../../arm/pxa2xx-pcm.h"
#include "pxa-ssp.h"
/*
@@ -42,8 +37,11 @@
*/
struct ssp_priv {
struct ssp_device *ssp;
+ struct clk *extclk;
+ unsigned long ssp_clk;
unsigned int sysclk;
- int dai_fmt;
+ unsigned int dai_fmt;
+ unsigned int configured_dai_fmt;
#ifdef CONFIG_PM
uint32_t cr0;
uint32_t cr1;
@@ -54,31 +52,15 @@ struct ssp_priv {
static void dump_registers(struct ssp_device *ssp)
{
- dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
+ dev_dbg(ssp->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1),
pxa_ssp_read_reg(ssp, SSTO));
- dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
+ dev_dbg(ssp->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR),
pxa_ssp_read_reg(ssp, SSACD));
}
-static void pxa_ssp_enable(struct ssp_device *ssp)
-{
- uint32_t sscr0;
-
- sscr0 = __raw_readl(ssp->mmio_base + SSCR0) | SSCR0_SSE;
- __raw_writel(sscr0, ssp->mmio_base + SSCR0);
-}
-
-static void pxa_ssp_disable(struct ssp_device *ssp)
-{
- uint32_t sscr0;
-
- sscr0 = __raw_readl(ssp->mmio_base + SSCR0) & ~SSCR0_SSE;
- __raw_writel(sscr0, ssp->mmio_base + SSCR0);
-}
-
static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
int out, struct snd_dmaengine_dai_dma_data *dma)
{
@@ -96,17 +78,18 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
struct snd_dmaengine_dai_dma_data *dma;
int ret = 0;
- if (!cpu_dai->active) {
+ if (!snd_soc_dai_active(cpu_dai)) {
clk_prepare_enable(ssp->clk);
pxa_ssp_disable(ssp);
}
+ clk_prepare_enable(priv->extclk);
+
dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL);
if (!dma)
return -ENOMEM;
-
- dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- &ssp->drcmr_tx : &ssp->drcmr_rx;
+ dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "tx" : "rx";
snd_soc_dai_set_dma_data(cpu_dai, substream, dma);
@@ -119,23 +102,25 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
- if (!cpu_dai->active) {
+ if (!snd_soc_dai_active(cpu_dai)) {
pxa_ssp_disable(ssp);
clk_disable_unprepare(ssp->clk);
}
+ clk_disable_unprepare(priv->extclk);
+
kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
}
#ifdef CONFIG_PM
-static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
+static int pxa_ssp_suspend(struct snd_soc_component *component)
{
- struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_priv *priv = snd_soc_component_get_drvdata(component);
struct ssp_device *ssp = priv->ssp;
- if (!cpu_dai->active)
+ if (!snd_soc_component_active(component))
clk_prepare_enable(ssp->clk);
priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
@@ -148,9 +133,9 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
return 0;
}
-static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
+static int pxa_ssp_resume(struct snd_soc_component *component)
{
- struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_priv *priv = snd_soc_component_get_drvdata(component);
struct ssp_device *ssp = priv->ssp;
uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;
@@ -162,7 +147,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
__raw_writel(priv->to, ssp->mmio_base + SSTO);
__raw_writel(priv->psp, ssp->mmio_base + SSPSP);
- if (cpu_dai->active)
+ if (snd_soc_component_active(component))
pxa_ssp_enable(ssp);
else
clk_disable_unprepare(ssp->clk);
@@ -175,7 +160,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
#define pxa_ssp_resume NULL
#endif
-/**
+/*
* ssp_set_clkdiv - set SSP clock divider
* @div: serial clock rate divider
*/
@@ -193,21 +178,6 @@ static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div)
pxa_ssp_write_reg(ssp, SSCR0, sscr0);
}
-/**
- * pxa_ssp_get_clkdiv - get SSP clock divider
- */
-static u32 pxa_ssp_get_scr(struct ssp_device *ssp)
-{
- u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0);
- u32 div;
-
- if (ssp->type == PXA25x_SSP)
- div = ((sscr0 >> 8) & 0xff) * 2 + 2;
- else
- div = ((sscr0 >> 8) & 0xfff) + 1;
- return div;
-}
-
/*
* Set the SSP ports SYSCLK.
*/
@@ -216,12 +186,26 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
{
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
- int val;
u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) &
- ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
+ ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
+
+ if (priv->extclk) {
+ int ret;
- dev_dbg(&ssp->pdev->dev,
+ /*
+ * For DT based boards, if an extclk is given, use it
+ * here and configure PXA_SSP_CLK_EXT.
+ */
+
+ ret = clk_set_rate(priv->extclk, freq);
+ if (ret < 0)
+ return ret;
+
+ clk_id = PXA_SSP_CLK_EXT;
+ }
+
+ dev_dbg(ssp->dev,
"pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n",
cpu_dai->id, clk_id, freq);
@@ -257,8 +241,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
* on PXA2xx. On PXA3xx it must be enabled when doing so. */
if (ssp->type != PXA3xx_SSP)
clk_disable_unprepare(ssp->clk);
- val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0;
- pxa_ssp_write_reg(ssp, SSCR0, val);
+ pxa_ssp_write_reg(ssp, SSCR0, sscr0);
if (ssp->type != PXA3xx_SSP)
clk_prepare_enable(ssp->clk);
@@ -266,66 +249,17 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
}
/*
- * Set the SSP clock dividers.
- */
-static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
- struct ssp_device *ssp = priv->ssp;
- int val;
-
- switch (div_id) {
- case PXA_SSP_AUDIO_DIV_ACDS:
- val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div);
- pxa_ssp_write_reg(ssp, SSACD, val);
- break;
- case PXA_SSP_AUDIO_DIV_SCDB:
- val = pxa_ssp_read_reg(ssp, SSACD);
- val &= ~SSACD_SCDB;
- if (ssp->type == PXA3xx_SSP)
- val &= ~SSACD_SCDX8;
- switch (div) {
- case PXA_SSP_CLK_SCDB_1:
- val |= SSACD_SCDB;
- break;
- case PXA_SSP_CLK_SCDB_4:
- break;
- case PXA_SSP_CLK_SCDB_8:
- if (ssp->type == PXA3xx_SSP)
- val |= SSACD_SCDX8;
- else
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- pxa_ssp_write_reg(ssp, SSACD, val);
- break;
- case PXA_SSP_DIV_SCR:
- pxa_ssp_set_scr(ssp, div);
- break;
- default:
- return -ENODEV;
- }
-
- return 0;
-}
-
-/*
* Configure the PLL frequency pxa27x and (afaik - pxa320 only)
*/
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
- int source, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq)
{
- struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70;
if (ssp->type == PXA3xx_SSP)
pxa_ssp_write_reg(ssp, SSACDD, 0);
- switch (freq_out) {
+ switch (freq) {
case 5622000:
break;
case 11345000:
@@ -354,8 +288,9 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
if (ssp->type == PXA3xx_SSP) {
u32 val;
u64 tmp = 19968;
+
tmp *= 1000000;
- do_div(tmp, freq_out);
+ do_div(tmp, freq);
val = tmp;
val = (val << 16) | 64;
@@ -363,9 +298,9 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
ssacd |= (0x6 << 4);
- dev_dbg(&ssp->pdev->dev,
+ dev_dbg(ssp->dev,
"Using SSACDD %x to supply %uHz\n",
- val, freq_out);
+ val, freq);
break;
}
@@ -432,49 +367,85 @@ static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
return 0;
}
+static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BP_FP:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Settings will be applied in hw_params() */
+ priv->dai_fmt = fmt;
+
+ return 0;
+}
+
/*
* Set up the SSP DAI format.
* The SSP Port must be inactive before calling this function as the
* physical interface format is changed.
*/
-static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
+static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv)
{
- struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
u32 sscr0, sscr1, sspsp, scfr;
/* check if we need to change anything at all */
- if (priv->dai_fmt == fmt)
+ if (priv->configured_dai_fmt == priv->dai_fmt)
return 0;
- /* we can only change the settings if the port is not in use */
- if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
- dev_err(&ssp->pdev->dev,
- "can't change hardware dai format: stream is in use");
- return -EINVAL;
- }
-
/* reset port settings */
sscr0 = pxa_ssp_read_reg(ssp, SSCR0) &
- ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
- sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
- sspsp = 0;
+ ~(SSCR0_PSP | SSCR0_MOD);
+ sscr1 = pxa_ssp_read_reg(ssp, SSCR1) &
+ ~(SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR |
+ SSCR1_RWOT | SSCR1_TRAIL | SSCR1_TFT | SSCR1_RFT);
+ sspsp = pxa_ssp_read_reg(ssp, SSPSP) &
+ ~(SSPSP_SFRMP | SSPSP_SCMODE(3));
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ sscr1 |= SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
+
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_BC_FP:
sscr1 |= SSCR1_SCLKDIR | SSCR1_SCFR;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_BP_FP:
break;
default:
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
sspsp |= SSPSP_SFRMP;
break;
@@ -490,7 +461,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
sscr0 |= SSCR0_PSP;
sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
@@ -499,6 +470,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_DSP_A:
sspsp |= SSPSP_FSRT;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_B:
sscr0 |= SSCR0_MOD | SSCR0_PSP;
sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
@@ -512,9 +484,9 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
pxa_ssp_write_reg(ssp, SSCR1, sscr1);
pxa_ssp_write_reg(ssp, SSPSP, sspsp);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ case SND_SOC_DAIFMT_BC_FP:
scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR;
pxa_ssp_write_reg(ssp, SSCR1, scfr);
@@ -529,11 +501,29 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
* we have to defer some things until hw_params() where we
* know parameters like the sample size.
*/
- priv->dai_fmt = fmt;
+ priv->configured_dai_fmt = priv->dai_fmt;
return 0;
}
+struct pxa_ssp_clock_mode {
+ int rate;
+ int pll;
+ u8 acds;
+ u8 scdb;
+};
+
+static const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = {
+ { .rate = 8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X },
+ { .rate = 11025, .pll = 5622000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_4X },
+ { .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X },
+ { .rate = 22050, .pll = 5622000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X },
+ { .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X },
+ { .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X },
+ { .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_1X },
+ {}
+};
+
/*
* Set the SSP audio DMA parameters and sample size.
* Can be called multiple times by oss emulation.
@@ -545,11 +535,13 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
int chn = params_channels(params);
- u32 sscr0;
- u32 sspsp;
+ u32 sscr0, sspsp;
int width = snd_pcm_format_physical_width(params_format(params));
int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf;
struct snd_dmaengine_dai_dma_data *dma_data;
+ int rate = params_rate(params);
+ int bclk = rate * chn * (width / 8);
+ int ret;
dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
@@ -565,6 +557,10 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
return 0;
+ ret = pxa_ssp_configure_dai_fmt(priv);
+ if (ret < 0)
+ return ret;
+
/* clear selected SSP bits */
sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
@@ -584,19 +580,59 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
}
pxa_ssp_write_reg(ssp, SSCR0, sscr0);
+ if (sscr0 & SSCR0_ACS) {
+ ret = pxa_ssp_set_pll(priv, bclk);
+
+ /*
+ * If we were able to generate the bclk directly,
+ * all is fine. Otherwise, look up the closest rate
+ * from the table and also set the dividers.
+ */
+
+ if (ret < 0) {
+ const struct pxa_ssp_clock_mode *m;
+ int ssacd;
+
+ for (m = pxa_ssp_clock_modes; m->rate; m++) {
+ if (m->rate == rate)
+ break;
+ }
+
+ if (!m->rate)
+ return -EINVAL;
+
+ ret = pxa_ssp_set_pll(priv, bclk);
+ if (ret < 0)
+ return ret;
+
+ ssacd = pxa_ssp_read_reg(ssp, SSACD);
+ ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X);
+ ssacd |= SSACD_ACDS(m->acds);
+ ssacd |= m->scdb;
+ pxa_ssp_write_reg(ssp, SSACD, ssacd);
+ }
+ } else if (sscr0 & SSCR0_ECS) {
+ /*
+ * For setups with external clocking, the PLL and its diviers
+ * are not active. Instead, the SCR bits in SSCR0 can be used
+ * to divide the clock.
+ */
+ pxa_ssp_set_scr(ssp, bclk / rate);
+ }
+
switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
sspsp = pxa_ssp_read_reg(ssp, SSPSP);
- if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) {
+ if (((priv->sysclk / bclk) == 64) && (width == 16)) {
/* This is a special case where the bitclk is 64fs
- * and we're not dealing with 2*32 bits of audio
- * samples.
- *
- * The SSP values used for that are all found out by
- * trying and failing a lot; some of the registers
- * needed for that mode are only available on PXA3xx.
- */
+ * and we're not dealing with 2*32 bits of audio
+ * samples.
+ *
+ * The SSP values used for that are all found out by
+ * trying and failing a lot; some of the registers
+ * needed for that mode are only available on PXA3xx.
+ */
if (ssp->type != PXA3xx_SSP)
return -EINVAL;
@@ -627,7 +663,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
* - complain loudly and fail if they've not been set up yet.
*/
if ((sscr0 & SSCR0_MOD) && !ttsa) {
- dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
+ dev_err(ssp->dev, "No TDM timeslot configured\n");
return -EINVAL;
}
@@ -732,6 +768,15 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai)
ret = -ENODEV;
goto err_priv;
}
+
+ priv->extclk = devm_clk_get(dev, "extclk");
+ if (IS_ERR(priv->extclk)) {
+ ret = PTR_ERR(priv->extclk);
+ if (ret == -EPROBE_DEFER)
+ goto err_priv;
+
+ priv->extclk = NULL;
+ }
} else {
priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
if (priv->ssp == NULL) {
@@ -768,23 +813,19 @@ static int pxa_ssp_remove(struct snd_soc_dai *dai)
#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
.startup = pxa_ssp_startup,
.shutdown = pxa_ssp_shutdown,
.trigger = pxa_ssp_trigger,
.hw_params = pxa_ssp_hw_params,
.set_sysclk = pxa_ssp_set_dai_sysclk,
- .set_clkdiv = pxa_ssp_set_dai_clkdiv,
- .set_pll = pxa_ssp_set_dai_pll,
.set_fmt = pxa_ssp_set_dai_fmt,
.set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
.set_tristate = pxa_ssp_set_dai_tristate,
};
static struct snd_soc_dai_driver pxa_ssp_dai = {
- .probe = pxa_ssp_probe,
- .remove = pxa_ssp_remove,
- .suspend = pxa_ssp_suspend,
- .resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -801,7 +842,17 @@ static struct snd_soc_dai_driver pxa_ssp_dai = {
};
static const struct snd_soc_component_driver pxa_ssp_component = {
- .name = "pxa-ssp",
+ .name = "pxa-ssp",
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .legacy_dai_naming = 1,
};
#ifdef CONFIG_OF
@@ -833,3 +884,4 @@ module_platform_driver(asoc_ssp_driver);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-ssp-dai");
diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h
index bc79da221c0d..d3b05109dff1 100644
--- a/sound/soc/pxa/pxa-ssp.h
+++ b/sound/soc/pxa/pxa-ssp.h
@@ -1,20 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ASoC PXA SSP port support
- *
- * This program is free software; you can 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 _PXA_SSP_H
#define _PXA_SSP_H
-/* pxa DAI SSP IDs */
-#define PXA_DAI_SSP1 0
-#define PXA_DAI_SSP2 1
-#define PXA_DAI_SSP3 2
-#define PXA_DAI_SSP4 3
-
/* SSP clock sources */
#define PXA_SSP_CLK_PLL 0
#define PXA_SSP_CLK_EXT 1
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index f3de615aacd7..78f50032afc5 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
*
* Author: Nicolas Pitre
* Created: Dec 02, 2004
* Copyright: MontaVista Software 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/init.h>
@@ -17,94 +14,80 @@
#include <linux/dmaengine.h>
#include <linux/dma/pxa-dma.h>
+#include <sound/ac97/controller.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include <mach/hardware.h>
-#include <mach/regs-ac97.h>
-#include <mach/audio.h>
+#include <linux/platform_data/asoc-pxa.h>
-#include "pxa2xx-ac97.h"
+#define PCDR 0x0040 /* PCM FIFO Data Register */
+#define MODR 0x0140 /* Modem FIFO Data Register */
+#define MCDR 0x0060 /* Mic-in FIFO Data Register */
-static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
+static void pxa2xx_ac97_warm_reset(struct ac97_controller *adrv)
{
- pxa2xx_ac97_try_warm_reset(ac97);
+ pxa2xx_ac97_try_warm_reset();
- pxa2xx_ac97_finish_reset(ac97);
+ pxa2xx_ac97_finish_reset();
}
-static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
+static void pxa2xx_ac97_cold_reset(struct ac97_controller *adrv)
{
- pxa2xx_ac97_try_cold_reset(ac97);
+ pxa2xx_ac97_try_cold_reset();
+
+ pxa2xx_ac97_finish_reset();
+}
- pxa2xx_ac97_finish_reset(ac97);
+static int pxa2xx_ac97_read_actrl(struct ac97_controller *adrv, int slot,
+ unsigned short reg)
+{
+ return pxa2xx_ac97_read(slot, reg);
+}
+
+static int pxa2xx_ac97_write_actrl(struct ac97_controller *adrv, int slot,
+ unsigned short reg, unsigned short val)
+{
+ return pxa2xx_ac97_write(slot, reg, val);
}
-static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_read,
- .write = pxa2xx_ac97_write,
+static struct ac97_controller_ops pxa2xx_ac97_ops = {
+ .read = pxa2xx_ac97_read_actrl,
+ .write = pxa2xx_ac97_write_actrl,
.warm_reset = pxa2xx_ac97_warm_reset,
.reset = pxa2xx_ac97_cold_reset,
};
-static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 11,
-};
-
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
- .addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "pcm_pcm_stereo_in",
.maxburst = 32,
- .filter_data = &pxa2xx_ac97_pcm_stereo_in_req,
-};
-
-static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 12,
};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
- .addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "pcm_pcm_stereo_out",
.maxburst = 32,
- .filter_data = &pxa2xx_ac97_pcm_stereo_out_req,
};
-static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 10,
-};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
- .addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .chan_name = "pcm_aux_mono_out",
.maxburst = 16,
- .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req,
};
-static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 9,
-};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
- .addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .chan_name = "pcm_aux_mono_in",
.maxburst = 16,
- .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req,
};
-static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = {
- .prio = PXAD_PRIO_LOWEST,
- .drcmr = 8,
-};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
- .addr = __PREG(MCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .chan_name = "pcm_aux_mic_mono",
.maxburst = 16,
- .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req,
};
static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream,
@@ -142,9 +125,8 @@ static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
- else
- snd_soc_dai_set_dma_data(cpu_dai, substream,
- &pxa2xx_ac97_pcm_mic_mono_in);
+ snd_soc_dai_set_dma_data(cpu_dai, substream,
+ &pxa2xx_ac97_pcm_mic_mono_in);
return 0;
}
@@ -172,7 +154,6 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
{
.name = "pxa2xx-ac97",
- .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -189,7 +170,6 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
},
{
.name = "pxa2xx-ac97-aux",
- .bus_control = true,
.playback = {
.stream_name = "AC97 Aux Playback",
.channels_min = 1,
@@ -206,7 +186,6 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
},
{
.name = "pxa2xx-ac97-mic",
- .bus_control = true,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
@@ -219,44 +198,79 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
static const struct snd_soc_component_driver pxa_ac97_component = {
.name = "pxa-ac97",
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa2xx_ac97_dt_ids[] = {
+ { .compatible = "marvell,pxa250-ac97", },
+ { .compatible = "marvell,pxa270-ac97", },
+ { .compatible = "marvell,pxa300-ac97", },
+ { }
};
+MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids);
+
+#endif
static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
{
int ret;
+ struct ac97_controller *ctrl;
+ pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
+ struct resource *regs;
+ void **codecs_pdata;
if (pdev->id != -1) {
dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
return -ENXIO;
}
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ pxa2xx_ac97_pcm_stereo_in.addr = regs->start + PCDR;
+ pxa2xx_ac97_pcm_stereo_out.addr = regs->start + PCDR;
+ pxa2xx_ac97_pcm_aux_mono_out.addr = regs->start + MODR;
+ pxa2xx_ac97_pcm_aux_mono_in.addr = regs->start + MODR;
+ pxa2xx_ac97_pcm_mic_mono_in.addr = regs->start + MCDR;
+
ret = pxa2xx_ac97_hw_probe(pdev);
if (ret) {
dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret);
return ret;
}
- ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops);
- if (ret != 0)
- return ret;
+ codecs_pdata = pdata ? pdata->codec_pdata : NULL;
+ ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev,
+ AC97_SLOTS_AVAILABLE_ALL,
+ codecs_pdata);
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);
+ platform_set_drvdata(pdev, ctrl);
/* Punt most of the init to the SoC probe; we may need the machine
* driver to do interesting things with the clocking to get us up
* and running.
*/
- return snd_soc_register_component(&pdev->dev, &pxa_ac97_component,
+ return devm_snd_soc_register_component(&pdev->dev, &pxa_ac97_component,
pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver));
}
-static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
+static void pxa2xx_ac97_dev_remove(struct platform_device *pdev)
{
- snd_soc_unregister_component(&pdev->dev);
- snd_soc_set_ac97_ops(NULL);
+ struct ac97_controller *ctrl = platform_get_drvdata(pdev);
+
+ snd_ac97_controller_unregister(ctrl);
pxa2xx_ac97_hw_remove(pdev);
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int pxa2xx_ac97_dev_suspend(struct device *dev)
{
return pxa2xx_ac97_hw_suspend();
@@ -267,18 +281,16 @@ static int pxa2xx_ac97_dev_resume(struct device *dev)
return pxa2xx_ac97_hw_resume();
}
-static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops,
+static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops,
pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume);
-#endif
static struct platform_driver pxa2xx_ac97_driver = {
.probe = pxa2xx_ac97_dev_probe,
.remove = pxa2xx_ac97_dev_remove,
.driver = {
.name = "pxa2xx-ac97",
-#ifdef CONFIG_PM_SLEEP
.pm = &pxa2xx_ac97_pm_ops,
-#endif
+ .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids),
},
};
@@ -287,3 +299,4 @@ module_platform_driver(pxa2xx_ac97_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
deleted file mode 100644
index a49c21ba3842..000000000000
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/sound/soc/pxa/pxa2xx-ac97.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 _PXA2XX_AC97_H
-#define _PXA2XX_AC97_H
-
-/* pxa2xx DAI ID's */
-#define PXA2XX_DAI_AC97_HIFI 0
-#define PXA2XX_DAI_AC97_AUX 1
-#define PXA2XX_DAI_AC97_MIC 2
-
-#endif
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 0389cf7b4b1e..849fbf176a70 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pxa2xx-i2s.c -- ALSA Soc Audio Layer
*
* 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.
*/
#include <linux/init.h>
@@ -25,31 +21,30 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include <mach/hardware.h>
-#include <mach/audio.h>
+#include <linux/platform_data/asoc-pxa.h>
#include "pxa2xx-i2s.h"
/*
* I2S Controller Register and Bit Definitions
*/
-#define SACR0 __REG(0x40400000) /* Global Control Register */
-#define SACR1 __REG(0x40400004) /* Serial Audio I 2 S/MSB-Justified Control Register */
-#define SASR0 __REG(0x4040000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
-#define SAIMR __REG(0x40400014) /* Serial Audio Interrupt Mask Register */
-#define SAICR __REG(0x40400018) /* Serial Audio Interrupt Clear Register */
-#define SADIV __REG(0x40400060) /* Audio Clock Divider Register. */
-#define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */
+#define SACR0 (0x0000) /* Global Control Register */
+#define SACR1 (0x0004) /* Serial Audio I 2 S/MSB-Justified Control Register */
+#define SASR0 (0x000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
+#define SAIMR (0x0014) /* Serial Audio Interrupt Mask Register */
+#define SAICR (0x0018) /* Serial Audio Interrupt Clear Register */
+#define SADIV (0x0060) /* Audio Clock Divider Register. */
+#define SADR (0x0080) /* Serial Audio Data Register (TX and RX FIFO access Register). */
#define SACR0_RFTH(x) ((x) << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */
#define SACR0_TFTH(x) ((x) << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */
#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */
#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */
#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */
-#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
+#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
#define SACR0_ENB (1 << 0) /* Enable I2S Link */
#define SACR1_ENLBF (1 << 5) /* Enable Loopback */
-#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
+#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
#define SACR1_DREC (1 << 3) /* Disable Recording Function */
#define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */
@@ -60,7 +55,7 @@
#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */
#define SASR0_BSY (1 << 2) /* I2S Busy */
#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */
-#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
+#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */
#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */
@@ -81,34 +76,31 @@ struct pxa_i2s_port {
static struct pxa_i2s_port pxa_i2s;
static struct clk *clk_i2s;
static int clk_ena = 0;
+static void __iomem *i2s_reg_base;
-static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3;
static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
- .addr = __PREG(SADR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "tx",
.maxburst = 32,
- .filter_data = &pxa2xx_i2s_pcm_stereo_out_req,
};
-static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2;
static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
- .addr = __PREG(SADR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "rx",
.maxburst = 32,
- .filter_data = &pxa2xx_i2s_pcm_stereo_in_req,
};
static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
if (IS_ERR(clk_i2s))
return PTR_ERR(clk_i2s);
- if (!cpu_dai->active)
- SACR0 = 0;
+ if (!snd_soc_dai_active(cpu_dai))
+ writel(0, i2s_reg_base + SACR0);
return 0;
}
@@ -119,8 +111,8 @@ static int pxa_i2s_wait(void)
int i;
/* flush the Rx FIFO */
- for(i = 0; i < 16; i++)
- SADR;
+ for (i = 0; i < 16; i++)
+ readl(i2s_reg_base + SADR);
return 0;
}
@@ -137,11 +129,11 @@ static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
break;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
pxa_i2s.master = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_BC_FP:
pxa_i2s.master = 0;
break;
default:
@@ -180,39 +172,39 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
/* is port used by another stream */
if (!(SACR0 & SACR0_ENB)) {
- SACR0 = 0;
+ writel(0, i2s_reg_base + SACR0);
if (pxa_i2s.master)
- SACR0 |= SACR0_BCKD;
+ writel(readl(i2s_reg_base + SACR0) | (SACR0_BCKD), i2s_reg_base + SACR0);
- SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
- SACR1 |= pxa_i2s.fmt;
+ writel(readl(i2s_reg_base + SACR0) | (SACR0_RFTH(14) | SACR0_TFTH(1)), i2s_reg_base + SACR0);
+ writel(readl(i2s_reg_base + SACR1) | (pxa_i2s.fmt), i2s_reg_base + SACR1);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- SAIMR |= SAIMR_TFS;
+ writel(readl(i2s_reg_base + SAIMR) | (SAIMR_TFS), i2s_reg_base + SAIMR);
else
- SAIMR |= SAIMR_RFS;
+ writel(readl(i2s_reg_base + SAIMR) | (SAIMR_RFS), i2s_reg_base + SAIMR);
switch (params_rate(params)) {
case 8000:
- SADIV = 0x48;
+ writel(0x48, i2s_reg_base + SADIV);
break;
case 11025:
- SADIV = 0x34;
+ writel(0x34, i2s_reg_base + SADIV);
break;
case 16000:
- SADIV = 0x24;
+ writel(0x24, i2s_reg_base + SADIV);
break;
case 22050:
- SADIV = 0x1a;
+ writel(0x1a, i2s_reg_base + SADIV);
break;
case 44100:
- SADIV = 0xd;
+ writel(0xd, i2s_reg_base + SADIV);
break;
case 48000:
- SADIV = 0xc;
+ writel(0xc, i2s_reg_base + SADIV);
break;
case 96000: /* not in manual and possibly slightly inaccurate */
- SADIV = 0x6;
+ writel(0x6, i2s_reg_base + SADIV);
break;
}
@@ -227,10 +219,10 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- SACR1 &= ~SACR1_DRPL;
+ writel(readl(i2s_reg_base + SACR1) & (~SACR1_DRPL), i2s_reg_base + SACR1);
else
- SACR1 &= ~SACR1_DREC;
- SACR0 |= SACR0_ENB;
+ writel(readl(i2s_reg_base + SACR1) & (~SACR1_DREC), i2s_reg_base + SACR1);
+ writel(readl(i2s_reg_base + SACR0) | (SACR0_ENB), i2s_reg_base + SACR0);
break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -249,15 +241,15 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- SACR1 |= SACR1_DRPL;
- SAIMR &= ~SAIMR_TFS;
+ writel(readl(i2s_reg_base + SACR1) | (SACR1_DRPL), i2s_reg_base + SACR1);
+ writel(readl(i2s_reg_base + SAIMR) & (~SAIMR_TFS), i2s_reg_base + SAIMR);
} else {
- SACR1 |= SACR1_DREC;
- SAIMR &= ~SAIMR_RFS;
+ writel(readl(i2s_reg_base + SACR1) | (SACR1_DREC), i2s_reg_base + SACR1);
+ writel(readl(i2s_reg_base + SAIMR) & (~SAIMR_RFS), i2s_reg_base + SAIMR);
}
- if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
- SACR0 &= ~SACR0_ENB;
+ if ((readl(i2s_reg_base + SACR1) & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
+ writel(readl(i2s_reg_base + SACR0) & (~SACR0_ENB), i2s_reg_base + SACR0);
pxa_i2s_wait();
if (clk_ena) {
clk_disable_unprepare(clk_i2s);
@@ -267,37 +259,37 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
}
#ifdef CONFIG_PM
-static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
+static int pxa2xx_soc_pcm_suspend(struct snd_soc_component *component)
{
/* store registers */
- pxa_i2s.sacr0 = SACR0;
- pxa_i2s.sacr1 = SACR1;
- pxa_i2s.saimr = SAIMR;
- pxa_i2s.sadiv = SADIV;
+ pxa_i2s.sacr0 = readl(i2s_reg_base + SACR0);
+ pxa_i2s.sacr1 = readl(i2s_reg_base + SACR1);
+ pxa_i2s.saimr = readl(i2s_reg_base + SAIMR);
+ pxa_i2s.sadiv = readl(i2s_reg_base + SADIV);
/* deactivate link */
- SACR0 &= ~SACR0_ENB;
+ writel(readl(i2s_reg_base + SACR0) & (~SACR0_ENB), i2s_reg_base + SACR0);
pxa_i2s_wait();
return 0;
}
-static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
+static int pxa2xx_soc_pcm_resume(struct snd_soc_component *component)
{
pxa_i2s_wait();
- SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
- SACR1 = pxa_i2s.sacr1;
- SAIMR = pxa_i2s.saimr;
- SADIV = pxa_i2s.sadiv;
+ writel(pxa_i2s.sacr0 & ~SACR0_ENB, i2s_reg_base + SACR0);
+ writel(pxa_i2s.sacr1, i2s_reg_base + SACR1);
+ writel(pxa_i2s.saimr, i2s_reg_base + SAIMR);
+ writel(pxa_i2s.sadiv, i2s_reg_base + SADIV);
- SACR0 = pxa_i2s.sacr0;
+ writel(pxa_i2s.sacr0, i2s_reg_base + SACR0);
return 0;
}
#else
-#define pxa2xx_i2s_suspend NULL
-#define pxa2xx_i2s_resume NULL
+#define pxa2xx_soc_pcm_suspend NULL
+#define pxa2xx_soc_pcm_resume NULL
#endif
static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
@@ -312,12 +304,12 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
* the SACR0[RST] bit must also be set and cleared to reset all
* I2S controller registers.
*/
- SACR0 = SACR0_RST;
- SACR0 = 0;
+ writel(SACR0_RST, i2s_reg_base + SACR0);
+ writel(0, i2s_reg_base + SACR0);
/* Make sure RPL and REC are disabled */
- SACR1 = SACR1_DRPL | SACR1_DREC;
+ writel(SACR1_DRPL | SACR1_DREC, i2s_reg_base + SACR1);
/* Along with FIFO servicing */
- SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+ writel(readl(i2s_reg_base + SAIMR) & (~(SAIMR_RFS | SAIMR_TFS)), i2s_reg_base + SAIMR);
snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
&pxa2xx_i2s_pcm_stereo_in);
@@ -337,6 +329,8 @@ static int pxa2xx_i2s_remove(struct snd_soc_dai *dai)
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
+ .probe = pxa2xx_i2s_probe,
+ .remove = pxa2xx_i2s_remove,
.startup = pxa2xx_i2s_startup,
.shutdown = pxa2xx_i2s_shutdown,
.trigger = pxa2xx_i2s_trigger,
@@ -346,10 +340,6 @@ static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
};
static struct snd_soc_dai_driver pxa_i2s_dai = {
- .probe = pxa2xx_i2s_probe,
- .remove = pxa2xx_i2s_remove,
- .suspend = pxa2xx_i2s_suspend,
- .resume = pxa2xx_i2s_resume,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -361,15 +351,34 @@ static struct snd_soc_dai_driver pxa_i2s_dai = {
.rates = PXA2XX_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = &pxa_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static const struct snd_soc_component_driver pxa_i2s_component = {
- .name = "pxa-i2s",
+ .name = "pxa-i2s",
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
+ .suspend = pxa2xx_soc_pcm_suspend,
+ .resume = pxa2xx_soc_pcm_resume,
+ .legacy_dai_naming = 1,
};
static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
{
+ struct resource *res;
+
+ i2s_reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(i2s_reg_base))
+ return PTR_ERR(i2s_reg_base);
+
+ pxa2xx_i2s_pcm_stereo_out.addr = res->start + SADR;
+ pxa2xx_i2s_pcm_stereo_in.addr = res->start + SADR;
+
return devm_snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
&pxa_i2s_dai, 1);
}
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h
index 070f3c6059fe..263568d44544 100644
--- a/sound/soc/pxa/pxa2xx-i2s.h
+++ b/sound/soc/pxa/pxa2xx-i2s.h
@@ -1,17 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/sound/soc/pxa/pxa2xx-i2s.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 _PXA2XX_I2S_H
#define _PXA2XX_I2S_H
-/* pxa2xx DAI ID's */
-#define PXA2XX_DAI_I2S 0
-
/* I2S clock */
#define PXA2XX_I2S_SYSCLK 0
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 9f390398d518..9d6c41f775e5 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
*
* Author: Nicolas Pitre
* Created: Nov 30, 2004
* Copyright: (C) 2004 MontaVista Software, 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/dma-mapping.h>
@@ -20,93 +17,25 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include "../../arm/pxa2xx-pcm.h"
-
-static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_dmaengine_dai_dma_data *dma;
-
- dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- /* return if this is a bufferless transfer e.g.
- * codec <--> BT codec or GSM modem -- lg FIXME */
- if (!dma)
- return 0;
-
- return __pxa2xx_pcm_hw_params(substream, params);
-}
-
-static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- __pxa2xx_pcm_hw_free(substream);
-
- return 0;
-}
-
-static struct snd_pcm_ops pxa2xx_pcm_ops = {
- .open = __pxa2xx_pcm_open,
- .close = __pxa2xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pxa2xx_pcm_hw_params,
- .hw_free = pxa2xx_pcm_hw_free,
- .prepare = __pxa2xx_pcm_prepare,
- .trigger = pxa2xx_pcm_trigger,
- .pointer = pxa2xx_pcm_pointer,
- .mmap = pxa2xx_pcm_mmap,
-};
-
-static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-static struct snd_soc_platform_driver pxa2xx_soc_platform = {
- .ops = &pxa2xx_pcm_ops,
- .pcm_new = pxa2xx_soc_pcm_new,
- .pcm_free = pxa2xx_pcm_free_dma_buffers,
+static const struct snd_soc_component_driver pxa2xx_soc_platform = {
+ .pcm_construct = pxa2xx_soc_pcm_new,
+ .open = pxa2xx_soc_pcm_open,
+ .close = pxa2xx_soc_pcm_close,
+ .hw_params = pxa2xx_soc_pcm_hw_params,
+ .prepare = pxa2xx_soc_pcm_prepare,
+ .trigger = pxa2xx_soc_pcm_trigger,
+ .pointer = pxa2xx_soc_pcm_pointer,
};
static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
{
- return devm_snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform);
+ return devm_snd_soc_register_component(&pdev->dev, &pxa2xx_soc_platform,
+ NULL, 0);
}
-#ifdef CONFIG_OF
-static const struct of_device_id snd_soc_pxa_audio_match[] = {
- { .compatible = "mrvl,pxa-pcm-audio" },
- { }
-};
-MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
-#endif
-
static struct platform_driver pxa_pcm_driver = {
.driver = {
.name = "pxa-pcm-audio",
- .of_match_table = of_match_ptr(snd_soc_pxa_audio_match),
},
.probe = pxa2xx_soc_platform_probe,
@@ -117,3 +46,4 @@ module_platform_driver(pxa_pcm_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-pcm-audio");
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
deleted file mode 100644
index 552b763005ed..000000000000
--- a/sound/soc/pxa/raumfeld.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * raumfeld_audio.c -- SoC audio for Raumfeld audio devices
- *
- * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
- *
- * based on code from:
- *
- * Wolfson Microelectronics PLC.
- * Openedhand Ltd.
- * Liam Girdwood <lrg@slimlogic.co.uk>
- * Richard Purdie <richard@openedhand.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/i2c.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-
-#include "pxa-ssp.h"
-
-#define GPIO_SPDIF_RESET (38)
-#define GPIO_MCLK_RESET (111)
-#define GPIO_CODEC_RESET (120)
-
-static struct i2c_client *max9486_client;
-static struct i2c_board_info max9486_hwmon_info = {
- I2C_BOARD_INFO("max9485", 0x63),
-};
-
-#define MAX9485_MCLK_FREQ_112896 0x22
-#define MAX9485_MCLK_FREQ_122880 0x23
-#define MAX9485_MCLK_FREQ_225792 0x32
-#define MAX9485_MCLK_FREQ_245760 0x33
-
-static void set_max9485_clk(char clk)
-{
- i2c_master_send(max9486_client, &clk, 1);
-}
-
-static void raumfeld_enable_audio(bool en)
-{
- if (en) {
- gpio_set_value(GPIO_MCLK_RESET, 1);
-
- /* wait some time to let the clocks become stable */
- msleep(100);
-
- gpio_set_value(GPIO_SPDIF_RESET, 1);
- gpio_set_value(GPIO_CODEC_RESET, 1);
- } else {
- gpio_set_value(GPIO_MCLK_RESET, 0);
- gpio_set_value(GPIO_SPDIF_RESET, 0);
- gpio_set_value(GPIO_CODEC_RESET, 0);
- }
-}
-
-/* CS4270 */
-static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- /* set freq to 0 to enable all possible codec sample rates */
- return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
-}
-
-static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- /* set freq to 0 to enable all possible codec sample rates */
- snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
-}
-
-static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret = 0;
-
- switch (params_rate(params)) {
- case 44100:
- set_max9485_clk(MAX9485_MCLK_FREQ_112896);
- clk = 11289600;
- break;
- case 48000:
- set_max9485_clk(MAX9485_MCLK_FREQ_122880);
- clk = 12288000;
- break;
- case 88200:
- set_max9485_clk(MAX9485_MCLK_FREQ_225792);
- clk = 22579200;
- break;
- case 96000:
- set_max9485_clk(MAX9485_MCLK_FREQ_245760);
- clk = 24576000;
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0);
- if (ret < 0)
- return ret;
-
- /* setup the CPU DAI */
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops raumfeld_cs4270_ops = {
- .startup = raumfeld_cs4270_startup,
- .shutdown = raumfeld_cs4270_shutdown,
- .hw_params = raumfeld_cs4270_hw_params,
-};
-
-static int raumfeld_analog_suspend(struct snd_soc_card *card)
-{
- raumfeld_enable_audio(false);
- return 0;
-}
-
-static int raumfeld_analog_resume(struct snd_soc_card *card)
-{
- raumfeld_enable_audio(true);
- return 0;
-}
-
-/* AK4104 */
-
-static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret = 0, clk = 0;
-
- switch (params_rate(params)) {
- case 44100:
- set_max9485_clk(MAX9485_MCLK_FREQ_112896);
- clk = 11289600;
- break;
- case 48000:
- set_max9485_clk(MAX9485_MCLK_FREQ_122880);
- clk = 12288000;
- break;
- case 88200:
- set_max9485_clk(MAX9485_MCLK_FREQ_225792);
- clk = 22579200;
- break;
- case 96000:
- set_max9485_clk(MAX9485_MCLK_FREQ_245760);
- clk = 24576000;
- break;
- default:
- return -EINVAL;
- }
-
- /* setup the CPU DAI */
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops raumfeld_ak4104_ops = {
- .hw_params = raumfeld_ak4104_hw_params,
-};
-
-#define DAI_LINK_CS4270 \
-{ \
- .name = "CS4270", \
- .stream_name = "CS4270", \
- .cpu_dai_name = "pxa-ssp-dai.0", \
- .platform_name = "pxa-pcm-audio", \
- .codec_dai_name = "cs4270-hifi", \
- .codec_name = "cs4270.0-0048", \
- .dai_fmt = SND_SOC_DAIFMT_I2S | \
- SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS, \
- .ops = &raumfeld_cs4270_ops, \
-}
-
-#define DAI_LINK_AK4104 \
-{ \
- .name = "ak4104", \
- .stream_name = "Playback", \
- .cpu_dai_name = "pxa-ssp-dai.1", \
- .codec_dai_name = "ak4104-hifi", \
- .platform_name = "pxa-pcm-audio", \
- .dai_fmt = SND_SOC_DAIFMT_I2S | \
- SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS, \
- .ops = &raumfeld_ak4104_ops, \
- .codec_name = "spi0.0", \
-}
-
-static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] =
-{
- DAI_LINK_CS4270,
- DAI_LINK_AK4104,
-};
-
-static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] =
-{
- DAI_LINK_CS4270,
-};
-
-static struct snd_soc_card snd_soc_raumfeld_connector = {
- .name = "Raumfeld Connector",
- .owner = THIS_MODULE,
- .dai_link = snd_soc_raumfeld_connector_dai,
- .num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai),
- .suspend_post = raumfeld_analog_suspend,
- .resume_pre = raumfeld_analog_resume,
-};
-
-static struct snd_soc_card snd_soc_raumfeld_speaker = {
- .name = "Raumfeld Speaker",
- .owner = THIS_MODULE,
- .dai_link = snd_soc_raumfeld_speaker_dai,
- .num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai),
- .suspend_post = raumfeld_analog_suspend,
- .resume_pre = raumfeld_analog_resume,
-};
-
-static struct platform_device *raumfeld_audio_device;
-
-static int __init raumfeld_audio_init(void)
-{
- int ret;
-
- if (!machine_is_raumfeld_speaker() &&
- !machine_is_raumfeld_connector())
- return 0;
-
- max9486_client = i2c_new_device(i2c_get_adapter(0),
- &max9486_hwmon_info);
-
- if (!max9486_client)
- return -ENOMEM;
-
- set_max9485_clk(MAX9485_MCLK_FREQ_122880);
-
- /* Register analog device */
- raumfeld_audio_device = platform_device_alloc("soc-audio", 0);
- if (!raumfeld_audio_device)
- return -ENOMEM;
-
- if (machine_is_raumfeld_speaker())
- platform_set_drvdata(raumfeld_audio_device,
- &snd_soc_raumfeld_speaker);
-
- if (machine_is_raumfeld_connector())
- platform_set_drvdata(raumfeld_audio_device,
- &snd_soc_raumfeld_connector);
-
- ret = platform_device_add(raumfeld_audio_device);
- if (ret < 0) {
- platform_device_put(raumfeld_audio_device);
- return ret;
- }
-
- raumfeld_enable_audio(true);
- return 0;
-}
-
-static void __exit raumfeld_audio_exit(void)
-{
- raumfeld_enable_audio(false);
-
- platform_device_unregister(raumfeld_audio_device);
-
- i2c_unregister_device(max9486_client);
-
- gpio_free(GPIO_MCLK_RESET);
- gpio_free(GPIO_CODEC_RESET);
- gpio_free(GPIO_SPDIF_RESET);
-}
-
-module_init(raumfeld_audio_init);
-module_exit(raumfeld_audio_exit);
-
-/* Module information */
-MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("Raumfeld audio SoC");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index b00222620fd0..2ea21a2091ee 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
*
@@ -6,12 +7,6 @@
*
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.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>
@@ -19,13 +14,12 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <asm/mach-types.h>
-#include <mach/spitz.h>
#include "../codecs/wm8750.h"
#include "pxa2xx-i2s.h"
@@ -42,7 +36,7 @@
static int spitz_jack_func;
static int spitz_spk_func;
-static int spitz_mic_gpio;
+static struct gpio_desc *gpiod_mic, *gpiod_mute_l, *gpiod_mute_r;
static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
{
@@ -61,8 +55,8 @@ static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
- gpio_set_value(SPITZ_GPIO_MUTE_L, 1);
- gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
+ gpiod_set_value(gpiod_mute_l, 1);
+ gpiod_set_value(gpiod_mute_r, 1);
break;
case SPITZ_MIC:
/* enable mic jack and bias, mute hp */
@@ -70,8 +64,8 @@ static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
- gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
- gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
+ gpiod_set_value(gpiod_mute_l, 0);
+ gpiod_set_value(gpiod_mute_r, 0);
break;
case SPITZ_LINE:
/* enable line jack, disable mic bias and mute hp */
@@ -79,8 +73,8 @@ static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
- gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
- gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
+ gpiod_set_value(gpiod_mute_l, 0);
+ gpiod_set_value(gpiod_mute_r, 0);
break;
case SPITZ_HEADSET:
/* enable and unmute headset jack enable mic bias, mute L hp */
@@ -88,8 +82,8 @@ static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
- gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
- gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
+ gpiod_set_value(gpiod_mute_l, 0);
+ gpiod_set_value(gpiod_mute_r, 1);
break;
case SPITZ_HP_OFF:
@@ -98,8 +92,8 @@ static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
- gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
- gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
+ gpiod_set_value(gpiod_mute_l, 0);
+ gpiod_set_value(gpiod_mute_r, 0);
break;
}
@@ -110,10 +104,11 @@ static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
static int spitz_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
/* check the jack status at stream startup */
- spitz_ext_control(&rtd->card->dapm);
+ spitz_ext_control(dapm);
return 0;
}
@@ -121,9 +116,9 @@ static int spitz_startup(struct snd_pcm_substream *substream)
static int spitz_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
unsigned int clk = 0;
int ret = 0;
@@ -156,7 +151,7 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops spitz_ops = {
+static const struct snd_soc_ops spitz_ops = {
.startup = spitz_startup,
.hw_params = spitz_hw_params,
};
@@ -164,7 +159,7 @@ static struct snd_soc_ops spitz_ops = {
static int spitz_get_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- ucontrol->value.integer.value[0] = spitz_jack_func;
+ ucontrol->value.enumerated.item[0] = spitz_jack_func;
return 0;
}
@@ -172,19 +167,20 @@ static int spitz_set_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
- if (spitz_jack_func == ucontrol->value.integer.value[0])
+ if (spitz_jack_func == ucontrol->value.enumerated.item[0])
return 0;
- spitz_jack_func = ucontrol->value.integer.value[0];
- spitz_ext_control(&card->dapm);
+ spitz_jack_func = ucontrol->value.enumerated.item[0];
+ spitz_ext_control(dapm);
return 1;
}
static int spitz_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- ucontrol->value.integer.value[0] = spitz_spk_func;
+ ucontrol->value.enumerated.item[0] = spitz_spk_func;
return 0;
}
@@ -192,19 +188,20 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
- if (spitz_spk_func == ucontrol->value.integer.value[0])
+ if (spitz_spk_func == ucontrol->value.enumerated.item[0])
return 0;
- spitz_spk_func = ucontrol->value.integer.value[0];
- spitz_ext_control(&card->dapm);
+ spitz_spk_func = ucontrol->value.enumerated.item[0];
+ spitz_ext_control(dapm);
return 1;
}
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(gpiod_mic, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -230,8 +227,8 @@ static const struct snd_soc_dapm_route spitz_audio_map[] = {
{"Headset Jack", NULL, "ROUT1"},
/* ext speaker connected to LOUT2, ROUT2 */
- {"Ext Spk", NULL , "ROUT2"},
- {"Ext Spk", NULL , "LOUT2"},
+ {"Ext Spk", NULL, "ROUT2"},
+ {"Ext Spk", NULL, "LOUT2"},
/* mic is connected to input 1 - with bias */
{"LINPUT1", NULL, "Mic Bias"},
@@ -241,9 +238,9 @@ static const struct snd_soc_dapm_route spitz_audio_map[] = {
{"LINPUT1", NULL, "Line Jack"},
};
-static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
- "Off"};
-static const char *spk_function[] = {"On", "Off"};
+static const char * const jack_function[] = {"Headphone", "Mic", "Line",
+ "Headset", "Off"};
+static const char * const spk_function[] = {"On", "Off"};
static const struct soc_enum spitz_enum[] = {
SOC_ENUM_SINGLE_EXT(5, jack_function),
SOC_ENUM_SINGLE_EXT(2, spk_function),
@@ -257,16 +254,18 @@ static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
};
/* spitz digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(wm8750,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
static struct snd_soc_dai_link spitz_dai = {
.name = "wm8750",
.stream_name = "WM8750",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "wm8750-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm8750.0-001b",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ops = &spitz_ops,
+ SND_SOC_DAILINK_REG(wm8750),
};
/* spitz audio machine driver */
@@ -290,49 +289,32 @@ static int spitz_probe(struct platform_device *pdev)
struct snd_soc_card *card = &snd_soc_spitz;
int ret;
- if (machine_is_akita())
- spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
- else
- spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
-
- ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
- if (ret)
- goto err1;
-
- ret = gpio_direction_output(spitz_mic_gpio, 0);
- if (ret)
- goto err2;
+ gpiod_mic = devm_gpiod_get(&pdev->dev, "mic", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod_mic))
+ return PTR_ERR(gpiod_mic);
+ gpiod_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod_mute_l))
+ return PTR_ERR(gpiod_mute_l);
+ gpiod_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod_mute_r))
+ return PTR_ERR(gpiod_mute_r);
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
- goto err2;
- }
-
- return 0;
-err2:
- gpio_free(spitz_mic_gpio);
-err1:
return ret;
}
-static int spitz_remove(struct platform_device *pdev)
-{
- gpio_free(spitz_mic_gpio);
- return 0;
-}
-
static struct platform_driver spitz_driver = {
.driver = {
.name = "spitz-audio",
.pm = &snd_soc_pm_ops,
},
.probe = spitz_probe,
- .remove = spitz_remove,
};
module_platform_driver(spitz_driver);
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
deleted file mode 100644
index 49518dd642aa..000000000000
--- a/sound/soc/pxa/tosa.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * tosa.c -- SoC audio for Tosa
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- *
- * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
- * Richard Purdie <richard@openedhand.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.
- *
- * GPIO's
- * 1 - Jack Insertion
- * 5 - Hookswitch (headset answer/hang up switch)
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/tosa.h>
-#include <mach/audio.h>
-
-#include "../codecs/wm9712.h"
-#include "pxa2xx-ac97.h"
-
-#define TOSA_HP 0
-#define TOSA_MIC_INT 1
-#define TOSA_HEADSET 2
-#define TOSA_HP_OFF 3
-#define TOSA_SPK_ON 0
-#define TOSA_SPK_OFF 1
-
-static int tosa_jack_func;
-static int tosa_spk_func;
-
-static void tosa_ext_control(struct snd_soc_dapm_context *dapm)
-{
-
- snd_soc_dapm_mutex_lock(dapm);
-
- /* set up jack connection */
- switch (tosa_jack_func) {
- case TOSA_HP:
- snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
- break;
- case TOSA_MIC_INT:
- snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
- break;
- case TOSA_HEADSET:
- snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
- break;
- }
-
- if (tosa_spk_func == TOSA_SPK_ON)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-}
-
-static int tosa_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* check the jack status at stream startup */
- tosa_ext_control(&rtd->card->dapm);
-
- return 0;
-}
-
-static struct snd_soc_ops tosa_ops = {
- .startup = tosa_startup,
-};
-
-static int tosa_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = tosa_jack_func;
- return 0;
-}
-
-static int tosa_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (tosa_jack_func == ucontrol->value.integer.value[0])
- return 0;
-
- tosa_jack_func = ucontrol->value.integer.value[0];
- tosa_ext_control(&card->dapm);
- return 1;
-}
-
-static int tosa_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = tosa_spk_func;
- return 0;
-}
-
-static int tosa_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (tosa_spk_func == ucontrol->value.integer.value[0])
- return 0;
-
- tosa_spk_func = ucontrol->value.integer.value[0];
- tosa_ext_control(&card->dapm);
- return 1;
-}
-
-/* tosa dapm event handlers */
-static int tosa_hp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
- return 0;
-}
-
-/* tosa machine dapm widgets */
-static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
-SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
-SND_SOC_DAPM_HP("Headset Jack", NULL),
-SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
-SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-/* tosa audio map */
-static const struct snd_soc_dapm_route audio_map[] = {
-
- /* headphone connected to HPOUTL, HPOUTR */
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Headphone Jack", NULL, "HPOUTR"},
-
- /* ext speaker connected to LOUT2, ROUT2 */
- {"Speaker", NULL, "LOUT2"},
- {"Speaker", NULL, "ROUT2"},
-
- /* internal mic is connected to mic1, mic2 differential - with bias */
- {"MIC1", NULL, "Mic Bias"},
- {"MIC2", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Mic (Internal)"},
-
- /* headset is connected to HPOUTR, and LINEINR with bias */
- {"Headset Jack", NULL, "HPOUTR"},
- {"LINEINR", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Headset Jack"},
-};
-
-static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
- "Off"};
-static const char *spk_function[] = {"On", "Off"};
-static const struct soc_enum tosa_enum[] = {
- SOC_ENUM_SINGLE_EXT(5, jack_function),
- SOC_ENUM_SINGLE_EXT(2, spk_function),
-};
-
-static const struct snd_kcontrol_new tosa_controls[] = {
- SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
- tosa_set_jack),
- SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
- tosa_set_spk),
-};
-
-static struct snd_soc_dai_link tosa_dai[] = {
-{
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9712-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9712-codec",
- .ops = &tosa_ops,
-},
-{
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name = "wm9712-aux",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm9712-codec",
- .ops = &tosa_ops,
-},
-};
-
-static struct snd_soc_card tosa = {
- .name = "Tosa",
- .owner = THIS_MODULE,
- .dai_link = tosa_dai,
- .num_links = ARRAY_SIZE(tosa_dai),
-
- .controls = tosa_controls,
- .num_controls = ARRAY_SIZE(tosa_controls),
- .dapm_widgets = tosa_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static int tosa_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &tosa;
- int ret;
-
- ret = gpio_request_one(TOSA_GPIO_L_MUTE, GPIOF_OUT_INIT_LOW,
- "Headphone Jack");
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- gpio_free(TOSA_GPIO_L_MUTE);
- }
- return ret;
-}
-
-static int tosa_remove(struct platform_device *pdev)
-{
- gpio_free(TOSA_GPIO_L_MUTE);
- return 0;
-}
-
-static struct platform_driver tosa_driver = {
- .driver = {
- .name = "tosa-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = tosa_probe,
- .remove = tosa_remove,
-};
-
-module_platform_driver(tosa_driver);
-
-/* Module information */
-MODULE_AUTHOR("Richard Purdie");
-MODULE_DESCRIPTION("ALSA SoC Tosa");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:tosa-audio");
diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c
deleted file mode 100644
index 65c20f779177..000000000000
--- a/sound/soc/pxa/ttc-dkb.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * linux/sound/soc/pxa/ttc_dkb.c
- *
- * Copyright (C) 2012 Marvell International 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.
- *
- * 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 <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <asm/mach-types.h>
-#include <sound/pcm_params.h>
-#include "../codecs/88pm860x-codec.h"
-
-static struct snd_soc_jack hs_jack, mic_jack;
-
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, },
-};
-
-static struct snd_soc_jack_pin mic_jack_pins[] = {
- { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, },
-};
-
-/* ttc machine dapm widgets */
-static const struct snd_soc_dapm_widget ttc_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headset Stereophone", NULL),
- SND_SOC_DAPM_LINE("Lineout Out 1", NULL),
- SND_SOC_DAPM_LINE("Lineout Out 2", NULL),
- SND_SOC_DAPM_SPK("Ext Speaker", NULL),
- SND_SOC_DAPM_MIC("Ext Mic 1", NULL),
- SND_SOC_DAPM_MIC("Headset Mic 2", NULL),
- SND_SOC_DAPM_MIC("Ext Mic 3", NULL),
-};
-
-/* ttc machine audio map */
-static const struct snd_soc_dapm_route ttc_audio_map[] = {
- {"Headset Stereophone", NULL, "HS1"},
- {"Headset Stereophone", NULL, "HS2"},
-
- {"Ext Speaker", NULL, "LSP"},
- {"Ext Speaker", NULL, "LSN"},
-
- {"Lineout Out 1", NULL, "LINEOUT1"},
- {"Lineout Out 2", NULL, "LINEOUT2"},
-
- {"MIC1P", NULL, "Mic1 Bias"},
- {"MIC1N", NULL, "Mic1 Bias"},
- {"Mic1 Bias", NULL, "Ext Mic 1"},
-
- {"MIC2P", NULL, "Mic1 Bias"},
- {"MIC2N", NULL, "Mic1 Bias"},
- {"Mic1 Bias", NULL, "Headset Mic 2"},
-
- {"MIC3P", NULL, "Mic3 Bias"},
- {"MIC3N", NULL, "Mic3 Bias"},
- {"Mic3 Bias", NULL, "Ext Mic 3"},
-};
-
-static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
-
- /* Headset jack detection */
- snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
- &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
- snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE,
- &mic_jack, mic_jack_pins,
- ARRAY_SIZE(mic_jack_pins));
-
- /* headphone, microphone detection & headset short detection */
- pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE,
- SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2);
- pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE);
-
- return 0;
-}
-
-/* ttc/td-dkb digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = {
-{
- .name = "88pm860x i2s",
- .stream_name = "audio playback",
- .codec_name = "88pm860x-codec",
- .platform_name = "mmp-pcm-audio",
- .cpu_dai_name = "pxa-ssp-dai.1",
- .codec_dai_name = "88pm860x-i2s",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .init = ttc_pm860x_init,
-},
-};
-
-/* ttc/td audio machine driver */
-static struct snd_soc_card ttc_dkb_card = {
- .name = "ttc-dkb-hifi",
- .owner = THIS_MODULE,
- .dai_link = ttc_pm860x_hifi_dai,
- .num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai),
-
- .dapm_widgets = ttc_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ttc_dapm_widgets),
- .dapm_routes = ttc_audio_map,
- .num_dapm_routes = ARRAY_SIZE(ttc_audio_map),
-};
-
-static int ttc_dkb_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &ttc_dkb_card;
- int ret;
-
- card->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
-
- return ret;
-}
-
-static struct platform_driver ttc_dkb_driver = {
- .driver = {
- .name = "ttc-dkb-audio",
- .pm = &snd_soc_pm_ops,
- },
- .probe = ttc_dkb_probe,
-};
-
-module_platform_driver(ttc_dkb_driver);
-
-/* Module information */
-MODULE_AUTHOR("Qiao Zhou, <zhouqiao@marvell.com>");
-MODULE_DESCRIPTION("ALSA SoC TTC DKB");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ttc-dkb-audio");
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c
deleted file mode 100644
index 990b1aa6d7f6..000000000000
--- a/sound/soc/pxa/z2.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * linux/sound/soc/pxa/z2.c
- *
- * SoC Audio driver for Aeronix Zipit Z2
- *
- * Copyright (C) 2009 Ken McGuire <kenm@desertweyr.com>
- * Copyright (C) 2010 Marek Vasut <marek.vasut@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/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/audio.h>
-#include <mach/z2.h>
-
-#include "../codecs/wm8750.h"
-#include "pxa2xx-i2s.h"
-
-static struct snd_soc_card snd_soc_z2;
-
-static int z2_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret = 0;
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 48000:
- case 96000:
- clk = 12288000;
- break;
- case 11025:
- case 22050:
- case 44100:
- clk = 11289600;
- break;
- }
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set the I2S system clock as input (unused) */
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_jack hs_jack;
-
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "Ext Spk",
- .mask = SND_JACK_HEADPHONE,
- .invert = 1
- },
-};
-
-/* Headset jack detection gpios */
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
- {
- .gpio = GPIO37_ZIPITZ2_HEADSET_DETECT,
- .name = "hsdet-gpio",
- .report = SND_JACK_HEADSET,
- .debounce_time = 200,
- .invert = 1,
- },
-};
-
-/* z2 machine dapm widgets */
-static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-
- /* headset is a mic and mono headphone */
- SND_SOC_DAPM_HP("Headset Jack", NULL),
-};
-
-/* Z2 machine audio_map */
-static const struct snd_soc_dapm_route z2_audio_map[] = {
-
- /* headphone connected to LOUT1, ROUT1 */
- {"Headphone Jack", NULL, "LOUT1"},
- {"Headphone Jack", NULL, "ROUT1"},
-
- /* ext speaker connected to LOUT2, ROUT2 */
- {"Ext Spk", NULL , "ROUT2"},
- {"Ext Spk", NULL , "LOUT2"},
-
- /* mic is connected to R input 2 - with bias */
- {"RINPUT2", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Mic Jack"},
-};
-
-/*
- * Logic for a wm8750 as connected on a Z2 Device
- */
-static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
-{
- int ret;
-
- /* Jack detection API stuff */
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
- &hs_jack, hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
- if (ret)
- goto err;
-
- ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- return ret;
-}
-
-static struct snd_soc_ops z2_ops = {
- .hw_params = z2_hw_params,
-};
-
-/* z2 digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link z2_dai = {
- .name = "wm8750",
- .stream_name = "WM8750",
- .cpu_dai_name = "pxa2xx-i2s",
- .codec_dai_name = "wm8750-hifi",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "wm8750.0-001b",
- .init = z2_wm8750_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &z2_ops,
-};
-
-/* z2 audio machine driver */
-static struct snd_soc_card snd_soc_z2 = {
- .name = "Z2",
- .owner = THIS_MODULE,
- .dai_link = &z2_dai,
- .num_links = 1,
-
- .dapm_widgets = wm8750_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
- .dapm_routes = z2_audio_map,
- .num_dapm_routes = ARRAY_SIZE(z2_audio_map),
- .fully_routed = true,
-};
-
-static struct platform_device *z2_snd_device;
-
-static int __init z2_init(void)
-{
- int ret;
-
- if (!machine_is_zipit2())
- return -ENODEV;
-
- z2_snd_device = platform_device_alloc("soc-audio", -1);
- if (!z2_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(z2_snd_device, &snd_soc_z2);
- ret = platform_device_add(z2_snd_device);
-
- if (ret)
- platform_device_put(z2_snd_device);
-
- return ret;
-}
-
-static void __exit z2_exit(void)
-{
- platform_device_unregister(z2_snd_device);
-}
-
-module_init(z2_init);
-module_exit(z2_exit);
-
-MODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, "
- "Marek Vasut <marek.vasut@gmail.com>");
-MODULE_DESCRIPTION("ALSA SoC ZipitZ2");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
deleted file mode 100644
index 8f301c72ee5e..000000000000
--- a/sound/soc/pxa/zylonite.c
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * zylonite.c -- SoC audio for Zylonite
- *
- * 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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/clk.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "../codecs/wm9713.h"
-#include "pxa2xx-ac97.h"
-#include "pxa-ssp.h"
-
-/*
- * There is a physical switch SW15 on the board which changes the MCLK
- * for the WM9713 between the standard AC97 master clock and the
- * output of the CLK_POUT signal from the PXA.
- */
-static int clk_pout;
-module_param(clk_pout, int, 0);
-MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board).");
-
-static struct clk *pout;
-
-static struct snd_soc_card zylonite;
-
-static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Microphone", NULL),
- SND_SOC_DAPM_MIC("Handset Microphone", NULL),
- SND_SOC_DAPM_SPK("Multiactor", NULL),
- SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
-};
-
-/* Currently supported audio map */
-static const struct snd_soc_dapm_route audio_map[] = {
-
- /* Headphone output connected to HPL/HPR */
- { "Headphone", NULL, "HPL" },
- { "Headphone", NULL, "HPR" },
-
- /* On-board earpiece */
- { "Headset Earpiece", NULL, "OUT3" },
-
- /* Headphone mic */
- { "MIC2A", NULL, "Mic Bias" },
- { "Mic Bias", NULL, "Headset Microphone" },
-
- /* On-board mic */
- { "MIC1", NULL, "Mic Bias" },
- { "Mic Bias", NULL, "Handset Microphone" },
-
- /* Multiactor differentially connected over SPKL/SPKR */
- { "Multiactor", NULL, "SPKL" },
- { "Multiactor", NULL, "SPKR" },
-};
-
-static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd)
-{
- if (clk_pout)
- snd_soc_dai_set_pll(rtd->codec_dai, 0, 0,
- clk_get_rate(pout), 0);
-
- return 0;
-}
-
-static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int pll_out = 0;
- unsigned int wm9713_div = 0;
- int ret = 0;
- int rate = params_rate(params);
- int width = snd_pcm_format_physical_width(params_format(params));
-
- /* Only support ratios that we can generate neatly from the AC97
- * based master clock - in particular, this excludes 44.1kHz.
- * In most applications the voice DAC will be used for telephony
- * data so multiples of 8kHz will be the common case.
- */
- switch (rate) {
- case 8000:
- wm9713_div = 12;
- break;
- case 16000:
- wm9713_div = 6;
- break;
- case 48000:
- wm9713_div = 2;
- break;
- default:
- /* Don't support OSS emulation */
- return -EINVAL;
- }
-
- /* Add 1 to the width for the leading clock cycle */
- pll_out = rate * (width + 1) * 8;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
- if (ret < 0)
- return ret;
-
- if (clk_pout)
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV,
- WM9713_PCMDIV(wm9713_div));
- else
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
- WM9713_PCMDIV(wm9713_div));
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops zylonite_voice_ops = {
- .hw_params = zylonite_voice_hw_params,
-};
-
-static struct snd_soc_dai_link zylonite_dai[] = {
-{
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .codec_name = "wm9713-codec",
- .platform_name = "pxa-pcm-audio",
- .cpu_dai_name = "pxa2xx-ac97",
- .codec_dai_name = "wm9713-hifi",
- .init = zylonite_wm9713_init,
-},
-{
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .codec_name = "wm9713-codec",
- .platform_name = "pxa-pcm-audio",
- .cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name = "wm9713-aux",
-},
-{
- .name = "WM9713 Voice",
- .stream_name = "WM9713 Voice",
- .codec_name = "wm9713-codec",
- .platform_name = "pxa-pcm-audio",
- .cpu_dai_name = "pxa-ssp-dai.2",
- .codec_dai_name = "wm9713-voice",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &zylonite_voice_ops,
-},
-};
-
-static int zylonite_probe(struct snd_soc_card *card)
-{
- int ret;
-
- if (clk_pout) {
- pout = clk_get(NULL, "CLK_POUT");
- if (IS_ERR(pout)) {
- dev_err(card->dev, "Unable to obtain CLK_POUT: %ld\n",
- PTR_ERR(pout));
- return PTR_ERR(pout);
- }
-
- ret = clk_enable(pout);
- if (ret != 0) {
- dev_err(card->dev, "Unable to enable CLK_POUT: %d\n",
- ret);
- clk_put(pout);
- return ret;
- }
-
- dev_dbg(card->dev, "MCLK enabled at %luHz\n",
- clk_get_rate(pout));
- }
-
- return 0;
-}
-
-static int zylonite_remove(struct snd_soc_card *card)
-{
- if (clk_pout) {
- clk_disable(pout);
- clk_put(pout);
- }
-
- return 0;
-}
-
-static int zylonite_suspend_post(struct snd_soc_card *card)
-{
- if (clk_pout)
- clk_disable(pout);
-
- return 0;
-}
-
-static int zylonite_resume_pre(struct snd_soc_card *card)
-{
- int ret = 0;
-
- if (clk_pout) {
- ret = clk_enable(pout);
- if (ret != 0)
- dev_err(card->dev, "Unable to enable CLK_POUT: %d\n",
- ret);
- }
-
- return ret;
-}
-
-static struct snd_soc_card zylonite = {
- .name = "Zylonite",
- .owner = THIS_MODULE,
- .probe = &zylonite_probe,
- .remove = &zylonite_remove,
- .suspend_post = &zylonite_suspend_post,
- .resume_pre = &zylonite_resume_pre,
- .dai_link = zylonite_dai,
- .num_links = ARRAY_SIZE(zylonite_dai),
-
- .dapm_widgets = zylonite_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *zylonite_snd_ac97_device;
-
-static int __init zylonite_init(void)
-{
- int ret;
-
- zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
- if (!zylonite_snd_ac97_device)
- return -ENOMEM;
-
- platform_set_drvdata(zylonite_snd_ac97_device, &zylonite);
-
- ret = platform_device_add(zylonite_snd_ac97_device);
- if (ret != 0)
- platform_device_put(zylonite_snd_ac97_device);
-
- return ret;
-}
-
-static void __exit zylonite_exit(void)
-{
- platform_device_unregister(zylonite_snd_ac97_device);
-}
-
-module_init(zylonite_init);
-module_exit(zylonite_exit);
-
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 3cc252e55468..e6e24f3b9922 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -1,18 +1,29 @@
-config SND_SOC_QCOM
- tristate "ASoC support for QCOM platforms"
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig SND_SOC_QCOM
+ tristate "Qualcomm"
depends on ARCH_QCOM || COMPILE_TEST
help
- Say Y or M if you want to add support to use audio devices
- in Qualcomm Technologies SOC-based platforms.
+ Say Y or M if you want to add support to use audio devices
+ in Qualcomm Technologies SOC-based platforms.
+
+if SND_SOC_QCOM
config SND_SOC_LPASS_CPU
tristate
select REGMAP_MMIO
+config SND_SOC_LPASS_HDMI
+ tristate
+ select REGMAP_MMIO
+
config SND_SOC_LPASS_PLATFORM
tristate
select REGMAP_MMIO
+config SND_SOC_LPASS_CDC_DMA
+ tristate
+ select REGMAP_MMIO
+
config SND_SOC_LPASS_IPQ806X
tristate
select SND_SOC_LPASS_CPU
@@ -23,20 +34,222 @@ config SND_SOC_LPASS_APQ8016
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
+config SND_SOC_LPASS_SC7180
+ tristate
+ select SND_SOC_LPASS_CPU
+ select SND_SOC_LPASS_PLATFORM
+ select SND_SOC_LPASS_HDMI
+
+config SND_SOC_LPASS_SC7280
+ tristate
+ select SND_SOC_LPASS_CPU
+ select SND_SOC_LPASS_PLATFORM
+ select SND_SOC_LPASS_HDMI
+ select SND_SOC_LPASS_CDC_DMA
+
config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards"
- depends on SND_SOC_QCOM
+ depends on GPIOLIB
select SND_SOC_LPASS_IPQ806X
select SND_SOC_MAX98357A
help
- Say Y or M if you want add support for SoC audio on the
- Qualcomm Technologies IPQ806X-based Storm board.
+ Say Y or M if you want add support for SoC audio on the
+ Qualcomm Technologies IPQ806X-based Storm board.
config SND_SOC_APQ8016_SBC
tristate "SoC Audio support for APQ8016 SBC platforms"
- depends on SND_SOC_QCOM
select SND_SOC_LPASS_APQ8016
+ select SND_SOC_QCOM_COMMON
+ help
+ Support for Qualcomm Technologies LPASS audio block in
+ APQ8016 SOC-based systems.
+ Say Y if you want to use audio devices on MI2S.
+
+config SND_SOC_QCOM_COMMON
+ tristate
+
+config SND_SOC_QCOM_SDW
+ tristate
+
+config SND_SOC_QDSP6_COMMON
+ tristate
+
+config SND_SOC_QDSP6_CORE
+ tristate
+
+config SND_SOC_QDSP6_AFE
+ tristate
+
+config SND_SOC_QDSP6_AFE_DAI
+ tristate
+
+config SND_SOC_QDSP6_AFE_CLOCKS
+ tristate
+
+config SND_SOC_QDSP6_ADM
+ tristate
+
+config SND_SOC_QDSP6_ROUTING
+ tristate
+
+config SND_SOC_QDSP6_ASM
+ tristate
+
+config SND_SOC_QDSP6_ASM_DAI
+ select SND_SOC_COMPRESS
+ tristate
+
+config SND_SOC_QDSP6_APM_DAI
+ tristate
+ select SND_SOC_COMPRESS
+
+config SND_SOC_QDSP6_APM_LPASS_DAI
+ tristate
+
+config SND_SOC_QDSP6_APM
+ tristate
+ select SND_SOC_QDSP6_APM_DAI
+ select SND_SOC_QDSP6_APM_LPASS_DAI
+
+config SND_SOC_QDSP6_PRM_LPASS_CLOCKS
+ tristate
+
+config SND_SOC_QDSP6_PRM
+ tristate
+ select SND_SOC_QDSP6_PRM_LPASS_CLOCKS
+
+config SND_SOC_QCOM_OFFLOAD_UTILS
+ tristate
+
+config SND_SOC_QDSP6_USB
+ tristate "SoC ALSA USB offloading backing for QDSP6"
+ depends on SND_SOC_USB
+ select AUXILIARY_BUS
+ select SND_SOC_QCOM_OFFLOAD_UTILS
+
+ help
+ Adds support for USB offloading for QDSP6 ASoC
+ based platform sound cards. This will enable the
+ Q6USB DPCM backend DAI link, which will interact
+ with the SoC USB framework to initialize a session
+ with active USB SND devices.
+
+config SND_SOC_QDSP6
+ tristate "SoC ALSA audio driver for QDSP6"
+ depends on QCOM_APR
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6_COMMON
+ select SND_SOC_QDSP6_CORE
+ select SND_SOC_QDSP6_AFE
+ select SND_SOC_QDSP6_AFE_DAI
+ select SND_SOC_QDSP6_AFE_CLOCKS
+ select SND_SOC_QDSP6_ADM
+ select SND_SOC_QDSP6_ROUTING
+ select SND_SOC_QDSP6_ASM
+ select SND_SOC_QDSP6_ASM_DAI
+ select SND_SOC_TOPOLOGY
+ select SND_SOC_QDSP6_APM
+ select SND_SOC_QDSP6_PRM
help
- Support for Qualcomm Technologies LPASS audio block in
- APQ8016 SOC-based systems.
- Say Y if you want to use audio devices on MI2S.
+ To add support for MSM QDSP6 Soc Audio.
+ This will enable sound soc platform specific
+ audio drivers. This includes q6asm, q6adm,
+ q6afe interfaces to DSP using apr.
+
+config SND_SOC_MSM8996
+ tristate "SoC Machine driver for MSM8996 and APQ8096 boards"
+ depends on QCOM_APR
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ help
+ Support for Qualcomm Technologies LPASS audio block in
+ APQ8096 SoC-based systems.
+ Say Y if you want to use audio device on this SoCs
+
+config SND_SOC_SDM845
+ tristate "SoC Machine driver for SDM845 boards"
+ depends on QCOM_APR && I2C && SOUNDWIRE
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
+ select SND_SOC_RT5663
+ select SND_SOC_MAX98927
+ imply SND_SOC_CROS_EC_CODEC
+ help
+ To add support for audio on Qualcomm Technologies Inc.
+ SDM845 SoC-based systems.
+ Say Y if you want to use audio device on this SoCs.
+
+config SND_SOC_SM8250
+ tristate "SoC Machine driver for SM8250 boards"
+ depends on QCOM_APR && SOUNDWIRE
+ depends on COMMON_CLK
+ depends on SND_SOC_QCOM_OFFLOAD_UTILS || !SND_SOC_QCOM_OFFLOAD_UTILS
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
+ help
+ To add support for audio on Qualcomm Technologies Inc.
+ SM8250 SoC-based systems.
+ Say Y if you want to use audio device on this SoCs.
+
+config SND_SOC_SC8280XP
+ tristate "SoC Machine driver for SC8280XP boards"
+ depends on QCOM_APR && SOUNDWIRE
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
+ help
+ To add support for audio on Qualcomm Technologies Inc.
+ SC8280XP SoC-based systems.
+ Say Y if you want to use audio device on this SoCs.
+
+config SND_SOC_SC7180
+ tristate "SoC Machine driver for SC7180 boards"
+ depends on I2C && GPIOLIB
+ depends on SOUNDWIRE || SOUNDWIRE=n
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_LPASS_SC7180
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
+ select SND_SOC_ADAU7002
+ help
+ To add support for audio on Qualcomm Technologies Inc.
+ SC7180 SoC-based systems.
+ Say Y if you want to use audio device on this SoCs.
+
+config SND_SOC_SC7280
+ tristate "SoC Machine driver for SC7280 boards"
+ depends on I2C && SOUNDWIRE
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
+ select SND_SOC_LPASS_SC7280
+ select SND_SOC_MAX98357A
+ select SND_SOC_WCD938X_SDW
+ select SND_SOC_LPASS_MACRO_COMMON
+ imply SND_SOC_LPASS_RX_MACRO
+ imply SND_SOC_LPASS_TX_MACRO
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
+ help
+ Add support for audio on Qualcomm Technologies Inc.
+ SC7280 SoC-based systems.
+ Say Y or M if you want to use audio device on this SoCs.
+
+config SND_SOC_X1E80100
+ tristate "SoC Machine driver for X1E80100 boards"
+ depends on QCOM_APR && SOUNDWIRE
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
+ help
+ Add support for audio on Qualcomm Technologies Inc.
+ X1E80100 SoC-based systems.
+ Say Y or M if you want to use audio device on this SoCs.
+
+endif #SND_SOC_QCOM
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 79e5c50a8f71..985ce2ae286b 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -1,17 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0
# Platform
-snd-soc-lpass-cpu-objs := lpass-cpu.o
-snd-soc-lpass-platform-objs := lpass-platform.o
-snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
-snd-soc-lpass-apq8016-objs := lpass-apq8016.o
+snd-soc-lpass-cpu-y := lpass-cpu.o
+snd-soc-lpass-cdc-dma-y := lpass-cdc-dma.o
+snd-soc-lpass-hdmi-y := lpass-hdmi.o
+snd-soc-lpass-platform-y := lpass-platform.o
+snd-soc-lpass-ipq806x-y := lpass-ipq806x.o
+snd-soc-lpass-apq8016-y := lpass-apq8016.o
+snd-soc-lpass-sc7180-y := lpass-sc7180.o
+snd-soc-lpass-sc7280-y := lpass-sc7280.o
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
+obj-$(CONFIG_SND_SOC_LPASS_CDC_DMA) += snd-soc-lpass-cdc-dma.o
+obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
+obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o
+obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o
# Machine
-snd-soc-storm-objs := storm.o
-snd-soc-apq8016-sbc-objs := apq8016_sbc.o
+snd-soc-storm-y := storm.o
+snd-soc-apq8016-sbc-y := apq8016_sbc.o
+snd-soc-apq8096-y := apq8096.o
+snd-soc-sc7180-y := sc7180.o
+snd-soc-sc7280-y := sc7280.o
+snd-soc-sdm845-y := sdm845.o
+snd-soc-sm8250-y := sm8250.o
+snd-soc-sc8280xp-y := sc8280xp.o
+snd-soc-qcom-common-y := common.o
+snd-soc-qcom-sdw-y := sdw.o
+snd-soc-x1e80100-y := x1e80100.o
+snd-soc-qcom-offload-utils-objs := usb_offload_utils.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
+obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o
+obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o
+obj-$(CONFIG_SND_SOC_SC7280) += snd-soc-sc7280.o
+obj-$(CONFIG_SND_SOC_SC8280XP) += snd-soc-sc8280xp.o
+obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o
+obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
+obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
+obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o
+obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o
+obj-$(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) += snd-soc-qcom-offload-utils.o
+
+#DSP lib
+obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index 1efdf0088ecd..3023cf180a75 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 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.
- *
*/
#include <linux/device.h>
@@ -21,27 +12,60 @@
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <sound/jack.h>
#include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
#include <dt-bindings/sound/apq8016-lpass.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include "common.h"
+#include "qdsp6/q6afe.h"
+
+#define MI2S_COUNT (MI2S_QUATERNARY + 1)
struct apq8016_sbc_data {
+ struct snd_soc_card card;
void __iomem *mic_iomux;
void __iomem *spkr_iomux;
- struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
+ struct snd_soc_jack jack;
+ bool jack_setup;
+ int mi2s_clk_count[MI2S_COUNT];
};
+#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17)
#define MIC_CTRL_TLMM_SCLK_EN BIT(1)
#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16))
+#define SPKR_CTL_TLMM_MCLK_EN BIT(1)
+#define SPKR_CTL_TLMM_SCLK_EN BIT(2)
+#define SPKR_CTL_TLMM_DATA1_EN BIT(3)
+#define SPKR_CTL_TLMM_WS_OUT_SEL_MASK GENMASK(7, 6)
+#define SPKR_CTL_TLMM_WS_OUT_SEL_SEC BIT(6)
+#define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18)
+#define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18)
+#define DEFAULT_MCLK_RATE 9600000
+#define MI2S_BCLK_RATE 1536000
+
+static struct snd_soc_jack_pin apq8016_sbc_jack_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
-static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
+static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s)
{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_component *component;
struct snd_soc_card *card = rtd->card;
struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card);
- int rval = 0;
+ int i, rval;
+ u32 value;
- switch (cpu_dai->id) {
+ switch (mi2s) {
case MI2S_PRIMARY:
writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
pdata->spkr_iomux);
@@ -53,133 +77,258 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
MIC_CTRL_TLMM_SCLK_EN,
pdata->mic_iomux);
break;
+ case MI2S_SECONDARY:
+ /* Clear TLMM_WS_OUT_SEL and TLMM_WS_EN_SEL fields */
+ value = readl(pdata->spkr_iomux) &
+ ~(SPKR_CTL_TLMM_WS_OUT_SEL_MASK | SPKR_CTL_TLMM_WS_EN_SEL_MASK);
+ /* Configure the Sec MI2S to TLMM */
+ writel(value | SPKR_CTL_TLMM_MCLK_EN | SPKR_CTL_TLMM_SCLK_EN |
+ SPKR_CTL_TLMM_DATA1_EN | SPKR_CTL_TLMM_WS_OUT_SEL_SEC |
+ SPKR_CTL_TLMM_WS_EN_SEL_SEC, pdata->spkr_iomux);
+ break;
+ case MI2S_TERTIARY:
+ writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL |
+ MIC_CTRL_TLMM_SCLK_EN,
+ pdata->mic_iomux);
+
+ break;
default:
dev_err(card->dev, "unsupported cpu dai configuration\n");
- rval = -EINVAL;
- break;
+ return -EINVAL;
}
- return rval;
+ if (!pdata->jack_setup) {
+ struct snd_jack *jack;
+
+ rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4,
+ &pdata->jack,
+ apq8016_sbc_jack_pins,
+ ARRAY_SIZE(apq8016_sbc_jack_pins));
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headphone Jack\n");
+ return rval;
+ }
+
+ jack = pdata->jack.jack;
+
+ snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ pdata->jack_setup = true;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+
+ component = codec_dai->component;
+ /* Set default mclk for internal codec */
+ rval = snd_soc_component_set_sysclk(component, 0, 0, DEFAULT_MCLK_RATE,
+ SND_SOC_CLOCK_IN);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set mclk: %d\n", rval);
+ return rval;
+ }
+ rval = snd_soc_component_set_jack(component, &pdata->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ }
+
+ return 0;
+}
+
+static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return apq8016_dai_init(rtd, cpu_dai->id);
}
-static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
+static void apq8016_sbc_add_ops(struct snd_soc_card *card)
{
- struct device *dev = card->dev;
struct snd_soc_dai_link *link;
- struct device_node *np, *codec, *cpu, *node = dev->of_node;
- struct apq8016_sbc_data *data;
- int ret, num_links;
+ int i;
+
+ for_each_card_prelinks(card, i, link)
+ link->init = apq8016_sbc_dai_init;
+}
- ret = snd_soc_of_parse_card_name(card, "qcom,model");
- if (ret) {
- dev_err(dev, "Error parsing card name: %d\n", ret);
- return ERR_PTR(ret);
+static int qdsp6_dai_get_lpass_id(struct snd_soc_dai *cpu_dai)
+{
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ return MI2S_PRIMARY;
+ case SECONDARY_MI2S_RX:
+ case SECONDARY_MI2S_TX:
+ return MI2S_SECONDARY;
+ case TERTIARY_MI2S_RX:
+ case TERTIARY_MI2S_TX:
+ return MI2S_TERTIARY;
+ case QUATERNARY_MI2S_RX:
+ case QUATERNARY_MI2S_TX:
+ return MI2S_QUATERNARY;
+ default:
+ return -EINVAL;
}
+}
- /* Populate links */
- num_links = of_get_child_count(node);
+static int msm8916_qdsp6_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
- /* Allocate the private data and the DAI link array */
- data = devm_kzalloc(dev, sizeof(*data) + sizeof(*link) * num_links,
- GFP_KERNEL);
- if (!data)
- return ERR_PTR(-ENOMEM);
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+ return apq8016_dai_init(rtd, qdsp6_dai_get_lpass_id(cpu_dai));
+}
- card->dai_link = &data->dai_link[0];
- card->num_links = num_links;
+static int msm8916_qdsp6_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int mi2s, ret;
- link = data->dai_link;
+ mi2s = qdsp6_dai_get_lpass_id(cpu_dai);
+ if (mi2s < 0)
+ return mi2s;
- for_each_child_of_node(node, np) {
- cpu = of_get_child_by_name(np, "cpu");
- codec = of_get_child_by_name(np, "codec");
+ if (++data->mi2s_clk_count[mi2s] > 1)
+ return 0;
- if (!cpu || !codec) {
- dev_err(dev, "Can't find cpu/codec DT node\n");
- return ERR_PTR(-EINVAL);
- }
+ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, MI2S_BCLK_RATE, 0);
+ if (ret)
+ dev_err(card->dev, "Failed to enable LPAIF bit clk: %d\n", ret);
+ return ret;
+}
- link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
- if (!link->cpu_of_node) {
- dev_err(card->dev, "error getting cpu phandle\n");
- return ERR_PTR(-EINVAL);
- }
+static void msm8916_qdsp6_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int mi2s, ret;
- link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0);
- if (!link->codec_of_node) {
- dev_err(card->dev, "error getting codec phandle\n");
- return ERR_PTR(-EINVAL);
- }
+ mi2s = qdsp6_dai_get_lpass_id(cpu_dai);
+ if (mi2s < 0)
+ return;
- ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name);
- if (ret) {
- dev_err(card->dev, "error getting cpu dai name\n");
- return ERR_PTR(ret);
- }
+ if (--data->mi2s_clk_count[mi2s] > 0)
+ return;
- ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name);
- if (ret) {
- dev_err(card->dev, "error getting codec dai name\n");
- return ERR_PTR(ret);
- }
+ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, 0, 0);
+ if (ret)
+ dev_err(card->dev, "Failed to disable LPAIF bit clk: %d\n", ret);
+}
- link->platform_of_node = link->cpu_of_node;
- /* For now we only support playback */
- link->playback_only = true;
+static const struct snd_soc_ops msm8916_qdsp6_be_ops = {
+ .startup = msm8916_qdsp6_startup,
+ .shutdown = msm8916_qdsp6_shutdown,
+};
- ret = of_property_read_string(np, "link-name", &link->name);
- if (ret) {
- dev_err(card->dev, "error getting codec dai_link name\n");
- return ERR_PTR(ret);
- }
+static int msm8916_qdsp6_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- link->stream_name = link->name;
- link->init = apq8016_sbc_dai_init;
- link++;
- }
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
- return data;
+static void msm8916_qdsp6_add_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ /* Make it obvious to userspace that QDSP6 is used */
+ card->components = "qdsp6";
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm) {
+ link->init = msm8916_qdsp6_dai_init;
+ link->ops = &msm8916_qdsp6_be_ops;
+ link->be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup;
+ }
+ }
}
+static const struct snd_kcontrol_new apq8016_sbc_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Secondary Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+};
+
static int apq8016_sbc_platform_probe(struct platform_device *pdev)
{
+ void (*add_ops)(struct snd_soc_card *card);
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
struct apq8016_sbc_data *data;
- struct resource *res;
+ int ret;
- card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
- if (!card)
+ add_ops = device_get_match_data(&pdev->dev);
+ if (!add_ops)
+ return -EINVAL;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
+ card = &data->card;
card->dev = dev;
- data = apq8016_sbc_parse_of(card);
- if (IS_ERR(data)) {
- dev_err(&pdev->dev, "Error resolving dai links: %ld\n",
- PTR_ERR(data));
- return PTR_ERR(data);
- }
+ card->owner = THIS_MODULE;
+ card->dapm_widgets = apq8016_sbc_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets);
+ card->controls = apq8016_sbc_snd_controls;
+ card->num_controls = ARRAY_SIZE(apq8016_sbc_snd_controls);
+
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux");
- data->mic_iomux = devm_ioremap_resource(dev, res);
+ data->mic_iomux = devm_platform_ioremap_resource_byname(pdev, "mic-iomux");
if (IS_ERR(data->mic_iomux))
return PTR_ERR(data->mic_iomux);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux");
- data->spkr_iomux = devm_ioremap_resource(dev, res);
+ data->spkr_iomux = devm_platform_ioremap_resource_byname(pdev, "spkr-iomux");
if (IS_ERR(data->spkr_iomux))
return PTR_ERR(data->spkr_iomux);
- platform_set_drvdata(pdev, data);
snd_soc_card_set_drvdata(card, data);
+ add_ops(card);
return devm_snd_soc_register_card(&pdev->dev, card);
}
-static const struct of_device_id apq8016_sbc_device_id[] = {
- { .compatible = "qcom,apq8016-sbc-sndcard" },
+static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = {
+ { .compatible = "qcom,apq8016-sbc-sndcard", .data = apq8016_sbc_add_ops },
+ { .compatible = "qcom,msm8916-qdsp6-sndcard", .data = msm8916_qdsp6_add_ops },
{},
};
MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id);
@@ -195,4 +344,4 @@ module_platform_driver(apq8016_sbc_platform_driver);
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
new file mode 100644
index 000000000000..4f6594cc723c
--- /dev/null
+++ b/sound/soc/qcom/apq8096.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, Linaro Limited
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include "common.h"
+
+#define SLIM_MAX_TX_PORTS 16
+#define SLIM_MAX_RX_PORTS 16
+#define WCD9335_DEFAULT_MCLK_RATE 9600000
+
+static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret = 0;
+
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ pr_err("failed to get codec chan map, err:%d\n", ret);
+ goto end;
+ } else if (ret == -ENOTSUPP) {
+ return 0;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ rx_ch_cnt, rx_ch);
+ else
+ ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch,
+ 0, NULL);
+ if (ret != 0 && ret != -ENOTSUPP)
+ pr_err("Failed to set cpu chan map, err:%d\n", ret);
+ else if (ret == -ENOTSUPP)
+ ret = 0;
+end:
+ return ret;
+}
+
+static const struct snd_soc_ops apq8096_ops = {
+ .hw_params = msm_snd_hw_params,
+};
+
+static int apq8096_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ /*
+ * Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+ snd_soc_dai_set_sysclk(codec_dai, 0, WCD9335_DEFAULT_MCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+
+ return 0;
+}
+
+static void apq8096_add_be_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->be_hw_params_fixup = apq8096_be_hw_params_fixup;
+ link->init = apq8096_init;
+ link->ops = &apq8096_ops;
+ }
+ }
+}
+
+static int apq8096_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->driver_name = "apq8096";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ dev_set_drvdata(dev, card);
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ apq8096_add_be_ops(card);
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id msm_snd_apq8096_dt_match[] = {
+ {.compatible = "qcom,apq8096-sndcard"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_snd_apq8096_dt_match);
+
+static struct platform_driver msm_snd_apq8096_driver = {
+ .probe = apq8096_platform_probe,
+ .driver = {
+ .name = "msm-snd-apq8096",
+ .of_match_table = msm_snd_apq8096_dt_match,
+ },
+};
+module_platform_driver(msm_snd_apq8096_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
new file mode 100644
index 000000000000..7ee60a58a336
--- /dev/null
+++ b/sound/soc/qcom/common.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, Linaro Limited.
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <linux/input-event-codes.h>
+#include "common.h"
+
+#define NAME_SIZE 32
+
+static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("DP0 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP1 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP2 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP3 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP4 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP5 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP6 Jack", NULL),
+ SND_SOC_DAPM_SPK("DP7 Jack", NULL),
+};
+
+int qcom_snd_parse_of(struct snd_soc_card *card)
+{
+ struct device_node *np;
+ struct device_node *codec = NULL;
+ struct device_node *platform = NULL;
+ struct device_node *cpu = NULL;
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *link;
+ struct of_phandle_args args;
+ struct snd_soc_dai_link_component *dlc;
+ int ret, num_links;
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret == 0 && !card->name)
+ /* Deprecated, only for compatibility with old device trees */
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(dev, "Error parsing card name: %d\n", ret);
+ return ret;
+ }
+
+ if (of_property_present(dev->of_node, "widgets")) {
+ ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets");
+ if (ret)
+ return ret;
+ }
+
+ /* DAPM routes */
+ if (of_property_present(dev->of_node, "audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret)
+ return ret;
+ }
+ /* Deprecated, only for compatibility with old device trees */
+ if (of_property_present(dev->of_node, "qcom,audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+ if (ret)
+ return ret;
+ }
+
+ ret = snd_soc_of_parse_pin_switches(card, "pin-switches");
+ if (ret)
+ return ret;
+
+ ret = snd_soc_of_parse_aux_devs(card, "aux-devs");
+ if (ret)
+ return ret;
+
+ /* Populate links */
+ num_links = of_get_available_child_count(dev->of_node);
+
+ /* Allocate the DAI link array */
+ card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
+
+ card->num_links = num_links;
+ link = card->dai_link;
+
+ for_each_available_child_of_node(dev->of_node, np) {
+ dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc) {
+ ret = -ENOMEM;
+ goto err_put_np;
+ }
+
+ link->cpus = &dlc[0];
+ link->platforms = &dlc[1];
+
+ link->num_cpus = 1;
+ link->num_platforms = 1;
+
+ ret = of_property_read_string(np, "link-name", &link->name);
+ if (ret) {
+ dev_err(card->dev, "error getting codec dai_link name\n");
+ goto err_put_np;
+ }
+
+ cpu = of_get_child_by_name(np, "cpu");
+ platform = of_get_child_by_name(np, "platform");
+ codec = of_get_child_by_name(np, "codec");
+
+ if (!cpu) {
+ dev_err(dev, "%s: Can't find cpu DT node\n", link->name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0);
+ if (ret) {
+ dev_err_probe(card->dev, ret,
+ "%s: error getting cpu dai name\n", link->name);
+ goto err;
+ }
+
+ link->id = args.args[0];
+
+ if (platform) {
+ link->platforms->of_node = of_parse_phandle(platform,
+ "sound-dai",
+ 0);
+ if (!link->platforms->of_node) {
+ dev_err(card->dev, "%s: platform dai not found\n", link->name);
+ ret = -EINVAL;
+ goto err;
+ }
+ } else {
+ link->platforms->of_node = link->cpus->of_node;
+ }
+
+ if (codec) {
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+ if (ret < 0) {
+ dev_err_probe(card->dev, ret,
+ "%s: codec dai not found\n", link->name);
+ goto err;
+ }
+
+ if (platform) {
+ /* DPCM backend */
+ link->no_pcm = 1;
+ link->ignore_pmdown_time = 1;
+ }
+ } else {
+ /* DPCM frontend */
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+ link->dynamic = 1;
+ }
+
+ if (platform || !codec) {
+ /* DPCM */
+ link->ignore_suspend = 1;
+ link->nonatomic = 1;
+ }
+
+ link->stream_name = link->name;
+ link++;
+
+ of_node_put(cpu);
+ of_node_put(codec);
+ of_node_put(platform);
+ }
+
+ if (!card->dapm_widgets) {
+ card->dapm_widgets = qcom_jack_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(qcom_jack_snd_widgets);
+ }
+
+ return 0;
+err:
+ of_node_put(cpu);
+ of_node_put(codec);
+ of_node_put(platform);
+err_put_np:
+ of_node_put(np);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_parse_of);
+
+static struct snd_soc_jack_pin qcom_headset_jack_pins[] = {
+ /* Headset */
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ int rval, i;
+
+ if (!*jack_setup) {
+ rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_MECHANICAL |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5,
+ jack, qcom_headset_jack_pins,
+ ARRAY_SIZE(qcom_headset_jack_pins));
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headphone Jack\n");
+ return rval;
+ }
+
+ 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);
+ *jack_setup = true;
+ }
+
+ switch (cpu_dai->id) {
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_component_set_jack(codec_dai->component,
+ jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup);
+
+int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *dp_jack, int dp_pcm_id)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ char jack_name[NAME_SIZE];
+ int rval, i;
+
+ snprintf(jack_name, sizeof(jack_name), "DP%d Jack", dp_pcm_id);
+ rval = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, dp_jack);
+ if (rval)
+ return rval;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_component_set_jack(codec_dai->component, dp_jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_dp_jack_setup);
+
+MODULE_DESCRIPTION("ASoC Qualcomm helper functions");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
new file mode 100644
index 000000000000..1b8d3f90bffa
--- /dev/null
+++ b/sound/soc/qcom/common.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#ifndef __QCOM_SND_COMMON_H__
+#define __QCOM_SND_COMMON_H__
+
+#include <sound/soc.h>
+
+int qcom_snd_parse_of(struct snd_soc_card *card);
+int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup);
+int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *dp_jack, int id);
+
+
+#endif
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index 94efc01020c4..b8f23414eb77 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
- *
*/
@@ -50,7 +41,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
.channels_min = 1,
.channels_max = 8,
},
- .probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
[MI2S_SECONDARY] = {
@@ -71,7 +61,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
.channels_min = 1,
.channels_max = 8,
},
- .probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
[MI2S_TERTIARY] = {
@@ -92,7 +81,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
.channels_min = 1,
.channels_max = 8,
},
- .probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
[MI2S_QUATERNARY] = {
@@ -128,28 +116,40 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
.channels_min = 1,
.channels_max = 8,
},
- .probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
};
-static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+ int direction, unsigned int dai_id)
{
- struct lpass_variant *v = drvdata->variant;
- int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+ const struct lpass_variant *v = drvdata->variant;
+ int chan = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
v->rdma_channels);
- if (chan >= v->rdma_channels)
- return -EBUSY;
+ if (chan >= v->rdma_channels)
+ return -EBUSY;
+ } else {
+ chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+ v->wrdma_channel_start +
+ v->wrdma_channels,
+ v->wrdma_channel_start);
+
+ if (chan >= v->wrdma_channel_start + v->wrdma_channels)
+ return -EBUSY;
+ }
- set_bit(chan, &drvdata->rdma_ch_bit_map);
+ set_bit(chan, &drvdata->dma_ch_bit_map);
return chan;
}
-static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
{
- clear_bit(chan, &drvdata->rdma_ch_bit_map);
+ clear_bit(chan, &drvdata->dma_ch_bit_map);
return 0;
}
@@ -157,52 +157,73 @@ static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
static int apq8016_lpass_init(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ const struct lpass_variant *variant = drvdata->variant;
struct device *dev = &pdev->dev;
- int ret;
+ int ret, i;
+
+
+ drvdata->clks = devm_kcalloc(dev, variant->num_clks,
+ sizeof(*drvdata->clks), GFP_KERNEL);
+ if (!drvdata->clks)
+ return -ENOMEM;
+ drvdata->num_clks = variant->num_clks;
- drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");
- if (IS_ERR(drvdata->pcnoc_mport_clk)) {
- dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n",
- __func__, PTR_ERR(drvdata->pcnoc_mport_clk));
- return PTR_ERR(drvdata->pcnoc_mport_clk);
+ for (i = 0; i < drvdata->num_clks; i++)
+ drvdata->clks[i].id = variant->clk_name[i];
+
+ ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);
+ if (ret) {
+ dev_err(dev, "Failed to get clocks %d\n", ret);
+ return ret;
}
- ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);
+ ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
if (ret) {
- dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n",
- __func__, ret);
+ dev_err(dev, "apq8016 clk_enable failed\n");
return ret;
}
- drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");
- if (IS_ERR(drvdata->pcnoc_sway_clk)) {
- dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n",
- __func__, PTR_ERR(drvdata->pcnoc_sway_clk));
- return PTR_ERR(drvdata->pcnoc_sway_clk);
+ drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk");
+ if (IS_ERR(drvdata->ahbix_clk)) {
+ dev_err(dev, "error getting ahbix-clk: %ld\n",
+ PTR_ERR(drvdata->ahbix_clk));
+ ret = PTR_ERR(drvdata->ahbix_clk);
+ goto err_ahbix_clk;
}
- ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);
+ ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
if (ret) {
- dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n",
- __func__, ret);
- return ret;
+ dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret);
+ goto err_ahbix_clk;
+ }
+ dev_dbg(dev, "set ahbix_clk rate to %lu\n",
+ clk_get_rate(drvdata->ahbix_clk));
+
+ ret = clk_prepare_enable(drvdata->ahbix_clk);
+ if (ret) {
+ dev_err(dev, "error enabling ahbix_clk: %d\n", ret);
+ goto err_ahbix_clk;
}
return 0;
+
+err_ahbix_clk:
+ clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+ return ret;
}
static int apq8016_lpass_exit(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
- clk_disable_unprepare(drvdata->pcnoc_mport_clk);
- clk_disable_unprepare(drvdata->pcnoc_sway_clk);
+ clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+ clk_disable_unprepare(drvdata->ahbix_clk);
return 0;
}
-static struct lpass_variant apq8016_data = {
+static const struct lpass_variant apq8016_data = {
.i2sctrl_reg_base = 0x1000,
.i2sctrl_reg_stride = 0x1000,
.i2s_ports = 4,
@@ -212,17 +233,63 @@ static struct lpass_variant apq8016_data = {
.rdma_reg_base = 0x8400,
.rdma_reg_stride = 0x1000,
.rdma_channels = 2,
- .rdmactl_audif_start = 1,
+ .dmactl_audif_start = 1,
+ .wrdma_reg_base = 0xB000,
+ .wrdma_reg_stride = 0x1000,
+ .wrdma_channel_start = 5,
+ .wrdma_channels = 2,
+ .loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000),
+ .spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000),
+ .spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000),
+ .spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000),
+ .micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000),
+ .micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000),
+ .micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000),
+ .wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000),
+ .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 4, 0x1000),
+
+ .rdma_dyncclk = REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000),
+ .rdma_bursten = REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000),
+ .rdma_wpscnt = REG_FIELD_ID(0x8400, 8, 10, 2, 0x1000),
+ .rdma_intf = REG_FIELD_ID(0x8400, 4, 7, 2, 0x1000),
+ .rdma_fifowm = REG_FIELD_ID(0x8400, 1, 3, 2, 0x1000),
+ .rdma_enable = REG_FIELD_ID(0x8400, 0, 0, 2, 0x1000),
+
+ .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 2, 0x1000),
+ .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 2, 0x1000),
+ .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 2, 0x1000),
+ .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 2, 0x1000),
+ .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 2, 0x1000),
+ .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 2, 0x1000),
+
+ .clk_name = (const char*[]) {
+ "pcnoc-mport-clk",
+ "pcnoc-sway-clk",
+ },
+ .num_clks = 2,
.dai_driver = apq8016_lpass_cpu_dai_driver,
.num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+ .dai_osr_clk_names = (const char *[]) {
+ "mi2s-osr-clk0",
+ "mi2s-osr-clk1",
+ "mi2s-osr-clk2",
+ "mi2s-osr-clk3",
+ },
+ .dai_bit_clk_names = (const char *[]) {
+ "mi2s-bit-clk0",
+ "mi2s-bit-clk1",
+ "mi2s-bit-clk2",
+ "mi2s-bit-clk3",
+ },
.init = apq8016_lpass_init,
.exit = apq8016_lpass_exit,
.alloc_dma_channel = apq8016_lpass_alloc_dma_channel,
.free_dma_channel = apq8016_lpass_free_dma_channel,
};
-static const struct of_device_id apq8016_lpass_cpu_device_id[] = {
+static const struct of_device_id apq8016_lpass_cpu_device_id[] __maybe_unused = {
{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
+ { .compatible = "qcom,apq8016-lpass-cpu", .data = &apq8016_data },
{}
};
MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
@@ -233,10 +300,10 @@ static struct platform_driver apq8016_lpass_cpu_platform_driver = {
.of_match_table = of_match_ptr(apq8016_lpass_cpu_device_id),
},
.probe = asoc_qcom_lpass_cpu_platform_probe,
- .remove = asoc_qcom_lpass_cpu_platform_remove,
+ .remove = asoc_qcom_lpass_cpu_platform_remove,
};
module_platform_driver(apq8016_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
new file mode 100644
index 000000000000..2dc8c75c4bf0
--- /dev/null
+++ b/sound/soc/qcom/lpass-cdc-dma.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 The Linux Foundation. All rights reserved.
+ *
+ * lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS
+ */
+
+#include <dt-bindings/sound/qcom,lpass.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+#define CODEC_MEM_HZ_NORMAL 153600000
+
+enum codec_dma_interfaces {
+ LPASS_CDC_DMA_INTERFACE1 = 1,
+ LPASS_CDC_DMA_INTERFACE2,
+ LPASS_CDC_DMA_INTERFACE3,
+ LPASS_CDC_DMA_INTERFACE4,
+ LPASS_CDC_DMA_INTERFACE5,
+ LPASS_CDC_DMA_INTERFACE6,
+ LPASS_CDC_DMA_INTERFACE7,
+ LPASS_CDC_DMA_INTERFACE8,
+ LPASS_CDC_DMA_INTERFACE9,
+ LPASS_CDC_DMA_INTERFACE10,
+};
+
+static void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
+ struct lpaif_dmactl **dmactl, int *id)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ switch (dai_id) {
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ *dmactl = drvdata->rxtx_rd_dmactl;
+ *id = pcm_data->dma_ch;
+ break;
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ *dmactl = drvdata->rxtx_wr_dmactl;
+ *id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ *dmactl = drvdata->va_wr_dmactl;
+ *id = pcm_data->dma_ch - v->va_wrdma_channel_start;
+ break;
+ default:
+ dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id);
+ break;
+ }
+}
+
+static int __lpass_get_codec_dma_intf_type(int dai_id)
+{
+ int ret;
+
+ switch (dai_id) {
+ case LPASS_CDC_DMA_RX0:
+ case LPASS_CDC_DMA_TX0:
+ case LPASS_CDC_DMA_VA_TX0:
+ ret = LPASS_CDC_DMA_INTERFACE1;
+ break;
+ case LPASS_CDC_DMA_RX1:
+ case LPASS_CDC_DMA_TX1:
+ case LPASS_CDC_DMA_VA_TX1:
+ ret = LPASS_CDC_DMA_INTERFACE2;
+ break;
+ case LPASS_CDC_DMA_RX2:
+ case LPASS_CDC_DMA_TX2:
+ case LPASS_CDC_DMA_VA_TX2:
+ ret = LPASS_CDC_DMA_INTERFACE3;
+ break;
+ case LPASS_CDC_DMA_RX3:
+ case LPASS_CDC_DMA_TX3:
+ case LPASS_CDC_DMA_VA_TX3:
+ ret = LPASS_CDC_DMA_INTERFACE4;
+ break;
+ case LPASS_CDC_DMA_RX4:
+ case LPASS_CDC_DMA_TX4:
+ case LPASS_CDC_DMA_VA_TX4:
+ ret = LPASS_CDC_DMA_INTERFACE5;
+ break;
+ case LPASS_CDC_DMA_RX5:
+ case LPASS_CDC_DMA_TX5:
+ case LPASS_CDC_DMA_VA_TX5:
+ ret = LPASS_CDC_DMA_INTERFACE6;
+ break;
+ case LPASS_CDC_DMA_RX6:
+ case LPASS_CDC_DMA_TX6:
+ case LPASS_CDC_DMA_VA_TX6:
+ ret = LPASS_CDC_DMA_INTERFACE7;
+ break;
+ case LPASS_CDC_DMA_RX7:
+ case LPASS_CDC_DMA_TX7:
+ case LPASS_CDC_DMA_VA_TX7:
+ ret = LPASS_CDC_DMA_INTERFACE8;
+ break;
+ case LPASS_CDC_DMA_RX8:
+ case LPASS_CDC_DMA_TX8:
+ case LPASS_CDC_DMA_VA_TX8:
+ ret = LPASS_CDC_DMA_INTERFACE9;
+ break;
+ case LPASS_CDC_DMA_RX9:
+ ret = LPASS_CDC_DMA_INTERFACE10;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int __lpass_platform_codec_intf_init(struct snd_soc_dai *dai,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpaif_dmactl *dmactl = NULL;
+ struct device *dev = soc_runtime->dev;
+ int ret, id, codec_intf;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ codec_intf = __lpass_get_codec_dma_intf_type(dai_id);
+ if (codec_intf < 0) {
+ dev_err(dev, "failed to get codec_intf: %d\n", codec_intf);
+ return codec_intf;
+ }
+
+ __lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
+ if (!dmactl)
+ return -EINVAL;
+
+ ret = regmap_fields_write(dmactl->codec_intf, id, codec_intf);
+ if (ret) {
+ dev_err(dev, "error writing to dmactl codec_intf reg field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->codec_fs_sel, id, 0x0);
+ if (ret) {
+ dev_err(dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->codec_fs_delay, id, 0x0);
+ if (ret) {
+ dev_err(dev, "error writing to dmactl codec_fs_delay reg field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->codec_pack, id, 0x1);
+ if (ret) {
+ dev_err(dev, "error writing to dmactl codec_pack reg field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_ON);
+ if (ret) {
+ dev_err(dev, "error writing to dmactl codec_enable reg field: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+
+ switch (dai->id) {
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);
+ clk_prepare_enable(drvdata->codec_mem0);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
+ clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL);
+ clk_prepare_enable(drvdata->va_mem0);
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id);
+ break;
+ }
+ return 0;
+}
+
+static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+
+ switch (dai->id) {
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ clk_disable_unprepare(drvdata->codec_mem0);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
+ clk_disable_unprepare(drvdata->va_mem0);
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id);
+ break;
+ }
+}
+
+static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct lpaif_dmactl *dmactl = NULL;
+ unsigned int regval;
+ unsigned int channels = params_channels(params);
+ int ret;
+ int id;
+
+ switch (channels) {
+ case 1:
+ regval = LPASS_CDC_DMA_INTF_ONE_CHANNEL;
+ break;
+ case 2:
+ regval = LPASS_CDC_DMA_INTF_TWO_CHANNEL;
+ break;
+ case 4:
+ regval = LPASS_CDC_DMA_INTF_FOUR_CHANNEL;
+ break;
+ case 6:
+ regval = LPASS_CDC_DMA_INTF_SIX_CHANNEL;
+ break;
+ case 8:
+ regval = LPASS_CDC_DMA_INTF_EIGHT_CHANNEL;
+ break;
+ default:
+ dev_err(soc_runtime->dev, "invalid PCM config\n");
+ return -EINVAL;
+ }
+
+ __lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
+ if (!dmactl)
+ return -EINVAL;
+
+ ret = regmap_fields_write(dmactl->codec_channel, id, regval);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to dmactl codec_channel reg field: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct lpaif_dmactl *dmactl = NULL;
+ int ret = 0, id;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ __lpass_platform_codec_intf_init(dai, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ __lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
+ if (!dmactl)
+ return -EINVAL;
+
+ ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_OFF);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to dmactl codec_enable reg: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd);
+ break;
+ }
+ return ret;
+}
+
+const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops = {
+ .startup = lpass_cdc_dma_daiops_startup,
+ .shutdown = lpass_cdc_dma_daiops_shutdown,
+ .hw_params = lpass_cdc_dma_daiops_hw_params,
+ .trigger = lpass_cdc_dma_daiops_trigger,
+};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops);
+
+MODULE_DESCRIPTION("QTi LPASS CDC DMA Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 00b6c9d039cf..242bc16da36d 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1,23 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS
*/
+#include <dt-bindings/sound/qcom,lpass.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -27,19 +19,63 @@
#include "lpass-lpaif-reg.h"
#include "lpass.h"
+#define LPASS_CPU_MAX_MI2S_LINES 4
+#define LPASS_CPU_I2S_SD0_MASK BIT(0)
+#define LPASS_CPU_I2S_SD1_MASK BIT(1)
+#define LPASS_CPU_I2S_SD2_MASK BIT(2)
+#define LPASS_CPU_I2S_SD3_MASK BIT(3)
+#define LPASS_CPU_I2S_SD0_1_MASK GENMASK(1, 0)
+#define LPASS_CPU_I2S_SD2_3_MASK GENMASK(3, 2)
+#define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0)
+#define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0)
+#define LPASS_REG_READ 1
+#define LPASS_REG_WRITE 0
+
+/*
+ * Channel maps for Quad channel playbacks on MI2S Secondary
+ */
+static struct snd_pcm_chmap_elem lpass_quad_chmaps[] = {
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_RL,
+ SNDRV_CHMAP_FR, SNDRV_CHMAP_RR } },
+ { }
+};
+static int lpass_cpu_init_i2sctl_bitfields(struct device *dev,
+ struct lpaif_i2sctl *i2sctl, struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+
+ i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback);
+ i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken);
+ i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode);
+ i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono);
+ i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen);
+ i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode);
+ i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono);
+ i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc);
+ i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth);
+
+ if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) ||
+ IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) ||
+ IS_ERR(i2sctl->micen) || IS_ERR(i2sctl->micmode) ||
+ IS_ERR(i2sctl->micmono) || IS_ERR(i2sctl->wssrc) ||
+ IS_ERR(i2sctl->bitwidth))
+ return -EINVAL;
+
+ return 0;
+}
+
static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
- if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
- return 0;
-
ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
if (ret)
- dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
- __func__, freq, ret);
+ dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n",
+ freq, ret);
return ret;
}
@@ -50,26 +86,17 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
- if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
- ret = clk_prepare_enable(
- drvdata->mi2s_osr_clk[dai->driver->id]);
- if (ret) {
- dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
- __func__, ret);
- return ret;
- }
+ ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]);
+ if (ret) {
+ dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);
+ return ret;
}
-
- ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
+ ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]);
if (ret) {
- dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
- __func__, ret);
- if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
- clk_disable_unprepare(
- drvdata->mi2s_osr_clk[dai->driver->id]);
+ dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+ clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
return ret;
}
-
return 0;
}
@@ -77,108 +104,262 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
+ unsigned int id = dai->driver->id;
+
+ clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
+ /*
+ * Ensure LRCLK is disabled even in device node validation.
+ * Will not impact if disabled in lpass_cpu_daiops_trigger()
+ * suspend.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE);
+ else
+ regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE);
+
+ /*
+ * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before
+ * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in
+ * lpass_cpu_daiops_prepare.
+ */
+ if (drvdata->mi2s_was_prepared[dai->driver->id]) {
+ drvdata->mi2s_was_prepared[dai->driver->id] = false;
+ clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
+ }
- clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
-
- if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
- clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
+ clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
}
static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
+ unsigned int id = dai->driver->id;
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
+ unsigned int mode;
unsigned int regval;
int bitwidth, ret;
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
- dev_err(dai->dev, "%s() invalid bit width given: %d\n",
- __func__, bitwidth);
+ dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth);
return bitwidth;
}
- regval = LPAIF_I2SCTL_LOOPBACK_DISABLE |
- LPAIF_I2SCTL_WSSRC_INTERNAL;
+ ret = regmap_fields_write(i2sctl->loopback, id,
+ LPAIF_I2SCTL_LOOPBACK_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "error updating loopback field: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_fields_write(i2sctl->wssrc, id,
+ LPAIF_I2SCTL_WSSRC_INTERNAL);
+ if (ret) {
+ dev_err(dai->dev, "error updating wssrc field: %d\n", ret);
+ return ret;
+ }
switch (bitwidth) {
case 16:
- regval |= LPAIF_I2SCTL_BITWIDTH_16;
+ regval = LPAIF_I2SCTL_BITWIDTH_16;
break;
case 24:
- regval |= LPAIF_I2SCTL_BITWIDTH_24;
+ regval = LPAIF_I2SCTL_BITWIDTH_24;
break;
case 32:
- regval |= LPAIF_I2SCTL_BITWIDTH_32;
+ regval = LPAIF_I2SCTL_BITWIDTH_32;
break;
default:
- dev_err(dai->dev, "%s() invalid bitwidth given: %d\n",
- __func__, bitwidth);
+ dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth);
+ return -EINVAL;
+ }
+
+ ret = regmap_fields_write(i2sctl->bitwidth, id, regval);
+ if (ret) {
+ dev_err(dai->dev, "error updating bitwidth field: %d\n", ret);
+ return ret;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mode = drvdata->mi2s_playback_sd_mode[id];
+ else
+ mode = drvdata->mi2s_capture_sd_mode[id];
+
+ if (!mode) {
+ dev_err(dai->dev, "no line is assigned\n");
return -EINVAL;
}
switch (channels) {
case 1:
- regval |= LPAIF_I2SCTL_SPKMODE_SD0;
- regval |= LPAIF_I2SCTL_SPKMONO_MONO;
- break;
case 2:
- regval |= LPAIF_I2SCTL_SPKMODE_SD0;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ switch (mode) {
+ case LPAIF_I2SCTL_MODE_QUAD01:
+ case LPAIF_I2SCTL_MODE_6CH:
+ case LPAIF_I2SCTL_MODE_8CH:
+ mode = LPAIF_I2SCTL_MODE_SD0;
+ break;
+ case LPAIF_I2SCTL_MODE_QUAD23:
+ mode = LPAIF_I2SCTL_MODE_SD2;
+ break;
+ }
+
break;
case 4:
- regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ if (mode < LPAIF_I2SCTL_MODE_QUAD01) {
+ dev_err(dai->dev, "cannot configure 4 channels with mode %d\n",
+ mode);
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case LPAIF_I2SCTL_MODE_6CH:
+ case LPAIF_I2SCTL_MODE_8CH:
+ mode = LPAIF_I2SCTL_MODE_QUAD01;
+ break;
+ }
break;
case 6:
- regval |= LPAIF_I2SCTL_SPKMODE_6CH;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ if (mode < LPAIF_I2SCTL_MODE_6CH) {
+ dev_err(dai->dev, "cannot configure 6 channels with mode %d\n",
+ mode);
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case LPAIF_I2SCTL_MODE_8CH:
+ mode = LPAIF_I2SCTL_MODE_6CH;
+ break;
+ }
break;
case 8:
- regval |= LPAIF_I2SCTL_SPKMODE_8CH;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ if (mode < LPAIF_I2SCTL_MODE_8CH) {
+ dev_err(dai->dev, "cannot configure 8 channels with mode %d\n",
+ mode);
+ return -EINVAL;
+ }
break;
default:
- dev_err(dai->dev, "%s() invalid channels given: %u\n",
- __func__, channels);
+ dev_err(dai->dev, "invalid channels given: %u\n", channels);
return -EINVAL;
}
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
- regval);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = regmap_fields_write(i2sctl->spkmode, id,
+ LPAIF_I2SCTL_SPKMODE(mode));
+ if (ret) {
+ dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n",
+ ret);
+ return ret;
+ }
+ if (channels >= 2)
+ ret = regmap_fields_write(i2sctl->spkmono, id,
+ LPAIF_I2SCTL_SPKMONO_STEREO);
+ else
+ ret = regmap_fields_write(i2sctl->spkmono, id,
+ LPAIF_I2SCTL_SPKMONO_MONO);
+ } else {
+ ret = regmap_fields_write(i2sctl->micmode, id,
+ LPAIF_I2SCTL_MICMODE(mode));
+ if (ret) {
+ dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n",
+ ret);
+ return ret;
+ }
+ if (channels >= 2)
+ ret = regmap_fields_write(i2sctl->micmono, id,
+ LPAIF_I2SCTL_MICMONO_STEREO);
+ else
+ ret = regmap_fields_write(i2sctl->micmono, id,
+ LPAIF_I2SCTL_MICMONO_MONO);
+ }
+
if (ret) {
- dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
- __func__, ret);
+ dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n",
+ ret);
return ret;
}
- ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
+ ret = clk_set_rate(drvdata->mi2s_bit_clk[id],
rate * bitwidth * 2);
if (ret) {
- dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
- __func__, rate * bitwidth * 2, ret);
+ dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
+ rate * bitwidth * 2, ret);
return ret;
}
return 0;
}
-static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
- int ret;
+ struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
+ unsigned int id = dai->driver->id;
+ int ret = -EINVAL;
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
- 0);
- if (ret)
- dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
- __func__, ret);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /*
+ * Ensure lpass BCLK/LRCLK is enabled during
+ * device resume as lpass_cpu_daiops_prepare() is not called
+ * after the device resumes. We don't check mi2s_was_prepared before
+ * enable/disable BCLK in trigger events because:
+ * 1. These trigger events are paired, so the BCLK
+ * enable_count is balanced.
+ * 2. the BCLK can be shared (ex: headset and headset mic),
+ * we need to increase the enable_count so that we don't
+ * turn off the shared BCLK while other devices are using
+ * it.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = regmap_fields_write(i2sctl->spken, id,
+ LPAIF_I2SCTL_SPKEN_ENABLE);
+ } else {
+ ret = regmap_fields_write(i2sctl->micen, id,
+ LPAIF_I2SCTL_MICEN_ENABLE);
+ }
+ if (ret)
+ dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
+ ret);
+
+ ret = clk_enable(drvdata->mi2s_bit_clk[id]);
+ if (ret) {
+ dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+ clk_disable(drvdata->mi2s_osr_clk[id]);
+ return ret;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /*
+ * To ensure lpass BCLK/LRCLK is disabled during
+ * device suspend.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = regmap_fields_write(i2sctl->spken, id,
+ LPAIF_I2SCTL_SPKEN_DISABLE);
+ } else {
+ ret = regmap_fields_write(i2sctl->micen, id,
+ LPAIF_I2SCTL_MICEN_DISABLE);
+ }
+ if (ret)
+ dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
+ ret);
+
+ clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
+
+ break;
+ }
return ret;
}
@@ -187,89 +368,127 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
+ unsigned int id = dai->driver->id;
int ret;
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
- LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
- if (ret)
- dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
- __func__, ret);
+ /*
+ * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture
+ * data flow starts. This allows other codec to have some delay before
+ * the data flow.
+ * (ex: to drop start up pop noise before capture starts).
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE);
+ else
+ ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE);
- return ret;
+ if (ret) {
+ dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can
+ * be called multiple times. It's paired with the clk_disable in
+ * lpass_cpu_daiops_shutdown.
+ */
+ if (!drvdata->mi2s_was_prepared[dai->driver->id]) {
+ ret = clk_enable(drvdata->mi2s_bit_clk[id]);
+ if (ret) {
+ dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+ return ret;
+ }
+ drvdata->mi2s_was_prepared[dai->driver->id] = true;
+ }
+ return 0;
}
-static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static int lpass_cpu_daiops_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
+ int ret;
+ struct snd_soc_dai_driver *drv = dai->driver;
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
- int ret = -EINVAL;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(drvdata->variant,
- dai->driver->id),
- LPAIF_I2SCTL_SPKEN_MASK,
- LPAIF_I2SCTL_SPKEN_ENABLE);
- if (ret)
- dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
- __func__, ret);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(drvdata->variant,
- dai->driver->id),
- LPAIF_I2SCTL_SPKEN_MASK,
- LPAIF_I2SCTL_SPKEN_DISABLE);
- if (ret)
- dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
- __func__, ret);
- break;
+ if (drvdata->mi2s_playback_sd_mode[dai->id] == LPAIF_I2SCTL_MODE_QUAD01) {
+ ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ lpass_quad_chmaps, drv->playback.channels_max, 0,
+ NULL);
+ if (ret < 0)
+ return ret;
}
+ return 0;
+}
+
+static int lpass_cpu_daiops_probe(struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* ensure audio hardware is disabled */
+ ret = regmap_write(drvdata->lpaif_map,
+ LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
+ if (ret)
+ dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
+
return ret;
}
const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
+ .probe = lpass_cpu_daiops_probe,
.set_sysclk = lpass_cpu_daiops_set_sysclk,
.startup = lpass_cpu_daiops_startup,
.shutdown = lpass_cpu_daiops_shutdown,
.hw_params = lpass_cpu_daiops_hw_params,
- .hw_free = lpass_cpu_daiops_hw_free,
- .prepare = lpass_cpu_daiops_prepare,
.trigger = lpass_cpu_daiops_trigger,
+ .prepare = lpass_cpu_daiops_prepare,
};
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
-int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2 = {
+ .pcm_new = lpass_cpu_daiops_pcm_new,
+ .probe = lpass_cpu_daiops_probe,
+ .set_sysclk = lpass_cpu_daiops_set_sysclk,
+ .startup = lpass_cpu_daiops_startup,
+ .shutdown = lpass_cpu_daiops_shutdown,
+ .hw_params = lpass_cpu_daiops_hw_params,
+ .trigger = lpass_cpu_daiops_trigger,
+ .prepare = lpass_cpu_daiops_prepare,
+};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops2);
+
+static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
{
- struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
- int ret;
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ const struct lpass_variant *variant = drvdata->variant;
+ int id = args->args[0];
+ int ret = -EINVAL;
+ int i;
- /* ensure audio hardware is disabled */
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
- if (ret)
- dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
- __func__, ret);
+ for (i = 0; i < variant->num_dai; i++) {
+ if (variant->dai_driver[i].id == id) {
+ *dai_name = variant->dai_driver[i].name;
+ ret = 0;
+ break;
+ }
+ }
return ret;
}
-EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
.name = "lpass-cpu",
+ .of_xlate_dai_name = asoc_qcom_of_xlate_dai_name,
+ .legacy_dai_naming = 1,
};
static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
- struct lpass_variant *v = drvdata->variant;
+ const struct lpass_variant *v = drvdata->variant;
int i;
for (i = 0; i < v->i2s_ports; ++i)
@@ -294,13 +513,24 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
return true;
}
+ for (i = 0; i < v->wrdma_channels; ++i) {
+ if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+ return true;
+ }
+
return false;
}
static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
- struct lpass_variant *v = drvdata->variant;
+ const struct lpass_variant *v = drvdata->variant;
int i;
for (i = 0; i < v->i2s_ports; ++i)
@@ -308,6 +538,8 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
return true;
for (i = 0; i < v->irq_ports; ++i) {
+ if (reg == LPAIF_IRQCLEAR_REG(v, i))
+ return true;
if (reg == LPAIF_IRQEN_REG(v, i))
return true;
if (reg == LPAIF_IRQSTAT_REG(v, i))
@@ -327,27 +559,48 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
return true;
}
+ for (i = 0; i < v->wrdma_channels; ++i) {
+ if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+ return true;
+ }
+
return false;
}
static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
- struct lpass_variant *v = drvdata->variant;
+ const struct lpass_variant *v = drvdata->variant;
int i;
- for (i = 0; i < v->irq_ports; ++i)
+ for (i = 0; i < v->irq_ports; ++i) {
+ if (reg == LPAIF_IRQCLEAR_REG(v, i))
+ return true;
if (reg == LPAIF_IRQSTAT_REG(v, i))
return true;
+ }
for (i = 0; i < v->rdma_channels; ++i)
if (reg == LPAIF_RDMACURR_REG(v, i))
return true;
+ for (i = 0; i < v->wrdma_channels; ++i)
+ if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+ return true;
+
return false;
}
static struct regmap_config lpass_cpu_regmap_config = {
+ .name = "lpass_cpu",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@@ -355,153 +608,696 @@ static struct regmap_config lpass_cpu_regmap_config = {
.readable_reg = lpass_cpu_regmap_readable,
.volatile_reg = lpass_cpu_regmap_volatile,
.cache_type = REGCACHE_FLAT,
- .val_format_endian = REGMAP_ENDIAN_LITTLE,
};
+static int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ unsigned int i;
+ struct lpass_hdmi_tx_ctl *tx_ctl;
+ struct regmap_field *legacy_en;
+ struct lpass_vbit_ctrl *vbit_ctl;
+ struct regmap_field *tx_parity;
+ struct lpass_dp_metadata_ctl *meta_ctl;
+ struct lpass_sstream_ctl *sstream_ctl;
+ struct regmap_field *ch_msb;
+ struct regmap_field *ch_lsb;
+ struct lpass_hdmitx_dmactl *tx_dmactl;
+ int rval;
+
+ tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL);
+ if (!tx_ctl)
+ return -ENOMEM;
+
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset);
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset);
+ drvdata->tx_ctl = tx_ctl;
+
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en);
+ drvdata->hdmitx_legacy_en = legacy_en;
+
+ vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL);
+ if (!vbit_ctl)
+ return -ENOMEM;
+
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit);
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream);
+ drvdata->vbit_ctl = vbit_ctl;
+
+
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity);
+ drvdata->hdmitx_parity_calc_en = tx_parity;
+
+ meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL);
+ if (!meta_ctl)
+ return -ENOMEM;
+
+ rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7);
+ if (rval)
+ return rval;
+ drvdata->meta_ctl = meta_ctl;
+
+ sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL);
+ if (!sstream_ctl)
+ return -ENOMEM;
+
+ rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9);
+ if (rval)
+ return rval;
+
+ drvdata->sstream_ctl = sstream_ctl;
+
+ for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) {
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb);
+ drvdata->hdmitx_ch_msb[i] = ch_msb;
+
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb);
+ drvdata->hdmitx_ch_lsb[i] = ch_lsb;
+
+ tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL);
+ if (!tx_dmactl)
+ return -ENOMEM;
+
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs);
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr);
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel);
+ QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel);
+ drvdata->hdmi_tx_dmactl[i] = tx_dmactl;
+ }
+ return 0;
+}
+
+static bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ if (reg == LPASS_HDMI_TX_CTL_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_DP_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMITX_APP_IRQEN_REG(v))
+ return true;
+ if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v))
+ return true;
+
+ for (i = 0; i < v->hdmi_rdma_channels; i++) {
+ if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
+ return true;
+ }
+
+ for (i = 0; i < v->hdmi_rdma_channels; ++i) {
+ if (reg == LPAIF_HDMI_RDMACTL_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABASE_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMAPER_REG(v, i))
+ return true;
+ }
+ return false;
+}
+
+static bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ if (reg == LPASS_HDMI_TX_CTL_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
+ return true;
+
+ for (i = 0; i < v->hdmi_rdma_channels; i++) {
+ if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
+ return true;
+ }
+
+ if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_DP_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMITX_APP_IRQEN_REG(v))
+ return true;
+ if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v))
+ return true;
+
+ for (i = 0; i < v->hdmi_rdma_channels; ++i) {
+ if (reg == LPAIF_HDMI_RDMACTL_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABASE_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMAPER_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMACURR_REG(v, i))
+ return true;
+ }
+
+ return false;
+}
+
+static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
+ return true;
+
+ for (i = 0; i < v->hdmi_rdma_channels; ++i) {
+ if (reg == LPAIF_HDMI_RDMACURR_REG(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
+ return true;
+ }
+ return false;
+}
+
+static struct regmap_config lpass_hdmi_regmap_config = {
+ .name = "lpass_hdmi",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .writeable_reg = lpass_hdmi_regmap_writeable,
+ .readable_reg = lpass_hdmi_regmap_readable,
+ .volatile_reg = lpass_hdmi_regmap_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static bool __lpass_rxtx_regmap_accessible(struct device *dev, unsigned int reg, bool rw)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ for (i = 0; i < v->rxtx_irq_ports; ++i) {
+ if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i))
+ return true;
+ if (reg == LPAIF_RXTX_IRQEN_REG(v, i))
+ return true;
+ if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i))
+ return true;
+ }
+
+ for (i = 0; i < v->rxtx_rdma_channels; ++i) {
+ if (reg == LPAIF_CDC_RXTX_RDMACTL_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ if (reg == LPAIF_CDC_RXTX_RDMABASE_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ if (reg == LPAIF_CDC_RXTX_RDMABUFF_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ if (rw == LPASS_REG_READ) {
+ if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ }
+ if (reg == LPAIF_CDC_RXTX_RDMAPER_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ if (reg == LPAIF_CDC_RXTX_RDMA_INTF_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ }
+
+ for (i = 0; i < v->rxtx_wrdma_channels; ++i) {
+ if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start,
+ LPASS_CDC_DMA_TX3))
+ return true;
+ if (reg == LPAIF_CDC_RXTX_WRDMABASE_REG(v, i + v->rxtx_wrdma_channel_start,
+ LPASS_CDC_DMA_TX3))
+ return true;
+ if (reg == LPAIF_CDC_RXTX_WRDMABUFF_REG(v, i + v->rxtx_wrdma_channel_start,
+ LPASS_CDC_DMA_TX3))
+ return true;
+ if (rw == LPASS_REG_READ) {
+ if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+ }
+ if (reg == LPAIF_CDC_RXTX_WRDMAPER_REG(v, i + v->rxtx_wrdma_channel_start,
+ LPASS_CDC_DMA_TX3))
+ return true;
+ if (reg == LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, i + v->rxtx_wrdma_channel_start,
+ LPASS_CDC_DMA_TX3))
+ return true;
+ }
+ return false;
+}
+
+static bool lpass_rxtx_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_WRITE);
+}
+
+static bool lpass_rxtx_regmap_readable(struct device *dev, unsigned int reg)
+{
+ return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_READ);
+}
+
+static bool lpass_rxtx_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ for (i = 0; i < v->rxtx_irq_ports; ++i) {
+ if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i))
+ return true;
+ if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i))
+ return true;
+ }
+
+ for (i = 0; i < v->rxtx_rdma_channels; ++i)
+ if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
+ return true;
+
+ for (i = 0; i < v->rxtx_wrdma_channels; ++i)
+ if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i + v->rxtx_wrdma_channel_start,
+ LPASS_CDC_DMA_TX3))
+ return true;
+
+ return false;
+}
+
+static bool __lpass_va_regmap_accessible(struct device *dev, unsigned int reg, bool rw)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ for (i = 0; i < v->va_irq_ports; ++i) {
+ if (reg == LPAIF_VA_IRQCLEAR_REG(v, i))
+ return true;
+ if (reg == LPAIF_VA_IRQEN_REG(v, i))
+ return true;
+ if (reg == LPAIF_VA_IRQSTAT_REG(v, i))
+ return true;
+ }
+
+ for (i = 0; i < v->va_wrdma_channels; ++i) {
+ if (reg == LPAIF_CDC_VA_WRDMACTL_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ if (reg == LPAIF_CDC_VA_WRDMABASE_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ if (reg == LPAIF_CDC_VA_WRDMABUFF_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ if (rw == LPASS_REG_READ) {
+ if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ }
+ if (reg == LPAIF_CDC_VA_WRDMAPER_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ if (reg == LPAIF_CDC_VA_WRDMA_INTF_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ }
+ return false;
+}
+
+static bool lpass_va_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_WRITE);
+}
+
+static bool lpass_va_regmap_readable(struct device *dev, unsigned int reg)
+{
+ return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_READ);
+}
+
+static bool lpass_va_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ for (i = 0; i < v->va_irq_ports; ++i) {
+ if (reg == LPAIF_VA_IRQCLEAR_REG(v, i))
+ return true;
+ if (reg == LPAIF_VA_IRQSTAT_REG(v, i))
+ return true;
+ }
+
+ for (i = 0; i < v->va_wrdma_channels; ++i) {
+ if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
+ LPASS_CDC_DMA_VA_TX0))
+ return true;
+ }
+
+ return false;
+}
+
+static struct regmap_config lpass_rxtx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .writeable_reg = lpass_rxtx_regmap_writeable,
+ .readable_reg = lpass_rxtx_regmap_readable,
+ .volatile_reg = lpass_rxtx_regmap_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static struct regmap_config lpass_va_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .writeable_reg = lpass_va_regmap_writeable,
+ .readable_reg = lpass_va_regmap_readable,
+ .volatile_reg = lpass_va_regmap_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
+ struct device_node *node,
+ const char *name)
+{
+ unsigned int lines[LPASS_CPU_MAX_MI2S_LINES];
+ unsigned int sd_line_mask = 0;
+ int num_lines, i;
+
+ num_lines = of_property_read_variable_u32_array(node, name, lines, 0,
+ LPASS_CPU_MAX_MI2S_LINES);
+ if (num_lines < 0)
+ return LPAIF_I2SCTL_MODE_NONE;
+
+ for (i = 0; i < num_lines; i++)
+ sd_line_mask |= BIT(lines[i]);
+
+ switch (sd_line_mask) {
+ case LPASS_CPU_I2S_SD0_MASK:
+ return LPAIF_I2SCTL_MODE_SD0;
+ case LPASS_CPU_I2S_SD1_MASK:
+ return LPAIF_I2SCTL_MODE_SD1;
+ case LPASS_CPU_I2S_SD2_MASK:
+ return LPAIF_I2SCTL_MODE_SD2;
+ case LPASS_CPU_I2S_SD3_MASK:
+ return LPAIF_I2SCTL_MODE_SD3;
+ case LPASS_CPU_I2S_SD0_1_MASK:
+ return LPAIF_I2SCTL_MODE_QUAD01;
+ case LPASS_CPU_I2S_SD2_3_MASK:
+ return LPAIF_I2SCTL_MODE_QUAD23;
+ case LPASS_CPU_I2S_SD0_1_2_MASK:
+ return LPAIF_I2SCTL_MODE_6CH;
+ case LPASS_CPU_I2S_SD0_1_2_3_MASK:
+ return LPAIF_I2SCTL_MODE_8CH;
+ default:
+ dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask);
+ return LPAIF_I2SCTL_MODE_NONE;
+ }
+}
+
+static void of_lpass_cpu_parse_dai_data(struct device *dev,
+ struct lpass_data *data)
+{
+ struct device_node *node;
+ int ret, i, id;
+
+ /* Allow all channels by default for backwards compatibility */
+ for (i = 0; i < data->variant->num_dai; i++) {
+ id = data->variant->dai_driver[i].id;
+ data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+ data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+ }
+
+ for_each_child_of_node(dev->of_node, node) {
+ ret = of_property_read_u32(node, "reg", &id);
+ if (ret || id < 0) {
+ dev_err(dev, "valid dai id not found: %d\n", ret);
+ continue;
+ }
+ if (id == LPASS_DP_RX) {
+ data->hdmi_port_enable = 1;
+ } else if (is_cdc_dma_port(id)) {
+ data->codec_dma_enable = 1;
+ } else {
+ data->mi2s_playback_sd_mode[id] =
+ of_lpass_cpu_parse_sd_lines(dev, node,
+ "qcom,playback-sd-lines");
+ data->mi2s_capture_sd_mode[id] =
+ of_lpass_cpu_parse_sd_lines(dev, node,
+ "qcom,capture-sd-lines");
+ }
+ }
+}
+
+static int of_lpass_cdc_dma_clks_parse(struct device *dev,
+ struct lpass_data *data)
+{
+ data->codec_mem0 = devm_clk_get(dev, "audio_cc_codec_mem0");
+ if (IS_ERR(data->codec_mem0))
+ return PTR_ERR(data->codec_mem0);
+
+ data->codec_mem1 = devm_clk_get(dev, "audio_cc_codec_mem1");
+ if (IS_ERR(data->codec_mem1))
+ return PTR_ERR(data->codec_mem1);
+
+ data->codec_mem2 = devm_clk_get(dev, "audio_cc_codec_mem2");
+ if (IS_ERR(data->codec_mem2))
+ return PTR_ERR(data->codec_mem2);
+
+ data->va_mem0 = devm_clk_get(dev, "aon_cc_va_mem0");
+ if (IS_ERR(data->va_mem0))
+ return PTR_ERR(data->va_mem0);
+
+ return 0;
+}
+
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
{
struct lpass_data *drvdata;
struct device_node *dsp_of_node;
struct resource *res;
- struct lpass_variant *variant;
+ const struct lpass_variant *variant;
struct device *dev = &pdev->dev;
- const struct of_device_id *match;
- char clk_name[16];
int ret, i, dai_id;
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
if (dsp_of_node) {
- dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n",
- __func__);
+ dev_err(dev, "DSP exists and holds audio resources\n");
+ of_node_put(dsp_of_node);
return -EBUSY;
}
- drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data),
- GFP_KERNEL);
+ drvdata = devm_kzalloc(dev, sizeof(struct lpass_data), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
platform_set_drvdata(pdev, drvdata);
- match = of_match_device(dev->driver->of_match_table, dev);
- if (!match || !match->data)
+ variant = device_get_match_data(dev);
+ if (!variant)
return -EINVAL;
- drvdata->variant = (struct lpass_variant *)match->data;
- variant = drvdata->variant;
+ if (of_device_is_compatible(dev->of_node, "qcom,lpass-cpu-apq8016"))
+ dev_warn(dev, "qcom,lpass-cpu-apq8016 compatible is deprecated\n");
+
+ drvdata->variant = variant;
+
+ of_lpass_cpu_parse_dai_data(dev, drvdata);
+
+ if (drvdata->codec_dma_enable) {
+ drvdata->rxtx_lpaif =
+ devm_platform_ioremap_resource_byname(pdev, "lpass-rxtx-lpaif");
+ if (IS_ERR(drvdata->rxtx_lpaif))
+ return PTR_ERR(drvdata->rxtx_lpaif);
+
+ drvdata->va_lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-va-lpaif");
+ if (IS_ERR(drvdata->va_lpaif))
+ return PTR_ERR(drvdata->va_lpaif);
+
+ lpass_rxtx_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant,
+ variant->rxtx_wrdma_channels +
+ variant->rxtx_wrdma_channel_start, LPASS_CDC_DMA_TX3);
+
+ drvdata->rxtx_lpaif_map = devm_regmap_init_mmio(dev, drvdata->rxtx_lpaif,
+ &lpass_rxtx_regmap_config);
+ if (IS_ERR(drvdata->rxtx_lpaif_map))
+ return PTR_ERR(drvdata->rxtx_lpaif_map);
+
+ lpass_va_regmap_config.max_register = LPAIF_CDC_VA_WRDMAPER_REG(variant,
+ variant->va_wrdma_channels +
+ variant->va_wrdma_channel_start, LPASS_CDC_DMA_VA_TX0);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
+ drvdata->va_lpaif_map = devm_regmap_init_mmio(dev, drvdata->va_lpaif,
+ &lpass_va_regmap_config);
+ if (IS_ERR(drvdata->va_lpaif_map))
+ return PTR_ERR(drvdata->va_lpaif_map);
- drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR((void const __force *)drvdata->lpaif)) {
- dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n",
- __func__,
- PTR_ERR((void const __force *)drvdata->lpaif));
- return PTR_ERR((void const __force *)drvdata->lpaif);
+ ret = of_lpass_cdc_dma_clks_parse(dev, drvdata);
+ if (ret) {
+ dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
+ if (!res)
+ return -EINVAL;
+ drvdata->rxtx_cdc_dma_lpm_buf = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
+ if (!res)
+ return -EINVAL;
+ drvdata->va_cdc_dma_lpm_buf = res->start;
}
- lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
- variant->rdma_channels);
+ drvdata->lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-lpaif");
+ if (IS_ERR(drvdata->lpaif))
+ return PTR_ERR(drvdata->lpaif);
+
+ lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
+ variant->wrdma_channels +
+ variant->wrdma_channel_start);
- drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
+ drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif,
&lpass_cpu_regmap_config);
if (IS_ERR(drvdata->lpaif_map)) {
- dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n",
- __func__, PTR_ERR(drvdata->lpaif_map));
+ dev_err(dev, "error initializing regmap: %ld\n",
+ PTR_ERR(drvdata->lpaif_map));
return PTR_ERR(drvdata->lpaif_map);
}
- if (variant->init)
- variant->init(pdev);
+ if (drvdata->hdmi_port_enable) {
+ drvdata->hdmiif = devm_platform_ioremap_resource_byname(pdev, "lpass-hdmiif");
+ if (IS_ERR(drvdata->hdmiif))
+ return PTR_ERR(drvdata->hdmiif);
+
+ lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant,
+ variant->hdmi_rdma_channels - 1);
+ drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif,
+ &lpass_hdmi_regmap_config);
+ if (IS_ERR(drvdata->hdmiif_map)) {
+ dev_err(dev, "error initializing regmap: %ld\n",
+ PTR_ERR(drvdata->hdmiif_map));
+ return PTR_ERR(drvdata->hdmiif_map);
+ }
+ }
- for (i = 0; i < variant->num_dai; i++) {
- dai_id = variant->dai_driver[i].id;
- if (variant->num_dai > 1)
- sprintf(clk_name, "mi2s-osr-clk%d", i);
- else
- sprintf(clk_name, "mi2s-osr-clk");
-
- drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
- clk_name);
- if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
- dev_warn(&pdev->dev,
- "%s() error getting mi2s-osr-clk: %ld\n",
- __func__,
- PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
+ if (variant->init) {
+ ret = variant->init(pdev);
+ if (ret) {
+ dev_err(dev, "error initializing variant: %d\n", ret);
+ return ret;
}
+ }
- if (variant->num_dai > 1)
- sprintf(clk_name, "mi2s-bit-clk%d", i);
- else
- sprintf(clk_name, "mi2s-bit-clk");
+ for (i = 0; i < variant->num_dai; i++) {
+ dai_id = variant->dai_driver[i].id;
+ if (dai_id == LPASS_DP_RX || is_cdc_dma_port(dai_id))
+ continue;
- drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
- clk_name);
+ drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev,
+ variant->dai_osr_clk_names[i]);
+ drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(dev,
+ variant->dai_bit_clk_names[i]);
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
- dev_err(&pdev->dev,
- "%s() error getting mi2s-bit-clk: %ld\n",
- __func__,
+ dev_err(dev,
+ "error getting %s: %ld\n",
+ variant->dai_bit_clk_names[i],
PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
}
+ if (drvdata->mi2s_playback_sd_mode[dai_id] ==
+ LPAIF_I2SCTL_MODE_QUAD01) {
+ variant->dai_driver[dai_id].playback.channels_min = 4;
+ variant->dai_driver[dai_id].playback.channels_max = 4;
+ }
}
- drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
- if (IS_ERR(drvdata->ahbix_clk)) {
- dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n",
- __func__, PTR_ERR(drvdata->ahbix_clk));
- return PTR_ERR(drvdata->ahbix_clk);
- }
+ /* Allocation for i2sctl regmap fields */
+ drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl),
+ GFP_KERNEL);
+ if (!drvdata->i2sctl)
+ return -ENOMEM;
- ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
+ /* Initialize bitfields for dai I2SCTL register */
+ ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl,
+ drvdata->lpaif_map);
if (ret) {
- dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n",
- __func__, ret);
+ dev_err(dev, "error init i2sctl field: %d\n", ret);
return ret;
}
- dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__,
- clk_get_rate(drvdata->ahbix_clk));
- ret = clk_prepare_enable(drvdata->ahbix_clk);
- if (ret) {
- dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n",
- __func__, ret);
- return ret;
+ if (drvdata->hdmi_port_enable) {
+ ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map);
+ if (ret) {
+ dev_err(dev, "%s error hdmi init failed\n", __func__);
+ return ret;
+ }
}
-
- ret = devm_snd_soc_register_component(&pdev->dev,
+ ret = devm_snd_soc_register_component(dev,
&lpass_cpu_comp_driver,
variant->dai_driver,
variant->num_dai);
if (ret) {
- dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
- __func__, ret);
- goto err_clk;
+ dev_err(dev, "error registering cpu driver: %d\n", ret);
+ goto err;
}
ret = asoc_qcom_lpass_platform_register(pdev);
if (ret) {
- dev_err(&pdev->dev, "%s() error registering platform driver: %d\n",
- __func__, ret);
- goto err_clk;
+ dev_err(dev, "error registering platform driver: %d\n", ret);
+ goto err;
}
- return 0;
-
-err_clk:
- clk_disable_unprepare(drvdata->ahbix_clk);
+err:
return ret;
}
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
-int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
+void asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
if (drvdata->variant->exit)
drvdata->variant->exit(pdev);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
+
+void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
- clk_disable_unprepare(drvdata->ahbix_clk);
+ if (drvdata->variant->exit)
+ drvdata->variant->exit(pdev);
- return 0;
}
-EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_shutdown);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c
new file mode 100644
index 000000000000..6d9795306cfa
--- /dev/null
+++ b/sound/soc/qcom/lpass-hdmi.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * lpass-hdmi.c -- ALSA SoC HDMI-CPU DAI driver for QTi LPASS HDMI
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <dt-bindings/sound/sc7180-lpass.h>
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ int bitwidth;
+ unsigned int word_length;
+ unsigned int ch_sts_buf0;
+ unsigned int ch_sts_buf1;
+ unsigned int data_format;
+ unsigned int sampling_freq;
+ unsigned int ch = 0;
+ struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
+ struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(format);
+ if (bitwidth < 0) {
+ dev_err(dai->dev, "%s invalid bit width given : %d\n",
+ __func__, bitwidth);
+ return bitwidth;
+ }
+
+ switch (bitwidth) {
+ case 16:
+ word_length = LPASS_DP_AUDIO_BITWIDTH16;
+ break;
+ case 24:
+ word_length = LPASS_DP_AUDIO_BITWIDTH24;
+ break;
+ default:
+ dev_err(dai->dev, "%s invalid bit width given : %d\n",
+ __func__, bitwidth);
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 32000:
+ sampling_freq = LPASS_SAMPLING_FREQ32;
+ break;
+ case 44100:
+ sampling_freq = LPASS_SAMPLING_FREQ44;
+ break;
+ case 48000:
+ sampling_freq = LPASS_SAMPLING_FREQ48;
+ break;
+ default:
+ dev_err(dai->dev, "%s invalid bit width given : %d\n",
+ __func__, bitwidth);
+ return -EINVAL;
+ }
+ data_format = LPASS_DATA_FORMAT_LINEAR;
+ ch_sts_buf0 = (((data_format << LPASS_DATA_FORMAT_SHIFT) & LPASS_DATA_FORMAT_MASK)
+ | ((sampling_freq << LPASS_FREQ_BIT_SHIFT) & LPASS_FREQ_BIT_MASK));
+ ch_sts_buf1 = (word_length) & LPASS_WORDLENGTH_MASK;
+
+ ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_RESET);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_CLEAR);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmitx_legacy_en, LPASS_HDMITX_LEGACY_DISABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmitx_parity_calc_en, HDMITX_PARITY_CALC_EN);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->vbit_ctl->replace_vbit, REPLACE_VBIT);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->vbit_ctl->vbit_stream, LINEAR_PCM_DATA);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmitx_ch_msb[0], ch_sts_buf1);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmitx_ch_lsb[0], ch_sts_buf0);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_chs, HW_MODE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_chs_sel, SW_MODE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_usr, HW_MODE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_usr_sel, SW_MODE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->as_sdp_cc, channels - 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->as_sdp_ct, LPASS_META_DEFAULT_VAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->aif_db4, LPASS_META_DEFAULT_VAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->frequency, sampling_freq);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->mst_index, LPASS_META_DEFAULT_VAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->dptx_index, LPASS_META_DEFAULT_VAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->dma_sel, ch);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->auto_bbit_en, LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->layout, LPASS_SSTREAM_DEFAULT_DISABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->layout_sp, LPASS_LAYOUT_SP_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->dp_audio, LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->set_sp_on_en, LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->dp_sp_b_hw_en, LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->dp_staffing_en, LPASS_SSTREAM_DEFAULT_ENABLE);
+
+ return ret;
+}
+
+static int lpass_hdmi_daiops_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+
+ ret = regmap_field_write(drvdata->sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(drvdata->meta_ctl->mute, LPASS_MUTE_DISABLE);
+
+ return ret;
+}
+
+static int lpass_hdmi_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
+ struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
+ int ret = -EINVAL;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_DISABLE);
+ if (ret)
+ return ret;
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(sstream_ctl->dp_audio, 0);
+ if (ret)
+ return ret;
+
+ break;
+ }
+ return ret;
+}
+
+const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = {
+ .hw_params = lpass_hdmi_daiops_hw_params,
+ .prepare = lpass_hdmi_daiops_prepare,
+ .trigger = lpass_hdmi_daiops_trigger,
+};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops);
+
+MODULE_DESCRIPTION("QTi LPASS HDMI Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-hdmi.h b/sound/soc/qcom/lpass-hdmi.h
new file mode 100644
index 000000000000..ee74d783027a
--- /dev/null
+++ b/sound/soc/qcom/lpass-hdmi.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * lpass_hdmi.h - Definitions for the QTi LPASS HDMI
+ */
+
+#ifndef __LPASS_HDMI_H__
+#define __LPASS_HDMI_H__
+
+#include <linux/regmap.h>
+
+#define LPASS_HDMITX_LEGACY_DISABLE 0x0
+#define LPASS_HDMITX_LEGACY_ENABLE 0x1
+#define LPASS_DP_AUDIO_BITWIDTH16 0x0
+#define LPASS_DP_AUDIO_BITWIDTH24 0xb
+#define LPASS_DATA_FORMAT_SHIFT 0x1
+#define LPASS_FREQ_BIT_SHIFT 24
+#define LPASS_DATA_FORMAT_LINEAR 0x0
+#define LPASS_DATA_FORMAT_NON_LINEAR 0x1
+#define LPASS_SAMPLING_FREQ32 0x3
+#define LPASS_SAMPLING_FREQ44 0x0
+#define LPASS_SAMPLING_FREQ48 0x2
+#define LPASS_TX_CTL_RESET 0x1
+#define LPASS_TX_CTL_CLEAR 0x0
+#define LPASS_SSTREAM_ENABLE 1
+#define LPASS_SSTREAM_DISABLE 0
+#define LPASS_LAYOUT_SP_DEFAULT 0xf
+#define LPASS_SSTREAM_DEFAULT_ENABLE 1
+#define LPASS_SSTREAM_DEFAULT_DISABLE 0
+#define LPASS_MUTE_ENABLE 1
+#define LPASS_MUTE_DISABLE 0
+#define LPASS_META_DEFAULT_VAL 0
+#define HW_MODE 1
+#define SW_MODE 0
+#define LEGACY_LPASS_LPAIF 1
+#define LEGACY_LPASS_HDMI 0
+#define REPLACE_VBIT 0x1
+#define LINEAR_PCM_DATA 0x0
+#define NON_LINEAR_PCM_DATA 0x1
+#define HDMITX_PARITY_CALC_EN 0x1
+#define HDMITX_PARITY_CALC_DIS 0x0
+#define LPASS_DATA_FORMAT_MASK GENMASK(1, 1)
+#define LPASS_WORDLENGTH_MASK GENMASK(3, 0)
+#define LPASS_FREQ_BIT_MASK GENMASK(27, 24)
+
+#define LPASS_HDMI_TX_CTL_ADDR(v) (v->hdmi_tx_ctl_addr)
+#define LPASS_HDMI_TX_LEGACY_ADDR(v) (v->hdmi_legacy_addr)
+#define LPASS_HDMI_TX_VBIT_CTL_ADDR(v) (v->hdmi_vbit_addr)
+#define LPASS_HDMI_TX_PARITY_ADDR(v) (v->hdmi_parity_addr)
+#define LPASS_HDMI_TX_DP_ADDR(v) (v->hdmi_DP_addr)
+#define LPASS_HDMI_TX_SSTREAM_ADDR(v) (v->hdmi_sstream_addr)
+
+#define LPASS_HDMI_TX_CH_LSB_ADDR(v, port) \
+ (v->hdmi_ch_lsb_addr + v->ch_stride * (port))
+#define LPASS_HDMI_TX_CH_MSB_ADDR(v, port) \
+ (v->hdmi_ch_msb_addr + v->ch_stride * (port))
+#define LPASS_HDMI_TX_DMA_ADDR(v, port) \
+ (v->hdmi_dmactl_addr + v->hdmi_dma_stride * (port))
+
+struct lpass_sstream_ctl {
+ struct regmap_field *sstream_en;
+ struct regmap_field *dma_sel;
+ struct regmap_field *auto_bbit_en;
+ struct regmap_field *layout;
+ struct regmap_field *layout_sp;
+ struct regmap_field *set_sp_on_en;
+ struct regmap_field *dp_audio;
+ struct regmap_field *dp_staffing_en;
+ struct regmap_field *dp_sp_b_hw_en;
+};
+
+struct lpass_dp_metadata_ctl {
+ struct regmap_field *mute;
+ struct regmap_field *as_sdp_cc;
+ struct regmap_field *as_sdp_ct;
+ struct regmap_field *aif_db4;
+ struct regmap_field *frequency;
+ struct regmap_field *mst_index;
+ struct regmap_field *dptx_index;
+};
+
+struct lpass_hdmi_tx_ctl {
+ struct regmap_field *soft_reset;
+ struct regmap_field *force_reset;
+};
+
+struct lpass_hdmitx_dmactl {
+ struct regmap_field *use_hw_chs;
+ struct regmap_field *use_hw_usr;
+ struct regmap_field *hw_chs_sel;
+ struct regmap_field *hw_usr_sel;
+};
+
+struct lpass_vbit_ctrl {
+ struct regmap_field *replace_vbit;
+ struct regmap_field *vbit_stream;
+};
+
+extern const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops;
+
+#endif /* __LPASS_HDMI_H__ */
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
index 7a4167952711..e57d29ea4ce7 100644
--- a/sound/soc/qcom/lpass-ipq806x.c
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
* Splited out the IPQ8064 soc specific from lpass-cpu.c
*/
@@ -59,21 +51,64 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
.channels_min = 1,
.channels_max = 8,
},
- .probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
};
-static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int ipq806x_lpass_init(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk");
+ if (IS_ERR(drvdata->ahbix_clk)) {
+ dev_err(dev, "error getting ahbix-clk: %ld\n",
+ PTR_ERR(drvdata->ahbix_clk));
+ ret = PTR_ERR(drvdata->ahbix_clk);
+ goto err_ahbix_clk;
+ }
+
+ ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
+ if (ret) {
+ dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret);
+ goto err_ahbix_clk;
+ }
+ dev_dbg(dev, "set ahbix_clk rate to %lu\n",
+ clk_get_rate(drvdata->ahbix_clk));
+
+ ret = clk_prepare_enable(drvdata->ahbix_clk);
+ if (ret) {
+ dev_err(dev, "error enabling ahbix_clk: %d\n", ret);
+ goto err_ahbix_clk;
+ }
+
+err_ahbix_clk:
+ return ret;
+}
+
+static int ipq806x_lpass_exit(struct platform_device *pdev)
{
- return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(drvdata->ahbix_clk);
+
+ return 0;
}
-static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir, unsigned int dai_id)
+{
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+ else /* Capture currently not implemented */
+ return -EINVAL;
+}
+
+static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
{
return 0;
}
-static struct lpass_variant ipq806x_data = {
+static const struct lpass_variant ipq806x_data = {
.i2sctrl_reg_base = 0x0010,
.i2sctrl_reg_stride = 0x04,
.i2s_ports = 5,
@@ -83,13 +118,49 @@ static struct lpass_variant ipq806x_data = {
.rdma_reg_base = 0x6000,
.rdma_reg_stride = 0x1000,
.rdma_channels = 4,
+ .wrdma_reg_base = 0xB000,
+ .wrdma_reg_stride = 0x1000,
+ .wrdma_channel_start = 5,
+ .wrdma_channels = 4,
+ .loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4),
+ .spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4),
+ .spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4),
+ .spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4),
+ .micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4),
+ .micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4),
+ .micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4),
+ .wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4),
+ .bitwidth = REG_FIELD_ID(0x0010, 0, 1, 5, 0x4),
+
+ .rdma_dyncclk = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000),
+ .rdma_bursten = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000),
+ .rdma_wpscnt = REG_FIELD_ID(0x6000, 8, 10, 4, 0x1000),
+ .rdma_intf = REG_FIELD_ID(0x6000, 4, 7, 4, 0x1000),
+ .rdma_fifowm = REG_FIELD_ID(0x6000, 1, 3, 4, 0x1000),
+ .rdma_enable = REG_FIELD_ID(0x6000, 0, 0, 4, 0x1000),
+
+ .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 4, 0x1000),
+ .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 4, 0x1000),
+ .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 4, 0x1000),
+ .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 4, 0x1000),
+ .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 4, 0x1000),
+ .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 4, 0x1000),
+
.dai_driver = &ipq806x_lpass_cpu_dai_driver,
.num_dai = 1,
+ .dai_osr_clk_names = (const char *[]) {
+ "mi2s-osr-clk",
+ },
+ .dai_bit_clk_names = (const char *[]) {
+ "mi2s-bit-clk",
+ },
+ .init = ipq806x_lpass_init,
+ .exit = ipq806x_lpass_exit,
.alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
.free_dma_channel = ipq806x_lpass_free_dma_channel,
};
-static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
+static const struct of_device_id ipq806x_lpass_cpu_device_id[] __maybe_unused = {
{ .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
{}
};
@@ -101,9 +172,9 @@ static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
.of_match_table = of_match_ptr(ipq806x_lpass_cpu_device_id),
},
.probe = asoc_qcom_lpass_cpu_platform_probe,
- .remove = asoc_qcom_lpass_cpu_platform_remove,
+ .remove = asoc_qcom_lpass_cpu_platform_remove,
};
module_platform_driver(ipq806x_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
index 95e22f131052..6d9d9d1f6a4d 100644
--- a/sound/soc/qcom/lpass-lpaif-reg.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -1,14 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef __LPASS_LPAIF_REG_H__
@@ -20,43 +12,57 @@
(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
#define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
-#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000
-#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15
-#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
-#define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
-
-#define LPAIF_I2SCTL_SPKEN_MASK 0x4000
-#define LPAIF_I2SCTL_SPKEN_SHIFT 14
-#define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT)
-#define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT)
-
-#define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00
-#define LPAIF_I2SCTL_SPKMODE_SHIFT 10
-#define LPAIF_I2SCTL_SPKMODE_NONE (0 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD0 (1 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD1 (2 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD2 (3 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD3 (4 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_QUAD01 (5 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_QUAD23 (6 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_6CH (7 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_8CH (8 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-
-#define LPAIF_I2SCTL_SPKMONO_MASK 0x0200
-#define LPAIF_I2SCTL_SPKMONO_SHIFT 9
-#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
-#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
-
-#define LPAIF_I2SCTL_WSSRC_MASK 0x0004
-#define LPAIF_I2SCTL_WSSRC_SHIFT 2
-#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT)
-#define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT)
-
-#define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003
-#define LPAIF_I2SCTL_BITWIDTH_SHIFT 0
-#define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
-#define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
-#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+
+#define LPAIF_I2SCTL_LOOPBACK_DISABLE 0
+#define LPAIF_I2SCTL_LOOPBACK_ENABLE 1
+
+#define LPAIF_I2SCTL_SPKEN_DISABLE 0
+#define LPAIF_I2SCTL_SPKEN_ENABLE 1
+
+#define LPAIF_I2SCTL_MODE_NONE 0
+#define LPAIF_I2SCTL_MODE_SD0 1
+#define LPAIF_I2SCTL_MODE_SD1 2
+#define LPAIF_I2SCTL_MODE_SD2 3
+#define LPAIF_I2SCTL_MODE_SD3 4
+#define LPAIF_I2SCTL_MODE_QUAD01 5
+#define LPAIF_I2SCTL_MODE_QUAD23 6
+#define LPAIF_I2SCTL_MODE_6CH 7
+#define LPAIF_I2SCTL_MODE_8CH 8
+#define LPAIF_I2SCTL_MODE_10CH 9
+#define LPAIF_I2SCTL_MODE_12CH 10
+#define LPAIF_I2SCTL_MODE_14CH 11
+#define LPAIF_I2SCTL_MODE_16CH 12
+#define LPAIF_I2SCTL_MODE_SD4 13
+#define LPAIF_I2SCTL_MODE_SD5 14
+#define LPAIF_I2SCTL_MODE_SD6 15
+#define LPAIF_I2SCTL_MODE_SD7 16
+#define LPAIF_I2SCTL_MODE_QUAD45 17
+#define LPAIF_I2SCTL_MODE_QUAD47 18
+#define LPAIF_I2SCTL_MODE_8CH_2 19
+
+#define LPAIF_I2SCTL_SPKMODE(mode) mode
+
+#define LPAIF_I2SCTL_SPKMONO_STEREO 0
+#define LPAIF_I2SCTL_SPKMONO_MONO 1
+
+#define LPAIF_I2SCTL_MICEN_DISABLE 0
+#define LPAIF_I2SCTL_MICEN_ENABLE 1
+
+#define LPAIF_I2SCTL_MICMODE(mode) mode
+
+#define LPAIF_I2SCTL_MICMONO_STEREO 0
+#define LPAIF_I2SCTL_MICMONO_MONO 1
+
+#define LPAIF_I2SCTL_WSSRC_INTERNAL 0
+#define LPAIF_I2SCTL_WSSRC_EXTERNAL 1
+
+#define LPAIF_I2SCTL_BITWIDTH_16 0
+#define LPAIF_I2SCTL_BITWIDTH_24 1
+#define LPAIF_I2SCTL_BITWIDTH_32 2
+
+#define LPAIF_I2SCTL_RESET_STATE 0x003C0004
+#define LPAIF_DMACTL_RESET_STATE 0x00200000
+
/* LPAIF IRQ */
#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
@@ -68,6 +74,29 @@
#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
+/* LPAIF RXTX IRQ */
+#define LPAIF_RXTX_IRQ_REG_ADDR(v, addr, port) \
+ (v->rxtx_irq_reg_base + (addr) + v->rxtx_irq_reg_stride * (port))
+
+#define LPAIF_RXTX_IRQEN_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x0, port)
+#define LPAIF_RXTX_IRQSTAT_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x4, port)
+#define LPAIF_RXTX_IRQCLEAR_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0xC, port)
+
+/* LPAIF VA IRQ */
+#define LPAIF_VA_IRQ_REG_ADDR(v, addr, port) \
+ (v->va_irq_reg_base + (addr) + v->va_irq_reg_stride * (port))
+
+#define LPAIF_VA_IRQEN_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0x0, port)
+#define LPAIF_VA_IRQSTAT_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0x4, port)
+#define LPAIF_VA_IRQCLEAR_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0xC, port)
+
+#define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \
+ ((v->hdmi_irq_reg_base) + (addr))
+
+#define LPASS_HDMITX_APP_IRQEN_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x4)
+#define LPASS_HDMITX_APP_IRQSTAT_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x8)
+#define LPASS_HDMITX_APP_IRQCLEAR_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0xC)
+
#define LPAIF_IRQ_BITSTRIDE 3
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
@@ -75,8 +104,22 @@
#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+#define LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) (1 << (14 + chan))
+#define LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan) (1 << (24 + chan))
+#define LPAIF_IRQ_HDMI_METADONE BIT(23)
/* LPAIF DMA */
+#define LPAIF_HDMI_RDMA_REG_ADDR(v, addr, chan) \
+ (v->hdmi_rdma_reg_base + (addr) + v->hdmi_rdma_reg_stride * (chan))
+
+#define LPAIF_HDMI_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
+
+#define LPAIF_HDMI_RDMACTL_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_HDMI_RDMABASE_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x04, (chan))
+#define LPAIF_HDMI_RDMABUFF_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_HDMI_RDMACURR_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x0C, (chan))
+#define LPAIF_HDMI_RDMAPER_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x10, (chan))
+#define LPAIF_HDMI_RDMAPERCNT_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
@@ -90,37 +133,187 @@
#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
-#define LPAIF_RDMACTL_BURSTEN_MASK 0x800
-#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
-#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-
-#define LPAIF_RDMACTL_WPSCNT_MASK 0x700
-#define LPAIF_RDMACTL_WPSCNT_SHIFT 8
-#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-
-#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
-#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
-
-#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E
-#define LPAIF_RDMACTL_FIFOWM_SHIFT 1
-#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-
-#define LPAIF_RDMACTL_ENABLE_MASK 0x1
-#define LPAIF_RDMACTL_ENABLE_SHIFT 0
-#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
-#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
+#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \
+ (v->wrdma_reg_base + (addr) + \
+ v->wrdma_reg_stride * (chan - v->wrdma_channel_start))
+
+#define LPAIF_WRDMACTL_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_WRDMABASE_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan))
+#define LPAIF_WRDMABUFF_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_WRDMACURR_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan))
+#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
+#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
+
+#define LPAIF_INTFDMA_REG(v, chan, reg, dai_id) \
+ ((dai_id == LPASS_DP_RX) ? \
+ LPAIF_HDMI_RDMA##reg##_REG(v, chan) : \
+ LPAIF_RDMA##reg##_REG(v, chan))
+
+#define __LPAIF_DMA_REG(v, chan, dir, reg, dai_id) \
+ ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
+ (LPAIF_INTFDMA_REG(v, chan, reg, dai_id)) : \
+ LPAIF_WRDMA##reg##_REG(v, chan))
+
+#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ __LPAIF_CDC_DMA_REG(v, chan, dir, CTL, dai_id) : \
+ __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id))
+#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ __LPAIF_CDC_DMA_REG(v, chan, dir, BASE, dai_id) : \
+ __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id))
+#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ __LPAIF_CDC_DMA_REG(v, chan, dir, BUFF, dai_id) : \
+ __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id))
+#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ __LPAIF_CDC_DMA_REG(v, chan, dir, CURR, dai_id) : \
+ __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id))
+#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ __LPAIF_CDC_DMA_REG(v, chan, dir, PER, dai_id) : \
+ __LPAIF_DMA_REG(v, chan, dir, PER, dai_id))
+#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ __LPAIF_CDC_DMA_REG(v, chan, dir, PERCNT, dai_id) : \
+ __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id))
+
+#define LPAIF_CDC_RDMA_REG_ADDR(v, addr, chan, dai_id) \
+ (is_rxtx_cdc_dma_port(dai_id) ? \
+ (v->rxtx_rdma_reg_base + (addr) + v->rxtx_rdma_reg_stride * (chan)) : \
+ (v->va_rdma_reg_base + (addr) + v->va_rdma_reg_stride * (chan)))
+
+#define LPAIF_CDC_RXTX_RDMACTL_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id)
+#define LPAIF_CDC_RXTX_RDMABASE_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id)
+#define LPAIF_CDC_RXTX_RDMABUFF_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id)
+#define LPAIF_CDC_RXTX_RDMACURR_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
+#define LPAIF_CDC_RXTX_RDMAPER_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id)
+#define LPAIF_CDC_RXTX_RDMA_INTF_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id)
+
+#define LPAIF_CDC_VA_RDMACTL_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id)
+#define LPAIF_CDC_VA_RDMABASE_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id)
+#define LPAIF_CDC_VA_RDMABUFF_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id)
+#define LPAIF_CDC_VA_RDMACURR_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
+#define LPAIF_CDC_VA_RDMAPER_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id)
+#define LPAIF_CDC_VA_RDMA_INTF_REG(v, chan, dai_id) \
+ LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id)
+
+#define LPAIF_CDC_WRDMA_REG_ADDR(v, addr, chan, dai_id) \
+ (is_rxtx_cdc_dma_port(dai_id) ? \
+ (v->rxtx_wrdma_reg_base + (addr) + \
+ v->rxtx_wrdma_reg_stride * (chan - v->rxtx_wrdma_channel_start)) : \
+ (v->va_wrdma_reg_base + (addr) + \
+ v->va_wrdma_reg_stride * (chan - v->va_wrdma_channel_start)))
+
+#define LPAIF_CDC_RXTX_WRDMACTL_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id)
+#define LPAIF_CDC_RXTX_WRDMABASE_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id)
+#define LPAIF_CDC_RXTX_WRDMABUFF_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id)
+#define LPAIF_CDC_RXTX_WRDMACURR_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
+#define LPAIF_CDC_RXTX_WRDMAPER_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id)
+#define LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id)
+
+#define LPAIF_CDC_VA_WRDMACTL_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id)
+#define LPAIF_CDC_VA_WRDMABASE_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id)
+#define LPAIF_CDC_VA_WRDMABUFF_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id)
+#define LPAIF_CDC_VA_WRDMACURR_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
+#define LPAIF_CDC_VA_WRDMAPER_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id)
+#define LPAIF_CDC_VA_WRDMA_INTF_REG(v, chan, dai_id) \
+ LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id)
+
+#define __LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) \
+ (is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_RDMA##reg##_REG(v, chan, dai_id) : \
+ LPAIF_CDC_VA_RDMA##reg##_REG(v, chan, dai_id))
+
+#define __LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id) \
+ (is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_WRDMA##reg##_REG(v, chan, dai_id) : \
+ LPAIF_CDC_VA_WRDMA##reg##_REG(v, chan, dai_id))
+
+#define __LPAIF_CDC_DMA_REG(v, chan, dir, reg, dai_id) \
+ ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
+ __LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) : \
+ __LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id))
+
+#define LPAIF_CDC_INTF_REG(v, chan, dir, dai_id) \
+ ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
+ LPAIF_CDC_RDMA_INTF_REG(v, chan, dai_id) : \
+ LPAIF_CDC_WRDMA_INTF_REG(v, chan, dai_id))
+
+#define LPAIF_INTF_REG(v, chan, dir, dai_id) \
+ (is_cdc_dma_port(dai_id) ? \
+ LPAIF_CDC_INTF_REG(v, chan, dir, dai_id) : \
+ LPAIF_DMACTL_REG(v, chan, dir, dai_id))
+
+#define LPAIF_DMACTL_BURSTEN_SINGLE 0
+#define LPAIF_DMACTL_BURSTEN_INCR4 1
+
+#define LPAIF_DMACTL_WPSCNT_ONE 0
+#define LPAIF_DMACTL_WPSCNT_TWO 1
+#define LPAIF_DMACTL_WPSCNT_THREE 2
+#define LPAIF_DMACTL_WPSCNT_FOUR 3
+#define LPAIF_DMACTL_WPSCNT_SIX 5
+#define LPAIF_DMACTL_WPSCNT_EIGHT 7
+#define LPAIF_DMACTL_WPSCNT_TEN 9
+#define LPAIF_DMACTL_WPSCNT_TWELVE 11
+#define LPAIF_DMACTL_WPSCNT_FOURTEEN 13
+#define LPAIF_DMACTL_WPSCNT_SIXTEEN 15
+
+#define LPAIF_DMACTL_AUDINTF(id) id
+
+#define LPAIF_DMACTL_FIFOWM_1 0
+#define LPAIF_DMACTL_FIFOWM_2 1
+#define LPAIF_DMACTL_FIFOWM_3 2
+#define LPAIF_DMACTL_FIFOWM_4 3
+#define LPAIF_DMACTL_FIFOWM_5 4
+#define LPAIF_DMACTL_FIFOWM_6 5
+#define LPAIF_DMACTL_FIFOWM_7 6
+#define LPAIF_DMACTL_FIFOWM_8 7
+#define LPAIF_DMACTL_FIFOWM_9 8
+#define LPAIF_DMACTL_FIFOWM_10 9
+#define LPAIF_DMACTL_FIFOWM_11 10
+#define LPAIF_DMACTL_FIFOWM_12 11
+#define LPAIF_DMACTL_FIFOWM_13 12
+#define LPAIF_DMACTL_FIFOWM_14 13
+#define LPAIF_DMACTL_FIFOWM_15 14
+#define LPAIF_DMACTL_FIFOWM_16 15
+#define LPAIF_DMACTL_FIFOWM_17 16
+#define LPAIF_DMACTL_FIFOWM_18 17
+#define LPAIF_DMACTL_FIFOWM_19 18
+#define LPAIF_DMACTL_FIFOWM_20 19
+#define LPAIF_DMACTL_FIFOWM_21 20
+#define LPAIF_DMACTL_FIFOWM_22 21
+#define LPAIF_DMACTL_FIFOWM_23 22
+#define LPAIF_DMACTL_FIFOWM_24 23
+#define LPAIF_DMACTL_FIFOWM_25 24
+#define LPAIF_DMACTL_FIFOWM_26 25
+#define LPAIF_DMACTL_FIFOWM_27 26
+#define LPAIF_DMACTL_FIFOWM_28 27
+#define LPAIF_DMACTL_FIFOWM_29 28
+#define LPAIF_DMACTL_FIFOWM_30 29
+#define LPAIF_DMACTL_FIFOWM_31 30
+#define LPAIF_DMACTL_FIFOWM_32 31
+
+#define LPAIF_DMACTL_ENABLE_OFF 0
+#define LPAIF_DMACTL_ENABLE_ON 1
+
+#define LPAIF_DMACTL_DYNCLK_OFF 0
+#define LPAIF_DMACTL_DYNCLK_ON 1
#endif /* __LPASS_LPAIF_REG_H__ */
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 79688aa1941a..b456e096f138 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
*/
+#include <dt-bindings/sound/qcom,lpass.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/kernel.h>
@@ -24,15 +17,15 @@
#include "lpass-lpaif-reg.h"
#include "lpass.h"
-struct lpass_pcm_data {
- int rdma_ch;
- int i2s_port;
-};
+#define DRV_NAME "lpass-platform"
-#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
+#define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024)
#define LPASS_PLATFORM_PERIODS 2
+#define LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE (8 * 1024)
+#define LPASS_VA_CDC_DMA_LPM_BUFF_SIZE (12 * 1024)
+#define LPASS_CDC_DMA_REGISTER_FIELDS_MAX 15
-static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
+static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -56,73 +49,472 @@ static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
.fifo_size = 0,
};
-static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
+static const struct snd_pcm_hardware lpass_platform_rxtx_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE,
+ .period_bytes_max = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
+ LPASS_PLATFORM_PERIODS,
+ .period_bytes_min = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
+ LPASS_PLATFORM_PERIODS,
+ .periods_min = LPASS_PLATFORM_PERIODS,
+ .periods_max = LPASS_PLATFORM_PERIODS,
+ .fifo_size = 0,
+};
+
+static const struct snd_pcm_hardware lpass_platform_va_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE,
+ .period_bytes_max = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE /
+ LPASS_PLATFORM_PERIODS,
+ .period_bytes_min = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE /
+ LPASS_PLATFORM_PERIODS,
+ .periods_min = LPASS_PLATFORM_PERIODS,
+ .periods_max = LPASS_PLATFORM_PERIODS,
+ .fifo_size = 0,
+};
+
+static int lpass_platform_alloc_rxtx_dmactl_fields(struct device *dev,
+ struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
+ int rval;
+
+ rd_dmactl = devm_kzalloc(dev, sizeof(*rd_dmactl), GFP_KERNEL);
+ if (!rd_dmactl)
+ return -ENOMEM;
+
+ wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL);
+ if (!wr_dmactl)
+ return -ENOMEM;
+
+ drvdata->rxtx_rd_dmactl = rd_dmactl;
+ drvdata->rxtx_wr_dmactl = wr_dmactl;
+
+ rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf,
+ &v->rxtx_rdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
+ if (rval)
+ return rval;
+
+ return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
+ &v->rxtx_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
+}
+
+static int lpass_platform_alloc_va_dmactl_fields(struct device *dev,
+ struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpaif_dmactl *wr_dmactl;
+
+ wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL);
+ if (!wr_dmactl)
+ return -ENOMEM;
+
+ drvdata->va_wr_dmactl = wr_dmactl;
+ return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
+ &v->va_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
+}
+
+
+static int lpass_platform_alloc_dmactl_fields(struct device *dev,
+ struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
+ int rval;
+
+ drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
+ GFP_KERNEL);
+ if (drvdata->rd_dmactl == NULL)
+ return -ENOMEM;
+
+ drvdata->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
+ GFP_KERNEL);
+ if (drvdata->wr_dmactl == NULL)
+ return -ENOMEM;
+
+ rd_dmactl = drvdata->rd_dmactl;
+ wr_dmactl = drvdata->wr_dmactl;
+
+ rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf,
+ &v->rdma_intf, 6);
+ if (rval)
+ return rval;
+
+ return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
+ &v->wrdma_intf, 6);
+}
+
+static int lpass_platform_alloc_hdmidmactl_fields(struct device *dev,
+ struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpaif_dmactl *rd_dmactl;
+
+ rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), GFP_KERNEL);
+ if (rd_dmactl == NULL)
+ return -ENOMEM;
+
+ drvdata->hdmi_rd_dmactl = rd_dmactl;
+
+ return devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->bursten,
+ &v->hdmi_rdma_bursten, 8);
+}
+
+static int lpass_platform_pcmops_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- int ret;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ const struct lpass_variant *v = drvdata->variant;
+ int ret, dma_ch, dir = substream->stream;
+ struct lpass_pcm_data *data;
+ struct regmap *map;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->i2s_port = cpu_dai->driver->id;
+ runtime->private_data = data;
- snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
+ if (v->alloc_dma_channel)
+ dma_ch = v->alloc_dma_channel(drvdata, dir, dai_id);
+ else
+ dma_ch = 0;
- runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
+ if (dma_ch < 0) {
+ kfree(data);
+ return dma_ch;
+ }
+ switch (dai_id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ map = drvdata->lpaif_map;
+ drvdata->substream[dma_ch] = substream;
+ break;
+ case LPASS_DP_RX:
+ map = drvdata->hdmiif_map;
+ drvdata->hdmi_substream[dma_ch] = substream;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ map = drvdata->rxtx_lpaif_map;
+ drvdata->rxtx_substream[dma_ch] = substream;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ map = drvdata->va_lpaif_map;
+ drvdata->va_substream[dma_ch] = substream;
+ break;
+ default:
+ break;
+ }
+
+ data->dma_ch = dma_ch;
+ switch (dai_id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ case LPASS_DP_RX:
+ ret = regmap_write(map, LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0);
+ if (ret) {
+ kfree(data);
+ dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret);
+ return ret;
+ }
+ snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
+ runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ snd_soc_set_runtime_hwparams(substream, &lpass_platform_rxtx_hardware);
+ runtime->dma_bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ snd_soc_set_runtime_hwparams(substream, &lpass_platform_va_hardware);
+ runtime->dma_bytes = lpass_platform_va_hardware.buffer_bytes_max;
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ break;
+ default:
+ break;
+ }
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
- dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n",
- __func__, ret);
+ kfree(data);
+ dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
+ ret);
return -EINVAL;
}
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int lpass_platform_pcmops_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpass_pcm_data *data;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ data = runtime->private_data;
+ switch (dai_id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ drvdata->substream[data->dma_ch] = NULL;
+ break;
+ case LPASS_DP_RX:
+ drvdata->hdmi_substream[data->dma_ch] = NULL;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ drvdata->rxtx_substream[data->dma_ch] = NULL;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ drvdata->va_substream[data->dma_ch] = NULL;
+ break;
+ default:
+ break;
+ }
+
+ if (v->free_dma_channel)
+ v->free_dma_channel(drvdata, data->dma_ch, dai_id);
+
+ kfree(data);
return 0;
}
-static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_substream *substream,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct lpaif_dmactl *dmactl = NULL;
+
+ switch (cpu_dai->driver->id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dmactl = drvdata->rd_dmactl;
+ else
+ dmactl = drvdata->wr_dmactl;
+ break;
+ case LPASS_DP_RX:
+ dmactl = drvdata->hdmi_rd_dmactl;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ dmactl = drvdata->rxtx_rd_dmactl;
+ break;
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ dmactl = drvdata->rxtx_wr_dmactl;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ dmactl = drvdata->va_wr_dmactl;
+ break;
+ }
+
+ return dmactl;
+}
+
+static int __lpass_get_id(const struct snd_pcm_substream *substream,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
+ int id;
+
+ switch (cpu_dai->driver->id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ id = pcm_data->dma_ch;
+ else
+ id = pcm_data->dma_ch - v->wrdma_channel_start;
+ break;
+ case LPASS_DP_RX:
+ id = pcm_data->dma_ch;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ id = pcm_data->dma_ch;
+ break;
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ id = pcm_data->dma_ch - v->va_wrdma_channel_start;
+ break;
+ }
+
+ return id;
+}
+
+static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream *substream,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct regmap *map = NULL;
+
+ switch (cpu_dai->driver->id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ map = drvdata->lpaif_map;
+ break;
+ case LPASS_DP_RX:
+ map = drvdata->hdmiif_map;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ map = drvdata->rxtx_lpaif_map;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ map = drvdata->va_lpaif_map;
+ break;
+ }
+
+ return map;
+}
+
+static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
unsigned int regval;
+ struct lpaif_dmactl *dmactl;
+ int id;
int bitwidth;
- int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
+ int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ dmactl = __lpass_get_dmactl_handle(substream, component);
+ id = __lpass_get_id(substream, component);
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
- dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
- __func__, bitwidth);
+ dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
+ bitwidth);
return bitwidth;
}
- regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
- LPAIF_RDMACTL_AUDINTF(rdma_port) |
- LPAIF_RDMACTL_FIFOWM_8;
+ ret = regmap_fields_write(dmactl->bursten, id, LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating bursten field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret);
+ return ret;
+ }
+
+ switch (dai_id) {
+ case LPASS_DP_RX:
+ ret = regmap_fields_write(dmactl->burst8, id,
+ LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating burst8en field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->burst16, id,
+ LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating burst16en field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->dynburst, id,
+ LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating dynbursten field: %d\n", ret);
+ return ret;
+ }
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ case MI2S_TERTIARY:
+ case MI2S_QUATERNARY:
+ case MI2S_QUINARY:
+ ret = regmap_fields_write(dmactl->intf, id,
+ LPAIF_DMACTL_AUDINTF(dma_port));
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating audio interface field: %d\n",
+ ret);
+ return ret;
+ }
+
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai_id);
+ break;
+ }
switch (bitwidth) {
case 16:
switch (channels) {
case 1:
case 2:
- regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+ regval = LPAIF_DMACTL_WPSCNT_ONE;
break;
case 4:
- regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+ regval = LPAIF_DMACTL_WPSCNT_TWO;
break;
case 6:
- regval |= LPAIF_RDMACTL_WPSCNT_THREE;
+ regval = LPAIF_DMACTL_WPSCNT_THREE;
break;
case 8:
- regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+ regval = LPAIF_DMACTL_WPSCNT_FOUR;
break;
default:
- dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
- __func__, bitwidth, channels);
+ dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
+ bitwidth, channels);
return -EINVAL;
}
break;
@@ -130,173 +522,331 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
case 32:
switch (channels) {
case 1:
- regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+ regval = LPAIF_DMACTL_WPSCNT_ONE;
break;
case 2:
- regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+ regval = (dai_id == LPASS_DP_RX ?
+ LPAIF_DMACTL_WPSCNT_ONE :
+ LPAIF_DMACTL_WPSCNT_TWO);
break;
case 4:
- regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+ regval = (dai_id == LPASS_DP_RX ?
+ LPAIF_DMACTL_WPSCNT_TWO :
+ LPAIF_DMACTL_WPSCNT_FOUR);
break;
case 6:
- regval |= LPAIF_RDMACTL_WPSCNT_SIX;
+ regval = (dai_id == LPASS_DP_RX ?
+ LPAIF_DMACTL_WPSCNT_THREE :
+ LPAIF_DMACTL_WPSCNT_SIX);
break;
case 8:
- regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
+ regval = (dai_id == LPASS_DP_RX ?
+ LPAIF_DMACTL_WPSCNT_FOUR :
+ LPAIF_DMACTL_WPSCNT_EIGHT);
break;
default:
- dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
- __func__, bitwidth, channels);
+ dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
+ bitwidth, channels);
return -EINVAL;
}
break;
default:
- dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
- __func__, bitwidth, channels);
+ dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
+ bitwidth, channels);
return -EINVAL;
}
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
+ ret = regmap_fields_write(dmactl->wpscnt, id, regval);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to dmactl reg: %d\n",
+ ret);
return ret;
}
return 0;
}
-static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
+static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
+ unsigned int reg;
int ret;
+ struct regmap *map;
+ unsigned int dai_id = cpu_dai->driver->id;
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
+ if (is_cdc_dma_port(dai_id))
+ return 0;
+ map = __lpass_get_regmap_handle(substream, component);
+
+ reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id);
+ ret = regmap_write(map, reg, 0);
if (ret)
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
+ ret);
return ret;
}
-static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
+static int lpass_platform_pcmops_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
- int ret, ch = pcm_data->rdma_ch;
-
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMABASE_REG(v, ch),
- runtime->dma_addr);
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpaif_dmactl *dmactl;
+ struct regmap *map;
+ int ret, id, ch, dir = substream->stream;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ ch = pcm_data->dma_ch;
+
+ dmactl = __lpass_get_dmactl_handle(substream, component);
+ id = __lpass_get_id(substream, component);
+ map = __lpass_get_regmap_handle(substream, component);
+
+ ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id),
+ runtime->dma_addr);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
+ ret);
return ret;
}
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMABUFF_REG(v, ch),
+ ret = regmap_write(map, LPAIF_DMABUFF_REG(v, ch, dir, dai_id),
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
+ ret);
return ret;
}
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMAPER_REG(v, ch),
+ ret = regmap_write(map, LPAIF_DMAPER_REG(v, ch, dir, dai_id),
(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
+ ret);
return ret;
}
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, ch),
- LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
+ if (is_cdc_dma_port(dai_id)) {
+ ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error writing fifowm field to dmactl reg: %d, id: %d\n",
+ ret, id);
+ return ret;
+ }
+ }
+ ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
+ ret);
return ret;
}
return 0;
}
-static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
- int cmd)
+static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int cmd)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
- int ret, ch = pcm_data->rdma_ch;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
+ struct lpaif_dmactl *dmactl;
+ struct regmap *map;
+ int ret, ch, id;
+ unsigned int reg_irqclr = 0, val_irqclr = 0;
+ unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ ch = pcm_data->dma_ch;
+ dmactl = __lpass_get_dmactl_handle(substream, component);
+ id = __lpass_get_id(substream, component);
+ map = __lpass_get_regmap_handle(substream, component);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- /* clear status before enabling interrupts */
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(ch));
+ ret = regmap_fields_write(dmactl->enable, id,
+ LPAIF_DMACTL_ENABLE_ON);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg: %d\n", ret);
return ret;
}
+ switch (dai_id) {
+ case LPASS_DP_RX:
+ ret = regmap_fields_write(dmactl->dyncclk, id,
+ LPAIF_DMACTL_DYNCLK_ON);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg: %d\n", ret);
+ return ret;
+ }
+ reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
+ val_irqclr = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+
+ reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
+ val_mask = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+ val_irqen = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ case MI2S_TERTIARY:
+ case MI2S_QUATERNARY:
+ case MI2S_QUINARY:
+ reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_irqclr = LPAIF_IRQ_ALL(ch);
+
+
+ reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = LPAIF_IRQ_ALL(ch);
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg field: %d\n", ret);
+ return ret;
+ }
+ reg_irqclr = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_irqclr = LPAIF_IRQ_ALL(ch);
+
+ reg_irqen = LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = LPAIF_IRQ_ALL(ch);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg field: %d\n", ret);
+ return ret;
+ }
+ reg_irqclr = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_irqclr = LPAIF_IRQ_ALL(ch);
+
+ reg_irqen = LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = LPAIF_IRQ_ALL(ch);
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(ch),
- LPAIF_IRQ_ALL(ch));
+ ret = regmap_write_bits(map, reg_irqclr, val_irqclr, val_irqclr);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", ret);
return ret;
}
-
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, ch),
- LPAIF_RDMACTL_ENABLE_MASK,
- LPAIF_RDMACTL_ENABLE_ON);
+ ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret);
return ret;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, ch),
- LPAIF_RDMACTL_ENABLE_MASK,
- LPAIF_RDMACTL_ENABLE_OFF);
+ ret = regmap_fields_write(dmactl->enable, id,
+ LPAIF_DMACTL_ENABLE_OFF);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg: %d\n", ret);
return ret;
}
+ switch (dai_id) {
+ case LPASS_DP_RX:
+ ret = regmap_fields_write(dmactl->dyncclk, id,
+ LPAIF_DMACTL_DYNCLK_OFF);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg: %d\n", ret);
+ return ret;
+ }
+ reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
+ val_mask = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+ val_irqen = 0;
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ case MI2S_TERTIARY:
+ case MI2S_QUATERNARY:
+ case MI2S_QUINARY:
+ reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = 0;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg field: %d\n", ret);
+ return ret;
+ }
+
+ reg_irqclr = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_irqclr = LPAIF_IRQ_ALL(ch);
+
+ reg_irqen = LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = LPAIF_IRQ_ALL(ch);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg field: %d\n", ret);
+ return ret;
+ }
+
+ reg_irqclr = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_irqclr = LPAIF_IRQ_ALL(ch);
+
+ reg_irqen = LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = LPAIF_IRQ_ALL(ch);
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(ch), 0);
+ ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev,
+ "error writing to irqen reg: %d\n", ret);
return ret;
}
break;
@@ -306,73 +856,123 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
+ struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
+ const struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr;
- int ret, ch = pcm_data->rdma_ch;
+ int ret, ch, dir = substream->stream;
+ struct regmap *map;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ map = __lpass_get_regmap_handle(substream, component);
+ ch = pcm_data->dma_ch;
- ret = regmap_read(drvdata->lpaif_map,
- LPAIF_RDMABASE_REG(v, ch), &base_addr);
+ ret = regmap_read(map,
+ LPAIF_DMABASE_REG(v, ch, dir, dai_id), &base_addr);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev,
+ "error reading from rdmabase reg: %d\n", ret);
return ret;
}
- ret = regmap_read(drvdata->lpaif_map,
- LPAIF_RDMACURR_REG(v, ch), &curr_addr);
+ ret = regmap_read(map,
+ LPAIF_DMACURR_REG(v, ch, dir, dai_id), &curr_addr);
if (ret) {
- dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
- __func__, ret);
+ dev_err(soc_runtime->dev,
+ "error reading from rdmacurr reg: %d\n", ret);
return ret;
}
return bytes_to_frames(substream->runtime, curr_addr - base_addr);
}
-static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int lpass_platform_cdc_dma_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long size, offset;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ size = vma->vm_end - vma->vm_start;
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ return io_remap_pfn_range(vma, vma->vm_start,
+ (runtime->dma_addr + offset) >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
- return dma_mmap_coherent(substream->pcm->card->dev, vma,
- runtime->dma_area, runtime->dma_addr,
- runtime->dma_bytes);
}
-static struct snd_pcm_ops lpass_platform_pcm_ops = {
- .open = lpass_platform_pcmops_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = lpass_platform_pcmops_hw_params,
- .hw_free = lpass_platform_pcmops_hw_free,
- .prepare = lpass_platform_pcmops_prepare,
- .trigger = lpass_platform_pcmops_trigger,
- .pointer = lpass_platform_pcmops_pointer,
- .mmap = lpass_platform_pcmops_mmap,
-};
+static int lpass_platform_pcmops_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ if (is_cdc_dma_port(dai_id))
+ return lpass_platform_cdc_dma_mmap(substream, vma);
+
+ return snd_pcm_lib_default_mmap(substream, vma);
+}
static irqreturn_t lpass_dma_interrupt_handler(
struct snd_pcm_substream *substream,
struct lpass_data *drvdata,
int chan, u32 interrupts)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_variant *v = drvdata->variant;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ const struct lpass_variant *v = drvdata->variant;
irqreturn_t ret = IRQ_NONE;
int rv;
-
+ unsigned int reg, val, mask;
+ struct regmap *map;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ mask = LPAIF_IRQ_ALL(chan);
+ switch (dai_id) {
+ case LPASS_DP_RX:
+ map = drvdata->hdmiif_map;
+ reg = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
+ val = (LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan));
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ case MI2S_TERTIARY:
+ case MI2S_QUATERNARY:
+ case MI2S_QUINARY:
+ map = drvdata->lpaif_map;
+ reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val = 0;
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ map = drvdata->rxtx_lpaif_map;
+ reg = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val = 0;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ map = drvdata->va_lpaif_map;
+ reg = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val = 0;
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
if (interrupts & LPAIF_IRQ_PER(chan)) {
- rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_PER(chan));
+ rv = regmap_write_bits(map, reg, mask, (LPAIF_IRQ_PER(chan) | val));
if (rv) {
- dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
- __func__, rv);
+ dev_err(soc_runtime->dev,
+ "error writing to irqclear reg: %d\n", rv);
return IRQ_NONE;
}
snd_pcm_period_elapsed(substream);
@@ -380,48 +980,54 @@ static irqreturn_t lpass_dma_interrupt_handler(
}
if (interrupts & LPAIF_IRQ_XRUN(chan)) {
- rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_XRUN(chan));
+ rv = regmap_write_bits(map, reg, mask, (LPAIF_IRQ_XRUN(chan) | val));
if (rv) {
- dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
- __func__, rv);
+ dev_err(soc_runtime->dev,
+ "error writing to irqclear reg: %d\n", rv);
return IRQ_NONE;
}
- dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ dev_warn_ratelimited(soc_runtime->dev, "xrun warning\n");
+
+ snd_pcm_stop_xrun(substream);
ret = IRQ_HANDLED;
}
if (interrupts & LPAIF_IRQ_ERR(chan)) {
- rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ERR(chan));
+ rv = regmap_write_bits(map, reg, mask, (LPAIF_IRQ_ERR(chan) | val));
if (rv) {
- dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
- __func__, rv);
+ dev_err(soc_runtime->dev,
+ "error writing to irqclear reg: %d\n", rv);
return IRQ_NONE;
}
- dev_err(soc_runtime->dev, "%s() bus access error\n", __func__);
+ dev_err(soc_runtime->dev, "bus access error\n");
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
ret = IRQ_HANDLED;
}
+ if (interrupts & val) {
+ rv = regmap_write(map, reg, val);
+ if (rv) {
+ dev_err(soc_runtime->dev,
+ "error writing to irqclear reg: %d\n", rv);
+ return IRQ_NONE;
+ }
+ ret = IRQ_HANDLED;
+ }
+
return ret;
}
static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
{
struct lpass_data *drvdata = data;
- struct lpass_variant *v = drvdata->variant;
+ const struct lpass_variant *v = drvdata->variant;
unsigned int irqs;
int rv, chan;
rv = regmap_read(drvdata->lpaif_map,
LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
if (rv) {
- pr_err("%s() error reading from irqstat reg: %d\n",
- __func__, rv);
+ pr_err("error reading from irqstat reg: %d\n", rv);
return IRQ_NONE;
}
@@ -439,131 +1045,251 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
- struct snd_soc_pcm_runtime *soc_runtime)
+static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data)
{
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
+ struct lpass_data *drvdata = data;
+ const struct lpass_variant *v = drvdata->variant;
+ unsigned int irqs;
+ int rv, chan;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = soc_runtime->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr,
- GFP_KERNEL);
- if (!buf->area) {
- dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n",
- __func__);
- return -ENOMEM;
+ rv = regmap_read(drvdata->hdmiif_map,
+ LPASS_HDMITX_APP_IRQSTAT_REG(v), &irqs);
+ if (rv) {
+ pr_err("error reading from irqstat reg: %d\n", rv);
+ return IRQ_NONE;
}
- buf->bytes = size;
- return 0;
+ /* Handle per channel interrupts */
+ for (chan = 0; chan < LPASS_MAX_HDMI_DMA_CHANNELS; chan++) {
+ if (irqs & (LPAIF_IRQ_ALL(chan) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan))
+ && drvdata->hdmi_substream[chan]) {
+ rv = lpass_dma_interrupt_handler(
+ drvdata->hdmi_substream[chan],
+ drvdata, chan, irqs);
+ if (rv != IRQ_HANDLED)
+ return rv;
+ }
+ }
+ return IRQ_HANDLED;
}
-static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
- struct snd_soc_pcm_runtime *soc_runtime)
+static irqreturn_t lpass_platform_rxtxif_irq(int irq, void *data)
{
- struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct lpass_data *drvdata = data;
+ const struct lpass_variant *v = drvdata->variant;
+ unsigned int irqs;
+ irqreturn_t rv;
+ int chan;
- if (buf->area) {
- dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area,
- buf->addr);
+ rv = regmap_read(drvdata->rxtx_lpaif_map,
+ LPAIF_RXTX_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+
+ /* Handle per channel interrupts */
+ for (chan = 0; chan < LPASS_MAX_CDC_DMA_CHANNELS; chan++) {
+ if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->rxtx_substream[chan]) {
+ rv = lpass_dma_interrupt_handler(
+ drvdata->rxtx_substream[chan],
+ drvdata, chan, irqs);
+ if (rv != IRQ_HANDLED)
+ return rv;
+ }
}
- buf->area = NULL;
+
+ return IRQ_HANDLED;
}
-static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
+static irqreturn_t lpass_platform_vaif_irq(int irq, void *data)
{
- struct snd_pcm *pcm = soc_runtime->pcm;
- struct snd_pcm_substream *substream =
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
- int ret;
- struct lpass_pcm_data *data;
-
- data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
+ struct lpass_data *drvdata = data;
+ const struct lpass_variant *v = drvdata->variant;
+ unsigned int irqs;
+ irqreturn_t rv;
+ int chan;
- if (v->alloc_dma_channel)
- data->rdma_ch = v->alloc_dma_channel(drvdata);
+ rv = regmap_read(drvdata->va_lpaif_map,
+ LPAIF_VA_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
- if (IS_ERR_VALUE(data->rdma_ch))
- return data->rdma_ch;
+ /* Handle per channel interrupts */
+ for (chan = 0; chan < LPASS_MAX_VA_CDC_DMA_CHANNELS; chan++) {
+ if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->va_substream[chan]) {
+ rv = lpass_dma_interrupt_handler(
+ drvdata->va_substream[chan],
+ drvdata, chan, irqs);
+ if (rv != IRQ_HANDLED)
+ return rv;
+ }
+ }
+ return IRQ_HANDLED;
+}
- drvdata->substream[data->rdma_ch] = substream;
- data->i2s_port = cpu_dai->driver->id;
+static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *component,
+ struct snd_pcm *pcm, int dai_id)
+{
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
- snd_soc_pcm_set_drvdata(soc_runtime, data);
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ else
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
- soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
- soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
+ buf = &substream->dma_buffer;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
- ret = lpass_platform_alloc_buffer(substream, soc_runtime);
- if (ret)
- return ret;
+ /* Assign Codec DMA buffer pointers */
+ buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
- if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
- goto err_buf;
+ switch (dai_id) {
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
+ buf->addr = drvdata->rxtx_cdc_dma_lpm_buf;
+ break;
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
+ buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ buf->bytes = lpass_platform_va_hardware.buffer_bytes_max;
+ buf->addr = drvdata->va_cdc_dma_lpm_buf;
+ break;
+ default:
+ break;
}
+ buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WC);
+
return 0;
+}
-err_buf:
- lpass_platform_free_buffer(substream, soc_runtime);
- return ret;
+static int lpass_platform_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *soc_runtime)
+{
+ struct snd_pcm *pcm = soc_runtime->pcm;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
+
+ /*
+ * Lpass codec dma can access only lpass lpm hardware memory.
+ * ioremap is for HLOS to access hardware memory.
+ */
+ if (is_cdc_dma_port(dai_id))
+ return lpass_platform_prealloc_cdc_dma_buffer(component, pcm, dai_id);
+
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_NONCOHERENT,
+ component->dev, size);
}
-static void lpass_platform_pcm_free(struct snd_pcm *pcm)
+static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
{
- struct snd_pcm_substream *substream =
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_variant *v = drvdata->variant;
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct regmap *map;
- drvdata->substream[data->rdma_ch] = NULL;
+ if (drvdata->hdmi_port_enable) {
+ map = drvdata->hdmiif_map;
+ regcache_cache_only(map, true);
+ regcache_mark_dirty(map);
+ }
- if (v->free_dma_channel)
- v->free_dma_channel(drvdata, data->rdma_ch);
+ map = drvdata->lpaif_map;
+ regcache_cache_only(map, true);
+ regcache_mark_dirty(map);
- lpass_platform_free_buffer(substream, soc_runtime);
+ return 0;
+}
+
+static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
+{
+ struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+ struct regmap *map;
+ int ret;
+
+ if (drvdata->hdmi_port_enable) {
+ map = drvdata->hdmiif_map;
+ regcache_cache_only(map, false);
+ ret = regcache_sync(map);
+ if (ret)
+ return ret;
+ }
+
+ map = drvdata->lpaif_map;
+ regcache_cache_only(map, false);
+
+ return regcache_sync(map);
+}
+
+static int lpass_platform_copy(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int channel,
+ unsigned long pos, struct iov_iter *buf,
+ unsigned long bytes)
+{
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ unsigned int dai_id = cpu_dai->driver->id;
+ int ret = 0;
+
+ void __iomem *dma_buf = (void __iomem *) (rt->dma_area + pos +
+ channel * (rt->dma_bytes / rt->channels));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (is_cdc_dma_port(dai_id)) {
+ if (copy_from_iter_toio(dma_buf, bytes, buf) != bytes)
+ ret = -EFAULT;
+ } else {
+ if (copy_from_iter((void __force *)dma_buf, bytes, buf) != bytes)
+ ret = -EFAULT;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (is_cdc_dma_port(dai_id)) {
+ if (copy_to_iter_fromio(dma_buf, bytes, buf) != bytes)
+ ret = -EFAULT;
+ } else {
+ if (copy_to_iter((void __force *)dma_buf, bytes, buf) != bytes)
+ ret = -EFAULT;
+ }
+ }
+
+ return ret;
}
-static struct snd_soc_platform_driver lpass_platform_driver = {
- .pcm_new = lpass_platform_pcm_new,
- .pcm_free = lpass_platform_pcm_free,
- .ops = &lpass_platform_pcm_ops,
+static const struct snd_soc_component_driver lpass_component_driver = {
+ .name = DRV_NAME,
+ .open = lpass_platform_pcmops_open,
+ .close = lpass_platform_pcmops_close,
+ .hw_params = lpass_platform_pcmops_hw_params,
+ .hw_free = lpass_platform_pcmops_hw_free,
+ .prepare = lpass_platform_pcmops_prepare,
+ .trigger = lpass_platform_pcmops_trigger,
+ .pointer = lpass_platform_pcmops_pointer,
+ .mmap = lpass_platform_pcmops_mmap,
+ .pcm_construct = lpass_platform_pcm_new,
+ .suspend = lpass_platform_pcmops_suspend,
+ .resume = lpass_platform_pcmops_resume,
+ .copy = lpass_platform_copy,
+
};
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
- struct lpass_variant *v = drvdata->variant;
+ const struct lpass_variant *v = drvdata->variant;
int ret;
drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
- if (drvdata->lpaif_irq < 0) {
- dev_err(&pdev->dev, "%s() error getting irq handle: %d\n",
- __func__, drvdata->lpaif_irq);
+ if (drvdata->lpaif_irq < 0)
return -ENODEV;
- }
/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
if (ret) {
- dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
- __func__, ret);
+ dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
return ret;
}
@@ -571,16 +1297,100 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
"lpass-irq-lpaif", drvdata);
if (ret) {
- dev_err(&pdev->dev, "%s() irq request failed: %d\n",
- __func__, ret);
+ dev_err(&pdev->dev, "irq request failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = lpass_platform_alloc_dmactl_fields(&pdev->dev,
+ drvdata->lpaif_map);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "error initializing dmactl fields: %d\n", ret);
return ret;
}
+ if (drvdata->codec_dma_enable) {
+ ret = regmap_write(drvdata->rxtx_lpaif_map,
+ LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0x0);
+ if (ret) {
+ dev_err(&pdev->dev, "error writing to rxtx irqen reg: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(drvdata->va_lpaif_map,
+ LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0x0);
+ if (ret) {
+ dev_err(&pdev->dev, "error writing to rxtx irqen reg: %d\n", ret);
+ return ret;
+ }
+ drvdata->rxtxif_irq = platform_get_irq_byname(pdev, "lpass-irq-rxtxif");
+ if (drvdata->rxtxif_irq < 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, drvdata->rxtxif_irq,
+ lpass_platform_rxtxif_irq, 0, "lpass-irq-rxtxif", drvdata);
+ if (ret) {
+ dev_err(&pdev->dev, "rxtx irq request failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = lpass_platform_alloc_rxtx_dmactl_fields(&pdev->dev,
+ drvdata->rxtx_lpaif_map);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "error initializing rxtx dmactl fields: %d\n", ret);
+ return ret;
+ }
- return devm_snd_soc_register_platform(&pdev->dev,
- &lpass_platform_driver);
+ drvdata->vaif_irq = platform_get_irq_byname(pdev, "lpass-irq-vaif");
+ if (drvdata->vaif_irq < 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, drvdata->vaif_irq,
+ lpass_platform_vaif_irq, 0, "lpass-irq-vaif", drvdata);
+ if (ret) {
+ dev_err(&pdev->dev, "va irq request failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = lpass_platform_alloc_va_dmactl_fields(&pdev->dev,
+ drvdata->va_lpaif_map);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "error initializing va dmactl fields: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (drvdata->hdmi_port_enable) {
+ drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi");
+ if (drvdata->hdmiif_irq < 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, drvdata->hdmiif_irq,
+ lpass_platform_hdmiif_irq, 0, "lpass-irq-hdmi", drvdata);
+ if (ret) {
+ dev_err(&pdev->dev, "irq hdmi request failed: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(drvdata->hdmiif_map,
+ LPASS_HDMITX_APP_IRQEN_REG(v), 0);
+ if (ret) {
+ dev_err(&pdev->dev, "error writing to hdmi irqen reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = lpass_platform_alloc_hdmidmactl_fields(&pdev->dev,
+ drvdata->hdmiif_map);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "error initializing hdmidmactl fields: %d\n", ret);
+ return ret;
+ }
+ }
+ return devm_snd_soc_register_component(&pdev->dev,
+ &lpass_component_driver, NULL, 0);
}
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
MODULE_DESCRIPTION("QTi LPASS Platform Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
new file mode 100644
index 000000000000..7c6a9b0fda89
--- /dev/null
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <dt-bindings/sound/sc7180-lpass.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = {
+ {
+ .id = MI2S_PRIMARY,
+ .name = "Primary MI2S",
+ .playback = {
+ .stream_name = "Primary Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "Primary Capture",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ }, {
+ .id = MI2S_SECONDARY,
+ .name = "Secondary MI2S",
+ .playback = {
+ .stream_name = "Secondary Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_cpu_dai_ops2,
+ }, {
+ .id = LPASS_DP_RX,
+ .name = "Hdmi",
+ .playback = {
+ .stream_name = "Hdmi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S24,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_hdmi_dai_ops,
+ },
+};
+
+static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+ int direction, unsigned int dai_id)
+{
+ const struct lpass_variant *v = drvdata->variant;
+ int chan = 0;
+
+ if (dai_id == LPASS_DP_RX) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map,
+ v->hdmi_rdma_channels);
+
+ if (chan >= v->hdmi_rdma_channels)
+ return -EBUSY;
+ }
+ set_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
+ } else {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
+ v->rdma_channels);
+
+ if (chan >= v->rdma_channels)
+ return -EBUSY;
+ } else {
+ chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+ v->wrdma_channel_start +
+ v->wrdma_channels,
+ v->wrdma_channel_start);
+
+ if (chan >= v->wrdma_channel_start + v->wrdma_channels)
+ return -EBUSY;
+ }
+
+ set_bit(chan, &drvdata->dma_ch_bit_map);
+ }
+ return chan;
+}
+
+static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
+{
+ if (dai_id == LPASS_DP_RX)
+ clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
+ else
+ clear_bit(chan, &drvdata->dma_ch_bit_map);
+
+ return 0;
+}
+
+static int sc7180_lpass_init(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ const struct lpass_variant *variant = drvdata->variant;
+ struct device *dev = &pdev->dev;
+ int ret, i;
+
+ drvdata->clks = devm_kcalloc(dev, variant->num_clks,
+ sizeof(*drvdata->clks), GFP_KERNEL);
+ if (!drvdata->clks)
+ return -ENOMEM;
+
+ drvdata->num_clks = variant->num_clks;
+
+ for (i = 0; i < drvdata->num_clks; i++)
+ drvdata->clks[i].id = variant->clk_name[i];
+
+ ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);
+ if (ret) {
+ dev_err(dev, "Failed to get clocks %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+ if (ret) {
+ dev_err(dev, "sc7180 clk_enable failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sc7180_lpass_exit(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+ clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+ return 0;
+}
+
+static int sc7180_lpass_dev_resume(struct device *dev)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+}
+
+static int sc7180_lpass_dev_suspend(struct device *dev)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+ return 0;
+}
+
+static const struct dev_pm_ops sc7180_lpass_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(sc7180_lpass_dev_suspend, sc7180_lpass_dev_resume)
+};
+
+static const struct lpass_variant sc7180_data = {
+ .i2sctrl_reg_base = 0x1000,
+ .i2sctrl_reg_stride = 0x1000,
+ .i2s_ports = 3,
+ .irq_reg_base = 0x9000,
+ .irq_reg_stride = 0x1000,
+ .irq_ports = 3,
+ .rdma_reg_base = 0xC000,
+ .rdma_reg_stride = 0x1000,
+ .rdma_channels = 5,
+ .hdmi_rdma_reg_base = 0x64000,
+ .hdmi_rdma_reg_stride = 0x1000,
+ .hdmi_rdma_channels = 4,
+ .dmactl_audif_start = 1,
+ .wrdma_reg_base = 0x18000,
+ .wrdma_reg_stride = 0x1000,
+ .wrdma_channel_start = 5,
+ .wrdma_channels = 4,
+
+ .loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
+ .spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
+ .spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
+ .spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
+ .micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
+ .micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
+ .micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
+ .wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
+ .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000),
+
+ .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
+ .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
+ .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000),
+ .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
+ .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000),
+ .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000),
+
+ .wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000),
+ .wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000),
+ .wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000),
+ .wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000),
+ .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000),
+ .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000),
+
+ .hdmi_tx_ctl_addr = 0x1000,
+ .hdmi_legacy_addr = 0x1008,
+ .hdmi_vbit_addr = 0x610c0,
+ .hdmi_ch_lsb_addr = 0x61048,
+ .hdmi_ch_msb_addr = 0x6104c,
+ .ch_stride = 0x8,
+ .hdmi_parity_addr = 0x61034,
+ .hdmi_dmactl_addr = 0x61038,
+ .hdmi_dma_stride = 0x4,
+ .hdmi_DP_addr = 0x610c8,
+ .hdmi_sstream_addr = 0x6101c,
+ .hdmi_irq_reg_base = 0x63000,
+ .hdmi_irq_ports = 1,
+
+ .hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
+ .hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
+ .hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
+ .hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
+ .hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
+ .hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
+ .hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
+ .hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
+
+ .sstream_en = REG_FIELD(0x6101c, 0, 0),
+ .dma_sel = REG_FIELD(0x6101c, 1, 2),
+ .auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
+ .layout = REG_FIELD(0x6101c, 4, 4),
+ .layout_sp = REG_FIELD(0x6101c, 5, 8),
+ .set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
+ .dp_audio = REG_FIELD(0x6101c, 11, 11),
+ .dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
+ .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
+
+ .mute = REG_FIELD(0x610c8, 0, 0),
+ .as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
+ .as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
+ .aif_db4 = REG_FIELD(0x610c8, 8, 15),
+ .frequency = REG_FIELD(0x610c8, 16, 21),
+ .mst_index = REG_FIELD(0x610c8, 28, 29),
+ .dptx_index = REG_FIELD(0x610c8, 30, 31),
+
+ .soft_reset = REG_FIELD(0x1000, 31, 31),
+ .force_reset = REG_FIELD(0x1000, 30, 30),
+
+ .use_hw_chs = REG_FIELD(0x61038, 0, 0),
+ .use_hw_usr = REG_FIELD(0x61038, 1, 1),
+ .hw_chs_sel = REG_FIELD(0x61038, 2, 4),
+ .hw_usr_sel = REG_FIELD(0x61038, 5, 6),
+
+ .replace_vbit = REG_FIELD(0x610c0, 0, 0),
+ .vbit_stream = REG_FIELD(0x610c0, 1, 1),
+
+ .legacy_en = REG_FIELD(0x1008, 0, 0),
+ .calc_en = REG_FIELD(0x61034, 0, 0),
+ .lsb_bits = REG_FIELD(0x61048, 0, 31),
+ .msb_bits = REG_FIELD(0x6104c, 0, 31),
+
+
+ .clk_name = (const char*[]) {
+ "pcnoc-sway-clk",
+ "audio-core",
+ "pcnoc-mport-clk",
+ },
+ .num_clks = 3,
+ .dai_driver = sc7180_lpass_cpu_dai_driver,
+ .num_dai = ARRAY_SIZE(sc7180_lpass_cpu_dai_driver),
+ .dai_osr_clk_names = (const char *[]) {
+ "mclk0",
+ "null",
+ },
+ .dai_bit_clk_names = (const char *[]) {
+ "mi2s-bit-clk0",
+ "mi2s-bit-clk1",
+ },
+ .init = sc7180_lpass_init,
+ .exit = sc7180_lpass_exit,
+ .alloc_dma_channel = sc7180_lpass_alloc_dma_channel,
+ .free_dma_channel = sc7180_lpass_free_dma_channel,
+};
+
+static const struct of_device_id sc7180_lpass_cpu_device_id[] __maybe_unused = {
+ {.compatible = "qcom,sc7180-lpass-cpu", .data = &sc7180_data},
+ {}
+};
+MODULE_DEVICE_TABLE(of, sc7180_lpass_cpu_device_id);
+
+static struct platform_driver sc7180_lpass_cpu_platform_driver = {
+ .driver = {
+ .name = "sc7180-lpass-cpu",
+ .of_match_table = of_match_ptr(sc7180_lpass_cpu_device_id),
+ .pm = pm_ptr(&sc7180_lpass_pm_ops),
+ },
+ .probe = asoc_qcom_lpass_cpu_platform_probe,
+ .remove = asoc_qcom_lpass_cpu_platform_remove,
+ .shutdown = asoc_qcom_lpass_cpu_platform_shutdown,
+};
+
+module_platform_driver(sc7180_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("SC7180 LPASS CPU DRIVER");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c
new file mode 100644
index 000000000000..817c824f9179
--- /dev/null
+++ b/sound/soc/qcom/lpass-sc7280.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ *
+ * lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS
+ */
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/pm.h>
+
+#include <dt-bindings/sound/sc7180-lpass.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = {
+ {
+ .id = MI2S_PRIMARY,
+ .name = "Primary MI2S",
+ .playback = {
+ .stream_name = "Primary Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "Primary Capture",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ }, {
+ .id = MI2S_SECONDARY,
+ .name = "Secondary MI2S",
+ .playback = {
+ .stream_name = "Secondary MI2S Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_cpu_dai_ops,
+ }, {
+ .id = LPASS_DP_RX,
+ .name = "Hdmi",
+ .playback = {
+ .stream_name = "DP Playback",
+ .formats = SNDRV_PCM_FMTBIT_S24,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_hdmi_dai_ops,
+ }, {
+ .id = LPASS_CDC_DMA_RX0,
+ .name = "CDC DMA RX",
+ .playback = {
+ .stream_name = "WCD Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_cdc_dma_dai_ops,
+ }, {
+ .id = LPASS_CDC_DMA_TX3,
+ .name = "CDC DMA TX",
+ .capture = {
+ .stream_name = "WCD Capture",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &asoc_qcom_lpass_cdc_dma_dai_ops,
+ }, {
+ .id = LPASS_CDC_DMA_VA_TX0,
+ .name = "CDC DMA VA",
+ .capture = {
+ .stream_name = "DMIC Capture",
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 4,
+ },
+ .ops = &asoc_qcom_lpass_cdc_dma_dai_ops,
+ },
+};
+
+static int sc7280_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+ int direction, unsigned int dai_id)
+{
+ const struct lpass_variant *v = drvdata->variant;
+ int chan = 0;
+
+ switch (dai_id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
+ v->rdma_channels);
+
+ if (chan >= v->rdma_channels)
+ return -EBUSY;
+ } else {
+ chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+ v->wrdma_channel_start +
+ v->wrdma_channels,
+ v->wrdma_channel_start);
+
+ if (chan >= v->wrdma_channel_start + v->wrdma_channels)
+ return -EBUSY;
+ }
+ set_bit(chan, &drvdata->dma_ch_bit_map);
+ break;
+ case LPASS_DP_RX:
+ chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map,
+ v->hdmi_rdma_channels);
+ if (chan >= v->hdmi_rdma_channels)
+ return -EBUSY;
+ set_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ chan = find_first_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
+ v->rxtx_rdma_channels);
+ if (chan >= v->rxtx_rdma_channels)
+ return -EBUSY;
+ break;
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ chan = find_next_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
+ v->rxtx_wrdma_channel_start +
+ v->rxtx_wrdma_channels,
+ v->rxtx_wrdma_channel_start);
+ if (chan >= v->rxtx_wrdma_channel_start + v->rxtx_wrdma_channels)
+ return -EBUSY;
+ set_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ chan = find_next_zero_bit(&drvdata->va_dma_ch_bit_map,
+ v->va_wrdma_channel_start +
+ v->va_wrdma_channels,
+ v->va_wrdma_channel_start);
+ if (chan >= v->va_wrdma_channel_start + v->va_wrdma_channels)
+ return -EBUSY;
+ set_bit(chan, &drvdata->va_dma_ch_bit_map);
+ break;
+ default:
+ break;
+ }
+
+ return chan;
+}
+
+static int sc7280_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
+{
+ switch (dai_id) {
+ case MI2S_PRIMARY ... MI2S_QUINARY:
+ clear_bit(chan, &drvdata->dma_ch_bit_map);
+ break;
+ case LPASS_DP_RX:
+ clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
+ break;
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ clear_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
+ break;
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ clear_bit(chan, &drvdata->va_dma_ch_bit_map);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int sc7280_lpass_init(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+ const struct lpass_variant *variant = drvdata->variant;
+ struct device *dev = &pdev->dev;
+ int ret, i;
+
+ drvdata->clks = devm_kcalloc(dev, variant->num_clks,
+ sizeof(*drvdata->clks), GFP_KERNEL);
+ if (!drvdata->clks)
+ return -ENOMEM;
+
+ drvdata->num_clks = variant->num_clks;
+
+ for (i = 0; i < drvdata->num_clks; i++)
+ drvdata->clks[i].id = variant->clk_name[i];
+
+ ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);
+ if (ret) {
+ dev_err(dev, "Failed to get clocks %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+ if (ret) {
+ dev_err(dev, "sc7280 clk_enable failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sc7280_lpass_exit(struct platform_device *pdev)
+{
+ struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+ clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+ return 0;
+}
+
+static int sc7280_lpass_dev_resume(struct device *dev)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+}
+
+static int sc7280_lpass_dev_suspend(struct device *dev)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+ return 0;
+}
+
+static const struct dev_pm_ops sc7280_lpass_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(sc7280_lpass_dev_suspend, sc7280_lpass_dev_resume)
+};
+
+static const struct lpass_variant sc7280_data = {
+ .i2sctrl_reg_base = 0x1000,
+ .i2sctrl_reg_stride = 0x1000,
+ .i2s_ports = 3,
+ .irq_reg_base = 0x9000,
+ .irq_reg_stride = 0x1000,
+ .irq_ports = 3,
+ .rdma_reg_base = 0xC000,
+ .rdma_reg_stride = 0x1000,
+ .rdma_channels = 5,
+ .rxtx_rdma_reg_base = 0xC000,
+ .rxtx_rdma_reg_stride = 0x1000,
+ .rxtx_rdma_channels = 8,
+ .hdmi_rdma_reg_base = 0x64000,
+ .hdmi_rdma_reg_stride = 0x1000,
+ .hdmi_rdma_channels = 4,
+ .dmactl_audif_start = 1,
+ .wrdma_reg_base = 0x18000,
+ .wrdma_reg_stride = 0x1000,
+ .wrdma_channel_start = 5,
+ .wrdma_channels = 4,
+ .rxtx_irq_reg_base = 0x9000,
+ .rxtx_irq_reg_stride = 0x1000,
+ .rxtx_irq_ports = 3,
+ .rxtx_wrdma_reg_base = 0x18000,
+ .rxtx_wrdma_reg_stride = 0x1000,
+ .rxtx_wrdma_channel_start = 5,
+ .rxtx_wrdma_channels = 6,
+ .va_wrdma_reg_base = 0x18000,
+ .va_wrdma_reg_stride = 0x1000,
+ .va_wrdma_channel_start = 5,
+ .va_wrdma_channels = 3,
+ .va_irq_reg_base = 0x9000,
+ .va_irq_reg_stride = 0x1000,
+ .va_irq_ports = 3,
+
+ .loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
+ .spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
+ .spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
+ .spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
+ .micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
+ .micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
+ .micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
+ .wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
+ .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000),
+
+ .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
+ .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
+ .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000),
+ .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
+ .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000),
+ .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000),
+
+ .wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000),
+ .wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000),
+ .wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000),
+ .wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000),
+ .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000),
+ .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000),
+
+ .rxtx_rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 7, 0x1000),
+ .rxtx_rdma_fifowm = REG_FIELD_ID(0xC000, 1, 11, 7, 0x1000),
+ .rxtx_rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 7, 0x1000),
+ .rxtx_rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 7, 0x1000),
+ .rxtx_rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 7, 0x1000),
+ .rxtx_rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 7, 0x1000),
+
+ .rxtx_rdma_codec_ch = REG_FIELD_ID(0xC050, 0, 7, 7, 0x1000),
+ .rxtx_rdma_codec_intf = REG_FIELD_ID(0xC050, 16, 19, 7, 0x1000),
+ .rxtx_rdma_codec_fs_delay = REG_FIELD_ID(0xC050, 21, 24, 7, 0x1000),
+ .rxtx_rdma_codec_fs_sel = REG_FIELD_ID(0xC050, 25, 27, 7, 0x1000),
+ .rxtx_rdma_codec_pack = REG_FIELD_ID(0xC050, 29, 29, 5, 0x1000),
+ .rxtx_rdma_codec_enable = REG_FIELD_ID(0xC050, 30, 30, 7, 0x1000),
+
+ .rxtx_wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 5, 0x1000),
+ .rxtx_wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 11, 5, 0x1000),
+ .rxtx_wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 5, 0x1000),
+ .rxtx_wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 5, 0x1000),
+ .rxtx_wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 5, 0x1000),
+ .rxtx_wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 5, 0x1000),
+
+ .rxtx_wrdma_codec_ch = REG_FIELD_ID(0x18050, 0, 7, 5, 0x1000),
+ .rxtx_wrdma_codec_intf = REG_FIELD_ID(0x18050, 16, 19, 5, 0x1000),
+ .rxtx_wrdma_codec_fs_delay = REG_FIELD_ID(0x18050, 21, 24, 5, 0x1000),
+ .rxtx_wrdma_codec_fs_sel = REG_FIELD_ID(0x18050, 25, 27, 5, 0x1000),
+ .rxtx_wrdma_codec_pack = REG_FIELD_ID(0x18050, 29, 29, 5, 0x1000),
+ .rxtx_wrdma_codec_enable = REG_FIELD_ID(0x18050, 30, 30, 5, 0x1000),
+
+ .va_wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 5, 0x1000),
+ .va_wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 11, 5, 0x1000),
+ .va_wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 5, 0x1000),
+ .va_wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 5, 0x1000),
+ .va_wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 5, 0x1000),
+ .va_wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 5, 0x1000),
+
+ .va_wrdma_codec_ch = REG_FIELD_ID(0x18050, 0, 7, 5, 0x1000),
+ .va_wrdma_codec_intf = REG_FIELD_ID(0x18050, 16, 19, 5, 0x1000),
+ .va_wrdma_codec_fs_delay = REG_FIELD_ID(0x18050, 21, 24, 5, 0x1000),
+ .va_wrdma_codec_fs_sel = REG_FIELD_ID(0x18050, 25, 27, 5, 0x1000),
+ .va_wrdma_codec_pack = REG_FIELD_ID(0x18050, 29, 29, 5, 0x1000),
+ .va_wrdma_codec_enable = REG_FIELD_ID(0x18050, 30, 30, 5, 0x1000),
+
+ .hdmi_tx_ctl_addr = 0x1000,
+ .hdmi_legacy_addr = 0x1008,
+ .hdmi_vbit_addr = 0x610c0,
+ .hdmi_ch_lsb_addr = 0x61048,
+ .hdmi_ch_msb_addr = 0x6104c,
+ .ch_stride = 0x8,
+ .hdmi_parity_addr = 0x61034,
+ .hdmi_dmactl_addr = 0x61038,
+ .hdmi_dma_stride = 0x4,
+ .hdmi_DP_addr = 0x610c8,
+ .hdmi_sstream_addr = 0x6101c,
+ .hdmi_irq_reg_base = 0x63000,
+ .hdmi_irq_ports = 1,
+
+ .hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
+ .hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
+ .hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
+ .hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
+ .hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
+ .hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
+ .hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
+ .hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
+
+ .sstream_en = REG_FIELD(0x6101c, 0, 0),
+ .dma_sel = REG_FIELD(0x6101c, 1, 2),
+ .auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
+ .layout = REG_FIELD(0x6101c, 4, 4),
+ .layout_sp = REG_FIELD(0x6101c, 5, 8),
+ .set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
+ .dp_audio = REG_FIELD(0x6101c, 11, 11),
+ .dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
+ .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
+
+ .mute = REG_FIELD(0x610c8, 0, 0),
+ .as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
+ .as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
+ .aif_db4 = REG_FIELD(0x610c8, 8, 15),
+ .frequency = REG_FIELD(0x610c8, 16, 21),
+ .mst_index = REG_FIELD(0x610c8, 28, 29),
+ .dptx_index = REG_FIELD(0x610c8, 30, 31),
+
+ .soft_reset = REG_FIELD(0x1000, 31, 31),
+ .force_reset = REG_FIELD(0x1000, 30, 30),
+
+ .use_hw_chs = REG_FIELD(0x61038, 0, 0),
+ .use_hw_usr = REG_FIELD(0x61038, 1, 1),
+ .hw_chs_sel = REG_FIELD(0x61038, 2, 4),
+ .hw_usr_sel = REG_FIELD(0x61038, 5, 6),
+
+ .replace_vbit = REG_FIELD(0x610c0, 0, 0),
+ .vbit_stream = REG_FIELD(0x610c0, 1, 1),
+
+ .legacy_en = REG_FIELD(0x1008, 0, 0),
+ .calc_en = REG_FIELD(0x61034, 0, 0),
+ .lsb_bits = REG_FIELD(0x61048, 0, 31),
+ .msb_bits = REG_FIELD(0x6104c, 0, 31),
+
+ .clk_name = (const char*[]) {
+ "core_cc_sysnoc_mport_core"
+ },
+ .num_clks = 1,
+
+ .dai_driver = sc7280_lpass_cpu_dai_driver,
+ .num_dai = ARRAY_SIZE(sc7280_lpass_cpu_dai_driver),
+ .dai_osr_clk_names = (const char *[]) {
+ "audio_cc_ext_mclk0",
+ "null"
+ },
+ .dai_bit_clk_names = (const char *[]) {
+ "core_cc_ext_if0_ibit",
+ "core_cc_ext_if1_ibit"
+ },
+ .init = sc7280_lpass_init,
+ .exit = sc7280_lpass_exit,
+ .alloc_dma_channel = sc7280_lpass_alloc_dma_channel,
+ .free_dma_channel = sc7280_lpass_free_dma_channel,
+};
+
+static const struct of_device_id sc7280_lpass_cpu_device_id[] = {
+ {.compatible = "qcom,sc7280-lpass-cpu", .data = &sc7280_data},
+ {}
+};
+MODULE_DEVICE_TABLE(of, sc7280_lpass_cpu_device_id);
+
+static struct platform_driver sc7280_lpass_cpu_platform_driver = {
+ .driver = {
+ .name = "sc7280-lpass-cpu",
+ .of_match_table = of_match_ptr(sc7280_lpass_cpu_device_id),
+ .pm = pm_ptr(&sc7280_lpass_pm_ops),
+ },
+ .probe = asoc_qcom_lpass_cpu_platform_probe,
+ .remove = asoc_qcom_lpass_cpu_platform_remove,
+ .shutdown = asoc_qcom_lpass_cpu_platform_shutdown,
+};
+
+module_platform_driver(sc7280_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("SC7280 LPASS CPU DRIVER");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 0b63e2e5bcc9..de3ec6f594c1 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -1,14 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (c) 2010-2011,2013-2015,2020 The Linux Foundation. All rights reserved.
*
* lpass.h - Definitions for the QTi LPASS
*/
@@ -20,10 +12,84 @@
#include <linux/compiler.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <dt-bindings/sound/qcom,lpass.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include "lpass-hdmi.h"
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
+#define LPASS_MAX_PORTS (DISPLAY_PORT_RX_7 + 1)
#define LPASS_MAX_MI2S_PORTS (8)
#define LPASS_MAX_DMA_CHANNELS (8)
+#define LPASS_MAX_HDMI_DMA_CHANNELS (4)
+#define LPASS_MAX_CDC_DMA_CHANNELS (8)
+#define LPASS_MAX_VA_CDC_DMA_CHANNELS (8)
+#define LPASS_CDC_DMA_INTF_ONE_CHANNEL (0x01)
+#define LPASS_CDC_DMA_INTF_TWO_CHANNEL (0x03)
+#define LPASS_CDC_DMA_INTF_FOUR_CHANNEL (0x0F)
+#define LPASS_CDC_DMA_INTF_SIX_CHANNEL (0x3F)
+#define LPASS_CDC_DMA_INTF_EIGHT_CHANNEL (0xFF)
+
+#define LPASS_ACTIVE_PDS (4)
+#define LPASS_PROXY_PDS (8)
+
+#define QCOM_REGMAP_FIELD_ALLOC(d, m, f, mf) \
+ do { \
+ mf = devm_regmap_field_alloc(d, m, f); \
+ if (IS_ERR(mf)) \
+ return -EINVAL; \
+ } while (0)
+
+static inline bool is_cdc_dma_port(int dai_id)
+{
+ switch (dai_id) {
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+ return true;
+ }
+ return false;
+}
+
+static inline bool is_rxtx_cdc_dma_port(int dai_id)
+{
+ switch (dai_id) {
+ case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+ case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+ return true;
+ }
+ return false;
+}
+
+struct lpaif_i2sctl {
+ struct regmap_field *loopback;
+ struct regmap_field *spken;
+ struct regmap_field *spkmode;
+ struct regmap_field *spkmono;
+ struct regmap_field *micen;
+ struct regmap_field *micmode;
+ struct regmap_field *micmono;
+ struct regmap_field *wssrc;
+ struct regmap_field *bitwidth;
+};
+
+
+struct lpaif_dmactl {
+ struct regmap_field *intf;
+ struct regmap_field *bursten;
+ struct regmap_field *wpscnt;
+ struct regmap_field *fifowm;
+ struct regmap_field *enable;
+ struct regmap_field *dyncclk;
+ struct regmap_field *burst8;
+ struct regmap_field *burst16;
+ struct regmap_field *dynburst;
+ struct regmap_field *codec_enable;
+ struct regmap_field *codec_pack;
+ struct regmap_field *codec_intf;
+ struct regmap_field *codec_fs_sel;
+ struct regmap_field *codec_channel;
+ struct regmap_field *codec_fs_delay;
+};
/* Both the CPU DAI and platform drivers will access this data */
struct lpass_data {
@@ -37,62 +103,308 @@ struct lpass_data {
/* MI2S bit clock (derived from system clock by a divider */
struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
+ struct clk *codec_mem0;
+ struct clk *codec_mem1;
+ struct clk *codec_mem2;
+ struct clk *va_mem0;
+
+ /* MI2S SD lines to use for playback/capture */
+ unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
+ unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
+
+ /* The state of MI2S prepare dai_ops was called */
+ bool mi2s_was_prepared[LPASS_MAX_MI2S_PORTS];
+
+ int hdmi_port_enable;
+ int codec_dma_enable;
+
/* low-power audio interface (LPAIF) registers */
void __iomem *lpaif;
+ void __iomem *hdmiif;
+ void __iomem *rxtx_lpaif;
+ void __iomem *va_lpaif;
+
+ u32 rxtx_cdc_dma_lpm_buf;
+ u32 va_cdc_dma_lpm_buf;
/* regmap backed by the low-power audio interface (LPAIF) registers */
struct regmap *lpaif_map;
+ struct regmap *hdmiif_map;
+ struct regmap *rxtx_lpaif_map;
+ struct regmap *va_lpaif_map;
/* interrupts from the low-power audio interface (LPAIF) */
int lpaif_irq;
+ int hdmiif_irq;
+ int rxtxif_irq;
+ int vaif_irq;
/* SOC specific variations in the LPASS IP integration */
- struct lpass_variant *variant;
+ const struct lpass_variant *variant;
/* bit map to keep track of static channel allocations */
- unsigned long rdma_ch_bit_map;
+ unsigned long dma_ch_bit_map;
+ unsigned long hdmi_dma_ch_bit_map;
+ unsigned long rxtx_dma_ch_bit_map;
+ unsigned long va_dma_ch_bit_map;
/* used it for handling interrupt per dma channel */
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
+ struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct snd_pcm_substream *rxtx_substream[LPASS_MAX_CDC_DMA_CHANNELS];
+ struct snd_pcm_substream *va_substream[LPASS_MAX_CDC_DMA_CHANNELS];
+
+ /* SOC specific clock list */
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ /* Regmap fields of I2SCTL & DMACTL registers bitfields */
+ struct lpaif_i2sctl *i2sctl;
+ struct lpaif_dmactl *rd_dmactl;
+ struct lpaif_dmactl *wr_dmactl;
+ struct lpaif_dmactl *hdmi_rd_dmactl;
- /* 8016 specific */
- struct clk *pcnoc_mport_clk;
- struct clk *pcnoc_sway_clk;
+ /* Regmap fields of CODEC DMA CTRL registers */
+ struct lpaif_dmactl *rxtx_rd_dmactl;
+ struct lpaif_dmactl *rxtx_wr_dmactl;
+ struct lpaif_dmactl *va_wr_dmactl;
+
+ /* Regmap fields of HDMI_CTRL registers*/
+ struct regmap_field *hdmitx_legacy_en;
+ struct regmap_field *hdmitx_parity_calc_en;
+ struct regmap_field *hdmitx_ch_msb[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct regmap_field *hdmitx_ch_lsb[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct lpass_hdmi_tx_ctl *tx_ctl;
+ struct lpass_vbit_ctrl *vbit_ctl;
+ struct lpass_hdmitx_dmactl *hdmi_tx_dmactl[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct lpass_dp_metadata_ctl *meta_ctl;
+ struct lpass_sstream_ctl *sstream_ctl;
};
/* Vairant data per each SOC */
struct lpass_variant {
- u32 i2sctrl_reg_base;
- u32 i2sctrl_reg_stride;
- u32 i2s_ports;
u32 irq_reg_base;
u32 irq_reg_stride;
u32 irq_ports;
u32 rdma_reg_base;
u32 rdma_reg_stride;
u32 rdma_channels;
+ u32 hdmi_rdma_reg_base;
+ u32 hdmi_rdma_reg_stride;
+ u32 hdmi_rdma_channels;
+ u32 wrdma_reg_base;
+ u32 wrdma_reg_stride;
+ u32 wrdma_channels;
+ u32 rxtx_irq_reg_base;
+ u32 rxtx_irq_reg_stride;
+ u32 rxtx_irq_ports;
+ u32 rxtx_rdma_reg_base;
+ u32 rxtx_rdma_reg_stride;
+ u32 rxtx_rdma_channels;
+ u32 rxtx_wrdma_reg_base;
+ u32 rxtx_wrdma_reg_stride;
+ u32 rxtx_wrdma_channels;
+ u32 va_irq_reg_base;
+ u32 va_irq_reg_stride;
+ u32 va_irq_ports;
+ u32 va_rdma_reg_base;
+ u32 va_rdma_reg_stride;
+ u32 va_rdma_channels;
+ u32 va_wrdma_reg_base;
+ u32 va_wrdma_reg_stride;
+ u32 va_wrdma_channels;
+ u32 i2sctrl_reg_base;
+ u32 i2sctrl_reg_stride;
+ u32 i2s_ports;
+
+ /* I2SCTL Register fields */
+ struct reg_field loopback;
+ struct reg_field spken;
+ struct reg_field spkmode;
+ struct reg_field spkmono;
+ struct reg_field micen;
+ struct reg_field micmode;
+ struct reg_field micmono;
+ struct reg_field wssrc;
+ struct reg_field bitwidth;
+
+ u32 hdmi_irq_reg_base;
+ u32 hdmi_irq_reg_stride;
+ u32 hdmi_irq_ports;
+
+ /* HDMI specific controls */
+ u32 hdmi_tx_ctl_addr;
+ u32 hdmi_legacy_addr;
+ u32 hdmi_vbit_addr;
+ u32 hdmi_ch_lsb_addr;
+ u32 hdmi_ch_msb_addr;
+ u32 ch_stride;
+ u32 hdmi_parity_addr;
+ u32 hdmi_dmactl_addr;
+ u32 hdmi_dma_stride;
+ u32 hdmi_DP_addr;
+ u32 hdmi_sstream_addr;
+
+ /* HDMI SSTREAM CTRL fields */
+ struct reg_field sstream_en;
+ struct reg_field dma_sel;
+ struct reg_field auto_bbit_en;
+ struct reg_field layout;
+ struct reg_field layout_sp;
+ struct reg_field set_sp_on_en;
+ struct reg_field dp_audio;
+ struct reg_field dp_staffing_en;
+ struct reg_field dp_sp_b_hw_en;
+
+ /* HDMI DP METADATA CTL fields */
+ struct reg_field mute;
+ struct reg_field as_sdp_cc;
+ struct reg_field as_sdp_ct;
+ struct reg_field aif_db4;
+ struct reg_field frequency;
+ struct reg_field mst_index;
+ struct reg_field dptx_index;
+
+ /* HDMI TX CTRL fields */
+ struct reg_field soft_reset;
+ struct reg_field force_reset;
+
+ /* HDMI TX DMA CTRL */
+ struct reg_field use_hw_chs;
+ struct reg_field use_hw_usr;
+ struct reg_field hw_chs_sel;
+ struct reg_field hw_usr_sel;
+
+ /* HDMI VBIT CTRL */
+ struct reg_field replace_vbit;
+ struct reg_field vbit_stream;
+
+ /* HDMI TX LEGACY */
+ struct reg_field legacy_en;
+
+ /* HDMI TX PARITY */
+ struct reg_field calc_en;
+
+ /* HDMI CH LSB */
+ struct reg_field lsb_bits;
+
+ /* HDMI CH MSB */
+ struct reg_field msb_bits;
+
+ struct reg_field hdmi_rdma_bursten;
+ struct reg_field hdmi_rdma_wpscnt;
+ struct reg_field hdmi_rdma_fifowm;
+ struct reg_field hdmi_rdma_enable;
+ struct reg_field hdmi_rdma_dyncclk;
+ struct reg_field hdmi_rdma_burst8;
+ struct reg_field hdmi_rdma_burst16;
+ struct reg_field hdmi_rdma_dynburst;
+
+ /* RD_DMA Register fields */
+ struct reg_field rdma_intf;
+ struct reg_field rdma_bursten;
+ struct reg_field rdma_wpscnt;
+ struct reg_field rdma_fifowm;
+ struct reg_field rdma_enable;
+ struct reg_field rdma_dyncclk;
+
+ /* WR_DMA Register fields */
+ struct reg_field wrdma_intf;
+ struct reg_field wrdma_bursten;
+ struct reg_field wrdma_wpscnt;
+ struct reg_field wrdma_fifowm;
+ struct reg_field wrdma_enable;
+ struct reg_field wrdma_dyncclk;
+
+ /* CDC RXTX RD_DMA */
+ struct reg_field rxtx_rdma_intf;
+ struct reg_field rxtx_rdma_bursten;
+ struct reg_field rxtx_rdma_wpscnt;
+ struct reg_field rxtx_rdma_fifowm;
+ struct reg_field rxtx_rdma_enable;
+ struct reg_field rxtx_rdma_dyncclk;
+ struct reg_field rxtx_rdma_burst8;
+ struct reg_field rxtx_rdma_burst16;
+ struct reg_field rxtx_rdma_dynburst;
+ struct reg_field rxtx_rdma_codec_enable;
+ struct reg_field rxtx_rdma_codec_pack;
+ struct reg_field rxtx_rdma_codec_intf;
+ struct reg_field rxtx_rdma_codec_fs_sel;
+ struct reg_field rxtx_rdma_codec_ch;
+ struct reg_field rxtx_rdma_codec_fs_delay;
+
+ /* CDC RXTX WR_DMA */
+ struct reg_field rxtx_wrdma_intf;
+ struct reg_field rxtx_wrdma_bursten;
+ struct reg_field rxtx_wrdma_wpscnt;
+ struct reg_field rxtx_wrdma_fifowm;
+ struct reg_field rxtx_wrdma_enable;
+ struct reg_field rxtx_wrdma_dyncclk;
+ struct reg_field rxtx_wrdma_burst8;
+ struct reg_field rxtx_wrdma_burst16;
+ struct reg_field rxtx_wrdma_dynburst;
+ struct reg_field rxtx_wrdma_codec_enable;
+ struct reg_field rxtx_wrdma_codec_pack;
+ struct reg_field rxtx_wrdma_codec_intf;
+ struct reg_field rxtx_wrdma_codec_fs_sel;
+ struct reg_field rxtx_wrdma_codec_ch;
+ struct reg_field rxtx_wrdma_codec_fs_delay;
+
+ /* CDC VA WR_DMA */
+ struct reg_field va_wrdma_intf;
+ struct reg_field va_wrdma_bursten;
+ struct reg_field va_wrdma_wpscnt;
+ struct reg_field va_wrdma_fifowm;
+ struct reg_field va_wrdma_enable;
+ struct reg_field va_wrdma_dyncclk;
+ struct reg_field va_wrdma_burst8;
+ struct reg_field va_wrdma_burst16;
+ struct reg_field va_wrdma_dynburst;
+ struct reg_field va_wrdma_codec_enable;
+ struct reg_field va_wrdma_codec_pack;
+ struct reg_field va_wrdma_codec_intf;
+ struct reg_field va_wrdma_codec_fs_sel;
+ struct reg_field va_wrdma_codec_ch;
+ struct reg_field va_wrdma_codec_fs_delay;
/**
* on SOCs like APQ8016 the channel control bits start
* at different offset to ipq806x
**/
- u32 rdmactl_audif_start;
- /* SOC specific intialization like clocks */
+ u32 dmactl_audif_start;
+ u32 wrdma_channel_start;
+ u32 rxtx_wrdma_channel_start;
+ u32 va_wrdma_channel_start;
+
+ /* SOC specific initialization like clocks */
int (*init)(struct platform_device *pdev);
int (*exit)(struct platform_device *pdev);
- int (*alloc_dma_channel)(struct lpass_data *data);
- int (*free_dma_channel)(struct lpass_data *data, int ch);
+ int (*alloc_dma_channel)(struct lpass_data *data, int direction, unsigned int dai_id);
+ int (*free_dma_channel)(struct lpass_data *data, int ch, unsigned int dai_id);
/* SOC specific dais */
struct snd_soc_dai_driver *dai_driver;
int num_dai;
+ const char * const *dai_osr_clk_names;
+ const char * const *dai_bit_clk_names;
+
+ /* SOC specific clocks configuration */
+ const char **clk_name;
+ int num_clks;
+};
+
+struct lpass_pcm_data {
+ int dma_ch;
+ int i2s_port;
};
/* register the platform driver from the CPU DAI driver */
-int asoc_qcom_lpass_platform_register(struct platform_device *);
-int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
+int asoc_qcom_lpass_platform_register(struct platform_device *pdev);
+void asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
+void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev);
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
-int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
+extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2;
+extern const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops;
#endif /* __LPASS_H__ */
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
new file mode 100644
index 000000000000..67267304e7e9
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-q6dsp-common-y := q6dsp-common.o q6dsp-lpass-ports.o q6dsp-lpass-clocks.o
+snd-q6apm-y := q6apm.o audioreach.o topology.o
+
+obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += snd-q6dsp-common.o
+obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
+obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
+obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o
+obj-$(CONFIG_SND_SOC_QDSP6_AFE_CLOCKS) += q6afe-clocks.o
+obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
+obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o
+obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
+obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o
+
+obj-$(CONFIG_SND_SOC_QDSP6_APM) += snd-q6apm.o
+obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o
+obj-$(CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI) += q6apm-lpass-dais.o
+obj-$(CONFIG_SND_SOC_QDSP6_PRM) += q6prm.o
+obj-$(CONFIG_SND_SOC_QDSP6_PRM_LPASS_CLOCKS) += q6prm-clocks.o
+obj-$(CONFIG_SND_SOC_QDSP6_USB) += q6usb.o
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
new file mode 100644
index 000000000000..ded49124581b
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -0,0 +1,1339 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/apr.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <dt-bindings/soc/qcom,gpr.h>
+#include "q6apm.h"
+#include "audioreach.h"
+
+/* SubGraph Config */
+struct apm_sub_graph_data {
+ struct apm_sub_graph_cfg sub_graph_cfg;
+ struct apm_prop_data perf_data;
+ struct apm_sg_prop_id_perf_mode perf;
+ struct apm_prop_data dir_data;
+ struct apm_sg_prop_id_direction dir;
+ struct apm_prop_data sid_data;
+ struct apm_sg_prop_id_scenario_id sid;
+
+} __packed;
+
+#define APM_SUB_GRAPH_CFG_NPROP 3
+
+struct apm_sub_graph_params {
+ struct apm_module_param_data param_data;
+ uint32_t num_sub_graphs;
+ struct apm_sub_graph_data sg_cfg[];
+} __packed;
+
+#define APM_SUB_GRAPH_PSIZE(p, n) ALIGN(struct_size(p, sg_cfg, n), 8)
+
+/* container config */
+struct apm_container_obj {
+ struct apm_container_cfg container_cfg;
+ /* Capability ID list */
+ struct apm_prop_data cap_data;
+ uint32_t num_capability_id;
+ uint32_t capability_id;
+
+ /* Container graph Position */
+ struct apm_prop_data pos_data;
+ struct apm_cont_prop_id_graph_pos pos;
+
+ /* Container Stack size */
+ struct apm_prop_data stack_data;
+ struct apm_cont_prop_id_stack_size stack;
+
+ /* Container proc domain id */
+ struct apm_prop_data domain_data;
+ struct apm_cont_prop_id_domain domain;
+} __packed;
+
+struct apm_container_params {
+ struct apm_module_param_data param_data;
+ uint32_t num_containers;
+ struct apm_container_obj cont_obj[];
+} __packed;
+
+#define APM_CONTAINER_PSIZE(p, n) ALIGN(struct_size(p, cont_obj, n), 8)
+
+/* Module List config */
+struct apm_mod_list_obj {
+ /* Modules list cfg */
+ uint32_t sub_graph_id;
+ uint32_t container_id;
+ uint32_t num_modules;
+ struct apm_module_obj mod_cfg[];
+} __packed;
+
+#define APM_MOD_LIST_OBJ_PSIZE(p, n) struct_size(p, mod_cfg, n)
+
+struct apm_module_list_params {
+ struct apm_module_param_data param_data;
+ uint32_t num_modules_list;
+ /* Module list config array */
+ struct apm_mod_list_obj mod_list_obj[];
+} __packed;
+
+
+/* Module Properties */
+struct apm_mod_prop_obj {
+ u32 instance_id;
+ u32 num_props;
+ struct apm_prop_data prop_data_1;
+ struct apm_module_prop_id_port_info prop_id_port;
+} __packed;
+
+struct apm_prop_list_params {
+ struct apm_module_param_data param_data;
+ u32 num_modules_prop_cfg;
+ struct apm_mod_prop_obj mod_prop_obj[];
+
+} __packed;
+
+#define APM_MOD_PROP_PSIZE(p, n) ALIGN(struct_size(p, mod_prop_obj, n), 8)
+
+/* Module Connections */
+struct apm_mod_conn_list_params {
+ struct apm_module_param_data param_data;
+ u32 num_connections;
+ struct apm_module_conn_obj conn_obj[];
+
+} __packed;
+
+#define APM_MOD_CONN_PSIZE(p, n) ALIGN(struct_size(p, conn_obj, n), 8)
+
+struct apm_graph_open_params {
+ struct apm_cmd_header *cmd_header;
+ struct apm_sub_graph_params *sg_data;
+ struct apm_container_params *cont_data;
+ struct apm_module_list_params *mod_list_data;
+ struct apm_prop_list_params *mod_prop_data;
+ struct apm_mod_conn_list_params *mod_conn_list_data;
+} __packed;
+
+struct apm_pcm_module_media_fmt_cmd {
+ struct apm_module_param_data param_data;
+ struct param_id_pcm_output_format_cfg header;
+ struct payload_pcm_output_format_cfg media_cfg;
+} __packed;
+
+struct apm_rd_shmem_module_config_cmd {
+ struct apm_module_param_data param_data;
+ struct param_id_rd_sh_mem_cfg cfg;
+} __packed;
+
+struct apm_sh_module_media_fmt_cmd {
+ struct media_format header;
+ struct payload_media_fmt_pcm cfg;
+} __packed;
+
+#define APM_SHMEM_FMT_CFG_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_sh_module_media_fmt_cmd) + \
+ ch * sizeof(uint8_t), 8)
+
+/* num of channels as argument */
+#define APM_PCM_MODULE_FMT_CMD_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_pcm_module_media_fmt_cmd) + \
+ ch * sizeof(uint8_t), 8)
+
+#define APM_PCM_OUT_FMT_CFG_PSIZE(p, n) ALIGN(struct_size(p, channel_mapping, n), 4)
+
+struct apm_i2s_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_i2s_intf_cfg cfg;
+} __packed;
+
+#define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8)
+
+struct apm_module_hw_ep_mf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_mf mf;
+} __packed;
+
+#define APM_HW_EP_CFG_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_mf_cfg), 8)
+
+#define APM_MFC_CFG_PSIZE(p, n) ALIGN(struct_size(p, channel_mapping, n), 4)
+
+struct apm_module_frame_size_factor_cfg {
+ struct apm_module_param_data param_data;
+ uint32_t frame_size_factor;
+} __packed;
+
+#define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8)
+
+struct apm_module_hw_ep_power_mode_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_power_mode_cfg power_mode;
+} __packed;
+
+#define APM_HW_EP_PMODE_CFG_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_power_mode_cfg), 8)
+
+struct apm_module_hw_ep_dma_data_align_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_dma_data_align align;
+} __packed;
+
+#define APM_HW_EP_DALIGN_CFG_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_dma_data_align_cfg), 8)
+
+struct apm_gain_module_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_gain_cfg gain_cfg;
+} __packed;
+
+#define APM_GAIN_CFG_PSIZE ALIGN(sizeof(struct apm_gain_module_cfg), 8)
+
+struct apm_codec_dma_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_codec_dma_intf_cfg cfg;
+} __packed;
+
+#define APM_CDMA_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_codec_dma_module_intf_cfg), 8)
+
+struct apm_display_port_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_display_port_intf_cfg cfg;
+} __packed;
+#define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8)
+
+static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int pkt_size = GPR_HDR_SIZE + payload_size;
+
+ if (has_cmd_hdr)
+ pkt_size += APM_CMD_HDR_SIZE;
+
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ pkt = p;
+ pkt->hdr.version = GPR_PKT_VER;
+ pkt->hdr.hdr_size = GPR_PKT_HEADER_WORD_SIZE;
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.dest_port = dest_port;
+ pkt->hdr.src_port = src_port;
+
+ pkt->hdr.dest_domain = GPR_DOMAIN_ID_ADSP;
+ pkt->hdr.src_domain = GPR_DOMAIN_ID_APPS;
+ pkt->hdr.token = token;
+ pkt->hdr.opcode = opcode;
+
+ if (has_cmd_hdr) {
+ struct apm_cmd_header *cmd_header;
+
+ p = p + GPR_HDR_SIZE;
+ cmd_header = p;
+ cmd_header->payload_size = payload_size;
+ }
+
+ return pkt;
+}
+
+void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port, uint32_t dest_port)
+{
+ return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, false);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_pkt);
+
+void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token, uint32_t src_port)
+{
+ return __audioreach_alloc_pkt(pkt_size, opcode, token, src_port, APM_MODULE_INSTANCE_ID,
+ false);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_apm_pkt);
+
+void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port, uint32_t dest_port)
+{
+ return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, true);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_cmd_pkt);
+
+void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token)
+{
+ return __audioreach_alloc_pkt(pkt_size, opcode, token, GPR_APM_MODULE_IID,
+ APM_MODULE_INSTANCE_ID, true);
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt);
+
+void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels)
+{
+ if (num_channels == 1) {
+ ch_map[0] = PCM_CHANNEL_FL;
+ } else if (num_channels == 2) {
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ } else if (num_channels == 4) {
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_LS;
+ ch_map[3] = PCM_CHANNEL_RS;
+ }
+}
+EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping);
+
+static void apm_populate_container_config(struct apm_container_obj *cfg,
+ struct audioreach_container *cont)
+{
+
+ /* Container Config */
+ cfg->container_cfg.container_id = cont->container_id;
+ cfg->container_cfg.num_prop = 4;
+
+ /* Capability list */
+ cfg->cap_data.prop_id = APM_CONTAINER_PROP_ID_CAPABILITY_LIST;
+ cfg->cap_data.prop_size = APM_CONTAINER_PROP_ID_CAPABILITY_SIZE;
+ cfg->num_capability_id = 1;
+ cfg->capability_id = cont->capability_id;
+
+ /* Graph Position */
+ cfg->pos_data.prop_id = APM_CONTAINER_PROP_ID_GRAPH_POS;
+ cfg->pos_data.prop_size = sizeof(struct apm_cont_prop_id_graph_pos);
+ cfg->pos.graph_pos = cont->graph_pos;
+
+ /* Stack size */
+ cfg->stack_data.prop_id = APM_CONTAINER_PROP_ID_STACK_SIZE;
+ cfg->stack_data.prop_size = sizeof(struct apm_cont_prop_id_stack_size);
+ cfg->stack.stack_size = cont->stack_size;
+
+ /* Proc domain */
+ cfg->domain_data.prop_id = APM_CONTAINER_PROP_ID_PROC_DOMAIN;
+ cfg->domain_data.prop_size = sizeof(struct apm_cont_prop_id_domain);
+ cfg->domain.proc_domain = cont->proc_domain;
+}
+
+static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
+ struct audioreach_sub_graph *sg)
+{
+ cfg->sub_graph_cfg.sub_graph_id = sg->sub_graph_id;
+ cfg->sub_graph_cfg.num_sub_graph_prop = APM_SUB_GRAPH_CFG_NPROP;
+
+ /* Perf Mode */
+ cfg->perf_data.prop_id = APM_SUB_GRAPH_PROP_ID_PERF_MODE;
+ cfg->perf_data.prop_size = APM_SG_PROP_ID_PERF_MODE_SIZE;
+ cfg->perf.perf_mode = sg->perf_mode;
+
+ /* Direction */
+ cfg->dir_data.prop_id = APM_SUB_GRAPH_PROP_ID_DIRECTION;
+ cfg->dir_data.prop_size = APM_SG_PROP_ID_DIR_SIZE;
+ cfg->dir.direction = sg->direction;
+
+ /* Scenario ID */
+ cfg->sid_data.prop_id = APM_SUB_GRAPH_PROP_ID_SCENARIO_ID;
+ cfg->sid_data.prop_size = APM_SG_PROP_ID_SID_SIZE;
+ cfg->sid.scenario_id = sg->scenario_id;
+}
+
+static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
+ struct audioreach_module *module)
+{
+
+ obj->instance_id = module->instance_id;
+ obj->num_props = 1;
+ obj->prop_data_1.prop_id = APM_MODULE_PROP_ID_PORT_INFO;
+ obj->prop_data_1.prop_size = APM_MODULE_PROP_ID_PORT_INFO_SZ;
+ obj->prop_id_port.max_ip_port = module->max_ip_port;
+ obj->prop_id_port.max_op_port = module->max_op_port;
+}
+
+static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
+ struct audioreach_container *container,
+ int sub_graph_id)
+{
+ struct audioreach_module *module;
+ int i;
+
+ obj->sub_graph_id = sub_graph_id;
+ obj->container_id = container->container_id;
+ obj->num_modules = container->num_modules;
+ i = 0;
+ list_for_each_entry(module, &container->modules_list, node) {
+ obj->mod_cfg[i].module_id = module->module_id;
+ obj->mod_cfg[i].instance_id = module->instance_id;
+ i++;
+ }
+}
+
+static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph_info *info,
+ struct apm_graph_open_params *open,
+ struct list_head *sg_list,
+ int num_sub_graphs)
+{
+ struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data;
+ struct apm_module_list_params *ml_data = open->mod_list_data;
+ struct apm_prop_list_params *mp_data = open->mod_prop_data;
+ struct apm_container_params *c_data = open->cont_data;
+ struct apm_sub_graph_params *sg_data = open->sg_data;
+ int ncontainer = 0, nmodule = 0, nconn = 0;
+ struct apm_mod_prop_obj *module_prop_obj;
+ struct audioreach_container *container;
+ struct apm_module_conn_obj *conn_obj;
+ struct audioreach_module *module;
+ struct audioreach_sub_graph *sg;
+ struct apm_container_obj *cobj;
+ struct apm_mod_list_obj *mlobj;
+ int i = 0;
+
+ mlobj = &ml_data->mod_list_obj[0];
+
+
+ if (info->dst_mod_inst_id && info->src_mod_inst_id) {
+ conn_obj = &mc_data->conn_obj[nconn];
+ conn_obj->src_mod_inst_id = info->src_mod_inst_id;
+ conn_obj->src_mod_op_port_id = info->src_mod_op_port_id;
+ conn_obj->dst_mod_inst_id = info->dst_mod_inst_id;
+ conn_obj->dst_mod_ip_port_id = info->dst_mod_ip_port_id;
+ nconn++;
+ }
+
+ list_for_each_entry(sg, sg_list, node) {
+ struct apm_sub_graph_data *sg_cfg = &sg_data->sg_cfg[i++];
+
+ apm_populate_sub_graph_config(sg_cfg, sg);
+
+ list_for_each_entry(container, &sg->container_list, node) {
+ cobj = &c_data->cont_obj[ncontainer];
+
+ apm_populate_container_config(cobj, container);
+ apm_populate_module_list_obj(mlobj, container, sg->sub_graph_id);
+
+ list_for_each_entry(module, &container->modules_list, node) {
+ int pn;
+
+ module_prop_obj = &mp_data->mod_prop_obj[nmodule++];
+ apm_populate_module_prop_obj(module_prop_obj, module);
+
+ if (!module->max_op_port)
+ continue;
+
+ for (pn = 0; pn < module->max_op_port; pn++) {
+ if (module->dst_mod_inst_id[pn]) {
+ conn_obj = &mc_data->conn_obj[nconn];
+ conn_obj->src_mod_inst_id = module->instance_id;
+ conn_obj->src_mod_op_port_id =
+ module->src_mod_op_port_id[pn];
+ conn_obj->dst_mod_inst_id =
+ module->dst_mod_inst_id[pn];
+ conn_obj->dst_mod_ip_port_id =
+ module->dst_mod_ip_port_id[pn];
+ nconn++;
+ }
+ }
+ }
+ mlobj = (void *) mlobj + APM_MOD_LIST_OBJ_PSIZE(mlobj,
+ container->num_modules);
+
+ ncontainer++;
+ }
+ }
+}
+
+void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info *info)
+{
+ int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz;
+ struct apm_module_param_data *param_data;
+ struct apm_container_params *cont_params;
+ struct audioreach_container *container;
+ struct apm_sub_graph_params *sg_params;
+ struct apm_mod_conn_list_params *mcon;
+ struct apm_graph_open_params params;
+ struct apm_prop_list_params *mprop;
+ struct audioreach_module *module;
+ struct audioreach_sub_graph *sgs;
+ struct apm_mod_list_obj *mlobj;
+ struct list_head *sg_list;
+ int num_connections = 0;
+ int num_containers = 0;
+ int num_sub_graphs = 0;
+ int num_modules = 0;
+ int num_modules_list;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ sg_list = &info->sg_list;
+ ml_sz = 0;
+
+ /* add FE-BE connections */
+ if (info->dst_mod_inst_id && info->src_mod_inst_id)
+ num_connections++;
+
+ list_for_each_entry(sgs, sg_list, node) {
+ num_sub_graphs++;
+ list_for_each_entry(container, &sgs->container_list, node) {
+ num_containers++;
+ num_modules += container->num_modules;
+ ml_sz = ml_sz + sizeof(struct apm_module_list_params) +
+ APM_MOD_LIST_OBJ_PSIZE(mlobj, container->num_modules);
+
+ list_for_each_entry(module, &container->modules_list, node) {
+ num_connections += module->num_connections;
+ }
+ }
+ }
+
+ num_modules_list = num_containers;
+ sg_sz = APM_SUB_GRAPH_PSIZE(sg_params, num_sub_graphs);
+ cont_sz = APM_CONTAINER_PSIZE(cont_params, num_containers);
+
+ ml_sz = ALIGN(ml_sz, 8);
+
+ mp_sz = APM_MOD_PROP_PSIZE(mprop, num_modules);
+ mc_sz = APM_MOD_CONN_PSIZE(mcon, num_connections);
+
+ payload_size = sg_sz + cont_sz + ml_sz + mp_sz + mc_sz;
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_GRAPH_OPEN, 0);
+ if (IS_ERR(pkt))
+ return pkt;
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ /* SubGraph */
+ params.sg_data = p;
+ param_data = &params.sg_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_SUB_GRAPH_CONFIG;
+ param_data->param_size = sg_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.sg_data->num_sub_graphs = num_sub_graphs;
+ p += sg_sz;
+
+ /* Container */
+ params.cont_data = p;
+ param_data = &params.cont_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_CONTAINER_CONFIG;
+ param_data->param_size = cont_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.cont_data->num_containers = num_containers;
+ p += cont_sz;
+
+ /* Module List*/
+ params.mod_list_data = p;
+ param_data = &params.mod_list_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_MODULE_LIST;
+ param_data->param_size = ml_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.mod_list_data->num_modules_list = num_modules_list;
+ p += ml_sz;
+
+ /* Module Properties */
+ params.mod_prop_data = p;
+ param_data = &params.mod_prop_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_MODULE_PROP;
+ param_data->param_size = mp_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.mod_prop_data->num_modules_prop_cfg = num_modules;
+ p += mp_sz;
+
+ /* Module Connections */
+ params.mod_conn_list_data = p;
+ param_data = &params.mod_conn_list_data->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_MODULE_CONN;
+ param_data->param_size = mc_sz - APM_MODULE_PARAM_DATA_SIZE;
+ params.mod_conn_list_data->num_connections = num_connections;
+ p += mc_sz;
+
+ audioreach_populate_graph(apm, info, &params, sg_list, num_sub_graphs);
+
+ return pkt;
+}
+EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt);
+
+int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
+ struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock,
+ gpr_port_t *port, wait_queue_head_t *cmd_wait,
+ struct gpr_pkt *pkt, uint32_t rsp_opcode)
+{
+
+ struct gpr_hdr *hdr = &pkt->hdr;
+ int rc;
+
+ mutex_lock(cmd_lock);
+ result->opcode = 0;
+ result->status = 0;
+
+ if (port)
+ rc = gpr_send_port_pkt(port, pkt);
+ else if (gdev)
+ rc = gpr_send_pkt(gdev, pkt);
+ else
+ rc = -EINVAL;
+
+ if (rc < 0)
+ goto err;
+
+ if (rsp_opcode)
+ rc = wait_event_timeout(*cmd_wait, (result->opcode == hdr->opcode) ||
+ (result->opcode == rsp_opcode), 5 * HZ);
+ else
+ rc = wait_event_timeout(*cmd_wait, (result->opcode == hdr->opcode), 5 * HZ);
+
+ if (!rc) {
+ dev_err(dev, "CMD timeout for [%x] opcode\n", hdr->opcode);
+ rc = -ETIMEDOUT;
+ } else if (result->status > 0) {
+ dev_err(dev, "DSP returned error[%x] %x\n", hdr->opcode, result->status);
+ rc = -EINVAL;
+ } else {
+ /* DSP successfully finished the command */
+ rc = 0;
+ }
+
+err:
+ mutex_unlock(cmd_lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_send_cmd_sync);
+
+int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
+ uint32_t rsp_opcode)
+{
+
+ return audioreach_send_cmd_sync(graph->dev, NULL, &graph->result, &graph->lock,
+ graph->port, &graph->cmd_wait, pkt, rsp_opcode);
+}
+EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync);
+
+static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_display_port_module_intf_cfg *intf_cfg;
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz = APM_DP_INTF_CFG_PSIZE;
+ int ep_sz = APM_HW_EP_CFG_PSIZE;
+ int fs_sz = APM_FS_CFG_PSIZE;
+ int size = ic_sz + ep_sz + fs_sz;
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+ p += ep_sz;
+
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+ p += fs_sz;
+
+ intf_cfg = p;
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_DISPLAY_PORT_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.channel_allocation = cfg->channel_allocation;
+ intf_cfg->cfg.mst_idx = 0;
+ intf_cfg->cfg.dptx_idx = cfg->dp_idx;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+/* LPASS Codec DMA port Module Media Format Setup */
+static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_codec_dma_module_intf_cfg *intf_cfg;
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_hw_ep_power_mode_cfg *pm_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz = APM_CDMA_INTF_CFG_PSIZE;
+ int ep_sz = APM_HW_EP_CFG_PSIZE;
+ int fs_sz = APM_FS_CFG_PSIZE;
+ int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE;
+ int size = ic_sz + ep_sz + fs_sz + pm_sz;
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+ p += ep_sz;
+
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+ p += fs_sz;
+
+ intf_cfg = p;
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_CODEC_DMA_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.lpaif_type = module->hw_interface_type;
+ intf_cfg->cfg.intf_index = module->hw_interface_idx;
+ intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1;
+ p += ic_sz;
+
+ pm_cfg = p;
+ param_data = &pm_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_POWER_MODE_CFG;
+ param_data->param_size = pm_sz - APM_MODULE_PARAM_DATA_SIZE;
+ pm_cfg->power_mode.power_mode = 0;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ uint32_t param_id, uint32_t param_val)
+{
+ struct apm_module_param_data *param_data;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+ uint32_t *param;
+ int payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE;
+ void *p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = param_id;
+ param_data->param_size = sizeof(uint32_t);
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ param = p;
+ *param = param_val;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+EXPORT_SYMBOL_GPL(audioreach_send_u32_param);
+
+static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
+ struct audioreach_module *module, bool enable)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable);
+}
+
+static int audioreach_sal_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width);
+}
+
+static int audioreach_module_enable(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ bool enable)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable);
+}
+
+static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY,
+ EARLY_EOS_DELAY_MS);
+}
+
+static int audioreach_set_module_config(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ int size = le32_to_cpu(module->data->size);
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ memcpy(p, module->data->data, size);
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_module_param_data *param_data;
+ struct param_id_mfc_media_format *media_format;
+ uint32_t num_channels = cfg->num_channels;
+ int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) +
+ APM_MODULE_PARAM_DATA_SIZE;
+ int i;
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+ param_data->param_size = APM_MFC_CFG_PSIZE(media_format, num_channels);
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ media_format = p;
+
+ media_format->sample_rate = cfg->sample_rate;
+ media_format->bit_width = cfg->bit_width;
+ media_format->num_channels = cfg->num_channels;
+ for (i = 0; i < num_channels; i++)
+ media_format->channel_mapping[i] = cfg->channel_map[i];
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
+ void *p, struct audioreach_module_config *mcfg)
+{
+ struct payload_media_fmt_aac_t *aac_cfg;
+ struct payload_media_fmt_pcm *mp3_cfg;
+ struct payload_media_fmt_flac_t *flac_cfg;
+ struct payload_media_fmt_opus_t *opus_cfg;
+
+ switch (mcfg->fmt) {
+ case SND_AUDIOCODEC_MP3:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_MP3;
+ media_fmt_hdr->payload_size = 0;
+ p = p + sizeof(*media_fmt_hdr);
+ mp3_cfg = p;
+ mp3_cfg->sample_rate = mcfg->sample_rate;
+ mp3_cfg->bit_width = mcfg->bit_width;
+ mp3_cfg->alignment = PCM_LSB_ALIGNED;
+ mp3_cfg->bits_per_sample = mcfg->bit_width;
+ mp3_cfg->q_factor = mcfg->bit_width - 1;
+ mp3_cfg->endianness = PCM_LITTLE_ENDIAN;
+ mp3_cfg->num_channels = mcfg->num_channels;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_AAC;
+ media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_aac_t);
+ p = p + sizeof(*media_fmt_hdr);
+ aac_cfg = p;
+ aac_cfg->aac_fmt_flag = 0;
+ aac_cfg->audio_obj_type = 5;
+ aac_cfg->num_channels = mcfg->num_channels;
+ aac_cfg->total_size_of_PCE_bits = 0;
+ aac_cfg->sample_rate = mcfg->sample_rate;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_FLAC;
+ media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_flac_t);
+ p = p + sizeof(*media_fmt_hdr);
+ flac_cfg = p;
+ flac_cfg->sample_size = mcfg->codec.options.flac_d.sample_size;
+ flac_cfg->num_channels = mcfg->num_channels;
+ flac_cfg->min_blk_size = mcfg->codec.options.flac_d.min_blk_size;
+ flac_cfg->max_blk_size = mcfg->codec.options.flac_d.max_blk_size;
+ flac_cfg->sample_rate = mcfg->sample_rate;
+ flac_cfg->min_frame_size = mcfg->codec.options.flac_d.min_frame_size;
+ flac_cfg->max_frame_size = mcfg->codec.options.flac_d.max_frame_size;
+ break;
+ case SND_AUDIOCODEC_OPUS_RAW:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_OPUS;
+ media_fmt_hdr->payload_size = sizeof(*opus_cfg);
+ p = p + sizeof(*media_fmt_hdr);
+ opus_cfg = p;
+ /* raw opus packets prepended with 4 bytes of length */
+ opus_cfg->bitstream_format = 1;
+ /*
+ * payload_type:
+ * 0 -- read metadata from opus stream;
+ * 1 -- metadata is provided by filling in the struct here.
+ */
+ opus_cfg->payload_type = 1;
+ opus_cfg->version = mcfg->codec.options.opus_d.version;
+ opus_cfg->num_channels = mcfg->codec.options.opus_d.num_channels;
+ opus_cfg->pre_skip = mcfg->codec.options.opus_d.pre_skip;
+ opus_cfg->sample_rate = mcfg->codec.options.opus_d.sample_rate;
+ opus_cfg->output_gain = mcfg->codec.options.opus_d.output_gain;
+ opus_cfg->mapping_family = mcfg->codec.options.opus_d.mapping_family;
+ opus_cfg->stream_count = mcfg->codec.options.opus_d.chan_map.stream_count;
+ opus_cfg->coupled_count = mcfg->codec.options.opus_d.chan_map.coupled_count;
+ memcpy(opus_cfg->channel_mapping, mcfg->codec.options.opus_d.chan_map.channel_map,
+ sizeof(opus_cfg->channel_mapping));
+ opus_cfg->reserved[0] = opus_cfg->reserved[1] = opus_cfg->reserved[2] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg)
+{
+ struct media_format *header;
+ int rc;
+ void *p;
+ int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ int payload_size = sizeof(struct apm_sh_module_media_fmt_cmd);
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(payload_size,
+ DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT,
+ 0, graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return -ENOMEM;
+
+
+ p = (void *)pkt + GPR_HDR_SIZE;
+ header = p;
+ rc = audioreach_set_compr_media_format(header, p, mcfg);
+ if (rc)
+ return rc;
+
+ return gpr_send_port_pkt(graph->port, pkt);
+}
+EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
+
+static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_i2s_module_intf_cfg *intf_cfg;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz = APM_I2S_INTF_CFG_PSIZE;
+ int ep_sz = APM_HW_EP_CFG_PSIZE;
+ int fs_sz = APM_FS_CFG_PSIZE;
+ int size = ic_sz + ep_sz + fs_sz;
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ intf_cfg = p;
+
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_I2S_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.lpaif_type = module->hw_interface_type;
+ intf_cfg->cfg.intf_idx = module->hw_interface_idx;
+ intf_cfg->cfg.sd_line_idx = module->sd_line_idx;
+
+ switch (cfg->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ intf_cfg->cfg.ws_src = CONFIG_I2S_WS_SRC_INTERNAL;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ /* CPU is slave */
+ intf_cfg->cfg.ws_src = CONFIG_I2S_WS_SRC_EXTERNAL;
+ break;
+ default:
+ break;
+ }
+
+ p += ic_sz;
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+
+ p += ep_sz;
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module)
+{
+ struct apm_module_param_data *param_data;
+ struct data_logging_config *cfg;
+ int size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_DATA_LOGGING_CONFIG;
+ param_data->param_size = size - APM_MODULE_PARAM_DATA_SIZE;
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ cfg = p;
+ cfg->log_code = module->log_code;
+ cfg->log_tap_point_id = module->log_tap_point_id;
+ cfg->mode = module->log_mode;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *mcfg)
+{
+ struct payload_pcm_output_format_cfg *media_cfg;
+ uint32_t num_channels = mcfg->num_channels;
+ struct apm_pcm_module_media_fmt_cmd *cfg;
+ struct apm_module_param_data *param_data;
+ int payload_size;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+
+ if (num_channels > 4) {
+ dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
+ return -EINVAL;
+ }
+
+ payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels);
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ cfg = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_PCM_OUTPUT_FORMAT_CFG;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->header.data_format = DATA_FORMAT_FIXED_POINT;
+ cfg->header.fmt_id = MEDIA_FMT_ID_PCM;
+ cfg->header.payload_size = APM_PCM_OUT_FMT_CFG_PSIZE(media_cfg, num_channels);
+
+ media_cfg = &cfg->media_cfg;
+ media_cfg->alignment = PCM_LSB_ALIGNED;
+ media_cfg->bit_width = mcfg->bit_width;
+ media_cfg->endianness = PCM_LITTLE_ENDIAN;
+ media_cfg->interleaved = module->interleave_type;
+ media_cfg->num_channels = mcfg->num_channels;
+ media_cfg->q_factor = mcfg->bit_width - 1;
+ media_cfg->bits_per_sample = mcfg->bit_width;
+ memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels);
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *mcfg)
+{
+ uint32_t num_channels = mcfg->num_channels;
+ struct apm_module_param_data *param_data;
+ struct payload_media_fmt_pcm *cfg;
+ struct media_format *header;
+ int rc, payload_size;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+ void *p;
+
+ if (num_channels > 4) {
+ dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
+ return -EINVAL;
+ }
+
+ payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels) + APM_MODULE_PARAM_DATA_SIZE;
+
+ pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0,
+ graph->port->id, module->instance_id);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_MEDIA_FORMAT;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+
+ header = p;
+ if (mcfg->fmt == SND_AUDIOCODEC_PCM) {
+ header->data_format = DATA_FORMAT_FIXED_POINT;
+ header->fmt_id = MEDIA_FMT_ID_PCM;
+ header->payload_size = payload_size - sizeof(*header);
+
+ p = p + sizeof(*header);
+ cfg = p;
+ cfg->sample_rate = mcfg->sample_rate;
+ cfg->bit_width = mcfg->bit_width;
+ cfg->alignment = PCM_LSB_ALIGNED;
+ cfg->bits_per_sample = mcfg->bit_width;
+ cfg->q_factor = mcfg->bit_width - 1;
+ cfg->endianness = PCM_LITTLE_ENDIAN;
+ cfg->num_channels = mcfg->num_channels;
+ memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels);
+ } else {
+ rc = audioreach_set_compr_media_format(header, p, mcfg);
+ if (rc)
+ return rc;
+ }
+
+ return audioreach_graph_send_cmd_sync(graph, pkt, 0);
+}
+
+int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol)
+{
+ struct param_id_vol_ctrl_master_gain *cfg;
+ struct apm_module_param_data *param_data;
+ int size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
+ void *p;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_VOL_CTRL_MASTER_GAIN;
+ param_data->param_size = size - APM_MODULE_PARAM_DATA_SIZE;
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ cfg = p;
+ cfg->master_gain = vol;
+ return q6apm_send_cmd_sync(apm, pkt, 0);
+}
+EXPORT_SYMBOL_GPL(audioreach_gain_set_vol_ctrl);
+
+static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_module *module)
+{
+ struct apm_module_param_data *param_data;
+ struct apm_gain_module_cfg *cfg;
+ int size = APM_GAIN_CFG_PSIZE;
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ cfg = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = APM_PARAM_ID_GAIN;
+ param_data->param_size = size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->gain_cfg.gain = module->gain;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
+int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ int rc;
+
+ switch (module->module_id) {
+ case MODULE_ID_DATA_LOGGING:
+ rc = audioreach_module_enable(graph, module, true);
+ if (!rc)
+ rc = audioreach_logging_set_media_format(graph, module);
+ break;
+ case MODULE_ID_PCM_DEC:
+ case MODULE_ID_PCM_ENC:
+ case MODULE_ID_PCM_CNV:
+ case MODULE_ID_PLACEHOLDER_DECODER:
+ case MODULE_ID_PLACEHOLDER_ENCODER:
+ rc = audioreach_pcm_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_DISPLAY_PORT_SINK:
+ rc = audioreach_display_port_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_SMECNS_V2:
+ rc = audioreach_set_module_config(graph, module, cfg);
+ break;
+ case MODULE_ID_I2S_SOURCE:
+ case MODULE_ID_I2S_SINK:
+ rc = audioreach_i2s_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_WR_SHARED_MEM_EP:
+ rc = audioreach_shmem_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_GAIN:
+ rc = audioreach_gain_set(graph, module);
+ break;
+ case MODULE_ID_CODEC_DMA_SINK:
+ case MODULE_ID_CODEC_DMA_SOURCE:
+ rc = audioreach_codec_dma_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_SAL:
+ rc = audioreach_sal_set_media_format(graph, module, cfg);
+ if (!rc)
+ rc = audioreach_sal_limiter_enable(graph, module, true);
+ break;
+ case MODULE_ID_MFC:
+ rc = audioreach_mfc_set_media_format(graph, module, cfg);
+ break;
+ case MODULE_ID_GAPLESS:
+ rc = audioreach_gapless_set_media_format(graph, module, cfg);
+ break;
+ default:
+ rc = 0;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_set_media_format);
+
+void audioreach_graph_free_buf(struct q6apm_graph *graph)
+{
+ struct audioreach_graph_data *port;
+
+ mutex_lock(&graph->lock);
+ port = &graph->rx_data;
+ port->num_periods = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+
+ port = &graph->tx_data;
+ port->num_periods = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ mutex_unlock(&graph->lock);
+}
+EXPORT_SYMBOL_GPL(audioreach_graph_free_buf);
+
+int audioreach_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, size_t period_sz,
+ unsigned int periods, bool is_contiguous)
+{
+ struct apm_shared_map_region_payload *mregions;
+ struct apm_cmd_shared_mem_map_regions *cmd;
+ uint32_t num_regions, buf_sz, payload_size;
+ struct audioreach_graph_data *data;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+ void *p;
+ int i;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ if (is_contiguous) {
+ num_regions = 1;
+ buf_sz = period_sz * periods;
+ } else {
+ buf_sz = period_sz;
+ num_regions = periods;
+ }
+
+ /* DSP expects size should be aligned to 4K */
+ buf_sz = ALIGN(buf_sz, 4096);
+
+ payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions);
+
+ pkt = audioreach_alloc_apm_pkt(payload_size, APM_CMD_SHARED_MEM_MAP_REGIONS, dir,
+ graph->port->id);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE;
+ cmd = p;
+ cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL;
+ cmd->num_regions = num_regions;
+
+ cmd->property_flag = 0x0;
+
+ mregions = p + sizeof(*cmd);
+
+ mutex_lock(&graph->lock);
+
+ for (i = 0; i < num_regions; i++) {
+ struct audio_buffer *ab;
+
+ ab = &data->buf[i];
+ mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+ mregions->shm_addr_msw = upper_32_bits(ab->phys);
+ mregions->mem_size_bytes = buf_sz;
+ ++mregions;
+ }
+ mutex_unlock(&graph->lock);
+
+ return audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS);
+}
+EXPORT_SYMBOL_GPL(audioreach_map_memory_regions);
+
+int audioreach_shared_memory_send_eos(struct q6apm_graph *graph)
+{
+ struct data_cmd_wr_sh_mem_ep_eos *eos;
+ int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(sizeof(*eos),
+ DATA_CMD_WR_SH_MEM_EP_EOS, 0, graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ eos = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ eos->policy = WR_SH_MEM_EP_EOS_POLICY_LAST;
+
+ return gpr_send_port_pkt(graph->port, pkt);
+}
+EXPORT_SYMBOL_GPL(audioreach_shared_memory_send_eos);
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
new file mode 100644
index 000000000000..d1b60b36468a
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -0,0 +1,821 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __AUDIOREACH_H__
+#define __AUDIOREACH_H__
+#include <linux/types.h>
+#include <linux/soc/qcom/apr.h>
+#include <uapi/sound/snd_ar_tokens.h>
+#include <sound/soc.h>
+struct q6apm;
+struct q6apm_graph;
+
+/* Module IDs */
+#define MODULE_ID_WR_SHARED_MEM_EP 0x07001000
+#define MODULE_ID_RD_SHARED_MEM_EP 0x07001001
+#define MODULE_ID_GAIN 0x07001002
+#define MODULE_ID_PCM_CNV 0x07001003
+#define MODULE_ID_PCM_ENC 0x07001004
+#define MODULE_ID_PCM_DEC 0x07001005
+#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008
+#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
+#define MODULE_ID_I2S_SINK 0x0700100A
+#define MODULE_ID_I2S_SOURCE 0x0700100B
+#define MODULE_ID_SAL 0x07001010
+#define MODULE_ID_MFC 0x07001015
+#define MODULE_ID_DATA_LOGGING 0x0700101A
+#define MODULE_ID_AAC_DEC 0x0700101F
+#define MODULE_ID_CODEC_DMA_SINK 0x07001023
+#define MODULE_ID_CODEC_DMA_SOURCE 0x07001024
+#define MODULE_ID_FLAC_DEC 0x0700102F
+#define MODULE_ID_SMECNS_V2 0x07001031
+#define MODULE_ID_MP3_DECODE 0x0700103B
+#define MODULE_ID_GAPLESS 0x0700104D
+#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069
+#define MODULE_ID_OPUS_DEC 0x07001174
+
+#define APM_CMD_GET_SPF_STATE 0x01001021
+#define APM_CMD_RSP_GET_SPF_STATE 0x02001007
+
+#define APM_MODULE_INSTANCE_ID 0x00000001
+#define PRM_MODULE_INSTANCE_ID 0x00000002
+#define AMDB_MODULE_INSTANCE_ID 0x00000003
+#define VCPM_MODULE_INSTANCE_ID 0x00000004
+#define AR_MODULE_INSTANCE_ID_START 0x00006000
+#define AR_MODULE_INSTANCE_ID_END 0x00007000
+#define AR_MODULE_DYNAMIC_INSTANCE_ID_START 0x00007000
+#define AR_MODULE_DYNAMIC_INSTANCE_ID_END 0x00008000
+#define AR_CONT_INSTANCE_ID_START 0x00005000
+#define AR_CONT_INSTANCE_ID_END 0x00006000
+#define AR_SG_INSTANCE_ID_START 0x00004000
+
+#define APM_CMD_GRAPH_OPEN 0x01001000
+#define APM_CMD_GRAPH_PREPARE 0x01001001
+#define APM_CMD_GRAPH_START 0x01001002
+#define APM_CMD_GRAPH_STOP 0x01001003
+#define APM_CMD_GRAPH_CLOSE 0x01001004
+#define APM_CMD_GRAPH_FLUSH 0x01001005
+#define APM_CMD_SET_CFG 0x01001006
+#define APM_CMD_GET_CFG 0x01001007
+#define APM_CMD_SHARED_MEM_MAP_REGIONS 0x0100100C
+#define APM_CMD_SHARED_MEM_UNMAP_REGIONS 0x0100100D
+#define APM_CMD_RSP_SHARED_MEM_MAP_REGIONS 0x02001001
+#define APM_CMD_RSP_GET_CFG 0x02001000
+#define APM_CMD_CLOSE_ALL 0x01001013
+#define APM_CMD_REGISTER_SHARED_CFG 0x0100100A
+
+#define APM_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+struct apm_cmd_shared_mem_map_regions {
+ uint16_t mem_pool_id;
+ uint16_t num_regions;
+ uint32_t property_flag;
+} __packed;
+
+struct apm_shared_map_region_payload {
+ uint32_t shm_addr_lsw;
+ uint32_t shm_addr_msw;
+ uint32_t mem_size_bytes;
+} __packed;
+
+struct apm_cmd_shared_mem_unmap_regions {
+ uint32_t mem_map_handle;
+} __packed;
+
+struct apm_cmd_rsp_shared_mem_map_regions {
+ uint32_t mem_map_handle;
+} __packed;
+
+/* APM module */
+#define APM_PARAM_ID_SUB_GRAPH_LIST 0x08001005
+
+#define APM_PARAM_ID_MODULE_LIST 0x08001002
+
+struct apm_param_id_modules_list {
+ uint32_t num_modules_list;
+} __packed;
+
+#define APM_PARAM_ID_MODULE_PROP 0x08001003
+
+struct apm_param_id_module_prop {
+ uint32_t num_modules_prop_cfg;
+} __packed;
+
+struct apm_module_prop_cfg {
+ uint32_t instance_id;
+ uint32_t num_props;
+} __packed;
+
+#define APM_PARAM_ID_MODULE_CONN 0x08001004
+
+struct apm_param_id_module_conn {
+ uint32_t num_connections;
+} __packed;
+
+struct apm_module_conn_obj {
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id;
+ uint32_t dst_mod_inst_id;
+ uint32_t dst_mod_ip_port_id;
+} __packed;
+
+#define APM_PARAM_ID_GAIN 0x08001006
+
+struct param_id_gain_cfg {
+ uint16_t gain;
+ uint16_t reserved;
+} __packed;
+
+#define PARAM_ID_PCM_OUTPUT_FORMAT_CFG 0x08001008
+
+struct param_id_pcm_output_format_cfg {
+ uint32_t data_format;
+ uint32_t fmt_id;
+ uint32_t payload_size;
+} __packed;
+
+struct payload_pcm_output_format_cfg {
+ uint16_t bit_width;
+ uint16_t alignment;
+ uint16_t bits_per_sample;
+ uint16_t q_factor;
+ uint16_t endianness;
+ uint16_t interleaved;
+ uint16_t reserved;
+ uint16_t num_channels;
+ uint8_t channel_mapping[];
+} __packed;
+
+#define PARAM_ID_ENC_BITRATE 0x08001052
+
+struct param_id_enc_bitrate_param {
+ uint32_t bitrate;
+} __packed;
+
+#define DATA_FORMAT_FIXED_POINT 1
+#define DATA_FORMAT_GENERIC_COMPRESSED 5
+#define DATA_FORMAT_RAW_COMPRESSED 6
+#define PCM_LSB_ALIGNED 1
+#define PCM_MSB_ALIGNED 2
+#define PCM_LITTLE_ENDIAN 1
+#define PCM_BIT_ENDIAN 2
+
+#define MEDIA_FMT_ID_PCM 0x09001000
+#define MEDIA_FMT_ID_MP3 0x09001009
+#define SAMPLE_RATE_48K 48000
+#define BIT_WIDTH_16 16
+
+#define APM_PARAM_ID_PROP_PORT_INFO 0x08001015
+
+struct apm_modules_prop_info {
+ uint32_t max_ip_port;
+ uint32_t max_op_port;
+} __packed;
+
+/* Shared memory module */
+#define DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER 0x04001000
+#define WR_SH_MEM_EP_TIMESTAMP_VALID_FLAG BIT(31)
+#define WR_SH_MEM_EP_LAST_BUFFER_FLAG BIT(30)
+#define WR_SH_MEM_EP_TS_CONTINUE_FLAG BIT(29)
+#define WR_SH_MEM_EP_EOF_FLAG BIT(4)
+
+struct apm_data_cmd_wr_sh_mem_ep_data_buffer {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+} __packed;
+
+#define DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2 0x0400100A
+
+struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+ uint32_t md_addr_lsw;
+ uint32_t md_addr_msw;
+ uint32_t md_map_handle;
+ uint32_t md_buf_size;
+} __packed;
+
+#define DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE 0x05001000
+
+struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t status;
+
+} __packed;
+
+#define DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2 0x05001004
+
+struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t status;
+ uint32_t md_buf_addr_lsw;
+ uint32_t md_buf_addr_msw;
+ uint32_t md_mem_map_handle;
+ uint32_t md_status;
+} __packed;
+
+#define PARAM_ID_MEDIA_FORMAT 0x0800100C
+#define DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT 0x04001001
+
+struct apm_media_format {
+ uint32_t data_format;
+ uint32_t fmt_id;
+ uint32_t payload_size;
+} __packed;
+
+#define MEDIA_FMT_ID_FLAC 0x09001004
+
+struct payload_media_fmt_flac_t {
+ uint16_t num_channels;
+ uint16_t sample_size;
+ uint16_t min_blk_size;
+ uint16_t max_blk_size;
+ uint32_t sample_rate;
+ uint32_t min_frame_size;
+ uint32_t max_frame_size;
+} __packed;
+
+#define MEDIA_FMT_ID_AAC 0x09001001
+
+struct payload_media_fmt_aac_t {
+ uint16_t aac_fmt_flag;
+ uint16_t audio_obj_type;
+ uint16_t num_channels;
+ uint16_t total_size_of_PCE_bits;
+ uint32_t sample_rate;
+} __packed;
+
+#define MEDIA_FMT_ID_OPUS 0x09001039
+struct payload_media_fmt_opus_t {
+ uint16_t bitstream_format;
+ uint16_t payload_type;
+ uint8_t version;
+ uint8_t num_channels;
+ uint16_t pre_skip;
+ uint32_t sample_rate;
+ uint16_t output_gain;
+ uint8_t mapping_family;
+ uint8_t stream_count;
+ uint8_t coupled_count;
+ uint8_t channel_mapping[8];
+ uint8_t reserved[3];
+} __packed;
+
+#define DATA_CMD_WR_SH_MEM_EP_EOS 0x04001002
+#define WR_SH_MEM_EP_EOS_POLICY_LAST 1
+#define WR_SH_MEM_EP_EOS_POLICY_EACH 2
+
+struct data_cmd_wr_sh_mem_ep_eos {
+ uint32_t policy;
+
+} __packed;
+
+#define DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER 0x04001003
+
+struct data_cmd_rd_sh_mem_ep_data_buffer {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+} __packed;
+
+#define DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER 0x05001002
+
+struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done {
+ uint32_t status;
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t data_size;
+ uint32_t offset;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+ uint32_t num_frames;
+} __packed;
+
+#define DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2 0x0400100B
+
+struct data_cmd_rd_sh_mem_ep_data_buffer_v2 {
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t buf_size;
+ uint32_t md_buf_addr_lsw;
+ uint32_t md_buf_addr_msw;
+ uint32_t md_mem_map_handle;
+ uint32_t md_buf_size;
+} __packed;
+
+#define DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2 0x05001005
+
+struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 {
+ uint32_t status;
+ uint32_t buf_addr_lsw;
+ uint32_t buf_addr_msw;
+ uint32_t mem_map_handle;
+ uint32_t data_size;
+ uint32_t offset;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t flags;
+ uint32_t num_frames;
+ uint32_t md_status;
+ uint32_t md_buf_addr_lsw;
+ uint32_t md_buf_addr_msw;
+ uint32_t md_mem_map_handle;
+ uint32_t md_size;
+} __packed;
+
+#define PARAM_ID_RD_SH_MEM_CFG 0x08001007
+
+struct param_id_rd_sh_mem_cfg {
+ uint32_t num_frames_per_buffer;
+ uint32_t metadata_control_flags;
+
+} __packed;
+
+#define DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED 0x05001001
+
+struct data_cmd_wr_sh_mem_ep_eos_rendered {
+ uint32_t module_instance_id;
+ uint32_t render_status;
+} __packed;
+
+#define MODULE_ID_WR_SHARED_MEM_EP 0x07001000
+
+struct apm_cmd_header {
+ uint32_t payload_address_lsw;
+ uint32_t payload_address_msw;
+ uint32_t mem_map_handle;
+ uint32_t payload_size;
+} __packed;
+
+#define APM_CMD_HDR_SIZE sizeof(struct apm_cmd_header)
+
+struct apm_module_param_data {
+ uint32_t module_instance_id;
+ uint32_t param_id;
+ uint32_t param_size;
+ uint32_t error_code;
+} __packed;
+
+#define APM_MODULE_PARAM_DATA_SIZE sizeof(struct apm_module_param_data)
+
+struct apm_module_param_shared_data {
+ uint32_t param_id;
+ uint32_t param_size;
+} __packed;
+
+struct apm_prop_data {
+ uint32_t prop_id;
+ uint32_t prop_size;
+} __packed;
+
+/* Sub-Graph Properties */
+#define APM_PARAM_ID_SUB_GRAPH_CONFIG 0x08001001
+
+struct apm_param_id_sub_graph_cfg {
+ uint32_t num_sub_graphs;
+} __packed;
+
+struct apm_sub_graph_cfg {
+ uint32_t sub_graph_id;
+ uint32_t num_sub_graph_prop;
+} __packed;
+
+#define APM_SUB_GRAPH_PROP_ID_PERF_MODE 0x0800100E
+
+struct apm_sg_prop_id_perf_mode {
+ uint32_t perf_mode;
+} __packed;
+
+#define APM_SG_PROP_ID_PERF_MODE_SIZE 4
+
+#define APM_SUB_GRAPH_PROP_ID_DIRECTION 0x0800100F
+
+struct apm_sg_prop_id_direction {
+ uint32_t direction;
+} __packed;
+
+#define APM_SG_PROP_ID_DIR_SIZE 4
+
+#define APM_SUB_GRAPH_PROP_ID_SCENARIO_ID 0x08001010
+#define APM_SUB_GRAPH_SID_AUDIO_PLAYBACK 0x1
+#define APM_SUB_GRAPH_SID_AUDIO_RECORD 0x2
+#define APM_SUB_GRAPH_SID_AUDIO_VOICE_CALL 0x3
+
+struct apm_sg_prop_id_scenario_id {
+ uint32_t scenario_id;
+} __packed;
+
+#define APM_SG_PROP_ID_SID_SIZE 4
+/* container api */
+#define APM_PARAM_ID_CONTAINER_CONFIG 0x08001000
+
+struct apm_param_id_container_cfg {
+ uint32_t num_containers;
+} __packed;
+
+struct apm_container_cfg {
+ uint32_t container_id;
+ uint32_t num_prop;
+} __packed;
+
+struct apm_cont_capability {
+ uint32_t capability_id;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_CAPABILITY_LIST 0x08001011
+#define APM_CONTAINER_PROP_ID_CAPABILITY_SIZE 8
+
+#define APM_PROP_ID_INVALID 0x0
+#define APM_CONTAINER_CAP_ID_PP 0x1
+#define APM_CONTAINER_CAP_ID_PP 0x1
+
+struct apm_cont_prop_id_cap_list {
+ uint32_t num_capability_id;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_GRAPH_POS 0x08001012
+
+struct apm_cont_prop_id_graph_pos {
+ uint32_t graph_pos;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_STACK_SIZE 0x08001013
+
+struct apm_cont_prop_id_stack_size {
+ uint32_t stack_size;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_PROC_DOMAIN 0x08001014
+
+struct apm_cont_prop_id_domain {
+ uint32_t proc_domain;
+} __packed;
+
+#define CONFIG_I2S_WS_SRC_EXTERNAL 0x0
+#define CONFIG_I2S_WS_SRC_INTERNAL 0x1
+
+#define PARAM_ID_I2S_INTF_CFG 0x08001019
+struct param_id_i2s_intf_cfg {
+ uint32_t lpaif_type;
+ uint32_t intf_idx;
+ uint16_t sd_line_idx;
+ uint16_t ws_src;
+} __packed;
+
+#define I2S_INTF_TYPE_PRIMARY 0
+#define I2S_INTF_TYPE_SECONDARY 1
+#define I2S_INTF_TYPE_TERTIARY 2
+#define I2S_INTF_TYPE_QUATERNARY 3
+#define I2S_INTF_TYPE_QUINARY 4
+#define I2S_SD0 1
+#define I2S_SD1 2
+#define I2S_SD2 3
+#define I2S_SD3 4
+
+#define PORT_ID_I2S_INPUT 2
+#define PORT_ID_I2S_OUPUT 1
+#define I2S_STACK_SIZE 2048
+
+#define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154
+
+struct param_id_display_port_intf_cfg {
+ uint32_t channel_allocation;
+ /* Multi-Steam Transport index */
+ uint32_t mst_idx;
+ uint32_t dptx_idx;
+} __packed;
+
+#define PARAM_ID_HW_EP_MF_CFG 0x08001017
+struct param_id_hw_ep_mf {
+ uint32_t sample_rate;
+ uint16_t bit_width;
+ uint16_t num_channels;
+ uint32_t data_format;
+} __packed;
+
+#define PARAM_ID_HW_EP_FRAME_SIZE_FACTOR 0x08001018
+
+struct param_id_fram_size_factor {
+ uint32_t frame_size_factor;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_PARENT_CONTAINER_ID 0x080010CB
+
+struct apm_cont_prop_id_parent_container {
+ uint32_t parent_container_id;
+} __packed;
+
+#define APM_CONTAINER_PROP_ID_HEAP_ID 0x08001174
+#define APM_CONT_HEAP_DEFAULT 0x1
+#define APM_CONT_HEAP_LOW_POWER 0x2
+
+struct apm_cont_prop_id_headp_id {
+ uint32_t heap_id;
+} __packed;
+
+struct apm_modules_list {
+ uint32_t sub_graph_id;
+ uint32_t container_id;
+ uint32_t num_modules;
+} __packed;
+
+struct apm_module_obj {
+ uint32_t module_id;
+ uint32_t instance_id;
+} __packed;
+
+#define APM_MODULE_PROP_ID_PORT_INFO 0x08001015
+#define APM_MODULE_PROP_ID_PORT_INFO_SZ 8
+struct apm_module_prop_id_port_info {
+ uint32_t max_ip_port;
+ uint32_t max_op_port;
+} __packed;
+
+#define DATA_LOGGING_MAX_INPUT_PORTS 0x1
+#define DATA_LOGGING_MAX_OUTPUT_PORTS 0x1
+#define DATA_LOGGING_STACK_SIZE 2048
+#define PARAM_ID_DATA_LOGGING_CONFIG 0x08001031
+
+struct data_logging_config {
+ uint32_t log_code;
+ uint32_t log_tap_point_id;
+ uint32_t mode;
+} __packed;
+
+#define PARAM_ID_SAL_OUTPUT_CFG 0x08001016
+struct param_id_sal_output_config {
+ uint32_t bits_per_sample;
+} __packed;
+
+#define PARAM_ID_SAL_LIMITER_ENABLE 0x0800101E
+struct param_id_sal_limiter_enable {
+ uint32_t enable_lim;
+} __packed;
+
+#define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024
+#define PARAM_ID_EARLY_EOS_DELAY 0x0800114C
+#define EARLY_EOS_DELAY_MS 150
+
+struct param_id_mfc_media_format {
+ uint32_t sample_rate;
+ uint16_t bit_width;
+ uint16_t num_channels;
+ uint16_t channel_mapping[];
+} __packed;
+
+struct param_id_gapless_early_eos_delay_t {
+ uint32_t early_eos_delay_ms;
+} __packed;
+
+struct media_format {
+ uint32_t data_format;
+ uint32_t fmt_id;
+ uint32_t payload_size;
+} __packed;
+
+struct payload_media_fmt_pcm {
+ uint32_t sample_rate;
+ uint16_t bit_width;
+ uint16_t alignment;
+ uint16_t bits_per_sample;
+ uint16_t q_factor;
+ uint16_t endianness;
+ uint16_t num_channels;
+ uint8_t channel_mapping[];
+} __packed;
+
+#define PARAM_ID_MODULE_ENABLE 0x08001026
+struct param_id_module_enable {
+ uint32_t enable;
+} __packed;
+
+#define PARAM_ID_CODEC_DMA_INTF_CFG 0x08001063
+
+struct param_id_codec_dma_intf_cfg {
+ /* 1 - RXTX
+ * 2 - WSA
+ * 3 - VA
+ * 4 - AXI
+ */
+ uint32_t lpaif_type;
+ /*
+ * RX0 | TX0 = 1
+ * RX1 | TX1 = 2
+ * RX2 | TX2 = 3... so on
+ */
+ uint32_t intf_index;
+ uint32_t active_channels_mask;
+} __packed;
+
+struct audio_hw_clk_cfg {
+ uint32_t clock_id;
+ uint32_t clock_freq;
+ uint32_t clock_attri;
+ uint32_t clock_root;
+} __packed;
+
+struct audio_hw_clk_rel_cfg {
+ uint32_t clock_id;
+} __packed;
+
+#define PARAM_ID_HW_EP_POWER_MODE_CFG 0x8001176
+#define AR_HW_EP_POWER_MODE_0 0 /* default */
+#define AR_HW_EP_POWER_MODE_1 1 /* XO Shutdown allowed */
+#define AR_HW_EP_POWER_MODE_2 2 /* XO Shutdown not allowed */
+
+struct param_id_hw_ep_power_mode_cfg {
+ uint32_t power_mode;
+} __packed;
+
+#define PARAM_ID_HW_EP_DMA_DATA_ALIGN 0x08001233
+#define AR_HW_EP_DMA_DATA_ALIGN_MSB 0
+#define AR_HW_EP_DMA_DATA_ALIGN_LSB 1
+#define AR_PCM_MAX_NUM_CHANNEL 8
+
+struct param_id_hw_ep_dma_data_align {
+ uint32_t dma_data_align;
+} __packed;
+
+#define PARAM_ID_VOL_CTRL_MASTER_GAIN 0x08001035
+#define VOL_CTRL_DEFAULT_GAIN 0x2000
+
+struct param_id_vol_ctrl_master_gain {
+ uint16_t master_gain;
+ uint16_t reserved;
+} __packed;
+
+
+#define PARAM_ID_REMOVE_INITIAL_SILENCE 0x0800114B
+#define PARAM_ID_REMOVE_TRAILING_SILENCE 0x0800115D
+
+#define PARAM_ID_REAL_MODULE_ID 0x0800100B
+
+struct param_id_placeholder_real_module_id {
+ uint32_t real_module_id;
+} __packed;
+
+/* Graph */
+struct audioreach_connection {
+ /* Connections */
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id;
+ uint32_t dst_mod_inst_id;
+ uint32_t dst_mod_ip_port_id;
+ struct list_head node;
+};
+
+struct audioreach_graph_info {
+ int id;
+ uint32_t num_sub_graphs;
+ struct list_head sg_list;
+ /* DPCM connection from FE Graph to BE graph */
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id;
+ uint32_t dst_mod_inst_id;
+ uint32_t dst_mod_ip_port_id;
+};
+
+struct audioreach_sub_graph {
+ uint32_t sub_graph_id;
+ uint32_t perf_mode;
+ uint32_t direction;
+ uint32_t scenario_id;
+ struct list_head node;
+
+ struct audioreach_graph_info *info;
+ uint32_t num_containers;
+ struct list_head container_list;
+};
+
+struct audioreach_container {
+ uint32_t container_id;
+ uint32_t capability_id;
+ uint32_t graph_pos;
+ uint32_t stack_size;
+ uint32_t proc_domain;
+ struct list_head node;
+
+ uint32_t num_modules;
+ struct list_head modules_list;
+ struct audioreach_sub_graph *sub_graph;
+};
+
+#define AR_MAX_MOD_LINKS 8
+
+struct audioreach_module {
+ uint32_t module_id;
+ uint32_t instance_id;
+
+ uint32_t max_ip_port;
+ uint32_t max_op_port;
+
+ uint32_t num_connections;
+ /* Connections */
+ uint32_t src_mod_inst_id;
+ uint32_t src_mod_op_port_id[AR_MAX_MOD_LINKS];
+ uint32_t dst_mod_inst_id[AR_MAX_MOD_LINKS];
+ uint32_t dst_mod_ip_port_id[AR_MAX_MOD_LINKS];
+
+ /* Format specifics */
+ uint32_t ch_fmt;
+ uint32_t rate;
+ uint32_t bit_depth;
+
+ /* I2S module */
+ uint32_t hw_interface_idx;
+ uint32_t sd_line_idx;
+ uint32_t ws_src;
+ uint32_t frame_size_factor;
+ uint32_t data_format;
+ uint32_t hw_interface_type;
+
+ /* PCM module specific */
+ uint32_t interleave_type;
+
+ /* GAIN/Vol Control Module */
+ uint16_t gain;
+
+ /* Logging */
+ uint32_t log_code;
+ uint32_t log_tap_point_id;
+ uint32_t log_mode;
+
+ /* bookkeeping */
+ struct list_head node;
+ struct audioreach_container *container;
+ struct snd_soc_dapm_widget *widget;
+ struct audioreach_module_priv_data *data;
+};
+
+struct audioreach_module_config {
+ int direction;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 bits_per_sample;
+
+ u16 data_format;
+ u16 num_channels;
+ u16 dp_idx;
+ u32 channel_allocation;
+ u32 sd_line_mask;
+ int fmt;
+ struct snd_codec codec;
+ u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
+};
+
+/* Packet Allocation routines */
+void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t
+ token);
+void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels);
+void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode,
+ uint32_t token, uint32_t src_port,
+ uint32_t dest_port);
+void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
+ uint32_t src_port);
+void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
+ uint32_t token, uint32_t src_port,
+ uint32_t dest_port);
+void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info
+ *info);
+/* Topology specific */
+int audioreach_tplg_init(struct snd_soc_component *component);
+
+/* Module specific */
+void audioreach_graph_free_buf(struct q6apm_graph *graph);
+int audioreach_map_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir, size_t period_sz,
+ unsigned int periods,
+ bool is_contiguous);
+int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result,
+ struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait,
+ struct gpr_pkt *pkt, uint32_t rsp_opcode);
+int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
+ uint32_t rsp_opcode);
+int audioreach_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg);
+int audioreach_shared_memory_send_eos(struct q6apm_graph *graph);
+int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
+ struct audioreach_module *module, int vol);
+int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ uint32_t param_id, uint32_t param_val);
+int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg);
+
+#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c
new file mode 100644
index 000000000000..0b8d06ec8b26
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6adm.c
@@ -0,0 +1,607 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/wait.h>
+#include <sound/asound.h>
+#include "q6adm.h"
+#include "q6afe.h"
+#include "q6core.h"
+#include "q6dsp-common.h"
+#include "q6dsp-errno.h"
+
+#define ADM_CMD_DEVICE_OPEN_V5 0x00010326
+#define ADM_CMDRSP_DEVICE_OPEN_V5 0x00010329
+#define ADM_CMD_DEVICE_CLOSE_V5 0x00010327
+#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325
+
+#define TIMEOUT_MS 1000
+#define RESET_COPP_ID 99
+#define INVALID_COPP_ID 0xFF
+/* Definition for a legacy device session. */
+#define ADM_LEGACY_DEVICE_SESSION 0
+#define ADM_MATRIX_ID_AUDIO_RX 0
+#define ADM_MATRIX_ID_AUDIO_TX 1
+
+struct q6copp {
+ int afe_port;
+ int copp_idx;
+ int id;
+ int topology;
+ int mode;
+ int rate;
+ int bit_width;
+ int channels;
+ int app_type;
+ int acdb_id;
+
+ struct aprv2_ibasic_rsp_result_t result;
+ struct kref refcount;
+ wait_queue_head_t wait;
+ struct list_head node;
+ struct q6adm *adm;
+};
+
+struct q6adm {
+ struct apr_device *apr;
+ struct device *dev;
+ struct q6core_svc_api_info ainfo;
+ unsigned long copp_bitmap[AFE_MAX_PORTS];
+ struct list_head copps_list;
+ spinlock_t copps_list_lock;
+ struct aprv2_ibasic_rsp_result_t result;
+ struct mutex lock;
+ wait_queue_head_t matrix_map_wait;
+};
+
+struct q6adm_cmd_device_open_v5 {
+ u16 flags;
+ u16 mode_of_operation;
+ u16 endpoint_id_1;
+ u16 endpoint_id_2;
+ u32 topology_id;
+ u16 dev_num_channel;
+ u16 bit_width;
+ u32 sample_rate;
+ u8 dev_channel_mapping[8];
+} __packed;
+
+struct q6adm_cmd_matrix_map_routings_v5 {
+ u32 matrix_id;
+ u32 num_sessions;
+} __packed;
+
+struct q6adm_session_map_node_v5 {
+ u16 session_id;
+ u16 num_copps;
+} __packed;
+
+static struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx,
+ int copp_idx)
+{
+ struct q6copp *c;
+ struct q6copp *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adm->copps_list_lock, flags);
+ list_for_each_entry(c, &adm->copps_list, node) {
+ if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) {
+ ret = c;
+ kref_get(&c->refcount);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&adm->copps_list_lock, flags);
+
+ return ret;
+
+}
+
+static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp,
+ struct apr_pkt *pkt, uint32_t rsp_opcode)
+{
+ struct device *dev = adm->dev;
+ uint32_t opcode = pkt->hdr.opcode;
+ int ret;
+
+ mutex_lock(&adm->lock);
+ copp->result.opcode = 0;
+ copp->result.status = 0;
+ ret = apr_send_pkt(adm->apr, pkt);
+ if (ret < 0) {
+ dev_err(dev, "Failed to send APR packet\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Wait for the callback with copp id */
+ if (rsp_opcode)
+ ret = wait_event_timeout(copp->wait,
+ (copp->result.opcode == opcode) ||
+ (copp->result.opcode == rsp_opcode),
+ msecs_to_jiffies(TIMEOUT_MS));
+ else
+ ret = wait_event_timeout(copp->wait,
+ (copp->result.opcode == opcode),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ dev_err(dev, "ADM copp cmd timedout\n");
+ ret = -ETIMEDOUT;
+ } else if (copp->result.status > 0) {
+ dev_err(dev, "DSP returned error[%d]\n",
+ copp->result.status);
+ ret = -EINVAL;
+ }
+
+err:
+ mutex_unlock(&adm->lock);
+ return ret;
+}
+
+static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp,
+ int port_id, int copp_idx)
+{
+ struct apr_pkt close;
+
+ close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ close.hdr.pkt_size = sizeof(close);
+ close.hdr.src_port = port_id;
+ close.hdr.dest_port = copp->id;
+ close.hdr.token = port_id << 16 | copp_idx;
+ close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5;
+
+ return q6adm_apr_send_copp_pkt(adm, copp, &close, 0);
+}
+
+static void q6adm_free_copp(struct kref *ref)
+{
+ struct q6copp *c = container_of(ref, struct q6copp, refcount);
+ struct q6adm *adm = c->adm;
+ unsigned long flags;
+ int ret;
+
+ ret = q6adm_device_close(adm, c, c->afe_port, c->copp_idx);
+ if (ret < 0)
+ dev_err(adm->dev, "Failed to close copp %d\n", ret);
+
+ spin_lock_irqsave(&adm->copps_list_lock, flags);
+ clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]);
+ list_del(&c->node);
+ spin_unlock_irqrestore(&adm->copps_list_lock, flags);
+ kfree(c);
+}
+
+static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+{
+ struct aprv2_ibasic_rsp_result_t *result = data->payload;
+ int port_idx, copp_idx;
+ struct apr_hdr *hdr = &data->hdr;
+ struct q6copp *copp;
+ struct q6adm *adm = dev_get_drvdata(&adev->dev);
+
+ if (!data->payload_size)
+ return 0;
+
+ copp_idx = (hdr->token) & 0XFF;
+ port_idx = ((hdr->token) >> 16) & 0xFF;
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ dev_err(&adev->dev, "Invalid port idx %d token %d\n",
+ port_idx, hdr->token);
+ return 0;
+ }
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ dev_err(&adev->dev, "Invalid copp idx %d token %d\n",
+ copp_idx, hdr->token);
+ return 0;
+ }
+
+ switch (hdr->opcode) {
+ case APR_BASIC_RSP_RESULT: {
+ if (result->status != 0) {
+ dev_err(&adev->dev, "cmd = 0x%x return error = 0x%x\n",
+ result->opcode, result->status);
+ }
+ switch (result->opcode) {
+ case ADM_CMD_DEVICE_OPEN_V5:
+ case ADM_CMD_DEVICE_CLOSE_V5:
+ list_for_each_entry(copp, &adm->copps_list, node) {
+ if ((port_idx == copp->afe_port) && (copp_idx == copp->copp_idx)) {
+ copp->result = *result;
+ wake_up(&copp->wait);
+ break;
+ }
+ }
+ break;
+ case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
+ adm->result = *result;
+ wake_up(&adm->matrix_map_wait);
+ break;
+
+ default:
+ dev_err(&adev->dev, "Unknown Cmd: 0x%x\n",
+ result->opcode);
+ break;
+ }
+ return 0;
+ }
+ case ADM_CMDRSP_DEVICE_OPEN_V5: {
+ struct adm_cmd_rsp_device_open_v5 {
+ u32 status;
+ u16 copp_id;
+ u16 reserved;
+ } __packed *open = data->payload;
+
+ copp = q6adm_find_copp(adm, port_idx, copp_idx);
+ if (!copp)
+ return 0;
+
+ if (open->copp_id == INVALID_COPP_ID) {
+ dev_err(&adev->dev, "Invalid coppid rxed %d\n",
+ open->copp_id);
+ copp->result.status = ADSP_EBADPARAM;
+ wake_up(&copp->wait);
+ kref_put(&copp->refcount, q6adm_free_copp);
+ break;
+ }
+ copp->result.opcode = hdr->opcode;
+ copp->id = open->copp_id;
+ wake_up(&copp->wait);
+ kref_put(&copp->refcount, q6adm_free_copp);
+ }
+ break;
+ default:
+ dev_err(&adev->dev, "Unknown cmd:0x%x\n",
+ hdr->opcode);
+ break;
+ }
+
+ return 0;
+}
+
+static struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx)
+{
+ struct q6copp *c;
+ int idx;
+
+ idx = find_first_zero_bit(&adm->copp_bitmap[port_idx],
+ MAX_COPPS_PER_PORT);
+
+ if (idx >= MAX_COPPS_PER_PORT)
+ return ERR_PTR(-EBUSY);
+
+ c = kzalloc(sizeof(*c), GFP_ATOMIC);
+ if (!c)
+ return ERR_PTR(-ENOMEM);
+
+ set_bit(idx, &adm->copp_bitmap[port_idx]);
+ c->copp_idx = idx;
+ c->afe_port = port_idx;
+ c->adm = adm;
+
+ init_waitqueue_head(&c->wait);
+
+ return c;
+}
+
+static struct q6copp *q6adm_find_matching_copp(struct q6adm *adm,
+ int port_id, int topology,
+ int mode, int rate,
+ int channel_mode, int bit_width,
+ int app_type)
+{
+ struct q6copp *c;
+ struct q6copp *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adm->copps_list_lock, flags);
+
+ list_for_each_entry(c, &adm->copps_list, node) {
+ if ((port_id == c->afe_port) && (topology == c->topology) &&
+ (mode == c->mode) && (rate == c->rate) &&
+ (bit_width == c->bit_width) && (app_type == c->app_type)) {
+ ret = c;
+ kref_get(&c->refcount);
+ }
+ }
+ spin_unlock_irqrestore(&adm->copps_list_lock, flags);
+
+ return ret;
+}
+
+static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp,
+ int port_id, int path, int topology,
+ int channel_mode, int bit_width, int rate)
+{
+ struct q6adm_cmd_device_open_v5 *open;
+ int afe_port = q6afe_get_port_id(port_id);
+ struct apr_pkt *pkt;
+ int ret, pkt_size = APR_HDR_SIZE + sizeof(*open);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ open = p + APR_HDR_SIZE;
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = afe_port;
+ pkt->hdr.dest_port = afe_port;
+ pkt->hdr.token = port_id << 16 | copp->copp_idx;
+ pkt->hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
+ open->flags = ADM_LEGACY_DEVICE_SESSION;
+ open->mode_of_operation = path;
+ open->endpoint_id_1 = afe_port;
+ open->topology_id = topology;
+ open->dev_num_channel = channel_mode & 0x00FF;
+ open->bit_width = bit_width;
+ open->sample_rate = rate;
+
+ ret = q6dsp_map_channels(&open->dev_channel_mapping[0],
+ channel_mode);
+ if (ret)
+ return ret;
+
+ return q6adm_apr_send_copp_pkt(adm, copp, pkt, ADM_CMDRSP_DEVICE_OPEN_V5);
+}
+
+/**
+ * q6adm_open() - open adm and grab a free copp
+ *
+ * @dev: Pointer to adm child device.
+ * @port_id: port id
+ * @path: playback or capture path.
+ * @rate: rate at which copp is required.
+ * @channel_mode: channel mode
+ * @topology: adm topology id
+ * @perf_mode: performace mode.
+ * @bit_width: audio sample bit width
+ * @app_type: Application type.
+ * @acdb_id: ACDB id
+ *
+ * Return: Will be an negative on error or a valid copp pointer on success.
+ */
+struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate,
+ int channel_mode, int topology, int perf_mode,
+ uint16_t bit_width, int app_type, int acdb_id)
+{
+ struct q6adm *adm = dev_get_drvdata(dev->parent);
+ struct q6copp *copp;
+ unsigned long flags;
+ int ret = 0;
+
+ if (port_id < 0) {
+ dev_err(dev, "Invalid port_id %d\n", port_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ copp = q6adm_find_matching_copp(adm, port_id, topology, perf_mode,
+ rate, channel_mode, bit_width, app_type);
+ if (copp) {
+ dev_err(dev, "Found Matching Copp 0x%x\n", copp->copp_idx);
+ return copp;
+ }
+
+ spin_lock_irqsave(&adm->copps_list_lock, flags);
+ copp = q6adm_alloc_copp(adm, port_id);
+ if (IS_ERR(copp)) {
+ spin_unlock_irqrestore(&adm->copps_list_lock, flags);
+ return ERR_CAST(copp);
+ }
+
+ list_add_tail(&copp->node, &adm->copps_list);
+ spin_unlock_irqrestore(&adm->copps_list_lock, flags);
+
+ kref_init(&copp->refcount);
+ copp->topology = topology;
+ copp->mode = perf_mode;
+ copp->rate = rate;
+ copp->channels = channel_mode;
+ copp->bit_width = bit_width;
+ copp->app_type = app_type;
+
+ ret = q6adm_device_open(adm, copp, port_id, path, topology,
+ channel_mode, bit_width, rate);
+ if (ret < 0) {
+ kref_put(&copp->refcount, q6adm_free_copp);
+ return ERR_PTR(ret);
+ }
+
+ return copp;
+}
+EXPORT_SYMBOL_GPL(q6adm_open);
+
+/**
+ * q6adm_get_copp_id() - get copp index
+ *
+ * @copp: Pointer to valid copp
+ *
+ * Return: Will be an negative on error or a valid copp index on success.
+ **/
+int q6adm_get_copp_id(struct q6copp *copp)
+{
+ if (!copp)
+ return -EINVAL;
+
+ return copp->copp_idx;
+}
+EXPORT_SYMBOL_GPL(q6adm_get_copp_id);
+
+/**
+ * q6adm_matrix_map() - Map asm streams and afe ports using payload
+ *
+ * @dev: Pointer to adm child device.
+ * @path: playback or capture path.
+ * @payload_map: map between session id and afe ports.
+ * @perf_mode: Performace mode.
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int q6adm_matrix_map(struct device *dev, int path,
+ struct route_payload payload_map, int perf_mode)
+{
+ struct q6adm *adm = dev_get_drvdata(dev->parent);
+ struct q6adm_cmd_matrix_map_routings_v5 *route;
+ struct q6adm_session_map_node_v5 *node;
+ struct apr_pkt *pkt;
+ uint16_t *copps_list;
+ int ret, i, copp_idx;
+ /* Assumes port_ids have already been validated during adm_open */
+ struct q6copp *copp;
+ int pkt_size = (APR_HDR_SIZE + sizeof(*route) + sizeof(*node) +
+ (sizeof(uint32_t) * payload_map.num_copps));
+ void *matrix_map __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!matrix_map)
+ return -ENOMEM;
+
+ pkt = matrix_map;
+ route = matrix_map + APR_HDR_SIZE;
+ node = matrix_map + APR_HDR_SIZE + sizeof(*route);
+ copps_list = matrix_map + APR_HDR_SIZE + sizeof(*route) + sizeof(*node);
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.token = 0;
+ pkt->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
+ route->num_sessions = 1;
+
+ switch (path) {
+ case ADM_PATH_PLAYBACK:
+ route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
+ break;
+ case ADM_PATH_LIVE_REC:
+ route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
+ break;
+ default:
+ dev_err(dev, "Wrong path set[%d]\n", path);
+ break;
+ }
+
+ node->session_id = payload_map.session_id;
+ node->num_copps = payload_map.num_copps;
+
+ for (i = 0; i < payload_map.num_copps; i++) {
+ int port_idx = payload_map.port_id[i];
+
+ if (port_idx < 0) {
+ dev_err(dev, "Invalid port_id %d\n",
+ payload_map.port_id[i]);
+ return -EINVAL;
+ }
+ copp_idx = payload_map.copp_idx[i];
+
+ copp = q6adm_find_copp(adm, port_idx, copp_idx);
+ if (!copp)
+ return -EINVAL;
+
+ copps_list[i] = copp->id;
+ kref_put(&copp->refcount, q6adm_free_copp);
+ }
+
+ mutex_lock(&adm->lock);
+ adm->result.status = 0;
+ adm->result.opcode = 0;
+
+ ret = apr_send_pkt(adm->apr, pkt);
+ if (ret < 0) {
+ dev_err(dev, "routing for stream %d failed ret %d\n",
+ payload_map.session_id, ret);
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(adm->matrix_map_wait,
+ adm->result.opcode == pkt->hdr.opcode,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ dev_err(dev, "routing for stream %d failed\n",
+ payload_map.session_id);
+ ret = -ETIMEDOUT;
+ goto fail_cmd;
+ } else if (adm->result.status > 0) {
+ dev_err(dev, "DSP returned error[%d]\n",
+ adm->result.status);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ mutex_unlock(&adm->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6adm_matrix_map);
+
+/**
+ * q6adm_close() - Close adm copp
+ *
+ * @dev: Pointer to adm child device.
+ * @copp: pointer to previously opened copp
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int q6adm_close(struct device *dev, struct q6copp *copp)
+{
+ kref_put(&copp->refcount, q6adm_free_copp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6adm_close);
+
+static int q6adm_probe(struct apr_device *adev)
+{
+ struct device *dev = &adev->dev;
+ struct q6adm *adm;
+
+ adm = devm_kzalloc(dev, sizeof(*adm), GFP_KERNEL);
+ if (!adm)
+ return -ENOMEM;
+
+ adm->apr = adev;
+ dev_set_drvdata(dev, adm);
+ adm->dev = dev;
+ q6core_get_svc_api_info(adev->svc_id, &adm->ainfo);
+ mutex_init(&adm->lock);
+ init_waitqueue_head(&adm->matrix_map_wait);
+
+ INIT_LIST_HEAD(&adm->copps_list);
+ spin_lock_init(&adm->copps_list_lock);
+
+ return devm_of_platform_populate(dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6adm_device_id[] = {
+ { .compatible = "qcom,q6adm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6adm_device_id);
+#endif
+
+static struct apr_driver qcom_q6adm_driver = {
+ .probe = q6adm_probe,
+ .callback = q6adm_callback,
+ .driver = {
+ .name = "qcom-q6adm",
+ .of_match_table = of_match_ptr(q6adm_device_id),
+ },
+};
+
+module_apr_driver(qcom_q6adm_driver);
+MODULE_DESCRIPTION("Q6 Audio Device Manager");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6adm.h b/sound/soc/qcom/qdsp6/q6adm.h
new file mode 100644
index 000000000000..4f56999b7fab
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6adm.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __Q6_ADM_V2_H__
+#define __Q6_ADM_V2_H__
+
+#define ADM_PATH_PLAYBACK 0x1
+#define ADM_PATH_LIVE_REC 0x2
+#define MAX_COPPS_PER_PORT 8
+#define NULL_COPP_TOPOLOGY 0x00010312
+
+/* multiple copp per stream. */
+struct route_payload {
+ int num_copps;
+ int session_id;
+ int copp_idx[MAX_COPPS_PER_PORT];
+ int port_id[MAX_COPPS_PER_PORT];
+};
+
+struct q6copp;
+struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate,
+ int channel_mode, int topology, int perf_mode,
+ uint16_t bit_width, int app_type, int acdb_id);
+int q6adm_close(struct device *dev, struct q6copp *copp);
+int q6adm_get_copp_id(struct q6copp *copp);
+int q6adm_matrix_map(struct device *dev, int path,
+ struct route_payload payload_map, int perf_mode);
+
+#endif /* __Q6_ADM_V2_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c
new file mode 100644
index 000000000000..84b9018c36ba
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include "q6dsp-lpass-clocks.h"
+#include "q6afe.h"
+
+#define Q6AFE_CLK(id) { \
+ .clk_id = id, \
+ .q6dsp_clk_id = Q6AFE_##id, \
+ .name = #id, \
+ .rate = 19200000, \
+ }
+
+
+static const struct q6dsp_clk_init q6afe_clks[] = {
+ Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_OSR),
+ Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT0_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT1_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT2_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT3_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT4_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_OSR),
+ Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUI_PCM_OSR),
+ Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_IBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_EBIT),
+ Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_OSR),
+ Q6AFE_CLK(LPASS_CLK_ID_MCLK_1),
+ Q6AFE_CLK(LPASS_CLK_ID_MCLK_2),
+ Q6AFE_CLK(LPASS_CLK_ID_MCLK_3),
+ Q6AFE_CLK(LPASS_CLK_ID_MCLK_4),
+ Q6AFE_CLK(LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE),
+ Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_0),
+ Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_1),
+ Q6AFE_CLK(LPASS_CLK_ID_WSA_CORE_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_TX_CORE_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_TX_CORE_NPL_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK),
+ Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK),
+ Q6DSP_VOTE_CLK(LPASS_HW_AVTIMER_VOTE,
+ Q6AFE_LPASS_CORE_AVTIMER_BLOCK,
+ "LPASS_AVTIMER_MACRO"),
+ Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE,
+ Q6AFE_LPASS_CORE_HW_MACRO_BLOCK,
+ "LPASS_HW_MACRO"),
+ Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE,
+ Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK,
+ "LPASS_HW_DCODEC"),
+};
+
+static const struct q6dsp_clk_desc q6dsp_clk_q6afe __maybe_unused = {
+ .clks = q6afe_clks,
+ .num_clks = ARRAY_SIZE(q6afe_clks),
+ .lpass_set_clk = q6afe_set_lpass_clock,
+ .lpass_vote_clk = q6afe_vote_lpass_core_hw,
+ .lpass_unvote_clk = q6afe_unvote_lpass_core_hw,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6afe_clock_device_id[] = {
+ { .compatible = "qcom,q6afe-clocks", .data = &q6dsp_clk_q6afe },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6afe_clock_device_id);
+#endif
+
+static struct platform_driver q6afe_clock_platform_driver = {
+ .driver = {
+ .name = "q6afe-clock",
+ .of_match_table = of_match_ptr(q6afe_clock_device_id),
+ },
+ .probe = q6dsp_clock_dev_probe,
+};
+module_platform_driver(q6afe_clock_platform_driver);
+
+MODULE_DESCRIPTION("Q6 Audio Frontend clock driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
new file mode 100644
index 000000000000..0f47aadaabe1
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -0,0 +1,1147 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "q6dsp-lpass-ports.h"
+#include "q6dsp-common.h"
+#include "q6afe.h"
+
+
+struct q6afe_dai_priv_data {
+ uint32_t sd_line_mask;
+ uint32_t sync_mode;
+ uint32_t sync_src;
+ uint32_t data_out_enable;
+ uint32_t invert_sync;
+ uint32_t data_delay;
+ uint32_t data_align;
+};
+
+struct q6afe_dai_data {
+ struct q6afe_port *port[AFE_PORT_MAX];
+ struct q6afe_port_config port_config[AFE_PORT_MAX];
+ bool is_port_started[AFE_PORT_MAX];
+ struct q6afe_dai_priv_data priv[AFE_PORT_MAX];
+};
+
+static int q6slim_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_slim_cfg *slim = &dai_data->port_config[dai->id].slim;
+
+ slim->sample_rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ slim->bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ slim->bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ slim->bit_width = 32;
+ break;
+ default:
+ pr_err("%s: format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int channels = params_channels(params);
+ struct q6afe_hdmi_cfg *hdmi = &dai_data->port_config[dai->id].hdmi;
+ int ret;
+
+ hdmi->sample_rate = params_rate(params);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ hdmi->bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ hdmi->bit_width = 24;
+ break;
+ }
+
+ ret = q6dsp_get_channel_allocation(channels);
+ if (ret < 0)
+ return ret;
+
+ hdmi->channel_allocation = (u16) ret;
+
+ return 0;
+}
+
+static int q6afe_usb_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int channels = params_channels(params);
+ int rate = params_rate(params);
+ struct q6afe_usb_cfg *usb = &dai_data->port_config[dai->id].usb_audio;
+
+ usb->sample_rate = rate;
+ usb->num_channels = channels;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ usb->bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ usb->bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ usb->bit_width = 32;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int q6i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_i2s_cfg *i2s = &dai_data->port_config[dai->id].i2s_cfg;
+
+ i2s->sample_rate = params_rate(params);
+ i2s->bit_width = params_width(params);
+ i2s->num_channels = params_channels(params);
+ i2s->sd_line_mask = dai_data->priv[dai->id].sd_line_mask;
+
+ return 0;
+}
+
+static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_i2s_cfg *i2s = &dai_data->port_config[dai->id].i2s_cfg;
+
+ i2s->fmt = fmt;
+
+ return 0;
+}
+
+static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_tdm_cfg *tdm = &dai_data->port_config[dai->id].tdm;
+ unsigned int cap_mask;
+ int rc = 0;
+
+ /* HW only supports 16 and 32 bit slot width configuration */
+ if ((slot_width != 16) && (slot_width != 32)) {
+ dev_err(dai->dev, "%s: invalid slot_width %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */
+ switch (slots) {
+ case 2:
+ cap_mask = 0x03;
+ break;
+ case 4:
+ cap_mask = 0x0F;
+ break;
+ case 8:
+ cap_mask = 0xFF;
+ break;
+ case 16:
+ cap_mask = 0xFFFF;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid slots %d\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ tdm->nslots_per_frame = slots;
+ tdm->slot_width = slot_width;
+ /* TDM RX dais ids are even and tx are odd */
+ tdm->slot_mask = ((dai->id & 0x1) ? tx_mask : rx_mask) & cap_mask;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int q6tdm_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, const unsigned int *tx_slot,
+ unsigned int rx_num, const unsigned int *rx_slot)
+{
+
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_tdm_cfg *tdm = &dai_data->port_config[dai->id].tdm;
+ int rc = 0;
+ int i = 0;
+
+ switch (dai->id) {
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ if (dai->id & 0x1) {
+ if (!tx_slot) {
+ dev_err(dai->dev, "tx slot not found\n");
+ return -EINVAL;
+ }
+ if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "invalid tx num %d\n",
+ tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tx_num; i++)
+ tdm->ch_mapping[i] = tx_slot[i];
+
+ for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+ tdm->ch_mapping[i] = Q6AFE_CMAP_INVALID;
+
+ tdm->num_channels = tx_num;
+ } else {
+ /* rx */
+ if (!rx_slot) {
+ dev_err(dai->dev, "rx slot not found\n");
+ return -EINVAL;
+ }
+ if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "invalid rx num %d\n",
+ rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++)
+ tdm->ch_mapping[i] = rx_slot[i];
+
+ for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+ tdm->ch_mapping[i] = Q6AFE_CMAP_INVALID;
+
+ tdm->num_channels = rx_num;
+ }
+
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int q6tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_tdm_cfg *tdm = &dai_data->port_config[dai->id].tdm;
+
+ tdm->bit_width = params_width(params);
+ tdm->sample_rate = params_rate(params);
+ tdm->num_channels = params_channels(params);
+ tdm->data_align_type = dai_data->priv[dai->id].data_align;
+ tdm->sync_src = dai_data->priv[dai->id].sync_src;
+ tdm->sync_mode = dai_data->priv[dai->id].sync_mode;
+
+ return 0;
+}
+
+static int q6dma_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_ch_mask,
+ unsigned int rx_num,
+ const unsigned int *rx_ch_mask)
+{
+
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg;
+ int ch_mask;
+ int rc = 0;
+
+ switch (dai->id) {
+ case WSA_CODEC_DMA_TX_0:
+ case WSA_CODEC_DMA_TX_1:
+ case WSA_CODEC_DMA_TX_2:
+ case VA_CODEC_DMA_TX_0:
+ case VA_CODEC_DMA_TX_1:
+ case VA_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ case TX_CODEC_DMA_TX_4:
+ case TX_CODEC_DMA_TX_5:
+ if (!tx_ch_mask) {
+ dev_err(dai->dev, "tx slot not found\n");
+ return -EINVAL;
+ }
+
+ if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "invalid tx num %d\n",
+ tx_num);
+ return -EINVAL;
+ }
+ ch_mask = *tx_ch_mask;
+
+ break;
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_2:
+ case RX_CODEC_DMA_RX_3:
+ case RX_CODEC_DMA_RX_4:
+ case RX_CODEC_DMA_RX_5:
+ case RX_CODEC_DMA_RX_6:
+ case RX_CODEC_DMA_RX_7:
+ /* rx */
+ if (!rx_ch_mask) {
+ dev_err(dai->dev, "rx slot not found\n");
+ return -EINVAL;
+ }
+ if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "invalid rx num %d\n",
+ rx_num);
+ return -EINVAL;
+ }
+ ch_mask = *rx_ch_mask;
+
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ cfg->active_channels_mask = ch_mask;
+
+ return rc;
+}
+
+static int q6dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg;
+
+ cfg->bit_width = params_width(params);
+ cfg->sample_rate = params_rate(params);
+ cfg->num_channels = params_channels(params);
+
+ return 0;
+}
+static void q6afe_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc;
+
+ if (!dai_data->is_port_started[dai->id])
+ return;
+
+ rc = q6afe_port_stop(dai_data->port[dai->id]);
+ if (rc < 0)
+ dev_err(dai->dev, "fail to close AFE port (%d)\n", rc);
+
+ dai_data->is_port_started[dai->id] = false;
+
+}
+
+static int q6afe_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc;
+
+ if (dai_data->is_port_started[dai->id]) {
+ /* stop the port and restart with new port config */
+ rc = q6afe_port_stop(dai_data->port[dai->id]);
+ if (rc < 0) {
+ dev_err(dai->dev, "fail to close AFE port (%d)\n", rc);
+ return rc;
+ }
+ }
+
+ switch (dai->id) {
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ q6afe_hdmi_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].hdmi);
+ break;
+ case SLIMBUS_0_RX ... SLIMBUS_6_TX:
+ q6afe_slim_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].slim);
+ break;
+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
+ rc = q6afe_i2s_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].i2s_cfg);
+ if (rc < 0) {
+ dev_err(dai->dev, "fail to prepare AFE port %x\n",
+ dai->id);
+ return rc;
+ }
+ break;
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ q6afe_tdm_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].tdm);
+ break;
+ case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7:
+ q6afe_cdc_dma_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].dma_cfg);
+ break;
+ case USB_RX:
+ q6afe_usb_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].usb_audio);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = q6afe_port_start(dai_data->port[dai->id]);
+ if (rc < 0) {
+ dev_err(dai->dev, "fail to start AFE port %x\n", dai->id);
+ return rc;
+ }
+ dai_data->is_port_started[dai->id] = true;
+
+ return 0;
+}
+
+static int q6slim_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id];
+ int i;
+
+ if (dai->id & 0x1) {
+ /* TX */
+ if (!tx_slot) {
+ pr_err("%s: tx slot not found\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tx_num; i++)
+ pcfg->slim.ch_mapping[i] = tx_slot[i];
+
+ pcfg->slim.num_channels = tx_num;
+
+
+ } else {
+ if (!rx_slot) {
+ pr_err("%s: rx slot not found\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++)
+ pcfg->slim.ch_mapping[i] = rx_slot[i];
+
+ pcfg->slim.num_channels = rx_num;
+
+ }
+
+ return 0;
+}
+
+static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_port *port = dai_data->port[dai->id];
+
+ switch (clk_id) {
+ case LPAIF_DIG_CLK:
+ return q6afe_port_set_sysclk(port, clk_id, 0, 5, freq, dir);
+ case LPAIF_BIT_CLK:
+ case LPAIF_OSR_CLK:
+ return q6afe_port_set_sysclk(port, clk_id,
+ Q6AFE_LPASS_CLK_SRC_INTERNAL,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ freq, dir);
+ case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR:
+ case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1:
+ case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK:
+ return q6afe_port_set_sysclk(port, clk_id,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ freq, dir);
+ case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT:
+ return q6afe_port_set_sysclk(port, clk_id,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ freq, dir);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route q6afe_dapm_routes[] = {
+ {"HDMI Playback", NULL, "HDMI_RX"},
+ {"DISPLAY_PORT_RX_0 Playback", NULL, "DISPLAY_PORT_RX"},
+ {"Slimbus Playback", NULL, "SLIMBUS_0_RX"},
+ {"Slimbus1 Playback", NULL, "SLIMBUS_1_RX"},
+ {"Slimbus2 Playback", NULL, "SLIMBUS_2_RX"},
+ {"Slimbus3 Playback", NULL, "SLIMBUS_3_RX"},
+ {"Slimbus4 Playback", NULL, "SLIMBUS_4_RX"},
+ {"Slimbus5 Playback", NULL, "SLIMBUS_5_RX"},
+ {"Slimbus6 Playback", NULL, "SLIMBUS_6_RX"},
+
+ {"SLIMBUS_0_TX", NULL, "Slimbus Capture"},
+ {"SLIMBUS_1_TX", NULL, "Slimbus1 Capture"},
+ {"SLIMBUS_2_TX", NULL, "Slimbus2 Capture"},
+ {"SLIMBUS_3_TX", NULL, "Slimbus3 Capture"},
+ {"SLIMBUS_4_TX", NULL, "Slimbus4 Capture"},
+ {"SLIMBUS_5_TX", NULL, "Slimbus5 Capture"},
+ {"SLIMBUS_6_TX", NULL, "Slimbus6 Capture"},
+
+ {"Primary MI2S Playback", NULL, "PRI_MI2S_RX"},
+ {"Secondary MI2S Playback", NULL, "SEC_MI2S_RX"},
+ {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"},
+ {"Quaternary MI2S Playback", NULL, "QUAT_MI2S_RX"},
+ {"Quinary MI2S Playback", NULL, "QUIN_MI2S_RX"},
+
+ {"Primary TDM0 Playback", NULL, "PRIMARY_TDM_RX_0"},
+ {"Primary TDM1 Playback", NULL, "PRIMARY_TDM_RX_1"},
+ {"Primary TDM2 Playback", NULL, "PRIMARY_TDM_RX_2"},
+ {"Primary TDM3 Playback", NULL, "PRIMARY_TDM_RX_3"},
+ {"Primary TDM4 Playback", NULL, "PRIMARY_TDM_RX_4"},
+ {"Primary TDM5 Playback", NULL, "PRIMARY_TDM_RX_5"},
+ {"Primary TDM6 Playback", NULL, "PRIMARY_TDM_RX_6"},
+ {"Primary TDM7 Playback", NULL, "PRIMARY_TDM_RX_7"},
+
+ {"Secondary TDM0 Playback", NULL, "SEC_TDM_RX_0"},
+ {"Secondary TDM1 Playback", NULL, "SEC_TDM_RX_1"},
+ {"Secondary TDM2 Playback", NULL, "SEC_TDM_RX_2"},
+ {"Secondary TDM3 Playback", NULL, "SEC_TDM_RX_3"},
+ {"Secondary TDM4 Playback", NULL, "SEC_TDM_RX_4"},
+ {"Secondary TDM5 Playback", NULL, "SEC_TDM_RX_5"},
+ {"Secondary TDM6 Playback", NULL, "SEC_TDM_RX_6"},
+ {"Secondary TDM7 Playback", NULL, "SEC_TDM_RX_7"},
+
+ {"Tertiary TDM0 Playback", NULL, "TERT_TDM_RX_0"},
+ {"Tertiary TDM1 Playback", NULL, "TERT_TDM_RX_1"},
+ {"Tertiary TDM2 Playback", NULL, "TERT_TDM_RX_2"},
+ {"Tertiary TDM3 Playback", NULL, "TERT_TDM_RX_3"},
+ {"Tertiary TDM4 Playback", NULL, "TERT_TDM_RX_4"},
+ {"Tertiary TDM5 Playback", NULL, "TERT_TDM_RX_5"},
+ {"Tertiary TDM6 Playback", NULL, "TERT_TDM_RX_6"},
+ {"Tertiary TDM7 Playback", NULL, "TERT_TDM_RX_7"},
+
+ {"Quaternary TDM0 Playback", NULL, "QUAT_TDM_RX_0"},
+ {"Quaternary TDM1 Playback", NULL, "QUAT_TDM_RX_1"},
+ {"Quaternary TDM2 Playback", NULL, "QUAT_TDM_RX_2"},
+ {"Quaternary TDM3 Playback", NULL, "QUAT_TDM_RX_3"},
+ {"Quaternary TDM4 Playback", NULL, "QUAT_TDM_RX_4"},
+ {"Quaternary TDM5 Playback", NULL, "QUAT_TDM_RX_5"},
+ {"Quaternary TDM6 Playback", NULL, "QUAT_TDM_RX_6"},
+ {"Quaternary TDM7 Playback", NULL, "QUAT_TDM_RX_7"},
+
+ {"Quinary TDM0 Playback", NULL, "QUIN_TDM_RX_0"},
+ {"Quinary TDM1 Playback", NULL, "QUIN_TDM_RX_1"},
+ {"Quinary TDM2 Playback", NULL, "QUIN_TDM_RX_2"},
+ {"Quinary TDM3 Playback", NULL, "QUIN_TDM_RX_3"},
+ {"Quinary TDM4 Playback", NULL, "QUIN_TDM_RX_4"},
+ {"Quinary TDM5 Playback", NULL, "QUIN_TDM_RX_5"},
+ {"Quinary TDM6 Playback", NULL, "QUIN_TDM_RX_6"},
+ {"Quinary TDM7 Playback", NULL, "QUIN_TDM_RX_7"},
+
+ {"PRIMARY_TDM_TX_0", NULL, "Primary TDM0 Capture"},
+ {"PRIMARY_TDM_TX_1", NULL, "Primary TDM1 Capture"},
+ {"PRIMARY_TDM_TX_2", NULL, "Primary TDM2 Capture"},
+ {"PRIMARY_TDM_TX_3", NULL, "Primary TDM3 Capture"},
+ {"PRIMARY_TDM_TX_4", NULL, "Primary TDM4 Capture"},
+ {"PRIMARY_TDM_TX_5", NULL, "Primary TDM5 Capture"},
+ {"PRIMARY_TDM_TX_6", NULL, "Primary TDM6 Capture"},
+ {"PRIMARY_TDM_TX_7", NULL, "Primary TDM7 Capture"},
+
+ {"SEC_TDM_TX_0", NULL, "Secondary TDM0 Capture"},
+ {"SEC_TDM_TX_1", NULL, "Secondary TDM1 Capture"},
+ {"SEC_TDM_TX_2", NULL, "Secondary TDM2 Capture"},
+ {"SEC_TDM_TX_3", NULL, "Secondary TDM3 Capture"},
+ {"SEC_TDM_TX_4", NULL, "Secondary TDM4 Capture"},
+ {"SEC_TDM_TX_5", NULL, "Secondary TDM5 Capture"},
+ {"SEC_TDM_TX_6", NULL, "Secondary TDM6 Capture"},
+ {"SEC_TDM_TX_7", NULL, "Secondary TDM7 Capture"},
+
+ {"TERT_TDM_TX_0", NULL, "Tertiary TDM0 Capture"},
+ {"TERT_TDM_TX_1", NULL, "Tertiary TDM1 Capture"},
+ {"TERT_TDM_TX_2", NULL, "Tertiary TDM2 Capture"},
+ {"TERT_TDM_TX_3", NULL, "Tertiary TDM3 Capture"},
+ {"TERT_TDM_TX_4", NULL, "Tertiary TDM4 Capture"},
+ {"TERT_TDM_TX_5", NULL, "Tertiary TDM5 Capture"},
+ {"TERT_TDM_TX_6", NULL, "Tertiary TDM6 Capture"},
+ {"TERT_TDM_TX_7", NULL, "Tertiary TDM7 Capture"},
+
+ {"QUAT_TDM_TX_0", NULL, "Quaternary TDM0 Capture"},
+ {"QUAT_TDM_TX_1", NULL, "Quaternary TDM1 Capture"},
+ {"QUAT_TDM_TX_2", NULL, "Quaternary TDM2 Capture"},
+ {"QUAT_TDM_TX_3", NULL, "Quaternary TDM3 Capture"},
+ {"QUAT_TDM_TX_4", NULL, "Quaternary TDM4 Capture"},
+ {"QUAT_TDM_TX_5", NULL, "Quaternary TDM5 Capture"},
+ {"QUAT_TDM_TX_6", NULL, "Quaternary TDM6 Capture"},
+ {"QUAT_TDM_TX_7", NULL, "Quaternary TDM7 Capture"},
+
+ {"QUIN_TDM_TX_0", NULL, "Quinary TDM0 Capture"},
+ {"QUIN_TDM_TX_1", NULL, "Quinary TDM1 Capture"},
+ {"QUIN_TDM_TX_2", NULL, "Quinary TDM2 Capture"},
+ {"QUIN_TDM_TX_3", NULL, "Quinary TDM3 Capture"},
+ {"QUIN_TDM_TX_4", NULL, "Quinary TDM4 Capture"},
+ {"QUIN_TDM_TX_5", NULL, "Quinary TDM5 Capture"},
+ {"QUIN_TDM_TX_6", NULL, "Quinary TDM6 Capture"},
+ {"QUIN_TDM_TX_7", NULL, "Quinary TDM7 Capture"},
+
+ {"TERT_MI2S_TX", NULL, "Tertiary MI2S Capture"},
+ {"PRI_MI2S_TX", NULL, "Primary MI2S Capture"},
+ {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"},
+ {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"},
+ {"QUIN_MI2S_TX", NULL, "Quinary MI2S Capture"},
+
+ {"WSA_CODEC_DMA_RX_0 Playback", NULL, "WSA_CODEC_DMA_RX_0"},
+ {"WSA_CODEC_DMA_TX_0", NULL, "WSA_CODEC_DMA_TX_0 Capture"},
+ {"WSA_CODEC_DMA_RX_1 Playback", NULL, "WSA_CODEC_DMA_RX_1"},
+ {"WSA_CODEC_DMA_TX_1", NULL, "WSA_CODEC_DMA_TX_1 Capture"},
+ {"WSA_CODEC_DMA_TX_2", NULL, "WSA_CODEC_DMA_TX_2 Capture"},
+ {"VA_CODEC_DMA_TX_0", NULL, "VA_CODEC_DMA_TX_0 Capture"},
+ {"VA_CODEC_DMA_TX_1", NULL, "VA_CODEC_DMA_TX_1 Capture"},
+ {"VA_CODEC_DMA_TX_2", NULL, "VA_CODEC_DMA_TX_2 Capture"},
+ {"RX_CODEC_DMA_RX_0 Playback", NULL, "RX_CODEC_DMA_RX_0"},
+ {"TX_CODEC_DMA_TX_0", NULL, "TX_CODEC_DMA_TX_0 Capture"},
+ {"RX_CODEC_DMA_RX_1 Playback", NULL, "RX_CODEC_DMA_RX_1"},
+ {"TX_CODEC_DMA_TX_1", NULL, "TX_CODEC_DMA_TX_1 Capture"},
+ {"RX_CODEC_DMA_RX_2 Playback", NULL, "RX_CODEC_DMA_RX_2"},
+ {"TX_CODEC_DMA_TX_2", NULL, "TX_CODEC_DMA_TX_2 Capture"},
+ {"RX_CODEC_DMA_RX_3 Playback", NULL, "RX_CODEC_DMA_RX_3"},
+ {"TX_CODEC_DMA_TX_3", NULL, "TX_CODEC_DMA_TX_3 Capture"},
+ {"RX_CODEC_DMA_RX_4 Playback", NULL, "RX_CODEC_DMA_RX_4"},
+ {"TX_CODEC_DMA_TX_4", NULL, "TX_CODEC_DMA_TX_4 Capture"},
+ {"RX_CODEC_DMA_RX_5 Playback", NULL, "RX_CODEC_DMA_RX_5"},
+ {"TX_CODEC_DMA_TX_5", NULL, "TX_CODEC_DMA_TX_5 Capture"},
+ {"RX_CODEC_DMA_RX_6 Playback", NULL, "RX_CODEC_DMA_RX_6"},
+ {"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"},
+
+ /* USB playback AFE port receives data for playback, hence use the RX port */
+ {"USB Playback", NULL, "USB_RX"},
+};
+
+static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6afe_port *port;
+
+ port = q6afe_port_get_from_id(dai->dev, dai->id);
+ if (IS_ERR(port)) {
+ dev_err(dai->dev, "Unable to get afe port\n");
+ return -EINVAL;
+ }
+ dai_data->port[dai->id] = port;
+
+ return 0;
+}
+
+static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ q6afe_port_put(dai_data->port[dai->id]);
+ dai_data->port[dai->id] = NULL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops q6afe_usb_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .prepare = q6afe_dai_prepare,
+ .hw_params = q6afe_usb_hw_params,
+ /*
+ * Shutdown callback required to stop the USB AFE port, which is enabled
+ * by the prepare() stage. This stops the audio traffic on the USB AFE
+ * port on the Q6DSP.
+ */
+ .shutdown = q6afe_dai_shutdown,
+ /*
+ * Startup callback not needed, as AFE port start command passes the PCM
+ * parameters within the AFE command, which is provided by the PCM core
+ * during the prepare() stage.
+ */
+};
+
+static const struct snd_soc_dai_ops q6hdmi_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .prepare = q6afe_dai_prepare,
+ .hw_params = q6hdmi_hw_params,
+ .shutdown = q6afe_dai_shutdown,
+};
+
+static const struct snd_soc_dai_ops q6i2s_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .prepare = q6afe_dai_prepare,
+ .hw_params = q6i2s_hw_params,
+ .set_fmt = q6i2s_set_fmt,
+ .shutdown = q6afe_dai_shutdown,
+ .set_sysclk = q6afe_mi2s_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops q6slim_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .prepare = q6afe_dai_prepare,
+ .hw_params = q6slim_hw_params,
+ .shutdown = q6afe_dai_shutdown,
+ .set_channel_map = q6slim_set_channel_map,
+};
+
+static const struct snd_soc_dai_ops q6tdm_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .prepare = q6afe_dai_prepare,
+ .shutdown = q6afe_dai_shutdown,
+ .set_sysclk = q6afe_mi2s_set_sysclk,
+ .set_tdm_slot = q6tdm_set_tdm_slot,
+ .set_channel_map = q6tdm_set_channel_map,
+ .hw_params = q6tdm_hw_params,
+};
+
+static const struct snd_soc_dai_ops q6dma_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ .prepare = q6afe_dai_prepare,
+ .shutdown = q6afe_dai_shutdown,
+ .set_sysclk = q6afe_mi2s_set_sysclk,
+ .set_channel_map = q6dma_set_channel_map,
+ .hw_params = q6dma_hw_params,
+};
+
+static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("HDMI_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_0_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_1_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_2_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_3_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_4_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_5_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_6_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_MI2S_RX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_TX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_RX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_TX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_RX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_TX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_RX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_TX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_RX_SD1",
+ "Secondary MI2S Playback SD1",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_MI2S_RX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_MI2S_TX", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_RX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_TX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_0", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_1", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_2", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_3", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_4", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_5", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_6", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL,
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT_RX", "NULL", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_0", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_0", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_1", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_1", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_2", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_0", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_1", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_2", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_0", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_0", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_1", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_1", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_2", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_2", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_3", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_3", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_4", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_4", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_5", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_5", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_6", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_7", "NULL",
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_component_driver q6afe_dai_component = {
+ .name = "q6afe-dai-component",
+ .dapm_widgets = q6afe_dai_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(q6afe_dai_widgets),
+ .dapm_routes = q6afe_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(q6afe_dapm_routes),
+ .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name,
+
+};
+
+static void of_q6afe_parse_dai_data(struct device *dev,
+ struct q6afe_dai_data *data)
+{
+ struct device_node *node;
+ int ret;
+
+ for_each_child_of_node(dev->of_node, node) {
+ unsigned int lines[Q6AFE_MAX_MI2S_LINES];
+ struct q6afe_dai_priv_data *priv;
+ int id, i, num_lines;
+
+ ret = of_property_read_u32(node, "reg", &id);
+ if (ret || id < 0 || id >= AFE_PORT_MAX) {
+ dev_err(dev, "valid dai id not found:%d\n", ret);
+ continue;
+ }
+
+ switch (id) {
+ /* MI2S specific properties */
+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
+ priv = &data->priv[id];
+ ret = of_property_read_variable_u32_array(node,
+ "qcom,sd-lines",
+ lines, 0,
+ Q6AFE_MAX_MI2S_LINES);
+ if (ret < 0)
+ num_lines = 0;
+ else
+ num_lines = ret;
+
+ priv->sd_line_mask = 0;
+
+ for (i = 0; i < num_lines; i++)
+ priv->sd_line_mask |= BIT(lines[i]);
+
+ break;
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ priv = &data->priv[id];
+ ret = of_property_read_u32(node, "qcom,tdm-sync-mode",
+ &priv->sync_mode);
+ if (ret) {
+ dev_err(dev, "No Sync mode from DT\n");
+ break;
+ }
+ ret = of_property_read_u32(node, "qcom,tdm-sync-src",
+ &priv->sync_src);
+ if (ret) {
+ dev_err(dev, "No Sync Src from DT\n");
+ break;
+ }
+ ret = of_property_read_u32(node, "qcom,tdm-data-out",
+ &priv->data_out_enable);
+ if (ret) {
+ dev_err(dev, "No Data out enable from DT\n");
+ break;
+ }
+ ret = of_property_read_u32(node, "qcom,tdm-invert-sync",
+ &priv->invert_sync);
+ if (ret) {
+ dev_err(dev, "No Invert sync from DT\n");
+ break;
+ }
+ ret = of_property_read_u32(node, "qcom,tdm-data-delay",
+ &priv->data_delay);
+ if (ret) {
+ dev_err(dev, "No Data Delay from DT\n");
+ break;
+ }
+ ret = of_property_read_u32(node, "qcom,tdm-data-align",
+ &priv->data_align);
+ if (ret) {
+ dev_err(dev, "No Data align from DT\n");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int q6afe_dai_dev_probe(struct platform_device *pdev)
+{
+ struct q6dsp_audio_port_dai_driver_config cfg;
+ struct snd_soc_dai_driver *dais;
+ struct q6afe_dai_data *dai_data;
+ struct device *dev = &pdev->dev;
+ int num_dais;
+
+ dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
+ if (!dai_data)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, dai_data);
+ of_q6afe_parse_dai_data(dev, dai_data);
+
+ cfg.q6hdmi_ops = &q6hdmi_ops;
+ cfg.q6slim_ops = &q6slim_ops;
+ cfg.q6i2s_ops = &q6i2s_ops;
+ cfg.q6tdm_ops = &q6tdm_ops;
+ cfg.q6dma_ops = &q6dma_ops;
+ cfg.q6usb_ops = &q6afe_usb_ops;
+ dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
+
+ return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6afe_dai_device_id[] = {
+ { .compatible = "qcom,q6afe-dais" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6afe_dai_device_id);
+#endif
+
+static struct platform_driver q6afe_dai_platform_driver = {
+ .driver = {
+ .name = "q6afe-dai",
+ .of_match_table = of_match_ptr(q6afe_dai_device_id),
+ },
+ .probe = q6afe_dai_dev_probe,
+};
+module_platform_driver(q6afe_dai_platform_driver);
+
+MODULE_DESCRIPTION("Q6 Audio Frontend dai driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
new file mode 100644
index 000000000000..980851a12976
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -0,0 +1,1941 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/soc/qcom/apr.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "q6dsp-errno.h"
+#include "q6core.h"
+#include "q6afe.h"
+
+/* AFE CMDs */
+#define AFE_PORT_CMD_DEVICE_START 0x000100E5
+#define AFE_PORT_CMD_DEVICE_STOP 0x000100E6
+#define AFE_PORT_CMD_SET_PARAM_V2 0x000100EF
+#define AFE_SVC_CMD_SET_PARAM 0x000100f3
+#define AFE_PORT_CMDRSP_GET_PARAM_V2 0x00010106
+#define AFE_PARAM_ID_HDMI_CONFIG 0x00010210
+#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C
+#define AFE_MODULE_TDM 0x0001028A
+
+#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235
+#define AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS 0x000102A5
+#define AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT 0x000102AA
+
+#define AFE_PARAM_ID_LPAIF_CLK_CONFIG 0x00010238
+#define AFE_PARAM_ID_INT_DIGITAL_CDC_CLK_CONFIG 0x00010239
+
+#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212
+#define AFE_PARAM_ID_I2S_CONFIG 0x0001020D
+#define AFE_PARAM_ID_TDM_CONFIG 0x0001029D
+#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297
+#define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8
+#define AFE_PARAM_ID_USB_AUDIO_CONFIG 0x000102A4
+#define AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f4
+#define AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f5
+#define AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST 0x000100f6
+
+/* I2S config specific */
+#define AFE_API_VERSION_I2S_CONFIG 0x1
+#define AFE_PORT_I2S_SD0 0x1
+#define AFE_PORT_I2S_SD1 0x2
+#define AFE_PORT_I2S_SD2 0x3
+#define AFE_PORT_I2S_SD3 0x4
+#define AFE_PORT_I2S_SD0_MASK BIT(0x0)
+#define AFE_PORT_I2S_SD1_MASK BIT(0x1)
+#define AFE_PORT_I2S_SD2_MASK BIT(0x2)
+#define AFE_PORT_I2S_SD3_MASK BIT(0x3)
+#define AFE_PORT_I2S_SD0_1_MASK GENMASK(1, 0)
+#define AFE_PORT_I2S_SD2_3_MASK GENMASK(3, 2)
+#define AFE_PORT_I2S_SD0_1_2_MASK GENMASK(2, 0)
+#define AFE_PORT_I2S_SD0_1_2_3_MASK GENMASK(3, 0)
+#define AFE_PORT_I2S_QUAD01 0x5
+#define AFE_PORT_I2S_QUAD23 0x6
+#define AFE_PORT_I2S_6CHS 0x7
+#define AFE_PORT_I2S_8CHS 0x8
+#define AFE_PORT_I2S_MONO 0x0
+#define AFE_PORT_I2S_STEREO 0x1
+#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL 0x0
+#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL 0x1
+#define AFE_LINEAR_PCM_DATA 0x0
+
+#define AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG 0x1
+
+/* Port IDs */
+#define AFE_API_VERSION_HDMI_CONFIG 0x1
+#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E
+#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020
+
+/* USB AFE port */
+#define AFE_PORT_ID_USB_RX 0x7000
+
+#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1
+/* Clock set API version */
+#define AFE_API_VERSION_CLOCK_SET 1
+#define Q6AFE_LPASS_CLK_CONFIG_API_VERSION 0x1
+#define AFE_MODULE_CLOCK_SET 0x0001028F
+#define AFE_PARAM_ID_CLOCK_SET 0x00010290
+
+/* SLIMbus Rx port on channel 0. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX 0x4000
+/* SLIMbus Tx port on channel 0. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX 0x4001
+/* SLIMbus Rx port on channel 1. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX 0x4002
+/* SLIMbus Tx port on channel 1. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX 0x4003
+/* SLIMbus Rx port on channel 2. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX 0x4004
+/* SLIMbus Tx port on channel 2. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX 0x4005
+/* SLIMbus Rx port on channel 3. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX 0x4006
+/* SLIMbus Tx port on channel 3. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX 0x4007
+/* SLIMbus Rx port on channel 4. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008
+/* SLIMbus Tx port on channel 4. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009
+/* SLIMbus Rx port on channel 5. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a
+/* SLIMbus Tx port on channel 5. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX 0x400b
+/* SLIMbus Rx port on channel 6. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c
+/* SLIMbus Tx port on channel 6. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d
+#define AFE_PORT_ID_PRIMARY_MI2S_RX 0x1000
+#define AFE_PORT_ID_PRIMARY_MI2S_TX 0x1001
+#define AFE_PORT_ID_SECONDARY_MI2S_RX 0x1002
+#define AFE_PORT_ID_SECONDARY_MI2S_TX 0x1003
+#define AFE_PORT_ID_TERTIARY_MI2S_RX 0x1004
+#define AFE_PORT_ID_TERTIARY_MI2S_TX 0x1005
+#define AFE_PORT_ID_QUATERNARY_MI2S_RX 0x1006
+#define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007
+#define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016
+#define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017
+
+/* Start of the range of port IDs for TDM devices. */
+#define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000
+
+/* End of the range of port IDs for TDM devices. */
+#define AFE_PORT_ID_TDM_PORT_RANGE_END \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START+0x50-1)
+
+/* Size of the range of port IDs for TDM ports. */
+#define AFE_PORT_ID_TDM_PORT_RANGE_SIZE \
+ (AFE_PORT_ID_TDM_PORT_RANGE_END - \
+ AFE_PORT_ID_TDM_PORT_RANGE_START+1)
+
+#define AFE_PORT_ID_PRIMARY_TDM_RX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x00)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_1 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_2 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_3 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_4 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_5 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_6 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_7 \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_PRIMARY_TDM_TX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x01)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_1 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_2 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_3 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_4 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_5 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_6 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_7 \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_SECONDARY_TDM_RX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x10)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_1 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_2 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_3 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_4 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_5 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_6 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_7 \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_SECONDARY_TDM_TX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x11)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_1 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_2 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_3 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_4 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_5 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_6 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_7 \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_TERTIARY_TDM_RX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x20)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_1 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_2 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_3 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_4 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_5 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_6 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_7 \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_TERTIARY_TDM_TX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x21)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_1 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_2 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_3 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_4 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_5 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_6 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_7 \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_QUATERNARY_TDM_RX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x30)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_1 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_2 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_3 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_4 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_5 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_6 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_7 \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_QUATERNARY_TDM_TX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x31)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_1 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_2 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_3 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_4 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_5 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_6 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_7 \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_QUINARY_TDM_RX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x40)
+#define AFE_PORT_ID_QUINARY_TDM_RX_1 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_QUINARY_TDM_RX_2 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_QUINARY_TDM_RX_3 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_QUINARY_TDM_RX_4 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_QUINARY_TDM_RX_5 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_QUINARY_TDM_RX_6 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_QUINARY_TDM_RX_7 \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_QUINARY_TDM_TX \
+ (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x41)
+#define AFE_PORT_ID_QUINARY_TDM_TX_1 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_QUINARY_TDM_TX_2 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_QUINARY_TDM_TX_3 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_QUINARY_TDM_TX_4 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_QUINARY_TDM_TX_5 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_QUINARY_TDM_TX_6 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_QUINARY_TDM_TX_7 \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E)
+
+/* AFE WSA Codec DMA Rx port 0 */
+#define AFE_PORT_ID_WSA_CODEC_DMA_RX_0 0xB000
+/* AFE WSA Codec DMA Tx port 0 */
+#define AFE_PORT_ID_WSA_CODEC_DMA_TX_0 0xB001
+/* AFE WSA Codec DMA Rx port 1 */
+#define AFE_PORT_ID_WSA_CODEC_DMA_RX_1 0xB002
+/* AFE WSA Codec DMA Tx port 1 */
+#define AFE_PORT_ID_WSA_CODEC_DMA_TX_1 0xB003
+/* AFE WSA Codec DMA Tx port 2 */
+#define AFE_PORT_ID_WSA_CODEC_DMA_TX_2 0xB005
+/* AFE VA Codec DMA Tx port 0 */
+#define AFE_PORT_ID_VA_CODEC_DMA_TX_0 0xB021
+/* AFE VA Codec DMA Tx port 1 */
+#define AFE_PORT_ID_VA_CODEC_DMA_TX_1 0xB023
+/* AFE VA Codec DMA Tx port 2 */
+#define AFE_PORT_ID_VA_CODEC_DMA_TX_2 0xB025
+/* AFE Rx Codec DMA Rx port 0 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_0 0xB030
+/* AFE Tx Codec DMA Tx port 0 */
+#define AFE_PORT_ID_TX_CODEC_DMA_TX_0 0xB031
+/* AFE Rx Codec DMA Rx port 1 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_1 0xB032
+/* AFE Tx Codec DMA Tx port 1 */
+#define AFE_PORT_ID_TX_CODEC_DMA_TX_1 0xB033
+/* AFE Rx Codec DMA Rx port 2 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_2 0xB034
+/* AFE Tx Codec DMA Tx port 2 */
+#define AFE_PORT_ID_TX_CODEC_DMA_TX_2 0xB035
+/* AFE Rx Codec DMA Rx port 3 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_3 0xB036
+/* AFE Tx Codec DMA Tx port 3 */
+#define AFE_PORT_ID_TX_CODEC_DMA_TX_3 0xB037
+/* AFE Rx Codec DMA Rx port 4 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_4 0xB038
+/* AFE Tx Codec DMA Tx port 4 */
+#define AFE_PORT_ID_TX_CODEC_DMA_TX_4 0xB039
+/* AFE Rx Codec DMA Rx port 5 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_5 0xB03A
+/* AFE Tx Codec DMA Tx port 5 */
+#define AFE_PORT_ID_TX_CODEC_DMA_TX_5 0xB03B
+/* AFE Rx Codec DMA Rx port 6 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_6 0xB03C
+/* AFE Rx Codec DMA Rx port 7 */
+#define AFE_PORT_ID_RX_CODEC_DMA_RX_7 0xB03E
+
+#define Q6AFE_LPASS_MODE_CLK1_VALID 1
+#define Q6AFE_LPASS_MODE_CLK2_VALID 2
+#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1
+#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
+#define AFE_API_VERSION_TDM_CONFIG 1
+#define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1
+#define AFE_API_VERSION_CODEC_DMA_CONFIG 1
+
+#define TIMEOUT_MS 3000
+#define AFE_CMD_RESP_AVAIL 0
+#define AFE_CMD_RESP_NONE 1
+#define AFE_CLK_TOKEN 1024
+
+struct q6afe {
+ struct apr_device *apr;
+ struct device *dev;
+ struct q6core_svc_api_info ainfo;
+ struct mutex lock;
+ struct aprv2_ibasic_rsp_result_t result;
+ wait_queue_head_t wait;
+ struct list_head port_list;
+ spinlock_t port_list_lock;
+};
+
+struct afe_port_cmd_device_start {
+ u16 port_id;
+ u16 reserved;
+} __packed;
+
+struct afe_port_cmd_device_stop {
+ u16 port_id;
+ u16 reserved;
+/* Reserved for 32-bit alignment. This field must be set to 0.*/
+} __packed;
+
+struct afe_port_param_data_v2 {
+ u32 module_id;
+ u32 param_id;
+ u16 param_size;
+ u16 reserved;
+} __packed;
+
+struct afe_svc_cmd_set_param {
+ uint32_t payload_size;
+ uint32_t payload_address_lsw;
+ uint32_t payload_address_msw;
+ uint32_t mem_map_handle;
+} __packed;
+
+struct afe_port_cmd_set_param_v2 {
+ u16 port_id;
+ u16 payload_size;
+ u32 payload_address_lsw;
+ u32 payload_address_msw;
+ u32 mem_map_handle;
+} __packed;
+
+struct afe_param_id_hdmi_multi_chan_audio_cfg {
+ u32 hdmi_cfg_minor_version;
+ u16 datatype;
+ u16 channel_allocation;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 reserved;
+} __packed;
+
+struct afe_param_id_slimbus_cfg {
+ u32 sb_cfg_minor_version;
+/* Minor version used for tracking the version of the SLIMBUS
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_SLIMBUS_CONFIG
+ */
+
+ u16 slimbus_dev_id;
+/* SLIMbus hardware device ID, which is required to handle
+ * multiple SLIMbus hardware blocks.
+ * Supported values: - #AFE_SLIMBUS_DEVICE_1 - #AFE_SLIMBUS_DEVICE_2
+ */
+ u16 bit_width;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+ u16 data_format;
+/* Data format supported by the SLIMbus hardware. The default is
+ * 0 (#AFE_SB_DATA_FORMAT_NOT_INDICATED), which indicates the
+ * hardware does not perform any format conversions before the data
+ * transfer.
+ */
+ u16 num_channels;
+/* Number of channels.
+ * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT
+ */
+ u8 shared_ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+/* Mapping of shared channel IDs (128 to 255) to which the
+ * master port is to be connected.
+ * Shared_channel_mapping[i] represents the shared channel assigned
+ * for audio channel i in multichannel audio data.
+ */
+ u32 sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ * - #AFE_PORT_SAMPLE_RATE_96K
+ * - #AFE_PORT_SAMPLE_RATE_192K
+ */
+} __packed;
+
+struct afe_clk_cfg {
+ u32 i2s_cfg_minor_version;
+ u32 clk_val1;
+ u32 clk_val2;
+ u16 clk_src;
+ u16 clk_root;
+ u16 clk_set_mode;
+ u16 reserved;
+} __packed;
+
+struct afe_digital_clk_cfg {
+ u32 i2s_cfg_minor_version;
+ u32 clk_val;
+ u16 clk_root;
+ u16 reserved;
+} __packed;
+
+struct afe_param_id_i2s_cfg {
+ u32 i2s_cfg_minor_version;
+ u16 bit_width;
+ u16 channel_mode;
+ u16 mono_stereo;
+ u16 ws_src;
+ u32 sample_rate;
+ u16 data_format;
+ u16 reserved;
+} __packed;
+
+struct afe_param_id_tdm_cfg {
+ u32 tdm_cfg_minor_version;
+ u32 num_channels;
+ u32 sample_rate;
+ u32 bit_width;
+ u16 data_format;
+ u16 sync_mode;
+ u16 sync_src;
+ u16 nslots_per_frame;
+ u16 ctrl_data_out_enable;
+ u16 ctrl_invert_sync_pulse;
+ u16 ctrl_sync_data_delay;
+ u16 slot_width;
+ u32 slot_mask;
+} __packed;
+
+struct afe_param_id_cdc_dma_cfg {
+ u32 cdc_dma_cfg_minor_version;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 data_format;
+ u16 num_channels;
+ u16 active_channels_mask;
+} __packed;
+
+struct afe_param_id_usb_cfg {
+/* Minor version used for tracking USB audio device configuration.
+ * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ */
+ u32 cfg_minor_version;
+/* Sampling rate of the port.
+ * Supported values:
+ * - AFE_PORT_SAMPLE_RATE_8K
+ * - AFE_PORT_SAMPLE_RATE_11025
+ * - AFE_PORT_SAMPLE_RATE_12K
+ * - AFE_PORT_SAMPLE_RATE_16K
+ * - AFE_PORT_SAMPLE_RATE_22050
+ * - AFE_PORT_SAMPLE_RATE_24K
+ * - AFE_PORT_SAMPLE_RATE_32K
+ * - AFE_PORT_SAMPLE_RATE_44P1K
+ * - AFE_PORT_SAMPLE_RATE_48K
+ * - AFE_PORT_SAMPLE_RATE_96K
+ * - AFE_PORT_SAMPLE_RATE_192K
+ */
+ u32 sample_rate;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+ u16 bit_width;
+/* Number of channels.
+ * Supported values: 1 and 2
+ */
+ u16 num_channels;
+/* Data format supported by the USB. The supported value is
+ * 0 (#AFE_USB_AUDIO_DATA_FORMAT_LINEAR_PCM).
+ */
+ u16 data_format;
+/* this field must be 0 */
+ u16 reserved;
+/* device token of actual end USB audio device */
+ u32 dev_token;
+/* endianness of this interface */
+ u32 endian;
+/* service interval */
+ u32 service_interval;
+} __packed;
+
+/**
+ * struct afe_param_id_usb_audio_dev_params
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @dev_token: device token of actual end USB audio device
+ **/
+struct afe_param_id_usb_audio_dev_params {
+ u32 cfg_minor_version;
+ u32 dev_token;
+} __packed;
+
+/**
+ * struct afe_param_id_usb_audio_dev_lpcm_fmt
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @endian: endianness of this interface
+ **/
+struct afe_param_id_usb_audio_dev_lpcm_fmt {
+ u32 cfg_minor_version;
+ u32 endian;
+} __packed;
+
+#define AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL 0x000102B7
+
+/**
+ * struct afe_param_id_usb_audio_svc_interval
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @svc_interval: service interval
+ **/
+struct afe_param_id_usb_audio_svc_interval {
+ u32 cfg_minor_version;
+ u32 svc_interval;
+} __packed;
+
+union afe_port_config {
+ struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch;
+ struct afe_param_id_slimbus_cfg slim_cfg;
+ struct afe_param_id_i2s_cfg i2s_cfg;
+ struct afe_param_id_tdm_cfg tdm_cfg;
+ struct afe_param_id_cdc_dma_cfg dma_cfg;
+ struct afe_param_id_usb_cfg usb_cfg;
+} __packed;
+
+
+struct afe_clk_set {
+ uint32_t clk_set_minor_version;
+ uint32_t clk_id;
+ uint32_t clk_freq_in_hz;
+ uint16_t clk_attri;
+ uint16_t clk_root;
+ uint32_t enable;
+};
+
+struct afe_param_id_slot_mapping_cfg {
+ u32 minor_version;
+ u16 num_channels;
+ u16 bitwidth;
+ u32 data_align_type;
+ u16 ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+} __packed;
+
+struct q6afe_port {
+ wait_queue_head_t wait;
+ union afe_port_config port_cfg;
+ struct afe_param_id_slot_mapping_cfg *scfg;
+ struct aprv2_ibasic_rsp_result_t result;
+ int token;
+ int id;
+ int cfg_type;
+ struct q6afe *afe;
+ struct kref refcount;
+ struct list_head node;
+};
+
+struct afe_cmd_remote_lpass_core_hw_vote_request {
+ uint32_t hw_block_id;
+ char client_name[8];
+} __packed;
+
+struct afe_cmd_remote_lpass_core_hw_devote_request {
+ uint32_t hw_block_id;
+ uint32_t client_handle;
+} __packed;
+
+
+
+struct afe_port_map {
+ int port_id;
+ int token;
+ int is_rx;
+ int is_dig_pcm;
+};
+
+/*
+ * Mapping between Virtual Port IDs to DSP AFE Port ID
+ * On B Family SoCs DSP Port IDs are consistent across multiple SoCs
+ * on A Family SoCs DSP port IDs are same as virtual Port IDs.
+ */
+
+static struct afe_port_map port_maps[AFE_PORT_MAX] = {
+ [HDMI_RX] = { AFE_PORT_ID_MULTICHAN_HDMI_RX, HDMI_RX, 1, 1},
+ [SLIMBUS_0_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX,
+ SLIMBUS_0_RX, 1, 1},
+ [SLIMBUS_1_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX,
+ SLIMBUS_1_RX, 1, 1},
+ [SLIMBUS_2_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX,
+ SLIMBUS_2_RX, 1, 1},
+ [SLIMBUS_3_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX,
+ SLIMBUS_3_RX, 1, 1},
+ [SLIMBUS_4_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX,
+ SLIMBUS_4_RX, 1, 1},
+ [SLIMBUS_5_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX,
+ SLIMBUS_5_RX, 1, 1},
+ [SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX,
+ SLIMBUS_6_RX, 1, 1},
+ [SLIMBUS_0_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX,
+ SLIMBUS_0_TX, 0, 1},
+ [SLIMBUS_1_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX,
+ SLIMBUS_1_TX, 0, 1},
+ [SLIMBUS_2_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX,
+ SLIMBUS_2_TX, 0, 1},
+ [SLIMBUS_3_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX,
+ SLIMBUS_3_TX, 0, 1},
+ [SLIMBUS_4_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX,
+ SLIMBUS_4_TX, 0, 1},
+ [SLIMBUS_5_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX,
+ SLIMBUS_5_TX, 0, 1},
+ [SLIMBUS_6_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX,
+ SLIMBUS_6_TX, 0, 1},
+ [PRIMARY_MI2S_RX] = { AFE_PORT_ID_PRIMARY_MI2S_RX,
+ PRIMARY_MI2S_RX, 1, 1},
+ [PRIMARY_MI2S_TX] = { AFE_PORT_ID_PRIMARY_MI2S_TX,
+ PRIMARY_MI2S_RX, 0, 1},
+ [SECONDARY_MI2S_RX] = { AFE_PORT_ID_SECONDARY_MI2S_RX,
+ SECONDARY_MI2S_RX, 1, 1},
+ [SECONDARY_MI2S_TX] = { AFE_PORT_ID_SECONDARY_MI2S_TX,
+ SECONDARY_MI2S_TX, 0, 1},
+ [TERTIARY_MI2S_RX] = { AFE_PORT_ID_TERTIARY_MI2S_RX,
+ TERTIARY_MI2S_RX, 1, 1},
+ [TERTIARY_MI2S_TX] = { AFE_PORT_ID_TERTIARY_MI2S_TX,
+ TERTIARY_MI2S_TX, 0, 1},
+ [QUATERNARY_MI2S_RX] = { AFE_PORT_ID_QUATERNARY_MI2S_RX,
+ QUATERNARY_MI2S_RX, 1, 1},
+ [QUATERNARY_MI2S_TX] = { AFE_PORT_ID_QUATERNARY_MI2S_TX,
+ QUATERNARY_MI2S_TX, 0, 1},
+ [QUINARY_MI2S_RX] = { AFE_PORT_ID_QUINARY_MI2S_RX,
+ QUINARY_MI2S_RX, 1, 1},
+ [QUINARY_MI2S_TX] = { AFE_PORT_ID_QUINARY_MI2S_TX,
+ QUINARY_MI2S_TX, 0, 1},
+ [PRIMARY_TDM_RX_0] = { AFE_PORT_ID_PRIMARY_TDM_RX,
+ PRIMARY_TDM_RX_0, 1, 1},
+ [PRIMARY_TDM_TX_0] = { AFE_PORT_ID_PRIMARY_TDM_TX,
+ PRIMARY_TDM_TX_0, 0, 1},
+ [PRIMARY_TDM_RX_1] = { AFE_PORT_ID_PRIMARY_TDM_RX_1,
+ PRIMARY_TDM_RX_1, 1, 1},
+ [PRIMARY_TDM_TX_1] = { AFE_PORT_ID_PRIMARY_TDM_TX_1,
+ PRIMARY_TDM_TX_1, 0, 1},
+ [PRIMARY_TDM_RX_2] = { AFE_PORT_ID_PRIMARY_TDM_RX_2,
+ PRIMARY_TDM_RX_2, 1, 1},
+ [PRIMARY_TDM_TX_2] = { AFE_PORT_ID_PRIMARY_TDM_TX_2,
+ PRIMARY_TDM_TX_2, 0, 1},
+ [PRIMARY_TDM_RX_3] = { AFE_PORT_ID_PRIMARY_TDM_RX_3,
+ PRIMARY_TDM_RX_3, 1, 1},
+ [PRIMARY_TDM_TX_3] = { AFE_PORT_ID_PRIMARY_TDM_TX_3,
+ PRIMARY_TDM_TX_3, 0, 1},
+ [PRIMARY_TDM_RX_4] = { AFE_PORT_ID_PRIMARY_TDM_RX_4,
+ PRIMARY_TDM_RX_4, 1, 1},
+ [PRIMARY_TDM_TX_4] = { AFE_PORT_ID_PRIMARY_TDM_TX_4,
+ PRIMARY_TDM_TX_4, 0, 1},
+ [PRIMARY_TDM_RX_5] = { AFE_PORT_ID_PRIMARY_TDM_RX_5,
+ PRIMARY_TDM_RX_5, 1, 1},
+ [PRIMARY_TDM_TX_5] = { AFE_PORT_ID_PRIMARY_TDM_TX_5,
+ PRIMARY_TDM_TX_5, 0, 1},
+ [PRIMARY_TDM_RX_6] = { AFE_PORT_ID_PRIMARY_TDM_RX_6,
+ PRIMARY_TDM_RX_6, 1, 1},
+ [PRIMARY_TDM_TX_6] = { AFE_PORT_ID_PRIMARY_TDM_TX_6,
+ PRIMARY_TDM_TX_6, 0, 1},
+ [PRIMARY_TDM_RX_7] = { AFE_PORT_ID_PRIMARY_TDM_RX_7,
+ PRIMARY_TDM_RX_7, 1, 1},
+ [PRIMARY_TDM_TX_7] = { AFE_PORT_ID_PRIMARY_TDM_TX_7,
+ PRIMARY_TDM_TX_7, 0, 1},
+ [SECONDARY_TDM_RX_0] = { AFE_PORT_ID_SECONDARY_TDM_RX,
+ SECONDARY_TDM_RX_0, 1, 1},
+ [SECONDARY_TDM_TX_0] = { AFE_PORT_ID_SECONDARY_TDM_TX,
+ SECONDARY_TDM_TX_0, 0, 1},
+ [SECONDARY_TDM_RX_1] = { AFE_PORT_ID_SECONDARY_TDM_RX_1,
+ SECONDARY_TDM_RX_1, 1, 1},
+ [SECONDARY_TDM_TX_1] = { AFE_PORT_ID_SECONDARY_TDM_TX_1,
+ SECONDARY_TDM_TX_1, 0, 1},
+ [SECONDARY_TDM_RX_2] = { AFE_PORT_ID_SECONDARY_TDM_RX_2,
+ SECONDARY_TDM_RX_2, 1, 1},
+ [SECONDARY_TDM_TX_2] = { AFE_PORT_ID_SECONDARY_TDM_TX_2,
+ SECONDARY_TDM_TX_2, 0, 1},
+ [SECONDARY_TDM_RX_3] = { AFE_PORT_ID_SECONDARY_TDM_RX_3,
+ SECONDARY_TDM_RX_3, 1, 1},
+ [SECONDARY_TDM_TX_3] = { AFE_PORT_ID_SECONDARY_TDM_TX_3,
+ SECONDARY_TDM_TX_3, 0, 1},
+ [SECONDARY_TDM_RX_4] = { AFE_PORT_ID_SECONDARY_TDM_RX_4,
+ SECONDARY_TDM_RX_4, 1, 1},
+ [SECONDARY_TDM_TX_4] = { AFE_PORT_ID_SECONDARY_TDM_TX_4,
+ SECONDARY_TDM_TX_4, 0, 1},
+ [SECONDARY_TDM_RX_5] = { AFE_PORT_ID_SECONDARY_TDM_RX_5,
+ SECONDARY_TDM_RX_5, 1, 1},
+ [SECONDARY_TDM_TX_5] = { AFE_PORT_ID_SECONDARY_TDM_TX_5,
+ SECONDARY_TDM_TX_5, 0, 1},
+ [SECONDARY_TDM_RX_6] = { AFE_PORT_ID_SECONDARY_TDM_RX_6,
+ SECONDARY_TDM_RX_6, 1, 1},
+ [SECONDARY_TDM_TX_6] = { AFE_PORT_ID_SECONDARY_TDM_TX_6,
+ SECONDARY_TDM_TX_6, 0, 1},
+ [SECONDARY_TDM_RX_7] = { AFE_PORT_ID_SECONDARY_TDM_RX_7,
+ SECONDARY_TDM_RX_7, 1, 1},
+ [SECONDARY_TDM_TX_7] = { AFE_PORT_ID_SECONDARY_TDM_TX_7,
+ SECONDARY_TDM_TX_7, 0, 1},
+ [TERTIARY_TDM_RX_0] = { AFE_PORT_ID_TERTIARY_TDM_RX,
+ TERTIARY_TDM_RX_0, 1, 1},
+ [TERTIARY_TDM_TX_0] = { AFE_PORT_ID_TERTIARY_TDM_TX,
+ TERTIARY_TDM_TX_0, 0, 1},
+ [TERTIARY_TDM_RX_1] = { AFE_PORT_ID_TERTIARY_TDM_RX_1,
+ TERTIARY_TDM_RX_1, 1, 1},
+ [TERTIARY_TDM_TX_1] = { AFE_PORT_ID_TERTIARY_TDM_TX_1,
+ TERTIARY_TDM_TX_1, 0, 1},
+ [TERTIARY_TDM_RX_2] = { AFE_PORT_ID_TERTIARY_TDM_RX_2,
+ TERTIARY_TDM_RX_2, 1, 1},
+ [TERTIARY_TDM_TX_2] = { AFE_PORT_ID_TERTIARY_TDM_TX_2,
+ TERTIARY_TDM_TX_2, 0, 1},
+ [TERTIARY_TDM_RX_3] = { AFE_PORT_ID_TERTIARY_TDM_RX_3,
+ TERTIARY_TDM_RX_3, 1, 1},
+ [TERTIARY_TDM_TX_3] = { AFE_PORT_ID_TERTIARY_TDM_TX_3,
+ TERTIARY_TDM_TX_3, 0, 1},
+ [TERTIARY_TDM_RX_4] = { AFE_PORT_ID_TERTIARY_TDM_RX_4,
+ TERTIARY_TDM_RX_4, 1, 1},
+ [TERTIARY_TDM_TX_4] = { AFE_PORT_ID_TERTIARY_TDM_TX_4,
+ TERTIARY_TDM_TX_4, 0, 1},
+ [TERTIARY_TDM_RX_5] = { AFE_PORT_ID_TERTIARY_TDM_RX_5,
+ TERTIARY_TDM_RX_5, 1, 1},
+ [TERTIARY_TDM_TX_5] = { AFE_PORT_ID_TERTIARY_TDM_TX_5,
+ TERTIARY_TDM_TX_5, 0, 1},
+ [TERTIARY_TDM_RX_6] = { AFE_PORT_ID_TERTIARY_TDM_RX_6,
+ TERTIARY_TDM_RX_6, 1, 1},
+ [TERTIARY_TDM_TX_6] = { AFE_PORT_ID_TERTIARY_TDM_TX_6,
+ TERTIARY_TDM_TX_6, 0, 1},
+ [TERTIARY_TDM_RX_7] = { AFE_PORT_ID_TERTIARY_TDM_RX_7,
+ TERTIARY_TDM_RX_7, 1, 1},
+ [TERTIARY_TDM_TX_7] = { AFE_PORT_ID_TERTIARY_TDM_TX_7,
+ TERTIARY_TDM_TX_7, 0, 1},
+ [QUATERNARY_TDM_RX_0] = { AFE_PORT_ID_QUATERNARY_TDM_RX,
+ QUATERNARY_TDM_RX_0, 1, 1},
+ [QUATERNARY_TDM_TX_0] = { AFE_PORT_ID_QUATERNARY_TDM_TX,
+ QUATERNARY_TDM_TX_0, 0, 1},
+ [QUATERNARY_TDM_RX_1] = { AFE_PORT_ID_QUATERNARY_TDM_RX_1,
+ QUATERNARY_TDM_RX_1, 1, 1},
+ [QUATERNARY_TDM_TX_1] = { AFE_PORT_ID_QUATERNARY_TDM_TX_1,
+ QUATERNARY_TDM_TX_1, 0, 1},
+ [QUATERNARY_TDM_RX_2] = { AFE_PORT_ID_QUATERNARY_TDM_RX_2,
+ QUATERNARY_TDM_RX_2, 1, 1},
+ [QUATERNARY_TDM_TX_2] = { AFE_PORT_ID_QUATERNARY_TDM_TX_2,
+ QUATERNARY_TDM_TX_2, 0, 1},
+ [QUATERNARY_TDM_RX_3] = { AFE_PORT_ID_QUATERNARY_TDM_RX_3,
+ QUATERNARY_TDM_RX_3, 1, 1},
+ [QUATERNARY_TDM_TX_3] = { AFE_PORT_ID_QUATERNARY_TDM_TX_3,
+ QUATERNARY_TDM_TX_3, 0, 1},
+ [QUATERNARY_TDM_RX_4] = { AFE_PORT_ID_QUATERNARY_TDM_RX_4,
+ QUATERNARY_TDM_RX_4, 1, 1},
+ [QUATERNARY_TDM_TX_4] = { AFE_PORT_ID_QUATERNARY_TDM_TX_4,
+ QUATERNARY_TDM_TX_4, 0, 1},
+ [QUATERNARY_TDM_RX_5] = { AFE_PORT_ID_QUATERNARY_TDM_RX_5,
+ QUATERNARY_TDM_RX_5, 1, 1},
+ [QUATERNARY_TDM_TX_5] = { AFE_PORT_ID_QUATERNARY_TDM_TX_5,
+ QUATERNARY_TDM_TX_5, 0, 1},
+ [QUATERNARY_TDM_RX_6] = { AFE_PORT_ID_QUATERNARY_TDM_RX_6,
+ QUATERNARY_TDM_RX_6, 1, 1},
+ [QUATERNARY_TDM_TX_6] = { AFE_PORT_ID_QUATERNARY_TDM_TX_6,
+ QUATERNARY_TDM_TX_6, 0, 1},
+ [QUATERNARY_TDM_RX_7] = { AFE_PORT_ID_QUATERNARY_TDM_RX_7,
+ QUATERNARY_TDM_RX_7, 1, 1},
+ [QUATERNARY_TDM_TX_7] = { AFE_PORT_ID_QUATERNARY_TDM_TX_7,
+ QUATERNARY_TDM_TX_7, 0, 1},
+ [QUINARY_TDM_RX_0] = { AFE_PORT_ID_QUINARY_TDM_RX,
+ QUINARY_TDM_RX_0, 1, 1},
+ [QUINARY_TDM_TX_0] = { AFE_PORT_ID_QUINARY_TDM_TX,
+ QUINARY_TDM_TX_0, 0, 1},
+ [QUINARY_TDM_RX_1] = { AFE_PORT_ID_QUINARY_TDM_RX_1,
+ QUINARY_TDM_RX_1, 1, 1},
+ [QUINARY_TDM_TX_1] = { AFE_PORT_ID_QUINARY_TDM_TX_1,
+ QUINARY_TDM_TX_1, 0, 1},
+ [QUINARY_TDM_RX_2] = { AFE_PORT_ID_QUINARY_TDM_RX_2,
+ QUINARY_TDM_RX_2, 1, 1},
+ [QUINARY_TDM_TX_2] = { AFE_PORT_ID_QUINARY_TDM_TX_2,
+ QUINARY_TDM_TX_2, 0, 1},
+ [QUINARY_TDM_RX_3] = { AFE_PORT_ID_QUINARY_TDM_RX_3,
+ QUINARY_TDM_RX_3, 1, 1},
+ [QUINARY_TDM_TX_3] = { AFE_PORT_ID_QUINARY_TDM_TX_3,
+ QUINARY_TDM_TX_3, 0, 1},
+ [QUINARY_TDM_RX_4] = { AFE_PORT_ID_QUINARY_TDM_RX_4,
+ QUINARY_TDM_RX_4, 1, 1},
+ [QUINARY_TDM_TX_4] = { AFE_PORT_ID_QUINARY_TDM_TX_4,
+ QUINARY_TDM_TX_4, 0, 1},
+ [QUINARY_TDM_RX_5] = { AFE_PORT_ID_QUINARY_TDM_RX_5,
+ QUINARY_TDM_RX_5, 1, 1},
+ [QUINARY_TDM_TX_5] = { AFE_PORT_ID_QUINARY_TDM_TX_5,
+ QUINARY_TDM_TX_5, 0, 1},
+ [QUINARY_TDM_RX_6] = { AFE_PORT_ID_QUINARY_TDM_RX_6,
+ QUINARY_TDM_RX_6, 1, 1},
+ [QUINARY_TDM_TX_6] = { AFE_PORT_ID_QUINARY_TDM_TX_6,
+ QUINARY_TDM_TX_6, 0, 1},
+ [QUINARY_TDM_RX_7] = { AFE_PORT_ID_QUINARY_TDM_RX_7,
+ QUINARY_TDM_RX_7, 1, 1},
+ [QUINARY_TDM_TX_7] = { AFE_PORT_ID_QUINARY_TDM_TX_7,
+ QUINARY_TDM_TX_7, 0, 1},
+ [DISPLAY_PORT_RX] = { AFE_PORT_ID_HDMI_OVER_DP_RX,
+ DISPLAY_PORT_RX, 1, 1},
+ [WSA_CODEC_DMA_RX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_0,
+ WSA_CODEC_DMA_RX_0, 1, 1},
+ [WSA_CODEC_DMA_TX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_0,
+ WSA_CODEC_DMA_TX_0, 0, 1},
+ [WSA_CODEC_DMA_RX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_1,
+ WSA_CODEC_DMA_RX_1, 1, 1},
+ [WSA_CODEC_DMA_TX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_1,
+ WSA_CODEC_DMA_TX_1, 0, 1},
+ [WSA_CODEC_DMA_TX_2] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_2,
+ WSA_CODEC_DMA_TX_2, 0, 1},
+ [VA_CODEC_DMA_TX_0] = { AFE_PORT_ID_VA_CODEC_DMA_TX_0,
+ VA_CODEC_DMA_TX_0, 0, 1},
+ [VA_CODEC_DMA_TX_1] = { AFE_PORT_ID_VA_CODEC_DMA_TX_1,
+ VA_CODEC_DMA_TX_1, 0, 1},
+ [VA_CODEC_DMA_TX_2] = { AFE_PORT_ID_VA_CODEC_DMA_TX_2,
+ VA_CODEC_DMA_TX_2, 0, 1},
+ [RX_CODEC_DMA_RX_0] = { AFE_PORT_ID_RX_CODEC_DMA_RX_0,
+ RX_CODEC_DMA_RX_0, 1, 1},
+ [TX_CODEC_DMA_TX_0] = { AFE_PORT_ID_TX_CODEC_DMA_TX_0,
+ TX_CODEC_DMA_TX_0, 0, 1},
+ [RX_CODEC_DMA_RX_1] = { AFE_PORT_ID_RX_CODEC_DMA_RX_1,
+ RX_CODEC_DMA_RX_1, 1, 1},
+ [TX_CODEC_DMA_TX_1] = { AFE_PORT_ID_TX_CODEC_DMA_TX_1,
+ TX_CODEC_DMA_TX_1, 0, 1},
+ [RX_CODEC_DMA_RX_2] = { AFE_PORT_ID_RX_CODEC_DMA_RX_2,
+ RX_CODEC_DMA_RX_2, 1, 1},
+ [TX_CODEC_DMA_TX_2] = { AFE_PORT_ID_TX_CODEC_DMA_TX_2,
+ TX_CODEC_DMA_TX_2, 0, 1},
+ [RX_CODEC_DMA_RX_3] = { AFE_PORT_ID_RX_CODEC_DMA_RX_3,
+ RX_CODEC_DMA_RX_3, 1, 1},
+ [TX_CODEC_DMA_TX_3] = { AFE_PORT_ID_TX_CODEC_DMA_TX_3,
+ TX_CODEC_DMA_TX_3, 0, 1},
+ [RX_CODEC_DMA_RX_4] = { AFE_PORT_ID_RX_CODEC_DMA_RX_4,
+ RX_CODEC_DMA_RX_4, 1, 1},
+ [TX_CODEC_DMA_TX_4] = { AFE_PORT_ID_TX_CODEC_DMA_TX_4,
+ TX_CODEC_DMA_TX_4, 0, 1},
+ [RX_CODEC_DMA_RX_5] = { AFE_PORT_ID_RX_CODEC_DMA_RX_5,
+ RX_CODEC_DMA_RX_5, 1, 1},
+ [TX_CODEC_DMA_TX_5] = { AFE_PORT_ID_TX_CODEC_DMA_TX_5,
+ TX_CODEC_DMA_TX_5, 0, 1},
+ [RX_CODEC_DMA_RX_6] = { AFE_PORT_ID_RX_CODEC_DMA_RX_6,
+ RX_CODEC_DMA_RX_6, 1, 1},
+ [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7,
+ RX_CODEC_DMA_RX_7, 1, 1},
+ [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1},
+};
+
+static void q6afe_port_free(struct kref *ref)
+{
+ struct q6afe_port *port;
+ struct q6afe *afe;
+ unsigned long flags;
+
+ port = container_of(ref, struct q6afe_port, refcount);
+ afe = port->afe;
+ spin_lock_irqsave(&afe->port_list_lock, flags);
+ list_del(&port->node);
+ spin_unlock_irqrestore(&afe->port_list_lock, flags);
+ kfree(port->scfg);
+ kfree(port);
+}
+
+static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token)
+{
+ struct q6afe_port *p;
+ struct q6afe_port *ret = NULL;
+
+ guard(spinlock)(&afe->port_list_lock);
+ list_for_each_entry(p, &afe->port_list, node)
+ if (p->token == token) {
+ ret = p;
+ kref_get(&p->refcount);
+ break;
+ }
+
+ return ret;
+}
+
+static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+{
+ struct q6afe *afe = dev_get_drvdata(&adev->dev);
+ struct aprv2_ibasic_rsp_result_t *res;
+ struct apr_hdr *hdr = &data->hdr;
+ struct q6afe_port *port;
+
+ if (!data->payload_size)
+ return 0;
+
+ res = data->payload;
+ switch (hdr->opcode) {
+ case APR_BASIC_RSP_RESULT: {
+ if (res->status) {
+ dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n",
+ res->opcode, res->status);
+ }
+ switch (res->opcode) {
+ case AFE_PORT_CMD_SET_PARAM_V2:
+ case AFE_PORT_CMD_DEVICE_STOP:
+ case AFE_PORT_CMD_DEVICE_START:
+ case AFE_SVC_CMD_SET_PARAM:
+ port = q6afe_find_port(afe, hdr->token);
+ if (port) {
+ port->result = *res;
+ wake_up(&port->wait);
+ kref_put(&port->refcount, q6afe_port_free);
+ } else if (hdr->token == AFE_CLK_TOKEN) {
+ afe->result = *res;
+ wake_up(&afe->wait);
+ }
+ break;
+ default:
+ dev_err(afe->dev, "Unknown cmd 0x%x\n", res->opcode);
+ break;
+ }
+ }
+ break;
+ case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST:
+ afe->result.opcode = hdr->opcode;
+ afe->result.status = res->status;
+ wake_up(&afe->wait);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * q6afe_get_port_id() - Get port id from a given port index
+ *
+ * @index: port index
+ *
+ * Return: Will be an negative on error or valid port_id on success
+ */
+int q6afe_get_port_id(int index)
+{
+ if (index < 0 || index >= AFE_PORT_MAX)
+ return -EINVAL;
+
+ return port_maps[index].port_id;
+}
+EXPORT_SYMBOL_GPL(q6afe_get_port_id);
+
+static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt,
+ struct q6afe_port *port, uint32_t rsp_opcode)
+{
+ wait_queue_head_t *wait;
+ struct aprv2_ibasic_rsp_result_t *result;
+ int ret;
+
+ mutex_lock(&afe->lock);
+ if (port) {
+ wait = &port->wait;
+ result = &port->result;
+ } else {
+ result = &afe->result;
+ wait = &afe->wait;
+ }
+
+ result->opcode = 0;
+ result->status = 0;
+
+ ret = apr_send_pkt(afe->apr, pkt);
+ if (ret < 0) {
+ dev_err(afe->dev, "packet not transmitted (%d)\n", ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = wait_event_timeout(*wait, (result->opcode == rsp_opcode),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ } else if (result->status > 0) {
+ dev_err(afe->dev, "DSP returned error[%x]\n",
+ result->status);
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
+
+err:
+ mutex_unlock(&afe->lock);
+
+ return ret;
+}
+
+static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port,
+ void *data, int param_id, int module_id, int psize,
+ int token)
+{
+ struct afe_svc_cmd_set_param *param;
+ struct afe_port_param_data_v2 *pdata;
+ struct apr_pkt *pkt;
+ int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize;
+ void *pl;
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ param = p + APR_HDR_SIZE;
+ pdata = p + APR_HDR_SIZE + sizeof(*param);
+ pl = p + APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata);
+ memcpy(pl, data, psize);
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.token = token;
+ pkt->hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+ param->payload_size = sizeof(*pdata) + psize;
+ param->payload_address_lsw = 0x00;
+ param->payload_address_msw = 0x00;
+ param->mem_map_handle = 0x00;
+ pdata->module_id = module_id;
+ pdata->param_id = param_id;
+ pdata->param_size = psize;
+
+ ret = afe_apr_send_pkt(afe, pkt, port, AFE_SVC_CMD_SET_PARAM);
+ if (ret)
+ dev_err(afe->dev, "AFE set params failed %d\n", ret);
+
+ return ret;
+}
+
+static int q6afe_port_set_param(struct q6afe_port *port, void *data,
+ int param_id, int module_id, int psize)
+{
+ return q6afe_set_param(port->afe, port, data, param_id, module_id,
+ psize, port->token);
+}
+
+static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data,
+ int param_id, int module_id, int psize)
+{
+ struct afe_port_cmd_set_param_v2 *param;
+ struct afe_port_param_data_v2 *pdata;
+ struct q6afe *afe = port->afe;
+ struct apr_pkt *pkt;
+ u16 port_id = port->id;
+ int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize;
+ void *pl;
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ param = p + APR_HDR_SIZE;
+ pdata = p + APR_HDR_SIZE + sizeof(*param);
+ pl = p + APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata);
+ memcpy(pl, data, psize);
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.token = port->token;
+ pkt->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+
+ param->port_id = port_id;
+ param->payload_size = sizeof(*pdata) + psize;
+ param->payload_address_lsw = 0x00;
+ param->payload_address_msw = 0x00;
+ param->mem_map_handle = 0x00;
+ pdata->module_id = module_id;
+ pdata->param_id = param_id;
+ pdata->param_size = psize;
+
+ ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_SET_PARAM_V2);
+ if (ret)
+ dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n",
+ port_id, ret);
+
+ return ret;
+}
+
+static int q6afe_port_set_lpass_clock(struct q6afe_port *port,
+ struct afe_clk_cfg *cfg)
+{
+ return q6afe_port_set_param_v2(port, cfg,
+ AFE_PARAM_ID_LPAIF_CLK_CONFIG,
+ AFE_MODULE_AUDIO_DEV_INTERFACE,
+ sizeof(*cfg));
+}
+
+static int q6afe_set_lpass_clock_v2(struct q6afe_port *port,
+ struct afe_clk_set *cfg)
+{
+ return q6afe_port_set_param(port, cfg, AFE_PARAM_ID_CLOCK_SET,
+ AFE_MODULE_CLOCK_SET, sizeof(*cfg));
+}
+
+static int q6afe_set_digital_codec_core_clock(struct q6afe_port *port,
+ struct afe_digital_clk_cfg *cfg)
+{
+ return q6afe_port_set_param_v2(port, cfg,
+ AFE_PARAM_ID_INT_DIGITAL_CDC_CLK_CONFIG,
+ AFE_MODULE_AUDIO_DEV_INTERFACE,
+ sizeof(*cfg));
+}
+
+int q6afe_set_lpass_clock(struct device *dev, int clk_id, int attri,
+ int clk_root, unsigned int freq)
+{
+ struct q6afe *afe = dev_get_drvdata(dev->parent);
+ struct afe_clk_set cset = {0,};
+
+ cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET;
+ cset.clk_id = clk_id;
+ cset.clk_freq_in_hz = freq;
+ cset.clk_attri = attri;
+ cset.clk_root = clk_root;
+ cset.enable = !!freq;
+
+ return q6afe_set_param(afe, NULL, &cset, AFE_PARAM_ID_CLOCK_SET,
+ AFE_MODULE_CLOCK_SET, sizeof(cset),
+ AFE_CLK_TOKEN);
+}
+EXPORT_SYMBOL_GPL(q6afe_set_lpass_clock);
+
+int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id,
+ int clk_src, int clk_root,
+ unsigned int freq, int dir)
+{
+ struct afe_clk_cfg ccfg = {0,};
+ struct afe_clk_set cset = {0,};
+ struct afe_digital_clk_cfg dcfg = {0,};
+ int ret;
+
+ switch (clk_id) {
+ case LPAIF_DIG_CLK:
+ dcfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG;
+ dcfg.clk_val = freq;
+ dcfg.clk_root = clk_root;
+ ret = q6afe_set_digital_codec_core_clock(port, &dcfg);
+ break;
+ case LPAIF_BIT_CLK:
+ ccfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG;
+ ccfg.clk_val1 = freq;
+ ccfg.clk_src = clk_src;
+ ccfg.clk_root = clk_root;
+ ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID;
+ ret = q6afe_port_set_lpass_clock(port, &ccfg);
+ break;
+
+ case LPAIF_OSR_CLK:
+ ccfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG;
+ ccfg.clk_val2 = freq;
+ ccfg.clk_src = clk_src;
+ ccfg.clk_root = clk_root;
+ ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK2_VALID;
+ ret = q6afe_port_set_lpass_clock(port, &ccfg);
+ break;
+ case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR:
+ case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1:
+ case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT:
+ case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK:
+ cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET;
+ cset.clk_id = clk_id;
+ cset.clk_freq_in_hz = freq;
+ cset.clk_attri = clk_src;
+ cset.clk_root = clk_root;
+ cset.enable = !!freq;
+ ret = q6afe_set_lpass_clock_v2(port, &cset);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6afe_port_set_sysclk);
+
+/**
+ * q6afe_port_stop() - Stop a afe port
+ *
+ * @port: Instance of port to stop
+ *
+ * Return: Will be an negative on packet size on success.
+ */
+int q6afe_port_stop(struct q6afe_port *port)
+{
+ struct afe_port_cmd_device_stop *stop;
+ struct q6afe *afe = port->afe;
+ struct apr_pkt *pkt;
+ int port_id = port->id;
+ int ret = 0;
+ int index, pkt_size;
+ void *p __free(kfree) = NULL;
+
+ index = port->token;
+ if (index < 0 || index >= AFE_PORT_MAX) {
+ dev_err(afe->dev, "AFE port index[%d] invalid!\n", index);
+ return -EINVAL;
+ }
+
+ pkt_size = APR_HDR_SIZE + sizeof(*stop);
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ stop = p + APR_HDR_SIZE;
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.token = index;
+ pkt->hdr.opcode = AFE_PORT_CMD_DEVICE_STOP;
+ stop->port_id = port_id;
+ stop->reserved = 0;
+
+ ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_DEVICE_STOP);
+ if (ret)
+ dev_err(afe->dev, "AFE close failed %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6afe_port_stop);
+
+/**
+ * q6afe_slim_port_prepare() - Prepare slim afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: SLIM configuration for the afe port
+ *
+ */
+void q6afe_slim_port_prepare(struct q6afe_port *port,
+ struct q6afe_slim_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+
+ pcfg->slim_cfg.sb_cfg_minor_version = AFE_API_VERSION_SLIMBUS_CONFIG;
+ pcfg->slim_cfg.sample_rate = cfg->sample_rate;
+ pcfg->slim_cfg.bit_width = cfg->bit_width;
+ pcfg->slim_cfg.num_channels = cfg->num_channels;
+ pcfg->slim_cfg.data_format = cfg->data_format;
+ pcfg->slim_cfg.shared_ch_mapping[0] = cfg->ch_mapping[0];
+ pcfg->slim_cfg.shared_ch_mapping[1] = cfg->ch_mapping[1];
+ pcfg->slim_cfg.shared_ch_mapping[2] = cfg->ch_mapping[2];
+ pcfg->slim_cfg.shared_ch_mapping[3] = cfg->ch_mapping[3];
+
+}
+EXPORT_SYMBOL_GPL(q6afe_slim_port_prepare);
+
+/**
+ * q6afe_tdm_port_prepare() - Prepare tdm afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: TDM configuration for the afe port
+ *
+ */
+void q6afe_tdm_port_prepare(struct q6afe_port *port,
+ struct q6afe_tdm_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+
+ pcfg->tdm_cfg.tdm_cfg_minor_version = AFE_API_VERSION_TDM_CONFIG;
+ pcfg->tdm_cfg.num_channels = cfg->num_channels;
+ pcfg->tdm_cfg.sample_rate = cfg->sample_rate;
+ pcfg->tdm_cfg.bit_width = cfg->bit_width;
+ pcfg->tdm_cfg.data_format = cfg->data_format;
+ pcfg->tdm_cfg.sync_mode = cfg->sync_mode;
+ pcfg->tdm_cfg.sync_src = cfg->sync_src;
+ pcfg->tdm_cfg.nslots_per_frame = cfg->nslots_per_frame;
+
+ pcfg->tdm_cfg.slot_width = cfg->slot_width;
+ pcfg->tdm_cfg.slot_mask = cfg->slot_mask;
+ port->scfg = kzalloc(sizeof(*port->scfg), GFP_KERNEL);
+ if (!port->scfg)
+ return;
+
+ port->scfg->minor_version = AFE_API_VERSION_SLOT_MAPPING_CONFIG;
+ port->scfg->num_channels = cfg->num_channels;
+ port->scfg->bitwidth = cfg->bit_width;
+ port->scfg->data_align_type = cfg->data_align_type;
+ memcpy(port->scfg->ch_mapping, cfg->ch_mapping,
+ sizeof(u16) * AFE_PORT_MAX_AUDIO_CHAN_CNT);
+}
+EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare);
+
+/**
+ * afe_port_send_usb_dev_param() - Send USB dev token
+ *
+ * @port: Instance of afe port
+ * @cardidx: USB SND card index to reference
+ * @pcmidx: USB SND PCM device index to reference
+ *
+ * The USB dev token carries information about which USB SND card instance and
+ * PCM device to execute the offload on. This information is carried through
+ * to the stream enable QMI request, which is handled by the offload class
+ * driver. The information is parsed to determine which USB device to query
+ * the required resources for.
+ */
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx)
+{
+ struct afe_param_id_usb_audio_dev_params usb_dev;
+ int ret;
+
+ memset(&usb_dev, 0, sizeof(usb_dev));
+
+ usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ usb_dev.dev_token = (cardidx << 16) | (pcmidx << 8);
+ ret = q6afe_port_set_param_v2(port, &usb_dev,
+ AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS,
+ AFE_MODULE_AUDIO_DEV_INTERFACE,
+ sizeof(usb_dev));
+ if (ret)
+ dev_err(port->afe->dev, "%s: AFE device param cmd failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(afe_port_send_usb_dev_param);
+
+static int afe_port_send_usb_params(struct q6afe_port *port, struct q6afe_usb_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+ struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt;
+ struct afe_param_id_usb_audio_svc_interval svc_int;
+ int ret;
+
+ if (!pcfg) {
+ dev_err(port->afe->dev, "%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&lpcm_fmt, 0, sizeof(lpcm_fmt));
+ memset(&svc_int, 0, sizeof(svc_int));
+
+ lpcm_fmt.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ lpcm_fmt.endian = pcfg->usb_cfg.endian;
+ ret = q6afe_port_set_param_v2(port, &lpcm_fmt,
+ AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT,
+ AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(lpcm_fmt));
+ if (ret) {
+ dev_err(port->afe->dev, "%s: AFE device param cmd LPCM_FMT failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ svc_int.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ svc_int.svc_interval = pcfg->usb_cfg.service_interval;
+ ret = q6afe_port_set_param_v2(port, &svc_int,
+ AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL,
+ AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(svc_int));
+ if (ret)
+ dev_err(port->afe->dev, "%s: AFE device param cmd svc_interval failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/**
+ * q6afe_usb_port_prepare() - Prepare usb afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: USB configuration for the afe port
+ *
+ */
+void q6afe_usb_port_prepare(struct q6afe_port *port,
+ struct q6afe_usb_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+
+ pcfg->usb_cfg.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ pcfg->usb_cfg.sample_rate = cfg->sample_rate;
+ pcfg->usb_cfg.num_channels = cfg->num_channels;
+ pcfg->usb_cfg.bit_width = cfg->bit_width;
+
+ afe_port_send_usb_params(port, cfg);
+}
+EXPORT_SYMBOL_GPL(q6afe_usb_port_prepare);
+
+/**
+ * q6afe_hdmi_port_prepare() - Prepare hdmi afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: HDMI configuration for the afe port
+ *
+ */
+void q6afe_hdmi_port_prepare(struct q6afe_port *port,
+ struct q6afe_hdmi_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+
+ pcfg->hdmi_multi_ch.hdmi_cfg_minor_version =
+ AFE_API_VERSION_HDMI_CONFIG;
+ pcfg->hdmi_multi_ch.datatype = cfg->datatype;
+ pcfg->hdmi_multi_ch.channel_allocation = cfg->channel_allocation;
+ pcfg->hdmi_multi_ch.sample_rate = cfg->sample_rate;
+ pcfg->hdmi_multi_ch.bit_width = cfg->bit_width;
+}
+EXPORT_SYMBOL_GPL(q6afe_hdmi_port_prepare);
+
+/**
+ * q6afe_i2s_port_prepare() - Prepare i2s afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: I2S configuration for the afe port
+ * Return: Will be an negative on error and zero on success.
+ */
+int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+ struct device *dev = port->afe->dev;
+ int num_sd_lines;
+
+ pcfg->i2s_cfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG;
+ pcfg->i2s_cfg.sample_rate = cfg->sample_rate;
+ pcfg->i2s_cfg.bit_width = cfg->bit_width;
+ pcfg->i2s_cfg.data_format = AFE_LINEAR_PCM_DATA;
+
+ switch (cfg->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ /* CPU is slave */
+ pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL;
+ break;
+ default:
+ break;
+ }
+
+ num_sd_lines = hweight_long(cfg->sd_line_mask);
+
+ switch (num_sd_lines) {
+ case 0:
+ dev_err(dev, "no line is assigned\n");
+ return -EINVAL;
+ case 1:
+ switch (cfg->sd_line_mask) {
+ case AFE_PORT_I2S_SD0_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0;
+ break;
+ case AFE_PORT_I2S_SD1_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD1;
+ break;
+ case AFE_PORT_I2S_SD2_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2;
+ break;
+ case AFE_PORT_I2S_SD3_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD3;
+ break;
+ default:
+ dev_err(dev, "Invalid SD lines\n");
+ return -EINVAL;
+ }
+ break;
+ case 2:
+ switch (cfg->sd_line_mask) {
+ case AFE_PORT_I2S_SD0_1_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD01;
+ break;
+ case AFE_PORT_I2S_SD2_3_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD23;
+ break;
+ default:
+ dev_err(dev, "Invalid SD lines\n");
+ return -EINVAL;
+ }
+ break;
+ case 3:
+ switch (cfg->sd_line_mask) {
+ case AFE_PORT_I2S_SD0_1_2_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_6CHS;
+ break;
+ default:
+ dev_err(dev, "Invalid SD lines\n");
+ return -EINVAL;
+ }
+ break;
+ case 4:
+ switch (cfg->sd_line_mask) {
+ case AFE_PORT_I2S_SD0_1_2_3_MASK:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_8CHS;
+
+ break;
+ default:
+ dev_err(dev, "Invalid SD lines\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid SD lines\n");
+ return -EINVAL;
+ }
+
+ switch (cfg->num_channels) {
+ case 1:
+ case 2:
+ switch (pcfg->i2s_cfg.channel_mode) {
+ case AFE_PORT_I2S_QUAD01:
+ case AFE_PORT_I2S_6CHS:
+ case AFE_PORT_I2S_8CHS:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0;
+ break;
+ case AFE_PORT_I2S_QUAD23:
+ pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2;
+ break;
+ }
+
+ if (cfg->num_channels == 2)
+ pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_STEREO;
+ else
+ pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_MONO;
+
+ break;
+ case 3:
+ case 4:
+ if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_QUAD01) {
+ dev_err(dev, "Invalid Channel mode\n");
+ return -EINVAL;
+ }
+ break;
+ case 5:
+ case 6:
+ if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_6CHS) {
+ dev_err(dev, "Invalid Channel mode\n");
+ return -EINVAL;
+ }
+ break;
+ case 7:
+ case 8:
+ if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_8CHS) {
+ dev_err(dev, "Invalid Channel mode\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare);
+
+/**
+ * q6afe_cdc_dma_port_prepare() - Prepare dma afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: DMA configuration for the afe port
+ *
+ */
+void q6afe_cdc_dma_port_prepare(struct q6afe_port *port,
+ struct q6afe_cdc_dma_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+ struct afe_param_id_cdc_dma_cfg *dma_cfg = &pcfg->dma_cfg;
+
+ dma_cfg->cdc_dma_cfg_minor_version = AFE_API_VERSION_CODEC_DMA_CONFIG;
+ dma_cfg->sample_rate = cfg->sample_rate;
+ dma_cfg->bit_width = cfg->bit_width;
+ dma_cfg->data_format = cfg->data_format;
+ dma_cfg->num_channels = cfg->num_channels;
+ if (!cfg->active_channels_mask)
+ dma_cfg->active_channels_mask = (1 << cfg->num_channels) - 1;
+}
+EXPORT_SYMBOL_GPL(q6afe_cdc_dma_port_prepare);
+/**
+ * q6afe_port_start() - Start a afe port
+ *
+ * @port: Instance of port to start
+ *
+ * Return: Will be an negative on packet size on success.
+ */
+int q6afe_port_start(struct q6afe_port *port)
+{
+ struct afe_port_cmd_device_start *start;
+ struct q6afe *afe = port->afe;
+ int port_id = port->id;
+ int ret, param_id = port->cfg_type;
+ struct apr_pkt *pkt;
+ int pkt_size;
+ void *p __free(kfree) = NULL;
+
+ ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id,
+ AFE_MODULE_AUDIO_DEV_INTERFACE,
+ sizeof(port->port_cfg));
+ if (ret) {
+ dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n",
+ port_id, ret);
+ return ret;
+ }
+
+ if (port->scfg) {
+ ret = q6afe_port_set_param_v2(port, port->scfg,
+ AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG,
+ AFE_MODULE_TDM, sizeof(*port->scfg));
+ if (ret) {
+ dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n",
+ port_id, ret);
+ return ret;
+ }
+ }
+
+ pkt_size = APR_HDR_SIZE + sizeof(*start);
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ start = p + APR_HDR_SIZE;
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.token = port->token;
+ pkt->hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+
+ start->port_id = port_id;
+
+ ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_DEVICE_START);
+ if (ret)
+ dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n",
+ port_id, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6afe_port_start);
+
+/**
+ * q6afe_port_get_from_id() - Get port instance from a port id
+ *
+ * @dev: Pointer to afe child device.
+ * @id: port id
+ *
+ * Return: Will be an error pointer on error or a valid afe port
+ * on success.
+ */
+struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
+{
+ int port_id;
+ struct q6afe *afe = dev_get_drvdata(dev->parent);
+ struct q6afe_port *port;
+ int cfg_type;
+
+ if (id < 0 || id >= AFE_PORT_MAX) {
+ dev_err(dev, "AFE port token[%d] invalid!\n", id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* if port is multiple times bind/unbind before callback finishes */
+ port = q6afe_find_port(afe, id);
+ if (port) {
+ dev_err(dev, "AFE Port already open\n");
+ return port;
+ }
+
+ port_id = port_maps[id].port_id;
+
+ switch (port_id) {
+ case AFE_PORT_ID_MULTICHAN_HDMI_RX:
+ case AFE_PORT_ID_HDMI_OVER_DP_RX:
+ cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
+ break;
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX:
+ case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX:
+ cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
+ break;
+
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7:
+ cfg_type = AFE_PARAM_ID_TDM_CONFIG;
+ break;
+ case AFE_PORT_ID_WSA_CODEC_DMA_RX_0 ... AFE_PORT_ID_RX_CODEC_DMA_RX_7:
+ cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG;
+ break;
+ case AFE_PORT_ID_USB_RX:
+ cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG;
+ break;
+ default:
+ dev_err(dev, "Invalid port id 0x%x\n", port_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ init_waitqueue_head(&port->wait);
+
+ port->token = id;
+ port->id = port_id;
+ port->afe = afe;
+ port->cfg_type = cfg_type;
+ kref_init(&port->refcount);
+
+ guard(spinlock)(&afe->port_list_lock);
+ list_add_tail(&port->node, &afe->port_list);
+
+ return port;
+
+}
+EXPORT_SYMBOL_GPL(q6afe_port_get_from_id);
+
+/**
+ * q6afe_port_put() - Release port reference
+ *
+ * @port: Instance of port to put
+ */
+void q6afe_port_put(struct q6afe_port *port)
+{
+ kref_put(&port->refcount, q6afe_port_free);
+}
+EXPORT_SYMBOL_GPL(q6afe_port_put);
+
+int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ uint32_t client_handle)
+{
+ struct q6afe *afe = dev_get_drvdata(dev->parent);
+ struct afe_cmd_remote_lpass_core_hw_devote_request *vote_cfg;
+ struct apr_pkt *pkt;
+ int ret = 0;
+ int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ vote_cfg = p + APR_HDR_SIZE;
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.token = hw_block_id;
+ pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST;
+ vote_cfg->hw_block_id = hw_block_id;
+ vote_cfg->client_handle = client_handle;
+
+ ret = apr_send_pkt(afe->apr, pkt);
+ if (ret < 0)
+ dev_err(afe->dev, "AFE failed to unvote (%d)\n", hw_block_id);
+
+ return ret;
+}
+EXPORT_SYMBOL(q6afe_unvote_lpass_core_hw);
+
+int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ const char *client_name, uint32_t *client_handle)
+{
+ struct q6afe *afe = dev_get_drvdata(dev->parent);
+ struct afe_cmd_remote_lpass_core_hw_vote_request *vote_cfg;
+ struct apr_pkt *pkt;
+ int ret = 0;
+ int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ vote_cfg = p + APR_HDR_SIZE;
+
+ pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.token = hw_block_id;
+ pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST;
+ vote_cfg->hw_block_id = hw_block_id;
+ strscpy(vote_cfg->client_name, client_name,
+ sizeof(vote_cfg->client_name));
+
+ ret = afe_apr_send_pkt(afe, pkt, NULL,
+ AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST);
+ if (ret)
+ dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id);
+
+ return ret;
+}
+EXPORT_SYMBOL(q6afe_vote_lpass_core_hw);
+
+static int q6afe_probe(struct apr_device *adev)
+{
+ struct q6afe *afe;
+ struct device *dev = &adev->dev;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ q6core_get_svc_api_info(adev->svc_id, &afe->ainfo);
+ afe->apr = adev;
+ mutex_init(&afe->lock);
+ init_waitqueue_head(&afe->wait);
+ afe->dev = dev;
+ INIT_LIST_HEAD(&afe->port_list);
+ spin_lock_init(&afe->port_list_lock);
+
+ dev_set_drvdata(dev, afe);
+
+ return devm_of_platform_populate(dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6afe_device_id[] = {
+ { .compatible = "qcom,q6afe" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6afe_device_id);
+#endif
+
+static struct apr_driver qcom_q6afe_driver = {
+ .probe = q6afe_probe,
+ .callback = q6afe_callback,
+ .driver = {
+ .name = "qcom-q6afe",
+ .of_match_table = of_match_ptr(q6afe_device_id),
+
+ },
+};
+
+module_apr_driver(qcom_q6afe_driver);
+MODULE_DESCRIPTION("Q6 Audio Front End");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h
new file mode 100644
index 000000000000..a29abe4ce436
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6afe.h
@@ -0,0 +1,274 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6AFE_H__
+#define __Q6AFE_H__
+
+#define AFE_PORT_MAX 137
+
+#define MSM_AFE_PORT_TYPE_RX 0
+#define MSM_AFE_PORT_TYPE_TX 1
+#define AFE_MAX_PORTS AFE_PORT_MAX
+
+#define Q6AFE_MAX_MI2S_LINES 4
+
+#define AFE_MAX_CHAN_COUNT 8
+#define AFE_PORT_MAX_AUDIO_CHAN_CNT 0x8
+
+#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1
+#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
+
+#define LPAIF_DIG_CLK 1
+#define LPAIF_BIT_CLK 2
+#define LPAIF_OSR_CLK 3
+
+/* Clock ID for Primary I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100
+/* Clock ID for Primary I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT 0x101
+/* Clock ID for Secondary I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT 0x102
+/* Clock ID for Secondary I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT 0x103
+/* Clock ID for Tertiary I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT 0x104
+/* Clock ID for Tertiary I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT 0x105
+/* Clock ID for Quartnery I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT 0x106
+/* Clock ID for Quartnery I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT 0x107
+/* Clock ID for Speaker I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_IBIT 0x108
+/* Clock ID for Speaker I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_EBIT 0x109
+/* Clock ID for Speaker I2S OSR */
+#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR 0x10A
+
+/* Clock ID for QUINARY I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT 0x10B
+/* Clock ID for QUINARY I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT 0x10C
+/* Clock ID for SENARY I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT 0x10D
+/* Clock ID for SENARY I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_EBIT 0x10E
+/* Clock ID for INT0 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT 0x10F
+/* Clock ID for INT1 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT 0x110
+/* Clock ID for INT2 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT 0x111
+/* Clock ID for INT3 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT 0x112
+/* Clock ID for INT4 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT 0x113
+/* Clock ID for INT5 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT 0x114
+/* Clock ID for INT6 I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115
+
+/* Clock ID for QUINARY MI2S OSR CLK */
+#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR 0x116
+
+/* Clock ID for Primary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT 0x200
+/* Clock ID for Primary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT 0x201
+/* Clock ID for Secondary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT 0x202
+/* Clock ID for Secondary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT 0x203
+/* Clock ID for Tertiary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT 0x204
+/* Clock ID for Tertiary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT 0x205
+/* Clock ID for Quartery PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT 0x206
+/* Clock ID for Quartery PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT 0x207
+/* Clock ID for Quinary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT 0x208
+/* Clock ID for Quinary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUIN_PCM_EBIT 0x209
+/* Clock ID for QUINARY PCM OSR */
+#define Q6AFE_LPASS_CLK_ID_QUI_PCM_OSR 0x20A
+
+/** Clock ID for Primary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT 0x200
+/** Clock ID for Primary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT 0x201
+/** Clock ID for Secondary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT 0x202
+/** Clock ID for Secondary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT 0x203
+/** Clock ID for Tertiary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT 0x204
+/** Clock ID for Tertiary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT 0x205
+/** Clock ID for Quartery TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT 0x206
+/** Clock ID for Quartery TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT 0x207
+/** Clock ID for Quinary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT 0x208
+/** Clock ID for Quinary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT 0x209
+/** Clock ID for Quinary TDM OSR */
+#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_OSR 0x20A
+
+/* Clock ID for MCLK1 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_1 0x300
+/* Clock ID for MCLK2 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_2 0x301
+/* Clock ID for MCLK3 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_3 0x302
+/* Clock ID for MCLK4 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_4 0x304
+/* Clock ID for Internal Digital Codec Core */
+#define Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 0x303
+/* Clock ID for INT MCLK0 */
+#define Q6AFE_LPASS_CLK_ID_INT_MCLK_0 0x305
+/* Clock ID for INT MCLK1 */
+#define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306
+
+#define Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK 0x309
+#define Q6AFE_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x30a
+#define Q6AFE_LPASS_CLK_ID_TX_CORE_MCLK 0x30c
+#define Q6AFE_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d
+#define Q6AFE_LPASS_CLK_ID_RX_CORE_MCLK 0x30e
+#define Q6AFE_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f
+#define Q6AFE_LPASS_CLK_ID_VA_CORE_MCLK 0x30b
+#define Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x310
+
+#define Q6AFE_LPASS_CORE_AVTIMER_BLOCK 0x2
+#define Q6AFE_LPASS_CORE_HW_MACRO_BLOCK 0x3
+#define Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK 0x4
+
+/* Clock attribute for invalid use (reserved for internal usage) */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID 0x0
+/* Clock attribute for no couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1
+/* Clock attribute for dividend couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2
+/* Clock attribute for divisor couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3
+/* Clock attribute for invert and no couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO 0x4
+
+#define Q6AFE_CMAP_INVALID 0xFFFF
+
+struct q6afe_hdmi_cfg {
+ u16 datatype;
+ u16 channel_allocation;
+ u32 sample_rate;
+ u16 bit_width;
+};
+
+struct q6afe_slim_cfg {
+ u32 sample_rate;
+ u16 bit_width;
+ u16 data_format;
+ u16 num_channels;
+ u8 ch_mapping[AFE_MAX_CHAN_COUNT];
+};
+
+struct q6afe_i2s_cfg {
+ u32 sample_rate;
+ u16 bit_width;
+ u16 data_format;
+ u16 num_channels;
+ u32 sd_line_mask;
+ int fmt;
+};
+
+struct q6afe_tdm_cfg {
+ u16 num_channels;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 data_format;
+ u16 sync_mode;
+ u16 sync_src;
+ u16 nslots_per_frame;
+ u16 slot_width;
+ u16 slot_mask;
+ u32 data_align_type;
+ u16 ch_mapping[AFE_MAX_CHAN_COUNT];
+};
+
+struct q6afe_cdc_dma_cfg {
+ u16 sample_rate;
+ u16 bit_width;
+ u16 data_format;
+ u16 num_channels;
+ u16 active_channels_mask;
+};
+
+/**
+ * struct q6afe_usb_cfg
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @sample_rate: Sampling rate of the port
+ * Supported values:
+ * AFE_PORT_SAMPLE_RATE_8K
+ * AFE_PORT_SAMPLE_RATE_11025
+ * AFE_PORT_SAMPLE_RATE_12K
+ * AFE_PORT_SAMPLE_RATE_16K
+ * AFE_PORT_SAMPLE_RATE_22050
+ * AFE_PORT_SAMPLE_RATE_24K
+ * AFE_PORT_SAMPLE_RATE_32K
+ * AFE_PORT_SAMPLE_RATE_44P1K
+ * AFE_PORT_SAMPLE_RATE_48K
+ * AFE_PORT_SAMPLE_RATE_96K
+ * AFE_PORT_SAMPLE_RATE_192K
+ * @bit_width: Bit width of the sample.
+ * Supported values: 16, 24
+ * @num_channels: Number of channels
+ * Supported values: 1, 2
+ **/
+struct q6afe_usb_cfg {
+ u32 cfg_minor_version;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 num_channels;
+};
+
+struct q6afe_port_config {
+ struct q6afe_hdmi_cfg hdmi;
+ struct q6afe_slim_cfg slim;
+ struct q6afe_i2s_cfg i2s_cfg;
+ struct q6afe_tdm_cfg tdm;
+ struct q6afe_cdc_dma_cfg dma_cfg;
+ struct q6afe_usb_cfg usb_audio;
+};
+
+struct q6afe_port;
+
+struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id);
+int q6afe_port_start(struct q6afe_port *port);
+int q6afe_port_stop(struct q6afe_port *port);
+void q6afe_port_put(struct q6afe_port *port);
+int q6afe_get_port_id(int index);
+void q6afe_usb_port_prepare(struct q6afe_port *port,
+ struct q6afe_usb_cfg *cfg);
+void q6afe_hdmi_port_prepare(struct q6afe_port *port,
+ struct q6afe_hdmi_cfg *cfg);
+void q6afe_slim_port_prepare(struct q6afe_port *port,
+ struct q6afe_slim_cfg *cfg);
+int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg);
+void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg);
+void q6afe_cdc_dma_port_prepare(struct q6afe_port *port,
+ struct q6afe_cdc_dma_cfg *cfg);
+
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx);
+int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id,
+ int clk_src, int clk_root,
+ unsigned int freq, int dir);
+int q6afe_set_lpass_clock(struct device *dev, int clk_id, int attri,
+ int clk_root, unsigned int freq);
+int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ const char *client_name, uint32_t *client_handle);
+int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ uint32_t client_handle);
+#endif /* __Q6AFE_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
new file mode 100644
index 000000000000..aaeeadded7aa
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -0,0 +1,884 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/spinlock.h>
+#include <sound/pcm.h>
+#include <asm/div64.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <sound/pcm_params.h>
+#include "q6apm.h"
+
+#define DRV_NAME "q6apm-dai"
+
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 65536
+#define PLAYBACK_MIN_PERIOD_SIZE 128
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 65536
+#define CAPTURE_MIN_PERIOD_SIZE 6144
+#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
+#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
+#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
+#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
+#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
+#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
+#define SID_MASK_DEFAULT 0xF
+
+static const struct snd_compr_codec_caps q6apm_compr_caps = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000 },
+ .descriptor[0].num_sample_rates = 13,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 128,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
+enum stream_state {
+ Q6APM_STREAM_IDLE = 0,
+ Q6APM_STREAM_STOPPED,
+ Q6APM_STREAM_RUNNING,
+};
+
+struct q6apm_dai_rtd {
+ struct snd_pcm_substream *substream;
+ struct snd_compr_stream *cstream;
+ struct snd_codec codec;
+ struct snd_compr_params codec_param;
+ struct snd_dma_buffer dma_buffer;
+ phys_addr_t phys;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int periods;
+ uint64_t bytes_sent;
+ uint64_t bytes_received;
+ uint64_t copied_total;
+ uint16_t bits_per_sample;
+ snd_pcm_uframes_t queue_ptr;
+ bool next_track;
+ enum stream_state state;
+ struct q6apm_graph *graph;
+ spinlock_t lock;
+ bool notify_on_drain;
+};
+
+struct q6apm_dai_data {
+ long long sid;
+};
+
+static const struct snd_pcm_hardware q6apm_dai_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR |
+ SNDRV_PCM_INFO_BATCH),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 4,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static const struct snd_pcm_hardware q6apm_dai_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR |
+ SNDRV_PCM_INFO_BATCH),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE),
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *priv)
+{
+ struct q6apm_dai_rtd *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+
+ switch (opcode) {
+ case APM_CLIENT_EVENT_CMD_EOS_DONE:
+ prtd->state = Q6APM_STREAM_STOPPED;
+ break;
+ case APM_CLIENT_EVENT_DATA_WRITE_DONE:
+ snd_pcm_period_elapsed(substream);
+
+ break;
+ case APM_CLIENT_EVENT_DATA_READ_DONE:
+ snd_pcm_period_elapsed(substream);
+ if (prtd->state == Q6APM_STREAM_RUNNING)
+ q6apm_read(prtd->graph);
+
+ break;
+ default:
+ break;
+ }
+}
+
+static void event_handler_compr(uint32_t opcode, uint32_t token,
+ void *payload, void *priv)
+{
+ struct q6apm_dai_rtd *prtd = priv;
+ struct snd_compr_stream *substream = prtd->cstream;
+ uint32_t wflags = 0;
+ uint64_t avail;
+ uint32_t bytes_written, bytes_to_write;
+ bool is_last_buffer = false;
+
+ guard(spinlock_irqsave)(&prtd->lock);
+ switch (opcode) {
+ case APM_CLIENT_EVENT_CMD_EOS_DONE:
+ if (prtd->notify_on_drain) {
+ snd_compr_drain_notify(prtd->cstream);
+ prtd->notify_on_drain = false;
+ } else {
+ prtd->state = Q6APM_STREAM_STOPPED;
+ }
+ break;
+ case APM_CLIENT_EVENT_DATA_WRITE_DONE:
+ bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT;
+ prtd->copied_total += bytes_written;
+ snd_compr_fragment_elapsed(substream);
+
+ if (prtd->state != Q6APM_STREAM_RUNNING)
+ break;
+
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail > prtd->pcm_count) {
+ bytes_to_write = prtd->pcm_count;
+ } else {
+ if (substream->partial_drain || prtd->notify_on_drain)
+ is_last_buffer = true;
+ bytes_to_write = avail;
+ }
+
+ if (bytes_to_write) {
+ if (substream->partial_drain && is_last_buffer)
+ wflags |= APM_LAST_BUFFER_FLAG;
+
+ q6apm_write_async(prtd->graph,
+ bytes_to_write, 0, 0, wflags);
+
+ prtd->bytes_sent += bytes_to_write;
+
+ if (prtd->notify_on_drain && is_last_buffer)
+ audioreach_shared_memory_send_eos(prtd->graph);
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+static int q6apm_dai_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct audioreach_module_config cfg;
+ struct device *dev = component->dev;
+ struct q6apm_dai_data *pdata;
+ int ret;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ if (!prtd || !prtd->graph) {
+ dev_err(dev, "%s: private data null or audio client freed\n", __func__);
+ return -EINVAL;
+ }
+
+ cfg.direction = substream->stream;
+ cfg.sample_rate = runtime->rate;
+ cfg.num_channels = runtime->channels;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = SND_AUDIOCODEC_PCM;
+ audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels);
+
+ if (prtd->state) {
+ /* clear the previous setup if any */
+ q6apm_graph_stop(prtd->graph);
+ q6apm_unmap_memory_regions(prtd->graph, substream->stream);
+ }
+
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ /* rate and channels are sent to audio driver */
+ ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
+ if (ret < 0) {
+ dev_err(dev, "%s: q6apm_open_write failed\n", __func__);
+ return ret;
+ }
+
+ ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
+ if (ret < 0)
+ dev_err(dev, "%s: CMD Format block failed\n", __func__);
+
+ ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys,
+ (prtd->pcm_size / prtd->periods), prtd->periods);
+
+ if (ret < 0) {
+ dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret);
+ return -ENOMEM;
+ }
+
+ ret = q6apm_graph_prepare(prtd->graph);
+ if (ret) {
+ dev_err(dev, "Failed to prepare Graph %d\n", ret);
+ return ret;
+ }
+
+ ret = q6apm_graph_start(prtd->graph);
+ if (ret) {
+ dev_err(dev, "Failed to Start Graph %d\n", ret);
+ return ret;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ int i;
+ /* Queue the buffers for Capture ONLY after graph is started */
+ for (i = 0; i < runtime->periods; i++)
+ q6apm_read(prtd->graph);
+
+ }
+
+ /* Now that graph as been prepared and started update the internal state accordingly */
+ prtd->state = Q6APM_STREAM_RUNNING;
+
+ return 0;
+}
+
+static int q6apm_dai_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int i, ret = 0, avail_periods;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ avail_periods = (runtime->control->appl_ptr - prtd->queue_ptr)/runtime->period_size;
+ for (i = 0; i < avail_periods; i++) {
+ ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ dev_err(component->dev, "Error queuing playback buffer %d\n", ret);
+ return ret;
+ }
+ prtd->queue_ptr += runtime->period_size;
+ }
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* TODO support be handled via SoftPause Module */
+ prtd->state = Q6APM_STREAM_STOPPED;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0);
+ struct device *dev = component->dev;
+ struct q6apm_dai_data *pdata;
+ struct q6apm_dai_rtd *prtd;
+ int graph_id, ret;
+
+ graph_id = cpu_dai->driver->id;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata) {
+ dev_err(dev, "Drv data not found ..\n");
+ return -EINVAL;
+ }
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&prtd->lock);
+ prtd->substream = substream;
+ prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id);
+ if (IS_ERR(prtd->graph)) {
+ dev_err(dev, "%s: Could not allocate memory\n", __func__);
+ ret = PTR_ERR(prtd->graph);
+ goto err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = q6apm_dai_hardware_playback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = q6apm_dai_hardware_capture;
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(dev, "snd_pcm_hw_constraint_integer failed\n");
+ goto err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ BUFFER_BYTES_MIN, BUFFER_BYTES_MAX);
+ if (ret < 0) {
+ dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret);
+ goto err;
+ }
+ }
+
+ /* setup 10ms latency to accommodate DSP restrictions */
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 480);
+ if (ret < 0) {
+ dev_err(dev, "constraint for period bytes step ret = %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 480);
+ if (ret < 0) {
+ dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret);
+ goto err;
+ }
+
+ runtime->private_data = prtd;
+ runtime->dma_bytes = BUFFER_BYTES_MAX;
+ if (pdata->sid < 0)
+ prtd->phys = substream->dma_buffer.addr;
+ else
+ prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
+
+ return 0;
+err:
+ kfree(prtd);
+
+ return ret;
+}
+
+static int q6apm_dai_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ if (prtd->state) { /* only stop graph that is started */
+ q6apm_graph_stop(prtd->graph);
+ q6apm_unmap_memory_regions(prtd->graph, substream->stream);
+ }
+
+ q6apm_graph_close(prtd->graph);
+ prtd->graph = NULL;
+ kfree(prtd);
+ runtime->private_data = NULL;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ snd_pcm_uframes_t ptr;
+
+ ptr = q6apm_get_hw_pointer(prtd->graph, substream->stream) * runtime->period_size;
+ if (ptr)
+ return ptr - 1;
+
+ return 0;
+}
+
+static int q6apm_dai_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ prtd->pcm_size = params_buffer_bytes(params);
+ prtd->periods = params_periods(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ prtd->bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ prtd->bits_per_sample = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd)
+{
+ int size = BUFFER_BYTES_MAX;
+
+ return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
+}
+
+static int q6apm_dai_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd;
+ struct q6apm_dai_data *pdata;
+ struct device *dev = component->dev;
+ int ret, size;
+ int graph_id;
+
+ graph_id = cpu_dai->driver->id;
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ prtd->cstream = stream;
+ prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id);
+ if (IS_ERR(prtd->graph)) {
+ ret = PTR_ERR(prtd->graph);
+ kfree(prtd);
+ return ret;
+ }
+
+ runtime->private_data = prtd;
+ runtime->dma_bytes = BUFFER_BYTES_MAX;
+ size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer);
+ if (ret)
+ return ret;
+
+ if (pdata->sid < 0)
+ prtd->phys = prtd->dma_buffer.addr;
+ else
+ prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);
+
+ snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);
+ spin_lock_init(&prtd->lock);
+
+ q6apm_enable_compress_module(dev, prtd->graph, true);
+ return 0;
+}
+
+static int q6apm_dai_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ q6apm_graph_stop(prtd->graph);
+ q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK);
+ q6apm_graph_close(prtd->graph);
+ snd_dma_free_pages(&prtd->dma_buffer);
+ prtd->graph = NULL;
+ kfree(prtd);
+ runtime->private_data = NULL;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps)
+{
+ caps->direction = SND_COMPRESS_PLAYBACK;
+ caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;
+ caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
+ caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
+ caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ caps->num_codecs = 4;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+ caps->codecs[2] = SND_AUDIOCODEC_FLAC;
+ caps->codecs[3] = SND_AUDIOCODEC_OPUS_RAW;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_codec_caps *codec)
+{
+ switch (codec->codec) {
+ case SND_AUDIOCODEC_MP3:
+ *codec = q6apm_compr_caps;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int q6apm_dai_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_tstamp64 *tstamp)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ uint64_t temp_copied_total;
+
+ guard(spinlock_irqsave)(&prtd->lock);
+ tstamp->copied_total = prtd->copied_total;
+ temp_copied_total = tstamp->copied_total;
+ tstamp->byte_offset = do_div(temp_copied_total, prtd->pcm_size);
+
+ return 0;
+}
+
+static int q6apm_dai_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, int cmd)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ case SND_COMPR_TRIGGER_NEXT_TRACK:
+ prtd->next_track = true;
+ break;
+ case SND_COMPR_TRIGGER_DRAIN:
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ prtd->notify_on_drain = true;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ guard(spinlock_irqsave)(&prtd->lock);
+ prtd->bytes_received += count;
+
+ return count;
+}
+
+static int q6apm_dai_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct q6apm_dai_data *pdata;
+ struct audioreach_module_config cfg;
+ struct snd_codec *codec = &params->codec;
+ int dir = stream->direction;
+ int ret;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ prtd->periods = runtime->fragments;
+ prtd->pcm_count = runtime->fragment_size;
+ prtd->pcm_size = runtime->fragments * runtime->fragment_size;
+ prtd->bits_per_sample = 16;
+
+ if (prtd->next_track != true) {
+ memcpy(&prtd->codec, codec, sizeof(*codec));
+
+ ret = q6apm_set_real_module_id(component->dev, prtd->graph, codec->id);
+ if (ret)
+ return ret;
+
+ cfg.direction = dir;
+ cfg.sample_rate = codec->sample_rate;
+ cfg.num_channels = 2;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = codec->id;
+ audioreach_set_default_channel_mapping(cfg.channel_map,
+ cfg.num_channels);
+ memcpy(&cfg.codec, codec, sizeof(*codec));
+
+ ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
+ if (ret < 0)
+ return ret;
+
+ ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
+ if (ret)
+ return ret;
+
+ ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK,
+ prtd->phys, (prtd->pcm_size / prtd->periods),
+ prtd->periods);
+ if (ret < 0)
+ return -ENOMEM;
+
+ ret = q6apm_graph_prepare(prtd->graph);
+ if (ret)
+ return ret;
+
+ ret = q6apm_graph_start(prtd->graph);
+ if (ret)
+ return ret;
+
+ } else {
+ cfg.direction = dir;
+ cfg.sample_rate = codec->sample_rate;
+ cfg.num_channels = 2;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = codec->id;
+ memcpy(&cfg.codec, codec, sizeof(*codec));
+
+ ret = audioreach_compr_set_param(prtd->graph, &cfg);
+ if (ret < 0)
+ return ret;
+ }
+ prtd->state = Q6APM_STREAM_RUNNING;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (metadata->key) {
+ case SNDRV_COMPRESS_ENCODER_PADDING:
+ q6apm_remove_trailing_silence(component->dev, prtd->graph,
+ metadata->value[0]);
+ break;
+ case SNDRV_COMPRESS_ENCODER_DELAY:
+ q6apm_remove_initial_silence(component->dev, prtd->graph,
+ metadata->value[0]);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_compr_mmap(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct vm_area_struct *vma)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct device *dev = component->dev;
+
+ return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr,
+ prtd->dma_buffer.bytes);
+}
+
+static int q6apm_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, char __user *buf,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ void *dstn;
+ size_t copy;
+ u32 wflags = 0;
+ u32 app_pointer;
+ uint64_t bytes_received;
+ uint64_t temp_bytes_received;
+ uint32_t bytes_to_write;
+ uint64_t avail, bytes_in_flight = 0;
+
+ bytes_received = prtd->bytes_received;
+ temp_bytes_received = bytes_received;
+
+ /**
+ * Make sure that next track data pointer is aligned at 32 bit boundary
+ * This is a Mandatory requirement from DSP data buffers alignment
+ */
+ if (prtd->next_track) {
+ bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);
+ temp_bytes_received = bytes_received;
+ }
+
+ app_pointer = do_div(temp_bytes_received, prtd->pcm_size);
+ dstn = prtd->dma_buffer.area + app_pointer;
+
+ if (count < prtd->pcm_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = prtd->pcm_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy))
+ return -EFAULT;
+ }
+
+ guard(spinlock_irqsave)(&prtd->lock);
+ bytes_in_flight = prtd->bytes_received - prtd->copied_total;
+
+ if (prtd->next_track) {
+ prtd->next_track = false;
+ prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);
+ prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);
+ }
+
+ prtd->bytes_received = bytes_received + count;
+
+ /* Kick off the data to dsp if its starving!! */
+ if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) {
+ bytes_to_write = prtd->pcm_count;
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail < prtd->pcm_count)
+ bytes_to_write = avail;
+
+ q6apm_write_async(prtd->graph, bytes_to_write, 0, 0, wflags);
+ prtd->bytes_sent += bytes_to_write;
+ }
+
+ return count;
+}
+
+static const struct snd_compress_ops q6apm_dai_compress_ops = {
+ .open = q6apm_dai_compr_open,
+ .free = q6apm_dai_compr_free,
+ .get_caps = q6apm_dai_compr_get_caps,
+ .get_codec_caps = q6apm_dai_compr_get_codec_caps,
+ .pointer = q6apm_dai_compr_pointer,
+ .trigger = q6apm_dai_compr_trigger,
+ .ack = q6apm_dai_compr_ack,
+ .set_params = q6apm_dai_compr_set_params,
+ .set_metadata = q6apm_dai_compr_set_metadata,
+ .mmap = q6apm_dai_compr_mmap,
+ .copy = q6apm_compr_copy,
+};
+
+static const struct snd_soc_component_driver q6apm_fe_dai_component = {
+ .name = DRV_NAME,
+ .open = q6apm_dai_open,
+ .close = q6apm_dai_close,
+ .prepare = q6apm_dai_prepare,
+ .pcm_construct = q6apm_dai_pcm_new,
+ .hw_params = q6apm_dai_hw_params,
+ .pointer = q6apm_dai_pointer,
+ .trigger = q6apm_dai_trigger,
+ .ack = q6apm_dai_ack,
+ .compress_ops = &q6apm_dai_compress_ops,
+ .use_dai_pcm_id = true,
+};
+
+static int q6apm_dai_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct q6apm_dai_data *pdata;
+ struct of_phandle_args args;
+ int rc;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
+ if (rc < 0)
+ pdata->sid = -1;
+ else
+ pdata->sid = args.args[0] & SID_MASK_DEFAULT;
+
+ dev_set_drvdata(dev, pdata);
+
+ return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6apm_dai_device_id[] = {
+ { .compatible = "qcom,q6apm-dais" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6apm_dai_device_id);
+#endif
+
+static struct platform_driver q6apm_dai_platform_driver = {
+ .driver = {
+ .name = "q6apm-dai",
+ .of_match_table = of_match_ptr(q6apm_dai_device_id),
+ },
+ .probe = q6apm_dai_probe,
+};
+module_platform_driver(q6apm_dai_platform_driver);
+
+MODULE_DESCRIPTION("Q6APM dai driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
new file mode 100644
index 000000000000..528756f1332b
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "q6dsp-lpass-ports.h"
+#include "q6dsp-common.h"
+#include "audioreach.h"
+#include "q6apm.h"
+
+#define AUDIOREACH_BE_PCM_BASE 16
+
+struct q6apm_lpass_dai_data {
+ struct q6apm_graph *graph[APM_PORT_MAX];
+ bool is_port_started[APM_PORT_MAX];
+ struct audioreach_module_config module_config[APM_PORT_MAX];
+};
+
+static int q6dma_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num,
+ const unsigned int *tx_ch_mask,
+ unsigned int rx_num,
+ const unsigned int *rx_ch_mask)
+{
+
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case WSA_CODEC_DMA_TX_0:
+ case WSA_CODEC_DMA_TX_1:
+ case WSA_CODEC_DMA_TX_2:
+ case VA_CODEC_DMA_TX_0:
+ case VA_CODEC_DMA_TX_1:
+ case VA_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ case TX_CODEC_DMA_TX_4:
+ case TX_CODEC_DMA_TX_5:
+ if (!tx_ch_mask) {
+ dev_err(dai->dev, "tx slot not found\n");
+ return -EINVAL;
+ }
+
+ if (tx_num > AR_PCM_MAX_NUM_CHANNEL) {
+ dev_err(dai->dev, "invalid tx num %d\n",
+ tx_num);
+ return -EINVAL;
+ }
+ for (i = 0; i < tx_num; i++)
+ cfg->channel_map[i] = tx_ch_mask[i];
+
+ break;
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_2:
+ case RX_CODEC_DMA_RX_3:
+ case RX_CODEC_DMA_RX_4:
+ case RX_CODEC_DMA_RX_5:
+ case RX_CODEC_DMA_RX_6:
+ case RX_CODEC_DMA_RX_7:
+ /* rx */
+ if (!rx_ch_mask) {
+ dev_err(dai->dev, "rx slot not found\n");
+ return -EINVAL;
+ }
+ if (rx_num > APM_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "invalid rx num %d\n",
+ rx_num);
+ return -EINVAL;
+ }
+ for (i = 0; i < rx_num; i++)
+ cfg->channel_map[i] = rx_ch_mask[i];
+
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ int channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max;
+ int ret;
+
+ cfg->bit_width = params_width(params);
+ cfg->sample_rate = params_rate(params);
+ cfg->num_channels = channels;
+ audioreach_set_default_channel_mapping(cfg->channel_map, channels);
+
+ switch (dai->id) {
+ case DISPLAY_PORT_RX_0:
+ cfg->dp_idx = 0;
+ break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ cfg->dp_idx = dai->id - DISPLAY_PORT_RX_1 + 1;
+ break;
+ }
+
+ ret = q6dsp_get_channel_allocation(channels);
+ if (ret < 0)
+ return ret;
+
+ cfg->channel_allocation = ret;
+
+ return 0;
+}
+
+static int q6dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ int channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max;
+
+ cfg->bit_width = params_width(params);
+ cfg->sample_rate = params_rate(params);
+ cfg->num_channels = channels;
+ audioreach_set_default_channel_mapping(cfg->channel_map, channels);
+
+ return 0;
+}
+
+static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc;
+
+ if (dai_data->is_port_started[dai->id]) {
+ rc = q6apm_graph_stop(dai_data->graph[dai->id]);
+ dai_data->is_port_started[dai->id] = false;
+ if (rc < 0)
+ dev_err(dai->dev, "fail to close APM port (%d)\n", rc);
+ }
+
+ if (dai_data->graph[dai->id]) {
+ q6apm_graph_close(dai_data->graph[dai->id]);
+ dai_data->graph[dai->id] = NULL;
+ }
+}
+
+static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ struct q6apm_graph *graph;
+ int graph_id = dai->id;
+ int rc;
+
+ if (dai_data->is_port_started[dai->id]) {
+ q6apm_graph_stop(dai_data->graph[dai->id]);
+ dai_data->is_port_started[dai->id] = false;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ q6apm_graph_close(dai_data->graph[dai->id]);
+ dai_data->graph[dai->id] = NULL;
+ }
+ }
+
+ /**
+ * It is recommend to load DSP with source graph first and then sink
+ * graph, so sequence for playback and capture will be different
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id);
+ if (IS_ERR(graph)) {
+ dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id);
+ rc = PTR_ERR(graph);
+ return rc;
+ }
+ dai_data->graph[graph_id] = graph;
+ }
+
+ cfg->direction = substream->stream;
+ rc = q6apm_graph_media_format_pcm(dai_data->graph[dai->id], cfg);
+ if (rc) {
+ dev_err(dai->dev, "Failed to set media format %d\n", rc);
+ goto err;
+ }
+
+ rc = q6apm_graph_prepare(dai_data->graph[dai->id]);
+ if (rc) {
+ dev_err(dai->dev, "Failed to prepare Graph %d\n", rc);
+ goto err;
+ }
+
+ rc = q6apm_graph_start(dai_data->graph[dai->id]);
+ if (rc < 0) {
+ dev_err(dai->dev, "Failed to start APM port %d\n", dai->id);
+ goto err;
+ }
+ dai_data->is_port_started[dai->id] = true;
+
+ return 0;
+err:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ q6apm_graph_close(dai_data->graph[dai->id]);
+ dai_data->graph[dai->id] = NULL;
+ }
+ return rc;
+}
+
+static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct q6apm_graph *graph;
+ int graph_id = dai->id;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id);
+ if (IS_ERR(graph)) {
+ dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id);
+ return PTR_ERR(graph);
+ }
+ dai_data->graph[graph_id] = graph;
+ }
+
+ return 0;
+}
+
+static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+
+ cfg->fmt = fmt;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops q6dma_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .set_channel_map = q6dma_set_channel_map,
+ .hw_params = q6dma_hw_params,
+};
+
+static const struct snd_soc_dai_ops q6i2s_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .set_channel_map = q6dma_set_channel_map,
+ .hw_params = q6dma_hw_params,
+ .set_fmt = q6i2s_set_fmt,
+};
+
+static const struct snd_soc_dai_ops q6hdmi_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .hw_params = q6hdmi_hw_params,
+ .set_fmt = q6i2s_set_fmt,
+};
+
+static const struct snd_soc_component_driver q6apm_lpass_dai_component = {
+ .name = "q6apm-be-dai-component",
+ .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name,
+ .be_pcm_base = AUDIOREACH_BE_PCM_BASE,
+ .use_dai_pcm_id = true,
+};
+
+static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
+{
+ struct q6dsp_audio_port_dai_driver_config cfg;
+ struct q6apm_lpass_dai_data *dai_data;
+ struct snd_soc_dai_driver *dais;
+ struct device *dev = &pdev->dev;
+ int num_dais;
+
+ dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
+ if (!dai_data)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, dai_data);
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.q6i2s_ops = &q6i2s_ops;
+ cfg.q6dma_ops = &q6dma_ops;
+ cfg.q6hdmi_ops = &q6hdmi_ops;
+ dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
+
+ return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6apm_lpass_dai_device_id[] = {
+ { .compatible = "qcom,q6apm-lpass-dais" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6apm_lpass_dai_device_id);
+#endif
+
+static struct platform_driver q6apm_lpass_dai_platform_driver = {
+ .driver = {
+ .name = "q6apm-lpass-dais",
+ .of_match_table = of_match_ptr(q6apm_lpass_dai_device_id),
+ },
+ .probe = q6apm_lpass_dai_dev_probe,
+};
+module_platform_driver(q6apm_lpass_dai_platform_driver);
+
+MODULE_DESCRIPTION("AUDIOREACH APM LPASS dai driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
new file mode 100644
index 000000000000..94cc6376a367
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <dt-bindings/soc/qcom,gpr.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/wait.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include "audioreach.h"
+#include "q6apm.h"
+
+/* Graph Management */
+struct apm_graph_mgmt_cmd {
+ struct apm_module_param_data param_data;
+ uint32_t num_sub_graphs;
+ uint32_t sub_graph_id_list[];
+} __packed;
+
+#define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
+
+static struct q6apm *g_apm;
+
+int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
+{
+ gpr_device_t *gdev = apm->gdev;
+
+ return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
+ NULL, &apm->wait, pkt, rsp_opcode);
+}
+
+static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, uint32_t graph_id)
+{
+ struct audioreach_graph_info *info;
+ struct audioreach_graph *graph;
+ int id;
+
+ mutex_lock(&apm->lock);
+ graph = idr_find(&apm->graph_idr, graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (graph) {
+ kref_get(&graph->refcount);
+ return graph;
+ }
+
+ info = idr_find(&apm->graph_info_idr, graph_id);
+
+ if (!info)
+ return ERR_PTR(-ENODEV);
+
+ graph = kzalloc(sizeof(*graph), GFP_KERNEL);
+ if (!graph)
+ return ERR_PTR(-ENOMEM);
+
+ graph->apm = apm;
+ graph->info = info;
+ graph->id = graph_id;
+
+ graph->graph = audioreach_alloc_graph_pkt(apm, info);
+ if (IS_ERR(graph->graph)) {
+ void *err = graph->graph;
+
+ kfree(graph);
+ return ERR_CAST(err);
+ }
+
+ mutex_lock(&apm->lock);
+ id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id);
+ kfree(graph->graph);
+ kfree(graph);
+ mutex_unlock(&apm->lock);
+ return ERR_PTR(id);
+ }
+ mutex_unlock(&apm->lock);
+
+ kref_init(&graph->refcount);
+
+ q6apm_send_cmd_sync(apm, graph->graph, 0);
+
+ return graph;
+}
+
+static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
+{
+ struct audioreach_graph_info *info = graph->info;
+ int num_sub_graphs = info->num_sub_graphs;
+ struct apm_module_param_data *param_data;
+ struct apm_graph_mgmt_cmd *mgmt_cmd;
+ struct audioreach_sub_graph *sg;
+ struct q6apm *apm = graph->apm;
+ int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ mgmt_cmd->num_sub_graphs = num_sub_graphs;
+
+ param_data = &mgmt_cmd->param_data;
+ param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
+ param_data->param_id = APM_PARAM_ID_SUB_GRAPH_LIST;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ list_for_each_entry(sg, &info->sg_list, node)
+ mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id;
+
+ return q6apm_send_cmd_sync(apm, pkt, 0);
+}
+
+static void q6apm_put_audioreach_graph(struct kref *ref)
+{
+ struct audioreach_graph *graph;
+ struct q6apm *apm;
+
+ graph = container_of(ref, struct audioreach_graph, refcount);
+ apm = graph->apm;
+
+ audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
+
+ mutex_lock(&apm->lock);
+ graph = idr_remove(&apm->graph_idr, graph->id);
+ mutex_unlock(&apm->lock);
+
+ kfree(graph->graph);
+ kfree(graph);
+}
+
+
+static int q6apm_get_apm_state(struct q6apm *apm)
+{
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(0,
+ APM_CMD_GET_SPF_STATE, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
+
+ return apm->state;
+}
+
+bool q6apm_is_adsp_ready(void)
+{
+ if (g_apm)
+ return q6apm_get_apm_state(g_apm);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready);
+
+static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
+ struct audioreach_graph_info *info,
+ uint32_t mid)
+{
+ struct audioreach_container *container;
+ struct audioreach_sub_graph *sgs;
+ struct audioreach_module *module;
+
+ list_for_each_entry(sgs, &info->sg_list, node) {
+ list_for_each_entry(container, &sgs->container_list, node) {
+ list_for_each_entry(module, &container->modules_list, node) {
+ if (mid == module->module_id)
+ return module;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
+ struct audioreach_module_config *cfg)
+{
+ struct audioreach_module *module;
+
+ if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
+ else
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
+
+ if (!module)
+ return -ENODEV;
+
+ audioreach_set_media_format(graph, module, cfg);
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
+
+int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
+ size_t period_sz, unsigned int periods)
+{
+ struct audioreach_graph_data *data;
+ struct audio_buffer *buf;
+ int cnt;
+ int rc;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ mutex_lock(&graph->lock);
+
+ if (data->buf) {
+ mutex_unlock(&graph->lock);
+ return 0;
+ }
+
+ buf = kcalloc(periods, sizeof(struct audio_buffer), GFP_KERNEL);
+ if (!buf) {
+ mutex_unlock(&graph->lock);
+ return -ENOMEM;
+ }
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ data->buf = buf;
+
+ buf[0].phys = phys;
+ buf[0].size = period_sz;
+
+ for (cnt = 1; cnt < periods; cnt++) {
+ if (period_sz > 0) {
+ buf[cnt].phys = buf[0].phys + (cnt * period_sz);
+ buf[cnt].size = period_sz;
+ }
+ }
+ data->num_periods = periods;
+
+ mutex_unlock(&graph->lock);
+
+ rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
+ if (rc < 0) {
+ dev_err(graph->dev, "Memory_map_regions failed\n");
+ audioreach_graph_free_buf(graph);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
+
+int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
+{
+ struct apm_cmd_shared_mem_unmap_regions *cmd;
+ struct audioreach_graph_data *data;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+ int rc;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ if (!data->mem_map_handle)
+ return 0;
+
+ pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
+ graph->port->id);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ cmd = (void *)pkt + GPR_HDR_SIZE;
+ cmd->mem_map_handle = data->mem_map_handle;
+
+ rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
+
+ audioreach_graph_free_buf(graph);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
+
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples);
+}
+EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence);
+
+int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples);
+}
+EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence);
+
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en);
+}
+EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);
+
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph,
+ uint32_t codec_id)
+{
+ struct audioreach_module *module;
+ uint32_t module_id;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ switch (codec_id) {
+ case SND_AUDIOCODEC_MP3:
+ module_id = MODULE_ID_MP3_DECODE;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ module_id = MODULE_ID_AAC_DEC;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ module_id = MODULE_ID_FLAC_DEC;
+ break;
+ case SND_AUDIOCODEC_OPUS_RAW:
+ module_id = MODULE_ID_OPUS_DEC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID,
+ module_id);
+}
+EXPORT_SYMBOL_GPL(q6apm_set_real_module_id);
+
+int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
+{
+ struct audioreach_graph_info *info = graph->info;
+ struct audioreach_sub_graph *sgs;
+ struct audioreach_container *container;
+ struct audioreach_module *module;
+
+ list_for_each_entry(sgs, &info->sg_list, node) {
+ list_for_each_entry(container, &sgs->container_list, node) {
+ list_for_each_entry(module, &container->modules_list, node) {
+ if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||
+ (module->module_id == MODULE_ID_RD_SHARED_MEM_EP))
+ continue;
+
+ audioreach_set_media_format(graph, module, cfg);
+ }
+ }
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
+
+static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
+ if (!module)
+ return -ENODEV;
+
+ return module->instance_id;
+
+}
+
+int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
+ if (!module)
+ return -ENODEV;
+
+ return module->instance_id;
+
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
+
+int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t wflags)
+{
+ struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
+ struct audio_buffer *ab;
+ int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer),
+ DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
+ graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
+ graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ write_buffer = (void *)pkt + GPR_HDR_SIZE;
+
+ mutex_lock(&graph->lock);
+ ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
+
+ write_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
+ write_buffer->buf_addr_msw = upper_32_bits(ab->phys);
+ write_buffer->buf_size = len;
+ write_buffer->timestamp_lsw = lsw_ts;
+ write_buffer->timestamp_msw = msw_ts;
+ write_buffer->mem_map_handle = graph->rx_data.mem_map_handle;
+ write_buffer->flags = wflags;
+
+ graph->rx_data.dsp_buf++;
+
+ if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
+ graph->rx_data.dsp_buf = 0;
+
+ mutex_unlock(&graph->lock);
+
+ return gpr_send_port_pkt(graph->port, pkt);
+}
+EXPORT_SYMBOL_GPL(q6apm_write_async);
+
+int q6apm_read(struct q6apm_graph *graph)
+{
+ struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;
+ struct audioreach_graph_data *port;
+ struct audio_buffer *ab;
+ int iid = q6apm_graph_get_tx_shmem_module_iid(graph);
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer),
+ DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
+ graph->tx_data.dsp_buf, graph->port->id, iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ read_buffer = (void *)pkt + GPR_HDR_SIZE;
+
+ mutex_lock(&graph->lock);
+ port = &graph->tx_data;
+ ab = &port->buf[port->dsp_buf];
+
+ read_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
+ read_buffer->buf_addr_msw = upper_32_bits(ab->phys);
+ read_buffer->mem_map_handle = port->mem_map_handle;
+ read_buffer->buf_size = ab->size;
+
+ port->dsp_buf++;
+
+ if (port->dsp_buf >= port->num_periods)
+ port->dsp_buf = 0;
+
+ mutex_unlock(&graph->lock);
+
+ return gpr_send_port_pkt(graph->port, pkt);
+}
+EXPORT_SYMBOL_GPL(q6apm_read);
+
+int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir)
+{
+ struct audioreach_graph_data *data;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ return (int)atomic_read(&data->hw_ptr);
+}
+EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer);
+
+static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
+ struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
+ struct apm_cmd_rsp_shared_mem_map_regions *rsp;
+ struct gpr_ibasic_rsp_result_t *result;
+ struct q6apm_graph *graph = priv;
+ struct gpr_hdr *hdr = &data->hdr;
+ struct device *dev = graph->dev;
+ uint32_t client_event;
+ phys_addr_t phys;
+ int token;
+
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
+ if (!graph->ar_graph)
+ break;
+ client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
+ mutex_lock(&graph->lock);
+ token = hdr->token & APM_WRITE_TOKEN_MASK;
+
+ done = data->payload;
+ phys = graph->rx_data.buf[token].phys;
+ mutex_unlock(&graph->lock);
+ /* token numbering starts at 0 */
+ atomic_set(&graph->rx_data.hw_ptr, token + 1);
+ if (lower_32_bits(phys) == done->buf_addr_lsw &&
+ upper_32_bits(phys) == done->buf_addr_msw) {
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = done->status;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
+ } else {
+ dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
+ done->buf_addr_msw);
+ }
+
+ break;
+ case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = 0;
+ rsp = data->payload;
+
+ if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
+ graph->rx_data.mem_map_handle = rsp->mem_map_handle;
+ else
+ graph->tx_data.mem_map_handle = rsp->mem_map_handle;
+
+ wake_up(&graph->cmd_wait);
+ break;
+ case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
+ if (!graph->ar_graph)
+ break;
+ client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
+ mutex_lock(&graph->lock);
+ rd_done = data->payload;
+ phys = graph->tx_data.buf[hdr->token].phys;
+ mutex_unlock(&graph->lock);
+ /* token numbering starts at 0 */
+ atomic_set(&graph->tx_data.hw_ptr, hdr->token + 1);
+
+ if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
+ lower_32_bits(phys) == rd_done->buf_addr_lsw) {
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = rd_done->status;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
+ } else {
+ dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
+ rd_done->buf_addr_msw);
+ }
+ break;
+ case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
+ client_event = APM_CLIENT_EVENT_CMD_EOS_DONE;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
+ break;
+ case GPR_BASIC_RSP_RESULT:
+ switch (result->opcode) {
+ case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ graph->result.opcode = result->opcode;
+ graph->result.status = 0;
+ if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
+ graph->rx_data.mem_map_handle = 0;
+ else
+ graph->tx_data.mem_map_handle = 0;
+
+ wake_up(&graph->cmd_wait);
+ break;
+ case APM_CMD_SHARED_MEM_MAP_REGIONS:
+ case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
+ case APM_CMD_SET_CFG:
+ graph->result.opcode = result->opcode;
+ graph->result.status = result->status;
+ if (result->status)
+ dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",
+ result->status, result->opcode);
+ wake_up(&graph->cmd_wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
+ void *priv, int graph_id)
+{
+ struct q6apm *apm = dev_get_drvdata(dev->parent);
+ struct audioreach_graph *ar_graph;
+ struct q6apm_graph *graph;
+ int ret;
+
+ ar_graph = q6apm_get_audioreach_graph(apm, graph_id);
+ if (IS_ERR(ar_graph)) {
+ dev_err(dev, "No graph found with id %d\n", graph_id);
+ return ERR_CAST(ar_graph);
+ }
+
+ graph = kzalloc(sizeof(*graph), GFP_KERNEL);
+ if (!graph) {
+ ret = -ENOMEM;
+ goto put_ar_graph;
+ }
+
+ graph->apm = apm;
+ graph->priv = priv;
+ graph->cb = cb;
+ graph->info = ar_graph->info;
+ graph->ar_graph = ar_graph;
+ graph->id = ar_graph->id;
+ graph->dev = dev;
+
+ mutex_init(&graph->lock);
+ init_waitqueue_head(&graph->cmd_wait);
+
+ graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);
+ if (IS_ERR(graph->port)) {
+ ret = PTR_ERR(graph->port);
+ goto free_graph;
+ }
+
+ return graph;
+
+free_graph:
+ kfree(graph);
+put_ar_graph:
+ kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_open);
+
+int q6apm_graph_close(struct q6apm_graph *graph)
+{
+ struct audioreach_graph *ar_graph = graph->ar_graph;
+
+ graph->ar_graph = NULL;
+ kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
+ gpr_free_port(graph->port);
+ kfree(graph);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_close);
+
+int q6apm_graph_prepare(struct q6apm_graph *graph)
+{
+ return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
+
+int q6apm_graph_start(struct q6apm_graph *graph)
+{
+ struct audioreach_graph *ar_graph = graph->ar_graph;
+ int ret = 0;
+
+ if (ar_graph->start_count == 0)
+ ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
+
+ ar_graph->start_count++;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_start);
+
+int q6apm_graph_stop(struct q6apm_graph *graph)
+{
+ struct audioreach_graph *ar_graph = graph->ar_graph;
+
+ if (--ar_graph->start_count > 0)
+ return 0;
+
+ return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_stop);
+
+int q6apm_graph_flush(struct q6apm_graph *graph)
+{
+ return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
+}
+EXPORT_SYMBOL_GPL(q6apm_graph_flush);
+
+static int q6apm_audio_probe(struct snd_soc_component *component)
+{
+ return audioreach_tplg_init(component);
+}
+
+static void q6apm_audio_remove(struct snd_soc_component *component)
+{
+ /* remove topology */
+ snd_soc_tplg_component_remove(component);
+}
+
+#define APM_AUDIO_DRV_NAME "q6apm-audio"
+
+static const struct snd_soc_component_driver q6apm_audio_component = {
+ .name = APM_AUDIO_DRV_NAME,
+ .probe = q6apm_audio_probe,
+ .remove = q6apm_audio_remove,
+};
+
+static int apm_probe(gpr_device_t *gdev)
+{
+ struct device *dev = &gdev->dev;
+ struct q6apm *apm;
+ int ret;
+
+ apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
+ if (!apm)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, apm);
+
+ mutex_init(&apm->lock);
+ apm->dev = dev;
+ apm->gdev = gdev;
+ init_waitqueue_head(&apm->wait);
+
+ INIT_LIST_HEAD(&apm->widget_list);
+ idr_init(&apm->graph_idr);
+ idr_init(&apm->graph_info_idr);
+ idr_init(&apm->sub_graphs_idr);
+ idr_init(&apm->containers_idr);
+
+ idr_init(&apm->modules_idr);
+
+ g_apm = apm;
+
+ q6apm_get_apm_state(apm);
+
+ ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to register q6apm: %d\n", ret);
+ return ret;
+ }
+
+ return of_platform_populate(dev->of_node, NULL, NULL, dev);
+}
+
+struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
+{
+ struct audioreach_graph_info *info = graph->info;
+ struct q6apm *apm = graph->apm;
+
+ return __q6apm_find_module_by_mid(apm, info, mid);
+
+}
+
+static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ gpr_device_t *gdev = priv;
+ struct q6apm *apm = dev_get_drvdata(&gdev->dev);
+ struct device *dev = &gdev->dev;
+ struct gpr_ibasic_rsp_result_t *result;
+ struct gpr_hdr *hdr = &data->hdr;
+
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case APM_CMD_RSP_GET_SPF_STATE:
+ apm->result.opcode = hdr->opcode;
+ apm->result.status = 0;
+ /* First word of result it state */
+ apm->state = result->opcode;
+ wake_up(&apm->wait);
+ break;
+ case GPR_BASIC_RSP_RESULT:
+ switch (result->opcode) {
+ case APM_CMD_GRAPH_START:
+ case APM_CMD_GRAPH_OPEN:
+ case APM_CMD_GRAPH_PREPARE:
+ case APM_CMD_GRAPH_CLOSE:
+ case APM_CMD_GRAPH_FLUSH:
+ case APM_CMD_GRAPH_STOP:
+ case APM_CMD_SET_CFG:
+ apm->result.opcode = result->opcode;
+ apm->result.status = result->status;
+ if (result->status)
+ dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
+ result->opcode);
+ wake_up(&apm->wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id apm_device_id[] = {
+ { .compatible = "qcom,q6apm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, apm_device_id);
+#endif
+
+static gpr_driver_t apm_driver = {
+ .probe = apm_probe,
+ .gpr_callback = apm_callback,
+ .driver = {
+ .name = "qcom-apm",
+ .of_match_table = of_match_ptr(apm_device_id),
+ },
+};
+
+module_gpr_driver(apm_driver);
+MODULE_DESCRIPTION("Audio Process Manager");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
new file mode 100644
index 000000000000..7ce08b401e31
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __Q6APM_H__
+#define __Q6APM_H__
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <linux/of_platform.h>
+#include <linux/jiffies.h>
+#include <linux/soc/qcom/apr.h>
+#include "audioreach.h"
+
+#define APM_PORT_MAX 127
+#define APM_PORT_MAX_AUDIO_CHAN_CNT 8
+#define PCM_CHANNEL_NULL 0
+#define PCM_CHANNEL_FL 1 /* Front left channel. */
+#define PCM_CHANNEL_FR 2 /* Front right channel. */
+#define PCM_CHANNEL_FC 3 /* Front center channel. */
+#define PCM_CHANNEL_LS 4 /* Left surround channel. */
+#define PCM_CHANNEL_RS 5 /* Right surround channel. */
+#define PCM_CHANNEL_LFE 6 /* Low frequency effect channel. */
+#define PCM_CHANNEL_CS 7 /* Center surround channel; Rear center ch */
+#define PCM_CHANNEL_LB 8 /* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_RB 9 /* Right back channel; Rear right channel. */
+#define PCM_CHANNELS 10 /* Top surround channel. */
+
+#define APM_TIMESTAMP_FLAG 0x80000000
+#define FORMAT_LINEAR_PCM 0x0000
+/* APM client callback events */
+#define APM_CMD_EOS 0x0003
+#define APM_CLIENT_EVENT_CMD_EOS_DONE 0x1003
+#define APM_CMD_CLOSE 0x0004
+#define APM_CLIENT_EVENT_CMD_CLOSE_DONE 0x1004
+#define APM_CLIENT_EVENT_CMD_RUN_DONE 0x1008
+#define APM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009
+#define APM_CLIENT_EVENT_DATA_READ_DONE 0x100a
+#define APM_WRITE_TOKEN_MASK GENMASK(15, 0)
+#define APM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16)
+#define APM_WRITE_TOKEN_LEN_SHIFT 16
+
+#define APM_MAX_SESSIONS 8
+#define APM_LAST_BUFFER_FLAG BIT(30)
+#define NO_TIMESTAMP 0xFF00
+
+struct q6apm {
+ struct device *dev;
+ gpr_port_t *port;
+ gpr_device_t *gdev;
+ /* For Graph OPEN/START/STOP/CLOSE operations */
+ wait_queue_head_t wait;
+ struct gpr_ibasic_rsp_result_t result;
+
+ struct mutex cmd_lock;
+ struct mutex lock;
+ uint32_t state;
+
+ struct list_head widget_list;
+ struct idr graph_idr;
+ struct idr graph_info_idr;
+ struct idr sub_graphs_idr;
+ struct idr containers_idr;
+ struct idr modules_idr;
+};
+
+struct audio_buffer {
+ phys_addr_t phys;
+ uint32_t size; /* size of buffer */
+};
+
+struct audioreach_graph_data {
+ struct audio_buffer *buf;
+ uint32_t num_periods;
+ uint32_t dsp_buf;
+ uint32_t mem_map_handle;
+ atomic_t hw_ptr;
+};
+
+struct audioreach_graph {
+ struct audioreach_graph_info *info;
+ uint32_t id;
+ int state;
+ int start_count;
+ /* Cached Graph data */
+ void *graph;
+ struct kref refcount;
+ struct q6apm *apm;
+};
+
+typedef void (*q6apm_cb) (uint32_t opcode, uint32_t token,
+ void *payload, void *priv);
+struct q6apm_graph {
+ void *priv;
+ q6apm_cb cb;
+ uint32_t id;
+ struct device *dev;
+ struct q6apm *apm;
+ gpr_port_t *port;
+ struct audioreach_graph_data rx_data;
+ struct audioreach_graph_data tx_data;
+ struct gpr_ibasic_rsp_result_t result;
+ wait_queue_head_t cmd_wait;
+ struct mutex lock;
+ struct audioreach_graph *ar_graph;
+ struct audioreach_graph_info *info;
+};
+
+/* Graph Operations */
+struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
+ void *priv, int graph_id);
+int q6apm_graph_close(struct q6apm_graph *graph);
+int q6apm_graph_prepare(struct q6apm_graph *graph);
+int q6apm_graph_start(struct q6apm_graph *graph);
+int q6apm_graph_stop(struct q6apm_graph *graph);
+int q6apm_graph_flush(struct q6apm_graph *graph);
+
+/* Media Format */
+int q6apm_graph_media_format_pcm(struct q6apm_graph *graph,
+ struct audioreach_module_config *cfg);
+
+int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
+ struct audioreach_module_config *cfg);
+
+/* read/write related */
+int q6apm_read(struct q6apm_graph *graph);
+int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t wflags);
+
+/* Memory Map related */
+int q6apm_map_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir, phys_addr_t phys,
+ size_t period_sz, unsigned int periods);
+int q6apm_unmap_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir);
+/* Helpers */
+int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
+ uint32_t rsp_opcode);
+
+/* Callback for graph specific */
+struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph,
+ uint32_t mid);
+int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
+
+bool q6apm_is_adsp_ready(void);
+
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en);
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
+int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id);
+int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir);
+#endif /* __APM_GRAPH_ */
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
new file mode 100644
index 000000000000..709b4f3318ff
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -0,0 +1,1343 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6asm.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <linux/spinlock.h>
+#include <sound/compress_driver.h>
+#include <asm/div64.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <sound/pcm_params.h>
+#include "q6asm.h"
+#include "q6routing.h"
+#include "q6dsp-errno.h"
+
+#define DRV_NAME "q6asm-fe-dai"
+
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 65536
+#define PLAYBACK_MIN_PERIOD_SIZE 128
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 4096
+#define CAPTURE_MIN_PERIOD_SIZE 320
+#define SID_MASK_DEFAULT 0xF
+
+/* Default values used if user space does not set */
+#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
+#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
+#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
+#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
+
+#define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1)
+#define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2)
+
+enum stream_state {
+ Q6ASM_STREAM_IDLE = 0,
+ Q6ASM_STREAM_STOPPED,
+ Q6ASM_STREAM_RUNNING,
+};
+
+struct q6asm_dai_rtd {
+ struct snd_pcm_substream *substream;
+ struct snd_compr_stream *cstream;
+ struct snd_codec codec;
+ struct snd_dma_buffer dma_buffer;
+ spinlock_t lock;
+ phys_addr_t phys;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int periods;
+ uint64_t bytes_sent;
+ uint64_t bytes_received;
+ uint64_t copied_total;
+ uint16_t bits_per_sample;
+ snd_pcm_uframes_t queue_ptr;
+ uint16_t source; /* Encoding source bit mask */
+ struct audio_client *audio_client;
+ uint32_t next_track_stream_id;
+ bool next_track;
+ uint32_t stream_id;
+ uint16_t session_id;
+ enum stream_state state;
+ uint32_t initial_samples_drop;
+ uint32_t trailing_samples_drop;
+ bool notify_on_drain;
+};
+
+struct q6asm_dai_data {
+ struct snd_soc_dai_driver *dais;
+ int num_dais;
+ long long int sid;
+};
+
+static const struct snd_pcm_hardware q6asm_dai_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
+ CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static const struct snd_pcm_hardware q6asm_dai_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS *
+ PLAYBACK_MAX_PERIOD_SIZE),
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+#define Q6ASM_FEDAI_DRIVER(num) { \
+ .playback = { \
+ .stream_name = "MultiMedia"#num" Playback", \
+ .rates = (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000), \
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE), \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 192000, \
+ }, \
+ .capture = { \
+ .stream_name = "MultiMedia"#num" Capture", \
+ .rates = (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000), \
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE), \
+ .channels_min = 1, \
+ .channels_max = 4, \
+ .rate_min = 8000, \
+ .rate_max = 48000, \
+ }, \
+ .name = "MultiMedia"#num, \
+ .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \
+ }
+
+static const struct snd_compr_codec_caps q6asm_compr_caps = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000 },
+ .descriptor[0].num_sample_rates = 13,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 128,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
+static void event_handler(uint32_t opcode, uint32_t token,
+ void *payload, void *priv)
+{
+ struct q6asm_dai_rtd *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+
+ switch (opcode) {
+ case ASM_CLIENT_EVENT_CMD_RUN_DONE:
+ break;
+ case ASM_CLIENT_EVENT_CMD_EOS_DONE:
+ prtd->state = Q6ASM_STREAM_STOPPED;
+ break;
+ case ASM_CLIENT_EVENT_DATA_WRITE_DONE: {
+ snd_pcm_period_elapsed(substream);
+ break;
+ }
+ case ASM_CLIENT_EVENT_DATA_READ_DONE:
+ snd_pcm_period_elapsed(substream);
+ if (prtd->state == Q6ASM_STREAM_RUNNING)
+ q6asm_read(prtd->audio_client, prtd->stream_id);
+
+ break;
+ default:
+ break;
+ }
+}
+
+static int q6asm_dai_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ struct q6asm_dai_data *pdata;
+ struct device *dev = component->dev;
+ int ret, i;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ if (!prtd || !prtd->audio_client) {
+ dev_err(dev, "%s: private data null or audio client freed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ /* rate and channels are sent to audio driver */
+ if (prtd->state == Q6ASM_STREAM_RUNNING) {
+ /* clear the previous setup if any */
+ q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
+ q6asm_unmap_memory_regions(substream->stream,
+ prtd->audio_client);
+ q6routing_stream_close(soc_prtd->dai_link->id,
+ substream->stream);
+ prtd->state = Q6ASM_STREAM_STOPPED;
+ }
+
+ ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client,
+ prtd->phys,
+ (prtd->pcm_size / prtd->periods),
+ prtd->periods);
+
+ if (ret < 0) {
+ dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n",
+ ret);
+ return -ENOMEM;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = q6asm_open_write(prtd->audio_client, prtd->stream_id,
+ FORMAT_LINEAR_PCM,
+ 0, prtd->bits_per_sample, false);
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = q6asm_open_read(prtd->audio_client, prtd->stream_id,
+ FORMAT_LINEAR_PCM,
+ prtd->bits_per_sample);
+ }
+
+ if (ret < 0) {
+ dev_err(dev, "%s: q6asm_open_write failed\n", __func__);
+ goto open_err;
+ }
+
+ prtd->session_id = q6asm_get_session_id(prtd->audio_client);
+ ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE,
+ prtd->session_id, substream->stream);
+ if (ret) {
+ dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret);
+ goto routing_err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = q6asm_media_format_block_multi_ch_pcm(
+ prtd->audio_client, prtd->stream_id,
+ runtime->rate, runtime->channels, NULL,
+ prtd->bits_per_sample);
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client,
+ prtd->stream_id,
+ runtime->rate,
+ runtime->channels,
+ prtd->bits_per_sample);
+
+ /* Queue the buffers */
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client, prtd->stream_id);
+
+ }
+ if (ret < 0)
+ dev_info(dev, "%s: CMD Format block failed\n", __func__);
+ else
+ prtd->state = Q6ASM_STREAM_RUNNING;
+
+ return ret;
+
+routing_err:
+ q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
+open_err:
+ q6asm_unmap_memory_regions(substream->stream, prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+
+ return ret;
+}
+
+static int q6asm_dai_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ int i, ret = 0, avail_periods;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && prtd->state == Q6ASM_STREAM_RUNNING) {
+ avail_periods = (runtime->control->appl_ptr - prtd->queue_ptr)/runtime->period_size;
+ for (i = 0; i < avail_periods; i++) {
+ ret = q6asm_write_async(prtd->audio_client, prtd->stream_id,
+ prtd->pcm_count, 0, 0, 0);
+
+ if (ret < 0) {
+ dev_err(component->dev, "Error queuing playback buffer %d\n", ret);
+ return ret;
+ }
+ prtd->queue_ptr += runtime->period_size;
+ }
+ }
+
+ return ret;
+}
+
+static int q6asm_dai_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id,
+ 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ prtd->state = Q6ASM_STREAM_STOPPED;
+ ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
+ CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
+ CMD_PAUSE);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6asm_dai_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0);
+ struct q6asm_dai_rtd *prtd;
+ struct q6asm_dai_data *pdata;
+ struct device *dev = component->dev;
+ int ret = 0;
+ int stream_id;
+
+ stream_id = cpu_dai->driver->id;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata) {
+ dev_err(dev, "Drv data not found ..\n");
+ return -EINVAL;
+ }
+
+ prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(dev,
+ (q6asm_cb)event_handler, prtd, stream_id,
+ LEGACY_PCM_MODE);
+ if (IS_ERR(prtd->audio_client)) {
+ dev_info(dev, "%s: Could not allocate memory\n", __func__);
+ ret = PTR_ERR(prtd->audio_client);
+ kfree(prtd);
+ return ret;
+ }
+
+ /* DSP expects stream id from 1 */
+ prtd->stream_id = 1;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = q6asm_dai_hardware_playback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = q6asm_dai_hardware_capture;
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_info(dev, "snd_pcm_hw_constraint_integer failed\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
+ PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);
+ if (ret < 0) {
+ dev_err(dev, "constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 480);
+ if (ret < 0) {
+ dev_err(dev, "constraint for period bytes step ret = %d\n",
+ ret);
+ }
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 480);
+ if (ret < 0) {
+ dev_err(dev, "constraint for buffer bytes step ret = %d\n",
+ ret);
+ }
+
+ runtime->private_data = prtd;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback);
+ runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max;
+ } else {
+ snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_capture);
+ runtime->dma_bytes = q6asm_dai_hardware_capture.buffer_bytes_max;
+ }
+
+ if (pdata->sid < 0)
+ prtd->phys = substream->dma_buffer.addr;
+ else
+ prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
+
+ return 0;
+}
+
+static int q6asm_dai_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+ if (prtd->audio_client) {
+ if (prtd->state)
+ q6asm_cmd(prtd->audio_client, prtd->stream_id,
+ CMD_CLOSE);
+
+ q6asm_unmap_memory_regions(substream->stream,
+ prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ }
+ q6routing_stream_close(soc_prtd->dai_link->id,
+ substream->stream);
+ kfree(prtd);
+ return 0;
+}
+
+static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ snd_pcm_uframes_t ptr;
+
+ ptr = q6asm_get_hw_pointer(prtd->audio_client, substream->stream) * runtime->period_size;
+ if (ptr)
+ return ptr - 1;
+
+ return 0;
+}
+
+static int q6asm_dai_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+ prtd->pcm_size = params_buffer_bytes(params);
+ prtd->periods = params_periods(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ prtd->bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ prtd->bits_per_sample = 24;
+ break;
+ }
+
+ return 0;
+}
+
+static void compress_event_handler(uint32_t opcode, uint32_t token,
+ void *payload, void *priv)
+{
+ struct q6asm_dai_rtd *prtd = priv;
+ struct snd_compr_stream *substream = prtd->cstream;
+ u32 wflags = 0;
+ uint64_t avail;
+ uint32_t bytes_written, bytes_to_write;
+ bool is_last_buffer = false;
+
+ guard(spinlock_irqsave)(&prtd->lock);
+
+ switch (opcode) {
+ case ASM_CLIENT_EVENT_CMD_RUN_DONE:
+ if (!prtd->bytes_sent) {
+ q6asm_stream_remove_initial_silence(prtd->audio_client,
+ prtd->stream_id,
+ prtd->initial_samples_drop);
+
+ q6asm_write_async(prtd->audio_client, prtd->stream_id,
+ prtd->pcm_count, 0, 0, 0);
+ prtd->bytes_sent += prtd->pcm_count;
+ }
+
+ break;
+
+ case ASM_CLIENT_EVENT_CMD_EOS_DONE:
+ if (prtd->notify_on_drain) {
+ if (substream->partial_drain) {
+ /*
+ * Close old stream and make it stale, switch
+ * the active stream now!
+ */
+ q6asm_cmd_nowait(prtd->audio_client,
+ prtd->stream_id,
+ CMD_CLOSE);
+ /*
+ * vaild stream ids start from 1, So we are
+ * toggling this between 1 and 2.
+ */
+ prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1);
+ }
+
+ snd_compr_drain_notify(prtd->cstream);
+ prtd->notify_on_drain = false;
+
+ } else {
+ prtd->state = Q6ASM_STREAM_STOPPED;
+ }
+ break;
+
+ case ASM_CLIENT_EVENT_DATA_WRITE_DONE:
+
+ bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT;
+ prtd->copied_total += bytes_written;
+ snd_compr_fragment_elapsed(substream);
+
+ if (prtd->state != Q6ASM_STREAM_RUNNING)
+ break;
+
+ avail = prtd->bytes_received - prtd->bytes_sent;
+ if (avail > prtd->pcm_count) {
+ bytes_to_write = prtd->pcm_count;
+ } else {
+ if (substream->partial_drain || prtd->notify_on_drain)
+ is_last_buffer = true;
+ bytes_to_write = avail;
+ }
+
+ if (bytes_to_write) {
+ if (substream->partial_drain && is_last_buffer) {
+ wflags |= ASM_LAST_BUFFER_FLAG;
+ q6asm_stream_remove_trailing_silence(prtd->audio_client,
+ prtd->stream_id,
+ prtd->trailing_samples_drop);
+ }
+
+ q6asm_write_async(prtd->audio_client, prtd->stream_id,
+ bytes_to_write, 0, 0, wflags);
+
+ prtd->bytes_sent += bytes_to_write;
+ }
+
+ if (prtd->notify_on_drain && is_last_buffer)
+ q6asm_cmd_nowait(prtd->audio_client,
+ prtd->stream_id, CMD_EOS);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int q6asm_dai_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct q6asm_dai_data *pdata;
+ struct device *dev = component->dev;
+ struct q6asm_dai_rtd *prtd;
+ int stream_id, size, ret;
+
+ stream_id = cpu_dai->driver->id;
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata) {
+ dev_err(dev, "Drv data not found ..\n");
+ return -EINVAL;
+ }
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ /* DSP expects stream id from 1 */
+ prtd->stream_id = 1;
+
+ prtd->cstream = stream;
+ prtd->audio_client = q6asm_audio_client_alloc(dev,
+ (q6asm_cb)compress_event_handler,
+ prtd, stream_id, LEGACY_PCM_MODE);
+ if (IS_ERR(prtd->audio_client)) {
+ dev_err(dev, "Could not allocate memory\n");
+ ret = PTR_ERR(prtd->audio_client);
+ goto free_prtd;
+ }
+
+ size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
+ COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size,
+ &prtd->dma_buffer);
+ if (ret) {
+ dev_err(dev, "Cannot allocate buffer(s)\n");
+ goto free_client;
+ }
+
+ if (pdata->sid < 0)
+ prtd->phys = prtd->dma_buffer.addr;
+ else
+ prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);
+
+ snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);
+ spin_lock_init(&prtd->lock);
+ runtime->private_data = prtd;
+
+ return 0;
+
+free_client:
+ q6asm_audio_client_free(prtd->audio_client);
+free_prtd:
+ kfree(prtd);
+
+ return ret;
+}
+
+static int q6asm_dai_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+
+ if (prtd->audio_client) {
+ if (prtd->state) {
+ q6asm_cmd(prtd->audio_client, prtd->stream_id,
+ CMD_CLOSE);
+ if (prtd->next_track_stream_id) {
+ q6asm_cmd(prtd->audio_client,
+ prtd->next_track_stream_id,
+ CMD_CLOSE);
+ }
+ }
+
+ snd_dma_free_pages(&prtd->dma_buffer);
+ q6asm_unmap_memory_regions(stream->direction,
+ prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ }
+ q6routing_stream_close(rtd->dai_link->id, stream->direction);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_codec *codec,
+ int stream_id)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ struct q6asm_flac_cfg flac_cfg;
+ struct q6asm_wma_cfg wma_cfg;
+ struct q6asm_alac_cfg alac_cfg;
+ struct q6asm_ape_cfg ape_cfg;
+ unsigned int wma_v9 = 0;
+ struct device *dev = component->dev;
+ int ret;
+ union snd_codec_options *codec_options;
+ struct snd_dec_flac *flac;
+ struct snd_dec_wma *wma;
+ struct snd_dec_alac *alac;
+ struct snd_dec_ape *ape;
+
+ codec_options = &(prtd->codec.options);
+
+ memcpy(&prtd->codec, codec, sizeof(*codec));
+
+ switch (codec->id) {
+ case SND_AUDIOCODEC_FLAC:
+
+ memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg));
+ flac = &codec_options->flac_d;
+
+ flac_cfg.ch_cfg = codec->ch_in;
+ flac_cfg.sample_rate = codec->sample_rate;
+ flac_cfg.stream_info_present = 1;
+ flac_cfg.sample_size = flac->sample_size;
+ flac_cfg.min_blk_size = flac->min_blk_size;
+ flac_cfg.max_blk_size = flac->max_blk_size;
+ flac_cfg.max_frame_size = flac->max_frame_size;
+ flac_cfg.min_frame_size = flac->min_frame_size;
+
+ ret = q6asm_stream_media_format_block_flac(prtd->audio_client,
+ stream_id,
+ &flac_cfg);
+ if (ret < 0) {
+ dev_err(dev, "FLAC CMD Format block failed:%d\n", ret);
+ return -EIO;
+ }
+ break;
+
+ case SND_AUDIOCODEC_WMA:
+ wma = &codec_options->wma_d;
+
+ memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg));
+
+ wma_cfg.sample_rate = codec->sample_rate;
+ wma_cfg.num_channels = codec->ch_in;
+ wma_cfg.bytes_per_sec = codec->bit_rate / 8;
+ wma_cfg.block_align = codec->align;
+ wma_cfg.bits_per_sample = prtd->bits_per_sample;
+ wma_cfg.enc_options = wma->encoder_option;
+ wma_cfg.adv_enc_options = wma->adv_encoder_option;
+ wma_cfg.adv_enc_options2 = wma->adv_encoder_option2;
+
+ if (wma_cfg.num_channels == 1)
+ wma_cfg.channel_mask = 4; /* Mono Center */
+ else if (wma_cfg.num_channels == 2)
+ wma_cfg.channel_mask = 3; /* Stereo FL/FR */
+ else
+ return -EINVAL;
+
+ /* check the codec profile */
+ switch (codec->profile) {
+ case SND_AUDIOPROFILE_WMA9:
+ wma_cfg.fmtag = 0x161;
+ wma_v9 = 1;
+ break;
+
+ case SND_AUDIOPROFILE_WMA10:
+ wma_cfg.fmtag = 0x166;
+ break;
+
+ case SND_AUDIOPROFILE_WMA9_PRO:
+ wma_cfg.fmtag = 0x162;
+ break;
+
+ case SND_AUDIOPROFILE_WMA9_LOSSLESS:
+ wma_cfg.fmtag = 0x163;
+ break;
+
+ case SND_AUDIOPROFILE_WMA10_LOSSLESS:
+ wma_cfg.fmtag = 0x167;
+ break;
+
+ default:
+ dev_err(dev, "Unknown WMA profile:%x\n",
+ codec->profile);
+ return -EIO;
+ }
+
+ if (wma_v9)
+ ret = q6asm_stream_media_format_block_wma_v9(
+ prtd->audio_client, stream_id,
+ &wma_cfg);
+ else
+ ret = q6asm_stream_media_format_block_wma_v10(
+ prtd->audio_client, stream_id,
+ &wma_cfg);
+ if (ret < 0) {
+ dev_err(dev, "WMA9 CMD failed:%d\n", ret);
+ return -EIO;
+ }
+ break;
+
+ case SND_AUDIOCODEC_ALAC:
+ memset(&alac_cfg, 0x0, sizeof(alac_cfg));
+ alac = &codec_options->alac_d;
+
+ alac_cfg.sample_rate = codec->sample_rate;
+ alac_cfg.avg_bit_rate = codec->bit_rate;
+ alac_cfg.bit_depth = prtd->bits_per_sample;
+ alac_cfg.num_channels = codec->ch_in;
+
+ alac_cfg.frame_length = alac->frame_length;
+ alac_cfg.pb = alac->pb;
+ alac_cfg.mb = alac->mb;
+ alac_cfg.kb = alac->kb;
+ alac_cfg.max_run = alac->max_run;
+ alac_cfg.compatible_version = alac->compatible_version;
+ alac_cfg.max_frame_bytes = alac->max_frame_bytes;
+
+ switch (codec->ch_in) {
+ case 1:
+ alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO;
+ break;
+ case 2:
+ alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_STEREO;
+ break;
+ }
+ ret = q6asm_stream_media_format_block_alac(prtd->audio_client,
+ stream_id,
+ &alac_cfg);
+ if (ret < 0) {
+ dev_err(dev, "ALAC CMD Format block failed:%d\n", ret);
+ return -EIO;
+ }
+ break;
+
+ case SND_AUDIOCODEC_APE:
+ memset(&ape_cfg, 0x0, sizeof(ape_cfg));
+ ape = &codec_options->ape_d;
+
+ ape_cfg.sample_rate = codec->sample_rate;
+ ape_cfg.num_channels = codec->ch_in;
+ ape_cfg.bits_per_sample = prtd->bits_per_sample;
+
+ ape_cfg.compatible_version = ape->compatible_version;
+ ape_cfg.compression_level = ape->compression_level;
+ ape_cfg.format_flags = ape->format_flags;
+ ape_cfg.blocks_per_frame = ape->blocks_per_frame;
+ ape_cfg.final_frame_blocks = ape->final_frame_blocks;
+ ape_cfg.total_frames = ape->total_frames;
+ ape_cfg.seek_table_present = ape->seek_table_present;
+
+ ret = q6asm_stream_media_format_block_ape(prtd->audio_client,
+ stream_id,
+ &ape_cfg);
+ if (ret < 0) {
+ dev_err(dev, "APE CMD Format block failed:%d\n", ret);
+ return -EIO;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ int dir = stream->direction;
+ struct q6asm_dai_data *pdata;
+ struct device *dev = component->dev;
+ int ret;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ if (!prtd || !prtd->audio_client) {
+ dev_err(dev, "private data null or audio client freed\n");
+ return -EINVAL;
+ }
+
+ prtd->periods = runtime->fragments;
+ prtd->pcm_count = runtime->fragment_size;
+ prtd->pcm_size = runtime->fragments * runtime->fragment_size;
+ prtd->bits_per_sample = 16;
+
+ if (dir == SND_COMPRESS_PLAYBACK) {
+ ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id,
+ params->codec.profile, prtd->bits_per_sample,
+ true);
+
+ if (ret < 0) {
+ dev_err(dev, "q6asm_open_write failed\n");
+ goto open_err;
+ }
+ }
+
+ prtd->session_id = q6asm_get_session_id(prtd->audio_client);
+ ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE,
+ prtd->session_id, dir);
+ if (ret) {
+ dev_err(dev, "Stream reg failed ret:%d\n", ret);
+ goto q6_err;
+ }
+
+ ret = __q6asm_dai_compr_set_codec_params(component, stream,
+ &params->codec,
+ prtd->stream_id);
+ if (ret) {
+ dev_err(dev, "codec param setup failed ret:%d\n", ret);
+ goto q6_err;
+ }
+
+ ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys,
+ (prtd->pcm_size / prtd->periods),
+ prtd->periods);
+
+ if (ret < 0) {
+ dev_err(dev, "Buffer Mapping failed ret:%d\n", ret);
+ ret = -ENOMEM;
+ goto q6_err;
+ }
+
+ prtd->state = Q6ASM_STREAM_RUNNING;
+
+ return 0;
+
+q6_err:
+ q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
+
+open_err:
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return ret;
+}
+
+static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (metadata->key) {
+ case SNDRV_COMPRESS_ENCODER_PADDING:
+ prtd->trailing_samples_drop = metadata->value[0];
+ break;
+ case SNDRV_COMPRESS_ENCODER_DELAY:
+ prtd->initial_samples_drop = metadata->value[0];
+ if (prtd->next_track_stream_id) {
+ ret = q6asm_open_write(prtd->audio_client,
+ prtd->next_track_stream_id,
+ prtd->codec.id,
+ prtd->codec.profile,
+ prtd->bits_per_sample,
+ true);
+ if (ret < 0) {
+ dev_err(component->dev, "q6asm_open_write failed\n");
+ return ret;
+ }
+ ret = __q6asm_dai_compr_set_codec_params(component, stream,
+ &prtd->codec,
+ prtd->next_track_stream_id);
+ if (ret < 0) {
+ dev_err(component->dev, "q6asm_open_write failed\n");
+ return ret;
+ }
+
+ ret = q6asm_stream_remove_initial_silence(prtd->audio_client,
+ prtd->next_track_stream_id,
+ prtd->initial_samples_drop);
+ prtd->next_track_stream_id = 0;
+
+ }
+
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6asm_dai_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, int cmd)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id,
+ 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ prtd->state = Q6ASM_STREAM_STOPPED;
+ ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
+ CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
+ CMD_PAUSE);
+ break;
+ case SND_COMPR_TRIGGER_NEXT_TRACK:
+ prtd->next_track = true;
+ prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1);
+ break;
+ case SND_COMPR_TRIGGER_DRAIN:
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ prtd->notify_on_drain = true;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6asm_dai_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_tstamp64 *tstamp)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ uint64_t temp_copied_total;
+
+ guard(spinlock_irqsave)(&prtd->lock);
+
+ tstamp->copied_total = prtd->copied_total;
+ temp_copied_total = tstamp->copied_total;
+ tstamp->byte_offset = do_div(temp_copied_total, prtd->pcm_size);
+
+ return 0;
+}
+
+static int q6asm_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, char __user *buf,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ u32 wflags = 0;
+ uint64_t avail, bytes_in_flight = 0;
+ void *dstn;
+ size_t copy;
+ u32 app_pointer;
+ uint64_t bytes_received;
+ uint64_t temp_bytes_received;
+
+ bytes_received = prtd->bytes_received;
+ temp_bytes_received = bytes_received;
+
+ /**
+ * Make sure that next track data pointer is aligned at 32 bit boundary
+ * This is a Mandatory requirement from DSP data buffers alignment
+ */
+ if (prtd->next_track) {
+ bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);
+ temp_bytes_received = bytes_received;
+ }
+
+ app_pointer = do_div(temp_bytes_received, prtd->pcm_size);
+ dstn = prtd->dma_buffer.area + app_pointer;
+
+ if (count < prtd->pcm_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = prtd->pcm_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(prtd->dma_buffer.area, buf + copy,
+ count - copy))
+ return -EFAULT;
+ }
+
+ guard(spinlock_irqsave)(&prtd->lock);
+
+ bytes_in_flight = prtd->bytes_received - prtd->copied_total;
+
+ if (prtd->next_track) {
+ prtd->next_track = false;
+ prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);
+ prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);
+ }
+
+ prtd->bytes_received = bytes_received + count;
+
+ /* Kick off the data to dsp if its starving!! */
+ if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) {
+ uint32_t bytes_to_write = prtd->pcm_count;
+
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail < prtd->pcm_count)
+ bytes_to_write = avail;
+
+ q6asm_write_async(prtd->audio_client, prtd->stream_id,
+ bytes_to_write, 0, 0, wflags);
+ prtd->bytes_sent += bytes_to_write;
+ }
+
+ return count;
+}
+
+static int q6asm_dai_compr_mmap(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct vm_area_struct *vma)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6asm_dai_rtd *prtd = runtime->private_data;
+ struct device *dev = component->dev;
+
+ return dma_mmap_coherent(dev, vma,
+ prtd->dma_buffer.area, prtd->dma_buffer.addr,
+ prtd->dma_buffer.bytes);
+}
+
+static int q6asm_dai_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps)
+{
+ caps->direction = SND_COMPRESS_PLAYBACK;
+ caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;
+ caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
+ caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
+ caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ caps->num_codecs = 5;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_FLAC;
+ caps->codecs[2] = SND_AUDIOCODEC_WMA;
+ caps->codecs[3] = SND_AUDIOCODEC_ALAC;
+ caps->codecs[4] = SND_AUDIOCODEC_APE;
+
+ return 0;
+}
+
+static int q6asm_dai_compr_get_codec_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_codec_caps *codec)
+{
+ switch (codec->codec) {
+ case SND_AUDIOCODEC_MP3:
+ *codec = q6asm_compr_caps;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_compress_ops q6asm_dai_compress_ops = {
+ .open = q6asm_dai_compr_open,
+ .free = q6asm_dai_compr_free,
+ .set_params = q6asm_dai_compr_set_params,
+ .set_metadata = q6asm_dai_compr_set_metadata,
+ .pointer = q6asm_dai_compr_pointer,
+ .trigger = q6asm_dai_compr_trigger,
+ .get_caps = q6asm_dai_compr_get_caps,
+ .get_codec_caps = q6asm_dai_compr_get_codec_caps,
+ .mmap = q6asm_dai_compr_mmap,
+ .copy = q6asm_compr_copy,
+};
+
+static int q6asm_dai_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ size_t size = q6asm_dai_hardware_playback.buffer_bytes_max;
+
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ component->dev, size);
+}
+
+static const struct snd_soc_dapm_widget q6asm_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL7", "MultiMedia7 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_component_driver q6asm_fe_dai_component = {
+ .name = DRV_NAME,
+ .open = q6asm_dai_open,
+ .hw_params = q6asm_dai_hw_params,
+ .close = q6asm_dai_close,
+ .prepare = q6asm_dai_prepare,
+ .trigger = q6asm_dai_trigger,
+ .ack = q6asm_dai_ack,
+ .pointer = q6asm_dai_pointer,
+ .pcm_construct = q6asm_dai_pcm_new,
+ .compress_ops = &q6asm_dai_compress_ops,
+ .dapm_widgets = q6asm_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(q6asm_dapm_widgets),
+ .legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver q6asm_fe_dais_template[] = {
+ Q6ASM_FEDAI_DRIVER(1),
+ Q6ASM_FEDAI_DRIVER(2),
+ Q6ASM_FEDAI_DRIVER(3),
+ Q6ASM_FEDAI_DRIVER(4),
+ Q6ASM_FEDAI_DRIVER(5),
+ Q6ASM_FEDAI_DRIVER(6),
+ Q6ASM_FEDAI_DRIVER(7),
+ Q6ASM_FEDAI_DRIVER(8),
+};
+
+static const struct snd_soc_dai_ops q6asm_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static int of_q6asm_parse_dai_data(struct device *dev,
+ struct q6asm_dai_data *pdata)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ struct snd_soc_pcm_stream empty_stream;
+ struct device_node *node;
+ int ret, id, dir, idx = 0;
+
+
+ pdata->num_dais = of_get_child_count(dev->of_node);
+ if (!pdata->num_dais) {
+ dev_err(dev, "No dais found in DT\n");
+ return -EINVAL;
+ }
+
+ pdata->dais = devm_kcalloc(dev, pdata->num_dais, sizeof(*dai_drv),
+ GFP_KERNEL);
+ if (!pdata->dais)
+ return -ENOMEM;
+
+ memset(&empty_stream, 0, sizeof(empty_stream));
+
+ for_each_child_of_node(dev->of_node, node) {
+ ret = of_property_read_u32(node, "reg", &id);
+ if (ret || id >= MAX_SESSIONS || id < 0) {
+ dev_err(dev, "valid dai id not found:%d\n", ret);
+ continue;
+ }
+
+ dai_drv = &pdata->dais[idx++];
+ *dai_drv = q6asm_fe_dais_template[id];
+
+ ret = of_property_read_u32(node, "direction", &dir);
+ if (ret)
+ continue;
+
+ if (dir == Q6ASM_DAI_RX)
+ dai_drv->capture = empty_stream;
+ else if (dir == Q6ASM_DAI_TX)
+ dai_drv->playback = empty_stream;
+
+ if (of_property_read_bool(node, "is-compress-dai"))
+ dai_drv->ops = &q6asm_dai_ops;
+ }
+
+ return 0;
+}
+
+static int q6asm_dai_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct of_phandle_args args;
+ struct q6asm_dai_data *pdata;
+ int rc;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
+ if (rc < 0)
+ pdata->sid = -1;
+ else
+ pdata->sid = args.args[0] & SID_MASK_DEFAULT;
+
+ dev_set_drvdata(dev, pdata);
+
+ rc = of_q6asm_parse_dai_data(dev, pdata);
+ if (rc)
+ return rc;
+
+ return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component,
+ pdata->dais, pdata->num_dais);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6asm_dai_device_id[] = {
+ { .compatible = "qcom,q6asm-dais" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6asm_dai_device_id);
+#endif
+
+static struct platform_driver q6asm_dai_platform_driver = {
+ .driver = {
+ .name = "q6asm-dai",
+ .of_match_table = of_match_ptr(q6asm_dai_device_id),
+ },
+ .probe = q6asm_dai_probe,
+};
+module_platform_driver(q6asm_dai_platform_driver);
+
+MODULE_DESCRIPTION("Q6ASM dai driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
new file mode 100644
index 000000000000..e7295b7b2461
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -0,0 +1,1676 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6asm.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/atomic.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/device.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/of.h>
+#include <uapi/sound/asound.h>
+#include <uapi/sound/compress_params.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include "q6asm.h"
+#include "q6core.h"
+#include "q6dsp-errno.h"
+#include "q6dsp-common.h"
+
+#define ASM_STREAM_CMD_CLOSE 0x00010BCD
+#define ASM_STREAM_CMD_FLUSH 0x00010BCE
+#define ASM_SESSION_CMD_PAUSE 0x00010BD3
+#define ASM_DATA_CMD_EOS 0x00010BDB
+#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C
+#define ASM_NULL_POPP_TOPOLOGY 0x00010C68
+#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09
+#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10
+#define ASM_STREAM_POSTPROC_TOPO_ID_NONE 0x00010C68
+#define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92
+#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93
+#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94
+#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98
+#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99
+#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3
+#define ASM_SESSION_CMD_RUN_V2 0x00010DAA
+#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5
+#define ASM_MEDIA_FMT_MP3 0x00010BE9
+#define ASM_MEDIA_FMT_FLAC 0x00010C16
+#define ASM_MEDIA_FMT_WMA_V9 0x00010DA8
+#define ASM_MEDIA_FMT_WMA_V10 0x00010DA7
+#define ASM_DATA_CMD_WRITE_V2 0x00010DAB
+#define ASM_DATA_CMD_READ_V2 0x00010DAC
+#define ASM_SESSION_CMD_SUSPEND 0x00010DEC
+#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3
+#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4
+#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A
+#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D
+#define ASM_MEDIA_FMT_ALAC 0x00012f31
+#define ASM_MEDIA_FMT_APE 0x00012f32
+#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67
+#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68
+
+
+#define ASM_LEGACY_STREAM_SESSION 0
+/* Bit shift for the stream_perf_mode subfield. */
+#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ 29
+#define ASM_END_POINT_DEVICE_MATRIX 0
+#define ASM_DEFAULT_APP_TYPE 0
+#define ASM_SYNC_IO_MODE 0x0001
+#define ASM_ASYNC_IO_MODE 0x0002
+#define ASM_TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */
+#define ASM_TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */
+#define ASM_SHIFT_GAPLESS_MODE_FLAG 31
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+struct avs_cmd_shared_mem_map_regions {
+ u16 mem_pool_id;
+ u16 num_regions;
+ u32 property_flag;
+} __packed;
+
+struct avs_shared_map_region_payload {
+ u32 shm_addr_lsw;
+ u32 shm_addr_msw;
+ u32 mem_size_bytes;
+} __packed;
+
+struct avs_cmd_shared_mem_unmap_regions {
+ u32 mem_map_handle;
+} __packed;
+
+struct asm_data_cmd_media_fmt_update_v2 {
+ u32 fmt_blk_size;
+} __packed;
+
+struct asm_multi_channel_pcm_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 num_channels;
+ u16 bits_per_sample;
+ u32 sample_rate;
+ u16 is_signed;
+ u16 reserved;
+ u8 channel_mapping[PCM_MAX_NUM_CHANNEL];
+} __packed;
+
+struct asm_flac_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 is_stream_info_present;
+ u16 num_channels;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 md5_sum[8];
+ u32 sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+ u16 sample_size;
+ u16 reserved;
+} __packed;
+
+struct asm_wmastdv9_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 fmtag;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 bytes_per_sec;
+ u16 blk_align;
+ u16 bits_per_sample;
+ u32 channel_mask;
+ u16 enc_options;
+ u16 reserved;
+} __packed;
+
+struct asm_wmaprov10_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 fmtag;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 bytes_per_sec;
+ u16 blk_align;
+ u16 bits_per_sample;
+ u32 channel_mask;
+ u16 enc_options;
+ u16 advanced_enc_options1;
+ u32 advanced_enc_options2;
+} __packed;
+
+struct asm_alac_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u32 frame_length;
+ u8 compatible_version;
+ u8 bit_depth;
+ u8 pb;
+ u8 mb;
+ u8 kb;
+ u8 num_channels;
+ u16 max_run;
+ u32 max_frame_bytes;
+ u32 avg_bit_rate;
+ u32 sample_rate;
+ u32 channel_layout_tag;
+} __packed;
+
+struct asm_ape_fmt_blk_v2 {
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ u16 compatible_version;
+ u16 compression_level;
+ u32 format_flags;
+ u32 blocks_per_frame;
+ u32 final_frame_blocks;
+ u32 total_frames;
+ u16 bits_per_sample;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 seek_table_present;
+} __packed;
+
+struct asm_stream_cmd_set_encdec_param {
+ u32 param_id;
+ u32 param_size;
+} __packed;
+
+struct asm_enc_cfg_blk_param_v2 {
+ u32 frames_per_buf;
+ u32 enc_cfg_blk_size;
+} __packed;
+
+struct asm_multi_channel_pcm_enc_cfg_v2 {
+ struct asm_stream_cmd_set_encdec_param encdec;
+ struct asm_enc_cfg_blk_param_v2 encblk;
+ uint16_t num_channels;
+ uint16_t bits_per_sample;
+ uint32_t sample_rate;
+ uint16_t is_signed;
+ uint16_t reserved;
+ uint8_t channel_mapping[8];
+} __packed;
+
+struct asm_data_cmd_read_v2 {
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+ u32 mem_map_handle;
+ u32 buf_size;
+ u32 seq_id;
+} __packed;
+
+struct asm_data_cmd_read_v2_done {
+ u32 status;
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+};
+
+struct asm_stream_cmd_open_read_v3 {
+ u32 mode_flags;
+ u32 src_endpointype;
+ u32 preprocopo_id;
+ u32 enc_cfg_id;
+ u16 bits_per_sample;
+ u16 reserved;
+} __packed;
+
+struct asm_data_cmd_write_v2 {
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+ u32 mem_map_handle;
+ u32 buf_size;
+ u32 seq_id;
+ u32 timestamp_lsw;
+ u32 timestamp_msw;
+ u32 flags;
+} __packed;
+
+struct asm_stream_cmd_open_write_v3 {
+ uint32_t mode_flags;
+ uint16_t sink_endpointype;
+ uint16_t bits_per_sample;
+ uint32_t postprocopo_id;
+ uint32_t dec_fmt_id;
+} __packed;
+
+struct asm_session_cmd_run_v2 {
+ u32 flags;
+ u32 time_lsw;
+ u32 time_msw;
+} __packed;
+
+struct audio_buffer {
+ phys_addr_t phys;
+ uint32_t size; /* size of buffer */
+};
+
+struct audio_port_data {
+ struct audio_buffer *buf;
+ uint32_t num_periods;
+ uint32_t dsp_buf;
+ uint32_t mem_map_handle;
+ atomic_t hw_ptr;
+};
+
+struct q6asm {
+ struct apr_device *adev;
+ struct device *dev;
+ struct q6core_svc_api_info ainfo;
+ wait_queue_head_t mem_wait;
+ spinlock_t slock;
+ struct audio_client *session[MAX_SESSIONS + 1];
+};
+
+struct audio_client {
+ int session;
+ q6asm_cb cb;
+ void *priv;
+ uint32_t io_mode;
+ struct apr_device *adev;
+ struct mutex cmd_lock;
+ spinlock_t lock;
+ struct kref refcount;
+ /* idx:1 out port, 0: in port */
+ struct audio_port_data port[2];
+ wait_queue_head_t cmd_wait;
+ struct aprv2_ibasic_rsp_result_t result;
+ int perf_mode;
+ struct q6asm *q6asm;
+ struct device *dev;
+};
+
+static inline void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg,
+ uint32_t stream_id)
+{
+ hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ hdr->pkt_size = pkt_size;
+ if (cmd_flg)
+ hdr->token = ac->session;
+}
+
+static int q6asm_apr_send_session_pkt(struct q6asm *a, struct audio_client *ac,
+ struct apr_pkt *pkt, uint32_t rsp_opcode)
+{
+ struct apr_hdr *hdr = &pkt->hdr;
+ int rc;
+
+ mutex_lock(&ac->cmd_lock);
+ ac->result.opcode = 0;
+ ac->result.status = 0;
+ rc = apr_send_pkt(a->adev, pkt);
+ if (rc < 0)
+ goto err;
+
+ if (rsp_opcode)
+ rc = wait_event_timeout(a->mem_wait,
+ (ac->result.opcode == hdr->opcode) ||
+ (ac->result.opcode == rsp_opcode),
+ 5 * HZ);
+ else
+ rc = wait_event_timeout(a->mem_wait,
+ (ac->result.opcode == hdr->opcode),
+ 5 * HZ);
+
+ if (!rc) {
+ dev_err(a->dev, "CMD %x timeout\n", hdr->opcode);
+ rc = -ETIMEDOUT;
+ } else if (ac->result.status > 0) {
+ dev_err(a->dev, "DSP returned error[%x]\n",
+ ac->result.status);
+ rc = -EINVAL;
+ }
+
+err:
+ mutex_unlock(&ac->cmd_lock);
+ return rc;
+}
+
+static int __q6asm_memory_unmap(struct audio_client *ac,
+ phys_addr_t buf_add, int dir)
+{
+ struct avs_cmd_shared_mem_unmap_regions *mem_unmap;
+ struct q6asm *a = dev_get_drvdata(ac->dev->parent);
+ struct apr_pkt *pkt;
+ int rc, pkt_size;
+ void *p __free(kfree) = NULL;
+
+ if (ac->port[dir].mem_map_handle == 0) {
+ dev_err(ac->dev, "invalid mem handle\n");
+ return -EINVAL;
+ }
+
+ pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap);
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ mem_unmap = p + APR_HDR_SIZE;
+
+ pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.token = ((ac->session << 8) | dir);
+
+ pkt->hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mem_unmap->mem_map_handle = ac->port[dir].mem_map_handle;
+
+ rc = q6asm_apr_send_session_pkt(a, ac, pkt, 0);
+ if (rc < 0)
+ return rc;
+
+ ac->port[dir].mem_map_handle = 0;
+
+ return 0;
+}
+
+
+static void q6asm_audio_client_free_buf(struct audio_client *ac,
+ struct audio_port_data *port)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ac->lock, flags);
+ port->num_periods = 0;
+ spin_unlock_irqrestore(&ac->lock, flags);
+ kfree(port->buf);
+ port->buf = NULL;
+}
+
+/**
+ * q6asm_unmap_memory_regions() - unmap memory regions in the dsp.
+ *
+ * @dir: direction of audio stream
+ * @ac: audio client instanace
+ *
+ * Return: Will be an negative value on failure or zero on success
+ */
+int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+
+ port = &ac->port[dir];
+ if (!port->buf) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ cnt = port->num_periods - 1;
+ if (cnt >= 0) {
+ rc = __q6asm_memory_unmap(ac, port->buf[dir].phys, dir);
+ if (rc < 0) {
+ dev_err(ac->dev, "%s: Memory_unmap_regions failed %d\n",
+ __func__, rc);
+ goto err;
+ }
+ }
+
+ q6asm_audio_client_free_buf(ac, port);
+
+err:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_unmap_memory_regions);
+
+static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ size_t period_sz, unsigned int periods,
+ bool is_contiguous)
+{
+ struct avs_cmd_shared_mem_map_regions *cmd = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ struct q6asm *a = dev_get_drvdata(ac->dev->parent);
+ struct audio_port_data *port = NULL;
+ struct audio_buffer *ab = NULL;
+ struct apr_pkt *pkt;
+ void *p __free(kfree) = NULL;
+ unsigned long flags;
+ uint32_t num_regions, buf_sz;
+ int i, pkt_size;
+
+ if (is_contiguous) {
+ num_regions = 1;
+ buf_sz = period_sz * periods;
+ } else {
+ buf_sz = period_sz;
+ num_regions = periods;
+ }
+
+ /* DSP expects size should be aligned to 4K */
+ buf_sz = ALIGN(buf_sz, 4096);
+
+ pkt_size = APR_HDR_SIZE + sizeof(*cmd) +
+ (sizeof(*mregions) * num_regions);
+
+ p = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ cmd = p + APR_HDR_SIZE;
+ mregions = p + APR_HDR_SIZE + sizeof(*cmd);
+
+ pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD;
+ pkt->hdr.src_port = 0;
+ pkt->hdr.dest_port = 0;
+ pkt->hdr.pkt_size = pkt_size;
+ pkt->hdr.token = ((ac->session << 8) | dir);
+ pkt->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
+
+ cmd->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ cmd->num_regions = num_regions;
+ cmd->property_flag = 0x00;
+
+ spin_lock_irqsave(&ac->lock, flags);
+ port = &ac->port[dir];
+
+ for (i = 0; i < num_regions; i++) {
+ ab = &port->buf[i];
+ mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+ mregions->shm_addr_msw = upper_32_bits(ab->phys);
+ mregions->mem_size_bytes = buf_sz;
+ ++mregions;
+ }
+ spin_unlock_irqrestore(&ac->lock, flags);
+
+ return q6asm_apr_send_session_pkt(a, ac, pkt, ASM_CMDRSP_SHARED_MEM_MAP_REGIONS);
+}
+
+/**
+ * q6asm_map_memory_regions() - map memory regions in the dsp.
+ *
+ * @dir: direction of audio stream
+ * @ac: audio client instanace
+ * @phys: physical address that needs mapping.
+ * @period_sz: audio period size
+ * @periods: number of periods
+ *
+ * Return: Will be an negative value on failure or zero on success
+ */
+int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac,
+ phys_addr_t phys,
+ size_t period_sz, unsigned int periods)
+{
+ struct audio_buffer *buf;
+ unsigned long flags;
+ int cnt;
+ int rc;
+
+ spin_lock_irqsave(&ac->lock, flags);
+ if (ac->port[dir].buf) {
+ dev_err(ac->dev, "Buffer already allocated\n");
+ spin_unlock_irqrestore(&ac->lock, flags);
+ return 0;
+ }
+
+ buf = kcalloc(periods, sizeof(*buf), GFP_ATOMIC);
+ if (!buf) {
+ spin_unlock_irqrestore(&ac->lock, flags);
+ return -ENOMEM;
+ }
+
+
+ ac->port[dir].buf = buf;
+
+ buf[0].phys = phys;
+ buf[0].size = period_sz;
+
+ for (cnt = 1; cnt < periods; cnt++) {
+ if (period_sz > 0) {
+ buf[cnt].phys = buf[0].phys + (cnt * period_sz);
+ buf[cnt].size = period_sz;
+ }
+ }
+ ac->port[dir].num_periods = periods;
+
+ spin_unlock_irqrestore(&ac->lock, flags);
+
+ rc = __q6asm_memory_map_regions(ac, dir, period_sz, periods, 1);
+ if (rc < 0) {
+ dev_err(ac->dev, "Memory_map_regions failed\n");
+ q6asm_audio_client_free_buf(ac, &ac->port[dir]);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_map_memory_regions);
+
+static void q6asm_audio_client_release(struct kref *ref)
+{
+ struct audio_client *ac;
+ struct q6asm *a;
+ unsigned long flags;
+
+ ac = container_of(ref, struct audio_client, refcount);
+ a = ac->q6asm;
+
+ spin_lock_irqsave(&a->slock, flags);
+ a->session[ac->session] = NULL;
+ spin_unlock_irqrestore(&a->slock, flags);
+
+ kfree(ac);
+}
+
+/**
+ * q6asm_audio_client_free() - Freee allocated audio client
+ *
+ * @ac: audio client to free
+ */
+void q6asm_audio_client_free(struct audio_client *ac)
+{
+ kref_put(&ac->refcount, q6asm_audio_client_release);
+}
+EXPORT_SYMBOL_GPL(q6asm_audio_client_free);
+
+static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
+ int session_id)
+{
+ struct audio_client *ac = NULL;
+
+ guard(spinlock_irqsave)(&a->slock);
+ if ((session_id <= 0) || (session_id > MAX_SESSIONS)) {
+ dev_err(a->dev, "invalid session: %d\n", session_id);
+ goto err;
+ }
+
+ /* check for valid session */
+ if (!a->session[session_id])
+ goto err;
+ else if (a->session[session_id]->session != session_id)
+ goto err;
+
+ ac = a->session[session_id];
+ kref_get(&ac->refcount);
+err:
+ return ac;
+}
+
+int q6asm_get_hw_pointer(struct audio_client *ac, unsigned int dir)
+{
+ struct audio_port_data *data = &ac->port[dir];
+
+ return (int)atomic_read(&data->hw_ptr);
+}
+EXPORT_SYMBOL_GPL(q6asm_get_hw_pointer);
+
+static int32_t q6asm_stream_callback(struct apr_device *adev,
+ struct apr_resp_pkt *data,
+ int session_id)
+{
+ struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
+ struct aprv2_ibasic_rsp_result_t *result;
+ struct apr_hdr *hdr = &data->hdr;
+ struct audio_port_data *port;
+ struct audio_client *ac;
+ uint32_t client_event = 0;
+ int ret = 0;
+
+ ac = q6asm_get_audio_client(q6asm, session_id);
+ if (!ac)/* Audio client might already be freed by now */
+ return 0;
+
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case APR_BASIC_RSP_RESULT:
+ switch (result->opcode) {
+ case ASM_SESSION_CMD_PAUSE:
+ client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE;
+ break;
+ case ASM_SESSION_CMD_SUSPEND:
+ client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE;
+ break;
+ case ASM_STREAM_CMD_FLUSH:
+ client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE;
+ break;
+ case ASM_SESSION_CMD_RUN_V2:
+ client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE;
+ break;
+ case ASM_STREAM_CMD_CLOSE:
+ client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE;
+ break;
+ case ASM_STREAM_CMD_FLUSH_READBUFS:
+ client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE;
+ break;
+ case ASM_STREAM_CMD_OPEN_WRITE_V3:
+ case ASM_DATA_CMD_WRITE_V2:
+ case ASM_STREAM_CMD_OPEN_READ_V3:
+ case ASM_STREAM_CMD_OPEN_READWRITE_V2:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
+ case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
+ if (result->status != 0) {
+ dev_err(ac->dev,
+ "cmd = 0x%x returned error = 0x%x\n",
+ result->opcode, result->status);
+ ac->result = *result;
+ wake_up(&ac->cmd_wait);
+ ret = 0;
+ goto done;
+ }
+ break;
+ case ASM_DATA_CMD_EOS:
+ case ASM_DATA_CMD_READ_V2:
+ /* response as result of close stream */
+ break;
+ default:
+ dev_err(ac->dev, "command[0x%x] not expecting rsp\n",
+ result->opcode);
+ break;
+ }
+
+ ac->result = *result;
+ wake_up(&ac->cmd_wait);
+
+ if (ac->cb)
+ ac->cb(client_event, hdr->token,
+ data->payload, ac->priv);
+
+ ret = 0;
+ goto done;
+
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE;
+ if (ac->io_mode & ASM_SYNC_IO_MODE) {
+ phys_addr_t phys;
+ int token = hdr->token & ASM_WRITE_TOKEN_MASK;
+
+ guard(spinlock_irqsave)(&ac->lock);
+
+ port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (!port->buf) {
+ ret = 0;
+ goto done;
+ }
+
+ phys = port->buf[token].phys;
+
+ if (lower_32_bits(phys) != result->opcode ||
+ upper_32_bits(phys) != result->status) {
+ dev_err(ac->dev, "Expected addr %pa\n",
+ &port->buf[token].phys);
+ ret = -EINVAL;
+ goto done;
+ }
+ atomic_set(&port->hw_ptr, token + 1);
+ }
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ client_event = ASM_CLIENT_EVENT_DATA_READ_DONE;
+ if (ac->io_mode & ASM_SYNC_IO_MODE) {
+ struct asm_data_cmd_read_v2_done *done = data->payload;
+ phys_addr_t phys;
+
+ guard(spinlock_irqsave)(&ac->lock);
+ port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];
+ if (!port->buf) {
+ ret = 0;
+ goto done;
+ }
+
+ phys = port->buf[hdr->token].phys;
+ atomic_set(&port->hw_ptr, hdr->token + 1);
+
+ if (upper_32_bits(phys) != done->buf_addr_msw ||
+ lower_32_bits(phys) != done->buf_addr_lsw) {
+ dev_err(ac->dev, "Expected addr %pa %08x-%08x\n",
+ &port->buf[hdr->token].phys,
+ done->buf_addr_lsw,
+ done->buf_addr_msw);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
+ break;
+ }
+
+ if (ac->cb)
+ ac->cb(client_event, hdr->token, data->payload, ac->priv);
+
+done:
+ kref_put(&ac->refcount, q6asm_audio_client_release);
+ return ret;
+}
+
+static int q6asm_srvc_callback(struct apr_device *adev,
+ struct apr_resp_pkt *data)
+{
+ struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
+ struct aprv2_ibasic_rsp_result_t *result;
+ struct audio_port_data *port;
+ struct audio_client *ac = NULL;
+ struct apr_hdr *hdr = &data->hdr;
+ struct q6asm *a;
+ uint32_t sid = 0;
+ uint32_t dir = 0;
+ int session_id;
+
+ session_id = (hdr->dest_port >> 8) & 0xFF;
+ if (session_id)
+ return q6asm_stream_callback(adev, data, session_id);
+
+ sid = (hdr->token >> 8) & 0x0F;
+ ac = q6asm_get_audio_client(q6asm, sid);
+ if (!ac) {
+ dev_err(&adev->dev, "Audio Client not active\n");
+ return 0;
+ }
+
+ a = dev_get_drvdata(ac->dev->parent);
+ dir = (hdr->token & 0x0F);
+ port = &ac->port[dir];
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case APR_BASIC_RSP_RESULT:
+ switch (result->opcode) {
+ case ASM_CMD_SHARED_MEM_MAP_REGIONS:
+ case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ ac->result = *result;
+ wake_up(&a->mem_wait);
+ break;
+ default:
+ dev_err(&adev->dev, "command[0x%x] not expecting rsp\n",
+ result->opcode);
+ break;
+ }
+ goto done;
+ case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:
+ ac->result.status = 0;
+ ac->result.opcode = hdr->opcode;
+ port->mem_map_handle = result->opcode;
+ wake_up(&a->mem_wait);
+ break;
+ case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ ac->result.opcode = hdr->opcode;
+ ac->result.status = 0;
+ port->mem_map_handle = 0;
+ wake_up(&a->mem_wait);
+ break;
+ default:
+ dev_dbg(&adev->dev, "command[0x%x]success [0x%x]\n",
+ result->opcode, result->status);
+ break;
+ }
+
+ if (ac->cb)
+ ac->cb(hdr->opcode, hdr->token, data->payload, ac->priv);
+
+done:
+ kref_put(&ac->refcount, q6asm_audio_client_release);
+
+ return 0;
+}
+
+/**
+ * q6asm_get_session_id() - get session id for audio client
+ *
+ * @c: audio client pointer
+ *
+ * Return: Will be an session id of the audio client.
+ */
+int q6asm_get_session_id(struct audio_client *c)
+{
+ return c->session;
+}
+EXPORT_SYMBOL_GPL(q6asm_get_session_id);
+
+/**
+ * q6asm_audio_client_alloc() - Allocate a new audio client
+ *
+ * @dev: Pointer to asm child device.
+ * @cb: event callback.
+ * @priv: private data associated with this client.
+ * @session_id: session id
+ * @perf_mode: performace mode for this client
+ *
+ * Return: Will be an error pointer on error or a valid audio client
+ * on success.
+ */
+struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb,
+ void *priv, int session_id,
+ int perf_mode)
+{
+ struct q6asm *a = dev_get_drvdata(dev->parent);
+ struct audio_client *ac;
+ unsigned long flags;
+
+ ac = q6asm_get_audio_client(a, session_id + 1);
+ if (ac) {
+ dev_err(dev, "Audio Client already active\n");
+ return ac;
+ }
+
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_irqsave(&a->slock, flags);
+ a->session[session_id + 1] = ac;
+ spin_unlock_irqrestore(&a->slock, flags);
+ ac->session = session_id + 1;
+ ac->cb = cb;
+ ac->dev = dev;
+ ac->q6asm = a;
+ ac->priv = priv;
+ ac->io_mode = ASM_SYNC_IO_MODE;
+ ac->perf_mode = perf_mode;
+ ac->adev = a->adev;
+ kref_init(&ac->refcount);
+
+ init_waitqueue_head(&ac->cmd_wait);
+ mutex_init(&ac->cmd_lock);
+ spin_lock_init(&ac->lock);
+
+ return ac;
+}
+EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);
+
+static int q6asm_ac_send_cmd_sync(struct audio_client *ac, struct apr_pkt *pkt)
+{
+ struct apr_hdr *hdr = &pkt->hdr;
+ int rc;
+
+ mutex_lock(&ac->cmd_lock);
+ ac->result.opcode = 0;
+ ac->result.status = 0;
+
+ rc = apr_send_pkt(ac->adev, pkt);
+ if (rc < 0)
+ goto err;
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (ac->result.opcode == hdr->opcode), 5 * HZ);
+ if (!rc) {
+ dev_err(ac->dev, "CMD %x timeout\n", hdr->opcode);
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (ac->result.status > 0) {
+ dev_err(ac->dev, "DSP returned error[%x]\n",
+ ac->result.status);
+ rc = -EINVAL;
+ } else {
+ rc = 0;
+ }
+
+
+err:
+ mutex_unlock(&ac->cmd_lock);
+ return rc;
+}
+
+/**
+ * q6asm_open_write() - Open audio client for writing
+ * @ac: audio client pointer
+ * @stream_id: stream id of q6asm session
+ * @format: audio sample format
+ * @codec_profile: compressed format profile
+ * @bits_per_sample: bits per sample
+ * @is_gapless: flag to indicate if this is a gapless stream
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, u32 codec_profile,
+ uint16_t bits_per_sample, bool is_gapless)
+{
+ struct asm_stream_cmd_open_write_v3 *open;
+ struct apr_pkt *pkt;
+ int rc, pkt_size = APR_HDR_SIZE + sizeof(*open);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ open = p + APR_HDR_SIZE;
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
+ open->mode_flags = 0x00;
+ open->mode_flags |= ASM_LEGACY_STREAM_SESSION;
+ if (is_gapless)
+ open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG);
+
+ /* source endpoint : matrix */
+ open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+ open->bits_per_sample = bits_per_sample;
+ open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY;
+
+ switch (format) {
+ case SND_AUDIOCODEC_MP3:
+ open->dec_fmt_id = ASM_MEDIA_FMT_MP3;
+ break;
+ case FORMAT_LINEAR_PCM:
+ open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ open->dec_fmt_id = ASM_MEDIA_FMT_FLAC;
+ break;
+ case SND_AUDIOCODEC_WMA:
+ switch (codec_profile) {
+ case SND_AUDIOPROFILE_WMA9:
+ open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V9;
+ break;
+ case SND_AUDIOPROFILE_WMA10:
+ case SND_AUDIOPROFILE_WMA9_PRO:
+ case SND_AUDIOPROFILE_WMA9_LOSSLESS:
+ case SND_AUDIOPROFILE_WMA10_LOSSLESS:
+ open->dec_fmt_id = ASM_MEDIA_FMT_WMA_V10;
+ break;
+ default:
+ dev_err(ac->dev, "Invalid codec profile 0x%x\n",
+ codec_profile);
+ rc = -EINVAL;
+ goto err;
+ }
+ break;
+ case SND_AUDIOCODEC_ALAC:
+ open->dec_fmt_id = ASM_MEDIA_FMT_ALAC;
+ break;
+ case SND_AUDIOCODEC_APE:
+ open->dec_fmt_id = ASM_MEDIA_FMT_APE;
+ break;
+ default:
+ dev_err(ac->dev, "Invalid format 0x%x\n", format);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ rc = q6asm_ac_send_cmd_sync(ac, pkt);
+ if (rc < 0)
+ goto err;
+
+ ac->io_mode |= ASM_TUN_WRITE_IO_MODE;
+
+err:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_open_write);
+
+static int __q6asm_run(struct audio_client *ac, uint32_t stream_id,
+ uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts,
+ bool wait)
+{
+ struct asm_session_cmd_run_v2 *run;
+ struct apr_pkt *pkt;
+ int rc, pkt_size = APR_HDR_SIZE + sizeof(*run);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ run = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2;
+ run->flags = flags;
+ run->time_lsw = lsw_ts;
+ run->time_msw = msw_ts;
+ if (wait) {
+ rc = q6asm_ac_send_cmd_sync(ac, pkt);
+ } else {
+ rc = apr_send_pkt(ac->adev, pkt);
+ if (rc == pkt_size)
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * q6asm_run() - start the audio client
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id of q6asm session
+ * @flags: flags associated with write
+ * @msw_ts: timestamp msw
+ * @lsw_ts: timestamp lsw
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true);
+}
+EXPORT_SYMBOL_GPL(q6asm_run);
+
+/**
+ * q6asm_run_nowait() - start the audio client withou blocking
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @flags: flags associated with write
+ * @msw_ts: timestamp msw
+ * @lsw_ts: timestamp lsw
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id,
+ uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts)
+{
+ return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false);
+}
+EXPORT_SYMBOL_GPL(q6asm_run_nowait);
+
+/**
+ * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @rate: audio sample rate
+ * @channels: number of audio channels.
+ * @channel_map: channel map pointer
+ * @bits_per_sample: bits per sample
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t stream_id,
+ uint32_t rate, uint32_t channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct asm_multi_channel_pcm_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ u8 *channel_mapping;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->num_channels = channels;
+ fmt->bits_per_sample = bits_per_sample;
+ fmt->sample_rate = rate;
+ fmt->is_signed = 1;
+
+ channel_mapping = fmt->channel_mapping;
+
+ if (channel_map) {
+ memcpy(channel_mapping, channel_map, PCM_MAX_NUM_CHANNEL);
+ } else {
+ if (q6dsp_map_channels(channel_mapping, channels)) {
+ dev_err(ac->dev, " map channels failed %d\n", channels);
+ return -EINVAL;
+ }
+ }
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
+
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_flac_cfg *cfg)
+{
+ struct asm_flac_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->is_stream_info_present = cfg->stream_info_present;
+ fmt->num_channels = cfg->ch_cfg;
+ fmt->min_blk_size = cfg->min_blk_size;
+ fmt->max_blk_size = cfg->max_blk_size;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->min_frame_size = cfg->min_frame_size;
+ fmt->max_frame_size = cfg->max_frame_size;
+ fmt->sample_size = cfg->sample_size;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac);
+
+int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_wma_cfg *cfg)
+{
+ struct asm_wmastdv9_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->fmtag = cfg->fmtag;
+ fmt->num_channels = cfg->num_channels;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->bytes_per_sec = cfg->bytes_per_sec;
+ fmt->blk_align = cfg->block_align;
+ fmt->bits_per_sample = cfg->bits_per_sample;
+ fmt->channel_mask = cfg->channel_mask;
+ fmt->enc_options = cfg->enc_options;
+ fmt->reserved = 0;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9);
+
+int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_wma_cfg *cfg)
+{
+ struct asm_wmaprov10_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+ fmt->fmtag = cfg->fmtag;
+ fmt->num_channels = cfg->num_channels;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->bytes_per_sec = cfg->bytes_per_sec;
+ fmt->blk_align = cfg->block_align;
+ fmt->bits_per_sample = cfg->bits_per_sample;
+ fmt->channel_mask = cfg->channel_mask;
+ fmt->enc_options = cfg->enc_options;
+ fmt->advanced_enc_options1 = cfg->adv_enc_options;
+ fmt->advanced_enc_options2 = cfg->adv_enc_options2;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10);
+
+int q6asm_stream_media_format_block_alac(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_alac_cfg *cfg)
+{
+ struct asm_alac_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+
+ fmt->frame_length = cfg->frame_length;
+ fmt->compatible_version = cfg->compatible_version;
+ fmt->bit_depth = cfg->bit_depth;
+ fmt->num_channels = cfg->num_channels;
+ fmt->max_run = cfg->max_run;
+ fmt->max_frame_bytes = cfg->max_frame_bytes;
+ fmt->avg_bit_rate = cfg->avg_bit_rate;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->channel_layout_tag = cfg->channel_layout_tag;
+ fmt->pb = cfg->pb;
+ fmt->mb = cfg->mb;
+ fmt->kb = cfg->kb;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac);
+
+int q6asm_stream_media_format_block_ape(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_ape_cfg *cfg)
+{
+ struct asm_ape_fmt_blk_v2 *fmt;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ fmt = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk);
+
+ fmt->compatible_version = cfg->compatible_version;
+ fmt->compression_level = cfg->compression_level;
+ fmt->format_flags = cfg->format_flags;
+ fmt->blocks_per_frame = cfg->blocks_per_frame;
+ fmt->final_frame_blocks = cfg->final_frame_blocks;
+ fmt->total_frames = cfg->total_frames;
+ fmt->bits_per_sample = cfg->bits_per_sample;
+ fmt->num_channels = cfg->num_channels;
+ fmt->sample_rate = cfg->sample_rate;
+ fmt->seek_table_present = cfg->seek_table_present;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape);
+
+static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id,
+ uint32_t cmd,
+ uint32_t num_samples)
+{
+ uint32_t *samples;
+ struct apr_pkt *pkt;
+ int rc, pkt_size = APR_HDR_SIZE + sizeof(uint32_t);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ samples = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = cmd;
+ *samples = num_samples;
+ rc = apr_send_pkt(ac->adev, pkt);
+ if (rc == pkt_size)
+ rc = 0;
+
+ return rc;
+}
+
+int q6asm_stream_remove_initial_silence(struct audio_client *ac,
+ uint32_t stream_id,
+ uint32_t initial_samples)
+{
+ return q6asm_stream_remove_silence(ac, stream_id,
+ ASM_DATA_CMD_REMOVE_INITIAL_SILENCE,
+ initial_samples);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence);
+
+int q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id,
+ uint32_t trailing_samples)
+{
+ return q6asm_stream_remove_silence(ac, stream_id,
+ ASM_DATA_CMD_REMOVE_TRAILING_SILENCE,
+ trailing_samples);
+}
+EXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence);
+
+/**
+ * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @rate: audio sample rate
+ * @channels: number of audio channels.
+ * @bits_per_sample: bits per sample
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+ uint32_t stream_id, uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg;
+ struct apr_pkt *pkt;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+ int pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ enc_cfg = p + APR_HDR_SIZE;
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+
+ pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg->encdec.param_size = sizeof(*enc_cfg) - sizeof(enc_cfg->encdec);
+ enc_cfg->encblk.frames_per_buf = frames_per_buf;
+ enc_cfg->encblk.enc_cfg_blk_size = enc_cfg->encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg->num_channels = channels;
+ enc_cfg->bits_per_sample = bits_per_sample;
+ enc_cfg->sample_rate = rate;
+ enc_cfg->is_signed = 1;
+ channel_mapping = enc_cfg->channel_mapping;
+
+ if (q6dsp_map_channels(channel_mapping, channels))
+ return -EINVAL;
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support);
+
+
+/**
+ * q6asm_read() - read data of period size from audio client
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_read(struct audio_client *ac, uint32_t stream_id)
+{
+ struct asm_data_cmd_read_v2 *read;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ struct apr_pkt *pkt;
+ unsigned long flags;
+ int pkt_size = APR_HDR_SIZE + sizeof(*read);
+ int rc = 0;
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ read = p + APR_HDR_SIZE;
+
+ spin_lock_irqsave(&ac->lock, flags);
+ port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id);
+ ab = &port->buf[port->dsp_buf];
+ pkt->hdr.opcode = ASM_DATA_CMD_READ_V2;
+ read->buf_addr_lsw = lower_32_bits(ab->phys);
+ read->buf_addr_msw = upper_32_bits(ab->phys);
+ read->mem_map_handle = port->mem_map_handle;
+
+ read->buf_size = ab->size;
+ read->seq_id = port->dsp_buf;
+ pkt->hdr.token = port->dsp_buf;
+
+ port->dsp_buf++;
+
+ if (port->dsp_buf >= port->num_periods)
+ port->dsp_buf = 0;
+
+ spin_unlock_irqrestore(&ac->lock, flags);
+ rc = apr_send_pkt(ac->adev, pkt);
+ if (rc == pkt_size)
+ rc = 0;
+ else
+ pr_err("read op[0x%x]rc[%d]\n", pkt->hdr.opcode, rc);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_read);
+
+static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, uint16_t bits_per_sample)
+{
+ struct asm_stream_cmd_open_read_v3 *open;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*open);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ open = p + APR_HDR_SIZE;
+
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id);
+ pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;
+ /* Stream prio : High, provide meta info with encoded frames */
+ open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+
+ open->preprocopo_id = ASM_STREAM_POSTPROC_TOPO_ID_NONE;
+ open->bits_per_sample = bits_per_sample;
+ open->mode_flags = 0x0;
+
+ open->mode_flags |= ASM_LEGACY_STREAM_SESSION <<
+ ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open->mode_flags |= 0x00;
+ open->enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", format);
+ }
+
+ return q6asm_ac_send_cmd_sync(ac, pkt);
+}
+
+/**
+ * q6asm_open_read() - Open audio client for reading
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @format: audio sample format
+ * @bits_per_sample: bits per sample
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, uint16_t bits_per_sample)
+{
+ return __q6asm_open_read(ac, stream_id, format, bits_per_sample);
+}
+EXPORT_SYMBOL_GPL(q6asm_open_read);
+
+/**
+ * q6asm_write_async() - non blocking write
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @len: length in bytes
+ * @msw_ts: timestamp msw
+ * @lsw_ts: timestamp lsw
+ * @wflags: flags associated with write
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,
+ uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags)
+{
+ struct asm_data_cmd_write_v2 *write;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ unsigned long flags;
+ struct apr_pkt *pkt;
+ int pkt_size = APR_HDR_SIZE + sizeof(*write);
+ int rc = 0;
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
+ if (!p)
+ return -ENOMEM;
+
+ pkt = p;
+ write = p + APR_HDR_SIZE;
+
+ spin_lock_irqsave(&ac->lock, flags);
+ port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
+ q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id);
+
+ ab = &port->buf[port->dsp_buf];
+ pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT);
+ pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+ write->buf_addr_lsw = lower_32_bits(ab->phys);
+ write->buf_addr_msw = upper_32_bits(ab->phys);
+ write->buf_size = len;
+ write->seq_id = port->dsp_buf;
+ write->timestamp_lsw = lsw_ts;
+ write->timestamp_msw = msw_ts;
+ write->mem_map_handle =
+ ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle;
+
+ write->flags = wflags;
+
+ port->dsp_buf++;
+
+ if (port->dsp_buf >= port->num_periods)
+ port->dsp_buf = 0;
+
+ spin_unlock_irqrestore(&ac->lock, flags);
+ rc = apr_send_pkt(ac->adev, pkt);
+ if (rc == pkt_size)
+ rc = 0;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_write_async);
+
+static void q6asm_reset_buf_state(struct audio_client *ac)
+{
+ struct audio_port_data *port;
+
+ guard(spinlock_irqsave)(&ac->lock);
+ port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
+ port->dsp_buf = 0;
+ port = &ac->port[SNDRV_PCM_STREAM_CAPTURE];
+ port->dsp_buf = 0;
+}
+
+static int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd,
+ bool wait)
+{
+ struct apr_pkt pkt;
+ int rc;
+
+ q6asm_add_hdr(ac, &pkt.hdr, APR_HDR_SIZE, true, stream_id);
+
+ switch (cmd) {
+ case CMD_PAUSE:
+ pkt.hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ break;
+ case CMD_SUSPEND:
+ pkt.hdr.opcode = ASM_SESSION_CMD_SUSPEND;
+ break;
+ case CMD_FLUSH:
+ pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH;
+ break;
+ case CMD_OUT_FLUSH:
+ pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
+ break;
+ case CMD_EOS:
+ pkt.hdr.opcode = ASM_DATA_CMD_EOS;
+ break;
+ case CMD_CLOSE:
+ pkt.hdr.opcode = ASM_STREAM_CMD_CLOSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (wait)
+ rc = q6asm_ac_send_cmd_sync(ac, &pkt);
+ else
+ return apr_send_pkt(ac->adev, &pkt);
+
+ if (rc < 0)
+ return rc;
+
+ if (cmd == CMD_FLUSH)
+ q6asm_reset_buf_state(ac);
+
+ return 0;
+}
+
+/**
+ * q6asm_cmd() - run cmd on audio client
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @cmd: command to run on audio client.
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd)
+{
+ return __q6asm_cmd(ac, stream_id, cmd, true);
+}
+EXPORT_SYMBOL_GPL(q6asm_cmd);
+
+/**
+ * q6asm_cmd_nowait() - non blocking, run cmd on audio client
+ *
+ * @ac: audio client pointer
+ * @stream_id: stream id
+ * @cmd: command to run on audio client.
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd)
+{
+ return __q6asm_cmd(ac, stream_id, cmd, false);
+}
+EXPORT_SYMBOL_GPL(q6asm_cmd_nowait);
+
+static int q6asm_probe(struct apr_device *adev)
+{
+ struct device *dev = &adev->dev;
+ struct q6asm *q6asm;
+
+ q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL);
+ if (!q6asm)
+ return -ENOMEM;
+
+ q6core_get_svc_api_info(adev->svc_id, &q6asm->ainfo);
+
+ q6asm->dev = dev;
+ q6asm->adev = adev;
+ init_waitqueue_head(&q6asm->mem_wait);
+ spin_lock_init(&q6asm->slock);
+ dev_set_drvdata(dev, q6asm);
+
+ return devm_of_platform_populate(dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6asm_device_id[] = {
+ { .compatible = "qcom,q6asm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6asm_device_id);
+#endif
+
+static struct apr_driver qcom_q6asm_driver = {
+ .probe = q6asm_probe,
+ .callback = q6asm_srvc_callback,
+ .driver = {
+ .name = "qcom-q6asm",
+ .of_match_table = of_match_ptr(q6asm_device_id),
+ },
+};
+
+module_apr_driver(qcom_q6asm_driver);
+MODULE_DESCRIPTION("Q6 Audio Stream Manager driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
new file mode 100644
index 000000000000..6fafda5bd849
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __Q6_ASM_H__
+#define __Q6_ASM_H__
+#include "q6dsp-common.h"
+
+/* ASM client callback events */
+#define CMD_PAUSE 0x0001
+#define ASM_CLIENT_EVENT_CMD_PAUSE_DONE 0x1001
+#define CMD_FLUSH 0x0002
+#define ASM_CLIENT_EVENT_CMD_FLUSH_DONE 0x1002
+#define CMD_EOS 0x0003
+#define ASM_CLIENT_EVENT_CMD_EOS_DONE 0x1003
+#define CMD_CLOSE 0x0004
+#define ASM_CLIENT_EVENT_CMD_CLOSE_DONE 0x1004
+#define CMD_OUT_FLUSH 0x0005
+#define ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE 0x1005
+#define CMD_SUSPEND 0x0006
+#define ASM_CLIENT_EVENT_CMD_SUSPEND_DONE 0x1006
+#define ASM_CLIENT_EVENT_CMD_RUN_DONE 0x1008
+#define ASM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009
+#define ASM_CLIENT_EVENT_DATA_READ_DONE 0x100a
+#define ASM_WRITE_TOKEN_MASK GENMASK(15, 0)
+#define ASM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16)
+#define ASM_WRITE_TOKEN_LEN_SHIFT 16
+
+enum {
+ LEGACY_PCM_MODE = 0,
+ LOW_LATENCY_PCM_MODE,
+ ULTRA_LOW_LATENCY_PCM_MODE,
+ ULL_POST_PROCESSING_PCM_MODE,
+};
+
+#define MAX_SESSIONS 8
+#define FORMAT_LINEAR_PCM 0x0000
+#define ASM_LAST_BUFFER_FLAG BIT(30)
+
+struct q6asm_flac_cfg {
+ u32 sample_rate;
+ u32 ext_sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+ u16 stream_info_present;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 ch_cfg;
+ u16 sample_size;
+ u16 md5_sum;
+};
+
+struct q6asm_wma_cfg {
+ u32 fmtag;
+ u32 num_channels;
+ u32 sample_rate;
+ u32 bytes_per_sec;
+ u32 block_align;
+ u32 bits_per_sample;
+ u32 channel_mask;
+ u32 enc_options;
+ u32 adv_enc_options;
+ u32 adv_enc_options2;
+};
+
+struct q6asm_alac_cfg {
+ u32 frame_length;
+ u8 compatible_version;
+ u8 bit_depth;
+ u8 pb;
+ u8 mb;
+ u8 kb;
+ u8 num_channels;
+ u16 max_run;
+ u32 max_frame_bytes;
+ u32 avg_bit_rate;
+ u32 sample_rate;
+ u32 channel_layout_tag;
+};
+
+struct q6asm_ape_cfg {
+ u16 compatible_version;
+ u16 compression_level;
+ u32 format_flags;
+ u32 blocks_per_frame;
+ u32 final_frame_blocks;
+ u32 total_frames;
+ u16 bits_per_sample;
+ u16 num_channels;
+ u32 sample_rate;
+ u32 seek_table_present;
+};
+
+typedef void (*q6asm_cb) (uint32_t opcode, uint32_t token,
+ void *payload, void *priv);
+struct audio_client;
+struct audio_client *q6asm_audio_client_alloc(struct device *dev,
+ q6asm_cb cb, void *priv,
+ int session_id, int perf_mode);
+void q6asm_audio_client_free(struct audio_client *ac);
+int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,
+ uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags);
+int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, u32 codec_profile,
+ uint16_t bits_per_sample, bool is_gapless);
+
+int q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
+ uint32_t format, uint16_t bits_per_sample);
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+ uint32_t stream_id, uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample);
+
+int q6asm_read(struct audio_client *ac, uint32_t stream_id);
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t stream_id,
+ uint32_t rate, uint32_t channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample);
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_flac_cfg *cfg);
+int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_wma_cfg *cfg);
+int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_wma_cfg *cfg);
+int q6asm_stream_media_format_block_alac(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_alac_cfg *cfg);
+int q6asm_stream_media_format_block_ape(struct audio_client *ac,
+ uint32_t stream_id,
+ struct q6asm_ape_cfg *cfg);
+int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts);
+int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id,
+ uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts);
+int q6asm_stream_remove_initial_silence(struct audio_client *ac,
+ uint32_t stream_id,
+ uint32_t initial_samples);
+int q6asm_stream_remove_trailing_silence(struct audio_client *ac,
+ uint32_t stream_id,
+ uint32_t trailing_samples);
+int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd);
+int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd);
+int q6asm_get_session_id(struct audio_client *c);
+int q6asm_map_memory_regions(unsigned int dir,
+ struct audio_client *ac,
+ phys_addr_t phys,
+ size_t period_sz, unsigned int periods);
+int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac);
+int q6asm_get_hw_pointer(struct audio_client *ac, unsigned int dir);
+#endif /* __Q6_ASM_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c
new file mode 100644
index 000000000000..49cfb32cd209
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6core.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/jiffies.h>
+#include <linux/soc/qcom/apr.h>
+#include "q6core.h"
+#include "q6dsp-errno.h"
+
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+#define Q6_READY_TIMEOUT_MS 100
+#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C
+#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D
+#define AVCS_GET_VERSIONS 0x00012905
+#define AVCS_GET_VERSIONS_RSP 0x00012906
+#define AVCS_CMD_GET_FWK_VERSION 0x001292c
+#define AVCS_CMDRSP_GET_FWK_VERSION 0x001292d
+
+struct avcs_svc_info {
+ uint32_t service_id;
+ uint32_t version;
+} __packed;
+
+struct avcs_cmdrsp_get_version {
+ uint32_t build_id;
+ uint32_t num_services;
+ struct avcs_svc_info svc_api_info[];
+} __packed;
+
+/* for ADSP2.8 and above */
+struct avcs_svc_api_info {
+ uint32_t service_id;
+ uint32_t api_version;
+ uint32_t api_branch_version;
+} __packed;
+
+struct avcs_cmdrsp_get_fwk_version {
+ uint32_t build_major_version;
+ uint32_t build_minor_version;
+ uint32_t build_branch_version;
+ uint32_t build_subbranch_version;
+ uint32_t num_services;
+ struct avcs_svc_api_info svc_api_info[];
+} __packed;
+
+struct q6core {
+ struct apr_device *adev;
+ wait_queue_head_t wait;
+ uint32_t avcs_state;
+ struct mutex lock;
+ bool resp_received;
+ uint32_t num_services;
+ struct avcs_cmdrsp_get_fwk_version *fwk_version;
+ struct avcs_cmdrsp_get_version *svc_version;
+ bool fwk_version_supported;
+ bool get_state_supported;
+ bool get_version_supported;
+ bool is_version_requested;
+};
+
+static struct q6core *g_core;
+
+static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+{
+ struct q6core *core = dev_get_drvdata(&adev->dev);
+ struct aprv2_ibasic_rsp_result_t *result;
+ struct apr_hdr *hdr = &data->hdr;
+
+ result = data->payload;
+ switch (hdr->opcode) {
+ case APR_BASIC_RSP_RESULT:{
+ result = data->payload;
+ switch (result->opcode) {
+ case AVCS_GET_VERSIONS:
+ if (result->status == ADSP_EUNSUPPORTED)
+ core->get_version_supported = false;
+ core->resp_received = true;
+ break;
+ case AVCS_CMD_GET_FWK_VERSION:
+ if (result->status == ADSP_EUNSUPPORTED)
+ core->fwk_version_supported = false;
+ core->resp_received = true;
+ break;
+ case AVCS_CMD_ADSP_EVENT_GET_STATE:
+ if (result->status == ADSP_EUNSUPPORTED)
+ core->get_state_supported = false;
+ core->resp_received = true;
+ break;
+ }
+ break;
+ }
+ case AVCS_CMDRSP_GET_FWK_VERSION: {
+ struct avcs_cmdrsp_get_fwk_version *fwk;
+
+ fwk = data->payload;
+
+ core->fwk_version = kmemdup(data->payload,
+ struct_size(fwk, svc_api_info,
+ fwk->num_services),
+ GFP_ATOMIC);
+ if (!core->fwk_version)
+ return -ENOMEM;
+
+ core->fwk_version_supported = true;
+ core->resp_received = true;
+
+ break;
+ }
+ case AVCS_GET_VERSIONS_RSP: {
+ struct avcs_cmdrsp_get_version *v;
+
+ v = data->payload;
+
+ core->svc_version = kmemdup(data->payload,
+ struct_size(v, svc_api_info,
+ v->num_services),
+ GFP_ATOMIC);
+ if (!core->svc_version)
+ return -ENOMEM;
+
+ core->get_version_supported = true;
+ core->resp_received = true;
+
+ break;
+ }
+ case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
+ core->get_state_supported = true;
+ core->avcs_state = result->opcode;
+
+ core->resp_received = true;
+ break;
+ default:
+ dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
+ hdr->opcode);
+ break;
+ }
+
+ if (core->resp_received)
+ wake_up(&core->wait);
+
+ return 0;
+}
+
+static int q6core_get_fwk_versions(struct q6core *core)
+{
+ struct apr_device *adev = core->adev;
+ struct apr_pkt pkt;
+ int rc;
+
+ pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ pkt.hdr.pkt_size = APR_HDR_SIZE;
+ pkt.hdr.opcode = AVCS_CMD_GET_FWK_VERSION;
+
+ rc = apr_send_pkt(adev, &pkt);
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(core->wait, (core->resp_received),
+ msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+ if (rc > 0 && core->resp_received) {
+ core->resp_received = false;
+
+ if (!core->fwk_version_supported)
+ return -ENOTSUPP;
+ else
+ return 0;
+ }
+
+
+ return rc;
+}
+
+static int q6core_get_svc_versions(struct q6core *core)
+{
+ struct apr_device *adev = core->adev;
+ struct apr_pkt pkt;
+ int rc;
+
+ pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ pkt.hdr.pkt_size = APR_HDR_SIZE;
+ pkt.hdr.opcode = AVCS_GET_VERSIONS;
+
+ rc = apr_send_pkt(adev, &pkt);
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(core->wait, (core->resp_received),
+ msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+ if (rc > 0 && core->resp_received) {
+ core->resp_received = false;
+ return 0;
+ }
+
+ return rc;
+}
+
+static bool __q6core_is_adsp_ready(struct q6core *core)
+{
+ struct apr_device *adev = core->adev;
+ struct apr_pkt pkt;
+ int rc;
+
+ core->get_state_supported = false;
+
+ pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ pkt.hdr.pkt_size = APR_HDR_SIZE;
+ pkt.hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
+
+ rc = apr_send_pkt(adev, &pkt);
+ if (rc < 0)
+ return false;
+
+ rc = wait_event_timeout(core->wait, (core->resp_received),
+ msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+ if (rc > 0 && core->resp_received) {
+ core->resp_received = false;
+
+ if (core->avcs_state)
+ return true;
+ }
+
+ /* assume that the adsp is up if we not support this command */
+ if (!core->get_state_supported)
+ return true;
+
+ return false;
+}
+
+/**
+ * q6core_get_svc_api_info() - Get version number of a service.
+ *
+ * @svc_id: service id of the service.
+ * @ainfo: Valid struct pointer to fill svc api information.
+ *
+ * Return: zero on success and error code on failure or unsupported
+ */
+int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo)
+{
+ int i;
+ int ret = -ENOTSUPP;
+
+ if (!g_core || !ainfo)
+ return 0;
+
+ mutex_lock(&g_core->lock);
+ if (!g_core->is_version_requested) {
+ if (q6core_get_fwk_versions(g_core) == -ENOTSUPP)
+ q6core_get_svc_versions(g_core);
+ g_core->is_version_requested = true;
+ }
+
+ if (g_core->fwk_version_supported) {
+ for (i = 0; i < g_core->fwk_version->num_services; i++) {
+ struct avcs_svc_api_info *info;
+
+ info = &g_core->fwk_version->svc_api_info[i];
+ if (svc_id != info->service_id)
+ continue;
+
+ ainfo->api_version = info->api_version;
+ ainfo->api_branch_version = info->api_branch_version;
+ ret = 0;
+ break;
+ }
+ } else if (g_core->get_version_supported) {
+ for (i = 0; i < g_core->svc_version->num_services; i++) {
+ struct avcs_svc_info *info;
+
+ info = &g_core->svc_version->svc_api_info[i];
+ if (svc_id != info->service_id)
+ continue;
+
+ ainfo->api_version = info->version;
+ ainfo->api_branch_version = 0;
+ ret = 0;
+ break;
+ }
+ }
+
+ mutex_unlock(&g_core->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6core_get_svc_api_info);
+
+/**
+ * q6core_is_adsp_ready() - Get status of adsp
+ *
+ * Return: Will be an true if adsp is ready and false if not.
+ */
+bool q6core_is_adsp_ready(void)
+{
+ unsigned long timeout;
+ bool ret = false;
+
+ if (!g_core)
+ return false;
+
+ mutex_lock(&g_core->lock);
+ timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+ for (;;) {
+ if (__q6core_is_adsp_ready(g_core)) {
+ ret = true;
+ break;
+ }
+
+ if (!time_after(timeout, jiffies)) {
+ ret = false;
+ break;
+ }
+ }
+
+ mutex_unlock(&g_core->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
+
+static int q6core_probe(struct apr_device *adev)
+{
+ g_core = kzalloc(sizeof(*g_core), GFP_KERNEL);
+ if (!g_core)
+ return -ENOMEM;
+
+ dev_set_drvdata(&adev->dev, g_core);
+
+ mutex_init(&g_core->lock);
+ g_core->adev = adev;
+ init_waitqueue_head(&g_core->wait);
+ return 0;
+}
+
+static void q6core_exit(struct apr_device *adev)
+{
+ struct q6core *core = dev_get_drvdata(&adev->dev);
+
+ if (core->fwk_version_supported)
+ kfree(core->fwk_version);
+ if (core->get_version_supported)
+ kfree(core->svc_version);
+
+ g_core = NULL;
+ kfree(core);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6core_device_id[] = {
+ { .compatible = "qcom,q6core" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6core_device_id);
+#endif
+
+static struct apr_driver qcom_q6core_driver = {
+ .probe = q6core_probe,
+ .remove = q6core_exit,
+ .callback = q6core_callback,
+ .driver = {
+ .name = "qcom-q6core",
+ .of_match_table = of_match_ptr(q6core_device_id),
+ },
+};
+
+module_apr_driver(qcom_q6core_driver);
+MODULE_DESCRIPTION("q6 core");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6core.h b/sound/soc/qcom/qdsp6/q6core.h
new file mode 100644
index 000000000000..4105b1d730be
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6core.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6CORE_H__
+#define __Q6CORE_H__
+
+struct q6core_svc_api_info {
+ uint32_t service_id;
+ uint32_t api_version;
+ uint32_t api_branch_version;
+};
+
+bool q6core_is_adsp_ready(void);
+int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo);
+
+#endif /* __Q6CORE_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.c b/sound/soc/qcom/qdsp6/q6dsp-common.c
new file mode 100644
index 000000000000..f74585d88bd6
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-common.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include "q6dsp-common.h"
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch)
+{
+ memset(ch_map, 0, PCM_MAX_NUM_CHANNEL);
+
+ switch (ch) {
+ case 1:
+ ch_map[0] = PCM_CHANNEL_FC;
+ break;
+ case 2:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ break;
+ case 3:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_FC;
+ break;
+ case 4:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_LS;
+ ch_map[3] = PCM_CHANNEL_RS;
+ break;
+ case 5:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_FC;
+ ch_map[3] = PCM_CHANNEL_LS;
+ ch_map[4] = PCM_CHANNEL_RS;
+ break;
+ case 6:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_LFE;
+ ch_map[3] = PCM_CHANNEL_FC;
+ ch_map[4] = PCM_CHANNEL_LS;
+ ch_map[5] = PCM_CHANNEL_RS;
+ break;
+ case 8:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_LFE;
+ ch_map[3] = PCM_CHANNEL_FC;
+ ch_map[4] = PCM_CHANNEL_LS;
+ ch_map[5] = PCM_CHANNEL_RS;
+ ch_map[6] = PCM_CHANNEL_LB;
+ ch_map[7] = PCM_CHANNEL_RB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6dsp_map_channels);
+
+int q6dsp_get_channel_allocation(int channels)
+{
+ int channel_allocation;
+
+ /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */
+ switch (channels) {
+ case 2:
+ channel_allocation = 0;
+ break;
+ case 3:
+ channel_allocation = 0x02;
+ break;
+ case 4:
+ channel_allocation = 0x06;
+ break;
+ case 5:
+ channel_allocation = 0x0A;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ break;
+ case 7:
+ channel_allocation = 0x12;
+ break;
+ case 8:
+ channel_allocation = 0x13;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return channel_allocation;
+}
+EXPORT_SYMBOL_GPL(q6dsp_get_channel_allocation);
+
+MODULE_DESCRIPTION("ASoC MSM QDSP6 helper functions");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.h b/sound/soc/qcom/qdsp6/q6dsp-common.h
new file mode 100644
index 000000000000..9e704db5f604
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-common.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6DSP_COMMON_H__
+#define __Q6DSP_COMMON_H__
+
+#include <linux/kernel.h>
+
+#define PCM_MAX_NUM_CHANNEL 8
+#define PCM_CHANNEL_NULL 0
+
+#define PCM_CHANNEL_FL 1 /* Front left channel. */
+#define PCM_CHANNEL_FR 2 /* Front right channel. */
+#define PCM_CHANNEL_FC 3 /* Front center channel. */
+#define PCM_CHANNEL_LS 4 /* Left surround channel. */
+#define PCM_CHANNEL_RS 5 /* Right surround channel. */
+#define PCM_CHANNEL_LFE 6 /* Low frequency effect channel. */
+#define PCM_CHANNEL_CS 7 /* Center surround channel; Rear center ch */
+#define PCM_CHANNEL_LB 8 /* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_RB 9 /* Right back channel; Rear right channel. */
+#define PCM_CHANNELS 10 /* Top surround channel. */
+
+int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch);
+int q6dsp_get_channel_allocation(int channels);
+
+#endif /* __Q6DSP_COMMON_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-errno.h b/sound/soc/qcom/qdsp6/q6dsp-errno.h
new file mode 100644
index 000000000000..1ec00ff8c1d2
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-errno.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6DSP_ERR_NO_H__
+#define __Q6DSP_ERR_NO_H__
+#include <linux/kernel.h>
+
+/* Success. The operation completed with no errors. */
+#define ADSP_EOK 0x00000000
+/* General failure. */
+#define ADSP_EFAILED 0x00000001
+/* Bad operation parameter. */
+#define ADSP_EBADPARAM 0x00000002
+/* Unsupported routine or operation. */
+#define ADSP_EUNSUPPORTED 0x00000003
+/* Unsupported version. */
+#define ADSP_EVERSION 0x00000004
+/* Unexpected problem encountered. */
+#define ADSP_EUNEXPECTED 0x00000005
+/* Unhandled problem occurred. */
+#define ADSP_EPANIC 0x00000006
+/* Unable to allocate resource. */
+#define ADSP_ENORESOURCE 0x00000007
+/* Invalid handle. */
+#define ADSP_EHANDLE 0x00000008
+/* Operation is already processed. */
+#define ADSP_EALREADY 0x00000009
+/* Operation is not ready to be processed. */
+#define ADSP_ENOTREADY 0x0000000A
+/* Operation is pending completion. */
+#define ADSP_EPENDING 0x0000000B
+/* Operation could not be accepted or processed. */
+#define ADSP_EBUSY 0x0000000C
+/* Operation aborted due to an error. */
+#define ADSP_EABORTED 0x0000000D
+/* Operation preempted by a higher priority. */
+#define ADSP_EPREEMPTED 0x0000000E
+/* Operation requests intervention to complete. */
+#define ADSP_ECONTINUE 0x0000000F
+/* Operation requests immediate intervention to complete. */
+#define ADSP_EIMMEDIATE 0x00000010
+/* Operation is not implemented. */
+#define ADSP_ENOTIMPL 0x00000011
+/* Operation needs more data or resources. */
+#define ADSP_ENEEDMORE 0x00000012
+/* Operation does not have memory. */
+#define ADSP_ENOMEMORY 0x00000014
+/* Item does not exist. */
+#define ADSP_ENOTEXIST 0x00000015
+/* Max count for adsp error code sent to HLOS*/
+
+#endif /*__Q6DSP_ERR_NO_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
new file mode 100644
index 000000000000..03838582aead
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "q6dsp-lpass-clocks.h"
+
+#define Q6DSP_MAX_CLK_ID 104
+#define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0
+
+
+struct q6dsp_clk {
+ struct device *dev;
+ int q6dsp_clk_id;
+ int attributes;
+ int rate;
+ uint32_t handle;
+ struct clk_hw hw;
+};
+
+#define to_q6dsp_clk(_hw) container_of(_hw, struct q6dsp_clk, hw)
+
+struct q6dsp_cc {
+ struct device *dev;
+ struct q6dsp_clk *clks[Q6DSP_MAX_CLK_ID];
+ const struct q6dsp_clk_desc *desc;
+};
+
+static int clk_q6dsp_prepare(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ return cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
+ Q6DSP_LPASS_CLK_ROOT_DEFAULT, clk->rate);
+}
+
+static void clk_q6dsp_unprepare(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
+ Q6DSP_LPASS_CLK_ROOT_DEFAULT, 0);
+}
+
+static int clk_q6dsp_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+
+ return clk->rate;
+}
+
+static int clk_q6dsp_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ return 0;
+}
+
+static const struct clk_ops clk_q6dsp_ops = {
+ .prepare = clk_q6dsp_prepare,
+ .unprepare = clk_q6dsp_unprepare,
+ .set_rate = clk_q6dsp_set_rate,
+ .determine_rate = clk_q6dsp_determine_rate,
+ .recalc_rate = clk_q6dsp_recalc_rate,
+};
+
+static int clk_vote_q6dsp_block(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ return cc->desc->lpass_vote_clk(clk->dev, clk->q6dsp_clk_id,
+ clk_hw_get_name(&clk->hw), &clk->handle);
+}
+
+static void clk_unvote_q6dsp_block(struct clk_hw *hw)
+{
+ struct q6dsp_clk *clk = to_q6dsp_clk(hw);
+ struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
+
+ cc->desc->lpass_unvote_clk(clk->dev, clk->q6dsp_clk_id, clk->handle);
+}
+
+static const struct clk_ops clk_vote_q6dsp_ops = {
+ .prepare = clk_vote_q6dsp_block,
+ .unprepare = clk_unvote_q6dsp_block,
+};
+
+
+static struct clk_hw *q6dsp_of_clk_hw_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct q6dsp_cc *cc = data;
+ unsigned int idx = clkspec->args[0];
+ unsigned int attr = clkspec->args[1];
+
+ if (idx >= Q6DSP_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) {
+ dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (cc->clks[idx]) {
+ cc->clks[idx]->attributes = attr;
+ return &cc->clks[idx]->hw;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+int q6dsp_clock_dev_probe(struct platform_device *pdev)
+{
+ struct q6dsp_cc *cc;
+ struct device *dev = &pdev->dev;
+ const struct q6dsp_clk_init *q6dsp_clks;
+ const struct q6dsp_clk_desc *desc;
+ int i, ret;
+
+ cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ cc->desc = desc;
+ cc->dev = dev;
+ q6dsp_clks = desc->clks;
+
+ for (i = 0; i < desc->num_clks; i++) {
+ unsigned int id = q6dsp_clks[i].clk_id;
+ struct clk_init_data init = {
+ .name = q6dsp_clks[i].name,
+ };
+ struct q6dsp_clk *clk;
+
+ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return -ENOMEM;
+
+ clk->dev = dev;
+ clk->q6dsp_clk_id = q6dsp_clks[i].q6dsp_clk_id;
+ clk->rate = q6dsp_clks[i].rate;
+ clk->hw.init = &init;
+
+ if (clk->rate)
+ init.ops = &clk_q6dsp_ops;
+ else
+ init.ops = &clk_vote_q6dsp_ops;
+
+ cc->clks[id] = clk;
+
+ ret = devm_clk_hw_register(dev, &clk->hw);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, q6dsp_of_clk_hw_get, cc);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, cc);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6dsp_clock_dev_probe);
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h
new file mode 100644
index 000000000000..3770d81f2bd6
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6DSP_AUDIO_CLOCKS_H__
+#define __Q6DSP_AUDIO_CLOCKS_H__
+
+struct q6dsp_clk_init {
+ int clk_id;
+ int q6dsp_clk_id;
+ char *name;
+ int rate;
+};
+
+#define Q6DSP_VOTE_CLK(id, blkid, n) { \
+ .clk_id = id, \
+ .q6dsp_clk_id = blkid, \
+ .name = n, \
+ }
+
+struct q6dsp_clk_desc {
+ const struct q6dsp_clk_init *clks;
+ size_t num_clks;
+ int (*lpass_set_clk)(struct device *dev, int clk_id, int attr,
+ int root_clk, unsigned int freq);
+ int (*lpass_vote_clk)(struct device *dev, uint32_t hid, const char *n, uint32_t *h);
+ int (*lpass_unvote_clk)(struct device *dev, uint32_t hid, uint32_t h);
+};
+
+int q6dsp_clock_dev_probe(struct platform_device *pdev);
+
+#endif /* __Q6DSP_AUDIO_CLOCKS_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
new file mode 100644
index 000000000000..4eed54b071a5
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
@@ -0,0 +1,658 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include "q6dsp-lpass-ports.h"
+
+#define Q6AFE_TDM_PB_DAI(pre, num, did) { \
+ .playback = { \
+ .stream_name = pre" TDM"#num" Playback", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_TDM_CAP_DAI(pre, num, did) { \
+ .capture = { \
+ .stream_name = pre" TDM"#num" Capture", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_CDC_DMA_RX_DAI(did) { \
+ .playback = { \
+ .stream_name = #did" Playback", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_CDC_DMA_TX_DAI(did) { \
+ .capture = { \
+ .stream_name = #did" Capture", \
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_176400, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels_min = 1, \
+ .channels_max = 8, \
+ .rate_min = 8000, \
+ .rate_max = 176400, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+#define Q6AFE_DP_RX_DAI(did) { \
+ .playback = { \
+ .stream_name = #did" Playback", \
+ .rates = SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE, \
+ .channels_min = 2, \
+ .channels_max = 8, \
+ .rate_min = 48000, \
+ .rate_max = 192000, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
+
+static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
+ {
+ .playback = {
+ .stream_name = "USB 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_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .id = USB_RX,
+ .name = "USB_RX",
+ },
+ {
+ .playback = {
+ .stream_name = "HDMI Playback",
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ },
+ .id = HDMI_RX,
+ .name = "HDMI",
+ }, {
+ .name = "SLIMBUS_0_RX",
+ .id = SLIMBUS_0_RX,
+ .playback = {
+ .stream_name = "Slimbus Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .name = "SLIMBUS_0_TX",
+ .id = SLIMBUS_0_TX,
+ .capture = {
+ .stream_name = "Slimbus Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus1 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_1_RX",
+ .id = SLIMBUS_1_RX,
+ }, {
+ .name = "SLIMBUS_1_TX",
+ .id = SLIMBUS_1_TX,
+ .capture = {
+ .stream_name = "Slimbus1 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus2 Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_2_RX",
+ .id = SLIMBUS_2_RX,
+
+ }, {
+ .name = "SLIMBUS_2_TX",
+ .id = SLIMBUS_2_TX,
+ .capture = {
+ .stream_name = "Slimbus2 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus3 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_3_RX",
+ .id = SLIMBUS_3_RX,
+
+ }, {
+ .name = "SLIMBUS_3_TX",
+ .id = SLIMBUS_3_TX,
+ .capture = {
+ .stream_name = "Slimbus3 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus4 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_4_RX",
+ .id = SLIMBUS_4_RX,
+
+ }, {
+ .name = "SLIMBUS_4_TX",
+ .id = SLIMBUS_4_TX,
+ .capture = {
+ .stream_name = "Slimbus4 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus5 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_5_RX",
+ .id = SLIMBUS_5_RX,
+
+ }, {
+ .name = "SLIMBUS_5_TX",
+ .id = SLIMBUS_5_TX,
+ .capture = {
+ .stream_name = "Slimbus5 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Slimbus6 Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .name = "SLIMBUS_6_RX",
+ .id = SLIMBUS_6_RX,
+
+ }, {
+ .name = "SLIMBUS_6_TX",
+ .id = SLIMBUS_6_TX,
+ .capture = {
+ .stream_name = "Slimbus6 Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ }, {
+ .playback = {
+ .stream_name = "Primary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = PRIMARY_MI2S_RX,
+ .name = "PRI_MI2S_RX",
+ }, {
+ .capture = {
+ .stream_name = "Primary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = PRIMARY_MI2S_TX,
+ .name = "PRI_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Secondary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .name = "SEC_MI2S_RX",
+ .id = SECONDARY_MI2S_RX,
+ }, {
+ .capture = {
+ .stream_name = "Secondary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = SECONDARY_MI2S_TX,
+ .name = "SEC_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Tertiary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .name = "TERT_MI2S_RX",
+ .id = TERTIARY_MI2S_RX,
+ }, {
+ .capture = {
+ .stream_name = "Tertiary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = TERTIARY_MI2S_TX,
+ .name = "TERT_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Quaternary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .name = "QUAT_MI2S_RX",
+ .id = QUATERNARY_MI2S_RX,
+ }, {
+ .capture = {
+ .stream_name = "Quaternary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = QUATERNARY_MI2S_TX,
+ .name = "QUAT_MI2S_TX",
+ }, {
+ .playback = {
+ .stream_name = "Quinary MI2S Playback",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .id = QUINARY_MI2S_RX,
+ .name = "QUIN_MI2S_RX",
+ }, {
+ .capture = {
+ .stream_name = "Quinary MI2S Capture",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = QUINARY_MI2S_TX,
+ .name = "QUIN_MI2S_TX",
+ },
+ Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Primary", 3, PRIMARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Primary", 4, PRIMARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Primary", 5, PRIMARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Primary", 6, PRIMARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Primary", 7, PRIMARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Primary", 0, PRIMARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Primary", 1, PRIMARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Primary", 2, PRIMARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Primary", 3, PRIMARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Primary", 4, PRIMARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Primary", 5, PRIMARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Primary", 6, PRIMARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Primary", 7, PRIMARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Secondary", 0, SECONDARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Secondary", 1, SECONDARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Secondary", 2, SECONDARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Secondary", 3, SECONDARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Secondary", 4, SECONDARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Secondary", 5, SECONDARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Secondary", 6, SECONDARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Secondary", 7, SECONDARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Secondary", 0, SECONDARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Secondary", 1, SECONDARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Secondary", 2, SECONDARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Secondary", 3, SECONDARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Secondary", 4, SECONDARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Secondary", 5, SECONDARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Secondary", 6, SECONDARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Secondary", 7, SECONDARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Tertiary", 0, TERTIARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Tertiary", 1, TERTIARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Tertiary", 2, TERTIARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Tertiary", 3, TERTIARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Tertiary", 4, TERTIARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Tertiary", 5, TERTIARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Tertiary", 6, TERTIARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Tertiary", 7, TERTIARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 0, TERTIARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 1, TERTIARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 2, TERTIARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 3, TERTIARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 4, TERTIARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 5, TERTIARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 6, TERTIARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Tertiary", 7, TERTIARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Quaternary", 0, QUATERNARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Quaternary", 1, QUATERNARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Quaternary", 2, QUATERNARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Quaternary", 3, QUATERNARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Quaternary", 4, QUATERNARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Quaternary", 5, QUATERNARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Quaternary", 6, QUATERNARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Quaternary", 7, QUATERNARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 0, QUATERNARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 1, QUATERNARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 2, QUATERNARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 3, QUATERNARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 4, QUATERNARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 5, QUATERNARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 6, QUATERNARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Quaternary", 7, QUATERNARY_TDM_TX_7),
+ Q6AFE_TDM_PB_DAI("Quinary", 0, QUINARY_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("Quinary", 1, QUINARY_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("Quinary", 2, QUINARY_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("Quinary", 3, QUINARY_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("Quinary", 4, QUINARY_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("Quinary", 5, QUINARY_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("Quinary", 6, QUINARY_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("Quinary", 7, QUINARY_TDM_RX_7),
+ Q6AFE_TDM_CAP_DAI("Quinary", 0, QUINARY_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("Quinary", 1, QUINARY_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("Quinary", 2, QUINARY_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("Quinary", 3, QUINARY_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("Quinary", 4, QUINARY_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_0),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_1),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_2),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_3),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_4),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_5),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_6),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_7),
+ Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0),
+ Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0),
+ Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1),
+ Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1),
+ Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2),
+ Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0),
+ Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1),
+ Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5),
+ Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6),
+ Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7),
+};
+
+int q6dsp_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ int id = args->args[0];
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) {
+ if (q6dsp_audio_fe_dais[i].id == id) {
+ *dai_name = q6dsp_audio_fe_dais[i].name;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(q6dsp_audio_ports_of_xlate_dai_name);
+
+struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
+ struct q6dsp_audio_port_dai_driver_config *cfg,
+ int *num_dais)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) {
+ switch (q6dsp_audio_fe_dais[i].id) {
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
+ break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
+ break;
+ case SLIMBUS_0_RX ... SLIMBUS_6_TX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops;
+ break;
+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops;
+ break;
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6tdm_ops;
+ break;
+ case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6dma_ops;
+ break;
+ case USB_RX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6usb_ops;
+ break;
+ default:
+ break;
+ }
+ }
+
+ *num_dais = ARRAY_SIZE(q6dsp_audio_fe_dais);
+ return q6dsp_audio_fe_dais;
+}
+EXPORT_SYMBOL_GPL(q6dsp_audio_ports_set_config);
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
new file mode 100644
index 000000000000..d8dde6dd0aca
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6DSP_AUDIO_PORTS_H__
+#define __Q6DSP_AUDIO_PORTS_H__
+
+struct q6dsp_audio_port_dai_driver_config {
+ int (*probe)(struct snd_soc_dai *dai);
+ int (*remove)(struct snd_soc_dai *dai);
+ const struct snd_soc_dai_ops *q6hdmi_ops;
+ const struct snd_soc_dai_ops *q6slim_ops;
+ const struct snd_soc_dai_ops *q6i2s_ops;
+ const struct snd_soc_dai_ops *q6tdm_ops;
+ const struct snd_soc_dai_ops *q6dma_ops;
+ const struct snd_soc_dai_ops *q6usb_ops;
+};
+
+struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
+ struct q6dsp_audio_port_dai_driver_config *cfg,
+ int *num_dais);
+int q6dsp_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name);
+#endif /* __Q6DSP_AUDIO_PORTS_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c
new file mode 100644
index 000000000000..4c574b48ab00
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include "q6dsp-lpass-clocks.h"
+#include "q6prm.h"
+
+#define Q6PRM_CLK(id) { \
+ .clk_id = id, \
+ .q6dsp_clk_id = Q6PRM_##id, \
+ .name = #id, \
+ .rate = 19200000, \
+ }
+
+static const struct q6dsp_clk_init q6prm_clks[] = {
+ Q6PRM_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEC_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_TER_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_TER_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUAD_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUAD_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SPEAKER_I2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SPEAKER_I2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SPEAKER_I2S_OSR),
+ Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEN_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_SEN_MI2S_EBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT0_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT1_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT2_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT3_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT4_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT),
+ Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_TX_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_TX_CORE_NPL_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_TX_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_TX_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_TX_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_TX_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK),
+ Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS,
+ "LPASS_HW_MACRO"),
+ Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC,
+ "LPASS_HW_DCODEC"),
+};
+
+static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = {
+ .clks = q6prm_clks,
+ .num_clks = ARRAY_SIZE(q6prm_clks),
+ .lpass_set_clk = q6prm_set_lpass_clock,
+ .lpass_vote_clk = q6prm_vote_lpass_core_hw,
+ .lpass_unvote_clk = q6prm_unvote_lpass_core_hw,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6prm_clock_device_id[] = {
+ { .compatible = "qcom,q6prm-lpass-clocks", .data = &q6dsp_clk_q6prm },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6prm_clock_device_id);
+#endif
+
+static struct platform_driver q6prm_clock_platform_driver = {
+ .driver = {
+ .name = "q6prm-lpass-clock",
+ .of_match_table = of_match_ptr(q6prm_clock_device_id),
+ },
+ .probe = q6dsp_clock_dev_probe,
+};
+module_platform_driver(q6prm_clock_platform_driver);
+
+MODULE_DESCRIPTION("Q6 Proxy Resource Manager LPASS clock driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c
new file mode 100644
index 000000000000..0b8fad0bc832
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6prm.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Linaro Limited
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/jiffies.h>
+#include <linux/soc/qcom/apr.h>
+#include <dt-bindings/soc/qcom,gpr.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "q6apm.h"
+#include "q6prm.h"
+#include "audioreach.h"
+
+struct q6prm {
+ struct device *dev;
+ gpr_device_t *gdev;
+ wait_queue_head_t wait;
+ struct gpr_ibasic_rsp_result_t result;
+ struct mutex lock;
+};
+
+#define PRM_CMD_REQUEST_HW_RSC 0x0100100F
+#define PRM_CMD_RSP_REQUEST_HW_RSC 0x02001002
+#define PRM_CMD_RELEASE_HW_RSC 0x01001010
+#define PRM_CMD_RSP_RELEASE_HW_RSC 0x02001003
+#define PARAM_ID_RSC_HW_CORE 0x08001032
+#define PARAM_ID_RSC_LPASS_CORE 0x0800102B
+#define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C
+
+struct prm_cmd_request_hw_core {
+ struct apm_module_param_data param_data;
+ uint32_t hw_clk_id;
+} __packed;
+
+struct prm_cmd_request_rsc {
+ struct apm_module_param_data param_data;
+ uint32_t num_clk_id;
+ struct audio_hw_clk_cfg clock_id;
+} __packed;
+
+struct prm_cmd_release_rsc {
+ struct apm_module_param_data param_data;
+ uint32_t num_clk_id;
+ struct audio_hw_clk_rel_cfg clock_id;
+} __packed;
+
+static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
+{
+ return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
+ NULL, &prm->wait, pkt, rsp_opcode);
+}
+
+static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable)
+{
+ struct q6prm *prm = dev_get_drvdata(dev->parent);
+ struct apm_module_param_data *param_data;
+ struct prm_cmd_request_hw_core *req;
+ gpr_device_t *gdev = prm->gdev;
+ uint32_t opcode, rsp_opcode;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+
+ if (enable) {
+ opcode = PRM_CMD_REQUEST_HW_RSC;
+ rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC;
+ } else {
+ opcode = PRM_CMD_RELEASE_HW_RSC;
+ rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;
+ }
+
+ pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &req->param_data;
+
+ param_data->module_instance_id = GPR_PRM_MODULE_IID;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_RSC_HW_CORE;
+ param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
+
+ req->hw_clk_id = hw_block_id;
+
+ return q6prm_send_cmd_sync(prm, pkt, rsp_opcode);
+}
+
+int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ const char *client_name, uint32_t *client_handle)
+{
+ return q6prm_set_hw_core_req(dev, hw_block_id, true);
+
+}
+EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw);
+
+int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle)
+{
+ return q6prm_set_hw_core_req(dev, hw_block_id, false);
+}
+EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);
+
+static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
+ unsigned int freq)
+{
+ struct q6prm *prm = dev_get_drvdata(dev->parent);
+ struct apm_module_param_data *param_data;
+ struct prm_cmd_request_rsc *req;
+ gpr_device_t *gdev = prm->gdev;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+
+ pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,
+ GPR_PRM_MODULE_IID);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &req->param_data;
+
+ param_data->module_instance_id = GPR_PRM_MODULE_IID;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
+ param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
+
+ req->num_clk_id = 1;
+ req->clock_id.clock_id = clk_id;
+ req->clock_id.clock_freq = freq;
+ req->clock_id.clock_attri = clk_attr;
+ req->clock_id.clock_root = clk_root;
+
+ return q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC);
+}
+
+static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
+ unsigned int freq)
+{
+ struct q6prm *prm = dev_get_drvdata(dev->parent);
+ struct apm_module_param_data *param_data;
+ struct prm_cmd_release_rsc *rel;
+ gpr_device_t *gdev = prm->gdev;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+
+ pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,
+ GPR_PRM_MODULE_IID);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ rel = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = &rel->param_data;
+
+ param_data->module_instance_id = GPR_PRM_MODULE_IID;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
+ param_data->param_size = sizeof(*rel) - APM_MODULE_PARAM_DATA_SIZE;
+
+ rel->num_clk_id = 1;
+ rel->clock_id.clock_id = clk_id;
+
+ return q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_RELEASE_HW_RSC);
+}
+
+int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
+ unsigned int freq)
+{
+ if (freq)
+ return q6prm_request_lpass_clock(dev, clk_id, clk_attr, clk_root, freq);
+
+ return q6prm_release_lpass_clock(dev, clk_id, clk_attr, clk_root, freq);
+}
+EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
+
+static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ gpr_device_t *gdev = priv;
+ struct q6prm *prm = dev_get_drvdata(&gdev->dev);
+ struct gpr_ibasic_rsp_result_t *result;
+ struct gpr_hdr *hdr = &data->hdr;
+
+ switch (hdr->opcode) {
+ case PRM_CMD_RSP_REQUEST_HW_RSC:
+ case PRM_CMD_RSP_RELEASE_HW_RSC:
+ result = data->payload;
+ prm->result.opcode = hdr->opcode;
+ prm->result.status = result->status;
+ wake_up(&prm->wait);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int prm_probe(gpr_device_t *gdev)
+{
+ struct device *dev = &gdev->dev;
+ struct q6prm *cc;
+
+ cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->dev = dev;
+ cc->gdev = gdev;
+ mutex_init(&cc->lock);
+ init_waitqueue_head(&cc->wait);
+ dev_set_drvdata(dev, cc);
+
+ if (!q6apm_is_adsp_ready())
+ return -EPROBE_DEFER;
+
+ return devm_of_platform_populate(dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id prm_device_id[] = {
+ { .compatible = "qcom,q6prm" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, prm_device_id);
+#endif
+
+static gpr_driver_t prm_driver = {
+ .probe = prm_probe,
+ .gpr_callback = prm_callback,
+ .driver = {
+ .name = "qcom-prm",
+ .of_match_table = of_match_ptr(prm_device_id),
+ },
+};
+
+module_gpr_driver(prm_driver);
+MODULE_DESCRIPTION("Q6 Proxy Resource Manager");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h
new file mode 100644
index 000000000000..a988a32086fe
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6prm.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __Q6PRM_H__
+#define __Q6PRM_H__
+
+/* Clock ID for Primary I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100
+/* Clock ID for Primary I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_EBIT 0x101
+/* Clock ID for Secondary I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_SEC_MI2S_IBIT 0x102
+/* Clock ID for Secondary I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_SEC_MI2S_EBIT 0x103
+/* Clock ID for Tertiary I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_TER_MI2S_IBIT 0x104
+/* Clock ID for Tertiary I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_TER_MI2S_EBIT 0x105
+/* Clock ID for Quartnery I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_QUAD_MI2S_IBIT 0x106
+/* Clock ID for Quartnery I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_QUAD_MI2S_EBIT 0x107
+/* Clock ID for Speaker I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_IBIT 0x108
+/* Clock ID for Speaker I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_EBIT 0x109
+/* Clock ID for Speaker I2S OSR */
+#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_OSR 0x10A
+
+/* Clock ID for QUINARY I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_IBIT 0x10B
+/* Clock ID for QUINARY I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_EBIT 0x10C
+/* Clock ID for SENARY I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_SEN_MI2S_IBIT 0x10D
+/* Clock ID for SENARY I2S EBIT */
+#define Q6PRM_LPASS_CLK_ID_SEN_MI2S_EBIT 0x10E
+/* Clock ID for INT0 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT0_MI2S_IBIT 0x10F
+/* Clock ID for INT1 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT1_MI2S_IBIT 0x110
+/* Clock ID for INT2 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT2_MI2S_IBIT 0x111
+/* Clock ID for INT3 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT3_MI2S_IBIT 0x112
+/* Clock ID for INT4 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT4_MI2S_IBIT 0x113
+/* Clock ID for INT5 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT5_MI2S_IBIT 0x114
+/* Clock ID for INT6 I2S IBIT */
+#define Q6PRM_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115
+
+/* Clock ID for QUINARY MI2S OSR CLK */
+#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116
+
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306
+
+#define Q6PRM_LPASS_CLK_ID_VA_CORE_MCLK 0x307
+#define Q6PRM_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x308
+
+#define Q6PRM_LPASS_CLK_ID_TX_CORE_MCLK 0x30c
+#define Q6PRM_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d
+
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK 0x30e
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f
+
+/* Clock ID for MCLK for WSA2 core */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_MCLK 0x310
+/* Clock ID for NPL MCLK for WSA2 core */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_2X_MCLK 0x311
+/* Clock ID for RX Core TX MCLK */
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_TX_MCLK 0x312
+/* Clock ID for RX CORE TX 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_TX_2X_MCLK 0x313
+/* Clock ID for WSA core TX MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_TX_MCLK 0x314
+/* Clock ID for WSA core TX 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_TX_2X_MCLK 0x315
+/* Clock ID for WSA2 core TX MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_TX_MCLK 0x316
+/* Clock ID for WSA2 core TX 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK 0x317
+/* Clock ID for RX CORE MCLK2 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318
+
+#define Q6PRM_LPASS_CLK_SRC_INTERNAL 1
+#define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0
+#define Q6PRM_HW_CORE_ID_LPASS 1
+#define Q6PRM_HW_CORE_ID_DCODEC 2
+
+int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr,
+ int clk_root, unsigned int freq);
+int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ const char *client_name, uint32_t *client_handle);
+int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
+ uint32_t client_handle);
+#endif /* __Q6PRM_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
new file mode 100644
index 000000000000..aaa3af9f1993
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6routing.c
@@ -0,0 +1,1174 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+// Copyright (c) 2018, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6asm.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asound.h>
+#include <sound/pcm_params.h>
+#include "q6afe.h"
+#include "q6asm.h"
+#include "q6adm.h"
+#include "q6routing.h"
+
+#define DRV_NAME "q6routing-component"
+
+#define Q6ROUTING_RX_MIXERS(id) \
+ SOC_SINGLE_EXT("MultiMedia1", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia2", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia3", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia4", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia5", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia6", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia7", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("MultiMedia8", id, \
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,\
+ msm_routing_put_audio_mixer),
+
+#define Q6ROUTING_RX_DAPM_ROUTE(mix_name, s) \
+ { mix_name, "MultiMedia1", "MM_DL1" }, \
+ { mix_name, "MultiMedia2", "MM_DL2" }, \
+ { mix_name, "MultiMedia3", "MM_DL3" }, \
+ { mix_name, "MultiMedia4", "MM_DL4" }, \
+ { mix_name, "MultiMedia5", "MM_DL5" }, \
+ { mix_name, "MultiMedia6", "MM_DL6" }, \
+ { mix_name, "MultiMedia7", "MM_DL7" }, \
+ { mix_name, "MultiMedia8", "MM_DL8" }, \
+ { s, NULL, mix_name }
+
+#define Q6ROUTING_TX_DAPM_ROUTE(mix_name) \
+ { mix_name, "PRI_MI2S_TX", "PRI_MI2S_TX" }, \
+ { mix_name, "SEC_MI2S_TX", "SEC_MI2S_TX" }, \
+ { mix_name, "QUAT_MI2S_TX", "QUAT_MI2S_TX" }, \
+ { mix_name, "QUIN_MI2S_TX", "QUIN_MI2S_TX" }, \
+ { mix_name, "TERT_MI2S_TX", "TERT_MI2S_TX" }, \
+ { mix_name, "SLIMBUS_0_TX", "SLIMBUS_0_TX" }, \
+ { mix_name, "SLIMBUS_1_TX", "SLIMBUS_1_TX" }, \
+ { mix_name, "SLIMBUS_2_TX", "SLIMBUS_2_TX" }, \
+ { mix_name, "SLIMBUS_3_TX", "SLIMBUS_3_TX" }, \
+ { mix_name, "SLIMBUS_4_TX", "SLIMBUS_4_TX" }, \
+ { mix_name, "SLIMBUS_5_TX", "SLIMBUS_5_TX" }, \
+ { mix_name, "SLIMBUS_6_TX", "SLIMBUS_6_TX" }, \
+ { mix_name, "PRIMARY_TDM_TX_0", "PRIMARY_TDM_TX_0"}, \
+ { mix_name, "PRIMARY_TDM_TX_1", "PRIMARY_TDM_TX_1"}, \
+ { mix_name, "PRIMARY_TDM_TX_2", "PRIMARY_TDM_TX_2"}, \
+ { mix_name, "PRIMARY_TDM_TX_3", "PRIMARY_TDM_TX_3"}, \
+ { mix_name, "PRIMARY_TDM_TX_4", "PRIMARY_TDM_TX_4"}, \
+ { mix_name, "PRIMARY_TDM_TX_5", "PRIMARY_TDM_TX_5"}, \
+ { mix_name, "PRIMARY_TDM_TX_6", "PRIMARY_TDM_TX_6"}, \
+ { mix_name, "PRIMARY_TDM_TX_7", "PRIMARY_TDM_TX_7"}, \
+ { mix_name, "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, \
+ { mix_name, "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, \
+ { mix_name, "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, \
+ { mix_name, "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, \
+ { mix_name, "SEC_TDM_TX_4", "SEC_TDM_TX_4"}, \
+ { mix_name, "SEC_TDM_TX_5", "SEC_TDM_TX_5"}, \
+ { mix_name, "SEC_TDM_TX_6", "SEC_TDM_TX_6"}, \
+ { mix_name, "SEC_TDM_TX_7", "SEC_TDM_TX_7"}, \
+ { mix_name, "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, \
+ { mix_name, "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, \
+ { mix_name, "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, \
+ { mix_name, "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, \
+ { mix_name, "TERT_TDM_TX_4", "TERT_TDM_TX_4"}, \
+ { mix_name, "TERT_TDM_TX_5", "TERT_TDM_TX_5"}, \
+ { mix_name, "TERT_TDM_TX_6", "TERT_TDM_TX_6"}, \
+ { mix_name, "TERT_TDM_TX_7", "TERT_TDM_TX_7"}, \
+ { mix_name, "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, \
+ { mix_name, "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, \
+ { mix_name, "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, \
+ { mix_name, "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, \
+ { mix_name, "QUAT_TDM_TX_4", "QUAT_TDM_TX_4"}, \
+ { mix_name, "QUAT_TDM_TX_5", "QUAT_TDM_TX_5"}, \
+ { mix_name, "QUAT_TDM_TX_6", "QUAT_TDM_TX_6"}, \
+ { mix_name, "QUAT_TDM_TX_7", "QUAT_TDM_TX_7"}, \
+ { mix_name, "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, \
+ { mix_name, "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, \
+ { mix_name, "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, \
+ { mix_name, "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, \
+ { mix_name, "QUIN_TDM_TX_4", "QUIN_TDM_TX_4"}, \
+ { mix_name, "QUIN_TDM_TX_5", "QUIN_TDM_TX_5"}, \
+ { mix_name, "QUIN_TDM_TX_6", "QUIN_TDM_TX_6"}, \
+ { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"}, \
+ { mix_name, "WSA_CODEC_DMA_TX_0", "WSA_CODEC_DMA_TX_0"}, \
+ { mix_name, "WSA_CODEC_DMA_TX_1", "WSA_CODEC_DMA_TX_1"}, \
+ { mix_name, "WSA_CODEC_DMA_TX_2", "WSA_CODEC_DMA_TX_2"}, \
+ { mix_name, "VA_CODEC_DMA_TX_0", "VA_CODEC_DMA_TX_0"}, \
+ { mix_name, "VA_CODEC_DMA_TX_1", "VA_CODEC_DMA_TX_1"}, \
+ { mix_name, "VA_CODEC_DMA_TX_2", "VA_CODEC_DMA_TX_2"}, \
+ { mix_name, "TX_CODEC_DMA_TX_0", "TX_CODEC_DMA_TX_0"}, \
+ { mix_name, "TX_CODEC_DMA_TX_1", "TX_CODEC_DMA_TX_1"}, \
+ { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \
+ { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \
+ { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \
+ { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}
+
+#define Q6ROUTING_TX_MIXERS(id) \
+ SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_MI2S_TX", SECONDARY_MI2S_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_MI2S_TX", TERTIARY_MI2S_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_MI2S_TX", QUATERNARY_MI2S_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_MI2S_TX", QUINARY_MI2S_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_0_TX", SLIMBUS_0_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_1_TX", SLIMBUS_1_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_2_TX", SLIMBUS_2_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_3_TX", SLIMBUS_3_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_4_TX", SLIMBUS_4_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_5_TX", SLIMBUS_5_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SLIMBUS_6_TX", SLIMBUS_6_TX, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_0", PRIMARY_TDM_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_1", PRIMARY_TDM_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_2", PRIMARY_TDM_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_3", PRIMARY_TDM_TX_3, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_4", PRIMARY_TDM_TX_4, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_5", PRIMARY_TDM_TX_5, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_6", PRIMARY_TDM_TX_6, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("PRIMARY_TDM_TX_7", PRIMARY_TDM_TX_7, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_0", SECONDARY_TDM_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_1", SECONDARY_TDM_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_2", SECONDARY_TDM_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_3", SECONDARY_TDM_TX_3, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_4", SECONDARY_TDM_TX_4, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_5", SECONDARY_TDM_TX_5, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_6", SECONDARY_TDM_TX_6, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("SEC_TDM_TX_7", SECONDARY_TDM_TX_7, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_0", TERTIARY_TDM_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_1", TERTIARY_TDM_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_2", TERTIARY_TDM_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_3", TERTIARY_TDM_TX_3, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_4", TERTIARY_TDM_TX_4, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_5", TERTIARY_TDM_TX_5, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_6", TERTIARY_TDM_TX_6, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TERT_TDM_TX_7", TERTIARY_TDM_TX_7, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", QUATERNARY_TDM_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_1", QUATERNARY_TDM_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_2", QUATERNARY_TDM_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_3", QUATERNARY_TDM_TX_3, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_4", QUATERNARY_TDM_TX_4, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_5", QUATERNARY_TDM_TX_5, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_6", QUATERNARY_TDM_TX_6, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUAT_TDM_TX_7", QUATERNARY_TDM_TX_7, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_0", QUINARY_TDM_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_1", QUINARY_TDM_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_2", QUINARY_TDM_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_3", QUINARY_TDM_TX_3, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_4", QUINARY_TDM_TX_4, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_5", QUINARY_TDM_TX_5, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_6", QUINARY_TDM_TX_6, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("QUIN_TDM_TX_7", QUINARY_TDM_TX_7, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_0", WSA_CODEC_DMA_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_1", WSA_CODEC_DMA_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_2", WSA_CODEC_DMA_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("VA_CODEC_DMA_TX_0", VA_CODEC_DMA_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("VA_CODEC_DMA_TX_1", VA_CODEC_DMA_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("VA_CODEC_DMA_TX_2", VA_CODEC_DMA_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_0", TX_CODEC_DMA_TX_0, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_1", TX_CODEC_DMA_TX_1, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_2", TX_CODEC_DMA_TX_2, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_3", TX_CODEC_DMA_TX_3, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_4", TX_CODEC_DMA_TX_4, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer), \
+ SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \
+ id, 1, 0, msm_routing_get_audio_mixer, \
+ msm_routing_put_audio_mixer),
+
+struct session_data {
+ int state;
+ int port_id;
+ int path_type;
+ int app_type;
+ int acdb_id;
+ int sample_rate;
+ int bits_per_sample;
+ int channels;
+ int perf_mode;
+ int numcopps;
+ int fedai_id;
+ unsigned long copp_map;
+ struct q6copp *copps[MAX_COPPS_PER_PORT];
+};
+
+struct msm_routing_data {
+ struct session_data sessions[MAX_SESSIONS];
+ struct session_data port_data[AFE_MAX_PORTS];
+ struct device *dev;
+ struct mutex lock;
+};
+
+static struct msm_routing_data *routing_data;
+
+/**
+ * q6routing_stream_open() - Register a new stream for route setup
+ *
+ * @fedai_id: Frontend dai id.
+ * @perf_mode: Performance mode.
+ * @stream_id: ASM stream id to map.
+ * @stream_type: Direction of stream
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int q6routing_stream_open(int fedai_id, int perf_mode,
+ int stream_id, int stream_type)
+{
+ int j, topology, num_copps = 0;
+ struct route_payload payload;
+ struct q6copp *copp;
+ int copp_idx;
+ struct session_data *session, *pdata;
+
+ if (!routing_data) {
+ pr_err("Routing driver not yet ready\n");
+ return -EINVAL;
+ }
+
+ session = &routing_data->sessions[stream_id - 1];
+ if (session->port_id < 0) {
+ dev_err(routing_data->dev, "Routing not setup for MultiMedia%d Session\n",
+ session->fedai_id);
+ return -EINVAL;
+ }
+
+ pdata = &routing_data->port_data[session->port_id];
+
+ mutex_lock(&routing_data->lock);
+ session->fedai_id = fedai_id;
+
+ session->path_type = pdata->path_type;
+ session->sample_rate = pdata->sample_rate;
+ session->channels = pdata->channels;
+ session->bits_per_sample = pdata->bits_per_sample;
+
+ payload.num_copps = 0; /* only RX needs to use payload */
+ topology = NULL_COPP_TOPOLOGY;
+ copp = q6adm_open(routing_data->dev, session->port_id,
+ session->path_type, session->sample_rate,
+ session->channels, topology, perf_mode,
+ session->bits_per_sample, 0, 0);
+
+ if (IS_ERR_OR_NULL(copp)) {
+ mutex_unlock(&routing_data->lock);
+ return -EINVAL;
+ }
+
+ copp_idx = q6adm_get_copp_id(copp);
+ set_bit(copp_idx, &session->copp_map);
+ session->copps[copp_idx] = copp;
+
+ for_each_set_bit(j, &session->copp_map, MAX_COPPS_PER_PORT) {
+ payload.port_id[num_copps] = session->port_id;
+ payload.copp_idx[num_copps] = j;
+ num_copps++;
+ }
+
+ if (num_copps) {
+ payload.num_copps = num_copps;
+ payload.session_id = stream_id;
+ q6adm_matrix_map(routing_data->dev, session->path_type,
+ payload, perf_mode);
+ }
+ mutex_unlock(&routing_data->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(q6routing_stream_open);
+
+static struct session_data *get_session_from_id(struct msm_routing_data *data,
+ int fedai_id)
+{
+ int i;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ if (fedai_id == data->sessions[i].fedai_id)
+ return &data->sessions[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * q6routing_stream_close() - Deregister a stream
+ *
+ * @fedai_id: Frontend dai id.
+ * @stream_type: Direction of stream
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+void q6routing_stream_close(int fedai_id, int stream_type)
+{
+ struct session_data *session;
+ int idx;
+
+ session = get_session_from_id(routing_data, fedai_id);
+ if (!session)
+ return;
+
+ for_each_set_bit(idx, &session->copp_map, MAX_COPPS_PER_PORT) {
+ if (session->copps[idx]) {
+ q6adm_close(routing_data->dev, session->copps[idx]);
+ session->copps[idx] = NULL;
+ }
+ }
+
+ session->fedai_id = -1;
+ session->copp_map = 0;
+}
+EXPORT_SYMBOL_GPL(q6routing_stream_close);
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int session_id = mc->shift;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct msm_routing_data *priv = dev_get_drvdata(c->dev);
+ struct session_data *session = &priv->sessions[session_id];
+
+ if (session->port_id == mc->reg)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct msm_routing_data *data = dev_get_drvdata(c->dev);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ int be_id = mc->reg;
+ int session_id = mc->shift;
+ struct session_data *session = &data->sessions[session_id];
+
+ if (ucontrol->value.integer.value[0]) {
+ if (session->port_id == be_id)
+ return 0;
+
+ session->port_id = be_id;
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update);
+ } else {
+ if (session->port_id == -1 || session->port_id != be_id)
+ return 0;
+
+ session->port_id = -1;
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update);
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new usb_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(USB_RX) };
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(HDMI_RX) };
+
+static const struct snd_kcontrol_new display_port_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(DISPLAY_PORT_RX) };
+
+static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_MI2S_RX) };
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_MI2S_RX) };
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_MI2S_RX) };
+
+static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_MI2S_RX) };
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_MI2S_RX) };
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_0_RX) };
+
+static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_1_RX) };
+
+static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_2_RX) };
+
+static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_3_RX) };
+
+static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_4_RX) };
+
+static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_5_RX) };
+
+static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SLIMBUS_6_RX) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_0) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_1) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_2) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_3) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_4_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_4) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_5_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_5) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_6_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_6) };
+
+static const struct snd_kcontrol_new pri_tdm_rx_7_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_7) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_0) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_1) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_2) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_3) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_4_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_4) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_5_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_5) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_6_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_6) };
+
+static const struct snd_kcontrol_new sec_tdm_rx_7_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_7) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_0) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_1) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_2) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_3) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_4) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_5_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_5) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_6_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_6) };
+
+static const struct snd_kcontrol_new tert_tdm_rx_7_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_7) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_0) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_1) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_2) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_3) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_4_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_4) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_5_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_5) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_6_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_6) };
+
+static const struct snd_kcontrol_new quat_tdm_rx_7_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_7) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_0) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_1) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_2_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_2) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_3_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_3) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_4_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_4) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_5_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_5) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_6_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_6) };
+
+static const struct snd_kcontrol_new quin_tdm_rx_7_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_7) };
+
+static const struct snd_kcontrol_new wsa_codec_dma_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_0) };
+
+static const struct snd_kcontrol_new wsa_codec_dma_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_1) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_0_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_0) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_1_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_1) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_2_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_2) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_3_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_3) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_4_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_4) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_5_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_5) };
+
+static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_6) };
+
+static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) };
+
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) };
+
+static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA2) };
+
+static const struct snd_kcontrol_new mmul3_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA3) };
+
+static const struct snd_kcontrol_new mmul4_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA4) };
+
+static const struct snd_kcontrol_new mmul5_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA5) };
+
+static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA6) };
+
+static const struct snd_kcontrol_new mmul7_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA7) };
+
+static const struct snd_kcontrol_new mmul8_mixer_controls[] = {
+ Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA8) };
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls,
+ ARRAY_SIZE(hdmi_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ display_port_mixer_controls,
+ ARRAY_SIZE(display_port_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_1_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_1_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_2_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_2_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_2_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_3_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_3_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_3_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_4_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_4_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_5_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_5_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_5_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_6_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_6_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ primary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(primary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quaternary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quinary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(quinary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_4_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_5_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_6_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_7_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_7_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_4_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_5_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_6_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_7_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_7_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_4_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_5_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_6_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_7_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_7_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_4_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_5_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_6_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_7_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_7_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_4_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_5_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_6_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quin_tdm_rx_7_mixer_controls,
+ ARRAY_SIZE(quin_tdm_rx_7_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ wsa_codec_dma_rx_0_mixer_controls,
+ ARRAY_SIZE(wsa_codec_dma_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ wsa_codec_dma_rx_1_mixer_controls,
+ ARRAY_SIZE(wsa_codec_dma_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_0_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_1_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_2_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_3_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_4_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_5_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rxcodec_dma_rx_6_mixer_controls,
+ ARRAY_SIZE(rxcodec_dma_rx_6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ rx_codec_dma_rx_7_mixer_controls,
+ ARRAY_SIZE(rx_codec_dma_rx_7_mixer_controls)),
+ SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ usb_rx_mixer_controls,
+ ARRAY_SIZE(usb_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia3 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul3_mixer_controls, ARRAY_SIZE(mmul3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia7 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul7_mixer_controls, ARRAY_SIZE(mmul7_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ Q6ROUTING_RX_DAPM_ROUTE("HDMI Mixer", "HDMI_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("DISPLAY_PORT_RX Audio Mixer",
+ "DISPLAY_PORT_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_0_RX Audio Mixer", "SLIMBUS_0_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_1_RX Audio Mixer", "SLIMBUS_1_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_2_RX Audio Mixer", "SLIMBUS_2_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_3_RX Audio Mixer", "SLIMBUS_3_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_4_RX Audio Mixer", "SLIMBUS_4_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_5_RX Audio Mixer", "SLIMBUS_5_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_6_RX Audio Mixer", "SLIMBUS_6_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_MI2S_RX Audio Mixer", "QUAT_MI2S_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_MI2S_RX Audio Mixer", "QUIN_MI2S_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_MI2S_RX Audio Mixer", "TERT_MI2S_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_MI2S_RX Audio Mixer", "SEC_MI2S_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRI_MI2S_RX Audio Mixer", "PRI_MI2S_RX"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_0 Audio Mixer",
+ "PRIMARY_TDM_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_1 Audio Mixer",
+ "PRIMARY_TDM_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_2 Audio Mixer",
+ "PRIMARY_TDM_RX_2"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_3 Audio Mixer",
+ "PRIMARY_TDM_RX_3"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_4 Audio Mixer",
+ "PRIMARY_TDM_RX_4"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_5 Audio Mixer",
+ "PRIMARY_TDM_RX_5"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_6 Audio Mixer",
+ "PRIMARY_TDM_RX_6"),
+ Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_7 Audio Mixer",
+ "PRIMARY_TDM_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_0 Audio Mixer", "SEC_TDM_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_1 Audio Mixer", "SEC_TDM_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_2 Audio Mixer", "SEC_TDM_RX_2"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_3 Audio Mixer", "SEC_TDM_RX_3"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_4 Audio Mixer", "SEC_TDM_RX_4"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_5 Audio Mixer", "SEC_TDM_RX_5"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_6 Audio Mixer", "SEC_TDM_RX_6"),
+ Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_7 Audio Mixer", "SEC_TDM_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_0 Audio Mixer", "TERT_TDM_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_1 Audio Mixer", "TERT_TDM_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_2 Audio Mixer", "TERT_TDM_RX_2"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_3 Audio Mixer", "TERT_TDM_RX_3"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_4 Audio Mixer", "TERT_TDM_RX_4"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_5 Audio Mixer", "TERT_TDM_RX_5"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_6 Audio Mixer", "TERT_TDM_RX_6"),
+ Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_7 Audio Mixer", "TERT_TDM_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_0 Audio Mixer", "QUAT_TDM_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_1 Audio Mixer", "QUAT_TDM_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_2 Audio Mixer", "QUAT_TDM_RX_2"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_3 Audio Mixer", "QUAT_TDM_RX_3"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_4 Audio Mixer", "QUAT_TDM_RX_4"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_5 Audio Mixer", "QUAT_TDM_RX_5"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_6 Audio Mixer", "QUAT_TDM_RX_6"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_7 Audio Mixer", "QUAT_TDM_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_0 Audio Mixer", "QUIN_TDM_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_1 Audio Mixer", "QUIN_TDM_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_2 Audio Mixer", "QUIN_TDM_RX_2"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_3 Audio Mixer", "QUIN_TDM_RX_3"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_4 Audio Mixer", "QUIN_TDM_RX_4"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_5 Audio Mixer", "QUIN_TDM_RX_5"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_6 Audio Mixer", "QUIN_TDM_RX_6"),
+ Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_7 Audio Mixer", "QUIN_TDM_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_0 Audio Mixer", "WSA_CODEC_DMA_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_1 Audio Mixer", "WSA_CODEC_DMA_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_0 Audio Mixer", "RX_CODEC_DMA_RX_0"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_1 Audio Mixer", "RX_CODEC_DMA_RX_1"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_2 Audio Mixer", "RX_CODEC_DMA_RX_2"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_3 Audio Mixer", "RX_CODEC_DMA_RX_3"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_4 Audio Mixer", "RX_CODEC_DMA_RX_4"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_5 Audio Mixer", "RX_CODEC_DMA_RX_5"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"),
+ Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia4 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia5 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia6 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia7 Mixer"),
+ Q6ROUTING_TX_DAPM_ROUTE("MultiMedia8 Mixer"),
+
+ {"MM_UL1", NULL, "MultiMedia1 Mixer"},
+ {"MM_UL2", NULL, "MultiMedia2 Mixer"},
+ {"MM_UL3", NULL, "MultiMedia3 Mixer"},
+ {"MM_UL4", NULL, "MultiMedia4 Mixer"},
+ {"MM_UL5", NULL, "MultiMedia5 Mixer"},
+ {"MM_UL6", NULL, "MultiMedia6 Mixer"},
+ {"MM_UL7", NULL, "MultiMedia7 Mixer"},
+ {"MM_UL8", NULL, "MultiMedia8 Mixer"},
+};
+
+static int routing_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct msm_routing_data *data = dev_get_drvdata(component->dev);
+ unsigned int be_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct session_data *session;
+ int path_type;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ path_type = ADM_PATH_PLAYBACK;
+ else
+ path_type = ADM_PATH_LIVE_REC;
+
+ if (be_id >= AFE_MAX_PORTS)
+ return -EINVAL;
+
+ session = &data->port_data[be_id];
+
+ mutex_lock(&data->lock);
+
+ session->path_type = path_type;
+ session->sample_rate = params_rate(params);
+ session->channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ session->bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ session->bits_per_sample = 24;
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&data->lock);
+ return 0;
+}
+
+static int msm_routing_probe(struct snd_soc_component *c)
+{
+ int i;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ routing_data->sessions[i].port_id = -1;
+ routing_data->sessions[i].fedai_id = -1;
+ }
+
+ return 0;
+}
+
+static unsigned int q6routing_reg_read(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ /* default value */
+ return 0;
+}
+
+static int q6routing_reg_write(struct snd_soc_component *component,
+ unsigned int reg, unsigned int val)
+{
+ /* dummy */
+ return 0;
+}
+
+static const struct snd_soc_component_driver msm_soc_routing_component = {
+ .probe = msm_routing_probe,
+ .name = DRV_NAME,
+ .hw_params = routing_hw_params,
+ .dapm_widgets = msm_qdsp6_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ .read = q6routing_reg_read,
+ .write = q6routing_reg_write,
+};
+
+static int q6pcm_routing_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ routing_data = kzalloc(sizeof(*routing_data), GFP_KERNEL);
+ if (!routing_data)
+ return -ENOMEM;
+
+ routing_data->dev = dev;
+
+ mutex_init(&routing_data->lock);
+ dev_set_drvdata(dev, routing_data);
+
+ return devm_snd_soc_register_component(dev, &msm_soc_routing_component,
+ NULL, 0);
+}
+
+static void q6pcm_routing_remove(struct platform_device *pdev)
+{
+ kfree(routing_data);
+ routing_data = NULL;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id q6pcm_routing_device_id[] = {
+ { .compatible = "qcom,q6adm-routing" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id);
+#endif
+
+static struct platform_driver q6pcm_routing_platform_driver = {
+ .driver = {
+ .name = "q6routing",
+ .of_match_table = of_match_ptr(q6pcm_routing_device_id),
+ },
+ .probe = q6pcm_routing_probe,
+ .remove = q6pcm_routing_remove,
+};
+module_platform_driver(q6pcm_routing_platform_driver);
+
+MODULE_DESCRIPTION("Q6 Routing platform");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6routing.h b/sound/soc/qcom/qdsp6/q6routing.h
new file mode 100644
index 000000000000..35514e651130
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6routing.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _Q6_PCM_ROUTING_H
+#define _Q6_PCM_ROUTING_H
+
+int q6routing_stream_open(int fedai_id, int perf_mode,
+ int stream_id, int stream_type);
+void q6routing_stream_close(int fedai_id, int stream_type);
+
+#endif /*_Q6_PCM_ROUTING_H */
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
new file mode 100644
index 000000000000..6381b289c55c
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6usb.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-map-ops.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/asound.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/q6usboffload.h>
+#include <sound/soc.h>
+#include <sound/soc-usb.h>
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+
+#include "q6afe.h"
+#include "q6dsp-lpass-ports.h"
+
+#define Q6_USB_SID_MASK 0xF
+
+struct q6usb_port_data {
+ struct auxiliary_device uauxdev;
+ struct q6afe_usb_cfg usb_cfg;
+ struct snd_soc_usb *usb;
+ struct snd_soc_jack *hs_jack;
+ struct q6usb_offload priv;
+
+ /* Protects against operations between SOC USB and ASoC */
+ struct mutex mutex;
+ struct list_head devices;
+};
+
+static const struct snd_soc_dapm_widget q6usb_dai_widgets[] = {
+ SND_SOC_DAPM_HP("USB_RX_BE", NULL),
+};
+
+static const struct snd_soc_dapm_route q6usb_dapm_routes[] = {
+ {"USB Playback", NULL, "USB_RX_BE"},
+};
+
+static int q6usb_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int direction = substream->stream;
+ struct q6afe_port *q6usb_afe;
+ struct snd_soc_usb_device *sdev;
+ int ret = -EINVAL;
+
+ mutex_lock(&data->mutex);
+
+ /* No active chip index */
+ if (list_empty(&data->devices))
+ goto out;
+
+ sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list);
+
+ ret = snd_soc_usb_find_supported_format(sdev->chip_idx, params, direction);
+ if (ret < 0)
+ goto out;
+
+ q6usb_afe = q6afe_port_get_from_id(cpu_dai->dev, USB_RX);
+ if (IS_ERR(q6usb_afe)) {
+ ret = PTR_ERR(q6usb_afe);
+ goto out;
+ }
+
+ /* Notify audio DSP about the devices being offloaded */
+ ret = afe_port_send_usb_dev_param(q6usb_afe, sdev->card_idx,
+ sdev->ppcm_idx[sdev->num_playback - 1]);
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops q6usb_ops = {
+ .hw_params = q6usb_hw_params,
+};
+
+static struct snd_soc_dai_driver q6usb_be_dais[] = {
+ {
+ .playback = {
+ .stream_name = "USB BE RX",
+ .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_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ },
+ .id = USB_RX,
+ .name = "USB_RX_BE",
+ .ops = &q6usb_ops,
+ },
+};
+
+static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ int id = args->args[0];
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(q6usb_be_dais); i++) {
+ if (q6usb_be_dais[i].id == id) {
+ *dai_name = q6usb_be_dais[i].name;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int q6usb_get_pcm_id_from_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *dai;
+
+ for_each_card_rtds(card, rtd) {
+ dai = snd_soc_rtd_to_cpu(rtd, 0);
+ /*
+ * Only look for playback widget. RTD number carries the assigned
+ * PCM index.
+ */
+ if (dai->stream[0].widget == w)
+ return rtd->id;
+ }
+
+ return -1;
+}
+
+static int q6usb_usb_mixer_enabled(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+
+ /* Checks to ensure USB path is enabled/connected */
+ snd_soc_dapm_widget_for_each_sink_path(w, p)
+ if (!strcmp(p->sink->name, "USB Mixer") && p->connect)
+ return 1;
+
+ return 0;
+}
+
+static int q6usb_get_pcm_id(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p;
+ int pidx;
+
+ /*
+ * Traverse widgets to find corresponding FE widget. The DAI links are
+ * built like the following:
+ * MultiMedia* <-> MM_DL* <-> USB Mixer*
+ */
+ for_each_card_widgets(component->card, w) {
+ if (!strncmp(w->name, "MultiMedia", 10)) {
+ /*
+ * Look up all paths associated with the FE widget to see if
+ * the USB BE is enabled. The sink widget is responsible to
+ * link with the USB mixers.
+ */
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (q6usb_usb_mixer_enabled(p->sink)) {
+ pidx = q6usb_get_pcm_id_from_widget(w);
+ return pidx;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int q6usb_update_offload_route(struct snd_soc_component *component, int card,
+ int pcm, int direction, enum snd_soc_usb_kctl path,
+ long *route)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+ struct snd_soc_usb_device *sdev;
+ int ret = 0;
+ int idx = -1;
+
+ mutex_lock(&data->mutex);
+
+ if (list_empty(&data->devices) ||
+ direction == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list);
+
+ /*
+ * Will always look for last PCM device discovered/probed as the
+ * active offload index.
+ */
+ if (card == sdev->card_idx &&
+ pcm == sdev->ppcm_idx[sdev->num_playback - 1]) {
+ idx = path == SND_SOC_USB_KCTL_CARD_ROUTE ?
+ component->card->snd_card->number :
+ q6usb_get_pcm_id(component);
+ }
+
+out:
+ route[0] = idx;
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
+ struct snd_soc_usb_device *sdev, bool connected)
+{
+ struct q6usb_port_data *data;
+
+ if (!usb->component)
+ return -ENODEV;
+
+ data = dev_get_drvdata(usb->component->dev);
+
+ mutex_lock(&data->mutex);
+ if (connected) {
+ if (data->hs_jack)
+ snd_jack_report(data->hs_jack->jack, SND_JACK_USB);
+
+ /* Selects the latest USB headset plugged in for offloading */
+ list_add_tail(&sdev->list, &data->devices);
+ } else {
+ list_del(&sdev->list);
+
+ if (data->hs_jack)
+ snd_jack_report(data->hs_jack->jack, 0);
+ }
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static void q6usb_component_disable_jack(struct q6usb_port_data *data)
+{
+ /* Offload jack has already been disabled */
+ if (!data->hs_jack)
+ return;
+
+ snd_jack_report(data->hs_jack->jack, 0);
+ data->hs_jack = NULL;
+}
+
+static void q6usb_component_enable_jack(struct q6usb_port_data *data,
+ struct snd_soc_jack *jack)
+{
+ snd_jack_report(jack->jack, !list_empty(&data->devices) ? SND_JACK_USB : 0);
+ data->hs_jack = jack;
+}
+
+static int q6usb_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *priv)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+
+ mutex_lock(&data->mutex);
+ if (jack)
+ q6usb_component_enable_jack(data, jack);
+ else
+ q6usb_component_disable_jack(data);
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static void q6usb_dai_aux_release(struct device *dev) {}
+
+static int q6usb_dai_add_aux_device(struct q6usb_port_data *data,
+ struct auxiliary_device *auxdev)
+{
+ int ret;
+
+ auxdev->dev.parent = data->priv.dev;
+ auxdev->dev.release = q6usb_dai_aux_release;
+ auxdev->name = "qc-usb-audio-offload";
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret)
+ auxiliary_device_uninit(auxdev);
+
+ return ret;
+}
+
+static int q6usb_component_probe(struct snd_soc_component *component)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+ struct snd_soc_usb *usb;
+ int ret;
+
+ /* Add the QC USB SND aux device */
+ ret = q6usb_dai_add_aux_device(data, &data->uauxdev);
+ if (ret < 0)
+ return ret;
+
+ usb = snd_soc_usb_allocate_port(component, &data->priv);
+ if (IS_ERR(usb))
+ return -ENOMEM;
+
+ usb->connection_status_cb = q6usb_alsa_connection_cb;
+ usb->update_offload_route_info = q6usb_update_offload_route;
+
+ snd_soc_usb_add_port(usb);
+ data->usb = usb;
+
+ return 0;
+}
+
+static void q6usb_component_remove(struct snd_soc_component *component)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+
+ snd_soc_usb_remove_port(data->usb);
+ auxiliary_device_delete(&data->uauxdev);
+ auxiliary_device_uninit(&data->uauxdev);
+ snd_soc_usb_free_port(data->usb);
+}
+
+static const struct snd_soc_component_driver q6usb_dai_component = {
+ .probe = q6usb_component_probe,
+ .set_jack = q6usb_component_set_jack,
+ .remove = q6usb_component_remove,
+ .name = "q6usb-dai-component",
+ .dapm_widgets = q6usb_dai_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(q6usb_dai_widgets),
+ .dapm_routes = q6usb_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(q6usb_dapm_routes),
+ .of_xlate_dai_name = q6usb_audio_ports_of_xlate_dai_name,
+};
+
+static int q6usb_dai_dev_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct q6usb_port_data *data;
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args args;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = of_property_read_u16(node, "qcom,usb-audio-intr-idx",
+ &data->priv.intr_num);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read intr idx.\n");
+ return ret;
+ }
+
+ ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
+ if (!ret)
+ data->priv.sid = args.args[0] & Q6_USB_SID_MASK;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret < 0)
+ return ret;
+
+ data->priv.domain = iommu_get_domain_for_dev(&pdev->dev);
+
+ data->priv.dev = dev;
+ INIT_LIST_HEAD(&data->devices);
+ dev_set_drvdata(dev, data);
+
+ return devm_snd_soc_register_component(dev, &q6usb_dai_component,
+ q6usb_be_dais, ARRAY_SIZE(q6usb_be_dais));
+}
+
+static const struct of_device_id q6usb_dai_device_id[] = {
+ { .compatible = "qcom,q6usb" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6usb_dai_device_id);
+
+static struct platform_driver q6usb_dai_platform_driver = {
+ .driver = {
+ .name = "q6usb-dai",
+ .of_match_table = q6usb_dai_device_id,
+ },
+ .probe = q6usb_dai_dev_probe,
+ /*
+ * Remove not required as resources are cleaned up as part of
+ * component removal. Others are device managed resources.
+ */
+};
+module_platform_driver(q6usb_dai_platform_driver);
+
+MODULE_DESCRIPTION("Q6 USB backend dai driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
new file mode 100644
index 000000000000..5ce6edf3305e
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -0,0 +1,1340 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <linux/cleanup.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asound.h>
+#include <linux/firmware.h>
+#include <sound/soc-topology.h>
+#include <sound/soc-dpcm.h>
+#include <uapi/sound/snd_ar_tokens.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include "q6apm.h"
+#include "audioreach.h"
+
+struct snd_ar_control {
+ u32 graph_id; /* Graph ID */
+ u32 sgid; /* Sub Graph ID */
+ u32 module_instance_id; /* Connected Module Instance ID */
+ struct snd_soc_dapm_widget *w;
+ struct list_head node;
+ struct snd_soc_component *scomp;
+};
+
+static struct audioreach_graph_info *audioreach_tplg_alloc_graph_info(struct q6apm *apm,
+ uint32_t graph_id,
+ bool *found)
+{
+ struct audioreach_graph_info *info;
+ int ret;
+
+ mutex_lock(&apm->lock);
+ info = idr_find(&apm->graph_info_idr, graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (info) {
+ *found = true;
+ return info;
+ }
+
+ *found = false;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&info->sg_list);
+
+ mutex_lock(&apm->lock);
+ ret = idr_alloc_u32(&apm->graph_info_idr, info, &graph_id, graph_id, GFP_KERNEL);
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Graph ID (%x)\n", graph_id);
+ kfree(info);
+ return ERR_PTR(ret);
+ }
+
+ info->id = graph_id;
+
+ return info;
+}
+
+static void audioreach_tplg_add_sub_graph(struct audioreach_sub_graph *sg,
+ struct audioreach_graph_info *info)
+{
+ list_add_tail(&sg->node, &info->sg_list);
+ sg->info = info;
+ info->num_sub_graphs++;
+}
+
+static struct audioreach_sub_graph *audioreach_tplg_alloc_sub_graph(struct q6apm *apm,
+ uint32_t sub_graph_id,
+ bool *found)
+{
+ struct audioreach_sub_graph *sg;
+ int ret;
+
+ if (!sub_graph_id)
+ return ERR_PTR(-EINVAL);
+
+ /* Find if there is already a matching sub-graph */
+ mutex_lock(&apm->lock);
+ sg = idr_find(&apm->sub_graphs_idr, sub_graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (sg) {
+ *found = true;
+ return sg;
+ }
+
+ *found = false;
+ sg = kzalloc(sizeof(*sg), GFP_KERNEL);
+ if (!sg)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&sg->container_list);
+
+ mutex_lock(&apm->lock);
+ ret = idr_alloc_u32(&apm->sub_graphs_idr, sg, &sub_graph_id, sub_graph_id, GFP_KERNEL);
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Sub-Graph Instance ID (%x)\n", sub_graph_id);
+ kfree(sg);
+ return ERR_PTR(ret);
+ }
+
+ sg->sub_graph_id = sub_graph_id;
+
+ return sg;
+}
+
+static struct audioreach_container *audioreach_tplg_alloc_container(struct q6apm *apm,
+ struct audioreach_sub_graph *sg,
+ uint32_t container_id,
+ bool *found)
+{
+ struct audioreach_container *cont;
+ int ret;
+
+ if (!container_id)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&apm->lock);
+ cont = idr_find(&apm->containers_idr, container_id);
+ mutex_unlock(&apm->lock);
+
+ if (cont) {
+ *found = true;
+ return cont;
+ }
+ *found = false;
+
+ cont = kzalloc(sizeof(*cont), GFP_KERNEL);
+ if (!cont)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&cont->modules_list);
+
+ mutex_lock(&apm->lock);
+ ret = idr_alloc_u32(&apm->containers_idr, cont, &container_id, container_id, GFP_KERNEL);
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Container Instance ID (%x)\n", container_id);
+ kfree(cont);
+ return ERR_PTR(ret);
+ }
+
+ cont->container_id = container_id;
+ cont->sub_graph = sg;
+ /* add to container list */
+ list_add_tail(&cont->node, &sg->container_list);
+ sg->num_containers++;
+
+ return cont;
+}
+
+static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm,
+ struct audioreach_container *cont,
+ struct snd_soc_dapm_widget *w,
+ uint32_t module_id, bool *found)
+{
+ struct audioreach_module *mod;
+ int ret;
+
+ mutex_lock(&apm->lock);
+ mod = idr_find(&apm->modules_idr, module_id);
+ mutex_unlock(&apm->lock);
+
+ if (mod) {
+ *found = true;
+ return mod;
+ }
+ *found = false;
+ mod = kzalloc(sizeof(*mod), GFP_KERNEL);
+ if (!mod)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&apm->lock);
+ if (!module_id) { /* alloc module id dynamically */
+ ret = idr_alloc_cyclic(&apm->modules_idr, mod,
+ AR_MODULE_DYNAMIC_INSTANCE_ID_START,
+ AR_MODULE_DYNAMIC_INSTANCE_ID_END, GFP_KERNEL);
+ } else {
+ ret = idr_alloc_u32(&apm->modules_idr, mod, &module_id, module_id, GFP_KERNEL);
+ }
+ mutex_unlock(&apm->lock);
+
+ if (ret < 0) {
+ dev_err(apm->dev, "Failed to allocate Module Instance ID (%x)\n", module_id);
+ kfree(mod);
+ return ERR_PTR(ret);
+ }
+
+ mod->instance_id = module_id;
+ /* add to module list */
+ list_add_tail(&mod->node, &cont->modules_list);
+ mod->container = cont;
+ mod->widget = w;
+ cont->num_modules++;
+
+ return mod;
+}
+
+static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_array *sg_array = NULL;
+ bool found = false;
+ int sz;
+
+ for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
+ struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ int tkn_count = 0;
+
+ sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ sg_elem = sg_array->value;
+ sz = sz + le32_to_cpu(sg_array->size);
+ while (!found && tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) {
+ switch (le32_to_cpu(sg_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ sg_elem++;
+ }
+ }
+
+ if (found)
+ return sg_array;
+
+ return NULL;
+}
+
+static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_array *cont_array = NULL;
+ bool found = false;
+ int sz;
+
+ for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
+ struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ int tkn_count = 0;
+
+ cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ cont_elem = cont_array->value;
+ sz = sz + le32_to_cpu(cont_array->size);
+ while (!found && tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) {
+ switch (le32_to_cpu(cont_elem->token)) {
+ case AR_TKN_U32_CONTAINER_INSTANCE_ID:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ cont_elem++;
+ }
+ }
+
+ if (found)
+ return cont_array;
+
+ return NULL;
+}
+
+static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_array *mod_array = NULL;
+ bool found = false;
+ int sz = 0;
+
+ for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ mod_elem = mod_array->value;
+ sz = sz + le32_to_cpu(mod_array->size);
+ while (!found && tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_INSTANCE_ID:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+ }
+
+ if (found)
+ return mod_array;
+
+ return NULL;
+}
+
+static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
+ struct snd_soc_tplg_private *private)
+{
+ int sz;
+
+ for (sz = 0; sz < le32_to_cpu(private->size); ) {
+ struct snd_soc_tplg_vendor_array *mod_array;
+
+ mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
+ struct audioreach_module_priv_data *pdata;
+
+ pdata = kzalloc(struct_size(pdata, data, le32_to_cpu(mod_array->size)),
+ GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data,
+ le32_to_cpu(mod_array->size)));
+ return pdata;
+ }
+
+ sz = sz + le32_to_cpu(mod_array->size);
+ }
+
+ return NULL;
+}
+
+static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ struct snd_soc_tplg_vendor_array *sg_array;
+ struct audioreach_graph_info *info = NULL;
+ int graph_id, sub_graph_id, tkn_count = 0;
+ struct audioreach_sub_graph *sg;
+ bool found;
+
+ sg_array = audioreach_get_sg_array(private);
+ sg_elem = sg_array->value;
+
+ while (tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) {
+ switch (le32_to_cpu(sg_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ sub_graph_id = le32_to_cpu(sg_elem->value);
+ sg = audioreach_tplg_alloc_sub_graph(apm, sub_graph_id, &found);
+ if (IS_ERR(sg)) {
+ return sg;
+ } else if (found) {
+ /* Already parsed data for this sub-graph */
+ return sg;
+ }
+ break;
+ case AR_TKN_DAI_INDEX:
+ /* Sub graph is associated with predefined graph */
+ graph_id = le32_to_cpu(sg_elem->value);
+ info = audioreach_tplg_alloc_graph_info(apm, graph_id, &found);
+ if (IS_ERR(info))
+ return ERR_CAST(info);
+ break;
+ case AR_TKN_U32_SUB_GRAPH_PERF_MODE:
+ sg->perf_mode = le32_to_cpu(sg_elem->value);
+ break;
+ case AR_TKN_U32_SUB_GRAPH_DIRECTION:
+ sg->direction = le32_to_cpu(sg_elem->value);
+ break;
+ case AR_TKN_U32_SUB_GRAPH_SCENARIO_ID:
+ sg->scenario_id = le32_to_cpu(sg_elem->value);
+ break;
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n", sg_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ sg_elem++;
+ }
+
+ /* Sub graph is associated with predefined graph */
+ if (info)
+ audioreach_tplg_add_sub_graph(sg, info);
+
+ return sg;
+}
+
+static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm,
+ struct audioreach_sub_graph *sg,
+ struct snd_soc_tplg_private *private)
+{
+ struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ struct snd_soc_tplg_vendor_array *cont_array;
+ struct audioreach_container *cont;
+ int container_id, tkn_count = 0;
+ bool found = false;
+
+ cont_array = audioreach_get_cont_array(private);
+ cont_elem = cont_array->value;
+
+ while (tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) {
+ switch (le32_to_cpu(cont_elem->token)) {
+ case AR_TKN_U32_CONTAINER_INSTANCE_ID:
+ container_id = le32_to_cpu(cont_elem->value);
+ cont = audioreach_tplg_alloc_container(apm, sg, container_id, &found);
+ if (IS_ERR(cont) || found)/* Error or Already parsed container data */
+ return cont;
+ break;
+ case AR_TKN_U32_CONTAINER_CAPABILITY_ID:
+ cont->capability_id = le32_to_cpu(cont_elem->value);
+ break;
+ case AR_TKN_U32_CONTAINER_STACK_SIZE:
+ cont->stack_size = le32_to_cpu(cont_elem->value);
+ break;
+ case AR_TKN_U32_CONTAINER_GRAPH_POS:
+ cont->graph_pos = le32_to_cpu(cont_elem->value);
+ break;
+ case AR_TKN_U32_CONTAINER_PROC_DOMAIN:
+ cont->proc_domain = le32_to_cpu(cont_elem->value);
+ break;
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n", cont_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ cont_elem++;
+ }
+
+ return cont;
+}
+
+static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm,
+ struct audioreach_container *cont,
+ struct snd_soc_tplg_private *private,
+ struct snd_soc_dapm_widget *w)
+{
+ uint32_t max_ip_port = 0, max_op_port = 0;
+ uint32_t src_mod_op_port_id[AR_MAX_MOD_LINKS] = { 0, };
+ uint32_t dst_mod_inst_id[AR_MAX_MOD_LINKS] = { 0, };
+ uint32_t dst_mod_ip_port_id[AR_MAX_MOD_LINKS] = { 0, };
+ uint32_t src_mod_inst_id = 0;
+
+ int module_id = 0, instance_id = 0, tkn_count = 0;
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audioreach_module *mod = NULL;
+ uint32_t token;
+ bool found;
+ int max_tokens;
+
+ mod_array = audioreach_get_module_array(private);
+ mod_elem = mod_array->value;
+ max_tokens = le32_to_cpu(mod_array->num_elems);
+ while (tkn_count <= (max_tokens - 1)) {
+ token = le32_to_cpu(mod_elem->token);
+ switch (token) {
+ /* common module info */
+ case AR_TKN_U32_MODULE_ID:
+ module_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_INSTANCE_ID:
+ instance_id = le32_to_cpu(mod_elem->value);
+ mod = audioreach_tplg_alloc_module(apm, cont, w,
+ instance_id, &found);
+ if (IS_ERR(mod)) {
+ return mod;
+ } else if (found) {
+ dev_err(apm->dev, "Duplicate Module Instance ID 0x%08x found\n",
+ instance_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ break;
+ case AR_TKN_U32_MODULE_MAX_IP_PORTS:
+ max_ip_port = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_MAX_OP_PORTS:
+ max_op_port = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_INSTANCE_ID:
+ src_mod_inst_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID:
+ src_mod_op_port_id[0] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID1:
+ src_mod_op_port_id[1] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID2:
+ src_mod_op_port_id[2] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID3:
+ src_mod_op_port_id[3] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID4:
+ src_mod_op_port_id[4] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID5:
+ src_mod_op_port_id[5] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID6:
+ src_mod_op_port_id[6] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SRC_OP_PORT_ID7:
+ src_mod_op_port_id[7] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID:
+ dst_mod_inst_id[0] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID1:
+ dst_mod_inst_id[1] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID2:
+ dst_mod_inst_id[2] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID3:
+ dst_mod_inst_id[3] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID4:
+ dst_mod_inst_id[4] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID5:
+ dst_mod_inst_id[5] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID6:
+ dst_mod_inst_id[6] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_INSTANCE_ID7:
+ dst_mod_inst_id[7] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID:
+ dst_mod_ip_port_id[0] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID1:
+ dst_mod_ip_port_id[1] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID2:
+ dst_mod_ip_port_id[2] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID3:
+ dst_mod_ip_port_id[3] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID4:
+ dst_mod_ip_port_id[4] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID5:
+ dst_mod_ip_port_id[5] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID6:
+ dst_mod_ip_port_id[6] = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_DST_IN_PORT_ID7:
+ dst_mod_ip_port_id[7] = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ if (mod) {
+ int pn, id = 0;
+
+ mod->module_id = module_id;
+ mod->max_ip_port = max_ip_port;
+ mod->max_op_port = max_op_port;
+ mod->src_mod_inst_id = src_mod_inst_id;
+ for (pn = 0; pn < mod->max_op_port; pn++) {
+ if (src_mod_op_port_id[pn] && dst_mod_inst_id[pn] &&
+ dst_mod_ip_port_id[pn]) {
+ mod->src_mod_op_port_id[id] = src_mod_op_port_id[pn];
+ mod->dst_mod_inst_id[id] = dst_mod_inst_id[pn];
+ mod->dst_mod_ip_port_id[id] = dst_mod_ip_port_id[pn];
+ id++;
+ mod->num_connections = id;
+ }
+ }
+ }
+
+ return mod;
+}
+
+static int audioreach_widget_load_module_common(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct q6apm *apm = dev_get_drvdata(component->dev);
+ struct audioreach_container *cont;
+ struct audioreach_sub_graph *sg;
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+
+ sg = audioreach_parse_sg_tokens(apm, &tplg_w->priv);
+ if (IS_ERR(sg))
+ return PTR_ERR(sg);
+
+ cont = audioreach_parse_cont_tokens(apm, sg, &tplg_w->priv);
+ if (IS_ERR(cont))
+ return PTR_ERR(cont);
+
+ mod = audioreach_parse_common_tokens(apm, cont, &tplg_w->priv, w);
+ if (IS_ERR_OR_NULL(mod))
+ return mod ? PTR_ERR(mod) : -ENODEV;
+
+ mod->data = audioreach_get_module_priv_data(&tplg_w->priv);
+
+ dobj = &w->dobj;
+ dobj->private = mod;
+
+ return 0;
+}
+
+static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+ int tkn_count = 0;
+ int ret;
+
+ ret = audioreach_widget_load_module_common(component, index, w, tplg_w);
+ if (ret)
+ return ret;
+
+ dobj = &w->dobj;
+ mod = dobj->private;
+ mod_array = audioreach_get_module_array(&tplg_w->priv);
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_FMT_INTERLEAVE:
+ mod->interleave_type = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_SAMPLE_RATE:
+ mod->rate = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_BIT_DEPTH:
+ mod->bit_depth = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_log_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+
+ case AR_TKN_U32_MODULE_LOG_CODE:
+ mod->log_code = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_LOG_TAP_POINT_ID:
+ mod->log_tap_point_id = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_LOG_MODE:
+ mod->log_mode = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_dma_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_HW_IF_IDX:
+ mod->hw_interface_idx = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_DATA:
+ mod->data_format = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_HW_IF_TYPE:
+ mod->hw_interface_type = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_HW_IF_IDX:
+ mod->hw_interface_idx = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_FMT_DATA:
+ mod->data_format = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_HW_IF_TYPE:
+ mod->hw_interface_type = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_SD_LINE_IDX:
+ mod->sd_line_idx = le32_to_cpu(mod_elem->value);
+ break;
+ case AR_TKN_U32_MODULE_WS_SRC:
+ mod->ws_src = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_dp_module_load(struct audioreach_module *mod,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_FMT_DATA:
+ mod->data_format = le32_to_cpu(mod_elem->value);
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_load_buffer(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+ int ret;
+
+ ret = audioreach_widget_load_module_common(component, index, w, tplg_w);
+ if (ret)
+ return ret;
+
+ dobj = &w->dobj;
+ mod = dobj->private;
+
+ mod_array = audioreach_get_module_array(&tplg_w->priv);
+
+ switch (mod->module_id) {
+ case MODULE_ID_CODEC_DMA_SINK:
+ case MODULE_ID_CODEC_DMA_SOURCE:
+ audioreach_widget_dma_module_load(mod, mod_array);
+ break;
+ case MODULE_ID_DATA_LOGGING:
+ audioreach_widget_log_module_load(mod, mod_array);
+ break;
+ case MODULE_ID_I2S_SINK:
+ case MODULE_ID_I2S_SOURCE:
+ audioreach_widget_i2s_module_load(mod, mod_array);
+ break;
+ case MODULE_ID_DISPLAY_PORT_SINK:
+ audioreach_widget_dp_module_load(mod, mod_array);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_load_mixer(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_value_elem *w_elem;
+ struct snd_soc_tplg_vendor_array *w_array;
+ struct snd_ar_control *scontrol;
+ struct q6apm *data = dev_get_drvdata(component->dev);
+ struct snd_soc_dobj *dobj;
+ int tkn_count = 0;
+
+ w_array = &tplg_w->priv.array[0];
+
+ scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);
+ if (!scontrol)
+ return -ENOMEM;
+
+ scontrol->scomp = component;
+ dobj = &w->dobj;
+ dobj->private = scontrol;
+
+ w_elem = w_array->value;
+ while (tkn_count <= (le32_to_cpu(w_array->num_elems) - 1)) {
+ switch (le32_to_cpu(w_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ scontrol->sgid = le32_to_cpu(w_elem->value);
+ break;
+ case AR_TKN_DAI_INDEX:
+ scontrol->graph_id = le32_to_cpu(w_elem->value);
+ break;
+ default: /* ignore other tokens */
+ break;
+ }
+ tkn_count++;
+ w_elem++;
+ }
+
+ scontrol->w = w;
+ list_add_tail(&scontrol->node, &data->widget_list);
+
+ return 0;
+}
+
+static int audioreach_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct audioreach_module *mod = w->dobj.private;
+ struct q6apm *apm = dev_get_drvdata(c->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* apply gain after power up of widget */
+ audioreach_gain_set_vol_ctrl(apm, mod, mod->gain);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = {
+ { AR_PGA_DAPM_EVENT, audioreach_pga_event },
+};
+
+static int audioreach_widget_load_pga(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct audioreach_module *mod;
+ struct snd_soc_dobj *dobj;
+ int ret;
+
+ ret = audioreach_widget_load_module_common(component, index, w, tplg_w);
+ if (ret)
+ return ret;
+
+ dobj = &w->dobj;
+ mod = dobj->private;
+ mod->gain = VOL_CTRL_DEFAULT_GAIN;
+
+ ret = snd_soc_tplg_widget_bind_event(w, audioreach_widget_ops,
+ ARRAY_SIZE(audioreach_widget_ops),
+ le16_to_cpu(tplg_w->event_type));
+ if (ret) {
+ dev_err(component->dev, "matching event handlers NOT found for %d\n",
+ le16_to_cpu(tplg_w->event_type));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_ready(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ audioreach_widget_load_buffer(component, index, w, tplg_w);
+ break;
+ case snd_soc_dapm_decoder:
+ case snd_soc_dapm_encoder:
+ case snd_soc_dapm_src:
+ audioreach_widget_load_enc_dec_cnv(component, index, w, tplg_w);
+ break;
+ case snd_soc_dapm_buffer:
+ audioreach_widget_load_buffer(component, index, w, tplg_w);
+ break;
+ case snd_soc_dapm_mixer:
+ return audioreach_widget_load_mixer(component, index, w, tplg_w);
+ case snd_soc_dapm_pga:
+ return audioreach_widget_load_pga(component, index, w, tplg_w);
+ case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_scheduler:
+ case snd_soc_dapm_out_drv:
+ default:
+ dev_err(component->dev, "Widget type (0x%x) not yet supported\n", w->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int audioreach_widget_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+ struct q6apm *apm = dev_get_drvdata(scomp->dev);
+ struct audioreach_container *cont;
+ struct audioreach_module *mod;
+
+ mod = dobj->private;
+ cont = mod->container;
+
+ if (w->id == snd_soc_dapm_mixer) {
+ /* virtual widget */
+ struct snd_ar_control *scontrol = dobj->private;
+
+ list_del(&scontrol->node);
+ kfree(scontrol);
+ return 0;
+ }
+
+ mutex_lock(&apm->lock);
+ idr_remove(&apm->modules_idr, mod->instance_id);
+ cont->num_modules--;
+
+ list_del(&mod->node);
+ kfree(mod->data);
+ kfree(mod);
+ /* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */
+ if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */
+ struct audioreach_sub_graph *sg = cont->sub_graph;
+
+ idr_remove(&apm->containers_idr, cont->container_id);
+ list_del(&cont->node);
+ sg->num_containers--;
+ kfree(cont);
+ /* check if there are no more containers in the sub graph and remove it */
+ if (list_empty(&sg->container_list)) {
+ struct audioreach_graph_info *info = sg->info;
+
+ idr_remove(&apm->sub_graphs_idr, sg->sub_graph_id);
+ list_del(&sg->node);
+ info->num_sub_graphs--;
+ kfree(sg);
+ /* Check if there are no more sub-graphs left then remove graph info */
+ if (list_empty(&info->sg_list)) {
+ idr_remove(&apm->graph_info_idr, info->id);
+ kfree(info);
+ }
+ }
+ }
+
+ mutex_unlock(&apm->lock);
+
+ return 0;
+}
+
+static struct snd_ar_control *audioreach_find_widget(struct snd_soc_component *comp,
+ const char *name)
+{
+ struct q6apm *apm = dev_get_drvdata(comp->dev);
+ struct snd_ar_control *control;
+
+ list_for_each_entry(control, &apm->widget_list, node) {
+ if (control->w && !strcmp(name, control->w->name))
+ return control;
+ }
+
+ return NULL;
+}
+
+static struct audioreach_module *audioreach_find_module(struct snd_soc_component *comp,
+ const char *name)
+{
+ struct q6apm *apm = dev_get_drvdata(comp->dev);
+ struct audioreach_module *module;
+ int id;
+
+ idr_for_each_entry(&apm->modules_idr, module, id) {
+ if (!strcmp(name, module->widget->name))
+ return module;
+ }
+
+ return NULL;
+}
+
+static int audioreach_route_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dapm_route *route)
+{
+ struct audioreach_module *src_module, *sink_module;
+ struct snd_ar_control *control;
+ struct snd_soc_dapm_widget *w;
+ int i;
+
+ /* check if these are actual modules */
+ src_module = audioreach_find_module(scomp, route->source);
+ sink_module = audioreach_find_module(scomp, route->sink);
+
+ if (sink_module && !src_module) {
+ control = audioreach_find_widget(scomp, route->source);
+ if (control)
+ control->module_instance_id = sink_module->instance_id;
+
+ } else if (!sink_module && src_module && route->control) {
+ /* check if this is a virtual mixer */
+ control = audioreach_find_widget(scomp, route->sink);
+ if (!control || !control->w)
+ return 0;
+
+ w = control->w;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (!strcmp(route->control, w->kcontrol_news[i].name)) {
+ struct soc_mixer_control *sm;
+ struct snd_soc_dobj *dobj;
+ struct snd_ar_control *scontrol;
+
+ sm = (struct soc_mixer_control *)w->kcontrol_news[i].private_value;
+ dobj = &sm->dobj;
+ scontrol = dobj->private;
+ scontrol->module_instance_id = src_module->instance_id;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+static int audioreach_route_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ return 0;
+}
+
+static int audioreach_tplg_complete(struct snd_soc_component *component)
+{
+ /* TBD */
+ return 0;
+}
+
+/* DAI link - used for any driver specific init */
+static int audioreach_link_load(struct snd_soc_component *component, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg)
+{
+ link->nonatomic = true;
+ link->dynamic = true;
+ link->platforms->name = NULL;
+ link->platforms->of_node = of_get_compatible_child(component->dev->of_node,
+ "qcom,q6apm-dais");
+ return 0;
+}
+
+static void audioreach_connect_sub_graphs(struct q6apm *apm,
+ struct snd_ar_control *m1,
+ struct snd_ar_control *m2,
+ bool connect)
+{
+ struct audioreach_graph_info *info;
+
+ mutex_lock(&apm->lock);
+ info = idr_find(&apm->graph_info_idr, m2->graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (connect) {
+ info->src_mod_inst_id = m1->module_instance_id;
+ info->src_mod_op_port_id = 1;
+ info->dst_mod_inst_id = m2->module_instance_id;
+ info->dst_mod_ip_port_id = 2;
+
+ } else {
+ info->src_mod_inst_id = 0;
+ info->src_mod_op_port_id = 0;
+ info->dst_mod_inst_id = 0;
+ info->dst_mod_ip_port_id = 0;
+ }
+}
+
+static bool audioreach_is_vmixer_connected(struct q6apm *apm,
+ struct snd_ar_control *m1,
+ struct snd_ar_control *m2)
+{
+ struct audioreach_graph_info *info;
+
+ mutex_lock(&apm->lock);
+ info = idr_find(&apm->graph_info_idr, m2->graph_id);
+ mutex_unlock(&apm->lock);
+
+ if (info->dst_mod_inst_id == m2->module_instance_id &&
+ info->src_mod_inst_id == m1->module_instance_id)
+ return true;
+
+ return false;
+}
+
+static int audioreach_get_audio_mixer(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_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ struct snd_ar_control *scontrol = mc->dobj.private;
+ struct q6apm *data = dev_get_drvdata(c->dev);
+ bool connected;
+
+ connected = audioreach_is_vmixer_connected(data, scontrol, dapm_scontrol);
+ if (connected)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int audioreach_put_audio_mixer(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_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
+ struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ struct snd_ar_control *scontrol = mc->dobj.private;
+ struct q6apm *data = dev_get_drvdata(c->dev);
+
+ if (ucontrol->value.integer.value[0]) {
+ audioreach_connect_sub_graphs(data, scontrol, dapm_scontrol, true);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, NULL);
+ } else {
+ audioreach_connect_sub_graphs(data, scontrol, dapm_scontrol, false);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, NULL);
+ }
+ return 0;
+}
+
+static int audioreach_get_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct audioreach_module *mod = dw->dobj.private;
+
+ ucontrol->value.integer.value[0] = mod->gain;
+
+ return 0;
+}
+
+static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ struct audioreach_module *mod = dw->dobj.private;
+
+ mod->gain = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int audioreach_control_load_mix(struct snd_soc_component *scomp,
+ struct snd_ar_control *scontrol,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_soc_tplg_vendor_value_elem *c_elem;
+ struct snd_soc_tplg_vendor_array *c_array;
+ struct snd_soc_tplg_mixer_control *mc;
+ int tkn_count = 0;
+
+ mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
+ c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data;
+
+ c_elem = c_array->value;
+
+ while (tkn_count <= (le32_to_cpu(c_array->num_elems) - 1)) {
+ switch (le32_to_cpu(c_elem->token)) {
+ case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:
+ scontrol->sgid = le32_to_cpu(c_elem->value);
+ break;
+ case AR_TKN_DAI_INDEX:
+ scontrol->graph_id = le32_to_cpu(c_elem->value);
+ break;
+ default:
+ /* Ignore other tokens */
+ break;
+ }
+ c_elem++;
+ tkn_count++;
+ }
+
+ return 0;
+}
+
+static int audioreach_control_load(struct snd_soc_component *scomp, int index,
+ struct snd_kcontrol_new *kc,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_ar_control *scontrol;
+ struct soc_mixer_control *sm;
+ struct snd_soc_dobj *dobj;
+ int ret = 0;
+
+ scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);
+ if (!scontrol)
+ return -ENOMEM;
+
+ scontrol->scomp = scomp;
+
+ switch (le32_to_cpu(hdr->ops.get)) {
+ case SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ dobj = &sm->dobj;
+ ret = audioreach_control_load_mix(scomp, scontrol, kc, hdr);
+ break;
+ case SND_SOC_AR_TPLG_VOL_CTL:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ dobj = &sm->dobj;
+ break;
+ default:
+ dev_warn(scomp->dev, "control type not supported %d:%d:%d\n",
+ hdr->ops.get, hdr->ops.put, hdr->ops.info);
+ kfree(scontrol);
+ return -EINVAL;
+ }
+
+ dobj->private = scontrol;
+ return ret;
+}
+
+static int audioreach_control_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ struct snd_ar_control *scontrol = dobj->private;
+
+ kfree(scontrol);
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_kcontrol_ops audioreach_io_ops[] = {
+ {SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX, audioreach_get_audio_mixer,
+ audioreach_put_audio_mixer, snd_soc_info_volsw},
+ {SND_SOC_AR_TPLG_VOL_CTL, audioreach_get_vol_ctrl_audio_mixer,
+ audioreach_put_vol_ctrl_audio_mixer, snd_soc_info_volsw},
+};
+
+static const struct snd_soc_tplg_ops audioreach_tplg_ops = {
+ .io_ops = audioreach_io_ops,
+ .io_ops_count = ARRAY_SIZE(audioreach_io_ops),
+
+ .control_load = audioreach_control_load,
+ .control_unload = audioreach_control_unload,
+
+ .widget_ready = audioreach_widget_ready,
+ .widget_unload = audioreach_widget_unload,
+
+ .complete = audioreach_tplg_complete,
+ .link_load = audioreach_link_load,
+
+ .dapm_route_load = audioreach_route_load,
+ .dapm_route_unload = audioreach_route_unload,
+};
+
+int audioreach_tplg_init(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = component->card;
+ struct device *dev = component->dev;
+ const struct firmware *fw;
+ int ret;
+
+ /* Inline with Qualcomm UCM configs and linux-firmware path */
+ char *tplg_fw_name __free(kfree) = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin",
+ card->driver_name,
+ card->name);
+ if (!tplg_fw_name)
+ return -ENOMEM;
+
+ ret = request_firmware(&fw, tplg_fw_name, dev);
+ if (ret < 0) {
+ dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "tplg component load failed: %d\n", ret);
+ }
+
+ release_firmware(fw);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audioreach_tplg_init);
diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c
new file mode 100644
index 000000000000..bc5a18883479
--- /dev/null
+++ b/sound/soc/qcom/sc7180.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) 2020, The Linux Foundation. All rights reserved.
+//
+// sc7180.c -- ALSA SoC Machine driver for SC7180
+
+#include <dt-bindings/sound/sc7180-lpass.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
+
+#include "../codecs/rt5682.h"
+#include "../codecs/rt5682s.h"
+#include "common.h"
+#include "qdsp6/q6afe.h"
+
+#define DEFAULT_MCLK_RATE 19200000
+#define MI2S_BCLK_RATE 1536000
+#define RT5682_PLL1_FREQ (48000 * 512)
+
+#define DRIVER_NAME "SC7180"
+
+struct sc7180_snd_data {
+ struct snd_soc_card card;
+ u32 pri_mi2s_clk_count;
+ struct snd_soc_jack hs_jack;
+ struct snd_soc_jack hdmi_jack;
+ struct gpio_desc *dmic_sel;
+ int dmic_switch;
+};
+
+static void sc7180_jack_free(struct snd_jack *jack)
+{
+ struct snd_soc_component *component = jack->private_data;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static struct snd_soc_jack_pin sc7180_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_jack *jack;
+ int rval;
+
+ rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pdata->hs_jack,
+ sc7180_jack_pins,
+ ARRAY_SIZE(sc7180_jack_pins));
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headset Jack\n");
+ return rval;
+ }
+
+ jack = pdata->hs_jack.jack;
+
+ snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ jack->private_data = component;
+ jack->private_free = sc7180_jack_free;
+
+ return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
+}
+
+static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_jack *jack;
+ int rval;
+
+ rval = snd_soc_card_jack_new(
+ card, "HDMI Jack",
+ SND_JACK_LINEOUT,
+ &pdata->hdmi_jack);
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add HDMI Jack\n");
+ return rval;
+ }
+
+ jack = pdata->hdmi_jack.jack;
+ jack->private_data = component;
+ jack->private_free = sc7180_jack_free;
+
+ return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
+}
+
+static int sc7180_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ return sc7180_headset_init(rtd);
+ case MI2S_SECONDARY:
+ return 0;
+ case LPASS_DP_RX:
+ return sc7180_hdmi_init(rtd);
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sc7180_qdsp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ return sc7180_headset_init(rtd);
+ case PRIMARY_MI2S_TX:
+ case TERTIARY_MI2S_RX:
+ return 0;
+ case DISPLAY_PORT_RX:
+ return sc7180_hdmi_init(rtd);
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sc7180_startup_realtek_codec(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
+
+ if (!strcmp(codec_dai->name, "rt5682-aif1")) {
+ pll_source = RT5682_PLL1_S_MCLK;
+ pll_id = 0;
+ clk_id = RT5682_SCLK_S_PLL1;
+ pll_out = RT5682_PLL1_FREQ;
+ pll_in = DEFAULT_MCLK_RATE;
+ } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) {
+ pll_source = RT5682S_PLL_S_MCLK;
+ pll_id = RT5682S_PLL2;
+ clk_id = RT5682S_SCLK_S_PLL2;
+ pll_out = RT5682_PLL1_FREQ;
+ pll_in = DEFAULT_MCLK_RATE;
+ } else {
+ return 0;
+ }
+ snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_BC_FC |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_I2S);
+
+ /* Configure PLL1 for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
+ pll_in, pll_out);
+ if (ret) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure sysclk for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
+ ret);
+
+ return ret;
+}
+
+static int sc7180_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ if (++data->pri_mi2s_clk_count == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ LPASS_MCLK0,
+ DEFAULT_MCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ ret = sc7180_startup_realtek_codec(rtd);
+ if (ret)
+ return ret;
+
+ break;
+ case MI2S_SECONDARY:
+ break;
+ case LPASS_DP_RX:
+ break;
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sc7180_qdsp_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ if (++data->pri_mi2s_clk_count == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_MCLK_1,
+ DEFAULT_MCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ MI2S_BCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+
+ ret = sc7180_startup_realtek_codec(rtd);
+ if (ret)
+ return ret;
+
+ break;
+ case TERTIARY_MI2S_RX:
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ MI2S_BCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+
+ snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_BC_FC |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_I2S);
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+ break;
+ case DISPLAY_PORT_RX:
+ break;
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.integer.value[0] = data->dmic_switch;
+ return 0;
+}
+
+static int dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
+ struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
+
+ data->dmic_switch = ucontrol->value.integer.value[0];
+ gpiod_set_value(data->dmic_sel, data->dmic_switch);
+ return 0;
+}
+
+static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ if (--data->pri_mi2s_clk_count == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ LPASS_MCLK0,
+ 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ break;
+ case MI2S_SECONDARY:
+ break;
+ case LPASS_DP_RX:
+ break;
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ break;
+ }
+}
+
+static void sc7180_qdsp_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ if (--data->pri_mi2s_clk_count == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_MCLK_1,
+ 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ break;
+ case TERTIARY_MI2S_RX:
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ break;
+ case DISPLAY_PORT_RX:
+ break;
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ break;
+ }
+}
+
+static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ return 0;
+ case MI2S_SECONDARY:
+ return 0;
+ case LPASS_DP_RX:
+ return sc7180_hdmi_init(rtd);
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBC_CFC |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_I2S);
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);
+
+ break;
+ case MI2S_SECONDARY:
+ break;
+ case LPASS_DP_RX:
+ break;
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
+ cpu_dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
+static const struct snd_soc_ops sc7180_ops = {
+ .startup = sc7180_snd_startup,
+ .shutdown = sc7180_snd_shutdown,
+};
+
+static const struct snd_soc_ops sc7180_qdsp_ops = {
+ .startup = sc7180_qdsp_snd_startup,
+ .shutdown = sc7180_qdsp_snd_shutdown,
+};
+
+static const struct snd_soc_ops sc7180_adau7002_ops = {
+ .startup = sc7180_adau7002_snd_startup,
+};
+
+static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_kcontrol_new sc7180_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static const char * const dmic_mux_text[] = {
+ "Front Mic",
+ "Rear Mic",
+};
+
+static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,
+ SND_SOC_NOPM, 0, dmic_mux_text);
+
+static const struct snd_kcontrol_new sc7180_dmic_mux_control =
+ SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,
+ dmic_get, dmic_set);
+
+static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),
+};
+
+static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+};
+
+static int sc7180_snd_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sc7180_snd_data *data;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_link *link;
+ int ret;
+ int i;
+ bool qdsp = false, no_headphone = false;
+
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card = &data->card;
+ snd_soc_card_set_drvdata(card, data);
+
+ card->owner = THIS_MODULE;
+ card->driver_name = DRIVER_NAME;
+ card->dev = dev;
+ card->dapm_widgets = sc7180_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);
+ card->controls = sc7180_snd_controls;
+ card->num_controls = ARRAY_SIZE(sc7180_snd_controls);
+
+ if (of_property_present(dev->of_node, "dmic-gpios")) {
+ card->dapm_widgets = sc7180_snd_dual_mic_widgets,
+ card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),
+ card->controls = sc7180_snd_dual_mic_controls,
+ card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls),
+ card->dapm_routes = sc7180_snd_dual_mic_audio_route,
+ card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),
+ data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
+ if (IS_ERR(data->dmic_sel)) {
+ dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
+ return PTR_ERR(data->dmic_sel);
+ }
+ }
+
+ if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {
+ no_headphone = true;
+ card->dapm_widgets = sc7180_adau7002_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);
+ } else if (of_device_is_compatible(dev->of_node, "qcom,sc7180-qdsp6-sndcard")) {
+ qdsp = true;
+ }
+
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ for_each_card_prelinks(card, i, link) {
+ if (no_headphone) {
+ link->ops = &sc7180_adau7002_ops;
+ link->init = sc7180_adau7002_init;
+ } else if (qdsp) {
+ if (link->no_pcm == 1) {
+ link->ops = &sc7180_qdsp_ops;
+ link->be_hw_params_fixup = sc7180_qdsp_be_hw_params_fixup;
+ link->init = sc7180_qdsp_init;
+ }
+ } else {
+ link->ops = &sc7180_ops;
+ link->init = sc7180_init;
+ }
+ }
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id sc7180_snd_device_id[] = {
+ {.compatible = "google,sc7180-trogdor"},
+ {.compatible = "google,sc7180-coachz"},
+ {.compatible = "qcom,sc7180-qdsp6-sndcard"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
+
+static struct platform_driver sc7180_snd_driver = {
+ .probe = sc7180_snd_platform_probe,
+ .driver = {
+ .name = "msm-snd-sc7180",
+ .of_match_table = sc7180_snd_device_id,
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(sc7180_snd_driver);
+
+MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c
new file mode 100644
index 000000000000..abdd58c1d0a4
--- /dev/null
+++ b/sound/soc/qcom/sc7280.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+//
+// ALSA SoC Machine driver for sc7280
+
+#include <dt-bindings/sound/qcom,lpass.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/input.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/rt5682s.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/rt5682.h"
+#include "../codecs/rt5682s.h"
+#include "common.h"
+#include "lpass.h"
+#include "qdsp6/q6afe.h"
+#include "sdw.h"
+
+#define DEFAULT_MCLK_RATE 19200000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define MI2S_BCLK_RATE 1536000
+
+struct sc7280_snd_data {
+ struct snd_soc_card card;
+ u32 pri_mi2s_clk_count;
+ struct snd_soc_jack hs_jack;
+ struct snd_soc_jack hdmi_jack;
+ bool jack_setup;
+ bool stream_prepared[LPASS_MAX_PORTS];
+};
+
+static void sc7280_jack_free(struct snd_jack *jack)
+{
+ struct snd_soc_component *component = jack->private_data;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static struct snd_soc_jack_pin sc7280_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_jack *jack;
+ int rval, i;
+
+ if (!pdata->jack_setup) {
+ rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_MECHANICAL |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5,
+ &pdata->hs_jack,
+ sc7280_jack_pins,
+ ARRAY_SIZE(sc7280_jack_pins));
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headset Jack\n");
+ return rval;
+ }
+
+ jack = pdata->hs_jack.jack;
+
+ snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ jack->private_data = component;
+ jack->private_free = sc7280_jack_free;
+ pdata->jack_setup = true;
+ }
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ case LPASS_CDC_DMA_RX0:
+ case LPASS_CDC_DMA_TX3:
+ case TX_CODEC_DMA_TX_3:
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_err(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_jack *jack;
+ int rval;
+
+ rval = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
+ &pdata->hdmi_jack);
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add HDMI Jack\n");
+ return rval;
+ }
+
+ jack = pdata->hdmi_jack.jack;
+ jack->private_data = component;
+ jack->private_free = sc7280_jack_free;
+
+ return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
+}
+
+static int sc7280_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ if (++data->pri_mi2s_clk_count == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ LPASS_MCLK0,
+ DEFAULT_MCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBC_CFC |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_I2S);
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ DEFAULT_MCLK_RATE, RT5682_PLL_FREQ);
+ if (ret) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ,
+ SND_SOC_CLOCK_IN);
+
+ if (ret) {
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sc7280_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ case LPASS_CDC_DMA_TX3:
+ case TX_CODEC_DMA_TX_3:
+ return sc7280_headset_init(rtd);
+ case LPASS_CDC_DMA_RX0:
+ case LPASS_CDC_DMA_VA_TX0:
+ case MI2S_SECONDARY:
+ case RX_CODEC_DMA_RX_0:
+ case SECONDARY_MI2S_RX:
+ case VA_CODEC_DMA_TX_0:
+ return 0;
+ case LPASS_DP_RX:
+ return sc7280_hdmi_init(rtd);
+ default:
+ dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
+ }
+
+ return -EINVAL;
+}
+
+static int sc7280_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ if (!rtd->dai_link->no_pcm) {
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
+ }
+
+ return 0;
+}
+
+static int sc7280_snd_swr_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ const struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sc7280_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+
+ return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static int sc7280_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ const struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case LPASS_CDC_DMA_RX0:
+ case LPASS_CDC_DMA_TX3:
+ case RX_CODEC_DMA_RX_0:
+ case TX_CODEC_DMA_TX_3:
+ case VA_CODEC_DMA_TX_0:
+ return sc7280_snd_swr_prepare(substream);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int sc7280_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sc7280_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ const struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static void sc7280_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ if (--data->pri_mi2s_clk_count == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ LPASS_MCLK0,
+ 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ break;
+ case SECONDARY_MI2S_RX:
+ snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ 0, SNDRV_PCM_STREAM_PLAYBACK);
+ break;
+ default:
+ break;
+ }
+
+ qcom_snd_sdw_shutdown(substream);
+}
+
+static int sc7280_snd_startup(struct snd_pcm_substream *substream)
+{
+ unsigned int fmt = SND_SOC_DAIFMT_CBC_CFC;
+ unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBC_CFC;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret = 0;
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ ret = sc7280_rt5682_init(rtd);
+ if (ret)
+ return ret;
+ break;
+ case SECONDARY_MI2S_RX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+
+ snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ default:
+ break;
+ }
+
+ return qcom_snd_sdw_startup(substream);
+}
+
+static const struct snd_soc_ops sc7280_ops = {
+ .startup = sc7280_snd_startup,
+ .hw_params = sc7280_snd_hw_params,
+ .hw_free = sc7280_snd_hw_free,
+ .prepare = sc7280_snd_prepare,
+ .shutdown = sc7280_snd_shutdown,
+};
+
+static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_kcontrol_new sc7280_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int sc7280_snd_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sc7280_snd_data *data;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_link *link;
+ int ret, i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card = &data->card;
+ snd_soc_card_set_drvdata(card, data);
+
+ card->owner = THIS_MODULE;
+ card->driver_name = "SC7280";
+ card->dev = dev;
+
+ card->dapm_widgets = sc7280_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sc7280_snd_widgets);
+ card->controls = sc7280_snd_controls;
+ card->num_controls = ARRAY_SIZE(sc7280_snd_controls);
+
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ for_each_card_prelinks(card, i, link) {
+ link->init = sc7280_init;
+ link->ops = &sc7280_ops;
+ if (link->no_pcm == 1)
+ link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup;
+ }
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id sc7280_snd_device_id[] = {
+ { .compatible = "google,sc7280-herobrine" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sc7280_snd_device_id);
+
+static struct platform_driver sc7280_snd_driver = {
+ .probe = sc7280_snd_platform_probe,
+ .driver = {
+ .name = "msm-snd-sc7280",
+ .of_match_table = sc7280_snd_device_id,
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(sc7280_snd_driver);
+
+MODULE_DESCRIPTION("sc7280 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
new file mode 100644
index 000000000000..7925aa3f63ba
--- /dev/null
+++ b/sound/soc/qcom/sc8280xp.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/jack.h>
+#include <linux/input-event-codes.h>
+#include "qdsp6/q6afe.h"
+#include "common.h"
+#include "sdw.h"
+
+struct sc8280xp_snd_data {
+ bool stream_prepared[AFE_PORT_MAX];
+ struct snd_soc_card *card;
+ struct snd_soc_jack jack;
+ struct snd_soc_jack dp_jack[8];
+ bool jack_setup;
+};
+
+static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_jack *dp_jack = NULL;
+ int dp_pcm_id = 0;
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
+ case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+ break;
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ /*
+ * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume
+ * to reduce the risk of speaker damage until we have active
+ * speaker protection in place.
+ */
+ snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81);
+ snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81);
+ snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17);
+ snd_soc_limit_volume(card, "SpkrRight PA Volume", 17);
+ break;
+ case DISPLAY_PORT_RX_0:
+ /* DISPLAY_PORT dai ids are not contiguous */
+ dp_pcm_id = 0;
+ dp_jack = &data->dp_jack[dp_pcm_id];
+ break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1;
+ dp_jack = &data->dp_jack[dp_pcm_id];
+ break;
+ default:
+ break;
+ }
+
+ if (dp_jack)
+ return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id);
+
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+}
+
+static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ channels->min = 2;
+ channels->max = 2;
+ switch (cpu_dai->id) {
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ channels->min = 1;
+ break;
+ default:
+ break;
+ }
+
+
+ return 0;
+}
+
+static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+
+ return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static const struct snd_soc_ops sc8280xp_be_ops = {
+ .startup = qcom_snd_sdw_startup,
+ .shutdown = qcom_snd_sdw_shutdown,
+ .hw_free = sc8280xp_snd_hw_free,
+ .prepare = sc8280xp_snd_prepare,
+};
+
+static void sc8280xp_add_be_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->init = sc8280xp_snd_init;
+ link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup;
+ link->ops = &sc8280xp_be_ops;
+ }
+ }
+}
+
+static int sc8280xp_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sc8280xp_snd_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ card->owner = THIS_MODULE;
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card->dev = dev;
+ dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, data);
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ card->driver_name = of_device_get_match_data(dev);
+ sc8280xp_add_be_ops(card);
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id snd_sc8280xp_dt_match[] = {
+ {.compatible = "qcom,kaanapali-sndcard", "kaanapali"},
+ {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"},
+ {.compatible = "qcom,qcs615-sndcard", "qcs615"},
+ {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"},
+ {.compatible = "qcom,qcs8275-sndcard", "qcs8300"},
+ {.compatible = "qcom,qcs9075-sndcard", "sa8775p"},
+ {.compatible = "qcom,qcs9100-sndcard", "sa8775p"},
+ {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"},
+ {.compatible = "qcom,sm8450-sndcard", "sm8450"},
+ {.compatible = "qcom,sm8550-sndcard", "sm8550"},
+ {.compatible = "qcom,sm8650-sndcard", "sm8650"},
+ {.compatible = "qcom,sm8750-sndcard", "sm8750"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, snd_sc8280xp_dt_match);
+
+static struct platform_driver snd_sc8280xp_driver = {
+ .probe = sc8280xp_platform_probe,
+ .driver = {
+ .name = "snd-sc8280xp",
+ .of_match_table = snd_sc8280xp_dt_match,
+ },
+};
+module_platform_driver(snd_sc8280xp_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("SC8280XP ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
new file mode 100644
index 000000000000..e18a8e44f2db
--- /dev/null
+++ b/sound/soc/qcom/sdm845.c
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <linux/soundwire/sdw.h>
+#include <uapi/linux/input-event-codes.h>
+#include "common.h"
+#include "qdsp6/q6afe.h"
+#include "sdw.h"
+#include "../codecs/rt5663.h"
+
+#define DRIVER_NAME "sdm845"
+#define DEFAULT_SAMPLE_RATE_48K 48000
+#define DEFAULT_MCLK_RATE 24576000
+#define TDM_BCLK_RATE 6144000
+#define MI2S_BCLK_RATE 1536000
+#define LEFT_SPK_TDM_TX_MASK 0x30
+#define RIGHT_SPK_TDM_TX_MASK 0xC0
+#define SPK_TDM_RX_MASK 0x03
+#define NUM_TDM_SLOTS 8
+#define SLIM_MAX_TX_PORTS 16
+#define SLIM_MAX_RX_PORTS 13
+#define WCD934X_DEFAULT_MCLK_RATE 9600000
+
+struct sdm845_snd_data {
+ struct snd_soc_jack jack;
+ bool jack_setup;
+ bool slim_port_setup;
+ bool stream_prepared[AFE_PORT_MAX];
+ struct snd_soc_card *card;
+ uint32_t pri_mi2s_clk_count;
+ uint32_t sec_mi2s_clk_count;
+ uint32_t quat_tdm_clk_count;
+};
+
+static struct snd_soc_jack_pin sdm845_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28};
+
+static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret = 0, i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+
+ if (ret != 0 && ret != -ENOTSUPP) {
+ pr_err("failed to get codec chan map, err:%d\n", ret);
+ return ret;
+ } else if (ret == -ENOTSUPP) {
+ /* Ignore unsupported */
+ continue;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ rx_ch_cnt, rx_ch);
+ else
+ ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt,
+ tx_ch, 0, NULL);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "failed to set cpu chan map, err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ int ret = 0, j;
+ int channels, slot_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ slot_width = 16;
+ break;
+ default:
+ dev_err(rtd->dev, "%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x3,
+ 8, slot_width);
+ if (ret < 0) {
+ dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ channels, tdm_slot_offset);
+ if (ret < 0) {
+ dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0,
+ 8, slot_width);
+ if (ret < 0) {
+ dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+ tdm_slot_offset, 0, NULL);
+ if (ret < 0) {
+ dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+
+ if (!strcmp(codec_dai->component->name_prefix, "Left")) {
+ ret = snd_soc_dai_set_tdm_slot(
+ codec_dai, LEFT_SPK_TDM_TX_MASK,
+ SPK_TDM_RX_MASK, NUM_TDM_SLOTS,
+ slot_width);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "DEV0 TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!strcmp(codec_dai->component->name_prefix, "Right")) {
+ ret = snd_soc_dai_set_tdm_slot(
+ codec_dai, RIGHT_SPK_TDM_TX_MASK,
+ SPK_TDM_RX_MASK, NUM_TDM_SLOTS,
+ slot_width);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "DEV1 TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+end:
+ return ret;
+}
+
+static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret = 0;
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ /*
+ * Use ASRC for internal clocks, as PLL rate isn't multiple
+ * of BCLK.
+ */
+ rt5663_sel_asrc_clk_src(
+ codec_dai->component,
+ RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER,
+ RT5663_CLK_SEL_I2S1_ASRC);
+ ret = snd_soc_dai_set_sysclk(
+ codec_dai, RT5663_SCLK_S_MCLK, DEFAULT_MCLK_RATE,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev,
+ "snd_soc_dai_set_sysclk err = %d\n", ret);
+ break;
+ case QUATERNARY_TDM_RX_0:
+ case QUATERNARY_TDM_TX_0:
+ ret = sdm845_tdm_snd_hw_params(substream, params);
+ break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ ret = sdm845_slim_snd_hw_params(substream, params);
+ break;
+ case QUATERNARY_MI2S_RX:
+ case SECONDARY_MI2S_RX:
+ break;
+ default:
+ pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+ return ret;
+}
+
+static void sdm845_jack_free(struct snd_jack *jack)
+{
+ struct snd_soc_component *component = jack->private_data;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *link = rtd->dai_link;
+ struct snd_jack *jack;
+ /*
+ * Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+ int rval, i;
+
+
+ if (!pdata->jack_setup) {
+ rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pdata->jack,
+ sdm845_jack_pins,
+ ARRAY_SIZE(sdm845_jack_pins));
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headphone Jack\n");
+ return rval;
+ }
+
+ jack = pdata->jack.jack;
+
+ snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ pdata->jack_setup = true;
+ }
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ jack = pdata->jack.jack;
+ component = codec_dai->component;
+
+ jack->private_data = component;
+ jack->private_free = sdm845_jack_free;
+ rval = snd_soc_component_set_jack(component,
+ &pdata->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ /* setting up wcd multiple times for slim port is redundant */
+ if (pdata->slim_port_setup || !link->no_pcm)
+ return 0;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_dai_set_channel_map(codec_dai,
+ ARRAY_SIZE(tx_ch),
+ tx_ch,
+ ARRAY_SIZE(rx_ch),
+ rx_ch);
+ if (rval != 0 && rval != -ENOTSUPP)
+ return rval;
+
+ snd_soc_dai_set_sysclk(codec_dai, 0,
+ WCD934X_DEFAULT_MCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+
+ rval = snd_soc_component_set_jack(codec_dai->component,
+ &pdata->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+ }
+
+ pdata->slim_port_setup = true;
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int sdm845_snd_startup(struct snd_pcm_substream *substream)
+{
+ unsigned int fmt = SND_SOC_DAIFMT_BP_FP;
+ unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int j;
+ int ret;
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF;
+ if (++(data->pri_mi2s_clk_count) == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_MCLK_1,
+ DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+
+ case SECONDARY_MI2S_RX:
+ case SECONDARY_MI2S_TX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+ if (++(data->sec_mi2s_clk_count) == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE);
+ }
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ case QUATERNARY_MI2S_RX:
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ break;
+
+ case QUATERNARY_TDM_RX_0:
+ case QUATERNARY_TDM_TX_0:
+ if (++(data->quat_tdm_clk_count) == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT,
+ TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B;
+
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+
+ if (!strcmp(codec_dai->component->name_prefix,
+ "Left")) {
+ ret = snd_soc_dai_set_fmt(
+ codec_dai, codec_dai_fmt);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "Left TDM fmt err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!strcmp(codec_dai->component->name_prefix,
+ "Right")) {
+ ret = snd_soc_dai_set_fmt(
+ codec_dai, codec_dai_fmt);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "Right TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ }
+ break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ break;
+
+ default:
+ pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+ return qcom_snd_sdw_startup(substream);
+}
+
+static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ case PRIMARY_MI2S_TX:
+ if (--(data->pri_mi2s_clk_count) == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_MCLK_1,
+ 0, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ 0, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ break;
+
+ case SECONDARY_MI2S_RX:
+ case SECONDARY_MI2S_TX:
+ if (--(data->sec_mi2s_clk_count) == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ 0, SNDRV_PCM_STREAM_CAPTURE);
+ }
+ break;
+
+ case QUATERNARY_TDM_RX_0:
+ case QUATERNARY_TDM_TX_0:
+ if (--(data->quat_tdm_clk_count) == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT,
+ 0, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ case QUATERNARY_MI2S_RX:
+ break;
+
+ default:
+ pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+
+ qcom_snd_sdw_shutdown(substream);
+}
+
+static int sdm845_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static int sdm845_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static const struct snd_soc_ops sdm845_be_ops = {
+ .hw_params = sdm845_snd_hw_params,
+ .hw_free = sdm845_snd_hw_free,
+ .prepare = sdm845_snd_prepare,
+ .startup = sdm845_snd_startup,
+ .shutdown = sdm845_snd_shutdown,
+};
+
+static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_kcontrol_new sdm845_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static void sdm845_add_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->ops = &sdm845_be_ops;
+ link->be_hw_params_fixup = sdm845_be_hw_params_fixup;
+ }
+ link->init = sdm845_dai_init;
+ }
+}
+
+static int sdm845_snd_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sdm845_snd_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card->driver_name = DRIVER_NAME;
+ card->dapm_widgets = sdm845_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets);
+ card->controls = sdm845_snd_controls;
+ card->num_controls = ARRAY_SIZE(sdm845_snd_controls);
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ dev_set_drvdata(dev, card);
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ data->card = card;
+ snd_soc_card_set_drvdata(card, data);
+
+ sdm845_add_ops(card);
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id sdm845_snd_device_id[] = {
+ { .compatible = "qcom,sdm845-sndcard" },
+ /* Do not grow the list for compatible devices */
+ { .compatible = "qcom,db845c-sndcard" },
+ { .compatible = "lenovo,yoga-c630-sndcard" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sdm845_snd_device_id);
+
+static struct platform_driver sdm845_snd_driver = {
+ .probe = sdm845_snd_platform_probe,
+ .driver = {
+ .name = "msm-snd-sdm845",
+ .of_match_table = sdm845_snd_device_id,
+ },
+};
+module_platform_driver(sdm845_snd_driver);
+
+MODULE_DESCRIPTION("sdm845 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c
new file mode 100644
index 000000000000..6576b47a4c8c
--- /dev/null
+++ b/sound/soc/qcom/sdw.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2023, Linaro Limited.
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#include <dt-bindings/sound/qcom,lpass.h>
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include "sdw.h"
+
+static bool qcom_snd_is_sdw_dai(int id)
+{
+ switch (id) {
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_TX_0:
+ case WSA_CODEC_DMA_RX_1:
+ case WSA_CODEC_DMA_TX_1:
+ case WSA_CODEC_DMA_TX_2:
+ case RX_CODEC_DMA_RX_0:
+ case TX_CODEC_DMA_TX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_1:
+ case RX_CODEC_DMA_RX_2:
+ case TX_CODEC_DMA_TX_2:
+ case RX_CODEC_DMA_RX_3:
+ case TX_CODEC_DMA_TX_3:
+ case RX_CODEC_DMA_RX_4:
+ case TX_CODEC_DMA_TX_4:
+ case RX_CODEC_DMA_RX_5:
+ case TX_CODEC_DMA_TX_5:
+ case RX_CODEC_DMA_RX_6:
+ case RX_CODEC_DMA_RX_7:
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ return true;
+ default:
+ break;
+ }
+
+ /* DSP Bypass usecase, cpu dai index overlaps with DSP dai ids,
+ * DO NOT MERGE into top switch case */
+ switch (id) {
+ case LPASS_CDC_DMA_TX3:
+ case LPASS_CDC_DMA_RX0:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
+ * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
+ *
+ * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
+ * Soundwire stream runtime to each codec DAI.
+ *
+ * The shutdown() callback should call sdw_release_stream() on the same
+ * sdw_stream_runtime.
+ *
+ * Return: 0 or errno
+ */
+int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS];
+ struct sdw_stream_runtime *sruntime;
+ struct snd_soc_dai *codec_dai;
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret, i, j;
+
+ if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+ return 0;
+
+ sruntime = sdw_alloc_stream(cpu_dai->name, SDW_STREAM_PCM);
+ if (!sruntime)
+ return -ENOMEM;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_stream(codec_dai, sruntime,
+ substream->stream);
+ if (ret < 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name);
+ goto err_set_stream;
+ } else if (ret == -ENOTSUPP) {
+ /* Ignore unsupported */
+ continue;
+ }
+
+ ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch,
+ &rx_ch_cnt, rx_ch);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name);
+ goto err_set_stream;
+ } else if (ret == -ENOTSUPP) {
+ /* Ignore unsupported */
+ continue;
+ }
+ }
+
+ switch (cpu_dai->id) {
+ case RX_CODEC_DMA_RX_0:
+ case TX_CODEC_DMA_TX_3:
+ if (tx_ch_cnt || rx_ch_cnt) {
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ ret = snd_soc_dai_set_channel_map(codec_dai,
+ tx_ch_cnt, tx_ch,
+ rx_ch_cnt, rx_ch);
+ if (ret != 0 && ret != -ENOTSUPP)
+ goto err_set_stream;
+ }
+ }
+ }
+
+ return 0;
+
+err_set_stream:
+ sdw_release_stream(sruntime);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup);
+
+int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
+ bool *stream_prepared)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime;
+ int ret;
+
+
+ if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+ return 0;
+
+ sruntime = qcom_snd_sdw_get_stream(substream);
+ if (!sruntime)
+ return 0;
+
+ if (*stream_prepared)
+ return 0;
+
+ ret = sdw_prepare_stream(sruntime);
+ if (ret)
+ return ret;
+
+ /**
+ * NOTE: there is a strict hw requirement about the ordering of port
+ * enables and actual WSA881x PA enable. PA enable should only happen
+ * after soundwire ports are enabled if not DC on the line is
+ * accumulated resulting in Click/Pop Noise
+ * PA enable/mute are handled as part of codec DAPM and digital mute.
+ */
+
+ ret = sdw_enable_stream(sruntime);
+ if (ret) {
+ sdw_deprepare_stream(sruntime);
+ return ret;
+ }
+ *stream_prepared = true;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
+
+struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime;
+ int i;
+
+ if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+ return NULL;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
+ if (sruntime != ERR_PTR(-ENOTSUPP))
+ return sruntime;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_get_stream);
+
+void qcom_snd_sdw_shutdown(struct snd_pcm_substream *substream)
+{
+ struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
+
+ sdw_release_stream(sruntime);
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_shutdown);
+
+int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream, bool *stream_prepared)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime;
+
+ if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+ return 0;
+
+ sruntime = qcom_snd_sdw_get_stream(substream);
+ if (sruntime && *stream_prepared) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ *stream_prepared = false;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
+MODULE_DESCRIPTION("Qualcomm ASoC SoundWire helper functions");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sdw.h b/sound/soc/qcom/sdw.h
new file mode 100644
index 000000000000..061a63f1ac52
--- /dev/null
+++ b/sound/soc/qcom/sdw.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#ifndef __QCOM_SND_SDW_H__
+#define __QCOM_SND_SDW_H__
+
+#include <linux/soundwire/sdw.h>
+
+int qcom_snd_sdw_startup(struct snd_pcm_substream *substream);
+void qcom_snd_sdw_shutdown(struct snd_pcm_substream *substream);
+int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
+ bool *stream_prepared);
+struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *stream);
+int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
+ bool *stream_prepared);
+#endif
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
new file mode 100644
index 000000000000..05a8b04e50e0
--- /dev/null
+++ b/sound/soc/qcom/sm8250.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/jack.h>
+#include <linux/input-event-codes.h>
+#include "qdsp6/q6afe.h"
+#include "common.h"
+#include "usb_offload_utils.h"
+#include "sdw.h"
+
+#define MI2S_BCLK_RATE 1536000
+
+struct sm8250_snd_data {
+ bool stream_prepared[AFE_PORT_MAX];
+ struct snd_soc_card *card;
+ struct snd_soc_jack jack;
+ struct snd_soc_jack usb_offload_jack;
+ bool usb_offload_jack_setup;
+ struct snd_soc_jack dp_jack;
+ bool jack_setup;
+};
+
+static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case DISPLAY_PORT_RX:
+ return qcom_snd_dp_jack_setup(rtd, &data->dp_jack, 0);
+ case USB_RX:
+ return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack,
+ &data->usb_offload_jack_setup);
+ default:
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+ }
+}
+
+static void sm8250_snd_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ if (cpu_dai->id == USB_RX)
+ qcom_snd_usb_offload_jack_remove(rtd,
+ &data->usb_offload_jack_setup);
+
+}
+
+static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int sm8250_snd_startup(struct snd_pcm_substream *substream)
+{
+ unsigned int fmt = SND_SOC_DAIFMT_BP_FP;
+ unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ case SECONDARY_MI2S_RX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ case TERTIARY_MI2S_RX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ default:
+ break;
+ }
+
+ return qcom_snd_sdw_startup(substream);
+}
+
+static int sm8250_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+
+ return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static int sm8250_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static const struct snd_soc_ops sm8250_be_ops = {
+ .startup = sm8250_snd_startup,
+ .shutdown = qcom_snd_sdw_shutdown,
+ .hw_free = sm8250_snd_hw_free,
+ .prepare = sm8250_snd_prepare,
+};
+
+static void sm8250_add_be_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->init = sm8250_snd_init;
+ link->exit = sm8250_snd_exit;
+ link->be_hw_params_fixup = sm8250_be_hw_params_fixup;
+ link->ops = &sm8250_be_ops;
+ }
+ }
+}
+
+static int sm8250_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sm8250_snd_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->owner = THIS_MODULE;
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card->dev = dev;
+ dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, data);
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ card->driver_name = of_device_get_match_data(dev);
+ sm8250_add_be_ops(card);
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id snd_sm8250_dt_match[] = {
+ { .compatible = "fairphone,fp4-sndcard", .data = "sm7225" },
+ { .compatible = "fairphone,fp5-sndcard", .data = "qcm6490" },
+ { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" },
+ { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" },
+ { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" },
+ { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, snd_sm8250_dt_match);
+
+static struct platform_driver snd_sm8250_driver = {
+ .probe = sm8250_platform_probe,
+ .driver = {
+ .name = "snd-sm8250",
+ .of_match_table = snd_sm8250_dt_match,
+ },
+};
+module_platform_driver(snd_sm8250_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("SM8250 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c
index 2d833bffdba0..c8d5ac43a176 100644
--- a/sound/soc/qcom/storm.c
+++ b/sound/soc/qcom/storm.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board
*/
@@ -27,7 +19,7 @@
static int storm_ops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = soc_runtime->card;
snd_pcm_format_t format = params_format(params);
unsigned int rate = params_rate(params);
@@ -36,8 +28,7 @@ static int storm_ops_hw_params(struct snd_pcm_substream *substream,
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
- dev_err(card->dev, "%s() invalid bit width given: %d\n",
- __func__, bitwidth);
+ dev_err(card->dev, "invalid bit width given: %d\n", bitwidth);
return bitwidth;
}
@@ -48,25 +39,30 @@ static int storm_ops_hw_params(struct snd_pcm_substream *substream,
*/
sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT;
- ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0);
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(soc_runtime, 0), 0, sysclk_freq, 0);
if (ret) {
- dev_err(card->dev, "%s() error setting sysclk to %u: %d\n",
- __func__, sysclk_freq, ret);
+ dev_err(card->dev, "error setting sysclk to %u: %d\n",
+ sysclk_freq, ret);
return ret;
}
return 0;
}
-static struct snd_soc_ops storm_soc_ops = {
+static const struct snd_soc_ops storm_soc_ops = {
.hw_params = storm_ops_hw_params,
};
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
static struct snd_soc_dai_link storm_dai_link = {
.name = "Primary",
.stream_name = "Primary",
- .codec_dai_name = "HiFi",
.ops = &storm_soc_ops,
+ SND_SOC_DAILINK_REG(hifi),
};
static int storm_parse_of(struct snd_soc_card *card)
@@ -74,18 +70,16 @@ static int storm_parse_of(struct snd_soc_card *card)
struct snd_soc_dai_link *dai_link = card->dai_link;
struct device_node *np = card->dev->of_node;
- dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0);
- if (!dai_link->cpu_of_node) {
- dev_err(card->dev, "%s() error getting cpu phandle\n",
- __func__);
+ dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0);
+ if (!dai_link->cpus->of_node) {
+ dev_err(card->dev, "error getting cpu phandle\n");
return -EINVAL;
}
- dai_link->platform_of_node = dai_link->cpu_of_node;
+ dai_link->platforms->of_node = dai_link->cpus->of_node;
- dai_link->codec_of_node = of_parse_phandle(np, "codec", 0);
- if (!dai_link->codec_of_node) {
- dev_err(card->dev, "%s() error getting codec phandle\n",
- __func__);
+ dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0);
+ if (!dai_link->codecs->of_node) {
+ dev_err(card->dev, "error getting codec phandle\n");
return -EINVAL;
}
@@ -102,12 +96,11 @@ static int storm_platform_probe(struct platform_device *pdev)
return -ENOMEM;
card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
+ card->owner = THIS_MODULE;
ret = snd_soc_of_parse_card_name(card, "qcom,model");
if (ret) {
- dev_err(&pdev->dev, "%s() error parsing card name: %d\n",
- __func__, ret);
+ dev_err(&pdev->dev, "error parsing card name: %d\n", ret);
return ret;
}
@@ -116,15 +109,13 @@ static int storm_platform_probe(struct platform_device *pdev)
ret = storm_parse_of(card);
if (ret) {
- dev_err(&pdev->dev, "%s() error resolving dai links: %d\n",
- __func__, ret);
+ dev_err(&pdev->dev, "error resolving dai links: %d\n", ret);
return ret;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
- dev_err(&pdev->dev, "%s() error registering soundcard: %d\n",
- __func__, ret);
+ dev_err(&pdev->dev, "error registering soundcard: %d\n", ret);
return ret;
@@ -149,4 +140,4 @@ static struct platform_driver storm_platform_driver = {
module_platform_driver(storm_platform_driver);
MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/usb_offload_utils.c b/sound/soc/qcom/usb_offload_utils.c
new file mode 100644
index 000000000000..0a24b278fcdf
--- /dev/null
+++ b/sound/soc/qcom/usb_offload_utils.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/soc-usb.h>
+
+#include "usb_offload_utils.h"
+
+int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret = 0;
+
+ if (cpu_dai->id != USB_RX)
+ return -EINVAL;
+
+ if (!*jack_setup) {
+ ret = snd_soc_usb_setup_offload_jack(codec_dai->component, jack);
+ if (ret)
+ return ret;
+ }
+
+ *jack_setup = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_usb_offload_jack_setup);
+
+int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
+ bool *jack_setup)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret = 0;
+
+ if (cpu_dai->id != USB_RX)
+ return -EINVAL;
+
+ if (*jack_setup) {
+ ret = snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+ if (ret)
+ return ret;
+ }
+
+ *jack_setup = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_usb_offload_jack_remove);
+MODULE_DESCRIPTION("ASoC Q6 USB offload controls");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/usb_offload_utils.h b/sound/soc/qcom/usb_offload_utils.h
new file mode 100644
index 000000000000..c3f411f565b0
--- /dev/null
+++ b/sound/soc/qcom/usb_offload_utils.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef __QCOM_SND_USB_OFFLOAD_UTILS_H__
+#define __QCOM_SND_USB_OFFLOAD_UTILS_H__
+
+#include <sound/soc.h>
+
+#if IS_ENABLED(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS)
+int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup);
+
+int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
+ bool *jack_setup);
+#else
+static inline int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack,
+ bool *jack_setup)
+{
+ return -ENODEV;
+}
+
+static inline int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
+ bool *jack_setup)
+{
+ return -ENODEV;
+}
+#endif /* IS_ENABLED(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) */
+#endif /* __QCOM_SND_USB_OFFLOAD_UTILS_H__ */
diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c
new file mode 100644
index 000000000000..a3f4785c4bbe
--- /dev/null
+++ b/sound/soc/qcom/x1e80100.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2023, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "common.h"
+#include "qdsp6/q6afe.h"
+#include "qdsp6/q6dsp-common.h"
+#include "sdw.h"
+
+struct x1e80100_snd_data {
+ bool stream_prepared[AFE_PORT_MAX];
+ struct snd_soc_card *card;
+ struct snd_soc_jack jack;
+ struct snd_soc_jack dp_jack[8];
+ bool jack_setup;
+};
+
+static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_jack *dp_jack = NULL;
+ int dp_pcm_id = 0;
+
+ switch (cpu_dai->id) {
+ case DISPLAY_PORT_RX_0:
+ dp_pcm_id = 0;
+ dp_jack = &data->dp_jack[dp_pcm_id];
+ break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1;
+ dp_jack = &data->dp_jack[dp_pcm_id];
+ break;
+ default:
+ break;
+ }
+
+ if (dp_jack)
+ return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id);
+
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+}
+
+static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = 48000;
+ switch (cpu_dai->id) {
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ channels->min = 1;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
+{
+ switch (num) {
+ case 1:
+ ch_map[0] = PCM_CHANNEL_FC;
+ break;
+ case 2:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ break;
+ case 3:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_FC;
+ break;
+ case 4:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_LB;
+ ch_map[2] = PCM_CHANNEL_FR;
+ ch_map[3] = PCM_CHANNEL_RB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int x1e80100_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ unsigned int channels = substream->runtime->channels;
+ unsigned int rx_slot[4];
+ int ret;
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ ret = x1e80100_snd_hw_map_channels(rx_slot, channels);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ channels, rx_slot);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static int x1e80100_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static const struct snd_soc_ops x1e80100_be_ops = {
+ .startup = qcom_snd_sdw_startup,
+ .shutdown = qcom_snd_sdw_shutdown,
+ .hw_free = x1e80100_snd_hw_free,
+ .prepare = x1e80100_snd_prepare,
+};
+
+static void x1e80100_add_be_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->init = x1e80100_snd_init;
+ link->be_hw_params_fixup = x1e80100_be_hw_params_fixup;
+ link->ops = &x1e80100_be_ops;
+ }
+ }
+}
+
+static int x1e80100_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct x1e80100_snd_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+ dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, data);
+
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ card->driver_name = of_device_get_match_data(dev);
+ x1e80100_add_be_ops(card);
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id snd_x1e80100_dt_match[] = {
+ { .compatible = "qcom,x1e80100-sndcard", .data = "x1e80100" },
+ { .compatible = "qcom,glymur-sndcard", .data = "glymur" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, snd_x1e80100_dt_match);
+
+static struct platform_driver snd_x1e80100_driver = {
+ .probe = x1e80100_platform_probe,
+ .driver = {
+ .name = "snd-x1e80100",
+ .of_match_table = snd_x1e80100_dt_match,
+ },
+};
+module_platform_driver(snd_x1e80100_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
+MODULE_DESCRIPTION("Qualcomm X1E80100 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/Kconfig b/sound/soc/renesas/Kconfig
index c9902a6d6fa0..11c2027c88a7 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/renesas/Kconfig
@@ -1,5 +1,6 @@
-menu "SoC Audio support for SuperH"
- depends on SUPERH || ARCH_SHMOBILE
+# SPDX-License-Identifier: GPL-2.0
+menu "Renesas"
+ depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
config SND_SOC_PCM_SH7760
tristate "SoC Audio support for Renesas SH7760"
@@ -22,30 +23,41 @@ config SND_SOC_SH4_SSI
config SND_SOC_SH4_FSI
tristate "SH4 FSI support"
+ depends on SUPERH || COMMON_CLK
select SND_SIMPLE_CARD
help
This option enables FSI sound support
config SND_SOC_SH4_SIU
tristate
- depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+ depends on ARCH_SHMOBILE && HAVE_CLK
+ depends on DMADEVICES
select DMA_ENGINE
- select DMADEVICES
select SH_DMAE
select FW_LOADER
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on COMMON_CLK
- select SND_SIMPLE_CARD
+ depends on OF
+ select SND_SIMPLE_CARD_UTILS
+ select SND_DMAENGINE_PCM
select REGMAP_MMIO
help
This option enables R-Car SRU/SCU/SSIU/SSI sound support
-config SND_SOC_RSRC_CARD
- tristate "Renesas Sampling Rate Convert Sound Card"
+config SND_SOC_MSIOF
+ tristate "R-Car series MSIOF support"
+ depends on OF
+ select SND_DMAENGINE_PCM
+ help
+ This option enables R-Car MSIOF sound support
+
+config SND_SOC_RZ
+ tristate "RZ/G2L series SSIF-2 support"
+ depends on ARCH_RZG2L || COMPILE_TEST
help
- This option enables simple sound if you need sampling rate convert
+ This option enables RZ/G2L SSIF-2 sound support.
##
## Boards
@@ -62,7 +74,7 @@ config SND_SH7760_AC97
config SND_SIU_MIGOR
tristate "SIU sound support on Migo-R"
- depends on SH_MIGOR && I2C
+ depends on SH_MIGOR && I2C && DMADEVICES
select SND_SOC_SH4_SIU
select SND_SOC_WM8978
help
diff --git a/sound/soc/sh/Makefile b/sound/soc/renesas/Makefile
index aaf3dcd1ee2a..f0e19cbd1581 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/renesas/Makefile
@@ -1,12 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
## DMA engines
-snd-soc-dma-sh7760-objs := dma-sh7760.o
+snd-soc-dma-sh7760-y := dma-sh7760.o
obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o
## audio units found on some SH-4
-snd-soc-hac-objs := hac.o
-snd-soc-ssi-objs := ssi.o
-snd-soc-fsi-objs := fsi.o
-snd-soc-siu-objs := siu_pcm.o siu_dai.o
+snd-soc-hac-y := hac.o
+snd-soc-ssi-y := ssi.o
+snd-soc-fsi-y := fsi.o
+snd-soc-siu-y := siu_pcm.o siu_dai.o
obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o
obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o
@@ -16,8 +17,12 @@ obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o
obj-$(CONFIG_SND_SOC_RCAR) += rcar/
## boards
-snd-soc-sh7760-ac97-objs := sh7760-ac97.o
-snd-soc-migor-objs := migor.o
+snd-soc-sh7760-ac97-y := sh7760-ac97.o
+snd-soc-migor-y := migor.o
obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o
obj-$(CONFIG_SND_SIU_MIGOR) += snd-soc-migor.o
+
+# RZ/G2L
+snd-soc-rz-ssi-y := rz-ssi.o
+obj-$(CONFIG_SND_SOC_RZ) += snd-soc-rz-ssi.o
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/renesas/dma-sh7760.c
index 8fad4441c87d..c53539482c20 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/renesas/dma-sh7760.c
@@ -1,16 +1,14 @@
-/*
- * SH7760 ("camelot") DMABRG audio DMA unit support
- *
- * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
- * licensed under the terms outlined in the file COPYING at the root
- * of the linux kernel sources.
- *
- * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
- * trigger an interrupt when one half of the programmed transfer size
- * has been xmitted.
- *
- * FIXME: little-endian only for now
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// SH7760 ("camelot") DMABRG audio DMA unit support
+//
+// Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+//
+// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
+// trigger an interrupt when one half of the programmed transfer size
+// has been xmitted.
+//
+// FIXME: little-endian only for now
#include <linux/module.h>
#include <linux/gfp.h>
@@ -89,7 +87,7 @@ struct camelot_pcm {
#define DMABRG_PREALLOC_BUFFER 32 * 1024
#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024
-static struct snd_pcm_hardware camelot_pcm_hardware = {
+static const struct snd_pcm_hardware camelot_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -117,10 +115,11 @@ static void camelot_rxdma(void *data)
snd_pcm_period_elapsed(cam->rx_ss);
}
-static int camelot_pcm_open(struct snd_pcm_substream *substream)
+static int camelot_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
int ret, dmairq;
@@ -133,7 +132,7 @@ static int camelot_pcm_open(struct snd_pcm_substream *substream)
ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
if (unlikely(ret)) {
pr_debug("audio unit %d irqs already taken!\n",
- rtd->cpu_dai->id);
+ snd_soc_rtd_to_cpu(rtd, 0)->id);
return -EBUSY;
}
(void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
@@ -142,7 +141,7 @@ static int camelot_pcm_open(struct snd_pcm_substream *substream)
ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
if (unlikely(ret)) {
pr_debug("audio unit %d irqs already taken!\n",
- rtd->cpu_dai->id);
+ snd_soc_rtd_to_cpu(rtd, 0)->id);
return -EBUSY;
}
(void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
@@ -150,10 +149,11 @@ static int camelot_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
-static int camelot_pcm_close(struct snd_pcm_substream *substream)
+static int camelot_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
int dmairq;
@@ -170,18 +170,13 @@ static int camelot_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static int camelot_hw_params(struct snd_pcm_substream *substream,
+static int camelot_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
- int ret;
-
- ret = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (ret < 0)
- return ret;
if (recv) {
cam->rx_period_size = params_period_bytes(hw_params);
@@ -193,20 +188,16 @@ static int camelot_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int camelot_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int camelot_prepare(struct snd_pcm_substream *substream)
+static int camelot_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id];
+
+ pr_debug("PCM data: addr %pad len %zu\n", &runtime->dma_addr,
+ runtime->dma_bytes);
- pr_debug("PCM data: addr 0x%08ulx len %d\n",
- (u32)runtime->dma_addr, runtime->dma_bytes);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
BRGREG(BRGATXTCR) = runtime->dma_bytes;
@@ -246,10 +237,11 @@ static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
BRGREG(BRGACR) = acr | ACR_RDS;
}
-static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
+static int camelot_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
switch (cmd) {
@@ -272,11 +264,12 @@ static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
-static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
unsigned long pos;
@@ -294,40 +287,36 @@ static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, pos);
}
-static struct snd_pcm_ops camelot_pcm_ops = {
- .open = camelot_pcm_open,
- .close = camelot_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = camelot_hw_params,
- .hw_free = camelot_hw_free,
- .prepare = camelot_prepare,
- .trigger = camelot_trigger,
- .pointer = camelot_pos,
-};
-
-static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int camelot_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
* in MMAP mode (i.e. aplay -M)
*/
- snd_pcm_lib_preallocate_pages_for_all(pcm,
+ snd_pcm_set_managed_buffer_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
+ NULL,
DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX);
return 0;
}
-static struct snd_soc_platform_driver sh7760_soc_platform = {
- .ops = &camelot_pcm_ops,
- .pcm_new = camelot_pcm_new,
+static const struct snd_soc_component_driver sh7760_soc_component = {
+ .open = camelot_pcm_open,
+ .close = camelot_pcm_close,
+ .hw_params = camelot_hw_params,
+ .prepare = camelot_prepare,
+ .trigger = camelot_trigger,
+ .pointer = camelot_pos,
+ .pcm_construct = camelot_pcm_new,
};
static int sh7760_soc_platform_probe(struct platform_device *pdev)
{
- return devm_snd_soc_register_platform(&pdev->dev, &sh7760_soc_platform);
+ return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component,
+ NULL, 0);
}
static struct platform_driver sh7760_pcm_driver = {
@@ -340,6 +329,6 @@ static struct platform_driver sh7760_pcm_driver = {
module_platform_driver(sh7760_pcm_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/renesas/fsi.c
index ead520182e26..1491c2f2cc96 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/renesas/fsi.c
@@ -1,23 +1,18 @@
-/*
- * Fifo-attached Serial Interface (FSI) support for SH7724
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ssi.c
- * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Fifo-attached Serial Interface (FSI) support for SH7724
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Based on ssi.c
+// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/scatterlist.h>
#include <linux/sh_dma.h>
#include <linux/slab.h>
@@ -223,9 +218,9 @@ struct fsi_stream {
u32 bus_option;
/*
- * thse are initialized by fsi_handler_init()
+ * these are initialized by fsi_handler_init()
*/
- struct fsi_stream_handler *handler;
+ const struct fsi_stream_handler *handler;
struct fsi_priv *priv;
/*
@@ -301,7 +296,12 @@ struct fsi_master {
spinlock_t lock;
};
-static int fsi_stream_is_play(struct fsi_priv *fsi, struct fsi_stream *io);
+static inline int fsi_stream_is_play(struct fsi_priv *fsi,
+ struct fsi_stream *io)
+{
+ return &fsi->playback == io;
+}
+
/*
* basic read write function
@@ -343,14 +343,9 @@ static void __fsi_reg_mask_set(u32 __iomem *reg, u32 mask, u32 data)
#define fsi_core_read(p, r) _fsi_master_read(p, p->core->r)
static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
{
- u32 ret;
- unsigned long flags;
+ guard(spinlock_irqsave)(&master->lock);
- spin_lock_irqsave(&master->lock, flags);
- ret = __fsi_reg_read(master->base + reg);
- spin_unlock_irqrestore(&master->lock, flags);
-
- return ret;
+ return __fsi_reg_read(master->base + reg);
}
#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d)
@@ -358,11 +353,9 @@ static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
static void _fsi_master_mask_set(struct fsi_master *master,
u32 reg, u32 mask, u32 data)
{
- unsigned long flags;
+ guard(spinlock_irqsave)(&master->lock);
- spin_lock_irqsave(&master->lock, flags);
__fsi_reg_mask_set(master->base + reg, mask, data);
- spin_unlock_irqrestore(&master->lock, flags);
}
/*
@@ -405,9 +398,9 @@ static int fsi_is_play(struct snd_pcm_substream *substream)
static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- return rtd->cpu_dai;
+ return snd_soc_rtd_to_cpu(rtd, 0);
}
static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai)
@@ -489,12 +482,6 @@ static void fsi_count_fifo_err(struct fsi_priv *fsi)
/*
* fsi_stream_xx() function
*/
-static inline int fsi_stream_is_play(struct fsi_priv *fsi,
- struct fsi_stream *io)
-{
- return &fsi->playback == io;
-}
-
static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi,
struct snd_pcm_substream *substream)
{
@@ -505,14 +492,10 @@ static int fsi_stream_is_working(struct fsi_priv *fsi,
struct fsi_stream *io)
{
struct fsi_master *master = fsi_get_master(fsi);
- unsigned long flags;
- int ret;
- spin_lock_irqsave(&master->lock, flags);
- ret = !!(io->substream && io->substream->runtime);
- spin_unlock_irqrestore(&master->lock, flags);
+ guard(spinlock_irqsave)(&master->lock);
- return ret;
+ return !!(io->substream && io->substream->runtime);
}
static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io)
@@ -526,9 +509,9 @@ static void fsi_stream_init(struct fsi_priv *fsi,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_master *master = fsi_get_master(fsi);
- unsigned long flags;
- spin_lock_irqsave(&master->lock, flags);
+ guard(spinlock_irqsave)(&master->lock);
+
io->substream = substream;
io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size);
io->buff_sample_pos = 0;
@@ -539,16 +522,14 @@ static void fsi_stream_init(struct fsi_priv *fsi,
io->oerr_num = -1; /* ignore 1st err */
io->uerr_num = -1; /* ignore 1st err */
fsi_stream_handler_call(io, init, fsi, io);
- spin_unlock_irqrestore(&master->lock, flags);
}
static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
{
struct snd_soc_dai *dai = fsi_get_dai(io->substream);
struct fsi_master *master = fsi_get_master(fsi);
- unsigned long flags;
- spin_lock_irqsave(&master->lock, flags);
+ guard(spinlock_irqsave)(&master->lock);
if (io->oerr_num > 0)
dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
@@ -566,7 +547,6 @@ static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
io->bus_option = 0;
io->oerr_num = 0;
io->uerr_num = 0;
- spin_unlock_irqrestore(&master->lock, flags);
}
static int fsi_stream_transfer(struct fsi_stream *io)
@@ -785,7 +765,7 @@ static int fsi_clk_init(struct device *dev,
return -EINVAL;
}
if (clock->div == clock->own) {
- dev_err(dev, "cpu doens't support div clock\n");
+ dev_err(dev, "cpu doesn't support div clock\n");
return -EINVAL;
}
}
@@ -821,14 +801,27 @@ static int fsi_clk_enable(struct device *dev,
return ret;
}
- clk_enable(clock->xck);
- clk_enable(clock->ick);
- clk_enable(clock->div);
+ ret = clk_enable(clock->xck);
+ if (ret)
+ goto err;
+ ret = clk_enable(clock->ick);
+ if (ret)
+ goto disable_xck;
+ ret = clk_enable(clock->div);
+ if (ret)
+ goto disable_ick;
clock->count++;
}
return ret;
+
+disable_ick:
+ clk_disable(clock->ick);
+disable_xck:
+ clk_disable(clock->xck);
+err:
+ return ret;
}
static int fsi_clk_disable(struct device *dev,
@@ -1222,13 +1215,13 @@ static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io)
return 0;
}
-static struct fsi_stream_handler fsi_pio_push_handler = {
+static const struct fsi_stream_handler fsi_pio_push_handler = {
.init = fsi_pio_push_init,
.transfer = fsi_pio_push,
.start_stop = fsi_pio_start_stop,
};
-static struct fsi_stream_handler fsi_pio_pop_handler = {
+static const struct fsi_stream_handler fsi_pio_pop_handler = {
.init = fsi_pio_pop_init,
.transfer = fsi_pio_pop,
.start_stop = fsi_pio_start_stop,
@@ -1372,7 +1365,9 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev
io->chan = dma_request_channel(mask, shdma_chan_filter,
(void *)io->dma_id);
#else
- io->chan = dma_request_slave_channel(dev, is_play ? "tx" : "rx");
+ io->chan = dma_request_chan(dev, is_play ? "tx" : "rx");
+ if (IS_ERR(io->chan))
+ io->chan = NULL;
#endif
if (io->chan) {
struct dma_slave_config cfg = {};
@@ -1423,7 +1418,7 @@ static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io)
return 0;
}
-static struct fsi_stream_handler fsi_dma_push_handler = {
+static const struct fsi_stream_handler fsi_dma_push_handler = {
.init = fsi_dma_init,
.probe = fsi_dma_probe,
.transfer = fsi_dma_transfer,
@@ -1637,12 +1632,12 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
int ret;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* set clock master audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- fsi->clk_master = 1; /* codec is slave, cpu is master */
+ case SND_SOC_DAIFMT_BP_FP:
+ fsi->clk_master = 1; /* cpu is master */
break;
default:
return -EINVAL;
@@ -1699,19 +1694,34 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+/*
+ * Select below from Sound Card, not auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 fsi_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops fsi_dai_ops = {
.startup = fsi_dai_startup,
.shutdown = fsi_dai_shutdown,
.trigger = fsi_dai_trigger,
.set_fmt = fsi_dai_set_fmt,
.hw_params = fsi_dai_hw_params,
+ .auto_selectable_formats = &fsi_dai_formats,
+ .num_auto_selectable_formats = 1,
};
/*
* pcm ops
*/
-static struct snd_pcm_hardware fsi_pcm_hardware = {
+static const struct snd_pcm_hardware fsi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID,
@@ -1723,7 +1733,8 @@ static struct snd_pcm_hardware fsi_pcm_hardware = {
.fifo_size = 256,
};
-static int fsi_pcm_open(struct snd_pcm_substream *substream)
+static int fsi_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
@@ -1736,19 +1747,8 @@ static int fsi_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int fsi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int fsi_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_stream *io = fsi_stream_get(fsi, substream);
@@ -1756,28 +1756,22 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
return fsi_sample2frame(fsi, io->buff_sample_pos);
}
-static struct snd_pcm_ops fsi_pcm_ops = {
- .open = fsi_pcm_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fsi_hw_params,
- .hw_free = fsi_hw_free,
- .pointer = fsi_pointer,
-};
-
/*
- * snd_soc_platform
+ * snd_soc_component
*/
#define PREALLOC_BUFFER (32 * 1024)
#define PREALLOC_BUFFER_MAX (32 * 1024)
-static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int fsi_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- return snd_pcm_lib_preallocate_pages_for_all(
+ snd_pcm_set_managed_buffer_all(
rtd->pcm,
SNDRV_DMA_TYPE_DEV,
rtd->card->snd_card->dev,
PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+ return 0;
}
/*
@@ -1819,13 +1813,11 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
},
};
-static struct snd_soc_platform_driver fsi_soc_platform = {
- .ops = &fsi_pcm_ops,
- .pcm_new = fsi_pcm_new,
-};
-
static const struct snd_soc_component_driver fsi_soc_component = {
.name = "fsi",
+ .open = fsi_pcm_open,
+ .pointer = fsi_pointer,
+ .pcm_construct = fsi_pcm_new,
};
/*
@@ -1850,7 +1842,7 @@ static void fsi_of_parse(char *name,
for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) {
sprintf(prop, "%s,%s", name, of_parse_property[i].name);
- if (of_get_property(np, prop, NULL))
+ if (of_property_present(np, prop))
flags |= of_parse_property[i].val;
}
info->flags = flags;
@@ -1933,14 +1925,9 @@ static int fsi_probe(struct platform_device *pdev)
core = NULL;
if (np) {
- const struct of_device_id *of_id;
-
- of_id = of_match_device(fsi_of_match, &pdev->dev);
- if (of_id) {
- core = of_id->data;
- fsi_of_parse("fsia", np, &info.port_a, &pdev->dev);
- fsi_of_parse("fsib", np, &info.port_b, &pdev->dev);
- }
+ core = of_device_get_match_data(&pdev->dev);
+ fsi_of_parse("fsia", np, &info.port_a, &pdev->dev);
+ fsi_of_parse("fsib", np, &info.port_b, &pdev->dev);
} else {
const struct platform_device_id *id_entry = pdev->id_entry;
if (id_entry)
@@ -1963,13 +1950,10 @@ static int fsi_probe(struct platform_device *pdev)
}
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
- if (!master) {
- dev_err(&pdev->dev, "Could not allocate master\n");
+ if (!master)
return -ENOMEM;
- }
- master->base = devm_ioremap_nocache(&pdev->dev,
- res->start, resource_size(res));
+ master->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!master->base) {
dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
return -ENXIO;
@@ -2015,23 +1999,15 @@ static int fsi_probe(struct platform_device *pdev)
goto exit_fsib;
}
- ret = snd_soc_register_platform(&pdev->dev, &fsi_soc_platform);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot snd soc register\n");
- goto exit_fsib;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &fsi_soc_component,
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsi_soc_component,
fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
if (ret < 0) {
dev_err(&pdev->dev, "cannot snd component register\n");
- goto exit_snd_soc;
+ goto exit_fsib;
}
return ret;
-exit_snd_soc:
- snd_soc_unregister_platform(&pdev->dev);
exit_fsib:
pm_runtime_disable(&pdev->dev);
fsi_stream_remove(&master->fsib);
@@ -2041,7 +2017,7 @@ exit_fsia:
return ret;
}
-static int fsi_remove(struct platform_device *pdev)
+static void fsi_remove(struct platform_device *pdev)
{
struct fsi_master *master;
@@ -2049,13 +2025,8 @@ static int fsi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
-
fsi_stream_remove(&master->fsia);
fsi_stream_remove(&master->fsib);
-
- return 0;
}
static void __fsi_suspend(struct fsi_priv *fsi,
@@ -2110,7 +2081,7 @@ static int fsi_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops fsi_pm_ops = {
+static const struct dev_pm_ops fsi_pm_ops = {
.suspend = fsi_suspend,
.resume = fsi_resume,
};
diff --git a/sound/soc/sh/hac.c b/sound/soc/renesas/hac.c
index 84c51037a7d0..db618c09d1e0 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/renesas/hac.c
@@ -1,13 +1,11 @@
-/*
- * Hitachi Audio Controller (AC97) support for SH7760/SH7780
- *
- * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
- * licensed under the terms outlined in the file COPYING at the root
- * of the linux kernel sources.
- *
- * dont forget to set IPSEL/OMSEL register bits (in your board code) to
- * enable HAC output pins!
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Hitachi Audio Controller (AC97) support for SH7760/SH7780
+//
+// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+//
+// dont forget to set IPSEL/OMSEL register bits (in your board code) to
+// enable HAC output pins!
/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
* the FIRST can be used since ASoC does not pass any information to the
@@ -272,7 +270,6 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
static struct snd_soc_dai_driver sh4_hac_dai[] = {
{
.name = "hac-dai.0",
- .bus_control = true,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -310,24 +307,25 @@ static struct snd_soc_dai_driver sh4_hac_dai[] = {
};
static const struct snd_soc_component_driver sh4_hac_component = {
- .name = "sh4-hac",
+ .name = "sh4-hac",
+ .legacy_dai_naming = 1,
};
static int hac_soc_platform_probe(struct platform_device *pdev)
{
+ int ret;
+
ret = snd_soc_set_ac97_ops(&hac_ac97_ops);
if (ret != 0)
return ret;
- return snd_soc_register_component(&pdev->dev, &sh4_hac_component,
+ return devm_snd_soc_register_component(&pdev->dev, &sh4_hac_component,
sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
}
-static int hac_soc_platform_remove(struct platform_device *pdev)
+static void hac_soc_platform_remove(struct platform_device *pdev)
{
- snd_soc_unregister_component(&pdev->dev);
snd_soc_set_ac97_ops(NULL);
- return 0;
}
static struct platform_driver hac_pcm_driver = {
@@ -341,6 +339,6 @@ static struct platform_driver hac_pcm_driver = {
module_platform_driver(hac_pcm_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/migor.c b/sound/soc/renesas/migor.c
index 672bcd4c252b..45fc594d331b 100644
--- a/sound/soc/sh/migor.c
+++ b/sound/soc/renesas/migor.c
@@ -1,12 +1,8 @@
-/*
- * ALSA SoC driver for Migo-R
- *
- * Copyright (C) 2009-2010 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC driver for Migo-R
+//
+// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
#include <linux/clkdev.h>
#include <linux/device.h>
@@ -49,8 +45,8 @@ static struct clk_lookup *siumckb_lookup;
static int migor_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
unsigned int rate = params_rate(params);
@@ -71,7 +67,7 @@ static int migor_hw_params(struct snd_pcm_substream *substream,
clk_set_rate(&siumckb_clk, codec_freq);
dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
- ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, SIU_CLKB_EXT,
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), SIU_CLKB_EXT,
codec_freq / 2, SND_SOC_CLOCK_IN);
if (!ret)
@@ -82,8 +78,8 @@ static int migor_hw_params(struct snd_pcm_substream *substream,
static int migor_hw_free(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
if (use_count) {
use_count--;
@@ -98,7 +94,7 @@ static int migor_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_soc_ops migor_dai_ops = {
+static const struct snd_soc_ops migor_dai_ops = {
.hw_params = migor_hw_params,
.hw_free = migor_hw_free,
};
@@ -127,16 +123,18 @@ static const struct snd_soc_dapm_route audio_map[] = {
};
/* migor digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(wm8978,
+ DAILINK_COMP_ARRAY(COMP_CPU("siu-pcm-audio")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8978.0-001a", "wm8978-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("siu-pcm-audio")));
+
static struct snd_soc_dai_link migor_dai = {
.name = "wm8978",
.stream_name = "WM8978",
- .cpu_dai_name = "siu-pcm-audio",
- .codec_dai_name = "wm8978-hifi",
- .platform_name = "siu-pcm-audio",
- .codec_name = "wm8978.0-001a",
.dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ops = &migor_dai_ops,
+ SND_SOC_DAILINK_REG(wm8978),
};
/* migor audio machine driver */
diff --git a/sound/soc/renesas/rcar/Makefile b/sound/soc/renesas/rcar/Makefile
new file mode 100644
index 000000000000..3a2c875595bd
--- /dev/null
+++ b/sound/soc/renesas/rcar/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-rcar-y := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o debugfs.o
+obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
+
+snd-soc-msiof-y := msiof.o
+obj-$(CONFIG_SND_SOC_MSIOF) += snd-soc-msiof.o
diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c
new file mode 100644
index 000000000000..8641b73d1f77
--- /dev/null
+++ b/sound/soc/renesas/rcar/adg.c
@@ -0,0 +1,819 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Helper routines for R-Car sound ADG.
+//
+// Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "rsnd.h"
+
+#define CLKA 0
+#define CLKB 1
+#define CLKC 2
+#define CLKI 3
+#define CLKINMAX 4
+
+#define CLKOUT 0
+#define CLKOUT1 1
+#define CLKOUT2 2
+#define CLKOUT3 3
+#define CLKOUTMAX 4
+
+#define BRGCKR_31 (1 << 31)
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+ .name = "adg",
+};
+
+#define ADG_HZ_441 0
+#define ADG_HZ_48 1
+#define ADG_HZ_SIZE 2
+
+struct rsnd_adg {
+ struct clk *adg;
+ struct clk *clkin[CLKINMAX];
+ struct clk *clkout[CLKOUTMAX];
+ struct clk *null_clk;
+ struct clk_onecell_data onecell;
+ struct rsnd_mod mod;
+ int clkin_rate[CLKINMAX];
+ int clkin_size;
+ int clkout_size;
+ u32 ckr;
+ u32 brga;
+ u32 brgb;
+
+ int brg_rate[ADG_HZ_SIZE]; /* BRGA / BRGB */
+};
+
+#define for_each_rsnd_clkin(pos, adg, i) \
+ for (i = 0; \
+ (i < adg->clkin_size) && \
+ ((pos) = adg->clkin[i]); \
+ i++)
+#define for_each_rsnd_clkout(pos, adg, i) \
+ for (i = 0; \
+ (i < adg->clkout_size) && \
+ ((pos) = adg->clkout[i]); \
+ i++)
+#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+
+static const char * const clkin_name_gen4[] = {
+ [CLKA] = "clkin",
+};
+
+static const char * const clkin_name_gen2[] = {
+ [CLKA] = "clk_a",
+ [CLKB] = "clk_b",
+ [CLKC] = "clk_c",
+ [CLKI] = "clk_i",
+};
+
+static const char * const clkout_name_gen2[] = {
+ [CLKOUT] = "audio_clkout",
+ [CLKOUT1] = "audio_clkout1",
+ [CLKOUT2] = "audio_clkout2",
+ [CLKOUT3] = "audio_clkout3",
+};
+
+static u32 rsnd_adg_calculate_brgx(unsigned long div)
+{
+ int i;
+
+ if (!div)
+ return 0;
+
+ for (i = 3; i >= 0; i--) {
+ int ratio = 2 << (i * 2);
+ if (0 == (div % ratio))
+ return (u32)((i << 8) | ((div / ratio) - 1));
+ }
+
+ return ~0;
+}
+
+static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ int id = rsnd_mod_id(ssi_mod);
+ int ws = id;
+
+ if (rsnd_ssi_is_pin_sharing(io)) {
+ switch (id) {
+ case 1:
+ case 2:
+ case 9:
+ ws = 0;
+ break;
+ case 4:
+ ws = 3;
+ break;
+ case 8:
+ ws = 7;
+ break;
+ }
+ } else {
+ /*
+ * SSI8 is not connected to ADG.
+ * Thus SSI9 is using ws = 8
+ */
+ if (id == 9)
+ ws = 8;
+ }
+
+ return (0x6 + ws) << 8;
+}
+
+static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ unsigned int target_rate,
+ unsigned int *target_val,
+ unsigned int *target_en)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int sel;
+ unsigned int val, en;
+ unsigned int min, diff;
+ unsigned int sel_rate[] = {
+ adg->clkin_rate[CLKA], /* 0000: CLKA */
+ adg->clkin_rate[CLKB], /* 0001: CLKB */
+ adg->clkin_rate[CLKC], /* 0010: CLKC */
+ adg->brg_rate[ADG_HZ_441], /* 0011: BRGA */
+ adg->brg_rate[ADG_HZ_48], /* 0100: BRGB */
+ };
+
+ min = ~0;
+ val = 0;
+ en = 0;
+ for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+ int idx = 0;
+ int step = 2;
+ int div;
+
+ if (!sel_rate[sel])
+ continue;
+
+ for (div = 2; div <= 98304; div += step) {
+ diff = abs(target_rate - sel_rate[sel] / div);
+ if (min > diff) {
+ val = (sel << 8) | idx;
+ min = diff;
+ en = 1 << (sel + 1); /* fixme */
+ }
+
+ /*
+ * step of 0_0000 / 0_0001 / 0_1101
+ * are out of order
+ */
+ if ((idx > 2) && (idx % 2))
+ step *= 2;
+ if (idx == 0x1c) {
+ div += step;
+ step *= 2;
+ }
+ idx++;
+ }
+ }
+
+ if (min == ~0) {
+ dev_err(dev, "no Input clock\n");
+ return;
+ }
+
+ *target_val = val;
+ if (target_en)
+ *target_en = en;
+}
+
+static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate,
+ u32 *in, u32 *out, u32 *en)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ unsigned int target_rate;
+ u32 *target_val;
+ u32 _in;
+ u32 _out;
+ u32 _en;
+
+ /* default = SSI WS */
+ _in =
+ _out = rsnd_adg_ssi_ws_timing_gen2(io);
+
+ target_rate = 0;
+ target_val = NULL;
+ _en = 0;
+ if (runtime->rate != in_rate) {
+ target_rate = out_rate;
+ target_val = &_out;
+ } else if (runtime->rate != out_rate) {
+ target_rate = in_rate;
+ target_val = &_in;
+ }
+
+ if (target_rate)
+ __rsnd_adg_get_timesel_ratio(priv, io,
+ target_rate,
+ target_val, &_en);
+
+ if (in)
+ *in = _in;
+ if (out)
+ *out = _out;
+ if (en)
+ *en = _en;
+}
+
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ int id = rsnd_mod_id(cmd_mod);
+ int shift = (id % 2) ? 16 : 0;
+ u32 mask, val;
+
+ rsnd_adg_get_timesel_ratio(priv, io,
+ rsnd_src_get_in_rate(priv, io),
+ rsnd_src_get_out_rate(priv, io),
+ NULL, &val, NULL);
+
+ val = val << shift;
+ mask = 0x0f1f << shift;
+
+ rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
+
+ return 0;
+}
+
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ u32 in, out;
+ u32 mask, en;
+ int id = rsnd_mod_id(src_mod);
+ int shift = (id % 2) ? 16 : 0;
+
+ rsnd_mod_make_sure(src_mod, RSND_MOD_SRC);
+
+ rsnd_adg_get_timesel_ratio(priv, io,
+ in_rate, out_rate,
+ &in, &out, &en);
+
+ in = in << shift;
+ out = out << shift;
+ mask = 0x0f1f << shift;
+
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2), mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out);
+
+ if (en)
+ rsnd_mod_bset(adg_mod, DIV_EN, en, en);
+
+ return 0;
+}
+
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int id = rsnd_mod_id(ssi_mod);
+ int shift = (id % 4) * 8;
+ u32 mask = 0xFF << shift;
+
+ rsnd_mod_make_sure(ssi_mod, RSND_MOD_SSI);
+
+ val = val << shift;
+
+ /*
+ * SSI 8 is not connected to ADG.
+ * it works with SSI 7
+ */
+ if (id == 8)
+ return;
+
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val);
+
+ dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val);
+}
+
+int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct clk *clk;
+ int i;
+ int sel_table[] = {
+ [CLKA] = 0x1,
+ [CLKB] = 0x2,
+ [CLKC] = 0x3,
+ [CLKI] = 0x0,
+ };
+
+ /*
+ * find suitable clock from
+ * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
+ */
+ for_each_rsnd_clkin(clk, adg, i)
+ if (rate == adg->clkin_rate[i])
+ return sel_table[i];
+
+ /*
+ * find divided clock from BRGA/BRGB
+ */
+ if (rate == adg->brg_rate[ADG_HZ_441])
+ return 0x10;
+
+ if (rate == adg->brg_rate[ADG_HZ_48])
+ return 0x20;
+
+ return -EIO;
+}
+
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
+{
+ rsnd_adg_set_ssi_clk(ssi_mod, 0);
+
+ return 0;
+}
+
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ int data;
+ u32 ckr = 0;
+
+ data = rsnd_adg_clk_query(priv, rate);
+ if (data < 0)
+ return data;
+
+ rsnd_adg_set_ssi_clk(ssi_mod, data);
+
+ ckr = adg->ckr & ~BRGCKR_31;
+ if (0 == (rate % 8000))
+ ckr |= BRGCKR_31; /* use BRGB output = 48kHz */
+ if (ckr != adg->ckr) {
+ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);
+ adg->ckr = ckr;
+ }
+
+ dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n",
+ (ckr) ? 'B' : 'A',
+ (ckr) ? adg->brg_rate[ADG_HZ_48] :
+ adg->brg_rate[ADG_HZ_441]);
+
+ return 0;
+}
+
+int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ struct clk *clk;
+ int ret = 0, i;
+
+ if (enable) {
+ ret = clk_prepare_enable(adg->adg);
+ if (ret < 0)
+ return ret;
+
+ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);
+ rsnd_mod_write(adg_mod, BRRA, adg->brga);
+ rsnd_mod_write(adg_mod, BRRB, adg->brgb);
+ }
+
+ for_each_rsnd_clkin(clk, adg, i) {
+ if (enable) {
+ ret = clk_prepare_enable(clk);
+
+ /*
+ * We shouldn't use clk_get_rate() under
+ * atomic context. Let's keep it when
+ * rsnd_adg_clk_enable() was called
+ */
+ if (ret < 0)
+ break;
+
+ adg->clkin_rate[i] = clk_get_rate(clk);
+ } else {
+ if (adg->clkin_rate[i])
+ clk_disable_unprepare(clk);
+
+ adg->clkin_rate[i] = 0;
+ }
+ }
+
+ /*
+ * rsnd_adg_clk_enable() might return error (_disable() will not).
+ * We need to rollback in such case
+ */
+ if (ret < 0)
+ rsnd_adg_clk_disable(priv);
+
+ /* disable adg */
+ if (!enable)
+ clk_disable_unprepare(adg->adg);
+
+ return ret;
+}
+
+static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv,
+ const char * const name,
+ const char *parent)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(dev, name, parent, 0, 0);
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "create null clk error\n");
+ return ERR_CAST(clk);
+ }
+
+ return clk;
+}
+
+static struct clk *rsnd_adg_null_clk_get(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+
+ if (!adg->null_clk) {
+ static const char * const name = "rsnd_adg_null";
+
+ adg->null_clk = rsnd_adg_create_null_clk(priv, name, NULL);
+ }
+
+ return adg->null_clk;
+}
+
+static void rsnd_adg_null_clk_clean(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+
+ if (adg->null_clk)
+ clk_unregister_fixed_rate(adg->null_clk);
+}
+
+static int rsnd_adg_get_clkin(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ const char * const *clkin_name;
+ int clkin_size;
+ int i;
+
+ clkin_name = clkin_name_gen2;
+ clkin_size = ARRAY_SIZE(clkin_name_gen2);
+ if (rsnd_is_gen4(priv)) {
+ clkin_name = clkin_name_gen4;
+ clkin_size = ARRAY_SIZE(clkin_name_gen4);
+ }
+
+ /*
+ * get adg
+ * No "adg" is not error
+ */
+ clk = devm_clk_get(dev, "adg");
+ if (IS_ERR_OR_NULL(clk))
+ clk = rsnd_adg_null_clk_get(priv);
+ adg->adg = clk;
+
+ /* get clkin */
+ for (i = 0; i < clkin_size; i++) {
+ clk = devm_clk_get(dev, clkin_name[i]);
+
+ if (IS_ERR_OR_NULL(clk))
+ clk = rsnd_adg_null_clk_get(priv);
+ if (IS_ERR_OR_NULL(clk))
+ goto err;
+
+ adg->clkin[i] = clk;
+ }
+
+ adg->clkin_size = clkin_size;
+
+ return 0;
+
+err:
+ dev_err(dev, "adg clock IN get failed\n");
+
+ rsnd_adg_null_clk_clean(priv);
+
+ return -EIO;
+}
+
+static void rsnd_adg_unregister_clkout(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+ struct clk *clk;
+ int i;
+
+ for_each_rsnd_clkout(clk, adg, i)
+ clk_unregister_fixed_rate(clk);
+}
+
+static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+ struct clk *clk;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+ struct property *prop;
+ u32 ckr, brgx, brga, brgb;
+ u32 req_rate[ADG_HZ_SIZE] = {};
+ uint32_t count = 0;
+ unsigned long req_Hz[ADG_HZ_SIZE];
+ int clkout_size;
+ int i, req_size;
+ int approximate = 0;
+ const char *parent_clk_name = NULL;
+ const char * const *clkout_name;
+ int brg_table[] = {
+ [CLKA] = 0x0,
+ [CLKB] = 0x1,
+ [CLKC] = 0x4,
+ [CLKI] = 0x2,
+ };
+
+ ckr = 0;
+ brga = 0xff; /* default */
+ brgb = 0xff; /* default */
+
+ /*
+ * ADG supports BRRA/BRRB output only
+ * this means all clkout0/1/2/3 will be same rate
+ */
+ prop = of_find_property(np, "clock-frequency", NULL);
+ if (!prop)
+ goto rsnd_adg_get_clkout_end;
+
+ req_size = prop->length / sizeof(u32);
+ if (req_size > ADG_HZ_SIZE) {
+ dev_err(dev, "too many clock-frequency\n");
+ return -EINVAL;
+ }
+
+ of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
+ req_Hz[ADG_HZ_48] = 0;
+ req_Hz[ADG_HZ_441] = 0;
+ for (i = 0; i < req_size; i++) {
+ if (0 == (req_rate[i] % 44100))
+ req_Hz[ADG_HZ_441] = req_rate[i];
+ if (0 == (req_rate[i] % 48000))
+ req_Hz[ADG_HZ_48] = req_rate[i];
+ }
+
+ /*
+ * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
+ * have 44.1kHz or 48kHz base clocks for now.
+ *
+ * SSI itself can divide parent clock by 1/1 - 1/16
+ * see
+ * rsnd_adg_ssi_clk_try_start()
+ * rsnd_ssi_master_clk_start()
+ */
+
+ /*
+ * [APPROXIMATE]
+ *
+ * clk_i (internal clock) can't create accurate rate, it will be approximate rate.
+ *
+ * <Note>
+ *
+ * clk_i needs x2 of required maximum rate.
+ * see
+ * - Minimum division of BRRA/BRRB
+ * - rsnd_ssi_clk_query()
+ *
+ * Sample Settings for TDM 8ch, 32bit width
+ *
+ * 8(ch) x 32(bit) x 44100(Hz) x 2<Note> = 22579200
+ * 8(ch) x 32(bit) x 48000(Hz) x 2<Note> = 24576000
+ *
+ * clock-frequency = <22579200 24576000>;
+ */
+ for_each_rsnd_clkin(clk, adg, i) {
+ u32 rate, div;
+
+ rate = clk_get_rate(clk);
+
+ if (0 == rate) /* not used */
+ continue;
+
+ /* BRGA */
+
+ if (i == CLKI)
+ /* see [APPROXIMATE] */
+ rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441];
+ if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) {
+ div = rate / req_Hz[ADG_HZ_441];
+ brgx = rsnd_adg_calculate_brgx(div);
+ if (BRRx_MASK(brgx) == brgx) {
+ brga = brgx;
+ adg->brg_rate[ADG_HZ_441] = rate / div;
+ ckr |= brg_table[i] << 20;
+ if (req_Hz[ADG_HZ_441])
+ parent_clk_name = __clk_get_name(clk);
+ if (i == CLKI)
+ approximate = 1;
+ }
+ }
+
+ /* BRGB */
+
+ if (i == CLKI)
+ /* see [APPROXIMATE] */
+ rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48];
+ if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) {
+ div = rate / req_Hz[ADG_HZ_48];
+ brgx = rsnd_adg_calculate_brgx(div);
+ if (BRRx_MASK(brgx) == brgx) {
+ brgb = brgx;
+ adg->brg_rate[ADG_HZ_48] = rate / div;
+ ckr |= brg_table[i] << 16;
+ if (req_Hz[ADG_HZ_48])
+ parent_clk_name = __clk_get_name(clk);
+ if (i == CLKI)
+ approximate = 1;
+ }
+ }
+ }
+
+ if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) &&
+ !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441]))
+ goto rsnd_adg_get_clkout_end;
+
+ if (approximate)
+ dev_info(dev, "It uses CLK_I as approximate rate");
+
+ clkout_name = clkout_name_gen2;
+ clkout_size = ARRAY_SIZE(clkout_name_gen2);
+ if (rsnd_is_gen4(priv))
+ clkout_size = 1; /* reuse clkout_name_gen2[] */
+
+ /*
+ * ADG supports BRRA/BRRB output only.
+ * this means all clkout0/1/2/3 will be * same rate
+ */
+
+ of_property_read_u32(np, "#clock-cells", &count);
+ /*
+ * for clkout
+ */
+ if (!count) {
+ clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+ parent_clk_name, 0, req_rate[0]);
+ if (IS_ERR_OR_NULL(clk))
+ goto err;
+
+ adg->clkout[CLKOUT] = clk;
+ adg->clkout_size = 1;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ }
+ /*
+ * for clkout0/1/2/3
+ */
+ else {
+ for (i = 0; i < clkout_size; i++) {
+ clk = clk_register_fixed_rate(dev, clkout_name[i],
+ parent_clk_name, 0,
+ req_rate[0]);
+ if (IS_ERR_OR_NULL(clk))
+ goto err;
+
+ adg->clkout[i] = clk;
+ }
+ adg->onecell.clks = adg->clkout;
+ adg->onecell.clk_num = clkout_size;
+ adg->clkout_size = clkout_size;
+ of_clk_add_provider(np, of_clk_src_onecell_get,
+ &adg->onecell);
+ }
+
+rsnd_adg_get_clkout_end:
+ if (0 == (req_rate[0] % 8000))
+ ckr |= BRGCKR_31; /* use BRGB output = 48kHz */
+
+ adg->ckr = ckr;
+ adg->brga = brga;
+ adg->brgb = brgb;
+
+ return 0;
+
+err:
+ dev_err(dev, "adg clock OUT get failed\n");
+
+ rsnd_adg_unregister_clkout(priv);
+
+ return -EIO;
+}
+
+#if defined(DEBUG) || defined(CONFIG_DEBUG_FS)
+__printf(3, 4)
+static void dbg_msg(struct device *dev, struct seq_file *m,
+ const char *fmt, ...)
+{
+ char msg[128];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, args);
+ va_end(args);
+
+ if (m)
+ seq_puts(m, msg);
+ else
+ dev_dbg(dev, "%s", msg);
+}
+
+void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ int i;
+
+ for_each_rsnd_clkin(clk, adg, i)
+ dbg_msg(dev, m, "%-18s : %pa : %ld\n",
+ __clk_get_name(clk), clk, clk_get_rate(clk));
+
+ dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+ adg->ckr, adg->brga, adg->brgb);
+ dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->brg_rate[ADG_HZ_441]);
+ dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->brg_rate[ADG_HZ_48]);
+
+ /*
+ * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start()
+ * by BRGCKR::BRGCKR_31
+ */
+ for_each_rsnd_clkout(clk, adg, i)
+ dbg_msg(dev, m, "%-18s : %pa : %ld\n",
+ __clk_get_name(clk), clk, clk_get_rate(clk));
+}
+#else
+#define rsnd_adg_clk_dbg_info(priv, m)
+#endif
+
+int rsnd_adg_probe(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int ret;
+
+ adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
+ if (!adg)
+ return -ENOMEM;
+
+ ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
+ NULL, 0, 0);
+ if (ret)
+ return ret;
+
+ priv->adg = adg;
+
+ ret = rsnd_adg_get_clkin(priv);
+ if (ret)
+ return ret;
+
+ ret = rsnd_adg_get_clkout(priv);
+ if (ret)
+ return ret;
+
+ ret = rsnd_adg_clk_enable(priv);
+ if (ret)
+ return ret;
+
+ rsnd_adg_clk_dbg_info(priv, NULL);
+
+ return 0;
+}
+
+void rsnd_adg_remove(struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+
+ rsnd_adg_unregister_clkout(priv);
+
+ of_clk_del_provider(np);
+
+ rsnd_adg_clk_disable(priv);
+
+ /* It should be called after rsnd_adg_clk_disable() */
+ rsnd_adg_null_clk_clean(priv);
+}
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/renesas/rcar/cmd.c
index cd1f064e63c4..8d9a1e345a22 100644
--- a/sound/soc/sh/rcar/cmd.c
+++ b/sound/soc/renesas/rcar/cmd.c
@@ -1,13 +1,10 @@
-/*
- * Renesas R-Car CMD support
- *
- * Copyright (C) 2015 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car CMD support
+//
+// Copyright (C) 2015 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
#include "rsnd.h"
struct rsnd_cmd {
@@ -29,24 +26,24 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
{
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
- struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
+ static const u32 path[] = {
+ [1] = 1 << 0,
+ [5] = 1 << 8,
+ [6] = 1 << 12,
+ [9] = 1 << 15,
+ };
if (!mix && !dvc)
return 0;
+ if (ARRAY_SIZE(path) < rsnd_mod_id(mod) + 1)
+ return -ENXIO;
+
if (mix) {
struct rsnd_dai *rdai;
int i;
- u32 path[] = {
- [0] = 0,
- [1] = 1 << 0,
- [2] = 0,
- [3] = 0,
- [4] = 0,
- [5] = 1 << 8
- };
/*
* it is assuming that integrater is well understanding about
@@ -55,31 +52,43 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
*/
data = 0;
for_each_rsnd_dai(rdai, priv, i) {
- io = &rdai->playback;
- if (mix == rsnd_io_to_mod_mix(io))
+ struct rsnd_dai_stream *tio = &rdai->playback;
+ struct rsnd_mod *src = rsnd_io_to_mod_src(tio);
+
+ if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
- io = &rdai->capture;
- if (mix == rsnd_io_to_mod_mix(io))
+ tio = &rdai->capture;
+ src = rsnd_io_to_mod_src(tio);
+ if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
}
} else {
- u32 path[] = {
- [0] = 0x30000,
- [1] = 0x30001,
- [2] = 0x40000,
- [3] = 0x10000,
- [4] = 0x20000,
- [5] = 0x40100
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
+ static const u8 cmd_case[] = {
+ [0] = 0x3,
+ [1] = 0x3,
+ [2] = 0x4,
+ [3] = 0x1,
+ [4] = 0x2,
+ [5] = 0x4,
+ [6] = 0x1,
+ [9] = 0x2,
};
- data = path[rsnd_mod_id(src)];
+ if (unlikely(!src))
+ return -EIO;
+
+ data = path[rsnd_mod_id(src)] |
+ cmd_case[rsnd_mod_id(src)] << 16;
}
- dev_dbg(dev, "ctu/mix path = 0x%08x", data);
+ dev_dbg(dev, "ctu/mix path = 0x%08x\n", data);
rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
+ rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1);
rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
rsnd_adg_set_cmd_timsel_gen2(mod, io);
@@ -105,13 +114,35 @@ static int rsnd_cmd_stop(struct rsnd_mod *mod,
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_cmd_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ 0x180 + rsnd_mod_id_raw(mod) * 0x20, 0x30);
+}
+#define DEBUG_INFO .debug_info = rsnd_cmd_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_cmd_ops = {
- .name = CMD_NAME,
- .init = rsnd_cmd_init,
- .start = rsnd_cmd_start,
- .stop = rsnd_cmd_stop,
+ .name = CMD_NAME,
+ .init = rsnd_cmd_init,
+ .start = rsnd_cmd_start,
+ .stop = rsnd_cmd_stop,
+ .get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
+static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
+}
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
@@ -120,30 +151,18 @@ int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
return rsnd_dai_connect(mod, io, mod->type);
}
-struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
-{
- if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
- id = 0;
-
- return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
-}
-
int rsnd_cmd_probe(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_cmd *cmd;
- int i, nr, ret;
-
- /* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv))
- return 0;
+ int i, nr;
/* same number as DVC */
nr = priv->dvc_nr;
if (!nr)
return 0;
- cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL);
+ cmd = devm_kcalloc(dev, nr, sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
@@ -151,8 +170,9 @@ int rsnd_cmd_probe(struct rsnd_priv *priv)
priv->cmd = cmd;
for_each_rsnd_cmd(cmd, priv, i) {
- ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
- &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
+ int ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
+ &rsnd_cmd_ops, NULL,
+ RSND_MOD_CMD, i);
if (ret)
return ret;
}
diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c
new file mode 100644
index 000000000000..69fb19964a71
--- /dev/null
+++ b/sound/soc/renesas/rcar/core.c
@@ -0,0 +1,2080 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SRU/SCU/SSIU/SSI support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// Based on fsi.c
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+
+/*
+ * Renesas R-Car sound device structure
+ *
+ * Gen1
+ *
+ * SRU : Sound Routing Unit
+ * - SRC : Sampling Rate Converter
+ * - CMD
+ * - CTU : Channel Count Conversion Unit
+ * - MIX : Mixer
+ * - DVC : Digital Volume and Mute Function
+ * - SSI : Serial Sound Interface
+ *
+ * Gen2
+ *
+ * SCU : Sampling Rate Converter Unit
+ * - SRC : Sampling Rate Converter
+ * - CMD
+ * - CTU : Channel Count Conversion Unit
+ * - MIX : Mixer
+ * - DVC : Digital Volume and Mute Function
+ * SSIU : Serial Sound Interface Unit
+ * - SSI : Serial Sound Interface
+ */
+
+/*
+ * driver data Image
+ *
+ * rsnd_priv
+ * |
+ * | ** this depends on Gen1/Gen2
+ * |
+ * +- gen
+ * |
+ * | ** these depend on data path
+ * | ** gen and platform data control it
+ * |
+ * +- rdai[0]
+ * | | sru ssiu ssi
+ * | +- playback -> [mod] -> [mod] -> [mod] -> ...
+ * | |
+ * | | sru ssiu ssi
+ * | +- capture -> [mod] -> [mod] -> [mod] -> ...
+ * |
+ * +- rdai[1]
+ * | | sru ssiu ssi
+ * | +- playback -> [mod] -> [mod] -> [mod] -> ...
+ * | |
+ * | | sru ssiu ssi
+ * | +- capture -> [mod] -> [mod] -> [mod] -> ...
+ * ...
+ * |
+ * | ** these control ssi
+ * |
+ * +- ssi
+ * | |
+ * | +- ssi[0]
+ * | +- ssi[1]
+ * | +- ssi[2]
+ * | ...
+ * |
+ * | ** these control src
+ * |
+ * +- src
+ * |
+ * +- src[0]
+ * +- src[1]
+ * +- src[2]
+ * ...
+ *
+ *
+ * for_each_rsnd_dai(xx, priv, xx)
+ * rdai[0] => rdai[1] => rdai[2] => ...
+ *
+ * for_each_rsnd_mod(xx, rdai, xx)
+ * [mod] => [mod] => [mod] => ...
+ *
+ * rsnd_dai_call(xxx, fn )
+ * [mod]->fn() -> [mod]->fn() -> [mod]->fn()...
+ *
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/of_graph.h>
+#include "rsnd.h"
+
+#define RSND_RATES SNDRV_PCM_RATE_8000_192000
+#define RSND_FMTS (SNDRV_PCM_FMTBIT_S8 |\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct of_device_id rsnd_of_match[] = {
+ { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
+ { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
+ { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 },
+ { .compatible = "renesas,rcar_sound-gen4", .data = (void *)RSND_GEN4 },
+ /* Special Handling */
+ { .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rsnd_of_match);
+
+/*
+ * rsnd_mod functions
+ */
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+ if (mod->type != type) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "%s is not your expected module\n",
+ rsnd_mod_name(mod));
+ }
+}
+
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ if (!mod || !mod->ops || !mod->ops->dma_req)
+ return NULL;
+
+ return mod->ops->dma_req(io, mod);
+}
+
+#define MOD_NAME_NUM 5
+#define MOD_NAME_SIZE 16
+char *rsnd_mod_name(struct rsnd_mod *mod)
+{
+ static char names[MOD_NAME_NUM][MOD_NAME_SIZE];
+ static int num;
+ char *name = names[num];
+
+ num++;
+ if (num >= MOD_NAME_NUM)
+ num = 0;
+
+ /*
+ * Let's use same char to avoid pointlessness memory
+ * Thus, rsnd_mod_name() should be used immediately
+ * Don't keep pointer
+ */
+ if ((mod)->ops->id_sub) {
+ snprintf(name, MOD_NAME_SIZE, "%s[%d%d]",
+ mod->ops->name,
+ rsnd_mod_id(mod),
+ rsnd_mod_id_sub(mod));
+ } else {
+ snprintf(name, MOD_NAME_SIZE, "%s[%d]",
+ mod->ops->name,
+ rsnd_mod_id(mod));
+ }
+
+ return name;
+}
+
+u32 *rsnd_mod_get_status(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type)
+{
+ return &mod->status;
+}
+
+int rsnd_mod_id_raw(struct rsnd_mod *mod)
+{
+ return mod->id;
+}
+
+int rsnd_mod_id(struct rsnd_mod *mod)
+{
+ if ((mod)->ops->id)
+ return (mod)->ops->id(mod);
+
+ return rsnd_mod_id_raw(mod);
+}
+
+int rsnd_mod_id_sub(struct rsnd_mod *mod)
+{
+ if ((mod)->ops->id_sub)
+ return (mod)->ops->id_sub(mod);
+
+ return 0;
+}
+
+int rsnd_mod_init(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_mod_ops *ops,
+ struct clk *clk,
+ enum rsnd_mod_type type,
+ int id)
+{
+ int ret = clk_prepare(clk);
+
+ if (ret)
+ return ret;
+
+ mod->id = id;
+ mod->ops = ops;
+ mod->type = type;
+ mod->clk = clk;
+ mod->priv = priv;
+
+ return 0;
+}
+
+void rsnd_mod_quit(struct rsnd_mod *mod)
+{
+ clk_unprepare(mod->clk);
+ mod->clk = NULL;
+}
+
+void rsnd_mod_interrupt(struct rsnd_mod *mod,
+ void (*callback)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io))
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_dai *rdai;
+ int i;
+
+ for_each_rsnd_dai(rdai, priv, i) {
+ struct rsnd_dai_stream *io = &rdai->playback;
+
+ if (mod == io->mod[mod->type])
+ callback(mod, io);
+
+ io = &rdai->capture;
+ if (mod == io->mod[mod->type])
+ callback(mod, io);
+ }
+}
+
+int rsnd_io_is_working(struct rsnd_dai_stream *io)
+{
+ /* see rsnd_dai_stream_init/quit() */
+ if (io->substream)
+ return snd_pcm_running(io->substream);
+
+ return 0;
+}
+
+int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+ /*
+ * params will be added when refine
+ * see
+ * __rsnd_soc_hw_rule_rate()
+ * __rsnd_soc_hw_rule_channels()
+ */
+ if (params)
+ return params_channels(params);
+ else if (runtime)
+ return runtime->channels;
+ return 0;
+}
+
+int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io,
+ struct snd_pcm_hw_params *params)
+{
+ int chan = rsnd_runtime_channel_original_with_params(io, params);
+ struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io);
+
+ if (ctu_mod) {
+ u32 converted_chan = rsnd_io_converted_chan(io);
+
+ /*
+ * !! Note !!
+ *
+ * converted_chan will be used for CTU,
+ * or TDM Split mode.
+ * User shouldn't use CTU with TDM Split mode.
+ */
+ if (rsnd_runtime_is_tdm_split(io)) {
+ struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));
+
+ dev_err(dev, "CTU and TDM Split should be used\n");
+ }
+
+ if (converted_chan)
+ return converted_chan;
+ }
+
+ return chan;
+}
+
+int rsnd_channel_normalization(int chan)
+{
+ if (WARN_ON((chan > 8) || (chan < 0)))
+ return 0;
+
+ /* TDM Extend Mode needs 8ch */
+ if (chan == 6)
+ chan = 8;
+
+ return chan;
+}
+
+int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io,
+ struct snd_pcm_hw_params *params)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ int chan = rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu_with_params(io, params) :
+ rsnd_runtime_channel_original_with_params(io, params);
+
+ /* Use Multi SSI */
+ if (rsnd_runtime_is_multi_ssi(io))
+ chan /= rsnd_rdai_ssi_lane_get(rdai);
+
+ return rsnd_channel_normalization(chan);
+}
+
+int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ int lane = rsnd_rdai_ssi_lane_get(rdai);
+ int chan = rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io);
+
+ return (chan > 2) && (lane > 1);
+}
+
+int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io)
+{
+ return rsnd_runtime_channel_for_ssi(io) >= 6;
+}
+
+int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io)
+{
+ return !!rsnd_flags_has(io, RSND_STREAM_TDM_SPLIT);
+}
+
+/*
+ * ADINR function
+ */
+u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ switch (snd_pcm_format_width(runtime->format)) {
+ case 8:
+ return 16 << 16;
+ case 16:
+ return 8 << 16;
+ case 24:
+ return 0 << 16;
+ }
+
+ dev_warn(dev, "not supported sample bits\n");
+
+ return 0;
+}
+
+/*
+ * DALIGN function
+ */
+u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+{
+ static const u32 dalign_values[8] = {
+ 0x76543210, 0x00000032, 0x00007654, 0x00000076,
+ 0xfedcba98, 0x000000ba, 0x0000fedc, 0x000000fe,
+ };
+ int id = 0;
+ struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
+ struct rsnd_mod *target;
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ u32 dalign;
+
+ /*
+ * *Hardware* L/R and *Software* L/R are inverted for 16bit data.
+ * 31..16 15...0
+ * HW: [L ch] [R ch]
+ * SW: [R ch] [L ch]
+ * We need to care about inversion timing to control
+ * Playback/Capture correctly.
+ * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R
+ *
+ * sL/R : software L/R
+ * hL/R : hardware L/R
+ * (*) : conversion timing
+ *
+ * Playback
+ * sL/R (*) hL/R hL/R hL/R hL/R hL/R
+ * [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec
+ *
+ * Capture
+ * hL/R hL/R hL/R hL/R hL/R (*) sL/R
+ * codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM]
+ */
+ if (rsnd_io_is_play(io)) {
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
+ target = src ? src : ssiu;
+ } else {
+ struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
+
+ target = cmd ? cmd : ssiu;
+ }
+
+ if (mod == ssiu)
+ id = rsnd_mod_id_sub(mod);
+
+ dalign = dalign_values[id];
+
+ if (mod == target && snd_pcm_format_width(runtime->format) == 16) {
+ /* Target mod needs inverted DALIGN when 16bit */
+ dalign = (dalign & 0xf0f0f0f0) >> 4 |
+ (dalign & 0x0f0f0f0f) << 4;
+ }
+
+ return dalign;
+}
+
+u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
+{
+ static const enum rsnd_mod_type playback_mods[] = {
+ RSND_MOD_SRC,
+ RSND_MOD_CMD,
+ RSND_MOD_SSIU,
+ };
+ static const enum rsnd_mod_type capture_mods[] = {
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ RSND_MOD_SSIU,
+ };
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_mod *tmod = NULL;
+ const enum rsnd_mod_type *mods =
+ rsnd_io_is_play(io) ?
+ playback_mods : capture_mods;
+ int i;
+
+ /*
+ * This is needed for 24bit data
+ * We need to shift 8bit
+ *
+ * Linux 24bit data is located as 0x00******
+ * HW 24bit data is located as 0x******00
+ *
+ */
+ if (snd_pcm_format_width(runtime->format) != 24)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(playback_mods); i++) {
+ tmod = rsnd_io_to_mod(io, mods[i]);
+ if (tmod)
+ break;
+ }
+
+ if (tmod != mod)
+ return 0;
+
+ if (rsnd_io_is_play(io))
+ return (0 << 20) | /* shift to Left */
+ (8 << 16); /* 8bit */
+ else
+ return (1 << 20) | /* shift to Right */
+ (8 << 16); /* 8bit */
+}
+
+/*
+ * rsnd_dai functions
+ */
+struct rsnd_mod *rsnd_mod_next(int *iterator,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type *array,
+ int array_size)
+{
+ int max = array ? array_size : RSND_MOD_MAX;
+
+ for (; *iterator < max; (*iterator)++) {
+ enum rsnd_mod_type type = (array) ? array[*iterator] : *iterator;
+ struct rsnd_mod *mod = rsnd_io_to_mod(io, type);
+
+ if (mod)
+ return mod;
+ }
+
+ return NULL;
+}
+
+static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
+ {
+ /* CAPTURE */
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ RSND_MOD_SSIU,
+ RSND_MOD_SSIM3,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIP,
+ RSND_MOD_SSI,
+ }, {
+ /* PLAYBACK */
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_SSIM3,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIP,
+ RSND_MOD_SSI,
+ RSND_MOD_SSIU,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ },
+};
+
+static int rsnd_status_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod, enum rsnd_mod_type type,
+ int shift, int add, int timing)
+{
+ u32 *status = mod->ops->get_status(mod, io, type);
+ u32 mask = 0xF << shift;
+ u8 val = (*status >> shift) & 0xF;
+ u8 next_val = (val + add) & 0xF;
+ int func_call = (val == timing);
+
+ /* no status update */
+ if (add == 0 || shift == 28)
+ return 1;
+
+ if (next_val == 0xF) /* underflow case */
+ func_call = -1;
+ else
+ *status = (*status & ~mask) + (next_val << shift);
+
+ return func_call;
+}
+
+#define rsnd_dai_call(fn, io, param...) \
+({ \
+ struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \
+ struct rsnd_mod *mod; \
+ int is_play = rsnd_io_is_play(io); \
+ int ret = 0, i; \
+ enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \
+ for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \
+ int tmp = 0; \
+ int func_call = rsnd_status_update(io, mod, types[i], \
+ __rsnd_mod_shift_##fn, \
+ __rsnd_mod_add_##fn, \
+ __rsnd_mod_call_##fn); \
+ if (func_call > 0 && (mod)->ops->fn) \
+ tmp = (mod)->ops->fn(mod, io, param); \
+ if (unlikely(func_call < 0) || \
+ unlikely(tmp && (tmp != -EPROBE_DEFER))) \
+ dev_err(dev, "%s : %s error (%d, %d)\n", \
+ rsnd_mod_name(mod), #fn, tmp, func_call);\
+ ret |= tmp; \
+ } \
+ ret; \
+})
+
+int rsnd_dai_connect(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type)
+{
+ struct rsnd_priv *priv;
+ struct device *dev;
+
+ if (!mod)
+ return -EIO;
+
+ if (io->mod[type] == mod)
+ return 0;
+
+ if (io->mod[type])
+ return -EINVAL;
+
+ priv = rsnd_mod_to_priv(mod);
+ dev = rsnd_priv_to_dev(priv);
+
+ io->mod[type] = mod;
+
+ dev_dbg(dev, "%s is connected to io (%s)\n",
+ rsnd_mod_name(mod),
+ rsnd_io_is_play(io) ? "Playback" : "Capture");
+
+ return 0;
+}
+
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type)
+{
+ io->mod[type] = NULL;
+}
+
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
+ int max_channels)
+{
+ if (max_channels > 0)
+ rdai->max_channels = max_channels;
+
+ return rdai->max_channels;
+}
+
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
+ int ssi_lane)
+{
+ if (ssi_lane > 0)
+ rdai->ssi_lane = ssi_lane;
+
+ return rdai->ssi_lane;
+}
+
+int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width)
+{
+ if (width > 0)
+ rdai->chan_width = width;
+
+ return rdai->chan_width;
+}
+
+struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
+{
+ if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
+ return NULL;
+
+ return priv->rdai + id;
+}
+
+static struct snd_soc_dai_driver
+*rsnd_daidrv_get(struct rsnd_priv *priv, int id)
+{
+ if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
+ return NULL;
+
+ return priv->daidrv + id;
+}
+
+#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
+static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
+{
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+
+ return rsnd_rdai_get(priv, dai->id);
+}
+
+static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream)
+{
+ io->substream = substream;
+}
+
+static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
+{
+ io->substream = NULL;
+}
+
+static
+struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ return snd_soc_rtd_to_cpu(rtd, 0);
+}
+
+static
+struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
+ struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return &rdai->playback;
+ else
+ return &rdai->capture;
+}
+
+static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ int ret;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = rsnd_dai_call(init, io, priv);
+ if (ret < 0)
+ break;
+
+ ret = rsnd_dai_call(start, io, priv);
+ if (ret < 0)
+ break;
+
+ ret = rsnd_dai_call(irq, io, priv, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = rsnd_dai_call(irq, io, priv, 0);
+
+ ret |= rsnd_dai_call(stop, io, priv);
+
+ ret |= rsnd_dai_call(quit, io, priv);
+
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+
+ /* set clock master for audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ rdai->clk_master = 0;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ rdai->clk_master = 1; /* cpu is master */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set format */
+ rdai->bit_clk_inv = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ rdai->sys_delay = 0;
+ rdai->data_alignment = 0;
+ rdai->frm_clk_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ rdai->sys_delay = 1;
+ rdai->data_alignment = 0;
+ rdai->frm_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ rdai->sys_delay = 1;
+ rdai->data_alignment = 1;
+ rdai->frm_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ rdai->sys_delay = 0;
+ rdai->data_alignment = 0;
+ rdai->frm_clk_inv = 1;
+ break;
+ }
+
+ /* set clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ rdai->frm_clk_inv = !rdai->frm_clk_inv;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ rdai->bit_clk_inv = !rdai->bit_clk_inv;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ rdai->bit_clk_inv = !rdai->bit_clk_inv;
+ rdai->frm_clk_inv = !rdai->frm_clk_inv;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ switch (slot_width) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ /* use default */
+ /*
+ * Indicate warning if DT has "dai-tdm-slot-width"
+ * but the value was not expected.
+ */
+ if (slot_width)
+ dev_warn(dev, "unsupported TDM slot width (%d), force to use default 32\n",
+ slot_width);
+ slot_width = 32;
+ }
+
+ switch (slots) {
+ case 2:
+ /* TDM Split Mode */
+ case 6:
+ case 8:
+ /* TDM Extend Mode */
+ rsnd_rdai_channels_set(rdai, slots);
+ rsnd_rdai_ssi_lane_set(rdai, 1);
+ rsnd_rdai_width_set(rdai, slot_width);
+ break;
+ default:
+ dev_err(dev, "unsupported TDM slots (%d)\n", slots);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int rsnd_soc_hw_channels_list[] = {
+ 2, 6, 8,
+};
+
+static unsigned int rsnd_soc_hw_rate_list[] = {
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+static int rsnd_soc_hw_rule(struct rsnd_dai *rdai,
+ unsigned int *list, int list_num,
+ struct snd_interval *baseline, struct snd_interval *iv,
+ struct rsnd_dai_stream *io, char *unit)
+{
+ struct snd_interval p;
+ unsigned int rate;
+ int i;
+
+ snd_interval_any(&p);
+ p.min = UINT_MAX;
+ p.max = 0;
+
+ for (i = 0; i < list_num; i++) {
+
+ if (!snd_interval_test(iv, list[i]))
+ continue;
+
+ rate = rsnd_ssi_clk_query(rdai,
+ baseline->min, list[i], NULL);
+ if (rate > 0) {
+ p.min = min(p.min, list[i]);
+ p.max = max(p.max, list[i]);
+ }
+
+ rate = rsnd_ssi_clk_query(rdai,
+ baseline->max, list[i], NULL);
+ if (rate > 0) {
+ p.min = min(p.min, list[i]);
+ p.max = max(p.max, list[i]);
+ }
+ }
+
+ /* Indicate error once if it can't handle */
+ if (!rsnd_flags_has(io, RSND_HW_RULE_ERR) && (p.min > p.max)) {
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "It can't handle %d %s <-> %d %s\n",
+ baseline->min, unit, baseline->max, unit);
+ rsnd_flags_set(io, RSND_HW_RULE_ERR);
+ }
+
+ return snd_interval_refine(iv, &p);
+}
+
+static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval ic;
+ struct rsnd_dai_stream *io = rule->private;
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+
+ /*
+ * possible sampling rate limitation is same as
+ * 2ch if it supports multi ssi
+ * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init())
+ */
+ ic = *ic_;
+ ic.min =
+ ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params);
+
+ return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_rate_list,
+ ARRAY_SIZE(rsnd_soc_hw_rate_list),
+ &ic, ir, io, "ch");
+}
+
+static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval ic;
+ struct rsnd_dai_stream *io = rule->private;
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+
+ /*
+ * possible sampling rate limitation is same as
+ * 2ch if it supports multi ssi
+ * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init())
+ */
+ ic = *ic_;
+ ic.min =
+ ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params);
+
+ return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_channels_list,
+ ARRAY_SIZE(rsnd_soc_hw_channels_list),
+ ir, &ic, io, "Hz");
+}
+
+static const struct snd_pcm_hardware rsnd_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 256,
+};
+
+static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int max_channels = rsnd_rdai_channels_get(rdai);
+ int i;
+
+ rsnd_flags_del(io, RSND_HW_RULE_ERR);
+
+ rsnd_dai_stream_init(io, substream);
+
+ /*
+ * Channel Limitation
+ * It depends on Platform design
+ */
+ constraint->list = rsnd_soc_hw_channels_list;
+ constraint->count = 0;
+ constraint->mask = 0;
+
+ for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) {
+ if (rsnd_soc_hw_channels_list[i] > max_channels)
+ break;
+ constraint->count = i + 1;
+ }
+
+ snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware);
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
+
+ snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /*
+ * Sampling Rate / Channel Limitation
+ * It depends on Clock Master Mode
+ */
+ if (rsnd_rdai_is_clk_master(rdai)) {
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ rsnd_soc_hw_rule_rate,
+ is_play ? &rdai->playback : &rdai->capture,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ rsnd_soc_hw_rule_channels,
+ is_play ? &rdai->playback : &rdai->capture,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ }
+
+ return 0;
+}
+
+static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+ /*
+ * call rsnd_dai_call without spinlock
+ */
+ rsnd_dai_call(cleanup, io, priv);
+
+ rsnd_dai_stream_quit(io);
+}
+
+static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+ return rsnd_dai_call(prepare, io, priv);
+}
+
+static const u64 rsnd_soc_dai_formats[] = {
+ /*
+ * 1st Priority
+ *
+ * Well tested formats.
+ * Select below from Sound Card, not auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /*
+ * 2nd Priority
+ *
+ * Supported, but not well tested
+ */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ struct device_node *dai_np)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *ssiu_np = rsnd_ssiu_of_node(priv);
+ int is_play = rsnd_io_is_play(io);
+ int i;
+
+ if (!ssiu_np)
+ return;
+
+ /*
+ * This driver assumes that it is TDM Split mode
+ * if it includes ssiu node
+ */
+ for (i = 0;; i++) {
+ struct device_node *node = is_play ?
+ of_parse_phandle(dai_np, "playback", i) :
+ of_parse_phandle(dai_np, "capture", i);
+
+ if (!node)
+ break;
+
+ for_each_child_of_node_scoped(ssiu_np, np) {
+ if (np == node) {
+ rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT);
+ dev_dbg(dev, "%s is part of TDM Split\n", io->name);
+ }
+ }
+
+ of_node_put(node);
+ }
+
+ of_node_put(ssiu_np);
+}
+
+static void rsnd_parse_connect_simple(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ struct device_node *dai_np)
+{
+ if (!rsnd_io_to_mod_ssi(io))
+ return;
+
+ rsnd_parse_tdm_split_mode(priv, io, dai_np);
+}
+
+static void rsnd_parse_connect_graph(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ struct device_node *endpoint)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *remote_node;
+
+ if (!rsnd_io_to_mod_ssi(io))
+ return;
+
+ remote_node = of_graph_get_remote_port_parent(endpoint);
+
+ /* HDMI0 */
+ if (strstr(remote_node->full_name, "hdmi@fead0000")) {
+ rsnd_flags_set(io, RSND_STREAM_HDMI0);
+ dev_dbg(dev, "%s connected to HDMI0\n", io->name);
+ }
+
+ /* HDMI1 */
+ if (strstr(remote_node->full_name, "hdmi@feae0000")) {
+ rsnd_flags_set(io, RSND_STREAM_HDMI1);
+ dev_dbg(dev, "%s connected to HDMI1\n", io->name);
+ }
+
+ rsnd_parse_tdm_split_mode(priv, io, endpoint);
+
+ of_node_put(remote_node);
+}
+
+void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
+ struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
+ struct device_node *node,
+ struct device_node *playback,
+ struct device_node *capture)
+{
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int i;
+
+ if (!node)
+ return;
+
+ i = 0;
+ for_each_child_of_node_scoped(node, np) {
+ struct rsnd_mod *mod;
+
+ i = rsnd_node_fixed_index(dev, np, name, i);
+ if (i < 0)
+ break;
+
+ mod = mod_get(priv, i);
+
+ if (np == playback)
+ rsnd_dai_connect(mod, &rdai->playback, mod->type);
+ if (np == capture)
+ rsnd_dai_connect(mod, &rdai->capture, mod->type);
+ i++;
+ }
+
+ of_node_put(node);
+}
+
+int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx)
+{
+ char node_name[16];
+
+ /*
+ * rsnd is assuming each device nodes are sequential numbering,
+ * but some of them are not.
+ * This function adjusts index for it.
+ *
+ * ex)
+ * Normal case, special case
+ * ssi-0
+ * ssi-1
+ * ssi-2
+ * ssi-3 ssi-3
+ * ssi-4 ssi-4
+ * ...
+ *
+ * assume Max 64 node
+ */
+ for (; idx < 64; idx++) {
+ snprintf(node_name, sizeof(node_name), "%s-%d", name, idx);
+
+ if (strncmp(node_name, of_node_full_name(node), sizeof(node_name)) == 0)
+ return idx;
+ }
+
+ dev_err(dev, "strange node numbering (%s)",
+ of_node_full_name(node));
+ return -EINVAL;
+}
+
+int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int i;
+
+ i = 0;
+ for_each_child_of_node_scoped(node, np) {
+ i = rsnd_node_fixed_index(dev, np, name, i);
+ if (i < 0)
+ return 0;
+ i++;
+ }
+
+ return i;
+}
+
+static struct device_node*
+ rsnd_pick_endpoint_node_for_ports(struct device_node *e_ports,
+ struct device_node *e_port)
+{
+ if (of_node_name_eq(e_ports, "ports"))
+ return e_ports;
+
+ if (of_node_name_eq(e_ports, "port"))
+ return e_port;
+
+ return NULL;
+}
+
+static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+ struct device_node *node;
+ int nr = 0;
+ int i = 0;
+
+ *is_graph = 0;
+
+ /*
+ * parse both previous dai (= rcar_sound,dai), and
+ * graph dai (= ports/port)
+ */
+
+ /*
+ * Simple-Card
+ */
+ node = of_get_child_by_name(np, RSND_NODE_DAI);
+ if (!node)
+ goto audio_graph;
+
+ of_node_put(node);
+
+ for_each_child_of_node_scoped(np, node) {
+ if (!of_node_name_eq(node, RSND_NODE_DAI))
+ continue;
+
+ priv->component_dais[i] = of_get_child_count(node);
+ nr += priv->component_dais[i];
+ i++;
+ if (i >= RSND_MAX_COMPONENT) {
+ dev_info(dev, "reach to max component\n");
+ break;
+ }
+ }
+
+ return nr;
+
+audio_graph:
+ /*
+ * Audio-Graph-Card
+ */
+ for_each_child_of_node_scoped(np, ports) {
+ node = rsnd_pick_endpoint_node_for_ports(ports, np);
+ if (!node)
+ continue;
+ priv->component_dais[i] = of_graph_get_endpoint_count(node);
+ nr += priv->component_dais[i];
+ i++;
+ if (i >= RSND_MAX_COMPONENT) {
+ dev_info(dev, "reach to max component\n");
+ break;
+ }
+ }
+
+ *is_graph = 1;
+
+ return nr;
+}
+
+
+#define PREALLOC_BUFFER (32 * 1024)
+#define PREALLOC_BUFFER_MAX (32 * 1024)
+
+static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd,
+ struct rsnd_dai_stream *io,
+ int stream)
+{
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct snd_pcm_substream *substream;
+
+ /*
+ * use Audio-DMAC dev if we can use IPMMU
+ * see
+ * rsnd_dmaen_attach()
+ */
+ if (io->dmac_dev)
+ dev = io->dmac_dev;
+
+ for (substream = rtd->pcm->streams[stream].substream;
+ substream;
+ substream = substream->next) {
+ snd_pcm_set_managed_buffer(substream,
+ SNDRV_DMA_TYPE_DEV,
+ dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+ }
+
+ return 0;
+}
+
+static int rsnd_soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ int ret;
+
+ ret = rsnd_dai_call(pcm_new, &rdai->playback, rtd);
+ if (ret)
+ return ret;
+
+ ret = rsnd_dai_call(pcm_new, &rdai->capture, rtd);
+ if (ret)
+ return ret;
+
+ ret = rsnd_preallocate_pages(rtd, &rdai->playback,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+
+ ret = rsnd_preallocate_pages(rtd, &rdai->capture,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+ .pcm_new = rsnd_soc_dai_pcm_new,
+ .startup = rsnd_soc_dai_startup,
+ .shutdown = rsnd_soc_dai_shutdown,
+ .trigger = rsnd_soc_dai_trigger,
+ .set_fmt = rsnd_soc_dai_set_fmt,
+ .set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
+ .prepare = rsnd_soc_dai_prepare,
+ .auto_selectable_formats = rsnd_soc_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats),
+};
+
+static void __rsnd_dai_probe(struct rsnd_priv *priv,
+ struct device_node *dai_np,
+ struct device_node *node_np,
+ uint32_t node_arg,
+ int dai_i)
+{
+ struct rsnd_dai_stream *io_playback;
+ struct rsnd_dai_stream *io_capture;
+ struct snd_soc_dai_driver *drv;
+ struct rsnd_dai *rdai;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int playback_exist = 0, capture_exist = 0;
+ int io_i;
+
+ rdai = rsnd_rdai_get(priv, dai_i);
+ drv = rsnd_daidrv_get(priv, dai_i);
+ io_playback = &rdai->playback;
+ io_capture = &rdai->capture;
+
+ snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
+
+ /* for multi Component */
+ rdai->dai_args.np = node_np;
+ rdai->dai_args.args_count = 1;
+ rdai->dai_args.args[0] = node_arg;
+
+ rdai->priv = priv;
+ drv->name = rdai->name;
+ drv->ops = &rsnd_soc_dai_ops;
+ drv->id = dai_i;
+ drv->dai_args = &rdai->dai_args;
+
+ io_playback->rdai = rdai;
+ io_capture->rdai = rdai;
+ rsnd_rdai_channels_set(rdai, 2); /* default 2ch */
+ rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */
+ rsnd_rdai_width_set(rdai, 32); /* default 32bit width */
+
+ for (io_i = 0;; io_i++) {
+ struct device_node *playback = of_parse_phandle(dai_np, "playback", io_i);
+ struct device_node *capture = of_parse_phandle(dai_np, "capture", io_i);
+
+ if (!playback && !capture)
+ break;
+
+ if (io_i == 0) {
+ /* check whether playback/capture property exists */
+ if (playback)
+ playback_exist = 1;
+ if (capture)
+ capture_exist = 1;
+ }
+
+ rsnd_parse_connect_ssi(rdai, playback, capture);
+ rsnd_parse_connect_ssiu(rdai, playback, capture);
+ rsnd_parse_connect_src(rdai, playback, capture);
+ rsnd_parse_connect_ctu(rdai, playback, capture);
+ rsnd_parse_connect_mix(rdai, playback, capture);
+ rsnd_parse_connect_dvc(rdai, playback, capture);
+
+ of_node_put(playback);
+ of_node_put(capture);
+ }
+
+ if (playback_exist) {
+ snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i);
+ drv->playback.rates = RSND_RATES;
+ drv->playback.formats = RSND_FMTS;
+ drv->playback.channels_min = 2;
+ drv->playback.channels_max = 8;
+ drv->playback.stream_name = io_playback->name;
+ }
+ if (capture_exist) {
+ snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i);
+ drv->capture.rates = RSND_RATES;
+ drv->capture.formats = RSND_FMTS;
+ drv->capture.channels_min = 2;
+ drv->capture.channels_max = 8;
+ drv->capture.stream_name = io_capture->name;
+ }
+
+ if (rsnd_ssi_is_pin_sharing(io_capture) ||
+ rsnd_ssi_is_pin_sharing(io_playback)) {
+ /* should have symmetric_rate if pin sharing */
+ drv->symmetric_rate = 1;
+ }
+
+ dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
+ rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
+ rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
+}
+
+static int rsnd_dai_probe(struct rsnd_priv *priv)
+{
+ struct snd_soc_dai_driver *rdrv;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+ struct rsnd_dai *rdai;
+ int nr = 0;
+ int is_graph;
+ int dai_i;
+
+ nr = rsnd_dai_of_node(priv, &is_graph);
+
+ /*
+ * There is a case that it is used only for ADG (Sound Clock).
+ * No DAI is not error
+ */
+ if (!nr)
+ return 0;
+
+ rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL);
+ rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL);
+ if (!rdrv || !rdai)
+ return -ENOMEM;
+
+ priv->rdai_nr = nr;
+ priv->daidrv = rdrv;
+ priv->rdai = rdai;
+
+ /*
+ * parse all dai
+ */
+ dai_i = 0;
+ if (is_graph) {
+ struct device_node *dai_np_port;
+ struct device_node *dai_np;
+
+ for_each_child_of_node_scoped(np, ports) {
+ dai_np_port = rsnd_pick_endpoint_node_for_ports(ports, np);
+ if (!dai_np_port)
+ continue;
+
+ for_each_endpoint_of_node(dai_np_port, dai_np) {
+ __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i);
+ if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) {
+ rdai = rsnd_rdai_get(priv, dai_i);
+
+ rsnd_parse_connect_graph(priv, &rdai->playback, dai_np);
+ rsnd_parse_connect_graph(priv, &rdai->capture, dai_np);
+ }
+ dai_i++;
+ }
+ }
+ } else {
+ for_each_child_of_node_scoped(np, node) {
+ if (!of_node_name_eq(node, RSND_NODE_DAI))
+ continue;
+
+ for_each_child_of_node_scoped(node, dai_np) {
+ __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i);
+ if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) {
+ rdai = rsnd_rdai_get(priv, dai_i);
+
+ rsnd_parse_connect_simple(priv, &rdai->playback, dai_np);
+ rsnd_parse_connect_simple(priv, &rdai->capture, dai_np);
+ }
+ dai_i++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * pcm ops
+ */
+static int rsnd_hw_update(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ int ret;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ if (hw_params)
+ ret = rsnd_dai_call(hw_params, io, substream, hw_params);
+ else
+ ret = rsnd_dai_call(hw_free, io, substream);
+
+ return ret;
+}
+
+static int rsnd_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream);
+
+ /*
+ * rsnd assumes that it might be used under DPCM if user want to use
+ * channel / rate convert. Then, rsnd should be FE.
+ * And then, this function will be called *after* BE settings.
+ * this means, each BE already has fixuped hw_params.
+ * see
+ * dpcm_fe_dai_hw_params()
+ * dpcm_be_dai_hw_params()
+ */
+ io->converted_rate = 0;
+ io->converted_chan = 0;
+ if (fe->dai_link->dynamic) {
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct snd_soc_dpcm *dpcm;
+ int stream = substream->stream;
+
+ for_each_dpcm_be(fe, stream, dpcm) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_hw_params *be_params = &be->dpcm[stream].hw_params;
+
+ if (params_channels(hw_params) != params_channels(be_params))
+ io->converted_chan = params_channels(be_params);
+ if (params_rate(hw_params) != params_rate(be_params))
+ io->converted_rate = params_rate(be_params);
+ }
+ if (io->converted_chan)
+ dev_dbg(dev, "convert channels = %d\n", io->converted_chan);
+ if (io->converted_rate) {
+ /*
+ * SRC supports convert rates from params_rate(hw_params)/k_down
+ * to params_rate(hw_params)*k_up, where k_up is always 6, and
+ * k_down depends on number of channels and SRC unit.
+ * So all SRC units can upsample audio up to 6 times regardless
+ * its number of channels. And all SRC units can downsample
+ * 2 channel audio up to 6 times too.
+ */
+ int k_up = 6;
+ int k_down = 6;
+ int channel;
+ struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+
+ dev_dbg(dev, "convert rate = %d\n", io->converted_rate);
+
+ channel = io->converted_chan ? io->converted_chan :
+ params_channels(hw_params);
+
+ switch (rsnd_mod_id(src_mod)) {
+ /*
+ * SRC0 can downsample 4, 6 and 8 channel audio up to 4 times.
+ * SRC1, SRC3 and SRC4 can downsample 4 channel audio
+ * up to 4 times.
+ * SRC1, SRC3 and SRC4 can downsample 6 and 8 channel audio
+ * no more than twice.
+ */
+ case 1:
+ case 3:
+ case 4:
+ if (channel > 4) {
+ k_down = 2;
+ break;
+ }
+ fallthrough;
+ case 0:
+ if (channel > 2)
+ k_down = 4;
+ break;
+
+ /* Other SRC units do not support more than 2 channels */
+ default:
+ if (channel > 2)
+ return -EINVAL;
+ }
+
+ if (params_rate(hw_params) > io->converted_rate * k_down) {
+ hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min =
+ io->converted_rate * k_down;
+ hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max =
+ io->converted_rate * k_down;
+ hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE;
+ } else if (params_rate(hw_params) * k_up < io->converted_rate) {
+ hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min =
+ DIV_ROUND_UP(io->converted_rate, k_up);
+ hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max =
+ DIV_ROUND_UP(io->converted_rate, k_up);
+ hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE;
+ }
+
+ /*
+ * TBD: Max SRC input and output rates also depend on number
+ * of channels and SRC unit:
+ * SRC1, SRC3 and SRC4 do not support more than 128kHz
+ * for 6 channel and 96kHz for 8 channel audio.
+ * Perhaps this function should return EINVAL if the input or
+ * the output rate exceeds the limitation.
+ */
+ }
+ }
+
+ return rsnd_hw_update(substream, hw_params);
+}
+
+static int rsnd_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return rsnd_hw_update(substream, NULL);
+}
+
+static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ snd_pcm_uframes_t pointer = 0;
+
+ rsnd_dai_call(pointer, io, &pointer);
+
+ return pointer;
+}
+
+/*
+ * snd_kcontrol
+ */
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl);
+
+ if (cfg->texts) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = cfg->size;
+ uinfo->value.enumerated.items = cfg->max;
+ if (uinfo->value.enumerated.item >= cfg->max)
+ uinfo->value.enumerated.item = cfg->max - 1;
+ strscpy(uinfo->value.enumerated.name,
+ cfg->texts[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+ } else {
+ uinfo->count = cfg->size;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = cfg->max;
+ uinfo->type = (cfg->max == 1) ?
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+ SNDRV_CTL_ELEM_TYPE_INTEGER;
+ }
+
+ return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *uc)
+{
+ struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl);
+ int i;
+
+ for (i = 0; i < cfg->size; i++)
+ if (cfg->texts)
+ uc->value.enumerated.item[i] = cfg->val[i];
+ else
+ uc->value.integer.value[i] = cfg->val[i];
+
+ return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *uc)
+{
+ struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl);
+ int i, change = 0;
+
+ if (!cfg->accept(cfg->io))
+ return 0;
+
+ for (i = 0; i < cfg->size; i++) {
+ if (cfg->texts) {
+ change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+ cfg->val[i] = uc->value.enumerated.item[i];
+ } else {
+ change |= (uc->value.integer.value[i] != cfg->val[i]);
+ cfg->val[i] = uc->value.integer.value[i];
+ }
+ }
+
+ if (change && cfg->update)
+ cfg->update(cfg->io, cfg->mod);
+
+ return change;
+}
+
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io)
+{
+ return 1;
+}
+
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
+{
+ cfg->cfg.val = cfg->val;
+
+ return &cfg->cfg;
+}
+
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg)
+{
+ cfg->cfg.val = &cfg->val;
+
+ return &cfg->cfg;
+}
+
+const char * const volume_ramp_rate[] = {
+ "128 dB/1 step", /* 00000 */
+ "64 dB/1 step", /* 00001 */
+ "32 dB/1 step", /* 00010 */
+ "16 dB/1 step", /* 00011 */
+ "8 dB/1 step", /* 00100 */
+ "4 dB/1 step", /* 00101 */
+ "2 dB/1 step", /* 00110 */
+ "1 dB/1 step", /* 00111 */
+ "0.5 dB/1 step", /* 01000 */
+ "0.25 dB/1 step", /* 01001 */
+ "0.125 dB/1 step", /* 01010 = VOLUME_RAMP_MAX_MIX */
+ "0.125 dB/2 steps", /* 01011 */
+ "0.125 dB/4 steps", /* 01100 */
+ "0.125 dB/8 steps", /* 01101 */
+ "0.125 dB/16 steps", /* 01110 */
+ "0.125 dB/32 steps", /* 01111 */
+ "0.125 dB/64 steps", /* 10000 */
+ "0.125 dB/128 steps", /* 10001 */
+ "0.125 dB/256 steps", /* 10010 */
+ "0.125 dB/512 steps", /* 10011 */
+ "0.125 dB/1024 steps", /* 10100 */
+ "0.125 dB/2048 steps", /* 10101 */
+ "0.125 dB/4096 steps", /* 10110 */
+ "0.125 dB/8192 steps", /* 10111 = VOLUME_RAMP_MAX_DVC */
+};
+
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ int (*accept)(struct rsnd_dai_stream *io),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg *cfg,
+ const char * const *texts,
+ int size,
+ u32 max)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_kcontrol *kctrl;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = name,
+ .info = rsnd_kctrl_info,
+ .index = rtd->id,
+ .get = rsnd_kctrl_get,
+ .put = rsnd_kctrl_put,
+ };
+ int ret;
+
+ /*
+ * 1) Avoid duplicate register for DVC with MIX case
+ * 2) Allow duplicate register for MIX
+ * 3) re-register if card was rebinded
+ */
+ list_for_each_entry(kctrl, &card->controls, list) {
+ struct rsnd_kctrl_cfg *c = kctrl->private_data;
+
+ if (c == cfg)
+ return 0;
+ }
+
+ if (size > RSND_MAX_CHANNELS)
+ return -EINVAL;
+
+ kctrl = snd_ctl_new1(&knew, cfg);
+ if (!kctrl)
+ return -ENOMEM;
+
+ ret = snd_ctl_add(card, kctrl);
+ if (ret < 0)
+ return ret;
+
+ cfg->texts = texts;
+ cfg->max = max;
+ cfg->size = size;
+ cfg->accept = accept;
+ cfg->update = update;
+ cfg->card = card;
+ cfg->kctrl = kctrl;
+ cfg->io = io;
+ cfg->mod = mod;
+
+ return 0;
+}
+
+/*
+ * snd_soc_component
+ */
+static const struct snd_soc_component_driver rsnd_soc_component = {
+ .name = "rsnd",
+ .probe = rsnd_debugfs_probe,
+ .hw_params = rsnd_hw_params,
+ .hw_free = rsnd_hw_free,
+ .pointer = rsnd_pointer,
+ .legacy_dai_naming = 1,
+};
+
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io)
+{
+ int ret;
+
+ ret = rsnd_dai_call(probe, io, priv);
+ if (ret == -EAGAIN) {
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *mod;
+ int i;
+
+ /*
+ * Fallback to PIO mode
+ */
+
+ /*
+ * call "remove" for SSI/SRC/DVC
+ * SSI will be switch to PIO mode if it was DMA mode
+ * see
+ * rsnd_dma_init()
+ * rsnd_ssi_fallback()
+ */
+ rsnd_dai_call(remove, io, priv);
+
+ /*
+ * remove all mod from io
+ * and, re connect ssi
+ */
+ for_each_rsnd_mod(i, mod, io)
+ rsnd_dai_disconnect(mod, io, i);
+ rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
+
+ /*
+ * fallback
+ */
+ rsnd_dai_call(fallback, io, priv);
+
+ /*
+ * retry to "probe".
+ * DAI has SSI which is PIO mode only now.
+ */
+ ret = rsnd_dai_call(probe, io, priv);
+ }
+
+ return ret;
+}
+
+/*
+ * rsnd probe
+ */
+static int rsnd_probe(struct platform_device *pdev)
+{
+ struct rsnd_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct rsnd_dai *rdai;
+ int (*probe_func[])(struct rsnd_priv *priv) = {
+ rsnd_gen_probe,
+ rsnd_dma_probe,
+ rsnd_ssi_probe,
+ rsnd_ssiu_probe,
+ rsnd_src_probe,
+ rsnd_ctu_probe,
+ rsnd_mix_probe,
+ rsnd_dvc_probe,
+ rsnd_cmd_probe,
+ rsnd_adg_probe,
+ rsnd_dai_probe,
+ };
+ int ret, i;
+ int ci;
+
+ /*
+ * init priv data
+ */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENODEV;
+
+ priv->pdev = pdev;
+ priv->flags = (unsigned long)of_device_get_match_data(dev);
+ spin_lock_init(&priv->lock);
+
+ /*
+ * init each module
+ */
+ for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
+ ret = probe_func[i](priv);
+ if (ret)
+ return ret;
+ }
+
+ for_each_rsnd_dai(rdai, priv, i) {
+ ret = rsnd_rdai_continuance_probe(priv, &rdai->playback);
+ if (ret)
+ goto exit_snd_probe;
+
+ ret = rsnd_rdai_continuance_probe(priv, &rdai->capture);
+ if (ret)
+ goto exit_snd_probe;
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ /*
+ * asoc register
+ */
+ ci = 0;
+ for (i = 0; priv->component_dais[i] > 0; i++) {
+ int nr = priv->component_dais[i];
+
+ ret = devm_snd_soc_register_component(dev, &rsnd_soc_component,
+ priv->daidrv + ci, nr);
+ if (ret < 0) {
+ dev_err(dev, "cannot snd component register\n");
+ goto exit_snd_probe;
+ }
+
+ ci += nr;
+ }
+
+ pm_runtime_enable(dev);
+
+ dev_info(dev, "probed\n");
+ return ret;
+
+exit_snd_probe:
+ for_each_rsnd_dai(rdai, priv, i) {
+ rsnd_dai_call(remove, &rdai->playback, priv);
+ rsnd_dai_call(remove, &rdai->capture, priv);
+ }
+
+ /*
+ * adg is very special mod which can't use rsnd_dai_call(remove),
+ * and it registers ADG clock on probe.
+ * It should be unregister if probe failed.
+ * Mainly it is assuming -EPROBE_DEFER case
+ */
+ rsnd_adg_remove(priv);
+
+ return ret;
+}
+
+static void rsnd_remove(struct platform_device *pdev)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
+ struct rsnd_dai *rdai;
+ void (*remove_func[])(struct rsnd_priv *priv) = {
+ rsnd_ssi_remove,
+ rsnd_ssiu_remove,
+ rsnd_src_remove,
+ rsnd_ctu_remove,
+ rsnd_mix_remove,
+ rsnd_dvc_remove,
+ rsnd_cmd_remove,
+ rsnd_adg_remove,
+ };
+ int i;
+
+ pm_runtime_disable(&pdev->dev);
+
+ for_each_rsnd_dai(rdai, priv, i) {
+ int ret;
+
+ ret = rsnd_dai_call(remove, &rdai->playback, priv);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to remove playback dai #%d\n", i);
+
+ ret = rsnd_dai_call(remove, &rdai->capture, priv);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to remove capture dai #%d\n", i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(remove_func); i++)
+ remove_func[i](priv);
+}
+
+static int rsnd_suspend(struct device *dev)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+ rsnd_adg_clk_disable(priv);
+
+ return 0;
+}
+
+static int rsnd_resume(struct device *dev)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+ return rsnd_adg_clk_enable(priv);
+}
+
+static const struct dev_pm_ops rsnd_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume)
+};
+
+static struct platform_driver rsnd_driver = {
+ .driver = {
+ .name = "rcar_sound",
+ .pm = pm_ptr(&rsnd_pm_ops),
+ .of_match_table = rsnd_of_match,
+ },
+ .probe = rsnd_probe,
+ .remove = rsnd_remove,
+};
+module_platform_driver(rsnd_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_ALIAS("platform:rcar-pcm-audio");
diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c
new file mode 100644
index 000000000000..bd4c61f9fb3c
--- /dev/null
+++ b/sound/soc/renesas/rcar/ctu.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ctu.c
+//
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include "rsnd.h"
+
+#define CTU_NAME_SIZE 16
+#define CTU_NAME "ctu"
+
+/*
+ * User needs to setup CTU by amixer, and its settings are
+ * based on below registers
+ *
+ * CTUn_CPMDR : amixser set "CTU Pass"
+ * CTUn_SV0xR : amixser set "CTU SV0"
+ * CTUn_SV1xR : amixser set "CTU SV1"
+ * CTUn_SV2xR : amixser set "CTU SV2"
+ * CTUn_SV3xR : amixser set "CTU SV3"
+ *
+ * [CTU Pass]
+ * 0000: default
+ * 0001: Connect input data of channel 0
+ * 0010: Connect input data of channel 1
+ * 0011: Connect input data of channel 2
+ * 0100: Connect input data of channel 3
+ * 0101: Connect input data of channel 4
+ * 0110: Connect input data of channel 5
+ * 0111: Connect input data of channel 6
+ * 1000: Connect input data of channel 7
+ * 1001: Connect calculated data by scale values of matrix row 0
+ * 1010: Connect calculated data by scale values of matrix row 1
+ * 1011: Connect calculated data by scale values of matrix row 2
+ * 1100: Connect calculated data by scale values of matrix row 3
+ *
+ * [CTU SVx]
+ * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07]
+ * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17]
+ * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27]
+ * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37]
+ * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ *
+ * [SVxx]
+ * Plus Minus
+ * value time dB value time dB
+ * -----------------------------------------------------------------------
+ * H'7F_FFFF 2 6 H'80_0000 2 6
+ * ...
+ * H'40_0000 1 0 H'C0_0000 1 0
+ * ...
+ * H'00_0001 2.38 x 10^-7 -132
+ * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132
+ *
+ *
+ * Ex) Input ch -> Output ch
+ * 1ch -> 0ch
+ * 0ch -> 1ch
+ *
+ * amixer set "CTU Reset" on
+ * amixer set "CTU Pass" 9,10
+ * amixer set "CTU SV0" 0,4194304
+ * amixer set "CTU SV1" 4194304,0
+ * or
+ * amixer set "CTU Reset" on
+ * amixer set "CTU Pass" 2,1
+ */
+
+struct rsnd_ctu {
+ struct rsnd_mod mod;
+ struct rsnd_kctrl_cfg_m pass;
+ struct rsnd_kctrl_cfg_m sv[4];
+ struct rsnd_kctrl_cfg_s reset;
+ int channels;
+ u32 flags;
+};
+
+#define KCTRL_INITIALIZED (1 << 0)
+
+#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
+#define for_each_rsnd_ctu(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_ctu_nr(priv)) && \
+ ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
+ i++)
+
+#define rsnd_mod_to_ctu(_mod) \
+ container_of((_mod), struct rsnd_ctu, mod)
+
+#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
+
+static void rsnd_ctu_activation(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, CTU_SWRSR, 0);
+ rsnd_mod_write(mod, CTU_SWRSR, 1);
+}
+
+static void rsnd_ctu_halt(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, CTU_CTUIR, 1);
+ rsnd_mod_write(mod, CTU_SWRSR, 0);
+}
+
+static int rsnd_ctu_probe_(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ return rsnd_cmd_attach(io, rsnd_mod_id(mod));
+}
+
+static void rsnd_ctu_value_init(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ u32 cpmdr = 0;
+ u32 scmdr = 0;
+ int i, j;
+
+ for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+ u32 val = rsnd_kctrl_valm(ctu->pass, i);
+
+ cpmdr |= val << (28 - (i * 4));
+
+ if ((val > 0x8) && (scmdr < (val - 0x8)))
+ scmdr = val - 0x8;
+ }
+
+ rsnd_mod_write(mod, CTU_CTUIR, 1);
+
+ rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io));
+
+ rsnd_mod_write(mod, CTU_CPMDR, cpmdr);
+
+ rsnd_mod_write(mod, CTU_SCMDR, scmdr);
+
+ for (i = 0; i < 4; i++) {
+
+ if (i >= scmdr)
+ break;
+
+ for (j = 0; j < RSND_MAX_CHANNELS; j++)
+ rsnd_mod_write(mod, CTU_SVxxR(i, j), rsnd_kctrl_valm(ctu->sv[i], j));
+ }
+
+ rsnd_mod_write(mod, CTU_CTUIR, 0);
+}
+
+static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ int i;
+
+ if (!rsnd_kctrl_vals(ctu->reset))
+ return;
+
+ for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+ rsnd_kctrl_valm(ctu->pass, i) = 0;
+ rsnd_kctrl_valm(ctu->sv[0], i) = 0;
+ rsnd_kctrl_valm(ctu->sv[1], i) = 0;
+ rsnd_kctrl_valm(ctu->sv[2], i) = 0;
+ rsnd_kctrl_valm(ctu->sv[3], i) = 0;
+ }
+ rsnd_kctrl_vals(ctu->reset) = 0;
+}
+
+static int rsnd_ctu_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ int ret;
+
+ ret = rsnd_mod_power_on(mod);
+ if (ret < 0)
+ return ret;
+
+ rsnd_ctu_activation(mod);
+
+ rsnd_ctu_value_init(io, mod);
+
+ return 0;
+}
+
+static int rsnd_ctu_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_ctu_halt(mod);
+
+ rsnd_mod_power_off(mod);
+
+ return 0;
+}
+
+static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ int ret;
+
+ if (rsnd_flags_has(ctu, KCTRL_INITIALIZED))
+ return 0;
+
+ /* CTU Pass */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
+ rsnd_kctrl_accept_anytime,
+ NULL,
+ &ctu->pass, RSND_MAX_CHANNELS,
+ 0xC);
+ if (ret < 0)
+ return ret;
+
+ /* ROW0 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
+ rsnd_kctrl_accept_anytime,
+ NULL,
+ &ctu->sv[0], RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW1 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
+ rsnd_kctrl_accept_anytime,
+ NULL,
+ &ctu->sv[1], RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW2 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
+ rsnd_kctrl_accept_anytime,
+ NULL,
+ &ctu->sv[2], RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW3 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
+ rsnd_kctrl_accept_anytime,
+ NULL,
+ &ctu->sv[3], RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* Reset */
+ ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
+ rsnd_kctrl_accept_anytime,
+ rsnd_ctu_value_reset,
+ &ctu->reset, 1);
+
+ rsnd_flags_set(ctu, KCTRL_INITIALIZED);
+
+ return ret;
+}
+
+static int rsnd_ctu_id(struct rsnd_mod *mod)
+{
+ /*
+ * ctu00: -> 0, ctu01: -> 0, ctu02: -> 0, ctu03: -> 0
+ * ctu10: -> 1, ctu11: -> 1, ctu12: -> 1, ctu13: -> 1
+ */
+ return mod->id / 4;
+}
+
+static int rsnd_ctu_id_sub(struct rsnd_mod *mod)
+{
+ /*
+ * ctu00: -> 0, ctu01: -> 1, ctu02: -> 2, ctu03: -> 3
+ * ctu10: -> 0, ctu11: -> 1, ctu12: -> 2, ctu13: -> 3
+ */
+ return mod->id % 4;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_ctu_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ 0x500 + rsnd_mod_id_raw(mod) * 0x100, 0x100);
+}
+#define DEBUG_INFO .debug_info = rsnd_ctu_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
+static struct rsnd_mod_ops rsnd_ctu_ops = {
+ .name = CTU_NAME,
+ .probe = rsnd_ctu_probe_,
+ .init = rsnd_ctu_init,
+ .quit = rsnd_ctu_quit,
+ .pcm_new = rsnd_ctu_pcm_new,
+ .get_status = rsnd_mod_get_status,
+ .id = rsnd_ctu_id,
+ .id_sub = rsnd_ctu_id_sub,
+ .id_cmd = rsnd_mod_id_raw,
+ DEBUG_INFO
+};
+
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get(rsnd_ctu_get(priv, id));
+}
+
+int rsnd_ctu_probe(struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_ctu *ctu;
+ struct clk *clk;
+ char name[CTU_NAME_SIZE];
+ int i, nr, ret;
+
+ node = rsnd_ctu_of_node(priv);
+ if (!node)
+ return 0; /* not used is not error */
+
+ nr = of_get_child_count(node);
+ if (!nr) {
+ ret = -EINVAL;
+ goto rsnd_ctu_probe_done;
+ }
+
+ ctu = devm_kcalloc(dev, nr, sizeof(*ctu), GFP_KERNEL);
+ if (!ctu) {
+ ret = -ENOMEM;
+ goto rsnd_ctu_probe_done;
+ }
+
+ priv->ctu_nr = nr;
+ priv->ctu = ctu;
+
+ i = 0;
+ ret = 0;
+ for_each_child_of_node_scoped(node, np) {
+ ctu = rsnd_ctu_get(priv, i);
+
+ /*
+ * CTU00, CTU01, CTU02, CTU03 => CTU0
+ * CTU10, CTU11, CTU12, CTU13 => CTU1
+ */
+ snprintf(name, CTU_NAME_SIZE, "%s.%d",
+ CTU_NAME, i / 4);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto rsnd_ctu_probe_done;
+ }
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
+ clk, RSND_MOD_CTU, i);
+ if (ret)
+ goto rsnd_ctu_probe_done;
+
+ i++;
+ }
+
+
+rsnd_ctu_probe_done:
+ of_node_put(node);
+
+ return ret;
+}
+
+void rsnd_ctu_remove(struct rsnd_priv *priv)
+{
+ struct rsnd_ctu *ctu;
+ int i;
+
+ for_each_rsnd_ctu(ctu, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(ctu));
+ }
+}
diff --git a/sound/soc/renesas/rcar/debugfs.c b/sound/soc/renesas/rcar/debugfs.c
new file mode 100644
index 000000000000..26d3b310b9db
--- /dev/null
+++ b/sound/soc/renesas/rcar/debugfs.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// // Renesas R-Car debugfs support
+//
+// Copyright (c) 2021 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// > mount -t debugfs none /sys/kernel/debug
+// > cd /sys/kernel/debug/asoc/rcar-sound/ec500000.sound/rdai{N}/
+// > cat playback/xxx
+// > cat capture/xxx
+//
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include "rsnd.h"
+
+static int rsnd_debugfs_show(struct seq_file *m, void *v)
+{
+ struct rsnd_dai_stream *io = m->private;
+ struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int i;
+
+ /* adg is out of mods */
+ rsnd_adg_clk_dbg_info(priv, m);
+
+ for_each_rsnd_mod(i, mod, io) {
+ u32 *status = mod->ops->get_status(mod, io, mod->type);
+
+ seq_printf(m, "name: %s\n", rsnd_mod_name(mod));
+ seq_printf(m, "status: %08x\n", *status);
+
+ if (mod->ops->debug_info)
+ mod->ops->debug_info(m, io, mod);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rsnd_debugfs);
+
+void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr,
+ void __iomem *base, int offset, int size)
+{
+ int i, j;
+
+ for (i = 0; i < size; i += 0x10) {
+ phys_addr_t addr = _addr + offset + i;
+
+ seq_printf(m, "%pa:", &addr);
+ for (j = 0; j < 0x10; j += 0x4)
+ seq_printf(m, " %08x", __raw_readl(base + offset + i + j));
+ seq_puts(m, "\n");
+ }
+}
+
+void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod,
+ int reg_id, int offset, int size)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+ rsnd_debugfs_reg_show(m,
+ rsnd_gen_get_phy_addr(priv, reg_id),
+ rsnd_gen_get_base_addr(priv, reg_id),
+ offset, size);
+}
+
+int rsnd_debugfs_probe(struct snd_soc_component *component)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(component->dev);
+ struct rsnd_dai *rdai;
+ struct dentry *dir;
+ char name[64];
+ int i;
+
+ /* Gen1 is not supported */
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ for_each_rsnd_dai(rdai, priv, i) {
+ /*
+ * created debugfs will be automatically
+ * removed, nothing to do for _remove.
+ * see
+ * soc_cleanup_component_debugfs()
+ */
+ snprintf(name, sizeof(name), "rdai%d", i);
+ dir = debugfs_create_dir(name, component->debugfs_root);
+
+ debugfs_create_file("playback", 0444, dir, &rdai->playback, &rsnd_debugfs_fops);
+ debugfs_create_file("capture", 0444, dir, &rdai->capture, &rsnd_debugfs_fops);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/renesas/rcar/dma.c
index 418e6fdd06a3..2035ce06fe4c 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/renesas/rcar/dma.c
@@ -1,15 +1,13 @@
-/*
- * Renesas R-Car Audio DMAC support
- *
- * Copyright (C) 2015 Renesas Electronics Corp.
- * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car Audio DMAC support
+//
+// Copyright (C) 2015 Renesas Electronics Corp.
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
#include <linux/delay.h>
#include <linux/of_dma.h>
+#include <sound/dmaengine_pcm.h>
#include "rsnd.h"
/*
@@ -34,6 +32,8 @@ struct rsnd_dmapp {
struct rsnd_dma {
struct rsnd_mod mod;
+ struct rsnd_mod *mod_from;
+ struct rsnd_mod *mod_to;
dma_addr_t src_addr;
dma_addr_t dst_addr;
union {
@@ -43,7 +43,8 @@ struct rsnd_dma {
};
struct rsnd_dma_ctrl {
- void __iomem *base;
+ void __iomem *ppbase;
+ phys_addr_t ppres;
int dmaen_num;
int dmapp_num;
};
@@ -53,55 +54,85 @@ struct rsnd_dma_ctrl {
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
+/* for DEBUG */
+static struct rsnd_mod_ops mem_ops = {
+ .name = "mem",
+};
+
+static struct rsnd_mod mem = {
+};
+
/*
* Audio DMAC
*/
-static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io)
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod_from,
+ struct rsnd_mod *mod_to)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- bool elapsed = false;
- unsigned long flags;
-
- /*
- * Renesas sound Gen1 needs 1 DMAC,
- * Gen2 needs 2 DMAC.
- * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
- * But, Audio-DMAC-peri-peri doesn't have interrupt,
- * and this driver is assuming that here.
- *
- * If Audio-DMAC-peri-peri has interrpt,
- * rsnd_dai_pointer_update() will be called twice,
- * ant it will breaks io->byte_pos
- */
- spin_lock_irqsave(&priv->lock, flags);
-
- if (rsnd_io_is_working(io))
- elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
+ if ((!mod_from && !mod_to) ||
+ (mod_from && mod_to))
+ return NULL;
- spin_unlock_irqrestore(&priv->lock, flags);
+ if (mod_from)
+ return rsnd_mod_dma_req(io, mod_from);
+ else
+ return rsnd_mod_dma_req(io, mod_to);
+}
- if (elapsed)
- rsnd_dai_period_elapsed(io);
+static int rsnd_dmaen_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_STOP);
}
-static void rsnd_dmaen_complete(void *data)
+static int rsnd_dmaen_cleanup(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
{
- struct rsnd_mod *mod = data;
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
- rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
+ /*
+ * DMAEngine release uses mutex lock.
+ * Thus, it shouldn't be called under spinlock.
+ * Let's call it under prepare
+ */
+ if (dmaen->chan)
+ snd_dmaengine_pcm_close_release_chan(io->substream);
+
+ dmaen->chan = NULL;
+
+ return 0;
}
-static int rsnd_dmaen_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_dmaen_prepare(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+ struct device *dev = rsnd_priv_to_dev(priv);
- dmaengine_terminate_all(dmaen->chan);
+ /* maybe suspended */
+ if (dmaen->chan)
+ return 0;
- return 0;
+ /*
+ * DMAEngine request uses mutex lock.
+ * Thus, it shouldn't be called under spinlock.
+ * Let's call it under prepare
+ */
+ dmaen->chan = rsnd_dmaen_request_channel(io,
+ dma->mod_from,
+ dma->mod_to);
+ if (IS_ERR_OR_NULL(dmaen->chan)) {
+ dmaen->chan = NULL;
+ dev_err(dev, "can't get dma channel\n");
+ return -EIO;
+ }
+
+ return snd_dmaengine_pcm_open(io->substream, dmaen->chan);
}
static int rsnd_dmaen_start(struct rsnd_mod *mod,
@@ -110,173 +141,160 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
- struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
- struct dma_async_tx_descriptor *desc;
- int is_play = rsnd_io_is_play(io);
+ struct dma_slave_config cfg = {};
+ enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ int ret;
- desc = dmaengine_prep_dma_cyclic(dmaen->chan,
- substream->runtime->dma_addr,
- snd_pcm_lib_buffer_bytes(substream),
- snd_pcm_lib_period_bytes(substream),
- is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ /*
+ * in case of monaural data writing or reading through Audio-DMAC
+ * data is always in Left Justified format, so both src and dst
+ * DMA Bus width need to be set equal to physical data width.
+ */
+ if (rsnd_runtime_channel_original(io) == 1) {
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ int bits = snd_pcm_format_physical_width(runtime->format);
- if (!desc) {
- dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
- return -EIO;
+ switch (bits) {
+ case 8:
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case 16:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case 32:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ dev_err(dev, "invalid format width %d\n", bits);
+ return -EINVAL;
+ }
}
- desc->callback = rsnd_dmaen_complete;
- desc->callback_param = rsnd_mod_get(dma);
+ cfg.direction = snd_pcm_substream_to_dma_direction(io->substream);
+ cfg.src_addr = dma->src_addr;
+ cfg.dst_addr = dma->dst_addr;
+ cfg.src_addr_width = buswidth;
+ cfg.dst_addr_width = buswidth;
- if (dmaengine_submit(desc) < 0) {
- dev_err(dev, "dmaengine_submit() fail\n");
- return -EIO;
- }
+ dev_dbg(dev, "%s %pad -> %pad\n",
+ rsnd_mod_name(mod),
+ &cfg.src_addr, &cfg.dst_addr);
- dma_async_issue_pending(dmaen->chan);
+ ret = dmaengine_slave_config(dmaen->chan, &cfg);
+ if (ret < 0)
+ return ret;
- return 0;
+ return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_START);
}
-struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
- struct rsnd_mod *mod, char *name)
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
+ struct rsnd_mod *mod, char *x)
{
- struct dma_chan *chan;
- struct device_node *np;
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct dma_chan *chan = NULL;
int i = 0;
- for_each_child_of_node(of_node, np) {
- if (i == rsnd_mod_id(mod))
+ for_each_child_of_node_scoped(of_node, np) {
+ i = rsnd_node_fixed_index(dev, np, name, i);
+ if (i < 0) {
+ chan = NULL;
break;
+ }
+
+ if (i == rsnd_mod_id_raw(mod) && (!chan))
+ chan = of_dma_request_slave_channel(np, x);
i++;
}
- chan = of_dma_request_slave_channel(np, name);
-
- of_node_put(np);
+ /* It should call of_node_put(), since, it is rsnd_xxx_of_node() */
of_node_put(of_node);
return chan;
}
-static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod_from,
- struct rsnd_mod *mod_to)
-{
- if ((!mod_from && !mod_to) ||
- (mod_from && mod_to))
- return NULL;
-
- if (mod_from)
- return rsnd_mod_dma_req(io, mod_from);
- else
- return rsnd_mod_dma_req(io, mod_to);
-}
-
-static int rsnd_dmaen_remove(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
-
- if (dmaen->chan)
- dma_release_channel(dmaen->chan);
-
- dmaen->chan = NULL;
-
- return 0;
-}
-
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
- struct rsnd_dma *dma, int id,
+ struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
- struct rsnd_mod *mod = rsnd_mod_get(dma);
- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct dma_slave_config cfg = {};
- int is_play = rsnd_io_is_play(io);
- int ret;
-
- if (dmaen->chan) {
- dev_err(dev, "it already has dma channel\n");
- return -EIO;
- }
-
- if (dev->of_node) {
- dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
- } else {
- dma_cap_mask_t mask;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ struct dma_chan *chan;
- dmaen->chan = dma_request_channel(mask, shdma_chan_filter,
- (void *)(uintptr_t)id);
- }
- if (IS_ERR_OR_NULL(dmaen->chan)) {
- dmaen->chan = NULL;
- dev_err(dev, "can't get dma channel\n");
- goto rsnd_dma_channel_err;
+ /* try to get DMAEngine channel */
+ chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
+ if (IS_ERR_OR_NULL(chan)) {
+ /* Let's follow when -EPROBE_DEFER case */
+ if (PTR_ERR(chan) == -EPROBE_DEFER)
+ return PTR_ERR(chan);
+
+ /*
+ * DMA failed. try to PIO mode
+ * see
+ * rsnd_ssi_fallback()
+ * rsnd_rdai_continuance_probe()
+ */
+ return -EAGAIN;
}
- cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
- cfg.src_addr = dma->src_addr;
- cfg.dst_addr = dma->dst_addr;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-
- dev_dbg(dev, "%s[%d] %pad -> %pad\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- &cfg.src_addr, &cfg.dst_addr);
+ /*
+ * use it for IPMMU if needed
+ * see
+ * rsnd_preallocate_pages()
+ */
+ io->dmac_dev = chan->device->dev;
- ret = dmaengine_slave_config(dmaen->chan, &cfg);
- if (ret < 0)
- goto rsnd_dma_attach_err;
+ dma_release_channel(chan);
dmac->dmaen_num++;
return 0;
+}
-rsnd_dma_attach_err:
- rsnd_dmaen_remove(mod, io, priv);
-rsnd_dma_channel_err:
+static int rsnd_dmaen_pointer(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ snd_pcm_uframes_t *pointer)
+{
+ *pointer = snd_dmaengine_pcm_pointer(io->substream);
- /*
- * DMA failed. try to PIO mode
- * see
- * rsnd_ssi_fallback()
- * rsnd_rdai_continuance_probe()
- */
- return -EAGAIN;
+ return 0;
}
static struct rsnd_mod_ops rsnd_dmaen_ops = {
- .name = "audmac",
- .start = rsnd_dmaen_start,
- .stop = rsnd_dmaen_stop,
- .remove = rsnd_dmaen_remove,
+ .name = "audmac",
+ .prepare = rsnd_dmaen_prepare,
+ .cleanup = rsnd_dmaen_cleanup,
+ .start = rsnd_dmaen_start,
+ .stop = rsnd_dmaen_stop,
+ .pointer = rsnd_dmaen_pointer,
+ .get_status = rsnd_mod_get_status,
};
/*
* Audio DMAC peri peri
*/
static const u8 gen2_id_table_ssiu[] = {
- 0x00, /* SSI00 */
- 0x04, /* SSI10 */
- 0x08, /* SSI20 */
- 0x0c, /* SSI3 */
- 0x0d, /* SSI4 */
- 0x0e, /* SSI5 */
- 0x0f, /* SSI6 */
- 0x10, /* SSI7 */
- 0x11, /* SSI8 */
- 0x12, /* SSI90 */
+ /* SSI00 ~ SSI07 */
+ 0x00, 0x01, 0x02, 0x03, 0x39, 0x3a, 0x3b, 0x3c,
+ /* SSI10 ~ SSI17 */
+ 0x04, 0x05, 0x06, 0x07, 0x3d, 0x3e, 0x3f, 0x40,
+ /* SSI20 ~ SSI27 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x41, 0x42, 0x43, 0x44,
+ /* SSI30 ~ SSI37 */
+ 0x0c, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+ /* SSI40 ~ SSI47 */
+ 0x0d, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
+ /* SSI5 */
+ 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* SSI6 */
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* SSI7 */
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* SSI8 */
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* SSI90 ~ SSI97 */
+ 0x12, 0x13, 0x14, 0x15, 0x53, 0x54, 0x55, 0x56,
};
static const u8 gen2_id_table_scu[] = {
0x2d, /* SCU_SRCI0 */
@@ -299,28 +317,38 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
const u8 *entry = NULL;
- int id = rsnd_mod_id(mod);
+ int id = 255;
int size = 0;
- if (mod == ssi) {
+ if ((mod == ssi) ||
+ (mod == ssiu)) {
+ int busif = rsnd_mod_id_sub(ssiu);
+
entry = gen2_id_table_ssiu;
size = ARRAY_SIZE(gen2_id_table_ssiu);
+ id = (rsnd_mod_id(mod) * 8) + busif;
} else if (mod == src) {
entry = gen2_id_table_scu;
size = ARRAY_SIZE(gen2_id_table_scu);
+ id = rsnd_mod_id(mod);
} else if (mod == dvc) {
entry = gen2_id_table_cmd;
size = ARRAY_SIZE(gen2_id_table_cmd);
+ id = rsnd_mod_id(mod);
}
- if (!entry)
- return 0xFF;
+ if ((!entry) || (size <= id)) {
+ struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));
- if (size <= id)
- return 0xFF;
+ dev_err(dev, "unknown connection (%s)\n", rsnd_mod_name(mod));
+
+ /* use non-prohibited SRS number as error */
+ return 0x00; /* SSI00 */
+ }
return entry[id];
}
@@ -334,7 +362,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
}
#define rsnd_dmapp_addr(dmac, dma, reg) \
- (dmac->base + 0x20 + reg + \
+ (dmac->ppbase + 0x20 + reg + \
(0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
{
@@ -343,7 +371,7 @@ static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct device *dev = rsnd_priv_to_dev(priv);
- dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
+ dev_dbg(dev, "w 0x%px : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg));
}
@@ -357,6 +385,20 @@ static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
}
+static void rsnd_dmapp_bset(struct rsnd_dma *dma, u32 data, u32 mask, u32 reg)
+{
+ struct rsnd_mod *mod = rsnd_mod_get(dma);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+ void __iomem *addr = rsnd_dmapp_addr(dmac, dma, reg);
+ u32 val = ioread32(addr);
+
+ val &= ~mask;
+ val |= (data & mask);
+
+ iowrite32(val, addr);
+}
+
static int rsnd_dmapp_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
@@ -364,10 +406,10 @@ static int rsnd_dmapp_stop(struct rsnd_mod *mod,
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
int i;
- rsnd_dmapp_write(dma, 0, PDMACHCR);
+ rsnd_dmapp_bset(dma, 0, PDMACHCR_DE, PDMACHCR);
for (i = 0; i < 1024; i++) {
- if (0 == rsnd_dmapp_read(dma, PDMACHCR))
+ if (0 == (rsnd_dmapp_read(dma, PDMACHCR) & PDMACHCR_DE))
return 0;
udelay(1);
}
@@ -390,7 +432,7 @@ static int rsnd_dmapp_start(struct rsnd_mod *mod,
}
static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
- struct rsnd_dma *dma, int id,
+ struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
@@ -409,11 +451,31 @@ static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_dmapp_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+
+ rsnd_debugfs_reg_show(m, dmac->ppres, dmac->ppbase,
+ 0x20 + 0x10 * dmapp->dmapp_id, 0x10);
+}
+#define DEBUG_INFO .debug_info = rsnd_dmapp_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_dmapp_ops = {
- .name = "audmac-pp",
- .start = rsnd_dmapp_start,
- .stop = rsnd_dmapp_stop,
- .quit = rsnd_dmapp_stop,
+ .name = "audmac-pp",
+ .start = rsnd_dmapp_start,
+ .stop = rsnd_dmapp_stop,
+ .quit = rsnd_dmapp_stop,
+ .get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
/*
@@ -438,11 +500,11 @@ static struct rsnd_mod_ops rsnd_dmapp_ops = {
#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
-#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
-#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
+#define RDMA_SSIU_I_N(addr, i, j) (addr ##_reg - 0x00441000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4)))
+#define RDMA_SSIU_O_N(addr, i, j) RDMA_SSIU_I_N(addr, i, j)
-#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
-#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSIU_I_P(addr, i, j) (addr ##_reg - 0x00141000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4)))
+#define RDMA_SSIU_O_P(addr, i, j) RDMA_SSIU_I_P(addr, i, j)
#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i))
#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i))
@@ -460,21 +522,23 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
- phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
- phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
- int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
+ phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SSI);
+ phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SCU);
+ int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) ||
+ !!(rsnd_io_to_mod_ssiu(io) == mod);
int use_src = !!rsnd_io_to_mod_src(io);
int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
!!rsnd_io_to_mod_mix(io) ||
!!rsnd_io_to_mod_ctu(io);
int id = rsnd_mod_id(mod);
+ int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io));
struct dma_addr {
dma_addr_t out_addr;
dma_addr_t in_addr;
} dma_addrs[3][2][3] = {
/* SRC */
+ /* Capture */
{{{ 0, 0 },
- /* Capture */
{ RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) },
{ RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } },
/* Playback */
@@ -484,25 +548,35 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
},
/* SSI */
/* Capture */
- {{{ RDMA_SSI_O_N(ssi, id), 0 },
- { RDMA_SSIU_O_P(ssi, id), 0 },
- { RDMA_SSIU_O_P(ssi, id), 0 } },
+ {{{ RDMA_SSI_O_N(ssi, id), 0 },
+ { RDMA_SSIU_O_P(ssi, id, busif), 0 },
+ { RDMA_SSIU_O_P(ssi, id, busif), 0 } },
/* Playback */
- {{ 0, RDMA_SSI_I_N(ssi, id) },
- { 0, RDMA_SSIU_I_P(ssi, id) },
- { 0, RDMA_SSIU_I_P(ssi, id) } }
+ {{ 0, RDMA_SSI_I_N(ssi, id) },
+ { 0, RDMA_SSIU_I_P(ssi, id, busif) },
+ { 0, RDMA_SSIU_I_P(ssi, id, busif) } }
},
/* SSIU */
/* Capture */
- {{{ RDMA_SSIU_O_N(ssi, id), 0 },
- { RDMA_SSIU_O_P(ssi, id), 0 },
- { RDMA_SSIU_O_P(ssi, id), 0 } },
+ {{{ RDMA_SSIU_O_N(ssi, id, busif), 0 },
+ { RDMA_SSIU_O_P(ssi, id, busif), 0 },
+ { RDMA_SSIU_O_P(ssi, id, busif), 0 } },
/* Playback */
- {{ 0, RDMA_SSIU_I_N(ssi, id) },
- { 0, RDMA_SSIU_I_P(ssi, id) },
- { 0, RDMA_SSIU_I_P(ssi, id) } } },
+ {{ 0, RDMA_SSIU_I_N(ssi, id, busif) },
+ { 0, RDMA_SSIU_I_P(ssi, id, busif) },
+ { 0, RDMA_SSIU_I_P(ssi, id, busif) } } },
};
+ /*
+ * FIXME
+ *
+ * We can't support SSI9-4/5/6/7, because its address is
+ * out of calculation rule
+ */
+ if ((id == 9) && (busif >= 4))
+ dev_err(dev, "This driver doesn't support SSI%d-%d, so far",
+ id, busif);
+
/* it shouldn't happen */
if (use_cmd && !use_src)
dev_err(dev, "DVC is selected without SRC\n");
@@ -516,22 +590,54 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr;
}
+/*
+ * Gen4 DMA read/write register offset
+ *
+ * ex) R-Car V4H case
+ * mod / SYS-DMAC in / SYS-DMAC out
+ * SSI_SDMC: 0xec400000 / 0xec400000 / 0xec400000
+ */
+#define RDMA_SSI_SDMC(addr, i) (addr + (0x8000 * i))
+static dma_addr_t
+rsnd_gen4_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+ int is_play, int is_from)
+{
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ phys_addr_t addr = rsnd_gen_get_phy_addr(priv, RSND_BASE_SDMC);
+ int id = rsnd_mod_id(mod);
+ int busif = rsnd_mod_id_sub(mod);
+
+ /*
+ * SSI0 only is supported
+ */
+ if (id != 0) {
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_err(dev, "This driver doesn't support non SSI0");
+ return -EINVAL;
+ }
+
+ return RDMA_SSI_SDMC(addr, busif);
+}
+
static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
int is_play, int is_from)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ if (!mod)
+ return 0;
+
/*
* gen1 uses default DMA addr
*/
if (rsnd_is_gen1(priv))
return 0;
-
- if (!mod)
- return 0;
-
- return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
+ else if (rsnd_is_gen4(priv))
+ return rsnd_gen4_dma_addr(io, mod, is_play, is_from);
+ else
+ return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
}
#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
@@ -541,7 +647,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
struct rsnd_mod **mod_from,
struct rsnd_mod **mod_to)
{
- struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssi;
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
@@ -552,6 +658,28 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
struct device *dev = rsnd_priv_to_dev(priv);
int nr, i, idx;
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssiu_dma_req()
+ * rsnd_ssi_dma_req()
+ */
+ if (rsnd_ssiu_of_node(priv)) {
+ struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
+
+ /* use SSIU */
+ ssi = ssiu;
+ if (this == rsnd_io_to_mod_ssi(io))
+ this = ssiu;
+ } else {
+ /* keep compatible, use SSI */
+ ssi = rsnd_io_to_mod_ssi(io);
+ }
+
if (!ssi)
return;
@@ -612,29 +740,27 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
*mod_to = mod[1];
}
- dev_dbg(dev, "module connection (this is %s[%d])\n",
- rsnd_mod_name(this), rsnd_mod_id(this));
+ dev_dbg(dev, "module connection (this is %s)\n", rsnd_mod_name(this));
for (i = 0; i <= idx; i++) {
- dev_dbg(dev, " %s[%d]%s\n",
- rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]),
- (mod[i] == *mod_from) ? " from" :
- (mod[i] == *mod_to) ? " to" : "");
+ dev_dbg(dev, " %s%s\n",
+ rsnd_mod_name(mod[i] ? mod[i] : &mem),
+ (mod[i] == *mod_from) ? " from" :
+ (mod[i] == *mod_to) ? " to" : "");
}
}
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, int id)
+static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+ struct rsnd_mod **dma_mod)
{
- struct rsnd_mod *dma_mod;
struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL;
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
- struct rsnd_dma *dma;
struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_dma *dma;
struct rsnd_mod_ops *ops;
enum rsnd_mod_type type;
- int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
+ int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
int is_play = rsnd_io_is_play(io);
int ret, dma_id;
@@ -646,18 +772,11 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
* rsnd_rdai_continuance_probe()
*/
if (!dmac)
- return ERR_PTR(-EAGAIN);
-
- dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
- return ERR_PTR(-ENOMEM);
+ return -EAGAIN;
rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
- dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
- dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
-
- /* for Gen2 */
+ /* for Gen2 or later */
if (mod_from && mod_to) {
ops = &rsnd_dmapp_ops;
attach = rsnd_dmapp_attach;
@@ -678,27 +797,45 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
type = RSND_MOD_AUDMA;
}
- dma_mod = rsnd_mod_get(dma);
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
- ret = rsnd_mod_init(priv, dma_mod,
- ops, NULL, type, dma_id);
+ *dma_mod = rsnd_mod_get(dma);
+
+ ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
+ type, dma_id);
if (ret < 0)
- return ERR_PTR(ret);
+ return ret;
- dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
- rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
- rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
- rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
+ dev_dbg(dev, "%s %s -> %s\n",
+ rsnd_mod_name(*dma_mod),
+ rsnd_mod_name(mod_from ? mod_from : &mem),
+ rsnd_mod_name(mod_to ? mod_to : &mem));
- ret = attach(io, dma, id, mod_from, mod_to);
+ ret = attach(io, dma, mod_from, mod_to);
if (ret < 0)
- return ERR_PTR(ret);
+ return ret;
- ret = rsnd_dai_connect(dma_mod, io, type);
- if (ret < 0)
- return ERR_PTR(ret);
+ dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+ dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
+ dma->mod_from = mod_from;
+ dma->mod_to = mod_to;
- return rsnd_mod_get(dma);
+ return 0;
+}
+
+int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+ struct rsnd_mod **dma_mod)
+{
+ if (!(*dma_mod)) {
+ int ret = rsnd_dma_alloc(io, mod, dma_mod);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type);
}
int rsnd_dma_probe(struct rsnd_priv *priv)
@@ -715,21 +852,32 @@ int rsnd_dma_probe(struct rsnd_priv *priv)
return 0;
/*
- * for Gen2
+ * for Gen2 or later
*/
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
- if (!dmac || !res) {
+ if (!dmac) {
dev_err(dev, "dma allocate failed\n");
return 0; /* it will be PIO mode */
}
- dmac->dmapp_num = 0;
- dmac->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(dmac->base))
- return PTR_ERR(dmac->base);
+ /* for Gen4 doesn't have DMA-pp */
+ if (rsnd_is_gen4(priv))
+ goto audmapp_end;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
+ if (!res) {
+ dev_err(dev, "lack of audmapp in DT\n");
+ return 0; /* it will be PIO mode */
+ }
+
+ dmac->dmapp_num = 0;
+ dmac->ppres = res->start;
+ dmac->ppbase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dmac->ppbase))
+ return PTR_ERR(dmac->ppbase);
+audmapp_end:
priv->dma = dmac;
- return 0;
+ /* dummy mem mod for debug */
+ return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0);
}
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c
index d45ffe496397..988cbddbc611 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/renesas/rcar/dvc.c
@@ -1,13 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car DVC support
+//
+// Copyright (C) 2014 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
/*
- * Renesas R-Car DVC support
+ * Playback Volume
+ * amixer set "DVC Out" 100%
+ *
+ * Capture Volume
+ * amixer set "DVC In" 100%
*
- * Copyright (C) 2014 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * Playback Mute
+ * amixer set "DVC Out Mute" on
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * Capture Mute
+ * amixer set "DVC In Mute" on
+ *
+ * Volume Ramp
+ * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
+ * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+ * amixer set "DVC Out Ramp" on
+ * aplay xxx.wav &
+ * amixer set "DVC Out" 80% // Volume Down
+ * amixer set "DVC Out" 100% // Volume Up
*/
+
#include "rsnd.h"
#define RSND_DVC_NAME_SIZE 16
@@ -25,8 +44,6 @@ struct rsnd_dvc {
#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
-#define rsnd_dvc_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
#define rsnd_mod_to_dvc(_mod) \
container_of((_mod), struct rsnd_dvc, mod)
@@ -37,33 +54,6 @@ struct rsnd_dvc {
((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \
i++)
-static const char * const dvc_ramp_rate[] = {
- "128 dB/1 step", /* 00000 */
- "64 dB/1 step", /* 00001 */
- "32 dB/1 step", /* 00010 */
- "16 dB/1 step", /* 00011 */
- "8 dB/1 step", /* 00100 */
- "4 dB/1 step", /* 00101 */
- "2 dB/1 step", /* 00110 */
- "1 dB/1 step", /* 00111 */
- "0.5 dB/1 step", /* 01000 */
- "0.25 dB/1 step", /* 01001 */
- "0.125 dB/1 step", /* 01010 */
- "0.125 dB/2 steps", /* 01011 */
- "0.125 dB/4 steps", /* 01100 */
- "0.125 dB/8 steps", /* 01101 */
- "0.125 dB/16 steps", /* 01110 */
- "0.125 dB/32 steps", /* 01111 */
- "0.125 dB/64 steps", /* 10000 */
- "0.125 dB/128 steps", /* 10001 */
- "0.125 dB/256 steps", /* 10010 */
- "0.125 dB/512 steps", /* 10011 */
- "0.125 dB/1024 steps", /* 10100 */
- "0.125 dB/2048 steps", /* 10101 */
- "0.125 dB/4096 steps", /* 10110 */
- "0.125 dB/8192 steps", /* 10111 */
-};
-
static void rsnd_dvc_activation(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, DVC_SWRSR, 0);
@@ -76,33 +66,28 @@ static void rsnd_dvc_halt(struct rsnd_mod *mod)
rsnd_mod_write(mod, DVC_SWRSR, 0);
}
-#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val)
-#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13))
+#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \
+ rsnd_kctrl_vals(dvc->rdown))
+#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13))
static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
- u32 val[RSND_DVC_CHANNELS];
+ u32 val[RSND_MAX_CHANNELS];
int i;
/* Enable Ramp */
- if (dvc->ren.val)
- for (i = 0; i < RSND_DVC_CHANNELS; i++)
- val[i] = dvc->volume.cfg.max;
+ if (rsnd_kctrl_vals(dvc->ren))
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
+ val[i] = rsnd_kctrl_max(dvc->volume);
else
- for (i = 0; i < RSND_DVC_CHANNELS; i++)
- val[i] = dvc->volume.val[i];
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
+ val[i] = rsnd_kctrl_valm(dvc->volume, i);
/* Enable Digital Volume */
- rsnd_mod_write(mod, DVC_VOL0R, val[0]);
- rsnd_mod_write(mod, DVC_VOL1R, val[1]);
- rsnd_mod_write(mod, DVC_VOL2R, val[2]);
- rsnd_mod_write(mod, DVC_VOL3R, val[3]);
- rsnd_mod_write(mod, DVC_VOL4R, val[4]);
- rsnd_mod_write(mod, DVC_VOL5R, val[5]);
- rsnd_mod_write(mod, DVC_VOL6R, val[6]);
- rsnd_mod_write(mod, DVC_VOL7R, val[7]);
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
+ rsnd_mod_write(mod, DVC_VOLxR(i), val[i]);
}
static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
@@ -116,13 +101,13 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
u32 vrdbr = 0;
adinr = rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io);
+ rsnd_runtime_channel_after_ctu(io);
/* Enable Digital Volume, Zero Cross Mute Mode */
dvucr |= 0x101;
/* Enable Ramp */
- if (dvc->ren.val) {
+ if (rsnd_kctrl_vals(dvc->ren)) {
dvucr |= 0x10;
/*
@@ -164,10 +149,10 @@ static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
u32 vrdbr = 0;
int i;
- for (i = 0; i < dvc->mute.cfg.size; i++)
- zcmcr |= (!!dvc->mute.cfg.val[i]) << i;
+ for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++)
+ zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i;
- if (dvc->ren.val) {
+ if (rsnd_kctrl_vals(dvc->ren)) {
vrpdr = rsnd_dvc_get_vrpdr(dvc);
vrdbr = rsnd_dvc_get_vrdbr(dvc);
}
@@ -197,26 +182,15 @@ static int rsnd_dvc_probe_(struct rsnd_mod *mod,
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
}
-static int rsnd_dvc_remove_(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-
- rsnd_kctrl_remove(dvc->volume);
- rsnd_kctrl_remove(dvc->mute);
- rsnd_kctrl_remove(dvc->ren);
- rsnd_kctrl_remove(dvc->rup);
- rsnd_kctrl_remove(dvc->rdown);
-
- return 0;
-}
-
static int rsnd_dvc_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_power_on(mod);
+ int ret;
+
+ ret = rsnd_mod_power_on(mod);
+ if (ret < 0)
+ return ret;
rsnd_dvc_activation(mod);
@@ -243,16 +217,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
int is_play = rsnd_io_is_play(io);
- int slots = rsnd_get_slot(io);
+ int channels = rsnd_rdai_channels_get(rdai);
int ret;
/* Volume */
ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
"DVC Out Playback Volume" : "DVC In Capture Volume",
+ rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update,
- &dvc->volume, slots,
+ &dvc->volume, channels,
0x00800000 - 1);
if (ret < 0)
return ret;
@@ -261,8 +237,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
"DVC Out Mute Switch" : "DVC In Mute Switch",
+ rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update,
- &dvc->mute, slots,
+ &dvc->mute, channels,
1);
if (ret < 0)
return ret;
@@ -271,6 +248,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_s(mod, io, rtd,
is_play ?
"DVC Out Ramp Switch" : "DVC In Ramp Switch",
+ rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update,
&dvc->ren, 1);
if (ret < 0)
@@ -279,18 +257,22 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
- &dvc->rup,
+ rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update,
- dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+ &dvc->rup,
+ volume_ramp_rate,
+ VOLUME_RAMP_MAX_DVC);
if (ret < 0)
return ret;
ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
- &dvc->rdown,
+ rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update,
- dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+ &dvc->rdown,
+ volume_ramp_rate,
+ VOLUME_RAMP_MAX_DVC);
if (ret < 0)
return ret;
@@ -304,17 +286,31 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
- mod, "tx");
+ DVC_NAME, mod, "tx");
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_dvc_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ 0xe00 + rsnd_mod_id(mod) * 0x100, 0x60);
}
+#define DEBUG_INFO .debug_info = rsnd_dvc_debug_info
+#else
+#define DEBUG_INFO
+#endif
static struct rsnd_mod_ops rsnd_dvc_ops = {
.name = DVC_NAME,
.dma_req = rsnd_dvc_dma_req,
.probe = rsnd_dvc_probe_,
- .remove = rsnd_dvc_remove_,
.init = rsnd_dvc_init,
.quit = rsnd_dvc_quit,
.pcm_new = rsnd_dvc_pcm_new,
+ .get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
@@ -328,17 +324,12 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
int rsnd_dvc_probe(struct rsnd_priv *priv)
{
struct device_node *node;
- struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dvc *dvc;
struct clk *clk;
char name[RSND_DVC_NAME_SIZE];
int i, nr, ret;
- /* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv))
- return 0;
-
node = rsnd_dvc_of_node(priv);
if (!node)
return 0; /* not used is not error */
@@ -349,7 +340,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
goto rsnd_dvc_probe_done;
}
- dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
+ dvc = devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL);
if (!dvc) {
ret = -ENOMEM;
goto rsnd_dvc_probe_done;
@@ -360,7 +351,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
i = 0;
ret = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
dvc = rsnd_dvc_get(priv, i);
snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
@@ -373,7 +364,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
- clk, RSND_MOD_DVC, i);
+ clk, RSND_MOD_DVC, i);
if (ret)
goto rsnd_dvc_probe_done;
diff --git a/sound/soc/renesas/rcar/gen.c b/sound/soc/renesas/rcar/gen.c
new file mode 100644
index 000000000000..d1f20cde66be
--- /dev/null
+++ b/sound/soc/renesas/rcar/gen.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car Gen1 SRU/SSI support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
+#include "rsnd.h"
+
+struct rsnd_gen {
+ struct rsnd_gen_ops *ops;
+
+ /* RSND_BASE_MAX base */
+ void __iomem *base[RSND_BASE_MAX];
+ phys_addr_t res[RSND_BASE_MAX];
+ struct regmap *regmap[RSND_BASE_MAX];
+
+ /* RSND_REG_MAX base */
+ struct regmap_field *regs[REG_MAX];
+ const char *reg_name[REG_MAX];
+};
+
+#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
+#define rsnd_reg_name(gen, id) ((gen)->reg_name[id])
+
+struct rsnd_regmap_field_conf {
+ int idx;
+ unsigned int reg_offset;
+ unsigned int id_offset;
+ const char *reg_name;
+};
+
+#define RSND_REG_SET(id, offset, _id_offset, n) \
+{ \
+ .idx = id, \
+ .reg_offset = offset, \
+ .id_offset = _id_offset, \
+ .reg_name = n, \
+}
+/* single address mapping */
+#define RSND_GEN_S_REG(id, offset) \
+ RSND_REG_SET(id, offset, 0, #id)
+
+/* multi address mapping */
+#define RSND_GEN_M_REG(id, offset, _id_offset) \
+ RSND_REG_SET(id, offset, _id_offset, #id)
+
+/*
+ * basic function
+ */
+static int rsnd_is_accessible_reg(struct rsnd_priv *priv,
+ struct rsnd_gen *gen, enum rsnd_reg reg)
+{
+ if (!gen->regs[reg]) {
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_err(dev, "unsupported register access %x\n", reg);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int rsnd_mod_id_cmd(struct rsnd_mod *mod)
+{
+ if (mod->ops->id_cmd)
+ return mod->ops->id_cmd(mod);
+
+ return rsnd_mod_id(mod);
+}
+
+u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ u32 val;
+
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return 0;
+
+ regmap_fields_read(gen->regs[reg], rsnd_mod_id_cmd(mod), &val);
+
+ dev_dbg(dev, "r %s - %-18s (%4d) : %08x\n",
+ rsnd_mod_name(mod),
+ rsnd_reg_name(gen, reg), reg, val);
+
+ return val;
+}
+
+void rsnd_mod_write(struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 data)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return;
+
+ regmap_fields_force_write(gen->regs[reg], rsnd_mod_id_cmd(mod), data);
+
+ dev_dbg(dev, "w %s - %-18s (%4d) : %08x\n",
+ rsnd_mod_name(mod),
+ rsnd_reg_name(gen, reg), reg, data);
+}
+
+void rsnd_mod_bset(struct rsnd_mod *mod,
+ enum rsnd_reg reg, u32 mask, u32 data)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return;
+
+ regmap_fields_force_update_bits(gen->regs[reg],
+ rsnd_mod_id_cmd(mod), mask, data);
+
+ dev_dbg(dev, "b %s - %-18s (%4d) : %08x/%08x\n",
+ rsnd_mod_name(mod),
+ rsnd_reg_name(gen, reg), reg, data, mask);
+
+}
+
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
+{
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ return gen->res[reg_id];
+}
+
+#ifdef CONFIG_DEBUG_FS
+void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id)
+{
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ return gen->base[reg_id];
+}
+#endif
+
+#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \
+ _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf))
+static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
+ int id_size,
+ int reg_id,
+ const char *name,
+ const struct rsnd_regmap_field_conf *conf,
+ int conf_size)
+{
+ struct platform_device *pdev = rsnd_priv_to_pdev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct resource *res;
+ struct regmap_config regc;
+ struct regmap_field *regs;
+ struct regmap *regmap;
+ struct reg_field regf;
+ void __iomem *base;
+ int i;
+
+ memset(&regc, 0, sizeof(regc));
+ regc.reg_bits = 32;
+ regc.val_bits = 32;
+ regc.reg_stride = 4;
+ regc.name = name;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!res)
+ return -ENODEV;
+
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, &regc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* RSND_BASE_MAX base */
+ gen->base[reg_id] = base;
+ gen->regmap[reg_id] = regmap;
+ gen->res[reg_id] = res->start;
+
+ for (i = 0; i < conf_size; i++) {
+
+ regf.reg = conf[i].reg_offset;
+ regf.id_offset = conf[i].id_offset;
+ regf.lsb = 0;
+ regf.msb = 31;
+ regf.id_size = id_size;
+
+ regs = devm_regmap_field_alloc(dev, regmap, regf);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /* RSND_REG_MAX base */
+ gen->regs[conf[i].idx] = regs;
+ gen->reg_name[conf[i].idx] = conf[i].reg_name;
+ }
+
+ return 0;
+}
+
+/*
+ * (A) : Gen4 is 0xa0c, but it is not used.
+ * see
+ * rsnd_ssiu_init()
+ */
+static const struct rsnd_regmap_field_conf conf_common_ssiu[] = {
+ RSND_GEN_S_REG(SSI_MODE0, 0x800),
+ RSND_GEN_S_REG(SSI_MODE1, 0x804),
+ RSND_GEN_S_REG(SSI_MODE2, 0x808), // (A)
+ RSND_GEN_S_REG(SSI_CONTROL, 0x810),
+ RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840),
+ RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844),
+ RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848),
+ RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c),
+ RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880),
+ RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884),
+ RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
+ RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE1, 0x854),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE3, 0x85c),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE4, 0x890),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE5, 0x894),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE6, 0x898),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE7, 0x89c),
+ RSND_GEN_S_REG(HDMI0_SEL, 0x9e0),
+ RSND_GEN_S_REG(HDMI1_SEL, 0x9e4),
+ RSND_GEN_M_REG(SSI_BUSIF0_MODE, 0x0, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF0_ADINR, 0x4, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF0_DALIGN, 0x8, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF1_MODE, 0x20, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF1_ADINR, 0x24, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF1_DALIGN, 0x28, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF2_MODE, 0x40, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF2_ADINR, 0x44, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF2_DALIGN, 0x48, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF3_MODE, 0x60, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF3_ADINR, 0x64, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF3_DALIGN, 0x68, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF4_MODE, 0x500, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF4_ADINR, 0x504, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF4_DALIGN, 0x508, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF5_MODE, 0x520, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF5_ADINR, 0x524, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF5_DALIGN, 0x528, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF6_MODE, 0x540, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF6_ADINR, 0x544, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF6_DALIGN, 0x548, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF7_MODE, 0x560, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF7_ADINR, 0x564, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF7_DALIGN, 0x568, 0x80),
+ RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80),
+ RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80),
+ RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
+ RSND_GEN_S_REG(SSI9_BUSIF0_MODE, 0x48c),
+ RSND_GEN_S_REG(SSI9_BUSIF0_ADINR, 0x484),
+ RSND_GEN_S_REG(SSI9_BUSIF0_DALIGN, 0x488),
+ RSND_GEN_S_REG(SSI9_BUSIF1_MODE, 0x4a0),
+ RSND_GEN_S_REG(SSI9_BUSIF1_ADINR, 0x4a4),
+ RSND_GEN_S_REG(SSI9_BUSIF1_DALIGN, 0x4a8),
+ RSND_GEN_S_REG(SSI9_BUSIF2_MODE, 0x4c0),
+ RSND_GEN_S_REG(SSI9_BUSIF2_ADINR, 0x4c4),
+ RSND_GEN_S_REG(SSI9_BUSIF2_DALIGN, 0x4c8),
+ RSND_GEN_S_REG(SSI9_BUSIF3_MODE, 0x4e0),
+ RSND_GEN_S_REG(SSI9_BUSIF3_ADINR, 0x4e4),
+ RSND_GEN_S_REG(SSI9_BUSIF3_DALIGN, 0x4e8),
+ RSND_GEN_S_REG(SSI9_BUSIF4_MODE, 0xd80),
+ RSND_GEN_S_REG(SSI9_BUSIF4_ADINR, 0xd84),
+ RSND_GEN_S_REG(SSI9_BUSIF4_DALIGN, 0xd88),
+ RSND_GEN_S_REG(SSI9_BUSIF5_MODE, 0xda0),
+ RSND_GEN_S_REG(SSI9_BUSIF5_ADINR, 0xda4),
+ RSND_GEN_S_REG(SSI9_BUSIF5_DALIGN, 0xda8),
+ RSND_GEN_S_REG(SSI9_BUSIF6_MODE, 0xdc0),
+ RSND_GEN_S_REG(SSI9_BUSIF6_ADINR, 0xdc4),
+ RSND_GEN_S_REG(SSI9_BUSIF6_DALIGN, 0xdc8),
+ RSND_GEN_S_REG(SSI9_BUSIF7_MODE, 0xde0),
+ RSND_GEN_S_REG(SSI9_BUSIF7_ADINR, 0xde4),
+ RSND_GEN_S_REG(SSI9_BUSIF7_DALIGN, 0xde8),
+};
+
+static const struct rsnd_regmap_field_conf conf_common_scu[] = {
+ RSND_GEN_M_REG(SRC_I_BUSIF_MODE, 0x0, 0x20),
+ RSND_GEN_M_REG(SRC_O_BUSIF_MODE, 0x4, 0x20),
+ RSND_GEN_M_REG(SRC_BUSIF_DALIGN, 0x8, 0x20),
+ RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
+ RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
+ RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20),
+ RSND_GEN_M_REG(CMD_BUSIF_MODE, 0x184, 0x20),
+ RSND_GEN_M_REG(CMD_BUSIF_DALIGN, 0x188, 0x20),
+ RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20),
+ RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20),
+ RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
+ RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc),
+ RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0),
+ RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4),
+ RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
+ RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
+ RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
+ RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40),
+ RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40),
+ RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
+ RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
+ RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
+ RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100),
+ RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
+ RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
+ RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100),
+ RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100),
+ RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100),
+ RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100),
+ RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100),
+ RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100),
+ RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100),
+ RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100),
+ RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100),
+ RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100),
+ RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100),
+ RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100),
+ RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100),
+ RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100),
+ RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100),
+ RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100),
+ RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100),
+ RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100),
+ RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100),
+ RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100),
+ RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100),
+ RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100),
+ RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100),
+ RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100),
+ RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100),
+ RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100),
+ RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100),
+ RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100),
+ RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100),
+ RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100),
+ RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100),
+ RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100),
+ RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100),
+ RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100),
+ RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
+ RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
+ RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),
+ RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40),
+ RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40),
+ RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40),
+ RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40),
+ RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40),
+ RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40),
+ RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40),
+ RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100),
+ RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100),
+ RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
+ RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100),
+ RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100),
+ RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100),
+ RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100),
+ RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100),
+ RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
+ RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
+ RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100),
+ RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100),
+ RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100),
+ RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100),
+ RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100),
+ RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100),
+ RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
+};
+
+static const struct rsnd_regmap_field_conf conf_common_adg[] = {
+ RSND_GEN_S_REG(BRRA, 0x00),
+ RSND_GEN_S_REG(BRRB, 0x04),
+ RSND_GEN_S_REG(BRGCKR, 0x08),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14),
+ RSND_GEN_S_REG(DIV_EN, 0x30),
+ RSND_GEN_S_REG(SRCIN_TIMSEL0, 0x34),
+ RSND_GEN_S_REG(SRCIN_TIMSEL1, 0x38),
+ RSND_GEN_S_REG(SRCIN_TIMSEL2, 0x3c),
+ RSND_GEN_S_REG(SRCIN_TIMSEL3, 0x40),
+ RSND_GEN_S_REG(SRCIN_TIMSEL4, 0x44),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL0, 0x48),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL1, 0x4c),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL2, 0x50),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
+ RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
+};
+
+static const struct rsnd_regmap_field_conf conf_common_ssi[] = {
+ RSND_GEN_M_REG(SSICR, 0x00, 0x40),
+ RSND_GEN_M_REG(SSISR, 0x04, 0x40),
+ RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
+ RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
+ RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
+};
+
+/*
+ * Gen4
+ */
+static int rsnd_gen4_probe(struct rsnd_priv *priv)
+{
+ struct rsnd_regmap_field_conf conf_null[] = { };
+
+ /*
+ * ssiu: SSIU0
+ * ssi : SSI0
+ */
+ int ret_ssiu = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SSIU, "ssiu", conf_common_ssiu);
+ int ret_ssi = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SSI, "ssi", conf_common_ssi);
+ int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg);
+ int ret_sdmc = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SDMC, "sdmc", conf_null);
+
+ return ret_adg | ret_ssiu | ret_ssi | ret_sdmc;
+}
+
+/*
+ * Gen2
+ */
+static int rsnd_gen2_probe(struct rsnd_priv *priv)
+{
+ /*
+ * ssi : SSI0 - SSI9
+ * ssiu: SSIU0 - SSIU9
+ * scu : SRC0 - SRC9 etc
+ */
+ int ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSI, "ssi", conf_common_ssi);
+ int ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSIU, "ssiu", conf_common_ssiu);
+ int ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SCU, "scu", conf_common_scu);
+ int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg);
+
+ return ret_ssi | ret_ssiu | ret_scu | ret_adg;
+}
+
+/*
+ * Gen1
+ */
+
+static int rsnd_gen1_probe(struct rsnd_priv *priv)
+{
+ /*
+ * ssi : SSI0 - SSI8
+ */
+ int ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_BASE_SSI, "ssi", conf_common_ssi);
+ int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg);
+
+ return ret_adg | ret_ssi;
+}
+
+/*
+ * Gen
+ */
+int rsnd_gen_probe(struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen;
+ int ret;
+
+ gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
+ if (!gen)
+ return -ENOMEM;
+
+ priv->gen = gen;
+
+ ret = -ENODEV;
+ if (rsnd_is_gen1(priv))
+ ret = rsnd_gen1_probe(priv);
+ else if (rsnd_is_gen2(priv) ||
+ rsnd_is_gen3(priv))
+ ret = rsnd_gen2_probe(priv);
+ else if (rsnd_is_gen4(priv))
+ ret = rsnd_gen4_probe(priv);
+
+ if (ret < 0)
+ dev_err(dev, "unknown generation R-Car sound device\n");
+
+ return ret;
+}
diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c
new file mode 100644
index 000000000000..aea74e703305
--- /dev/null
+++ b/sound/soc/renesas/rcar/mix.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mix.c
+//
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ * CTUn MIXn
+ * +------+ +------+
+ * [SRC3 / SRC6] -> |CTU n0| -> [MIX n0| ->
+ * [SRC4 / SRC9] -> |CTU n1| -> [MIX n1| ->
+ * [SRC0 / SRC1] -> |CTU n2| -> [MIX n2| ->
+ * [SRC2 / SRC5] -> |CTU n3| -> [MIX n3| ->
+ * +------+ +------+
+ *
+ * ex)
+ * DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>;
+ * DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;
+ *
+ * MIX Volume
+ * amixer set "MIX",0 100% // DAI0 Volume
+ * amixer set "MIX",1 100% // DAI1 Volume
+ *
+ * Volume Ramp
+ * amixer set "MIX Ramp Up Rate" "0.125 dB/1 step"
+ * amixer set "MIX Ramp Down Rate" "4 dB/1 step"
+ * amixer set "MIX Ramp" on
+ * aplay xxx.wav &
+ * amixer set "MIX",0 80% // DAI0 Volume Down
+ * amixer set "MIX",1 100% // DAI1 Volume Up
+ */
+
+#include "rsnd.h"
+
+#define MIX_NAME_SIZE 16
+#define MIX_NAME "mix"
+
+struct rsnd_mix {
+ struct rsnd_mod mod;
+ struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */
+ struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */
+ struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */
+ struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */
+ struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */
+ struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */
+ struct rsnd_kctrl_cfg_s rdw; /* Ramp Rate Down */
+ u32 flags;
+};
+
+#define ONCE_KCTRL_INITIALIZED (1 << 0)
+#define HAS_VOLA (1 << 1)
+#define HAS_VOLB (1 << 2)
+#define HAS_VOLC (1 << 3)
+#define HAS_VOLD (1 << 4)
+
+#define VOL_MAX 0x3ff
+
+#define rsnd_mod_to_mix(_mod) \
+ container_of((_mod), struct rsnd_mix, mod)
+
+#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
+#define rsnd_mix_nr(priv) ((priv)->mix_nr)
+#define for_each_rsnd_mix(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_mix_nr(priv)) && \
+ ((pos) = (struct rsnd_mix *)(priv)->mix + i); \
+ i++)
+
+static void rsnd_mix_activation(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, MIX_SWRSR, 0);
+ rsnd_mod_write(mod, MIX_SWRSR, 1);
+}
+
+static void rsnd_mix_halt(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, MIX_MIXIR, 1);
+ rsnd_mod_write(mod, MIX_SWRSR, 0);
+}
+
+#define rsnd_mix_get_vol(mix, X) \
+ rsnd_flags_has(mix, HAS_VOL##X) ? \
+ (VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0
+static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mix *mix = rsnd_mod_to_mix(mod);
+ u32 volA = rsnd_mix_get_vol(mix, A);
+ u32 volB = rsnd_mix_get_vol(mix, B);
+ u32 volC = rsnd_mix_get_vol(mix, C);
+ u32 volD = rsnd_mix_get_vol(mix, D);
+
+ dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n",
+ volA, volB, volC, volD);
+
+ rsnd_mod_write(mod, MIX_MDBAR, volA);
+ rsnd_mod_write(mod, MIX_MDBBR, volB);
+ rsnd_mod_write(mod, MIX_MDBCR, volC);
+ rsnd_mod_write(mod, MIX_MDBDR, volD);
+}
+
+static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_mix *mix = rsnd_mod_to_mix(mod);
+
+ rsnd_mod_write(mod, MIX_MIXIR, 1);
+
+ /* General Information */
+ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));
+
+ /* volume step */
+ rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren));
+ rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 |
+ rsnd_kctrl_vals(mix->rdw));
+
+ /* common volume parameter */
+ rsnd_mix_volume_parameter(io, mod);
+
+ rsnd_mod_write(mod, MIX_MIXIR, 0);
+}
+
+static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ /* Disable MIX dB setting */
+ rsnd_mod_write(mod, MIX_MDBER, 0);
+
+ /* common volume parameter */
+ rsnd_mix_volume_parameter(io, mod);
+
+ /* Enable MIX dB setting */
+ rsnd_mod_write(mod, MIX_MDBER, 1);
+}
+
+static int rsnd_mix_probe_(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ return rsnd_cmd_attach(io, rsnd_mod_id(mod));
+}
+
+static int rsnd_mix_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ int ret;
+
+ ret = rsnd_mod_power_on(mod);
+ if (ret < 0)
+ return ret;
+
+ rsnd_mix_activation(mod);
+
+ rsnd_mix_volume_init(io, mod);
+
+ rsnd_mix_volume_update(io, mod);
+
+ return 0;
+}
+
+static int rsnd_mix_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mix_halt(mod);
+
+ rsnd_mod_power_off(mod);
+
+ return 0;
+}
+
+static int rsnd_mix_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mix *mix = rsnd_mod_to_mix(mod);
+ struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+ struct rsnd_kctrl_cfg_s *volume;
+ int ret;
+
+ switch (rsnd_mod_id(src_mod)) {
+ case 3:
+ case 6: /* MDBAR */
+ volume = &mix->volumeA;
+ rsnd_flags_set(mix, HAS_VOLA);
+ break;
+ case 4:
+ case 9: /* MDBBR */
+ volume = &mix->volumeB;
+ rsnd_flags_set(mix, HAS_VOLB);
+ break;
+ case 0:
+ case 1: /* MDBCR */
+ volume = &mix->volumeC;
+ rsnd_flags_set(mix, HAS_VOLC);
+ break;
+ case 2:
+ case 5: /* MDBDR */
+ volume = &mix->volumeD;
+ rsnd_flags_set(mix, HAS_VOLD);
+ break;
+ default:
+ dev_err(dev, "unknown SRC is connected\n");
+ return -EINVAL;
+ }
+
+ /* Volume */
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
+ "MIX Playback Volume",
+ rsnd_kctrl_accept_anytime,
+ rsnd_mix_volume_update,
+ volume, VOL_MAX);
+ if (ret < 0)
+ return ret;
+ rsnd_kctrl_vals(*volume) = VOL_MAX;
+
+ if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED))
+ return ret;
+
+ /* Ramp */
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
+ "MIX Ramp Switch",
+ rsnd_kctrl_accept_anytime,
+ rsnd_mix_volume_update,
+ &mix->ren, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_kctrl_new_e(mod, io, rtd,
+ "MIX Ramp Up Rate",
+ rsnd_kctrl_accept_anytime,
+ rsnd_mix_volume_update,
+ &mix->rup,
+ volume_ramp_rate,
+ VOLUME_RAMP_MAX_MIX);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_kctrl_new_e(mod, io, rtd,
+ "MIX Ramp Down Rate",
+ rsnd_kctrl_accept_anytime,
+ rsnd_mix_volume_update,
+ &mix->rdw,
+ volume_ramp_rate,
+ VOLUME_RAMP_MAX_MIX);
+
+ rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED);
+
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_mix_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ 0xd00 + rsnd_mod_id(mod) * 0x40, 0x30);
+}
+#define DEBUG_INFO .debug_info = rsnd_mix_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
+static struct rsnd_mod_ops rsnd_mix_ops = {
+ .name = MIX_NAME,
+ .probe = rsnd_mix_probe_,
+ .init = rsnd_mix_init,
+ .quit = rsnd_mix_quit,
+ .pcm_new = rsnd_mix_pcm_new,
+ .get_status = rsnd_mod_get_status,
+ DEBUG_INFO
+};
+
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get(rsnd_mix_get(priv, id));
+}
+
+int rsnd_mix_probe(struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mix *mix;
+ struct clk *clk;
+ char name[MIX_NAME_SIZE];
+ int i, nr, ret;
+
+ node = rsnd_mix_of_node(priv);
+ if (!node)
+ return 0; /* not used is not error */
+
+ nr = of_get_child_count(node);
+ if (!nr) {
+ ret = -EINVAL;
+ goto rsnd_mix_probe_done;
+ }
+
+ mix = devm_kcalloc(dev, nr, sizeof(*mix), GFP_KERNEL);
+ if (!mix) {
+ ret = -ENOMEM;
+ goto rsnd_mix_probe_done;
+ }
+
+ priv->mix_nr = nr;
+ priv->mix = mix;
+
+ i = 0;
+ ret = 0;
+ for_each_child_of_node_scoped(node, np) {
+ mix = rsnd_mix_get(priv, i);
+
+ snprintf(name, MIX_NAME_SIZE, "%s.%d",
+ MIX_NAME, i);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto rsnd_mix_probe_done;
+ }
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
+ clk, RSND_MOD_MIX, i);
+ if (ret)
+ goto rsnd_mix_probe_done;
+
+ i++;
+ }
+
+rsnd_mix_probe_done:
+ of_node_put(node);
+
+ return ret;
+}
+
+void rsnd_mix_remove(struct rsnd_priv *priv)
+{
+ struct rsnd_mix *mix;
+ int i;
+
+ for_each_rsnd_mix(mix, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(mix));
+ }
+}
diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c
new file mode 100644
index 000000000000..f2addfbac923
--- /dev/null
+++ b/sound/soc/renesas/rcar/msiof.c
@@ -0,0 +1,629 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car MSIOF (Clock-Synchronized Serial Interface with FIFO) I2S driver
+//
+// Copyright (C) 2025 Renesas Solutions Corp.
+// Author: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+
+/*
+ * [NOTE-CLOCK-MODE]
+ *
+ * This driver doesn't support Clock/Frame Provider Mode
+ *
+ * Basically MSIOF is created for SPI, but we can use it as I2S (Sound), etc. Because of it, when
+ * we use it as I2S (Sound) with Provider Mode, we need to send dummy TX data even though it was
+ * used for RX. Because SPI HW needs TX Clock/Frame output for RX purpose.
+ * But it makes driver code complex in I2S (Sound).
+ *
+ * And when we use it as I2S (Sound) as Provider Mode, the clock source is [MSO clock] (= 133.33MHz)
+ * SoC internal clock. It is not for 48kHz/44.1kHz base clock. Thus the output/input will not be
+ * accurate sound.
+ *
+ * Because of these reasons, this driver doesn't support Clock/Frame Provider Mode. Use it as
+ * Clock/Frame Consumer Mode.
+ */
+
+/*
+ * [NOTE-RESET]
+ *
+ * MSIOF has TXRST/RXRST to reset FIFO, but it shouldn't be used during SYNC signal was asserted,
+ * because it will be cause of HW issue.
+ *
+ * When MSIOF is used as Sound driver, this driver is assuming it is used as clock consumer mode
+ * (= Codec is clock provider). This means, it can't control SYNC signal by itself.
+ *
+ * We need to use SW reset (= reset_control_xxx()) instead of TXRST/RXRST.
+ */
+
+/*
+ * [NOTE-BOTH-SETTING]
+ *
+ * SITMDRn / SIRMDRn and some other registers should not be updated during working even though it
+ * was not related the target direction (for example, do TX settings during RX is working),
+ * otherwise it cause a FSERR.
+ *
+ * Setup both direction (Playback/Capture) in the same time.
+ */
+
+/*
+ * [NOTE-R/L]
+ *
+ * The data of Captured might be R/L opposite.
+ *
+ * This driver is assuming MSIOF is used as Clock/Frame Consumer Mode, and there is a case that some
+ * Codec (= Clock/Frame Provider) might output Clock/Frame before setup MSIOF. It depends on Codec
+ * driver implementation.
+ *
+ * MSIOF will capture data without checking SYNC signal Hi/Low (= R/L).
+ *
+ * This means, if MSIOF RXE bit was set as 1 in case of SYNC signal was Hi (= R) timing, it will
+ * start capture data since next SYNC low singla (= L). Because Linux assumes sound data is lined
+ * up as R->L->R->L->..., the data R/L will be opposite.
+ *
+ * The only solution in this case is start CLK/SYNC *after* MSIOF settings, but it depends when and
+ * how Codec driver start it.
+ */
+
+/*
+ * [NOTE-FSERR]
+ *
+ * We can't remove all FSERR.
+ *
+ * Renesas have tried to minimize the occurrence of FSERR errors as much as possible, but
+ * unfortunately we cannot remove them completely, because MSIOF might setup its register during
+ * CLK/SYNC are inputed. It can be happen because MSIOF is working as Clock/Frame Consumer.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/spi/sh_msiof.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+/* SISTR */
+#define SISTR_ERR_TX (SISTR_TFSERR | SISTR_TFOVF | SISTR_TFUDF)
+#define SISTR_ERR_RX (SISTR_RFSERR | SISTR_RFOVF | SISTR_RFUDF)
+
+/*
+ * The data on memory in 24bit case is located at <right> side
+ * [ xxxxxx]
+ * [ xxxxxx]
+ * [ xxxxxx]
+ *
+ * HW assuming signal in 24bit case is located at <left> side
+ * ---+ +---------+
+ * +---------+ +---------+...
+ * [xxxxxx ][xxxxxx ][xxxxxx ]
+ *
+ * When we use 24bit data, it will be transferred via 32bit width via DMA,
+ * and MSIOF/DMA doesn't support data shift, we can't use 24bit data correctly.
+ * There is no such issue on 16/32bit data case.
+ */
+#define MSIOF_RATES SNDRV_PCM_RATE_8000_192000
+#define MSIOF_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct msiof_priv {
+ struct device *dev;
+ struct snd_pcm_substream *substream[SNDRV_PCM_STREAM_LAST + 1];
+ struct reset_control *reset;
+ spinlock_t lock;
+ void __iomem *base;
+ resource_size_t phy_addr;
+
+ int count;
+
+ /* for error */
+ int err_syc[SNDRV_PCM_STREAM_LAST + 1];
+ int err_ovf[SNDRV_PCM_STREAM_LAST + 1];
+ int err_udf[SNDRV_PCM_STREAM_LAST + 1];
+
+ /* bit field */
+ u32 flags;
+#define MSIOF_FLAGS_NEED_DELAY (1 << 0)
+};
+#define msiof_flag_has(priv, flag) (priv->flags & flag)
+#define msiof_flag_set(priv, flag) (priv->flags |= flag)
+
+#define msiof_is_play(substream) ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK)
+#define msiof_read(priv, reg) ioread32((priv)->base + reg)
+#define msiof_write(priv, reg, val) iowrite32(val, (priv)->base + reg)
+
+static int msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val)
+{
+ u32 old = msiof_read(priv, reg);
+ u32 new = (old & ~mask) | (val & mask);
+ int updated = false;
+
+ if (old != new) {
+ msiof_write(priv, reg, new);
+ updated = true;
+ }
+
+ return updated;
+}
+
+static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u32 val, u32 expect)
+{
+ u32 data;
+ int ret;
+
+ ret = msiof_update(priv, reg, mask, val);
+ if (!ret) /* no update */
+ return;
+
+ ret = readl_poll_timeout_atomic(priv->base + reg, data,
+ (data & mask) == expect, 1, 128);
+ if (ret)
+ dev_warn(priv->dev, "write timeout [0x%02x] 0x%08x / 0x%08x\n",
+ reg, data, expect);
+}
+
+static int msiof_hw_start(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct msiof_priv *priv = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int is_play = msiof_is_play(substream);
+ int width = snd_pcm_format_width(runtime->format);
+ u32 val;
+
+ /*
+ * see
+ * [NOTE-CLOCK-MODE] on top of this driver
+ */
+ /*
+ * see
+ * Datasheet 109.3.6 [Transmit and Receive Procedures]
+ *
+ * TX: Fig 109.14 - Fig 109.23
+ * RX: Fig 109.15
+ */
+
+ /*
+ * Use reset_control_xx() instead of TXRST/RXRST.
+ * see
+ * [NOTE-RESET]
+ */
+ if (!priv->count)
+ reset_control_deassert(priv->reset);
+
+ priv->count++;
+
+ /*
+ * Reset errors. ignore 1st FSERR
+ *
+ * see
+ * [NOTE-FSERR]
+ */
+ priv->err_syc[substream->stream] = -1;
+ priv->err_ovf[substream->stream] =
+ priv->err_udf[substream->stream] = 0;
+
+ /* Start DMAC */
+ snd_dmaengine_pcm_trigger(substream, cmd);
+
+ /*
+ * setup both direction (Playback/Capture) in the same time.
+ * see
+ * above [NOTE-BOTH-SETTING]
+ */
+
+ /* SITMDRx */
+ val = SITMDR1_PCON | SIMDR1_SYNCAC | SIMDR1_XXSTP |
+ FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR);
+ if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY))
+ val |= FIELD_PREP(SIMDR1_DTDL, 1);
+
+ msiof_write(priv, SITMDR1, val);
+
+ val = FIELD_PREP(SIMDR2_BITLEN1, width - 1);
+ msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1));
+ msiof_write(priv, SITMDR3, val);
+
+ /* SIRMDRx */
+ val = SIMDR1_SYNCAC |
+ FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR);
+ if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY))
+ val |= FIELD_PREP(SIMDR1_DTDL, 1);
+
+ msiof_write(priv, SIRMDR1, val);
+
+ val = FIELD_PREP(SIMDR2_BITLEN1, width - 1);
+ msiof_write(priv, SIRMDR2, val | FIELD_PREP(SIMDR2_GRP, 1));
+ msiof_write(priv, SIRMDR3, val);
+
+ /* SIFCTR */
+ msiof_write(priv, SIFCTR,
+ FIELD_PREP(SIFCTR_TFWM, SIFCTR_TFWM_1) |
+ FIELD_PREP(SIFCTR_RFWM, SIFCTR_RFWM_1));
+
+ /* SIIER */
+ if (is_play)
+ val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX;
+ else
+ val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX;
+ msiof_update(priv, SIIER, val, val);
+
+ /* clear status */
+ if (is_play)
+ val = SISTR_ERR_TX;
+ else
+ val = SISTR_ERR_RX;
+ msiof_update(priv, SISTR, val, val);
+
+ /* SICTR */
+ val = SICTR_TEDG | SICTR_REDG;
+ if (is_play)
+ val |= SICTR_TXE;
+ else
+ val |= SICTR_RXE;
+ msiof_update_and_wait(priv, SICTR, val, val, val);
+
+ return 0;
+}
+
+static int msiof_hw_stop(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct msiof_priv *priv = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int is_play = msiof_is_play(substream);
+ u32 val;
+
+ /* SIIER */
+ if (is_play)
+ val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX;
+ else
+ val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX;
+ msiof_update(priv, SIIER, val, 0);
+
+ /* SICTR */
+ if (is_play)
+ val = SICTR_TXE;
+ else
+ val = SICTR_RXE;
+ msiof_update_and_wait(priv, SICTR, val, 0, 0);
+
+ /* Stop DMAC */
+ snd_dmaengine_pcm_trigger(substream, cmd);
+
+ /*
+ * Ignore 1st FSERR
+ *
+ * see
+ * [NOTE-FSERR]
+ */
+ if (priv->err_syc[substream->stream] < 0)
+ priv->err_syc[substream->stream] = 0;
+
+ /* indicate error status if exist */
+ if (priv->err_syc[substream->stream] ||
+ priv->err_ovf[substream->stream] ||
+ priv->err_udf[substream->stream])
+ dev_warn(dev, "%s: FSERR = %d, FOVF = %d, FUDF = %d\n",
+ snd_pcm_direction_name(substream->stream),
+ priv->err_syc[substream->stream],
+ priv->err_ovf[substream->stream],
+ priv->err_udf[substream->stream]);
+
+ priv->count--;
+
+ if (!priv->count)
+ reset_control_assert(priv->reset);
+
+ return 0;
+}
+
+static int msiof_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msiof_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ /*
+ * It supports Clock/Frame Consumer Mode only
+ * see
+ * [NOTE] on top of this driver
+ */
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ /* others are error */
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ /* it supports NB_NF only */
+ case SND_SOC_DAIFMT_NB_NF:
+ default:
+ break;
+ /* others are error */
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ msiof_flag_set(priv, MSIOF_FLAGS_NEED_DELAY);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Select below from Sound Card, not auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 msiof_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
+static const struct snd_soc_dai_ops msiof_dai_ops = {
+ .set_fmt = msiof_dai_set_fmt,
+ .auto_selectable_formats = &msiof_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static struct snd_soc_dai_driver msiof_dai_driver = {
+ .name = "msiof-dai",
+ .playback = {
+ .rates = MSIOF_RATES,
+ .formats = MSIOF_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .rates = MSIOF_RATES,
+ .formats = MSIOF_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &msiof_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static struct snd_pcm_hardware msiof_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 64,
+};
+
+static int msiof_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct dma_chan *chan;
+ static const char * const dma_names[] = {"rx", "tx"};
+ int is_play = msiof_is_play(substream);
+ int ret;
+
+ chan = of_dma_request_slave_channel(dev->of_node, dma_names[is_play]);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ ret = snd_dmaengine_pcm_open(substream, chan);
+ if (ret < 0)
+ goto open_err_dma;
+
+ snd_soc_set_runtime_hwparams(substream, &msiof_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+open_err_dma:
+ if (ret < 0)
+ dma_release_channel(chan);
+
+ return ret;
+}
+
+static int msiof_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_close_release_chan(substream);
+}
+
+static snd_pcm_uframes_t msiof_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_pointer(substream);
+}
+
+#define PREALLOC_BUFFER (32 * 1024)
+#define PREALLOC_BUFFER_MAX (32 * 1024)
+static int msiof_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+ return 0;
+}
+
+static int msiof_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct device *dev = component->dev;
+ struct msiof_priv *priv = dev_get_drvdata(dev);
+ int ret = -EINVAL;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ priv->substream[substream->stream] = substream;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = msiof_hw_start(component, substream, cmd);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ priv->substream[substream->stream] = NULL;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = msiof_hw_stop(component, substream, cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static int msiof_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct msiof_priv *priv = dev_get_drvdata(component->dev);
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct dma_slave_config cfg = {};
+ int ret;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &cfg);
+ if (ret < 0)
+ return ret;
+
+ cfg.dst_addr = priv->phy_addr + SITFDR;
+ cfg.src_addr = priv->phy_addr + SIRFDR;
+
+ return dmaengine_slave_config(chan, &cfg);
+}
+
+static const struct snd_soc_component_driver msiof_component_driver = {
+ .name = "msiof",
+ .open = msiof_open,
+ .close = msiof_close,
+ .pointer = msiof_pointer,
+ .pcm_construct = msiof_new,
+ .trigger = msiof_trigger,
+ .hw_params = msiof_hw_params,
+};
+
+static irqreturn_t msiof_interrupt(int irq, void *data)
+{
+ struct msiof_priv *priv = data;
+ struct snd_pcm_substream *substream;
+ u32 sistr;
+
+ scoped_guard(spinlock, &priv->lock) {
+ sistr = msiof_read(priv, SISTR);
+ msiof_write(priv, SISTR, SISTR_ERR_TX | SISTR_ERR_RX);
+ }
+
+ /* overflow/underflow error */
+ substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream && (sistr & SISTR_ERR_TX)) {
+ // snd_pcm_stop_xrun(substream);
+ if (sistr & SISTR_TFSERR)
+ priv->err_syc[SNDRV_PCM_STREAM_PLAYBACK]++;
+ if (sistr & SISTR_TFOVF)
+ priv->err_ovf[SNDRV_PCM_STREAM_PLAYBACK]++;
+ if (sistr & SISTR_TFUDF)
+ priv->err_udf[SNDRV_PCM_STREAM_PLAYBACK]++;
+ }
+
+ substream = priv->substream[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream && (sistr & SISTR_ERR_RX)) {
+ // snd_pcm_stop_xrun(substream);
+ if (sistr & SISTR_RFSERR)
+ priv->err_syc[SNDRV_PCM_STREAM_CAPTURE]++;
+ if (sistr & SISTR_RFOVF)
+ priv->err_ovf[SNDRV_PCM_STREAM_CAPTURE]++;
+ if (sistr & SISTR_RFUDF)
+ priv->err_udf[SNDRV_PCM_STREAM_CAPTURE]++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int msiof_probe(struct platform_device *pdev)
+{
+ struct msiof_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int irq, ret;
+
+ /* Check MSIOF as Sound mode or SPI mode */
+ struct device_node *port __free(device_node) = of_graph_get_next_port(dev->of_node, NULL);
+ if (!port)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
+ reset_control_assert(priv->reset);
+
+ ret = devm_request_irq(dev, irq, msiof_interrupt, 0, dev_name(dev), priv);
+ if (ret)
+ return ret;
+
+ priv->dev = dev;
+ priv->phy_addr = res->start;
+ priv->count = 0;
+
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+
+ devm_pm_runtime_enable(dev);
+
+ ret = devm_snd_soc_register_component(dev, &msiof_component_driver,
+ &msiof_dai_driver, 1);
+
+ return ret;
+}
+
+static const struct of_device_id msiof_of_match[] = {
+ { .compatible = "renesas,rcar-gen4-msiof", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, msiof_of_match);
+
+static struct platform_driver msiof_driver = {
+ .driver = {
+ .name = "msiof-pcm-audio",
+ .of_match_table = msiof_of_match,
+ },
+ .probe = msiof_probe,
+};
+module_platform_driver(msiof_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car MSIOF I2S audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h
new file mode 100644
index 000000000000..04c70690f7a2
--- /dev/null
+++ b/sound/soc/renesas/rcar/rsnd.h
@@ -0,0 +1,895 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#ifndef RSND_H
+#define RSND_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/sh_dma.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#define RSND_BASE_ADG 0
+#define RSND_BASE_SSI 1
+#define RSND_BASE_SSIU 2
+#define RSND_BASE_SCU 3 // for Gen2/Gen3
+#define RSND_BASE_SDMC 3 // for Gen4 reuse
+#define RSND_BASE_MAX 4
+
+/*
+ * pseudo register
+ *
+ * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different.
+ * This driver uses pseudo register in order to hide it.
+ * see gen1/gen2 for detail
+ */
+enum rsnd_reg {
+ /* SCU (MIX/CTU/DVC) */
+ SRC_I_BUSIF_MODE,
+ SRC_O_BUSIF_MODE,
+ SRC_ROUTE_MODE0,
+ SRC_SWRSR,
+ SRC_SRCIR,
+ SRC_ADINR,
+ SRC_IFSCR,
+ SRC_IFSVR,
+ SRC_SRCCR,
+ SRC_CTRL,
+ SRC_BSDSR,
+ SRC_BSISR,
+ SRC_INT_ENABLE0,
+ SRC_BUSIF_DALIGN,
+ SRCIN_TIMSEL0,
+ SRCIN_TIMSEL1,
+ SRCIN_TIMSEL2,
+ SRCIN_TIMSEL3,
+ SRCIN_TIMSEL4,
+ SRCOUT_TIMSEL0,
+ SRCOUT_TIMSEL1,
+ SRCOUT_TIMSEL2,
+ SRCOUT_TIMSEL3,
+ SRCOUT_TIMSEL4,
+ SCU_SYS_STATUS0,
+ SCU_SYS_STATUS1,
+ SCU_SYS_INT_EN0,
+ SCU_SYS_INT_EN1,
+ CMD_CTRL,
+ CMD_BUSIF_MODE,
+ CMD_BUSIF_DALIGN,
+ CMD_ROUTE_SLCT,
+ CMDOUT_TIMSEL,
+ CTU_SWRSR,
+ CTU_CTUIR,
+ CTU_ADINR,
+ CTU_CPMDR,
+ CTU_SCMDR,
+ CTU_SV00R,
+ CTU_SV01R,
+ CTU_SV02R,
+ CTU_SV03R,
+ CTU_SV04R,
+ CTU_SV05R,
+ CTU_SV06R,
+ CTU_SV07R,
+ CTU_SV10R,
+ CTU_SV11R,
+ CTU_SV12R,
+ CTU_SV13R,
+ CTU_SV14R,
+ CTU_SV15R,
+ CTU_SV16R,
+ CTU_SV17R,
+ CTU_SV20R,
+ CTU_SV21R,
+ CTU_SV22R,
+ CTU_SV23R,
+ CTU_SV24R,
+ CTU_SV25R,
+ CTU_SV26R,
+ CTU_SV27R,
+ CTU_SV30R,
+ CTU_SV31R,
+ CTU_SV32R,
+ CTU_SV33R,
+ CTU_SV34R,
+ CTU_SV35R,
+ CTU_SV36R,
+ CTU_SV37R,
+ MIX_SWRSR,
+ MIX_MIXIR,
+ MIX_ADINR,
+ MIX_MIXMR,
+ MIX_MVPDR,
+ MIX_MDBAR,
+ MIX_MDBBR,
+ MIX_MDBCR,
+ MIX_MDBDR,
+ MIX_MDBER,
+ DVC_SWRSR,
+ DVC_DVUIR,
+ DVC_ADINR,
+ DVC_DVUCR,
+ DVC_ZCMCR,
+ DVC_VOL0R,
+ DVC_VOL1R,
+ DVC_VOL2R,
+ DVC_VOL3R,
+ DVC_VOL4R,
+ DVC_VOL5R,
+ DVC_VOL6R,
+ DVC_VOL7R,
+ DVC_DVUER,
+ DVC_VRCTR,
+ DVC_VRPDR,
+ DVC_VRDBR,
+
+ /* ADG */
+ BRRA,
+ BRRB,
+ BRGCKR,
+ DIV_EN,
+ AUDIO_CLK_SEL0,
+ AUDIO_CLK_SEL1,
+ AUDIO_CLK_SEL2,
+
+ /* SSIU */
+ SSI_MODE,
+ SSI_MODE0,
+ SSI_MODE1,
+ SSI_MODE2,
+ SSI_CONTROL,
+ SSI_CTRL,
+ SSI_BUSIF0_MODE,
+ SSI_BUSIF1_MODE,
+ SSI_BUSIF2_MODE,
+ SSI_BUSIF3_MODE,
+ SSI_BUSIF4_MODE,
+ SSI_BUSIF5_MODE,
+ SSI_BUSIF6_MODE,
+ SSI_BUSIF7_MODE,
+ SSI_BUSIF0_ADINR,
+ SSI_BUSIF1_ADINR,
+ SSI_BUSIF2_ADINR,
+ SSI_BUSIF3_ADINR,
+ SSI_BUSIF4_ADINR,
+ SSI_BUSIF5_ADINR,
+ SSI_BUSIF6_ADINR,
+ SSI_BUSIF7_ADINR,
+ SSI_BUSIF0_DALIGN,
+ SSI_BUSIF1_DALIGN,
+ SSI_BUSIF2_DALIGN,
+ SSI_BUSIF3_DALIGN,
+ SSI_BUSIF4_DALIGN,
+ SSI_BUSIF5_DALIGN,
+ SSI_BUSIF6_DALIGN,
+ SSI_BUSIF7_DALIGN,
+ SSI_INT_ENABLE,
+ SSI_SYS_STATUS0,
+ SSI_SYS_STATUS1,
+ SSI_SYS_STATUS2,
+ SSI_SYS_STATUS3,
+ SSI_SYS_STATUS4,
+ SSI_SYS_STATUS5,
+ SSI_SYS_STATUS6,
+ SSI_SYS_STATUS7,
+ SSI_SYS_INT_ENABLE0,
+ SSI_SYS_INT_ENABLE1,
+ SSI_SYS_INT_ENABLE2,
+ SSI_SYS_INT_ENABLE3,
+ SSI_SYS_INT_ENABLE4,
+ SSI_SYS_INT_ENABLE5,
+ SSI_SYS_INT_ENABLE6,
+ SSI_SYS_INT_ENABLE7,
+ HDMI0_SEL,
+ HDMI1_SEL,
+ SSI9_BUSIF0_MODE,
+ SSI9_BUSIF1_MODE,
+ SSI9_BUSIF2_MODE,
+ SSI9_BUSIF3_MODE,
+ SSI9_BUSIF4_MODE,
+ SSI9_BUSIF5_MODE,
+ SSI9_BUSIF6_MODE,
+ SSI9_BUSIF7_MODE,
+ SSI9_BUSIF0_ADINR,
+ SSI9_BUSIF1_ADINR,
+ SSI9_BUSIF2_ADINR,
+ SSI9_BUSIF3_ADINR,
+ SSI9_BUSIF4_ADINR,
+ SSI9_BUSIF5_ADINR,
+ SSI9_BUSIF6_ADINR,
+ SSI9_BUSIF7_ADINR,
+ SSI9_BUSIF0_DALIGN,
+ SSI9_BUSIF1_DALIGN,
+ SSI9_BUSIF2_DALIGN,
+ SSI9_BUSIF3_DALIGN,
+ SSI9_BUSIF4_DALIGN,
+ SSI9_BUSIF5_DALIGN,
+ SSI9_BUSIF6_DALIGN,
+ SSI9_BUSIF7_DALIGN,
+
+ /* SSI */
+ SSICR,
+ SSISR,
+ SSITDR,
+ SSIRDR,
+ SSIWSR,
+
+ REG_MAX,
+};
+#define SRCIN_TIMSEL(i) (SRCIN_TIMSEL0 + (i))
+#define SRCOUT_TIMSEL(i) (SRCOUT_TIMSEL0 + (i))
+#define CTU_SVxxR(i, j) (CTU_SV00R + (i * 8) + (j))
+#define DVC_VOLxR(i) (DVC_VOL0R + (i))
+#define AUDIO_CLK_SEL(i) (AUDIO_CLK_SEL0 + (i))
+#define SSI_BUSIF_MODE(i) (SSI_BUSIF0_MODE + (i))
+#define SSI_BUSIF_ADINR(i) (SSI_BUSIF0_ADINR + (i))
+#define SSI_BUSIF_DALIGN(i) (SSI_BUSIF0_DALIGN + (i))
+#define SSI9_BUSIF_MODE(i) (SSI9_BUSIF0_MODE + (i))
+#define SSI9_BUSIF_ADINR(i) (SSI9_BUSIF0_ADINR + (i))
+#define SSI9_BUSIF_DALIGN(i) (SSI9_BUSIF0_DALIGN + (i))
+#define SSI_SYS_STATUS(i) (SSI_SYS_STATUS0 + (i))
+#define SSI_SYS_INT_ENABLE(i) (SSI_SYS_INT_ENABLE0 + (i))
+
+
+struct rsnd_priv;
+struct rsnd_mod;
+struct rsnd_dai;
+struct rsnd_dai_stream;
+
+/*
+ * R-Car basic functions
+ */
+u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg);
+void rsnd_mod_write(struct rsnd_mod *mod, enum rsnd_reg reg, u32 data);
+void rsnd_mod_bset(struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data);
+u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+
+/*
+ * R-Car DMA
+ */
+int rsnd_dma_attach(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod, struct rsnd_mod **dma_mod);
+int rsnd_dma_probe(struct rsnd_priv *priv);
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
+ struct rsnd_mod *mod, char *x);
+
+/*
+ * R-Car sound mod
+ */
+enum rsnd_mod_type {
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ RSND_MOD_SSIM3, /* SSI multi 3 */
+ RSND_MOD_SSIM2, /* SSI multi 2 */
+ RSND_MOD_SSIM1, /* SSI multi 1 */
+ RSND_MOD_SSIP, /* SSI parent */
+ RSND_MOD_SSI,
+ RSND_MOD_SSIU,
+ RSND_MOD_MAX,
+};
+
+struct rsnd_mod_ops {
+ char *name;
+ struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod);
+ int (*probe)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*remove)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*init)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*quit)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*start)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*stop)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*irq)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv, int enable);
+ int (*pcm_new)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd);
+ int (*hw_params)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params);
+ int (*pointer)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ snd_pcm_uframes_t *pointer);
+ int (*fallback)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*prepare)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*cleanup)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*hw_free)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream);
+ u32 *(*get_status)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type);
+ int (*id)(struct rsnd_mod *mod);
+ int (*id_sub)(struct rsnd_mod *mod);
+ int (*id_cmd)(struct rsnd_mod *mod);
+
+#ifdef CONFIG_DEBUG_FS
+ void (*debug_info)(struct seq_file *m,
+ struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+#endif
+};
+
+struct rsnd_dai_stream;
+struct rsnd_mod {
+ int id;
+ enum rsnd_mod_type type;
+ struct rsnd_mod_ops *ops;
+ struct rsnd_priv *priv;
+ struct clk *clk;
+ u32 status;
+};
+/*
+ * status
+ *
+ * 0xH000DCB0
+ *
+ * B 0: init 1: quit
+ * C 0: start 1: stop
+ * D 0: hw_params 1: hw_free
+ *
+ * H is always called (see __rsnd_mod_call)
+ */
+#define __rsnd_mod_shift_init 4
+#define __rsnd_mod_shift_quit 4
+#define __rsnd_mod_shift_start 8
+#define __rsnd_mod_shift_stop 8
+#define __rsnd_mod_shift_hw_params 12
+#define __rsnd_mod_shift_hw_free 12
+#define __rsnd_mod_shift_probe 28 /* always called */
+#define __rsnd_mod_shift_remove 28 /* always called */
+#define __rsnd_mod_shift_irq 28 /* always called */
+#define __rsnd_mod_shift_pcm_new 28 /* always called */
+#define __rsnd_mod_shift_fallback 28 /* always called */
+#define __rsnd_mod_shift_pointer 28 /* always called */
+#define __rsnd_mod_shift_prepare 28 /* always called */
+#define __rsnd_mod_shift_cleanup 28 /* always called */
+
+#define __rsnd_mod_add_probe 0
+#define __rsnd_mod_add_remove 0
+#define __rsnd_mod_add_prepare 0
+#define __rsnd_mod_add_cleanup 0
+#define __rsnd_mod_add_init 1 /* needs protect */
+#define __rsnd_mod_add_quit -1 /* needs protect */
+#define __rsnd_mod_add_start 1 /* needs protect */
+#define __rsnd_mod_add_stop -1 /* needs protect */
+#define __rsnd_mod_add_hw_params 1 /* needs protect */
+#define __rsnd_mod_add_hw_free -1 /* needs protect */
+#define __rsnd_mod_add_irq 0
+#define __rsnd_mod_add_pcm_new 0
+#define __rsnd_mod_add_fallback 0
+#define __rsnd_mod_add_pointer 0
+
+#define __rsnd_mod_call_probe 0
+#define __rsnd_mod_call_remove 0
+#define __rsnd_mod_call_prepare 0
+#define __rsnd_mod_call_cleanup 0
+#define __rsnd_mod_call_init 0 /* needs protect */
+#define __rsnd_mod_call_quit 1 /* needs protect */
+#define __rsnd_mod_call_start 0 /* needs protect */
+#define __rsnd_mod_call_stop 1 /* needs protect */
+#define __rsnd_mod_call_hw_params 0 /* needs protect */
+#define __rsnd_mod_call_hw_free 1 /* needs protect */
+#define __rsnd_mod_call_irq 0
+#define __rsnd_mod_call_pcm_new 0
+#define __rsnd_mod_call_fallback 0
+#define __rsnd_mod_call_pointer 0
+
+#define rsnd_mod_to_priv(mod) ((mod)->priv)
+#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
+#define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
+#define rsnd_mod_get(ip) (&(ip)->mod)
+
+int rsnd_mod_init(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_mod_ops *ops,
+ struct clk *clk,
+ enum rsnd_mod_type type,
+ int id);
+void rsnd_mod_quit(struct rsnd_mod *mod);
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod);
+void rsnd_mod_interrupt(struct rsnd_mod *mod,
+ void (*callback)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io));
+u32 *rsnd_mod_get_status(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type);
+int rsnd_mod_id(struct rsnd_mod *mod);
+int rsnd_mod_id_raw(struct rsnd_mod *mod);
+int rsnd_mod_id_sub(struct rsnd_mod *mod);
+char *rsnd_mod_name(struct rsnd_mod *mod);
+struct rsnd_mod *rsnd_mod_next(int *iterator,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type *array,
+ int array_size);
+#define for_each_rsnd_mod(iterator, pos, io) \
+ for (iterator = 0; \
+ (pos = rsnd_mod_next(&iterator, io, NULL, 0)); iterator++)
+#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size) \
+ for (iterator = 0; \
+ (pos = rsnd_mod_next(&iterator, io, array, size)); iterator++)
+#define for_each_rsnd_mod_array(iterator, pos, io, array) \
+ for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array))
+
+void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
+ struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
+ struct device_node *node,
+ struct device_node *playback,
+ struct device_node *capture);
+int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name);
+int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx);
+
+int rsnd_channel_normalization(int chan);
+#define rsnd_runtime_channel_original(io) \
+ rsnd_runtime_channel_original_with_params(io, NULL)
+int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io,
+ struct snd_pcm_hw_params *params);
+#define rsnd_runtime_channel_after_ctu(io) \
+ rsnd_runtime_channel_after_ctu_with_params(io, NULL)
+int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io,
+ struct snd_pcm_hw_params *params);
+#define rsnd_runtime_channel_for_ssi(io) \
+ rsnd_runtime_channel_for_ssi_with_params(io, NULL)
+int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io,
+ struct snd_pcm_hw_params *params);
+int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io);
+
+/*
+ * DT
+ */
+#define rsnd_parse_of_node(priv, node) \
+ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node)
+#define RSND_NODE_DAI "rcar_sound,dai"
+#define RSND_NODE_SSI "rcar_sound,ssi"
+#define RSND_NODE_SSIU "rcar_sound,ssiu"
+#define RSND_NODE_SRC "rcar_sound,src"
+#define RSND_NODE_CTU "rcar_sound,ctu"
+#define RSND_NODE_MIX "rcar_sound,mix"
+#define RSND_NODE_DVC "rcar_sound,dvc"
+
+/*
+ * R-Car sound DAI
+ */
+#define RSND_DAI_NAME_SIZE 16
+struct rsnd_dai_stream {
+ char name[RSND_DAI_NAME_SIZE];
+ struct snd_pcm_substream *substream;
+ struct rsnd_mod *mod[RSND_MOD_MAX];
+ struct rsnd_mod *dma;
+ struct rsnd_dai *rdai;
+ struct device *dmac_dev; /* for IPMMU */
+ u32 converted_rate; /* converted sampling rate */
+ int converted_chan; /* converted channels */
+ u32 parent_ssi_status;
+ u32 flags;
+};
+
+/* flags */
+#define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */
+#define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */
+#define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */
+#define RSND_HW_RULE_ERR (1 << 3) /* hw_rule error */
+
+#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
+#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
+#define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU)
+#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
+#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
+#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
+#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
+#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
+#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD)
+#define rsnd_io_to_rdai(io) ((io)->rdai)
+#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
+#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+ (io)->substream->runtime : NULL)
+#define rsnd_io_converted_rate(io) ((io)->converted_rate)
+#define rsnd_io_converted_chan(io) ((io)->converted_chan)
+int rsnd_io_is_working(struct rsnd_dai_stream *io);
+
+struct rsnd_dai {
+ char name[RSND_DAI_NAME_SIZE];
+ struct rsnd_dai_stream playback;
+ struct rsnd_dai_stream capture;
+ struct rsnd_priv *priv;
+ struct snd_pcm_hw_constraint_list constraint;
+ struct of_phandle_args dai_args;
+
+ int max_channels; /* 2ch - 16ch */
+ int ssi_lane; /* 1lane - 4lane */
+ int chan_width; /* 16/24/32 bit width */
+
+ unsigned int clk_master:1;
+ unsigned int bit_clk_inv:1;
+ unsigned int frm_clk_inv:1;
+ unsigned int sys_delay:1;
+ unsigned int data_alignment:1;
+};
+
+#define rsnd_rdai_nr(priv) ((priv)->rdai_nr)
+#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
+#define rsnd_rdai_to_priv(rdai) ((rdai)->priv)
+#define for_each_rsnd_dai(rdai, priv, i) \
+ for (i = 0; \
+ (i < rsnd_rdai_nr(priv)) && \
+ ((rdai) = rsnd_rdai_get(priv, i)); \
+ i++)
+
+struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
+
+#define rsnd_rdai_channels_set(rdai, max_channels) \
+ rsnd_rdai_channels_ctrl(rdai, max_channels)
+#define rsnd_rdai_channels_get(rdai) \
+ rsnd_rdai_channels_ctrl(rdai, 0)
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
+ int max_channels);
+
+#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \
+ rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane)
+#define rsnd_rdai_ssi_lane_get(rdai) \
+ rsnd_rdai_ssi_lane_ctrl(rdai, 0)
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
+ int ssi_lane);
+
+#define rsnd_rdai_width_set(rdai, width) \
+ rsnd_rdai_width_ctrl(rdai, width)
+#define rsnd_rdai_width_get(rdai) \
+ rsnd_rdai_width_ctrl(rdai, 0)
+int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width);
+int rsnd_dai_connect(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type);
+
+/*
+ * R-Car Gen1/Gen2
+ */
+int rsnd_gen_probe(struct rsnd_priv *priv);
+void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ enum rsnd_reg reg);
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
+#ifdef CONFIG_DEBUG_FS
+void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id);
+#endif
+
+/*
+ * R-Car ADG
+ */
+int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate);
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod);
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate);
+int rsnd_adg_probe(struct rsnd_priv *priv);
+void rsnd_adg_remove(struct rsnd_priv *priv);
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate);
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
+ struct rsnd_dai_stream *io);
+#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1)
+#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0)
+int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
+void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m);
+
+/*
+ * R-Car sound priv
+ */
+struct rsnd_priv {
+
+ struct platform_device *pdev;
+ spinlock_t lock;
+ unsigned long flags;
+#define RSND_GEN_MASK (0xF << 0)
+#define RSND_GEN1 (1 << 0)
+#define RSND_GEN2 (2 << 0)
+#define RSND_GEN3 (3 << 0)
+#define RSND_GEN4 (4 << 0)
+#define RSND_SOC_MASK (0xFF << 4)
+#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */
+
+ /*
+ * below value will be filled on rsnd_gen_probe()
+ */
+ void *gen;
+
+ /*
+ * below value will be filled on rsnd_adg_probe()
+ */
+ void *adg;
+
+ /*
+ * below value will be filled on rsnd_dma_probe()
+ */
+ void *dma;
+
+ /*
+ * below value will be filled on rsnd_ssi_probe()
+ */
+ void *ssi;
+ int ssi_nr;
+
+ /*
+ * below value will be filled on rsnd_ssiu_probe()
+ */
+ void *ssiu;
+ int ssiu_nr;
+
+ /*
+ * below value will be filled on rsnd_src_probe()
+ */
+ void *src;
+ int src_nr;
+
+ /*
+ * below value will be filled on rsnd_ctu_probe()
+ */
+ void *ctu;
+ int ctu_nr;
+
+ /*
+ * below value will be filled on rsnd_mix_probe()
+ */
+ void *mix;
+ int mix_nr;
+
+ /*
+ * below value will be filled on rsnd_dvc_probe()
+ */
+ void *dvc;
+ int dvc_nr;
+
+ /*
+ * below value will be filled on rsnd_cmd_probe()
+ */
+ void *cmd;
+ int cmd_nr;
+
+ /*
+ * below value will be filled on rsnd_dai_probe()
+ */
+ struct snd_soc_dai_driver *daidrv;
+ struct rsnd_dai *rdai;
+ int rdai_nr;
+
+#define RSND_MAX_COMPONENT 3
+ int component_dais[RSND_MAX_COMPONENT];
+};
+
+#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
+#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
+
+#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
+#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
+#define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3)
+#define rsnd_is_gen4(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN4)
+#define rsnd_is_gen3_e3(priv) (((priv)->flags & \
+ (RSND_GEN_MASK | RSND_SOC_MASK)) == \
+ (RSND_GEN3 | RSND_SOC_E))
+
+#define rsnd_flags_has(p, f) ((p)->flags & (f))
+#define rsnd_flags_set(p, f) ((p)->flags |= (f))
+#define rsnd_flags_del(p, f) ((p)->flags &= ~(f))
+
+/*
+ * rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+ unsigned int max;
+ unsigned int size;
+ u32 *val;
+ const char * const *texts;
+ int (*accept)(struct rsnd_dai_stream *io);
+ void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+ struct rsnd_dai_stream *io;
+ struct snd_card *card;
+ struct snd_kcontrol *kctrl;
+ struct rsnd_mod *mod;
+};
+
+#define RSND_MAX_CHANNELS 8
+struct rsnd_kctrl_cfg_m {
+ struct rsnd_kctrl_cfg cfg;
+ u32 val[RSND_MAX_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+ struct rsnd_kctrl_cfg cfg;
+ u32 val;
+};
+#define rsnd_kctrl_size(x) ((x).cfg.size)
+#define rsnd_kctrl_max(x) ((x).cfg.max)
+#define rsnd_kctrl_valm(x, i) ((x).val[i]) /* = (x).cfg.val[i] */
+#define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */
+
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg);
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ int (*accept)(struct rsnd_dai_stream *io),
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg *cfg,
+ const char * const *texts,
+ int size,
+ u32 max);
+
+#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \
+ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \
+ NULL, size, max)
+
+#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \
+ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
+ NULL, 1, max)
+
+#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts, size) \
+ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
+ texts, 1, size)
+
+extern const char * const volume_ramp_rate[];
+#define VOLUME_RAMP_MAX_DVC (0x17 + 1)
+#define VOLUME_RAMP_MAX_MIX (0x0a + 1)
+
+/*
+ * R-Car SSI
+ */
+int rsnd_ssi_probe(struct rsnd_priv *priv);
+void rsnd_ssi_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
+
+#define rsnd_ssi_is_pin_sharing(io) \
+ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+
+#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI)
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture);
+unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai,
+ int param1, int param2, int *idx);
+
+/*
+ * R-Car SSIU
+ */
+int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod);
+int rsnd_ssiu_probe(struct rsnd_priv *priv);
+void rsnd_ssiu_remove(struct rsnd_priv *priv);
+void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture);
+#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU)
+bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod);
+
+/*
+ * R-Car SRC
+ */
+int rsnd_src_probe(struct rsnd_priv *priv);
+void rsnd_src_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
+
+#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1)
+#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ int is_in);
+
+#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC)
+#define rsnd_parse_connect_src(rdai, playback, capture) \
+ rsnd_parse_connect_common(rdai, "src", rsnd_src_mod_get, \
+ rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
+ playback, capture)
+
+/*
+ * R-Car CTU
+ */
+int rsnd_ctu_probe(struct rsnd_priv *priv);
+void rsnd_ctu_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU)
+#define rsnd_parse_connect_ctu(rdai, playback, capture) \
+ rsnd_parse_connect_common(rdai, "ctu", rsnd_ctu_mod_get, \
+ rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
+ playback, capture)
+
+/*
+ * R-Car MIX
+ */
+int rsnd_mix_probe(struct rsnd_priv *priv);
+void rsnd_mix_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX)
+#define rsnd_parse_connect_mix(rdai, playback, capture) \
+ rsnd_parse_connect_common(rdai, "mix", rsnd_mix_mod_get, \
+ rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
+ playback, capture)
+
+/*
+ * R-Car DVC
+ */
+int rsnd_dvc_probe(struct rsnd_priv *priv);
+void rsnd_dvc_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC)
+#define rsnd_parse_connect_dvc(rdai, playback, capture) \
+ rsnd_parse_connect_common(rdai, "dvc", rsnd_dvc_mod_get, \
+ rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
+ playback, capture)
+
+/*
+ * R-Car CMD
+ */
+int rsnd_cmd_probe(struct rsnd_priv *priv);
+void rsnd_cmd_remove(struct rsnd_priv *priv);
+int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
+
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+
+/*
+ * If you don't need interrupt status debug message,
+ * define RSND_DEBUG_NO_IRQ_STATUS as 1 on top of src.c/ssi.c
+ *
+ * #define RSND_DEBUG_NO_IRQ_STATUS 1
+ */
+#define rsnd_print_irq_status(dev, param...) do { \
+ if (!IS_BUILTIN(RSND_DEBUG_NO_IRQ_STATUS)) \
+ dev_info(dev, param); \
+} while (0)
+
+#ifdef CONFIG_DEBUG_FS
+int rsnd_debugfs_probe(struct snd_soc_component *component);
+void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr,
+ void __iomem *base, int offset, int size);
+void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod,
+ int reg_id, int offset, int size);
+
+#else
+#define rsnd_debugfs_probe NULL
+#endif
+
+#endif /* RSND_H */
diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c
new file mode 100644
index 000000000000..6a3dbc84f474
--- /dev/null
+++ b/sound/soc/renesas/rcar/src.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SRC support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ * You can use Synchronous Sampling Rate Convert (if no DVC)
+ *
+ * amixer set "SRC Out Rate" on
+ * aplay xxx.wav &
+ * amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz
+ * amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz
+ */
+
+/*
+ * you can enable below define if you don't need
+ * SSI interrupt status debug message when debugging
+ * see rsnd_print_irq_status()
+ *
+ * #define RSND_DEBUG_NO_IRQ_STATUS 1
+ */
+
+#include <linux/of_irq.h>
+#include "rsnd.h"
+
+#define SRC_NAME "src"
+
+/* SCU_SYSTEM_STATUS0/1 */
+#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id))
+
+struct rsnd_src {
+ struct rsnd_mod mod;
+ struct rsnd_mod *dma;
+ struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
+ struct rsnd_kctrl_cfg_s sync; /* sync convert */
+ u32 current_sync_rate;
+ int irq;
+};
+
+#define RSND_SRC_NAME_SIZE 16
+
+#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
+#define rsnd_src_nr(priv) ((priv)->src_nr)
+#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
+
+#define rsnd_mod_to_src(_mod) \
+ container_of((_mod), struct rsnd_src, mod)
+
+#define for_each_rsnd_src(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_src_nr(priv)) && \
+ ((pos) = (struct rsnd_src *)(priv)->src + i); \
+ i++)
+
+
+/*
+ * image of SRC (Sampling Rate Converter)
+ *
+ * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+
+ * 48kHz <-> | SRC | <------> | SSI | <-----> | codec |
+ * 44.1kHz <-> +-----+ +-----+ +-------+
+ * ...
+ *
+ */
+
+static void rsnd_src_activation(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, SRC_SWRSR, 0);
+ rsnd_mod_write(mod, SRC_SWRSR, 1);
+}
+
+static void rsnd_src_halt(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, SRC_SRCIR, 1);
+ rsnd_mod_write(mod, SRC_SWRSR, 0);
+}
+
+static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int is_play = rsnd_io_is_play(io);
+
+ return rsnd_dma_request_channel(rsnd_src_of_node(priv),
+ SRC_NAME, mod,
+ is_play ? "rx" : "tx");
+}
+
+static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ u32 convert_rate;
+
+ if (!runtime)
+ return 0;
+
+ if (!rsnd_src_sync_is_enabled(mod))
+ return rsnd_io_converted_rate(io);
+
+ convert_rate = src->current_sync_rate;
+
+ if (!convert_rate)
+ convert_rate = rsnd_io_converted_rate(io);
+
+ if (!convert_rate)
+ convert_rate = runtime->rate;
+
+ return convert_rate;
+}
+
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ int is_in)
+{
+ struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ unsigned int rate = 0;
+ int is_play = rsnd_io_is_play(io);
+
+ /*
+ * Playback
+ * runtime_rate -> [SRC] -> convert_rate
+ *
+ * Capture
+ * convert_rate -> [SRC] -> runtime_rate
+ */
+
+ if (is_play == is_in)
+ return runtime->rate;
+
+ /*
+ * return convert rate if SRC is used,
+ * otherwise, return runtime->rate as usual
+ */
+ if (src_mod)
+ rate = rsnd_src_convert_rate(io, src_mod);
+
+ if (!rate)
+ rate = runtime->rate;
+
+ return rate;
+}
+
+static const u32 bsdsr_table_pattern1[] = {
+ 0x01800000, /* 6 - 1/6 */
+ 0x01000000, /* 6 - 1/4 */
+ 0x00c00000, /* 6 - 1/3 */
+ 0x00800000, /* 6 - 1/2 */
+ 0x00600000, /* 6 - 2/3 */
+ 0x00400000, /* 6 - 1 */
+};
+
+static const u32 bsdsr_table_pattern2[] = {
+ 0x02400000, /* 6 - 1/6 */
+ 0x01800000, /* 6 - 1/4 */
+ 0x01200000, /* 6 - 1/3 */
+ 0x00c00000, /* 6 - 1/2 */
+ 0x00900000, /* 6 - 2/3 */
+ 0x00600000, /* 6 - 1 */
+};
+
+static const u32 bsisr_table[] = {
+ 0x00100060, /* 6 - 1/6 */
+ 0x00100040, /* 6 - 1/4 */
+ 0x00100030, /* 6 - 1/3 */
+ 0x00100020, /* 6 - 1/2 */
+ 0x00100020, /* 6 - 2/3 */
+ 0x00100020, /* 6 - 1 */
+};
+
+static const u32 chan288888[] = {
+ 0x00000006, /* 1 to 2 */
+ 0x000001fe, /* 1 to 8 */
+ 0x000001fe, /* 1 to 8 */
+ 0x000001fe, /* 1 to 8 */
+ 0x000001fe, /* 1 to 8 */
+ 0x000001fe, /* 1 to 8 */
+};
+
+static const u32 chan244888[] = {
+ 0x00000006, /* 1 to 2 */
+ 0x0000001e, /* 1 to 4 */
+ 0x0000001e, /* 1 to 4 */
+ 0x000001fe, /* 1 to 8 */
+ 0x000001fe, /* 1 to 8 */
+ 0x000001fe, /* 1 to 8 */
+};
+
+static const u32 chan222222[] = {
+ 0x00000006, /* 1 to 2 */
+ 0x00000006, /* 1 to 2 */
+ 0x00000006, /* 1 to 2 */
+ 0x00000006, /* 1 to 2 */
+ 0x00000006, /* 1 to 2 */
+ 0x00000006, /* 1 to 2 */
+};
+
+static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ u32 fin, fout, new_rate;
+ int inc, cnt, rate;
+ u64 base, val;
+
+ if (!runtime)
+ return;
+
+ if (!rsnd_src_sync_is_enabled(mod))
+ return;
+
+ fin = rsnd_src_get_in_rate(priv, io);
+ fout = rsnd_src_get_out_rate(priv, io);
+
+ new_rate = src->sync.val;
+
+ if (!new_rate)
+ new_rate = fout;
+
+ /* Do nothing if no diff */
+ if (new_rate == src->current_sync_rate)
+ return;
+
+ /*
+ * SRCm_IFSVR::INTIFS can change within 1%
+ * see
+ * SRCm_IFSVR::INTIFS Note
+ */
+ inc = fout / 100;
+ cnt = abs(new_rate - fout) / inc;
+ if (fout > new_rate)
+ inc *= -1;
+
+ /*
+ * After start running SRC, we can update only SRC_IFSVR
+ * for Synchronous Mode
+ */
+ base = (u64)0x0400000 * fin;
+ rate = fout;
+ for (int i = 0; i < cnt; i++) {
+ val = base;
+ rate += inc;
+ do_div(val, rate);
+
+ rsnd_mod_write(mod, SRC_IFSVR, val);
+ }
+ val = base;
+ do_div(val, new_rate);
+
+ rsnd_mod_write(mod, SRC_IFSVR, val);
+
+ /* update current_sync_rate */
+ src->current_sync_rate = new_rate;
+}
+
+static void rsnd_src_init_convert_rate(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int is_play = rsnd_io_is_play(io);
+ int use_src = 0;
+ u32 fin, fout;
+ u32 ifscr, adinr;
+ u32 cr, route;
+ u32 i_busif, o_busif, tmp;
+ const u32 *bsdsr_table;
+ const u32 *chptn;
+ uint ratio;
+ int chan;
+ int idx;
+
+ if (!runtime)
+ return;
+
+ fin = rsnd_src_get_in_rate(priv, io);
+ fout = rsnd_src_get_out_rate(priv, io);
+
+ chan = rsnd_runtime_channel_original(io);
+
+ /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
+ if (fin == fout)
+ ratio = 0;
+ else if (fin > fout)
+ ratio = 100 * fin / fout;
+ else
+ ratio = 100 * fout / fin;
+
+ if (ratio > 600) {
+ dev_err(dev, "FSO/FSI ratio error\n");
+ return;
+ }
+
+ use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod);
+
+ /*
+ * SRC_ADINR
+ */
+ adinr = rsnd_get_adinr_bit(mod, io) | chan;
+
+ /*
+ * SRC_IFSCR
+ * SRC_SRCCR / SRC_ROUTE_MODE0
+ */
+ ifscr = 0;
+ cr = 0x00011110;
+ route = 0x0;
+ if (use_src) {
+ route = 0x1;
+ ifscr = 0x1;
+
+ if (rsnd_src_sync_is_enabled(mod)) {
+ cr |= 0x1;
+ route |= rsnd_io_is_play(io) ?
+ (0x1 << 24) : (0x1 << 25);
+ }
+ }
+
+ /*
+ * SRC_BSDSR / SRC_BSISR
+ *
+ * see
+ * Combination of Register Setting Related to
+ * FSO/FSI Ratio and Channel, Latency
+ */
+ switch (rsnd_mod_id(mod)) {
+ case 0:
+ chptn = chan288888;
+ bsdsr_table = bsdsr_table_pattern1;
+ break;
+ case 1:
+ case 3:
+ case 4:
+ chptn = chan244888;
+ bsdsr_table = bsdsr_table_pattern1;
+ break;
+ case 2:
+ case 9:
+ chptn = chan222222;
+ bsdsr_table = bsdsr_table_pattern1;
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ chptn = chan222222;
+ bsdsr_table = bsdsr_table_pattern2;
+ break;
+ default:
+ goto convert_rate_err;
+ }
+
+ /*
+ * E3 need to overwrite
+ */
+ if (rsnd_is_gen3_e3(priv))
+ switch (rsnd_mod_id(mod)) {
+ case 0:
+ case 4:
+ chptn = chan222222;
+ }
+
+ for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++)
+ if (chptn[idx] & (1 << chan))
+ break;
+
+ if (chan > 8 ||
+ idx >= ARRAY_SIZE(chan222222))
+ goto convert_rate_err;
+
+ /* BUSIF_MODE */
+ tmp = rsnd_get_busif_shift(io, mod);
+ i_busif = ( is_play ? tmp : 0) | 1;
+ o_busif = (!is_play ? tmp : 0) | 1;
+
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
+ rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
+ rsnd_mod_write(mod, SRC_ADINR, adinr);
+ rsnd_mod_write(mod, SRC_IFSCR, ifscr);
+ rsnd_mod_write(mod, SRC_SRCCR, cr);
+ rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]);
+ rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]);
+ rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
+
+ rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif);
+ rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif);
+
+ rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
+
+ rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
+
+ /* update SRC_IFSVR */
+ rsnd_src_set_convert_rate(io, mod);
+
+ return;
+
+convert_rate_err:
+ dev_err(dev, "unknown BSDSR/BSDIR settings\n");
+}
+
+static int rsnd_src_irq(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv,
+ int enable)
+{
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ u32 sys_int_val, int_val, sys_int_mask;
+ int irq = src->irq;
+ int id = rsnd_mod_id(mod);
+
+ sys_int_val =
+ sys_int_mask = OUF_SRC(id);
+ int_val = 0x3300;
+
+ /*
+ * IRQ is not supported on non-DT
+ * see
+ * rsnd_src_probe_()
+ */
+ if ((irq <= 0) || !enable) {
+ sys_int_val = 0;
+ int_val = 0;
+ }
+
+ /*
+ * WORKAROUND
+ *
+ * ignore over flow error when rsnd_src_sync_is_enabled()
+ */
+ if (rsnd_src_sync_is_enabled(mod))
+ sys_int_val = sys_int_val & 0xffff;
+
+ rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
+ rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
+ rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
+
+ return 0;
+}
+
+static void rsnd_src_status_clear(struct rsnd_mod *mod)
+{
+ u32 val = OUF_SRC(rsnd_mod_id(mod));
+
+ rsnd_mod_write(mod, SCU_SYS_STATUS0, val);
+ rsnd_mod_write(mod, SCU_SYS_STATUS1, val);
+}
+
+static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 val0, val1;
+ u32 status0, status1;
+ bool ret = false;
+
+ val0 = val1 = OUF_SRC(rsnd_mod_id(mod));
+
+ /*
+ * WORKAROUND
+ *
+ * ignore over flow error when rsnd_src_sync_is_enabled()
+ */
+ if (rsnd_src_sync_is_enabled(mod))
+ val0 = val0 & 0xffff;
+
+ status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0);
+ status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1);
+ if ((status0 & val0) || (status1 & val1)) {
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n",
+ rsnd_mod_name(mod), status0, status1);
+
+ ret = true;
+ }
+
+ return ret;
+}
+
+static int rsnd_src_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ u32 val;
+
+ /*
+ * WORKAROUND
+ *
+ * Enable SRC output if you want to use sync convert together with DVC
+ */
+ val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
+ 0x01 : 0x11;
+
+ rsnd_mod_write(mod, SRC_CTRL, val);
+
+ return 0;
+}
+
+static int rsnd_src_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_write(mod, SRC_CTRL, 0);
+
+ return 0;
+}
+
+static int rsnd_src_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ int ret;
+
+ /* reset sync convert_rate */
+ src->sync.val =
+ src->current_sync_rate = 0;
+
+ ret = rsnd_mod_power_on(mod);
+ if (ret < 0)
+ return ret;
+
+ rsnd_src_activation(mod);
+
+ rsnd_src_init_convert_rate(io, mod);
+
+ rsnd_src_status_clear(mod);
+
+ return 0;
+}
+
+static int rsnd_src_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+ rsnd_src_halt(mod);
+
+ rsnd_mod_power_off(mod);
+
+ /* reset sync convert_rate */
+ src->sync.val =
+ src->current_sync_rate = 0;
+
+ return 0;
+}
+
+static void __rsnd_src_interrupt(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ bool stop = false;
+
+ scoped_guard(spinlock, &priv->lock) {
+ /* ignore all cases if not working */
+ if (!rsnd_io_is_working(io))
+ break;
+
+ if (rsnd_src_error_occurred(mod))
+ stop = true;
+
+ rsnd_src_status_clear(mod);
+ }
+
+ if (stop)
+ snd_pcm_stop_xrun(io->substream);
+}
+
+static irqreturn_t rsnd_src_interrupt(int irq, void *data)
+{
+ struct rsnd_mod *mod = data;
+
+ rsnd_mod_interrupt(mod, __rsnd_src_interrupt);
+
+ return IRQ_HANDLED;
+}
+
+static int rsnd_src_kctrl_accept_runtime(struct rsnd_dai_stream *io)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+ if (!runtime) {
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "\"SRC Out Rate\" can use during running\n");
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int rsnd_src_probe_(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int irq = src->irq;
+ int ret;
+
+ if (irq > 0) {
+ /*
+ * IRQ is not supported on non-DT
+ * see
+ * rsnd_src_irq()
+ */
+ ret = devm_request_irq(dev, irq,
+ rsnd_src_interrupt,
+ IRQF_SHARED,
+ dev_name(dev), mod);
+ if (ret)
+ return ret;
+ }
+
+ ret = rsnd_dma_attach(io, mod, &src->dma);
+
+ return ret;
+}
+
+static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ int ret;
+
+ /*
+ * enable SRC sync convert if possible
+ */
+
+ /*
+ * It can't use SRC Synchronous convert
+ * when Capture if it uses CMD
+ */
+ if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
+ return 0;
+
+ /*
+ * enable sync convert
+ */
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
+ rsnd_io_is_play(io) ?
+ "SRC Out Rate Switch" :
+ "SRC In Rate Switch",
+ rsnd_kctrl_accept_anytime,
+ rsnd_src_init_convert_rate,
+ &src->sen, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_kctrl_new_s(mod, io, rtd,
+ rsnd_io_is_play(io) ?
+ "SRC Out Rate" :
+ "SRC In Rate",
+ rsnd_src_kctrl_accept_runtime,
+ rsnd_src_set_convert_rate,
+ &src->sync, 192000);
+
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_src_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ rsnd_mod_id(mod) * 0x20, 0x20);
+ seq_puts(m, "\n");
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ 0x1c0, 0x20);
+ seq_puts(m, "\n");
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU,
+ 0x200 + rsnd_mod_id(mod) * 0x40, 0x40);
+}
+#define DEBUG_INFO .debug_info = rsnd_src_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
+static struct rsnd_mod_ops rsnd_src_ops = {
+ .name = SRC_NAME,
+ .dma_req = rsnd_src_dma_req,
+ .probe = rsnd_src_probe_,
+ .init = rsnd_src_init,
+ .quit = rsnd_src_quit,
+ .start = rsnd_src_start,
+ .stop = rsnd_src_stop,
+ .irq = rsnd_src_irq,
+ .pcm_new = rsnd_src_pcm_new,
+ .get_status = rsnd_mod_get_status,
+ DEBUG_INFO
+};
+
+struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get(rsnd_src_get(priv, id));
+}
+
+int rsnd_src_probe(struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_src *src;
+ struct clk *clk;
+ char name[RSND_SRC_NAME_SIZE];
+ int i, nr, ret;
+
+ node = rsnd_src_of_node(priv);
+ if (!node)
+ return 0; /* not used is not error */
+
+ nr = rsnd_node_count(priv, node, SRC_NAME);
+ if (!nr) {
+ ret = -EINVAL;
+ goto rsnd_src_probe_done;
+ }
+
+ src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL);
+ if (!src) {
+ ret = -ENOMEM;
+ goto rsnd_src_probe_done;
+ }
+
+ priv->src_nr = nr;
+ priv->src = src;
+
+ i = 0;
+ for_each_child_of_node_scoped(node, np) {
+ if (!of_device_is_available(np))
+ goto skip;
+
+ i = rsnd_node_fixed_index(dev, np, SRC_NAME, i);
+ if (i < 0) {
+ ret = -EINVAL;
+ goto rsnd_src_probe_done;
+ }
+
+ src = rsnd_src_get(priv, i);
+
+ snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
+ SRC_NAME, i);
+
+ src->irq = irq_of_parse_and_map(np, 0);
+ if (!src->irq) {
+ ret = -EINVAL;
+ goto rsnd_src_probe_done;
+ }
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto rsnd_src_probe_done;
+ }
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(src),
+ &rsnd_src_ops, clk, RSND_MOD_SRC, i);
+ if (ret)
+ goto rsnd_src_probe_done;
+
+skip:
+ i++;
+ }
+
+ ret = 0;
+
+rsnd_src_probe_done:
+ of_node_put(node);
+
+ return ret;
+}
+
+void rsnd_src_remove(struct rsnd_priv *priv)
+{
+ struct rsnd_src *src;
+ int i;
+
+ for_each_rsnd_src(src, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(src));
+ }
+}
diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c
new file mode 100644
index 000000000000..0420041e282c
--- /dev/null
+++ b/sound/soc/renesas/rcar/ssi.c
@@ -0,0 +1,1252 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SSIU/SSI support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// Based on fsi.c
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+
+/*
+ * you can enable below define if you don't need
+ * SSI interrupt status debug message when debugging
+ * see rsnd_print_irq_status()
+ *
+ * #define RSND_DEBUG_NO_IRQ_STATUS 1
+ */
+
+#include <sound/simple_card_utils.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include "rsnd.h"
+#define RSND_SSI_NAME_SIZE 16
+
+/*
+ * SSICR
+ */
+#define FORCE (1u << 31) /* Fixed */
+#define DMEN (1u << 28) /* DMA Enable */
+#define UIEN (1u << 27) /* Underflow Interrupt Enable */
+#define OIEN (1u << 26) /* Overflow Interrupt Enable */
+#define IIEN (1u << 25) /* Idle Mode Interrupt Enable */
+#define DIEN (1u << 24) /* Data Interrupt Enable */
+#define CHNL_4 (1u << 22) /* Channels */
+#define CHNL_6 (2u << 22) /* Channels */
+#define CHNL_8 (3u << 22) /* Channels */
+#define DWL_MASK (7u << 19) /* Data Word Length mask */
+#define DWL_8 (0u << 19) /* Data Word Length */
+#define DWL_16 (1u << 19) /* Data Word Length */
+#define DWL_18 (2u << 19) /* Data Word Length */
+#define DWL_20 (3u << 19) /* Data Word Length */
+#define DWL_22 (4u << 19) /* Data Word Length */
+#define DWL_24 (5u << 19) /* Data Word Length */
+#define DWL_32 (6u << 19) /* Data Word Length */
+
+/*
+ * System word length
+ */
+#define SWL_16 (1 << 16) /* R/W System Word Length */
+#define SWL_24 (2 << 16) /* R/W System Word Length */
+#define SWL_32 (3 << 16) /* R/W System Word Length */
+
+#define SCKD (1 << 15) /* Serial Bit Clock Direction */
+#define SWSD (1 << 14) /* Serial WS Direction */
+#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
+#define SWSP (1 << 12) /* Serial WS Polarity */
+#define SDTA (1 << 10) /* Serial Data Alignment */
+#define PDTA (1 << 9) /* Parallel Data Alignment */
+#define DEL (1 << 8) /* Serial Data Delay */
+#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
+#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
+#define EN (1 << 0) /* SSI Module Enable */
+
+/*
+ * SSISR
+ */
+#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */
+#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */
+#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
+#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
+
+/*
+ * SSIWSR
+ */
+#define CONT (1 << 8) /* WS Continue Function */
+#define WS_MODE (1 << 0) /* WS Mode */
+
+#define SSI_NAME "ssi"
+
+struct rsnd_ssi {
+ struct rsnd_mod mod;
+
+ u32 flags;
+ u32 cr_own;
+ u32 cr_clk;
+ u32 cr_mode;
+ u32 cr_en;
+ u32 wsr;
+ int chan;
+ int rate;
+ int irq;
+ unsigned int usrcnt;
+
+ /* for PIO */
+ int byte_pos;
+ int byte_per_period;
+ int next_period_byte;
+};
+
+/* flags */
+#define RSND_SSI_CLK_PIN_SHARE (1 << 0)
+#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
+#define RSND_SSI_PROBED (1 << 2)
+
+#define for_each_rsnd_ssi(pos, priv, i) \
+ for (i = 0; \
+ (i < rsnd_ssi_nr(priv)) && \
+ ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \
+ i++)
+
+#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
+#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
+#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
+#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
+#define rsnd_ssi_is_multi_secondary(mod, io) \
+ (rsnd_ssi_multi_secondaries(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_is_run_mods(mod, io) \
+ (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod))
+
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ int use_busif = 0;
+
+ if (!rsnd_ssi_is_dma_mode(mod))
+ return 0;
+
+ if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF)))
+ use_busif = 1;
+ if (rsnd_io_to_mod_src(io))
+ use_busif = 1;
+
+ return use_busif;
+}
+
+static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, SSISR, 0);
+}
+
+static u32 rsnd_ssi_status_get(struct rsnd_mod *mod)
+{
+ return rsnd_mod_read(mod, SSISR);
+}
+
+static void rsnd_ssi_status_check(struct rsnd_mod *mod,
+ u32 bit)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 status;
+ int i;
+
+ for (i = 0; i < 1024; i++) {
+ status = rsnd_ssi_status_get(mod);
+ if (status & bit)
+ return;
+
+ udelay(5);
+ }
+
+ dev_warn(dev, "%s status check failed\n", rsnd_mod_name(mod));
+}
+
+static u32 rsnd_ssi_multi_secondaries(struct rsnd_dai_stream *io)
+{
+ static const enum rsnd_mod_type types[] = {
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM3,
+ };
+ int i, mask;
+
+ mask = 0;
+ for (i = 0; i < ARRAY_SIZE(types); i++) {
+ struct rsnd_mod *mod = rsnd_io_to_mod(io, types[i]);
+
+ if (!mod)
+ continue;
+
+ mask |= 1 << rsnd_mod_id(mod);
+ }
+
+ return mask;
+}
+
+static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
+ u32 mods;
+
+ mods = rsnd_ssi_multi_secondaries_runtime(io) |
+ 1 << rsnd_mod_id(ssi_mod);
+
+ if (ssi_parent_mod)
+ mods |= 1 << rsnd_mod_id(ssi_parent_mod);
+
+ return mods;
+}
+
+u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io)
+{
+ if (rsnd_runtime_is_multi_ssi(io))
+ return rsnd_ssi_multi_secondaries(io);
+
+ return 0;
+}
+
+static u32 rsnd_rdai_width_to_swl(struct rsnd_dai *rdai)
+{
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int width = rsnd_rdai_width_get(rdai);
+
+ switch (width) {
+ case 32: return SWL_32;
+ case 24: return SWL_24;
+ case 16: return SWL_16;
+ }
+
+ dev_err(dev, "unsupported slot width value: %d\n", width);
+ return 0;
+}
+
+unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai,
+ int param1, int param2, int *idx)
+{
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ static const int ssi_clk_mul_table[] = {
+ 1, 2, 4, 8, 16, 6, 12,
+ };
+ int j, ret;
+ unsigned int main_rate;
+ int width = rsnd_rdai_width_get(rdai);
+
+ for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+ /*
+ * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+ * with it is not allowed. (SSIWSR.WS_MODE with
+ * SSICR.CKDV = 000 is not allowed either).
+ * Skip it. See SSICR.CKDV
+ */
+ if (j == 0)
+ continue;
+
+ main_rate = width * param1 * param2 * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_clk_query(priv, main_rate);
+ if (ret < 0)
+ continue;
+
+ if (idx)
+ *idx = j;
+
+ return main_rate;
+ }
+
+ return 0;
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ int chan = rsnd_runtime_channel_for_ssi(io);
+ int idx, ret;
+ unsigned int main_rate;
+ unsigned int rate = rsnd_io_is_play(io) ?
+ rsnd_src_get_out_rate(priv, io) :
+ rsnd_src_get_in_rate(priv, io);
+
+ if (!rsnd_rdai_is_clk_master(rdai))
+ return 0;
+
+ if (!rsnd_ssi_can_output_clk(mod))
+ return 0;
+
+ if (rsnd_ssi_is_multi_secondary(mod, io))
+ return 0;
+
+ if (rsnd_runtime_is_tdm_split(io))
+ chan = rsnd_io_converted_chan(io);
+
+ chan = rsnd_channel_normalization(chan);
+
+ if (ssi->usrcnt > 0) {
+ if (ssi->rate != rate) {
+ dev_err(dev, "SSI parent/child should use same rate\n");
+ return -EINVAL;
+ }
+
+ if (ssi->chan != chan) {
+ dev_err(dev, "SSI parent/child should use same chan\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ ret = -EIO;
+ main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx);
+ if (!main_rate)
+ goto rate_err;
+
+ ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+ if (ret < 0)
+ goto rate_err;
+
+ /*
+ * SSI clock will be output contiguously
+ * by below settings.
+ * This means, rsnd_ssi_master_clk_start()
+ * and rsnd_ssi_register_setup() are necessary
+ * for SSI parent
+ *
+ * SSICR : FORCE, SCKD, SWSD
+ * SSIWSR : CONT
+ */
+ ssi->cr_clk = FORCE | rsnd_rdai_width_to_swl(rdai) |
+ SCKD | SWSD | CKDV(idx);
+ ssi->wsr = CONT;
+ ssi->rate = rate;
+ ssi->chan = chan;
+
+ dev_dbg(dev, "%s outputs %d chan %u Hz\n",
+ rsnd_mod_name(mod), chan, rate);
+
+ return 0;
+
+rate_err:
+ dev_err(dev, "unsupported clock rate (%d)\n", rate);
+
+ return ret;
+}
+
+static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ if (!rsnd_rdai_is_clk_master(rdai))
+ return;
+
+ if (!rsnd_ssi_can_output_clk(mod))
+ return;
+
+ if (ssi->usrcnt > 1)
+ return;
+
+ ssi->cr_clk = 0;
+ ssi->rate = 0;
+ ssi->chan = 0;
+
+ rsnd_adg_ssi_clk_stop(mod);
+}
+
+static void rsnd_ssi_config_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 cr_own = ssi->cr_own;
+ u32 cr_mode = ssi->cr_mode;
+ u32 wsr = ssi->wsr;
+ int width;
+ int is_tdm, is_tdm_split;
+
+ is_tdm = rsnd_runtime_is_tdm(io);
+ is_tdm_split = rsnd_runtime_is_tdm_split(io);
+
+ if (is_tdm)
+ dev_dbg(dev, "TDM mode\n");
+ if (is_tdm_split)
+ dev_dbg(dev, "TDM Split mode\n");
+
+ cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai);
+
+ if (rdai->bit_clk_inv)
+ cr_own |= SCKP;
+ if (rdai->frm_clk_inv && !is_tdm)
+ cr_own |= SWSP;
+ if (rdai->data_alignment)
+ cr_own |= SDTA;
+ if (rdai->sys_delay)
+ cr_own |= DEL;
+
+ /*
+ * TDM Mode
+ * see
+ * rsnd_ssiu_init_gen2()
+ */
+ if (is_tdm || is_tdm_split) {
+ wsr |= WS_MODE;
+ cr_own |= CHNL_8;
+ }
+
+ /*
+ * We shouldn't exchange SWSP after running.
+ * This means, parent needs to care it.
+ */
+ if (rsnd_ssi_is_parent(mod, io))
+ goto init_end;
+
+ if (rsnd_io_is_play(io))
+ cr_own |= TRMD;
+
+ cr_own &= ~DWL_MASK;
+ width = snd_pcm_format_width(runtime->format);
+ if (is_tdm_split) {
+ /*
+ * The SWL and DWL bits in SSICR should be fixed at 32-bit
+ * setting when TDM split mode.
+ * see datasheet
+ * Operation :: TDM Format Split Function (TDM Split Mode)
+ */
+ width = 32;
+ }
+
+ switch (width) {
+ case 8:
+ cr_own |= DWL_8;
+ break;
+ case 16:
+ cr_own |= DWL_16;
+ break;
+ case 24:
+ cr_own |= DWL_24;
+ break;
+ case 32:
+ cr_own |= DWL_32;
+ break;
+ }
+
+ if (rsnd_ssi_is_dma_mode(mod)) {
+ cr_mode = UIEN | OIEN | /* over/under run */
+ DMEN; /* DMA : enable DMA */
+ } else {
+ cr_mode = DIEN; /* PIO : enable Data interrupt */
+ }
+
+init_end:
+ ssi->cr_own = cr_own;
+ ssi->cr_mode = cr_mode;
+ ssi->wsr = wsr;
+}
+
+static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ rsnd_mod_write(mod, SSIWSR, ssi->wsr);
+ rsnd_mod_write(mod, SSICR, ssi->cr_own |
+ ssi->cr_clk |
+ ssi->cr_mode |
+ ssi->cr_en);
+}
+
+/*
+ * SSI mod common functions
+ */
+static int rsnd_ssi_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ int ret;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ ret = rsnd_ssi_master_clk_start(mod, io);
+ if (ret < 0)
+ return ret;
+
+ ssi->usrcnt++;
+
+ ret = rsnd_mod_power_on(mod);
+ if (ret < 0)
+ return ret;
+
+ rsnd_ssi_config_init(mod, io);
+
+ rsnd_ssi_register_setup(mod);
+
+ /* clear error status */
+ rsnd_ssi_status_clear(mod);
+
+ return 0;
+}
+
+static int rsnd_ssi_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ if (!ssi->usrcnt) {
+ dev_err(dev, "%s usrcnt error\n", rsnd_mod_name(mod));
+ return -EIO;
+ }
+
+ rsnd_ssi_master_clk_stop(mod, io);
+
+ rsnd_mod_power_off(mod);
+
+ ssi->usrcnt--;
+
+ if (!ssi->usrcnt) {
+ ssi->cr_own = 0;
+ ssi->cr_mode = 0;
+ ssi->wsr = 0;
+ }
+
+ return 0;
+}
+
+static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ unsigned int fmt_width = snd_pcm_format_width(params_format(params));
+
+ if (fmt_width > rdai->chan_width) {
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_err(dev, "invalid combination of slot-width and format-data-width\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ /*
+ * EN will be set via SSIU :: SSI_CONTROL
+ * if Multi channel mode
+ */
+ if (rsnd_ssi_multi_secondaries_runtime(io))
+ return 0;
+
+ /*
+ * EN is for data output.
+ * SSI parent EN is not needed.
+ */
+ if (rsnd_ssi_is_parent(mod, io))
+ return 0;
+
+ ssi->cr_en = EN;
+
+ rsnd_mod_write(mod, SSICR, ssi->cr_own |
+ ssi->cr_clk |
+ ssi->cr_mode |
+ ssi->cr_en);
+
+ return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 cr;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ if (rsnd_ssi_is_parent(mod, io))
+ return 0;
+
+ cr = ssi->cr_own |
+ ssi->cr_clk;
+
+ /*
+ * disable all IRQ,
+ * Playback: Wait all data was sent
+ * Capture: It might not receave data. Do nothing
+ */
+ if (rsnd_io_is_play(io)) {
+ rsnd_mod_write(mod, SSICR, cr | ssi->cr_en);
+ rsnd_ssi_status_check(mod, DIRQ);
+ }
+
+ /* In multi-SSI mode, stop is performed by setting ssi0129 in
+ * SSI_CONTROL to 0 (in rsnd_ssio_stop_gen2). Do nothing here.
+ */
+ if (rsnd_ssi_multi_secondaries_runtime(io))
+ return 0;
+
+ /*
+ * disable SSI,
+ * and, wait idle state
+ */
+ rsnd_mod_write(mod, SSICR, cr); /* disabled all */
+ rsnd_ssi_status_check(mod, IIRQ);
+
+ ssi->cr_en = 0;
+
+ return 0;
+}
+
+static int rsnd_ssi_irq(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv,
+ int enable)
+{
+ u32 val = 0;
+ int is_tdm, is_tdm_split;
+ int id = rsnd_mod_id(mod);
+
+ is_tdm = rsnd_runtime_is_tdm(io);
+ is_tdm_split = rsnd_runtime_is_tdm_split(io);
+
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ if (rsnd_ssi_is_parent(mod, io))
+ return 0;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ if (enable)
+ val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
+
+ if (is_tdm || is_tdm_split) {
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 9:
+ val |= 0x0000ff00;
+ break;
+ }
+ }
+
+ rsnd_mod_write(mod, SSI_INT_ENABLE, val);
+
+ return 0;
+}
+
+static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io);
+static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int is_dma = rsnd_ssi_is_dma_mode(mod);
+ u32 status;
+ bool elapsed = false;
+ bool stop = false;
+
+ scoped_guard(spinlock, &priv->lock) {
+
+ /* ignore all cases if not working */
+ if (!rsnd_io_is_working(io))
+ break;
+
+ status = rsnd_ssi_status_get(mod);
+
+ /* PIO only */
+ if (!is_dma && (status & DIRQ))
+ elapsed = rsnd_ssi_pio_interrupt(mod, io);
+
+ /* DMA only */
+ if (is_dma && (status & (UIRQ | OIRQ))) {
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
+
+ stop = true;
+ }
+
+ stop |= rsnd_ssiu_busif_err_status_clear(mod);
+
+ rsnd_ssi_status_clear(mod);
+ }
+
+ if (elapsed)
+ snd_pcm_period_elapsed(io->substream);
+
+ if (stop)
+ snd_pcm_stop_xrun(io->substream);
+
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
+{
+ struct rsnd_mod *mod = data;
+
+ rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt);
+
+ return IRQ_HANDLED;
+}
+
+static u32 *rsnd_ssi_get_status(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type)
+{
+ /*
+ * SSIP (= SSI parent) needs to be special, otherwise,
+ * 2nd SSI might doesn't start. see also rsnd_mod_call()
+ *
+ * We can't include parent SSI status on SSI, because we don't know
+ * how many SSI requests parent SSI. Thus, it is localed on "io" now.
+ * ex) trouble case
+ * Playback: SSI0
+ * Capture : SSI1 (needs SSI0)
+ *
+ * 1) start Capture -> SSI0/SSI1 are started.
+ * 2) start Playback -> SSI0 doesn't work, because it is already
+ * marked as "started" on 1)
+ *
+ * OTOH, using each mod's status is good for MUX case.
+ * It doesn't need to start in 2nd start
+ * ex)
+ * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0
+ * |
+ * IO-1: SRC1 -> CTU2 -+
+ *
+ * 1) start IO-0 -> start SSI0
+ * 2) start IO-1 -> SSI0 doesn't need to start, because it is
+ * already started on 1)
+ */
+ if (type == RSND_MOD_SSIP)
+ return &io->parent_ssi_status;
+
+ return rsnd_mod_get_status(mod, io, type);
+}
+
+/*
+ * SSI PIO
+ */
+static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+ if (!__rsnd_ssi_is_pin_sharing(mod))
+ return;
+
+ if (!rsnd_rdai_is_clk_master(rdai))
+ return;
+
+ if (rsnd_ssi_is_multi_secondary(mod, io))
+ return;
+
+ switch (rsnd_mod_id(mod)) {
+ case 1:
+ case 2:
+ case 9:
+ rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP);
+ break;
+ case 4:
+ rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP);
+ break;
+ case 8:
+ rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP);
+ break;
+ }
+}
+
+static int rsnd_ssi_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ /*
+ * rsnd_rdai_is_clk_master() will be enabled after set_fmt,
+ * and, pcm_new will be called after it.
+ * This function reuse pcm_new at this point.
+ */
+ rsnd_ssi_parent_attach(mod, io);
+
+ return 0;
+}
+
+static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ int ret = 0;
+
+ /*
+ * SSIP/SSIU/IRQ are not needed on
+ * SSI Multi secondaries
+ */
+ if (rsnd_ssi_is_multi_secondary(mod, io))
+ return 0;
+
+ /*
+ * It can't judge ssi parent at this point
+ * see rsnd_ssi_pcm_new()
+ */
+
+ /*
+ * SSI might be called again as PIO fallback
+ * It is easy to manual handling for IRQ request/free
+ *
+ * OTOH, this function might be called many times if platform is
+ * using MIX. It needs xxx_attach() many times on xxx_probe().
+ * Because of it, we can't control .probe/.remove calling count by
+ * mod->status.
+ * But it don't need to call request_irq() many times.
+ * Let's control it by RSND_SSI_PROBED flag.
+ */
+ if (!rsnd_flags_has(ssi, RSND_SSI_PROBED)) {
+ ret = request_irq(ssi->irq,
+ rsnd_ssi_interrupt,
+ IRQF_SHARED,
+ dev_name(dev), mod);
+
+ rsnd_flags_set(ssi, RSND_SSI_PROBED);
+ }
+
+ return ret;
+}
+
+static int rsnd_ssi_common_remove(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io);
+
+ /* Do nothing if non SSI (= SSI parent, multi SSI) mod */
+ if (pure_ssi_mod != mod)
+ return 0;
+
+ /* PIO will request IRQ again */
+ if (rsnd_flags_has(ssi, RSND_SSI_PROBED)) {
+ free_irq(ssi->irq, mod);
+
+ rsnd_flags_del(ssi, RSND_SSI_PROBED);
+ }
+
+ return 0;
+}
+
+/*
+ * SSI PIO functions
+ */
+static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 *buf = (u32 *)(runtime->dma_area + ssi->byte_pos);
+ int shift = 0;
+ int byte_pos;
+ bool elapsed = false;
+
+ if (snd_pcm_format_width(runtime->format) == 24)
+ shift = 8;
+
+ /*
+ * 8/16/32 data can be assesse to TDR/RDR register
+ * directly as 32bit data
+ * see rsnd_ssi_init()
+ */
+ if (rsnd_io_is_play(io))
+ rsnd_mod_write(mod, SSITDR, (*buf) << shift);
+ else
+ *buf = (rsnd_mod_read(mod, SSIRDR) >> shift);
+
+ byte_pos = ssi->byte_pos + sizeof(*buf);
+
+ if (byte_pos >= ssi->next_period_byte) {
+ int period_pos = byte_pos / ssi->byte_per_period;
+
+ if (period_pos >= runtime->periods) {
+ byte_pos = 0;
+ period_pos = 0;
+ }
+
+ ssi->next_period_byte = (period_pos + 1) * ssi->byte_per_period;
+
+ elapsed = true;
+ }
+
+ WRITE_ONCE(ssi->byte_pos, byte_pos);
+
+ return elapsed;
+}
+
+static int rsnd_ssi_pio_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ if (!rsnd_ssi_is_parent(mod, io)) {
+ ssi->byte_pos = 0;
+ ssi->byte_per_period = runtime->period_size *
+ runtime->channels *
+ samples_to_bytes(runtime, 1);
+ ssi->next_period_byte = ssi->byte_per_period;
+ }
+
+ return rsnd_ssi_init(mod, io, priv);
+}
+
+static int rsnd_ssi_pio_pointer(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ snd_pcm_uframes_t *pointer)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+ *pointer = bytes_to_frames(runtime, READ_ONCE(ssi->byte_pos));
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
+ .name = SSI_NAME,
+ .probe = rsnd_ssi_common_probe,
+ .remove = rsnd_ssi_common_remove,
+ .init = rsnd_ssi_pio_init,
+ .quit = rsnd_ssi_quit,
+ .start = rsnd_ssi_start,
+ .stop = rsnd_ssi_stop,
+ .irq = rsnd_ssi_irq,
+ .pointer = rsnd_ssi_pio_pointer,
+ .pcm_new = rsnd_ssi_pcm_new,
+ .hw_params = rsnd_ssi_hw_params,
+ .get_status = rsnd_ssi_get_status,
+};
+
+static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ int ret;
+
+ /*
+ * SSIP/SSIU/IRQ/DMA are not needed on
+ * SSI Multi secondaries
+ */
+ if (rsnd_ssi_is_multi_secondary(mod, io))
+ return 0;
+
+ ret = rsnd_ssi_common_probe(mod, io, priv);
+ if (ret)
+ return ret;
+
+ /* SSI probe might be called many times in MUX multi path */
+ ret = rsnd_dma_attach(io, mod, &io->dma);
+
+ return ret;
+}
+
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ /*
+ * fallback to PIO
+ *
+ * SSI .probe might be called again.
+ * see
+ * rsnd_rdai_continuance_probe()
+ */
+ mod->ops = &rsnd_ssi_pio_ops;
+
+ dev_info(dev, "%s fallback to PIO mode\n", rsnd_mod_name(mod));
+
+ return 0;
+}
+
+static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int is_play = rsnd_io_is_play(io);
+ char *name;
+
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssiu_dma_req()
+ * rsnd_dma_of_path()
+ */
+
+ if (rsnd_ssi_use_busif(io))
+ name = is_play ? "rxu" : "txu";
+ else
+ name = is_play ? "rx" : "tx";
+
+ return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
+ SSI_NAME, mod, name);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_ssi_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ seq_printf(m, "clock: %s\n", rsnd_rdai_is_clk_master(rdai) ?
+ "provider" : "consumer");
+ seq_printf(m, "bit_clk_inv: %d\n", rdai->bit_clk_inv);
+ seq_printf(m, "frm_clk_inv: %d\n", rdai->frm_clk_inv);
+ seq_printf(m, "pin share: %d\n", __rsnd_ssi_is_pin_sharing(mod));
+ seq_printf(m, "can out clk: %d\n", rsnd_ssi_can_output_clk(mod));
+ seq_printf(m, "multi secondary: %d\n", rsnd_ssi_is_multi_secondary(mod, io));
+ seq_printf(m, "tdm: %d, %d\n", rsnd_runtime_is_tdm(io),
+ rsnd_runtime_is_tdm_split(io));
+ seq_printf(m, "chan: %d\n", ssi->chan);
+ seq_printf(m, "user: %d\n", ssi->usrcnt);
+
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSI,
+ rsnd_mod_id(mod) * 0x40, 0x40);
+}
+#define DEBUG_INFO .debug_info = rsnd_ssi_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
+static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
+ .name = SSI_NAME,
+ .dma_req = rsnd_ssi_dma_req,
+ .probe = rsnd_ssi_dma_probe,
+ .remove = rsnd_ssi_common_remove,
+ .init = rsnd_ssi_init,
+ .quit = rsnd_ssi_quit,
+ .start = rsnd_ssi_start,
+ .stop = rsnd_ssi_stop,
+ .irq = rsnd_ssi_irq,
+ .pcm_new = rsnd_ssi_pcm_new,
+ .fallback = rsnd_ssi_fallback,
+ .hw_params = rsnd_ssi_hw_params,
+ .get_status = rsnd_ssi_get_status,
+ DEBUG_INFO
+};
+
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+ return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+/*
+ * ssi mod function
+ */
+static void rsnd_ssi_connect(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ static const enum rsnd_mod_type types[] = {
+ RSND_MOD_SSI,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM3,
+ };
+ enum rsnd_mod_type type;
+ int i;
+
+ /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
+ for (i = 0; i < ARRAY_SIZE(types); i++) {
+ type = types[i];
+ if (!rsnd_io_to_mod(io, type)) {
+ rsnd_dai_connect(mod, io, type);
+ rsnd_rdai_channels_set(rdai, (i + 1) * 2);
+ rsnd_rdai_ssi_lane_set(rdai, (i + 1));
+ return;
+ }
+ }
+}
+
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture)
+{
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *node;
+ int i;
+
+ node = rsnd_ssi_of_node(priv);
+ if (!node)
+ return;
+
+ i = 0;
+ for_each_child_of_node_scoped(node, np) {
+ struct rsnd_mod *mod;
+
+ i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
+ if (i < 0)
+ break;
+
+ mod = rsnd_ssi_mod_get(priv, i);
+
+ if (np == playback)
+ rsnd_ssi_connect(mod, &rdai->playback);
+ if (np == capture)
+ rsnd_ssi_connect(mod, &rdai->capture);
+ i++;
+ }
+
+ of_node_put(node);
+}
+
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get(rsnd_ssi_get(priv, id));
+}
+
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+{
+ if (!mod)
+ return 0;
+
+ return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE));
+}
+
+int rsnd_ssi_probe(struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod_ops *ops;
+ struct clk *clk;
+ struct rsnd_ssi *ssi;
+ char name[RSND_SSI_NAME_SIZE];
+ int i, nr, ret;
+
+ node = rsnd_ssi_of_node(priv);
+ if (!node)
+ return -EINVAL;
+
+ nr = rsnd_node_count(priv, node, SSI_NAME);
+ if (!nr) {
+ ret = -EINVAL;
+ goto rsnd_ssi_probe_done;
+ }
+
+ ssi = devm_kcalloc(dev, nr, sizeof(*ssi), GFP_KERNEL);
+ if (!ssi) {
+ ret = -ENOMEM;
+ goto rsnd_ssi_probe_done;
+ }
+
+ priv->ssi = ssi;
+ priv->ssi_nr = nr;
+
+ i = 0;
+ for_each_child_of_node_scoped(node, np) {
+ if (!of_device_is_available(np))
+ goto skip;
+
+ i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
+ if (i < 0) {
+ ret = -EINVAL;
+ goto rsnd_ssi_probe_done;
+ }
+
+ ssi = rsnd_ssi_get(priv, i);
+
+ snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
+ SSI_NAME, i);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto rsnd_ssi_probe_done;
+ }
+
+ if (of_property_read_bool(np, "shared-pin"))
+ rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE);
+
+ if (of_property_read_bool(np, "no-busif"))
+ rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF);
+
+ ssi->irq = irq_of_parse_and_map(np, 0);
+ if (!ssi->irq) {
+ ret = -EINVAL;
+ goto rsnd_ssi_probe_done;
+ }
+
+ if (of_property_read_bool(np, "pio-transfer"))
+ ops = &rsnd_ssi_pio_ops;
+ else
+ ops = &rsnd_ssi_dma_ops;
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
+ RSND_MOD_SSI, i);
+ if (ret)
+ goto rsnd_ssi_probe_done;
+
+skip:
+ i++;
+ }
+
+ ret = 0;
+
+rsnd_ssi_probe_done:
+ of_node_put(node);
+
+ return ret;
+}
+
+void rsnd_ssi_remove(struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi;
+ int i;
+
+ for_each_rsnd_ssi(ssi, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(ssi));
+ }
+}
diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c
new file mode 100644
index 000000000000..244fb833292a
--- /dev/null
+++ b/sound/soc/renesas/rcar/ssiu.c
@@ -0,0 +1,605 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SSIU support
+//
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include "rsnd.h"
+
+#define SSIU_NAME "ssiu"
+
+struct rsnd_ssiu {
+ struct rsnd_mod mod;
+ u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */
+ unsigned int usrcnt;
+ int id;
+ int id_sub;
+};
+
+/* SSI_MODE */
+#define TDM_EXT (1 << 0)
+#define TDM_SPLIT (1 << 8)
+
+#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
+#define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod)
+#define for_each_rsnd_ssiu(pos, priv, i) \
+ for (i = 0; \
+ (i < rsnd_ssiu_nr(priv)) && \
+ ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \
+ i++)
+
+/*
+ * SSI Gen2 Gen3 Gen4
+ * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7
+ * 1 BUSIF0-3 BUSIF0-7
+ * 2 BUSIF0-3 BUSIF0-7
+ * 3 BUSIF0 BUSIF0-7
+ * 4 BUSIF0 BUSIF0-7
+ * 5 BUSIF0 BUSIF0
+ * 6 BUSIF0 BUSIF0
+ * 7 BUSIF0 BUSIF0
+ * 8 BUSIF0 BUSIF0
+ * 9 BUSIF0-3 BUSIF0-7
+ * total 22 52 8
+ */
+static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 };
+static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
+static const int gen4_id[] = { 0 };
+
+/* enable busif buffer over/under run interrupt. */
+#define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1)
+#define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0)
+static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable)
+{
+ int id = rsnd_mod_id(mod);
+ int shift, offset;
+ int i;
+
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ shift = id;
+ offset = 0;
+ break;
+ case 9:
+ shift = 1;
+ offset = 1;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset);
+ u32 val = 0xf << (shift * 4);
+ u32 sys_int_enable = rsnd_mod_read(mod, reg);
+
+ if (enable)
+ sys_int_enable |= val;
+ else
+ sys_int_enable &= ~val;
+ rsnd_mod_write(mod, reg, sys_int_enable);
+ }
+}
+
+bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
+{
+ bool error = false;
+ int id = rsnd_mod_id(mod);
+ int shift, offset;
+ int i;
+
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ shift = id;
+ offset = 0;
+ break;
+ case 9:
+ shift = 1;
+ offset = 1;
+ break;
+ default:
+ goto out;
+ }
+
+ for (i = 0; i < 4; i++) {
+ u32 reg = SSI_SYS_STATUS(i * 2) + offset;
+ u32 status = rsnd_mod_read(mod, reg);
+ u32 val = 0xf << (shift * 4);
+
+ status &= val;
+ if (status) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
+ error = true;
+ }
+ rsnd_mod_write(mod, reg, val);
+ }
+out:
+ return error;
+}
+
+static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type type)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+ int busif = rsnd_mod_id_sub(mod);
+
+ return &ssiu->busif_status[busif];
+}
+
+static int rsnd_ssiu_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ u32 ssis = rsnd_ssi_multi_secondaries_runtime(io);
+ int use_busif = rsnd_ssi_use_busif(io);
+ int id = rsnd_mod_id(mod);
+ int is_clk_master = rsnd_rdai_is_clk_master(rdai);
+ u32 val1, val2;
+
+ /* clear status */
+ rsnd_ssiu_busif_err_status_clear(mod);
+
+ /* Gen4 doesn't have SSI_MODE */
+ if (rsnd_is_gen4(priv))
+ goto ssi_mode_setting_end;
+
+ /*
+ * SSI_MODE0
+ */
+ rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
+
+ /*
+ * SSI_MODE1 / SSI_MODE2
+ *
+ * FIXME
+ * sharing/multi with SSI0 are mainly supported
+ */
+ val1 = rsnd_mod_read(mod, SSI_MODE1);
+ val2 = rsnd_mod_read(mod, SSI_MODE2);
+ if (rsnd_ssi_is_pin_sharing(io)) {
+
+ ssis |= (1 << id);
+
+ } else if (ssis) {
+ /*
+ * Multi SSI
+ *
+ * set synchronized bit here
+ */
+
+ /* SSI4 is synchronized with SSI3 */
+ if (ssis & (1 << 4))
+ val1 |= (1 << 20);
+ /* SSI012 are synchronized */
+ if (ssis == 0x0006)
+ val1 |= (1 << 4);
+ /* SSI0129 are synchronized */
+ if (ssis == 0x0206)
+ val2 |= (1 << 4);
+ }
+
+ /* SSI1 is sharing pin with SSI0 */
+ if (ssis & (1 << 1))
+ val1 |= is_clk_master ? 0x2 : 0x1;
+
+ /* SSI2 is sharing pin with SSI0 */
+ if (ssis & (1 << 2))
+ val1 |= is_clk_master ? 0x2 << 2 :
+ 0x1 << 2;
+ /* SSI4 is sharing pin with SSI3 */
+ if (ssis & (1 << 4))
+ val1 |= is_clk_master ? 0x2 << 16 :
+ 0x1 << 16;
+ /* SSI9 is sharing pin with SSI0 */
+ if (ssis & (1 << 9))
+ val2 |= is_clk_master ? 0x2 : 0x1;
+
+ rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1);
+ rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2);
+
+ssi_mode_setting_end:
+ /*
+ * Enable busif buffer over/under run interrupt.
+ * It will be handled from ssi.c
+ * see
+ * __rsnd_ssi_interrupt()
+ */
+ rsnd_ssiu_busif_err_irq_enable(mod);
+
+ return 0;
+}
+
+static int rsnd_ssiu_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ /* disable busif buffer over/under run interrupt. */
+ rsnd_ssiu_busif_err_irq_disable(mod);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
+ .name = SSIU_NAME,
+ .init = rsnd_ssiu_init,
+ .quit = rsnd_ssiu_quit,
+ .get_status = rsnd_ssiu_get_status,
+};
+
+static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+ u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0);
+ u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1);
+ int ret;
+ u32 mode = 0;
+
+ ret = rsnd_ssiu_init(mod, io, priv);
+ if (ret < 0)
+ return ret;
+
+ ssiu->usrcnt++;
+
+ /*
+ * TDM Extend/Split Mode
+ * see
+ * rsnd_ssi_config_init()
+ */
+ if (rsnd_runtime_is_tdm(io))
+ mode = TDM_EXT;
+ else if (rsnd_runtime_is_tdm_split(io))
+ mode = TDM_SPLIT;
+
+ rsnd_mod_write(mod, SSI_MODE, mode);
+
+ if (rsnd_ssi_use_busif(io)) {
+ int id = rsnd_mod_id(mod);
+ int busif = rsnd_mod_id_sub(mod);
+ enum rsnd_reg adinr_reg, mode_reg, dalign_reg;
+
+ if ((id == 9) && (busif >= 4)) {
+ adinr_reg = SSI9_BUSIF_ADINR(busif);
+ mode_reg = SSI9_BUSIF_MODE(busif);
+ dalign_reg = SSI9_BUSIF_DALIGN(busif);
+ } else {
+ adinr_reg = SSI_BUSIF_ADINR(busif);
+ mode_reg = SSI_BUSIF_MODE(busif);
+ dalign_reg = SSI_BUSIF_DALIGN(busif);
+ }
+
+ rsnd_mod_write(mod, adinr_reg,
+ rsnd_get_adinr_bit(mod, io) |
+ (rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io)));
+ rsnd_mod_write(mod, mode_reg,
+ rsnd_get_busif_shift(io, mod) | 1);
+ rsnd_mod_write(mod, dalign_reg,
+ rsnd_get_dalign(mod, io));
+ }
+
+ if (has_hdmi0 || has_hdmi1) {
+ enum rsnd_mod_type rsnd_ssi_array[] = {
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM3,
+ };
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *pos;
+ u32 val;
+ int i;
+
+ i = rsnd_mod_id(ssi_mod);
+
+ /* output all same SSI as default */
+ val = i << 16 |
+ i << 20 |
+ i << 24 |
+ i << 28 |
+ i;
+
+ for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
+ int shift = (i * 4) + 20;
+
+ val = (val & ~(0xF << shift)) |
+ rsnd_mod_id(pos) << shift;
+ }
+
+ if (has_hdmi0)
+ rsnd_mod_write(mod, HDMI0_SEL, val);
+ if (has_hdmi1)
+ rsnd_mod_write(mod, HDMI1_SEL, val);
+ }
+
+ return 0;
+}
+
+static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ int busif = rsnd_mod_id_sub(mod);
+
+ if (!rsnd_ssi_use_busif(io))
+ return 0;
+
+ rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4));
+
+ if (rsnd_ssi_multi_secondaries_runtime(io))
+ rsnd_mod_write(mod, SSI_CONTROL, 0x1);
+
+ return 0;
+}
+
+static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+ int busif = rsnd_mod_id_sub(mod);
+
+ if (!rsnd_ssi_use_busif(io))
+ return 0;
+
+ rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0);
+
+ if (--ssiu->usrcnt)
+ return 0;
+
+ if (rsnd_ssi_multi_secondaries_runtime(io))
+ rsnd_mod_write(mod, SSI_CONTROL, 0);
+
+ return 0;
+}
+
+static int rsnd_ssiu_id(struct rsnd_mod *mod)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+
+ /* see rsnd_ssiu_probe() */
+ return ssiu->id;
+}
+
+static int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+
+ /* see rsnd_ssiu_probe() */
+ return ssiu->id_sub;
+}
+
+static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int is_play = rsnd_io_is_play(io);
+ char *name;
+
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssi_dma_req()
+ * rsnd_dma_of_path()
+ */
+
+ name = is_play ? "rx" : "tx";
+
+ return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
+ SSIU_NAME, mod, name);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_ssiu_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSIU,
+ rsnd_mod_id(mod) * 0x80, 0x80);
+}
+#define DEBUG_INFO .debug_info = rsnd_ssiu_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
+static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
+ .name = SSIU_NAME,
+ .dma_req = rsnd_ssiu_dma_req,
+ .init = rsnd_ssiu_init_gen2,
+ .quit = rsnd_ssiu_quit,
+ .start = rsnd_ssiu_start_gen2,
+ .stop = rsnd_ssiu_stop_gen2,
+ .get_status = rsnd_ssiu_get_status,
+ DEBUG_INFO
+};
+
+static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
+ id = 0;
+
+ return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
+}
+
+static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_ssiu *ssiu;
+ int is_dma_mode;
+ int i;
+
+ if (!ssi_mod)
+ return;
+
+ is_dma_mode = rsnd_ssi_is_dma_mode(ssi_mod);
+
+ /* select BUSIF0 */
+ for_each_rsnd_ssiu(ssiu, priv, i) {
+ struct rsnd_mod *mod = rsnd_mod_get(ssiu);
+
+ if (is_dma_mode &&
+ (rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) &&
+ (rsnd_mod_id_sub(mod) == 0)) {
+ rsnd_dai_connect(mod, io, mod->type);
+ return;
+ }
+ }
+}
+
+void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture)
+{
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *node = rsnd_ssiu_of_node(priv);
+ struct rsnd_dai_stream *io_p = &rdai->playback;
+ struct rsnd_dai_stream *io_c = &rdai->capture;
+
+ /* use rcar_sound,ssiu if exist */
+ if (node) {
+ int i = 0;
+
+ for_each_child_of_node_scoped(node, np) {
+ struct rsnd_mod *mod;
+
+ i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
+ if (i < 0)
+ break;
+
+ mod = rsnd_ssiu_mod_get(priv, i);
+
+ if (np == playback)
+ rsnd_dai_connect(mod, io_p, mod->type);
+ if (np == capture)
+ rsnd_dai_connect(mod, io_c, mod->type);
+ i++;
+ }
+
+ of_node_put(node);
+ }
+
+ /* Keep DT compatibility */
+ if (!rsnd_io_to_mod_ssiu(io_p))
+ rsnd_parse_connect_ssiu_compatible(priv, io_p);
+ if (!rsnd_io_to_mod_ssiu(io_c))
+ rsnd_parse_connect_ssiu_compatible(priv, io_c);
+}
+
+int rsnd_ssiu_probe(struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *node __free(device_node) = rsnd_ssiu_of_node(priv);
+ struct rsnd_ssiu *ssiu;
+ struct rsnd_mod_ops *ops;
+ const int *list = NULL;
+ int i, nr;
+
+ /*
+ * Keep DT compatibility.
+ * if it has "rcar_sound,ssiu", use it.
+ * if not, use "rcar_sound,ssi"
+ * see
+ * rsnd_ssiu_bufsif_to_id()
+ */
+ if (node)
+ nr = rsnd_node_count(priv, node, SSIU_NAME);
+ else
+ nr = priv->ssi_nr;
+
+ if (!nr)
+ return -EINVAL;
+
+ ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
+ if (!ssiu)
+ return -ENOMEM;
+
+ priv->ssiu = ssiu;
+ priv->ssiu_nr = nr;
+
+ if (rsnd_is_gen1(priv))
+ ops = &rsnd_ssiu_ops_gen1;
+ else
+ ops = &rsnd_ssiu_ops_gen2;
+
+ /* Keep compatibility */
+ nr = 0;
+ if ((node) &&
+ (ops == &rsnd_ssiu_ops_gen2)) {
+ ops->id = rsnd_ssiu_id;
+ ops->id_sub = rsnd_ssiu_id_sub;
+
+ if (rsnd_is_gen2(priv)) {
+ list = gen2_id;
+ nr = ARRAY_SIZE(gen2_id);
+ } else if (rsnd_is_gen3(priv)) {
+ list = gen3_id;
+ nr = ARRAY_SIZE(gen3_id);
+ } else if (rsnd_is_gen4(priv)) {
+ list = gen4_id;
+ nr = ARRAY_SIZE(gen4_id);
+ } else {
+ dev_err(dev, "unknown SSIU\n");
+ return -ENODEV;
+ }
+ }
+
+ for_each_rsnd_ssiu(ssiu, priv, i) {
+ int ret;
+
+ if (node) {
+ int j;
+
+ /*
+ * see
+ * rsnd_ssiu_get_id()
+ * rsnd_ssiu_get_id_sub()
+ */
+ for (j = 0; j < nr; j++) {
+ if (list[j] > i)
+ break;
+ ssiu->id = j;
+ ssiu->id_sub = i - list[ssiu->id];
+ }
+ } else {
+ ssiu->id = i;
+ }
+
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
+ ops, NULL, RSND_MOD_SSIU, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void rsnd_ssiu_remove(struct rsnd_priv *priv)
+{
+ struct rsnd_ssiu *ssiu;
+ int i;
+
+ for_each_rsnd_ssiu(ssiu, priv, i) {
+ rsnd_mod_quit(rsnd_mod_get(ssiu));
+ }
+}
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
new file mode 100644
index 000000000000..f4dc2f68dead
--- /dev/null
+++ b/sound/soc/renesas/rz-ssi.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas RZ/G2L ASoC Serial Sound Interface (SSIF-2) Driver
+//
+// Copyright (C) 2021 Renesas Electronics Corp.
+// Copyright (C) 2019 Chris Brandt.
+//
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/* REGISTER OFFSET */
+#define SSICR 0x000
+#define SSISR 0x004
+#define SSIFCR 0x010
+#define SSIFSR 0x014
+#define SSIFTDR 0x018
+#define SSIFRDR 0x01c
+#define SSIOFR 0x020
+#define SSISCR 0x024
+
+/* SSI REGISTER BITS */
+#define SSICR_DWL(x) (((x) & 0x7) << 19)
+#define SSICR_SWL(x) (((x) & 0x7) << 16)
+
+#define SSICR_CKS BIT(30)
+#define SSICR_TUIEN BIT(29)
+#define SSICR_TOIEN BIT(28)
+#define SSICR_RUIEN BIT(27)
+#define SSICR_ROIEN BIT(26)
+#define SSICR_MST BIT(14)
+#define SSICR_BCKP BIT(13)
+#define SSICR_LRCKP BIT(12)
+#define SSICR_CKDV(x) (((x) & 0xf) << 4)
+#define SSICR_TEN BIT(1)
+#define SSICR_REN BIT(0)
+
+#define SSISR_TUIRQ BIT(29)
+#define SSISR_TOIRQ BIT(28)
+#define SSISR_RUIRQ BIT(27)
+#define SSISR_ROIRQ BIT(26)
+#define SSISR_IIRQ BIT(25)
+
+#define SSIFCR_AUCKE BIT(31)
+#define SSIFCR_SSIRST BIT(16)
+#define SSIFCR_TIE BIT(3)
+#define SSIFCR_RIE BIT(2)
+#define SSIFCR_TFRST BIT(1)
+#define SSIFCR_RFRST BIT(0)
+#define SSIFCR_FIFO_RST (SSIFCR_TFRST | SSIFCR_RFRST)
+
+#define SSIFSR_TDC_MASK 0x3f
+#define SSIFSR_TDC_SHIFT 24
+#define SSIFSR_RDC_MASK 0x3f
+#define SSIFSR_RDC_SHIFT 8
+
+#define SSIFSR_TDE BIT(16)
+#define SSIFSR_RDF BIT(0)
+
+#define SSIOFR_LRCONT BIT(8)
+
+#define SSISCR_TDES(x) (((x) & 0x1f) << 8)
+#define SSISCR_RDFS(x) (((x) & 0x1f) << 0)
+
+/* Pre allocated buffers sizes */
+#define PREALLOC_BUFFER (SZ_32K)
+#define PREALLOC_BUFFER_MAX (SZ_32K)
+
+#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */
+#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE
+#define SSI_CHAN_MIN 2
+#define SSI_CHAN_MAX 2
+#define SSI_FIFO_DEPTH 32
+
+struct rz_ssi_priv;
+
+struct rz_ssi_stream {
+ struct rz_ssi_priv *priv;
+ struct snd_pcm_substream *substream;
+ int fifo_sample_size; /* sample capacity of SSI FIFO */
+ int dma_buffer_pos; /* The address for the next DMA descriptor */
+ int completed_dma_buf_pos; /* The address of the last completed DMA descriptor. */
+ int period_counter; /* for keeping track of periods transferred */
+ int buffer_pos; /* current frame position in the buffer */
+ int running; /* 0=stopped, 1=running */
+
+ int uerr_num;
+ int oerr_num;
+
+ struct dma_chan *dma_ch;
+
+ int (*transfer)(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm);
+};
+
+struct rz_ssi_priv {
+ void __iomem *base;
+ struct reset_control *rstc;
+ struct device *dev;
+ struct clk *sfr_clk;
+ struct clk *clk;
+
+ phys_addr_t phys;
+ int irq_int;
+ int irq_tx;
+ int irq_rx;
+ int irq_rt;
+
+ spinlock_t lock;
+
+ /*
+ * The SSI supports full-duplex transmission and reception.
+ * However, if an error occurs, channel reset (both transmission
+ * and reception reset) is required.
+ * So it is better to use as half-duplex (playing and recording
+ * should be done on separate channels).
+ */
+ struct rz_ssi_stream playback;
+ struct rz_ssi_stream capture;
+
+ /* clock */
+ unsigned long audio_mck;
+ unsigned long audio_clk_1;
+ unsigned long audio_clk_2;
+
+ bool lrckp_fsync_fall; /* LR clock polarity (SSICR.LRCKP) */
+ bool bckp_rise; /* Bit clock polarity (SSICR.BCKP) */
+ bool dma_rt;
+
+ struct {
+ bool tx_active;
+ bool rx_active;
+ bool one_stream_triggered;
+ } dup;
+
+ /* Full duplex communication support */
+ struct {
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int sample_width;
+ unsigned int sample_bits;
+ } hw_params_cache;
+};
+
+static void rz_ssi_dma_complete(void *data);
+
+static void rz_ssi_reg_writel(struct rz_ssi_priv *priv, uint reg, u32 data)
+{
+ writel(data, (priv->base + reg));
+}
+
+static u32 rz_ssi_reg_readl(struct rz_ssi_priv *priv, uint reg)
+{
+ return readl(priv->base + reg);
+}
+
+static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg,
+ u32 bclr, u32 bset)
+{
+ u32 val;
+
+ val = readl(priv->base + reg);
+ val = (val & ~bclr) | bset;
+ writel(val, (priv->base + reg));
+}
+
+static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream)
+{
+ return substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+}
+
+static inline struct rz_ssi_stream *
+rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream)
+{
+ struct rz_ssi_stream *stream = &ssi->playback;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ stream = &ssi->capture;
+
+ return stream;
+}
+
+static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi)
+{
+ return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch));
+}
+
+static void rz_ssi_set_substream(struct rz_ssi_stream *strm,
+ struct snd_pcm_substream *substream)
+{
+ struct rz_ssi_priv *ssi = strm->priv;
+
+ guard(spinlock_irqsave)(&ssi->lock);
+
+ strm->substream = substream;
+}
+
+static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi,
+ struct rz_ssi_stream *strm)
+{
+ guard(spinlock_irqsave)(&ssi->lock);
+
+ return strm->substream && strm->substream->runtime;
+}
+
+static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm)
+{
+ return strm->substream && strm->running;
+}
+
+static void rz_ssi_stream_init(struct rz_ssi_stream *strm,
+ struct snd_pcm_substream *substream)
+{
+ rz_ssi_set_substream(strm, substream);
+ strm->dma_buffer_pos = 0;
+ strm->completed_dma_buf_pos = 0;
+ strm->period_counter = 0;
+ strm->buffer_pos = 0;
+
+ strm->oerr_num = 0;
+ strm->uerr_num = 0;
+ strm->running = 0;
+
+ /* fifo init */
+ strm->fifo_sample_size = SSI_FIFO_DEPTH;
+}
+
+static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi,
+ struct rz_ssi_stream *strm)
+{
+ struct device *dev = ssi->dev;
+
+ rz_ssi_set_substream(strm, NULL);
+
+ if (strm->oerr_num > 0)
+ dev_info(dev, "overrun = %d\n", strm->oerr_num);
+
+ if (strm->uerr_num > 0)
+ dev_info(dev, "underrun = %d\n", strm->uerr_num);
+}
+
+static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
+ unsigned int channels)
+{
+ static u8 ckdv[] = { 1, 2, 4, 8, 16, 32, 64, 128, 6, 12, 24, 48, 96 };
+ unsigned int channel_bits = 32; /* System Word Length */
+ unsigned long bclk_rate = rate * channels * channel_bits;
+ unsigned int div;
+ unsigned int i;
+ u32 ssicr = 0;
+ u32 clk_ckdv;
+
+ /* Clear AUCKE so we can set MST */
+ rz_ssi_reg_writel(ssi, SSIFCR, 0);
+
+ /* Continue to output LRCK pin even when idle */
+ rz_ssi_reg_writel(ssi, SSIOFR, SSIOFR_LRCONT);
+ if (ssi->audio_clk_1 && ssi->audio_clk_2) {
+ if (ssi->audio_clk_1 % bclk_rate)
+ ssi->audio_mck = ssi->audio_clk_2;
+ else
+ ssi->audio_mck = ssi->audio_clk_1;
+ }
+
+ /* Clock setting */
+ ssicr |= SSICR_MST;
+ if (ssi->audio_mck == ssi->audio_clk_1)
+ ssicr |= SSICR_CKS;
+ if (ssi->bckp_rise)
+ ssicr |= SSICR_BCKP;
+ if (ssi->lrckp_fsync_fall)
+ ssicr |= SSICR_LRCKP;
+
+ /* Determine the clock divider */
+ clk_ckdv = 0;
+ div = ssi->audio_mck / bclk_rate;
+ /* try to find an match */
+ for (i = 0; i < ARRAY_SIZE(ckdv); i++) {
+ if (ckdv[i] == div) {
+ clk_ckdv = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(ckdv)) {
+ dev_err(ssi->dev, "Rate not divisible by audio clock source\n");
+ return -EINVAL;
+ }
+
+ /*
+ * DWL: Data Word Length = 16 bits
+ * SWL: System Word Length = 32 bits
+ */
+ ssicr |= SSICR_CKDV(clk_ckdv);
+ ssicr |= SSICR_DWL(1) | SSICR_SWL(3);
+ rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ rz_ssi_reg_writel(ssi, SSIFCR, SSIFCR_AUCKE | SSIFCR_FIFO_RST);
+
+ return 0;
+}
+
+static void rz_ssi_set_idle(struct rz_ssi_priv *ssi)
+{
+ u32 tmp;
+ int ret;
+
+ /* Disable irqs */
+ rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN |
+ SSICR_RUIEN | SSICR_ROIEN, 0);
+ rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_TIE | SSIFCR_RIE, 0);
+
+ /* Clear all error flags */
+ rz_ssi_reg_mask_setl(ssi, SSISR,
+ (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ |
+ SSISR_RUIRQ), 0);
+
+ /* Wait for idle */
+ ret = readl_poll_timeout_atomic(ssi->base + SSISR, tmp, (tmp & SSISR_IIRQ), 1, 100);
+ if (ret)
+ dev_warn_ratelimited(ssi->dev, "timeout waiting for SSI idle\n");
+
+ /* Hold FIFOs in reset */
+ rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_FIFO_RST);
+}
+
+static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
+{
+ bool is_play = rz_ssi_stream_is_play(strm->substream);
+ bool is_full_duplex;
+ u32 ssicr, ssifcr;
+
+ is_full_duplex = ssi->dup.tx_active && ssi->dup.rx_active;
+ ssicr = rz_ssi_reg_readl(ssi, SSICR);
+ ssifcr = rz_ssi_reg_readl(ssi, SSIFCR);
+ if (!is_full_duplex) {
+ ssifcr &= ~0xF;
+ } else if (ssi->dup.one_stream_triggered) {
+ rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0);
+ rz_ssi_set_idle(ssi);
+ ssifcr &= ~SSIFCR_FIFO_RST;
+ }
+
+ /* FIFO interrupt thresholds */
+ if (rz_ssi_is_dma_enabled(ssi))
+ rz_ssi_reg_writel(ssi, SSISCR, 0);
+ else
+ rz_ssi_reg_writel(ssi, SSISCR,
+ SSISCR_TDES(strm->fifo_sample_size / 2 - 1) |
+ SSISCR_RDFS(0));
+
+ /* enable IRQ */
+ if (is_play) {
+ ssicr |= SSICR_TUIEN | SSICR_TOIEN;
+ ssifcr |= SSIFCR_TIE;
+ if (!is_full_duplex)
+ ssifcr |= SSIFCR_RFRST;
+ } else {
+ ssicr |= SSICR_RUIEN | SSICR_ROIEN;
+ ssifcr |= SSIFCR_RIE;
+ if (!is_full_duplex)
+ ssifcr |= SSIFCR_TFRST;
+ }
+
+ rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ rz_ssi_reg_writel(ssi, SSIFCR, ssifcr);
+
+ /* Clear all error flags */
+ rz_ssi_reg_mask_setl(ssi, SSISR,
+ (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ |
+ SSISR_RUIRQ), 0);
+
+ strm->running = 1;
+ if (!is_full_duplex) {
+ ssicr |= is_play ? SSICR_TEN : SSICR_REN;
+ rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ } else if (ssi->dup.one_stream_triggered) {
+ ssicr |= SSICR_TEN | SSICR_REN;
+ rz_ssi_reg_writel(ssi, SSICR, ssicr);
+ ssi->dup.one_stream_triggered = false;
+ } else {
+ ssi->dup.one_stream_triggered = true;
+ }
+
+ return 0;
+}
+
+static int rz_ssi_swreset(struct rz_ssi_priv *ssi)
+{
+ u32 tmp;
+
+ rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST);
+ rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0);
+ return readl_poll_timeout_atomic(ssi->base + SSIFCR, tmp, !(tmp & SSIFCR_SSIRST), 1, 5);
+}
+
+static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
+{
+ strm->running = 0;
+
+ if (rz_ssi_is_stream_running(&ssi->playback) ||
+ rz_ssi_is_stream_running(&ssi->capture))
+ return 0;
+
+ /* Disable TX/RX */
+ rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0);
+
+ /* Cancel all remaining DMA transactions */
+ if (rz_ssi_is_dma_enabled(ssi)) {
+ if (ssi->playback.dma_ch)
+ dmaengine_terminate_async(ssi->playback.dma_ch);
+ if (ssi->capture.dma_ch)
+ dmaengine_terminate_async(ssi->capture.dma_ch);
+ }
+
+ rz_ssi_set_idle(ssi);
+
+ return 0;
+}
+
+static void rz_ssi_pointer_update(struct rz_ssi_stream *strm, int frames)
+{
+ struct snd_pcm_substream *substream = strm->substream;
+ struct snd_pcm_runtime *runtime;
+ int current_period;
+
+ if (!strm->running || !substream || !substream->runtime)
+ return;
+
+ runtime = substream->runtime;
+ strm->buffer_pos += frames;
+ WARN_ON(strm->buffer_pos > runtime->buffer_size);
+
+ /* ring buffer */
+ if (strm->buffer_pos == runtime->buffer_size)
+ strm->buffer_pos = 0;
+
+ current_period = strm->buffer_pos / runtime->period_size;
+ if (strm->period_counter != current_period) {
+ snd_pcm_period_elapsed(strm->substream);
+ strm->period_counter = current_period;
+ }
+
+ strm->completed_dma_buf_pos += runtime->period_size;
+ if (strm->completed_dma_buf_pos >= runtime->buffer_size)
+ strm->completed_dma_buf_pos = 0;
+}
+
+static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
+{
+ struct snd_pcm_substream *substream = strm->substream;
+ struct snd_pcm_runtime *runtime;
+ u16 *buf;
+ int fifo_samples;
+ int frames_left;
+ int samples;
+ int i;
+
+ if (!rz_ssi_stream_is_valid(ssi, strm))
+ return -EINVAL;
+
+ runtime = substream->runtime;
+
+ do {
+ /* frames left in this period */
+ frames_left = runtime->period_size -
+ (strm->buffer_pos % runtime->period_size);
+ if (!frames_left)
+ frames_left = runtime->period_size;
+
+ /* Samples in RX FIFO */
+ fifo_samples = (rz_ssi_reg_readl(ssi, SSIFSR) >>
+ SSIFSR_RDC_SHIFT) & SSIFSR_RDC_MASK;
+
+ /* Only read full frames at a time */
+ samples = 0;
+ while (frames_left && (fifo_samples >= runtime->channels)) {
+ samples += runtime->channels;
+ fifo_samples -= runtime->channels;
+ frames_left--;
+ }
+
+ /* not enough samples yet */
+ if (!samples)
+ break;
+
+ /* calculate new buffer index */
+ buf = (u16 *)runtime->dma_area;
+ buf += strm->buffer_pos * runtime->channels;
+
+ /* Note, only supports 16-bit samples */
+ for (i = 0; i < samples; i++)
+ *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16);
+
+ rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
+ rz_ssi_pointer_update(strm, samples / runtime->channels);
+ } while (!frames_left && fifo_samples >= runtime->channels);
+
+ return 0;
+}
+
+static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
+{
+ struct snd_pcm_substream *substream = strm->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int sample_space;
+ int samples = 0;
+ int frames_left;
+ int i;
+ u32 ssifsr;
+ u16 *buf;
+
+ if (!rz_ssi_stream_is_valid(ssi, strm))
+ return -EINVAL;
+
+ /* frames left in this period */
+ frames_left = runtime->period_size - (strm->buffer_pos %
+ runtime->period_size);
+ if (frames_left == 0)
+ frames_left = runtime->period_size;
+
+ sample_space = strm->fifo_sample_size;
+ ssifsr = rz_ssi_reg_readl(ssi, SSIFSR);
+ sample_space -= (ssifsr >> SSIFSR_TDC_SHIFT) & SSIFSR_TDC_MASK;
+ if (sample_space < 0)
+ return -EINVAL;
+
+ /* Only add full frames at a time */
+ while (frames_left && (sample_space >= runtime->channels)) {
+ samples += runtime->channels;
+ sample_space -= runtime->channels;
+ frames_left--;
+ }
+
+ /* no space to send anything right now */
+ if (samples == 0)
+ return 0;
+
+ /* calculate new buffer index */
+ buf = (u16 *)(runtime->dma_area);
+ buf += strm->buffer_pos * runtime->channels;
+
+ /* Note, only supports 16-bit samples */
+ for (i = 0; i < samples; i++)
+ rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16));
+
+ rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_TDE, 0);
+ rz_ssi_pointer_update(strm, samples / runtime->channels);
+
+ return 0;
+}
+
+static irqreturn_t rz_ssi_interrupt(int irq, void *data)
+{
+ struct rz_ssi_stream *strm_playback = NULL;
+ struct rz_ssi_stream *strm_capture = NULL;
+ struct rz_ssi_priv *ssi = data;
+ u32 ssisr = rz_ssi_reg_readl(ssi, SSISR);
+
+ if (ssi->playback.substream)
+ strm_playback = &ssi->playback;
+ if (ssi->capture.substream)
+ strm_capture = &ssi->capture;
+
+ if (!strm_playback && !strm_capture)
+ return IRQ_HANDLED; /* Left over TX/RX interrupt */
+
+ if (irq == ssi->irq_int) { /* error or idle */
+ bool is_stopped = false;
+ int i, count;
+
+ if (rz_ssi_is_dma_enabled(ssi))
+ count = 4;
+ else
+ count = 1;
+
+ if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ))
+ is_stopped = true;
+
+ if (ssi->capture.substream && is_stopped) {
+ if (ssisr & SSISR_RUIRQ)
+ strm_capture->uerr_num++;
+ if (ssisr & SSISR_ROIRQ)
+ strm_capture->oerr_num++;
+
+ rz_ssi_stop(ssi, strm_capture);
+ }
+
+ if (ssi->playback.substream && is_stopped) {
+ if (ssisr & SSISR_TUIRQ)
+ strm_playback->uerr_num++;
+ if (ssisr & SSISR_TOIRQ)
+ strm_playback->oerr_num++;
+
+ rz_ssi_stop(ssi, strm_playback);
+ }
+
+ /* Clear all flags */
+ rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | SSISR_TUIRQ |
+ SSISR_ROIRQ | SSISR_RUIRQ, 0);
+
+ /* Add/remove more data */
+ if (ssi->capture.substream && is_stopped) {
+ for (i = 0; i < count; i++)
+ strm_capture->transfer(ssi, strm_capture);
+ }
+
+ if (ssi->playback.substream && is_stopped) {
+ for (i = 0; i < count; i++)
+ strm_playback->transfer(ssi, strm_playback);
+ }
+
+ /* Resume */
+ if (ssi->playback.substream && is_stopped)
+ rz_ssi_start(ssi, &ssi->playback);
+ if (ssi->capture.substream && is_stopped)
+ rz_ssi_start(ssi, &ssi->capture);
+ }
+
+ if (!rz_ssi_is_stream_running(&ssi->playback) &&
+ !rz_ssi_is_stream_running(&ssi->capture))
+ return IRQ_HANDLED;
+
+ /* tx data empty */
+ if (irq == ssi->irq_tx && rz_ssi_is_stream_running(&ssi->playback))
+ strm_playback->transfer(ssi, &ssi->playback);
+
+ /* rx data full */
+ if (irq == ssi->irq_rx && rz_ssi_is_stream_running(&ssi->capture)) {
+ strm_capture->transfer(ssi, &ssi->capture);
+ rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
+ }
+
+ if (irq == ssi->irq_rt) {
+ if (ssi->playback.substream) {
+ strm_playback->transfer(ssi, &ssi->playback);
+ } else {
+ strm_capture->transfer(ssi, &ssi->capture);
+ rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi,
+ struct dma_chan *dma_ch, bool is_play)
+{
+ struct dma_slave_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ cfg.dst_addr = ssi->phys + SSIFTDR;
+ cfg.src_addr = ssi->phys + SSIFRDR;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ return dmaengine_slave_config(dma_ch, &cfg);
+}
+
+static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi,
+ struct rz_ssi_stream *strm)
+{
+ struct snd_pcm_substream *substream = strm->substream;
+ struct dma_async_tx_descriptor *desc;
+ struct snd_pcm_runtime *runtime;
+ enum dma_transfer_direction dir;
+ u32 dma_paddr, dma_size;
+ int amount;
+
+ if (!rz_ssi_stream_is_valid(ssi, strm))
+ return -EINVAL;
+
+ runtime = substream->runtime;
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING)
+ /*
+ * Stream is ending, so do not queue up any more DMA
+ * transfers otherwise we play partial sound clips
+ * because we can't shut off the DMA quick enough.
+ */
+ return 0;
+
+ dir = rz_ssi_stream_is_play(substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+
+ /* Always transfer 1 period */
+ amount = runtime->period_size;
+
+ /* DMA physical address and size */
+ dma_paddr = runtime->dma_addr + frames_to_bytes(runtime,
+ strm->dma_buffer_pos);
+ dma_size = frames_to_bytes(runtime, amount);
+ desc = dmaengine_prep_slave_single(strm->dma_ch, dma_paddr, dma_size,
+ dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(ssi->dev, "dmaengine_prep_slave_single() fail\n");
+ return -ENOMEM;
+ }
+
+ desc->callback = rz_ssi_dma_complete;
+ desc->callback_param = strm;
+
+ if (dmaengine_submit(desc) < 0) {
+ dev_err(ssi->dev, "dmaengine_submit() fail\n");
+ return -EIO;
+ }
+
+ /* Update DMA pointer */
+ strm->dma_buffer_pos += amount;
+ if (strm->dma_buffer_pos >= runtime->buffer_size)
+ strm->dma_buffer_pos = 0;
+
+ /* Start DMA */
+ dma_async_issue_pending(strm->dma_ch);
+
+ return 0;
+}
+
+static void rz_ssi_dma_complete(void *data)
+{
+ struct rz_ssi_stream *strm = (struct rz_ssi_stream *)data;
+
+ if (!strm->running || !strm->substream || !strm->substream->runtime)
+ return;
+
+ /* Note that next DMA transaction has probably already started */
+ rz_ssi_pointer_update(strm, strm->substream->runtime->period_size);
+
+ /* Queue up another DMA transaction */
+ rz_ssi_dma_transfer(strm->priv, strm);
+}
+
+static void rz_ssi_release_dma_channels(struct rz_ssi_priv *ssi)
+{
+ if (ssi->playback.dma_ch) {
+ dma_release_channel(ssi->playback.dma_ch);
+ ssi->playback.dma_ch = NULL;
+ if (ssi->dma_rt)
+ ssi->dma_rt = false;
+ }
+
+ if (ssi->capture.dma_ch) {
+ dma_release_channel(ssi->capture.dma_ch);
+ ssi->capture.dma_ch = NULL;
+ }
+}
+
+static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev)
+{
+ ssi->playback.dma_ch = dma_request_chan(dev, "tx");
+ if (IS_ERR(ssi->playback.dma_ch))
+ ssi->playback.dma_ch = NULL;
+
+ ssi->capture.dma_ch = dma_request_chan(dev, "rx");
+ if (IS_ERR(ssi->capture.dma_ch))
+ ssi->capture.dma_ch = NULL;
+
+ if (!ssi->playback.dma_ch && !ssi->capture.dma_ch) {
+ ssi->playback.dma_ch = dma_request_chan(dev, "rt");
+ if (IS_ERR(ssi->playback.dma_ch)) {
+ ssi->playback.dma_ch = NULL;
+ goto no_dma;
+ }
+
+ ssi->dma_rt = true;
+ }
+
+ if (!rz_ssi_is_dma_enabled(ssi))
+ goto no_dma;
+
+ if (ssi->playback.dma_ch &&
+ (rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, true) < 0))
+ goto no_dma;
+
+ if (ssi->capture.dma_ch &&
+ (rz_ssi_dma_slave_config(ssi, ssi->capture.dma_ch, false) < 0))
+ goto no_dma;
+
+ return 0;
+
+no_dma:
+ rz_ssi_release_dma_channels(ssi);
+
+ return -ENODEV;
+}
+
+static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
+{
+ struct snd_pcm_substream *substream = strm->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ strm->dma_buffer_pos = strm->completed_dma_buf_pos + runtime->period_size;
+
+ if (rz_ssi_is_stream_running(&ssi->playback) ||
+ rz_ssi_is_stream_running(&ssi->capture))
+ return 0;
+
+ ret = rz_ssi_swreset(ssi);
+ if (ret)
+ return ret;
+
+ return rz_ssi_clk_setup(ssi, ssi->hw_params_cache.rate,
+ ssi->hw_params_cache.channels);
+}
+
+static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+ struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream);
+ int ret = 0, i, num_transfer = 1;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = rz_ssi_trigger_resume(ssi, strm);
+ if (ret)
+ return ret;
+
+ fallthrough;
+
+ case SNDRV_PCM_TRIGGER_START:
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ rz_ssi_stream_init(strm, substream);
+
+ if (ssi->dma_rt) {
+ bool is_playback;
+
+ is_playback = rz_ssi_stream_is_play(substream);
+ ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
+ is_playback);
+ /* Fallback to pio */
+ if (ret < 0) {
+ ssi->playback.transfer = rz_ssi_pio_send;
+ ssi->capture.transfer = rz_ssi_pio_recv;
+ rz_ssi_release_dma_channels(ssi);
+ }
+ }
+
+ /* For DMA, queue up multiple DMA descriptors */
+ if (rz_ssi_is_dma_enabled(ssi))
+ num_transfer = 4;
+
+ for (i = 0; i < num_transfer; i++) {
+ ret = strm->transfer(ssi, strm);
+ if (ret)
+ goto done;
+ }
+
+ ret = rz_ssi_start(ssi, strm);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ rz_ssi_stop(ssi, strm);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ rz_ssi_stop(ssi, strm);
+ rz_ssi_stream_quit(ssi, strm);
+ break;
+ }
+
+done:
+ return ret;
+}
+
+static int rz_ssi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ break;
+ default:
+ dev_err(ssi->dev, "Codec should be clk and frame consumer\n");
+ return -EINVAL;
+ }
+
+ /*
+ * set clock polarity
+ *
+ * "normal" BCLK = Signal is available at rising edge of BCLK
+ * "normal" FSYNC = (I2S) Left ch starts with falling FSYNC edge
+ */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ ssi->bckp_rise = false;
+ ssi->lrckp_fsync_fall = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ssi->bckp_rise = false;
+ ssi->lrckp_fsync_fall = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ssi->bckp_rise = true;
+ ssi->lrckp_fsync_fall = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ssi->bckp_rise = true;
+ ssi->lrckp_fsync_fall = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* only i2s support */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ dev_err(ssi->dev, "Only I2S mode is supported.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rz_ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ssi->dup.tx_active = true;
+ else
+ ssi->dup.rx_active = true;
+
+ return 0;
+}
+
+static void rz_ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ssi->dup.tx_active = false;
+ else
+ ssi->dup.rx_active = false;
+}
+
+static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate,
+ unsigned int channels,
+ unsigned int sample_width,
+ unsigned int sample_bits)
+{
+ if (ssi->hw_params_cache.rate != rate ||
+ ssi->hw_params_cache.channels != channels ||
+ ssi->hw_params_cache.sample_width != sample_width ||
+ ssi->hw_params_cache.sample_bits != sample_bits)
+ return false;
+
+ return true;
+}
+
+static void rz_ssi_cache_hw_params(struct rz_ssi_priv *ssi, unsigned int rate,
+ unsigned int channels,
+ unsigned int sample_width,
+ unsigned int sample_bits)
+{
+ ssi->hw_params_cache.rate = rate;
+ ssi->hw_params_cache.channels = channels;
+ ssi->hw_params_cache.sample_width = sample_width;
+ ssi->hw_params_cache.sample_bits = sample_bits;
+}
+
+static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+ unsigned int sample_bits = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+ unsigned int sample_width = params_width(params);
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ if (sample_bits != 16) {
+ dev_err(ssi->dev, "Unsupported sample width: %d\n",
+ sample_bits);
+ return -EINVAL;
+ }
+
+ if (channels != 2) {
+ dev_err(ssi->dev, "Number of channels not matched: %d\n",
+ channels);
+ return -EINVAL;
+ }
+
+ if (rz_ssi_is_stream_running(&ssi->playback) ||
+ rz_ssi_is_stream_running(&ssi->capture)) {
+ if (rz_ssi_is_valid_hw_params(ssi, rate, channels, sample_width, sample_bits))
+ return 0;
+
+ dev_err(ssi->dev, "Full duplex needs same HW params\n");
+ return -EINVAL;
+ }
+
+ rz_ssi_cache_hw_params(ssi, rate, channels, sample_width, sample_bits);
+
+ ret = rz_ssi_swreset(ssi);
+ if (ret)
+ return ret;
+
+ return rz_ssi_clk_setup(ssi, rate, channels);
+}
+
+static const struct snd_soc_dai_ops rz_ssi_dai_ops = {
+ .startup = rz_ssi_startup,
+ .shutdown = rz_ssi_shutdown,
+ .trigger = rz_ssi_dai_trigger,
+ .set_fmt = rz_ssi_dai_set_fmt,
+ .hw_params = rz_ssi_dai_hw_params,
+};
+
+static const struct snd_pcm_hardware rz_ssi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME,
+ .buffer_bytes_max = PREALLOC_BUFFER,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .channels_min = SSI_CHAN_MIN,
+ .channels_max = SSI_CHAN_MAX,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 32 * 2,
+};
+
+static int rz_ssi_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rz_ssi_pcm_hardware);
+
+ return snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
+ struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream);
+
+ return strm->buffer_pos;
+}
+
+static int rz_ssi_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+ return 0;
+}
+
+static struct snd_soc_dai_driver rz_ssi_soc_dai[] = {
+ {
+ .name = "rz-ssi-dai",
+ .playback = {
+ .rates = SSI_RATES,
+ .formats = SSI_FMTS,
+ .channels_min = SSI_CHAN_MIN,
+ .channels_max = SSI_CHAN_MAX,
+ },
+ .capture = {
+ .rates = SSI_RATES,
+ .formats = SSI_FMTS,
+ .channels_min = SSI_CHAN_MIN,
+ .channels_max = SSI_CHAN_MAX,
+ },
+ .ops = &rz_ssi_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver rz_ssi_soc_component = {
+ .name = "rz-ssi",
+ .open = rz_ssi_pcm_open,
+ .pointer = rz_ssi_pcm_pointer,
+ .pcm_construct = rz_ssi_pcm_new,
+ .legacy_dai_naming = 1,
+};
+
+static int rz_ssi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rz_ssi_priv *ssi;
+ struct clk *audio_clk;
+ struct resource *res;
+ int ret;
+
+ ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
+ if (!ssi)
+ return -ENOMEM;
+
+ ssi->dev = dev;
+ ssi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(ssi->base))
+ return PTR_ERR(ssi->base);
+
+ ssi->phys = res->start;
+ ssi->clk = devm_clk_get(dev, "ssi");
+ if (IS_ERR(ssi->clk))
+ return PTR_ERR(ssi->clk);
+
+ ssi->sfr_clk = devm_clk_get(dev, "ssi_sfr");
+ if (IS_ERR(ssi->sfr_clk))
+ return PTR_ERR(ssi->sfr_clk);
+
+ audio_clk = devm_clk_get(dev, "audio_clk1");
+ if (IS_ERR(audio_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
+ "no audio clk1");
+
+ ssi->audio_clk_1 = clk_get_rate(audio_clk);
+ audio_clk = devm_clk_get(dev, "audio_clk2");
+ if (IS_ERR(audio_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
+ "no audio clk2");
+
+ ssi->audio_clk_2 = clk_get_rate(audio_clk);
+ if (!(ssi->audio_clk_1 || ssi->audio_clk_2))
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "no audio clk1 or audio clk2");
+
+ ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2;
+
+ /* Detect DMA support */
+ ret = rz_ssi_dma_request(ssi, dev);
+ if (ret < 0) {
+ dev_warn(dev, "DMA not available, using PIO\n");
+ ssi->playback.transfer = rz_ssi_pio_send;
+ ssi->capture.transfer = rz_ssi_pio_recv;
+ } else {
+ dev_info(dev, "DMA enabled");
+ ssi->playback.transfer = rz_ssi_dma_transfer;
+ ssi->capture.transfer = rz_ssi_dma_transfer;
+ }
+
+ ssi->playback.priv = ssi;
+ ssi->capture.priv = ssi;
+
+ spin_lock_init(&ssi->lock);
+ dev_set_drvdata(dev, ssi);
+
+ /* Error Interrupt */
+ ssi->irq_int = platform_get_irq_byname(pdev, "int_req");
+ if (ssi->irq_int < 0) {
+ ret = ssi->irq_int;
+ goto err_release_dma_chs;
+ }
+
+ ret = devm_request_irq(dev, ssi->irq_int, &rz_ssi_interrupt,
+ 0, dev_name(dev), ssi);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "irq request error (int_req)\n");
+ goto err_release_dma_chs;
+ }
+
+ if (!rz_ssi_is_dma_enabled(ssi)) {
+ /* Tx and Rx interrupts (pio only) */
+ ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx");
+ ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx");
+ if (ssi->irq_tx == -ENXIO && ssi->irq_rx == -ENXIO) {
+ ssi->irq_rt = platform_get_irq_byname(pdev, "dma_rt");
+ if (ssi->irq_rt < 0)
+ return ssi->irq_rt;
+
+ ret = devm_request_irq(dev, ssi->irq_rt,
+ &rz_ssi_interrupt, 0,
+ dev_name(dev), ssi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "irq request error (dma_rt)\n");
+ } else {
+ if (ssi->irq_tx < 0)
+ return ssi->irq_tx;
+
+ if (ssi->irq_rx < 0)
+ return ssi->irq_rx;
+
+ ret = devm_request_irq(dev, ssi->irq_tx,
+ &rz_ssi_interrupt, 0,
+ dev_name(dev), ssi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "irq request error (dma_tx)\n");
+
+ ret = devm_request_irq(dev, ssi->irq_rx,
+ &rz_ssi_interrupt, 0,
+ dev_name(dev), ssi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "irq request error (dma_rx)\n");
+ }
+ }
+
+ ssi->rstc = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(ssi->rstc)) {
+ ret = PTR_ERR(ssi->rstc);
+ goto err_release_dma_chs;
+ }
+
+ /* Default 0 for power saving. Can be overridden via sysfs. */
+ pm_runtime_set_autosuspend_delay(dev, 0);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable runtime PM!\n");
+ goto err_release_dma_chs;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &rz_ssi_soc_component,
+ rz_ssi_soc_dai,
+ ARRAY_SIZE(rz_ssi_soc_dai));
+ if (ret < 0) {
+ dev_err(dev, "failed to register snd component\n");
+ goto err_release_dma_chs;
+ }
+
+ return 0;
+
+err_release_dma_chs:
+ rz_ssi_release_dma_channels(ssi);
+
+ return ret;
+}
+
+static void rz_ssi_remove(struct platform_device *pdev)
+{
+ struct rz_ssi_priv *ssi = dev_get_drvdata(&pdev->dev);
+
+ rz_ssi_release_dma_channels(ssi);
+
+ reset_control_assert(ssi->rstc);
+}
+
+static const struct of_device_id rz_ssi_of_match[] = {
+ { .compatible = "renesas,rz-ssi", },
+ {/* Sentinel */},
+};
+MODULE_DEVICE_TABLE(of, rz_ssi_of_match);
+
+static int rz_ssi_runtime_suspend(struct device *dev)
+{
+ struct rz_ssi_priv *ssi = dev_get_drvdata(dev);
+
+ return reset_control_assert(ssi->rstc);
+}
+
+static int rz_ssi_runtime_resume(struct device *dev)
+{
+ struct rz_ssi_priv *ssi = dev_get_drvdata(dev);
+
+ return reset_control_deassert(ssi->rstc);
+}
+
+static const struct dev_pm_ops rz_ssi_pm_ops = {
+ RUNTIME_PM_OPS(rz_ssi_runtime_suspend, rz_ssi_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver rz_ssi_driver = {
+ .driver = {
+ .name = "rz-ssi-pcm-audio",
+ .of_match_table = rz_ssi_of_match,
+ .pm = pm_ptr(&rz_ssi_pm_ops),
+ },
+ .probe = rz_ssi_probe,
+ .remove = rz_ssi_remove,
+};
+
+module_platform_driver(rz_ssi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas RZ/G2L ASoC Serial Sound Interface Driver");
+MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/renesas/sh7760-ac97.c
index 4a3568a9bf59..d267243a159b 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/renesas/sh7760-ac97.c
@@ -1,10 +1,8 @@
-/*
- * Generic AC97 sound support for SH7760
- *
- * (c) 2007 Manuel Lauss
- *
- * Licensed under the GPLv2.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Generic AC97 sound support for SH7760
+//
+// (c) 2007 Manuel Lauss
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -16,14 +14,15 @@
#define IPSEL 0xFE400034
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("hac-dai.0")), /* HAC0 */
+ DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec", "ac97-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("sh7760-pcm-audio")));
+
static struct snd_soc_dai_link sh7760_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
- .cpu_dai_name = "hac-dai.0", /* HAC0 */
- .codec_dai_name = "ac97-hifi",
- .platform_name = "sh7760-pcm-audio",
- .codec_name = "ac97-codec",
- .ops = NULL,
+ SND_SOC_DAILINK_REG(ac97),
};
static struct snd_soc_card sh7760_ac97_soc_machine = {
@@ -68,6 +67,6 @@ static void __exit sh7760_ac97_exit(void)
module_init(sh7760_ac97_init);
module_exit(sh7760_ac97_exit);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/siu.h b/sound/soc/renesas/siu.h
index 83c3430ad797..a675c36fc9d9 100644
--- a/sound/soc/sh/siu.h
+++ b/sound/soc/renesas/siu.h
@@ -1,23 +1,9 @@
-/*
- * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
- *
- * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
+//
+// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
#ifndef SIU_H
#define SIU_H
@@ -110,7 +96,7 @@ struct siu_info {
};
struct siu_stream {
- struct tasklet_struct tasklet;
+ struct work_struct work;
struct snd_pcm_substream *substream;
snd_pcm_format_t format;
size_t buf_bytes;
@@ -183,7 +169,7 @@ static inline u32 siu_read32(u32 __iomem *addr)
#define SIU_BRGBSEL (0x108 / sizeof(u32))
#define SIU_BRRB (0x10c / sizeof(u32))
-extern struct snd_soc_platform_driver siu_platform;
+extern const struct snd_soc_component_driver siu_component;
extern struct siu_info *siu_i2s_data;
int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card);
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/renesas/siu_dai.c
index 76b2ab8c2b4a..7e771a164a80 100644
--- a/sound/soc/sh/siu_dai.c
+++ b/sound/soc/renesas/siu_dai.c
@@ -1,23 +1,9 @@
-/*
- * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
- *
- * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
+//
+// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
#include <linux/delay.h>
#include <linux/firmware.h>
@@ -333,7 +319,7 @@ static void siu_dai_spbstop(struct siu_port *port_info)
/* API functions */
/* Playback and capture hardware properties are identical */
-static struct snd_pcm_hardware siu_dai_pcm_hw = {
+static const struct snd_pcm_hardware siu_dai_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED,
.formats = SNDRV_PCM_FMTBIT_S16,
.rates = SNDRV_PCM_RATE_8000_48000,
@@ -441,7 +427,7 @@ static int siu_dai_put_volume(struct snd_kcontrol *kctrl,
return 0;
}
-static struct snd_kcontrol_new playback_controls = {
+static const struct snd_kcontrol_new playback_controls = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
@@ -451,7 +437,7 @@ static struct snd_kcontrol_new playback_controls = {
.private_value = VOLUME_PLAYBACK,
};
-static struct snd_kcontrol_new capture_controls = {
+static const struct snd_kcontrol_new capture_controls = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Volume",
.index = 0,
@@ -727,10 +713,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = {
.ops = &siu_dai_ops,
};
-static const struct snd_soc_component_driver siu_i2s_component = {
- .name = "siu-i2s",
-};
-
static int siu_probe(struct platform_device *pdev)
{
const struct firmware *fw_entry;
@@ -786,24 +768,19 @@ static int siu_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, info);
/* register using ARRAY version so we can keep dai name */
- ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+ ret = devm_snd_soc_register_component(&pdev->dev, &siu_component,
&siu_i2s_dai, 1);
if (ret < 0)
return ret;
- ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform);
- if (ret < 0)
- return ret;
-
pm_runtime_enable(&pdev->dev);
return 0;
}
-static int siu_remove(struct platform_device *pdev)
+static void siu_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- return 0;
}
static struct platform_driver siu_driver = {
@@ -819,3 +796,5 @@ module_platform_driver(siu_driver);
MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("siu_spb.bin");
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/renesas/siu_pcm.c
index 82902f56e82f..f15ff36e7934 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/renesas/siu_pcm.c
@@ -1,23 +1,10 @@
-/*
- * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
- *
- * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
+//
+// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
+
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
@@ -35,6 +22,7 @@
#include "siu.h"
+#define DRV_NAME "siu-i2s"
#define GET_MAX_PERIODS(buf_bytes, period_bytes) \
((buf_bytes) / (period_bytes))
#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \
@@ -82,7 +70,7 @@ static int siu_pcm_stmwrite_start(struct siu_port *port_info)
siu_stream->rw_flg = RWF_STM_WT;
/* DMA transfer start */
- tasklet_schedule(&siu_stream->tasklet);
+ queue_work(system_highpri_wq, &siu_stream->work);
return 0;
}
@@ -105,7 +93,7 @@ static void siu_dma_tx_complete(void *arg)
siu_stream->cur_period * siu_stream->period_bytes,
siu_stream->buf_bytes, siu_stream->cookie);
- tasklet_schedule(&siu_stream->tasklet);
+ queue_work(system_highpri_wq, &siu_stream->work);
/* Notify alsa: a period is done */
snd_pcm_period_elapsed(siu_stream->substream);
@@ -210,9 +198,10 @@ static int siu_pcm_rd_set(struct siu_port *port_info,
return 0;
}
-static void siu_io_tasklet(unsigned long data)
+static void siu_io_work(struct work_struct *work)
{
- struct siu_stream *siu_stream = (struct siu_stream *)data;
+ struct siu_stream *siu_stream = container_of(work, struct siu_stream,
+ work);
struct snd_pcm_substream *substream = siu_stream->substream;
struct device *dev = substream->pcm->card->dev;
struct snd_pcm_runtime *rt = substream->runtime;
@@ -228,14 +217,10 @@ static void siu_io_tasklet(unsigned long data)
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
dma_addr_t buff;
size_t count;
- u8 *virt;
buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
siu_stream->cur_period,
siu_stream->period_bytes);
- virt = PERIOD_OFFSET(rt->dma_area,
- siu_stream->cur_period,
- siu_stream->period_bytes);
count = siu_stream->period_bytes;
/* DMA transfer start */
@@ -265,7 +250,7 @@ static int siu_pcm_stmread_start(struct siu_port *port_info)
/* during stmread flag set */
siu_stream->rw_flg = RWF_STM_RD;
- tasklet_schedule(&siu_stream->tasklet);
+ queue_work(system_highpri_wq, &siu_stream->work);
return 0;
}
@@ -293,54 +278,21 @@ static int siu_pcm_stmread_stop(struct siu_port *port_info)
return 0;
}
-static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
- struct snd_pcm_hw_params *hw_params)
-{
- struct siu_info *info = siu_i2s_data;
- struct device *dev = ss->pcm->card->dev;
- int ret;
-
- dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
-
- ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params));
- if (ret < 0)
- dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n");
-
- return ret;
-}
-
-static int siu_pcm_hw_free(struct snd_pcm_substream *ss)
-{
- struct siu_info *info = siu_i2s_data;
- struct siu_port *port_info = siu_port_info(ss);
- struct device *dev = ss->pcm->card->dev;
- struct siu_stream *siu_stream;
-
- if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
- siu_stream = &port_info->playback;
- else
- siu_stream = &port_info->capture;
-
- dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
-
- return snd_pcm_lib_free_pages(ss);
-}
-
-static bool filter(struct dma_chan *chan, void *slave)
+static bool filter(struct dma_chan *chan, void *secondary)
{
- struct sh_dmae_slave *param = slave;
+ struct sh_dmae_slave *param = secondary;
- pr_debug("%s: slave ID %d\n", __func__, param->shdma_slave.slave_id);
+ pr_debug("%s: secondary ID %d\n", __func__, param->shdma_slave.slave_id);
chan->private = &param->shdma_slave;
return true;
}
-static int siu_pcm_open(struct snd_pcm_substream *ss)
+static int siu_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
/* Playback / Capture */
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
- struct siu_platform *pdata = rtd->platform->dev->platform_data;
+ struct siu_platform *pdata = component->dev->platform_data;
struct siu_info *info = siu_i2s_data;
struct siu_port *port_info = siu_port_info(ss);
struct siu_stream *siu_stream;
@@ -378,7 +330,8 @@ static int siu_pcm_open(struct snd_pcm_substream *ss)
return 0;
}
-static int siu_pcm_close(struct snd_pcm_substream *ss)
+static int siu_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct siu_info *info = siu_i2s_data;
struct device *dev = ss->pcm->card->dev;
@@ -400,12 +353,13 @@ static int siu_pcm_close(struct snd_pcm_substream *ss)
return 0;
}
-static int siu_pcm_prepare(struct snd_pcm_substream *ss)
+static int siu_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct siu_info *info = siu_i2s_data;
struct siu_port *port_info = siu_port_info(ss);
struct device *dev = ss->pcm->card->dev;
- struct snd_pcm_runtime *rt = ss->runtime;
+ struct snd_pcm_runtime *rt;
struct siu_stream *siu_stream;
snd_pcm_sframes_t xfer_cnt;
@@ -446,7 +400,8 @@ static int siu_pcm_prepare(struct snd_pcm_substream *ss)
return 0;
}
-static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
+static int siu_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss, int cmd)
{
struct siu_info *info = siu_i2s_data;
struct device *dev = ss->pcm->card->dev;
@@ -488,7 +443,9 @@ static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
* So far only resolution of one period is supported, subject to extending the
* dmangine API
*/
-static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
+static snd_pcm_uframes_t
+siu_pcm_pointer_dma(struct snd_soc_component *component,
+ struct snd_pcm_substream *ss)
{
struct device *dev = ss->pcm->card->dev;
struct siu_info *info = siu_i2s_data;
@@ -523,7 +480,8 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
-static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int siu_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
/* card->dev == socdev->dev, see snd_soc_new_pcms() */
struct snd_card *card = rtd->card->snd_card;
@@ -552,61 +510,44 @@ static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (ret < 0)
return ret;
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV, NULL,
+ snd_pcm_set_managed_buffer_all(pcm,
+ SNDRV_DMA_TYPE_DEV, card->dev,
SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX);
- if (ret < 0) {
- dev_err(card->dev,
- "snd_pcm_lib_preallocate_pages_for_all() err=%d",
- ret);
- goto fail;
- }
(*port_info)->pcm = pcm;
- /* IO tasklets */
- tasklet_init(&(*port_info)->playback.tasklet, siu_io_tasklet,
- (unsigned long)&(*port_info)->playback);
- tasklet_init(&(*port_info)->capture.tasklet, siu_io_tasklet,
- (unsigned long)&(*port_info)->capture);
+ /* IO works */
+ INIT_WORK(&(*port_info)->playback.work, siu_io_work);
+ INIT_WORK(&(*port_info)->capture.work, siu_io_work);
}
dev_info(card->dev, "SuperH SIU driver initialized.\n");
return 0;
-
-fail:
- siu_free_port(siu_ports[pdev->id]);
- dev_err(card->dev, "SIU: failed to initialize.\n");
- return ret;
}
-static void siu_pcm_free(struct snd_pcm *pcm)
+static void siu_pcm_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct platform_device *pdev = to_platform_device(pcm->card->dev);
struct siu_port *port_info = siu_ports[pdev->id];
- tasklet_kill(&port_info->capture.tasklet);
- tasklet_kill(&port_info->playback.tasklet);
+ cancel_work_sync(&port_info->capture.work);
+ cancel_work_sync(&port_info->playback.work);
siu_free_port(port_info);
dev_dbg(pcm->card->dev, "%s\n", __func__);
}
-static struct snd_pcm_ops siu_pcm_ops = {
- .open = siu_pcm_open,
- .close = siu_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = siu_pcm_hw_params,
- .hw_free = siu_pcm_hw_free,
- .prepare = siu_pcm_prepare,
- .trigger = siu_pcm_trigger,
- .pointer = siu_pcm_pointer_dma,
-};
-
-struct snd_soc_platform_driver siu_platform = {
- .ops = &siu_pcm_ops,
- .pcm_new = siu_pcm_new,
- .pcm_free = siu_pcm_free,
+const struct snd_soc_component_driver siu_component = {
+ .name = DRV_NAME,
+ .open = siu_pcm_open,
+ .close = siu_pcm_close,
+ .prepare = siu_pcm_prepare,
+ .trigger = siu_pcm_trigger,
+ .pointer = siu_pcm_pointer_dma,
+ .pcm_construct = siu_pcm_new,
+ .pcm_destruct = siu_pcm_free,
+ .legacy_dai_naming = 1,
};
-EXPORT_SYMBOL_GPL(siu_platform);
+EXPORT_SYMBOL_GPL(siu_component);
diff --git a/sound/soc/sh/ssi.c b/sound/soc/renesas/ssi.c
index 89ed1b107ac5..96cf523c2273 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/renesas/ssi.c
@@ -1,14 +1,11 @@
-/*
- * Serial Sound Interface (I2S) support for SH7760/SH7780
- *
- * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
- *
- * licensed under the terms outlined in the file COPYING at the root
- * of the linux kernel sources.
- *
- * dont forget to set IPSEL/OMSEL register bits (in your board code) to
- * enable SSI output pins!
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Serial Sound Interface (I2S) support for SH7760/SH7780
+//
+// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+//
+// dont forget to set IPSEL/OMSEL register bits (in your board code) to
+// enable SSI output pins!
/*
* LIMITATIONS:
@@ -294,20 +291,20 @@ static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_BP_FC:
ssicr |= CR_SCK_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_BC_FP:
ssicr |= CR_SWS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_BP_FP:
ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
break;
default:
- pr_debug("ssi: invalid master/slave configuration\n");
+ pr_debug("ssi: invalid master/secondary configuration\n");
return -EINVAL;
}
@@ -380,7 +377,8 @@ static struct snd_soc_dai_driver sh4_ssi_dai[] = {
};
static const struct snd_soc_component_driver sh4_ssi_component = {
- .name = "sh4-ssi",
+ .name = "sh4-ssi",
+ .legacy_dai_naming = 1,
};
static int sh4_soc_dai_probe(struct platform_device *pdev)
@@ -400,6 +398,6 @@ static struct platform_driver sh4_ssi_driver = {
module_platform_driver(sh4_ssi_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index f1e0c703e0d2..bd210fafe9fe 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -1,23 +1,46 @@
-config SND_SOC_ROCKCHIP
- tristate "ASoC support for Rockchip"
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Rockchip"
depends on COMPILE_TEST || ARCH_ROCKCHIP
- help
- Say Y or M if you want to add support for codecs attached to
- the Rockchip SoCs' Audio interfaces. You will also need to
- select the audio interfaces to support below.
+ depends on HAVE_CLK
config SND_SOC_ROCKCHIP_I2S
tristate "Rockchip I2S Device Driver"
- depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S driver for
- Rockchip I2S device. The device supports upto maximum of
+ Rockchip I2S device. The device supports up to maximum of
8 channels each for play and record.
+config SND_SOC_ROCKCHIP_I2S_TDM
+ tristate "Rockchip I2S/TDM Device Driver"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for the I2S/TDM driver for
+ Rockchip I2S/TDM devices, found in Rockchip SoCs. These devices
+ interface between the AHB bus and the I2S bus, and support up to a
+ maximum of 8 channels each for playback and recording.
+
+
+config SND_SOC_ROCKCHIP_PDM
+ tristate "Rockchip PDM Controller Driver"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select RATIONAL
+ help
+ Say Y or M if you want to add support for PDM driver for
+ Rockchip PDM Controller. The Controller supports up to maximum of
+ 8 channels record.
+
+config SND_SOC_ROCKCHIP_SAI
+ tristate "Rockchip SAI Controller Driver"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for the Rockchip Serial Audio
+ Interface controller found on Rockchip SoCs such as the RK3576. The
+ controller may support both playback and recording, with up to 4 lanes
+ for each and up to 128 channels per lane in TDM mode.
+
config SND_SOC_ROCKCHIP_SPDIF
tristate "Rockchip SPDIF Device Driver"
- depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF driver for
@@ -25,19 +48,48 @@ config SND_SOC_ROCKCHIP_SPDIF
config SND_SOC_ROCKCHIP_MAX98090
tristate "ASoC support for Rockchip boards using a MAX98090 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
+ depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
+ select SND_SOC_HDMI_CODEC
help
Say Y or M here if you want to add support for SoC audio on Rockchip
- boards using the MAX98090 codec, such as Veyron.
+ boards using the MAX98090 codec and HDMI codec, such as Veyron.
config SND_SOC_ROCKCHIP_RT5645
tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
+ depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_RT5645
help
Say Y or M here if you want to add support for SoC audio on Rockchip
boards using the RT5645/RT5650 codec, such as Veyron.
+
+config SND_SOC_RK3288_HDMI_ANALOG
+ tristate "ASoC support multiple codecs for Rockchip RK3288 boards"
+ depends on I2C && GPIOLIB
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_HDMI_CODEC
+ select SND_SOC_ES8328_I2C
+ select SND_SOC_ES8328_SPI if SPI_MASTER
+ select DRM_DW_HDMI_I2S_AUDIO if DRM_DW_HDMI
+ help
+ Say Y or M here if you want to add support for SoC audio on Rockchip
+ RK3288 boards using an analog output and the built-in HDMI audio.
+
+config SND_SOC_RK3399_GRU_SOUND
+ tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards"
+ depends on I2C && GPIOLIB && SPI
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5514
+ select SND_SOC_DA7219
+ select SND_SOC_RT5514_SPI
+ select SND_SOC_HDMI_CODEC
+ select SND_SOC_DMIC
+ help
+ Say Y or M here if you want to add support multiple codecs for SoC
+ audio on Rockchip RK3399 GRU boards.
+
+endmenu
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index c0bf560125f3..af6dc1165347 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -1,12 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
# ROCKCHIP Platform Support
-snd-soc-rockchip-i2s-objs := rockchip_i2s.o
-snd-soc-rockchip-spdif-objs := rockchip_spdif.o
+snd-soc-rockchip-i2s-y := rockchip_i2s.o
+snd-soc-rockchip-i2s-tdm-y := rockchip_i2s_tdm.o
+snd-soc-rockchip-pdm-y := rockchip_pdm.o
+snd-soc-rockchip-sai-y := rockchip_sai.o
+snd-soc-rockchip-spdif-y := rockchip_spdif.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SAI) += snd-soc-rockchip-sai.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o
-snd-soc-rockchip-max98090-objs := rockchip_max98090.o
-snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
+snd-soc-rockchip-max98090-y := rockchip_max98090.o
+snd-soc-rockchip-rt5645-y := rockchip_rt5645.o
+snd-soc-rk3288-hdmi-analog-y := rk3288_hdmi_analog.o
+snd-soc-rk3399-gru-sound-y := rk3399_gru_sound.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o
+obj-$(CONFIG_SND_SOC_RK3288_HDMI_ANALOG) += snd-soc-rk3288-hdmi-analog.o
+obj-$(CONFIG_SND_SOC_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o
diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c
new file mode 100644
index 000000000000..cf642a23c38a
--- /dev/null
+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip machine ASoC driver for RK3288 boards that have an HDMI and analog
+ * audio output
+ *
+ * Copyright (c) 2016, Collabora Ltd.
+ *
+ * Authors: Sjoerd Simons <sjoerd.simons@collabora.com>,
+ * Romain Perier <romain.perier@collabora.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "rockchip_i2s.h"
+
+#define DRV_NAME "rk3288-snd-hdmi-analog"
+
+struct rk_drvdata {
+ struct gpio_desc *gpio_hp_en;
+};
+
+static int rk_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct rk_drvdata *machine = snd_soc_card_get_drvdata(card);
+
+ gpiod_set_value_cansleep(machine->gpio_hp_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static struct snd_soc_jack headphone_jack;
+static struct snd_soc_jack_pin headphone_jack_pins[] = {
+ {
+ .pin = "Analog",
+ .mask = SND_JACK_HEADPHONE
+ },
+};
+
+static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Analog", rk_hp_power),
+ SND_SOC_DAPM_LINE("HDMI", NULL),
+};
+
+static const struct snd_kcontrol_new rk_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Analog"),
+ SOC_DAPM_PIN_SWITCH("HDMI"),
+};
+
+static int rk_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int mclk;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 192000:
+ mclk = 24576000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev, "Can't set cpu clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_jack_gpio rk_hp_jack_gpio = {
+ .name = "rockchip,hp-det",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150
+};
+
+static int rk_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct device *dev = card->dev;
+
+ /* Enable optional Headset Jack detection */
+ if (of_property_present(dev->of_node, "rockchip,hp-det-gpios")) {
+ rk_hp_jack_gpio.gpiod_dev = dev;
+ snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &headphone_jack,
+ headphone_jack_pins,
+ ARRAY_SIZE(headphone_jack_pins));
+ snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops rk_ops = {
+ .hw_params = rk_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(audio,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, NULL),
+ COMP_CODEC("hdmi-audio-codec.2.auto", "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link rk_dailink = {
+ .name = "Codecs",
+ .stream_name = "Audio",
+ .init = rk_init,
+ .ops = &rk_ops,
+ /* Set codecs as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(audio),
+};
+
+static struct snd_soc_card snd_soc_card_rk = {
+ .name = "ROCKCHIP-I2S",
+ .dai_link = &rk_dailink,
+ .num_links = 1,
+ .num_aux_devs = 0,
+ .dapm_widgets = rk_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
+ .controls = rk_mc_controls,
+ .num_controls = ARRAY_SIZE(rk_mc_controls),
+};
+
+static int snd_rk_mc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card = &snd_soc_card_rk;
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_drvdata *machine;
+ struct of_phandle_args args;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct rk_drvdata),
+ GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card->dev = &pdev->dev;
+
+ machine->gpio_hp_en = devm_gpiod_get_optional(&pdev->dev, "rockchip,hp-en", GPIOD_OUT_LOW);
+ if (IS_ERR(machine->gpio_hp_en))
+ return PTR_ERR(machine->gpio_hp_en);
+ gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en");
+
+ ret = snd_soc_of_parse_card_name(card, "rockchip,model");
+ if (ret) {
+ dev_err(card->dev, "SoC parse card name failed %d\n", ret);
+ return ret;
+ }
+
+ rk_dailink.codecs[0].of_node = of_parse_phandle(np,
+ "rockchip,audio-codec",
+ 0);
+ if (!rk_dailink.codecs[0].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ ret = of_parse_phandle_with_fixed_args(np, "rockchip,audio-codec",
+ 0, 0, &args);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Unable to parse property 'rockchip,audio-codec'\n");
+ return ret;
+ }
+
+ ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
+ return ret;
+ }
+
+ rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller",
+ 0);
+ if (!rk_dailink.cpus->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'rockchip,i2s-controller' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
+
+ ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Unable to parse 'rockchip,routing' property\n");
+ return ret;
+ }
+
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Soc register card failed\n");
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_sound_of_match[] = {
+ { .compatible = "rockchip,rk3288-hdmi-analog", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
+
+static struct platform_driver rockchip_sound_driver = {
+ .probe = snd_rk_mc_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = rockchip_sound_of_match,
+ },
+};
+
+module_platform_driver(rockchip_sound_driver);
+
+MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.com>");
+MODULE_DESCRIPTION("Rockchip RK3288 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c
new file mode 100644
index 000000000000..c8137e8883c4
--- /dev/null
+++ b/sound/soc/rockchip/rk3399_gru_sound.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA7219
+ *
+ * Copyright (c) 2016, ROCKCHIP CORPORATION. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "rockchip_i2s.h"
+#include "../codecs/da7219.h"
+#include "../codecs/rt5514.h"
+
+#define DRV_NAME "rk3399-gru-sound"
+
+#define SOUND_FS 256
+
+static unsigned int dmic_wakeup_delay;
+
+static struct snd_soc_jack rockchip_sound_jack;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin rockchip_sound_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_LINE("HDMI", NULL),
+};
+
+static const struct snd_kcontrol_new rockchip_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("HDMI"),
+};
+
+static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int mclk;
+ int ret;
+
+ mclk = params_rate(params) * SOUND_FS;
+
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, 0);
+ if (ret) {
+ dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
+ __func__, mclk, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int mclk;
+ int ret;
+
+ mclk = params_rate(params) * SOUND_FS;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Can't set cpu clock out %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK,
+ mclk, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
+ __func__, params_rate(params) * 512, ret);
+ return ret;
+ }
+
+ /* Wait for DMIC stable */
+ msleep(dmic_wakeup_delay);
+
+ return 0;
+}
+
+static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int mclk, ret;
+
+ /* in bypass mode, the mclk has to be one of the frequencies below */
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set codec clock in %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set pll sysclk mclk %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_jack cdn_dp_card_jack;
+
+static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ /* Enable jack detection. */
+ ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT,
+ &cdn_dp_card_jack);
+ if (ret) {
+ dev_err(card->dev, "Can't create DP Jack %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_component_set_jack(component, &cdn_dp_card_jack, NULL);
+}
+
+static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ /* We need default MCLK and PLL settings for the accessory detection */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Init can't set codec clock in %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Init can't set pll sysclk mclk %d\n", ret);
+ return ret;
+ }
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rockchip_sound_jack,
+ rockchip_sound_jack_pins,
+ ARRAY_SIZE(rockchip_sound_jack_pins));
+
+ if (ret) {
+ dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(
+ rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(
+ rockchip_sound_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(
+ rockchip_sound_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(
+ rockchip_sound_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ snd_soc_component_set_jack(component, &rockchip_sound_jack, NULL);
+
+ return 0;
+}
+
+static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int mclk;
+ int ret;
+
+ mclk = params_rate(params) * SOUND_FS;
+
+ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, 0);
+ if (ret) {
+ dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
+ __func__, mclk, ret);
+ return ret;
+ }
+
+ /* Wait for DMIC stable */
+ msleep(dmic_wakeup_delay);
+
+ return 0;
+}
+
+static int rockchip_sound_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ return snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ 8000, 96000);
+}
+
+static const struct snd_soc_ops rockchip_sound_max98357a_ops = {
+ .startup = rockchip_sound_startup,
+ .hw_params = rockchip_sound_max98357a_hw_params,
+};
+
+static const struct snd_soc_ops rockchip_sound_rt5514_ops = {
+ .startup = rockchip_sound_startup,
+ .hw_params = rockchip_sound_rt5514_hw_params,
+};
+
+static const struct snd_soc_ops rockchip_sound_da7219_ops = {
+ .startup = rockchip_sound_startup,
+ .hw_params = rockchip_sound_da7219_hw_params,
+};
+
+static const struct snd_soc_ops rockchip_sound_dmic_ops = {
+ .startup = rockchip_sound_startup,
+ .hw_params = rockchip_sound_dmic_hw_params,
+};
+
+static struct snd_soc_card rockchip_sound_card = {
+ .name = "rk3399-gru-sound",
+ .owner = THIS_MODULE,
+ .dapm_widgets = rockchip_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),
+ .controls = rockchip_controls,
+ .num_controls = ARRAY_SIZE(rockchip_controls),
+};
+
+enum {
+ DAILINK_CDNDP,
+ DAILINK_DA7219,
+ DAILINK_DMIC,
+ DAILINK_MAX98357A,
+ DAILINK_RT5514,
+ DAILINK_RT5514_DSP,
+};
+
+SND_SOC_DAILINK_DEFS(cdndp,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "spdif-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(da7219,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "da7219-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(dmic,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "dmic-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(max98357a,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(rt5514,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5514-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(rt5514_dsp,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static const struct snd_soc_dai_link rockchip_dais[] = {
+ [DAILINK_CDNDP] = {
+ .name = "DP",
+ .stream_name = "DP PCM",
+ .init = rockchip_sound_cdndp_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(cdndp),
+ },
+ [DAILINK_DA7219] = {
+ .name = "DA7219",
+ .stream_name = "DA7219 PCM",
+ .init = rockchip_sound_da7219_init,
+ .ops = &rockchip_sound_da7219_ops,
+ /* set da7219 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(da7219),
+ },
+ [DAILINK_DMIC] = {
+ .name = "DMIC",
+ .stream_name = "DMIC PCM",
+ .ops = &rockchip_sound_dmic_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(dmic),
+ },
+ [DAILINK_MAX98357A] = {
+ .name = "MAX98357A",
+ .stream_name = "MAX98357A PCM",
+ .ops = &rockchip_sound_max98357a_ops,
+ /* set max98357a as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(max98357a),
+ },
+ [DAILINK_RT5514] = {
+ .name = "RT5514",
+ .stream_name = "RT5514 PCM",
+ .ops = &rockchip_sound_rt5514_ops,
+ /* set rt5514 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(rt5514),
+ },
+ /* RT5514 DSP for voice wakeup via spi bus */
+ [DAILINK_RT5514_DSP] = {
+ .name = "RT5514 DSP",
+ .stream_name = "Wake on Voice",
+ SND_SOC_DAILINK_REG(rt5514_dsp),
+ },
+};
+
+static const struct snd_soc_dapm_route rockchip_sound_cdndp_routes[] = {
+ /* Output */
+ {"HDMI", NULL, "TX"},
+};
+
+static const struct snd_soc_dapm_route rockchip_sound_da7219_routes[] = {
+ /* Output */
+ {"Headphones", NULL, "HPL"},
+ {"Headphones", NULL, "HPR"},
+
+ /* Input */
+ {"MIC", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route rockchip_sound_dmic_routes[] = {
+ /* Input */
+ {"DMic", NULL, "Int Mic"},
+};
+
+static const struct snd_soc_dapm_route rockchip_sound_max98357a_routes[] = {
+ /* Output */
+ {"Speakers", NULL, "Speaker"},
+};
+
+static const struct snd_soc_dapm_route rockchip_sound_rt5514_routes[] = {
+ /* Input */
+ {"DMIC1L", NULL, "Int Mic"},
+ {"DMIC1R", NULL, "Int Mic"},
+};
+
+struct rockchip_sound_route {
+ const struct snd_soc_dapm_route *routes;
+ int num_routes;
+};
+
+static const struct rockchip_sound_route rockchip_routes[] = {
+ [DAILINK_CDNDP] = {
+ .routes = rockchip_sound_cdndp_routes,
+ .num_routes = ARRAY_SIZE(rockchip_sound_cdndp_routes),
+ },
+ [DAILINK_DA7219] = {
+ .routes = rockchip_sound_da7219_routes,
+ .num_routes = ARRAY_SIZE(rockchip_sound_da7219_routes),
+ },
+ [DAILINK_DMIC] = {
+ .routes = rockchip_sound_dmic_routes,
+ .num_routes = ARRAY_SIZE(rockchip_sound_dmic_routes),
+ },
+ [DAILINK_MAX98357A] = {
+ .routes = rockchip_sound_max98357a_routes,
+ .num_routes = ARRAY_SIZE(rockchip_sound_max98357a_routes),
+ },
+ [DAILINK_RT5514] = {
+ .routes = rockchip_sound_rt5514_routes,
+ .num_routes = ARRAY_SIZE(rockchip_sound_rt5514_routes),
+ },
+ [DAILINK_RT5514_DSP] = {},
+};
+
+struct dailink_match_data {
+ const char *compatible;
+ const struct bus_type *bus_type;
+};
+
+static const struct dailink_match_data dailink_match[] = {
+ [DAILINK_CDNDP] = {
+ .compatible = "rockchip,rk3399-cdn-dp",
+ },
+ [DAILINK_DA7219] = {
+ .compatible = "dlg,da7219",
+ },
+ [DAILINK_DMIC] = {
+ .compatible = "dmic-codec",
+ },
+ [DAILINK_MAX98357A] = {
+ .compatible = "maxim,max98357a",
+ },
+ [DAILINK_RT5514] = {
+ .compatible = "realtek,rt5514",
+ .bus_type = &i2c_bus_type,
+ },
+ [DAILINK_RT5514_DSP] = {
+ .compatible = "realtek,rt5514",
+ .bus_type = &spi_bus_type,
+ },
+};
+
+static int rockchip_sound_codec_node_match(struct device_node *np_codec)
+{
+ struct device *dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dailink_match); i++) {
+ if (!of_device_is_compatible(np_codec,
+ dailink_match[i].compatible))
+ continue;
+
+ if (dailink_match[i].bus_type) {
+ dev = bus_find_device_by_of_node(dailink_match[i].bus_type,
+ np_codec);
+ if (!dev)
+ continue;
+ put_device(dev);
+ }
+
+ return i;
+ }
+ return -1;
+}
+
+static int rockchip_sound_of_parse_dais(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct device_node *np_cpu, *np_cpu0, *np_cpu1;
+ struct device_node *np_codec;
+ struct snd_soc_dai_link *dai;
+ struct snd_soc_dapm_route *routes;
+ int i, index;
+ int num_routes;
+
+ card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais),
+ GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
+
+ num_routes = 0;
+ for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++)
+ num_routes += rockchip_routes[i].num_routes;
+ routes = devm_kcalloc(dev, num_routes, sizeof(*routes),
+ GFP_KERNEL);
+ if (!routes)
+ return -ENOMEM;
+ card->dapm_routes = routes;
+
+ np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0);
+ np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1);
+
+ card->num_dapm_routes = 0;
+ card->num_links = 0;
+ for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) {
+ np_codec = of_parse_phandle(dev->of_node,
+ "rockchip,codec", i);
+ if (!np_codec)
+ break;
+
+ if (!of_device_is_available(np_codec))
+ continue;
+
+ index = rockchip_sound_codec_node_match(np_codec);
+ if (index < 0)
+ continue;
+
+ switch (index) {
+ case DAILINK_CDNDP:
+ np_cpu = np_cpu1;
+ break;
+ case DAILINK_RT5514_DSP:
+ np_cpu = np_codec;
+ break;
+ default:
+ np_cpu = np_cpu0;
+ break;
+ }
+
+ if (!np_cpu) {
+ dev_err(dev, "Missing 'rockchip,cpu' for %s\n",
+ rockchip_dais[index].name);
+ return -EINVAL;
+ }
+
+ dai = &card->dai_link[card->num_links++];
+ *dai = rockchip_dais[index];
+
+ if (!dai->codecs->name)
+ dai->codecs->of_node = np_codec;
+ dai->platforms->of_node = np_cpu;
+ dai->cpus->of_node = np_cpu;
+
+ if (card->num_dapm_routes + rockchip_routes[index].num_routes >
+ num_routes) {
+ dev_err(dev, "Too many routes\n");
+ return -EINVAL;
+ }
+
+ memcpy(routes + card->num_dapm_routes,
+ rockchip_routes[index].routes,
+ rockchip_routes[index].num_routes * sizeof(*routes));
+ card->num_dapm_routes += rockchip_routes[index].num_routes;
+ }
+
+ return 0;
+}
+
+static int rockchip_sound_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &rockchip_sound_card;
+ int ret;
+
+ ret = rockchip_sound_of_parse_dais(&pdev->dev, card);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to parse dais: %d\n", ret);
+ return ret;
+ }
+
+ /* Set DMIC wakeup delay */
+ ret = device_property_read_u32(&pdev->dev, "dmic-wakeup-delay-ms",
+ &dmic_wakeup_delay);
+ if (ret) {
+ dmic_wakeup_delay = 0;
+ dev_dbg(&pdev->dev,
+ "no optional property 'dmic-wakeup-delay-ms' found, default: no delay\n");
+ }
+
+ card->dev = &pdev->dev;
+ return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static const struct of_device_id rockchip_sound_of_match[] = {
+ { .compatible = "rockchip,rk3399-gru-sound", },
+ {},
+};
+
+static struct platform_driver rockchip_sound_driver = {
+ .probe = rockchip_sound_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = rockchip_sound_of_match,
+#ifdef CONFIG_PM
+ .pm = &snd_soc_pm_ops,
+#endif
+ },
+};
+
+module_platform_driver(rockchip_sound_driver);
+
+MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 6561c4cc2edd..0a0a95b4f520 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -1,21 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* sound/soc/rockchip/rockchip_i2s.c
*
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun <jay.xu@rock-chips.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/mfd/syscon.h>
#include <linux/delay.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/spinlock.h>
#include <sound/pcm_params.h>
#include <sound/dmaengine_pcm.h>
@@ -23,6 +23,11 @@
#define DRV_NAME "rockchip-i2s"
+struct rk_i2s_pins {
+ u32 reg_offset;
+ u32 shift;
+};
+
struct rk_i2s_dev {
struct device *dev;
@@ -33,14 +38,59 @@ struct rk_i2s_dev {
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct regmap *regmap;
-
+ struct regmap *grf;
+
+ bool has_capture;
+ bool has_playback;
+
+/*
+ * Used to indicate the tx/rx status.
+ * I2S controller hopes to start the tx and rx together,
+ * also to stop them when they are both try to stop.
+*/
+ bool tx_start;
+ bool rx_start;
bool is_master_mode;
+ const struct rk_i2s_pins *pins;
+ unsigned int bclk_ratio;
+ spinlock_t lock; /* tx/rx lock */
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *bclk_on;
+ struct pinctrl_state *bclk_off;
};
+static int i2s_pinctrl_select_bclk_on(struct rk_i2s_dev *i2s)
+{
+ int ret = 0;
+
+ if (!IS_ERR(i2s->pinctrl) && !IS_ERR_OR_NULL(i2s->bclk_on))
+ ret = pinctrl_select_state(i2s->pinctrl, i2s->bclk_on);
+
+ if (ret)
+ dev_err(i2s->dev, "bclk enable failed %d\n", ret);
+
+ return ret;
+}
+
+static int i2s_pinctrl_select_bclk_off(struct rk_i2s_dev *i2s)
+{
+
+ int ret = 0;
+
+ if (!IS_ERR(i2s->pinctrl) && !IS_ERR_OR_NULL(i2s->bclk_off))
+ ret = pinctrl_select_state(i2s->pinctrl, i2s->bclk_off);
+
+ if (ret)
+ dev_err(i2s->dev, "bclk disable failed %d\n", ret);
+
+ return ret;
+}
+
static int i2s_runtime_suspend(struct device *dev)
{
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+ regcache_cache_only(i2s->regmap, true);
clk_disable_unprepare(i2s->mclk);
return 0;
@@ -57,7 +107,14 @@ static int i2s_runtime_resume(struct device *dev)
return ret;
}
- return 0;
+ regcache_cache_only(i2s->regmap, false);
+ regcache_mark_dirty(i2s->regmap);
+
+ ret = regcache_sync(i2s->regmap);
+ if (ret)
+ clk_disable_unprepare(i2s->mclk);
+
+ return ret;
}
static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
@@ -65,80 +122,119 @@ static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
return snd_soc_dai_get_drvdata(dai);
}
-static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
+static int rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
{
unsigned int val = 0;
- int retry = 10;
+ int ret = 0;
+ spin_lock(&i2s->lock);
if (on) {
- regmap_update_bits(i2s->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
-
- regmap_update_bits(i2s->regmap, I2S_XFER,
- I2S_XFER_TXS_START,
- I2S_XFER_TXS_START);
+ ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
+ I2S_DMACR_TDE_ENABLE,
+ I2S_DMACR_TDE_ENABLE);
+ if (ret < 0)
+ goto end;
+ ret = regmap_update_bits(i2s->regmap, I2S_XFER,
+ I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+ I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+ if (ret < 0)
+ goto end;
+ i2s->tx_start = true;
} else {
- regmap_update_bits(i2s->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
-
- regmap_update_bits(i2s->regmap, I2S_XFER,
- I2S_XFER_TXS_START,
- I2S_XFER_TXS_STOP);
-
- regmap_update_bits(i2s->regmap, I2S_CLR,
- I2S_CLR_TXC,
- I2S_CLR_TXC);
-
- regmap_read(i2s->regmap, I2S_CLR, &val);
-
- /* Should wait for clear operation to finish */
- while (val & I2S_CLR_TXC) {
- regmap_read(i2s->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_warn(i2s->dev, "fail to clear\n");
- break;
- }
+ i2s->tx_start = false;
+
+ ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
+ I2S_DMACR_TDE_ENABLE,
+ I2S_DMACR_TDE_DISABLE);
+ if (ret < 0)
+ goto end;
+
+ if (!i2s->rx_start) {
+ ret = regmap_update_bits(i2s->regmap, I2S_XFER,
+ I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+ I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP);
+ if (ret < 0)
+ goto end;
+ udelay(150);
+ ret = regmap_update_bits(i2s->regmap, I2S_CLR,
+ I2S_CLR_TXC | I2S_CLR_RXC,
+ I2S_CLR_TXC | I2S_CLR_RXC);
+ if (ret < 0)
+ goto end;
+ ret = regmap_read_poll_timeout_atomic(i2s->regmap,
+ I2S_CLR,
+ val,
+ val == 0,
+ 20,
+ 200);
+ if (ret < 0)
+ dev_warn(i2s->dev, "fail to clear: %d\n", ret);
}
}
+end:
+ spin_unlock(&i2s->lock);
+ if (ret < 0)
+ dev_err(i2s->dev, "lrclk update failed\n");
+
+ return ret;
}
-static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
+static int rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
{
unsigned int val = 0;
- int retry = 10;
+ int ret = 0;
+ spin_lock(&i2s->lock);
if (on) {
- regmap_update_bits(i2s->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
-
- regmap_update_bits(i2s->regmap, I2S_XFER,
- I2S_XFER_RXS_START,
- I2S_XFER_RXS_START);
+ ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
+ I2S_DMACR_RDE_ENABLE,
+ I2S_DMACR_RDE_ENABLE);
+ if (ret < 0)
+ goto end;
+
+ ret = regmap_update_bits(i2s->regmap, I2S_XFER,
+ I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+ I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+ if (ret < 0)
+ goto end;
+ i2s->rx_start = true;
} else {
- regmap_update_bits(i2s->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
-
- regmap_update_bits(i2s->regmap, I2S_XFER,
- I2S_XFER_RXS_START,
- I2S_XFER_RXS_STOP);
-
- regmap_update_bits(i2s->regmap, I2S_CLR,
- I2S_CLR_RXC,
- I2S_CLR_RXC);
-
- regmap_read(i2s->regmap, I2S_CLR, &val);
-
- /* Should wait for clear operation to finish */
- while (val & I2S_CLR_RXC) {
- regmap_read(i2s->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_warn(i2s->dev, "fail to clear\n");
- break;
- }
+ i2s->rx_start = false;
+
+ ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
+ I2S_DMACR_RDE_ENABLE,
+ I2S_DMACR_RDE_DISABLE);
+ if (ret < 0)
+ goto end;
+
+ if (!i2s->tx_start) {
+ ret = regmap_update_bits(i2s->regmap, I2S_XFER,
+ I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+ I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP);
+ if (ret < 0)
+ goto end;
+ udelay(150);
+ ret = regmap_update_bits(i2s->regmap, I2S_CLR,
+ I2S_CLR_TXC | I2S_CLR_RXC,
+ I2S_CLR_TXC | I2S_CLR_RXC);
+ if (ret < 0)
+ goto end;
+ ret = regmap_read_poll_timeout_atomic(i2s->regmap,
+ I2S_CLR,
+ val,
+ val == 0,
+ 20,
+ 200);
+ if (ret < 0)
+ dev_warn(i2s->dev, "fail to clear: %d\n", ret);
}
}
+end:
+ spin_unlock(&i2s->lock);
+ if (ret < 0)
+ dev_err(i2s->dev, "lrclk update failed\n");
+
+ return ret;
}
static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
@@ -146,25 +242,57 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
{
struct rk_i2s_dev *i2s = to_info(cpu_dai);
unsigned int mask = 0, val = 0;
+ int ret = 0;
+ pm_runtime_get_sync(cpu_dai->dev);
mask = I2S_CKR_MSS_MASK;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
/* Set source clock in Master mode */
val = I2S_CKR_MSS_MASTER;
i2s->is_master_mode = true;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_BC_FC:
val = I2S_CKR_MSS_SLAVE;
i2s->is_master_mode = false;
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
- mask = I2S_TXCR_IBM_MASK;
+ mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = I2S_CKR_CKP_NORMAL |
+ I2S_CKR_TLP_NORMAL |
+ I2S_CKR_RLP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = I2S_CKR_CKP_NORMAL |
+ I2S_CKR_TLP_INVERTED |
+ I2S_CKR_RLP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = I2S_CKR_CKP_INVERTED |
+ I2S_CKR_TLP_NORMAL |
+ I2S_CKR_RLP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = I2S_CKR_CKP_INVERTED |
+ I2S_CKR_TLP_INVERTED |
+ I2S_CKR_RLP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
+
+ mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S_TXCR_IBM_RSJM;
@@ -175,13 +303,20 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_I2S:
val = I2S_TXCR_IBM_NORMAL;
break;
+ case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 bit mode */
+ val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */
+ val = I2S_TXCR_TFS_PCM;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
- mask = I2S_RXCR_IBM_MASK;
+ mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S_RXCR_IBM_RSJM;
@@ -192,13 +327,23 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_I2S:
val = I2S_RXCR_IBM_NORMAL;
break;
+ case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 bit mode */
+ val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */
+ val = I2S_RXCR_TFS_PCM;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_RXCR, mask, val);
- return 0;
+err_pm_put:
+ pm_runtime_put(cpu_dai->dev);
+
+ return ret;
}
static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
@@ -206,17 +351,17 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = to_info(dai);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
unsigned int val = 0;
unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck;
if (i2s->is_master_mode) {
mclk_rate = clk_get_rate(i2s->mclk);
- bclk_rate = 2 * 32 * params_rate(params);
- if (bclk_rate && mclk_rate % bclk_rate)
+ bclk_rate = i2s->bclk_ratio * params_rate(params);
+ if (!bclk_rate)
return -EINVAL;
- div_bclk = mclk_rate / bclk_rate;
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
div_lrck = bclk_rate / params_rate(params);
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_MDIV_MASK,
@@ -277,14 +422,38 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
val);
+ if (!IS_ERR(i2s->grf) && i2s->pins) {
+ regmap_read(i2s->regmap, I2S_TXCR, &val);
+ val &= I2S_TXCR_CSR_MASK;
+
+ switch (val) {
+ case I2S_CHN_4:
+ val = I2S_IO_4CH_OUT_6CH_IN;
+ break;
+ case I2S_CHN_6:
+ val = I2S_IO_6CH_OUT_4CH_IN;
+ break;
+ case I2S_CHN_8:
+ val = I2S_IO_8CH_OUT_2CH_IN;
+ break;
+ default:
+ val = I2S_IO_2CH_OUT_8CH_IN;
+ break;
+ }
+
+ val <<= i2s->pins->shift;
+ val |= (I2S_IO_DIRECTION_MASK << i2s->pins->shift) << 16;
+ regmap_write(i2s->grf, i2s->pins->reg_offset, val);
+ }
+
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
I2S_DMACR_RDL(16));
val = I2S_CKR_TRCM_TXRX;
- if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
- val = I2S_CKR_TRCM_TXSHARE;
+ if (dai->driver->symmetric_rate && rtd->dai_link->symmetric_rate)
+ val = I2S_CKR_TRCM_TXONLY;
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_TRCM_MASK,
@@ -303,17 +472,25 @@ static int rockchip_i2s_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- rockchip_snd_rxctrl(i2s, 1);
+ ret = rockchip_snd_rxctrl(i2s, 1);
else
- rockchip_snd_txctrl(i2s, 1);
+ ret = rockchip_snd_txctrl(i2s, 1);
+ if (ret < 0)
+ return ret;
+ i2s_pinctrl_select_bclk_on(i2s);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- rockchip_snd_rxctrl(i2s, 0);
- else
- rockchip_snd_txctrl(i2s, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!i2s->tx_start)
+ i2s_pinctrl_select_bclk_off(i2s);
+ ret = rockchip_snd_rxctrl(i2s, 0);
+ } else {
+ if (!i2s->rx_start)
+ i2s_pinctrl_select_bclk_off(i2s);
+ ret = rockchip_snd_txctrl(i2s, 0);
+ }
break;
default:
ret = -EINVAL;
@@ -323,12 +500,25 @@ static int rockchip_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int rockchip_i2s_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct rk_i2s_dev *i2s = to_info(dai);
+
+ i2s->bclk_ratio = ratio;
+
+ return 0;
+}
+
static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
unsigned int freq, int dir)
{
struct rk_i2s_dev *i2s = to_info(cpu_dai);
int ret;
+ if (freq == 0)
+ return 0;
+
ret = clk_set_rate(i2s->mclk, freq);
if (ret)
dev_err(i2s->dev, "Fail to set mclk %d\n", ret);
@@ -340,49 +530,30 @@ static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
- dai->capture_dma_data = &i2s->capture_dma_data;
- dai->playback_dma_data = &i2s->playback_dma_data;
+ snd_soc_dai_init_dma_data(dai,
+ i2s->has_playback ? &i2s->playback_dma_data : NULL,
+ i2s->has_capture ? &i2s->capture_dma_data : NULL);
return 0;
}
static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
+ .probe = rockchip_i2s_dai_probe,
.hw_params = rockchip_i2s_hw_params,
+ .set_bclk_ratio = rockchip_i2s_set_bclk_ratio,
.set_sysclk = rockchip_i2s_set_sysclk,
.set_fmt = rockchip_i2s_set_fmt,
.trigger = rockchip_i2s_trigger,
};
static struct snd_soc_dai_driver rockchip_i2s_dai = {
- .probe = rockchip_i2s_dai_probe,
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .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),
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .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),
- },
.ops = &rockchip_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static const struct snd_soc_component_driver rockchip_i2s_component = {
.name = DRV_NAME,
+ .legacy_dai_naming = 1,
};
static bool rockchip_i2s_wr_reg(struct device *dev, unsigned int reg)
@@ -412,6 +583,7 @@ static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg)
case I2S_INTCR:
case I2S_XFER:
case I2S_CLR:
+ case I2S_TXDR:
case I2S_RXDR:
case I2S_FIFOLR:
case I2S_INTSR:
@@ -426,6 +598,9 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case I2S_INTSR:
case I2S_CLR:
+ case I2S_FIFOLR:
+ case I2S_TXDR:
+ case I2S_RXDR:
return true;
default:
return false;
@@ -435,16 +610,28 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case I2S_RXDR:
+ return true;
default:
return false;
}
}
+static const struct reg_default rockchip_i2s_reg_defaults[] = {
+ {0x00, 0x0000000f},
+ {0x04, 0x0000000f},
+ {0x08, 0x00071f1f},
+ {0x10, 0x001f0000},
+ {0x14, 0x01f00000},
+};
+
static const struct regmap_config rockchip_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = I2S_RXDR,
+ .reg_defaults = rockchip_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults),
.writeable_reg = rockchip_i2s_wr_reg,
.readable_reg = rockchip_i2s_rd_reg,
.volatile_reg = rockchip_i2s_volatile_reg,
@@ -452,20 +639,121 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct rk_i2s_pins rk3399_i2s_pins = {
+ .reg_offset = 0xe220,
+ .shift = 11,
+};
+
+static const struct of_device_id rockchip_i2s_match[] __maybe_unused = {
+ { .compatible = "rockchip,px30-i2s", },
+ { .compatible = "rockchip,rk1808-i2s", },
+ { .compatible = "rockchip,rk3036-i2s", },
+ { .compatible = "rockchip,rk3066-i2s", },
+ { .compatible = "rockchip,rk3128-i2s", },
+ { .compatible = "rockchip,rk3188-i2s", },
+ { .compatible = "rockchip,rk3228-i2s", },
+ { .compatible = "rockchip,rk3288-i2s", },
+ { .compatible = "rockchip,rk3308-i2s", },
+ { .compatible = "rockchip,rk3328-i2s", },
+ { .compatible = "rockchip,rk3366-i2s", },
+ { .compatible = "rockchip,rk3368-i2s", },
+ { .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
+ { .compatible = "rockchip,rk3588-i2s", },
+ { .compatible = "rockchip,rv1126-i2s", },
+ {},
+};
+
+static int rockchip_i2s_init_dai(struct rk_i2s_dev *i2s, struct resource *res,
+ struct snd_soc_dai_driver **dp)
+{
+ struct device_node *node = i2s->dev->of_node;
+ struct snd_soc_dai_driver *dai;
+ struct property *dma_names;
+ const char *dma_name;
+ unsigned int val;
+
+ of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
+ if (!strcmp(dma_name, "tx"))
+ i2s->has_playback = true;
+ if (!strcmp(dma_name, "rx"))
+ i2s->has_capture = true;
+ }
+
+ dai = devm_kmemdup(i2s->dev, &rockchip_i2s_dai,
+ sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ if (i2s->has_playback) {
+ dai->playback.stream_name = "Playback";
+ dai->playback.channels_min = 2;
+ dai->playback.channels_max = 8;
+ dai->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ dai->playback.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;
+
+ i2s->playback_dma_data.addr = res->start + I2S_TXDR;
+ i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->playback_dma_data.maxburst = 8;
+
+ if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
+ if (val >= 2 && val <= 8)
+ dai->playback.channels_max = val;
+ }
+ }
+
+ if (i2s->has_capture) {
+ dai->capture.stream_name = "Capture";
+ dai->capture.channels_min = 2;
+ dai->capture.channels_max = 8;
+ dai->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ dai->capture.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;
+
+ i2s->capture_dma_data.addr = res->start + I2S_RXDR;
+ i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->capture_dma_data.maxburst = 8;
+
+ if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
+ if (val >= 2 && val <= 8)
+ dai->capture.channels_max = val;
+ }
+ }
+
+ if (dp)
+ *dp = dai;
+
+ return 0;
+}
+
static int rockchip_i2s_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct rk_i2s_dev *i2s;
- struct snd_soc_dai_driver *soc_dai;
+ struct snd_soc_dai_driver *dai;
struct resource *res;
void __iomem *regs;
int ret;
- int val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
- if (!i2s) {
- dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n");
+ if (!i2s)
return -ENOMEM;
+
+ spin_lock_init(&i2s->lock);
+ i2s->dev = &pdev->dev;
+
+ i2s->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+ if (!IS_ERR(i2s->grf)) {
+ i2s->pins = device_get_match_data(&pdev->dev);
+ if (!i2s->pins)
+ return -EINVAL;
+
}
/* try to prepare related clocks */
@@ -483,31 +771,43 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk");
if (IS_ERR(i2s->mclk)) {
dev_err(&pdev->dev, "Can't retrieve i2s master clock\n");
- return PTR_ERR(i2s->mclk);
+ ret = PTR_ERR(i2s->mclk);
+ goto err_clk;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto err_clk;
+ }
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&rockchip_i2s_regmap_config);
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev,
"Failed to initialise managed register map\n");
- return PTR_ERR(i2s->regmap);
+ ret = PTR_ERR(i2s->regmap);
+ goto err_clk;
}
- i2s->playback_dma_data.addr = res->start + I2S_TXDR;
- i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- i2s->playback_dma_data.maxburst = 4;
+ i2s->bclk_ratio = 64;
+ i2s->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!IS_ERR(i2s->pinctrl)) {
+ i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on");
+ if (!IS_ERR_OR_NULL(i2s->bclk_on)) {
+ i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off");
+ if (IS_ERR_OR_NULL(i2s->bclk_off)) {
+ dev_err(&pdev->dev, "failed to find i2s bclk_off\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+ }
+ } else {
+ dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n");
+ }
- i2s->capture_dma_data.addr = res->start + I2S_RXDR;
- i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- i2s->capture_dma_data.maxburst = 4;
+ i2s_pinctrl_select_bclk_off(i2s);
- i2s->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, i2s);
pm_runtime_enable(&pdev->dev);
@@ -517,25 +817,13 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- soc_dai = devm_kzalloc(&pdev->dev,
- sizeof(*soc_dai), GFP_KERNEL);
- if (!soc_dai)
- return -ENOMEM;
-
- memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai));
- if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
- if (val >= 2 && val <= 8)
- soc_dai->playback.channels_max = val;
- }
-
- if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
- if (val >= 2 && val <= 8)
- soc_dai->capture.channels_max = val;
- }
+ ret = rockchip_i2s_init_dai(i2s, res, &dai);
+ if (ret)
+ goto err_pm_disable;
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component,
- soc_dai, 1);
+ dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI\n");
@@ -545,7 +833,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
- return ret;
+ goto err_suspend;
}
return 0;
@@ -555,11 +843,12 @@ err_suspend:
i2s_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
-
+err_clk:
+ clk_disable_unprepare(i2s->hclk);
return ret;
}
-static int rockchip_i2s_remove(struct platform_device *pdev)
+static void rockchip_i2s_remove(struct platform_device *pdev)
{
struct rk_i2s_dev *i2s = dev_get_drvdata(&pdev->dev);
@@ -567,20 +856,11 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
if (!pm_runtime_status_suspended(&pdev->dev))
i2s_runtime_suspend(&pdev->dev);
- clk_disable_unprepare(i2s->mclk);
clk_disable_unprepare(i2s->hclk);
-
- return 0;
}
-static const struct of_device_id rockchip_i2s_match[] = {
- { .compatible = "rockchip,rk3066-i2s", },
- {},
-};
-
static const struct dev_pm_ops rockchip_i2s_pm_ops = {
- SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL)
};
static struct platform_driver rockchip_i2s_driver = {
@@ -589,7 +869,7 @@ static struct platform_driver rockchip_i2s_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(rockchip_i2s_match),
- .pm = &rockchip_i2s_pm_ops,
+ .pm = pm_ptr(&rockchip_i2s_pm_ops),
},
};
module_platform_driver(rockchip_i2s_driver);
diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h
index dc6e2c74d088..251851bf4f2c 100644
--- a/sound/soc/rockchip/rockchip_i2s.h
+++ b/sound/soc/rockchip/rockchip_i2s.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* sound/soc/rockchip/rockchip_i2s.h
*
@@ -5,10 +6,6 @@
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun xu <jay.xu@rock-chips.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 _ROCKCHIP_IIS_H
@@ -41,6 +38,7 @@
#define I2S_TXCR_TFS_SHIFT 5
#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_VDW_SHIFT 0
#define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT)
#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
@@ -70,6 +68,7 @@
#define I2S_RXCR_TFS_SHIFT 5
#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_VDW_SHIFT 0
#define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT)
#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
@@ -81,22 +80,25 @@
#define I2S_CKR_TRCM_SHIFT 28
#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
-#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT)
-#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_CKP_SHIFT 26
-#define I2S_CKR_CKP_NEG (0 << I2S_CKR_CKP_SHIFT)
-#define I2S_CKR_CKP_POS (1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_RLP_SHIFT 25
#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
-#define I2S_CKR_RLP_OPPSITE (1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_TLP_SHIFT 24
#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT)
-#define I2S_CKR_TLP_OPPSITE (1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_MDIV_SHIFT 16
#define I2S_CKR_MDIV(x) ((x - 1) << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT)
@@ -236,4 +238,11 @@ enum {
#define I2S_TXDR (0x0024)
#define I2S_RXDR (0x0028)
+/* io direction cfg register */
+#define I2S_IO_DIRECTION_MASK (7)
+#define I2S_IO_8CH_OUT_2CH_IN (0)
+#define I2S_IO_6CH_OUT_4CH_IN (4)
+#define I2S_IO_4CH_OUT_6CH_IN (6)
+#define I2S_IO_2CH_OUT_8CH_IN (7)
+
#endif /* _ROCKCHIP_IIS_H */
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c
new file mode 100644
index 000000000000..770b9bfbb384
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -0,0 +1,1435 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver
+
+// Copyright (c) 2018 Rockchip Electronics Co. Ltd.
+// Author: Sugar Zhang <sugar.zhang@rock-chips.com>
+// Author: Nicolas Frattaroli <frattaroli.nicolas@gmail.com>
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "rockchip_i2s_tdm.h"
+
+#define DRV_NAME "rockchip-i2s-tdm"
+
+#define CH_GRP_MAX 4 /* The max channel 8 / 2 */
+#define MULTIPLEX_CH_MAX 10
+
+#define TRCM_TXRX 0
+#define TRCM_TX 1
+#define TRCM_RX 2
+
+struct txrx_config {
+ u32 addr;
+ u32 reg;
+ u32 txonly;
+ u32 rxonly;
+};
+
+struct rk_i2s_soc_data {
+ u32 softrst_offset;
+ u32 grf_reg_offset;
+ u32 grf_shift;
+ int config_count;
+ const struct txrx_config *configs;
+ int (*init)(struct device *dev, u32 addr);
+};
+
+struct rk_i2s_tdm_dev {
+ struct device *dev;
+ struct clk *hclk;
+ struct clk *mclk_tx;
+ struct clk *mclk_rx;
+ struct regmap *regmap;
+ struct regmap *grf;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct reset_control *tx_reset;
+ struct reset_control *rx_reset;
+ const struct rk_i2s_soc_data *soc_data;
+ bool is_master_mode;
+ bool io_multiplex;
+ bool tdm_mode;
+ unsigned int frame_width;
+ unsigned int clk_trcm;
+ unsigned int i2s_sdis[CH_GRP_MAX];
+ unsigned int i2s_sdos[CH_GRP_MAX];
+ int refcount;
+ spinlock_t lock; /* xfer lock */
+ bool has_playback;
+ bool has_capture;
+ struct snd_soc_dai_driver *dai;
+ unsigned int mclk_rx_freq;
+ unsigned int mclk_tx_freq;
+};
+
+static int to_ch_num(unsigned int val)
+{
+ switch (val) {
+ case I2S_CHN_4:
+ return 4;
+ case I2S_CHN_6:
+ return 6;
+ case I2S_CHN_8:
+ return 8;
+ default:
+ return 2;
+ }
+}
+
+static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+ clk_disable_unprepare(i2s_tdm->mclk_rx);
+}
+
+/**
+ * i2s_tdm_prepare_enable_mclk - prepare to enable all mclks, disable them on
+ * failure.
+ * @i2s_tdm: rk_i2s_tdm_dev struct
+ *
+ * This function attempts to enable all mclk clocks, but cleans up after
+ * itself on failure. Guarantees to balance its calls.
+ *
+ * Returns success (0) or negative errno.
+ */
+static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(i2s_tdm->mclk_tx);
+ if (ret)
+ goto err_mclk_tx;
+ ret = clk_prepare_enable(i2s_tdm->mclk_rx);
+ if (ret)
+ goto err_mclk_rx;
+
+ return 0;
+
+err_mclk_rx:
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+err_mclk_tx:
+ return ret;
+}
+
+static int i2s_tdm_runtime_suspend(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s_tdm->regmap, true);
+ i2s_tdm_disable_unprepare_mclk(i2s_tdm);
+
+ clk_disable_unprepare(i2s_tdm->hclk);
+
+ return 0;
+}
+
+static int i2s_tdm_runtime_resume(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2s_tdm->hclk);
+ if (ret)
+ goto err_hclk;
+
+ ret = i2s_tdm_prepare_enable_mclk(i2s_tdm);
+ if (ret)
+ goto err_mclk;
+
+ regcache_cache_only(i2s_tdm->regmap, false);
+ regcache_mark_dirty(i2s_tdm->regmap);
+
+ ret = regcache_sync(i2s_tdm->regmap);
+ if (ret)
+ goto err_regcache;
+
+ return 0;
+
+err_regcache:
+ i2s_tdm_disable_unprepare_mclk(i2s_tdm);
+err_mclk:
+ clk_disable_unprepare(i2s_tdm->hclk);
+err_hclk:
+ return ret;
+}
+
+static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai)
+{
+ return snd_soc_dai_get_drvdata(dai);
+}
+
+/*
+ * Makes sure that both tx and rx are reset at the same time to sync lrck
+ * when clk_trcm > 0.
+ */
+static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ /* This is technically race-y.
+ *
+ * In an ideal world, we could atomically assert both resets at the
+ * same time, through an atomic bulk reset API. This API however does
+ * not exist, so what the downstream vendor code used to do was
+ * implement half a reset controller here and require the CRU to be
+ * passed to the driver as a device tree node. Violating abstractions
+ * like that is bad, especially when it influences something like the
+ * bindings which are supposed to describe the hardware, not whatever
+ * workarounds the driver needs, so it was dropped.
+ *
+ * In practice, asserting the resets one by one appears to work just
+ * fine for playback. During duplex (playback + capture) operation,
+ * this might become an issue, but that should be solved by the
+ * implementation of the aforementioned API, not by shoving a reset
+ * controller into an audio driver.
+ */
+
+ reset_control_assert(i2s_tdm->tx_reset);
+ reset_control_assert(i2s_tdm->rx_reset);
+ udelay(10);
+ reset_control_deassert(i2s_tdm->tx_reset);
+ reset_control_deassert(i2s_tdm->rx_reset);
+ udelay(10);
+}
+
+static void rockchip_snd_reset(struct reset_control *rc)
+{
+ reset_control_assert(rc);
+ udelay(10);
+ reset_control_deassert(rc);
+ udelay(10);
+}
+
+static void rockchip_snd_xfer_clear(struct rk_i2s_tdm_dev *i2s_tdm,
+ unsigned int clr)
+{
+ unsigned int xfer_mask = 0;
+ unsigned int xfer_val = 0;
+ unsigned int val;
+ int retry = 10;
+ bool tx = clr & I2S_CLR_TXC;
+ bool rx = clr & I2S_CLR_RXC;
+
+ if (!(rx || tx))
+ return;
+
+ if (tx) {
+ xfer_mask = I2S_XFER_TXS_START;
+ xfer_val = I2S_XFER_TXS_STOP;
+ }
+ if (rx) {
+ xfer_mask |= I2S_XFER_RXS_START;
+ xfer_val |= I2S_XFER_RXS_STOP;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, xfer_mask, xfer_val);
+ udelay(150);
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr);
+
+ regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
+ /* Wait on the clear operation to finish */
+ while (val) {
+ udelay(15);
+ regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
+ retry--;
+ if (!retry) {
+ dev_warn(i2s_tdm->dev, "clear failed, reset %s%s\n",
+ tx ? "tx" : "", rx ? "rx" : "");
+ if (rx && tx)
+ rockchip_snd_xfer_sync_reset(i2s_tdm);
+ else if (tx)
+ rockchip_snd_reset(i2s_tdm->tx_reset);
+ else if (rx)
+ rockchip_snd_reset(i2s_tdm->rx_reset);
+ break;
+ }
+ }
+}
+
+static inline void rockchip_enable_tde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE,
+ I2S_DMACR_TDE_ENABLE);
+}
+
+static inline void rockchip_disable_tde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE,
+ I2S_DMACR_TDE_DISABLE);
+}
+
+static inline void rockchip_enable_rde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE,
+ I2S_DMACR_RDE_ENABLE);
+}
+
+static inline void rockchip_disable_rde(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE,
+ I2S_DMACR_RDE_DISABLE);
+}
+
+/* only used when clk_trcm > 0 */
+static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai, int on)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2s_tdm->lock, flags);
+ if (on) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_enable_tde(i2s_tdm->regmap);
+ else
+ rockchip_enable_rde(i2s_tdm->regmap);
+
+ if (++i2s_tdm->refcount == 1) {
+ rockchip_snd_xfer_sync_reset(i2s_tdm);
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START);
+ }
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_disable_tde(i2s_tdm->regmap);
+ else
+ rockchip_disable_rde(i2s_tdm->regmap);
+
+ if (--i2s_tdm->refcount == 0) {
+ rockchip_snd_xfer_clear(i2s_tdm,
+ I2S_CLR_TXC | I2S_CLR_RXC);
+ }
+ }
+ spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+}
+
+static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
+{
+ if (on) {
+ rockchip_enable_tde(i2s_tdm->regmap);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_START,
+ I2S_XFER_TXS_START);
+ } else {
+ rockchip_disable_tde(i2s_tdm->regmap);
+
+ rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC);
+ }
+}
+
+static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
+{
+ if (on) {
+ rockchip_enable_rde(i2s_tdm->regmap);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_RXS_START,
+ I2S_XFER_RXS_START);
+ } else {
+ rockchip_disable_rde(i2s_tdm->regmap);
+
+ rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_RXC);
+ }
+}
+
+static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
+ unsigned int mask, val, tdm_val, txcr_val, rxcr_val;
+ int ret;
+ bool is_tdm = i2s_tdm->tdm_mode;
+
+ ret = pm_runtime_resume_and_get(cpu_dai->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ mask = I2S_CKR_MSS_MASK;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ val = I2S_CKR_MSS_MASTER;
+ i2s_tdm->is_master_mode = true;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ val = I2S_CKR_MSS_SLAVE;
+ i2s_tdm->is_master_mode = false;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val);
+
+ mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = I2S_CKR_CKP_NORMAL |
+ I2S_CKR_TLP_NORMAL |
+ I2S_CKR_RLP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = I2S_CKR_CKP_NORMAL |
+ I2S_CKR_TLP_INVERTED |
+ I2S_CKR_RLP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = I2S_CKR_CKP_INVERTED |
+ I2S_CKR_TLP_NORMAL |
+ I2S_CKR_RLP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = I2S_CKR_CKP_INVERTED |
+ I2S_CKR_TLP_INVERTED |
+ I2S_CKR_RLP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ txcr_val = I2S_TXCR_IBM_RSJM;
+ rxcr_val = I2S_RXCR_IBM_RSJM;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ txcr_val = I2S_TXCR_IBM_LSJM;
+ rxcr_val = I2S_RXCR_IBM_LSJM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ txcr_val = I2S_TXCR_IBM_NORMAL;
+ rxcr_val = I2S_RXCR_IBM_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 mode */
+ txcr_val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
+ rxcr_val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */
+ txcr_val = I2S_TXCR_TFS_PCM;
+ rxcr_val = I2S_RXCR_TFS_PCM;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, txcr_val);
+
+ mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, rxcr_val);
+
+ if (is_tdm) {
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = I2S_TXCR_TFS_TDM_I2S;
+ tdm_val = TDM_SHIFT_CTRL(2);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = I2S_TXCR_TFS_TDM_I2S;
+ tdm_val = TDM_SHIFT_CTRL(1);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = I2S_TXCR_TFS_TDM_I2S;
+ tdm_val = TDM_SHIFT_CTRL(0);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val = I2S_TXCR_TFS_TDM_PCM;
+ tdm_val = TDM_SHIFT_CTRL(2);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ val = I2S_TXCR_TFS_TDM_PCM;
+ tdm_val = TDM_SHIFT_CTRL(4);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ tdm_val |= TDM_FSYNC_WIDTH_SEL1(1);
+ tdm_val |= TDM_FSYNC_WIDTH_HALF_FRAME;
+
+ mask = I2S_TXCR_TFS_MASK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val);
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val);
+
+ mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK |
+ TDM_SHIFT_CTRL_MSK;
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR,
+ mask, tdm_val);
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR,
+ mask, tdm_val);
+ }
+
+err_pm_put:
+ pm_runtime_put(cpu_dai->dev);
+
+ return ret;
+}
+
+static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream,
+ struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int stream;
+
+ stream = SNDRV_PCM_STREAM_LAST - substream->stream;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_disable_tde(i2s_tdm->regmap);
+ else
+ rockchip_disable_rde(i2s_tdm->regmap);
+
+ rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC | I2S_CLR_RXC);
+}
+
+static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream,
+ struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int stream;
+
+ stream = SNDRV_PCM_STREAM_LAST - substream->stream;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rockchip_enable_tde(i2s_tdm->regmap);
+ else
+ rockchip_enable_rde(i2s_tdm->regmap);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START);
+}
+
+static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ int usable_chs = MULTIPLEX_CH_MAX;
+ unsigned int val = 0;
+
+ if (!i2s_tdm->io_multiplex)
+ return 0;
+
+ if (IS_ERR_OR_NULL(i2s_tdm->grf)) {
+ dev_err(i2s_tdm->dev,
+ "io multiplex not supported for this device\n");
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ struct snd_pcm_str *playback_str =
+ &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (playback_str->substream_opened) {
+ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
+ val &= I2S_TXCR_CSR_MASK;
+ usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
+ }
+
+ regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
+ val &= I2S_RXCR_CSR_MASK;
+
+ if (to_ch_num(val) > usable_chs) {
+ dev_err(i2s_tdm->dev,
+ "Capture channels (%d) > usable channels (%d)\n",
+ to_ch_num(val), usable_chs);
+ return -EINVAL;
+ }
+
+ } else {
+ struct snd_pcm_str *capture_str =
+ &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (capture_str->substream_opened) {
+ regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
+ val &= I2S_RXCR_CSR_MASK;
+ usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
+ }
+
+ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
+ val &= I2S_TXCR_CSR_MASK;
+
+ if (to_ch_num(val) > usable_chs) {
+ dev_err(i2s_tdm->dev,
+ "Playback channels (%d) > usable channels (%d)\n",
+ to_ch_num(val), usable_chs);
+ return -EINVAL;
+ }
+ }
+
+ val <<= i2s_tdm->soc_data->grf_shift;
+ val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16;
+ regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val);
+
+ return 0;
+}
+
+static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ unsigned int div_bclk,
+ unsigned int div_lrck,
+ unsigned int fmt)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ unsigned long flags;
+
+ if (!i2s_tdm->clk_trcm)
+ return 0;
+
+ spin_lock_irqsave(&i2s_tdm->lock, flags);
+ if (i2s_tdm->refcount)
+ rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK,
+ I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK,
+ I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ fmt);
+ else
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ fmt);
+
+ if (i2s_tdm->refcount)
+ rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm);
+ spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream,
+ unsigned int freq, int dir)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
+
+ if (i2s_tdm->clk_trcm) {
+ i2s_tdm->mclk_tx_freq = freq;
+ i2s_tdm->mclk_rx_freq = freq;
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_tdm->mclk_tx_freq = freq;
+ else
+ i2s_tdm->mclk_rx_freq = freq;
+ }
+
+ dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n",
+ stream ? "rx" : "tx", freq);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ unsigned int val = 0;
+ unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64;
+ int err;
+
+ if (i2s_tdm->is_master_mode) {
+ struct clk *mclk;
+
+ if (i2s_tdm->clk_trcm == TRCM_TX) {
+ mclk = i2s_tdm->mclk_tx;
+ mclk_rate = i2s_tdm->mclk_tx_freq;
+ } else if (i2s_tdm->clk_trcm == TRCM_RX) {
+ mclk = i2s_tdm->mclk_rx;
+ mclk_rate = i2s_tdm->mclk_rx_freq;
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mclk = i2s_tdm->mclk_tx;
+ mclk_rate = i2s_tdm->mclk_tx_freq;
+ } else {
+ mclk = i2s_tdm->mclk_rx;
+ mclk_rate = i2s_tdm->mclk_rx_freq;
+ }
+
+ err = clk_set_rate(mclk, mclk_rate);
+ if (err)
+ return err;
+
+ mclk_rate = clk_get_rate(mclk);
+ bclk_rate = i2s_tdm->frame_width * params_rate(params);
+ if (!bclk_rate)
+ return -EINVAL;
+
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+ div_lrck = bclk_rate / params_rate(params);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ val |= I2S_TXCR_VDW(8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= I2S_TXCR_VDW(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= I2S_TXCR_VDW(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= I2S_TXCR_VDW(24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val |= I2S_TXCR_VDW(32);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_channels(params)) {
+ case 8:
+ val |= I2S_CHN_8;
+ break;
+ case 6:
+ val |= I2S_CHN_6;
+ break;
+ case 4:
+ val |= I2S_CHN_4;
+ break;
+ case 2:
+ val |= I2S_CHN_2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (i2s_tdm->clk_trcm) {
+ rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, val);
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_TXM_MASK,
+ I2S_CLKDIV_TXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_TSD_MASK,
+ I2S_CKR_TSD(div_lrck));
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ val);
+ } else {
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_RXM_MASK,
+ I2S_CLKDIV_RXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_RSD_MASK,
+ I2S_CKR_RSD(div_lrck));
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ val);
+ }
+
+ return rockchip_i2s_io_multiplex(substream, dai);
+}
+
+static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (i2s_tdm->clk_trcm)
+ rockchip_snd_txrxctrl(substream, dai, 1);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rockchip_snd_rxctrl(i2s_tdm, 1);
+ else
+ rockchip_snd_txctrl(i2s_tdm, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (i2s_tdm->clk_trcm)
+ rockchip_snd_txrxctrl(substream, dai, 0);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rockchip_snd_rxctrl(i2s_tdm, 0);
+ else
+ rockchip_snd_txctrl(i2s_tdm, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ if (i2s_tdm->has_capture)
+ snd_soc_dai_dma_data_set_capture(dai, &i2s_tdm->capture_dma_data);
+ if (i2s_tdm->has_playback)
+ snd_soc_dai_dma_data_set_playback(dai, &i2s_tdm->playback_dma_data);
+
+ return 0;
+}
+
+static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ i2s_tdm->tdm_mode = true;
+ i2s_tdm->frame_width = slots * slot_width;
+ mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK;
+ val = TDM_SLOT_BIT_WIDTH(slot_width) |
+ TDM_FRAME_WIDTH(slots * slot_width);
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR,
+ mask, val);
+ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR,
+ mask, val);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ if (ratio < 32 || ratio > 512 || ratio % 2 == 1)
+ return -EINVAL;
+
+ i2s_tdm->frame_width = ratio;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
+ .probe = rockchip_i2s_tdm_dai_probe,
+ .hw_params = rockchip_i2s_tdm_hw_params,
+ .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio,
+ .set_fmt = rockchip_i2s_tdm_set_fmt,
+ .set_sysclk = rockchip_i2s_tdm_set_sysclk,
+ .set_tdm_slot = rockchip_dai_tdm_slot,
+ .trigger = rockchip_i2s_tdm_trigger,
+};
+
+static const struct snd_soc_component_driver rockchip_i2s_tdm_component = {
+ .name = DRV_NAME,
+ .legacy_dai_naming = 1,
+};
+
+static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TXCR:
+ case I2S_RXCR:
+ case I2S_CKR:
+ case I2S_DMACR:
+ case I2S_INTCR:
+ case I2S_XFER:
+ case I2S_CLR:
+ case I2S_TXDR:
+ case I2S_TDM_TXCR:
+ case I2S_TDM_RXCR:
+ case I2S_CLKDIV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TXCR:
+ case I2S_RXCR:
+ case I2S_CKR:
+ case I2S_DMACR:
+ case I2S_INTCR:
+ case I2S_XFER:
+ case I2S_CLR:
+ case I2S_TXDR:
+ case I2S_RXDR:
+ case I2S_TXFIFOLR:
+ case I2S_INTSR:
+ case I2S_RXFIFOLR:
+ case I2S_TDM_TXCR:
+ case I2S_TDM_RXCR:
+ case I2S_CLKDIV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case I2S_TXFIFOLR:
+ case I2S_INTSR:
+ case I2S_CLR:
+ case I2S_TXDR:
+ case I2S_RXDR:
+ case I2S_RXFIFOLR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg)
+{
+ if (reg == I2S_RXDR)
+ return true;
+ return false;
+}
+
+static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = {
+ {0x00, 0x7200000f},
+ {0x04, 0x01c8000f},
+ {0x08, 0x00001f1f},
+ {0x10, 0x001f0000},
+ {0x14, 0x01f00000},
+ {0x30, 0x00003eff},
+ {0x34, 0x00003eff},
+ {0x38, 0x00000707},
+};
+
+static const struct regmap_config rockchip_i2s_tdm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = I2S_CLKDIV,
+ .reg_defaults = rockchip_i2s_tdm_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults),
+ .writeable_reg = rockchip_i2s_tdm_wr_reg,
+ .readable_reg = rockchip_i2s_tdm_rd_reg,
+ .volatile_reg = rockchip_i2s_tdm_volatile_reg,
+ .precious_reg = rockchip_i2s_tdm_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int common_soc_init(struct device *dev, u32 addr)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+ const struct txrx_config *configs = i2s_tdm->soc_data->configs;
+ u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm;
+ int i;
+
+ if (trcm == TRCM_TXRX)
+ return 0;
+
+ if (IS_ERR_OR_NULL(i2s_tdm->grf)) {
+ dev_err(i2s_tdm->dev,
+ "no grf present but non-txrx TRCM specified\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < i2s_tdm->soc_data->config_count; i++) {
+ if (addr != configs[i].addr)
+ continue;
+ reg = configs[i].reg;
+ if (trcm == TRCM_TX)
+ val = configs[i].txonly;
+ else
+ val = configs[i].rxonly;
+
+ if (reg)
+ regmap_write(i2s_tdm->grf, reg, val);
+ }
+
+ return 0;
+}
+
+static const struct txrx_config px30_txrx_config[] = {
+ { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY },
+};
+
+static const struct txrx_config rk1808_txrx_config[] = {
+ { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY },
+};
+
+static const struct txrx_config rk3308_txrx_config[] = {
+ { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY },
+ { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY },
+};
+
+static const struct txrx_config rk3568_txrx_config[] = {
+ { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY },
+ { 0xfe410000, 0x508, RK3568_I2S1_MCLK_TX_OE, RK3568_I2S1_MCLK_RX_OE },
+ { 0xfe420000, 0x508, RK3568_I2S2_MCLK_OE, RK3568_I2S2_MCLK_OE },
+ { 0xfe430000, 0x504, RK3568_I2S3_CLK_TXONLY, RK3568_I2S3_CLK_RXONLY },
+ { 0xfe430000, 0x508, RK3568_I2S3_MCLK_TXONLY, RK3568_I2S3_MCLK_RXONLY },
+ { 0xfe430000, 0x508, RK3568_I2S3_MCLK_OE, RK3568_I2S3_MCLK_OE },
+};
+
+static const struct txrx_config rv1126_txrx_config[] = {
+ { 0xff800000, 0x10260, RV1126_I2S0_CLK_TXONLY, RV1126_I2S0_CLK_RXONLY },
+};
+
+static const struct rk_i2s_soc_data px30_i2s_soc_data = {
+ .softrst_offset = 0x0300,
+ .configs = px30_txrx_config,
+ .config_count = ARRAY_SIZE(px30_txrx_config),
+ .init = common_soc_init,
+};
+
+static const struct rk_i2s_soc_data rk1808_i2s_soc_data = {
+ .softrst_offset = 0x0300,
+ .configs = rk1808_txrx_config,
+ .config_count = ARRAY_SIZE(rk1808_txrx_config),
+ .init = common_soc_init,
+};
+
+static const struct rk_i2s_soc_data rk3308_i2s_soc_data = {
+ .softrst_offset = 0x0400,
+ .grf_reg_offset = 0x0308,
+ .grf_shift = 5,
+ .configs = rk3308_txrx_config,
+ .config_count = ARRAY_SIZE(rk3308_txrx_config),
+ .init = common_soc_init,
+};
+
+static const struct rk_i2s_soc_data rk3568_i2s_soc_data = {
+ .softrst_offset = 0x0400,
+ .configs = rk3568_txrx_config,
+ .config_count = ARRAY_SIZE(rk3568_txrx_config),
+ .init = common_soc_init,
+};
+
+static const struct rk_i2s_soc_data rv1126_i2s_soc_data = {
+ .softrst_offset = 0x0300,
+ .configs = rv1126_txrx_config,
+ .config_count = ARRAY_SIZE(rv1126_txrx_config),
+ .init = common_soc_init,
+};
+
+static const struct of_device_id rockchip_i2s_tdm_match[] = {
+ { .compatible = "rockchip,px30-i2s-tdm", .data = &px30_i2s_soc_data },
+ { .compatible = "rockchip,rk1808-i2s-tdm", .data = &rk1808_i2s_soc_data },
+ { .compatible = "rockchip,rk3308-i2s-tdm", .data = &rk3308_i2s_soc_data },
+ { .compatible = "rockchip,rk3568-i2s-tdm", .data = &rk3568_i2s_soc_data },
+ { .compatible = "rockchip,rk3588-i2s-tdm" },
+ { .compatible = "rockchip,rv1126-i2s-tdm", .data = &rv1126_i2s_soc_data },
+ {},
+};
+
+static const struct snd_soc_dai_driver i2s_tdm_dai = {
+ .ops = &rockchip_i2s_tdm_dai_ops,
+};
+
+static int rockchip_i2s_tdm_init_dai(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ struct snd_soc_dai_driver *dai;
+ struct property *dma_names;
+ const char *dma_name;
+ u64 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);
+ struct device_node *node = i2s_tdm->dev->of_node;
+
+ of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
+ if (!strcmp(dma_name, "tx"))
+ i2s_tdm->has_playback = true;
+ if (!strcmp(dma_name, "rx"))
+ i2s_tdm->has_capture = true;
+ }
+
+ dai = devm_kmemdup(i2s_tdm->dev, &i2s_tdm_dai,
+ sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ if (i2s_tdm->has_playback) {
+ dai->playback.stream_name = "Playback";
+ dai->playback.channels_min = 2;
+ dai->playback.channels_max = 8;
+ dai->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ dai->playback.formats = formats;
+ }
+
+ if (i2s_tdm->has_capture) {
+ dai->capture.stream_name = "Capture";
+ dai->capture.channels_min = 2;
+ dai->capture.channels_max = 8;
+ dai->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ dai->capture.formats = formats;
+ }
+
+ if (i2s_tdm->clk_trcm != TRCM_TXRX)
+ dai->symmetric_rate = 1;
+
+ i2s_tdm->dai = dai;
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num,
+ bool is_rx_path)
+{
+ unsigned int *i2s_data;
+ int i, j;
+
+ if (is_rx_path)
+ i2s_data = i2s_tdm->i2s_sdis;
+ else
+ i2s_data = i2s_tdm->i2s_sdos;
+
+ for (i = 0; i < num; i++) {
+ if (i2s_data[i] > CH_GRP_MAX - 1) {
+ dev_err(i2s_tdm->dev,
+ "%s path i2s_data[%d]: %d is too high, max is: %d\n",
+ is_rx_path ? "RX" : "TX",
+ i, i2s_data[i], CH_GRP_MAX);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < num; j++) {
+ if (i == j)
+ continue;
+
+ if (i2s_data[i] == i2s_data[j]) {
+ dev_err(i2s_tdm->dev,
+ "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n",
+ is_rx_path ? "RX" : "TX",
+ i, i2s_data[i],
+ j, i2s_data[j]);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num)
+{
+ int idx;
+
+ for (idx = 0; idx < num; idx++) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_PATH_MASK(idx),
+ I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx]));
+ }
+}
+
+static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num)
+{
+ int idx;
+
+ for (idx = 0; idx < num; idx++) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_PATH_MASK(idx),
+ I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx]));
+ }
+}
+
+static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm,
+ int num, bool is_rx_path)
+{
+ if (is_rx_path)
+ rockchip_i2s_tdm_rx_path_config(i2s_tdm, num);
+ else
+ rockchip_i2s_tdm_tx_path_config(i2s_tdm, num);
+}
+
+static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct device_node *np,
+ bool is_rx_path)
+{
+ char *i2s_tx_path_prop = "rockchip,i2s-tx-route";
+ char *i2s_rx_path_prop = "rockchip,i2s-rx-route";
+ char *i2s_path_prop;
+ unsigned int *i2s_data;
+ int num, ret = 0;
+
+ if (is_rx_path) {
+ i2s_path_prop = i2s_rx_path_prop;
+ i2s_data = i2s_tdm->i2s_sdis;
+ } else {
+ i2s_path_prop = i2s_tx_path_prop;
+ i2s_data = i2s_tdm->i2s_sdos;
+ }
+
+ num = of_count_phandle_with_args(np, i2s_path_prop, NULL);
+ if (num < 0) {
+ if (num != -ENOENT) {
+ dev_err(i2s_tdm->dev,
+ "Failed to read '%s' num: %d\n",
+ i2s_path_prop, num);
+ ret = num;
+ }
+ return ret;
+ } else if (num != CH_GRP_MAX) {
+ dev_err(i2s_tdm->dev,
+ "The num: %d should be: %d\n", num, CH_GRP_MAX);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(np, i2s_path_prop,
+ i2s_data, num);
+ if (ret < 0) {
+ dev_err(i2s_tdm->dev,
+ "Failed to read '%s': %d\n",
+ i2s_path_prop, ret);
+ return ret;
+ }
+
+ ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path);
+ if (ret < 0) {
+ dev_err(i2s_tdm->dev,
+ "Failed to check i2s data bus: %d\n", ret);
+ return ret;
+ }
+
+ rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct device_node *np)
+{
+ return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 0);
+}
+
+static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm,
+ struct device_node *np)
+{
+ return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1);
+}
+
+static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct rk_i2s_tdm_dev *i2s_tdm;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL);
+ if (!i2s_tdm)
+ return -ENOMEM;
+
+ i2s_tdm->dev = &pdev->dev;
+
+ spin_lock_init(&i2s_tdm->lock);
+ i2s_tdm->soc_data = device_get_match_data(&pdev->dev);
+ i2s_tdm->frame_width = 64;
+
+ i2s_tdm->clk_trcm = TRCM_TXRX;
+ if (of_property_read_bool(node, "rockchip,trcm-sync-tx-only"))
+ i2s_tdm->clk_trcm = TRCM_TX;
+ if (of_property_read_bool(node, "rockchip,trcm-sync-rx-only")) {
+ if (i2s_tdm->clk_trcm) {
+ dev_err(i2s_tdm->dev, "invalid trcm-sync configuration\n");
+ return -EINVAL;
+ }
+ i2s_tdm->clk_trcm = TRCM_RX;
+ }
+
+ ret = rockchip_i2s_tdm_init_dai(i2s_tdm);
+ if (ret)
+ return ret;
+
+ i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+ i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
+ "tx-m");
+ if (IS_ERR(i2s_tdm->tx_reset)) {
+ ret = PTR_ERR(i2s_tdm->tx_reset);
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "Error in tx-m reset control\n");
+ }
+
+ i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
+ "rx-m");
+ if (IS_ERR(i2s_tdm->rx_reset)) {
+ ret = PTR_ERR(i2s_tdm->rx_reset);
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "Error in rx-m reset control\n");
+ }
+
+ i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(i2s_tdm->hclk)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->hclk),
+ "Failed to get clock hclk\n");
+ }
+
+ i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx");
+ if (IS_ERR(i2s_tdm->mclk_tx)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_tx),
+ "Failed to get clock mclk_tx\n");
+ }
+
+ i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx");
+ if (IS_ERR(i2s_tdm->mclk_rx)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_rx),
+ "Failed to get clock mclk_rx\n");
+ }
+
+ i2s_tdm->io_multiplex =
+ of_property_read_bool(node, "rockchip,io-multiplex");
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(regs),
+ "Failed to get resource IORESOURCE_MEM\n");
+ }
+
+ i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &rockchip_i2s_tdm_regmap_config);
+ if (IS_ERR(i2s_tdm->regmap)) {
+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->regmap),
+ "Failed to initialise regmap\n");
+ }
+
+ if (i2s_tdm->has_playback) {
+ i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;
+ i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s_tdm->playback_dma_data.maxburst = 8;
+ }
+
+ if (i2s_tdm->has_capture) {
+ i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR;
+ i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s_tdm->capture_dma_data.maxburst = 8;
+ }
+
+ ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&pdev->dev, i2s_tdm);
+
+ ret = clk_prepare_enable(i2s_tdm->hclk);
+ if (ret) {
+ return dev_err_probe(i2s_tdm->dev, ret,
+ "Failed to enable clock hclk\n");
+ }
+
+ ret = i2s_tdm_prepare_enable_mclk(i2s_tdm);
+ if (ret) {
+ dev_err_probe(i2s_tdm->dev, ret, "Failed to enable one or more mclks\n");
+ goto err_disable_hclk;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
+ I2S_DMACR_TDL(16));
+ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
+ I2S_DMACR_RDL(16));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, I2S_CKR_TRCM_MASK,
+ i2s_tdm->clk_trcm << I2S_CKR_TRCM_SHIFT);
+
+ if (i2s_tdm->soc_data && i2s_tdm->soc_data->init)
+ i2s_tdm->soc_data->init(&pdev->dev, res->start);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rockchip_i2s_tdm_component,
+ i2s_tdm->dai, 1);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI\n");
+ goto err_suspend;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM\n");
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ i2s_tdm_runtime_suspend(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+err_disable_hclk:
+ clk_disable_unprepare(i2s_tdm->hclk);
+
+ return ret;
+}
+
+static void rockchip_i2s_tdm_remove(struct platform_device *pdev)
+{
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ i2s_tdm_runtime_suspend(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int rockchip_i2s_tdm_suspend(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+
+ regcache_mark_dirty(i2s_tdm->regmap);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_resume(struct device *dev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+ ret = regcache_sync(i2s_tdm->regmap);
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = {
+ RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, rockchip_i2s_tdm_resume)
+};
+
+static struct platform_driver rockchip_i2s_tdm_driver = {
+ .probe = rockchip_i2s_tdm_probe,
+ .remove = rockchip_i2s_tdm_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = rockchip_i2s_tdm_match,
+ .pm = pm_ptr(&rockchip_i2s_tdm_pm_ops),
+ },
+};
+module_platform_driver(rockchip_i2s_tdm_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface");
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match);
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h
new file mode 100644
index 000000000000..0171e05ee886
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.h
@@ -0,0 +1,400 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver
+ *
+ * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
+ * Author: Sugar Zhang <sugar.zhang@rock-chips.com>
+ *
+ */
+
+#ifndef _ROCKCHIP_I2S_TDM_H
+#define _ROCKCHIP_I2S_TDM_H
+
+#include <linux/hw_bitfield.h>
+
+/*
+ * TXCR
+ * transmit operation control register
+ */
+#define I2S_TXCR_PATH_SHIFT(x) (23 + (x) * 2)
+#define I2S_TXCR_PATH_MASK(x) (0x3 << I2S_TXCR_PATH_SHIFT(x))
+#define I2S_TXCR_PATH(x, v) ((v) << I2S_TXCR_PATH_SHIFT(x))
+#define I2S_TXCR_RCNT_SHIFT 17
+#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT)
+#define I2S_TXCR_CSR_SHIFT 15
+#define I2S_TXCR_CSR(x) ((x) << I2S_TXCR_CSR_SHIFT)
+#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT)
+#define I2S_TXCR_HWT BIT(14)
+#define I2S_TXCR_SJM_SHIFT 12
+#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT)
+#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT)
+#define I2S_TXCR_FBM_SHIFT 11
+#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT)
+#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT)
+#define I2S_TXCR_IBM_SHIFT 9
+#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT)
+#define I2S_TXCR_PBM_SHIFT 7
+#define I2S_TXCR_PBM_MODE(x) ((x) << I2S_TXCR_PBM_SHIFT)
+#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT)
+#define I2S_TXCR_TFS_SHIFT 5
+#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_TDM_PCM (2 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_TDM_I2S (3 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_TFS_MASK (3 << I2S_TXCR_TFS_SHIFT)
+#define I2S_TXCR_VDW_SHIFT 0
+#define I2S_TXCR_VDW(x) (((x) - 1) << I2S_TXCR_VDW_SHIFT)
+#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
+
+/*
+ * RXCR
+ * receive operation control register
+ */
+#define I2S_RXCR_PATH_SHIFT(x) (17 + (x) * 2)
+#define I2S_RXCR_PATH_MASK(x) (0x3 << I2S_RXCR_PATH_SHIFT(x))
+#define I2S_RXCR_PATH(x, v) ((v) << I2S_RXCR_PATH_SHIFT(x))
+#define I2S_RXCR_CSR_SHIFT 15
+#define I2S_RXCR_CSR(x) ((x) << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_HWT BIT(14)
+#define I2S_RXCR_SJM_SHIFT 12
+#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
+#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT)
+#define I2S_RXCR_FBM_SHIFT 11
+#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT)
+#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT)
+#define I2S_RXCR_IBM_SHIFT 9
+#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT)
+#define I2S_RXCR_PBM_SHIFT 7
+#define I2S_RXCR_PBM_MODE(x) ((x) << I2S_RXCR_PBM_SHIFT)
+#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT)
+#define I2S_RXCR_TFS_SHIFT 5
+#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_TDM_PCM (2 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_TDM_I2S (3 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_TFS_MASK (3 << I2S_RXCR_TFS_SHIFT)
+#define I2S_RXCR_VDW_SHIFT 0
+#define I2S_RXCR_VDW(x) (((x) - 1) << I2S_RXCR_VDW_SHIFT)
+#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
+
+/*
+ * CKR
+ * clock generation register
+ */
+#define I2S_CKR_TRCM_SHIFT 28
+#define I2S_CKR_TRCM(x) ((x) << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_MSS_SHIFT 27
+#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
+#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
+#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT)
+#define I2S_CKR_CKP_SHIFT 26
+#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_RLP_SHIFT 25
+#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_TLP_SHIFT 24
+#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_MDIV_SHIFT 16
+#define I2S_CKR_MDIV(x) (((x) - 1) << I2S_CKR_MDIV_SHIFT)
+#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT)
+#define I2S_CKR_RSD_SHIFT 8
+#define I2S_CKR_RSD(x) (((x) - 1) << I2S_CKR_RSD_SHIFT)
+#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT)
+#define I2S_CKR_TSD_SHIFT 0
+#define I2S_CKR_TSD(x) (((x) - 1) << I2S_CKR_TSD_SHIFT)
+#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT)
+
+/*
+ * FIFOLR
+ * FIFO level register
+ */
+#define I2S_FIFOLR_RFL_SHIFT 24
+#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT)
+#define I2S_FIFOLR_TFL3_SHIFT 18
+#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT)
+#define I2S_FIFOLR_TFL2_SHIFT 12
+#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT)
+#define I2S_FIFOLR_TFL1_SHIFT 6
+#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT)
+#define I2S_FIFOLR_TFL0_SHIFT 0
+#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT)
+
+/*
+ * DMACR
+ * DMA control register
+ */
+#define I2S_DMACR_RDE_SHIFT 24
+#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT)
+#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT)
+#define I2S_DMACR_RDL_SHIFT 16
+#define I2S_DMACR_RDL(x) (((x) - 1) << I2S_DMACR_RDL_SHIFT)
+#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT)
+#define I2S_DMACR_TDE_SHIFT 8
+#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT)
+#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT)
+#define I2S_DMACR_TDL_SHIFT 0
+#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT)
+#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT)
+
+/*
+ * INTCR
+ * interrupt control register
+ */
+#define I2S_INTCR_RFT_SHIFT 20
+#define I2S_INTCR_RFT(x) (((x) - 1) << I2S_INTCR_RFT_SHIFT)
+#define I2S_INTCR_RXOIC BIT(18)
+#define I2S_INTCR_RXOIE_SHIFT 17
+#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT)
+#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT)
+#define I2S_INTCR_RXFIE_SHIFT 16
+#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT)
+#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT)
+#define I2S_INTCR_TFT_SHIFT 4
+#define I2S_INTCR_TFT(x) (((x) - 1) << I2S_INTCR_TFT_SHIFT)
+#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT)
+#define I2S_INTCR_TXUIC BIT(2)
+#define I2S_INTCR_TXUIE_SHIFT 1
+#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT)
+#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT)
+
+/*
+ * INTSR
+ * interrupt status register
+ */
+#define I2S_INTSR_TXEIE_SHIFT 0
+#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT)
+#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT)
+#define I2S_INTSR_RXOI_SHIFT 17
+#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT)
+#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT)
+#define I2S_INTSR_RXFI_SHIFT 16
+#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT)
+#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT)
+#define I2S_INTSR_TXUI_SHIFT 1
+#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT)
+#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT)
+#define I2S_INTSR_TXEI_SHIFT 0
+#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT)
+#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT)
+
+/*
+ * XFER
+ * Transfer start register
+ */
+#define I2S_XFER_RXS_SHIFT 1
+#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT)
+#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT)
+#define I2S_XFER_TXS_SHIFT 0
+#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT)
+#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT)
+
+/*
+ * CLR
+ * clear SCLK domain logic register
+ */
+#define I2S_CLR_RXC BIT(1)
+#define I2S_CLR_TXC BIT(0)
+
+/*
+ * TXDR
+ * Transimt FIFO data register, write only.
+ */
+#define I2S_TXDR_MASK (0xff)
+
+/*
+ * RXDR
+ * Receive FIFO data register, write only.
+ */
+#define I2S_RXDR_MASK (0xff)
+
+/*
+ * TDM_CTRL
+ * TDM ctrl register
+ */
+#define TDM_FSYNC_WIDTH_SEL1_MSK GENMASK(20, 18)
+#define TDM_FSYNC_WIDTH_SEL1(x) (((x) - 1) << 18)
+#define TDM_FSYNC_WIDTH_SEL0_MSK BIT(17)
+#define TDM_FSYNC_WIDTH_HALF_FRAME 0
+#define TDM_FSYNC_WIDTH_ONE_FRAME BIT(17)
+#define TDM_SHIFT_CTRL_MSK GENMASK(16, 14)
+#define TDM_SHIFT_CTRL(x) ((x) << 14)
+#define TDM_SLOT_BIT_WIDTH_MSK GENMASK(13, 9)
+#define TDM_SLOT_BIT_WIDTH(x) (((x) - 1) << 9)
+#define TDM_FRAME_WIDTH_MSK GENMASK(8, 0)
+#define TDM_FRAME_WIDTH(x) (((x) - 1) << 0)
+
+/*
+ * CLKDIV
+ * Mclk div register
+ */
+#define I2S_CLKDIV_TXM_SHIFT 0
+#define I2S_CLKDIV_TXM(x) (((x) - 1) << I2S_CLKDIV_TXM_SHIFT)
+#define I2S_CLKDIV_TXM_MASK (0xff << I2S_CLKDIV_TXM_SHIFT)
+#define I2S_CLKDIV_RXM_SHIFT 8
+#define I2S_CLKDIV_RXM(x) (((x) - 1) << I2S_CLKDIV_RXM_SHIFT)
+#define I2S_CLKDIV_RXM_MASK (0xff << I2S_CLKDIV_RXM_SHIFT)
+
+/* Clock divider id */
+enum {
+ ROCKCHIP_DIV_MCLK = 0,
+ ROCKCHIP_DIV_BCLK,
+};
+
+/* channel select */
+#define I2S_CSR_SHIFT 15
+#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
+#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
+#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
+#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
+
+/* io direction cfg register */
+#define I2S_IO_DIRECTION_MASK (7)
+#define I2S_IO_8CH_OUT_2CH_IN (7)
+#define I2S_IO_6CH_OUT_4CH_IN (3)
+#define I2S_IO_4CH_OUT_6CH_IN (1)
+#define I2S_IO_2CH_OUT_8CH_IN (0)
+
+/* I2S REGS */
+#define I2S_TXCR (0x0000)
+#define I2S_RXCR (0x0004)
+#define I2S_CKR (0x0008)
+#define I2S_TXFIFOLR (0x000c)
+#define I2S_DMACR (0x0010)
+#define I2S_INTCR (0x0014)
+#define I2S_INTSR (0x0018)
+#define I2S_XFER (0x001c)
+#define I2S_CLR (0x0020)
+#define I2S_TXDR (0x0024)
+#define I2S_RXDR (0x0028)
+#define I2S_RXFIFOLR (0x002c)
+#define I2S_TDM_TXCR (0x0030)
+#define I2S_TDM_RXCR (0x0034)
+#define I2S_CLKDIV (0x0038)
+
+#define HIWORD_UPDATE(v, h, l) (FIELD_PREP_WM16_CONST(GENMASK((h), (l)), (v)))
+
+/* PX30 GRF CONFIGS */
+#define PX30_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 13, 12)
+#define PX30_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 13, 12)
+#define PX30_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5)
+#define PX30_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5)
+
+#define PX30_I2S0_CLK_TXONLY \
+ (PX30_I2S0_MCLK_OUT_SRC_FROM_TX | PX30_I2S0_CLK_IN_SRC_FROM_TX)
+
+#define PX30_I2S0_CLK_RXONLY \
+ (PX30_I2S0_MCLK_OUT_SRC_FROM_RX | PX30_I2S0_CLK_IN_SRC_FROM_RX)
+
+/* RK1808 GRF CONFIGS */
+#define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2)
+#define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2)
+#define RK1808_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 1, 0)
+#define RK1808_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 1, 0)
+
+#define RK1808_I2S0_CLK_TXONLY \
+ (RK1808_I2S0_MCLK_OUT_SRC_FROM_TX | RK1808_I2S0_CLK_IN_SRC_FROM_TX)
+
+#define RK1808_I2S0_CLK_RXONLY \
+ (RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | RK1808_I2S0_CLK_IN_SRC_FROM_RX)
+
+/* RK3308 GRF CONFIGS */
+#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 10, 10)
+#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 10, 10)
+#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 9, 9)
+#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 9, 9)
+#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 8, 8)
+#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 8, 8)
+#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2)
+#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2)
+#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 1, 1)
+#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 1, 1)
+#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 0, 0)
+#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 0, 0)
+
+#define RK3308_I2S0_CLK_TXONLY \
+ (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX | \
+ RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX | \
+ RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX)
+
+#define RK3308_I2S0_CLK_RXONLY \
+ (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX | \
+ RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX | \
+ RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX)
+
+#define RK3308_I2S1_CLK_TXONLY \
+ (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX | \
+ RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX | \
+ RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX)
+
+#define RK3308_I2S1_CLK_RXONLY \
+ (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX | \
+ RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX | \
+ RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX)
+
+/* RK3568 GRF CONFIGS */
+#define RK3568_I2S1_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5)
+#define RK3568_I2S1_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5)
+
+#define RK3568_I2S1_CLK_TXONLY \
+ RK3568_I2S1_MCLK_OUT_SRC_FROM_TX
+
+#define RK3568_I2S1_CLK_RXONLY \
+ RK3568_I2S1_MCLK_OUT_SRC_FROM_RX
+
+#define RK3568_I2S3_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 15, 15)
+#define RK3568_I2S3_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 15, 15)
+#define RK3568_I2S3_SCLK_SRC_FROM_TX HIWORD_UPDATE(1, 7, 7)
+#define RK3568_I2S3_SCLK_SRC_FROM_RX HIWORD_UPDATE(0, 7, 7)
+#define RK3568_I2S3_LRCK_SRC_FROM_TX HIWORD_UPDATE(1, 6, 6)
+#define RK3568_I2S3_LRCK_SRC_FROM_RX HIWORD_UPDATE(0, 6, 6)
+
+#define RK3568_I2S3_MCLK_TXONLY \
+ RK3568_I2S3_MCLK_OUT_SRC_FROM_TX
+
+#define RK3568_I2S3_CLK_TXONLY \
+ (RK3568_I2S3_SCLK_SRC_FROM_TX | \
+ RK3568_I2S3_LRCK_SRC_FROM_TX)
+
+#define RK3568_I2S3_MCLK_RXONLY \
+ RK3568_I2S3_MCLK_OUT_SRC_FROM_RX
+
+#define RK3568_I2S3_CLK_RXONLY \
+ (RK3568_I2S3_SCLK_SRC_FROM_RX | \
+ RK3568_I2S3_LRCK_SRC_FROM_RX)
+
+#define RK3568_I2S3_MCLK_IE HIWORD_UPDATE(0, 3, 3)
+#define RK3568_I2S3_MCLK_OE HIWORD_UPDATE(1, 3, 3)
+#define RK3568_I2S2_MCLK_IE HIWORD_UPDATE(0, 2, 2)
+#define RK3568_I2S2_MCLK_OE HIWORD_UPDATE(1, 2, 2)
+#define RK3568_I2S1_MCLK_TX_IE HIWORD_UPDATE(0, 1, 1)
+#define RK3568_I2S1_MCLK_TX_OE HIWORD_UPDATE(1, 1, 1)
+#define RK3568_I2S1_MCLK_RX_IE HIWORD_UPDATE(0, 0, 0)
+#define RK3568_I2S1_MCLK_RX_OE HIWORD_UPDATE(1, 0, 0)
+
+/* RV1126 GRF CONFIGS */
+#define RV1126_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 9, 9)
+#define RV1126_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 9, 9)
+
+#define RV1126_I2S0_CLK_TXONLY \
+ RV1126_I2S0_MCLK_OUT_SRC_FROM_TX
+
+#define RV1126_I2S0_CLK_RXONLY \
+ RV1126_I2S0_MCLK_OUT_SRC_FROM_RX
+
+#endif /* _ROCKCHIP_I2S_TDM_H */
diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c
index 543610282cdb..075d0990a126 100644
--- a/sound/soc/rockchip/rockchip_max98090.c
+++ b/sound/soc/rockchip/rockchip_max98090.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip machine ASoC driver for boards using a MAX90809 CODEC.
*
* Copyright (c) 2014, ROCKCHIP CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
- *
*/
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -34,47 +21,130 @@
#define DRV_NAME "rockchip-snd-max98090"
static struct snd_soc_jack headset_jack;
+
+/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin headset_jack_pins[] = {
{
- .pin = "Headset Jack",
- .mask = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
},
+
+};
+
+#define RK_MAX98090_WIDGETS \
+ SND_SOC_DAPM_HP("Headphone", NULL), \
+ SND_SOC_DAPM_MIC("Headset Mic", NULL), \
+ SND_SOC_DAPM_MIC("Int Mic", NULL), \
+ SND_SOC_DAPM_SPK("Speaker", NULL)
+
+#define RK_HDMI_WIDGETS \
+ SND_SOC_DAPM_LINE("HDMI", NULL)
+
+static const struct snd_soc_dapm_widget rk_max98090_dapm_widgets[] = {
+ RK_MAX98090_WIDGETS,
+};
+
+static const struct snd_soc_dapm_widget rk_hdmi_dapm_widgets[] = {
+ RK_HDMI_WIDGETS,
+};
+
+static const struct snd_soc_dapm_widget rk_max98090_hdmi_dapm_widgets[] = {
+ RK_MAX98090_WIDGETS,
+ RK_HDMI_WIDGETS,
+};
+
+#define RK_MAX98090_AUDIO_MAP \
+ {"IN34", NULL, "Headset Mic"}, \
+ {"Headset Mic", NULL, "MICBIAS"}, \
+ {"DMICL", NULL, "Int Mic"}, \
+ {"Headphone", NULL, "HPL"}, \
+ {"Headphone", NULL, "HPR"}, \
+ {"Speaker", NULL, "SPKL"}, \
+ {"Speaker", NULL, "SPKR"}
+
+#define RK_HDMI_AUDIO_MAP \
+ {"HDMI", NULL, "TX"}
+
+static const struct snd_soc_dapm_route rk_max98090_audio_map[] = {
+ RK_MAX98090_AUDIO_MAP,
};
-static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
+static const struct snd_soc_dapm_route rk_hdmi_audio_map[] = {
+ RK_HDMI_AUDIO_MAP,
+};
+
+static const struct snd_soc_dapm_route rk_max98090_hdmi_audio_map[] = {
+ RK_MAX98090_AUDIO_MAP,
+ RK_HDMI_AUDIO_MAP,
+};
+
+#define RK_MAX98090_CONTROLS \
+ SOC_DAPM_PIN_SWITCH("Headphone"), \
+ SOC_DAPM_PIN_SWITCH("Headset Mic"), \
+ SOC_DAPM_PIN_SWITCH("Int Mic"), \
+ SOC_DAPM_PIN_SWITCH("Speaker")
+
+#define RK_HDMI_CONTROLS \
+ SOC_DAPM_PIN_SWITCH("HDMI")
+
+static const struct snd_kcontrol_new rk_max98090_controls[] = {
+ RK_MAX98090_CONTROLS,
};
-static const struct snd_soc_dapm_route rk_audio_map[] = {
- {"IN34", NULL, "Headset Mic"},
- {"IN34", NULL, "MICBIAS"},
- {"MICBIAS", NULL, "Headset Mic"},
- {"DMICL", NULL, "Int Mic"},
- {"Headphone", NULL, "HPL"},
- {"Headphone", NULL, "HPR"},
- {"Speaker", NULL, "SPKL"},
- {"Speaker", NULL, "SPKR"},
+static const struct snd_kcontrol_new rk_hdmi_controls[] = {
+ RK_HDMI_CONTROLS,
};
-static const struct snd_kcontrol_new rk_mc_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Speaker"),
+static const struct snd_kcontrol_new rk_max98090_hdmi_controls[] = {
+ RK_MAX98090_CONTROLS,
+ RK_HDMI_CONTROLS,
};
+static int rk_jack_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(jack->card);
+
+ if (event & SND_JACK_MICROPHONE) {
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SHDN");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_disable_pin(dapm, "SHDN");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct notifier_block rk_jack_nb = {
+ .notifier_call = rk_jack_event,
+};
+
+static int rk_init(struct snd_soc_pcm_runtime *runtime)
+{
+ /*
+ * The jack has already been created in the rk_98090_headset_init()
+ * function.
+ */
+ snd_soc_jack_notifier_register(&headset_jack, &rk_jack_nb);
+
+ return 0;
+}
+
static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int mclk;
switch (params_rate(params)) {
@@ -99,108 +169,264 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
- if (ret < 0) {
- dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+ if (ret) {
+ dev_err(cpu_dai->dev, "Can't set cpu dai clock %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
+
+ /* HDMI codec dai does not need to set sysclk. */
+ if (!strcmp(rtd->dai_link->name, "HDMI"))
+ return 0;
+
+ if (ret) {
+ dev_err(codec_dai->dev, "Can't set codec dai clock %d\n", ret);
return ret;
}
return ret;
}
-static int rk_init(struct snd_soc_pcm_runtime *runtime)
+static int rk_aif1_startup(struct snd_pcm_substream *substream)
{
- /* Enable Headset and 4 Buttons Jack detection */
- return snd_soc_card_jack_new(runtime->card, "Headset Jack",
- SND_JACK_HEADSET |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &headset_jack,
- headset_jack_pins,
- ARRAY_SIZE(headset_jack_pins));
+ /*
+ * Set period size to 240 because pl330 has issue
+ * dealing with larger period in stress testing.
+ */
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 240, 240);
}
-static int rk_98090_headset_init(struct snd_soc_component *component)
+static const struct snd_soc_ops rk_aif1_ops = {
+ .hw_params = rk_aif1_hw_params,
+ .startup = rk_aif1_startup,
+};
+
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+enum {
+ DAILINK_MAX98090,
+ DAILINK_HDMI,
+};
+
+static struct snd_soc_jack rk_hdmi_jack;
+
+static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime)
{
- return ts3a227e_enable_jack_detect(component, &headset_jack);
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
+ int ret;
+
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
+ &rk_hdmi_jack);
+ if (ret) {
+ dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_component_set_jack(component, &rk_hdmi_jack, NULL);
}
-static struct snd_soc_ops rk_aif1_ops = {
- .hw_params = rk_aif1_hw_params,
+/* max98090 dai_link */
+static struct snd_soc_dai_link rk_max98090_dailinks[] = {
+ {
+ .name = "max98090",
+ .stream_name = "Analog",
+ .init = rk_init,
+ .ops = &rk_aif1_ops,
+ /* set max98090 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(analog),
+ },
};
+/* HDMI codec dai_link */
+static struct snd_soc_dai_link rk_hdmi_dailinks[] = {
+ {
+ .name = "HDMI",
+ .stream_name = "HDMI",
+ .init = rk_hdmi_init,
+ .ops = &rk_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(hdmi),
+ }
+};
+
+/* max98090 and HDMI codec dai_link */
+static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = {
+ [DAILINK_MAX98090] = {
+ .name = "max98090",
+ .stream_name = "Analog",
+ .init = rk_init,
+ .ops = &rk_aif1_ops,
+ /* set max98090 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(analog),
+ },
+ [DAILINK_HDMI] = {
+ .name = "HDMI",
+ .stream_name = "HDMI",
+ .init = rk_hdmi_init,
+ .ops = &rk_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(hdmi),
+ }
+};
+
+static int rk_98090_headset_init(struct snd_soc_component *component);
+
static struct snd_soc_aux_dev rk_98090_headset_dev = {
- .name = "Headset Chip",
+ .dlc = COMP_EMPTY(),
.init = rk_98090_headset_init,
};
-static struct snd_soc_dai_link rk_dailink = {
- .name = "max98090",
- .stream_name = "Audio",
- .codec_dai_name = "HiFi",
- .init = rk_init,
- .ops = &rk_aif1_ops,
- /* set max98090 as slave */
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+static struct snd_soc_card rockchip_max98090_card = {
+ .name = "ROCKCHIP-I2S",
+ .owner = THIS_MODULE,
+ .dai_link = rk_max98090_dailinks,
+ .num_links = ARRAY_SIZE(rk_max98090_dailinks),
+ .aux_dev = &rk_98090_headset_dev,
+ .num_aux_devs = 1,
+ .dapm_widgets = rk_max98090_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_max98090_dapm_widgets),
+ .dapm_routes = rk_max98090_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_max98090_audio_map),
+ .controls = rk_max98090_controls,
+ .num_controls = ARRAY_SIZE(rk_max98090_controls),
};
-static struct snd_soc_card snd_soc_card_rk = {
- .name = "ROCKCHIP-I2S",
+static struct snd_soc_card rockchip_hdmi_card = {
+ .name = "ROCKCHIP-HDMI",
.owner = THIS_MODULE,
- .dai_link = &rk_dailink,
- .num_links = 1,
+ .dai_link = rk_hdmi_dailinks,
+ .num_links = ARRAY_SIZE(rk_hdmi_dailinks),
+ .dapm_widgets = rk_hdmi_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_hdmi_dapm_widgets),
+ .dapm_routes = rk_hdmi_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_hdmi_audio_map),
+ .controls = rk_hdmi_controls,
+ .num_controls = ARRAY_SIZE(rk_hdmi_controls),
+};
+
+static struct snd_soc_card rockchip_max98090_hdmi_card = {
+ .name = "ROCKCHIP-MAX98090-HDMI",
+ .owner = THIS_MODULE,
+ .dai_link = rk_max98090_hdmi_dailinks,
+ .num_links = ARRAY_SIZE(rk_max98090_hdmi_dailinks),
.aux_dev = &rk_98090_headset_dev,
.num_aux_devs = 1,
- .dapm_widgets = rk_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
- .dapm_routes = rk_audio_map,
- .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
- .controls = rk_mc_controls,
- .num_controls = ARRAY_SIZE(rk_mc_controls),
+ .dapm_widgets = rk_max98090_hdmi_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk_max98090_hdmi_dapm_widgets),
+ .dapm_routes = rk_max98090_hdmi_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rk_max98090_hdmi_audio_map),
+ .controls = rk_max98090_hdmi_controls,
+ .num_controls = ARRAY_SIZE(rk_max98090_hdmi_controls),
};
+static int rk_98090_headset_init(struct snd_soc_component *component)
+{
+ int ret;
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new_pins(component->card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret)
+ return ret;
+
+ ret = ts3a227e_enable_jack_detect(component, &headset_jack);
+
+ return ret;
+}
+
+static int rk_parse_headset_from_of(struct device *dev, struct device_node *np)
+{
+ rk_98090_headset_dev.dlc.of_node = of_parse_phandle(
+ np, "rockchip,headset-codec", 0);
+ if (!rk_98090_headset_dev.dlc.of_node) {
+ dev_err(dev,
+ "Property 'rockchip,headset-codec' missing/invalid\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int snd_rk_mc_probe(struct platform_device *pdev)
{
int ret = 0;
- struct snd_soc_card *card = &snd_soc_card_rk;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
+ struct device_node *np_cpu;
+ struct device_node *np_audio, *np_hdmi;
- /* register the soc card */
- card->dev = &pdev->dev;
+ /* Parse DTS for I2S controller. */
+ np_cpu = of_parse_phandle(np, "rockchip,i2s-controller", 0);
- rk_dailink.codec_of_node = of_parse_phandle(np,
- "rockchip,audio-codec", 0);
- if (!rk_dailink.codec_of_node) {
+ if (!np_cpu) {
dev_err(&pdev->dev,
- "Property 'rockchip,audio-codec' missing or invalid\n");
+ "Property 'rockchip,i2s-controller missing or invalid\n");
return -EINVAL;
}
- rk_dailink.cpu_of_node = of_parse_phandle(np,
- "rockchip,i2s-controller", 0);
- if (!rk_dailink.cpu_of_node) {
- dev_err(&pdev->dev,
- "Property 'rockchip,i2s-controller' missing or invalid\n");
+ /*
+ * Find the card to use based on the presences of audio codec
+ * and hdmi codec in device property. Set their of_node accordingly.
+ */
+ np_audio = of_parse_phandle(np, "rockchip,audio-codec", 0);
+ np_hdmi = of_parse_phandle(np, "rockchip,hdmi-codec", 0);
+ if (np_audio && np_hdmi) {
+ card = &rockchip_max98090_hdmi_card;
+ card->dai_link[DAILINK_MAX98090].codecs->of_node = np_audio;
+ card->dai_link[DAILINK_HDMI].codecs->of_node = np_hdmi;
+ card->dai_link[DAILINK_MAX98090].cpus->of_node = np_cpu;
+ card->dai_link[DAILINK_MAX98090].platforms->of_node = np_cpu;
+ card->dai_link[DAILINK_HDMI].cpus->of_node = np_cpu;
+ card->dai_link[DAILINK_HDMI].platforms->of_node = np_cpu;
+ } else if (np_audio) {
+ card = &rockchip_max98090_card;
+ card->dai_link[0].codecs->of_node = np_audio;
+ card->dai_link[0].cpus->of_node = np_cpu;
+ card->dai_link[0].platforms->of_node = np_cpu;
+ } else if (np_hdmi) {
+ card = &rockchip_hdmi_card;
+ card->dai_link[0].codecs->of_node = np_hdmi;
+ card->dai_link[0].cpus->of_node = np_cpu;
+ card->dai_link[0].platforms->of_node = np_cpu;
+ } else {
+ dev_err(dev, "At least one of codecs should be specified\n");
return -EINVAL;
}
- rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
+ card->dev = dev;
- rk_98090_headset_dev.codec_of_node = of_parse_phandle(np,
- "rockchip,headset-codec", 0);
- if (!rk_98090_headset_dev.codec_of_node) {
- dev_err(&pdev->dev,
- "Property 'rockchip,headset-codec' missing/invalid\n");
- return -EINVAL;
+ /* Parse headset detection codec. */
+ if (np_audio) {
+ ret = rk_parse_headset_from_of(dev, np);
+ if (ret)
+ return ret;
}
+ /* Parse card name. */
ret = snd_soc_of_parse_card_name(card, "rockchip,model");
if (ret) {
dev_err(&pdev->dev,
@@ -208,6 +434,7 @@ static int snd_rk_mc_probe(struct platform_device *pdev)
return ret;
}
+ /* register the soc card */
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
new file mode 100644
index 000000000000..c1ee470ec607
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/rational.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "rockchip_pdm.h"
+
+#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */
+#define PDM_SIGNOFF_CLK_RATE (100000000)
+#define PDM_PATH_MAX (4)
+
+enum rk_pdm_version {
+ RK_PDM_RK3229,
+ RK_PDM_RK3308,
+ RK_PDM_RV1126,
+};
+
+struct rk_pdm_dev {
+ struct device *dev;
+ struct clk *clk;
+ struct clk *hclk;
+ struct regmap *regmap;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct reset_control *reset;
+ enum rk_pdm_version version;
+};
+
+struct rk_pdm_clkref {
+ unsigned int sr;
+ unsigned int clk;
+ unsigned int clk_out;
+};
+
+struct rk_pdm_ds_ratio {
+ unsigned int ratio;
+ unsigned int sr;
+};
+
+static struct rk_pdm_clkref clkref[] = {
+ { 8000, 40960000, 2048000 },
+ { 11025, 56448000, 2822400 },
+ { 12000, 61440000, 3072000 },
+ { 8000, 98304000, 2048000 },
+ { 12000, 98304000, 3072000 },
+};
+
+static struct rk_pdm_ds_ratio ds_ratio[] = {
+ { 0, 192000 },
+ { 0, 176400 },
+ { 0, 128000 },
+ { 1, 96000 },
+ { 1, 88200 },
+ { 1, 64000 },
+ { 2, 48000 },
+ { 2, 44100 },
+ { 2, 32000 },
+ { 3, 24000 },
+ { 3, 22050 },
+ { 3, 16000 },
+ { 4, 12000 },
+ { 4, 11025 },
+ { 4, 8000 },
+};
+
+static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr,
+ unsigned int *clk_src, unsigned int *clk_out)
+{
+ unsigned int i, count, clk, div, rate;
+
+ clk = 0;
+ if (!sr)
+ return clk;
+
+ count = ARRAY_SIZE(clkref);
+ for (i = 0; i < count; i++) {
+ if (sr % clkref[i].sr)
+ continue;
+ div = sr / clkref[i].sr;
+ if ((div & (div - 1)) == 0) {
+ *clk_out = clkref[i].clk_out;
+ rate = clk_round_rate(pdm->clk, clkref[i].clk);
+ if (rate != clkref[i].clk)
+ continue;
+ clk = clkref[i].clk;
+ *clk_src = clkref[i].clk;
+ break;
+ }
+ }
+
+ if (!clk) {
+ clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE);
+ *clk_src = clk;
+ }
+ return clk;
+}
+
+static unsigned int get_pdm_ds_ratio(unsigned int sr)
+{
+ unsigned int i, count, ratio;
+
+ ratio = 0;
+ if (!sr)
+ return ratio;
+
+ count = ARRAY_SIZE(ds_ratio);
+ for (i = 0; i < count; i++) {
+ if (sr == ds_ratio[i].sr)
+ ratio = ds_ratio[i].ratio;
+ }
+ return ratio;
+}
+
+static unsigned int get_pdm_cic_ratio(unsigned int clk)
+{
+ switch (clk) {
+ case 4096000:
+ case 5644800:
+ case 6144000:
+ return 0;
+ case 2048000:
+ case 2822400:
+ case 3072000:
+ return 1;
+ case 1024000:
+ case 1411200:
+ case 1536000:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static unsigned int samplerate_to_bit(unsigned int samplerate)
+{
+ switch (samplerate) {
+ case 8000:
+ case 11025:
+ case 12000:
+ return 0;
+ case 16000:
+ case 22050:
+ case 24000:
+ return 1;
+ case 32000:
+ return 2;
+ case 44100:
+ case 48000:
+ return 3;
+ case 64000:
+ case 88200:
+ case 96000:
+ return 4;
+ case 128000:
+ case 176400:
+ case 192000:
+ return 5;
+ default:
+ return 1;
+ }
+}
+
+static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)
+{
+ return snd_soc_dai_get_drvdata(dai);
+}
+
+static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)
+{
+ if (on) {
+ regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
+ PDM_DMA_RD_MSK, PDM_DMA_RD_EN);
+ regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
+ PDM_RX_MASK, PDM_RX_START);
+ } else {
+ regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
+ PDM_DMA_RD_MSK, PDM_DMA_RD_DIS);
+ regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
+ PDM_RX_MASK | PDM_RX_CLR_MASK,
+ PDM_RX_STOP | PDM_RX_CLR_WR);
+ }
+}
+
+static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_pdm_dev *pdm = to_info(dai);
+ unsigned int val = 0;
+ unsigned int clk_rate, clk_div, samplerate;
+ unsigned int clk_src, clk_out = 0;
+ unsigned long m, n;
+ bool change;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ samplerate = params_rate(params);
+ clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out);
+ if (!clk_rate)
+ return -EINVAL;
+
+ ret = clk_set_rate(pdm->clk, clk_src);
+ if (ret)
+ return -EINVAL;
+
+ if (pdm->version == RK_PDM_RK3308 ||
+ pdm->version == RK_PDM_RV1126) {
+ rational_best_approximation(clk_out, clk_src,
+ GENMASK(16 - 1, 0),
+ GENMASK(16 - 1, 0),
+ &m, &n);
+
+ val = (m << PDM_FD_NUMERATOR_SFT) |
+ (n << PDM_FD_DENOMINATOR_SFT);
+ regmap_update_bits_check(pdm->regmap, PDM_CTRL1,
+ PDM_FD_NUMERATOR_MSK |
+ PDM_FD_DENOMINATOR_MSK,
+ val, &change);
+ if (change) {
+ reset_control_assert(pdm->reset);
+ reset_control_deassert(pdm->reset);
+ rockchip_pdm_rxctrl(pdm, 0);
+ }
+ clk_div = n / m;
+ if (clk_div >= 40)
+ val = PDM_CLK_FD_RATIO_40;
+ else if (clk_div <= 35)
+ val = PDM_CLK_FD_RATIO_35;
+ else
+ return -EINVAL;
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL,
+ PDM_CLK_FD_RATIO_MSK,
+ val);
+ }
+
+ if (pdm->version == RK_PDM_RV1126) {
+ val = get_pdm_cic_ratio(clk_out);
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val);
+ val = samplerate_to_bit(samplerate);
+ regmap_update_bits(pdm->regmap, PDM_CTRL0,
+ PDM_SAMPLERATE_MSK, PDM_SAMPLERATE(val));
+ } else {
+ val = get_pdm_ds_ratio(samplerate);
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);
+ }
+
+ regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
+ PDM_HPF_CF_MSK, PDM_HPF_60HZ);
+ regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
+ PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE);
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN);
+ if (pdm->version != RK_PDM_RK3229)
+ regmap_update_bits(pdm->regmap, PDM_CTRL0,
+ PDM_MODE_MSK, PDM_MODE_LJ);
+
+ val = 0;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ val |= PDM_VDW(8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= PDM_VDW(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= PDM_VDW(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= PDM_VDW(24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val |= PDM_VDW(32);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_channels(params)) {
+ case 8:
+ val |= PDM_PATH3_EN;
+ fallthrough;
+ case 6:
+ val |= PDM_PATH2_EN;
+ fallthrough;
+ case 4:
+ val |= PDM_PATH1_EN;
+ fallthrough;
+ case 2:
+ val |= PDM_PATH0_EN;
+ break;
+ default:
+ dev_err(pdm->dev, "invalid channel: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(pdm->regmap, PDM_CTRL0,
+ PDM_PATH_MSK | PDM_VDW_MSK,
+ val);
+ /* all channels share the single FIFO */
+ regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK,
+ PDM_DMA_RDL(8 * params_channels(params)));
+
+ return 0;
+}
+
+static int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct rk_pdm_dev *pdm = to_info(cpu_dai);
+ unsigned int mask = 0, val = 0;
+
+ mask = PDM_CKP_MSK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = PDM_CKP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = PDM_CKP_INVERTED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(cpu_dai->dev);
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val);
+ pm_runtime_put(cpu_dai->dev);
+
+ return 0;
+}
+
+static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct rk_pdm_dev *pdm = to_info(dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rockchip_pdm_rxctrl(pdm, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rockchip_pdm_rxctrl(pdm, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_pdm_dev *pdm = to_info(dai);
+
+ snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
+ .probe = rockchip_pdm_dai_probe,
+ .set_fmt = rockchip_pdm_set_fmt,
+ .trigger = rockchip_pdm_trigger,
+ .hw_params = rockchip_pdm_hw_params,
+};
+
+#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000
+#define ROCKCHIP_PDM_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 rockchip_pdm_dai = {
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ROCKCHIP_PDM_RATES,
+ .formats = ROCKCHIP_PDM_FORMATS,
+ },
+ .ops = &rockchip_pdm_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_soc_component_driver rockchip_pdm_component = {
+ .name = "rockchip-pdm",
+ .legacy_dai_naming = 1,
+};
+
+static int rockchip_pdm_runtime_suspend(struct device *dev)
+{
+ struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(pdm->clk);
+ clk_disable_unprepare(pdm->hclk);
+
+ return 0;
+}
+
+static int rockchip_pdm_runtime_resume(struct device *dev)
+{
+ struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(pdm->clk);
+ if (ret) {
+ dev_err(pdm->dev, "clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(pdm->hclk);
+ if (ret) {
+ clk_disable_unprepare(pdm->clk);
+ dev_err(pdm->dev, "hclock enable failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PDM_SYSCONFIG:
+ case PDM_CTRL0:
+ case PDM_CTRL1:
+ case PDM_CLK_CTRL:
+ case PDM_HPF_CTRL:
+ case PDM_FIFO_CTRL:
+ case PDM_DMA_CTRL:
+ case PDM_INT_EN:
+ case PDM_INT_CLR:
+ case PDM_DATA_VALID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PDM_SYSCONFIG:
+ case PDM_CTRL0:
+ case PDM_CTRL1:
+ case PDM_CLK_CTRL:
+ case PDM_HPF_CTRL:
+ case PDM_FIFO_CTRL:
+ case PDM_DMA_CTRL:
+ case PDM_INT_EN:
+ case PDM_INT_CLR:
+ case PDM_INT_ST:
+ case PDM_DATA_VALID:
+ case PDM_RXFIFO_DATA:
+ case PDM_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PDM_SYSCONFIG:
+ case PDM_FIFO_CTRL:
+ case PDM_INT_CLR:
+ case PDM_INT_ST:
+ case PDM_RXFIFO_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PDM_RXFIFO_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default rockchip_pdm_reg_defaults[] = {
+ { PDM_CTRL0, 0x78000017 },
+ { PDM_CTRL1, 0x0bb8ea60 },
+ { PDM_CLK_CTRL, 0x0000e401 },
+ { PDM_DMA_CTRL, 0x0000001f },
+};
+
+static const struct regmap_config rockchip_pdm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = PDM_VERSION,
+ .reg_defaults = rockchip_pdm_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_pdm_reg_defaults),
+ .writeable_reg = rockchip_pdm_wr_reg,
+ .readable_reg = rockchip_pdm_rd_reg,
+ .volatile_reg = rockchip_pdm_volatile_reg,
+ .precious_reg = rockchip_pdm_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id rockchip_pdm_match[] __maybe_unused = {
+ { .compatible = "rockchip,pdm",
+ .data = (void *)RK_PDM_RK3229 },
+ { .compatible = "rockchip,px30-pdm",
+ .data = (void *)RK_PDM_RK3308 },
+ { .compatible = "rockchip,rk1808-pdm",
+ .data = (void *)RK_PDM_RK3308 },
+ { .compatible = "rockchip,rk3308-pdm",
+ .data = (void *)RK_PDM_RK3308 },
+ { .compatible = "rockchip,rk3568-pdm",
+ .data = (void *)RK_PDM_RV1126 },
+ { .compatible = "rockchip,rv1126-pdm",
+ .data = (void *)RK_PDM_RV1126 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_pdm_match);
+
+static int rockchip_pdm_path_parse(struct rk_pdm_dev *pdm, struct device_node *node)
+{
+ unsigned int path[PDM_PATH_MAX];
+ int cnt = 0, ret = 0, i = 0, val = 0, msk = 0;
+
+ cnt = of_count_phandle_with_args(node, "rockchip,path-map",
+ NULL);
+ if (cnt != PDM_PATH_MAX)
+ return cnt;
+
+ ret = of_property_read_u32_array(node, "rockchip,path-map",
+ path, cnt);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < cnt; i++) {
+ if (path[i] >= PDM_PATH_MAX)
+ return -EINVAL;
+ msk |= PDM_PATH_MASK(i);
+ val |= PDM_PATH(i, path[i]);
+ }
+
+ regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, msk, val);
+
+ return 0;
+}
+
+static int rockchip_pdm_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct rk_pdm_dev *pdm;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL);
+ if (!pdm)
+ return -ENOMEM;
+
+ pdm->version = (enum rk_pdm_version)device_get_match_data(&pdev->dev);
+ if (pdm->version == RK_PDM_RK3308) {
+ pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m");
+ if (IS_ERR(pdm->reset))
+ return PTR_ERR(pdm->reset);
+ }
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &rockchip_pdm_regmap_config);
+ if (IS_ERR(pdm->regmap))
+ return PTR_ERR(pdm->regmap);
+
+ pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA;
+ pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE;
+
+ pdm->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, pdm);
+
+ pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk");
+ if (IS_ERR(pdm->clk))
+ return PTR_ERR(pdm->clk);
+
+ pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk");
+ if (IS_ERR(pdm->hclk))
+ return PTR_ERR(pdm->hclk);
+
+ ret = clk_prepare_enable(pdm->hclk);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = rockchip_pdm_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rockchip_pdm_component,
+ &rockchip_pdm_dai, 1);
+
+ if (ret) {
+ dev_err(&pdev->dev, "could not register dai: %d\n", ret);
+ goto err_suspend;
+ }
+
+ rockchip_pdm_rxctrl(pdm, 0);
+
+ ret = rockchip_pdm_path_parse(pdm, node);
+ if (ret != 0 && ret != -ENOENT)
+ goto err_suspend;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ rockchip_pdm_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ clk_disable_unprepare(pdm->hclk);
+
+ return ret;
+}
+
+static void rockchip_pdm_remove(struct platform_device *pdev)
+{
+ struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ rockchip_pdm_runtime_suspend(&pdev->dev);
+
+ clk_disable_unprepare(pdm->clk);
+ clk_disable_unprepare(pdm->hclk);
+}
+
+static int rockchip_pdm_suspend(struct device *dev)
+{
+ struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
+
+ regcache_mark_dirty(pdm->regmap);
+
+ return 0;
+}
+
+static int rockchip_pdm_resume(struct device *dev)
+{
+ struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regcache_sync(pdm->regmap);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct dev_pm_ops rockchip_pdm_pm_ops = {
+ RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend,
+ rockchip_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume)
+};
+
+static struct platform_driver rockchip_pdm_driver = {
+ .probe = rockchip_pdm_probe,
+ .remove = rockchip_pdm_remove,
+ .driver = {
+ .name = "rockchip-pdm",
+ .of_match_table = of_match_ptr(rockchip_pdm_match),
+ .pm = pm_ptr(&rockchip_pdm_pm_ops),
+ },
+};
+
+module_platform_driver(rockchip_pdm_driver);
+
+MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip PDM Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/rockchip/rockchip_pdm.h b/sound/soc/rockchip/rockchip_pdm.h
new file mode 100644
index 000000000000..cab977272ee6
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_pdm.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ */
+
+#ifndef _ROCKCHIP_PDM_H
+#define _ROCKCHIP_PDM_H
+
+/* PDM REGS */
+#define PDM_SYSCONFIG (0x0000)
+#define PDM_CTRL0 (0x0004)
+#define PDM_CTRL1 (0x0008)
+#define PDM_CLK_CTRL (0x000c)
+#define PDM_HPF_CTRL (0x0010)
+#define PDM_FIFO_CTRL (0x0014)
+#define PDM_DMA_CTRL (0x0018)
+#define PDM_INT_EN (0x001c)
+#define PDM_INT_CLR (0x0020)
+#define PDM_INT_ST (0x0024)
+#define PDM_RXFIFO_DATA (0x0030)
+#define PDM_DATA_VALID (0x0054)
+#define PDM_VERSION (0x0058)
+
+/* PDM_SYSCONFIG */
+#define PDM_RX_MASK (0x1 << 2)
+#define PDM_RX_START (0x1 << 2)
+#define PDM_RX_STOP (0x0 << 2)
+#define PDM_RX_CLR_MASK (0x1 << 0)
+#define PDM_RX_CLR_WR (0x1 << 0)
+#define PDM_RX_CLR_DONE (0x0 << 0)
+
+/* PDM CTRL0 */
+#define PDM_PATH_MSK (0xf << 27)
+#define PDM_MODE_MSK BIT(31)
+#define PDM_MODE_RJ 0
+#define PDM_MODE_LJ BIT(31)
+#define PDM_PATH3_EN BIT(30)
+#define PDM_PATH2_EN BIT(29)
+#define PDM_PATH1_EN BIT(28)
+#define PDM_PATH0_EN BIT(27)
+#define PDM_HWT_EN BIT(26)
+#define PDM_SAMPLERATE_MSK GENMASK(7, 5)
+#define PDM_SAMPLERATE(x) ((x) << 5)
+#define PDM_VDW_MSK (0x1f << 0)
+#define PDM_VDW(X) ((X - 1) << 0)
+
+/* PDM CTRL1 */
+#define PDM_FD_NUMERATOR_SFT 16
+#define PDM_FD_NUMERATOR_MSK GENMASK(31, 16)
+#define PDM_FD_DENOMINATOR_SFT 0
+#define PDM_FD_DENOMINATOR_MSK GENMASK(15, 0)
+
+/* PDM CLK CTRL */
+#define PDM_PATH_SHIFT(x) (8 + (x) * 2)
+#define PDM_PATH_MASK(x) (0x3 << PDM_PATH_SHIFT(x))
+#define PDM_PATH(x, v) ((v) << PDM_PATH_SHIFT(x))
+#define PDM_CLK_FD_RATIO_MSK BIT(6)
+#define PDM_CLK_FD_RATIO_40 (0X0 << 6)
+#define PDM_CLK_FD_RATIO_35 BIT(6)
+#define PDM_CLK_MSK BIT(5)
+#define PDM_CLK_EN BIT(5)
+#define PDM_CLK_DIS (0x0 << 5)
+#define PDM_CKP_MSK BIT(3)
+#define PDM_CKP_NORMAL (0x0 << 3)
+#define PDM_CKP_INVERTED BIT(3)
+#define PDM_DS_RATIO_MSK (0x7 << 0)
+#define PDM_CLK_320FS (0x0 << 0)
+#define PDM_CLK_640FS (0x1 << 0)
+#define PDM_CLK_1280FS (0x2 << 0)
+#define PDM_CLK_2560FS (0x3 << 0)
+#define PDM_CLK_5120FS (0x4 << 0)
+#define PDM_CIC_RATIO_MSK (0x3 << 0)
+
+/* PDM HPF CTRL */
+#define PDM_HPF_LE BIT(3)
+#define PDM_HPF_RE BIT(2)
+#define PDM_HPF_CF_MSK (0x3 << 0)
+#define PDM_HPF_3P79HZ (0x0 << 0)
+#define PDM_HPF_60HZ (0x1 << 0)
+#define PDM_HPF_243HZ (0x2 << 0)
+#define PDM_HPF_493HZ (0x3 << 0)
+
+/* PDM DMA CTRL */
+#define PDM_DMA_RD_MSK BIT(8)
+#define PDM_DMA_RD_EN BIT(8)
+#define PDM_DMA_RD_DIS (0x0 << 8)
+#define PDM_DMA_RDL_MSK (0x7f << 0)
+#define PDM_DMA_RDL(X) ((X - 1) << 0)
+
+#endif /* _ROCKCHIP_PDM_H */
diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c
index 440a8026346a..590b64b362f6 100644
--- a/sound/soc/rockchip/rockchip_rt5645.c
+++ b/sound/soc/rockchip/rockchip_rt5645.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC.
*
* Copyright (c) 2015, ROCKCHIP CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
- *
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/jack.h>
@@ -29,15 +15,21 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "rockchip_i2s.h"
+#include "../codecs/rt5645.h"
#define DRV_NAME "rockchip-snd-rt5645"
static struct snd_soc_jack headset_jack;
-
-/* Jack detect via rt5645 driver. */
-extern int rt5645_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
- struct snd_soc_jack *btn_jack);
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
@@ -71,9 +63,9 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int mclk;
switch (params_rate(params)) {
@@ -119,35 +111,42 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime)
int ret;
/* Enable Headset and 4 Buttons Jack detection */
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &headset_jack, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
if (ret) {
dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
return ret;
}
- return rt5645_set_jack_detect(runtime->codec,
+ return rt5645_set_jack_detect(snd_soc_rtd_to_codec(runtime, 0)->component,
&headset_jack,
&headset_jack,
&headset_jack);
}
-static struct snd_soc_ops rk_aif1_ops = {
+static const struct snd_soc_ops rk_aif1_ops = {
.hw_params = rk_aif1_hw_params,
};
+SND_SOC_DAILINK_DEFS(pcm,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
static struct snd_soc_dai_link rk_dailink = {
.name = "rt5645",
.stream_name = "rt5645 PCM",
- .codec_dai_name = "rt5645-aif1",
.init = rk_init,
.ops = &rk_aif1_ops,
/* set rt5645 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card snd_soc_card_rk = {
@@ -172,39 +171,57 @@ static int snd_rk_mc_probe(struct platform_device *pdev)
/* register the soc card */
card->dev = &pdev->dev;
- rk_dailink.codec_of_node = of_parse_phandle(np,
+ rk_dailink.codecs->of_node = of_parse_phandle(np,
"rockchip,audio-codec", 0);
- if (!rk_dailink.codec_of_node) {
+ if (!rk_dailink.codecs->of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,audio-codec' missing or invalid\n");
return -EINVAL;
}
- rk_dailink.cpu_of_node = of_parse_phandle(np,
+ rk_dailink.cpus->of_node = of_parse_phandle(np,
"rockchip,i2s-controller", 0);
- if (!rk_dailink.cpu_of_node) {
+ if (!rk_dailink.cpus->of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,i2s-controller' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_codec_of_node;
}
- rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
+ rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
ret = snd_soc_of_parse_card_name(card, "rockchip,model");
if (ret) {
dev_err(&pdev->dev,
"Soc parse card name failed %d\n", ret);
- return ret;
+ goto put_cpu_of_node;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
"Soc register card failed %d\n", ret);
- return ret;
+ goto put_cpu_of_node;
}
return ret;
+
+put_cpu_of_node:
+ of_node_put(rk_dailink.cpus->of_node);
+ rk_dailink.cpus->of_node = NULL;
+put_codec_of_node:
+ of_node_put(rk_dailink.codecs->of_node);
+ rk_dailink.codecs->of_node = NULL;
+
+ return ret;
+}
+
+static void snd_rk_mc_remove(struct platform_device *pdev)
+{
+ of_node_put(rk_dailink.cpus->of_node);
+ rk_dailink.cpus->of_node = NULL;
+ of_node_put(rk_dailink.codecs->of_node);
+ rk_dailink.codecs->of_node = NULL;
}
static const struct of_device_id rockchip_rt5645_of_match[] = {
@@ -216,6 +233,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
static struct platform_driver snd_rk_mc_driver = {
.probe = snd_rk_mc_probe,
+ .remove = snd_rk_mc_remove,
.driver = {
.name = DRV_NAME,
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c
new file mode 100644
index 000000000000..ebdf0056065b
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_sai.c
@@ -0,0 +1,1529 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA SoC Audio Layer - Rockchip SAI Controller driver
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ * Copyright (c) 2025 Collabora Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/tlv.h>
+
+#include "rockchip_sai.h"
+
+#define DRV_NAME "rockchip-sai"
+
+#define CLK_SHIFT_RATE_HZ_MAX 5
+#define FW_RATIO_MAX 8
+#define FW_RATIO_MIN 1
+#define MAXBURST_PER_FIFO 8
+
+#define TIMEOUT_US 1000
+#define WAIT_TIME_MS_MAX 10000
+
+#define MAX_LANES 4
+
+enum fpw_mode {
+ FPW_ONE_BCLK_WIDTH,
+ FPW_ONE_SLOT_WIDTH,
+ FPW_HALF_FRAME_WIDTH,
+};
+
+struct rk_sai_dev {
+ struct device *dev;
+ struct clk *hclk;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct reset_control *rst_h;
+ struct reset_control *rst_m;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
+ unsigned int mclk_rate;
+ unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1];
+ unsigned int tx_lanes;
+ unsigned int rx_lanes;
+ unsigned int sdi[MAX_LANES];
+ unsigned int sdo[MAX_LANES];
+ unsigned int version;
+ enum fpw_mode fpw;
+ int fw_ratio;
+ bool has_capture;
+ bool has_playback;
+ bool is_master_mode;
+ bool is_tdm;
+ bool initialized;
+ /* protects register writes that depend on the state of XFER[1:0] */
+ spinlock_t xfer_lock;
+};
+
+static bool rockchip_sai_stream_valid(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ if (!substream)
+ return false;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ sai->has_playback)
+ return true;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ sai->has_capture)
+ return true;
+
+ return false;
+}
+
+static int rockchip_sai_fsync_lost_detect(struct rk_sai_dev *sai, bool en)
+{
+ unsigned int fw, cnt;
+
+ if (sai->is_master_mode || sai->version < SAI_VER_2311)
+ return 0;
+
+ regmap_read(sai->regmap, SAI_FSCR, &fw);
+ cnt = SAI_FSCR_FW_V(fw) << 1; /* two fsync lost */
+
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOST_MASK,
+ SAI_INTCR_FSLOST(en));
+ /*
+ * The `cnt` is the number of SCLK cycles of the CRU's SCLK signal that
+ * should be used as timeout. Consequently, in slave mode, this value
+ * is only correct if the CRU SCLK is equal to the external SCLK.
+ */
+ regmap_update_bits(sai->regmap, SAI_FS_TIMEOUT,
+ SAI_FS_TIMEOUT_VAL_MASK | SAI_FS_TIMEOUT_EN_MASK,
+ SAI_FS_TIMEOUT_VAL(cnt) | SAI_FS_TIMEOUT_EN(en));
+
+ return 0;
+}
+
+static int rockchip_sai_fsync_err_detect(struct rk_sai_dev *sai,
+ bool en)
+{
+ if (sai->is_master_mode || sai->version < SAI_VER_2311)
+ return 0;
+
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERRC, SAI_INTCR_FSERRC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERR_MASK,
+ SAI_INTCR_FSERR(en));
+
+ return 0;
+}
+
+static int rockchip_sai_poll_clk_idle(struct rk_sai_dev *sai)
+{
+ unsigned int reg, idle, val;
+ int ret;
+
+ if (sai->version >= SAI_VER_2307) {
+ reg = SAI_STATUS;
+ idle = SAI_STATUS_FS_IDLE;
+ idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle;
+ } else {
+ reg = SAI_XFER;
+ idle = SAI_XFER_FS_IDLE;
+ }
+
+ ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val,
+ (val & idle), 10, TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(sai->dev, "Failed to idle FS\n");
+
+ return ret;
+}
+
+static int rockchip_sai_poll_stream_idle(struct rk_sai_dev *sai, bool playback, bool capture)
+{
+ unsigned int reg, val;
+ unsigned int idle = 0;
+ int ret;
+
+ if (sai->version >= SAI_VER_2307) {
+ reg = SAI_STATUS;
+ if (playback)
+ idle |= SAI_STATUS_TX_IDLE;
+ if (capture)
+ idle |= SAI_STATUS_RX_IDLE;
+ idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle;
+ } else {
+ reg = SAI_XFER;
+ if (playback)
+ idle |= SAI_XFER_TX_IDLE;
+ if (capture)
+ idle |= SAI_XFER_RX_IDLE;
+ }
+
+ ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val,
+ (val & idle), 10, TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(sai->dev, "Failed to idle stream\n");
+
+ return ret;
+}
+
+/**
+ * rockchip_sai_xfer_clk_stop_and_wait() - stop the xfer clock and wait for it to be idle
+ * @sai: pointer to the driver instance's rk_sai_dev struct
+ * @to_restore: pointer to store the CLK/FSS register values in as they were
+ * found before they were cleared, or NULL.
+ *
+ * Clear the XFER_CLK and XFER_FSS registers if needed, then busy-waits for the
+ * XFER clocks to be idle. Before clearing the bits, it stores the state of the
+ * registers as it encountered them in to_restore if it isn't NULL.
+ *
+ * Context: Any context. Expects sai->xfer_lock to be held by caller.
+ */
+static void rockchip_sai_xfer_clk_stop_and_wait(struct rk_sai_dev *sai, unsigned int *to_restore)
+{
+ unsigned int mask = SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK;
+ unsigned int disable = SAI_XFER_CLK_DIS | SAI_XFER_FSS_DIS;
+ unsigned int val;
+
+ assert_spin_locked(&sai->xfer_lock);
+
+ regmap_read(sai->regmap, SAI_XFER, &val);
+ if ((val & mask) == disable)
+ goto wait_for_idle;
+
+ if (sai->is_master_mode)
+ regmap_update_bits(sai->regmap, SAI_XFER, mask, disable);
+
+wait_for_idle:
+ rockchip_sai_poll_clk_idle(sai);
+
+ if (to_restore)
+ *to_restore = val;
+}
+
+static int rockchip_sai_runtime_suspend(struct device *dev)
+{
+ struct rk_sai_dev *sai = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ rockchip_sai_fsync_lost_detect(sai, 0);
+ rockchip_sai_fsync_err_detect(sai, 0);
+
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, NULL);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+
+ regcache_cache_only(sai->regmap, true);
+ /*
+ * After FS is idle, we should wait at least 2 BCLK cycles to make sure
+ * the CLK gate operation has completed, and only then disable mclk.
+ *
+ * Otherwise, the BCLK is still ungated, and once the mclk is enabled,
+ * there is a risk that a few BCLK cycles leak. This is true especially
+ * at low speeds, such as with a samplerate of 8k.
+ *
+ * Ideally we'd adjust the delay based on the samplerate, but it's such
+ * a tiny value that we can just delay for the maximum clock period
+ * for the sake of simplicity.
+ *
+ * The maximum BCLK period is 31us @ 8K-8Bit (64kHz BCLK). We wait for
+ * 40us to give ourselves a safety margin in case udelay falls short.
+ */
+ udelay(40);
+ clk_disable_unprepare(sai->mclk);
+ clk_disable_unprepare(sai->hclk);
+
+ return 0;
+}
+
+static int rockchip_sai_runtime_resume(struct device *dev)
+{
+ struct rk_sai_dev *sai = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(sai->hclk);
+ if (ret)
+ goto err_hclk;
+
+ ret = clk_prepare_enable(sai->mclk);
+ if (ret)
+ goto err_mclk;
+
+ regcache_cache_only(sai->regmap, false);
+ regcache_mark_dirty(sai->regmap);
+ ret = regcache_sync(sai->regmap);
+ if (ret)
+ goto err_regmap;
+
+ return 0;
+
+err_regmap:
+ clk_disable_unprepare(sai->mclk);
+err_mclk:
+ clk_disable_unprepare(sai->hclk);
+err_hclk:
+ return ret;
+}
+
+static void rockchip_sai_fifo_xrun_detect(struct rk_sai_dev *sai,
+ int stream, bool en)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* clear irq status which was asserted before TXUIE enabled */
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIC, SAI_INTCR_TXUIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIE_MASK,
+ SAI_INTCR_TXUIE(en));
+ } else {
+ /* clear irq status which was asserted before RXOIE enabled */
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIC, SAI_INTCR_RXOIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIE_MASK,
+ SAI_INTCR_RXOIE(en));
+ }
+}
+
+static void rockchip_sai_dma_ctrl(struct rk_sai_dev *sai,
+ int stream, bool en)
+{
+ if (!en)
+ rockchip_sai_fifo_xrun_detect(sai, stream, 0);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(sai->regmap, SAI_DMACR,
+ SAI_DMACR_TDE_MASK,
+ SAI_DMACR_TDE(en));
+ } else {
+ regmap_update_bits(sai->regmap, SAI_DMACR,
+ SAI_DMACR_RDE_MASK,
+ SAI_DMACR_RDE(en));
+ }
+
+ if (en)
+ rockchip_sai_fifo_xrun_detect(sai, stream, 1);
+}
+
+static void rockchip_sai_reset(struct rk_sai_dev *sai)
+{
+ /*
+ * It is advised to reset the hclk domain before resetting the mclk
+ * domain, especially in slave mode without a clock input.
+ *
+ * To deal with the aforementioned case of slave mode without a clock
+ * input, we work around a potential issue by resetting the whole
+ * controller, bringing it back into master mode, and then recovering
+ * the controller configuration in the regmap.
+ */
+ reset_control_assert(sai->rst_h);
+ udelay(10);
+ reset_control_deassert(sai->rst_h);
+ udelay(10);
+ reset_control_assert(sai->rst_m);
+ udelay(10);
+ reset_control_deassert(sai->rst_m);
+ udelay(10);
+
+ /* recover regmap config */
+ regcache_mark_dirty(sai->regmap);
+ regcache_sync(sai->regmap);
+}
+
+static int rockchip_sai_clear(struct rk_sai_dev *sai, unsigned int clr)
+{
+ unsigned int val = 0;
+ int ret = 0;
+
+ regmap_update_bits(sai->regmap, SAI_CLR, clr, clr);
+ ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_CLR, val,
+ !(val & clr), 10, TIMEOUT_US);
+ if (ret < 0) {
+ dev_warn(sai->dev, "Failed to clear %u\n", clr);
+ rockchip_sai_reset(sai);
+ }
+
+ return ret;
+}
+
+static void rockchip_sai_xfer_start(struct rk_sai_dev *sai, int stream)
+{
+ unsigned int msk, val;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msk = SAI_XFER_TXS_MASK;
+ val = SAI_XFER_TXS_EN;
+
+ } else {
+ msk = SAI_XFER_RXS_MASK;
+ val = SAI_XFER_RXS_EN;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_XFER, msk, val);
+}
+
+static void rockchip_sai_xfer_stop(struct rk_sai_dev *sai, int stream)
+{
+ unsigned int msk = 0, val = 0, clr = 0;
+ bool capture = stream == SNDRV_PCM_STREAM_CAPTURE || stream < 0;
+ bool playback = stream == SNDRV_PCM_STREAM_PLAYBACK || stream < 0;
+ /* could be <= 0 but we don't want to depend on enum values */
+
+ if (playback) {
+ msk |= SAI_XFER_TXS_MASK;
+ val |= SAI_XFER_TXS_DIS;
+ clr |= SAI_CLR_TXC;
+ }
+ if (capture) {
+ msk |= SAI_XFER_RXS_MASK;
+ val |= SAI_XFER_RXS_DIS;
+ clr |= SAI_CLR_RXC;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_XFER, msk, val);
+ rockchip_sai_poll_stream_idle(sai, playback, capture);
+
+ rockchip_sai_clear(sai, clr);
+}
+
+static void rockchip_sai_start(struct rk_sai_dev *sai, int stream)
+{
+ rockchip_sai_dma_ctrl(sai, stream, 1);
+ rockchip_sai_xfer_start(sai, stream);
+}
+
+static void rockchip_sai_stop(struct rk_sai_dev *sai, int stream)
+{
+ rockchip_sai_dma_ctrl(sai, stream, 0);
+ rockchip_sai_xfer_stop(sai, stream);
+}
+
+static void rockchip_sai_fmt_create(struct rk_sai_dev *sai, unsigned int fmt)
+{
+ unsigned int xcr_mask = 0, xcr_val = 0, xsft_mask = 0, xsft_val = 0;
+ unsigned int fscr_mask = 0, fscr_val = 0;
+
+ assert_spin_locked(&sai->xfer_lock);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_R | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(0);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_DUAL;
+ sai->fpw = FPW_HALF_FRAME_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(0);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_DUAL;
+ sai->fpw = FPW_HALF_FRAME_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_1;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ if (sai->is_tdm)
+ xsft_val = SAI_XSHIFT_RIGHT(1);
+ else
+ xsft_val = SAI_XSHIFT_RIGHT(2);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_DUAL;
+ sai->fpw = FPW_HALF_FRAME_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(2);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_RISING;
+ sai->fpw = FPW_ONE_BCLK_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(0);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_RISING;
+ sai->fpw = FPW_ONE_BCLK_WIDTH;
+ break;
+ default:
+ dev_err(sai->dev, "Unsupported fmt %u\n", fmt);
+ break;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_TXCR, xcr_mask, xcr_val);
+ regmap_update_bits(sai->regmap, SAI_RXCR, xcr_mask, xcr_val);
+ regmap_update_bits(sai->regmap, SAI_TX_SHIFT, xsft_mask, xsft_val);
+ regmap_update_bits(sai->regmap, SAI_RX_SHIFT, xsft_mask, xsft_val);
+ regmap_update_bits(sai->regmap, SAI_FSCR, fscr_mask, fscr_val);
+}
+
+static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask = 0, val = 0;
+ unsigned int clk_gates;
+ unsigned long flags;
+ int ret = 0;
+
+ pm_runtime_get_sync(dai->dev);
+
+ mask = SAI_CKR_MSS_MASK;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ val = SAI_CKR_MSS_MASTER;
+ sai->is_master_mode = true;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ val = SAI_CKR_MSS_SLAVE;
+ sai->is_master_mode = false;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
+ if (sai->initialized) {
+ if (sai->has_capture && sai->has_playback)
+ rockchip_sai_xfer_stop(sai, -1);
+ else if (sai->has_capture)
+ rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE);
+ else
+ rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK);
+ } else {
+ rockchip_sai_clear(sai, 0);
+ sai->initialized = true;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
+
+ mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
+
+ rockchip_sai_fmt_create(sai, fmt);
+
+err_xfer_unlock:
+ if (clk_gates)
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
+ clk_gates);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+err_pm_put:
+ pm_runtime_put(dai->dev);
+
+ return ret;
+}
+
+static int rockchip_sai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ unsigned int mclk_rate, mclk_req_rate, bclk_rate, div_bclk;
+ unsigned int ch_per_lane, slot_width;
+ unsigned int val, fscr, reg;
+ unsigned int lanes, req_lanes;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+ dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2;
+
+ pm_runtime_get_sync(sai->dev);
+
+ regmap_read(sai->regmap, SAI_DMACR, &val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg = SAI_TXCR;
+ lanes = sai->tx_lanes;
+ } else {
+ reg = SAI_RXCR;
+ lanes = sai->rx_lanes;
+ }
+
+ if (!sai->is_tdm) {
+ req_lanes = DIV_ROUND_UP(params_channels(params), 2);
+ if (lanes < req_lanes) {
+ dev_err(sai->dev, "not enough lanes (%d) for requested number of %s channels (%d)\n",
+ lanes, reg == SAI_TXCR ? "playback" : "capture",
+ params_channels(params));
+ ret = -EINVAL;
+ goto err_pm_put;
+ } else {
+ lanes = req_lanes;
+ }
+ }
+
+ dev_dbg(sai->dev, "using %d lanes totalling %d%s channels for %s\n",
+ lanes, params_channels(params), sai->is_tdm ? " (TDM)" : "",
+ reg == SAI_TXCR ? "playback" : "capture");
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ val = SAI_XCR_VDW(8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = SAI_XCR_VDW(16);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = SAI_XCR_VDW(24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ val = SAI_XCR_VDW(32);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ val |= SAI_XCR_CSR(lanes);
+
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val);
+
+ regmap_read(sai->regmap, reg, &val);
+
+ slot_width = SAI_XCR_SBW_V(val);
+ ch_per_lane = params_channels(params) / lanes;
+
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK,
+ SAI_XCR_SNB(ch_per_lane));
+
+ fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane);
+
+ switch (sai->fpw) {
+ case FPW_ONE_BCLK_WIDTH:
+ fscr |= SAI_FSCR_FPW(1);
+ break;
+ case FPW_ONE_SLOT_WIDTH:
+ fscr |= SAI_FSCR_FPW(slot_width);
+ break;
+ case FPW_HALF_FRAME_WIDTH:
+ fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2);
+ break;
+ default:
+ dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_FSCR,
+ SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr);
+
+ if (sai->is_master_mode) {
+ bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params);
+ ret = clk_set_rate(sai->mclk, sai->mclk_rate);
+ if (ret) {
+ dev_err(sai->dev, "Failed to set mclk to %u: %pe\n",
+ sai->mclk_rate, ERR_PTR(ret));
+ goto err_xfer_unlock;
+ }
+
+ mclk_rate = clk_get_rate(sai->mclk);
+ if (mclk_rate < bclk_rate) {
+ dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n",
+ mclk_rate, bclk_rate);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+ mclk_req_rate = bclk_rate * div_bclk;
+
+ if (mclk_rate < mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX ||
+ mclk_rate > mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) {
+ dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n",
+ mclk_rate, mclk_req_rate, CLK_SHIFT_RATE_HZ_MAX);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK,
+ SAI_CKR_MDIV(div_bclk));
+ }
+
+err_xfer_unlock:
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+err_pm_put:
+ pm_runtime_put(sai->dev);
+
+ return ret;
+}
+
+static int rockchip_sai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ if (sai->is_master_mode) {
+ /*
+ * We should wait for the first BCLK pulse to have definitely
+ * occurred after any DIV settings have potentially been
+ * changed in order to guarantee a clean clock signal once we
+ * ungate the clock.
+ *
+ * Ideally, this would be done depending on the samplerate, but
+ * for the sake of simplicity, we'll just delay for the maximum
+ * possible clock offset time, which is quite a small value.
+ *
+ * The maximum BCLK offset is 15.6us @ 8K-8Bit (64kHz BCLK). We
+ * wait for 20us in order to give us a safety margin in case
+ * udelay falls short.
+ */
+ udelay(20);
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK |
+ SAI_XFER_FSS_MASK,
+ SAI_XFER_CLK_EN |
+ SAI_XFER_FSS_EN);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+ }
+
+ rockchip_sai_fsync_lost_detect(sai, 1);
+ rockchip_sai_fsync_err_detect(sai, 1);
+
+ return 0;
+}
+
+static void rockchip_sai_path_config(struct rk_sai_dev *sai,
+ int num, bool is_rx)
+{
+ int i;
+
+ if (is_rx)
+ for (i = 0; i < num; i++)
+ regmap_update_bits(sai->regmap, SAI_PATH_SEL,
+ SAI_RX_PATH_MASK(i),
+ SAI_RX_PATH(i, sai->sdi[i]));
+ else
+ for (i = 0; i < num; i++)
+ regmap_update_bits(sai->regmap, SAI_PATH_SEL,
+ SAI_TX_PATH_MASK(i),
+ SAI_TX_PATH(i, sai->sdo[i]));
+}
+
+static int rockchip_sai_path_prepare(struct rk_sai_dev *sai,
+ struct device_node *np,
+ bool is_rx)
+{
+ const char *path_prop;
+ unsigned int *data;
+ unsigned int *lanes;
+ int i, num, ret;
+
+ if (is_rx) {
+ path_prop = "rockchip,sai-rx-route";
+ data = sai->sdi;
+ lanes = &sai->rx_lanes;
+ } else {
+ path_prop = "rockchip,sai-tx-route";
+ data = sai->sdo;
+ lanes = &sai->tx_lanes;
+ }
+
+ num = of_count_phandle_with_args(np, path_prop, NULL);
+ if (num == -ENOENT) {
+ return 0;
+ } else if (num > MAX_LANES || num == 0) {
+ dev_err(sai->dev, "found %d entries in %s, outside of range 1 to %d\n",
+ num, path_prop, MAX_LANES);
+ return -EINVAL;
+ } else if (num < 0) {
+ dev_err(sai->dev, "error in %s property: %pe\n", path_prop,
+ ERR_PTR(num));
+ return num;
+ }
+
+ *lanes = num;
+ ret = device_property_read_u32_array(sai->dev, path_prop, data, num);
+ if (ret < 0) {
+ dev_err(sai->dev, "failed to read property '%s': %pe\n",
+ path_prop, ERR_PTR(ret));
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (data[i] >= MAX_LANES) {
+ dev_err(sai->dev, "%s[%d] is %d, should be less than %d\n",
+ path_prop, i, data[i], MAX_LANES);
+ return -EINVAL;
+ }
+ }
+
+ rockchip_sai_path_config(sai, num, is_rx);
+
+ return 0;
+}
+
+static int rockchip_sai_parse_paths(struct rk_sai_dev *sai,
+ struct device_node *np)
+{
+ int ret;
+
+ if (sai->has_playback) {
+ sai->tx_lanes = 1;
+ ret = rockchip_sai_path_prepare(sai, np, false);
+ if (ret < 0) {
+ dev_err(sai->dev, "Failed to prepare TX path: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+
+ if (sai->has_capture) {
+ sai->rx_lanes = 1;
+ ret = rockchip_sai_path_prepare(sai, np, true);
+ if (ret < 0) {
+ dev_err(sai->dev, "Failed to prepare RX path: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rockchip_sai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rockchip_sai_start(sai, substream->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ rockchip_sai_stop(sai, substream->stream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int rockchip_sai_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai,
+ sai->has_playback ? &sai->playback_dma_data : NULL,
+ sai->has_capture ? &sai->capture_dma_data : NULL);
+
+ return 0;
+}
+
+static int rockchip_sai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ int stream = substream->stream;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ if (sai->substreams[stream])
+ return -EBUSY;
+
+ if (sai->wait_time[stream])
+ substream->wait_time = sai->wait_time[stream];
+
+ sai->substreams[stream] = substream;
+
+ return 0;
+}
+
+static void rockchip_sai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return;
+
+ sai->substreams[substream->stream] = NULL;
+}
+
+static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
+ unsigned int clk_gates;
+ int sw = slot_width;
+
+ if (!slots) {
+ /* Disabling TDM, set slot width back to 32 bits */
+ sai->is_tdm = false;
+ sw = 32;
+ } else {
+ sai->is_tdm = true;
+ }
+
+ if (sw < 16 || sw > 32)
+ return -EINVAL;
+
+ pm_runtime_get_sync(dai->dev);
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
+ regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(sw));
+ regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(sw));
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
+ clk_gates);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+ pm_runtime_put(dai->dev);
+
+ return 0;
+}
+
+static int rockchip_sai_set_sysclk(struct snd_soc_dai *dai, int stream,
+ unsigned int freq, int dir)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ sai->mclk_rate = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rockchip_sai_dai_ops = {
+ .probe = rockchip_sai_dai_probe,
+ .startup = rockchip_sai_startup,
+ .shutdown = rockchip_sai_shutdown,
+ .hw_params = rockchip_sai_hw_params,
+ .set_fmt = rockchip_sai_set_fmt,
+ .set_sysclk = rockchip_sai_set_sysclk,
+ .prepare = rockchip_sai_prepare,
+ .trigger = rockchip_sai_trigger,
+ .set_tdm_slot = rockchip_sai_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_driver rockchip_sai_dai = {
+ .ops = &rockchip_sai_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static bool rockchip_sai_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_TXCR:
+ case SAI_FSCR:
+ case SAI_RXCR:
+ case SAI_MONO_CR:
+ case SAI_XFER:
+ case SAI_CLR:
+ case SAI_CKR:
+ case SAI_DMACR:
+ case SAI_INTCR:
+ case SAI_TXDR:
+ case SAI_PATH_SEL:
+ case SAI_TX_SLOT_MASK0:
+ case SAI_TX_SLOT_MASK1:
+ case SAI_TX_SLOT_MASK2:
+ case SAI_TX_SLOT_MASK3:
+ case SAI_RX_SLOT_MASK0:
+ case SAI_RX_SLOT_MASK1:
+ case SAI_RX_SLOT_MASK2:
+ case SAI_RX_SLOT_MASK3:
+ case SAI_TX_SHIFT:
+ case SAI_RX_SHIFT:
+ case SAI_FSXN:
+ case SAI_FS_TIMEOUT:
+ case SAI_LOOPBACK_LR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_sai_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_TXCR:
+ case SAI_FSCR:
+ case SAI_RXCR:
+ case SAI_MONO_CR:
+ case SAI_XFER:
+ case SAI_CLR:
+ case SAI_CKR:
+ case SAI_TXFIFOLR:
+ case SAI_RXFIFOLR:
+ case SAI_DMACR:
+ case SAI_INTCR:
+ case SAI_INTSR:
+ case SAI_TXDR:
+ case SAI_RXDR:
+ case SAI_PATH_SEL:
+ case SAI_TX_SLOT_MASK0:
+ case SAI_TX_SLOT_MASK1:
+ case SAI_TX_SLOT_MASK2:
+ case SAI_TX_SLOT_MASK3:
+ case SAI_RX_SLOT_MASK0:
+ case SAI_RX_SLOT_MASK1:
+ case SAI_RX_SLOT_MASK2:
+ case SAI_RX_SLOT_MASK3:
+ case SAI_TX_DATA_CNT:
+ case SAI_RX_DATA_CNT:
+ case SAI_TX_SHIFT:
+ case SAI_RX_SHIFT:
+ case SAI_STATUS:
+ case SAI_VERSION:
+ case SAI_FSXN:
+ case SAI_FS_TIMEOUT:
+ case SAI_LOOPBACK_LR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_sai_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_XFER:
+ case SAI_INTCR:
+ case SAI_INTSR:
+ case SAI_CLR:
+ case SAI_TXFIFOLR:
+ case SAI_RXFIFOLR:
+ case SAI_TXDR:
+ case SAI_RXDR:
+ case SAI_TX_DATA_CNT:
+ case SAI_RX_DATA_CNT:
+ case SAI_STATUS:
+ case SAI_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_sai_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_RXDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default rockchip_sai_reg_defaults[] = {
+ { SAI_TXCR, 0x00000bff },
+ { SAI_FSCR, 0x0001f03f },
+ { SAI_RXCR, 0x00000bff },
+ { SAI_PATH_SEL, 0x0000e4e4 },
+};
+
+static const struct regmap_config rockchip_sai_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SAI_LOOPBACK_LR,
+ .reg_defaults = rockchip_sai_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_sai_reg_defaults),
+ .writeable_reg = rockchip_sai_wr_reg,
+ .readable_reg = rockchip_sai_rd_reg,
+ .volatile_reg = rockchip_sai_volatile_reg,
+ .precious_reg = rockchip_sai_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rockchip_sai_init_dai(struct rk_sai_dev *sai, struct resource *res,
+ struct snd_soc_dai_driver **dp)
+{
+ struct device_node *node = sai->dev->of_node;
+ struct snd_soc_dai_driver *dai;
+ struct property *dma_names;
+ const char *dma_name;
+
+ of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
+ if (!strcmp(dma_name, "tx"))
+ sai->has_playback = true;
+ if (!strcmp(dma_name, "rx"))
+ sai->has_capture = true;
+ }
+
+ dai = devm_kmemdup(sai->dev, &rockchip_sai_dai,
+ sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ if (sai->has_playback) {
+ dai->playback.stream_name = "Playback";
+ dai->playback.channels_min = 1;
+ dai->playback.channels_max = 512;
+ dai->playback.rates = SNDRV_PCM_RATE_8000_384000;
+ dai->playback.formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+
+ sai->playback_dma_data.addr = res->start + SAI_TXDR;
+ sai->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ sai->playback_dma_data.maxburst = MAXBURST_PER_FIFO;
+ }
+
+ if (sai->has_capture) {
+ dai->capture.stream_name = "Capture";
+ dai->capture.channels_min = 1;
+ dai->capture.channels_max = 512;
+ dai->capture.rates = SNDRV_PCM_RATE_8000_384000;
+ dai->capture.formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+
+ sai->capture_dma_data.addr = res->start + SAI_RXDR;
+ sai->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ sai->capture_dma_data.maxburst = MAXBURST_PER_FIFO;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_TDL_MASK,
+ SAI_DMACR_TDL(16));
+ regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_RDL_MASK,
+ SAI_DMACR_RDL(16));
+
+ if (dp)
+ *dp = dai;
+
+ return 0;
+}
+
+static const char * const mono_text[] = { "Disable", "Enable" };
+
+static DECLARE_TLV_DB_SCALE(rmss_tlv, 0, 128, 0);
+
+static const char * const lplrc_text[] = { "L:MIC R:LP", "L:LP R:MIC" };
+static const char * const lplr_text[] = { "Disable", "Enable" };
+
+static const char * const lpx_text[] = {
+ "From SDO0", "From SDO1", "From SDO2", "From SDO3" };
+
+static const char * const lps_text[] = { "Disable", "Enable" };
+static const char * const sync_out_text[] = { "From CRU", "From IO" };
+static const char * const sync_in_text[] = { "From IO", "From Sync Port" };
+
+static const char * const rpaths_text[] = {
+ "From SDI0", "From SDI1", "From SDI2", "From SDI3" };
+
+static const char * const tpaths_text[] = {
+ "From PATH0", "From PATH1", "From PATH2", "From PATH3" };
+
+/* MONO_CR */
+static SOC_ENUM_SINGLE_DECL(rmono_switch, SAI_MONO_CR, 1, mono_text);
+static SOC_ENUM_SINGLE_DECL(tmono_switch, SAI_MONO_CR, 0, mono_text);
+
+/* PATH_SEL */
+static SOC_ENUM_SINGLE_DECL(lp3_enum, SAI_PATH_SEL, 28, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp2_enum, SAI_PATH_SEL, 26, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp1_enum, SAI_PATH_SEL, 24, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp0_enum, SAI_PATH_SEL, 22, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp3_switch, SAI_PATH_SEL, 21, lps_text);
+static SOC_ENUM_SINGLE_DECL(lp2_switch, SAI_PATH_SEL, 20, lps_text);
+static SOC_ENUM_SINGLE_DECL(lp1_switch, SAI_PATH_SEL, 19, lps_text);
+static SOC_ENUM_SINGLE_DECL(lp0_switch, SAI_PATH_SEL, 18, lps_text);
+static SOC_ENUM_SINGLE_DECL(sync_out_switch, SAI_PATH_SEL, 17, sync_out_text);
+static SOC_ENUM_SINGLE_DECL(sync_in_switch, SAI_PATH_SEL, 16, sync_in_text);
+static SOC_ENUM_SINGLE_DECL(rpath3_enum, SAI_PATH_SEL, 14, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath2_enum, SAI_PATH_SEL, 12, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath1_enum, SAI_PATH_SEL, 10, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath0_enum, SAI_PATH_SEL, 8, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath3_enum, SAI_PATH_SEL, 6, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath2_enum, SAI_PATH_SEL, 4, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath1_enum, SAI_PATH_SEL, 2, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath0_enum, SAI_PATH_SEL, 0, tpaths_text);
+
+/* LOOPBACK_LR */
+static SOC_ENUM_SINGLE_DECL(lp3lrc_enum, SAI_LOOPBACK_LR, 7, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp2lrc_enum, SAI_LOOPBACK_LR, 6, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp1lrc_enum, SAI_LOOPBACK_LR, 5, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp0lrc_enum, SAI_LOOPBACK_LR, 4, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp3lr_switch, SAI_LOOPBACK_LR, 3, lplr_text);
+static SOC_ENUM_SINGLE_DECL(lp2lr_switch, SAI_LOOPBACK_LR, 2, lplr_text);
+static SOC_ENUM_SINGLE_DECL(lp1lr_switch, SAI_LOOPBACK_LR, 1, lplr_text);
+static SOC_ENUM_SINGLE_DECL(lp0lr_switch, SAI_LOOPBACK_LR, 0, lplr_text);
+
+static int rockchip_sai_wait_time_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = WAIT_TIME_MS_MAX;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int rockchip_sai_rd_wait_time_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
+static int rockchip_sai_rd_wait_time_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+ return -EINVAL;
+
+ sai->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int rockchip_sai_wr_wait_time_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK];
+
+ return 0;
+}
+
+static int rockchip_sai_wr_wait_time_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+ return -EINVAL;
+
+ sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, \
+ .info = rockchip_sai_wait_time_info, \
+ .get = xhandler_get, .put = xhandler_put }
+
+static const struct snd_kcontrol_new rockchip_sai_controls[] = {
+ SOC_SINGLE_TLV("Receive Mono Slot Select", SAI_MONO_CR,
+ 2, 128, 0, rmss_tlv),
+ SOC_ENUM("Receive Mono Switch", rmono_switch),
+ SOC_ENUM("Transmit Mono Switch", tmono_switch),
+
+ SOC_ENUM("SDI3 Loopback I2S LR Channel Sel", lp3lrc_enum),
+ SOC_ENUM("SDI2 Loopback I2S LR Channel Sel", lp2lrc_enum),
+ SOC_ENUM("SDI1 Loopback I2S LR Channel Sel", lp1lrc_enum),
+ SOC_ENUM("SDI0 Loopback I2S LR Channel Sel", lp0lrc_enum),
+ SOC_ENUM("SDI3 Loopback I2S LR Switch", lp3lr_switch),
+ SOC_ENUM("SDI2 Loopback I2S LR Switch", lp2lr_switch),
+ SOC_ENUM("SDI1 Loopback I2S LR Switch", lp1lr_switch),
+ SOC_ENUM("SDI0 Loopback I2S LR Switch", lp0lr_switch),
+
+ SOC_ENUM("SDI3 Loopback Src Select", lp3_enum),
+ SOC_ENUM("SDI2 Loopback Src Select", lp2_enum),
+ SOC_ENUM("SDI1 Loopback Src Select", lp1_enum),
+ SOC_ENUM("SDI0 Loopback Src Select", lp0_enum),
+ SOC_ENUM("SDI3 Loopback Switch", lp3_switch),
+ SOC_ENUM("SDI2 Loopback Switch", lp2_switch),
+ SOC_ENUM("SDI1 Loopback Switch", lp1_switch),
+ SOC_ENUM("SDI0 Loopback Switch", lp0_switch),
+ SOC_ENUM("Sync Out Switch", sync_out_switch),
+ SOC_ENUM("Sync In Switch", sync_in_switch),
+ SOC_ENUM("Receive PATH3 Source Select", rpath3_enum),
+ SOC_ENUM("Receive PATH2 Source Select", rpath2_enum),
+ SOC_ENUM("Receive PATH1 Source Select", rpath1_enum),
+ SOC_ENUM("Receive PATH0 Source Select", rpath0_enum),
+ SOC_ENUM("Transmit SDO3 Source Select", tpath3_enum),
+ SOC_ENUM("Transmit SDO2 Source Select", tpath2_enum),
+ SOC_ENUM("Transmit SDO1 Source Select", tpath1_enum),
+ SOC_ENUM("Transmit SDO0 Source Select", tpath0_enum),
+
+ SAI_PCM_WAIT_TIME("PCM Read Wait Time MS",
+ rockchip_sai_rd_wait_time_get,
+ rockchip_sai_rd_wait_time_put),
+ SAI_PCM_WAIT_TIME("PCM Write Wait Time MS",
+ rockchip_sai_wr_wait_time_get,
+ rockchip_sai_wr_wait_time_put),
+};
+
+static const struct snd_soc_component_driver rockchip_sai_component = {
+ .name = DRV_NAME,
+ .controls = rockchip_sai_controls,
+ .num_controls = ARRAY_SIZE(rockchip_sai_controls),
+ .legacy_dai_naming = 1,
+};
+
+static irqreturn_t rockchip_sai_isr(int irq, void *devid)
+{
+ struct rk_sai_dev *sai = (struct rk_sai_dev *)devid;
+ struct snd_pcm_substream *substream;
+ u32 val;
+
+ regmap_read(sai->regmap, SAI_INTSR, &val);
+ if (val & SAI_INTSR_TXUI_ACT) {
+ dev_warn_ratelimited(sai->dev, "TX FIFO Underrun\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIC, SAI_INTCR_TXUIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIE_MASK,
+ SAI_INTCR_TXUIE(0));
+ substream = sai->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (val & SAI_INTSR_RXOI_ACT) {
+ dev_warn_ratelimited(sai->dev, "RX FIFO Overrun\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIC, SAI_INTCR_RXOIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIE_MASK,
+ SAI_INTCR_RXOIE(0));
+ substream = sai->substreams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (val & SAI_INTSR_FSERRI_ACT) {
+ dev_warn_ratelimited(sai->dev, "Frame Sync Error\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERRC, SAI_INTCR_FSERRC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERR_MASK,
+ SAI_INTCR_FSERR(0));
+ }
+
+ if (val & SAI_INTSR_FSLOSTI_ACT) {
+ dev_warn_ratelimited(sai->dev, "Frame Sync Lost\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOST_MASK,
+ SAI_INTCR_FSLOST(0));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rockchip_sai_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct rk_sai_dev *sai;
+ struct snd_soc_dai_driver *dai;
+ struct resource *res;
+ void __iomem *regs;
+ int ret, irq;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ sai->dev = &pdev->dev;
+ sai->fw_ratio = 1;
+ /* match to register default */
+ sai->is_master_mode = true;
+ dev_set_drvdata(&pdev->dev, sai);
+
+ spin_lock_init(&sai->xfer_lock);
+
+ sai->rst_h = devm_reset_control_get_optional_exclusive(&pdev->dev, "h");
+ if (IS_ERR(sai->rst_h))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_h),
+ "Error in 'h' reset control\n");
+
+ sai->rst_m = devm_reset_control_get_optional_exclusive(&pdev->dev, "m");
+ if (IS_ERR(sai->rst_m))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_m),
+ "Error in 'm' reset control\n");
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return dev_err_probe(&pdev->dev, PTR_ERR(regs),
+ "Failed to get and ioremap resource\n");
+
+ sai->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &rockchip_sai_regmap_config);
+ if (IS_ERR(sai->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap),
+ "Failed to initialize regmap\n");
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq > 0) {
+ ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr,
+ IRQF_SHARED, node->name, sai);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to request irq %d\n", irq);
+ } else {
+ dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq);
+ }
+
+ sai->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(sai->mclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->mclk),
+ "Failed to get mclk\n");
+
+ sai->hclk = devm_clk_get_enabled(&pdev->dev, "hclk");
+ if (IS_ERR(sai->hclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk),
+ "Failed to get hclk\n");
+
+ regmap_read(sai->regmap, SAI_VERSION, &sai->version);
+
+ ret = rockchip_sai_init_dai(sai, res, &dai);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to initialize DAI\n");
+
+ ret = rockchip_sai_parse_paths(sai, node);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n");
+
+ /*
+ * From here on, all register accesses need to be wrapped in
+ * pm_runtime_get_sync/pm_runtime_put calls
+ *
+ * NB: we don't rely on _resume_and_get in case of !CONFIG_PM
+ */
+ devm_pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+ ret = rockchip_sai_runtime_resume(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n");
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret);
+ goto err_runtime_suspend;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rockchip_sai_component,
+ dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_runtime_suspend;
+ }
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
+
+ clk_disable_unprepare(sai->hclk);
+
+ return 0;
+
+err_runtime_suspend:
+ /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */
+ if (pm_runtime_put(&pdev->dev))
+ rockchip_sai_runtime_suspend(&pdev->dev);
+
+ return ret;
+}
+
+static void rockchip_sai_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_PM
+ rockchip_sai_runtime_suspend(&pdev->dev);
+#endif
+}
+
+static const struct dev_pm_ops rockchip_sai_pm_ops = {
+ SET_RUNTIME_PM_OPS(rockchip_sai_runtime_suspend, rockchip_sai_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static const struct of_device_id rockchip_sai_match[] = {
+ { .compatible = "rockchip,rk3576-sai", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_sai_match);
+
+static struct platform_driver rockchip_sai_driver = {
+ .probe = rockchip_sai_probe,
+ .remove = rockchip_sai_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = rockchip_sai_match,
+ .pm = &rockchip_sai_pm_ops,
+ },
+};
+module_platform_driver(rockchip_sai_driver);
+
+MODULE_DESCRIPTION("Rockchip SAI ASoC Interface");
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rockchip/rockchip_sai.h b/sound/soc/rockchip/rockchip_sai.h
new file mode 100644
index 000000000000..c359c5d0311c
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_sai.h
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ALSA SoC Audio Layer - Rockchip SAI Controller driver
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ */
+
+#ifndef _ROCKCHIP_SAI_H
+#define _ROCKCHIP_SAI_H
+
+/* XCR Transmit / Receive Control Register */
+#define SAI_XCR_START_SEL_MASK BIT(23)
+#define SAI_XCR_START_SEL_CHAINED BIT(23)
+#define SAI_XCR_START_SEL_STANDALONE 0
+#define SAI_XCR_EDGE_SHIFT_MASK BIT(22)
+#define SAI_XCR_EDGE_SHIFT_1 BIT(22)
+#define SAI_XCR_EDGE_SHIFT_0 0
+#define SAI_XCR_CSR_MASK GENMASK(21, 20)
+#define SAI_XCR_CSR(x) ((x - 1) << 20)
+#define SAI_XCR_CSR_V(v) ((((v) & SAI_XCR_CSR_MASK) >> 20) + 1)
+#define SAI_XCR_SJM_MASK BIT(19)
+#define SAI_XCR_SJM_L BIT(19)
+#define SAI_XCR_SJM_R 0
+#define SAI_XCR_FBM_MASK BIT(18)
+#define SAI_XCR_FBM_LSB BIT(18)
+#define SAI_XCR_FBM_MSB 0
+#define SAI_XCR_SNB_MASK GENMASK(17, 11)
+#define SAI_XCR_SNB(x) ((x - 1) << 11)
+#define SAI_XCR_VDJ_MASK BIT(10)
+#define SAI_XCR_VDJ_L BIT(10)
+#define SAI_XCR_VDJ_R 0
+#define SAI_XCR_SBW_MASK GENMASK(9, 5)
+#define SAI_XCR_SBW(x) ((x - 1) << 5)
+#define SAI_XCR_SBW_V(v) ((((v) & SAI_XCR_SBW_MASK) >> 5) + 1)
+#define SAI_XCR_VDW_MASK GENMASK(4, 0)
+#define SAI_XCR_VDW(x) ((x - 1) << 0)
+
+/* FSCR Frame Sync Control Register */
+#define SAI_FSCR_EDGE_MASK BIT(24)
+#define SAI_FSCR_EDGE_DUAL BIT(24)
+#define SAI_FSCR_EDGE_RISING 0
+#define SAI_FSCR_FPW_MASK GENMASK(23, 12)
+#define SAI_FSCR_FPW(x) ((x - 1) << 12)
+#define SAI_FSCR_FW_MASK GENMASK(11, 0)
+#define SAI_FSCR_FW(x) ((x - 1) << 0)
+#define SAI_FSCR_FW_V(v) ((((v) & SAI_FSCR_FW_MASK) >> 0) + 1)
+
+/* MONO_CR Mono Control Register */
+#define SAI_MCR_RX_MONO_SLOT_MASK GENMASK(8, 2)
+#define SAI_MCR_RX_MONO_SLOT_SEL(x) ((x - 1) << 2)
+#define SAI_MCR_RX_MONO_MASK BIT(1)
+#define SAI_MCR_RX_MONO_EN BIT(1)
+#define SAI_MCR_RX_MONO_DIS 0
+#define SAI_MCR_TX_MONO_MASK BIT(0)
+#define SAI_MCR_TX_MONO_EN BIT(0)
+#define SAI_MCR_TX_MONO_DIS 0
+
+/* XFER Transfer Start Register */
+#define SAI_XFER_RX_IDLE BIT(8)
+#define SAI_XFER_TX_IDLE BIT(7)
+#define SAI_XFER_FS_IDLE BIT(6)
+/*
+ * Used for TX only (VERSION >= SAI_VER_2311)
+ *
+ * SCLK/FSYNC auto gated when TX FIFO empty.
+ */
+#define SAI_XFER_TX_AUTO_MASK BIT(6)
+#define SAI_XFER_TX_AUTO_EN BIT(6)
+#define SAI_XFER_TX_AUTO_DIS 0
+#define SAI_XFER_RX_CNT_MASK BIT(5)
+#define SAI_XFER_RX_CNT_EN BIT(5)
+#define SAI_XFER_RX_CNT_DIS 0
+#define SAI_XFER_TX_CNT_MASK BIT(4)
+#define SAI_XFER_TX_CNT_EN BIT(4)
+#define SAI_XFER_TX_CNT_DIS 0
+#define SAI_XFER_RXS_MASK BIT(3)
+#define SAI_XFER_RXS_EN BIT(3)
+#define SAI_XFER_RXS_DIS 0
+#define SAI_XFER_TXS_MASK BIT(2)
+#define SAI_XFER_TXS_EN BIT(2)
+#define SAI_XFER_TXS_DIS 0
+#define SAI_XFER_FSS_MASK BIT(1)
+#define SAI_XFER_FSS_EN BIT(1)
+#define SAI_XFER_FSS_DIS 0
+#define SAI_XFER_CLK_MASK BIT(0)
+#define SAI_XFER_CLK_EN BIT(0)
+#define SAI_XFER_CLK_DIS 0
+
+/* CLR Clear Logic Register */
+#define SAI_CLR_FCR BIT(3) /* TODO: what is this? */
+#define SAI_CLR_FSC BIT(2)
+#define SAI_CLR_RXC BIT(1)
+#define SAI_CLR_TXC BIT(0)
+
+/* CKR Clock Generation Register */
+#define SAI_CKR_MDIV_MASK GENMASK(14, 3)
+#define SAI_CKR_MDIV(x) ((x - 1) << 3)
+#define SAI_CKR_MSS_MASK BIT(2)
+#define SAI_CKR_MSS_SLAVE BIT(2)
+#define SAI_CKR_MSS_MASTER 0
+#define SAI_CKR_CKP_MASK BIT(1)
+#define SAI_CKR_CKP_INVERTED BIT(1)
+#define SAI_CKR_CKP_NORMAL 0
+#define SAI_CKR_FSP_MASK BIT(0)
+#define SAI_CKR_FSP_INVERTED BIT(0)
+#define SAI_CKR_FSP_NORMAL 0
+
+/* DMACR DMA Control Register */
+#define SAI_DMACR_RDE_MASK BIT(24)
+#define SAI_DMACR_RDE(x) ((x) << 24)
+#define SAI_DMACR_RDL_MASK GENMASK(20, 16)
+#define SAI_DMACR_RDL(x) ((x - 1) << 16)
+#define SAI_DMACR_RDL_V(v) ((((v) & SAI_DMACR_RDL_MASK) >> 16) + 1)
+#define SAI_DMACR_TDE_MASK BIT(8)
+#define SAI_DMACR_TDE(x) ((x) << 8)
+#define SAI_DMACR_TDL_MASK GENMASK(4, 0)
+#define SAI_DMACR_TDL(x) ((x) << 0)
+#define SAI_DMACR_TDL_V(v) (((v) & SAI_DMACR_TDL_MASK) >> 0)
+
+/* INTCR Interrupt Ctrl Register */
+#define SAI_INTCR_FSLOSTC BIT(28)
+#define SAI_INTCR_FSLOST_MASK BIT(27)
+#define SAI_INTCR_FSLOST(x) ((x) << 27)
+#define SAI_INTCR_FSERRC BIT(26)
+#define SAI_INTCR_FSERR_MASK BIT(25)
+#define SAI_INTCR_FSERR(x) ((x) << 25)
+#define SAI_INTCR_RXOIC BIT(18)
+#define SAI_INTCR_RXOIE_MASK BIT(17)
+#define SAI_INTCR_RXOIE(x) ((x) << 17)
+#define SAI_INTCR_TXUIC BIT(2)
+#define SAI_INTCR_TXUIE_MASK BIT(1)
+#define SAI_INTCR_TXUIE(x) ((x) << 1)
+
+/* INTSR Interrupt Status Register */
+#define SAI_INTSR_FSLOSTI_INA 0
+#define SAI_INTSR_FSLOSTI_ACT BIT(19)
+#define SAI_INTSR_FSERRI_INA 0
+#define SAI_INTSR_FSERRI_ACT BIT(18)
+#define SAI_INTSR_RXOI_INA 0
+#define SAI_INTSR_RXOI_ACT BIT(17)
+#define SAI_INTSR_TXUI_INA 0
+#define SAI_INTSR_TXUI_ACT BIT(1)
+
+/* PATH_SEL: Transfer / Receive Path Select Register */
+#define SAI_RX_PATH_SHIFT(x) (8 + (x) * 2)
+#define SAI_RX_PATH_MASK(x) (0x3 << SAI_RX_PATH_SHIFT(x))
+#define SAI_RX_PATH(x, v) ((v) << SAI_RX_PATH_SHIFT(x))
+#define SAI_TX_PATH_SHIFT(x) (0 + (x) * 2)
+#define SAI_TX_PATH_MASK(x) (0x3 << SAI_TX_PATH_SHIFT(x))
+#define SAI_TX_PATH(x, v) ((v) << SAI_TX_PATH_SHIFT(x))
+
+/* XSHIFT: Transfer / Receive Frame Sync Shift Register */
+
+/*
+ * TX-ONLY: LEFT Direction Feature
+ * +------------------------------------------------+
+ * | DATA LEFTx (step: 0.5 cycle) | FSYNC Edge |
+ * +------------------------------------------------+
+ */
+#define SAI_XSHIFT_LEFT_MASK GENMASK(25, 24)
+#define SAI_XSHIFT_LEFT(x) ((x) << 24)
+/*
+ * +------------------------------------------------+
+ * | FSYNC Edge | DATA RIGHTx (step: 0.5 cycle) |
+ * +------------------------------------------------+
+ */
+#define SAI_XSHIFT_RIGHT_MASK GENMASK(23, 0)
+#define SAI_XSHIFT_RIGHT(x) (x)
+
+/* XFIFOLR: Transfer / Receive FIFO Level Register */
+#define SAI_FIFOLR_XFL3_SHIFT 18
+#define SAI_FIFOLR_XFL3_MASK GENMASK(23, 18)
+#define SAI_FIFOLR_XFL2_SHIFT 12
+#define SAI_FIFOLR_XFL2_MASK GENMASK(17, 12)
+#define SAI_FIFOLR_XFL1_SHIFT 6
+#define SAI_FIFOLR_XFL1_MASK GENMASK(11, 6)
+#define SAI_FIFOLR_XFL0_SHIFT 0
+#define SAI_FIFOLR_XFL0_MASK GENMASK(5, 0)
+
+/* STATUS Status Register (VERSION >= SAI_VER_2307) */
+#define SAI_STATUS_RX_IDLE BIT(3)
+#define SAI_STATUS_TX_IDLE BIT(2)
+#define SAI_STATUS_FS_IDLE BIT(1)
+
+/* VERSION */
+/*
+ * Updates:
+ *
+ * VERSION >= SAI_VER_2311
+ *
+ * Support Frame Sync xN (FSXN)
+ * Support Frame Sync Error Detect (FSE)
+ * Support Frame Sync Lost Detect (FSLOST)
+ * Support Force Clear (FCR)
+ * Support SAIn-Chained (e.g. SAI0-CLK-DATA + SAI3-DATA +...)
+ * Support Transmit Auto Gate Mode
+ * Support Timing Shift Left for TX
+ *
+ * Optimize SCLK/FSYNC Timing Alignment
+ *
+ * VERSION >= SAI_VER_2403
+ *
+ * Support Loopback LR Select (e.g. L:MIC R:LP)
+ *
+ */
+#define SAI_VER_2307 0x23073576
+#define SAI_VER_2311 0x23112118
+#define SAI_VER_2401 0x24013506
+#define SAI_VER_2403 0x24031103
+
+/* FS_TIMEOUT: Frame Sync Timeout Register */
+#define SAI_FS_TIMEOUT_VAL_MASK GENMASK(31, 1)
+#define SAI_FS_TIMEOUT_VAL(x) ((x) << 1)
+#define SAI_FS_TIMEOUT_EN_MASK BIT(0)
+#define SAI_FS_TIMEOUT_EN(x) ((x) << 0)
+
+/* SAI Registers */
+#define SAI_TXCR (0x0000)
+#define SAI_FSCR (0x0004)
+#define SAI_RXCR (0x0008)
+#define SAI_MONO_CR (0x000c)
+#define SAI_XFER (0x0010)
+#define SAI_CLR (0x0014)
+#define SAI_CKR (0x0018)
+#define SAI_TXFIFOLR (0x001c)
+#define SAI_RXFIFOLR (0x0020)
+#define SAI_DMACR (0x0024)
+#define SAI_INTCR (0x0028)
+#define SAI_INTSR (0x002c)
+#define SAI_TXDR (0x0030)
+#define SAI_RXDR (0x0034)
+#define SAI_PATH_SEL (0x0038)
+#define SAI_TX_SLOT_MASK0 (0x003c)
+#define SAI_TX_SLOT_MASK1 (0x0040)
+#define SAI_TX_SLOT_MASK2 (0x0044)
+#define SAI_TX_SLOT_MASK3 (0x0048)
+#define SAI_RX_SLOT_MASK0 (0x004c)
+#define SAI_RX_SLOT_MASK1 (0x0050)
+#define SAI_RX_SLOT_MASK2 (0x0054)
+#define SAI_RX_SLOT_MASK3 (0x0058)
+#define SAI_TX_DATA_CNT (0x005c)
+#define SAI_RX_DATA_CNT (0x0060)
+#define SAI_TX_SHIFT (0x0064)
+#define SAI_RX_SHIFT (0x0068)
+#define SAI_STATUS (0x006c)
+#define SAI_VERSION (0x0070)
+#define SAI_FSXN (0x0074)
+#define SAI_FS_TIMEOUT (0x0078)
+#define SAI_LOOPBACK_LR (0x007c)
+
+#endif /* _ROCKCHIP_SAI_H */
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 5a806da89f42..d365168934dc 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* sound/soc/rockchip/rk_spdif.c
*
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
@@ -6,15 +7,10 @@
* Author: Jianqun <jay.xu@rock-chips.com>
* Copyright (c) 2015 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.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/delay.h>
-#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/mfd/syscon.h>
@@ -28,6 +24,7 @@ enum rk_spdif_type {
RK_SPDIF_RK3066,
RK_SPDIF_RK3188,
RK_SPDIF_RK3288,
+ RK_SPDIF_RK3366,
};
#define RK3288_GRF_SOC_CON2 0x24c
@@ -43,13 +40,25 @@ struct rk_spdif_dev {
struct regmap *regmap;
};
-static const struct of_device_id rk_spdif_match[] = {
+static const struct of_device_id rk_spdif_match[] __maybe_unused = {
{ .compatible = "rockchip,rk3066-spdif",
- .data = (void *) RK_SPDIF_RK3066 },
+ .data = (void *)RK_SPDIF_RK3066 },
{ .compatible = "rockchip,rk3188-spdif",
- .data = (void *) RK_SPDIF_RK3188 },
+ .data = (void *)RK_SPDIF_RK3188 },
+ { .compatible = "rockchip,rk3228-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3288-spdif",
- .data = (void *) RK_SPDIF_RK3288 },
+ .data = (void *)RK_SPDIF_RK3288 },
+ { .compatible = "rockchip,rk3328-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3366-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3368-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3399-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3568-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
{},
};
MODULE_DEVICE_TABLE(of, rk_spdif_match);
@@ -58,6 +67,7 @@ static int rk_spdif_runtime_suspend(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+ regcache_cache_only(spdif->regmap, true);
clk_disable_unprepare(spdif->mclk);
clk_disable_unprepare(spdif->hclk);
@@ -77,16 +87,26 @@ static int rk_spdif_runtime_resume(struct device *dev)
ret = clk_prepare_enable(spdif->hclk);
if (ret) {
+ clk_disable_unprepare(spdif->mclk);
dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
return ret;
}
- return 0;
+ regcache_cache_only(spdif->regmap, false);
+ regcache_mark_dirty(spdif->regmap);
+
+ ret = regcache_sync(spdif->regmap);
+ if (ret) {
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+ }
+
+ return ret;
}
static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
@@ -94,21 +114,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
int ret;
srate = params_rate(params);
- switch (srate) {
- case 32000:
- case 48000:
- case 96000:
- mclk = 96000 * 128; /* 12288000 hz */
- break;
- case 44100:
- mclk = 44100 * 256; /* 11289600 hz */
- break;
- case 192000:
- mclk = 192000 * 128; /* 24576000 hz */
- break;
- default:
- return -EINVAL;
- }
+ mclk = srate * 128;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -132,17 +138,16 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
- SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
- SDPIF_CFGR_VDW_MASK,
- val);
+ SPDIF_CFGR_CLK_DIV_MASK |
+ SPDIF_CFGR_HALFWORD_ENABLE |
+ SDPIF_CFGR_VDW_MASK, val);
return ret;
}
static int rk_spdif_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+ int cmd, struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
int ret;
@@ -152,31 +157,31 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE |
- SPDIF_DMACR_TDL_MASK,
- SPDIF_DMACR_TDE_ENABLE |
- SPDIF_DMACR_TDL(16));
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL_MASK,
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL(16));
if (ret != 0)
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
- SPDIF_XFER_TXS_START,
- SPDIF_XFER_TXS_START);
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_START);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE,
- SPDIF_DMACR_TDE_DISABLE);
+ SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_DISABLE);
if (ret != 0)
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
- SPDIF_XFER_TXS_START,
- SPDIF_XFER_TXS_STOP);
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_STOP);
break;
default:
ret = -EINVAL;
@@ -190,18 +195,18 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
- dai->playback_dma_data = &spdif->playback_dma_data;
+ snd_soc_dai_dma_data_set_playback(dai, &spdif->playback_dma_data);
return 0;
}
static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+ .probe = rk_spdif_dai_probe,
.hw_params = rk_spdif_hw_params,
.trigger = rk_spdif_trigger,
};
static struct snd_soc_dai_driver rk_spdif_dai = {
- .probe = rk_spdif_dai_probe,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
@@ -220,6 +225,7 @@ static struct snd_soc_dai_driver rk_spdif_dai = {
static const struct snd_soc_component_driver rk_spdif_component = {
.name = "rockchip-spdif",
+ .legacy_dai_naming = 1,
};
static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
@@ -244,6 +250,7 @@ static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
case SPDIF_INTCR:
case SPDIF_INTSR:
case SPDIF_XFER:
+ case SPDIF_SMPDR:
return true;
default:
return false;
@@ -255,6 +262,7 @@ static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case SPDIF_INTSR:
case SPDIF_SDBLR:
+ case SPDIF_SMPDR:
return true;
default:
return false;
@@ -288,7 +296,7 @@ static int rk_spdif_probe(struct platform_device *pdev)
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(grf)) {
dev_err(&pdev->dev,
- "rockchip_spdif missing 'rockchip,grf' \n");
+ "rockchip_spdif missing 'rockchip,grf'\n");
return PTR_ERR(grf);
}
@@ -303,40 +311,21 @@ static int rk_spdif_probe(struct platform_device *pdev)
return -ENOMEM;
spdif->hclk = devm_clk_get(&pdev->dev, "hclk");
- if (IS_ERR(spdif->hclk)) {
- dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n");
+ if (IS_ERR(spdif->hclk))
return PTR_ERR(spdif->hclk);
- }
- ret = clk_prepare_enable(spdif->hclk);
- if (ret) {
- dev_err(spdif->dev, "hclock enable failed %d\n", ret);
- return ret;
- }
spdif->mclk = devm_clk_get(&pdev->dev, "mclk");
- if (IS_ERR(spdif->mclk)) {
- dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n");
+ if (IS_ERR(spdif->mclk))
return PTR_ERR(spdif->mclk);
- }
-
- ret = clk_prepare_enable(spdif->mclk);
- if (ret) {
- dev_err(spdif->dev, "clock enable failed %d\n", ret);
- return ret;
- }
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs,
&rk_spdif_regmap_config);
- if (IS_ERR(spdif->regmap)) {
- dev_err(&pdev->dev,
- "Failed to initialise managed register map\n");
+ if (IS_ERR(spdif->regmap))
return PTR_ERR(spdif->regmap);
- }
spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR;
spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -345,49 +334,47 @@ static int rk_spdif_probe(struct platform_device *pdev)
spdif->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, spdif);
- pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- pm_request_idle(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = rk_spdif_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_runtime;
+ }
ret = devm_snd_soc_register_component(&pdev->dev,
&rk_spdif_component,
&rk_spdif_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI\n");
- goto err_pm_runtime;
+ goto err_pm_suspend;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
- goto err_pm_runtime;
+ goto err_pm_suspend;
}
return 0;
+err_pm_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ rk_spdif_runtime_suspend(&pdev->dev);
err_pm_runtime:
pm_runtime_disable(&pdev->dev);
return ret;
}
-static int rk_spdif_remove(struct platform_device *pdev)
+static void rk_spdif_remove(struct platform_device *pdev)
{
- struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev);
-
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
rk_spdif_runtime_suspend(&pdev->dev);
-
- clk_disable_unprepare(spdif->mclk);
- clk_disable_unprepare(spdif->hclk);
-
- return 0;
}
static const struct dev_pm_ops rk_spdif_pm_ops = {
- SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL)
};
static struct platform_driver rk_spdif_driver = {
@@ -396,7 +383,7 @@ static struct platform_driver rk_spdif_driver = {
.driver = {
.name = "rockchip-spdif",
.of_match_table = of_match_ptr(rk_spdif_match),
- .pm = &rk_spdif_pm_ops,
+ .pm = pm_ptr(&rk_spdif_pm_ops),
},
};
module_platform_driver(rk_spdif_driver);
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
index 3ef12770ae12..d8be9aae5b19 100644
--- a/sound/soc/rockchip/rockchip_spdif.h
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
*
* Copyright (c) 2015 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.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.
*/
#ifndef _ROCKCHIP_SPDIF_H
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 78baa26e938b..ec7204f57fd4 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -1,66 +1,28 @@
-config SND_SOC_SAMSUNG
- tristate "ASoC support for Samsung"
- depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig SND_SOC_SAMSUNG
+ tristate "Samsung"
+ depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+ depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
the Samsung SoCs' Audio interfaces. You will also need to
select the audio interfaces to support below.
-config SND_S3C24XX_I2S
- tristate
-
-config SND_S3C_I2SV2_SOC
- tristate
-
-config SND_S3C2412_SOC_I2S
- tristate
- select SND_S3C_I2SV2_SOC
+if SND_SOC_SAMSUNG
config SND_SAMSUNG_PCM
- tristate
-
-config SND_SAMSUNG_AC97
- tristate
- select SND_SOC_AC97_BUS
+ tristate "Samsung PCM interface support"
config SND_SAMSUNG_SPDIF
- tristate
+ tristate "Samsung SPDIF transmitter support"
select SND_SOC_SPDIF
config SND_SAMSUNG_I2S
- tristate
-
-config SND_SOC_SAMSUNG_NEO1973_WM8753
- tristate "Audio support for Openmoko Neo1973 Smartphones (GTA02)"
- depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
- select SND_S3C24XX_I2S
- select SND_SOC_WM8753
- select SND_SOC_BT_SCO
- help
- Say Y here to enable audio support for the Openmoko Neo1973
- Smartphones.
-
-config SND_SOC_SAMSUNG_JIVE_WM8750
- tristate "SoC I2S Audio support for Jive"
- depends on SND_SOC_SAMSUNG && MACH_JIVE && I2C
- select SND_SOC_WM8750
- select SND_S3C2412_SOC_I2S
- help
- Say Y if you want to add support for SoC audio on the Jive.
-
-config SND_SOC_SAMSUNG_SMDK_WM8580
- tristate "SoC I2S Audio support for WM8580 on SMDK"
- depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110)
- depends on I2C
- select SND_SOC_WM8580
- select SND_SAMSUNG_I2S
- help
- Say Y if you want to add support for SoC audio on the SMDKs.
+ tristate "Samsung I2S interface support"
config SND_SOC_SAMSUNG_SMDK_WM8994
tristate "SoC I2S Audio support for WM8994 on SMDK"
- depends on SND_SOC_SAMSUNG
depends on I2C=y
select MFD_WM8994
select SND_SOC_WM8994
@@ -68,101 +30,14 @@ config SND_SOC_SAMSUNG_SMDK_WM8994
help
Say Y if you want to add support for SoC audio on the SMDKs.
-config SND_SOC_SAMSUNG_SMDK2443_WM9710
- tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
- depends on SND_SOC_SAMSUNG && MACH_SMDK2443
- select AC97_BUS
- select SND_SOC_AC97_CODEC
- select SND_SAMSUNG_AC97
- help
- Say Y if you want to add support for SoC audio on smdk2443
- with the WM9710.
-
-config SND_SOC_SAMSUNG_LN2440SBC_ALC650
- tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
- select AC97_BUS
- select SND_SOC_AC97_CODEC
- select SND_SAMSUNG_AC97
- help
- Say Y if you want to add support for SoC audio on ln2440sbc
- with the ALC650.
-
-config SND_SOC_SAMSUNG_S3C24XX_UDA134X
- tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
- select SND_S3C24XX_I2S
- select SND_SOC_L3
- select SND_SOC_UDA134X
-
-config SND_SOC_SAMSUNG_SIMTEC
- tristate
- help
- Internal node for common S3C24XX/Simtec suppor
-
-config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23
- tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C
- select SND_S3C24XX_I2S
- select SND_SOC_TLV320AIC23_I2C
- select SND_SOC_SAMSUNG_SIMTEC
-
-config SND_SOC_SAMSUNG_SIMTEC_HERMES
- tristate "SoC I2S Audio support for Simtec Hermes board"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C
- select SND_S3C24XX_I2S
- select SND_SOC_TLV320AIC3X
- select SND_SOC_SAMSUNG_SIMTEC
-
-config SND_SOC_SAMSUNG_H1940_UDA1380
- tristate "Audio support for the HP iPAQ H1940"
- depends on SND_SOC_SAMSUNG && ARCH_H1940 && I2C
- select SND_S3C24XX_I2S
- select SND_SOC_UDA1380
- help
- This driver provides audio support for HP iPAQ h1940 PDA.
-
-config SND_SOC_SAMSUNG_RX1950_UDA1380
- tristate "Audio support for the HP iPAQ RX1950"
- depends on SND_SOC_SAMSUNG && MACH_RX1950 && I2C
- select SND_S3C24XX_I2S
- select SND_SOC_UDA1380
- help
- This driver provides audio support for HP iPAQ RX1950 PDA.
-
-config SND_SOC_SAMSUNG_SMDK_WM9713
- tristate "SoC AC97 Audio support for SMDK with WM9713"
- depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110)
- select SND_SOC_WM9713
- select SND_SAMSUNG_AC97
- help
- Say Y if you want to add support for SoC audio on the SMDK.
-
-config SND_SOC_SMARTQ
- tristate "SoC I2S Audio support for SmartQ board"
- depends on SND_SOC_SAMSUNG && MACH_SMARTQ && I2C
- select SND_SAMSUNG_I2S
- select SND_SOC_WM8750
-
config SND_SOC_SAMSUNG_SMDK_SPDIF
tristate "SoC S/PDIF Audio support for SMDK"
- depends on SND_SOC_SAMSUNG
select SND_SAMSUNG_SPDIF
help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
-config SND_SOC_SMDK_WM8580_PCM
- tristate "SoC PCM Audio support for WM8580 on SMDK"
- depends on SND_SOC_SAMSUNG && (MACH_SMDKV210 || MACH_SMDKC110)
- depends on I2C
- select SND_SOC_WM8580
- select SND_SAMSUNG_PCM
- help
- Say Y if you want to add support for SoC audio on the SMDK.
-
config SND_SOC_SMDK_WM8994_PCM
tristate "SoC PCM Audio support for WM8994 on SMDK"
- depends on SND_SOC_SAMSUNG
depends on I2C=y
select MFD_WM8994
select SND_SOC_WM8994
@@ -172,7 +47,7 @@ config SND_SOC_SMDK_WM8994_PCM
config SND_SOC_SPEYSIDE
tristate "Audio support for Wolfson Speyside"
- depends on SND_SOC_SAMSUNG && I2C && SPI_MASTER
+ depends on I2C && SPI_MASTER
depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM8996
@@ -182,14 +57,14 @@ config SND_SOC_SPEYSIDE
config SND_SOC_TOBERMORY
tristate "Audio support for Wolfson Tobermory"
- depends on SND_SOC_SAMSUNG && INPUT && I2C
+ depends on INPUT && I2C
depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM8962
config SND_SOC_BELLS
tristate "Audio support for Wolfson Bells"
- depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
+ depends on MFD_ARIZONA && MFD_WM5102 && MFD_WM5110 && I2C && SPI_MASTER
depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM5102
@@ -200,7 +75,7 @@ config SND_SOC_BELLS
config SND_SOC_LOWLAND
tristate "Audio support for Wolfson Lowland"
- depends on SND_SOC_SAMSUNG && I2C
+ depends on I2C
depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM5100
@@ -208,7 +83,7 @@ config SND_SOC_LOWLAND
config SND_SOC_LITTLEMILL
tristate "Audio support for Wolfson Littlemill"
- depends on SND_SOC_SAMSUNG && I2C
+ depends on I2C
depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select MFD_WM8994
@@ -216,7 +91,7 @@ config SND_SOC_LITTLEMILL
config SND_SOC_SNOW
tristate "Audio support for Google Snow boards"
- depends on SND_SOC_SAMSUNG && I2C
+ depends on I2C
select SND_SOC_MAX98090
select SND_SOC_MAX98095
select SND_SAMSUNG_I2S
@@ -224,16 +99,53 @@ config SND_SOC_SNOW
Say Y if you want to add audio support for various Snow
boards based on Exynos5 series of SoCs.
-config SND_SOC_ODROIDX2
- tristate "Audio support for Odroid-X2 and Odroid-U3"
+config SND_SOC_ODROID
+ tristate "Audio support for Odroid XU3/XU4"
depends on SND_SOC_SAMSUNG && I2C
select SND_SOC_MAX98090
select SND_SAMSUNG_I2S
help
- Say Y here to enable audio support for the Odroid-X2/U3.
+ Say Y here to enable audio support for the Odroid XU3/XU4.
+
+config SND_SOC_ARNDALE
+ tristate "Audio support for Arndale Board"
+ depends on I2C
+ select SND_SAMSUNG_I2S
+ select SND_SOC_RT5631
+ select MFD_WM8994
+ select SND_SOC_WM8994
+
+config SND_SOC_SAMSUNG_TM2_WM5110
+ tristate "SoC I2S Audio support for WM5110 on TM2 board"
+ depends on SND_SOC_SAMSUNG && MFD_ARIZONA && MFD_WM5110 && I2C && SPI_MASTER
+ depends on GPIOLIB || COMPILE_TEST
+ select SND_SOC_MAX98504
+ select SND_SOC_WM5110
+ select SND_SAMSUNG_I2S
+ help
+ Say Y if you want to add support for SoC audio on the TM2 board.
+
+config SND_SOC_SAMSUNG_ARIES_WM8994
+ tristate "SoC I2S Audio support for WM8994 on Aries"
+ depends on SND_SOC_SAMSUNG && I2C && IIO && EXTCON
+ select SND_SOC_BT_SCO
+ select MFD_WM8994
+ select SND_SOC_WM8994
+ select SND_SAMSUNG_I2S
+ help
+ Say Y if you want to add support for SoC audio on Aries boards,
+ which has a WM8994 codec connected to a BT codec, a cellular
+ modem, and the Samsung I2S controller. Jack detection is done
+ via ADC, GPIOs, and an extcon device. Switching between the Mic
+ and TV-Out path is also handled.
+
+config SND_SOC_SAMSUNG_MIDAS_WM1811
+ tristate "SoC I2S Audio support for Midas boards"
+ depends on SND_SOC_SAMSUNG && I2C && IIO
+ select SND_SAMSUNG_I2S
+ select MFD_WM8994
+ select SND_SOC_WM8994
+ help
+ Say Y if you want to add support for SoC audio on the Midas boards.
-config SND_SOC_ARNDALE_RT5631_ALC5631
- tristate "Audio support for RT5631(ALC5631) on Arndale Board"
- depends on SND_SOC_SAMSUNG && I2C
- select SND_SAMSUNG_I2S
- select SND_SOC_RT5631
+endif #SND_SOC_SAMSUNG
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 052fe71be518..8d5f09147900 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -1,73 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
# S3c24XX Platform Support
-snd-soc-s3c-dma-objs := dmaengine.o
-snd-soc-idma-objs := idma.o
-snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
-snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
-snd-soc-ac97-objs := ac97.o
-snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
-snd-soc-samsung-spdif-objs := spdif.o
-snd-soc-pcm-objs := pcm.o
-snd-soc-i2s-objs := i2s.o
+snd-soc-s3c-dma-y := dmaengine.o
+snd-soc-idma-y := idma.o
+snd-soc-samsung-spdif-y := spdif.o
+snd-soc-pcm-y := pcm.o
+snd-soc-i2s-y := i2s.o
obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c-dma.o
-obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
-obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o
-obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
-obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o
obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
# S3C24XX Machine Support
-snd-soc-jive-wm8750-objs := jive_wm8750.o
-snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
-snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
-snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
-snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
-snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
-snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
-snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
-snd-soc-h1940-uda1380-objs := h1940_uda1380.o
-snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o
-snd-soc-smdk-wm8580-objs := smdk_wm8580.o
-snd-soc-smdk-wm8994-objs := smdk_wm8994.o
-snd-soc-snow-objs := snow.o
-snd-soc-smdk-wm9713-objs := smdk_wm9713.o
-snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
-snd-soc-smdk-spdif-objs := smdk_spdif.o
-snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
-snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o
-snd-soc-speyside-objs := speyside.o
-snd-soc-tobermory-objs := tobermory.o
-snd-soc-lowland-objs := lowland.o
-snd-soc-littlemill-objs := littlemill.o
-snd-soc-bells-objs := bells.o
-snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o
-snd-soc-arndale-rt5631-objs := arndale_rt5631.o
+snd-soc-smdk-wm8994-y := smdk_wm8994.o
+snd-soc-snow-y := snow.o
+snd-soc-smdk-spdif-y := smdk_spdif.o
+snd-soc-smdk-wm8994pcm-y := smdk_wm8994pcm.o
+snd-soc-speyside-y := speyside.o
+snd-soc-tobermory-y := tobermory.o
+snd-soc-lowland-y := lowland.o
+snd-soc-littlemill-y := littlemill.o
+snd-soc-bells-y := bells.o
+snd-soc-odroid-y := odroid.o
+snd-soc-arndale-y := arndale.o
+snd-soc-tm2-wm5110-y := tm2_wm5110.o
+snd-soc-aries-wm8994-y := aries_wm8994.o
+snd-soc-midas-wm1811-y := midas_wm1811.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_H1940_UDA1380) += snd-soc-h1940-uda1380.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994) += snd-soc-smdk-wm8994.o
obj-$(CONFIG_SND_SOC_SNOW) += snd-soc-snow.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
-obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
-obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o
obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
-obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o
-obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
+obj-$(CONFIG_SND_SOC_ODROID) += snd-soc-odroid.o
+obj-$(CONFIG_SND_SOC_ARNDALE) += snd-soc-arndale.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_ARIES_WM8994) += snd-soc-aries-wm8994.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_MIDAS_WM1811) += snd-soc-midas-wm1811.o
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
deleted file mode 100644
index 4a7a503fe13c..000000000000
--- a/sound/soc/samsung/ac97.c
+++ /dev/null
@@ -1,436 +0,0 @@
-/* sound/soc/samsung/ac97.c
- *
- * ALSA SoC Audio Layer - S3C AC97 Controller driver
- * Evolved from s3c2443-ac97.c
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd
- * Author: Jaswinder Singh <jassisinghbrar@gmail.com>
- * Credits: Graeme Gregory, Sean Choi
- *
- * This program is free software; you can 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/io.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-
-#include "regs-ac97.h"
-#include <linux/platform_data/asoc-s3c.h>
-
-#include "dma.h"
-
-#define AC_CMD_ADDR(x) (x << 16)
-#define AC_CMD_DATA(x) (x & 0xffff)
-
-#define S3C_AC97_DAI_PCM 0
-#define S3C_AC97_DAI_MIC 1
-
-struct s3c_ac97_info {
- struct clk *ac97_clk;
- void __iomem *regs;
- struct mutex lock;
- struct completion done;
-};
-static struct s3c_ac97_info s3c_ac97;
-
-static struct s3c_dma_params s3c_ac97_pcm_out = {
- .dma_size = 4,
-};
-
-static struct s3c_dma_params s3c_ac97_pcm_in = {
- .dma_size = 4,
-};
-
-static struct s3c_dma_params s3c_ac97_mic_in = {
- .dma_size = 4,
-};
-
-static void s3c_ac97_activate(struct snd_ac97 *ac97)
-{
- u32 ac_glbctrl, stat;
-
- stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
- if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
- return; /* Return if already active */
-
- reinit_completion(&s3c_ac97.done);
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- msleep(1);
-
- ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- msleep(1);
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
- pr_err("AC97: Unable to activate!");
-}
-
-static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- u32 ac_glbctrl, ac_codec_cmd;
- u32 stat, addr, data;
-
- mutex_lock(&s3c_ac97.lock);
-
- s3c_ac97_activate(ac97);
-
- reinit_completion(&s3c_ac97.done);
-
- ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
- ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
- writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
-
- udelay(50);
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
- pr_err("AC97: Unable to read!");
-
- stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
- addr = (stat >> 16) & 0x7f;
- data = (stat & 0xffff);
-
- if (addr != reg)
- pr_err("ac97: req addr = %02x, rep addr = %02x\n",
- reg, addr);
-
- mutex_unlock(&s3c_ac97.lock);
-
- return (unsigned short)data;
-}
-
-static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- u32 ac_glbctrl, ac_codec_cmd;
-
- mutex_lock(&s3c_ac97.lock);
-
- s3c_ac97_activate(ac97);
-
- reinit_completion(&s3c_ac97.done);
-
- ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
- ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
- writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
-
- udelay(50);
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
- pr_err("AC97: Unable to write!");
-
- ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
- ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
- writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
-
- mutex_unlock(&s3c_ac97.lock);
-}
-
-static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
-{
- pr_debug("AC97: Cold reset\n");
- writel(S3C_AC97_GLBCTRL_COLDRESET,
- s3c_ac97.regs + S3C_AC97_GLBCTRL);
- msleep(1);
-
- writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- msleep(1);
-}
-
-static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- u32 stat;
-
- stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
- if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
- return; /* Return if already active */
-
- pr_debug("AC97: Warm reset\n");
-
- writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- msleep(1);
-
- writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- msleep(1);
-
- s3c_ac97_activate(ac97);
-}
-
-static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
-{
- u32 ac_glbctrl, ac_glbstat;
-
- ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
-
- if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- complete(&s3c_ac97.done);
- }
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl |= (1<<30); /* Clear interrupt */
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- return IRQ_HANDLED;
-}
-
-static struct snd_ac97_bus_ops s3c_ac97_ops = {
- .read = s3c_ac97_read,
- .write = s3c_ac97_write,
- .warm_reset = s3c_ac97_warm_reset,
- .reset = s3c_ac97_cold_reset,
-};
-
-static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- u32 ac_glbctrl;
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
- else
- ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
- else
- ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- break;
- }
-
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- return 0;
-}
-
-static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- u32 ac_glbctrl;
-
- ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
- ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- break;
- }
-
- writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops s3c_ac97_dai_ops = {
- .trigger = s3c_ac97_trigger,
-};
-
-static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
- .trigger = s3c_ac97_mic_trigger,
-};
-
-static int s3c_ac97_dai_probe(struct snd_soc_dai *dai)
-{
- samsung_asoc_init_dma_data(dai, &s3c_ac97_pcm_out, &s3c_ac97_pcm_in);
-
- return 0;
-}
-
-static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
-{
- samsung_asoc_init_dma_data(dai, NULL, &s3c_ac97_mic_in);
-
- return 0;
-}
-
-static struct snd_soc_dai_driver s3c_ac97_dai[] = {
- [S3C_AC97_DAI_PCM] = {
- .name = "samsung-ac97",
- .bus_control = true,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .probe = s3c_ac97_dai_probe,
- .ops = &s3c_ac97_dai_ops,
- },
- [S3C_AC97_DAI_MIC] = {
- .name = "samsung-ac97-mic",
- .bus_control = true,
- .capture = {
- .stream_name = "AC97 Mic Capture",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .probe = s3c_ac97_mic_dai_probe,
- .ops = &s3c_ac97_mic_dai_ops,
- },
-};
-
-static const struct snd_soc_component_driver s3c_ac97_component = {
- .name = "s3c-ac97",
-};
-
-static int s3c_ac97_probe(struct platform_device *pdev)
-{
- struct resource *mem_res, *irq_res;
- struct s3c_audio_pdata *ac97_pdata;
- int ret;
-
- ac97_pdata = pdev->dev.platform_data;
- if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
- dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
- return -EINVAL;
- }
-
- /* Check for availability of necessary resource */
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res) {
- dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
- return -ENXIO;
- }
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res);
- if (IS_ERR(s3c_ac97.regs))
- return PTR_ERR(s3c_ac97.regs);
-
- s3c_ac97_pcm_out.slave = ac97_pdata->dma_playback;
- s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
- s3c_ac97_pcm_in.slave = ac97_pdata->dma_capture;
- s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
- s3c_ac97_mic_in.slave = ac97_pdata->dma_capture_mic;
- s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
-
- init_completion(&s3c_ac97.done);
- mutex_init(&s3c_ac97.lock);
-
- s3c_ac97.ac97_clk = devm_clk_get(&pdev->dev, "ac97");
- if (IS_ERR(s3c_ac97.ac97_clk)) {
- dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n");
- ret = -ENODEV;
- goto err2;
- }
- clk_prepare_enable(s3c_ac97.ac97_clk);
-
- if (ac97_pdata->cfg_gpio(pdev)) {
- dev_err(&pdev->dev, "Unable to configure gpio\n");
- ret = -EINVAL;
- goto err3;
- }
-
- ret = request_irq(irq_res->start, s3c_ac97_irq,
- 0, "AC97", NULL);
- if (ret < 0) {
- dev_err(&pdev->dev, "ac97: interrupt request failed.\n");
- goto err4;
- }
-
- ret = snd_soc_set_ac97_ops(&s3c_ac97_ops);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
- goto err4;
- }
-
- ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
- s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
- if (ret)
- goto err5;
-
- ret = samsung_asoc_dma_platform_register(&pdev->dev,
- ac97_pdata->dma_filter);
- if (ret) {
- dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
- goto err5;
- }
-
- return 0;
-err5:
- free_irq(irq_res->start, NULL);
-err4:
-err3:
- clk_disable_unprepare(s3c_ac97.ac97_clk);
-err2:
- snd_soc_set_ac97_ops(NULL);
- return ret;
-}
-
-static int s3c_ac97_remove(struct platform_device *pdev)
-{
- struct resource *irq_res;
-
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (irq_res)
- free_irq(irq_res->start, NULL);
-
- clk_disable_unprepare(s3c_ac97.ac97_clk);
- snd_soc_set_ac97_ops(NULL);
-
- return 0;
-}
-
-static struct platform_driver s3c_ac97_driver = {
- .probe = s3c_ac97_probe,
- .remove = s3c_ac97_remove,
- .driver = {
- .name = "samsung-ac97",
- },
-};
-
-module_platform_driver(s3c_ac97_driver);
-
-MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
-MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:samsung-ac97");
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
new file mode 100644
index 000000000000..48ccc1d1854b
--- /dev/null
+++ b/sound/soc/samsung/aries_wm8994.c
@@ -0,0 +1,702 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/extcon.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/consumer.h>
+#include <linux/input-event-codes.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "i2s.h"
+#include "../codecs/wm8994.h"
+
+#define ARIES_MCLK1_FREQ 24000000
+
+struct aries_wm8994_variant {
+ unsigned int modem_dai_fmt;
+ bool has_fm_radio;
+};
+
+struct aries_wm8994_data {
+ struct extcon_dev *usb_extcon;
+ struct regulator *reg_main_micbias;
+ struct regulator *reg_headset_micbias;
+ struct gpio_desc *gpio_headset_detect;
+ struct gpio_desc *gpio_headset_key;
+ struct gpio_desc *gpio_earpath_sel;
+ struct iio_channel *adc;
+ const struct aries_wm8994_variant *variant;
+};
+
+/* USB dock */
+static struct snd_soc_jack aries_dock;
+
+static struct snd_soc_jack_pin dock_pins[] = {
+ {
+ .pin = "LINE",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static int aries_extcon_notifier(struct notifier_block *this,
+ unsigned long connected, void *_cmd)
+{
+ if (connected)
+ snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
+ SND_JACK_LINEOUT);
+ else
+ snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block aries_extcon_notifier_block = {
+ .notifier_call = aries_extcon_notifier,
+};
+
+/* Headset jack */
+static struct snd_soc_jack aries_headset;
+
+static struct snd_soc_jack_pin jack_pins[] = {
+ {
+ .pin = "HP",
+ .mask = SND_JACK_HEADPHONE,
+ }, {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_zone headset_zones[] = {
+ {
+ .min_mv = 0,
+ .max_mv = 241,
+ .jack_type = SND_JACK_HEADPHONE,
+ }, {
+ .min_mv = 242,
+ .max_mv = 2980,
+ .jack_type = SND_JACK_HEADSET,
+ }, {
+ .min_mv = 2981,
+ .max_mv = UINT_MAX,
+ .jack_type = SND_JACK_HEADPHONE,
+ },
+};
+
+static irqreturn_t headset_det_irq_thread(int irq, void *data)
+{
+ struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
+ int ret = 0;
+ int time_left_ms = 300;
+ int adc;
+
+ while (time_left_ms > 0) {
+ if (!gpiod_get_value(priv->gpio_headset_detect)) {
+ snd_soc_jack_report(&aries_headset, 0,
+ SND_JACK_HEADSET);
+ gpiod_set_value(priv->gpio_earpath_sel, 0);
+ return IRQ_HANDLED;
+ }
+ msleep(20);
+ time_left_ms -= 20;
+ }
+
+ /* Temporarily enable micbias and earpath selector */
+ ret = regulator_enable(priv->reg_headset_micbias);
+ if (ret)
+ pr_err("%s failed to enable micbias: %d", __func__, ret);
+
+ gpiod_set_value(priv->gpio_earpath_sel, 1);
+
+ ret = iio_read_channel_processed(priv->adc, &adc);
+ if (ret < 0) {
+ /* failed to read ADC, so assume headphone */
+ pr_err("%s failed to read ADC, assuming headphones", __func__);
+ snd_soc_jack_report(&aries_headset, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ } else {
+ snd_soc_jack_report(&aries_headset,
+ snd_soc_jack_get_type(&aries_headset, adc),
+ SND_JACK_HEADSET);
+ }
+
+ ret = regulator_disable(priv->reg_headset_micbias);
+ if (ret)
+ pr_err("%s failed disable micbias: %d", __func__, ret);
+
+ /* Disable earpath selector when no mic connected */
+ if (!(aries_headset.status & SND_JACK_MICROPHONE))
+ gpiod_set_value(priv->gpio_earpath_sel, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int headset_button_check(void *data)
+{
+ struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
+
+ /* Filter out keypresses when 4 pole jack not detected */
+ if (gpiod_get_value_cansleep(priv->gpio_headset_key) &&
+ aries_headset.status & SND_JACK_MICROPHONE)
+ return SND_JACK_BTN_0;
+
+ return 0;
+}
+
+static struct snd_soc_jack_gpio headset_button_gpio[] = {
+ {
+ .name = "Media Button",
+ .report = SND_JACK_BTN_0,
+ .debounce_time = 30,
+ .jack_status_check = headset_button_check,
+ },
+};
+
+static int aries_spk_cfg(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_component *component;
+ int ret = 0;
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ /**
+ * We have an odd setup - the SPKMODE pin is pulled up so
+ * we only have access to the left side SPK configs,
+ * but SPKOUTR isn't bridged so when playing back in
+ * stereo, we only get the left hand channel. The only
+ * option we're left with is to force the AIF into mono
+ * mode.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = snd_soc_component_update_bits(component,
+ WM8994_AIF1_DAC1_FILTERS_1,
+ WM8994_AIF1DAC1_MONO, WM8994_AIF1DAC1_MONO);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = snd_soc_component_update_bits(component,
+ WM8994_AIF1_DAC1_FILTERS_1,
+ WM8994_AIF1DAC1_MONO, 0);
+ break;
+ }
+
+ return ret;
+}
+
+static int aries_main_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regulator_enable(priv->reg_main_micbias);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = regulator_disable(priv->reg_main_micbias);
+ break;
+ }
+
+ return ret;
+}
+
+static int aries_headset_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regulator_enable(priv->reg_headset_micbias);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = regulator_disable(priv->reg_headset_micbias);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aries_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Modem In"),
+ SOC_DAPM_PIN_SWITCH("Modem Out"),
+};
+
+static const struct snd_soc_dapm_widget aries_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("HP", NULL),
+
+ SND_SOC_DAPM_SPK("SPK", aries_spk_cfg),
+ SND_SOC_DAPM_SPK("RCV", NULL),
+
+ SND_SOC_DAPM_LINE("LINE", NULL),
+
+ SND_SOC_DAPM_MIC("Main Mic", aries_main_bias),
+ SND_SOC_DAPM_MIC("Headset Mic", aries_headset_bias),
+
+ SND_SOC_DAPM_MIC("Bluetooth Mic", NULL),
+ SND_SOC_DAPM_SPK("Bluetooth SPK", NULL),
+
+ SND_SOC_DAPM_LINE("Modem In", NULL),
+ SND_SOC_DAPM_LINE("Modem Out", NULL),
+
+ /* This must be last as it is conditionally not used */
+ SND_SOC_DAPM_LINE("FM In", NULL),
+};
+
+static int aries_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int pll_out;
+ int ret;
+
+ /* AIF1CLK should be >=3MHz for optimal performance */
+ if (params_width(params) == 24)
+ pll_out = params_rate(params) * 384;
+ else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+ pll_out = params_rate(params) * 512;
+ else
+ pll_out = params_rate(params) * 256;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
+ ARIES_MCLK1_FREQ, pll_out);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int aries_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ /* Switch sysclk to MCLK1 */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
+ ARIES_MCLK1_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Stop PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
+ ARIES_MCLK1_FREQ, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Main DAI operations
+ */
+static const struct snd_soc_ops aries_ops = {
+ .hw_params = aries_hw_params,
+ .hw_free = aries_hw_free,
+};
+
+static int aries_baseband_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int pll_out;
+ int ret;
+
+ pll_out = 8000 * 512;
+
+ /* Set the codec FLL */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1,
+ ARIES_MCLK1_FREQ, pll_out);
+ if (ret < 0)
+ return ret;
+
+ /* Set the codec system clock */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int aries_late_probe(struct snd_soc_card *card)
+{
+ struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
+ int ret, irq;
+
+ ret = snd_soc_card_jack_new_pins(card, "Dock", SND_JACK_LINEOUT,
+ &aries_dock, dock_pins, ARRAY_SIZE(dock_pins));
+ if (ret)
+ return ret;
+
+ ret = devm_extcon_register_notifier(card->dev,
+ priv->usb_extcon, EXTCON_JACK_LINE_OUT,
+ &aries_extcon_notifier_block);
+ if (ret)
+ return ret;
+
+ if (extcon_get_state(priv->usb_extcon,
+ EXTCON_JACK_LINE_OUT) > 0)
+ snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
+ SND_JACK_LINEOUT);
+ else
+ snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &aries_headset,
+ jack_pins, ARRAY_SIZE(jack_pins));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_zones(&aries_headset, ARRAY_SIZE(headset_zones),
+ headset_zones);
+ if (ret)
+ return ret;
+
+ irq = gpiod_to_irq(priv->gpio_headset_detect);
+ if (irq < 0) {
+ dev_err(card->dev, "Failed to map headset detect gpio to irq");
+ return -EINVAL;
+ }
+
+ ret = devm_request_threaded_irq(card->dev, irq, NULL,
+ headset_det_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT, "headset_detect", priv);
+ if (ret) {
+ dev_err(card->dev, "Failed to request headset detect irq");
+ return ret;
+ }
+
+ headset_button_gpio[0].data = priv;
+ headset_button_gpio[0].desc = priv->gpio_headset_key;
+
+ snd_jack_set_key(aries_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
+
+ return snd_soc_jack_add_gpios(&aries_headset,
+ ARRAY_SIZE(headset_button_gpio), headset_button_gpio);
+}
+
+static const struct snd_soc_pcm_stream baseband_params = {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+};
+
+static const struct snd_soc_pcm_stream bluetooth_params = {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+};
+
+static const struct snd_soc_dapm_widget aries_modem_widgets[] = {
+ SND_SOC_DAPM_INPUT("Modem RX"),
+ SND_SOC_DAPM_OUTPUT("Modem TX"),
+};
+
+static const struct snd_soc_dapm_route aries_modem_routes[] = {
+ { "Modem Capture", NULL, "Modem RX" },
+ { "Modem TX", NULL, "Modem Playback" },
+};
+
+static const struct snd_soc_component_driver aries_component = {
+ .name = "aries-audio",
+ .dapm_widgets = aries_modem_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aries_modem_widgets),
+ .dapm_routes = aries_modem_routes,
+ .num_dapm_routes = ARRAY_SIZE(aries_modem_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static struct snd_soc_dai_driver aries_ext_dai[] = {
+ {
+ .name = "Voice call",
+ .playback = {
+ .stream_name = "Modem Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Modem Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+SND_SOC_DAILINK_DEFS(aif1,
+ DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("Voice call")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")));
+
+SND_SOC_DAILINK_DEFS(bluetooth,
+ DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")));
+
+static struct snd_soc_dai_link aries_dai[] = {
+ {
+ .name = "WM8994 AIF1",
+ .stream_name = "HiFi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &aries_ops,
+ SND_SOC_DAILINK_REG(aif1),
+ },
+ {
+ .name = "WM8994 AIF2",
+ .stream_name = "Baseband",
+ .init = &aries_baseband_init,
+ .c2c_params = &baseband_params,
+ .num_c2c_params = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(baseband),
+ },
+ {
+ .name = "WM8994 AIF3",
+ .stream_name = "Bluetooth",
+ .c2c_params = &bluetooth_params,
+ .num_c2c_params = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(bluetooth),
+ },
+};
+
+static struct snd_soc_card aries_card = {
+ .name = "ARIES",
+ .owner = THIS_MODULE,
+ .dai_link = aries_dai,
+ .num_links = ARRAY_SIZE(aries_dai),
+ .controls = aries_controls,
+ .num_controls = ARRAY_SIZE(aries_controls),
+ .dapm_widgets = aries_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aries_dapm_widgets),
+ .late_probe = aries_late_probe,
+};
+
+static const struct aries_wm8994_variant fascinate4g_variant = {
+ .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_IB_NF,
+ .has_fm_radio = false,
+};
+
+static const struct aries_wm8994_variant aries_variant = {
+ .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBP_CFP
+ | SND_SOC_DAIFMT_IB_NF,
+ .has_fm_radio = true,
+};
+
+static const struct of_device_id samsung_wm8994_of_match[] = {
+ {
+ .compatible = "samsung,fascinate4g-wm8994",
+ .data = &fascinate4g_variant,
+ },
+ {
+ .compatible = "samsung,aries-wm8994",
+ .data = &aries_variant,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
+
+static int aries_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *cpu, *codec, *extcon_np;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card = &aries_card;
+ struct aries_wm8994_data *priv;
+ struct snd_soc_dai_link *dai_link;
+ const struct of_device_id *match;
+ enum iio_chan_type channel_type;
+ int ret, i;
+
+ if (!np)
+ return -EINVAL;
+
+ card->dev = dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ match = of_match_node(samsung_wm8994_of_match, np);
+ priv->variant = match->data;
+
+ /* Remove FM widget if not present */
+ if (!priv->variant->has_fm_radio)
+ card->num_dapm_widgets--;
+
+ priv->reg_main_micbias = devm_regulator_get(dev, "main-micbias");
+ if (IS_ERR(priv->reg_main_micbias)) {
+ dev_err(dev, "Failed to get main micbias regulator\n");
+ return PTR_ERR(priv->reg_main_micbias);
+ }
+
+ priv->reg_headset_micbias = devm_regulator_get(dev, "headset-micbias");
+ if (IS_ERR(priv->reg_headset_micbias)) {
+ dev_err(dev, "Failed to get headset micbias regulator\n");
+ return PTR_ERR(priv->reg_headset_micbias);
+ }
+
+ priv->gpio_earpath_sel = devm_gpiod_get(dev, "earpath-sel",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpio_earpath_sel)) {
+ dev_err(dev, "Failed to get earpath selector gpio");
+ return PTR_ERR(priv->gpio_earpath_sel);
+ }
+
+ extcon_np = of_parse_phandle(np, "extcon", 0);
+ priv->usb_extcon = extcon_find_edev_by_node(extcon_np);
+ of_node_put(extcon_np);
+ if (IS_ERR(priv->usb_extcon))
+ return dev_err_probe(dev, PTR_ERR(priv->usb_extcon),
+ "Failed to get extcon device");
+
+ priv->adc = devm_iio_channel_get(dev, "headset-detect");
+ if (IS_ERR(priv->adc))
+ return dev_err_probe(dev, PTR_ERR(priv->adc),
+ "Failed to get ADC channel");
+
+ ret = iio_get_channel_type(priv->adc, &channel_type);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get ADC channel type");
+ if (channel_type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
+ GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_key)) {
+ dev_err(dev, "Failed to get headset key gpio");
+ return PTR_ERR(priv->gpio_headset_key);
+ }
+
+ priv->gpio_headset_detect = devm_gpiod_get(dev,
+ "headset-detect", GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_detect)) {
+ dev_err(dev, "Failed to get headset detect gpio");
+ return PTR_ERR(priv->gpio_headset_detect);
+ }
+
+ /* Update card-name if provided through DT, else use default name */
+ snd_soc_of_parse_card_name(card, "model");
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret < 0) {
+ /* Backwards compatible way */
+ ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+ if (ret < 0) {
+ dev_err(dev, "Audio routing invalid/unspecified\n");
+ return ret;
+ }
+ }
+
+ aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt;
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ if (!cpu)
+ return -EINVAL;
+
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ if (!codec) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ dai_link->codecs->of_node = of_parse_phandle(codec,
+ "sound-dai", 0);
+ if (!dai_link->codecs->of_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* Set CPU and platform of_node for main DAI */
+ aries_dai[0].cpus->of_node = of_parse_phandle(cpu,
+ "sound-dai", 0);
+ if (!aries_dai[0].cpus->of_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node;
+
+ /* Set CPU of_node for BT DAI */
+ aries_dai[2].cpus->of_node = of_parse_phandle(cpu,
+ "sound-dai", 1);
+ if (!aries_dai[2].cpus->of_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &aries_component,
+ aries_ext_dai, ARRAY_SIZE(aries_ext_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto out;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ dev_err(dev, "snd_soc_register_card() failed:%d\n", ret);
+
+out:
+ of_node_put(cpu);
+ of_node_put(codec);
+
+ return ret;
+}
+
+static struct platform_driver aries_audio_driver = {
+ .driver = {
+ .name = "aries-audio-wm8994",
+ .of_match_table = of_match_ptr(samsung_wm8994_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = aries_audio_probe,
+};
+
+module_platform_driver(aries_audio_driver);
+
+MODULE_DESCRIPTION("ALSA SoC ARIES WM8994");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:aries-audio-wm8994");
diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c
new file mode 100644
index 000000000000..172943bb3b24
--- /dev/null
+++ b/sound/soc/samsung/arndale.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2014, Insignal Co., Ltd.
+//
+// Author: Claude <claude@insginal.co.kr>
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/wm8994.h"
+#include "i2s.h"
+
+static int arndale_rt5631_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int rfs, ret;
+ unsigned long rclk;
+
+ rfs = 256;
+
+ rclk = params_rate(params) * rfs;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+ 0, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
+ 0, SND_SOC_CLOCK_OUT);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops arndale_rt5631_ops = {
+ .hw_params = arndale_rt5631_hw_params,
+};
+
+static int arndale_wm1811_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rfs, rclk;
+
+ /* Ensure AIF1CLK is >= 3 MHz for optimal performance */
+ if (params_width(params) == 24)
+ rfs = 384;
+ else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+ rfs = 512;
+ else
+ rfs = 256;
+
+ rclk = params_rate(params) * rfs;
+
+ /*
+ * We add 1 to the frequency value to ensure proper EPLL setting
+ * for each audio sampling rate (see epll_24mhz_tbl in drivers/clk/
+ * samsung/clk-exynos5250.c for list of available EPLL rates).
+ * The CODEC uses clk API and the value will be rounded hence the MCLK1
+ * clock's frequency will still be exact multiple of the sample rate.
+ */
+ return snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
+ rclk + 1, SND_SOC_CLOCK_IN);
+}
+
+static const struct snd_soc_ops arndale_wm1811_ops = {
+ .hw_params = arndale_wm1811_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(rt5631_hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link arndale_rt5631_dai[] = {
+ {
+ .name = "RT5631 HiFi",
+ .stream_name = "Primary",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &arndale_rt5631_ops,
+ SND_SOC_DAILINK_REG(rt5631_hifi),
+ },
+};
+
+SND_SOC_DAILINK_DEFS(wm1811_hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link arndale_wm1811_dai[] = {
+ {
+ .name = "WM1811 HiFi",
+ .stream_name = "Primary",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .ops = &arndale_wm1811_ops,
+ SND_SOC_DAILINK_REG(wm1811_hifi),
+ },
+};
+
+static struct snd_soc_card arndale_rt5631 = {
+ .name = "Arndale RT5631",
+ .owner = THIS_MODULE,
+ .dai_link = arndale_rt5631_dai,
+ .num_links = ARRAY_SIZE(arndale_rt5631_dai),
+};
+
+static struct snd_soc_card arndale_wm1811 = {
+ .name = "Arndale WM1811",
+ .owner = THIS_MODULE,
+ .dai_link = arndale_wm1811_dai,
+ .num_links = ARRAY_SIZE(arndale_wm1811_dai),
+};
+
+static void arndale_put_of_nodes(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ int i;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ of_node_put(dai_link->cpus->of_node);
+ of_node_put(dai_link->codecs->of_node);
+ }
+}
+
+static int arndale_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *dai_link;
+ int ret;
+
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ card->dev = &pdev->dev;
+ dai_link = card->dai_link;
+
+ dai_link->cpus->of_node = of_parse_phandle(np, "samsung,audio-cpu", 0);
+ if (!dai_link->cpus->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'samsung,audio-cpu' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ if (!dai_link->platforms->name)
+ dai_link->platforms->of_node = dai_link->cpus->of_node;
+
+ dai_link->codecs->of_node = of_parse_phandle(np, "samsung,audio-codec", 0);
+ if (!dai_link->codecs->of_node) {
+ dev_err(&pdev->dev,
+ "Property 'samsung,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err_put_of_nodes;
+ }
+
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed\n");
+ goto err_put_of_nodes;
+ }
+ return 0;
+
+err_put_of_nodes:
+ arndale_put_of_nodes(card);
+ return ret;
+}
+
+static void arndale_audio_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ arndale_put_of_nodes(card);
+}
+
+static const struct of_device_id arndale_audio_of_match[] = {
+ { .compatible = "samsung,arndale-rt5631", .data = &arndale_rt5631 },
+ { .compatible = "samsung,arndale-alc5631", .data = &arndale_rt5631 },
+ { .compatible = "samsung,arndale-wm1811", .data = &arndale_wm1811 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, arndale_audio_of_match);
+
+static struct platform_driver arndale_audio_driver = {
+ .driver = {
+ .name = "arndale-audio",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = arndale_audio_of_match,
+ },
+ .probe = arndale_audio_probe,
+ .remove = arndale_audio_remove,
+};
+
+module_platform_driver(arndale_audio_driver);
+
+MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
+MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c
deleted file mode 100644
index ee1fda92f2f4..000000000000
--- a/sound/soc/samsung/arndale_rt5631.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * arndale_rt5631.c
- *
- * Copyright (c) 2014, Insignal Co., Ltd.
- *
- * Author: Claude <claude@insginal.co.kr>
- *
- * 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/platform_device.h>
-#include <linux/clk.h>
-
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include "i2s.h"
-
-static int arndale_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int rfs, ret;
- unsigned long rclk;
-
- rfs = 256;
-
- rclk = params_rate(params) * rfs;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
- 0, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
- 0, SND_SOC_CLOCK_OUT);
-
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops arndale_ops = {
- .hw_params = arndale_hw_params,
-};
-
-static struct snd_soc_dai_link arndale_rt5631_dai[] = {
- {
- .name = "RT5631 HiFi",
- .stream_name = "Primary",
- .codec_dai_name = "rt5631-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .ops = &arndale_ops,
- },
-};
-
-static struct snd_soc_card arndale_rt5631 = {
- .name = "Arndale RT5631",
- .owner = THIS_MODULE,
- .dai_link = arndale_rt5631_dai,
- .num_links = ARRAY_SIZE(arndale_rt5631_dai),
-};
-
-static int arndale_audio_probe(struct platform_device *pdev)
-{
- int n, ret;
- struct device_node *np = pdev->dev.of_node;
- struct snd_soc_card *card = &arndale_rt5631;
-
- card->dev = &pdev->dev;
-
- for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) {
- if (!arndale_rt5631_dai[n].cpu_dai_name) {
- arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np,
- "samsung,audio-cpu", n);
-
- if (!arndale_rt5631_dai[n].cpu_of_node) {
- dev_err(&pdev->dev,
- "Property 'samsung,audio-cpu' missing or invalid\n");
- return -EINVAL;
- }
- }
- if (!arndale_rt5631_dai[n].platform_name)
- arndale_rt5631_dai[n].platform_of_node =
- arndale_rt5631_dai[n].cpu_of_node;
-
- arndale_rt5631_dai[n].codec_name = NULL;
- arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np,
- "samsung,audio-codec", n);
- if (!arndale_rt5631_dai[0].codec_of_node) {
- dev_err(&pdev->dev,
- "Property 'samsung,audio-codec' missing or invalid\n");
- return -EINVAL;
- }
- }
-
- ret = devm_snd_soc_register_card(card->dev, card);
-
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
-
- return ret;
-}
-
-static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = {
- { .compatible = "samsung,arndale-rt5631", },
- { .compatible = "samsung,arndale-alc5631", },
- {},
-};
-MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match);
-
-static struct platform_driver arndale_audio_driver = {
- .driver = {
- .name = "arndale-audio",
- .pm = &snd_soc_pm_ops,
- .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match),
- },
- .probe = arndale_audio_probe,
-};
-
-module_platform_driver(arndale_audio_driver);
-
-MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
-MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index 3dd246fa0059..fc4963d3b99b 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -1,18 +1,12 @@
-/*
- * Bells audio support
- *
- * Copyright 2012 Wolfson Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Bells audio support
+//
+// Copyright 2012 Wolfson Microelectronics
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
-#include <linux/gpio.h>
#include <linux/module.h>
#include "../codecs/wm5102.h"
@@ -60,23 +54,23 @@ static int bells_set_bias_level(struct snd_soc_card *card,
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct bells_drvdata *bells = card->drvdata;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
- codec_dai = rtd->codec_dai;
- codec = codec_dai->codec;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ component = codec_dai->component;
- if (dapm->dev != codec_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
+ if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_STANDBY)
break;
- ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
+ ret = snd_soc_component_set_pll(component, WM5102_FLL1,
ARIZONA_FLL_SRC_MCLK1,
MCLK_RATE,
bells->sysclk_rate);
@@ -84,7 +78,7 @@ static int bells_set_bias_level(struct snd_soc_card *card,
pr_err("Failed to start FLL: %d\n", ret);
if (bells->asyncclk_rate) {
- ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
+ ret = snd_soc_component_set_pll(component, WM5102_FLL2,
ARIZONA_FLL_SRC_AIF2BCLK,
BCLK2_RATE,
bells->asyncclk_rate);
@@ -106,27 +100,27 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct bells_drvdata *bells = card->drvdata;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
- codec_dai = rtd->codec_dai;
- codec = codec_dai->codec;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ component = codec_dai->component;
- if (dapm->dev != codec_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_STANDBY:
- ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
+ ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0);
if (ret < 0) {
pr_err("Failed to stop FLL: %d\n", ret);
return ret;
}
if (bells->asyncclk_rate) {
- ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
+ ret = snd_soc_component_set_pll(component, WM5102_FLL2,
0, 0, 0);
if (ret < 0) {
pr_err("Failed to stop FLL: %d\n", ret);
@@ -139,8 +133,6 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
@@ -148,31 +140,31 @@ static int bells_late_probe(struct snd_soc_card *card)
{
struct bells_drvdata *bells = card->drvdata;
struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_codec *wm0010;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *wm0010;
+ struct snd_soc_component *component;
struct snd_soc_dai *aif1_dai;
struct snd_soc_dai *aif2_dai;
struct snd_soc_dai *aif3_dai;
struct snd_soc_dai *wm9081_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name);
- wm0010 = rtd->codec;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]);
+ wm0010 = snd_soc_rtd_to_codec(rtd, 0)->component;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
- codec = rtd->codec;
- aif1_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
+ component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
- ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
+ ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,
ARIZONA_CLK_SRC_FLL1,
bells->sysclk_rate,
SND_SOC_CLOCK_IN);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+ dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
return ret;
}
- ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
+ ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
if (ret != 0) {
dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
return ret;
@@ -182,25 +174,25 @@ static int bells_late_probe(struct snd_soc_card *card)
if (ret != 0)
dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
- ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
+ ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0,
SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
if (ret != 0)
- dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
+ dev_err(component->dev, "Failed to set OPCLK: %d\n", ret);
if (card->num_rtd == DAI_CODEC_CP)
return 0;
- ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
+ ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,
ARIZONA_CLK_SRC_FLL2,
bells->asyncclk_rate,
SND_SOC_CLOCK_IN);
if (ret != 0) {
- dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret);
+ dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret);
return ret;
}
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name);
- aif2_dai = rtd->cpu_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]);
+ aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);
ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
if (ret != 0) {
@@ -211,9 +203,9 @@ static int bells_late_probe(struct snd_soc_card *card)
if (card->num_rtd == DAI_CODEC_SUB)
return 0;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name);
- aif3_dai = rtd->cpu_dai;
- wm9081_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]);
+ aif3_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ wm9081_dai = snd_soc_rtd_to_codec(rtd, 0);
ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
if (ret != 0) {
@@ -221,7 +213,7 @@ static int bells_late_probe(struct snd_soc_card *card)
return ret;
}
- ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
+ ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK,
0, SYS_MCLK_RATE, 0);
if (ret != 0) {
dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
@@ -247,134 +239,162 @@ static const struct snd_soc_pcm_stream sub_params = {
.channels_max = 2,
};
+SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(wm2200_dsp_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm2200.1-003a", "wm2200")));
+
static struct snd_soc_dai_link bells_dai_wm2200[] = {
{
.name = "CPU-DSP",
.stream_name = "CPU-DSP",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm0010-sdi1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "spi0.0",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(wm2200_cpu_dsp),
},
{
.name = "DSP-CODEC",
.stream_name = "DSP-CODEC",
- .cpu_dai_name = "wm0010-sdi2",
- .codec_dai_name = "wm2200",
- .codec_name = "wm2200.1-003a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .params = &sub_params,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .c2c_params = &sub_params,
+ .num_c2c_params = 1,
.ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(wm2200_dsp_codec),
},
};
+SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(wm5102_dsp_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1")));
+
+SND_SOC_DAILINK_DEFS(wm5102_baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
+
+SND_SOC_DAILINK_DEFS(wm5102_sub,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
+
static struct snd_soc_dai_link bells_dai_wm5102[] = {
{
.name = "CPU-DSP",
.stream_name = "CPU-DSP",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm0010-sdi1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "spi0.0",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(wm5102_cpu_dsp),
},
{
.name = "DSP-CODEC",
.stream_name = "DSP-CODEC",
- .cpu_dai_name = "wm0010-sdi2",
- .codec_dai_name = "wm5102-aif1",
- .codec_name = "wm5102-codec",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .params = &sub_params,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .c2c_params = &sub_params,
+ .num_c2c_params = 1,
.ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(wm5102_dsp_codec),
},
{
.name = "Baseband",
.stream_name = "Baseband",
- .cpu_dai_name = "wm5102-aif2",
- .codec_dai_name = "wm1250-ev1",
- .codec_name = "wm1250-ev1.1-0027",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
- .params = &baseband_params,
+ .c2c_params = &baseband_params,
+ .num_c2c_params = 1,
+ SND_SOC_DAILINK_REG(wm5102_baseband),
},
{
.name = "Sub",
.stream_name = "Sub",
- .cpu_dai_name = "wm5102-aif3",
- .codec_dai_name = "wm9081-hifi",
- .codec_name = "wm9081.1-006c",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.ignore_suspend = 1,
- .params = &sub_params,
+ .c2c_params = &sub_params,
+ .num_c2c_params = 1,
+ SND_SOC_DAILINK_REG(wm5102_sub),
},
};
+SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(wm5110_dsp_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1")));
+
+SND_SOC_DAILINK_DEFS(wm5110_baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
+
+
+SND_SOC_DAILINK_DEFS(wm5110_sub,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
+
static struct snd_soc_dai_link bells_dai_wm5110[] = {
{
.name = "CPU-DSP",
.stream_name = "CPU-DSP",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm0010-sdi1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "spi0.0",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(wm5110_cpu_dsp),
},
{
.name = "DSP-CODEC",
.stream_name = "DSP-CODEC",
- .cpu_dai_name = "wm0010-sdi2",
- .codec_dai_name = "wm5110-aif1",
- .codec_name = "wm5110-codec",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .params = &sub_params,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .c2c_params = &sub_params,
+ .num_c2c_params = 1,
.ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(wm5110_dsp_codec),
},
{
.name = "Baseband",
.stream_name = "Baseband",
- .cpu_dai_name = "wm5110-aif2",
- .codec_dai_name = "wm1250-ev1",
- .codec_name = "wm1250-ev1.1-0027",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
- .params = &baseband_params,
+ .c2c_params = &baseband_params,
+ .num_c2c_params = 1,
+ SND_SOC_DAILINK_REG(wm5110_baseband),
},
{
.name = "Sub",
.stream_name = "Sub",
- .cpu_dai_name = "wm5110-aif3",
- .codec_dai_name = "wm9081-hifi",
- .codec_name = "wm9081.1-006c",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
+ | SND_SOC_DAIFMT_CBC_CFC,
.ignore_suspend = 1,
- .params = &sub_params,
+ .c2c_params = &sub_params,
+ .num_c2c_params = 1,
+ SND_SOC_DAILINK_REG(wm5110_sub),
},
};
static struct snd_soc_codec_conf bells_codec_conf[] = {
{
- .dev_name = "wm9081.1-006c",
+ .dlc = COMP_CODEC_CONF("wm9081.1-006c"),
.name_prefix = "Sub",
},
};
-static struct snd_soc_dapm_widget bells_widgets[] = {
+static const struct snd_soc_dapm_widget bells_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
-static struct snd_soc_dapm_route bells_routes[] = {
+static const struct snd_soc_dapm_route bells_routes[] = {
{ "Sub CLK_SYS", NULL, "OPCLK" },
{ "CLKIN", NULL, "OPCLK" },
@@ -446,7 +466,6 @@ static struct snd_soc_card bells_cards[] = {
},
};
-
static int bells_probe(struct platform_device *pdev)
{
int ret;
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index a7616cc9b39e..7b5d4556e0fd 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -1,32 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * dma.h --
- *
- * 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.
- *
- * ALSA PCM interface for the Samsung SoC
+ * ALSA PCM interface for the Samsung SoC
*/
-#ifndef _S3C_AUDIO_H
-#define _S3C_AUDIO_H
+#ifndef _SAMSUNG_DMA_H
+#define _SAMSUNG_DMA_H
#include <sound/dmaengine_pcm.h>
-#include <linux/dmaengine.h>
-struct s3c_dma_params {
- void *slave; /* Channel ID */
- dma_addr_t dma_addr;
- int dma_size; /* Size of the DMA transfer */
- char *ch_name;
- struct snd_dmaengine_dai_dma_data dma_data;
-};
-
-void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
- struct s3c_dma_params *playback,
- struct s3c_dma_params *capture);
-int samsung_asoc_dma_platform_register(struct device *dev,
- dma_filter_fn fn);
-
-#endif
+/*
+ * @tx, @rx arguments can be NULL if the DMA channel names are "tx", "rx",
+ * otherwise actual DMA channel names must be passed to this function.
+ */
+int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
+ const char *tx, const char *rx,
+ struct device *dma_dev);
+#endif /* _SAMSUNG_DMA_H */
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 063125937311..2802789a323e 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -1,72 +1,38 @@
-/*
- * dmaengine.c - Samsung dmaengine wrapper
- *
- * Author: Mark Brown <broonie@linaro.org>
- * Copyright 2013 Linaro
- *
- * This program is free software; you can 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// dmaengine.c - Samsung dmaengine wrapper
+//
+// Author: Mark Brown <broonie@linaro.org>
+// Copyright 2013 Linaro
#include <linux/module.h>
-#include <linux/amba/pl08x.h>
-#include <linux/platform_data/dma-s3c24xx.h>
-
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/dmaengine_pcm.h>
#include <sound/soc.h>
-#include <sound/soc-dai.h>
#include "dma.h"
-static struct snd_dmaengine_pcm_config samsung_dmaengine_pcm_config = {
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
-};
-
-void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
- struct s3c_dma_params *playback,
- struct s3c_dma_params *capture)
+int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
+ const char *tx, const char *rx,
+ struct device *dma_dev)
{
- struct snd_dmaengine_dai_dma_data *playback_data = NULL;
- struct snd_dmaengine_dai_dma_data *capture_data = NULL;
+ struct snd_dmaengine_pcm_config *pcm_conf;
- if (playback) {
- playback_data = &playback->dma_data;
- playback_data->filter_data = playback->slave;
- playback_data->chan_name = playback->ch_name;
- playback_data->addr = playback->dma_addr;
- playback_data->addr_width = playback->dma_size;
- }
- if (capture) {
- capture_data = &capture->dma_data;
- capture_data->filter_data = capture->slave;
- capture_data->chan_name = capture->ch_name;
- capture_data->addr = capture->dma_addr;
- capture_data->addr_width = capture->dma_size;
- }
+ pcm_conf = devm_kzalloc(dev, sizeof(*pcm_conf), GFP_KERNEL);
+ if (!pcm_conf)
+ return -ENOMEM;
- snd_soc_dai_init_dma_data(dai, playback_data, capture_data);
-}
-EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
+ pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
+ pcm_conf->compat_filter_fn = filter;
+ pcm_conf->dma_dev = dma_dev;
-int samsung_asoc_dma_platform_register(struct device *dev,
- dma_filter_fn filter)
-{
- samsung_dmaengine_pcm_config.compat_filter_fn = filter;
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
- return devm_snd_dmaengine_pcm_register(dev,
- &samsung_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
- SND_DMAENGINE_PCM_FLAG_COMPAT);
+ return devm_snd_dmaengine_pcm_register(dev, pcm_conf,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c
deleted file mode 100644
index 5f5825faeb2a..000000000000
--- a/sound/soc/samsung/h1940_uda1380.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * h1940-uda1380.c -- ALSA Soc Audio Layer
- *
- * Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
- * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
- *
- * Based on version from 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/types.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include "regs-iis.h"
-#include <asm/mach-types.h>
-
-#include <mach/gpio-samsung.h>
-#include "s3c24xx-i2s.h"
-
-static const unsigned int rates[] = {
- 11025,
- 22050,
- 44100,
-};
-
-static const struct snd_pcm_hw_constraint_list hw_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
-};
-
-static struct snd_soc_jack hp_jack;
-
-static struct snd_soc_jack_pin hp_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "Speaker",
- .mask = SND_JACK_HEADPHONE,
- .invert = 1,
- },
-};
-
-static struct snd_soc_jack_gpio hp_jack_gpios[] = {
- {
- .gpio = S3C2410_GPG(4),
- .name = "hp-gpio",
- .report = SND_JACK_HEADPHONE,
- .invert = 1,
- .debounce_time = 200,
- },
-};
-
-static int h1940_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_rates);
-}
-
-static int h1940_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int div;
- int ret;
- unsigned int rate = params_rate(params);
-
- switch (rate) {
- case 11025:
- case 22050:
- case 44100:
- div = s3c24xx_i2s_get_clockrate() / (384 * rate);
- if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate))
- div++;
- break;
- default:
- dev_err(rtd->dev, "%s: rate %d is not supported\n",
- __func__, rate);
- return -EINVAL;
- }
-
- /* select clock source */
- ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- /* set MCLK division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
- S3C2410_IISMOD_384FS);
- if (ret < 0)
- return ret;
-
- /* set BCLK division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
- S3C2410_IISMOD_32FS);
- if (ret < 0)
- return ret;
-
- /* set prescaler division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
- S3C24XX_PRESCALE(div, div));
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops h1940_ops = {
- .startup = h1940_startup,
- .hw_params = h1940_hw_params,
-};
-
-static int h1940_spk_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- gpio_set_value(S3C_GPIO_END + 9, 1);
- else
- gpio_set_value(S3C_GPIO_END + 9, 0);
-
- return 0;
-}
-
-/* h1940 machine dapm widgets */
-static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", h1940_spk_power),
-};
-
-/* h1940 machine audio_map */
-static const struct snd_soc_dapm_route audio_map[] = {
- /* headphone connected to VOUTLHP, VOUTRHP */
- {"Headphone Jack", NULL, "VOUTLHP"},
- {"Headphone Jack", NULL, "VOUTRHP"},
-
- /* ext speaker connected to VOUTL, VOUTR */
- {"Speaker", NULL, "VOUTL"},
- {"Speaker", NULL, "VOUTR"},
-
- /* mic is connected to VINM */
- {"VINM", NULL, "Mic Jack"},
-};
-
-static struct platform_device *s3c24xx_snd_device;
-
-static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
-{
- snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
- &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
-
- snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
- hp_jack_gpios);
-
- return 0;
-}
-
-static int h1940_uda1380_card_remove(struct snd_soc_card *card)
-{
- snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
- hp_jack_gpios);
-
- return 0;
-}
-
-/* s3c24xx digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link h1940_uda1380_dai[] = {
- {
- .name = "uda1380",
- .stream_name = "UDA1380 Duplex",
- .cpu_dai_name = "s3c24xx-iis",
- .codec_dai_name = "uda1380-hifi",
- .init = h1940_uda1380_init,
- .platform_name = "s3c24xx-iis",
- .codec_name = "uda1380-codec.0-001a",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &h1940_ops,
- },
-};
-
-static struct snd_soc_card h1940_asoc = {
- .name = "h1940",
- .owner = THIS_MODULE,
- .remove = h1940_uda1380_card_remove,
- .dai_link = h1940_uda1380_dai,
- .num_links = ARRAY_SIZE(h1940_uda1380_dai),
-
- .dapm_widgets = uda1380_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static int __init h1940_init(void)
-{
- int ret;
-
- if (!machine_is_h1940())
- return -ENODEV;
-
- /* configure some gpios */
- ret = gpio_request(S3C_GPIO_END + 9, "speaker-power");
- if (ret)
- goto err_out;
-
- ret = gpio_direction_output(S3C_GPIO_END + 9, 0);
- if (ret)
- goto err_gpio;
-
- s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
- if (!s3c24xx_snd_device) {
- ret = -ENOMEM;
- goto err_gpio;
- }
-
- platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc);
- ret = platform_device_add(s3c24xx_snd_device);
-
- if (ret)
- goto err_plat;
-
- return 0;
-
-err_plat:
- platform_device_put(s3c24xx_snd_device);
-err_gpio:
- gpio_free(S3C_GPIO_END + 9);
-
-err_out:
- return ret;
-}
-
-static void __exit h1940_exit(void)
-{
- platform_device_unregister(s3c24xx_snd_device);
- gpio_free(S3C_GPIO_END + 9);
-}
-
-module_init(h1940_init);
-module_exit(h1940_exit);
-
-/* Module information */
-MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick");
-MODULE_DESCRIPTION("ALSA SoC H1940");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index 9170c311d66e..138e95581979 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -1,15 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * linux/sound/soc/samsung/i2s-regs.h
- *
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung I2S driver's register header
- *
- * 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 __SND_SOC_SAMSUNG_I2S_REGS_H
@@ -65,11 +59,12 @@
#define CON_RXDMA_ACTIVE (1 << 1)
#define CON_ACTIVE (1 << 0)
-#define MOD_OPCLK_CDCLK_OUT (0 << 30)
-#define MOD_OPCLK_CDCLK_IN (1 << 30)
-#define MOD_OPCLK_BCLK_OUT (2 << 30)
-#define MOD_OPCLK_PCLK (3 << 30)
-#define MOD_OPCLK_MASK (3 << 30)
+#define MOD_OPCLK_SHIFT 30
+#define MOD_OPCLK_CDCLK_OUT (0 << MOD_OPCLK_SHIFT)
+#define MOD_OPCLK_CDCLK_IN (1 << MOD_OPCLK_SHIFT)
+#define MOD_OPCLK_BCLK_OUT (2 << MOD_OPCLK_SHIFT)
+#define MOD_OPCLK_PCLK (3 << MOD_OPCLK_SHIFT)
+#define MOD_OPCLK_MASK (3 << MOD_OPCLK_SHIFT)
#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
#define MOD_BLCS_SHIFT 26
@@ -137,6 +132,7 @@
#define EXYNOS7_MOD_RCLK_192FS 7
#define PSR_PSREN (1 << 15)
+#define PSR_PSVAL(x) ((((x) - 1) << 8) & 0x3f00)
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
@@ -160,5 +156,3 @@
#define I2SSIZE_SHIFT (16)
#endif /* __SND_SOC_SAMSUNG_I2S_REGS_H */
-
-
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 84d9e77c0fbe..e9964f0e010a 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1,14 +1,9 @@
-/* sound/soc/samsung/i2s.c
- *
- * ALSA SoC Audio Layer - Samsung I2S Controller driver
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd.
- * Jaswinder Singh <jassisinghbrar@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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Audio Layer - Samsung I2S Controller driver
+//
+// Copyright (c) 2010 Samsung Electronics Co. Ltd.
+// Jaswinder Singh <jassisinghbrar@gmail.com>
#include <dt-bindings/sound/samsung-i2s.h>
#include <linux/delay.h>
@@ -18,7 +13,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
@@ -33,10 +27,8 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
-enum samsung_dai_type {
- TYPE_PRI,
- TYPE_SEC,
-};
+#define SAMSUNG_I2S_ID_PRIMARY 1
+#define SAMSUNG_I2S_ID_SECONDARY 2
struct samsung_i2s_variant_regs {
unsigned int bfs_off;
@@ -53,72 +45,98 @@ struct samsung_i2s_variant_regs {
};
struct samsung_i2s_dai_data {
- int dai_type;
u32 quirks;
+ unsigned int pcm_rates;
const struct samsung_i2s_variant_regs *i2s_variant_regs;
+ void (*fixup_early)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+ void (*fixup_late)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
};
struct i2s_dai {
/* Platform device for this DAI */
struct platform_device *pdev;
- /* Memory mapped SFR region */
- void __iomem *addr;
- /* Rate of RCLK source clock */
- unsigned long rclk_srcrate;
- /* Frame Clock */
+
+ /* Frame clock */
unsigned frmclk;
/*
- * Specifically requested RCLK,BCLK by MACHINE Driver.
+ * Specifically requested RCLK, BCLK by machine driver.
* 0 indicates CPU driver is free to choose any value.
*/
unsigned rfs, bfs;
- /* I2S Controller's core clock */
- struct clk *clk;
- /* Clock for generating I2S signals */
- struct clk *op_clk;
/* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */
struct i2s_dai *pri_dai;
/* Pointer to the Secondary_Fifo if it has one, NULL otherwise */
struct i2s_dai *sec_dai;
-#define DAI_OPENED (1 << 0) /* Dai is opened */
-#define DAI_MANAGER (1 << 1) /* Dai is the manager */
+
+#define DAI_OPENED (1 << 0) /* DAI is opened */
+#define DAI_MANAGER (1 << 1) /* DAI is the manager */
unsigned mode;
+
/* Driver for this DAI */
- struct snd_soc_dai_driver i2s_dai_drv;
+ struct snd_soc_dai_driver *drv;
+
/* DMA parameters */
- struct s3c_dma_params dma_playback;
- struct s3c_dma_params dma_capture;
- struct s3c_dma_params idma_playback;
+ struct snd_dmaengine_dai_dma_data dma_playback;
+ struct snd_dmaengine_dai_dma_data dma_capture;
+ struct snd_dmaengine_dai_dma_data idma_playback;
dma_filter_fn filter;
- u32 quirks;
- u32 suspend_i2smod;
- u32 suspend_i2scon;
- u32 suspend_i2spsr;
- const struct samsung_i2s_variant_regs *variant_regs;
- /* Spinlock protecting access to the device's registers */
- spinlock_t spinlock;
- spinlock_t *lock;
+ struct samsung_i2s_priv *priv;
+};
+
+struct samsung_i2s_priv {
+ struct platform_device *pdev;
+ struct platform_device *pdev_sec;
+
+ /* Lock for cross interface checks */
+ spinlock_t pcm_lock;
+
+ /* CPU DAIs and their corresponding drivers */
+ struct i2s_dai *dai;
+ struct snd_soc_dai_driver *dai_drv;
+ int num_dais;
+
+ /* The I2S controller's core clock */
+ struct clk *clk;
+
+ /* Clock for generating I2S signals */
+ struct clk *op_clk;
+
+ /* Rate of RCLK source clock */
+ unsigned long rclk_srcrate;
+
+ /* Cache of selected I2S registers for system suspend */
+ u32 suspend_i2smod;
+ u32 suspend_i2scon;
+ u32 suspend_i2spsr;
- /* Below fields are only valid if this is the primary FIFO */
+ const struct samsung_i2s_variant_regs *variant_regs;
+ void (*fixup_early)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+ void (*fixup_late)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+ u32 quirks;
+
+ /* The clock provider's data */
struct clk *clk_table[3];
struct clk_onecell_data clk_data;
-};
-/* Lock for cross i/f checks */
-static DEFINE_SPINLOCK(lock);
+ /* Spinlock protecting member fields below */
+ spinlock_t lock;
-/* If this is the 'overlay' stereo DAI */
-static inline bool is_secondary(struct i2s_dai *i2s)
-{
- return i2s->pri_dai ? true : false;
-}
+ /* Memory mapped SFR region */
+ void __iomem *addr;
+
+ /* A flag indicating the I2S slave mode operation */
+ bool slave_mode;
+};
-/* If operating in SoC-Slave mode */
-static inline bool is_slave(struct i2s_dai *i2s)
+/* Returns true if this is the 'overlay' stereo DAI */
+static inline bool is_secondary(struct i2s_dai *i2s)
{
- u32 mod = readl(i2s->addr + I2SMOD);
- return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false;
+ return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY;
}
/* If this interface of the controller is transmitting data */
@@ -129,7 +147,7 @@ static inline bool tx_active(struct i2s_dai *i2s)
if (!i2s)
return false;
- active = readl(i2s->addr + I2SCON);
+ active = readl(i2s->priv->addr + I2SCON);
if (is_secondary(i2s))
active &= CON_TXSDMA_ACTIVE;
@@ -167,7 +185,7 @@ static inline bool rx_active(struct i2s_dai *i2s)
if (!i2s)
return false;
- active = readl(i2s->addr + I2SCON) & CON_RXDMA_ACTIVE;
+ active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE;
return active ? true : false;
}
@@ -206,7 +224,9 @@ static inline bool any_active(struct i2s_dai *i2s)
static inline struct i2s_dai *to_info(struct snd_soc_dai *dai)
{
- return snd_soc_dai_get_drvdata(dai);
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ return &priv->dai[dai->id - 1];
}
static inline bool is_opened(struct i2s_dai *i2s)
@@ -228,9 +248,11 @@ static inline bool is_manager(struct i2s_dai *i2s)
/* Read RCLK of I2S (in multiples of LRCLK) */
static inline unsigned get_rfs(struct i2s_dai *i2s)
{
+ struct samsung_i2s_priv *priv = i2s->priv;
u32 rfs;
- rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off;
- rfs &= i2s->variant_regs->rfs_mask;
+
+ rfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->rfs_off;
+ rfs &= priv->variant_regs->rfs_mask;
switch (rfs) {
case 7: return 192;
@@ -247,10 +269,11 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
/* Write RCLK of I2S (in multiples of LRCLK) */
static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
{
- u32 mod = readl(i2s->addr + I2SMOD);
- int rfs_shift = i2s->variant_regs->rfs_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ u32 mod = readl(priv->addr + I2SMOD);
+ int rfs_shift = priv->variant_regs->rfs_off;
- mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift);
+ mod &= ~(priv->variant_regs->rfs_mask << rfs_shift);
switch (rfs) {
case 192:
@@ -279,15 +302,17 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
break;
}
- writel(mod, i2s->addr + I2SMOD);
+ writel(mod, priv->addr + I2SMOD);
}
-/* Read Bit-Clock of I2S (in multiples of LRCLK) */
+/* Read bit-clock of I2S (in multiples of LRCLK) */
static inline unsigned get_bfs(struct i2s_dai *i2s)
{
+ struct samsung_i2s_priv *priv = i2s->priv;
u32 bfs;
- bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off;
- bfs &= i2s->variant_regs->bfs_mask;
+
+ bfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->bfs_off;
+ bfs &= priv->variant_regs->bfs_mask;
switch (bfs) {
case 8: return 256;
@@ -302,12 +327,13 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
}
}
-/* Write Bit-Clock of I2S (in multiples of LRCLK) */
+/* Write bit-clock of I2S (in multiples of LRCLK) */
static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
{
- u32 mod = readl(i2s->addr + I2SMOD);
- int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
- int bfs_shift = i2s->variant_regs->bfs_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ u32 mod = readl(priv->addr + I2SMOD);
+ int tdm = priv->quirks & QUIRK_SUPPORTS_TDM;
+ int bfs_shift = priv->variant_regs->bfs_off;
/* Non-TDM I2S controllers do not support BCLK > 48 * FS */
if (!tdm && bfs > 48) {
@@ -315,7 +341,7 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
return;
}
- mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift);
+ mod &= ~(priv->variant_regs->bfs_mask << bfs_shift);
switch (bfs) {
case 48:
@@ -350,13 +376,13 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
return;
}
- writel(mod, i2s->addr + I2SMOD);
+ writel(mod, priv->addr + I2SMOD);
}
-/* Sample-Size */
+/* Sample size */
static inline int get_blc(struct i2s_dai *i2s)
{
- int blc = readl(i2s->addr + I2SMOD);
+ int blc = readl(i2s->priv->addr + I2SMOD);
blc = (blc >> 13) & 0x3;
@@ -367,11 +393,12 @@ static inline int get_blc(struct i2s_dai *i2s)
}
}
-/* TX Channel Control */
+/* TX channel control */
static void i2s_txctrl(struct i2s_dai *i2s, int on)
{
- void __iomem *addr = i2s->addr;
- int txr_off = i2s->variant_regs->txr_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ void __iomem *addr = priv->addr;
+ int txr_off = priv->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
@@ -420,8 +447,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
/* RX Channel Control */
static void i2s_rxctrl(struct i2s_dai *i2s, int on)
{
- void __iomem *addr = i2s->addr;
- int txr_off = i2s->variant_regs->txr_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ void __iomem *addr = priv->addr;
+ int txr_off = priv->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
@@ -457,9 +485,9 @@ static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
return;
if (is_secondary(i2s))
- fic = i2s->addr + I2SFICS;
+ fic = i2s->priv->addr + I2SFICS;
else
- fic = i2s->addr + I2SFIC;
+ fic = i2s->priv->addr + I2SFIC;
/* Flush the FIFO */
writel(readl(fic) | flush, fic);
@@ -472,24 +500,29 @@ static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
writel(readl(fic) & ~flush, fic);
}
-static int i2s_set_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int rfs, int dir)
+static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs,
+ int dir)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
- const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+ const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs;
unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
u32 mod, mask, val = 0;
+ unsigned long flags;
+ int ret = 0;
+
+ pm_runtime_get_sync(dai->dev);
- spin_lock(i2s->lock);
- mod = readl(i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
switch (clk_id) {
case SAMSUNG_I2S_OPCLK:
mask = MOD_OPCLK_MASK;
- val = dir;
+ val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK;
break;
case SAMSUNG_I2S_CDCLK:
mask = 1 << i2s_regs->cdclkcon_off;
@@ -505,7 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
&& (mod & cdcon_mask))))) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto err;
}
if (dir == SND_SOC_CLOCK_IN)
@@ -518,53 +552,55 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */
mask = 1 << i2s_regs->rclksrc_off;
- if ((i2s->quirks & QUIRK_NO_MUXPSR)
+ if ((priv->quirks & QUIRK_NO_MUXPSR)
|| (clk_id == SAMSUNG_I2S_RCLKSRC_0))
clk_id = 0;
else
clk_id = 1;
if (!any_active(i2s)) {
- if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
+ if (priv->op_clk && !IS_ERR(priv->op_clk)) {
if ((clk_id && !(mod & rsrc_mask)) ||
(!clk_id && (mod & rsrc_mask))) {
- clk_disable_unprepare(i2s->op_clk);
- clk_put(i2s->op_clk);
+ clk_disable_unprepare(priv->op_clk);
+ clk_put(priv->op_clk);
} else {
- i2s->rclk_srcrate =
- clk_get_rate(i2s->op_clk);
- return 0;
+ priv->rclk_srcrate =
+ clk_get_rate(priv->op_clk);
+ goto done;
}
}
if (clk_id)
- i2s->op_clk = clk_get(&i2s->pdev->dev,
+ priv->op_clk = clk_get(&i2s->pdev->dev,
"i2s_opclk1");
else
- i2s->op_clk = clk_get(&i2s->pdev->dev,
+ priv->op_clk = clk_get(&i2s->pdev->dev,
"i2s_opclk0");
- if (WARN_ON(IS_ERR(i2s->op_clk)))
- return PTR_ERR(i2s->op_clk);
-
- clk_prepare_enable(i2s->op_clk);
- i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
+ if (WARN_ON(IS_ERR(priv->op_clk))) {
+ ret = PTR_ERR(priv->op_clk);
+ priv->op_clk = NULL;
+ goto err;
+ }
- /* Over-ride the other's */
- if (other) {
- other->op_clk = i2s->op_clk;
- other->rclk_srcrate = i2s->rclk_srcrate;
+ ret = clk_prepare_enable(priv->op_clk);
+ if (ret) {
+ clk_put(priv->op_clk);
+ priv->op_clk = NULL;
+ goto err;
}
+ priv->rclk_srcrate = clk_get_rate(priv->op_clk);
+
} else if ((!clk_id && (mod & rsrc_mask))
|| (clk_id && !(mod & rsrc_mask))) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto err;
} else {
/* Call can't be on the active DAI */
- i2s->op_clk = other->op_clk;
- i2s->rclk_srcrate = other->rclk_srcrate;
- return 0;
+ goto done;
}
if (clk_id == 1)
@@ -572,28 +608,35 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
break;
default:
dev_err(&i2s->pdev->dev, "We don't serve that!\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
- spin_lock(i2s->lock);
- mod = readl(i2s->addr + I2SMOD);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
mod = (mod & ~mask) | val;
- writel(mod, i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ writel(mod, priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+done:
+ pm_runtime_put(dai->dev);
return 0;
+err:
+ pm_runtime_put(dai->dev);
+ return ret;
}
-static int i2s_set_fmt(struct snd_soc_dai *dai,
- unsigned int fmt)
+static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
u32 mod, tmp = 0;
+ unsigned long flags;
- lrp_shift = i2s->variant_regs->lrp_off;
- sdf_shift = i2s->variant_regs->sdf_off;
- mod_slave = 1 << i2s->variant_regs->mss_off;
+ lrp_shift = priv->variant_regs->lrp_off;
+ sdf_shift = priv->variant_regs->sdf_off;
+ mod_slave = 1 << priv->variant_regs->mss_off;
sdf_mask = MOD_SDF_MASK << sdf_shift;
lrp_rlow = MOD_LR_RLOW << lrp_shift;
@@ -634,13 +677,17 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
tmp |= mod_slave;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Set default source clock in Master mode */
- if (i2s->rclk_srcrate == 0)
+ case SND_SOC_DAIFMT_BP_FP:
+ /*
+ * Set default source clock in Master mode, only when the
+ * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
+ * clock configuration assigned in DT is not overwritten.
+ */
+ if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
break;
@@ -649,15 +696,17 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- spin_lock(i2s->lock);
- mod = readl(i2s->addr + I2SMOD);
+ pm_runtime_get_sync(dai->dev);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
/*
* Don't change the I2S mode if any controller is active on this
* channel.
*/
if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ pm_runtime_put(dai->dev);
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
@@ -665,8 +714,10 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
mod &= ~(sdf_mask | lrp_rlow | mod_slave);
mod |= tmp;
- writel(mod, i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ writel(mod, priv->addr + I2SMOD);
+ priv->slave_mode = (mod & mod_slave);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ pm_runtime_put(dai->dev);
return 0;
}
@@ -674,8 +725,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
static int i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
u32 mod, mask = 0, val = 0;
+ struct clk *rclksrc;
+ unsigned long flags;
+
+ WARN_ON(!pm_runtime_active(dai->dev));
if (!is_secondary(i2s))
mask |= (MOD_DC2_EN | MOD_DC1_EN);
@@ -683,20 +739,21 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_channels(params)) {
case 6:
val |= MOD_DC2_EN;
+ fallthrough;
case 4:
val |= MOD_DC1_EN;
break;
case 2:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s->dma_playback.dma_size = 4;
+ i2s->dma_playback.addr_width = 4;
else
- i2s->dma_capture.dma_size = 4;
+ i2s->dma_capture.addr_width = 4;
break;
case 1:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s->dma_playback.dma_size = 2;
+ i2s->dma_playback.addr_width = 2;
else
- i2s->dma_capture.dma_size = 2;
+ i2s->dma_capture.addr_width = 2;
break;
default:
@@ -744,28 +801,35 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- spin_lock(i2s->lock);
- mod = readl(i2s->addr + I2SMOD);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
mod = (mod & ~mask) | val;
- writel(mod, i2s->addr + I2SMOD);
- spin_unlock(i2s->lock);
+ writel(mod, priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
- samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
+ snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
i2s->frmclk = params_rate(params);
+ rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC];
+ if (rclksrc && !IS_ERR(rclksrc))
+ priv->rclk_srcrate = clk_get_rate(rclksrc);
+
return 0;
}
-/* We set constraints on the substream acc to the version of I2S */
+/* We set constraints on the substream according to the version of I2S */
static int i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
- spin_lock_irqsave(&lock, flags);
+ pm_runtime_get_sync(dai->dev);
+
+ spin_lock_irqsave(&priv->pcm_lock, flags);
i2s->mode |= DAI_OPENED;
@@ -774,10 +838,10 @@ static int i2s_startup(struct snd_pcm_substream *substream,
else
i2s->mode |= DAI_MANAGER;
- if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
- writel(CON_RSTCLR, i2s->addr + I2SCON);
+ if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR))
+ writel(CON_RSTCLR, i2s->priv->addr + I2SCON);
- spin_unlock_irqrestore(&lock, flags);
+ spin_unlock_irqrestore(&priv->pcm_lock, flags);
return 0;
}
@@ -785,11 +849,12 @@ static int i2s_startup(struct snd_pcm_substream *substream,
static void i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
- spin_lock_irqsave(&lock, flags);
+ spin_lock_irqsave(&priv->pcm_lock, flags);
i2s->mode &= ~DAI_OPENED;
i2s->mode &= ~DAI_MANAGER;
@@ -801,11 +866,14 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
i2s->rfs = 0;
i2s->bfs = 0;
- spin_unlock_irqrestore(&lock, flags);
+ spin_unlock_irqrestore(&priv->pcm_lock, flags);
+
+ pm_runtime_put(dai->dev);
}
static int config_setup(struct i2s_dai *i2s)
{
+ struct samsung_i2s_priv *priv = i2s->priv;
struct i2s_dai *other = get_other_dai(i2s);
unsigned rfs, bfs, blc;
u32 psr;
@@ -850,15 +918,15 @@ static int config_setup(struct i2s_dai *i2s)
set_rfs(i2s, rfs);
/* Don't bother with PSR in Slave mode */
- if (is_slave(i2s))
+ if (priv->slave_mode)
return 0;
- if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
- psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
- writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
+ if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
+ psr = priv->rclk_srcrate / i2s->frmclk / rfs;
+ writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR);
dev_dbg(&i2s->pdev->dev,
"RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n",
- i2s->rclk_srcrate, psr, rfs, bfs);
+ priv->rclk_srcrate, psr, rfs, bfs);
}
return 0;
@@ -867,33 +935,42 @@ static int config_setup(struct i2s_dai *i2s)
static int i2s_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct i2s_dai *i2s = to_info(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0));
unsigned long flags;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock_irqsave(i2s->lock, flags);
+ pm_runtime_get_sync(dai->dev);
+
+ if (priv->fixup_early)
+ priv->fixup_early(substream, dai);
+
+ spin_lock_irqsave(&priv->lock, flags);
if (config_setup(i2s)) {
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return -EINVAL;
}
+ if (priv->fixup_late)
+ priv->fixup_late(substream, dai);
+
if (capture)
i2s_rxctrl(i2s, 1);
else
i2s_txctrl(i2s, 1);
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- spin_lock_irqsave(i2s->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
if (capture) {
i2s_rxctrl(i2s, 0);
@@ -903,7 +980,8 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
i2s_fifo(i2s, FIC_TXFLUSH);
}
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ pm_runtime_put(dai->dev);
break;
}
@@ -918,13 +996,16 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
switch (div_id) {
case SAMSUNG_I2S_DIV_BCLK:
+ pm_runtime_get_sync(dai->dev);
if ((any_active(i2s) && div && (get_bfs(i2s) != div))
|| (other && other->bfs && (other->bfs != div))) {
+ pm_runtime_put(dai->dev);
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
}
i2s->bfs = div;
+ pm_runtime_put(dai->dev);
break;
default:
dev_err(&i2s->pdev->dev,
@@ -938,42 +1019,32 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
static snd_pcm_sframes_t
i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
- u32 reg = readl(i2s->addr + I2SFIC);
+ u32 reg = readl(priv->addr + I2SFIC);
snd_pcm_sframes_t delay;
- const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+
+ WARN_ON(!pm_runtime_active(dai->dev));
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
delay = FIC_RXCOUNT(reg);
else if (is_secondary(i2s))
- delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
+ delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS));
else
- delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f;
+ delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f;
return delay;
}
#ifdef CONFIG_PM
-static int i2s_suspend(struct snd_soc_dai *dai)
+static int i2s_suspend(struct snd_soc_component *component)
{
- struct i2s_dai *i2s = to_info(dai);
-
- i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
- i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
- i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
-
- return 0;
+ return pm_runtime_force_suspend(component->dev);
}
-static int i2s_resume(struct snd_soc_dai *dai)
+static int i2s_resume(struct snd_soc_component *component)
{
- struct i2s_dai *i2s = to_info(dai);
-
- writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
- writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
- writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
-
- return 0;
+ return pm_runtime_force_resume(component->dev);
}
#else
#define i2s_suspend NULL
@@ -982,62 +1053,73 @@ static int i2s_resume(struct snd_soc_dai *dai)
static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
- if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
- samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
- NULL);
+ pm_runtime_get_sync(dai->dev);
+
+ if (is_secondary(i2s)) {
+ /* If this is probe on the secondary DAI */
+ snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, NULL);
} else {
- samsung_asoc_init_dma_data(dai, &i2s->dma_playback,
- &i2s->dma_capture);
+ snd_soc_dai_init_dma_data(dai, &i2s->dma_playback,
+ &i2s->dma_capture);
- if (i2s->quirks & QUIRK_NEED_RSTCLR)
- writel(CON_RSTCLR, i2s->addr + I2SCON);
+ if (priv->quirks & QUIRK_NEED_RSTCLR)
+ writel(CON_RSTCLR, priv->addr + I2SCON);
- if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
- idma_reg_addr_init(i2s->addr,
- i2s->sec_dai->idma_playback.dma_addr);
+ if (priv->quirks & QUIRK_SUPPORTS_IDMA)
+ idma_reg_addr_init(priv->addr,
+ other->idma_playback.addr);
}
/* Reset any constraint on RFS and BFS */
i2s->rfs = 0;
i2s->bfs = 0;
- i2s->rclk_srcrate = 0;
- spin_lock_irqsave(i2s->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
i2s_txctrl(i2s, 0);
i2s_rxctrl(i2s, 0);
i2s_fifo(i2s, FIC_TXFLUSH);
i2s_fifo(other, FIC_TXFLUSH);
i2s_fifo(i2s, FIC_RXFLUSH);
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
/* Gate CDCLK by default */
if (!is_opened(other))
i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
0, SND_SOC_CLOCK_IN);
+ pm_runtime_put(dai->dev);
return 0;
}
static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
{
- struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct i2s_dai *i2s = to_info(dai);
+ unsigned long flags;
+
+ pm_runtime_get_sync(dai->dev);
if (!is_secondary(i2s)) {
- if (i2s->quirks & QUIRK_NEED_RSTCLR) {
- spin_lock(i2s->lock);
- writel(0, i2s->addr + I2SCON);
- spin_unlock(i2s->lock);
+ if (priv->quirks & QUIRK_NEED_RSTCLR) {
+ spin_lock_irqsave(&priv->lock, flags);
+ writel(0, priv->addr + I2SCON);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
}
+ pm_runtime_put(dai->dev);
+
return 0;
}
static const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
+ .probe = samsung_i2s_dai_probe,
+ .remove = samsung_i2s_dai_remove,
.trigger = i2s_trigger,
.hw_params = i2s_hw_params,
.set_fmt = i2s_set_fmt,
@@ -1048,126 +1130,161 @@ static const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
.delay = i2s_delay,
};
-static const struct snd_soc_component_driver samsung_i2s_component = {
- .name = "samsung-i2s",
+static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = {
+ /* Backend DAI */
+ SND_SOC_DAPM_AIF_OUT("Mixer DAI TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Mixer DAI RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Playback Mixer */
+ SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
};
-#define SAMSUNG_I2S_RATES SNDRV_PCM_RATE_8000_96000
+static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = {
+ { "Playback Mixer", NULL, "Primary Playback" },
+ { "Playback Mixer", NULL, "Secondary Playback" },
-#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE)
+ { "Mixer DAI TX", NULL, "Playback Mixer" },
+ { "Primary Capture", NULL, "Mixer DAI RX" },
+};
-static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
-{
- struct i2s_dai *i2s;
- int ret;
+static const struct snd_soc_component_driver samsung_i2s_component = {
+ .name = "samsung-i2s",
- i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
- if (i2s == NULL)
- return NULL;
-
- i2s->pdev = pdev;
- i2s->pri_dai = NULL;
- i2s->sec_dai = NULL;
- i2s->i2s_dai_drv.symmetric_rates = 1;
- i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe;
- i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove;
- i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops;
- i2s->i2s_dai_drv.suspend = i2s_suspend;
- i2s->i2s_dai_drv.resume = i2s_resume;
- i2s->i2s_dai_drv.playback.channels_min = 1;
- i2s->i2s_dai_drv.playback.channels_max = 2;
- i2s->i2s_dai_drv.playback.rates = SAMSUNG_I2S_RATES;
- i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS;
-
- if (!sec) {
- i2s->i2s_dai_drv.capture.channels_min = 1;
- i2s->i2s_dai_drv.capture.channels_max = 2;
- i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
- i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
- dev_set_drvdata(&i2s->pdev->dev, i2s);
- } else { /* Create a new platform_device for Secondary */
- i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1);
- if (!i2s->pdev)
- return NULL;
-
- i2s->pdev->dev.parent = &pdev->dev;
-
- platform_set_drvdata(i2s->pdev, i2s);
- ret = platform_device_add(i2s->pdev);
- if (ret < 0)
- return NULL;
- }
+ .dapm_widgets = samsung_i2s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets),
- return i2s;
-}
+ .dapm_routes = samsung_i2s_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes),
-static const struct of_device_id exynos_i2s_match[];
+ .suspend = i2s_suspend,
+ .resume = i2s_resume,
-static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
- struct platform_device *pdev)
+ .legacy_dai_naming = 1,
+};
+
+#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static int i2s_alloc_dais(struct samsung_i2s_priv *priv,
+ const struct samsung_i2s_dai_data *i2s_dai_data,
+ int num_dais)
{
- if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
- const struct of_device_id *match;
- match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
- return match ? match->data : NULL;
- } else {
- return (struct samsung_i2s_dai_data *)
- platform_get_device_id(pdev)->driver_data;
+ static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" };
+ static const char *stream_names[] = { "Primary Playback",
+ "Secondary Playback" };
+ struct snd_soc_dai_driver *dai_drv;
+ int i;
+
+ priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais,
+ sizeof(struct i2s_dai), GFP_KERNEL);
+ if (!priv->dai)
+ return -ENOMEM;
+
+ priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais,
+ sizeof(*dai_drv), GFP_KERNEL);
+ if (!priv->dai_drv)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dais; i++) {
+ dai_drv = &priv->dai_drv[i];
+
+ dai_drv->symmetric_rate = 1;
+ dai_drv->ops = &samsung_i2s_dai_ops;
+
+ dai_drv->playback.channels_min = 1;
+ dai_drv->playback.channels_max = 2;
+ dai_drv->playback.rates = i2s_dai_data->pcm_rates;
+ dai_drv->playback.formats = SAMSUNG_I2S_FMTS;
+ dai_drv->playback.stream_name = stream_names[i];
+
+ dai_drv->id = i + 1;
+ dai_drv->name = dai_names[i];
+
+ priv->dai[i].drv = &priv->dai_drv[i];
+ priv->dai[i].pdev = priv->pdev;
}
+
+ /* Initialize capture only for the primary DAI */
+ dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1];
+
+ dai_drv->capture.channels_min = 1;
+ dai_drv->capture.channels_max = 2;
+ dai_drv->capture.rates = i2s_dai_data->pcm_rates;
+ dai_drv->capture.formats = SAMSUNG_I2S_FMTS;
+ dai_drv->capture.stream_name = "Primary Capture";
+
+ return 0;
}
-#ifdef CONFIG_PM
static int i2s_runtime_suspend(struct device *dev)
{
- struct i2s_dai *i2s = dev_get_drvdata(dev);
+ struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
- clk_disable_unprepare(i2s->clk);
+ priv->suspend_i2smod = readl(priv->addr + I2SMOD);
+ priv->suspend_i2scon = readl(priv->addr + I2SCON);
+ priv->suspend_i2spsr = readl(priv->addr + I2SPSR);
+
+ clk_disable_unprepare(priv->op_clk);
+ clk_disable_unprepare(priv->clk);
return 0;
}
static int i2s_runtime_resume(struct device *dev)
{
- struct i2s_dai *i2s = dev_get_drvdata(dev);
+ struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
+ int ret;
- clk_prepare_enable(i2s->clk);
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ if (priv->op_clk) {
+ ret = clk_prepare_enable(priv->op_clk);
+ if (ret) {
+ clk_disable_unprepare(priv->clk);
+ return ret;
+ }
+ }
+
+ writel(priv->suspend_i2scon, priv->addr + I2SCON);
+ writel(priv->suspend_i2smod, priv->addr + I2SMOD);
+ writel(priv->suspend_i2spsr, priv->addr + I2SPSR);
return 0;
}
-#endif /* CONFIG_PM */
-static void i2s_unregister_clocks(struct i2s_dai *i2s)
+static void i2s_unregister_clocks(struct samsung_i2s_priv *priv)
{
int i;
- for (i = 0; i < i2s->clk_data.clk_num; i++) {
- if (!IS_ERR(i2s->clk_table[i]))
- clk_unregister(i2s->clk_table[i]);
+ for (i = 0; i < priv->clk_data.clk_num; i++) {
+ if (!IS_ERR(priv->clk_table[i]))
+ clk_unregister(priv->clk_table[i]);
}
}
-static void i2s_unregister_clock_provider(struct platform_device *pdev)
+static void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv)
{
- struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev);
-
- of_clk_del_provider(pdev->dev.of_node);
- i2s_unregister_clocks(i2s);
+ of_clk_del_provider(priv->pdev->dev.of_node);
+ i2s_unregister_clocks(priv);
}
-static int i2s_register_clock_provider(struct platform_device *pdev)
+
+static int i2s_register_clock_provider(struct samsung_i2s_priv *priv)
{
- struct device *dev = &pdev->dev;
- struct i2s_dai *i2s = dev_get_drvdata(dev);
+
+ const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" };
const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" };
const char *p_names[2] = { NULL };
- const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs;
+ struct device *dev = &priv->pdev->dev;
+ const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs;
+ const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)];
struct clk *rclksrc;
int ret, i;
/* Register the clock provider only if it's expected in the DTB */
- if (!of_find_property(dev->of_node, "#clock-cells", NULL))
+ if (!of_property_present(dev->of_node, "#clock-cells"))
return 0;
/* Get the RCLKSRC mux clock parent clock names */
@@ -1179,210 +1296,306 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
clk_put(rclksrc);
}
- if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
+ for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) {
+ i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
+ dev_name(dev), i2s_clk_desc[i]);
+ if (!i2s_clk_name[i])
+ return -ENOMEM;
+ }
+
+ if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
/* Activate the prescaler */
- u32 val = readl(i2s->addr + I2SPSR);
- writel(val | PSR_PSREN, i2s->addr + I2SPSR);
+ u32 val = readl(priv->addr + I2SPSR);
+ writel(val | PSR_PSREN, priv->addr + I2SPSR);
- i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL,
- "i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
+ priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
+ i2s_clk_name[CLK_I2S_RCLK_SRC], p_names,
+ ARRAY_SIZE(p_names),
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
- i2s->addr + I2SMOD, reg_info->rclksrc_off,
- 1, 0, i2s->lock);
+ priv->addr + I2SMOD, reg_info->rclksrc_off,
+ 1, 0, &priv->lock);
- i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL,
- "i2s_presc", "i2s_rclksrc",
+ priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
+ i2s_clk_name[CLK_I2S_RCLK_PSR],
+ i2s_clk_name[CLK_I2S_RCLK_SRC],
CLK_SET_RATE_PARENT,
- i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
+ priv->addr + I2SPSR, 8, 6, 0, &priv->lock);
- p_names[0] = "i2s_presc";
- i2s->clk_data.clk_num = 2;
+ p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR];
+ priv->clk_data.clk_num = 2;
}
- of_property_read_string_index(dev->of_node,
- "clock-output-names", 0, &clk_name[0]);
- i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0],
- p_names[0], CLK_SET_RATE_PARENT,
- i2s->addr + I2SMOD, reg_info->cdclkcon_off,
- CLK_GATE_SET_TO_DISABLE, i2s->lock);
+ priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev,
+ i2s_clk_name[CLK_I2S_CDCLK], p_names[0],
+ CLK_SET_RATE_PARENT,
+ priv->addr + I2SMOD, reg_info->cdclkcon_off,
+ CLK_GATE_SET_TO_DISABLE, &priv->lock);
- i2s->clk_data.clk_num += 1;
- i2s->clk_data.clks = i2s->clk_table;
+ priv->clk_data.clk_num += 1;
+ priv->clk_data.clks = priv->clk_table;
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
- &i2s->clk_data);
+ &priv->clk_data);
if (ret < 0) {
dev_err(dev, "failed to add clock provider: %d\n", ret);
- i2s_unregister_clocks(i2s);
+ i2s_unregister_clocks(priv);
}
return ret;
}
+/* Create platform device for the secondary PCM */
+static int i2s_create_secondary_device(struct samsung_i2s_priv *priv)
+{
+ struct platform_device *pdev_sec;
+ const char *devname;
+ int ret;
+
+ devname = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s-sec",
+ dev_name(&priv->pdev->dev));
+ if (!devname)
+ return -ENOMEM;
+
+ pdev_sec = platform_device_alloc(devname, -1);
+ if (!pdev_sec)
+ return -ENOMEM;
+
+ pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL);
+ if (!pdev_sec->driver_override) {
+ platform_device_put(pdev_sec);
+ return -ENOMEM;
+ }
+
+ ret = platform_device_add(pdev_sec);
+ if (ret < 0) {
+ platform_device_put(pdev_sec);
+ return ret;
+ }
+
+ ret = device_attach(&pdev_sec->dev);
+ if (ret <= 0) {
+ platform_device_unregister(priv->pdev_sec);
+ dev_info(&pdev_sec->dev, "device_attach() failed\n");
+ return ret;
+ }
+
+ priv->pdev_sec = pdev_sec;
+
+ return 0;
+}
+
+static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv)
+{
+ platform_device_unregister(priv->pdev_sec);
+ priv->pdev_sec = NULL;
+}
+
static int samsung_i2s_probe(struct platform_device *pdev)
{
struct i2s_dai *pri_dai, *sec_dai = NULL;
struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
- struct samsung_i2s *i2s_cfg = NULL;
- struct resource *res;
- u32 regs_base, quirks = 0, idma_addr = 0;
+ u32 regs_base, idma_addr = 0;
struct device_node *np = pdev->dev.of_node;
const struct samsung_i2s_dai_data *i2s_dai_data;
- int ret;
+ const struct platform_device_id *id;
+ struct samsung_i2s_priv *priv;
+ struct resource *res;
+ int num_dais, ret;
- /* Call during Seconday interface registration */
- i2s_dai_data = samsung_i2s_get_driver_data(pdev);
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ i2s_dai_data = of_device_get_match_data(&pdev->dev);
+ } else {
+ id = platform_get_device_id(pdev);
- if (i2s_dai_data->dai_type == TYPE_SEC) {
- sec_dai = dev_get_drvdata(&pdev->dev);
- if (!sec_dai) {
- dev_err(&pdev->dev, "Unable to get drvdata\n");
- return -EFAULT;
- }
- ret = devm_snd_soc_register_component(&sec_dai->pdev->dev,
- &samsung_i2s_component,
- &sec_dai->i2s_dai_drv, 1);
- if (ret != 0)
- return ret;
+ /* Nothing to do if it is the secondary device probe */
+ if (!id)
+ return 0;
- return samsung_asoc_dma_platform_register(&pdev->dev,
- sec_dai->filter);
+ i2s_dai_data = (struct samsung_i2s_dai_data *)id->driver_data;
}
- pri_dai = i2s_alloc_dai(pdev, false);
- if (!pri_dai) {
- dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- }
- spin_lock_init(&pri_dai->spinlock);
- pri_dai->lock = &pri_dai->spinlock;
-
- if (!np) {
- if (i2s_pdata == NULL) {
- dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
+ if (np) {
+ priv->quirks = i2s_dai_data->quirks;
+ priv->fixup_early = i2s_dai_data->fixup_early;
+ priv->fixup_late = i2s_dai_data->fixup_late;
+ } else {
+ if (!i2s_pdata) {
+ dev_err(&pdev->dev, "Missing platform data\n");
return -EINVAL;
}
+ priv->quirks = i2s_pdata->type.quirks;
+ }
- pri_dai->dma_playback.slave = i2s_pdata->dma_playback;
- pri_dai->dma_capture.slave = i2s_pdata->dma_capture;
- pri_dai->filter = i2s_pdata->dma_filter;
+ num_dais = (priv->quirks & QUIRK_SEC_DAI) ? 2 : 1;
+ priv->pdev = pdev;
+ priv->variant_regs = i2s_dai_data->i2s_variant_regs;
- if (&i2s_pdata->type)
- i2s_cfg = &i2s_pdata->type.i2s;
+ ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais);
+ if (ret < 0)
+ return ret;
- if (i2s_cfg) {
- quirks = i2s_cfg->quirks;
- idma_addr = i2s_cfg->idma_addr;
- }
+ pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1];
+
+ spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->pcm_lock);
+
+ if (!np) {
+ pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback;
+ pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
+ pri_dai->filter = i2s_pdata->dma_filter;
+
+ idma_addr = i2s_pdata->type.idma_addr;
} else {
- quirks = i2s_dai_data->quirks;
if (of_property_read_u32(np, "samsung,idma-addr",
&idma_addr)) {
- if (quirks & QUIRK_SUPPORTS_IDMA) {
+ if (priv->quirks & QUIRK_SUPPORTS_IDMA) {
dev_info(&pdev->dev, "idma address is not"\
"specified");
}
}
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pri_dai->addr = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pri_dai->addr))
- return PTR_ERR(pri_dai->addr);
+ priv->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->addr))
+ return PTR_ERR(priv->addr);
regs_base = res->start;
- pri_dai->clk = devm_clk_get(&pdev->dev, "iis");
- if (IS_ERR(pri_dai->clk)) {
+ priv->clk = devm_clk_get(&pdev->dev, "iis");
+ if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "Failed to get iis clock\n");
- return PTR_ERR(pri_dai->clk);
+ return PTR_ERR(priv->clk);
}
- ret = clk_prepare_enable(pri_dai->clk);
+ ret = clk_prepare_enable(priv->clk);
if (ret != 0) {
dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
- pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
- pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
- pri_dai->dma_playback.ch_name = "tx";
- pri_dai->dma_capture.ch_name = "rx";
- pri_dai->dma_playback.dma_size = 4;
- pri_dai->dma_capture.dma_size = 4;
- pri_dai->quirks = quirks;
- pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
-
- if (quirks & QUIRK_PRI_6CHAN)
- pri_dai->i2s_dai_drv.playback.channels_max = 6;
-
- if (quirks & QUIRK_SEC_DAI) {
- sec_dai = i2s_alloc_dai(pdev, true);
- if (!sec_dai) {
- dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
- return -ENOMEM;
- }
+ pri_dai->dma_playback.addr = regs_base + I2STXD;
+ pri_dai->dma_capture.addr = regs_base + I2SRXD;
+ pri_dai->dma_playback.chan_name = "tx";
+ pri_dai->dma_capture.chan_name = "rx";
+ pri_dai->dma_playback.addr_width = 4;
+ pri_dai->dma_capture.addr_width = 4;
+ pri_dai->priv = priv;
+
+ if (priv->quirks & QUIRK_PRI_6CHAN)
+ pri_dai->drv->playback.channels_max = 6;
+
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
+ "tx", "rx", NULL);
+ if (ret < 0)
+ goto err_disable_clk;
- sec_dai->lock = &pri_dai->spinlock;
- sec_dai->variant_regs = pri_dai->variant_regs;
- sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
- sec_dai->dma_playback.ch_name = "tx-sec";
+ if (priv->quirks & QUIRK_SEC_DAI) {
+ sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1];
+
+ sec_dai->dma_playback.addr = regs_base + I2STXDS;
+ sec_dai->dma_playback.chan_name = "tx-sec";
if (!np) {
- sec_dai->dma_playback.slave = i2s_pdata->dma_play_sec;
+ sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec;
sec_dai->filter = i2s_pdata->dma_filter;
}
- sec_dai->dma_playback.dma_size = 4;
- sec_dai->addr = pri_dai->addr;
- sec_dai->clk = pri_dai->clk;
- sec_dai->quirks = quirks;
- sec_dai->idma_playback.dma_addr = idma_addr;
+ sec_dai->dma_playback.addr_width = 4;
+ sec_dai->idma_playback.addr = idma_addr;
sec_dai->pri_dai = pri_dai;
+ sec_dai->priv = priv;
pri_dai->sec_dai = sec_dai;
+
+ ret = i2s_create_secondary_device(priv);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev,
+ sec_dai->filter, "tx-sec", NULL,
+ &pdev->dev);
+ if (ret < 0)
+ goto err_del_sec;
+
}
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to configure gpio\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_del_sec;
}
- devm_snd_soc_register_component(&pri_dai->pdev->dev,
+ dev_set_drvdata(&pdev->dev, priv);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
&samsung_i2s_component,
- &pri_dai->i2s_dai_drv, 1);
+ priv->dai_drv, num_dais);
+ if (ret < 0)
+ goto err_del_sec;
+ pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter);
- if (ret != 0)
- return ret;
+ ret = i2s_register_clock_provider(priv);
+ if (ret < 0)
+ goto err_disable_pm;
- return i2s_register_clock_provider(pdev);
+ priv->op_clk = clk_get_parent(priv->clk_table[CLK_I2S_RCLK_SRC]);
+
+ return 0;
+
+err_disable_pm:
+ pm_runtime_disable(&pdev->dev);
+err_del_sec:
+ i2s_delete_secondary_device(priv);
+err_disable_clk:
+ clk_disable_unprepare(priv->clk);
+ return ret;
}
-static int samsung_i2s_remove(struct platform_device *pdev)
+static void samsung_i2s_remove(struct platform_device *pdev)
{
- struct i2s_dai *i2s, *other;
+ struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev);
- i2s = dev_get_drvdata(&pdev->dev);
- other = get_other_dai(i2s);
+ /* The secondary device has no driver data assigned */
+ if (!priv)
+ return;
- if (other) {
- other->pri_dai = NULL;
- other->sec_dai = NULL;
- } else {
- pm_runtime_disable(&pdev->dev);
- }
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
- if (!is_secondary(i2s)) {
- i2s_unregister_clock_provider(pdev);
- clk_disable_unprepare(i2s->clk);
+ i2s_unregister_clock_provider(priv);
+ i2s_delete_secondary_device(priv);
+ clk_disable_unprepare(priv->clk);
+
+ pm_runtime_put_noidle(&pdev->dev);
+}
+
+static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0));
+ struct i2s_dai *other = get_other_dai(i2s);
+
+ if (!is_opened(other)) {
+ i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 0, SND_SOC_CLOCK_OUT);
+ i2s_set_sysclk(dai, SAMSUNG_I2S_OPCLK, 0, MOD_OPCLK_PCLK);
}
+}
- i2s->pri_dai = NULL;
- i2s->sec_dai = NULL;
+static void fsd_i2s_fixup_late(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0));
+ struct i2s_dai *other = get_other_dai(i2s);
- return 0;
+ if (!is_opened(other))
+ writel(PSR_PSVAL(2) | PSR_PSREN, priv->addr + I2SPSR);
}
static const struct samsung_i2s_variant_regs i2sv3_regs = {
@@ -1442,56 +1655,50 @@ static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
};
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
- .dai_type = TYPE_PRI,
.quirks = QUIRK_NO_MUXPSR,
+ .pcm_rates = SNDRV_PCM_RATE_8000_96000,
.i2s_variant_regs = &i2sv3_regs,
};
-static const struct samsung_i2s_dai_data i2sv5_dai_type = {
- .dai_type = TYPE_PRI,
+static const struct samsung_i2s_dai_data i2sv5_dai_type __maybe_unused = {
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_IDMA,
+ .pcm_rates = SNDRV_PCM_RATE_8000_96000,
.i2s_variant_regs = &i2sv3_regs,
};
-static const struct samsung_i2s_dai_data i2sv6_dai_type = {
- .dai_type = TYPE_PRI,
+static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = {
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
+ .pcm_rates = SNDRV_PCM_RATE_8000_96000,
.i2s_variant_regs = &i2sv6_regs,
};
-static const struct samsung_i2s_dai_data i2sv7_dai_type = {
- .dai_type = TYPE_PRI,
+static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = {
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM,
+ .pcm_rates = SNDRV_PCM_RATE_8000_192000,
.i2s_variant_regs = &i2sv7_regs,
};
-static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
- .dai_type = TYPE_PRI,
+static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 __maybe_unused = {
.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
+ .pcm_rates = SNDRV_PCM_RATE_8000_96000,
.i2s_variant_regs = &i2sv5_i2s1_regs,
};
-static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
- .dai_type = TYPE_PRI,
-};
-
-static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
- .dai_type = TYPE_SEC,
+static const struct samsung_i2s_dai_data fsd_dai_type __maybe_unused = {
+ .quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM,
+ .pcm_rates = SNDRV_PCM_RATE_8000_192000,
+ .i2s_variant_regs = &i2sv7_regs,
+ .fixup_early = fsd_i2s_fixup_early,
+ .fixup_late = fsd_i2s_fixup_late,
};
static const struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
.driver_data = (kernel_ulong_t)&i2sv3_dai_type,
- }, {
- .name = "samsung-i2s-sec",
- .driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
- }, {
- .name = "samsung-i2sv4",
- .driver_data = (kernel_ulong_t)&i2sv5_dai_type,
},
{},
};
@@ -1514,6 +1721,9 @@ static const struct of_device_id exynos_i2s_match[] = {
}, {
.compatible = "samsung,exynos7-i2s1",
.data = &i2sv5_dai_type_i2s1,
+ }, {
+ .compatible = "tesla,fsd-i2s",
+ .data = &fsd_dai_type,
},
{},
};
@@ -1521,8 +1731,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match);
#endif
static const struct dev_pm_ops samsung_i2s_pm = {
- SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
- i2s_runtime_resume, NULL)
+ RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver samsung_i2s_driver = {
@@ -1532,7 +1742,7 @@ static struct platform_driver samsung_i2s_driver = {
.driver = {
.name = "samsung-i2s",
.of_match_table = of_match_ptr(exynos_i2s_match),
- .pm = &samsung_i2s_pm,
+ .pm = pm_ptr(&samsung_i2s_pm),
},
};
@@ -1541,5 +1751,4 @@ module_platform_driver(samsung_i2s_driver);
/* Module information */
MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
MODULE_DESCRIPTION("Samsung I2S Interface");
-MODULE_ALIAS("platform:samsung-i2s");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h
index 21ff24e930db..78b475ef98d9 100644
--- a/sound/soc/samsung/i2s.h
+++ b/sound/soc/samsung/i2s.h
@@ -1,23 +1,27 @@
-/* sound/soc/samsung/i2s.h
- *
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
* ALSA SoC Audio Layer - Samsung I2S Controller driver
*
* Copyright (c) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassisinghbrar@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.
*/
#ifndef __SND_SOC_SAMSUNG_I2S_H
#define __SND_SOC_SAMSUNG_I2S_H
-#define SAMSUNG_I2S_DIV_BCLK 1
+#define SAMSUNG_I2S_DAI "samsung-i2s"
+#define SAMSUNG_I2S_DAI_SEC "samsung-i2s-sec"
+
+#define SAMSUNG_I2S_DIV_BCLK 1
-#define SAMSUNG_I2S_RCLKSRC_0 0
-#define SAMSUNG_I2S_RCLKSRC_1 1
+#define SAMSUNG_I2S_RCLKSRC_0 0
+#define SAMSUNG_I2S_RCLKSRC_1 1
#define SAMSUNG_I2S_CDCLK 2
+/* Operation clock for IIS logic */
#define SAMSUNG_I2S_OPCLK 3
+#define SAMSUNG_I2S_OPCLK_CDCLK_OUT 0 /* CODEC clock out */
+#define SAMSUNG_I2S_OPCLK_CDCLK_IN 1 /* CODEC clock in */
+#define SAMSUNG_I2S_OPCLK_BCLK_OUT 2 /* Bit clock out */
+#define SAMSUNG_I2S_OPCLK_PCLK 3 /* Audio bus clock */
#endif /* __SND_SOC_SAMSUNG_I2S_H */
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
index 4ed29ffc1c54..402ccadad46c 100644
--- a/sound/soc/samsung/idma.c
+++ b/sound/soc/samsung/idma.c
@@ -1,16 +1,10 @@
-/*
- * sound/soc/samsung/idma.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * I2S0's Internal DMA 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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// idma.c - I2S0 internal DMA driver
+//
+// Copyright (c) 2011 Samsung Electronics Co., Ltd.
+// http://www.samsung.com
+
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
@@ -22,7 +16,6 @@
#include "i2s.h"
#include "idma.h"
-#include "dma.h"
#include "i2s-regs.h"
#define ST_RUNNING (1<<0)
@@ -144,8 +137,9 @@ static void idma_done(void *id, int bytes_xfer)
snd_pcm_period_elapsed(substream);
}
-static int idma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int idma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = substream->runtime->private_data;
@@ -170,14 +164,16 @@ static int idma_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int idma_hw_free(struct snd_pcm_substream *substream)
+static int idma_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-static int idma_prepare(struct snd_pcm_substream *substream)
+static int idma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct idma_ctrl *prtd = substream->runtime->private_data;
@@ -190,7 +186,8 @@ static int idma_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static int idma_trigger(struct snd_pcm_substream *substream, int cmd)
+static int idma_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
{
struct idma_ctrl *prtd = substream->runtime->private_data;
int ret = 0;
@@ -223,7 +220,8 @@ static int idma_trigger(struct snd_pcm_substream *substream, int cmd)
}
static snd_pcm_uframes_t
- idma_pointer(struct snd_pcm_substream *substream)
+idma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = runtime->private_data;
@@ -240,22 +238,20 @@ static snd_pcm_uframes_t
return bytes_to_frames(substream->runtime, res);
}
-static int idma_mmap(struct snd_pcm_substream *substream,
+static int idma_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long size, offset;
- int ret;
/* From snd_pcm_lib_mmap_iomem */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
size = vma->vm_end - vma->vm_start;
offset = vma->vm_pgoff << PAGE_SHIFT;
- ret = io_remap_pfn_range(vma, vma->vm_start,
+ return io_remap_pfn_range(vma, vma->vm_start,
(runtime->dma_addr + offset) >> PAGE_SHIFT,
size, vma->vm_page_prot);
-
- return ret;
}
static irqreturn_t iis_irq(int irqno, void *dev_id)
@@ -285,7 +281,8 @@ static irqreturn_t iis_irq(int irqno, void *dev_id)
return IRQ_HANDLED;
}
-static int idma_open(struct snd_pcm_substream *substream)
+static int idma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd;
@@ -311,7 +308,8 @@ static int idma_open(struct snd_pcm_substream *substream)
return 0;
}
-static int idma_close(struct snd_pcm_substream *substream)
+static int idma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = runtime->private_data;
@@ -326,19 +324,8 @@ static int idma_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops idma_ops = {
- .open = idma_open,
- .close = idma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = idma_trigger,
- .pointer = idma_pointer,
- .mmap = idma_mmap,
- .hw_params = idma_hw_params,
- .hw_free = idma_hw_free,
- .prepare = idma_prepare,
-};
-
-static void idma_free(struct snd_pcm *pcm)
+static void idma_free(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -370,11 +357,14 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream)
buf->addr = idma.lp_tx_addr;
buf->bytes = idma_hardware.buffer_bytes_max;
buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes);
+ if (!buf->area)
+ return -ENOMEM;
return 0;
}
-static int idma_new(struct snd_soc_pcm_runtime *rtd)
+static int idma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -400,10 +390,17 @@ void idma_reg_addr_init(void __iomem *regs, dma_addr_t addr)
}
EXPORT_SYMBOL_GPL(idma_reg_addr_init);
-static struct snd_soc_platform_driver asoc_idma_platform = {
- .ops = &idma_ops,
- .pcm_new = idma_new,
- .pcm_free = idma_free,
+static const struct snd_soc_component_driver asoc_idma_platform = {
+ .open = idma_open,
+ .close = idma_close,
+ .trigger = idma_trigger,
+ .pointer = idma_pointer,
+ .mmap = idma_mmap,
+ .hw_params = idma_hw_params,
+ .hw_free = idma_hw_free,
+ .prepare = idma_prepare,
+ .pcm_construct = idma_new,
+ .pcm_destruct = idma_free,
};
static int asoc_idma_platform_probe(struct platform_device *pdev)
@@ -412,7 +409,8 @@ static int asoc_idma_platform_probe(struct platform_device *pdev)
if (idma_irq < 0)
return idma_irq;
- return devm_snd_soc_register_platform(&pdev->dev, &asoc_idma_platform);
+ return devm_snd_soc_register_component(&pdev->dev, &asoc_idma_platform,
+ NULL, 0);
}
static struct platform_driver asoc_idma_driver = {
diff --git a/sound/soc/samsung/idma.h b/sound/soc/samsung/idma.h
index 8644946973e5..8a46a918ed2a 100644
--- a/sound/soc/samsung/idma.h
+++ b/sound/soc/samsung/idma.h
@@ -1,14 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- * sound/soc/samsung/idma.h
- *
* Copyright (c) 2011 Samsung Electronics Co., Ltd
* http://www.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 __SND_SOC_SAMSUNG_IDMA_H_
diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c
deleted file mode 100644
index 7fcb51faa2a0..000000000000
--- a/sound/soc/samsung/jive_wm8750.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/* sound/soc/samsung/jive_wm8750.c
- *
- * Copyright 2007,2008 Simtec Electronics
- *
- * Based on sound/soc/pxa/spitz.c
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand 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.
-*/
-
-#include <linux/module.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-
-#include "s3c2412-i2s.h"
-#include "../codecs/wm8750.h"
-
-static const struct snd_soc_dapm_route audio_map[] = {
- { "Headphone Jack", NULL, "LOUT1" },
- { "Headphone Jack", NULL, "ROUT1" },
- { "Internal Speaker", NULL, "LOUT2" },
- { "Internal Speaker", NULL, "ROUT2" },
- { "LINPUT1", NULL, "Line Input" },
- { "RINPUT1", NULL, "Line Input" },
-};
-
-static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Internal Speaker", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static int jive_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct s3c_i2sv2_rate_calc div;
- unsigned int clk = 0;
- int ret = 0;
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 48000:
- case 96000:
- clk = 12288000;
- break;
- case 11025:
- case 22050:
- case 44100:
- clk = 11289600;
- break;
- }
-
- s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params),
- s3c_i2sv2_get_clock(cpu_dai));
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, div.fs_div);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER,
- div.clk_div - 1);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops jive_ops = {
- .hw_params = jive_hw_params,
-};
-
-static struct snd_soc_dai_link jive_dai = {
- .name = "wm8750",
- .stream_name = "WM8750",
- .cpu_dai_name = "s3c2412-i2s",
- .codec_dai_name = "wm8750-hifi",
- .platform_name = "s3c2412-i2s",
- .codec_name = "wm8750.0-001a",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &jive_ops,
-};
-
-/* jive audio machine driver */
-static struct snd_soc_card snd_soc_machine_jive = {
- .name = "Jive",
- .owner = THIS_MODULE,
- .dai_link = &jive_dai,
- .num_links = 1,
-
- .dapm_widgets = wm8750_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static struct platform_device *jive_snd_device;
-
-static int __init jive_init(void)
-{
- int ret;
-
- if (!machine_is_jive())
- return 0;
-
- printk("JIVE WM8750 Audio support\n");
-
- jive_snd_device = platform_device_alloc("soc-audio", -1);
- if (!jive_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(jive_snd_device, &snd_soc_machine_jive);
- ret = platform_device_add(jive_snd_device);
-
- if (ret)
- platform_device_put(jive_snd_device);
-
- return ret;
-}
-
-static void __exit jive_exit(void)
-{
- platform_device_unregister(jive_snd_device);
-}
-
-module_init(jive_init);
-module_exit(jive_exit);
-
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("ALSA SoC Jive Audio support");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index 7cb204e649ca..d0d1b1ee326e 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -1,18 +1,12 @@
-/*
- * Littlemill audio support
- *
- * Copyright 2011 Wolfson Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Littlemill audio support
+//
+// Copyright 2011 Wolfson Microelectronics
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
-#include <linux/gpio.h>
#include <linux/module.h>
#include "../codecs/wm8994.h"
@@ -27,10 +21,10 @@ static int littlemill_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dai *aif1_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- aif1_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
- if (dapm->dev != aif1_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != aif1_dai->dev)
return 0;
switch (level) {
@@ -39,7 +33,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card,
* If we've not already clocked things via hw_params()
* then do so now, otherwise these are noops.
*/
- if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
WM8994_FLL_SRC_MCLK2, 32768,
sample_rate * 512);
@@ -74,10 +68,10 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dai *aif1_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- aif1_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
- if (dapm->dev != aif1_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != aif1_dai->dev)
return 0;
switch (level) {
@@ -101,16 +95,14 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
static int littlemill_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
sample_rate = params_rate(params);
@@ -135,7 +127,7 @@ static int littlemill_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops littlemill_ops = {
+static const struct snd_soc_ops littlemill_ops = {
.hw_params = littlemill_hw_params,
};
@@ -147,41 +139,47 @@ static const struct snd_soc_pcm_stream baseband_params = {
.channels_max = 2,
};
+SND_SOC_DAILINK_DEFS(cpu,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm8994-aif2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027",
+ "wm1250-ev1")));
+
static struct snd_soc_dai_link littlemill_dai[] = {
{
.name = "CPU",
.stream_name = "CPU",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8994-aif1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm8994-codec",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &littlemill_ops,
+ SND_SOC_DAILINK_REG(cpu),
},
{
.name = "Baseband",
.stream_name = "Baseband",
- .cpu_dai_name = "wm8994-aif2",
- .codec_dai_name = "wm1250-ev1",
- .codec_name = "wm1250-ev1.1-0027",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
- .params = &baseband_params,
+ .c2c_params = &baseband_params,
+ .num_c2c_params = 1,
+ SND_SOC_DAILINK_REG(baseband),
},
};
static int bbclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_card *card = w->dapm->card;
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *aif2_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
- aif2_dai = rtd->cpu_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+ aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -224,12 +222,15 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w,
}
static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("WM1250 Input"),
SOC_DAPM_PIN_SWITCH("WM1250 Output"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_HP("Headset Mic", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
SND_SOC_DAPM_MIC("DMIC", NULL),
@@ -239,7 +240,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "Headphone", NULL, "HPOUT1L" },
{ "Headphone", NULL, "HPOUT1R" },
@@ -254,21 +255,31 @@ static struct snd_soc_dapm_route audio_paths[] = {
};
static struct snd_soc_jack littlemill_headset;
+static struct snd_soc_jack_pin littlemill_headset_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
static int littlemill_late_probe(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct snd_soc_dai *aif1_dai;
struct snd_soc_dai *aif2_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec = rtd->codec;
- aif1_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
- aif2_dai = rtd->cpu_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+ aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);
ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
@@ -280,20 +291,22 @@ static int littlemill_late_probe(struct snd_soc_card *card)
if (ret < 0)
return ret;
- ret = snd_soc_card_jack_new(card, "Headset",
- SND_JACK_HEADSET | SND_JACK_MECHANICAL |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3 |
- SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &littlemill_headset, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_MECHANICAL |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5,
+ &littlemill_headset,
+ littlemill_headset_pins,
+ ARRAY_SIZE(littlemill_headset_pins));
if (ret)
return ret;
/* This will check device compatibility itself */
- wm8958_mic_detect(codec, &littlemill_headset, NULL, NULL, NULL, NULL);
+ wm8958_mic_detect(component, &littlemill_headset, NULL, NULL, NULL, NULL);
/* As will this */
- wm8994_mic_detect(codec, &littlemill_headset, 1);
+ wm8994_mic_detect(component, &littlemill_headset, 1);
return 0;
}
@@ -326,8 +339,7 @@ static int littlemill_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c
deleted file mode 100644
index 9342fc270c2b..000000000000
--- a/sound/soc/samsung/ln2440sbc_alc650.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SoC audio for ln2440sbc
- *
- * Copyright 2007 KonekTel, a.s.
- * Author: Ivan Kuten
- * ivan.kuten@promwad.com
- *
- * Heavily based on smdk2443_wm9710.c
- * 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 version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <sound/soc.h>
-
-static struct snd_soc_card ln2440sbc;
-
-static struct snd_soc_dai_link ln2440sbc_dai[] = {
-{
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "samsung-ac97",
- .codec_dai_name = "ac97-hifi",
- .codec_name = "ac97-codec",
- .platform_name = "samsung-ac97",
-},
-};
-
-static struct snd_soc_card ln2440sbc = {
- .name = "LN2440SBC",
- .owner = THIS_MODULE,
- .dai_link = ln2440sbc_dai,
- .num_links = ARRAY_SIZE(ln2440sbc_dai),
-};
-
-static struct platform_device *ln2440sbc_snd_ac97_device;
-
-static int __init ln2440sbc_init(void)
-{
- int ret;
-
- ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1);
- if (!ln2440sbc_snd_ac97_device)
- return -ENOMEM;
-
- platform_set_drvdata(ln2440sbc_snd_ac97_device, &ln2440sbc);
- ret = platform_device_add(ln2440sbc_snd_ac97_device);
-
- if (ret)
- platform_device_put(ln2440sbc_snd_ac97_device);
-
- return ret;
-}
-
-static void __exit ln2440sbc_exit(void)
-{
- platform_device_unregister(ln2440sbc_snd_ac97_device);
-}
-
-module_init(ln2440sbc_init);
-module_exit(ln2440sbc_exit);
-
-/* Module information */
-MODULE_AUTHOR("Ivan Kuten");
-MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 0d0f58208b75..ca9b78007dfe 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -1,18 +1,12 @@
-/*
- * Lowland audio support
- *
- * Copyright 2011 Wolfson Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Lowland audio support
+//
+// Copyright 2011 Wolfson Microelectronics
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
-#include <linux/gpio.h>
#include <linux/module.h>
#include "../codecs/wm5100.h"
@@ -27,20 +21,24 @@ static struct snd_soc_jack lowland_headset;
static struct snd_soc_jack_pin lowland_headset_pins[] = {
{
.pin = "Headphone",
- .mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
+ .mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
};
static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
int ret;
- ret = snd_soc_codec_set_sysclk(codec, WM5100_CLK_SYSCLK,
+ ret = snd_soc_component_set_sysclk(component, WM5100_CLK_SYSCLK,
WM5100_CLKSRC_MCLK1, MCLK1_RATE,
SND_SOC_CLOCK_IN);
if (ret < 0) {
@@ -49,33 +47,35 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd)
}
/* Clock OPCLK, used by the other audio components. */
- ret = snd_soc_codec_set_sysclk(codec, WM5100_CLK_OPCLK, 0,
+ ret = snd_soc_component_set_sysclk(component, WM5100_CLK_OPCLK, 0,
CLKOUT_RATE, 0);
if (ret < 0) {
pr_err("Failed to set OPCLK rate: %d\n", ret);
return ret;
}
- ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &lowland_headset, lowland_headset_pins,
- ARRAY_SIZE(lowland_headset_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
+ SND_JACK_LINEOUT | SND_JACK_HEADSET |
+ SND_JACK_BTN_0,
+ &lowland_headset, lowland_headset_pins,
+ ARRAY_SIZE(lowland_headset_pins));
if (ret)
return ret;
- wm5100_detect(codec, &lowland_headset);
+ wm5100_detect(component, &lowland_headset);
return 0;
}
static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
- snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT");
/* At any time the WM9081 is active it will have this clock */
- return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
+ return snd_soc_component_set_sysclk(component, WM9081_SYSCLK_MCLK, 0,
CLKOUT_RATE, 0);
}
@@ -87,45 +87,52 @@ static const struct snd_soc_pcm_stream sub_params = {
.channels_max = 2,
};
+SND_SOC_DAILINK_DEFS(cpu,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm5100.1-001a", "wm5100-aif1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm5100-aif2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
+
+SND_SOC_DAILINK_DEFS(speaker,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm5100-aif3")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
+
static struct snd_soc_dai_link lowland_dai[] = {
{
.name = "CPU",
.stream_name = "CPU",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm5100-aif1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm5100.1-001a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.init = lowland_wm5100_init,
+ SND_SOC_DAILINK_REG(cpu),
},
{
.name = "Baseband",
.stream_name = "Baseband",
- .cpu_dai_name = "wm5100-aif2",
- .codec_dai_name = "wm1250-ev1",
- .codec_name = "wm1250-ev1.1-0027",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(baseband),
},
{
.name = "Sub Speaker",
.stream_name = "Sub Speaker",
- .cpu_dai_name = "wm5100-aif3",
- .codec_dai_name = "wm9081-hifi",
- .codec_name = "wm9081.1-006c",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
- .params = &sub_params,
+ .c2c_params = &sub_params,
+ .num_c2c_params = 1,
.init = lowland_wm9081_init,
+ SND_SOC_DAILINK_REG(speaker),
},
};
static struct snd_soc_codec_conf lowland_codec_conf[] = {
{
- .dev_name = "wm9081.1-006c",
+ .dlc = COMP_CODEC_CONF("wm9081.1-006c"),
.name_prefix = "Sub",
},
};
@@ -137,11 +144,13 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("WM1250 Input"),
SOC_DAPM_PIN_SWITCH("WM1250 Output"),
SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_SPK("Main Speaker", NULL),
@@ -149,7 +158,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_MIC("Main DMIC", NULL),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "Sub IN1", NULL, "HPOUT2L" },
{ "Sub IN2", NULL, "HPOUT2R" },
@@ -183,8 +192,7 @@ static int lowland_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c
new file mode 100644
index 000000000000..bc34dbbb50c9
--- /dev/null
+++ b/sound/soc/samsung/midas_wm1811.c
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Midas audio support
+//
+// Copyright (C) 2018 Simon Shields <simon@lineageos.org>
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/consumer.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/input-event-codes.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "i2s.h"
+#include "../codecs/wm8994.h"
+
+/*
+ * The MCLK1 clock source is XCLKOUT with its mux set to the external fixed rate
+ * oscillator (XXTI).
+ */
+#define MCLK1_RATE 24000000U
+#define MCLK2_RATE 32768U
+#define DEFAULT_FLL1_RATE 11289600U
+
+struct midas_priv {
+ struct gpio_desc *gpio_fm_sel;
+ struct gpio_desc *gpio_lineout_sel;
+ struct gpio_desc *gpio_headset_detect;
+ struct gpio_desc *gpio_headset_key;
+ struct iio_channel *adc_headset_detect;
+ unsigned int fll1_rate;
+
+ struct snd_soc_jack headset_jack;
+};
+
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+/*
+ * min_mv/max_mv values in this struct are set up based on DT values.
+ */
+static struct snd_soc_jack_zone headset_jack_zones[] = {
+ { .jack_type = SND_JACK_HEADPHONE, },
+ { .jack_type = SND_JACK_HEADSET, },
+ { .jack_type = SND_JACK_HEADPHONE, },
+};
+
+/*
+ * This is used for manual detection in headset_key_check, we reuse the
+ * structure since it's convenient.
+ *
+ * min_mv/max_mv values in this struct are set up based on DT values.
+ */
+static struct snd_soc_jack_zone headset_key_zones[] = {
+ { .jack_type = SND_JACK_BTN_0, }, /* Media */
+ { .jack_type = SND_JACK_BTN_1, }, /* Volume Up */
+ { .jack_type = SND_JACK_BTN_2, }, /* Volume Down */
+};
+
+static int headset_jack_check(void *data)
+{
+ struct snd_soc_component *codec = data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(codec);
+ struct midas_priv *priv = snd_soc_card_get_drvdata(codec->card);
+ int adc, ret;
+ int jack_type = 0;
+
+ if (!gpiod_get_value_cansleep(priv->gpio_headset_detect))
+ return 0;
+
+ /* Enable headset mic bias regulator so that the ADC reading works */
+ ret = snd_soc_dapm_force_enable_pin(dapm, "headset-mic-bias");
+ if (ret < 0) {
+ pr_err("%s: Failed to enable headset mic bias regulator (%d), assuming headphones\n",
+ __func__, ret);
+ return SND_JACK_HEADPHONE;
+ }
+ snd_soc_dapm_sync(dapm);
+
+ /* Sleep for a small amount of time to get the value to stabilize */
+ msleep(20);
+
+ ret = iio_read_channel_processed(priv->adc_headset_detect, &adc);
+ if (ret) {
+ pr_err("%s: Failed to read ADC (%d), assuming headphones\n",
+ __func__, ret);
+ jack_type = SND_JACK_HEADPHONE;
+ goto out;
+ }
+ pr_debug("%s: ADC value is %d\n", __func__, adc);
+
+ jack_type = snd_soc_jack_get_type(&priv->headset_jack, adc);
+
+out:
+ ret = snd_soc_dapm_disable_pin(dapm, "headset-mic-bias");
+ if (ret < 0)
+ pr_err("%s: Failed to disable headset mic bias regulator (%d)\n",
+ __func__, ret);
+ snd_soc_dapm_sync(dapm);
+
+ return jack_type;
+}
+
+static int headset_key_check(void *data)
+{
+ struct snd_soc_component *codec = data;
+ struct midas_priv *priv = snd_soc_card_get_drvdata(codec->card);
+ int adc, i, ret;
+
+ if (!gpiod_get_value_cansleep(priv->gpio_headset_key))
+ return 0;
+
+ /* Filter out keypresses when 4 pole jack not detected */
+ if (!(priv->headset_jack.status & SND_JACK_MICROPHONE))
+ return 0;
+
+ ret = iio_read_channel_processed(priv->adc_headset_detect, &adc);
+ if (ret) {
+ pr_err("%s: Failed to read ADC (%d), can't detect key type\n",
+ __func__, ret);
+ return 0;
+ }
+ pr_debug("%s: ADC value is %d\n", __func__, adc);
+
+ for (i = 0; i < ARRAY_SIZE(headset_key_zones); i++) {
+ if (adc >= headset_key_zones[i].min_mv &&
+ adc <= headset_key_zones[i].max_mv) {
+ return headset_key_zones[i].jack_type;
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_jack_gpio headset_gpio[] = {
+ {
+ .name = "Headset Jack",
+ .report = SND_JACK_HEADSET,
+ .debounce_time = 150,
+ .jack_status_check = headset_jack_check,
+ },
+ {
+ .name = "Headset Key",
+ .report = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ .debounce_time = 30,
+ .jack_status_check = headset_key_check,
+ },
+};
+
+static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct midas_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+
+ if (!rate)
+ rate = priv->fll1_rate;
+ /*
+ * If no new rate is requested, set FLL1 to a sane default for jack
+ * detection.
+ */
+ if (!rate)
+ rate = DEFAULT_FLL1_RATE;
+
+ if (rate != priv->fll1_rate && priv->fll1_rate) {
+ /* while reconfiguring, switch to MCLK2 for SYSCLK */
+ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
+ MCLK2_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Unable to switch to MCLK2: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
+ MCLK1_RATE, rate);
+ if (ret < 0) {
+ dev_err(card->dev, "Failed to set FLL1 rate: %d\n", ret);
+ return ret;
+ }
+ priv->fll1_rate = rate;
+
+ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1,
+ priv->fll1_rate, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Failed to set SYSCLK source: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK, 0,
+ SAMSUNG_I2S_OPCLK_PCLK);
+ if (ret < 0) {
+ dev_err(card->dev, "Failed to set OPCLK source: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int midas_stop_fll1(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct midas_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
+ MCLK2_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Unable to switch to MCLK2: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0);
+ if (ret < 0) {
+ dev_err(card->dev, "Unable to stop FLL1: %d\n", ret);
+ return ret;
+ }
+
+ priv->fll1_rate = 0;
+
+ return 0;
+}
+
+static int midas_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int pll_out;
+
+ /* AIF1CLK should be at least 3MHz for "optimal performance" */
+ if (params_rate(params) == 8000 || params_rate(params) == 11025)
+ pll_out = params_rate(params) * 512;
+ else
+ pll_out = params_rate(params) * 256;
+
+ return midas_start_fll1(rtd, pll_out);
+}
+
+static const struct snd_soc_ops midas_aif1_ops = {
+ .hw_params = midas_aif1_hw_params,
+};
+
+/*
+ * We only have a single external speaker, so mix stereo data
+ * to a single mono stream.
+ */
+static int midas_ext_spkmode(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = snd_soc_component_update_bits(codec, WM8994_SPKOUT_MIXERS,
+ WM8994_SPKMIXR_TO_SPKOUTL_MASK,
+ WM8994_SPKMIXR_TO_SPKOUTL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = snd_soc_component_update_bits(codec, WM8994_SPKOUT_MIXERS,
+ WM8994_SPKMIXR_TO_SPKOUTL_MASK,
+ 0);
+ break;
+ }
+
+ return ret;
+}
+
+static int midas_fm_set(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct midas_priv *priv = snd_soc_card_get_drvdata(card);
+
+ if (!priv->gpio_fm_sel)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpio_fm_sel, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpio_fm_sel, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int midas_line_set(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct midas_priv *priv = snd_soc_card_get_drvdata(card);
+
+ if (!priv->gpio_lineout_sel)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpio_lineout_sel, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpio_lineout_sel, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new midas_controls[] = {
+ SOC_DAPM_PIN_SWITCH("HP"),
+
+ SOC_DAPM_PIN_SWITCH("SPK"),
+ SOC_DAPM_PIN_SWITCH("RCV"),
+
+ SOC_DAPM_PIN_SWITCH("LINE"),
+ SOC_DAPM_PIN_SWITCH("HDMI"),
+
+ SOC_DAPM_PIN_SWITCH("Main Mic"),
+ SOC_DAPM_PIN_SWITCH("Sub Mic"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+
+ SOC_DAPM_PIN_SWITCH("FM In"),
+};
+
+static const struct snd_soc_dapm_widget midas_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("HP", NULL),
+
+ SND_SOC_DAPM_SPK("SPK", midas_ext_spkmode),
+ SND_SOC_DAPM_SPK("RCV", NULL),
+
+ /* FIXME: toggle MAX77693 on i9300/i9305 */
+ SND_SOC_DAPM_LINE("LINE", midas_line_set),
+ SND_SOC_DAPM_LINE("HDMI", NULL),
+ SND_SOC_DAPM_LINE("FM In", midas_fm_set),
+
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("headset-mic-bias", 0, 0),
+ SND_SOC_DAPM_MIC("Main Mic", NULL),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("mic-bias", 0, 0),
+ SND_SOC_DAPM_MIC("Sub Mic", NULL),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("submic-bias", 0, 0),
+};
+
+/* Default routing; supplemented by audio-routing DT property */
+static const struct snd_soc_dapm_route midas_dapm_routes[] = {
+ /* Bind microphones with their respective regulator supplies */
+ {"Main Mic", NULL, "mic-bias"},
+ {"Sub Mic", NULL, "submic-bias"},
+ {"Headset Mic", NULL, "headset-mic-bias"},
+};
+
+static int midas_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card,
+ &card->dai_link[0]);
+ struct snd_soc_dai *aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ if (snd_soc_dapm_to_dev(dapm) != aif1_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ return midas_stop_fll1(rtd);
+ case SND_SOC_BIAS_PREPARE:
+ return midas_start_fll1(rtd, 0);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int midas_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card,
+ &card->dai_link[0]);
+ struct snd_soc_dai *aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct midas_priv *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ /* Use MCLK2 as SYSCLK for boot */
+ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MCLK2_RATE,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(aif1_dai->dev, "Failed to switch to MCLK2: %d\n", ret);
+ return ret;
+ }
+
+ if (!priv->gpio_headset_detect) {
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_MECHANICAL |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5,
+ &priv->headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret)
+ return ret;
+
+ wm8958_mic_detect(aif1_dai->component, &priv->headset_jack,
+ NULL, NULL, NULL, NULL);
+ } else {
+ /* Some devices (n8000, t310) use a GPIO to detect the jack. */
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &priv->headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret) {
+ dev_err(card->dev,
+ "Failed to set up headset pins: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_jack_add_zones(&priv->headset_jack,
+ ARRAY_SIZE(headset_jack_zones),
+ headset_jack_zones);
+ if (ret) {
+ dev_err(card->dev,
+ "Failed to set up headset zones: %d\n", ret);
+ return ret;
+ }
+
+ headset_gpio[0].data = aif1_dai->component;
+ headset_gpio[0].desc = priv->gpio_headset_detect;
+
+ headset_gpio[1].data = aif1_dai->component;
+ headset_gpio[1].desc = priv->gpio_headset_key;
+
+ snd_jack_set_key(priv->headset_jack.jack,
+ SND_JACK_BTN_0, KEY_MEDIA);
+ snd_jack_set_key(priv->headset_jack.jack,
+ SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(priv->headset_jack.jack,
+ SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_jack_add_gpios(&priv->headset_jack,
+ ARRAY_SIZE(headset_gpio),
+ headset_gpio);
+ if (ret)
+ dev_err(card->dev,
+ "Failed to set up headset jack GPIOs: %d\n",
+ ret);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver midas_ext_dai[] = {
+ {
+ .name = "Voice call",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "Bluetooth",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+static const struct snd_soc_component_driver midas_component = {
+ .name = "midas-audio",
+};
+
+SND_SOC_DAILINK_DEFS(wm1811_hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(wm1811_voice,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(wm1811_bt,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link midas_dai[] = {
+ {
+ .name = "WM8994 AIF1",
+ .stream_name = "HiFi Primary",
+ .ops = &midas_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(wm1811_hifi),
+ }, {
+ .name = "WM1811 Voice",
+ .stream_name = "Voice call",
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(wm1811_voice),
+ }, {
+ .name = "WM1811 BT",
+ .stream_name = "Bluetooth",
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(wm1811_bt),
+ },
+};
+
+static struct snd_soc_card midas_card = {
+ .name = "Midas WM1811",
+ .owner = THIS_MODULE,
+
+ .dai_link = midas_dai,
+ .num_links = ARRAY_SIZE(midas_dai),
+ .controls = midas_controls,
+ .num_controls = ARRAY_SIZE(midas_controls),
+ .dapm_widgets = midas_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(midas_dapm_widgets),
+ .dapm_routes = midas_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(midas_dapm_routes),
+
+ .set_bias_level = midas_set_bias_level,
+ .late_probe = midas_late_probe,
+};
+
+static int midas_probe(struct platform_device *pdev)
+{
+ struct device_node *cpu_dai_node = NULL, *codec_dai_node = NULL;
+ struct device_node *cpu = NULL, *codec = NULL;
+ struct snd_soc_card *card = &midas_card;
+ struct device *dev = &pdev->dev;
+ static struct snd_soc_dai_link *dai_link;
+ enum iio_chan_type channel_type;
+ u32 fourpole_threshold[2];
+ u32 button_threshold[3];
+ struct midas_priv *priv;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, priv);
+ card->dev = dev;
+
+ priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_fm_sel))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_fm_sel),
+ "Failed to get FM selection GPIO\n");
+
+ priv->gpio_lineout_sel = devm_gpiod_get_optional(dev, "lineout-sel",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_lineout_sel))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_lineout_sel),
+ "Failed to get line out selection GPIO\n");
+
+ priv->gpio_headset_detect = devm_gpiod_get_optional(dev,
+ "headset-detect", GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_detect))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_headset_detect),
+ "Failed to get headset jack detect GPIO\n");
+
+ if (priv->gpio_headset_detect) {
+ priv->adc_headset_detect = devm_iio_channel_get(dev,
+ "headset-detect");
+ if (IS_ERR(priv->adc_headset_detect))
+ return dev_err_probe(dev,
+ PTR_ERR(priv->adc_headset_detect),
+ "Failed to get ADC channel\n");
+
+ ret = iio_get_channel_type(priv->adc_headset_detect,
+ &channel_type);
+ if (ret) {
+ dev_err(dev, "Failed to get ADC channel type\n");
+ return ret;
+ }
+
+ if (channel_type != IIO_VOLTAGE) {
+ dev_err(dev, "ADC channel is not voltage\n");
+ return -EINVAL;
+ }
+
+ priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
+ GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_key))
+ return dev_err_probe(dev,
+ PTR_ERR(priv->gpio_headset_key),
+ "Failed to get headset key GPIO\n");
+
+ ret = of_property_read_u32_array(dev->of_node,
+ "samsung,headset-4pole-threshold-microvolt",
+ fourpole_threshold,
+ ARRAY_SIZE(fourpole_threshold));
+ if (ret) {
+ dev_err(dev, "Failed to get 4-pole jack detection threshold\n");
+ return ret;
+ }
+
+ if (fourpole_threshold[0] > fourpole_threshold[1]) {
+ dev_err(dev, "Invalid 4-pole jack detection threshold value\n");
+ return -EINVAL;
+ }
+
+ headset_jack_zones[0].max_mv = (fourpole_threshold[0]);
+ headset_jack_zones[1].min_mv = (fourpole_threshold[0] + 1);
+
+ headset_jack_zones[1].max_mv = (fourpole_threshold[1]);
+ headset_jack_zones[2].min_mv = (fourpole_threshold[1] + 1);
+
+ ret = of_property_read_u32_array(dev->of_node,
+ "samsung,headset-button-threshold-microvolt",
+ button_threshold,
+ ARRAY_SIZE(button_threshold));
+ if (ret) {
+ dev_err(dev, "Failed to get headset button detection threshold\n");
+ return ret;
+ }
+
+ if (button_threshold[0] > button_threshold[1] ||
+ button_threshold[1] > button_threshold[2]) {
+ dev_err(dev, "Invalid headset button detection threshold value\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (i != 0 && button_threshold[i] <= 0) {
+ dev_err(dev, "Invalid headset button detection threshold value\n");
+ return -EINVAL;
+ }
+
+ headset_key_zones[i].min_mv = button_threshold[i];
+
+ if (i == 2)
+ headset_key_zones[i].max_mv = UINT_MAX;
+ else
+ headset_key_zones[i].max_mv = \
+ (button_threshold[i+1] - 1);
+ }
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret < 0) {
+ dev_err(dev, "Card name is not specified\n");
+ return ret;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret < 0) {
+ /* Backwards compatible way */
+ ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+ if (ret < 0) {
+ dev_err(dev, "Audio routing invalid/unspecified\n");
+ return ret;
+ }
+ }
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ if (!cpu)
+ return -EINVAL;
+
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ if (!codec) {
+ of_node_put(cpu);
+ return -EINVAL;
+ }
+
+ cpu_dai_node = of_parse_phandle(cpu, "sound-dai", 0);
+ of_node_put(cpu);
+ if (!cpu_dai_node) {
+ dev_err(dev, "parsing cpu/sound-dai failed\n");
+ of_node_put(codec);
+ return -EINVAL;
+ }
+
+ codec_dai_node = of_parse_phandle(codec, "sound-dai", 0);
+ of_node_put(codec);
+ if (!codec_dai_node) {
+ dev_err(dev, "audio-codec property invalid/missing\n");
+ ret = -EINVAL;
+ goto put_cpu_dai_node;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ dai_link->codecs->of_node = codec_dai_node;
+ dai_link->cpus->of_node = cpu_dai_node;
+ dai_link->platforms->of_node = cpu_dai_node;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &midas_component,
+ midas_ext_dai, ARRAY_SIZE(midas_ext_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto put_codec_dai_node;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register card: %d\n", ret);
+ goto put_codec_dai_node;
+ }
+
+ return 0;
+
+put_codec_dai_node:
+ of_node_put(codec_dai_node);
+put_cpu_dai_node:
+ of_node_put(cpu_dai_node);
+ return ret;
+}
+
+static const struct of_device_id midas_of_match[] = {
+ { .compatible = "samsung,midas-audio" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, midas_of_match);
+
+static struct platform_driver midas_driver = {
+ .driver = {
+ .name = "midas-audio",
+ .of_match_table = midas_of_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = midas_probe,
+};
+module_platform_driver(midas_driver);
+
+MODULE_AUTHOR("Simon Shields <simon@lineageos.org>");
+MODULE_DESCRIPTION("ASoC support for Midas");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c
deleted file mode 100644
index 65602b935377..000000000000
--- a/sound/soc/samsung/neo1973_wm8753.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * neo1973_wm8753.c -- SoC audio for Openmoko Neo1973 and Freerunner devices
- *
- * Copyright 2007 Openmoko Inc
- * Author: Graeme Gregory <graeme@openmoko.org>
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- * Copyright 2009 Wolfson Microelectronics
- *
- * 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/platform_device.h>
-#include <linux/gpio.h>
-
-#include <sound/soc.h>
-
-#include <mach/gpio-samsung.h>
-#include <asm/mach-types.h>
-#include "regs-iis.h"
-
-#include "../codecs/wm8753.h"
-#include "s3c24xx-i2s.h"
-
-static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int pll_out = 0, bclk = 0;
- int ret = 0;
- unsigned long iis_clkrate;
-
- iis_clkrate = s3c24xx_i2s_get_clockrate();
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- pll_out = 12288000;
- break;
- case 48000:
- bclk = WM8753_BCLK_DIV_4;
- pll_out = 12288000;
- break;
- case 96000:
- bclk = WM8753_BCLK_DIV_2;
- pll_out = 12288000;
- break;
- case 11025:
- bclk = WM8753_BCLK_DIV_16;
- pll_out = 11289600;
- break;
- case 22050:
- bclk = WM8753_BCLK_DIV_8;
- pll_out = 11289600;
- break;
- case 44100:
- bclk = WM8753_BCLK_DIV_4;
- pll_out = 11289600;
- break;
- case 88200:
- bclk = WM8753_BCLK_DIV_2;
- pll_out = 11289600;
- break;
- }
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set MCLK division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
- S3C2410_IISMOD_32FS);
- if (ret < 0)
- return ret;
-
- /* set codec BCLK division for sample rate */
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
- if (ret < 0)
- return ret;
-
- /* set prescaler division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
- S3C24XX_PRESCALE(4, 4));
- if (ret < 0)
- return ret;
-
- /* codec PLL input is PCLK/4 */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
- iis_clkrate / 4, pll_out);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- /* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
-}
-
-/*
- * Neo1973 WM8753 HiFi DAI opserations.
- */
-static struct snd_soc_ops neo1973_hifi_ops = {
- .hw_params = neo1973_hifi_hw_params,
- .hw_free = neo1973_hifi_hw_free,
-};
-
-static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int pcmdiv = 0;
- int ret = 0;
- unsigned long iis_clkrate;
-
- iis_clkrate = s3c24xx_i2s_get_clockrate();
-
- if (params_rate(params) != 8000)
- return -EINVAL;
- if (params_channels(params) != 1)
- return -EINVAL;
-
- pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set codec PCM division for sample rate */
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
- if (ret < 0)
- return ret;
-
- /* configure and enable PLL for 12.288MHz output */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
- iis_clkrate / 4, 12288000);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- /* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
-}
-
-static struct snd_soc_ops neo1973_voice_ops = {
- .hw_params = neo1973_voice_hw_params,
- .hw_free = neo1973_voice_hw_free,
-};
-
-static int gta02_speaker_enabled;
-
-static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- gta02_speaker_enabled = ucontrol->value.integer.value[0];
-
- gpio_set_value(S3C2410_GPJ(2), !gta02_speaker_enabled);
-
- return 0;
-}
-
-static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = gta02_speaker_enabled;
- return 0;
-}
-
-static int lm4853_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- gpio_set_value(S3C2410_GPJ(1), SND_SOC_DAPM_EVENT_OFF(event));
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("GSM Line Out", NULL),
- SND_SOC_DAPM_LINE("GSM Line In", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Handset Mic", NULL),
- SND_SOC_DAPM_SPK("Handset Spk", NULL),
- SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
-};
-
-static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {
- /* Connections to the GSM Module */
- {"GSM Line Out", NULL, "MONO1"},
- {"GSM Line Out", NULL, "MONO2"},
- {"RXP", NULL, "GSM Line In"},
- {"RXN", NULL, "GSM Line In"},
-
- /* Connections to Headset */
- {"MIC1", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Headset Mic"},
-
- /* Call Mic */
- {"MIC2", NULL, "Mic Bias"},
- {"MIC2N", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Handset Mic"},
-
- /* Connect the ALC pins */
- {"ACIN", NULL, "ACOP"},
-
- /* Connections to the amp */
- {"Stereo Out", NULL, "LOUT1"},
- {"Stereo Out", NULL, "ROUT1"},
-
- /* Call Speaker */
- {"Handset Spk", NULL, "LOUT2"},
- {"Handset Spk", NULL, "ROUT2"},
-};
-
-static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
- SOC_DAPM_PIN_SWITCH("GSM Line Out"),
- SOC_DAPM_PIN_SWITCH("GSM Line In"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Handset Mic"),
- SOC_DAPM_PIN_SWITCH("Handset Spk"),
- SOC_DAPM_PIN_SWITCH("Stereo Out"),
-
- SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0,
- lm4853_get_spk,
- lm4853_set_spk),
-};
-
-static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
-
- /* set endpoints to default off mode */
- snd_soc_dapm_disable_pin(&card->dapm, "GSM Line Out");
- snd_soc_dapm_disable_pin(&card->dapm, "GSM Line In");
- snd_soc_dapm_disable_pin(&card->dapm, "Headset Mic");
- snd_soc_dapm_disable_pin(&card->dapm, "Handset Mic");
- snd_soc_dapm_disable_pin(&card->dapm, "Stereo Out");
- snd_soc_dapm_disable_pin(&card->dapm, "Handset Spk");
-
- /* allow audio paths from the GSM modem to run during suspend */
- snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line Out");
- snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line In");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Mic");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Stereo Out");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Spk");
-
- return 0;
-}
-
-static struct snd_soc_dai_link neo1973_dai[] = {
-{ /* Hifi Playback - for similatious use with voice below */
- .name = "WM8753",
- .stream_name = "WM8753 HiFi",
- .platform_name = "s3c24xx-iis",
- .cpu_dai_name = "s3c24xx-iis",
- .codec_dai_name = "wm8753-hifi",
- .codec_name = "wm8753.0-001a",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .init = neo1973_wm8753_init,
- .ops = &neo1973_hifi_ops,
-},
-{ /* Voice via BT */
- .name = "Bluetooth",
- .stream_name = "Voice",
- .cpu_dai_name = "bt-sco-pcm",
- .codec_dai_name = "wm8753-voice",
- .codec_name = "wm8753.0-001a",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &neo1973_voice_ops,
-},
-};
-
-static struct snd_soc_aux_dev neo1973_aux_devs[] = {
- {
- .name = "dfbmcs320",
- .codec_name = "dfbmcs320.0",
- },
-};
-
-static struct snd_soc_codec_conf neo1973_codec_conf[] = {
- {
- .dev_name = "lm4857.0-007c",
- .name_prefix = "Amp",
- },
-};
-
-static const struct gpio neo1973_gta02_gpios[] = {
- { S3C2410_GPJ(2), GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" },
- { S3C2410_GPJ(1), GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" },
-};
-
-static struct snd_soc_card neo1973 = {
- .name = "neo1973",
- .owner = THIS_MODULE,
- .dai_link = neo1973_dai,
- .num_links = ARRAY_SIZE(neo1973_dai),
- .aux_dev = neo1973_aux_devs,
- .num_aux_devs = ARRAY_SIZE(neo1973_aux_devs),
- .codec_conf = neo1973_codec_conf,
- .num_configs = ARRAY_SIZE(neo1973_codec_conf),
-
- .controls = neo1973_wm8753_controls,
- .num_controls = ARRAY_SIZE(neo1973_wm8753_controls),
- .dapm_widgets = neo1973_wm8753_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(neo1973_wm8753_dapm_widgets),
- .dapm_routes = neo1973_wm8753_routes,
- .num_dapm_routes = ARRAY_SIZE(neo1973_wm8753_routes),
- .fully_routed = true,
-};
-
-static struct platform_device *neo1973_snd_device;
-
-static int __init neo1973_init(void)
-{
- int ret;
-
- if (!machine_is_neo1973_gta02())
- return -ENODEV;
-
- if (machine_is_neo1973_gta02()) {
- neo1973.name = "neo1973gta02";
- neo1973.num_aux_devs = 1;
-
- ret = gpio_request_array(neo1973_gta02_gpios,
- ARRAY_SIZE(neo1973_gta02_gpios));
- if (ret)
- return ret;
- }
-
- neo1973_snd_device = platform_device_alloc("soc-audio", -1);
- if (!neo1973_snd_device) {
- ret = -ENOMEM;
- goto err_gpio_free;
- }
-
- platform_set_drvdata(neo1973_snd_device, &neo1973);
- ret = platform_device_add(neo1973_snd_device);
-
- if (ret)
- goto err_put_device;
-
- return 0;
-
-err_put_device:
- platform_device_put(neo1973_snd_device);
-err_gpio_free:
- if (machine_is_neo1973_gta02()) {
- gpio_free_array(neo1973_gta02_gpios,
- ARRAY_SIZE(neo1973_gta02_gpios));
- }
- return ret;
-}
-module_init(neo1973_init);
-
-static void __exit neo1973_exit(void)
-{
- platform_device_unregister(neo1973_snd_device);
-
- if (machine_is_neo1973_gta02()) {
- gpio_free_array(neo1973_gta02_gpios,
- ARRAY_SIZE(neo1973_gta02_gpios));
- }
-}
-module_exit(neo1973_exit);
-
-/* Module information */
-MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
-MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
new file mode 100644
index 000000000000..ab3398f39f4a
--- /dev/null
+++ b/sound/soc/samsung/odroid.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2017 Samsung Electronics Co., Ltd.
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "i2s.h"
+#include "i2s-regs.h"
+
+struct odroid_priv {
+ struct snd_soc_card card;
+ struct clk *clk_i2s_bus;
+ struct clk *sclk_i2s;
+
+ /* Spinlock protecting fields below */
+ spinlock_t lock;
+ unsigned int be_sample_rate;
+ bool be_active;
+};
+
+static int odroid_card_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+
+ return 0;
+}
+
+static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->be_active && priv->be_sample_rate != params_rate(params))
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static const struct snd_soc_ops odroid_card_fe_ops = {
+ .startup = odroid_card_fe_startup,
+ .hw_params = odroid_card_fe_hw_params,
+};
+
+static int odroid_card_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ unsigned int pll_freq, rclk_freq, rfs;
+ unsigned long flags;
+ int ret;
+
+ switch (params_rate(params)) {
+ case 64000:
+ pll_freq = 196608001U;
+ rfs = 384;
+ break;
+ case 44100:
+ case 88200:
+ pll_freq = 180633609U;
+ rfs = 512;
+ break;
+ case 32000:
+ case 48000:
+ case 96000:
+ pll_freq = 196608001U;
+ rfs = 512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(priv->clk_i2s_bus, pll_freq / 2 + 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * We add 2 to the rclk_freq value in order to avoid too low clock
+ * frequency values due to the EPLL output frequency not being exact
+ * multiple of the audio sampling rate.
+ */
+ rclk_freq = params_rate(params) * rfs + 2;
+
+ ret = clk_set_rate(priv->sclk_i2s, rclk_freq);
+ if (ret < 0)
+ return ret;
+
+ if (rtd->dai_link->num_codecs > 1) {
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 1);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->be_sample_rate = params_rate(params);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ priv->be_active = true;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->be_active = false;
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static const struct snd_soc_ops odroid_card_be_ops = {
+ .hw_params = odroid_card_be_hw_params,
+ .trigger = odroid_card_be_trigger,
+};
+
+/* DAPM routes for backward compatibility with old DTS */
+static const struct snd_soc_dapm_route odroid_dapm_routes[] = {
+ { "I2S Playback", NULL, "Mixer DAI TX" },
+ { "HiFi Playback", NULL, "Mixer DAI TX" },
+};
+
+SND_SOC_DAILINK_DEFS(primary,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("3830000.i2s")));
+
+SND_SOC_DAILINK_DEFS(mixer,
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(secondary,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("3830000.i2s-sec")));
+
+static struct snd_soc_dai_link odroid_card_dais[] = {
+ {
+ /* Primary FE <-> BE link */
+ .ops = &odroid_card_fe_ops,
+ .name = "Primary",
+ .stream_name = "Primary",
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(primary),
+ }, {
+ /* BE <-> CODECs link */
+ .name = "I2S Mixer",
+ .ops = &odroid_card_be_ops,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(mixer),
+ }, {
+ /* Secondary FE <-> BE link */
+ .ops = &odroid_card_fe_ops,
+ .name = "Secondary",
+ .stream_name = "Secondary",
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(secondary),
+ }
+};
+
+static int odroid_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *cpu_dai = NULL;
+ struct device_node *cpu, *codec;
+ struct odroid_priv *priv;
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *link, *codec_link;
+ int num_pcms, ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ card = &priv->card;
+ card->dev = dev;
+
+ card->owner = THIS_MODULE;
+ card->fully_routed = true;
+
+ spin_lock_init(&priv->lock);
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret < 0)
+ return ret;
+
+ if (of_property_present(dev->of_node, "samsung,audio-widgets")) {
+ ret = snd_soc_of_parse_audio_simple_widgets(card,
+ "samsung,audio-widgets");
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = 0;
+ if (of_property_present(dev->of_node, "audio-routing"))
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ else if (of_property_present(dev->of_node, "samsung,audio-routing"))
+ ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+ if (ret < 0)
+ return ret;
+
+ card->dai_link = odroid_card_dais;
+ card->num_links = ARRAY_SIZE(odroid_card_dais);
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ link = card->dai_link;
+ codec_link = &card->dai_link[1];
+
+ /*
+ * For backwards compatibility create the secondary CPU DAI link only
+ * if there are 2 CPU DAI entries in the cpu sound-dai property in DT.
+ * Also add required DAPM routes not available in old DTS.
+ */
+ num_pcms = of_count_phandle_with_args(cpu, "sound-dai",
+ "#sound-dai-cells");
+ if (num_pcms == 1) {
+ card->dapm_routes = odroid_dapm_routes;
+ card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes);
+ card->num_links--;
+ }
+
+ for (i = 0; i < num_pcms; i++, link += 2) {
+ ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name, i);
+ if (ret < 0)
+ break;
+ }
+ if (ret == 0) {
+ cpu_dai = of_parse_phandle(cpu, "sound-dai", 0);
+ if (!cpu_dai)
+ ret = -EINVAL;
+ }
+
+ of_node_put(cpu);
+ if (ret < 0)
+ goto err_put_node;
+
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link);
+ if (ret < 0)
+ goto err_put_cpu_dai;
+
+ /* Set capture capability only for boards with the MAX98090 CODEC */
+ if (codec_link->num_codecs > 1) {
+ card->dai_link[0].playback_only = 0;
+ card->dai_link[1].playback_only = 0;
+ }
+
+ priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1");
+ if (IS_ERR(priv->sclk_i2s)) {
+ ret = PTR_ERR(priv->sclk_i2s);
+ goto err_put_cpu_dai;
+ }
+
+ priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis");
+ if (IS_ERR(priv->clk_i2s_bus)) {
+ ret = PTR_ERR(priv->clk_i2s_bus);
+ goto err_put_sclk;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "snd_soc_register_card() failed\n");
+ goto err_put_clk_i2s;
+ }
+
+ of_node_put(cpu_dai);
+ of_node_put(codec);
+ return 0;
+
+err_put_clk_i2s:
+ clk_put(priv->clk_i2s_bus);
+err_put_sclk:
+ clk_put(priv->sclk_i2s);
+err_put_cpu_dai:
+ of_node_put(cpu_dai);
+ snd_soc_of_put_dai_link_codecs(codec_link);
+err_put_node:
+ of_node_put(codec);
+ return ret;
+}
+
+static void odroid_audio_remove(struct platform_device *pdev)
+{
+ struct odroid_priv *priv = platform_get_drvdata(pdev);
+
+ snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
+ clk_put(priv->sclk_i2s);
+ clk_put(priv->clk_i2s_bus);
+}
+
+static const struct of_device_id odroid_audio_of_match[] = {
+ { .compatible = "hardkernel,odroid-xu3-audio" },
+ { .compatible = "hardkernel,odroid-xu4-audio" },
+ { .compatible = "samsung,odroid-xu3-audio" },
+ { .compatible = "samsung,odroid-xu4-audio" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
+
+static struct platform_driver odroid_audio_driver = {
+ .driver = {
+ .name = "odroid-audio",
+ .of_match_table = odroid_audio_of_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = odroid_audio_probe,
+ .remove = odroid_audio_remove,
+};
+module_platform_driver(odroid_audio_driver);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c
deleted file mode 100644
index 04217279fe25..000000000000
--- a/sound/soc/samsung/odroidx2_max98090.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2014 Samsung Electronics 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; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/of.h>
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#include "i2s.h"
-
-struct odroidx2_drv_data {
- const struct snd_soc_dapm_widget *dapm_widgets;
- unsigned int num_dapm_widgets;
-};
-
-/* The I2S CDCLK output clock frequency for the MAX98090 codec */
-#define MAX98090_MCLK 19200000
-
-static struct snd_soc_dai_link odroidx2_dai[];
-
-static int odroidx2_late_probe(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai *codec_dai;
- struct snd_soc_dai *cpu_dai;
- int ret;
-
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
- cpu_dai = rtd->cpu_dai;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
- SND_SOC_CLOCK_IN);
-
- if (ret < 0 || of_find_property(odroidx2_dai[0].codec_of_node,
- "clocks", NULL))
- return ret;
-
- /* Set the cpu DAI configuration in order to use CDCLK */
- return snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
- 0, SND_SOC_CLOCK_OUT);
-}
-
-static const struct snd_soc_dapm_widget odroidx2_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_MIC("DMIC", NULL),
-};
-
-static const struct snd_soc_dapm_widget odroidu3_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speakers", NULL),
-};
-
-static struct snd_soc_dai_link odroidx2_dai[] = {
- {
- .name = "MAX98090",
- .stream_name = "MAX98090 PCM",
- .codec_dai_name = "HiFi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- }
-};
-
-static struct snd_soc_card odroidx2 = {
- .owner = THIS_MODULE,
- .dai_link = odroidx2_dai,
- .num_links = ARRAY_SIZE(odroidx2_dai),
- .fully_routed = true,
- .late_probe = odroidx2_late_probe,
-};
-
-static const struct odroidx2_drv_data odroidx2_drvdata = {
- .dapm_widgets = odroidx2_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(odroidx2_dapm_widgets),
-};
-
-static const struct odroidx2_drv_data odroidu3_drvdata = {
- .dapm_widgets = odroidu3_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(odroidu3_dapm_widgets),
-};
-
-static const struct of_device_id odroidx2_audio_of_match[] = {
- {
- .compatible = "samsung,odroidx2-audio",
- .data = &odroidx2_drvdata,
- }, {
- .compatible = "samsung,odroidu3-audio",
- .data = &odroidu3_drvdata,
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, odroidx2_audio_of_match);
-
-static int odroidx2_audio_probe(struct platform_device *pdev)
-{
- struct device_node *snd_node = pdev->dev.of_node;
- struct snd_soc_card *card = &odroidx2;
- struct device_node *i2s_node, *codec_node;
- struct odroidx2_drv_data *dd;
- const struct of_device_id *of_id;
- int ret;
-
- of_id = of_match_node(odroidx2_audio_of_match, snd_node);
- dd = (struct odroidx2_drv_data *)of_id->data;
-
- card->num_dapm_widgets = dd->num_dapm_widgets;
- card->dapm_widgets = dd->dapm_widgets;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_of_parse_card_name(card, "samsung,model");
- if (ret < 0)
- return ret;
-
- ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
- if (ret < 0)
- return ret;
-
- codec_node = of_parse_phandle(snd_node, "samsung,audio-codec", 0);
- if (!codec_node) {
- dev_err(&pdev->dev,
- "Failed parsing samsung,i2s-codec property\n");
- return -EINVAL;
- }
-
- i2s_node = of_parse_phandle(snd_node, "samsung,i2s-controller", 0);
- if (!i2s_node) {
- dev_err(&pdev->dev,
- "Failed parsing samsung,i2s-controller property\n");
- ret = -EINVAL;
- goto err_put_codec_n;
- }
-
- odroidx2_dai[0].codec_of_node = codec_node;
- odroidx2_dai[0].cpu_of_node = i2s_node;
- odroidx2_dai[0].platform_of_node = i2s_node;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- goto err_put_i2s_n;
- }
- return 0;
-
-err_put_i2s_n:
- of_node_put(i2s_node);
-err_put_codec_n:
- of_node_put(codec_node);
- return ret;
-}
-
-static int odroidx2_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- of_node_put(odroidx2_dai[0].cpu_of_node);
- of_node_put(odroidx2_dai[0].codec_of_node);
-
- return 0;
-}
-
-static struct platform_driver odroidx2_audio_driver = {
- .driver = {
- .name = "odroidx2-audio",
- .of_match_table = odroidx2_audio_of_match,
- .pm = &snd_soc_pm_ops,
- },
- .probe = odroidx2_audio_probe,
- .remove = odroidx2_audio_remove,
-};
-module_platform_driver(odroidx2_audio_driver);
-
-MODULE_AUTHOR("Chen Zhen <zhen1.chen@samsung.com>");
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("ALSA SoC Odroid X2/U3 Audio Support");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 498f563a4c9c..a03ba9374c2e 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -1,15 +1,10 @@
-/* sound/soc/samsung/pcm.c
- *
- * ALSA SoC Audio Layer - S3C PCM-Controller driver
- *
- * Copyright (c) 2009 Samsung Electronics Co. Ltd
- * Author: Jaswinder Singh <jassisinghbrar@gmail.com>
- * based upon I2S drivers by Ben Dooks.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Audio Layer - S3C PCM-Controller driver
+//
+// Copyright (c) 2009 Samsung Electronics Co. Ltd
+// Author: Jaswinder Singh <jassisinghbrar@gmail.com>
+// based upon I2S drivers by Ben Dooks.
#include <linux/clk.h>
#include <linux/io.h>
@@ -109,8 +104,13 @@
/**
* struct s3c_pcm_info - S3C PCM Controller information
+ * @lock: Spin lock
* @dev: The parent device passed to use from the probe.
* @regs: The pointer to the device register block.
+ * @sclk_per_fs: number of sclk per frame sync
+ * @idleclk: Whether to keep PCMSCLK enabled even when idle (no active xfer)
+ * @pclk: the PCLK_PCM (pcm) clock pointer
+ * @cclk: the SCLK_AUDIO (audio-bus) clock pointer
* @dma_playback: DMA information for playback channel.
* @dma_capture: DMA information for capture channel.
*/
@@ -127,25 +127,25 @@ struct s3c_pcm_info {
struct clk *pclk;
struct clk *cclk;
- struct s3c_dma_params *dma_playback;
- struct s3c_dma_params *dma_capture;
+ struct snd_dmaengine_dai_dma_data *dma_playback;
+ struct snd_dmaengine_dai_dma_data *dma_capture;
};
-static struct s3c_dma_params s3c_pcm_stereo_out[] = {
+static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_out[] = {
[0] = {
- .dma_size = 4,
+ .addr_width = 4,
},
[1] = {
- .dma_size = 4,
+ .addr_width = 4,
},
};
-static struct s3c_dma_params s3c_pcm_stereo_in[] = {
+static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_in[] = {
[0] = {
- .dma_size = 4,
+ .addr_width = 4,
},
[1] = {
- .dma_size = 4,
+ .addr_width = 4,
},
};
@@ -216,8 +216,8 @@ static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
unsigned long flags;
dev_dbg(pcm->dev, "Entered %s\n", __func__);
@@ -260,8 +260,8 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *socdai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
void __iomem *regs = pcm->regs;
struct clk *clk;
int sclk_div, sync_div;
@@ -340,8 +340,8 @@ static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
goto exit;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
/* Nothing to do, Master by default */
break;
default:
@@ -432,14 +432,6 @@ static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
return 0;
}
-static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
- .set_sysclk = s3c_pcm_set_sysclk,
- .set_clkdiv = s3c_pcm_set_clkdiv,
- .trigger = s3c_pcm_trigger,
- .hw_params = s3c_pcm_hw_params,
- .set_fmt = s3c_pcm_set_fmt,
-};
-
static int s3c_pcm_dai_probe(struct snd_soc_dai *dai)
{
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai);
@@ -449,11 +441,19 @@ static int s3c_pcm_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
+ .probe = s3c_pcm_dai_probe,
+ .set_sysclk = s3c_pcm_set_sysclk,
+ .set_clkdiv = s3c_pcm_set_clkdiv,
+ .trigger = s3c_pcm_trigger,
+ .hw_params = s3c_pcm_hw_params,
+ .set_fmt = s3c_pcm_set_fmt,
+};
+
#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
#define S3C_PCM_DAI_DECLARE \
- .symmetric_rates = 1, \
- .probe = s3c_pcm_dai_probe, \
+ .symmetric_rate = 1, \
.ops = &s3c_pcm_dai_ops, \
.playback = { \
.channels_min = 2, \
@@ -480,7 +480,8 @@ static struct snd_soc_dai_driver s3c_pcm_dai[] = {
};
static const struct snd_soc_component_driver s3c_pcm_component = {
- .name = "s3c-pcm",
+ .name = "s3c-pcm",
+ .legacy_dai_naming = 1,
};
static int s3c_pcm_dev_probe(struct platform_device *pdev)
@@ -499,13 +500,6 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
pcm_pdata = pdev->dev.platform_data;
- /* Check for availability of necessary resource */
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem_res) {
- dev_err(&pdev->dev, "Unable to get register resource\n");
- return -ENXIO;
- }
-
if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to configure gpio\n");
return -EINVAL;
@@ -519,99 +513,79 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
/* Default is 128fs */
pcm->sclk_per_fs = 128;
+ pcm->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
+ if (IS_ERR(pcm->regs))
+ return PTR_ERR(pcm->regs);
+
pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus");
if (IS_ERR(pcm->cclk)) {
- dev_err(&pdev->dev, "failed to get audio-bus\n");
- ret = PTR_ERR(pcm->cclk);
- goto err1;
+ dev_err(&pdev->dev, "failed to get audio-bus clock\n");
+ return PTR_ERR(pcm->cclk);
}
- clk_prepare_enable(pcm->cclk);
+ ret = clk_prepare_enable(pcm->cclk);
+ if (ret)
+ return ret;
/* record our pcm structure for later use in the callbacks */
dev_set_drvdata(&pdev->dev, pcm);
- if (!request_mem_region(mem_res->start,
- resource_size(mem_res), "samsung-pcm")) {
- dev_err(&pdev->dev, "Unable to request register region\n");
- ret = -EBUSY;
- goto err2;
- }
-
- pcm->regs = ioremap(mem_res->start, 0x100);
- if (pcm->regs == NULL) {
- dev_err(&pdev->dev, "cannot ioremap registers\n");
- ret = -ENXIO;
- goto err3;
- }
-
pcm->pclk = devm_clk_get(&pdev->dev, "pcm");
if (IS_ERR(pcm->pclk)) {
- dev_err(&pdev->dev, "failed to get pcm_clock\n");
- ret = -ENOENT;
- goto err4;
+ dev_err(&pdev->dev, "failed to get pcm clock\n");
+ ret = PTR_ERR(pcm->pclk);
+ goto err_dis_cclk;
}
- clk_prepare_enable(pcm->pclk);
+ ret = clk_prepare_enable(pcm->pclk);
+ if (ret)
+ goto err_dis_cclk;
- s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start
- + S3C_PCM_RXFIFO;
- s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
- + S3C_PCM_TXFIFO;
+ s3c_pcm_stereo_in[pdev->id].addr = mem_res->start + S3C_PCM_RXFIFO;
+ s3c_pcm_stereo_out[pdev->id].addr = mem_res->start + S3C_PCM_TXFIFO;
filter = NULL;
if (pcm_pdata) {
- s3c_pcm_stereo_in[pdev->id].slave = pcm_pdata->dma_capture;
- s3c_pcm_stereo_out[pdev->id].slave = pcm_pdata->dma_playback;
+ s3c_pcm_stereo_in[pdev->id].filter_data = pcm_pdata->dma_capture;
+ s3c_pcm_stereo_out[pdev->id].filter_data = pcm_pdata->dma_playback;
filter = pcm_pdata->dma_filter;
}
pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
+ NULL, NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
+ goto err_dis_pclk;
+ }
+
pm_runtime_enable(&pdev->dev);
ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
&s3c_pcm_dai[pdev->id], 1);
if (ret != 0) {
dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
- goto err5;
- }
-
- ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
- if (ret) {
- dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
- goto err5;
+ goto err_dis_pm;
}
return 0;
-err5:
+err_dis_pm:
+ pm_runtime_disable(&pdev->dev);
+err_dis_pclk:
clk_disable_unprepare(pcm->pclk);
-err4:
- iounmap(pcm->regs);
-err3:
- release_mem_region(mem_res->start, resource_size(mem_res));
-err2:
+err_dis_cclk:
clk_disable_unprepare(pcm->cclk);
-err1:
return ret;
}
-static int s3c_pcm_dev_remove(struct platform_device *pdev)
+static void s3c_pcm_dev_remove(struct platform_device *pdev)
{
struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
- struct resource *mem_res;
pm_runtime_disable(&pdev->dev);
-
- iounmap(pcm->regs);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem_res->start, resource_size(mem_res));
-
clk_disable_unprepare(pcm->cclk);
clk_disable_unprepare(pcm->pclk);
-
- return 0;
}
static struct platform_driver s3c_pcm_driver = {
diff --git a/sound/soc/samsung/pcm.h b/sound/soc/samsung/pcm.h
index 726baf814613..208d8da27de1 100644
--- a/sound/soc/samsung/pcm.h
+++ b/sound/soc/samsung/pcm.h
@@ -1,10 +1,4 @@
-/* sound/soc/samsung/pcm.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.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __S3C_PCM_H
#define __S3C_PCM_H __FILE__
diff --git a/sound/soc/samsung/regs-ac97.h b/sound/soc/samsung/regs-ac97.h
deleted file mode 100644
index a71be45bbffc..000000000000
--- a/sound/soc/samsung/regs-ac97.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2006 Simtec Electronics <linux@simtec.co.uk>
- * http://www.simtec.co.uk/products/SWLINUX/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * S3C2440 AC97 Controller
-*/
-
-#ifndef __SAMSUNG_REGS_AC97_H__
-#define __SAMSUNG_REGS_AC97_H__
-
-#define S3C_AC97_GLBCTRL (0x00)
-
-#define S3C_AC97_GLBCTRL_CODECREADYIE (1<<22)
-#define S3C_AC97_GLBCTRL_PCMOUTURIE (1<<21)
-#define S3C_AC97_GLBCTRL_PCMINORIE (1<<20)
-#define S3C_AC97_GLBCTRL_MICINORIE (1<<19)
-#define S3C_AC97_GLBCTRL_PCMOUTTIE (1<<18)
-#define S3C_AC97_GLBCTRL_PCMINTIE (1<<17)
-#define S3C_AC97_GLBCTRL_MICINTIE (1<<16)
-#define S3C_AC97_GLBCTRL_PCMOUTTM_OFF (0<<12)
-#define S3C_AC97_GLBCTRL_PCMOUTTM_PIO (1<<12)
-#define S3C_AC97_GLBCTRL_PCMOUTTM_DMA (2<<12)
-#define S3C_AC97_GLBCTRL_PCMOUTTM_MASK (3<<12)
-#define S3C_AC97_GLBCTRL_PCMINTM_OFF (0<<10)
-#define S3C_AC97_GLBCTRL_PCMINTM_PIO (1<<10)
-#define S3C_AC97_GLBCTRL_PCMINTM_DMA (2<<10)
-#define S3C_AC97_GLBCTRL_PCMINTM_MASK (3<<10)
-#define S3C_AC97_GLBCTRL_MICINTM_OFF (0<<8)
-#define S3C_AC97_GLBCTRL_MICINTM_PIO (1<<8)
-#define S3C_AC97_GLBCTRL_MICINTM_DMA (2<<8)
-#define S3C_AC97_GLBCTRL_MICINTM_MASK (3<<8)
-#define S3C_AC97_GLBCTRL_TRANSFERDATAENABLE (1<<3)
-#define S3C_AC97_GLBCTRL_ACLINKON (1<<2)
-#define S3C_AC97_GLBCTRL_WARMRESET (1<<1)
-#define S3C_AC97_GLBCTRL_COLDRESET (1<<0)
-
-#define S3C_AC97_GLBSTAT (0x04)
-
-#define S3C_AC97_GLBSTAT_CODECREADY (1<<22)
-#define S3C_AC97_GLBSTAT_PCMOUTUR (1<<21)
-#define S3C_AC97_GLBSTAT_PCMINORI (1<<20)
-#define S3C_AC97_GLBSTAT_MICINORI (1<<19)
-#define S3C_AC97_GLBSTAT_PCMOUTTI (1<<18)
-#define S3C_AC97_GLBSTAT_PCMINTI (1<<17)
-#define S3C_AC97_GLBSTAT_MICINTI (1<<16)
-#define S3C_AC97_GLBSTAT_MAINSTATE_IDLE (0<<0)
-#define S3C_AC97_GLBSTAT_MAINSTATE_INIT (1<<0)
-#define S3C_AC97_GLBSTAT_MAINSTATE_READY (2<<0)
-#define S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE (3<<0)
-#define S3C_AC97_GLBSTAT_MAINSTATE_LP (4<<0)
-#define S3C_AC97_GLBSTAT_MAINSTATE_WARM (5<<0)
-
-#define S3C_AC97_CODEC_CMD (0x08)
-
-#define S3C_AC97_CODEC_CMD_READ (1<<23)
-
-#define S3C_AC97_STAT (0x0c)
-#define S3C_AC97_PCM_ADDR (0x10)
-#define S3C_AC97_PCM_DATA (0x18)
-#define S3C_AC97_MIC_DATA (0x1C)
-
-#endif /* __SAMSUNG_REGS_AC97_H__ */
diff --git a/sound/soc/samsung/regs-i2s-v2.h b/sound/soc/samsung/regs-i2s-v2.h
deleted file mode 100644
index 5e5e5680580b..000000000000
--- a/sound/soc/samsung/regs-i2s-v2.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* linux/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h
- *
- * Copyright 2007 Simtec Electronics <linux@simtec.co.uk>
- * http://armlinux.simtec.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.
- *
- * S3C2412 IIS register definition
-*/
-
-#ifndef __ASM_ARCH_REGS_S3C2412_IIS_H
-#define __ASM_ARCH_REGS_S3C2412_IIS_H
-
-#define S3C2412_IISCON (0x00)
-#define S3C2412_IISMOD (0x04)
-#define S3C2412_IISFIC (0x08)
-#define S3C2412_IISPSR (0x0C)
-#define S3C2412_IISTXD (0x10)
-#define S3C2412_IISRXD (0x14)
-
-#define S5PC1XX_IISFICS 0x18
-#define S5PC1XX_IISTXDS 0x1C
-
-#define S5PC1XX_IISCON_SW_RST (1 << 31)
-#define S5PC1XX_IISCON_FRXOFSTATUS (1 << 26)
-#define S5PC1XX_IISCON_FRXORINTEN (1 << 25)
-#define S5PC1XX_IISCON_FTXSURSTAT (1 << 24)
-#define S5PC1XX_IISCON_FTXSURINTEN (1 << 23)
-#define S5PC1XX_IISCON_TXSDMAPAUSE (1 << 20)
-#define S5PC1XX_IISCON_TXSDMACTIVE (1 << 18)
-
-#define S3C64XX_IISCON_FTXURSTATUS (1 << 17)
-#define S3C64XX_IISCON_FTXURINTEN (1 << 16)
-#define S3C64XX_IISCON_TXFIFO2_EMPTY (1 << 15)
-#define S3C64XX_IISCON_TXFIFO1_EMPTY (1 << 14)
-#define S3C64XX_IISCON_TXFIFO2_FULL (1 << 13)
-#define S3C64XX_IISCON_TXFIFO1_FULL (1 << 12)
-
-#define S3C2412_IISCON_LRINDEX (1 << 11)
-#define S3C2412_IISCON_TXFIFO_EMPTY (1 << 10)
-#define S3C2412_IISCON_RXFIFO_EMPTY (1 << 9)
-#define S3C2412_IISCON_TXFIFO_FULL (1 << 8)
-#define S3C2412_IISCON_RXFIFO_FULL (1 << 7)
-#define S3C2412_IISCON_TXDMA_PAUSE (1 << 6)
-#define S3C2412_IISCON_RXDMA_PAUSE (1 << 5)
-#define S3C2412_IISCON_TXCH_PAUSE (1 << 4)
-#define S3C2412_IISCON_RXCH_PAUSE (1 << 3)
-#define S3C2412_IISCON_TXDMA_ACTIVE (1 << 2)
-#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
-#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
-
-#define S5PC1XX_IISMOD_OPCLK_CDCLK_OUT (0 << 30)
-#define S5PC1XX_IISMOD_OPCLK_CDCLK_IN (1 << 30)
-#define S5PC1XX_IISMOD_OPCLK_BCLK_OUT (2 << 30)
-#define S5PC1XX_IISMOD_OPCLK_PCLK (3 << 30)
-#define S5PC1XX_IISMOD_OPCLK_MASK (3 << 30)
-#define S5PC1XX_IISMOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
-#define S5PC1XX_IISMOD_BLCS_MASK 0x3
-#define S5PC1XX_IISMOD_BLCS_SHIFT 26
-#define S5PC1XX_IISMOD_BLCP_MASK 0x3
-#define S5PC1XX_IISMOD_BLCP_SHIFT 24
-
-#define S3C64XX_IISMOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
-#define S3C64XX_IISMOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
-#define S3C64XX_IISMOD_C1DD_HHALF (1 << 19)
-#define S3C64XX_IISMOD_C1DD_LHALF (1 << 18)
-#define S3C64XX_IISMOD_DC2_EN (1 << 17)
-#define S3C64XX_IISMOD_DC1_EN (1 << 16)
-#define S3C64XX_IISMOD_BLC_16BIT (0 << 13)
-#define S3C64XX_IISMOD_BLC_8BIT (1 << 13)
-#define S3C64XX_IISMOD_BLC_24BIT (2 << 13)
-#define S3C64XX_IISMOD_BLC_MASK (3 << 13)
-
-#define S3C2412_IISMOD_IMS_SYSMUX (1 << 10)
-#define S3C2412_IISMOD_SLAVE (1 << 11)
-#define S3C2412_IISMOD_MODE_TXONLY (0 << 8)
-#define S3C2412_IISMOD_MODE_RXONLY (1 << 8)
-#define S3C2412_IISMOD_MODE_TXRX (2 << 8)
-#define S3C2412_IISMOD_MODE_MASK (3 << 8)
-#define S3C2412_IISMOD_LR_LLOW (0 << 7)
-#define S3C2412_IISMOD_LR_RLOW (1 << 7)
-#define S3C2412_IISMOD_SDF_IIS (0 << 5)
-#define S3C2412_IISMOD_SDF_MSB (1 << 5)
-#define S3C2412_IISMOD_SDF_LSB (2 << 5)
-#define S3C2412_IISMOD_SDF_MASK (3 << 5)
-#define S3C2412_IISMOD_RCLK_256FS (0 << 3)
-#define S3C2412_IISMOD_RCLK_512FS (1 << 3)
-#define S3C2412_IISMOD_RCLK_384FS (2 << 3)
-#define S3C2412_IISMOD_RCLK_768FS (3 << 3)
-#define S3C2412_IISMOD_RCLK_MASK (3 << 3)
-#define S3C2412_IISMOD_BCLK_32FS (0 << 1)
-#define S3C2412_IISMOD_BCLK_48FS (1 << 1)
-#define S3C2412_IISMOD_BCLK_16FS (2 << 1)
-#define S3C2412_IISMOD_BCLK_24FS (3 << 1)
-#define S3C2412_IISMOD_BCLK_MASK (3 << 1)
-#define S3C2412_IISMOD_8BIT (1 << 0)
-
-#define S3C64XX_IISMOD_CDCLKCON (1 << 12)
-
-#define S3C2412_IISPSR_PSREN (1 << 15)
-
-#define S3C64XX_IISFIC_TX2COUNT(x) (((x) >> 24) & 0xf)
-#define S3C64XX_IISFIC_TX1COUNT(x) (((x) >> 16) & 0xf)
-
-#define S3C2412_IISFIC_TXFLUSH (1 << 15)
-#define S3C2412_IISFIC_RXFLUSH (1 << 7)
-#define S3C2412_IISFIC_TXCOUNT(x) (((x) >> 8) & 0xf)
-#define S3C2412_IISFIC_RXCOUNT(x) (((x) >> 0) & 0xf)
-
-#define S5PC1XX_IISFICS_TXFLUSH (1 << 15)
-#define S5PC1XX_IISFICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
-
-#endif /* __ASM_ARCH_REGS_S3C2412_IIS_H */
diff --git a/sound/soc/samsung/regs-iis.h b/sound/soc/samsung/regs-iis.h
deleted file mode 100644
index dc6cbbe9c4f0..000000000000
--- a/sound/soc/samsung/regs-iis.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
- * http://www.simtec.co.uk/products/SWLINUX/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * S3C2410 IIS register definition
-*/
-
-#ifndef __SAMSUNG_REGS_IIS_H__
-#define __SAMSUNG_REGS_IIS_H__
-
-#define S3C2410_IISCON (0x00)
-
-#define S3C2410_IISCON_LRINDEX (1 << 8)
-#define S3C2410_IISCON_TXFIFORDY (1 << 7)
-#define S3C2410_IISCON_RXFIFORDY (1 << 6)
-#define S3C2410_IISCON_TXDMAEN (1 << 5)
-#define S3C2410_IISCON_RXDMAEN (1 << 4)
-#define S3C2410_IISCON_TXIDLE (1 << 3)
-#define S3C2410_IISCON_RXIDLE (1 << 2)
-#define S3C2410_IISCON_PSCEN (1 << 1)
-#define S3C2410_IISCON_IISEN (1 << 0)
-
-#define S3C2410_IISMOD (0x04)
-
-#define S3C2440_IISMOD_MPLL (1 << 9)
-#define S3C2410_IISMOD_SLAVE (1 << 8)
-#define S3C2410_IISMOD_NOXFER (0 << 6)
-#define S3C2410_IISMOD_RXMODE (1 << 6)
-#define S3C2410_IISMOD_TXMODE (2 << 6)
-#define S3C2410_IISMOD_TXRXMODE (3 << 6)
-#define S3C2410_IISMOD_LR_LLOW (0 << 5)
-#define S3C2410_IISMOD_LR_RLOW (1 << 5)
-#define S3C2410_IISMOD_IIS (0 << 4)
-#define S3C2410_IISMOD_MSB (1 << 4)
-#define S3C2410_IISMOD_8BIT (0 << 3)
-#define S3C2410_IISMOD_16BIT (1 << 3)
-#define S3C2410_IISMOD_BITMASK (1 << 3)
-#define S3C2410_IISMOD_256FS (0 << 2)
-#define S3C2410_IISMOD_384FS (1 << 2)
-#define S3C2410_IISMOD_16FS (0 << 0)
-#define S3C2410_IISMOD_32FS (1 << 0)
-#define S3C2410_IISMOD_48FS (2 << 0)
-#define S3C2410_IISMOD_FS_MASK (3 << 0)
-
-#define S3C2410_IISPSR (0x08)
-
-#define S3C2410_IISPSR_INTMASK (31 << 5)
-#define S3C2410_IISPSR_INTSHIFT (5)
-#define S3C2410_IISPSR_EXTMASK (31 << 0)
-#define S3C2410_IISPSR_EXTSHFIT (0)
-
-#define S3C2410_IISFCON (0x0c)
-
-#define S3C2410_IISFCON_TXDMA (1 << 15)
-#define S3C2410_IISFCON_RXDMA (1 << 14)
-#define S3C2410_IISFCON_TXENABLE (1 << 13)
-#define S3C2410_IISFCON_RXENABLE (1 << 12)
-#define S3C2410_IISFCON_TXMASK (0x3f << 6)
-#define S3C2410_IISFCON_TXSHIFT (6)
-#define S3C2410_IISFCON_RXMASK (0x3f)
-#define S3C2410_IISFCON_RXSHIFT (0)
-
-#define S3C2410_IISFIFO (0x10)
-
-#endif /* __SAMSUNG_REGS_IIS_H__ */
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
deleted file mode 100644
index fa096abe9e75..000000000000
--- a/sound/soc/samsung/rx1950_uda1380.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * rx1950.c -- ALSA Soc Audio Layer
- *
- * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
- *
- * Based on smdk2440.c and magician.c
- *
- * Authors: Graeme Gregory graeme.gregory@wolfsonmicro.com
- * Philipp Zabel <philipp.zabel@gmail.com>
- * Denis Grigoriev <dgreenday@gmail.com>
- * Vasily Khoruzhick <anarsoul@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.
- *
- */
-
-#include <linux/types.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <mach/gpio-samsung.h>
-#include "regs-iis.h"
-#include <asm/mach-types.h>
-
-#include "s3c24xx-i2s.h"
-
-static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
-static int rx1950_uda1380_card_remove(struct snd_soc_card *card);
-static int rx1950_startup(struct snd_pcm_substream *substream);
-static int rx1950_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params);
-static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event);
-
-static const unsigned int rates[] = {
- 16000,
- 44100,
- 48000,
-};
-
-static const struct snd_pcm_hw_constraint_list hw_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
-};
-
-static struct snd_soc_jack hp_jack;
-
-static struct snd_soc_jack_pin hp_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "Speaker",
- .mask = SND_JACK_HEADPHONE,
- .invert = 1,
- },
-};
-
-static struct snd_soc_jack_gpio hp_jack_gpios[] = {
- [0] = {
- .gpio = S3C2410_GPG(12),
- .name = "hp-gpio",
- .report = SND_JACK_HEADPHONE,
- .invert = 1,
- .debounce_time = 200,
- },
-};
-
-static struct snd_soc_ops rx1950_ops = {
- .startup = rx1950_startup,
- .hw_params = rx1950_hw_params,
-};
-
-/* s3c24xx digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link rx1950_uda1380_dai[] = {
- {
- .name = "uda1380",
- .stream_name = "UDA1380 Duplex",
- .cpu_dai_name = "s3c24xx-iis",
- .codec_dai_name = "uda1380-hifi",
- .init = rx1950_uda1380_init,
- .platform_name = "s3c24xx-iis",
- .codec_name = "uda1380-codec.0-001a",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &rx1950_ops,
- },
-};
-
-/* rx1950 machine dapm widgets */
-static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", rx1950_spk_power),
-};
-
-/* rx1950 machine audio_map */
-static const struct snd_soc_dapm_route audio_map[] = {
- /* headphone connected to VOUTLHP, VOUTRHP */
- {"Headphone Jack", NULL, "VOUTLHP"},
- {"Headphone Jack", NULL, "VOUTRHP"},
-
- /* ext speaker connected to VOUTL, VOUTR */
- {"Speaker", NULL, "VOUTL"},
- {"Speaker", NULL, "VOUTR"},
-
- /* mic is connected to VINM */
- {"VINM", NULL, "Mic Jack"},
-};
-
-static struct snd_soc_card rx1950_asoc = {
- .name = "rx1950",
- .owner = THIS_MODULE,
- .remove = rx1950_uda1380_card_remove,
- .dai_link = rx1950_uda1380_dai,
- .num_links = ARRAY_SIZE(rx1950_uda1380_dai),
-
- .dapm_widgets = uda1380_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *s3c24xx_snd_device;
-
-static int rx1950_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_rates);
-}
-
-static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- gpio_set_value(S3C2410_GPA(1), 1);
- else
- gpio_set_value(S3C2410_GPA(1), 0);
-
- return 0;
-}
-
-static int rx1950_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int div;
- int ret;
- unsigned int rate = params_rate(params);
- int clk_source, fs_mode;
-
- switch (rate) {
- case 16000:
- case 48000:
- clk_source = S3C24XX_CLKSRC_PCLK;
- fs_mode = S3C2410_IISMOD_256FS;
- div = s3c24xx_i2s_get_clockrate() / (256 * rate);
- if (s3c24xx_i2s_get_clockrate() % (256 * rate) > (128 * rate))
- div++;
- break;
- case 44100:
- case 88200:
- clk_source = S3C24XX_CLKSRC_MPLL;
- fs_mode = S3C2410_IISMOD_384FS;
- div = 1;
- break;
- default:
- printk(KERN_ERR "%s: rate %d is not supported\n",
- __func__, rate);
- return -EINVAL;
- }
-
- /* select clock source */
- ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source, rate,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- /* set MCLK division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
- fs_mode);
- if (ret < 0)
- return ret;
-
- /* set BCLK division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
- S3C2410_IISMOD_32FS);
- if (ret < 0)
- return ret;
-
- /* set prescaler division for sample rate */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
- S3C24XX_PRESCALE(div, div));
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
-{
- snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
- &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
-
- snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
- hp_jack_gpios);
-
- return 0;
-}
-
-static int rx1950_uda1380_card_remove(struct snd_soc_card *card)
-{
- snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
- hp_jack_gpios);
-
- return 0;
-}
-
-static int __init rx1950_init(void)
-{
- int ret;
-
- if (!machine_is_rx1950())
- return -ENODEV;
-
- /* configure some gpios */
- ret = gpio_request(S3C2410_GPA(1), "speaker-power");
- if (ret)
- goto err_gpio;
-
- ret = gpio_direction_output(S3C2410_GPA(1), 0);
- if (ret)
- goto err_gpio_conf;
-
- s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
- if (!s3c24xx_snd_device) {
- ret = -ENOMEM;
- goto err_plat_alloc;
- }
-
- platform_set_drvdata(s3c24xx_snd_device, &rx1950_asoc);
- ret = platform_device_add(s3c24xx_snd_device);
-
- if (ret) {
- platform_device_put(s3c24xx_snd_device);
- goto err_plat_add;
- }
-
- return 0;
-
-err_plat_add:
-err_plat_alloc:
-err_gpio_conf:
- gpio_free(S3C2410_GPA(1));
-
-err_gpio:
- return ret;
-}
-
-static void __exit rx1950_exit(void)
-{
- platform_device_unregister(s3c24xx_snd_device);
- gpio_free(S3C2410_GPA(1));
-}
-
-module_init(rx1950_init);
-module_exit(rx1950_exit);
-
-/* Module information */
-MODULE_AUTHOR("Vasily Khoruzhick");
-MODULE_DESCRIPTION("ALSA SoC RX1950");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
deleted file mode 100644
index df65c5b494b1..000000000000
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ /dev/null
@@ -1,735 +0,0 @@
-/* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
- *
- * Copyright (c) 2006 Wolfson Microelectronics PLC.
- * Graeme Gregory graeme.gregory@wolfsonmicro.com
- * linux@wolfsonmicro.com
- *
- * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.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/delay.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "regs-i2s-v2.h"
-#include "s3c-i2s-v2.h"
-#include "dma.h"
-
-#undef S3C_IIS_V2_SUPPORTED
-
-#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
- || defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_CPU_S5PV210)
-#define S3C_IIS_V2_SUPPORTED
-#endif
-
-#ifndef S3C_IIS_V2_SUPPORTED
-#error Unsupported CPU model
-#endif
-
-#define S3C2412_I2S_DEBUG_CON 0
-
-static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
-{
- return snd_soc_dai_get_drvdata(cpu_dai);
-}
-
-#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
-
-#if S3C2412_I2S_DEBUG_CON
-static void dbg_showcon(const char *fn, u32 con)
-{
- printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
- bit_set(con, S3C2412_IISCON_LRINDEX),
- bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
- bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
- bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
- bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
-
- printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
- fn,
- bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
- bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
- bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
- bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
- printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
- bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
- bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
- bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
-}
-#else
-static inline void dbg_showcon(const char *fn, u32 con)
-{
-}
-#endif
-
-
-/* Turn on or off the transmission path. */
-static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
-{
- void __iomem *regs = i2s->regs;
- u32 fic, con, mod;
-
- pr_debug("%s(%d)\n", __func__, on);
-
- fic = readl(regs + S3C2412_IISFIC);
- con = readl(regs + S3C2412_IISCON);
- mod = readl(regs + S3C2412_IISMOD);
-
- pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-
- if (on) {
- con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
- con &= ~S3C2412_IISCON_TXDMA_PAUSE;
- con &= ~S3C2412_IISCON_TXCH_PAUSE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_TXONLY:
- case S3C2412_IISMOD_MODE_TXRX:
- /* do nothing, we are in the right mode */
- break;
-
- case S3C2412_IISMOD_MODE_RXONLY:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_TXRX;
- break;
-
- default:
- dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
- mod & S3C2412_IISMOD_MODE_MASK);
- break;
- }
-
- writel(con, regs + S3C2412_IISCON);
- writel(mod, regs + S3C2412_IISMOD);
- } else {
- /* Note, we do not have any indication that the FIFO problems
- * tha the S3C2410/2440 had apply here, so we should be able
- * to disable the DMA and TX without resetting the FIFOS.
- */
-
- con |= S3C2412_IISCON_TXDMA_PAUSE;
- con |= S3C2412_IISCON_TXCH_PAUSE;
- con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_TXRX:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_RXONLY;
- break;
-
- case S3C2412_IISMOD_MODE_TXONLY:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- con &= ~S3C2412_IISCON_IIS_ACTIVE;
- break;
-
- default:
- dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
- mod & S3C2412_IISMOD_MODE_MASK);
- break;
- }
-
- writel(mod, regs + S3C2412_IISMOD);
- writel(con, regs + S3C2412_IISCON);
- }
-
- fic = readl(regs + S3C2412_IISFIC);
- dbg_showcon(__func__, con);
- pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-}
-
-static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
-{
- void __iomem *regs = i2s->regs;
- u32 fic, con, mod;
-
- pr_debug("%s(%d)\n", __func__, on);
-
- fic = readl(regs + S3C2412_IISFIC);
- con = readl(regs + S3C2412_IISCON);
- mod = readl(regs + S3C2412_IISMOD);
-
- pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-
- if (on) {
- con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
- con &= ~S3C2412_IISCON_RXDMA_PAUSE;
- con &= ~S3C2412_IISCON_RXCH_PAUSE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_TXRX:
- case S3C2412_IISMOD_MODE_RXONLY:
- /* do nothing, we are in the right mode */
- break;
-
- case S3C2412_IISMOD_MODE_TXONLY:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_TXRX;
- break;
-
- default:
- dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
- mod & S3C2412_IISMOD_MODE_MASK);
- }
-
- writel(mod, regs + S3C2412_IISMOD);
- writel(con, regs + S3C2412_IISCON);
- } else {
- /* See txctrl notes on FIFOs. */
-
- con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
- con |= S3C2412_IISCON_RXDMA_PAUSE;
- con |= S3C2412_IISCON_RXCH_PAUSE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_RXONLY:
- con &= ~S3C2412_IISCON_IIS_ACTIVE;
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- break;
-
- case S3C2412_IISMOD_MODE_TXRX:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_TXONLY;
- break;
-
- default:
- dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
- mod & S3C2412_IISMOD_MODE_MASK);
- }
-
- writel(con, regs + S3C2412_IISCON);
- writel(mod, regs + S3C2412_IISMOD);
- }
-
- fic = readl(regs + S3C2412_IISFIC);
- pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-}
-
-#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
-
-/*
- * Wait for the LR signal to allow synchronisation to the L/R clock
- * from the codec. May only be needed for slave mode.
- */
-static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
-{
- u32 iiscon;
- unsigned long loops = msecs_to_loops(5);
-
- pr_debug("Entered %s\n", __func__);
-
- while (--loops) {
- iiscon = readl(i2s->regs + S3C2412_IISCON);
- if (iiscon & S3C2412_IISCON_LRINDEX)
- break;
-
- cpu_relax();
- }
-
- if (!loops) {
- printk(KERN_ERR "%s: timeout\n", __func__);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-/*
- * Set S3C2412 I2S DAI format
- */
-static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- iismod = readl(i2s->regs + S3C2412_IISMOD);
- pr_debug("hw_params r: IISMOD: %x \n", iismod);
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- i2s->master = 0;
- iismod |= S3C2412_IISMOD_SLAVE;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- i2s->master = 1;
- iismod &= ~S3C2412_IISMOD_SLAVE;
- break;
- default:
- pr_err("unknwon master/slave format\n");
- return -EINVAL;
- }
-
- iismod &= ~S3C2412_IISMOD_SDF_MASK;
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_RIGHT_J:
- iismod |= S3C2412_IISMOD_LR_RLOW;
- iismod |= S3C2412_IISMOD_SDF_MSB;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- iismod |= S3C2412_IISMOD_LR_RLOW;
- iismod |= S3C2412_IISMOD_SDF_LSB;
- break;
- case SND_SOC_DAIFMT_I2S:
- iismod &= ~S3C2412_IISMOD_LR_RLOW;
- iismod |= S3C2412_IISMOD_SDF_IIS;
- break;
- default:
- pr_err("Unknown data format\n");
- return -EINVAL;
- }
-
- writel(iismod, i2s->regs + S3C2412_IISMOD);
- pr_debug("hw_params w: IISMOD: %x \n", iismod);
- return 0;
-}
-
-static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct s3c_i2sv2_info *i2s = to_info(dai);
- struct s3c_dma_params *dma_data;
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dma_data = i2s->dma_playback;
- else
- dma_data = i2s->dma_capture;
-
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
-
- /* Working copies of register */
- iismod = readl(i2s->regs + S3C2412_IISMOD);
- pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
-
- iismod &= ~S3C64XX_IISMOD_BLC_MASK;
- /* Sample size */
- switch (params_width(params)) {
- case 8:
- iismod |= S3C64XX_IISMOD_BLC_8BIT;
- break;
- case 16:
- break;
- case 24:
- iismod |= S3C64XX_IISMOD_BLC_24BIT;
- break;
- }
-
- writel(iismod, i2s->regs + S3C2412_IISMOD);
- pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
-
- return 0;
-}
-
-static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
- u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
-
- pr_debug("Entered %s\n", __func__);
- pr_debug("%s r: IISMOD: %x\n", __func__, iismod);
-
- switch (clk_id) {
- case S3C_I2SV2_CLKSRC_PCLK:
- iismod &= ~S3C2412_IISMOD_IMS_SYSMUX;
- break;
-
- case S3C_I2SV2_CLKSRC_AUDIOBUS:
- iismod |= S3C2412_IISMOD_IMS_SYSMUX;
- break;
-
- case S3C_I2SV2_CLKSRC_CDCLK:
- /* Error if controller doesn't have the CDCLKCON bit */
- if (!(i2s->feature & S3C_FEATURE_CDCLKCON))
- return -EINVAL;
-
- switch (dir) {
- case SND_SOC_CLOCK_IN:
- iismod |= S3C64XX_IISMOD_CDCLKCON;
- break;
- case SND_SOC_CLOCK_OUT:
- iismod &= ~S3C64XX_IISMOD_CDCLKCON;
- break;
- default:
- return -EINVAL;
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- writel(iismod, i2s->regs + S3C2412_IISMOD);
- pr_debug("%s w: IISMOD: %x\n", __func__, iismod);
-
- return 0;
-}
-
-static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai);
- int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- unsigned long irqs;
- int ret = 0;
-
- pr_debug("Entered %s\n", __func__);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- /* On start, ensure that the FIFOs are cleared and reset. */
-
- writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
- i2s->regs + S3C2412_IISFIC);
-
- /* clear again, just in case */
- writel(0x0, i2s->regs + S3C2412_IISFIC);
-
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!i2s->master) {
- ret = s3c2412_snd_lrsync(i2s);
- if (ret)
- goto exit_err;
- }
-
- local_irq_save(irqs);
-
- if (capture)
- s3c2412_snd_rxctrl(i2s, 1);
- else
- s3c2412_snd_txctrl(i2s, 1);
-
- local_irq_restore(irqs);
-
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- local_irq_save(irqs);
-
- if (capture)
- s3c2412_snd_rxctrl(i2s, 0);
- else
- s3c2412_snd_txctrl(i2s, 0);
-
- local_irq_restore(irqs);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
-exit_err:
- return ret;
-}
-
-/*
- * Set S3C2412 Clock dividers
- */
-static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
- u32 reg;
-
- pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
-
- switch (div_id) {
- case S3C_I2SV2_DIV_BCLK:
- switch (div) {
- case 16:
- div = S3C2412_IISMOD_BCLK_16FS;
- break;
-
- case 32:
- div = S3C2412_IISMOD_BCLK_32FS;
- break;
-
- case 24:
- div = S3C2412_IISMOD_BCLK_24FS;
- break;
-
- case 48:
- div = S3C2412_IISMOD_BCLK_48FS;
- break;
-
- default:
- return -EINVAL;
- }
-
- reg = readl(i2s->regs + S3C2412_IISMOD);
- reg &= ~S3C2412_IISMOD_BCLK_MASK;
- writel(reg | div, i2s->regs + S3C2412_IISMOD);
-
- pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
- break;
-
- case S3C_I2SV2_DIV_RCLK:
- switch (div) {
- case 256:
- div = S3C2412_IISMOD_RCLK_256FS;
- break;
-
- case 384:
- div = S3C2412_IISMOD_RCLK_384FS;
- break;
-
- case 512:
- div = S3C2412_IISMOD_RCLK_512FS;
- break;
-
- case 768:
- div = S3C2412_IISMOD_RCLK_768FS;
- break;
-
- default:
- return -EINVAL;
- }
-
- reg = readl(i2s->regs + S3C2412_IISMOD);
- reg &= ~S3C2412_IISMOD_RCLK_MASK;
- writel(reg | div, i2s->regs + S3C2412_IISMOD);
- pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
- break;
-
- case S3C_I2SV2_DIV_PRESCALER:
- if (div >= 0) {
- writel((div << 8) | S3C2412_IISPSR_PSREN,
- i2s->regs + S3C2412_IISPSR);
- } else {
- writel(0x0, i2s->regs + S3C2412_IISPSR);
- }
- pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct s3c_i2sv2_info *i2s = to_info(dai);
- u32 reg = readl(i2s->regs + S3C2412_IISFIC);
- snd_pcm_sframes_t delay;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- delay = S3C2412_IISFIC_TXCOUNT(reg);
- else
- delay = S3C2412_IISFIC_RXCOUNT(reg);
-
- return delay;
-}
-
-struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai)
-{
- struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
- u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
-
- if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
- return i2s->iis_cclk;
- else
- return i2s->iis_pclk;
-}
-EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
-
-/* default table of all avaialable root fs divisors */
-static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
-
-int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
- unsigned int *fstab,
- unsigned int rate, struct clk *clk)
-{
- unsigned long clkrate = clk_get_rate(clk);
- unsigned int div;
- unsigned int fsclk;
- unsigned int actual;
- unsigned int fs;
- unsigned int fsdiv;
- signed int deviation = 0;
- unsigned int best_fs = 0;
- unsigned int best_div = 0;
- unsigned int best_rate = 0;
- unsigned int best_deviation = INT_MAX;
-
- pr_debug("Input clock rate %ldHz\n", clkrate);
-
- if (fstab == NULL)
- fstab = iis_fs_tab;
-
- for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
- fsdiv = iis_fs_tab[fs];
-
- fsclk = clkrate / fsdiv;
- div = fsclk / rate;
-
- if ((fsclk % rate) > (rate / 2))
- div++;
-
- if (div <= 1)
- continue;
-
- actual = clkrate / (fsdiv * div);
- deviation = actual - rate;
-
- printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
- fsdiv, div, actual, deviation);
-
- deviation = abs(deviation);
-
- if (deviation < best_deviation) {
- best_fs = fsdiv;
- best_div = div;
- best_rate = actual;
- best_deviation = deviation;
- }
-
- if (deviation == 0)
- break;
- }
-
- printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
- best_fs, best_div, best_rate);
-
- info->fs_div = best_fs;
- info->clk_div = best_div;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
-
-int s3c_i2sv2_probe(struct snd_soc_dai *dai,
- struct s3c_i2sv2_info *i2s,
- unsigned long base)
-{
- struct device *dev = dai->dev;
- unsigned int iismod;
-
- i2s->dev = dev;
-
- /* record our i2s structure for later use in the callbacks */
- snd_soc_dai_set_drvdata(dai, i2s);
-
- i2s->iis_pclk = clk_get(dev, "iis");
- if (IS_ERR(i2s->iis_pclk)) {
- dev_err(dev, "failed to get iis_clock\n");
- iounmap(i2s->regs);
- return -ENOENT;
- }
-
- clk_enable(i2s->iis_pclk);
-
- /* Mark ourselves as in TXRX mode so we can run through our cleanup
- * process without warnings. */
- iismod = readl(i2s->regs + S3C2412_IISMOD);
- iismod |= S3C2412_IISMOD_MODE_TXRX;
- writel(iismod, i2s->regs + S3C2412_IISMOD);
- s3c2412_snd_txctrl(i2s, 0);
- s3c2412_snd_rxctrl(i2s, 0);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
-
-#ifdef CONFIG_PM
-static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct s3c_i2sv2_info *i2s = to_info(dai);
- u32 iismod;
-
- if (dai->active) {
- i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
- i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
- i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
-
- /* some basic suspend checks */
-
- iismod = readl(i2s->regs + S3C2412_IISMOD);
-
- if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
- pr_warning("%s: RXDMA active?\n", __func__);
-
- if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
- pr_warning("%s: TXDMA active?\n", __func__);
-
- if (iismod & S3C2412_IISCON_IIS_ACTIVE)
- pr_warning("%s: IIS active\n", __func__);
- }
-
- return 0;
-}
-
-static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
-{
- struct s3c_i2sv2_info *i2s = to_info(dai);
-
- pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
- dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
-
- if (dai->active) {
- writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
- writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
- writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
-
- writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
- i2s->regs + S3C2412_IISFIC);
-
- ndelay(250);
- writel(0x0, i2s->regs + S3C2412_IISFIC);
- }
-
- return 0;
-}
-#else
-#define s3c2412_i2s_suspend NULL
-#define s3c2412_i2s_resume NULL
-#endif
-
-int s3c_i2sv2_register_component(struct device *dev, int id,
- struct snd_soc_component_driver *cmp_drv,
- struct snd_soc_dai_driver *dai_drv)
-{
- struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
-
- ops->trigger = s3c2412_i2s_trigger;
- if (!ops->hw_params)
- ops->hw_params = s3c_i2sv2_hw_params;
- ops->set_fmt = s3c2412_i2s_set_fmt;
- ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
- ops->set_sysclk = s3c_i2sv2_set_sysclk;
-
- /* Allow overriding by (for example) IISv4 */
- if (!ops->delay)
- ops->delay = s3c2412_i2s_delay;
-
- dai_drv->suspend = s3c2412_i2s_suspend;
- dai_drv->resume = s3c2412_i2s_resume;
-
- return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
-}
-EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
-
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h
deleted file mode 100644
index 90abab364b49..000000000000
--- a/sound/soc/samsung/s3c-i2s-v2.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/* sound/soc/samsung/s3c-i2s-v2.h
- *
- * ALSA Soc Audio Layer - S3C_I2SV2 I2S driver
- *
- * Copyright (c) 2007 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.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.
-*/
-
-/* This code is the core support for the I2S block found in a number of
- * Samsung SoC devices which is unofficially named I2S-V2. Currently the
- * S3C2412 and the S3C64XX series use this block to provide 1 or 2 I2S
- * channels via configurable GPIO.
- */
-
-#ifndef __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H
-#define __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H __FILE__
-
-#define S3C_I2SV2_DIV_BCLK (1)
-#define S3C_I2SV2_DIV_RCLK (2)
-#define S3C_I2SV2_DIV_PRESCALER (3)
-
-#define S3C_I2SV2_CLKSRC_PCLK 0
-#define S3C_I2SV2_CLKSRC_AUDIOBUS 1
-#define S3C_I2SV2_CLKSRC_CDCLK 2
-
-/* Set this flag for I2S controllers that have the bit IISMOD[12]
- * bridge/break RCLK signal and external Xi2sCDCLK pin.
- */
-#define S3C_FEATURE_CDCLKCON (1 << 0)
-
-/**
- * struct s3c_i2sv2_info - S3C I2S-V2 information
- * @dev: The parent device passed to use from the probe.
- * @regs: The pointer to the device registe block.
- * @feature: Set of bit-flags indicating features of the controller.
- * @master: True if the I2S core is the I2S bit clock master.
- * @dma_playback: DMA information for playback channel.
- * @dma_capture: DMA information for capture channel.
- * @suspend_iismod: PM save for the IISMOD register.
- * @suspend_iiscon: PM save for the IISCON register.
- * @suspend_iispsr: PM save for the IISPSR register.
- *
- * This is the private codec state for the hardware associated with an
- * I2S channel such as the register mappings and clock sources.
- */
-struct s3c_i2sv2_info {
- struct device *dev;
- void __iomem *regs;
-
- u32 feature;
-
- struct clk *iis_pclk;
- struct clk *iis_cclk;
-
- unsigned char master;
-
- struct s3c_dma_params *dma_playback;
- struct s3c_dma_params *dma_capture;
-
- u32 suspend_iismod;
- u32 suspend_iiscon;
- u32 suspend_iispsr;
-
- unsigned long base;
-};
-
-extern struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai);
-
-struct s3c_i2sv2_rate_calc {
- unsigned int clk_div; /* for prescaler */
- unsigned int fs_div; /* for root frame clock */
-};
-
-extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
- unsigned int *fstab,
- unsigned int rate, struct clk *clk);
-
-/**
- * s3c_i2sv2_probe - probe for i2s device helper
- * @dai: The ASoC DAI structure supplied to the original probe.
- * @i2s: Our local i2s structure to fill in.
- * @base: The base address for the registers.
- */
-extern int s3c_i2sv2_probe(struct snd_soc_dai *dai,
- struct s3c_i2sv2_info *i2s,
- unsigned long base);
-
-/**
- * s3c_i2sv2_register_component - register component and dai with soc core
- * @dev: DAI device
- * @id: DAI ID
- * @drv: The driver structure to register
- *
- * Fill in any missing fields and then register the given dai with the
- * soc core.
- */
-extern int s3c_i2sv2_register_component(struct device *dev, int id,
- struct snd_soc_component_driver *cmp_drv,
- struct snd_soc_dai_driver *dai_drv);
-
-#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
deleted file mode 100644
index 204029d12f5b..000000000000
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/* sound/soc/samsung/s3c2412-i2s.c
- *
- * ALSA Soc Audio Layer - S3C2412 I2S driver
- *
- * Copyright (c) 2006 Wolfson Microelectronics PLC.
- * Graeme Gregory graeme.gregory@wolfsonmicro.com
- * linux@wolfsonmicro.com
- *
- * Copyright (c) 2007, 2004-2005 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.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/delay.h>
-#include <linux/gpio.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <mach/gpio-samsung.h>
-#include <plat/gpio-cfg.h>
-
-#include "dma.h"
-#include "regs-i2s-v2.h"
-#include "s3c2412-i2s.h"
-
-#include <linux/platform_data/asoc-s3c.h>
-
-static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
- .ch_name = "tx",
- .dma_size = 4,
-};
-
-static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
- .ch_name = "rx",
- .dma_size = 4,
-};
-
-static struct s3c_i2sv2_info s3c2412_i2s;
-
-static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
-{
- int ret;
-
- pr_debug("Entered %s\n", __func__);
-
- samsung_asoc_init_dma_data(dai, &s3c2412_i2s_pcm_stereo_out,
- &s3c2412_i2s_pcm_stereo_in);
-
- ret = s3c_i2sv2_probe(dai, &s3c2412_i2s, S3C2410_PA_IIS);
- if (ret)
- return ret;
-
- s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
- s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
-
- s3c2412_i2s.iis_cclk = devm_clk_get(dai->dev, "i2sclk");
- if (IS_ERR(s3c2412_i2s.iis_cclk)) {
- pr_err("failed to get i2sclk clock\n");
- return PTR_ERR(s3c2412_i2s.iis_cclk);
- }
-
- /* Set MPLL as the source for IIS CLK */
-
- clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
- clk_prepare_enable(s3c2412_i2s.iis_cclk);
-
- s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
-
- /* Configure the I2S pins (GPE0...GPE4) in correct mode */
- s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
- S3C_GPIO_PULL_NONE);
-
- return 0;
-}
-
-static int s3c2412_i2s_remove(struct snd_soc_dai *dai)
-{
- clk_disable_unprepare(s3c2412_i2s.iis_cclk);
-
- return 0;
-}
-
-static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
-{
- struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(cpu_dai);
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- iismod = readl(i2s->regs + S3C2412_IISMOD);
- pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
-
- switch (params_width(params)) {
- case 8:
- iismod |= S3C2412_IISMOD_8BIT;
- break;
- case 16:
- iismod &= ~S3C2412_IISMOD_8BIT;
- break;
- }
-
- writel(iismod, i2s->regs + S3C2412_IISMOD);
- pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
-
- return 0;
-}
-
-#define S3C2412_I2S_RATES \
- (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-
-static const struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
- .hw_params = s3c2412_i2s_hw_params,
-};
-
-static struct snd_soc_dai_driver s3c2412_i2s_dai = {
- .probe = s3c2412_i2s_probe,
- .remove = s3c2412_i2s_remove,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = S3C2412_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = S3C2412_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &s3c2412_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver s3c2412_i2s_component = {
- .name = "s3c2412-i2s",
-};
-
-static int s3c2412_iis_dev_probe(struct platform_device *pdev)
-{
- int ret = 0;
- struct resource *res;
- struct s3c_audio_pdata *pdata = dev_get_platdata(&pdev->dev);
-
- if (!pdata) {
- dev_err(&pdev->dev, "missing platform data");
- return -ENXIO;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- s3c2412_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(s3c2412_i2s.regs))
- return PTR_ERR(s3c2412_i2s.regs);
-
- s3c2412_i2s_pcm_stereo_out.dma_addr = res->start + S3C2412_IISTXD;
- s3c2412_i2s_pcm_stereo_out.slave = pdata->dma_playback;
- s3c2412_i2s_pcm_stereo_in.dma_addr = res->start + S3C2412_IISRXD;
- s3c2412_i2s_pcm_stereo_in.slave = pdata->dma_capture;
-
- ret = s3c_i2sv2_register_component(&pdev->dev, -1,
- &s3c2412_i2s_component,
- &s3c2412_i2s_dai);
- if (ret) {
- pr_err("failed to register the dai\n");
- return ret;
- }
-
- ret = samsung_asoc_dma_platform_register(&pdev->dev,
- pdata->dma_filter);
- if (ret)
- pr_err("failed to register the DMA: %d\n", ret);
-
- return ret;
-}
-
-static struct platform_driver s3c2412_iis_driver = {
- .probe = s3c2412_iis_dev_probe,
- .driver = {
- .name = "s3c2412-iis",
- },
-};
-
-module_platform_driver(s3c2412_iis_driver);
-
-/* Module information */
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c2412-iis");
diff --git a/sound/soc/samsung/s3c2412-i2s.h b/sound/soc/samsung/s3c2412-i2s.h
deleted file mode 100644
index 02ad5794c0a9..000000000000
--- a/sound/soc/samsung/s3c2412-i2s.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* sound/soc/samsung/s3c2412-i2s.c
- *
- * ALSA Soc Audio Layer - S3C2412 I2S driver
- *
- * Copyright (c) 2007 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.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 __SND_SOC_S3C24XX_S3C2412_I2S_H
-#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
-
-#include "s3c-i2s-v2.h"
-
-#define S3C2412_DIV_BCLK S3C_I2SV2_DIV_BCLK
-#define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK
-#define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER
-
-#define S3C2412_CLKSRC_PCLK S3C_I2SV2_CLKSRC_PCLK
-#define S3C2412_CLKSRC_I2SCLK S3C_I2SV2_CLKSRC_AUDIOBUS
-
-#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
deleted file mode 100644
index b3a475d73ba7..000000000000
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * s3c24xx-i2s.c -- ALSA Soc Audio Layer
- *
- * (c) 2006 Wolfson Microelectronics PLC.
- * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * Copyright 2004-2005 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.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/delay.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <mach/gpio-samsung.h>
-#include <plat/gpio-cfg.h>
-#include "regs-iis.h"
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
-
-#include <linux/platform_data/asoc-s3c.h>
-
-static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
- .ch_name = "tx",
- .dma_size = 2,
-};
-
-static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
- .ch_name = "rx",
- .dma_size = 2,
-};
-
-struct s3c24xx_i2s_info {
- void __iomem *regs;
- struct clk *iis_clk;
- u32 iiscon;
- u32 iismod;
- u32 iisfcon;
- u32 iispsr;
-};
-static struct s3c24xx_i2s_info s3c24xx_i2s;
-
-static void s3c24xx_snd_txctrl(int on)
-{
- u32 iisfcon;
- u32 iiscon;
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
- iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
- iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-
- pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
-
- if (on) {
- iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
- iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
- iiscon &= ~S3C2410_IISCON_TXIDLE;
- iismod |= S3C2410_IISMOD_TXMODE;
-
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
- writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
- } else {
- /* note, we have to disable the FIFOs otherwise bad things
- * seem to happen when the DMA stops. According to the
- * Samsung supplied kernel, this should allow the DMA
- * engine and FIFOs to reset. If this isn't allowed, the
- * DMA engine will simply freeze randomly.
- */
-
- iisfcon &= ~S3C2410_IISFCON_TXENABLE;
- iisfcon &= ~S3C2410_IISFCON_TXDMA;
- iiscon |= S3C2410_IISCON_TXIDLE;
- iiscon &= ~S3C2410_IISCON_TXDMAEN;
- iismod &= ~S3C2410_IISMOD_TXMODE;
-
- writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
- writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- }
-
- pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
-}
-
-static void s3c24xx_snd_rxctrl(int on)
-{
- u32 iisfcon;
- u32 iiscon;
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
- iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
- iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-
- pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
-
- if (on) {
- iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
- iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
- iiscon &= ~S3C2410_IISCON_RXIDLE;
- iismod |= S3C2410_IISMOD_RXMODE;
-
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
- writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
- } else {
- /* note, we have to disable the FIFOs otherwise bad things
- * seem to happen when the DMA stops. According to the
- * Samsung supplied kernel, this should allow the DMA
- * engine and FIFOs to reset. If this isn't allowed, the
- * DMA engine will simply freeze randomly.
- */
-
- iisfcon &= ~S3C2410_IISFCON_RXENABLE;
- iisfcon &= ~S3C2410_IISFCON_RXDMA;
- iiscon |= S3C2410_IISCON_RXIDLE;
- iiscon &= ~S3C2410_IISCON_RXDMAEN;
- iismod &= ~S3C2410_IISMOD_RXMODE;
-
- writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
- writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- }
-
- pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
-}
-
-/*
- * Wait for the LR signal to allow synchronisation to the L/R clock
- * from the codec. May only be needed for slave mode.
- */
-static int s3c24xx_snd_lrsync(void)
-{
- u32 iiscon;
- int timeout = 50; /* 5ms */
-
- pr_debug("Entered %s\n", __func__);
-
- while (1) {
- iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
- if (iiscon & S3C2410_IISCON_LRINDEX)
- break;
-
- if (!timeout--)
- return -ETIMEDOUT;
- udelay(100);
- }
-
- return 0;
-}
-
-/*
- * Check whether CPU is the master or slave
- */
-static inline int s3c24xx_snd_is_clkmaster(void)
-{
- pr_debug("Entered %s\n", __func__);
-
- return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
-}
-
-/*
- * Set S3C24xx I2S DAI format
- */
-static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- pr_debug("hw_params r: IISMOD: %x \n", iismod);
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- iismod |= S3C2410_IISMOD_SLAVE;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- iismod &= ~S3C2410_IISMOD_SLAVE;
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_LEFT_J:
- iismod |= S3C2410_IISMOD_MSB;
- break;
- case SND_SOC_DAIFMT_I2S:
- iismod &= ~S3C2410_IISMOD_MSB;
- break;
- default:
- return -EINVAL;
- }
-
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- pr_debug("hw_params w: IISMOD: %x \n", iismod);
- return 0;
-}
-
-static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_dmaengine_dai_dma_data *dma_data;
- u32 iismod;
-
- pr_debug("Entered %s\n", __func__);
-
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
-
- /* Working copies of register */
- iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- pr_debug("hw_params r: IISMOD: %x\n", iismod);
-
- switch (params_width(params)) {
- case 8:
- iismod &= ~S3C2410_IISMOD_16BIT;
- dma_data->addr_width = 1;
- break;
- case 16:
- iismod |= S3C2410_IISMOD_16BIT;
- dma_data->addr_width = 2;
- break;
- default:
- return -EINVAL;
- }
-
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- pr_debug("hw_params w: IISMOD: %x\n", iismod);
- return 0;
-}
-
-static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- int ret = 0;
-
- pr_debug("Entered %s\n", __func__);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!s3c24xx_snd_is_clkmaster()) {
- ret = s3c24xx_snd_lrsync();
- if (ret)
- goto exit_err;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- s3c24xx_snd_rxctrl(1);
- else
- s3c24xx_snd_txctrl(1);
-
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- s3c24xx_snd_rxctrl(0);
- else
- s3c24xx_snd_txctrl(0);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
-exit_err:
- return ret;
-}
-
-/*
- * Set S3C24xx Clock source
- */
-static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-
- pr_debug("Entered %s\n", __func__);
-
- iismod &= ~S3C2440_IISMOD_MPLL;
-
- switch (clk_id) {
- case S3C24XX_CLKSRC_PCLK:
- break;
- case S3C24XX_CLKSRC_MPLL:
- iismod |= S3C2440_IISMOD_MPLL;
- break;
- default:
- return -EINVAL;
- }
-
- writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- return 0;
-}
-
-/*
- * Set S3C24xx Clock dividers
- */
-static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- u32 reg;
-
- pr_debug("Entered %s\n", __func__);
-
- switch (div_id) {
- case S3C24XX_DIV_BCLK:
- reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
- writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
- break;
- case S3C24XX_DIV_MCLK:
- reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
- writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
- break;
- case S3C24XX_DIV_PRESCALER:
- writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
- reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
- writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * To avoid duplicating clock code, allow machine driver to
- * get the clockrate from here.
- */
-u32 s3c24xx_i2s_get_clockrate(void)
-{
- return clk_get_rate(s3c24xx_i2s.iis_clk);
-}
-EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
-
-static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
-{
- pr_debug("Entered %s\n", __func__);
-
- samsung_asoc_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
- &s3c24xx_i2s_pcm_stereo_in);
-
- s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
- if (IS_ERR(s3c24xx_i2s.iis_clk)) {
- pr_err("failed to get iis_clock\n");
- return PTR_ERR(s3c24xx_i2s.iis_clk);
- }
- clk_prepare_enable(s3c24xx_i2s.iis_clk);
-
- /* Configure the I2S pins (GPE0...GPE4) in correct mode */
- s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
- S3C_GPIO_PULL_NONE);
-
- writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
-
- s3c24xx_snd_txctrl(0);
- s3c24xx_snd_rxctrl(0);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
-{
- pr_debug("Entered %s\n", __func__);
-
- s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
- s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
- s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
-
- clk_disable_unprepare(s3c24xx_i2s.iis_clk);
-
- return 0;
-}
-
-static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
-{
- pr_debug("Entered %s\n", __func__);
- clk_prepare_enable(s3c24xx_i2s.iis_clk);
-
- writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
- writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
- writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
-
- return 0;
-}
-#else
-#define s3c24xx_i2s_suspend NULL
-#define s3c24xx_i2s_resume NULL
-#endif
-
-
-#define S3C24XX_I2S_RATES \
- (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-
-static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
- .trigger = s3c24xx_i2s_trigger,
- .hw_params = s3c24xx_i2s_hw_params,
- .set_fmt = s3c24xx_i2s_set_fmt,
- .set_clkdiv = s3c24xx_i2s_set_clkdiv,
- .set_sysclk = s3c24xx_i2s_set_sysclk,
-};
-
-static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
- .probe = s3c24xx_i2s_probe,
- .suspend = s3c24xx_i2s_suspend,
- .resume = s3c24xx_i2s_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = S3C24XX_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = S3C24XX_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &s3c24xx_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver s3c24xx_i2s_component = {
- .name = "s3c24xx-i2s",
-};
-
-static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
-{
- int ret = 0;
- struct resource *res;
- struct s3c_audio_pdata *pdata = dev_get_platdata(&pdev->dev);
-
- if (!pdata) {
- dev_err(&pdev->dev, "missing platform data");
- return -ENXIO;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "Can't get IO resource.\n");
- return -ENOENT;
- }
- s3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(s3c24xx_i2s.regs))
- return PTR_ERR(s3c24xx_i2s.regs);
-
- s3c24xx_i2s_pcm_stereo_out.dma_addr = res->start + S3C2410_IISFIFO;
- s3c24xx_i2s_pcm_stereo_out.slave = pdata->dma_playback;
- s3c24xx_i2s_pcm_stereo_in.dma_addr = res->start + S3C2410_IISFIFO;
- s3c24xx_i2s_pcm_stereo_in.slave = pdata->dma_capture;
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
- if (ret) {
- pr_err("failed to register the dai\n");
- return ret;
- }
-
- ret = samsung_asoc_dma_platform_register(&pdev->dev,
- pdata->dma_filter);
- if (ret)
- pr_err("failed to register the dma: %d\n", ret);
-
- return ret;
-}
-
-static struct platform_driver s3c24xx_iis_driver = {
- .probe = s3c24xx_iis_dev_probe,
- .driver = {
- .name = "s3c24xx-iis",
- },
-};
-
-module_platform_driver(s3c24xx_iis_driver);
-
-/* Module information */
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c24xx-iis");
diff --git a/sound/soc/samsung/s3c24xx-i2s.h b/sound/soc/samsung/s3c24xx-i2s.h
deleted file mode 100644
index f9ca04edacb7..000000000000
--- a/sound/soc/samsung/s3c24xx-i2s.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * s3c24xx-i2s.c -- ALSA Soc Audio Layer
- *
- * Copyright 2005 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.
- *
- * Revision history
- * 10th Nov 2006 Initial version.
- */
-
-#ifndef S3C24XXI2S_H_
-#define S3C24XXI2S_H_
-
-/* clock sources */
-#define S3C24XX_CLKSRC_PCLK 0
-#define S3C24XX_CLKSRC_MPLL 1
-
-/* Clock dividers */
-#define S3C24XX_DIV_MCLK 0
-#define S3C24XX_DIV_BCLK 1
-#define S3C24XX_DIV_PRESCALER 2
-
-/* prescaler */
-#define S3C24XX_PRESCALE(a,b) \
- (((a - 1) << S3C2410_IISPSR_INTSHIFT) | ((b - 1) << S3C2410_IISPSR_EXTSHFIT))
-
-u32 s3c24xx_i2s_get_clockrate(void);
-
-#endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c
deleted file mode 100644
index dcc008d1e1ab..000000000000
--- a/sound/soc/samsung/s3c24xx_simtec.c
+++ /dev/null
@@ -1,372 +0,0 @@
-/* sound/soc/samsung/s3c24xx_simtec.c
- *
- * Copyright 2009 Simtec Electronics
- *
- * This program is free software; you can 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/clk.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-
-#include <linux/platform_data/asoc-s3c24xx_simtec.h>
-
-#include "s3c24xx-i2s.h"
-#include "s3c24xx_simtec.h"
-
-static struct s3c24xx_audio_simtec_pdata *pdata;
-static struct clk *xtal_clk;
-
-static int spk_gain;
-static int spk_unmute;
-
-/**
- * speaker_gain_get - read the speaker gain setting.
- * @kcontrol: The control for the speaker gain.
- * @ucontrol: The value that needs to be updated.
- *
- * Read the value for the AMP gain control.
- */
-static int speaker_gain_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = spk_gain;
- return 0;
-}
-
-/**
- * speaker_gain_set - set the value of the speaker amp gain
- * @value: The value to write.
- */
-static void speaker_gain_set(int value)
-{
- gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
- gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
-}
-
-/**
- * speaker_gain_put - set the speaker gain setting.
- * @kcontrol: The control for the speaker gain.
- * @ucontrol: The value that needs to be set.
- *
- * Set the value of the speaker gain from the specified
- * @ucontrol setting.
- *
- * Note, if the speaker amp is muted, then we do not set a gain value
- * as at-least one of the ICs that is fitted will try and power up even
- * if the main control is set to off.
- */
-static int speaker_gain_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int value = ucontrol->value.integer.value[0];
-
- spk_gain = value;
-
- if (!spk_unmute)
- speaker_gain_set(value);
-
- return 0;
-}
-
-static const struct snd_kcontrol_new amp_gain_controls[] = {
- SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
- speaker_gain_get, speaker_gain_put),
-};
-
-/**
- * spk_unmute_state - set the unmute state of the speaker
- * @to: zero to unmute, non-zero to ununmute.
- */
-static void spk_unmute_state(int to)
-{
- pr_debug("%s: to=%d\n", __func__, to);
-
- spk_unmute = to;
- gpio_set_value(pdata->amp_gpio, to);
-
- /* if we're umuting, also re-set the gain */
- if (to && pdata->amp_gain[0] > 0)
- speaker_gain_set(spk_gain);
-}
-
-/**
- * speaker_unmute_get - read the speaker unmute setting.
- * @kcontrol: The control for the speaker gain.
- * @ucontrol: The value that needs to be updated.
- *
- * Read the value for the AMP gain control.
- */
-static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = spk_unmute;
- return 0;
-}
-
-/**
- * speaker_unmute_put - set the speaker unmute setting.
- * @kcontrol: The control for the speaker gain.
- * @ucontrol: The value that needs to be set.
- *
- * Set the value of the speaker gain from the specified
- * @ucontrol setting.
- */
-static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- spk_unmute_state(ucontrol->value.integer.value[0]);
- return 0;
-}
-
-/* This is added as a manual control as the speaker amps create clicks
- * when their power state is changed, which are far more noticeable than
- * anything produced by the CODEC itself.
- */
-static const struct snd_kcontrol_new amp_unmute_controls[] = {
- SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
- speaker_unmute_get, speaker_unmute_put),
-};
-
-void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
-
- if (pdata->amp_gpio > 0) {
- pr_debug("%s: adding amp routes\n", __func__);
-
- snd_soc_add_card_controls(card, amp_unmute_controls,
- ARRAY_SIZE(amp_unmute_controls));
- }
-
- if (pdata->amp_gain[0] > 0) {
- pr_debug("%s: adding amp controls\n", __func__);
- snd_soc_add_card_controls(card, amp_gain_controls,
- ARRAY_SIZE(amp_gain_controls));
- }
-}
-EXPORT_SYMBOL_GPL(simtec_audio_init);
-
-#define CODEC_CLOCK 12000000
-
-/**
- * simtec_hw_params - update hardware parameters
- * @substream: The audio substream instance.
- * @params: The parameters requested.
- *
- * Update the codec data routing and configuration settings
- * from the supplied data.
- */
-static int simtec_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0,
- CODEC_CLOCK, SND_SOC_CLOCK_IN);
- if (ret) {
- pr_err( "%s: failed setting codec sysclk\n", __func__);
- return ret;
- }
-
- if (pdata->use_mpllin) {
- ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
- 0, SND_SOC_CLOCK_OUT);
-
- if (ret) {
- pr_err("%s: failed to set MPLLin as clksrc\n",
- __func__);
- return ret;
- }
- }
-
- if (pdata->output_cdclk) {
- int cdclk_scale;
-
- cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
- cdclk_scale--;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
- cdclk_scale);
- }
-
- return 0;
-}
-
-static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
-{
- /* call any board supplied startup code, this currently only
- * covers the bast/vr1000 which have a CPLD in the way of the
- * LRCLK */
- if (pd->startup)
- pd->startup();
-
- return 0;
-}
-
-static struct snd_soc_ops simtec_snd_ops = {
- .hw_params = simtec_hw_params,
-};
-
-/**
- * attach_gpio_amp - get and configure the necessary gpios
- * @dev: The device we're probing.
- * @pd: The platform data supplied by the board.
- *
- * If there is a GPIO based amplifier attached to the board, claim
- * the necessary GPIO lines for it, and set default values.
- */
-static int attach_gpio_amp(struct device *dev,
- struct s3c24xx_audio_simtec_pdata *pd)
-{
- int ret;
-
- /* attach gpio amp gain (if any) */
- if (pdata->amp_gain[0] > 0) {
- ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
- if (ret) {
- dev_err(dev, "cannot get amp gpio gain0\n");
- return ret;
- }
-
- ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
- if (ret) {
- dev_err(dev, "cannot get amp gpio gain1\n");
- gpio_free(pdata->amp_gain[0]);
- return ret;
- }
-
- gpio_direction_output(pd->amp_gain[0], 0);
- gpio_direction_output(pd->amp_gain[1], 0);
- }
-
- /* note, currently we assume GPA0 isn't valid amp */
- if (pdata->amp_gpio > 0) {
- ret = gpio_request(pd->amp_gpio, "gpio-amp");
- if (ret) {
- dev_err(dev, "cannot get amp gpio %d (%d)\n",
- pd->amp_gpio, ret);
- goto err_amp;
- }
-
- /* set the amp off at startup */
- spk_unmute_state(0);
- }
-
- return 0;
-
-err_amp:
- if (pd->amp_gain[0] > 0) {
- gpio_free(pd->amp_gain[0]);
- gpio_free(pd->amp_gain[1]);
- }
-
- return ret;
-}
-
-static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
-{
- if (pd->amp_gain[0] > 0) {
- gpio_free(pd->amp_gain[0]);
- gpio_free(pd->amp_gain[1]);
- }
-
- if (pd->amp_gpio > 0)
- gpio_free(pd->amp_gpio);
-}
-
-#ifdef CONFIG_PM
-static int simtec_audio_resume(struct device *dev)
-{
- simtec_call_startup(pdata);
- return 0;
-}
-
-const struct dev_pm_ops simtec_audio_pmops = {
- .resume = simtec_audio_resume,
-};
-EXPORT_SYMBOL_GPL(simtec_audio_pmops);
-#endif
-
-int simtec_audio_core_probe(struct platform_device *pdev,
- struct snd_soc_card *card)
-{
- struct platform_device *snd_dev;
- int ret;
-
- card->dai_link->ops = &simtec_snd_ops;
- card->dai_link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data supplied\n");
- return -EINVAL;
- }
-
- simtec_call_startup(pdata);
-
- xtal_clk = clk_get(&pdev->dev, "xtal");
- if (IS_ERR(xtal_clk)) {
- dev_err(&pdev->dev, "could not get clkout0\n");
- return -EINVAL;
- }
-
- dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
-
- ret = attach_gpio_amp(&pdev->dev, pdata);
- if (ret)
- goto err_clk;
-
- snd_dev = platform_device_alloc("soc-audio", -1);
- if (!snd_dev) {
- dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");
- ret = -ENOMEM;
- goto err_gpio;
- }
-
- platform_set_drvdata(snd_dev, card);
-
- ret = platform_device_add(snd_dev);
- if (ret) {
- dev_err(&pdev->dev, "failed to add soc-audio dev\n");
- goto err_pdev;
- }
-
- platform_set_drvdata(pdev, snd_dev);
- return 0;
-
-err_pdev:
- platform_device_put(snd_dev);
-
-err_gpio:
- detach_gpio_amp(pdata);
-
-err_clk:
- clk_put(xtal_clk);
- return ret;
-}
-EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
-
-int simtec_audio_remove(struct platform_device *pdev)
-{
- struct platform_device *snd_dev = platform_get_drvdata(pdev);
-
- platform_device_unregister(snd_dev);
-
- detach_gpio_amp(pdata);
- clk_put(xtal_clk);
- return 0;
-}
-EXPORT_SYMBOL_GPL(simtec_audio_remove);
-
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c24xx_simtec.h b/sound/soc/samsung/s3c24xx_simtec.h
deleted file mode 100644
index 8270748a2c41..000000000000
--- a/sound/soc/samsung/s3c24xx_simtec.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* sound/soc/samsung/s3c24xx_simtec.h
- *
- * Copyright 2009 Simtec Electronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-extern void simtec_audio_init(struct snd_soc_pcm_runtime *rtd);
-
-extern int simtec_audio_core_probe(struct platform_device *pdev,
- struct snd_soc_card *card);
-
-extern int simtec_audio_remove(struct platform_device *pdev);
-
-#ifdef CONFIG_PM
-extern const struct dev_pm_ops simtec_audio_pmops;
-#define simtec_audio_pm &simtec_audio_pmops
-#else
-#define simtec_audio_pm NULL
-#endif
diff --git a/sound/soc/samsung/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c
deleted file mode 100644
index 7ac924c595bf..000000000000
--- a/sound/soc/samsung/s3c24xx_simtec_hermes.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/* sound/soc/samsung/s3c24xx_simtec_hermes.c
- *
- * Copyright 2009 Simtec Electronics
- *
- * This program is free software; you can 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>
-
-#include "s3c24xx_simtec.h"
-
-static const struct snd_soc_dapm_widget dapm_widgets[] = {
- SND_SOC_DAPM_LINE("GSM Out", NULL),
- SND_SOC_DAPM_LINE("GSM In", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_LINE("ZV", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route base_map[] = {
- /* Headphone connected to HP{L,R}OUT and HP{L,R}COM */
-
- { "Headphone Jack", NULL, "HPLOUT" },
- { "Headphone Jack", NULL, "HPLCOM" },
- { "Headphone Jack", NULL, "HPROUT" },
- { "Headphone Jack", NULL, "HPRCOM" },
-
- /* ZV connected to Line1 */
-
- { "LINE1L", NULL, "ZV" },
- { "LINE1R", NULL, "ZV" },
-
- /* Line In connected to Line2 */
-
- { "LINE2L", NULL, "Line In" },
- { "LINE2R", NULL, "Line In" },
-
- /* Microphone connected to MIC3R and MIC_BIAS */
-
- { "MIC3L", NULL, "Mic Jack" },
-
- /* GSM connected to MONO_LOUT and MIC3L (in) */
-
- { "GSM Out", NULL, "MONO_LOUT" },
- { "MIC3L", NULL, "GSM In" },
-
- /* Speaker is connected to LINEOUT{LN,LP,RN,RP}, however we are
- * not using the DAPM to power it up and down as there it makes
- * a click when powering up. */
-};
-
-/**
- * simtec_hermes_init - initialise and add controls
- * @codec; The codec instance to attach to.
- *
- * Attach our controls and configure the necessary codec
- * mappings for our sound card instance.
-*/
-static int simtec_hermes_init(struct snd_soc_pcm_runtime *rtd)
-{
- simtec_audio_init(rtd);
-
- return 0;
-}
-
-static struct snd_soc_dai_link simtec_dai_aic33 = {
- .name = "tlv320aic33",
- .stream_name = "TLV320AIC33",
- .codec_name = "tlv320aic3x-codec.0-001a",
- .cpu_dai_name = "s3c24xx-iis",
- .codec_dai_name = "tlv320aic3x-hifi",
- .platform_name = "s3c24xx-iis",
- .init = simtec_hermes_init,
-};
-
-/* simtec audio machine driver */
-static struct snd_soc_card snd_soc_machine_simtec_aic33 = {
- .name = "Simtec-Hermes",
- .owner = THIS_MODULE,
- .dai_link = &simtec_dai_aic33,
- .num_links = 1,
-
- .dapm_widgets = dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
- .dapm_routes = base_map,
- .num_dapm_routes = ARRAY_SIZE(base_map),
-};
-
-static int simtec_audio_hermes_probe(struct platform_device *pd)
-{
- dev_info(&pd->dev, "probing....\n");
- return simtec_audio_core_probe(pd, &snd_soc_machine_simtec_aic33);
-}
-
-static struct platform_driver simtec_audio_hermes_platdrv = {
- .driver = {
- .name = "s3c24xx-simtec-hermes-snd",
- .pm = simtec_audio_pm,
- },
- .probe = simtec_audio_hermes_probe,
- .remove = simtec_audio_remove,
-};
-
-module_platform_driver(simtec_audio_hermes_platdrv);
-
-MODULE_ALIAS("platform:s3c24xx-simtec-hermes-snd");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
deleted file mode 100644
index b4ed2fc1a65c..000000000000
--- a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
- *
- * Copyright 2009 Simtec Electronics
- *
- * This program is free software; you can 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>
-
-#include "s3c24xx_simtec.h"
-
-/* supported machines:
- *
- * Machine Connections AMP
- * ------- ----------- ---
- * BAST MIC, HPOUT, LOUT, LIN TPA2001D1 (HPOUTL,R) (gain hardwired)
- * VR1000 HPOUT, LIN None
- * VR2000 LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
- * DePicture LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
- * Anubis LIN, LOUT, MIC, HP TPA2001D1 (HPOUTL,R)
- */
-
-static const struct snd_soc_dapm_widget dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route base_map[] = {
- { "Headphone Jack", NULL, "LHPOUT"},
- { "Headphone Jack", NULL, "RHPOUT"},
-
- { "Line Out", NULL, "LOUT" },
- { "Line Out", NULL, "ROUT" },
-
- { "LLINEIN", NULL, "Line In"},
- { "RLINEIN", NULL, "Line In"},
-
- { "MICIN", NULL, "Mic Jack"},
-};
-
-/**
- * simtec_tlv320aic23_init - initialise and add controls
- * @codec; The codec instance to attach to.
- *
- * Attach our controls and configure the necessary codec
- * mappings for our sound card instance.
-*/
-static int simtec_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
-{
- simtec_audio_init(rtd);
-
- return 0;
-}
-
-static struct snd_soc_dai_link simtec_dai_aic23 = {
- .name = "tlv320aic23",
- .stream_name = "TLV320AIC23",
- .codec_name = "tlv320aic3x-codec.0-001a",
- .cpu_dai_name = "s3c24xx-iis",
- .codec_dai_name = "tlv320aic3x-hifi",
- .platform_name = "s3c24xx-iis",
- .init = simtec_tlv320aic23_init,
-};
-
-/* simtec audio machine driver */
-static struct snd_soc_card snd_soc_machine_simtec_aic23 = {
- .name = "Simtec",
- .owner = THIS_MODULE,
- .dai_link = &simtec_dai_aic23,
- .num_links = 1,
-
- .dapm_widgets = dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
- .dapm_routes = base_map,
- .num_dapm_routes = ARRAY_SIZE(base_map),
-};
-
-static int simtec_audio_tlv320aic23_probe(struct platform_device *pd)
-{
- return simtec_audio_core_probe(pd, &snd_soc_machine_simtec_aic23);
-}
-
-static struct platform_driver simtec_audio_tlv320aic23_driver = {
- .driver = {
- .name = "s3c24xx-simtec-tlv320aic23",
- .pm = simtec_audio_pm,
- },
- .probe = simtec_audio_tlv320aic23_probe,
- .remove = simtec_audio_remove,
-};
-
-module_platform_driver(simtec_audio_tlv320aic23_driver);
-
-MODULE_ALIAS("platform:s3c24xx-simtec-tlv320aic23");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c
deleted file mode 100644
index 50849e137fc0..000000000000
--- a/sound/soc/samsung/s3c24xx_uda134x.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Modifications by Christian Pellegrin <chripell@evolware.org>
- *
- * s3c24xx_uda134x.c -- S3C24XX_UDA134X ALSA SoC Audio board driver
- *
- * Copyright 2007 Dension Audio Systems Ltd.
- * Author: Zoltan Devai
- *
- * This program is free software; you can 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/gpio.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/s3c24xx_uda134x.h>
-
-#include "regs-iis.h"
-
-#include "s3c24xx-i2s.h"
-
-/* #define ENFORCE_RATES 1 */
-/*
- Unfortunately the S3C24XX in master mode has a limited capacity of
- generating the clock for the codec. If you define this only rates
- that are really available will be enforced. But be careful, most
- user level application just want the usual sampling frequencies (8,
- 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
- operation for embedded systems. So if you aren't very lucky or your
- hardware engineer wasn't very forward-looking it's better to leave
- this undefined. If you do so an approximate value for the requested
- sampling rate in the range -/+ 5% will be chosen. If this in not
- possible an error will be returned.
-*/
-
-static struct clk *xtal;
-static struct clk *pclk;
-/* this is need because we don't have a place where to keep the
- * pointers to the clocks in each substream. We get the clocks only
- * when we are actually using them so we don't block stuff like
- * frequency change or oscillator power-off */
-static int clk_users;
-static DEFINE_MUTEX(clk_lock);
-
-static unsigned int rates[33 * 2];
-#ifdef ENFORCE_RATES
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-#endif
-
-static struct platform_device *s3c24xx_uda134x_snd_device;
-
-static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
-{
- int ret = 0;
-#ifdef ENFORCE_RATES
- struct snd_pcm_runtime *runtime = substream->runtime;
-#endif
-
- mutex_lock(&clk_lock);
- pr_debug("%s %d\n", __func__, clk_users);
- if (clk_users == 0) {
- xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
- if (IS_ERR(xtal)) {
- printk(KERN_ERR "%s cannot get xtal\n", __func__);
- ret = PTR_ERR(xtal);
- } else {
- pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
- "pclk");
- if (IS_ERR(pclk)) {
- printk(KERN_ERR "%s cannot get pclk\n",
- __func__);
- clk_put(xtal);
- ret = PTR_ERR(pclk);
- }
- }
- if (!ret) {
- int i, j;
-
- for (i = 0; i < 2; i++) {
- int fs = i ? 256 : 384;
-
- rates[i*33] = clk_get_rate(xtal) / fs;
- for (j = 1; j < 33; j++)
- rates[i*33 + j] = clk_get_rate(pclk) /
- (j * fs);
- }
- }
- }
- clk_users += 1;
- mutex_unlock(&clk_lock);
- if (!ret) {
-#ifdef ENFORCE_RATES
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates);
- if (ret < 0)
- printk(KERN_ERR "%s cannot set constraints\n",
- __func__);
-#endif
- }
- return ret;
-}
-
-static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
-{
- mutex_lock(&clk_lock);
- pr_debug("%s %d\n", __func__, clk_users);
- clk_users -= 1;
- if (clk_users == 0) {
- clk_put(xtal);
- xtal = NULL;
- clk_put(pclk);
- pclk = NULL;
- }
- mutex_unlock(&clk_lock);
-}
-
-static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret = 0;
- int clk_source, fs_mode;
- unsigned long rate = params_rate(params);
- long err, cerr;
- unsigned int div;
- int i, bi;
-
- err = 999999;
- bi = 0;
- for (i = 0; i < 2*33; i++) {
- cerr = rates[i] - rate;
- if (cerr < 0)
- cerr = -cerr;
- if (cerr < err) {
- err = cerr;
- bi = i;
- }
- }
- if (bi / 33 == 1)
- fs_mode = S3C2410_IISMOD_256FS;
- else
- fs_mode = S3C2410_IISMOD_384FS;
- if (bi % 33 == 0) {
- clk_source = S3C24XX_CLKSRC_MPLL;
- div = 1;
- } else {
- clk_source = S3C24XX_CLKSRC_PCLK;
- div = bi % 33;
- }
- pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
-
- clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
- pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
- fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
- clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
- div, clk, err);
-
- if ((err * 100 / rate) > 5) {
- printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
- "too different from desired (%ld%%)\n",
- err * 100 / rate);
- return -EINVAL;
- }
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
- S3C2410_IISMOD_32FS);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
- S3C24XX_PRESCALE(div, div));
- if (ret < 0)
- return ret;
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops s3c24xx_uda134x_ops = {
- .startup = s3c24xx_uda134x_startup,
- .shutdown = s3c24xx_uda134x_shutdown,
- .hw_params = s3c24xx_uda134x_hw_params,
-};
-
-static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
- .name = "UDA134X",
- .stream_name = "UDA134X",
- .codec_name = "uda134x-codec",
- .codec_dai_name = "uda134x-hifi",
- .cpu_dai_name = "s3c24xx-iis",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &s3c24xx_uda134x_ops,
- .platform_name = "s3c24xx-iis",
-};
-
-static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
- .name = "S3C24XX_UDA134X",
- .owner = THIS_MODULE,
- .dai_link = &s3c24xx_uda134x_dai_link,
- .num_links = 1,
-};
-
-static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
-
-static void setdat(int v)
-{
- gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
-}
-
-static void setclk(int v)
-{
- gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
-}
-
-static void setmode(int v)
-{
- gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
-}
-
-/* FIXME - This must be codec platform data but in which board file ?? */
-static struct uda134x_platform_data s3c24xx_uda134x = {
- .l3 = {
- .setdat = setdat,
- .setclk = setclk,
- .setmode = setmode,
- .data_hold = 1,
- .data_setup = 1,
- .clock_high = 1,
- .mode_hold = 1,
- .mode = 1,
- .mode_setup = 1,
- },
-};
-
-static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
-{
- if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
- printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
- "l3 %s pin already in use", fun);
- return -EBUSY;
- }
- gpio_direction_output(pin, 0);
- return 0;
-}
-
-static int s3c24xx_uda134x_probe(struct platform_device *pdev)
-{
- int ret;
-
- printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
-
- s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
- if (s3c24xx_uda134x_l3_pins == NULL) {
- printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
- "unable to find platform data\n");
- return -ENODEV;
- }
- s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
- s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
-
- if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
- "data") < 0)
- return -EBUSY;
- if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
- "clk") < 0) {
- gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
- return -EBUSY;
- }
- if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
- "mode") < 0) {
- gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
- gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
- return -EBUSY;
- }
-
- s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
- if (!s3c24xx_uda134x_snd_device) {
- printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
- "Unable to register\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(s3c24xx_uda134x_snd_device,
- &snd_soc_s3c24xx_uda134x);
- platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
- ret = platform_device_add(s3c24xx_uda134x_snd_device);
- if (ret) {
- printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
- platform_device_put(s3c24xx_uda134x_snd_device);
- }
-
- return ret;
-}
-
-static int s3c24xx_uda134x_remove(struct platform_device *pdev)
-{
- platform_device_unregister(s3c24xx_uda134x_snd_device);
- gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
- gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
- gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
- return 0;
-}
-
-static struct platform_driver s3c24xx_uda134x_driver = {
- .probe = s3c24xx_uda134x_probe,
- .remove = s3c24xx_uda134x_remove,
- .driver = {
- .name = "s3c24xx_uda134x",
- },
-};
-
-module_platform_driver(s3c24xx_uda134x_driver);
-
-MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
-MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
deleted file mode 100644
index 425ee2ba37f0..000000000000
--- a/sound/soc/samsung/smartq_wm8987.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/* sound/soc/samsung/smartq_wm8987.c
- *
- * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
- *
- * Based on smdk6410_wm8987.c
- * Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
- * 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/gpio/consumer.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include "i2s.h"
-#include "../codecs/wm8750.h"
-
-/*
- * WM8987 is register compatible with WM8750, so using that as base driver.
- */
-
-static struct snd_soc_card snd_soc_smartq;
-
-static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int clk = 0;
- int ret;
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 32000:
- case 48000:
- case 96000:
- clk = 12288000;
- break;
- case 11025:
- case 22050:
- case 44100:
- case 88200:
- clk = 11289600;
- break;
- }
-
- /* Use PCLK for I2S signal generation */
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
- 0, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* Gate the RCLK output on PAD */
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
- 0, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
- SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-/*
- * SmartQ WM8987 HiFi DAI operations.
- */
-static struct snd_soc_ops smartq_hifi_ops = {
- .hw_params = smartq_hifi_hw_params,
-};
-
-static struct snd_soc_jack smartq_jack;
-
-static struct snd_soc_jack_pin smartq_jack_pins[] = {
- /* Disable speaker when headphone is plugged in */
- {
- .pin = "Internal Speaker",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
- {
- .gpio = -1,
- .name = "headphone detect",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 200,
- },
-};
-
-static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
- SOC_DAPM_PIN_SWITCH("Internal Speaker"),
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
- SOC_DAPM_PIN_SWITCH("Internal Mic"),
-};
-
-static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k,
- int event)
-{
- struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq);
-
- gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event));
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Internal Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Jack", NULL, "LOUT2"},
- {"Headphone Jack", NULL, "ROUT2"},
-
- {"Internal Speaker", NULL, "LOUT2"},
- {"Internal Speaker", NULL, "ROUT2"},
-
- {"Mic Bias", NULL, "Internal Mic"},
- {"LINPUT2", NULL, "Mic Bias"},
-};
-
-static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
- int err = 0;
-
- /* set endpoints to not connected */
- snd_soc_dapm_nc_pin(dapm, "LINPUT1");
- snd_soc_dapm_nc_pin(dapm, "RINPUT1");
- snd_soc_dapm_nc_pin(dapm, "OUT3");
- snd_soc_dapm_nc_pin(dapm, "ROUT1");
-
- /* Headphone jack detection */
- err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &smartq_jack,
- smartq_jack_pins,
- ARRAY_SIZE(smartq_jack_pins));
- if (err)
- return err;
-
- err = snd_soc_jack_add_gpios(&smartq_jack,
- ARRAY_SIZE(smartq_jack_gpios),
- smartq_jack_gpios);
-
- return err;
-}
-
-static int smartq_wm8987_card_remove(struct snd_soc_card *card)
-{
- snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
- smartq_jack_gpios);
-
- return 0;
-}
-
-static struct snd_soc_dai_link smartq_dai[] = {
- {
- .name = "wm8987",
- .stream_name = "SmartQ Hi-Fi",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8750-hifi",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm8750.0-0x1a",
- .init = smartq_wm8987_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &smartq_hifi_ops,
- },
-};
-
-static struct snd_soc_card snd_soc_smartq = {
- .name = "SmartQ",
- .owner = THIS_MODULE,
- .remove = smartq_wm8987_card_remove,
- .dai_link = smartq_dai,
- .num_links = ARRAY_SIZE(smartq_dai),
-
- .dapm_widgets = wm8987_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .controls = wm8987_smartq_controls,
- .num_controls = ARRAY_SIZE(wm8987_smartq_controls),
-};
-
-static int smartq_probe(struct platform_device *pdev)
-{
- struct gpio_desc *gpio;
- int ret;
-
- platform_set_drvdata(pdev, &snd_soc_smartq);
-
- /* Initialise GPIOs used by amplifiers */
- gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown",
- GPIOD_OUT_HIGH);
- if (IS_ERR(gpio)) {
- dev_err(&pdev->dev, "Failed to register GPK12\n");
- ret = PTR_ERR(gpio);
- goto out;
- }
- snd_soc_card_set_drvdata(&snd_soc_smartq, gpio);
-
- ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq);
- if (ret)
- dev_err(&pdev->dev, "Failed to register card\n");
-
-out:
- return ret;
-}
-
-static struct platform_driver smartq_driver = {
- .driver = {
- .name = "smartq-audio",
- },
- .probe = smartq_probe,
-};
-
-module_platform_driver(smartq_driver);
-
-/* Module information */
-MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
-MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c
deleted file mode 100644
index c390aad68cfb..000000000000
--- a/sound/soc/samsung/smdk2443_wm9710.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * smdk2443_wm9710.c -- SoC audio for smdk2443
- *
- * 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.
- *
- */
-
-#include <linux/module.h>
-#include <sound/soc.h>
-
-static struct snd_soc_card smdk2443;
-
-static struct snd_soc_dai_link smdk2443_dai[] = {
-{
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "samsung-ac97",
- .codec_dai_name = "ac97-hifi",
- .codec_name = "ac97-codec",
- .platform_name = "samsung-ac97",
-},
-};
-
-static struct snd_soc_card smdk2443 = {
- .name = "SMDK2443",
- .owner = THIS_MODULE,
- .dai_link = smdk2443_dai,
- .num_links = ARRAY_SIZE(smdk2443_dai),
-};
-
-static struct platform_device *smdk2443_snd_ac97_device;
-
-static int __init smdk2443_init(void)
-{
- int ret;
-
- smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1);
- if (!smdk2443_snd_ac97_device)
- return -ENOMEM;
-
- platform_set_drvdata(smdk2443_snd_ac97_device, &smdk2443);
- ret = platform_device_add(smdk2443_snd_ac97_device);
-
- if (ret)
- platform_device_put(smdk2443_snd_ac97_device);
-
- return ret;
-}
-
-static void __exit smdk2443_exit(void)
-{
- platform_device_unregister(smdk2443_snd_ac97_device);
-}
-
-module_init(smdk2443_init);
-module_exit(smdk2443_exit);
-
-/* Module information */
-MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
-MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c
index a2f2363fe1c2..2474eb619882 100644
--- a/sound/soc/samsung/smdk_spdif.c
+++ b/sound/soc/samsung/smdk_spdif.c
@@ -1,14 +1,8 @@
-/*
- * smdk_spdif.c -- S/PDIF audio for SMDK
- *
- * Copyright 2010 Samsung Electronics 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; either version 2 of the
- * License, or (at your option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// smdk_spdif.c - S/PDIF audio for SMDK
+//
+// Copyright (C) 2010 Samsung Electronics Co., Ltd.
#include <linux/clk.h>
#include <linux/module.h>
@@ -106,8 +100,8 @@ static int set_audio_clock_rate(unsigned long epll_rate,
static int smdk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
unsigned long pll_out, rclk_rate;
int ret, ratio;
@@ -144,18 +138,20 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_ops smdk_spdif_ops = {
+static const struct snd_soc_ops smdk_spdif_ops = {
.hw_params = smdk_hw_params,
};
+SND_SOC_DAILINK_DEFS(spdif,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-spdif")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spdif-dit", "dit-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-spdif")));
+
static struct snd_soc_dai_link smdk_dai = {
.name = "S/PDIF",
.stream_name = "S/PDIF PCM Playback",
- .platform_name = "samsung-spdif",
- .cpu_dai_name = "samsung-spdif",
- .codec_dai_name = "dit-hifi",
- .codec_name = "spdif-dit",
.ops = &smdk_spdif_ops,
+ SND_SOC_DAILINK_REG(spdif),
};
static struct snd_soc_card smdk = {
diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c
deleted file mode 100644
index 548bfd993788..000000000000
--- a/sound/soc/samsung/smdk_wm8580.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * smdk_wm8580.c
- *
- * Copyright (c) 2009 Samsung Electronics Co. Ltd
- * Author: Jaswinder Singh <jassisinghbrar@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.
- */
-
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/wm8580.h"
-#include "i2s.h"
-
-/*
- * Default CFG switch settings to use this driver:
- *
- * SMDK6410: Set CFG1 1-3 Off, CFG2 1-4 On
- */
-
-/* SMDK has a 12MHZ crystal attached to WM8580 */
-#define SMDK_WM8580_FREQ 12000000
-
-static int smdk_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int pll_out;
- int bfs, rfs, ret;
-
- switch (params_width(params)) {
- case 8:
- bfs = 16;
- break;
- case 16:
- bfs = 32;
- break;
- default:
- return -EINVAL;
- }
-
- /* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
- * This criterion can't be met if we request PLL output
- * as {8000x256, 64000x256, 11025x256}Hz.
- * As a wayout, we rather change rfs to a minimum value that
- * results in (params_rate(params) * rfs), and itself, acceptable
- * to both - the CODEC and the CPU.
- */
- switch (params_rate(params)) {
- case 16000:
- case 22050:
- case 32000:
- case 44100:
- case 48000:
- case 88200:
- case 96000:
- rfs = 256;
- break;
- case 64000:
- rfs = 384;
- break;
- case 8000:
- case 11025:
- rfs = 512;
- break;
- default:
- return -EINVAL;
- }
- pll_out = params_rate(params) * rfs;
-
- /* Set WM8580 to drive MCLK from its PLLA */
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
- WM8580_CLKSRC_PLLA);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
- SMDK_WM8580_FREQ, pll_out);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
- pll_out, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-/*
- * SMDK WM8580 DAI operations.
- */
-static struct snd_soc_ops smdk_ops = {
- .hw_params = smdk_hw_params,
-};
-
-/* SMDK Playback widgets */
-static const struct snd_soc_dapm_widget smdk_wm8580_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Front", NULL),
- SND_SOC_DAPM_HP("Center+Sub", NULL),
- SND_SOC_DAPM_HP("Rear", NULL),
-
- SND_SOC_DAPM_MIC("MicIn", NULL),
- SND_SOC_DAPM_LINE("LineIn", NULL),
-};
-
-/* SMDK-PAIFTX connections */
-static const struct snd_soc_dapm_route smdk_wm8580_audio_map[] = {
- /* MicIn feeds AINL */
- {"AINL", NULL, "MicIn"},
-
- /* LineIn feeds AINL/R */
- {"AINL", NULL, "LineIn"},
- {"AINR", NULL, "LineIn"},
-
- /* Front Left/Right are fed VOUT1L/R */
- {"Front", NULL, "VOUT1L"},
- {"Front", NULL, "VOUT1R"},
-
- /* Center/Sub are fed VOUT2L/R */
- {"Center+Sub", NULL, "VOUT2L"},
- {"Center+Sub", NULL, "VOUT2R"},
-
- /* Rear Left/Right are fed VOUT3L/R */
- {"Rear", NULL, "VOUT3L"},
- {"Rear", NULL, "VOUT3R"},
-};
-
-static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd)
-{
- /* Enabling the microphone requires the fitting of a 0R
- * resistor to connect the line from the microphone jack.
- */
- snd_soc_dapm_disable_pin(&rtd->card->dapm, "MicIn");
-
- return 0;
-}
-
-enum {
- PRI_PLAYBACK = 0,
- PRI_CAPTURE,
- SEC_PLAYBACK,
-};
-
-#define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBM_CFM)
-
-static struct snd_soc_dai_link smdk_dai[] = {
- [PRI_PLAYBACK] = { /* Primary Playback i/f */
- .name = "WM8580 PAIF RX",
- .stream_name = "Playback",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8580-hifi-playback",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm8580.0-001b",
- .dai_fmt = SMDK_DAI_FMT,
- .ops = &smdk_ops,
- },
- [PRI_CAPTURE] = { /* Primary Capture i/f */
- .name = "WM8580 PAIF TX",
- .stream_name = "Capture",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8580-hifi-capture",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm8580.0-001b",
- .dai_fmt = SMDK_DAI_FMT,
- .init = smdk_wm8580_init_paiftx,
- .ops = &smdk_ops,
- },
- [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
- .name = "Sec_FIFO TX",
- .stream_name = "Playback",
- .cpu_dai_name = "samsung-i2s-sec",
- .codec_dai_name = "wm8580-hifi-playback",
- .platform_name = "samsung-i2s-sec",
- .codec_name = "wm8580.0-001b",
- .dai_fmt = SMDK_DAI_FMT,
- .ops = &smdk_ops,
- },
-};
-
-static struct snd_soc_card smdk = {
- .name = "SMDK-I2S",
- .owner = THIS_MODULE,
- .dai_link = smdk_dai,
- .num_links = 2,
-
- .dapm_widgets = smdk_wm8580_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(smdk_wm8580_dapm_widgets),
- .dapm_routes = smdk_wm8580_audio_map,
- .num_dapm_routes = ARRAY_SIZE(smdk_wm8580_audio_map),
-};
-
-static struct platform_device *smdk_snd_device;
-
-static int __init smdk_audio_init(void)
-{
- int ret;
- char *str;
-
- if (machine_is_smdkc100()
- || machine_is_smdkv210() || machine_is_smdkc110()) {
- smdk.num_links = 3;
- } else if (machine_is_smdk6410()) {
- str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
- str[strlen(str) - 1] = '2';
- str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
- str[strlen(str) - 1] = '2';
- }
-
- smdk_snd_device = platform_device_alloc("soc-audio", -1);
- if (!smdk_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(smdk_snd_device, &smdk);
- ret = platform_device_add(smdk_snd_device);
-
- if (ret)
- platform_device_put(smdk_snd_device);
-
- return ret;
-}
-module_init(smdk_audio_init);
-
-static void __exit smdk_audio_exit(void)
-{
- platform_device_unregister(smdk_snd_device);
-}
-module_exit(smdk_audio_exit);
-
-MODULE_AUTHOR("Jaswinder Singh, jassisinghbrar@gmail.com");
-MODULE_DESCRIPTION("ALSA SoC SMDK WM8580");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c
deleted file mode 100644
index 6deec5234c92..000000000000
--- a/sound/soc/samsung/smdk_wm8580pcm.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * sound/soc/samsung/smdk_wm8580pcm.c
- *
- * Copyright (c) 2011 Samsung Electronics 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; either version 2 of the License, or (at your
- * option) any later version.
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#include <sound/pcm.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/wm8580.h"
-#include "dma.h"
-#include "pcm.h"
-
-/*
- * Board Settings:
- * o '1' means 'ON'
- * o '0' means 'OFF'
- * o 'X' means 'Don't care'
- *
- * SMDK6410 Base B/D: CFG1-0000, CFG2-1111
- * SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000
- */
-
-#define SMDK_WM8580_EXT_OSC 12000000
-#define SMDK_WM8580_EXT_MCLK 4096000
-#define SMDK_WM8580_EXT_VOICE 2048000
-
-static unsigned long mclk_freq;
-static unsigned long xtal_freq;
-
-/*
- * If MCLK clock directly gets from XTAL, we don't have to use PLL
- * to make MCLK, but if XTAL clock source connects with other codec
- * pin (like XTI), we should have to set codec's PLL to make MCLK.
- * Because Samsung SoC does not support pcmcdclk output like I2S.
- */
-
-static int smdk_wm8580_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int rfs, ret;
-
- switch (params_rate(params)) {
- case 8000:
- break;
- default:
- printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n",
- __func__, __LINE__, params_rate(params));
- return -EINVAL;
- }
-
- rfs = mclk_freq / params_rate(params) / 2;
-
- if (mclk_freq == xtal_freq) {
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK,
- mclk_freq, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
- WM8580_CLKSRC_MCLK);
- if (ret < 0)
- return ret;
- } else {
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
- mclk_freq, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
- WM8580_CLKSRC_PLLA);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
- xtal_freq, mclk_freq);
- if (ret < 0)
- return ret;
- }
-
- /* Set PCM source clock on CPU */
- ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX,
- mclk_freq, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* Set SCLK_DIV for making bclk */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops smdk_wm8580_pcm_ops = {
- .hw_params = smdk_wm8580_pcm_hw_params,
-};
-
-#define SMDK_DAI_FMT (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | \
- SND_SOC_DAIFMT_CBS_CFS)
-
-static struct snd_soc_dai_link smdk_dai[] = {
- {
- .name = "WM8580 PAIF PCM RX",
- .stream_name = "Playback",
- .cpu_dai_name = "samsung-pcm.0",
- .codec_dai_name = "wm8580-hifi-playback",
- .platform_name = "samsung-audio",
- .codec_name = "wm8580.0-001b",
- .dai_fmt = SMDK_DAI_FMT,
- .ops = &smdk_wm8580_pcm_ops,
- }, {
- .name = "WM8580 PAIF PCM TX",
- .stream_name = "Capture",
- .cpu_dai_name = "samsung-pcm.0",
- .codec_dai_name = "wm8580-hifi-capture",
- .platform_name = "samsung-pcm.0",
- .codec_name = "wm8580.0-001b",
- .dai_fmt = SMDK_DAI_FMT,
- .ops = &smdk_wm8580_pcm_ops,
- },
-};
-
-static struct snd_soc_card smdk_pcm = {
- .name = "SMDK-PCM",
- .owner = THIS_MODULE,
- .dai_link = smdk_dai,
- .num_links = 2,
-};
-
-/*
- * After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1)
- * is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4),
- * 2.0484Mhz, directly with MCLK both Codec and SoC.
- */
-static int snd_smdk_probe(struct platform_device *pdev)
-{
- int ret = 0;
-
- xtal_freq = SMDK_WM8580_EXT_OSC;
- mclk_freq = SMDK_WM8580_EXT_MCLK;
-
- if (machine_is_smdkc110() || machine_is_smdkv210())
- xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE;
-
- smdk_pcm.dev = &pdev->dev;
- ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
-
- return ret;
-}
-
-static struct platform_driver snd_smdk_driver = {
- .driver = {
- .name = "samsung-smdk-pcm",
- },
- .probe = snd_smdk_probe,
-};
-
-module_platform_driver(snd_smdk_driver);
-
-MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>");
-MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index ff57b192d37d..911f56b12f0a 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -1,18 +1,10 @@
-/*
- * smdk_wm8994.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
#include "../codecs/wm8994.h"
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
/*
* Default CFG switch settings to use this driver:
@@ -39,20 +31,11 @@
/* SMDK has a 16.934MHZ crystal attached to WM8994 */
#define SMDK_WM8994_FREQ 16934000
-struct smdk_wm8994_data {
- int mclk1_rate;
-};
-
-/* Default SMDKs */
-static struct smdk_wm8994_data smdk_board_data = {
- .mclk1_rate = SMDK_WM8994_FREQ,
-};
-
static int smdk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int pll_out;
int ret;
@@ -80,55 +63,59 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
/*
* SMDK WM8994 DAI operations.
*/
-static struct snd_soc_ops smdk_ops = {
+static const struct snd_soc_ops smdk_ops = {
.hw_params = smdk_hw_params,
};
static int smdk_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
/* Other pins NC */
- snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
- snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
- snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
- snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
- snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
- snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
- snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
- snd_soc_dapm_nc_pin(dapm, "IN1LP");
- snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
- snd_soc_dapm_nc_pin(dapm, "IN1RP");
- snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+ snd_soc_dapm_disable_pin(dapm, "HPOUT2P");
+ snd_soc_dapm_disable_pin(dapm, "HPOUT2N");
+ snd_soc_dapm_disable_pin(dapm, "SPKOUTLN");
+ snd_soc_dapm_disable_pin(dapm, "SPKOUTLP");
+ snd_soc_dapm_disable_pin(dapm, "SPKOUTRP");
+ snd_soc_dapm_disable_pin(dapm, "SPKOUTRN");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT1N");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT1P");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT2N");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT2P");
+ snd_soc_dapm_disable_pin(dapm, "IN1LP");
+ snd_soc_dapm_disable_pin(dapm, "IN2LP:VXRN");
+ snd_soc_dapm_disable_pin(dapm, "IN1RP");
+ snd_soc_dapm_disable_pin(dapm, "IN2RP:VXRP");
return 0;
}
+SND_SOC_DAILINK_DEFS(aif1,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(fifo_tx,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s-sec")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s-sec")));
+
static struct snd_soc_dai_link smdk_dai[] = {
{ /* Primary DAI i/f */
.name = "WM8994 AIF1",
.stream_name = "Pri_Dai",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8994-aif1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm8994-codec",
.init = smdk_wm8994_init_paiftx,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &smdk_ops,
+ SND_SOC_DAILINK_REG(aif1),
}, { /* Sec_Fifo Playback i/f */
.name = "Sec_FIFO TX",
.stream_name = "Sec_Dai",
- .cpu_dai_name = "samsung-i2s-sec",
- .codec_dai_name = "wm8994-aif1",
- .platform_name = "samsung-i2s-sec",
- .codec_name = "wm8994-codec",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
+ SND_SOC_DAIFMT_CBP_CFP,
.ops = &smdk_ops,
+ SND_SOC_DAILINK_REG(fifo_tx),
},
};
@@ -140,7 +127,7 @@ static struct snd_soc_card smdk = {
};
static const struct of_device_id samsung_wm8994_of_match[] = {
- { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data },
+ { .compatible = "samsung,smdk-wm8994" },
{},
};
MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
@@ -150,39 +137,28 @@ static int smdk_audio_probe(struct platform_device *pdev)
int ret;
struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &smdk;
- struct smdk_wm8994_data *board;
- const struct of_device_id *id;
card->dev = &pdev->dev;
- board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
- if (!board)
- return -ENOMEM;
-
if (np) {
- smdk_dai[0].cpu_dai_name = NULL;
- smdk_dai[0].cpu_of_node = of_parse_phandle(np,
+ smdk_dai[0].cpus->dai_name = NULL;
+ smdk_dai[0].cpus->of_node = of_parse_phandle(np,
"samsung,i2s-controller", 0);
- if (!smdk_dai[0].cpu_of_node) {
+ if (!smdk_dai[0].cpus->of_node) {
dev_err(&pdev->dev,
"Property 'samsung,i2s-controller' missing or invalid\n");
ret = -EINVAL;
+ return ret;
}
- smdk_dai[0].platform_name = NULL;
- smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
+ smdk_dai[0].platforms->name = NULL;
+ smdk_dai[0].platforms->of_node = smdk_dai[0].cpus->of_node;
}
- id = of_match_device(of_match_ptr(samsung_wm8994_of_match), &pdev->dev);
- if (id)
- *board = *((struct smdk_wm8994_data *)id->data);
-
- platform_set_drvdata(pdev, board);
-
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
@@ -190,7 +166,7 @@ static int smdk_audio_probe(struct platform_device *pdev)
static struct platform_driver smdk_audio_driver = {
.driver = {
.name = "smdk-audio-wm8994",
- .of_match_table = of_match_ptr(samsung_wm8994_of_match),
+ .of_match_table = samsung_wm8994_of_match,
.pm = &snd_soc_pm_ops,
},
.probe = smdk_audio_probe,
diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c
index b1c89ec2d999..a3f539fbde5f 100644
--- a/sound/soc/samsung/smdk_wm8994pcm.c
+++ b/sound/soc/samsung/smdk_wm8994pcm.c
@@ -1,21 +1,14 @@
-/*
- * sound/soc/samsung/smdk_wm8994pcm.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- * http://www.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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2011 Samsung Electronics Co., Ltd
+// http://www.samsung.com
+
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "../codecs/wm8994.h"
-#include "dma.h"
#include "pcm.h"
/*
@@ -50,9 +43,9 @@
static int smdk_wm8994_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
unsigned long mclk_freq;
int rfs, ret;
@@ -92,21 +85,23 @@ static int smdk_wm8994_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops smdk_wm8994_pcm_ops = {
+static const struct snd_soc_ops smdk_wm8994_pcm_ops = {
.hw_params = smdk_wm8994_pcm_hw_params,
};
+SND_SOC_DAILINK_DEFS(paif_pcm,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-pcm.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-pcm.0")));
+
static struct snd_soc_dai_link smdk_dai[] = {
{
.name = "WM8994 PAIF PCM",
.stream_name = "Primary PCM",
- .cpu_dai_name = "samsung-pcm.0",
- .codec_dai_name = "wm8994-aif1",
- .platform_name = "samsung-pcm.0",
- .codec_name = "wm8994-codec",
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
+ SND_SOC_DAIFMT_CBC_CFC,
.ops = &smdk_wm8994_pcm_ops,
+ SND_SOC_DAILINK_REG(paif_pcm),
},
};
@@ -124,7 +119,7 @@ static int snd_smdk_probe(struct platform_device *pdev)
smdk_pcm.dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm);
if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
return ret;
}
diff --git a/sound/soc/samsung/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c
deleted file mode 100644
index 0d20e4ed27aa..000000000000
--- a/sound/soc/samsung/smdk_wm9713.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * smdk_wm9713.c -- SoC audio for SMDK
- *
- * Copyright 2010 Samsung Electronics Co. Ltd.
- * Author: Jaswinder Singh Brar <jassisinghbrar@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.
- *
- */
-
-#include <linux/module.h>
-#include <sound/soc.h>
-
-static struct snd_soc_card smdk;
-
-/*
- * Default CFG switch settings to use this driver:
- *
- * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off
- * SMDKC100: Set CFG6 1-3 On, CFG7 1 On
- * SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On
- * SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On
- * SMDKV310: Set CFG2 1-2 Off, CFG4 All On, CFG7 All Off, CFG8 1-On
- */
-
-/*
- Playback (HeadPhone):-
- $ amixer sset 'Headphone' unmute
- $ amixer sset 'Right Headphone Out Mux' 'Headphone'
- $ amixer sset 'Left Headphone Out Mux' 'Headphone'
- $ amixer sset 'Right HP Mixer PCM' unmute
- $ amixer sset 'Left HP Mixer PCM' unmute
-
- Capture (LineIn):-
- $ amixer sset 'Right Capture Source' 'Line'
- $ amixer sset 'Left Capture Source' 'Line'
-*/
-
-static struct snd_soc_dai_link smdk_dai = {
- .name = "AC97",
- .stream_name = "AC97 PCM",
- .platform_name = "samsung-ac97",
- .cpu_dai_name = "samsung-ac97",
- .codec_dai_name = "wm9713-hifi",
- .codec_name = "wm9713-codec",
-};
-
-static struct snd_soc_card smdk = {
- .name = "SMDK WM9713",
- .owner = THIS_MODULE,
- .dai_link = &smdk_dai,
- .num_links = 1,
-};
-
-static struct platform_device *smdk_snd_wm9713_device;
-static struct platform_device *smdk_snd_ac97_device;
-
-static int __init smdk_init(void)
-{
- int ret;
-
- smdk_snd_wm9713_device = platform_device_alloc("wm9713-codec", -1);
- if (!smdk_snd_wm9713_device)
- return -ENOMEM;
-
- ret = platform_device_add(smdk_snd_wm9713_device);
- if (ret)
- goto err1;
-
- smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1);
- if (!smdk_snd_ac97_device) {
- ret = -ENOMEM;
- goto err2;
- }
-
- platform_set_drvdata(smdk_snd_ac97_device, &smdk);
-
- ret = platform_device_add(smdk_snd_ac97_device);
- if (ret)
- goto err3;
-
- return 0;
-
-err3:
- platform_device_put(smdk_snd_ac97_device);
-err2:
- platform_device_del(smdk_snd_wm9713_device);
-err1:
- platform_device_put(smdk_snd_wm9713_device);
- return ret;
-}
-
-static void __exit smdk_exit(void)
-{
- platform_device_unregister(smdk_snd_ac97_device);
- platform_device_unregister(smdk_snd_wm9713_device);
-}
-
-module_init(smdk_init);
-module_exit(smdk_exit);
-
-/* Module information */
-MODULE_AUTHOR("Jaswinder Singh Brar, jassisinghbrar@gmail.com");
-MODULE_DESCRIPTION("ALSA SoC SMDK+WM9713");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c
index d8ac907bbb0d..66ef49dff1ba 100644
--- a/sound/soc/samsung/snow.c
+++ b/sound/soc/samsung/snow.c
@@ -1,113 +1,233 @@
-/*
- * ASoC machine driver for Snow boards
- *
- * This program is free software; you can 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ASoC machine driver for Snow boards
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-
+#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "i2s.h"
#define FIN_PLL_RATE 24000000
-static struct snd_soc_dai_link snow_dai[] = {
- {
- .name = "Primary",
- .stream_name = "Primary",
- .codec_dai_name = "HiFi",
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- },
+SND_SOC_DAILINK_DEFS(links,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+struct snow_priv {
+ struct snd_soc_dai_link dai_link;
+ struct clk *clk_i2s_bus;
};
-static int snow_late_probe(struct snd_soc_card *card)
+static int snow_card_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai *codec_dai;
- struct snd_soc_dai *cpu_dai;
- int ret;
+ static const unsigned int pll_rate[] = {
+ 73728000U, 67737602U, 49152000U, 45158401U, 32768001U
+ };
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snow_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ int bfs, psr, rfs, bitwidth;
+ unsigned long int rclk;
+ long int freq = -EINVAL;
+ int ret, i;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);
+ return bitwidth;
+ }
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
- cpu_dai = rtd->cpu_dai;
+ if (bitwidth != 16 && bitwidth != 24) {
+ dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);
+ return -EINVAL;
+ }
- /* Set the MCLK rate for the codec */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0,
- FIN_PLL_RATE, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
+ bfs = 2 * bitwidth;
+
+ switch (params_rate(params)) {
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ rfs = 8 * bfs;
+ break;
+ case 64000:
+ rfs = 384;
+ break;
+ case 8000:
+ case 11025:
+ case 12000:
+ rfs = 16 * bfs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rclk = params_rate(params) * rfs;
+
+ for (psr = 8; psr > 0; psr /= 2) {
+ for (i = 0; i < ARRAY_SIZE(pll_rate); i++) {
+ if ((pll_rate[i] - rclk * psr) <= 2) {
+ freq = pll_rate[i];
+ break;
+ }
+ }
+ }
+ if (freq < 0) {
+ dev_err(rtd->card->dev, "Unsupported RCLK rate: %lu\n", rclk);
+ return -EINVAL;
+ }
- /* Select I2S Bus clock to set RCLK and BCLK */
- ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
- 0, SND_SOC_CLOCK_IN);
- if (ret < 0)
+ ret = clk_set_rate(priv->clk_i2s_bus, freq);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "I2S bus clock rate set failed\n");
return ret;
+ }
return 0;
}
+static const struct snd_soc_ops snow_card_ops = {
+ .hw_params = snow_card_hw_params,
+};
+
+static int snow_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+ /* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ /* Set the MCLK rate for the codec */
+ return snd_soc_dai_set_sysclk(codec_dai, 0,
+ FIN_PLL_RATE, SND_SOC_CLOCK_IN);
+}
+
static struct snd_soc_card snow_snd = {
.name = "Snow-I2S",
.owner = THIS_MODULE,
- .dai_link = snow_dai,
- .num_links = ARRAY_SIZE(snow_dai),
-
.late_probe = snow_late_probe,
};
static int snow_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct snd_soc_card *card = &snow_snd;
- struct device_node *i2s_node, *codec_node;
- int i, ret;
-
- i2s_node = of_parse_phandle(pdev->dev.of_node,
- "samsung,i2s-controller", 0);
- if (!i2s_node) {
- dev_err(&pdev->dev,
- "Property 'i2s-controller' missing or invalid\n");
- return -EINVAL;
- }
-
- codec_node = of_parse_phandle(pdev->dev.of_node,
- "samsung,audio-codec", 0);
- if (!codec_node) {
- dev_err(&pdev->dev,
- "Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
- }
+ struct device_node *cpu, *codec;
+ struct snd_soc_dai_link *link;
+ struct snow_priv *priv;
+ int ret;
- for (i = 0; i < ARRAY_SIZE(snow_dai); i++) {
- snow_dai[i].codec_of_node = codec_node;
- snow_dai[i].cpu_of_node = i2s_node;
- snow_dai[i].platform_of_node = i2s_node;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ link = &priv->dai_link;
+
+ link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC;
+
+ link->name = "Primary";
+ link->stream_name = link->name;
+
+ link->cpus = links_cpus;
+ link->num_cpus = ARRAY_SIZE(links_cpus);
+ link->codecs = links_codecs;
+ link->num_codecs = ARRAY_SIZE(links_codecs);
+ link->platforms = links_platforms;
+ link->num_platforms = ARRAY_SIZE(links_platforms);
+
+ card->dai_link = link;
+ card->num_links = 1;
+ card->dev = dev;
+
+ /* Try new DT bindings with HDMI support first. */
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+
+ if (cpu) {
+ link->ops = &snow_card_ops;
+
+ link->cpus->of_node = of_parse_phandle(cpu, "sound-dai", 0);
+ of_node_put(cpu);
+
+ if (!link->cpus->of_node) {
+ dev_err(dev, "Failed parsing cpu/sound-dai property\n");
+ return -EINVAL;
+ }
+
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+ of_node_put(codec);
+
+ if (ret < 0) {
+ of_node_put(link->cpus->of_node);
+ dev_err(dev, "Failed parsing codec node\n");
+ return ret;
+ }
+
+ priv->clk_i2s_bus = of_clk_get_by_name(link->cpus->of_node,
+ "i2s_opclk0");
+ if (IS_ERR(priv->clk_i2s_bus)) {
+ snd_soc_of_put_dai_link_codecs(link);
+ of_node_put(link->cpus->of_node);
+ return PTR_ERR(priv->clk_i2s_bus);
+ }
+ } else {
+ link->codecs->dai_name = "HiFi";
+
+ link->cpus->of_node = of_parse_phandle(dev->of_node,
+ "samsung,i2s-controller", 0);
+ if (!link->cpus->of_node) {
+ dev_err(dev, "i2s-controller property parse error\n");
+ return -EINVAL;
+ }
+
+ link->codecs->of_node = of_parse_phandle(dev->of_node,
+ "samsung,audio-codec", 0);
+ if (!link->codecs->of_node) {
+ of_node_put(link->cpus->of_node);
+ dev_err(dev, "audio-codec property parse error\n");
+ return -EINVAL;
+ }
}
- card->dev = &pdev->dev;
+ link->platforms->of_node = link->cpus->of_node;
/* Update card-name if provided through DT, else use default name */
snd_soc_of_parse_card_name(card, "samsung,model");
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- return ret;
- }
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card failed\n");
+
+ return 0;
+}
+
+static void snow_remove(struct platform_device *pdev)
+{
+ struct snow_priv *priv = platform_get_drvdata(pdev);
+ struct snd_soc_dai_link *link = &priv->dai_link;
+
+ of_node_put(link->cpus->of_node);
+ of_node_put(link->codecs->of_node);
+ snd_soc_of_put_dai_link_codecs(link);
- return ret;
+ clk_put(priv->clk_i2s_bus);
}
static const struct of_device_id snow_of_match[] = {
@@ -125,6 +245,7 @@ static struct platform_driver snow_driver = {
.of_match_table = snow_of_match,
},
.probe = snow_probe,
+ .remove = snow_remove,
};
module_platform_driver(snow_driver);
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 4687f521197c..235d0063d1b3 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -1,14 +1,9 @@
-/* sound/soc/samsung/spdif.c
- *
- * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd
- * http://www.samsung.com/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
+//
+// Copyright (c) 2010 Samsung Electronics Co. Ltd
+// http://www.samsung.com/
#include <linux/clk.h>
#include <linux/io.h>
@@ -75,9 +70,9 @@
* @clk_rate: Current clock rate for calcurate ratio.
* @pclk: The peri-clock pointer for spdif master operation.
* @sclk: The source clock pointer for making sync signals.
- * @save_clkcon: Backup clkcon reg. in suspend.
- * @save_con: Backup con reg. in suspend.
- * @save_cstas: Backup cstas reg. in suspend.
+ * @saved_clkcon: Backup clkcon reg. in suspend.
+ * @saved_con: Backup con reg. in suspend.
+ * @saved_cstas: Backup cstas reg. in suspend.
* @dma_playback: DMA information for playback channel.
*/
struct samsung_spdif_info {
@@ -90,12 +85,18 @@ struct samsung_spdif_info {
u32 saved_clkcon;
u32 saved_con;
u32 saved_cstas;
- struct s3c_dma_params *dma_playback;
+ struct snd_dmaengine_dai_dma_data *dma_playback;
};
-static struct s3c_dma_params spdif_stereo_out;
+static struct snd_dmaengine_dai_dma_data spdif_stereo_out;
static struct samsung_spdif_info spdif_info;
+static inline struct samsung_spdif_info
+*component_to_info(struct snd_soc_component *component)
+{
+ return snd_soc_component_get_drvdata(component);
+}
+
static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
{
return snd_soc_dai_get_drvdata(cpu_dai);
@@ -140,8 +141,8 @@ static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct samsung_spdif_info *spdif = to_info(snd_soc_rtd_to_cpu(rtd, 0));
unsigned long flags;
dev_dbg(spdif->dev, "Entered %s\n", __func__);
@@ -176,10 +177,10 @@ static int spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *socdai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct samsung_spdif_info *spdif = to_info(snd_soc_rtd_to_cpu(rtd, 0));
void __iomem *regs = spdif->regs;
- struct s3c_dma_params *dma_data;
+ struct snd_dmaengine_dai_dma_data *dma_data;
u32 con, clkcon, cstas;
unsigned long flags;
int i, ratio;
@@ -193,7 +194,7 @@ static int spdif_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
+ snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream, dma_data);
spin_lock_irqsave(&spdif->lock, flags);
@@ -278,8 +279,8 @@ err:
static void spdif_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct samsung_spdif_info *spdif = to_info(snd_soc_rtd_to_cpu(rtd, 0));
void __iomem *regs = spdif->regs;
u32 con, clkcon;
@@ -295,9 +296,9 @@ static void spdif_shutdown(struct snd_pcm_substream *substream,
}
#ifdef CONFIG_PM
-static int spdif_suspend(struct snd_soc_dai *cpu_dai)
+static int spdif_suspend(struct snd_soc_component *component)
{
- struct samsung_spdif_info *spdif = to_info(cpu_dai);
+ struct samsung_spdif_info *spdif = component_to_info(component);
u32 con = spdif->saved_con;
dev_dbg(spdif->dev, "Entered %s\n", __func__);
@@ -312,9 +313,9 @@ static int spdif_suspend(struct snd_soc_dai *cpu_dai)
return 0;
}
-static int spdif_resume(struct snd_soc_dai *cpu_dai)
+static int spdif_resume(struct snd_soc_component *component)
{
- struct samsung_spdif_info *spdif = to_info(cpu_dai);
+ struct samsung_spdif_info *spdif = component_to_info(component);
dev_dbg(spdif->dev, "Entered %s\n", __func__);
@@ -348,12 +349,13 @@ static struct snd_soc_dai_driver samsung_spdif_dai = {
SNDRV_PCM_RATE_96000),
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.ops = &spdif_dai_ops,
- .suspend = spdif_suspend,
- .resume = spdif_resume,
};
static const struct snd_soc_component_driver samsung_spdif_component = {
- .name = "samsung-spdif",
+ .name = "samsung-spdif",
+ .suspend = spdif_suspend,
+ .resume = spdif_resume,
+ .legacy_dai_naming = 1,
};
static int spdif_probe(struct platform_device *pdev)
@@ -391,7 +393,9 @@ static int spdif_probe(struct platform_device *pdev)
ret = -ENOENT;
goto err0;
}
- clk_prepare_enable(spdif->pclk);
+ ret = clk_prepare_enable(spdif->pclk);
+ if (ret)
+ goto err0;
spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif");
if (IS_ERR(spdif->sclk)) {
@@ -399,7 +403,9 @@ static int spdif_probe(struct platform_device *pdev)
ret = -ENOENT;
goto err1;
}
- clk_prepare_enable(spdif->sclk);
+ ret = clk_prepare_enable(spdif->sclk);
+ if (ret)
+ goto err1;
/* Request S/PDIF Register's memory region */
if (!request_mem_region(mem_res->start,
@@ -416,31 +422,31 @@ static int spdif_probe(struct platform_device *pdev)
goto err3;
}
- dev_set_drvdata(&pdev->dev, spdif);
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &samsung_spdif_component, &samsung_spdif_dai, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "fail to register dai\n");
- goto err4;
- }
-
- spdif_stereo_out.dma_size = 2;
- spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
+ spdif_stereo_out.addr_width = 2;
+ spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF;
filter = NULL;
if (spdif_pdata) {
- spdif_stereo_out.slave = spdif_pdata->dma_playback;
+ spdif_stereo_out.filter_data = spdif_pdata->dma_playback;
filter = spdif_pdata->dma_filter;
}
-
spdif->dma_playback = &spdif_stereo_out;
- ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
+ NULL, NULL, NULL);
if (ret) {
dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
goto err4;
}
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &samsung_spdif_component, &samsung_spdif_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "fail to register dai\n");
+ goto err4;
+ }
+
return 0;
err4:
iounmap(spdif->regs);
@@ -454,7 +460,7 @@ err0:
return ret;
}
-static int spdif_remove(struct platform_device *pdev)
+static void spdif_remove(struct platform_device *pdev)
{
struct samsung_spdif_info *spdif = &spdif_info;
struct resource *mem_res;
@@ -462,18 +468,15 @@ static int spdif_remove(struct platform_device *pdev)
iounmap(spdif->regs);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mem_res)
- release_mem_region(mem_res->start, resource_size(mem_res));
+ release_mem_region(mem_res->start, resource_size(mem_res));
clk_disable_unprepare(spdif->sclk);
clk_disable_unprepare(spdif->pclk);
-
- return 0;
}
static struct platform_driver samsung_spdif_driver = {
.probe = spdif_probe,
- .remove = spdif_remove,
+ .remove = spdif_remove,
.driver = {
.name = "samsung-spdif",
},
diff --git a/sound/soc/samsung/spdif.h b/sound/soc/samsung/spdif.h
index 4f72cb446dbf..461da60ab040 100644
--- a/sound/soc/samsung/spdif.h
+++ b/sound/soc/samsung/spdif.h
@@ -1,13 +1,9 @@
-/* sound/soc/samsung/spdif.h
- *
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
* ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
*
* Copyright (c) 2010 Samsung Electronics Co. Ltd
* http://www.samsung.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 __SND_SOC_SAMSUNG_SPDIF_H
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index 083ef5e21b17..26701a55fe0a 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -1,24 +1,19 @@
-/*
- * Speyside audio support
- *
- * Copyright 2011 Wolfson Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Speyside audio support
+//
+// Copyright 2011 Wolfson Microelectronics
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
-#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include "../codecs/wm8996.h"
#include "../codecs/wm9081.h"
-#define WM8996_HPSEL_GPIO 214
#define MCLK_AUDIO_RATE (512 * 48000)
static int speyside_set_bias_level(struct snd_soc_card *card,
@@ -29,10 +24,10 @@ static int speyside_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dai *codec_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
- codec_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
- if (dapm->dev != codec_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
return 0;
switch (level) {
@@ -65,15 +60,15 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dai *codec_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
- codec_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
- if (dapm->dev != codec_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
ret = snd_soc_dai_set_pll(codec_dai, 0,
WM8996_FLL_MCLK2,
32768, MCLK_AUDIO_RATE);
@@ -95,8 +90,6 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card,
break;
}
- card->dapm.bias_level = level;
-
return 0;
}
@@ -110,33 +103,34 @@ static struct snd_soc_jack_pin speyside_headset_pins[] = {
},
};
+static struct gpio_desc *speyside_hpsel_gpio;
/* Default the headphone selection to active high */
static int speyside_jack_polarity;
static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
+ if (speyside_jack_polarity && (snd_soc_dapm_widget_name_cmp(source, "MICB1") == 0))
return 1;
- if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
+ if (!speyside_jack_polarity && (snd_soc_dapm_widget_name_cmp(source, "MICB2") == 0))
return 1;
return 0;
}
-static void speyside_set_polarity(struct snd_soc_codec *codec,
+static void speyside_set_polarity(struct snd_soc_component *component,
int polarity)
{
speyside_jack_polarity = !polarity;
- gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
+ gpiod_direction_output(speyside_hpsel_gpio, speyside_jack_polarity);
/* Re-run DAPM to make sure we're using the correct mic bias */
- snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ snd_soc_dapm_sync(snd_soc_component_to_dapm(component));
}
static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *dai = rtd->codec_dai;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
ret = snd_soc_dai_set_sysclk(dai, 0, MCLK_AUDIO_RATE, 0);
@@ -148,40 +142,50 @@ static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *dai = rtd->codec_dai;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = dai->component;
+ enum gpiod_flags flags;
int ret;
ret = snd_soc_dai_set_sysclk(dai, WM8996_SYSCLK_MCLK2, 32768, 0);
if (ret < 0)
return ret;
- ret = gpio_request(WM8996_HPSEL_GPIO, "HP_SEL");
- if (ret != 0)
- pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
- gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
-
- ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &speyside_headset, speyside_headset_pins,
- ARRAY_SIZE(speyside_headset_pins));
+ if (speyside_jack_polarity)
+ flags = GPIOD_OUT_HIGH;
+ else
+ flags = GPIOD_OUT_LOW;
+ speyside_hpsel_gpio = devm_gpiod_get(rtd->card->dev,
+ "hp-sel",
+ flags);
+ if (IS_ERR(speyside_hpsel_gpio))
+ return PTR_ERR(speyside_hpsel_gpio);
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
+ SND_JACK_LINEOUT | SND_JACK_HEADSET |
+ SND_JACK_BTN_0,
+ &speyside_headset,
+ speyside_headset_pins,
+ ARRAY_SIZE(speyside_headset_pins));
if (ret)
return ret;
- wm8996_detect(codec, &speyside_headset, speyside_set_polarity);
+ wm8996_detect(component, &speyside_headset, speyside_set_polarity);
return 0;
}
static int speyside_late_probe(struct snd_soc_card *card)
{
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Main Speaker");
- snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
- snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+
+ snd_soc_dapm_ignore_suspend(dapm, "Headphone");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Main AMIC");
+ snd_soc_dapm_ignore_suspend(dapm, "Main DMIC");
+ snd_soc_dapm_ignore_suspend(dapm, "Main Speaker");
+ snd_soc_dapm_ignore_suspend(dapm, "WM1250 Output");
+ snd_soc_dapm_ignore_suspend(dapm, "WM1250 Input");
return 0;
}
@@ -194,62 +198,66 @@ static const struct snd_soc_pcm_stream dsp_codec_params = {
.channels_max = 2,
};
+SND_SOC_DAILINK_DEFS(cpu_dsp,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(dsp_codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8996.1-001a", "wm8996-aif1")));
+
+SND_SOC_DAILINK_DEFS(baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("wm8996-aif2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
+
static struct snd_soc_dai_link speyside_dai[] = {
{
.name = "CPU-DSP",
.stream_name = "CPU-DSP",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm0010-sdi1",
- .platform_name = "samsung-i2s.0",
- .codec_name = "spi0.0",
.init = speyside_wm0010_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(cpu_dsp),
},
{
.name = "DSP-CODEC",
.stream_name = "DSP-CODEC",
- .cpu_dai_name = "wm0010-sdi2",
- .codec_dai_name = "wm8996-aif1",
- .codec_name = "wm8996.1-001a",
.init = speyside_wm8996_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .params = &dsp_codec_params,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .c2c_params = &dsp_codec_params,
+ .num_c2c_params = 1,
.ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(dsp_codec),
},
{
.name = "Baseband",
.stream_name = "Baseband",
- .cpu_dai_name = "wm8996-aif2",
- .codec_dai_name = "wm1250-ev1",
- .codec_name = "wm1250-ev1.1-0027",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(baseband),
},
};
static int speyside_wm9081_init(struct snd_soc_component *component)
{
- struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
-
/* At any time the WM9081 is active it will have this clock */
- return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
+ return snd_soc_component_set_sysclk(component, WM9081_SYSCLK_MCLK, 0,
MCLK_AUDIO_RATE, 0);
}
static struct snd_soc_aux_dev speyside_aux_dev[] = {
{
- .name = "wm9081",
- .codec_name = "wm9081.1-006c",
+ .dlc = COMP_AUX("wm9081.1-006c"),
.init = speyside_wm9081_init,
},
};
static struct snd_soc_codec_conf speyside_codec_conf[] = {
{
- .dev_name = "wm9081.1-006c",
+ .dlc = COMP_CODEC_CONF("wm9081.1-006c"),
.name_prefix = "Sub",
},
};
@@ -263,7 +271,7 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -273,7 +281,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_MIC("Main DMIC", NULL),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "IN1RN", NULL, "MICB1" },
{ "IN1RP", NULL, "MICB1" },
{ "IN1RN", NULL, "MICB2" },
@@ -324,6 +332,26 @@ static struct snd_soc_card speyside = {
.late_probe = speyside_late_probe,
};
+static struct gpiod_lookup_table wm8996_gpiod_table = {
+ /* Hardcoded device name in board file mach-crag6410.c */
+ .dev_id = "speyside",
+ .table = {
+ /*
+ * This line was hardcoded to 214 in the global GPIO
+ * number space, S3C GPIO macros seems top set the
+ * wm8996 codec GPIO start offset to 212, so this will
+ * be GPIO 214 - 212 = 2 on the wm8996.
+ */
+ GPIO_LOOKUP("wm8996", 2, "hp-sel", GPIO_ACTIVE_HIGH),
+ { },
+ },
+};
+
+static void speyside_gpiod_table_action(void *data)
+{
+ gpiod_remove_lookup_table(&wm8996_gpiod_table);
+}
+
static int speyside_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &speyside;
@@ -331,10 +359,15 @@ static int speyside_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
+ gpiod_add_lookup_table(&wm8996_gpiod_table);
+ ret = devm_add_action_or_reset(&pdev->dev, speyside_gpiod_table_action,
+ NULL);
+ if (ret)
+ return ret;
+
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c
new file mode 100644
index 000000000000..f1f59e059f5d
--- /dev/null
+++ b/sound/soc/samsung/tm2_wm5110.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
+//
+// Authors: Inha Song <ideal.song@samsung.com>
+// Sylwester Nawrocki <s.nawrocki@samsung.com>
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "i2s.h"
+#include "../codecs/wm5110.h"
+
+/*
+ * The source clock is XCLKOUT with its mux set to the external fixed rate
+ * oscillator (XXTI).
+ */
+#define MCLK_RATE 24000000U
+
+#define TM2_DAI_AIF1 0
+#define TM2_DAI_AIF2 1
+
+struct tm2_machine_priv {
+ struct snd_soc_component *component;
+ unsigned int sysclk_rate;
+ struct gpio_desc *gpio_mic_bias;
+};
+
+static int tm2_start_sysclk(struct snd_soc_card *card)
+{
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component = priv->component;
+ int ret;
+
+ ret = snd_soc_component_set_pll(component, WM5110_FLL1_REFCLK,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ priv->sysclk_rate);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set FLL1 source: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_pll(component, WM5110_FLL1,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ priv->sysclk_rate);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to start FLL1: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,
+ ARIZONA_CLK_SRC_FLL1,
+ priv->sysclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set SYSCLK source: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tm2_stop_sysclk(struct snd_soc_card *card)
+{
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component = priv->component;
+ int ret;
+
+ ret = snd_soc_component_set_pll(component, WM5110_FLL1, 0, 0, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to stop FLL1: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,
+ ARIZONA_CLK_SRC_FLL1, 0, 0);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to stop SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tm2_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ switch (params_rate(params)) {
+ case 4000:
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ /* Highest possible SYSCLK frequency: 147.456MHz */
+ priv->sysclk_rate = 147456000U;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ case 176400:
+ /* Highest possible SYSCLK frequency: 135.4752 MHz */
+ priv->sysclk_rate = 135475200U;
+ break;
+ default:
+ dev_err(component->dev, "Not supported sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ return tm2_start_sysclk(rtd->card);
+}
+
+static const struct snd_soc_ops tm2_aif1_ops = {
+ .hw_params = tm2_aif1_hw_params,
+};
+
+static int tm2_aif2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ unsigned int asyncclk_rate;
+ int ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ /* Highest possible ASYNCCLK frequency: 49.152MHz */
+ asyncclk_rate = 49152000U;
+ break;
+ case 11025:
+ /* Highest possible ASYNCCLK frequency: 45.1584 MHz */
+ asyncclk_rate = 45158400U;
+ break;
+ default:
+ dev_err(component->dev, "Not supported sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_set_pll(component, WM5110_FLL2_REFCLK,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ asyncclk_rate);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set FLL2 source: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_pll(component, WM5110_FLL2,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ asyncclk_rate);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to start FLL2: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,
+ ARIZONA_CLK_SRC_FLL2,
+ asyncclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(component->dev, "Failed to set ASYNCCLK source: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tm2_aif2_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ /* disable FLL2 */
+ ret = snd_soc_component_set_pll(component, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1,
+ 0, 0);
+ if (ret < 0)
+ dev_err(component->dev, "Failed to stop FLL2: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops tm2_aif2_ops = {
+ .hw_params = tm2_aif2_hw_params,
+ .hw_free = tm2_aif2_hw_free,
+};
+
+static int tm2_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ unsigned int bfs;
+ int bitwidth, ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ switch (bitwidth) {
+ case 48:
+ bfs = 64;
+ break;
+ case 16:
+ bfs = 32;
+ break;
+ default:
+ dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 48000:
+ case 96000:
+ case 192000:
+ break;
+ default:
+ dev_err(rtd->card->dev, "Unsupported sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK,
+ 0, SAMSUNG_I2S_OPCLK_PCLK);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops tm2_hdmi_ops = {
+ .hw_params = tm2_hdmi_hw_params,
+};
+
+static int tm2_mic_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpio_mic_bias, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpio_mic_bias, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int tm2_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *card_dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+ if (snd_soc_dapm_to_dev(dapm) != snd_soc_rtd_to_codec(rtd, 0)->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(card_dapm) == SND_SOC_BIAS_OFF)
+ tm2_start_sysclk(card);
+ break;
+ case SND_SOC_BIAS_OFF:
+ tm2_stop_sysclk(card);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_aux_dev tm2_speaker_amp_dev;
+
+static int tm2_late_probe(struct snd_soc_card *card)
+{
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+ unsigned int ch_map[] = { 0, 1 };
+ struct snd_soc_dai *amp_pdm_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *aif1_dai;
+ struct snd_soc_dai *aif2_dai;
+ int ret;
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF1]);
+ aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
+ priv->component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF2]);
+ aif2_dai = snd_soc_rtd_to_codec(rtd, 0);
+
+ ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret);
+ return ret;
+ }
+
+ amp_pdm_dai = snd_soc_find_dai(&tm2_speaker_amp_dev.dlc);
+ if (!amp_pdm_dai)
+ return -ENODEV;
+
+ /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */
+ ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map),
+ ch_map, 0, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tm2_controls[] = {
+ SOC_DAPM_PIN_SWITCH("HP"),
+ SOC_DAPM_PIN_SWITCH("SPK"),
+ SOC_DAPM_PIN_SWITCH("RCV"),
+ SOC_DAPM_PIN_SWITCH("VPS"),
+ SOC_DAPM_PIN_SWITCH("HDMI"),
+
+ SOC_DAPM_PIN_SWITCH("Main Mic"),
+ SOC_DAPM_PIN_SWITCH("Sub Mic"),
+ SOC_DAPM_PIN_SWITCH("Third Mic"),
+
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget tm2_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("HP", NULL),
+ SND_SOC_DAPM_SPK("SPK", NULL),
+ SND_SOC_DAPM_SPK("RCV", NULL),
+ SND_SOC_DAPM_LINE("VPS", NULL),
+ SND_SOC_DAPM_LINE("HDMI", NULL),
+
+ SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias),
+ SND_SOC_DAPM_MIC("Sub Mic", NULL),
+ SND_SOC_DAPM_MIC("Third Mic", NULL),
+
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_component_driver tm2_component = {
+ .name = "tm2-audio",
+};
+
+static struct snd_soc_dai_driver tm2_ext_dai[] = {
+ {
+ .name = "Voice call",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "Bluetooth",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+SND_SOC_DAILINK_DEFS(aif1,
+ DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(voice,
+ DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif2")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(bt,
+ DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif3")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(hdmi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link tm2_dai_links[] = {
+ {
+ .name = "WM5110 AIF1",
+ .stream_name = "HiFi Primary",
+ .ops = &tm2_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ SND_SOC_DAILINK_REG(aif1),
+ }, {
+ .name = "WM5110 Voice",
+ .stream_name = "Voice call",
+ .ops = &tm2_aif2_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(voice),
+ }, {
+ .name = "WM5110 BT",
+ .stream_name = "Bluetooth",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(bt),
+ }, {
+ .name = "HDMI",
+ .stream_name = "i2s1",
+ .ops = &tm2_hdmi_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(hdmi),
+ }
+};
+
+static struct snd_soc_card tm2_card = {
+ .owner = THIS_MODULE,
+
+ .dai_link = tm2_dai_links,
+ .controls = tm2_controls,
+ .num_controls = ARRAY_SIZE(tm2_controls),
+ .dapm_widgets = tm2_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tm2_dapm_widgets),
+ .aux_dev = &tm2_speaker_amp_dev,
+ .num_aux_devs = 1,
+
+ .late_probe = tm2_late_probe,
+ .set_bias_level = tm2_set_bias_level,
+};
+
+static int tm2_probe(struct platform_device *pdev)
+{
+ struct device_node *cpu_dai_node[2] = {};
+ struct device_node *codec_dai_node[2] = {};
+ const char *cells_name = NULL;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card = &tm2_card;
+ struct tm2_machine_priv *priv;
+ struct snd_soc_dai_link *dai_link;
+ int num_codecs, ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, priv);
+ card->dev = dev;
+
+ priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_mic_bias)) {
+ dev_err(dev, "Failed to get mic bias gpio\n");
+ return PTR_ERR(priv->gpio_mic_bias);
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret < 0) {
+ dev_err(dev, "Card name is not specified\n");
+ return ret;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret < 0) {
+ /* Backwards compatible way */
+ ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+ if (ret < 0) {
+ dev_err(dev, "Audio routing is not specified or invalid\n");
+ return ret;
+ }
+ }
+
+ card->aux_dev[0].dlc.of_node = of_parse_phandle(dev->of_node,
+ "audio-amplifier", 0);
+ if (!card->aux_dev[0].dlc.of_node) {
+ dev_err(dev, "audio-amplifier property invalid or missing\n");
+ return -EINVAL;
+ }
+
+ num_codecs = of_count_phandle_with_args(dev->of_node, "audio-codec",
+ NULL);
+
+ /* Skip the HDMI link if not specified in DT */
+ if (num_codecs > 1) {
+ card->num_links = ARRAY_SIZE(tm2_dai_links);
+ cells_name = "#sound-dai-cells";
+ } else {
+ card->num_links = ARRAY_SIZE(tm2_dai_links) - 1;
+ }
+
+ for (i = 0; i < num_codecs; i++) {
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "i2s-controller",
+ cells_name, i, &args);
+ if (ret) {
+ dev_err(dev, "i2s-controller property parse error: %d\n", i);
+ ret = -EINVAL;
+ goto dai_node_put;
+ }
+ cpu_dai_node[i] = args.np;
+
+ codec_dai_node[i] = of_parse_phandle(dev->of_node,
+ "audio-codec", i);
+ if (!codec_dai_node[i]) {
+ dev_err(dev, "audio-codec property parse error\n");
+ ret = -EINVAL;
+ goto dai_node_put;
+ }
+ }
+
+ /* Initialize WM5110 - I2S and HDMI - I2S1 DAI links */
+ for_each_card_prelinks(card, i, dai_link) {
+ unsigned int dai_index = 0; /* WM5110 */
+
+ dai_link->cpus->name = NULL;
+ dai_link->platforms->name = NULL;
+
+ if (num_codecs > 1 && i == card->num_links - 1)
+ dai_index = 1; /* HDMI */
+
+ dai_link->codecs->of_node = codec_dai_node[dai_index];
+ dai_link->cpus->of_node = cpu_dai_node[dai_index];
+ dai_link->platforms->of_node = cpu_dai_node[dai_index];
+ }
+
+ if (num_codecs > 1) {
+ struct of_phandle_args args;
+
+ /* HDMI DAI link (I2S1) */
+ i = card->num_links - 1;
+
+ ret = of_parse_phandle_with_fixed_args(dev->of_node,
+ "audio-codec", 0, 1, &args);
+ if (ret) {
+ dev_err(dev, "audio-codec property parse error\n");
+ goto dai_node_put;
+ }
+
+ ret = snd_soc_get_dai_name(&args, &card->dai_link[i].codecs->dai_name);
+ if (ret) {
+ dev_err(dev, "Unable to get codec_dai_name\n");
+ goto dai_node_put;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(dev, &tm2_component,
+ tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto dai_node_put;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "Failed to register card\n");
+ goto dai_node_put;
+ }
+
+dai_node_put:
+ for (i = 0; i < num_codecs; i++) {
+ of_node_put(codec_dai_node[i]);
+ of_node_put(cpu_dai_node[i]);
+ }
+
+ of_node_put(card->aux_dev[0].dlc.of_node);
+
+ return ret;
+}
+
+static int tm2_pm_prepare(struct device *dev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+
+ return tm2_stop_sysclk(card);
+}
+
+static void tm2_pm_complete(struct device *dev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+
+ tm2_start_sysclk(card);
+}
+
+static const struct dev_pm_ops tm2_pm_ops = {
+ .prepare = tm2_pm_prepare,
+ .suspend = snd_soc_suspend,
+ .resume = snd_soc_resume,
+ .complete = tm2_pm_complete,
+ .freeze = snd_soc_suspend,
+ .thaw = snd_soc_resume,
+ .poweroff = snd_soc_poweroff,
+ .restore = snd_soc_resume,
+};
+
+static const struct of_device_id tm2_of_match[] = {
+ { .compatible = "samsung,tm2-audio" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tm2_of_match);
+
+static struct platform_driver tm2_driver = {
+ .driver = {
+ .name = "tm2-audio",
+ .pm = &tm2_pm_ops,
+ .of_match_table = tm2_of_match,
+ },
+ .probe = tm2_probe,
+};
+module_platform_driver(tm2_driver);
+
+MODULE_AUTHOR("Inha Song <ideal.song@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c
index 3310eda7cf53..5448c16199bd 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -1,18 +1,12 @@
-/*
- * Tobermory audio support
- *
- * Copyright 2011 Wolfson Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Tobermory audio support
+//
+// Copyright 2011 Wolfson Microelectronics
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
-#include <linux/gpio.h>
#include <linux/module.h>
#include "../codecs/wm8962.h"
@@ -27,15 +21,15 @@ static int tobermory_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dai *codec_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
- if (dapm->dev != codec_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_STANDBY) {
ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
WM8962_FLL_MCLK, 32768,
sample_rate * 512);
@@ -70,10 +64,10 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dai *codec_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
- if (dapm->dev != codec_dai->dev)
+ if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
return 0;
switch (level) {
@@ -97,8 +91,6 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
@@ -110,21 +102,23 @@ static int tobermory_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops tobermory_ops = {
+static const struct snd_soc_ops tobermory_ops = {
.hw_params = tobermory_hw_params,
};
+SND_SOC_DAILINK_DEFS(cpu,
+ DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8962.1-001a", "wm8962")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
+
static struct snd_soc_dai_link tobermory_dai[] = {
{
.name = "CPU",
.stream_name = "CPU",
- .cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8962",
- .platform_name = "samsung-i2s.0",
- .codec_name = "wm8962.1-001a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &tobermory_ops,
+ SND_SOC_DAILINK_REG(cpu),
},
};
@@ -133,7 +127,7 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("DMIC"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -143,7 +137,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_SPK("Main Speaker", NULL),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "Headphone", NULL, "HPOUTL" },
{ "Headphone", NULL, "HPOUTR" },
@@ -179,27 +173,27 @@ static struct snd_soc_jack_pin tobermory_headset_pins[] = {
static int tobermory_late_probe(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct snd_soc_dai *codec_dai;
int ret;
- rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
- codec = rtd->codec;
- codec_dai = rtd->codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
- ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET |
- SND_JACK_BTN_0, &tobermory_headset,
- tobermory_headset_pins,
- ARRAY_SIZE(tobermory_headset_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET |
+ SND_JACK_BTN_0, &tobermory_headset,
+ tobermory_headset_pins,
+ ARRAY_SIZE(tobermory_headset_pins));
if (ret)
return ret;
- wm8962_mic_detect(codec, &tobermory_headset);
+ wm8962_mic_detect(component, &tobermory_headset);
return 0;
}
@@ -233,8 +227,7 @@ static int tobermory_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
return ret;
}
diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig
new file mode 100644
index 000000000000..fabb69a3450d
--- /dev/null
+++ b/sound/soc/sdca/Kconfig
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "SoundWire (SDCA)"
+
+config SND_SOC_SDCA
+ tristate
+ depends on ACPI
+ select AUXILIARY_BUS
+ help
+ This option enables support for the MIPI SoundWire Device
+ Class for Audio (SDCA).
+
+config SND_SOC_SDCA_HID
+ bool "SDCA HID support"
+ depends on SND_SOC_SDCA
+ depends on HID=y || HID=SND_SOC_SDCA
+ default y
+ help
+ This option enables support for audio jack button reporting using HID.
+
+config SND_SOC_SDCA_IRQ
+ bool "SDCA IRQ support"
+ select REGMAP
+ select REGMAP_IRQ
+ depends on SND_SOC_SDCA
+ default y
+ help
+ This option enables support for SDCA IRQs.
+
+config SND_SOC_SDCA_FDL
+ bool "SDCA FDL (File DownLoad) support"
+ depends on SND_SOC_SDCA
+ default y
+ help
+ This option enables support for the File Download using UMP,
+ typically used for downloading firmware to devices.
+
+config SND_SOC_SDCA_OPTIONAL
+ def_tristate SND_SOC_SDCA || !SND_SOC_SDCA
+
+config SND_SOC_SDCA_CLASS
+ tristate "SDCA Class Driver"
+ depends on SOUNDWIRE
+ depends on HID=y || HID=SND_SOC_SDCA
+ depends on SND_SOC_SDCA
+ select SND_SOC_SDCA_CLASS_FUNCTION
+ select SND_SOC_SDCA_FDL
+ select SND_SOC_SDCA_HID
+ select SND_SOC_SDCA_IRQ
+ help
+ This option enables support for the SDCA Class driver which should
+ support any class compliant SDCA part.
+
+config SND_SOC_SDCA_CLASS_FUNCTION
+ tristate
+ help
+ This option enables support for the SDCA Class Function drivers,
+ these implement the individual functions of the SDCA Class driver.
+
+endmenu
diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile
new file mode 100644
index 000000000000..f6b73275d964
--- /dev/null
+++ b/sound/soc/sdca/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_function_device.o \
+ sdca_regmap.o sdca_asoc.o sdca_ump.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_FDL) += sdca_fdl.o
+
+snd-soc-sdca-class-y := sdca_class.o
+snd-soc-sdca-class-function-y := sdca_class_function.o
+
+obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
+
+obj-$(CONFIG_SND_SOC_SDCA_CLASS) += snd-soc-sdca-class.o
+obj-$(CONFIG_SND_SOC_SDCA_CLASS_FUNCTION) += snd-soc-sdca-class-function.o
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
new file mode 100644
index 000000000000..2d328bbb95b9
--- /dev/null
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -0,0 +1,1559 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/bits.h>
+#include <linux/bitmap.h>
+#include <linux/build_bug.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/string_helpers.h>
+#include <linux/types.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdca.h>
+#include <sound/sdca_asoc.h>
+#include <sound/sdca_function.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+static bool exported_control(struct sdca_entity *entity, struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ return true;
+ default:
+ break;
+ }
+
+ return control->layers & (SDCA_ACCESS_LAYER_USER |
+ SDCA_ACCESS_LAYER_APPLICATION);
+}
+
+static bool readonly_control(struct sdca_control *control)
+{
+ return control->has_fixed || control->mode == SDCA_ACCESS_MODE_RO;
+}
+
+/**
+ * sdca_asoc_count_component - count the various component parts
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @num_widgets: Output integer pointer, will be filled with the
+ * required number of DAPM widgets for the Function.
+ * @num_routes: Output integer pointer, will be filled with the
+ * required number of DAPM routes for the Function.
+ * @num_controls: Output integer pointer, will be filled with the
+ * required number of ALSA controls for the Function.
+ * @num_dais: Output integer pointer, will be filled with the
+ * required number of ASoC DAIs for the Function.
+ *
+ * This function counts various things within the SDCA Function such
+ * that the calling driver can allocate appropriate space before
+ * calling the appropriate population functions.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *function,
+ int *num_widgets, int *num_routes, int *num_controls,
+ int *num_dais)
+{
+ int i, j;
+
+ *num_widgets = function->num_entities - 1;
+ *num_routes = 0;
+ *num_controls = 0;
+ *num_dais = 0;
+
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ /* Add supply/DAI widget connections */
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ *num_routes += !!entity->iot.clock;
+ *num_routes += !!entity->iot.is_dataport;
+ *num_controls += !entity->iot.is_dataport;
+ *num_dais += !!entity->iot.is_dataport;
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ *num_routes += entity->pde.num_managed;
+ break;
+ default:
+ break;
+ }
+
+ if (entity->group)
+ (*num_routes)++;
+
+ /* Add primary entity connections from DisCo */
+ *num_routes += entity->num_sources;
+
+ for (j = 0; j < entity->num_controls; j++) {
+ if (exported_control(entity, &entity->controls[j]))
+ (*num_controls)++;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA");
+
+static int entity_early_parse_ge(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity)
+{
+ struct sdca_control_range *range;
+ struct sdca_control *control;
+ struct snd_kcontrol_new *kctl;
+ struct soc_enum *soc_enum;
+ const char *control_name;
+ unsigned int *values;
+ const char **texts;
+ int i;
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE);
+ if (!control)
+ return -EINVAL;
+
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ range = sdca_control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, control->label);
+ if (!control_name)
+ return -ENOMEM;
+
+ kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL);
+ if (!kctl)
+ return -ENOMEM;
+
+ soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL);
+ if (!soc_enum)
+ return -ENOMEM;
+
+ texts = devm_kcalloc(dev, range->rows + 3, sizeof(*texts), GFP_KERNEL);
+ if (!texts)
+ return -ENOMEM;
+
+ values = devm_kcalloc(dev, range->rows + 3, sizeof(*values), GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ texts[0] = "Jack Unplugged";
+ texts[1] = "Jack Unknown";
+ texts[2] = "Detection in Progress";
+ values[0] = SDCA_DETECTED_MODE_JACK_UNPLUGGED;
+ values[1] = SDCA_DETECTED_MODE_JACK_UNKNOWN;
+ values[2] = SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS;
+ for (i = 0; i < range->rows; i++) {
+ enum sdca_terminal_type type;
+
+ type = sdca_range(range, SDCA_SELECTED_MODE_TERM_TYPE, i);
+
+ values[i + 3] = sdca_range(range, SDCA_SELECTED_MODE_INDEX, i);
+ texts[i + 3] = sdca_find_terminal_name(type);
+ if (!texts[i + 3]) {
+ dev_err(dev, "%s: unrecognised terminal type: %#x\n",
+ entity->label, type);
+ return -EINVAL;
+ }
+ }
+
+ soc_enum->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ soc_enum->items = range->rows + 3;
+ soc_enum->mask = roundup_pow_of_two(soc_enum->items) - 1;
+ soc_enum->texts = texts;
+ soc_enum->values = values;
+
+ kctl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl->name = control_name;
+ kctl->info = snd_soc_info_enum_double;
+ kctl->get = snd_soc_dapm_get_enum_double;
+ kctl->put = snd_soc_dapm_put_enum_double;
+ kctl->private_value = (unsigned long)soc_enum;
+
+ entity->ge.kctl = kctl;
+
+ return 0;
+}
+
+static void add_route(struct snd_soc_dapm_route **route, const char *sink,
+ const char *control, const char *source)
+{
+ (*route)->sink = sink;
+ (*route)->control = control;
+ (*route)->source = source;
+ (*route)++;
+}
+
+static int entity_parse_simple(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route,
+ enum snd_soc_dapm_type id)
+{
+ int i;
+
+ (*widget)->id = id;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ return 0;
+}
+
+static int entity_parse_it(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ int i;
+
+ if (entity->iot.is_dataport) {
+ const char *aif_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, "Playback");
+ if (!aif_name)
+ return -ENOMEM;
+
+ (*widget)->id = snd_soc_dapm_aif_in;
+
+ add_route(route, entity->label, NULL, aif_name);
+ } else {
+ (*widget)->id = snd_soc_dapm_mic;
+ }
+
+ if (entity->iot.clock)
+ add_route(route, entity->label, NULL, entity->iot.clock->label);
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ (*widget)++;
+
+ return 0;
+}
+
+static int entity_parse_ot(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ int i;
+
+ if (entity->iot.is_dataport) {
+ const char *aif_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, "Capture");
+ if (!aif_name)
+ return -ENOMEM;
+
+ (*widget)->id = snd_soc_dapm_aif_out;
+
+ add_route(route, aif_name, NULL, entity->label);
+ } else {
+ (*widget)->id = snd_soc_dapm_spk;
+ }
+
+ if (entity->iot.clock)
+ add_route(route, entity->label, NULL, entity->iot.clock->label);
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ (*widget)++;
+
+ return 0;
+}
+
+static int entity_pde_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kctl, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct sdca_entity *entity = widget->priv;
+ static const int polls = 100;
+ unsigned int reg, val;
+ int from, to, i;
+ int poll_us;
+ int ret;
+
+ if (!component)
+ return -EIO;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ from = widget->on_val;
+ to = widget->off_val;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ from = widget->off_val;
+ to = widget->on_val;
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < entity->pde.num_max_delay; i++) {
+ struct sdca_pde_delay *delay = &entity->pde.max_delay[i];
+
+ if (delay->from_ps == from && delay->to_ps == to) {
+ poll_us = delay->us / polls;
+ break;
+ }
+ }
+
+ reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(widget->reg),
+ SDW_SDCA_CTL_ENT(widget->reg),
+ SDCA_CTL_PDE_ACTUAL_PS, 0);
+
+ for (i = 0; i < polls; i++) {
+ if (i)
+ fsleep(poll_us);
+
+ ret = regmap_read(component->regmap, reg, &val);
+ if (ret)
+ return ret;
+ else if (val == to)
+ return 0;
+ }
+
+ dev_err(component->dev, "%s: power transition failed: %x\n",
+ entity->label, val);
+ return -ETIMEDOUT;
+}
+
+static int entity_parse_pde(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ unsigned int target = (1 << SDCA_PDE_PS0) | (1 << SDCA_PDE_PS3);
+ struct sdca_control_range *range;
+ struct sdca_control *control;
+ unsigned int mask = 0;
+ int i;
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS);
+ if (!control)
+ return -EINVAL;
+
+ /* Power should only be controlled by the driver */
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ range = sdca_control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++)
+ mask |= 1 << sdca_range(range, SDCA_REQUESTED_PS_STATE, i);
+
+ if ((mask & target) != target) {
+ dev_err(dev, "%s: power control missing states\n", entity->label);
+ return -EINVAL;
+ }
+
+ (*widget)->id = snd_soc_dapm_supply;
+ (*widget)->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ (*widget)->mask = GENMASK(control->nbits - 1, 0);
+ (*widget)->on_val = SDCA_PDE_PS0;
+ (*widget)->off_val = SDCA_PDE_PS3;
+ (*widget)->event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD;
+ (*widget)->event = entity_pde_event;
+ (*widget)->priv = entity;
+ (*widget)++;
+
+ for (i = 0; i < entity->pde.num_managed; i++)
+ add_route(route, entity->pde.managed[i]->label, NULL, entity->label);
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ return 0;
+}
+
+/* Device selector units are controlled through a group entity */
+static int entity_parse_su_device(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct sdca_control_range *range;
+ int num_routes = 0;
+ int i, j;
+
+ if (!entity->group) {
+ dev_err(dev, "%s: device selector unit missing group\n", entity->label);
+ return -EINVAL;
+ }
+
+ range = sdca_selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE,
+ SDCA_SELECTED_MODE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ (*widget)->id = snd_soc_dapm_mux;
+ (*widget)->kcontrol_news = entity->group->ge.kctl;
+ (*widget)->num_kcontrols = 1;
+ (*widget)++;
+
+ for (i = 0; i < entity->group->ge.num_modes; i++) {
+ struct sdca_ge_mode *mode = &entity->group->ge.modes[i];
+
+ for (j = 0; j < mode->num_controls; j++) {
+ struct sdca_ge_control *affected = &mode->controls[j];
+ int term;
+
+ if (affected->id != entity->id ||
+ affected->sel != SDCA_CTL_SU_SELECTOR ||
+ !affected->val)
+ continue;
+
+ if (affected->val - 1 >= entity->num_sources) {
+ dev_err(dev, "%s: bad control value: %#x\n",
+ entity->label, affected->val);
+ return -EINVAL;
+ }
+
+ if (++num_routes > entity->num_sources) {
+ dev_err(dev, "%s: too many input routes\n", entity->label);
+ return -EINVAL;
+ }
+
+ term = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
+ mode->val, SDCA_SELECTED_MODE_TERM_TYPE);
+ if (!term) {
+ dev_err(dev, "%s: mode not found: %#x\n",
+ entity->label, mode->val);
+ return -EINVAL;
+ }
+
+ add_route(route, entity->label, sdca_find_terminal_name(term),
+ entity->sources[affected->val - 1]->label);
+ }
+ }
+
+ return 0;
+}
+
+/* Class selector units will be exported as an ALSA control */
+static int entity_parse_su_class(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct snd_kcontrol_new *kctl;
+ struct soc_enum *soc_enum;
+ const char **texts;
+ int i;
+
+ kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL);
+ if (!kctl)
+ return -ENOMEM;
+
+ soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL);
+ if (!soc_enum)
+ return -ENOMEM;
+
+ texts = devm_kcalloc(dev, entity->num_sources + 1, sizeof(*texts), GFP_KERNEL);
+ if (!texts)
+ return -ENOMEM;
+
+ texts[0] = "No Signal";
+ for (i = 0; i < entity->num_sources; i++)
+ texts[i + 1] = entity->sources[i]->label;
+
+ soc_enum->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ soc_enum->items = entity->num_sources + 1;
+ soc_enum->mask = roundup_pow_of_two(soc_enum->items) - 1;
+ soc_enum->texts = texts;
+
+ kctl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl->name = "Route";
+ kctl->info = snd_soc_info_enum_double;
+ kctl->get = snd_soc_dapm_get_enum_double;
+ kctl->put = snd_soc_dapm_put_enum_double;
+ kctl->private_value = (unsigned long)soc_enum;
+
+ (*widget)->id = snd_soc_dapm_mux;
+ (*widget)->kcontrol_news = kctl;
+ (*widget)->num_kcontrols = 1;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, texts[i + 1], entity->sources[i]->label);
+
+ return 0;
+}
+
+static int entity_parse_su(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct sdca_control *control;
+
+ if (!entity->num_sources) {
+ dev_err(dev, "%s: selector with no inputs\n", entity->label);
+ return -EINVAL;
+ }
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR);
+ if (!control)
+ return -EINVAL;
+
+ if (control->layers == SDCA_ACCESS_LAYER_DEVICE)
+ return entity_parse_su_device(dev, function, entity, widget, route);
+
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ return entity_parse_su_class(dev, function, entity, control, widget, route);
+}
+
+static int entity_parse_mu(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct sdca_control *control;
+ struct snd_kcontrol_new *kctl;
+ int i;
+
+ if (!entity->num_sources) {
+ dev_err(dev, "%s: selector 1 or more inputs\n", entity->label);
+ return -EINVAL;
+ }
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_MU_MIXER);
+ if (!control)
+ return -EINVAL;
+
+ /* MU control should be through DAPM */
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ kctl = devm_kcalloc(dev, entity->num_sources, sizeof(*kctl), GFP_KERNEL);
+ if (!kctl)
+ return -ENOMEM;
+
+ for (i = 0; i < entity->num_sources; i++) {
+ const char *control_name;
+ struct soc_mixer_control *mc;
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %d",
+ control->label, i + 1);
+ if (!control_name)
+ return -ENOMEM;
+
+ mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ mc->reg = SND_SOC_NOPM;
+ mc->rreg = SND_SOC_NOPM;
+ mc->invert = 1; // Ensure default is connected
+ mc->min = 0;
+ mc->max = 1;
+
+ kctl[i].name = control_name;
+ kctl[i].private_value = (unsigned long)mc;
+ kctl[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl[i].info = snd_soc_info_volsw;
+ kctl[i].get = snd_soc_dapm_get_volsw;
+ kctl[i].put = snd_soc_dapm_put_volsw;
+ }
+
+ (*widget)->id = snd_soc_dapm_mixer;
+ (*widget)->kcontrol_news = kctl;
+ (*widget)->num_kcontrols = entity->num_sources;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, kctl[i].name, entity->sources[i]->label);
+
+ return 0;
+}
+
+static int entity_cs_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kctl, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct sdca_entity *entity = widget->priv;
+
+ if (!component)
+ return -EIO;
+
+ if (entity->cs.max_delay)
+ fsleep(entity->cs.max_delay);
+
+ return 0;
+}
+
+static int entity_parse_cs(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ int i;
+
+ (*widget)->id = snd_soc_dapm_supply;
+ (*widget)->subseq = 1; /* Ensure these run after PDEs */
+ (*widget)->event_flags = SND_SOC_DAPM_POST_PMU;
+ (*widget)->event = entity_cs_event;
+ (*widget)->priv = entity;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ return 0;
+}
+
+/**
+ * sdca_asoc_populate_dapm - fill in arrays of DAPM widgets and routes
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @widget: Array of DAPM widgets to be populated.
+ * @route: Array of DAPM routes to be populated.
+ *
+ * This function populates arrays of DAPM widgets and routes from the
+ * DisCo information for a particular SDCA Function. Typically,
+ * snd_soc_asoc_count_component will be used to allocate appropriately
+ * sized arrays before calling this function.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data *function,
+ struct snd_soc_dapm_widget *widget,
+ struct snd_soc_dapm_route *route)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ /*
+ * Some entities need to add controls "early" as they are
+ * referenced by other entities.
+ */
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_GE:
+ ret = entity_early_parse_ge(dev, function, entity);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ widget->name = entity->label;
+ widget->reg = SND_SOC_NOPM;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ ret = entity_parse_it(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ ret = entity_parse_ot(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ ret = entity_parse_pde(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_SU:
+ ret = entity_parse_su(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_MU:
+ ret = entity_parse_mu(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_CS:
+ ret = entity_parse_cs(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_CX:
+ /*
+ * FIXME: For now we will just treat these as a supply,
+ * meaning all options are enabled.
+ */
+ dev_warn(dev, "%s: clock selectors not fully supported yet\n",
+ entity->label);
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_supply);
+ break;
+ case SDCA_ENTITY_TYPE_TG:
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_siggen);
+ break;
+ case SDCA_ENTITY_TYPE_GE:
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_supply);
+ break;
+ default:
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_pga);
+ break;
+ }
+ if (ret)
+ return ret;
+
+ if (entity->group)
+ add_route(&route, entity->label, NULL, entity->group->label);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA");
+
+static int control_limit_kctl(struct device *dev,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct snd_kcontrol_new *kctl)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
+ struct sdca_control_range *range;
+ int min, max, step;
+ unsigned int *tlv;
+
+ if (control->type != SDCA_CTL_DATATYPE_Q7P8DB)
+ return 0;
+
+ /*
+ * FIXME: For now only handle the simple case of a single linear range
+ */
+ range = sdca_control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1);
+ if (!range)
+ return -EINVAL;
+
+ min = sdca_range(range, SDCA_VOLUME_LINEAR_MIN, 0);
+ max = sdca_range(range, SDCA_VOLUME_LINEAR_MAX, 0);
+ step = sdca_range(range, SDCA_VOLUME_LINEAR_STEP, 0);
+
+ min = sign_extend32(min, control->nbits - 1);
+ max = sign_extend32(max, control->nbits - 1);
+
+ tlv = devm_kcalloc(dev, 4, sizeof(*tlv), GFP_KERNEL);
+ if (!tlv)
+ return -ENOMEM;
+
+ tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
+ tlv[1] = 2 * sizeof(*tlv);
+ tlv[2] = (min * 100) >> 8;
+ tlv[3] = (max * 100) >> 8;
+
+ step = (step * 100) >> 8;
+
+ mc->min = ((int)tlv[2] / step);
+ mc->max = ((int)tlv[3] / step);
+ mc->shift = step;
+ mc->sign_bit = 15;
+ mc->sdca_q78 = 1;
+
+ kctl->tlv.p = tlv;
+ kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+
+ return 0;
+}
+
+static int populate_control(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct snd_kcontrol_new **kctl)
+{
+ const char *control_suffix = "";
+ const char *control_name;
+ struct soc_mixer_control *mc;
+ int index = 0;
+ int ret;
+ int cn;
+
+ if (!exported_control(entity, control))
+ return 0;
+
+ if (control->type == SDCA_CTL_DATATYPE_ONEBIT)
+ control_suffix = " Switch";
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s%s", entity->label,
+ control->label, control_suffix);
+ if (!control_name)
+ return -ENOMEM;
+
+ mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ for_each_set_bit(cn, (unsigned long *)&control->cn_list,
+ BITS_PER_TYPE(control->cn_list)) {
+ switch (index++) {
+ case 0:
+ mc->reg = SDW_SDCA_CTL(function->desc->adr, entity->id,
+ control->sel, cn);
+ mc->rreg = mc->reg;
+ break;
+ case 1:
+ mc->rreg = SDW_SDCA_CTL(function->desc->adr, entity->id,
+ control->sel, cn);
+ break;
+ default:
+ dev_err(dev, "%s: %s: only mono/stereo controls supported\n",
+ entity->label, control->label);
+ return -EINVAL;
+ }
+ }
+
+ mc->min = 0;
+ mc->max = clamp((0x1ull << control->nbits) - 1, 0, type_max(mc->max));
+
+ if (SDCA_CTL_TYPE(entity->type, control->sel) == SDCA_CTL_TYPE_S(FU, MUTE))
+ mc->invert = true;
+
+ (*kctl)->name = control_name;
+ (*kctl)->private_value = (unsigned long)mc;
+ (*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ (*kctl)->info = snd_soc_info_volsw;
+ (*kctl)->get = snd_soc_get_volsw;
+ (*kctl)->put = snd_soc_put_volsw;
+
+ if (readonly_control(control))
+ (*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READ;
+ else
+ (*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+
+ ret = control_limit_kctl(dev, entity, control, *kctl);
+ if (ret)
+ return ret;
+
+ (*kctl)++;
+
+ return 0;
+}
+
+static int populate_pin_switch(struct device *dev,
+ struct sdca_entity *entity,
+ struct snd_kcontrol_new **kctl)
+{
+ const char *control_name;
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s Switch", entity->label);
+ if (!control_name)
+ return -ENOMEM;
+
+ (*kctl)->name = control_name;
+ (*kctl)->private_value = (unsigned long)entity->label;
+ (*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ (*kctl)->info = snd_soc_dapm_info_pin_switch;
+ (*kctl)->get = snd_soc_dapm_get_component_pin_switch;
+ (*kctl)->put = snd_soc_dapm_put_component_pin_switch;
+ (*kctl)++;
+
+ return 0;
+}
+
+/**
+ * sdca_asoc_populate_controls - fill in an array of ALSA controls for a Function
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @kctl: Array of ALSA controls to be populated.
+ *
+ * This function populates an array of ALSA controls from the DisCo
+ * information for a particular SDCA Function. Typically,
+ * snd_soc_asoc_count_component will be used to allocate an
+ * appropriately sized array before calling this function.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_controls(struct device *dev,
+ struct sdca_function_data *function,
+ struct snd_kcontrol_new *kctl)
+{
+ int i, j;
+ int ret;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ if (!entity->iot.is_dataport) {
+ ret = populate_pin_switch(dev, entity, &kctl);
+ if (ret)
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ for (j = 0; j < entity->num_controls; j++) {
+ ret = populate_control(dev, function, entity,
+ &entity->controls[j], &kctl);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_controls, "SND_SOC_SDCA");
+
+static unsigned int rate_find_mask(unsigned int rate)
+{
+ switch (rate) {
+ case 0:
+ return SNDRV_PCM_RATE_8000_768000;
+ case 5512:
+ return SNDRV_PCM_RATE_5512;
+ case 8000:
+ return SNDRV_PCM_RATE_8000;
+ case 11025:
+ return SNDRV_PCM_RATE_11025;
+ case 16000:
+ return SNDRV_PCM_RATE_16000;
+ case 22050:
+ return SNDRV_PCM_RATE_22050;
+ case 32000:
+ return SNDRV_PCM_RATE_32000;
+ case 44100:
+ return SNDRV_PCM_RATE_44100;
+ case 48000:
+ return SNDRV_PCM_RATE_48000;
+ case 64000:
+ return SNDRV_PCM_RATE_64000;
+ case 88200:
+ return SNDRV_PCM_RATE_88200;
+ case 96000:
+ return SNDRV_PCM_RATE_96000;
+ case 176400:
+ return SNDRV_PCM_RATE_176400;
+ case 192000:
+ return SNDRV_PCM_RATE_192000;
+ case 352800:
+ return SNDRV_PCM_RATE_352800;
+ case 384000:
+ return SNDRV_PCM_RATE_384000;
+ case 705600:
+ return SNDRV_PCM_RATE_705600;
+ case 768000:
+ return SNDRV_PCM_RATE_768000;
+ case 12000:
+ return SNDRV_PCM_RATE_12000;
+ case 24000:
+ return SNDRV_PCM_RATE_24000;
+ case 128000:
+ return SNDRV_PCM_RATE_128000;
+ default:
+ return 0;
+ }
+}
+
+static u64 width_find_mask(unsigned int bits)
+{
+ switch (bits) {
+ case 0:
+ return SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE;
+ case 8:
+ return SNDRV_PCM_FMTBIT_S8;
+ case 16:
+ return SNDRV_PCM_FMTBIT_S16_LE;
+ case 20:
+ return SNDRV_PCM_FMTBIT_S20_LE;
+ case 24:
+ return SNDRV_PCM_FMTBIT_S24_LE;
+ case 32:
+ return SNDRV_PCM_FMTBIT_S32_LE;
+ default:
+ return 0;
+ }
+}
+
+static int populate_rate_format(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_pcm_stream *stream)
+{
+ struct sdca_control_range *range;
+ unsigned int sample_rate, sample_width;
+ unsigned int clock_rates = 0;
+ unsigned int rates = 0;
+ u64 formats = 0;
+ int sel, i;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ sel = SDCA_CTL_IT_USAGE;
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ sel = SDCA_CTL_OT_USAGE;
+ break;
+ default:
+ dev_err(dev, "%s: entity type has no usage control\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ if (entity->iot.clock) {
+ range = sdca_selector_find_range(dev, entity->iot.clock,
+ SDCA_CTL_CS_SAMPLERATEINDEX,
+ SDCA_SAMPLERATEINDEX_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ sample_rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i);
+ clock_rates |= rate_find_mask(sample_rate);
+ }
+ } else {
+ clock_rates = UINT_MAX;
+ }
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ sample_rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
+ sample_rate = rate_find_mask(sample_rate);
+
+ if (sample_rate & clock_rates) {
+ rates |= sample_rate;
+
+ sample_width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
+ formats |= width_find_mask(sample_width);
+ }
+ }
+
+ stream->formats = formats;
+ stream->rates = rates;
+
+ return 0;
+}
+
+/**
+ * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @dais: Array of DAI drivers to be populated.
+ * @ops: DAI ops to be attached to each of the created DAI drivers.
+ *
+ * This function populates an array of ASoC DAI drivers from the DisCo
+ * information for a particular SDCA Function. Typically,
+ * snd_soc_asoc_count_component will be used to allocate an
+ * appropriately sized array before calling this function.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *function,
+ struct snd_soc_dai_driver *dais,
+ const struct snd_soc_dai_ops *ops)
+{
+ int i, j;
+ int ret;
+
+ for (i = 0, j = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+ struct snd_soc_pcm_stream *stream;
+ const char *stream_suffix;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ stream = &dais[j].playback;
+ stream_suffix = "Playback";
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ stream = &dais[j].capture;
+ stream_suffix = "Capture";
+ break;
+ default:
+ continue;
+ }
+
+ /* Can't check earlier as only terminals have an iot member. */
+ if (!entity->iot.is_dataport)
+ continue;
+
+ stream->stream_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, stream_suffix);
+ if (!stream->stream_name)
+ return -ENOMEM;
+ /* Channels will be further limited by constraints */
+ stream->channels_min = 1;
+ stream->channels_max = SDCA_MAX_CHANNEL_COUNT;
+
+ ret = populate_rate_format(dev, function, entity, stream);
+ if (ret)
+ return ret;
+
+ dais[j].id = i;
+ dais[j].name = entity->label;
+ dais[j].ops = ops;
+ j++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_dais, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_populate_component - fill in a component driver for a Function
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @component_drv: Pointer to the component driver to be populated.
+ * @dai_drv: Pointer to the DAI driver array to be allocated and populated.
+ * @num_dai_drv: Pointer to integer that will be populated with the number of
+ * DAI drivers.
+ * @ops: DAI ops pointer that will be used for each DAI driver.
+ *
+ * This function populates a snd_soc_component_driver structure based
+ * on the DisCo information for a particular SDCA Function. It does
+ * all allocation internally.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_component(struct device *dev,
+ struct sdca_function_data *function,
+ struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver **dai_drv, int *num_dai_drv,
+ const struct snd_soc_dai_ops *ops)
+{
+ struct snd_soc_dapm_widget *widgets;
+ struct snd_soc_dapm_route *routes;
+ struct snd_kcontrol_new *controls;
+ struct snd_soc_dai_driver *dais;
+ int num_widgets, num_routes, num_controls, num_dais;
+ int ret;
+
+ ret = sdca_asoc_count_component(dev, function, &num_widgets, &num_routes,
+ &num_controls, &num_dais);
+ if (ret)
+ return ret;
+
+ widgets = devm_kcalloc(dev, num_widgets, sizeof(*widgets), GFP_KERNEL);
+ if (!widgets)
+ return -ENOMEM;
+
+ routes = devm_kcalloc(dev, num_routes, sizeof(*routes), GFP_KERNEL);
+ if (!routes)
+ return -ENOMEM;
+
+ controls = devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
+ if (!dais)
+ return -ENOMEM;
+
+ ret = sdca_asoc_populate_dapm(dev, function, widgets, routes);
+ if (ret)
+ return ret;
+
+ ret = sdca_asoc_populate_controls(dev, function, controls);
+ if (ret)
+ return ret;
+
+ ret = sdca_asoc_populate_dais(dev, function, dais, ops);
+ if (ret)
+ return ret;
+
+ component_drv->dapm_widgets = widgets;
+ component_drv->num_dapm_widgets = num_widgets;
+ component_drv->dapm_routes = routes;
+ component_drv->num_dapm_routes = num_routes;
+ component_drv->controls = controls;
+ component_drv->num_controls = num_controls;
+
+ *dai_drv = dais;
+ *num_dai_drv = num_dais;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_set_constraints - constrain channels available on a DAI
+ * @dev: Pointer to the device, used for error messages.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information.
+ * @substream: Pointer to the PCM substream.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from startup().
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ static const unsigned int channel_list[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ };
+ struct sdca_entity *entity = &function->entities[dai->id];
+ struct snd_pcm_hw_constraint_list *constraint;
+ struct sdca_control_range *range;
+ struct sdca_control *control;
+ unsigned int channel_mask = 0;
+ int i, ret;
+
+ static_assert(ARRAY_SIZE(channel_list) == SDCA_MAX_CHANNEL_COUNT);
+ static_assert(sizeof(channel_mask) * BITS_PER_BYTE >= SDCA_MAX_CHANNEL_COUNT);
+
+ if (entity->type != SDCA_ENTITY_TYPE_IT)
+ return 0;
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_IT_CLUSTERINDEX);
+ if (!control)
+ return -EINVAL;
+
+ range = sdca_control_find_range(dev, entity, control, SDCA_CLUSTER_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ int clusterid = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i);
+ struct sdca_cluster *cluster;
+
+ cluster = sdca_id_find_cluster(dev, function, clusterid);
+ if (!cluster)
+ return -ENODEV;
+
+ channel_mask |= (1 << (cluster->num_channels - 1));
+ }
+
+ dev_dbg(dev, "%s: set channel constraint mask: %#x\n",
+ entity->label, channel_mask);
+
+ constraint = kzalloc(sizeof(*constraint), GFP_KERNEL);
+ if (!constraint)
+ return -ENOMEM;
+
+ constraint->count = ARRAY_SIZE(channel_list);
+ constraint->list = channel_list;
+ constraint->mask = channel_mask;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ constraint);
+ if (ret) {
+ dev_err(dev, "%s: failed to add constraint: %d\n", entity->label, ret);
+ kfree(constraint);
+ return ret;
+ }
+
+ dai->priv = constraint;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_set_constraints, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_free_constraints - free constraint allocations
+ * @substream: Pointer to the PCM substream.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from shutdown().
+ */
+void sdca_asoc_free_constraints(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_hw_constraint_list *constraint = dai->priv;
+
+ kfree(constraint);
+}
+EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_get_port - return SoundWire port for a DAI
+ * @dev: Pointer to the device, used for error messages.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from hw_params().
+ *
+ * Return: Returns a positive port number on success, and a negative error
+ * code on failure.
+ */
+int sdca_asoc_get_port(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct snd_soc_dai *dai)
+{
+ struct sdca_entity *entity = &function->entities[dai->id];
+ struct sdca_control_range *range;
+ unsigned int reg, val;
+ int sel = -EINVAL;
+ int i, ret;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ sel = SDCA_CTL_IT_DATAPORT_SELECTOR;
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ sel = SDCA_CTL_OT_DATAPORT_SELECTOR;
+ break;
+ default:
+ break;
+ }
+
+ if (sel < 0 || !entity->iot.is_dataport) {
+ dev_err(dev, "%s: port number only available for dataports\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_DATAPORT_SELECTOR_NCOLS,
+ SDCA_DATAPORT_SELECTOR_NROWS);
+ if (!range)
+ return -EINVAL;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0);
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "%s: failed to read dataport selector: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ for (i = 0; i < range->rows; i++) {
+ static const u8 port_mask = 0xF;
+
+ sel = sdca_range(range, val & port_mask, i);
+
+ /*
+ * FIXME: Currently only a single dataport is supported, so
+ * return the first one found, technically up to 4 dataports
+ * could be linked, but this is not yet supported.
+ */
+ if (sel != 0xFF)
+ return sel;
+
+ val >>= hweight8(port_mask);
+ }
+
+ dev_err(dev, "%s: no dataport found\n", entity->label);
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA");
+
+static int set_cluster(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity, unsigned int channels)
+{
+ int sel = SDCA_CTL_IT_CLUSTERINDEX;
+ struct sdca_control_range *range;
+ int i, ret;
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_CLUSTER_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ int cluster_id = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i);
+ struct sdca_cluster *cluster;
+
+ cluster = sdca_id_find_cluster(dev, function, cluster_id);
+ if (!cluster)
+ return -ENODEV;
+
+ if (cluster->num_channels == channels) {
+ int index = sdca_range(range, SDCA_CLUSTER_BYTEINDEX, i);
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id, sel, 0);
+
+ ret = regmap_update_bits(regmap, reg, 0xFF, index);
+ if (ret) {
+ dev_err(dev, "%s: failed to write cluster index: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: set cluster to %d (%d channels)\n",
+ entity->label, index, channels);
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "%s: no cluster for %d channels\n", entity->label, channels);
+ return -EINVAL;
+}
+
+static int set_clock(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity, int target_rate)
+{
+ int sel = SDCA_CTL_CS_SAMPLERATEINDEX;
+ struct sdca_control_range *range;
+ int i, ret;
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_SAMPLERATEINDEX_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ unsigned int rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i);
+
+ if (rate == target_rate) {
+ unsigned int index = sdca_range(range,
+ SDCA_SAMPLERATEINDEX_INDEX,
+ i);
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id, sel, 0);
+
+ ret = regmap_update_bits(regmap, reg, 0xFF, index);
+ if (ret) {
+ dev_err(dev, "%s: failed to write clock rate: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: set clock rate to %d (%dHz)\n",
+ entity->label, index, rate);
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "%s: no clock rate for %dHz\n", entity->label, target_rate);
+ return -EINVAL;
+}
+
+static int set_usage(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity, int sel,
+ int target_rate, int target_width)
+{
+ struct sdca_control_range *range;
+ int i, ret;
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
+ unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
+
+ if ((!rate || rate == target_rate) && width == target_width) {
+ unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i);
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id, sel, 0);
+
+ ret = regmap_update_bits(regmap, reg, 0xFF, usage);
+ if (ret) {
+ dev_err(dev, "%s: failed to write usage: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: set usage to %#x (%dHz, %d bits)\n",
+ entity->label, usage, target_rate, target_width);
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "%s: no usage for %dHz, %dbits\n",
+ entity->label, target_rate, target_width);
+ return -EINVAL;
+}
+
+/**
+ * sdca_asoc_hw_params - set SDCA channels, sample rate and bit depth
+ * @dev: Pointer to the device, used for error messages.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information.
+ * @substream: Pointer to the PCM substream.
+ * @params: Pointer to the hardware parameters.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from hw_params().
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sdca_entity *entity = &function->entities[dai->id];
+ int channels = params_channels(params);
+ int width = params_width(params);
+ int rate = params_rate(params);
+ int usage_sel;
+ int ret;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ ret = set_cluster(dev, regmap, function, entity, channels);
+ if (ret)
+ return ret;
+
+ usage_sel = SDCA_CTL_IT_USAGE;
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ usage_sel = SDCA_CTL_OT_USAGE;
+ break;
+ default:
+ dev_err(dev, "%s: hw_params on non-terminal entity\n", entity->label);
+ return -EINVAL;
+ }
+
+ if (entity->iot.clock) {
+ ret = set_clock(dev, regmap, function, entity->iot.clock, rate);
+ if (ret)
+ return ret;
+ }
+
+ ret = set_usage(dev, regmap, function, entity, usage_sel, rate, width);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_hw_params, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
new file mode 100644
index 000000000000..349d32933ba8
--- /dev/null
+++ b/sound/soc/sdca/sdca_class.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_regmap.h>
+#include "sdca_class.h"
+
+#define CLASS_SDW_ATTACH_TIMEOUT_MS 5000
+
+static int class_read_prop(struct sdw_slave *sdw)
+{
+ struct sdw_slave_prop *prop = &sdw->prop;
+
+ sdw_slave_read_prop(sdw);
+
+ prop->use_domain_irq = true;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
+ SDW_SCP_INT1_IMPL_DEF;
+
+ return 0;
+}
+
+static int class_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(&sdw->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(drv->dev, "device attach\n");
+
+ drv->attached = true;
+
+ complete(&drv->device_attach);
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(drv->dev, "device detach\n");
+
+ drv->attached = false;
+
+ reinit_completion(&drv->device_attach);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct sdw_slave_ops class_sdw_ops = {
+ .read_prop = class_read_prop,
+ .update_status = class_sdw_update_status,
+};
+
+static void class_regmap_lock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_lock(lock);
+}
+
+static void class_regmap_unlock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_unlock(lock);
+}
+
+static int class_wait_for_attach(struct sdca_class_drv *drv)
+{
+ if (!drv->attached) {
+ unsigned long timeout = msecs_to_jiffies(CLASS_SDW_ATTACH_TIMEOUT_MS);
+ unsigned long time;
+
+ time = wait_for_completion_timeout(&drv->device_attach, timeout);
+ if (!time) {
+ dev_err(drv->dev, "timed out waiting for device re-attach\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ regcache_cache_only(drv->dev_regmap, false);
+
+ return 0;
+}
+
+static bool class_dev_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool class_dev_regmap_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INT4:
+ case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config class_dev_regmap_config = {
+ .name = "sdca-device",
+ .reg_bits = 32,
+ .val_bits = 8,
+
+ .max_register = SDW_SDCA_MAX_REGISTER,
+ .volatile_reg = class_dev_regmap_volatile,
+ .precious_reg = class_dev_regmap_precious,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .lock = class_regmap_lock,
+ .unlock = class_regmap_unlock,
+};
+
+static void class_boot_work(struct work_struct *work)
+{
+ struct sdca_class_drv *drv = container_of(work,
+ struct sdca_class_drv,
+ boot_work);
+ int ret;
+
+ ret = class_wait_for_attach(drv);
+ if (ret)
+ goto err;
+
+ drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap,
+ drv->sdw->irq);
+ if (IS_ERR(drv->irq_info))
+ goto err;
+
+ ret = sdca_dev_register_functions(drv->sdw);
+ if (ret)
+ goto err;
+
+ dev_dbg(drv->dev, "boot work complete\n");
+
+ pm_runtime_mark_last_busy(drv->dev);
+ pm_runtime_put_autosuspend(drv->dev);
+
+ return;
+
+err:
+ pm_runtime_put_sync(drv->dev);
+}
+
+static void class_dev_remove(void *data)
+{
+ struct sdca_class_drv *drv = data;
+
+ cancel_work_sync(&drv->boot_work);
+
+ sdca_dev_unregister_functions(drv->sdw);
+}
+
+static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
+{
+ struct device *dev = &sdw->dev;
+ struct sdca_device_data *data = &sdw->sdca_data;
+ struct regmap_config *dev_config;
+ struct sdca_class_drv *drv;
+ int ret;
+
+ sdca_lookup_swft(sdw);
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ dev_config = devm_kmemdup(dev, &class_dev_regmap_config,
+ sizeof(*dev_config), GFP_KERNEL);
+ if (!dev_config)
+ return -ENOMEM;
+
+ drv->functions = devm_kcalloc(dev, data->num_functions,
+ sizeof(*drv->functions),
+ GFP_KERNEL);
+ if (!drv->functions)
+ return -ENOMEM;
+
+ drv->dev = dev;
+ drv->sdw = sdw;
+ mutex_init(&drv->regmap_lock);
+
+ dev_set_drvdata(drv->dev, drv);
+
+ INIT_WORK(&drv->boot_work, class_boot_work);
+ init_completion(&drv->device_attach);
+
+ dev_config->lock_arg = &drv->regmap_lock;
+
+ drv->dev_regmap = devm_regmap_init_sdw(sdw, dev_config);
+ if (IS_ERR(drv->dev_regmap))
+ return dev_err_probe(drv->dev, PTR_ERR(drv->dev_regmap),
+ "failed to create device regmap\n");
+
+ regcache_cache_only(drv->dev_regmap, true);
+
+ pm_runtime_set_autosuspend_delay(dev, 250);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, class_dev_remove, drv);
+ if (ret)
+ return ret;
+
+ queue_work(system_long_wq, &drv->boot_work);
+
+ return 0;
+}
+
+static int class_runtime_suspend(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+
+ /*
+ * Whilst the driver doesn't power the chip down here, going into runtime
+ * suspend lets the SoundWire bus power down, which means the driver
+ * can't communicate with the device any more.
+ */
+ regcache_cache_only(drv->dev_regmap, true);
+
+ return 0;
+}
+
+static int class_runtime_resume(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = class_wait_for_attach(drv);
+ if (ret)
+ goto err;
+
+ regcache_mark_dirty(drv->dev_regmap);
+
+ ret = regcache_sync(drv->dev_regmap);
+ if (ret) {
+ dev_err(drv->dev, "failed to restore cache: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(drv->dev_regmap, true);
+
+ return ret;
+}
+
+static const struct dev_pm_ops class_pm_ops = {
+ RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id class_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x4245, 0),
+ {}
+};
+MODULE_DEVICE_TABLE(sdw, class_sdw_id);
+
+static struct sdw_driver class_sdw_driver = {
+ .driver = {
+ .name = "sdca_class",
+ .pm = pm_ptr(&class_pm_ops),
+ },
+
+ .probe = class_sdw_probe,
+ .id_table = class_sdw_id,
+ .ops = &class_sdw_ops,
+};
+module_sdw_driver(class_sdw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SDCA Class Driver");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h
new file mode 100644
index 000000000000..bb4c9dd12429
--- /dev/null
+++ b/sound/soc/sdca/sdca_class.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright (C) 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __SDCA_CLASS_H__
+#define __SDCA_CLASS_H__
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+struct device;
+struct regmap;
+struct sdw_slave;
+struct sdca_function_data;
+
+struct sdca_class_drv {
+ struct device *dev;
+ struct regmap *dev_regmap;
+ struct sdw_slave *sdw;
+
+ struct sdca_function_data *functions;
+ struct sdca_interrupt_info *irq_info;
+
+ struct mutex regmap_lock;
+ struct work_struct boot_work;
+ struct completion device_attach;
+
+ bool attached;
+};
+
+#endif /* __SDCA_CLASS_H__ */
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
new file mode 100644
index 000000000000..0028482a1e75
--- /dev/null
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/pcm.h>
+#include <sound/sdca_asoc.h>
+#include <sound/sdca_fdl.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_regmap.h>
+#include <sound/sdw.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+#include "sdca_class.h"
+
+struct class_function_drv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct sdca_class_drv *core;
+
+ struct sdca_function_data *function;
+};
+
+static void class_function_regmap_lock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_lock(lock);
+}
+
+static void class_function_regmap_unlock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_unlock(lock);
+}
+
+static bool class_function_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_writeable(drv->function, reg);
+}
+
+static bool class_function_regmap_readable(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_readable(drv->function, reg);
+}
+
+static bool class_function_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_volatile(drv->function, reg);
+}
+
+static const struct regmap_config class_function_regmap_config = {
+ .name = "sdca",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+
+ .max_register = SDW_SDCA_MAX_REGISTER,
+ .readable_reg = class_function_regmap_readable,
+ .writeable_reg = class_function_regmap_writeable,
+ .volatile_reg = class_function_regmap_volatile,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .lock = class_function_regmap_lock,
+ .unlock = class_function_regmap_unlock,
+};
+
+static int class_function_regmap_mbq_size(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_mbq_size(drv->function, reg);
+}
+
+static bool class_function_regmap_deferrable(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_deferrable(drv->function, reg);
+}
+
+static const struct regmap_sdw_mbq_cfg class_function_mbq_config = {
+ .mbq_size = class_function_regmap_mbq_size,
+ .deferrable = class_function_regmap_deferrable,
+ .retry_us = 1000,
+ .timeout_us = 10000,
+};
+
+static int class_function_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);
+
+ return sdca_asoc_set_constraints(drv->dev, drv->regmap, drv->function,
+ substream, dai);
+}
+
+static int class_function_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(drv->dev->parent);
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &sconfig, &pconfig);
+
+ /*
+ * FIXME: As also noted in sdca_asoc_get_port(), currently only
+ * a single unshared port is supported for each DAI.
+ */
+ ret = sdca_asoc_get_port(drv->dev, drv->regmap, drv->function, dai);
+ if (ret < 0)
+ return ret;
+
+ pconfig.num = ret;
+
+ ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream);
+ if (ret) {
+ dev_err(drv->dev, "failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return sdca_asoc_hw_params(drv->dev, drv->regmap, drv->function,
+ substream, params, dai);
+}
+
+static int class_function_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(drv->dev->parent);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ return sdw_stream_remove_slave(sdw, sdw_stream);
+}
+
+static int class_function_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops class_function_sdw_ops = {
+ .startup = class_function_startup,
+ .shutdown = sdca_asoc_free_constraints,
+ .set_stream = class_function_sdw_set_stream,
+ .hw_params = class_function_sdw_add_peripheral,
+ .hw_free = class_function_sdw_remove_peripheral,
+};
+
+static int class_function_component_probe(struct snd_soc_component *component)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
+ struct sdca_class_drv *core = drv->core;
+
+ return sdca_irq_populate(drv->function, component, core->irq_info);
+}
+
+static const struct snd_soc_component_driver class_function_component_drv = {
+ .probe = class_function_component_probe,
+ .endianness = 1,
+};
+
+static int class_function_boot(struct class_function_drv *drv)
+{
+ unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(drv->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to read function status: %d\n", ret);
+ return ret;
+ }
+
+ if (!(val & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {
+ dev_dbg(drv->dev, "reset function device\n");
+
+ ret = sdca_reset_function(drv->dev, drv->function, drv->regmap);
+ if (ret)
+ return ret;
+ }
+
+ if (val & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {
+ dev_dbg(drv->dev, "write initialisation\n");
+
+ ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap,
+ drv->function);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(drv->regmap, reg,
+ SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION);
+ if (ret < 0) {
+ dev_err(drv->dev,
+ "failed to clear function init status: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* Start FDL process */
+ ret = sdca_irq_populate_early(drv->dev, drv->regmap, drv->function,
+ drv->core->irq_info);
+ if (ret)
+ return ret;
+
+ ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info);
+ if (ret)
+ return ret;
+
+ ret = sdca_regmap_write_defaults(drv->dev, drv->regmap, drv->function);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(drv->regmap, reg, 0xFF);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to clear function status: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int class_function_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *aux_dev_id)
+{
+ struct device *dev = &auxdev->dev;
+ struct sdca_class_drv *core = dev_get_drvdata(dev->parent);
+ struct sdca_device_data *data = &core->sdw->sdca_data;
+ struct sdca_function_desc *desc;
+ struct snd_soc_component_driver *cmp_drv;
+ struct snd_soc_dai_driver *dais;
+ struct class_function_drv *drv;
+ struct regmap_sdw_mbq_cfg *mbq_config;
+ struct regmap_config *config;
+ struct reg_default *defaults;
+ int ndefaults;
+ int num_dais;
+ int ret;
+ int i;
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ cmp_drv = devm_kmemdup(dev, &class_function_component_drv, sizeof(*cmp_drv),
+ GFP_KERNEL);
+ if (!cmp_drv)
+ return -ENOMEM;
+
+ config = devm_kmemdup(dev, &class_function_regmap_config, sizeof(*config),
+ GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ mbq_config = devm_kmemdup(dev, &class_function_mbq_config, sizeof(*mbq_config),
+ GFP_KERNEL);
+ if (!mbq_config)
+ return -ENOMEM;
+
+ drv->dev = dev;
+ drv->core = core;
+
+ for (i = 0; i < data->num_functions; i++) {
+ desc = &data->function[i];
+
+ if (desc->type == aux_dev_id->driver_data)
+ break;
+ }
+ if (i == core->sdw->sdca_data.num_functions) {
+ dev_err(dev, "failed to locate function\n");
+ return -EINVAL;
+ }
+
+ drv->function = &core->functions[i];
+
+ ret = sdca_parse_function(dev, core->sdw, desc, drv->function);
+ if (ret)
+ return ret;
+
+ ndefaults = sdca_regmap_count_constants(dev, drv->function);
+ if (ndefaults < 0)
+ return ndefaults;
+
+ defaults = devm_kcalloc(dev, ndefaults, sizeof(*defaults), GFP_KERNEL);
+ if (!defaults)
+ return -ENOMEM;
+
+ ret = sdca_regmap_populate_constants(dev, drv->function, defaults);
+ if (ret < 0)
+ return ret;
+
+ regcache_sort_defaults(defaults, ndefaults);
+
+ auxiliary_set_drvdata(auxdev, drv);
+
+ config->reg_defaults = defaults;
+ config->num_reg_defaults = ndefaults;
+ config->lock_arg = &core->regmap_lock;
+
+ if (drv->function->busy_max_delay) {
+ mbq_config->timeout_us = drv->function->busy_max_delay;
+ mbq_config->retry_us = umax(drv->function->busy_max_delay / 10,
+ mbq_config->retry_us);
+ }
+
+ drv->regmap = devm_regmap_init_sdw_mbq_cfg(dev, core->sdw, config, mbq_config);
+ if (IS_ERR(drv->regmap))
+ return dev_err_probe(dev, PTR_ERR(drv->regmap),
+ "failed to create regmap");
+
+ ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv,
+ &dais, &num_dais,
+ &class_function_sdw_ops);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 200);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = class_function_boot(drv);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register component\n");
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int class_function_runtime_suspend(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ /*
+ * Whilst the driver doesn't power the chip down here, going into
+ * runtime suspend means the driver can't be sure the bus won't
+ * power down which would prevent communication with the device.
+ */
+ regcache_cache_only(drv->regmap, true);
+
+ return 0;
+}
+
+static int class_function_runtime_resume(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+ int ret;
+
+ regcache_mark_dirty(drv->regmap);
+ regcache_cache_only(drv->regmap, false);
+
+ ret = regcache_sync(drv->regmap);
+ if (ret) {
+ dev_err(drv->dev, "failed to restore register cache: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(drv->regmap, true);
+
+ return ret;
+}
+
+static const struct dev_pm_ops class_function_pm_ops = {
+ RUNTIME_PM_OPS(class_function_runtime_suspend,
+ class_function_runtime_resume, NULL)
+};
+
+static const struct auxiliary_device_id class_function_id_table[] = {
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SMART_AMP_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_SMART_AMP,
+ },
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SMART_MIC_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_SMART_MIC,
+ },
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_UAJ_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_UAJ,
+ },
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_HID_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_HID,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, class_function_id_table);
+
+static struct auxiliary_driver class_function_drv = {
+ .driver = {
+ .name = "sdca_function",
+ .pm = pm_ptr(&class_function_pm_ops),
+ },
+
+ .probe = class_function_probe,
+ .id_table = class_function_id_table
+};
+module_auxiliary_driver(class_function_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SDCA Class Function Driver");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c
new file mode 100644
index 000000000000..405e80b979de
--- /dev/null
+++ b/sound/soc/sdca/sdca_device.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2024 Intel Corporation
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+
+void sdca_lookup_interface_revision(struct sdw_slave *slave)
+{
+ struct fwnode_handle *fwnode = slave->dev.fwnode;
+
+ /*
+ * if this property is not present, then the sdca_interface_revision will
+ * remain zero, which will be considered as 'not defined' or 'invalid'.
+ */
+ fwnode_property_read_u32(fwnode, "mipi-sdw-sdca-interface-revision",
+ &slave->sdca_data.interface_revision);
+}
+EXPORT_SYMBOL_NS(sdca_lookup_interface_revision, "SND_SOC_SDCA");
+
+static void devm_acpi_table_put(void *ptr)
+{
+ acpi_put_table((struct acpi_table_header *)ptr);
+}
+
+void sdca_lookup_swft(struct sdw_slave *slave)
+{
+ acpi_status status;
+
+ status = acpi_get_table(ACPI_SIG_SWFT, 0,
+ (struct acpi_table_header **)&slave->sdca_data.swft);
+ if (ACPI_FAILURE(status))
+ dev_info(&slave->dev, "SWFT not available\n");
+ else
+ devm_add_action_or_reset(&slave->dev, devm_acpi_table_put,
+ &slave->sdca_data.swft);
+}
+EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA");
+
+static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave)
+{
+ struct sdw_slave_id *id = &slave->id;
+ int i;
+
+ /*
+ * The RT712_VA relies on the v06r04 draft, and the
+ * RT712_VB on a more recent v08r01 draft.
+ */
+ if (slave->sdca_data.interface_revision < 0x0801)
+ return false;
+
+ if (id->mfg_id != 0x025d)
+ return false;
+
+ if (id->part_id != 0x712 &&
+ id->part_id != 0x713 &&
+ id->part_id != 0x716 &&
+ id->part_id != 0x717)
+ return false;
+
+ for (i = 0; i < slave->sdca_data.num_functions; i++) {
+ if (slave->sdca_data.function[i].type == SDCA_FUNCTION_TYPE_SMART_MIC)
+ return true;
+ }
+
+ return false;
+}
+
+static bool sdca_device_quirk_skip_func_type_patching(struct sdw_slave *slave)
+{
+ const char *vendor, *sku;
+
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ sku = dmi_get_system_info(DMI_PRODUCT_SKU);
+
+ if (vendor && sku &&
+ !strcmp(vendor, "Dell Inc.") &&
+ (!strcmp(sku, "0C62") || !strcmp(sku, "0C63") || !strcmp(sku, "0C6B")) &&
+ slave->sdca_data.interface_revision == 0x061c &&
+ slave->id.mfg_id == 0x01fa && slave->id.part_id == 0x4243)
+ return true;
+
+ return false;
+}
+
+bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk)
+{
+ switch (quirk) {
+ case SDCA_QUIRKS_RT712_VB:
+ return sdca_device_quirk_rt712_vb(slave);
+ case SDCA_QUIRKS_SKIP_FUNC_TYPE_PATCHING:
+ return sdca_device_quirk_skip_func_type_patching(slave);
+ default:
+ break;
+ }
+ return false;
+}
+EXPORT_SYMBOL_NS(sdca_device_quirk_match, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
new file mode 100644
index 000000000000..3180ebd07c40
--- /dev/null
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/sprintf.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/sdca.h>
+#include <sound/sdca_fdl.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_ump.h>
+
+/**
+ * sdca_reset_function - send an SDCA function reset
+ * @dev: Device pointer for error messages.
+ * @function: Pointer to the SDCA Function.
+ * @regmap: Pointer to the SDCA Function regmap.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
+ struct regmap *regmap)
+{
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_ACTION, 0);
+ unsigned int val, poll_us;
+ int ret;
+
+ ret = regmap_write(regmap, reg, SDCA_CTL_ENTITY_0_RESET_FUNCTION_NOW);
+ if (ret) // Allowed for function reset to not be implemented
+ return 0;
+
+ if (!function->reset_max_delay) {
+ dev_err(dev, "No reset delay specified in DisCo\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Poll up to 16 times but no more than once per ms, these are just
+ * arbitrarily selected values, so may be fine tuned in future.
+ */
+ poll_us = umin(function->reset_max_delay >> 4, 1000);
+
+ ret = regmap_read_poll_timeout(regmap, reg, val, !val, poll_us,
+ function->reset_max_delay);
+ if (ret) {
+ dev_err(dev, "Failed waiting for function reset: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_reset_function, "SND_SOC_SDCA");
+
+/**
+ * sdca_fdl_sync - wait for a function to finish FDL
+ * @dev: Device pointer for error messages.
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ static const int fdl_retries = 6;
+ unsigned long begin_timeout = msecs_to_jiffies(100);
+ unsigned long done_timeout = msecs_to_jiffies(4000);
+ int nfdl;
+ int i, j;
+
+ for (i = 0; i < fdl_retries; i++) {
+ nfdl = 0;
+
+ for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) {
+ struct sdca_interrupt *interrupt = &info->irqs[j];
+ struct fdl_state *fdl_state;
+ unsigned long time;
+
+ if (interrupt->function != function ||
+ !interrupt->entity || !interrupt->control ||
+ interrupt->entity->type != SDCA_ENTITY_TYPE_XU ||
+ interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER)
+ continue;
+
+ fdl_state = interrupt->priv;
+ nfdl++;
+
+ /*
+ * Looking for timeout without any new FDL requests
+ * to imply the device has completed initial
+ * firmware setup. Alas the specification doesn't
+ * have any mechanism to detect this.
+ */
+ time = wait_for_completion_timeout(&fdl_state->begin,
+ begin_timeout);
+ if (!time) {
+ dev_dbg(dev, "no new FDL starts\n");
+ nfdl--;
+ continue;
+ }
+
+ time = wait_for_completion_timeout(&fdl_state->done,
+ done_timeout);
+ if (!time) {
+ dev_err(dev, "timed out waiting for FDL to complete\n");
+ goto error;
+ }
+ }
+
+ if (!nfdl)
+ return 0;
+ }
+
+ dev_err(dev, "too many FDL requests\n");
+
+error:
+ for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) {
+ struct sdca_interrupt *interrupt = &info->irqs[j];
+ struct fdl_state *fdl_state;
+
+ if (interrupt->function != function ||
+ !interrupt->entity || !interrupt->control ||
+ interrupt->entity->type != SDCA_ENTITY_TYPE_XU ||
+ interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER)
+ continue;
+
+ disable_irq(interrupt->irq);
+
+ fdl_state = interrupt->priv;
+
+ sdca_ump_cancel_timeout(&fdl_state->timeout);
+ }
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA");
+
+static char *fdl_get_sku_filename(struct device *dev,
+ struct sdca_fdl_file *fdl_file)
+{
+ struct device *parent = dev;
+ const char *product_vendor;
+ const char *product_sku;
+
+ /*
+ * Try to find pci_dev manually because the card may not be ready to be
+ * used for snd_soc_card_get_pci_ssid yet
+ */
+ while (parent) {
+ if (dev_is_pci(parent)) {
+ struct pci_dev *pci_dev = to_pci_dev(parent);
+
+ return kasprintf(GFP_KERNEL, "sdca/%x/%x/%x/%x.bin",
+ fdl_file->vendor_id,
+ pci_dev->subsystem_vendor,
+ pci_dev->subsystem_device,
+ fdl_file->file_id);
+ } else {
+ parent = parent->parent;
+ }
+ }
+
+ product_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ if (!product_vendor || !strcmp(product_vendor, "Default string"))
+ product_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ if (!product_vendor || !strcmp(product_vendor, "Default string"))
+ product_vendor = dmi_get_system_info(DMI_CHASSIS_VENDOR);
+ if (!product_vendor)
+ product_vendor = "unknown";
+
+ product_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
+ if (!product_sku || !strcmp(product_sku, "Default string"))
+ product_sku = dmi_get_system_info(DMI_PRODUCT_NAME);
+ if (!product_sku)
+ product_sku = "unknown";
+
+ return kasprintf(GFP_KERNEL, "sdca/%x/%s/%s/%x.bin", fdl_file->vendor_id,
+ product_vendor, product_sku, fdl_file->file_id);
+}
+
+static int fdl_load_file(struct sdca_interrupt *interrupt,
+ struct sdca_fdl_set *set, int file_index)
+{
+ struct device *dev = interrupt->dev;
+ struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data;
+ const struct firmware *firmware = NULL;
+ struct acpi_sw_file *swf = NULL, *tmp;
+ struct sdca_fdl_file *fdl_file;
+ char *disk_filename;
+ int ret;
+ int i;
+
+ if (!set) {
+ dev_err(dev, "request to load SWF with no set\n");
+ return -EINVAL;
+ }
+
+ fdl_file = &set->files[file_index];
+
+ if (fdl_data->swft) {
+ tmp = fdl_data->swft->files;
+ for (i = 0; i < fdl_data->swft->header.length; i += tmp->file_length,
+ tmp = ACPI_ADD_PTR(struct acpi_sw_file, tmp, tmp->file_length)) {
+ if (tmp->vendor_id == fdl_file->vendor_id &&
+ tmp->file_id == fdl_file->file_id) {
+ dev_dbg(dev, "located SWF in ACPI: %x-%x-%x\n",
+ tmp->vendor_id, tmp->file_id,
+ tmp->file_version);
+ swf = tmp;
+ break;
+ }
+ }
+ }
+
+ disk_filename = fdl_get_sku_filename(dev, fdl_file);
+ if (!disk_filename)
+ return -ENOMEM;
+
+ dev_dbg(dev, "FDL disk filename: %s\n", disk_filename);
+
+ ret = firmware_request_nowarn(&firmware, disk_filename, dev);
+ kfree(disk_filename);
+ if (ret) {
+ disk_filename = kasprintf(GFP_KERNEL, "sdca/%x/%x.bin",
+ fdl_file->vendor_id, fdl_file->file_id);
+ if (!disk_filename)
+ return -ENOMEM;
+
+ dev_dbg(dev, "FDL disk filename: %s\n", disk_filename);
+
+ ret = firmware_request_nowarn(&firmware, disk_filename, dev);
+ kfree(disk_filename);
+ }
+
+ if (!ret) {
+ tmp = (struct acpi_sw_file *)&firmware->data[0];
+
+ if (firmware->size < sizeof(*tmp) ||
+ tmp->file_length != firmware->size) {
+ dev_err(dev, "bad disk SWF size\n");
+ } else if (!swf || swf->file_version <= tmp->file_version) {
+ dev_dbg(dev, "using SWF from disk: %x-%x-%x\n",
+ tmp->vendor_id, tmp->file_id, tmp->file_version);
+ swf = tmp;
+ }
+ }
+
+ if (!swf) {
+ dev_err(dev, "failed to locate SWF\n");
+ return -ENOENT;
+ }
+
+ ret = sdca_ump_write_message(dev, interrupt->device_regmap,
+ interrupt->function_regmap,
+ interrupt->function, interrupt->entity,
+ SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset,
+ SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data,
+ swf->file_length - offsetof(struct acpi_sw_file, data));
+ release_firmware(firmware);
+ return ret;
+}
+
+static struct sdca_fdl_set *fdl_get_set(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data;
+ struct sdca_entity *xu = interrupt->entity;
+ struct sdca_control_range *range;
+ unsigned int val;
+ int i, ret;
+
+ ret = regmap_read(interrupt->function_regmap,
+ SDW_SDCA_CTL(interrupt->function->desc->adr, xu->id,
+ SDCA_CTL_XU_FDL_SET_INDEX, 0),
+ &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FDL set index: %d\n", ret);
+ return NULL;
+ }
+
+ range = sdca_selector_find_range(dev, xu, SDCA_CTL_XU_FDL_SET_INDEX,
+ SDCA_FDL_SET_INDEX_NCOLS, 0);
+
+ val = sdca_range_search(range, SDCA_FDL_SET_INDEX_SET_NUMBER,
+ val, SDCA_FDL_SET_INDEX_FILE_SET_ID);
+
+ for (i = 0; i < fdl_data->num_sets; i++) {
+ if (fdl_data->sets[i].id == val)
+ return &fdl_data->sets[i];
+ }
+
+ dev_err(dev, "invalid fileset id: %d\n", val);
+ return NULL;
+}
+
+static void fdl_end(struct sdca_interrupt *interrupt)
+{
+ struct fdl_state *fdl_state = interrupt->priv;
+
+ if (!fdl_state->set)
+ return;
+
+ fdl_state->set = NULL;
+
+ pm_runtime_put(interrupt->dev);
+ complete(&fdl_state->done);
+
+ dev_dbg(interrupt->dev, "completed FDL process\n");
+}
+
+static void sdca_fdl_timeout_work(struct work_struct *work)
+{
+ struct fdl_state *fdl_state = container_of(work, struct fdl_state,
+ timeout.work);
+ struct sdca_interrupt *interrupt = fdl_state->interrupt;
+ struct device *dev = interrupt->dev;
+
+ dev_err(dev, "FDL transaction timed out\n");
+
+ guard(mutex)(&fdl_state->lock);
+
+ fdl_end(interrupt);
+ sdca_reset_function(dev, interrupt->function, interrupt->function_regmap);
+}
+
+static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status)
+{
+ struct fdl_state *fdl_state = interrupt->priv;
+ int ret;
+
+ switch (status) {
+ case SDCA_CTL_XU_FDLD_NEEDS_SET:
+ dev_dbg(interrupt->dev, "starting FDL process...\n");
+
+ pm_runtime_get(interrupt->dev);
+ complete(&fdl_state->begin);
+
+ fdl_state->file_index = 0;
+ fdl_state->set = fdl_get_set(interrupt);
+ fallthrough;
+ case SDCA_CTL_XU_FDLD_MORE_FILES_OK:
+ ret = fdl_load_file(interrupt, fdl_state->set, fdl_state->file_index);
+ if (ret) {
+ fdl_end(interrupt);
+ return SDCA_CTL_XU_FDLH_REQ_ABORT;
+ }
+
+ return SDCA_CTL_XU_FDLH_FILE_AVAILABLE;
+ case SDCA_CTL_XU_FDLD_FILE_OK:
+ if (!fdl_state->set) {
+ fdl_end(interrupt);
+ return SDCA_CTL_XU_FDLH_REQ_ABORT;
+ }
+
+ fdl_state->file_index++;
+
+ if (fdl_state->file_index < fdl_state->set->num_files)
+ return SDCA_CTL_XU_FDLH_MORE_FILES;
+ fallthrough;
+ case SDCA_CTL_XU_FDLD_COMPLETE:
+ fdl_end(interrupt);
+ return SDCA_CTL_XU_FDLH_COMPLETE;
+ default:
+ fdl_end(interrupt);
+
+ if (status & SDCA_CTL_XU_FDLD_REQ_RESET)
+ return SDCA_CTL_XU_FDLH_RESET_ACK;
+ else if (status & SDCA_CTL_XU_FDLD_REQ_ABORT)
+ return SDCA_CTL_XU_FDLH_COMPLETE;
+
+ dev_err(interrupt->dev, "invalid FDL status: %x\n", status);
+ return -EINVAL;
+ }
+}
+
+/**
+ * sdca_fdl_process - Process the FDL state machine
+ * @interrupt: SDCA interrupt structure
+ *
+ * Based on section 13.2.5 Flow Diagram for File Download, Host side.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_fdl_process(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct sdca_entity_xu *xu = &interrupt->entity->xu;
+ struct fdl_state *fdl_state = interrupt->priv;
+ unsigned int reg, status;
+ int response, ret;
+
+ ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
+ interrupt->function, interrupt->entity,
+ interrupt->control);
+ if (ret)
+ goto reset_function;
+
+ sdca_ump_cancel_timeout(&fdl_state->timeout);
+
+ scoped_guard(mutex, &fdl_state->lock) {
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
+ interrupt->entity->id, SDCA_CTL_XU_FDL_STATUS, 0);
+ ret = regmap_read(interrupt->function_regmap, reg, &status);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FDL status: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "FDL status: %#x\n", status);
+
+ ret = fdl_status_process(interrupt, status);
+ if (ret < 0)
+ goto reset_function;
+
+ response = ret;
+
+ dev_dbg(dev, "FDL response: %#x\n", response);
+
+ ret = regmap_write(interrupt->function_regmap, reg,
+ response | (status & ~SDCA_CTL_XU_FDLH_MASK));
+ if (ret < 0) {
+ dev_err(dev, "failed to set FDL status signal: %d\n", ret);
+ return ret;
+ }
+
+ ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
+ interrupt->function,
+ interrupt->entity,
+ interrupt->control);
+ if (ret)
+ return ret;
+
+ switch (response) {
+ case SDCA_CTL_XU_FDLH_RESET_ACK:
+ dev_dbg(dev, "FDL request reset\n");
+
+ switch (xu->reset_mechanism) {
+ default:
+ dev_warn(dev, "Requested reset mechanism not implemented\n");
+ fallthrough;
+ case SDCA_XU_RESET_FUNCTION:
+ goto reset_function;
+ }
+ case SDCA_CTL_XU_FDLH_COMPLETE:
+ if (status & SDCA_CTL_XU_FDLD_REQ_ABORT ||
+ status == SDCA_CTL_XU_FDLD_COMPLETE)
+ return 0;
+ fallthrough;
+ default:
+ sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay);
+ return 0;
+ }
+ }
+
+reset_function:
+ sdca_reset_function(dev, interrupt->function, interrupt->function_regmap);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA");
+
+/**
+ * sdca_fdl_alloc_state - allocate state for an FDL interrupt
+ * @interrupt: SDCA interrupt structure.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct fdl_state *fdl_state;
+
+ fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL);
+ if (!fdl_state)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&fdl_state->timeout, sdca_fdl_timeout_work);
+ init_completion(&fdl_state->begin);
+ init_completion(&fdl_state->done);
+ mutex_init(&fdl_state->lock);
+ fdl_state->interrupt = interrupt;
+
+ interrupt->priv = fdl_state;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c
new file mode 100644
index 000000000000..c6cc880a150e
--- /dev/null
+++ b/sound/soc/sdca/sdca_function_device.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2024 Intel Corporation.
+
+/*
+ * SDCA Function Device management
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include "sdca_function_device.h"
+
+/*
+ * A SoundWire device can have multiple SDCA functions identified by
+ * their type and ADR. there can be multiple SoundWire devices per
+ * link, or multiple devices spread across multiple links. An IDA is
+ * required to identify each instance.
+ */
+static DEFINE_IDA(sdca_function_ida);
+
+static void sdca_dev_release(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev);
+
+ ida_free(&sdca_function_ida, auxdev->id);
+ kfree(sdev);
+}
+
+/* alloc, init and add link devices */
+static struct sdca_dev *sdca_dev_register(struct device *parent,
+ struct sdca_function_desc *function_desc)
+{
+ struct sdca_dev *sdev;
+ struct auxiliary_device *auxdev;
+ int ret;
+ int rc;
+
+ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return ERR_PTR(-ENOMEM);
+
+ auxdev = &sdev->auxdev;
+ auxdev->name = function_desc->name;
+ auxdev->dev.parent = parent;
+ auxdev->dev.fwnode = function_desc->node;
+ auxdev->dev.release = sdca_dev_release;
+
+ sdev->function.desc = function_desc;
+
+ rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);
+ if (rc < 0) {
+ kfree(sdev);
+ return ERR_PTR(rc);
+ }
+ auxdev->id = rc;
+
+ /* now follow the two-step init/add sequence */
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(parent, "failed to initialize SDCA function dev %s\n",
+ function_desc->name);
+ ida_free(&sdca_function_ida, auxdev->id);
+ kfree(sdev);
+ return ERR_PTR(ret);
+ }
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret < 0) {
+ dev_err(parent, "failed to add SDCA function dev %s\n",
+ sdev->auxdev.name);
+ /* sdev will be freed with the put_device() and .release sequence */
+ auxiliary_device_uninit(&sdev->auxdev);
+ return ERR_PTR(ret);
+ }
+
+ return sdev;
+}
+
+static void sdca_dev_unregister(struct sdca_dev *sdev)
+{
+ auxiliary_device_delete(&sdev->auxdev);
+ auxiliary_device_uninit(&sdev->auxdev);
+}
+
+int sdca_dev_register_functions(struct sdw_slave *slave)
+{
+ struct sdca_device_data *sdca_data = &slave->sdca_data;
+ int i;
+
+ for (i = 0; i < sdca_data->num_functions; i++) {
+ struct sdca_dev *func_dev;
+
+ func_dev = sdca_dev_register(&slave->dev,
+ &sdca_data->function[i]);
+ if (IS_ERR(func_dev))
+ return PTR_ERR(func_dev);
+
+ sdca_data->function[i].func_dev = func_dev;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
+
+void sdca_dev_unregister_functions(struct sdw_slave *slave)
+{
+ struct sdca_device_data *sdca_data = &slave->sdca_data;
+ int i;
+
+ for (i = 0; i < sdca_data->num_functions; i++)
+ sdca_dev_unregister(sdca_data->function[i].func_dev);
+}
+EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_function_device.h b/sound/soc/sdca/sdca_function_device.h
new file mode 100644
index 000000000000..5adf7551d3a4
--- /dev/null
+++ b/sound/soc/sdca/sdca_function_device.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* Copyright(c) 2024 Intel Corporation. */
+
+#ifndef __SDCA_FUNCTION_DEVICE_H
+#define __SDCA_FUNCTION_DEVICE_H
+
+struct sdca_dev {
+ struct auxiliary_device auxdev;
+ struct sdca_function_data function;
+};
+
+#define auxiliary_dev_to_sdca_dev(auxiliary_dev) \
+ container_of(auxiliary_dev, struct sdca_dev, auxdev)
+
+#endif
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
new file mode 100644
index 000000000000..5a1f120487ef
--- /dev/null
+++ b/sound/soc/sdca/sdca_functions.c
@@ -0,0 +1,2282 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2024 Intel Corporation
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_hid.h>
+
+/*
+ * Should be long enough to encompass all the MIPI DisCo properties.
+ */
+#define SDCA_PROPERTY_LENGTH 64
+
+static int patch_sdca_function_type(u32 interface_revision, u32 *function_type)
+{
+ /*
+ * Unfortunately early SDCA specifications used different indices for Functions,
+ * for backwards compatibility we have to reorder the values found.
+ */
+ if (interface_revision < 0x0801) {
+ switch (*function_type) {
+ case 1:
+ *function_type = SDCA_FUNCTION_TYPE_SMART_AMP;
+ break;
+ case 2:
+ *function_type = SDCA_FUNCTION_TYPE_SMART_MIC;
+ break;
+ case 3:
+ *function_type = SDCA_FUNCTION_TYPE_SPEAKER_MIC;
+ break;
+ case 4:
+ *function_type = SDCA_FUNCTION_TYPE_UAJ;
+ break;
+ case 5:
+ *function_type = SDCA_FUNCTION_TYPE_RJ;
+ break;
+ case 6:
+ *function_type = SDCA_FUNCTION_TYPE_HID;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static const char *get_sdca_function_name(u32 function_type)
+{
+ switch (function_type) {
+ case SDCA_FUNCTION_TYPE_SMART_AMP:
+ return SDCA_FUNCTION_TYPE_SMART_AMP_NAME;
+ case SDCA_FUNCTION_TYPE_SMART_MIC:
+ return SDCA_FUNCTION_TYPE_SMART_MIC_NAME;
+ case SDCA_FUNCTION_TYPE_UAJ:
+ return SDCA_FUNCTION_TYPE_UAJ_NAME;
+ case SDCA_FUNCTION_TYPE_HID:
+ return SDCA_FUNCTION_TYPE_HID_NAME;
+ case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
+ return SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME;
+ case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
+ return SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME;
+ case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
+ return SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME;
+ case SDCA_FUNCTION_TYPE_RJ:
+ return SDCA_FUNCTION_TYPE_RJ_NAME;
+ case SDCA_FUNCTION_TYPE_COMPANION_AMP:
+ return SDCA_FUNCTION_TYPE_COMPANION_AMP_NAME;
+ case SDCA_FUNCTION_TYPE_IMP_DEF:
+ return SDCA_FUNCTION_TYPE_IMP_DEF_NAME;
+ default:
+ return NULL;
+ }
+}
+
+static int find_sdca_function(struct acpi_device *adev, void *data)
+{
+ struct fwnode_handle *function_node = acpi_fwnode_handle(adev);
+ struct sdca_device_data *sdca_data = data;
+ struct sdw_slave *slave = container_of(sdca_data, struct sdw_slave, sdca_data);
+ struct device *dev = &adev->dev;
+ struct fwnode_handle *control5; /* used to identify function type */
+ const char *function_name;
+ u32 function_type;
+ int function_index;
+ u64 addr;
+ int ret;
+
+ if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) {
+ dev_err(dev, "maximum number of functions exceeded\n");
+ return -EINVAL;
+ }
+
+ ret = acpi_get_local_u64_address(adev->handle, &addr);
+ if (ret < 0)
+ return ret;
+
+ if (!addr || addr > 0x7) {
+ dev_err(dev, "invalid addr: 0x%llx\n", addr);
+ return -ENODEV;
+ }
+
+ /*
+ * Extracting the topology type for an SDCA function is a
+ * convoluted process.
+ * The Function type is only visible as a result of a read
+ * from a control. In theory this would mean reading from the hardware,
+ * but the SDCA/DisCo specs defined the notion of "DC value" - a constant
+ * represented with a DSD subproperty.
+ * Drivers have to query the properties for the control
+ * SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY (0x05)
+ */
+ control5 = fwnode_get_named_child_node(function_node,
+ "mipi-sdca-control-0x5-subproperties");
+ if (!control5)
+ return -ENODEV;
+
+ ret = fwnode_property_read_u32(control5, "mipi-sdca-control-dc-value",
+ &function_type);
+
+ fwnode_handle_put(control5);
+
+ if (ret < 0) {
+ dev_err(dev, "function type only supported as DisCo constant\n");
+ return ret;
+ }
+
+ if (!sdca_device_quirk_match(slave, SDCA_QUIRKS_SKIP_FUNC_TYPE_PATCHING)) {
+ ret = patch_sdca_function_type(sdca_data->interface_revision, &function_type);
+ if (ret < 0) {
+ dev_err(dev, "SDCA version %#x invalid function type %d\n",
+ sdca_data->interface_revision, function_type);
+ return ret;
+ }
+ }
+
+ function_name = get_sdca_function_name(function_type);
+ if (!function_name) {
+ dev_err(dev, "invalid SDCA function type %d\n", function_type);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "SDCA function %s (type %d) at 0x%llx\n",
+ function_name, function_type, addr);
+
+ /* store results */
+ function_index = sdca_data->num_functions;
+ sdca_data->function[function_index].adr = addr;
+ sdca_data->function[function_index].type = function_type;
+ sdca_data->function[function_index].name = function_name;
+ sdca_data->function[function_index].node = function_node;
+ sdca_data->num_functions++;
+
+ return 0;
+}
+
+/**
+ * sdca_lookup_functions - Parse sdca_device_desc for each Function
+ * @slave: SoundWire slave device to be processed.
+ *
+ * Iterate through the available SDCA Functions and fill in a short
+ * descriptor (struct sdca_function_desc) for each function, this
+ * information is stored along with the SoundWire slave device and
+ * used for adding drivers and quirks before the devices have fully
+ * probed.
+ */
+void sdca_lookup_functions(struct sdw_slave *slave)
+{
+ struct device *sdev = &slave->dev;
+ struct acpi_device *adev = to_acpi_device_node(sdev->fwnode);
+
+ if (!adev) {
+ dev_info(sdev, "no matching ACPI device found, ignoring peripheral\n");
+ return;
+ }
+
+ acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
+}
+EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
+
+struct raw_init_write {
+ __le32 addr;
+ u8 val;
+} __packed;
+
+static int find_sdca_init_table(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ struct raw_init_write *raw __free(kfree) = NULL;
+ struct sdca_init_write *init_write;
+ int i, num_init_writes;
+
+ num_init_writes = fwnode_property_count_u8(function_node,
+ "mipi-sdca-function-initialization-table");
+ if (!num_init_writes || num_init_writes == -EINVAL) {
+ return 0;
+ } else if (num_init_writes < 0) {
+ dev_err(dev, "%pfwP: failed to read initialization table: %d\n",
+ function_node, num_init_writes);
+ return num_init_writes;
+ } else if (num_init_writes % sizeof(*raw) != 0) {
+ dev_err(dev, "%pfwP: init table size invalid\n", function_node);
+ return -EINVAL;
+ } else if ((num_init_writes / sizeof(*raw)) > SDCA_MAX_INIT_COUNT) {
+ dev_err(dev, "%pfwP: maximum init table size exceeded\n", function_node);
+ return -EINVAL;
+ }
+
+ raw = kzalloc(num_init_writes, GFP_KERNEL);
+ if (!raw)
+ return -ENOMEM;
+
+ fwnode_property_read_u8_array(function_node,
+ "mipi-sdca-function-initialization-table",
+ (u8 *)raw, num_init_writes);
+
+ num_init_writes /= sizeof(*raw);
+
+ init_write = devm_kcalloc(dev, num_init_writes, sizeof(*init_write), GFP_KERNEL);
+ if (!init_write)
+ return -ENOMEM;
+
+ for (i = 0; i < num_init_writes; i++) {
+ init_write[i].addr = le32_to_cpu(raw[i].addr);
+ init_write[i].val = raw[i].val;
+ }
+
+ function->num_init_table = num_init_writes;
+ function->init_table = init_write;
+
+ return 0;
+}
+
+static const char *find_sdca_control_label(struct device *dev,
+ const struct sdca_entity *entity,
+ const struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(IT, MIC_BIAS):
+ return SDCA_CTL_MIC_BIAS_NAME;
+ case SDCA_CTL_TYPE_S(IT, USAGE):
+ case SDCA_CTL_TYPE_S(OT, USAGE):
+ return SDCA_CTL_USAGE_NAME;
+ case SDCA_CTL_TYPE_S(IT, LATENCY):
+ case SDCA_CTL_TYPE_S(OT, LATENCY):
+ case SDCA_CTL_TYPE_S(MU, LATENCY):
+ case SDCA_CTL_TYPE_S(SU, LATENCY):
+ case SDCA_CTL_TYPE_S(FU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, LATENCY):
+ case SDCA_CTL_TYPE_S(CRU, LATENCY):
+ case SDCA_CTL_TYPE_S(UDMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SAPU, LATENCY):
+ case SDCA_CTL_TYPE_S(PPU, LATENCY):
+ return SDCA_CTL_LATENCY_NAME;
+ case SDCA_CTL_TYPE_S(IT, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(CRU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(UDMPU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(MFPU, CLUSTERINDEX):
+ return SDCA_CTL_CLUSTERINDEX_NAME;
+ case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR):
+ return SDCA_CTL_DATAPORT_SELECTOR_NAME;
+ case SDCA_CTL_TYPE_S(IT, MATCHING_GUID):
+ case SDCA_CTL_TYPE_S(OT, MATCHING_GUID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, MATCHING_GUID):
+ return SDCA_CTL_MATCHING_GUID_NAME;
+ case SDCA_CTL_TYPE_S(IT, KEEP_ALIVE):
+ case SDCA_CTL_TYPE_S(OT, KEEP_ALIVE):
+ return SDCA_CTL_KEEP_ALIVE_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_STREAM):
+ case SDCA_CTL_TYPE_S(OT, NDAI_STREAM):
+ return SDCA_CTL_NDAI_STREAM_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_CATEGORY):
+ case SDCA_CTL_TYPE_S(OT, NDAI_CATEGORY):
+ return SDCA_CTL_NDAI_CATEGORY_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_CODINGTYPE):
+ case SDCA_CTL_TYPE_S(OT, NDAI_CODINGTYPE):
+ return SDCA_CTL_NDAI_CODINGTYPE_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_PACKETTYPE):
+ case SDCA_CTL_TYPE_S(OT, NDAI_PACKETTYPE):
+ return SDCA_CTL_NDAI_PACKETTYPE_NAME;
+ case SDCA_CTL_TYPE_S(MU, MIXER):
+ return SDCA_CTL_MIXER_NAME;
+ case SDCA_CTL_TYPE_S(SU, SELECTOR):
+ return SDCA_CTL_SELECTOR_NAME;
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ return SDCA_CTL_MUTE_NAME;
+ case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME):
+ return SDCA_CTL_CHANNEL_VOLUME_NAME;
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ return SDCA_CTL_AGC_NAME;
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ return SDCA_CTL_BASS_BOOST_NAME;
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ return SDCA_CTL_LOUDNESS_NAME;
+ case SDCA_CTL_TYPE_S(FU, GAIN):
+ return SDCA_CTL_GAIN_NAME;
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ return SDCA_CTL_BYPASS_NAME;
+ case SDCA_CTL_TYPE_S(XU, XU_ID):
+ return SDCA_CTL_XU_ID_NAME;
+ case SDCA_CTL_TYPE_S(XU, XU_VERSION):
+ return SDCA_CTL_XU_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ return SDCA_CTL_FDL_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET):
+ return SDCA_CTL_FDL_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH):
+ return SDCA_CTL_FDL_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_STATUS):
+ return SDCA_CTL_FDL_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_SET_INDEX):
+ return SDCA_CTL_FDL_SET_INDEX_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST):
+ return SDCA_CTL_FDL_HOST_REQUEST_NAME;
+ case SDCA_CTL_TYPE_S(CS, CLOCK_VALID):
+ return SDCA_CTL_CLOCK_VALID_NAME;
+ case SDCA_CTL_TYPE_S(CS, SAMPLERATEINDEX):
+ return SDCA_CTL_SAMPLERATEINDEX_NAME;
+ case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT):
+ return SDCA_CTL_CLOCK_SELECT_NAME;
+ case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS):
+ return SDCA_CTL_REQUESTED_PS_NAME;
+ case SDCA_CTL_TYPE_S(PDE, ACTUAL_PS):
+ return SDCA_CTL_ACTUAL_PS_NAME;
+ case SDCA_CTL_TYPE_S(GE, SELECTED_MODE):
+ return SDCA_CTL_SELECTED_MODE_NAME;
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ return SDCA_CTL_DETECTED_MODE_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVATE):
+ return SDCA_CTL_PRIVATE_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_POLICY):
+ return SDCA_CTL_PRIVACY_POLICY_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_LOCKSTATE):
+ return SDCA_CTL_PRIVACY_LOCKSTATE_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_OWNER):
+ return SDCA_CTL_PRIVACY_OWNER_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER):
+ return SDCA_CTL_AUTHTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET):
+ return SDCA_CTL_AUTHTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH):
+ return SDCA_CTL_AUTHTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER):
+ return SDCA_CTL_AUTHRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET):
+ return SDCA_CTL_AUTHRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH):
+ return SDCA_CTL_AUTHRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR):
+ return SDCA_CTL_ACOUSTIC_ENERGY_LEVEL_MONITOR_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN):
+ return SDCA_CTL_ULTRASOUND_LOOP_GAIN_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_0):
+ return SDCA_CTL_OPAQUESET_0_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_1):
+ return SDCA_CTL_OPAQUESET_1_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_2):
+ return SDCA_CTL_OPAQUESET_2_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_3):
+ return SDCA_CTL_OPAQUESET_3_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_4):
+ return SDCA_CTL_OPAQUESET_4_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_5):
+ return SDCA_CTL_OPAQUESET_5_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_6):
+ return SDCA_CTL_OPAQUESET_6_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_7):
+ return SDCA_CTL_OPAQUESET_7_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_8):
+ return SDCA_CTL_OPAQUESET_8_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_9):
+ return SDCA_CTL_OPAQUESET_9_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_10):
+ return SDCA_CTL_OPAQUESET_10_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_11):
+ return SDCA_CTL_OPAQUESET_11_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_12):
+ return SDCA_CTL_OPAQUESET_12_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_13):
+ return SDCA_CTL_OPAQUESET_13_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_14):
+ return SDCA_CTL_OPAQUESET_14_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_15):
+ return SDCA_CTL_OPAQUESET_15_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_16):
+ return SDCA_CTL_OPAQUESET_16_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_17):
+ return SDCA_CTL_OPAQUESET_17_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_18):
+ return SDCA_CTL_OPAQUESET_18_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_19):
+ return SDCA_CTL_OPAQUESET_19_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_20):
+ return SDCA_CTL_OPAQUESET_20_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_21):
+ return SDCA_CTL_OPAQUESET_21_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_22):
+ return SDCA_CTL_OPAQUESET_22_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_23):
+ return SDCA_CTL_OPAQUESET_23_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_READY):
+ return SDCA_CTL_ALGORITHM_READY_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_ENABLE):
+ return SDCA_CTL_ALGORITHM_ENABLE_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_PREPARE):
+ return SDCA_CTL_ALGORITHM_PREPARE_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, CENTER_FREQUENCY_INDEX):
+ return SDCA_CTL_CENTER_FREQUENCY_INDEX_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL):
+ return SDCA_CTL_ULTRASOUND_LEVEL_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_NUMBER):
+ return SDCA_CTL_AE_NUMBER_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER):
+ return SDCA_CTL_AE_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET):
+ return SDCA_CTL_AE_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH):
+ return SDCA_CTL_AE_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE):
+ return SDCA_CTL_TRIGGER_ENABLE_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_STATUS):
+ return SDCA_CTL_TRIGGER_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_MODE):
+ return SDCA_CTL_HIST_BUFFER_MODE_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_PREAMBLE):
+ return SDCA_CTL_HIST_BUFFER_PREAMBLE_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_ERROR):
+ return SDCA_CTL_HIST_ERROR_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_EXTENSION):
+ return SDCA_CTL_TRIGGER_EXTENSION_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_READY):
+ return SDCA_CTL_TRIGGER_READY_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER):
+ return SDCA_CTL_HIST_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET):
+ return SDCA_CTL_HIST_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH):
+ return SDCA_CTL_HIST_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER):
+ return SDCA_CTL_DTODTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH):
+ return SDCA_CTL_DTODTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER):
+ return SDCA_CTL_DTODRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH):
+ return SDCA_CTL_DTODRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, PROTECTION_MODE):
+ return SDCA_CTL_PROTECTION_MODE_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, PROTECTION_STATUS):
+ return SDCA_CTL_PROTECTION_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, OPAQUESETREQ_INDEX):
+ return SDCA_CTL_OPAQUESETREQ_INDEX_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER):
+ return SDCA_CTL_DTODTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH):
+ return SDCA_CTL_DTODTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER):
+ return SDCA_CTL_DTODRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH):
+ return SDCA_CTL_DTODRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(PPU, POSTURENUMBER):
+ return SDCA_CTL_POSTURENUMBER_NAME;
+ case SDCA_CTL_TYPE_S(PPU, POSTUREEXTENSION):
+ return SDCA_CTL_POSTUREEXTENSION_NAME;
+ case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE):
+ return SDCA_CTL_HORIZONTALBALANCE_NAME;
+ case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE):
+ return SDCA_CTL_VERTICALBALANCE_NAME;
+ case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER):
+ return SDCA_CTL_TONE_DIVIDER_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ return SDCA_CTL_HIDTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET):
+ return SDCA_CTL_HIDTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH):
+ return SDCA_CTL_HIDTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER):
+ return SDCA_CTL_HIDRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET):
+ return SDCA_CTL_HIDRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH):
+ return SDCA_CTL_HIDRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK):
+ return SDCA_CTL_COMMIT_GROUP_MASK_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_SDCA_VERSION):
+ return SDCA_CTL_FUNCTION_SDCA_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_TYPE):
+ return SDCA_CTL_FUNCTION_TYPE_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID):
+ return SDCA_CTL_FUNCTION_MANUFACTURER_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID):
+ return SDCA_CTL_FUNCTION_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_VERSION):
+ return SDCA_CTL_FUNCTION_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID):
+ return SDCA_CTL_FUNCTION_EXTENSION_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_VERSION):
+ return SDCA_CTL_FUNCTION_EXTENSION_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ return SDCA_CTL_FUNCTION_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ACTION):
+ return SDCA_CTL_FUNCTION_ACTION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID):
+ return SDCA_CTL_DEVICE_MANUFACTURER_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID):
+ return SDCA_CTL_DEVICE_PART_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_VERSION):
+ return SDCA_CTL_DEVICE_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_SDCA_VERSION):
+ return SDCA_CTL_DEVICE_SDCA_VERSION_NAME;
+ default:
+ return devm_kasprintf(dev, GFP_KERNEL, "Imp-Def %#x", control->sel);
+ }
+}
+
+static unsigned int find_sdca_control_bits(const struct sdca_entity *entity,
+ const struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(IT, LATENCY):
+ case SDCA_CTL_TYPE_S(OT, LATENCY):
+ case SDCA_CTL_TYPE_S(MU, LATENCY):
+ case SDCA_CTL_TYPE_S(SU, LATENCY):
+ case SDCA_CTL_TYPE_S(FU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(CRU, LATENCY):
+ case SDCA_CTL_TYPE_S(UDMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(PPU, LATENCY):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH):
+ return 32;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID):
+ case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(MU, MIXER):
+ case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME):
+ case SDCA_CTL_TYPE_S(FU, GAIN):
+ case SDCA_CTL_TYPE_S(XU, XU_ID):
+ case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR):
+ case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN):
+ case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL):
+ case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE):
+ case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE):
+ return 16;
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ return 1;
+ default:
+ return 8;
+ }
+}
+
+static enum sdca_control_datatype
+find_sdca_control_datatype(const struct sdca_entity *entity,
+ const struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ return SDCA_CTL_DATATYPE_ONEBIT;
+ case SDCA_CTL_TYPE_S(IT, LATENCY):
+ case SDCA_CTL_TYPE_S(OT, LATENCY):
+ case SDCA_CTL_TYPE_S(MU, LATENCY):
+ case SDCA_CTL_TYPE_S(SU, LATENCY):
+ case SDCA_CTL_TYPE_S(FU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, LATENCY):
+ case SDCA_CTL_TYPE_S(CRU, LATENCY):
+ case SDCA_CTL_TYPE_S(UDMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SAPU, LATENCY):
+ case SDCA_CTL_TYPE_S(PPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SU, SELECTOR):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_0):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_1):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_2):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_3):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_4):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_5):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_6):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_7):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_8):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_9):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_10):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_11):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_12):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_13):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_14):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_15):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_16):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_17):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_18):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_19):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_20):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_21):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_22):
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_23):
+ case SDCA_CTL_TYPE_S(SAPU, PROTECTION_MODE):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_PREAMBLE):
+ case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST):
+ case SDCA_CTL_TYPE_S(XU, XU_ID):
+ case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT):
+ case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH):
+ return SDCA_CTL_DATATYPE_INTEGER;
+ case SDCA_CTL_TYPE_S(IT, MIC_BIAS):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_MODE):
+ case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS):
+ case SDCA_CTL_TYPE_S(PDE, ACTUAL_PS):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_TYPE):
+ return SDCA_CTL_DATATYPE_SPEC_ENCODED_VALUE;
+ case SDCA_CTL_TYPE_S(XU, XU_VERSION):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_SDCA_VERSION):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_VERSION):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_VERSION):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_VERSION):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_SDCA_VERSION):
+ return SDCA_CTL_DATATYPE_BCD;
+ case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME):
+ case SDCA_CTL_TYPE_S(FU, GAIN):
+ case SDCA_CTL_TYPE_S(MU, MIXER):
+ case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE):
+ case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE):
+ case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL):
+ case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR):
+ case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN):
+ return SDCA_CTL_DATATYPE_Q7P8DB;
+ case SDCA_CTL_TYPE_S(IT, USAGE):
+ case SDCA_CTL_TYPE_S(OT, USAGE):
+ case SDCA_CTL_TYPE_S(IT, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(CRU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(UDMPU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(MFPU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(MFPU, CENTER_FREQUENCY_INDEX):
+ case SDCA_CTL_TYPE_S(MFPU, AE_NUMBER):
+ case SDCA_CTL_TYPE_S(SAPU, OPAQUESETREQ_INDEX):
+ case SDCA_CTL_TYPE_S(XU, FDL_SET_INDEX):
+ case SDCA_CTL_TYPE_S(CS, SAMPLERATEINDEX):
+ case SDCA_CTL_TYPE_S(GE, SELECTED_MODE):
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ return SDCA_CTL_DATATYPE_BYTEINDEX;
+ case SDCA_CTL_TYPE_S(PPU, POSTURENUMBER):
+ return SDCA_CTL_DATATYPE_POSTURENUMBER;
+ case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR):
+ return SDCA_CTL_DATATYPE_DP_INDEX;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_READY):
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_ENABLE):
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_PREPARE):
+ case SDCA_CTL_TYPE_S(SAPU, PROTECTION_STATUS):
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE):
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_STATUS):
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_READY):
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_POLICY):
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_OWNER):
+ return SDCA_CTL_DATATYPE_BITINDEX;
+ case SDCA_CTL_TYPE_S(IT, KEEP_ALIVE):
+ case SDCA_CTL_TYPE_S(OT, KEEP_ALIVE):
+ case SDCA_CTL_TYPE_S(IT, NDAI_STREAM):
+ case SDCA_CTL_TYPE_S(OT, NDAI_STREAM):
+ case SDCA_CTL_TYPE_S(IT, NDAI_CATEGORY):
+ case SDCA_CTL_TYPE_S(OT, NDAI_CATEGORY):
+ case SDCA_CTL_TYPE_S(IT, NDAI_CODINGTYPE):
+ case SDCA_CTL_TYPE_S(OT, NDAI_CODINGTYPE):
+ case SDCA_CTL_TYPE_S(IT, NDAI_PACKETTYPE):
+ case SDCA_CTL_TYPE_S(OT, NDAI_PACKETTYPE):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_ERROR):
+ case SDCA_CTL_TYPE_S(XU, FDL_STATUS):
+ case SDCA_CTL_TYPE_S(CS, CLOCK_VALID):
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_LOCKSTATE):
+ case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ACTION):
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER):
+ return SDCA_CTL_DATATYPE_BITMAP;
+ case SDCA_CTL_TYPE_S(IT, MATCHING_GUID):
+ case SDCA_CTL_TYPE_S(OT, MATCHING_GUID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, MATCHING_GUID):
+ return SDCA_CTL_DATATYPE_GUID;
+ default:
+ return SDCA_CTL_DATATYPE_IMPDEF;
+ }
+}
+
+static bool find_sdca_control_volatile(const struct sdca_entity *entity,
+ const struct sdca_control *control)
+{
+ switch (control->mode) {
+ case SDCA_ACCESS_MODE_DC:
+ return false;
+ case SDCA_ACCESS_MODE_RO:
+ case SDCA_ACCESS_MODE_RW1S:
+ case SDCA_ACCESS_MODE_RW1C:
+ return true;
+ default:
+ break;
+ }
+
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(XU, FDL_STATUS):
+ case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int find_sdca_control_range(struct device *dev,
+ struct fwnode_handle *control_node,
+ struct sdca_control_range *range)
+{
+ u8 *range_list;
+ int num_range;
+ u16 *limits;
+ int i;
+
+ num_range = fwnode_property_count_u8(control_node, "mipi-sdca-control-range");
+ if (!num_range || num_range == -EINVAL)
+ return 0;
+ else if (num_range < 0)
+ return num_range;
+
+ range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL);
+ if (!range_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u8_array(control_node, "mipi-sdca-control-range",
+ range_list, num_range);
+
+ limits = (u16 *)range_list;
+
+ range->cols = le16_to_cpu(limits[0]);
+ range->rows = le16_to_cpu(limits[1]);
+ range->data = (u32 *)&limits[2];
+
+ num_range = (num_range - (2 * sizeof(*limits))) / sizeof(*range->data);
+ if (num_range != range->cols * range->rows)
+ return -EINVAL;
+
+ for (i = 0; i < num_range; i++)
+ range->data[i] = le32_to_cpu(range->data[i]);
+
+ return 0;
+}
+
+static int find_sdca_control_value(struct device *dev, struct sdca_entity *entity,
+ struct fwnode_handle *control_node,
+ struct sdca_control *control,
+ const char * const label)
+{
+ char property[SDCA_PROPERTY_LENGTH];
+ bool global = true;
+ int ret, cn, i;
+ u32 tmp;
+
+ snprintf(property, sizeof(property), "mipi-sdca-control-%s", label);
+
+ ret = fwnode_property_read_u32(control_node, property, &tmp);
+ if (ret == -EINVAL)
+ global = false;
+ else if (ret)
+ return ret;
+
+ i = 0;
+ for_each_set_bit(cn, (unsigned long *)&control->cn_list,
+ BITS_PER_TYPE(control->cn_list)) {
+ if (!global) {
+ snprintf(property, sizeof(property),
+ "mipi-sdca-control-cn-%d-%s", cn, label);
+
+ ret = fwnode_property_read_u32(control_node, property, &tmp);
+ if (ret)
+ return ret;
+ }
+
+ control->values[i] = tmp;
+ i++;
+ }
+
+ return 0;
+}
+
+/*
+ * TODO: Add support for -cn- properties, allowing different channels to have
+ * different defaults etc.
+ */
+static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity,
+ struct fwnode_handle *control_node,
+ struct sdca_control *control)
+{
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-access-mode", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: access mode missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->mode = tmp;
+
+ ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-access-layer", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: access layer missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->layers = tmp;
+
+ ret = fwnode_property_read_u64(control_node, "mipi-sdca-control-cn-list",
+ &control->cn_list);
+ if (ret == -EINVAL) {
+ /* Spec allows not specifying cn-list if only the first number is used */
+ control->cn_list = 0x1;
+ } else if (ret || !control->cn_list) {
+ dev_err(dev, "%s: control %#x: cn list missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->values = devm_kcalloc(dev, hweight64(control->cn_list),
+ sizeof(int), GFP_KERNEL);
+ if (!control->values)
+ return -ENOMEM;
+
+ switch (control->mode) {
+ case SDCA_ACCESS_MODE_DC:
+ ret = find_sdca_control_value(dev, entity, control_node, control,
+ "dc-value");
+ if (ret) {
+ dev_err(dev, "%s: control %#x: dc value missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->has_fixed = true;
+ break;
+ case SDCA_ACCESS_MODE_RW:
+ case SDCA_ACCESS_MODE_DUAL:
+ ret = find_sdca_control_value(dev, entity, control_node, control,
+ "default-value");
+ if (!ret)
+ control->has_default = true;
+
+ ret = find_sdca_control_value(dev, entity, control_node, control,
+ "fixed-value");
+ if (!ret)
+ control->has_fixed = true;
+ fallthrough;
+ case SDCA_ACCESS_MODE_RO:
+ control->deferrable = fwnode_property_read_bool(control_node,
+ "mipi-sdca-control-deferrable");
+ break;
+ default:
+ break;
+ }
+
+ control->is_volatile = find_sdca_control_volatile(entity, control);
+
+ ret = find_sdca_control_range(dev, control_node, &control->range);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: range missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ ret = fwnode_property_read_u32(control_node,
+ "mipi-sdca-control-interrupt-position",
+ &tmp);
+ if (!ret)
+ control->interrupt_position = tmp;
+ else
+ control->interrupt_position = SDCA_NO_INTERRUPT;
+
+ control->label = find_sdca_control_label(dev, entity, control);
+ if (!control->label)
+ return -ENOMEM;
+
+ control->type = find_sdca_control_datatype(entity, control);
+ control->nbits = find_sdca_control_bits(entity, control);
+
+ dev_dbg(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d %s\n",
+ entity->label, control->label, control->sel,
+ control->mode, control->layers, control->cn_list,
+ control->interrupt_position, control->deferrable ? "deferrable" : "");
+
+ return 0;
+}
+
+static int find_sdca_entity_controls(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_control *controls;
+ int num_controls;
+ u64 control_list;
+ int control_sel;
+ int i, ret;
+
+ ret = fwnode_property_read_u64(entity_node, "mipi-sdca-control-list", &control_list);
+ if (ret == -EINVAL) {
+ /* Allow missing control lists, assume no controls. */
+ dev_warn(dev, "%s: missing control list\n", entity->label);
+ return 0;
+ } else if (ret) {
+ dev_err(dev, "%s: failed to read control list: %d\n", entity->label, ret);
+ return ret;
+ } else if (!control_list) {
+ return 0;
+ }
+
+ num_controls = hweight64(control_list);
+ controls = devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_set_bit(control_sel, (unsigned long *)&control_list,
+ BITS_PER_TYPE(control_list)) {
+ struct fwnode_handle *control_node;
+ char control_property[SDCA_PROPERTY_LENGTH];
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(control_property, sizeof(control_property),
+ "mipi-sdca-control-0x%X-subproperties", control_sel);
+
+ control_node = fwnode_get_named_child_node(entity_node, control_property);
+ if (!control_node) {
+ dev_err(dev, "%s: control node %s not found\n",
+ entity->label, control_property);
+ return -EINVAL;
+ }
+
+ controls[i].sel = control_sel;
+
+ ret = find_sdca_entity_control(dev, entity, control_node, &controls[i]);
+ fwnode_handle_put(control_node);
+ if (ret)
+ return ret;
+
+ i++;
+ }
+
+ entity->num_controls = num_controls;
+ entity->controls = controls;
+
+ return 0;
+}
+
+static bool find_sdca_iot_dataport(struct sdca_entity_iot *terminal)
+{
+ switch (terminal->type) {
+ case SDCA_TERM_TYPE_GENERIC:
+ case SDCA_TERM_TYPE_ULTRASOUND:
+ case SDCA_TERM_TYPE_CAPTURE_DIRECT_PCM_MIC:
+ case SDCA_TERM_TYPE_RAW_PDM_MIC:
+ case SDCA_TERM_TYPE_SPEECH:
+ case SDCA_TERM_TYPE_VOICE:
+ case SDCA_TERM_TYPE_SECONDARY_PCM_MIC:
+ case SDCA_TERM_TYPE_ACOUSTIC_CONTEXT_AWARENESS:
+ case SDCA_TERM_TYPE_DTOD_STREAM:
+ case SDCA_TERM_TYPE_REFERENCE_STREAM:
+ case SDCA_TERM_TYPE_SENSE_CAPTURE:
+ case SDCA_TERM_TYPE_STREAMING_MIC:
+ case SDCA_TERM_TYPE_OPTIMIZATION_STREAM:
+ case SDCA_TERM_TYPE_PDM_RENDER_STREAM:
+ case SDCA_TERM_TYPE_COMPANION_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int find_sdca_entity_iot(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_iot *terminal = &entity->iot;
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-terminal-type", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: terminal type missing: %d\n", entity->label, ret);
+ return ret;
+ }
+
+ terminal->type = tmp;
+ terminal->is_dataport = find_sdca_iot_dataport(terminal);
+
+ if (!terminal->is_dataport) {
+ const char *type_name = sdca_find_terminal_name(terminal->type);
+
+ if (type_name)
+ entity->label = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, type_name);
+ }
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-terminal-reference-number", &tmp);
+ if (!ret)
+ terminal->reference = tmp;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-terminal-connector-type", &tmp);
+ if (!ret)
+ terminal->connector = tmp;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-terminal-transducer-count", &tmp);
+ if (!ret)
+ terminal->num_transducer = tmp;
+
+ dev_dbg(dev, "%s: terminal type %#x ref %#x conn %#x count %d\n",
+ entity->label, terminal->type, terminal->reference,
+ terminal->connector, terminal->num_transducer);
+
+ return 0;
+}
+
+static int find_sdca_entity_cs(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_cs *clock = &entity->cs;
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-cs-type", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: clock type missing: %d\n", entity->label, ret);
+ return ret;
+ }
+
+ clock->type = tmp;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-clock-valid-max-delay", &tmp);
+ if (!ret)
+ clock->max_delay = tmp;
+
+ dev_dbg(dev, "%s: clock type %#x delay %d\n", entity->label,
+ clock->type, clock->max_delay);
+
+ return 0;
+}
+
+static int find_sdca_entity_pde(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ static const int mult_delay = 3;
+ struct sdca_entity_pde *power = &entity->pde;
+ u32 *delay_list __free(kfree) = NULL;
+ struct sdca_pde_delay *delays;
+ int num_delays;
+ int i, j;
+
+ num_delays = fwnode_property_count_u32(entity_node,
+ "mipi-sdca-powerdomain-transition-max-delay");
+ if (num_delays <= 0) {
+ dev_err(dev, "%s: max delay list missing: %d\n",
+ entity->label, num_delays);
+ return -EINVAL;
+ } else if (num_delays % mult_delay != 0) {
+ dev_err(dev, "%s: delays not multiple of %d\n",
+ entity->label, mult_delay);
+ return -EINVAL;
+ } else if (num_delays > SDCA_MAX_DELAY_COUNT) {
+ dev_err(dev, "%s: maximum number of transition delays exceeded\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL);
+ if (!delay_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-powerdomain-transition-max-delay",
+ delay_list, num_delays);
+
+ num_delays /= mult_delay;
+
+ delays = devm_kcalloc(dev, num_delays, sizeof(*delays), GFP_KERNEL);
+ if (!delays)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < num_delays; i++) {
+ delays[i].from_ps = delay_list[j++];
+ delays[i].to_ps = delay_list[j++];
+ delays[i].us = delay_list[j++];
+
+ dev_dbg(dev, "%s: from %#x to %#x delay %dus\n", entity->label,
+ delays[i].from_ps, delays[i].to_ps, delays[i].us);
+ }
+
+ power->num_max_delay = num_delays;
+ power->max_delay = delays;
+
+ return 0;
+}
+
+struct raw_ge_mode {
+ u8 val;
+ u8 num_controls;
+ struct {
+ u8 id;
+ u8 sel;
+ u8 cn;
+ __le32 val;
+ } __packed controls[] __counted_by(num_controls);
+} __packed;
+
+static int find_sdca_entity_ge(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_ge *group = &entity->ge;
+ u8 *affected_list __free(kfree) = NULL;
+ u8 *affected_iter;
+ int num_affected;
+ int i, j;
+
+ num_affected = fwnode_property_count_u8(entity_node,
+ "mipi-sdca-ge-selectedmode-controls-affected");
+ if (!num_affected) {
+ return 0;
+ } else if (num_affected < 0) {
+ dev_err(dev, "%s: failed to read affected controls: %d\n",
+ entity->label, num_affected);
+ return num_affected;
+ } else if (num_affected > SDCA_MAX_AFFECTED_COUNT) {
+ dev_err(dev, "%s: maximum affected controls size exceeded\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ affected_list = kcalloc(num_affected, sizeof(*affected_list), GFP_KERNEL);
+ if (!affected_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u8_array(entity_node,
+ "mipi-sdca-ge-selectedmode-controls-affected",
+ affected_list, num_affected);
+
+ group->num_modes = *affected_list;
+ affected_iter = affected_list + 1;
+
+ group->modes = devm_kcalloc(dev, group->num_modes, sizeof(*group->modes),
+ GFP_KERNEL);
+ if (!group->modes)
+ return -ENOMEM;
+
+ for (i = 0; i < group->num_modes; i++) {
+ struct raw_ge_mode *raw = (struct raw_ge_mode *)affected_iter;
+ struct sdca_ge_mode *mode = &group->modes[i];
+
+ affected_iter += sizeof(*raw);
+ if (affected_iter > affected_list + num_affected)
+ goto bad_list;
+
+ mode->val = raw->val;
+ mode->num_controls = raw->num_controls;
+
+ affected_iter += mode->num_controls * sizeof(raw->controls[0]);
+ if (affected_iter > affected_list + num_affected)
+ goto bad_list;
+
+ mode->controls = devm_kcalloc(dev, mode->num_controls,
+ sizeof(*mode->controls), GFP_KERNEL);
+ if (!mode->controls)
+ return -ENOMEM;
+
+ for (j = 0; j < mode->num_controls; j++) {
+ mode->controls[j].id = raw->controls[j].id;
+ mode->controls[j].sel = raw->controls[j].sel;
+ mode->controls[j].cn = raw->controls[j].cn;
+ mode->controls[j].val = le32_to_cpu(raw->controls[j].val);
+ }
+ }
+
+ return 0;
+
+bad_list:
+ dev_err(dev, "%s: malformed affected controls list\n", entity->label);
+ return -EINVAL;
+}
+
+static int
+find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw,
+ struct fwnode_handle *function_node,
+ struct fwnode_handle *entity_node, struct sdca_entity *entity)
+{
+ struct sdca_entity_hide *hide = &entity->hide;
+ unsigned int delay, *af_list = hide->af_number_list;
+ int nval, ret;
+ unsigned char *report_desc = NULL;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay);
+ if (!ret)
+ hide->max_delay = delay;
+
+ nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids");
+ if (nval > 0) {
+ hide->num_hidtx_ids = nval;
+ hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids,
+ sizeof(*hide->hidtx_ids), GFP_KERNEL);
+ if (!hide->hidtx_ids)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-HIDTx-supported-report-ids",
+ hide->hidtx_ids,
+ hide->num_hidtx_ids);
+ if (ret < 0)
+ return ret;
+ }
+
+ nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids");
+ if (nval > 0) {
+ hide->num_hidrx_ids = nval;
+ hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids,
+ sizeof(*hide->hidrx_ids), GFP_KERNEL);
+ if (!hide->hidrx_ids)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-HIDRx-supported-report-ids",
+ hide->hidrx_ids,
+ hide->num_hidrx_ids);
+ if (ret < 0)
+ return ret;
+ }
+
+ nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list");
+ if (nval <= 0) {
+ dev_err(dev, "%pfwP: audio function numbers list missing: %d\n",
+ entity_node, nval);
+ return -EINVAL;
+ } else if (nval > SDCA_MAX_FUNCTION_COUNT) {
+ dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node);
+ return -EINVAL;
+ }
+
+ hide->hide_reside_function_num = nval;
+ fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-hide-related-audio-function-list", af_list, nval);
+
+ nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor");
+ if (nval)
+ fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor",
+ (u8 *)&hide->hid_desc, nval);
+
+ if (hide->hid_desc.bNumDescriptors) {
+ nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor");
+ if (nval) {
+ report_desc = devm_kzalloc(dev, nval, GFP_KERNEL);
+ if (!report_desc)
+ return -ENOMEM;
+ hide->hid_report_desc = report_desc;
+ fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor",
+ report_desc, nval);
+
+ /* add HID device */
+ ret = sdca_add_hid_device(dev, sdw, entity);
+ if (ret) {
+ dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int find_sdca_entity_xu(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_xu *xu = &entity->xu;
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-RxUMP-ownership-transition-max-delay",
+ &tmp);
+ if (!ret)
+ xu->max_delay = tmp;
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-FDL-reset-mechanism",
+ &tmp);
+ if (!ret)
+ xu->reset_mechanism = tmp;
+
+ return 0;
+}
+
+static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw,
+ struct fwnode_handle *function_node,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_string(entity_node, "mipi-sdca-entity-label",
+ &entity->label);
+ if (ret) {
+ dev_err(dev, "%pfwP: entity %#x: label missing: %d\n",
+ function_node, entity->id, ret);
+ return ret;
+ }
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-entity-type", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: type missing: %d\n", entity->label, ret);
+ return ret;
+ }
+
+ entity->type = tmp;
+
+ dev_dbg(dev, "%s: entity %#x type %#x\n",
+ entity->label, entity->id, entity->type);
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ ret = find_sdca_entity_iot(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_XU:
+ ret = find_sdca_entity_xu(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_CS:
+ ret = find_sdca_entity_cs(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ ret = find_sdca_entity_pde(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_GE:
+ ret = find_sdca_entity_ge(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_HIDE:
+ ret = find_sdca_entity_hide(dev, sdw, function_node,
+ entity_node, entity);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ return ret;
+
+ ret = find_sdca_entity_controls(dev, entity_node, entity);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ u32 *entity_list __free(kfree) = NULL;
+ struct sdca_entity *entities;
+ int num_entities;
+ int i, ret;
+
+ num_entities = fwnode_property_count_u32(function_node,
+ "mipi-sdca-entity-id-list");
+ if (num_entities <= 0) {
+ dev_err(dev, "%pfwP: entity id list missing: %d\n",
+ function_node, num_entities);
+ return -EINVAL;
+ } else if (num_entities > SDCA_MAX_ENTITY_COUNT) {
+ dev_err(dev, "%pfwP: maximum number of entities exceeded\n",
+ function_node);
+ return -EINVAL;
+ }
+
+ /* Add 1 to make space for Entity 0 */
+ entities = devm_kcalloc(dev, num_entities + 1, sizeof(*entities), GFP_KERNEL);
+ if (!entities)
+ return -ENOMEM;
+
+ entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL);
+ if (!entity_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(function_node, "mipi-sdca-entity-id-list",
+ entity_list, num_entities);
+
+ for (i = 0; i < num_entities; i++)
+ entities[i].id = entity_list[i];
+
+ /* now read subproperties */
+ for (i = 0; i < num_entities; i++) {
+ char entity_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *entity_node;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(entity_property, sizeof(entity_property),
+ "mipi-sdca-entity-id-0x%X-subproperties", entities[i].id);
+
+ entity_node = fwnode_get_named_child_node(function_node, entity_property);
+ if (!entity_node) {
+ dev_err(dev, "%pfwP: entity node %s not found\n",
+ function_node, entity_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_entity(dev, sdw, function_node,
+ entity_node, &entities[i]);
+ fwnode_handle_put(entity_node);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Add Entity 0 at end of the array, makes it easy to skip during
+ * all the Entity searches involved in creating connections.
+ */
+ entities[num_entities].label = "entity0";
+
+ ret = find_sdca_entity_controls(dev, function_node, &entities[num_entities]);
+ if (ret)
+ return ret;
+
+ function->num_entities = num_entities + 1;
+ function->entities = entities;
+
+ return 0;
+}
+
+static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function,
+ const char *entity_label)
+{
+ int i;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ if (!strncmp(entity->label, entity_label, strlen(entity_label)))
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function,
+ const int id)
+{
+ int i;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ if (entity->id == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static int find_sdca_entity_connection_iot(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_iot *terminal = &entity->iot;
+ struct fwnode_handle *clock_node;
+ struct sdca_entity *clock_entity;
+ const char *clock_label;
+ int ret;
+
+ clock_node = fwnode_get_named_child_node(entity_node,
+ "mipi-sdca-terminal-clock-connection");
+ if (!clock_node)
+ return 0;
+
+ ret = fwnode_property_read_string(clock_node, "mipi-sdca-entity-label",
+ &clock_label);
+ if (ret) {
+ dev_err(dev, "%s: clock label missing: %d\n", entity->label, ret);
+ fwnode_handle_put(clock_node);
+ return ret;
+ }
+
+ clock_entity = find_sdca_entity_by_label(function, clock_label);
+ if (!clock_entity) {
+ dev_err(dev, "%s: failed to find clock with label %s\n",
+ entity->label, clock_label);
+ fwnode_handle_put(clock_node);
+ return -EINVAL;
+ }
+
+ terminal->clock = clock_entity;
+
+ dev_dbg(dev, "%s -> %s\n", clock_entity->label, entity->label);
+
+ fwnode_handle_put(clock_node);
+ return 0;
+}
+
+static int find_sdca_entity_connection_pde(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_pde *power = &entity->pde;
+ u32 *managed_list __free(kfree) = NULL;
+ struct sdca_entity **managed;
+ int num_managed;
+ int i;
+
+ num_managed = fwnode_property_count_u32(entity_node,
+ "mipi-sdca-powerdomain-managed-list");
+ if (!num_managed) {
+ return 0;
+ } else if (num_managed < 0) {
+ dev_err(dev, "%s: managed list missing: %d\n", entity->label, num_managed);
+ return num_managed;
+ } else if (num_managed > SDCA_MAX_ENTITY_COUNT) {
+ dev_err(dev, "%s: maximum number of managed entities exceeded\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ managed = devm_kcalloc(dev, num_managed, sizeof(*managed), GFP_KERNEL);
+ if (!managed)
+ return -ENOMEM;
+
+ managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL);
+ if (!managed_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-powerdomain-managed-list",
+ managed_list, num_managed);
+
+ for (i = 0; i < num_managed; i++) {
+ managed[i] = find_sdca_entity_by_id(function, managed_list[i]);
+ if (!managed[i]) {
+ dev_err(dev, "%s: failed to find entity with id %#x\n",
+ entity->label, managed_list[i]);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%s -> %s\n", managed[i]->label, entity->label);
+ }
+
+ power->num_managed = num_managed;
+ power->managed = managed;
+
+ return 0;
+}
+
+static int find_sdca_entity_connection_ge(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ int i, j;
+
+ for (i = 0; i < entity->ge.num_modes; i++) {
+ struct sdca_ge_mode *mode = &entity->ge.modes[i];
+
+ for (j = 0; j < mode->num_controls; j++) {
+ struct sdca_ge_control *affected = &mode->controls[j];
+ struct sdca_entity *managed;
+
+ managed = find_sdca_entity_by_id(function, affected->id);
+ if (!managed) {
+ dev_err(dev, "%s: failed to find entity with id %#x\n",
+ entity->label, affected->id);
+ return -EINVAL;
+ }
+
+ if (managed->group && managed->group != entity) {
+ dev_err(dev,
+ "%s: entity controlled by two groups %s, %s\n",
+ managed->label, managed->group->label,
+ entity->label);
+ return -EINVAL;
+ }
+
+ managed->group = entity;
+ }
+ }
+
+ return 0;
+}
+
+static int find_sdca_entity_connection(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity **pins;
+ int num_pins, pin;
+ u64 pin_list;
+ int i, ret;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ ret = find_sdca_entity_connection_iot(dev, function,
+ entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ ret = find_sdca_entity_connection_pde(dev, function,
+ entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_GE:
+ ret = find_sdca_entity_connection_ge(dev, function,
+ entity_node, entity);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list);
+ if (ret == -EINVAL) {
+ /* Allow missing pin lists, assume no pins. */
+ return 0;
+ } else if (ret) {
+ dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret);
+ return ret;
+ } else if (pin_list & BIT(0)) {
+ /*
+ * Each bit set in the pin-list refers to an entity_id in this
+ * Function. Entity 0 is an illegal connection since it is used
+ * for Function-level configurations.
+ */
+ dev_err(dev, "%s: pin 0 used as input\n", entity->label);
+ return -EINVAL;
+ } else if (!pin_list) {
+ return 0;
+ }
+
+ num_pins = hweight64(pin_list);
+ pins = devm_kcalloc(dev, num_pins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_set_bit(pin, (unsigned long *)&pin_list, BITS_PER_TYPE(pin_list)) {
+ char pin_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *connected_node;
+ struct sdca_entity *connected_entity;
+ const char *connected_label;
+
+ snprintf(pin_property, sizeof(pin_property), "mipi-sdca-input-pin-%d", pin);
+
+ connected_node = fwnode_get_named_child_node(entity_node, pin_property);
+ if (!connected_node) {
+ dev_err(dev, "%s: pin node %s not found\n",
+ entity->label, pin_property);
+ return -EINVAL;
+ }
+
+ ret = fwnode_property_read_string(connected_node, "mipi-sdca-entity-label",
+ &connected_label);
+ if (ret) {
+ dev_err(dev, "%s: pin %d label missing: %d\n",
+ entity->label, pin, ret);
+ fwnode_handle_put(connected_node);
+ return ret;
+ }
+
+ connected_entity = find_sdca_entity_by_label(function, connected_label);
+ if (!connected_entity) {
+ dev_err(dev, "%s: failed to find entity with label %s\n",
+ entity->label, connected_label);
+ fwnode_handle_put(connected_node);
+ return -EINVAL;
+ }
+
+ pins[i] = connected_entity;
+
+ dev_dbg(dev, "%s -> %s\n", connected_entity->label, entity->label);
+
+ i++;
+ fwnode_handle_put(connected_node);
+ }
+
+ entity->num_sources = num_pins;
+ entity->sources = pins;
+
+ return 0;
+}
+
+static int find_sdca_connections(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ int i;
+
+ /* Entity 0 cannot have connections */
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+ char entity_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *entity_node;
+ int ret;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(entity_property, sizeof(entity_property),
+ "mipi-sdca-entity-id-0x%X-subproperties",
+ entity->id);
+
+ entity_node = fwnode_get_named_child_node(function_node, entity_property);
+ if (!entity_node) {
+ dev_err(dev, "%pfwP: entity node %s not found\n",
+ function_node, entity_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_entity_connection(dev, function, entity_node, entity);
+ fwnode_handle_put(entity_node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int find_sdca_cluster_channel(struct device *dev,
+ struct sdca_cluster *cluster,
+ struct fwnode_handle *channel_node,
+ struct sdca_channel *channel)
+{
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(channel_node, "mipi-sdca-cluster-channel-id", &tmp);
+ if (ret) {
+ dev_err(dev, "cluster %#x: missing channel id: %d\n",
+ cluster->id, ret);
+ return ret;
+ }
+
+ channel->id = tmp;
+
+ ret = fwnode_property_read_u32(channel_node,
+ "mipi-sdca-cluster-channel-purpose",
+ &tmp);
+ if (ret) {
+ dev_err(dev, "cluster %#x: channel %#x: missing purpose: %d\n",
+ cluster->id, channel->id, ret);
+ return ret;
+ }
+
+ channel->purpose = tmp;
+
+ ret = fwnode_property_read_u32(channel_node,
+ "mipi-sdca-cluster-channel-relationship",
+ &tmp);
+ if (ret) {
+ dev_err(dev, "cluster %#x: channel %#x: missing relationship: %d\n",
+ cluster->id, channel->id, ret);
+ return ret;
+ }
+
+ channel->relationship = tmp;
+
+ dev_dbg(dev, "cluster %#x: channel id %#x purpose %#x relationship %#x\n",
+ cluster->id, channel->id, channel->purpose, channel->relationship);
+
+ return 0;
+}
+
+static int find_sdca_cluster_channels(struct device *dev,
+ struct fwnode_handle *cluster_node,
+ struct sdca_cluster *cluster)
+{
+ struct sdca_channel *channels;
+ u32 num_channels;
+ int i, ret;
+
+ ret = fwnode_property_read_u32(cluster_node, "mipi-sdca-channel-count",
+ &num_channels);
+ if (ret < 0) {
+ dev_err(dev, "cluster %#x: failed to read channel list: %d\n",
+ cluster->id, ret);
+ return ret;
+ } else if (num_channels > SDCA_MAX_CHANNEL_COUNT) {
+ dev_err(dev, "cluster %#x: maximum number of channels exceeded\n",
+ cluster->id);
+ return -EINVAL;
+ }
+
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < num_channels; i++) {
+ char channel_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *channel_node;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(channel_property, sizeof(channel_property),
+ "mipi-sdca-channel-%d-subproperties", i + 1);
+
+ channel_node = fwnode_get_named_child_node(cluster_node, channel_property);
+ if (!channel_node) {
+ dev_err(dev, "cluster %#x: channel node %s not found\n",
+ cluster->id, channel_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_cluster_channel(dev, cluster, channel_node, &channels[i]);
+ fwnode_handle_put(channel_node);
+ if (ret)
+ return ret;
+ }
+
+ cluster->num_channels = num_channels;
+ cluster->channels = channels;
+
+ return 0;
+}
+
+static int find_sdca_clusters(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ u32 *cluster_list __free(kfree) = NULL;
+ struct sdca_cluster *clusters;
+ int num_clusters;
+ int i, ret;
+
+ num_clusters = fwnode_property_count_u32(function_node, "mipi-sdca-cluster-id-list");
+ if (!num_clusters || num_clusters == -EINVAL) {
+ return 0;
+ } else if (num_clusters < 0) {
+ dev_err(dev, "%pfwP: failed to read cluster id list: %d\n",
+ function_node, num_clusters);
+ return num_clusters;
+ } else if (num_clusters > SDCA_MAX_CLUSTER_COUNT) {
+ dev_err(dev, "%pfwP: maximum number of clusters exceeded\n", function_node);
+ return -EINVAL;
+ }
+
+ clusters = devm_kcalloc(dev, num_clusters, sizeof(*clusters), GFP_KERNEL);
+ if (!clusters)
+ return -ENOMEM;
+
+ cluster_list = kcalloc(num_clusters, sizeof(*cluster_list), GFP_KERNEL);
+ if (!cluster_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(function_node, "mipi-sdca-cluster-id-list",
+ cluster_list, num_clusters);
+
+ for (i = 0; i < num_clusters; i++)
+ clusters[i].id = cluster_list[i];
+
+ /* now read subproperties */
+ for (i = 0; i < num_clusters; i++) {
+ char cluster_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *cluster_node;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(cluster_property, sizeof(cluster_property),
+ "mipi-sdca-cluster-id-0x%X-subproperties", clusters[i].id);
+
+ cluster_node = fwnode_get_named_child_node(function_node, cluster_property);
+ if (!cluster_node) {
+ dev_err(dev, "%pfwP: cluster node %s not found\n",
+ function_node, cluster_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_cluster_channels(dev, cluster_node, &clusters[i]);
+ fwnode_handle_put(cluster_node);
+ if (ret)
+ return ret;
+ }
+
+ function->num_clusters = num_clusters;
+ function->clusters = clusters;
+
+ return 0;
+}
+
+static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ static const int mult_fileset = 3;
+ char fileset_name[SDCA_PROPERTY_LENGTH];
+ u32 *filesets_list __free(kfree) = NULL;
+ struct sdca_fdl_set *sets;
+ int num_sets;
+ int i, j;
+
+ num_sets = fwnode_property_count_u32(function_node,
+ "mipi-sdca-file-set-id-list");
+ if (num_sets == 0 || num_sets == -EINVAL) {
+ return 0;
+ } else if (num_sets < 0) {
+ dev_err(dev, "%pfwP: failed to read file set list: %d\n",
+ function_node, num_sets);
+ return num_sets;
+ }
+
+ filesets_list = kcalloc(num_sets, sizeof(u32), GFP_KERNEL);
+ if (!filesets_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(function_node, "mipi-sdca-file-set-id-list",
+ filesets_list, num_sets);
+
+ sets = devm_kcalloc(dev, num_sets, sizeof(struct sdca_fdl_set), GFP_KERNEL);
+ if (!sets)
+ return -ENOMEM;
+
+ for (i = 0; i < num_sets; i++) {
+ u32 *fileset_entries __free(kfree) = NULL;
+ struct sdca_fdl_set *set = &sets[i];
+ struct sdca_fdl_file *files;
+ int num_files, num_entries;
+
+ snprintf(fileset_name, sizeof(fileset_name),
+ "mipi-sdca-file-set-id-0x%X", filesets_list[i]);
+
+ num_entries = fwnode_property_count_u32(function_node, fileset_name);
+ if (num_entries <= 0) {
+ dev_err(dev, "%pfwP: file set %d missing entries: %d\n",
+ function_node, filesets_list[i], num_entries);
+ return -EINVAL;
+ } else if (num_entries % mult_fileset != 0) {
+ dev_err(dev, "%pfwP: file set %d files not multiple of %d\n",
+ function_node, filesets_list[i], mult_fileset);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "fileset: %#x\n", filesets_list[i]);
+
+ files = devm_kcalloc(dev, num_entries / mult_fileset,
+ sizeof(struct sdca_fdl_file), GFP_KERNEL);
+ if (!files)
+ return -ENOMEM;
+
+ fileset_entries = kcalloc(num_entries, sizeof(u32), GFP_KERNEL);
+ if (!fileset_entries)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(function_node, fileset_name,
+ fileset_entries, num_entries);
+
+ for (j = 0, num_files = 0; j < num_entries; num_files++) {
+ struct sdca_fdl_file *file = &files[num_files];
+
+ file->vendor_id = fileset_entries[j++];
+ file->file_id = fileset_entries[j++];
+ file->fdl_offset = fileset_entries[j++];
+
+ dev_dbg(dev, "file: %#x, vendor: %#x, offset: %#x\n",
+ file->file_id, file->vendor_id, file->fdl_offset);
+ }
+
+ set->id = filesets_list[i];
+ set->num_files = num_files;
+ set->files = files;
+ }
+
+ function->fdl_data.swft = sdw->sdca_data.swft;
+ function->fdl_data.num_sets = num_sets;
+ function->fdl_data.sets = sets;
+
+ return 0;
+}
+
+/**
+ * sdca_parse_function - parse ACPI DisCo for a Function
+ * @dev: Pointer to device against which function data will be allocated.
+ * @sdw: SoundWire slave device to be processed.
+ * @function_desc: Pointer to the Function short descriptor.
+ * @function: Pointer to the Function information, to be populated.
+ *
+ * Return: Returns 0 for success.
+ */
+int sdca_parse_function(struct device *dev, struct sdw_slave *sdw,
+ struct sdca_function_desc *function_desc,
+ struct sdca_function_data *function)
+{
+ u32 tmp;
+ int ret;
+
+ function->desc = function_desc;
+
+ ret = fwnode_property_read_u32(function_desc->node,
+ "mipi-sdca-function-busy-max-delay", &tmp);
+ if (!ret)
+ function->busy_max_delay = tmp;
+
+ ret = fwnode_property_read_u32(function_desc->node,
+ "mipi-sdca-function-reset-max-delay", &tmp);
+ if (!ret)
+ function->reset_max_delay = tmp;
+
+ dev_dbg(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n",
+ function->desc->node, function->desc->name,
+ function->busy_max_delay, function->reset_max_delay);
+
+ ret = find_sdca_init_table(dev, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ ret = find_sdca_entities(dev, sdw, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ ret = find_sdca_connections(dev, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ ret = find_sdca_clusters(dev, function_desc->node, function);
+ if (ret < 0)
+ return ret;
+
+ ret = find_sdca_filesets(dev, sdw, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA");
+
+const char *sdca_find_terminal_name(enum sdca_terminal_type type)
+{
+ switch (type) {
+ case SDCA_TERM_TYPE_LINEIN_STEREO:
+ return SDCA_TERM_TYPE_LINEIN_STEREO_NAME;
+ case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
+ return SDCA_TERM_TYPE_LINEIN_FRONT_LR_NAME;
+ case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
+ return SDCA_TERM_TYPE_LINEIN_CENTER_LFE_NAME;
+ case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
+ return SDCA_TERM_TYPE_LINEIN_SURROUND_LR_NAME;
+ case SDCA_TERM_TYPE_LINEIN_REAR_LR:
+ return SDCA_TERM_TYPE_LINEIN_REAR_LR_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_STEREO:
+ return SDCA_TERM_TYPE_LINEOUT_STEREO_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
+ return SDCA_TERM_TYPE_LINEOUT_FRONT_LR_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
+ return SDCA_TERM_TYPE_LINEOUT_CENTER_LFE_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
+ return SDCA_TERM_TYPE_LINEOUT_SURROUND_LR_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
+ return SDCA_TERM_TYPE_LINEOUT_REAR_LR_NAME;
+ case SDCA_TERM_TYPE_MIC_JACK:
+ return SDCA_TERM_TYPE_MIC_JACK_NAME;
+ case SDCA_TERM_TYPE_STEREO_JACK:
+ return SDCA_TERM_TYPE_STEREO_JACK_NAME;
+ case SDCA_TERM_TYPE_FRONT_LR_JACK:
+ return SDCA_TERM_TYPE_FRONT_LR_JACK_NAME;
+ case SDCA_TERM_TYPE_CENTER_LFE_JACK:
+ return SDCA_TERM_TYPE_CENTER_LFE_JACK_NAME;
+ case SDCA_TERM_TYPE_SURROUND_LR_JACK:
+ return SDCA_TERM_TYPE_SURROUND_LR_JACK_NAME;
+ case SDCA_TERM_TYPE_REAR_LR_JACK:
+ return SDCA_TERM_TYPE_REAR_LR_JACK_NAME;
+ case SDCA_TERM_TYPE_HEADPHONE_JACK:
+ return SDCA_TERM_TYPE_HEADPHONE_JACK_NAME;
+ case SDCA_TERM_TYPE_HEADSET_JACK:
+ return SDCA_TERM_TYPE_HEADSET_JACK_NAME;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_NS(sdca_find_terminal_name, "SND_SOC_SDCA");
+
+struct sdca_control *sdca_selector_find_control(struct device *dev,
+ struct sdca_entity *entity,
+ const int sel)
+{
+ int i;
+
+ for (i = 0; i < entity->num_controls; i++) {
+ struct sdca_control *control = &entity->controls[i];
+
+ if (control->sel == sel)
+ return control;
+ }
+
+ dev_err(dev, "%s: control %#x: missing\n", entity->label, sel);
+ return NULL;
+}
+EXPORT_SYMBOL_NS(sdca_selector_find_control, "SND_SOC_SDCA");
+
+struct sdca_control_range *sdca_control_find_range(struct device *dev,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ int cols, int rows)
+{
+ struct sdca_control_range *range = &control->range;
+
+ if ((cols && range->cols != cols) || (rows && range->rows != rows) ||
+ !range->data) {
+ dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n",
+ entity->label, control->sel, range->cols, range->rows);
+ return NULL;
+ }
+
+ return range;
+}
+EXPORT_SYMBOL_NS(sdca_control_find_range, "SND_SOC_SDCA");
+
+struct sdca_control_range *sdca_selector_find_range(struct device *dev,
+ struct sdca_entity *entity,
+ int sel, int cols, int rows)
+{
+ struct sdca_control *control;
+
+ control = sdca_selector_find_control(dev, entity, sel);
+ if (!control)
+ return NULL;
+
+ return sdca_control_find_range(dev, entity, control, cols, rows);
+}
+EXPORT_SYMBOL_NS(sdca_selector_find_range, "SND_SOC_SDCA");
+
+struct sdca_cluster *sdca_id_find_cluster(struct device *dev,
+ struct sdca_function_data *function,
+ const int id)
+{
+ int i;
+
+ for (i = 0; i < function->num_clusters; i++) {
+ struct sdca_cluster *cluster = &function->clusters[i];
+
+ if (cluster->id == id)
+ return cluster;
+ }
+
+ dev_err(dev, "%s: cluster %#x: missing\n", function->desc->name, id);
+ return NULL;
+}
+EXPORT_SYMBOL_NS(sdca_id_find_cluster, "SND_SOC_SDCA");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SDCA library");
diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c
new file mode 100644
index 000000000000..abbd56a3d297
--- /dev/null
+++ b/sound/soc/sdca/sdca_hid.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_hid.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_ump.h>
+
+static int sdwhid_parse(struct hid_device *hid)
+{
+ struct sdca_entity *entity = hid->driver_data;
+ unsigned int rsize;
+ int ret;
+
+ rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength);
+
+ if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+ dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize);
+ return -EINVAL;
+ }
+
+ ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize);
+
+ if (!ret)
+ return 0;
+
+ dev_err(&hid->dev, "parsing report descriptor failed\n");
+ return ret;
+}
+
+static int sdwhid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void sdwhid_stop(struct hid_device *hid)
+{
+}
+
+static int sdwhid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype, int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ /* not implemented yet */
+ return 0;
+ case HID_REQ_SET_REPORT:
+ /* not implemented yet */
+ return 0;
+ default:
+ return -EIO;
+ }
+}
+
+static int sdwhid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void sdwhid_close(struct hid_device *hid)
+{
+}
+
+static const struct hid_ll_driver sdw_hid_driver = {
+ .parse = sdwhid_parse,
+ .start = sdwhid_start,
+ .stop = sdwhid_stop,
+ .open = sdwhid_open,
+ .close = sdwhid_close,
+ .raw_request = sdwhid_raw_request,
+};
+
+int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw,
+ struct sdca_entity *entity)
+{
+ struct sdw_bus *bus = sdw->bus;
+ struct hid_device *hid;
+ int ret;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid->ll_driver = &sdw_hid_driver;
+
+ hid->dev.parent = dev;
+ hid->bus = BUS_SDW;
+ hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID);
+
+ snprintf(hid->name, sizeof(hid->name),
+ "HID sdw:%01x:%01x:%04x:%04x:%02x",
+ bus->controller_id, bus->link_id, sdw->id.mfg_id,
+ sdw->id.part_id, sdw->id.class_id);
+
+ snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name);
+
+ hid->driver_data = entity;
+
+ ret = hid_add_device(hid);
+ if (ret && ret != -ENODEV) {
+ dev_err(dev, "can't add hid device: %d\n", ret);
+ hid_destroy_device(hid);
+ return ret;
+ }
+
+ entity->hide.hid = hid;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA");
+
+/**
+ * sdca_hid_process_report - read a HID event from the device and report
+ * @interrupt: Pointer to the SDCA interrupt information structure.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_hid_process_report(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct hid_device *hid = interrupt->entity->hide.hid;
+ void *val __free(kfree) = NULL;
+ int len, ret;
+
+ ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
+ interrupt->function, interrupt->entity,
+ interrupt->control);
+ if (ret)
+ return ret;
+
+ len = sdca_ump_read_message(dev, interrupt->device_regmap,
+ interrupt->function_regmap,
+ interrupt->function, interrupt->entity,
+ SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET,
+ SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH, &val);
+ if (len < 0)
+ return len;
+
+ ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
+ interrupt->function, interrupt->entity,
+ interrupt->control);
+ if (ret)
+ return ret;
+
+ ret = hid_input_report(hid, HID_INPUT_REPORT, val, len, true);
+ if (ret < 0) {
+ dev_err(dev, "failed to report hid event: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_hid_process_report, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
new file mode 100644
index 000000000000..8f6a2adfb6fb
--- /dev/null
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/sdca.h>
+#include <sound/sdca_fdl.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_hid.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_ump.h>
+#include <sound/soc-component.h>
+#include <sound/soc.h>
+
+#define IRQ_SDCA(number) REGMAP_IRQ_REG(number, ((number) / BITS_PER_BYTE), \
+ SDW_SCP_SDCA_INTMASK_SDCA_##number)
+
+static const struct regmap_irq regmap_irqs[SDCA_MAX_INTERRUPTS] = {
+ IRQ_SDCA(0),
+ IRQ_SDCA(1),
+ IRQ_SDCA(2),
+ IRQ_SDCA(3),
+ IRQ_SDCA(4),
+ IRQ_SDCA(5),
+ IRQ_SDCA(6),
+ IRQ_SDCA(7),
+ IRQ_SDCA(8),
+ IRQ_SDCA(9),
+ IRQ_SDCA(10),
+ IRQ_SDCA(11),
+ IRQ_SDCA(12),
+ IRQ_SDCA(13),
+ IRQ_SDCA(14),
+ IRQ_SDCA(15),
+ IRQ_SDCA(16),
+ IRQ_SDCA(17),
+ IRQ_SDCA(18),
+ IRQ_SDCA(19),
+ IRQ_SDCA(20),
+ IRQ_SDCA(21),
+ IRQ_SDCA(22),
+ IRQ_SDCA(23),
+ IRQ_SDCA(24),
+ IRQ_SDCA(25),
+ IRQ_SDCA(26),
+ IRQ_SDCA(27),
+ IRQ_SDCA(28),
+ IRQ_SDCA(29),
+ IRQ_SDCA(30),
+};
+
+static const struct regmap_irq_chip sdca_irq_chip = {
+ .name = "sdca_irq",
+
+ .status_base = SDW_SCP_SDCA_INT1,
+ .unmask_base = SDW_SCP_SDCA_INTMASK1,
+ .ack_base = SDW_SCP_SDCA_INT1,
+ .num_regs = 4,
+
+ .irqs = regmap_irqs,
+ .num_irqs = SDCA_MAX_INTERRUPTS,
+
+ .runtime_pm = true,
+};
+
+static irqreturn_t base_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->dev;
+
+ dev_info(dev, "%s irq without full handling\n", interrupt->name);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t function_status_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->dev;
+ irqreturn_t irqret = IRQ_NONE;
+ unsigned int reg, val;
+ unsigned long status;
+ unsigned int mask;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume for function status: %d\n", ret);
+ goto error;
+ }
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ interrupt->control->sel, 0);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read function status: %d\n", ret);
+ goto error;
+ }
+
+ dev_dbg(dev, "function status: %#x\n", val);
+
+ status = val;
+ for_each_set_bit(mask, &status, BITS_PER_BYTE) {
+ mask = 1 << mask;
+
+ switch (mask) {
+ case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION:
+ //FIXME: Add init writes
+ break;
+ case SDCA_CTL_ENTITY_0_FUNCTION_FAULT:
+ dev_err(dev, "function fault\n");
+ break;
+ case SDCA_CTL_ENTITY_0_UMP_SEQUENCE_FAULT:
+ dev_err(dev, "ump sequence fault\n");
+ break;
+ case SDCA_CTL_ENTITY_0_FUNCTION_BUSY:
+ dev_info(dev, "unexpected function busy\n");
+ break;
+ case SDCA_CTL_ENTITY_0_DEVICE_NEWLY_ATTACHED:
+ case SDCA_CTL_ENTITY_0_INTS_DISABLED_ABNORMALLY:
+ case SDCA_CTL_ENTITY_0_STREAMING_STOPPED_ABNORMALLY:
+ case SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET:
+ break;
+ }
+ }
+
+ ret = regmap_write(interrupt->function_regmap, reg, val);
+ if (ret < 0) {
+ dev_err(dev, "failed to clear function status: %d\n", ret);
+ goto error;
+ }
+
+ irqret = IRQ_HANDLED;
+error:
+ pm_runtime_put(dev);
+ return irqret;
+}
+
+static irqreturn_t detected_mode_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->dev;
+ struct snd_soc_component *component = interrupt->component;
+ struct snd_soc_card *card = component->card;
+ struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
+ struct snd_kcontrol *kctl = interrupt->priv;
+ struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ struct soc_enum *soc_enum;
+ irqreturn_t irqret = IRQ_NONE;
+ unsigned int reg, val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume for detected mode: %d\n", ret);
+ goto error;
+ }
+
+ if (!kctl) {
+ const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+ interrupt->entity->label,
+ SDCA_CTL_SELECTED_MODE_NAME);
+
+ if (!name)
+ goto error;
+
+ kctl = snd_soc_component_get_kcontrol(component, name);
+ if (!kctl) {
+ dev_dbg(dev, "control not found: %s\n", name);
+ goto error;
+ }
+
+ interrupt->priv = kctl;
+ }
+
+ soc_enum = (struct soc_enum *)kctl->private_value;
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ interrupt->control->sel, 0);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read detected mode: %d\n", ret);
+ goto error;
+ }
+
+ switch (val) {
+ case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
+ case SDCA_DETECTED_MODE_JACK_UNKNOWN:
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
+ interrupt->entity->id,
+ SDCA_CTL_GE_SELECTED_MODE, 0);
+
+ /*
+ * Selected mode is not normally marked as volatile register
+ * (RW), but here force a read from the hardware. If the
+ * detected mode is unknown we need to see what the device
+ * selected as a "safe" option.
+ */
+ regcache_drop_region(interrupt->function_regmap, reg, reg);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "failed to re-check selected mode: %d\n", ret);
+ goto error;
+ }
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
+
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ goto error;
+
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
+
+ down_write(rwsem);
+ ret = kctl->put(kctl, ucontrol);
+ up_write(rwsem);
+ if (ret < 0) {
+ dev_err(dev, "failed to update selected mode: %d\n", ret);
+ goto error;
+ }
+
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+
+ irqret = IRQ_HANDLED;
+error:
+ pm_runtime_put(dev);
+ return irqret;
+}
+
+static irqreturn_t hid_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->dev;
+ irqreturn_t irqret = IRQ_NONE;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume for hid: %d\n", ret);
+ goto error;
+ }
+
+ ret = sdca_hid_process_report(interrupt);
+ if (ret)
+ goto error;
+
+ irqret = IRQ_HANDLED;
+error:
+ pm_runtime_put(dev);
+ return irqret;
+}
+
+static irqreturn_t fdl_owner_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->dev;
+ irqreturn_t irqret = IRQ_NONE;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume for fdl: %d\n", ret);
+ goto error;
+ }
+
+ ret = sdca_fdl_process(interrupt);
+ if (ret)
+ goto error;
+
+ irqret = IRQ_HANDLED;
+error:
+ pm_runtime_put(dev);
+ return irqret;
+}
+
+static int sdca_irq_request_locked(struct device *dev,
+ struct sdca_interrupt_info *info,
+ int sdca_irq, const char *name,
+ irq_handler_t handler, void *data)
+{
+ int irq;
+ int ret;
+
+ irq = regmap_irq_get_virq(info->irq_data, sdca_irq);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, handler,
+ IRQF_ONESHOT, name, data);
+ if (ret)
+ return ret;
+
+ info->irqs[sdca_irq].irq = irq;
+
+ dev_dbg(dev, "requested irq %d for %s\n", irq, name);
+
+ return 0;
+}
+
+/**
+ * sdca_request_irq - request an individual SDCA interrupt
+ * @dev: Pointer to the struct device against which things should be allocated.
+ * @interrupt_info: Pointer to the interrupt information structure.
+ * @sdca_irq: SDCA interrupt position.
+ * @name: Name to be given to the IRQ.
+ * @handler: A callback thread function to be called for the IRQ.
+ * @data: Private data pointer that will be passed to the handler.
+ *
+ * Typically this is handled internally by sdca_irq_populate, however if
+ * a device requires custom IRQ handling this can be called manually before
+ * calling sdca_irq_populate, which will then skip that IRQ whilst processing.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info,
+ int sdca_irq, const char *name, irq_handler_t handler,
+ void *data)
+{
+ int ret;
+
+ if (sdca_irq < 0 || sdca_irq >= SDCA_MAX_INTERRUPTS) {
+ dev_err(dev, "bad irq request: %d\n", sdca_irq);
+ return -EINVAL;
+ }
+
+ guard(mutex)(&info->irq_lock);
+
+ ret = sdca_irq_request_locked(dev, info, sdca_irq, name, handler, data);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n", name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_data_populate - Populate common interrupt data
+ * @dev: Pointer to the Function device.
+ * @regmap: Pointer to the Function regmap.
+ * @component: Pointer to the ASoC component for the Function.
+ * @function: Pointer to the SDCA Function.
+ * @entity: Pointer to the SDCA Entity.
+ * @control: Pointer to the SDCA Control.
+ * @interrupt: Pointer to the SDCA interrupt for this IRQ.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_data_populate(struct device *dev, struct regmap *regmap,
+ struct snd_soc_component *component,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct sdca_interrupt *interrupt)
+{
+ const char *name;
+
+ if (!dev && component)
+ dev = component->dev;
+ if (!dev)
+ return -ENODEV;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name,
+ entity->label, control->label);
+ if (!name)
+ return -ENOMEM;
+
+ interrupt->name = name;
+ interrupt->dev = dev;
+ if (!regmap && component)
+ interrupt->function_regmap = component->regmap;
+ else
+ interrupt->function_regmap = regmap;
+ interrupt->component = component;
+ interrupt->function = function;
+ interrupt->entity = entity;
+ interrupt->control = control;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA");
+
+static struct sdca_interrupt *get_interrupt_data(struct device *dev, int irq,
+ struct sdca_interrupt_info *info)
+{
+ if (irq == SDCA_NO_INTERRUPT) {
+ return NULL;
+ } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) {
+ dev_err(dev, "bad irq position: %d\n", irq);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (info->irqs[irq].irq) {
+ dev_dbg(dev, "skipping irq %d, already requested\n", irq);
+ return NULL;
+ }
+
+ return &info->irqs[irq];
+}
+
+/**
+ * sdca_irq_populate_early - process pre-audio card IRQ registrations
+ * @dev: Device pointer for SDCA Function.
+ * @regmap: Regmap pointer for the SDCA Function.
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * This is intended to be used as part of the Function boot process. It
+ * can be called before the soundcard is registered (ie. doesn't depend
+ * on component) and will register the FDL interrupts.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ int i, j;
+
+ guard(mutex)(&info->irq_lock);
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ for (j = 0; j < entity->num_controls; j++) {
+ struct sdca_control *control = &entity->controls[j];
+ int irq = control->interrupt_position;
+ struct sdca_interrupt *interrupt;
+ int ret;
+
+ interrupt = get_interrupt_data(dev, irq, info);
+ if (IS_ERR(interrupt))
+ return PTR_ERR(interrupt);
+ else if (!interrupt)
+ continue;
+
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ ret = sdca_irq_data_populate(dev, regmap, NULL,
+ function, entity,
+ control, interrupt);
+ if (ret)
+ return ret;
+
+ ret = sdca_fdl_alloc_state(interrupt);
+ if (ret)
+ return ret;
+
+ ret = sdca_irq_request_locked(dev, info, irq,
+ interrupt->name,
+ fdl_owner_handler,
+ interrupt);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n",
+ interrupt->name, ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_populate_early, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_populate - Request all the individual IRQs for an SDCA Function
+ * @function: Pointer to the SDCA Function.
+ * @component: Pointer to the ASoC component for the Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_populate(struct sdca_function_data *function,
+ struct snd_soc_component *component,
+ struct sdca_interrupt_info *info)
+{
+ struct device *dev = component->dev;
+ int i, j;
+
+ guard(mutex)(&info->irq_lock);
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ for (j = 0; j < entity->num_controls; j++) {
+ struct sdca_control *control = &entity->controls[j];
+ int irq = control->interrupt_position;
+ struct sdca_interrupt *interrupt;
+ irq_handler_t handler;
+ int ret;
+
+ interrupt = get_interrupt_data(dev, irq, info);
+ if (IS_ERR(interrupt))
+ return PTR_ERR(interrupt);
+ else if (!interrupt)
+ continue;
+
+ ret = sdca_irq_data_populate(dev, NULL, component,
+ function, entity, control,
+ interrupt);
+ if (ret)
+ return ret;
+
+ handler = base_handler;
+
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ handler = function_status_handler;
+ break;
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ handler = detected_mode_handler;
+ break;
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ ret = sdca_fdl_alloc_state(interrupt);
+ if (ret)
+ return ret;
+
+ handler = fdl_owner_handler;
+ break;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ handler = hid_handler;
+ break;
+ default:
+ break;
+ }
+
+ ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
+ handler, interrupt);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n",
+ interrupt->name, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_allocate - allocate an SDCA interrupt structure for a device
+ * @sdev: Device pointer against which things should be allocated.
+ * @regmap: regmap to be used for accessing the SDCA IRQ registers.
+ * @irq: The interrupt number.
+ *
+ * Typically this would be called from the top level driver for the whole
+ * SDCA device, as only a single instance is required across all Functions
+ * on the device.
+ *
+ * Return: A pointer to the allocated sdca_interrupt_info struct, or an
+ * error code.
+ */
+struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
+ struct regmap *regmap, int irq)
+{
+ struct sdca_interrupt_info *info;
+ int ret, i;
+
+ info = devm_kzalloc(sdev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->irq_chip = sdca_irq_chip;
+
+ for (i = 0; i < ARRAY_SIZE(info->irqs); i++)
+ info->irqs[i].device_regmap = regmap;
+
+ ret = devm_mutex_init(sdev, &info->irq_lock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_regmap_add_irq_chip(sdev, regmap, irq, IRQF_ONESHOT, 0,
+ &info->irq_chip, &info->irq_data);
+ if (ret) {
+ dev_err(sdev, "failed to register irq chip: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ dev_dbg(sdev, "registered on irq %d\n", irq);
+
+ return info;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c
new file mode 100644
index 000000000000..2cca9a9c71ea
--- /dev/null
+++ b/sound/soc/sdca/sdca_regmap.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/bitops.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/types.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_regmap.h>
+
+static struct sdca_entity *
+function_find_entity(struct sdca_function_data *function, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < function->num_entities; i++)
+ if (SDW_SDCA_CTL_ENT(reg) == function->entities[i].id)
+ return &function->entities[i];
+
+ return NULL;
+}
+
+static struct sdca_control *
+entity_find_control(struct sdca_entity *entity, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < entity->num_controls; i++) {
+ if (SDW_SDCA_CTL_CSEL(reg) == entity->controls[i].sel)
+ return &entity->controls[i];
+ }
+
+ return NULL;
+}
+
+static struct sdca_control *
+function_find_control(struct sdca_function_data *function, unsigned int reg)
+{
+ struct sdca_entity *entity;
+
+ entity = function_find_entity(function, reg);
+ if (!entity)
+ return NULL;
+
+ return entity_find_control(entity, reg);
+}
+
+/**
+ * sdca_regmap_readable - return if a given SDCA Control is readable
+ * @function: Pointer to the Function information.
+ * @reg: Register address/Control to be processed.
+ *
+ * Return: Returns true if the register is readable.
+ */
+bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg)
+{
+ struct sdca_control *control;
+
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return false;
+
+ control = function_find_control(function, reg);
+ if (!control)
+ return false;
+
+ if (!(BIT(SDW_SDCA_CTL_CNUM(reg)) & control->cn_list))
+ return false;
+
+ switch (control->mode) {
+ case SDCA_ACCESS_MODE_RW:
+ case SDCA_ACCESS_MODE_RO:
+ case SDCA_ACCESS_MODE_RW1S:
+ case SDCA_ACCESS_MODE_RW1C:
+ if (SDW_SDCA_NEXT_CTL(0) & reg)
+ return false;
+ fallthrough;
+ case SDCA_ACCESS_MODE_DUAL:
+ /* No access to registers marked solely for device use */
+ return control->layers & ~SDCA_ACCESS_LAYER_DEVICE;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS(sdca_regmap_readable, "SND_SOC_SDCA");
+
+/**
+ * sdca_regmap_writeable - return if a given SDCA Control is writeable
+ * @function: Pointer to the Function information.
+ * @reg: Register address/Control to be processed.
+ *
+ * Return: Returns true if the register is writeable.
+ */
+bool sdca_regmap_writeable(struct sdca_function_data *function, unsigned int reg)
+{
+ struct sdca_control *control;
+
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return false;
+
+ control = function_find_control(function, reg);
+ if (!control)
+ return false;
+
+ if (!(BIT(SDW_SDCA_CTL_CNUM(reg)) & control->cn_list))
+ return false;
+
+ switch (control->mode) {
+ case SDCA_ACCESS_MODE_RW:
+ case SDCA_ACCESS_MODE_RW1S:
+ case SDCA_ACCESS_MODE_RW1C:
+ if (SDW_SDCA_NEXT_CTL(0) & reg)
+ return false;
+ fallthrough;
+ case SDCA_ACCESS_MODE_DUAL:
+ /* No access to registers marked solely for device use */
+ return control->layers & ~SDCA_ACCESS_LAYER_DEVICE;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS(sdca_regmap_writeable, "SND_SOC_SDCA");
+
+/**
+ * sdca_regmap_volatile - return if a given SDCA Control is volatile
+ * @function: Pointer to the Function information.
+ * @reg: Register address/Control to be processed.
+ *
+ * Return: Returns true if the register is volatile.
+ */
+bool sdca_regmap_volatile(struct sdca_function_data *function, unsigned int reg)
+{
+ struct sdca_control *control;
+
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return false;
+
+ control = function_find_control(function, reg);
+ if (!control)
+ return false;
+
+ return control->is_volatile;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_volatile, "SND_SOC_SDCA");
+
+/**
+ * sdca_regmap_deferrable - return if a given SDCA Control is deferrable
+ * @function: Pointer to the Function information.
+ * @reg: Register address/Control to be processed.
+ *
+ * Return: Returns true if the register is deferrable.
+ */
+bool sdca_regmap_deferrable(struct sdca_function_data *function, unsigned int reg)
+{
+ struct sdca_control *control;
+
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return false;
+
+ control = function_find_control(function, reg);
+ if (!control)
+ return false;
+
+ return control->deferrable;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_deferrable, "SND_SOC_SDCA");
+
+/**
+ * sdca_regmap_mbq_size - return size in bytes of a given SDCA Control
+ * @function: Pointer to the Function information.
+ * @reg: Register address/Control to be processed.
+ *
+ * Return: Returns the size in bytes of the Control.
+ */
+int sdca_regmap_mbq_size(struct sdca_function_data *function, unsigned int reg)
+{
+ struct sdca_control *control;
+
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return -EINVAL;
+
+ control = function_find_control(function, reg);
+ if (!control)
+ return -EINVAL;
+
+ return clamp_val(control->nbits / BITS_PER_BYTE, sizeof(u8), sizeof(u32));
+}
+EXPORT_SYMBOL_NS(sdca_regmap_mbq_size, "SND_SOC_SDCA");
+
+/**
+ * sdca_regmap_count_constants - count the number of DisCo constant Controls
+ * @dev: Pointer to the device.
+ * @function: Pointer to the Function information, to be parsed.
+ *
+ * This function returns the number of DisCo constant Controls present
+ * in a function. Typically this information will be used to populate
+ * the regmap defaults array, allowing drivers to access the values of
+ * DisCo constants as any other physical register.
+ *
+ * Return: Returns number of DisCo constant controls, or a negative error
+ * code on failure.
+ */
+int sdca_regmap_count_constants(struct device *dev,
+ struct sdca_function_data *function)
+{
+ int nconsts = 0;
+ int i, j;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ for (j = 0; j < entity->num_controls; j++) {
+ if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC)
+ nconsts += hweight64(entity->controls[j].cn_list);
+ }
+ }
+
+ return nconsts;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_count_constants, "SND_SOC_SDCA");
+
+/**
+ * sdca_regmap_populate_constants - fill an array with DisCo constant values
+ * @dev: Pointer to the device.
+ * @function: Pointer to the Function information, to be parsed.
+ * @consts: Pointer to the array which should be filled with the DisCo
+ * constant values.
+ *
+ * This function will populate a regmap struct reg_default array with
+ * the values of the DisCo constants for a given Function. This
+ * allows to access the values of DisCo constants the same as any
+ * other physical register.
+ *
+ * Return: Returns the number of constants populated on success, a negative
+ * error code on failure.
+ */
+int sdca_regmap_populate_constants(struct device *dev,
+ struct sdca_function_data *function,
+ struct reg_default *consts)
+{
+ int i, j, k, l;
+
+ for (i = 0, k = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ for (j = 0; j < entity->num_controls; j++) {
+ struct sdca_control *control = &entity->controls[j];
+ int cn;
+
+ if (control->mode != SDCA_ACCESS_MODE_DC)
+ continue;
+
+ l = 0;
+ for_each_set_bit(cn, (unsigned long *)&control->cn_list,
+ BITS_PER_TYPE(control->cn_list)) {
+ consts[k].reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id,
+ control->sel, cn);
+ consts[k].def = control->values[l];
+ k++;
+ l++;
+ }
+ }
+ }
+
+ return k;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA");
+
+static int populate_control_defaults(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control)
+{
+ int i, ret;
+ int cn;
+
+ if (control->mode == SDCA_ACCESS_MODE_DC)
+ return 0;
+
+ if (control->layers & SDCA_ACCESS_LAYER_DEVICE)
+ return 0;
+
+ i = 0;
+ for_each_set_bit(cn, (unsigned long *)&control->cn_list,
+ BITS_PER_TYPE(control->cn_list)) {
+ unsigned int reg, val;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, cn);
+
+ if (control->has_default || control->has_fixed) {
+ ret = regmap_write(regmap, reg, control->values[i]);
+ if (ret) {
+ dev_err(dev, "Failed to write default %#x: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ i++;
+ } else if (!control->is_volatile) {
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read initial %#x: %d\n",
+ reg, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * sdca_regmap_write_defaults - write out DisCo defaults to device
+ * @dev: Pointer to the device.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information, to be parsed.
+ *
+ * This function will write out to the hardware all the DisCo default and
+ * fixed value controls. This will cause them to be populated into the cache,
+ * and subsequent handling can be done through a cache sync. It will also
+ * read any non-volatile registers that don't have defaults/fixed values to
+ * populate those into the cache, this ensures they are available for reads
+ * even when the device is runtime suspended.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function)
+{
+ int i, j;
+ int ret;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ for (j = 0; j < entity->num_controls; j++) {
+ struct sdca_control *control = &entity->controls[j];
+
+ ret = populate_control_defaults(dev, regmap, function,
+ entity, control);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA");
+
+int sdca_regmap_write_init(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function)
+{
+ struct sdca_init_write *init = function->init_table;
+ int ret, i;
+
+ for (i = 0; i < function->num_init_table; i++) {
+ ret = regmap_write(regmap, init[i].addr, init[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_write_init, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c
new file mode 100644
index 000000000000..8aba3ff16872
--- /dev/null
+++ b/sound/soc/sdca/sdca_ump.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_ump.h>
+#include <sound/soc-component.h>
+#include <linux/soundwire/sdw_registers.h>
+
+/**
+ * sdca_ump_get_owner_host - check a UMP buffer is owned by the host
+ * @dev: Pointer to the struct device used for error messages.
+ * @function_regmap: Pointer to the regmap for the SDCA Function.
+ * @function: Pointer to the Function information.
+ * @entity: Pointer to the SDCA Entity.
+ * @control: Pointer to the SDCA Control for the UMP Owner.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_ump_get_owner_host(struct device *dev,
+ struct regmap *function_regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control)
+{
+ unsigned int reg, owner;
+ int ret;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ ret = regmap_read(function_regmap, reg, &owner);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to read UMP owner: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ if (owner != SDCA_UMP_OWNER_HOST) {
+ dev_err(dev, "%s: host is not the UMP owner\n", entity->label);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_ump_get_owner_host, "SND_SOC_SDCA");
+
+/**
+ * sdca_ump_set_owner_device - set a UMP buffer's ownership back to the device
+ * @dev: Pointer to the struct device used for error messages.
+ * @function_regmap: Pointer to the regmap for the SDCA Function.
+ * @function: Pointer to the Function information.
+ * @entity: Pointer to the SDCA Entity.
+ * @control: Pointer to the SDCA Control for the UMP Owner.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_ump_set_owner_device(struct device *dev,
+ struct regmap *function_regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control)
+{
+ unsigned int reg;
+ int ret;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ ret = regmap_write(function_regmap, reg, SDCA_UMP_OWNER_DEVICE);
+ if (ret < 0)
+ dev_err(dev, "%s: failed to write UMP owner: %d\n",
+ entity->label, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_ump_set_owner_device, "SND_SOC_SDCA");
+
+/**
+ * sdca_ump_read_message - read a UMP message from the device
+ * @dev: Pointer to the struct device used for error messages.
+ * @device_regmap: Pointer to the Device register map.
+ * @function_regmap: Pointer to the regmap for the SDCA Function.
+ * @function: Pointer to the Function information.
+ * @entity: Pointer to the SDCA Entity.
+ * @offset_sel: Control Selector for the UMP Offset Control.
+ * @length_sel: Control Selector for the UMP Length Control.
+ * @msg: Pointer that will be populated with an dynamically buffer
+ * containing the UMP message. Note this needs to be freed by the
+ * caller.
+ *
+ * The caller should first call sdca_ump_get_owner_host() to ensure the host
+ * currently owns the UMP buffer, and then this function can be used to
+ * retrieve a message. It is the callers responsibility to free the
+ * message once it is finished with it. Finally sdca_ump_set_owner_device()
+ * should be called to return the buffer to the device.
+ *
+ * Return: Returns the message length on success, and a negative error
+ * code on failure.
+ */
+int sdca_ump_read_message(struct device *dev,
+ struct regmap *device_regmap,
+ struct regmap *function_regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ unsigned int offset_sel, unsigned int length_sel,
+ void **msg)
+{
+ struct sdca_control_range *range;
+ unsigned int msg_offset, msg_len;
+ unsigned int buf_addr, buf_len;
+ unsigned int reg;
+ int ret;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, offset_sel, 0);
+ ret = regmap_read(function_regmap, reg, &msg_offset);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to read UMP offset: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ range = sdca_selector_find_range(dev, entity, offset_sel,
+ SDCA_MESSAGEOFFSET_NCOLS, 1);
+ if (!range)
+ return -ENOENT;
+
+ buf_addr = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS, 0);
+ buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0);
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, length_sel, 0);
+ ret = regmap_read(function_regmap, reg, &msg_len);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to read UMP length: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ if (msg_len > buf_len - msg_offset) {
+ dev_err(dev, "%s: message too big for UMP buffer: %d\n",
+ entity->label, msg_len);
+ return -EINVAL;
+ }
+
+ *msg = kmalloc(msg_len, GFP_KERNEL);
+ if (!*msg)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(device_regmap, buf_addr + msg_offset, *msg, msg_len);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to read UMP message: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ return msg_len;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_ump_read_message, "SND_SOC_SDCA");
+
+/**
+ * sdca_ump_write_message - write a UMP message to the device
+ * @dev: Pointer to the struct device used for error messages.
+ * @device_regmap: Pointer to the Device register map.
+ * @function_regmap: Pointer to the regmap for the SDCA Function.
+ * @function: Pointer to the Function information.
+ * @entity: Pointer to the SDCA Entity.
+ * @offset_sel: Control Selector for the UMP Offset Control.
+ * @msg_offset: Offset within the UMP buffer at which the message should
+ * be written.
+ * @length_sel: Control Selector for the UMP Length Control.
+ * @msg: Pointer to the data that should be written to the UMP buffer.
+ * @msg_len: Length of the message data in bytes.
+ *
+ * The caller should first call sdca_ump_get_owner_host() to ensure the host
+ * currently owns the UMP buffer, and then this function can be used to
+ * write a message. Finally sdca_ump_set_owner_device() should be called to
+ * return the buffer to the device, allowing the device to access the
+ * message.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_ump_write_message(struct device *dev,
+ struct regmap *device_regmap,
+ struct regmap *function_regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ unsigned int offset_sel, unsigned int msg_offset,
+ unsigned int length_sel,
+ void *msg, int msg_len)
+{
+ struct sdca_control_range *range;
+ unsigned int buf_addr, buf_len, ump_mode;
+ unsigned int reg;
+ int ret;
+
+ range = sdca_selector_find_range(dev, entity, offset_sel,
+ SDCA_MESSAGEOFFSET_NCOLS, 1);
+ if (!range)
+ return -ENOENT;
+
+ buf_addr = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS, 0);
+ buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0);
+ ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0);
+
+ if (msg_len > buf_len - msg_offset) {
+ dev_err(dev, "%s: message too big for UMP buffer: %d\n",
+ entity->label, msg_len);
+ return -EINVAL;
+ }
+
+ if (ump_mode != SDCA_UMP_MODE_DIRECT) {
+ dev_err(dev, "%s: only direct mode currently supported\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ ret = regmap_raw_write(device_regmap, buf_addr + msg_offset, msg, msg_len);
+ if (ret) {
+ dev_err(dev, "%s: failed to write UMP message: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, offset_sel, 0);
+ ret = regmap_write(function_regmap, reg, msg_offset);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to write UMP offset: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, length_sel, 0);
+ ret = regmap_write(function_regmap, reg, msg_len);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to write UMP length: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_ump_write_message, "SND_SOC_SDCA");
+
+void sdca_ump_cancel_timeout(struct delayed_work *work)
+{
+ cancel_delayed_work_sync(work);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_ump_cancel_timeout, "SND_SOC_SDCA");
+
+void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_us)
+{
+ if (!timeout_us)
+ return;
+
+ queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us));
+}
+EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA");
diff --git a/sound/soc/sdw_utils/Kconfig b/sound/soc/sdw_utils/Kconfig
new file mode 100644
index 000000000000..d915083c3889
--- /dev/null
+++ b/sound/soc/sdw_utils/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SOC_SDW_UTILS
+ tristate
+ help
+ This option enables to use SoundWire common helper functions and
+ SoundWire codec helper functions in machine driver.
diff --git a/sound/soc/sdw_utils/Makefile b/sound/soc/sdw_utils/Makefile
new file mode 100644
index 000000000000..e8bd5ffb1a6a
--- /dev/null
+++ b/sound/soc/sdw_utils/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-sdw-utils-y := soc_sdw_utils.o soc_sdw_dmic.o soc_sdw_rt_dmic.o \
+ soc_sdw_rt700.o soc_sdw_rt711.o \
+ soc_sdw_rt5682.o soc_sdw_rt_sdca_jack_common.o \
+ soc_sdw_rt_amp.o soc_sdw_rt_mf_sdca.o \
+ soc_sdw_bridge_cs35l56.o \
+ soc_sdw_cs42l42.o soc_sdw_cs42l43.o \
+ soc_sdw_cs42l45.o \
+ soc_sdw_cs_amp.o \
+ soc_sdw_maxim.o \
+ soc_sdw_ti_amp.o
+obj-$(CONFIG_SND_SOC_SDW_UTILS) += snd-soc-sdw-utils.o
diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c
new file mode 100644
index 000000000000..2a7109d53cbe
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2024 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_bridge_cs35l56 - codec helper functions for handling CS35L56 Smart AMP
+ */
+
+#include <linux/module.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-acpi.h>
+#include <sound/soc_sdw_utils.h>
+
+static const struct snd_soc_dapm_widget bridge_widgets[] = {
+ SND_SOC_DAPM_SPK("Bridge Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route bridge_map[] = {
+ {"Bridge Speaker", NULL, "AMPL SPK"},
+ {"Bridge Speaker", NULL, "AMPR SPK"},
+};
+
+static const char * const bridge_cs35l56_name_prefixes[] = {
+ "AMPL",
+ "AMPR",
+};
+
+static int asoc_sdw_bridge_cs35l56_asp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int i, ret;
+ unsigned int rx_mask = 3; // ASP RX1, RX2
+ unsigned int tx_mask = 3; // ASP TX1, TX2
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s spk:cs35l56-bridge",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_new_controls(dapm, bridge_widgets,
+ ARRAY_SIZE(bridge_widgets));
+ if (ret) {
+ dev_err(card->dev, "widgets addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, bridge_map, ARRAY_SIZE(bridge_map));
+ if (ret) {
+ dev_err(card->dev, "map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ /* 4 x 16-bit sample slots and FSYNC=48000, BCLK=3.072 MHz */
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = asoc_sdw_cs35l56_volume_limit(card, codec_dai->component->name_prefix);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask, rx_mask, 4, 16);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 3072000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_mask, rx_mask, 4, 16);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_pcm_stream asoc_sdw_bridge_params = {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+SND_SOC_DAILINK_DEFS(asoc_sdw_bridge_dai,
+ DAILINK_COMP_ARRAY(COMP_CODEC("cs42l43-codec", "cs42l43-asp")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("spi-cs35l56-left", "cs35l56-asp1"),
+ COMP_CODEC("spi-cs35l56-right", "cs35l56-asp1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("cs42l43-codec")));
+
+static const struct snd_soc_dai_link bridge_dai_template = {
+ .name = "cs42l43-cs35l56",
+ .init = asoc_sdw_bridge_cs35l56_asp_init,
+ .c2c_params = &asoc_sdw_bridge_params,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai),
+};
+
+int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card,
+ int *num_dais, int *num_devs)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) {
+ (*num_dais)++;
+ (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_bridge_cs35l56_count_sidecar, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) {
+ **dai_links = bridge_dai_template;
+
+ for (int i = 0; i < ARRAY_SIZE(bridge_cs35l56_name_prefixes); i++) {
+ (*codec_conf)->dlc.name = (*dai_links)->codecs[i].name;
+ (*codec_conf)->name_prefix = bridge_cs35l56_name_prefixes[i];
+ (*codec_conf)++;
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_bridge_cs35l56_add_sidecar, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_bridge_cs35l56_spk_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS)
+ info->amp_num += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_bridge_cs35l56_spk_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l42.c b/sound/soc/sdw_utils/soc_sdw_cs42l42.c
new file mode 100644
index 000000000000..ff1bdf52d483
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_cs42l42.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2023 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+/*
+ * soc_sdw_cs42l42 - Helpers to handle CS42L42 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc_sdw_utils.h>
+
+static const struct snd_soc_dapm_route cs42l42_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone", NULL, "cs42l42 HP"},
+
+ /* other jacks */
+ {"cs42l42 HS", NULL, "Headset Mic"},
+};
+
+static struct snd_soc_jack_pin cs42l42_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+int asoc_sdw_cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int ret;
+
+ component = dai->component;
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:cs42l42",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, cs42l42_map,
+ ARRAY_SIZE(cs42l42_map));
+
+ if (ret) {
+ dev_err(card->dev, "cs42l42 map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ cs42l42_jack_pins,
+ ARRAY_SIZE(cs42l42_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l42_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l43.c b/sound/soc/sdw_utils/soc_sdw_cs42l43.c
new file mode 100644
index 000000000000..4c954501e500
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_cs42l43.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Based on sof_sdw_rt5682.c
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2023 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_cs42l43 - Helpers to handle CS42L43 from generic machine driver
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <sound/jack.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/cs42l43.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc_sdw_utils.h>
+
+#define CS42L43_SPK_VOLUME_0DB 128 /* 0dB Max */
+
+static const struct snd_soc_dapm_route cs42l43_hs_map[] = {
+ { "Headphone", NULL, "cs42l43 AMP3_OUT" },
+ { "Headphone", NULL, "cs42l43 AMP4_OUT" },
+ { "cs42l43 ADC1_IN1_P", NULL, "Headset Mic" },
+ { "cs42l43 ADC1_IN1_N", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route cs42l43_spk_map[] = {
+ { "Speaker", NULL, "cs42l43 AMP1_OUT_P", },
+ { "Speaker", NULL, "cs42l43 AMP1_OUT_N", },
+ { "Speaker", NULL, "cs42l43 AMP2_OUT_P", },
+ { "Speaker", NULL, "cs42l43 AMP2_OUT_N", },
+};
+
+static const struct snd_soc_dapm_route cs42l43_dmic_map[] = {
+ { "cs42l43 PDM1_DIN", NULL, "DMIC" },
+ { "cs42l43 PDM2_DIN", NULL, "DMIC" },
+};
+
+static struct snd_soc_jack_pin soc_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+int asoc_sdw_cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &ctx->sdw_headset;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l43",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, cs42l43_hs_map,
+ ARRAY_SIZE(cs42l43_hs_map));
+ if (ret) {
+ dev_err(card->dev, "cs42l43 hs map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Jack",
+ SND_JACK_MECHANICAL | SND_JACK_AVOUT |
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ jack, soc_jack_pins,
+ ARRAY_SIZE(soc_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "Failed to create jack: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(card->dev, "Failed to register jack: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_sysclk(component, CS42L43_SYSCLK, CS42L43_SYSCLK_SDW,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret)
+ dev_err(card->dev, "Failed to set sysclk: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_hs_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ if (!(ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS)) {
+ /* Will be set by the bridge code in this case */
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s spk:cs42l43-spk",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ ret = snd_soc_limit_volume(card, "cs42l43 Speaker Digital Volume",
+ CS42L43_SPK_VOLUME_0DB);
+ if (ret)
+ dev_err(card->dev, "cs42l43 speaker volume limit failed: %d\n", ret);
+ else
+ dev_info(card->dev, "Setting CS42L43 Speaker volume limit to %d\n",
+ CS42L43_SPK_VOLUME_0DB);
+
+ ret = snd_soc_dapm_add_routes(dapm, cs42l43_spk_map,
+ ARRAY_SIZE(cs42l43_spk_map));
+ if (ret)
+ dev_err(card->dev, "cs42l43 speaker map addition failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_spk_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs42l43_spk_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ /* Do init on playback link only. */
+ if (!playback)
+ return 0;
+
+ info->amp_num++;
+
+ return asoc_sdw_bridge_cs35l56_spk_init(card, dai_links, info, playback);
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_spk_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs42l43-dmic",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, cs42l43_dmic_map,
+ ARRAY_SIZE(cs42l43_dmic_map));
+ if (ret)
+ dev_err(card->dev, "cs42l43 dmic map addition failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_dmic_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l45.c b/sound/soc/sdw_utils/soc_sdw_cs42l45.c
new file mode 100644
index 000000000000..647923d9669f
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_cs42l45.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Based on sof_sdw_rt5682.c
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2023 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_cs42l45 - Helpers to handle CS42L45 from generic machine driver
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-card.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc_sdw_utils.h>
+
+static struct snd_soc_jack_pin soc_jack_pins[] = {
+ {
+ .pin = "cs42l45 OT 43 Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "cs42l45 OT 45 Headset",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "cs42l45 IT 31 Microphone",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "cs42l45 IT 33 Headset",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+int asoc_sdw_cs42l45_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack *jack = &ctx->sdw_headset;
+ int ret;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l45",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Jack", SND_JACK_MECHANICAL |
+ SND_JACK_HEADSET | SND_JACK_LINEOUT, jack,
+ soc_jack_pins, ARRAY_SIZE(soc_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "Failed to create jack: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(card->dev, "Failed to register jack: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l45_hs_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs42l45_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs42l45-dmic",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs42l45_dmic_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c
new file mode 100644
index 000000000000..325ab7230481
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2023 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_cs_amp - Helpers to handle CS35L56 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+#include <sound/soc_sdw_utils.h>
+
+#define CS_AMP_CHANNELS_PER_AMP 4
+#define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */
+
+int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix)
+{
+ char *volume_ctl_name;
+ int ret;
+
+ volume_ctl_name = kasprintf(GFP_KERNEL, "%s Speaker Volume", name_prefix);
+ if (!volume_ctl_name)
+ return -ENOMEM;
+
+ ret = snd_soc_limit_volume(card, volume_ctl_name, CS35L56_SPK_VOLUME_0DB);
+ if (ret)
+ dev_err(card->dev, "%s limit set failed: %d\n", volume_ctl_name, ret);
+
+ kfree(volume_ctl_name);
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs35l56_volume_limit, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ char widget_name[16];
+ struct snd_soc_dapm_route route = { "Speaker", NULL, widget_name };
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!strstr(codec_dai->name, "cs35l56"))
+ continue;
+
+ snprintf(widget_name, sizeof(widget_name), "%s SPK",
+ codec_dai->component->name_prefix);
+
+ ret = asoc_sdw_cs35l56_volume_limit(card, codec_dai->component->name_prefix);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, &route, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ const struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ const struct snd_soc_dai_link_ch_map *ch_map;
+ const struct snd_soc_dai_link_component *codec_dlc;
+ struct snd_soc_dai *codec_dai;
+ u8 ch_slot[8] = {};
+ unsigned int amps_per_bus, ch_per_amp, mask;
+ int i, ret;
+
+ WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot));
+
+ /*
+ * CS35L56 has 4 TX channels. When the capture is aggregated the
+ * same bus slots will be allocated to all the amps on a bus. Only
+ * one amp on that bus can be transmitting in each slot so divide
+ * the available 4 slots between all the amps on a bus.
+ */
+ amps_per_bus = dai_link->num_codecs / dai_link->num_cpus;
+ if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) {
+ dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n",
+ dai_link->num_codecs, dai_link->num_cpus);
+ return -EINVAL;
+ }
+
+ ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus;
+
+ for_each_rtd_ch_maps(rtd, i, ch_map) {
+ codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i);
+ codec_dai = snd_soc_find_dai(codec_dlc);
+ mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu];
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret);
+ return ret;
+ }
+
+ ch_slot[ch_map->cpu] += ch_per_amp;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ /* Do init on playback link only. */
+ if (!playback)
+ return 0;
+
+ info->amp_num++;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_cs_amp_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_dmic.c b/sound/soc/sdw_utils/soc_sdw_dmic.c
new file mode 100644
index 000000000000..6075cd235452
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_dmic.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_dmic - Helpers to handle dmic from generic machine driver
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc_sdw_utils.h>
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+int asoc_sdw_dmic_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, dmic_map,
+ ARRAY_SIZE(dmic_map));
+
+ if (ret)
+ dev_err(card->dev, "DMic map addition failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_dmic_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_maxim.c b/sound/soc/sdw_utils/soc_sdw_maxim.c
new file mode 100644
index 000000000000..14090b032cb6
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_maxim.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+//
+// soc_sdw_maxim - Helpers to handle maxim codecs
+// codec devices from generic machine driver
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc_sdw_utils.h>
+
+static int maxim_part_id;
+#define SOC_SDW_PART_ID_MAX98363 0x8363
+#define SOC_SDW_PART_ID_MAX98373 0x8373
+
+static const struct snd_soc_dapm_route max_98373_dapm_routes[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, max_98373_dapm_routes, 2);
+ if (ret)
+ dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_maxim_spk_rtd_init, "SND_SOC_SDW_UTILS");
+
+static int asoc_sdw_mx8373_enable_spk_pin(struct snd_pcm_substream *substream, bool enable)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
+ int ret;
+ int j;
+
+ /* set spk pin by playback only */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cpu_dai->component);
+ char pin_name[16];
+
+ snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk",
+ codec_dai->component->name_prefix);
+
+ if (enable)
+ ret = snd_soc_dapm_enable_pin(dapm, pin_name);
+ else
+ ret = snd_soc_dapm_disable_pin(dapm, pin_name);
+
+ if (!ret)
+ snd_soc_dapm_sync(dapm);
+ }
+
+ return 0;
+}
+
+static int asoc_sdw_mx8373_prepare(struct snd_pcm_substream *substream)
+{
+ int ret;
+
+ /* according to soc_pcm_prepare dai link prepare is called first */
+ ret = asoc_sdw_prepare(substream);
+ if (ret < 0)
+ return ret;
+
+ return asoc_sdw_mx8373_enable_spk_pin(substream, true);
+}
+
+static int asoc_sdw_mx8373_hw_free(struct snd_pcm_substream *substream)
+{
+ int ret;
+
+ /* according to soc_pcm_hw_free dai link free is called first */
+ ret = asoc_sdw_hw_free(substream);
+ if (ret < 0)
+ return ret;
+
+ return asoc_sdw_mx8373_enable_spk_pin(substream, false);
+}
+
+static const struct snd_soc_ops max_98373_sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_mx8373_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_mx8373_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static int asoc_sdw_mx8373_sdw_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+
+ /* Disable Left and Right Spk pin after boot */
+ snd_soc_dapm_disable_pin(dapm, "Left Spk");
+ snd_soc_dapm_disable_pin(dapm, "Right Spk");
+ return snd_soc_dapm_sync(dapm);
+}
+
+int asoc_sdw_maxim_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ info->amp_num++;
+
+ maxim_part_id = info->part_id;
+ switch (maxim_part_id) {
+ case SOC_SDW_PART_ID_MAX98363:
+ /* Default ops are set in function init_dai_link.
+ * called as part of function create_sdw_dailink
+ */
+ break;
+ case SOC_SDW_PART_ID_MAX98373:
+ info->codec_card_late_probe = asoc_sdw_mx8373_sdw_late_probe;
+ dai_links->ops = &max_98373_sdw_ops;
+ break;
+ default:
+ dev_err(card->dev, "Invalid maxim_part_id %#x\n", maxim_part_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_maxim_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt5682.c b/sound/soc/sdw_utils/soc_sdw_rt5682.c
new file mode 100644
index 000000000000..641362835b4e
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt5682.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_rt5682 - Helpers to handle RT5682 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc_sdw_utils.h>
+
+static const struct snd_soc_dapm_route rt5682_map[] = {
+ /*Headphones*/
+ { "Headphone", NULL, "rt5682 HPOL" },
+ { "Headphone", NULL, "rt5682 HPOR" },
+ { "rt5682 IN1P", NULL, "Headset Mic" },
+};
+
+static struct snd_soc_jack_pin rt5682_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+int asoc_sdw_rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int ret;
+
+ component = dai->component;
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:rt5682",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, rt5682_map,
+ ARRAY_SIZE(rt5682_map));
+
+ if (ret) {
+ dev_err(card->dev, "rt5682 map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ rt5682_jack_pins,
+ ARRAY_SIZE(rt5682_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt5682_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt700.c b/sound/soc/sdw_utils/soc_sdw_rt700.c
new file mode 100644
index 000000000000..ffedfde955d0
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt700.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_rt700 - Helpers to handle RT700 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc_sdw_utils.h>
+
+static const struct snd_soc_dapm_route rt700_map[] = {
+ /* Headphones */
+ { "Headphones", NULL, "rt700 HP" },
+ { "Speaker", NULL, "rt700 SPK" },
+ { "rt700 MIC2", NULL, "AMIC" },
+};
+
+static struct snd_soc_jack_pin rt700_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "AMIC",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+int asoc_sdw_rt700_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int ret;
+
+ component = dai->component;
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:rt700",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, rt700_map,
+ ARRAY_SIZE(rt700_map));
+
+ if (ret) {
+ dev_err(card->dev, "rt700 map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ rt700_jack_pins,
+ ARRAY_SIZE(rt700_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt700_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt711.c b/sound/soc/sdw_utils/soc_sdw_rt711.c
new file mode 100644
index 000000000000..3a3a66b4b737
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt711.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_rt711 - Helpers to handle RT711 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc_sdw_utils.h>
+
+/*
+ * Note this MUST be called before snd_soc_register_card(), so that the props
+ * are in place before the codec component driver's probe function parses them.
+ */
+static int rt711_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)
+{
+ struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ if (!SOC_SDW_JACK_JDSRC(quirk))
+ return 0;
+ props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOC_SDW_JACK_JDSRC(quirk));
+
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode))
+ return PTR_ERR(fwnode);
+
+ ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_route rt711_map[] = {
+ /* Headphones */
+ { "Headphone", NULL, "rt711 HP" },
+ { "rt711 MIC2", NULL, "Headset Mic" },
+};
+
+static struct snd_soc_jack_pin rt711_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+int asoc_sdw_rt711_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int ret;
+
+ component = dai->component;
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:rt711",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, rt711_map,
+ ARRAY_SIZE(rt711_map));
+
+ if (ret) {
+ dev_err(card->dev, "rt711 map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ rt711_jack_pins,
+ ARRAY_SIZE(rt711_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt711_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (!ctx->headset_codec_dev)
+ return 0;
+
+ device_remove_software_node(ctx->headset_codec_dev);
+ put_device(ctx->headset_codec_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt711_exit, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rt711_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct device *sdw_dev;
+ int ret;
+
+ /*
+ * headset should be initialized once.
+ * Do it with dai link for playback.
+ */
+ if (!playback)
+ return 0;
+
+ sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
+ if (!sdw_dev)
+ return -EPROBE_DEFER;
+
+ ret = rt711_add_codec_device_props(sdw_dev, ctx->mc_quirk);
+ if (ret < 0) {
+ put_device(sdw_dev);
+ return ret;
+ }
+ ctx->headset_codec_dev = sdw_dev;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt711_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_amp.c b/sound/soc/sdw_utils/soc_sdw_rt_amp.c
new file mode 100644
index 000000000000..4e9b08cb653d
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt_amp.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2022 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/dmi.h>
+#include <sound/soc_sdw_utils.h>
+#include "soc_sdw_rt_amp_coeff_tables.h"
+#include "../codecs/rt1308.h"
+
+#define CODEC_NAME_SIZE 7
+
+/* choose a larger value to resolve compatibility issues */
+#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG
+
+struct rt_amp_platform_data {
+ const unsigned char *bq_params;
+ const unsigned int bq_params_cnt;
+};
+
+static const struct rt_amp_platform_data dell_0a5d_platform_data = {
+ .bq_params = dell_0a5d_bq_params,
+ .bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),
+};
+
+static const struct rt_amp_platform_data dell_0b00_platform_data = {
+ .bq_params = dell_0b00_bq_params,
+ .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
+};
+
+static const struct dmi_system_id dmi_platform_data[] = {
+ /* CometLake devices */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990")
+ },
+ .driver_data = (void *)&dell_0a5d_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F")
+ },
+ .driver_data = (void *)&dell_0a5d_platform_data,
+ },
+ /* TigerLake devices */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")
+ },
+ .driver_data = (void *)&dell_0a5d_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
+ },
+ .driver_data = (void *)&dell_0a5d_platform_data,
+ },
+ /* AlderLake devices */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {},
+};
+
+static int rt_amp_add_device_props(struct device *sdw_dev)
+{
+ struct property_entry props[3] = {};
+ struct fwnode_handle *fwnode;
+ const struct dmi_system_id *dmi_data;
+ const struct rt_amp_platform_data *pdata;
+ unsigned char params[RT_AMP_MAX_BQ_REG];
+ int ret;
+
+ dmi_data = dmi_first_match(dmi_platform_data);
+ if (!dmi_data)
+ return 0;
+
+ pdata = dmi_data->driver_data;
+ memcpy(&params, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);
+
+ props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);
+ props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);
+
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode))
+ return PTR_ERR(fwnode);
+
+ ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+/*
+ * dapm routes for rt1308/rt1316/rt1318 will be registered dynamically
+ * according to the number of rt1308/rt1316/rt1318 used. The first two
+ * entries will be registered for one codec case, and the last two entries
+ * are also registered if two 1308s/1316s/1318s are used.
+ */
+static const struct snd_soc_dapm_route rt1308_map[] = {
+ { "Speaker", NULL, "rt1308-1 SPOL" },
+ { "Speaker", NULL, "rt1308-1 SPOR" },
+ { "Speaker", NULL, "rt1308-2 SPOL" },
+ { "Speaker", NULL, "rt1308-2 SPOR" },
+};
+
+static const struct snd_soc_dapm_route rt1316_map[] = {
+ { "Speaker", NULL, "rt1316-1 SPOL" },
+ { "Speaker", NULL, "rt1316-1 SPOR" },
+ { "Speaker", NULL, "rt1316-2 SPOL" },
+ { "Speaker", NULL, "rt1316-2 SPOR" },
+};
+
+static const struct snd_soc_dapm_route rt1318_map[] = {
+ { "Speaker", NULL, "rt1318-1 SPOL" },
+ { "Speaker", NULL, "rt1318-1 SPOR" },
+ { "Speaker", NULL, "rt1318-2 SPOL" },
+ { "Speaker", NULL, "rt1318-2 SPOR" },
+};
+
+static const struct snd_soc_dapm_route rt1320_map[] = {
+ { "Speaker", NULL, "rt1320-1 SPOL" },
+ { "Speaker", NULL, "rt1320-1 SPOR" },
+ { "Speaker", NULL, "rt1320-2 SPOL" },
+ { "Speaker", NULL, "rt1320-2 SPOR" },
+};
+
+static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_dai *dai,
+ char *codec_name)
+{
+ /* get the codec name */
+ snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);
+
+ /* choose the right codec's map */
+ if (strcmp(codec_name, "rt1308") == 0)
+ return rt1308_map;
+ else if (strcmp(codec_name, "rt1316") == 0)
+ return rt1316_map;
+ else if (strcmp(codec_name, "rt1318") == 0)
+ return rt1318_map;
+ else
+ return rt1320_map;
+}
+
+int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ const struct snd_soc_dapm_route *rt_amp_map;
+ char codec_name[CODEC_NAME_SIZE];
+ struct snd_soc_dai *codec_dai;
+ int ret = -EINVAL;
+ int i;
+
+ rt_amp_map = get_codec_name_and_route(dai, codec_name);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strstr(codec_dai->component->name_prefix, "-1"))
+ ret = snd_soc_dapm_add_routes(dapm, rt_amp_map, 2);
+ else if (strstr(codec_dai->component->name_prefix, "-2"))
+ ret = snd_soc_dapm_add_routes(dapm, rt_amp_map + 2, 2);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, "SND_SOC_SDW_UTILS");
+
+static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int clk_id, clk_freq, pll_out;
+ int err;
+
+ clk_id = RT1308_PLL_S_MCLK;
+ clk_freq = 38400000;
+
+ pll_out = params_rate(params) * 512;
+
+ /* Set rt1308 pll */
+ err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
+ if (err < 0) {
+ dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err);
+ return err;
+ }
+
+ /* Set rt1308 sysclk */
+ err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* machine stream operations */
+const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = {
+ .hw_params = rt1308_i2s_hw_params,
+};
+EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (ctx->amp_dev1) {
+ device_remove_software_node(ctx->amp_dev1);
+ put_device(ctx->amp_dev1);
+ }
+
+ if (ctx->amp_dev2) {
+ device_remove_software_node(ctx->amp_dev2);
+ put_device(ctx->amp_dev2);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rt_amp_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct device *sdw_dev1, *sdw_dev2;
+ int ret;
+
+ /* Count amp number and do init on playback link only. */
+ if (!playback)
+ return 0;
+
+ info->amp_num++;
+
+ if (info->amp_num == 2) {
+ sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
+ if (!sdw_dev1)
+ return -EPROBE_DEFER;
+
+ ret = rt_amp_add_device_props(sdw_dev1);
+ if (ret < 0) {
+ put_device(sdw_dev1);
+ return ret;
+ }
+ ctx->amp_dev1 = sdw_dev1;
+
+ sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
+ if (!sdw_dev2)
+ return -EPROBE_DEFER;
+
+ ret = rt_amp_add_device_props(sdw_dev2);
+ if (ret < 0) {
+ put_device(sdw_dev2);
+ return ret;
+ }
+ ctx->amp_dev2 = sdw_dev2;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_amp_coeff_tables.h b/sound/soc/sdw_utils/soc_sdw_rt_amp_coeff_tables.h
new file mode 100644
index 000000000000..4803d134d071
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt_amp_coeff_tables.h
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ */
+
+/*
+ * soc_sdw_rt_amp_coeff_tables.h - related coefficients for RTK amplifier parameters
+ */
+
+#ifndef SND_SOC_SDW_RT_SDW_AMP_COEFF_H
+#define SND_SOC_SDW_RT_SDW_AMP_COEFF_H
+
+#define RT1308_MAX_BQ_REG 480
+#define RT1316_MAX_BQ_REG 580
+
+static const u8 __maybe_unused dell_0a5d_bq_params[] = {
+ 0xb0, 0xc5, 0x00, /* address: 0xc5b0; data: 0x00 */
+ 0xb1, 0xc5, 0x32,
+ 0xb2, 0xc5, 0x44,
+ 0xb3, 0xc5, 0x19,
+ 0xc0, 0xc5, 0x04,
+ 0xc1, 0xc5, 0x00,
+ 0xc2, 0xc5, 0x00,
+ 0xc3, 0xc5, 0x00,
+ 0xd0, 0xc5, 0x02,
+ 0xd1, 0xc5, 0x00,
+ 0xd2, 0xc5, 0x00,
+ 0xd3, 0xc5, 0x00,
+ 0xe0, 0xc5, 0x01,
+ 0xe1, 0xc5, 0xe8,
+ 0xe2, 0xc5, 0x5f,
+ 0xe3, 0xc5, 0x8a,
+ 0xf0, 0xc5, 0x1f,
+ 0xf1, 0xc5, 0x4e,
+ 0xf2, 0xc5, 0x90,
+ 0xf3, 0xc5, 0x11,
+ 0x50, 0xc6, 0x01,
+ 0x51, 0xc6, 0xff,
+ 0x52, 0xc6, 0x45,
+ 0x53, 0xc6, 0x41,
+ 0x60, 0xc6, 0x1c,
+ 0x61, 0xc6, 0x00,
+ 0x62, 0xc6, 0x00,
+ 0x63, 0xc6, 0x00,
+ 0x70, 0xc6, 0x02,
+ 0x71, 0xc6, 0x00,
+ 0x72, 0xc6, 0x00,
+ 0x73, 0xc6, 0x00,
+ 0x80, 0xc6, 0x03,
+ 0x81, 0xc6, 0xfe,
+ 0x82, 0xc6, 0x89,
+ 0x83, 0xc6, 0xfa,
+ 0x90, 0xc6, 0x1e,
+ 0x91, 0xc6, 0x01,
+ 0x92, 0xc6, 0x74,
+ 0x93, 0xc6, 0xf6,
+ 0x00, 0xc6, 0x01,
+ 0x01, 0xc6, 0xd9,
+ 0x02, 0xc6, 0xfb,
+ 0x03, 0xc6, 0xc4,
+ 0x10, 0xc6, 0x1c,
+ 0x11, 0xc6, 0x00,
+ 0x12, 0xc6, 0x00,
+ 0x13, 0xc6, 0x00,
+ 0x20, 0xc6, 0x02,
+ 0x21, 0xc6, 0x00,
+ 0x22, 0xc6, 0x00,
+ 0x23, 0xc6, 0x00,
+ 0x30, 0xc6, 0x03,
+ 0x31, 0xc6, 0xaf,
+ 0x32, 0xc6, 0x23,
+ 0x33, 0xc6, 0xcb,
+ 0x40, 0xc6, 0x1e,
+ 0x41, 0xc6, 0x47,
+ 0x42, 0xc6, 0x34,
+ 0x43, 0xc6, 0xba,
+ 0xa0, 0xc6, 0x01,
+ 0xa1, 0xc6, 0xff,
+ 0xa2, 0xc6, 0x45,
+ 0xa3, 0xc6, 0x41,
+ 0xb0, 0xc6, 0x1c,
+ 0xb1, 0xc6, 0x00,
+ 0xb2, 0xc6, 0x00,
+ 0xb3, 0xc6, 0x00,
+ 0xc0, 0xc6, 0x02,
+ 0xc1, 0xc6, 0x00,
+ 0xc2, 0xc6, 0x00,
+ 0xc3, 0xc6, 0x00,
+ 0xd0, 0xc6, 0x03,
+ 0xd1, 0xc6, 0xfe,
+ 0xd2, 0xc6, 0x89,
+ 0xd3, 0xc6, 0xfa,
+ 0xe0, 0xc6, 0x1e,
+ 0xe1, 0xc6, 0x01,
+ 0xe2, 0xc6, 0x74,
+ 0xe3, 0xc6, 0xf6,
+ 0x40, 0xc5, 0x0d,
+ 0x30, 0xc7, 0x15,
+ 0x31, 0xc7, 0x7c,
+ 0x32, 0xc7, 0x0f,
+ 0x33, 0xc7, 0xa0,
+ 0x40, 0xc7, 0x00,
+ 0x41, 0xc7, 0x00,
+ 0x42, 0xc7, 0xf8,
+ 0x43, 0xc7, 0xf8,
+ 0x50, 0xc7, 0x00,
+ 0x51, 0xc7, 0x00,
+ 0x52, 0xc7, 0x00,
+ 0x53, 0xc7, 0x01,
+ 0x90, 0xc7, 0x00,
+ 0x91, 0xc7, 0x14,
+ 0x92, 0xc7, 0x00,
+ 0x93, 0xc7, 0x14,
+ 0xa0, 0xc7, 0x00,
+ 0xa1, 0xc7, 0x00,
+ 0xa2, 0xc7, 0xf8,
+ 0xa3, 0xc7, 0xf8,
+ 0xb0, 0xc7, 0x00,
+ 0xb1, 0xc7, 0x00,
+ 0xb2, 0xc7, 0x00,
+ 0xb3, 0xc7, 0x00,
+ 0x60, 0xc7, 0x03,
+ 0x61, 0xc7, 0xe8,
+ 0x62, 0xc7, 0x03,
+ 0x63, 0xc7, 0xb6,
+ 0x70, 0xc7, 0x00,
+ 0x71, 0xc7, 0x00,
+ 0x72, 0xc7, 0xf8,
+ 0x73, 0xc7, 0xf8,
+ 0x80, 0xc7, 0x00,
+ 0x81, 0xc7, 0x00,
+ 0x82, 0xc7, 0x00,
+ 0x83, 0xc7, 0x00,
+ 0xc0, 0xc7, 0x00,
+ 0xc1, 0xc7, 0x14,
+ 0xc2, 0xc7, 0x00,
+ 0xc3, 0xc7, 0x14,
+ 0xd0, 0xc7, 0x00,
+ 0xd1, 0xc7, 0x00,
+ 0xd2, 0xc7, 0xf8,
+ 0xd3, 0xc7, 0xf8,
+ 0xe0, 0xc7, 0x00,
+ 0xe1, 0xc7, 0x00,
+ 0xe2, 0xc7, 0x00,
+ 0xe3, 0xc7, 0x00,
+ 0x60, 0xc5, 0x02,
+ 0x61, 0xc5, 0x00,
+ 0x62, 0xc5, 0x00,
+ 0x63, 0xc5, 0x00,
+ 0x70, 0xc5, 0x02,
+ 0x71, 0xc5, 0x00,
+ 0x72, 0xc5, 0x00,
+ 0x73, 0xc5, 0x00,
+ 0x80, 0xc5, 0x02,
+ 0x81, 0xc5, 0x00,
+ 0x82, 0xc5, 0x00,
+ 0x83, 0xc5, 0x00,
+ 0x90, 0xc5, 0x02,
+ 0x91, 0xc5, 0x00,
+ 0x92, 0xc5, 0x00,
+ 0x93, 0xc5, 0x00,
+ 0x50, 0xc5, 0x01,
+};
+
+static const u8 __maybe_unused dell_0b00_bq_params[] = {
+ 0x03, 0xc2, 0x00,
+ 0x04, 0xc2, 0xb2,
+ 0x05, 0xc2, 0xe0,
+ 0x06, 0xc2, 0x3a,
+ 0x07, 0xc2, 0x01,
+ 0x08, 0xc2, 0x65,
+ 0x09, 0xc2, 0xc0,
+ 0x0a, 0xc2, 0x75,
+ 0x0b, 0xc2, 0x00,
+ 0x0c, 0xc2, 0xb2,
+ 0x0d, 0xc2, 0xe0,
+ 0x0e, 0xc2, 0x3a,
+ 0x0f, 0xc2, 0xf7,
+ 0x10, 0xc2, 0x4d,
+ 0x11, 0xc2, 0x5b,
+ 0x12, 0xc2, 0xe9,
+ 0x13, 0xc2, 0x03,
+ 0x14, 0xc2, 0x7e,
+ 0x15, 0xc2, 0x25,
+ 0x16, 0xc2, 0x01,
+ 0x17, 0xc2, 0x07,
+ 0x18, 0xc2, 0xfd,
+ 0x19, 0xc2, 0x15,
+ 0x1a, 0xc2, 0x04,
+ 0x1b, 0xc2, 0xf0,
+ 0x1c, 0xc2, 0x05,
+ 0x1d, 0xc2, 0xd5,
+ 0x1e, 0xc2, 0xf7,
+ 0x1f, 0xc2, 0x07,
+ 0x20, 0xc2, 0xfd,
+ 0x21, 0xc2, 0x15,
+ 0x22, 0xc2, 0x04,
+ 0x23, 0xc2, 0xf0,
+ 0x24, 0xc2, 0x05,
+ 0x25, 0xc2, 0xd8,
+ 0x26, 0xc2, 0x17,
+ 0x27, 0xc2, 0x07,
+ 0x28, 0xc2, 0xfa,
+ 0x29, 0xc2, 0x2c,
+ 0x2a, 0xc2, 0x29,
+ 0x2b, 0xc2, 0x07,
+ 0x2c, 0xc2, 0x74,
+ 0x2d, 0xc2, 0xe0,
+ 0x2e, 0xc2, 0x33,
+ 0x2f, 0xc2, 0xf1,
+ 0x30, 0xc2, 0x16,
+ 0x31, 0xc2, 0x3f,
+ 0x32, 0xc2, 0x9b,
+ 0x33, 0xc2, 0x07,
+ 0x34, 0xc2, 0x74,
+ 0x35, 0xc2, 0xe0,
+ 0x36, 0xc2, 0x33,
+ 0x37, 0xc2, 0xf1,
+ 0x38, 0xc2, 0x29,
+ 0x39, 0xc2, 0xb0,
+ 0x3a, 0xc2, 0x4d,
+ 0x3b, 0xc2, 0x06,
+ 0x3c, 0xc2, 0xfd,
+ 0x3d, 0xc2, 0x31,
+ 0x3e, 0xc2, 0x18,
+ 0x3f, 0xc2, 0x07,
+ 0x40, 0xc2, 0xfd,
+ 0x41, 0xc2, 0x15,
+ 0x42, 0xc2, 0x04,
+ 0x43, 0xc2, 0xf0,
+ 0x44, 0xc2, 0x05,
+ 0x45, 0xc2, 0xd5,
+ 0x46, 0xc2, 0xf7,
+ 0x47, 0xc2, 0x07,
+ 0x48, 0xc2, 0xfd,
+ 0x49, 0xc2, 0x15,
+ 0x4a, 0xc2, 0x04,
+ 0x4b, 0xc2, 0xf0,
+ 0x4c, 0xc2, 0x05,
+ 0x4d, 0xc2, 0xd8,
+ 0x4e, 0xc2, 0x17,
+ 0x4f, 0xc2, 0x07,
+ 0x50, 0xc2, 0xfa,
+ 0x51, 0xc2, 0x2c,
+ 0x52, 0xc2, 0x29,
+ 0x0b, 0xc0, 0x30,
+ 0x80, 0xc3, 0x13,
+ 0x81, 0xc3, 0x88,
+ 0x82, 0xc3, 0x17,
+ 0x83, 0xc3, 0x70,
+ 0x84, 0xc3, 0x00,
+ 0x85, 0xc3, 0x00,
+ 0x86, 0xc3, 0xff,
+ 0x87, 0xc3, 0xee,
+ 0x88, 0xc3, 0x02,
+ 0x92, 0xc3, 0x00,
+ 0x93, 0xc3, 0x14,
+ 0x94, 0xc3, 0x00,
+ 0x95, 0xc3, 0x14,
+ 0x96, 0xc3, 0x00,
+ 0x97, 0xc3, 0x00,
+ 0x98, 0xc3, 0x00,
+ 0x99, 0xc3, 0x00,
+ 0x9a, 0xc3, 0x01,
+ 0x89, 0xc3, 0x03,
+ 0x8a, 0xc3, 0xe8,
+ 0x8b, 0xc3, 0x03,
+ 0x8c, 0xc3, 0xb6,
+ 0x8d, 0xc3, 0x00,
+ 0x8e, 0xc3, 0x00,
+ 0x8f, 0xc3, 0xff,
+ 0x90, 0xc3, 0xee,
+ 0x91, 0xc3, 0x01,
+ 0x9b, 0xc3, 0x00,
+ 0x9c, 0xc3, 0x14,
+ 0x9d, 0xc3, 0x00,
+ 0x9e, 0xc3, 0x14,
+ 0x9f, 0xc3, 0x00,
+ 0xa0, 0xc3, 0x00,
+ 0xa1, 0xc3, 0x00,
+ 0xa2, 0xc3, 0x00,
+ 0xa3, 0xc3, 0x01,
+ 0x61, 0xc2, 0x08,
+ 0x62, 0xc2, 0x00,
+ 0x63, 0xc2, 0x00,
+ 0x64, 0xc2, 0x00,
+ 0x65, 0xc2, 0x08,
+ 0x66, 0xc2, 0x00,
+ 0x67, 0xc2, 0x00,
+ 0x68, 0xc2, 0x00,
+ 0x69, 0xc2, 0x08,
+ 0x6a, 0xc2, 0x00,
+ 0x6b, 0xc2, 0x00,
+ 0x6c, 0xc2, 0x00,
+ 0x6d, 0xc2, 0x08,
+ 0x6e, 0xc2, 0x00,
+ 0x6f, 0xc2, 0x00,
+ 0x70, 0xc2, 0x00,
+ 0x00, 0xc2, 0xc0,
+};
+
+#endif
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_dmic.c b/sound/soc/sdw_utils/soc_sdw_rt_dmic.c
new file mode 100644
index 000000000000..97be110a59b6
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt_dmic.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2024 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_rt_dmic - Helpers to handle Realtek SDW DMIC from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc_sdw_utils.h>
+
+int asoc_sdw_rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_component *component;
+ char *mic_name;
+
+ component = dai->component;
+
+ /*
+ * rt715-sdca (aka rt714) is a special case that uses different name in card->components
+ * and component->name_prefix.
+ */
+ if (!strcmp(component->name_prefix, "rt714"))
+ mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "rt715-sdca");
+ else
+ mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s", component->name_prefix);
+ if (!mic_name)
+ return -ENOMEM;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:%s", card->components,
+ mic_name);
+ if (!card->components)
+ return -ENOMEM;
+
+ dev_dbg(card->dev, "card->components: %s\n", card->components);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_dmic_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
new file mode 100644
index 000000000000..5bf3627a97a0
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2024 Intel Corporation.
+
+/*
+ * soc_sdw_rt_mf_sdca
+ * - Helpers to handle RT Multifunction Codec from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc_sdw_utils.h>
+
+#define CODEC_NAME_SIZE 6
+
+/* dapm routes for RT-SPK will be registered dynamically */
+static const struct snd_soc_dapm_route rt712_spk_map[] = {
+ { "Speaker", NULL, "rt712 SPOL" },
+ { "Speaker", NULL, "rt712 SPOR" },
+};
+
+static const struct snd_soc_dapm_route rt721_spk_map[] = {
+ { "Speaker", NULL, "rt721 SPK" },
+};
+
+static const struct snd_soc_dapm_route rt722_spk_map[] = {
+ { "Speaker", NULL, "rt722 SPK" },
+};
+
+/* Structure to map codec names to respective route arrays and sizes */
+struct codec_route_map {
+ const char *codec_name;
+ const struct snd_soc_dapm_route *route_map;
+ size_t route_size;
+};
+
+/* Codec route maps array */
+static const struct codec_route_map codec_routes[] = {
+ { "rt712", rt712_spk_map, ARRAY_SIZE(rt712_spk_map) },
+ { "rt721", rt721_spk_map, ARRAY_SIZE(rt721_spk_map) },
+ { "rt722", rt722_spk_map, ARRAY_SIZE(rt722_spk_map) },
+};
+
+static const struct codec_route_map *get_codec_route_map(const char *codec_name)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(codec_routes); i++) {
+ if (strcmp(codec_routes[i].codec_name, codec_name) == 0)
+ return &codec_routes[i];
+ }
+ return NULL;
+}
+
+int asoc_sdw_rt_mf_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ char codec_name[CODEC_NAME_SIZE];
+ int ret;
+
+ /* acquire codec name */
+ snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);
+
+ /* acquire corresponding route map and size */
+ const struct codec_route_map *route_map = get_codec_route_map(codec_name);
+
+ if (!route_map) {
+ dev_err(rtd->dev, "failed to get codec name and route map\n");
+ return -EINVAL;
+ }
+
+ /* Add routes */
+ ret = snd_soc_dapm_add_routes(dapm, route_map->route_map, route_map->route_size);
+ if (ret)
+ dev_err(rtd->dev, "failed to add rt sdca spk map: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_mf_sdca_spk_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c b/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c
new file mode 100644
index 000000000000..2547b5b3fdd7
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * soc_sdw_rt711_sdca - Helpers to handle RT711-SDCA from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc_sdw_utils.h>
+
+/*
+ * Note this MUST be called before snd_soc_register_card(), so that the props
+ * are in place before the codec component driver's probe function parses them.
+ */
+static int rt_sdca_jack_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)
+{
+ struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ if (!SOC_SDW_JACK_JDSRC(quirk))
+ return 0;
+
+ props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOC_SDW_JACK_JDSRC(quirk));
+
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode))
+ return PTR_ERR(fwnode);
+
+ ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_route rt711_sdca_map[] = {
+ { "Headphone", NULL, "rt711 HP" },
+ { "rt711 MIC2", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_map[] = {
+ { "Headphone", NULL, "rt712 HP" },
+ { "rt712 MIC2", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route rt713_sdca_map[] = {
+ { "Headphone", NULL, "rt713 HP" },
+ { "rt713 MIC2", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route rt721_sdca_map[] = {
+ { "Headphone", NULL, "rt721 HP" },
+ { "rt721 MIC2", NULL, "Headset Mic" },
+};
+
+static const struct snd_soc_dapm_route rt722_sdca_map[] = {
+ { "Headphone", NULL, "rt722 HP" },
+ { "rt722 MIC2", NULL, "Headset Mic" },
+};
+
+static struct snd_soc_jack_pin rt_sdca_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+/*
+ * The sdca suffix is required for rt711 since there are two generations of the same chip.
+ * RT713 is an SDCA device but the sdca suffix is required for backwards-compatibility with
+ * previous UCM definitions.
+ */
+static const char * const need_sdca_suffix[] = {
+ "rt711", "rt713"
+};
+
+int asoc_sdw_rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int ret;
+ int i;
+
+ component = dai->component;
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:%s",
+ card->components, component->name_prefix);
+ if (!card->components)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(need_sdca_suffix); i++) {
+ if (strstr(component->name_prefix, need_sdca_suffix[i])) {
+ /* Add -sdca suffix for existing UCMs */
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s-sdca", card->components);
+ if (!card->components)
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ if (strstr(component->name_prefix, "rt711")) {
+ ret = snd_soc_dapm_add_routes(dapm, rt711_sdca_map,
+ ARRAY_SIZE(rt711_sdca_map));
+ } else if (strstr(component->name_prefix, "rt712")) {
+ ret = snd_soc_dapm_add_routes(dapm, rt712_sdca_map,
+ ARRAY_SIZE(rt712_sdca_map));
+ } else if (strstr(component->name_prefix, "rt713")) {
+ ret = snd_soc_dapm_add_routes(dapm, rt713_sdca_map,
+ ARRAY_SIZE(rt713_sdca_map));
+ } else if (strstr(component->name_prefix, "rt721")) {
+ ret = snd_soc_dapm_add_routes(dapm, rt721_sdca_map,
+ ARRAY_SIZE(rt721_sdca_map));
+ } else if (strstr(component->name_prefix, "rt722")) {
+ ret = snd_soc_dapm_add_routes(dapm, rt722_sdca_map,
+ ARRAY_SIZE(rt722_sdca_map));
+ } else {
+ dev_err(card->dev, "%s is not supported\n", component->name_prefix);
+ return -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(card->dev, "rt sdca jack map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ rt_sdca_jack_pins,
+ ARRAY_SIZE(rt_sdca_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (!ctx->headset_codec_dev)
+ return 0;
+
+ if (!SOC_SDW_JACK_JDSRC(ctx->mc_quirk))
+ return 0;
+
+ device_remove_software_node(ctx->headset_codec_dev);
+ put_device(ctx->headset_codec_dev);
+ ctx->headset_codec_dev = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_exit, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct device *sdw_dev;
+ int ret;
+
+ /*
+ * Jack detection should be only initialized once for headsets since
+ * the playback/capture is sharing the same jack
+ */
+ if (ctx->headset_codec_dev)
+ return 0;
+
+ sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
+ if (!sdw_dev)
+ return -EPROBE_DEFER;
+
+ ret = rt_sdca_jack_add_codec_device_props(sdw_dev, ctx->mc_quirk);
+ if (ret < 0) {
+ put_device(sdw_dev);
+ return ret;
+ }
+ ctx->headset_codec_dev = sdw_dev;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_ti_amp.c b/sound/soc/sdw_utils/soc_sdw_ti_amp.c
new file mode 100644
index 000000000000..cbd60faecd09
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_ti_amp.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Texas Instruments Inc.
+
+/*
+ * soc_sdw_ti_amp - Helpers to handle TI's soundwire based codecs
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+#include <sound/soc_sdw_utils.h>
+
+#define TIAMP_SPK_VOLUME_0DB 200
+
+int asoc_sdw_ti_amp_initial_settings(struct snd_soc_card *card,
+ const char *name_prefix)
+{
+ char *volume_ctl_name;
+ int ret;
+
+ volume_ctl_name = kasprintf(GFP_KERNEL, "%s Speaker Volume",
+ name_prefix);
+ if (!volume_ctl_name)
+ return -ENOMEM;
+
+ ret = snd_soc_limit_volume(card, volume_ctl_name,
+ TIAMP_SPK_VOLUME_0DB);
+ if (ret)
+ dev_err(card->dev,
+ "%s update failed %d\n",
+ volume_ctl_name, ret);
+
+ kfree(volume_ctl_name);
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_ti_amp_initial_settings, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_ti_spk_rtd_init(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ char widget_name[16];
+ char speaker[16];
+ struct snd_soc_dapm_route route = {speaker, NULL, widget_name};
+ struct snd_soc_dai *codec_dai;
+ const char *prefix;
+ int i, ret = 0;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!strstr(codec_dai->name, "tas2783"))
+ continue;
+
+ prefix = codec_dai->component->name_prefix;
+ if (!strncmp(prefix, "tas2783-1", strlen("tas2783-1"))) {
+ strscpy(speaker, "Left Spk", sizeof(speaker));
+ } else if (!strncmp(prefix, "tas2783-2", strlen("tas2783-2"))) {
+ strscpy(speaker, "Right Spk", sizeof(speaker));
+ } else {
+ ret = -EINVAL;
+ dev_err(card->dev, "unhandled prefix %s", prefix);
+ break;
+ }
+
+ snprintf(widget_name, sizeof(widget_name), "%s SPK", prefix);
+ ret = asoc_sdw_ti_amp_initial_settings(card, prefix);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, &route, 1);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_ti_spk_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_ti_amp_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ if (!playback)
+ return 0;
+
+ info->amp_num++;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_ti_amp_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
new file mode 100644
index 000000000000..6c656b2f7f3a
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -0,0 +1,1612 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+/*
+ * soc-sdw-utils.c - common SoundWire machine driver helper functions
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/sdca_function.h>
+#include <sound/soc_sdw_utils.h>
+
+static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_kcontrol_new generic_jack_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget generic_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_kcontrol_new generic_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_widget lr_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_kcontrol_new lr_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget rt700_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_kcontrol_new rt700_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("AMIC"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+struct asoc_sdw_codec_info codec_info_list[] = {
+ {
+ .part_id = 0x0000, /* TAS2783A */
+ .name_prefix = "tas2783",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "tas2783-codec",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .init = asoc_sdw_ti_amp_init,
+ .rtd_init = asoc_sdw_ti_spk_rtd_init,
+ .controls = lr_spk_controls,
+ .num_controls = ARRAY_SIZE(lr_spk_controls),
+ .widgets = lr_spk_widgets,
+ .num_widgets = ARRAY_SIZE(lr_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x700,
+ .name_prefix = "rt700",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt700-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .rtd_init = asoc_sdw_rt700_rtd_init,
+ .controls = rt700_controls,
+ .num_controls = ARRAY_SIZE(rt700_controls),
+ .widgets = rt700_widgets,
+ .num_widgets = ARRAY_SIZE(rt700_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x711,
+ .name_prefix = "rt711",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt711-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x711,
+ .name_prefix = "rt711",
+ .version_id = 2,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt711-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt711_init,
+ .exit = asoc_sdw_rt711_exit,
+ .rtd_init = asoc_sdw_rt711_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x712,
+ .name_prefix = "rt712",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt712-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt712-sdca-aif2",
+ .component_name = "rt712",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "rt712-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .part_id = 0x1712,
+ .name_prefix = "rt712-dmic",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt712-sdca-dmic-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x713,
+ .name_prefix = "rt713",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt712-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "rt712-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 2,
+ },
+ {
+ .part_id = 0x1713,
+ .name_prefix = "rt713-dmic",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt712-sdca-dmic-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x1308,
+ .name_prefix = "rt1308",
+ .acpi_id = "10EC1308",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "rt1308-aif",
+ .component_name = "rt1308",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ .ops = &soc_sdw_rt1308_i2s_ops,
+ },
+ {
+ .part_id = 0x1316,
+ .name_prefix = "rt1316",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt1316-aif",
+ .component_name = "rt1316",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x1318,
+ .name_prefix = "rt1318",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt1318-aif",
+ .component_name = "rt1318",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x1320,
+ .name_prefix = "rt1320",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "rt1320-aif1",
+ .component_name = "rt1320",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x1321,
+ .name_prefix = "rt1320",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "rt1320-aif1",
+ .component_name = "rt1320",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x714,
+ .name_prefix = "rt714",
+ .version_id = 3,
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-sdca-aif2",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x715,
+ .name_prefix = "rt715",
+ .version_id = 3,
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-sdca-aif2",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x714,
+ .name_prefix = "rt714",
+ .version_id = 2,
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-aif2",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x715,
+ .name_prefix = "rt715",
+ .version_id = 2,
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-aif2",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x721,
+ .name_prefix = "rt721",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt721-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt721-sdca-aif2",
+ .component_name = "rt721",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ /* No feedback capability is provided by rt721-sdca codec driver*/
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "rt721-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .part_id = 0x722,
+ .name_prefix = "rt722",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt722-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt722-sdca-aif2",
+ .component_name = "rt722",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ /* No feedback capability is provided by rt722-sdca codec driver*/
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ .quirk = SOC_SDW_CODEC_SPKR,
+ .quirk_exclude = true,
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "rt722-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .part_id = 0x8373,
+ .name_prefix = "Left",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "max98373-aif1",
+ .component_name = "mx8373",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .init = asoc_sdw_maxim_init,
+ .rtd_init = asoc_sdw_maxim_spk_rtd_init,
+ .controls = lr_spk_controls,
+ .num_controls = ARRAY_SIZE(lr_spk_controls),
+ .widgets = lr_spk_widgets,
+ .num_widgets = ARRAY_SIZE(lr_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x8363,
+ .name_prefix = "Left",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "max98363-aif1",
+ .component_name = "mx8363",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_maxim_init,
+ .rtd_init = asoc_sdw_maxim_spk_rtd_init,
+ .controls = lr_spk_controls,
+ .num_controls = ARRAY_SIZE(lr_spk_controls),
+ .widgets = lr_spk_widgets,
+ .num_widgets = ARRAY_SIZE(lr_spk_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x5682,
+ .name_prefix = "rt5682",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt5682-sdw",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .rtd_init = asoc_sdw_rt5682_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x3556,
+ .name_prefix = "AMP",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "cs35l56-sdw1",
+ .component_name = "cs35l56",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_cs_amp_init,
+ .rtd_init = asoc_sdw_cs_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "cs35l56-sdw1c",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
+ },
+ },
+ .dai_num = 2,
+ },
+ {
+ .part_id = 0x3557,
+ .name_prefix = "AMP",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "cs35l56-sdw1",
+ .component_name = "cs35l56",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_cs_amp_init,
+ .rtd_init = asoc_sdw_cs_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "cs35l56-sdw1c",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
+ },
+ },
+ .dai_num = 2,
+ },
+ {
+ .part_id = 0x3563,
+ .name_prefix = "AMP",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "cs35l56-sdw1",
+ .component_name = "cs35l56",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_cs_amp_init,
+ .rtd_init = asoc_sdw_cs_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "cs35l56-sdw1c",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
+ },
+ },
+ .dai_num = 2,
+ },
+ {
+ .part_id = 0x4242,
+ .name_prefix = "cs42l42",
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "cs42l42-sdw",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .rtd_init = asoc_sdw_cs42l42_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x4243,
+ .name_prefix = "cs42l43",
+ .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar,
+ .add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar,
+ .dais = {
+ {
+ .direction = {true, false},
+ .codec_name = "cs42l43-codec",
+ .dai_name = "cs42l43-dp5",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .rtd_init = asoc_sdw_cs42l43_hs_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {false, true},
+ .codec_name = "cs42l43-codec",
+ .dai_name = "cs42l43-dp1",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_cs42l43_dmic_rtd_init,
+ .widgets = generic_dmic_widgets,
+ .num_widgets = ARRAY_SIZE(generic_dmic_widgets),
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
+ },
+ {
+ .direction = {false, true},
+ .codec_name = "cs42l43-codec",
+ .dai_name = "cs42l43-dp2",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ },
+ {
+ .direction = {true, false},
+ .codec_name = "cs42l43-codec",
+ .dai_name = "cs42l43-dp6",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_cs42l43_spk_init,
+ .rtd_init = asoc_sdw_cs42l43_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ .quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
+ },
+ },
+ .dai_num = 4,
+ },
+ {
+ .part_id = 0x4245,
+ .name_prefix = "cs42l45",
+ .dais = {
+ {
+ .direction = {true, false},
+ .codec_name = "snd_soc_sdca.UAJ.1",
+ .dai_name = "IT 41",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .rtd_init = asoc_sdw_cs42l45_hs_rtd_init,
+ },
+ {
+ .direction = {false, true},
+ .codec_name = "snd_soc_sdca.SmartMic.0",
+ .dai_name = "OT 113",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_cs42l45_dmic_rtd_init,
+ },
+ {
+ .direction = {false, true},
+ .codec_name = "snd_soc_sdca.UAJ.1",
+ .dai_name = "OT 36",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ },
+ },
+ .dai_num = 3,
+ .auxs = {
+ {
+ .codec_name = "snd_soc_sdca.HID.2",
+ },
+ },
+ .aux_num = 1,
+ },
+ {
+ .part_id = 0xaaaa, /* generic codec mockup */
+ .name_prefix = "sdw_mockup_mmulti-function",
+ .version_id = 0,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .part_id = 0xaa55, /* headset codec mockup */
+ .name_prefix = "sdw_mockup_headset0",
+ .version_id = 0,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x55aa, /* amplifier mockup */
+ .name_prefix = "sdw_mockup_amp1",
+ .version_id = 0,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x5555,
+ .name_prefix = "sdw_mockup_mic0",
+ .version_id = 0,
+ .dais = {
+ {
+ .dai_name = "sdw-mockup-aif1",
+ .direction = {false, true},
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ },
+ },
+ .dai_num = 1,
+ },
+};
+EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_get_codec_info_list_count(void)
+{
+ return ARRAY_SIZE(codec_info_list);
+};
+EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS");
+
+struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
+{
+ unsigned int part_id, sdw_version;
+ int i;
+
+ part_id = SDW_PART_ID(adr);
+ sdw_version = SDW_VERSION(adr);
+ for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
+ /*
+ * A codec info is for all sdw version with the part id if
+ * version_id is not specified in the codec info.
+ */
+ if (part_id == codec_info_list[i].part_id &&
+ (!codec_info_list[i].version_id ||
+ sdw_version == codec_info_list[i].version_id))
+ return &codec_info_list[i];
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, "SND_SOC_SDW_UTILS");
+
+struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
+{
+ int i;
+
+ if (!acpi_id[0])
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
+ if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
+ return &codec_info_list[i];
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, "SND_SOC_SDW_UTILS");
+
+struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
+ for (j = 0; j < codec_info_list[i].dai_num; j++) {
+ if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
+ *dai_index = j;
+ return &codec_info_list[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_codec_info *codec_info;
+ struct snd_soc_dai *dai;
+ const char *spk_components="";
+ int dai_index;
+ int ret;
+ int i;
+
+ for_each_rtd_codec_dais(rtd, i, dai) {
+ codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index);
+ if (!codec_info)
+ return -EINVAL;
+
+ /*
+ * A codec dai can be connected to different dai links for capture and playback,
+ * but we only need to call the rtd_init function once.
+ * The rtd_init for each codec dai is independent. So, the order of rtd_init
+ * doesn't matter.
+ */
+ if (codec_info->dais[dai_index].rtd_init_done)
+ continue;
+
+ /*
+ * Add card controls and dapm widgets for the first codec dai.
+ * The controls and widgets will be used for all codec dais.
+ */
+
+ if (i > 0)
+ goto skip_add_controls_widgets;
+
+ if (codec_info->dais[dai_index].controls) {
+ ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
+ codec_info->dais[dai_index].num_controls);
+ if (ret) {
+ dev_err(card->dev, "%#x controls addition failed: %d\n",
+ codec_info->part_id, ret);
+ return ret;
+ }
+ }
+ if (codec_info->dais[dai_index].widgets) {
+ ret = snd_soc_dapm_new_controls(dapm,
+ codec_info->dais[dai_index].widgets,
+ codec_info->dais[dai_index].num_widgets);
+ if (ret) {
+ dev_err(card->dev, "%#x widgets addition failed: %d\n",
+ codec_info->part_id, ret);
+ return ret;
+ }
+ }
+
+skip_add_controls_widgets:
+ if (codec_info->dais[dai_index].rtd_init) {
+ ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
+ if (ret)
+ return ret;
+ }
+
+ /* Generate the spk component string for card->components string */
+ if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP &&
+ codec_info->dais[dai_index].component_name) {
+ if (strlen (spk_components) == 0)
+ spk_components =
+ devm_kasprintf(card->dev, GFP_KERNEL, "%s",
+ codec_info->dais[dai_index].component_name);
+ else
+ /* Append component name to spk_components */
+ spk_components =
+ devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s+%s", spk_components,
+ codec_info->dais[dai_index].component_name);
+ }
+
+ codec_info->dais[dai_index].rtd_init_done = true;
+
+ }
+
+ if (strlen (spk_components) > 0) {
+ /* Update card components for speaker components */
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s",
+ card->components, spk_components);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, "SND_SOC_SDW_UTILS");
+
+/* these wrappers are only needed to avoid typecast compilation errors */
+int asoc_sdw_startup(struct snd_pcm_substream *substream)
+{
+ return sdw_startup_stream(substream);
+}
+EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdw_stream_runtime *sdw_stream;
+ struct snd_soc_dai *dai;
+
+ /* Find stream from first CPU DAI */
+ dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
+ if (IS_ERR(sdw_stream)) {
+ dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
+ return PTR_ERR(sdw_stream);
+ }
+
+ return sdw_prepare_stream(sdw_stream);
+}
+EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdw_stream_runtime *sdw_stream;
+ struct snd_soc_dai *dai;
+ int ret;
+
+ /* Find stream from first CPU DAI */
+ dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
+ if (IS_ERR(sdw_stream)) {
+ dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
+ return PTR_ERR(sdw_stream);
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = sdw_enable_stream(sdw_stream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = sdw_disable_stream(sdw_stream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_trigger, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link_ch_map *ch_maps;
+ int ch = params_channels(params);
+ unsigned int ch_mask;
+ int num_codecs;
+ int step;
+ int i;
+
+ if (!rtd->dai_link->ch_maps)
+ return 0;
+
+ /* Identical data will be sent to all codecs in playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_mask = GENMASK(ch - 1, 0);
+ step = 0;
+ } else {
+ num_codecs = rtd->dai_link->num_codecs;
+
+ if (ch < num_codecs || ch % num_codecs != 0) {
+ dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
+ ch, num_codecs);
+ return -EINVAL;
+ }
+
+ ch_mask = GENMASK(ch / num_codecs - 1, 0);
+ step = hweight_long(ch_mask);
+ }
+
+ /*
+ * The captured data will be combined from each cpu DAI if the dai
+ * link has more than one codec DAIs. Set codec channel mask and
+ * ASoC will set the corresponding channel numbers for each cpu dai.
+ */
+ for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
+ ch_maps->ch_mask = ch_mask << (i * step);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_hw_params, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdw_stream_runtime *sdw_stream;
+ struct snd_soc_dai *dai;
+
+ /* Find stream from first CPU DAI */
+ dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
+ if (IS_ERR(sdw_stream)) {
+ dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
+ return PTR_ERR(sdw_stream);
+ }
+
+ return sdw_deprepare_stream(sdw_stream);
+}
+EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS");
+
+void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
+{
+ sdw_shutdown_stream(substream);
+}
+EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS");
+
+static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
+ unsigned int sdw_version,
+ unsigned int mfg_id,
+ unsigned int part_id,
+ unsigned int class_id,
+ int index_in_link)
+{
+ int i;
+
+ for (i = 0; i < adr_link->num_adr; i++) {
+ unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
+ u64 adr;
+
+ /* skip itself */
+ if (i == index_in_link)
+ continue;
+
+ adr = adr_link->adr_d[i].adr;
+
+ sdw1_version = SDW_VERSION(adr);
+ mfg1_id = SDW_MFG_ID(adr);
+ part1_id = SDW_PART_ID(adr);
+ class1_id = SDW_CLASS_ID(adr);
+
+ if (sdw_version == sdw1_version &&
+ mfg_id == mfg1_id &&
+ part_id == part1_id &&
+ class_id == class1_id)
+ return false;
+ }
+
+ return true;
+}
+
+static const char *_asoc_sdw_get_codec_name(struct device *dev,
+ const struct snd_soc_acpi_link_adr *adr_link,
+ int adr_index)
+{
+ u64 adr = adr_link->adr_d[adr_index].adr;
+ unsigned int sdw_version = SDW_VERSION(adr);
+ unsigned int link_id = SDW_DISCO_LINK_ID(adr);
+ unsigned int unique_id = SDW_UNIQUE_ID(adr);
+ unsigned int mfg_id = SDW_MFG_ID(adr);
+ unsigned int part_id = SDW_PART_ID(adr);
+ unsigned int class_id = SDW_CLASS_ID(adr);
+
+ if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
+ class_id, adr_index))
+ return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
+ link_id, mfg_id, part_id, class_id);
+
+ return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
+ link_id, mfg_id, part_id, class_id, unique_id);
+}
+
+const char *asoc_sdw_get_codec_name(struct device *dev,
+ const struct asoc_sdw_dai_info *dai_info,
+ const struct snd_soc_acpi_link_adr *adr_link,
+ int adr_index)
+{
+ if (dai_info->codec_name)
+ return devm_kstrdup(dev, dai_info->codec_name, GFP_KERNEL);
+
+ return _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
+}
+EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");
+
+/* helper to get the link that the codec DAI is used */
+struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
+ const char *dai_name)
+{
+ struct snd_soc_dai_link *dai_link;
+ int i;
+ int j;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ for (j = 0; j < dai_link->num_codecs; j++) {
+ /* Check each codec in a link */
+ if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
+ return dai_link;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS");
+
+void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ int ret;
+ int i, j;
+
+ for (i = 0; i < ctx->codec_info_list_count; i++) {
+ for (j = 0; j < codec_info_list[i].dai_num; j++) {
+ codec_info_list[i].dais[j].rtd_init_done = false;
+ /* Check each dai in codec_info_lis to see if it is used in the link */
+ if (!codec_info_list[i].dais[j].exit)
+ continue;
+ /*
+ * We don't need to call .exit function if there is no matched
+ * dai link found.
+ */
+ dai_link = asoc_sdw_mc_find_codec_dai_used(card,
+ codec_info_list[i].dais[j].dai_name);
+ if (dai_link) {
+ /* Do the .exit function if the codec dai is used in the link */
+ ret = codec_info_list[i].dais[j].exit(card, dai_link);
+ if (ret)
+ dev_warn(card->dev,
+ "codec exit failed %d\n",
+ ret);
+ break;
+ }
+ }
+ }
+}
+EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_card_late_probe(struct snd_soc_card *card)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
+ if (codec_info_list[i].codec_card_late_probe) {
+ ret = codec_info_list[i].codec_card_late_probe(card);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS");
+
+void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
+ int *be_id, char *name, int playback, int capture,
+ struct snd_soc_dai_link_component *cpus, int cpus_num,
+ struct snd_soc_dai_link_component *platform_component,
+ int num_platforms, struct snd_soc_dai_link_component *codecs,
+ int codecs_num, int no_pcm,
+ int (*init)(struct snd_soc_pcm_runtime *rtd),
+ const struct snd_soc_ops *ops)
+{
+ dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
+ dai_links->id = (*be_id)++;
+ dai_links->name = name;
+ dai_links->stream_name = name;
+ dai_links->platforms = platform_component;
+ dai_links->num_platforms = num_platforms;
+ dai_links->no_pcm = no_pcm;
+ dai_links->cpus = cpus;
+ dai_links->num_cpus = cpus_num;
+ dai_links->codecs = codecs;
+ dai_links->num_codecs = codecs_num;
+ dai_links->playback_only = playback && !capture;
+ dai_links->capture_only = !playback && capture;
+ dai_links->init = init;
+ dai_links->ops = ops;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
+ int *be_id, char *name, int playback, int capture,
+ const char *cpu_dai_name, const char *platform_comp_name,
+ const char *codec_name, const char *codec_dai_name,
+ int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd),
+ const struct snd_soc_ops *ops)
+{
+ struct snd_soc_dai_link_component *dlc;
+
+ /* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
+ dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
+ return -ENOMEM;
+
+ dlc[0].dai_name = cpu_dai_name;
+ dlc[1].name = platform_comp_name;
+
+ dlc[2].name = codec_name;
+ dlc[2].dai_name = codec_dai_name;
+
+ asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
+ &dlc[0], 1, &dlc[1], 1, &dlc[2], 1,
+ no_pcm, init, ops);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card,
+ int *num_devs, int *num_ends, int *num_aux)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ const struct snd_soc_acpi_link_adr *adr_link;
+ int i;
+
+ for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
+ *num_devs += adr_link->num_adr;
+
+ for (i = 0; i < adr_link->num_adr; i++) {
+ const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
+ struct asoc_sdw_codec_info *codec_info;
+
+ *num_ends += adr_dev->num_endpoints;
+
+ codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
+ if (!codec_info)
+ return -EINVAL;
+
+ *num_aux += codec_info->aux_num;
+ }
+ }
+
+ dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, "SND_SOC_SDW_UTILS");
+
+struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
+ const struct snd_soc_acpi_endpoint *new)
+{
+ while (dailinks->initialised) {
+ if (new->aggregated && dailinks->group_id == new->group_id)
+ return dailinks;
+
+ dailinks++;
+ }
+
+ INIT_LIST_HEAD(&dailinks->endpoints);
+ dailinks->group_id = new->group_id;
+ dailinks->initialised = true;
+
+ return dailinks;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_get_dai_type(u32 type)
+{
+ switch (type) {
+ case SDCA_FUNCTION_TYPE_SMART_AMP:
+ case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
+ return SOC_SDW_DAI_TYPE_AMP;
+ case SDCA_FUNCTION_TYPE_SMART_MIC:
+ case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
+ case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
+ return SOC_SDW_DAI_TYPE_MIC;
+ case SDCA_FUNCTION_TYPE_UAJ:
+ case SDCA_FUNCTION_TYPE_RJ:
+ case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
+ return SOC_SDW_DAI_TYPE_JACK;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_NS(asoc_sdw_get_dai_type, "SND_SOC_SDW_UTILS");
+
+/*
+ * Check if the SDCA endpoint is present by the SDW peripheral
+ *
+ * @dev: Device pointer
+ * @codec_info: Codec info pointer
+ * @adr_link: ACPI link address
+ * @adr_index: Index of the ACPI link address
+ * @end_index: Index of the endpoint
+ *
+ * Return: 1 if the endpoint is present,
+ * 0 if the endpoint is not present,
+ * negative error code.
+ */
+
+static int is_sdca_endpoint_present(struct device *dev,
+ struct asoc_sdw_codec_info *codec_info,
+ const struct snd_soc_acpi_link_adr *adr_link,
+ int adr_index, int end_index)
+{
+ const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
+ const struct snd_soc_acpi_endpoint *adr_end;
+ const struct asoc_sdw_dai_info *dai_info;
+ struct snd_soc_dai_link_component *dlc;
+ struct snd_soc_dai *codec_dai;
+ struct sdw_slave *slave;
+ struct device *sdw_dev;
+ const char *sdw_codec_name;
+ int ret, i;
+
+ dlc = kzalloc(sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ adr_end = &adr_dev->endpoints[end_index];
+ dai_info = &codec_info->dais[adr_end->num];
+
+ dlc->dai_name = dai_info->dai_name;
+ codec_dai = snd_soc_find_dai_with_mutex(dlc);
+ if (!codec_dai) {
+ dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
+ kfree(dlc);
+ return -EPROBE_DEFER;
+ }
+ kfree(dlc);
+
+ sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
+ if (!sdw_codec_name)
+ return -ENOMEM;
+
+ sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
+ if (!sdw_dev) {
+ dev_err(dev, "codec %s not found\n", sdw_codec_name);
+ return -EINVAL;
+ }
+
+ slave = dev_to_sdw_dev(sdw_dev);
+ if (!slave) {
+ ret = -EINVAL;
+ goto put_device;
+ }
+
+ /* Make sure BIOS provides SDCA properties */
+ if (!slave->sdca_data.interface_revision) {
+ dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
+ ret = 1;
+ goto put_device;
+ }
+
+ for (i = 0; i < slave->sdca_data.num_functions; i++) {
+ int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);
+
+ if (dai_type == dai_info->dai_type) {
+ dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
+ dai_type, slave->sdca_data.function[i].name);
+ ret = 1;
+ goto put_device;
+ }
+ }
+
+ dev_dbg(&slave->dev,
+ "SDCA device function for DAI type %d not supported, skip endpoint\n",
+ dai_info->dai_type);
+
+ ret = 0;
+
+put_device:
+ put_device(sdw_dev);
+ return ret;
+}
+
+int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
+ struct snd_soc_aux_dev *soc_aux,
+ struct asoc_sdw_dailink *soc_dais,
+ struct asoc_sdw_endpoint *soc_ends,
+ int *num_devs)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ const struct snd_soc_acpi_link_adr *adr_link;
+ struct asoc_sdw_endpoint *soc_end = soc_ends;
+ int num_dais = 0;
+ int i, j;
+ int ret;
+
+ for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
+ int num_link_dailinks = 0;
+
+ if (!is_power_of_2(adr_link->mask)) {
+ dev_err(dev, "link with multiple mask bits: 0x%x\n",
+ adr_link->mask);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < adr_link->num_adr; i++) {
+ const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
+ struct asoc_sdw_codec_info *codec_info;
+ const char *codec_name;
+ bool check_sdca = false;
+
+ if (!adr_dev->name_prefix) {
+ dev_err(dev, "codec 0x%llx does not have a name prefix\n",
+ adr_dev->adr);
+ return -EINVAL;
+ }
+
+ codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
+ if (!codec_info)
+ return -EINVAL;
+
+ for (j = 0; j < codec_info->aux_num; j++) {
+ soc_aux->dlc.name = codec_info->auxs[j].codec_name;
+ soc_aux++;
+ }
+
+ ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
+
+ if (codec_info->count_sidecar && codec_info->add_sidecar) {
+ ret = codec_info->count_sidecar(card, &num_dais, num_devs);
+ if (ret)
+ return ret;
+
+ soc_end->include_sidecar = true;
+ }
+
+ if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1)
+ check_sdca = true;
+
+ for (j = 0; j < adr_dev->num_endpoints; j++) {
+ const struct snd_soc_acpi_endpoint *adr_end;
+ const struct asoc_sdw_dai_info *dai_info;
+ struct asoc_sdw_dailink *soc_dai;
+ int stream;
+
+ adr_end = &adr_dev->endpoints[j];
+ dai_info = &codec_info->dais[adr_end->num];
+ soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
+
+ /*
+ * quirk should have higher priority than the sdca properties
+ * in the BIOS. We can't always check the DAI quirk because we
+ * will set the mc_quirk when the BIOS doesn't provide the right
+ * information. The endpoint will be skipped if the dai_info->
+ * quirk_exclude and mc_quirk are both not set if we always skip
+ * the endpoint according to the quirk information. We need to
+ * keep the endpoint if it is present in the BIOS. So, only
+ * check the DAI quirk when the mc_quirk is set or SDCA endpoint
+ * present check is not needed.
+ */
+ if (dai_info->quirk & ctx->mc_quirk || !check_sdca) {
+ /*
+ * Check the endpoint if a matching quirk is set or SDCA
+ * endpoint check is not necessary
+ */
+ if (dai_info->quirk &&
+ !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
+ continue;
+ } else {
+ /* Check SDCA codec endpoint if there is no matching quirk */
+ ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j);
+ if (ret < 0)
+ return ret;
+
+ /* The endpoint is not present, skip */
+ if (!ret)
+ continue;
+ }
+
+ dev_dbg(dev,
+ "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
+ ffs(adr_link->mask) - 1, adr_dev->adr,
+ adr_end->num, dai_info->dai_type,
+ dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
+ dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
+ adr_end->aggregated ? "group" : "solo",
+ adr_end->group_id);
+
+ if (adr_end->num >= codec_info->dai_num) {
+ dev_err(dev,
+ "%d is too many endpoints for codec: 0x%x\n",
+ adr_end->num, codec_info->part_id);
+ return -EINVAL;
+ }
+
+ for_each_pcm_streams(stream) {
+ if (dai_info->direction[stream] &&
+ dai_info->dailink[stream] < 0) {
+ dev_err(dev,
+ "Invalid dailink id %d for codec: 0x%x\n",
+ dai_info->dailink[stream],
+ codec_info->part_id);
+ return -EINVAL;
+ }
+
+ if (dai_info->direction[stream]) {
+ num_dais += !soc_dai->num_devs[stream];
+ soc_dai->num_devs[stream]++;
+ soc_dai->link_mask[stream] |= adr_link->mask;
+ }
+ }
+
+ num_link_dailinks += !!list_empty(&soc_dai->endpoints);
+ list_add_tail(&soc_end->list, &soc_dai->endpoints);
+
+ codec_name = asoc_sdw_get_codec_name(dev, dai_info,
+ adr_link, i);
+ if (!codec_name)
+ return -ENOMEM;
+
+ dev_dbg(dev, "Adding prefix %s for %s\n",
+ adr_dev->name_prefix, codec_name);
+
+ soc_end->name_prefix = adr_dev->name_prefix;
+
+ soc_end->link_mask = adr_link->mask;
+ soc_end->codec_name = codec_name;
+ soc_end->codec_info = codec_info;
+ soc_end->dai_info = dai_info;
+ soc_end++;
+ }
+ }
+
+ ctx->append_dai_type |= (num_link_dailinks > 1);
+ }
+
+ return num_dais;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, "SND_SOC_SDW_UTILS");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SoundWire ASoC helpers");
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
deleted file mode 100644
index a89ddf758695..000000000000
--- a/sound/soc/sh/rcar/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o
-obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
-
-snd-soc-rsrc-card-objs := rsrc-card.o
-obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
deleted file mode 100644
index 6d3ef366d536..000000000000
--- a/sound/soc/sh/rcar/adg.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Helper routines for R-Car sound ADG.
- *
- * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/clk-provider.h>
-#include "rsnd.h"
-
-#define CLKA 0
-#define CLKB 1
-#define CLKC 2
-#define CLKI 3
-#define CLKMAX 4
-
-#define CLKOUT 0
-#define CLKOUT1 1
-#define CLKOUT2 2
-#define CLKOUT3 3
-#define CLKOUTMAX 4
-
-#define BRRx_MASK(x) (0x3FF & x)
-
-static struct rsnd_mod_ops adg_ops = {
- .name = "adg",
-};
-
-struct rsnd_adg {
- struct clk *clk[CLKMAX];
- struct clk *clkout[CLKOUTMAX];
- struct clk_onecell_data onecell;
- struct rsnd_mod mod;
-
- int rbga_rate_for_441khz; /* RBGA */
- int rbgb_rate_for_48khz; /* RBGB */
-};
-
-#define for_each_rsnd_clk(pos, adg, i) \
- for (i = 0; \
- (i < CLKMAX) && \
- ((pos) = adg->clk[i]); \
- i++)
-#define for_each_rsnd_clkout(pos, adg, i) \
- for (i = 0; \
- (i < CLKOUTMAX) && \
- ((pos) = adg->clkout[i]); \
- i++)
-#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
-
-static u32 rsnd_adg_calculate_rbgx(unsigned long div)
-{
- int i, ratio;
-
- if (!div)
- return 0;
-
- for (i = 3; i >= 0; i--) {
- ratio = 2 << (i * 2);
- if (0 == (div % ratio))
- return (u32)((i << 8) | ((div / ratio) - 1));
- }
-
- return ~0;
-}
-
-static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
-{
- struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
- int id = rsnd_mod_id(ssi_mod);
- int ws = id;
-
- if (rsnd_ssi_is_pin_sharing(io)) {
- switch (id) {
- case 1:
- case 2:
- ws = 0;
- break;
- case 4:
- ws = 3;
- break;
- case 8:
- ws = 7;
- break;
- }
- }
-
- return (0x6 + ws) << 8;
-}
-
-int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- int id = rsnd_mod_id(cmd_mod);
- int shift = (id % 2) ? 16 : 0;
- u32 mask, val;
-
- val = rsnd_adg_ssi_ws_timing_gen2(io);
-
- val = val << shift;
- mask = 0xffff << shift;
-
- rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
-
- return 0;
-}
-
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io,
- u32 timsel)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- int is_play = rsnd_io_is_play(io);
- int id = rsnd_mod_id(src_mod);
- int shift = (id % 2) ? 16 : 0;
- u32 mask, ws;
- u32 in, out;
-
- rsnd_mod_confirm_src(src_mod);
-
- ws = rsnd_adg_ssi_ws_timing_gen2(io);
-
- in = (is_play) ? timsel : ws;
- out = (is_play) ? ws : timsel;
-
- in = in << shift;
- out = out << shift;
- mask = 0xffff << shift;
-
- switch (id / 2) {
- case 0:
- rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in);
- rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
- break;
- case 1:
- rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in);
- rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
- break;
- case 2:
- rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in);
- rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
- break;
- case 3:
- rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in);
- rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
- break;
- case 4:
- rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in);
- rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
- break;
- }
-
- return 0;
-}
-
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io,
- unsigned int src_rate,
- unsigned int dst_rate)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- struct device *dev = rsnd_priv_to_dev(priv);
- int idx, sel, div, step, ret;
- u32 val, en;
- unsigned int min, diff;
- unsigned int sel_rate [] = {
- clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
- clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
- clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
- adg->rbga_rate_for_441khz, /* 0011: RBGA */
- adg->rbgb_rate_for_48khz, /* 0100: RBGB */
- };
-
- rsnd_mod_confirm_src(src_mod);
-
- min = ~0;
- val = 0;
- en = 0;
- for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
- idx = 0;
- step = 2;
-
- if (!sel_rate[sel])
- continue;
-
- for (div = 2; div <= 98304; div += step) {
- diff = abs(src_rate - sel_rate[sel] / div);
- if (min > diff) {
- val = (sel << 8) | idx;
- min = diff;
- en = 1 << (sel + 1); /* fixme */
- }
-
- /*
- * step of 0_0000 / 0_0001 / 0_1101
- * are out of order
- */
- if ((idx > 2) && (idx % 2))
- step *= 2;
- if (idx == 0x1c) {
- div += step;
- step *= 2;
- }
- idx++;
- }
- }
-
- if (min == ~0) {
- dev_err(dev, "no Input clock\n");
- return -EIO;
- }
-
- ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
- if (ret < 0) {
- dev_err(dev, "timsel error\n");
- return ret;
- }
-
- rsnd_mod_bset(adg_mod, DIV_EN, en, en);
-
- dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
-
- return 0;
-}
-
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io)
-{
- u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
-
- rsnd_mod_confirm_src(src_mod);
-
- return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
-}
-
-static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- int id = rsnd_mod_id(ssi_mod);
- int shift = (id % 4) * 8;
- u32 mask = 0xFF << shift;
-
- rsnd_mod_confirm_ssi(ssi_mod);
-
- val = val << shift;
-
- /*
- * SSI 8 is not connected to ADG.
- * it works with SSI 7
- */
- if (id == 8)
- return;
-
- switch (id / 4) {
- case 0:
- rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
- break;
- case 1:
- rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
- break;
- case 2:
- rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
- break;
- }
-}
-
-int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
-{
- rsnd_adg_set_ssi_clk(ssi_mod, 0);
-
- return 0;
-}
-
-int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct clk *clk;
- int i;
- u32 data;
- int sel_table[] = {
- [CLKA] = 0x1,
- [CLKB] = 0x2,
- [CLKC] = 0x3,
- [CLKI] = 0x0,
- };
-
- dev_dbg(dev, "request clock = %d\n", rate);
-
- /*
- * find suitable clock from
- * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
- */
- data = 0;
- for_each_rsnd_clk(clk, adg, i) {
- if (rate == clk_get_rate(clk)) {
- data = sel_table[i];
- goto found_clock;
- }
- }
-
- /*
- * find divided clock from BRGA/BRGB
- */
- if (rate == adg->rbga_rate_for_441khz) {
- data = 0x10;
- goto found_clock;
- }
-
- if (rate == adg->rbgb_rate_for_48khz) {
- data = 0x20;
- goto found_clock;
- }
-
- return -EIO;
-
-found_clock:
-
- rsnd_adg_set_ssi_clk(ssi_mod, data);
-
- dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
- rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
- data, rate);
-
- return 0;
-}
-
-static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
- struct rsnd_adg *adg)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct clk *clk;
- static const char * const clk_name[] = {
- [CLKA] = "clk_a",
- [CLKB] = "clk_b",
- [CLKC] = "clk_c",
- [CLKI] = "clk_i",
- };
- int i, ret;
-
- for (i = 0; i < CLKMAX; i++) {
- clk = devm_clk_get(dev, clk_name[i]);
- adg->clk[i] = IS_ERR(clk) ? NULL : clk;
- }
-
- for_each_rsnd_clk(clk, adg, i) {
- ret = clk_prepare_enable(clk);
- if (ret < 0)
- dev_warn(dev, "can't use clk %d\n", i);
-
- dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
- }
-}
-
-static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
- struct rsnd_adg *adg)
-{
- struct clk *clk;
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct device_node *np = dev->of_node;
- u32 ckr, rbgx, rbga, rbgb;
- u32 rate, req_rate = 0, div;
- uint32_t count = 0;
- unsigned long req_48kHz_rate, req_441kHz_rate;
- int i;
- const char *parent_clk_name = NULL;
- static const char * const clkout_name[] = {
- [CLKOUT] = "audio_clkout",
- [CLKOUT1] = "audio_clkout1",
- [CLKOUT2] = "audio_clkout2",
- [CLKOUT3] = "audio_clkout3",
- };
- int brg_table[] = {
- [CLKA] = 0x0,
- [CLKB] = 0x1,
- [CLKC] = 0x4,
- [CLKI] = 0x2,
- };
-
- of_property_read_u32(np, "#clock-cells", &count);
-
- /*
- * ADG supports BRRA/BRRB output only
- * this means all clkout0/1/2/3 will be same rate
- */
- of_property_read_u32(np, "clock-frequency", &req_rate);
- req_48kHz_rate = 0;
- req_441kHz_rate = 0;
- if (0 == (req_rate % 44100))
- req_441kHz_rate = req_rate;
- if (0 == (req_rate % 48000))
- req_48kHz_rate = req_rate;
-
- /*
- * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
- * have 44.1kHz or 48kHz base clocks for now.
- *
- * SSI itself can divide parent clock by 1/1 - 1/16
- * see
- * rsnd_adg_ssi_clk_try_start()
- * rsnd_ssi_master_clk_start()
- */
- ckr = 0;
- rbga = 2; /* default 1/6 */
- rbgb = 2; /* default 1/6 */
- adg->rbga_rate_for_441khz = 0;
- adg->rbgb_rate_for_48khz = 0;
- for_each_rsnd_clk(clk, adg, i) {
- rate = clk_get_rate(clk);
-
- if (0 == rate) /* not used */
- continue;
-
- /* RBGA */
- if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
- div = 6;
- if (req_441kHz_rate)
- div = rate / req_441kHz_rate;
- rbgx = rsnd_adg_calculate_rbgx(div);
- if (BRRx_MASK(rbgx) == rbgx) {
- rbga = rbgx;
- adg->rbga_rate_for_441khz = rate / div;
- ckr |= brg_table[i] << 20;
- if (req_441kHz_rate)
- parent_clk_name = __clk_get_name(clk);
- }
- }
-
- /* RBGB */
- if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
- div = 6;
- if (req_48kHz_rate)
- div = rate / req_48kHz_rate;
- rbgx = rsnd_adg_calculate_rbgx(div);
- if (BRRx_MASK(rbgx) == rbgx) {
- rbgb = rbgx;
- adg->rbgb_rate_for_48khz = rate / div;
- ckr |= brg_table[i] << 16;
- if (req_48kHz_rate) {
- parent_clk_name = __clk_get_name(clk);
- ckr |= 0x80000000;
- }
- }
- }
- }
-
- /*
- * ADG supports BRRA/BRRB output only.
- * this means all clkout0/1/2/3 will be * same rate
- */
-
- /*
- * for clkout
- */
- if (!count) {
- clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
- parent_clk_name,
- (parent_clk_name) ?
- 0 : CLK_IS_ROOT, req_rate);
- if (!IS_ERR(clk)) {
- adg->clkout[CLKOUT] = clk;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
- }
- }
- /*
- * for clkout0/1/2/3
- */
- else {
- for (i = 0; i < CLKOUTMAX; i++) {
- clk = clk_register_fixed_rate(dev, clkout_name[i],
- parent_clk_name,
- (parent_clk_name) ?
- 0 : CLK_IS_ROOT,
- req_rate);
- if (!IS_ERR(clk)) {
- adg->onecell.clks = adg->clkout;
- adg->onecell.clk_num = CLKOUTMAX;
-
- adg->clkout[i] = clk;
-
- of_clk_add_provider(np, of_clk_src_onecell_get,
- &adg->onecell);
- }
- }
- }
-
- rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
- rsnd_mod_write(adg_mod, BRRA, rbga);
- rsnd_mod_write(adg_mod, BRRB, rbgb);
-
- for_each_rsnd_clkout(clk, adg, i)
- dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
- dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
- ckr, rbga, rbgb);
-}
-
-int rsnd_adg_probe(struct rsnd_priv *priv)
-{
- struct rsnd_adg *adg;
- struct device *dev = rsnd_priv_to_dev(priv);
-
- adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
- if (!adg) {
- dev_err(dev, "ADG allocate failed\n");
- return -ENOMEM;
- }
-
- /*
- * ADG is special module.
- * Use ADG mod without rsnd_mod_init() to make debug easy
- * for rsnd_write/rsnd_read
- */
- adg->mod.ops = &adg_ops;
- adg->mod.priv = priv;
-
- rsnd_adg_get_clkin(priv, adg);
- rsnd_adg_get_clkout(priv, adg);
-
- priv->adg = adg;
-
- return 0;
-}
-
-void rsnd_adg_remove(struct rsnd_priv *priv)
-{
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct clk *clk;
- int i;
-
- for_each_rsnd_clk(clk, adg, i) {
- clk_disable_unprepare(clk);
- }
-}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
deleted file mode 100644
index 02b4b085b8d7..000000000000
--- a/sound/soc/sh/rcar/core.c
+++ /dev/null
@@ -1,1185 +0,0 @@
-/*
- * Renesas R-Car SRU/SCU/SSIU/SSI support
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * Based on fsi.c
- * Kuninori Morimoto <morimoto.kuninori@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.
- */
-
-/*
- * Renesas R-Car sound device structure
- *
- * Gen1
- *
- * SRU : Sound Routing Unit
- * - SRC : Sampling Rate Converter
- * - CMD
- * - CTU : Channel Count Conversion Unit
- * - MIX : Mixer
- * - DVC : Digital Volume and Mute Function
- * - SSI : Serial Sound Interface
- *
- * Gen2
- *
- * SCU : Sampling Rate Converter Unit
- * - SRC : Sampling Rate Converter
- * - CMD
- * - CTU : Channel Count Conversion Unit
- * - MIX : Mixer
- * - DVC : Digital Volume and Mute Function
- * SSIU : Serial Sound Interface Unit
- * - SSI : Serial Sound Interface
- */
-
-/*
- * driver data Image
- *
- * rsnd_priv
- * |
- * | ** this depends on Gen1/Gen2
- * |
- * +- gen
- * |
- * | ** these depend on data path
- * | ** gen and platform data control it
- * |
- * +- rdai[0]
- * | | sru ssiu ssi
- * | +- playback -> [mod] -> [mod] -> [mod] -> ...
- * | |
- * | | sru ssiu ssi
- * | +- capture -> [mod] -> [mod] -> [mod] -> ...
- * |
- * +- rdai[1]
- * | | sru ssiu ssi
- * | +- playback -> [mod] -> [mod] -> [mod] -> ...
- * | |
- * | | sru ssiu ssi
- * | +- capture -> [mod] -> [mod] -> [mod] -> ...
- * ...
- * |
- * | ** these control ssi
- * |
- * +- ssi
- * | |
- * | +- ssi[0]
- * | +- ssi[1]
- * | +- ssi[2]
- * | ...
- * |
- * | ** these control src
- * |
- * +- src
- * |
- * +- src[0]
- * +- src[1]
- * +- src[2]
- * ...
- *
- *
- * for_each_rsnd_dai(xx, priv, xx)
- * rdai[0] => rdai[1] => rdai[2] => ...
- *
- * for_each_rsnd_mod(xx, rdai, xx)
- * [mod] => [mod] => [mod] => ...
- *
- * rsnd_dai_call(xxx, fn )
- * [mod]->fn() -> [mod]->fn() -> [mod]->fn()...
- *
- */
-#include <linux/pm_runtime.h>
-#include "rsnd.h"
-
-#define RSND_RATES SNDRV_PCM_RATE_8000_96000
-#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-
-static const struct of_device_id rsnd_of_match[] = {
- { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
- { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
- { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */
- {},
-};
-MODULE_DEVICE_TABLE(of, rsnd_of_match);
-
-/*
- * rsnd_mod functions
- */
-void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
-{
- if (mod->type != type) {
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- dev_warn(dev, "%s[%d] is not your expected module\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
-}
-
-char *rsnd_mod_name(struct rsnd_mod *mod)
-{
- if (!mod || !mod->ops)
- return "unknown";
-
- return mod->ops->name;
-}
-
-struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- if (!mod || !mod->ops || !mod->ops->dma_req)
- return NULL;
-
- return mod->ops->dma_req(io, mod);
-}
-
-int rsnd_mod_init(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- struct rsnd_mod_ops *ops,
- struct clk *clk,
- enum rsnd_mod_type type,
- int id)
-{
- int ret = clk_prepare(clk);
-
- if (ret)
- return ret;
-
- mod->id = id;
- mod->ops = ops;
- mod->type = type;
- mod->clk = clk;
- mod->priv = priv;
-
- return ret;
-}
-
-void rsnd_mod_quit(struct rsnd_mod *mod)
-{
- if (mod->clk)
- clk_unprepare(mod->clk);
-}
-
-void rsnd_mod_interrupt(struct rsnd_mod *mod,
- void (*callback)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io))
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_dai_stream *io;
- struct rsnd_dai *rdai;
- int i;
-
- for_each_rsnd_dai(rdai, priv, i) {
- io = &rdai->playback;
- if (mod == io->mod[mod->type])
- callback(mod, io);
-
- io = &rdai->capture;
- if (mod == io->mod[mod->type])
- callback(mod, io);
- }
-}
-
-int rsnd_io_is_working(struct rsnd_dai_stream *io)
-{
- /* see rsnd_dai_stream_init/quit() */
- return !!io->substream;
-}
-
-void rsnd_set_slot(struct rsnd_dai *rdai,
- int slots, int num)
-{
- rdai->slots = slots;
- rdai->slots_num = num;
-}
-
-int rsnd_get_slot(struct rsnd_dai_stream *io)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-
- return rdai->slots;
-}
-
-int rsnd_get_slot_num(struct rsnd_dai_stream *io)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-
- return rdai->slots_num;
-}
-
-int rsnd_get_slot_width(struct rsnd_dai_stream *io)
-{
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- int chan = runtime->channels;
-
- /* Multi channel Mode */
- if (rsnd_ssi_multi_slaves(io))
- chan /= rsnd_get_slot_num(io);
-
- /* TDM Extend Mode needs 8ch */
- if (chan == 6)
- chan = 8;
-
- return chan;
-}
-
-/*
- * ADINR function
- */
-u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- switch (runtime->sample_bits) {
- case 16:
- return 8 << 16;
- case 32:
- return 0 << 16;
- }
-
- dev_warn(dev, "not supported sample bits\n");
-
- return 0;
-}
-
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 chan = runtime->channels;
-
- switch (chan) {
- case 1:
- case 2:
- case 4:
- case 6:
- case 8:
- break;
- default:
- dev_warn(dev, "not supported channel\n");
- chan = 0;
- break;
- }
-
- return chan;
-}
-
-/*
- * DALIGN function
- */
-u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
-{
- struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
- struct rsnd_mod *target;
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- u32 val = 0x76543210;
- u32 mask = ~0;
-
- if (rsnd_io_is_play(io)) {
- struct rsnd_mod *src = rsnd_io_to_mod_src(io);
-
- target = src ? src : ssi;
- } else {
- struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
-
- target = cmd ? cmd : ssi;
- }
-
- mask <<= runtime->channels * 4;
- val = val & mask;
-
- switch (runtime->sample_bits) {
- case 16:
- val |= 0x67452301 & ~mask;
- break;
- case 32:
- val |= 0x76543210 & ~mask;
- break;
- }
-
- /*
- * exchange channeles on SRC if possible,
- * otherwise, R/L volume settings on DVC
- * changes inverted channels
- */
- if (mod == target)
- return val;
- else
- return 0x76543210;
-}
-
-/*
- * rsnd_dai functions
- */
-#define rsnd_mod_call(idx, io, func, param...) \
-({ \
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
- struct rsnd_mod *mod = (io)->mod[idx]; \
- struct device *dev = rsnd_priv_to_dev(priv); \
- u32 *status = (io)->mod_status + idx; \
- u32 mask = 0xF << __rsnd_mod_shift_##func; \
- u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
- u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
- int ret = 0; \
- int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
- *status = (*status & ~mask) + \
- (add << __rsnd_mod_shift_##func); \
- dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), \
- *status, call ? #func : ""); \
- if (call) \
- ret = (mod)->ops->func(mod, io, param); \
- ret; \
-})
-
-#define rsnd_dai_call(fn, io, param...) \
-({ \
- struct rsnd_mod *mod; \
- int ret = 0, i; \
- for (i = 0; i < RSND_MOD_MAX; i++) { \
- mod = (io)->mod[i]; \
- if (!mod) \
- continue; \
- ret |= rsnd_mod_call(i, io, fn, param); \
- } \
- ret; \
-})
-
-int rsnd_dai_connect(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- enum rsnd_mod_type type)
-{
- struct rsnd_priv *priv;
- struct device *dev;
-
- if (!mod)
- return -EIO;
-
- if (io->mod[type])
- return -EINVAL;
-
- priv = rsnd_mod_to_priv(mod);
- dev = rsnd_priv_to_dev(priv);
-
- io->mod[type] = mod;
-
- dev_dbg(dev, "%s[%d] is connected to io (%s)\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_io_is_play(io) ? "Playback" : "Capture");
-
- return 0;
-}
-
-static void rsnd_dai_disconnect(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- enum rsnd_mod_type type)
-{
- io->mod[type] = NULL;
-}
-
-struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
-{
- if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
- return NULL;
-
- return priv->rdai + id;
-}
-
-#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
-static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
-{
- struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
-
- return rsnd_rdai_get(priv, dai->id);
-}
-
-/*
- * rsnd_soc_dai functions
- */
-int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
-{
- struct snd_pcm_substream *substream = io->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int pos = io->byte_pos + additional;
-
- pos %= (runtime->periods * io->byte_per_period);
-
- return pos;
-}
-
-bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
-{
- io->byte_pos += byte;
-
- if (io->byte_pos >= io->next_period_byte) {
- struct snd_pcm_substream *substream = io->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- io->period_pos++;
- io->next_period_byte += io->byte_per_period;
-
- if (io->period_pos >= runtime->periods) {
- io->byte_pos = 0;
- io->period_pos = 0;
- io->next_period_byte = io->byte_per_period;
- }
-
- return true;
- }
-
- return false;
-}
-
-void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
-{
- struct snd_pcm_substream *substream = io->substream;
-
- /*
- * this function should be called...
- *
- * - if rsnd_dai_pointer_update() returns true
- * - without spin lock
- */
-
- snd_pcm_period_elapsed(substream);
-}
-
-static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
- struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- io->substream = substream;
- io->byte_pos = 0;
- io->period_pos = 0;
- io->byte_per_period = runtime->period_size *
- runtime->channels *
- samples_to_bytes(runtime, 1);
- io->next_period_byte = io->byte_per_period;
-}
-
-static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
-{
- io->substream = NULL;
-}
-
-static
-struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- return rtd->cpu_dai;
-}
-
-static
-struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
- struct snd_pcm_substream *substream)
-{
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return &rdai->playback;
- else
- return &rdai->capture;
-}
-
-static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- rsnd_dai_stream_init(io, substream);
-
- ret = rsnd_dai_call(init, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
-
- ret = rsnd_dai_call(start, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- ret = rsnd_dai_call(stop, io, priv);
-
- ret |= rsnd_dai_call(quit, io, priv);
-
- rsnd_dai_stream_quit(io);
- break;
- default:
- ret = -EINVAL;
- }
-
-dai_trigger_end:
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return ret;
-}
-
-static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
-{
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
-
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- rdai->clk_master = 0;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- rdai->clk_master = 1; /* codec is slave, cpu is master */
- break;
- default:
- return -EINVAL;
- }
-
- /* set format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- rdai->sys_delay = 0;
- rdai->data_alignment = 0;
- rdai->frm_clk_inv = 0;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- rdai->sys_delay = 1;
- rdai->data_alignment = 0;
- rdai->frm_clk_inv = 1;
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- rdai->sys_delay = 1;
- rdai->data_alignment = 1;
- rdai->frm_clk_inv = 1;
- break;
- }
-
- /* set clock inversion */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_IF:
- rdai->bit_clk_inv = rdai->bit_clk_inv;
- rdai->frm_clk_inv = !rdai->frm_clk_inv;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- rdai->bit_clk_inv = !rdai->bit_clk_inv;
- rdai->frm_clk_inv = rdai->frm_clk_inv;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- rdai->bit_clk_inv = !rdai->bit_clk_inv;
- rdai->frm_clk_inv = !rdai->frm_clk_inv;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- default:
- break;
- }
-
- return 0;
-}
-
-static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
- u32 tx_mask, u32 rx_mask,
- int slots, int slot_width)
-{
- struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- switch (slots) {
- case 6:
- /* TDM Extend Mode */
- rsnd_set_slot(rdai, slots, 1);
- break;
- default:
- dev_err(dev, "unsupported TDM slots (%d)\n", slots);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
- .trigger = rsnd_soc_dai_trigger,
- .set_fmt = rsnd_soc_dai_set_fmt,
- .set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
-};
-
-void rsnd_parse_connect_common(struct rsnd_dai *rdai,
- struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
- struct device_node *node,
- struct device_node *playback,
- struct device_node *capture)
-{
- struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
- struct device_node *np;
- struct rsnd_mod *mod;
- int i;
-
- if (!node)
- return;
-
- i = 0;
- for_each_child_of_node(node, np) {
- mod = mod_get(priv, i);
- if (np == playback)
- rsnd_dai_connect(mod, &rdai->playback, mod->type);
- if (np == capture)
- rsnd_dai_connect(mod, &rdai->capture, mod->type);
- i++;
- }
-
- of_node_put(node);
-}
-
-static int rsnd_dai_probe(struct rsnd_priv *priv)
-{
- struct device_node *dai_node;
- struct device_node *dai_np;
- struct device_node *playback, *capture;
- struct rsnd_dai_stream *io_playback;
- struct rsnd_dai_stream *io_capture;
- struct snd_soc_dai_driver *rdrv, *drv;
- struct rsnd_dai *rdai;
- struct device *dev = rsnd_priv_to_dev(priv);
- int nr, dai_i, io_i;
- int ret;
-
- dai_node = rsnd_dai_of_node(priv);
- nr = of_get_child_count(dai_node);
- if (!nr) {
- ret = -EINVAL;
- goto rsnd_dai_probe_done;
- }
-
- rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
- rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
- if (!rdrv || !rdai) {
- ret = -ENOMEM;
- goto rsnd_dai_probe_done;
- }
-
- priv->rdai_nr = nr;
- priv->daidrv = rdrv;
- priv->rdai = rdai;
-
- /*
- * parse all dai
- */
- dai_i = 0;
- for_each_child_of_node(dai_node, dai_np) {
- rdai = rsnd_rdai_get(priv, dai_i);
- drv = rdrv + dai_i;
- io_playback = &rdai->playback;
- io_capture = &rdai->capture;
-
- snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
-
- rdai->priv = priv;
- drv->name = rdai->name;
- drv->ops = &rsnd_soc_dai_ops;
-
- snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
- "DAI%d Playback", dai_i);
- drv->playback.rates = RSND_RATES;
- drv->playback.formats = RSND_FMTS;
- drv->playback.channels_min = 2;
- drv->playback.channels_max = 6;
- drv->playback.stream_name = rdai->playback.name;
-
- snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
- "DAI%d Capture", dai_i);
- drv->capture.rates = RSND_RATES;
- drv->capture.formats = RSND_FMTS;
- drv->capture.channels_min = 2;
- drv->capture.channels_max = 6;
- drv->capture.stream_name = rdai->capture.name;
-
- rdai->playback.rdai = rdai;
- rdai->capture.rdai = rdai;
- rsnd_set_slot(rdai, 2, 1); /* default */
-
- for (io_i = 0;; io_i++) {
- playback = of_parse_phandle(dai_np, "playback", io_i);
- capture = of_parse_phandle(dai_np, "capture", io_i);
-
- if (!playback && !capture)
- break;
-
- rsnd_parse_connect_ssi(rdai, playback, capture);
- rsnd_parse_connect_src(rdai, playback, capture);
- rsnd_parse_connect_ctu(rdai, playback, capture);
- rsnd_parse_connect_mix(rdai, playback, capture);
- rsnd_parse_connect_dvc(rdai, playback, capture);
-
- of_node_put(playback);
- of_node_put(capture);
- }
-
- dai_i++;
-
- dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
- rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
- rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
- }
-
- ret = 0;
-
-rsnd_dai_probe_done:
- of_node_put(dai_node);
-
- return ret;
-}
-
-/*
- * pcm ops
- */
-static struct snd_pcm_hardware rsnd_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID,
- .buffer_bytes_max = 64 * 1024,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 32,
- .fifo_size = 256,
-};
-
-static int rsnd_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret = 0;
-
- snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware);
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
-
- return ret;
-}
-
-static int rsnd_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
- int ret;
-
- ret = rsnd_dai_call(hw_params, io, substream, hw_params);
- if (ret)
- return ret;
-
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
-
- return bytes_to_frames(runtime, io->byte_pos);
-}
-
-static struct snd_pcm_ops rsnd_pcm_ops = {
- .open = rsnd_pcm_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = rsnd_hw_params,
- .hw_free = snd_pcm_lib_free_pages,
- .pointer = rsnd_pointer,
-};
-
-/*
- * snd_kcontrol
- */
-#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
-static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_info *uinfo)
-{
- struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
-
- if (cfg->texts) {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = cfg->size;
- uinfo->value.enumerated.items = cfg->max;
- if (uinfo->value.enumerated.item >= cfg->max)
- uinfo->value.enumerated.item = cfg->max - 1;
- strlcpy(uinfo->value.enumerated.name,
- cfg->texts[uinfo->value.enumerated.item],
- sizeof(uinfo->value.enumerated.name));
- } else {
- uinfo->count = cfg->size;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = cfg->max;
- uinfo->type = (cfg->max == 1) ?
- SNDRV_CTL_ELEM_TYPE_BOOLEAN :
- SNDRV_CTL_ELEM_TYPE_INTEGER;
- }
-
- return 0;
-}
-
-static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *uc)
-{
- struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
- int i;
-
- for (i = 0; i < cfg->size; i++)
- if (cfg->texts)
- uc->value.enumerated.item[i] = cfg->val[i];
- else
- uc->value.integer.value[i] = cfg->val[i];
-
- return 0;
-}
-
-static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *uc)
-{
- struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
- struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
- int i, change = 0;
-
- for (i = 0; i < cfg->size; i++) {
- if (cfg->texts) {
- change |= (uc->value.enumerated.item[i] != cfg->val[i]);
- cfg->val[i] = uc->value.enumerated.item[i];
- } else {
- change |= (uc->value.integer.value[i] != cfg->val[i]);
- cfg->val[i] = uc->value.integer.value[i];
- }
- }
-
- if (change)
- cfg->update(cfg->io, mod);
-
- return change;
-}
-
-static int __rsnd_kctrl_new(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- struct rsnd_kctrl_cfg *cfg,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod))
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_kcontrol *kctrl;
- struct snd_kcontrol_new knew = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = name,
- .info = rsnd_kctrl_info,
- .index = rtd->num,
- .get = rsnd_kctrl_get,
- .put = rsnd_kctrl_put,
- .private_value = (unsigned long)cfg,
- };
- int ret;
-
- kctrl = snd_ctl_new1(&knew, mod);
- if (!kctrl)
- return -ENOMEM;
-
- ret = snd_ctl_add(card, kctrl);
- if (ret < 0) {
- snd_ctl_free_one(kctrl);
- return ret;
- }
-
- cfg->update = update;
- cfg->card = card;
- cfg->kctrl = kctrl;
- cfg->io = io;
-
- return 0;
-}
-
-void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
-{
- snd_ctl_remove(cfg->card, cfg->kctrl);
-}
-
-int rsnd_kctrl_new_m(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_m *_cfg,
- int ch_size,
- u32 max)
-{
- if (ch_size > RSND_DVC_CHANNELS)
- return -EINVAL;
-
- _cfg->cfg.max = max;
- _cfg->cfg.size = ch_size;
- _cfg->cfg.val = _cfg->val;
- return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
-int rsnd_kctrl_new_s(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_s *_cfg,
- u32 max)
-{
- _cfg->cfg.max = max;
- _cfg->cfg.size = 1;
- _cfg->cfg.val = &_cfg->val;
- return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
-int rsnd_kctrl_new_e(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- struct rsnd_kctrl_cfg_s *_cfg,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- const char * const *texts,
- u32 max)
-{
- _cfg->cfg.max = max;
- _cfg->cfg.size = 1;
- _cfg->cfg.val = &_cfg->val;
- _cfg->cfg.texts = texts;
- return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
-/*
- * snd_soc_platform
- */
-
-#define PREALLOC_BUFFER (32 * 1024)
-#define PREALLOC_BUFFER_MAX (32 * 1024)
-
-static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *dai = rtd->cpu_dai;
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- int ret;
-
- ret = rsnd_dai_call(pcm_new, &rdai->playback, rtd);
- if (ret)
- return ret;
-
- ret = rsnd_dai_call(pcm_new, &rdai->capture, rtd);
- if (ret)
- return ret;
-
- return snd_pcm_lib_preallocate_pages_for_all(
- rtd->pcm,
- SNDRV_DMA_TYPE_DEV,
- rtd->card->snd_card->dev,
- PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
-}
-
-static struct snd_soc_platform_driver rsnd_soc_platform = {
- .ops = &rsnd_pcm_ops,
- .pcm_new = rsnd_pcm_new,
-};
-
-static const struct snd_soc_component_driver rsnd_soc_component = {
- .name = "rsnd",
-};
-
-static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io)
-{
- int ret;
-
- ret = rsnd_dai_call(probe, io, priv);
- if (ret == -EAGAIN) {
- struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
- int i;
-
- /*
- * Fallback to PIO mode
- */
-
- /*
- * call "remove" for SSI/SRC/DVC
- * SSI will be switch to PIO mode if it was DMA mode
- * see
- * rsnd_dma_init()
- * rsnd_ssi_fallback()
- */
- rsnd_dai_call(remove, io, priv);
-
- /*
- * remove all mod from io
- * and, re connect ssi
- */
- for (i = 0; i < RSND_MOD_MAX; i++)
- rsnd_dai_disconnect((io)->mod[i], io, i);
- rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
-
- /*
- * fallback
- */
- rsnd_dai_call(fallback, io, priv);
-
- /*
- * retry to "probe".
- * DAI has SSI which is PIO mode only now.
- */
- ret = rsnd_dai_call(probe, io, priv);
- }
-
- return ret;
-}
-
-/*
- * rsnd probe
- */
-static int rsnd_probe(struct platform_device *pdev)
-{
- struct rsnd_priv *priv;
- struct device *dev = &pdev->dev;
- struct rsnd_dai *rdai;
- const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
- int (*probe_func[])(struct rsnd_priv *priv) = {
- rsnd_gen_probe,
- rsnd_dma_probe,
- rsnd_ssi_probe,
- rsnd_ssiu_probe,
- rsnd_src_probe,
- rsnd_ctu_probe,
- rsnd_mix_probe,
- rsnd_dvc_probe,
- rsnd_cmd_probe,
- rsnd_adg_probe,
- rsnd_dai_probe,
- };
- int ret, i;
-
- /*
- * init priv data
- */
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(dev, "priv allocate failed\n");
- return -ENODEV;
- }
-
- priv->pdev = pdev;
- priv->flags = (unsigned long)of_id->data;
- spin_lock_init(&priv->lock);
-
- /*
- * init each module
- */
- for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
- ret = probe_func[i](priv);
- if (ret)
- return ret;
- }
-
- for_each_rsnd_dai(rdai, priv, i) {
- ret = rsnd_rdai_continuance_probe(priv, &rdai->playback);
- if (ret)
- goto exit_snd_probe;
-
- ret = rsnd_rdai_continuance_probe(priv, &rdai->capture);
- if (ret)
- goto exit_snd_probe;
- }
-
- dev_set_drvdata(dev, priv);
-
- /*
- * asoc register
- */
- ret = snd_soc_register_platform(dev, &rsnd_soc_platform);
- if (ret < 0) {
- dev_err(dev, "cannot snd soc register\n");
- return ret;
- }
-
- ret = snd_soc_register_component(dev, &rsnd_soc_component,
- priv->daidrv, rsnd_rdai_nr(priv));
- if (ret < 0) {
- dev_err(dev, "cannot snd dai register\n");
- goto exit_snd_soc;
- }
-
- pm_runtime_enable(dev);
-
- dev_info(dev, "probed\n");
- return ret;
-
-exit_snd_soc:
- snd_soc_unregister_platform(dev);
-exit_snd_probe:
- for_each_rsnd_dai(rdai, priv, i) {
- rsnd_dai_call(remove, &rdai->playback, priv);
- rsnd_dai_call(remove, &rdai->capture, priv);
- }
-
- return ret;
-}
-
-static int rsnd_remove(struct platform_device *pdev)
-{
- struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
- struct rsnd_dai *rdai;
- void (*remove_func[])(struct rsnd_priv *priv) = {
- rsnd_ssi_remove,
- rsnd_ssiu_remove,
- rsnd_src_remove,
- rsnd_ctu_remove,
- rsnd_mix_remove,
- rsnd_dvc_remove,
- rsnd_cmd_remove,
- rsnd_adg_remove,
- };
- int ret = 0, i;
-
- pm_runtime_disable(&pdev->dev);
-
- for_each_rsnd_dai(rdai, priv, i) {
- ret |= rsnd_dai_call(remove, &rdai->playback, priv);
- ret |= rsnd_dai_call(remove, &rdai->capture, priv);
- }
-
- for (i = 0; i < ARRAY_SIZE(remove_func); i++)
- remove_func[i](priv);
-
- snd_soc_unregister_component(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
-
- return ret;
-}
-
-static struct platform_driver rsnd_driver = {
- .driver = {
- .name = "rcar_sound",
- .of_match_table = rsnd_of_match,
- },
- .probe = rsnd_probe,
- .remove = rsnd_remove,
-};
-module_platform_driver(rsnd_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Renesas R-Car audio driver");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
-MODULE_ALIAS("platform:rcar-pcm-audio");
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
deleted file mode 100644
index d53a225d19e9..000000000000
--- a/sound/soc/sh/rcar/ctu.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * ctu.c
- *
- * Copyright (c) 2015 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 "rsnd.h"
-
-#define CTU_NAME_SIZE 16
-#define CTU_NAME "ctu"
-
-struct rsnd_ctu {
- struct rsnd_mod mod;
-};
-
-#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
-#define for_each_rsnd_ctu(pos, priv, i) \
- for ((i) = 0; \
- ((i) < rsnd_ctu_nr(priv)) && \
- ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
- i++)
-
-#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
-#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
-#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
-static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
-{
- rsnd_mod_write(mod, CTU_CTUIR, enable);
-}
-
-static int rsnd_ctu_probe_(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
-}
-
-static int rsnd_ctu_init(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- rsnd_mod_power_on(mod);
-
- rsnd_ctu_initialize_lock(mod);
-
- rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
-
- rsnd_ctu_initialize_unlock(mod);
-
- return 0;
-}
-
-static int rsnd_ctu_quit(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- rsnd_mod_power_off(mod);
-
- return 0;
-}
-
-static struct rsnd_mod_ops rsnd_ctu_ops = {
- .name = CTU_NAME,
- .probe = rsnd_ctu_probe_,
- .init = rsnd_ctu_init,
- .quit = rsnd_ctu_quit,
-};
-
-struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
-{
- if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
- id = 0;
-
- return rsnd_mod_get(rsnd_ctu_get(priv, id));
-}
-
-int rsnd_ctu_probe(struct rsnd_priv *priv)
-{
- struct device_node *node;
- struct device_node *np;
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_ctu *ctu;
- struct clk *clk;
- char name[CTU_NAME_SIZE];
- int i, nr, ret;
-
- /* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv))
- return 0;
-
- node = rsnd_ctu_of_node(priv);
- if (!node)
- return 0; /* not used is not error */
-
- nr = of_get_child_count(node);
- if (!nr) {
- ret = -EINVAL;
- goto rsnd_ctu_probe_done;
- }
-
- ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
- if (!ctu) {
- ret = -ENOMEM;
- goto rsnd_ctu_probe_done;
- }
-
- priv->ctu_nr = nr;
- priv->ctu = ctu;
-
- i = 0;
- ret = 0;
- for_each_child_of_node(node, np) {
- ctu = rsnd_ctu_get(priv, i);
-
- /*
- * CTU00, CTU01, CTU02, CTU03 => CTU0
- * CTU10, CTU11, CTU12, CTU13 => CTU1
- */
- snprintf(name, CTU_NAME_SIZE, "%s.%d",
- CTU_NAME, i / 4);
-
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto rsnd_ctu_probe_done;
- }
-
- ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
- clk, RSND_MOD_CTU, i);
- if (ret)
- goto rsnd_ctu_probe_done;
-
- i++;
- }
-
-
-rsnd_ctu_probe_done:
- of_node_put(node);
-
- return ret;
-}
-
-void rsnd_ctu_remove(struct rsnd_priv *priv)
-{
- struct rsnd_ctu *ctu;
- int i;
-
- for_each_rsnd_ctu(ctu, priv, i) {
- rsnd_mod_quit(rsnd_mod_get(ctu));
- }
-}
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
deleted file mode 100644
index ea24247eba73..000000000000
--- a/sound/soc/sh/rcar/gen.c
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Renesas R-Car Gen1 SRU/SSI support
- *
- * 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.
- */
-
-/*
- * #define DEBUG
- *
- * you can also add below in
- * ${LINUX}/drivers/base/regmap/regmap.c
- * for regmap debug
- *
- * #define LOG_DEVICE "xxxx.rcar_sound"
- */
-
-#include "rsnd.h"
-
-struct rsnd_gen {
- struct rsnd_gen_ops *ops;
-
- /* RSND_BASE_MAX base */
- void __iomem *base[RSND_BASE_MAX];
- phys_addr_t res[RSND_BASE_MAX];
- struct regmap *regmap[RSND_BASE_MAX];
-
- /* RSND_REG_MAX base */
- struct regmap_field *regs[RSND_REG_MAX];
- const char *reg_name[RSND_REG_MAX];
-};
-
-#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
-#define rsnd_reg_name(gen, id) ((gen)->reg_name[id])
-
-struct rsnd_regmap_field_conf {
- int idx;
- unsigned int reg_offset;
- unsigned int id_offset;
- const char *reg_name;
-};
-
-#define RSND_REG_SET(id, offset, _id_offset, n) \
-{ \
- .idx = id, \
- .reg_offset = offset, \
- .id_offset = _id_offset, \
- .reg_name = n, \
-}
-/* single address mapping */
-#define RSND_GEN_S_REG(id, offset) \
- RSND_REG_SET(RSND_REG_##id, offset, 0, #id)
-
-/* multi address mapping */
-#define RSND_GEN_M_REG(id, offset, _id_offset) \
- RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id)
-
-/*
- * basic function
- */
-static int rsnd_is_accessible_reg(struct rsnd_priv *priv,
- struct rsnd_gen *gen, enum rsnd_reg reg)
-{
- if (!gen->regs[reg]) {
- struct device *dev = rsnd_priv_to_dev(priv);
-
- dev_err(dev, "unsupported register access %x\n", reg);
- return 0;
- }
-
- return 1;
-}
-
-u32 rsnd_read(struct rsnd_priv *priv,
- struct rsnd_mod *mod, enum rsnd_reg reg)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
- u32 val;
-
- if (!rsnd_is_accessible_reg(priv, gen, reg))
- return 0;
-
- regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
-
- dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_reg_name(gen, reg), reg, val);
-
- return val;
-}
-
-void rsnd_write(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 data)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- if (!rsnd_is_accessible_reg(priv, gen, reg))
- return;
-
- regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
-
- dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_reg_name(gen, reg), reg, data);
-}
-
-void rsnd_force_write(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 data)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- if (!rsnd_is_accessible_reg(priv, gen, reg))
- return;
-
- regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
-
- dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_reg_name(gen, reg), reg, data);
-}
-
-void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 mask, u32 data)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- if (!rsnd_is_accessible_reg(priv, gen, reg))
- return;
-
- regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
- mask, data);
-
- dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_reg_name(gen, reg), reg, data, mask);
-
-}
-
-phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
-{
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- return gen->res[reg_id];
-}
-
-#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \
- _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf))
-static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
- int id_size,
- int reg_id,
- const char *name,
- const struct rsnd_regmap_field_conf *conf,
- int conf_size)
-{
- struct platform_device *pdev = rsnd_priv_to_pdev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct resource *res;
- struct regmap_config regc;
- struct regmap_field *regs;
- struct regmap *regmap;
- struct reg_field regf;
- void __iomem *base;
- int i;
-
- memset(&regc, 0, sizeof(regc));
- regc.reg_bits = 32;
- regc.val_bits = 32;
- regc.reg_stride = 4;
- regc.name = name;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
- if (!res)
- res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
- if (!res)
- return -ENODEV;
-
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- regmap = devm_regmap_init_mmio(dev, base, &regc);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- /* RSND_BASE_MAX base */
- gen->base[reg_id] = base;
- gen->regmap[reg_id] = regmap;
- gen->res[reg_id] = res->start;
-
- for (i = 0; i < conf_size; i++) {
-
- regf.reg = conf[i].reg_offset;
- regf.id_offset = conf[i].id_offset;
- regf.lsb = 0;
- regf.msb = 31;
- regf.id_size = id_size;
-
- regs = devm_regmap_field_alloc(dev, regmap, regf);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- /* RSND_REG_MAX base */
- gen->regs[conf[i].idx] = regs;
- gen->reg_name[conf[i].idx] = conf[i].reg_name;
- }
-
- return 0;
-}
-
-/*
- * Gen2
- */
-static int rsnd_gen2_probe(struct rsnd_priv *priv)
-{
- const static struct rsnd_regmap_field_conf conf_ssiu[] = {
- RSND_GEN_S_REG(SSI_MODE0, 0x800),
- RSND_GEN_S_REG(SSI_MODE1, 0x804),
- RSND_GEN_S_REG(SSI_MODE2, 0x808),
- RSND_GEN_S_REG(SSI_CONTROL, 0x810),
-
- /* FIXME: it needs SSI_MODE2/3 in the future */
- RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
- RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
- RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80),
- RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80),
- RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80),
- RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
- };
-
- const static struct rsnd_regmap_field_conf conf_scu[] = {
- RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0, 0x20),
- RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4, 0x20),
- RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20),
- RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
- RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
- RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20),
- RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188, 0x20),
- RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20),
- RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20),
- RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
- RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc),
- RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0),
- RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4),
- RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
- RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
- RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
- RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40),
- RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40),
- RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
- RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
- RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
- RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
- RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
- RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
- RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
- RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),
- RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40),
- RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40),
- RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40),
- RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40),
- RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40),
- RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40),
- RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40),
- RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100),
- RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100),
- RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
- RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100),
- RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100),
- RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100),
- RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100),
- RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100),
- RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
- RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
- RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100),
- RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100),
- RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100),
- RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100),
- RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100),
- RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100),
- RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
- };
- const static struct rsnd_regmap_field_conf conf_adg[] = {
- RSND_GEN_S_REG(BRRA, 0x00),
- RSND_GEN_S_REG(BRRB, 0x04),
- RSND_GEN_S_REG(SSICKR, 0x08),
- RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
- RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
- RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14),
- RSND_GEN_S_REG(DIV_EN, 0x30),
- RSND_GEN_S_REG(SRCIN_TIMSEL0, 0x34),
- RSND_GEN_S_REG(SRCIN_TIMSEL1, 0x38),
- RSND_GEN_S_REG(SRCIN_TIMSEL2, 0x3c),
- RSND_GEN_S_REG(SRCIN_TIMSEL3, 0x40),
- RSND_GEN_S_REG(SRCIN_TIMSEL4, 0x44),
- RSND_GEN_S_REG(SRCOUT_TIMSEL0, 0x48),
- RSND_GEN_S_REG(SRCOUT_TIMSEL1, 0x4c),
- RSND_GEN_S_REG(SRCOUT_TIMSEL2, 0x50),
- RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54),
- RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
- RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
- };
- const static struct rsnd_regmap_field_conf conf_ssi[] = {
- RSND_GEN_M_REG(SSICR, 0x00, 0x40),
- RSND_GEN_M_REG(SSISR, 0x04, 0x40),
- RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
- RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
- RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
- };
- int ret_ssiu;
- int ret_scu;
- int ret_adg;
- int ret_ssi;
-
- ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu);
- ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, "scu", conf_scu);
- ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, "adg", conf_adg);
- ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, "ssi", conf_ssi);
- if (ret_ssiu < 0 ||
- ret_scu < 0 ||
- ret_adg < 0 ||
- ret_ssi < 0)
- return ret_ssiu | ret_scu | ret_adg | ret_ssi;
-
- return 0;
-}
-
-/*
- * Gen1
- */
-
-static int rsnd_gen1_probe(struct rsnd_priv *priv)
-{
- const static struct rsnd_regmap_field_conf conf_adg[] = {
- RSND_GEN_S_REG(BRRA, 0x00),
- RSND_GEN_S_REG(BRRB, 0x04),
- RSND_GEN_S_REG(SSICKR, 0x08),
- RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
- RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
- };
- const static struct rsnd_regmap_field_conf conf_ssi[] = {
- RSND_GEN_M_REG(SSICR, 0x00, 0x40),
- RSND_GEN_M_REG(SSISR, 0x04, 0x40),
- RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
- RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
- RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
- };
- int ret_adg;
- int ret_ssi;
-
- ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg);
- ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
- if (ret_adg < 0 ||
- ret_ssi < 0)
- return ret_adg | ret_ssi;
-
- return 0;
-}
-
-/*
- * Gen
- */
-int rsnd_gen_probe(struct rsnd_priv *priv)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen;
- int ret;
-
- gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
- if (!gen) {
- dev_err(dev, "GEN allocate failed\n");
- return -ENOMEM;
- }
-
- priv->gen = gen;
-
- ret = -ENODEV;
- if (rsnd_is_gen1(priv))
- ret = rsnd_gen1_probe(priv);
- else if (rsnd_is_gen2(priv))
- ret = rsnd_gen2_probe(priv);
-
- if (ret < 0)
- dev_err(dev, "unknown generation R-Car sound device\n");
-
- return ret;
-}
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
deleted file mode 100644
index 65542b6a89e9..000000000000
--- a/sound/soc/sh/rcar/mix.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * mix.c
- *
- * Copyright (c) 2015 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 "rsnd.h"
-
-#define MIX_NAME_SIZE 16
-#define MIX_NAME "mix"
-
-struct rsnd_mix {
- struct rsnd_mod mod;
-};
-
-#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
-#define rsnd_mix_nr(priv) ((priv)->mix_nr)
-#define for_each_rsnd_mix(pos, priv, i) \
- for ((i) = 0; \
- ((i) < rsnd_mix_nr(priv)) && \
- ((pos) = (struct rsnd_mix *)(priv)->mix + i); \
- i++)
-
-static void rsnd_mix_activation(struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, MIX_SWRSR, 0);
- rsnd_mod_write(mod, MIX_SWRSR, 1);
-}
-
-static void rsnd_mix_halt(struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, MIX_MIXIR, 1);
- rsnd_mod_write(mod, MIX_SWRSR, 0);
-}
-
-static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, MIX_MDBAR, 0);
- rsnd_mod_write(mod, MIX_MDBBR, 0);
- rsnd_mod_write(mod, MIX_MDBCR, 0);
- rsnd_mod_write(mod, MIX_MDBDR, 0);
-}
-
-static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, MIX_MIXIR, 1);
-
- /* General Information */
- rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
-
- /* volume step */
- rsnd_mod_write(mod, MIX_MIXMR, 0);
- rsnd_mod_write(mod, MIX_MVPDR, 0);
-
- /* common volume parameter */
- rsnd_mix_volume_parameter(io, mod);
-
- rsnd_mod_write(mod, MIX_MIXIR, 0);
-}
-
-static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- /* Disable MIX dB setting */
- rsnd_mod_write(mod, MIX_MDBER, 0);
-
- /* common volume parameter */
- rsnd_mix_volume_parameter(io, mod);
-
- /* Enable MIX dB setting */
- rsnd_mod_write(mod, MIX_MDBER, 1);
-}
-
-static int rsnd_mix_probe_(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- return rsnd_cmd_attach(io, rsnd_mod_id(mod));
-}
-
-static int rsnd_mix_init(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- rsnd_mod_power_on(mod);
-
- rsnd_mix_activation(mod);
-
- rsnd_mix_volume_init(io, mod);
-
- rsnd_mix_volume_update(io, mod);
-
- return 0;
-}
-
-static int rsnd_mix_quit(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- rsnd_mix_halt(mod);
-
- rsnd_mod_power_off(mod);
-
- return 0;
-}
-
-static struct rsnd_mod_ops rsnd_mix_ops = {
- .name = MIX_NAME,
- .probe = rsnd_mix_probe_,
- .init = rsnd_mix_init,
- .quit = rsnd_mix_quit,
-};
-
-struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
-{
- if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
- id = 0;
-
- return rsnd_mod_get(rsnd_mix_get(priv, id));
-}
-
-int rsnd_mix_probe(struct rsnd_priv *priv)
-{
- struct device_node *node;
- struct device_node *np;
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_mix *mix;
- struct clk *clk;
- char name[MIX_NAME_SIZE];
- int i, nr, ret;
-
- /* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv))
- return 0;
-
- node = rsnd_mix_of_node(priv);
- if (!node)
- return 0; /* not used is not error */
-
- nr = of_get_child_count(node);
- if (!nr) {
- ret = -EINVAL;
- goto rsnd_mix_probe_done;
- }
-
- mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
- if (!mix) {
- ret = -ENOMEM;
- goto rsnd_mix_probe_done;
- }
-
- priv->mix_nr = nr;
- priv->mix = mix;
-
- i = 0;
- ret = 0;
- for_each_child_of_node(node, np) {
- mix = rsnd_mix_get(priv, i);
-
- snprintf(name, MIX_NAME_SIZE, "%s.%d",
- MIX_NAME, i);
-
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto rsnd_mix_probe_done;
- }
-
- ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
- clk, RSND_MOD_MIX, i);
- if (ret)
- goto rsnd_mix_probe_done;
-
- i++;
- }
-
-rsnd_mix_probe_done:
- of_node_put(node);
-
- return ret;
-}
-
-void rsnd_mix_remove(struct rsnd_priv *priv)
-{
- struct rsnd_mix *mix;
- int i;
-
- for_each_rsnd_mix(mix, priv, i) {
- rsnd_mod_quit(rsnd_mod_get(mix));
- }
-}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
deleted file mode 100644
index 317dd793149a..000000000000
--- a/sound/soc/sh/rcar/rsnd.h
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Renesas R-Car
- *
- * 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.
- */
-#ifndef RSND_H
-#define RSND_H
-
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/io.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-#include <linux/sh_dma.h>
-#include <linux/workqueue.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#define RSND_GEN1_SRU 0
-#define RSND_GEN1_ADG 1
-#define RSND_GEN1_SSI 2
-
-#define RSND_GEN2_SCU 0
-#define RSND_GEN2_ADG 1
-#define RSND_GEN2_SSIU 2
-#define RSND_GEN2_SSI 3
-
-#define RSND_BASE_MAX 4
-
-/*
- * pseudo register
- *
- * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different.
- * This driver uses pseudo register in order to hide it.
- * see gen1/gen2 for detail
- */
-enum rsnd_reg {
- /* SCU (SRC/SSIU/MIX/CTU/DVC) */
- RSND_REG_SSI_MODE, /* Gen2 only */
- RSND_REG_SSI_MODE0,
- RSND_REG_SSI_MODE1,
- RSND_REG_SSI_MODE2,
- RSND_REG_SSI_CONTROL,
- RSND_REG_SSI_CTRL, /* Gen2 only */
- RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
- RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
- RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */
- RSND_REG_SSI_INT_ENABLE, /* Gen2 only */
- RSND_REG_SRC_I_BUSIF_MODE,
- RSND_REG_SRC_O_BUSIF_MODE,
- RSND_REG_SRC_ROUTE_MODE0,
- RSND_REG_SRC_SWRSR,
- RSND_REG_SRC_SRCIR,
- RSND_REG_SRC_ADINR,
- RSND_REG_SRC_IFSCR,
- RSND_REG_SRC_IFSVR,
- RSND_REG_SRC_SRCCR,
- RSND_REG_SRC_CTRL, /* Gen2 only */
- RSND_REG_SRC_BSDSR, /* Gen2 only */
- RSND_REG_SRC_BSISR, /* Gen2 only */
- RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */
- RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */
- RSND_REG_SCU_SYS_STATUS0,
- RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */
- RSND_REG_SCU_SYS_INT_EN0,
- RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */
- RSND_REG_CMD_CTRL, /* Gen2 only */
- RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
- RSND_REG_CMD_ROUTE_SLCT,
- RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
- RSND_REG_CTU_CTUIR,
- RSND_REG_CTU_ADINR,
- RSND_REG_MIX_SWRSR,
- RSND_REG_MIX_MIXIR,
- RSND_REG_MIX_ADINR,
- RSND_REG_MIX_MIXMR,
- RSND_REG_MIX_MVPDR,
- RSND_REG_MIX_MDBAR,
- RSND_REG_MIX_MDBBR,
- RSND_REG_MIX_MDBCR,
- RSND_REG_MIX_MDBDR,
- RSND_REG_MIX_MDBER,
- RSND_REG_DVC_SWRSR,
- RSND_REG_DVC_DVUIR,
- RSND_REG_DVC_ADINR,
- RSND_REG_DVC_DVUCR,
- RSND_REG_DVC_ZCMCR,
- RSND_REG_DVC_VOL0R,
- RSND_REG_DVC_VOL1R,
- RSND_REG_DVC_VOL2R,
- RSND_REG_DVC_VOL3R,
- RSND_REG_DVC_VOL4R,
- RSND_REG_DVC_VOL5R,
- RSND_REG_DVC_VOL6R,
- RSND_REG_DVC_VOL7R,
- RSND_REG_DVC_DVUER,
- RSND_REG_DVC_VRCTR, /* Gen2 only */
- RSND_REG_DVC_VRPDR, /* Gen2 only */
- RSND_REG_DVC_VRDBR, /* Gen2 only */
-
- /* ADG */
- RSND_REG_BRRA,
- RSND_REG_BRRB,
- RSND_REG_SSICKR,
- RSND_REG_DIV_EN, /* Gen2 only */
- RSND_REG_AUDIO_CLK_SEL0,
- RSND_REG_AUDIO_CLK_SEL1,
- RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */
-
- /* SSI */
- RSND_REG_SSICR,
- RSND_REG_SSISR,
- RSND_REG_SSITDR,
- RSND_REG_SSIRDR,
- RSND_REG_SSIWSR,
-
- RSND_REG_MAX,
-};
-
-struct rsnd_priv;
-struct rsnd_mod;
-struct rsnd_dai;
-struct rsnd_dai_stream;
-
-/*
- * R-Car basic functions
- */
-#define rsnd_mod_read(m, r) \
- rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
-#define rsnd_mod_write(m, r, d) \
- rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
-#define rsnd_mod_force_write(m, r, d) \
- rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
-#define rsnd_mod_bset(m, r, s, d) \
- rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
-
-u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);
-void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 data);
-void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 data);
-void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
- u32 mask, u32 data);
-u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
-u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
-
-/*
- * R-Car DMA
- */
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, int id);
-int rsnd_dma_probe(struct rsnd_priv *priv);
-struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
- struct rsnd_mod *mod, char *name);
-
-/*
- * R-Car sound mod
- */
-enum rsnd_mod_type {
- RSND_MOD_AUDMAPP,
- RSND_MOD_AUDMA,
- RSND_MOD_DVC,
- RSND_MOD_MIX,
- RSND_MOD_CTU,
- RSND_MOD_CMD,
- RSND_MOD_SRC,
- RSND_MOD_SSIM3, /* SSI multi 3 */
- RSND_MOD_SSIM2, /* SSI multi 2 */
- RSND_MOD_SSIM1, /* SSI multi 1 */
- RSND_MOD_SSIP, /* SSI parent */
- RSND_MOD_SSI,
- RSND_MOD_SSIU,
- RSND_MOD_MAX,
-};
-
-struct rsnd_mod_ops {
- char *name;
- struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod);
- int (*probe)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
- int (*remove)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
- int (*init)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
- int (*quit)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
- int (*start)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
- int (*stop)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
- int (*pcm_new)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd);
- int (*hw_params)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params);
- int (*fallback)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv);
-};
-
-struct rsnd_dai_stream;
-struct rsnd_mod {
- int id;
- enum rsnd_mod_type type;
- struct rsnd_mod_ops *ops;
- struct rsnd_priv *priv;
- struct clk *clk;
-};
-/*
- * status
- *
- * 0xH0000CBA
- *
- * A 0: probe 1: remove
- * B 0: init 1: quit
- * C 0: start 1: stop
- *
- * H is always called (see __rsnd_mod_call)
- * H 0: pcm_new
- * H 0: fallback
- * H 0: hw_params
- */
-#define __rsnd_mod_shift_probe 0
-#define __rsnd_mod_shift_remove 0
-#define __rsnd_mod_shift_init 4
-#define __rsnd_mod_shift_quit 4
-#define __rsnd_mod_shift_start 8
-#define __rsnd_mod_shift_stop 8
-#define __rsnd_mod_shift_pcm_new 28 /* always called */
-#define __rsnd_mod_shift_fallback 28 /* always called */
-#define __rsnd_mod_shift_hw_params 28 /* always called */
-
-#define __rsnd_mod_add_probe 1
-#define __rsnd_mod_add_remove -1
-#define __rsnd_mod_add_init 1
-#define __rsnd_mod_add_quit -1
-#define __rsnd_mod_add_start 1
-#define __rsnd_mod_add_stop -1
-#define __rsnd_mod_add_pcm_new 0
-#define __rsnd_mod_add_fallback 0
-#define __rsnd_mod_add_hw_params 0
-
-#define __rsnd_mod_call_probe 0
-#define __rsnd_mod_call_remove 1
-#define __rsnd_mod_call_init 0
-#define __rsnd_mod_call_quit 1
-#define __rsnd_mod_call_start 0
-#define __rsnd_mod_call_stop 1
-#define __rsnd_mod_call_pcm_new 0
-#define __rsnd_mod_call_fallback 0
-#define __rsnd_mod_call_hw_params 0
-
-#define rsnd_mod_to_priv(mod) ((mod)->priv)
-#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
-#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
-#define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
-#define rsnd_mod_get(ip) (&(ip)->mod)
-
-int rsnd_mod_init(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- struct rsnd_mod_ops *ops,
- struct clk *clk,
- enum rsnd_mod_type type,
- int id);
-void rsnd_mod_quit(struct rsnd_mod *mod);
-char *rsnd_mod_name(struct rsnd_mod *mod);
-struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod);
-void rsnd_mod_interrupt(struct rsnd_mod *mod,
- void (*callback)(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io));
-void rsnd_parse_connect_common(struct rsnd_dai *rdai,
- struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
- struct device_node *node,
- struct device_node *playback,
- struct device_node *capture);
-
-void rsnd_set_slot(struct rsnd_dai *rdai,
- int slots, int slots_total);
-int rsnd_get_slot(struct rsnd_dai_stream *io);
-int rsnd_get_slot_width(struct rsnd_dai_stream *io);
-int rsnd_get_slot_num(struct rsnd_dai_stream *io);
-
-/*
- * R-Car sound DAI
- */
-#define RSND_DAI_NAME_SIZE 16
-struct rsnd_dai_stream {
- char name[RSND_DAI_NAME_SIZE];
- struct snd_pcm_substream *substream;
- struct rsnd_mod *mod[RSND_MOD_MAX];
- struct rsnd_dai_path_info *info; /* rcar_snd.h */
- struct rsnd_dai *rdai;
- u32 mod_status[RSND_MOD_MAX];
- int byte_pos;
- int period_pos;
- int byte_per_period;
- int next_period_byte;
-};
-#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
-#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
-#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
-#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
-#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
-#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
-#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
-#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD)
-#define rsnd_io_to_rdai(io) ((io)->rdai)
-#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
-#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
-#define rsnd_io_to_runtime(io) ((io)->substream ? \
- (io)->substream->runtime : NULL)
-int rsnd_io_is_working(struct rsnd_dai_stream *io);
-
-struct rsnd_dai {
- char name[RSND_DAI_NAME_SIZE];
- struct rsnd_dai_stream playback;
- struct rsnd_dai_stream capture;
- struct rsnd_priv *priv;
-
- int slots;
- int slots_num;
-
- unsigned int clk_master:1;
- unsigned int bit_clk_inv:1;
- unsigned int frm_clk_inv:1;
- unsigned int sys_delay:1;
- unsigned int data_alignment:1;
-};
-
-#define rsnd_rdai_nr(priv) ((priv)->rdai_nr)
-#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
-#define rsnd_rdai_to_priv(rdai) ((rdai)->priv)
-#define for_each_rsnd_dai(rdai, priv, i) \
- for (i = 0; \
- (i < rsnd_rdai_nr(priv)) && \
- ((rdai) = rsnd_rdai_get(priv, i)); \
- i++)
-
-struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
-
-bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
-void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
-int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
-int rsnd_dai_connect(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- enum rsnd_mod_type type);
-#define rsnd_dai_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai")
-
-/*
- * R-Car Gen1/Gen2
- */
-int rsnd_gen_probe(struct rsnd_priv *priv);
-void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- enum rsnd_reg reg);
-phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
-
-/*
- * R-Car ADG
- */
-int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
-int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
-int rsnd_adg_probe(struct rsnd_priv *priv);
-void rsnd_adg_remove(struct rsnd_priv *priv);
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- unsigned int src_rate,
- unsigned int dst_rate);
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io);
-int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io);
-
-/*
- * R-Car sound priv
- */
-struct rsnd_priv {
-
- struct platform_device *pdev;
- spinlock_t lock;
- unsigned long flags;
-#define RSND_GEN_MASK (0xF << 0)
-#define RSND_GEN1 (1 << 0)
-#define RSND_GEN2 (2 << 0)
-
- /*
- * below value will be filled on rsnd_gen_probe()
- */
- void *gen;
-
- /*
- * below value will be filled on rsnd_adg_probe()
- */
- void *adg;
-
- /*
- * below value will be filled on rsnd_dma_probe()
- */
- void *dma;
-
- /*
- * below value will be filled on rsnd_ssi_probe()
- */
- void *ssi;
- int ssi_nr;
-
- /*
- * below value will be filled on rsnd_ssiu_probe()
- */
- void *ssiu;
- int ssiu_nr;
-
- /*
- * below value will be filled on rsnd_src_probe()
- */
- void *src;
- int src_nr;
-
- /*
- * below value will be filled on rsnd_ctu_probe()
- */
- void *ctu;
- int ctu_nr;
-
- /*
- * below value will be filled on rsnd_mix_probe()
- */
- void *mix;
- int mix_nr;
-
- /*
- * below value will be filled on rsnd_dvc_probe()
- */
- void *dvc;
- int dvc_nr;
-
- /*
- * below value will be filled on rsnd_cmd_probe()
- */
- void *cmd;
- int cmd_nr;
-
- /*
- * below value will be filled on rsnd_dai_probe()
- */
- struct snd_soc_dai_driver *daidrv;
- struct rsnd_dai *rdai;
- int rdai_nr;
-};
-
-#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
-#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
-
-#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
-#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
-
-/*
- * rsnd_kctrl
- */
-struct rsnd_kctrl_cfg {
- unsigned int max;
- unsigned int size;
- u32 *val;
- const char * const *texts;
- void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
- struct rsnd_dai_stream *io;
- struct snd_card *card;
- struct snd_kcontrol *kctrl;
-};
-
-#define RSND_DVC_CHANNELS 8
-struct rsnd_kctrl_cfg_m {
- struct rsnd_kctrl_cfg cfg;
- u32 val[RSND_DVC_CHANNELS];
-};
-
-struct rsnd_kctrl_cfg_s {
- struct rsnd_kctrl_cfg cfg;
- u32 val;
-};
-
-void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg);
-#define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg))
-
-int rsnd_kctrl_new_m(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_m *_cfg,
- int ch_size,
- u32 max);
-int rsnd_kctrl_new_s(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_s *_cfg,
- u32 max);
-int rsnd_kctrl_new_e(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- struct rsnd_kctrl_cfg_s *_cfg,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- const char * const *texts,
- u32 max);
-
-/*
- * R-Car SSI
- */
-int rsnd_ssi_probe(struct rsnd_priv *priv);
-void rsnd_ssi_remove(struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
-int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
-
-#define rsnd_ssi_is_pin_sharing(io) \
- __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
-int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
-
-#define rsnd_ssi_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
-void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
- struct device_node *playback,
- struct device_node *capture);
-
-/*
- * R-Car SSIU
- */
-int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod);
-int rsnd_ssiu_probe(struct rsnd_priv *priv);
-void rsnd_ssiu_remove(struct rsnd_priv *priv);
-
-/*
- * R-Car SRC
- */
-int rsnd_src_probe(struct rsnd_priv *priv);
-void rsnd_src_remove(struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io,
- struct snd_pcm_runtime *runtime);
-#define rsnd_src_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
-#define rsnd_parse_connect_src(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
- rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
- playback, capture)
-
-/*
- * R-Car CTU
- */
-int rsnd_ctu_probe(struct rsnd_priv *priv);
-void rsnd_ctu_remove(struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_ctu_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
-#define rsnd_parse_connect_ctu(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \
- rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
- playback, capture)
-
-/*
- * R-Car MIX
- */
-int rsnd_mix_probe(struct rsnd_priv *priv);
-void rsnd_mix_remove(struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_mix_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix")
-#define rsnd_parse_connect_mix(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \
- rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
- playback, capture)
-
-/*
- * R-Car DVC
- */
-int rsnd_dvc_probe(struct rsnd_priv *priv);
-void rsnd_dvc_remove(struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_dvc_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
-#define rsnd_parse_connect_dvc(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \
- rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
- playback, capture)
-
-/*
- * R-Car CMD
- */
-int rsnd_cmd_probe(struct rsnd_priv *priv);
-void rsnd_cmd_remove(struct rsnd_priv *priv);
-int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
-struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
-
-#ifdef DEBUG
-void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
-#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
-#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
-#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
-#else
-#define rsnd_mod_confirm_ssi(mssi)
-#define rsnd_mod_confirm_src(msrc)
-#define rsnd_mod_confirm_dvc(mdvc)
-#endif
-
-#endif
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
deleted file mode 100644
index 8a357fdf1077..000000000000
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Renesas Sampling Rate Convert Sound Card for DPCM
- *
- * Copyright (C) 2015 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * based on ${LINUX}/sound/soc/generic/simple-card.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/clk.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/string.h>
-#include <sound/jack.h>
-#include <sound/soc.h>
-#include <sound/soc-dai.h>
-
-struct rsrc_card_of_data {
- const char *prefix;
- const struct snd_soc_dapm_route *routes;
- int num_routes;
-};
-
-static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = {
- {"ak4642 Playback", NULL, "DAI0 Playback"},
- {"DAI0 Capture", NULL, "ak4642 Capture"},
-};
-
-static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = {
- .prefix = "ak4642",
- .routes = routes_ssi0_ak4642,
- .num_routes = ARRAY_SIZE(routes_ssi0_ak4642),
-};
-
-static const struct of_device_id rsrc_card_of_match[] = {
- { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 },
- { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 },
- { .compatible = "renesas,rsrc-card", },
- {},
-};
-MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
-
-#define DAI_NAME_NUM 32
-struct rsrc_card_dai {
- unsigned int sysclk;
- unsigned int tx_slot_mask;
- unsigned int rx_slot_mask;
- int slots;
- int slot_width;
- struct clk *clk;
- char dai_name[DAI_NAME_NUM];
-};
-
-#define IDX_CPU 0
-#define IDX_CODEC 1
-struct rsrc_card_priv {
- struct snd_soc_card snd_card;
- struct snd_soc_codec_conf codec_conf;
- struct rsrc_card_dai *dai_props;
- struct snd_soc_dai_link *dai_link;
- int dai_num;
- u32 convert_rate;
-};
-
-#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
-#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
-#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
-
-static int rsrc_card_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct rsrc_card_dai *dai_props =
- rsrc_priv_to_props(priv, rtd->num);
-
- return clk_prepare_enable(dai_props->clk);
-}
-
-static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct rsrc_card_dai *dai_props =
- rsrc_priv_to_props(priv, rtd->num);
-
- clk_disable_unprepare(dai_props->clk);
-}
-
-static struct snd_soc_ops rsrc_card_ops = {
- .startup = rsrc_card_startup,
- .shutdown = rsrc_card_shutdown,
-};
-
-static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_dai *dai;
- struct snd_soc_dai_link *dai_link;
- struct rsrc_card_dai *dai_props;
- int num = rtd->num;
- int ret;
-
- dai_link = rsrc_priv_to_link(priv, num);
- dai_props = rsrc_priv_to_props(priv, num);
- dai = dai_link->dynamic ?
- rtd->cpu_dai :
- rtd->codec_dai;
-
- if (dai_props->sysclk) {
- ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dai->dev, "set_sysclk error\n");
- goto err;
- }
- }
-
- if (dai_props->slots) {
- ret = snd_soc_dai_set_tdm_slot(dai,
- dai_props->tx_slot_mask,
- dai_props->rx_slot_mask,
- dai_props->slots,
- dai_props->slot_width);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dai->dev, "set_tdm_slot error\n");
- goto err;
- }
- }
-
- ret = 0;
-
-err:
- return ret;
-}
-
-static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
-
- if (!priv->convert_rate)
- return 0;
-
- rate->min = rate->max = priv->convert_rate;
-
- return 0;
-}
-
-static int rsrc_card_parse_daifmt(struct device_node *node,
- struct device_node *codec,
- struct rsrc_card_priv *priv,
- struct snd_soc_dai_link *dai_link,
- unsigned int *retfmt)
-{
- struct device_node *bitclkmaster = NULL;
- struct device_node *framemaster = NULL;
- unsigned int daifmt;
-
- daifmt = snd_soc_of_parse_daifmt(node, NULL,
- &bitclkmaster, &framemaster);
- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
- if (!bitclkmaster && !framemaster)
- return -EINVAL;
-
- if (codec == bitclkmaster)
- daifmt |= (codec == framemaster) ?
- SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
- else
- daifmt |= (codec == framemaster) ?
- SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
-
- of_node_put(bitclkmaster);
- of_node_put(framemaster);
-
- *retfmt = daifmt;
-
- return 0;
-}
-
-static int rsrc_card_parse_links(struct device_node *np,
- struct rsrc_card_priv *priv,
- int idx, bool is_fe)
-{
- struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
- struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
- struct of_phandle_args args;
- int ret;
-
- /*
- * Get node via "sound-dai = <&phandle port>"
- * it will be used as xxx_of_node on soc_bind_dai_link()
- */
- ret = of_parse_phandle_with_args(np, "sound-dai",
- "#sound-dai-cells", 0, &args);
- if (ret)
- return ret;
-
- /* Parse TDM slot */
- ret = snd_soc_of_parse_tdm_slot(np,
- &dai_props->tx_slot_mask,
- &dai_props->rx_slot_mask,
- &dai_props->slots,
- &dai_props->slot_width);
- if (ret)
- return ret;
-
- if (is_fe) {
- /* BE is dummy */
- dai_link->codec_of_node = NULL;
- dai_link->codec_dai_name = "snd-soc-dummy-dai";
- dai_link->codec_name = "snd-soc-dummy";
-
- /* FE settings */
- dai_link->dynamic = 1;
- dai_link->dpcm_merged_format = 1;
- dai_link->cpu_of_node = args.np;
- ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
- if (ret < 0)
- return ret;
-
- /* set dai_name */
- snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
- dai_link->cpu_dai_name);
-
- /*
- * In soc_bind_dai_link() will check cpu name after
- * of_node matching if dai_link has cpu_dai_name.
- * but, it will never match if name was created by
- * fmt_single_name() remove cpu_dai_name if cpu_args
- * was 0. See:
- * fmt_single_name()
- * fmt_multiple_name()
- */
- if (!args.args_count)
- dai_link->cpu_dai_name = NULL;
- } else {
- struct device *dev = rsrc_priv_to_dev(priv);
- const struct rsrc_card_of_data *of_data;
-
- of_data = rsrc_dev_to_of_data(dev);
-
- /* FE is dummy */
- dai_link->cpu_of_node = NULL;
- dai_link->cpu_dai_name = "snd-soc-dummy-dai";
- dai_link->cpu_name = "snd-soc-dummy";
-
- /* BE settings */
- dai_link->no_pcm = 1;
- dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
- dai_link->codec_of_node = args.np;
- ret = snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
- if (ret < 0)
- return ret;
-
- /* additional name prefix */
- if (of_data) {
- priv->codec_conf.of_node = dai_link->codec_of_node;
- priv->codec_conf.name_prefix = of_data->prefix;
- } else {
- snd_soc_of_parse_audio_prefix(&priv->snd_card,
- &priv->codec_conf,
- dai_link->codec_of_node,
- "audio-prefix");
- }
-
- /* set dai_name */
- snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s",
- dai_link->codec_dai_name);
- }
-
- /* Simple Card assumes platform == cpu */
- dai_link->platform_of_node = dai_link->cpu_of_node;
- dai_link->dpcm_playback = 1;
- dai_link->dpcm_capture = 1;
- dai_link->name = dai_props->dai_name;
- dai_link->stream_name = dai_props->dai_name;
- dai_link->ops = &rsrc_card_ops;
- dai_link->init = rsrc_card_dai_init;
-
- return 0;
-}
-
-static int rsrc_card_parse_clk(struct device_node *np,
- struct rsrc_card_priv *priv,
- int idx, bool is_fe)
-{
- struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
- struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
- struct clk *clk;
- struct device_node *of_np = is_fe ? dai_link->cpu_of_node :
- dai_link->codec_of_node;
- u32 val;
-
- /*
- * Parse dai->sysclk come from "clocks = <&xxx>"
- * (if system has common clock)
- * or "system-clock-frequency = <xxx>"
- * or device's module clock.
- */
- if (of_property_read_bool(np, "clocks")) {
- clk = of_clk_get(np, 0);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- dai_props->sysclk = clk_get_rate(clk);
- dai_props->clk = clk;
- } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
- dai_props->sysclk = val;
- } else {
- clk = of_clk_get(of_np, 0);
- if (!IS_ERR(clk))
- dai_props->sysclk = clk_get_rate(clk);
- }
-
- return 0;
-}
-
-static int rsrc_card_dai_sub_link_of(struct device_node *node,
- struct device_node *np,
- struct rsrc_card_priv *priv,
- int idx, bool is_fe)
-{
- struct device *dev = rsrc_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
- struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
- int ret;
-
- ret = rsrc_card_parse_links(np, priv, idx, is_fe);
- if (ret < 0)
- return ret;
-
- ret = rsrc_card_parse_clk(np, priv, idx, is_fe);
- if (ret < 0)
- return ret;
-
- dev_dbg(dev, "\t%s / %04x / %d\n",
- dai_props->dai_name,
- dai_link->dai_fmt,
- dai_props->sysclk);
-
- return ret;
-}
-
-static int rsrc_card_dai_link_of(struct device_node *node,
- struct rsrc_card_priv *priv)
-{
- struct snd_soc_dai_link *dai_link;
- struct device_node *np;
- unsigned int daifmt = 0;
- int ret, i;
- bool is_fe;
-
- /* find 1st codec */
- i = 0;
- for_each_child_of_node(node, np) {
- dai_link = rsrc_priv_to_link(priv, i);
-
- if (strcmp(np->name, "codec") == 0) {
- ret = rsrc_card_parse_daifmt(node, np, priv,
- dai_link, &daifmt);
- if (ret < 0)
- return ret;
- break;
- }
- i++;
- }
-
- i = 0;
- for_each_child_of_node(node, np) {
- dai_link = rsrc_priv_to_link(priv, i);
- dai_link->dai_fmt = daifmt;
-
- is_fe = false;
- if (strcmp(np->name, "cpu") == 0)
- is_fe = true;
-
- ret = rsrc_card_dai_sub_link_of(node, np, priv, i, is_fe);
- if (ret < 0)
- return ret;
- i++;
- }
-
- return 0;
-}
-
-static int rsrc_card_parse_of(struct device_node *node,
- struct rsrc_card_priv *priv,
- struct device *dev)
-{
- const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
- struct rsrc_card_dai *props;
- struct snd_soc_dai_link *links;
- int ret;
- int num;
-
- if (!node)
- return -EINVAL;
-
- num = of_get_child_count(node);
- props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL);
- links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL);
- if (!props || !links)
- return -ENOMEM;
-
- priv->dai_props = props;
- priv->dai_link = links;
- priv->dai_num = num;
-
- /* Init snd_soc_card */
- priv->snd_card.owner = THIS_MODULE;
- priv->snd_card.dev = dev;
- priv->snd_card.dai_link = priv->dai_link;
- priv->snd_card.num_links = num;
- priv->snd_card.codec_conf = &priv->codec_conf;
- priv->snd_card.num_configs = 1;
-
- if (of_data) {
- priv->snd_card.of_dapm_routes = of_data->routes;
- priv->snd_card.num_of_dapm_routes = of_data->num_routes;
- } else {
- snd_soc_of_parse_audio_routing(&priv->snd_card,
- "audio-routing");
- }
-
- /* Parse the card name from DT */
- snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
-
- /* sampling rate convert */
- of_property_read_u32(node, "convert-rate", &priv->convert_rate);
-
- dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
- priv->snd_card.name ? priv->snd_card.name : "",
- priv->convert_rate);
-
- ret = rsrc_card_dai_link_of(node, priv);
- if (ret < 0)
- return ret;
-
- if (!priv->snd_card.name)
- priv->snd_card.name = priv->snd_card.dai_link->name;
-
- return 0;
-}
-
-/* Decrease the reference count of the device nodes */
-static int rsrc_card_unref(struct snd_soc_card *card)
-{
- struct snd_soc_dai_link *dai_link;
- int num_links;
-
- for (num_links = 0, dai_link = card->dai_link;
- num_links < card->num_links;
- num_links++, dai_link++) {
- of_node_put(dai_link->cpu_of_node);
- of_node_put(dai_link->codec_of_node);
- }
- return 0;
-}
-
-static int rsrc_card_probe(struct platform_device *pdev)
-{
- struct rsrc_card_priv *priv;
- struct device_node *np = pdev->dev.of_node;
- struct device *dev = &pdev->dev;
- int ret;
-
- /* Allocate the private data */
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- ret = rsrc_card_parse_of(np, priv, dev);
- if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "parse error %d\n", ret);
- goto err;
- }
-
- snd_soc_card_set_drvdata(&priv->snd_card, priv);
-
- ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
- if (ret >= 0)
- return ret;
-err:
- rsrc_card_unref(&priv->snd_card);
-
- return ret;
-}
-
-static int rsrc_card_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- return rsrc_card_unref(card);
-}
-
-static struct platform_driver rsrc_card = {
- .driver = {
- .name = "renesas-src-audio-card",
- .of_match_table = rsrc_card_of_match,
- },
- .probe = rsrc_card_probe,
- .remove = rsrc_card_remove,
-};
-
-module_platform_driver(rsrc_card);
-
-MODULE_ALIAS("platform:renesas-src-audio-card");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
deleted file mode 100644
index 5eda056d9f20..000000000000
--- a/sound/soc/sh/rcar/src.c
+++ /dev/null
@@ -1,648 +0,0 @@
-/*
- * Renesas R-Car SRC support
- *
- * 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 "rsnd.h"
-
-#define SRC_NAME "src"
-
-/* SRCx_STATUS */
-#define OUF_SRCO ((1 << 12) | (1 << 13))
-#define OUF_SRCI ((1 << 9) | (1 << 8))
-
-/* SCU_SYSTEM_STATUS0/1 */
-#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id))
-
-struct rsnd_src {
- struct rsnd_mod mod;
- struct rsnd_mod *dma;
- struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
- struct rsnd_kctrl_cfg_s sync; /* sync convert */
- u32 convert_rate; /* sampling rate convert */
- int err;
- int irq;
-};
-
-#define RSND_SRC_NAME_SIZE 16
-
-#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
-#define rsnd_src_to_dma(src) ((src)->dma)
-#define rsnd_src_nr(priv) ((priv)->src_nr)
-#define rsnd_enable_sync_convert(src) ((src)->sen.val)
-
-#define rsnd_mod_to_src(_mod) \
- container_of((_mod), struct rsnd_src, mod)
-
-#define for_each_rsnd_src(pos, priv, i) \
- for ((i) = 0; \
- ((i) < rsnd_src_nr(priv)) && \
- ((pos) = (struct rsnd_src *)(priv)->src + i); \
- i++)
-
-
-/*
- * image of SRC (Sampling Rate Converter)
- *
- * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+
- * 48kHz <-> | SRC | <------> | SSI | <-----> | codec |
- * 44.1kHz <-> +-----+ +-----+ +-------+
- * ...
- *
- */
-
-/*
- * src.c is caring...
- *
- * Gen1
- *
- * [mem] -> [SRU] -> [SSI]
- * |--------|
- *
- * Gen2
- *
- * [mem] -> [SRC] -> [SSIU] -> [SSI]
- * |-----------------|
- */
-
-static void rsnd_src_activation(struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, SRC_SWRSR, 0);
- rsnd_mod_write(mod, SRC_SWRSR, 1);
-}
-
-static void rsnd_src_halt(struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, SRC_SRCIR, 1);
- rsnd_mod_write(mod, SRC_SWRSR, 0);
-}
-
-static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- int is_play = rsnd_io_is_play(io);
-
- return rsnd_dma_request_channel(rsnd_src_of_node(priv),
- mod,
- is_play ? "rx" : "tx");
-}
-
-static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
- struct rsnd_src *src)
-{
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- u32 convert_rate;
-
- if (!runtime)
- return 0;
-
- if (!rsnd_enable_sync_convert(src))
- return src->convert_rate;
-
- convert_rate = src->sync.val;
-
- if (!convert_rate)
- convert_rate = src->convert_rate;
-
- if (!convert_rate)
- convert_rate = runtime->rate;
-
- return convert_rate;
-}
-
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io,
- struct snd_pcm_runtime *runtime)
-{
- struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
- struct rsnd_src *src;
- unsigned int rate = 0;
-
- if (src_mod) {
- src = rsnd_mod_to_src(src_mod);
-
- /*
- * return convert rate if SRC is used,
- * otherwise, return runtime->rate as usual
- */
- rate = rsnd_src_convert_rate(io, src);
- }
-
- if (!rate)
- rate = runtime->rate;
-
- return rate;
-}
-
-static int rsnd_src_hw_params(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *fe_params)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct snd_soc_pcm_runtime *fe = substream->private_data;
-
- /*
- * SRC assumes that it is used under DPCM if user want to use
- * sampling rate convert. Then, SRC should be FE.
- * And then, this function will be called *after* BE settings.
- * this means, each BE already has fixuped hw_params.
- * see
- * dpcm_fe_dai_hw_params()
- * dpcm_be_dai_hw_params()
- */
- if (fe->dai_link->dynamic) {
- int stream = substream->stream;
- struct snd_soc_dpcm *dpcm;
- struct snd_pcm_hw_params *be_params;
-
- list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
- be_params = &dpcm->hw_params;
-
- if (params_rate(fe_params) != params_rate(be_params))
- src->convert_rate = params_rate(be_params);
- }
- }
-
- return 0;
-}
-
-static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(io, src);
- u32 ifscr, fsrate, adinr;
- u32 cr, route;
- u32 bsdsr, bsisr;
- uint ratio;
-
- if (!runtime)
- return;
-
- /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
- if (!convert_rate)
- ratio = 0;
- else if (convert_rate > runtime->rate)
- ratio = 100 * convert_rate / runtime->rate;
- else
- ratio = 100 * runtime->rate / convert_rate;
-
- if (ratio > 600) {
- dev_err(dev, "FSO/FSI ratio error\n");
- return;
- }
-
- /*
- * SRC_ADINR
- */
- adinr = rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io);
-
- /*
- * SRC_IFSCR / SRC_IFSVR
- */
- ifscr = 0;
- fsrate = 0;
- if (convert_rate) {
- ifscr = 1;
- fsrate = 0x0400000 / convert_rate * runtime->rate;
- }
-
- /*
- * SRC_SRCCR / SRC_ROUTE_MODE0
- */
- cr = 0x00011110;
- route = 0x0;
- if (convert_rate) {
- route = 0x1;
-
- if (rsnd_enable_sync_convert(src)) {
- cr |= 0x1;
- route |= rsnd_io_is_play(io) ?
- (0x1 << 24) : (0x1 << 25);
- }
- }
-
- /*
- * SRC_BSDSR / SRC_BSISR
- */
- switch (rsnd_mod_id(mod)) {
- case 5:
- case 6:
- case 7:
- case 8:
- bsdsr = 0x02400000; /* 6 - 1/6 */
- bsisr = 0x00100060; /* 6 - 1/6 */
- break;
- default:
- bsdsr = 0x01800000; /* 6 - 1/6 */
- bsisr = 0x00100060 ;/* 6 - 1/6 */
- break;
- }
-
- rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
- rsnd_mod_write(mod, SRC_ADINR, adinr);
- rsnd_mod_write(mod, SRC_IFSCR, ifscr);
- rsnd_mod_write(mod, SRC_IFSVR, fsrate);
- rsnd_mod_write(mod, SRC_SRCCR, cr);
- rsnd_mod_write(mod, SRC_BSDSR, bsdsr);
- rsnd_mod_write(mod, SRC_BSISR, bsisr);
- rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
-
- rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
- rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
- rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
- rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
-
- if (convert_rate)
- rsnd_adg_set_convert_clk_gen2(mod, io,
- runtime->rate,
- convert_rate);
- else
- rsnd_adg_set_convert_timing_gen2(mod, io);
-}
-
-#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1)
-#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
-static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 sys_int_val, int_val, sys_int_mask;
- int irq = src->irq;
- int id = rsnd_mod_id(mod);
-
- sys_int_val =
- sys_int_mask = OUF_SRC(id);
- int_val = 0x3300;
-
- /*
- * IRQ is not supported on non-DT
- * see
- * rsnd_src_probe_()
- */
- if ((irq <= 0) || !enable) {
- sys_int_val = 0;
- int_val = 0;
- }
-
- /*
- * WORKAROUND
- *
- * ignore over flow error when rsnd_enable_sync_convert()
- */
- if (rsnd_enable_sync_convert(src))
- sys_int_val = sys_int_val & 0xffff;
-
- rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
- rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
- rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
-}
-
-static void rsnd_src_status_clear(struct rsnd_mod *mod)
-{
- u32 val = OUF_SRC(rsnd_mod_id(mod));
-
- rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val);
- rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
-}
-
-static bool rsnd_src_record_error(struct rsnd_mod *mod)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 val0, val1;
- bool ret = false;
-
- val0 = val1 = OUF_SRC(rsnd_mod_id(mod));
-
- /*
- * WORKAROUND
- *
- * ignore over flow error when rsnd_enable_sync_convert()
- */
- if (rsnd_enable_sync_convert(src))
- val0 = val0 & 0xffff;
-
- if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) ||
- (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) {
- struct rsnd_src *src = rsnd_mod_to_src(mod);
-
- src->err++;
- ret = true;
- }
-
- return ret;
-}
-
-static int rsnd_src_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 val;
-
- /*
- * WORKAROUND
- *
- * Enable SRC output if you want to use sync convert together with DVC
- */
- val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ?
- 0x01 : 0x11;
-
- rsnd_mod_write(mod, SRC_CTRL, val);
-
- return 0;
-}
-
-static int rsnd_src_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- /*
- * stop SRC output only
- * see rsnd_src_quit
- */
- rsnd_mod_write(mod, SRC_CTRL, 0x01);
-
- return 0;
-}
-
-static int rsnd_src_init(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
-
- rsnd_mod_power_on(mod);
-
- rsnd_src_activation(mod);
-
- rsnd_src_set_convert_rate(io, mod);
-
- rsnd_src_status_clear(mod);
-
- rsnd_src_irq_enable(mod);
-
- src->err = 0;
-
- /* reset sync convert_rate */
- src->sync.val = 0;
-
- return 0;
-}
-
-static int rsnd_src_quit(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- rsnd_src_irq_disable(mod);
-
- /* stop both out/in */
- rsnd_mod_write(mod, SRC_CTRL, 0);
-
- rsnd_src_halt(mod);
-
- rsnd_mod_power_off(mod);
-
- if (src->err)
- dev_warn(dev, "%s[%d] under/over flow err = %d\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
-
- src->convert_rate = 0;
-
- /* reset sync convert_rate */
- src->sync.val = 0;
-
- return 0;
-}
-
-static void __rsnd_src_interrupt(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- spin_lock(&priv->lock);
-
- /* ignore all cases if not working */
- if (!rsnd_io_is_working(io))
- goto rsnd_src_interrupt_out;
-
- if (rsnd_src_record_error(mod)) {
-
- dev_dbg(dev, "%s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- rsnd_src_stop(mod, io, priv);
- rsnd_src_start(mod, io, priv);
- }
-
- if (src->err > 1024) {
- rsnd_src_irq_disable(mod);
-
- dev_warn(dev, "no more %s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
-
- rsnd_src_status_clear(mod);
-rsnd_src_interrupt_out:
-
- spin_unlock(&priv->lock);
-}
-
-static irqreturn_t rsnd_src_interrupt(int irq, void *data)
-{
- struct rsnd_mod *mod = data;
-
- rsnd_mod_interrupt(mod, __rsnd_src_interrupt);
-
- return IRQ_HANDLED;
-}
-
-static int rsnd_src_probe_(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- int irq = src->irq;
- int ret;
-
- if (irq > 0) {
- /*
- * IRQ is not supported on non-DT
- * see
- * rsnd_src_irq_enable()
- */
- ret = devm_request_irq(dev, irq,
- rsnd_src_interrupt,
- IRQF_SHARED,
- dev_name(dev), mod);
- if (ret)
- return ret;
- }
-
- src->dma = rsnd_dma_attach(io, mod, 0);
- if (IS_ERR(src->dma))
- return PTR_ERR(src->dma);
-
- return ret;
-}
-
-static int rsnd_src_pcm_new(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- int ret;
-
- /*
- * enable SRC sync convert if possible
- */
-
- /*
- * SRC sync convert needs clock master
- */
- if (!rsnd_rdai_is_clk_master(rdai))
- return 0;
-
- /*
- * SRC In doesn't work if DVC was enabled
- */
- if (dvc && !rsnd_io_is_play(io))
- return 0;
-
- /*
- * enable sync convert
- */
- ret = rsnd_kctrl_new_s(mod, io, rtd,
- rsnd_io_is_play(io) ?
- "SRC Out Rate Switch" :
- "SRC In Rate Switch",
- rsnd_src_set_convert_rate,
- &src->sen, 1);
- if (ret < 0)
- return ret;
-
- ret = rsnd_kctrl_new_s(mod, io, rtd,
- rsnd_io_is_play(io) ?
- "SRC Out Rate" :
- "SRC In Rate",
- rsnd_src_set_convert_rate,
- &src->sync, 192000);
-
- return ret;
-}
-
-static struct rsnd_mod_ops rsnd_src_ops = {
- .name = SRC_NAME,
- .dma_req = rsnd_src_dma_req,
- .probe = rsnd_src_probe_,
- .init = rsnd_src_init,
- .quit = rsnd_src_quit,
- .start = rsnd_src_start,
- .stop = rsnd_src_stop,
- .hw_params = rsnd_src_hw_params,
- .pcm_new = rsnd_src_pcm_new,
-};
-
-struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
-{
- if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
- id = 0;
-
- return rsnd_mod_get(rsnd_src_get(priv, id));
-}
-
-int rsnd_src_probe(struct rsnd_priv *priv)
-{
- struct device_node *node;
- struct device_node *np;
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_src *src;
- struct clk *clk;
- char name[RSND_SRC_NAME_SIZE];
- int i, nr, ret;
-
- /* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv))
- return 0;
-
- node = rsnd_src_of_node(priv);
- if (!node)
- return 0; /* not used is not error */
-
- nr = of_get_child_count(node);
- if (!nr) {
- ret = -EINVAL;
- goto rsnd_src_probe_done;
- }
-
- src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL);
- if (!src) {
- ret = -ENOMEM;
- goto rsnd_src_probe_done;
- }
-
- priv->src_nr = nr;
- priv->src = src;
-
- i = 0;
- for_each_child_of_node(node, np) {
- src = rsnd_src_get(priv, i);
-
- snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
- SRC_NAME, i);
-
- src->irq = irq_of_parse_and_map(np, 0);
- if (!src->irq) {
- ret = -EINVAL;
- goto rsnd_src_probe_done;
- }
-
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto rsnd_src_probe_done;
- }
-
- ret = rsnd_mod_init(priv, rsnd_mod_get(src),
- &rsnd_src_ops, clk, RSND_MOD_SRC, i);
- if (ret)
- goto rsnd_src_probe_done;
-
- i++;
- }
-
- ret = 0;
-
-rsnd_src_probe_done:
- of_node_put(node);
-
- return ret;
-}
-
-void rsnd_src_remove(struct rsnd_priv *priv)
-{
- struct rsnd_src *src;
- int i;
-
- for_each_rsnd_src(src, priv, i) {
- rsnd_mod_quit(rsnd_mod_get(src));
- }
-}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
deleted file mode 100644
index 7ee89da4dd5f..000000000000
--- a/sound/soc/sh/rcar/ssi.c
+++ /dev/null
@@ -1,946 +0,0 @@
-/*
- * Renesas R-Car SSIU/SSI support
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * Based on fsi.c
- * Kuninori Morimoto <morimoto.kuninori@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/delay.h>
-#include "rsnd.h"
-#define RSND_SSI_NAME_SIZE 16
-
-/*
- * SSICR
- */
-#define FORCE (1 << 31) /* Fixed */
-#define DMEN (1 << 28) /* DMA Enable */
-#define UIEN (1 << 27) /* Underflow Interrupt Enable */
-#define OIEN (1 << 26) /* Overflow Interrupt Enable */
-#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
-#define DIEN (1 << 24) /* Data Interrupt Enable */
-#define CHNL_4 (1 << 22) /* Channels */
-#define CHNL_6 (2 << 22) /* Channels */
-#define CHNL_8 (3 << 22) /* Channels */
-#define DWL_8 (0 << 19) /* Data Word Length */
-#define DWL_16 (1 << 19) /* Data Word Length */
-#define DWL_18 (2 << 19) /* Data Word Length */
-#define DWL_20 (3 << 19) /* Data Word Length */
-#define DWL_22 (4 << 19) /* Data Word Length */
-#define DWL_24 (5 << 19) /* Data Word Length */
-#define DWL_32 (6 << 19) /* Data Word Length */
-
-#define SWL_32 (3 << 16) /* R/W System Word Length */
-#define SCKD (1 << 15) /* Serial Bit Clock Direction */
-#define SWSD (1 << 14) /* Serial WS Direction */
-#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
-#define SWSP (1 << 12) /* Serial WS Polarity */
-#define SDTA (1 << 10) /* Serial Data Alignment */
-#define PDTA (1 << 9) /* Parallel Data Alignment */
-#define DEL (1 << 8) /* Serial Data Delay */
-#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
-#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
-#define EN (1 << 0) /* SSI Module Enable */
-
-/*
- * SSISR
- */
-#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */
-#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */
-#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
-#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
-
-/*
- * SSIWSR
- */
-#define CONT (1 << 8) /* WS Continue Function */
-#define WS_MODE (1 << 0) /* WS Mode */
-
-#define SSI_NAME "ssi"
-
-struct rsnd_ssi {
- struct rsnd_ssi *parent;
- struct rsnd_mod mod;
- struct rsnd_mod *dma;
-
- u32 flags;
- u32 cr_own;
- u32 cr_clk;
- u32 cr_mode;
- u32 wsr;
- int chan;
- int rate;
- int err;
- int irq;
- unsigned int usrcnt;
-};
-
-/* flags */
-#define RSND_SSI_CLK_PIN_SHARE (1 << 0)
-#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
-
-#define for_each_rsnd_ssi(pos, priv, i) \
- for (i = 0; \
- (i < rsnd_ssi_nr(priv)) && \
- ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \
- i++)
-
-#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
-#define rsnd_ssi_to_dma(mod) ((ssi)->dma)
-#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
-#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
-#define rsnd_ssi_mode_flags(p) ((p)->flags)
-#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
-#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
-
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
-{
- struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int use_busif = 0;
-
- if (!rsnd_ssi_is_dma_mode(mod))
- return 0;
-
- if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
- use_busif = 1;
- if (rsnd_io_to_mod_src(io))
- use_busif = 1;
-
- return use_busif;
-}
-
-static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
-{
- rsnd_mod_write(mod, SSISR, 0);
-}
-
-static u32 rsnd_ssi_status_get(struct rsnd_mod *mod)
-{
- return rsnd_mod_read(mod, SSISR);
-}
-
-static void rsnd_ssi_status_check(struct rsnd_mod *mod,
- u32 bit)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 status;
- int i;
-
- for (i = 0; i < 1024; i++) {
- status = rsnd_ssi_status_get(mod);
- if (status & bit)
- return;
-
- udelay(50);
- }
-
- dev_warn(dev, "status check failed\n");
-}
-
-static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
- if (rsnd_is_gen1(priv))
- return 0;
-
- /* enable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
- rsnd_ssi_is_dma_mode(ssi_mod) ?
- 0x0e000000 : 0x0f000000);
-
- return 0;
-}
-
-static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
- if (rsnd_is_gen1(priv))
- return 0;
-
- /* disable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
-
- return 0;
-}
-
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
-{
- struct rsnd_mod *mod;
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct device *dev = rsnd_priv_to_dev(priv);
- enum rsnd_mod_type types[] = {
- RSND_MOD_SSIM1,
- RSND_MOD_SSIM2,
- RSND_MOD_SSIM3,
- };
- int i, mask;
-
- switch (runtime->channels) {
- case 2: /* Multi channel is not needed for Stereo */
- return 0;
- case 6:
- break;
- default:
- dev_err(dev, "unsupported channel\n");
- return 0;
- }
-
- mask = 0;
- for (i = 0; i < ARRAY_SIZE(types); i++) {
- mod = rsnd_io_to_mod(io, types[i]);
- if (!mod)
- continue;
-
- mask |= 1 << rsnd_mod_id(mod);
- }
-
- return mask;
-}
-
-static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
- struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
- int slots = rsnd_get_slot_width(io);
- int j, ret;
- int ssi_clk_mul_table[] = {
- 1, 2, 4, 8, 16, 6, 12,
- };
- unsigned int main_rate;
- unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
-
- if (!rsnd_rdai_is_clk_master(rdai))
- return 0;
-
- if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
- return 0;
-
- if (rsnd_ssi_is_multi_slave(mod, io))
- return 0;
-
- if (ssi->usrcnt > 1) {
- if (ssi->rate != rate) {
- dev_err(dev, "SSI parent/child should use same rate\n");
- return -EINVAL;
- }
-
- return 0;
- }
-
- /*
- * Find best clock, and try to start ADG
- */
- for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
-
- /*
- * this driver is assuming that
- * system word is 32bit x slots
- * see rsnd_ssi_init()
- */
- main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
-
- ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
- if (0 == ret) {
- ssi->cr_clk = FORCE | SWL_32 |
- SCKD | SWSD | CKDV(j);
- ssi->wsr = CONT;
-
- ssi->rate = rate;
-
- dev_dbg(dev, "%s[%d] outputs %u Hz\n",
- rsnd_mod_name(mod),
- rsnd_mod_id(mod), rate);
-
- return 0;
- }
- }
-
- dev_err(dev, "unsupported clock rate\n");
- return -EIO;
-}
-
-static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
- struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
-
- if (!rsnd_rdai_is_clk_master(rdai))
- return;
-
- if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
- return;
-
- if (ssi->usrcnt > 1)
- return;
-
- ssi->cr_clk = 0;
- ssi->rate = 0;
-
- rsnd_adg_ssi_clk_stop(mod);
-}
-
-static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- u32 cr_own;
- u32 cr_mode;
- u32 wsr;
- int is_tdm;
-
- is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
-
- /*
- * always use 32bit system word.
- * see also rsnd_ssi_master_clk_enable()
- */
- cr_own = FORCE | SWL_32 | PDTA;
-
- if (rdai->bit_clk_inv)
- cr_own |= SCKP;
- if (rdai->frm_clk_inv ^ is_tdm)
- cr_own |= SWSP;
- if (rdai->data_alignment)
- cr_own |= SDTA;
- if (rdai->sys_delay)
- cr_own |= DEL;
- if (rsnd_io_is_play(io))
- cr_own |= TRMD;
-
- switch (runtime->sample_bits) {
- case 16:
- cr_own |= DWL_16;
- break;
- case 32:
- cr_own |= DWL_24;
- break;
- default:
- return -EINVAL;
- }
-
- if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
- cr_mode = UIEN | OIEN | /* over/under run */
- DMEN; /* DMA : enable DMA */
- } else {
- cr_mode = DIEN; /* PIO : enable Data interrupt */
- }
-
- /*
- * TDM Extend Mode
- * see
- * rsnd_ssiu_init_gen2()
- */
- wsr = ssi->wsr;
- if (is_tdm) {
- wsr |= WS_MODE;
- cr_own |= CHNL_8;
- }
-
- ssi->cr_own = cr_own;
- ssi->cr_mode = cr_mode;
- ssi->wsr = wsr;
-
- return 0;
-}
-
-/*
- * SSI mod common functions
- */
-static int rsnd_ssi_init(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int ret;
-
- ssi->usrcnt++;
-
- rsnd_mod_power_on(mod);
-
- ret = rsnd_ssi_master_clk_start(ssi, io);
- if (ret < 0)
- return ret;
-
- if (rsnd_ssi_is_parent(mod, io))
- return 0;
-
- ret = rsnd_ssi_config_init(ssi, io);
- if (ret < 0)
- return ret;
-
- ssi->err = -1; /* ignore 1st error */
-
- /* clear error status */
- rsnd_ssi_status_clear(mod);
-
- rsnd_ssi_irq_enable(mod);
-
- return 0;
-}
-
-static int rsnd_ssi_quit(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- if (!ssi->usrcnt) {
- dev_err(dev, "%s[%d] usrcnt error\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- return -EIO;
- }
-
- if (!rsnd_ssi_is_parent(mod, io)) {
- if (ssi->err > 0)
- dev_warn(dev, "%s[%d] under/over flow err = %d\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- ssi->err);
-
- ssi->cr_own = 0;
- ssi->err = 0;
-
- rsnd_ssi_irq_disable(mod);
- }
-
- rsnd_ssi_master_clk_stop(ssi, io);
-
- rsnd_mod_power_off(mod);
-
- ssi->usrcnt--;
-
- return 0;
-}
-
-static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int chan = params_channels(params);
-
- /*
- * Already working.
- * It will happen if SSI has parent/child connection.
- */
- if (ssi->usrcnt > 1) {
- /*
- * it is error if child <-> parent SSI uses
- * different channels.
- */
- if (ssi->chan != chan)
- return -EIO;
- }
-
- ssi->chan = chan;
-
- return 0;
-}
-
-static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
-{
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
- u32 status = rsnd_ssi_status_get(mod);
-
- /* under/over flow error */
- if (status & (UIRQ | OIRQ))
- ssi->err++;
-
- return status;
-}
-
-static int __rsnd_ssi_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- u32 cr;
-
- cr = ssi->cr_own |
- ssi->cr_clk |
- ssi->cr_mode;
-
- /*
- * EN will be set via SSIU :: SSI_CONTROL
- * if Multi channel mode
- */
- if (!rsnd_ssi_multi_slaves(io))
- cr |= EN;
-
- rsnd_mod_write(mod, SSICR, cr);
- rsnd_mod_write(mod, SSIWSR, ssi->wsr);
-
- return 0;
-}
-
-static int rsnd_ssi_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- /*
- * no limit to start
- * see also
- * rsnd_ssi_stop
- * rsnd_ssi_interrupt
- */
- return __rsnd_ssi_start(mod, io, priv);
-}
-
-static int __rsnd_ssi_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- u32 cr;
-
- /*
- * disable all IRQ,
- * and, wait all data was sent
- */
- cr = ssi->cr_own |
- ssi->cr_clk;
-
- rsnd_mod_write(mod, SSICR, cr | EN);
- rsnd_ssi_status_check(mod, DIRQ);
-
- /*
- * disable SSI,
- * and, wait idle state
- */
- rsnd_mod_write(mod, SSICR, cr); /* disabled all */
- rsnd_ssi_status_check(mod, IIRQ);
-
- return 0;
-}
-
-static int rsnd_ssi_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
- /*
- * don't stop if not last user
- * see also
- * rsnd_ssi_start
- * rsnd_ssi_interrupt
- */
- if (ssi->usrcnt > 1)
- return 0;
-
- return __rsnd_ssi_stop(mod, io, priv);
-}
-
-static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- int is_dma = rsnd_ssi_is_dma_mode(mod);
- u32 status;
- bool elapsed = false;
-
- spin_lock(&priv->lock);
-
- /* ignore all cases if not working */
- if (!rsnd_io_is_working(io))
- goto rsnd_ssi_interrupt_out;
-
- status = rsnd_ssi_record_error(ssi);
-
- /* PIO only */
- if (!is_dma && (status & DIRQ)) {
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- u32 *buf = (u32 *)(runtime->dma_area +
- rsnd_dai_pointer_offset(io, 0));
-
- /*
- * 8/16/32 data can be assesse to TDR/RDR register
- * directly as 32bit data
- * see rsnd_ssi_init()
- */
- if (rsnd_io_is_play(io))
- rsnd_mod_write(mod, SSITDR, *buf);
- else
- *buf = rsnd_mod_read(mod, SSIRDR);
-
- elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
- }
-
- /* DMA only */
- if (is_dma && (status & (UIRQ | OIRQ))) {
- /*
- * restart SSI
- */
- dev_dbg(dev, "%s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- __rsnd_ssi_stop(mod, io, priv);
- __rsnd_ssi_start(mod, io, priv);
- }
-
- if (ssi->err > 1024) {
- rsnd_ssi_irq_disable(mod);
-
- dev_warn(dev, "no more %s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
-
- rsnd_ssi_status_clear(mod);
-rsnd_ssi_interrupt_out:
- spin_unlock(&priv->lock);
-
- if (elapsed)
- rsnd_dai_period_elapsed(io);
-}
-
-static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
-{
- struct rsnd_mod *mod = data;
-
- rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt);
-
- return IRQ_HANDLED;
-}
-
-/*
- * SSI PIO
- */
-static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- if (!__rsnd_ssi_is_pin_sharing(mod))
- return;
-
- switch (rsnd_mod_id(mod)) {
- case 1:
- case 2:
- rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP);
- break;
- case 4:
- rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP);
- break;
- case 8:
- rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP);
- break;
- }
-}
-
-static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int ret;
-
- /*
- * SSIP/SSIU/IRQ are not needed on
- * SSI Multi slaves
- */
- if (rsnd_ssi_is_multi_slave(mod, io))
- return 0;
-
- rsnd_ssi_parent_attach(mod, io, priv);
-
- ret = rsnd_ssiu_attach(io, mod);
- if (ret < 0)
- return ret;
-
- ret = devm_request_irq(dev, ssi->irq,
- rsnd_ssi_interrupt,
- IRQF_SHARED,
- dev_name(dev), mod);
-
- return ret;
-}
-
-static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
- .name = SSI_NAME,
- .probe = rsnd_ssi_common_probe,
- .init = rsnd_ssi_init,
- .quit = rsnd_ssi_quit,
- .start = rsnd_ssi_start,
- .stop = rsnd_ssi_stop,
- .hw_params = rsnd_ssi_hw_params,
-};
-
-static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int dma_id = 0; /* not needed */
- int ret;
-
- /*
- * SSIP/SSIU/IRQ/DMA are not needed on
- * SSI Multi slaves
- */
- if (rsnd_ssi_is_multi_slave(mod, io))
- return 0;
-
- ret = rsnd_ssi_common_probe(mod, io, priv);
- if (ret)
- return ret;
-
- ssi->dma = rsnd_dma_attach(io, mod, dma_id);
- if (IS_ERR(ssi->dma))
- return PTR_ERR(ssi->dma);
-
- return ret;
-}
-
-static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- int irq = ssi->irq;
-
- /* PIO will request IRQ again */
- devm_free_irq(dev, irq, mod);
-
- return 0;
-}
-
-static int rsnd_ssi_fallback(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
-
- /*
- * fallback to PIO
- *
- * SSI .probe might be called again.
- * see
- * rsnd_rdai_continuance_probe()
- */
- mod->ops = &rsnd_ssi_pio_ops;
-
- dev_info(dev, "%s[%d] fallback to PIO mode\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- return 0;
-}
-
-static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- int is_play = rsnd_io_is_play(io);
- char *name;
-
- if (rsnd_ssi_use_busif(io))
- name = is_play ? "rxu" : "txu";
- else
- name = is_play ? "rx" : "tx";
-
- return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
- mod, name);
-}
-
-static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
- .name = SSI_NAME,
- .dma_req = rsnd_ssi_dma_req,
- .probe = rsnd_ssi_dma_probe,
- .remove = rsnd_ssi_dma_remove,
- .init = rsnd_ssi_init,
- .quit = rsnd_ssi_quit,
- .start = rsnd_ssi_start,
- .stop = rsnd_ssi_stop,
- .fallback = rsnd_ssi_fallback,
- .hw_params = rsnd_ssi_hw_params,
-};
-
-int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
-{
- return mod->ops == &rsnd_ssi_dma_ops;
-}
-
-
-/*
- * Non SSI
- */
-static struct rsnd_mod_ops rsnd_ssi_non_ops = {
- .name = SSI_NAME,
-};
-
-/*
- * ssi mod function
- */
-static void rsnd_ssi_connect(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- enum rsnd_mod_type types[] = {
- RSND_MOD_SSI,
- RSND_MOD_SSIM1,
- RSND_MOD_SSIM2,
- RSND_MOD_SSIM3,
- };
- enum rsnd_mod_type type;
- int i;
-
- /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
- for (i = 0; i < ARRAY_SIZE(types); i++) {
- type = types[i];
- if (!rsnd_io_to_mod(io, type)) {
- rsnd_dai_connect(mod, io, type);
- rsnd_set_slot(rdai, 2 * (i + 1), (i + 1));
- return;
- }
- }
-}
-
-void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
- struct device_node *playback,
- struct device_node *capture)
-{
- struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
- struct device_node *node;
- struct device_node *np;
- struct rsnd_mod *mod;
- int i;
-
- node = rsnd_ssi_of_node(priv);
- if (!node)
- return;
-
- i = 0;
- for_each_child_of_node(node, np) {
- mod = rsnd_ssi_mod_get(priv, i);
- if (np == playback)
- rsnd_ssi_connect(mod, &rdai->playback);
- if (np == capture)
- rsnd_ssi_connect(mod, &rdai->capture);
- i++;
- }
-
- of_node_put(node);
-}
-
-struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
-{
- if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
- id = 0;
-
- return rsnd_mod_get(rsnd_ssi_get(priv, id));
-}
-
-int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
- return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
-}
-
-int rsnd_ssi_probe(struct rsnd_priv *priv)
-{
- struct device_node *node;
- struct device_node *np;
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_mod_ops *ops;
- struct clk *clk;
- struct rsnd_ssi *ssi;
- char name[RSND_SSI_NAME_SIZE];
- int i, nr, ret;
-
- node = rsnd_ssi_of_node(priv);
- if (!node)
- return -EINVAL;
-
- nr = of_get_child_count(node);
- if (!nr) {
- ret = -EINVAL;
- goto rsnd_ssi_probe_done;
- }
-
- ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
- if (!ssi) {
- ret = -ENOMEM;
- goto rsnd_ssi_probe_done;
- }
-
- priv->ssi = ssi;
- priv->ssi_nr = nr;
-
- i = 0;
- for_each_child_of_node(node, np) {
- ssi = rsnd_ssi_get(priv, i);
-
- snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
- SSI_NAME, i);
-
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto rsnd_ssi_probe_done;
- }
-
- if (of_get_property(np, "shared-pin", NULL))
- ssi->flags |= RSND_SSI_CLK_PIN_SHARE;
-
- if (of_get_property(np, "no-busif", NULL))
- ssi->flags |= RSND_SSI_NO_BUSIF;
-
- ssi->irq = irq_of_parse_and_map(np, 0);
- if (!ssi->irq) {
- ret = -EINVAL;
- goto rsnd_ssi_probe_done;
- }
-
- ops = &rsnd_ssi_non_ops;
- if (of_get_property(np, "pio-transfer", NULL))
- ops = &rsnd_ssi_pio_ops;
- else
- ops = &rsnd_ssi_dma_ops;
-
- ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
- RSND_MOD_SSI, i);
- if (ret)
- goto rsnd_ssi_probe_done;
-
- i++;
- }
-
- ret = 0;
-
-rsnd_ssi_probe_done:
- of_node_put(node);
-
- return ret;
-}
-
-void rsnd_ssi_remove(struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi;
- int i;
-
- for_each_rsnd_ssi(ssi, priv, i) {
- rsnd_mod_quit(rsnd_mod_get(ssi));
- }
-}
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
deleted file mode 100644
index 06d72828e5bc..000000000000
--- a/sound/soc/sh/rcar/ssiu.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Renesas R-Car SSIU support
- *
- * Copyright (c) 2015 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 "rsnd.h"
-
-#define SSIU_NAME "ssiu"
-
-struct rsnd_ssiu {
- struct rsnd_mod mod;
-};
-
-#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
-#define for_each_rsnd_ssiu(pos, priv, i) \
- for (i = 0; \
- (i < rsnd_ssiu_nr(priv)) && \
- ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \
- i++)
-
-static int rsnd_ssiu_init(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
- int use_busif = rsnd_ssi_use_busif(io);
- int id = rsnd_mod_id(mod);
- u32 mask1, val1;
- u32 mask2, val2;
-
- /*
- * SSI_MODE0
- */
- rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
-
- /*
- * SSI_MODE1
- */
- mask1 = (1 << 4) | (1 << 20); /* mask sync bit */
- mask2 = (1 << 4); /* mask sync bit */
- val1 = val2 = 0;
- if (rsnd_ssi_is_pin_sharing(io)) {
- int shift = -1;
-
- switch (id) {
- case 1:
- shift = 0;
- break;
- case 2:
- shift = 2;
- break;
- case 4:
- shift = 16;
- break;
- default:
- return -EINVAL;
- }
-
- mask1 |= 0x3 << shift;
- val1 = rsnd_rdai_is_clk_master(rdai) ?
- 0x2 << shift : 0x1 << shift;
-
- } else if (multi_ssi_slaves) {
-
- mask2 |= 0x00000007;
- mask1 |= 0x0000000f;
-
- switch (multi_ssi_slaves) {
- case 0x0206: /* SSI0/1/2/9 */
- val2 = (1 << 4) | /* SSI0129 sync */
- (rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1);
- /* fall through */
- case 0x0006: /* SSI0/1/2 */
- val1 = rsnd_rdai_is_clk_master(rdai) ?
- 0xa : 0x5;
-
- if (!val2) /* SSI012 sync */
- val1 |= (1 << 4);
- }
- }
-
- rsnd_mod_bset(mod, SSI_MODE1, mask1, val1);
- rsnd_mod_bset(mod, SSI_MODE2, mask2, val2);
-
- return 0;
-}
-
-static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
- .name = SSIU_NAME,
- .init = rsnd_ssiu_init,
-};
-
-static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- int ret;
-
- ret = rsnd_ssiu_init(mod, io, priv);
- if (ret < 0)
- return ret;
-
- if (rsnd_get_slot_width(io) >= 6) {
- /*
- * TDM Extend Mode
- * see
- * rsnd_ssi_config_init()
- */
- rsnd_mod_write(mod, SSI_MODE, 0x1);
- }
-
- if (rsnd_ssi_use_busif(io)) {
- u32 val = rsnd_get_dalign(mod, io);
-
- rsnd_mod_write(mod, SSI_BUSIF_ADINR,
- rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io));
- rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
- rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
- }
-
- return 0;
-}
-
-static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- if (!rsnd_ssi_use_busif(io))
- return 0;
-
- rsnd_mod_write(mod, SSI_CTRL, 0x1);
-
- if (rsnd_ssi_multi_slaves(io))
- rsnd_mod_write(mod, SSI_CONTROL, 0x1);
-
- return 0;
-}
-
-static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- if (!rsnd_ssi_use_busif(io))
- return 0;
-
- rsnd_mod_write(mod, SSI_CTRL, 0);
-
- if (rsnd_ssi_multi_slaves(io))
- rsnd_mod_write(mod, SSI_CONTROL, 0);
-
- return 0;
-}
-
-static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
- .name = SSIU_NAME,
- .init = rsnd_ssiu_init_gen2,
- .start = rsnd_ssiu_start_gen2,
- .stop = rsnd_ssiu_stop_gen2,
-};
-
-static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
-{
- if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
- id = 0;
-
- return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
-}
-
-int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
-
- rsnd_mod_confirm_ssi(ssi_mod);
-
- return rsnd_dai_connect(mod, io, mod->type);
-}
-
-int rsnd_ssiu_probe(struct rsnd_priv *priv)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_ssiu *ssiu;
- static struct rsnd_mod_ops *ops;
- int i, nr, ret;
-
- /* same number to SSI */
- nr = priv->ssi_nr;
- ssiu = devm_kzalloc(dev, sizeof(*ssiu) * nr, GFP_KERNEL);
- if (!ssiu)
- return -ENOMEM;
-
- priv->ssiu = ssiu;
- priv->ssiu_nr = nr;
-
- if (rsnd_is_gen1(priv))
- ops = &rsnd_ssiu_ops_gen1;
- else
- ops = &rsnd_ssiu_ops_gen2;
-
- for_each_rsnd_ssiu(ssiu, priv, i) {
- ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
- ops, NULL, RSND_MOD_SSIU, i);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-void rsnd_ssiu_remove(struct rsnd_priv *priv)
-{
- struct rsnd_ssiu *ssiu;
- int i;
-
- for_each_rsnd_ssiu(ssiu, priv, i) {
- rsnd_mod_quit(rsnd_mod_get(ssiu));
- }
-}
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
deleted file mode 100644
index 840058dcad09..000000000000
--- a/sound/soc/sirf/Kconfig
+++ /dev/null
@@ -1,20 +0,0 @@
-config SND_SOC_SIRF
- tristate "SoC Audio for the SiRF SoC chips"
- depends on ARCH_SIRF || COMPILE_TEST
- select SND_SOC_GENERIC_DMAENGINE_PCM
-
-config SND_SOC_SIRF_AUDIO
- tristate "SoC Audio support for SiRF internal audio codec"
- depends on SND_SOC_SIRF
- select SND_SOC_SIRF_AUDIO_CODEC
- select SND_SOC_SIRF_AUDIO_PORT
-
-config SND_SOC_SIRF_AUDIO_PORT
- select REGMAP_MMIO
- tristate
-
-config SND_SOC_SIRF_USP
- tristate "SoC Audio (I2S protocol) for SiRF SoC USP interface"
- depends on SND_SOC_SIRF
- select REGMAP_MMIO
- tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
deleted file mode 100644
index dd917f20f12f..000000000000
--- a/sound/soc/sirf/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-snd-soc-sirf-audio-objs := sirf-audio.o
-snd-soc-sirf-audio-port-objs := sirf-audio-port.o
-snd-soc-sirf-usp-objs := sirf-usp.o
-
-obj-$(CONFIG_SND_SOC_SIRF_AUDIO) += snd-soc-sirf-audio.o
-obj-$(CONFIG_SND_SOC_SIRF_AUDIO_PORT) += snd-soc-sirf-audio-port.o
-obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-audio-port.c b/sound/soc/sirf/sirf-audio-port.c
deleted file mode 100644
index 3f2cce03275c..000000000000
--- a/sound/soc/sirf/sirf-audio-port.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * SiRF Audio port driver
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-struct sirf_audio_port {
- struct regmap *regmap;
- struct snd_dmaengine_dai_dma_data playback_dma_data;
- struct snd_dmaengine_dai_dma_data capture_dma_data;
-};
-
-
-static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai)
-{
- struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai);
- snd_soc_dai_init_dma_data(dai, &port->playback_dma_data,
- &port->capture_dma_data);
- return 0;
-}
-
-static struct snd_soc_dai_driver sirf_audio_port_dai = {
- .probe = sirf_audio_port_dai_probe,
- .name = "sirf-audio-port",
- .id = 0,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-};
-
-static const struct snd_soc_component_driver sirf_audio_port_component = {
- .name = "sirf-audio-port",
-};
-
-static int sirf_audio_port_probe(struct platform_device *pdev)
-{
- int ret;
- struct sirf_audio_port *port;
-
- port = devm_kzalloc(&pdev->dev,
- sizeof(struct sirf_audio_port), GFP_KERNEL);
- if (!port)
- return -ENOMEM;
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &sirf_audio_port_component, &sirf_audio_port_dai, 1);
- if (ret)
- return ret;
-
- platform_set_drvdata(pdev, port);
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
-}
-
-static const struct of_device_id sirf_audio_port_of_match[] = {
- { .compatible = "sirf,audio-port", },
- {}
-};
-MODULE_DEVICE_TABLE(of, sirf_audio_port_of_match);
-
-static struct platform_driver sirf_audio_port_driver = {
- .driver = {
- .name = "sirf-audio-port",
- .of_match_table = sirf_audio_port_of_match,
- },
- .probe = sirf_audio_port_probe,
-};
-
-module_platform_driver(sirf_audio_port_driver);
-
-MODULE_DESCRIPTION("SiRF Audio Port driver");
-MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-audio.c b/sound/soc/sirf/sirf-audio.c
deleted file mode 100644
index 94ea152e0362..000000000000
--- a/sound/soc/sirf/sirf-audio.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * SiRF audio card driver
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-struct sirf_audio_card {
- unsigned int gpio_hp_pa;
- unsigned int gpio_spk_pa;
-};
-
-static int sirf_audio_hp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *ctrl, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card);
- int on = !SND_SOC_DAPM_EVENT_OFF(event);
- if (gpio_is_valid(sirf_audio_card->gpio_hp_pa))
- gpio_set_value(sirf_audio_card->gpio_hp_pa, on);
- return 0;
-}
-
-static int sirf_audio_spk_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *ctrl, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card);
- int on = !SND_SOC_DAPM_EVENT_OFF(event);
-
- if (gpio_is_valid(sirf_audio_card->gpio_spk_pa))
- gpio_set_value(sirf_audio_card->gpio_spk_pa, on);
-
- return 0;
-}
-static const struct snd_soc_dapm_widget sirf_audio_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Hp", sirf_audio_hp_event),
- SND_SOC_DAPM_SPK("Ext Spk", sirf_audio_spk_event),
- SND_SOC_DAPM_MIC("Ext Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route intercon[] = {
- {"Hp", NULL, "HPOUTL"},
- {"Hp", NULL, "HPOUTR"},
- {"Ext Spk", NULL, "SPKOUT"},
- {"MICIN1", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Ext Mic"},
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sirf_audio_dai_link[] = {
- {
- .name = "SiRF audio card",
- .stream_name = "SiRF audio HiFi",
- .codec_dai_name = "sirf-audio-codec",
- },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_sirf_audio_card = {
- .name = "SiRF audio card",
- .owner = THIS_MODULE,
- .dai_link = sirf_audio_dai_link,
- .num_links = ARRAY_SIZE(sirf_audio_dai_link),
- .dapm_widgets = sirf_audio_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sirf_audio_dapm_widgets),
- .dapm_routes = intercon,
- .num_dapm_routes = ARRAY_SIZE(intercon),
-};
-
-static int sirf_audio_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_sirf_audio_card;
- struct sirf_audio_card *sirf_audio_card;
- int ret;
-
- sirf_audio_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_audio_card),
- GFP_KERNEL);
- if (sirf_audio_card == NULL)
- return -ENOMEM;
-
- sirf_audio_dai_link[0].cpu_of_node =
- of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0);
- sirf_audio_dai_link[0].platform_of_node =
- of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0);
- sirf_audio_dai_link[0].codec_of_node =
- of_parse_phandle(pdev->dev.of_node, "sirf,audio-codec", 0);
- sirf_audio_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node,
- "spk-pa-gpios", 0);
- sirf_audio_card->gpio_hp_pa = of_get_named_gpio(pdev->dev.of_node,
- "hp-pa-gpios", 0);
- if (gpio_is_valid(sirf_audio_card->gpio_spk_pa)) {
- ret = devm_gpio_request_one(&pdev->dev,
- sirf_audio_card->gpio_spk_pa,
- GPIOF_OUT_INIT_LOW, "SPA_PA_SD");
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to request GPIO_%d for reset: %d\n",
- sirf_audio_card->gpio_spk_pa, ret);
- return ret;
- }
- }
- if (gpio_is_valid(sirf_audio_card->gpio_hp_pa)) {
- ret = devm_gpio_request_one(&pdev->dev,
- sirf_audio_card->gpio_hp_pa,
- GPIOF_OUT_INIT_LOW, "HP_PA_SD");
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to request GPIO_%d for reset: %d\n",
- sirf_audio_card->gpio_hp_pa, ret);
- return ret;
- }
- }
-
- card->dev = &pdev->dev;
- snd_soc_card_set_drvdata(card, sirf_audio_card);
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
-
- return ret;
-}
-
-static const struct of_device_id sirf_audio_of_match[] = {
- {.compatible = "sirf,sirf-audio-card", },
- { },
-};
-MODULE_DEVICE_TABLE(of, sirf_audio_of_match);
-
-static struct platform_driver sirf_audio_driver = {
- .driver = {
- .name = "sirf-audio-card",
- .pm = &snd_soc_pm_ops,
- .of_match_table = sirf_audio_of_match,
- },
- .probe = sirf_audio_probe,
-};
-module_platform_driver(sirf_audio_driver);
-
-MODULE_AUTHOR("RongJun Ying <RongJun.Ying@csr.com>");
-MODULE_DESCRIPTION("ALSA SoC SIRF audio card driver");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c
deleted file mode 100644
index 45fc06c0e0e5..000000000000
--- a/sound/soc/sirf/sirf-usp.c
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * SiRF USP in I2S/DSP mode
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/clk.h>
-#include <linux/pm_runtime.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "sirf-usp.h"
-
-struct sirf_usp {
- struct regmap *regmap;
- struct clk *clk;
- u32 mode1_reg;
- u32 mode2_reg;
- int daifmt_format;
- struct snd_dmaengine_dai_dma_data playback_dma_data;
- struct snd_dmaengine_dai_dma_data capture_dma_data;
-};
-
-static void sirf_usp_tx_enable(struct sirf_usp *usp)
-{
- regmap_update_bits(usp->regmap, USP_TX_FIFO_OP,
- USP_TX_FIFO_RESET, USP_TX_FIFO_RESET);
- regmap_write(usp->regmap, USP_TX_FIFO_OP, 0);
-
- regmap_update_bits(usp->regmap, USP_TX_FIFO_OP,
- USP_TX_FIFO_START, USP_TX_FIFO_START);
-
- regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
- USP_TX_ENA, USP_TX_ENA);
-}
-
-static void sirf_usp_tx_disable(struct sirf_usp *usp)
-{
- regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
- USP_TX_ENA, ~USP_TX_ENA);
- /* FIFO stop */
- regmap_write(usp->regmap, USP_TX_FIFO_OP, 0);
-}
-
-static void sirf_usp_rx_enable(struct sirf_usp *usp)
-{
- regmap_update_bits(usp->regmap, USP_RX_FIFO_OP,
- USP_RX_FIFO_RESET, USP_RX_FIFO_RESET);
- regmap_write(usp->regmap, USP_RX_FIFO_OP, 0);
-
- regmap_update_bits(usp->regmap, USP_RX_FIFO_OP,
- USP_RX_FIFO_START, USP_RX_FIFO_START);
-
- regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
- USP_RX_ENA, USP_RX_ENA);
-}
-
-static void sirf_usp_rx_disable(struct sirf_usp *usp)
-{
- regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
- USP_RX_ENA, ~USP_RX_ENA);
- /* FIFO stop */
- regmap_write(usp->regmap, USP_RX_FIFO_OP, 0);
-}
-
-static int sirf_usp_pcm_dai_probe(struct snd_soc_dai *dai)
-{
- struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
- snd_soc_dai_init_dma_data(dai, &usp->playback_dma_data,
- &usp->capture_dma_data);
- return 0;
-}
-
-static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai,
- unsigned int fmt)
-{
- struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
-
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- default:
- dev_err(dai->dev, "Only CBM and CFM supported\n");
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- case SND_SOC_DAIFMT_DSP_A:
- usp->daifmt_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
- break;
- default:
- dev_err(dai->dev, "Only I2S and DSP_A format supported\n");
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_IB_NF:
- usp->daifmt_format |= (fmt & SND_SOC_DAIFMT_INV_MASK);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void sirf_usp_i2s_init(struct sirf_usp *usp)
-{
- /* Configure RISC mode */
- regmap_update_bits(usp->regmap, USP_RISC_DSP_MODE,
- USP_RISC_DSP_SEL, ~USP_RISC_DSP_SEL);
-
- /*
- * Configure DMA IO Length register
- * Set no limit, USP can receive data continuously until it is diabled
- */
- regmap_write(usp->regmap, USP_TX_DMA_IO_LEN, 0);
- regmap_write(usp->regmap, USP_RX_DMA_IO_LEN, 0);
-
- /* Configure Mode2 register */
- regmap_write(usp->regmap, USP_MODE2, (1 << USP_RXD_DELAY_LEN_OFFSET) |
- (0 << USP_TXD_DELAY_LEN_OFFSET) |
- USP_TFS_CLK_SLAVE_MODE | USP_RFS_CLK_SLAVE_MODE);
-
- /* Configure Mode1 register */
- regmap_write(usp->regmap, USP_MODE1,
- USP_SYNC_MODE | USP_EN | USP_TXD_ACT_EDGE_FALLING |
- USP_RFS_ACT_LEVEL_LOGIC1 | USP_TFS_ACT_LEVEL_LOGIC1 |
- USP_TX_UFLOW_REPEAT_ZERO | USP_CLOCK_MODE_SLAVE);
-
- /* Configure RX DMA IO Control register */
- regmap_write(usp->regmap, USP_RX_DMA_IO_CTRL, 0);
-
- /* Congiure RX FIFO Control register */
- regmap_write(usp->regmap, USP_RX_FIFO_CTRL,
- (USP_RX_FIFO_THRESHOLD << USP_RX_FIFO_THD_OFFSET) |
- (USP_TX_RX_FIFO_WIDTH_DWORD << USP_RX_FIFO_WIDTH_OFFSET));
-
- /* Congiure RX FIFO Level Check register */
- regmap_write(usp->regmap, USP_RX_FIFO_LEVEL_CHK,
- RX_FIFO_SC(0x04) | RX_FIFO_LC(0x0E) | RX_FIFO_HC(0x1B));
-
- /* Configure TX DMA IO Control register*/
- regmap_write(usp->regmap, USP_TX_DMA_IO_CTRL, 0);
-
- /* Configure TX FIFO Control register */
- regmap_write(usp->regmap, USP_TX_FIFO_CTRL,
- (USP_TX_FIFO_THRESHOLD << USP_TX_FIFO_THD_OFFSET) |
- (USP_TX_RX_FIFO_WIDTH_DWORD << USP_TX_FIFO_WIDTH_OFFSET));
- /* Congiure TX FIFO Level Check register */
- regmap_write(usp->regmap, USP_TX_FIFO_LEVEL_CHK,
- TX_FIFO_SC(0x1B) | TX_FIFO_LC(0x0E) | TX_FIFO_HC(0x04));
-}
-
-static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
-{
- struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
- u32 data_len, frame_len, shifter_len;
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- data_len = 16;
- frame_len = 16;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- data_len = 24;
- frame_len = 32;
- break;
- case SNDRV_PCM_FORMAT_S24_3LE:
- data_len = 24;
- frame_len = 24;
- break;
- default:
- dev_err(dai->dev, "Format unsupported\n");
- return -EINVAL;
- }
-
- shifter_len = data_len;
-
- switch (usp->daifmt_format & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
- USP_I2S_SYNC_CHG, USP_I2S_SYNC_CHG);
- break;
- case SND_SOC_DAIFMT_DSP_A:
- regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
- USP_I2S_SYNC_CHG, 0);
- frame_len = data_len * params_channels(params);
- data_len = frame_len;
- break;
- default:
- dev_err(dai->dev, "Only support I2S and DSP_A mode\n");
- return -EINVAL;
- }
-
- switch (usp->daifmt_format & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_IB_NF:
- regmap_update_bits(usp->regmap, USP_MODE1,
- USP_RXD_ACT_EDGE_FALLING | USP_TXD_ACT_EDGE_FALLING,
- USP_RXD_ACT_EDGE_FALLING);
- break;
- default:
- return -EINVAL;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_update_bits(usp->regmap, USP_TX_FRAME_CTRL,
- USP_TXC_DATA_LEN_MASK | USP_TXC_FRAME_LEN_MASK
- | USP_TXC_SHIFTER_LEN_MASK | USP_TXC_SLAVE_CLK_SAMPLE,
- ((data_len - 1) << USP_TXC_DATA_LEN_OFFSET)
- | ((frame_len - 1) << USP_TXC_FRAME_LEN_OFFSET)
- | ((shifter_len - 1) << USP_TXC_SHIFTER_LEN_OFFSET)
- | USP_TXC_SLAVE_CLK_SAMPLE);
- else
- regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
- USP_RXC_DATA_LEN_MASK | USP_RXC_FRAME_LEN_MASK
- | USP_RXC_SHIFTER_LEN_MASK | USP_SINGLE_SYNC_MODE,
- ((data_len - 1) << USP_RXC_DATA_LEN_OFFSET)
- | ((frame_len - 1) << USP_RXC_FRAME_LEN_OFFSET)
- | ((shifter_len - 1) << USP_RXC_SHIFTER_LEN_OFFSET)
- | USP_SINGLE_SYNC_MODE);
-
- return 0;
-}
-
-static int sirf_usp_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sirf_usp_tx_enable(usp);
- else
- sirf_usp_rx_enable(usp);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sirf_usp_tx_disable(usp);
- else
- sirf_usp_rx_disable(usp);
- break;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops sirf_usp_pcm_dai_ops = {
- .trigger = sirf_usp_pcm_trigger,
- .set_fmt = sirf_usp_pcm_set_dai_fmt,
- .hw_params = sirf_usp_pcm_hw_params,
-};
-
-static struct snd_soc_dai_driver sirf_usp_pcm_dai = {
- .probe = sirf_usp_pcm_dai_probe,
- .name = "sirf-usp-pcm",
- .id = 0,
- .playback = {
- .stream_name = "SiRF USP PCM Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE,
- },
- .capture = {
- .stream_name = "SiRF USP PCM Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE,
- },
- .ops = &sirf_usp_pcm_dai_ops,
-};
-
-static int sirf_usp_pcm_runtime_suspend(struct device *dev)
-{
- struct sirf_usp *usp = dev_get_drvdata(dev);
- clk_disable_unprepare(usp->clk);
- return 0;
-}
-
-static int sirf_usp_pcm_runtime_resume(struct device *dev)
-{
- struct sirf_usp *usp = dev_get_drvdata(dev);
- int ret;
- ret = clk_prepare_enable(usp->clk);
- if (ret) {
- dev_err(dev, "clk_enable failed: %d\n", ret);
- return ret;
- }
- sirf_usp_i2s_init(usp);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int sirf_usp_pcm_suspend(struct device *dev)
-{
- struct sirf_usp *usp = dev_get_drvdata(dev);
-
- if (!pm_runtime_status_suspended(dev)) {
- regmap_read(usp->regmap, USP_MODE1, &usp->mode1_reg);
- regmap_read(usp->regmap, USP_MODE2, &usp->mode2_reg);
- sirf_usp_pcm_runtime_suspend(dev);
- }
- return 0;
-}
-
-static int sirf_usp_pcm_resume(struct device *dev)
-{
- struct sirf_usp *usp = dev_get_drvdata(dev);
- int ret;
-
- if (!pm_runtime_status_suspended(dev)) {
- ret = sirf_usp_pcm_runtime_resume(dev);
- if (ret)
- return ret;
- regmap_write(usp->regmap, USP_MODE1, usp->mode1_reg);
- regmap_write(usp->regmap, USP_MODE2, usp->mode2_reg);
- }
- return 0;
-}
-#endif
-
-static const struct snd_soc_component_driver sirf_usp_component = {
- .name = "sirf-usp",
-};
-
-static const struct regmap_config sirf_usp_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = USP_RX_FIFO_DATA,
- .cache_type = REGCACHE_NONE,
-};
-
-static int sirf_usp_pcm_probe(struct platform_device *pdev)
-{
- int ret;
- struct sirf_usp *usp;
- void __iomem *base;
- struct resource *mem_res;
-
- usp = devm_kzalloc(&pdev->dev, sizeof(struct sirf_usp),
- GFP_KERNEL);
- if (!usp)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, usp);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap(&pdev->dev, mem_res->start,
- resource_size(mem_res));
- if (base == NULL)
- return -ENOMEM;
- usp->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &sirf_usp_regmap_config);
- if (IS_ERR(usp->regmap))
- return PTR_ERR(usp->regmap);
-
- usp->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(usp->clk)) {
- dev_err(&pdev->dev, "Get clock failed.\n");
- return PTR_ERR(usp->clk);
- }
-
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- ret = sirf_usp_pcm_runtime_resume(&pdev->dev);
- if (ret)
- return ret;
- }
-
- ret = devm_snd_soc_register_component(&pdev->dev, &sirf_usp_component,
- &sirf_usp_pcm_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
- return ret;
- }
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
-}
-
-static int sirf_usp_pcm_remove(struct platform_device *pdev)
-{
- if (!pm_runtime_enabled(&pdev->dev))
- sirf_usp_pcm_runtime_suspend(&pdev->dev);
- else
- pm_runtime_disable(&pdev->dev);
- return 0;
-}
-
-static const struct of_device_id sirf_usp_pcm_of_match[] = {
- { .compatible = "sirf,prima2-usp-pcm", },
- {}
-};
-MODULE_DEVICE_TABLE(of, sirf_usp_pcm_of_match);
-
-static const struct dev_pm_ops sirf_usp_pcm_pm_ops = {
- SET_RUNTIME_PM_OPS(sirf_usp_pcm_runtime_suspend,
- sirf_usp_pcm_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(sirf_usp_pcm_suspend, sirf_usp_pcm_resume)
-};
-
-static struct platform_driver sirf_usp_pcm_driver = {
- .driver = {
- .name = "sirf-usp-pcm",
- .of_match_table = sirf_usp_pcm_of_match,
- .pm = &sirf_usp_pcm_pm_ops,
- },
- .probe = sirf_usp_pcm_probe,
- .remove = sirf_usp_pcm_remove,
-};
-
-module_platform_driver(sirf_usp_pcm_driver);
-
-MODULE_DESCRIPTION("SiRF SoC USP PCM bus driver");
-MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-usp.h b/sound/soc/sirf/sirf-usp.h
deleted file mode 100644
index bf0201cb15bc..000000000000
--- a/sound/soc/sirf/sirf-usp.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * arch/arm/mach-prima2/include/mach/sirfsoc_usp.h
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#ifndef _SIRF_USP_H
-#define _SIRF_USP_H
-
-/* USP Registers */
-#define USP_MODE1 0x00
-#define USP_MODE2 0x04
-#define USP_TX_FRAME_CTRL 0x08
-#define USP_RX_FRAME_CTRL 0x0C
-#define USP_TX_RX_ENABLE 0x10
-#define USP_INT_ENABLE 0x14
-#define USP_INT_STATUS 0x18
-#define USP_PIN_IO_DATA 0x1C
-#define USP_RISC_DSP_MODE 0x20
-#define USP_AYSNC_PARAM_REG 0x24
-#define USP_IRDA_X_MODE_DIV 0x28
-#define USP_SM_CFG 0x2C
-#define USP_TX_DMA_IO_CTRL 0x100
-#define USP_TX_DMA_IO_LEN 0x104
-#define USP_TX_FIFO_CTRL 0x108
-#define USP_TX_FIFO_LEVEL_CHK 0x10C
-#define USP_TX_FIFO_OP 0x110
-#define USP_TX_FIFO_STATUS 0x114
-#define USP_TX_FIFO_DATA 0x118
-#define USP_RX_DMA_IO_CTRL 0x120
-#define USP_RX_DMA_IO_LEN 0x124
-#define USP_RX_FIFO_CTRL 0x128
-#define USP_RX_FIFO_LEVEL_CHK 0x12C
-#define USP_RX_FIFO_OP 0x130
-#define USP_RX_FIFO_STATUS 0x134
-#define USP_RX_FIFO_DATA 0x138
-
-/* USP MODE register-1 */
-#define USP_SYNC_MODE 0x00000001
-#define USP_CLOCK_MODE_SLAVE 0x00000002
-#define USP_LOOP_BACK_EN 0x00000004
-#define USP_HPSIR_EN 0x00000008
-#define USP_ENDIAN_CTRL_LSBF 0x00000010
-#define USP_EN 0x00000020
-#define USP_RXD_ACT_EDGE_FALLING 0x00000040
-#define USP_TXD_ACT_EDGE_FALLING 0x00000080
-#define USP_RFS_ACT_LEVEL_LOGIC1 0x00000100
-#define USP_TFS_ACT_LEVEL_LOGIC1 0x00000200
-#define USP_SCLK_IDLE_MODE_TOGGLE 0x00000400
-#define USP_SCLK_IDLE_LEVEL_LOGIC1 0x00000800
-#define USP_SCLK_PIN_MODE_IO 0x00001000
-#define USP_RFS_PIN_MODE_IO 0x00002000
-#define USP_TFS_PIN_MODE_IO 0x00004000
-#define USP_RXD_PIN_MODE_IO 0x00008000
-#define USP_TXD_PIN_MODE_IO 0x00010000
-#define USP_SCLK_IO_MODE_INPUT 0x00020000
-#define USP_RFS_IO_MODE_INPUT 0x00040000
-#define USP_TFS_IO_MODE_INPUT 0x00080000
-#define USP_RXD_IO_MODE_INPUT 0x00100000
-#define USP_TXD_IO_MODE_INPUT 0x00200000
-#define USP_IRDA_WIDTH_DIV_MASK 0x3FC00000
-#define USP_IRDA_WIDTH_DIV_OFFSET 0
-#define USP_IRDA_IDLE_LEVEL_HIGH 0x40000000
-#define USP_TX_UFLOW_REPEAT_ZERO 0x80000000
-#define USP_TX_ENDIAN_MODE 0x00000020
-#define USP_RX_ENDIAN_MODE 0x00000020
-
-/* USP Mode Register-2 */
-#define USP_RXD_DELAY_LEN_MASK 0x000000FF
-#define USP_RXD_DELAY_LEN_OFFSET 0
-
-#define USP_TXD_DELAY_LEN_MASK 0x0000FF00
-#define USP_TXD_DELAY_LEN_OFFSET 8
-
-#define USP_ENA_CTRL_MODE 0x00010000
-#define USP_FRAME_CTRL_MODE 0x00020000
-#define USP_TFS_SOURCE_MODE 0x00040000
-#define USP_TFS_MS_MODE 0x00080000
-#define USP_CLK_DIVISOR_MASK 0x7FE00000
-#define USP_CLK_DIVISOR_OFFSET 21
-
-#define USP_TFS_CLK_SLAVE_MODE (1<<20)
-#define USP_RFS_CLK_SLAVE_MODE (1<<19)
-
-#define USP_IRDA_DATA_WIDTH 0x80000000
-
-/* USP Transmit Frame Control Register */
-
-#define USP_TXC_DATA_LEN_MASK 0x000000FF
-#define USP_TXC_DATA_LEN_OFFSET 0
-
-#define USP_TXC_SYNC_LEN_MASK 0x0000FF00
-#define USP_TXC_SYNC_LEN_OFFSET 8
-
-#define USP_TXC_FRAME_LEN_MASK 0x00FF0000
-#define USP_TXC_FRAME_LEN_OFFSET 16
-
-#define USP_TXC_SHIFTER_LEN_MASK 0x1F000000
-#define USP_TXC_SHIFTER_LEN_OFFSET 24
-
-#define USP_TXC_SLAVE_CLK_SAMPLE 0x20000000
-
-#define USP_TXC_CLK_DIVISOR_MASK 0xC0000000
-#define USP_TXC_CLK_DIVISOR_OFFSET 30
-
-/* USP Receive Frame Control Register */
-
-#define USP_RXC_DATA_LEN_MASK 0x000000FF
-#define USP_RXC_DATA_LEN_OFFSET 0
-
-#define USP_RXC_FRAME_LEN_MASK 0x0000FF00
-#define USP_RXC_FRAME_LEN_OFFSET 8
-
-#define USP_RXC_SHIFTER_LEN_MASK 0x001F0000
-#define USP_RXC_SHIFTER_LEN_OFFSET 16
-
-#define USP_START_EDGE_MODE 0x00800000
-#define USP_I2S_SYNC_CHG 0x00200000
-
-#define USP_RXC_CLK_DIVISOR_MASK 0x0F000000
-#define USP_RXC_CLK_DIVISOR_OFFSET 24
-#define USP_SINGLE_SYNC_MODE 0x00400000
-
-/* Tx - RX Enable Register */
-
-#define USP_RX_ENA 0x00000001
-#define USP_TX_ENA 0x00000002
-
-/* USP Interrupt Enable and status Register */
-#define USP_RX_DONE_INT 0x00000001
-#define USP_TX_DONE_INT 0x00000002
-#define USP_RX_OFLOW_INT 0x00000004
-#define USP_TX_UFLOW_INT 0x00000008
-#define USP_RX_IO_DMA_INT 0x00000010
-#define USP_TX_IO_DMA_INT 0x00000020
-#define USP_RXFIFO_FULL_INT 0x00000040
-#define USP_TXFIFO_EMPTY_INT 0x00000080
-#define USP_RXFIFO_THD_INT 0x00000100
-#define USP_TXFIFO_THD_INT 0x00000200
-#define USP_UART_FRM_ERR_INT 0x00000400
-#define USP_RX_TIMEOUT_INT 0x00000800
-#define USP_TX_ALLOUT_INT 0x00001000
-#define USP_RXD_BREAK_INT 0x00008000
-
-/* All possible TX interruots */
-#define USP_TX_INTERRUPT (USP_TX_DONE_INT|USP_TX_UFLOW_INT|\
- USP_TX_IO_DMA_INT|\
- USP_TXFIFO_EMPTY_INT|\
- USP_TXFIFO_THD_INT)
-/* All possible RX interruots */
-#define USP_RX_INTERRUPT (USP_RX_DONE_INT|USP_RX_OFLOW_INT|\
- USP_RX_IO_DMA_INT|\
- USP_RXFIFO_FULL_INT|\
- USP_RXFIFO_THD_INT|\
- USP_RXFIFO_THD_INT|USP_RX_TIMEOUT_INT)
-
-#define USP_INT_ALL 0x1FFF
-
-/* USP Pin I/O Data Register */
-
-#define USP_RFS_PIN_VALUE_MASK 0x00000001
-#define USP_TFS_PIN_VALUE_MASK 0x00000002
-#define USP_RXD_PIN_VALUE_MASK 0x00000004
-#define USP_TXD_PIN_VALUE_MASK 0x00000008
-#define USP_SCLK_PIN_VALUE_MASK 0x00000010
-
-/* USP RISC/DSP Mode Register */
-#define USP_RISC_DSP_SEL 0x00000001
-
-/* USP ASYNC PARAMETER Register*/
-
-#define USP_ASYNC_TIMEOUT_MASK 0x0000FFFF
-#define USP_ASYNC_TIMEOUT_OFFSET 0
-#define USP_ASYNC_TIMEOUT(x) (((x)&USP_ASYNC_TIMEOUT_MASK) \
- <<USP_ASYNC_TIMEOUT_OFFSET)
-
-#define USP_ASYNC_DIV2_MASK 0x003F0000
-#define USP_ASYNC_DIV2_OFFSET 16
-
-/* USP TX DMA I/O MODE Register */
-#define USP_TX_MODE_IO 0x00000001
-
-/* USP TX DMA I/O Length Register */
-#define USP_TX_DATA_LEN_MASK 0xFFFFFFFF
-#define USP_TX_DATA_LEN_OFFSET 0
-
-/* USP TX FIFO Control Register */
-#define USP_TX_FIFO_WIDTH_MASK 0x00000003
-#define USP_TX_FIFO_WIDTH_OFFSET 0
-
-#define USP_TX_FIFO_THD_MASK 0x000001FC
-#define USP_TX_FIFO_THD_OFFSET 2
-
-/* USP TX FIFO Level Check Register */
-#define USP_TX_FIFO_LEVEL_CHECK_MASK 0x1F
-#define USP_TX_FIFO_SC_OFFSET 0
-#define USP_TX_FIFO_LC_OFFSET 10
-#define USP_TX_FIFO_HC_OFFSET 20
-
-#define TX_FIFO_SC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \
- << USP_TX_FIFO_SC_OFFSET)
-#define TX_FIFO_LC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \
- << USP_TX_FIFO_LC_OFFSET)
-#define TX_FIFO_HC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \
- << USP_TX_FIFO_HC_OFFSET)
-
-/* USP TX FIFO Operation Register */
-#define USP_TX_FIFO_RESET 0x00000001
-#define USP_TX_FIFO_START 0x00000002
-
-/* USP TX FIFO Status Register */
-#define USP_TX_FIFO_LEVEL_MASK 0x0000007F
-#define USP_TX_FIFO_LEVEL_OFFSET 0
-
-#define USP_TX_FIFO_FULL 0x00000080
-#define USP_TX_FIFO_EMPTY 0x00000100
-
-/* USP TX FIFO Data Register */
-#define USP_TX_FIFO_DATA_MASK 0xFFFFFFFF
-#define USP_TX_FIFO_DATA_OFFSET 0
-
-/* USP RX DMA I/O MODE Register */
-#define USP_RX_MODE_IO 0x00000001
-#define USP_RX_DMA_FLUSH 0x00000004
-
-/* USP RX DMA I/O Length Register */
-#define USP_RX_DATA_LEN_MASK 0xFFFFFFFF
-#define USP_RX_DATA_LEN_OFFSET 0
-
-/* USP RX FIFO Control Register */
-#define USP_RX_FIFO_WIDTH_MASK 0x00000003
-#define USP_RX_FIFO_WIDTH_OFFSET 0
-
-#define USP_RX_FIFO_THD_MASK 0x000001FC
-#define USP_RX_FIFO_THD_OFFSET 2
-
-/* USP RX FIFO Level Check Register */
-
-#define USP_RX_FIFO_LEVEL_CHECK_MASK 0x1F
-#define USP_RX_FIFO_SC_OFFSET 0
-#define USP_RX_FIFO_LC_OFFSET 10
-#define USP_RX_FIFO_HC_OFFSET 20
-
-#define RX_FIFO_SC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \
- << USP_RX_FIFO_SC_OFFSET)
-#define RX_FIFO_LC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \
- << USP_RX_FIFO_LC_OFFSET)
-#define RX_FIFO_HC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \
- << USP_RX_FIFO_HC_OFFSET)
-
-/* USP RX FIFO Operation Register */
-#define USP_RX_FIFO_RESET 0x00000001
-#define USP_RX_FIFO_START 0x00000002
-
-/* USP RX FIFO Status Register */
-
-#define USP_RX_FIFO_LEVEL_MASK 0x0000007F
-#define USP_RX_FIFO_LEVEL_OFFSET 0
-
-#define USP_RX_FIFO_FULL 0x00000080
-#define USP_RX_FIFO_EMPTY 0x00000100
-
-/* USP RX FIFO Data Register */
-
-#define USP_RX_FIFO_DATA_MASK 0xFFFFFFFF
-#define USP_RX_FIFO_DATA_OFFSET 0
-
-/*
- * When rx thd irq occur, sender just disable tx empty irq,
- * Remaining data in tx fifo wil also be sent out.
- */
-#define USP_FIFO_SIZE 128
-#define USP_TX_FIFO_THRESHOLD (USP_FIFO_SIZE/2)
-#define USP_RX_FIFO_THRESHOLD (USP_FIFO_SIZE/2)
-
-/* FIFO_WIDTH for the USP_TX_FIFO_CTRL and USP_RX_FIFO_CTRL registers */
-#define USP_FIFO_WIDTH_BYTE 0x00
-#define USP_FIFO_WIDTH_WORD 0x01
-#define USP_FIFO_WIDTH_DWORD 0x02
-
-#define USP_ASYNC_DIV2 16
-
-#define USP_PLUGOUT_RETRY_CNT 2
-
-#define USP_TX_RX_FIFO_WIDTH_DWORD 2
-
-#define SIRF_USP_DIV_MCLK 0
-
-#define SIRF_USP_I2S_TFS_SYNC 0
-#define SIRF_USP_I2S_RFS_SYNC 1
-#endif
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index 7e0acd83b0e6..37486d6a438e 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -1,28 +1,22 @@
-/*
- * soc-ac97.c -- ALSA SoC Audio Layer AC97 support
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- * Copyright (C) 2010 Slimlogic Ltd.
- * Copyright (C) 2010 Texas Instruments Inc.
- *
- * Author: Liam Girdwood <lrg@slimlogic.co.uk>
- * with code, comments and ideas from :-
- * Richard Purdie <richard@openedhand.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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// soc-ac97.c -- ALSA SoC Audio Layer AC97 support
+//
+// Copyright 2005 Wolfson Microelectronics PLC.
+// Copyright 2005 Openedhand Ltd.
+// Copyright (C) 2010 Slimlogic Ltd.
+// Copyright (C) 2010 Texas Instruments Inc.
+//
+// Author: Liam Girdwood <lrg@slimlogic.co.uk>
+// with code, comments and ideas from :-
+// Richard Purdie <richard@openedhand.com>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/export.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
-#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/slab.h>
@@ -34,17 +28,9 @@ struct snd_ac97_reset_cfg {
struct pinctrl_state *pstate_reset;
struct pinctrl_state *pstate_warm_reset;
struct pinctrl_state *pstate_run;
- int gpio_sdata;
- int gpio_sync;
- int gpio_reset;
-};
-
-struct snd_ac97_gpio_priv {
-#ifdef CONFIG_GPIOLIB
- struct gpio_chip gpio_chip;
-#endif
- unsigned int gpios_set;
- struct snd_soc_codec *codec;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *sdata_gpio;
+ struct gpio_desc *sync_gpio;
};
static struct snd_ac97_bus soc_ac97_bus = {
@@ -57,15 +43,20 @@ static void soc_ac97_device_release(struct device *dev)
}
#ifdef CONFIG_GPIOLIB
-static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip)
+struct snd_ac97_gpio_priv {
+ struct gpio_chip gpio_chip;
+ unsigned int gpios_set;
+ struct snd_soc_component *component;
+};
+
+static inline struct snd_soc_component *gpio_to_component(struct gpio_chip *chip)
{
- struct snd_ac97_gpio_priv *gpio_priv =
- container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
+ struct snd_ac97_gpio_priv *gpio_priv = gpiochip_get_data(chip);
- return gpio_priv->codec;
+ return gpio_priv->component;
}
-static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
+static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
if (offset >= AC97_NUM_GPIOS)
return -EINVAL;
@@ -74,51 +65,60 @@ static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
}
static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
- unsigned offset)
+ unsigned int offset)
{
- struct snd_soc_codec *codec = gpio_to_codec(chip);
+ struct snd_soc_component *component = gpio_to_component(chip);
- dev_dbg(codec->dev, "set gpio %d to output\n", offset);
- return snd_soc_update_bits(codec, AC97_GPIO_CFG,
+ dev_dbg(component->dev, "set gpio %d to output\n", offset);
+ return snd_soc_component_update_bits(component, AC97_GPIO_CFG,
1 << offset, 1 << offset);
}
-static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
+static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
- struct snd_soc_codec *codec = gpio_to_codec(chip);
+ struct snd_soc_component *component = gpio_to_component(chip);
int ret;
- ret = snd_soc_read(codec, AC97_GPIO_STATUS);
- dev_dbg(codec->dev, "get gpio %d : %d\n", offset,
- ret < 0 ? ret : ret & (1 << offset));
+ ret = snd_soc_component_read(component, AC97_GPIO_STATUS);
+
+ dev_dbg(component->dev, "get gpio %d : %d\n", offset,
+ ret & (1 << offset));
- return ret < 0 ? ret : !!(ret & (1 << offset));
+ return !!(ret & (1 << offset));
}
-static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
- int value)
+static int snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- struct snd_ac97_gpio_priv *gpio_priv =
- container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
- struct snd_soc_codec *codec = gpio_to_codec(chip);
+ struct snd_ac97_gpio_priv *gpio_priv = gpiochip_get_data(chip);
+ struct snd_soc_component *component = gpio_to_component(chip);
gpio_priv->gpios_set &= ~(1 << offset);
gpio_priv->gpios_set |= (!!value) << offset;
- snd_soc_write(codec, AC97_GPIO_STATUS, gpio_priv->gpios_set);
- dev_dbg(codec->dev, "set gpio %d to %d\n", offset, !!value);
+ snd_soc_component_write(component, AC97_GPIO_STATUS,
+ gpio_priv->gpios_set);
+ dev_dbg(component->dev, "set gpio %d to %d\n", offset, !!value);
+
+ return 0;
}
static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct snd_soc_codec *codec = gpio_to_codec(chip);
+ struct snd_soc_component *component = gpio_to_component(chip);
+ int ret;
- dev_dbg(codec->dev, "set gpio %d to output\n", offset);
- snd_soc_ac97_gpio_set(chip, offset, value);
- return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0);
+ dev_dbg(component->dev, "set gpio %d to output\n", offset);
+
+ ret = snd_soc_ac97_gpio_set(chip, offset, value);
+ if (ret)
+ return ret;
+
+ return snd_soc_component_update_bits(component, AC97_GPIO_CFG,
+ 1 << offset, 0);
}
-static struct gpio_chip snd_soc_ac97_gpio_chip = {
+static const struct gpio_chip snd_soc_ac97_gpio_chip = {
.label = "snd_soc_ac97",
.owner = THIS_MODULE,
.request = snd_soc_ac97_gpio_request,
@@ -130,24 +130,24 @@ static struct gpio_chip snd_soc_ac97_gpio_chip = {
};
static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
struct snd_ac97_gpio_priv *gpio_priv;
int ret;
- gpio_priv = devm_kzalloc(codec->dev, sizeof(*gpio_priv), GFP_KERNEL);
+ gpio_priv = devm_kzalloc(component->dev, sizeof(*gpio_priv), GFP_KERNEL);
if (!gpio_priv)
return -ENOMEM;
ac97->gpio_priv = gpio_priv;
- gpio_priv->codec = codec;
+ gpio_priv->component = component;
gpio_priv->gpio_chip = snd_soc_ac97_gpio_chip;
gpio_priv->gpio_chip.ngpio = AC97_NUM_GPIOS;
- gpio_priv->gpio_chip.parent = codec->dev;
+ gpio_priv->gpio_chip.parent = component->dev;
gpio_priv->gpio_chip.base = -1;
- ret = gpiochip_add(&gpio_priv->gpio_chip);
+ ret = gpiochip_add_data(&gpio_priv->gpio_chip, gpio_priv);
if (ret != 0)
- dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+ dev_err(component->dev, "Failed to add GPIOs: %d\n", ret);
return ret;
}
@@ -157,7 +157,7 @@ static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
}
#else
static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
return 0;
}
@@ -168,16 +168,16 @@ static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
#endif
/**
- * snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device
- * @codec: The CODEC for which to create the AC'97 device
+ * snd_soc_alloc_ac97_component() - Allocate new a AC'97 device
+ * @component: The COMPONENT for which to create the AC'97 device
*
* Allocated a new snd_ac97 device and intializes it, but does not yet register
* it. The caller is responsible to either call device_add(&ac97->dev) to
* register the device, or to call put_device(&ac97->dev) to free the device.
*
- * Returns: A snd_ac97 device or a PTR_ERR in case of an error.
+ * Returns: A snd_ac97 device or an ERR_PTR in case of an error.
*/
-struct snd_ac97 *snd_soc_alloc_ac97_codec(struct snd_soc_codec *codec)
+struct snd_ac97 *snd_soc_alloc_ac97_component(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
@@ -189,47 +189,47 @@ struct snd_ac97 *snd_soc_alloc_ac97_codec(struct snd_soc_codec *codec)
ac97->num = 0;
ac97->dev.bus = &ac97_bus_type;
- ac97->dev.parent = codec->component.card->dev;
+ ac97->dev.parent = component->card->dev;
ac97->dev.release = soc_ac97_device_release;
dev_set_name(&ac97->dev, "%d-%d:%s",
- codec->component.card->snd_card->number, 0,
- codec->component.name);
+ component->card->snd_card->number, 0,
+ component->name);
device_initialize(&ac97->dev);
return ac97;
}
-EXPORT_SYMBOL(snd_soc_alloc_ac97_codec);
+EXPORT_SYMBOL(snd_soc_alloc_ac97_component);
/**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
+ * snd_soc_new_ac97_component - initailise AC97 device
+ * @component: audio component
* @id: The expected device ID
* @id_mask: Mask that is applied to the device ID before comparing with @id
*
- * Initialises AC97 codec resources for use by ad-hoc devices only.
+ * Initialises AC97 component resources for use by ad-hoc devices only.
*
* If @id is not 0 this function will reset the device, then read the ID from
* the device and check if it matches the expected ID. If it doesn't match an
* error will be returned and device will not be registered.
*
- * Returns: A PTR_ERR() on failure or a valid snd_ac97 struct on success.
+ * Returns: An ERR_PTR on failure or a valid snd_ac97 struct on success.
*/
-struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
+struct snd_ac97 *snd_soc_new_ac97_component(struct snd_soc_component *component,
unsigned int id, unsigned int id_mask)
{
struct snd_ac97 *ac97;
int ret;
- ac97 = snd_soc_alloc_ac97_codec(codec);
+ ac97 = snd_soc_alloc_ac97_component(component);
if (IS_ERR(ac97))
return ac97;
if (id) {
ret = snd_ac97_reset(ac97, false, id, id_mask);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset AC97 device: %d\n",
+ dev_err(component->dev, "Failed to reset AC97 device: %d\n",
ret);
goto err_put_device;
}
@@ -239,7 +239,7 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
if (ret)
goto err_put_device;
- ret = snd_soc_ac97_init_gpio(ac97, codec);
+ ret = snd_soc_ac97_init_gpio(ac97, component);
if (ret)
goto err_put_device;
@@ -249,22 +249,22 @@ err_put_device:
put_device(&ac97->dev);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_component);
/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
+ * snd_soc_free_ac97_component - free AC97 component device
+ * @ac97: snd_ac97 device to be freed
*
- * Frees AC97 codec device resources.
+ * Frees AC97 component device resources.
*/
-void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
+void snd_soc_free_ac97_component(struct snd_ac97 *ac97)
{
snd_soc_ac97_free_gpio(ac97);
device_del(&ac97->dev);
ac97->bus = NULL;
put_device(&ac97->dev);
}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_component);
static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
@@ -274,11 +274,11 @@ static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 1);
udelay(10);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 0);
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
msleep(2);
@@ -290,13 +290,13 @@ static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sdata_gpio, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.reset_gpio, 0);
udelay(10);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.reset_gpio, 1);
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
msleep(2);
@@ -307,8 +307,6 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev,
{
struct pinctrl *p;
struct pinctrl_state *state;
- int gpio;
- int ret;
p = devm_pinctrl_get(dev);
if (IS_ERR(p)) {
@@ -338,41 +336,20 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev,
}
cfg->pstate_run = state;
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sync gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sync");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sync gpio\n");
- return ret;
- }
- cfg->gpio_sync = gpio;
+ cfg->sync_gpio = devm_gpiod_get_index(dev, "ac97", 0, GPIOD_ASIS);
+ if (IS_ERR(cfg->sync_gpio))
+ return dev_err_probe(dev, PTR_ERR(cfg->sync_gpio), "Can't find ac97-sync gpio\n");
+ gpiod_set_consumer_name(cfg->sync_gpio, "AC97 link sync");
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sdata gpio\n");
- return ret;
- }
- cfg->gpio_sdata = gpio;
+ cfg->sdata_gpio = devm_gpiod_get_index(dev, "ac97", 1, GPIOD_ASIS);
+ if (IS_ERR(cfg->sdata_gpio))
+ return dev_err_probe(dev, PTR_ERR(cfg->sdata_gpio), "Can't find ac97-sdata gpio\n");
+ gpiod_set_consumer_name(cfg->sdata_gpio, "AC97 link sdata");
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-reset gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link reset");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-reset gpio\n");
- return ret;
- }
- cfg->gpio_reset = gpio;
+ cfg->reset_gpio = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_ASIS);
+ if (IS_ERR(cfg->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(cfg->reset_gpio), "Can't find ac97-reset gpio\n");
+ gpiod_set_consumer_name(cfg->reset_gpio, "AC97 link reset");
return 0;
}
@@ -397,6 +374,8 @@ EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
/**
* snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ * @ops: bus ops
+ * @pdev: platform device
*
* This function sets the reset and warm_reset properties of ops and parses
* the device node of pdev to get pinctrl states and gpio numbers to use.
diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c
new file mode 100644
index 000000000000..270f9777942f
--- /dev/null
+++ b/sound/soc/soc-acpi.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// soc-apci.c - support for ACPI enumeration.
+//
+// Copyright (c) 2013-15, Intel Corporation.
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <sound/soc-acpi.h>
+
+static bool snd_soc_acpi_id_present(struct snd_soc_acpi_mach *machine)
+{
+ const struct snd_soc_acpi_codecs *comp_ids = machine->comp_ids;
+ int i;
+
+ if (machine->id[0]) {
+ if (acpi_dev_present(machine->id, NULL, -1))
+ return true;
+ }
+
+ if (comp_ids) {
+ for (i = 0; i < comp_ids->num_codecs; i++) {
+ if (acpi_dev_present(comp_ids->codecs[i], NULL, -1)) {
+ strscpy(machine->id, comp_ids->codecs[i], ACPI_ID_LEN);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+struct snd_soc_acpi_mach *
+snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_acpi_mach *mach_alt;
+
+ for (mach = machines; mach->id[0] || mach->comp_ids; mach++) {
+ if (snd_soc_acpi_id_present(mach)) {
+ if (mach->machine_quirk) {
+ mach_alt = mach->machine_quirk(mach);
+ if (!mach_alt)
+ continue; /* not full match, ignore */
+ mach = mach_alt;
+ }
+
+ return mach;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine);
+
+static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
+ acpi_status status;
+ struct snd_soc_acpi_package_context *pkg_ctx = context;
+
+ pkg_ctx->data_valid = false;
+
+ if (adev && adev->status.present && adev->status.functional) {
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *myobj = NULL;
+
+ status = acpi_evaluate_object_typed(handle, pkg_ctx->name,
+ NULL, &buffer,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ myobj = buffer.pointer;
+ if (!myobj || myobj->package.count != pkg_ctx->length) {
+ kfree(buffer.pointer);
+ return AE_OK;
+ }
+
+ status = acpi_extract_package(myobj,
+ pkg_ctx->format, pkg_ctx->state);
+ if (ACPI_FAILURE(status)) {
+ kfree(buffer.pointer);
+ return AE_OK;
+ }
+
+ kfree(buffer.pointer);
+ pkg_ctx->data_valid = true;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
+ struct snd_soc_acpi_package_context *ctx)
+{
+ acpi_status status;
+
+ status = acpi_get_devices(hid, snd_soc_acpi_find_package, ctx, NULL);
+
+ if (ACPI_FAILURE(status) || !ctx->data_valid)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_find_package_from_hid);
+
+struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+ struct snd_soc_acpi_codecs *codec_list =
+ (struct snd_soc_acpi_codecs *) mach->quirk_data;
+ int i;
+
+ if (mach->quirk_data == NULL)
+ return mach;
+
+ for (i = 0; i < codec_list->num_codecs; i++) {
+ if (!acpi_dev_present(codec_list->codecs[i], NULL, -1))
+ return NULL;
+ }
+
+ return mach;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list);
+
+#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \
+ SDW_MFG_ID_MASK | SDW_PART_ID_MASK))
+
+/* Check if all Slaves defined on the link can be found */
+bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev,
+ const struct snd_soc_acpi_link_adr *link,
+ struct sdw_peripherals *peripherals)
+{
+ unsigned int part_id, link_id, unique_id, mfg_id, version;
+ int i, j, k;
+
+ for (i = 0; i < link->num_adr; i++) {
+ u64 adr = link->adr_d[i].adr;
+ int reported_part_count = 0;
+
+ mfg_id = SDW_MFG_ID(adr);
+ part_id = SDW_PART_ID(adr);
+ link_id = SDW_DISCO_LINK_ID(adr);
+ version = SDW_VERSION(adr);
+
+ for (j = 0; j < peripherals->num_peripherals; j++) {
+ struct sdw_slave *peripheral = peripherals->array[j];
+
+ /* find out how many identical parts were reported on that link */
+ if (peripheral->bus->link_id == link_id &&
+ peripheral->id.part_id == part_id &&
+ peripheral->id.mfg_id == mfg_id &&
+ peripheral->id.sdw_version == version)
+ reported_part_count++;
+ }
+
+ for (j = 0; j < peripherals->num_peripherals; j++) {
+ struct sdw_slave *peripheral = peripherals->array[j];
+ int expected_part_count = 0;
+
+ if (peripheral->bus->link_id != link_id ||
+ peripheral->id.part_id != part_id ||
+ peripheral->id.mfg_id != mfg_id ||
+ peripheral->id.sdw_version != version)
+ continue;
+
+ /* find out how many identical parts are expected */
+ for (k = 0; k < link->num_adr; k++) {
+ u64 adr2 = link->adr_d[k].adr;
+
+ if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr))
+ expected_part_count++;
+ }
+
+ if (reported_part_count == expected_part_count) {
+ /*
+ * we have to check unique id
+ * if there is more than one
+ * Slave on the link
+ */
+ unique_id = SDW_UNIQUE_ID(adr);
+ if (reported_part_count == 1 ||
+ peripheral->id.unique_id == unique_id) {
+ dev_dbg(dev, "found part_id %#x at link %d\n", part_id, link_id);
+ break;
+ }
+ } else {
+ dev_dbg(dev, "part_id %#x reported %d expected %d on link %d, skipping\n",
+ part_id, reported_part_count, expected_part_count, link_id);
+ }
+ }
+ if (j == peripherals->num_peripherals) {
+ dev_dbg(dev, "Slave part_id %#x not found\n", part_id);
+ return false;
+ }
+ }
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_sdw_link_slaves_found);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ALSA SoC ACPI module");
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
deleted file mode 100644
index 07f43356f963..000000000000
--- a/sound/soc/soc-cache.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * soc-cache.c -- ASoC register cache helpers
- *
- * 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <sound/soc.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-
-int snd_soc_cache_init(struct snd_soc_codec *codec)
-{
- const struct snd_soc_codec_driver *codec_drv = codec->driver;
- size_t reg_size;
-
- reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
-
- if (!reg_size)
- return 0;
-
- dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
- codec->component.name);
-
- if (codec_drv->reg_cache_default)
- codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
- reg_size, GFP_KERNEL);
- else
- codec->reg_cache = kzalloc(reg_size, GFP_KERNEL);
- if (!codec->reg_cache)
- return -ENOMEM;
-
- return 0;
-}
-
-/*
- * NOTE: keep in mind that this function might be called
- * multiple times.
- */
-int snd_soc_cache_exit(struct snd_soc_codec *codec)
-{
- dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n",
- codec->component.name);
- kfree(codec->reg_cache);
- codec->reg_cache = NULL;
- return 0;
-}
diff --git a/sound/soc/soc-card-test.c b/sound/soc/soc-card-test.c
new file mode 100644
index 000000000000..47dfd8b1babc
--- /dev/null
+++ b/sound/soc/soc-card-test.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-card.h>
+
+struct soc_card_test_priv {
+ struct device *card_dev;
+ struct snd_soc_card *card;
+};
+
+static const struct snd_kcontrol_new test_card_controls[] = {
+ SOC_SINGLE("Fee", SND_SOC_NOPM, 0, 1, 0),
+ SOC_SINGLE("Fi", SND_SOC_NOPM, 1, 1, 0),
+ SOC_SINGLE("Fo", SND_SOC_NOPM, 2, 1, 0),
+ SOC_SINGLE("Fum", SND_SOC_NOPM, 3, 1, 0),
+ SOC_SINGLE("Left Fee", SND_SOC_NOPM, 4, 1, 0),
+ SOC_SINGLE("Right Fee", SND_SOC_NOPM, 5, 1, 0),
+ SOC_SINGLE("Left Fi", SND_SOC_NOPM, 6, 1, 0),
+ SOC_SINGLE("Right Fi", SND_SOC_NOPM, 7, 1, 0),
+ SOC_SINGLE("Left Fo", SND_SOC_NOPM, 8, 1, 0),
+ SOC_SINGLE("Right Fo", SND_SOC_NOPM, 9, 1, 0),
+ SOC_SINGLE("Left Fum", SND_SOC_NOPM, 10, 1, 0),
+ SOC_SINGLE("Right Fum", SND_SOC_NOPM, 11, 1, 0),
+};
+
+static void test_snd_soc_card_get_kcontrol(struct kunit *test)
+{
+ struct soc_card_test_priv *priv = test->priv;
+ struct snd_soc_card *card = priv->card;
+ struct snd_kcontrol *kc;
+ struct soc_mixer_control *mc;
+ int i, ret;
+
+ ret = snd_soc_add_card_controls(card, test_card_controls, ARRAY_SIZE(test_card_controls));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ /* Look up every control */
+ for (i = 0; i < ARRAY_SIZE(test_card_controls); ++i) {
+ kc = snd_soc_card_get_kcontrol(card, test_card_controls[i].name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, kc, "Failed to find '%s'\n",
+ test_card_controls[i].name);
+ if (!kc)
+ continue;
+
+ /* Test that it is the correct control */
+ mc = (struct soc_mixer_control *)kc->private_value;
+ KUNIT_EXPECT_EQ_MSG(test, mc->shift, i, "For '%s'\n", test_card_controls[i].name);
+ }
+
+ /* Test some names that should not be found */
+ kc = snd_soc_card_get_kcontrol(card, "None");
+ KUNIT_EXPECT_NULL(test, kc);
+
+ kc = snd_soc_card_get_kcontrol(card, "Left None");
+ KUNIT_EXPECT_NULL(test, kc);
+
+ kc = snd_soc_card_get_kcontrol(card, "Left");
+ KUNIT_EXPECT_NULL(test, kc);
+
+ kc = snd_soc_card_get_kcontrol(card, NULL);
+ KUNIT_EXPECT_NULL(test, kc);
+}
+
+static int soc_card_test_case_init(struct kunit *test)
+{
+ struct soc_card_test_priv *priv;
+ int ret;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+
+ priv->card = kunit_kzalloc(test, sizeof(*priv->card), GFP_KERNEL);
+ if (!priv->card)
+ return -ENOMEM;
+
+ priv->card_dev = kunit_device_register(test, "sound-soc-card-test");
+ priv->card_dev = get_device(priv->card_dev);
+ if (!priv->card_dev)
+ return -ENODEV;
+
+ priv->card->name = "soc-card-test";
+ priv->card->dev = priv->card_dev;
+ priv->card->owner = THIS_MODULE;
+
+ ret = snd_soc_register_card(priv->card);
+ if (ret) {
+ put_device(priv->card_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void soc_card_test_case_exit(struct kunit *test)
+{
+ struct soc_card_test_priv *priv = test->priv;
+
+ if (priv->card)
+ snd_soc_unregister_card(priv->card);
+
+ if (priv->card_dev)
+ put_device(priv->card_dev);
+}
+
+static struct kunit_case soc_card_test_cases[] = {
+ KUNIT_CASE(test_snd_soc_card_get_kcontrol),
+ {}
+};
+
+static struct kunit_suite soc_card_test_suite = {
+ .name = "soc-card",
+ .test_cases = soc_card_test_cases,
+ .init = soc_card_test_case_init,
+ .exit = soc_card_test_case_exit,
+};
+
+kunit_test_suites(&soc_card_test_suite);
+
+MODULE_DESCRIPTION("ASoC soc-card KUnit test");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c
new file mode 100644
index 000000000000..235427d69061
--- /dev/null
+++ b/sound/soc/soc-card.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// soc-card.c
+//
+// Copyright (C) 2019 Renesas Electronics Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+
+#include <linux/lockdep.h>
+#include <linux/rwsem.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#define soc_card_ret(dai, ret) _soc_card_ret(dai, __func__, ret)
+static inline int _soc_card_ret(struct snd_soc_card *card,
+ const char *func, int ret)
+{
+ return snd_soc_ret(card->dev, ret,
+ "at %s() on %s\n", func, card->name);
+}
+
+struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
+ const char *name)
+{
+ if (unlikely(!name))
+ return NULL;
+
+ return snd_ctl_find_id_mixer(soc_card->snd_card, name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
+
+static int jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack, bool initial_kctl)
+{
+ mutex_init(&jack->mutex);
+ jack->card = card;
+ INIT_LIST_HEAD(&jack->pins);
+ INIT_LIST_HEAD(&jack->jack_zones);
+ BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
+
+ return snd_jack_new(card->snd_card, id, type, &jack->jack, initial_kctl, false);
+}
+
+/**
+ * snd_soc_card_jack_new - Create a new jack without pins
+ * @card: ASoC card
+ * @id: an identifying string for this jack
+ * @type: a bitmask of enum snd_jack_type values that can be detected by
+ * this jack
+ * @jack: structure to use for the jack
+ *
+ * Creates a new jack object without pins. If adding pins later,
+ * snd_soc_card_jack_new_pins() should be used instead with 0 as num_pins
+ * argument.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jack will be initialised.
+ */
+int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack)
+{
+ return soc_card_ret(card, jack_new(card, id, type, jack, true));
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
+
+/**
+ * snd_soc_card_jack_new_pins - Create a new jack with pins
+ * @card: ASoC card
+ * @id: an identifying string for this jack
+ * @type: a bitmask of enum snd_jack_type values that can be detected by
+ * this jack
+ * @jack: structure to use for the jack
+ * @pins: Array of jack pins to be added to the jack or NULL
+ * @num_pins: Number of elements in the @pins array
+ *
+ * Creates a new jack object with pins. If not adding pins,
+ * snd_soc_card_jack_new() should be used instead.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jack will be initialised.
+ */
+int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id,
+ int type, struct snd_soc_jack *jack,
+ struct snd_soc_jack_pin *pins,
+ unsigned int num_pins)
+{
+ int ret;
+
+ ret = jack_new(card, id, type, jack, false);
+ if (ret)
+ goto end;
+
+ if (num_pins)
+ ret = snd_soc_jack_add_pins(jack, num_pins, pins);
+end:
+ return soc_card_ret(card, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new_pins);
+
+int snd_soc_card_suspend_pre(struct snd_soc_card *card)
+{
+ int ret = 0;
+
+ if (card->suspend_pre)
+ ret = card->suspend_pre(card);
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_suspend_post(struct snd_soc_card *card)
+{
+ int ret = 0;
+
+ if (card->suspend_post)
+ ret = card->suspend_post(card);
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_resume_pre(struct snd_soc_card *card)
+{
+ int ret = 0;
+
+ if (card->resume_pre)
+ ret = card->resume_pre(card);
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_resume_post(struct snd_soc_card *card)
+{
+ int ret = 0;
+
+ if (card->resume_post)
+ ret = card->resume_post(card);
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_probe(struct snd_soc_card *card)
+{
+ if (card->probe) {
+ int ret = card->probe(card);
+
+ if (ret < 0)
+ return soc_card_ret(card, ret);
+
+ /*
+ * It has "card->probe" and "card->late_probe" callbacks.
+ * So, set "probed" flag here, because it needs to care
+ * about "late_probe".
+ *
+ * see
+ * snd_soc_bind_card()
+ * snd_soc_card_late_probe()
+ */
+ card->probed = 1;
+ }
+
+ return 0;
+}
+
+int snd_soc_card_late_probe(struct snd_soc_card *card)
+{
+ if (card->late_probe) {
+ int ret = card->late_probe(card);
+
+ if (ret < 0)
+ return soc_card_ret(card, ret);
+ }
+
+ /*
+ * It has "card->probe" and "card->late_probe" callbacks,
+ * and "late_probe" callback is called after "probe".
+ * This means, we can set "card->probed" flag afer "late_probe"
+ * for all cases.
+ *
+ * see
+ * snd_soc_bind_card()
+ * snd_soc_card_probe()
+ */
+ card->probed = 1;
+
+ return 0;
+}
+
+void snd_soc_card_fixup_controls(struct snd_soc_card *card)
+{
+ if (card->fixup_controls)
+ card->fixup_controls(card);
+}
+
+int snd_soc_card_remove(struct snd_soc_card *card)
+{
+ int ret = 0;
+
+ if (card->probed &&
+ card->remove)
+ ret = card->remove(card);
+
+ card->probed = 0;
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ int ret = 0;
+
+ if (card->set_bias_level)
+ ret = card->set_bias_level(card, dapm, level);
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ int ret = 0;
+
+ if (card->set_bias_level_post)
+ ret = card->set_bias_level_post(card, dapm, level);
+
+ return soc_card_ret(card, ret);
+}
+
+int snd_soc_card_add_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ int ret = 0;
+
+ if (card->add_dai_link)
+ ret = card->add_dai_link(card, dai_link);
+
+ return soc_card_ret(card, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_add_dai_link);
+
+void snd_soc_card_remove_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ if (card->remove_dai_link)
+ card->remove_dai_link(card, dai_link);
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_remove_dai_link);
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
new file mode 100644
index 000000000000..c815fd1b3fd1
--- /dev/null
+++ b/sound/soc/soc-component.c
@@ -0,0 +1,1300 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// soc-component.c
+//
+// Copyright 2009-2011 Wolfson Microelectronics PLC.
+// Copyright (C) 2019 Renesas Electronics Corp.
+//
+// Mark Brown <broonie@opensource.wolfsonmicro.com>
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <linux/bitops.h>
+
+#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret)
+static inline int _soc_component_ret(struct snd_soc_component *component, const char *func, int ret)
+{
+ return snd_soc_ret(component->dev, ret,
+ "at %s() on %s\n", func, component->name);
+}
+
+#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret_reg_rw(dai, __func__, ret, reg)
+static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component,
+ const char *func, int ret, int reg)
+{
+ return snd_soc_ret(component->dev, ret,
+ "at %s() on %s for register: [0x%08x]\n",
+ func, component->name, reg);
+}
+
+static inline int soc_component_field_shift(struct snd_soc_component *component,
+ unsigned int mask)
+{
+ if (!mask) {
+ dev_err(component->dev, "ASoC: error field mask is zero for %s\n",
+ component->name);
+ return 0;
+ }
+
+ return (ffs(mask) - 1);
+}
+
+/*
+ * We might want to check substream by using list.
+ * In such case, we can update these macros.
+ */
+#define soc_component_mark_push(component, substream, tgt) ((component)->mark_##tgt = substream)
+#define soc_component_mark_pop(component, tgt) ((component)->mark_##tgt = NULL)
+#define soc_component_mark_match(component, substream, tgt) ((component)->mark_##tgt == substream)
+
+void snd_soc_component_set_aux(struct snd_soc_component *component,
+ struct snd_soc_aux_dev *aux)
+{
+ component->init = (aux) ? aux->init : NULL;
+}
+
+int snd_soc_component_init(struct snd_soc_component *component)
+{
+ int ret = 0;
+
+ if (component->init)
+ ret = component->init(component);
+
+ return soc_component_ret(component, ret);
+}
+
+/**
+ * snd_soc_component_set_sysclk - configure COMPONENT system or master clock.
+ * @component: COMPONENT
+ * @clk_id: DAI specific clock ID
+ * @source: Source for the clock
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
+ *
+ * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
+ */
+int snd_soc_component_set_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ int ret = -ENOTSUPP;
+
+ if (component->driver->set_sysclk)
+ ret = component->driver->set_sysclk(component, clk_id, source,
+ freq, dir);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk);
+
+/*
+ * snd_soc_component_set_pll - configure component PLL.
+ * @component: COMPONENT
+ * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
+ *
+ * Configures and enables PLL to generate output clock based on input clock.
+ */
+int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ int ret = -EINVAL;
+
+ if (component->driver->set_pll)
+ ret = component->driver->set_pll(component, pll_id, source,
+ freq_in, freq_out);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_set_pll);
+
+void snd_soc_component_seq_notifier(struct snd_soc_component *component,
+ enum snd_soc_dapm_type type, int subseq)
+{
+ if (component->driver->seq_notifier)
+ component->driver->seq_notifier(component, type, subseq);
+}
+
+int snd_soc_component_stream_event(struct snd_soc_component *component,
+ int event)
+{
+ int ret = 0;
+
+ if (component->driver->stream_event)
+ ret = component->driver->stream_event(component, event);
+
+ return soc_component_ret(component, ret);
+}
+
+int snd_soc_component_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int ret = 0;
+
+ if (component->driver->set_bias_level)
+ ret = component->driver->set_bias_level(component, level);
+
+ return soc_component_ret(component, ret);
+}
+
+int snd_soc_component_enable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_enable_pin(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin);
+
+int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_enable_pin_unlocked(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked);
+
+int snd_soc_component_disable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_disable_pin(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin);
+
+int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_disable_pin_unlocked(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked);
+
+int snd_soc_component_nc_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_nc_pin(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin);
+
+int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_nc_pin_unlocked(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked);
+
+int snd_soc_component_get_pin_status(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_get_pin_status(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status);
+
+int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_force_enable_pin(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin);
+
+int snd_soc_component_force_enable_pin_unlocked(
+ struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked);
+
+static void soc_get_kcontrol_name(struct snd_soc_component *component,
+ char *buf, int size, const char * const ctl)
+{
+ /* When updating, change also snd_soc_dapm_widget_name_cmp() */
+ if (component->name_prefix)
+ snprintf(buf, size, "%s %s", component->name_prefix, ctl);
+ else
+ snprintf(buf, size, "%s", ctl);
+}
+
+struct snd_kcontrol *snd_soc_component_get_kcontrol(struct snd_soc_component *component,
+ const char * const ctl)
+{
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ soc_get_kcontrol_name(component, name, ARRAY_SIZE(name), ctl);
+
+ return snd_soc_card_get_kcontrol(component->card, name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_get_kcontrol);
+
+int snd_soc_component_notify_control(struct snd_soc_component *component,
+ const char * const ctl)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_soc_component_get_kcontrol(component, ctl);
+ if (!kctl)
+ return soc_component_ret(component, -EINVAL);
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_notify_control);
+
+/**
+ * snd_soc_component_set_jack - configure component jack.
+ * @component: COMPONENTs
+ * @jack: structure to use for the jack
+ * @data: can be used if codec driver need extra data for configuring jack
+ *
+ * Configures and enables jack detection function.
+ */
+int snd_soc_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ int ret = -ENOTSUPP;
+
+ if (component->driver->set_jack)
+ ret = component->driver->set_jack(component, jack, data);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_set_jack);
+
+/**
+ * snd_soc_component_get_jack_type
+ * @component: COMPONENTs
+ *
+ * Returns the jack type of the component
+ * This can either be the supported type or one read from
+ * devicetree with the property: jack-type.
+ */
+int snd_soc_component_get_jack_type(
+ struct snd_soc_component *component)
+{
+ int ret = -ENOTSUPP;
+
+ if (component->driver->get_jack_type)
+ ret = component->driver->get_jack_type(component);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_get_jack_type);
+
+int snd_soc_component_module_get(struct snd_soc_component *component,
+ void *mark, int upon_open)
+{
+ int ret = 0;
+
+ if (component->driver->module_get_upon_open == !!upon_open &&
+ !try_module_get(component->dev->driver->owner))
+ ret = -ENODEV;
+
+ /* mark module if succeeded */
+ if (ret == 0)
+ soc_component_mark_push(component, mark, module);
+
+ return soc_component_ret(component, ret);
+}
+
+void snd_soc_component_module_put(struct snd_soc_component *component,
+ void *mark, int upon_open, int rollback)
+{
+ if (rollback && !soc_component_mark_match(component, mark, module))
+ return;
+
+ if (component->driver->module_get_upon_open == !!upon_open)
+ module_put(component->dev->driver->owner);
+
+ /* remove the mark from module */
+ soc_component_mark_pop(component, module);
+}
+
+int snd_soc_component_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (component->driver->open)
+ ret = component->driver->open(component, substream);
+
+ /* mark substream if succeeded */
+ if (ret == 0)
+ soc_component_mark_push(component, substream, open);
+
+ return soc_component_ret(component, ret);
+}
+
+int snd_soc_component_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int rollback)
+{
+ int ret = 0;
+
+ if (rollback && !soc_component_mark_match(component, substream, open))
+ return 0;
+
+ if (component->driver->close)
+ ret = component->driver->close(component, substream);
+
+ /* remove marked substream */
+ soc_component_mark_pop(component, open);
+
+ return soc_component_ret(component, ret);
+}
+
+void snd_soc_component_suspend(struct snd_soc_component *component)
+{
+ if (component->driver->suspend)
+ component->driver->suspend(component);
+ component->suspended = 1;
+}
+
+void snd_soc_component_resume(struct snd_soc_component *component)
+{
+ if (component->driver->resume)
+ component->driver->resume(component);
+ component->suspended = 0;
+}
+
+int snd_soc_component_is_suspended(struct snd_soc_component *component)
+{
+ return component->suspended;
+}
+
+int snd_soc_component_probe(struct snd_soc_component *component)
+{
+ int ret = 0;
+
+ if (component->driver->probe)
+ ret = component->driver->probe(component);
+
+ return soc_component_ret(component, ret);
+}
+
+void snd_soc_component_remove(struct snd_soc_component *component)
+{
+ if (component->driver->remove)
+ component->driver->remove(component);
+}
+
+int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *ep)
+{
+ int ret = -ENOTSUPP;
+
+ if (component->driver->of_xlate_dai_id)
+ ret = component->driver->of_xlate_dai_id(component, ep);
+
+ return soc_component_ret(component, ret);
+}
+
+int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ if (component->driver->of_xlate_dai_name)
+ return component->driver->of_xlate_dai_name(component,
+ args, dai_name);
+ /*
+ * Don't use soc_component_ret here because we may not want to report
+ * the error just yet. If a device has more than one component, the
+ * first may not match and we don't want spam the log with this.
+ */
+ return -ENOTSUPP;
+}
+
+void snd_soc_component_setup_regmap(struct snd_soc_component *component)
+{
+ int val_bytes = regmap_get_val_bytes(component->regmap);
+
+ /* Errors are legitimate for non-integer byte multiples */
+ if (val_bytes > 0)
+ component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the
+ * component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+ struct regmap *regmap)
+{
+ component->regmap = regmap;
+ snd_soc_component_setup_regmap(component);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the
+ * component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+ regmap_exit(component->regmap);
+ component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
+
+int snd_soc_component_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
+{
+ int ret = 0;
+
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->open)
+ ret = component->driver->compress_ops->open(component, cstream);
+
+ /* mark substream if succeeded */
+ if (ret == 0)
+ soc_component_mark_push(component, cstream, compr_open);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_open);
+
+void snd_soc_component_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ int rollback)
+{
+ if (rollback && !soc_component_mark_match(component, cstream, compr_open))
+ return;
+
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->free)
+ component->driver->compress_ops->free(component, cstream);
+
+ /* remove marked substream */
+ soc_component_mark_pop(component, compr_open);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_free);
+
+int snd_soc_component_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->trigger) {
+ ret = component->driver->compress_ops->trigger(
+ component, cstream, cmd);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_trigger);
+
+int snd_soc_component_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->set_params) {
+ ret = component->driver->compress_ops->set_params(
+ component, cstream, params);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_set_params);
+
+int snd_soc_component_compr_get_params(struct snd_compr_stream *cstream,
+ struct snd_codec *params)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->get_params) {
+ ret = component->driver->compress_ops->get_params(
+ component, cstream, params);
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_get_params);
+
+int snd_soc_component_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret = 0;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->get_caps) {
+ ret = component->driver->compress_ops->get_caps(
+ component, cstream, caps);
+ break;
+ }
+ }
+
+ snd_soc_dpcm_mutex_unlock(rtd);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_get_caps);
+
+int snd_soc_component_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret = 0;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->get_codec_caps) {
+ ret = component->driver->compress_ops->get_codec_caps(
+ component, cstream, codec);
+ break;
+ }
+ }
+
+ snd_soc_dpcm_mutex_unlock(rtd);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_get_codec_caps);
+
+int snd_soc_component_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->ack) {
+ ret = component->driver->compress_ops->ack(
+ component, cstream, bytes);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_ack);
+
+int snd_soc_component_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp64 *tstamp)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->pointer) {
+ ret = component->driver->compress_ops->pointer(
+ component, cstream, tstamp);
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_pointer);
+
+int snd_soc_component_compr_copy(struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret = 0;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->copy) {
+ ret = component->driver->compress_ops->copy(
+ component, cstream, buf, count);
+ break;
+ }
+ }
+
+ snd_soc_dpcm_mutex_unlock(rtd);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_copy);
+
+int snd_soc_component_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->set_metadata) {
+ ret = component->driver->compress_ops->set_metadata(
+ component, cstream, metadata);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_set_metadata);
+
+int snd_soc_component_compr_get_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->compress_ops &&
+ component->driver->compress_ops->get_metadata) {
+ ret = component->driver->compress_ops->get_metadata(
+ component, cstream, metadata);
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_compr_get_metadata);
+
+static unsigned int soc_component_read_no_lock(
+ struct snd_soc_component *component,
+ unsigned int reg)
+{
+ int ret;
+ unsigned int val = 0;
+
+ if (component->regmap)
+ ret = regmap_read(component->regmap, reg, &val);
+ else if (component->driver->read) {
+ ret = 0;
+ val = component->driver->read(component, reg);
+ }
+ else
+ ret = -EIO;
+
+ if (ret < 0)
+ return soc_component_ret_reg_rw(component, ret, reg);
+
+ return val;
+}
+
+/**
+ * snd_soc_component_read() - Read register value
+ * @component: Component to read from
+ * @reg: Register to read
+ *
+ * Return: read value
+ */
+unsigned int snd_soc_component_read(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int val;
+
+ mutex_lock(&component->io_mutex);
+ val = soc_component_read_no_lock(component, reg);
+ mutex_unlock(&component->io_mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_read);
+
+static int soc_component_write_no_lock(
+ struct snd_soc_component *component,
+ unsigned int reg, unsigned int val)
+{
+ int ret = -EIO;
+
+ if (component->regmap)
+ ret = regmap_write(component->regmap, reg, val);
+ else if (component->driver->write)
+ ret = component->driver->write(component, reg, val);
+
+ return soc_component_ret_reg_rw(component, ret, reg);
+}
+
+/**
+ * snd_soc_component_write() - Write register value
+ * @component: Component to write to
+ * @reg: Register to write
+ * @val: Value to write to the register
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int snd_soc_component_write(struct snd_soc_component *component,
+ unsigned int reg, unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&component->io_mutex);
+ ret = soc_component_write_no_lock(component, reg, val);
+ mutex_unlock(&component->io_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_write);
+
+static int snd_soc_component_update_bits_legacy(
+ struct snd_soc_component *component, unsigned int reg,
+ unsigned int mask, unsigned int val, bool *change)
+{
+ unsigned int old, new;
+ int ret = 0;
+
+ mutex_lock(&component->io_mutex);
+
+ old = soc_component_read_no_lock(component, reg);
+
+ new = (old & ~mask) | (val & mask);
+ *change = old != new;
+ if (*change)
+ ret = soc_component_write_no_lock(component, reg, new);
+
+ mutex_unlock(&component->io_mutex);
+
+ return soc_component_ret_reg_rw(component, ret, reg);
+}
+
+/**
+ * snd_soc_component_update_bits() - Perform read/modify/write cycle
+ * @component: Component to update
+ * @reg: Register to update
+ * @mask: Mask that specifies which bits to update
+ * @val: New value for the bits specified by mask
+ *
+ * Return: 1 if the operation was successful and the value of the register
+ * changed, 0 if the operation was successful, but the value did not change.
+ * Returns a negative error code otherwise.
+ */
+int snd_soc_component_update_bits(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ bool change;
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_update_bits_check(component->regmap, reg, mask,
+ val, &change);
+ else
+ ret = snd_soc_component_update_bits_legacy(component, reg,
+ mask, val, &change);
+
+ if (ret < 0)
+ return soc_component_ret_reg_rw(component, ret, reg);
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
+
+/**
+ * snd_soc_component_update_bits_async() - Perform asynchronous
+ * read/modify/write cycle
+ * @component: Component to update
+ * @reg: Register to update
+ * @mask: Mask that specifies which bits to update
+ * @val: New value for the bits specified by mask
+ *
+ * This function is similar to snd_soc_component_update_bits(), but the update
+ * operation is scheduled asynchronously. This means it may not be completed
+ * when the function returns. To make sure that all scheduled updates have been
+ * completed snd_soc_component_async_complete() must be called.
+ *
+ * Return: 1 if the operation was successful and the value of the register
+ * changed, 0 if the operation was successful, but the value did not change.
+ * Returns a negative error code otherwise.
+ */
+int snd_soc_component_update_bits_async(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ bool change;
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_update_bits_check_async(component->regmap, reg,
+ mask, val, &change);
+ else
+ ret = snd_soc_component_update_bits_legacy(component, reg,
+ mask, val, &change);
+
+ if (ret < 0)
+ return soc_component_ret_reg_rw(component, ret, reg);
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
+
+/**
+ * snd_soc_component_read_field() - Read register field value
+ * @component: Component to read from
+ * @reg: Register to read
+ * @mask: mask of the register field
+ *
+ * Return: read value of register field.
+ */
+unsigned int snd_soc_component_read_field(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, reg);
+
+ val = (val & mask) >> soc_component_field_shift(component, mask);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_read_field);
+
+/**
+ * snd_soc_component_write_field() - write to register field
+ * @component: Component to write to
+ * @reg: Register to write
+ * @mask: mask of the register field to update
+ * @val: value of the field to write
+ *
+ * Return: 1 for change, otherwise 0.
+ */
+int snd_soc_component_write_field(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask,
+ unsigned int val)
+{
+
+ val = (val << soc_component_field_shift(component, mask)) & mask;
+
+ return snd_soc_component_update_bits(component, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_write_field);
+
+/**
+ * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed
+ * @component: Component for which to wait
+ *
+ * This function blocks until all asynchronous I/O which has previously been
+ * scheduled using snd_soc_component_update_bits_async() has completed.
+ */
+void snd_soc_component_async_complete(struct snd_soc_component *component)
+{
+ if (component->regmap)
+ regmap_async_complete(component->regmap);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_async_complete);
+
+/**
+ * snd_soc_component_test_bits - Test register for change
+ * @component: component
+ * @reg: Register to test
+ * @mask: Mask that specifies which bits to test
+ * @value: Value to test against
+ *
+ * Tests a register with a new value and checks if the new value is
+ * different from the old value.
+ *
+ * Return: 1 for change, otherwise 0.
+ */
+int snd_soc_component_test_bits(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ unsigned int old, new;
+
+ old = snd_soc_component_read(component, reg);
+ new = (old & ~mask) | value;
+ return old != new;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_test_bits);
+
+int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i;
+
+ /* FIXME: use 1st pointer */
+ for_each_rtd_components(rtd, i, component)
+ if (component->driver->pointer)
+ return component->driver->pointer(component, substream);
+
+ return 0;
+}
+
+static bool snd_soc_component_is_codec_on_rtd(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai;
+ int i;
+
+ for_each_rtd_codec_dais(rtd, i, dai) {
+ if (dai->component == component)
+ return true;
+ }
+
+ return false;
+}
+
+void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t *cpu_delay,
+ snd_pcm_sframes_t *codec_delay)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ snd_pcm_sframes_t delay;
+ int i;
+
+ /*
+ * We're looking for the delay through the full audio path so it needs to
+ * be the maximum of the Components doing transmit and the maximum of the
+ * Components doing receive (ie, all CPUs and all CODECs) rather than
+ * just the maximum of all Components.
+ */
+ for_each_rtd_components(rtd, i, component) {
+ if (!component->driver->delay)
+ continue;
+
+ delay = component->driver->delay(component, substream);
+
+ if (snd_soc_component_is_codec_on_rtd(rtd, component))
+ *codec_delay = max(*codec_delay, delay);
+ else
+ *cpu_delay = max(*cpu_delay, delay);
+ }
+}
+
+int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i;
+
+ /* FIXME: use 1st ioctl */
+ for_each_rtd_components(rtd, i, component)
+ if (component->driver->ioctl)
+ return soc_component_ret(
+ component,
+ component->driver->ioctl(component,
+ substream, cmd, arg));
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->sync_stop) {
+ ret = component->driver->sync_stop(component,
+ substream);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+
+int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ struct iov_iter *iter, unsigned long bytes)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i;
+
+ /* FIXME. it returns 1st copy now */
+ for_each_rtd_components(rtd, i, component)
+ if (component->driver->copy)
+ return soc_component_ret(component,
+ component->driver->copy(component, substream,
+ channel, pos, iter, bytes));
+
+ return -EINVAL;
+}
+
+struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ struct page *page;
+ int i;
+
+ /* FIXME. it returns 1st page now */
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->page) {
+ page = component->driver->page(component,
+ substream, offset);
+ if (page)
+ return page;
+ }
+ }
+
+ return NULL;
+}
+
+int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i;
+
+ /* FIXME. it returns 1st mmap now */
+ for_each_rtd_components(rtd, i, component)
+ if (component->driver->mmap)
+ return soc_component_ret(
+ component,
+ component->driver->mmap(component,
+ substream, vma));
+
+ return -EINVAL;
+}
+
+int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component;
+ int ret;
+ int i;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->pcm_construct) {
+ ret = component->driver->pcm_construct(component, rtd);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+
+void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component;
+ int i;
+
+ if (!rtd->pcm)
+ return;
+
+ for_each_rtd_components(rtd, i, component)
+ if (component->driver->pcm_destruct)
+ component->driver->pcm_destruct(component, rtd->pcm);
+}
+
+int snd_soc_pcm_component_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->prepare) {
+ ret = component->driver->prepare(component, substream);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+
+int snd_soc_pcm_component_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->hw_params) {
+ ret = component->driver->hw_params(component,
+ substream, params);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ /* mark substream if succeeded */
+ soc_component_mark_push(component, substream, hw_params);
+ }
+
+ return 0;
+}
+
+void snd_soc_pcm_component_hw_free(struct snd_pcm_substream *substream,
+ int rollback)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (rollback && !soc_component_mark_match(component, substream, hw_params))
+ continue;
+
+ if (component->driver->hw_free) {
+ ret = component->driver->hw_free(component, substream);
+ if (ret < 0)
+ soc_component_ret(component, ret);
+ }
+
+ /* remove marked substream */
+ soc_component_mark_pop(component, hw_params);
+ }
+}
+
+static int soc_component_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int ret = 0;
+
+ if (component->driver->trigger)
+ ret = component->driver->trigger(component, substream, cmd);
+
+ return soc_component_ret(component, ret);
+}
+
+int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream,
+ int cmd, int rollback)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i, r, ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ for_each_rtd_components(rtd, i, component) {
+ ret = soc_component_trigger(component, substream, cmd);
+ if (ret < 0)
+ break;
+ soc_component_mark_push(component, substream, trigger);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ for_each_rtd_components(rtd, i, component) {
+ if (rollback && !soc_component_mark_match(component, substream, trigger))
+ continue;
+
+ r = soc_component_trigger(component, substream, cmd);
+ if (r < 0)
+ ret = r; /* use last ret */
+ soc_component_mark_pop(component, trigger);
+ }
+ }
+
+ return ret;
+}
+
+int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd,
+ void *stream)
+{
+ struct snd_soc_component *component;
+ int i;
+
+ for_each_rtd_components(rtd, i, component) {
+ int ret = pm_runtime_get_sync(component->dev);
+ if (ret < 0 && ret != -EACCES) {
+ pm_runtime_put_noidle(component->dev);
+ return soc_component_ret(component, ret);
+ }
+ /* mark stream if succeeded */
+ soc_component_mark_push(component, stream, pm);
+ }
+
+ return 0;
+}
+
+void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd,
+ void *stream, int rollback)
+{
+ struct snd_soc_component *component;
+ int i;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (rollback && !soc_component_mark_match(component, stream, pm))
+ continue;
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ /* remove marked stream */
+ soc_component_mark_pop(component, pm);
+ }
+}
+
+int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component;
+ int i;
+
+ /* FIXME: use 1st pointer */
+ for_each_rtd_components(rtd, i, component)
+ if (component->driver->ack)
+ return component->driver->ack(component, substream);
+
+ return 0;
+}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 875733c52953..7b81dffc6a93 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -1,18 +1,12 @@
-/*
- * soc-compress.c -- ALSA SoC Compress
- *
- * Copyright (C) 2012 Intel Corp.
- *
- * Authors: Namarta Kohli <namartax.kohli@intel.com>
- * Ramesh Babu K V <ramesh.babu@linux.intel.com>
- * Vinod Koul <vinod.koul@linux.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; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// soc-compress.c -- ALSA SoC Compress
+//
+// Copyright (C) 2012 Intel Corp.
+//
+// Authors: Namarta Kohli <namartax.kohli@intel.com>
+// Ramesh Babu K V <ramesh.babu@linux.intel.com>
+// Vinod Koul <vinod.koul@linux.intel.com>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -25,105 +19,161 @@
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/soc-dpcm.h>
+#include <sound/soc-link.h>
-static int soc_compr_open(struct snd_compr_stream *cstream)
+static int snd_soc_compr_components_open(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_component *component;
int ret = 0;
+ int i;
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ for_each_rtd_components(rtd, i, component) {
+ ret = snd_soc_component_module_get_when_open(component, cstream);
+ if (ret < 0)
+ break;
- if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
- ret = platform->driver->compr_ops->open(cstream);
- if (ret < 0) {
- pr_err("compress asoc: can't open platform %s\n",
- platform->component.name);
- goto out;
- }
+ ret = snd_soc_component_compr_open(component, cstream);
+ if (ret < 0)
+ break;
}
- if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
- ret = rtd->dai_link->compr_ops->startup(cstream);
- if (ret < 0) {
- pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name);
- goto machine_err;
- }
+ return ret;
+}
+
+static void snd_soc_compr_components_free(struct snd_compr_stream *cstream,
+ int rollback)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component;
+ int i;
+
+ for_each_rtd_components(rtd, i, component) {
+ snd_soc_component_compr_free(component, cstream, rollback);
+ snd_soc_component_module_put_when_close(component, cstream, rollback);
}
+}
+
+static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+
+ snd_soc_dpcm_mutex_lock(rtd);
+
+ if (!rollback)
+ snd_soc_runtime_deactivate(rtd, stream);
+
+ snd_soc_dai_digital_mute(codec_dai, 1, stream);
+
+ if (!snd_soc_dai_active(cpu_dai))
+ cpu_dai->symmetric_rate = 0;
+
+ if (!snd_soc_dai_active(codec_dai))
+ codec_dai->symmetric_rate = 0;
+
+ snd_soc_link_compr_shutdown(cstream, rollback);
- snd_soc_runtime_activate(rtd, cstream->direction);
+ snd_soc_compr_components_free(cstream, rollback);
- mutex_unlock(&rtd->pcm_mutex);
+ snd_soc_dai_compr_shutdown(cpu_dai, cstream, rollback);
+
+ if (!rollback)
+ snd_soc_dapm_stream_stop(rtd, stream);
+
+ snd_soc_dpcm_mutex_unlock(rtd);
+
+ snd_soc_pcm_component_pm_runtime_put(rtd, cstream, rollback);
return 0;
+}
+
+static int soc_compr_free(struct snd_compr_stream *cstream)
+{
+ return soc_compr_clean(cstream, 0);
+}
+
+static int soc_compr_open(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+ int ret;
+
+ ret = snd_soc_pcm_component_pm_runtime_get(rtd, cstream);
+ if (ret < 0)
+ goto err_no_lock;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+
+ ret = snd_soc_dai_compr_startup(cpu_dai, cstream);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_soc_compr_components_open(cstream);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_soc_link_compr_startup(cstream);
+ if (ret < 0)
+ goto err;
+
+ snd_soc_runtime_activate(rtd, stream);
+err:
+ snd_soc_dpcm_mutex_unlock(rtd);
+err_no_lock:
+ if (ret < 0)
+ soc_compr_clean(cstream, 1);
-machine_err:
- if (platform->driver->compr_ops && platform->driver->compr_ops->free)
- platform->driver->compr_ops->free(cstream);
-out:
- mutex_unlock(&rtd->pcm_mutex);
return ret;
}
static int soc_compr_open_fe(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
- struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream;
- struct snd_soc_platform *platform = fe->platform;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
struct snd_soc_dpcm *dpcm;
struct snd_soc_dapm_widget_list *list;
- int stream;
- int ret = 0;
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+ int ret;
- if (cstream->direction == SND_COMPRESS_PLAYBACK)
- stream = SNDRV_PCM_STREAM_PLAYBACK;
- else
- stream = SNDRV_PCM_STREAM_CAPTURE;
-
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
- ret = platform->driver->compr_ops->open(cstream);
- if (ret < 0) {
- pr_err("compress asoc: can't open platform %s\n",
- platform->component.name);
- goto out;
- }
- }
-
- if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) {
- ret = fe->dai_link->compr_ops->startup(cstream);
- if (ret < 0) {
- pr_err("compress asoc: %s startup failed\n", fe->dai_link->name);
- goto machine_err;
- }
- }
-
- fe->dpcm[stream].runtime = fe_substream->runtime;
+ snd_soc_card_mutex_lock(fe->card);
ret = dpcm_path_get(fe, stream, &list);
if (ret < 0)
- goto fe_err;
- else if (ret == 0)
- dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
- fe->dai_link->name, stream ? "capture" : "playback");
+ goto be_err;
+
+ snd_soc_dpcm_mutex_lock(fe);
/* calculate valid and active FE <-> BE dpcms */
- dpcm_process_paths(fe, stream, &list, 1);
+ dpcm_add_paths(fe, stream, &list);
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
ret = dpcm_be_dai_startup(fe, stream);
if (ret < 0) {
/* clean up all links */
- list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+ for_each_dpcm_be(fe, stream, dpcm)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
dpcm_be_disconnect(fe, stream);
- fe->dpcm[stream].runtime = NULL;
- goto fe_err;
+ goto out;
}
+ ret = snd_soc_dai_compr_startup(cpu_dai, cstream);
+ if (ret < 0)
+ goto out;
+
+ ret = snd_soc_compr_components_open(cstream);
+ if (ret < 0)
+ goto open_err;
+
+ ret = snd_soc_link_compr_startup(cstream);
+ if (ret < 0)
+ goto machine_err;
+
dpcm_clear_pending_state(fe, stream);
dpcm_path_put(&list);
@@ -131,131 +181,45 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
snd_soc_runtime_activate(fe, stream);
+ snd_soc_dpcm_mutex_unlock(fe);
- mutex_unlock(&fe->card->mutex);
+ snd_soc_card_mutex_unlock(fe->card);
return 0;
-fe_err:
- if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown)
- fe->dai_link->compr_ops->shutdown(cstream);
machine_err:
- if (platform->driver->compr_ops && platform->driver->compr_ops->free)
- platform->driver->compr_ops->free(cstream);
+ snd_soc_compr_components_free(cstream, 1);
+open_err:
+ snd_soc_dai_compr_shutdown(cpu_dai, cstream, 1);
out:
+ dpcm_path_put(&list);
+ snd_soc_dpcm_mutex_unlock(fe);
+be_err:
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
- mutex_unlock(&fe->card->mutex);
+ snd_soc_card_mutex_unlock(fe->card);
return ret;
}
-/*
- * Power down the audio subsystem pmdown_time msecs after close is called.
- * This is to ensure there are no pops or clicks in between any music tracks
- * due to DAPM power cycling.
- */
-static void close_delayed_work(struct work_struct *work)
-{
- struct snd_soc_pcm_runtime *rtd =
- container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-
- dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
- codec_dai->driver->playback.stream_name,
- codec_dai->playback_active ? "active" : "inactive",
- rtd->pop_wait ? "yes" : "no");
-
- /* are we waiting on this codec DAI stream */
- if (rtd->pop_wait == 1) {
- rtd->pop_wait = 0;
- snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_STOP);
- }
-
- mutex_unlock(&rtd->pcm_mutex);
-}
-
-static int soc_compr_free(struct snd_compr_stream *cstream)
-{
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int stream;
-
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-
- if (cstream->direction == SND_COMPRESS_PLAYBACK)
- stream = SNDRV_PCM_STREAM_PLAYBACK;
- else
- stream = SNDRV_PCM_STREAM_CAPTURE;
-
- snd_soc_runtime_deactivate(rtd, stream);
-
- snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
-
- if (!cpu_dai->active)
- cpu_dai->rate = 0;
-
- if (!codec_dai->active)
- codec_dai->rate = 0;
-
-
- if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown)
- rtd->dai_link->compr_ops->shutdown(cstream);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->free)
- platform->driver->compr_ops->free(cstream);
-
- if (cstream->direction == SND_COMPRESS_PLAYBACK) {
- if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_STOP);
- } else {
- rtd->pop_wait = 1;
- queue_delayed_work(system_power_efficient_wq,
- &rtd->delayed_work,
- msecs_to_jiffies(rtd->pmdown_time));
- }
- } else {
- /* capture streams can be powered down now */
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_CAPTURE,
- SND_SOC_DAPM_STREAM_STOP);
- }
-
- mutex_unlock(&rtd->pcm_mutex);
- return 0;
-}
-
static int soc_compr_free_fe(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
- struct snd_soc_platform *platform = fe->platform;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
struct snd_soc_dpcm *dpcm;
- int stream, ret;
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-
- if (cstream->direction == SND_COMPRESS_PLAYBACK)
- stream = SNDRV_PCM_STREAM_PLAYBACK;
- else
- stream = SNDRV_PCM_STREAM_CAPTURE;
+ snd_soc_card_mutex_lock(fe->card);
+ snd_soc_dpcm_mutex_lock(fe);
snd_soc_runtime_deactivate(fe, stream);
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
- ret = dpcm_be_dai_hw_free(fe, stream);
- if (ret < 0)
- dev_err(fe->dev, "compressed hw_free failed %d\n", ret);
+ dpcm_be_dai_hw_free(fe, stream);
- ret = dpcm_be_dai_shutdown(fe, stream);
+ dpcm_be_dai_shutdown(fe, stream);
/* mark FE's links ready to prune */
- list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+ for_each_dpcm_be(fe, stream, dpcm)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
@@ -265,76 +229,70 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
dpcm_be_disconnect(fe, stream);
- fe->dpcm[stream].runtime = NULL;
+ snd_soc_dpcm_mutex_unlock(fe);
+
+ snd_soc_link_compr_shutdown(cstream, 0);
- if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown)
- fe->dai_link->compr_ops->shutdown(cstream);
+ snd_soc_compr_components_free(cstream, 0);
- if (platform->driver->compr_ops && platform->driver->compr_ops->free)
- platform->driver->compr_ops->free(cstream);
+ snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0);
- mutex_unlock(&fe->card->mutex);
+ snd_soc_card_mutex_unlock(fe->card);
return 0;
}
static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
{
-
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret = 0;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+ int ret;
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ snd_soc_dpcm_mutex_lock(rtd);
- if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
- ret = platform->driver->compr_ops->trigger(cstream, cmd);
- if (ret < 0)
- goto out;
- }
+ ret = snd_soc_component_compr_trigger(cstream, cmd);
+ if (ret < 0)
+ goto out;
+
+ ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
+ if (ret < 0)
+ goto out;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction);
+ snd_soc_dai_digital_mute(codec_dai, 0, stream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
+ snd_soc_dai_digital_mute(codec_dai, 1, stream);
break;
}
out:
- mutex_unlock(&rtd->pcm_mutex);
+ snd_soc_dpcm_mutex_unlock(rtd);
return ret;
}
static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
- struct snd_soc_platform *platform = fe->platform;
- int ret = 0, stream;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+ int ret;
if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
- cmd == SND_COMPR_TRIGGER_DRAIN) {
-
- if (platform->driver->compr_ops &&
- platform->driver->compr_ops->trigger)
- return platform->driver->compr_ops->trigger(cstream,
- cmd);
- }
-
- if (cstream->direction == SND_COMPRESS_PLAYBACK)
- stream = SNDRV_PCM_STREAM_PLAYBACK;
- else
- stream = SNDRV_PCM_STREAM_CAPTURE;
+ cmd == SND_COMPR_TRIGGER_DRAIN)
+ return snd_soc_component_compr_trigger(cstream, cmd);
+ snd_soc_card_mutex_lock(fe->card);
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
+ if (ret < 0)
+ goto out;
- if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
- ret = platform->driver->compr_ops->trigger(cstream, cmd);
- if (ret < 0)
- goto out;
- }
+ ret = snd_soc_component_compr_trigger(cstream, cmd);
+ if (ret < 0)
+ goto out;
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
@@ -357,83 +315,65 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
out:
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
- mutex_unlock(&fe->card->mutex);
+ snd_soc_card_mutex_unlock(fe->card);
return ret;
}
static int soc_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params)
+ struct snd_compr_params *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+ int ret;
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ snd_soc_dpcm_mutex_lock(rtd);
- /* first we call set_params for the platform driver
- * this should configure the soc side
- * if the machine has compressed ops then we call that as well
- * expectation is that platform and machine will configure everything
- * for this compress path, like configuring pcm port for codec
+ /*
+ * First we call set_params for the CPU DAI, then the component
+ * driver this should configure the SoC side. If the machine has
+ * compressed ops then we call that as well. The expectation is
+ * that these callbacks will configure everything for this compress
+ * path, like configuring a PCM port for a CODEC.
*/
- if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
- ret = platform->driver->compr_ops->set_params(cstream, params);
- if (ret < 0)
- goto err;
- }
+ ret = snd_soc_dai_compr_set_params(cpu_dai, cstream, params);
+ if (ret < 0)
+ goto err;
- if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
- ret = rtd->dai_link->compr_ops->set_params(cstream);
- if (ret < 0)
- goto err;
- }
+ ret = snd_soc_component_compr_set_params(cstream, params);
+ if (ret < 0)
+ goto err;
- if (cstream->direction == SND_COMPRESS_PLAYBACK)
- snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_START);
- else
- snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
- SND_SOC_DAPM_STREAM_START);
+ ret = snd_soc_link_compr_set_params(cstream);
+ if (ret < 0)
+ goto err;
+
+ snd_soc_dapm_stream_event(rtd, stream, SND_SOC_DAPM_STREAM_START);
/* cancel any delayed stream shutdown that is pending */
rtd->pop_wait = 0;
- mutex_unlock(&rtd->pcm_mutex);
+ snd_soc_dpcm_mutex_unlock(rtd);
cancel_delayed_work_sync(&rtd->delayed_work);
- return ret;
+ return 0;
err:
- mutex_unlock(&rtd->pcm_mutex);
+ snd_soc_dpcm_mutex_unlock(rtd);
return ret;
}
static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
- struct snd_compr_params *params)
+ struct snd_compr_params *params)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
- struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream;
- struct snd_soc_platform *platform = fe->platform;
- int ret = 0, stream;
-
- if (cstream->direction == SND_COMPRESS_PLAYBACK)
- stream = SNDRV_PCM_STREAM_PLAYBACK;
- else
- stream = SNDRV_PCM_STREAM_CAPTURE;
-
- mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
- ret = platform->driver->compr_ops->set_params(cstream, params);
- if (ret < 0)
- goto out;
- }
+ struct snd_pcm_substream *fe_substream =
+ fe->pcm->streams[cstream->direction].substream;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
+ int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+ int ret;
- if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) {
- ret = fe->dai_link->compr_ops->set_params(cstream);
- if (ret < 0)
- goto out;
- }
+ snd_soc_card_mutex_lock(fe->card);
/*
* Create an empty hw_params for the BE as the machine driver must
@@ -445,141 +385,122 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ snd_soc_dpcm_mutex_lock(fe);
ret = dpcm_be_dai_hw_params(fe, stream);
+ snd_soc_dpcm_mutex_unlock(fe);
if (ret < 0)
goto out;
+ snd_soc_dpcm_mutex_lock(fe);
ret = dpcm_be_dai_prepare(fe, stream);
+ snd_soc_dpcm_mutex_unlock(fe);
if (ret < 0)
goto out;
+ ret = snd_soc_dai_compr_set_params(cpu_dai, cstream, params);
+ if (ret < 0)
+ goto out;
+
+ ret = snd_soc_component_compr_set_params(cstream, params);
+ if (ret < 0)
+ goto out;
+
+ ret = snd_soc_link_compr_set_params(cstream);
+ if (ret < 0)
+ goto out;
+ snd_soc_dpcm_mutex_lock(fe);
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
+ snd_soc_dpcm_mutex_unlock(fe);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
- mutex_unlock(&fe->card->mutex);
+ snd_soc_card_mutex_unlock(fe->card);
return ret;
}
static int soc_compr_get_params(struct snd_compr_stream *cstream,
- struct snd_codec *params)
+ struct snd_codec *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
int ret = 0;
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
- ret = platform->driver->compr_ops->get_params(cstream, params);
-
- mutex_unlock(&rtd->pcm_mutex);
- return ret;
-}
-
-static int soc_compr_get_caps(struct snd_compr_stream *cstream,
- struct snd_compr_caps *caps)
-{
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
-
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
- ret = platform->driver->compr_ops->get_caps(cstream, caps);
+ snd_soc_dpcm_mutex_lock(rtd);
- mutex_unlock(&rtd->pcm_mutex);
- return ret;
-}
-
-static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
- struct snd_compr_codec_caps *codec)
-{
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
-
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
- ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
+ ret = snd_soc_dai_compr_get_params(cpu_dai, cstream, params);
+ if (ret < 0)
+ goto err;
- mutex_unlock(&rtd->pcm_mutex);
+ ret = snd_soc_component_compr_get_params(cstream, params);
+err:
+ snd_soc_dpcm_mutex_unlock(rtd);
return ret;
}
static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ snd_soc_dpcm_mutex_lock(rtd);
- if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
- ret = platform->driver->compr_ops->ack(cstream, bytes);
+ ret = snd_soc_dai_compr_ack(cpu_dai, cstream, bytes);
+ if (ret < 0)
+ goto err;
- mutex_unlock(&rtd->pcm_mutex);
+ ret = snd_soc_component_compr_ack(cstream, bytes);
+err:
+ snd_soc_dpcm_mutex_unlock(rtd);
return ret;
}
static int soc_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp)
+ struct snd_compr_tstamp64 *tstamp)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
-
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ int ret;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
- if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
- platform->driver->compr_ops->pointer(cstream, tstamp);
+ snd_soc_dpcm_mutex_lock(rtd);
- mutex_unlock(&rtd->pcm_mutex);
- return 0;
-}
-
-static int soc_compr_copy(struct snd_compr_stream *cstream,
- char __user *buf, size_t count)
-{
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
-
- mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-
- if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
- ret = platform->driver->compr_ops->copy(cstream, buf, count);
+ ret = snd_soc_dai_compr_pointer(cpu_dai, cstream, tstamp);
+ if (ret < 0)
+ goto out;
- mutex_unlock(&rtd->pcm_mutex);
+ ret = snd_soc_component_compr_pointer(cstream, tstamp);
+out:
+ snd_soc_dpcm_mutex_unlock(rtd);
return ret;
}
static int soc_compr_set_metadata(struct snd_compr_stream *cstream,
- struct snd_compr_metadata *metadata)
+ struct snd_compr_metadata *metadata)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
- if (platform->driver->compr_ops && platform->driver->compr_ops->set_metadata)
- ret = platform->driver->compr_ops->set_metadata(cstream, metadata);
+ ret = snd_soc_dai_compr_set_metadata(cpu_dai, cstream, metadata);
+ if (ret < 0)
+ return ret;
- return ret;
+ return snd_soc_component_compr_set_metadata(cstream, metadata);
}
static int soc_compr_get_metadata(struct snd_compr_stream *cstream,
- struct snd_compr_metadata *metadata)
+ struct snd_compr_metadata *metadata)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- int ret = 0;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
- if (platform->driver->compr_ops && platform->driver->compr_ops->get_metadata)
- ret = platform->driver->compr_ops->get_metadata(cstream, metadata);
+ ret = snd_soc_dai_compr_get_metadata(cpu_dai, cstream, metadata);
+ if (ret < 0)
+ return ret;
- return ret;
+ return snd_soc_component_compr_get_metadata(cstream, metadata);
}
/* ASoC Compress operations */
@@ -593,8 +514,8 @@ static struct snd_compr_ops soc_compr_ops = {
.trigger = soc_compr_trigger,
.pointer = soc_compr_pointer,
.ack = soc_compr_ack,
- .get_caps = soc_compr_get_caps,
- .get_codec_caps = soc_compr_get_codec_caps
+ .get_caps = snd_soc_component_compr_get_caps,
+ .get_codec_caps = snd_soc_component_compr_get_codec_caps,
};
/* ASoC Dynamic Compress operations */
@@ -608,129 +529,147 @@ static struct snd_compr_ops soc_compr_dyn_ops = {
.trigger = soc_compr_trigger_fe,
.pointer = soc_compr_pointer,
.ack = soc_compr_ack,
- .get_caps = soc_compr_get_caps,
- .get_codec_caps = soc_compr_get_codec_caps
+ .get_caps = snd_soc_component_compr_get_caps,
+ .get_codec_caps = snd_soc_component_compr_get_codec_caps,
};
/**
* snd_soc_new_compress - create a new compress.
*
* @rtd: The runtime for which we will create compress
- * @num: the device index number (zero based - shared with normal PCMs)
*
* Return: 0 for success, else error.
*/
-int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
+int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_component *component;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_compr *compr;
struct snd_pcm *be_pcm;
char new_name[64];
int ret = 0, direction = 0;
int playback = 0, capture = 0;
+ int i;
+
+ /*
+ * make sure these are same value,
+ * and then use these as equally
+ */
+ BUILD_BUG_ON((int)SNDRV_PCM_STREAM_PLAYBACK != (int)SND_COMPRESS_PLAYBACK);
+ BUILD_BUG_ON((int)SNDRV_PCM_STREAM_CAPTURE != (int)SND_COMPRESS_CAPTURE);
- if (rtd->num_codecs > 1) {
- dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n");
+ if (rtd->dai_link->num_cpus > 1 ||
+ rtd->dai_link->num_codecs > 1) {
+ dev_err(rtd->card->dev,
+ "Compress ASoC: Multi CPU/Codec not supported\n");
return -EINVAL;
}
- /* check client and interface hw capabilities */
- snprintf(new_name, sizeof(new_name), "%s %s-%d",
- rtd->dai_link->stream_name, codec_dai->name, num);
+ if (!codec_dai) {
+ dev_err(rtd->card->dev, "Missing codec\n");
+ return -EINVAL;
+ }
- if (codec_dai->driver->playback.channels_min)
+ /* check client and interface hw capabilities */
+ if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
+ snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK))
playback = 1;
- if (codec_dai->driver->capture.channels_min)
+ if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
+ snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE))
capture = 1;
- capture = capture && cpu_dai->driver->capture.channels_min;
- playback = playback && cpu_dai->driver->playback.channels_min;
-
/*
* Compress devices are unidirectional so only one of the directions
* should be set, check for that (xor)
*/
if (playback + capture != 1) {
- dev_err(rtd->card->dev, "Invalid direction for compress P %d, C %d\n",
- playback, capture);
+ dev_err(rtd->card->dev,
+ "Compress ASoC: Invalid direction for P %d, C %d\n",
+ playback, capture);
return -EINVAL;
}
- if(playback)
+ if (playback)
direction = SND_COMPRESS_PLAYBACK;
else
direction = SND_COMPRESS_CAPTURE;
- compr = kzalloc(sizeof(*compr), GFP_KERNEL);
- if (compr == NULL) {
- snd_printk(KERN_ERR "Cannot allocate compr\n");
+ compr = devm_kzalloc(rtd->card->dev, sizeof(*compr), GFP_KERNEL);
+ if (!compr)
return -ENOMEM;
- }
compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops),
GFP_KERNEL);
- if (compr->ops == NULL) {
- dev_err(rtd->card->dev, "Cannot allocate compressed ops\n");
- ret = -ENOMEM;
- goto compr_err;
- }
+ if (!compr->ops)
+ return -ENOMEM;
if (rtd->dai_link->dynamic) {
+ int playback = 1;
+ int capture = 1;
+
+ if (rtd->dai_link->capture_only)
+ playback = 0;
+ if (rtd->dai_link->playback_only)
+ capture = 0;
+
snprintf(new_name, sizeof(new_name), "(%s)",
rtd->dai_link->stream_name);
- ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
- rtd->dai_link->dpcm_playback,
- rtd->dai_link->dpcm_capture, &be_pcm);
+ ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, rtd->id,
+ playback, capture, &be_pcm);
if (ret < 0) {
- dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n",
- rtd->dai_link->name);
- goto compr_err;
+ dev_err(rtd->card->dev,
+ "Compress ASoC: can't create compressed for %s: %d\n",
+ rtd->dai_link->name, ret);
+ return ret;
}
+ /* inherit atomicity from DAI link */
+ be_pcm->nonatomic = rtd->dai_link->nonatomic;
+
rtd->pcm = be_pcm;
rtd->fe_compr = 1;
- if (rtd->dai_link->dpcm_playback)
+ if (playback)
be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
- else if (rtd->dai_link->dpcm_capture)
+ if (capture)
be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops));
- } else
- memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
+ } else {
+ snprintf(new_name, sizeof(new_name), "%s %s-%d",
+ rtd->dai_link->stream_name, codec_dai->name, rtd->id);
- /* Add copy callback for not memory mapped DSPs */
- if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
- compr->ops->copy = soc_compr_copy;
+ memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
+ }
- mutex_init(&compr->lock);
+ for_each_rtd_components(rtd, i, component) {
+ if (!component->driver->compress_ops ||
+ !component->driver->compress_ops->copy)
+ continue;
- snprintf(new_name, sizeof(new_name), "%s %s-%d",
- rtd->dai_link->stream_name,
- rtd->codec_dai->name, num);
+ compr->ops->copy = snd_soc_component_compr_copy;
+ break;
+ }
- ret = snd_compress_new(rtd->card->snd_card, num, direction,
+ ret = snd_compress_new(rtd->card->snd_card, rtd->id, direction,
new_name, compr);
if (ret < 0) {
- pr_err("compress asoc: can't create compress for codec %s\n",
- codec->component.name);
- goto compr_err;
+ component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ dev_err(component->dev,
+ "Compress ASoC: can't create compress for codec %s: %d\n",
+ component->name, ret);
+ return ret;
}
/* DAPM dai link stream work */
- INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+ rtd->close_delayed_work_func = snd_soc_close_delayed_work;
rtd->compr = compr;
compr->private_data = rtd;
- printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
- cpu_dai->name);
- return ret;
+ dev_dbg(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n",
+ codec_dai->name, cpu_dai->name);
-compr_err:
- kfree(compr);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_new_compress);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 790ee2bf1a47..e4b21bf39e59 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1,26 +1,21 @@
-/*
- * soc-core.c -- ALSA SoC Audio Layer
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- * Copyright (C) 2010 Slimlogic Ltd.
- * Copyright (C) 2010 Texas Instruments Inc.
- *
- * Author: Liam Girdwood <lrg@slimlogic.co.uk>
- * with code, comments and ideas from :-
- * Richard Purdie <richard@openedhand.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.
- *
- * TODO:
- * o Add hw rules to enforce rates, etc.
- * o More testing with other codecs/machines.
- * o Add more codecs and platforms to ensure good API coverage.
- * o Support TDM on PCM and I2S
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// soc-core.c -- ALSA SoC Audio Layer
+//
+// Copyright 2005 Wolfson Microelectronics PLC.
+// Copyright 2005 Openedhand Ltd.
+// Copyright (C) 2010 Slimlogic Ltd.
+// Copyright (C) 2010 Texas Instruments Inc.
+//
+// Author: Liam Girdwood <lrg@slimlogic.co.uk>
+// with code, comments and ideas from :-
+// Richard Purdie <richard@openedhand.com>
+//
+// TODO:
+// o Add hw rules to enforce rates, etc.
+// o More testing with other codecs/machines.
+// o Add more codecs and platforms to ensure good API coverage.
+// o Support TDM on PCM and I2S
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -34,29 +29,35 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
-#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
#include <sound/soc-topology.h>
+#include <sound/soc-link.h>
#include <sound/initval.h>
#define CREATE_TRACE_POINTS
#include <trace/events/asoc.h>
-#define NAME_SIZE 32
-
-#ifdef CONFIG_DEBUG_FS
-struct dentry *snd_soc_debugfs_root;
-EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
-#endif
-
static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(platform_list);
-static LIST_HEAD(codec_list);
static LIST_HEAD(component_list);
+static LIST_HEAD(unbind_card_list);
+
+#define for_each_component(component) \
+ list_for_each_entry(component, &component_list, list)
+
+/*
+ * This is used if driver don't need to have CPU/Codec/Platform
+ * dai_link. see soc.h
+ */
+struct snd_soc_dai_link_component null_dailink_component[0];
+EXPORT_SYMBOL_GPL(null_dailink_component);
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
@@ -67,109 +68,17 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
-/* returns the minimum number of bytes needed to represent
- * a particular given value */
-static int min_bytes_needed(unsigned long val)
-{
- int c = 0;
- int i;
-
- for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c)
- if (val & (1UL << i))
- break;
- c = (sizeof val * 8) - c;
- if (!c || (c % 8))
- c = (c + 8) / 8;
- else
- c /= 8;
- return c;
-}
-
-/* fill buf which is 'len' bytes with a formatted
- * string of the form 'reg: value\n' */
-static int format_register_str(struct snd_soc_codec *codec,
- unsigned int reg, char *buf, size_t len)
-{
- int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
- int regsize = codec->driver->reg_word_size * 2;
- int ret;
-
- /* +2 for ': ' and + 1 for '\n' */
- if (wordsize + regsize + 2 + 1 != len)
- return -EINVAL;
-
- sprintf(buf, "%.*x: ", wordsize, reg);
- buf += wordsize + 2;
-
- ret = snd_soc_read(codec, reg);
- if (ret < 0)
- memset(buf, 'X', regsize);
- else
- sprintf(buf, "%.*x", regsize, ret);
- buf[regsize] = '\n';
- /* no NUL-termination needed */
- return 0;
-}
-
-/* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
- size_t count, loff_t pos)
-{
- int i, step = 1;
- int wordsize, regsize;
- int len;
- size_t total = 0;
- loff_t p = 0;
-
- wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
- regsize = codec->driver->reg_word_size * 2;
-
- len = wordsize + regsize + 2 + 1;
-
- if (!codec->driver->reg_cache_size)
- return 0;
-
- if (codec->driver->reg_cache_step)
- step = codec->driver->reg_cache_step;
-
- for (i = 0; i < codec->driver->reg_cache_size; i += step) {
- /* only support larger than PAGE_SIZE bytes debugfs
- * entries for the default case */
- if (p >= pos) {
- if (total + len >= count - 1)
- break;
- format_register_str(codec, i, buf + total, len);
- total += len;
- }
- p += len;
- }
-
- total = min(total, count - 1);
-
- return total;
-}
-
-static ssize_t codec_reg_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
-
- return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0);
-}
-
-static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
-
static ssize_t pmdown_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
- return sprintf(buf, "%ld\n", rtd->pmdown_time);
+ return sysfs_emit(buf, "%ld\n", rtd->pmdown_time);
}
-static ssize_t pmdown_time_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pmdown_time_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
int ret;
@@ -181,10 +90,9 @@ static ssize_t pmdown_time_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
+static DEVICE_ATTR_RW(pmdown_time);
static struct attribute *soc_dev_attrs[] = {
- &dev_attr_codec_reg.attr,
&dev_attr_pmdown_time.attr,
NULL
};
@@ -195,92 +103,33 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
+ if (!rtd)
+ return 0;
+
if (attr == &dev_attr_pmdown_time.attr)
return attr->mode; /* always visible */
- return rtd->codec ? attr->mode : 0; /* enabled only with codec */
+ return rtd->dai_link->num_codecs ? attr->mode : 0; /* enabled only with codec */
}
static const struct attribute_group soc_dapm_dev_group = {
- .attrs = soc_dapm_dev_attrs,
+ .attrs = snd_soc_dapm_dev_attrs,
.is_visible = soc_dev_attr_is_visible,
};
-static const struct attribute_group soc_dev_roup = {
+static const struct attribute_group soc_dev_group = {
.attrs = soc_dev_attrs,
.is_visible = soc_dev_attr_is_visible,
};
static const struct attribute_group *soc_dev_attr_groups[] = {
&soc_dapm_dev_group,
- &soc_dev_roup,
+ &soc_dev_group,
NULL
};
#ifdef CONFIG_DEBUG_FS
-static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- ssize_t ret;
- struct snd_soc_codec *codec = file->private_data;
- char *buf;
-
- if (*ppos < 0 || !count)
- return -EINVAL;
-
- buf = kmalloc(count, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = soc_codec_reg_show(codec, buf, count, *ppos);
- if (ret >= 0) {
- if (copy_to_user(user_buf, buf, ret)) {
- kfree(buf);
- return -EFAULT;
- }
- *ppos += ret;
- }
-
- kfree(buf);
- return ret;
-}
-
-static ssize_t codec_reg_write_file(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- char buf[32];
- size_t buf_size;
- char *start = buf;
- unsigned long reg, value;
- struct snd_soc_codec *codec = file->private_data;
- int ret;
-
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- while (*start == ' ')
- start++;
- reg = simple_strtoul(start, &start, 16);
- while (*start == ' ')
- start++;
- ret = kstrtoul(start, 16, &value);
- if (ret)
- return ret;
-
- /* Userspace has been fiddling around behind the kernel's back */
- add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE);
-
- snd_soc_write(codec, reg, value);
- return buf_size;
-}
-
-static const struct file_operations codec_reg_fops = {
- .open = simple_open,
- .read = codec_reg_read_file,
- .write = codec_reg_write_file,
- .llseek = default_llseek,
-};
+struct dentry *snd_soc_debugfs_root;
+EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
static void soc_init_component_debugfs(struct snd_soc_component *component)
{
@@ -302,476 +151,617 @@ static void soc_init_component_debugfs(struct snd_soc_component *component)
component->card->debugfs_card_root);
}
- if (!component->debugfs_root) {
- dev_warn(component->dev,
- "ASoC: Failed to create component debugfs directory\n");
- return;
- }
-
- snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component),
+ snd_soc_dapm_debugfs_init(snd_soc_component_to_dapm(component),
component->debugfs_root);
-
- if (component->init_debugfs)
- component->init_debugfs(component);
}
static void soc_cleanup_component_debugfs(struct snd_soc_component *component)
{
+ if (!component->debugfs_root)
+ return;
debugfs_remove_recursive(component->debugfs_root);
+ component->debugfs_root = NULL;
}
-static void soc_init_codec_debugfs(struct snd_soc_component *component)
+static int dai_list_show(struct seq_file *m, void *v)
{
- struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ struct snd_soc_component *component;
+ struct snd_soc_dai *dai;
+
+ mutex_lock(&client_mutex);
+
+ for_each_component(component)
+ for_each_component_dais(component, dai)
+ seq_printf(m, "%s\n", dai->name);
+
+ mutex_unlock(&client_mutex);
- codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
- codec->component.debugfs_root,
- codec, &codec_reg_fops);
- if (!codec->debugfs_reg)
- dev_warn(codec->dev,
- "ASoC: Failed to create codec register debugfs file\n");
+ return 0;
}
+DEFINE_SHOW_ATTRIBUTE(dai_list);
-static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int component_list_show(struct seq_file *m, void *v)
{
- char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- ssize_t len, ret = 0;
- struct snd_soc_codec *codec;
-
- if (!buf)
- return -ENOMEM;
+ struct snd_soc_component *component;
mutex_lock(&client_mutex);
- list_for_each_entry(codec, &codec_list, list) {
- len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
- codec->component.name);
- if (len >= 0)
- ret += len;
- if (ret > PAGE_SIZE) {
- ret = PAGE_SIZE;
- break;
- }
- }
+ for_each_component(component)
+ seq_printf(m, "%s\n", component->name);
mutex_unlock(&client_mutex);
- if (ret >= 0)
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(component_list);
+
+static void soc_init_card_debugfs(struct snd_soc_card *card)
+{
+ card->debugfs_card_root = debugfs_create_dir(card->name,
+ snd_soc_debugfs_root);
- kfree(buf);
+ debugfs_create_u32("dapm_pop_time", 0644, card->debugfs_card_root,
+ &card->pop_time);
- return ret;
+ snd_soc_dapm_debugfs_init(snd_soc_card_to_dapm(card), card->debugfs_card_root);
}
-static const struct file_operations codec_list_fops = {
- .read = codec_list_read_file,
- .llseek = default_llseek,/* read accesses f_pos */
-};
+static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
+{
+ debugfs_remove_recursive(card->debugfs_card_root);
+ card->debugfs_card_root = NULL;
+}
-static ssize_t dai_list_read_file(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static void snd_soc_debugfs_init(void)
{
- char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- ssize_t len, ret = 0;
- struct snd_soc_component *component;
- struct snd_soc_dai *dai;
+ snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
- if (!buf)
- return -ENOMEM;
+ debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
+ &dai_list_fops);
- mutex_lock(&client_mutex);
+ debugfs_create_file("components", 0444, snd_soc_debugfs_root, NULL,
+ &component_list_fops);
+}
- list_for_each_entry(component, &component_list, list) {
- list_for_each_entry(dai, &component->dai_list, list) {
- len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
- dai->name);
- if (len >= 0)
- ret += len;
- if (ret > PAGE_SIZE) {
- ret = PAGE_SIZE;
- break;
- }
- }
- }
+static void snd_soc_debugfs_exit(void)
+{
+ debugfs_remove_recursive(snd_soc_debugfs_root);
+}
- mutex_unlock(&client_mutex);
+#else
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+static inline void soc_init_component_debugfs(struct snd_soc_component *component) { }
+static inline void soc_cleanup_component_debugfs(struct snd_soc_component *component) { }
+static inline void soc_init_card_debugfs(struct snd_soc_card *card) { }
+static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { }
+static inline void snd_soc_debugfs_init(void) { }
+static inline void snd_soc_debugfs_exit(void) { }
- kfree(buf);
+#endif
- return ret;
+static int snd_soc_is_match_dai_args(const struct of_phandle_args *args1,
+ const struct of_phandle_args *args2)
+{
+ if (!args1 || !args2)
+ return 0;
+
+ if (args1->np != args2->np)
+ return 0;
+
+ for (int i = 0; i < args1->args_count; i++)
+ if (args1->args[i] != args2->args[i])
+ return 0;
+
+ return 1;
}
-static const struct file_operations dai_list_fops = {
- .read = dai_list_read_file,
- .llseek = default_llseek,/* read accesses f_pos */
-};
+static inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc)
+{
+ return !(dlc->dai_args || dlc->name || dlc->of_node);
+}
-static ssize_t platform_list_read_file(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
+static inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_component *dlc)
{
- char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- ssize_t len, ret = 0;
- struct snd_soc_platform *platform;
+ return (dlc->name && dlc->of_node);
+}
- if (!buf)
- return -ENOMEM;
+static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dlc)
+{
+ return !(dlc->dai_args || dlc->dai_name);
+}
- mutex_lock(&client_mutex);
+static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
+ struct snd_soc_dai *dai)
+{
+ if (!dlc)
+ return 0;
- list_for_each_entry(platform, &platform_list, list) {
- len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
- platform->component.name);
- if (len >= 0)
- ret += len;
- if (ret > PAGE_SIZE) {
- ret = PAGE_SIZE;
- break;
- }
- }
+ if (dlc->dai_args)
+ return snd_soc_is_match_dai_args(dai->driver->dai_args, dlc->dai_args);
- mutex_unlock(&client_mutex);
+ if (!dlc->dai_name)
+ return 1;
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ /* see snd_soc_dai_name_get() */
- kfree(buf);
+ if (dai->driver->name &&
+ strcmp(dlc->dai_name, dai->driver->name) == 0)
+ return 1;
- return ret;
+ if (strcmp(dlc->dai_name, dai->name) == 0)
+ return 1;
+
+ if (dai->component->name &&
+ strcmp(dlc->dai_name, dai->component->name) == 0)
+ return 1;
+
+ return 0;
}
-static const struct file_operations platform_list_fops = {
- .read = platform_list_read_file,
- .llseek = default_llseek,/* read accesses f_pos */
-};
+const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai)
+{
+ /* see snd_soc_is_matching_dai() */
+ if (dai->driver->name)
+ return dai->driver->name;
-static void soc_init_card_debugfs(struct snd_soc_card *card)
+ if (dai->name)
+ return dai->name;
+
+ if (dai->component->name)
+ return dai->component->name;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_name_get);
+
+static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_component *component)
{
- if (!snd_soc_debugfs_root)
- return;
+ struct snd_soc_component *comp;
+ int i;
- card->debugfs_card_root = debugfs_create_dir(card->name,
- snd_soc_debugfs_root);
- if (!card->debugfs_card_root) {
- dev_warn(card->dev,
- "ASoC: Failed to create card debugfs directory\n");
- return;
+ for_each_rtd_components(rtd, i, comp) {
+ /* already connected */
+ if (comp == component)
+ return 0;
}
- card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644,
- card->debugfs_card_root,
- &card->pop_time);
- if (!card->debugfs_pop_time)
- dev_warn(card->dev,
- "ASoC: Failed to create pop time debugfs file\n");
+ /* see for_each_rtd_components */
+ rtd->num_components++; // increment flex array count at first
+ rtd->components[rtd->num_components - 1] = component;
+
+ return 0;
}
-static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
+struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
+ const char *driver_name)
{
- debugfs_remove_recursive(card->debugfs_card_root);
-}
+ struct snd_soc_component *component;
+ int i;
+ if (!driver_name)
+ return NULL;
-static void snd_soc_debugfs_init(void)
-{
- snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
- if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
- pr_warn("ASoC: Failed to create debugfs directory\n");
- snd_soc_debugfs_root = NULL;
- return;
- }
+ /*
+ * NOTE
+ *
+ * snd_soc_rtdcom_lookup() will find component from rtd by using
+ * specified driver name.
+ * But, if many components which have same driver name are connected
+ * to 1 rtd, this function will return 1st found component.
+ */
+ for_each_rtd_components(rtd, i, component) {
+ const char *component_name = component->driver->name;
- if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
- &codec_list_fops))
- pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
+ if (!component_name)
+ continue;
- if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
- &dai_list_fops))
- pr_warn("ASoC: Failed to create DAI list debugfs file\n");
+ if ((component_name == driver_name) ||
+ strcmp(component_name, driver_name) == 0)
+ return component;
+ }
- if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
- &platform_list_fops))
- pr_warn("ASoC: Failed to create platform list debugfs file\n");
+ return NULL;
}
+EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup);
-static void snd_soc_debugfs_exit(void)
+struct snd_soc_component
+*snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name)
{
- debugfs_remove_recursive(snd_soc_debugfs_root);
-}
+ struct snd_soc_component *component;
-#else
+ for_each_component(component) {
+ if (dev != component->dev)
+ continue;
-#define soc_init_codec_debugfs NULL
+ if (!driver_name)
+ return component;
-static inline void soc_init_component_debugfs(
- struct snd_soc_component *component)
-{
+ if (!component->driver->name)
+ continue;
+
+ if (component->driver->name == driver_name)
+ return component;
+
+ if (strcmp(component->driver->name, driver_name) == 0)
+ return component;
+ }
+
+ return NULL;
}
+EXPORT_SYMBOL_GPL(snd_soc_lookup_component_nolocked);
-static inline void soc_cleanup_component_debugfs(
- struct snd_soc_component *component)
+struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
+ const char *driver_name)
{
+ struct snd_soc_component *component;
+
+ mutex_lock(&client_mutex);
+ component = snd_soc_lookup_component_nolocked(dev, driver_name);
+ mutex_unlock(&client_mutex);
+
+ return component;
}
+EXPORT_SYMBOL_GPL(snd_soc_lookup_component);
-static inline void soc_init_card_debugfs(struct snd_soc_card *card)
+struct snd_soc_pcm_runtime
+*snd_soc_get_pcm_runtime(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
{
+ struct snd_soc_pcm_runtime *rtd;
+
+ for_each_card_rtds(card, rtd) {
+ if (rtd->dai_link == dai_link)
+ return rtd;
+ }
+ dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link->name);
+ return NULL;
}
+EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
-static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
+/*
+ * Power down the audio subsystem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int playback = SNDRV_PCM_STREAM_PLAYBACK;
+
+ snd_soc_dpcm_mutex_lock(rtd);
+
+ dev_dbg(rtd->dev,
+ "ASoC: pop wq checking: %s status: %s waiting: %s\n",
+ codec_dai->driver->playback.stream_name,
+ snd_soc_dai_stream_active(codec_dai, playback) ?
+ "active" : "inactive",
+ str_yes_no(rtd->pop_wait));
+
+ /* are we waiting on this codec DAI stream */
+ if (rtd->pop_wait == 1) {
+ rtd->pop_wait = 0;
+ snd_soc_dapm_stream_event(rtd, playback,
+ SND_SOC_DAPM_STREAM_STOP);
+ }
+
+ snd_soc_dpcm_mutex_unlock(rtd);
}
+EXPORT_SYMBOL_GPL(snd_soc_close_delayed_work);
-static inline void snd_soc_debugfs_init(void)
+static void soc_release_rtd_dev(struct device *dev)
{
+ /* "dev" means "rtd->dev" */
+ kfree(dev);
}
-static inline void snd_soc_debugfs_exit(void)
+static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
{
-}
+ if (!rtd)
+ return;
-#endif
+ list_del(&rtd->list);
-struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
- const char *dai_link, int stream)
-{
- struct snd_soc_pcm_runtime *rtd;
+ if (delayed_work_pending(&rtd->delayed_work))
+ flush_delayed_work(&rtd->delayed_work);
+ snd_soc_pcm_component_free(rtd);
- list_for_each_entry(rtd, &card->rtd_list, list) {
- if (rtd->dai_link->no_pcm &&
- !strcmp(rtd->dai_link->name, dai_link))
- return rtd->pcm->streams[stream].substream;
- }
- dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link);
- return NULL;
+ /*
+ * we don't need to call kfree() for rtd->dev
+ * see
+ * soc_release_rtd_dev()
+ *
+ * We don't need rtd->dev NULL check, because
+ * it is alloced *before* rtd.
+ * see
+ * soc_new_pcm_runtime()
+ *
+ * We don't need to mind freeing for rtd,
+ * because it was created from dev (= rtd->dev)
+ * see
+ * soc_new_pcm_runtime()
+ *
+ * rtd = devm_kzalloc(dev, ...);
+ * rtd->dev = dev
+ */
+ device_unregister(rtd->dev);
+}
+
+static void close_delayed_work(struct work_struct *work) {
+ struct snd_soc_pcm_runtime *rtd =
+ container_of(work, struct snd_soc_pcm_runtime,
+ delayed_work.work);
+
+ if (rtd->close_delayed_work_func)
+ rtd->close_delayed_work_func(rtd);
}
-EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct snd_soc_pcm_runtime *rtd;
+ struct device *dev;
+ int ret;
+ int stream;
- rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
- if (!rtd)
+ /*
+ * for rtd->dev
+ */
+ dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!dev)
return NULL;
- rtd->card = card;
- rtd->dai_link = dai_link;
- rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) *
- dai_link->num_codecs,
- GFP_KERNEL);
- if (!rtd->codec_dais) {
- kfree(rtd);
+ dev->parent = card->dev;
+ dev->release = soc_release_rtd_dev;
+
+ dev_set_name(dev, "%s", dai_link->name);
+
+ ret = device_register(dev);
+ if (ret < 0) {
+ put_device(dev); /* soc_release_rtd_dev */
return NULL;
}
- return rtd;
-}
+ /*
+ * for rtd
+ */
+ rtd = devm_kzalloc(dev,
+ struct_size(rtd, components,
+ dai_link->num_cpus +
+ dai_link->num_codecs +
+ dai_link->num_platforms),
+ GFP_KERNEL);
+ if (!rtd) {
+ device_unregister(dev);
+ return NULL;
+ }
-static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
-{
- if (rtd && rtd->codec_dais)
- kfree(rtd->codec_dais);
- kfree(rtd);
-}
+ rtd->dev = dev;
+ INIT_LIST_HEAD(&rtd->list);
+ for_each_pcm_streams(stream) {
+ INIT_LIST_HEAD(&rtd->dpcm[stream].be_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[stream].fe_clients);
+ }
+ dev_set_drvdata(dev, rtd);
+ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
-static void soc_add_pcm_runtime(struct snd_soc_card *card,
- struct snd_soc_pcm_runtime *rtd)
-{
+ if ((dai_link->num_cpus + dai_link->num_codecs) == 0) {
+ dev_err(dev, "ASoC: it has no CPU or codec DAIs\n");
+ goto free_rtd;
+ }
+
+ /*
+ * for rtd->dais
+ */
+ rtd->dais = devm_kcalloc(dev, dai_link->num_cpus + dai_link->num_codecs,
+ sizeof(struct snd_soc_dai *),
+ GFP_KERNEL);
+ if (!rtd->dais)
+ goto free_rtd;
+
+ /*
+ * dais = [][][][][][][][][][][][][][][][][][]
+ * ^cpu_dais ^codec_dais
+ * |--- num_cpus ---|--- num_codecs --|
+ * see
+ * snd_soc_rtd_to_cpu()
+ * snd_soc_rtd_to_codec()
+ */
+ rtd->card = card;
+ rtd->dai_link = dai_link;
+ rtd->id = card->num_rtd++;
+ rtd->pmdown_time = pmdown_time; /* default power off timeout */
+
+ /* see for_each_card_rtds */
list_add_tail(&rtd->list, &card->rtd_list);
- rtd->num = card->num_rtd;
- card->num_rtd++;
+
+ ret = device_add_groups(dev, soc_dev_attr_groups);
+ if (ret < 0)
+ goto free_rtd;
+
+ return rtd;
+
+free_rtd:
+ soc_free_pcm_runtime(rtd);
+ return NULL;
}
-static void soc_remove_pcm_runtimes(struct snd_soc_card *card)
+static void snd_soc_fill_dummy_dai(struct snd_soc_card *card)
{
- struct snd_soc_pcm_runtime *rtd, *_rtd;
+ struct snd_soc_dai_link *dai_link;
+ int i;
- list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) {
- list_del(&rtd->list);
- soc_free_pcm_runtime(rtd);
+ /*
+ * COMP_DUMMY() creates size 0 array on dai_link.
+ * Fill it as dummy DAI in case of CPU/Codec here.
+ * Do nothing for Platform.
+ */
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->num_cpus == 0 && dai_link->cpus) {
+ dai_link->num_cpus = 1;
+ dai_link->cpus = &snd_soc_dummy_dlc;
+ }
+ if (dai_link->num_codecs == 0 && dai_link->codecs) {
+ dai_link->num_codecs = 1;
+ dai_link->codecs = &snd_soc_dummy_dlc;
+ }
}
+}
- card->num_rtd = 0;
+static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ for_each_card_rtds(card, rtd)
+ flush_delayed_work(&rtd->delayed_work);
}
-struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
- const char *dai_link)
+#ifdef CONFIG_PM_SLEEP
+static void soc_playback_digital_mute(struct snd_soc_card *card, int mute)
{
struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *dai;
+ int playback = SNDRV_PCM_STREAM_PLAYBACK;
+ int i;
- list_for_each_entry(rtd, &card->rtd_list, list) {
- if (!strcmp(rtd->dai_link->name, dai_link))
- return rtd;
+ for_each_card_rtds(card, rtd) {
+
+ if (rtd->dai_link->ignore_suspend)
+ continue;
+
+ for_each_rtd_dais(rtd, i, dai) {
+ if (snd_soc_dai_stream_active(dai, playback))
+ snd_soc_dai_digital_mute(dai, mute, playback);
+ }
}
- dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link);
- return NULL;
}
-EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
-static void codec2codec_close_delayed_work(struct work_struct *work)
+static void soc_dapm_suspend_resume(struct snd_soc_card *card, int event)
{
- /* Currently nothing to do for c2c links
- * Since c2c links are internal nodes in the DAPM graph and
- * don't interface with the outside world or application layer
- * we don't have to do any special handling on close.
- */
+ struct snd_soc_pcm_runtime *rtd;
+ int stream;
+
+ for_each_card_rtds(card, rtd) {
+
+ if (rtd->dai_link->ignore_suspend)
+ continue;
+
+ for_each_pcm_streams(stream)
+ snd_soc_dapm_stream_event(rtd, stream, event);
+ }
}
-#ifdef CONFIG_PM_SLEEP
/* powers down audio subsystem for suspend */
int snd_soc_suspend(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct snd_soc_pcm_runtime *rtd;
int i;
/* If the card is not initialized yet there is nothing to do */
- if (!card->instantiated)
+ if (!snd_soc_card_is_instantiated(card))
return 0;
- /* Due to the resume being scheduled into a workqueue we could
- * suspend before that's finished - wait for it to complete.
+ /*
+ * Due to the resume being scheduled into a workqueue we could
+ * suspend before that's finished - wait for it to complete.
*/
- snd_power_lock(card->snd_card);
- snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0);
- snd_power_unlock(card->snd_card);
+ snd_power_wait(card->snd_card);
/* we're going to block userspace touching us until resume completes */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
/* mute any active DACs */
- list_for_each_entry(rtd, &card->rtd_list, list) {
-
- if (rtd->dai_link->ignore_suspend)
- continue;
-
- for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_dai *dai = rtd->codec_dais[i];
- struct snd_soc_dai_driver *drv = dai->driver;
-
- if (drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, 1);
- }
- }
+ soc_playback_digital_mute(card, 1);
/* suspend all pcms */
- list_for_each_entry(rtd, &card->rtd_list, list) {
+ for_each_card_rtds(card, rtd) {
if (rtd->dai_link->ignore_suspend)
continue;
snd_pcm_suspend_all(rtd->pcm);
}
- if (card->suspend_pre)
- card->suspend_pre(card);
+ snd_soc_card_suspend_pre(card);
- list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- if (rtd->dai_link->ignore_suspend)
- continue;
+ /* close any waiting streams */
+ snd_soc_flush_all_delayed_work(card);
- if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
- cpu_dai->driver->suspend(cpu_dai);
- }
+ soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_SUSPEND);
- /* close any waiting streams */
- list_for_each_entry(rtd, &card->rtd_list, list)
- flush_delayed_work(&rtd->delayed_work);
+ /* Recheck all endpoints too, their state is affected by suspend */
+ snd_soc_dapm_mark_endpoints_dirty(card);
+ snd_soc_dapm_sync(snd_soc_card_to_dapm(card));
- list_for_each_entry(rtd, &card->rtd_list, list) {
+ /* suspend all COMPONENTs */
+ for_each_card_rtds(card, rtd) {
if (rtd->dai_link->ignore_suspend)
continue;
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_SUSPEND);
-
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_CAPTURE,
- SND_SOC_DAPM_STREAM_SUSPEND);
- }
-
- /* Recheck all endpoints too, their state is affected by suspend */
- dapm_mark_endpoints_dirty(card);
- snd_soc_dapm_sync(&card->dapm);
+ for_each_rtd_components(rtd, i, component) {
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- /* suspend all CODECs */
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ /*
+ * ignore if component was already suspended
+ */
+ if (snd_soc_component_is_suspended(component))
+ continue;
- /* If there are paths active then the CODEC will be held with
- * bias _ON and should not be suspended. */
- if (!codec->suspended) {
+ /*
+ * If there are paths active then the COMPONENT will be
+ * held with bias _ON and should not be suspended.
+ */
switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_STANDBY:
/*
- * If the CODEC is capable of idle
+ * If the COMPONENT is capable of idle
* bias off then being in STANDBY
* means it's doing something,
* otherwise fall through.
*/
- if (dapm->idle_bias_off) {
- dev_dbg(codec->dev,
+ if (!snd_soc_dapm_get_idle_bias(dapm)) {
+ dev_dbg(component->dev,
"ASoC: idle_bias_off CODEC on over suspend\n");
break;
}
+ fallthrough;
case SND_SOC_BIAS_OFF:
- if (codec->driver->suspend)
- codec->driver->suspend(codec);
- codec->suspended = 1;
- if (codec->component.regmap)
- regcache_mark_dirty(codec->component.regmap);
+ snd_soc_component_suspend(component);
+ if (component->regmap)
+ regcache_mark_dirty(component->regmap);
/* deactivate pins to sleep state */
- pinctrl_pm_select_sleep_state(codec->dev);
+ pinctrl_pm_select_sleep_state(component->dev);
break;
default:
- dev_dbg(codec->dev,
- "ASoC: CODEC is on over suspend\n");
+ dev_dbg(component->dev,
+ "ASoC: COMPONENT is on over suspend\n");
break;
}
}
}
- list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- if (rtd->dai_link->ignore_suspend)
- continue;
-
- if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
- cpu_dai->driver->suspend(cpu_dai);
-
- /* deactivate pins to sleep state */
- pinctrl_pm_select_sleep_state(cpu_dai->dev);
- }
-
- if (card->suspend_post)
- card->suspend_post(card);
+ snd_soc_card_suspend_post(card);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_suspend);
-/* deferred resume work, so resume can complete before we finished
+/*
+ * deferred resume work, so resume can complete before we finished
* setting our codec back up, which can be very slow on I2C
*/
static void soc_resume_deferred(struct work_struct *work)
{
struct snd_soc_card *card =
- container_of(work, struct snd_soc_card, deferred_resume_work);
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_codec *codec;
- int i;
+ container_of(work, struct snd_soc_card,
+ deferred_resume_work);
+ struct snd_soc_component *component;
- /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
+ /*
+ * our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
* so userspace apps are blocked from touching us
*/
@@ -780,75 +770,25 @@ static void soc_resume_deferred(struct work_struct *work)
/* Bring us up into D2 so that DAPM starts enabling things */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2);
- if (card->resume_pre)
- card->resume_pre(card);
-
- /* resume control bus DAIs */
- list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ snd_soc_card_resume_pre(card);
- if (rtd->dai_link->ignore_suspend)
- continue;
-
- if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
- cpu_dai->driver->resume(cpu_dai);
+ for_each_card_components(card, component) {
+ if (snd_soc_component_is_suspended(component))
+ snd_soc_component_resume(component);
}
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (codec->suspended) {
- if (codec->driver->resume)
- codec->driver->resume(codec);
- codec->suspended = 0;
- }
- }
-
- list_for_each_entry(rtd, &card->rtd_list, list) {
-
- if (rtd->dai_link->ignore_suspend)
- continue;
-
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_RESUME);
-
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_CAPTURE,
- SND_SOC_DAPM_STREAM_RESUME);
- }
+ soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_RESUME);
/* unmute any active DACs */
- list_for_each_entry(rtd, &card->rtd_list, list) {
-
- if (rtd->dai_link->ignore_suspend)
- continue;
-
- for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_dai *dai = rtd->codec_dais[i];
- struct snd_soc_dai_driver *drv = dai->driver;
-
- if (drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, 0);
- }
- }
-
- list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- if (rtd->dai_link->ignore_suspend)
- continue;
-
- if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
- cpu_dai->driver->resume(cpu_dai);
- }
+ soc_playback_digital_mute(card, 0);
- if (card->resume_post)
- card->resume_post(card);
+ snd_soc_card_resume_post(card);
dev_dbg(card->dev, "ASoC: resume work completed\n");
/* Recheck all endpoints too, their state is affected by suspend */
- dapm_mark_endpoints_dirty(card);
- snd_soc_dapm_sync(&card->dapm);
+ snd_soc_dapm_mark_endpoints_dirty(card);
+ snd_soc_dapm_sync(snd_soc_card_to_dapm(card));
/* userspace can access us now we are back as we were before */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
@@ -858,539 +798,851 @@ static void soc_resume_deferred(struct work_struct *work)
int snd_soc_resume(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- bool bus_control = false;
- struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_component *component;
/* If the card is not initialized yet there is nothing to do */
- if (!card->instantiated)
+ if (!snd_soc_card_is_instantiated(card))
return 0;
/* activate pins from sleep state */
- list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai **codec_dais = rtd->codec_dais;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int j;
-
- if (cpu_dai->active)
- pinctrl_pm_select_default_state(cpu_dai->dev);
-
- for (j = 0; j < rtd->num_codecs; j++) {
- struct snd_soc_dai *codec_dai = codec_dais[j];
- if (codec_dai->active)
- pinctrl_pm_select_default_state(codec_dai->dev);
- }
- }
+ for_each_card_components(card, component)
+ if (snd_soc_component_active(component))
+ pinctrl_pm_select_default_state(component->dev);
- /*
- * DAIs that also act as the control bus master might have other drivers
- * hanging off them so need to resume immediately. Other drivers don't
- * have that problem and may take a substantial amount of time to resume
- * due to I/O costs and anti-pop so handle them out of line.
- */
- list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- bus_control |= cpu_dai->driver->bus_control;
- }
- if (bus_control) {
- dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
- soc_resume_deferred(&card->deferred_resume_work);
- } else {
- dev_dbg(dev, "ASoC: Scheduling resume work\n");
- if (!schedule_work(&card->deferred_resume_work))
- dev_err(dev, "ASoC: resume work item may be lost\n");
- }
+ dev_dbg(dev, "ASoC: Scheduling resume work\n");
+ if (!schedule_work(&card->deferred_resume_work))
+ dev_err(dev, "ASoC: resume work item may be lost\n");
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_resume);
+
+static void soc_resume_init(struct snd_soc_card *card)
+{
+ /* deferred resume work */
+ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
+}
#else
#define snd_soc_suspend NULL
#define snd_soc_resume NULL
+static inline void soc_resume_init(struct snd_soc_card *card) { }
#endif
-static const struct snd_soc_dai_ops null_dai_ops = {
-};
+static struct device_node
+*soc_component_to_node(struct snd_soc_component *component)
+{
+ struct device_node *of_node;
+
+ of_node = component->dev->of_node;
+ if (!of_node && component->dev->parent)
+ of_node = component->dev->parent->of_node;
+
+ return of_node;
+}
+
+struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev,
+ const struct of_phandle_args *args)
+{
+ struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL);
+
+ if (!ret)
+ return NULL;
+
+ *ret = *args;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_copy_dai_args);
+
+static int snd_soc_is_matching_component(
+ const struct snd_soc_dai_link_component *dlc,
+ struct snd_soc_component *component)
+{
+ struct device_node *component_of_node;
+
+ if (!dlc)
+ return 0;
+
+ if (dlc->dai_args) {
+ struct snd_soc_dai *dai;
+
+ for_each_component_dais(component, dai)
+ if (snd_soc_is_matching_dai(dlc, dai))
+ return 1;
+ return 0;
+ }
+
+ component_of_node = soc_component_to_node(component);
+
+ if (dlc->of_node && component_of_node != dlc->of_node)
+ return 0;
+ if (dlc->name && strcmp(component->name, dlc->name))
+ return 0;
+
+ return 1;
+}
static struct snd_soc_component *soc_find_component(
- const struct device_node *of_node, const char *name)
+ const struct snd_soc_dai_link_component *dlc)
{
struct snd_soc_component *component;
lockdep_assert_held(&client_mutex);
- list_for_each_entry(component, &component_list, list) {
- if (of_node) {
- if (component->dev->of_node == of_node)
- return component;
- } else if (strcmp(component->name, name) == 0) {
+ /*
+ * NOTE
+ *
+ * It returns *1st* found component, but some driver
+ * has few components by same of_node/name
+ * ex)
+ * CPU component and generic DMAEngine component
+ */
+ for_each_component(component)
+ if (snd_soc_is_matching_component(dlc, component))
return component;
- }
- }
return NULL;
}
-static struct snd_soc_dai *snd_soc_find_dai(
+/**
+ * snd_soc_find_dai - Find a registered DAI
+ *
+ * @dlc: name of the DAI or the DAI driver and optional component info to match
+ *
+ * This function will search all registered components and their DAIs to
+ * find the DAI of the same name. The component's of_node and name
+ * should also match if being specified.
+ *
+ * Return: pointer of DAI, or NULL if not found.
+ */
+struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc)
{
struct snd_soc_component *component;
struct snd_soc_dai *dai;
- struct device_node *component_of_node;
lockdep_assert_held(&client_mutex);
- /* Find CPU DAI from registered DAIs*/
- list_for_each_entry(component, &component_list, list) {
- component_of_node = component->dev->of_node;
- if (!component_of_node && component->dev->parent)
- component_of_node = component->dev->parent->of_node;
-
- if (dlc->of_node && component_of_node != dlc->of_node)
- continue;
- if (dlc->name && strcmp(component->name, dlc->name))
- continue;
- list_for_each_entry(dai, &component->dai_list, list) {
- if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
- continue;
-
- return dai;
- }
- }
+ /* Find CPU DAI from registered DAIs */
+ for_each_component(component)
+ if (snd_soc_is_matching_component(dlc, component))
+ for_each_component_dais(component, dai)
+ if (snd_soc_is_matching_dai(dlc, dai))
+ return dai;
return NULL;
}
+EXPORT_SYMBOL_GPL(snd_soc_find_dai);
-static bool soc_is_dai_link_bound(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
+struct snd_soc_dai *snd_soc_find_dai_with_mutex(
+ const struct snd_soc_dai_link_component *dlc)
{
- struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *dai;
- list_for_each_entry(rtd, &card->rtd_list, list) {
- if (rtd->dai_link == dai_link)
- return true;
- }
+ mutex_lock(&client_mutex);
+ dai = snd_soc_find_dai(dlc);
+ mutex_unlock(&client_mutex);
- return false;
+ return dai;
}
+EXPORT_SYMBOL_GPL(snd_soc_find_dai_with_mutex);
-static int soc_bind_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
+static int soc_dai_link_sanity_check(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
{
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai_link_component *codecs = dai_link->codecs;
- struct snd_soc_dai_link_component cpu_dai_component;
- struct snd_soc_dai **codec_dais;
- struct snd_soc_platform *platform;
- const char *platform_name;
int i;
+ struct snd_soc_dai_link_component *dlc;
- dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
+ /* Codec check */
+ for_each_link_codecs(link, i, dlc) {
+ /*
+ * Codec must be specified by 1 of name or OF node,
+ * not both or neither.
+ */
+ if (snd_soc_dlc_component_is_invalid(dlc))
+ goto component_invalid;
- rtd = soc_new_pcm_runtime(card, dai_link);
- if (!rtd)
- return -ENOMEM;
+ if (snd_soc_dlc_component_is_empty(dlc))
+ goto component_empty;
- if (soc_is_dai_link_bound(card, dai_link)) {
- dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
- dai_link->name);
- return 0;
- }
+ /* Codec DAI name must be specified */
+ if (snd_soc_dlc_dai_is_empty(dlc))
+ goto dai_empty;
- cpu_dai_component.name = dai_link->cpu_name;
- cpu_dai_component.of_node = dai_link->cpu_of_node;
- cpu_dai_component.dai_name = dai_link->cpu_dai_name;
- rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
- if (!rtd->cpu_dai) {
- dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
- dai_link->cpu_dai_name);
- goto _err_defer;
+ /*
+ * Defer card registration if codec component is not added to
+ * component list.
+ */
+ if (!soc_find_component(dlc))
+ goto component_not_found;
}
- rtd->num_codecs = dai_link->num_codecs;
+ /* Platform check */
+ for_each_link_platforms(link, i, dlc) {
+ /*
+ * Platform may be specified by either name or OF node, but it
+ * can be left unspecified, then no components will be inserted
+ * in the rtdcom list
+ */
+ if (snd_soc_dlc_component_is_invalid(dlc))
+ goto component_invalid;
- /* Find CODEC from registered CODECs */
- codec_dais = rtd->codec_dais;
- for (i = 0; i < rtd->num_codecs; i++) {
- codec_dais[i] = snd_soc_find_dai(&codecs[i]);
- if (!codec_dais[i]) {
- dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
- codecs[i].dai_name);
- goto _err_defer;
- }
+ if (snd_soc_dlc_component_is_empty(dlc))
+ goto component_empty;
+
+ /*
+ * Defer card registration if platform component is not added to
+ * component list.
+ */
+ if (!soc_find_component(dlc))
+ goto component_not_found;
}
- /* Single codec links expect codec and codec_dai in runtime data */
- rtd->codec_dai = codec_dais[0];
- rtd->codec = rtd->codec_dai->codec;
+ /* CPU check */
+ for_each_link_cpus(link, i, dlc) {
+ /*
+ * CPU device may be specified by either name or OF node, but
+ * can be left unspecified, and will be matched based on DAI
+ * name alone..
+ */
+ if (snd_soc_dlc_component_is_invalid(dlc))
+ goto component_invalid;
- /* if there's no platform we match on the empty platform */
- platform_name = dai_link->platform_name;
- if (!platform_name && !dai_link->platform_of_node)
- platform_name = "snd-soc-dummy";
- /* find one from the set of registered platforms */
- list_for_each_entry(platform, &platform_list, list) {
- if (dai_link->platform_of_node) {
- if (platform->dev->of_node !=
- dai_link->platform_of_node)
- continue;
+ if (snd_soc_dlc_component_is_empty(dlc)) {
+ /*
+ * At least one of CPU DAI name or CPU device name/node must be specified
+ */
+ if (snd_soc_dlc_dai_is_empty(dlc))
+ goto component_dai_empty;
} else {
- if (strcmp(platform->component.name, platform_name))
- continue;
+ /*
+ * Defer card registration if Component is not added
+ */
+ if (!soc_find_component(dlc))
+ goto component_not_found;
}
-
- rtd->platform = platform;
- }
- if (!rtd->platform) {
- dev_err(card->dev, "ASoC: platform %s not registered\n",
- dai_link->platform_name);
- return -EPROBE_DEFER;
}
- soc_add_pcm_runtime(card, rtd);
return 0;
-_err_defer:
- soc_free_pcm_runtime(rtd);
- return -EPROBE_DEFER;
-}
+component_invalid:
+ dev_err(card->dev, "ASoC: Both Component name/of_node are set for %s\n", link->name);
+ return -EINVAL;
+
+component_empty:
+ dev_err(card->dev, "ASoC: Neither Component name/of_node are set for %s\n", link->name);
+ return -EINVAL;
-static void soc_remove_component(struct snd_soc_component *component)
+component_not_found:
+ dev_dbg(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name);
+ return -EPROBE_DEFER;
+
+dai_empty:
+ dev_err(card->dev, "ASoC: DAI name is not set for %s\n", link->name);
+ return -EINVAL;
+
+component_dai_empty:
+ dev_err(card->dev, "ASoC: Neither DAI/Component name/of_node are set for %s\n", link->name);
+ return -EINVAL;
+}
+
+#define MAX_DEFAULT_CH_MAP_SIZE 8
+static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZE] = {
+ { .cpu = 0, .codec = 0 },
+ { .cpu = 1, .codec = 1 },
+ { .cpu = 2, .codec = 2 },
+ { .cpu = 3, .codec = 3 },
+ { .cpu = 4, .codec = 4 },
+ { .cpu = 5, .codec = 5 },
+ { .cpu = 6, .codec = 6 },
+ { .cpu = 7, .codec = 7 },
+};
+static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZE] = {
+ { .cpu = 0, .codec = 0 },
+ { .cpu = 0, .codec = 1 },
+ { .cpu = 0, .codec = 2 },
+ { .cpu = 0, .codec = 3 },
+ { .cpu = 0, .codec = 4 },
+ { .cpu = 0, .codec = 5 },
+ { .cpu = 0, .codec = 6 },
+ { .cpu = 0, .codec = 7 },
+};
+static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_SIZE] = {
+ { .cpu = 0, .codec = 0 },
+ { .cpu = 1, .codec = 0 },
+ { .cpu = 2, .codec = 0 },
+ { .cpu = 3, .codec = 0 },
+ { .cpu = 4, .codec = 0 },
+ { .cpu = 5, .codec = 0 },
+ { .cpu = 6, .codec = 0 },
+ { .cpu = 7, .codec = 0 },
+};
+static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
{
- if (!component->card)
- return;
+ struct snd_soc_dai_link_ch_map *ch_maps;
+ int i;
- /* This is a HACK and will be removed soon */
- if (component->codec)
- list_del(&component->codec->card_list);
+ /*
+ * dai_link->ch_maps indicates how CPU/Codec are connected.
+ * It will be a map seen from a larger number of DAI.
+ * see
+ * soc.h :: [dai_link->ch_maps Image sample]
+ */
- if (component->remove)
- component->remove(component);
+ /* it should have ch_maps if connection was N:M */
+ if (dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
+ dai_link->num_cpus != dai_link->num_codecs && !dai_link->ch_maps) {
+ dev_err(card->dev, "need to have ch_maps when N:M connection (%s)",
+ dai_link->name);
+ return -EINVAL;
+ }
- snd_soc_dapm_free(snd_soc_component_get_dapm(component));
+ /* do nothing if it has own maps */
+ if (dai_link->ch_maps)
+ goto sanity_check;
- soc_cleanup_component_debugfs(component);
- component->card = NULL;
- module_put(component->dev->driver->owner);
-}
+ /* check default map size */
+ if (dai_link->num_cpus > MAX_DEFAULT_CH_MAP_SIZE ||
+ dai_link->num_codecs > MAX_DEFAULT_CH_MAP_SIZE) {
+ dev_err(card->dev, "soc-core.c needs update default_connection_maps");
+ return -EINVAL;
+ }
-static void soc_remove_dai(struct snd_soc_dai *dai, int order)
-{
- int err;
+ /* Compensate missing map for ... */
+ if (dai_link->num_cpus == dai_link->num_codecs)
+ dai_link->ch_maps = default_ch_map_sync; /* for 1:1 or N:N */
+ else if (dai_link->num_cpus < dai_link->num_codecs)
+ dai_link->ch_maps = default_ch_map_1cpu; /* for 1:N */
+ else
+ dai_link->ch_maps = default_ch_map_1codec; /* for N:1 */
- if (dai && dai->probed &&
- dai->driver->remove_order == order) {
- if (dai->driver->remove) {
- err = dai->driver->remove(dai);
- if (err < 0)
- dev_err(dai->dev,
- "ASoC: failed to remove %s: %d\n",
- dai->name, err);
+sanity_check:
+ dev_dbg(card->dev, "dai_link %s\n", dai_link->stream_name);
+ for_each_link_ch_maps(dai_link, i, ch_maps) {
+ if ((ch_maps->cpu >= dai_link->num_cpus) ||
+ (ch_maps->codec >= dai_link->num_codecs)) {
+ dev_err(card->dev,
+ "unexpected dai_link->ch_maps[%d] index (cpu(%d/%d) codec(%d/%d))",
+ i,
+ ch_maps->cpu, dai_link->num_cpus,
+ ch_maps->codec, dai_link->num_codecs);
+ return -EINVAL;
}
- dai->probed = 0;
+
+ dev_dbg(card->dev, " [%d] cpu%d <-> codec%d\n",
+ i, ch_maps->cpu, ch_maps->codec);
}
+
+ return 0;
}
-static void soc_remove_link_dais(struct snd_soc_card *card,
- struct snd_soc_pcm_runtime *rtd, int order)
+/**
+ * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card
+ * @card: The ASoC card to which the pcm_runtime has
+ * @rtd: The pcm_runtime to remove
+ *
+ * This function removes a pcm_runtime from the ASoC card.
+ */
+void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd)
{
- int i;
+ if (!rtd)
+ return;
- /* unregister the rtd device */
- if (rtd->dev_registered) {
- device_unregister(rtd->dev);
- rtd->dev_registered = 0;
- }
+ lockdep_assert_held(&client_mutex);
- /* remove the CODEC DAI */
- for (i = 0; i < rtd->num_codecs; i++)
- soc_remove_dai(rtd->codec_dais[i], order);
+ /*
+ * Notify the machine driver for extra destruction
+ */
+ snd_soc_card_remove_dai_link(card, rtd->dai_link);
- soc_remove_dai(rtd->cpu_dai, order);
+ soc_free_pcm_runtime(rtd);
}
+EXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime);
-static void soc_remove_link_components(struct snd_soc_card *card,
- struct snd_soc_pcm_runtime *rtd, int order)
+/**
+ * snd_soc_add_pcm_runtime - Add a pcm_runtime dynamically via dai_link
+ * @card: The ASoC card to which the pcm_runtime is added
+ * @dai_link: The DAI link to find pcm_runtime
+ *
+ * This function adds a pcm_runtime ASoC card by using dai_link.
+ *
+ * Note: Topology can use this API to add pcm_runtime when probing the
+ * topology component. And machine drivers can still define static
+ * DAI links in dai_link array.
+ */
+static int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai_link_component *codec, *platform, *cpu;
struct snd_soc_component *component;
- int i;
+ int i, id, ret;
- /* remove the platform */
- if (platform && platform->component.driver->remove_order == order)
- soc_remove_component(&platform->component);
+ lockdep_assert_held(&client_mutex);
- /* remove the CODEC-side CODEC */
- for (i = 0; i < rtd->num_codecs; i++) {
- component = rtd->codec_dais[i]->component;
- if (component->driver->remove_order == order)
- soc_remove_component(component);
- }
+ /*
+ * Notify the machine driver for extra initialization
+ */
+ ret = snd_soc_card_add_dai_link(card, dai_link);
+ if (ret < 0)
+ return ret;
- /* remove any CPU-side CODEC */
- if (cpu_dai) {
- if (cpu_dai->component->driver->remove_order == order)
- soc_remove_component(cpu_dai->component);
- }
-}
+ if (dai_link->ignore)
+ return 0;
-static void soc_remove_dai_links(struct snd_soc_card *card)
-{
- int order;
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai_link *link, *_link;
+ dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
- for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
- order++) {
- list_for_each_entry(rtd, &card->rtd_list, list)
- soc_remove_link_dais(card, rtd, order);
- }
+ ret = soc_dai_link_sanity_check(card, dai_link);
+ if (ret < 0)
+ return ret;
+
+ rtd = soc_new_pcm_runtime(card, dai_link);
+ if (!rtd)
+ return -ENOMEM;
- for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
- order++) {
- list_for_each_entry(rtd, &card->rtd_list, list)
- soc_remove_link_components(card, rtd, order);
+ for_each_link_cpus(dai_link, i, cpu) {
+ snd_soc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu);
+ if (!snd_soc_rtd_to_cpu(rtd, i)) {
+ dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
+ cpu->dai_name);
+ goto _err_defer;
+ }
+ snd_soc_rtd_add_component(rtd, snd_soc_rtd_to_cpu(rtd, i)->component);
}
- list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
- if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK)
- dev_warn(card->dev, "Topology forgot to remove link %s?\n",
- link->name);
+ /* Find CODEC from registered CODECs */
+ for_each_link_codecs(dai_link, i, codec) {
+ snd_soc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec);
+ if (!snd_soc_rtd_to_codec(rtd, i)) {
+ dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n",
+ codec->dai_name);
+ goto _err_defer;
+ }
- list_del(&link->list);
- card->num_dai_links--;
+ snd_soc_rtd_add_component(rtd, snd_soc_rtd_to_codec(rtd, i)->component);
}
-}
-static int snd_soc_init_multicodec(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
-{
- /* Legacy codec/codec_dai link is a single entry in multicodec */
- if (dai_link->codec_name || dai_link->codec_of_node ||
- dai_link->codec_dai_name) {
- dai_link->num_codecs = 1;
+ /* Find PLATFORM from registered PLATFORMs */
+ for_each_link_platforms(dai_link, i, platform) {
+ for_each_component(component) {
+ if (!snd_soc_is_matching_component(platform, component))
+ continue;
- dai_link->codecs = devm_kzalloc(card->dev,
- sizeof(struct snd_soc_dai_link_component),
- GFP_KERNEL);
- if (!dai_link->codecs)
- return -ENOMEM;
+ if (snd_soc_component_is_dummy(component) && component->num_dai)
+ continue;
- dai_link->codecs[0].name = dai_link->codec_name;
- dai_link->codecs[0].of_node = dai_link->codec_of_node;
- dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
+ snd_soc_rtd_add_component(rtd, component);
+ }
}
- if (!dai_link->codecs) {
- dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
- return -EINVAL;
+ /*
+ * Most drivers will register their PCMs using DAI link ordering but
+ * topology based drivers can use the DAI link id field to set PCM
+ * device number and then use rtd + a base offset of the BEs.
+ *
+ * FIXME
+ *
+ * This should be implemented by using "dai_link" feature instead of
+ * "component" feature.
+ */
+ id = rtd->id;
+ for_each_rtd_components(rtd, i, component) {
+ if (!component->driver->use_dai_pcm_id)
+ continue;
+
+ if (rtd->dai_link->no_pcm)
+ id += component->driver->be_pcm_base;
+ else
+ id = rtd->dai_link->id;
}
+ rtd->id = id;
return 0;
+
+_err_defer:
+ snd_soc_remove_pcm_runtime(card, rtd);
+ return -EPROBE_DEFER;
}
-static int soc_init_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *link)
+int snd_soc_add_pcm_runtimes(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link,
+ int num_dai_link)
{
- int i, ret;
+ for (int i = 0; i < num_dai_link; i++) {
+ int ret;
- ret = snd_soc_init_multicodec(card, link);
- if (ret) {
- dev_err(card->dev, "ASoC: failed to init multicodec\n");
- return ret;
- }
+ ret = snd_soc_compensate_channel_connection_map(card, dai_link + i);
+ if (ret < 0)
+ return ret;
- for (i = 0; i < link->num_codecs; i++) {
- /*
- * Codec must be specified by 1 of name or OF node,
- * not both or neither.
- */
- if (!!link->codecs[i].name ==
- !!link->codecs[i].of_node) {
- dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
- /* Codec DAI name must be specified */
- if (!link->codecs[i].dai_name) {
- dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
- link->name);
- return -EINVAL;
- }
+ ret = snd_soc_add_pcm_runtime(card, dai_link + i);
+ if (ret < 0)
+ return ret;
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtimes);
+
+static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_dai *dai, *not_used;
+ u64 pos, possible_fmt;
+ unsigned int mask = 0, dai_fmt = 0;
+ int i, j, priority, pri, until;
+
/*
- * Platform may be specified by either name or OF node, but
- * can be left unspecified, and a dummy platform will be used.
+ * Get selectable format from each DAIs.
+ *
+ ****************************
+ * NOTE
+ * Using .auto_selectable_formats is not mandatory,
+ * we can select format manually from Sound Card.
+ * When use it, driver should list well tested format only.
+ ****************************
+ *
+ * ex)
+ * auto_selectable_formats (= SND_SOC_POSSIBLE_xxx)
+ * (A) (B) (C)
+ * DAI0_: { 0x000F, 0x00F0, 0x0F00 };
+ * DAI1 : { 0xF000, 0x0F00 };
+ * (X) (Y)
+ *
+ * "until" will be 3 in this case (MAX array size from DAI0 and DAI1)
+ * Here is dev_dbg() message and comments
+ *
+ * priority = 1
+ * DAI0: (pri, fmt) = (1, 000000000000000F) // 1st check (A) DAI1 is not selected
+ * DAI1: (pri, fmt) = (0, 0000000000000000) // Necessary Waste
+ * DAI0: (pri, fmt) = (1, 000000000000000F) // 2nd check (A)
+ * DAI1: (pri, fmt) = (1, 000000000000F000) // (X)
+ * priority = 2
+ * DAI0: (pri, fmt) = (2, 00000000000000FF) // 3rd check (A) + (B)
+ * DAI1: (pri, fmt) = (1, 000000000000F000) // (X)
+ * DAI0: (pri, fmt) = (2, 00000000000000FF) // 4th check (A) + (B)
+ * DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y)
+ * priority = 3
+ * DAI0: (pri, fmt) = (3, 0000000000000FFF) // 5th check (A) + (B) + (C)
+ * DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y)
+ * found auto selected format: 0000000000000F00
*/
- if (link->platform_name && link->platform_of_node) {
- dev_err(card->dev,
- "ASoC: Both platform name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
+ until = snd_soc_dai_get_fmt_max_priority(rtd);
+ for (priority = 1; priority <= until; priority++) {
+ for_each_rtd_dais(rtd, j, not_used) {
+
+ possible_fmt = ULLONG_MAX;
+ for_each_rtd_dais(rtd, i, dai) {
+ u64 fmt = 0;
+ pri = (j >= i) ? priority : priority - 1;
+ fmt = snd_soc_dai_get_fmt(dai, pri);
+ possible_fmt &= fmt;
+ }
+ if (possible_fmt)
+ goto found;
+ }
+ }
+ /* Not Found */
+ return;
+found:
/*
- * CPU device may be specified by either name or OF node, but
- * can be left unspecified, and will be matched based on DAI
- * name alone..
+ * convert POSSIBLE_DAIFMT to DAIFMT
+ *
+ * Some basic/default settings on each is defined as 0.
+ * see
+ * SND_SOC_DAIFMT_NB_NF
+ * SND_SOC_DAIFMT_GATED
+ *
+ * SND_SOC_DAIFMT_xxx_MASK can't notice it if Sound Card specify
+ * these value, and will be overwrite to auto selected value.
+ *
+ * To avoid such issue, loop from 63 to 0 here.
+ * Small number of SND_SOC_POSSIBLE_xxx will be Hi priority.
+ * Basic/Default settings of each part and above are defined
+ * as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx.
*/
- if (link->cpu_name && link->cpu_of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both cpu name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
+ for (i = 63; i >= 0; i--) {
+ pos = 1ULL << i;
+ switch (possible_fmt & pos) {
+ /*
+ * for format
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_I2S:
+ case SND_SOC_POSSIBLE_DAIFMT_RIGHT_J:
+ case SND_SOC_POSSIBLE_DAIFMT_LEFT_J:
+ case SND_SOC_POSSIBLE_DAIFMT_DSP_A:
+ case SND_SOC_POSSIBLE_DAIFMT_DSP_B:
+ case SND_SOC_POSSIBLE_DAIFMT_AC97:
+ case SND_SOC_POSSIBLE_DAIFMT_PDM:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | i;
+ break;
+ /*
+ * for clock
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_CONT:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_CONT;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_GATED:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_GATED;
+ break;
+ /*
+ * for clock invert
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_NB_NF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_NF;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_NB_IF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_IF;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_IB_NF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_NF;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_IB_IF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_IF;
+ break;
+ /*
+ * for clock provider / consumer
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_CBP_CFP:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_CBC_CFP:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFP;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_CBP_CFC:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFC;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_CBC_CFC:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ }
}
+
/*
- * At least one of CPU DAI name or CPU device name/node must be
- * specified
+ * Some driver might have very complex limitation.
+ * In such case, user want to auto-select non-limitation part,
+ * and want to manually specify complex part.
+ *
+ * Or for example, if both CPU and Codec can be clock provider,
+ * but because of its quality, user want to specify it manually.
+ *
+ * Use manually specified settings if sound card did.
*/
- if (!link->cpu_dai_name &&
- !(link->cpu_name || link->cpu_of_node)) {
- dev_err(card->dev,
- "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK))
+ mask |= SND_SOC_DAIFMT_FORMAT_MASK;
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_MASK))
+ mask |= SND_SOC_DAIFMT_CLOCK_MASK;
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_INV_MASK))
+ mask |= SND_SOC_DAIFMT_INV_MASK;
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK))
+ mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
- return 0;
+ dai_link->dai_fmt |= (dai_fmt & mask);
}
/**
- * snd_soc_add_dai_link - Add a DAI link dynamically
- * @card: The ASoC card to which the DAI link is added
- * @dai_link: The new DAI link to add
+ * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime
+ * @rtd: The runtime for which the DAI link format should be changed
+ * @dai_fmt: The new DAI link format
*
- * This function adds a DAI link to the ASoC card's link list.
+ * This function updates the DAI link format for all DAIs connected to the DAI
+ * link for the specified runtime.
*
- * Note: Topology can use this API to add DAI links when probing the
- * topology component. And machine drivers can still define static
- * DAI links in dai_link array.
+ * Note: For setups with a static format set the dai_fmt field in the
+ * corresponding snd_dai_link struct instead of using this function.
+ *
+ * Returns 0 on success, otherwise a negative error code.
*/
-int snd_soc_add_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
+int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
+ unsigned int dai_fmt)
{
- if (dai_link->dobj.type
- && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
- dev_err(card->dev, "Invalid dai link type %d\n",
- dai_link->dobj.type);
- return -EINVAL;
- }
+ struct snd_soc_dai *cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ unsigned int ext_fmt;
+ unsigned int i;
+ int ret;
- lockdep_assert_held(&client_mutex);
- /* Notify the machine driver for extra initialization
- * on the link created by topology.
+ if (!dai_fmt)
+ return 0;
+
+ /*
+ * dai_fmt has 4 types
+ * 1. SND_SOC_DAIFMT_FORMAT_MASK
+ * 2. SND_SOC_DAIFMT_CLOCK
+ * 3. SND_SOC_DAIFMT_INV
+ * 4. SND_SOC_DAIFMT_CLOCK_PROVIDER
+ *
+ * 4. CLOCK_PROVIDER is set from Codec perspective in dai_fmt. So it will be flipped
+ * when this function calls set_fmt() for CPU (CBx_CFx -> Bx_Cx). see below.
+ * This mean, we can't set CPU/Codec both are clock consumer for example.
+ * New idea handles 4. in each dai->ext_fmt. It can keep compatibility.
+ *
+ * Legacy
+ * dai_fmt includes 1, 2, 3, 4
+ *
+ * New idea
+ * dai_fmt includes 1, 2, 3
+ * ext_fmt includes 4
*/
- if (dai_link->dobj.type && card->add_dai_link)
- card->add_dai_link(card, dai_link);
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ext_fmt = rtd->dai_link->codecs[i].ext_fmt;
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt | ext_fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ return ret;
+ }
+
+ /* Flip the polarity for the "CPU" end of link */
+ /* Will effect only for 4. SND_SOC_DAIFMT_CLOCK_PROVIDER */
+ dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt);
- list_add_tail(&dai_link->list, &card->dai_link_list);
- card->num_dai_links++;
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ ext_fmt = rtd->dai_link->cpus[i].ext_fmt;
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt | ext_fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ return ret;
+ }
return 0;
}
-EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
+EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
-/**
- * snd_soc_remove_dai_link - Remove a DAI link from the list
- * @card: The ASoC card that owns the link
- * @dai_link: The DAI link to remove
- *
- * This function removes a DAI link from the ASoC card's link list.
- *
- * For DAI links previously added by topology, topology should
- * remove them by using the dobj embedded in the link.
- */
-void snd_soc_remove_dai_link(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
+static int soc_init_pcm_runtime(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai_link *link, *_link;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
- if (dai_link->dobj.type
- && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
- dev_err(card->dev, "Invalid dai link type %d\n",
- dai_link->dobj.type);
- return;
- }
+ /* do machine specific initialization */
+ ret = snd_soc_link_init(rtd);
+ if (ret < 0)
+ return ret;
- lockdep_assert_held(&client_mutex);
- /* Notify the machine driver for extra destruction
- * on the link created by topology.
- */
- if (dai_link->dobj.type && card->remove_dai_link)
- card->remove_dai_link(card, dai_link);
+ snd_soc_runtime_get_dai_fmt(rtd);
+ ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
+ if (ret)
+ goto err;
- list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
- if (link == dai_link) {
- list_del(&link->list);
- card->num_dai_links--;
- return;
- }
+ /* add DPCM sysfs entries */
+ soc_dpcm_debugfs_add(rtd);
+
+ /* create compress_device if possible */
+ ret = snd_soc_dai_compress_new(cpu_dai, rtd);
+ if (ret != -ENOTSUPP)
+ goto err;
+
+ /* create the pcm */
+ ret = soc_new_pcm(rtd);
+ if (ret < 0) {
+ dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
+ dai_link->stream_name, ret);
+ goto err;
}
+
+ ret = snd_soc_pcm_dai_new(rtd);
+ if (ret < 0)
+ goto err;
+
+ rtd->initialized = true;
+
+ return 0;
+err:
+ snd_soc_link_exit(rtd);
+ return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
static void soc_set_name_prefix(struct snd_soc_card *card,
struct snd_soc_component *component)
{
- int i;
-
- if (card->codec_conf == NULL)
- return;
+ struct device_node *of_node = soc_component_to_node(component);
+ const char *str;
+ int ret, i;
for (i = 0; i < card->num_configs; i++) {
struct snd_soc_codec_conf *map = &card->codec_conf[i];
- if (map->of_node && component->dev->of_node != map->of_node)
- continue;
- if (map->dev_name && strcmp(component->name, map->dev_name))
- continue;
- component->name_prefix = map->name_prefix;
- break;
+
+ if (snd_soc_is_matching_component(&map->dlc, component) &&
+ map->name_prefix) {
+ component->name_prefix = map->name_prefix;
+ return;
+ }
}
+
+ /*
+ * If there is no configuration table or no match in the table,
+ * check if a prefix is provided in the node
+ */
+ ret = of_property_read_string(of_node, "sound-name-prefix", &str);
+ if (ret < 0)
+ return;
+
+ component->name_prefix = str;
+}
+
+static void soc_remove_component(struct snd_soc_component *component,
+ int probed)
+{
+
+ if (!component->card)
+ return;
+
+ if (probed)
+ snd_soc_component_remove(component);
+
+ list_del_init(&component->card_list);
+ snd_soc_dapm_free(snd_soc_component_to_dapm(component));
+ soc_cleanup_component_debugfs(component);
+ component->card = NULL;
+ snd_soc_component_module_put_when_remove(component);
}
static int soc_probe_component(struct snd_soc_card *card,
- struct snd_soc_component *component)
+ struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct snd_soc_dai *dai;
+ int probed = 0;
int ret;
- if (!strcmp(component->name, "snd-soc-dummy"))
+ if (snd_soc_component_is_dummy(component))
return 0;
if (component->card) {
if (component->card != card) {
dev_err(component->dev,
- "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n",
- card->name, component->card->name);
+ "Trying to bind component \"%s\" to card \"%s\" but is already bound to card \"%s\"\n",
+ component->name, card->name, component->card->name);
return -ENODEV;
}
return 0;
}
- if (!try_module_get(component->dev->driver->owner))
- return -ENODEV;
+ ret = snd_soc_component_module_get_when_probe(component);
+ if (ret < 0)
+ return ret;
component->card = card;
- dapm->card = card;
soc_set_name_prefix(card, component);
soc_init_component_debugfs(component);
- if (component->dapm_widgets) {
- ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,
- component->num_dapm_widgets);
+ snd_soc_dapm_init(dapm, card, component);
- if (ret != 0) {
- dev_err(component->dev,
- "Failed to create new controls %d\n", ret);
- goto err_probe;
- }
+ ret = snd_soc_dapm_new_controls(dapm,
+ component->driver->dapm_widgets,
+ component->driver->num_dapm_widgets);
+
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to create new controls %d\n", ret);
+ goto err_probe;
}
- list_for_each_entry(dai, &component->dai_list, list) {
+ for_each_component_dais(component, dai) {
ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
if (ret != 0) {
dev_err(component->dev,
@@ -1399,471 +1651,535 @@ static int soc_probe_component(struct snd_soc_card *card,
}
}
- if (component->probe) {
- ret = component->probe(component);
- if (ret < 0) {
- dev_err(component->dev,
- "ASoC: failed to probe component %d\n", ret);
- goto err_probe;
- }
-
- WARN(dapm->idle_bias_off &&
- dapm->bias_level != SND_SOC_BIAS_OFF,
- "codec %s can not start from non-off bias with idle_bias_off==1\n",
- component->name);
- }
+ ret = snd_soc_component_probe(component);
+ if (ret < 0)
+ goto err_probe;
- /* machine specific init */
- if (component->init) {
- ret = component->init(component);
- if (ret < 0) {
- dev_err(component->dev,
- "Failed to do machine specific init %d\n", ret);
- goto err_probe;
- }
- }
+ WARN(!snd_soc_dapm_get_idle_bias(dapm) &&
+ snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_OFF,
+ "codec %s can not start from non-off bias with idle_bias_off==1\n",
+ component->name);
+ probed = 1;
- if (component->controls)
- snd_soc_add_component_controls(component, component->controls,
- component->num_controls);
- if (component->dapm_routes)
- snd_soc_dapm_add_routes(dapm, component->dapm_routes,
- component->num_dapm_routes);
+ /*
+ * machine specific init
+ * see
+ * snd_soc_component_set_aux()
+ */
+ ret = snd_soc_component_init(component);
+ if (ret < 0)
+ goto err_probe;
- list_add(&dapm->list, &card->dapm_list);
+ ret = snd_soc_add_component_controls(component,
+ component->driver->controls,
+ component->driver->num_controls);
+ if (ret < 0)
+ goto err_probe;
- /* This is a HACK and will be removed soon */
- if (component->codec)
- list_add(&component->codec->card_list, &card->codec_dev_list);
+ ret = snd_soc_dapm_add_routes(dapm,
+ component->driver->dapm_routes,
+ component->driver->num_dapm_routes);
+ if (ret < 0)
+ goto err_probe;
- return 0;
+ /* see for_each_card_components */
+ list_add(&component->card_list, &card->component_dev_list);
err_probe:
- soc_cleanup_component_debugfs(component);
- component->card = NULL;
- module_put(component->dev->driver->owner);
+ if (ret < 0)
+ soc_remove_component(component, probed);
return ret;
}
-static void rtd_release(struct device *dev)
+static void soc_remove_link_dais(struct snd_soc_card *card)
{
- kfree(dev);
+ struct snd_soc_pcm_runtime *rtd;
+ int order;
+
+ for_each_comp_order(order) {
+ for_each_card_rtds(card, rtd) {
+ /* remove all rtd connected DAIs in good order */
+ snd_soc_pcm_dai_remove(rtd, order);
+ }
+ }
}
-static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
- const char *name)
+static int soc_probe_link_dais(struct snd_soc_card *card)
{
- int ret = 0;
+ struct snd_soc_pcm_runtime *rtd;
+ int order, ret;
- /* register the rtd device */
- rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!rtd->dev)
- return -ENOMEM;
- device_initialize(rtd->dev);
- rtd->dev->parent = rtd->card->dev;
- rtd->dev->release = rtd_release;
- rtd->dev->groups = soc_dev_attr_groups;
- dev_set_name(rtd->dev, "%s", name);
- dev_set_drvdata(rtd->dev, rtd);
- mutex_init(&rtd->pcm_mutex);
- INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
- INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
- INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
- INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
- ret = device_add(rtd->dev);
- if (ret < 0) {
- /* calling put_device() here to free the rtd->dev */
- put_device(rtd->dev);
- dev_err(rtd->card->dev,
- "ASoC: failed to register runtime device: %d\n", ret);
- return ret;
+ for_each_comp_order(order) {
+ for_each_card_rtds(card, rtd) {
+ /* probe all rtd connected DAIs in good order */
+ ret = snd_soc_pcm_dai_probe(rtd, order);
+ if (ret)
+ return ret;
+ }
}
- rtd->dev_registered = 1;
+
return 0;
}
-static int soc_probe_link_components(struct snd_soc_card *card,
- struct snd_soc_pcm_runtime *rtd,
- int order)
+static void soc_remove_link_components(struct snd_soc_card *card)
{
- struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_component *component;
- int i, ret;
+ struct snd_soc_pcm_runtime *rtd;
+ int i, order;
- /* probe the CPU-side component, if it is a CODEC */
- component = rtd->cpu_dai->component;
- if (component->driver->probe_order == order) {
- ret = soc_probe_component(card, component);
- if (ret < 0)
- return ret;
+ for_each_comp_order(order) {
+ for_each_card_rtds(card, rtd) {
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->remove_order != order)
+ continue;
+
+ soc_remove_component(component, 1);
+ }
+ }
}
+}
- /* probe the CODEC-side components */
- for (i = 0; i < rtd->num_codecs; i++) {
- component = rtd->codec_dais[i]->component;
- if (component->driver->probe_order == order) {
- ret = soc_probe_component(card, component);
- if (ret < 0)
- return ret;
+static int soc_probe_link_components(struct snd_soc_card *card)
+{
+ struct snd_soc_component *component;
+ struct snd_soc_pcm_runtime *rtd;
+ int i, ret, order;
+
+ for_each_comp_order(order) {
+ for_each_card_rtds(card, rtd) {
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->probe_order != order)
+ continue;
+
+ ret = soc_probe_component(card, component);
+ if (ret < 0)
+ return ret;
+ }
}
}
- /* probe the platform */
- if (platform->component.driver->probe_order == order) {
- ret = soc_probe_component(card, &platform->component);
- if (ret < 0)
- return ret;
+ return 0;
+}
+
+static void soc_unbind_aux_dev(struct snd_soc_card *card)
+{
+ struct snd_soc_component *component, *_component;
+
+ for_each_card_auxs_safe(card, component, _component) {
+ /* for snd_soc_component_init() */
+ snd_soc_component_set_aux(component, NULL);
+ list_del(&component->card_aux_list);
}
+}
+
+static int soc_bind_aux_dev(struct snd_soc_card *card)
+{
+ struct snd_soc_component *component;
+ struct snd_soc_aux_dev *aux;
+ int i;
+
+ for_each_card_pre_auxs(card, i, aux) {
+ /* codecs, usually analog devices */
+ component = soc_find_component(&aux->dlc);
+ if (!component)
+ return -EPROBE_DEFER;
+ /* for snd_soc_component_init() */
+ snd_soc_component_set_aux(component, aux);
+ /* see for_each_card_auxs */
+ list_add(&component->card_aux_list, &card->aux_comp_list);
+ }
return 0;
}
-static int soc_probe_dai(struct snd_soc_dai *dai, int order)
+static int soc_probe_aux_devices(struct snd_soc_card *card)
{
+ struct snd_soc_component *component;
+ int order;
int ret;
- if (!dai->probed && dai->driver->probe_order == order) {
- if (dai->driver->probe) {
- ret = dai->driver->probe(dai);
- if (ret < 0) {
- dev_err(dai->dev,
- "ASoC: failed to probe DAI %s: %d\n",
- dai->name, ret);
+ for_each_comp_order(order) {
+ for_each_card_auxs(card, component) {
+ if (component->driver->probe_order != order)
+ continue;
+
+ ret = soc_probe_component(card, component);
+ if (ret < 0)
return ret;
- }
}
-
- dai->probed = 1;
}
return 0;
}
-static int soc_link_dai_widgets(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link,
- struct snd_soc_pcm_runtime *rtd)
+static void soc_remove_aux_devices(struct snd_soc_card *card)
{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dapm_widget *sink, *source;
- int ret;
-
- if (rtd->num_codecs > 1)
- dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
+ struct snd_soc_component *comp, *_comp;
+ int order;
- /* link the DAI widgets */
- sink = codec_dai->playback_widget;
- source = cpu_dai->capture_widget;
- if (sink && source) {
- ret = snd_soc_dapm_new_pcm(card, dai_link->params,
- dai_link->num_params,
- source, sink);
- if (ret != 0) {
- dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
- sink->name, source->name, ret);
- return ret;
+ for_each_comp_order(order) {
+ for_each_card_auxs_safe(card, comp, _comp) {
+ if (comp->driver->remove_order == order)
+ soc_remove_component(comp, 1);
}
}
+}
- sink = cpu_dai->playback_widget;
- source = codec_dai->capture_widget;